1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41#include <linux/kernel.h>
42#include <linux/module.h>
43#include <linux/errno.h>
44#include <linux/init.h>
45#include <linux/uaccess.h>
46#include <linux/usb.h>
47
48#include "../comedidev.h"
49
50#define DT9812_DIAGS_BOARD_INFO_ADDR 0xFBFF
51#define DT9812_MAX_WRITE_CMD_PIPE_SIZE 32
52#define DT9812_MAX_READ_CMD_PIPE_SIZE 32
53
54
55#define DT9812_USB_TIMEOUT 1000
56
57
58
59
60#define F020_SFR_P4 0x84
61#define F020_SFR_P1 0x90
62#define F020_SFR_P2 0xa0
63#define F020_SFR_P3 0xb0
64#define F020_SFR_AMX0CF 0xba
65#define F020_SFR_AMX0SL 0xbb
66#define F020_SFR_ADC0CF 0xbc
67#define F020_SFR_ADC0L 0xbe
68#define F020_SFR_ADC0H 0xbf
69#define F020_SFR_DAC0L 0xd2
70#define F020_SFR_DAC0H 0xd3
71#define F020_SFR_DAC0CN 0xd4
72#define F020_SFR_DAC1L 0xd5
73#define F020_SFR_DAC1H 0xd6
74#define F020_SFR_DAC1CN 0xd7
75#define F020_SFR_ADC0CN 0xe8
76
77#define F020_MASK_ADC0CF_AMP0GN0 0x01
78#define F020_MASK_ADC0CF_AMP0GN1 0x02
79#define F020_MASK_ADC0CF_AMP0GN2 0x04
80
81#define F020_MASK_ADC0CN_AD0EN 0x80
82#define F020_MASK_ADC0CN_AD0INT 0x20
83#define F020_MASK_ADC0CN_AD0BUSY 0x10
84
85#define F020_MASK_DACxCN_DACxEN 0x80
86
87enum {
88
89 DT9812_DEVID_DT9812_10,
90 DT9812_DEVID_DT9812_2PT5,
91};
92
93enum dt9812_gain {
94 DT9812_GAIN_0PT25 = 1,
95 DT9812_GAIN_0PT5 = 2,
96 DT9812_GAIN_1 = 4,
97 DT9812_GAIN_2 = 8,
98 DT9812_GAIN_4 = 16,
99 DT9812_GAIN_8 = 32,
100 DT9812_GAIN_16 = 64,
101};
102
103enum {
104 DT9812_LEAST_USB_FIRMWARE_CMD_CODE = 0,
105
106 DT9812_W_FLASH_DATA = 0,
107
108 DT9812_R_FLASH_DATA = 1,
109
110
111
112
113
114
115 DT9812_R_SINGLE_BYTE_REG = 2,
116
117 DT9812_W_SINGLE_BYTE_REG = 3,
118
119 DT9812_R_MULTI_BYTE_REG = 4,
120
121 DT9812_W_MULTI_BYTE_REG = 5,
122
123 DT9812_RMW_SINGLE_BYTE_REG = 6,
124
125 DT9812_RMW_MULTI_BYTE_REG = 7,
126
127
128
129
130
131
132 DT9812_R_SINGLE_BYTE_SMBUS = 8,
133
134 DT9812_W_SINGLE_BYTE_SMBUS = 9,
135
136 DT9812_R_MULTI_BYTE_SMBUS = 10,
137
138 DT9812_W_MULTI_BYTE_SMBUS = 11,
139
140
141
142
143
144
145 DT9812_R_SINGLE_BYTE_DEV = 12,
146
147 DT9812_W_SINGLE_BYTE_DEV = 13,
148
149 DT9812_R_MULTI_BYTE_DEV = 14,
150
151 DT9812_W_MULTI_BYTE_DEV = 15,
152
153
154 DT9812_W_DAC_THRESHOLD = 16,
155
156
157 DT9812_W_INT_ON_CHANGE_MASK = 17,
158
159
160 DT9812_W_CGL = 18,
161
162 DT9812_R_MULTI_BYTE_USBMEM = 19,
163
164 DT9812_W_MULTI_BYTE_USBMEM = 20,
165
166
167 DT9812_START_SUBSYSTEM = 21,
168
169 DT9812_STOP_SUBSYSTEM = 22,
170
171
172 DT9812_CALIBRATE_POT = 23,
173
174 DT9812_W_DAC_FIFO_SIZE = 24,
175
176 DT9812_W_CGL_DAC = 25,
177
178 DT9812_R_SINGLE_VALUE_CMD = 26,
179
180 DT9812_W_SINGLE_VALUE_CMD = 27,
181
182 DT9812_MAX_USB_FIRMWARE_CMD_CODE,
183};
184
185struct dt9812_flash_data {
186 __le16 numbytes;
187 __le16 address;
188};
189
190#define DT9812_MAX_NUM_MULTI_BYTE_RDS \
191 ((DT9812_MAX_WRITE_CMD_PIPE_SIZE - 4 - 1) / sizeof(u8))
192
193struct dt9812_read_multi {
194 u8 count;
195 u8 address[DT9812_MAX_NUM_MULTI_BYTE_RDS];
196};
197
198struct dt9812_write_byte {
199 u8 address;
200 u8 value;
201};
202
203#define DT9812_MAX_NUM_MULTI_BYTE_WRTS \
204 ((DT9812_MAX_WRITE_CMD_PIPE_SIZE - 4 - 1) / \
205 sizeof(struct dt9812_write_byte))
206
207struct dt9812_write_multi {
208 u8 count;
209 struct dt9812_write_byte write[DT9812_MAX_NUM_MULTI_BYTE_WRTS];
210};
211
212struct dt9812_rmw_byte {
213 u8 address;
214 u8 and_mask;
215 u8 or_value;
216};
217
218#define DT9812_MAX_NUM_MULTI_BYTE_RMWS \
219 ((DT9812_MAX_WRITE_CMD_PIPE_SIZE - 4 - 1) / \
220 sizeof(struct dt9812_rmw_byte))
221
222struct dt9812_rmw_multi {
223 u8 count;
224 struct dt9812_rmw_byte rmw[DT9812_MAX_NUM_MULTI_BYTE_RMWS];
225};
226
227struct dt9812_usb_cmd {
228 __le32 cmd;
229 union {
230 struct dt9812_flash_data flash_data_info;
231 struct dt9812_read_multi read_multi_info;
232 struct dt9812_write_multi write_multi_info;
233 struct dt9812_rmw_multi rmw_multi_info;
234 } u;
235};
236
237struct dt9812_private {
238 struct semaphore sem;
239 struct {
240 __u8 addr;
241 size_t size;
242 } cmd_wr, cmd_rd;
243 u16 device;
244 u16 ao_shadow[2];
245};
246
247static int dt9812_read_info(struct comedi_device *dev,
248 int offset, void *buf, size_t buf_size)
249{
250 struct usb_device *usb = comedi_to_usb_dev(dev);
251 struct dt9812_private *devpriv = dev->private;
252 struct dt9812_usb_cmd cmd;
253 int count, ret;
254
255 cmd.cmd = cpu_to_le32(DT9812_R_FLASH_DATA);
256 cmd.u.flash_data_info.address =
257 cpu_to_le16(DT9812_DIAGS_BOARD_INFO_ADDR + offset);
258 cmd.u.flash_data_info.numbytes = cpu_to_le16(buf_size);
259
260
261 ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
262 &cmd, 32, &count, DT9812_USB_TIMEOUT);
263 if (ret)
264 return ret;
265
266 return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr),
267 buf, buf_size, &count, DT9812_USB_TIMEOUT);
268}
269
270static int dt9812_read_multiple_registers(struct comedi_device *dev,
271 int reg_count, u8 *address,
272 u8 *value)
273{
274 struct usb_device *usb = comedi_to_usb_dev(dev);
275 struct dt9812_private *devpriv = dev->private;
276 struct dt9812_usb_cmd cmd;
277 int i, count, ret;
278
279 cmd.cmd = cpu_to_le32(DT9812_R_MULTI_BYTE_REG);
280 cmd.u.read_multi_info.count = reg_count;
281 for (i = 0; i < reg_count; i++)
282 cmd.u.read_multi_info.address[i] = address[i];
283
284
285 ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
286 &cmd, 32, &count, DT9812_USB_TIMEOUT);
287 if (ret)
288 return ret;
289
290 return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr),
291 value, reg_count, &count, DT9812_USB_TIMEOUT);
292}
293
294static int dt9812_write_multiple_registers(struct comedi_device *dev,
295 int reg_count, u8 *address,
296 u8 *value)
297{
298 struct usb_device *usb = comedi_to_usb_dev(dev);
299 struct dt9812_private *devpriv = dev->private;
300 struct dt9812_usb_cmd cmd;
301 int i, count;
302
303 cmd.cmd = cpu_to_le32(DT9812_W_MULTI_BYTE_REG);
304 cmd.u.read_multi_info.count = reg_count;
305 for (i = 0; i < reg_count; i++) {
306 cmd.u.write_multi_info.write[i].address = address[i];
307 cmd.u.write_multi_info.write[i].value = value[i];
308 }
309
310
311 return usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
312 &cmd, 32, &count, DT9812_USB_TIMEOUT);
313}
314
315static int dt9812_rmw_multiple_registers(struct comedi_device *dev,
316 int reg_count,
317 struct dt9812_rmw_byte *rmw)
318{
319 struct usb_device *usb = comedi_to_usb_dev(dev);
320 struct dt9812_private *devpriv = dev->private;
321 struct dt9812_usb_cmd cmd;
322 int i, count;
323
324 cmd.cmd = cpu_to_le32(DT9812_RMW_MULTI_BYTE_REG);
325 cmd.u.rmw_multi_info.count = reg_count;
326 for (i = 0; i < reg_count; i++)
327 cmd.u.rmw_multi_info.rmw[i] = rmw[i];
328
329
330 return usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
331 &cmd, 32, &count, DT9812_USB_TIMEOUT);
332}
333
334static int dt9812_digital_in(struct comedi_device *dev, u8 *bits)
335{
336 struct dt9812_private *devpriv = dev->private;
337 u8 reg[2] = { F020_SFR_P3, F020_SFR_P1 };
338 u8 value[2];
339 int ret;
340
341 down(&devpriv->sem);
342 ret = dt9812_read_multiple_registers(dev, 2, reg, value);
343 if (ret == 0) {
344
345
346
347
348
349 *bits = (value[0] & 0x7f) | ((value[1] & 0x08) << 4);
350 }
351 up(&devpriv->sem);
352
353 return ret;
354}
355
356static int dt9812_digital_out(struct comedi_device *dev, u8 bits)
357{
358 struct dt9812_private *devpriv = dev->private;
359 u8 reg[1] = { F020_SFR_P2 };
360 u8 value[1] = { bits };
361 int ret;
362
363 down(&devpriv->sem);
364 ret = dt9812_write_multiple_registers(dev, 1, reg, value);
365 up(&devpriv->sem);
366
367 return ret;
368}
369
370static void dt9812_configure_mux(struct comedi_device *dev,
371 struct dt9812_rmw_byte *rmw, int channel)
372{
373 struct dt9812_private *devpriv = dev->private;
374
375 if (devpriv->device == DT9812_DEVID_DT9812_10) {
376
377 rmw->address = F020_SFR_P1;
378 rmw->and_mask = 0xe0;
379 rmw->or_value = channel << 5;
380 } else {
381
382 rmw->address = F020_SFR_AMX0SL;
383 rmw->and_mask = 0xff;
384 rmw->or_value = channel & 0x07;
385 }
386}
387
388static void dt9812_configure_gain(struct comedi_device *dev,
389 struct dt9812_rmw_byte *rmw,
390 enum dt9812_gain gain)
391{
392 struct dt9812_private *devpriv = dev->private;
393
394
395 if (devpriv->device == DT9812_DEVID_DT9812_10)
396 gain <<= 1;
397
398 rmw->address = F020_SFR_ADC0CF;
399 rmw->and_mask = F020_MASK_ADC0CF_AMP0GN2 |
400 F020_MASK_ADC0CF_AMP0GN1 |
401 F020_MASK_ADC0CF_AMP0GN0;
402
403 switch (gain) {
404
405
406
407
408
409
410
411
412 case DT9812_GAIN_0PT5:
413 rmw->or_value = F020_MASK_ADC0CF_AMP0GN2 |
414 F020_MASK_ADC0CF_AMP0GN1;
415 break;
416 default:
417
418 case DT9812_GAIN_1:
419 rmw->or_value = 0x00;
420 break;
421 case DT9812_GAIN_2:
422 rmw->or_value = F020_MASK_ADC0CF_AMP0GN0;
423 break;
424 case DT9812_GAIN_4:
425 rmw->or_value = F020_MASK_ADC0CF_AMP0GN1;
426 break;
427 case DT9812_GAIN_8:
428 rmw->or_value = F020_MASK_ADC0CF_AMP0GN1 |
429 F020_MASK_ADC0CF_AMP0GN0;
430 break;
431 case DT9812_GAIN_16:
432 rmw->or_value = F020_MASK_ADC0CF_AMP0GN2;
433 break;
434 }
435}
436
437static int dt9812_analog_in(struct comedi_device *dev,
438 int channel, u16 *value, enum dt9812_gain gain)
439{
440 struct dt9812_private *devpriv = dev->private;
441 struct dt9812_rmw_byte rmw[3];
442 u8 reg[3] = {
443 F020_SFR_ADC0CN,
444 F020_SFR_ADC0H,
445 F020_SFR_ADC0L
446 };
447 u8 val[3];
448 int ret;
449
450 down(&devpriv->sem);
451
452
453 dt9812_configure_gain(dev, &rmw[0], gain);
454
455
456 dt9812_configure_mux(dev, &rmw[1], channel);
457
458
459 rmw[2].address = F020_SFR_ADC0CN;
460 rmw[2].and_mask = 0xff;
461 rmw[2].or_value = F020_MASK_ADC0CN_AD0EN | F020_MASK_ADC0CN_AD0BUSY;
462
463 ret = dt9812_rmw_multiple_registers(dev, 3, rmw);
464 if (ret)
465 goto exit;
466
467
468 ret = dt9812_read_multiple_registers(dev, 3, reg, val);
469 if (ret)
470 goto exit;
471
472
473
474
475
476
477
478
479
480
481 if ((val[0] & (F020_MASK_ADC0CN_AD0INT | F020_MASK_ADC0CN_AD0BUSY)) ==
482 F020_MASK_ADC0CN_AD0INT) {
483 switch (devpriv->device) {
484 case DT9812_DEVID_DT9812_10:
485
486
487
488
489
490 *value = ((val[1] << 8) | val[2]) + 0x800;
491 break;
492 case DT9812_DEVID_DT9812_2PT5:
493 *value = (val[1] << 8) | val[2];
494 break;
495 }
496 }
497
498exit:
499 up(&devpriv->sem);
500
501 return ret;
502}
503
504static int dt9812_analog_out(struct comedi_device *dev, int channel, u16 value)
505{
506 struct dt9812_private *devpriv = dev->private;
507 struct dt9812_rmw_byte rmw[3];
508 int ret;
509
510 down(&devpriv->sem);
511
512 switch (channel) {
513 case 0:
514
515 rmw[0].address = F020_SFR_DAC0CN;
516 rmw[0].and_mask = 0xff;
517 rmw[0].or_value = F020_MASK_DACxCN_DACxEN;
518
519
520 rmw[1].address = F020_SFR_DAC0L;
521 rmw[1].and_mask = 0xff;
522 rmw[1].or_value = value & 0xff;
523
524
525
526 rmw[2].address = F020_SFR_DAC0H;
527 rmw[2].and_mask = 0xff;
528 rmw[2].or_value = (value >> 8) & 0xf;
529 break;
530
531 case 1:
532
533 rmw[0].address = F020_SFR_DAC1CN;
534 rmw[0].and_mask = 0xff;
535 rmw[0].or_value = F020_MASK_DACxCN_DACxEN;
536
537
538 rmw[1].address = F020_SFR_DAC1L;
539 rmw[1].and_mask = 0xff;
540 rmw[1].or_value = value & 0xff;
541
542
543
544 rmw[2].address = F020_SFR_DAC1H;
545 rmw[2].and_mask = 0xff;
546 rmw[2].or_value = (value >> 8) & 0xf;
547 break;
548 }
549 ret = dt9812_rmw_multiple_registers(dev, 3, rmw);
550 devpriv->ao_shadow[channel] = value;
551
552 up(&devpriv->sem);
553
554 return ret;
555}
556
557static int dt9812_di_insn_bits(struct comedi_device *dev,
558 struct comedi_subdevice *s,
559 struct comedi_insn *insn,
560 unsigned int *data)
561{
562 u8 bits = 0;
563 int ret;
564
565 ret = dt9812_digital_in(dev, &bits);
566 if (ret)
567 return ret;
568
569 data[1] = bits;
570
571 return insn->n;
572}
573
574static int dt9812_do_insn_bits(struct comedi_device *dev,
575 struct comedi_subdevice *s,
576 struct comedi_insn *insn,
577 unsigned int *data)
578{
579 if (comedi_dio_update_state(s, data))
580 dt9812_digital_out(dev, s->state);
581
582 data[1] = s->state;
583
584 return insn->n;
585}
586
587static int dt9812_ai_insn_read(struct comedi_device *dev,
588 struct comedi_subdevice *s,
589 struct comedi_insn *insn,
590 unsigned int *data)
591{
592 unsigned int chan = CR_CHAN(insn->chanspec);
593 u16 val = 0;
594 int ret;
595 int i;
596
597 for (i = 0; i < insn->n; i++) {
598 ret = dt9812_analog_in(dev, chan, &val, DT9812_GAIN_1);
599 if (ret)
600 return ret;
601 data[i] = val;
602 }
603
604 return insn->n;
605}
606
607static int dt9812_ao_insn_read(struct comedi_device *dev,
608 struct comedi_subdevice *s,
609 struct comedi_insn *insn,
610 unsigned int *data)
611{
612 struct dt9812_private *devpriv = dev->private;
613 unsigned int chan = CR_CHAN(insn->chanspec);
614 int i;
615
616 down(&devpriv->sem);
617 for (i = 0; i < insn->n; i++)
618 data[i] = devpriv->ao_shadow[chan];
619 up(&devpriv->sem);
620
621 return insn->n;
622}
623
624static int dt9812_ao_insn_write(struct comedi_device *dev,
625 struct comedi_subdevice *s,
626 struct comedi_insn *insn,
627 unsigned int *data)
628{
629 unsigned int chan = CR_CHAN(insn->chanspec);
630 int ret;
631 int i;
632
633 for (i = 0; i < insn->n; i++) {
634 ret = dt9812_analog_out(dev, chan, data[i]);
635 if (ret)
636 return ret;
637 }
638
639 return insn->n;
640}
641
642static int dt9812_find_endpoints(struct comedi_device *dev)
643{
644 struct usb_interface *intf = comedi_to_usb_interface(dev);
645 struct usb_host_interface *host = intf->cur_altsetting;
646 struct dt9812_private *devpriv = dev->private;
647 struct usb_endpoint_descriptor *ep;
648 int i;
649
650 if (host->desc.bNumEndpoints != 5) {
651 dev_err(dev->class_dev, "Wrong number of endpoints\n");
652 return -ENODEV;
653 }
654
655 for (i = 0; i < host->desc.bNumEndpoints; ++i) {
656 int dir = -1;
657 ep = &host->endpoint[i].desc;
658 switch (i) {
659 case 0:
660
661 dir = USB_DIR_IN;
662 break;
663 case 1:
664 dir = USB_DIR_OUT;
665 devpriv->cmd_wr.addr = ep->bEndpointAddress;
666 devpriv->cmd_wr.size = le16_to_cpu(ep->wMaxPacketSize);
667 break;
668 case 2:
669 dir = USB_DIR_IN;
670 devpriv->cmd_rd.addr = ep->bEndpointAddress;
671 devpriv->cmd_rd.size = le16_to_cpu(ep->wMaxPacketSize);
672 break;
673 case 3:
674
675 dir = USB_DIR_OUT;
676 break;
677 case 4:
678
679 dir = USB_DIR_IN;
680 break;
681 }
682 if ((ep->bEndpointAddress & USB_DIR_IN) != dir) {
683 dev_err(dev->class_dev,
684 "Endpoint has wrong direction\n");
685 return -ENODEV;
686 }
687 }
688 return 0;
689}
690
691static int dt9812_reset_device(struct comedi_device *dev)
692{
693 struct usb_device *usb = comedi_to_usb_dev(dev);
694 struct dt9812_private *devpriv = dev->private;
695 u32 serial;
696 u16 vendor;
697 u16 product;
698 u8 tmp8;
699 __le16 tmp16;
700 __le32 tmp32;
701 int ret;
702 int i;
703
704 ret = dt9812_read_info(dev, 0, &tmp8, sizeof(tmp8));
705 if (ret) {
706
707
708
709
710 usb_reset_configuration(usb);
711 for (i = 0; i < 10; i++) {
712 ret = dt9812_read_info(dev, 1, &tmp8, sizeof(tmp8));
713 if (ret == 0)
714 break;
715 }
716 if (ret) {
717 dev_err(dev->class_dev,
718 "unable to reset configuration\n");
719 return ret;
720 }
721 }
722
723 ret = dt9812_read_info(dev, 1, &tmp16, sizeof(tmp16));
724 if (ret) {
725 dev_err(dev->class_dev, "failed to read vendor id\n");
726 return ret;
727 }
728 vendor = le16_to_cpu(tmp16);
729
730 ret = dt9812_read_info(dev, 3, &tmp16, sizeof(tmp16));
731 if (ret) {
732 dev_err(dev->class_dev, "failed to read product id\n");
733 return ret;
734 }
735 product = le16_to_cpu(tmp16);
736
737 ret = dt9812_read_info(dev, 5, &tmp16, sizeof(tmp16));
738 if (ret) {
739 dev_err(dev->class_dev, "failed to read device id\n");
740 return ret;
741 }
742 devpriv->device = le16_to_cpu(tmp16);
743
744 ret = dt9812_read_info(dev, 7, &tmp32, sizeof(tmp32));
745 if (ret) {
746 dev_err(dev->class_dev, "failed to read serial number\n");
747 return ret;
748 }
749 serial = le32_to_cpu(tmp32);
750
751
752 dev_info(dev->class_dev, "USB DT9812 (%4.4x.%4.4x.%4.4x) #0x%8.8x\n",
753 vendor, product, devpriv->device, serial);
754
755 if (devpriv->device != DT9812_DEVID_DT9812_10 &&
756 devpriv->device != DT9812_DEVID_DT9812_2PT5) {
757 dev_err(dev->class_dev, "Unsupported device!\n");
758 return -EINVAL;
759 }
760
761 return 0;
762}
763
764static int dt9812_auto_attach(struct comedi_device *dev,
765 unsigned long context)
766{
767 struct usb_interface *intf = comedi_to_usb_interface(dev);
768 struct dt9812_private *devpriv;
769 struct comedi_subdevice *s;
770 bool is_unipolar;
771 int ret;
772
773 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
774 if (!devpriv)
775 return -ENOMEM;
776
777 sema_init(&devpriv->sem, 1);
778 usb_set_intfdata(intf, devpriv);
779
780 ret = dt9812_find_endpoints(dev);
781 if (ret)
782 return ret;
783
784 ret = dt9812_reset_device(dev);
785 if (ret)
786 return ret;
787
788 is_unipolar = (devpriv->device == DT9812_DEVID_DT9812_2PT5);
789
790 ret = comedi_alloc_subdevices(dev, 4);
791 if (ret)
792 return ret;
793
794
795 s = &dev->subdevices[0];
796 s->type = COMEDI_SUBD_DI;
797 s->subdev_flags = SDF_READABLE;
798 s->n_chan = 8;
799 s->maxdata = 1;
800 s->range_table = &range_digital;
801 s->insn_bits = dt9812_di_insn_bits;
802
803
804 s = &dev->subdevices[1];
805 s->type = COMEDI_SUBD_DO;
806 s->subdev_flags = SDF_WRITEABLE;
807 s->n_chan = 8;
808 s->maxdata = 1;
809 s->range_table = &range_digital;
810 s->insn_bits = dt9812_do_insn_bits;
811
812
813 s = &dev->subdevices[2];
814 s->type = COMEDI_SUBD_AI;
815 s->subdev_flags = SDF_READABLE | SDF_GROUND;
816 s->n_chan = 8;
817 s->maxdata = 0x0fff;
818 s->range_table = is_unipolar ? &range_unipolar2_5 : &range_bipolar10;
819 s->insn_read = dt9812_ai_insn_read;
820
821
822 s = &dev->subdevices[3];
823 s->type = COMEDI_SUBD_AO;
824 s->subdev_flags = SDF_WRITEABLE;
825 s->n_chan = 2;
826 s->maxdata = 0x0fff;
827 s->range_table = is_unipolar ? &range_unipolar2_5 : &range_bipolar10;
828 s->insn_write = dt9812_ao_insn_write;
829 s->insn_read = dt9812_ao_insn_read;
830
831 devpriv->ao_shadow[0] = is_unipolar ? 0x0000 : 0x0800;
832 devpriv->ao_shadow[1] = is_unipolar ? 0x0000 : 0x0800;
833
834 return 0;
835}
836
837static void dt9812_detach(struct comedi_device *dev)
838{
839 struct usb_interface *intf = comedi_to_usb_interface(dev);
840 struct dt9812_private *devpriv = dev->private;
841
842 if (!devpriv)
843 return;
844
845 down(&devpriv->sem);
846
847 usb_set_intfdata(intf, NULL);
848
849 up(&devpriv->sem);
850}
851
852static struct comedi_driver dt9812_driver = {
853 .driver_name = "dt9812",
854 .module = THIS_MODULE,
855 .auto_attach = dt9812_auto_attach,
856 .detach = dt9812_detach,
857};
858
859static int dt9812_usb_probe(struct usb_interface *intf,
860 const struct usb_device_id *id)
861{
862 return comedi_usb_auto_config(intf, &dt9812_driver, id->driver_info);
863}
864
865static const struct usb_device_id dt9812_usb_table[] = {
866 { USB_DEVICE(0x0867, 0x9812) },
867 { }
868};
869MODULE_DEVICE_TABLE(usb, dt9812_usb_table);
870
871static struct usb_driver dt9812_usb_driver = {
872 .name = "dt9812",
873 .id_table = dt9812_usb_table,
874 .probe = dt9812_usb_probe,
875 .disconnect = comedi_usb_auto_unconfig,
876};
877module_comedi_usb_driver(dt9812_driver, dt9812_usb_driver);
878
879MODULE_AUTHOR("Anders Blomdell <anders.blomdell@control.lth.se>");
880MODULE_DESCRIPTION("Comedi DT9812 driver");
881MODULE_LICENSE("GPL");
882