vmk80xx.c revision b153d83efb3d0ea15f5e6c7dced98fadc66531de
1/* 2 comedi/drivers/vmk80xx.c 3 Velleman USB Interface Board Kernel-Space 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 Interface Board Kernel-Space Driver 28Devices: K8055, K8061 (in development) 29Author: Manuel Gebele <forensixs@gmx.de> 30Updated: Tue, 21 Apr 2009 19:40:55 +0200 31Status: works 32*/ 33 34#include <linux/kernel.h> 35#include <linux/module.h> 36#include <linux/mutex.h> 37#include <linux/errno.h> 38#include <linux/input.h> 39#include <linux/slab.h> 40#include <linux/poll.h> 41#include <linux/usb.h> 42#include <asm/uaccess.h> 43 44#include "../comedidev.h" /* comedi definitions */ 45 46/* ------------------------------------------------------------------------ */ 47#define VMK80XX_MODULE_DESC "Velleman USB Interface Board Kernel-Space Driver" 48#define VMK80XX_MODULE_DEVICE "Velleman K8055/K8061 USB Interface Board" 49#define VMK80XX_MODULE_AUTHOR "Copyright (C) 2009 Manuel Gebele, Germany" 50#define VMK80XX_MODULE_LICENSE "GPL" 51#define VMK80XX_MODULE_VERSION "0.7.76" 52 53/* Module device ID's */ 54static struct usb_device_id vm_id_table[] = { 55 /* k8055 */ 56 { USB_DEVICE(0x10cf, 0x5500 + 0x00) }, /* @ddr. 0 */ 57 { USB_DEVICE(0x10cf, 0x5500 + 0x01) }, /* @ddr. 1 */ 58 { USB_DEVICE(0x10cf, 0x5500 + 0x02) }, /* @ddr. 2 */ 59 { USB_DEVICE(0x10cf, 0x5500 + 0x03) }, /* @ddr. 3 */ 60 /* k8061 */ 61 { USB_DEVICE(0x10cf, 0x8061 + 0x00) }, /* @ddr. 0 */ 62 { USB_DEVICE(0x10cf, 0x8061 + 0x01) }, /* @ddr. 1 */ 63 { USB_DEVICE(0x10cf, 0x8061 + 0x02) }, /* @ddr. 2 */ 64 { USB_DEVICE(0x10cf, 0x8061 + 0x03) }, /* @ddr. 3 */ 65 { USB_DEVICE(0x10cf, 0x8061 + 0x04) }, /* @ddr. 4 */ 66 { USB_DEVICE(0x10cf, 0x8061 + 0x05) }, /* @ddr. 5 */ 67 { USB_DEVICE(0x10cf, 0x8061 + 0x06) }, /* @ddr. 6 */ 68 { USB_DEVICE(0x10cf, 0x8061 + 0x07) }, /* @ddr. 7 */ 69 { } /* terminating entry */ 70}; 71MODULE_DEVICE_TABLE(usb, vm_id_table); 72 73MODULE_AUTHOR(VMK80XX_MODULE_AUTHOR); 74MODULE_DESCRIPTION(VMK80XX_MODULE_DESC); 75MODULE_SUPPORTED_DEVICE(VMK80XX_MODULE_DEVICE); 76MODULE_VERSION(VMK80XX_MODULE_VERSION); 77MODULE_LICENSE(VMK80XX_MODULE_LICENSE); 78/* ------------------------------------------------------------------------ */ 79 80#define CONFIG_VMK80XX_DEBUG 81 82//#undef CONFIG_COMEDI_DEBUG /* Uncommend this line to disable comedi debug */ 83#undef CONFIG_VMK80XX_DEBUG /* Commend this line to enable vmk80xx debug */ 84 85#ifdef CONFIG_COMEDI_DEBUG 86 static int cm_dbg = 1; 87#else /* !CONFIG_COMEDI_DEBUG */ 88 static int cm_dbg = 0; 89#endif /* !CONFIG_COMEDI_DEBUG */ 90 91#ifdef CONFIG_VMK80XX_DEBUG 92 static int vm_dbg = 1; 93#else /* !CONFIG_VMK80XX_DEBUG */ 94 static int vm_dbg = 0; 95#endif /* !CONFIG_VMK80XX_DEBUG */ 96 97/* Define our own debug macros */ 98#define DBGCM(fmt, arg...) do { if (cm_dbg) printk(fmt, ##arg); } while (0) 99#define DBGVM(fmt, arg...) do { if (vm_dbg) printk(fmt, ##arg); } while (0) 100 101/* Velleman K8055 specific stuff */ 102#define VMK8055_DI 0 /* digital input offset */ 103#define VMK8055_DO 1 /* digital output offset */ 104#define VMK8055_AO1 2 /* analog output channel 1 offset */ 105#define VMK8055_AO2 3 /* analog output channel 2 offset */ 106#define VMK8055_CNT1 4 /* counter 1 offset */ 107#define VMK8055_CNT2 6 /* counter 2 offset */ 108#define VMK8055_CMD_RST 0x00 /* reset device registers */ 109#define VMK8055_CMD_DEB1 0x01 /* debounce time for pulse counter 1 */ 110#define VMK8055_CMD_DEB2 0x02 /* debounce time for pulse counter 2 */ 111#define VMK8055_CMD_RST_CNT1 0x03 /* reset pulse counter 1 */ 112#define VMK8055_CMD_RST_CNT2 0x04 /* reset pulse counter 2 */ 113#define VMK8055_CMD_AD 0x05 /* write to analog or digital channel */ 114#define VMK8055_EP_OUT 0x01 /* out endpoint address */ 115#define VMK8055_EP_IN 0x81 /* in endpoint address */ 116#define VMK8055_EP_SIZE 8 /* endpoint max packet size */ 117#define VMK8055_EP_INTERVAL 20 /* general conversion time per command */ 118#define VMK8055_MAX_BOARDS 16 119 120/* Structure to hold all of our device specific stuff */ 121struct vmk80xx_usb { 122 struct usb_interface *intf; 123 struct semaphore limit_sem; 124 wait_queue_head_t read_wait; 125 wait_queue_head_t write_wait; 126 size_t irq_out_endpoint_size; 127 __u8 irq_out_endpoint; 128 int irq_out_interval; 129 unsigned char *irq_out_buf; 130 struct urb *irq_out_urb; 131 int irq_out_busy; 132 size_t irq_in_endpoint_size; 133 __u8 irq_in_endpoint; 134 int irq_in_interval; 135 unsigned char *irq_in_buf; 136 struct urb *irq_in_urb; 137 int irq_in_busy; 138 int irq_in_running; 139 int probed; 140 int attached; 141 int id; 142}; 143 144static struct vmk80xx_usb vm_boards[VMK8055_MAX_BOARDS]; 145 146/* --------------------------------------------------------------------------- 147 * Abort active transfers and tidy up allocated resources. 148--------------------------------------------------------------------------- */ 149static void vm_abort_transfers(struct vmk80xx_usb *vm) 150{ 151 DBGVM("comedi#: vmk80xx: %s\n", __func__); 152 153 if (vm->irq_in_running) { 154 vm->irq_in_running = 0; 155 if (vm->intf) 156 usb_kill_urb(vm->irq_in_urb); 157 } 158 159 if (vm->irq_out_busy && vm->intf) 160 usb_kill_urb(vm->irq_out_urb); 161} 162 163static void vm_delete(struct vmk80xx_usb *vm) 164{ 165 DBGVM("comedi#: vmk80xx: %s\n", __func__); 166 167 vm_abort_transfers(vm); 168 169 /* Deallocate usb urbs and kernel buffers */ 170 if (vm->irq_in_urb) 171 usb_free_urb(vm->irq_in_urb); 172 173 if (vm->irq_out_urb); 174 usb_free_urb(vm->irq_out_urb); 175 176 if (vm->irq_in_buf) 177 kfree(vm->irq_in_buf); 178 179 if (vm->irq_out_buf) 180 kfree(vm->irq_out_buf); 181} 182 183/* --------------------------------------------------------------------------- 184 * Interrupt in and interrupt out callback for usb data transfer. 185--------------------------------------------------------------------------- */ 186static void vm_irq_in_callback(struct urb *urb) 187{ 188 struct vmk80xx_usb *vm = (struct vmk80xx_usb *)urb->context; 189 int err; 190 191 DBGVM("comedi#: vmk80xx: %s\n", __func__); 192 193 switch (urb->status) { 194 case 0: /* success */ 195 break; 196 case -ENOENT: 197 case -ECONNRESET: 198 case -ESHUTDOWN: 199 break; 200 default: 201 DBGCM("comedi#: vmk80xx: %s - nonzero urb status (%d)\n", 202 __func__, urb->status); 203 goto resubmit; /* maybe we can recover */ 204 } 205 206 goto exit; 207resubmit: 208 if (vm->irq_in_running && vm->intf) { 209 err = usb_submit_urb(vm->irq_in_urb, GFP_ATOMIC); 210 if (!err) goto exit; 211 /* FALL THROUGH */ 212 DBGCM("comedi#: vmk80xx: %s - submit urb failed (err# %d)\n", 213 __func__, err); 214 } 215exit: 216 vm->irq_in_busy = 0; 217 218 /* interrupt-in pipe is available again */ 219 wake_up_interruptible(&vm->read_wait); 220} 221 222static void vm_irq_out_callback(struct urb *urb) 223{ 224 struct vmk80xx_usb *vm; 225 226 DBGVM("comedi#: vmk80xx: %s\n", __func__); 227 228 /* sync/async unlink (hardware going away) faults aren't errors */ 229 if (urb->status && !(urb->status == -ENOENT 230 || urb->status == -ECONNRESET 231 || urb->status == -ESHUTDOWN)) 232 DBGCM("comedi#: vmk80xx: %s - nonzero urb status (%d)\n", 233 __func__, urb->status); 234 235 vm = (struct vmk80xx_usb *)urb->context; 236 vm->irq_out_busy = 0; 237 238 /* interrupt-out pipe is available again */ 239 wake_up_interruptible(&vm->write_wait); 240} 241 242/* --------------------------------------------------------------------------- 243 * Interface for digital/analog input/output and counter funcs (see below). 244--------------------------------------------------------------------------- */ 245static int vm_read(struct vmk80xx_usb *vm) 246{ 247 struct usb_device *udev; 248 int retval = -ENODEV; 249 250 DBGVM("comedi#: vmk80xx: %s\n", __func__); 251 252 /* Verify that the device wasn't un-plugged */ 253 if (!vm->intf) { 254 DBGCM("comedi#: vmk80xx: %s - No dev or dev un-plugged\n", 255 __func__); 256 goto exit; 257 } 258 259 if (vm->irq_in_busy) { 260 retval = wait_event_interruptible(vm->read_wait, 261 !vm->irq_in_busy); 262 if (retval < 0) { /* we were interrupted by a signal */ 263 retval = -ERESTART; 264 goto exit; 265 } 266 } 267 268 udev = interface_to_usbdev(vm->intf); 269 270 /* Fill the urb and send off */ 271 usb_fill_int_urb(vm->irq_in_urb, 272 udev, 273 usb_rcvintpipe(udev, vm->irq_in_endpoint), 274 vm->irq_in_buf, 275 vm->irq_in_endpoint_size, 276 vm_irq_in_callback, 277 vm, 278 vm->irq_in_interval); 279 280 vm->irq_in_running = 1; 281 vm->irq_in_busy = 1; /* disallow following read request's */ 282 283 retval = usb_submit_urb(vm->irq_in_urb, GFP_KERNEL); 284 if (!retval) goto exit; /* success */ 285 /* FALL TROUGH */ 286 vm->irq_in_running = 0; 287 DBGCM("comedi#: vmk80xx: %s - submit urb failed (err# %d)\n", 288 __func__, retval); 289 290exit: 291 return retval; 292} 293 294static int vm_write(struct vmk80xx_usb *vm, unsigned char cmd) 295{ 296 struct usb_device *udev; 297 int retval = -ENODEV; 298 299 DBGVM("comedi#: vmk80xx: %s\n", __func__); 300 301 /* Verify that the device wasn't un-plugged */ 302 if (!vm->intf) { 303 DBGCM("comedi#: vmk80xx: %s - No dev or dev un-plugged\n", 304 __func__); 305 goto exit; 306 } 307 308 if (vm->irq_out_busy) { 309 retval = wait_event_interruptible(vm->write_wait, 310 !vm->irq_out_busy); 311 if (retval < 0) { /* we were interrupted by a signal */ 312 retval = -ERESTART; 313 goto exit; 314 } 315 } 316 317 udev = interface_to_usbdev(vm->intf); 318 319 /* Set the command which should send to the device */ 320 vm->irq_out_buf[0] = cmd; 321 322 /* Fill the urb and send off */ 323 usb_fill_int_urb(vm->irq_out_urb, 324 udev, 325 usb_sndintpipe(udev, vm->irq_out_endpoint), 326 vm->irq_out_buf, 327 vm->irq_out_endpoint_size, 328 vm_irq_out_callback, 329 vm, 330 vm->irq_out_interval); 331 332 vm->irq_out_busy = 1; /* disallow following write request's */ 333 334 wmb(); 335 336 retval = usb_submit_urb(vm->irq_out_urb, GFP_KERNEL); 337 if (!retval) goto exit; /* success */ 338 /* FALL THROUGH */ 339 vm->irq_out_busy = 0; 340 DBGCM("comedi#: vmk80xx: %s - submit urb failed (err# %d)\n", 341 __func__, retval); 342 343exit: 344 return retval; 345} 346 347/* --------------------------------------------------------------------------- 348 * COMEDI-Interface (callback functions for the userspacs apps). 349--------------------------------------------------------------------------- */ 350static int vm_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 351 struct comedi_insn *insn, unsigned int *data) 352{ 353 struct vmk80xx_usb *vm; 354 int minor = dev->minor; 355 int ch, ch_offs, i; 356 int retval = -EFAULT; 357 358 DBGVM("comedi%d: vmk80xx: %s\n", minor, __func__); 359 360 if (!(vm = (struct vmk80xx_usb *)dev->private)) 361 return retval; 362 363 down(&vm->limit_sem); 364 365 /* We have an attached board ? */ 366 if (!vm->probed) { 367 retval = -ENODEV; 368 goto error; 369 } 370 371 /* interrupt-in pipe busy ? */ 372 if (vm->irq_in_busy) { 373 retval = -EBUSY; 374 goto error; 375 } 376 377 ch = CR_CHAN(insn->chanspec); 378 ch_offs = (!ch) ? VMK8055_AO1 : VMK8055_AO2; 379 380 for (i = 0; i < insn->n; i++) { 381 retval = vm_read(vm); 382 if (retval) 383 goto error; 384 385 /* NOTE: 386 * The input voltage of the selected 8-bit AD channel 387 * is converted to a value which lies between 388 * 0 and 255. 389 */ 390 data[i] = vm->irq_in_buf[ch_offs]; 391 } 392 393 up(&vm->limit_sem); 394 395 /* Return the number of samples read */ 396 return i; 397error: 398 up(&vm->limit_sem); 399 400 return retval; 401} 402 403static int vm_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, 404 struct comedi_insn *insn, unsigned int *data) 405{ 406 struct vmk80xx_usb *vm; 407 int minor = dev->minor; 408 int ch, ch_offs, i; 409 int retval = -EFAULT; 410 411 DBGVM("comedi%d: vmk80xx: %s\n", minor, __func__); 412 413 if (!(vm = (struct vmk80xx_usb *)dev->private)) 414 return retval; 415 416 down(&vm->limit_sem); 417 418 /* We have an attached board ? */ 419 if (!vm->probed) { 420 retval = -ENODEV; 421 goto error; 422 } 423 424 /* interrupt-out pipe busy ? */ 425 if (vm->irq_out_busy) { 426 retval = -EBUSY; 427 goto error; 428 } 429 430 ch = CR_CHAN(insn->chanspec); 431 ch_offs = (!ch) ? VMK8055_AO1 : VMK8055_AO2; 432 433 for (i = 0; i < insn->n; i++) { 434 /* NOTE: 435 * The indicated 8-bit DA channel is altered according 436 * to the new data. This means that the data corresponds 437 * to a specific voltage. The value 0 corresponds to a 438 * minimum output voltage (+-0 Volt) and the value 255 439 * corresponds to a maximum output voltage (+5 Volt). 440 */ 441 vm->irq_out_buf[ch_offs] = data[i]; 442 443 retval = vm_write(vm, VMK8055_CMD_AD); 444 if (retval) 445 goto error; 446 } 447 448 up(&vm->limit_sem); 449 450 /* Return the number of samples write */ 451 return i; 452error: 453 up(&vm->limit_sem); 454 455 return retval; 456} 457 458static int vm_di_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 459 struct comedi_insn *insn, unsigned int *data) 460{ 461 struct vmk80xx_usb *vm; 462 int minor = dev->minor; 463 int ch, i, inp; 464 int retval = -EFAULT; 465 466 DBGVM("comedi%d: vmk80xx: %s\n", minor, __func__); 467 468 if (!(vm = (struct vmk80xx_usb *)dev->private)) 469 return retval; 470 471 down(&vm->limit_sem); 472 473 /* We have an attached board ? */ 474 if (!vm->probed) { 475 retval = -ENODEV; 476 goto error; 477 } 478 479 /* interrupt-in pipe busy ? */ 480 if (vm->irq_in_busy) { 481 retval = -EBUSY; 482 goto error; 483 } 484 485 for (i = 0, ch = CR_CHAN(insn->chanspec); i < insn->n; i++) { 486 retval = vm_read(vm); 487 if (retval) 488 goto error; 489 490 /* NOTE: 491 * The status of the selected digital input channel is read. 492 */ 493 inp = (((vm->irq_in_buf[VMK8055_DI] >> 4) & 0x03) | 494 ((vm->irq_in_buf[VMK8055_DI] << 2) & 0x04) | 495 ((vm->irq_in_buf[VMK8055_DI] >> 3) & 0x18)); 496 data[i] = ((inp & (1 << ch)) > 0); 497 } 498 499 up(&vm->limit_sem); 500 501 return i; 502error: 503 up(&vm->limit_sem); 504 505 return retval; 506} 507 508static int vm_do_winsn(struct comedi_device *dev, struct comedi_subdevice *s, 509 struct comedi_insn *insn, unsigned int *data) 510{ 511 struct vmk80xx_usb *vm; 512 int minor = dev->minor; 513 int ch, i, mask; 514 int retval = -EFAULT; 515 516 DBGVM("comedi%d: vmk80xx: %s\n", minor, __func__); 517 518 if (!(vm = (struct vmk80xx_usb *)dev->private)) 519 return retval; 520 521 down(&vm->limit_sem); 522 523 /* We have an attached board ? */ 524 if (!vm->probed) { 525 retval = -ENODEV; 526 goto error; 527 } 528 529 /* interrupt-out pipe busy ? */ 530 if (vm->irq_out_busy) { 531 retval = -EBUSY; 532 goto error; 533 } 534 535 for (i = 0, ch = CR_CHAN(insn->chanspec); i < insn->n; i++) { 536 /* NOTE: 537 * The selected digital output channel is set or cleared. 538 */ 539 mask = (data[i] == 1) 540 ? vm->irq_out_buf[VMK8055_DO] | (1 << ch) 541 : vm->irq_out_buf[VMK8055_DO] ^ (1 << ch); 542 543 vm->irq_out_buf[VMK8055_DO] = mask; 544 545 retval = vm_write(vm, VMK8055_CMD_AD); 546 if (retval) 547 goto error; 548 } 549 550 up(&vm->limit_sem); 551 552 return i; 553error: 554 up(&vm->limit_sem); 555 556 return retval; 557} 558 559static int vm_cnt_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 560 struct comedi_insn *insn, unsigned int *data) 561{ 562 struct vmk80xx_usb *vm; 563 int minor = dev->minor; 564 int cnt, cnt_offs, i; 565 int retval = -EFAULT; 566 567 DBGVM("comedi%d: vmk80xx: %s\n", minor, __func__); 568 569 if (!(vm = (struct vmk80xx_usb *)dev->private)) 570 return retval; 571 572 down(&vm->limit_sem); 573 574 /* We have an attached board ? */ 575 if (!vm->probed) { 576 retval = -ENODEV; 577 goto error; 578 } 579 580 /* interrupt-in pipe busy ? */ 581 if (vm->irq_in_busy) { 582 retval = -EBUSY; 583 goto error; 584 } 585 586 cnt = CR_CHAN(insn->chanspec); 587 cnt_offs = (!cnt) ? VMK8055_CNT1 : VMK8055_CNT2; 588 589 for (i = 0; i < insn->n; i++) { 590 retval = vm_read(vm); 591 if (retval) 592 goto error; 593 594 /* NOTE: 595 * The status of the selected 16-bit pulse counter is 596 * read. The counter # 1 counts the pulses fed to the 597 * input Inp1 and the counter # 2 counts the pulses fed 598 * to the input Inp2. 599 */ 600 data[i] = vm->irq_in_buf[cnt_offs]; 601 } 602 603 up(&vm->limit_sem); 604 605 return i; 606error: 607 up(&vm->limit_sem); 608 609 return retval; 610} 611 612static int vm_cnt_winsn(struct comedi_device *dev, struct comedi_subdevice *s, 613 struct comedi_insn *insn, unsigned int *data) 614{ 615 struct vmk80xx_usb *vm; 616 int minor = dev->minor; 617 int cnt, cnt_offs, cmd, i; 618 int retval = -EFAULT; 619 620 DBGVM("comedi%d: vmk80xx: %s\n", minor, __func__); 621 622 if (!(vm = (struct vmk80xx_usb *)dev->private)) 623 return retval; 624 625 down(&vm->limit_sem); 626 627 /* We have an attached board ? */ 628 if (!vm->probed) { 629 retval = -ENODEV; 630 goto error; 631 } 632 633 /* interrupt-out pipe busy ? */ 634 if (vm->irq_out_busy) { 635 retval = -EBUSY; 636 goto error; 637 } 638 639 cnt = CR_CHAN(insn->chanspec); 640 cnt_offs = (!cnt) ? VMK8055_CNT1 : VMK8055_CNT2; 641 cmd = (!cnt) ? VMK8055_CMD_RST_CNT1 : VMK8055_CMD_RST_CNT2; 642 643 for (i = 0; i < insn->n; i++) { 644 /* NOTE: 645 * The selected 16-bit pulse counter is reset. 646 */ 647 vm->irq_out_buf[cnt_offs] = 0x00; 648 649 retval = vm_write(vm, cmd); 650 if (retval) 651 goto error; 652 } 653 654 up(&vm->limit_sem); 655 656 return i; 657error: 658 up(&vm->limit_sem); 659 660 return retval; 661} 662 663static int vm_cnt_cinsn(struct comedi_device *dev, struct comedi_subdevice *s, 664 struct comedi_insn *insn, unsigned int *data) 665{ 666 struct vmk80xx_usb *vm; 667 int minor = dev->minor; 668 int cnt, cmd, i; 669 unsigned int debtime, val; 670 int retval = -EFAULT; 671 672 DBGVM("comedi%d: vmk80xx: %s\n", minor, __func__); 673 674 if (!(vm = (struct vmk80xx_usb *)dev->private)) 675 return retval; 676 677 down(&vm->limit_sem); 678 679 /* We have an attached board ? */ 680 if (!vm->probed) { 681 retval = -ENODEV; 682 goto error; 683 } 684 685 /* interrupt-out pipe busy ? */ 686 if (vm->irq_out_busy) { 687 retval = -EBUSY; 688 goto error; 689 } 690 691 cnt = CR_CHAN(insn->chanspec); 692 cmd = (!cnt) ? VMK8055_CMD_DEB1 : VMK8055_CMD_DEB2; 693 694 /* NOTE: 695 * The counter inputs are debounced in the software to prevent 696 * false triggering when mechanical switches or relay inputs 697 * are used. The debounce time is equal for both falling and 698 * rising edges. The default debounce time is 2ms. This means 699 * the counter input must be stable for at least 2ms before it 700 * is recognised , giving the maximum count rate of about 200 701 * counts per second. If the debounce time is set to 0, then 702 * the maximum counting rate is about 2000 counts per second. 703 */ 704 for (i = 0; i < insn->n; i++) { 705 debtime = data[i]; 706 if (debtime == 0) 707 debtime = 1; 708 /* -------------------------------------------------- 709 * From libk8055.c 710 * --------------- 711 * Copyleft (C) 2005 by Sven Lindberg; 712 * Copyright (C) 2007 by Pjetur G. Hjaltason: 713 * By testing and measuring on the other hand I found 714 * the formula dbt=0.115*x^2......... 715 * 716 * I'm using here an adapted formula to avoid floating 717 * point operations inside the kernel. The time set 718 * with this formula is within +-4% +- 1. 719 * ------------------------------------------------ */ 720 val = int_sqrt(debtime * 1000 / 115); 721 if (((val + 1) * val) < debtime * 1000 / 115) 722 val += 1; 723 724 vm->irq_out_buf[cnt+6] = val; 725 726 retval = vm_write(vm, cmd); 727 if (retval) 728 goto error; 729 } 730 731 up(&vm->limit_sem); 732 733 return i; 734error: 735 up(&vm->limit_sem); 736 737 return retval; 738} 739 740/* Comedi subdevice offsets */ 741#define VMK8055_SUBD_AI_OFFSET 0 742#define VMK8055_SUBD_AO_OFFSET 1 743#define VMK8055_SUBD_DI_OFFSET 2 744#define VMK8055_SUBD_DO_OFFSET 3 745#define VMK8055_SUBD_CT_OFFSET 4 746 747static DEFINE_MUTEX(glb_mutex); 748 749/* --------------------------------------------------------------------------- 750 * Hook-up (or deallocate) the virtual device file '/dev/comedi[minor]' with 751 * the vmk80xx driver (comedi_config/rmmod). 752--------------------------------------------------------------------------- */ 753static int vm_attach(struct comedi_device *dev, struct comedi_devconfig *it) 754{ 755 struct comedi_subdevice *s; 756 int minor = dev->minor; 757 int idx, i; 758 759 DBGVM("comedi%d: vmk80xx: %s\n", minor, __func__); 760 761 mutex_lock(&glb_mutex); 762 763 /* Prepare user info... */ 764 printk("comedi%d: vmk80xx: ", minor); 765 766 idx = -1; 767 768 /* Find the last valid device which has been detected 769 * by the probe function */; 770 for (i = 0; i < VMK8055_MAX_BOARDS; i++) 771 if (vm_boards[i].probed && !vm_boards[i].attached) { 772 idx = i; 773 break; 774 } 775 776 if (idx == -1) { 777 printk("no boards attached\n"); 778 mutex_unlock(&glb_mutex); 779 return -ENODEV; 780 } 781 782 down(&vm_boards[idx].limit_sem); 783 784 /* OK, at that time we've an attached board and this is 785 * the first execution of the comedi_config command for 786 * this board */ 787 printk("board #%d is attached to comedi\n", vm_boards[idx].id); 788 789 dev->board_name = "vmk80xx"; 790 dev->private = vm_boards + idx; /* will be allocated in vm_probe */ 791 792 /* Subdevices section -> set properties */ 793 if (alloc_subdevices(dev, 5) < 0) { 794 printk("comedi%d: vmk80xx: couldn't allocate subdevs\n", 795 minor); 796 up(&vm_boards[idx].limit_sem); 797 mutex_unlock(&glb_mutex); 798 return -ENOMEM; 799 } 800 801 s = dev->subdevices + VMK8055_SUBD_AI_OFFSET; 802 s->type = COMEDI_SUBD_AI; 803 s->subdev_flags = SDF_READABLE | SDF_GROUND; 804 s->n_chan = 2; 805 s->maxdata = 0xff; /* +5 Volt */ 806 s->range_table = &range_unipolar5; /* +-0 Volt - +5 Volt */ 807 s->insn_read = vm_ai_rinsn; 808 809 s = dev->subdevices + VMK8055_SUBD_AO_OFFSET; 810 s->type = COMEDI_SUBD_AO; 811 s->subdev_flags = SDF_WRITEABLE | SDF_GROUND; 812 s->n_chan = 2; 813 s->maxdata = 0xff; 814 s->range_table = &range_unipolar5; 815 s->insn_write = vm_ao_winsn; 816 817 s = dev->subdevices + VMK8055_SUBD_DI_OFFSET; 818 s->type = COMEDI_SUBD_DI; 819 s->subdev_flags = SDF_READABLE | SDF_GROUND; 820 s->n_chan = 5; 821 s->insn_read = vm_di_rinsn; 822 823 s = dev->subdevices + VMK8055_SUBD_DO_OFFSET; 824 s->type = COMEDI_SUBD_DO; 825 s->subdev_flags = SDF_WRITEABLE | SDF_GROUND; 826 s->n_chan = 8; 827 s->maxdata = 1; 828 s->insn_write = vm_do_winsn; 829 830 s = dev->subdevices + VMK8055_SUBD_CT_OFFSET; 831 s->type = COMEDI_SUBD_COUNTER; 832 s->subdev_flags = SDF_READABLE | SDF_WRITEABLE; 833 s->n_chan = 2; 834 s->insn_read = vm_cnt_rinsn; 835 s->insn_write = vm_cnt_winsn; /* accept only a channel # as arg */ 836 s->insn_config = vm_cnt_cinsn; 837 838 /* Register the comedi board connection */ 839 vm_boards[idx].attached = 1; 840 841 up(&vm_boards[idx].limit_sem); 842 843 mutex_unlock(&glb_mutex); 844 845 return 0; 846} 847 848static int vm_detach(struct comedi_device *dev) 849{ 850 struct vmk80xx_usb *vm; 851 int minor = dev->minor; 852 853 DBGVM("comedi%d: vmk80xx: %s\n", minor, __func__); 854 855 if (!dev) { /* FIXME: I don't know if i need that here */ 856 printk("comedi%d: vmk80xx: %s - dev is NULL\n", 857 minor, __func__); 858 return -EFAULT; 859 } 860 861 if (!(vm = (struct vmk80xx_usb *)dev->private)) { 862 printk("comedi%d: vmk80xx: %s - dev->private is NULL\n", 863 minor, __func__); 864 return -EFAULT; 865 } 866 867 /* NOTE: dev->private and dev->subdevices are deallocated 868 * automatically by the comedi core */ 869 870 down(&vm->limit_sem); 871 872 dev->private = NULL; 873 vm->attached = 0; 874 875 printk("comedi%d: vmk80xx: board #%d removed from comedi core\n", 876 minor, vm->id); 877 878 up(&vm->limit_sem); 879 880 return 0; 881} 882 883/* --------------------------------------------------------------------------- 884 * Hook-up or remove the Velleman board from the usb. 885--------------------------------------------------------------------------- */ 886static int vm_probe(struct usb_interface *itf, const struct usb_device_id *id) 887{ 888 struct usb_device *udev; 889 int idx, i; 890 u16 product_id; 891 int retval = -ENOMEM; 892 893 DBGVM("comedi#: vmk80xx: %s\n", __func__); 894 895 mutex_lock(&glb_mutex); 896 897 udev = interface_to_usbdev(itf); 898 899 idx = -1; 900 901 /* TODO: k8061 only theoretically supported yet */ 902 product_id = le16_to_cpu(udev->descriptor.idProduct); 903 if (product_id == 0x8061) { 904 printk("comedi#: vmk80xx: Velleman K8061 detected " 905 "(no COMEDI support available yet)\n"); 906 mutex_unlock(&glb_mutex); 907 return -ENODEV; 908 } 909 910 /* Look for a free place to put the board into the array */ 911 for (i = 0; i < VMK8055_MAX_BOARDS; i++) { 912 if (!vm_boards[i].probed) { 913 idx = i; 914 i = VMK8055_MAX_BOARDS; 915 } 916 } 917 918 if (idx == -1) { 919 printk("comedi#: vmk80xx: only FOUR boards supported\n"); 920 mutex_unlock(&glb_mutex); 921 return -EMFILE; 922 } 923 924 /* Initialize device states (hard coded) */ 925 vm_boards[idx].intf = itf; 926 927 /* interrupt-in context */ 928 vm_boards[idx].irq_in_endpoint = VMK8055_EP_IN; 929 vm_boards[idx].irq_in_interval = VMK8055_EP_INTERVAL; 930 vm_boards[idx].irq_in_endpoint_size = VMK8055_EP_SIZE; 931 vm_boards[idx].irq_in_buf = kmalloc(VMK8055_EP_SIZE, GFP_KERNEL); 932 if (!vm_boards[idx].irq_in_buf) { 933 err("comedi#: vmk80xx: couldn't alloc irq_in_buf\n"); 934 goto error; 935 } 936 937 /* interrupt-out context */ 938 vm_boards[idx].irq_out_endpoint = VMK8055_EP_OUT; 939 vm_boards[idx].irq_out_interval = VMK8055_EP_INTERVAL; 940 vm_boards[idx].irq_out_endpoint_size = VMK8055_EP_SIZE; 941 vm_boards[idx].irq_out_buf = kmalloc(VMK8055_EP_SIZE, GFP_KERNEL); 942 if (!vm_boards[idx].irq_out_buf) { 943 err("comedi#: vmk80xx: couldn't alloc irq_out_buf\n"); 944 goto error; 945 } 946 947 /* Endpoints located ? */ 948 if (!vm_boards[idx].irq_in_endpoint) { 949 err("comedi#: vmk80xx: int-in endpoint not found\n"); 950 goto error; 951 } 952 953 if (!vm_boards[idx].irq_out_endpoint) { 954 err("comedi#: vmk80xx: int-out endpoint not found\n"); 955 goto error; 956 } 957 958 /* Try to allocate in/out urbs */ 959 vm_boards[idx].irq_in_urb = usb_alloc_urb(0, GFP_KERNEL); 960 if (!vm_boards[idx].irq_in_urb) { 961 err("comedi#: vmk80xx: couldn't alloc irq_in_urb\n"); 962 goto error; 963 } 964 965 vm_boards[idx].irq_out_urb = usb_alloc_urb(0, GFP_KERNEL); 966 if (!vm_boards[idx].irq_out_urb) { 967 err("comedi#: vmk80xx: couldn't alloc irq_out_urb\n"); 968 goto error; 969 } 970 971 /* Reset the device */ 972 vm_boards[idx].irq_out_buf[0] = VMK8055_CMD_RST; 973 vm_boards[idx].irq_out_buf[1] = 0x00; 974 vm_boards[idx].irq_out_buf[2] = 0x00; 975 vm_boards[idx].irq_out_buf[3] = 0x00; 976 vm_boards[idx].irq_out_buf[4] = 0x00; 977 vm_boards[idx].irq_out_buf[5] = 0x00; 978 vm_boards[idx].irq_out_buf[6] = 0x00; 979 vm_boards[idx].irq_out_buf[7] = 0x00; 980 981 usb_fill_int_urb(vm_boards[idx].irq_out_urb, 982 udev, 983 usb_sndintpipe(udev, 984 vm_boards[idx].irq_out_endpoint), 985 vm_boards[idx].irq_out_buf, 986 vm_boards[idx].irq_out_endpoint_size, 987 vm_irq_out_callback, 988 &vm_boards[idx], 989 vm_boards[idx].irq_out_interval); 990 991 retval = usb_submit_urb(vm_boards[idx].irq_out_urb, GFP_KERNEL); 992 if (retval) 993 DBGCM("comedi#: vmk80xx: device reset failed (err #%d)\n", 994 retval); 995 else 996 DBGCM("comedi#: vmk80xx: device reset success\n"); 997 998 999 usb_set_intfdata(itf, &vm_boards[idx]); 1000 1001 /* Show some debugging messages if required */ 1002 DBGCM("comedi#: vmk80xx: [<-] ep addr 0x%02x size %d interval %d\n", 1003 vm_boards[idx].irq_in_endpoint, 1004 vm_boards[idx].irq_in_endpoint_size, 1005 vm_boards[idx].irq_in_interval); 1006 DBGCM("comedi#: vmk80xx: [->] ep addr 0x%02x size %d interval %d\n", 1007 vm_boards[idx].irq_out_endpoint, 1008 vm_boards[idx].irq_out_endpoint_size, 1009 vm_boards[idx].irq_out_interval); 1010 1011 vm_boards[idx].id = idx; 1012 1013 /* Let the user know that the device is now attached */ 1014 printk("comedi#: vmk80xx: K8055 board #%d now attached\n", 1015 vm_boards[idx].id); 1016 1017 /* We have an attached velleman board */ 1018 vm_boards[idx].probed = 1; 1019 1020 mutex_unlock(&glb_mutex); 1021 1022 return retval; 1023error: 1024 vm_delete(&vm_boards[idx]); 1025 1026 mutex_unlock(&glb_mutex); 1027 1028 return retval; 1029} 1030 1031static void vm_disconnect(struct usb_interface *intf) 1032{ 1033 struct vmk80xx_usb *vm; 1034 1035 DBGVM("comedi#: vmk80xx: %s\n", __func__); 1036 1037 vm = (struct vmk80xx_usb *)usb_get_intfdata(intf); 1038 if (!vm) { 1039 printk("comedi#: vmk80xx: %s - vm is NULL\n", __func__); 1040 return; /* -EFAULT */ 1041 } 1042 1043 mutex_lock(&glb_mutex); 1044 /* Twill be needed if the driver supports more than one board */ 1045 down(&vm->limit_sem); 1046 1047 vm->probed = 0; /* we have -1 attached boards */ 1048 usb_set_intfdata(vm->intf, NULL); 1049 1050 vm_delete(vm); /* tidy up */ 1051 1052 /* Twill be needed if the driver supports more than one board */ 1053 up(&vm->limit_sem); 1054 mutex_unlock(&glb_mutex); 1055 1056 printk("comedi#: vmk80xx: Velleman board #%d now detached\n", 1057 vm->id); 1058} 1059 1060/* --------------------------------------------------------------------------- 1061 * Register/Deregister this driver with/from the usb subsystem and the comedi. 1062--------------------------------------------------------------------------- */ 1063static struct usb_driver vm_driver = { 1064 .name = "vmk80xx", 1065 .probe = vm_probe, 1066 .disconnect = vm_disconnect, 1067 .id_table = vm_id_table, 1068}; 1069 1070static struct comedi_driver driver_vm = { 1071 .module = THIS_MODULE, 1072 .driver_name = "vmk80xx", 1073 .attach = vm_attach, 1074 .detach = vm_detach, 1075}; 1076 1077static int __init vm_init(void) 1078{ 1079 int retval, idx; 1080 1081 printk("vmk80xx: version " VMK80XX_MODULE_VERSION " -" 1082 " Manuel Gebele <forensixs@gmx.de>\n"); 1083 1084 for (idx = 0; idx < VMK8055_MAX_BOARDS; idx++) { 1085 memset(&vm_boards[idx], 0x00, sizeof(vm_boards[idx])); 1086 init_MUTEX(&vm_boards[idx].limit_sem); 1087 init_waitqueue_head(&vm_boards[idx].read_wait); 1088 init_waitqueue_head(&vm_boards[idx].write_wait); 1089 } 1090 1091 /* Register with the usb subsystem */ 1092 retval = usb_register(&vm_driver); 1093 if (retval) { 1094 err("vmk80xx: usb subsystem registration failed (err #%d)\n", 1095 retval); 1096 return retval; 1097 } 1098 1099 /* Register with the comedi core */ 1100 retval = comedi_driver_register(&driver_vm); 1101 if (retval) { 1102 err("vmk80xx: comedi core registration failed (err #%d)\n", 1103 retval); 1104 usb_deregister(&vm_driver); 1105 } 1106 1107 return retval; 1108} 1109 1110static void __exit vm_exit(void) 1111{ 1112 comedi_driver_unregister(&driver_vm); 1113 usb_deregister(&vm_driver); 1114} 1115module_init(vm_init); 1116module_exit(vm_exit); 1117