# 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.509 -> 1.510 # drivers/usb/misc/Makefile 1.7 -> 1.8 # drivers/usb/Config.in 1.31 -> 1.32 # drivers/usb/Makefile 1.34 -> 1.35 # (new) -> 1.1 drivers/usb/misc/usblcd.c # (new) -> 1.1 drivers/usb/misc/Config.in # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/08/21 greg@kroah.com 1.510 # USB: added usblcd driver # # Ported it from 2.4, any breakage is my fault :) # -------------------------------------------- # diff -Nru a/drivers/usb/Config.in b/drivers/usb/Config.in --- a/drivers/usb/Config.in Wed Aug 21 15:45:41 2002 +++ b/drivers/usb/Config.in Wed Aug 21 15:45:41 2002 @@ -33,12 +33,6 @@ source drivers/usb/serial/Config.in - comment 'USB Miscellaneous drivers' - dep_tristate ' EMI 2|6 USB Audio interface support' CONFIG_USB_EMI26 $CONFIG_USB_AUDIO - dep_tristate ' Texas Instruments Graph Link USB (aka SilverLink) cable support' CONFIG_USB_TIGL $CONFIG_USB - dep_tristate ' USB Auerswald ISDN support (EXPERIMENTAL)' CONFIG_USB_AUERSWALD $CONFIG_USB $CONFIG_EXPERIMENTAL - dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB $CONFIG_EXPERIMENTAL - dep_tristate ' Tieman Voyager USB Braille display support (EXPERIMENTAL)' CONFIG_USB_BRLVGER $CONFIG_USB $CONFIG_EXPERIMENTAL - + source drivers/usb/misc/Config.in fi endmenu diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile --- a/drivers/usb/Makefile Wed Aug 21 15:45:41 2002 +++ b/drivers/usb/Makefile Wed Aug 21 15:45:41 2002 @@ -57,6 +57,7 @@ obj-$(CONFIG_USB_AUERSWALD) += misc/ obj-$(CONFIG_USB_BRLVGER) += misc/ obj-$(CONFIG_USB_EMI26) += misc/ +obj-$(CONFIG_USB_LCD) += misc/ obj-$(CONFIG_USB_RIO500) += misc/ obj-$(CONFIG_USB_TIGL) += misc/ obj-$(CONFIG_USB_USS720) += misc/ diff -Nru a/drivers/usb/misc/Config.in b/drivers/usb/misc/Config.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/misc/Config.in Wed Aug 21 15:45:41 2002 @@ -0,0 +1,10 @@ +# +# USB Miscellaneous driver configuration +# +comment 'USB Miscellaneous drivers' +dep_tristate ' EMI 2|6 USB Audio interface support' CONFIG_USB_EMI26 $CONFIG_USB_AUDIO +dep_tristate ' Texas Instruments Graph Link USB (aka SilverLink) cable support' CONFIG_USB_TIGL $CONFIG_USB +dep_tristate ' USB Auerswald ISDN support (EXPERIMENTAL)' CONFIG_USB_AUERSWALD $CONFIG_USB $CONFIG_EXPERIMENTAL +dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB $CONFIG_EXPERIMENTAL +dep_tristate ' Tieman Voyager USB Braille display support (EXPERIMENTAL)' CONFIG_USB_BRLVGER $CONFIG_USB $CONFIG_EXPERIMENTAL +dep_tristate ' USB LCD driver support' CONFIG_USB_LCD $CONFIG_USB diff -Nru a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile --- a/drivers/usb/misc/Makefile Wed Aug 21 15:45:41 2002 +++ b/drivers/usb/misc/Makefile Wed Aug 21 15:45:41 2002 @@ -6,6 +6,7 @@ obj-$(CONFIG_USB_AUERSWALD) += auerswald.o obj-$(CONFIG_USB_BRLVGER) += brlvger.o obj-$(CONFIG_USB_EMI26) += emi26.o +obj-$(CONFIG_USB_LCD) += usblcd.o obj-$(CONFIG_USB_RIO500) += rio500.o obj-$(CONFIG_USB_TIGL) += tiglusb.o obj-$(CONFIG_USB_USS720) += uss720.o diff -Nru a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/misc/usblcd.c Wed Aug 21 15:45:41 2002 @@ -0,0 +1,354 @@ +/***************************************************************************** + * USBLCD Kernel Driver * + * See http://www.usblcd.de for Hardware and Documentation. * + * Version 1.01 * + * (C) 2002 Adams IT Services * + * * + * This file is licensed under the GPL. See COPYING in the package. * + * Based on rio500.c by Cesar Miquel (miquel@df.uba.ar) which is based on * + * hp_scanner.c by David E. Nelson (dnelson@jump.net) * + * * + * 23.7.02 RA changed minor device number to the official assigned one * + *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "USBLCD Driver Version 1.01" + +#define USBLCD_MINOR 132 + +#define IOCTL_GET_HARD_VERSION 1 +#define IOCTL_GET_DRV_VERSION 2 + +/* stall/wait timeout for USBLCD */ +#define NAK_TIMEOUT (HZ) + +#define IBUF_SIZE 0x1000 +#define OBUF_SIZE 0x10000 + +struct lcd_usb_data { + struct usb_device *lcd_dev; /* init: probe_lcd */ + unsigned int ifnum; /* Interface number of the USB device */ + int minor; /* minor number for this device */ + int isopen; /* nz if open */ + int present; /* Device is present on the bus */ + char *obuf, *ibuf; /* transfer buffers */ + char bulk_in_ep, bulk_out_ep; /* Endpoint assignments */ + wait_queue_head_t wait_q; /* for timeouts */ +}; + +static struct lcd_usb_data lcd_instance; + +static int open_lcd(struct inode *inode, struct file *file) +{ + struct lcd_usb_data *lcd = &lcd_instance; + + if (lcd->isopen || !lcd->present) { + return -EBUSY; + } + lcd->isopen = 1; + + init_waitqueue_head(&lcd->wait_q); + + info("USBLCD opened."); + + return 0; +} + +static int close_lcd(struct inode *inode, struct file *file) +{ + struct lcd_usb_data *lcd = &lcd_instance; + + lcd->isopen = 0; + + info("USBLCD closed."); + return 0; +} + +static int +ioctl_lcd(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct lcd_usb_data *lcd = &lcd_instance; + int i; + char buf[30]; + + /* Sanity check to make sure lcd is connected, powered, etc */ + if (lcd == NULL || + lcd->present == 0 || + lcd->lcd_dev == NULL) + return -1; + + switch (cmd) { + case IOCTL_GET_HARD_VERSION: + i = (lcd->lcd_dev)->descriptor.bcdDevice; + sprintf(buf,"%1d%1d.%1d%1d",(i & 0xF000)>>12,(i & 0xF00)>>8, + (i & 0xF0)>>4,(i & 0xF)); + if (copy_to_user((void *)arg,buf,strlen(buf))!=0) + return -EFAULT; + break; + case IOCTL_GET_DRV_VERSION: + sprintf(buf,DRIVER_VERSION); + if (copy_to_user((void *)arg,buf,strlen(buf))!=0) + return -EFAULT; + break; + default: + return -ENOIOCTLCMD; + break; + } + + return 0; +} + +static ssize_t +write_lcd(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + struct lcd_usb_data *lcd = &lcd_instance; + + unsigned long copy_size; + unsigned long bytes_written = 0; + unsigned int partial; + + int result = 0; + int maxretry; + + /* Sanity check to make sure lcd is connected, powered, etc */ + if (lcd == NULL || + lcd->present == 0 || + lcd->lcd_dev == NULL) + return -1; + + do { + unsigned long thistime; + char *obuf = lcd->obuf; + + thistime = copy_size = + (count >= OBUF_SIZE) ? OBUF_SIZE : count; + if (copy_from_user(lcd->obuf, buffer, copy_size)) + return -EFAULT; + maxretry = 5; + while (thistime) { + if (!lcd->lcd_dev) + return -ENODEV; + if (signal_pending(current)) { + return bytes_written ? bytes_written : -EINTR; + } + + result = usb_bulk_msg(lcd->lcd_dev, + usb_sndbulkpipe(lcd->lcd_dev, 1), + obuf, thistime, &partial, 10 * HZ); + + dbg("write stats: result:%d thistime:%lu partial:%u", + result, thistime, partial); + + if (result == -ETIMEDOUT) { /* NAK - so hold for a while */ + if (!maxretry--) { + return -ETIME; + } + interruptible_sleep_on_timeout(&lcd-> wait_q, NAK_TIMEOUT); + continue; + } else if (!result & partial) { + obuf += partial; + thistime -= partial; + } else + break; + }; + if (result) { + err("Write Whoops - %x", result); + return -EIO; + } + bytes_written += copy_size; + count -= copy_size; + buffer += copy_size; + } while (count > 0); + + return bytes_written ? bytes_written : -EIO; +} + +static ssize_t +read_lcd(struct file *file, char *buffer, size_t count, loff_t * ppos) +{ + struct lcd_usb_data *lcd = &lcd_instance; + ssize_t read_count; + unsigned int partial; + int this_read; + int result; + int maxretry = 10; + char *ibuf = lcd->ibuf; + + /* Sanity check to make sure lcd is connected, powered, etc */ + if (lcd == NULL || + lcd->present == 0 || + lcd->lcd_dev == NULL) + return -1; + + read_count = 0; + + while (count > 0) { + if (signal_pending(current)) { + return read_count ? read_count : -EINTR; + } + if (!lcd->lcd_dev) + return -ENODEV; + this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count; + + result = usb_bulk_msg(lcd->lcd_dev, + usb_rcvbulkpipe(lcd->lcd_dev, 0), + ibuf, this_read, &partial, + (int) (HZ * 8)); + + dbg(KERN_DEBUG "read stats: result:%d this_read:%u partial:%u", + result, this_read, partial); + + if (partial) { + count = this_read = partial; + } else if (result == -ETIMEDOUT || result == 15) { /* FIXME: 15 ??? */ + if (!maxretry--) { + err("read_lcd: maxretry timeout"); + return -ETIME; + } + interruptible_sleep_on_timeout(&lcd->wait_q, + NAK_TIMEOUT); + continue; + } else if (result != -EREMOTEIO) { + err("Read Whoops - result:%u partial:%u this_read:%u", + result, partial, this_read); + return -EIO; + } else { + return (0); + } + + if (this_read) { + if (copy_to_user(buffer, ibuf, this_read)) + return -EFAULT; + count -= this_read; + read_count += this_read; + buffer += this_read; + } + } + return read_count; +} + +static struct +file_operations usb_lcd_fops = { + .owner = THIS_MODULE, + .read = read_lcd, + .write = write_lcd, + .ioctl = ioctl_lcd, + .open = open_lcd, + .release = close_lcd, +}; + +static void *probe_lcd(struct usb_device *dev, unsigned int ifnum) +{ + struct lcd_usb_data *lcd = &lcd_instance; + int i; + int retval; + + if (dev->descriptor.idProduct != 0x0001 ) { + warn(KERN_INFO "USBLCD model not supported."); + return NULL; + } + + if (lcd->present == 1) { + warn(KERN_INFO "Multiple USBLCDs are not supported!"); + return NULL; + } + + i = dev->descriptor.bcdDevice; + + info("USBLCD Version %1d%1d.%1d%1d found at address %d", + (i & 0xF000)>>12,(i & 0xF00)>>8,(i & 0xF0)>>4,(i & 0xF), + dev->devnum); + + retval = usb_register_dev(&usb_lcd_fops, USBLCD_MINOR, 1, &lcd->minor); + if (retval) { + err("Not able to get a minor for this device."); + return NULL; + } + + lcd->present = 1; + lcd->lcd_dev = dev; + + if (!(lcd->obuf = (char *) kmalloc(OBUF_SIZE, GFP_KERNEL))) { + err("probe_lcd: Not enough memory for the output buffer"); + return NULL; + } + dbg("probe_lcd: obuf address:%p", lcd->obuf); + + if (!(lcd->ibuf = (char *) kmalloc(IBUF_SIZE, GFP_KERNEL))) { + err("probe_lcd: Not enough memory for the input buffer"); + kfree(lcd->obuf); + return NULL; + } + dbg("probe_lcd: ibuf address:%p", lcd->ibuf); + + return lcd; +} + +static void disconnect_lcd(struct usb_device *dev, void *ptr) +{ + struct lcd_usb_data *lcd = (struct lcd_usb_data *) ptr; + + usb_deregister_dev(1, lcd->minor); + + if (lcd->isopen) { + lcd->isopen = 0; + /* better let it finish - the release will do whats needed */ + lcd->lcd_dev = NULL; + return; + } + kfree(lcd->ibuf); + kfree(lcd->obuf); + + info("USBLCD disconnected."); + + lcd->present = 0; +} + +static struct usb_device_id id_table [] = { + { .idVendor = 0x1212, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, }, + {}, +}; + +MODULE_DEVICE_TABLE (usb, id_table); + +static struct +usb_driver lcd_driver = { + .name = "usblcd", + .probe = (void *)probe_lcd, + .disconnect = disconnect_lcd, + .id_table = id_table, +}; + +int usb_lcd_init(void) +{ + if (usb_register(&lcd_driver) < 0) + return -1; + + info("%s (C) Adams IT Services http://www.usblcd.de", DRIVER_VERSION); + info("USBLCD support registered."); + return 0; +} + + +void usb_lcd_cleanup(void) +{ + struct lcd_usb_data *lcd = &lcd_instance; + + lcd->present = 0; + usb_deregister(&lcd_driver); +} + +module_init(usb_lcd_init); +module_exit(usb_lcd_cleanup); + +MODULE_AUTHOR("Adams IT Services "); +MODULE_DESCRIPTION(DRIVER_VERSION); +MODULE_LICENSE("GPL");