diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/usbduxfast.c')
-rw-r--r-- | drivers/staging/comedi/drivers/usbduxfast.c | 310 |
1 files changed, 122 insertions, 188 deletions
diff --git a/drivers/staging/comedi/drivers/usbduxfast.c b/drivers/staging/comedi/drivers/usbduxfast.c index d90dc59982be..10f94ec34536 100644 --- a/drivers/staging/comedi/drivers/usbduxfast.c +++ b/drivers/staging/comedi/drivers/usbduxfast.c @@ -97,7 +97,7 @@ /* * size of one A/D value */ -#define SIZEADIN (sizeof(int16_t)) +#define SIZEADIN (sizeof(s16)) /* * size of the input-buffer IN BYTES @@ -156,12 +156,11 @@ static const struct comedi_lrange range_usbduxfast_ai_range = { */ struct usbduxfast_private { struct urb *urb; /* BULK-transfer handling: urb */ - uint8_t *duxbuf; - int8_t *inbuf; + u8 *duxbuf; + s8 *inbuf; short int ai_cmd_running; /* asynchronous command is running */ - int ignore; /* counter which ignores the first - buffers */ - struct semaphore sem; + int ignore; /* counter which ignores the first buffers */ + struct mutex mut; }; /* @@ -190,8 +189,7 @@ static int usbduxfast_send_cmd(struct comedi_device *dev, int cmd_type) } static void usbduxfast_cmd_data(struct comedi_device *dev, int index, - uint8_t len, uint8_t op, uint8_t out, - uint8_t log) + u8 len, u8 op, u8 out, u8 log) { struct usbduxfast_private *devpriv = dev->private; @@ -223,12 +221,9 @@ static int usbduxfast_ai_cancel(struct comedi_device *dev, struct usbduxfast_private *devpriv = dev->private; int ret; - if (!devpriv) - return -EFAULT; - - down(&devpriv->sem); + mutex_lock(&devpriv->mut); ret = usbduxfast_ai_stop(dev, 1); - up(&devpriv->sem); + mutex_unlock(&devpriv->mut); return ret; } @@ -317,9 +312,6 @@ static int usbduxfast_submit_urb(struct comedi_device *dev) struct usbduxfast_private *devpriv = dev->private; int ret; - if (!devpriv) - return -EFAULT; - usb_fill_bulk_urb(devpriv->urb, usb, usb_rcvbulkpipe(usb, BULKINEP), devpriv->inbuf, SIZEINBUF, usbduxfast_ai_interrupt, dev); @@ -332,22 +324,50 @@ static int usbduxfast_submit_urb(struct comedi_device *dev) return 0; } +static int usbduxfast_ai_check_chanlist(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_cmd *cmd) +{ + unsigned int gain0 = CR_RANGE(cmd->chanlist[0]); + int i; + + if (cmd->chanlist_len > 3 && cmd->chanlist_len != 16) { + dev_err(dev->class_dev, "unsupported combination of channels\n"); + return -EINVAL; + } + + for (i = 0; i < cmd->chanlist_len; ++i) { + unsigned int chan = CR_CHAN(cmd->chanlist[i]); + unsigned int gain = CR_RANGE(cmd->chanlist[i]); + + if (chan != i) { + dev_err(dev->class_dev, + "channels are not consecutive\n"); + return -EINVAL; + } + if (gain != gain0 && cmd->chanlist_len > 3) { + dev_err(dev->class_dev, + "gain must be the same for all channels\n"); + return -EINVAL; + } + } + return 0; +} + static int usbduxfast_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_cmd *cmd) { int err = 0; - long int steps, tmp; - int min_sample_period; + unsigned int steps; + unsigned int arg; /* Step 1 : check if triggers are trivially valid */ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT | TRIG_INT); - err |= comedi_check_trigger_src(&cmd->scan_begin_src, - TRIG_FOLLOW | TRIG_EXT); - err |= comedi_check_trigger_src(&cmd->convert_src, - TRIG_TIMER | TRIG_EXT); + err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW); + err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER); err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); @@ -357,16 +377,10 @@ static int usbduxfast_ai_cmdtest(struct comedi_device *dev, /* Step 2a : make sure trigger sources are unique */ err |= comedi_check_trigger_is_unique(cmd->start_src); - err |= comedi_check_trigger_is_unique(cmd->scan_begin_src); - err |= comedi_check_trigger_is_unique(cmd->convert_src); err |= comedi_check_trigger_is_unique(cmd->stop_src); /* Step 2b : and mutually compatible */ - /* can't have external stop and start triggers at once */ - if (cmd->start_src == TRIG_EXT && cmd->stop_src == TRIG_EXT) - err |= -EINVAL; - if (err) return 2; @@ -377,47 +391,44 @@ static int usbduxfast_ai_cmdtest(struct comedi_device *dev, if (!cmd->chanlist_len) err |= -EINVAL; + /* external start trigger is only valid for 1 or 16 channels */ + if (cmd->start_src == TRIG_EXT && + cmd->chanlist_len != 1 && cmd->chanlist_len != 16) + err |= -EINVAL; + err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); - if (cmd->chanlist_len == 1) - min_sample_period = 1; - else - min_sample_period = MIN_SAMPLING_PERIOD; - - if (cmd->convert_src == TRIG_TIMER) { - steps = cmd->convert_arg * 30; - if (steps < (min_sample_period * 1000)) - steps = min_sample_period * 1000; - - if (steps > (MAX_SAMPLING_PERIOD * 1000)) - steps = MAX_SAMPLING_PERIOD * 1000; - - /* calc arg again */ - tmp = steps / 30; - err |= comedi_check_trigger_arg_is(&cmd->convert_arg, tmp); - } - - /* stop source */ - switch (cmd->stop_src) { - case TRIG_COUNT: + /* + * Validate the conversion timing: + * for 1 channel the timing in 30MHz "steps" is: + * steps <= MAX_SAMPLING_PERIOD + * for all other chanlist_len it is: + * MIN_SAMPLING_PERIOD <= steps <= MAX_SAMPLING_PERIOD + */ + steps = (cmd->convert_arg * 30) / 1000; + if (cmd->chanlist_len != 1) + err |= comedi_check_trigger_arg_min(&steps, + MIN_SAMPLING_PERIOD); + err |= comedi_check_trigger_arg_max(&steps, MAX_SAMPLING_PERIOD); + arg = (steps * 1000) / 30; + err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); + + if (cmd->stop_src == TRIG_COUNT) err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); - break; - case TRIG_NONE: + else /* TRIG_NONE */ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); - break; - /* - * TRIG_EXT doesn't care since it doesn't trigger - * off a numbered channel - */ - default: - break; - } if (err) return 3; - /* step 4: fix up any arguments */ + /* Step 4: fix up any arguments */ + + /* Step 5: check channel list if it exists */ + if (cmd->chanlist && cmd->chanlist_len > 0) + err |= usbduxfast_ai_check_chanlist(dev, s, cmd); + if (err) + return 5; return 0; } @@ -430,13 +441,10 @@ static int usbduxfast_ai_inttrig(struct comedi_device *dev, struct comedi_cmd *cmd = &s->async->cmd; int ret; - if (!devpriv) - return -EFAULT; - if (trig_num != cmd->start_arg) return -EINVAL; - down(&devpriv->sem); + mutex_lock(&devpriv->mut); if (!devpriv->ai_cmd_running) { devpriv->ai_cmd_running = 1; @@ -444,14 +452,14 @@ static int usbduxfast_ai_inttrig(struct comedi_device *dev, if (ret < 0) { dev_err(dev->class_dev, "urbSubmit: err=%d\n", ret); devpriv->ai_cmd_running = 0; - up(&devpriv->sem); + mutex_unlock(&devpriv->mut); return ret; } s->async->inttrig = NULL; } else { dev_err(dev->class_dev, "ai is already running\n"); } - up(&devpriv->sem); + mutex_unlock(&devpriv->mut); return 1; } @@ -460,19 +468,14 @@ static int usbduxfast_ai_cmd(struct comedi_device *dev, { struct usbduxfast_private *devpriv = dev->private; struct comedi_cmd *cmd = &s->async->cmd; - unsigned int chan, gain, rngmask = 0xff; - int i, j, ret; - int result; + unsigned int rngmask = 0xff; + int j, ret; long steps, steps_tmp; - if (!devpriv) - return -EFAULT; - - down(&devpriv->sem); + mutex_lock(&devpriv->mut); if (devpriv->ai_cmd_running) { - dev_err(dev->class_dev, "ai_cmd not possible\n"); - up(&devpriv->sem); - return -EBUSY; + ret = -EBUSY; + goto cmd_exit; } /* @@ -481,50 +484,7 @@ static int usbduxfast_ai_cmd(struct comedi_device *dev, */ devpriv->ignore = PACKETS_TO_IGNORE; - gain = CR_RANGE(cmd->chanlist[0]); - for (i = 0; i < cmd->chanlist_len; ++i) { - chan = CR_CHAN(cmd->chanlist[i]); - if (chan != i) { - dev_err(dev->class_dev, - "channels are not consecutive\n"); - up(&devpriv->sem); - return -EINVAL; - } - if ((gain != CR_RANGE(cmd->chanlist[i])) - && (cmd->chanlist_len > 3)) { - dev_err(dev->class_dev, - "gain must be the same for all channels\n"); - up(&devpriv->sem); - return -EINVAL; - } - if (i >= NUMCHANNELS) { - dev_err(dev->class_dev, "chanlist too long\n"); - break; - } - } - steps = 0; - if (cmd->convert_src == TRIG_TIMER) - steps = (cmd->convert_arg * 30) / 1000; - - if ((steps < MIN_SAMPLING_PERIOD) && (cmd->chanlist_len != 1)) { - dev_err(dev->class_dev, - "steps=%ld, scan_begin_arg=%d. Not properly tested by cmdtest?\n", - steps, cmd->scan_begin_arg); - up(&devpriv->sem); - return -EINVAL; - } - if (steps > MAX_SAMPLING_PERIOD) { - dev_err(dev->class_dev, "sampling rate too low\n"); - up(&devpriv->sem); - return -EINVAL; - } - if ((cmd->start_src == TRIG_EXT) && (cmd->chanlist_len != 1) - && (cmd->chanlist_len != 16)) { - dev_err(dev->class_dev, - "TRIG_EXT only with 1 or 16 channels possible\n"); - up(&devpriv->sem); - return -EINVAL; - } + steps = (cmd->convert_arg * 30) / 1000; switch (cmd->chanlist_len) { case 1: @@ -769,19 +729,12 @@ static int usbduxfast_ai_cmd(struct comedi_device *dev, usbduxfast_cmd_data(dev, 4, 0x09, 0x01, rngmask, 0xff); break; - - default: - dev_err(dev->class_dev, "unsupported combination of channels\n"); - up(&devpriv->sem); - return -EFAULT; } /* 0 means that the AD commands are sent */ - result = usbduxfast_send_cmd(dev, SENDADCOMMANDS); - if (result < 0) { - up(&devpriv->sem); - return result; - } + ret = usbduxfast_send_cmd(dev, SENDADCOMMANDS); + if (ret < 0) + goto cmd_exit; if ((cmd->start_src == TRIG_NOW) || (cmd->start_src == TRIG_EXT)) { /* enable this acquisition operation */ @@ -790,16 +743,17 @@ static int usbduxfast_ai_cmd(struct comedi_device *dev, if (ret < 0) { devpriv->ai_cmd_running = 0; /* fixme: unlink here?? */ - up(&devpriv->sem); - return ret; + goto cmd_exit; } s->async->inttrig = NULL; } else { /* TRIG_INT */ s->async->inttrig = usbduxfast_ai_inttrig; } - up(&devpriv->sem); - return 0; +cmd_exit: + mutex_unlock(&devpriv->mut); + + return ret; } /* @@ -814,16 +768,16 @@ static int usbduxfast_ai_insn_read(struct comedi_device *dev, struct usbduxfast_private *devpriv = dev->private; unsigned int chan = CR_CHAN(insn->chanspec); unsigned int range = CR_RANGE(insn->chanspec); - uint8_t rngmask = range ? (0xff - 0x04) : 0xff; + u8 rngmask = range ? (0xff - 0x04) : 0xff; int i, j, n, actual_length; int ret; - down(&devpriv->sem); + mutex_lock(&devpriv->mut); if (devpriv->ai_cmd_running) { dev_err(dev->class_dev, "ai_insn_read not possible, async cmd is running\n"); - up(&devpriv->sem); + mutex_unlock(&devpriv->mut); return -EBUSY; } @@ -845,7 +799,7 @@ static int usbduxfast_ai_insn_read(struct comedi_device *dev, ret = usbduxfast_send_cmd(dev, SENDADCOMMANDS); if (ret < 0) { - up(&devpriv->sem); + mutex_unlock(&devpriv->mut); return ret; } @@ -855,7 +809,7 @@ static int usbduxfast_ai_insn_read(struct comedi_device *dev, &actual_length, 10000); if (ret < 0) { dev_err(dev->class_dev, "insn timeout, no data\n"); - up(&devpriv->sem); + mutex_unlock(&devpriv->mut); return ret; } } @@ -866,65 +820,32 @@ static int usbduxfast_ai_insn_read(struct comedi_device *dev, &actual_length, 10000); if (ret < 0) { dev_err(dev->class_dev, "insn data error: %d\n", ret); - up(&devpriv->sem); + mutex_unlock(&devpriv->mut); return ret; } - n = actual_length / sizeof(uint16_t); + n = actual_length / sizeof(u16); if ((n % 16) != 0) { dev_err(dev->class_dev, "insn data packet corrupted\n"); - up(&devpriv->sem); + mutex_unlock(&devpriv->mut); return -EINVAL; } for (j = chan; (j < n) && (i < insn->n); j = j + 16) { - data[i] = ((uint16_t *) (devpriv->inbuf))[j]; + data[i] = ((u16 *)(devpriv->inbuf))[j]; i++; } } - up(&devpriv->sem); + mutex_unlock(&devpriv->mut); return insn->n; } -static int usbduxfast_attach_common(struct comedi_device *dev) -{ - struct usbduxfast_private *devpriv = dev->private; - struct comedi_subdevice *s; - int ret; - - down(&devpriv->sem); - - ret = comedi_alloc_subdevices(dev, 1); - if (ret) { - up(&devpriv->sem); - return ret; - } - - /* Analog Input subdevice */ - s = &dev->subdevices[0]; - dev->read_subdev = s; - s->type = COMEDI_SUBD_AI; - s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ; - s->n_chan = 16; - s->len_chanlist = 16; - s->insn_read = usbduxfast_ai_insn_read; - s->do_cmdtest = usbduxfast_ai_cmdtest; - s->do_cmd = usbduxfast_ai_cmd; - s->cancel = usbduxfast_ai_cancel; - s->maxdata = 0x1000; - s->range_table = &range_usbduxfast_ai_range; - - up(&devpriv->sem); - - return 0; -} - static int usbduxfast_upload_firmware(struct comedi_device *dev, const u8 *data, size_t size, unsigned long context) { struct usb_device *usb = comedi_to_usb_dev(dev); - uint8_t *buf; + u8 *buf; unsigned char *tmp; int ret; @@ -996,6 +917,7 @@ static int usbduxfast_auto_attach(struct comedi_device *dev, struct usb_interface *intf = comedi_to_usb_interface(dev); struct usb_device *usb = comedi_to_usb_dev(dev); struct usbduxfast_private *devpriv; + struct comedi_subdevice *s; int ret; if (usb->speed != USB_SPEED_HIGH) { @@ -1008,7 +930,7 @@ static int usbduxfast_auto_attach(struct comedi_device *dev, if (!devpriv) return -ENOMEM; - sema_init(&devpriv->sem, 1); + mutex_init(&devpriv->mut); usb_set_intfdata(intf, devpriv); devpriv->duxbuf = kmalloc(SIZEOFDUXBUF, GFP_KERNEL); @@ -1038,7 +960,25 @@ static int usbduxfast_auto_attach(struct comedi_device *dev, if (ret) return ret; - return usbduxfast_attach_common(dev); + ret = comedi_alloc_subdevices(dev, 1); + if (ret) + return ret; + + /* Analog Input subdevice */ + s = &dev->subdevices[0]; + dev->read_subdev = s; + s->type = COMEDI_SUBD_AI; + s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ; + s->n_chan = 16; + s->maxdata = 0x1000; /* 12-bit + 1 overflow bit */ + s->range_table = &range_usbduxfast_ai_range; + s->insn_read = usbduxfast_ai_insn_read; + s->len_chanlist = s->n_chan; + s->do_cmdtest = usbduxfast_ai_cmdtest; + s->do_cmd = usbduxfast_ai_cmd; + s->cancel = usbduxfast_ai_cancel; + + return 0; } static void usbduxfast_detach(struct comedi_device *dev) @@ -1049,7 +989,7 @@ static void usbduxfast_detach(struct comedi_device *dev) if (!devpriv) return; - down(&devpriv->sem); + mutex_lock(&devpriv->mut); usb_set_intfdata(intf, NULL); @@ -1058,18 +998,12 @@ static void usbduxfast_detach(struct comedi_device *dev) usb_kill_urb(devpriv->urb); kfree(devpriv->inbuf); - devpriv->inbuf = NULL; - usb_free_urb(devpriv->urb); - devpriv->urb = NULL; } kfree(devpriv->duxbuf); - devpriv->duxbuf = NULL; - - devpriv->ai_cmd_running = 0; - up(&devpriv->sem); + mutex_unlock(&devpriv->mut); } static struct comedi_driver usbduxfast_driver = { |