Re: [PATCH 1/2] iio: adc: ad7944: add support for chain mode

From: Jonathan Cameron
Date: Sun Apr 28 2024 - 12:29:39 EST


On Thu, 25 Apr 2024 09:09:59 -0500
David Lechner <dlechner@xxxxxxxxxxxx> wrote:

> This adds support for the chain mode of the AD7944 ADC. This mode allows
> multiple ADCs to be daisy-chained together. Data from all of the ADCs in
> is read by reading multiple words from the first ADC in the chain.
>
> Each chip in the chain adds an extra IIO input voltage channel to the
> IIO device.
>
> Only the wiring configuration where the SPI controller CS line is
> connected to the CNV pin of all of the ADCs in the chain is supported
> in this patch.
>
> Signed-off-by: David Lechner <dlechner@xxxxxxxxxxxx>

Looks good except for one minor tweak needed to ensure the allocated buffer
is zeroed as we don't necessarily overwrite the the whole thing.

Given that's all I found, I've just switched that to devm_kzalloc and
applied the series.

Applied to the togreg branch of iio.git and pushed out initially as testing
for 0-day to look at it.

Thanks,

Jonathan



>
> +/**
> + * ad7944_chain_mode_alloc - allocate and initialize channel specs and buffers
> + * for daisy-chained devices
> + * @dev: The device for devm_ functions
> + * @chan_template: The channel template for the devices (array of 2 channels
> + * voltage and timestamp)
> + * @n_chain_dev: The number of devices in the chain
> + * @chain_chan: Pointer to receive the allocated channel specs
> + * @chain_mode_buf: Pointer to receive the allocated rx buffer
> + * @chain_scan_masks: Pointer to receive the allocated scan masks
> + * Return: 0 on success, a negative error code on failure
> + */
> +static int ad7944_chain_mode_alloc(struct device *dev,
> + const struct iio_chan_spec *chan_template,
> + u32 n_chain_dev,
> + struct iio_chan_spec **chain_chan,
> + void **chain_mode_buf,
> + unsigned long **chain_scan_masks)
> +{
> + struct iio_chan_spec *chan;
> + size_t chain_mode_buf_size;
> + unsigned long *scan_masks;
> + void *buf;
> + int i;
> +
> + /* 1 channel for each device in chain plus 1 for soft timestamp */
> +
> + chan = devm_kcalloc(dev, n_chain_dev + 1, sizeof(*chan), GFP_KERNEL);
> + if (!chan)
> + return -ENOMEM;
> +
> + for (i = 0; i < n_chain_dev; i++) {
> + chan[i] = chan_template[0];
> +
> + if (chan_template[0].differential) {
> + chan[i].channel = 2 * i;
> + chan[i].channel2 = 2 * i + 1;
> + } else {
> + chan[i].channel = i;
> + }
> +
> + chan[i].scan_index = i;
> + }
> +
> + /* soft timestamp */
> + chan[i] = chan_template[1];
> + chan[i].scan_index = i;
> +
> + *chain_chan = chan;
> +
> + /* 1 word for each voltage channel + aligned u64 for timestamp */
> +
> + chain_mode_buf_size = ALIGN(n_chain_dev *
> + BITS_TO_BYTES(chan[0].scan_type.storagebits), sizeof(u64))
> + + sizeof(u64);
> + buf = devm_kmalloc(dev, chain_mode_buf_size, GFP_KERNEL);

Zero it - It's not a problem to leak stale ADC data or similar
into the gap between the data and the timestamp, but it is a problem
if it's general kernel data potentially leaking.

So play it safe and devm_kzalloc()

> + if (!buf)
> + return -ENOMEM;
> +
> + *chain_mode_buf = buf;
> +
> + /*
> + * Have to limit n_chain_dev due to current implementation of
> + * available_scan_masks.
> + */
> + if (n_chain_dev > BITS_PER_LONG)
> + return dev_err_probe(dev, -EINVAL,
> + "chain is limited to 32 devices\n");
> +
> + scan_masks = devm_kcalloc(dev, 2, sizeof(*scan_masks), GFP_KERNEL);
> + if (!scan_masks)
> + return -ENOMEM;
> +
> + /*
> + * Scan mask is needed since we always have to read all devices in the
> + * chain in one SPI transfer.
> + */
> + scan_masks[0] = GENMASK(n_chain_dev - 1, 0);
> +
> + *chain_scan_masks = scan_masks;
> +
> + return 0;
> +}