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