1 // SPDX-License-Identifier: GPL-2.0+
4 * Velleman USB Board Low-Level Driver
6 * Copyright (C) 2009 Manuel Gebele <forensixs@gmx.de>, Germany
8 * COMEDI - Linux Control and Measurement Device Interface
9 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
14 * Description: Velleman USB Board Low-Level Driver
15 * Devices: [Velleman] K8055 (K8055/VM110), K8061 (K8061/VM140),
16 * VM110 (K8055/VM110), VM140 (K8061/VM140)
17 * Author: Manuel Gebele <forensixs@gmx.de>
18 * Updated: Sun, 10 May 2009 11:14:59 +0200
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>
39 #include "../comedi_usb.h"
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
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
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
72 #define VMK8061_CMD_RD_AI 0x00
73 #define VMK8061_CMR_RD_ALL_AI 0x01 /* !non-active! */
74 #define VMK8061_CMD_SET_AO 0x02
75 #define VMK8061_CMD_SET_ALL_AO 0x03 /* !non-active! */
76 #define VMK8061_CMD_OUT_PWM 0x04
77 #define VMK8061_CMD_RD_DI 0x05
78 #define VMK8061_CMD_DO 0x06 /* !non-active! */
79 #define VMK8061_CMD_CLR_DO 0x07
80 #define VMK8061_CMD_SET_DO 0x08
81 #define VMK8061_CMD_RD_CNT 0x09 /* TODO: completely pointless? */
82 #define VMK8061_CMD_RST_CNT 0x0a /* TODO: completely pointless? */
83 #define VMK8061_CMD_RD_VERSION 0x0b /* internal usage */
84 #define VMK8061_CMD_RD_JMP_STAT 0x0c /* TODO: not implemented yet */
85 #define VMK8061_CMD_RD_PWR_STAT 0x0d /* internal usage */
86 #define VMK8061_CMD_RD_DO 0x0e
87 #define VMK8061_CMD_RD_AO 0x0f
88 #define VMK8061_CMD_RD_PWM 0x10
90 #define IC3_VERSION BIT(0)
91 #define IC6_VERSION BIT(1)
98 static const struct comedi_lrange vmk8061_range
= {
105 struct vmk80xx_board
{
107 enum vmk80xx_model model
;
108 const struct comedi_lrange
*range
;
110 unsigned int ai_maxdata
;
113 unsigned int cnt_maxdata
;
115 unsigned int pwm_maxdata
;
118 static const struct vmk80xx_board vmk80xx_boardinfo
[] = {
120 .name
= "K8055 (VM110)",
121 .model
= VMK8055_MODEL
,
122 .range
= &range_unipolar5
,
124 .ai_maxdata
= 0x00ff,
127 .cnt_maxdata
= 0xffff,
130 .name
= "K8061 (VM140)",
131 .model
= VMK8061_MODEL
,
132 .range
= &vmk8061_range
,
134 .ai_maxdata
= 0x03ff,
137 .cnt_maxdata
= 0, /* unknown, device is not writeable */
139 .pwm_maxdata
= 0x03ff,
143 struct 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
;
152 static void vmk80xx_do_bulk_msg(struct comedi_device
*dev
)
154 struct vmk80xx_private
*devpriv
= dev
->private;
155 struct usb_device
*usb
= comedi_to_usb_dev(dev
);
158 unsigned int tx_pipe
;
159 unsigned int rx_pipe
;
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
);
168 * The max packet size attributes of the K8061
169 * input/output endpoints are identical
171 size
= usb_endpoint_maxp(devpriv
->ep_tx
);
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);
178 static int vmk80xx_read_packet(struct comedi_device
*dev
)
180 struct vmk80xx_private
*devpriv
= dev
->private;
181 struct usb_device
*usb
= comedi_to_usb_dev(dev
);
182 struct usb_endpoint_descriptor
*ep
;
185 if (devpriv
->model
== VMK8061_MODEL
) {
186 vmk80xx_do_bulk_msg(dev
);
191 pipe
= usb_rcvintpipe(usb
, ep
->bEndpointAddress
);
192 return usb_interrupt_msg(usb
, pipe
, devpriv
->usb_rx_buf
,
193 usb_endpoint_maxp(ep
), NULL
,
197 static int vmk80xx_write_packet(struct comedi_device
*dev
, int cmd
)
199 struct vmk80xx_private
*devpriv
= dev
->private;
200 struct usb_device
*usb
= comedi_to_usb_dev(dev
);
201 struct usb_endpoint_descriptor
*ep
;
204 devpriv
->usb_tx_buf
[0] = cmd
;
206 if (devpriv
->model
== VMK8061_MODEL
) {
207 vmk80xx_do_bulk_msg(dev
);
212 pipe
= usb_sndintpipe(usb
, ep
->bEndpointAddress
);
213 return usb_interrupt_msg(usb
, pipe
, devpriv
->usb_tx_buf
,
214 usb_endpoint_maxp(ep
), NULL
,
218 static int vmk80xx_reset_device(struct comedi_device
*dev
)
220 struct vmk80xx_private
*devpriv
= dev
->private;
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
);
229 /* set outputs to known state as we cannot read them */
230 return vmk80xx_write_packet(dev
, VMK8055_CMD_WRT_AD
);
233 static int vmk80xx_ai_insn_read(struct comedi_device
*dev
,
234 struct comedi_subdevice
*s
,
235 struct comedi_insn
*insn
,
238 struct vmk80xx_private
*devpriv
= dev
->private;
243 down(&devpriv
->limit_sem
);
244 chan
= CR_CHAN(insn
->chanspec
);
246 switch (devpriv
->model
) {
249 reg
[0] = VMK8055_AI1_REG
;
251 reg
[0] = VMK8055_AI2_REG
;
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
;
262 for (n
= 0; n
< insn
->n
; n
++) {
263 if (vmk80xx_read_packet(dev
))
266 if (devpriv
->model
== VMK8055_MODEL
) {
267 data
[n
] = devpriv
->usb_rx_buf
[reg
[0]];
272 data
[n
] = devpriv
->usb_rx_buf
[reg
[0]] + 256 *
273 devpriv
->usb_rx_buf
[reg
[1]];
276 up(&devpriv
->limit_sem
);
281 static int vmk80xx_ao_insn_write(struct comedi_device
*dev
,
282 struct comedi_subdevice
*s
,
283 struct comedi_insn
*insn
,
286 struct vmk80xx_private
*devpriv
= dev
->private;
292 down(&devpriv
->limit_sem
);
293 chan
= CR_CHAN(insn
->chanspec
);
295 switch (devpriv
->model
) {
297 cmd
= VMK8055_CMD_WRT_AD
;
299 reg
= VMK8055_AO1_REG
;
301 reg
= VMK8055_AO2_REG
;
303 default: /* NOTE: avoid compiler warnings */
304 cmd
= VMK8061_CMD_SET_AO
;
305 reg
= VMK8061_AO_REG
;
306 devpriv
->usb_tx_buf
[VMK8061_CH_REG
] = chan
;
310 for (n
= 0; n
< insn
->n
; n
++) {
311 devpriv
->usb_tx_buf
[reg
] = data
[n
];
313 if (vmk80xx_write_packet(dev
, cmd
))
317 up(&devpriv
->limit_sem
);
322 static int vmk80xx_ao_insn_read(struct comedi_device
*dev
,
323 struct comedi_subdevice
*s
,
324 struct comedi_insn
*insn
,
327 struct vmk80xx_private
*devpriv
= dev
->private;
332 down(&devpriv
->limit_sem
);
333 chan
= CR_CHAN(insn
->chanspec
);
335 reg
= VMK8061_AO_REG
- 1;
337 devpriv
->usb_tx_buf
[0] = VMK8061_CMD_RD_AO
;
339 for (n
= 0; n
< insn
->n
; n
++) {
340 if (vmk80xx_read_packet(dev
))
343 data
[n
] = devpriv
->usb_rx_buf
[reg
+ chan
];
346 up(&devpriv
->limit_sem
);
351 static int vmk80xx_di_insn_bits(struct comedi_device
*dev
,
352 struct comedi_subdevice
*s
,
353 struct comedi_insn
*insn
,
356 struct vmk80xx_private
*devpriv
= dev
->private;
357 unsigned char *rx_buf
;
361 down(&devpriv
->limit_sem
);
363 rx_buf
= devpriv
->usb_rx_buf
;
365 if (devpriv
->model
== VMK8061_MODEL
) {
366 reg
= VMK8061_DI_REG
;
367 devpriv
->usb_tx_buf
[0] = VMK8061_CMD_RD_DI
;
369 reg
= VMK8055_DI_REG
;
372 retval
= vmk80xx_read_packet(dev
);
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));
380 data
[1] = rx_buf
[reg
];
385 up(&devpriv
->limit_sem
);
390 static int vmk80xx_do_insn_bits(struct comedi_device
*dev
,
391 struct comedi_subdevice
*s
,
392 struct comedi_insn
*insn
,
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
;
401 if (devpriv
->model
== VMK8061_MODEL
) {
402 reg
= VMK8061_DO_REG
;
403 cmd
= VMK8061_CMD_DO
;
404 } else { /* VMK8055_MODEL */
405 reg
= VMK8055_DO_REG
;
406 cmd
= VMK8055_CMD_WRT_AD
;
409 down(&devpriv
->limit_sem
);
411 if (comedi_dio_update_state(s
, data
)) {
412 tx_buf
[reg
] = s
->state
;
413 ret
= vmk80xx_write_packet(dev
, cmd
);
418 if (devpriv
->model
== VMK8061_MODEL
) {
419 tx_buf
[0] = VMK8061_CMD_RD_DO
;
420 ret
= vmk80xx_read_packet(dev
);
423 data
[1] = rx_buf
[reg
];
429 up(&devpriv
->limit_sem
);
431 return ret
? ret
: insn
->n
;
434 static int vmk80xx_cnt_insn_read(struct comedi_device
*dev
,
435 struct comedi_subdevice
*s
,
436 struct comedi_insn
*insn
,
439 struct vmk80xx_private
*devpriv
= dev
->private;
444 down(&devpriv
->limit_sem
);
445 chan
= CR_CHAN(insn
->chanspec
);
447 switch (devpriv
->model
) {
450 reg
[0] = VMK8055_CNT1_REG
;
452 reg
[0] = VMK8055_CNT2_REG
;
456 reg
[0] = VMK8061_CNT_REG
;
457 reg
[1] = VMK8061_CNT_REG
;
458 devpriv
->usb_tx_buf
[0] = VMK8061_CMD_RD_CNT
;
462 for (n
= 0; n
< insn
->n
; n
++) {
463 if (vmk80xx_read_packet(dev
))
466 if (devpriv
->model
== VMK8055_MODEL
)
467 data
[n
] = devpriv
->usb_rx_buf
[reg
[0]];
468 else /* VMK8061_MODEL */
469 data
[n
] = devpriv
->usb_rx_buf
[reg
[0] * (chan
+ 1) + 1]
470 + 256 * devpriv
->usb_rx_buf
[reg
[1] * 2 + 2];
473 up(&devpriv
->limit_sem
);
478 static int vmk80xx_cnt_insn_config(struct comedi_device
*dev
,
479 struct comedi_subdevice
*s
,
480 struct comedi_insn
*insn
,
483 struct vmk80xx_private
*devpriv
= dev
->private;
484 unsigned int chan
= CR_CHAN(insn
->chanspec
);
489 down(&devpriv
->limit_sem
);
491 case INSN_CONFIG_RESET
:
492 if (devpriv
->model
== VMK8055_MODEL
) {
494 cmd
= VMK8055_CMD_RST_CNT1
;
495 reg
= VMK8055_CNT1_REG
;
497 cmd
= VMK8055_CMD_RST_CNT2
;
498 reg
= VMK8055_CNT2_REG
;
500 devpriv
->usb_tx_buf
[reg
] = 0x00;
502 cmd
= VMK8061_CMD_RST_CNT
;
504 ret
= vmk80xx_write_packet(dev
, cmd
);
510 up(&devpriv
->limit_sem
);
512 return ret
? ret
: insn
->n
;
515 static int vmk80xx_cnt_insn_write(struct comedi_device
*dev
,
516 struct comedi_subdevice
*s
,
517 struct comedi_insn
*insn
,
520 struct vmk80xx_private
*devpriv
= dev
->private;
521 unsigned long debtime
;
527 down(&devpriv
->limit_sem
);
528 chan
= CR_CHAN(insn
->chanspec
);
531 cmd
= VMK8055_CMD_DEB1_TIME
;
533 cmd
= VMK8055_CMD_DEB2_TIME
;
535 for (n
= 0; n
< insn
->n
; n
++) {
540 /* TODO: Prevent overflows */
544 val
= int_sqrt(debtime
* 1000 / 115);
545 if (((val
+ 1) * val
) < debtime
* 1000 / 115)
548 devpriv
->usb_tx_buf
[6 + chan
] = val
;
550 if (vmk80xx_write_packet(dev
, cmd
))
554 up(&devpriv
->limit_sem
);
559 static int vmk80xx_pwm_insn_read(struct comedi_device
*dev
,
560 struct comedi_subdevice
*s
,
561 struct comedi_insn
*insn
,
564 struct vmk80xx_private
*devpriv
= dev
->private;
565 unsigned char *tx_buf
;
566 unsigned char *rx_buf
;
570 down(&devpriv
->limit_sem
);
572 tx_buf
= devpriv
->usb_tx_buf
;
573 rx_buf
= devpriv
->usb_rx_buf
;
575 reg
[0] = VMK8061_PWM_REG1
;
576 reg
[1] = VMK8061_PWM_REG2
;
578 tx_buf
[0] = VMK8061_CMD_RD_PWM
;
580 for (n
= 0; n
< insn
->n
; n
++) {
581 if (vmk80xx_read_packet(dev
))
584 data
[n
] = rx_buf
[reg
[0]] + 4 * rx_buf
[reg
[1]];
587 up(&devpriv
->limit_sem
);
592 static int vmk80xx_pwm_insn_write(struct comedi_device
*dev
,
593 struct comedi_subdevice
*s
,
594 struct comedi_insn
*insn
,
597 struct vmk80xx_private
*devpriv
= dev
->private;
598 unsigned char *tx_buf
;
603 down(&devpriv
->limit_sem
);
605 tx_buf
= devpriv
->usb_tx_buf
;
607 reg
[0] = VMK8061_PWM_REG1
;
608 reg
[1] = VMK8061_PWM_REG2
;
610 cmd
= VMK8061_CMD_OUT_PWM
;
613 * The followin piece of code was translated from the inline
614 * assembler code in the DLL source code.
617 * mov eax, k ; k is the value (data[n])
618 * and al, 03h ; al are the lower 8 bits of eax
619 * mov lo, al ; lo is the low part (tx_buf[reg[0]])
621 * shr eax, 2 ; right shift eax register by 2
622 * mov hi, al ; hi is the high part (tx_buf[reg[1]])
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;
629 if (vmk80xx_write_packet(dev
, cmd
))
633 up(&devpriv
->limit_sem
);
638 static int vmk80xx_find_usb_endpoints(struct comedi_device
*dev
)
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
;
646 if (iface_desc
->desc
.bNumEndpoints
!= 2)
649 for (i
= 0; i
< iface_desc
->desc
.bNumEndpoints
; i
++) {
650 ep_desc
= &iface_desc
->endpoint
[i
].desc
;
652 if (usb_endpoint_is_int_in(ep_desc
) ||
653 usb_endpoint_is_bulk_in(ep_desc
)) {
655 devpriv
->ep_rx
= ep_desc
;
659 if (usb_endpoint_is_int_out(ep_desc
) ||
660 usb_endpoint_is_bulk_out(ep_desc
)) {
662 devpriv
->ep_tx
= ep_desc
;
667 if (!devpriv
->ep_rx
|| !devpriv
->ep_tx
)
670 if (!usb_endpoint_maxp(devpriv
->ep_rx
) || !usb_endpoint_maxp(devpriv
->ep_tx
))
676 static int vmk80xx_alloc_usb_buffers(struct comedi_device
*dev
)
678 struct vmk80xx_private
*devpriv
= dev
->private;
681 size
= usb_endpoint_maxp(devpriv
->ep_rx
);
682 devpriv
->usb_rx_buf
= kzalloc(size
, GFP_KERNEL
);
683 if (!devpriv
->usb_rx_buf
)
686 size
= usb_endpoint_maxp(devpriv
->ep_tx
);
687 devpriv
->usb_tx_buf
= kzalloc(size
, GFP_KERNEL
);
688 if (!devpriv
->usb_tx_buf
)
694 static int vmk80xx_init_subdevices(struct comedi_device
*dev
)
696 const struct vmk80xx_board
*board
= dev
->board_ptr
;
697 struct vmk80xx_private
*devpriv
= dev
->private;
698 struct comedi_subdevice
*s
;
702 down(&devpriv
->limit_sem
);
704 if (devpriv
->model
== VMK8055_MODEL
)
708 ret
= comedi_alloc_subdevices(dev
, n_subd
);
710 up(&devpriv
->limit_sem
);
714 /* Analog input subdevice */
715 s
= &dev
->subdevices
[0];
716 s
->type
= COMEDI_SUBD_AI
;
717 s
->subdev_flags
= SDF_READABLE
| SDF_GROUND
;
718 s
->n_chan
= board
->ai_nchans
;
719 s
->maxdata
= board
->ai_maxdata
;
720 s
->range_table
= board
->range
;
721 s
->insn_read
= vmk80xx_ai_insn_read
;
723 /* Analog output subdevice */
724 s
= &dev
->subdevices
[1];
725 s
->type
= COMEDI_SUBD_AO
;
726 s
->subdev_flags
= SDF_WRITABLE
| SDF_GROUND
;
727 s
->n_chan
= board
->ao_nchans
;
729 s
->range_table
= board
->range
;
730 s
->insn_write
= vmk80xx_ao_insn_write
;
731 if (devpriv
->model
== VMK8061_MODEL
) {
732 s
->subdev_flags
|= SDF_READABLE
;
733 s
->insn_read
= vmk80xx_ao_insn_read
;
736 /* Digital input subdevice */
737 s
= &dev
->subdevices
[2];
738 s
->type
= COMEDI_SUBD_DI
;
739 s
->subdev_flags
= SDF_READABLE
;
740 s
->n_chan
= board
->di_nchans
;
742 s
->range_table
= &range_digital
;
743 s
->insn_bits
= vmk80xx_di_insn_bits
;
745 /* Digital output subdevice */
746 s
= &dev
->subdevices
[3];
747 s
->type
= COMEDI_SUBD_DO
;
748 s
->subdev_flags
= SDF_WRITABLE
;
751 s
->range_table
= &range_digital
;
752 s
->insn_bits
= vmk80xx_do_insn_bits
;
754 /* Counter subdevice */
755 s
= &dev
->subdevices
[4];
756 s
->type
= COMEDI_SUBD_COUNTER
;
757 s
->subdev_flags
= SDF_READABLE
;
759 s
->maxdata
= board
->cnt_maxdata
;
760 s
->insn_read
= vmk80xx_cnt_insn_read
;
761 s
->insn_config
= vmk80xx_cnt_insn_config
;
762 if (devpriv
->model
== VMK8055_MODEL
) {
763 s
->subdev_flags
|= SDF_WRITABLE
;
764 s
->insn_write
= vmk80xx_cnt_insn_write
;
768 if (devpriv
->model
== VMK8061_MODEL
) {
769 s
= &dev
->subdevices
[5];
770 s
->type
= COMEDI_SUBD_PWM
;
771 s
->subdev_flags
= SDF_READABLE
| SDF_WRITABLE
;
772 s
->n_chan
= board
->pwm_nchans
;
773 s
->maxdata
= board
->pwm_maxdata
;
774 s
->insn_read
= vmk80xx_pwm_insn_read
;
775 s
->insn_write
= vmk80xx_pwm_insn_write
;
778 up(&devpriv
->limit_sem
);
783 static int vmk80xx_auto_attach(struct comedi_device
*dev
,
784 unsigned long context
)
786 struct usb_interface
*intf
= comedi_to_usb_interface(dev
);
787 const struct vmk80xx_board
*board
= NULL
;
788 struct vmk80xx_private
*devpriv
;
791 if (context
< ARRAY_SIZE(vmk80xx_boardinfo
))
792 board
= &vmk80xx_boardinfo
[context
];
795 dev
->board_ptr
= board
;
796 dev
->board_name
= board
->name
;
798 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
802 devpriv
->model
= board
->model
;
804 sema_init(&devpriv
->limit_sem
, 8);
806 ret
= vmk80xx_find_usb_endpoints(dev
);
810 ret
= vmk80xx_alloc_usb_buffers(dev
);
814 usb_set_intfdata(intf
, devpriv
);
816 if (devpriv
->model
== VMK8055_MODEL
)
817 vmk80xx_reset_device(dev
);
819 return vmk80xx_init_subdevices(dev
);
822 static void vmk80xx_detach(struct comedi_device
*dev
)
824 struct usb_interface
*intf
= comedi_to_usb_interface(dev
);
825 struct vmk80xx_private
*devpriv
= dev
->private;
830 down(&devpriv
->limit_sem
);
832 usb_set_intfdata(intf
, NULL
);
834 kfree(devpriv
->usb_rx_buf
);
835 kfree(devpriv
->usb_tx_buf
);
837 up(&devpriv
->limit_sem
);
840 static struct comedi_driver vmk80xx_driver
= {
841 .module
= THIS_MODULE
,
842 .driver_name
= "vmk80xx",
843 .auto_attach
= vmk80xx_auto_attach
,
844 .detach
= vmk80xx_detach
,
847 static int vmk80xx_usb_probe(struct usb_interface
*intf
,
848 const struct usb_device_id
*id
)
850 return comedi_usb_auto_config(intf
, &vmk80xx_driver
, id
->driver_info
);
853 static const struct usb_device_id vmk80xx_usb_id_table
[] = {
854 { USB_DEVICE(0x10cf, 0x5500), .driver_info
= DEVICE_VMK8055
},
855 { USB_DEVICE(0x10cf, 0x5501), .driver_info
= DEVICE_VMK8055
},
856 { USB_DEVICE(0x10cf, 0x5502), .driver_info
= DEVICE_VMK8055
},
857 { USB_DEVICE(0x10cf, 0x5503), .driver_info
= DEVICE_VMK8055
},
858 { USB_DEVICE(0x10cf, 0x8061), .driver_info
= DEVICE_VMK8061
},
859 { USB_DEVICE(0x10cf, 0x8062), .driver_info
= DEVICE_VMK8061
},
860 { USB_DEVICE(0x10cf, 0x8063), .driver_info
= DEVICE_VMK8061
},
861 { USB_DEVICE(0x10cf, 0x8064), .driver_info
= DEVICE_VMK8061
},
862 { USB_DEVICE(0x10cf, 0x8065), .driver_info
= DEVICE_VMK8061
},
863 { USB_DEVICE(0x10cf, 0x8066), .driver_info
= DEVICE_VMK8061
},
864 { USB_DEVICE(0x10cf, 0x8067), .driver_info
= DEVICE_VMK8061
},
865 { USB_DEVICE(0x10cf, 0x8068), .driver_info
= DEVICE_VMK8061
},
868 MODULE_DEVICE_TABLE(usb
, vmk80xx_usb_id_table
);
870 static struct usb_driver vmk80xx_usb_driver
= {
872 .id_table
= vmk80xx_usb_id_table
,
873 .probe
= vmk80xx_usb_probe
,
874 .disconnect
= comedi_usb_auto_unconfig
,
876 module_comedi_usb_driver(vmk80xx_driver
, vmk80xx_usb_driver
);
878 MODULE_AUTHOR("Manuel Gebele <forensixs@gmx.de>");
879 MODULE_DESCRIPTION("Velleman USB Board Low-Level Driver");
880 MODULE_SUPPORTED_DEVICE("K8055/K8061 aka VM110/VM140");
881 MODULE_LICENSE("GPL");