linux/drivers/staging/media/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#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  38
  39#include <linux/errno.h>
  40#include <linux/init.h>
  41#include <linux/kernel.h>
  42#include <linux/module.h>
  43#include <linux/slab.h>
  44#include <linux/uaccess.h>
  45#include <linux/usb.h>
  46
  47#include <media/lirc.h>
  48#include <media/lirc_dev.h>
  49
  50
  51#define MOD_AUTHOR      "Oliver Stabel <oliver.stabel@gmx.de>, " \
  52                        "Tim Davies <tim@opensystems.net.au>"
  53#define MOD_DESC        "USB Driver for Sasem Remote Controller V1.1"
  54#define MOD_NAME        "lirc_sasem"
  55#define MOD_VERSION     "0.5"
  56
  57#define VFD_MINOR_BASE  144     /* Same as LCD */
  58#define DEVICE_NAME     "lcd%d"
  59
  60#define BUF_CHUNK_SIZE  8
  61#define BUF_SIZE        128
  62
  63#define IOCTL_LCD_CONTRAST 1
  64
  65/*** P R O T O T Y P E S ***/
  66
  67/* USB Callback prototypes */
  68static int sasem_probe(struct usb_interface *interface,
  69                        const struct usb_device_id *id);
  70static void sasem_disconnect(struct usb_interface *interface);
  71static void usb_rx_callback(struct urb *urb);
  72static void usb_tx_callback(struct urb *urb);
  73
  74/* VFD file_operations function prototypes */
  75static int vfd_open(struct inode *inode, struct file *file);
  76static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg);
  77static int vfd_close(struct inode *inode, struct file *file);
  78static ssize_t vfd_write(struct file *file, const char *buf,
  79                                size_t n_bytes, loff_t *pos);
  80
  81/* LIRC driver function prototypes */
  82static int ir_open(void *data);
  83static void ir_close(void *data);
  84
  85/*** G L O B A L S ***/
  86#define SASEM_DATA_BUF_SZ       32
  87
  88struct sasem_context {
  89
  90        struct usb_device *dev;
  91        int vfd_isopen;                 /* VFD port has been opened */
  92        unsigned int vfd_contrast;      /* VFD contrast */
  93        int ir_isopen;                  /* IR port has been opened */
  94        int dev_present;                /* USB device presence */
  95        struct mutex ctx_lock;          /* to lock this object */
  96        wait_queue_head_t remove_ok;    /* For unexpected USB disconnects */
  97
  98        struct lirc_driver *driver;
  99        struct usb_endpoint_descriptor *rx_endpoint;
 100        struct usb_endpoint_descriptor *tx_endpoint;
 101        struct urb *rx_urb;
 102        struct urb *tx_urb;
 103        unsigned char usb_rx_buf[8];
 104        unsigned char usb_tx_buf[8];
 105
 106        struct tx_t {
 107                unsigned char data_buf[SASEM_DATA_BUF_SZ]; /* user data
 108                                                            * buffer */
 109                struct completion finished;  /* wait for write to finish  */
 110                atomic_t busy;               /* write in progress */
 111                int status;                  /* status of tx completion */
 112        } tx;
 113
 114        /* for dealing with repeat codes (wish there was a toggle bit!) */
 115        struct timeval presstime;
 116        char lastcode[8];
 117        int codesaved;
 118};
 119
 120/* VFD file operations */
 121static const struct file_operations vfd_fops = {
 122        .owner          = THIS_MODULE,
 123        .open           = &vfd_open,
 124        .write          = &vfd_write,
 125        .unlocked_ioctl = &vfd_ioctl,
 126        .release        = &vfd_close,
 127        .llseek         = noop_llseek,
 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                pr_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                pr_err("%s: unable to deregister from lirc (%d)\n",
 187                       __func__, retval);
 188        else
 189                pr_info("Deregistered Sasem driver (minor:%d)\n", minor);
 190
 191}
 192
 193/**
 194 * Called when the VFD device (e.g. /dev/usb/lcd)
 195 * is opened by the application.
 196 */
 197static int vfd_open(struct inode *inode, struct file *file)
 198{
 199        struct usb_interface *interface;
 200        struct sasem_context *context = NULL;
 201        int subminor;
 202        int retval = 0;
 203
 204        /* prevent races with disconnect */
 205        mutex_lock(&disconnect_lock);
 206
 207        subminor = iminor(inode);
 208        interface = usb_find_interface(&sasem_driver, subminor);
 209        if (!interface) {
 210                pr_err("%s: could not find interface for minor %d\n",
 211                       __func__, subminor);
 212                retval = -ENODEV;
 213                goto exit;
 214        }
 215        context = usb_get_intfdata(interface);
 216
 217        if (!context) {
 218                dev_err(&interface->dev,
 219                        "%s: no context found for minor %d\n",
 220                        __func__, subminor);
 221                retval = -ENODEV;
 222                goto exit;
 223        }
 224
 225        mutex_lock(&context->ctx_lock);
 226
 227        if (context->vfd_isopen) {
 228                dev_err(&interface->dev,
 229                        "%s: VFD port is already open", __func__);
 230                retval = -EBUSY;
 231        } else {
 232                context->vfd_isopen = 1;
 233                file->private_data = context;
 234                dev_info(&interface->dev, "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                pr_err("%s: no context for device\n", __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                pr_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                pr_err("%s: no context for device\n", __func__);
 290                return -ENODEV;
 291        }
 292
 293        mutex_lock(&context->ctx_lock);
 294
 295        if (!context->vfd_isopen) {
 296                dev_err(&context->dev->dev, "%s: VFD is not open\n", __func__);
 297                retval = -EIO;
 298        } else {
 299                context->vfd_isopen = 0;
 300                dev_info(&context->dev->dev, "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                dev_err(&context->dev->dev, "%s: error submitting urb (%d)\n",
 342                        __func__, retval);
 343        } else {
 344                /* Wait for transmission to complete (or abort) */
 345                mutex_unlock(&context->ctx_lock);
 346                wait_for_completion(&context->tx.finished);
 347                mutex_lock(&context->ctx_lock);
 348
 349                retval = context->tx.status;
 350                if (retval)
 351                        dev_err(&context->dev->dev,
 352                                "%s: packet tx failed (%d)\n",
 353                                __func__, retval);
 354        }
 355
 356        return retval;
 357}
 358
 359/**
 360 * Writes data to the VFD.  The Sasem VFD is 2x16 characters
 361 * and requires data in 9 consecutive USB interrupt packets,
 362 * each packet carrying 8 bytes.
 363 */
 364static ssize_t vfd_write(struct file *file, const char *buf,
 365                                size_t n_bytes, loff_t *pos)
 366{
 367        int i;
 368        int retval = 0;
 369        struct sasem_context *context;
 370        int *data_buf = NULL;
 371
 372        context = (struct sasem_context *) file->private_data;
 373        if (!context) {
 374                pr_err("%s: no context for device\n", __func__);
 375                return -ENODEV;
 376        }
 377
 378        mutex_lock(&context->ctx_lock);
 379
 380        if (!context->dev_present) {
 381                pr_err("%s: no Sasem device present\n", __func__);
 382                retval = -ENODEV;
 383                goto exit;
 384        }
 385
 386        if (n_bytes <= 0 || n_bytes > SASEM_DATA_BUF_SZ) {
 387                dev_err(&context->dev->dev, "%s: invalid payload size\n",
 388                        __func__);
 389                retval = -EINVAL;
 390                goto exit;
 391        }
 392
 393        data_buf = memdup_user(buf, n_bytes);
 394        if (IS_ERR(data_buf)) {
 395                retval = PTR_ERR(data_buf);
 396                goto exit;
 397        }
 398
 399        memcpy(context->tx.data_buf, data_buf, n_bytes);
 400
 401        /* Pad with spaces */
 402        for (i = n_bytes; i < SASEM_DATA_BUF_SZ; ++i)
 403                context->tx.data_buf[i] = ' ';
 404
 405        /* Nine 8 byte packets to be sent */
 406        /* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0"
 407         *       will clear the VFD */
 408        for (i = 0; i < 9; i++) {
 409                switch (i) {
 410                case 0:
 411                        memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8);
 412                        context->usb_tx_buf[1] = (context->vfd_contrast) ?
 413                                (0x2B - (context->vfd_contrast - 1) / 250)
 414                                : 0x2B;
 415                        break;
 416                case 1:
 417                        memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
 418                        break;
 419                case 2:
 420                        memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8);
 421                        break;
 422                case 3:
 423                        memcpy(context->usb_tx_buf, context->tx.data_buf, 8);
 424                        break;
 425                case 4:
 426                        memcpy(context->usb_tx_buf,
 427                               context->tx.data_buf + 8, 8);
 428                        break;
 429                case 5:
 430                        memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
 431                        break;
 432                case 6:
 433                        memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8);
 434                        break;
 435                case 7:
 436                        memcpy(context->usb_tx_buf,
 437                               context->tx.data_buf + 16, 8);
 438                        break;
 439                case 8:
 440                        memcpy(context->usb_tx_buf,
 441                               context->tx.data_buf + 24, 8);
 442                        break;
 443                }
 444                retval = send_packet(context);
 445                if (retval) {
 446                        dev_err(&context->dev->dev,
 447                                "%s: send packet failed for packet #%d\n",
 448                                __func__, i);
 449                        goto exit;
 450                }
 451        }
 452exit:
 453
 454        mutex_unlock(&context->ctx_lock);
 455        kfree(data_buf);
 456
 457        return (!retval) ? n_bytes : retval;
 458}
 459
 460/**
 461 * Callback function for USB core API: transmit data
 462 */
 463static void usb_tx_callback(struct urb *urb)
 464{
 465        struct sasem_context *context;
 466
 467        if (!urb)
 468                return;
 469        context = (struct sasem_context *) urb->context;
 470        if (!context)
 471                return;
 472
 473        context->tx.status = urb->status;
 474
 475        /* notify waiters that write has finished */
 476        atomic_set(&context->tx.busy, 0);
 477        complete(&context->tx.finished);
 478
 479        return;
 480}
 481
 482/**
 483 * Called by lirc_dev when the application opens /dev/lirc
 484 */
 485static int ir_open(void *data)
 486{
 487        int retval = 0;
 488        struct sasem_context *context;
 489
 490        /* prevent races with disconnect */
 491        mutex_lock(&disconnect_lock);
 492
 493        context = (struct sasem_context *) data;
 494
 495        mutex_lock(&context->ctx_lock);
 496
 497        if (context->ir_isopen) {
 498                dev_err(&context->dev->dev, "%s: IR port is already open\n",
 499                        __func__);
 500                retval = -EBUSY;
 501                goto exit;
 502        }
 503
 504        usb_fill_int_urb(context->rx_urb, context->dev,
 505                usb_rcvintpipe(context->dev,
 506                                context->rx_endpoint->bEndpointAddress),
 507                context->usb_rx_buf, sizeof(context->usb_rx_buf),
 508                usb_rx_callback, context, context->rx_endpoint->bInterval);
 509
 510        retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
 511
 512        if (retval)
 513                dev_err(&context->dev->dev,
 514                        "%s: usb_submit_urb failed for ir_open (%d)\n",
 515                        __func__, retval);
 516        else {
 517                context->ir_isopen = 1;
 518                dev_info(&context->dev->dev, "IR port opened\n");
 519        }
 520
 521exit:
 522        mutex_unlock(&context->ctx_lock);
 523
 524        mutex_unlock(&disconnect_lock);
 525        return retval;
 526}
 527
 528/**
 529 * Called by lirc_dev when the application closes /dev/lirc
 530 */
 531static void ir_close(void *data)
 532{
 533        struct sasem_context *context;
 534
 535        context = (struct sasem_context *)data;
 536        if (!context) {
 537                pr_err("%s: no context for device\n", __func__);
 538                return;
 539        }
 540
 541        mutex_lock(&context->ctx_lock);
 542
 543        usb_kill_urb(context->rx_urb);
 544        context->ir_isopen = 0;
 545        pr_info("IR port closed\n");
 546
 547        if (!context->dev_present) {
 548
 549                /*
 550                 * Device disconnected while IR port was
 551                 * still open. Driver was not deregistered
 552                 * at disconnect time, so do it now.
 553                 */
 554                deregister_from_lirc(context);
 555
 556                if (!context->vfd_isopen) {
 557
 558                        mutex_unlock(&context->ctx_lock);
 559                        delete_context(context);
 560                        return;
 561                }
 562                /* If VFD port is open, context will be deleted by vfd_close */
 563        }
 564
 565        mutex_unlock(&context->ctx_lock);
 566        return;
 567}
 568
 569/**
 570 * Process the incoming packet
 571 */
 572static void incoming_packet(struct sasem_context *context,
 573                                   struct urb *urb)
 574{
 575        int len = urb->actual_length;
 576        unsigned char *buf = urb->transfer_buffer;
 577        long ms;
 578        struct timeval tv;
 579        int i;
 580
 581        if (len != 8) {
 582                dev_warn(&context->dev->dev,
 583                         "%s: invalid incoming packet size (%d)\n",
 584                         __func__, len);
 585                return;
 586        }
 587
 588        if (debug) {
 589                printk(KERN_INFO "Incoming data: ");
 590                for (i = 0; i < 8; ++i)
 591                        printk(KERN_CONT "%02x ", buf[i]);
 592                printk(KERN_CONT "\n");
 593        }
 594
 595        /*
 596         * Lirc could deal with the repeat code, but we really need to block it
 597         * if it arrives too late.  Otherwise we could repeat the wrong code.
 598         */
 599
 600        /* get the time since the last button press */
 601        do_gettimeofday(&tv);
 602        ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 +
 603             (tv.tv_usec - context->presstime.tv_usec) / 1000;
 604
 605        if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) {
 606                /*
 607                 * the repeat code is being sent, so we copy
 608                 * the old code to LIRC
 609                 */
 610
 611                /*
 612                 * NOTE: Only if the last code was less than 250ms ago
 613                 * - no one should be able to push another (undetected) button
 614                 *   in that time and then get a false repeat of the previous
 615                 *   press but it is long enough for a genuine repeat
 616                 */
 617                if ((ms < 250) && (context->codesaved != 0)) {
 618                        memcpy(buf, &context->lastcode, 8);
 619                        context->presstime.tv_sec = tv.tv_sec;
 620                        context->presstime.tv_usec = tv.tv_usec;
 621                }
 622        } else {
 623                /* save the current valid code for repeats */
 624                memcpy(&context->lastcode, buf, 8);
 625                /*
 626                 * set flag to signal a valid code was save;
 627                 * just for safety reasons
 628                 */
 629                context->codesaved = 1;
 630                context->presstime.tv_sec = tv.tv_sec;
 631                context->presstime.tv_usec = tv.tv_usec;
 632        }
 633
 634        lirc_buffer_write(context->driver->rbuf, buf);
 635        wake_up(&context->driver->rbuf->wait_poll);
 636}
 637
 638/**
 639 * Callback function for USB core API: receive data
 640 */
 641static void usb_rx_callback(struct urb *urb)
 642{
 643        struct sasem_context *context;
 644
 645        if (!urb)
 646                return;
 647        context = (struct sasem_context *) urb->context;
 648        if (!context)
 649                return;
 650
 651        switch (urb->status) {
 652
 653        case -ENOENT:           /* usbcore unlink successful! */
 654                return;
 655
 656        case 0:
 657                if (context->ir_isopen)
 658                        incoming_packet(context, urb);
 659                break;
 660
 661        default:
 662                dev_warn(&urb->dev->dev, "%s: status (%d): ignored",
 663                         __func__, urb->status);
 664                break;
 665        }
 666
 667        usb_submit_urb(context->rx_urb, GFP_ATOMIC);
 668        return;
 669}
 670
 671
 672
 673/**
 674 * Callback function for USB core API: Probe
 675 */
 676static int sasem_probe(struct usb_interface *interface,
 677                        const struct usb_device_id *id)
 678{
 679        struct usb_device *dev = NULL;
 680        struct usb_host_interface *iface_desc = NULL;
 681        struct usb_endpoint_descriptor *rx_endpoint = NULL;
 682        struct usb_endpoint_descriptor *tx_endpoint = NULL;
 683        struct urb *rx_urb = NULL;
 684        struct urb *tx_urb = NULL;
 685        struct lirc_driver *driver = NULL;
 686        struct lirc_buffer *rbuf = NULL;
 687        int lirc_minor = 0;
 688        int num_endpoints;
 689        int retval = 0;
 690        int vfd_ep_found;
 691        int ir_ep_found;
 692        int alloc_status;
 693        struct sasem_context *context = NULL;
 694        int i;
 695
 696        dev_info(&interface->dev, "%s: found Sasem device\n", __func__);
 697
 698
 699        dev = usb_get_dev(interface_to_usbdev(interface));
 700        iface_desc = interface->cur_altsetting;
 701        num_endpoints = iface_desc->desc.bNumEndpoints;
 702
 703        /*
 704         * Scan the endpoint list and set:
 705         *      first input endpoint = IR endpoint
 706         *      first output endpoint = VFD endpoint
 707         */
 708
 709        ir_ep_found = 0;
 710        vfd_ep_found = 0;
 711
 712        for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) {
 713
 714                struct usb_endpoint_descriptor *ep;
 715                int ep_dir;
 716                int ep_type;
 717                ep = &iface_desc->endpoint [i].desc;
 718                ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
 719                ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
 720
 721                if (!ir_ep_found &&
 722                        ep_dir == USB_DIR_IN &&
 723                        ep_type == USB_ENDPOINT_XFER_INT) {
 724
 725                        rx_endpoint = ep;
 726                        ir_ep_found = 1;
 727                        if (debug)
 728                                dev_info(&interface->dev,
 729                                        "%s: found IR endpoint\n", __func__);
 730
 731                } else if (!vfd_ep_found &&
 732                        ep_dir == USB_DIR_OUT &&
 733                        ep_type == USB_ENDPOINT_XFER_INT) {
 734
 735                        tx_endpoint = ep;
 736                        vfd_ep_found = 1;
 737                        if (debug)
 738                                dev_info(&interface->dev,
 739                                        "%s: found VFD endpoint\n", __func__);
 740                }
 741        }
 742
 743        /* Input endpoint is mandatory */
 744        if (!ir_ep_found) {
 745                dev_err(&interface->dev,
 746                        "%s: no valid input (IR) endpoint found.\n", __func__);
 747                retval = -ENODEV;
 748                goto exit;
 749        }
 750
 751        if (!vfd_ep_found)
 752                dev_info(&interface->dev,
 753                        "%s: no valid output (VFD) endpoint found.\n",
 754                        __func__);
 755
 756
 757        /* Allocate memory */
 758        alloc_status = 0;
 759
 760        context = kzalloc(sizeof(struct sasem_context), GFP_KERNEL);
 761        if (!context) {
 762                alloc_status = 1;
 763                goto alloc_status_switch;
 764        }
 765        driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
 766        if (!driver) {
 767                alloc_status = 2;
 768                goto alloc_status_switch;
 769        }
 770        rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
 771        if (!rbuf) {
 772                alloc_status = 3;
 773                goto alloc_status_switch;
 774        }
 775        if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
 776                dev_err(&interface->dev,
 777                        "%s: lirc_buffer_init failed\n", __func__);
 778                alloc_status = 4;
 779                goto alloc_status_switch;
 780        }
 781        rx_urb = usb_alloc_urb(0, GFP_KERNEL);
 782        if (!rx_urb) {
 783                dev_err(&interface->dev,
 784                        "%s: usb_alloc_urb failed for IR urb\n", __func__);
 785                alloc_status = 5;
 786                goto alloc_status_switch;
 787        }
 788        if (vfd_ep_found) {
 789                tx_urb = usb_alloc_urb(0, GFP_KERNEL);
 790                if (!tx_urb) {
 791                        dev_err(&interface->dev,
 792                                "%s: usb_alloc_urb failed for VFD urb",
 793                                __func__);
 794                        alloc_status = 6;
 795                        goto alloc_status_switch;
 796                }
 797        }
 798
 799        mutex_init(&context->ctx_lock);
 800
 801        strcpy(driver->name, MOD_NAME);
 802        driver->minor = -1;
 803        driver->code_length = 64;
 804        driver->sample_rate = 0;
 805        driver->features = LIRC_CAN_REC_LIRCCODE;
 806        driver->data = context;
 807        driver->rbuf = rbuf;
 808        driver->set_use_inc = ir_open;
 809        driver->set_use_dec = ir_close;
 810        driver->dev   = &interface->dev;
 811        driver->owner = THIS_MODULE;
 812
 813        mutex_lock(&context->ctx_lock);
 814
 815        lirc_minor = lirc_register_driver(driver);
 816        if (lirc_minor < 0) {
 817                dev_err(&interface->dev,
 818                        "%s: lirc_register_driver failed\n", __func__);
 819                alloc_status = 7;
 820                retval = lirc_minor;
 821                goto unlock;
 822        } else
 823                dev_info(&interface->dev,
 824                         "%s: Registered Sasem driver (minor:%d)\n",
 825                         __func__, lirc_minor);
 826
 827        /* Needed while unregistering! */
 828        driver->minor = lirc_minor;
 829
 830        context->dev = dev;
 831        context->dev_present = 1;
 832        context->rx_endpoint = rx_endpoint;
 833        context->rx_urb = rx_urb;
 834        if (vfd_ep_found) {
 835                context->tx_endpoint = tx_endpoint;
 836                context->tx_urb = tx_urb;
 837                context->vfd_contrast = 1000;   /* range 0 - 1000 */
 838        }
 839        context->driver = driver;
 840
 841        usb_set_intfdata(interface, context);
 842
 843        if (vfd_ep_found) {
 844
 845                if (debug)
 846                        dev_info(&interface->dev,
 847                                 "Registering VFD with sysfs\n");
 848                if (usb_register_dev(interface, &sasem_class))
 849                        /* Not a fatal error, so ignore */
 850                        dev_info(&interface->dev,
 851                                 "%s: could not get a minor number for VFD\n",
 852                                 __func__);
 853        }
 854
 855        dev_info(&interface->dev,
 856                 "%s: Sasem device on usb<%d:%d> initialized\n",
 857                 __func__, dev->bus->busnum, dev->devnum);
 858unlock:
 859        mutex_unlock(&context->ctx_lock);
 860
 861alloc_status_switch:
 862        switch (alloc_status) {
 863
 864        case 7:
 865                if (vfd_ep_found)
 866                        usb_free_urb(tx_urb);
 867        case 6:
 868                usb_free_urb(rx_urb);
 869        case 5:
 870                lirc_buffer_free(rbuf);
 871        case 4:
 872                kfree(rbuf);
 873        case 3:
 874                kfree(driver);
 875        case 2:
 876                kfree(context);
 877                context = NULL;
 878        case 1:
 879                if (retval == 0)
 880                        retval = -ENOMEM;
 881        }
 882
 883exit:
 884        return retval;
 885}
 886
 887/**
 888 * Callback function for USB core API: disconnect
 889 */
 890static void sasem_disconnect(struct usb_interface *interface)
 891{
 892        struct sasem_context *context;
 893
 894        /* prevent races with ir_open()/vfd_open() */
 895        mutex_lock(&disconnect_lock);
 896
 897        context = usb_get_intfdata(interface);
 898        mutex_lock(&context->ctx_lock);
 899
 900        dev_info(&interface->dev, "%s: Sasem device disconnected\n",
 901                 __func__);
 902
 903        usb_set_intfdata(interface, NULL);
 904        context->dev_present = 0;
 905
 906        /* Stop reception */
 907        usb_kill_urb(context->rx_urb);
 908
 909        /* Abort ongoing write */
 910        if (atomic_read(&context->tx.busy)) {
 911
 912                usb_kill_urb(context->tx_urb);
 913                wait_for_completion(&context->tx.finished);
 914        }
 915
 916        /* De-register from lirc_dev if IR port is not open */
 917        if (!context->ir_isopen)
 918                deregister_from_lirc(context);
 919
 920        usb_deregister_dev(interface, &sasem_class);
 921
 922        mutex_unlock(&context->ctx_lock);
 923
 924        if (!context->ir_isopen && !context->vfd_isopen)
 925                delete_context(context);
 926
 927        mutex_unlock(&disconnect_lock);
 928}
 929
 930module_usb_driver(sasem_driver);
 931