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