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#include <linux/kernel.h>
31#include <linux/module.h>
32#include <linux/mutex.h>
33#include <linux/errno.h>
34#include <linux/input.h>
35#include <linux/slab.h>
36#include <linux/poll.h>
37#include <linux/uaccess.h>
38
39#include "../comedi_usb.h"
40
41enum {
42 DEVICE_VMK8055,
43 DEVICE_VMK8061
44};
45
46#define VMK8055_DI_REG 0x00
47#define VMK8055_DO_REG 0x01
48#define VMK8055_AO1_REG 0x02
49#define VMK8055_AO2_REG 0x03
50#define VMK8055_AI1_REG 0x02
51#define VMK8055_AI2_REG 0x03
52#define VMK8055_CNT1_REG 0x04
53#define VMK8055_CNT2_REG 0x06
54
55#define VMK8061_CH_REG 0x01
56#define VMK8061_DI_REG 0x01
57#define VMK8061_DO_REG 0x01
58#define VMK8061_PWM_REG1 0x01
59#define VMK8061_PWM_REG2 0x02
60#define VMK8061_CNT_REG 0x02
61#define VMK8061_AO_REG 0x02
62#define VMK8061_AI_REG1 0x02
63#define VMK8061_AI_REG2 0x03
64
65#define VMK8055_CMD_RST 0x00
66#define VMK8055_CMD_DEB1_TIME 0x01
67#define VMK8055_CMD_DEB2_TIME 0x02
68#define VMK8055_CMD_RST_CNT1 0x03
69#define VMK8055_CMD_RST_CNT2 0x04
70#define VMK8055_CMD_WRT_AD 0x05
71
72#define VMK8061_CMD_RD_AI 0x00
73#define VMK8061_CMR_RD_ALL_AI 0x01
74#define VMK8061_CMD_SET_AO 0x02
75#define VMK8061_CMD_SET_ALL_AO 0x03
76#define VMK8061_CMD_OUT_PWM 0x04
77#define VMK8061_CMD_RD_DI 0x05
78#define VMK8061_CMD_DO 0x06
79#define VMK8061_CMD_CLR_DO 0x07
80#define VMK8061_CMD_SET_DO 0x08
81#define VMK8061_CMD_RD_CNT 0x09
82#define VMK8061_CMD_RST_CNT 0x0a
83#define VMK8061_CMD_RD_VERSION 0x0b
84#define VMK8061_CMD_RD_JMP_STAT 0x0c
85#define VMK8061_CMD_RD_PWR_STAT 0x0d
86#define VMK8061_CMD_RD_DO 0x0e
87#define VMK8061_CMD_RD_AO 0x0f
88#define VMK8061_CMD_RD_PWM 0x10
89
90#define IC3_VERSION BIT(0)
91#define IC6_VERSION BIT(1)
92
93enum vmk80xx_model {
94 VMK8055_MODEL,
95 VMK8061_MODEL
96};
97
98static const struct comedi_lrange vmk8061_range = {
99 2, {
100 UNI_RANGE(5),
101 UNI_RANGE(10)
102 }
103};
104
105struct vmk80xx_board {
106 const char *name;
107 enum vmk80xx_model model;
108 const struct comedi_lrange *range;
109 int ai_nchans;
110 unsigned int ai_maxdata;
111 int ao_nchans;
112 int di_nchans;
113 unsigned int cnt_maxdata;
114 int pwm_nchans;
115 unsigned int pwm_maxdata;
116};
117
118static const struct vmk80xx_board vmk80xx_boardinfo[] = {
119 [DEVICE_VMK8055] = {
120 .name = "K8055 (VM110)",
121 .model = VMK8055_MODEL,
122 .range = &range_unipolar5,
123 .ai_nchans = 2,
124 .ai_maxdata = 0x00ff,
125 .ao_nchans = 2,
126 .di_nchans = 6,
127 .cnt_maxdata = 0xffff,
128 },
129 [DEVICE_VMK8061] = {
130 .name = "K8061 (VM140)",
131 .model = VMK8061_MODEL,
132 .range = &vmk8061_range,
133 .ai_nchans = 8,
134 .ai_maxdata = 0x03ff,
135 .ao_nchans = 8,
136 .di_nchans = 8,
137 .cnt_maxdata = 0,
138 .pwm_nchans = 1,
139 .pwm_maxdata = 0x03ff,
140 },
141};
142
143struct vmk80xx_private {
144 struct usb_endpoint_descriptor *ep_rx;
145 struct usb_endpoint_descriptor *ep_tx;
146 struct semaphore limit_sem;
147 unsigned char *usb_rx_buf;
148 unsigned char *usb_tx_buf;
149 enum vmk80xx_model model;
150};
151
152static void vmk80xx_do_bulk_msg(struct comedi_device *dev)
153{
154 struct vmk80xx_private *devpriv = dev->private;
155 struct usb_device *usb = comedi_to_usb_dev(dev);
156 __u8 tx_addr;
157 __u8 rx_addr;
158 unsigned int tx_pipe;
159 unsigned int rx_pipe;
160 size_t size;
161
162 tx_addr = devpriv->ep_tx->bEndpointAddress;
163 rx_addr = devpriv->ep_rx->bEndpointAddress;
164 tx_pipe = usb_sndbulkpipe(usb, tx_addr);
165 rx_pipe = usb_rcvbulkpipe(usb, rx_addr);
166
167
168
169
170
171 size = usb_endpoint_maxp(devpriv->ep_tx);
172
173 usb_bulk_msg(usb, tx_pipe, devpriv->usb_tx_buf,
174 size, NULL, devpriv->ep_tx->bInterval);
175 usb_bulk_msg(usb, rx_pipe, devpriv->usb_rx_buf, size, NULL, HZ * 10);
176}
177
178static int vmk80xx_read_packet(struct comedi_device *dev)
179{
180 struct vmk80xx_private *devpriv = dev->private;
181 struct usb_device *usb = comedi_to_usb_dev(dev);
182 struct usb_endpoint_descriptor *ep;
183 unsigned int pipe;
184
185 if (devpriv->model == VMK8061_MODEL) {
186 vmk80xx_do_bulk_msg(dev);
187 return 0;
188 }
189
190 ep = devpriv->ep_rx;
191 pipe = usb_rcvintpipe(usb, ep->bEndpointAddress);
192 return usb_interrupt_msg(usb, pipe, devpriv->usb_rx_buf,
193 usb_endpoint_maxp(ep), NULL,
194 HZ * 10);
195}
196
197static int vmk80xx_write_packet(struct comedi_device *dev, int cmd)
198{
199 struct vmk80xx_private *devpriv = dev->private;
200 struct usb_device *usb = comedi_to_usb_dev(dev);
201 struct usb_endpoint_descriptor *ep;
202 unsigned int pipe;
203
204 devpriv->usb_tx_buf[0] = cmd;
205
206 if (devpriv->model == VMK8061_MODEL) {
207 vmk80xx_do_bulk_msg(dev);
208 return 0;
209 }
210
211 ep = devpriv->ep_tx;
212 pipe = usb_sndintpipe(usb, ep->bEndpointAddress);
213 return usb_interrupt_msg(usb, pipe, devpriv->usb_tx_buf,
214 usb_endpoint_maxp(ep), NULL,
215 HZ * 10);
216}
217
218static int vmk80xx_reset_device(struct comedi_device *dev)
219{
220 struct vmk80xx_private *devpriv = dev->private;
221 size_t size;
222 int retval;
223
224 size = usb_endpoint_maxp(devpriv->ep_tx);
225 memset(devpriv->usb_tx_buf, 0, size);
226 retval = vmk80xx_write_packet(dev, VMK8055_CMD_RST);
227 if (retval)
228 return retval;
229
230 return vmk80xx_write_packet(dev, VMK8055_CMD_WRT_AD);
231}
232
233static int vmk80xx_ai_insn_read(struct comedi_device *dev,
234 struct comedi_subdevice *s,
235 struct comedi_insn *insn,
236 unsigned int *data)
237{
238 struct vmk80xx_private *devpriv = dev->private;
239 int chan;
240 int reg[2];
241 int n;
242
243 down(&devpriv->limit_sem);
244 chan = CR_CHAN(insn->chanspec);
245
246 switch (devpriv->model) {
247 case VMK8055_MODEL:
248 if (!chan)
249 reg[0] = VMK8055_AI1_REG;
250 else
251 reg[0] = VMK8055_AI2_REG;
252 break;
253 case VMK8061_MODEL:
254 default:
255 reg[0] = VMK8061_AI_REG1;
256 reg[1] = VMK8061_AI_REG2;
257 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AI;
258 devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
259 break;
260 }
261
262 for (n = 0; n < insn->n; n++) {
263 if (vmk80xx_read_packet(dev))
264 break;
265
266 if (devpriv->model == VMK8055_MODEL) {
267 data[n] = devpriv->usb_rx_buf[reg[0]];
268 continue;
269 }
270
271
272 data[n] = devpriv->usb_rx_buf[reg[0]] + 256 *
273 devpriv->usb_rx_buf[reg[1]];
274 }
275
276 up(&devpriv->limit_sem);
277
278 return n;
279}
280
281static int vmk80xx_ao_insn_write(struct comedi_device *dev,
282 struct comedi_subdevice *s,
283 struct comedi_insn *insn,
284 unsigned int *data)
285{
286 struct vmk80xx_private *devpriv = dev->private;
287 int chan;
288 int cmd;
289 int reg;
290 int n;
291
292 down(&devpriv->limit_sem);
293 chan = CR_CHAN(insn->chanspec);
294
295 switch (devpriv->model) {
296 case VMK8055_MODEL:
297 cmd = VMK8055_CMD_WRT_AD;
298 if (!chan)
299 reg = VMK8055_AO1_REG;
300 else
301 reg = VMK8055_AO2_REG;
302 break;
303 default:
304 cmd = VMK8061_CMD_SET_AO;
305 reg = VMK8061_AO_REG;
306 devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
307 break;
308 }
309
310 for (n = 0; n < insn->n; n++) {
311 devpriv->usb_tx_buf[reg] = data[n];
312
313 if (vmk80xx_write_packet(dev, cmd))
314 break;
315 }
316
317 up(&devpriv->limit_sem);
318
319 return n;
320}
321
322static int vmk80xx_ao_insn_read(struct comedi_device *dev,
323 struct comedi_subdevice *s,
324 struct comedi_insn *insn,
325 unsigned int *data)
326{
327 struct vmk80xx_private *devpriv = dev->private;
328 int chan;
329 int reg;
330 int n;
331
332 down(&devpriv->limit_sem);
333 chan = CR_CHAN(insn->chanspec);
334
335 reg = VMK8061_AO_REG - 1;
336
337 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AO;
338
339 for (n = 0; n < insn->n; n++) {
340 if (vmk80xx_read_packet(dev))
341 break;
342
343 data[n] = devpriv->usb_rx_buf[reg + chan];
344 }
345
346 up(&devpriv->limit_sem);
347
348 return n;
349}
350
351static int vmk80xx_di_insn_bits(struct comedi_device *dev,
352 struct comedi_subdevice *s,
353 struct comedi_insn *insn,
354 unsigned int *data)
355{
356 struct vmk80xx_private *devpriv = dev->private;
357 unsigned char *rx_buf;
358 int reg;
359 int retval;
360
361 down(&devpriv->limit_sem);
362
363 rx_buf = devpriv->usb_rx_buf;
364
365 if (devpriv->model == VMK8061_MODEL) {
366 reg = VMK8061_DI_REG;
367 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_DI;
368 } else {
369 reg = VMK8055_DI_REG;
370 }
371
372 retval = vmk80xx_read_packet(dev);
373
374 if (!retval) {
375 if (devpriv->model == VMK8055_MODEL)
376 data[1] = (((rx_buf[reg] >> 4) & 0x03) |
377 ((rx_buf[reg] << 2) & 0x04) |
378 ((rx_buf[reg] >> 3) & 0x18));
379 else
380 data[1] = rx_buf[reg];
381
382 retval = 2;
383 }
384
385 up(&devpriv->limit_sem);
386
387 return retval;
388}
389
390static int vmk80xx_do_insn_bits(struct comedi_device *dev,
391 struct comedi_subdevice *s,
392 struct comedi_insn *insn,
393 unsigned int *data)
394{
395 struct vmk80xx_private *devpriv = dev->private;
396 unsigned char *rx_buf = devpriv->usb_rx_buf;
397 unsigned char *tx_buf = devpriv->usb_tx_buf;
398 int reg, cmd;
399 int ret = 0;
400
401 if (devpriv->model == VMK8061_MODEL) {
402 reg = VMK8061_DO_REG;
403 cmd = VMK8061_CMD_DO;
404 } else {
405 reg = VMK8055_DO_REG;
406 cmd = VMK8055_CMD_WRT_AD;
407 }
408
409 down(&devpriv->limit_sem);
410
411 if (comedi_dio_update_state(s, data)) {
412 tx_buf[reg] = s->state;
413 ret = vmk80xx_write_packet(dev, cmd);
414 if (ret)
415 goto out;
416 }
417
418 if (devpriv->model == VMK8061_MODEL) {
419 tx_buf[0] = VMK8061_CMD_RD_DO;
420 ret = vmk80xx_read_packet(dev);
421 if (ret)
422 goto out;
423 data[1] = rx_buf[reg];
424 } else {
425 data[1] = s->state;
426 }
427
428out:
429 up(&devpriv->limit_sem);
430
431 return ret ? ret : insn->n;
432}
433
434static int vmk80xx_cnt_insn_read(struct comedi_device *dev,
435 struct comedi_subdevice *s,
436 struct comedi_insn *insn,
437 unsigned int *data)
438{
439 struct vmk80xx_private *devpriv = dev->private;
440 int chan;
441 int reg[2];
442 int n;
443
444 down(&devpriv->limit_sem);
445 chan = CR_CHAN(insn->chanspec);
446
447 switch (devpriv->model) {
448 case VMK8055_MODEL:
449 if (!chan)
450 reg[0] = VMK8055_CNT1_REG;
451 else
452 reg[0] = VMK8055_CNT2_REG;
453 break;
454 case VMK8061_MODEL:
455 default:
456 reg[0] = VMK8061_CNT_REG;
457 reg[1] = VMK8061_CNT_REG;
458 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_CNT;
459 break;
460 }
461
462 for (n = 0; n < insn->n; n++) {
463 if (vmk80xx_read_packet(dev))
464 break;
465
466 if (devpriv->model == VMK8055_MODEL)
467 data[n] = devpriv->usb_rx_buf[reg[0]];
468 else
469 data[n] = devpriv->usb_rx_buf[reg[0] * (chan + 1) + 1]
470 + 256 * devpriv->usb_rx_buf[reg[1] * 2 + 2];
471 }
472
473 up(&devpriv->limit_sem);
474
475 return n;
476}
477
478static int vmk80xx_cnt_insn_config(struct comedi_device *dev,
479 struct comedi_subdevice *s,
480 struct comedi_insn *insn,
481 unsigned int *data)
482{
483 struct vmk80xx_private *devpriv = dev->private;
484 unsigned int chan = CR_CHAN(insn->chanspec);
485 int cmd;
486 int reg;
487 int ret;
488
489 down(&devpriv->limit_sem);
490 switch (data[0]) {
491 case INSN_CONFIG_RESET:
492 if (devpriv->model == VMK8055_MODEL) {
493 if (!chan) {
494 cmd = VMK8055_CMD_RST_CNT1;
495 reg = VMK8055_CNT1_REG;
496 } else {
497 cmd = VMK8055_CMD_RST_CNT2;
498 reg = VMK8055_CNT2_REG;
499 }
500 devpriv->usb_tx_buf[reg] = 0x00;
501 } else {
502 cmd = VMK8061_CMD_RST_CNT;
503 }
504 ret = vmk80xx_write_packet(dev, cmd);
505 break;
506 default:
507 ret = -EINVAL;
508 break;
509 }
510 up(&devpriv->limit_sem);
511
512 return ret ? ret : insn->n;
513}
514
515static int vmk80xx_cnt_insn_write(struct comedi_device *dev,
516 struct comedi_subdevice *s,
517 struct comedi_insn *insn,
518 unsigned int *data)
519{
520 struct vmk80xx_private *devpriv = dev->private;
521 unsigned long debtime;
522 unsigned long val;
523 int chan;
524 int cmd;
525 int n;
526
527 down(&devpriv->limit_sem);
528 chan = CR_CHAN(insn->chanspec);
529
530 if (!chan)
531 cmd = VMK8055_CMD_DEB1_TIME;
532 else
533 cmd = VMK8055_CMD_DEB2_TIME;
534
535 for (n = 0; n < insn->n; n++) {
536 debtime = data[n];
537 if (debtime == 0)
538 debtime = 1;
539
540
541 if (debtime > 7450)
542 debtime = 7450;
543
544 val = int_sqrt(debtime * 1000 / 115);
545 if (((val + 1) * val) < debtime * 1000 / 115)
546 val += 1;
547
548 devpriv->usb_tx_buf[6 + chan] = val;
549
550 if (vmk80xx_write_packet(dev, cmd))
551 break;
552 }
553
554 up(&devpriv->limit_sem);
555
556 return n;
557}
558
559static int vmk80xx_pwm_insn_read(struct comedi_device *dev,
560 struct comedi_subdevice *s,
561 struct comedi_insn *insn,
562 unsigned int *data)
563{
564 struct vmk80xx_private *devpriv = dev->private;
565 unsigned char *tx_buf;
566 unsigned char *rx_buf;
567 int reg[2];
568 int n;
569
570 down(&devpriv->limit_sem);
571
572 tx_buf = devpriv->usb_tx_buf;
573 rx_buf = devpriv->usb_rx_buf;
574
575 reg[0] = VMK8061_PWM_REG1;
576 reg[1] = VMK8061_PWM_REG2;
577
578 tx_buf[0] = VMK8061_CMD_RD_PWM;
579
580 for (n = 0; n < insn->n; n++) {
581 if (vmk80xx_read_packet(dev))
582 break;
583
584 data[n] = rx_buf[reg[0]] + 4 * rx_buf[reg[1]];
585 }
586
587 up(&devpriv->limit_sem);
588
589 return n;
590}
591
592static int vmk80xx_pwm_insn_write(struct comedi_device *dev,
593 struct comedi_subdevice *s,
594 struct comedi_insn *insn,
595 unsigned int *data)
596{
597 struct vmk80xx_private *devpriv = dev->private;
598 unsigned char *tx_buf;
599 int reg[2];
600 int cmd;
601 int n;
602
603 down(&devpriv->limit_sem);
604
605 tx_buf = devpriv->usb_tx_buf;
606
607 reg[0] = VMK8061_PWM_REG1;
608 reg[1] = VMK8061_PWM_REG2;
609
610 cmd = VMK8061_CMD_OUT_PWM;
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625 for (n = 0; n < insn->n; n++) {
626 tx_buf[reg[0]] = (unsigned char)(data[n] & 0x03);
627 tx_buf[reg[1]] = (unsigned char)(data[n] >> 2) & 0xff;
628
629 if (vmk80xx_write_packet(dev, cmd))
630 break;
631 }
632
633 up(&devpriv->limit_sem);
634
635 return n;
636}
637
638static int vmk80xx_find_usb_endpoints(struct comedi_device *dev)
639{
640 struct vmk80xx_private *devpriv = dev->private;
641 struct usb_interface *intf = comedi_to_usb_interface(dev);
642 struct usb_host_interface *iface_desc = intf->cur_altsetting;
643 struct usb_endpoint_descriptor *ep_desc;
644 int i;
645
646 if (iface_desc->desc.bNumEndpoints != 2)
647 return -ENODEV;
648
649 for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
650 ep_desc = &iface_desc->endpoint[i].desc;
651
652 if (usb_endpoint_is_int_in(ep_desc) ||
653 usb_endpoint_is_bulk_in(ep_desc)) {
654 if (!devpriv->ep_rx)
655 devpriv->ep_rx = ep_desc;
656 continue;
657 }
658
659 if (usb_endpoint_is_int_out(ep_desc) ||
660 usb_endpoint_is_bulk_out(ep_desc)) {
661 if (!devpriv->ep_tx)
662 devpriv->ep_tx = ep_desc;
663 continue;
664 }
665 }
666
667 if (!devpriv->ep_rx || !devpriv->ep_tx)
668 return -ENODEV;
669
670 return 0;
671}
672
673static int vmk80xx_alloc_usb_buffers(struct comedi_device *dev)
674{
675 struct vmk80xx_private *devpriv = dev->private;
676 size_t size;
677
678 size = usb_endpoint_maxp(devpriv->ep_rx);
679 devpriv->usb_rx_buf = kzalloc(size, GFP_KERNEL);
680 if (!devpriv->usb_rx_buf)
681 return -ENOMEM;
682
683 size = usb_endpoint_maxp(devpriv->ep_tx);
684 devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
685 if (!devpriv->usb_tx_buf) {
686 kfree(devpriv->usb_rx_buf);
687 return -ENOMEM;
688 }
689
690 return 0;
691}
692
693static int vmk80xx_init_subdevices(struct comedi_device *dev)
694{
695 const struct vmk80xx_board *board = dev->board_ptr;
696 struct vmk80xx_private *devpriv = dev->private;
697 struct comedi_subdevice *s;
698 int n_subd;
699 int ret;
700
701 down(&devpriv->limit_sem);
702
703 if (devpriv->model == VMK8055_MODEL)
704 n_subd = 5;
705 else
706 n_subd = 6;
707 ret = comedi_alloc_subdevices(dev, n_subd);
708 if (ret) {
709 up(&devpriv->limit_sem);
710 return ret;
711 }
712
713
714 s = &dev->subdevices[0];
715 s->type = COMEDI_SUBD_AI;
716 s->subdev_flags = SDF_READABLE | SDF_GROUND;
717 s->n_chan = board->ai_nchans;
718 s->maxdata = board->ai_maxdata;
719 s->range_table = board->range;
720 s->insn_read = vmk80xx_ai_insn_read;
721
722
723 s = &dev->subdevices[1];
724 s->type = COMEDI_SUBD_AO;
725 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
726 s->n_chan = board->ao_nchans;
727 s->maxdata = 0x00ff;
728 s->range_table = board->range;
729 s->insn_write = vmk80xx_ao_insn_write;
730 if (devpriv->model == VMK8061_MODEL) {
731 s->subdev_flags |= SDF_READABLE;
732 s->insn_read = vmk80xx_ao_insn_read;
733 }
734
735
736 s = &dev->subdevices[2];
737 s->type = COMEDI_SUBD_DI;
738 s->subdev_flags = SDF_READABLE;
739 s->n_chan = board->di_nchans;
740 s->maxdata = 1;
741 s->range_table = &range_digital;
742 s->insn_bits = vmk80xx_di_insn_bits;
743
744
745 s = &dev->subdevices[3];
746 s->type = COMEDI_SUBD_DO;
747 s->subdev_flags = SDF_WRITABLE;
748 s->n_chan = 8;
749 s->maxdata = 1;
750 s->range_table = &range_digital;
751 s->insn_bits = vmk80xx_do_insn_bits;
752
753
754 s = &dev->subdevices[4];
755 s->type = COMEDI_SUBD_COUNTER;
756 s->subdev_flags = SDF_READABLE;
757 s->n_chan = 2;
758 s->maxdata = board->cnt_maxdata;
759 s->insn_read = vmk80xx_cnt_insn_read;
760 s->insn_config = vmk80xx_cnt_insn_config;
761 if (devpriv->model == VMK8055_MODEL) {
762 s->subdev_flags |= SDF_WRITABLE;
763 s->insn_write = vmk80xx_cnt_insn_write;
764 }
765
766
767 if (devpriv->model == VMK8061_MODEL) {
768 s = &dev->subdevices[5];
769 s->type = COMEDI_SUBD_PWM;
770 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
771 s->n_chan = board->pwm_nchans;
772 s->maxdata = board->pwm_maxdata;
773 s->insn_read = vmk80xx_pwm_insn_read;
774 s->insn_write = vmk80xx_pwm_insn_write;
775 }
776
777 up(&devpriv->limit_sem);
778
779 return 0;
780}
781
782static int vmk80xx_auto_attach(struct comedi_device *dev,
783 unsigned long context)
784{
785 struct usb_interface *intf = comedi_to_usb_interface(dev);
786 const struct vmk80xx_board *board = NULL;
787 struct vmk80xx_private *devpriv;
788 int ret;
789
790 if (context < ARRAY_SIZE(vmk80xx_boardinfo))
791 board = &vmk80xx_boardinfo[context];
792 if (!board)
793 return -ENODEV;
794 dev->board_ptr = board;
795 dev->board_name = board->name;
796
797 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
798 if (!devpriv)
799 return -ENOMEM;
800
801 devpriv->model = board->model;
802
803 ret = vmk80xx_find_usb_endpoints(dev);
804 if (ret)
805 return ret;
806
807 ret = vmk80xx_alloc_usb_buffers(dev);
808 if (ret)
809 return ret;
810
811 sema_init(&devpriv->limit_sem, 8);
812
813 usb_set_intfdata(intf, devpriv);
814
815 if (devpriv->model == VMK8055_MODEL)
816 vmk80xx_reset_device(dev);
817
818 return vmk80xx_init_subdevices(dev);
819}
820
821static void vmk80xx_detach(struct comedi_device *dev)
822{
823 struct usb_interface *intf = comedi_to_usb_interface(dev);
824 struct vmk80xx_private *devpriv = dev->private;
825
826 if (!devpriv)
827 return;
828
829 down(&devpriv->limit_sem);
830
831 usb_set_intfdata(intf, NULL);
832
833 kfree(devpriv->usb_rx_buf);
834 kfree(devpriv->usb_tx_buf);
835
836 up(&devpriv->limit_sem);
837}
838
839static struct comedi_driver vmk80xx_driver = {
840 .module = THIS_MODULE,
841 .driver_name = "vmk80xx",
842 .auto_attach = vmk80xx_auto_attach,
843 .detach = vmk80xx_detach,
844};
845
846static int vmk80xx_usb_probe(struct usb_interface *intf,
847 const struct usb_device_id *id)
848{
849 return comedi_usb_auto_config(intf, &vmk80xx_driver, id->driver_info);
850}
851
852static const struct usb_device_id vmk80xx_usb_id_table[] = {
853 { USB_DEVICE(0x10cf, 0x5500), .driver_info = DEVICE_VMK8055 },
854 { USB_DEVICE(0x10cf, 0x5501), .driver_info = DEVICE_VMK8055 },
855 { USB_DEVICE(0x10cf, 0x5502), .driver_info = DEVICE_VMK8055 },
856 { USB_DEVICE(0x10cf, 0x5503), .driver_info = DEVICE_VMK8055 },
857 { USB_DEVICE(0x10cf, 0x8061), .driver_info = DEVICE_VMK8061 },
858 { USB_DEVICE(0x10cf, 0x8062), .driver_info = DEVICE_VMK8061 },
859 { USB_DEVICE(0x10cf, 0x8063), .driver_info = DEVICE_VMK8061 },
860 { USB_DEVICE(0x10cf, 0x8064), .driver_info = DEVICE_VMK8061 },
861 { USB_DEVICE(0x10cf, 0x8065), .driver_info = DEVICE_VMK8061 },
862 { USB_DEVICE(0x10cf, 0x8066), .driver_info = DEVICE_VMK8061 },
863 { USB_DEVICE(0x10cf, 0x8067), .driver_info = DEVICE_VMK8061 },
864 { USB_DEVICE(0x10cf, 0x8068), .driver_info = DEVICE_VMK8061 },
865 { }
866};
867MODULE_DEVICE_TABLE(usb, vmk80xx_usb_id_table);
868
869static struct usb_driver vmk80xx_usb_driver = {
870 .name = "vmk80xx",
871 .id_table = vmk80xx_usb_id_table,
872 .probe = vmk80xx_usb_probe,
873 .disconnect = comedi_usb_auto_unconfig,
874};
875module_comedi_usb_driver(vmk80xx_driver, vmk80xx_usb_driver);
876
877MODULE_AUTHOR("Manuel Gebele <forensixs@gmx.de>");
878MODULE_DESCRIPTION("Velleman USB Board Low-Level Driver");
879MODULE_SUPPORTED_DEVICE("K8055/K8061 aka VM110/VM140");
880MODULE_LICENSE("GPL");
881