diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-08-28 22:00:09 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-08-28 22:00:09 +0000 |
commit | 1a1d77dd589de5a567fa95e36aa6999c704ceca4 (patch) | |
tree | 141e31f89f18b9fe0831f31852e0435ceaccafc5 /drivers/usb | |
parent | fb9c690a18b3d66925a65b17441c37fa14d4370b (diff) |
Merge with 2.4.0-test7.
Diffstat (limited to 'drivers/usb')
36 files changed, 2084 insertions, 3220 deletions
diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index 1edbfcb44..496faba61 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -68,31 +68,23 @@ comment 'USB Devices' dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' PLUSB Prolific USB-Network driver (EXPERIMENTAL)' CONFIG_USB_PLUSB $CONFIG_USB $CONFIG_NET - dep_tristate ' USB ADMtek Pegasus-based device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET + dep_tristate ' USB ADMtek Pegasus-based ethernet device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB fi -comment 'USB HID' - dep_tristate ' USB Human Interface Device (HID) support' CONFIG_USB_HID $CONFIG_USB - if [ "$CONFIG_USB_HID" != "y" ]; then - dep_tristate ' USB HIDBP Keyboard support' CONFIG_USB_KBD $CONFIG_USB - dep_tristate ' USB HIDBP Mouse support' CONFIG_USB_MOUSE $CONFIG_USB - fi - dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB - dep_tristate ' I-Force joysticks/wheels' CONFIG_INPUT_IFORCE_USB $CONFIG_USB - if [ "$CONFIG_INPUT_IFORCE_USB" != "n" ]; then - define_tristate CONFIG_INPUT_IFORCE $CONFIG_INPUT_IFORCE_USB + comment 'USB Human Interface Devices (HID)' + if [ "$CONFIG_INPUT" = "n" ]; then + comment ' Input core support is needed for USB HID' + else + dep_tristate ' USB Human Interface Device (HID) support' CONFIG_USB_HID $CONFIG_USB $CONFIG_INPUT + if [ "$CONFIG_USB_HID" != "y" ]; then + dep_tristate ' USB HIDBP Keyboard support' CONFIG_USB_KBD $CONFIG_USB $CONFIG_INPUT + dep_tristate ' USB HIDBP Mouse support' CONFIG_USB_MOUSE $CONFIG_USB $CONFIG_INPUT fi - dep_tristate ' Keyboard support' CONFIG_INPUT_KEYBDEV $CONFIG_USB - dep_tristate ' Mouse support' CONFIG_INPUT_MOUSEDEV $CONFIG_USB - if [ "$CONFIG_INPUT_MOUSEDEV" != "n" ]; then - int ' Horizontal screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024 - int ' Vertical screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768 + dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB $CONFIG_INPUT fi - dep_tristate ' Joystick support' CONFIG_INPUT_JOYDEV $CONFIG_USB - dep_tristate ' Event interface support' CONFIG_INPUT_EVDEV $CONFIG_USB fi endmenu diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 5831dd1d9..7112ae635 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -17,7 +17,7 @@ O_OBJS := # Objects that export symbols. -export-objs := usb.o input.o +export-objs := usb.o # Multipart objects. @@ -37,27 +37,6 @@ obj-m := obj-n := obj- := -# Object files in subdirectories - -ifeq ($(CONFIG_USB_SERIAL),y) - SUB_DIRS += serial - obj-y += serial/usb-serial.o -else - ifeq ($(CONFIG_USB_SERIAL),m) - MOD_IN_SUB_DIRS += serial - endif -endif - -ifeq ($(CONFIG_USB_STORAGE),y) - SUB_DIRS += storage - obj-y += storage/usb-storage.o -else - ifeq ($(CONFIG_USB_STORAGE),m) - MOD_IN_SUB_DIRS += storage - endif -endif - - # Each configuration option enables a list of files. obj-$(CONFIG_USB) += usbcore.o @@ -65,15 +44,10 @@ obj-$(CONFIG_USB_UHCI) += usb-uhci.o obj-$(CONFIG_USB_UHCI_ALT) += uhci.o obj-$(CONFIG_USB_OHCI) += usb-ohci.o -obj-$(CONFIG_USB_MOUSE) += usbmouse.o input.o -obj-$(CONFIG_USB_HID) += hid.o input.o -obj-$(CONFIG_USB_KBD) += usbkbd.o input.o -obj-$(CONFIG_USB_WACOM) += wacom.o input.o -obj-$(CONFIG_INPUT_IFORCE) += iforce.o input.o -obj-$(CONFIG_INPUT_KEYBDEV) += keybdev.o input.o -obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o input.o -obj-$(CONFIG_INPUT_JOYDEV) += joydev.o input.o -obj-$(CONFIG_INPUT_EVDEV) += evdev.o input.o +obj-$(CONFIG_USB_MOUSE) += usbmouse.o +obj-$(CONFIG_USB_HID) += hid.o +obj-$(CONFIG_USB_KBD) += usbkbd.o +obj-$(CONFIG_USB_WACOM) += wacom.o obj-$(CONFIG_USB_SCANNER) += scanner.o obj-$(CONFIG_USB_ACM) += acm.o @@ -92,6 +66,26 @@ obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_USB_MICROTEK) += microtek.o obj-$(CONFIG_USB_BLUETOOTH) += bluetooth.o +# Object files in subdirectories + +ifeq ($(CONFIG_USB_SERIAL),y) + SUB_DIRS += serial + obj-y += serial/usb-serial.o +else + ifeq ($(CONFIG_USB_SERIAL),m) + MOD_IN_SUB_DIRS += serial + endif +endif + +ifeq ($(CONFIG_USB_STORAGE),y) + SUB_DIRS += storage + obj-y += storage/usb-storage.o +else + ifeq ($(CONFIG_USB_STORAGE),m) + MOD_IN_SUB_DIRS += storage + endif +endif + # Extract lists of the multi-part drivers. # The 'int-*' lists are the intermediate files used to build the multi's. @@ -111,8 +105,8 @@ obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y) # Translate to Rules.make lists. -O_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) -OX_OBJS := $(sort $(filter $(export-objs), $(obj-y))) +O_OBJS := $(filter-out $(export-objs), $(obj-y)) +OX_OBJS := $(filter $(export-objs), $(obj-y)) M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m))) diff --git a/drivers/usb/bluetooth.c b/drivers/usb/bluetooth.c index 4ee29a702..fd239671b 100644 --- a/drivers/usb/bluetooth.c +++ b/drivers/usb/bluetooth.c @@ -1,5 +1,5 @@ /* - * bluetooth.c Version 0.3 + * bluetooth.c Version 0.4 * * Copyright (c) 2000 Greg Kroah-Hartman <greg@kroah.com> * Copyright (c) 2000 Mark Douglas Corner <mcorner@umich.edu> @@ -7,6 +7,13 @@ * USB Bluetooth driver, based on the Bluetooth Spec version 1.0B * * + * (07/11/2000) Version 0.4 gkh + * Fixed bug in disconnect for when we call tty_hangup + * Fixed bug in bluetooth_ctrl_msg where the bluetooth struct was not + * getting attached to the control urb properly. + * Fixed bug in bluetooth_write where we pay attention to the result + * of bluetooth_ctrl_msg. + * * (08/03/2000) Version 0.3 gkh mdc * Merged in Mark's changes to make the driver play nice with the Axis * stack. @@ -251,7 +258,7 @@ static int bluetooth_ctrl_msg (struct usb_bluetooth *bluetooth, int request, int dr->length = cpu_to_le16p(&len); FILL_CONTROL_URB (urb, bluetooth->dev, usb_sndctrlpipe(bluetooth->dev, 0), - (unsigned char*)dr, buf, len, bluetooth_ctrl_callback, 0); + (unsigned char*)dr, buf, len, bluetooth_ctrl_callback, bluetooth); /* send it down the pipe */ status = usb_submit_urb(urb); @@ -400,7 +407,10 @@ static int bluetooth_write (struct tty_struct * tty, int from_user, const unsign else memcpy (new_buffer, buf+1, count-1); - bluetooth_ctrl_msg (bluetooth, 0x00, 0x00, new_buffer, count-1); + if (bluetooth_ctrl_msg (bluetooth, 0x00, 0x00, new_buffer, count-1) != 0) { + kfree (new_buffer); + return 0; + } /* need to free new_buffer somehow... FIXME */ return count; @@ -1099,6 +1109,9 @@ static void usb_bluetooth_disconnect(struct usb_device *dev, void *ptr) int i; if (bluetooth) { + if ((bluetooth->active) && (bluetooth->tty)) + tty_hangup(bluetooth->tty); + bluetooth->active = 0; if (bluetooth->read_urb) { @@ -1117,9 +1130,6 @@ static void usb_bluetooth_disconnect(struct usb_device *dev, void *ptr) tty_unregister_devfs (&bluetooth_tty_driver, bluetooth->minor); - if (bluetooth->tty) - tty_hangup(bluetooth->tty); - for (i = 0; i < NUM_BULK_URBS; ++i) { if (bluetooth->write_urb_pool[i]) { usb_unlink_urb (bluetooth->write_urb_pool[i]); diff --git a/drivers/usb/dc2xx.c b/drivers/usb/dc2xx.c index bb960a4af..67a55ab46 100644 --- a/drivers/usb/dc2xx.c +++ b/drivers/usb/dc2xx.c @@ -43,6 +43,7 @@ * added timeouts to bulk_msg calls. Minor updates, docs. * 03 Nov, 1999 -- update for 2.3.25 kernel API changes. * 08 Jan, 2000 .. multiple camera support + * 12 Aug, 2000 .. add some real locking, remove an Oops * * Thanks to: the folk who've provided USB product IDs, sent in * patches, and shared their sucesses! @@ -60,7 +61,6 @@ #include <linux/module.h> #undef DEBUG #include <linux/usb.h> -#include <linux/smp_lock.h> @@ -114,7 +114,7 @@ struct camera_state { int outEP; /* write endpoint */ const struct camera *info; /* DC-240, etc */ int subminor; /* which minor dev #? */ - int isActive; /* I/O taking place? */ + struct semaphore sem; /* locks this struct */ /* this is non-null iff the device is open */ char *buf; /* buffer for I/O */ @@ -127,21 +127,25 @@ struct camera_state { /* Support multiple cameras, possibly of different types. */ static struct camera_state *minor_data [MAX_CAMERAS]; +/* make this an rwlock if contention becomes an issue */ +static DECLARE_MUTEX (state_table_mutex); static ssize_t camera_read (struct file *file, char *buf, size_t len, loff_t *ppos) { struct camera_state *camera; int retries; + int retval = 0; if (len > MAX_PACKET_SIZE) return -EINVAL; camera = (struct camera_state *) file->private_data; - if (!camera->dev) + down (&camera->sem); + if (!camera->dev) { + up (&camera->sem); return -ENODEV; - if (camera->isActive++) - return -EBUSY; + } /* Big reads are common, for image downloading. Smaller ones * are also common (even "directory listing" commands don't @@ -150,37 +154,33 @@ static ssize_t camera_read (struct file *file, */ for (retries = 0; retries < MAX_READ_RETRY; retries++) { int count; - int result; if (signal_pending (current)) { - camera->isActive = 0; - return -EINTR; - } - if (!camera->dev) { - camera->isActive = 0; - return -ENODEV; + retval = -EINTR; + break; } - result = usb_bulk_msg (camera->dev, + retval = usb_bulk_msg (camera->dev, usb_rcvbulkpipe (camera->dev, camera->inEP), camera->buf, len, &count, HZ*10); - dbg ("read (%d) - 0x%x %d", len, result, count); + dbg ("read (%d) - 0x%x %d", len, retval, count); - if (!result) { + if (!retval) { if (copy_to_user (buf, camera->buf, count)) - return -EFAULT; - camera->isActive = 0; - return count; + retval = -EFAULT; + else + retval = count; + break; } - if (result != USB_ST_TIMEOUT) + if (retval != USB_ST_TIMEOUT) break; interruptible_sleep_on_timeout (&camera->wait, RETRY_TIMEOUT); dbg ("read (%d) - retry", len); } - camera->isActive = 0; - return -EIO; + up (&camera->sem); + return retval; } static ssize_t camera_write (struct file *file, @@ -193,10 +193,11 @@ static ssize_t camera_write (struct file *file, return -EINVAL; camera = (struct camera_state *) file->private_data; - if (!camera->dev) + down (&camera->sem); + if (!camera->dev) { + up (&camera->sem); return -ENODEV; - if (camera->isActive++) - return -EBUSY; + } /* most writes will be small: simple commands, sometimes with * parameters. putting images (like borders) into the camera @@ -224,18 +225,13 @@ static ssize_t camera_write (struct file *file, bytes_written = -EINTR; goto done; } - if (!camera->dev) { - if (!bytes_written) - bytes_written = -ENODEV; - goto done; - } result = usb_bulk_msg (camera->dev, usb_sndbulkpipe (camera->dev, camera->outEP), obuf, thistime, &count, HZ*10); if (result) - dbg ("write USB err - %x", result); + dbg ("write USB err - %d", result); if (count) { obuf += count; @@ -264,49 +260,69 @@ static ssize_t camera_write (struct file *file, buf += copy_size; } done: - camera->isActive = 0; + up (&camera->sem); dbg ("wrote %d", bytes_written); return bytes_written; } static int camera_open (struct inode *inode, struct file *file) { - struct camera_state *camera; + struct camera_state *camera = NULL; int subminor; + int value = 0; + down (&state_table_mutex); subminor = MINOR (inode->i_rdev) - USB_CAMERA_MINOR_BASE; if (subminor < 0 || subminor >= MAX_CAMERAS || !(camera = minor_data [subminor])) { + up (&state_table_mutex); return -ENODEV; } + down (&camera->sem); + up (&state_table_mutex); + + if (camera->buf) { + value = -EBUSY; + goto done; + } if (!(camera->buf = (char *) kmalloc (MAX_PACKET_SIZE, GFP_KERNEL))) { - return -ENOMEM; + value = -ENOMEM; + goto done; } - dbg ("open"); + dbg ("open #%d", subminor); - camera->isActive = 0; file->private_data = camera; - return 0; +done: + up (&camera->sem); + return value; } static int camera_release (struct inode *inode, struct file *file) { - struct camera_state *camera; + struct camera_state *camera; + int subminor; camera = (struct camera_state *) file->private_data; - kfree (camera->buf); + down (&state_table_mutex); + down (&camera->sem); + + if (camera->buf) { + kfree (camera->buf); + camera->buf = 0; + } + subminor = camera->subminor; /* If camera was unplugged with open file ... */ - lock_kernel(); if (!camera->dev) { - minor_data [camera->subminor] = NULL; + minor_data [subminor] = NULL; kfree (camera); } - unlock_kernel(); + up (&camera->sem); + up (&state_table_mutex); - dbg ("close"); + dbg ("close #%d", subminor); return 0; } @@ -333,7 +349,7 @@ static void * camera_probe(struct usb_device *dev, unsigned int ifnum) struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *endpoint; int direction, ep; - struct camera_state *camera; + struct camera_state *camera = NULL; /* Is it a supported camera? */ for (i = 0; i < sizeof (cameras) / sizeof (struct camera); i++) { @@ -368,27 +384,30 @@ static void * camera_probe(struct usb_device *dev, unsigned int ifnum) /* select "subminor" number (part of a minor number) */ + down (&state_table_mutex); for (i = 0; i < MAX_CAMERAS; i++) { if (!minor_data [i]) break; } if (i >= MAX_CAMERAS) { info ("Ignoring additional USB Camera"); - return NULL; + up (&state_table_mutex); + goto bye; } /* allocate & init camera state */ camera = minor_data [i] = kmalloc (sizeof *camera, GFP_KERNEL); if (!camera) { err ("no memory!"); - return NULL; + up (&state_table_mutex); + goto bye; } + + init_MUTEX (&camera->sem); camera->info = camera_info; camera->subminor = i; - camera->isActive = 0; camera->buf = NULL; init_waitqueue_head (&camera->wait); - info ("USB Camera #%d connected", camera->subminor); /* get input and output endpoints (either order) */ @@ -417,19 +436,19 @@ static void * camera_probe(struct usb_device *dev, unsigned int ifnum) goto error; } - - if (usb_set_configuration (dev, dev->config[0].bConfigurationValue)) { - err ("Failed usb_set_configuration"); - goto error; - } + info ("USB Camera #%d connected", camera->subminor); camera->dev = dev; - return camera; + usb_inc_dev_use (dev); + goto bye; error: minor_data [camera->subminor] = NULL; kfree (camera); - return NULL; + camera = NULL; +bye: + up (&state_table_mutex); + return camera; } static void camera_disconnect(struct usb_device *dev, void *ptr) @@ -437,6 +456,9 @@ static void camera_disconnect(struct usb_device *dev, void *ptr) struct camera_state *camera = (struct camera_state *) ptr; int subminor = camera->subminor; + down (&state_table_mutex); + down (&camera->sem); + /* If camera's not opened, we can clean up right away. * Else apps see a disconnect on next I/O; the release cleans. */ @@ -447,6 +469,10 @@ static void camera_disconnect(struct usb_device *dev, void *ptr) camera->dev = NULL; info ("USB Camera #%d disconnected", subminor); + usb_dec_dev_use (dev); + + up (&camera->sem); + up (&state_table_mutex); } static /* const */ struct usb_driver camera_driver = { diff --git a/drivers/usb/devio.c b/drivers/usb/devio.c index 5609ae1c2..00bd135e8 100644 --- a/drivers/usb/devio.c +++ b/drivers/usb/devio.c @@ -733,8 +733,7 @@ static int proc_clearhalt(struct dev_state *ps, void *arg) else pipe = usb_sndbulkpipe(ps->dev, ep & 0x7f); - usb_clear_halt(ps->dev, pipe); - return 0; + return usb_clear_halt(ps->dev, pipe); } diff --git a/drivers/usb/evdev.c b/drivers/usb/evdev.c deleted file mode 100644 index 65109e9ca..000000000 --- a/drivers/usb/evdev.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - * $Id: evdev.c,v 1.10 2000/06/23 09:23:00 vojtech Exp $ - * - * Copyright (c) 1999-2000 Vojtech Pavlik - * - * Event char devices, giving access to raw input device events. - * - * Sponsored by SuSE - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#define EVDEV_MINOR_BASE 64 -#define EVDEV_MINORS 32 -#define EVDEV_BUFFER_SIZE 64 - -#include <linux/poll.h> -#include <linux/malloc.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/input.h> -#include <linux/smp_lock.h> - -struct evdev { - int exist; - int open; - int minor; - struct input_handle handle; - wait_queue_head_t wait; - devfs_handle_t devfs; - struct evdev_list *list; -}; - -struct evdev_list { - struct input_event buffer[EVDEV_BUFFER_SIZE]; - int head; - int tail; - struct fasync_struct *fasync; - struct evdev *evdev; - struct evdev_list *next; -}; - -static struct evdev *evdev_table[EVDEV_MINORS] = { NULL, /* ... */ }; - -static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) -{ - struct evdev *evdev = handle->private; - struct evdev_list *list = evdev->list; - - while (list) { - - get_fast_time(&list->buffer[list->head].time); - list->buffer[list->head].type = type; - list->buffer[list->head].code = code; - list->buffer[list->head].value = value; - list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1); - - kill_fasync(&list->fasync, SIGIO, POLL_IN); - - list = list->next; - } - - wake_up_interruptible(&evdev->wait); -} - -static int evdev_fasync(int fd, struct file *file, int on) -{ - int retval; - struct evdev_list *list = file->private_data; - retval = fasync_helper(fd, file, on, &list->fasync); - return retval < 0 ? retval : 0; -} - -static int evdev_release(struct inode * inode, struct file * file) -{ - struct evdev_list *list = file->private_data; - struct evdev_list **listptr; - - lock_kernel(); - listptr = &list->evdev->list; - evdev_fasync(-1, file, 0); - - while (*listptr && (*listptr != list)) - listptr = &((*listptr)->next); - *listptr = (*listptr)->next; - - if (!--list->evdev->open) { - if (list->evdev->exist) { - input_close_device(&list->evdev->handle); - } else { - input_unregister_minor(list->evdev->devfs); - evdev_table[list->evdev->minor] = NULL; - kfree(list->evdev); - } - } - - kfree(list); - unlock_kernel(); - - return 0; -} - -static int evdev_open(struct inode * inode, struct file * file) -{ - struct evdev_list *list; - int i = MINOR(inode->i_rdev) - EVDEV_MINOR_BASE; - - if (i > EVDEV_MINORS || !evdev_table[i]) - return -ENODEV; - - if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL))) - return -ENOMEM; - memset(list, 0, sizeof(struct evdev_list)); - - list->evdev = evdev_table[i]; - list->next = evdev_table[i]->list; - evdev_table[i]->list = list; - - file->private_data = list; - - if (!list->evdev->open++) - if (list->evdev->exist) - input_open_device(&list->evdev->handle); - - return 0; -} - -static ssize_t evdev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -static ssize_t evdev_read(struct file * file, char * buffer, size_t count, loff_t *ppos) -{ - DECLARE_WAITQUEUE(wait, current); - struct evdev_list *list = file->private_data; - int retval = 0; - - if (list->head == list->tail) { - - add_wait_queue(&list->evdev->wait, &wait); - current->state = TASK_INTERRUPTIBLE; - - while (list->head == list->tail) { - - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - - schedule(); - } - - current->state = TASK_RUNNING; - remove_wait_queue(&list->evdev->wait, &wait); - } - - if (retval) - return retval; - - while (list->head != list->tail && retval + sizeof(struct input_event) <= count) { - if (copy_to_user(buffer + retval, list->buffer + list->tail, - sizeof(struct input_event))) return -EFAULT; - list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1); - retval += sizeof(struct input_event); - } - - return retval; -} - -/* No kernel lock - fine */ -static unsigned int evdev_poll(struct file *file, poll_table *wait) -{ - struct evdev_list *list = file->private_data; - poll_wait(file, &list->evdev->wait, wait); - if (list->head != list->tail) - return POLLIN | POLLRDNORM; - return 0; -} - -static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct evdev_list *list = file->private_data; - struct evdev *evdev = list->evdev; - struct input_dev *dev = evdev->handle.dev; - int retval; - - switch (cmd) { - - case EVIOCGVERSION: - return put_user(EV_VERSION, (int *) arg); - - case EVIOCGID: - if ((retval = put_user(dev->idbus, ((short *) arg) + 0))) return retval; - if ((retval = put_user(dev->idvendor, ((short *) arg) + 1))) return retval; - if ((retval = put_user(dev->idproduct, ((short *) arg) + 2))) return retval; - if ((retval = put_user(dev->idversion, ((short *) arg) + 3))) return retval; - return 0; - - default: - - if (_IOC_TYPE(cmd) != 'E' || _IOC_DIR(cmd) != _IOC_READ) - return -EINVAL; - - if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) { - - long *bits; - int len; - - switch (_IOC_NR(cmd) & EV_MAX) { - case 0: bits = dev->evbit; len = EV_MAX; break; - case EV_KEY: bits = dev->keybit; len = KEY_MAX; break; - case EV_REL: bits = dev->relbit; len = REL_MAX; break; - case EV_ABS: bits = dev->absbit; len = ABS_MAX; break; - case EV_LED: bits = dev->ledbit; len = LED_MAX; break; - case EV_SND: bits = dev->sndbit; len = SND_MAX; break; - default: return -EINVAL; - } - len = NBITS(len) * sizeof(long); - if (len > _IOC_SIZE(cmd)) { - printk(KERN_WARNING "evdev.c: Truncating bitfield length from %d to %d\n", - len, _IOC_SIZE(cmd)); - len = _IOC_SIZE(cmd); - } - return copy_to_user((char *) arg, bits, len) ? -EFAULT : len; - } - - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) { - int len; - if (!dev->name) return 0; - len = strlen(dev->name) + 1; - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user((char *) arg, dev->name, len) ? -EFAULT : len; - } - - if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { - - int t = _IOC_NR(cmd) & ABS_MAX; - - if ((retval = put_user(dev->abs[t], ((int *) arg) + 0))) return retval; - if ((retval = put_user(dev->absmin[t], ((int *) arg) + 1))) return retval; - if ((retval = put_user(dev->absmax[t], ((int *) arg) + 2))) return retval; - if ((retval = put_user(dev->absfuzz[t], ((int *) arg) + 3))) return retval; - if ((retval = put_user(dev->absflat[t], ((int *) arg) + 4))) return retval; - - return 0; - } - } - return -EINVAL; -} - -static struct file_operations evdev_fops = { - owner: THIS_MODULE, - read: evdev_read, - write: evdev_write, - poll: evdev_poll, - open: evdev_open, - release: evdev_release, - ioctl: evdev_ioctl, - fasync: evdev_fasync, -}; - -static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev) -{ - struct evdev *evdev; - int minor; - - for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); - if (evdev_table[minor]) { - printk(KERN_ERR "evdev: no more free evdev devices\n"); - return NULL; - } - - if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL))) - return NULL; - memset(evdev, 0, sizeof(struct evdev)); - - init_waitqueue_head(&evdev->wait); - - evdev->minor = minor; - evdev_table[minor] = evdev; - - evdev->handle.dev = dev; - evdev->handle.handler = handler; - evdev->handle.private = evdev; - - evdev->exist = 1; - - evdev->devfs = input_register_minor("event%d", minor, EVDEV_MINOR_BASE); - - printk(KERN_INFO "event%d: Event device for input%d\n", minor, dev->number); - - return &evdev->handle; -} - -static void evdev_disconnect(struct input_handle *handle) -{ - struct evdev *evdev = handle->private; - - evdev->exist = 0; - - if (evdev->open) { - input_close_device(handle); - } else { - input_unregister_minor(evdev->devfs); - evdev_table[evdev->minor] = NULL; - kfree(evdev); - } -} - -static struct input_handler evdev_handler = { - event: evdev_event, - connect: evdev_connect, - disconnect: evdev_disconnect, - fops: &evdev_fops, - minor: EVDEV_MINOR_BASE, -}; - -static int __init evdev_init(void) -{ - input_register_handler(&evdev_handler); - return 0; -} - -static void __exit evdev_exit(void) -{ - input_unregister_handler(&evdev_handler); -} - -module_init(evdev_init); -module_exit(evdev_exit); - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_DESCRIPTION("Event character device driver"); diff --git a/drivers/usb/hid.c b/drivers/usb/hid.c index 68a981e27..189cbbdfc 100644 --- a/drivers/usb/hid.c +++ b/drivers/usb/hid.c @@ -1,5 +1,5 @@ /* - * $Id: hid.c,v 1.6 2000/05/29 09:01:52 vojtech Exp $ + * $Id: hid.c,v 1.14 2000/08/14 21:05:26 vojtech Exp $ * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000 Vojtech Pavlik @@ -65,8 +65,8 @@ static unsigned char hid_keyboard[256] = { 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, 72, 73, 82, 83, 86,127,116,117, 85, 89, 90, 91, 92, 93, 94, 95, 120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113, - 115,114,unk,unk,unk,unk,unk,124,unk,unk,unk,unk,unk,unk,unk,unk, - unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, + 115,114,unk,unk,unk,124,unk,181,182,183,184,185,186,187,188,189, + 190,191,192,193,194,195,196,197,198,unk,unk,unk,unk,unk,unk,unk, unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, @@ -149,7 +149,7 @@ static int open_collection(struct hid_parser *parser, unsigned type) usage = parser->local.usage[0]; - if (type == HID_COLLECTION_APPLICATION) + if (type == HID_COLLECTION_APPLICATION && !parser->device->application) parser->device->application = usage; if (parser->collection_stack_ptr < HID_COLLECTION_STACK_SIZE) { /* PUSH on stack */ @@ -197,7 +197,7 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type) static int hid_add_usage(struct hid_parser *parser, unsigned usage) { - if (parser->local.usage_index >= MAX_USAGES) { + if (parser->local.usage_index >= HID_MAX_USAGES) { dbg("usage index exceeded"); return -1; } @@ -346,7 +346,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) return 0; case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: - if ((parser->global.report_count = item_udata(item)) > MAX_USAGES) { + if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) { dbg("invalid report_count %d", parser->global.report_count); return -1; } @@ -1224,10 +1224,31 @@ static int hid_find_field(struct hid_device *hid, unsigned int type, unsigned in return -1; } +static int hid_submit_out(struct hid_device *hid) +{ + hid->urbout.transfer_buffer_length = hid->out[hid->outtail].dr.length; + hid->urbout.transfer_buffer = hid->out[hid->outtail].buffer; + hid->urbout.setup_packet = (void *) &(hid->out[hid->outtail].dr); + + if (usb_submit_urb(&hid->urbout)) { + err("usb_submit_urb(out) failed"); + return -1; + } + + return 0; +} + static void hid_ctrl(struct urb *urb) { + struct hid_device *hid = urb->context; + if (urb->status) - warn("ctrl urb status %d received", urb->status); + warn("ctrl urb status %d received", urb->status); + + hid->outtail = (hid->outtail + 1) & (HID_CONTROL_FIFO_SIZE - 1); + + if (hid->outhead != hid->outtail) + hid_submit_out(hid); } static int hid_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) @@ -1242,22 +1263,18 @@ static int hid_event(struct input_dev *dev, unsigned int type, unsigned int code } hid_set_field(field, offset, value); + hid_output_report(field->report, hid->out[hid->outhead].buffer); - if (hid->urbout.status == -EINPROGRESS) { - warn("had to kill output urb"); - usb_unlink_urb(&hid->urbout); - } + hid->out[hid->outhead].dr.value = 0x200 | field->report->id; + hid->out[hid->outhead].dr.length = ((field->report->size - 1) >> 3) + 1; - hid_output_report(field->report, hid->bufout); + hid->outhead = (hid->outhead + 1) & (HID_CONTROL_FIFO_SIZE - 1); - hid->dr.value = 0x200 | field->report->id; - hid->dr.length = ((field->report->size - 1) >> 3) + 1; - hid->urbout.transfer_buffer_length = hid->dr.length; + if (hid->outhead == hid->outtail) + hid->outtail = (hid->outtail + 1) & (HID_CONTROL_FIFO_SIZE - 1); - if (usb_submit_urb(&hid->urbout)) { - err("usb_submit_urb(out) failed"); - return -1; - } + if (hid->urbout.status != -EINPROGRESS) + hid_submit_out(hid); return 0; } @@ -1364,7 +1381,7 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum, c if (hdesc->desc[n].bDescriptorType == USB_DT_REPORT) rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength); - if (!rsize || rsize > 1024) { + if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { dbg("weird size of report descriptor (%u)", rsize); return NULL; } @@ -1420,11 +1437,11 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum, c hid->dev = dev; hid->ifnum = interface->bInterfaceNumber; - hid->dr.requesttype = USB_TYPE_CLASS | USB_RECIP_INTERFACE; - hid->dr.request = USB_REQ_SET_REPORT; - hid->dr.value = 0x200; - hid->dr.index = hid->ifnum; - hid->dr.length = 1; + for (n = 0; n < HID_CONTROL_FIFO_SIZE; n++) { + hid->out[n].dr.requesttype = USB_TYPE_CLASS | USB_RECIP_INTERFACE; + hid->out[n].dr.request = USB_REQ_SET_REPORT; + hid->out[n].dr.index = hid->ifnum; + } hid->input.name = hid->name; hid->input.idbus = BUS_USB; @@ -1441,7 +1458,7 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum, c hid->input.idvendor, hid->input.idproduct); FILL_CONTROL_URB(&hid->urbout, dev, usb_sndctrlpipe(dev, 0), - (void*) &hid->dr, hid->bufout, 1, hid_ctrl, hid); + (void*) &hid->out[0].dr, hid->out[0].buffer, 1, hid_ctrl, hid); if (interface->bInterfaceSubClass == 1) usb_set_protocol(dev, hid->ifnum, 1); diff --git a/drivers/usb/hid.h b/drivers/usb/hid.h index 88ab5eaca..98abc8a76 100644 --- a/drivers/usb/hid.h +++ b/drivers/usb/hid.h @@ -2,7 +2,7 @@ #define __HID_H /* - * $Id: hid.h,v 1.4 2000/05/29 09:01:52 vojtech Exp $ + * $Id: hid.h,v 1.7 2000/05/31 22:57:48 vojtech Exp $ * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000 Vojtech Pavlik @@ -208,10 +208,11 @@ struct hid_global { * This is the local enviroment. It is resistent up the the next main-item. */ -#define MAX_USAGES 1024 +#define HID_MAX_DESCRIPTOR_SIZE 4096 +#define HID_MAX_USAGES 1024 struct hid_local { - unsigned usage[MAX_USAGES]; /* usage array */ + unsigned usage[HID_MAX_USAGES]; /* usage array */ unsigned usage_index; unsigned usage_minimum; unsigned delimiter_depth; @@ -275,6 +276,14 @@ struct hid_report_enum { #define HID_REPORT_TYPES 3 +#define HID_BUFFER_SIZE 32 +#define HID_CONTROL_FIFO_SIZE 8 + +struct hid_control_fifo { + devrequest dr; + char buffer[HID_BUFFER_SIZE]; +}; + struct hid_device { /* device report descriptor */ __u8 *rdesc; unsigned rsize; @@ -286,11 +295,13 @@ struct hid_device { /* device report descriptor */ struct usb_device *dev; /* USB device */ int ifnum; /* USB interface number */ - char buffer[32]; /* Receive buffer */ - char bufout[32]; /* Transmit buffer */ - devrequest dr; /* Startup packet */ struct urb urb; /* USB URB structure */ + char buffer[HID_BUFFER_SIZE]; /* Rx buffer */ + struct urb urbout; /* Output URB */ + struct hid_control_fifo out[HID_CONTROL_FIFO_SIZE]; /* Transmit buffer */ + unsigned char outhead, outtail; /* Tx buffer head & tail */ + struct input_dev input; /* input device structure */ int open; /* is the device open by input? */ int quirks; /* Various nasty tricks the device can pull on us */ diff --git a/drivers/usb/iforce.c b/drivers/usb/iforce.c deleted file mode 100644 index d70bfd90e..000000000 --- a/drivers/usb/iforce.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * $Id: iforce.c,v 1.7 2000/06/04 14:03:36 vojtech Exp $ - * - * Copyright (c) 2000 Vojtech Pavlik - * - * USB/RS232 I-Force joysticks and wheels. - * - * Sponsored by SuSE - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <linux/kernel.h> -#include <linux/malloc.h> -#include <linux/input.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/usb.h> -#include <linux/serio.h> -#include <linux/config.h> - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver"); - -#define USB_VENDOR_ID_LOGITECH 0x046d -#define USB_DEVICE_ID_LOGITECH_WMFORCE 0xc281 - -#define IFORCE_MAX_LENGTH 16 - -#if defined(CONFIG_INPUT_IFORCE_232) || defined(CONFIG_INPUT_IFORCE_232_MODULE) -#define IFORCE_232 -#endif -#if defined(CONFIG_INPUT_IFORCE_USB) || defined(CONFIG_INPUT_IFORCE_USB_MODULE) -#define IFORCE_USB -#endif - -struct iforce { - signed char data[IFORCE_MAX_LENGTH]; - struct input_dev dev; - struct urb irq; - int open; - int idx, pkt, len, id; - unsigned char csum; -}; - -static struct { - __s32 x; - __s32 y; -} iforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; - -static char *iforce_name = "I-Force joystick/wheel"; - -static void iforce_process_packet(struct input_dev *dev, unsigned char id, int idx, unsigned char *data) -{ - switch (id) { - - case 1: /* joystick position data */ - case 3: /* wheel position data */ - - if (id == 1) { - input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0])); - input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2])); - input_report_abs(dev, ABS_THROTTLE, 255 - data[4]); - } else { - input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0])); - input_report_abs(dev, ABS_GAS, 255 - data[2]); - input_report_abs(dev, ABS_BRAKE, 255 - data[3]); - } - - input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x); - input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y); - - input_report_key(dev, BTN_TRIGGER, data[5] & 0x01); - input_report_key(dev, BTN_TOP, data[5] & 0x02); - input_report_key(dev, BTN_THUMB, data[5] & 0x04); - input_report_key(dev, BTN_TOP2, data[5] & 0x08); - input_report_key(dev, BTN_BASE, data[5] & 0x10); - input_report_key(dev, BTN_BASE2, data[5] & 0x20); - input_report_key(dev, BTN_BASE3, data[5] & 0x40); - input_report_key(dev, BTN_BASE4, data[5] & 0x80); - input_report_key(dev, BTN_BASE5, data[6] & 0x01); - input_report_key(dev, BTN_A, data[6] & 0x02); - input_report_key(dev, BTN_B, data[6] & 0x04); - input_report_key(dev, BTN_C, data[6] & 0x08); - break; - - case 2: /* force feedback effect status */ - break; - } -} - - -static int iforce_open(struct input_dev *dev) -{ - struct iforce *iforce = dev->private; - - if (dev->idbus == BUS_USB && !iforce->open++) - if (usb_submit_urb(&iforce->irq)) - return -EIO; - - return 0; -} - -static void iforce_close(struct input_dev *dev) -{ - struct iforce *iforce = dev->private; - - if (dev->idbus == BUS_USB && !--iforce->open) - usb_unlink_urb(&iforce->irq); -} - -static void iforce_input_setup(struct iforce *iforce) -{ - int i; - - iforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - iforce->dev.keybit[LONG(BTN_JOYSTICK)] |= BIT(BTN_TRIGGER) | BIT(BTN_TOP) | BIT(BTN_THUMB) | BIT(BTN_TOP2) | - BIT(BTN_BASE) | BIT(BTN_BASE2) | BIT(BTN_BASE3) | BIT(BTN_BASE4) | BIT(BTN_BASE5); - iforce->dev.keybit[LONG(BTN_GAMEPAD)] |= BIT(BTN_A) | BIT(BTN_B) | BIT(BTN_C); - iforce->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y) - | BIT(ABS_WHEEL) | BIT(ABS_GAS) | BIT(ABS_BRAKE); - - for (i = ABS_X; i <= ABS_Y; i++) { - iforce->dev.absmax[i] = 1920; - iforce->dev.absmin[i] = -1920; - iforce->dev.absflat[i] = 128; - iforce->dev.absfuzz[i] = 16; - } - - for (i = ABS_THROTTLE; i <= ABS_RUDDER; i++) { - iforce->dev.absmax[i] = 255; - iforce->dev.absmin[i] = 0; - } - - for (i = ABS_HAT0X; i <= ABS_HAT0Y; i++) { - iforce->dev.absmax[i] = 1; - iforce->dev.absmin[i] = -1; - } - - iforce->dev.private = iforce; - iforce->dev.open = iforce_open; - iforce->dev.close = iforce_close; - - input_register_device(&iforce->dev); -} - -#ifdef IFORCE_USB - -static void iforce_usb_irq(struct urb *urb) -{ - struct iforce *iforce = urb->context; - if (urb->status) return; - iforce_process_packet(&iforce->dev, iforce->data[0], 8, iforce->data + 1); -} - -static void *iforce_usb_probe(struct usb_device *dev, unsigned int ifnum) -{ - struct usb_endpoint_descriptor *endpoint; - struct iforce *iforce; - - if (dev->descriptor.idVendor != USB_VENDOR_ID_LOGITECH || - dev->descriptor.idProduct != USB_DEVICE_ID_LOGITECH_WMFORCE) - return NULL; - - endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0; - - if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL))) return NULL; - memset(iforce, 0, sizeof(struct iforce)); - - iforce->dev.name = iforce_name; - iforce->dev.idbus = BUS_USB; - iforce->dev.idvendor = dev->descriptor.idVendor; - iforce->dev.idproduct = dev->descriptor.idProduct; - iforce->dev.idversion = dev->descriptor.bcdDevice; - - FILL_INT_URB(&iforce->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), - iforce->data, 8, iforce_usb_irq, iforce, endpoint->bInterval); - - iforce_input_setup(iforce); - - printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n", - iforce->dev.number, iforce_name, dev->bus->busnum, dev->devnum, ifnum); - - return iforce; -} - -static void iforce_usb_disconnect(struct usb_device *dev, void *ptr) -{ - struct iforce *iforce = ptr; - usb_unlink_urb(&iforce->irq); - input_unregister_device(&iforce->dev); - kfree(iforce); -} - -static struct usb_driver iforce_usb_driver = { - name: "iforce", - probe: iforce_usb_probe, - disconnect: iforce_usb_disconnect, -}; - -#endif - -#ifdef IFORCE_232 - -static void iforce_serio_irq(struct serio *serio, unsigned char data, unsigned int flags) -{ - struct iforce* iforce = serio->private; - - if (!iforce->pkt) { - if (data != 0x2b) { - return; - } - iforce->pkt = 1; - return; - } - - if (!iforce->id) { - if (data > 3) { - iforce->pkt = 0; - return; - } - iforce->id = data; - return; - } - - if (!iforce->len) { - if (data > IFORCE_MAX_LENGTH) { - iforce->pkt = 0; - iforce->id = 0; - return; - } - iforce->len = data; - return; - } - - if (iforce->idx < iforce->len) { - iforce->csum += iforce->data[iforce->idx++] = data; - return; - } - - if (iforce->idx == iforce->len) { - iforce_process_packet(&iforce->dev, iforce->id, iforce->idx, iforce->data); - iforce->pkt = 0; - iforce->id = 0; - iforce->len = 0; - iforce->idx = 0; - iforce->csum = 0; - } -} - -static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev) -{ - struct iforce *iforce; - - if (serio->type != (SERIO_RS232 | SERIO_IFORCE)) - return; - - if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL))) return; - memset(iforce, 0, sizeof(struct iforce)); - - iforce->dev.name = iforce_name; - iforce->dev.idbus = BUS_RS232; - iforce->dev.idvendor = SERIO_IFORCE; - iforce->dev.idproduct = 0x0001; - iforce->dev.idversion = 0x0100; - - serio->private = iforce; - - if (serio_open(serio, dev)) { - kfree(iforce); - return; - } - - iforce_input_setup(iforce); - - printk(KERN_INFO "input%d: %s on serio%d\n", - iforce->dev.number, iforce_name, serio->number); -} - -static void iforce_serio_disconnect(struct serio *serio) -{ - struct iforce* iforce = serio->private; - input_unregister_device(&iforce->dev); - serio_close(serio); - kfree(iforce); -} - -static struct serio_dev iforce_serio_dev = { - interrupt: iforce_serio_irq, - connect: iforce_serio_connect, - disconnect: iforce_serio_disconnect, -}; - -#endif - -static int __init iforce_init(void) -{ -#ifdef IFORCE_USB - usb_register(&iforce_usb_driver); -#endif -#ifdef IFORCE_232 - serio_register_device(&iforce_serio_dev); -#endif - return 0; -} - -static void __exit iforce_exit(void) -{ -#ifdef IFORCE_USB - usb_deregister(&iforce_usb_driver); -#endif -#ifdef IFORCE_232 - serio_unregister_device(&iforce_serio_dev); -#endif -} - -module_init(iforce_init); -module_exit(iforce_exit); diff --git a/drivers/usb/inode.c b/drivers/usb/inode.c index 50970155b..b55073398 100644 --- a/drivers/usb/inode.c +++ b/drivers/usb/inode.c @@ -290,14 +290,14 @@ static int usbdevfs_root_readdir(struct file *filp, void *dirent, filldir_t fill i = filp->f_pos; switch (i) { case 0: - if (filldir(dirent, ".", 1, i, IROOT) < 0) + if (filldir(dirent, ".", 1, i, IROOT, DT_DIR) < 0) return 0; filp->f_pos++; i++; /* fall through */ case 1: - if (filldir(dirent, "..", 2, i, IROOT) < 0) + if (filldir(dirent, "..", 2, i, IROOT, DT_DIR) < 0) return 0; filp->f_pos++; i++; @@ -307,7 +307,7 @@ static int usbdevfs_root_readdir(struct file *filp, void *dirent, filldir_t fill while (i >= 2 && i < 2+NRSPECIAL) { spec = &special[filp->f_pos-2]; - if (filldir(dirent, spec->name, strlen(spec->name), i, ISPECIAL | (filp->f_pos-2+IROOT)) < 0) + if (filldir(dirent, spec->name, strlen(spec->name), i, ISPECIAL | (filp->f_pos-2+IROOT), DT_UNKNOWN) < 0) return 0; filp->f_pos++; i++; @@ -323,7 +323,7 @@ static int usbdevfs_root_readdir(struct file *filp, void *dirent, filldir_t fill } bus = list_entry(list, struct usb_bus, bus_list); sprintf(numbuf, "%03d", bus->busnum); - if (filldir(dirent, numbuf, 3, filp->f_pos, IBUS | ((bus->busnum & 0xff) << 8)) < 0) + if (filldir(dirent, numbuf, 3, filp->f_pos, IBUS | ((bus->busnum & 0xff) << 8), DT_UNKNOWN) < 0) break; filp->f_pos++; } @@ -343,7 +343,7 @@ static int bus_readdir(struct usb_device *dev, unsigned long ino, int pos, struc if (pos > 0) pos--; else { - if (filldir(dirent, numbuf, 3, filp->f_pos, ino | (dev->devnum & 0xff)) < 0) + if (filldir(dirent, numbuf, 3, filp->f_pos, ino | (dev->devnum & 0xff), DT_UNKNOWN) < 0) return -1; filp->f_pos++; } @@ -368,13 +368,13 @@ static int usbdevfs_bus_readdir(struct file *filp, void *dirent, filldir_t filld return -EINVAL; switch ((unsigned int)filp->f_pos) { case 0: - if (filldir(dirent, ".", 1, filp->f_pos, ino) < 0) + if (filldir(dirent, ".", 1, filp->f_pos, ino, DT_DIR) < 0) return 0; filp->f_pos++; /* fall through */ case 1: - if (filldir(dirent, "..", 2, filp->f_pos, IROOT) < 0) + if (filldir(dirent, "..", 2, filp->f_pos, IROOT, DT_DIR) < 0) return 0; filp->f_pos++; /* fall through */ diff --git a/drivers/usb/input.c b/drivers/usb/input.c deleted file mode 100644 index f3701965e..000000000 --- a/drivers/usb/input.c +++ /dev/null @@ -1,426 +0,0 @@ -/* - * $Id: input.c,v 1.7 2000/05/28 17:31:36 vojtech Exp $ - * - * Copyright (c) 1999-2000 Vojtech Pavlik - * - * The input layer module itself - * - * Sponsored by SuSE - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <linux/init.h> -#include <linux/input.h> -#include <linux/module.h> -#include <linux/random.h> - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_DESCRIPTION("Input layer module"); - -EXPORT_SYMBOL(input_register_device); -EXPORT_SYMBOL(input_unregister_device); -EXPORT_SYMBOL(input_register_handler); -EXPORT_SYMBOL(input_unregister_handler); -EXPORT_SYMBOL(input_register_minor); -EXPORT_SYMBOL(input_unregister_minor); -EXPORT_SYMBOL(input_open_device); -EXPORT_SYMBOL(input_close_device); -EXPORT_SYMBOL(input_event); - -#define INPUT_MAJOR 13 -#define INPUT_DEVICES 256 - -static struct input_dev *input_dev = NULL; -static struct input_handler *input_handler = NULL; -static struct input_handler *input_table[8] = { NULL, /* ... */ }; -static devfs_handle_t input_devfs_handle = NULL; -static int input_number = 0; -static long input_devices[NBITS(INPUT_DEVICES)] = { 0, /* ... */ }; - -void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) -{ - struct input_handle *handle = dev->handle; - -/* - * Filter non-events, and bad input values out. - */ - - if (type > EV_MAX || !test_bit(type, dev->evbit)) - return; - - switch (type) { - - case EV_KEY: - - if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value) - return; - - if (value == 2) break; - - change_bit(code, dev->key); - - if (test_bit(EV_REP, dev->evbit) && dev->timer.function) { - if (value) { - mod_timer(&dev->timer, jiffies + dev->rep[REP_DELAY]); - dev->repeat_key = code; - break; - } - if (dev->repeat_key == code) - del_timer(&dev->timer); - } - - break; - - case EV_ABS: - - if (code > ABS_MAX || !test_bit(code, dev->absbit)) - return; - - if (dev->absfuzz[code]) { - if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) && - (value < dev->abs[code] + (dev->absfuzz[code] >> 1))) - return; - - if ((value > dev->abs[code] - dev->absfuzz[code]) && - (value < dev->abs[code] + dev->absfuzz[code])) - value = (dev->abs[code] * 3 + value) >> 2; - - if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) && - (value < dev->abs[code] + (dev->absfuzz[code] << 1))) - value = (dev->abs[code] + value) >> 1; - } - - if (dev->abs[code] == value) - return; - - dev->abs[code] = value; - break; - - case EV_REL: - - if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0)) - return; - - break; - - case EV_LED: - - if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value) - return; - - change_bit(code, dev->led); - if (dev->event) dev->event(dev, type, code, value); - - break; - - case EV_SND: - - if (code > SND_MAX || !test_bit(code, dev->sndbit) || !!test_bit(code, dev->snd) == value) - return; - - change_bit(code, dev->snd); - if (dev->event) dev->event(dev, type, code, value); - - break; - - case EV_REP: - - if (code > REP_MAX || dev->rep[code] == value) return; - - dev->rep[code] = value; - if (dev->event) dev->event(dev, type, code, value); - - break; - } -/* - * Add randomness. - */ - -#if 0 /* BUG */ - add_input_randomness(((unsigned long) dev) ^ (type << 24) ^ (code << 16) ^ value); -#endif - -/* - * Distribute the event to handler modules. - */ - - while (handle) { - if (handle->open) - handle->handler->event(handle, type, code, value); - handle = handle->dnext; - } -} - -static void input_repeat_key(unsigned long data) -{ - struct input_dev *dev = (void *) data; - input_event(dev, EV_KEY, dev->repeat_key, 2); - mod_timer(&dev->timer, jiffies + dev->rep[REP_PERIOD]); -} - -int input_open_device(struct input_handle *handle) -{ - handle->open++; - if (handle->dev->open) - return handle->dev->open(handle->dev); - return 0; -} - -void input_close_device(struct input_handle *handle) -{ - if (handle->dev->close) - handle->dev->close(handle->dev); - handle->open--; -} - -static void input_link_handle(struct input_handle *handle) -{ - handle->dnext = handle->dev->handle; - handle->hnext = handle->handler->handle; - handle->dev->handle = handle; - handle->handler->handle = handle; -} - -static void input_unlink_handle(struct input_handle *handle) -{ - struct input_handle **handleptr; - - handleptr = &handle->dev->handle; - while (*handleptr && (*handleptr != handle)) - handleptr = &((*handleptr)->dnext); - *handleptr = (*handleptr)->dnext; - - handleptr = &handle->handler->handle; - while (*handleptr && (*handleptr != handle)) - handleptr = &((*handleptr)->hnext); - *handleptr = (*handleptr)->hnext; -} - -void input_register_device(struct input_dev *dev) -{ - struct input_handler *handler = input_handler; - struct input_handle *handle; - -/* - * Initialize repeat timer to default values. - */ - - init_timer(&dev->timer); - dev->timer.data = (long) dev; - dev->timer.function = input_repeat_key; - dev->rep[REP_DELAY] = HZ/4; - dev->rep[REP_PERIOD] = HZ/33; - -/* - * Add the device. - */ - - if (input_number >= INPUT_DEVICES) { - printk(KERN_WARNING "input: ran out of input device numbers!\n"); - dev->number = input_number; - } else { - dev->number = find_first_zero_bit(input_devices, INPUT_DEVICES); - set_bit(dev->number, input_devices); - } - - dev->next = input_dev; - input_dev = dev; - input_number++; - -/* - * Notify handlers. - */ - - while (handler) { - if ((handle = handler->connect(handler, dev))) - input_link_handle(handle); - handler = handler->next; - } -} - -void input_unregister_device(struct input_dev *dev) -{ - struct input_handle *handle = dev->handle; - struct input_dev **devptr = &input_dev; - struct input_handle *dnext; - -/* - * Kill any pending repeat timers. - */ - - del_timer(&dev->timer); - -/* - * Notify handlers. - */ - - while (handle) { - dnext = handle->dnext; - input_unlink_handle(handle); - handle->handler->disconnect(handle); - handle = dnext; - } - -/* - * Remove the device. - */ - - while (*devptr && (*devptr != dev)) - devptr = &((*devptr)->next); - *devptr = (*devptr)->next; - - input_number--; - - if (dev->number < INPUT_DEVICES) - clear_bit(dev->number, input_devices); -} - -void input_register_handler(struct input_handler *handler) -{ - struct input_dev *dev = input_dev; - struct input_handle *handle; - -/* - * Add minors if needed. - */ - - if (handler->fops != NULL) - input_table[handler->minor >> 5] = handler; - -/* - * Add the handler. - */ - - handler->next = input_handler; - input_handler = handler; - -/* - * Notify it about all existing devices. - */ - - while (dev) { - if ((handle = handler->connect(handler, dev))) - input_link_handle(handle); - dev = dev->next; - } -} - -void input_unregister_handler(struct input_handler *handler) -{ - struct input_handler **handlerptr = &input_handler; - struct input_handle *handle = handler->handle; - struct input_handle *hnext; - -/* - * Tell the handler to disconnect from all devices it keeps open. - */ - - while (handle) { - hnext = handle->hnext; - input_unlink_handle(handle); - handler->disconnect(handle); - handle = hnext; - } - -/* - * Remove it. - */ - - while (*handlerptr && (*handlerptr != handler)) - handlerptr = &((*handlerptr)->next); - - *handlerptr = (*handlerptr)->next; - -/* - * Remove minors. - */ - - if (handler->fops != NULL) - input_table[handler->minor >> 5] = NULL; -} - -static int input_open_file(struct inode *inode, struct file *file) -{ - struct input_handler *handler = input_table[MINOR(inode->i_rdev) >> 5]; - struct file_operations *old_fops, *new_fops = NULL; - int err; - - /* No load-on-demand here? */ - if (!handler || !(new_fops = fops_get(handler->fops))) - return -ENODEV; - - /* - * That's _really_ odd. Usually NULL ->open means "nothing special", - * not "no device". Oh, well... - */ - if (!new_fops->open) { - fops_put(new_fops); - return -ENODEV; - } - old_fops = file->f_op; - file->f_op = new_fops; - err = new_fops->open(inode, file); - if (err) { - fops_put(file->f_op); - file->f_op = fops_get(old_fops); - } - fops_put(old_fops); - return err; -} - -static struct file_operations input_fops = { - owner: THIS_MODULE, - open: input_open_file, -}; - -devfs_handle_t input_register_minor(char *name, int minor, int minor_base) -{ - char devfs_name[16]; - sprintf(devfs_name, name, minor); - return devfs_register(input_devfs_handle, devfs_name, DEVFS_FL_DEFAULT, INPUT_MAJOR, minor + minor_base, - S_IFCHR | S_IRUGO | S_IWUSR, &input_fops, NULL); -} - -void input_unregister_minor(devfs_handle_t handle) -{ - devfs_unregister(handle); -} - -static int __init input_init(void) -{ - if (devfs_register_chrdev(INPUT_MAJOR, "input", &input_fops)) { - printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR); - return -EBUSY; - } - input_devfs_handle = devfs_mk_dir(NULL, "input", NULL); - return 0; -} - -static void __exit input_exit(void) -{ - devfs_unregister(input_devfs_handle); - if (devfs_unregister_chrdev(INPUT_MAJOR, "input")) - printk(KERN_ERR "input: can't unregister char major %d", INPUT_MAJOR); -} - -module_init(input_init); -module_exit(input_exit); diff --git a/drivers/usb/joydev.c b/drivers/usb/joydev.c deleted file mode 100644 index 99b17a3cd..000000000 --- a/drivers/usb/joydev.c +++ /dev/null @@ -1,497 +0,0 @@ -/* - * $Id: joydev.c,v 1.11 2000/06/23 09:23:00 vojtech Exp $ - * - * Copyright (c) 1999-2000 Vojtech Pavlik - * Copyright (c) 1999 Colin Van Dyke - * - * Joystick device driver for the input driver suite. - * - * Sponsored by SuSE and Intel - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <asm/segment.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/joystick.h> -#include <linux/input.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/malloc.h> -#include <linux/mm.h> -#include <linux/miscdevice.h> -#include <linux/module.h> -#include <linux/poll.h> -#include <linux/init.h> -#include <linux/smp_lock.h> - -#define JOYDEV_MINOR_BASE 0 -#define JOYDEV_MINORS 32 -#define JOYDEV_BUFFER_SIZE 64 - -struct joydev { - int exist; - int open; - int minor; - struct input_handle handle; - wait_queue_head_t wait; - devfs_handle_t devfs; - struct joydev *next; - struct joydev_list *list; - struct js_corr corr[ABS_MAX]; - struct JS_DATA_SAVE_TYPE glue; - int nabs; - int nkey; - __u16 keymap[KEY_MAX - BTN_MISC]; - __u16 keypam[KEY_MAX - BTN_MISC]; - __u8 absmap[ABS_MAX]; - __u8 abspam[ABS_MAX]; - __s16 abs[ABS_MAX]; -}; - -struct joydev_list { - struct js_event buffer[JOYDEV_BUFFER_SIZE]; - int head; - int tail; - int startup; - struct fasync_struct *fasync; - struct joydev *joydev; - struct joydev_list *next; -}; - -static struct joydev *joydev_table[JOYDEV_MINORS]; - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_DESCRIPTION("Joystick device driver"); -MODULE_SUPPORTED_DEVICE("input/js"); - -static int joydev_correct(int value, struct js_corr *corr) -{ - switch (corr->type) { - case JS_CORR_NONE: - break; - case JS_CORR_BROKEN: - value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : - ((corr->coef[3] * (value - corr->coef[1])) >> 14)) : - ((corr->coef[2] * (value - corr->coef[0])) >> 14); - break; - default: - return 0; - } - - if (value < -32767) return -32767; - if (value > 32767) return 32767; - - return value; -} - -static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) -{ - struct joydev *joydev = handle->private; - struct joydev_list *list = joydev->list; - struct js_event event; - - switch (type) { - - case EV_KEY: - if (code < BTN_MISC || value == 2) return; - event.type = JS_EVENT_BUTTON; - event.number = joydev->keymap[code - BTN_MISC]; - event.value = value; - break; - - case EV_ABS: - event.type = JS_EVENT_AXIS; - event.number = joydev->absmap[code]; - event.value = joydev_correct(value, joydev->corr + event.number); - if (event.value == joydev->abs[event.number]) return; - joydev->abs[event.number] = event.value; - break; - - default: - return; - } - - event.time = jiffies * (1000 / HZ); - - while (list) { - - memcpy(list->buffer + list->head, &event, sizeof(struct js_event)); - - if (list->startup == joydev->nabs + joydev->nkey) - if (list->tail == (list->head = (list->head + 1) & (JOYDEV_BUFFER_SIZE - 1))) - list->startup = 0; - - kill_fasync(&list->fasync, SIGIO, POLL_IN); - - list = list->next; - } - - wake_up_interruptible(&joydev->wait); -} - -static int joydev_fasync(int fd, struct file *file, int on) -{ - int retval; - struct joydev_list *list = file->private_data; - retval = fasync_helper(fd, file, on, &list->fasync); - return retval < 0 ? retval : 0; -} - -static int joydev_release(struct inode * inode, struct file * file) -{ - struct joydev_list *list = file->private_data; - struct joydev_list **listptr; - - lock_kernel(); - listptr = &list->joydev->list; - joydev_fasync(-1, file, 0); - - while (*listptr && (*listptr != list)) - listptr = &((*listptr)->next); - *listptr = (*listptr)->next; - - if (!--list->joydev->open) { - if (list->joydev->exist) { - input_close_device(&list->joydev->handle); - } else { - input_unregister_minor(list->joydev->devfs); - joydev_table[list->joydev->minor] = NULL; - kfree(list->joydev); - } - } - - kfree(list); - unlock_kernel(); - - return 0; -} - -static int joydev_open(struct inode *inode, struct file *file) -{ - struct joydev_list *list; - int i = MINOR(inode->i_rdev) - JOYDEV_MINOR_BASE; - - if (i > JOYDEV_MINORS || !joydev_table[i]) - return -ENODEV; - - if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL))) - return -ENOMEM; - memset(list, 0, sizeof(struct joydev_list)); - - list->joydev = joydev_table[i]; - list->next = joydev_table[i]->list; - joydev_table[i]->list = list; - - file->private_data = list; - - if (!list->joydev->open++) - if (list->joydev->exist) - input_open_device(&list->joydev->handle); - - return 0; -} - -static ssize_t joydev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -static ssize_t joydev_read(struct file *file, char *buf, size_t count, loff_t *ppos) -{ - DECLARE_WAITQUEUE(wait, current); - struct joydev_list *list = file->private_data; - struct joydev *joydev = list->joydev; - struct input_dev *input = joydev->handle.dev; - int retval = 0; - - if (count < sizeof(struct js_event)) - return -EINVAL; - - if (count == sizeof(struct JS_DATA_TYPE)) { - - struct JS_DATA_TYPE data; - - data.buttons = ((joydev->nkey > 0 && test_bit(joydev->keypam[0], input->key)) ? 1 : 0) | - ((joydev->nkey > 1 && test_bit(joydev->keypam[1], input->key)) ? 2 : 0); - data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x; - data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y; - - if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE))) - return -EFAULT; - - list->startup = 0; - list->tail = list->head; - - return sizeof(struct JS_DATA_TYPE); - } - - if (list->head == list->tail && list->startup == joydev->nabs + joydev->nkey) { - - add_wait_queue(&list->joydev->wait, &wait); - current->state = TASK_INTERRUPTIBLE; - - while (list->head == list->tail) { - - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - - schedule(); - } - - current->state = TASK_RUNNING; - remove_wait_queue(&list->joydev->wait, &wait); - } - - if (retval) - return retval; - - while (list->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) { - - struct js_event event; - - event.time = jiffies * (1000/HZ); - - if (list->startup < joydev->nkey) { - event.type = JS_EVENT_BUTTON | JS_EVENT_INIT; - event.number = list->startup; - event.value = !!test_bit(joydev->keypam[event.number], input->key); - } else { - event.type = JS_EVENT_AXIS | JS_EVENT_INIT; - event.number = list->startup - joydev->nkey; - event.value = joydev->abs[event.number]; - } - - if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) - return -EFAULT; - - list->startup++; - retval += sizeof(struct js_event); - } - - while (list->head != list->tail && retval + sizeof(struct js_event) <= count) { - - if (copy_to_user(buf + retval, list->buffer + list->tail, sizeof(struct js_event))) - return -EFAULT; - - list->tail = (list->tail + 1) & (JOYDEV_BUFFER_SIZE - 1); - retval += sizeof(struct js_event); - } - - return retval; -} - -/* No kernel lock - fine */ -static unsigned int joydev_poll(struct file *file, poll_table *wait) -{ - struct joydev_list *list = file->private_data; - poll_wait(file, &list->joydev->wait, wait); - if (list->head != list->tail || list->startup < list->joydev->nabs + list->joydev->nkey) - return POLLIN | POLLRDNORM; - return 0; -} - -static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct joydev_list *list = file->private_data; - struct joydev *joydev = list->joydev; - struct input_dev *dev = joydev->handle.dev; - - - switch (cmd) { - - case JS_SET_CAL: - return copy_from_user(&joydev->glue.JS_CORR, (struct JS_DATA_TYPE *) arg, - sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; - case JS_GET_CAL: - return copy_to_user((struct JS_DATA_TYPE *) arg, &joydev->glue.JS_CORR, - sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; - case JS_SET_TIMEOUT: - return get_user(joydev->glue.JS_TIMEOUT, (int *) arg); - case JS_GET_TIMEOUT: - return put_user(joydev->glue.JS_TIMEOUT, (int *) arg); - case JS_SET_TIMELIMIT: - return get_user(joydev->glue.JS_TIMELIMIT, (long *) arg); - case JS_GET_TIMELIMIT: - return put_user(joydev->glue.JS_TIMELIMIT, (long *) arg); - case JS_SET_ALL: - return copy_from_user(&joydev->glue, (struct JS_DATA_SAVE_TYPE *) arg, - sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; - case JS_GET_ALL: - return copy_to_user((struct JS_DATA_SAVE_TYPE *) arg, &joydev->glue, - sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; - - case JSIOCGVERSION: - return put_user(JS_VERSION, (__u32 *) arg); - case JSIOCGAXES: - return put_user(joydev->nabs, (__u8 *) arg); - case JSIOCGBUTTONS: - return put_user(joydev->nkey, (__u8 *) arg); - case JSIOCSCORR: - return copy_from_user(joydev->corr, (struct js_corr *) arg, - sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0; - case JSIOCGCORR: - return copy_to_user((struct js_corr *) arg, joydev->corr, - sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0; - default: - if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) { - int len; - if (!dev->name) return 0; - len = strlen(dev->name) + 1; - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - if (copy_to_user((char *) arg, dev->name, len)) return -EFAULT; - return len; - } - } - return -EINVAL; -} - -static struct file_operations joydev_fops = { - owner: THIS_MODULE, - read: joydev_read, - write: joydev_write, - poll: joydev_poll, - open: joydev_open, - release: joydev_release, - ioctl: joydev_ioctl, - fasync: joydev_fasync, -}; - -static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev) -{ - struct joydev *joydev; - int i, j, minor; - - if (!(test_bit(EV_KEY, dev->evbit) && test_bit(EV_ABS, dev->evbit) && - test_bit(ABS_X, dev->absbit) && test_bit(ABS_Y, dev->absbit) && - (test_bit(BTN_TRIGGER, dev->keybit) || test_bit(BTN_A, dev->keybit) - || test_bit(BTN_1, dev->keybit)))) return NULL; - - for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++); - if (joydev_table[minor]) { - printk(KERN_ERR "joydev: no more free joydev devices\n"); - return NULL; - } - - if (!(joydev = kmalloc(sizeof(struct joydev), GFP_KERNEL))) - return NULL; - memset(joydev, 0, sizeof(struct joydev)); - - init_waitqueue_head(&joydev->wait); - - joydev->minor = minor; - joydev_table[minor] = joydev; - - joydev->handle.dev = dev; - joydev->handle.handler = handler; - joydev->handle.private = joydev; - - joydev->exist = 1; - - for (i = 0; i < ABS_MAX; i++) - if (test_bit(i, dev->absbit)) { - joydev->absmap[i] = joydev->nabs; - joydev->abspam[joydev->nabs] = i; - joydev->nabs++; - } - - for (i = BTN_JOYSTICK - BTN_MISC; i < KEY_MAX - BTN_MISC; i++) - if (test_bit(i + BTN_MISC, dev->keybit)) { - joydev->keymap[i] = joydev->nkey; - joydev->keypam[joydev->nkey] = i + BTN_MISC; - joydev->nkey++; - } - - for (i = 0; i < BTN_JOYSTICK - BTN_MISC; i++) - if (test_bit(i + BTN_MISC, dev->keybit)) { - joydev->keymap[i] = joydev->nkey; - joydev->keypam[joydev->nkey] = i + BTN_MISC; - joydev->nkey++; - } - - for (i = 0; i < joydev->nabs; i++) { - j = joydev->abspam[i]; - if (dev->absmax[j] == dev->absmin[j]) { - joydev->corr[i].type = JS_CORR_NONE; - continue; - } - joydev->corr[i].type = JS_CORR_BROKEN; - joydev->corr[i].prec = dev->absfuzz[j]; - joydev->corr[i].coef[0] = (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j]; - joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j]; - joydev->corr[i].coef[2] = (1 << 29) / ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]); - joydev->corr[i].coef[3] = (1 << 29) / ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]); - - joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i); - } - - joydev->devfs = input_register_minor("js%d", minor, JOYDEV_MINOR_BASE); - - printk(KERN_INFO "js%d: Joystick device for input%d\n", minor, dev->number); - - return &joydev->handle; -} - -static void joydev_disconnect(struct input_handle *handle) -{ - struct joydev *joydev = handle->private; - - joydev->exist = 0; - - if (joydev->open) { - input_close_device(handle); - } else { - input_unregister_minor(joydev->devfs); - joydev_table[joydev->minor] = NULL; - kfree(joydev); - } -} - -static struct input_handler joydev_handler = { - event: joydev_event, - connect: joydev_connect, - disconnect: joydev_disconnect, - fops: &joydev_fops, - minor: JOYDEV_MINOR_BASE, -}; - -static int __init joydev_init(void) -{ - input_register_handler(&joydev_handler); - return 0; -} - -static void __exit joydev_exit(void) -{ - input_unregister_handler(&joydev_handler); -} - -module_init(joydev_init); -module_exit(joydev_exit); diff --git a/drivers/usb/keybdev.c b/drivers/usb/keybdev.c deleted file mode 100644 index 95fce5e80..000000000 --- a/drivers/usb/keybdev.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * $Id: keybdev.c,v 1.3 2000/05/28 17:31:36 vojtech Exp $ - * - * Copyright (c) 1999-2000 Vojtech Pavlik - * - * Input driver to keyboard driver binding. - * - * Sponsored by SuSE - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <linux/config.h> -#include <linux/kbd_ll.h> -#include <linux/input.h> -#include <linux/malloc.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/kbd_kern.h> - -#if defined(CONFIG_X86) || defined(CONFIG_IA64) || defined(__alpha__) || defined(__mips__) - -static int x86_sysrq_alt = 0; - -static unsigned short x86_keycodes[256] = - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 43, 85, 86, 87, 88,115,119,120,121,375,123, 90, - 284,285,309,298,312, 91,327,328,329,331,333,335,336,337,338,339, - 367,294,293,286,350, 92,334,512,116,377,109,111,373,347,348,349, - 360, 93, 94, 95, 98,376,100,101,357,316,354,304,289,102,351,355, - 103,104,105,275,281,272,306,106,274,107,288,364,358,363,362,361, - 291,108,381,290,287,292,279,305,280, 99,112,257,258,113,270,114, - 118,117,125,374,379,259,260,261,262,263,264,265,266,267,268,269, - 271,273,276,277,278,282,283,295,296,297,299,300,301,302,303,307, - 308,310,313,314,315,317,318,319,320,321,322,323,324,325,326,330, - 332,340,341,342,343,344,345,346,356,359,365,368,369,370,371,372 }; - -static int emulate_raw(unsigned int keycode, int down) -{ - if (keycode > 255 || !x86_keycodes[keycode]) - return -1; - - if (keycode == KEY_PAUSE) { - handle_scancode(0xe1, 1); - handle_scancode(0x1d, down); - handle_scancode(0x45, down); - return 0; - } - - if (keycode == KEY_SYSRQ && x86_sysrq_alt) { - handle_scancode(0x54, down); - return 0; - } - - if (x86_keycodes[keycode] & 0x100) - handle_scancode(0xe0, 1); - - handle_scancode(x86_keycodes[keycode] & 0x7f, down); - - if (keycode == KEY_SYSRQ) { - handle_scancode(0xe0, 1); - handle_scancode(0x37, down); - } - - if (keycode == KEY_LEFTALT || keycode == KEY_RIGHTALT) - x86_sysrq_alt = down; - - return 0; -} - -#elif defined(CONFIG_ADB_KEYBOARD) - -static unsigned char mac_keycodes[128] = - { 0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48, - 12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54,128, 1, - 2, 3, 5, 4, 38, 40, 37, 41, 39, 50, 56, 42, 6, 7, 8, 9, - 11, 45, 46, 43, 47, 44,123, 67, 58, 49, 57,122,120, 99,118, 96, - 97, 98,100,101,109, 71,107, 89, 91, 92, 78, 86, 87, 88, 69, 83, - 84, 85, 82, 65, 42, 0, 10,103,111, 0, 0, 0, 0, 0, 0, 0, - 76,125, 75,105,124, 0,115, 62,116, 59, 60,119, 61,121,114,117, - 0, 0, 0, 0,127, 81, 0,113, 0, 0, 0, 0, 0, 55, 55 }; - -static int emulate_raw(unsigned int keycode, int down) -{ - if (keycode > 127 || !mac_keycodes[keycode]) - return -1; - - handle_scancode(mac_keycodes[keycode] & 0x7f, down); - - return 0; -} - -#endif - -static struct input_handler keybdev_handler; - -void keybdev_ledfunc(unsigned int led) -{ - struct input_handle *handle; - - for (handle = keybdev_handler.handle; handle; handle = handle->hnext) { - - input_event(handle->dev, EV_LED, LED_SCROLLL, !!(led & 0x01)); - input_event(handle->dev, EV_LED, LED_NUML, !!(led & 0x02)); - input_event(handle->dev, EV_LED, LED_CAPSL, !!(led & 0x04)); - - } -} - -void keybdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int down) -{ - if (type != EV_KEY) return; - - if (emulate_raw(code, down)) - printk(KERN_WARNING "keyboard.c: can't emulate rawmode for keycode %d\n", code); - - tasklet_schedule(&keyboard_tasklet); -} - -static struct input_handle *keybdev_connect(struct input_handler *handler, struct input_dev *dev) -{ - struct input_handle *handle; - int i; - - if (!test_bit(EV_KEY, dev->evbit)) - return NULL; - - for (i = KEY_RESERVED; i < BTN_MISC; i++) - if (test_bit(i, dev->keybit)) break; - - if (i == BTN_MISC) - return NULL; - - if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL))) - return NULL; - memset(handle, 0, sizeof(struct input_handle)); - - handle->dev = dev; - handle->handler = handler; - - input_open_device(handle); - - printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number); - - return handle; -} - -static void keybdev_disconnect(struct input_handle *handle) -{ - printk(KERN_INFO "keybdev.c: Removing keyboard: input%d\n", handle->dev->number); - input_close_device(handle); - kfree(handle); -} - -static struct input_handler keybdev_handler = { - event: keybdev_event, - connect: keybdev_connect, - disconnect: keybdev_disconnect, -}; - -static int __init keybdev_init(void) -{ - input_register_handler(&keybdev_handler); - kbd_ledfunc = keybdev_ledfunc; - return 0; -} - -static void __exit keybdev_exit(void) -{ - kbd_ledfunc = NULL; - input_unregister_handler(&keybdev_handler); -} - -module_init(keybdev_init); -module_exit(keybdev_exit); - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_DESCRIPTION("Input driver to keyboard driver binding"); diff --git a/drivers/usb/mousedev.c b/drivers/usb/mousedev.c deleted file mode 100644 index ccb118520..000000000 --- a/drivers/usb/mousedev.c +++ /dev/null @@ -1,486 +0,0 @@ -/* - * $Id: mousedev.c,v 1.10 2000/06/23 09:23:00 vojtech Exp $ - * - * Copyright (c) 1999-2000 Vojtech Pavlik - * - * Input driver to PS/2 or ImPS/2 device driver module. - * - * Sponsored by SuSE - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#define MOUSEDEV_MINOR_BASE 32 -#define MOUSEDEV_MINORS 32 -#define MOUSEDEV_MIX 31 - -#include <linux/malloc.h> -#include <linux/poll.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/input.h> -#include <linux/config.h> -#include <linux/smp_lock.h> - -#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X -#define CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024 -#endif -#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y -#define CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768 -#endif - -struct mousedev { - int exist; - int open; - int minor; - wait_queue_head_t wait; - struct mousedev_list *list; - struct input_handle handle; - devfs_handle_t devfs; -}; - -struct mousedev_list { - struct fasync_struct *fasync; - struct mousedev *mousedev; - struct mousedev_list *next; - int dx, dy, dz, oldx, oldy; - char ps2[6]; - unsigned long buttons; - unsigned char ready, buffer, bufsiz; - unsigned char mode, genseq, impseq; -}; - -#define MOUSEDEV_GENIUS_LEN 5 -#define MOUSEDEV_IMPS_LEN 6 - -static unsigned char mousedev_genius_seq[] = { 0xe8, 3, 0xe6, 0xe6, 0xe6 }; -static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 }; - -static struct input_handler mousedev_handler; - -static struct mousedev *mousedev_table[MOUSEDEV_MINORS]; -static struct mousedev mousedev_mix; - -static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) -{ - struct mousedev *mousedevs[3] = { handle->private, &mousedev_mix, NULL }; - struct mousedev **mousedev = mousedevs; - struct mousedev_list *list; - int index, size; - - while (*mousedev) { - list = (*mousedev)->list; - while (list) { - switch (type) { - case EV_ABS: - switch (code) { - case ABS_X: - size = handle->dev->absmax[ABS_X] - handle->dev->absmin[ABS_X]; - list->dx += (value * CONFIG_INPUT_MOUSEDEV_SCREEN_X - list->oldx) / size; - list->oldx += list->dx * size; - break; - case ABS_Y: - size = handle->dev->absmax[ABS_Y] - handle->dev->absmin[ABS_Y]; - list->dy -= (value * CONFIG_INPUT_MOUSEDEV_SCREEN_Y - list->oldy) / size; - list->oldy -= list->dy * size; - break; - } - break; - case EV_REL: - switch (code) { - case REL_X: list->dx += value; break; - case REL_Y: list->dy -= value; break; - case REL_WHEEL: if (list->mode) list->dz -= value; break; - } - break; - - case EV_KEY: - switch (code) { - case BTN_0: - case BTN_TOUCH: - case BTN_LEFT: index = 0; break; - case BTN_4: - case BTN_EXTRA: if (list->mode > 1) { index = 4; break; } - case BTN_STYLUS: - case BTN_1: - case BTN_RIGHT: index = 1; break; - case BTN_3: - case BTN_SIDE: if (list->mode > 1) { index = 3; break; } - case BTN_2: - case BTN_STYLUS2: - case BTN_MIDDLE: index = 2; break; - default: return; - } - switch (value) { - case 0: clear_bit(index, &list->buttons); break; - case 1: set_bit(index, &list->buttons); break; - case 2: return; - } - break; - } - - list->ready = 1; - - kill_fasync(&list->fasync, SIGIO, POLL_IN); - - list = list->next; - } - - wake_up_interruptible(&((*mousedev)->wait)); - mousedev++; - } -} - -static int mousedev_fasync(int fd, struct file *file, int on) -{ - int retval; - struct mousedev_list *list = file->private_data; - retval = fasync_helper(fd, file, on, &list->fasync); - return retval < 0 ? retval : 0; -} - -static int mousedev_release(struct inode * inode, struct file * file) -{ - struct mousedev_list *list = file->private_data; - struct mousedev_list **listptr; - - lock_kernel(); - listptr = &list->mousedev->list; - mousedev_fasync(-1, file, 0); - - while (*listptr && (*listptr != list)) - listptr = &((*listptr)->next); - *listptr = (*listptr)->next; - - if (!--list->mousedev->open) { - if (list->mousedev->minor == MOUSEDEV_MIX) { - struct input_handle *handle = mousedev_handler.handle; - while (handle) { - struct mousedev *mousedev = handle->private; - if (!mousedev->open) { - if (mousedev->exist) { - input_close_device(&mousedev->handle); - } else { - input_unregister_minor(mousedev->devfs); - mousedev_table[mousedev->minor] = NULL; - kfree(mousedev); - } - } - handle = handle->hnext; - } - } else { - if (!mousedev_mix.open) { - if (list->mousedev->exist) { - input_close_device(&list->mousedev->handle); - } else { - input_unregister_minor(list->mousedev->devfs); - mousedev_table[list->mousedev->minor] = NULL; - kfree(list->mousedev); - } - } - } - } - - kfree(list); - unlock_kernel(); - - return 0; -} - -static int mousedev_open(struct inode * inode, struct file * file) -{ - struct mousedev_list *list; - int i = MINOR(inode->i_rdev) - MOUSEDEV_MINOR_BASE; - - if (i > MOUSEDEV_MINORS || !mousedev_table[i]) - return -ENODEV; - - if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL))) - return -ENOMEM; - memset(list, 0, sizeof(struct mousedev_list)); - - list->mousedev = mousedev_table[i]; - list->next = mousedev_table[i]->list; - mousedev_table[i]->list = list; - file->private_data = list; - - if (!list->mousedev->open++) { - if (list->mousedev->minor == MOUSEDEV_MIX) { - struct input_handle *handle = mousedev_handler.handle; - while (handle) { - struct mousedev *mousedev = handle->private; - if (!mousedev->open) - if (mousedev->exist) - input_open_device(handle); - handle = handle->hnext; - } - } else { - if (!mousedev_mix.open) - if (list->mousedev->exist) - input_open_device(&list->mousedev->handle); - } - } - - return 0; -} - -static void mousedev_packet(struct mousedev_list *list, unsigned char off) -{ - list->ps2[off] = 0x08 | ((list->dx < 0) << 4) | ((list->dy < 0) << 5) | (list->buttons & 0x07); - list->ps2[off + 1] = (list->dx > 127 ? 127 : (list->dx < -127 ? -127 : list->dx)); - list->ps2[off + 2] = (list->dy > 127 ? 127 : (list->dy < -127 ? -127 : list->dy)); - list->dx -= list->ps2[off + 1]; - list->dy -= list->ps2[off + 2]; - list->bufsiz = off + 3; - - if (list->mode > 1) - list->ps2[off] |= ((list->buttons & 0x30) << 2); - - if (list->mode) { - list->ps2[off + 3] = (list->dz > 127 ? 127 : (list->dz < -127 ? -127 : list->dz)); - list->bufsiz++; - list->dz -= list->ps2[off + 3]; - } - if (!list->dx && !list->dy && (!list->mode || !list->dz)) list->ready = 0; - list->buffer = list->bufsiz; -} - - -static ssize_t mousedev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos) -{ - struct mousedev_list *list = file->private_data; - unsigned char c; - int i; - - for (i = 0; i < count; i++) { - - c = buffer[i]; - - if (c == mousedev_genius_seq[list->genseq]) { - if (++list->genseq == MOUSEDEV_GENIUS_LEN) { - list->genseq = 0; - list->ready = 1; - list->mode = 2; - } - } else list->genseq = 0; - - if (c == mousedev_imps_seq[list->impseq]) { - if (++list->impseq == MOUSEDEV_IMPS_LEN) { - list->impseq = 0; - list->ready = 1; - list->mode = 1; - } - } else list->impseq = 0; - - list->ps2[0] = 0xfa; - list->bufsiz = 1; - - switch (c) { - - case 0xeb: /* Poll */ - mousedev_packet(list, 1); - break; - - case 0xf2: /* Get ID */ - list->ps2[1] = (list->mode == 1) ? 3 : 0; - list->bufsiz = 2; - break; - - case 0xe9: /* Get info */ - if (list->mode == 2) { - list->ps2[1] = 0x00; list->ps2[2] = 0x33; list->ps2[3] = 0x55; - } else { - list->ps2[1] = 0x60; list->ps2[2] = 3; list->ps2[3] = 200; - } - list->bufsiz = 4; - break; - } - - list->buffer = list->bufsiz; - } - - kill_fasync(&list->fasync, SIGIO, POLL_IN); - - wake_up_interruptible(&list->mousedev->wait); - - return count; -} - -static ssize_t mousedev_read(struct file * file, char * buffer, size_t count, loff_t *ppos) -{ - DECLARE_WAITQUEUE(wait, current); - struct mousedev_list *list = file->private_data; - int retval = 0; - - if (!list->ready && !list->buffer) { - - add_wait_queue(&list->mousedev->wait, &wait); - current->state = TASK_INTERRUPTIBLE; - - while (!list->ready) { - - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - - schedule(); - } - - current->state = TASK_RUNNING; - remove_wait_queue(&list->mousedev->wait, &wait); - } - - if (retval) - return retval; - - if (!list->buffer) - mousedev_packet(list, 0); - - if (count > list->buffer) - count = list->buffer; - - if (copy_to_user(buffer, list->ps2 + list->bufsiz - list->buffer, count)) - return -EFAULT; - - list->buffer -= count; - - return count; -} - -/* No kernel lock - fine */ -static unsigned int mousedev_poll(struct file *file, poll_table *wait) -{ - struct mousedev_list *list = file->private_data; - poll_wait(file, &list->mousedev->wait, wait); - if (list->ready || list->buffer) - return POLLIN | POLLRDNORM; - return 0; -} - -struct file_operations mousedev_fops = { - owner: THIS_MODULE, - read: mousedev_read, - write: mousedev_write, - poll: mousedev_poll, - open: mousedev_open, - release: mousedev_release, - fasync: mousedev_fasync, -}; - -static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev) -{ - struct mousedev *mousedev; - int minor = 0; - - if (!test_bit(EV_KEY, dev->evbit) || - (!test_bit(BTN_LEFT, dev->keybit) && !test_bit(BTN_TOUCH, dev->keybit))) - return NULL; - - if ((!test_bit(EV_REL, dev->evbit) || !test_bit(REL_X, dev->relbit)) && - (!test_bit(EV_ABS, dev->evbit) || !test_bit(ABS_X, dev->absbit))) - return NULL; - - for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++); - if (mousedev_table[minor]) { - printk(KERN_ERR "mousedev: no more free mousedev devices\n"); - return NULL; - } - - if (!(mousedev = kmalloc(sizeof(struct mousedev), GFP_KERNEL))) - return NULL; - memset(mousedev, 0, sizeof(struct mousedev)); - init_waitqueue_head(&mousedev->wait); - - mousedev->exist = 1; - mousedev->minor = minor; - mousedev_table[minor] = mousedev; - - mousedev->handle.dev = dev; - mousedev->handle.handler = handler; - mousedev->handle.private = mousedev; - - mousedev->devfs = input_register_minor("mouse%d", minor, MOUSEDEV_MINOR_BASE); - - if (mousedev_mix.open) - input_open_device(&mousedev->handle); - - printk(KERN_INFO "mouse%d: PS/2 mouse device for input%d\n", minor, dev->number); - - return &mousedev->handle; -} - -static void mousedev_disconnect(struct input_handle *handle) -{ - struct mousedev *mousedev = handle->private; - - mousedev->exist = 0; - - if (mousedev->open) { - input_close_device(handle); - } else { - if (mousedev_mix.open) - input_close_device(handle); - input_unregister_minor(mousedev->devfs); - mousedev_table[mousedev->minor] = NULL; - kfree(mousedev); - } -} - -static struct input_handler mousedev_handler = { - event: mousedev_event, - connect: mousedev_connect, - disconnect: mousedev_disconnect, - fops: &mousedev_fops, - minor: MOUSEDEV_MINOR_BASE, -}; - -static int __init mousedev_init(void) -{ - input_register_handler(&mousedev_handler); - - memset(&mousedev_mix, 0, sizeof(struct mousedev)); - init_waitqueue_head(&mousedev_mix.wait); - mousedev_table[MOUSEDEV_MIX] = &mousedev_mix; - mousedev_mix.exist = 1; - mousedev_mix.minor = MOUSEDEV_MIX; - mousedev_mix.devfs = input_register_minor("mice", MOUSEDEV_MIX, MOUSEDEV_MINOR_BASE); - - printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n"); - - return 0; -} - -static void __exit mousedev_exit(void) -{ - input_unregister_minor(mousedev_mix.devfs); - input_unregister_handler(&mousedev_handler); -} - -module_init(mousedev_init); -module_exit(mousedev_exit); - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_DESCRIPTION("Input driver to PS/2 or ImPS/2 device driver"); diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c index 0a264faaf..96a28da5e 100644 --- a/drivers/usb/pegasus.c +++ b/drivers/usb/pegasus.c @@ -1,7 +1,7 @@ /* ** Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller ** -** Copyright (c) 1999,2000 Petko Manolov - Petkan (petkan@spct.net) +** Copyright (c) 1999,2000 Petko Manolov - Petkan (petkan@dce.bg) ** ** ** ChangeLog: @@ -42,16 +42,26 @@ #include <linux/usb.h> -static const char *version = __FILE__ ": v0.4.0 2000/06/15 (C) 1999-2000 Petko Manolov (petkan@spct.net)\n"; +static const char *version = __FILE__ ": v0.4.3 2000/08/22 (C) 1999-2000 Petko Manolov (petkan@dce.bg)\n"; + + +#define PEGASUS_USE_WAITQ #define PEGASUS_MTU 1500 #define PEGASUS_MAX_MTU 1536 -#define SROM_WRITE 0x01 -#define SROM_READ 0x02 -#define PEGASUS_TX_TIMEOUT (HZ*5) -#define PEGASUS_CTRL_TIMEOUT 1000 -#define PEGASUS_RESET 1 +#define EPROM_WRITE 0x01 +#define EPROM_READ 0x02 +#define PEGASUS_TX_TIMEOUT (HZ*10) +#define PEGASUS_CTRL_TIMEOUT (HZ*5) +#define PEGASUS_CTRL_WAIT (1<<31) +#define PEGASUS_RUNNING 1 +#define PEGASUS_REQT_READ 0xc0 +#define PEGASUS_REQT_WRITE 0x40 +#define PEGASUS_REQ_GET_REGS 0xf0 +#define PEGASUS_REQ_SET_REGS 0xf1 +#define PEGASUS_REQ_SET_REG PEGASUS_REQ_SET_REGS +#define NUM_CTRL_URBS 0x10 #define ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES))) @@ -75,16 +85,26 @@ enum pegasus_registers { }; +struct pegasus; +struct ctrl_urb_pool { + struct pegasus *pegasus; + struct urb urb; + devrequest dr; + __u8 busy; +}; + + struct pegasus { struct usb_device *usb; struct net_device *net; struct net_device_stats stats; int flags; - spinlock_t pegasus_lock, ctrl_lock; - struct urb rx_urb, tx_urb, intr_urb, ctrl_urb; - devrequest dr; - unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]); - unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]); + struct urb rx_urb, tx_urb, intr_urb; + struct ctrl_urb_pool ALIGN(ctrl[NUM_CTRL_URBS]); + wait_queue_head_t ctrl_wait; + struct semaphore ctrl_sem; + unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]); + unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]); unsigned char ALIGN(intr_buff[8]); }; @@ -100,10 +120,11 @@ static int loopback = 0; static int multicast_filter_limit = 32; -MODULE_AUTHOR("Petko Manolov <petkan@spct.net>"); +MODULE_AUTHOR("Petko Manolov <petkan@dce.bg>"); MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver"); MODULE_PARM(loopback, "i"); -MODULE_PARM_DESC(loopback, "Enable loopback mode (Bit 0) and ??? (Bit 1)"); +MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)"); + static struct usb_eth_dev usb_dev_id[] = { {"Billionton USB-100", 0x08dd, 0x0986, NULL}, @@ -112,8 +133,10 @@ static struct usb_eth_dev usb_dev_id[] = { {"D-Link DSB-650TX", 0x2001, 0x4001, NULL}, {"D-Link DSB-650TX", 0x2001, 0x4002, NULL}, {"D-Link DSB-650TX(PNA)", 0x2001, 0x4003, NULL}, + {"D-Link DSB-650", 0x2001, 0xabc1, NULL}, {"D-Link DU-E10", 0x07b8, 0xabc1, NULL}, {"D-Link DU-E100", 0x07b8, 0x4002, NULL}, + {"Linksys USB10TX", 0x066b, 0x2202, NULL}, {"Linksys USB100TX", 0x066b, 0x2203, NULL}, {"Linksys USB100TX", 0x066b, 0x2204, NULL}, {"Linksys USB Ethernet Adapter", 0x066b, 0x2206, NULL}, @@ -122,112 +145,149 @@ static struct usb_eth_dev usb_dev_id[] = { {"Accton USB 10/100 Ethernet Adapter", 0x083a, 0x1046, NULL}, {"IO DATA USB ET/TX", 0x04bb, 0x0904, NULL}, {"LANEED USB Ethernet LD-USB/TX", 0x056e, 0x4002, NULL}, + {"SOHOware NUB100 Ethernet", 0x15e8, 0x9100, NULL}, {NULL, 0, 0, NULL} }; - -static void pegasus_ctrl_end( urb_t *urb ) +static void pegasus_unlink_ctrl_urbs( struct pegasus *pegasus ) { - if ( urb->status ) - warn("ctrl_urb end status %d", urb->status); + int i; + + for ( i=0; i < NUM_CTRL_URBS; i++ ) { + if ( pegasus->ctrl[i].urb.status == -EINPROGRESS ) + usb_unlink_urb( &pegasus->ctrl[i].urb ); + } } -static int pegasus_ctrl_timeout( urb_t *ctrl_urb ) +static int pegasus_find_ctrl_urb( struct pegasus *pegasus ) { - int timeout=0; - - while ( ctrl_urb->status == -EINPROGRESS ) { - if ( timeout++ < PEGASUS_CTRL_TIMEOUT ) { - udelay(100); - continue; - } - err("ctrl urb busy %d", ctrl_urb->status); - usb_unlink_urb( ctrl_urb ); - return ctrl_urb->status; - } - return 0; + int i=0; + + while( i < NUM_CTRL_URBS && (pegasus->ctrl[i].busy == 1 || + (pegasus->ctrl[i].urb.status == -EINPROGRESS)) ) + i++; + + return i; } -static int pegasus_get_registers( struct pegasus *pegasus, __u16 indx, __u16 size, void *data ) +static void pegasus_ctrl_end( urb_t *urb ) { - int ret; + struct ctrl_urb_pool *ctrl = urb->context; + struct pegasus *pegasus = ctrl->pegasus; - spin_lock( &pegasus->ctrl_lock ); - pegasus->dr.requesttype = 0xc0; - pegasus->dr.request = 0xf0; - pegasus->dr.value = 0x0; - pegasus->dr.index = indx; - pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = size; + if ( !pegasus ) + return; - FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, - usb_rcvctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, - data, size, pegasus_ctrl_end, pegasus ); + if ( urb->status ) + warn("ctrl_urb end status %d", urb->status); + ctrl->busy = 0; +#ifdef PEGASUS_USE_WAITQ + wake_up_interruptible( &pegasus->ctrl_wait ); +#endif +} - if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) - err("BAD CTRLs %d", ret); - else - ret = pegasus_ctrl_timeout( &pegasus->ctrl_urb ); - spin_unlock( &pegasus->ctrl_lock ); +static int pegasus_get_registers( struct pegasus *pegasus, __u16 indx, __u16 size, void *data ) +{ + int ret, i; + struct ctrl_urb_pool *ctrl; + + if ( (i = pegasus_find_ctrl_urb( pegasus )) == NUM_CTRL_URBS ) { + return -1; + } + ctrl = &pegasus->ctrl[i]; + ctrl->busy = 1; + ctrl->pegasus = pegasus; + + ctrl->dr.requesttype = PEGASUS_REQT_READ; + ctrl->dr.request = PEGASUS_REQ_GET_REGS; + ctrl->dr.value = 0; + ctrl->dr.index = cpu_to_le16p(&indx); + ctrl->dr.length = + ctrl->urb.transfer_buffer_length = cpu_to_le16p(&size); + + FILL_CONTROL_URB( &ctrl->urb, pegasus->usb, + usb_rcvctrlpipe(pegasus->usb,0), + (char *)&ctrl->dr, + data, size, pegasus_ctrl_end, ctrl ); + + if ( (ret = usb_submit_urb( &ctrl->urb )) ) + err( __FUNCTION__ " BAD CTRLs %d", ret); +#ifdef PEGASUS_USE_WAITQ + interruptible_sleep_on( &pegasus->ctrl_wait ); +#endif return ret; } static int pegasus_set_registers( struct pegasus *pegasus, __u16 indx, __u16 size, void *data ) { - int ret; - - - spin_lock( &pegasus->ctrl_lock ); - pegasus->dr.requesttype = 0x40; - pegasus->dr.request = 0xf1; - pegasus->dr.value = 0x0; - pegasus->dr.index = indx; - pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = size; - - FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, - usb_sndctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, - data, size, pegasus_ctrl_end, pegasus ); - - if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) - err("BAD CTRL %d", ret); - else - ret = pegasus_ctrl_timeout( &pegasus->ctrl_urb ); - - spin_unlock( &pegasus->ctrl_lock ); + int ret, i; + struct ctrl_urb_pool *ctrl; + + if ( (i = pegasus_find_ctrl_urb( pegasus )) == NUM_CTRL_URBS ) { + return -1; + } + ctrl = &pegasus->ctrl[i]; + ctrl->busy = 1; + ctrl->pegasus = pegasus; + + ctrl->dr.requesttype = PEGASUS_REQT_WRITE; + ctrl->dr.request = PEGASUS_REQ_SET_REGS; + ctrl->dr.value = 0; + ctrl->dr.index = cpu_to_le16p( &indx ); + ctrl->dr.length = + ctrl->urb.transfer_buffer_length = cpu_to_le16p( &size ); + + FILL_CONTROL_URB( &ctrl->urb, pegasus->usb, + usb_sndctrlpipe(pegasus->usb,0), + (char *)&ctrl->dr, + data, size, pegasus_ctrl_end, ctrl ); + + if ( (ret = usb_submit_urb( &ctrl->urb )) ) + err( __FUNCTION__ " BAD CTRL %d", ret); +#ifdef PEGASUS_USE_WAITQ + interruptible_sleep_on( &pegasus->ctrl_wait ); +#endif return ret; } static int pegasus_set_register( struct pegasus *pegasus, __u16 indx,__u8 data ) { - int ret; - - - spin_lock( &pegasus->ctrl_lock ); - pegasus->dr.requesttype = 0x40; - pegasus->dr.request = 0xf1; - pegasus->dr.value = data; - pegasus->dr.index = indx; - pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = 1; - - FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, - usb_sndctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, - &data, 1, pegasus_ctrl_end, pegasus ); - - if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) - err("BAD CTRL %d", ret); - else - ret = pegasus_ctrl_timeout( &pegasus->ctrl_urb ); - - spin_unlock( &pegasus->ctrl_lock ); + int ret, i; + struct ctrl_urb_pool *ctrl; + + if ( (i = pegasus_find_ctrl_urb( pegasus )) == NUM_CTRL_URBS ) { + return -1; + } + ctrl = &pegasus->ctrl[i]; + ctrl->busy = 1; + ctrl->pegasus = pegasus; + + ctrl->dr.requesttype = PEGASUS_REQT_WRITE; + ctrl->dr.request = PEGASUS_REQ_SET_REG; + ctrl->dr.value = cpu_to_le16p( &data ); + ctrl->dr.index = cpu_to_le16p( &indx ); + ctrl->dr.length = ctrl->urb.transfer_buffer_length = 1; + + FILL_CONTROL_URB( &ctrl->urb, pegasus->usb, + usb_sndctrlpipe(pegasus->usb,0), + (char *)&ctrl->dr, + &data, 1, pegasus_ctrl_end, ctrl ); + + if ( (ret = usb_submit_urb( &ctrl->urb )) ) + err( __FUNCTION__ " BAD CTRL %d", ret); +#ifdef PEGASUS_USE_WAITQ + interruptible_sleep_on( &pegasus->ctrl_wait ); +#endif return ret; } @@ -244,10 +304,9 @@ static int pegasus_read_phy_word(struct pegasus *pegasus, __u8 index, __u16 *reg *regdata = *(__u16 *)(data); return 0; } - udelay(100); } - warn("read_phy_word() failed"); + return 1; } @@ -262,10 +321,9 @@ static int pegasus_write_phy_word(struct pegasus *pegasus, __u8 index, __u16 reg pegasus_get_registers(pegasus, PhyCtrl, 1, data); if (data[0] & 0x80) return 0; - udelay(100); } - warn("write_phy_word() failed"); + return 1; } @@ -284,8 +342,8 @@ static int pegasus_rw_eprom_word(struct pegasus *pegasus, __u8 index, __u16 *ret return 0; } } - warn("pegasus_rw_eprom_word() failed"); + return 1; } @@ -294,7 +352,7 @@ static int pegasus_get_node_id(struct pegasus *pegasus, __u8 *id) { int i; for (i = 0; i < 3; i++) - if (pegasus_rw_eprom_word(pegasus,i,(__u16 *)&id[i*2],SROM_READ)) + if (pegasus_rw_eprom_word(pegasus, i, (__u16 *)&id[i*2], EPROM_READ)) return 1; return 0; } @@ -311,8 +369,6 @@ static int pegasus_reset_mac(struct pegasus *pegasus) if (~data & 0x08) { if (loopback & 1) return 0; - if (loopback & 2) - pegasus_write_phy_word(pegasus, 0, 0x4000); pegasus_set_register(pegasus, Gpio0, 0x24); pegasus_set_register(pegasus, Gpio0, 0x27); return 0; @@ -348,7 +404,6 @@ static int pegasus_start_net(struct net_device *dev, struct usb_device *usb) if ((partmedia & 0x1f) != 1) { warn("party FAIL %x", partmedia); - /* return 5; FIXME */ } data[0] = 0xc9; @@ -361,15 +416,21 @@ static int pegasus_start_net(struct net_device *dev, struct usb_device *usb) } -static void pegasus_read_bulk(struct urb *urb) +static void pegasus_read_bulk_callback( struct urb *urb ) { struct pegasus *pegasus = urb->context; - struct net_device *net = pegasus->net; + struct net_device *net; /* = pegasus->net;*/ int count = urb->actual_length, res; - int rx_status = *(int *)(pegasus->rx_buff + count - 4); + int rx_status; /*= *(int *)(pegasus->rx_buff + count - 4);*/ struct sk_buff *skb; __u16 pkt_len; + if ( !pegasus || !(pegasus->flags & PEGASUS_RUNNING) ) + return; + + net = pegasus->net; + rx_status = *(int *)(pegasus->rx_buff + count - 4); + if (urb->status) { dbg("%s: RX status %d", net->name, urb->status); goto goon; @@ -408,31 +469,30 @@ static void pegasus_read_bulk(struct urb *urb) pegasus->stats.rx_bytes += pkt_len; goon: - if ((res = usb_submit_urb(&pegasus->rx_urb))) + if ( (res = usb_submit_urb(&pegasus->rx_urb)) ) warn("(prb)failed rx_urb %d", res); } -static void pegasus_irq(urb_t *urb) +static void pegasus_irq_callback( urb_t *urb ) { __u8 *d = urb->transfer_buffer; - + + if ( d[0] ) dbg("txst0=0x%2x", d[0]); } -static void pegasus_write_bulk(struct urb *urb) +static void pegasus_write_bulk_callback(struct urb *urb) { struct pegasus *pegasus = urb->context; + if ( !pegasus ) + return; if (urb->status) info("%s: TX status %d", pegasus->net->name, urb->status); -#if 1 /* Should be fixed */ - if (urb->status == -ETIMEDOUT) - pegasus_reset_mac(pegasus); -#endif netif_wake_queue(pegasus->net); } @@ -440,10 +500,11 @@ static void pegasus_tx_timeout(struct net_device *net) { struct pegasus *pegasus = net->priv; + if ( !pegasus ) + return; usb_unlink_urb(&pegasus->tx_urb); - warn("%s: Tx timed out. Reseting...", net->name); - pegasus_reset_mac( pegasus ); + warn("%s: Tx timed out.", net->name); pegasus->stats.tx_errors++; net->trans_start = jiffies; @@ -457,13 +518,14 @@ static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net) int count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3; int res; - spin_lock(&pegasus->pegasus_lock); - netif_stop_queue(net); + if ( !(pegasus->flags & PEGASUS_RUNNING) ) + return 0; ((__u16 *)pegasus->tx_buff)[0] = skb->len; memcpy(pegasus->tx_buff+2, skb->data, skb->len); - (&pegasus->tx_urb)->transfer_buffer_length = count; + pegasus->tx_urb.transfer_buffer_length = count; + pegasus->tx_urb.transfer_flags |= USB_ASYNC_UNLINK; if ((res = usb_submit_urb(&pegasus->tx_urb))) { warn("failed tx_urb %d", res); @@ -477,8 +539,6 @@ static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net) dev_kfree_skb(skb); - spin_unlock(&pegasus->pegasus_lock); - return 0; } @@ -489,6 +549,15 @@ static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev) } +static inline void pegasus_stop_net( struct pegasus *pegasus ) +{ + int tmp; + + pegasus_get_registers( pegasus, EthCtrl0, 1, &tmp ); + pegasus_set_register( pegasus, EthCtrl0, tmp & 0x3f ); +} + + static int pegasus_open(struct net_device *net) { struct pegasus *pegasus = (struct pegasus *)net->priv; @@ -506,8 +575,7 @@ static int pegasus_open(struct net_device *net) warn("(open)failed intr_urb %d", res); netif_start_queue(net); - - MOD_INC_USE_COUNT; + pegasus->flags |= PEGASUS_RUNNING; return 0; } @@ -517,18 +585,15 @@ static int pegasus_close(struct net_device *net) { struct pegasus *pegasus = net->priv; + pegasus->flags &= ~PEGASUS_RUNNING; + pegasus_stop_net( pegasus ); + netif_stop_queue(net); - if ( pegasus->ctrl_urb.status == -EINPROGRESS ) - usb_unlink_urb(&pegasus->ctrl_urb); - if ( pegasus->rx_urb.status == -EINPROGRESS ) - usb_unlink_urb(&pegasus->rx_urb); - if ( pegasus->tx_urb.status == -EINPROGRESS ) - usb_unlink_urb(&pegasus->tx_urb); - if ( pegasus->intr_urb.status == -EINPROGRESS ) - usb_unlink_urb(&pegasus->intr_urb); - - MOD_DEC_USE_COUNT; + usb_unlink_urb(&pegasus->rx_urb); + usb_unlink_urb(&pegasus->tx_urb); + usb_unlink_urb(&pegasus->intr_urb); + pegasus_unlink_ctrl_urbs( pegasus ); return 0; } @@ -558,24 +623,32 @@ static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) static void pegasus_set_rx_mode(struct net_device *net) { +#ifndef PEGASUS_USE_WAITQ struct pegasus *pegasus = net->priv; __u8 tmp; +#endif netif_stop_queue(net); if (net->flags & IFF_PROMISC) { - info("%s: Promiscuous mode enabled", net->name); +#ifndef PEGASUS_USE_WAITQ pegasus_get_registers(pegasus, EthCtrl2, 1, &tmp); - pegasus_set_register(pegasus, EthCtrl2, tmp | 4); + pegasus_set_register(pegasus, EthCtrl2, tmp | 4); +#endif + info("%s: Promiscuous mode enabled", net->name); } else if ((net->mc_count > multicast_filter_limit) || (net->flags & IFF_ALLMULTI)) { +#ifndef PEGASUS_USE_WAITQ pegasus_set_register(pegasus, EthCtrl0, 0xfa); pegasus_set_register(pegasus, EthCtrl2, 0); +#endif info("%s set allmulti", net->name); } else { - info("%s: set Rx mode", net->name); +#ifndef PEGASUS_USE_WAITQ pegasus_get_registers(pegasus, EthCtrl2, 1, &tmp); - pegasus_set_register(pegasus, EthCtrl2, tmp & ~4); + pegasus_set_register(pegasus, EthCtrl2, tmp & ~4); +#endif + info("%s: set Rx mode", net->name); } netif_wake_queue(net); @@ -629,27 +702,32 @@ static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) net->get_stats = pegasus_netdev_stats; net->mtu = PEGASUS_MTU; + init_MUTEX( &pegasus-> ctrl_sem ); + init_waitqueue_head( &pegasus->ctrl_wait ); + pegasus->usb = dev; pegasus->net = net; - pegasus->pegasus_lock = SPIN_LOCK_UNLOCKED; - pegasus->ctrl_lock = SPIN_LOCK_UNLOCKED; - - FILL_BULK_URB(&pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1), - pegasus->rx_buff, PEGASUS_MAX_MTU, pegasus_read_bulk, - pegasus); - FILL_BULK_URB(&pegasus->tx_urb, dev, usb_sndbulkpipe(dev, 2), - pegasus->tx_buff, PEGASUS_MAX_MTU, pegasus_write_bulk, - pegasus); - FILL_INT_URB(&pegasus->intr_urb, dev, usb_rcvintpipe(dev, 3), - pegasus->intr_buff, 8, pegasus_irq, pegasus, 500); + + FILL_BULK_URB( &pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1), + pegasus->rx_buff, PEGASUS_MAX_MTU, + pegasus_read_bulk_callback, pegasus ); + FILL_BULK_URB( &pegasus->tx_urb, dev, usb_sndbulkpipe(dev, 2), + pegasus->tx_buff, PEGASUS_MAX_MTU, + pegasus_write_bulk_callback, pegasus ); + FILL_INT_URB( &pegasus->intr_urb, dev, usb_rcvintpipe(dev, 3), + pegasus->intr_buff, 8, pegasus_irq_callback, + pegasus, 128 ); if (pegasus_reset_mac(pegasus)) { err("can't reset MAC"); kfree(pegasus); + pegasus = NULL; return NULL; } - printk(KERN_INFO "%s: %s\n", net->name, usb_dev_id[dev_indx].name); + info( "%s: %s\n", net->name, usb_dev_id[dev_indx].name ); + + MOD_INC_USE_COUNT; return pegasus; } @@ -664,21 +742,18 @@ static void pegasus_disconnect(struct usb_device *dev, void *ptr) return; } - if (pegasus->net->flags & IFF_UP) - dev_close(pegasus->net); - + pegasus->flags &= ~PEGASUS_RUNNING; unregister_netdev(pegasus->net); - - if ( pegasus->ctrl_urb.status == -EINPROGRESS ) - usb_unlink_urb(&pegasus->ctrl_urb); - if ( pegasus->rx_urb.status == -EINPROGRESS ) - usb_unlink_urb(&pegasus->rx_urb); - if ( pegasus->tx_urb.status == -EINPROGRESS ) - usb_unlink_urb(&pegasus->tx_urb); - if ( pegasus->intr_urb.status == -EINPROGRESS ) - usb_unlink_urb(&pegasus->intr_urb); + + usb_unlink_urb(&pegasus->rx_urb); + usb_unlink_urb(&pegasus->tx_urb); + usb_unlink_urb(&pegasus->intr_urb); + pegasus_unlink_ctrl_urbs( pegasus ); kfree(pegasus); + pegasus = NULL; + + MOD_DEC_USE_COUNT; } @@ -690,7 +765,7 @@ static struct usb_driver pegasus_driver = { int __init pegasus_init(void) { - printk( version ); + info( "%s", version ); return usb_register(&pegasus_driver); } diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c index 96fa971d8..bbbc05f33 100644 --- a/drivers/usb/printer.c +++ b/drivers/usb/printer.c @@ -1,10 +1,10 @@ /* - * printer.c Version 0.5 + * printer.c Version 0.6 * * Copyright (c) 1999 Michael Gee <michael@linuxspecific.com> * Copyright (c) 1999 Pavel Machek <pavel@suse.cz> - * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz> * Copyright (c) 2000 Randy Dunlap <randy.dunlap@intel.com> + * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz> * * USB Printer Device Class driver for USB printers and printer cables * @@ -16,6 +16,7 @@ * v0.3 - cleaner again, waitqueue fixes * v0.4 - fixes in unidirectional mode * v0.5 - add DEVICE_ID string support + * v0.6 - never time out */ /* @@ -71,7 +72,7 @@ MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:H #define USBLP_MINORS 16 #define USBLP_MINOR_BASE 0 -#define USBLP_WRITE_TIMEOUT (60*60*HZ) /* 60 minutes */ +#define USBLP_WRITE_TIMEOUT (5*HZ) /* 5 seconds */ struct usblp { struct usb_device *dev; /* USB device */ @@ -83,10 +84,10 @@ struct usblp { unsigned char used; /* True if open */ unsigned char bidir; /* interface is bidirectional */ unsigned char *device_id_string; /* IEEE 1284 DEVICE ID string (ptr) */ - /* first 2 bytes are (big-endian) length */ + /* first 2 bytes are (big-endian) length */ }; -static struct usblp *usblp_table[USBLP_MINORS] = { NULL, /* ... */ }; +static struct usblp *usblp_table[USBLP_MINORS]; /* * Functions for usblp control messages. @@ -121,7 +122,8 @@ static void usblp_bulk(struct urb *urb) return; if (urb->status) - warn("nonzero read/write bulk status received: %d", urb->status); + warn("usblp%d: nonzero read/write bulk status received: %d", + usblp->minor, urb->status); wake_up_interruptible(&usblp->wait); } @@ -130,29 +132,27 @@ static void usblp_bulk(struct urb *urb) * Get and print printer errors. */ -static int usblp_check_status(struct usblp *usblp) +static char *usblp_messages[] = { "ok", "out of paper", "off-line", "on fire" }; + +static int usblp_check_status(struct usblp *usblp, int err) { - unsigned char status; + unsigned char status, newerr = 0; if (usblp_read_status(usblp, &status)) { - err("failed reading usblp status"); - return -EIO; + err("usblp%d: failed reading printer status", usblp->minor); + return 0; } if (~status & LP_PERRORP) { - if (status & LP_POUTPA) { - info("usblp%d: out of paper", usblp->minor); - return -ENOSPC; - } - if (~status & LP_PSELECD) { - info("usblp%d: off-line", usblp->minor); - return -EIO; - } - info("usblp%d: on fire", usblp->minor); - return -EIO; + newerr = 3; + if (status & LP_POUTPA) newerr = 1; + if (~status & LP_PSELECD) newerr = 2; } - return 0; + if (newerr != err) + info("usblp%d: %s", usblp->minor, usblp_messages[newerr]); + + return newerr; } /* @@ -179,8 +179,10 @@ static int usblp_open(struct inode *inode, struct file *file) if (usblp->used) goto out; - if ((retval = usblp_check_status(usblp))) + if ((retval = usblp_check_status(usblp, 0))) { + retval = retval > 1 ? -EIO : -ENOSPC; goto out; + } usblp->used = 1; file->private_data = usblp; @@ -228,27 +230,30 @@ static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int length; struct usblp *usblp = file->private_data; + int length; if ((_IOC_TYPE(cmd) != 'P') || (_IOC_DIR(cmd) != _IOC_READ)) return -EINVAL; switch (_IOC_NR(cmd)) { - case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */ - length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */ -#if 0 - dbg ("usblp_ioctl GET_DEVICE_ID: actlen=%d, user size=%d, string='%s'", - length, _IOC_SIZE(cmd), &usblp->device_id_string[2]); -#endif - if (length > _IOC_SIZE(cmd)) - length = _IOC_SIZE(cmd); /* truncate */ - if (copy_to_user ((unsigned char *)arg, usblp->device_id_string, (unsigned long) length)) - return -EFAULT; - break; - default: - return -EINVAL; + case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */ + + length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */ + + dbg ("usblp_ioctl GET_DEVICE_ID actlen: %d, size: %d, string: '%s'", + length, _IOC_SIZE(cmd), &usblp->device_id_string[2]); + + if (length > _IOC_SIZE(cmd)) length = _IOC_SIZE(cmd); /* truncate */ + + if (copy_to_user((unsigned char *) arg, usblp->device_id_string, (unsigned long) length)) + return -EFAULT; + + break; + + default: + return -EINVAL; } return 0; @@ -257,7 +262,7 @@ static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, static ssize_t usblp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct usblp *usblp = file->private_data; - int retval, timeout, writecount = 0; + int timeout, err = 0, writecount = 0; while (writecount < count) { @@ -276,28 +281,17 @@ static ssize_t usblp_write(struct file *file, const char *buffer, size_t count, } } - if (usblp->writeurb.status == -EINPROGRESS) { - usb_unlink_urb(&usblp->writeurb); - err("usblp%d: timed out", usblp->minor); - return -EIO; - } - if (!usblp->dev) return -ENODEV; - if (!usblp->writeurb.status) { - writecount += usblp->writeurb.transfer_buffer_length; - usblp->writeurb.transfer_buffer_length = 0; - } else { - if (!(retval = usblp_check_status(usblp))) { - err("usblp%d: error %d writing to printer (retval=%d)", - usblp->minor, usblp->writeurb.status, retval); - return -EIO; - } - - return retval; + if (usblp->writeurb.status) { + err = usblp_check_status(usblp, err); + continue; } + writecount += usblp->writeurb.transfer_buffer_length; + usblp->writeurb.transfer_buffer_length = 0; + if (writecount == count) continue; @@ -368,14 +362,14 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum) interface = &dev->actconfig->interface[ifnum].altsetting[i]; if (interface->bInterfaceClass != 7 || interface->bInterfaceSubClass != 1 || - (interface->bInterfaceProtocol != 1 && interface->bInterfaceProtocol != 2) || - (interface->bInterfaceProtocol > interface->bNumEndpoints)) + interface->bInterfaceProtocol < 1 || interface->bInterfaceProtocol > 3 || + (interface->bInterfaceProtocol > 1 && interface->bNumEndpoints < 2)) continue; if (alts == -1) alts = i; - if (!bidir && interface->bInterfaceProtocol == 2) { + if (!bidir && interface->bInterfaceProtocol > 1) { bidir = 1; alts = i; } diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index c5c403b2b..f168b1bfc 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -14,6 +14,58 @@ * Peter Berger (pberger@brimson.com) * Al Borchers (borchers@steinerpoint.com) * +* (8/8/2000) pberger and borchers +* -- Fixed close so that +* - it can timeout while waiting for transmit idle, if needed; +* - it ignores interrupts when flushing the port, turning +* of modem signalling, and so on; +* - it waits for the flush to really complete before returning. +* -- Read_bulk_callback and write_bulk_callback check for a closed +* port before using the tty struct or writing to the port. +* -- The two changes above fix the oops caused by interrupted closes. +* -- Added interruptible args to write_oob_command and set_modem_signals +* and added a timeout arg to transmit_idle; needed for fixes to +* close. +* -- Added code for rx_throttle and rx_unthrottle so that input flow +* control works. +* -- Added code to set overrun, parity, framing, and break errors +* (untested). +* -- Set USB_DISABLE_SPD flag for write bulk urbs, so no 0 length +* bulk writes are done. These hung the Digi USB device. The +* 0 length bulk writes were a new feature of usb-uhci added in +* the 2.4.0-test6 kernels. +* -- Fixed mod inc race in open; do mod inc before sleeping to wait +* for a close to finish. +* +* (7/31/2000) pberger +* -- Fixed bugs with hardware handshaking: +* - Added code to set/clear tty->hw_stopped in digi_read_oob_callback() +* and digi_set_termios() +* -- Added code in digi_set_termios() to +* - add conditional in code handling transition from B0 to only +* set RTS if RTS/CTS flow control is either not in use or if +* the port is not currently throttled. +* - handle turning off CRTSCTS. +* +* (7/30/2000) borchers +* -- Added support for more than one Digi USB device by moving +* globals to a private structure in the pointed to from the +* usb_serial structure. +* -- Moved the modem change and transmit idle wait queues into +* the port private structure, so each port has its own queue +* rather than sharing global queues. +* -- Added support for break signals. +* +* (7/25/2000) pberger +* -- Added USB-2 support. Note: the USB-2 supports 3 devices: two +* serial and a parallel port. The parallel port is implemented +* as a serial-to-parallel converter. That is, the driver actually +* presents all three USB-2 interfaces as serial ports, but the third +* one physically connects to a parallel device. Thus, for example, +* one could plug a parallel printer into the USB-2's third port, +* but from the kernel's (and userland's) point of view what's +* actually out there is a serial device. +* * (7/15/2000) borchers * -- Fixed race in open when a close is in progress. * -- Keep count of opens and dec the module use count for each @@ -24,8 +76,8 @@ * callbacks, and no longer restart read chains if there is * a status error or a sanity error. This fixed the seg * faults and other errors we used to get on disconnect. -* -- Port->active is once again a flag, not a count, as it was -* intended by usb-serial. Since it was only a char it would +* -- Port->active is once again a flag as usb-serial intended it +* to be, not a count. Since it was only a char it would * have been limited to 256 simultaneous opens. Now the open * count is kept in the port private structure in dp_open_count. * -- Added code for modularization of the digi_acceleport driver. @@ -51,7 +103,7 @@ * * (6/4/2000) pberger and borchers * -- Replaced separate calls to spin_unlock_irqrestore and -* interruptible_sleep_on_interruptible with a new function +* interruptible_sleep_on_timeout with a new function * cond_wait_interruptible_timeout_irqrestore. This eliminates * the race condition where the wake up could happen after * the unlock and before the sleep. @@ -157,7 +209,7 @@ * - Following Documentation/DocBook/kernel-locking.pdf no spin locks * are held when calling copy_to/from_user or printk. * -* $Id: digi_acceleport.c,v 1.5 2000/07/18 04:52:43 root Exp $ +* $Id: digi_acceleport.c,v 1.80 2000/08/09 06:36:18 root Exp $ */ #include <linux/config.h> @@ -188,9 +240,13 @@ /* Defines */ -/* port buffer length -- must be <= transfer buffer length - 2 */ +/* port output buffer length -- must be <= transfer buffer length - 2 */ /* so we can be sure to send the full buffer in one urb */ -#define DIGI_PORT_BUF_LEN 8 +#define DIGI_OUT_BUF_SIZE 8 + +/* port input buffer length -- must be >= transfer buffer length - 3 */ +/* so we can be sure to hold at least one full buffer from one urb */ +#define DIGI_IN_BUF_SIZE 64 /* retry timeout while sleeping */ #define DIGI_RETRY_TIMEOUT (HZ/10) @@ -205,7 +261,8 @@ /* ids */ #define DIGI_VENDOR_ID 0x05c5 -#define DIGI_ID 0x0004 +#define DIGI_2_ID 0x0002 /* USB-2 */ +#define DIGI_4_ID 0x0004 /* USB-4 */ /* commands * "INB": can be used on the in-band endpoint @@ -335,29 +392,45 @@ /* Structures */ -typedef struct digi_private { - int dp_port_num; +typedef struct digi_serial { + spinlock_t ds_serial_lock; + struct usb_serial_port *ds_oob_port; /* out-of-band port */ + int ds_oob_port_num; /* index of out-of-band port */ + int ds_device_started; +} digi_serial_t; + +typedef struct digi_port { spinlock_t dp_port_lock; - int dp_buf_len; - unsigned char dp_buf[DIGI_PORT_BUF_LEN]; + int dp_port_num; + int dp_out_buf_len; + unsigned char dp_out_buf[DIGI_OUT_BUF_SIZE]; + int dp_in_buf_len; + unsigned char dp_in_buf[DIGI_IN_BUF_SIZE]; + unsigned char dp_in_flag_buf[DIGI_IN_BUF_SIZE]; unsigned int dp_modem_signals; + wait_queue_head_t dp_modem_change_wait; int dp_open_count; /* inc on open, dec on close */ int dp_transmit_idle; + wait_queue_head_t dp_transmit_idle_wait; + int dp_throttled; + int dp_throttle_restart; + wait_queue_head_t dp_flush_wait; int dp_in_close; /* close in progress */ wait_queue_head_t dp_close_wait; /* wait queue for close */ struct tq_struct dp_wakeup_task; -} digi_private_t; +} digi_port_t; /* Local Function Declarations */ static void digi_wakeup_write( struct usb_serial_port *port ); static void digi_wakeup_write_lock( struct usb_serial_port *port ); -static int digi_write_oob_command( unsigned char *buf, int count ); +static int digi_write_oob_command( struct usb_serial_port *port, + unsigned char *buf, int count, int interruptible ); static int digi_write_inb_command( struct usb_serial_port *port, - unsigned char *buf, int count ) __attribute__((unused)); + unsigned char *buf, int count, unsigned long timeout ); static int digi_set_modem_signals( struct usb_serial_port *port, - unsigned int modem_signals ); + unsigned int modem_signals, int interruptible ); static int digi_transmit_idle( struct usb_serial_port *port, unsigned long timeout ); static void digi_rx_throttle (struct usb_serial_port *port); @@ -386,25 +459,40 @@ static int digi_read_oob_callback( struct urb *urb ); /* device info needed for the Digi serial converter */ static u16 digi_vendor_id = DIGI_VENDOR_ID; -static u16 digi_product_id = DIGI_ID; - -/* out of band port */ -static int oob_port_num; /* index of out-of-band port */ -static struct usb_serial_port *oob_port; /* out-of-band port */ -static int device_startup = 0; - -spinlock_t startup_lock; /* used by startup_device */ - -static wait_queue_head_t modem_change_wait; -static wait_queue_head_t transmit_idle_wait; +static u16 digi_product_2_id = DIGI_2_ID; /* USB 2 */ +static u16 digi_product_4_id = DIGI_4_ID; /* USB 4 */ +static struct usb_serial_device_type digi_acceleport_2_device = { + name: "Digi USB", + idVendor: &digi_vendor_id, + idProduct: &digi_product_2_id, + needs_interrupt_in: DONT_CARE, + needs_bulk_in: MUST_HAVE, + needs_bulk_out: MUST_HAVE, + num_interrupt_in: 0, + num_bulk_in: 4, + num_bulk_out: 4, + num_ports: 3, + open: digi_open, + close: digi_close, + write: digi_write, + write_room: digi_write_room, + write_bulk_callback: digi_write_bulk_callback, + read_bulk_callback: digi_read_bulk_callback, + chars_in_buffer: digi_chars_in_buffer, + throttle: digi_rx_throttle, + unthrottle: digi_rx_unthrottle, + ioctl: digi_ioctl, + set_termios: digi_set_termios, + break_ctl: digi_break_ctl, + startup: digi_startup, + shutdown: digi_shutdown, +}; -/* Globals */ - -struct usb_serial_device_type digi_acceleport_device = { +static struct usb_serial_device_type digi_acceleport_4_device = { name: "Digi USB", idVendor: &digi_vendor_id, - idProduct: &digi_product_id, + idProduct: &digi_product_4_id, needs_interrupt_in: DONT_CARE, needs_bulk_in: MUST_HAVE, needs_bulk_out: MUST_HAVE, @@ -479,7 +567,7 @@ static void digi_wakeup_write_lock( struct usb_serial_port *port ) { unsigned long flags; - digi_private_t *priv = (digi_private_t *)(port->private); + digi_port_t *priv = (digi_port_t *)(port->private); spin_lock_irqsave( &priv->dp_port_lock, flags ); @@ -515,20 +603,23 @@ static void digi_wakeup_write( struct usb_serial_port *port ) * Write commands on the out of band port. Commands are 4 * bytes each, multiple commands can be sent at once, and * no command will be split across USB packets. Returns 0 -* if successful, -EINTR if interrupted while sleeping, or -* a negative error returned by usb_submit_urb. +* if successful, -EINTR if interrupted while sleeping and +* the interruptible flag is true, or a negative error +* returned by usb_submit_urb. */ -static int digi_write_oob_command( unsigned char *buf, int count ) +static int digi_write_oob_command( struct usb_serial_port *port, + unsigned char *buf, int count, int interruptible ) { int ret = 0; int len; - digi_private_t *oob_priv = (digi_private_t *)(oob_port->private); + struct usb_serial_port *oob_port = (struct usb_serial_port *)((digi_serial_t *)port->serial->private)->ds_oob_port; + digi_port_t *oob_priv = (digi_port_t *)oob_port->private; unsigned long flags = 0; -dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_port_num, count ); +dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_priv->dp_port_num, count ); spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); @@ -538,7 +629,7 @@ dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_port_num, count ); cond_wait_interruptible_timeout_irqrestore( &oob_port->write_wait, DIGI_RETRY_TIMEOUT, &oob_priv->dp_port_lock, flags ); - if( signal_pending(current) ) { + if( interruptible && signal_pending(current) ) { return( -EINTR ); } spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); @@ -562,8 +653,8 @@ dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_port_num, count ); spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags ); if( ret ) { - dbg( "digi_write_oob_command: usb_submit_urb failed, ret=%d", - ret ); + err( __FUNCTION__ ": usb_submit_urb failed, ret=%d", + ret ); } return( ret ); @@ -576,17 +667,20 @@ dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_port_num, count ); * * Write commands on the given port. Commands are 4 * bytes each, multiple commands can be sent at once, and -* no command will be split across USB packets. Returns 0 -* if successful, or a negative error returned by digi_write. +* no command will be split across USB packets. If timeout +* is non-zero, write in band command will return after +* waiting unsuccessfully for the URB status to clear for +* timeout ticks. Returns 0 if successful, or a negative +* error returned by digi_write. */ static int digi_write_inb_command( struct usb_serial_port *port, - unsigned char *buf, int count ) + unsigned char *buf, int count, unsigned long timeout ) { int ret = 0; int len; - digi_private_t *priv = (digi_private_t *)(port->private); + digi_port_t *priv = (digi_port_t *)(port->private); unsigned char *data = port->write_urb->transfer_buffer; unsigned long flags = 0; @@ -594,11 +688,17 @@ static int digi_write_inb_command( struct usb_serial_port *port, dbg( "digi_write_inb_command: TOP: port=%d, count=%d", priv->dp_port_num, count ); + if( timeout ) + timeout += jiffies; + else + timeout = ULONG_MAX; + spin_lock_irqsave( &priv->dp_port_lock, flags ); - while( count > 0 ) { + while( count > 0 && ret == 0 ) { - while( port->write_urb->status == -EINPROGRESS ) { + while( port->write_urb->status == -EINPROGRESS + && jiffies < timeout ) { cond_wait_interruptible_timeout_irqrestore( &port->write_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags ); @@ -611,25 +711,26 @@ count ); /* len must be a multiple of 4 and small enough to */ /* guarantee the write will send buffered data first, */ /* so commands are in order with data and not split */ - len = MIN( count, port->bulk_out_size-2-priv->dp_buf_len ); + len = MIN( count, port->bulk_out_size-2-priv->dp_out_buf_len ); if( len > 4 ) len &= ~3; /* write any buffered data first */ - if( priv->dp_buf_len > 0 ) { + if( priv->dp_out_buf_len > 0 ) { data[0] = DIGI_CMD_SEND_DATA; - data[1] = priv->dp_buf_len; - memcpy( data+2, priv->dp_buf, priv->dp_buf_len ); - memcpy( data+2+priv->dp_buf_len, buf, len ); + data[1] = priv->dp_out_buf_len; + memcpy( data+2, priv->dp_out_buf, + priv->dp_out_buf_len ); + memcpy( data+2+priv->dp_out_buf_len, buf, len ); port->write_urb->transfer_buffer_length - = priv->dp_buf_len+2+len; + = priv->dp_out_buf_len+2+len; } else { memcpy( data, buf, len ); port->write_urb->transfer_buffer_length = len; } if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { - priv->dp_buf_len = 0; + priv->dp_out_buf_len = 0; count -= len; buf += len; } @@ -639,8 +740,8 @@ count ); spin_unlock_irqrestore( &priv->dp_port_lock, flags ); if( ret ) { - dbg( "digi_write_inb_command: usb_submit_urb failed, ret=%d", - ret ); + err( __FUNCTION__ ": usb_submit_urb failed, ret=%d, port=%d", + ret, priv->dp_port_num ); } return( ret ); @@ -659,13 +760,14 @@ count ); */ static int digi_set_modem_signals( struct usb_serial_port *port, - unsigned int modem_signals ) + unsigned int modem_signals, int interruptible ) { int ret; + digi_port_t *port_priv = (digi_port_t *)port->private; + struct usb_serial_port *oob_port = (struct usb_serial_port *)((digi_serial_t *)port->serial->private)->ds_oob_port; + digi_port_t *oob_priv = (digi_port_t *)oob_port->private; unsigned char *data = oob_port->write_urb->transfer_buffer; - digi_private_t *port_priv = (digi_private_t *)(port->private); - digi_private_t *oob_priv = (digi_private_t *)(oob_port->private); unsigned long flags = 0; @@ -680,7 +782,7 @@ port_priv->dp_port_num, modem_signals ); cond_wait_interruptible_timeout_irqrestore( &oob_port->write_wait, DIGI_RETRY_TIMEOUT, &oob_priv->dp_port_lock, flags ); - if( signal_pending(current) ) { + if( interruptible && signal_pending(current) ) { return( -EINTR ); } spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); @@ -711,7 +813,7 @@ port_priv->dp_port_num, modem_signals ); spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags ); if( ret ) { - dbg( "digi_set_modem_signals: usb_submit_urb failed, ret=%d", + err( __FUNCTION__ ": usb_submit_urb failed, ret=%d", ret ); } @@ -738,7 +840,7 @@ static int digi_transmit_idle( struct usb_serial_port *port, int ret; unsigned char buf[2]; - digi_private_t *priv = (digi_private_t *)(port->private); + digi_port_t *priv = (digi_port_t *)(port->private); unsigned long flags = 0; @@ -749,16 +851,16 @@ static int digi_transmit_idle( struct usb_serial_port *port, buf[0] = DIGI_CMD_TRANSMIT_IDLE; buf[1] = 0; - if( (ret=digi_write_inb_command( port, buf, 2 )) != 0 ) - return( ret ); - timeout += jiffies; + if( (ret=digi_write_inb_command( port, buf, 2, timeout-jiffies )) != 0 ) + return( ret ); + spin_lock_irqsave( &priv->dp_port_lock, flags ); while( jiffies < timeout && !priv->dp_transmit_idle ) { cond_wait_interruptible_timeout_irqrestore( - &transmit_idle_wait, DIGI_RETRY_TIMEOUT, + &priv->dp_transmit_idle_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags ); if( signal_pending(current) ) { return( -EINTR ); @@ -777,21 +879,18 @@ static int digi_transmit_idle( struct usb_serial_port *port, static void digi_rx_throttle( struct usb_serial_port *port ) { -#ifdef DEBUG - digi_private_t *priv = (digi_private_t *)(port->private); -#endif + unsigned long flags; + digi_port_t *priv = (digi_port_t *)(port->private); dbg( "digi_rx_throttle: TOP: port=%d", priv->dp_port_num ); - /* stop receiving characters. We just turn off the URB request, and - let chars pile up in the device. If we're doing hardware - flowcontrol, the device will signal the other end when its buffer - fills up. If we're doing XON/XOFF, this would be a good time to - send an XOFF, although it might make sense to foist that off - upon the device too. */ - - // usb_unlink_urb(port->interrupt_in_urb); + /* stop receiving characters by not resubmitting the read urb */ + spin_lock_irqsave( &priv->dp_port_lock, flags ); + priv->dp_throttled = 1; + priv->dp_throttle_restart = 0; + priv->dp_in_buf_len = 0; + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); } @@ -799,16 +898,43 @@ dbg( "digi_rx_throttle: TOP: port=%d", priv->dp_port_num ); static void digi_rx_unthrottle( struct usb_serial_port *port ) { -#ifdef DEBUG - digi_private_t *priv = (digi_private_t *)(port->private); -#endif + int ret = 0; + int len; + unsigned long flags; + digi_port_t *priv = (digi_port_t *)(port->private); + struct tty_struct *tty = port->tty; dbg( "digi_rx_unthrottle: TOP: port=%d", priv->dp_port_num ); - /* just restart the receive interrupt URB */ - //if (usb_submit_urb(port->interrupt_in_urb)) - // dbg( "digi_rx_unthrottle: usb_submit_urb failed" ); + spin_lock_irqsave( &priv->dp_port_lock, flags ); + + /* send any buffered chars from throttle time on to tty subsystem */ + len = MIN( priv->dp_in_buf_len, TTY_FLIPBUF_SIZE - tty->flip.count ); + if( len > 0 ) { + memcpy( tty->flip.char_buf_ptr, priv->dp_in_buf, len ); + memcpy( tty->flip.flag_buf_ptr, priv->dp_in_flag_buf, len ); + tty->flip.char_buf_ptr += len; + tty->flip.flag_buf_ptr += len; + tty->flip.count += len; + tty_flip_buffer_push( tty ); + } + + /* restart read chain */ + if( priv->dp_throttle_restart ) + ret = usb_submit_urb( port->read_urb ); + + /* turn throttle off */ + priv->dp_throttled = 0; + priv->dp_in_buf_len = 0; + priv->dp_throttle_restart = 0; + + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + + if( ret ) { + err( __FUNCTION__ ": usb_submit_urb failed, ret=%d, port=%d", + ret, priv->dp_port_num ); + } } @@ -817,12 +943,13 @@ static void digi_set_termios( struct usb_serial_port *port, struct termios *old_termios ) { - digi_private_t *priv = (digi_private_t *)(port->private); + digi_port_t *priv = (digi_port_t *)(port->private); unsigned int iflag = port->tty->termios->c_iflag; unsigned int cflag = port->tty->termios->c_cflag; unsigned int old_iflag = old_termios->c_iflag; unsigned int old_cflag = old_termios->c_cflag; unsigned char buf[32]; + unsigned int modem_signals; int arg,ret; int i = 0; @@ -837,13 +964,18 @@ dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, ol /* reassert DTR and (maybe) RTS on transition from B0 */ if( (old_cflag&CBAUD) == B0 ) { /* don't set RTS if using hardware flow control */ - /* and throttling input -- not implemented yet */ - digi_set_modem_signals( port, TIOCM_DTR|TIOCM_RTS ); + /* and throttling input */ + modem_signals = TIOCM_DTR; + if( !(port->tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &port->tty->flags) ) { + modem_signals |= TIOCM_RTS; + } + digi_set_modem_signals( port, modem_signals, 1 ); } switch( (cflag&CBAUD) ) { /* drop DTR and RTS on transition to B0 */ - case B0: digi_set_modem_signals( port, 0 ); break; + case B0: digi_set_modem_signals( port, 0, 1 ); break; case B50: arg = DIGI_BAUD_50; break; case B75: arg = DIGI_BAUD_75; break; case B110: arg = DIGI_BAUD_110; break; @@ -947,10 +1079,20 @@ dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, ol else arg &= ~DIGI_INPUT_FLOW_CONTROL_XON_XOFF; - if( (cflag&CRTSCTS) ) + if( (cflag&CRTSCTS) ) { + arg |= DIGI_INPUT_FLOW_CONTROL_RTS; - else + + /* On USB-4 it is necessary to assert RTS prior */ + /* to selecting RTS input flow control. */ + buf[i++] = DIGI_CMD_SET_RTS_SIGNAL; + buf[i++] = priv->dp_port_num; + buf[i++] = DIGI_RTS_ACTIVE; + buf[i++] = 0; + + } else { arg &= ~DIGI_INPUT_FLOW_CONTROL_RTS; + } buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL; buf[i++] = priv->dp_port_num; @@ -960,8 +1102,8 @@ dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, ol } /* set output flow control */ - /*if( (iflag&IXON) != (old_iflag&IXON) - || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) )*/ { + if( (iflag&IXON) != (old_iflag&IXON) + || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) { arg = 0; @@ -970,10 +1112,12 @@ dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, ol else arg &= ~DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF; - if( (cflag&CRTSCTS) ) + if( (cflag&CRTSCTS) ) { arg |= DIGI_OUTPUT_FLOW_CONTROL_CTS; - else + } else { arg &= ~DIGI_OUTPUT_FLOW_CONTROL_CTS; + port->tty->hw_stopped = 0; + } buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; buf[i++] = priv->dp_port_num; @@ -997,7 +1141,7 @@ dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, ol } - if( (ret=digi_write_oob_command( buf, i )) != 0 ) + if( (ret=digi_write_oob_command( port, buf, i, 1 )) != 0 ) dbg( "digi_set_termios: write oob failed, ret=%d", ret ); } @@ -1006,12 +1150,15 @@ dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, ol static void digi_break_ctl( struct usb_serial_port *port, int break_state ) { -#ifdef DEBUG - digi_private_t *priv = (digi_private_t *)(port->private); -#endif + unsigned char buf[4]; -dbg( "digi_break_ctl: TOP: port=%d", priv->dp_port_num ); + buf[0] = DIGI_CMD_BREAK_CONTROL; + buf[1] = 2; /* length */ + buf[2] = break_state ? 1 : 0; + buf[3] = 0; /* pad */ + + digi_write_inb_command( port, buf, 4, 0 ); } @@ -1020,7 +1167,7 @@ static int digi_ioctl( struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg ) { - digi_private_t *priv = (digi_private_t *)(port->private); + digi_port_t *priv = (digi_port_t *)(port->private); unsigned int val; unsigned long flags = 0; @@ -1048,7 +1195,7 @@ dbg( "digi_ioctl: TOP: port=%d, cmd=0x%x", priv->dp_port_num, cmd ); else if( cmd == TIOCMBIC ) val = priv->dp_modem_signals & ~val; spin_unlock_irqrestore( &priv->dp_port_lock, flags ); - return( digi_set_modem_signals( port, val ) ); + return( digi_set_modem_signals( port, val, 1 ) ); case TIOCMIWAIT: /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ @@ -1072,7 +1219,7 @@ static int digi_write( struct usb_serial_port *port, int from_user, { int ret,data_len,new_len; - digi_private_t *priv = (digi_private_t *)(port->private); + digi_port_t *priv = (digi_port_t *)(port->private); unsigned char *data = port->write_urb->transfer_buffer; unsigned char user_buf[64]; /* 64 bytes is max USB bulk packet */ unsigned long flags = 0; @@ -1098,9 +1245,10 @@ priv->dp_port_num, count, from_user, in_interrupt() ); /* buffer data if count is 1 (probably put_char) if possible */ if( count == 1 ) { new_len = MIN( count, - DIGI_PORT_BUF_LEN-priv->dp_buf_len ); - memcpy( priv->dp_buf+priv->dp_buf_len, buf, new_len ); - priv->dp_buf_len += new_len; + DIGI_OUT_BUF_SIZE-priv->dp_out_buf_len ); + memcpy( priv->dp_out_buf+priv->dp_out_buf_len, buf, + new_len ); + priv->dp_out_buf_len += new_len; } else { new_len = 0; } @@ -1113,8 +1261,8 @@ priv->dp_port_num, count, from_user, in_interrupt() ); /* allow space for any buffered data and for new data, up to */ /* transfer buffer size - 2 (for command and length bytes) */ - new_len = MIN( count, port->bulk_out_size-2-priv->dp_buf_len ); - data_len = new_len + priv->dp_buf_len; + new_len = MIN( count, port->bulk_out_size-2-priv->dp_out_buf_len ); + data_len = new_len + priv->dp_out_buf_len; if( data_len == 0 ) { spin_unlock_irqrestore( &priv->dp_port_lock, flags ); @@ -1127,22 +1275,24 @@ priv->dp_port_num, count, from_user, in_interrupt() ); *data++ = data_len; /* copy in buffered data first */ - memcpy( data, priv->dp_buf, priv->dp_buf_len ); - data += priv->dp_buf_len; + memcpy( data, priv->dp_out_buf, priv->dp_out_buf_len ); + data += priv->dp_out_buf_len; /* copy in new data */ memcpy( data, from_user ? user_buf : buf, new_len ); if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { ret = new_len; - priv->dp_buf_len = 0; + priv->dp_out_buf_len = 0; } /* return length of new data written, or error */ spin_unlock_irqrestore( &priv->dp_port_lock, flags ); if( ret < 0 ) { - dbg( "digi_write: usb_submit_urb failed, ret=%d", ret ); + err( __FUNCTION__ ": usb_submit_urb failed, ret=%d, port=%d", + ret, priv->dp_port_num ); } + dbg( "digi_write: returning %d", ret ); return( ret ); @@ -1154,21 +1304,27 @@ static void digi_write_bulk_callback( struct urb *urb ) struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial; - digi_private_t *priv; + digi_port_t *priv; int ret = 0; -dbg( "digi_write_bulk_callback: TOP" ); +dbg( "digi_write_bulk_callback: TOP, urb->status=%d", urb->status ); - /* port sanity check */ - if( port == NULL || (priv=(digi_private_t *)(port->private)) == NULL ) { + /* port and serial sanity check */ + if( port == NULL || (priv=(digi_port_t *)(port->private)) == NULL ) { err( __FUNCTION__ ": port or port->private is NULL, status=%d", urb->status ); return; } + serial = port->serial; + if( serial == NULL || serial->private == NULL ) { + err( __FUNCTION__ ": serial or serial->private is NULL, status=%d", urb->status ); + return; + } /* handle oob callback */ - if( priv->dp_port_num == oob_port_num ) { + if( priv->dp_port_num + == ((digi_serial_t *)(serial->private))->ds_oob_port_num ) { dbg( "digi_write_bulk_callback: oob callback" ); spin_lock( &priv->dp_port_lock ); wake_up_interruptible( &port->write_wait ); @@ -1176,29 +1332,29 @@ dbg( "digi_write_bulk_callback: TOP" ); return; } - /* sanity checks */ - if( port_paranoia_check( port, "digi_write_bulk_callback" ) ) - return; - serial = port->serial; - if( serial_paranoia_check( serial, "digi_write_bulk_callback" ) ) + /* further sanity checks */ + if( port_paranoia_check( port, __FUNCTION__ ) + || serial_paranoia_check( serial, __FUNCTION__ ) ) return; - /* try to send any buffered data on this port */ + /* try to send any buffered data on this port, if it is open */ spin_lock( &priv->dp_port_lock ); - if( port->write_urb->status != -EINPROGRESS && priv->dp_buf_len > 0 ) { + if( priv->dp_open_count && port->write_urb->status != -EINPROGRESS + && priv->dp_out_buf_len > 0 ) { *((unsigned char *)(port->write_urb->transfer_buffer)) = (unsigned char)DIGI_CMD_SEND_DATA; *((unsigned char *)(port->write_urb->transfer_buffer)+1) - = (unsigned char)priv->dp_buf_len; + = (unsigned char)priv->dp_out_buf_len; - port->write_urb->transfer_buffer_length = priv->dp_buf_len+2; + port->write_urb->transfer_buffer_length + = priv->dp_out_buf_len+2; - memcpy( port->write_urb->transfer_buffer+2, priv->dp_buf, - priv->dp_buf_len ); + memcpy( port->write_urb->transfer_buffer+2, priv->dp_out_buf, + priv->dp_out_buf_len ); if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { - priv->dp_buf_len = 0; + priv->dp_out_buf_len = 0; } } @@ -1224,7 +1380,7 @@ static int digi_write_room( struct usb_serial_port *port ) { int room; - digi_private_t *priv = (digi_private_t *)(port->private); + digi_port_t *priv = (digi_port_t *)(port->private); unsigned long flags = 0; @@ -1233,7 +1389,7 @@ static int digi_write_room( struct usb_serial_port *port ) if( port->write_urb->status == -EINPROGRESS ) room = 0; else - room = port->bulk_out_size - 2 - priv->dp_buf_len; + room = port->bulk_out_size - 2 - priv->dp_out_buf_len; spin_unlock_irqrestore( &priv->dp_port_lock, flags ); @@ -1246,7 +1402,7 @@ dbg( "digi_write_room: port=%d, room=%d", priv->dp_port_num, room ); static int digi_chars_in_buffer( struct usb_serial_port *port ) { - digi_private_t *priv = (digi_private_t *)(port->private); + digi_port_t *priv = (digi_port_t *)(port->private); if( port->write_urb->status == -EINPROGRESS ) { @@ -1254,8 +1410,8 @@ dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, port->bulk_ou /* return( port->bulk_out_size - 2 ); */ return( 256 ); } else { -dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, priv->dp_buf_len ); - return( priv->dp_buf_len ); +dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, priv->dp_out_buf_len ); + return( priv->dp_out_buf_len ); } } @@ -1266,7 +1422,7 @@ static int digi_open( struct usb_serial_port *port, struct file *filp ) int ret; unsigned char buf[32]; - digi_private_t *priv = (digi_private_t *)(port->private); + digi_port_t *priv = (digi_port_t *)(port->private); struct termios not_termios; unsigned long flags = 0; @@ -1285,12 +1441,18 @@ dbg( "digi_open: TOP: port=%d, active=%d, open_count=%d", priv->dp_port_num, por return( -EAGAIN ); } + /* inc module use count before sleeping to wait for closes */ + ++priv->dp_open_count; + MOD_INC_USE_COUNT; + /* wait for a close in progress to finish */ while( priv->dp_in_close ) { cond_wait_interruptible_timeout_irqrestore( &priv->dp_close_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags ); if( signal_pending(current) ) { + --priv->dp_open_count; + MOD_DEC_USE_COUNT; return( -EINTR ); } spin_lock_irqsave( &priv->dp_port_lock, flags ); @@ -1299,16 +1461,12 @@ dbg( "digi_open: TOP: port=%d, active=%d, open_count=%d", priv->dp_port_num, por /* if port is already open, just return */ /* be sure exactly one open proceeds */ if( port->active ) { - ++priv->dp_open_count; - MOD_INC_USE_COUNT; spin_unlock_irqrestore( &priv->dp_port_lock, flags ); return( 0 ); } - /* open is certain */ + /* first open, mark port as active */ port->active = 1; - ++priv->dp_open_count; - MOD_INC_USE_COUNT; spin_unlock_irqrestore( &priv->dp_port_lock, flags ); /* read modem signals automatically whenever they change */ @@ -1323,7 +1481,7 @@ dbg( "digi_open: TOP: port=%d, active=%d, open_count=%d", priv->dp_port_num, por buf[6] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; buf[7] = 0; - if( (ret=digi_write_oob_command( buf, 8 )) != 0 ) + if( (ret=digi_write_oob_command( port, buf, 8, 1 )) != 0 ) dbg( "digi_open: write oob failed, ret=%d", ret ); /* set termios settings */ @@ -1332,7 +1490,7 @@ dbg( "digi_open: TOP: port=%d, active=%d, open_count=%d", priv->dp_port_num, por digi_set_termios( port, ¬_termios ); /* set DTR and RTS */ - digi_set_modem_signals( port, TIOCM_DTR|TIOCM_RTS ); + digi_set_modem_signals( port, TIOCM_DTR|TIOCM_RTS, 1 ); return( 0 ); @@ -1345,7 +1503,7 @@ static void digi_close( struct usb_serial_port *port, struct file *filp ) int ret; unsigned char buf[32]; struct tty_struct *tty = port->tty; - digi_private_t *priv = (digi_private_t *)(port->private); + digi_port_t *priv = (digi_port_t *)port->private; unsigned long flags = 0; @@ -1386,7 +1544,7 @@ dbg( "digi_close: TOP: port=%d, active=%d, open_count=%d", priv->dp_port_num, po } /* drop DTR and RTS */ - digi_set_modem_signals( port, 0 ); + digi_set_modem_signals( port, 0, 0 ); /* disable input flow control */ buf[0] = DIGI_CMD_SET_INPUT_FLOW_CONTROL; @@ -1406,29 +1564,24 @@ dbg( "digi_close: TOP: port=%d, active=%d, open_count=%d", priv->dp_port_num, po buf[10] = DIGI_DISABLE; buf[11] = 0; - /* flush fifos */ - buf[12] = DIGI_CMD_IFLUSH_FIFO; + /* disable receive */ + buf[12] = DIGI_CMD_RECEIVE_ENABLE; buf[13] = priv->dp_port_num; - buf[14] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; + buf[14] = DIGI_DISABLE; buf[15] = 0; - /* disable receive */ - buf[16] = DIGI_CMD_RECEIVE_ENABLE; + /* flush fifos */ + buf[16] = DIGI_CMD_IFLUSH_FIFO; buf[17] = priv->dp_port_num; - buf[18] = DIGI_DISABLE; + buf[18] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; buf[19] = 0; - if( (ret=digi_write_oob_command( buf, 20 )) != 0 ) + if( (ret=digi_write_oob_command( port, buf, 20, 0 )) != 0 ) dbg( "digi_close: write oob failed, ret=%d", ret ); /* wait for final commands on oob port to complete */ - while( oob_port->write_urb->status == -EINPROGRESS ) { - interruptible_sleep_on_timeout( &oob_port->write_wait, - DIGI_RETRY_TIMEOUT ); - if( signal_pending(current) ) { - break; - } - } + interruptible_sleep_on_timeout( &priv->dp_flush_wait, + DIGI_CLOSE_TIMEOUT ); /* shutdown any outstanding bulk writes */ usb_unlink_urb (port->write_urb); @@ -1458,23 +1611,31 @@ static int digi_startup_device( struct usb_serial *serial ) { int i,ret = 0; + digi_serial_t *serial_priv = (digi_serial_t *)serial->private; + struct usb_serial_port *port; /* be sure this happens exactly once */ - spin_lock( &startup_lock ); - if( device_startup ) { - spin_unlock( &startup_lock ); + spin_lock( &serial_priv->ds_serial_lock ); + if( serial_priv->ds_device_started ) { + spin_unlock( &serial_priv->ds_serial_lock ); return( 0 ); } - device_startup = 1; - spin_unlock( &startup_lock ); + serial_priv->ds_device_started = 1; + spin_unlock( &serial_priv->ds_serial_lock ); /* start reading from each bulk in endpoint for the device */ - for( i=0; i<digi_acceleport_device.num_ports+1; i++ ) { + /* set USB_DISABLE_SPD flag for write bulk urbs */ + for( i=0; i<serial->type->num_ports+1; i++ ) { - if( (ret=usb_submit_urb(serial->port[i].read_urb)) != 0 ) { - dbg( "digi_startup_device: usb_submit_urb failed, port=%d, ret=%d", - i, ret ); + port = &serial->port[i]; + + port->write_urb->transfer_flags |= USB_DISABLE_SPD; + + if( (ret=usb_submit_urb(port->read_urb)) != 0 ) { + err( + __FUNCTION__ ": usb_submit_urb failed, ret=%d, port=%d", + ret, i ); break; } @@ -1489,51 +1650,68 @@ static int digi_startup( struct usb_serial *serial ) { int i; - digi_private_t *priv; + digi_port_t *priv; + digi_serial_t *serial_priv; dbg( "digi_startup: TOP" ); - spin_lock_init( &startup_lock ); - init_waitqueue_head( &modem_change_wait ); - init_waitqueue_head( &transmit_idle_wait ); - /* allocate the private data structures for all ports */ /* number of regular ports + 1 for the out-of-band port */ - for( i=0; i<digi_acceleport_device.num_ports+1; i++ ) { + for( i=0; i<serial->type->num_ports+1; i++ ) { serial->port[i].active = 0; - /* allocate private structure */ + /* allocate port private structure */ priv = serial->port[i].private = - (digi_private_t *)kmalloc( sizeof(digi_private_t), + (digi_port_t *)kmalloc( sizeof(digi_port_t), GFP_KERNEL ); - if( priv == (digi_private_t *)0 ) + if( priv == (digi_port_t *)0 ) { + while( --i >= 0 ) + kfree( serial->port[i].private ); return( 1 ); /* error */ + } - /* initialize private structure */ + /* initialize port private structure */ + spin_lock_init( &priv->dp_port_lock ); priv->dp_port_num = i; - priv->dp_buf_len = 0; + priv->dp_out_buf_len = 0; + priv->dp_in_buf_len = 0; priv->dp_modem_signals = 0; + init_waitqueue_head( &priv->dp_modem_change_wait ); priv->dp_open_count = 0; priv->dp_transmit_idle = 0; + init_waitqueue_head( &priv->dp_transmit_idle_wait ); + priv->dp_throttled = 0; + priv->dp_throttle_restart = 0; + init_waitqueue_head( &priv->dp_flush_wait ); priv->dp_in_close = 0; init_waitqueue_head( &priv->dp_close_wait ); priv->dp_wakeup_task.next = NULL; priv->dp_wakeup_task.sync = 0; priv->dp_wakeup_task.routine = (void *)digi_wakeup_write_lock; priv->dp_wakeup_task.data = (void *)(&serial->port[i]); - spin_lock_init( &priv->dp_port_lock ); /* initialize write wait queue for this port */ - init_waitqueue_head(&serial->port[i].write_wait); + init_waitqueue_head( &serial->port[i].write_wait ); + + } + /* allocate serial private structure */ + serial_priv = serial->private = + (digi_serial_t *)kmalloc( sizeof(digi_serial_t), + GFP_KERNEL ); + if( serial_priv == (digi_serial_t *)0 ) { + for( i=0; i<serial->type->num_ports+1; i++ ) + kfree( serial->port[i].private ); + return( 1 ); /* error */ } - /* initialize out of band port info */ - oob_port_num = digi_acceleport_device.num_ports; - oob_port = &serial->port[oob_port_num]; - device_startup = 0; + /* initialize serial private structure */ + spin_lock_init( &serial_priv->ds_serial_lock ); + serial_priv->ds_oob_port_num = serial->type->num_ports; + serial_priv->ds_oob_port = &serial->port[serial_priv->ds_oob_port_num]; + serial_priv->ds_device_started = 0; return( 0 ); @@ -1544,22 +1722,20 @@ static void digi_shutdown( struct usb_serial *serial ) { int i; - digi_private_t *priv; + digi_port_t *priv; unsigned long flags; dbg( "digi_shutdown: TOP, in_interrupt()=%d", in_interrupt() ); /* stop reads and writes on all ports */ - for( i=0; i<digi_acceleport_device.num_ports+1; i++ ) { + for( i=0; i<serial->type->num_ports+1; i++ ) { usb_unlink_urb( serial->port[i].read_urb ); usb_unlink_urb( serial->port[i].write_urb ); } - device_startup = 0; - /* dec module use count */ - for( i=0; i<digi_acceleport_device.num_ports; i++ ) { + for( i=0; i<serial->type->num_ports; i++ ) { priv = serial->port[i].private; spin_lock_irqsave( &priv->dp_port_lock, flags ); while( priv->dp_open_count > 0 ) { @@ -1571,8 +1747,9 @@ dbg( "digi_shutdown: TOP, in_interrupt()=%d", in_interrupt() ); /* free the private data structures for all ports */ /* number of regular ports + 1 for the out-of-band port */ - for( i=0; i<digi_acceleport_device.num_ports+1; i++ ) + for( i=0; i<serial->type->num_ports+1; i++ ) kfree( serial->port[i].private ); + kfree( serial->private ); } @@ -1581,18 +1758,24 @@ static void digi_read_bulk_callback( struct urb *urb ) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; - digi_private_t *priv; + digi_port_t *priv; int ret; dbg( "digi_read_bulk_callback: TOP" ); /* port sanity check, do not resubmit if port is not valid */ - if( port == NULL || (priv=(digi_private_t *)(port->private)) == NULL ) { + if( port == NULL || (priv=(digi_port_t *)(port->private)) == NULL ) { err( __FUNCTION__ ": port or port->private is NULL, status=%d", urb->status ); return; } + if( port->serial == NULL + || serial_paranoia_check( port->serial, __FUNCTION__ ) + || port->serial->private == NULL ) { + err( __FUNCTION__ ": serial is bad or serial->private is NULL, status=%d", urb->status ); + return; + } /* do not resubmit urb if it has any status error */ if( urb->status ) { @@ -1601,7 +1784,8 @@ dbg( "digi_read_bulk_callback: TOP" ); } /* handle oob or inb callback, do not resubmit if error */ - if( priv->dp_port_num == oob_port_num ) { + if( priv->dp_port_num + == ((digi_serial_t *)(port->serial->private))->ds_oob_port_num ) { if( digi_read_oob_callback( urb ) != 0 ) return; } else { @@ -1623,45 +1807,110 @@ dbg( "digi_read_bulk_callback: TOP" ); * * Digi Read INB Callback handles reads on the in band ports, sending * the data on to the tty subsystem. When called we know port and -* port->private are not NULL. It returns 0 if successful, and -1 if -* the sanity checks failed. +* port->private are not NULL and port->serial has been validated. +* It returns 0 if successful, 1 if successful but the port is +* throttled, and -1 if the sanity checks failed. */ static int digi_read_inb_callback( struct urb *urb ) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; - struct usb_serial *serial = port->serial; struct tty_struct *tty = port->tty; - digi_private_t *priv = (digi_private_t *)(port->private); + digi_port_t *priv = (digi_port_t *)(port->private); int opcode = ((unsigned char *)urb->transfer_buffer)[0]; int len = ((unsigned char *)urb->transfer_buffer)[1]; int status = ((unsigned char *)urb->transfer_buffer)[2]; unsigned char *data = ((unsigned char *)urb->transfer_buffer)+3; - int i; + int flag,throttled; - /* sanity checks */ - if( port_paranoia_check( port, __FUNCTION__ ) - || serial_paranoia_check( serial, __FUNCTION__ ) ) + /* sanity check */ + if( port_paranoia_check( port, __FUNCTION__ ) ) return( -1 ); - /* short packet check */ + /* do not process callbacks on closed ports */ + /* but do continue the read chain */ + if( priv->dp_open_count == 0 ) + return( 0 ); + + /* short/multiple packet check */ if( urb->actual_length != len + 2 ) { - err( __FUNCTION__ ": INCOMPLETE PACKET, urb->status=%d, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", urb->status, priv->dp_port_num, opcode, len, urb->actual_length, status ); + err( __FUNCTION__ ": INCOMPLETE OR MULTIPLE PACKET, urb->status=%d, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", urb->status, priv->dp_port_num, opcode, len, urb->actual_length, status ); return( -1 ); } + spin_lock( &priv->dp_port_lock ); + + /* check for throttle; if set, do not resubmit read urb */ + /* indicate the read chain needs to be restarted on unthrottle */ + throttled = priv->dp_throttled; + if( throttled ) + priv->dp_throttle_restart = 1; + /* receive data */ - if( opcode == DIGI_CMD_RECEIVE_DATA && urb->actual_length > 3 ) { - len = MIN( len, urb->actual_length-3 ); - for( i=0; i<len; ++i ) { - tty_insert_flip_char(tty, data[i], 0); - } - tty_flip_buffer_push(tty); + if( opcode == DIGI_CMD_RECEIVE_DATA ) { + + /* get flag from status */ + flag = 0; + + /* overrun is special, not associated with a char */ + if( status & DIGI_OVERRUN_ERROR ) { + tty_insert_flip_char( tty, 0, TTY_OVERRUN ); + } + + /* break takes precedence over parity, */ + /* which takes precedence over framing errors */ + if( status & DIGI_BREAK_ERROR ) { + flag = TTY_BREAK; + } else if( status & DIGI_PARITY_ERROR ) { + flag = TTY_PARITY; + } else if( status & DIGI_FRAMING_ERROR ) { + flag = TTY_FRAME; + } + + /* data length is len-1 (one byte of len is status) */ + --len; + + if( throttled ) { + + len = MIN( len, + DIGI_IN_BUF_SIZE - priv->dp_in_buf_len ); + + if( len > 0 ) { + memcpy( priv->dp_in_buf + priv->dp_in_buf_len, + data, len ); + memset( priv->dp_in_flag_buf + + priv->dp_in_buf_len, flag, len ); + priv->dp_in_buf_len += len; + } + + } else { + + len = MIN( len, TTY_FLIPBUF_SIZE - tty->flip.count ); + + if( len > 0 ) { + memcpy( tty->flip.char_buf_ptr, data, len ); + memset( tty->flip.flag_buf_ptr, flag, len ); + tty->flip.char_buf_ptr += len; + tty->flip.flag_buf_ptr += len; + tty->flip.count += len; + tty_flip_buffer_push( tty ); + } + + } + } - return( 0 ); + spin_unlock( &priv->dp_port_lock ); + + if( opcode == DIGI_CMD_RECEIVE_DISABLE ) { + dbg( __FUNCTION__ ": got RECEIVE_DISABLE" ); + } else if( opcode != DIGI_CMD_RECEIVE_DATA ) { + dbg( __FUNCTION__ ": unknown opcode: %d", opcode ); + } + + return( throttled ? 1 : 0 ); } @@ -1670,8 +1919,9 @@ static int digi_read_inb_callback( struct urb *urb ) * Digi Read OOB Callback * * Digi Read OOB Callback handles reads on the out of band port. -* When called we know port and port->private are not NULL. It -* returns 0 if successful, and -1 if the sanity checks failed. +* When called we know port and port->private are not NULL and +* the port->serial is valid. It returns 0 if successful, and +* -1 if the sanity checks failed. */ static int digi_read_oob_callback( struct urb *urb ) @@ -1679,19 +1929,13 @@ static int digi_read_oob_callback( struct urb *urb ) struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial = port->serial; - digi_private_t *priv = (digi_private_t *)(port->private); + digi_port_t *priv = (digi_port_t *)(port->private); int opcode, line, status, val; int i; -dbg( "digi_read_oob_callback: len=%d", urb->actual_length ); - - /* sanity check */ - if( serial == NULL ) { - err( __FUNCTION__ ": port->serial is NULL, status=%d, port=%d", - urb->status, priv->dp_port_num ); - return( -1 ); - } +dbg( "digi_read_oob_callback: port=%d, len=%d", priv->dp_port_num, +urb->actual_length ); /* handle each oob command */ for( i=0; i<urb->actual_length-3; ) { @@ -1701,25 +1945,39 @@ dbg( "digi_read_oob_callback: len=%d", urb->actual_length ); status = ((unsigned char *)urb->transfer_buffer)[i++]; val = ((unsigned char *)urb->transfer_buffer)[i++]; -dbg( "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d", opcode, line, status, val ); +dbg( "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d", +opcode, line, status, val ); - if( status != 0 ) + if( status != 0 || line >= serial->type->num_ports ) continue; - if( (priv=serial->port[line].private) == NULL ) { - dbg( __FUNCTION__ ": port[%d].private is NULL!", line ); - continue; - } + port = &serial->port[line]; + + if( port_paranoia_check( port, __FUNCTION__ ) + || (priv=port->private) == NULL ) + return( -1 ); if( opcode == DIGI_CMD_READ_INPUT_SIGNALS ) { spin_lock( &priv->dp_port_lock ); /* convert from digi flags to termiox flags */ - if( val & DIGI_READ_INPUT_SIGNALS_CTS ) + if( val & DIGI_READ_INPUT_SIGNALS_CTS ) { priv->dp_modem_signals |= TIOCM_CTS; - else + /* port must be open to use tty struct */ + if( priv->dp_open_count + && port->tty->termios->c_cflag & CRTSCTS ) { + port->tty->hw_stopped = 0; + digi_wakeup_write( port ); + } + } else { priv->dp_modem_signals &= ~TIOCM_CTS; + /* port must be open to use tty struct */ + if( priv->dp_open_count + && port->tty->termios->c_cflag & CRTSCTS ) { + port->tty->hw_stopped = 1; + } + } if( val & DIGI_READ_INPUT_SIGNALS_DSR ) priv->dp_modem_signals |= TIOCM_DSR; else @@ -1733,16 +1991,20 @@ dbg( "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d", opcode, li else priv->dp_modem_signals &= ~TIOCM_CD; - wake_up_interruptible( &modem_change_wait ); + wake_up_interruptible( &priv->dp_modem_change_wait ); spin_unlock( &priv->dp_port_lock ); } else if( opcode == DIGI_CMD_TRANSMIT_IDLE ) { spin_lock( &priv->dp_port_lock ); priv->dp_transmit_idle = 1; - wake_up_interruptible( &transmit_idle_wait ); + wake_up_interruptible( &priv->dp_transmit_idle_wait ); spin_unlock( &priv->dp_port_lock ); + } else if( opcode == DIGI_CMD_IFLUSH_FIFO ) { + + wake_up_interruptible( &priv->dp_flush_wait ); + } } @@ -1754,20 +2016,23 @@ dbg( "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d", opcode, li int digi_init (void) { - usb_serial_register (&digi_acceleport_device); + usb_serial_register (&digi_acceleport_2_device); + usb_serial_register (&digi_acceleport_4_device); return 0; } void digi_exit (void) { - usb_serial_deregister (&digi_acceleport_device); + usb_serial_deregister (&digi_acceleport_2_device); + usb_serial_deregister (&digi_acceleport_4_device); } module_init(digi_init); module_exit(digi_exit); + MODULE_AUTHOR("Peter Berger <pberger@brimson.com>, Al Borchers <borchers@steinerpoint.com>"); MODULE_DESCRIPTION("Digi AccelePort USB-4 Serial Converter driver"); diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 675764ad4..e118e29b8 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -1,8 +1,9 @@ /* * USB Keyspan PDA Converter driver * - * Copyright (C) 1999, 2000 - * Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 1999, 2000 Greg Kroah-Hartman <greg@kroah.com> + * Copyright (c) 1999, 2000 Brian Warner <warner@lothar.com> + * Copyright (c) 2000 Al Borchers <borchers@steinerpoint.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,6 +12,24 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (07/20/2000) borchers + * - keyspan_pda_write no longer sleeps if it is called on interrupt time; + * PPP and the line discipline with stty echo on can call write on + * interrupt time and this would cause an oops if write slept + * - if keyspan_pda_write is in an interrupt, it will not call + * usb_control_msg (which sleeps) to query the room in the device + * buffer, it simply uses the current room value it has + * - if the urb is busy or if it is throttled keyspan_pda_write just + * returns 0, rather than sleeping to wait for this to change; the + * write_chan code in n_tty.c will sleep if needed before calling + * keyspan_pda_write again + * - if the device needs to be unthrottled, write now queues up the + * call to usb_control_msg (which sleeps) to unthrottle the device + * - the wakeups from keyspan_pda_write_bulk_callback are queued rather + * than done directly from the callback to avoid the race in write_chan + * - keyspan_pda_chars_in_buffer also indicates its buffer is full if the + * urb status is -EINPROGRESS, meaning it cannot write at the moment + * * (07/19/2000) gkh * Added module_init and module_exit functions to handle the fact that this * driver is a loadable module now. @@ -35,6 +54,7 @@ #include <linux/tty.h> #include <linux/module.h> #include <linux/spinlock.h> +#include <linux/tqueue.h> #ifdef CONFIG_USB_SERIAL_DEBUG #define DEBUG @@ -56,6 +76,8 @@ struct ezusb_hex_record { struct keyspan_pda_private { int tx_room; int tx_throttled; + struct tq_struct wakeup_task; + struct tq_struct unthrottle_task; }; #define KEYSPAN_VENDOR_ID 0x06cd @@ -69,6 +91,45 @@ static __u16 keyspan_pda_product_id = KEYSPAN_PDA_ID; +static void keyspan_pda_wakeup_write( struct usb_serial_port *port ) +{ + + struct tty_struct *tty = port->tty; + + /* wake up port processes */ + wake_up_interruptible( &port->write_wait ); + + /* wake up line discipline */ + if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup ) + (tty->ldisc.write_wakeup)(tty); + + /* wake up other tty processes */ + wake_up_interruptible( &tty->write_wait ); + /* For 2.2.16 backport -- wake_up_interruptible( &tty->poll_wait ); */ + +} + +static void keyspan_pda_request_unthrottle( struct usb_serial *serial ) +{ + + dbg(" request_unthrottle"); + /* ask the device to tell us when the tx buffer becomes + sufficiently empty */ + usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + 7, /* request_unthrottle */ + USB_TYPE_VENDOR | USB_RECIP_INTERFACE + | USB_DIR_OUT, + 16, /* value: threshold */ + 0, /* index */ + NULL, + 0, + 2*HZ); + +} + + static void keyspan_pda_rx_interrupt (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; @@ -113,8 +174,8 @@ static void keyspan_pda_rx_interrupt (struct urb *urb) case 2: /* tx unthrottle interrupt */ tty = serial->port[0].tty; priv->tx_throttled = 0; - wake_up(&port->write_wait); /* wake up writer */ - wake_up(&tty->write_wait); /* them too */ + /* queue up a wakeup at scheduler time */ + queue_task( &priv->wakeup_task, &tq_scheduler ); break; default: break; @@ -355,7 +416,6 @@ static int keyspan_pda_write(struct usb_serial_port *port, int from_user, int request_unthrottle = 0; int rc = 0; struct keyspan_pda_private *priv; - DECLARE_WAITQUEUE(wait, current); priv = (struct keyspan_pda_private *)(port->private); /* guess how much room is left in the device's ring buffer, and if we @@ -376,53 +436,22 @@ static int keyspan_pda_write(struct usb_serial_port *port, int from_user, the TX urb is in-flight (wait until it completes) the device is full (wait until it says there is room) */ - while (port->write_urb->status == -EINPROGRESS) { - if (0 /* file->f_flags & O_NONBLOCK */) { - rc = -EAGAIN; - goto err; - } - interruptible_sleep_on(&port->write_wait); - if (signal_pending(current)) { - rc = -ERESTARTSYS; - goto err; - } + if (port->write_urb->status == -EINPROGRESS || priv->tx_throttled ) { + return( 0 ); } - /* at this point the URB is in our control, nobody else can submit it + /* At this point the URB is in our control, nobody else can submit it again (the only sudden transition was the one from EINPROGRESS to - finished) */ - - /* the next potential block is that our TX process might be throttled. - The transition from throttled->not happens because of an Rx - interrupt, and the wake_up occurs during the same interrupt, so we - have to be careful to avoid a race that would cause us to sleep - forever. */ - - add_wait_queue(&port->write_wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); - while (priv->tx_throttled) { - /* device can't accomodate any more characters. Sleep until it - can. Woken up by an Rx interrupt message, which clears - tx_throttled first. */ - dbg(" tx_throttled, going to sleep"); - if (signal_pending(current)) { - current->state = TASK_RUNNING; - remove_wait_queue(&port->write_wait, &wait); - dbg(" woke up because of signal"); - rc = -ERESTARTSYS; - goto err; - } - schedule(); - dbg(" woke up"); - } - remove_wait_queue(&port->write_wait, &wait); - set_current_state(TASK_RUNNING); + finished). Also, the tx process is not throttled. So we are + ready to write. */ count = (count > port->bulk_out_size) ? port->bulk_out_size : count; - if (count > priv->tx_room) { + + /* Check if we might overrun the Tx buffer. If so, ask the + device how much room it really has. This is done only on + scheduler time, since usb_control_msg() sleeps. */ + if (count > priv->tx_room && !in_interrupt()) { unsigned char room; - /* Looks like we might overrun the Tx buffer. Ask the device - how much room it really has */ rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), 6, /* write_room */ @@ -443,19 +472,20 @@ static int keyspan_pda_write(struct usb_serial_port *port, int from_user, } dbg(" roomquery says %d", room); priv->tx_room = room; - if (count > priv->tx_room) { - /* we're about to completely fill the Tx buffer, so - we'll be throttled afterwards. */ - count = priv->tx_room; - request_unthrottle = 1; - } } - priv->tx_room -= count; + if (count > priv->tx_room) { + /* we're about to completely fill the Tx buffer, so + we'll be throttled afterwards. */ + count = priv->tx_room; + request_unthrottle = 1; + } if (count) { /* now transfer data */ if (from_user) { - copy_from_user(port->write_urb->transfer_buffer, buf, count); + if( copy_from_user(port->write_urb->transfer_buffer, + buf, count) ) + return( -EFAULT ); } else { memcpy (port->write_urb->transfer_buffer, buf, count); @@ -463,6 +493,8 @@ static int keyspan_pda_write(struct usb_serial_port *port, int from_user, /* send the data out the bulk port */ port->write_urb->transfer_buffer_length = count; + priv->tx_room -= count; + if (usb_submit_urb(port->write_urb)) dbg(" usb_submit_urb(write bulk) failed"); } @@ -473,25 +505,11 @@ static int keyspan_pda_write(struct usb_serial_port *port, int from_user, } if (request_unthrottle) { - dbg(" request_unthrottle"); - /* ask the device to tell us when the tx buffer becomes - sufficiently empty */ priv->tx_throttled = 1; /* block writers */ - rc = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - 7, /* request_unthrottle */ - USB_TYPE_VENDOR | USB_RECIP_INTERFACE - | USB_DIR_OUT, - 16, /* value: threshold */ - 0, /* index */ - NULL, - 0, - 2*HZ); + queue_task( &priv->unthrottle_task, &tq_scheduler ); } return (count); - err: - return (rc); } @@ -499,7 +517,9 @@ static void keyspan_pda_write_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial; - struct tty_struct *tty; + struct keyspan_pda_private *priv; + + priv = (struct keyspan_pda_private *)(port->private); if (port_paranoia_check (port, "keyspan_pda_rx_interrupt")) { return; @@ -510,14 +530,9 @@ static void keyspan_pda_write_bulk_callback (struct urb *urb) return; } - wake_up_interruptible(&port->write_wait); + /* queue up a wakeup at scheduler time */ + queue_task( &priv->wakeup_task, &tq_scheduler ); - tty = port->tty; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - - wake_up_interruptible(&tty->write_wait); } @@ -541,7 +556,7 @@ static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port) /* when throttled, return at least WAKEUP_CHARS to tell select() (via n_tty.c:normal_poll() ) that we're not writeable. */ - if (priv->tx_throttled) + if( port->write_urb->status == -EINPROGRESS || priv->tx_throttled ) return 256; return 0; } @@ -644,14 +659,25 @@ static int keyspan_pda_fake_startup (struct usb_serial *serial) static int keyspan_pda_startup (struct usb_serial *serial) { + + struct keyspan_pda_private *priv; + /* allocate the private data structures for all ports. Well, for all one ports. */ - serial->port[0].private = kmalloc(sizeof(struct keyspan_pda_private), - GFP_KERNEL); - if (!serial->port[0].private) + priv = serial->port[0].private + = kmalloc(sizeof(struct keyspan_pda_private), GFP_KERNEL); + if (!priv) return (1); /* error */ init_waitqueue_head(&serial->port[0].write_wait); + priv->wakeup_task.next = NULL; + priv->wakeup_task.sync = 0; + priv->wakeup_task.routine = (void *)keyspan_pda_wakeup_write; + priv->wakeup_task.data = (void *)(&serial->port[0]); + priv->unthrottle_task.next = NULL; + priv->unthrottle_task.sync = 0; + priv->unthrottle_task.routine = (void *)keyspan_pda_request_unthrottle; + priv->unthrottle_task.data = (void *)(serial); return (0); } diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h index fe63007b8..10d57076c 100644 --- a/drivers/usb/serial/usb-serial.h +++ b/drivers/usb/serial/usb-serial.h @@ -11,6 +11,9 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (08/08/2000) gkh + * Added open_count to port structure. + * * (07/23/2000) gkh * Added bulk_out_endpointAddress to port structure. * @@ -59,6 +62,7 @@ struct usb_serial_port { wait_queue_head_t write_wait; struct tq_struct tqueue; /* task queue for line discipline waking up */ + int open_count; /* number of times this port has been opened */ void * private; /* data private to the specific port */ }; diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 568f2b5cd..bc0ea807a 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -11,6 +11,11 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (08/08/2000) gkh + * Fixed endian problem in visor_startup. + * Fixed MOD_INC and MOD_DEC logic and the ability to open a port more + * than once. + * * (07/23/2000) gkh * Added pool of write urbs to speed up transfers to the visor. * @@ -71,6 +76,7 @@ static int visor_write (struct usb_serial_port *port, int from_user, const uns static void visor_throttle (struct usb_serial_port *port); static void visor_unthrottle (struct usb_serial_port *port); static int visor_startup (struct usb_serial *serial); +static void visor_shutdown (struct usb_serial *serial); static int visor_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); static void visor_set_termios (struct usb_serial_port *port, struct termios *old_termios); static void visor_write_bulk_callback (struct urb *urb); @@ -94,6 +100,7 @@ struct usb_serial_device_type handspring_device = { throttle: visor_throttle, unthrottle: visor_unthrottle, startup: visor_startup, + shutdown: visor_shutdown, ioctl: visor_ioctl, set_termios: visor_set_termios, write: visor_write, @@ -117,13 +124,18 @@ static int visor_open (struct usb_serial_port *port, struct file *filp) return -EINVAL; } - port->active = 1; - - /*Start reading from the device*/ - if (usb_submit_urb(port->read_urb)) - dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed"); + ++port->open_count; + MOD_INC_USE_COUNT; + + if (!port->active) { + port->active = 1; - return (0); + /*Start reading from the device*/ + if (usb_submit_urb(port->read_urb)) + dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed"); + } + + return 0; } @@ -134,19 +146,24 @@ static void visor_close (struct usb_serial_port *port, struct file * filp) dbg(__FUNCTION__ " - port %d", port->number); - if (!transfer_buffer) { - err(__FUNCTION__ " - kmalloc(%d) failed.", 0x12); - } else { - /* send a shutdown message to the device */ - usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_CLOSE_NOTIFICATION, - 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300); - kfree (transfer_buffer); - } + --port->open_count; + MOD_DEC_USE_COUNT; + + if (port->open_count <= 0) { + if (!transfer_buffer) { + err(__FUNCTION__ " - kmalloc(%d) failed.", 0x12); + } else { + /* send a shutdown message to the device */ + usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_CLOSE_NOTIFICATION, + 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300); + kfree (transfer_buffer); + } - /* shutdown our bulk reads and writes */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - port->active = 0; + /* shutdown our bulk read */ + usb_unlink_urb (port->read_urb); + port->active = 0; + port->open_count = 0; + } } @@ -282,6 +299,8 @@ static int visor_startup (struct usb_serial *serial) } else { struct visor_connection_info *connection_info = (struct visor_connection_info *)transfer_buffer; char *string; + + le16_to_cpus(&connection_info->num_ports); info("%s: Number of ports: %d", serial->type->name, connection_info->num_ports); for (i = 0; i < connection_info->num_ports; ++i) { switch (connection_info->connections[i].port_function_id) { @@ -322,6 +341,21 @@ static int visor_startup (struct usb_serial *serial) } +static void visor_shutdown (struct usb_serial *serial) +{ + int i; + + dbg (__FUNCTION__); + + /* stop reads and writes on all ports */ + for (i=0; i < serial->num_ports; ++i) { + while (serial->port[i].open_count > 0) { + visor_close (&serial->port[i], NULL); + } + } +} + + static int visor_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) { dbg(__FUNCTION__ " - port %d, cmd 0x%.4x", port->number, cmd); diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile index 7da59f78e..e7761863e 100644 --- a/drivers/usb/storage/Makefile +++ b/drivers/usb/storage/Makefile @@ -5,6 +5,7 @@ O_TARGET := usb-storage.o M_OBJS := usb-storage.o O_OBJS := scsiglue.o protocol.o transport.o usb.o +MOD_LIST_NAME := USB_STORAGE_MODULES CFLAGS_scsiglue.o:= -I../../scsi/ CFLAGS_protocol.o:= -I../../scsi/ @@ -13,6 +14,7 @@ CFLAGS_debug.o:= -I../../scsi/ CFLAGS_usb.o:= -I../../scsi/ CFLAGS_shuttle_usbat.o:= -I../../scsi/ CFLAGS_sddr09.o:= -I../../scsi/ +CFLAGS_dpcm.o:= -I../../scsi/ ifeq ($(CONFIG_USB_STORAGE_DEBUG),y) O_OBJS += debug.o @@ -37,5 +39,9 @@ endif ifeq ($(CONFIG_USB_STORAGE_SHUTTLE_COMPACTFLASH),y) O_OBJS += shuttle_cf.o endif + +ifeq ($(CONFIG_USB_STORAGE_DPCM),y) + O_OBJS += dpcm.o +endif include $(TOPDIR)/Rules.make diff --git a/drivers/usb/storage/dpcm.c b/drivers/usb/storage/dpcm.c new file mode 100644 index 000000000..8b7195bfc --- /dev/null +++ b/drivers/usb/storage/dpcm.c @@ -0,0 +1,83 @@ +/* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader + * + * $Id: dpcm.c,v 1.1 2000/08/08 01:26:12 webbb Exp $ + * + * DPCM driver v0.1: + * + * First release + * + * Current development and maintainance by: + * (c) 2000 Brian Webb (webbb@earthlink.net) + * + * This device contains both a CompactFlash card reader, which + * usest the Control/Bulk w/o Interrupt protocol and + * a SmartMedia card reader that uses the same protocol + * as the SDDR09. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/config.h> +#include "transport.h" +#include "protocol.h" +#include "usb.h" +#include "debug.h" +#include "dpcm.h" +#include "sddr09.h" + + +/* + * Transport for the Microtech DPCM-USB + * + */ +int dpcm_transport(Scsi_Cmnd *srb, struct us_data *us) +{ + int ret; + + if(srb == NULL) + return USB_STOR_TRANSPORT_ERROR; + + US_DEBUGP("dpcm_transport: LUN=%d\n", srb->lun); + + switch(srb->lun) { + case 0: + + /* + * LUN 0 corresponds to the CompactFlash card reader. + */ + return usb_stor_CB_transport(srb, us); + +#ifdef CONFIG_USB_STORAGE_SDDR09 + case 1: + + /* + * LUN 1 corresponds to the SmartMedia card reader. + */ + + /* + * Set the LUN to 0 (just in case). + */ + srb->lun = 0; us->srb->lun = 0; + ret = sddr09_transport(srb, us); + srb->lun = 1; us->srb->lun = 1; + + return ret; +#endif + + default: + US_DEBUGP("dpcm_transport: Invalid LUN %d\n", srb->lun); + return USB_STOR_TRANSPORT_ERROR; + } +} diff --git a/drivers/usb/storage/dpcm.h b/drivers/usb/storage/dpcm.h new file mode 100644 index 000000000..d6e5f86ad --- /dev/null +++ b/drivers/usb/storage/dpcm.h @@ -0,0 +1,34 @@ +/* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader + * + * $Id: dpcm.h,v 1.1 2000/08/08 01:26:12 webbb Exp $ + * + * DPCM driver v0.1: + * + * First release + * + * Current development and maintainance by: + * (c) 2000 Brian Webb (webbb@earthlink.net) + * + * See dpcm.c for more explanation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _MICROTECH_DPCM_USB_H +#define _MICROTECH_DPCM_USB_H + +extern int dpcm_transport(Scsi_Cmnd *srb, struct us_data *us); + +#endif diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 7517d63bc..c823aaed1 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -1,7 +1,7 @@ /* Driver for USB Mass Storage compliant devices * SCSI layer glue code * - * $Id: scsiglue.c,v 1.7 2000/07/28 20:33:18 gowdy Exp $ + * $Id: scsiglue.c,v 1.8 2000/08/11 23:15:05 mdharm Exp $ * * Current development and maintainance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) @@ -189,6 +189,12 @@ static int command_abort( Scsi_Cmnd *srb ) return SUCCESS; } + /* This is a sanity check that we should never hit */ + if (in_interrupt()) { + printk(KERN_ERR "usb-storage: command_abort() called from an interrupt!!! BAD!!! BAD!! BAD!!\n"); + return FAILED; + } + /* if we have an urb pending, let's wake the control thread up */ if (us->current_urb->status == -EINPROGRESS) { /* cancel the URB */ diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index f68595c47..492bc7259 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -1,5 +1,7 @@ /* Driver for SCM Microsystems USB-ATAPI cable * + * $Id: shuttle_usbat.c,v 1.2 2000/08/03 00:03:39 groovyjava Exp $ + * * SCM driver v0.2: * * Removed any reference to maxlen for bulk transfers. diff --git a/drivers/usb/storage/shuttle_usbat.h b/drivers/usb/storage/shuttle_usbat.h index eede2651f..ff5c07541 100644 --- a/drivers/usb/storage/shuttle_usbat.h +++ b/drivers/usb/storage/shuttle_usbat.h @@ -1,6 +1,8 @@ /* Driver for SCM Microsystems USB-ATAPI cable * Header File * + * $Id: shuttle_usbat.h,v 1.2 2000/08/03 00:03:39 groovyjava Exp $ + * * Current development and maintainance by: * (c) 2000 Robert Baruch (autophile@dol.net) * diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index a0fb2407c..dd3c489f1 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1,12 +1,13 @@ /* Driver for USB Mass Storage compliant devices * - * $Id: transport.c,v 1.5 2000/07/28 22:40:20 mdharm Exp $ + * $Id: transport.c,v 1.12 2000/08/08 15:22:38 gowdy Exp $ * * Current development and maintainance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * * Developed with the assistance of: * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) + * (c) 2000 Stephen J. Gowdy (SGowdy@lbl.gov) * * Initial work by: * (c) 1999 Michael Gee (michael@linuxspecific.com) @@ -52,6 +53,353 @@ #include <linux/malloc.h> /*********************************************************************** + * Helper routines + ***********************************************************************/ + +/* Calculate the length of the data transfer (not the command) for any + * given SCSI command + */ +static unsigned int us_transfer_length(Scsi_Cmnd *srb, struct us_data *us) +{ + int i; + unsigned int total = 0; + struct scatterlist *sg; + + /* support those devices which need the length calculated + * differently + */ + if (srb->cmnd[0] == INQUIRY) { + srb->cmnd[4] = 36; + } + + if ((srb->cmnd[0] == INQUIRY) || (srb->cmnd[0] == MODE_SENSE)) + return srb->cmnd[4]; + + if (srb->cmnd[0] == TEST_UNIT_READY) + return 0; + + /* Are we going to scatter gather? */ + if (srb->use_sg) { + /* Add up the sizes of all the scatter-gather segments */ + sg = (struct scatterlist *) srb->request_buffer; + for (i = 0; i < srb->use_sg; i++) + total += sg[i].length; + + return total; + } + else + /* Just return the length of the buffer */ + return srb->request_bufflen; +} + +/* Calculate the length of the data transfer (not the command) for any + * given SCSI command + */ +static unsigned int us_transfer_length_new(Scsi_Cmnd *srb, struct us_data *us) +{ + int i; + int doDefault = 0; + unsigned int len = 0; + unsigned int total = 0; + struct scatterlist *sg; + + /* This table tells us: + X = command not supported + L = return length in cmnd[4] (8 bits). + M = return length in cmnd[8] (8 bits). + G = return length in cmnd[3] and cmnd[4] (16 bits) + H = return length in cmnd[7] and cmnd[8] (16 bits) + I = return length in cmnd[8] and cmnd[9] (16 bits) + C = return length in cmnd[2] to cmnd[5] (32 bits) + D = return length in cmnd[6] to cmnd[9] (32 bits) + B = return length in blocksize so we use buff_len + R = return length in cmnd[2] to cmnd[4] (24 bits) + S = return length in cmnd[3] to cmnd[5] (24 bits) + T = return length in cmnd[6] to cmnd[8] (24 bits) + U = return length in cmnd[7] to cmnd[9] (24 bits) + 0-9 = fixed return length + V = 20 bytes + W = 24 bytes + Z = return length is mode dependant or not in command, use buff_len + */ + + static char *lengths = + + /* 0123456789ABCDEF 0123456789ABCDEF */ + + "00XLZ6XZBXBBXXXB" "00LBBLG0R0L0GG0X" /* 00-1F */ + "XXXXT8XXB4B0BBBB" "ZZZ0B00HCSSZTBHH" /* 20-3F */ + "M0HHB0X000H0HH0X" "XHH00HXX0TH0H0XX" /* 40-5F */ + "XXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXX" /* 60-7F */ + "XXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXX" /* 80-9F */ + "X0XXX00XB0BXBXBB" "ZZZ0XUIDU000XHBX" /* A0-BF */ + "XXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXX" /* C0-DF */ + "XDXXXXXXXXXXXXXX" "XXW00HXXXXXXXXXX"; /* E0-FF */ + + /* Commands checked in table: + + CHANGE_DEFINITION 40 + COMPARE 39 + COPY 18 + COPY_AND_VERIFY 3a + ERASE 19 + ERASE_10 2c + ERASE_12 ac + EXCHANGE_MEDIUM a6 + FORMAT_UNIT 04 + GET_DATA_BUFFER_STATUS 34 + GET_MESSAGE_10 28 + GET_MESSAGE_12 a8 + GET_WINDOW 25 !!! Has more data than READ_CAPACITY, need to fix table + INITIALIZE_ELEMENT_STATUS 07 !!! REASSIGN_BLOCKS luckily uses buff_len + INQUIRY 12 + LOAD_UNLOAD 1b + LOCATE 2b + LOCK_UNLOCK_CACHE 36 + LOG_SELECT 4c + LOG_SENSE 4d + MEDIUM_SCAN 38 !!! This was M + MODE_SELECT6 15 + MODE_SELECT_10 55 + MODE_SENSE_6 1a + MODE_SENSE_10 5a + MOVE_MEDIUM a5 + OBJECT_POSITION 31 !!! Same as SEARCH_DATA_EQUAL + PAUSE_RESUME 4b + PLAY_AUDIO_10 45 + PLAY_AUDIO_12 a5 + PLAY_AUDIO_MSF 47 + PLAY_AUDIO_TRACK_INDEX 48 + PLAY_AUDIO_TRACK_RELATIVE_10 49 + PLAY_AUDIO_TRACK_RELATIVE_12 a9 + POSITION_TO_ELEMENT 2b + PRE-FETCH 34 + PREVENT_ALLOW_MEDIUM_REMOVAL 1e + PRINT 0a !!! Same as WRITE_6 but is always in bytes + READ_6 08 + READ_10 28 + READ_12 a8 + READ_BLOCK_LIMITS 05 + READ_BUFFER 3c + READ_CAPACITY 25 + READ_CDROM_CAPACITY 25 + READ_DEFECT_DATA 37 + READ_DEFECT_DATA_12 b7 + READ_ELEMENT_STATUS b8 !!! Think this is in bytes + READ_GENERATION 29 !!! Could also be M? + READ_HEADER 44 !!! This was L + READ_LONG 3e + READ_POSITION 34 !!! This should be V but conflicts with PRE-FETCH + READ_REVERSE 0f + READ_SUB-CHANNEL 42 !!! Is this in bytes? + READ_TOC 43 !!! Is this in bytes? + READ_UPDATED_BLOCK 2d + REASSIGN_BLOCKS 07 + RECEIVE 08 !!! Same as READ_6 probably in bytes though + RECEIVE_DIAGNOSTIC_RESULTS 1c + RECOVER_BUFFERED_DATA 14 !!! For PRINTERs this is bytes + RELEASE_UNIT 17 + REQUEST_SENSE 03 + REQUEST_VOLUME_ELEMENT_ADDRESS b5 !!! Think this is in bytes + RESERVE_UNIT 16 + REWIND 01 + REZERO_UNIT 01 + SCAN 1b !!! Conflicts with various commands, should be L + SEARCH_DATA_EQUAL 31 + SEARCH_DATA_EQUAL_12 b1 + SEARCH_DATA_LOW 30 + SEARCH_DATA_LOW_12 b0 + SEARCH_DATA_HIGH 32 + SEARCH_DATA_HIGH_12 b2 + SEEK_6 0b !!! Conflicts with SLEW_AND_PRINT + SEEK_10 2b + SEND 0a !!! Same as WRITE_6, probably in bytes though + SEND 2a !!! Similar to WRITE_10 but for scanners + SEND_DIAGNOSTIC 1d + SEND_MESSAGE_6 0a !!! Same as WRITE_6 - is in bytes + SEND_MESSAGE_10 2a !!! Same as WRITE_10 - is in bytes + SEND_MESSAGE_12 aa !!! Same as WRITE_12 - is in bytes + SEND_VOLUME_TAG b6 !!! Think this is in bytes + SET_LIMITS 33 + SET_LIMITS_12 b3 + SET_WINDOW 24 + SLEW_AND_PRINT 0b !!! Conflicts with SEEK_6 + SPACE 11 + START_STOP_UNIT 1b + STOP_PRINT 1b + SYNCHRONIZE_BUFFER 10 + SYNCHRONIZE_CACHE 35 + TEST_UNIT_READY 00 + UPDATE_BLOCK 3d + VERIFY 13 + VERIFY 2f + VERIFY_12 af + WRITE_6 0a + WRITE_10 2a + WRITE_12 aa + WRITE_AND_VERIFY 2e + WRITE_AND_VERIFY_12 ae + WRITE_BUFFER 3b + WRITE_FILEMARKS 10 + WRITE_LONG 3f + WRITE_SAME 41 + */ + + /* Not sure this is right as an INQUIRY can contain nonstandard info */ + if (srb->cmnd[0] == INQUIRY) + srb->cmnd[4] = 36; + + if (srb->sc_data_direction == SCSI_DATA_WRITE) { + doDefault = 1; + } + else + switch (lengths[srb->cmnd[0]]) { + case 'L': + len = srb->cmnd[4]; + break; + + case 'M': + len = srb->cmnd[8]; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + len = lengths[srb->cmnd[0]]-'0'; + break; + + case 'G': + len = (((unsigned int)srb->cmnd[3])<<8) | + srb->cmnd[4]; + break; + + case 'H': + len = (((unsigned int)srb->cmnd[7])<<8) | + srb->cmnd[8]; + break; + + case 'I': + len = (((unsigned int)srb->cmnd[8])<<8) | + srb->cmnd[9]; + break; + + case 'R': + len = (((unsigned int)srb->cmnd[2])<<16) | + (((unsigned int)srb->cmnd[3])<<8) | + srb->cmnd[4]; + break; + + case 'S': + len = (((unsigned int)srb->cmnd[3])<<16) | + (((unsigned int)srb->cmnd[4])<<8) | + srb->cmnd[5]; + break; + + case 'T': + len = (((unsigned int)srb->cmnd[6])<<16) | + (((unsigned int)srb->cmnd[7])<<8) | + srb->cmnd[8]; + break; + + case 'U': + len = (((unsigned int)srb->cmnd[7])<<16) | + (((unsigned int)srb->cmnd[8])<<8) | + srb->cmnd[9]; + break; + + case 'C': + len = (((unsigned int)srb->cmnd[2])<<24) | + (((unsigned int)srb->cmnd[3])<<16) | + (((unsigned int)srb->cmnd[4])<<8) | + srb->cmnd[5]; + break; + + case 'D': + len = (((unsigned int)srb->cmnd[6])<<24) | + (((unsigned int)srb->cmnd[7])<<16) | + (((unsigned int)srb->cmnd[8])<<8) | + srb->cmnd[9]; + break; + + case 'V': + len = 20; + break; + + case 'W': + len = 24; + break; + + case 'B': + /* Use buffer size due to different block sizes */ + doDefault = 1; + break; + + case 'X': + US_DEBUGP("Error: UNSUPPORTED COMMAND %02X\n", + srb->cmnd[0]); + doDefault = 1; + break; + + case 'Z': + /* Use buffer size due to mode dependence */ + doDefault = 1; + break; + + default: + US_DEBUGP("Error: COMMAND %02X out of range or table inconsistent (%c).\n", + srb->cmnd[0], lengths[srb->cmnd[0]] ); + doDefault = 1; + } + + if ( doDefault == 1 ) { + /* Are we going to scatter gather? */ + if (srb->use_sg) { + /* Add up the sizes of all the sg segments */ + sg = (struct scatterlist *) srb->request_buffer; + for (i = 0; i < srb->use_sg; i++) + total += sg[i].length; + len = total; + } + else + /* Just return the length of the buffer */ + len = srb->request_bufflen; + } + + return len; +} + +/* This is a version of usb_clear_halt() that doesn't read the status from + * the device -- this is because some devices crash their internal firmware + * when the status is requested after a halt + */ +static int clear_halt(struct usb_device *dev, int pipe) +{ + int result; + int endp = usb_pipeendpoint(pipe) | (usb_pipein(pipe) << 7); + + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, + endp, NULL, 0, HZ * 3); + + /* this is a failure case */ + if (result < 0) + return result; + + /* reset the toggles and endpoint flags */ + usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0); + + return 0; +} + +/*********************************************************************** * Data transfer routines ***********************************************************************/ @@ -217,7 +565,7 @@ static int us_transfer_partial(struct us_data *us, char *buf, int length) /* if we stall, we need to clear it before we go on */ if (result == -EPIPE) { US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); - usb_clear_halt(us->pusb_dev, pipe); + clear_halt(us->pusb_dev, pipe); } /* did we send all the data? */ @@ -287,44 +635,6 @@ static void us_transfer(Scsi_Cmnd *srb, struct us_data* us) srb->result = result; } -/* Calculate the length of the data transfer (not the command) for any - * given SCSI command - */ -static unsigned int us_transfer_length(Scsi_Cmnd *srb, struct us_data *us) -{ - int i; - unsigned int total = 0; - struct scatterlist *sg; - - /* support those devices which need the length calculated - * differently - */ - if (us->flags & US_FL_ALT_LENGTH) { - if (srb->cmnd[0] == INQUIRY) { - srb->cmnd[4] = 36; - } - - if ((srb->cmnd[0] == INQUIRY) || (srb->cmnd[0] == MODE_SENSE)) - return srb->cmnd[4]; - - if (srb->cmnd[0] == TEST_UNIT_READY) - return 0; - } - - /* Are we going to scatter gather? */ - if (srb->use_sg) { - /* Add up the sizes of all the scatter-gather segments */ - sg = (struct scatterlist *) srb->request_buffer; - for (i = 0; i < srb->use_sg; i++) - total += sg[i].length; - - return total; - } - else - /* Just return the length of the buffer */ - return srb->request_bufflen; -} - /*********************************************************************** * Transport routines ***********************************************************************/ @@ -363,7 +673,7 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us) * of determining status on it's own, we need to auto-sense almost * every time. */ - if (us->protocol == US_PR_CB) { + if (us->protocol == US_PR_CB || us->protocol == US_PR_DPCM_USB) { US_DEBUGP("-- CB transport device requiring auto-sense\n"); need_auto_sense = 1; @@ -490,7 +800,7 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us) * This is necessary because the auto-sense for some devices always * sets byte 0 == 0x70, even if there is no error */ - if ((us->protocol == US_PR_CB) && + if ((us->protocol == US_PR_CB || us->protocol == US_PR_DPCM_USB) && (result == USB_STOR_TRANSPORT_GOOD) && ((srb->sense_buffer[2] & 0xf) == 0x0)) srb->sense_buffer[0] = 0x0; @@ -548,10 +858,10 @@ int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us) /* STALL must be cleared when they are detected */ if (result == -EPIPE) { US_DEBUGP("-- Stall on control pipe. Clearing\n"); - result = usb_clear_halt(us->pusb_dev, - usb_sndctrlpipe(us->pusb_dev, - 0)); - US_DEBUGP("-- usb_clear_halt() returns %d\n", result); + result = clear_halt(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev, + 0)); + US_DEBUGP("-- clear_halt() returns %d\n", result); return USB_STOR_TRANSPORT_FAILED; } @@ -652,10 +962,10 @@ int usb_stor_CB_transport(Scsi_Cmnd *srb, struct us_data *us) /* a stall is a fatal condition from the device */ if (result == -EPIPE) { US_DEBUGP("-- Stall on control pipe. Clearing\n"); - result = usb_clear_halt(us->pusb_dev, - usb_sndctrlpipe(us->pusb_dev, - 0)); - US_DEBUGP("-- usb_clear_halt() returns %d\n", result); + result = clear_halt(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev, + 0)); + US_DEBUGP("-- clear_halt() returns %d\n", result); return USB_STOR_TRANSPORT_FAILED; } @@ -710,7 +1020,7 @@ int usb_stor_Bulk_max_lun(struct us_data *us) /* if we get a STALL, clear the stall */ if (result == -EPIPE) { US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); - usb_clear_halt(us->pusb_dev, pipe); + clear_halt(us->pusb_dev, pipe); } /* return the default -- no LUNs */ @@ -757,7 +1067,7 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) /* if we stall, we need to clear it before we go on */ if (result == -EPIPE) { US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); - usb_clear_halt(us->pusb_dev, pipe); + clear_halt(us->pusb_dev, pipe); } else if (result) { /* unknown error -- we've got a problem */ return USB_STOR_TRANSPORT_ERROR; @@ -796,7 +1106,7 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) /* did the attempt to read the CSW fail? */ if (result == -EPIPE) { US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); - usb_clear_halt(us->pusb_dev, pipe); + clear_halt(us->pusb_dev, pipe); /* get the status again */ US_DEBUGP("Attempting to get CSW (2nd try)...\n"); @@ -810,7 +1120,7 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) /* if it fails again, we need a reset and return an error*/ if (result == -EPIPE) { US_DEBUGP("clearing halt for pipe 0x%x\n", pipe); - usb_clear_halt(us->pusb_dev, pipe); + clear_halt(us->pusb_dev, pipe); return USB_STOR_TRANSPORT_ERROR; } } @@ -877,10 +1187,10 @@ int usb_stor_CB_reset(struct us_data *us) schedule_timeout(HZ*6); US_DEBUGP("CB_reset: clearing endpoint halt\n"); - usb_clear_halt(us->pusb_dev, - usb_rcvbulkpipe(us->pusb_dev, us->ep_in)); - usb_clear_halt(us->pusb_dev, - usb_rcvbulkpipe(us->pusb_dev, us->ep_out)); + clear_halt(us->pusb_dev, + usb_rcvbulkpipe(us->pusb_dev, us->ep_in)); + clear_halt(us->pusb_dev, + usb_rcvbulkpipe(us->pusb_dev, us->ep_out)); US_DEBUGP("CB_reset done\n"); return 0; @@ -904,14 +1214,13 @@ int usb_stor_Bulk_reset(struct us_data *us) if (result < 0) US_DEBUGP("Bulk hard reset failed %d\n", result); - usb_clear_halt(us->pusb_dev, - usb_rcvbulkpipe(us->pusb_dev, us->ep_in)); - usb_clear_halt(us->pusb_dev, - usb_sndbulkpipe(us->pusb_dev, us->ep_out)); + clear_halt(us->pusb_dev, + usb_rcvbulkpipe(us->pusb_dev, us->ep_in)); + clear_halt(us->pusb_dev, + usb_sndbulkpipe(us->pusb_dev, us->ep_out)); /* long wait for reset */ schedule_timeout(HZ*6); return result; } - diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index 9d0993c1e..4615569f3 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -1,7 +1,7 @@ /* Driver for USB Mass Storage compliant devices * Transport Functions Header File * - * $Id: transport.h,v 1.6 2000/07/27 14:42:43 groovyjava Exp $ + * $Id: transport.h,v 1.8 2000/08/08 01:23:55 webbb Exp $ * * Current development and maintainance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) @@ -58,6 +58,7 @@ #define US_PR_EUSB_SDDR09 0x81 /* SCM-SCSI bridge for SDDR-09 */ #endif +#define US_PR_DPCM_USB 0xf0 /* Combination CB/SDDR09 */ /* * Bulk only data structures @@ -133,4 +134,6 @@ extern int usb_stor_Bulk_reset(struct us_data*); void usb_stor_invoke_transport(Scsi_Cmnd *, struct us_data *); +extern int dpcm_transport(Scsi_Cmnd *srb, struct us_data *us); + #endif diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index fc70bc72a..50ebddd6f 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -1,6 +1,6 @@ /* Driver for USB Mass Storage compliant devices * - * $Id: usb.c,v 1.16 2000/08/01 22:01:19 mdharm Exp $ + * $Id: usb.c,v 1.23 2000/08/08 20:46:45 mdharm Exp $ * * Current development and maintainance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) @@ -44,7 +44,6 @@ */ #include <linux/config.h> - #include "usb.h" #include "scsiglue.h" #include "transport.h" @@ -56,6 +55,9 @@ #ifdef CONFIG_USB_STORAGE_SDDR09 #include "sddr09.h" #endif +#ifdef CONFIG_USB_STORAGE_DPCM +#include "dpcm.h" +#endif #include <linux/module.h> #include <linux/sched.h> @@ -63,6 +65,10 @@ #include <linux/init.h> #include <linux/malloc.h> +/* Some informational data */ +MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>"); +MODULE_DESCRIPTION("USB Mass Storage driver for Linux"); + /* * Per device data */ @@ -91,6 +97,58 @@ static struct usb_driver storage_driver = { disconnect: storage_disconnect, }; +/* + * fill_inquiry_response takes an unsigned char array (which must + * be at least 36 characters) and populates the vendor name, + * product name, and revision fields. Then the array is copied + * into the SCSI command's response buffer (oddly enough + * called request_buffer). data_len contains the length of the + * data array, which again must be at least 36. + */ + +void fill_inquiry_response(struct us_data *us, unsigned char *data, + unsigned int data_len) { + + int i; + struct scatterlist *sg; + int len = + us->srb->request_bufflen > data_len ? data_len : + us->srb->request_bufflen; + int transferred; + int amt; + + if (data_len<36) // You lose. + return; + + memcpy(data+8, us->unusual_dev->vendorName, + strlen(us->unusual_dev->vendorName) > 8 ? 8 : + strlen(us->unusual_dev->vendorName)); + memcpy(data+16, us->unusual_dev->productName, + strlen(us->unusual_dev->productName) > 16 ? 16 : + strlen(us->unusual_dev->productName)); + data[32] = 0x30 + ((us->pusb_dev->descriptor.bcdDevice>>12) & 0x0F); + data[33] = 0x30 + ((us->pusb_dev->descriptor.bcdDevice>>8) & 0x0F); + data[34] = 0x30 + ((us->pusb_dev->descriptor.bcdDevice>>4) & 0x0F); + data[35] = 0x30 + ((us->pusb_dev->descriptor.bcdDevice) & 0x0F); + + if (us->srb->use_sg) { + sg = (struct scatterlist *)us->srb->request_buffer; + for (i=0; i<us->srb->use_sg; i++) + memset(sg[i].address, 0, sg[i].length); + for (i=0, transferred=0; + i<us->srb->use_sg && transferred < len; + i++) { + amt = sg[i].length > len-transferred ? + len-transferred : sg[i].length; + memcpy(sg[i].address, data+transferred, amt); + transferred -= amt; + } + } else { + memset(us->srb->request_buffer, 0, us->srb->request_bufflen); + memcpy(us->srb->request_buffer, data, len); + } +} + static int usb_stor_control_thread(void * __us) { wait_queue_t wait; @@ -176,7 +234,7 @@ static int usb_stor_control_thread(void * __us) us->srb = NULL; break; } - + /* lock the device pointers */ down(&(us->dev_semaphore)); @@ -246,50 +304,140 @@ static int usb_stor_control_thread(void * __us) } /* This is the list of devices we recognize, along with their flag data */ + +/* The vendor name should be kept at eight characters or less, and + * the product name should be kept at 16 characters or less. If a device + * has the US_FL_DUMMY_INQUIRY flag, then the vendor and product names + * normally generated by a device thorugh the INQUIRY response will be + * taken from this list, and this is the reason for the above size + * restriction. However, if the flag is not present, then you + * are free to use as many characters as you like. + */ + static struct us_unusual_dev us_unusual_dev_list[] = { - { 0x03f0, 0x0107, 0x0200, 0x0200, "HP USB CD-Writer Plus", - US_SC_8070, US_PR_CB, 0}, + + { 0x03f0, 0x0107, 0x0200, 0x0200, + "HP", + "CD-Writer+", + US_SC_8070, US_PR_CB, NULL, + 0}, + #ifdef CONFIG_USB_STORAGE_HP8200e - { 0x03f0, 0x0207, 0x0001, 0x0001, "HP USB CD-Writer Plus 8200e", - US_SC_8070, US_PR_SCM_ATAPI, - US_FL_ALT_LENGTH | US_FL_NEED_INIT | US_FL_SINGLE_LUN}, + { 0x03f0, 0x0207, 0x0001, 0x0001, + "HP", + "CD-Writer+ 8200e", + US_SC_8070, US_PR_SCM_ATAPI, init_8200e, + US_FL_SINGLE_LUN}, #endif - { 0x04e6, 0x0001, 0x0200, 0x0200, "Matshita LS-120", - US_SC_8020, US_PR_CB, US_FL_SINGLE_LUN}, - { 0x04e6, 0x0002, 0x0100, 0x0100, "Shuttle eUSCSI Bridge", - US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, - { 0x04e6, 0x0006, 0x0100, 0x0100, "Shuttle eUSB MMC Adapter", - US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN}, - { 0x054c, 0x0010, 0x0210, 0x0210, "Sony DSC-S30/S70", - US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN | US_FL_START_STOP | - US_FL_MODE_XLATE | US_FL_ALT_LENGTH | US_FL_ALT_LENGTH}, - { 0x054c, 0x002d, 0x0100, 0x0100, "Sony Memorystick MSAC-US1", - US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN | US_FL_START_STOP | - US_FL_MODE_XLATE | US_FL_ALT_LENGTH}, - { 0x057b, 0x0000, 0x0000, 0x0299, "Y-E Data Flashbuster-U", - US_SC_UFI, US_PR_CB, US_FL_SINGLE_LUN}, - { 0x057b, 0x0000, 0x0300, 0x9999, "Y-E Data Flashbuster-U", - US_SC_UFI, US_PR_CBI, US_FL_SINGLE_LUN}, - { 0x0693, 0x0002, 0x0100, 0x0100, "Hagiwara FlashGate SmartMedia", - US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, - { 0x0781, 0x0001, 0x0200, 0x0200, "Sandisk ImageMate (SDDR-05a)", - US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN | US_FL_START_STOP}, -#ifdef CONFIG_USB_STORAGE_SDDR09 - { 0x0781, 0x0200, 0x0100, 0x0100, "Sandisk ImageMate (SDDR-09)", - US_SC_SCSI, US_PR_EUSB_SDDR09, + + { 0x04e6, 0x0001, 0x0200, 0x0200, + "Matshita", + "LS-120", + US_SC_8020, US_PR_CB, NULL, + US_FL_SINGLE_LUN}, + + { 0x04e6, 0x0002, 0x0100, 0x0100, + "Shuttle", + "eUSCSI Bridge", + US_SC_SCSI, US_PR_BULK, NULL, + 0 }, + + { 0x04e6, 0x0006, 0x0100, 0x0100, + "Shuttle", + "eUSB MMC Adapter", + US_SC_SCSI, US_PR_CB, NULL, + US_FL_SINGLE_LUN}, + + { 0x054c, 0x0010, 0x0210, 0x0210, + "Sony", + "DSC-S30/S70", + US_SC_SCSI, US_PR_CB, NULL, + US_FL_SINGLE_LUN | US_FL_START_STOP | US_FL_MODE_XLATE }, + + { 0x054c, 0x002d, 0x0100, 0x0100, + "Sony", + "Memorystick MSAC-US1", + US_SC_SCSI, US_PR_CB, NULL, + US_FL_SINGLE_LUN | US_FL_START_STOP | US_FL_MODE_XLATE }, + + { 0x057b, 0x0000, 0x0000, 0x0299, + "Y-E Data", + "Flashbuster-U", + US_SC_UFI, US_PR_CB, NULL, + US_FL_SINGLE_LUN}, + + { 0x057b, 0x0000, 0x0300, 0x9999, + "Y-E Data", + "Flashbuster-U", + US_SC_UFI, US_PR_CBI, NULL, + US_FL_SINGLE_LUN}, + + { 0x0693, 0x0002, 0x0100, 0x0100, + "Hagiwara", + "FlashGate SmartMedia", + US_SC_SCSI, US_PR_BULK, NULL, + 0 }, + + { 0x0781, 0x0001, 0x0200, 0x0200, + "Sandisk", + "ImageMate SDDR05a", + US_SC_SCSI, US_PR_CB, NULL, US_FL_SINGLE_LUN | US_FL_START_STOP}, + +#ifdef CONFIG_USB_STORAGE_SDDR09 + { 0x0781, 0x0200, 0x0100, 0x0100, + "Sandisk", + "ImageMate SDDR09", + US_SC_SCSI, US_PR_EUSB_SDDR09, NULL, + US_FL_SINGLE_LUN | US_FL_START_STOP }, +#endif + + { 0x0781, 0x0002, 0x0009, 0x0009, + "Sandisk", + "ImageMate SDDR31", + US_SC_SCSI, US_PR_BULK, NULL, + US_FL_IGNORE_SER}, + + { 0x07af, 0x0004, 0x0100, 0x0100, + "Microtech", + "USB-SCSI-DB25", + US_SC_SCSI, US_PR_BULK, NULL, + 0 }, + + { 0x059f, 0xa601, 0x0200, 0x0200, + "LaCie", + "USB Hard Disk", + US_SC_RBC, US_PR_CB, NULL, + 0 }, + +#ifdef CONFIG_USB_STORAGE_DPCM + { 0x07af, 0x0006, 0x0100, 0x0100, + "Microtech", + "CameraMate (DPCM_USB)", + US_SC_SCSI, US_PR_DPCM_USB, NULL, + US_FL_START_STOP }, #endif - { 0x0781, 0x0002, 0x0009, 0x0009, "Sandisk Imagemate (SDDR-31)", - US_SC_SCSI, US_PR_BULK, US_FL_IGNORE_SER}, - { 0x07af, 0x0004, 0x0100, 0x0100, "Microtech USB-SCSI-DB25", - US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, - { 0x07af, 0x0005, 0x0100, 0x0100, "Microtech USB-SCSI-HD50", - US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, - { 0x05ab, 0x0031, 0x0100, 0x0100, "In-System USB/IDE Bridge", - US_SC_8070, US_PR_BULK, US_FL_ALT_LENGTH}, - { 0x0693, 0x0005, 0x0100, 0x0100, "Hagiwara Flashgate", - US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, - { 0 }}; + + { 0x07af, 0x0005, 0x0100, 0x0100, + "Microtech", + "USB-SCSI-HD50", + US_SC_SCSI, US_PR_BULK, NULL, + 0 }, + + { 0x05ab, 0x0031, 0x0100, 0x0100, + "In-System", + "USB/IDE Bridge", + US_SC_8070, US_PR_BULK, NULL, + 0 }, + + { 0x0693, 0x0005, 0x0100, 0x0100, + "Hagiwara", + "Flashgate", + US_SC_SCSI, US_PR_BULK, NULL, + 0 }, + + { 0 } +}; /* Search our ususual device list, based on vendor/product combinations * to see if we can support this device. Returns a pointer to a structure @@ -319,7 +467,8 @@ static struct us_unusual_dev* us_find_dev(u16 idVendor, u16 idProduct, } /* otherwise, we found one! */ - US_DEBUGP("-- found matching device: %s\n", ptr->name); + US_DEBUGP("-- found matching device: %s %s\n", ptr->vendorName, + ptr->productName); return ptr; } @@ -546,15 +695,15 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) USB_ENDPOINT_NUMBER_MASK; ss->ep_int = ep_int; - /* Reset the device's NEED_INIT flag if it needs to be - initialized with a magic sequence */ - - if (flags & US_FL_NEED_INIT) - ss->flags |= US_FL_NEED_INIT; - /* allocate an IRQ callback if one is needed */ if ((ss->protocol == US_PR_CBI) && usb_stor_allocate_irq(ss)) return NULL; + + /* Re-Initialize the device if it needs it */ + + if (unusual_dev && unusual_dev->initFunction) + (*unusual_dev->initFunction)(ss); + } else { /* New device -- allocate memory and initialize */ US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid)); @@ -586,6 +735,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) ss->subclass = subclass; ss->protocol = protocol; ss->flags = flags; + ss->unusual_dev = unusual_dev; /* copy over the endpoint data */ if (ep_in) @@ -604,10 +754,22 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) strncpy(ss->vendor, mf, USB_STOR_STRING_LEN); strncpy(ss->product, prod, USB_STOR_STRING_LEN); strncpy(ss->serial, serial, USB_STOR_STRING_LEN); - if (strlen(ss->vendor) == 0) - strncpy(ss->vendor, "Unknown", USB_STOR_STRING_LEN); - if (strlen(ss->product) == 0) - strncpy(ss->product, "Unknown", USB_STOR_STRING_LEN); + if (strlen(ss->vendor) == 0) { + if (unusual_dev) + strncpy(ss->vendor, unusual_dev->vendorName, + USB_STOR_STRING_LEN); + else + strncpy(ss->vendor, "Unknown", + USB_STOR_STRING_LEN); + } + if (strlen(ss->product) == 0) { + if (unusual_dev) + strncpy(ss->product, unusual_dev->productName, + USB_STOR_STRING_LEN); + else + strncpy(ss->product, "Unknown", + USB_STOR_STRING_LEN); + } if (strlen(ss->serial) == 0) strncpy(ss->serial, "None", USB_STOR_STRING_LEN); @@ -657,6 +819,15 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) ss->max_lun = 1; break; #endif + +#ifdef CONFIG_USB_STORAGE_DPCM + case US_PR_DPCM_USB: + ss->transport_name = "Control/Bulk-EUSB/SDDR09"; + ss->transport = dpcm_transport; + ss->transport_reset = usb_stor_CB_reset; + ss->max_lun = 1; + break; +#endif default: ss->transport_name = "Unknown"; @@ -734,6 +905,11 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) * we to do it? */ (struct us_data *)ss->htmplt.proc_dir = ss; + + /* Just before we start our control thread, initialize + * the device if it needs initialization */ + if (unusual_dev && unusual_dev->initFunction) + (*unusual_dev->initFunction)(ss); /* start up our control thread */ ss->pid = kernel_thread(usb_stor_control_thread, ss, @@ -878,6 +1054,3 @@ void __exit usb_stor_exit(void) module_init(usb_stor_init) ; module_exit(usb_stor_exit) ; - -MODULE_AUTHOR("Michael Gee <michael@linuxspecific.com>, David L. Brown, Jr. <usb-storage@davidb.org>, Matthew Dharm <mdharm-usb@one-eyed-alien.net>"); -MODULE_DESCRIPTION("USB Mass Storage driver"); diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index b376d39ee..435ad9777 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -1,7 +1,7 @@ /* Driver for USB Mass Storage compliant devices * Main Header File * - * $Id: usb.h,v 1.4 2000/07/28 20:14:49 groovyjava Exp $ + * $Id: usb.h,v 1.7 2000/08/15 00:06:38 mdharm Exp $ * * Current development and maintainance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) @@ -77,6 +77,8 @@ static inline void make_guid( __u32 *pg, __u16 vendor, __u16 product, char *seri } } +struct us_data; + /* * Unusual device list definitions */ @@ -89,9 +91,11 @@ struct us_unusual_dev { __u16 bcdDeviceMax; /* the list specifies these parameters */ - const char* name; + const char* vendorName; + const char* productName; __u8 useProtocol; __u8 useTransport; + int (*initFunction)(struct us_data *); unsigned int flags; }; @@ -100,15 +104,11 @@ struct us_unusual_dev { #define US_FL_MODE_XLATE 0x00000002 /* translate _6 to _10 comands for Win/MacOS compatibility */ #define US_FL_START_STOP 0x00000004 /* ignore START_STOP commands */ -#define US_FL_ALT_LENGTH 0x00000008 /* use the alternate algorithm for - us_transfer_length() */ #define US_FL_IGNORE_SER 0x00000010 /* Ignore the serial number given */ -#define US_FL_NEED_INIT 0x00000020 /* Device needs initialization */ +#define US_FL_SCM_MULT_TARG 0x00000020 /* supports multiple targets */ #define USB_STOR_STRING_LEN 32 -struct us_data; - typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*); typedef int (*trans_reset)(struct us_data*); typedef void (*proto_cmnd)(Scsi_Cmnd*, struct us_data*); @@ -176,6 +176,7 @@ struct us_data { /* mutual exclusion structures */ struct semaphore notify; /* thread begin/end */ struct semaphore queue_exclusion; /* to protect data structs */ + struct us_unusual_dev *unusual_dev; /* If unusual device */ void *extra; /* Any extra data */ void (*extra_destructor)(void *); /* extra data destructor */ }; @@ -184,4 +185,9 @@ struct us_data { extern struct us_data *us_list; extern struct semaphore us_list_semaphore; +/* Function to fill an inquiry response. See usb.c for details */ + +extern void fill_inquiry_response(struct us_data *us, + unsigned char *data, unsigned int data_len); + #endif diff --git a/drivers/usb/usb-core.c b/drivers/usb/usb-core.c index 25d1d24cf..017a99dce 100644 --- a/drivers/usb/usb-core.c +++ b/drivers/usb/usb-core.c @@ -13,6 +13,7 @@ #include <linux/version.h> #include <linux/kernel.h> #include <linux/config.h> +#include <linux/init.h> #include <linux/usb.h> /* @@ -42,13 +43,11 @@ int dsbr100_init(void); int uhci_init(void); int ohci_hcd_init(void); -#ifdef MODULE - /* * Cleanup */ -void cleanup_module(void) +static void __exit usb_exit(void) { usb_major_cleanup(); usbdevfs_cleanup(); @@ -59,10 +58,7 @@ void cleanup_module(void) * Init */ -int init_module(void) -#else -int usb_init(void) -#endif +static int __init usb_init(void) { usb_major_init(); usbdevfs_init(); @@ -99,3 +95,6 @@ int usb_init(void) #endif return 0; } + +module_init(usb_init); +module_exit(usb_exit); diff --git a/drivers/usb/usb-ohci.c b/drivers/usb/usb-ohci.c index 367f0d3f6..316394a84 100644 --- a/drivers/usb/usb-ohci.c +++ b/drivers/usb/usb-ohci.c @@ -76,7 +76,6 @@ (OHCI_CTRL_CBSR & 0x3) \ | OHCI_CTRL_BLE | OHCI_CTRL_CLE | OHCI_CTRL_IE | OHCI_CTRL_PLE -static DECLARE_WAIT_QUEUE_HEAD (op_wakeup); static LIST_HEAD (ohci_hcd_list); static spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED; @@ -534,7 +533,6 @@ static int sohci_unlink_urb (urb_t * urb) { unsigned long flags; ohci_t * ohci; - DECLARE_WAITQUEUE (wait, current); if (!urb) /* just to be sure */ return -EINVAL; @@ -580,14 +578,18 @@ static int sohci_unlink_urb (urb_t * urb) spin_unlock_irqrestore (&usb_ed_lock, flags); if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) { + DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup); + DECLARE_WAITQUEUE (wait, current); + usb_dec_dev_use (urb->dev); - add_wait_queue (&op_wakeup, &wait); - current->state = TASK_UNINTERRUPTIBLE; /* wait until all TDs are deleted */ - if (!schedule_timeout (HZ / 10)) - err("unlink URB timeout!"); - remove_wait_queue (&op_wakeup, &wait); + add_wait_queue (&unlink_wakeup, &wait); + urb_priv->wait = &unlink_wakeup; + current->state = TASK_UNINTERRUPTIBLE; + schedule (); + remove_wait_queue (&unlink_wakeup, &wait); urb->status = -ENOENT; + urb_priv->wait = 0; } else { /* usb_dec_dev_use done in dl_del_list() */ urb->status = -EINPROGRESS; @@ -634,7 +636,6 @@ static int sohci_free_dev (struct usb_device * usb_dev) unsigned long flags; int i, cnt = 0; ed_t * ed; - DECLARE_WAITQUEUE (wait, current); struct ohci_device * dev = usb_to_ohci (usb_dev); ohci_t * ohci = usb_dev->bus->hcpriv; @@ -643,13 +644,20 @@ static int sohci_free_dev (struct usb_device * usb_dev) if (usb_dev->devnum >= 0) { - /* delete all TDs of all EDs */ + /* driver disconnects should have unlinked all urbs + * (freeing all the TDs, unlinking EDs) but we need + * to defend against bugs that prevent that. + */ spin_lock_irqsave (&usb_ed_lock, flags); for(i = 0; i < NUM_EDS; i++) { ed = &(dev->ed[i]); if (ed->state != ED_NEW) { - if (ed->state == ED_OPER) + if (ed->state == ED_OPER) { + /* driver on that interface didn't unlink an urb */ + dbg ("driver usb-%s dev %d ed 0x%x unfreed URB", + ohci->ohci_dev->slot_name, usb_dev->devnum, i); ep_unlink (ohci, ed); + } ep_rm_ed (usb_dev, ed); ed->state = ED_DEL; cnt++; @@ -657,6 +665,9 @@ static int sohci_free_dev (struct usb_device * usb_dev) } spin_unlock_irqrestore (&usb_ed_lock, flags); + /* if the controller is running, tds for those unlinked + * urbs get freed by dl_del_list at the next SF interrupt + */ if (cnt > 0) { if (ohci->disabled) { @@ -670,15 +681,21 @@ static int sohci_free_dev (struct usb_device * usb_dev) warn ("TD leak, %d", cnt); } else if (!in_interrupt ()) { + DECLARE_WAIT_QUEUE_HEAD (freedev_wakeup); + DECLARE_WAITQUEUE (wait, current); + /* SF interrupt handler calls dl_del_list */ - add_wait_queue (&op_wakeup, &wait); + add_wait_queue (&freedev_wakeup, &wait); + dev->wait = &freedev_wakeup; current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout (HZ / 10); - remove_wait_queue (&op_wakeup, &wait); + schedule (); + remove_wait_queue (&freedev_wakeup, &wait); } else { - /* drivers mustn't expect this to work */ - err ("can't free tds, interrupt context"); + /* likely some interface's driver has a refcount bug */ + err ("bus %s devnum %d deletion in interrupt", + ohci->ohci_dev->slot_name, usb_dev->devnum); + BUG (); } } } @@ -907,35 +924,34 @@ static int ep_unlink (ohci_t * ohci, ed_t * ed) #endif break; - case ISO: - if (ohci->ed_isotail == ed) - ohci->ed_isotail = ed->ed_prev; + case ISO: + if (ohci->ed_isotail == ed) + ohci->ed_isotail = ed->ed_prev; if (ed->hwNextED != 0) - ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev; - + ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev; + if (ed->ed_prev != NULL) { ed->ed_prev->hwNextED = ed->hwNextED; } else { - for (i = 0; i < 32; i += inter) { - inter = 1; + for (i = 0; i < 32; i++) { for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i)]); - *ed_p != 0; - ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) { - inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval); - if(((ed_t *) bus_to_virt (le32_to_cpup (ed_p))) == ed) { - *ed_p = ed->hwNextED; - break; - } - } + *ed_p != 0; + ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) { + // inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval); + if(((ed_t *) bus_to_virt (le32_to_cpup (ed_p))) == ed) { + *ed_p = ed->hwNextED; + break; + } + } } } #ifdef DEBUG ep_print_int_eds (ohci, "UNLINK_ISO"); #endif break; - } - ed->state = ED_UNLINK; - return 0; + } + ed->state = ED_UNLINK; + return 0; } @@ -1276,28 +1292,33 @@ static void dl_del_list (ohci_t * ohci, unsigned int frame) tdINFO = le32_to_cpup (&td->hwINFO); if (TD_CC_GET (tdINFO) < 0xE) dl_transfer_length (td); *td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3)); - if(++ (urb_priv->td_cnt) == urb_priv->length) + /* URB is done; clean up */ + if (++(urb_priv->td_cnt) == urb_priv->length) { + void *condition = urb_priv->wait; + urb_rm_priv (urb); if (urb->transfer_flags & USB_ASYNC_UNLINK) { usb_dec_dev_use (urb->dev); urb->status = -ECONNRESET; urb->complete (urb); - } else { - wake_up (&op_wakeup); + } else if (condition) { + /* unblock sohci_unlink_urb */ + wake_up (condition); } + } } else { td_p = &td->hwNextTD; } - } + if (ed->state & ED_DEL) { /* set by sohci_free_dev */ struct ohci_device * dev = usb_to_ohci (ohci->dev[edINFO & 0x7F]); - OHCI_FREE (tdTailP); /* free dummy td */ + OHCI_FREE (tdTailP); /* free dummy td */ ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); ed->state = ED_NEW; /* if all eds are removed wake up sohci_free_dev */ - if (!--dev->ed_cnt) - wake_up (&op_wakeup); + if (!--dev->ed_cnt && dev->wait) + wake_up (dev->wait); } else { ed->state &= ~ED_URB_DEL; @@ -1839,6 +1860,7 @@ static int hc_start (ohci_t * ohci) /* start controller operations */ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + ohci->disabled = 0; writel (ohci->hc_control, &ohci->regs->control); /* Choose the interrupts we care about now, others later on demand */ @@ -1850,25 +1872,28 @@ static int hc_start (ohci_t * ohci) writel ((readl(&ohci->regs->roothub.a) | RH_A_NPS) & ~RH_A_PSM, &ohci->regs->roothub.a); writel (RH_HS_LPSC, &ohci->regs->roothub.status); +#endif /* OHCI_USE_NPS */ + // POTPGT delay is bits 24-31, in 2 ms units. mdelay ((readl(&ohci->regs->roothub.a) >> 23) & 0x1fe); -#endif /* OHCI_USE_NPS */ /* connect the virtual root hub */ ohci->rh.devnum = 0; usb_dev = usb_alloc_dev (NULL, ohci->bus); - if (!usb_dev) + if (!usb_dev) { + ohci->disabled = 1; return -ENOMEM; + } dev = usb_to_ohci (usb_dev); ohci->bus->root_hub = usb_dev; usb_connect (usb_dev); if (usb_new_device (usb_dev) != 0) { usb_free_dev (usb_dev); + ohci->disabled = 1; return -ENODEV; } - ohci->disabled = 0; return 0; } @@ -1936,7 +1961,7 @@ static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r) /* allocate OHCI */ -static ohci_t * __devinit hc_alloc_ohci (void * mem_base) +static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base) { ohci_t * ohci; struct usb_bus * bus; @@ -1947,9 +1972,16 @@ static ohci_t * __devinit hc_alloc_ohci (void * mem_base) memset (ohci, 0, sizeof (ohci_t)); + ohci->disabled = 1; ohci->irq = -1; ohci->regs = mem_base; + ohci->ohci_dev = dev; + dev->driver_data = ohci; + + INIT_LIST_HEAD (&ohci->ohci_hcd_list); + list_add (&ohci->ohci_hcd_list, &ohci_hcd_list); + bus = usb_alloc_bus (&sohci_device_operations); if (!bus) { kfree (ohci); @@ -1958,8 +1990,7 @@ static ohci_t * __devinit hc_alloc_ohci (void * mem_base) ohci->bus = bus; bus->hcpriv = (void *) ohci; - ohci->disabled = 1; - + return ohci; } @@ -2008,6 +2039,7 @@ static int __devinit hc_found_ohci (struct pci_dev *dev, int irq, void * mem_base) { ohci_t * ohci; + u8 latency, limit; char buf[8], *bufp = buf; #ifndef __sparc__ @@ -2019,15 +2051,24 @@ hc_found_ohci (struct pci_dev *dev, int irq, void * mem_base) (unsigned long) mem_base, bufp); printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name); - ohci = hc_alloc_ohci (mem_base); + ohci = hc_alloc_ohci (dev, mem_base); if (!ohci) { return -ENOMEM; } - ohci->ohci_dev = dev; - dev->driver_data = ohci; - INIT_LIST_HEAD (&ohci->ohci_hcd_list); - list_add (&ohci->ohci_hcd_list, &ohci_hcd_list); + /* bad pci latencies can contribute to overruns */ + pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency); + if (latency) { + pci_read_config_byte (dev, PCI_MAX_LAT, &limit); + if (limit && limit < latency) { + dbg ("PCI latency reduced to max %d", limit); + pci_write_config_byte (dev, PCI_LATENCY_TIMER, limit); + ohci->pci_latency = limit; + } else { + /* it might already have been reduced */ + ohci->pci_latency = latency; + } + } if (hc_reset (ohci) < 0) { hc_release_ohci (ohci); @@ -2042,7 +2083,7 @@ hc_found_ohci (struct pci_dev *dev, int irq, void * mem_base) if (request_irq (irq, hc_interrupt, SA_SHIRQ, ohci_pci_driver.name, ohci) != 0) { - err ("request interrupt %d failed", irq); + err ("request interrupt %s failed", bufp); hc_release_ohci (ohci); return -EBUSY; } @@ -2072,6 +2113,9 @@ static void hc_restart (ohci_t *ohci) int temp; int i; + if (ohci->pci_latency) + pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency); + ohci->disabled = 1; if (ohci->bus->root_hub) usb_disconnect (&ohci->bus->root_hub); @@ -2106,7 +2150,6 @@ static int __devinit ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) { unsigned long mem_resource, mem_len; - u8 latency, limit; void *mem_base; if (pci_enable_device(dev) < 0) @@ -2128,14 +2171,6 @@ ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) /* controller writes into our memory */ pci_set_master (dev); - pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency); - if (latency) { - pci_read_config_byte (dev, PCI_MAX_LAT, &limit); - if (limit && limit < latency) { - dbg ("PCI latency reduced to max %d", limit); - pci_write_config_byte (dev, PCI_LATENCY_TIMER, limit); - } - } return hc_found_ohci (dev, dev->irq, mem_base); } @@ -2157,6 +2192,9 @@ ohci_pci_remove (struct pci_dev *dev) ohci->disabled ? " (disabled)" : "", in_interrupt () ? " in interrupt" : "" ); +#ifdef DEBUG + ohci_dump (ohci, 1); +#endif /* don't wake up sleeping controllers, or block in interrupt context */ if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER || in_interrupt ()) { @@ -2209,6 +2247,14 @@ ohci_pci_resume (struct pci_dev *dev) ohci_t *ohci = (ohci_t *) dev->driver_data; int temp; + /* guard against multiple resumes */ + atomic_inc (&ohci->resume_count); + if (atomic_read (&ohci->resume_count) != 1) { + err ("concurrent PCI resumes for usb-%s", dev->slot_name); + atomic_dec (&ohci->resume_count); + return; + } + /* did we suspend, or were we powered off? */ ohci->hc_control = readl (&ohci->regs->control); temp = ohci->hc_control & OHCI_CTRL_HCFS; @@ -2253,6 +2299,9 @@ ohci_pci_resume (struct pci_dev *dev) default: warn ("odd PCI resume for usb-%s", dev->slot_name); } + + /* controller is operational, extra resumes are harmless */ + atomic_dec (&ohci->resume_count); } #endif /* CONFIG_PM */ diff --git a/drivers/usb/usb-ohci.h b/drivers/usb/usb-ohci.h index e326f5ee6..d286265e3 100644 --- a/drivers/usb/usb-ohci.h +++ b/drivers/usb/usb-ohci.h @@ -366,6 +366,7 @@ typedef struct ohci { int irq; int disabled; /* e.g. got a UE, we're hung */ + atomic_t resume_count; /* defending against multiple resumes */ struct ohci_regs * regs; /* OHCI controller's memory */ struct list_head ohci_hcd_list; /* list of all ohci_hcd */ @@ -384,7 +385,10 @@ typedef struct ohci { struct usb_bus * bus; struct usb_device * dev[128]; struct virt_root_hub rh; - struct pci_dev *ohci_dev; + + /* PCI device handle and settings */ + struct pci_dev *ohci_dev; + u8 pci_latency; } ohci_t; diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c index bd721cba2..14c6e649e 100644 --- a/drivers/usb/usb-uhci.c +++ b/drivers/usb/usb-uhci.c @@ -12,7 +12,7 @@ * (C) Copyright 1999 Johannes Erdfelt * (C) Copyright 1999 Randy Dunlap * - * $Id: usb-uhci.c,v 1.236 2000/08/02 20:28:28 acher Exp $ + * $Id: usb-uhci.c,v 1.237 2000/08/08 14:58:17 acher Exp $ */ #include <linux/config.h> @@ -48,7 +48,7 @@ /* This enables an extra UHCI slab for memory debugging */ #define DEBUG_SLAB -#define VERSTR "$Revision: 1.236 $ time " __TIME__ " " __DATE__ +#define VERSTR "$Revision: 1.237 $ time " __TIME__ " " __DATE__ #include <linux/usb.h> #include "usb-uhci.h" @@ -888,7 +888,7 @@ _static int uhci_submit_bulk_urb (urb_t *urb, urb_t *bulk_urb) data += pktsze; len -= pktsze; - last = (len == 0 && (usb_pipein(pipe) || pktsze < maxsze || (urb->transfer_flags & USB_DISABLE_SPD))); + last = (len == 0 && (usb_pipein(pipe) || pktsze < maxsze || !(urb->transfer_flags & USB_DISABLE_SPD))); if (last) td->hw.td.status |= TD_CTRL_IOC; // last one generates INT diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index 777e2d650..93fe7826c 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -1222,9 +1222,7 @@ static int usb_parse_interface(struct usb_device *dev, struct usb_interface *int int usb_parse_configuration(struct usb_device *dev, struct usb_config_descriptor *config, char *buffer) { - int i; - int retval; - int size; + int i, retval, size; struct usb_descriptor_header *header; memcpy(config, buffer, USB_DT_CONFIG_SIZE); @@ -1252,19 +1250,54 @@ int usb_parse_configuration(struct usb_device *dev, struct usb_config_descriptor size -= config->bLength; for (i = 0; i < config->bNumInterfaces; i++) { - header = (struct usb_descriptor_header *)buffer; - if ((header->bLength > size) || (header->bLength <= 2)) { - err("ran out of descriptors parsing"); - return -1; - } - - if (header->bDescriptorType != USB_DT_INTERFACE) { - warn("unexpected descriptor 0x%X", - header->bDescriptorType); + int numskipped, len; + char *begin; + + /* Skip over the rest of the Class Specific or Vendor */ + /* Specific descriptors */ + begin = buffer; + numskipped = 0; + while (size >= sizeof(struct usb_descriptor_header)) { + header = (struct usb_descriptor_header *)buffer; + + if ((header->bLength > size) || (header->bLength < 2)) { + err("invalid descriptor length of %d", header->bLength); + return -1; + } + + /* If we find another descriptor which is at or below */ + /* us in the descriptor heirarchy then we're done */ + if ((header->bDescriptorType == USB_DT_ENDPOINT) || + (header->bDescriptorType == USB_DT_INTERFACE) || + (header->bDescriptorType == USB_DT_CONFIG) || + (header->bDescriptorType == USB_DT_DEVICE)) + break; + + dbg("skipping descriptor 0x%X", header->bDescriptorType); + numskipped++; buffer += header->bLength; size -= header->bLength; - continue; + } + if (numskipped) + dbg("skipped %d class/vendor specific endpoint descriptors", numskipped); + + /* Copy any unknown descriptors into a storage area for */ + /* drivers to later parse */ + len = (int)(buffer - begin); + if (!len) { + config->extra = NULL; + config->extralen = 0; + } else { + config->extra = kmalloc(len, GFP_KERNEL); + if (!config->extra) { + err("couldn't allocate memory for config extra descriptors"); + config->extralen = 0; + return -1; + } + + memcpy(config->extra, begin, len); + config->extralen = len; } retval = usb_parse_interface(dev, config->interface + i, buffer, size); diff --git a/drivers/usb/usbkbd.c b/drivers/usb/usbkbd.c index 89cc76537..bbf4b59ac 100644 --- a/drivers/usb/usbkbd.c +++ b/drivers/usb/usbkbd.c @@ -1,5 +1,5 @@ /* - * $Id: usbkbd.c,v 1.11 2000/05/29 09:01:52 vojtech Exp $ + * $Id: usbkbd.c,v 1.16 2000/08/14 21:05:26 vojtech Exp $ * * Copyright (c) 1999-2000 Vojtech Pavlik * @@ -47,8 +47,8 @@ static unsigned char usb_kbd_keycode[256] = { 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, 72, 73, 82, 83, 86,127,116,117, 85, 89, 90, 91, 92, 93, 94, 95, 120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113, - 115,114, 0, 0, 0, 0, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 115,114, 0, 0, 0,124, 0,181,182,183,184,185,186,187,188,189, + 190,191,192,193,194,195,196,197,198, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -63,7 +63,7 @@ struct usb_kbd { unsigned char old[8]; struct urb irq, led; devrequest dr; - unsigned char leds; + unsigned char leds, newleds; char name[128]; int open; }; @@ -104,28 +104,37 @@ int usb_kbd_event(struct input_dev *dev, unsigned int type, unsigned int code, i if (type != EV_LED) return -1; - if (kbd->led.status == -EINPROGRESS) { - warn("had to kill led urb"); - usb_unlink_urb(&kbd->led); - } - kbd->leds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) | - (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) | - (!!test_bit(LED_NUML, dev->led)); + kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) | + (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) | + (!!test_bit(LED_NUML, dev->led)); + + if (kbd->led.status == -EINPROGRESS) + return 0; + if (kbd->leds == kbd->newleds) + return 0; - if (usb_submit_urb(&kbd->led)) { + kbd->leds = kbd->newleds; + if (usb_submit_urb(&kbd->led)) err("usb_submit_urb(leds) failed"); - return -1; - } return 0; } static void usb_kbd_led(struct urb *urb) { + struct usb_kbd *kbd = urb->context; + if (urb->status) warn("led urb status %d received", urb->status); + + if (kbd->leds == kbd->newleds) + return; + + kbd->leds = kbd->newleds; + if (usb_submit_urb(&kbd->led)) + err("usb_submit_urb(leds) failed"); } static int usb_kbd_open(struct input_dev *dev) |