vmk80xx.c revision 6dd60bc62988fab9977b7fd0cd14eedc6e27eecd
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
156static int dbgvm = 1;
157#else
158static int dbgvm;
159#endif
160
161#ifdef CONFIG_COMEDI_DEBUG
162static int dbgcm = 1;
163#else
164static 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 || stat == -ESHUTDOWN))
257		dbgcm("comedi#: vmk80xx: %s - nonzero urb status (%d)\n",
258		      __func__, stat);
259
260	if (!test_bit(TRANS_OUT_BUSY, &dev->flags))
261		return;
262
263	clear_bit(TRANS_OUT_BUSY, &dev->flags);
264
265	wake_up_interruptible(&dev->write_wait);
266}
267
268static void vmk80xx_rx_callback(struct urb *urb)
269{
270	struct vmk80xx_usb *dev = urb->context;
271	int stat = urb->status;
272
273	dbgvm("vmk80xx: %s\n", __func__);
274
275	switch (stat) {
276	case 0:
277		break;
278	case -ENOENT:
279	case -ECONNRESET:
280	case -ESHUTDOWN:
281		break;
282	default:
283		dbgcm("comedi#: vmk80xx: %s - nonzero urb status (%d)\n",
284		      __func__, stat);
285		goto resubmit;
286	}
287
288	goto exit;
289resubmit:
290	if (test_bit(TRANS_IN_RUNNING, &dev->flags) && dev->intf) {
291		usb_anchor_urb(urb, &dev->rx_anchor);
292
293		if (!usb_submit_urb(urb, GFP_KERNEL))
294			goto exit;
295
296		err("comedi#: vmk80xx: %s - submit urb failed\n", __func__);
297
298		usb_unanchor_urb(urb);
299	}
300exit:
301	clear_bit(TRANS_IN_BUSY, &dev->flags);
302
303	wake_up_interruptible(&dev->read_wait);
304}
305
306static int vmk80xx_check_data_link(struct vmk80xx_usb *dev)
307{
308	unsigned int tx_pipe, rx_pipe;
309	unsigned char tx[1], rx[2];
310
311	dbgvm("vmk80xx: %s\n", __func__);
312
313	tx_pipe = usb_sndbulkpipe(dev->udev, 0x01);
314	rx_pipe = usb_rcvbulkpipe(dev->udev, 0x81);
315
316	tx[0] = VMK8061_CMD_RD_PWR_STAT;
317
318	/* Check that IC6 (PIC16F871) is powered and
319	 * running and the data link between IC3 and
320	 * IC6 is working properly */
321	usb_bulk_msg(dev->udev, tx_pipe, tx, 1, NULL, dev->ep_tx->bInterval);
322	usb_bulk_msg(dev->udev, rx_pipe, rx, 2, NULL, HZ * 10);
323
324	return (int)rx[1];
325}
326
327static void vmk80xx_read_eeprom(struct vmk80xx_usb *dev, int flag)
328{
329	unsigned int tx_pipe, rx_pipe;
330	unsigned char tx[1], rx[64];
331	int cnt;
332
333	dbgvm("vmk80xx: %s\n", __func__);
334
335	tx_pipe = usb_sndbulkpipe(dev->udev, 0x01);
336	rx_pipe = usb_rcvbulkpipe(dev->udev, 0x81);
337
338	tx[0] = VMK8061_CMD_RD_VERSION;
339
340	/* Read the firmware version info of IC3 and
341	 * IC6 from the internal EEPROM of the IC */
342	usb_bulk_msg(dev->udev, tx_pipe, tx, 1, NULL, dev->ep_tx->bInterval);
343	usb_bulk_msg(dev->udev, rx_pipe, rx, 64, &cnt, HZ * 10);
344
345	rx[cnt] = '\0';
346
347	if (flag & IC3_VERSION)
348		strncpy(dev->fw.ic3_vers, rx + 1, 24);
349	else			/* IC6_VERSION */
350		strncpy(dev->fw.ic6_vers, rx + 25, 24);
351}
352
353static int vmk80xx_reset_device(struct vmk80xx_usb *dev)
354{
355	struct urb *urb;
356	unsigned int tx_pipe;
357	int ival;
358	size_t size;
359
360	dbgvm("vmk80xx: %s\n", __func__);
361
362	urb = usb_alloc_urb(0, GFP_KERNEL);
363	if (!urb)
364		return -ENOMEM;
365
366	tx_pipe = usb_sndintpipe(dev->udev, 0x01);
367
368	ival = dev->ep_tx->bInterval;
369	size = le16_to_cpu(dev->ep_tx->wMaxPacketSize);
370
371	dev->usb_tx_buf[0] = VMK8055_CMD_RST;
372	dev->usb_tx_buf[1] = 0x00;
373	dev->usb_tx_buf[2] = 0x00;
374	dev->usb_tx_buf[3] = 0x00;
375	dev->usb_tx_buf[4] = 0x00;
376	dev->usb_tx_buf[5] = 0x00;
377	dev->usb_tx_buf[6] = 0x00;
378	dev->usb_tx_buf[7] = 0x00;
379
380	usb_fill_int_urb(urb, dev->udev, tx_pipe, dev->usb_tx_buf,
381			 size, vmk80xx_tx_callback, dev, ival);
382
383	usb_anchor_urb(urb, &dev->tx_anchor);
384
385	return usb_submit_urb(urb, GFP_KERNEL);
386}
387
388static void vmk80xx_build_int_urb(struct urb *urb, int flag)
389{
390	struct vmk80xx_usb *dev = urb->context;
391	__u8 rx_addr, tx_addr;
392	unsigned int pipe;
393	unsigned char *buf;
394	size_t size;
395	void (*callback) (struct urb *);
396	int ival;
397
398	dbgvm("vmk80xx: %s\n", __func__);
399
400	if (flag & URB_RCV_FLAG) {
401		rx_addr = dev->ep_rx->bEndpointAddress;
402		pipe = usb_rcvintpipe(dev->udev, rx_addr);
403		buf = dev->usb_rx_buf;
404		size = le16_to_cpu(dev->ep_rx->wMaxPacketSize);
405		callback = vmk80xx_rx_callback;
406		ival = dev->ep_rx->bInterval;
407	} else {		/* URB_SND_FLAG */
408		tx_addr = dev->ep_tx->bEndpointAddress;
409		pipe = usb_sndintpipe(dev->udev, tx_addr);
410		buf = dev->usb_tx_buf;
411		size = le16_to_cpu(dev->ep_tx->wMaxPacketSize);
412		callback = vmk80xx_tx_callback;
413		ival = dev->ep_tx->bInterval;
414	}
415
416	usb_fill_int_urb(urb, dev->udev, pipe, buf, size, callback, dev, ival);
417}
418
419static void vmk80xx_do_bulk_msg(struct vmk80xx_usb *dev)
420{
421	__u8 tx_addr, rx_addr;
422	unsigned int tx_pipe, rx_pipe;
423	size_t size;
424
425	dbgvm("vmk80xx: %s\n", __func__);
426
427	set_bit(TRANS_IN_BUSY, &dev->flags);
428	set_bit(TRANS_OUT_BUSY, &dev->flags);
429
430	tx_addr = dev->ep_tx->bEndpointAddress;
431	rx_addr = dev->ep_rx->bEndpointAddress;
432	tx_pipe = usb_sndbulkpipe(dev->udev, tx_addr);
433	rx_pipe = usb_rcvbulkpipe(dev->udev, rx_addr);
434
435	/* The max packet size attributes of the K8061
436	 * input/output endpoints are identical */
437	size = le16_to_cpu(dev->ep_tx->wMaxPacketSize);
438
439	usb_bulk_msg(dev->udev, tx_pipe, dev->usb_tx_buf,
440		     size, NULL, dev->ep_tx->bInterval);
441	usb_bulk_msg(dev->udev, rx_pipe, dev->usb_rx_buf, size, NULL, HZ * 10);
442
443	clear_bit(TRANS_OUT_BUSY, &dev->flags);
444	clear_bit(TRANS_IN_BUSY, &dev->flags);
445}
446
447static int vmk80xx_read_packet(struct vmk80xx_usb *dev)
448{
449	struct urb *urb;
450	int retval;
451
452	dbgvm("vmk80xx: %s\n", __func__);
453
454	if (!dev->intf)
455		return -ENODEV;
456
457	/* Only useful for interrupt transfers */
458	if (test_bit(TRANS_IN_BUSY, &dev->flags))
459		if (wait_event_interruptible(dev->read_wait,
460					     !test_bit(TRANS_IN_BUSY,
461						       &dev->flags)))
462			return -ERESTART;
463
464	if (dev->board.model == VMK8061_MODEL) {
465		vmk80xx_do_bulk_msg(dev);
466
467		return 0;
468	}
469
470	urb = usb_alloc_urb(0, GFP_KERNEL);
471	if (!urb)
472		return -ENOMEM;
473
474	urb->context = dev;
475	vmk80xx_build_int_urb(urb, URB_RCV_FLAG);
476
477	set_bit(TRANS_IN_RUNNING, &dev->flags);
478	set_bit(TRANS_IN_BUSY, &dev->flags);
479
480	usb_anchor_urb(urb, &dev->rx_anchor);
481
482	retval = usb_submit_urb(urb, GFP_KERNEL);
483	if (!retval)
484		goto exit;
485
486	clear_bit(TRANS_IN_RUNNING, &dev->flags);
487	usb_unanchor_urb(urb);
488
489exit:
490	usb_free_urb(urb);
491
492	return retval;
493}
494
495static int vmk80xx_write_packet(struct vmk80xx_usb *dev, int cmd)
496{
497	struct urb *urb;
498	int retval;
499
500	dbgvm("vmk80xx: %s\n", __func__);
501
502	if (!dev->intf)
503		return -ENODEV;
504
505	if (test_bit(TRANS_OUT_BUSY, &dev->flags))
506		if (wait_event_interruptible(dev->write_wait,
507					     !test_bit(TRANS_OUT_BUSY,
508						       &dev->flags)))
509			return -ERESTART;
510
511	if (dev->board.model == VMK8061_MODEL) {
512		dev->usb_tx_buf[0] = cmd;
513		vmk80xx_do_bulk_msg(dev);
514
515		return 0;
516	}
517
518	urb = usb_alloc_urb(0, GFP_KERNEL);
519	if (!urb)
520		return -ENOMEM;
521
522	urb->context = dev;
523	vmk80xx_build_int_urb(urb, URB_SND_FLAG);
524
525	set_bit(TRANS_OUT_BUSY, &dev->flags);
526
527	usb_anchor_urb(urb, &dev->tx_anchor);
528
529	dev->usb_tx_buf[0] = cmd;
530
531	retval = usb_submit_urb(urb, GFP_KERNEL);
532	if (!retval)
533		goto exit;
534
535	clear_bit(TRANS_OUT_BUSY, &dev->flags);
536	usb_unanchor_urb(urb);
537
538exit:
539	usb_free_urb(urb);
540
541	return retval;
542}
543
544#define DIR_IN  1
545#define DIR_OUT 2
546
547#define rudimentary_check(dir)                             \
548do {                                                       \
549	if (!dev)                                          \
550		return -EFAULT;                            \
551	if (!dev->probed)                                  \
552		return -ENODEV;                            \
553	if (!dev->attached)                                \
554		return -ENODEV;                            \
555	if ((dir) & DIR_IN) {                              \
556		if (test_bit(TRANS_IN_BUSY, &dev->flags))  \
557			return -EBUSY;                     \
558	} else {  /* DIR_OUT */                            \
559		if (test_bit(TRANS_OUT_BUSY, &dev->flags)) \
560			return -EBUSY;                     \
561	}                                                  \
562} while (0)
563
564static int vmk80xx_ai_rinsn(struct comedi_device *cdev,
565			    struct comedi_subdevice *s,
566			    struct comedi_insn *insn, unsigned int *data)
567{
568	struct vmk80xx_usb *dev = cdev->private;
569	int chan, reg[2];
570	int n;
571
572	dbgvm("vmk80xx: %s\n", __func__);
573
574	rudimentary_check(DIR_IN);
575
576	down(&dev->limit_sem);
577	chan = CR_CHAN(insn->chanspec);
578
579	switch (dev->board.model) {
580	case VMK8055_MODEL:
581		if (!chan)
582			reg[0] = VMK8055_AI1_REG;
583		else
584			reg[0] = VMK8055_AI2_REG;
585		break;
586	case VMK8061_MODEL:
587		reg[0] = VMK8061_AI_REG1;
588		reg[1] = VMK8061_AI_REG2;
589		dev->usb_tx_buf[0] = VMK8061_CMD_RD_AI;
590		dev->usb_tx_buf[VMK8061_CH_REG] = chan;
591		break;
592	}
593
594	for (n = 0; n < insn->n; n++) {
595		if (vmk80xx_read_packet(dev))
596			break;
597
598		if (dev->board.model == VMK8055_MODEL) {
599			data[n] = dev->usb_rx_buf[reg[0]];
600			continue;
601		}
602
603		/* VMK8061_MODEL */
604		data[n] = dev->usb_rx_buf[reg[0]] + 256 *
605		    dev->usb_rx_buf[reg[1]];
606	}
607
608	up(&dev->limit_sem);
609
610	return n;
611}
612
613static int vmk80xx_ao_winsn(struct comedi_device *cdev,
614			    struct comedi_subdevice *s,
615			    struct comedi_insn *insn, unsigned int *data)
616{
617	struct vmk80xx_usb *dev = cdev->private;
618	int chan, cmd, reg;
619	int n;
620
621	dbgvm("vmk80xx: %s\n", __func__);
622
623	rudimentary_check(DIR_OUT);
624
625	down(&dev->limit_sem);
626	chan = CR_CHAN(insn->chanspec);
627
628	switch (dev->board.model) {
629	case VMK8055_MODEL:
630		cmd = VMK8055_CMD_WRT_AD;
631		if (!chan)
632			reg = VMK8055_AO1_REG;
633		else
634			reg = VMK8055_AO2_REG;
635		break;
636	default:		/* NOTE: avoid compiler warnings */
637		cmd = VMK8061_CMD_SET_AO;
638		reg = VMK8061_AO_REG;
639		dev->usb_tx_buf[VMK8061_CH_REG] = chan;
640		break;
641	}
642
643	for (n = 0; n < insn->n; n++) {
644		dev->usb_tx_buf[reg] = data[n];
645
646		if (vmk80xx_write_packet(dev, cmd))
647			break;
648	}
649
650	up(&dev->limit_sem);
651
652	return n;
653}
654
655static int vmk80xx_ao_rinsn(struct comedi_device *cdev,
656			    struct comedi_subdevice *s,
657			    struct comedi_insn *insn, unsigned int *data)
658{
659	struct vmk80xx_usb *dev = cdev->private;
660	int chan, reg;
661	int n;
662
663	dbgvm("vmk80xx: %s\n", __func__);
664
665	rudimentary_check(DIR_IN);
666
667	down(&dev->limit_sem);
668	chan = CR_CHAN(insn->chanspec);
669
670	reg = VMK8061_AO_REG - 1;
671
672	dev->usb_tx_buf[0] = VMK8061_CMD_RD_AO;
673
674	for (n = 0; n < insn->n; n++) {
675		if (vmk80xx_read_packet(dev))
676			break;
677
678		data[n] = dev->usb_rx_buf[reg + chan];
679	}
680
681	up(&dev->limit_sem);
682
683	return n;
684}
685
686static int vmk80xx_di_rinsn(struct comedi_device *cdev,
687			    struct comedi_subdevice *s,
688			    struct comedi_insn *insn, unsigned int *data)
689{
690	struct vmk80xx_usb *dev = cdev->private;
691	int chan;
692	unsigned char *rx_buf;
693	int reg, inp;
694	int n;
695
696	dbgvm("vmk80xx: %s\n", __func__);
697
698	rudimentary_check(DIR_IN);
699
700	down(&dev->limit_sem);
701	chan = CR_CHAN(insn->chanspec);
702
703	rx_buf = dev->usb_rx_buf;
704
705	if (dev->board.model == VMK8061_MODEL) {
706		reg = VMK8061_DI_REG;
707		dev->usb_tx_buf[0] = VMK8061_CMD_RD_DI;
708	} else
709		reg = VMK8055_DI_REG;
710
711	for (n = 0; n < insn->n; n++) {
712		if (vmk80xx_read_packet(dev))
713			break;
714
715		if (dev->board.model == VMK8055_MODEL)
716			inp = (((rx_buf[reg] >> 4) & 0x03) |
717			       ((rx_buf[reg] << 2) & 0x04) |
718			       ((rx_buf[reg] >> 3) & 0x18));
719		else
720			inp = rx_buf[reg];
721
722		data[n] = ((inp & (1 << chan)) > 0);
723	}
724
725	up(&dev->limit_sem);
726
727	return n;
728}
729
730static int vmk80xx_do_winsn(struct comedi_device *cdev,
731			    struct comedi_subdevice *s,
732			    struct comedi_insn *insn, unsigned int *data)
733{
734
735	struct vmk80xx_usb *dev = cdev->private;
736	int chan;
737	unsigned char *tx_buf;
738	int reg, cmd;
739	int n;
740
741	dbgvm("vmk80xx: %s\n", __func__);
742
743	rudimentary_check(DIR_OUT);
744
745	down(&dev->limit_sem);
746	chan = CR_CHAN(insn->chanspec);
747
748	tx_buf = dev->usb_tx_buf;
749
750	for (n = 0; n < insn->n; n++) {
751		if (dev->board.model == VMK8055_MODEL) {
752			reg = VMK8055_DO_REG;
753			cmd = VMK8055_CMD_WRT_AD;
754			if (data[n] == 1)
755				tx_buf[reg] |= (1 << chan);
756			else
757				tx_buf[reg] ^= (1 << chan);
758
759			goto write_packet;
760		}
761
762		/* VMK8061_MODEL */
763		reg = VMK8061_DO_REG;
764		if (data[n] == 1) {
765			cmd = VMK8061_CMD_SET_DO;
766			tx_buf[reg] = 1 << chan;
767		} else {
768			cmd = VMK8061_CMD_CLR_DO;
769			tx_buf[reg] = 0xff - (1 << chan);
770		}
771
772write_packet:
773		if (vmk80xx_write_packet(dev, cmd))
774			break;
775	}
776
777	up(&dev->limit_sem);
778
779	return n;
780}
781
782static int vmk80xx_do_rinsn(struct comedi_device *cdev,
783			    struct comedi_subdevice *s,
784			    struct comedi_insn *insn, unsigned int *data)
785{
786	struct vmk80xx_usb *dev = cdev->private;
787	int chan, reg, mask;
788	int n;
789
790	dbgvm("vmk80xx: %s\n", __func__);
791
792	rudimentary_check(DIR_IN);
793
794	down(&dev->limit_sem);
795	chan = CR_CHAN(insn->chanspec);
796
797	reg = VMK8061_DO_REG;
798	mask = 1 << chan;
799
800	dev->usb_tx_buf[0] = VMK8061_CMD_RD_DO;
801
802	for (n = 0; n < insn->n; n++) {
803		if (vmk80xx_read_packet(dev))
804			break;
805
806		data[n] = (dev->usb_rx_buf[reg] & mask) >> chan;
807	}
808
809	up(&dev->limit_sem);
810
811	return n;
812}
813
814static int vmk80xx_cnt_rinsn(struct comedi_device *cdev,
815			     struct comedi_subdevice *s,
816			     struct comedi_insn *insn, unsigned int *data)
817{
818	struct vmk80xx_usb *dev = cdev->private;
819	int chan, reg[2];
820	int n;
821
822	dbgvm("vmk80xx: %s\n", __func__);
823
824	rudimentary_check(DIR_IN);
825
826	down(&dev->limit_sem);
827	chan = CR_CHAN(insn->chanspec);
828
829	switch (dev->board.model) {
830	case VMK8055_MODEL:
831		if (!chan)
832			reg[0] = VMK8055_CNT1_REG;
833		else
834			reg[0] = VMK8055_CNT2_REG;
835		break;
836	case VMK8061_MODEL:
837		reg[0] = VMK8061_CNT_REG;
838		reg[1] = VMK8061_CNT_REG;
839		dev->usb_tx_buf[0] = VMK8061_CMD_RD_CNT;
840		break;
841	}
842
843	for (n = 0; n < insn->n; n++) {
844		if (vmk80xx_read_packet(dev))
845			break;
846
847		if (dev->board.model == VMK8055_MODEL) {
848			data[n] = dev->usb_rx_buf[reg[0]];
849			continue;
850		}
851
852		/* VMK8061_MODEL */
853		data[n] = dev->usb_rx_buf[reg[0] * (chan + 1) + 1]
854		    + 256 * dev->usb_rx_buf[reg[1] * 2 + 2];
855	}
856
857	up(&dev->limit_sem);
858
859	return n;
860}
861
862static int vmk80xx_cnt_cinsn(struct comedi_device *cdev,
863			     struct comedi_subdevice *s,
864			     struct comedi_insn *insn, unsigned int *data)
865{
866	struct vmk80xx_usb *dev = cdev->private;
867	unsigned int insn_cmd;
868	int chan, cmd, reg;
869	int n;
870
871	dbgvm("vmk80xx: %s\n", __func__);
872
873	rudimentary_check(DIR_OUT);
874
875	down(&dev->limit_sem);
876
877	insn_cmd = data[0];
878	if (insn_cmd != INSN_CONFIG_RESET && insn_cmd != GPCT_RESET)
879		return -EINVAL;
880
881	chan = CR_CHAN(insn->chanspec);
882
883	if (dev->board.model == VMK8055_MODEL) {
884		if (!chan) {
885			cmd = VMK8055_CMD_RST_CNT1;
886			reg = VMK8055_CNT1_REG;
887		} else {
888			cmd = VMK8055_CMD_RST_CNT2;
889			reg = VMK8055_CNT2_REG;
890		}
891
892		dev->usb_tx_buf[reg] = 0x00;
893	} else
894		cmd = VMK8061_CMD_RST_CNT;
895
896	for (n = 0; n < insn->n; n++)
897		if (vmk80xx_write_packet(dev, cmd))
898			break;
899
900	up(&dev->limit_sem);
901
902	return n;
903}
904
905static int vmk80xx_cnt_winsn(struct comedi_device *cdev,
906			     struct comedi_subdevice *s,
907			     struct comedi_insn *insn, unsigned int *data)
908{
909	struct vmk80xx_usb *dev = cdev->private;
910	unsigned long debtime, val;
911	int chan, cmd;
912	int n;
913
914	dbgvm("vmk80xx: %s\n", __func__);
915
916	rudimentary_check(DIR_OUT);
917
918	down(&dev->limit_sem);
919	chan = CR_CHAN(insn->chanspec);
920
921	if (!chan)
922		cmd = VMK8055_CMD_DEB1_TIME;
923	else
924		cmd = VMK8055_CMD_DEB2_TIME;
925
926	for (n = 0; n < insn->n; n++) {
927		debtime = data[n];
928		if (debtime == 0)
929			debtime = 1;
930
931		/* TODO: Prevent overflows */
932		if (debtime > 7450)
933			debtime = 7450;
934
935		val = int_sqrt(debtime * 1000 / 115);
936		if (((val + 1) * val) < debtime * 1000 / 115)
937			val += 1;
938
939		dev->usb_tx_buf[6 + chan] = val;
940
941		if (vmk80xx_write_packet(dev, cmd))
942			break;
943	}
944
945	up(&dev->limit_sem);
946
947	return n;
948}
949
950static int vmk80xx_pwm_rinsn(struct comedi_device *cdev,
951			     struct comedi_subdevice *s,
952			     struct comedi_insn *insn, unsigned int *data)
953{
954	struct vmk80xx_usb *dev = cdev->private;
955	int reg[2];
956	int n;
957
958	dbgvm("vmk80xx: %s\n", __func__);
959
960	rudimentary_check(DIR_IN);
961
962	down(&dev->limit_sem);
963
964	reg[0] = VMK8061_PWM_REG1;
965	reg[1] = VMK8061_PWM_REG2;
966
967	dev->usb_tx_buf[0] = VMK8061_CMD_RD_PWM;
968
969	for (n = 0; n < insn->n; n++) {
970		if (vmk80xx_read_packet(dev))
971			break;
972
973		data[n] = dev->usb_rx_buf[reg[0]] + 4 * dev->usb_rx_buf[reg[1]];
974	}
975
976	up(&dev->limit_sem);
977
978	return n;
979}
980
981static int vmk80xx_pwm_winsn(struct comedi_device *cdev,
982			     struct comedi_subdevice *s,
983			     struct comedi_insn *insn, unsigned int *data)
984{
985	struct vmk80xx_usb *dev = cdev->private;
986	unsigned char *tx_buf;
987	int reg[2], cmd;
988	int n;
989
990	dbgvm("vmk80xx: %s\n", __func__);
991
992	rudimentary_check(DIR_OUT);
993
994	down(&dev->limit_sem);
995
996	tx_buf = dev->usb_tx_buf;
997
998	reg[0] = VMK8061_PWM_REG1;
999	reg[1] = VMK8061_PWM_REG2;
1000
1001	cmd = VMK8061_CMD_OUT_PWM;
1002
1003	/*
1004	 * The followin piece of code was translated from the inline
1005	 * assembler code in the DLL source code.
1006	 *
1007	 * asm
1008	 *   mov eax, k  ; k is the value (data[n])
1009	 *   and al, 03h ; al are the lower 8 bits of eax
1010	 *   mov lo, al  ; lo is the low part (tx_buf[reg[0]])
1011	 *   mov eax, k
1012	 *   shr eax, 2  ; right shift eax register by 2
1013	 *   mov hi, al  ; hi is the high part (tx_buf[reg[1]])
1014	 * end;
1015	 */
1016	for (n = 0; n < insn->n; n++) {
1017		tx_buf[reg[0]] = (unsigned char)(data[n] & 0x03);
1018		tx_buf[reg[1]] = (unsigned char)(data[n] >> 2) & 0xff;
1019
1020		if (vmk80xx_write_packet(dev, cmd))
1021			break;
1022	}
1023
1024	up(&dev->limit_sem);
1025
1026	return n;
1027}
1028
1029static int
1030vmk80xx_attach(struct comedi_device *cdev, struct comedi_devconfig *it)
1031{
1032	int i;
1033	struct vmk80xx_usb *dev;
1034	int n_subd;
1035	struct comedi_subdevice *s;
1036	int minor;
1037
1038	dbgvm("vmk80xx: %s\n", __func__);
1039
1040	mutex_lock(&glb_mutex);
1041
1042	for (i = 0; i < VMK80XX_MAX_BOARDS; i++)
1043		if (vmb[i].probed && !vmb[i].attached)
1044			break;
1045
1046	if (i == VMK80XX_MAX_BOARDS) {
1047		mutex_unlock(&glb_mutex);
1048		return -ENODEV;
1049	}
1050
1051	dev = &vmb[i];
1052
1053	down(&dev->limit_sem);
1054
1055	cdev->board_name = dev->board.name;
1056	cdev->private = dev;
1057
1058	if (dev->board.model == VMK8055_MODEL)
1059		n_subd = 5;
1060	else
1061		n_subd = 6;
1062
1063	if (alloc_subdevices(cdev, n_subd) < 0) {
1064		up(&dev->limit_sem);
1065		mutex_unlock(&glb_mutex);
1066		return -ENOMEM;
1067	}
1068
1069	/* Analog input subdevice */
1070	s = cdev->subdevices + VMK80XX_SUBD_AI;
1071	s->type = COMEDI_SUBD_AI;
1072	s->subdev_flags = SDF_READABLE | SDF_GROUND;
1073	s->n_chan = dev->board.ai_chans;
1074	s->maxdata = (1 << dev->board.ai_bits) - 1;
1075	s->range_table = dev->board.range;
1076	s->insn_read = vmk80xx_ai_rinsn;
1077
1078	/* Analog output subdevice */
1079	s = cdev->subdevices + VMK80XX_SUBD_AO;
1080	s->type = COMEDI_SUBD_AO;
1081	s->subdev_flags = SDF_WRITEABLE | SDF_GROUND;
1082	s->n_chan = dev->board.ao_chans;
1083	s->maxdata = (1 << dev->board.ao_bits) - 1;
1084	s->range_table = dev->board.range;
1085	s->insn_write = vmk80xx_ao_winsn;
1086
1087	if (dev->board.model == VMK8061_MODEL) {
1088		s->subdev_flags |= SDF_READABLE;
1089		s->insn_read = vmk80xx_ao_rinsn;
1090	}
1091
1092	/* Digital input subdevice */
1093	s = cdev->subdevices + VMK80XX_SUBD_DI;
1094	s->type = COMEDI_SUBD_DI;
1095	s->subdev_flags = SDF_READABLE | SDF_GROUND;
1096	s->n_chan = dev->board.di_chans;
1097	s->maxdata = (1 << dev->board.di_bits) - 1;
1098	s->insn_read = vmk80xx_di_rinsn;
1099
1100	/* Digital output subdevice */
1101	s = cdev->subdevices + VMK80XX_SUBD_DO;
1102	s->type = COMEDI_SUBD_DO;
1103	s->subdev_flags = SDF_WRITEABLE | SDF_GROUND;
1104	s->n_chan = dev->board.do_chans;
1105	s->maxdata = (1 << dev->board.do_bits) - 1;
1106	s->insn_write = vmk80xx_do_winsn;
1107
1108	if (dev->board.model == VMK8061_MODEL) {
1109		s->subdev_flags |= SDF_READABLE;
1110		s->insn_read = vmk80xx_do_rinsn;
1111	}
1112
1113	/* Counter subdevice */
1114	s = cdev->subdevices + VMK80XX_SUBD_CNT;
1115	s->type = COMEDI_SUBD_COUNTER;
1116	s->subdev_flags = SDF_READABLE;
1117	s->n_chan = dev->board.cnt_chans;
1118	s->insn_read = vmk80xx_cnt_rinsn;
1119	s->insn_config = vmk80xx_cnt_cinsn;
1120
1121	if (dev->board.model == VMK8055_MODEL) {
1122		s->subdev_flags |= SDF_WRITEABLE;
1123		s->maxdata = (1 << dev->board.cnt_bits) - 1;
1124		s->insn_write = vmk80xx_cnt_winsn;
1125	}
1126
1127	/* PWM subdevice */
1128	if (dev->board.model == VMK8061_MODEL) {
1129		s = cdev->subdevices + VMK80XX_SUBD_PWM;
1130		s->type = COMEDI_SUBD_PWM;
1131		s->subdev_flags = SDF_READABLE | SDF_WRITEABLE;
1132		s->n_chan = dev->board.pwm_chans;
1133		s->maxdata = (1 << dev->board.pwm_bits) - 1;
1134		s->insn_read = vmk80xx_pwm_rinsn;
1135		s->insn_write = vmk80xx_pwm_winsn;
1136	}
1137
1138	dev->attached = 1;
1139
1140	minor = cdev->minor;
1141
1142	printk(KERN_INFO
1143	       "comedi%d: vmk80xx: board #%d [%s] attached to comedi\n",
1144	       minor, dev->count, dev->board.name);
1145
1146	up(&dev->limit_sem);
1147	mutex_unlock(&glb_mutex);
1148
1149	return 0;
1150}
1151
1152static int vmk80xx_detach(struct comedi_device *cdev)
1153{
1154	struct vmk80xx_usb *dev;
1155	int minor;
1156
1157	dbgvm("vmk80xx: %s\n", __func__);
1158
1159	if (!cdev)
1160		return -EFAULT;
1161
1162	dev = cdev->private;
1163	if (!dev)
1164		return -EFAULT;
1165
1166	down(&dev->limit_sem);
1167
1168	cdev->private = NULL;
1169	dev->attached = 0;
1170
1171	minor = cdev->minor;
1172
1173	printk(KERN_INFO
1174	       "comedi%d: vmk80xx: board #%d [%s] detached from comedi\n",
1175	       minor, dev->count, dev->board.name);
1176
1177	up(&dev->limit_sem);
1178
1179	return 0;
1180}
1181
1182static int
1183vmk80xx_probe(struct usb_interface *intf, const struct usb_device_id *id)
1184{
1185	int i;
1186	struct vmk80xx_usb *dev;
1187	struct usb_host_interface *iface_desc;
1188	struct usb_endpoint_descriptor *ep_desc;
1189	size_t size;
1190
1191	dbgvm("vmk80xx: %s\n", __func__);
1192
1193	mutex_lock(&glb_mutex);
1194
1195	for (i = 0; i < VMK80XX_MAX_BOARDS; i++)
1196		if (!vmb[i].probed)
1197			break;
1198
1199	if (i == VMK80XX_MAX_BOARDS) {
1200		mutex_unlock(&glb_mutex);
1201		return -EMFILE;
1202	}
1203
1204	dev = &vmb[i];
1205
1206	memset(dev, 0x00, sizeof(struct vmk80xx_usb));
1207	dev->count = i;
1208
1209	iface_desc = intf->cur_altsetting;
1210	if (iface_desc->desc.bNumEndpoints != 2)
1211		goto error;
1212
1213	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
1214		ep_desc = &iface_desc->endpoint[i].desc;
1215
1216		if (usb_endpoint_is_int_in(ep_desc)) {
1217			dev->ep_rx = ep_desc;
1218			continue;
1219		}
1220
1221		if (usb_endpoint_is_int_out(ep_desc)) {
1222			dev->ep_tx = ep_desc;
1223			continue;
1224		}
1225
1226		if (usb_endpoint_is_bulk_in(ep_desc)) {
1227			dev->ep_rx = ep_desc;
1228			continue;
1229		}
1230
1231		if (usb_endpoint_is_bulk_out(ep_desc)) {
1232			dev->ep_tx = ep_desc;
1233			continue;
1234		}
1235	}
1236
1237	if (!dev->ep_rx || !dev->ep_tx)
1238		goto error;
1239
1240	size = le16_to_cpu(dev->ep_rx->wMaxPacketSize);
1241	dev->usb_rx_buf = kmalloc(size, GFP_KERNEL);
1242	if (!dev->usb_rx_buf) {
1243		mutex_unlock(&glb_mutex);
1244		return -ENOMEM;
1245	}
1246
1247	size = le16_to_cpu(dev->ep_tx->wMaxPacketSize);
1248	dev->usb_tx_buf = kmalloc(size, GFP_KERNEL);
1249	if (!dev->usb_tx_buf) {
1250		kfree(dev->usb_rx_buf);
1251		mutex_unlock(&glb_mutex);
1252		return -ENOMEM;
1253	}
1254
1255	dev->udev = interface_to_usbdev(intf);
1256	dev->intf = intf;
1257
1258	sema_init(&dev->limit_sem, 8);
1259	init_waitqueue_head(&dev->read_wait);
1260	init_waitqueue_head(&dev->write_wait);
1261
1262	init_usb_anchor(&dev->rx_anchor);
1263	init_usb_anchor(&dev->tx_anchor);
1264
1265	usb_set_intfdata(intf, dev);
1266
1267	switch (id->driver_info) {
1268	case DEVICE_VMK8055:
1269		dev->board.name = "K8055 (VM110)";
1270		dev->board.model = VMK8055_MODEL;
1271		dev->board.range = &vmk8055_range;
1272		dev->board.ai_chans = 2;
1273		dev->board.ai_bits = 8;
1274		dev->board.ao_chans = 2;
1275		dev->board.ao_bits = 8;
1276		dev->board.di_chans = 5;
1277		dev->board.di_bits = 1;
1278		dev->board.do_chans = 8;
1279		dev->board.do_bits = 1;
1280		dev->board.cnt_chans = 2;
1281		dev->board.cnt_bits = 16;
1282		dev->board.pwm_chans = 0;
1283		dev->board.pwm_bits = 0;
1284		break;
1285	case DEVICE_VMK8061:
1286		dev->board.name = "K8061 (VM140)";
1287		dev->board.model = VMK8061_MODEL;
1288		dev->board.range = &vmk8061_range;
1289		dev->board.ai_chans = 8;
1290		dev->board.ai_bits = 10;
1291		dev->board.ao_chans = 8;
1292		dev->board.ao_bits = 8;
1293		dev->board.di_chans = 8;
1294		dev->board.di_bits = 1;
1295		dev->board.do_chans = 8;
1296		dev->board.do_bits = 1;
1297		dev->board.cnt_chans = 2;
1298		dev->board.cnt_bits = 0;
1299		dev->board.pwm_chans = 1;
1300		dev->board.pwm_bits = 10;
1301		break;
1302	}
1303
1304	if (dev->board.model == VMK8061_MODEL) {
1305		vmk80xx_read_eeprom(dev, IC3_VERSION);
1306		printk(KERN_INFO "comedi#: vmk80xx: %s\n", dev->fw.ic3_vers);
1307
1308		if (vmk80xx_check_data_link(dev)) {
1309			vmk80xx_read_eeprom(dev, IC6_VERSION);
1310			printk(KERN_INFO "comedi#: vmk80xx: %s\n",
1311			       dev->fw.ic6_vers);
1312		} else
1313			dbgcm("comedi#: vmk80xx: no conn. to CPU\n");
1314	}
1315
1316	if (dev->board.model == VMK8055_MODEL)
1317		vmk80xx_reset_device(dev);
1318
1319	dev->probed = 1;
1320
1321	printk(KERN_INFO "comedi#: vmk80xx: board #%d [%s] now attached\n",
1322	       dev->count, dev->board.name);
1323
1324	mutex_unlock(&glb_mutex);
1325
1326	return 0;
1327error:
1328	mutex_unlock(&glb_mutex);
1329
1330	return -ENODEV;
1331}
1332
1333static void vmk80xx_disconnect(struct usb_interface *intf)
1334{
1335	struct vmk80xx_usb *dev = usb_get_intfdata(intf);
1336
1337	dbgvm("vmk80xx: %s\n", __func__);
1338
1339	if (!dev)
1340		return;
1341
1342	mutex_lock(&glb_mutex);
1343	down(&dev->limit_sem);
1344
1345	dev->probed = 0;
1346	usb_set_intfdata(dev->intf, NULL);
1347
1348	usb_kill_anchored_urbs(&dev->rx_anchor);
1349	usb_kill_anchored_urbs(&dev->tx_anchor);
1350
1351	kfree(dev->usb_rx_buf);
1352	kfree(dev->usb_tx_buf);
1353
1354	printk(KERN_INFO "comedi#: vmk80xx: board #%d [%s] now detached\n",
1355	       dev->count, dev->board.name);
1356
1357	up(&dev->limit_sem);
1358	mutex_unlock(&glb_mutex);
1359}
1360
1361/* TODO: Add support for suspend, resume, pre_reset,
1362 * post_reset and flush */
1363static struct usb_driver vmk80xx_driver = {
1364	.name = "vmk80xx",
1365	.probe = vmk80xx_probe,
1366	.disconnect = vmk80xx_disconnect,
1367	.id_table = vmk80xx_id_table
1368};
1369
1370static struct comedi_driver driver_vmk80xx = {
1371	.module = THIS_MODULE,
1372	.driver_name = "vmk80xx",
1373	.attach = vmk80xx_attach,
1374	.detach = vmk80xx_detach
1375};
1376
1377static int __init vmk80xx_init(void)
1378{
1379	printk(KERN_INFO "vmk80xx: version 0.8.01 "
1380	       "Manuel Gebele <forensixs@gmx.de>\n");
1381	usb_register(&vmk80xx_driver);
1382	return comedi_driver_register(&driver_vmk80xx);
1383}
1384
1385static void __exit vmk80xx_exit(void)
1386{
1387	comedi_driver_unregister(&driver_vmk80xx);
1388	usb_deregister(&vmk80xx_driver);
1389}
1390
1391module_init(vmk80xx_init);
1392module_exit(vmk80xx_exit);
1393