diff options
Diffstat (limited to 'drivers/staging/comedi/drivers.c')
-rw-r--r-- | drivers/staging/comedi/drivers.c | 102 |
1 files changed, 96 insertions, 6 deletions
diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c index 3e5bccbc9c39..61802d7947ae 100644 --- a/drivers/staging/comedi/drivers.c +++ b/drivers/staging/comedi/drivers.c @@ -109,6 +109,10 @@ int comedi_alloc_subdev_readback(struct comedi_subdevice *s) s->readback = kcalloc(s->n_chan, sizeof(*s->readback), GFP_KERNEL); if (!s->readback) return -ENOMEM; + + if (!s->insn_read) + s->insn_read = comedi_readback_insn_read; + return 0; } EXPORT_SYMBOL_GPL(comedi_alloc_subdev_readback); @@ -316,19 +320,91 @@ unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s) case COMEDI_SUBD_DI: case COMEDI_SUBD_DO: case COMEDI_SUBD_DIO: - bits_per_sample = 8 * bytes_per_sample(s); - num_samples = (cmd->chanlist_len + bits_per_sample - 1) / - bits_per_sample; + bits_per_sample = 8 * comedi_bytes_per_sample(s); + num_samples = DIV_ROUND_UP(cmd->scan_end_arg, bits_per_sample); break; default: - num_samples = cmd->chanlist_len; + num_samples = cmd->scan_end_arg; break; } - return num_samples * bytes_per_sample(s); + return comedi_samples_to_bytes(s, num_samples); } EXPORT_SYMBOL_GPL(comedi_bytes_per_scan); /** + * comedi_nscans_left - return the number of scans left in the command + * @s: comedi_subdevice struct + * @nscans: the expected number of scans + * + * If nscans is 0, the number of scans available in the async buffer will be + * used. Otherwise the expected number of scans will be used. + * + * If the async command has a stop_src of TRIG_COUNT, the nscans will be + * checked against the number of scans left in the command. + * + * The return value will then be either the expected number of scans or the + * number of scans remaining in the command. + */ +unsigned int comedi_nscans_left(struct comedi_subdevice *s, + unsigned int nscans) +{ + struct comedi_async *async = s->async; + struct comedi_cmd *cmd = &async->cmd; + + if (nscans == 0) { + unsigned int nbytes = comedi_buf_read_n_available(s); + + nscans = nbytes / comedi_bytes_per_scan(s); + } + + if (cmd->stop_src == TRIG_COUNT) { + unsigned int scans_left = 0; + + if (async->scans_done < cmd->stop_arg) + scans_left = cmd->stop_arg - async->scans_done; + + if (nscans > scans_left) + nscans = scans_left; + } + return nscans; +} +EXPORT_SYMBOL_GPL(comedi_nscans_left); + +/** + * comedi_nsamples_left - return the number of samples left in the command + * @s: comedi_subdevice struct + * @nsamples: the expected number of samples + * + * Returns the expected number of samples of the number of samples remaining + * in the command. + */ +unsigned int comedi_nsamples_left(struct comedi_subdevice *s, + unsigned int nsamples) +{ + struct comedi_async *async = s->async; + struct comedi_cmd *cmd = &async->cmd; + + if (cmd->stop_src == TRIG_COUNT) { + /* +1 to force comedi_nscans_left() to return the scans left */ + unsigned int nscans = (nsamples / cmd->scan_end_arg) + 1; + unsigned int scans_left = comedi_nscans_left(s, nscans); + unsigned int scan_pos = + comedi_bytes_to_samples(s, async->scan_progress); + unsigned long long samples_left = 0; + + if (scans_left) { + samples_left = ((unsigned long long)scans_left * + cmd->scan_end_arg) - scan_pos; + } + + if (samples_left < nsamples) + nsamples = samples_left; + } + return nsamples; +} +EXPORT_SYMBOL_GPL(comedi_nsamples_left); + +/** * comedi_inc_scan_progress - update scan progress in asynchronous command * @s: comedi_subdevice struct * @num_bytes: amount of data in bytes to increment scan progress @@ -342,10 +418,24 @@ void comedi_inc_scan_progress(struct comedi_subdevice *s, unsigned int num_bytes) { struct comedi_async *async = s->async; + struct comedi_cmd *cmd = &async->cmd; unsigned int scan_length = comedi_bytes_per_scan(s); + /* track the 'cur_chan' for non-SDF_PACKED subdevices */ + if (!(s->subdev_flags & SDF_PACKED)) { + async->cur_chan += comedi_bytes_to_samples(s, num_bytes); + async->cur_chan %= cmd->chanlist_len; + } + async->scan_progress += num_bytes; if (async->scan_progress >= scan_length) { + unsigned int nscans = async->scan_progress / scan_length; + + if (async->scans_done < (UINT_MAX - nscans)) + async->scans_done += nscans; + else + async->scans_done = UINT_MAX; + async->scan_progress %= scan_length; async->events |= COMEDI_CB_EOS; } @@ -376,7 +466,7 @@ unsigned int comedi_handle_events(struct comedi_device *dev, if (events == 0) return events; - if (events & (COMEDI_CB_EOA | COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) + if (events & COMEDI_CB_CANCEL_MASK) s->cancel(dev, s); comedi_event(dev, s); |