# 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.579 -> 1.580 # MAINTAINERS 1.68 -> 1.69 # (new) -> 1.1 drivers/usb/tiglusb.c # (new) -> 1.1 include/linux/ticable.h # (new) -> 1.1 Documentation/usb/silverlink.txt # (new) -> 1.1 drivers/usb/tiglusb.h # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/04/03 Romain.Lievin@esisar.inpg.fr 1.580 # USB tiusb # # added tiusb driver # some tweaks to the driver done by greg@kroah.com # -------------------------------------------- # 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 Wed Apr 3 16:39:05 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/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS Wed Apr 3 16:39:05 2002 +++ b/MAINTAINERS Wed Apr 3 16:39:05 2002 @@ -1502,6 +1502,13 @@ M: hch@infradead.org S: Maintained +TI GRAPH LINK USB (SilverLink) CABLE DRIVER +P: Romain Lievin +M: roms@lpg.ticalc.org +P: Julien Blache +M: jb@technologeek.org +S: Maintained + TLAN NETWORK DRIVER P: Torben Mathiasen M: torben.mathiasen@compaq.com @@ -1660,6 +1667,7 @@ M: petkan@users.sourceforge.net L: linux-usb-users@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net +W: http://pegasus2.sourceforge.net/ S: Maintained USB PRINTER DRIVER @@ -1667,6 +1675,14 @@ M: vojtech@suse.cz L: linux-usb-users@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net +S: Maintained + +USB RTL8150 DRIVER +P: Petko Manolov +M: petkan@users.sourceforge.net +L: linux-usb-users@lists.sourceforge.net +L: linux-usb-devel@lists.sourceforge.net +W: http://pegasus2.sourceforge.net/ S: Maintained USB SE401 DRIVER 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 Wed Apr 3 16:39:05 2002 @@ -0,0 +1,495 @@ +/* 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "tiglusb.h" + +/* + * Version Information + */ +#define DRIVER_VERSION "1.02" +#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 ------------------------------------------- */ + +/* Unregister device */ +static void usblp_cleanup (tiglusb_t * s) +{ + devfs_unregister (s->devfs); + //memset(tiglusb[s->minor], 0, sizeof(tiglusb_t)); + info ("tiglusb%d removed", s->minor); +} + +/* Re-initialize device */ +static int clear_device (struct usb_device *dev) +{ + if (usb_set_configuration (dev, dev->config[0].bConfigurationValue) < 0) { + printk ("tiglusb: clear_device failed\n"); + return -1; + } + + return 0; +} + +/* Clear input & output pipes (endpoints) */ +static int clear_pipes (struct usb_device *dev) +{ + unsigned int pipe; + + pipe = usb_sndbulkpipe (dev, 1); + if (usb_clear_halt (dev, usb_pipeendpoint (pipe))) { + printk ("tiglusb: clear_pipe (r), request failed\n"); + return -1; + } + + pipe = usb_sndbulkpipe (dev, 2); + if (usb_clear_halt (dev, usb_pipeendpoint (pipe))) { + printk ("tiglusb: clear_pipe (w), request failed\n"); + return -1; + } + + return 0; +} + +/* ----- kernel module functions--------------------------------------- */ + +static int tiglusb_open (struct inode *inode, struct file *file) +{ + int devnum = minor (inode->i_rdev); + ptiglusb_t s; + + if (devnum < TIUSB_MINOR || devnum >= (TIUSB_MINOR + MAXTIGL)) + return -EIO; + + s = &tiglusb[devnum - TIUSB_MINOR]; + + down (&s->mutex); + + while (!s->dev || s->opened) { + up (&s->mutex); + + if (file->f_flags & O_NONBLOCK) { + return -EBUSY; + } + schedule_timeout (HZ / 2); + + if (signal_pending (current)) { + return -EAGAIN; + } + down (&s->mutex); + } + + s->opened = 1; + up (&s->mutex); + + file->f_pos = 0; + file->private_data = s; + + return 0; +} + +static int tiglusb_release (struct inode *inode, struct file *file) +{ + ptiglusb_t s = (ptiglusb_t) file->private_data; + + lock_kernel (); + down (&s->mutex); + s->state = _stopped; + up (&s->mutex); + + if (!s->remove_pending) + clear_device (s->dev); + else + wake_up (&s->remove_ok); + + s->opened = 0; + unlock_kernel (); + + return 0; +} + +static ssize_t tiglusb_read (struct file *file, char *buf, size_t count, loff_t * ppos) +{ + ptiglusb_t s = (ptiglusb_t) file->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 (*ppos) + 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) { + printk ("quirk !\n"); + } + warn ("tiglusb_read, NAK received."); + goto out; + } else if (result == -EPIPE) { /* STALL -- shouldn't happen */ + warn ("CLEAR_FEATURE request to remove STALL condition.\n"); + if (usb_clear_halt (s->dev, usb_pipeendpoint (pipe))) + warn ("send_packet, request failed\n"); + //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 (copy_to_user (buf, buffer, bytes_read)) { + ret = -EFAULT; + goto out; + } + + out: + return ret ? ret : bytes_read; +} + +static ssize_t tiglusb_write (struct file *file, const char *buf, size_t count, loff_t * ppos) +{ + ptiglusb_t s = (ptiglusb_t) file->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 (*ppos) + 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_FEATURE request to remove STALL condition."); + if (usb_clear_halt (s->dev, usb_pipeendpoint (pipe))) + warn ("send_packet, request failed\n"); + //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; + goto out; + } + + out: + return ret ? ret : bytes_written; +} + +static int tiglusb_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + ptiglusb_t s = (ptiglusb_t) file->private_data; + int ret = 0; + + if (s->remove_pending) + return -EIO; + + down (&s->mutex); + + 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: + printk (KERN_DEBUG "IOCTL_TIGLUSB_RESET_DEVICE\n"); + if (clear_device (s->dev)) + ret = -EIO; + break; + case IOCTL_TIUSB_RESET_PIPES: + printk (KERN_DEBUG "IOCTL_TIGLUSB_RESET_PIPES\n"); + 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 = { + owner: THIS_MODULE, + llseek: no_llseek, + read: tiglusb_read, + write: tiglusb_write, + ioctl: tiglusb_ioctl, + open: tiglusb_open, + release: tiglusb_release, +}; + +static int tiglusb_find_struct (void) +{ + int u; + + for (u = 0; u < MAXTIGL; u++) { + ptiglusb_t s = &tiglusb[u]; + if (!s->dev) + return u; + } + + return -1; +} + +/* --- initialisation code ------------------------------------- */ + +static void *tiglusb_probe (struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) +{ + int minor; + ptiglusb_t s; + char name[8]; + + printk ("tiglusb: probing vendor id 0x%x, device id 0x%x ifnum:%d\n", + 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) { + printk ("tiglusb_probe: set_configuration failed\n"); + return NULL; + } + + minor = tiglusb_find_struct (); + 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); + printk ("tiglusb: registering to devfs : major = %d, minor = %d, node = %s\n", 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 */ + printk ("tiglusb: link cable version %i.%02x\n", + 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) + printk ("bogus disconnect"); + + s->remove_pending = 1; + wake_up (&s->wait); + if (s->state == _started) + sleep_on (&s->remove_ok); + s->dev = NULL; + s->opened = 0; + + /* cleanup now or later, on close */ + if (!s->opened) + usblp_cleanup (s); + else + up (&s->mutex); + + /* unregister device */ + devfs_unregister (s->devfs); + s->devfs = NULL; + printk ("tiglusb: device disconnected\n"); +} + +static struct usb_device_id tiglusb_ids[] = { + {USB_DEVICE (0x0451, 0xe001)}, + {} +}; + +MODULE_DEVICE_TABLE (usb, tiglusb_ids); + +static struct usb_driver tiglusb_driver = { + owner: THIS_MODULE, + name: "tiglusb", + probe: tiglusb_probe, + disconnect: tiglusb_disconnect, + id_table: tiglusb_ids, +}; + +/* --- initialisation code ------------------------------------- */ + +#ifndef MODULE +/* You must set these - there is no sane way to probe for this cable. + * You can use 'tipar=timeout,delay' to set these now. */ +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)) { + printk ("tiglusb: unable to get major %d\n", 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 ("tipar=", tiglusb_setup); +module_init (tiglusb_init); +module_exit (tiglusb_cleanup); + +MODULE_AUTHOR (DRIVER_AUTHOR); +MODULE_DESCRIPTION (DRIVER_DESC); +MODULE_LICENSE (DRIVER_LICENSE); + +EXPORT_NO_SYMBOLS; + +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 Wed Apr 3 16:39:05 2002 @@ -0,0 +1,55 @@ +/* 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 */ + struct semaphore sem; + + 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; + + char rd_buf[BULK_RCV_MAX]; /* read buffer */ + char wr_buf[BULK_SND_MAX]; /* write buffer */ + +} tiglusb_t, *ptiglusb_t; + +extern devfs_handle_t usb_devfs_handle; /* /dev/usb dir. */ + +#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 Wed Apr 3 16:39:05 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 */