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