# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.603 -> 1.604 # (new) -> 1.1 drivers/usb/tiglusb.c # (new) -> 1.1 Documentation/usb/silverlink.txt # (new) -> 1.1 drivers/usb/tiglusb.h # (new) -> 1.1 include/linux/ticable.h # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/08/05 greg@kroah.com 1.604 # USB: added tiglusb driver # -------------------------------------------- # diff -Nru a/Documentation/usb/silverlink.txt b/Documentation/usb/silverlink.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/usb/silverlink.txt Mon Aug 5 14:46:47 2002 @@ -0,0 +1,76 @@ +------------------------------------------------------------------------- +Readme for Linux device driver for the Texas Instruments SilverLink cable +------------------------------------------------------------------------- + +Author: Romain Liévin & Julien Blache +Homepage: http://lpg.ticalc.org/prj_usb + +INTRODUCTION: + +This is a driver for the TI-GRAPH LINK USB (aka SilverLink) cable, a cable +designed by TI for connecting their TI8x/9x calculators to a computer +(PC or Mac usually). + +If you need more information, please visit the 'SilverLink drivers' homepage +at the above URL. + +WHAT YOU NEED: + +A TI calculator of course and a program capable to communicate with your +calculator. +TiLP will work for sure (since I am his developer !). yal92 may be able to use +it by changing tidev for tiglusb (may require some hacking...). + +HOW TO USE IT: + +You must have first compiled USB support, support for your specific USB host +controller (UHCI or OHCI). + +Next, (as root) from your appropriate modules directory (lib/modules/2.5.XX): + + insmod usb/usbcore.o + insmod usb/usb-uhci.o insmod usb/ohci-hcd.o + insmod tiglusb.o + +If it is not already there (it usually is), create the device: + + mknod /dev/tiglusb0 c 115 16 + +You will have to set permissions on this device to allow you to read/write +from it: + + chmod 666 /dev/tiglusb0 + +Now you are ready to run a linking program such as TiLP. Be sure to configure +it properly (RTFM). + +MODULE PARAMETERS: + + You can set these with: insmod tiglusb NAME=VALUE + There is currently no way to set these on a per-cable basis. + + NAME: timeout + TYPE: integer + DEFAULT: 15 + DESC: Timeout value in tenth of seconds. If no data is available once this + time has expired then the driver will return with a timeout error. + +QUIRKS: + +The following problem seems to be specific to the link cable since it appears +on all platforms (Linux, Windows, Mac OS-X). + +In some very particular cases, the driver returns with success but +without any data. The application should retry a read operation at least once. + +HOW TO CONTACT US: + +You can email me at roms@lpg.ticalc.org. Please prefix the subject line +with "TIGLUSB: " so that I am certain to notice your message. +You can also mail JB at jb@jblache.org: he has written the first release of +this driver but he better knows the Mac OS-X driver. + +CREDITS: + +The code is based on dabusb.c, printer.c and scanner.c ! +The driver has been developed independantly of Texas Instruments. diff -Nru a/drivers/usb/tiglusb.c b/drivers/usb/tiglusb.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/tiglusb.c Mon Aug 5 14:46:47 2002 @@ -0,0 +1,514 @@ +/* Hey EMACS -*- linux-c -*- + * + * tiglusb -- Texas Instruments' USB GraphLink (aka SilverLink) driver. + * Target: Texas Instruments graphing calculators (http://lpg.ticalc.org). + * + * Copyright (C) 2001-2002: + * Romain Lievin + * Julien BLACHE + * under the terms of the GNU General Public License. + * + * Based on dabusb.c, printer.c & scanner.c + * + * Please see the file: linux/Documentation/usb/SilverLink.txt + * and the website at: http://lpg.ticalc.org/prj_usb/ + * for more info. + * + * History : + * 16/07/2002 : v1.04 -- Julien BLACHE + * + removed useless usblp_cleanup() + * + removed {un,}lock_kernel() as suggested on lkml + * + inlined clear_pipes() (used once) + * + inlined clear_device() (small, used twice) + * + removed tiglusb_find_struct() (used once, simple code) + * + replaced down() with down_interruptible() wherever possible + * + fixed double unregistering wrt devfs, causing devfs + * to force an oops when the device is deconnected + * + removed unused fields from struct tiglusb_t + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "tiglusb.h" + +/* + * Version Information + */ +#define DRIVER_VERSION "1.04" +#define DRIVER_AUTHOR "Romain Lievin & Julien Blache " +#define DRIVER_DESC "TI-GRAPH LINK USB (aka SilverLink) driver" +#define DRIVER_LICENSE "GPL" + +/* ----- global variables --------------------------------------------- */ + +static tiglusb_t tiglusb[MAXTIGL]; +static int timeout = TIMAXTIME; /* timeout in tenth of seconds */ + +static devfs_handle_t devfs_handle; + +/*---------- misc functions ------------------------------------------- */ + +/* + * Re-initialize device + */ +static inline int +clear_device (struct usb_device *dev) +{ + if (usb_set_configuration (dev, dev->config[0].bConfigurationValue) < 0) { + err ("clear_device failed"); + return -1; + } + + return 0; +} + +/* + * Clear input & output pipes (endpoints) + */ +static inline int +clear_pipes (struct usb_device *dev) +{ + unsigned int pipe; + + pipe = usb_sndbulkpipe (dev, 1); + if (usb_clear_halt (dev, usb_pipeendpoint (pipe))) { + err ("clear_pipe (r), request failed"); + return -1; + } + + pipe = usb_sndbulkpipe (dev, 2); + if (usb_clear_halt (dev, usb_pipeendpoint (pipe))) { + err ("clear_pipe (w), request failed"); + return -1; + } + + return 0; +} + +/* ----- file operations functions--------------------------------------- */ + +static int +tiglusb_open (struct inode *inode, struct file *filp) +{ + int devnum = minor (inode->i_rdev); + ptiglusb_t s; + + if (devnum < TIUSB_MINOR || devnum >= (TIUSB_MINOR + MAXTIGL)) + return -EIO; + + s = &tiglusb[devnum - TIUSB_MINOR]; + + if (down_interruptible (&s->mutex)) { + return -ERESTARTSYS; + } + + while (!s->dev || s->opened) { + up (&s->mutex); + + if (filp->f_flags & O_NONBLOCK) { + return -EBUSY; + } + + schedule_timeout (HZ / 2); + + if (signal_pending (current)) { + return -EAGAIN; + } + + if (down_interruptible (&s->mutex)) { + return -ERESTARTSYS; + } + } + + s->opened = 1; + up (&s->mutex); + + filp->f_pos = 0; + filp->private_data = s; + + return 0; +} + +static int +tiglusb_release (struct inode *inode, struct file *filp) +{ + ptiglusb_t s = (ptiglusb_t) filp->private_data; + + if (down_interruptible (&s->mutex)) { + return -ERESTARTSYS; + } + + s->state = _stopped; + up (&s->mutex); + + if (!s->remove_pending) + clear_device (s->dev); + else + wake_up (&s->remove_ok); + + s->opened = 0; + + return 0; +} + +static ssize_t +tiglusb_read (struct file *filp, char *buf, size_t count, loff_t * f_pos) +{ + ptiglusb_t s = (ptiglusb_t) filp->private_data; + ssize_t ret = 0; + int bytes_to_read = 0; + int bytes_read = 0; + int result = 0; + char buffer[BULK_RCV_MAX]; + unsigned int pipe; + + if (*f_pos) + return -ESPIPE; + + if (s->remove_pending) + return -EIO; + + if (!s->dev) + return -EIO; + + bytes_to_read = (count >= BULK_RCV_MAX) ? BULK_RCV_MAX : count; + + pipe = usb_rcvbulkpipe (s->dev, 1); + result = usb_bulk_msg (s->dev, pipe, buffer, bytes_to_read, + &bytes_read, HZ / (timeout / 10)); + if (result == -ETIMEDOUT) { /* NAK */ + ret = result; + if (!bytes_read) { + dbg ("quirk !"); + } + warn ("tiglusb_read, NAK received."); + goto out; + } else if (result == -EPIPE) { /* STALL -- shouldn't happen */ + warn ("clear_halt request to remove STALL condition."); + if (usb_clear_halt (s->dev, usb_pipeendpoint (pipe))) + err ("clear_halt, request failed"); + clear_device (s->dev); + ret = result; + goto out; + } else if (result < 0) { /* We should not get any I/O errors */ + err ("funky result: %d. Please notify maintainer.", result); + ret = -EIO; + goto out; + } + + if (copy_to_user (buf, buffer, bytes_read)) { + ret = -EFAULT; + } + + out: + return ret ? ret : bytes_read; +} + +static ssize_t +tiglusb_write (struct file *filp, const char *buf, size_t count, loff_t * f_pos) +{ + ptiglusb_t s = (ptiglusb_t) filp->private_data; + ssize_t ret = 0; + int bytes_to_write = 0; + int bytes_written = 0; + int result = 0; + char buffer[BULK_SND_MAX]; + unsigned int pipe; + + if (*f_pos) + return -ESPIPE; + + if (s->remove_pending) + return -EIO; + + if (!s->dev) + return -EIO; + + bytes_to_write = (count >= BULK_SND_MAX) ? BULK_SND_MAX : count; + if (copy_from_user (buffer, buf, bytes_to_write)) { + ret = -EFAULT; + goto out; + } + + pipe = usb_sndbulkpipe (s->dev, 2); + result = usb_bulk_msg (s->dev, pipe, buffer, bytes_to_write, + &bytes_written, HZ / (timeout / 10)); + + if (result == -ETIMEDOUT) { /* NAK */ + warn ("tiglusb_write, NAK received."); + ret = result; + goto out; + } else if (result == -EPIPE) { /* STALL -- shouldn't happen */ + warn ("clear_halt request to remove STALL condition."); + if (usb_clear_halt (s->dev, usb_pipeendpoint (pipe))) + err ("clear_halt, request failed"); + clear_device (s->dev); + ret = result; + goto out; + } else if (result < 0) { /* We should not get any I/O errors */ + warn ("funky result: %d. Please notify maintainer.", result); + ret = -EIO; + goto out; + } + + if (bytes_written != bytes_to_write) { + ret = -EIO; + } + + out: + return ret ? ret : bytes_written; +} + +static int +tiglusb_ioctl (struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + ptiglusb_t s = (ptiglusb_t) filp->private_data; + int ret = 0; + + if (s->remove_pending) + return -EIO; + + if (down_interruptible (&s->mutex)) { + return -ERESTARTSYS; + } + + if (!s->dev) { + up (&s->mutex); + return -EIO; + } + + switch (cmd) { + case IOCTL_TIUSB_TIMEOUT: + timeout = arg; // timeout value in tenth of seconds + break; + case IOCTL_TIUSB_RESET_DEVICE: + dbg ("IOCTL_TIGLUSB_RESET_DEVICE"); + if (clear_device (s->dev)) + ret = -EIO; + break; + case IOCTL_TIUSB_RESET_PIPES: + dbg ("IOCTL_TIGLUSB_RESET_PIPES"); + if (clear_pipes (s->dev)) + ret = -EIO; + break; + default: + ret = -ENOTTY; + break; + } + + up (&s->mutex); + + return ret; +} + +/* ----- kernel module registering ------------------------------------ */ + +static struct file_operations tiglusb_fops = { + .llseek = no_llseek, + .read = tiglusb_read, + .write = tiglusb_write, + .ioctl = tiglusb_ioctl, + .open = tiglusb_open, + .release = tiglusb_release, +}; + +/* --- initialisation code ------------------------------------- */ + +static void * +tiglusb_probe (struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) +{ + int minor = -1; + int i; + ptiglusb_t s; + char name[8]; + + dbg ("probing vendor id 0x%x, device id 0x%x ifnum:%d", + dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); + + /* + * We don't handle multiple configurations. As of version 0x0103 of + * the TIGL hardware, there's only 1 configuration. + */ + + if (dev->descriptor.bNumConfigurations != 1) + return NULL; + + if ((dev->descriptor.idProduct != 0xe001) + && (dev->descriptor.idVendor != 0x451)) + return NULL; + + if (usb_set_configuration (dev, dev->config[0].bConfigurationValue) < 0) { + err ("tiglusb_probe: set_configuration failed"); + return NULL; + } + + /* + * Find a tiglusb struct + */ + for (i = 0; i < MAXTIGL; i++) { + ptiglusb_t s = &tiglusb[i]; + if (!s->dev) { + minor = i; + break; + } + } + + if (minor == -1) + return NULL; + + s = &tiglusb[minor]; + + down (&s->mutex); + s->remove_pending = 0; + s->dev = dev; + up (&s->mutex); + dbg ("bound to interface: %d", ifnum); + + sprintf (name, "%d", s->minor); + dbg ("registering to devfs : major = %d, minor = %d, node = %s", + TIUSB_MAJOR, (TIUSB_MINOR + s->minor), name); + s->devfs = + devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, TIUSB_MAJOR, + TIUSB_MINOR + s->minor, S_IFCHR | S_IRUGO | S_IWUGO, + &tiglusb_fops, NULL); + + /* Display firmware version */ + info ("link cable version %i.%02x", + dev->descriptor.bcdDevice >> 8, + dev->descriptor.bcdDevice & 0xff); + + return s; +} + +static void +tiglusb_disconnect (struct usb_device *dev, void *drv_context) +{ + ptiglusb_t s = (ptiglusb_t) drv_context; + + if (!s || !s->dev) + info ("bogus disconnect"); + + s->remove_pending = 1; + wake_up (&s->wait); + if (s->state == _started) + sleep_on (&s->remove_ok); + down (&s->mutex); + s->dev = NULL; + s->opened = 0; + + devfs_unregister (s->devfs); + s->devfs = NULL; + + info ("device %d removed", s->minor); + + up (&s->mutex); +} + +static struct usb_device_id tiglusb_ids[] = { + {USB_DEVICE (0x0451, 0xe001)}, + {} +}; + +MODULE_DEVICE_TABLE (usb, tiglusb_ids); + +static struct usb_driver tiglusb_driver = { + .name = "tiglusb", + .probe = tiglusb_probe, + .disconnect = tiglusb_disconnect, + .id_table = tiglusb_ids, +}; + +/* --- initialisation code ------------------------------------- */ + +#ifndef MODULE +/* + * You can use 'tiusb=timeout' + */ +static int __init +tiglusb_setup (char *str) +{ + int ints[2]; + + str = get_options (str, ARRAY_SIZE (ints), ints); + + if (ints[0] > 0) { + timeout = ints[1]; + } + + return 1; +} +#endif + +static int __init +tiglusb_init (void) +{ + unsigned u; + int result; + + /* initialize struct */ + for (u = 0; u < MAXTIGL; u++) { + ptiglusb_t s = &tiglusb[u]; + memset (s, 0, sizeof (tiglusb_t)); + init_MUTEX (&s->mutex); + s->dev = NULL; + s->minor = u; + s->opened = 0; + init_waitqueue_head (&s->wait); + init_waitqueue_head (&s->remove_ok); + } + + /* register device */ + if (devfs_register_chrdev (TIUSB_MAJOR, "tiglusb", &tiglusb_fops)) { + err ("unable to get major %d", TIUSB_MAJOR); + return -EIO; + } + + /* Use devfs, tree: /dev/ticables/usb/[0..3] */ + devfs_handle = devfs_mk_dir (NULL, "ticables/usb", NULL); + + /* register USB module */ + result = usb_register (&tiglusb_driver); + if (result < 0) { + devfs_unregister_chrdev (TIUSB_MAJOR, "tiglusb"); + return -1; + } + + info (DRIVER_DESC ", " DRIVER_VERSION); + + return 0; +} + +static void __exit +tiglusb_cleanup (void) +{ + usb_deregister (&tiglusb_driver); + devfs_unregister (devfs_handle); + devfs_unregister_chrdev (TIUSB_MAJOR, "tiglusb"); +} + +/* --------------------------------------------------------------------- */ + +__setup ("tiusb=", tiglusb_setup); +module_init (tiglusb_init); +module_exit (tiglusb_cleanup); + +MODULE_AUTHOR (DRIVER_AUTHOR); +MODULE_DESCRIPTION (DRIVER_DESC); +MODULE_LICENSE (DRIVER_LICENSE); + +MODULE_PARM (timeout, "i"); +MODULE_PARM_DESC (timeout, "Timeout (default=1.5 seconds)"); + +/* --------------------------------------------------------------------- */ diff -Nru a/drivers/usb/tiglusb.h b/drivers/usb/tiglusb.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/tiglusb.h Mon Aug 5 14:46:47 2002 @@ -0,0 +1,48 @@ +/* Hey EMACS -*- linux-c -*- + * + * tiglusb - low level driver for SilverLink cable + * + * Copyright (C) 2000-2002, Romain Lievin + * under the terms of the GNU General Public License. + * + * Redistribution of this file is permitted under the terms of the GNU + * Public License (GPL) + */ + +#ifndef _TIGLUSB_H +#define _TIGLUSB_H + +/* + * Max. number of devices supported + */ +#define MAXTIGL 16 + +/* + * Max. packetsize for IN and OUT pipes + */ +#define BULK_RCV_MAX 32 +#define BULK_SND_MAX 32 + +/* + * The driver context... + */ + +typedef enum { _stopped=0, _started } driver_state_t; + +typedef struct +{ + struct usb_device *dev; /* USB device handle */ + struct semaphore mutex; /* locks this struct */ + + wait_queue_head_t wait; /* for timed waits */ + wait_queue_head_t remove_ok; + + int minor; /* which minor dev #? */ + devfs_handle_t devfs; /* devfs device */ + + driver_state_t state; /* started/stopped */ + int opened; /* tru if open */ + int remove_pending; +} tiglusb_t, *ptiglusb_t; + +#endif diff -Nru a/include/linux/ticable.h b/include/linux/ticable.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/linux/ticable.h Mon Aug 5 14:46:47 2002 @@ -0,0 +1,42 @@ +/* Hey EMACS -*- linux-c -*- + * + * tipar/tiser/tiusb - low level driver for handling link cables + * designed for Texas Instruments graphing calculators. + * + * Copyright (C) 2000-2002, Romain Lievin + * + * Redistribution of this file is permitted under the terms of the GNU + * Public License (GPL) + */ + +#ifndef _TICABLE_H +#define _TICABLE_H 1 + +/* Internal default constants for the kernel module */ +#define TIMAXTIME 15 /* 1.5 seconds */ +#define IO_DELAY 10 /* 10 micro-seconds */ + +/* Major & minor number for character devices */ +#define TIPAR_MAJOR 115 /* 0 to 7 */ +#define TIPAR_MINOR 0 + +#define TISER_MAJOR 115 /* 8 to 15 */ +#define TISER_MINOR 8 + +#define TIUSB_MAJOR 115 /* 16 to 31 */ +#define TIUSB_MINOR 16 + +/* + * Request values for the 'ioctl' function. + */ +#define IOCTL_TIPAR_DELAY _IOW('p', 0xa8, int) /* set delay */ +#define IOCTL_TIPAR_TIMEOUT _IOW('p', 0xa9, int) /* set timeout */ + +#define IOCTL_TISER_DELAY _IOW('p', 0xa0, int) /* set delay */ +#define IOCTL_TISER_TIMEOUT _IOW('p', 0xa1, int) /* set timeout */ + +#define IOCTL_TIUSB_TIMEOUT _IOW('N', 0x20, int) /* set timeout */ +#define IOCTL_TIUSB_RESET_DEVICE _IOW('N', 0x21, int) /* reset device */ +#define IOCTL_TIUSB_RESET_PIPES _IOW('N', 0x22, int) /* reset both pipes*/ + +#endif /* TICABLE_H */