diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/dt9812.c')
-rw-r--r-- | drivers/staging/comedi/drivers/dt9812.c | 1031 |
1 files changed, 391 insertions, 640 deletions
diff --git a/drivers/staging/comedi/drivers/dt9812.c b/drivers/staging/comedi/drivers/dt9812.c index 81eb5ed6ec97..6c60949d9193 100644 --- a/drivers/staging/comedi/drivers/dt9812.c +++ b/drivers/staging/comedi/drivers/dt9812.c @@ -15,11 +15,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * */ /* @@ -43,14 +38,11 @@ for my needs. * says P1). */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> -#include <linux/kref.h> #include <linux/uaccess.h> #include <linux/usb.h> @@ -60,6 +52,9 @@ for my needs. #define DT9812_MAX_WRITE_CMD_PIPE_SIZE 32 #define DT9812_MAX_READ_CMD_PIPE_SIZE 32 +/* usb_bulk_msg() timout in milliseconds */ +#define DT9812_USB_TIMEOUT 1000 + /* * See Silican Laboratories C8051F020/1/2/3 manual */ @@ -242,87 +237,25 @@ struct dt9812_usb_cmd { struct dt9812_write_multi write_multi_info; struct dt9812_rmw_multi rmw_multi_info; } u; -#if 0 - WRITE_BYTE_INFO WriteByteInfo; - READ_BYTE_INFO ReadByteInfo; - WRITE_MULTI_INFO WriteMultiInfo; - READ_MULTI_INFO ReadMultiInfo; - RMW_BYTE_INFO RMWByteInfo; - RMW_MULTI_INFO RMWMultiInfo; - DAC_THRESHOLD_INFO DacThresholdInfo; - INT_ON_CHANGE_MASK_INFO IntOnChangeMaskInfo; - CGL_INFO CglInfo; - SUBSYSTEM_INFO SubsystemInfo; - CAL_POT_CMD CalPotCmd; - WRITE_DEV_BYTE_INFO WriteDevByteInfo; - READ_DEV_BYTE_INFO ReadDevByteInfo; - WRITE_DEV_MULTI_INFO WriteDevMultiInfo; - READ_DEV_MULTI_INFO ReadDevMultiInfo; - READ_SINGLE_VALUE_INFO ReadSingleValueInfo; - WRITE_SINGLE_VALUE_INFO WriteSingleValueInfo; -#endif }; -#define DT9812_NUM_SLOTS 16 - -static DEFINE_SEMAPHORE(dt9812_mutex); - -static const struct usb_device_id dt9812_table[] = { - {USB_DEVICE(0x0867, 0x9812)}, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, dt9812_table); - -struct usb_dt9812 { - struct slot_dt9812 *slot; - struct usb_device *udev; - struct usb_interface *interface; - u16 vendor; - u16 product; - u16 device; - u32 serial; +struct dt9812_private { + struct semaphore sem; struct { __u8 addr; size_t size; - } message_pipe, command_write, command_read, write_stream, read_stream; - struct kref kref; - u16 analog_out_shadow[2]; - u8 digital_out_shadow; -}; - -struct comedi_dt9812 { - struct slot_dt9812 *slot; - u32 serial; -}; - -struct slot_dt9812 { - struct semaphore mutex; - u32 serial; - struct usb_dt9812 *usb; - struct comedi_dt9812 *comedi; + } cmd_wr, cmd_rd; + u16 device; + u16 ao_shadow[2]; }; -static struct slot_dt9812 dt9812[DT9812_NUM_SLOTS]; - -static inline struct usb_dt9812 *to_dt9812_dev(struct kref *d) -{ - return container_of(d, struct usb_dt9812, kref); -} - -static void dt9812_delete(struct kref *kref) -{ - struct usb_dt9812 *dev = to_dt9812_dev(kref); - - usb_put_dev(dev->udev); - kfree(dev); -} - -static int dt9812_read_info(struct usb_dt9812 *dev, int offset, void *buf, - size_t buf_size) +static int dt9812_read_info(struct comedi_device *dev, + int offset, void *buf, size_t buf_size) { + struct usb_device *usb = comedi_to_usb_dev(dev); + struct dt9812_private *devpriv = dev->private; struct dt9812_usb_cmd cmd; - int count, retval; + int count, ret; cmd.cmd = cpu_to_le32(DT9812_R_FLASH_DATA); cmd.u.flash_data_info.address = @@ -330,25 +263,23 @@ static int dt9812_read_info(struct usb_dt9812 *dev, int offset, void *buf, cmd.u.flash_data_info.numbytes = cpu_to_le16(buf_size); /* DT9812 only responds to 32 byte writes!! */ - count = 32; - retval = usb_bulk_msg(dev->udev, - usb_sndbulkpipe(dev->udev, - dev->command_write.addr), - &cmd, 32, &count, HZ * 1); - if (retval) - return retval; - retval = usb_bulk_msg(dev->udev, - usb_rcvbulkpipe(dev->udev, - dev->command_read.addr), - buf, buf_size, &count, HZ * 1); - return retval; + ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr), + &cmd, 32, &count, DT9812_USB_TIMEOUT); + if (ret) + return ret; + + return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr), + buf, buf_size, &count, DT9812_USB_TIMEOUT); } -static int dt9812_read_multiple_registers(struct usb_dt9812 *dev, int reg_count, - u8 *address, u8 *value) +static int dt9812_read_multiple_registers(struct comedi_device *dev, + int reg_count, u8 *address, + u8 *value) { + struct usb_device *usb = comedi_to_usb_dev(dev); + struct dt9812_private *devpriv = dev->private; struct dt9812_usb_cmd cmd; - int i, count, retval; + int i, count, ret; cmd.cmd = cpu_to_le32(DT9812_R_MULTI_BYTE_REG); cmd.u.read_multi_info.count = reg_count; @@ -356,26 +287,23 @@ static int dt9812_read_multiple_registers(struct usb_dt9812 *dev, int reg_count, cmd.u.read_multi_info.address[i] = address[i]; /* DT9812 only responds to 32 byte writes!! */ - count = 32; - retval = usb_bulk_msg(dev->udev, - usb_sndbulkpipe(dev->udev, - dev->command_write.addr), - &cmd, 32, &count, HZ * 1); - if (retval) - return retval; - retval = usb_bulk_msg(dev->udev, - usb_rcvbulkpipe(dev->udev, - dev->command_read.addr), - value, reg_count, &count, HZ * 1); - return retval; + ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr), + &cmd, 32, &count, DT9812_USB_TIMEOUT); + if (ret) + return ret; + + return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr), + value, reg_count, &count, DT9812_USB_TIMEOUT); } -static int dt9812_write_multiple_registers(struct usb_dt9812 *dev, +static int dt9812_write_multiple_registers(struct comedi_device *dev, int reg_count, u8 *address, u8 *value) { + struct usb_device *usb = comedi_to_usb_dev(dev); + struct dt9812_private *devpriv = dev->private; struct dt9812_usb_cmd cmd; - int i, count, retval; + int i, count; cmd.cmd = cpu_to_le32(DT9812_W_MULTI_BYTE_REG); cmd.u.read_multi_info.count = reg_count; @@ -383,19 +311,20 @@ static int dt9812_write_multiple_registers(struct usb_dt9812 *dev, cmd.u.write_multi_info.write[i].address = address[i]; cmd.u.write_multi_info.write[i].value = value[i]; } + /* DT9812 only responds to 32 byte writes!! */ - retval = usb_bulk_msg(dev->udev, - usb_sndbulkpipe(dev->udev, - dev->command_write.addr), - &cmd, 32, &count, HZ * 1); - return retval; + return usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr), + &cmd, 32, &count, DT9812_USB_TIMEOUT); } -static int dt9812_rmw_multiple_registers(struct usb_dt9812 *dev, int reg_count, +static int dt9812_rmw_multiple_registers(struct comedi_device *dev, + int reg_count, struct dt9812_rmw_byte *rmw) { + struct usb_device *usb = comedi_to_usb_dev(dev); + struct dt9812_private *devpriv = dev->private; struct dt9812_usb_cmd cmd; - int i, count, retval; + int i, count; cmd.cmd = cpu_to_le32(DT9812_RMW_MULTI_BYTE_REG); cmd.u.rmw_multi_info.count = reg_count; @@ -403,76 +332,52 @@ static int dt9812_rmw_multiple_registers(struct usb_dt9812 *dev, int reg_count, cmd.u.rmw_multi_info.rmw[i] = rmw[i]; /* DT9812 only responds to 32 byte writes!! */ - retval = usb_bulk_msg(dev->udev, - usb_sndbulkpipe(dev->udev, - dev->command_write.addr), - &cmd, 32, &count, HZ * 1); - return retval; + return usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr), + &cmd, 32, &count, DT9812_USB_TIMEOUT); } -static int dt9812_digital_in(struct slot_dt9812 *slot, u8 *bits) +static int dt9812_digital_in(struct comedi_device *dev, u8 *bits) { - int result = -ENODEV; - - down(&slot->mutex); - if (slot->usb) { - u8 reg[2] = { F020_SFR_P3, F020_SFR_P1 }; - u8 value[2]; + struct dt9812_private *devpriv = dev->private; + u8 reg[2] = { F020_SFR_P3, F020_SFR_P1 }; + u8 value[2]; + int ret; - result = dt9812_read_multiple_registers(slot->usb, 2, reg, - value); - if (result == 0) { - /* - * bits 0-6 in F020_SFR_P3 are bits 0-6 in the digital - * input port bit 3 in F020_SFR_P1 is bit 7 in the - * digital input port - */ - *bits = (value[0] & 0x7f) | ((value[1] & 0x08) << 4); - /* printk("%2.2x, %2.2x -> %2.2x\n", - value[0], value[1], *bits); */ - } + down(&devpriv->sem); + ret = dt9812_read_multiple_registers(dev, 2, reg, value); + if (ret == 0) { + /* + * bits 0-6 in F020_SFR_P3 are bits 0-6 in the digital + * input port bit 3 in F020_SFR_P1 is bit 7 in the + * digital input port + */ + *bits = (value[0] & 0x7f) | ((value[1] & 0x08) << 4); } - up(&slot->mutex); + up(&devpriv->sem); - return result; + return ret; } -static int dt9812_digital_out(struct slot_dt9812 *slot, u8 bits) +static int dt9812_digital_out(struct comedi_device *dev, u8 bits) { - int result = -ENODEV; - - down(&slot->mutex); - if (slot->usb) { - u8 reg[1]; - u8 value[1]; - - reg[0] = F020_SFR_P2; - value[0] = bits; - result = dt9812_write_multiple_registers(slot->usb, 1, reg, - value); - slot->usb->digital_out_shadow = bits; - } - up(&slot->mutex); - return result; -} + struct dt9812_private *devpriv = dev->private; + u8 reg[1] = { F020_SFR_P2 }; + u8 value[1] = { bits }; + int ret; -static int dt9812_digital_out_shadow(struct slot_dt9812 *slot, u8 *bits) -{ - int result = -ENODEV; + down(&devpriv->sem); + ret = dt9812_write_multiple_registers(dev, 1, reg, value); + up(&devpriv->sem); - down(&slot->mutex); - if (slot->usb) { - *bits = slot->usb->digital_out_shadow; - result = 0; - } - up(&slot->mutex); - return result; + return ret; } -static void dt9812_configure_mux(struct usb_dt9812 *dev, +static void dt9812_configure_mux(struct comedi_device *dev, struct dt9812_rmw_byte *rmw, int channel) { - if (dev->device == DT9812_DEVID_DT9812_10) { + struct dt9812_private *devpriv = dev->private; + + if (devpriv->device == DT9812_DEVID_DT9812_10) { /* In the DT9812/10V MUX is selected by P1.5-7 */ rmw->address = F020_SFR_P1; rmw->and_mask = 0xe0; @@ -485,18 +390,21 @@ static void dt9812_configure_mux(struct usb_dt9812 *dev, } } -static void dt9812_configure_gain(struct usb_dt9812 *dev, +static void dt9812_configure_gain(struct comedi_device *dev, struct dt9812_rmw_byte *rmw, enum dt9812_gain gain) { - if (dev->device == DT9812_DEVID_DT9812_10) { - /* In the DT9812/10V, there is an external gain of 0.5 */ + struct dt9812_private *devpriv = dev->private; + + /* In the DT9812/10V, there is an external gain of 0.5 */ + if (devpriv->device == DT9812_DEVID_DT9812_10) gain <<= 1; - } rmw->address = F020_SFR_ADC0CF; rmw->and_mask = F020_MASK_ADC0CF_AMP0GN2 | - F020_MASK_ADC0CF_AMP0GN1 | F020_MASK_ADC0CF_AMP0GN0; + F020_MASK_ADC0CF_AMP0GN1 | + F020_MASK_ADC0CF_AMP0GN0; + switch (gain) { /* * 000 -> Gain = 1 @@ -508,8 +416,10 @@ static void dt9812_configure_gain(struct usb_dt9812 *dev, */ case DT9812_GAIN_0PT5: rmw->or_value = F020_MASK_ADC0CF_AMP0GN2 | - F020_MASK_ADC0CF_AMP0GN1; + F020_MASK_ADC0CF_AMP0GN1; break; + default: + /* this should never happen, just use a gain of 1 */ case DT9812_GAIN_1: rmw->or_value = 0x00; break; @@ -521,20 +431,18 @@ static void dt9812_configure_gain(struct usb_dt9812 *dev, break; case DT9812_GAIN_8: rmw->or_value = F020_MASK_ADC0CF_AMP0GN1 | - F020_MASK_ADC0CF_AMP0GN0; + F020_MASK_ADC0CF_AMP0GN0; break; case DT9812_GAIN_16: rmw->or_value = F020_MASK_ADC0CF_AMP0GN2; break; - default: - dev_err(&dev->interface->dev, "Illegal gain %d\n", gain); - } } -static int dt9812_analog_in(struct slot_dt9812 *slot, int channel, u16 *value, - enum dt9812_gain gain) +static int dt9812_analog_in(struct comedi_device *dev, + int channel, u16 *value, enum dt9812_gain gain) { + struct dt9812_private *devpriv = dev->private; struct dt9812_rmw_byte rmw[3]; u8 reg[3] = { F020_SFR_ADC0CN, @@ -542,31 +450,30 @@ static int dt9812_analog_in(struct slot_dt9812 *slot, int channel, u16 *value, F020_SFR_ADC0L }; u8 val[3]; - int result = -ENODEV; + int ret; - down(&slot->mutex); - if (!slot->usb) - goto exit; + down(&devpriv->sem); /* 1 select the gain */ - dt9812_configure_gain(slot->usb, &rmw[0], gain); + dt9812_configure_gain(dev, &rmw[0], gain); /* 2 set the MUX to select the channel */ - dt9812_configure_mux(slot->usb, &rmw[1], channel); + dt9812_configure_mux(dev, &rmw[1], channel); /* 3 start conversion */ rmw[2].address = F020_SFR_ADC0CN; rmw[2].and_mask = 0xff; rmw[2].or_value = F020_MASK_ADC0CN_AD0EN | F020_MASK_ADC0CN_AD0BUSY; - result = dt9812_rmw_multiple_registers(slot->usb, 3, rmw); - if (result) + ret = dt9812_rmw_multiple_registers(dev, 3, rmw); + if (ret) goto exit; /* read the status and ADC */ - result = dt9812_read_multiple_registers(slot->usb, 3, reg, val); - if (result) + ret = dt9812_read_multiple_registers(dev, 3, reg, val); + if (ret) goto exit; + /* * An ADC conversion takes 16 SAR clocks cycles, i.e. about 9us. * Therefore, between the instant that AD0BUSY was set via @@ -578,7 +485,7 @@ static int dt9812_analog_in(struct slot_dt9812 *slot, int channel, u16 *value, */ if ((val[0] & (F020_MASK_ADC0CN_AD0INT | F020_MASK_ADC0CN_AD0BUSY)) == F020_MASK_ADC0CN_AD0INT) { - switch (slot->usb->device) { + switch (devpriv->device) { case DT9812_DEVID_DT9812_10: /* * For DT9812-10V the personality module set the @@ -594,422 +501,284 @@ static int dt9812_analog_in(struct slot_dt9812 *slot, int channel, u16 *value, } exit: - up(&slot->mutex); - return result; + up(&devpriv->sem); + + return ret; } -static int dt9812_analog_out_shadow(struct slot_dt9812 *slot, int channel, - u16 *value) +static int dt9812_analog_out(struct comedi_device *dev, int channel, u16 value) { - int result = -ENODEV; + struct dt9812_private *devpriv = dev->private; + struct dt9812_rmw_byte rmw[3]; + int ret; - down(&slot->mutex); - if (slot->usb) { - *value = slot->usb->analog_out_shadow[channel]; - result = 0; + down(&devpriv->sem); + + switch (channel) { + case 0: + /* 1. Set DAC mode */ + rmw[0].address = F020_SFR_DAC0CN; + rmw[0].and_mask = 0xff; + rmw[0].or_value = F020_MASK_DACxCN_DACxEN; + + /* 2 load low byte of DAC value first */ + rmw[1].address = F020_SFR_DAC0L; + rmw[1].and_mask = 0xff; + rmw[1].or_value = value & 0xff; + + /* 3 load high byte of DAC value next to latch the + 12-bit value */ + rmw[2].address = F020_SFR_DAC0H; + rmw[2].and_mask = 0xff; + rmw[2].or_value = (value >> 8) & 0xf; + break; + + case 1: + /* 1. Set DAC mode */ + rmw[0].address = F020_SFR_DAC1CN; + rmw[0].and_mask = 0xff; + rmw[0].or_value = F020_MASK_DACxCN_DACxEN; + + /* 2 load low byte of DAC value first */ + rmw[1].address = F020_SFR_DAC1L; + rmw[1].and_mask = 0xff; + rmw[1].or_value = value & 0xff; + + /* 3 load high byte of DAC value next to latch the + 12-bit value */ + rmw[2].address = F020_SFR_DAC1H; + rmw[2].and_mask = 0xff; + rmw[2].or_value = (value >> 8) & 0xf; + break; } - up(&slot->mutex); + ret = dt9812_rmw_multiple_registers(dev, 3, rmw); + devpriv->ao_shadow[channel] = value; - return result; + up(&devpriv->sem); + + return ret; } -static int dt9812_analog_out(struct slot_dt9812 *slot, int channel, u16 value) +static int dt9812_di_insn_bits(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) { - int result = -ENODEV; + u8 bits = 0; + int ret; - down(&slot->mutex); - if (slot->usb) { - struct dt9812_rmw_byte rmw[3]; + ret = dt9812_digital_in(dev, &bits); + if (ret) + return ret; - switch (channel) { - case 0: - /* 1. Set DAC mode */ - rmw[0].address = F020_SFR_DAC0CN; - rmw[0].and_mask = 0xff; - rmw[0].or_value = F020_MASK_DACxCN_DACxEN; - - /* 2 load low byte of DAC value first */ - rmw[1].address = F020_SFR_DAC0L; - rmw[1].and_mask = 0xff; - rmw[1].or_value = value & 0xff; - - /* 3 load high byte of DAC value next to latch the - 12-bit value */ - rmw[2].address = F020_SFR_DAC0H; - rmw[2].and_mask = 0xff; - rmw[2].or_value = (value >> 8) & 0xf; - break; + data[1] = bits; - case 1: - /* 1. Set DAC mode */ - rmw[0].address = F020_SFR_DAC1CN; - rmw[0].and_mask = 0xff; - rmw[0].or_value = F020_MASK_DACxCN_DACxEN; - - /* 2 load low byte of DAC value first */ - rmw[1].address = F020_SFR_DAC1L; - rmw[1].and_mask = 0xff; - rmw[1].or_value = value & 0xff; - - /* 3 load high byte of DAC value next to latch the - 12-bit value */ - rmw[2].address = F020_SFR_DAC1H; - rmw[2].and_mask = 0xff; - rmw[2].or_value = (value >> 8) & 0xf; - break; - } - result = dt9812_rmw_multiple_registers(slot->usb, 3, rmw); - slot->usb->analog_out_shadow[channel] = value; + return insn->n; +} + +static int dt9812_do_insn_bits(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + unsigned int mask = data[0]; + unsigned int bits = data[1]; + + if (mask) { + s->state &= ~mask; + s->state |= (bits & mask); + + dt9812_digital_out(dev, s->state); } - up(&slot->mutex); - return result; + data[1] = s->state; + + return insn->n; } -/* - * USB framework functions - */ +static int dt9812_ai_insn_read(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + unsigned int chan = CR_CHAN(insn->chanspec); + u16 val = 0; + int ret; + int i; -static int dt9812_probe(struct usb_interface *interface, - const struct usb_device_id *id) + for (i = 0; i < insn->n; i++) { + ret = dt9812_analog_in(dev, chan, &val, DT9812_GAIN_1); + if (ret) + return ret; + data[i] = val; + } + + return insn->n; +} + +static int dt9812_ao_insn_read(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) { - int retval = -ENOMEM; - struct usb_dt9812 *dev = NULL; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; + struct dt9812_private *devpriv = dev->private; + unsigned int chan = CR_CHAN(insn->chanspec); int i; - u8 fw; - /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) - goto error; + down(&devpriv->sem); + for (i = 0; i < insn->n; i++) + data[i] = devpriv->ao_shadow[chan]; + up(&devpriv->sem); + + return insn->n; +} + +static int dt9812_ao_insn_write(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + unsigned int chan = CR_CHAN(insn->chanspec); + int ret; + int i; - kref_init(&dev->kref); + for (i = 0; i < insn->n; i++) { + ret = dt9812_analog_out(dev, chan, data[i]); + if (ret) + return ret; + } - dev->udev = usb_get_dev(interface_to_usbdev(interface)); - dev->interface = interface; + return insn->n; +} - /* Check endpoints */ - iface_desc = interface->cur_altsetting; +static int dt9812_find_endpoints(struct comedi_device *dev) +{ + struct usb_interface *intf = comedi_to_usb_interface(dev); + struct usb_host_interface *host = intf->cur_altsetting; + struct dt9812_private *devpriv = dev->private; + struct usb_endpoint_descriptor *ep; + int i; - if (iface_desc->desc.bNumEndpoints != 5) { - dev_err(&interface->dev, "Wrong number of endpoints.\n"); - retval = -ENODEV; - goto error; + if (host->desc.bNumEndpoints != 5) { + dev_err(dev->class_dev, "Wrong number of endpoints\n"); + return -ENODEV; } - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - int direction = -1; - endpoint = &iface_desc->endpoint[i].desc; + for (i = 0; i < host->desc.bNumEndpoints; ++i) { + int dir = -1; + ep = &host->endpoint[i].desc; switch (i) { case 0: - direction = USB_DIR_IN; - dev->message_pipe.addr = endpoint->bEndpointAddress; - dev->message_pipe.size = - le16_to_cpu(endpoint->wMaxPacketSize); - + /* unused message pipe */ + dir = USB_DIR_IN; break; case 1: - direction = USB_DIR_OUT; - dev->command_write.addr = endpoint->bEndpointAddress; - dev->command_write.size = - le16_to_cpu(endpoint->wMaxPacketSize); + dir = USB_DIR_OUT; + devpriv->cmd_wr.addr = ep->bEndpointAddress; + devpriv->cmd_wr.size = le16_to_cpu(ep->wMaxPacketSize); break; case 2: - direction = USB_DIR_IN; - dev->command_read.addr = endpoint->bEndpointAddress; - dev->command_read.size = - le16_to_cpu(endpoint->wMaxPacketSize); + dir = USB_DIR_IN; + devpriv->cmd_rd.addr = ep->bEndpointAddress; + devpriv->cmd_rd.size = le16_to_cpu(ep->wMaxPacketSize); break; case 3: - direction = USB_DIR_OUT; - dev->write_stream.addr = endpoint->bEndpointAddress; - dev->write_stream.size = - le16_to_cpu(endpoint->wMaxPacketSize); + /* unused write stream */ + dir = USB_DIR_OUT; break; case 4: - direction = USB_DIR_IN; - dev->read_stream.addr = endpoint->bEndpointAddress; - dev->read_stream.size = - le16_to_cpu(endpoint->wMaxPacketSize); + /* unused read stream */ + dir = USB_DIR_IN; break; } - if ((endpoint->bEndpointAddress & USB_DIR_IN) != direction) { - dev_err(&interface->dev, - "Endpoint has wrong direction.\n"); - retval = -ENODEV; - goto error; + if ((ep->bEndpointAddress & USB_DIR_IN) != dir) { + dev_err(dev->class_dev, + "Endpoint has wrong direction\n"); + return -ENODEV; } } - if (dt9812_read_info(dev, 0, &fw, sizeof(fw)) != 0) { + return 0; +} + +static int dt9812_reset_device(struct comedi_device *dev) +{ + struct usb_device *usb = comedi_to_usb_dev(dev); + struct dt9812_private *devpriv = dev->private; + u32 serial; + u16 vendor; + u16 product; + u16 tmp16; + u8 tmp8; + int ret; + int i; + + ret = dt9812_read_info(dev, 0, &tmp8, sizeof(tmp8)); + if (ret) { /* * Seems like a configuration reset is necessary if driver is * reloaded while device is attached */ - usb_reset_configuration(dev->udev); + usb_reset_configuration(usb); for (i = 0; i < 10; i++) { - retval = dt9812_read_info(dev, 1, &fw, sizeof(fw)); - if (retval == 0) { - dev_info(&interface->dev, - "usb_reset_configuration succeeded " - "after %d iterations\n", i); + ret = dt9812_read_info(dev, 1, &tmp8, sizeof(tmp8)); + if (ret == 0) break; - } } - } - - if (dt9812_read_info(dev, 1, &dev->vendor, sizeof(dev->vendor)) != 0) { - dev_err(&interface->dev, "Failed to read vendor.\n"); - retval = -ENODEV; - goto error; - } - if (dt9812_read_info(dev, 3, &dev->product, sizeof(dev->product)) != 0) { - dev_err(&interface->dev, "Failed to read product.\n"); - retval = -ENODEV; - goto error; - } - if (dt9812_read_info(dev, 5, &dev->device, sizeof(dev->device)) != 0) { - dev_err(&interface->dev, "Failed to read device.\n"); - retval = -ENODEV; - goto error; - } - if (dt9812_read_info(dev, 7, &dev->serial, sizeof(dev->serial)) != 0) { - dev_err(&interface->dev, "Failed to read serial.\n"); - retval = -ENODEV; - goto error; - } - - dev->vendor = le16_to_cpu(dev->vendor); - dev->product = le16_to_cpu(dev->product); - dev->device = le16_to_cpu(dev->device); - dev->serial = le32_to_cpu(dev->serial); - switch (dev->device) { - case DT9812_DEVID_DT9812_10: - dev->analog_out_shadow[0] = 0x0800; - dev->analog_out_shadow[1] = 0x800; - break; - case DT9812_DEVID_DT9812_2PT5: - dev->analog_out_shadow[0] = 0x0000; - dev->analog_out_shadow[1] = 0x0000; - break; - } - dev->digital_out_shadow = 0; - - /* save our data pointer in this interface device */ - usb_set_intfdata(interface, dev); - - /* let the user know what node this device is now attached to */ - dev_info(&interface->dev, "USB DT9812 (%4.4x.%4.4x.%4.4x) #0x%8.8x\n", - dev->vendor, dev->product, dev->device, dev->serial); - - down(&dt9812_mutex); - { - /* Find a slot for the USB device */ - struct slot_dt9812 *first = NULL; - struct slot_dt9812 *best = NULL; - - for (i = 0; i < DT9812_NUM_SLOTS; i++) { - if (!first && !dt9812[i].usb && dt9812[i].serial == 0) - first = &dt9812[i]; - if (!best && dt9812[i].serial == dev->serial) - best = &dt9812[i]; - } - - if (!best) - best = first; - - if (best) { - down(&best->mutex); - best->usb = dev; - dev->slot = best; - up(&best->mutex); + if (ret) { + dev_err(dev->class_dev, + "unable to reset configuration\n"); + return ret; } } - up(&dt9812_mutex); - - return 0; -error: - if (dev) - kref_put(&dev->kref, dt9812_delete); - return retval; -} - -static void dt9812_disconnect(struct usb_interface *interface) -{ - struct usb_dt9812 *dev; - int minor = interface->minor; - - down(&dt9812_mutex); - dev = usb_get_intfdata(interface); - if (dev->slot) { - down(&dev->slot->mutex); - dev->slot->usb = NULL; - up(&dev->slot->mutex); - dev->slot = NULL; + ret = dt9812_read_info(dev, 1, &vendor, sizeof(vendor)); + if (ret) { + dev_err(dev->class_dev, "failed to read vendor id\n"); + return ret; } - usb_set_intfdata(interface, NULL); - up(&dt9812_mutex); - - /* queue final destruction */ - kref_put(&dev->kref, dt9812_delete); - - dev_info(&interface->dev, "USB Dt9812 #%d now disconnected\n", minor); -} + vendor = le16_to_cpu(vendor); -static struct usb_driver dt9812_usb_driver = { - .name = "dt9812", - .probe = dt9812_probe, - .disconnect = dt9812_disconnect, - .id_table = dt9812_table, -}; - -/* - * Comedi functions - */ - -static int dt9812_comedi_open(struct comedi_device *dev) -{ - struct comedi_dt9812 *devpriv = dev->private; - int result = -ENODEV; - - down(&devpriv->slot->mutex); - if (devpriv->slot->usb) { - /* We have an attached device, fill in current range info */ - struct comedi_subdevice *s; - - s = &dev->subdevices[0]; - s->n_chan = 8; - s->maxdata = 1; - - s = &dev->subdevices[1]; - s->n_chan = 8; - s->maxdata = 1; - - s = &dev->subdevices[2]; - s->n_chan = 8; - switch (devpriv->slot->usb->device) { - case 0:{ - s->maxdata = 4095; - s->range_table = &range_bipolar10; - } - break; - case 1:{ - s->maxdata = 4095; - s->range_table = &range_unipolar2_5; - } - break; - } - - s = &dev->subdevices[3]; - s->n_chan = 2; - switch (devpriv->slot->usb->device) { - case 0:{ - s->maxdata = 4095; - s->range_table = &range_bipolar10; - } - break; - case 1:{ - s->maxdata = 4095; - s->range_table = &range_unipolar2_5; - } - break; - } - result = 0; + ret = dt9812_read_info(dev, 3, &product, sizeof(product)); + if (ret) { + dev_err(dev->class_dev, "failed to read product id\n"); + return ret; } - up(&devpriv->slot->mutex); - return result; -} - -static int dt9812_di_rinsn(struct comedi_device *dev, - struct comedi_subdevice *s, struct comedi_insn *insn, - unsigned int *data) -{ - struct comedi_dt9812 *devpriv = dev->private; - unsigned int channel = CR_CHAN(insn->chanspec); - int n; - u8 bits = 0; - - dt9812_digital_in(devpriv->slot, &bits); - for (n = 0; n < insn->n; n++) - data[n] = ((1 << channel) & bits) != 0; - return n; -} + product = le16_to_cpu(product); -static int dt9812_do_winsn(struct comedi_device *dev, - struct comedi_subdevice *s, struct comedi_insn *insn, - unsigned int *data) -{ - struct comedi_dt9812 *devpriv = dev->private; - unsigned int channel = CR_CHAN(insn->chanspec); - int n; - u8 bits = 0; - - dt9812_digital_out_shadow(devpriv->slot, &bits); - for (n = 0; n < insn->n; n++) { - u8 mask = 1 << channel; - - bits &= ~mask; - if (data[n]) - bits |= mask; + ret = dt9812_read_info(dev, 5, &tmp16, sizeof(tmp16)); + if (ret) { + dev_err(dev->class_dev, "failed to read device id\n"); + return ret; } - dt9812_digital_out(devpriv->slot, bits); - return n; -} + devpriv->device = le16_to_cpu(tmp16); -static int dt9812_ai_rinsn(struct comedi_device *dev, - struct comedi_subdevice *s, struct comedi_insn *insn, - unsigned int *data) -{ - struct comedi_dt9812 *devpriv = dev->private; - unsigned int channel = CR_CHAN(insn->chanspec); - int n; - - for (n = 0; n < insn->n; n++) { - u16 value = 0; - - dt9812_analog_in(devpriv->slot, channel, &value, DT9812_GAIN_1); - data[n] = value; + ret = dt9812_read_info(dev, 7, &serial, sizeof(serial)); + if (ret) { + dev_err(dev->class_dev, "failed to read serial number\n"); + return ret; } - return n; -} + serial = le32_to_cpu(serial); -static int dt9812_ao_rinsn(struct comedi_device *dev, - struct comedi_subdevice *s, struct comedi_insn *insn, - unsigned int *data) -{ - struct comedi_dt9812 *devpriv = dev->private; - unsigned int channel = CR_CHAN(insn->chanspec); - int n; - u16 value; - - for (n = 0; n < insn->n; n++) { - value = 0; - dt9812_analog_out_shadow(devpriv->slot, channel, &value); - data[n] = value; - } - return n; -} + /* let the user know what node this device is now attached to */ + dev_info(dev->class_dev, "USB DT9812 (%4.4x.%4.4x.%4.4x) #0x%8.8x\n", + vendor, product, devpriv->device, serial); -static int dt9812_ao_winsn(struct comedi_device *dev, - struct comedi_subdevice *s, struct comedi_insn *insn, - unsigned int *data) -{ - struct comedi_dt9812 *devpriv = dev->private; - unsigned int channel = CR_CHAN(insn->chanspec); - int n; + if (devpriv->device != DT9812_DEVID_DT9812_10 && + devpriv->device != DT9812_DEVID_DT9812_2PT5) { + dev_err(dev->class_dev, "Unsupported device!\n"); + return -EINVAL; + } - for (n = 0; n < insn->n; n++) - dt9812_analog_out(devpriv->slot, channel, data[n]); - return n; + return 0; } -static int dt9812_attach(struct comedi_device *dev, struct comedi_devconfig *it) +static int dt9812_auto_attach(struct comedi_device *dev, + unsigned long context) { - struct comedi_dt9812 *devpriv; - int i; + struct usb_interface *intf = comedi_to_usb_interface(dev); + struct dt9812_private *devpriv; struct comedi_subdevice *s; + bool is_unipolar; int ret; devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL); @@ -1017,125 +786,107 @@ static int dt9812_attach(struct comedi_device *dev, struct comedi_devconfig *it) return -ENOMEM; dev->private = devpriv; - /* - * Special open routine, since USB unit may be unattached at - * comedi_config time, hence range can not be determined - */ - dev->open = dt9812_comedi_open; + sema_init(&devpriv->sem, 1); + usb_set_intfdata(intf, devpriv); - devpriv->serial = it->options[0]; + ret = dt9812_find_endpoints(dev); + if (ret) + return ret; + + ret = dt9812_reset_device(dev); + if (ret) + return ret; + + is_unipolar = (devpriv->device == DT9812_DEVID_DT9812_2PT5); ret = comedi_alloc_subdevices(dev, 4); if (ret) return ret; - /* digital input subdevice */ + /* Digital Input subdevice */ s = &dev->subdevices[0]; - s->type = COMEDI_SUBD_DI; - s->subdev_flags = SDF_READABLE; - s->n_chan = 0; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_read = &dt9812_di_rinsn; - - /* digital output subdevice */ + s->type = COMEDI_SUBD_DI; + s->subdev_flags = SDF_READABLE; + s->n_chan = 8; + s->maxdata = 1; + s->range_table = &range_digital; + s->insn_bits = dt9812_di_insn_bits; + + /* Digital Output subdevice */ s = &dev->subdevices[1]; - s->type = COMEDI_SUBD_DO; - s->subdev_flags = SDF_WRITEABLE; - s->n_chan = 0; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_write = &dt9812_do_winsn; - - /* analog input subdevice */ + s->type = COMEDI_SUBD_DO; + s->subdev_flags = SDF_WRITEABLE; + s->n_chan = 8; + s->maxdata = 1; + s->range_table = &range_digital; + s->insn_bits = dt9812_do_insn_bits; + + /* Analog Input subdevice */ s = &dev->subdevices[2]; - s->type = COMEDI_SUBD_AI; - s->subdev_flags = SDF_READABLE | SDF_GROUND; - s->n_chan = 0; - s->maxdata = 1; - s->range_table = NULL; - s->insn_read = &dt9812_ai_rinsn; - - /* analog output subdevice */ + s->type = COMEDI_SUBD_AI; + s->subdev_flags = SDF_READABLE | SDF_GROUND; + s->n_chan = 8; + s->maxdata = 0x0fff; + s->range_table = is_unipolar ? &range_unipolar2_5 : &range_bipolar10; + s->insn_read = dt9812_ai_insn_read; + + /* Analog Output subdevice */ s = &dev->subdevices[3]; - s->type = COMEDI_SUBD_AO; - s->subdev_flags = SDF_WRITEABLE; - s->n_chan = 0; - s->maxdata = 1; - s->range_table = NULL; - s->insn_write = &dt9812_ao_winsn; - s->insn_read = &dt9812_ao_rinsn; - - dev_info(dev->class_dev, "successfully attached to dt9812.\n"); - - down(&dt9812_mutex); - /* Find a slot for the comedi device */ - { - struct slot_dt9812 *first = NULL; - struct slot_dt9812 *best = NULL; - for (i = 0; i < DT9812_NUM_SLOTS; i++) { - if (!first && !dt9812[i].comedi) { - /* First free slot from comedi side */ - first = &dt9812[i]; - } - if (!best && - dt9812[i].usb && - dt9812[i].usb->serial == devpriv->serial) { - /* We have an attaced device with matching ID */ - best = &dt9812[i]; - } - } - if (!best) - best = first; - if (best) { - down(&best->mutex); - best->comedi = devpriv; - best->serial = devpriv->serial; - devpriv->slot = best; - up(&best->mutex); - } - } - up(&dt9812_mutex); + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITEABLE; + s->n_chan = 2; + s->maxdata = 0x0fff; + s->range_table = is_unipolar ? &range_unipolar2_5 : &range_bipolar10; + s->insn_write = dt9812_ao_insn_write; + s->insn_read = dt9812_ao_insn_read; + + devpriv->ao_shadow[0] = is_unipolar ? 0x0000 : 0x0800; + devpriv->ao_shadow[1] = is_unipolar ? 0x0000 : 0x0800; return 0; } static void dt9812_detach(struct comedi_device *dev) { - /* Nothing to cleanup */ -} + struct usb_interface *intf = comedi_to_usb_interface(dev); + struct dt9812_private *devpriv = dev->private; -static struct comedi_driver dt9812_comedi_driver = { - .module = THIS_MODULE, - .driver_name = "dt9812", - .attach = dt9812_attach, - .detach = dt9812_detach, -}; + if (!devpriv) + return; -static int __init usb_dt9812_init(void) -{ - int i; + down(&devpriv->sem); - /* Initialize all driver slots */ - for (i = 0; i < DT9812_NUM_SLOTS; i++) { - sema_init(&dt9812[i].mutex, 1); - dt9812[i].serial = 0; - dt9812[i].usb = NULL; - dt9812[i].comedi = NULL; - } - dt9812[12].serial = 0x0; + usb_set_intfdata(intf, NULL); - return comedi_usb_driver_register(&dt9812_comedi_driver, - &dt9812_usb_driver); + up(&devpriv->sem); } -static void __exit usb_dt9812_exit(void) +static struct comedi_driver dt9812_driver = { + .driver_name = "dt9812", + .module = THIS_MODULE, + .auto_attach = dt9812_auto_attach, + .detach = dt9812_detach, +}; + +static int dt9812_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) { - comedi_usb_driver_unregister(&dt9812_comedi_driver, &dt9812_usb_driver); + return comedi_usb_auto_config(intf, &dt9812_driver, id->driver_info); } -module_init(usb_dt9812_init); -module_exit(usb_dt9812_exit); +static const struct usb_device_id dt9812_usb_table[] = { + { USB_DEVICE(0x0867, 0x9812) }, + { } +}; +MODULE_DEVICE_TABLE(usb, dt9812_usb_table); + +static struct usb_driver dt9812_usb_driver = { + .name = "dt9812", + .id_table = dt9812_usb_table, + .probe = dt9812_usb_probe, + .disconnect = comedi_usb_auto_unconfig, +}; +module_comedi_usb_driver(dt9812_driver, dt9812_usb_driver); MODULE_AUTHOR("Anders Blomdell <anders.blomdell@control.lth.se>"); MODULE_DESCRIPTION("Comedi DT9812 driver"); |