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#include <linux/kernel.h>
37#include <linux/module.h>
38#include <linux/slab.h>
39#include <linux/input.h>
40#include <linux/fcntl.h>
41#include <linux/compiler.h>
42#include "../comedi_usb.h"
43
44
45
46
47#define EZTIMEOUT 30
48
49
50
51
52#define FIRMWARE "usbduxfast_firmware.bin"
53#define FIRMWARE_MAX_LEN 0x2000
54#define USBDUXFASTSUB_FIRMWARE 0xA0
55#define VENDOR_DIR_IN 0xC0
56#define VENDOR_DIR_OUT 0x40
57
58
59
60
61#define USBDUXFASTSUB_CPUCS 0xE600
62
63
64
65
66#define TB_LEN 0x2000
67
68
69
70
71#define BULKINEP 6
72
73
74
75
76#define CHANNELLISTEP 4
77
78
79
80
81#define NUMCHANNELS 32
82
83
84
85
86#define WAVESIZE 0x20
87
88
89
90
91#define SIZEADIN (sizeof(s16))
92
93
94
95
96#define SIZEINBUF 512
97
98
99
100
101#define SIZEINSNBUF 512
102
103
104
105
106#define SIZEOFDUXBUF 256
107
108
109
110
111#define NUMOFINBUFFERSHIGH 10
112
113
114
115
116
117
118
119#define MIN_SAMPLING_PERIOD 9
120
121
122
123
124#define MAX_SAMPLING_PERIOD 500
125
126
127
128
129
130#define PACKETS_TO_IGNORE 4
131
132
133
134
135static const struct comedi_lrange range_usbduxfast_ai_range = {
136 2, {
137 BIP_RANGE(0.75),
138 BIP_RANGE(0.5)
139 }
140};
141
142
143
144
145
146
147
148struct usbduxfast_private {
149 struct urb *urb;
150 u8 *duxbuf;
151 s8 *inbuf;
152 short int ai_cmd_running;
153 int ignore;
154 struct mutex mut;
155};
156
157
158
159
160#define SENDADCOMMANDS 0
161#define SENDINITEP6 1
162
163static int usbduxfast_send_cmd(struct comedi_device *dev, int cmd_type)
164{
165 struct usb_device *usb = comedi_to_usb_dev(dev);
166 struct usbduxfast_private *devpriv = dev->private;
167 int nsent;
168 int ret;
169
170 devpriv->duxbuf[0] = cmd_type;
171
172 ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, CHANNELLISTEP),
173 devpriv->duxbuf, SIZEOFDUXBUF,
174 &nsent, 10000);
175 if (ret < 0)
176 dev_err(dev->class_dev,
177 "could not transmit command to the usb-device, err=%d\n",
178 ret);
179 return ret;
180}
181
182static void usbduxfast_cmd_data(struct comedi_device *dev, int index,
183 u8 len, u8 op, u8 out, u8 log)
184{
185 struct usbduxfast_private *devpriv = dev->private;
186
187
188 devpriv->duxbuf[1 + 0x00 + index] = len;
189 devpriv->duxbuf[1 + 0x08 + index] = op;
190 devpriv->duxbuf[1 + 0x10 + index] = out;
191 devpriv->duxbuf[1 + 0x18 + index] = log;
192}
193
194static int usbduxfast_ai_stop(struct comedi_device *dev, int do_unlink)
195{
196 struct usbduxfast_private *devpriv = dev->private;
197
198
199 devpriv->ai_cmd_running = 0;
200
201 if (do_unlink && devpriv->urb) {
202
203 usb_kill_urb(devpriv->urb);
204 }
205
206 return 0;
207}
208
209static int usbduxfast_ai_cancel(struct comedi_device *dev,
210 struct comedi_subdevice *s)
211{
212 struct usbduxfast_private *devpriv = dev->private;
213 int ret;
214
215 mutex_lock(&devpriv->mut);
216 ret = usbduxfast_ai_stop(dev, 1);
217 mutex_unlock(&devpriv->mut);
218
219 return ret;
220}
221
222static void usbduxfast_ai_handle_urb(struct comedi_device *dev,
223 struct comedi_subdevice *s,
224 struct urb *urb)
225{
226 struct usbduxfast_private *devpriv = dev->private;
227 struct comedi_async *async = s->async;
228 struct comedi_cmd *cmd = &async->cmd;
229 int ret;
230
231 if (devpriv->ignore) {
232 devpriv->ignore--;
233 } else {
234 unsigned int nsamples;
235
236 nsamples = comedi_bytes_to_samples(s, urb->actual_length);
237 nsamples = comedi_nsamples_left(s, nsamples);
238 comedi_buf_write_samples(s, urb->transfer_buffer, nsamples);
239
240 if (cmd->stop_src == TRIG_COUNT &&
241 async->scans_done >= cmd->stop_arg)
242 async->events |= COMEDI_CB_EOA;
243 }
244
245
246 if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
247 urb->dev = comedi_to_usb_dev(dev);
248 urb->status = 0;
249 ret = usb_submit_urb(urb, GFP_ATOMIC);
250 if (ret < 0) {
251 dev_err(dev->class_dev, "urb resubm failed: %d", ret);
252 async->events |= COMEDI_CB_ERROR;
253 }
254 }
255}
256
257static void usbduxfast_ai_interrupt(struct urb *urb)
258{
259 struct comedi_device *dev = urb->context;
260 struct comedi_subdevice *s = dev->read_subdev;
261 struct comedi_async *async = s->async;
262 struct usbduxfast_private *devpriv = dev->private;
263
264
265 if (!devpriv->ai_cmd_running)
266 return;
267
268 switch (urb->status) {
269 case 0:
270 usbduxfast_ai_handle_urb(dev, s, urb);
271 break;
272
273 case -ECONNRESET:
274 case -ENOENT:
275 case -ESHUTDOWN:
276 case -ECONNABORTED:
277
278 async->events |= COMEDI_CB_ERROR;
279 break;
280
281 default:
282
283 dev_err(dev->class_dev,
284 "non-zero urb status received in ai intr context: %d\n",
285 urb->status);
286 async->events |= COMEDI_CB_ERROR;
287 break;
288 }
289
290
291
292
293
294 if (async->events & COMEDI_CB_CANCEL_MASK)
295 usbduxfast_ai_stop(dev, 0);
296
297 comedi_event(dev, s);
298}
299
300static int usbduxfast_submit_urb(struct comedi_device *dev)
301{
302 struct usb_device *usb = comedi_to_usb_dev(dev);
303 struct usbduxfast_private *devpriv = dev->private;
304 int ret;
305
306 usb_fill_bulk_urb(devpriv->urb, usb, usb_rcvbulkpipe(usb, BULKINEP),
307 devpriv->inbuf, SIZEINBUF,
308 usbduxfast_ai_interrupt, dev);
309
310 ret = usb_submit_urb(devpriv->urb, GFP_ATOMIC);
311 if (ret) {
312 dev_err(dev->class_dev, "usb_submit_urb error %d\n", ret);
313 return ret;
314 }
315 return 0;
316}
317
318static int usbduxfast_ai_check_chanlist(struct comedi_device *dev,
319 struct comedi_subdevice *s,
320 struct comedi_cmd *cmd)
321{
322 unsigned int gain0 = CR_RANGE(cmd->chanlist[0]);
323 int i;
324
325 if (cmd->chanlist_len > 3 && cmd->chanlist_len != 16) {
326 dev_err(dev->class_dev, "unsupported combination of channels\n");
327 return -EINVAL;
328 }
329
330 for (i = 0; i < cmd->chanlist_len; ++i) {
331 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
332 unsigned int gain = CR_RANGE(cmd->chanlist[i]);
333
334 if (chan != i) {
335 dev_err(dev->class_dev,
336 "channels are not consecutive\n");
337 return -EINVAL;
338 }
339 if (gain != gain0 && cmd->chanlist_len > 3) {
340 dev_err(dev->class_dev,
341 "gain must be the same for all channels\n");
342 return -EINVAL;
343 }
344 }
345 return 0;
346}
347
348static int usbduxfast_ai_cmdtest(struct comedi_device *dev,
349 struct comedi_subdevice *s,
350 struct comedi_cmd *cmd)
351{
352 int err = 0;
353 unsigned int steps;
354 unsigned int arg;
355
356
357
358 err |= comedi_check_trigger_src(&cmd->start_src,
359 TRIG_NOW | TRIG_EXT | TRIG_INT);
360 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
361 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
362 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
363 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
364
365 if (err)
366 return 1;
367
368
369
370 err |= comedi_check_trigger_is_unique(cmd->start_src);
371 err |= comedi_check_trigger_is_unique(cmd->stop_src);
372
373
374
375 if (err)
376 return 2;
377
378
379
380 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
381
382 if (!cmd->chanlist_len)
383 err |= -EINVAL;
384
385
386 if (cmd->start_src == TRIG_EXT &&
387 cmd->chanlist_len != 1 && cmd->chanlist_len != 16)
388 err |= -EINVAL;
389
390 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
391 cmd->chanlist_len);
392
393
394
395
396
397
398
399
400 steps = (cmd->convert_arg * 30) / 1000;
401 if (cmd->chanlist_len != 1)
402 err |= comedi_check_trigger_arg_min(&steps,
403 MIN_SAMPLING_PERIOD);
404 err |= comedi_check_trigger_arg_max(&steps, MAX_SAMPLING_PERIOD);
405 arg = (steps * 1000) / 30;
406 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
407
408 if (cmd->stop_src == TRIG_COUNT)
409 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
410 else
411 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
412
413 if (err)
414 return 3;
415
416
417
418
419 if (cmd->chanlist && cmd->chanlist_len > 0)
420 err |= usbduxfast_ai_check_chanlist(dev, s, cmd);
421 if (err)
422 return 5;
423
424 return 0;
425}
426
427static int usbduxfast_ai_inttrig(struct comedi_device *dev,
428 struct comedi_subdevice *s,
429 unsigned int trig_num)
430{
431 struct usbduxfast_private *devpriv = dev->private;
432 struct comedi_cmd *cmd = &s->async->cmd;
433 int ret;
434
435 if (trig_num != cmd->start_arg)
436 return -EINVAL;
437
438 mutex_lock(&devpriv->mut);
439
440 if (!devpriv->ai_cmd_running) {
441 devpriv->ai_cmd_running = 1;
442 ret = usbduxfast_submit_urb(dev);
443 if (ret < 0) {
444 dev_err(dev->class_dev, "urbSubmit: err=%d\n", ret);
445 devpriv->ai_cmd_running = 0;
446 mutex_unlock(&devpriv->mut);
447 return ret;
448 }
449 s->async->inttrig = NULL;
450 } else {
451 dev_err(dev->class_dev, "ai is already running\n");
452 }
453 mutex_unlock(&devpriv->mut);
454 return 1;
455}
456
457static int usbduxfast_ai_cmd(struct comedi_device *dev,
458 struct comedi_subdevice *s)
459{
460 struct usbduxfast_private *devpriv = dev->private;
461 struct comedi_cmd *cmd = &s->async->cmd;
462 unsigned int rngmask = 0xff;
463 int j, ret;
464 long steps, steps_tmp;
465
466 mutex_lock(&devpriv->mut);
467 if (devpriv->ai_cmd_running) {
468 ret = -EBUSY;
469 goto cmd_exit;
470 }
471
472
473
474
475
476 devpriv->ignore = PACKETS_TO_IGNORE;
477
478 steps = (cmd->convert_arg * 30) / 1000;
479
480 switch (cmd->chanlist_len) {
481 case 1:
482
483
484
485
486 if (CR_RANGE(cmd->chanlist[0]) > 0)
487 rngmask = 0xff - 0x04;
488 else
489 rngmask = 0xff;
490
491
492
493
494
495
496
497 if (cmd->start_src == TRIG_EXT) {
498
499
500
501 usbduxfast_cmd_data(dev, 0, 0x01, 0x01, rngmask, 0x00);
502 } else {
503 usbduxfast_cmd_data(dev, 0, 0x01, 0x00, rngmask, 0x00);
504 }
505
506 if (steps < MIN_SAMPLING_PERIOD) {
507
508 if (steps <= 1) {
509
510
511
512
513
514
515
516
517
518 usbduxfast_cmd_data(dev, 1,
519 0x89, 0x03, rngmask, 0xff);
520 } else {
521
522
523
524
525
526
527 usbduxfast_cmd_data(dev, 1, steps - 1,
528 0x02, rngmask, 0x00);
529
530
531
532
533 usbduxfast_cmd_data(dev, 2,
534 0x09, 0x01, rngmask, 0xff);
535 }
536 } else {
537
538
539
540
541
542
543 steps = steps - 1;
544
545
546 usbduxfast_cmd_data(dev, 1,
547 steps / 2, 0x00, rngmask, 0x00);
548
549
550 usbduxfast_cmd_data(dev, 2, steps - steps / 2,
551 0x00, rngmask, 0x00);
552
553
554
555
556
557
558 usbduxfast_cmd_data(dev, 3,
559 0x09, 0x03, rngmask, 0xff);
560 }
561 break;
562
563 case 2:
564
565
566
567
568
569 if (CR_RANGE(cmd->chanlist[0]) > 0)
570 rngmask = 0xff - 0x04;
571 else
572 rngmask = 0xff;
573
574
575 usbduxfast_cmd_data(dev, 0, 0x01, 0x02, rngmask, 0x00);
576
577
578 steps_tmp = steps - 1;
579
580 if (CR_RANGE(cmd->chanlist[1]) > 0)
581 rngmask = 0xff - 0x04;
582 else
583 rngmask = 0xff;
584
585
586
587 usbduxfast_cmd_data(dev, 1, steps_tmp / 2,
588 0x00, 0xfe & rngmask, 0x00);
589
590
591 usbduxfast_cmd_data(dev, 2, steps_tmp - steps_tmp / 2,
592 0x00, rngmask, 0x00);
593
594
595 usbduxfast_cmd_data(dev, 3, 0x01, 0x02, rngmask, 0x00);
596
597
598
599
600
601 steps_tmp = steps - 2;
602
603 if (CR_RANGE(cmd->chanlist[0]) > 0)
604 rngmask = 0xff - 0x04;
605 else
606 rngmask = 0xff;
607
608
609
610 usbduxfast_cmd_data(dev, 4, steps_tmp / 2,
611 0x00, (0xff - 0x02) & rngmask, 0x00);
612
613
614 usbduxfast_cmd_data(dev, 5, steps_tmp - steps_tmp / 2,
615 0x00, rngmask, 0x00);
616
617 usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
618 break;
619
620 case 3:
621
622
623
624 for (j = 0; j < 1; j++) {
625 int index = j * 2;
626
627 if (CR_RANGE(cmd->chanlist[j]) > 0)
628 rngmask = 0xff - 0x04;
629 else
630 rngmask = 0xff;
631
632
633
634
635
636
637 usbduxfast_cmd_data(dev, index, steps / 2,
638 0x02, rngmask, 0x00);
639
640 if (CR_RANGE(cmd->chanlist[j + 1]) > 0)
641 rngmask = 0xff - 0x04;
642 else
643 rngmask = 0xff;
644
645
646
647
648 usbduxfast_cmd_data(dev, index + 1, steps - steps / 2,
649 0x00, 0xfe & rngmask, 0x00);
650 }
651
652
653 steps_tmp = steps - 2;
654
655
656
657 usbduxfast_cmd_data(dev, 4, steps_tmp / 2,
658 0x02, rngmask, 0x00);
659
660 if (CR_RANGE(cmd->chanlist[0]) > 0)
661 rngmask = 0xff - 0x04;
662 else
663 rngmask = 0xff;
664
665
666
667
668 usbduxfast_cmd_data(dev, 5, steps_tmp - steps_tmp / 2,
669 0x00, (0xff - 0x02) & rngmask, 0x00);
670
671 usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
672 break;
673
674 case 16:
675 if (CR_RANGE(cmd->chanlist[0]) > 0)
676 rngmask = 0xff - 0x04;
677 else
678 rngmask = 0xff;
679
680 if (cmd->start_src == TRIG_EXT) {
681
682
683
684
685
686
687
688
689 usbduxfast_cmd_data(dev, 0, 0x01, 0x01,
690 (0xff - 0x02) & rngmask, 0x00);
691 } else {
692
693
694
695
696
697
698 usbduxfast_cmd_data(dev, 0, 0xff, 0x00,
699 (0xff - 0x02) & rngmask, 0x00);
700 }
701
702
703
704 usbduxfast_cmd_data(dev, 1, 0x01, 0x02, rngmask, 0x00);
705
706
707 steps = steps - 2;
708
709
710 usbduxfast_cmd_data(dev, 2, steps / 2,
711 0x00, 0xfe & rngmask, 0x00);
712
713
714 usbduxfast_cmd_data(dev, 3, steps - steps / 2,
715 0x00, rngmask, 0x00);
716
717
718
719
720 usbduxfast_cmd_data(dev, 4, 0x09, 0x01, rngmask, 0xff);
721
722 break;
723 }
724
725
726 ret = usbduxfast_send_cmd(dev, SENDADCOMMANDS);
727 if (ret < 0)
728 goto cmd_exit;
729
730 if ((cmd->start_src == TRIG_NOW) || (cmd->start_src == TRIG_EXT)) {
731
732 devpriv->ai_cmd_running = 1;
733 ret = usbduxfast_submit_urb(dev);
734 if (ret < 0) {
735 devpriv->ai_cmd_running = 0;
736
737 goto cmd_exit;
738 }
739 s->async->inttrig = NULL;
740 } else {
741 s->async->inttrig = usbduxfast_ai_inttrig;
742 }
743
744cmd_exit:
745 mutex_unlock(&devpriv->mut);
746
747 return ret;
748}
749
750
751
752
753static int usbduxfast_ai_insn_read(struct comedi_device *dev,
754 struct comedi_subdevice *s,
755 struct comedi_insn *insn,
756 unsigned int *data)
757{
758 struct usb_device *usb = comedi_to_usb_dev(dev);
759 struct usbduxfast_private *devpriv = dev->private;
760 unsigned int chan = CR_CHAN(insn->chanspec);
761 unsigned int range = CR_RANGE(insn->chanspec);
762 u8 rngmask = range ? (0xff - 0x04) : 0xff;
763 int i, j, n, actual_length;
764 int ret;
765
766 mutex_lock(&devpriv->mut);
767
768 if (devpriv->ai_cmd_running) {
769 dev_err(dev->class_dev,
770 "ai_insn_read not possible, async cmd is running\n");
771 mutex_unlock(&devpriv->mut);
772 return -EBUSY;
773 }
774
775
776
777
778
779 usbduxfast_cmd_data(dev, 0, 0x01, 0x02, rngmask, 0x00);
780
781
782 usbduxfast_cmd_data(dev, 1, 0x0c, 0x00, 0xfe & rngmask, 0x00);
783 usbduxfast_cmd_data(dev, 2, 0x01, 0x00, 0xfe & rngmask, 0x00);
784 usbduxfast_cmd_data(dev, 3, 0x01, 0x00, 0xfe & rngmask, 0x00);
785 usbduxfast_cmd_data(dev, 4, 0x01, 0x00, 0xfe & rngmask, 0x00);
786
787
788 usbduxfast_cmd_data(dev, 5, 0x0c, 0x00, rngmask, 0x00);
789 usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
790
791 ret = usbduxfast_send_cmd(dev, SENDADCOMMANDS);
792 if (ret < 0) {
793 mutex_unlock(&devpriv->mut);
794 return ret;
795 }
796
797 for (i = 0; i < PACKETS_TO_IGNORE; i++) {
798 ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, BULKINEP),
799 devpriv->inbuf, SIZEINBUF,
800 &actual_length, 10000);
801 if (ret < 0) {
802 dev_err(dev->class_dev, "insn timeout, no data\n");
803 mutex_unlock(&devpriv->mut);
804 return ret;
805 }
806 }
807
808 for (i = 0; i < insn->n;) {
809 ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, BULKINEP),
810 devpriv->inbuf, SIZEINBUF,
811 &actual_length, 10000);
812 if (ret < 0) {
813 dev_err(dev->class_dev, "insn data error: %d\n", ret);
814 mutex_unlock(&devpriv->mut);
815 return ret;
816 }
817 n = actual_length / sizeof(u16);
818 if ((n % 16) != 0) {
819 dev_err(dev->class_dev, "insn data packet corrupted\n");
820 mutex_unlock(&devpriv->mut);
821 return -EINVAL;
822 }
823 for (j = chan; (j < n) && (i < insn->n); j = j + 16) {
824 data[i] = ((u16 *)(devpriv->inbuf))[j];
825 i++;
826 }
827 }
828
829 mutex_unlock(&devpriv->mut);
830
831 return insn->n;
832}
833
834static int usbduxfast_upload_firmware(struct comedi_device *dev,
835 const u8 *data, size_t size,
836 unsigned long context)
837{
838 struct usb_device *usb = comedi_to_usb_dev(dev);
839 u8 *buf;
840 unsigned char *tmp;
841 int ret;
842
843 if (!data)
844 return 0;
845
846 if (size > FIRMWARE_MAX_LEN) {
847 dev_err(dev->class_dev, "firmware binary too large for FX2\n");
848 return -ENOMEM;
849 }
850
851
852 buf = kmemdup(data, size, GFP_KERNEL);
853 if (!buf)
854 return -ENOMEM;
855
856
857 tmp = kmalloc(1, GFP_KERNEL);
858 if (!tmp) {
859 kfree(buf);
860 return -ENOMEM;
861 }
862
863
864 *tmp = 1;
865 ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
866 USBDUXFASTSUB_FIRMWARE,
867 VENDOR_DIR_OUT,
868 USBDUXFASTSUB_CPUCS, 0x0000,
869 tmp, 1,
870 EZTIMEOUT);
871 if (ret < 0) {
872 dev_err(dev->class_dev, "can not stop firmware\n");
873 goto done;
874 }
875
876
877 ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
878 USBDUXFASTSUB_FIRMWARE,
879 VENDOR_DIR_OUT,
880 0, 0x0000,
881 buf, size,
882 EZTIMEOUT);
883 if (ret < 0) {
884 dev_err(dev->class_dev, "firmware upload failed\n");
885 goto done;
886 }
887
888
889 *tmp = 0;
890 ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
891 USBDUXFASTSUB_FIRMWARE,
892 VENDOR_DIR_OUT,
893 USBDUXFASTSUB_CPUCS, 0x0000,
894 tmp, 1,
895 EZTIMEOUT);
896 if (ret < 0)
897 dev_err(dev->class_dev, "can not start firmware\n");
898
899done:
900 kfree(tmp);
901 kfree(buf);
902 return ret;
903}
904
905static int usbduxfast_auto_attach(struct comedi_device *dev,
906 unsigned long context_unused)
907{
908 struct usb_interface *intf = comedi_to_usb_interface(dev);
909 struct usb_device *usb = comedi_to_usb_dev(dev);
910 struct usbduxfast_private *devpriv;
911 struct comedi_subdevice *s;
912 int ret;
913
914 if (usb->speed != USB_SPEED_HIGH) {
915 dev_err(dev->class_dev,
916 "This driver needs USB 2.0 to operate. Aborting...\n");
917 return -ENODEV;
918 }
919
920 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
921 if (!devpriv)
922 return -ENOMEM;
923
924 mutex_init(&devpriv->mut);
925 usb_set_intfdata(intf, devpriv);
926
927 devpriv->duxbuf = kmalloc(SIZEOFDUXBUF, GFP_KERNEL);
928 if (!devpriv->duxbuf)
929 return -ENOMEM;
930
931 ret = usb_set_interface(usb,
932 intf->altsetting->desc.bInterfaceNumber, 1);
933 if (ret < 0) {
934 dev_err(dev->class_dev,
935 "could not switch to alternate setting 1\n");
936 return -ENODEV;
937 }
938
939 devpriv->urb = usb_alloc_urb(0, GFP_KERNEL);
940 if (!devpriv->urb)
941 return -ENOMEM;
942
943 devpriv->inbuf = kmalloc(SIZEINBUF, GFP_KERNEL);
944 if (!devpriv->inbuf)
945 return -ENOMEM;
946
947 ret = comedi_load_firmware(dev, &usb->dev, FIRMWARE,
948 usbduxfast_upload_firmware, 0);
949 if (ret)
950 return ret;
951
952 ret = comedi_alloc_subdevices(dev, 1);
953 if (ret)
954 return ret;
955
956
957 s = &dev->subdevices[0];
958 dev->read_subdev = s;
959 s->type = COMEDI_SUBD_AI;
960 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
961 s->n_chan = 16;
962 s->maxdata = 0x1000;
963 s->range_table = &range_usbduxfast_ai_range;
964 s->insn_read = usbduxfast_ai_insn_read;
965 s->len_chanlist = s->n_chan;
966 s->do_cmdtest = usbduxfast_ai_cmdtest;
967 s->do_cmd = usbduxfast_ai_cmd;
968 s->cancel = usbduxfast_ai_cancel;
969
970 return 0;
971}
972
973static void usbduxfast_detach(struct comedi_device *dev)
974{
975 struct usb_interface *intf = comedi_to_usb_interface(dev);
976 struct usbduxfast_private *devpriv = dev->private;
977
978 if (!devpriv)
979 return;
980
981 mutex_lock(&devpriv->mut);
982
983 usb_set_intfdata(intf, NULL);
984
985 if (devpriv->urb) {
986
987 usb_kill_urb(devpriv->urb);
988
989 kfree(devpriv->inbuf);
990 usb_free_urb(devpriv->urb);
991 }
992
993 kfree(devpriv->duxbuf);
994
995 mutex_unlock(&devpriv->mut);
996}
997
998static struct comedi_driver usbduxfast_driver = {
999 .driver_name = "usbduxfast",
1000 .module = THIS_MODULE,
1001 .auto_attach = usbduxfast_auto_attach,
1002 .detach = usbduxfast_detach,
1003};
1004
1005static int usbduxfast_usb_probe(struct usb_interface *intf,
1006 const struct usb_device_id *id)
1007{
1008 return comedi_usb_auto_config(intf, &usbduxfast_driver, 0);
1009}
1010
1011static const struct usb_device_id usbduxfast_usb_table[] = {
1012
1013 { USB_DEVICE(0x13d8, 0x0010) },
1014 { USB_DEVICE(0x13d8, 0x0011) },
1015 { }
1016};
1017MODULE_DEVICE_TABLE(usb, usbduxfast_usb_table);
1018
1019static struct usb_driver usbduxfast_usb_driver = {
1020 .name = "usbduxfast",
1021 .probe = usbduxfast_usb_probe,
1022 .disconnect = comedi_usb_auto_unconfig,
1023 .id_table = usbduxfast_usb_table,
1024};
1025module_comedi_usb_driver(usbduxfast_driver, usbduxfast_usb_driver);
1026
1027MODULE_AUTHOR("Bernd Porr, BerndPorr@f2s.com");
1028MODULE_DESCRIPTION("USB-DUXfast, BerndPorr@f2s.com");
1029MODULE_LICENSE("GPL");
1030MODULE_FIRMWARE(FIRMWARE);
1031