diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/usbduxsigma.c')
-rw-r--r-- | drivers/staging/comedi/drivers/usbduxsigma.c | 317 |
1 files changed, 138 insertions, 179 deletions
diff --git a/drivers/staging/comedi/drivers/usbduxsigma.c b/drivers/staging/comedi/drivers/usbduxsigma.c index 898c3c450406..c47f4087568f 100644 --- a/drivers/staging/comedi/drivers/usbduxsigma.c +++ b/drivers/staging/comedi/drivers/usbduxsigma.c @@ -66,13 +66,6 @@ /* internal addresses of the 8051 processor */ #define USBDUXSUB_CPUCS 0xE600 -/* USB endpoints */ -#define USBDUXSIGMA_CMD_OUT_EP 1 /* command output */ -#define USBDUXSIGMA_ISO_OUT_EP 2 /* analog output ISO/IRQ */ -#define USBDUXSIGMA_PWM_OUT_EP 4 /* pwm output */ -#define USBDUXSIGMA_ISO_IN_EP 6 /* analog input ISO/IRQ */ -#define USBDUXSIGMA_CMD_IN_EP 8 /* command input */ - /* 300Hz max frequ under PWM */ #define MIN_PWM_PERIOD ((long)(1E9/300)) @@ -168,6 +161,7 @@ struct usbduxsigma_private { /* input buffer for single insn */ int8_t *insn_buf; + int8_t ao_chanlist[USBDUXSIGMA_NUM_AO_CHAN]; unsigned int ao_readback[USBDUXSIGMA_NUM_AO_CHAN]; unsigned high_speed:1; @@ -188,25 +182,25 @@ struct usbduxsigma_private { unsigned int ao_counter; /* interval in frames/uframes */ unsigned int ai_interval; - /* D/A commands */ - uint8_t *dac_commands; /* commands */ uint8_t *dux_commands; struct semaphore sem; }; +static void usbduxsigma_unlink_urbs(struct urb **urbs, int num_urbs) +{ + int i; + + for (i = 0; i < num_urbs; i++) + usb_kill_urb(urbs[i]); +} + static void usbduxsigma_ai_stop(struct comedi_device *dev, int do_unlink) { struct usbduxsigma_private *devpriv = dev->private; - if (do_unlink) { - int i; - - for (i = 0; i < devpriv->n_ai_urbs; i++) { - if (devpriv->ai_urbs[i]) - usb_kill_urb(devpriv->ai_urbs[i]); - } - } + if (do_unlink && devpriv->ai_urbs) + usbduxsigma_unlink_urbs(devpriv->ai_urbs, devpriv->n_ai_urbs); devpriv->ai_cmd_running = 0; } @@ -342,14 +336,8 @@ static void usbduxsigma_ao_stop(struct comedi_device *dev, int do_unlink) { struct usbduxsigma_private *devpriv = dev->private; - if (do_unlink) { - int i; - - for (i = 0; i < devpriv->n_ao_urbs; i++) { - if (devpriv->ao_urbs[i]) - usb_kill_urb(devpriv->ao_urbs[i]); - } - } + if (do_unlink && devpriv->ao_urbs) + usbduxsigma_unlink_urbs(devpriv->ao_urbs, devpriv->n_ao_urbs); devpriv->ao_cmd_running = 0; } @@ -432,7 +420,7 @@ static void usbduxsigma_ao_urb_complete(struct urb *urb) len = s->async->cmd.chanlist_len; *datap++ = len; for (i = 0; i < len; i++) { - unsigned int chan = devpriv->dac_commands[i]; + unsigned int chan = devpriv->ao_chanlist[i]; short val; ret = comedi_buf_get(s->async, &val); @@ -643,7 +631,7 @@ static int usbbuxsigma_send_cmd(struct comedi_device *dev, int cmd_type) devpriv->dux_commands[0] = cmd_type; - return usb_bulk_msg(usb, usb_sndbulkpipe(usb, USBDUXSIGMA_CMD_OUT_EP), + return usb_bulk_msg(usb, usb_sndbulkpipe(usb, 1), devpriv->dux_commands, SIZEOFDUXBUFFER, &nsent, BULK_TIMEOUT); } @@ -657,8 +645,7 @@ static int usbduxsigma_receive_cmd(struct comedi_device *dev, int command) int i; for (i = 0; i < RETRIES; i++) { - ret = usb_bulk_msg(usb, - usb_rcvbulkpipe(usb, USBDUXSIGMA_CMD_IN_EP), + ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, 8), devpriv->insn_buf, SIZEINSNBUF, &nrec, BULK_TIMEOUT); if (ret < 0) @@ -686,13 +673,14 @@ static int usbduxsigma_ai_inttrig(struct comedi_device *dev, down(&devpriv->sem); if (!devpriv->ai_cmd_running) { + devpriv->ai_cmd_running = 1; ret = usbduxsigma_submit_urbs(dev, devpriv->ai_urbs, devpriv->n_ai_urbs, 1); if (ret < 0) { + devpriv->ai_cmd_running = 0; up(&devpriv->sem); return ret; } - devpriv->ai_cmd_running = 1; s->async->inttrig = NULL; } up(&devpriv->sem); @@ -740,14 +728,15 @@ static int usbduxsigma_ai_cmd(struct comedi_device *dev, if (cmd->start_src == TRIG_NOW) { /* enable this acquisition operation */ + devpriv->ai_cmd_running = 1; ret = usbduxsigma_submit_urbs(dev, devpriv->ai_urbs, devpriv->n_ai_urbs, 1); if (ret < 0) { + devpriv->ai_cmd_running = 0; up(&devpriv->sem); return ret; } s->async->inttrig = NULL; - devpriv->ai_cmd_running = 1; } else { /* TRIG_INT */ /* wait for an internal signal and submit the urbs later */ s->async->inttrig = usbduxsigma_ai_inttrig; @@ -876,13 +865,14 @@ static int usbduxsigma_ao_inttrig(struct comedi_device *dev, down(&devpriv->sem); if (!devpriv->ao_cmd_running) { + devpriv->ao_cmd_running = 1; ret = usbduxsigma_submit_urbs(dev, devpriv->ao_urbs, devpriv->n_ao_urbs, 0); if (ret < 0) { + devpriv->ao_cmd_running = 0; up(&devpriv->sem); return ret; } - devpriv->ao_cmd_running = 1; s->async->inttrig = NULL; } up(&devpriv->sem); @@ -1020,20 +1010,21 @@ static int usbduxsigma_ao_cmd(struct comedi_device *dev, /* set current channel of the running acquisition to zero */ s->async->cur_chan = 0; for (i = 0; i < cmd->chanlist_len; ++i) - devpriv->dac_commands[i] = CR_CHAN(cmd->chanlist[i]); + devpriv->ao_chanlist[i] = CR_CHAN(cmd->chanlist[i]); devpriv->ao_counter = devpriv->ao_timer; if (cmd->start_src == TRIG_NOW) { /* enable this acquisition operation */ + devpriv->ao_cmd_running = 1; ret = usbduxsigma_submit_urbs(dev, devpriv->ao_urbs, devpriv->n_ao_urbs, 0); if (ret < 0) { + devpriv->ao_cmd_running = 0; up(&devpriv->sem); return ret; } s->async->inttrig = NULL; - devpriv->ao_cmd_running = 1; } else { /* TRIG_INT */ /* wait for an internal signal and submit the urbs later */ s->async->inttrig = usbduxsigma_ao_inttrig; @@ -1049,23 +1040,11 @@ static int usbduxsigma_dio_insn_config(struct comedi_device *dev, struct comedi_insn *insn, unsigned int *data) { - unsigned int chan = CR_CHAN(insn->chanspec); - unsigned int mask = 1 << chan; + int ret; - switch (data[0]) { - case INSN_CONFIG_DIO_OUTPUT: - s->io_bits |= mask; - break; - case INSN_CONFIG_DIO_INPUT: - s->io_bits &= ~mask; - break; - case INSN_CONFIG_DIO_QUERY: - data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT; - break; - default: - return -EINVAL; - break; - } + ret = comedi_dio_insn_config(dev, s, insn, data, 0); + if (ret) + return ret; /* * We don't tell the firmware here as it would take 8 frames @@ -1194,8 +1173,7 @@ static int usbduxsigma_submit_pwm_urb(struct comedi_device *dev) struct urb *urb = devpriv->pwm_urb; /* in case of a resubmission after an unlink... */ - usb_fill_bulk_urb(urb, - usb, usb_sndbulkpipe(usb, USBDUXSIGMA_PWM_OUT_EP), + usb_fill_bulk_urb(urb, usb, usb_sndbulkpipe(usb, 4), urb->transfer_buffer, devpriv->pwm_buf_sz, usbduxsigma_pwm_urb_complete, dev); @@ -1237,19 +1215,21 @@ static int usbduxsigma_pwm_start(struct comedi_device *dev, memset(devpriv->pwm_urb->transfer_buffer, 0, devpriv->pwm_buf_sz); + devpriv->pwm_cmd_running = 1; ret = usbduxsigma_submit_pwm_urb(dev); - if (ret < 0) + if (ret < 0) { + devpriv->pwm_cmd_running = 0; return ret; - devpriv->pwm_cmd_running = 1; + } return 0; } -static int usbduxsigma_pwm_pattern(struct comedi_device *dev, - struct comedi_subdevice *s, - unsigned int chan, - unsigned int value, - unsigned int sign) +static void usbduxsigma_pwm_pattern(struct comedi_device *dev, + struct comedi_subdevice *s, + unsigned int chan, + unsigned int value, + unsigned int sign) { struct usbduxsigma_private *devpriv = dev->private; char pwm_mask = (1 << chan); /* DIO bit for the PWM data */ @@ -1270,7 +1250,6 @@ static int usbduxsigma_pwm_pattern(struct comedi_device *dev, c |= sgn_mask; *buf++ = c; } - return 1; } static int usbduxsigma_pwm_write(struct comedi_device *dev, @@ -1291,7 +1270,9 @@ static int usbduxsigma_pwm_write(struct comedi_device *dev, * The sign is set via a special INSN only, this gives us 8 bits * for normal operation, sign is 0 by default. */ - return usbduxsigma_pwm_pattern(dev, s, chan, data[0], 0); + usbduxsigma_pwm_pattern(dev, s, chan, data[0], 0); + + return insn->n; } static int usbduxsigma_pwm_config(struct comedi_device *dev, @@ -1326,8 +1307,8 @@ static int usbduxsigma_pwm_config(struct comedi_device *dev, * data[1] = value * data[2] = sign (for a relay) */ - return usbduxsigma_pwm_pattern(dev, s, chan, - data[1], (data[2] != 0)); + usbduxsigma_pwm_pattern(dev, s, chan, data[1], (data[2] != 0)); + return 0; case INSN_CONFIG_PWM_GET_H_BRIDGE: /* values are not kept in this driver, nothing to return */ return -EINVAL; @@ -1386,90 +1367,6 @@ static int usbduxsigma_getstatusinfo(struct comedi_device *dev, int chan) return (int)val; } -static int usbduxsigma_attach_common(struct comedi_device *dev) -{ - struct usbduxsigma_private *devpriv = dev->private; - struct comedi_subdevice *s; - int n_subdevs; - int offset; - int ret; - - down(&devpriv->sem); - - if (devpriv->high_speed) - n_subdevs = 4; /* with pwm */ - else - n_subdevs = 3; /* without pwm */ - ret = comedi_alloc_subdevices(dev, n_subdevs); - 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 | SDF_LSAMPL; - s->n_chan = NUMCHANNELS; - s->len_chanlist = NUMCHANNELS; - s->maxdata = 0x00ffffff; - s->range_table = &usbduxsigma_ai_range; - s->insn_read = usbduxsigma_ai_insn_read; - s->do_cmdtest = usbduxsigma_ai_cmdtest; - s->do_cmd = usbduxsigma_ai_cmd; - s->cancel = usbduxsigma_ai_cancel; - - /* Analog Output subdevice */ - s = &dev->subdevices[1]; - dev->write_subdev = s; - s->type = COMEDI_SUBD_AO; - s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_CMD_WRITE; - s->n_chan = USBDUXSIGMA_NUM_AO_CHAN; - s->len_chanlist = s->n_chan; - s->maxdata = 0x00ff; - s->range_table = &range_unipolar2_5; - s->insn_write = usbduxsigma_ao_insn_write; - s->insn_read = usbduxsigma_ao_insn_read; - s->do_cmdtest = usbduxsigma_ao_cmdtest; - s->do_cmd = usbduxsigma_ao_cmd; - s->cancel = usbduxsigma_ao_cancel; - - /* Digital I/O subdevice */ - s = &dev->subdevices[2]; - s->type = COMEDI_SUBD_DIO; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE; - s->n_chan = 24; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_bits = usbduxsigma_dio_insn_bits; - s->insn_config = usbduxsigma_dio_insn_config; - - if (devpriv->high_speed) { - /* Timer / pwm subdevice */ - s = &dev->subdevices[3]; - s->type = COMEDI_SUBD_PWM; - s->subdev_flags = SDF_WRITABLE | SDF_PWM_HBRIDGE; - s->n_chan = 8; - s->maxdata = devpriv->pwm_buf_sz; - s->insn_write = usbduxsigma_pwm_write; - s->insn_config = usbduxsigma_pwm_config; - - usbduxsigma_pwm_period(dev, s, PWM_DEFAULT_PERIOD); - } - - up(&devpriv->sem); - - offset = usbduxsigma_getstatusinfo(dev, 0); - if (offset < 0) - dev_err(dev->class_dev, - "Communication to USBDUXSIGMA failed! Check firmware and cabling\n"); - - dev_info(dev->class_dev, "attached, ADC_zero = %x\n", offset); - - return 0; -} - static int usbduxsigma_firmware_upload(struct comedi_device *dev, const u8 *data, size_t size, unsigned long context) @@ -1548,7 +1445,6 @@ static int usbduxsigma_alloc_usb_buffers(struct comedi_device *dev) struct urb *urb; int i; - devpriv->dac_commands = kzalloc(NUMOUTCHANNELS, GFP_KERNEL); devpriv->dux_commands = kzalloc(SIZEOFDUXBUFFER, GFP_KERNEL); devpriv->in_buf = kzalloc(SIZEINBUF, GFP_KERNEL); devpriv->insn_buf = kzalloc(SIZEINSNBUF, GFP_KERNEL); @@ -1556,8 +1452,7 @@ static int usbduxsigma_alloc_usb_buffers(struct comedi_device *dev) GFP_KERNEL); devpriv->ao_urbs = kcalloc(devpriv->n_ao_urbs, sizeof(*urb), GFP_KERNEL); - if (!devpriv->dac_commands || !devpriv->dux_commands || - !devpriv->in_buf || !devpriv->insn_buf || + if (!devpriv->dux_commands || !devpriv->in_buf || !devpriv->insn_buf || !devpriv->ai_urbs || !devpriv->ao_urbs) return -ENOMEM; @@ -1571,7 +1466,7 @@ static int usbduxsigma_alloc_usb_buffers(struct comedi_device *dev) /* will be filled later with a pointer to the comedi-device */ /* and ONLY then the urb should be submitted */ urb->context = NULL; - urb->pipe = usb_rcvisocpipe(usb, USBDUXSIGMA_ISO_IN_EP); + urb->pipe = usb_rcvisocpipe(usb, 6); urb->transfer_flags = URB_ISO_ASAP; urb->transfer_buffer = kzalloc(SIZEINBUF, GFP_KERNEL); if (!urb->transfer_buffer) @@ -1593,7 +1488,7 @@ static int usbduxsigma_alloc_usb_buffers(struct comedi_device *dev) /* will be filled later with a pointer to the comedi-device */ /* and ONLY then the urb should be submitted */ urb->context = NULL; - urb->pipe = usb_sndisocpipe(usb, USBDUXSIGMA_ISO_OUT_EP); + urb->pipe = usb_sndisocpipe(usb, 2); urb->transfer_flags = URB_ISO_ASAP; urb->transfer_buffer = kzalloc(SIZEOUTBUF, GFP_KERNEL); if (!urb->transfer_buffer) @@ -1609,19 +1504,16 @@ static int usbduxsigma_alloc_usb_buffers(struct comedi_device *dev) urb->interval = 1; /* frames */ } - if (devpriv->high_speed) { - /* max bulk ep size in high speed */ - devpriv->pwm_buf_sz = 512; + if (devpriv->pwm_buf_sz) { urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return -ENOMEM; devpriv->pwm_urb = urb; - urb->transfer_buffer = kzalloc(devpriv->pwm_buf_sz, GFP_KERNEL); + + urb->transfer_buffer = kzalloc(devpriv->pwm_buf_sz, + GFP_KERNEL); if (!urb->transfer_buffer) return -ENOMEM; - } else { - devpriv->pwm_urb = NULL; - devpriv->pwm_buf_sz = 0; } return 0; @@ -1633,11 +1525,6 @@ static void usbduxsigma_free_usb_buffers(struct comedi_device *dev) struct urb *urb; int i; - /* force unlink all urbs */ - usbduxsigma_ai_stop(dev, 1); - usbduxsigma_ao_stop(dev, 1); - usbduxsigma_pwm_stop(dev, 1); - urb = devpriv->pwm_urb; if (urb) { kfree(urb->transfer_buffer); @@ -1666,7 +1553,6 @@ static void usbduxsigma_free_usb_buffers(struct comedi_device *dev) kfree(devpriv->insn_buf); kfree(devpriv->in_buf); kfree(devpriv->dux_commands); - kfree(devpriv->dac_commands); } static int usbduxsigma_auto_attach(struct comedi_device *dev, @@ -1675,29 +1561,23 @@ static int usbduxsigma_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 usbduxsigma_private *devpriv; + struct comedi_subdevice *s; + int offset; int ret; - devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL); + devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); if (!devpriv) return -ENOMEM; - dev->private = devpriv; sema_init(&devpriv->sem, 1); - usb_set_intfdata(intf, devpriv); - ret = usb_set_interface(usb, - intf->altsetting->desc.bInterfaceNumber, 3); - if (ret < 0) { - dev_err(dev->class_dev, - "could not set alternate setting 3 in high speed\n"); - return -ENODEV; - } + usb_set_intfdata(intf, devpriv); - /* test if it is high speed (USB 2.0) */ devpriv->high_speed = (usb->speed == USB_SPEED_HIGH); if (devpriv->high_speed) { devpriv->n_ai_urbs = NUMOFINBUFFERSHIGH; devpriv->n_ao_urbs = NUMOFOUTBUFFERSHIGH; + devpriv->pwm_buf_sz = 512; } else { devpriv->n_ai_urbs = NUMOFINBUFFERSFULL; devpriv->n_ao_urbs = NUMOFOUTBUFFERSFULL; @@ -1707,12 +1587,84 @@ static int usbduxsigma_auto_attach(struct comedi_device *dev, if (ret) return ret; + /* setting to alternate setting 3: enabling iso ep and bulk ep. */ + ret = usb_set_interface(usb, intf->altsetting->desc.bInterfaceNumber, + 3); + if (ret < 0) { + dev_err(dev->class_dev, + "could not set alternate setting 3 in high speed\n"); + return ret; + } + ret = comedi_load_firmware(dev, &usb->dev, FIRMWARE, usbduxsigma_firmware_upload, 0); if (ret) return ret; - return usbduxsigma_attach_common(dev); + ret = comedi_alloc_subdevices(dev, (devpriv->high_speed) ? 4 : 3); + 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 | SDF_LSAMPL; + s->n_chan = NUMCHANNELS; + s->len_chanlist = NUMCHANNELS; + s->maxdata = 0x00ffffff; + s->range_table = &usbduxsigma_ai_range; + s->insn_read = usbduxsigma_ai_insn_read; + s->do_cmdtest = usbduxsigma_ai_cmdtest; + s->do_cmd = usbduxsigma_ai_cmd; + s->cancel = usbduxsigma_ai_cancel; + + /* Analog Output subdevice */ + s = &dev->subdevices[1]; + dev->write_subdev = s; + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_CMD_WRITE; + s->n_chan = USBDUXSIGMA_NUM_AO_CHAN; + s->len_chanlist = s->n_chan; + s->maxdata = 0x00ff; + s->range_table = &range_unipolar2_5; + s->insn_write = usbduxsigma_ao_insn_write; + s->insn_read = usbduxsigma_ao_insn_read; + s->do_cmdtest = usbduxsigma_ao_cmdtest; + s->do_cmd = usbduxsigma_ao_cmd; + s->cancel = usbduxsigma_ao_cancel; + + /* Digital I/O subdevice */ + s = &dev->subdevices[2]; + s->type = COMEDI_SUBD_DIO; + s->subdev_flags = SDF_READABLE | SDF_WRITABLE; + s->n_chan = 24; + s->maxdata = 1; + s->range_table = &range_digital; + s->insn_bits = usbduxsigma_dio_insn_bits; + s->insn_config = usbduxsigma_dio_insn_config; + + if (devpriv->high_speed) { + /* Timer / pwm subdevice */ + s = &dev->subdevices[3]; + s->type = COMEDI_SUBD_PWM; + s->subdev_flags = SDF_WRITABLE | SDF_PWM_HBRIDGE; + s->n_chan = 8; + s->maxdata = devpriv->pwm_buf_sz; + s->insn_write = usbduxsigma_pwm_write; + s->insn_config = usbduxsigma_pwm_config; + + usbduxsigma_pwm_period(dev, s, PWM_DEFAULT_PERIOD); + } + + offset = usbduxsigma_getstatusinfo(dev, 0); + if (offset < 0) + dev_err(dev->class_dev, + "Communication to USBDUXSIGMA failed! Check firmware and cabling\n"); + + dev_info(dev->class_dev, "attached, ADC_zero = %x\n", offset); + + return 0; } static void usbduxsigma_detach(struct comedi_device *dev) @@ -1720,13 +1672,20 @@ static void usbduxsigma_detach(struct comedi_device *dev) struct usb_interface *intf = comedi_to_usb_interface(dev); struct usbduxsigma_private *devpriv = dev->private; + usb_set_intfdata(intf, NULL); + if (!devpriv) return; - usb_set_intfdata(intf, NULL); - down(&devpriv->sem); + + /* force unlink all urbs */ + usbduxsigma_ai_stop(dev, 1); + usbduxsigma_ao_stop(dev, 1); + usbduxsigma_pwm_stop(dev, 1); + usbduxsigma_free_usb_buffers(dev); + up(&devpriv->sem); } |