aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/comedi/drivers/dt9812.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/comedi/drivers/dt9812.c')
-rw-r--r--drivers/staging/comedi/drivers/dt9812.c1031
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");