Re: [PATCH 1/4] staging:iio: Add support for multiple buffers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 06/08/2012 04:13 PM, Lars-Peter Clausen wrote:
> On 05/30/2012 09:36 PM, Jonathan Cameron wrote:
>> Route all buffer writes through the demux.
>> Addition or removal of a buffer results in tear down and
>> setup of all the buffers for a given device.
>>
> 
> My test platform is currently broken in staging-next, so just some comments
> based on code review for now.
Annoying when that happens!

Thanks for taking a look.  This codes now been sitting around
long enough that I'm not entirely sure what some of it does myself...
> 
>> Signed-off-by: Jonathan Cameron <jic23@xxxxxxxxxx>
>> ---
>>  drivers/iio/industrialio-buffer.c               |  335 +++++++++++++++--------
>>  drivers/iio/industrialio-core.c                 |    1 +
>>  drivers/staging/iio/accel/adis16201_ring.c      |    4 +-
>>  drivers/staging/iio/accel/adis16203_ring.c      |    6 +-
>>  drivers/staging/iio/accel/adis16204_ring.c      |    3 +-
>>  drivers/staging/iio/accel/adis16209_ring.c      |    3 +-
>>  drivers/staging/iio/accel/adis16240_ring.c      |    4 +-
>>  drivers/staging/iio/accel/lis3l02dq_ring.c      |    3 +-
>>  drivers/staging/iio/adc/ad7192.c                |    3 +-
>>  drivers/staging/iio/adc/ad7298_ring.c           |    5 +-
>>  drivers/staging/iio/adc/ad7476_ring.c           |    2 +-
>>  drivers/staging/iio/adc/ad7606_ring.c           |    3 +-
>>  drivers/staging/iio/adc/ad7793.c                |    3 +-
>>  drivers/staging/iio/adc/ad7887_ring.c           |    2 +-
>>  drivers/staging/iio/adc/ad799x_ring.c           |    3 +-
>>  drivers/staging/iio/adc/max1363_ring.c          |    2 +-
>>  drivers/staging/iio/gyro/adis16260_ring.c       |    3 +-
>>  drivers/staging/iio/iio_simple_dummy_buffer.c   |    5 +-
>>  drivers/staging/iio/impedance-analyzer/ad5933.c |    3 +-
>>  drivers/staging/iio/imu/adis16400_ring.c        |    2 +-
>>  drivers/staging/iio/meter/ade7758_ring.c        |    3 +-
>>  include/linux/iio/buffer.h                      |   24 +-
>>  include/linux/iio/iio.h                         |    2 +
> 
> The at91_adc driver is not handled by this patch
good point. oops.  Darn these new drivers ;)
> 
>>  23 files changed, 267 insertions(+), 157 deletions(-)
>>
>> diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
>> index ac185b8..b04b543 100644
>> --- a/drivers/iio/industrialio-buffer.c
>> +++ b/drivers/iio/industrialio-buffer.c
>> @@ -31,6 +31,16 @@ static const char * const iio_endian_prefix[] = {
>>  	[IIO_LE] = "le",
>>  };
>>  
>> +static bool iio_buffer_is_primary_active(struct iio_dev *indio_dev)
>> +{
>> +	struct list_head *p;
>> +
>> +	list_for_each(p, &indio_dev->buffer_list)
>> +		if (p == &indio_dev->buffer->buffer_list)
>> +			return true;
> 
> More or less the same code is used below. I think it makes sense to have a
> generic iio_buffer_is_active(indio_dev, buffer)
Good point.
> 
>> +	return false;
>> +}
> 
>> [...]
>> @@ -534,31 +453,186 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev, const long *mask,
>>  	return bytes;
>>  }
>>  
>> -int iio_sw_buffer_preenable(struct iio_dev *indio_dev)
>> +int iio_update_buffers(struct iio_dev *indio_dev,
>> +		       struct iio_buffer *insert_buffer,
>> +		       struct iio_buffer *remove_buffer)
>>  {
>> -	struct iio_buffer *buffer = indio_dev->buffer;
>> -	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>> +	int ret;
> 
> drivers/iio/industrialio-buffer.c: In function ‘iio_update_buffers’:
> drivers/iio/industrialio-buffer.c:460: warning: ‘ret’ may be used
> uninitialized in this function
> 
> I think there a missing 'return 0', before the error handling. Right now the
> code always sets active_scan_mask to NULL.
The setting to NULL definitely isn't right, but we do still need to
free the compoundmask.  I'll fix that up.
> 
> 
>> +	struct iio_buffer *buffer;
>> +	unsigned long *compound_mask;
>>  
>> -	/* How much space will the demuxed element take? */
>> -	indio_dev->scan_bytes =
>> -		iio_compute_scan_bytes(indio_dev, buffer->scan_mask,
>> -				       buffer->scan_timestamp);
>> -	buffer->access->set_bytes_per_datum(buffer, indio_dev->scan_bytes);
>> +	/* Wind down existing buffers - iff there are any */
>> +	if (!list_empty(&indio_dev->buffer_list)) {
>> +		if (indio_dev->setup_ops->predisable) {
>> +			ret = indio_dev->setup_ops->predisable(indio_dev);
>> +			if (ret)
>> +				goto error_ret;
>> +		}
>> +		indio_dev->currentmode = INDIO_DIRECT_MODE;
>> +		if (indio_dev->setup_ops->postdisable) {
>> +			ret = indio_dev->setup_ops->postdisable(indio_dev);
>> +			if (ret)
>> +				goto error_ret;
>> +		}
>> +	}
>> +	if (!indio_dev->available_scan_masks)
>> +		kfree(indio_dev->active_scan_mask);
> 
> If the code errors out before reassigning active_scan_mask we'll leave a
> dangling pointer to the old scan mask, which will cause access after free or
> a double free.
good point this needs to be set to NULL.
> 
>> +
>> +	if (insert_buffer)
>> +		list_add(&insert_buffer->buffer_list, &indio_dev->buffer_list);
>> +	if (remove_buffer)
>> +		list_del(&remove_buffer->buffer_list);
> 
> I'd do the remove before the insert, makes this more robust, if the function
> is ever going to be called with both set to the same buffer.
Tempting as it is to take the view we should crash hard if anyone does
that you are right of course ;)
> 
>> +
>> +	/* If no buffers in list, we are done */
>> +	if (list_empty(&indio_dev->buffer_list))
>> +		return 0;
>>  
>>  	/* What scan mask do we actually have ?*/
>> +	compound_mask = kzalloc(BITS_TO_LONGS(indio_dev->masklength)
>> +				*sizeof(long), GFP_KERNEL);
>> +	if (compound_mask == NULL)
>> +		return -ENOMEM;
>> +	list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
>> +		bitmap_or(compound_mask, compound_mask, buffer->scan_mask,
>> +			  indio_dev->masklength);
>> +		indio_dev->scan_timestamp |= buffer->scan_timestamp;
>> +	}
>>  	if (indio_dev->available_scan_masks)
>>  		indio_dev->active_scan_mask =
>>  			iio_scan_mask_match(indio_dev->available_scan_masks,
>>  					    indio_dev->masklength,
>> -					    buffer->scan_mask);
>> +					    compound_mask);
> 
> active_scan_mask may be NULL if the device isn't able to handle the compound
> mask.
> 
> Btw. if it is not possible to add the new buffer to the list, e.g. because
> the compound scan mask is not supported, do we to do a rollback to the
> previous working configuration? I think this might be a better option,
> rather than leaving things broken.
hmm.. Indeed, that is a good idea...  Adds a bit of complexity, but not
too bad.
> 
>>  	else
>> -		indio_dev->active_scan_mask = buffer->scan_mask;
>> +		indio_dev->active_scan_mask = compound_mask;
>> +
>>  	iio_update_demux(indio_dev);
>>  
>> -	if (indio_dev->info->update_scan_mode)
>> -		return indio_dev->info
>> +	/* Wind up again */
>> +	if (indio_dev->setup_ops->preenable) {
>> +		ret = indio_dev->setup_ops->preenable(indio_dev);
>> +		if (ret) {
>> +			printk(KERN_ERR
>> +			       "Buffer not started:"
>> +			       "buffer preenable failed\n");
>> +			goto error_free_compound_mask;
>> +		}
>> +	}
>> +	indio_dev->scan_bytes =
>> +		iio_compute_scan_bytes(indio_dev,
>> +				       indio_dev->active_scan_mask,
>> +				       indio_dev->scan_timestamp);
>> +	list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list)
>> +		if (buffer->access->request_update) {
>> +			ret = buffer->access->request_update(buffer);
>> +			if (ret) {
>> +				printk(KERN_INFO
>> +				       "Buffer not started:"
>> +				       "buffer parameter update failed\n");
>> +				goto error_ret;
>> +			}
>> +		}
>> +	if (indio_dev->info->update_scan_mode) {
>> +		ret = indio_dev->info
>>  			->update_scan_mode(indio_dev,
>>  					   indio_dev->active_scan_mask);
>> +		if (ret < 0)
>> +			goto error_free_compound_mask;
>> +	}
>> +	/* Definitely possible for devices to support both of these.*/
>> +	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) {
>> +		if (!indio_dev->trig) {
>> +			printk(KERN_INFO "Buffer not started: no trigger\n");
>> +			ret = -EINVAL;
>> +			goto error_free_compound_mask;
>> +		}
>> +		indio_dev->currentmode = INDIO_BUFFER_TRIGGERED;
>> +	} else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) {
>> +		indio_dev->currentmode = INDIO_BUFFER_HARDWARE;
>> +	} else { /* should never be reached */
>> +		ret = -EINVAL;
>> +		goto error_free_compound_mask;
>> +	}
>> +
>> +	if (indio_dev->setup_ops->postenable) {
>> +		ret = indio_dev->setup_ops->postenable(indio_dev);
>> +		if (ret) {
>> +			printk(KERN_INFO
>> +			       "Buffer not started: postenable failed\n");
>> +			indio_dev->currentmode = INDIO_DIRECT_MODE;
>> +			if (indio_dev->setup_ops->postdisable)
>> +				indio_dev->setup_ops->postdisable(indio_dev);
>> +			goto error_free_compound_mask;
>> +		}
>> +	}
>> +error_free_compound_mask:
>> +	indio_dev->active_scan_mask = NULL;
>> +	kfree(compound_mask);
>> +error_ret:
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(iio_update_buffers);
>> +
>> +ssize_t iio_buffer_store_enable(struct device *dev,
>> +				struct device_attribute *attr,
>> +				const char *buf,
>> +				size_t len)
>> +{
>> +	int ret;
>> +	bool requested_state;
>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +	struct iio_buffer *pbuf = indio_dev->buffer;
>> +	struct list_head *p;
>> +	bool inlist = false;
>> +
>> +	ret = strtobool(buf, &requested_state);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	mutex_lock(&indio_dev->mlock);
>> +
>> +	/* Find out if it is in the list */
>> +	list_for_each(p, &indio_dev->buffer_list)
>> +		if (p == &pbuf->buffer_list) {
>> +			inlist = true;
>> +			break;
>> +		}
>> +	/* Already enabled */
>> +	if (inlist && requested_state)
>> +		goto done;
>> +	/* Already disabled */
>> +	if (!inlist && !requested_state)
>> +		goto done;
> 
>         if (inlist == requested_state)
> 		goto done;
> 
>> +
>> +	if (requested_state)
>> +		ret = iio_update_buffers(indio_dev,
>> +					 indio_dev->buffer, NULL);
>> +	else
>> +		ret = iio_update_buffers(indio_dev,
>> +					 NULL, indio_dev->buffer);
>> +
>> +	if (ret < 0)
>> +		goto done;
>> +done:
>> +	mutex_unlock(&indio_dev->mlock);
>> +	return (ret < 0) ? ret : len;
>> +}
>> +EXPORT_SYMBOL(iio_buffer_store_enable);
>> +
> [...]
>> @@ -567,7 +641,11 @@ EXPORT_SYMBOL(iio_sw_buffer_preenable);
>>   * iio_scan_mask_set() - set particular bit in the scan mask
>>   * @buffer: the buffer whose scan mask we are interested in
>>   * @bit: the bit to be set.
>> - **/
>> + *
>> + * Note that at this point we have no way of knowing what other
>> + * buffers might request, hence this code only verifies that the
>> + * individual buffers request is plausible.
>> + */
>>  int iio_scan_mask_set(struct iio_dev *indio_dev,
>>  		      struct iio_buffer *buffer, int bit)
>>  {
>> @@ -597,7 +675,7 @@ int iio_scan_mask_set(struct iio_dev *indio_dev,
>>  			return -EINVAL;
>>  		}
>>  	}
>> -	bitmap_copy(buffer->scan_mask, trialmask, indio_dev->masklength);
>> +	set_bit(bit, buffer->scan_mask);
> 
> Is this a unrelated fix?
Slight simplification.  Doesn't belong in this patch (and probably not
worth having at all really...
> 
>>  
>>  	kfree(trialmask);
>>  
> [...]
>> diff --git a/drivers/staging/iio/iio_simple_dummy_buffer.c b/drivers/staging/iio/iio_simple_dummy_buffer.c
>> index fdfc873..5931cbb 100644
>> --- a/drivers/staging/iio/iio_simple_dummy_buffer.c
>> +++ b/drivers/staging/iio/iio_simple_dummy_buffer.c
>> @@ -46,7 +46,6 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p)
>>  {
>>  	struct iio_poll_func *pf = p;
>>  	struct iio_dev *indio_dev = pf->indio_dev;
>> -	struct iio_buffer *buffer = indio_dev->buffer;
>>  	int len = 0;
>>  	u16 *data;
>>  
>> @@ -76,7 +75,7 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p)
>>  		     i < bitmap_weight(indio_dev->active_scan_mask,
>>  				       indio_dev->masklength);
>>  		     i++) {
>> -			j = find_next_bit(buffer->scan_mask,
>> +			j = find_next_bit(indio_dev->buffer->scan_mask,
> 
> should this be indio_dev->active_scan_mask?
This bit always give me a headache.  You are right of course. It's in
the trigger handler so should not care at all about the specific buffer
mask.
> 
>>  					  indio_dev->masklength, j + 1);
>>  			/* random access read form the 'device' */
>>  			data[i] = fakedata[j];
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux