1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * vmk80xx.c 4 * Velleman USB Board Low-Level Driver 5 * 6 * Copyright (C) 2009 Manuel Gebele <forensixs@gmx.de>, Germany 7 * 8 * COMEDI - Linux Control and Measurement Device Interface 9 * Copyright (C) 2000 David A. Schleef <ds@schleef.org> 10 */ 11 12 /* 13 * Driver: vmk80xx 14 * Description: Velleman USB Board Low-Level Driver 15 * Devices: [Velleman] K8055 (K8055/VM110), K8061 (K8061/VM140), 16 * VM110 (K8055/VM110), VM140 (K8061/VM140) 17 * Author: Manuel Gebele <forensixs@gmx.de> 18 * Updated: Sun, 10 May 2009 11:14:59 +0200 19 * Status: works 20 * 21 * Supports: 22 * - analog input 23 * - analog output 24 * - digital input 25 * - digital output 26 * - counter 27 * - pwm 28 */ 29 30 #include <linux/kernel.h> 31 #include <linux/module.h> 32 #include <linux/mutex.h> 33 #include <linux/errno.h> 34 #include <linux/input.h> 35 #include <linux/slab.h> 36 #include <linux/poll.h> 37 #include <linux/uaccess.h> 38 39 #include "../comedi_usb.h" 40 41 enum { 42 DEVICE_VMK8055, 43 DEVICE_VMK8061 44 }; 45 46 #define VMK8055_DI_REG 0x00 47 #define VMK8055_DO_REG 0x01 48 #define VMK8055_AO1_REG 0x02 49 #define VMK8055_AO2_REG 0x03 50 #define VMK8055_AI1_REG 0x02 51 #define VMK8055_AI2_REG 0x03 52 #define VMK8055_CNT1_REG 0x04 53 #define VMK8055_CNT2_REG 0x06 54 55 #define VMK8061_CH_REG 0x01 56 #define VMK8061_DI_REG 0x01 57 #define VMK8061_DO_REG 0x01 58 #define VMK8061_PWM_REG1 0x01 59 #define VMK8061_PWM_REG2 0x02 60 #define VMK8061_CNT_REG 0x02 61 #define VMK8061_AO_REG 0x02 62 #define VMK8061_AI_REG1 0x02 63 #define VMK8061_AI_REG2 0x03 64 65 #define VMK8055_CMD_RST 0x00 66 #define VMK8055_CMD_DEB1_TIME 0x01 67 #define VMK8055_CMD_DEB2_TIME 0x02 68 #define VMK8055_CMD_RST_CNT1 0x03 69 #define VMK8055_CMD_RST_CNT2 0x04 70 #define VMK8055_CMD_WRT_AD 0x05 71 72 #define VMK8061_CMD_RD_AI 0x00 73 #define VMK8061_CMR_RD_ALL_AI 0x01 /* !non-active! */ 74 #define VMK8061_CMD_SET_AO 0x02 75 #define VMK8061_CMD_SET_ALL_AO 0x03 /* !non-active! */ 76 #define VMK8061_CMD_OUT_PWM 0x04 77 #define VMK8061_CMD_RD_DI 0x05 78 #define VMK8061_CMD_DO 0x06 /* !non-active! */ 79 #define VMK8061_CMD_CLR_DO 0x07 80 #define VMK8061_CMD_SET_DO 0x08 81 #define VMK8061_CMD_RD_CNT 0x09 /* TODO: completely pointless? */ 82 #define VMK8061_CMD_RST_CNT 0x0a /* TODO: completely pointless? */ 83 #define VMK8061_CMD_RD_VERSION 0x0b /* internal usage */ 84 #define VMK8061_CMD_RD_JMP_STAT 0x0c /* TODO: not implemented yet */ 85 #define VMK8061_CMD_RD_PWR_STAT 0x0d /* internal usage */ 86 #define VMK8061_CMD_RD_DO 0x0e 87 #define VMK8061_CMD_RD_AO 0x0f 88 #define VMK8061_CMD_RD_PWM 0x10 89 90 #define IC3_VERSION BIT(0) 91 #define IC6_VERSION BIT(1) 92 93 #define MIN_BUF_SIZE 64 94 #define PACKET_TIMEOUT 10000 /* ms */ 95 96 enum vmk80xx_model { 97 VMK8055_MODEL, 98 VMK8061_MODEL 99 }; 100 101 static const struct comedi_lrange vmk8061_range = { 102 2, { 103 UNI_RANGE(5), 104 UNI_RANGE(10) 105 } 106 }; 107 108 struct vmk80xx_board { 109 const char *name; 110 enum vmk80xx_model model; 111 const struct comedi_lrange *range; 112 int ai_nchans; 113 unsigned int ai_maxdata; 114 int ao_nchans; 115 int di_nchans; 116 unsigned int cnt_maxdata; 117 int pwm_nchans; 118 unsigned int pwm_maxdata; 119 }; 120 121 static const struct vmk80xx_board vmk80xx_boardinfo[] = { 122 [DEVICE_VMK8055] = { 123 .name = "K8055 (VM110)", 124 .model = VMK8055_MODEL, 125 .range = &range_unipolar5, 126 .ai_nchans = 2, 127 .ai_maxdata = 0x00ff, 128 .ao_nchans = 2, 129 .di_nchans = 6, 130 .cnt_maxdata = 0xffff, 131 }, 132 [DEVICE_VMK8061] = { 133 .name = "K8061 (VM140)", 134 .model = VMK8061_MODEL, 135 .range = &vmk8061_range, 136 .ai_nchans = 8, 137 .ai_maxdata = 0x03ff, 138 .ao_nchans = 8, 139 .di_nchans = 8, 140 .cnt_maxdata = 0, /* unknown, device is not writeable */ 141 .pwm_nchans = 1, 142 .pwm_maxdata = 0x03ff, 143 }, 144 }; 145 146 struct vmk80xx_private { 147 struct usb_endpoint_descriptor *ep_rx; 148 struct usb_endpoint_descriptor *ep_tx; 149 struct semaphore limit_sem; 150 unsigned char *usb_rx_buf; 151 unsigned char *usb_tx_buf; 152 enum vmk80xx_model model; 153 }; 154 155 static void vmk80xx_do_bulk_msg(struct comedi_device *dev) 156 { 157 struct vmk80xx_private *devpriv = dev->private; 158 struct usb_device *usb = comedi_to_usb_dev(dev); 159 __u8 tx_addr; 160 __u8 rx_addr; 161 unsigned int tx_pipe; 162 unsigned int rx_pipe; 163 size_t tx_size; 164 size_t rx_size; 165 166 tx_addr = devpriv->ep_tx->bEndpointAddress; 167 rx_addr = devpriv->ep_rx->bEndpointAddress; 168 tx_pipe = usb_sndbulkpipe(usb, tx_addr); 169 rx_pipe = usb_rcvbulkpipe(usb, rx_addr); 170 tx_size = usb_endpoint_maxp(devpriv->ep_tx); 171 rx_size = usb_endpoint_maxp(devpriv->ep_rx); 172 173 usb_bulk_msg(usb, tx_pipe, devpriv->usb_tx_buf, tx_size, NULL, 174 PACKET_TIMEOUT); 175 176 usb_bulk_msg(usb, rx_pipe, devpriv->usb_rx_buf, rx_size, NULL, 177 PACKET_TIMEOUT); 178 } 179 180 static int vmk80xx_read_packet(struct comedi_device *dev) 181 { 182 struct vmk80xx_private *devpriv = dev->private; 183 struct usb_device *usb = comedi_to_usb_dev(dev); 184 struct usb_endpoint_descriptor *ep; 185 unsigned int pipe; 186 187 if (devpriv->model == VMK8061_MODEL) { 188 vmk80xx_do_bulk_msg(dev); 189 return 0; 190 } 191 192 ep = devpriv->ep_rx; 193 pipe = usb_rcvintpipe(usb, ep->bEndpointAddress); 194 return usb_interrupt_msg(usb, pipe, devpriv->usb_rx_buf, 195 usb_endpoint_maxp(ep), NULL, 196 PACKET_TIMEOUT); 197 } 198 199 static int vmk80xx_write_packet(struct comedi_device *dev, int cmd) 200 { 201 struct vmk80xx_private *devpriv = dev->private; 202 struct usb_device *usb = comedi_to_usb_dev(dev); 203 struct usb_endpoint_descriptor *ep; 204 unsigned int pipe; 205 206 devpriv->usb_tx_buf[0] = cmd; 207 208 if (devpriv->model == VMK8061_MODEL) { 209 vmk80xx_do_bulk_msg(dev); 210 return 0; 211 } 212 213 ep = devpriv->ep_tx; 214 pipe = usb_sndintpipe(usb, ep->bEndpointAddress); 215 return usb_interrupt_msg(usb, pipe, devpriv->usb_tx_buf, 216 usb_endpoint_maxp(ep), NULL, 217 PACKET_TIMEOUT); 218 } 219 220 static int vmk80xx_reset_device(struct comedi_device *dev) 221 { 222 struct vmk80xx_private *devpriv = dev->private; 223 size_t size; 224 int retval; 225 226 size = usb_endpoint_maxp(devpriv->ep_tx); 227 memset(devpriv->usb_tx_buf, 0, size); 228 retval = vmk80xx_write_packet(dev, VMK8055_CMD_RST); 229 if (retval) 230 return retval; 231 /* set outputs to known state as we cannot read them */ 232 return vmk80xx_write_packet(dev, VMK8055_CMD_WRT_AD); 233 } 234 235 static int vmk80xx_ai_insn_read(struct comedi_device *dev, 236 struct comedi_subdevice *s, 237 struct comedi_insn *insn, 238 unsigned int *data) 239 { 240 struct vmk80xx_private *devpriv = dev->private; 241 int chan; 242 int reg[2]; 243 int n; 244 245 down(&devpriv->limit_sem); 246 chan = CR_CHAN(insn->chanspec); 247 248 switch (devpriv->model) { 249 case VMK8055_MODEL: 250 if (!chan) 251 reg[0] = VMK8055_AI1_REG; 252 else 253 reg[0] = VMK8055_AI2_REG; 254 break; 255 case VMK8061_MODEL: 256 default: 257 reg[0] = VMK8061_AI_REG1; 258 reg[1] = VMK8061_AI_REG2; 259 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AI; 260 devpriv->usb_tx_buf[VMK8061_CH_REG] = chan; 261 break; 262 } 263 264 for (n = 0; n < insn->n; n++) { 265 if (vmk80xx_read_packet(dev)) 266 break; 267 268 if (devpriv->model == VMK8055_MODEL) { 269 data[n] = devpriv->usb_rx_buf[reg[0]]; 270 continue; 271 } 272 273 /* VMK8061_MODEL */ 274 data[n] = devpriv->usb_rx_buf[reg[0]] + 256 * 275 devpriv->usb_rx_buf[reg[1]]; 276 } 277 278 up(&devpriv->limit_sem); 279 280 return n; 281 } 282 283 static int vmk80xx_ao_insn_write(struct comedi_device *dev, 284 struct comedi_subdevice *s, 285 struct comedi_insn *insn, 286 unsigned int *data) 287 { 288 struct vmk80xx_private *devpriv = dev->private; 289 int chan; 290 int cmd; 291 int reg; 292 int n; 293 294 down(&devpriv->limit_sem); 295 chan = CR_CHAN(insn->chanspec); 296 297 switch (devpriv->model) { 298 case VMK8055_MODEL: 299 cmd = VMK8055_CMD_WRT_AD; 300 if (!chan) 301 reg = VMK8055_AO1_REG; 302 else 303 reg = VMK8055_AO2_REG; 304 break; 305 default: /* NOTE: avoid compiler warnings */ 306 cmd = VMK8061_CMD_SET_AO; 307 reg = VMK8061_AO_REG; 308 devpriv->usb_tx_buf[VMK8061_CH_REG] = chan; 309 break; 310 } 311 312 for (n = 0; n < insn->n; n++) { 313 devpriv->usb_tx_buf[reg] = data[n]; 314 315 if (vmk80xx_write_packet(dev, cmd)) 316 break; 317 } 318 319 up(&devpriv->limit_sem); 320 321 return n; 322 } 323 324 static int vmk80xx_ao_insn_read(struct comedi_device *dev, 325 struct comedi_subdevice *s, 326 struct comedi_insn *insn, 327 unsigned int *data) 328 { 329 struct vmk80xx_private *devpriv = dev->private; 330 int chan; 331 int reg; 332 int n; 333 334 down(&devpriv->limit_sem); 335 chan = CR_CHAN(insn->chanspec); 336 337 reg = VMK8061_AO_REG - 1; 338 339 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AO; 340 341 for (n = 0; n < insn->n; n++) { 342 if (vmk80xx_read_packet(dev)) 343 break; 344 345 data[n] = devpriv->usb_rx_buf[reg + chan]; 346 } 347 348 up(&devpriv->limit_sem); 349 350 return n; 351 } 352 353 static int vmk80xx_di_insn_bits(struct comedi_device *dev, 354 struct comedi_subdevice *s, 355 struct comedi_insn *insn, 356 unsigned int *data) 357 { 358 struct vmk80xx_private *devpriv = dev->private; 359 unsigned char *rx_buf; 360 int reg; 361 int retval; 362 363 down(&devpriv->limit_sem); 364 365 rx_buf = devpriv->usb_rx_buf; 366 367 if (devpriv->model == VMK8061_MODEL) { 368 reg = VMK8061_DI_REG; 369 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_DI; 370 } else { 371 reg = VMK8055_DI_REG; 372 } 373 374 retval = vmk80xx_read_packet(dev); 375 376 if (!retval) { 377 if (devpriv->model == VMK8055_MODEL) 378 data[1] = (((rx_buf[reg] >> 4) & 0x03) | 379 ((rx_buf[reg] << 2) & 0x04) | 380 ((rx_buf[reg] >> 3) & 0x18)); 381 else 382 data[1] = rx_buf[reg]; 383 384 retval = 2; 385 } 386 387 up(&devpriv->limit_sem); 388 389 return retval; 390 } 391 392 static int vmk80xx_do_insn_bits(struct comedi_device *dev, 393 struct comedi_subdevice *s, 394 struct comedi_insn *insn, 395 unsigned int *data) 396 { 397 struct vmk80xx_private *devpriv = dev->private; 398 unsigned char *rx_buf = devpriv->usb_rx_buf; 399 unsigned char *tx_buf = devpriv->usb_tx_buf; 400 int reg, cmd; 401 int ret = 0; 402 403 if (devpriv->model == VMK8061_MODEL) { 404 reg = VMK8061_DO_REG; 405 cmd = VMK8061_CMD_DO; 406 } else { /* VMK8055_MODEL */ 407 reg = VMK8055_DO_REG; 408 cmd = VMK8055_CMD_WRT_AD; 409 } 410 411 down(&devpriv->limit_sem); 412 413 if (comedi_dio_update_state(s, data)) { 414 tx_buf[reg] = s->state; 415 ret = vmk80xx_write_packet(dev, cmd); 416 if (ret) 417 goto out; 418 } 419 420 if (devpriv->model == VMK8061_MODEL) { 421 tx_buf[0] = VMK8061_CMD_RD_DO; 422 ret = vmk80xx_read_packet(dev); 423 if (ret) 424 goto out; 425 data[1] = rx_buf[reg]; 426 } else { 427 data[1] = s->state; 428 } 429 430 out: 431 up(&devpriv->limit_sem); 432 433 return ret ? ret : insn->n; 434 } 435 436 static int vmk80xx_cnt_insn_read(struct comedi_device *dev, 437 struct comedi_subdevice *s, 438 struct comedi_insn *insn, 439 unsigned int *data) 440 { 441 struct vmk80xx_private *devpriv = dev->private; 442 int chan; 443 int reg[2]; 444 int n; 445 446 down(&devpriv->limit_sem); 447 chan = CR_CHAN(insn->chanspec); 448 449 switch (devpriv->model) { 450 case VMK8055_MODEL: 451 if (!chan) 452 reg[0] = VMK8055_CNT1_REG; 453 else 454 reg[0] = VMK8055_CNT2_REG; 455 break; 456 case VMK8061_MODEL: 457 default: 458 reg[0] = VMK8061_CNT_REG; 459 reg[1] = VMK8061_CNT_REG; 460 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_CNT; 461 break; 462 } 463 464 for (n = 0; n < insn->n; n++) { 465 if (vmk80xx_read_packet(dev)) 466 break; 467 468 if (devpriv->model == VMK8055_MODEL) 469 data[n] = devpriv->usb_rx_buf[reg[0]]; 470 else /* VMK8061_MODEL */ 471 data[n] = devpriv->usb_rx_buf[reg[0] * (chan + 1) + 1] 472 + 256 * devpriv->usb_rx_buf[reg[1] * 2 + 2]; 473 } 474 475 up(&devpriv->limit_sem); 476 477 return n; 478 } 479 480 static int vmk80xx_cnt_insn_config(struct comedi_device *dev, 481 struct comedi_subdevice *s, 482 struct comedi_insn *insn, 483 unsigned int *data) 484 { 485 struct vmk80xx_private *devpriv = dev->private; 486 unsigned int chan = CR_CHAN(insn->chanspec); 487 int cmd; 488 int reg; 489 int ret; 490 491 down(&devpriv->limit_sem); 492 switch (data[0]) { 493 case INSN_CONFIG_RESET: 494 if (devpriv->model == VMK8055_MODEL) { 495 if (!chan) { 496 cmd = VMK8055_CMD_RST_CNT1; 497 reg = VMK8055_CNT1_REG; 498 } else { 499 cmd = VMK8055_CMD_RST_CNT2; 500 reg = VMK8055_CNT2_REG; 501 } 502 devpriv->usb_tx_buf[reg] = 0x00; 503 } else { 504 cmd = VMK8061_CMD_RST_CNT; 505 } 506 ret = vmk80xx_write_packet(dev, cmd); 507 break; 508 default: 509 ret = -EINVAL; 510 break; 511 } 512 up(&devpriv->limit_sem); 513 514 return ret ? ret : insn->n; 515 } 516 517 static int vmk80xx_cnt_insn_write(struct comedi_device *dev, 518 struct comedi_subdevice *s, 519 struct comedi_insn *insn, 520 unsigned int *data) 521 { 522 struct vmk80xx_private *devpriv = dev->private; 523 unsigned long debtime; 524 unsigned long val; 525 int chan; 526 int cmd; 527 int n; 528 529 down(&devpriv->limit_sem); 530 chan = CR_CHAN(insn->chanspec); 531 532 if (!chan) 533 cmd = VMK8055_CMD_DEB1_TIME; 534 else 535 cmd = VMK8055_CMD_DEB2_TIME; 536 537 for (n = 0; n < insn->n; n++) { 538 debtime = data[n]; 539 if (debtime == 0) 540 debtime = 1; 541 542 /* TODO: Prevent overflows */ 543 if (debtime > 7450) 544 debtime = 7450; 545 546 val = int_sqrt(debtime * 1000 / 115); 547 if (((val + 1) * val) < debtime * 1000 / 115) 548 val += 1; 549 550 devpriv->usb_tx_buf[6 + chan] = val; 551 552 if (vmk80xx_write_packet(dev, cmd)) 553 break; 554 } 555 556 up(&devpriv->limit_sem); 557 558 return n; 559 } 560 561 static int vmk80xx_pwm_insn_read(struct comedi_device *dev, 562 struct comedi_subdevice *s, 563 struct comedi_insn *insn, 564 unsigned int *data) 565 { 566 struct vmk80xx_private *devpriv = dev->private; 567 unsigned char *tx_buf; 568 unsigned char *rx_buf; 569 int reg[2]; 570 int n; 571 572 down(&devpriv->limit_sem); 573 574 tx_buf = devpriv->usb_tx_buf; 575 rx_buf = devpriv->usb_rx_buf; 576 577 reg[0] = VMK8061_PWM_REG1; 578 reg[1] = VMK8061_PWM_REG2; 579 580 tx_buf[0] = VMK8061_CMD_RD_PWM; 581 582 for (n = 0; n < insn->n; n++) { 583 if (vmk80xx_read_packet(dev)) 584 break; 585 586 data[n] = rx_buf[reg[0]] + 4 * rx_buf[reg[1]]; 587 } 588 589 up(&devpriv->limit_sem); 590 591 return n; 592 } 593 594 static int vmk80xx_pwm_insn_write(struct comedi_device *dev, 595 struct comedi_subdevice *s, 596 struct comedi_insn *insn, 597 unsigned int *data) 598 { 599 struct vmk80xx_private *devpriv = dev->private; 600 unsigned char *tx_buf; 601 int reg[2]; 602 int cmd; 603 int n; 604 605 down(&devpriv->limit_sem); 606 607 tx_buf = devpriv->usb_tx_buf; 608 609 reg[0] = VMK8061_PWM_REG1; 610 reg[1] = VMK8061_PWM_REG2; 611 612 cmd = VMK8061_CMD_OUT_PWM; 613 614 /* 615 * The followin piece of code was translated from the inline 616 * assembler code in the DLL source code. 617 * 618 * asm 619 * mov eax, k ; k is the value (data[n]) 620 * and al, 03h ; al are the lower 8 bits of eax 621 * mov lo, al ; lo is the low part (tx_buf[reg[0]]) 622 * mov eax, k 623 * shr eax, 2 ; right shift eax register by 2 624 * mov hi, al ; hi is the high part (tx_buf[reg[1]]) 625 * end; 626 */ 627 for (n = 0; n < insn->n; n++) { 628 tx_buf[reg[0]] = (unsigned char)(data[n] & 0x03); 629 tx_buf[reg[1]] = (unsigned char)(data[n] >> 2) & 0xff; 630 631 if (vmk80xx_write_packet(dev, cmd)) 632 break; 633 } 634 635 up(&devpriv->limit_sem); 636 637 return n; 638 } 639 640 static int vmk80xx_find_usb_endpoints(struct comedi_device *dev) 641 { 642 struct vmk80xx_private *devpriv = dev->private; 643 struct usb_interface *intf = comedi_to_usb_interface(dev); 644 struct usb_host_interface *iface_desc = intf->cur_altsetting; 645 struct usb_endpoint_descriptor *ep_desc; 646 int i; 647 648 if (iface_desc->desc.bNumEndpoints != 2) 649 return -ENODEV; 650 651 for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { 652 ep_desc = &iface_desc->endpoint[i].desc; 653 654 if (usb_endpoint_is_int_in(ep_desc) || 655 usb_endpoint_is_bulk_in(ep_desc)) { 656 if (!devpriv->ep_rx) 657 devpriv->ep_rx = ep_desc; 658 continue; 659 } 660 661 if (usb_endpoint_is_int_out(ep_desc) || 662 usb_endpoint_is_bulk_out(ep_desc)) { 663 if (!devpriv->ep_tx) 664 devpriv->ep_tx = ep_desc; 665 continue; 666 } 667 } 668 669 if (!devpriv->ep_rx || !devpriv->ep_tx) 670 return -ENODEV; 671 672 if (!usb_endpoint_maxp(devpriv->ep_rx) || !usb_endpoint_maxp(devpriv->ep_tx)) 673 return -EINVAL; 674 675 return 0; 676 } 677 678 static int vmk80xx_alloc_usb_buffers(struct comedi_device *dev) 679 { 680 struct vmk80xx_private *devpriv = dev->private; 681 size_t size; 682 683 size = max(usb_endpoint_maxp(devpriv->ep_rx), MIN_BUF_SIZE); 684 devpriv->usb_rx_buf = kzalloc(size, GFP_KERNEL); 685 if (!devpriv->usb_rx_buf) 686 return -ENOMEM; 687 688 size = max(usb_endpoint_maxp(devpriv->ep_rx), MIN_BUF_SIZE); 689 devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL); 690 if (!devpriv->usb_tx_buf) 691 return -ENOMEM; 692 693 return 0; 694 } 695 696 static int vmk80xx_init_subdevices(struct comedi_device *dev) 697 { 698 const struct vmk80xx_board *board = dev->board_ptr; 699 struct vmk80xx_private *devpriv = dev->private; 700 struct comedi_subdevice *s; 701 int n_subd; 702 int ret; 703 704 down(&devpriv->limit_sem); 705 706 if (devpriv->model == VMK8055_MODEL) 707 n_subd = 5; 708 else 709 n_subd = 6; 710 ret = comedi_alloc_subdevices(dev, n_subd); 711 if (ret) { 712 up(&devpriv->limit_sem); 713 return ret; 714 } 715 716 /* Analog input subdevice */ 717 s = &dev->subdevices[0]; 718 s->type = COMEDI_SUBD_AI; 719 s->subdev_flags = SDF_READABLE | SDF_GROUND; 720 s->n_chan = board->ai_nchans; 721 s->maxdata = board->ai_maxdata; 722 s->range_table = board->range; 723 s->insn_read = vmk80xx_ai_insn_read; 724 725 /* Analog output subdevice */ 726 s = &dev->subdevices[1]; 727 s->type = COMEDI_SUBD_AO; 728 s->subdev_flags = SDF_WRITABLE | SDF_GROUND; 729 s->n_chan = board->ao_nchans; 730 s->maxdata = 0x00ff; 731 s->range_table = board->range; 732 s->insn_write = vmk80xx_ao_insn_write; 733 if (devpriv->model == VMK8061_MODEL) { 734 s->subdev_flags |= SDF_READABLE; 735 s->insn_read = vmk80xx_ao_insn_read; 736 } 737 738 /* Digital input subdevice */ 739 s = &dev->subdevices[2]; 740 s->type = COMEDI_SUBD_DI; 741 s->subdev_flags = SDF_READABLE; 742 s->n_chan = board->di_nchans; 743 s->maxdata = 1; 744 s->range_table = &range_digital; 745 s->insn_bits = vmk80xx_di_insn_bits; 746 747 /* Digital output subdevice */ 748 s = &dev->subdevices[3]; 749 s->type = COMEDI_SUBD_DO; 750 s->subdev_flags = SDF_WRITABLE; 751 s->n_chan = 8; 752 s->maxdata = 1; 753 s->range_table = &range_digital; 754 s->insn_bits = vmk80xx_do_insn_bits; 755 756 /* Counter subdevice */ 757 s = &dev->subdevices[4]; 758 s->type = COMEDI_SUBD_COUNTER; 759 s->subdev_flags = SDF_READABLE; 760 s->n_chan = 2; 761 s->maxdata = board->cnt_maxdata; 762 s->insn_read = vmk80xx_cnt_insn_read; 763 s->insn_config = vmk80xx_cnt_insn_config; 764 if (devpriv->model == VMK8055_MODEL) { 765 s->subdev_flags |= SDF_WRITABLE; 766 s->insn_write = vmk80xx_cnt_insn_write; 767 } 768 769 /* PWM subdevice */ 770 if (devpriv->model == VMK8061_MODEL) { 771 s = &dev->subdevices[5]; 772 s->type = COMEDI_SUBD_PWM; 773 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 774 s->n_chan = board->pwm_nchans; 775 s->maxdata = board->pwm_maxdata; 776 s->insn_read = vmk80xx_pwm_insn_read; 777 s->insn_write = vmk80xx_pwm_insn_write; 778 } 779 780 up(&devpriv->limit_sem); 781 782 return 0; 783 } 784 785 static int vmk80xx_auto_attach(struct comedi_device *dev, 786 unsigned long context) 787 { 788 struct usb_interface *intf = comedi_to_usb_interface(dev); 789 const struct vmk80xx_board *board = NULL; 790 struct vmk80xx_private *devpriv; 791 int ret; 792 793 if (context < ARRAY_SIZE(vmk80xx_boardinfo)) 794 board = &vmk80xx_boardinfo[context]; 795 if (!board) 796 return -ENODEV; 797 dev->board_ptr = board; 798 dev->board_name = board->name; 799 800 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 801 if (!devpriv) 802 return -ENOMEM; 803 804 devpriv->model = board->model; 805 806 sema_init(&devpriv->limit_sem, 8); 807 808 ret = vmk80xx_find_usb_endpoints(dev); 809 if (ret) 810 return ret; 811 812 ret = vmk80xx_alloc_usb_buffers(dev); 813 if (ret) 814 return ret; 815 816 usb_set_intfdata(intf, devpriv); 817 818 if (devpriv->model == VMK8055_MODEL) 819 vmk80xx_reset_device(dev); 820 821 return vmk80xx_init_subdevices(dev); 822 } 823 824 static void vmk80xx_detach(struct comedi_device *dev) 825 { 826 struct usb_interface *intf = comedi_to_usb_interface(dev); 827 struct vmk80xx_private *devpriv = dev->private; 828 829 if (!devpriv) 830 return; 831 832 down(&devpriv->limit_sem); 833 834 usb_set_intfdata(intf, NULL); 835 836 kfree(devpriv->usb_rx_buf); 837 kfree(devpriv->usb_tx_buf); 838 839 up(&devpriv->limit_sem); 840 } 841 842 static struct comedi_driver vmk80xx_driver = { 843 .module = THIS_MODULE, 844 .driver_name = "vmk80xx", 845 .auto_attach = vmk80xx_auto_attach, 846 .detach = vmk80xx_detach, 847 }; 848 849 static int vmk80xx_usb_probe(struct usb_interface *intf, 850 const struct usb_device_id *id) 851 { 852 return comedi_usb_auto_config(intf, &vmk80xx_driver, id->driver_info); 853 } 854 855 static const struct usb_device_id vmk80xx_usb_id_table[] = { 856 { USB_DEVICE(0x10cf, 0x5500), .driver_info = DEVICE_VMK8055 }, 857 { USB_DEVICE(0x10cf, 0x5501), .driver_info = DEVICE_VMK8055 }, 858 { USB_DEVICE(0x10cf, 0x5502), .driver_info = DEVICE_VMK8055 }, 859 { USB_DEVICE(0x10cf, 0x5503), .driver_info = DEVICE_VMK8055 }, 860 { USB_DEVICE(0x10cf, 0x8061), .driver_info = DEVICE_VMK8061 }, 861 { USB_DEVICE(0x10cf, 0x8062), .driver_info = DEVICE_VMK8061 }, 862 { USB_DEVICE(0x10cf, 0x8063), .driver_info = DEVICE_VMK8061 }, 863 { USB_DEVICE(0x10cf, 0x8064), .driver_info = DEVICE_VMK8061 }, 864 { USB_DEVICE(0x10cf, 0x8065), .driver_info = DEVICE_VMK8061 }, 865 { USB_DEVICE(0x10cf, 0x8066), .driver_info = DEVICE_VMK8061 }, 866 { USB_DEVICE(0x10cf, 0x8067), .driver_info = DEVICE_VMK8061 }, 867 { USB_DEVICE(0x10cf, 0x8068), .driver_info = DEVICE_VMK8061 }, 868 { } 869 }; 870 MODULE_DEVICE_TABLE(usb, vmk80xx_usb_id_table); 871 872 static struct usb_driver vmk80xx_usb_driver = { 873 .name = "vmk80xx", 874 .id_table = vmk80xx_usb_id_table, 875 .probe = vmk80xx_usb_probe, 876 .disconnect = comedi_usb_auto_unconfig, 877 }; 878 module_comedi_usb_driver(vmk80xx_driver, vmk80xx_usb_driver); 879 880 MODULE_AUTHOR("Manuel Gebele <forensixs@gmx.de>"); 881 MODULE_DESCRIPTION("Velleman USB Board Low-Level Driver"); 882 MODULE_LICENSE("GPL"); 883