vmk80xx.c revision 985cafccbf9b7f862aa1c5ee566801e18b5161fb
1/*
2    comedi/drivers/vmk80xx.c
3    Velleman USB Board Low-Level Driver
4
5    Copyright (C) 2009 Manuel Gebele <forensixs@gmx.de>, Germany
6
7    COMEDI - Linux Control and Measurement Device Interface
8    Copyright (C) 2000 David A. Schleef <ds@schleef.org>
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
24*/
25/*
26Driver: vmk80xx
27Description: Velleman USB Board Low-Level Driver
28Devices: K8055/K8061 aka VM110/VM140
29Author: Manuel Gebele <forensixs@gmx.de>
30Updated: Sun, 10 May 2009 11:14:59 +0200
31Status: works
32
33Supports:
34 - analog input
35 - analog output
36 - digital input
37 - digital output
38 - counter
39 - pwm
40*/
41/*
42Changelog:
43
440.8.81	-3-  code completely rewritten (adjust driver logic)
450.8.81  -2-  full support for K8061
460.8.81  -1-  fix some mistaken among others the number of
47	     supported boards and I/O handling
48
490.7.76  -4-  renamed to vmk80xx
500.7.76  -3-  detect K8061 (only theoretically supported)
510.7.76  -2-  code completely rewritten (adjust driver logic)
520.7.76  -1-  support for digital and counter subdevice
53*/
54
55#include <linux/kernel.h>
56#include <linux/module.h>
57#include <linux/mutex.h>
58#include <linux/errno.h>
59#include <linux/input.h>
60#include <linux/slab.h>
61#include <linux/poll.h>
62#include <linux/usb.h>
63#include <linux/uaccess.h>
64
65#include "../comedidev.h"
66
67MODULE_AUTHOR("Manuel Gebele <forensixs@gmx.de>");
68MODULE_DESCRIPTION("Velleman USB Board Low-Level Driver");
69MODULE_SUPPORTED_DEVICE("K8055/K8061 aka VM110/VM140");
70MODULE_VERSION("0.8.01");
71MODULE_LICENSE("GPL");
72
73enum {
74	DEVICE_VMK8055,
75	DEVICE_VMK8061
76};
77
78static struct usb_device_id vmk80xx_id_table[] = {
79	{ USB_DEVICE(0x10cf, 0x5500), .driver_info = DEVICE_VMK8055 },
80	{ USB_DEVICE(0x10cf, 0x5501), .driver_info = DEVICE_VMK8055 },
81	{ USB_DEVICE(0x10cf, 0x5502), .driver_info = DEVICE_VMK8055 },
82	{ USB_DEVICE(0x10cf, 0x5503), .driver_info = DEVICE_VMK8055 },
83	{ USB_DEVICE(0x10cf, 0x8061), .driver_info = DEVICE_VMK8061 },
84	{ USB_DEVICE(0x10cf, 0x8062), .driver_info = DEVICE_VMK8061 },
85	{ USB_DEVICE(0x10cf, 0x8063), .driver_info = DEVICE_VMK8061 },
86	{ USB_DEVICE(0x10cf, 0x8064), .driver_info = DEVICE_VMK8061 },
87	{ USB_DEVICE(0x10cf, 0x8065), .driver_info = DEVICE_VMK8061 },
88	{ USB_DEVICE(0x10cf, 0x8066), .driver_info = DEVICE_VMK8061 },
89	{ USB_DEVICE(0x10cf, 0x8067), .driver_info = DEVICE_VMK8061 },
90	{ USB_DEVICE(0x10cf, 0x8068), .driver_info = DEVICE_VMK8061 },
91	{ } /* terminating entry */
92};
93
94MODULE_DEVICE_TABLE(usb, vmk80xx_id_table);
95
96#define VMK8055_DI_REG          0x00
97#define VMK8055_DO_REG          0x01
98#define VMK8055_AO1_REG         0x02
99#define VMK8055_AO2_REG         0x03
100#define VMK8055_AI1_REG         0x02
101#define VMK8055_AI2_REG         0x03
102#define VMK8055_CNT1_REG        0x04
103#define VMK8055_CNT2_REG        0x06
104
105#define VMK8061_CH_REG          0x01
106#define VMK8061_DI_REG          0x01
107#define VMK8061_DO_REG          0x01
108#define VMK8061_PWM_REG1        0x01
109#define VMK8061_PWM_REG2        0x02
110#define VMK8061_CNT_REG         0x02
111#define VMK8061_AO_REG          0x02
112#define VMK8061_AI_REG1         0x02
113#define VMK8061_AI_REG2         0x03
114
115#define VMK8055_CMD_RST         0x00
116#define VMK8055_CMD_DEB1_TIME   0x01
117#define VMK8055_CMD_DEB2_TIME   0x02
118#define VMK8055_CMD_RST_CNT1    0x03
119#define VMK8055_CMD_RST_CNT2    0x04
120#define VMK8055_CMD_WRT_AD      0x05
121
122#define VMK8061_CMD_RD_AI       0x00
123#define VMK8061_CMR_RD_ALL_AI   0x01    /* !non-active! */
124#define VMK8061_CMD_SET_AO      0x02
125#define VMK8061_CMD_SET_ALL_AO  0x03    /* !non-active! */
126#define VMK8061_CMD_OUT_PWM     0x04
127#define VMK8061_CMD_RD_DI       0x05
128#define VMK8061_CMD_DO          0x06    /* !non-active! */
129#define VMK8061_CMD_CLR_DO      0x07
130#define VMK8061_CMD_SET_DO      0x08
131#define VMK8061_CMD_RD_CNT      0x09    /* TODO: completely pointless? */
132#define VMK8061_CMD_RST_CNT     0x0a    /* TODO: completely pointless? */
133#define VMK8061_CMD_RD_VERSION  0x0b    /* internal usage */
134#define VMK8061_CMD_RD_JMP_STAT 0x0c    /* TODO: not implemented yet */
135#define VMK8061_CMD_RD_PWR_STAT 0x0d    /* internal usage */
136#define VMK8061_CMD_RD_DO       0x0e
137#define VMK8061_CMD_RD_AO       0x0f
138#define VMK8061_CMD_RD_PWM      0x10
139
140#define VMK80XX_MAX_BOARDS      COMEDI_NUM_BOARD_MINORS
141
142#define TRANS_OUT_BUSY          1
143#define TRANS_IN_BUSY           2
144#define TRANS_IN_RUNNING        3
145
146#define IC3_VERSION             (1 << 0)
147#define IC6_VERSION             (1 << 1)
148
149#define URB_RCV_FLAG            (1 << 0)
150#define URB_SND_FLAG            (1 << 1)
151
152#define CONFIG_VMK80XX_DEBUG
153#undef CONFIG_VMK80XX_DEBUG
154
155#ifdef CONFIG_VMK80XX_DEBUG
156 static int dbgvm = 1;
157#else
158 static int dbgvm;
159#endif
160
161#ifdef CONFIG_COMEDI_DEBUG
162 static int dbgcm = 1;
163#else
164 static int dbgcm;
165#endif
166
167#define dbgvm(fmt, arg...)                     \
168do {                                           \
169	if (dbgvm)                             \
170		printk(KERN_DEBUG fmt, ##arg); \
171} while (0)
172
173#define dbgcm(fmt, arg...)                     \
174do {                                           \
175	if (dbgcm)                             \
176		printk(KERN_DEBUG fmt, ##arg); \
177} while (0)
178
179enum vmk80xx_model {
180	VMK8055_MODEL,
181	VMK8061_MODEL
182};
183
184struct firmware_version {
185	unsigned char ic3_vers[32]; /* USB-Controller */
186	unsigned char ic6_vers[32]; /* CPU */
187};
188
189static const struct comedi_lrange vmk8055_range = {
190	1, { UNI_RANGE(5) }
191};
192
193static const struct comedi_lrange vmk8061_range = {
194	2, { UNI_RANGE(5), UNI_RANGE(10) }
195};
196
197struct vmk80xx_board {
198	const char *name;
199	enum vmk80xx_model model;
200	const struct comedi_lrange *range;
201	__u8   ai_chans;
202	__le16 ai_bits;
203	__u8   ao_chans;
204	__le16 ao_bits;
205	__u8   di_chans;
206	__le16 di_bits;
207	__u8   do_chans;
208	__le16 do_bits;
209	__u8   cnt_chans;
210	__le16 cnt_bits;
211	__u8   pwm_chans;
212	__le16 pwm_bits;
213};
214
215enum {
216	VMK80XX_SUBD_AI,
217	VMK80XX_SUBD_AO,
218	VMK80XX_SUBD_DI,
219	VMK80XX_SUBD_DO,
220	VMK80XX_SUBD_CNT,
221	VMK80XX_SUBD_PWM,
222};
223
224struct vmk80xx_usb {
225	struct usb_device *udev;
226	struct usb_interface *intf;
227	struct usb_endpoint_descriptor *ep_rx;
228	struct usb_endpoint_descriptor *ep_tx;
229	struct usb_anchor rx_anchor;
230	struct usb_anchor tx_anchor;
231	struct vmk80xx_board board;
232	struct firmware_version fw;
233	struct semaphore limit_sem;
234	wait_queue_head_t read_wait;
235	wait_queue_head_t write_wait;
236	unsigned char *usb_rx_buf;
237	unsigned char *usb_tx_buf;
238	unsigned long flags;
239	int probed;
240	int attached;
241	int count;
242};
243
244static struct vmk80xx_usb vmb[VMK80XX_MAX_BOARDS];
245
246static DEFINE_MUTEX(glb_mutex);
247
248static void vmk80xx_tx_callback(struct urb *urb)
249{
250	struct vmk80xx_usb *dev = urb->context;
251	int stat = urb->status;
252
253	dbgvm("vmk80xx: %s\n", __func__);
254
255	if (stat && !(stat == -ENOENT
256		 ||   stat == -ECONNRESET
257		 ||   stat == -ESHUTDOWN))
258		dbgcm("comedi#: vmk80xx: %s - nonzero urb status (%d)\n",
259		      __func__, stat);
260
261	if (!test_bit(TRANS_OUT_BUSY, &dev->flags))
262		return;
263
264	clear_bit(TRANS_OUT_BUSY, &dev->flags);
265
266	wake_up_interruptible(&dev->write_wait);
267}
268
269static void vmk80xx_rx_callback(struct urb *urb)
270{
271	struct vmk80xx_usb *dev = urb->context;
272	int stat = urb->status;
273
274	dbgvm("vmk80xx: %s\n", __func__);
275
276	switch (stat) {
277	case 0:
278		break;
279	case -ENOENT:
280	case -ECONNRESET:
281	case -ESHUTDOWN:
282		break;
283	default:
284		dbgcm("comedi#: vmk80xx: %s - nonzero urb status (%d)\n",
285		      __func__, stat);
286		goto resubmit;
287	}
288
289	goto exit;
290resubmit:
291	if (test_bit(TRANS_IN_RUNNING, &dev->flags) && dev->intf) {
292		usb_anchor_urb(urb, &dev->rx_anchor);
293
294		if (!usb_submit_urb(urb, GFP_KERNEL))
295			goto exit;
296
297		err("comedi#: vmk80xx: %s - submit urb failed\n", __func__);
298
299		usb_unanchor_urb(urb);
300	}
301exit:
302	clear_bit(TRANS_IN_BUSY, &dev->flags);
303
304	wake_up_interruptible(&dev->read_wait);
305}
306
307static int vmk80xx_check_data_link(struct vmk80xx_usb *dev)
308{
309	unsigned int tx_pipe, rx_pipe;
310	unsigned char tx[1], rx[2];
311
312	dbgvm("vmk80xx: %s\n", __func__);
313
314	tx_pipe = usb_sndbulkpipe(dev->udev, 0x01);
315	rx_pipe = usb_rcvbulkpipe(dev->udev, 0x81);
316
317	tx[0] = VMK8061_CMD_RD_PWR_STAT;
318
319	/* Check that IC6 (PIC16F871) is powered and
320	 * running and the data link between IC3 and
321	 * IC6 is working properly */
322	usb_bulk_msg(dev->udev, tx_pipe, tx, 1, NULL,
323		     dev->ep_tx->bInterval);
324	usb_bulk_msg(dev->udev, rx_pipe, rx, 2, NULL,
325		     HZ * 10);
326
327	return (int)rx[1];
328}
329
330static void vmk80xx_read_eeprom(struct vmk80xx_usb *dev, int flag)
331{
332	unsigned int tx_pipe, rx_pipe;
333	unsigned char tx[1], rx[64];
334	int cnt;
335
336	dbgvm("vmk80xx: %s\n", __func__);
337
338	tx_pipe = usb_sndbulkpipe(dev->udev, 0x01);
339	rx_pipe = usb_rcvbulkpipe(dev->udev, 0x81);
340
341	tx[0] = VMK8061_CMD_RD_VERSION;
342
343	/* Read the firmware version info of IC3 and
344	 * IC6 from the internal EEPROM of the IC */
345	usb_bulk_msg(dev->udev, tx_pipe, tx,  1, NULL,
346		     dev->ep_tx->bInterval);
347	usb_bulk_msg(dev->udev, rx_pipe, rx, 64, &cnt,
348		     HZ * 10);
349
350	rx[cnt] = '\0';
351
352	if (flag & IC3_VERSION)
353		strncpy(dev->fw.ic3_vers, rx +  1, 24);
354	else /* IC6_VERSION */
355		strncpy(dev->fw.ic6_vers, rx + 25, 24);
356}
357
358static int vmk80xx_reset_device(struct vmk80xx_usb *dev)
359{
360	struct urb *urb;
361	unsigned int tx_pipe;
362	int ival;
363	size_t size;
364
365	dbgvm("vmk80xx: %s\n", __func__);
366
367	urb = usb_alloc_urb(0, GFP_KERNEL);
368	if (!urb)
369		return -ENOMEM;
370
371	tx_pipe = usb_sndintpipe(dev->udev, 0x01);
372
373	ival = dev->ep_tx->bInterval;
374	size = le16_to_cpu(dev->ep_tx->wMaxPacketSize);
375
376	dev->usb_tx_buf[0] = VMK8055_CMD_RST;
377	dev->usb_tx_buf[1] = 0x00;
378	dev->usb_tx_buf[2] = 0x00;
379	dev->usb_tx_buf[3] = 0x00;
380	dev->usb_tx_buf[4] = 0x00;
381	dev->usb_tx_buf[5] = 0x00;
382	dev->usb_tx_buf[6] = 0x00;
383	dev->usb_tx_buf[7] = 0x00;
384
385	usb_fill_int_urb(urb, dev->udev, tx_pipe, dev->usb_tx_buf,
386			 size, vmk80xx_tx_callback, dev, ival);
387
388	usb_anchor_urb(urb, &dev->tx_anchor);
389
390	return usb_submit_urb(urb, GFP_KERNEL);
391}
392
393static void vmk80xx_build_int_urb(struct urb *urb, int flag)
394{
395	struct vmk80xx_usb *dev = urb->context;
396	__u8 rx_addr, tx_addr;
397	unsigned int pipe;
398	unsigned char *buf;
399	size_t size;
400	void (*callback)(struct urb *);
401	int ival;
402
403	dbgvm("vmk80xx: %s\n", __func__);
404
405	if (flag & URB_RCV_FLAG) {
406		rx_addr = dev->ep_rx->bEndpointAddress;
407		pipe = usb_rcvintpipe(dev->udev, rx_addr);
408		buf = dev->usb_rx_buf;
409		size = le16_to_cpu(dev->ep_rx->wMaxPacketSize);
410		callback = vmk80xx_rx_callback;
411		ival = dev->ep_rx->bInterval;
412	} else { /* URB_SND_FLAG */
413		tx_addr = dev->ep_tx->bEndpointAddress;
414		pipe = usb_sndintpipe(dev->udev, tx_addr);
415		buf = dev->usb_tx_buf;
416		size = le16_to_cpu(dev->ep_tx->wMaxPacketSize);
417		callback = vmk80xx_tx_callback;
418		ival = dev->ep_tx->bInterval;
419	}
420
421	usb_fill_int_urb(urb, dev->udev, pipe, buf,
422			 size, callback, dev, ival);
423}
424
425static void vmk80xx_do_bulk_msg(struct vmk80xx_usb *dev)
426{
427	__u8 tx_addr, rx_addr;
428	unsigned int tx_pipe, rx_pipe;
429	size_t size;
430
431	dbgvm("vmk80xx: %s\n", __func__);
432
433	set_bit(TRANS_IN_BUSY, &dev->flags);
434	set_bit(TRANS_OUT_BUSY, &dev->flags);
435
436	tx_addr = dev->ep_tx->bEndpointAddress;
437	rx_addr = dev->ep_rx->bEndpointAddress;
438	tx_pipe = usb_sndbulkpipe(dev->udev, tx_addr);
439	rx_pipe = usb_rcvbulkpipe(dev->udev, rx_addr);
440
441	/* The max packet size attributes of the K8061
442	 * input/output endpoints are identical */
443	size = le16_to_cpu(dev->ep_tx->wMaxPacketSize);
444
445	usb_bulk_msg(dev->udev, tx_pipe, dev->usb_tx_buf,
446		     size, NULL, dev->ep_tx->bInterval);
447	usb_bulk_msg(dev->udev, rx_pipe, dev->usb_rx_buf,
448		     size, NULL, HZ * 10);
449
450	clear_bit(TRANS_OUT_BUSY, &dev->flags);
451	clear_bit(TRANS_IN_BUSY, &dev->flags);
452}
453
454static int vmk80xx_read_packet(struct vmk80xx_usb *dev)
455{
456	struct urb *urb;
457	int retval;
458
459	dbgvm("vmk80xx: %s\n", __func__);
460
461	if (!dev->intf)
462		return -ENODEV;
463
464	/* Only useful for interrupt transfers */
465	if (test_bit(TRANS_IN_BUSY, &dev->flags))
466		if (wait_event_interruptible(dev->read_wait,
467			!test_bit(TRANS_IN_BUSY, &dev->flags)))
468			return -ERESTART;
469
470	if (dev->board.model == VMK8061_MODEL) {
471		vmk80xx_do_bulk_msg(dev);
472
473		return 0;
474	}
475
476	urb = usb_alloc_urb(0, GFP_KERNEL);
477	if (!urb)
478		return -ENOMEM;
479
480	urb->context = dev;
481	vmk80xx_build_int_urb(urb, URB_RCV_FLAG);
482
483	set_bit(TRANS_IN_RUNNING, &dev->flags);
484	set_bit(TRANS_IN_BUSY, &dev->flags);
485
486	usb_anchor_urb(urb, &dev->rx_anchor);
487
488	retval = usb_submit_urb(urb, GFP_KERNEL);
489	if (!retval)
490		goto exit;
491
492	clear_bit(TRANS_IN_RUNNING, &dev->flags);
493	usb_unanchor_urb(urb);
494
495exit:
496	usb_free_urb(urb);
497
498	return retval;
499}
500
501static int vmk80xx_write_packet(struct vmk80xx_usb *dev, int cmd)
502{
503	struct urb *urb;
504	int retval;
505
506	dbgvm("vmk80xx: %s\n", __func__);
507
508	if (!dev->intf)
509		return -ENODEV;
510
511	if (test_bit(TRANS_OUT_BUSY, &dev->flags))
512		if (wait_event_interruptible(dev->write_wait,
513			!test_bit(TRANS_OUT_BUSY, &dev->flags)))
514			return -ERESTART;
515
516	if (dev->board.model == VMK8061_MODEL) {
517		dev->usb_tx_buf[0] = cmd;
518		vmk80xx_do_bulk_msg(dev);
519
520		return 0;
521	}
522
523	urb = usb_alloc_urb(0, GFP_KERNEL);
524	if (!urb)
525		return -ENOMEM;
526
527	urb->context = dev;
528	vmk80xx_build_int_urb(urb, URB_SND_FLAG);
529
530	set_bit(TRANS_OUT_BUSY, &dev->flags);
531
532	usb_anchor_urb(urb, &dev->tx_anchor);
533
534	dev->usb_tx_buf[0] = cmd;
535
536	retval = usb_submit_urb(urb, GFP_KERNEL);
537	if (!retval)
538		goto exit;
539
540	clear_bit(TRANS_OUT_BUSY, &dev->flags);
541	usb_unanchor_urb(urb);
542
543exit:
544	usb_free_urb(urb);
545
546	return retval;
547}
548
549#define DIR_IN  1
550#define DIR_OUT 2
551
552#define rudimentary_check(dir)                             \
553do {                                                       \
554	if (!dev)                                          \
555		return -EFAULT;                            \
556	if (!dev->probed)                                  \
557		return -ENODEV;                            \
558	if (!dev->attached)                                \
559		return -ENODEV;                            \
560	if ((dir) & DIR_IN) {                              \
561		if (test_bit(TRANS_IN_BUSY, &dev->flags))  \
562			return -EBUSY;                     \
563	} else {  /* DIR_OUT */                            \
564		if (test_bit(TRANS_OUT_BUSY, &dev->flags)) \
565			return -EBUSY;                     \
566	}                                                  \
567} while (0)
568
569static int vmk80xx_ai_rinsn(struct comedi_device *cdev,
570			    struct comedi_subdevice *s,
571			    struct comedi_insn *insn, unsigned int *data)
572{
573	struct vmk80xx_usb *dev = cdev->private;
574	int chan, reg[2];
575	int n;
576
577	dbgvm("vmk80xx: %s\n", __func__);
578
579	rudimentary_check(DIR_IN);
580
581	down(&dev->limit_sem);
582	chan = CR_CHAN(insn->chanspec);
583
584	switch (dev->board.model) {
585	case VMK8055_MODEL:
586		if (!chan)
587			reg[0] = VMK8055_AI1_REG;
588		else
589			reg[0] = VMK8055_AI2_REG;
590		break;
591	case VMK8061_MODEL:
592		reg[0] = VMK8061_AI_REG1;
593		reg[1] = VMK8061_AI_REG2;
594		dev->usb_tx_buf[0] = VMK8061_CMD_RD_AI;
595		dev->usb_tx_buf[VMK8061_CH_REG] = chan;
596		break;
597	}
598
599	for (n = 0; n < insn->n; n++) {
600		if (vmk80xx_read_packet(dev))
601			break;
602
603		if (dev->board.model == VMK8055_MODEL) {
604			data[n] = dev->usb_rx_buf[reg[0]];
605			continue;
606		}
607
608		/* VMK8061_MODEL */
609		data[n] = dev->usb_rx_buf[reg[0]] + 256 *
610			  dev->usb_rx_buf[reg[1]];
611	}
612
613	up(&dev->limit_sem);
614
615	return n;
616}
617
618static int vmk80xx_ao_winsn(struct comedi_device *cdev,
619			    struct comedi_subdevice *s,
620			    struct comedi_insn *insn, unsigned int *data)
621{
622	struct vmk80xx_usb *dev = cdev->private;
623	int chan, cmd, reg;
624	int n;
625
626	dbgvm("vmk80xx: %s\n", __func__);
627
628	rudimentary_check(DIR_OUT);
629
630	down(&dev->limit_sem);
631	chan = CR_CHAN(insn->chanspec);
632
633	switch (dev->board.model) {
634	case VMK8055_MODEL:
635		cmd = VMK8055_CMD_WRT_AD;
636		if (!chan)
637			reg = VMK8055_AO1_REG;
638		else
639			reg = VMK8055_AO2_REG;
640		break;
641	default: /* NOTE: avoid compiler warnings */
642		cmd = VMK8061_CMD_SET_AO;
643		reg = VMK8061_AO_REG;
644		dev->usb_tx_buf[VMK8061_CH_REG] = chan;
645		break;
646	}
647
648	for (n = 0; n < insn->n; n++) {
649		dev->usb_tx_buf[reg] = data[n];
650
651		if (vmk80xx_write_packet(dev, cmd))
652			break;
653	}
654
655	up(&dev->limit_sem);
656
657	return n;
658}
659
660static int vmk80xx_ao_rinsn(struct comedi_device *cdev,
661			    struct comedi_subdevice *s,
662			    struct comedi_insn *insn, unsigned int *data)
663{
664	struct vmk80xx_usb *dev = cdev->private;
665	int chan, reg;
666	int n;
667
668	dbgvm("vmk80xx: %s\n", __func__);
669
670	rudimentary_check(DIR_IN);
671
672	down(&dev->limit_sem);
673	chan = CR_CHAN(insn->chanspec);
674
675	reg = VMK8061_AO_REG - 1;
676
677	dev->usb_tx_buf[0] = VMK8061_CMD_RD_AO;
678
679	for (n = 0; n < insn->n; n++) {
680		if (vmk80xx_read_packet(dev))
681			break;
682
683		data[n] = dev->usb_rx_buf[reg+chan];
684	}
685
686	up(&dev->limit_sem);
687
688	return n;
689}
690
691static int vmk80xx_di_rinsn(struct comedi_device *cdev,
692			    struct comedi_subdevice *s,
693			    struct comedi_insn *insn, unsigned int *data)
694{
695	struct vmk80xx_usb *dev = cdev->private;
696	int chan;
697	unsigned char *rx_buf;
698	int reg, inp;
699	int n;
700
701	dbgvm("vmk80xx: %s\n", __func__);
702
703	rudimentary_check(DIR_IN);
704
705	down(&dev->limit_sem);
706	chan = CR_CHAN(insn->chanspec);
707
708	rx_buf = dev->usb_rx_buf;
709
710	if (dev->board.model == VMK8061_MODEL) {
711		reg = VMK8061_DI_REG;
712		dev->usb_tx_buf[0] = VMK8061_CMD_RD_DI;
713	} else
714		reg = VMK8055_DI_REG;
715
716	for (n = 0; n < insn->n; n++) {
717		if (vmk80xx_read_packet(dev))
718			break;
719
720		if (dev->board.model == VMK8055_MODEL)
721			inp = (((rx_buf[reg] >> 4) & 0x03) |
722			       ((rx_buf[reg] << 2) & 0x04) |
723			       ((rx_buf[reg] >> 3) & 0x18));
724		else
725			inp = rx_buf[reg];
726
727		data[n] = ((inp & (1 << chan)) > 0);
728	}
729
730	up(&dev->limit_sem);
731
732	return n;
733}
734
735static int vmk80xx_do_winsn(struct comedi_device *cdev,
736			    struct comedi_subdevice *s,
737			    struct comedi_insn *insn, unsigned int *data)
738{
739
740	struct vmk80xx_usb *dev = cdev->private;
741	int chan;
742	unsigned char *tx_buf;
743	int reg, cmd;
744	int n;
745
746	dbgvm("vmk80xx: %s\n", __func__);
747
748	rudimentary_check(DIR_OUT);
749
750	down(&dev->limit_sem);
751	chan = CR_CHAN(insn->chanspec);
752
753	tx_buf = dev->usb_tx_buf;
754
755	for (n = 0; n < insn->n; n++) {
756		if (dev->board.model == VMK8055_MODEL) {
757			reg = VMK8055_DO_REG;
758			cmd = VMK8055_CMD_WRT_AD;
759			if (data[n] == 1)
760				tx_buf[reg] |= (1 << chan);
761			else
762				tx_buf[reg] ^= (1 << chan);
763
764			goto write_packet;
765		}
766
767		/* VMK8061_MODEL */
768		reg = VMK8061_DO_REG;
769		if (data[n] == 1) {
770			cmd = VMK8061_CMD_SET_DO;
771			tx_buf[reg] = 1 << chan;
772		} else {
773			cmd = VMK8061_CMD_CLR_DO;
774			tx_buf[reg] = 0xff - (1 << chan);
775		}
776
777write_packet:
778		if (vmk80xx_write_packet(dev, cmd))
779			break;
780	}
781
782	up(&dev->limit_sem);
783
784	return n;
785}
786
787static int vmk80xx_do_rinsn(struct comedi_device *cdev,
788			    struct comedi_subdevice *s,
789			    struct comedi_insn *insn, unsigned int *data)
790{
791	struct vmk80xx_usb *dev = cdev->private;
792	int chan, reg, mask;
793	int n;
794
795	dbgvm("vmk80xx: %s\n", __func__);
796
797	rudimentary_check(DIR_IN);
798
799	down(&dev->limit_sem);
800	chan = CR_CHAN(insn->chanspec);
801
802	reg = VMK8061_DO_REG;
803	mask = 1 << chan;
804
805	dev->usb_tx_buf[0] = VMK8061_CMD_RD_DO;
806
807	for (n = 0; n < insn->n; n++) {
808		if (vmk80xx_read_packet(dev))
809			break;
810
811		data[n] = (dev->usb_rx_buf[reg] & mask) >> chan;
812	}
813
814	up(&dev->limit_sem);
815
816	return n;
817}
818
819static int vmk80xx_cnt_rinsn(struct comedi_device *cdev,
820			     struct comedi_subdevice *s,
821			     struct comedi_insn *insn, unsigned int *data)
822{
823	struct vmk80xx_usb *dev = cdev->private;
824	int chan, reg[2];
825	int n;
826
827	dbgvm("vmk80xx: %s\n", __func__);
828
829	rudimentary_check(DIR_IN);
830
831	down(&dev->limit_sem);
832	chan = CR_CHAN(insn->chanspec);
833
834	switch (dev->board.model) {
835	case VMK8055_MODEL:
836		if (!chan)
837			reg[0] = VMK8055_CNT1_REG;
838		else
839			reg[0] = VMK8055_CNT2_REG;
840		break;
841	case VMK8061_MODEL:
842		reg[0] = VMK8061_CNT_REG;
843		reg[1] = VMK8061_CNT_REG;
844		dev->usb_tx_buf[0] = VMK8061_CMD_RD_CNT;
845		break;
846	}
847
848	for (n = 0; n < insn->n; n++) {
849		if (vmk80xx_read_packet(dev))
850			break;
851
852		if (dev->board.model == VMK8055_MODEL) {
853			data[n] = dev->usb_rx_buf[reg[0]];
854			continue;
855		}
856
857		/* VMK8061_MODEL */
858		data[n] = dev->usb_rx_buf[reg[0]*(chan+1)+1]
859		  + 256 * dev->usb_rx_buf[reg[1]*2+2];
860	}
861
862	up(&dev->limit_sem);
863
864	return n;
865}
866
867static int vmk80xx_cnt_cinsn(struct comedi_device *cdev,
868			     struct comedi_subdevice *s,
869			     struct comedi_insn *insn, unsigned int *data)
870{
871	struct vmk80xx_usb *dev = cdev->private;
872	unsigned int insn_cmd;
873	int chan, cmd, reg;
874	int n;
875
876	dbgvm("vmk80xx: %s\n", __func__);
877
878	rudimentary_check(DIR_OUT);
879
880	down(&dev->limit_sem);
881
882	insn_cmd = data[0];
883	if (insn_cmd != INSN_CONFIG_RESET && insn_cmd != GPCT_RESET)
884		return -EINVAL;
885
886	chan = CR_CHAN(insn->chanspec);
887
888	if (dev->board.model == VMK8055_MODEL) {
889		if (!chan) {
890			cmd = VMK8055_CMD_RST_CNT1;
891			reg = VMK8055_CNT1_REG;
892		} else {
893			cmd = VMK8055_CMD_RST_CNT2;
894			reg = VMK8055_CNT2_REG;
895		}
896
897		dev->usb_tx_buf[reg] = 0x00;
898	} else
899		cmd = VMK8061_CMD_RST_CNT;
900
901	for (n = 0; n < insn->n; n++)
902		if (vmk80xx_write_packet(dev, cmd))
903			break;
904
905	up(&dev->limit_sem);
906
907	return n;
908}
909
910static int vmk80xx_cnt_winsn(struct comedi_device *cdev,
911			     struct comedi_subdevice *s,
912			     struct comedi_insn *insn, unsigned int *data)
913{
914	struct vmk80xx_usb *dev = cdev->private;
915	unsigned long debtime, val;
916	int chan, cmd;
917	int n;
918
919	dbgvm("vmk80xx: %s\n", __func__);
920
921	rudimentary_check(DIR_OUT);
922
923	down(&dev->limit_sem);
924	chan = CR_CHAN(insn->chanspec);
925
926	if (!chan)
927		cmd = VMK8055_CMD_DEB1_TIME;
928	else
929		cmd = VMK8055_CMD_DEB2_TIME;
930
931	for (n = 0; n < insn->n; n++) {
932		debtime = data[n];
933		if (debtime == 0)
934			debtime = 1;
935
936		/* TODO: Prevent overflows */
937		if (debtime > 7450)
938			debtime = 7450;
939
940		val = int_sqrt(debtime * 1000 / 115);
941		if (((val + 1) * val) < debtime * 1000 / 115)
942			val += 1;
943
944		dev->usb_tx_buf[6+chan] = val;
945
946		if (vmk80xx_write_packet(dev, cmd))
947			break;
948	}
949
950	up(&dev->limit_sem);
951
952	return n;
953}
954
955static int vmk80xx_pwm_rinsn(struct comedi_device *cdev,
956			     struct comedi_subdevice *s,
957			     struct comedi_insn *insn, unsigned int *data)
958{
959	struct vmk80xx_usb *dev = cdev->private;
960	int reg[2];
961	int n;
962
963	dbgvm("vmk80xx: %s\n", __func__);
964
965	rudimentary_check(DIR_IN);
966
967	down(&dev->limit_sem);
968
969	reg[0] = VMK8061_PWM_REG1;
970	reg[1] = VMK8061_PWM_REG2;
971
972	dev->usb_tx_buf[0] = VMK8061_CMD_RD_PWM;
973
974	for (n = 0; n < insn->n; n++) {
975		if (vmk80xx_read_packet(dev))
976			break;
977
978		data[n] = dev->usb_rx_buf[reg[0]] + 4 *
979			  dev->usb_rx_buf[reg[1]];
980	}
981
982	up(&dev->limit_sem);
983
984	return n;
985}
986
987static int vmk80xx_pwm_winsn(struct comedi_device *cdev,
988			     struct comedi_subdevice *s,
989			     struct comedi_insn *insn, unsigned int *data)
990{
991	struct vmk80xx_usb *dev = cdev->private;
992	unsigned char *tx_buf;
993	int reg[2], cmd;
994	int n;
995
996	dbgvm("vmk80xx: %s\n", __func__);
997
998	rudimentary_check(DIR_OUT);
999
1000	down(&dev->limit_sem);
1001
1002	tx_buf = dev->usb_tx_buf;
1003
1004	reg[0] = VMK8061_PWM_REG1;
1005	reg[1] = VMK8061_PWM_REG2;
1006
1007	cmd = VMK8061_CMD_OUT_PWM;
1008
1009	/*
1010	 * The followin piece of code was translated from the inline
1011	 * assembler code in the DLL source code.
1012	 *
1013	 * asm
1014	 *   mov eax, k  ; k is the value (data[n])
1015	 *   and al, 03h ; al are the lower 8 bits of eax
1016	 *   mov lo, al  ; lo is the low part (tx_buf[reg[0]])
1017	 *   mov eax, k
1018	 *   shr eax, 2  ; right shift eax register by 2
1019	 *   mov hi, al  ; hi is the high part (tx_buf[reg[1]])
1020	 * end;
1021	 */
1022	for (n = 0; n < insn->n; n++) {
1023		tx_buf[reg[0]] = (unsigned char)(data[n] & 0x03);
1024		tx_buf[reg[1]] = (unsigned char)(data[n] >> 2) & 0xff;
1025
1026		if (vmk80xx_write_packet(dev, cmd))
1027			break;
1028	}
1029
1030	up(&dev->limit_sem);
1031
1032	return n;
1033}
1034
1035static int
1036vmk80xx_attach(struct comedi_device *cdev, struct comedi_devconfig *it)
1037{
1038	int i;
1039	struct vmk80xx_usb *dev;
1040	int n_subd;
1041	struct comedi_subdevice *s;
1042	int minor;
1043
1044	dbgvm("vmk80xx: %s\n", __func__);
1045
1046	mutex_lock(&glb_mutex);
1047
1048	for (i = 0; i < VMK80XX_MAX_BOARDS; i++)
1049		if (vmb[i].probed && !vmb[i].attached)
1050			break;
1051
1052	if (i == VMK80XX_MAX_BOARDS) {
1053		mutex_unlock(&glb_mutex);
1054		return -ENODEV;
1055	}
1056
1057	dev = &vmb[i];
1058
1059	down(&dev->limit_sem);
1060
1061	cdev->board_name = dev->board.name;
1062	cdev->private = dev;
1063
1064	if (dev->board.model == VMK8055_MODEL)
1065		n_subd = 5;
1066	else
1067		n_subd = 6;
1068
1069	if (alloc_subdevices(cdev, n_subd) < 0) {
1070		up(&dev->limit_sem);
1071		mutex_unlock(&glb_mutex);
1072		return -ENOMEM;
1073	}
1074
1075	/* Analog input subdevice */
1076	s = cdev->subdevices + VMK80XX_SUBD_AI;
1077	s->type = COMEDI_SUBD_AI;
1078	s->subdev_flags = SDF_READABLE | SDF_GROUND;
1079	s->n_chan = dev->board.ai_chans;
1080	s->maxdata = (1 << dev->board.ai_bits) - 1;
1081	s->range_table = dev->board.range;
1082	s->insn_read = vmk80xx_ai_rinsn;
1083
1084	/* Analog output subdevice */
1085	s = cdev->subdevices + VMK80XX_SUBD_AO;
1086	s->type = COMEDI_SUBD_AO;
1087	s->subdev_flags = SDF_WRITEABLE | SDF_GROUND;
1088	s->n_chan = dev->board.ao_chans;
1089	s->maxdata = (1 << dev->board.ao_bits) - 1;
1090	s->range_table = dev->board.range;
1091	s->insn_write = vmk80xx_ao_winsn;
1092
1093	if (dev->board.model == VMK8061_MODEL) {
1094		s->subdev_flags |= SDF_READABLE;
1095		s->insn_read = vmk80xx_ao_rinsn;
1096	}
1097
1098	/* Digital input subdevice */
1099	s = cdev->subdevices + VMK80XX_SUBD_DI;
1100	s->type = COMEDI_SUBD_DI;
1101	s->subdev_flags = SDF_READABLE | SDF_GROUND;
1102	s->n_chan = dev->board.di_chans;
1103	s->maxdata = (1 << dev->board.di_bits) - 1;
1104	s->insn_read = vmk80xx_di_rinsn;
1105
1106	/* Digital output subdevice */
1107	s = cdev->subdevices + VMK80XX_SUBD_DO;
1108	s->type = COMEDI_SUBD_DO;
1109	s->subdev_flags = SDF_WRITEABLE | SDF_GROUND;
1110	s->n_chan = dev->board.do_chans;
1111	s->maxdata = (1 << dev->board.do_bits) - 1;
1112	s->insn_write = vmk80xx_do_winsn;
1113
1114	if (dev->board.model == VMK8061_MODEL) {
1115		s->subdev_flags |= SDF_READABLE;
1116		s->insn_read = vmk80xx_do_rinsn;
1117	}
1118
1119	/* Counter subdevice */
1120	s = cdev->subdevices + VMK80XX_SUBD_CNT;
1121	s->type = COMEDI_SUBD_COUNTER;
1122	s->subdev_flags = SDF_READABLE;
1123	s->n_chan = dev->board.cnt_chans;
1124	s->insn_read = vmk80xx_cnt_rinsn;
1125	s->insn_config = vmk80xx_cnt_cinsn;
1126
1127	if (dev->board.model == VMK8055_MODEL) {
1128		s->subdev_flags |= SDF_WRITEABLE;
1129		s->maxdata = (1 << dev->board.cnt_bits) - 1;
1130		s->insn_write = vmk80xx_cnt_winsn;
1131	}
1132
1133	/* PWM subdevice */
1134	if (dev->board.model == VMK8061_MODEL) {
1135		s = cdev->subdevices + VMK80XX_SUBD_PWM;
1136		s->type = COMEDI_SUBD_PWM;
1137		s->subdev_flags = SDF_READABLE | SDF_WRITEABLE;
1138		s->n_chan = dev->board.pwm_chans;
1139		s->maxdata = (1 << dev->board.pwm_bits) - 1;
1140		s->insn_read = vmk80xx_pwm_rinsn;
1141		s->insn_write = vmk80xx_pwm_winsn;
1142	}
1143
1144	dev->attached = 1;
1145
1146	minor = cdev->minor;
1147
1148	printk(KERN_INFO
1149	       "comedi%d: vmk80xx: board #%d [%s] attached to comedi\n",
1150	       minor, dev->count, dev->board.name);
1151
1152	up(&dev->limit_sem);
1153	mutex_unlock(&glb_mutex);
1154
1155	return 0;
1156}
1157
1158static int vmk80xx_detach(struct comedi_device *cdev)
1159{
1160	struct vmk80xx_usb *dev;
1161	int minor;
1162
1163	dbgvm("vmk80xx: %s\n", __func__);
1164
1165	if (!cdev)
1166		return -EFAULT;
1167
1168	dev = cdev->private;
1169	if (!dev)
1170		return -EFAULT;
1171
1172	down(&dev->limit_sem);
1173
1174	cdev->private = NULL;
1175	dev->attached = 0;
1176
1177	minor = cdev->minor;
1178
1179	printk(KERN_INFO
1180	       "comedi%d: vmk80xx: board #%d [%s] detached from comedi\n",
1181	       minor, dev->count, dev->board.name);
1182
1183	up(&dev->limit_sem);
1184
1185	return 0;
1186}
1187
1188static int
1189vmk80xx_probe(struct usb_interface *intf, const struct usb_device_id *id)
1190{
1191	int i;
1192	struct vmk80xx_usb *dev;
1193	struct usb_host_interface *iface_desc;
1194	struct usb_endpoint_descriptor *ep_desc;
1195	size_t size;
1196
1197	dbgvm("vmk80xx: %s\n", __func__);
1198
1199	mutex_lock(&glb_mutex);
1200
1201	for (i = 0; i < VMK80XX_MAX_BOARDS; i++)
1202		if (!vmb[i].probed)
1203			break;
1204
1205	if (i == VMK80XX_MAX_BOARDS) {
1206		mutex_unlock(&glb_mutex);
1207		return -EMFILE;
1208	}
1209
1210	dev = &vmb[i];
1211
1212	memset(dev, 0x00, sizeof(struct vmk80xx_usb));
1213	dev->count = i;
1214
1215	iface_desc = intf->cur_altsetting;
1216	if (iface_desc->desc.bNumEndpoints != 2)
1217		goto error;
1218
1219	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
1220		ep_desc = &iface_desc->endpoint[i].desc;
1221
1222		if (usb_endpoint_is_int_in(ep_desc)) {
1223			dev->ep_rx = ep_desc;
1224			continue;
1225		}
1226
1227		if (usb_endpoint_is_int_out(ep_desc)) {
1228			dev->ep_tx = ep_desc;
1229			continue;
1230		}
1231
1232		if (usb_endpoint_is_bulk_in(ep_desc)) {
1233			dev->ep_rx = ep_desc;
1234			continue;
1235		}
1236
1237		if (usb_endpoint_is_bulk_out(ep_desc)) {
1238			dev->ep_tx = ep_desc;
1239			continue;
1240		}
1241	}
1242
1243	if (!dev->ep_rx || !dev->ep_tx)
1244		goto error;
1245
1246	size = le16_to_cpu(dev->ep_rx->wMaxPacketSize);
1247	dev->usb_rx_buf = kmalloc(size, GFP_KERNEL);
1248	if (!dev->usb_rx_buf) {
1249		mutex_unlock(&glb_mutex);
1250		return -ENOMEM;
1251	}
1252
1253	size = le16_to_cpu(dev->ep_tx->wMaxPacketSize);
1254	dev->usb_tx_buf = kmalloc(size, GFP_KERNEL);
1255	if (!dev->usb_tx_buf) {
1256		kfree(dev->usb_rx_buf);
1257		mutex_unlock(&glb_mutex);
1258		return -ENOMEM;
1259	}
1260
1261	dev->udev = interface_to_usbdev(intf);
1262	dev->intf = intf;
1263
1264	sema_init(&dev->limit_sem, 8);
1265	init_waitqueue_head(&dev->read_wait);
1266	init_waitqueue_head(&dev->write_wait);
1267
1268	init_usb_anchor(&dev->rx_anchor);
1269	init_usb_anchor(&dev->tx_anchor);
1270
1271	usb_set_intfdata(intf, dev);
1272
1273	switch (id->driver_info) {
1274	case DEVICE_VMK8055:
1275		dev->board.name = "K8055 (VM110)";
1276		dev->board.model = VMK8055_MODEL;
1277		dev->board.range = &vmk8055_range;
1278		dev->board.ai_chans = 2;
1279		dev->board.ai_bits = 8;
1280		dev->board.ao_chans = 2;
1281		dev->board.ao_bits = 8;
1282		dev->board.di_chans = 5;
1283		dev->board.di_bits = 1;
1284		dev->board.do_chans = 8;
1285		dev->board.do_bits = 1;
1286		dev->board.cnt_chans = 2;
1287		dev->board.cnt_bits = 16;
1288		dev->board.pwm_chans = 0;
1289		dev->board.pwm_bits = 0;
1290		break;
1291	case DEVICE_VMK8061:
1292		dev->board.name = "K8061 (VM140)";
1293		dev->board.model = VMK8061_MODEL;
1294		dev->board.range = &vmk8061_range;
1295		dev->board.ai_chans = 8;
1296		dev->board.ai_bits = 10;
1297		dev->board.ao_chans = 8;
1298		dev->board.ao_bits = 8;
1299		dev->board.di_chans = 8;
1300		dev->board.di_bits = 1;
1301		dev->board.do_chans = 8;
1302		dev->board.do_bits = 1;
1303		dev->board.cnt_chans = 2;
1304		dev->board.cnt_bits = 0;
1305		dev->board.pwm_chans = 1;
1306		dev->board.pwm_bits = 10;
1307		break;
1308	}
1309
1310	if (dev->board.model == VMK8061_MODEL) {
1311		vmk80xx_read_eeprom(dev, IC3_VERSION);
1312		printk(KERN_INFO "comedi#: vmk80xx: %s\n",
1313		       dev->fw.ic3_vers);
1314
1315		if (vmk80xx_check_data_link(dev)) {
1316			vmk80xx_read_eeprom(dev, IC6_VERSION);
1317			printk(KERN_INFO "comedi#: vmk80xx: %s\n",
1318			       dev->fw.ic6_vers);
1319		} else
1320			dbgcm("comedi#: vmk80xx: no conn. to CPU\n");
1321	}
1322
1323	if (dev->board.model == VMK8055_MODEL)
1324		vmk80xx_reset_device(dev);
1325
1326	dev->probed = 1;
1327
1328	printk(KERN_INFO "comedi#: vmk80xx: board #%d [%s] now attached\n",
1329	       dev->count, dev->board.name);
1330
1331	mutex_unlock(&glb_mutex);
1332
1333	return 0;
1334error:
1335	mutex_unlock(&glb_mutex);
1336
1337	return -ENODEV;
1338}
1339
1340static void vmk80xx_disconnect(struct usb_interface *intf)
1341{
1342	struct vmk80xx_usb *dev = usb_get_intfdata(intf);
1343
1344	dbgvm("vmk80xx: %s\n", __func__);
1345
1346	if (!dev)
1347		return;
1348
1349	mutex_lock(&glb_mutex);
1350	down(&dev->limit_sem);
1351
1352	dev->probed = 0;
1353	usb_set_intfdata(dev->intf, NULL);
1354
1355	usb_kill_anchored_urbs(&dev->rx_anchor);
1356	usb_kill_anchored_urbs(&dev->tx_anchor);
1357
1358	kfree(dev->usb_rx_buf);
1359	kfree(dev->usb_tx_buf);
1360
1361	printk(KERN_INFO "comedi#: vmk80xx: board #%d [%s] now detached\n",
1362	       dev->count, dev->board.name);
1363
1364	up(&dev->limit_sem);
1365	mutex_unlock(&glb_mutex);
1366}
1367
1368/* TODO: Add support for suspend, resume, pre_reset,
1369 * post_reset and flush */
1370static struct usb_driver vmk80xx_driver = {
1371	.name       = "vmk80xx",
1372	.probe      = vmk80xx_probe,
1373	.disconnect = vmk80xx_disconnect,
1374	.id_table   = vmk80xx_id_table
1375};
1376
1377static struct comedi_driver driver_vmk80xx = {
1378	.module      = THIS_MODULE,
1379	.driver_name = "vmk80xx",
1380	.attach      = vmk80xx_attach,
1381	.detach      = vmk80xx_detach
1382};
1383
1384static int __init vmk80xx_init(void)
1385{
1386	printk(KERN_INFO "vmk80xx: version 0.8.01 "
1387	       "Manuel Gebele <forensixs@gmx.de>\n");
1388	usb_register(&vmk80xx_driver);
1389	return comedi_driver_register(&driver_vmk80xx);
1390}
1391
1392static void __exit vmk80xx_exit(void)
1393{
1394	comedi_driver_unregister(&driver_vmk80xx);
1395	usb_deregister(&vmk80xx_driver);
1396}
1397
1398module_init(vmk80xx_init);
1399module_exit(vmk80xx_exit);
1400