Commit 7afba922 authored by Ian Abbott's avatar Ian Abbott Committed by Greg Kroah-Hartman
Browse files

comedi: Make insn_rw_emulate_bits() do insn->n samples



The `insn_rw_emulate_bits()` function is used as a default handler for
`INSN_READ` instructions for subdevices that have a handler for
`INSN_BITS` but not for `INSN_READ`.  Similarly, it is used as a default
handler for `INSN_WRITE` instructions for subdevices that have a handler
for `INSN_BITS` but not for `INSN_WRITE`. It works by emulating the
`INSN_READ` or `INSN_WRITE` instruction handling with a constructed
`INSN_BITS` instruction.  However, `INSN_READ` and `INSN_WRITE`
instructions are supposed to be able read or write multiple samples,
indicated by the `insn->n` value, but `insn_rw_emulate_bits()` currently
only handles a single sample.  For `INSN_READ`, the comedi core will
copy `insn->n` samples back to user-space.  (That triggered KASAN
kernel-infoleak errors when `insn->n` was greater than 1, but that is
being fixed more generally elsewhere in the comedi core.)

Make `insn_rw_emulate_bits()` either handle `insn->n` samples, or return
an error, to conform to the general expectation for `INSN_READ` and
`INSN_WRITE` handlers.

Fixes: ed9eccbe ("Staging: add comedi core")
Cc: stable <stable@kernel.org> # 5.13+
Signed-off-by: default avatarIan Abbott <abbotti@mev.co.uk>
Link: https://lore.kernel.org/r/20250725141034.87297-1-abbotti@mev.co.uk


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 3cd212e8
Loading
Loading
Loading
Loading
+12 −11
Original line number Diff line number Diff line
@@ -620,11 +620,9 @@ static int insn_rw_emulate_bits(struct comedi_device *dev,
	unsigned int chan = CR_CHAN(insn->chanspec);
	unsigned int base_chan = (chan < 32) ? 0 : chan;
	unsigned int _data[2];
	unsigned int i;
	int ret;

	if (insn->n == 0)
		return 0;

	memset(_data, 0, sizeof(_data));
	memset(&_insn, 0, sizeof(_insn));
	_insn.insn = INSN_BITS;
@@ -636,17 +634,20 @@ static int insn_rw_emulate_bits(struct comedi_device *dev,
		if (!(s->subdev_flags & SDF_WRITABLE))
			return -EINVAL;
		_data[0] = 1U << (chan - base_chan);		/* mask */
		_data[1] = data[0] ? (1U << (chan - base_chan)) : 0; /* bits */
	}
	for (i = 0; i < insn->n; i++) {
		if (insn->insn == INSN_WRITE)
			_data[1] = data[i] ? _data[0] : 0;	/* bits */

		ret = s->insn_bits(dev, s, &_insn, _data);
		if (ret < 0)
			return ret;

		if (insn->insn == INSN_READ)
		data[0] = (_data[1] >> (chan - base_chan)) & 1;
			data[i] = (_data[1] >> (chan - base_chan)) & 1;
	}

	return 1;
	return insn->n;
}

static int __comedi_device_postconfig_async(struct comedi_device *dev,