1/* 2 * lirc_sasem.c - USB remote support for LIRC 3 * Version 0.5 4 * 5 * Copyright (C) 2004-2005 Oliver Stabel <oliver.stabel@gmx.de> 6 * Tim Davies <tim@opensystems.net.au> 7 * 8 * This driver was derived from: 9 * Venky Raju <dev@venky.ws> 10 * "lirc_imon - "LIRC/VFD driver for Ahanix/Soundgraph IMON IR/VFD" 11 * Paul Miller <pmiller9@users.sourceforge.net>'s 2003-2004 12 * "lirc_atiusb - USB remote support for LIRC" 13 * Culver Consulting Services <henry@culcon.com>'s 2003 14 * "Sasem OnAir VFD/IR USB driver" 15 * 16 * 17 * NOTE - The LCDproc iMon driver should work with this module. More info at 18 * http://www.frogstorm.info/sasem 19 */ 20 21/* 22 * This program is free software; you can redistribute it and/or modify 23 * it under the terms of the GNU General Public License as published by 24 * the Free Software Foundation; either version 2 of the License, or 25 * (at your option) any later version. 26 * 27 * This program is distributed in the hope that it will be useful, 28 * but WITHOUT ANY WARRANTY; without even the implied warranty of 29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 30 * GNU General Public License for more details. 31 * 32 * You should have received a copy of the GNU General Public License 33 * along with this program; if not, write to the Free Software 34 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 35 */ 36 37#include <linux/errno.h> 38#include <linux/init.h> 39#include <linux/kernel.h> 40#include <linux/module.h> 41#include <linux/slab.h> 42#include <linux/uaccess.h> 43#include <linux/usb.h> 44 45#include <media/lirc.h> 46#include <media/lirc_dev.h> 47 48 49#define MOD_AUTHOR "Oliver Stabel <oliver.stabel@gmx.de>, " \ 50 "Tim Davies <tim@opensystems.net.au>" 51#define MOD_DESC "USB Driver for Sasem Remote Controller V1.1" 52#define MOD_NAME "lirc_sasem" 53#define MOD_VERSION "0.5" 54 55#define VFD_MINOR_BASE 144 /* Same as LCD */ 56#define DEVICE_NAME "lcd%d" 57 58#define BUF_CHUNK_SIZE 8 59#define BUF_SIZE 128 60 61#define IOCTL_LCD_CONTRAST 1 62 63/*** P R O T O T Y P E S ***/ 64 65/* USB Callback prototypes */ 66static int sasem_probe(struct usb_interface *interface, 67 const struct usb_device_id *id); 68static void sasem_disconnect(struct usb_interface *interface); 69static void usb_rx_callback(struct urb *urb); 70static void usb_tx_callback(struct urb *urb); 71 72/* VFD file_operations function prototypes */ 73static int vfd_open(struct inode *inode, struct file *file); 74static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg); 75static int vfd_close(struct inode *inode, struct file *file); 76static ssize_t vfd_write(struct file *file, const char *buf, 77 size_t n_bytes, loff_t *pos); 78 79/* LIRC driver function prototypes */ 80static int ir_open(void *data); 81static void ir_close(void *data); 82 83/* Driver init/exit prototypes */ 84static int __init sasem_init(void); 85static void __exit sasem_exit(void); 86 87/*** G L O B A L S ***/ 88#define SASEM_DATA_BUF_SZ 32 89 90struct sasem_context { 91 92 struct usb_device *dev; 93 int vfd_isopen; /* VFD port has been opened */ 94 unsigned int vfd_contrast; /* VFD contrast */ 95 int ir_isopen; /* IR port has been opened */ 96 int dev_present; /* USB device presence */ 97 struct mutex ctx_lock; /* to lock this object */ 98 wait_queue_head_t remove_ok; /* For unexpected USB disconnects */ 99 100 struct lirc_driver *driver; 101 struct usb_endpoint_descriptor *rx_endpoint; 102 struct usb_endpoint_descriptor *tx_endpoint; 103 struct urb *rx_urb; 104 struct urb *tx_urb; 105 unsigned char usb_rx_buf[8]; 106 unsigned char usb_tx_buf[8]; 107 108 struct tx_t { 109 unsigned char data_buf[SASEM_DATA_BUF_SZ]; /* user data buffer */ 110 struct completion finished; /* wait for write to finish */ 111 atomic_t busy; /* write in progress */ 112 int status; /* status of tx completion */ 113 } tx; 114 115 /* for dealing with repeat codes (wish there was a toggle bit!) */ 116 struct timeval presstime; 117 char lastcode[8]; 118 int codesaved; 119}; 120 121/* VFD file operations */ 122static const struct file_operations vfd_fops = { 123 .owner = THIS_MODULE, 124 .open = &vfd_open, 125 .write = &vfd_write, 126 .unlocked_ioctl = &vfd_ioctl, 127 .release = &vfd_close, 128}; 129 130/* USB Device ID for Sasem USB Control Board */ 131static struct usb_device_id sasem_usb_id_table[] = { 132 /* Sasem USB Control Board */ 133 { USB_DEVICE(0x11ba, 0x0101) }, 134 /* Terminating entry */ 135 {} 136}; 137 138/* USB Device data */ 139static struct usb_driver sasem_driver = { 140 .name = MOD_NAME, 141 .probe = sasem_probe, 142 .disconnect = sasem_disconnect, 143 .id_table = sasem_usb_id_table, 144}; 145 146static struct usb_class_driver sasem_class = { 147 .name = DEVICE_NAME, 148 .fops = &vfd_fops, 149 .minor_base = VFD_MINOR_BASE, 150}; 151 152/* to prevent races between open() and disconnect() */ 153static DEFINE_MUTEX(disconnect_lock); 154 155static int debug; 156 157 158/*** M O D U L E C O D E ***/ 159 160MODULE_AUTHOR(MOD_AUTHOR); 161MODULE_DESCRIPTION(MOD_DESC); 162MODULE_LICENSE("GPL"); 163module_param(debug, int, S_IRUGO | S_IWUSR); 164MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)"); 165 166static void delete_context(struct sasem_context *context) 167{ 168 usb_free_urb(context->tx_urb); /* VFD */ 169 usb_free_urb(context->rx_urb); /* IR */ 170 lirc_buffer_free(context->driver->rbuf); 171 kfree(context->driver->rbuf); 172 kfree(context->driver); 173 kfree(context); 174 175 if (debug) 176 printk(KERN_INFO "%s: context deleted\n", __func__); 177} 178 179static void deregister_from_lirc(struct sasem_context *context) 180{ 181 int retval; 182 int minor = context->driver->minor; 183 184 retval = lirc_unregister_driver(minor); 185 if (retval) 186 err("%s: unable to deregister from lirc (%d)", 187 __func__, retval); 188 else 189 printk(KERN_INFO "Deregistered Sasem driver (minor:%d)\n", 190 minor); 191 192} 193 194/** 195 * Called when the VFD device (e.g. /dev/usb/lcd) 196 * is opened by the application. 197 */ 198static int vfd_open(struct inode *inode, struct file *file) 199{ 200 struct usb_interface *interface; 201 struct sasem_context *context = NULL; 202 int subminor; 203 int retval = 0; 204 205 /* prevent races with disconnect */ 206 mutex_lock(&disconnect_lock); 207 208 subminor = iminor(inode); 209 interface = usb_find_interface(&sasem_driver, subminor); 210 if (!interface) { 211 err("%s: could not find interface for minor %d", 212 __func__, subminor); 213 retval = -ENODEV; 214 goto exit; 215 } 216 context = usb_get_intfdata(interface); 217 218 if (!context) { 219 err("%s: no context found for minor %d", 220 __func__, subminor); 221 retval = -ENODEV; 222 goto exit; 223 } 224 225 mutex_lock(&context->ctx_lock); 226 227 if (context->vfd_isopen) { 228 err("%s: VFD port is already open", __func__); 229 retval = -EBUSY; 230 } else { 231 context->vfd_isopen = 1; 232 file->private_data = context; 233 printk(KERN_INFO "VFD port opened\n"); 234 } 235 236 mutex_unlock(&context->ctx_lock); 237 238exit: 239 mutex_unlock(&disconnect_lock); 240 return retval; 241} 242 243/** 244 * Called when the VFD device (e.g. /dev/usb/lcd) 245 * is closed by the application. 246 */ 247static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg) 248{ 249 struct sasem_context *context = NULL; 250 251 context = (struct sasem_context *) file->private_data; 252 253 if (!context) { 254 err("%s: no context for device", __func__); 255 return -ENODEV; 256 } 257 258 mutex_lock(&context->ctx_lock); 259 260 switch (cmd) { 261 case IOCTL_LCD_CONTRAST: 262 if (arg > 1000) 263 arg = 1000; 264 context->vfd_contrast = (unsigned int)arg; 265 break; 266 default: 267 printk(KERN_INFO "Unknown IOCTL command\n"); 268 mutex_unlock(&context->ctx_lock); 269 return -ENOIOCTLCMD; /* not supported */ 270 } 271 272 mutex_unlock(&context->ctx_lock); 273 return 0; 274} 275 276/** 277 * Called when the VFD device (e.g. /dev/usb/lcd) 278 * is closed by the application. 279 */ 280static int vfd_close(struct inode *inode, struct file *file) 281{ 282 struct sasem_context *context = NULL; 283 int retval = 0; 284 285 context = (struct sasem_context *) file->private_data; 286 287 if (!context) { 288 err("%s: no context for device", __func__); 289 return -ENODEV; 290 } 291 292 mutex_lock(&context->ctx_lock); 293 294 if (!context->vfd_isopen) { 295 err("%s: VFD is not open", __func__); 296 retval = -EIO; 297 } else { 298 context->vfd_isopen = 0; 299 printk(KERN_INFO "VFD port closed\n"); 300 if (!context->dev_present && !context->ir_isopen) { 301 302 /* Device disconnected before close and IR port is 303 * not open. If IR port is open, context will be 304 * deleted by ir_close. */ 305 mutex_unlock(&context->ctx_lock); 306 delete_context(context); 307 return retval; 308 } 309 } 310 311 mutex_unlock(&context->ctx_lock); 312 return retval; 313} 314 315/** 316 * Sends a packet to the VFD. 317 */ 318static int send_packet(struct sasem_context *context) 319{ 320 unsigned int pipe; 321 int interval = 0; 322 int retval = 0; 323 324 pipe = usb_sndintpipe(context->dev, 325 context->tx_endpoint->bEndpointAddress); 326 interval = context->tx_endpoint->bInterval; 327 328 usb_fill_int_urb(context->tx_urb, context->dev, pipe, 329 context->usb_tx_buf, sizeof(context->usb_tx_buf), 330 usb_tx_callback, context, interval); 331 332 context->tx_urb->actual_length = 0; 333 334 init_completion(&context->tx.finished); 335 atomic_set(&(context->tx.busy), 1); 336 337 retval = usb_submit_urb(context->tx_urb, GFP_KERNEL); 338 if (retval) { 339 atomic_set(&(context->tx.busy), 0); 340 err("%s: error submitting urb (%d)", __func__, retval); 341 } else { 342 /* Wait for transmission to complete (or abort) */ 343 mutex_unlock(&context->ctx_lock); 344 wait_for_completion(&context->tx.finished); 345 mutex_lock(&context->ctx_lock); 346 347 retval = context->tx.status; 348 if (retval) 349 err("%s: packet tx failed (%d)", __func__, retval); 350 } 351 352 return retval; 353} 354 355/** 356 * Writes data to the VFD. The Sasem VFD is 2x16 characters 357 * and requires data in 9 consecutive USB interrupt packets, 358 * each packet carrying 8 bytes. 359 */ 360static ssize_t vfd_write(struct file *file, const char *buf, 361 size_t n_bytes, loff_t *pos) 362{ 363 int i; 364 int retval = 0; 365 struct sasem_context *context; 366 int *data_buf; 367 368 context = (struct sasem_context *) file->private_data; 369 if (!context) { 370 err("%s: no context for device", __func__); 371 return -ENODEV; 372 } 373 374 mutex_lock(&context->ctx_lock); 375 376 if (!context->dev_present) { 377 err("%s: no Sasem device present", __func__); 378 retval = -ENODEV; 379 goto exit; 380 } 381 382 if (n_bytes <= 0 || n_bytes > SASEM_DATA_BUF_SZ) { 383 err("%s: invalid payload size", __func__); 384 retval = -EINVAL; 385 goto exit; 386 } 387 388 data_buf = memdup_user(buf, n_bytes); 389 if (PTR_ERR(data_buf)) 390 return PTR_ERR(data_buf); 391 392 memcpy(context->tx.data_buf, data_buf, n_bytes); 393 394 /* Pad with spaces */ 395 for (i = n_bytes; i < SASEM_DATA_BUF_SZ; ++i) 396 context->tx.data_buf[i] = ' '; 397 398 /* Nine 8 byte packets to be sent */ 399 /* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0" 400 * will clear the VFD */ 401 for (i = 0; i < 9; i++) { 402 switch (i) { 403 case 0: 404 memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8); 405 context->usb_tx_buf[1] = (context->vfd_contrast) ? 406 (0x2B - (context->vfd_contrast - 1) / 250) 407 : 0x2B; 408 break; 409 case 1: 410 memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8); 411 break; 412 case 2: 413 memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8); 414 break; 415 case 3: 416 memcpy(context->usb_tx_buf, context->tx.data_buf, 8); 417 break; 418 case 4: 419 memcpy(context->usb_tx_buf, 420 context->tx.data_buf + 8, 8); 421 break; 422 case 5: 423 memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8); 424 break; 425 case 6: 426 memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8); 427 break; 428 case 7: 429 memcpy(context->usb_tx_buf, 430 context->tx.data_buf + 16, 8); 431 break; 432 case 8: 433 memcpy(context->usb_tx_buf, 434 context->tx.data_buf + 24, 8); 435 break; 436 } 437 retval = send_packet(context); 438 if (retval) { 439 440 err("%s: send packet failed for packet #%d", 441 __func__, i); 442 goto exit; 443 } 444 } 445exit: 446 447 mutex_unlock(&context->ctx_lock); 448 449 return (!retval) ? n_bytes : retval; 450} 451 452/** 453 * Callback function for USB core API: transmit data 454 */ 455static void usb_tx_callback(struct urb *urb) 456{ 457 struct sasem_context *context; 458 459 if (!urb) 460 return; 461 context = (struct sasem_context *) urb->context; 462 if (!context) 463 return; 464 465 context->tx.status = urb->status; 466 467 /* notify waiters that write has finished */ 468 atomic_set(&context->tx.busy, 0); 469 complete(&context->tx.finished); 470 471 return; 472} 473 474/** 475 * Called by lirc_dev when the application opens /dev/lirc 476 */ 477static int ir_open(void *data) 478{ 479 int retval = 0; 480 struct sasem_context *context; 481 482 /* prevent races with disconnect */ 483 mutex_lock(&disconnect_lock); 484 485 context = (struct sasem_context *) data; 486 487 mutex_lock(&context->ctx_lock); 488 489 if (context->ir_isopen) { 490 err("%s: IR port is already open", __func__); 491 retval = -EBUSY; 492 goto exit; 493 } 494 495 usb_fill_int_urb(context->rx_urb, context->dev, 496 usb_rcvintpipe(context->dev, 497 context->rx_endpoint->bEndpointAddress), 498 context->usb_rx_buf, sizeof(context->usb_rx_buf), 499 usb_rx_callback, context, context->rx_endpoint->bInterval); 500 501 retval = usb_submit_urb(context->rx_urb, GFP_KERNEL); 502 503 if (retval) 504 err("%s: usb_submit_urb failed for ir_open (%d)", 505 __func__, retval); 506 else { 507 context->ir_isopen = 1; 508 printk(KERN_INFO "IR port opened\n"); 509 } 510 511exit: 512 mutex_unlock(&context->ctx_lock); 513 514 mutex_unlock(&disconnect_lock); 515 return 0; 516} 517 518/** 519 * Called by lirc_dev when the application closes /dev/lirc 520 */ 521static void ir_close(void *data) 522{ 523 struct sasem_context *context; 524 525 context = (struct sasem_context *)data; 526 if (!context) { 527 err("%s: no context for device", __func__); 528 return; 529 } 530 531 mutex_lock(&context->ctx_lock); 532 533 usb_kill_urb(context->rx_urb); 534 context->ir_isopen = 0; 535 printk(KERN_INFO "IR port closed\n"); 536 537 if (!context->dev_present) { 538 539 /* 540 * Device disconnected while IR port was 541 * still open. Driver was not deregistered 542 * at disconnect time, so do it now. 543 */ 544 deregister_from_lirc(context); 545 546 if (!context->vfd_isopen) { 547 548 mutex_unlock(&context->ctx_lock); 549 delete_context(context); 550 return; 551 } 552 /* If VFD port is open, context will be deleted by vfd_close */ 553 } 554 555 mutex_unlock(&context->ctx_lock); 556 return; 557} 558 559/** 560 * Process the incoming packet 561 */ 562static void incoming_packet(struct sasem_context *context, 563 struct urb *urb) 564{ 565 int len = urb->actual_length; 566 unsigned char *buf = urb->transfer_buffer; 567 long ms; 568 struct timeval tv; 569 570 if (len != 8) { 571 printk(KERN_WARNING "%s: invalid incoming packet size (%d)\n", 572 __func__, len); 573 return; 574 } 575 576#ifdef DEBUG 577 int i; 578 for (i = 0; i < 8; ++i) 579 printk(KERN_INFO "%02x ", buf[i]); 580 printk(KERN_INFO "\n"); 581#endif 582 583 /* 584 * Lirc could deal with the repeat code, but we really need to block it 585 * if it arrives too late. Otherwise we could repeat the wrong code. 586 */ 587 588 /* get the time since the last button press */ 589 do_gettimeofday(&tv); 590 ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 + 591 (tv.tv_usec - context->presstime.tv_usec) / 1000; 592 593 if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) { 594 /* 595 * the repeat code is being sent, so we copy 596 * the old code to LIRC 597 */ 598 599 /* 600 * NOTE: Only if the last code was less than 250ms ago 601 * - no one should be able to push another (undetected) button 602 * in that time and then get a false repeat of the previous 603 * press but it is long enough for a genuine repeat 604 */ 605 if ((ms < 250) && (context->codesaved != 0)) { 606 memcpy(buf, &context->lastcode, 8); 607 context->presstime.tv_sec = tv.tv_sec; 608 context->presstime.tv_usec = tv.tv_usec; 609 } 610 } else { 611 /* save the current valid code for repeats */ 612 memcpy(&context->lastcode, buf, 8); 613 /* 614 * set flag to signal a valid code was save; 615 * just for safety reasons 616 */ 617 context->codesaved = 1; 618 context->presstime.tv_sec = tv.tv_sec; 619 context->presstime.tv_usec = tv.tv_usec; 620 } 621 622 lirc_buffer_write(context->driver->rbuf, buf); 623 wake_up(&context->driver->rbuf->wait_poll); 624} 625 626/** 627 * Callback function for USB core API: receive data 628 */ 629static void usb_rx_callback(struct urb *urb) 630{ 631 struct sasem_context *context; 632 633 if (!urb) 634 return; 635 context = (struct sasem_context *) urb->context; 636 if (!context) 637 return; 638 639 switch (urb->status) { 640 641 case -ENOENT: /* usbcore unlink successful! */ 642 return; 643 644 case 0: 645 if (context->ir_isopen) 646 incoming_packet(context, urb); 647 break; 648 649 default: 650 printk(KERN_WARNING "%s: status (%d): ignored", 651 __func__, urb->status); 652 break; 653 } 654 655 usb_submit_urb(context->rx_urb, GFP_ATOMIC); 656 return; 657} 658 659 660 661/** 662 * Callback function for USB core API: Probe 663 */ 664static int sasem_probe(struct usb_interface *interface, 665 const struct usb_device_id *id) 666{ 667 struct usb_device *dev = NULL; 668 struct usb_host_interface *iface_desc = NULL; 669 struct usb_endpoint_descriptor *rx_endpoint = NULL; 670 struct usb_endpoint_descriptor *tx_endpoint = NULL; 671 struct urb *rx_urb = NULL; 672 struct urb *tx_urb = NULL; 673 struct lirc_driver *driver = NULL; 674 struct lirc_buffer *rbuf = NULL; 675 int lirc_minor = 0; 676 int num_endpoints; 677 int retval = 0; 678 int vfd_ep_found; 679 int ir_ep_found; 680 int alloc_status; 681 struct sasem_context *context = NULL; 682 int i; 683 684 printk(KERN_INFO "%s: found Sasem device\n", __func__); 685 686 687 dev = usb_get_dev(interface_to_usbdev(interface)); 688 iface_desc = interface->cur_altsetting; 689 num_endpoints = iface_desc->desc.bNumEndpoints; 690 691 /* 692 * Scan the endpoint list and set: 693 * first input endpoint = IR endpoint 694 * first output endpoint = VFD endpoint 695 */ 696 697 ir_ep_found = 0; 698 vfd_ep_found = 0; 699 700 for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) { 701 702 struct usb_endpoint_descriptor *ep; 703 int ep_dir; 704 int ep_type; 705 ep = &iface_desc->endpoint [i].desc; 706 ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK; 707 ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; 708 709 if (!ir_ep_found && 710 ep_dir == USB_DIR_IN && 711 ep_type == USB_ENDPOINT_XFER_INT) { 712 713 rx_endpoint = ep; 714 ir_ep_found = 1; 715 if (debug) 716 printk(KERN_INFO "%s: found IR endpoint\n", 717 __func__); 718 719 } else if (!vfd_ep_found && 720 ep_dir == USB_DIR_OUT && 721 ep_type == USB_ENDPOINT_XFER_INT) { 722 723 tx_endpoint = ep; 724 vfd_ep_found = 1; 725 if (debug) 726 printk(KERN_INFO "%s: found VFD endpoint\n", 727 __func__); 728 } 729 } 730 731 /* Input endpoint is mandatory */ 732 if (!ir_ep_found) { 733 734 err("%s: no valid input (IR) endpoint found.", __func__); 735 retval = -ENODEV; 736 goto exit; 737 } 738 739 if (!vfd_ep_found) 740 printk(KERN_INFO "%s: no valid output (VFD) endpoint found.\n", 741 __func__); 742 743 744 /* Allocate memory */ 745 alloc_status = 0; 746 747 context = kzalloc(sizeof(struct sasem_context), GFP_KERNEL); 748 if (!context) { 749 err("%s: kzalloc failed for context", __func__); 750 alloc_status = 1; 751 goto alloc_status_switch; 752 } 753 driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); 754 if (!driver) { 755 err("%s: kzalloc failed for lirc_driver", __func__); 756 alloc_status = 2; 757 goto alloc_status_switch; 758 } 759 rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); 760 if (!rbuf) { 761 err("%s: kmalloc failed for lirc_buffer", __func__); 762 alloc_status = 3; 763 goto alloc_status_switch; 764 } 765 if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) { 766 err("%s: lirc_buffer_init failed", __func__); 767 alloc_status = 4; 768 goto alloc_status_switch; 769 } 770 rx_urb = usb_alloc_urb(0, GFP_KERNEL); 771 if (!rx_urb) { 772 err("%s: usb_alloc_urb failed for IR urb", __func__); 773 alloc_status = 5; 774 goto alloc_status_switch; 775 } 776 if (vfd_ep_found) { 777 tx_urb = usb_alloc_urb(0, GFP_KERNEL); 778 if (!tx_urb) { 779 err("%s: usb_alloc_urb failed for VFD urb", 780 __func__); 781 alloc_status = 6; 782 goto alloc_status_switch; 783 } 784 } 785 786 mutex_init(&context->ctx_lock); 787 788 strcpy(driver->name, MOD_NAME); 789 driver->minor = -1; 790 driver->code_length = 64; 791 driver->sample_rate = 0; 792 driver->features = LIRC_CAN_REC_LIRCCODE; 793 driver->data = context; 794 driver->rbuf = rbuf; 795 driver->set_use_inc = ir_open; 796 driver->set_use_dec = ir_close; 797 driver->dev = &interface->dev; 798 driver->owner = THIS_MODULE; 799 800 mutex_lock(&context->ctx_lock); 801 802 lirc_minor = lirc_register_driver(driver); 803 if (lirc_minor < 0) { 804 err("%s: lirc_register_driver failed", __func__); 805 alloc_status = 7; 806 mutex_unlock(&context->ctx_lock); 807 } else 808 printk(KERN_INFO "%s: Registered Sasem driver (minor:%d)\n", 809 __func__, lirc_minor); 810 811alloc_status_switch: 812 813 switch (alloc_status) { 814 815 case 7: 816 if (vfd_ep_found) 817 usb_free_urb(tx_urb); 818 case 6: 819 usb_free_urb(rx_urb); 820 case 5: 821 lirc_buffer_free(rbuf); 822 case 4: 823 kfree(rbuf); 824 case 3: 825 kfree(driver); 826 case 2: 827 kfree(context); 828 context = NULL; 829 case 1: 830 retval = -ENOMEM; 831 goto exit; 832 } 833 834 /* Needed while unregistering! */ 835 driver->minor = lirc_minor; 836 837 context->dev = dev; 838 context->dev_present = 1; 839 context->rx_endpoint = rx_endpoint; 840 context->rx_urb = rx_urb; 841 if (vfd_ep_found) { 842 context->tx_endpoint = tx_endpoint; 843 context->tx_urb = tx_urb; 844 context->vfd_contrast = 1000; /* range 0 - 1000 */ 845 } 846 context->driver = driver; 847 848 usb_set_intfdata(interface, context); 849 850 if (vfd_ep_found) { 851 852 if (debug) 853 printk(KERN_INFO "Registering VFD with sysfs\n"); 854 if (usb_register_dev(interface, &sasem_class)) 855 /* Not a fatal error, so ignore */ 856 printk(KERN_INFO "%s: could not get a minor number " 857 "for VFD\n", __func__); 858 } 859 860 printk(KERN_INFO "%s: Sasem device on usb<%d:%d> initialized\n", 861 __func__, dev->bus->busnum, dev->devnum); 862 863 mutex_unlock(&context->ctx_lock); 864exit: 865 return retval; 866} 867 868/** 869 * Callback function for USB core API: disonnect 870 */ 871static void sasem_disconnect(struct usb_interface *interface) 872{ 873 struct sasem_context *context; 874 875 /* prevent races with ir_open()/vfd_open() */ 876 mutex_lock(&disconnect_lock); 877 878 context = usb_get_intfdata(interface); 879 mutex_lock(&context->ctx_lock); 880 881 printk(KERN_INFO "%s: Sasem device disconnected\n", __func__); 882 883 usb_set_intfdata(interface, NULL); 884 context->dev_present = 0; 885 886 /* Stop reception */ 887 usb_kill_urb(context->rx_urb); 888 889 /* Abort ongoing write */ 890 if (atomic_read(&context->tx.busy)) { 891 892 usb_kill_urb(context->tx_urb); 893 wait_for_completion(&context->tx.finished); 894 } 895 896 /* De-register from lirc_dev if IR port is not open */ 897 if (!context->ir_isopen) 898 deregister_from_lirc(context); 899 900 usb_deregister_dev(interface, &sasem_class); 901 902 mutex_unlock(&context->ctx_lock); 903 904 if (!context->ir_isopen && !context->vfd_isopen) 905 delete_context(context); 906 907 mutex_unlock(&disconnect_lock); 908} 909 910static int __init sasem_init(void) 911{ 912 int rc; 913 914 printk(KERN_INFO MOD_DESC ", v" MOD_VERSION "\n"); 915 printk(KERN_INFO MOD_AUTHOR "\n"); 916 917 rc = usb_register(&sasem_driver); 918 if (rc < 0) { 919 err("%s: usb register failed (%d)", __func__, rc); 920 return -ENODEV; 921 } 922 return 0; 923} 924 925static void __exit sasem_exit(void) 926{ 927 usb_deregister(&sasem_driver); 928 printk(KERN_INFO "module removed. Goodbye!\n"); 929} 930 931 932module_init(sasem_init); 933module_exit(sasem_exit); 934