diff options
-rw-r--r-- | drivers/iio/industrialio-buffer.c | 82 |
1 files changed, 46 insertions, 36 deletions
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index b4d7dba163cf..11291259b7b9 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -539,6 +539,15 @@ static void iio_buffer_deactivate(struct iio_buffer *buffer) iio_buffer_put(buffer); } +static void iio_buffer_deactivate_all(struct iio_dev *indio_dev) +{ + struct iio_buffer *buffer, *_buffer; + + list_for_each_entry_safe(buffer, _buffer, + &indio_dev->buffer_list, buffer_list) + iio_buffer_deactivate(buffer); +} + static void iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev, struct iio_buffer *buffer) { @@ -719,36 +728,46 @@ err_undo_config: static int iio_disable_buffers(struct iio_dev *indio_dev) { - int ret; + int ret = 0; + int ret2; /* Wind down existing buffers - iff there are any */ if (list_empty(&indio_dev->buffer_list)) return 0; + /* + * If things go wrong at some step in disable we still need to continue + * to perform the other steps, otherwise we leave the device in a + * inconsistent state. We return the error code for the first error we + * encountered. + */ + if (indio_dev->setup_ops->predisable) { - ret = indio_dev->setup_ops->predisable(indio_dev); - if (ret) - return ret; + ret2 = indio_dev->setup_ops->predisable(indio_dev); + if (ret2 && !ret) + ret = ret2; } indio_dev->currentmode = INDIO_DIRECT_MODE; if (indio_dev->setup_ops->postdisable) { - ret = indio_dev->setup_ops->postdisable(indio_dev); - if (ret) - return ret; + ret2 = indio_dev->setup_ops->postdisable(indio_dev); + if (ret2 && !ret) + ret = ret2; } - return 0; + iio_free_scan_mask(indio_dev, indio_dev->active_scan_mask); + indio_dev->active_scan_mask = NULL; + + return ret; } static int __iio_update_buffers(struct iio_dev *indio_dev, struct iio_buffer *insert_buffer, struct iio_buffer *remove_buffer) { - int ret; - const unsigned long *old_mask; struct iio_device_config new_config; + int ret; ret = iio_verify_update(indio_dev, insert_buffer, remove_buffer, &new_config); @@ -761,15 +780,9 @@ static int __iio_update_buffers(struct iio_dev *indio_dev, goto err_free_config; } - /* Keep a copy of current setup to allow roll back */ - old_mask = indio_dev->active_scan_mask; - indio_dev->active_scan_mask = NULL; - ret = iio_disable_buffers(indio_dev); - if (ret) { - iio_free_scan_mask(indio_dev, old_mask); - goto err_free_config; - } + if (ret) + goto err_deactivate_all; if (remove_buffer) iio_buffer_deactivate(remove_buffer); @@ -777,22 +790,26 @@ static int __iio_update_buffers(struct iio_dev *indio_dev, iio_buffer_activate(indio_dev, insert_buffer); /* If no buffers in list, we are done */ - if (list_empty(&indio_dev->buffer_list)) { - iio_free_scan_mask(indio_dev, old_mask); + if (list_empty(&indio_dev->buffer_list)) return 0; - } ret = iio_enable_buffers(indio_dev, &new_config); - if (ret) { - if (insert_buffer) - iio_buffer_deactivate(insert_buffer); - indio_dev->active_scan_mask = old_mask; - goto err_free_config; - } + if (ret) + goto err_deactivate_all; - iio_free_scan_mask(indio_dev, old_mask); return 0; +err_deactivate_all: + /* + * We've already verified that the config is valid earlier. If things go + * wrong in either enable or disable the most likely reason is an IO + * error from the device. In this case there is no good recovery + * strategy. Just make sure to disable everything and leave the device + * in a sane state. With a bit of luck the device might come back to + * life again later and userspace can try again. + */ + iio_buffer_deactivate_all(indio_dev); + err_free_config: iio_free_scan_mask(indio_dev, new_config.scan_mask); return ret; @@ -838,15 +855,8 @@ EXPORT_SYMBOL_GPL(iio_update_buffers); void iio_disable_all_buffers(struct iio_dev *indio_dev) { - struct iio_buffer *buffer, *_buffer; - iio_disable_buffers(indio_dev); - iio_free_scan_mask(indio_dev, indio_dev->active_scan_mask); - indio_dev->active_scan_mask = NULL; - - list_for_each_entry_safe(buffer, _buffer, - &indio_dev->buffer_list, buffer_list) - iio_buffer_deactivate(buffer); + iio_buffer_deactivate_all(indio_dev); } static ssize_t iio_buffer_store_enable(struct device *dev, |