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