staging: comedi: dt9812: convert to use comedi (*auto_attach)
Converting this driver to use the comedi (*auto_attach) mechanism allows pushing the usb (*probe) into the comedi (*auto_attach) and the usb (disconnect) into the comedi (*detach). This removes the disconnect between the usb driver and the comedi driver. Now when the comedi driver is attached it will always have a usb device associated with it. This removes the 16 usb device limitation and allows bringing all the private data into a single struct that can be kzalloc'ed when the comedi driver is (*auto_attached). It also allows removing the the sanity checks that make sure a usb device is connected to the comedi device in the helper functions. For aesthetic reasons, add some whitespace to the subdevice init. Also, fix the analog out subdevice. There are 2 analog output channels available on the usb device. Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com> Cc: Ian Abbott <abbotti@mev.co.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
8db1eba1e2
commit
b78750c123
1 changed files with 168 additions and 280 deletions
|
@ -45,7 +45,6 @@ for my needs.
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/kref.h>
|
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
|
|
||||||
|
@ -258,55 +257,26 @@ struct dt9812_usb_cmd {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DT9812_NUM_SLOTS 16
|
struct dt9812_private {
|
||||||
|
struct semaphore sem;
|
||||||
static DEFINE_SEMAPHORE(dt9812_mutex);
|
|
||||||
|
|
||||||
struct usb_dt9812 {
|
|
||||||
struct slot_dt9812 *slot;
|
|
||||||
struct usb_device *udev;
|
|
||||||
u16 vendor;
|
|
||||||
u16 product;
|
|
||||||
u16 device;
|
|
||||||
u32 serial;
|
|
||||||
struct {
|
struct {
|
||||||
__u8 addr;
|
__u8 addr;
|
||||||
size_t size;
|
size_t size;
|
||||||
} cmd_wr, cmd_rd;
|
} cmd_wr, cmd_rd;
|
||||||
struct kref kref;
|
u32 serial;
|
||||||
};
|
u16 vendor;
|
||||||
|
u16 product;
|
||||||
struct dt9812_private {
|
u16 device;
|
||||||
struct semaphore sem;
|
|
||||||
struct slot_dt9812 *slot;
|
|
||||||
u16 ao_shadow[2];
|
u16 ao_shadow[2];
|
||||||
u8 do_shadow;
|
u8 do_shadow;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct slot_dt9812 {
|
static int dt9812_read_info(struct comedi_device *dev,
|
||||||
struct usb_dt9812 *usb;
|
int offset, void *buf, size_t buf_size)
|
||||||
struct dt9812_private *devpriv;
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
struct usb_interface *intf = comedi_to_usb_interface(dev);
|
||||||
}
|
struct usb_device *usb = interface_to_usbdev(intf);
|
||||||
|
struct dt9812_private *devpriv = dev->private;
|
||||||
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)
|
|
||||||
{
|
|
||||||
struct usb_device *usb = dev->udev;
|
|
||||||
struct dt9812_usb_cmd cmd;
|
struct dt9812_usb_cmd cmd;
|
||||||
int count, ret;
|
int count, ret;
|
||||||
|
|
||||||
|
@ -316,19 +286,22 @@ static int dt9812_read_info(struct usb_dt9812 *dev, int offset, void *buf,
|
||||||
cmd.u.flash_data_info.numbytes = cpu_to_le16(buf_size);
|
cmd.u.flash_data_info.numbytes = cpu_to_le16(buf_size);
|
||||||
|
|
||||||
/* DT9812 only responds to 32 byte writes!! */
|
/* DT9812 only responds to 32 byte writes!! */
|
||||||
ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, dev->cmd_wr.addr),
|
ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
|
||||||
&cmd, 32, &count, HZ * 1);
|
&cmd, 32, &count, HZ * 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, dev->cmd_rd.addr),
|
return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr),
|
||||||
buf, buf_size, &count, HZ * 1);
|
buf, buf_size, &count, HZ * 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dt9812_read_multiple_registers(struct usb_dt9812 *dev, int reg_count,
|
static int dt9812_read_multiple_registers(struct comedi_device *dev,
|
||||||
u8 *address, u8 *value)
|
int reg_count, u8 *address,
|
||||||
|
u8 *value)
|
||||||
{
|
{
|
||||||
struct usb_device *usb = dev->udev;
|
struct usb_interface *intf = comedi_to_usb_interface(dev);
|
||||||
|
struct usb_device *usb = interface_to_usbdev(intf);
|
||||||
|
struct dt9812_private *devpriv = dev->private;
|
||||||
struct dt9812_usb_cmd cmd;
|
struct dt9812_usb_cmd cmd;
|
||||||
int i, count, ret;
|
int i, count, ret;
|
||||||
|
|
||||||
|
@ -338,20 +311,22 @@ static int dt9812_read_multiple_registers(struct usb_dt9812 *dev, int reg_count,
|
||||||
cmd.u.read_multi_info.address[i] = address[i];
|
cmd.u.read_multi_info.address[i] = address[i];
|
||||||
|
|
||||||
/* DT9812 only responds to 32 byte writes!! */
|
/* DT9812 only responds to 32 byte writes!! */
|
||||||
ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, dev->cmd_wr.addr),
|
ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
|
||||||
&cmd, 32, &count, HZ * 1);
|
&cmd, 32, &count, HZ * 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, dev->cmd_rd.addr),
|
return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr),
|
||||||
value, reg_count, &count, HZ * 1);
|
value, reg_count, &count, HZ * 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
int reg_count, u8 *address,
|
||||||
u8 *value)
|
u8 *value)
|
||||||
{
|
{
|
||||||
struct usb_device *usb = dev->udev;
|
struct usb_interface *intf = comedi_to_usb_interface(dev);
|
||||||
|
struct usb_device *usb = interface_to_usbdev(intf);
|
||||||
|
struct dt9812_private *devpriv = dev->private;
|
||||||
struct dt9812_usb_cmd cmd;
|
struct dt9812_usb_cmd cmd;
|
||||||
int i, count;
|
int i, count;
|
||||||
|
|
||||||
|
@ -363,14 +338,17 @@ static int dt9812_write_multiple_registers(struct usb_dt9812 *dev,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* DT9812 only responds to 32 byte writes!! */
|
/* DT9812 only responds to 32 byte writes!! */
|
||||||
return usb_bulk_msg(usb, usb_sndbulkpipe(usb, dev->cmd_wr.addr),
|
return usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
|
||||||
&cmd, 32, &count, HZ * 1);
|
&cmd, 32, &count, HZ * 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 dt9812_rmw_byte *rmw)
|
||||||
{
|
{
|
||||||
struct usb_device *usb = dev->udev;
|
struct usb_interface *intf = comedi_to_usb_interface(dev);
|
||||||
|
struct usb_device *usb = interface_to_usbdev(intf);
|
||||||
|
struct dt9812_private *devpriv = dev->private;
|
||||||
struct dt9812_usb_cmd cmd;
|
struct dt9812_usb_cmd cmd;
|
||||||
int i, count;
|
int i, count;
|
||||||
|
|
||||||
|
@ -380,22 +358,19 @@ static int dt9812_rmw_multiple_registers(struct usb_dt9812 *dev, int reg_count,
|
||||||
cmd.u.rmw_multi_info.rmw[i] = rmw[i];
|
cmd.u.rmw_multi_info.rmw[i] = rmw[i];
|
||||||
|
|
||||||
/* DT9812 only responds to 32 byte writes!! */
|
/* DT9812 only responds to 32 byte writes!! */
|
||||||
return usb_bulk_msg(usb, usb_sndbulkpipe(usb, dev->cmd_wr.addr),
|
return usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
|
||||||
&cmd, 32, &count, HZ * 1);
|
&cmd, 32, &count, HZ * 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dt9812_digital_in(struct comedi_device *dev, u8 *bits)
|
static int dt9812_digital_in(struct comedi_device *dev, u8 *bits)
|
||||||
{
|
{
|
||||||
struct dt9812_private *devpriv = dev->private;
|
struct dt9812_private *devpriv = dev->private;
|
||||||
struct slot_dt9812 *slot = devpriv->slot;
|
|
||||||
int ret = -ENODEV;
|
|
||||||
|
|
||||||
down(&devpriv->sem);
|
|
||||||
if (slot->usb) {
|
|
||||||
u8 reg[2] = { F020_SFR_P3, F020_SFR_P1 };
|
u8 reg[2] = { F020_SFR_P3, F020_SFR_P1 };
|
||||||
u8 value[2];
|
u8 value[2];
|
||||||
|
int ret;
|
||||||
|
|
||||||
ret = dt9812_read_multiple_registers(slot->usb, 2, reg, value);
|
down(&devpriv->sem);
|
||||||
|
ret = dt9812_read_multiple_registers(dev, 2, reg, value);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
/*
|
/*
|
||||||
* bits 0-6 in F020_SFR_P3 are bits 0-6 in the digital
|
* bits 0-6 in F020_SFR_P3 are bits 0-6 in the digital
|
||||||
|
@ -404,7 +379,6 @@ static int dt9812_digital_in(struct comedi_device *dev, u8 *bits)
|
||||||
*/
|
*/
|
||||||
*bits = (value[0] & 0x7f) | ((value[1] & 0x08) << 4);
|
*bits = (value[0] & 0x7f) | ((value[1] & 0x08) << 4);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
up(&devpriv->sem);
|
up(&devpriv->sem);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -413,17 +387,13 @@ static int dt9812_digital_in(struct comedi_device *dev, u8 *bits)
|
||||||
static int dt9812_digital_out(struct comedi_device *dev, u8 bits)
|
static int dt9812_digital_out(struct comedi_device *dev, u8 bits)
|
||||||
{
|
{
|
||||||
struct dt9812_private *devpriv = dev->private;
|
struct dt9812_private *devpriv = dev->private;
|
||||||
struct slot_dt9812 *slot = devpriv->slot;
|
|
||||||
int ret = -ENODEV;
|
|
||||||
|
|
||||||
down(&devpriv->sem);
|
|
||||||
if (slot->usb) {
|
|
||||||
u8 reg[1] = { F020_SFR_P2 };
|
u8 reg[1] = { F020_SFR_P2 };
|
||||||
u8 value[1] = { bits };
|
u8 value[1] = { bits };
|
||||||
|
int ret;
|
||||||
|
|
||||||
ret = dt9812_write_multiple_registers(slot->usb, 1, reg, value);
|
down(&devpriv->sem);
|
||||||
|
ret = dt9812_write_multiple_registers(dev, 1, reg, value);
|
||||||
devpriv->do_shadow = bits;
|
devpriv->do_shadow = bits;
|
||||||
}
|
|
||||||
up(&devpriv->sem);
|
up(&devpriv->sem);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -444,10 +414,8 @@ static void dt9812_configure_mux(struct comedi_device *dev,
|
||||||
struct dt9812_rmw_byte *rmw, int channel)
|
struct dt9812_rmw_byte *rmw, int channel)
|
||||||
{
|
{
|
||||||
struct dt9812_private *devpriv = dev->private;
|
struct dt9812_private *devpriv = dev->private;
|
||||||
struct slot_dt9812 *slot = devpriv->slot;
|
|
||||||
struct usb_dt9812 *usb = slot->usb;
|
|
||||||
|
|
||||||
if (usb->device == DT9812_DEVID_DT9812_10) {
|
if (devpriv->device == DT9812_DEVID_DT9812_10) {
|
||||||
/* In the DT9812/10V MUX is selected by P1.5-7 */
|
/* In the DT9812/10V MUX is selected by P1.5-7 */
|
||||||
rmw->address = F020_SFR_P1;
|
rmw->address = F020_SFR_P1;
|
||||||
rmw->and_mask = 0xe0;
|
rmw->and_mask = 0xe0;
|
||||||
|
@ -465,11 +433,9 @@ static void dt9812_configure_gain(struct comedi_device *dev,
|
||||||
enum dt9812_gain gain)
|
enum dt9812_gain gain)
|
||||||
{
|
{
|
||||||
struct dt9812_private *devpriv = dev->private;
|
struct dt9812_private *devpriv = dev->private;
|
||||||
struct slot_dt9812 *slot = devpriv->slot;
|
|
||||||
struct usb_dt9812 *usb = slot->usb;
|
|
||||||
|
|
||||||
/* In the DT9812/10V, there is an external gain of 0.5 */
|
/* In the DT9812/10V, there is an external gain of 0.5 */
|
||||||
if (usb->device == DT9812_DEVID_DT9812_10)
|
if (devpriv->device == DT9812_DEVID_DT9812_10)
|
||||||
gain <<= 1;
|
gain <<= 1;
|
||||||
|
|
||||||
rmw->address = F020_SFR_ADC0CF;
|
rmw->address = F020_SFR_ADC0CF;
|
||||||
|
@ -516,7 +482,6 @@ static int dt9812_analog_in(struct comedi_device *dev,
|
||||||
int channel, u16 *value, enum dt9812_gain gain)
|
int channel, u16 *value, enum dt9812_gain gain)
|
||||||
{
|
{
|
||||||
struct dt9812_private *devpriv = dev->private;
|
struct dt9812_private *devpriv = dev->private;
|
||||||
struct slot_dt9812 *slot = devpriv->slot;
|
|
||||||
struct dt9812_rmw_byte rmw[3];
|
struct dt9812_rmw_byte rmw[3];
|
||||||
u8 reg[3] = {
|
u8 reg[3] = {
|
||||||
F020_SFR_ADC0CN,
|
F020_SFR_ADC0CN,
|
||||||
|
@ -524,11 +489,9 @@ static int dt9812_analog_in(struct comedi_device *dev,
|
||||||
F020_SFR_ADC0L
|
F020_SFR_ADC0L
|
||||||
};
|
};
|
||||||
u8 val[3];
|
u8 val[3];
|
||||||
int ret = -ENODEV;
|
int ret;
|
||||||
|
|
||||||
down(&devpriv->sem);
|
down(&devpriv->sem);
|
||||||
if (!slot->usb)
|
|
||||||
goto exit;
|
|
||||||
|
|
||||||
/* 1 select the gain */
|
/* 1 select the gain */
|
||||||
dt9812_configure_gain(dev, &rmw[0], gain);
|
dt9812_configure_gain(dev, &rmw[0], gain);
|
||||||
|
@ -541,12 +504,12 @@ static int dt9812_analog_in(struct comedi_device *dev,
|
||||||
rmw[2].and_mask = 0xff;
|
rmw[2].and_mask = 0xff;
|
||||||
rmw[2].or_value = F020_MASK_ADC0CN_AD0EN | F020_MASK_ADC0CN_AD0BUSY;
|
rmw[2].or_value = F020_MASK_ADC0CN_AD0EN | F020_MASK_ADC0CN_AD0BUSY;
|
||||||
|
|
||||||
ret = dt9812_rmw_multiple_registers(slot->usb, 3, rmw);
|
ret = dt9812_rmw_multiple_registers(dev, 3, rmw);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
/* read the status and ADC */
|
/* read the status and ADC */
|
||||||
ret = dt9812_read_multiple_registers(slot->usb, 3, reg, val);
|
ret = dt9812_read_multiple_registers(dev, 3, reg, val);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
|
@ -561,7 +524,7 @@ static int dt9812_analog_in(struct comedi_device *dev,
|
||||||
*/
|
*/
|
||||||
if ((val[0] & (F020_MASK_ADC0CN_AD0INT | F020_MASK_ADC0CN_AD0BUSY)) ==
|
if ((val[0] & (F020_MASK_ADC0CN_AD0INT | F020_MASK_ADC0CN_AD0BUSY)) ==
|
||||||
F020_MASK_ADC0CN_AD0INT) {
|
F020_MASK_ADC0CN_AD0INT) {
|
||||||
switch (slot->usb->device) {
|
switch (devpriv->device) {
|
||||||
case DT9812_DEVID_DT9812_10:
|
case DT9812_DEVID_DT9812_10:
|
||||||
/*
|
/*
|
||||||
* For DT9812-10V the personality module set the
|
* For DT9812-10V the personality module set the
|
||||||
|
@ -597,12 +560,10 @@ static int dt9812_analog_out_shadow(struct comedi_device *dev,
|
||||||
static int dt9812_analog_out(struct comedi_device *dev, int channel, u16 value)
|
static int dt9812_analog_out(struct comedi_device *dev, int channel, u16 value)
|
||||||
{
|
{
|
||||||
struct dt9812_private *devpriv = dev->private;
|
struct dt9812_private *devpriv = dev->private;
|
||||||
struct slot_dt9812 *slot = devpriv->slot;
|
struct dt9812_rmw_byte rmw[3];
|
||||||
int ret = -ENODEV;
|
int ret;
|
||||||
|
|
||||||
down(&devpriv->sem);
|
down(&devpriv->sem);
|
||||||
if (slot->usb) {
|
|
||||||
struct dt9812_rmw_byte rmw[3];
|
|
||||||
|
|
||||||
switch (channel) {
|
switch (channel) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -641,9 +602,9 @@ static int dt9812_analog_out(struct comedi_device *dev, int channel, u16 value)
|
||||||
rmw[2].or_value = (value >> 8) & 0xf;
|
rmw[2].or_value = (value >> 8) & 0xf;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ret = dt9812_rmw_multiple_registers(slot->usb, 3, rmw);
|
ret = dt9812_rmw_multiple_registers(dev, 3, rmw);
|
||||||
devpriv->ao_shadow[channel] = value;
|
devpriv->ao_shadow[channel] = value;
|
||||||
}
|
|
||||||
up(&devpriv->sem);
|
up(&devpriv->sem);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -727,10 +688,11 @@ static int dt9812_ao_winsn(struct comedi_device *dev,
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dt9812_find_endpoints(struct usb_interface *intf,
|
static int dt9812_find_endpoints(struct comedi_device *dev)
|
||||||
struct usb_dt9812 *devpriv)
|
|
||||||
{
|
{
|
||||||
|
struct usb_interface *intf = comedi_to_usb_interface(dev);
|
||||||
struct usb_host_interface *host = intf->cur_altsetting;
|
struct usb_host_interface *host = intf->cur_altsetting;
|
||||||
|
struct dt9812_private *devpriv = dev->private;
|
||||||
struct usb_endpoint_descriptor *ep;
|
struct usb_endpoint_descriptor *ep;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -774,24 +736,26 @@ static int dt9812_find_endpoints(struct usb_interface *intf,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dt9812_reset_device(struct usb_interface *intf,
|
static int dt9812_reset_device(struct comedi_device *dev)
|
||||||
struct usb_dt9812 *devpriv)
|
|
||||||
{
|
{
|
||||||
|
struct usb_interface *intf = comedi_to_usb_interface(dev);
|
||||||
|
struct usb_device *usb = interface_to_usbdev(intf);
|
||||||
|
struct dt9812_private *devpriv = dev->private;
|
||||||
int ret;
|
int ret;
|
||||||
int i;
|
int i;
|
||||||
u32 tmp32;
|
u32 tmp32;
|
||||||
u16 tmp16;
|
u16 tmp16;
|
||||||
u8 tmp8;
|
u8 tmp8;
|
||||||
|
|
||||||
ret = dt9812_read_info(devpriv, 0, &tmp8, sizeof(tmp8));
|
ret = dt9812_read_info(dev, 0, &tmp8, sizeof(tmp8));
|
||||||
if (ret) {
|
if (ret) {
|
||||||
/*
|
/*
|
||||||
* Seems like a configuration reset is necessary if driver is
|
* Seems like a configuration reset is necessary if driver is
|
||||||
* reloaded while device is attached
|
* reloaded while device is attached
|
||||||
*/
|
*/
|
||||||
usb_reset_configuration(devpriv->udev);
|
usb_reset_configuration(usb);
|
||||||
for (i = 0; i < 10; i++) {
|
for (i = 0; i < 10; i++) {
|
||||||
ret = dt9812_read_info(devpriv, 1, &tmp8, sizeof(tmp8));
|
ret = dt9812_read_info(dev, 1, &tmp8, sizeof(tmp8));
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -801,28 +765,28 @@ static int dt9812_reset_device(struct usb_interface *intf,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = dt9812_read_info(devpriv, 1, &tmp16, sizeof(tmp16));
|
ret = dt9812_read_info(dev, 1, &tmp16, sizeof(tmp16));
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&intf->dev, "failed to read vendor id\n");
|
dev_err(&intf->dev, "failed to read vendor id\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
devpriv->vendor = le16_to_cpu(tmp16);
|
devpriv->vendor = le16_to_cpu(tmp16);
|
||||||
|
|
||||||
ret = dt9812_read_info(devpriv, 3, &tmp16, sizeof(tmp16));
|
ret = dt9812_read_info(dev, 3, &tmp16, sizeof(tmp16));
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&intf->dev, "failed to read product id\n");
|
dev_err(&intf->dev, "failed to read product id\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
devpriv->product = le16_to_cpu(tmp16);
|
devpriv->product = le16_to_cpu(tmp16);
|
||||||
|
|
||||||
ret = dt9812_read_info(devpriv, 5, &tmp16, sizeof(tmp16));
|
ret = dt9812_read_info(dev, 5, &tmp16, sizeof(tmp16));
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&intf->dev, "failed to read device id\n");
|
dev_err(&intf->dev, "failed to read device id\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
devpriv->device = le16_to_cpu(tmp16);
|
devpriv->device = le16_to_cpu(tmp16);
|
||||||
|
|
||||||
ret = dt9812_read_info(devpriv, 7, &tmp32, sizeof(tmp32));
|
ret = dt9812_read_info(dev, 7, &tmp32, sizeof(tmp32));
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&intf->dev, "failed to read serial number\n");
|
dev_err(&intf->dev, "failed to read serial number\n");
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -837,11 +801,11 @@ static int dt9812_reset_device(struct usb_interface *intf,
|
||||||
return 0;
|
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 slot_dt9812 *slot = NULL;
|
struct usb_interface *intf = comedi_to_usb_interface(dev);
|
||||||
struct dt9812_private *devpriv;
|
struct dt9812_private *devpriv;
|
||||||
int i;
|
|
||||||
struct comedi_subdevice *s;
|
struct comedi_subdevice *s;
|
||||||
bool is_unipolar;
|
bool is_unipolar;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -850,186 +814,110 @@ static int dt9812_attach(struct comedi_device *dev, struct comedi_devconfig *it)
|
||||||
if (!devpriv)
|
if (!devpriv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
dev->private = devpriv;
|
dev->private = devpriv;
|
||||||
|
|
||||||
sema_init(&devpriv->sem, 1);
|
sema_init(&devpriv->sem, 1);
|
||||||
|
usb_set_intfdata(intf, devpriv);
|
||||||
|
|
||||||
down(&dt9812_mutex);
|
ret = dt9812_find_endpoints(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/*
|
ret = dt9812_reset_device(dev);
|
||||||
* Find the first unused slot for the comedi device
|
if (ret)
|
||||||
* that has a usb device connected.
|
return ret;
|
||||||
*/
|
|
||||||
for (i = 0; i < DT9812_NUM_SLOTS; i++) {
|
|
||||||
if (dt9812[i].usb && !dt9812[i].devpriv) {
|
|
||||||
slot = &dt9812[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!slot) {
|
|
||||||
up(&dt9812_mutex);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
slot->devpriv = devpriv;
|
is_unipolar = (devpriv->device == DT9812_DEVID_DT9812_2PT5);
|
||||||
devpriv->slot = slot;
|
|
||||||
is_unipolar = (slot->usb->device == DT9812_DEVID_DT9812_2PT5);
|
|
||||||
|
|
||||||
up(&dt9812_mutex);
|
|
||||||
|
|
||||||
ret = comedi_alloc_subdevices(dev, 4);
|
ret = comedi_alloc_subdevices(dev, 4);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* digital input subdevice */
|
/* Digital Input subdevice */
|
||||||
s = &dev->subdevices[0];
|
s = &dev->subdevices[0];
|
||||||
s->type = COMEDI_SUBD_DI;
|
s->type = COMEDI_SUBD_DI;
|
||||||
s->subdev_flags = SDF_READABLE;
|
s->subdev_flags = SDF_READABLE;
|
||||||
s->n_chan = 8;
|
s->n_chan = 8;
|
||||||
s->maxdata = 1;
|
s->maxdata = 1;
|
||||||
s->range_table = &range_digital;
|
s->range_table = &range_digital;
|
||||||
s->insn_read = &dt9812_di_rinsn;
|
s->insn_read = dt9812_di_rinsn;
|
||||||
|
|
||||||
/* digital output subdevice */
|
/* Digital Output subdevice */
|
||||||
s = &dev->subdevices[1];
|
s = &dev->subdevices[1];
|
||||||
s->type = COMEDI_SUBD_DO;
|
s->type = COMEDI_SUBD_DO;
|
||||||
s->subdev_flags = SDF_WRITEABLE;
|
s->subdev_flags = SDF_WRITEABLE;
|
||||||
s->n_chan = 8;
|
s->n_chan = 8;
|
||||||
s->maxdata = 1;
|
s->maxdata = 1;
|
||||||
s->range_table = &range_digital;
|
s->range_table = &range_digital;
|
||||||
s->insn_write = &dt9812_do_winsn;
|
s->insn_write = dt9812_do_winsn;
|
||||||
|
|
||||||
devpriv->do_shadow = 0;
|
devpriv->do_shadow = 0;
|
||||||
|
|
||||||
/* analog input subdevice */
|
/* Analog Input subdevice */
|
||||||
s = &dev->subdevices[2];
|
s = &dev->subdevices[2];
|
||||||
s->type = COMEDI_SUBD_AI;
|
s->type = COMEDI_SUBD_AI;
|
||||||
s->subdev_flags = SDF_READABLE | SDF_GROUND;
|
s->subdev_flags = SDF_READABLE | SDF_GROUND;
|
||||||
s->n_chan = 8;
|
s->n_chan = 8;
|
||||||
s->maxdata = 4095;
|
s->maxdata = 0x0fff;
|
||||||
s->range_table = is_unipolar ? &range_unipolar2_5 : &range_bipolar10;
|
s->range_table = is_unipolar ? &range_unipolar2_5 : &range_bipolar10;
|
||||||
s->insn_read = &dt9812_ai_rinsn;
|
s->insn_read = dt9812_ai_rinsn;
|
||||||
|
|
||||||
/* analog output subdevice */
|
/* Analog Output subdevice */
|
||||||
s = &dev->subdevices[3];
|
s = &dev->subdevices[3];
|
||||||
s->type = COMEDI_SUBD_AO;
|
s->type = COMEDI_SUBD_AO;
|
||||||
s->subdev_flags = SDF_WRITEABLE;
|
s->subdev_flags = SDF_WRITEABLE;
|
||||||
s->n_chan = 0;
|
s->n_chan = 2;
|
||||||
s->maxdata = 4095;
|
s->maxdata = 0x0fff;
|
||||||
s->range_table = is_unipolar ? &range_unipolar2_5 : &range_bipolar10;
|
s->range_table = is_unipolar ? &range_unipolar2_5 : &range_bipolar10;
|
||||||
s->insn_write = &dt9812_ao_winsn;
|
s->insn_write = dt9812_ao_winsn;
|
||||||
s->insn_read = &dt9812_ao_rinsn;
|
s->insn_read = dt9812_ao_rinsn;
|
||||||
|
|
||||||
devpriv->ao_shadow[0] = is_unipolar ? 0x0000 : 0x0800;
|
devpriv->ao_shadow[0] = is_unipolar ? 0x0000 : 0x0800;
|
||||||
devpriv->ao_shadow[1] = is_unipolar ? 0x0000 : 0x0800;
|
devpriv->ao_shadow[1] = is_unipolar ? 0x0000 : 0x0800;
|
||||||
|
|
||||||
dev_info(dev->class_dev, "successfully attached to dt9812.\n");
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dt9812_detach(struct comedi_device *dev)
|
static void dt9812_detach(struct comedi_device *dev)
|
||||||
{
|
{
|
||||||
|
struct usb_interface *intf = comedi_to_usb_interface(dev);
|
||||||
struct dt9812_private *devpriv = dev->private;
|
struct dt9812_private *devpriv = dev->private;
|
||||||
|
|
||||||
if (devpriv && devpriv->slot)
|
if (!devpriv)
|
||||||
devpriv->slot = NULL;
|
return;
|
||||||
|
|
||||||
|
down(&devpriv->sem);
|
||||||
|
|
||||||
|
usb_set_intfdata(intf, NULL);
|
||||||
|
|
||||||
|
up(&devpriv->sem);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct comedi_driver dt9812_comedi_driver = {
|
static struct comedi_driver dt9812_driver = {
|
||||||
.module = THIS_MODULE,
|
|
||||||
.driver_name = "dt9812",
|
.driver_name = "dt9812",
|
||||||
.attach = dt9812_attach,
|
.module = THIS_MODULE,
|
||||||
|
.auto_attach = dt9812_auto_attach,
|
||||||
.detach = dt9812_detach,
|
.detach = dt9812_detach,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int dt9812_probe(struct usb_interface *intf,
|
static int dt9812_usb_probe(struct usb_interface *intf,
|
||||||
const struct usb_device_id *id)
|
const struct usb_device_id *id)
|
||||||
{
|
{
|
||||||
struct slot_dt9812 *slot = NULL;
|
return comedi_usb_auto_config(intf, &dt9812_driver, id->driver_info);
|
||||||
struct usb_dt9812 *dev = NULL;
|
|
||||||
int retval = -ENOMEM;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* allocate memory for our device state and initialize it */
|
|
||||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
||||||
if (dev == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
kref_init(&dev->kref);
|
|
||||||
|
|
||||||
down(&dt9812_mutex);
|
|
||||||
|
|
||||||
/* Find an empty slot for the usb device */
|
|
||||||
for (i = 0; i < DT9812_NUM_SLOTS; i++) {
|
|
||||||
if (!dt9812[i].usb) {
|
|
||||||
slot = &dt9812[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!slot) {
|
|
||||||
up(&dt9812_mutex);
|
|
||||||
retval = -ENODEV;
|
|
||||||
goto error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
slot->usb = dev;
|
static const struct usb_device_id dt9812_usb_table[] = {
|
||||||
dev->slot = slot;
|
|
||||||
|
|
||||||
up(&dt9812_mutex);
|
|
||||||
|
|
||||||
dev->udev = usb_get_dev(interface_to_usbdev(intf));
|
|
||||||
|
|
||||||
retval = dt9812_find_endpoints(intf, dev);
|
|
||||||
if (retval)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
retval = dt9812_reset_device(intf, dev);
|
|
||||||
if (retval)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/* save our data pointer in this interface device */
|
|
||||||
usb_set_intfdata(intf, dev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error:
|
|
||||||
if (dev)
|
|
||||||
kref_put(&dev->kref, dt9812_delete);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dt9812_disconnect(struct usb_interface *intf)
|
|
||||||
{
|
|
||||||
struct usb_dt9812 *dev;
|
|
||||||
int minor = intf->minor;
|
|
||||||
|
|
||||||
down(&dt9812_mutex);
|
|
||||||
dev = usb_get_intfdata(intf);
|
|
||||||
if (dev->slot) {
|
|
||||||
dev->slot->usb = NULL;
|
|
||||||
dev->slot = NULL;
|
|
||||||
}
|
|
||||||
usb_set_intfdata(intf, NULL);
|
|
||||||
up(&dt9812_mutex);
|
|
||||||
|
|
||||||
/* queue final destruction */
|
|
||||||
kref_put(&dev->kref, dt9812_delete);
|
|
||||||
|
|
||||||
dev_info(&intf->dev, "USB Dt9812 #%d now disconnected\n", minor);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct usb_device_id dt9812_table[] = {
|
|
||||||
{ USB_DEVICE(0x0867, 0x9812) },
|
{ USB_DEVICE(0x0867, 0x9812) },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(usb, dt9812_table);
|
MODULE_DEVICE_TABLE(usb, dt9812_usb_table);
|
||||||
|
|
||||||
static struct usb_driver dt9812_usb_driver = {
|
static struct usb_driver dt9812_usb_driver = {
|
||||||
.name = "dt9812",
|
.name = "dt9812",
|
||||||
.id_table = dt9812_table,
|
.id_table = dt9812_usb_table,
|
||||||
.probe = dt9812_probe,
|
.probe = dt9812_usb_probe,
|
||||||
.disconnect = dt9812_disconnect,
|
.disconnect = comedi_usb_auto_unconfig,
|
||||||
};
|
};
|
||||||
module_comedi_usb_driver(dt9812_comedi_driver, dt9812_usb_driver);
|
module_comedi_usb_driver(dt9812_driver, dt9812_usb_driver);
|
||||||
|
|
||||||
MODULE_AUTHOR("Anders Blomdell <anders.blomdell@control.lth.se>");
|
MODULE_AUTHOR("Anders Blomdell <anders.blomdell@control.lth.se>");
|
||||||
MODULE_DESCRIPTION("Comedi DT9812 driver");
|
MODULE_DESCRIPTION("Comedi DT9812 driver");
|
||||||
|
|
Loading…
Add table
Reference in a new issue