]> www.infradead.org Git - users/hch/misc.git/commitdiff
comedi: Make insn_rw_emulate_bits() do insn->n samples
authorIan Abbott <abbotti@mev.co.uk>
Fri, 25 Jul 2025 14:10:34 +0000 (15:10 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 19 Aug 2025 10:57:01 +0000 (12:57 +0200)
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: ed9eccbe8970 ("Staging: add comedi core")
Cc: stable <stable@kernel.org> # 5.13+
Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
Link: https://lore.kernel.org/r/20250725141034.87297-1-abbotti@mev.co.uk
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/comedi/drivers.c

index f1dc854928c17684674cf4b72cd26ab902a304fb..c9ebaadc5e82af0ff95d1fe356539db22a89581b 100644 (file)
@@ -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;
@@ -635,18 +633,21 @@ static int insn_rw_emulate_bits(struct comedi_device *dev,
        if (insn->insn == INSN_WRITE) {
                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 */
+               _data[0] = 1U << (chan - base_chan);            /* mask */
        }
+       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;
+               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;
+               if (insn->insn == INSN_READ)
+                       data[i] = (_data[1] >> (chan - base_chan)) & 1;
+       }
 
-       return 1;
+       return insn->n;
 }
 
 static int __comedi_device_postconfig_async(struct comedi_device *dev,