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/kernel.h>
  41#include <linux/module.h>
  42#include <linux/slab.h>
  43#include <linux/uaccess.h>
  44#include <linux/usb.h>
  45#include <linux/ktime.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 __user *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        ktime_t 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
 176static void deregister_from_lirc(struct sasem_context *context)
 177{
 178        int retval;
 179        int minor = context->driver->minor;
 180
 181        retval = lirc_unregister_driver(minor);
 182        if (retval)
 183                dev_err(&context->dev->dev,
 184                        "%s: unable to deregister from lirc (%d)\n",
 185                        __func__, retval);
 186        else
 187                dev_info(&context->dev->dev,
 188                         "Deregistered Sasem driver (minor:%d)\n", minor);
 189
 190}
 191
 192/**
 193 * Called when the VFD device (e.g. /dev/usb/lcd)
 194 * is opened by the application.
 195 */
 196static int vfd_open(struct inode *inode, struct file *file)
 197{
 198        struct usb_interface *interface;
 199        struct sasem_context *context = NULL;
 200        int subminor;
 201        int retval = 0;
 202
 203        /* prevent races with disconnect */
 204        mutex_lock(&disconnect_lock);
 205
 206        subminor = iminor(inode);
 207        interface = usb_find_interface(&sasem_driver, subminor);
 208        if (!interface) {
 209                pr_err("%s: could not find interface for minor %d\n",
 210                       __func__, subminor);
 211                retval = -ENODEV;
 212                goto exit;
 213        }
 214        context = usb_get_intfdata(interface);
 215
 216        if (!context) {
 217                dev_err(&interface->dev, "no context found for minor %d\n",
 218                        subminor);
 219                retval = -ENODEV;
 220                goto exit;
 221        }
 222
 223        mutex_lock(&context->ctx_lock);
 224
 225        if (context->vfd_isopen) {
 226                dev_err(&interface->dev,
 227                        "%s: VFD port is already open", __func__);
 228                retval = -EBUSY;
 229        } else {
 230                context->vfd_isopen = 1;
 231                file->private_data = context;
 232                dev_info(&interface->dev, "VFD port opened\n");
 233        }
 234
 235        mutex_unlock(&context->ctx_lock);
 236
 237exit:
 238        mutex_unlock(&disconnect_lock);
 239        return retval;
 240}
 241
 242/**
 243 * Called when the VFD device (e.g. /dev/usb/lcd)
 244 * is closed by the application.
 245 */
 246static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 247{
 248        struct sasem_context *context;
 249
 250        context = (struct sasem_context *) file->private_data;
 251
 252        if (!context) {
 253                pr_err("%s: no context for device\n", __func__);
 254                return -ENODEV;
 255        }
 256
 257        mutex_lock(&context->ctx_lock);
 258
 259        switch (cmd) {
 260        case IOCTL_LCD_CONTRAST:
 261                if (arg > 1000)
 262                        arg = 1000;
 263                context->vfd_contrast = (unsigned int)arg;
 264                break;
 265        default:
 266                pr_info("Unknown IOCTL command\n");
 267                mutex_unlock(&context->ctx_lock);
 268                return -ENOIOCTLCMD;  /* not supported */
 269        }
 270
 271        mutex_unlock(&context->ctx_lock);
 272        return 0;
 273}
 274
 275/**
 276 * Called when the VFD device (e.g. /dev/usb/lcd)
 277 * is closed by the application.
 278 */
 279static int vfd_close(struct inode *inode, struct file *file)
 280{
 281        struct sasem_context *context = NULL;
 282        int retval = 0;
 283
 284        context = (struct sasem_context *) file->private_data;
 285
 286        if (!context) {
 287                pr_err("%s: no context for device\n", __func__);
 288                return -ENODEV;
 289        }
 290
 291        mutex_lock(&context->ctx_lock);
 292
 293        if (!context->vfd_isopen) {
 294                dev_err(&context->dev->dev, "%s: VFD is not open\n", __func__);
 295                retval = -EIO;
 296        } else {
 297                context->vfd_isopen = 0;
 298                dev_info(&context->dev->dev, "VFD port closed\n");
 299                if (!context->dev_present && !context->ir_isopen) {
 300
 301                        /* Device disconnected before close and IR port is
 302                         * not open. If IR port is open, context will be
 303                         * deleted by ir_close. */
 304                        mutex_unlock(&context->ctx_lock);
 305                        delete_context(context);
 306                        return retval;
 307                }
 308        }
 309
 310        mutex_unlock(&context->ctx_lock);
 311        return retval;
 312}
 313
 314/**
 315 * Sends a packet to the VFD.
 316 */
 317static int send_packet(struct sasem_context *context)
 318{
 319        unsigned int pipe;
 320        int interval = 0;
 321        int retval = 0;
 322
 323        pipe = usb_sndintpipe(context->dev,
 324                        context->tx_endpoint->bEndpointAddress);
 325        interval = context->tx_endpoint->bInterval;
 326
 327        usb_fill_int_urb(context->tx_urb, context->dev, pipe,
 328                context->usb_tx_buf, sizeof(context->usb_tx_buf),
 329                usb_tx_callback, context, interval);
 330
 331        context->tx_urb->actual_length = 0;
 332
 333        init_completion(&context->tx.finished);
 334        atomic_set(&context->tx.busy, 1);
 335
 336        retval =  usb_submit_urb(context->tx_urb, GFP_KERNEL);
 337        if (retval) {
 338                atomic_set(&context->tx.busy, 0);
 339                dev_err(&context->dev->dev, "error submitting urb (%d)\n",
 340                        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                        dev_err(&context->dev->dev,
 350                                "packet tx failed (%d)\n", 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 __user *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 = NULL;
 368
 369        context = (struct sasem_context *) file->private_data;
 370        if (!context) {
 371                pr_err("%s: no context for device\n", __func__);
 372                return -ENODEV;
 373        }
 374
 375        mutex_lock(&context->ctx_lock);
 376
 377        if (!context->dev_present) {
 378                pr_err("%s: no Sasem device present\n", __func__);
 379                retval = -ENODEV;
 380                goto exit;
 381        }
 382
 383        if (n_bytes <= 0 || n_bytes > SASEM_DATA_BUF_SZ) {
 384                dev_err(&context->dev->dev, "%s: invalid payload size\n",
 385                        __func__);
 386                retval = -EINVAL;
 387                goto exit;
 388        }
 389
 390        data_buf = memdup_user(buf, n_bytes);
 391        if (IS_ERR(data_buf)) {
 392                retval = PTR_ERR(data_buf);
 393                data_buf = NULL;
 394                goto exit;
 395        }
 396
 397        memcpy(context->tx.data_buf, data_buf, n_bytes);
 398
 399        /* Pad with spaces */
 400        for (i = n_bytes; i < SASEM_DATA_BUF_SZ; ++i)
 401                context->tx.data_buf[i] = ' ';
 402
 403        /* Nine 8 byte packets to be sent */
 404        /* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0"
 405         *       will clear the VFD */
 406        for (i = 0; i < 9; i++) {
 407                switch (i) {
 408                case 0:
 409                        memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8);
 410                        context->usb_tx_buf[1] = (context->vfd_contrast) ?
 411                                (0x2B - (context->vfd_contrast - 1) / 250)
 412                                : 0x2B;
 413                        break;
 414                case 1:
 415                        memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
 416                        break;
 417                case 2:
 418                        memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8);
 419                        break;
 420                case 3:
 421                        memcpy(context->usb_tx_buf, context->tx.data_buf, 8);
 422                        break;
 423                case 4:
 424                        memcpy(context->usb_tx_buf,
 425                               context->tx.data_buf + 8, 8);
 426                        break;
 427                case 5:
 428                        memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
 429                        break;
 430                case 6:
 431                        memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8);
 432                        break;
 433                case 7:
 434                        memcpy(context->usb_tx_buf,
 435                               context->tx.data_buf + 16, 8);
 436                        break;
 437                case 8:
 438                        memcpy(context->usb_tx_buf,
 439                               context->tx.data_buf + 24, 8);
 440                        break;
 441                }
 442                retval = send_packet(context);
 443                if (retval) {
 444                        dev_err(&context->dev->dev,
 445                                "send packet failed for packet #%d\n", i);
 446                        goto exit;
 447                }
 448        }
 449exit:
 450
 451        mutex_unlock(&context->ctx_lock);
 452        kfree(data_buf);
 453
 454        return (!retval) ? n_bytes : retval;
 455}
 456
 457/**
 458 * Callback function for USB core API: transmit data
 459 */
 460static void usb_tx_callback(struct urb *urb)
 461{
 462        struct sasem_context *context;
 463
 464        if (!urb)
 465                return;
 466        context = (struct sasem_context *) urb->context;
 467        if (!context)
 468                return;
 469
 470        context->tx.status = urb->status;
 471
 472        /* notify waiters that write has finished */
 473        atomic_set(&context->tx.busy, 0);
 474        complete(&context->tx.finished);
 475}
 476
 477/**
 478 * Called by lirc_dev when the application opens /dev/lirc
 479 */
 480static int ir_open(void *data)
 481{
 482        int retval = 0;
 483        struct sasem_context *context;
 484
 485        /* prevent races with disconnect */
 486        mutex_lock(&disconnect_lock);
 487
 488        context = data;
 489
 490        mutex_lock(&context->ctx_lock);
 491
 492        if (context->ir_isopen) {
 493                dev_err(&context->dev->dev, "%s: IR port is already open\n",
 494                        __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                dev_err(&context->dev->dev,
 509                        "usb_submit_urb failed for ir_open (%d)\n", retval);
 510        else {
 511                context->ir_isopen = 1;
 512                dev_info(&context->dev->dev, "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 = data;
 530        if (!context) {
 531                pr_err("%s: no context for device\n", __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        pr_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}
 561
 562/**
 563 * Process the incoming packet
 564 */
 565static void incoming_packet(struct sasem_context *context,
 566                                   struct urb *urb)
 567{
 568        int len = urb->actual_length;
 569        unsigned char *buf = urb->transfer_buffer;
 570        u64 ns;
 571        ktime_t kt;
 572
 573        if (len != 8) {
 574                dev_warn(&context->dev->dev,
 575                         "%s: invalid incoming packet size (%d)\n",
 576                         __func__, len);
 577                return;
 578        }
 579
 580        if (debug)
 581                dev_info(&context->dev->dev, "Incoming data: %*ph\n", len, buf);
 582        /*
 583         * Lirc could deal with the repeat code, but we really need to block it
 584         * if it arrives too late.  Otherwise we could repeat the wrong code.
 585         */
 586
 587        /* get the time since the last button press */
 588        kt = ktime_get();
 589        ns = ktime_to_ns(ktime_sub(kt, context->presstime));
 590
 591        if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) {
 592                /*
 593                 * the repeat code is being sent, so we copy
 594                 * the old code to LIRC
 595                 */
 596
 597                /*
 598                 * NOTE: Only if the last code was less than 250ms ago
 599                 * - no one should be able to push another (undetected) button
 600                 *   in that time and then get a false repeat of the previous
 601                 *   press but it is long enough for a genuine repeat
 602                 */
 603                if ((ns < 250 * NSEC_PER_MSEC) && (context->codesaved != 0)) {
 604                        memcpy(buf, &context->lastcode, 8);
 605                        context->presstime = kt;
 606                }
 607        } else {
 608                /* save the current valid code for repeats */
 609                memcpy(&context->lastcode, buf, 8);
 610                /*
 611                 * set flag to signal a valid code was save;
 612                 * just for safety reasons
 613                 */
 614                context->codesaved = 1;
 615                context->presstime = kt;
 616        }
 617
 618        lirc_buffer_write(context->driver->rbuf, buf);
 619        wake_up(&context->driver->rbuf->wait_poll);
 620}
 621
 622/**
 623 * Callback function for USB core API: receive data
 624 */
 625static void usb_rx_callback(struct urb *urb)
 626{
 627        struct sasem_context *context;
 628
 629        if (!urb)
 630                return;
 631        context = (struct sasem_context *) urb->context;
 632        if (!context)
 633                return;
 634
 635        switch (urb->status) {
 636
 637        case -ENOENT:           /* usbcore unlink successful! */
 638                return;
 639
 640        case 0:
 641                if (context->ir_isopen)
 642                        incoming_packet(context, urb);
 643                break;
 644
 645        default:
 646                dev_warn(&urb->dev->dev, "%s: status (%d): ignored",
 647                         __func__, urb->status);
 648                break;
 649        }
 650
 651        usb_submit_urb(context->rx_urb, GFP_ATOMIC);
 652}
 653
 654
 655
 656/**
 657 * Callback function for USB core API: Probe
 658 */
 659static int sasem_probe(struct usb_interface *interface,
 660                        const struct usb_device_id *id)
 661{
 662        struct usb_device *dev = NULL;
 663        struct usb_host_interface *iface_desc = NULL;
 664        struct usb_endpoint_descriptor *rx_endpoint = NULL;
 665        struct usb_endpoint_descriptor *tx_endpoint = NULL;
 666        struct urb *rx_urb = NULL;
 667        struct urb *tx_urb = NULL;
 668        struct lirc_driver *driver = NULL;
 669        struct lirc_buffer *rbuf = NULL;
 670        int lirc_minor = 0;
 671        int num_endpoints;
 672        int retval = 0;
 673        int vfd_ep_found;
 674        int ir_ep_found;
 675        int alloc_status;
 676        struct sasem_context *context = NULL;
 677        int i;
 678
 679        dev_info(&interface->dev, "%s: found Sasem device\n", __func__);
 680
 681
 682        dev = usb_get_dev(interface_to_usbdev(interface));
 683        iface_desc = interface->cur_altsetting;
 684        num_endpoints = iface_desc->desc.bNumEndpoints;
 685
 686        /*
 687         * Scan the endpoint list and set:
 688         *      first input endpoint = IR endpoint
 689         *      first output endpoint = VFD endpoint
 690         */
 691
 692        ir_ep_found = 0;
 693        vfd_ep_found = 0;
 694
 695        for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) {
 696
 697                struct usb_endpoint_descriptor *ep;
 698
 699                ep = &iface_desc->endpoint [i].desc;
 700
 701                if (!ir_ep_found &&
 702                        usb_endpoint_is_int_in(ep)) {
 703
 704                        rx_endpoint = ep;
 705                        ir_ep_found = 1;
 706                        if (debug)
 707                                dev_info(&interface->dev,
 708                                        "%s: found IR endpoint\n", __func__);
 709
 710                } else if (!vfd_ep_found &&
 711                        usb_endpoint_is_int_out(ep)) {
 712
 713                        tx_endpoint = ep;
 714                        vfd_ep_found = 1;
 715                        if (debug)
 716                                dev_info(&interface->dev,
 717                                        "%s: found VFD endpoint\n", __func__);
 718                }
 719        }
 720
 721        /* Input endpoint is mandatory */
 722        if (!ir_ep_found) {
 723                dev_err(&interface->dev,
 724                        "%s: no valid input (IR) endpoint found.\n", __func__);
 725                retval = -ENODEV;
 726                goto exit;
 727        }
 728
 729        if (!vfd_ep_found)
 730                dev_info(&interface->dev,
 731                        "%s: no valid output (VFD) endpoint found.\n",
 732                        __func__);
 733
 734
 735        /* Allocate memory */
 736        alloc_status = 0;
 737
 738        context = kzalloc(sizeof(struct sasem_context), GFP_KERNEL);
 739        if (!context) {
 740                alloc_status = 1;
 741                goto alloc_status_switch;
 742        }
 743        driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
 744        if (!driver) {
 745                alloc_status = 2;
 746                goto alloc_status_switch;
 747        }
 748        rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
 749        if (!rbuf) {
 750                alloc_status = 3;
 751                goto alloc_status_switch;
 752        }
 753        if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
 754                dev_err(&interface->dev,
 755                        "%s: lirc_buffer_init failed\n", __func__);
 756                alloc_status = 4;
 757                goto alloc_status_switch;
 758        }
 759        rx_urb = usb_alloc_urb(0, GFP_KERNEL);
 760        if (!rx_urb) {
 761                dev_err(&interface->dev,
 762                        "%s: usb_alloc_urb failed for IR urb\n", __func__);
 763                alloc_status = 5;
 764                goto alloc_status_switch;
 765        }
 766        if (vfd_ep_found) {
 767                tx_urb = usb_alloc_urb(0, GFP_KERNEL);
 768                if (!tx_urb) {
 769                        dev_err(&interface->dev,
 770                                "%s: usb_alloc_urb failed for VFD urb",
 771                                __func__);
 772                        alloc_status = 6;
 773                        goto alloc_status_switch;
 774                }
 775        }
 776
 777        mutex_init(&context->ctx_lock);
 778
 779        strcpy(driver->name, MOD_NAME);
 780        driver->minor = -1;
 781        driver->code_length = 64;
 782        driver->sample_rate = 0;
 783        driver->features = LIRC_CAN_REC_LIRCCODE;
 784        driver->data = context;
 785        driver->rbuf = rbuf;
 786        driver->set_use_inc = ir_open;
 787        driver->set_use_dec = ir_close;
 788        driver->dev   = &interface->dev;
 789        driver->owner = THIS_MODULE;
 790
 791        mutex_lock(&context->ctx_lock);
 792
 793        lirc_minor = lirc_register_driver(driver);
 794        if (lirc_minor < 0) {
 795                dev_err(&interface->dev,
 796                        "%s: lirc_register_driver failed\n", __func__);
 797                alloc_status = 7;
 798                retval = lirc_minor;
 799                goto unlock;
 800        } else
 801                dev_info(&interface->dev,
 802                         "%s: Registered Sasem driver (minor:%d)\n",
 803                         __func__, lirc_minor);
 804
 805        /* Needed while unregistering! */
 806        driver->minor = lirc_minor;
 807
 808        context->dev = dev;
 809        context->dev_present = 1;
 810        context->rx_endpoint = rx_endpoint;
 811        context->rx_urb = rx_urb;
 812        if (vfd_ep_found) {
 813                context->tx_endpoint = tx_endpoint;
 814                context->tx_urb = tx_urb;
 815                context->vfd_contrast = 1000;   /* range 0 - 1000 */
 816        }
 817        context->driver = driver;
 818
 819        usb_set_intfdata(interface, context);
 820
 821        if (vfd_ep_found) {
 822
 823                if (debug)
 824                        dev_info(&interface->dev,
 825                                 "Registering VFD with sysfs\n");
 826                if (usb_register_dev(interface, &sasem_class))
 827                        /* Not a fatal error, so ignore */
 828                        dev_info(&interface->dev,
 829                                 "%s: could not get a minor number for VFD\n",
 830                                 __func__);
 831        }
 832
 833        dev_info(&interface->dev,
 834                 "%s: Sasem device on usb<%d:%d> initialized\n",
 835                 __func__, dev->bus->busnum, dev->devnum);
 836unlock:
 837        mutex_unlock(&context->ctx_lock);
 838
 839alloc_status_switch:
 840        switch (alloc_status) {
 841
 842        case 7:
 843                if (vfd_ep_found)
 844                        usb_free_urb(tx_urb);
 845        case 6:
 846                usb_free_urb(rx_urb);
 847                /* fall-through */
 848        case 5:
 849                lirc_buffer_free(rbuf);
 850                /* fall-through */
 851        case 4:
 852                kfree(rbuf);
 853                /* fall-through */
 854        case 3:
 855                kfree(driver);
 856                /* fall-through */
 857        case 2:
 858                kfree(context);
 859                context = NULL;
 860                /* fall-through */
 861        case 1:
 862                if (retval == 0)
 863                        retval = -ENOMEM;
 864        }
 865
 866exit:
 867        return retval;
 868}
 869
 870/**
 871 * Callback function for USB core API: disconnect
 872 */
 873static void sasem_disconnect(struct usb_interface *interface)
 874{
 875        struct sasem_context *context;
 876
 877        /* prevent races with ir_open()/vfd_open() */
 878        mutex_lock(&disconnect_lock);
 879
 880        context = usb_get_intfdata(interface);
 881        mutex_lock(&context->ctx_lock);
 882
 883        dev_info(&interface->dev, "%s: Sasem device disconnected\n",
 884                 __func__);
 885
 886        usb_set_intfdata(interface, NULL);
 887        context->dev_present = 0;
 888
 889        /* Stop reception */
 890        usb_kill_urb(context->rx_urb);
 891
 892        /* Abort ongoing write */
 893        if (atomic_read(&context->tx.busy)) {
 894
 895                usb_kill_urb(context->tx_urb);
 896                wait_for_completion(&context->tx.finished);
 897        }
 898
 899        /* De-register from lirc_dev if IR port is not open */
 900        if (!context->ir_isopen)
 901                deregister_from_lirc(context);
 902
 903        usb_deregister_dev(interface, &sasem_class);
 904
 905        mutex_unlock(&context->ctx_lock);
 906
 907        if (!context->ir_isopen && !context->vfd_isopen)
 908                delete_context(context);
 909
 910        mutex_unlock(&disconnect_lock);
 911}
 912
 913module_usb_driver(sasem_driver);
 914