[PATCH v6 5/6] iio: dac: ad5504: introduce local lock to protect state and spi transfers
Taha Ed-Dafili <[email protected]>
| Newsgroups | org.kernel.vger.linux-iio,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
The driver lacks locking, exposing concurrent access to the shared DMA SPI buffers, the non-atomic CTRL+NOOP write sequence in powerdown, and the pwr_down_mask/pwr_down_mode state variables. Introduce a mutex via devm_mutex_init() and guard(mutex) at the IIO callbacks to serialize access. In read_raw() and write_raw(), scope the lock to IIO_CHAN_INFO_RAW only since IIO_CHAN_INFO_SCALE reads vref_mv which is immutable after probe. Signed-off-by: Taha Ed-Dafili <[email protected]> --- drivers/iio/dac/ad5504.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index 799726eafdf2..74679feea385 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -8,11 +8,13 @@ #include <linux/acpi.h> #include <linux/array_size.h> #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/dev_printk.h> #include <linux/errno.h> #include <linux/interrupt.h> #include <linux/kstrtox.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/property.h> #include <linux/regulator/consumer.h> #include <linux/spi/spi.h> @@ -55,10 +57,12 @@ * @pwr_down_mask: power down mask * @pwr_down_mode: current power down mode * @data: transfer buffer + * @lock: lock to protect state and spi transfers */ struct ad5504_state { struct spi_device *spi; struct regulator *reg; + struct mutex lock; unsigned short vref_mv; unsigned pwr_down_mask; unsigned pwr_down_mode; @@ -109,7 +113,9 @@ static int ad5504_read_raw(struct iio_dev *indio_dev, int ret; switch (m) { - case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_RAW: { + guard(mutex)(&st->lock); + ret = ad5504_spi_read(st, chan->address); if (ret < 0) return ret; @@ -117,6 +123,7 @@ static int ad5504_read_raw(struct iio_dev *indio_dev, *val = ret; return IIO_VAL_INT; + } case IIO_CHAN_INFO_SCALE: *val = st->vref_mv; *val2 = chan->scan_type.realbits; @@ -134,11 +141,14 @@ static int ad5504_write_raw(struct iio_dev *indio_dev, struct ad5504_state *st = iio_priv(indio_dev); switch (mask) { - case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_RAW: { + guard(mutex)(&st->lock); + if (val >= (1 << chan->scan_type.realbits) || val < 0) return -EINVAL; return ad5504_spi_write(st, chan->address, val); + } default: return -EINVAL; } @@ -154,6 +164,8 @@ static int ad5504_get_powerdown_mode(struct iio_dev *indio_dev, { struct ad5504_state *st = iio_priv(indio_dev); + guard(mutex)(&st->lock); + return st->pwr_down_mode; } @@ -162,6 +174,8 @@ static int ad5504_set_powerdown_mode(struct iio_dev *indio_dev, { struct ad5504_state *st = iio_priv(indio_dev); + guard(mutex)(&st->lock); + st->pwr_down_mode = mode; return 0; @@ -179,6 +193,8 @@ static ssize_t ad5504_read_dac_powerdown(struct iio_dev *indio_dev, { struct ad5504_state *st = iio_priv(indio_dev); + guard(mutex)(&st->lock); + return sysfs_emit(buf, "%d\n", !(st->pwr_down_mask & (1 << chan->channel))); } @@ -195,6 +211,8 @@ static ssize_t ad5504_write_dac_powerdown(struct iio_dev *indio_dev, if (ret) return ret; + guard(mutex)(&st->lock); + if (pwr_down) st->pwr_down_mask &= ~(1 << chan->channel); else @@ -290,6 +308,10 @@ static int ad5504_probe(struct spi_device *spi) st = iio_priv(indio_dev); + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + if (is_acpi_device_node(dev_fwnode(dev))) { st->vref_mv = AD5504_VREF_ACPI_DEFAULT_mV; } else { -- 2.55.0