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