linux-old/drivers/usb/tiglusb.c
<<
>>
Prefs
   1/* Hey EMACS -*- linux-c -*-
   2 *
   3 * tiglusb -- Texas Instruments' USB GraphLink (aka SilverLink) driver.
   4 * Target: Texas Instruments graphing calculators (http://lpg.ticalc.org).
   5 *
   6 * Copyright (C) 2001-2004:
   7 *   Romain Lievin <roms@lpg.ticalc.org>
   8 *   Julien BLACHE <jb@technologeek.org>
   9 * under the terms of the GNU General Public License.
  10 *
  11 * Based on dabusb.c, printer.c & scanner.c
  12 *
  13 * Please see the file: linux/Documentation/usb/SilverLink.txt
  14 * and the website at:  http://lpg.ticalc.org/prj_usb/
  15 * for more info.
  16 *
  17 * History:
  18 *   1.0x, Romain & Julien: initial submit.
  19 *   1.03, Greg Kroah: modifications.
  20 *   1.04, Julien: clean-up & fixes; Romain: 2.4 backport.
  21 *   1.05, Randy Dunlap: bug fix with the timeout parameter (divide-by-zero).
  22 *   1.06, Romain: synched with 2.5, version/firmware changed (confusing).
  23 *   1.07, Romain: fixed bad use of usb_clear_halt (invalid argument);
  24 *          timeout argument checked in ioctl + clean-up.
  25 */
  26
  27#include <linux/module.h>
  28#include <linux/socket.h>
  29#include <linux/miscdevice.h>
  30#include <linux/slab.h>
  31#include <linux/init.h>
  32#include <asm/uaccess.h>
  33#include <linux/delay.h>
  34#include <linux/usb.h>
  35#include <linux/smp_lock.h>
  36#include <linux/devfs_fs_kernel.h>
  37
  38#include <linux/ticable.h>
  39#include "tiglusb.h"
  40
  41/*
  42 * Version Information
  43 */
  44#define DRIVER_VERSION "1.07"
  45#define DRIVER_AUTHOR  "Romain Lievin <roms@tilp.info> & Julien Blache <jb@jblache.org>"
  46#define DRIVER_DESC    "TI-GRAPH LINK USB (aka SilverLink) driver"
  47#define DRIVER_LICENSE "GPL"
  48
  49/* ----- global variables --------------------------------------------- */
  50
  51static tiglusb_t tiglusb[MAXTIGL];
  52static int timeout = TIMAXTIME; /* timeout in tenth of seconds     */
  53
  54static devfs_handle_t devfs_handle;
  55
  56/*---------- misc functions ------------------------------------------- */
  57
  58/*
  59 * Re-initialize device
  60 */
  61static inline int
  62clear_device (struct usb_device *dev)
  63{
  64        if (usb_set_configuration (dev, dev->config[0].bConfigurationValue) < 0) {
  65                err ("clear_device failed");
  66                return -1;
  67        }
  68
  69        return 0;
  70}
  71
  72/* 
  73 * Clear input & output pipes (endpoints)
  74 */
  75static inline int
  76clear_pipes (struct usb_device *dev)
  77{
  78        unsigned int pipe;
  79
  80        pipe = usb_sndbulkpipe (dev, 2);
  81        if (usb_clear_halt (dev, pipe)) {
  82                err ("clear_pipe (w), request failed");
  83                return -1;
  84        }
  85
  86        pipe = usb_rcvbulkpipe (dev, 1);
  87        if (usb_clear_halt (dev, pipe)) {
  88                err ("clear_pipe (r), request failed");
  89                return -1;
  90        }
  91
  92        return 0;
  93}
  94
  95/* ----- file operations functions--------------------------------------- */
  96
  97static int
  98tiglusb_open (struct inode *inode, struct file *filp)
  99{
 100        int devnum = minor (inode->i_rdev);
 101        ptiglusb_t s;
 102
 103        if (devnum < TIUSB_MINOR || devnum >= (TIUSB_MINOR + MAXTIGL))
 104                return -EIO;
 105
 106        s = &tiglusb[devnum - TIUSB_MINOR];
 107
 108        if (down_interruptible (&s->mutex)) {
 109                return -ERESTARTSYS;
 110        }
 111
 112        while (!s->dev || s->opened) {
 113                up (&s->mutex);
 114
 115                if (filp->f_flags & O_NONBLOCK) {
 116                        return -EBUSY;
 117                }
 118
 119                schedule_timeout (HZ / 2);
 120
 121                if (signal_pending (current)) {
 122                        return -EAGAIN;
 123                }
 124
 125                if (down_interruptible (&s->mutex)) {
 126                        return -ERESTARTSYS;
 127                }
 128        }
 129
 130        s->opened = 1;
 131        up (&s->mutex);
 132
 133        filp->f_pos = 0;
 134        filp->private_data = s;
 135
 136        return 0;
 137}
 138
 139static int
 140tiglusb_release (struct inode *inode, struct file *filp)
 141{
 142        ptiglusb_t s = (ptiglusb_t) filp->private_data;
 143
 144        if (down_interruptible (&s->mutex)) {
 145                return -ERESTARTSYS;
 146        }
 147
 148        s->state = _stopped;
 149        up (&s->mutex);
 150
 151        if (!s->remove_pending)
 152                clear_device (s->dev);
 153        else
 154                wake_up (&s->remove_ok);
 155
 156        s->opened = 0;
 157
 158        return 0;
 159}
 160
 161static ssize_t
 162tiglusb_read (struct file *filp, char *buf, size_t count, loff_t * f_pos)
 163{
 164        ptiglusb_t s = (ptiglusb_t) filp->private_data;
 165        ssize_t ret = 0;
 166        int bytes_to_read = 0;
 167        int bytes_read = 0;
 168        int result = 0;
 169        char buffer[BULK_RCV_MAX];
 170        unsigned int pipe;
 171
 172        if (*f_pos)
 173                return -ESPIPE;
 174
 175        if (s->remove_pending)
 176                return -EIO;
 177
 178        if (!s->dev)
 179                return -EIO;
 180
 181        bytes_to_read = (count >= BULK_RCV_MAX) ? BULK_RCV_MAX : count;
 182
 183        pipe = usb_rcvbulkpipe (s->dev, 1);
 184        result = usb_bulk_msg (s->dev, pipe, buffer, bytes_to_read,
 185                               &bytes_read, HZ * 10 / timeout);
 186        if (result == -ETIMEDOUT) {     /* NAK */
 187                if (!bytes_read)
 188                        dbg ("quirk !");
 189                warn ("tiglusb_read, NAK received.");
 190                ret = result;
 191                goto out;
 192        } else if (result == -EPIPE) {  /* STALL -- shouldn't happen */
 193                warn ("clear_halt request to remove STALL condition.");
 194                if (usb_clear_halt (s->dev, pipe))
 195                        err ("clear_halt, request failed");
 196                clear_device (s->dev);
 197                ret = result;
 198                goto out;
 199        } else if (result < 0) {        /* We should not get any I/O errors */
 200                err ("funky result: %d. Please notify maintainer.", result);
 201                ret = -EIO;
 202                goto out;
 203        }
 204
 205        if (copy_to_user (buf, buffer, bytes_read)) {
 206                ret = -EFAULT;
 207        }
 208
 209      out:
 210        return ret ? ret : bytes_read;
 211}
 212
 213static ssize_t
 214tiglusb_write (struct file *filp, const char *buf, size_t count, loff_t * f_pos)
 215{
 216        ptiglusb_t s = (ptiglusb_t) filp->private_data;
 217        ssize_t ret = 0;
 218        int bytes_to_write = 0;
 219        int bytes_written = 0;
 220        int result = 0;
 221        char buffer[BULK_SND_MAX];
 222        unsigned int pipe;
 223
 224        if (*f_pos)
 225                return -ESPIPE;
 226
 227        if (s->remove_pending)
 228                return -EIO;
 229
 230        if (!s->dev)
 231                return -EIO;
 232
 233        bytes_to_write = (count >= BULK_SND_MAX) ? BULK_SND_MAX : count;
 234        if (copy_from_user (buffer, buf, bytes_to_write)) {
 235                ret = -EFAULT;
 236                goto out;
 237        }
 238
 239        pipe = usb_sndbulkpipe (s->dev, 2);
 240        result = usb_bulk_msg (s->dev, pipe, buffer, bytes_to_write,
 241                               &bytes_written, HZ * 10 / timeout);
 242
 243        if (result == -ETIMEDOUT) {     /* NAK */
 244                warn ("tiglusb_write, NAK received.");
 245                ret = result;
 246                goto out;
 247        } else if (result == -EPIPE) {  /* STALL -- shouldn't happen */
 248                warn ("clear_halt request to remove STALL condition.");
 249                if (usb_clear_halt (s->dev, pipe))
 250                        err ("clear_halt, request failed");
 251                clear_device (s->dev);
 252                ret = result;
 253                goto out;
 254        } else if (result < 0) {        /* We should not get any I/O errors */
 255                warn ("funky result: %d. Please notify maintainer.", result);
 256                ret = -EIO;
 257                goto out;
 258        }
 259
 260        if (bytes_written != bytes_to_write) {
 261                ret = -EIO;
 262        }
 263
 264      out:
 265        return ret ? ret : bytes_written;
 266}
 267
 268static int
 269tiglusb_ioctl (struct inode *inode, struct file *filp,
 270               unsigned int cmd, unsigned long arg)
 271{
 272        ptiglusb_t s = (ptiglusb_t) filp->private_data;
 273        int ret = 0;
 274
 275        if (s->remove_pending)
 276                return -EIO;
 277
 278        if (down_interruptible (&s->mutex)) {
 279                return -ERESTARTSYS;
 280        }
 281
 282        if (!s->dev) {
 283                up (&s->mutex);
 284                return -EIO;
 285        }
 286
 287        switch (cmd) {
 288        case IOCTL_TIUSB_TIMEOUT:
 289                if (arg > 0)
 290                        timeout = (int)arg;
 291                else
 292                        ret = -EINVAL;
 293                break;
 294        case IOCTL_TIUSB_RESET_DEVICE:
 295                if (clear_device (s->dev))
 296                        ret = -EIO;
 297                break;
 298        case IOCTL_TIUSB_RESET_PIPES:
 299                if (clear_pipes (s->dev))
 300                        ret = -EIO;
 301                break;
 302        default:
 303                ret = -ENOTTY;
 304                break;
 305        }
 306
 307        up (&s->mutex);
 308
 309        return ret;
 310}
 311
 312/* ----- kernel module registering ------------------------------------ */
 313
 314static struct file_operations tiglusb_fops = {
 315        .owner =        THIS_MODULE,
 316        .llseek =       no_llseek,
 317        .read =         tiglusb_read,
 318        .write =        tiglusb_write,
 319        .ioctl =        tiglusb_ioctl,
 320        .open =         tiglusb_open,
 321        .release =      tiglusb_release,
 322};
 323
 324/* --- initialisation code ------------------------------------- */
 325
 326static void *
 327tiglusb_probe (struct usb_device *dev, unsigned int ifnum,
 328               const struct usb_device_id *id)
 329{
 330        int minor = -1;
 331        int i;
 332        ptiglusb_t s;
 333        char name[8];
 334
 335        dbg ("probing vendor id 0x%x, device id 0x%x ifnum:%d",
 336             dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum);
 337
 338        /*
 339         * We don't handle multiple configurations. As of version 0x0103 of
 340         * the TIGL hardware, there's only 1 configuration.
 341         */
 342
 343        if (dev->descriptor.bNumConfigurations != 1)
 344                return NULL;
 345
 346        if ((dev->descriptor.idProduct != 0xe001)
 347            && (dev->descriptor.idVendor != 0x451))
 348                return NULL;
 349
 350        if (usb_set_configuration (dev, dev->config[0].bConfigurationValue) < 0) {
 351                err ("tiglusb_probe: set_configuration failed");
 352                return NULL;
 353        }
 354
 355        /*
 356         * Find a tiglusb struct
 357         */
 358        for (i = 0; i < MAXTIGL; i++) {
 359                ptiglusb_t s = &tiglusb[i];
 360                if (!s->dev) {
 361                        minor = i;
 362                        break;
 363                }
 364        }
 365
 366        if (minor == -1)
 367                return NULL;
 368
 369        s = &tiglusb[minor];
 370
 371        down (&s->mutex);
 372        s->remove_pending = 0;
 373        s->dev = dev;
 374        up (&s->mutex);
 375        dbg ("bound to interface: %d", ifnum);
 376
 377        sprintf (name, "%d", s->minor);
 378        dbg ("registering to devfs : major = %d, minor = %d, node = %s",
 379             TIUSB_MAJOR, (TIUSB_MINOR + s->minor), name);
 380        s->devfs =
 381            devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, TIUSB_MAJOR,
 382                            TIUSB_MINOR + s->minor, S_IFCHR | S_IRUGO | S_IWUGO,
 383                            &tiglusb_fops, NULL);
 384
 385        /* Display firmware version */
 386        info ("firmware revision %i.%02x",
 387                dev->descriptor.bcdDevice >> 8,
 388                dev->descriptor.bcdDevice & 0xff);
 389
 390        return s;
 391}
 392
 393static void
 394tiglusb_disconnect (struct usb_device *dev, void *drv_context)
 395{
 396        ptiglusb_t s = (ptiglusb_t) drv_context;
 397
 398        if (!s || !s->dev)
 399                info ("bogus disconnect");
 400
 401        s->remove_pending = 1;
 402        wake_up (&s->wait);
 403        if (s->state == _started)
 404                sleep_on (&s->remove_ok);
 405        down (&s->mutex);
 406        s->dev = NULL;
 407        s->opened = 0;
 408
 409        devfs_unregister (s->devfs);
 410        s->devfs = NULL;
 411
 412        info ("device %d removed", s->minor);
 413
 414        up (&s->mutex);
 415}
 416
 417static struct usb_device_id tiglusb_ids[] = {
 418        {USB_DEVICE (0x0451, 0xe001)},
 419        {}
 420};
 421
 422MODULE_DEVICE_TABLE (usb, tiglusb_ids);
 423
 424static struct usb_driver tiglusb_driver = {
 425        .owner =        THIS_MODULE,
 426        .name =         "tiglusb",
 427        .probe =        tiglusb_probe,
 428        .disconnect =   tiglusb_disconnect,
 429        .id_table =     tiglusb_ids,
 430};
 431
 432/* --- initialisation code ------------------------------------- */
 433
 434#ifndef MODULE
 435/*
 436 * You can use 'tiusb=timeout' to set timeout.
 437 */
 438static int __init
 439tiglusb_setup (char *str)
 440{
 441        int ints[2];
 442
 443        str = get_options (str, ARRAY_SIZE (ints), ints);
 444
 445        if (ints[0] > 0) {
 446                if (ints[1] > 0)
 447                        timeout = ints[1];
 448                else
 449                        info ("tiglusb: wrong timeout value (0), using default value.");
 450        }
 451
 452        return 1;
 453}
 454#endif
 455
 456static int __init
 457tiglusb_init (void)
 458{
 459        unsigned u;
 460        int result;
 461
 462        /* initialize struct */
 463        for (u = 0; u < MAXTIGL; u++) {
 464                ptiglusb_t s = &tiglusb[u];
 465                memset (s, 0, sizeof (tiglusb_t));
 466                init_MUTEX (&s->mutex);
 467                s->dev = NULL;
 468                s->minor = u;
 469                s->opened = 0;
 470                init_waitqueue_head (&s->wait);
 471                init_waitqueue_head (&s->remove_ok);
 472        }
 473
 474        /* register device */
 475        if (register_chrdev (TIUSB_MAJOR, "tiglusb", &tiglusb_fops)) {
 476                err ("unable to get major %d", TIUSB_MAJOR);
 477                return -EIO;
 478        }
 479
 480        /* Use devfs, tree: /dev/ticables/usb/[0..3] */
 481        devfs_handle = devfs_mk_dir (NULL, "ticables/usb", NULL);
 482
 483        /* register USB module */
 484        result = usb_register (&tiglusb_driver);
 485        if (result < 0) {
 486                unregister_chrdev (TIUSB_MAJOR, "tiglusb");
 487                return -1;
 488        }
 489
 490        info (DRIVER_DESC ", version " DRIVER_VERSION);
 491
 492        return 0;
 493}
 494
 495static void __exit
 496tiglusb_cleanup (void)
 497{
 498        usb_deregister (&tiglusb_driver);
 499        devfs_unregister (devfs_handle);
 500        unregister_chrdev (TIUSB_MAJOR, "tiglusb");
 501}
 502
 503/* --------------------------------------------------------------------- */
 504
 505__setup ("tiusb=", tiglusb_setup);
 506module_init (tiglusb_init);
 507module_exit (tiglusb_cleanup);
 508
 509MODULE_AUTHOR (DRIVER_AUTHOR);
 510MODULE_DESCRIPTION (DRIVER_DESC);
 511MODULE_LICENSE (DRIVER_LICENSE);
 512
 513MODULE_PARM (timeout, "i");
 514MODULE_PARM_DESC (timeout, "Timeout in tenths of seconds (default=1.5 seconds)");
 515
 516/* --------------------------------------------------------------------- */
 517
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.