summaryrefslogtreecommitdiff
path: root/drivers/comedi/drivers.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/comedi/drivers.c')
-rw-r--r--drivers/comedi/drivers.c177
1 files changed, 125 insertions, 52 deletions
diff --git a/drivers/comedi/drivers.c b/drivers/comedi/drivers.c
index 376130bfba8a..69cd2a253c66 100644
--- a/drivers/comedi/drivers.c
+++ b/drivers/comedi/drivers.c
@@ -158,7 +158,7 @@ static void comedi_device_detach_cleanup(struct comedi_device *dev)
int i;
struct comedi_subdevice *s;
- lockdep_assert_held(&dev->attach_lock);
+ lockdep_assert_held_write(&dev->attach_lock);
lockdep_assert_held(&dev->mutex);
if (dev->subdevices) {
for (i = 0; i < dev->n_subdevices; i++) {
@@ -196,16 +196,23 @@ static void comedi_device_detach_cleanup(struct comedi_device *dev)
comedi_clear_hw_dev(dev);
}
-void comedi_device_detach(struct comedi_device *dev)
+void comedi_device_detach_locked(struct comedi_device *dev)
{
+ lockdep_assert_held_write(&dev->attach_lock);
lockdep_assert_held(&dev->mutex);
comedi_device_cancel_all(dev);
- down_write(&dev->attach_lock);
dev->attached = false;
dev->detach_count++;
if (dev->driver)
dev->driver->detach(dev);
comedi_device_detach_cleanup(dev);
+}
+
+void comedi_device_detach(struct comedi_device *dev)
+{
+ lockdep_assert_held(&dev->mutex);
+ down_write(&dev->attach_lock);
+ comedi_device_detach_locked(dev);
up_write(&dev->attach_lock);
}
@@ -339,10 +346,10 @@ int comedi_dio_insn_config(struct comedi_device *dev,
unsigned int *data,
unsigned int mask)
{
- unsigned int chan_mask = 1 << CR_CHAN(insn->chanspec);
+ unsigned int chan = CR_CHAN(insn->chanspec);
- if (!mask)
- mask = chan_mask;
+ if (!mask && chan < 32)
+ mask = 1U << chan;
switch (data[0]) {
case INSN_CONFIG_DIO_INPUT:
@@ -382,7 +389,7 @@ EXPORT_SYMBOL_GPL(comedi_dio_insn_config);
unsigned int comedi_dio_update_state(struct comedi_subdevice *s,
unsigned int *data)
{
- unsigned int chanmask = (s->n_chan < 32) ? ((1 << s->n_chan) - 1)
+ unsigned int chanmask = (s->n_chan < 32) ? ((1U << s->n_chan) - 1)
: 0xffffffff;
unsigned int mask = data[0] & chanmask;
unsigned int bits = data[1];
@@ -434,6 +441,13 @@ unsigned int comedi_bytes_per_scan_cmd(struct comedi_subdevice *s,
}
EXPORT_SYMBOL_GPL(comedi_bytes_per_scan_cmd);
+static unsigned int _comedi_bytes_per_scan(struct comedi_subdevice *s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ return comedi_bytes_per_scan_cmd(s, cmd);
+}
+
/**
* comedi_bytes_per_scan() - Get length of asynchronous command "scan" in bytes
* @s: COMEDI subdevice.
@@ -451,9 +465,16 @@ EXPORT_SYMBOL_GPL(comedi_bytes_per_scan_cmd);
*/
unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s)
{
- struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int num_bytes;
- return comedi_bytes_per_scan_cmd(s, cmd);
+ if (comedi_get_is_subdevice_running(s)) {
+ num_bytes = _comedi_bytes_per_scan(s);
+ comedi_put_is_subdevice_running(s);
+ } else {
+ /* Use nomimal, single sample scan length. */
+ num_bytes = comedi_samples_to_bytes(s, 1);
+ }
+ return num_bytes;
}
EXPORT_SYMBOL_GPL(comedi_bytes_per_scan);
@@ -475,6 +496,17 @@ static unsigned int __comedi_nscans_left(struct comedi_subdevice *s,
return nscans;
}
+static unsigned int _comedi_nscans_left(struct comedi_subdevice *s,
+ unsigned int nscans)
+{
+ if (nscans == 0) {
+ unsigned int nbytes = _comedi_buf_read_n_available(s);
+
+ nscans = nbytes / _comedi_bytes_per_scan(s);
+ }
+ return __comedi_nscans_left(s, nscans);
+}
+
/**
* comedi_nscans_left() - Return the number of scans left in the command
* @s: COMEDI subdevice.
@@ -492,25 +524,18 @@ static unsigned int __comedi_nscans_left(struct comedi_subdevice *s,
unsigned int comedi_nscans_left(struct comedi_subdevice *s,
unsigned int nscans)
{
- if (nscans == 0) {
- unsigned int nbytes = comedi_buf_read_n_available(s);
-
- nscans = nbytes / comedi_bytes_per_scan(s);
+ if (comedi_get_is_subdevice_running(s)) {
+ nscans = _comedi_nscans_left(s, nscans);
+ comedi_put_is_subdevice_running(s);
+ } else {
+ nscans = 0;
}
- return __comedi_nscans_left(s, nscans);
+ return nscans;
}
EXPORT_SYMBOL_GPL(comedi_nscans_left);
-/**
- * comedi_nsamples_left() - Return the number of samples left in the command
- * @s: COMEDI subdevice.
- * @nsamples: The expected number of samples.
- *
- * Returns the number of samples remaining to complete the command, or the
- * specified expected number of samples (@nsamples), whichever is fewer.
- */
-unsigned int comedi_nsamples_left(struct comedi_subdevice *s,
- unsigned int nsamples)
+static unsigned int _comedi_nsamples_left(struct comedi_subdevice *s,
+ unsigned int nsamples)
{
struct comedi_async *async = s->async;
struct comedi_cmd *cmd = &async->cmd;
@@ -531,24 +556,34 @@ unsigned int comedi_nsamples_left(struct comedi_subdevice *s,
return samples_left;
return nsamples;
}
-EXPORT_SYMBOL_GPL(comedi_nsamples_left);
/**
- * comedi_inc_scan_progress() - Update scan progress in asynchronous command
+ * comedi_nsamples_left() - Return the number of samples left in the command
* @s: COMEDI subdevice.
- * @num_bytes: Amount of data in bytes to increment scan progress.
+ * @nsamples: The expected number of samples.
*
- * Increments the scan progress by the number of bytes specified by @num_bytes.
- * If the scan progress reaches or exceeds the scan length in bytes, reduce
- * it modulo the scan length in bytes and set the "end of scan" asynchronous
- * event flag (%COMEDI_CB_EOS) to be processed later.
+ * Returns the number of samples remaining to complete the command, or the
+ * specified expected number of samples (@nsamples), whichever is fewer.
*/
-void comedi_inc_scan_progress(struct comedi_subdevice *s,
- unsigned int num_bytes)
+unsigned int comedi_nsamples_left(struct comedi_subdevice *s,
+ unsigned int nsamples)
+{
+ if (comedi_get_is_subdevice_running(s)) {
+ nsamples = _comedi_nsamples_left(s, nsamples);
+ comedi_put_is_subdevice_running(s);
+ } else {
+ nsamples = 0;
+ }
+ return nsamples;
+}
+EXPORT_SYMBOL_GPL(comedi_nsamples_left);
+
+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);
+ unsigned int scan_length = _comedi_bytes_per_scan(s);
/* track the 'cur_chan' for non-SDF_PACKED subdevices */
if (!(s->subdev_flags & SDF_PACKED)) {
@@ -569,8 +604,43 @@ void comedi_inc_scan_progress(struct comedi_subdevice *s,
async->events |= COMEDI_CB_EOS;
}
}
+
+/**
+ * comedi_inc_scan_progress() - Update scan progress in asynchronous command
+ * @s: COMEDI subdevice.
+ * @num_bytes: Amount of data in bytes to increment scan progress.
+ *
+ * Increments the scan progress by the number of bytes specified by @num_bytes.
+ * If the scan progress reaches or exceeds the scan length in bytes, reduce
+ * it modulo the scan length in bytes and set the "end of scan" asynchronous
+ * event flag (%COMEDI_CB_EOS) to be processed later.
+ */
+void comedi_inc_scan_progress(struct comedi_subdevice *s,
+ unsigned int num_bytes)
+{
+ if (comedi_get_is_subdevice_running(s)) {
+ _comedi_inc_scan_progress(s, num_bytes);
+ comedi_put_is_subdevice_running(s);
+ }
+}
EXPORT_SYMBOL_GPL(comedi_inc_scan_progress);
+static unsigned int _comedi_handle_events(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned int events = s->async->events;
+
+ if (events == 0)
+ return events;
+
+ if ((events & COMEDI_CB_CANCEL_MASK) && s->cancel)
+ s->cancel(dev, s);
+
+ _comedi_event(dev, s);
+
+ return events;
+}
+
/**
* comedi_handle_events() - Handle events and possibly stop acquisition
* @dev: COMEDI device.
@@ -590,16 +660,14 @@ EXPORT_SYMBOL_GPL(comedi_inc_scan_progress);
unsigned int comedi_handle_events(struct comedi_device *dev,
struct comedi_subdevice *s)
{
- unsigned int events = s->async->events;
-
- if (events == 0)
- return events;
-
- if ((events & COMEDI_CB_CANCEL_MASK) && s->cancel)
- s->cancel(dev, s);
-
- comedi_event(dev, s);
+ unsigned int events;
+ if (comedi_get_is_subdevice_running(s)) {
+ events = _comedi_handle_events(dev, s);
+ comedi_put_is_subdevice_running(s);
+ } else {
+ events = 0;
+ }
return events;
}
EXPORT_SYMBOL_GPL(comedi_handle_events);
@@ -613,6 +681,7 @@ 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;
memset(_data, 0, sizeof(_data));
@@ -625,18 +694,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] = 1 << (chan - base_chan); /* mask */
- _data[1] = data[0] ? (1 << (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,
@@ -666,6 +738,7 @@ static int __comedi_device_postconfig_async(struct comedi_device *dev,
return -ENOMEM;
init_waitqueue_head(&async->wait_head);
+ init_completion(&async->run_complete);
s->async = async;
async->max_bufsize = comedi_default_buf_maxsize_kb * 1024;
@@ -709,7 +782,7 @@ static int __comedi_device_postconfig(struct comedi_device *dev)
if (s->type == COMEDI_SUBD_DO) {
if (s->n_chan < 32)
- s->io_bits = (1 << s->n_chan) - 1;
+ s->io_bits = (1U << s->n_chan) - 1;
else
s->io_bits = 0xffffffff;
}