diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/Config.in | 4 | ||||
-rw-r--r-- | drivers/usb/Makefile | 18 | ||||
-rw-r--r-- | drivers/usb/dsbr100.c | 353 | ||||
-rw-r--r-- | drivers/usb/inode.c | 1 | ||||
-rw-r--r-- | drivers/usb/ov511.c | 484 | ||||
-rw-r--r-- | drivers/usb/ov511.h | 9 | ||||
-rw-r--r-- | drivers/usb/pegasus.c | 714 | ||||
-rw-r--r-- | drivers/usb/serial/Makefile | 9 | ||||
-rw-r--r-- | drivers/usb/uhci.c | 56 | ||||
-rw-r--r-- | drivers/usb/uhci.h | 6 | ||||
-rw-r--r-- | drivers/usb/usb-core.c | 7 | ||||
-rw-r--r-- | drivers/usb/usb-storage.c | 1124 | ||||
-rw-r--r-- | drivers/usb/usb-storage.h | 24 | ||||
-rw-r--r-- | drivers/usb/usb-uhci-debug.h | 79 | ||||
-rw-r--r-- | drivers/usb/usb-uhci.c | 975 | ||||
-rw-r--r-- | drivers/usb/usb-uhci.h | 18 |
16 files changed, 2290 insertions, 1591 deletions
diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index 5f1b61d07..fbb7562dc 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -9,9 +9,6 @@ if [ ! "$CONFIG_USB" = "n" ]; then comment 'USB Controllers' dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB - if [ "$CONFIG_USB_UHCI" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool ' USB-UHCI High Bandwidth (EXPERIMENTAL)' CONFIG_USB_UHCI_HIGH_BANDWIDTH - fi if [ "$CONFIG_USB_UHCI" != "y" ]; then dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB if [ "$CONFIG_USB_UHCI_ALT" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then @@ -51,6 +48,7 @@ comment 'USB Devices' dep_tristate ' PLUSB Prolific USB-Network driver' CONFIG_USB_PLUSB $CONFIG_USB dep_tristate ' USB ADMtek Pegasus-based device support' CONFIG_USB_PEGASUS $CONFIG_USB dep_tristate ' USB Diamond Rio500 support' CONFIG_USB_RIO500 $CONFIG_USB + dep_tristate ' D-Link USB FM radio support' CONFIG_USB_DSBR $CONFIG_USB comment 'USB HID' dep_tristate ' USB Human Interface Device (HID) support' CONFIG_USB_HID $CONFIG_USB diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 7c5b02f21..35bef0e45 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -4,9 +4,10 @@ # Subdirs. -SUB_DIRS := serial +SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) +MOD_IN_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) serial # The target object and module list name. @@ -37,6 +38,18 @@ obj-m := obj-n := obj- := +# Object files in subdirectories + +ifeq ($(CONFIG_USB_SERIAL),y) + SUB_DIRS += serial + obj-y += serial/serial.o +else + ifeq ($(CONFIG_USB_SERIAL),m) + MOD_SUB_DIRS += serial + endif +endif + + # Each configuration option enables a list of files. obj-$(CONFIG_USB) += usbcore.o @@ -68,6 +81,7 @@ obj-$(CONFIG_USB_PLUSB) += plusb.o obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_PEGASUS) += pegasus.o obj-$(CONFIG_USB_RIO500) += rio500.o +obj-$(CONFIG_USB_DSBR) += dsbr100.o # Extract lists of the multi-part drivers. # The 'int-*' lists are the intermediate files used to build the multi's. diff --git a/drivers/usb/dsbr100.c b/drivers/usb/dsbr100.c new file mode 100644 index 000000000..8b81e06c0 --- /dev/null +++ b/drivers/usb/dsbr100.c @@ -0,0 +1,353 @@ +/* A driver for the D-Link DSB-R100 USB radio. The R100 plugs + into both the USB and an analog audio input, so this thing + only deals with initialisation and frequency setting, the + audio data has to be handled by a sound driver. + + Major issue: I can't find out where the device reports the signal + strength, and indeed the windows software appearantly just looks + at the stereo indicator as well. So, scanning will only find + stereo stations. Sad, but I can't help it. + + Also, the windows program sends oodles of messages over to the + device, and I couldn't figure out their meaning. My suspicion + is that they don't have any:-) + + You might find some interesting stuff about this module at + http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr + + Copyright (c) 2000 Markus Demleitner + + 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 + + History: + + Version 0.21: + Markus Demleitner <msdemlei@tucana.harvard.edu>: + Minor cleanup, warnings if something goes wrong, lame attempt + to adhere to Documentation/CodingStyle + + Version 0.2: + Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module + Markus: Copyright clarification + + Version 0.01: Markus: initial release + +*/ + + +#include <linux/kernel.h> + +#if CONFIG_MODVERSIONS==1 +#define MODVERSIONS +#include <linux/modversions.h> +#endif + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/malloc.h> +#include <linux/input.h> +#include <linux/videodev.h> +#include <linux/usb.h> + +#define DSB100_VENDOR 0x04b4 +#define DSB100_PRODUCT 0x1002 + +#define TB_LEN 16 + +static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum); +static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr); +static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd, + void *arg); +static int usb_dsbr100_open(struct video_device *dev, int flags); +static void usb_dsbr100_close(struct video_device *dev); + + +typedef struct +{ struct urb readurb,writeurb; + struct usb_device *dev; + char transfer_buffer[TB_LEN]; + int curfreq; + int stereo; + int ifnum; +} usb_dsbr100; + + +static struct video_device usb_dsbr100_radio= +{ + "D-Link DSB R-100 USB radio", + VID_TYPE_TUNER, + VID_HARDWARE_AZTECH, + usb_dsbr100_open, + usb_dsbr100_close, + NULL, /* Can't read (no capture ability) */ + NULL, /* Can't write */ + NULL, /* No poll */ + usb_dsbr100_ioctl, + NULL, + NULL +}; + +static int users = 0; + +static struct usb_driver usb_dsbr100_driver = { + name: "dsbr100", + probe: usb_dsbr100_probe, + disconnect: usb_dsbr100_disconnect, + driver_list: {NULL,NULL}, + fops: NULL, + minor: 0 +}; + + +static int dsbr100_start(usb_dsbr100 *radio) +{ + if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x00, 0xC0, 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x02, 0xC0, 0x01, 0x00, radio->transfer_buffer, 8, 300)<0) + return -1; + return (radio->transfer_buffer)[0]; +} + + +static int dsbr100_stop(usb_dsbr100 *radio) +{ + if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x00, 0xC0, 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x02, 0xC0, 0x00, 0x00, radio->transfer_buffer, 8, 300)<0) + return -1; + return (radio->transfer_buffer)[0]; +} + + +static int dsbr100_setfreq(usb_dsbr100 *radio, int freq) +{ + freq = (freq*80)/16+856; + if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x01, 0xC0, (freq&0xff00)>>8, freq&0xff, + radio->transfer_buffer, 8, 300)<0 + || usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x00, 0xC0, 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x00, 0xC0, 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) { + radio->stereo = -1; + return -1; + } + radio->stereo = ! ((radio->transfer_buffer)[0]&0x01); + return (radio->transfer_buffer)[0]; +} + +static void dsbr100_getstat(usb_dsbr100 *radio) +{ + if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x00, 0xC0, 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0) + radio->stereo = -1; + else + radio->stereo = ! (radio->transfer_buffer[0]&0x01); +} + + +static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum) +{ + usb_dsbr100 *radio; + + if (dev->descriptor.idVendor!=DSB100_VENDOR || + dev->descriptor.idProduct!=DSB100_PRODUCT) + return NULL; + if (!(radio = kmalloc(sizeof(usb_dsbr100),GFP_KERNEL))) + return NULL; + usb_dsbr100_radio.priv = radio; + radio->dev = dev; + radio->ifnum = ifnum; + radio->curfreq = 1454; + return (void*)radio; +} + +static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr) +{ + usb_dsbr100 *radio=ptr; + + if (users) + return; + kfree(radio); + usb_dsbr100_radio.priv = NULL; +} + +static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd, + void *arg) +{ + usb_dsbr100 *radio=dev->priv; + + if (!radio) + return -EINVAL; + + switch(cmd) + { + case VIDIOCGCAP: { + struct video_capability v; + v.type=VID_TYPE_TUNER; + v.channels=1; + v.audios=1; + /* No we don't do pictures */ + v.maxwidth=0; + v.maxheight=0; + v.minwidth=0; + v.minheight=0; + strcpy(v.name, "D-Link R-100 USB Radio"); + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCGTUNER: { + struct video_tuner v; + dsbr100_getstat(radio); + if(copy_from_user(&v, arg,sizeof(v))!=0) + return -EFAULT; + if(v.tuner) /* Only 1 tuner */ + return -EINVAL; + v.rangelow=(87*16000); + v.rangehigh=(108*16000); + /*v.flags=VIDEO_TUNER_LOW;*/ + v.mode=VIDEO_MODE_AUTO; + v.signal=radio->stereo; + v.flags|=VIDEO_TUNER_STEREO_ON; + strcpy(v.name, "FM"); + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSTUNER: { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.tuner!=0) + return -EINVAL; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + if (radio->curfreq==-1) + return -EINVAL; + if(copy_to_user(arg, &(radio->curfreq), + sizeof(radio->curfreq))) + return -EFAULT; + return 0; + + case VIDIOCSFREQ: + if(copy_from_user(&(radio->curfreq), arg, + sizeof(radio->curfreq))) + return -EFAULT; + if (dsbr100_setfreq(radio, radio->curfreq)==-1) + warn("set frequency failed"); + return 0; + + case VIDIOCGAUDIO: { + struct video_audio v; + memset(&v,0, sizeof(v)); + v.flags|=VIDEO_AUDIO_MUTABLE; + v.mode=VIDEO_SOUND_STEREO; + v.volume=1; + v.step=1; + strcpy(v.name, "Radio"); + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: { + struct video_audio v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.audio) + return -EINVAL; + + if(v.flags&VIDEO_AUDIO_MUTE) { + if (dsbr100_stop(radio)==-1) + warn("radio did not respond properly"); + } + else + if (dsbr100_start(radio)==-1) + warn("radio did not respond properly"); + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + + +static int usb_dsbr100_open(struct video_device *dev, int flags) +{ + usb_dsbr100 *radio=dev->priv; + + if (! radio) { + warn("radio not initialised"); + return -EAGAIN; + } + if(users) + { + warn("radio in use"); + return -EBUSY; + } + users++; + MOD_INC_USE_COUNT; + if (dsbr100_start(radio)<0) + warn("radio did not start up properly"); + dsbr100_setfreq(radio,radio->curfreq); + return 0; +} + +static void usb_dsbr100_close(struct video_device *dev) +{ + usb_dsbr100 *radio=dev->priv; + + if (!radio) + return; + users--; + dsbr100_stop(radio); + MOD_DEC_USE_COUNT; +} + +int __init dsbr100_init(void) +{ + usb_dsbr100_radio.priv = NULL; + usb_register(&usb_dsbr100_driver); + if (video_register_device(&usb_dsbr100_radio,VFL_TYPE_RADIO)==-1) { + warn("couldn't register video device"); + return -EINVAL; + } + return 0; +} + +int __init init_module(void) +{ + return dsbr100_init(); +} + +void cleanup_module(void) +{ + usb_dsbr100 *radio=usb_dsbr100_radio.priv; + + if (radio) + dsbr100_stop(radio); + video_unregister_device(&usb_dsbr100_radio); + usb_deregister(&usb_dsbr100_driver); +} + +/* +vi: ts=8 +Sigh. Of course, I am one of the ts=2 heretics, but Linus' wish is +my command. +*/ diff --git a/drivers/usb/inode.c b/drivers/usb/inode.c index 92a008d2f..c30f2eaff 100644 --- a/drivers/usb/inode.c +++ b/drivers/usb/inode.c @@ -29,6 +29,7 @@ /*****************************************************************************/ #define __NO_VERSION__ +#include <linux/config.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/sched.h> diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c index 4e9b4f65e..d1f5048e3 100644 --- a/drivers/usb/ov511.c +++ b/drivers/usb/ov511.c @@ -2,20 +2,17 @@ * OmniVision OV511 Camera-to-USB Bridge Driver * Copyright (c) 1999/2000 Mark W. McClelland * Many improvements by Bret Wallach - * + * Color fixes by by Orion Sky Lawlor, olawlor@acm.org, 2/26/2000 + * Snapshot code by Kevin Moore + * * Based on the Linux CPiA driver. * * Released under GPL v.2 license. * - * Important keywords in comments: - * CAMERA SPECIFIC - Camera specific code; may not work with other cameras. - * DEBUG - Debugging code. - * FIXME - Something that is broken or needs improvement. - * - * Version: 1.07 + * Version: 1.09 * * Please see the file: linux/Documentation/usb/ov511.txt - * and the website at: http://people.delphi.com/mmcclelland/linux/ + * and the website at: http://alpha.dyndns.org/ov511 * for more info. */ @@ -39,15 +36,6 @@ /* Handle mangled (versioned) external symbols */ -#include <linux/config.h> /* retrieve the CONFIG_* macros */ -#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS) -# define MODVERSIONS /* force it on */ -#endif - -#ifdef MODVERSIONS -#include <linux/modversions.h> -#endif - #include <linux/kernel.h> #include <linux/sched.h> #include <linux/list.h> @@ -58,6 +46,7 @@ #include <linux/vmalloc.h> #include <linux/wrapper.h> #include <linux/module.h> +#include <linux/init.h> #include <linux/spinlock.h> #include <linux/time.h> #include <linux/usb.h> @@ -67,8 +56,6 @@ #define OV511_I2C_RETRIES 3 -#define OV7610_AUTO_ADJUST 1 - /* Video Size 640 x 480 x 3 bytes for RGB */ #define MAX_FRAME_SIZE (640 * 480 * 3) #define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval)) @@ -77,6 +64,33 @@ #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 +// PARAMETER VARIABLES: +static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */ + +/* 0=no debug messages + * 1=init/detection/unload and other significant messages, + * 2=some warning messages + * 3=config/control function calls + * 4=most function calls and data parsing messages + * 5=highly repetitive mesgs + * NOTE: This should be changed to 0, 1, or 2 for production kernels + */ +static int debug = 3; + +/* Fix vertical misalignment of red and blue at 640x480 */ +static int fix_rgb_offset = 0; + +/* Snapshot mode enabled flag */ +static int snapshot = 0; + +MODULE_PARM(autoadjust, "i"); +MODULE_PARM(debug, "i"); +MODULE_PARM(fix_rgb_offset, "i"); +MODULE_PARM(snapshot, "i"); + +MODULE_AUTHOR("Mark McClelland (and others)"); +MODULE_DESCRIPTION("OV511 USB Camera Driver"); + char kernel_version[] = UTS_RELEASE; /*******************************/ @@ -206,10 +220,8 @@ int ov511_reg_write(struct usb_device *dev, unsigned char reg, unsigned char val USB_TYPE_CLASS | USB_RECIP_DEVICE, 0, (__u16)reg, &value, 1, HZ); -#if 0 - PDEBUG("reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc); -#endif - + PDEBUG(5, "reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc); + return rc; } @@ -225,9 +237,7 @@ int ov511_reg_read(struct usb_device *dev, unsigned char reg) USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE, 0, (__u16)reg, buffer, 1, HZ); -#if 0 - PDEBUG("reg read: 0x%02X:0x%02X", reg, buffer[0]); -#endif + PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]); if(rc < 0) return rc; @@ -239,9 +249,8 @@ int ov511_i2c_write(struct usb_device *dev, unsigned char reg, unsigned char val { int rc, retries; -#if 0 - PDEBUG("i2c write: 0x%02X:0x%02X", reg, value); -#endif + PDEBUG(5, "i2c write: 0x%02X:0x%02X", reg, value); + /* Three byte write cycle */ for(retries = OV511_I2C_RETRIES;;) { /* Select camera register */ @@ -321,9 +330,8 @@ int ov511_i2c_read(struct usb_device *dev, unsigned char reg) } value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT); -#if 0 - PDEBUG("i2c read: 0x%02X:0x%02X", reg, value); -#endif + + PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value); /* This is needed to make ov511_i2c_write() work */ rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05); @@ -355,9 +363,8 @@ int ov511_i2c_read(struct usb_device *dev, unsigned char reg) if (rc < 0) return rc; value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT); - #if 0 - PDEBUG("i2c read: 0x%02X:0x%02X", reg, value); - #endif + + PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value); return (value); } @@ -391,15 +398,14 @@ static void ov511_dump_i2c_range( struct usb_device *dev, int reg1, int regn) int rc; for(i=reg1; i<=regn; i++) { rc = ov511_i2c_read(dev, i); -#if 0 - PDEBUG("OV7610[0x%X] = 0x%X", i, rc); -#endif + + PDEBUG(1, "OV7610[0x%X] = 0x%X", i, rc); } } static void ov511_dump_i2c_regs( struct usb_device *dev) { - PDEBUG("I2C REGS"); + PDEBUG(3, "I2C REGS"); ov511_dump_i2c_range(dev, 0x00, 0x38); } @@ -409,27 +415,27 @@ static void ov511_dump_reg_range( struct usb_device *dev, int reg1, int regn) int rc; for(i=reg1; i<=regn; i++) { rc = ov511_reg_read(dev, i); - PDEBUG("OV511[0x%X] = 0x%X", i, rc); + PDEBUG(1, "OV511[0x%X] = 0x%X", i, rc); } } static void ov511_dump_regs( struct usb_device *dev) { - PDEBUG("CAMERA INTERFACE REGS"); + PDEBUG(1, "CAMERA INTERFACE REGS"); ov511_dump_reg_range(dev, 0x10, 0x1f); - PDEBUG("DRAM INTERFACE REGS"); + PDEBUG(1, "DRAM INTERFACE REGS"); ov511_dump_reg_range(dev, 0x20, 0x23); - PDEBUG("ISO FIFO REGS"); + PDEBUG(1, "ISO FIFO REGS"); ov511_dump_reg_range(dev, 0x30, 0x31); - PDEBUG("PIO REGS"); + PDEBUG(1, "PIO REGS"); ov511_dump_reg_range(dev, 0x38, 0x39); ov511_dump_reg_range(dev, 0x3e, 0x3e); - PDEBUG("I2C REGS"); + PDEBUG(1, "I2C REGS"); ov511_dump_reg_range(dev, 0x40, 0x49); - PDEBUG("SYSTEM CONTROL REGS"); + PDEBUG(1, "SYSTEM CONTROL REGS"); ov511_dump_reg_range(dev, 0x50, 0x53); ov511_dump_reg_range(dev, 0x5e, 0x5f); - PDEBUG("OmniCE REGS"); + PDEBUG(1, "OmniCE REGS"); ov511_dump_reg_range(dev, 0x70, 0x79); ov511_dump_reg_range(dev, 0x80, 0x9f); ov511_dump_reg_range(dev, 0xa0, 0xbf); @@ -441,7 +447,7 @@ int ov511_reset(struct usb_device *dev, unsigned char reset_type) { int rc; - PDEBUG("Reset: type=0x%X", reset_type); + PDEBUG(3, "Reset: type=0x%X", reset_type); rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type); if (rc < 0) err("reset: command failed"); @@ -457,9 +463,7 @@ int ov511_set_packet_size(struct usb_ov511 *ov511, int size) { int alt, multiplier, rc; -#if 0 - PDEBUG("set packet size: %d", size); -#endif + PDEBUG(3, "set packet size: %d", size); switch (size) { case 992: @@ -576,7 +580,7 @@ static inline int ov7610_get_picture(struct usb_ov511 *ov511, p->hue = 0x8000; p->whiteness = 105 << 8; - p->depth = 24; + p->depth = 3; /* Don't know if this is right */ p->palette = VIDEO_PALETTE_RGB24; /* Restart the camera */ @@ -594,10 +598,8 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511, int rc = 0; struct usb_device *dev = ov511->dev; -#if 0 - PDEBUG("ov511_mode_init_regs(ov511, %d, %d, %d, %d)", + PDEBUG(3, "ov511_mode_init_regs(ov511, w:%d, h:%d, mode:%d, sub:%d)", width, height, mode, sub_flag); -#endif // ov511_set_packet_size(ov511, 0); if (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d) < 0) { @@ -606,13 +608,19 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511, } if (mode == VIDEO_PALETTE_GREY) { - ov511_reg_write(dev, 0x16, 0); - ov511_i2c_write(dev, 0xe, 0x44); + ov511_reg_write(dev, 0x16, 0x00); + ov511_i2c_write(dev, 0x0e, 0x44); ov511_i2c_write(dev, 0x13, 0x21); + /* For snapshot */ + ov511_reg_write(dev, 0x1e, 0x00); + ov511_reg_write(dev, 0x1f, 0x01); } else { - ov511_reg_write(dev, 0x16, 1); - ov511_i2c_write(dev, 0xe, 0x4); - ov511_i2c_write(dev, 0x13, 0x1); + ov511_reg_write(dev, 0x16, 0x01); + ov511_i2c_write(dev, 0x0e, 0x04); + ov511_i2c_write(dev, 0x13, 0x01); + /* For snapshot */ + ov511_reg_write(dev, 0x1e, 0x01); + ov511_reg_write(dev, 0x1f, 0x03); } if (width == 640 && height == 480) { @@ -626,13 +634,26 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511, ov511_reg_write(ov511->dev, 0x12, (ov511->subw>>3)-1); ov511_reg_write(ov511->dev, 0x13, (ov511->subh>>3)-1); ov511_i2c_write(dev, 0x11, 0x01); + + /* Snapshot additions */ + ov511_reg_write(ov511->dev, 0x1a, (ov511->subw>>3)-1); + ov511_reg_write(ov511->dev, 0x1b, (ov511->subh>>3)-1); + ov511_reg_write(ov511->dev, 0x1c, 0x00); + ov511_reg_write(ov511->dev, 0x1d, 0x00); } else { ov511_i2c_write(ov511->dev, 0x17, 0x38); ov511_i2c_write(ov511->dev, 0x18, 0x3a + (640>>2)); ov511_i2c_write(ov511->dev, 0x19, 0x5); - ov511_i2c_write(ov511->dev, 0x1c, + (480>>1)); + ov511_i2c_write(ov511->dev, 0x1a, 5 + (480>>1)); ov511_reg_write(dev, 0x12, 0x4f); ov511_reg_write(dev, 0x13, 0x3d); + + /* Snapshot additions */ + ov511_reg_write(ov511->dev, 0x1a, 0x4f); + ov511_reg_write(ov511->dev, 0x1b, 0x3d); + ov511_reg_write(ov511->dev, 0x1c, 0x00); + ov511_reg_write(ov511->dev, 0x1d, 0x00); + if (mode == VIDEO_PALETTE_GREY) { ov511_i2c_write(dev, 0x11, 4); /* check */ } else { @@ -642,6 +663,8 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511, ov511_reg_write(dev, 0x14, 0x00); ov511_reg_write(dev, 0x15, 0x00); + + /* FIXME?? Shouldn't below be true only for YUV420? */ ov511_reg_write(dev, 0x18, 0x03); ov511_i2c_write(dev, 0x12, 0x24); @@ -654,6 +677,12 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511, ov511_reg_write(dev, 0x15, 0x00); ov511_reg_write(dev, 0x18, 0x03); + /* Snapshot additions */ + ov511_reg_write(dev, 0x1a, 0x27); + ov511_reg_write(dev, 0x1b, 0x1f); + ov511_reg_write(dev, 0x1c, 0x00); + ov511_reg_write(dev, 0x1d, 0x00); + if (mode == VIDEO_PALETTE_GREY) { ov511_i2c_write(dev, 0x11, 1); /* check */ } else { @@ -671,7 +700,7 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511, // ov511_set_packet_size(ov511, 993); if (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00) < 0) { - PDEBUG("reset: command failed"); + err("reset: command failed"); return -EIO; } @@ -683,52 +712,74 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511, Turn a YUV4:2:0 block into an RGB block +Video4Linux seems to use the blue, green, red channel +order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red. + +Color space conversion coefficients taken from the excellent +http://www.inforamp.net/~poynton/ColorFAQ.html +In his terminology, this is a CCIR 601.1 YCbCr -> RGB. +Y values are given for all 4 pixels, but the U (Pb) +and V (Pr) are assumed constant over the 2x2 block. + +To avoid floating point arithmetic, the color conversion +coefficients are scaled into 16.16 fixed-point integers. + *************************************************************/ -#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) -static inline void ov511_move_420_block(int y00, int y01, int y10, int y11, - int u, int v, int w, - unsigned char * pOut) +// LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. +#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16))) +static inline void ov511_move_420_block( + int yTL, int yTR, int yBL, int yBR, + int u, int v, + int rowPixels, unsigned char * rgb) { - int r = 68911 * v; - int g = -16915 * u + -35101 * v; - int b = 87097 * u; - y00 *= 49152; - y01 *= 49152; - y10 *= 49152; - y11 *= 49152; - *(pOut+w*3) = LIMIT(r + y10); - *pOut++ = LIMIT(r + y00); - *(pOut+w*3) = LIMIT(g + y10); - *pOut++ = LIMIT(g + y00); - *(pOut+w*3) = LIMIT(b + y10); - *pOut++ = LIMIT(b + y00); - *(pOut+w*3) = LIMIT(r + y11); - *pOut++ = LIMIT(r + y01); - *(pOut+w*3) = LIMIT(g + y11); - *pOut++ = LIMIT(g + y01); - *(pOut+w*3) = LIMIT(b + y11); - *pOut++ = LIMIT(b + y01); + const double brightness=1.0;//0->black; 1->full scale + const double saturation=1.0;//0->greyscale; 1->full color + const double fixScale=brightness*256*256; + const int rvScale=(int)(1.402*saturation*fixScale); + const int guScale=(int)(-0.344136*saturation*fixScale); + const int gvScale=(int)(-0.714136*saturation*fixScale); + const int buScale=(int)(1.772*saturation*fixScale); + const int yScale=(int)(fixScale); + + int r = rvScale * v; + int g = guScale * u + gvScale * v; + int b = buScale * u; + yTL *= yScale; yTR *= yScale; + yBL *= yScale; yBR *= yScale; + + //Write out top two pixels + rgb[0]=LIMIT(b+yTL); rgb[1]=LIMIT(g+yTL); rgb[2]=LIMIT(r+yTL); + rgb[3]=LIMIT(b+yTR); rgb[4]=LIMIT(g+yTR); rgb[5]=LIMIT(r+yTR); + rgb+=3*rowPixels;//Skip down to next line to write out bottom two pixels + rgb[0]=LIMIT(b+yBL); rgb[1]=LIMIT(g+yBL); rgb[2]=LIMIT(r+yBL); + rgb[3]=LIMIT(b+yBR); rgb[4]=LIMIT(g+yBR); rgb[5]=LIMIT(r+yBR); } + /*************************************************************** For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments. The -first 64 bytes of each segment are V, the next 64 are U. The V and -U are arranged as follows: +first 64 bytes of each segment are U, the next 64 are V. The U and +V are arranged as follows: 0 1 ... 7 8 9 ... 15 ... 56 57 ... 63 -The next 256 bytes are Y data and represent 4 squares of 8x8 pixels as -follows: +U and V are shipped at half resolution (1 U,V sample -> one 2x2 block). + +The next 256 bytes are full resolution Y data and represent 4 +squares of 8x8 pixels as follows: 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 8 9 ... 15 72 73 ... 79 200 201 ... 207 ... ... ... 56 57 ... 63 120 121 127 248 249 ... 255 +Note that the U and V data in one segment represents a 16 x 16 pixel +area, but the Y data represents a 32 x 8 pixel area. + If OV511_DUMPPIX is defined, _parse_data just dumps the incoming segments, verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480 this puts the data @@ -780,8 +831,8 @@ static void ov511_parse_data_rgb24(unsigned char * pIn0, int y01 = *(pOut+3); int y10 = *(pOut+iWidth*3); int y11 = *(pOut+iWidth*3+3); - int u = *(pIn+64) - 128; - int v = *pIn++ - 128; + int v = *(pIn+64) - 128; + int u = *pIn++ - 128; ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, pOut); pOut += 6; } @@ -810,8 +861,8 @@ static void ov511_parse_data_rgb24(unsigned char * pIn0, int y00 = *pIn++; int y11 = *(pIn+8); int y01 = *pIn++; - int u = *pOut1 - 128; - int v = *(pOut1+1) - 128; + int v = *pOut1 - 128; + int u = *(pOut1+1) - 128; ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, pOut1); pOut1 += 6; } @@ -868,6 +919,42 @@ static void ov511_parse_data_grey(unsigned char * pIn0, } } + +/************************************************************** + * fixFrameRGBoffset-- + * My camera seems to return the red channel about 1 pixel + * low, and the blue channel about 1 pixel high. After YUV->RGB + * conversion, we can correct this easily. OSL 2/24/2000. + *************************************************************/ +static void fixFrameRGBoffset(struct ov511_frame *frame) +{ + int x,y; + int rowBytes=frame->width*3,w=frame->width; + unsigned char *rgb=frame->data; + const int shift=1;//Distance to shift pixels by, vertically + + if (frame->width<400) + return;//Don't bother with little images + + //Shift red channel up + for (y=shift;y<frame->height;y++) + { + int lp=(y-shift)*rowBytes;//Previous line offset + int lc=y*rowBytes;//Current line offset + for (x=0;x<w;x++) + rgb[lp+x*3+2]=rgb[lc+x*3+2];//Shift red up + } + //Shift blue channel down + for (y=frame->height-shift-1;y>=0;y--) + { + int ln=(y+shift)*rowBytes;//Next line offset + int lc=y*rowBytes;//Current line offset + for (x=0;x<w;x++) + rgb[ln+x*3+0]=rgb[lc+x*3+0];//Shift blue down + } +} + + static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb) { unsigned char *cdata; @@ -887,7 +974,7 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb) if (!n || ov511->curframe == -1) continue; if (st) - PDEBUG("data error: [%d] len=%d, status=%d", i, n, st); + PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st); frame = &ov511->frame[ov511->curframe]; @@ -899,14 +986,15 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb) struct timeval *ts; ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE); do_gettimeofday(ts); -#if 0 - PDEBUG("Frame End, curframe = %d, packnum=%d, hw=%d, vw=%d", + + PDEBUG(4, "Frame End, curframe = %d, packnum=%d, hw=%d, vw=%d", ov511->curframe, (int)(cdata[992]), (int)(cdata[9]), (int)(cdata[10])); -#endif if (frame->scanstate == STATE_LINES) { int iFrameNext; + if (fix_rgb_offset) + fixFrameRGBoffset(frame); frame->grabstate = FRAME_DONE; if (waitqueue_active(&frame->wq)) { frame->grabstate = FRAME_DONE; @@ -919,10 +1007,10 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb) ov511->curframe = iFrameNext; ov511->frame[iFrameNext].scanstate = STATE_SCANNING; } else { -#if 0 - PDEBUG("Frame not ready? state = %d", + + PDEBUG(4, "Frame not ready? state = %d", ov511->frame[iFrameNext].grabstate); -#endif + ov511->curframe = -1; } } @@ -932,11 +1020,18 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb) else if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | cdata[4] | cdata[5] | cdata[6] | cdata[7]) == 0 && (cdata[8] & 8)) { -#if 0 - PDEBUG("ov511: Found Frame Start!, framenum = %d", + + PDEBUG(4, "ov511: Found Frame Start!, framenum = %d", ov511->curframe); -#endif - frame->scanstate = STATE_LINES; + + /* Check to see if it's a snapshot frame */ + /* FIXME?? Should the snapshot reset go here? Performance? */ + if (cdata[8] & 0x02) { + frame->snapshot = 1; + PDEBUG(3, "ov511_move_data: snapshot detected"); + } + + frame->scanstate = STATE_LINES; frame->segment = 0; } @@ -1014,11 +1109,11 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb) } } -#if 0 - PDEBUG("pn: %d %d %d %d %d %d %d %d %d %d\n", + + PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d\n", aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4], aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]); -#endif + return totlen; } @@ -1032,7 +1127,7 @@ static void ov511_isoc_irq(struct urb *urb) return; if (!ov511->streaming) { - PDEBUG("hmmm... not streaming, but got interrupt\n"); + PDEBUG(2, "hmmm... not streaming, but got interrupt"); return; } @@ -1166,6 +1261,7 @@ static int ov511_new_frame(struct usb_ov511 *ov511, int framenum) frame->grabstate = FRAME_GRABBING; frame->scanstate = STATE_SCANNING; frame->scanlength = 0; /* accumulated in ov511_parse_data() */ + frame->snapshot = 0; ov511->curframe = framenum; @@ -1192,7 +1288,7 @@ static int ov511_open(struct video_device *dev, int flags) int err = -EBUSY; struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; - PDEBUG("ov511_open"); + PDEBUG(4, "ov511_open"); down(&ov511->lock); if (ov511->user) @@ -1212,8 +1308,8 @@ static int ov511_open(struct video_device *dev, int flags) ov511->frame[1].data = ov511->fbuf + MAX_DATA_SIZE; ov511->sub_flag = 0; - PDEBUG("frame [0] @ %p", ov511->frame[0].data); - PDEBUG("frame [1] @ %p", ov511->frame[1].data); + PDEBUG(4, "frame [0] @ %p", ov511->frame[0].data); + PDEBUG(4, "frame [1] @ %p", ov511->frame[1].data); ov511->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); if (!ov511->sbuf[0].data) @@ -1222,8 +1318,8 @@ static int ov511_open(struct video_device *dev, int flags) if (!ov511->sbuf[1].data) goto open_err_on1; - PDEBUG("sbuf[0] @ %p", ov511->sbuf[0].data); - PDEBUG("sbuf[1] @ %p", ov511->sbuf[1].data); + PDEBUG(4, "sbuf[0] @ %p", ov511->sbuf[0].data); + PDEBUG(4, "sbuf[1] @ %p", ov511->sbuf[1].data); err = ov511_init_isoc(ov511); if (err) @@ -1254,7 +1350,7 @@ static void ov511_close(struct video_device *dev) { struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; - PDEBUG("ov511_close"); + PDEBUG(4, "ov511_close"); down(&ov511->lock); ov511->user--; @@ -1289,9 +1385,8 @@ static long ov511_write(struct video_device *dev, const char *buf, unsigned long static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) { struct usb_ov511 *ov511 = (struct usb_ov511 *)vdev; -#if 0 - PDEBUG("IOCtl: 0x%X", cmd); -#endif + + PDEBUG(4, "IOCtl: 0x%X", cmd); if (!ov511->dev) return -EIO; @@ -1464,11 +1559,9 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) return -EFAULT; -#if 0 - PDEBUG("MCAPTURE"); - PDEBUG("frame: %d, size: %dx%d, format: %d", + PDEBUG(4, "MCAPTURE"); + PDEBUG(4, "frame: %d, size: %dx%d, format: %d", vm.frame, vm.width, vm.height, vm.format); -#endif if (vm.format != VIDEO_PALETTE_RGB24 && vm.format != VIDEO_PALETTE_GREY) @@ -1516,10 +1609,9 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) if (copy_from_user((void *)&frame, arg, sizeof(int))) return -EFAULT; -#if 0 - PDEBUG("syncing to frame %d, grabstate = %d", frame, + PDEBUG(4, "syncing to frame %d, grabstate = %d", frame, ov511->frame[frame].grabstate); -#endif + switch (ov511->frame[frame].grabstate) { case FRAME_UNUSED: return -EINVAL; @@ -1552,7 +1644,17 @@ redo: } ov511->frame[frame].grabstate = FRAME_UNUSED; - + + /* Reset the hardware snapshot button */ + /* FIXME - Is this the best place for this? */ + if ((ov511->snap_enabled) && + (ov511->frame[frame].snapshot)) { + ov511->frame[frame].snapshot = 0; + ov511_reg_write(ov511->dev, 0x52, 0x01); + ov511_reg_write(ov511->dev, 0x52, 0x03); + ov511_reg_write(ov511->dev, 0x52, 0x01); + } + return 0; } case VIDIOCGFBUF: @@ -1594,7 +1696,7 @@ static long ov511_read(struct video_device *dev, char *buf, unsigned long count, int frmx = -1; volatile struct ov511_frame *frame; - PDEBUG("ov511_read: %ld bytes, noblock=%d", count, noblock); + PDEBUG(4, "ov511_read: %ld bytes, noblock=%d", count, noblock); if (!dev || !buf) return -EFAULT; @@ -1644,18 +1746,37 @@ restart: goto restart; } - PDEBUG("ov511_read: frmx=%d, bytes_read=%ld, scanlength=%ld", frmx, + + /* Repeat until we get a snapshot frame */ + if (ov511->snap_enabled && !frame->snapshot) { + frame->bytes_read = 0; + if (ov511_new_frame(ov511, frmx)) + err("ov511_read: ov511_new_frame error"); + goto restart; + } + + /* Clear the snapshot */ + if (ov511->snap_enabled && frame->snapshot) { + frame->snapshot = 0; + ov511_reg_write(ov511->dev, 0x52, 0x01); + ov511_reg_write(ov511->dev, 0x52, 0x03); + ov511_reg_write(ov511->dev, 0x52, 0x01); + } + + PDEBUG(4, "ov511_read: frmx=%d, bytes_read=%ld, scanlength=%ld", frmx, frame->bytes_read, frame->scanlength); /* copy bytes to user space; we allow for partials reads */ - if ((count + frame->bytes_read) > frame->scanlength) - count = frame->scanlength - frame->bytes_read; +// if ((count + frame->bytes_read) > frame->scanlength) +// count = frame->scanlength - frame->bytes_read; + /* FIXME - count hardwired to be one frame... */ + count = frame->width * frame->height * frame->depth; if (copy_to_user(buf, frame->data + frame->bytes_read, count)) return -EFAULT; frame->bytes_read += count; - PDEBUG("ov511_read: {copy} count used=%ld, new bytes_read=%ld", + PDEBUG(4, "ov511_read: {copy} count used=%ld, new bytes_read=%ld", count, frame->bytes_read); if (frame->bytes_read >= frame->scanlength) { /* All data has been read */ @@ -1679,7 +1800,7 @@ static int ov511_mmap(struct video_device *dev, const char *adr, unsigned long s if (!ov511->dev) return -EIO; - PDEBUG("mmap: %ld (%lX) bytes", size, size); + PDEBUG(4, "mmap: %ld (%lX) bytes", size, size); if (size > (((2 * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) return -EINVAL; @@ -1702,24 +1823,22 @@ static int ov511_mmap(struct video_device *dev, const char *adr, unsigned long s } static struct video_device ov511_template = { - "OV511 USB Camera", - VID_TYPE_CAPTURE, - VID_HARDWARE_OV511, - ov511_open, - ov511_close, - ov511_read, - ov511_write, - NULL, - ov511_ioctl, - ov511_mmap, - ov511_init_done, - NULL, - 0, - 0 + name: "OV511 USB Camera", + type: VID_TYPE_CAPTURE, + hardware: VID_HARDWARE_OV511, + open: ov511_open, + close: ov511_close, + read: ov511_read, + write: ov511_write, + ioctl: ov511_ioctl, + mmap: ov511_mmap, + initialize: ov511_init_done, }; static int ov7610_configure(struct usb_device *dev) { + int tries; + if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, OV7610_I2C_WRITE_ID) < 0) return -1; @@ -1731,6 +1850,7 @@ static int ov7610_configure(struct usb_device *dev) if (ov511_reset(dev, OV511_RESET_NOREGS) < 0) return -1; + /* Reset the 7610 and wait a bit for it to initialize */ if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; schedule_timeout (1 + 150 * HZ / 1000); @@ -1738,8 +1858,14 @@ static int ov7610_configure(struct usb_device *dev) if(ov511_i2c_read(dev, 0x00) < 0) return -1; - if((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) != 0x7F) || - (ov511_i2c_read(dev, OV7610_REG_ID_LOW) != 0xA2)) { + tries = 5; + while((tries > 0) && + ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) != 0x7F) || + (ov511_i2c_read(dev, OV7610_REG_ID_LOW) != 0xA2))) { + --tries; + } + + if (tries == 0) { err("Failed to read OV7610 ID. You might not have an OV7610,"); err("or it may be not responding. Report this to"); err("mmcclelland@delphi.com"); @@ -1786,12 +1912,11 @@ static int ov511_configure(struct usb_ov511 *ov511) {OV511_I2C_BUS, 0x16, 0x06}, {OV511_I2C_BUS, 0x28, 0x24}, /* 24 */ {OV511_I2C_BUS, 0x2b, 0xac}, - {OV511_I2C_BUS, 0x5, 0x00}, - {OV511_I2C_BUS, 0x6, 0x00}, -#if 0 -#endif + {OV511_I2C_BUS, 0x05, 0x00}, + {OV511_I2C_BUS, 0x06, 0x00}, + {OV511_I2C_BUS, 0x12, 0x00}, - {OV511_I2C_BUS, 0x13, 0x00}, +// {OV511_I2C_BUS, 0x13, 0x00}, {OV511_I2C_BUS, 0x38, 0x81}, {OV511_I2C_BUS, 0x28, 0x24}, /* 0c */ {OV511_I2C_BUS, 0x05, 0x00}, @@ -1813,7 +1938,7 @@ static int ov511_configure(struct usb_ov511 *ov511) {OV511_I2C_BUS, 0x33, 0x20}, {OV511_I2C_BUS, 0x34, 0x48}, {OV511_I2C_BUS, 0x12, 0x24}, - {OV511_I2C_BUS, 0x13, 0x01}, +// {OV511_I2C_BUS, 0x13, 0x01}, {OV511_I2C_BUS, 0x11, 0x01}, {OV511_I2C_BUS, 0x0c, 0x24}, {OV511_I2C_BUS, 0x0d, 0x24}, @@ -1853,7 +1978,8 @@ static int ov511_configure(struct usb_ov511 *ov511) } ov511->compress = 0; - + ov511->snap_enabled = snapshot; + /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used * (using read() instead). */ ov511->frame[0].width = DEFAULT_WIDTH; @@ -1864,10 +1990,16 @@ static int ov511_configure(struct usb_ov511 *ov511) ov511->frame[1].bytes_read = 0; /* Initialize to DEFAULT_WIDTH, DEFAULT_HEIGHT, YUV4:2:0 */ - if ((rc = ov511_write_regvals(dev, aRegvalsNorm))) return rc; + if ((rc = ov511_write_regvals(dev, aRegvalsNorm))) goto error; if ((rc = ov511_mode_init_regs(ov511, DEFAULT_WIDTH, DEFAULT_HEIGHT, - VIDEO_PALETTE_RGB24, 0)) < 0) return rc; + VIDEO_PALETTE_RGB24, 0)) < 0) goto error; + if (autoadjust) { + if (ov511_i2c_write(dev, 0x13, 0x01) < 0) goto error; + } + else { + if (ov511_i2c_write(dev, 0x13, 0x00) < 0 ) goto error; + } return 0; @@ -1887,7 +2019,7 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum) struct usb_ov511 *ov511; int rc; - PDEBUG("probing for device..."); + PDEBUG(1, "probing for device..."); /* We don't handle multi-config cameras */ if (dev->descriptor.bNumConfigurations != 1) @@ -1933,19 +2065,25 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum) case 3: printk("ov511: Camera is a D-Link DSB-C300\n"); break; + case 4: + printk("ov511: Camera is a generic OV511/OV7610\n"); + break; case 5: printk("ov511: Camera is a Puretek PT-6007\n"); break; case 21: printk("ov511: Camera is a Creative Labs WebCam 3\n"); break; + case 36: + printk("ov511: Camera is a Koala-Cam\n"); + break; case 100: printk("ov511: Camera is a Lifeview RoboCam\n"); break; case 102: printk("ov511: Camera is a AverMedia InterCam Elite\n"); break; - case 112: + case 112: /* The OmniVision OV7110 evaluation kit uses this too */ printk("ov511: Camera is a MediaForte MV300\n"); break; default: @@ -2025,31 +2163,21 @@ static struct usb_driver ov511_driver = { { NULL, NULL } }; -int usb_ov511_init(void) +static int __init usb_ov511_init(void) { - PDEBUG("usb_ov511_init()"); - - EXPORT_NO_SYMBOLS; - - return usb_register(&ov511_driver); -} + if (usb_register(&ov511_driver) < 0) + return -1; -void usb_ov511_cleanup(void) -{ - usb_deregister(&ov511_driver); -} + info("ov511 driver registered"); -#ifdef MODULE -int init_module(void) -{ - return usb_ov511_init(); + return 0; } -void cleanup_module(void) +static void __exit usb_ov511_exit(void) { - usb_ov511_cleanup(); - - PDEBUG("Module unloaded"); + usb_deregister(&ov511_driver); + info("ov511 driver deregistered"); } -#endif +module_init(usb_ov511_init); +module_exit(usb_ov511_exit); diff --git a/drivers/usb/ov511.h b/drivers/usb/ov511.h index ba54fb47a..6a4a332bf 100644 --- a/drivers/usb/ov511.h +++ b/drivers/usb/ov511.h @@ -6,9 +6,10 @@ #define OV511_DEBUG /* Turn on debug messages */ #ifdef OV511_DEBUG -# define PDEBUG(fmt, args...) printk("ov511: " fmt "\n" , ## args) +# define PDEBUG(level, fmt, args...) \ +if (debug >= level) printk("ov511: " fmt "\n" , ## args) #else -# define PDEBUG(fmt, args...) do {} while(0) +# define PDEBUG(level, fmt, args...) do {} while(0) #endif /* Camera interface register numbers */ @@ -227,6 +228,8 @@ struct ov511_frame { long bytes_read; /* amount of scanlength that has been read from *data */ wait_queue_head_t wq; /* Processes waiting */ + + int snapshot; /* True if frame was a snapshot */ }; #define OV511_NUMFRAMES 2 @@ -269,6 +272,8 @@ struct usb_ov511 { int scratchlen; wait_queue_head_t wq; /* Processes waiting */ + + int snap_enabled; /* Snapshot mode enabled */ }; #endif diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c index d3761bf52..2d5de6f73 100644 --- a/drivers/usb/pegasus.c +++ b/drivers/usb/pegasus.c @@ -1,578 +1,474 @@ /* -** ** Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller ** -** Copyleft (L) 1999 Petko Manolov - Petkan (petkan@spct.net) -** +** Copyright (R) 1999,2000 Petko Manolov - Petkan (petkan@spct.net) +** ** Distribute under GPL version 2 or later. */ - #include <linux/module.h> #include <linux/sched.h> #include <linux/malloc.h> #include <linux/init.h> #include <linux/delay.h> -#include <linux/usb.h> - #include <linux/netdevice.h> #include <linux/etherdevice.h> +#include <linux/usb.h> -#if LINUX_VERSION_CODE<0x2032d || !defined(__KERNEL__) || !defined(__OPTIMIZE__) -#error You can not compile this driver on this kernel with this C options! -#endif - - -#define ADMTEK_VENDOR_ID 0x07a6 -#define ADMTEK_HPNA_PEGASUS 0x0986 - -#define HPNA_MTU 1500 -#define MAX_MTU 1536 -#define TX_TIMEOUT (HZ*5) -#define SOMETHING (jiffies + TX_TIMEOUT) +static const char *version = __FILE__ ": v0.3.3 2000/03/13 Written by Petko Manolov (petkan@spct.net)\n"; -static const char version[] = "pegasus.c: v0.2.27 2000/02/29 Written by Petko Manolov (petkan@spct.net)\n"; +#define ADMTEK_VENDOR_ID 0x07a6 +#define ADMTEK_DEVICE_ID_PEGASUS 0x0986 +#define PEGASUS_MTU 1500 +#define PEGASUS_MAX_MTU 1536 +#define PEGASUS_TX_TIMEOUT (HZ*5) +#define ALIGN(x) x __attribute__((aligned(16))) -typedef struct usb_hpna -{ - struct usb_device *usb_dev; - struct net_device *net_dev; - int present; - int active; - void *irq_handler; - struct list_head list; +struct pegasus { + struct usb_device *usb; + struct net_device *net; struct net_device_stats stats; - spinlock_t hpna_lock; - struct timer_list timer; - - unsigned int rx_pipe; - unsigned char * rx_buff; - urb_t rx_urb; - - unsigned int tx_pipe; - unsigned char * tx_buff; - urb_t tx_urb; - struct sk_buff * tx_skbuff; - - __u8 intr_ival; - unsigned int intr_pipe; - unsigned char intr_buff[8]; - urb_t intr_urb; -} usb_hpna_t; - - -usb_hpna_t usb_dev_hpna; -static int loopback = 0; -int multicast_filter_limit = 32; -static LIST_HEAD(hpna_list); + spinlock_t pegasus_lock; + struct urb rx_urb, tx_urb, intr_urb; + unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]); + unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]); + unsigned char ALIGN(intr_buff[8]); +}; +static int loopback = 0; +static int multicast_filter_limit = 32; MODULE_AUTHOR("Petko Manolov <petkan@spct.net>"); -MODULE_DESCRIPTION("ADMtek \"Pegasus\" USB Ethernet driver"); +MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver"); MODULE_PARM(loopback, "i"); - -/*** vendor specific commands ***/ -static __inline__ int hpna_get_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data ) -{ - return usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, - indx, data, size, HZ); -} - - -static __inline__ int hpna_set_register( struct usb_device *dev, __u16 indx, __u8 value ) -{ - __u8 data = value; - return usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, - data, indx, &data, 1, HZ); -} +#define pegasus_get_registers(dev, indx, size, data)\ + usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, indx, data, size, HZ); +#define pegasus_set_registers(dev, indx, size, data)\ + usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0, indx, data, size, HZ); +#define pegasus_set_register(dev, indx, value) \ + { __u8 data = value; \ + usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, data, indx, &data, 1, HZ);} -static __inline__ int hpna_set_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data ) +static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata) { - return usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0, - indx, data, size, HZ); -} - - -static int read_phy_word( struct usb_device *dev, __u8 index, __u16 *regdata ) -{ - int i; - __u8 data[4]; - - data[0] = 1; - data[1] = 0; - data[2] = 0; - data[3] = 0x40 + index; - hpna_set_registers( dev, 0x25, 4, data ); - for ( i=0; i<100; i++ ) { - hpna_get_registers( dev, 0x25, 4, data ); - if ( data[3] & 0x80 ) { - *regdata = *(__u16 *)(data+1); - return 0; + int i; + __u8 data[4] = { 1, 0, 0, 0x40 + index }; + + pegasus_set_registers(dev, 0x25, 4, data); + for (i = 0; i < 100; i++) { + pegasus_get_registers(dev, 0x26, 3, data); + if (data[2] & 0x80) { + *regdata = *(__u16 *)(data); + return 0; } udelay(100); } + warn("read_phy_word() failed"); - return 1; + return 1; } - -static int write_phy_word( struct usb_device *dev, __u8 index, __u16 regdata ) +static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regdata) { - int i; - __u8 data[4]; - - data[0] = 1; - data[1] = regdata; - data[2] = regdata >> 8; - data[3] = 0x20 + index; - hpna_set_registers( dev, 0x25, 4, data ); - for ( i=0; i<100; i++ ) { - hpna_get_registers( dev, 0x28, 1, data ); - if ( data[0] & 0x80 ) { - return 0; - } + int i; + __u8 data[4] = { 1, regdata, regdata >> 8, 0x20 + index }; + + pegasus_set_registers(dev, 0x25, 4, data); + for (i = 0; i < 100; i++) { + pegasus_get_registers(dev, 0x28, 1, data); + if (data[0] & 0x80) + return 0; udelay(100); } + warn("write_phy_word() failed"); - return 1; + return 1; } - -int read_srom_word( struct usb_device *dev, __u8 index, __u16 *retdata) +static int pegasus_read_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata) { - int i; - __u8 data[4]; - - data[0] = index; - data[1] = data[2] = 0; - data[3] = 0x02; - hpna_set_registers(dev, 0x20, 4, data); - for ( i=0; i<100; i++ ) { - hpna_get_registers(dev, 0x23, 1, data); - if ( data[0] & 4 ) { - hpna_get_registers(dev, 0x21, 2, data); + int i; + __u8 data[4] = { index, 0, 0, 0x02 }; + + pegasus_set_registers(dev, 0x20, 4, data); + for (i = 0; i < 100; i++) { + pegasus_get_registers(dev, 0x23, 1, data); + if (data[0] & 4) { + pegasus_get_registers(dev, 0x21, 2, data); *retdata = *(__u16 *)data; - return 0; + return 0; } } + warn("read_srom_word() failed"); - return 1; + return 1; } -/*** end ***/ - - - -int get_node_id( struct usb_device *dev, __u8 *id ) +static int pegasus_get_node_id(struct usb_device *dev, __u8 *id) { - int i; - - for ( i=0; i<3; i++ ) { - if ( read_srom_word(dev, i, (__u16 *)&id[i*2] ) ) - return 1; - } - return 0; + int i; + for (i = 0; i < 3; i++) + if (pegasus_read_srom_word(dev, i, (__u16 *)&id[i * 2])) + return 1; + return 0; } - -static int reset_mac( struct usb_device *dev ) +static int pegasus_reset_mac(struct usb_device *dev) { - __u8 data = 0x8; - int i; - - hpna_set_register( dev, 1, 0x08 ); - for ( i=0; i<100; i++ ) { - hpna_get_registers( dev, 1, 1, &data); - if ( !(data & 0x08) ) { - if ( loopback & 1 ) - return 0; - else if ( loopback & 2 ) { - write_phy_word( dev, 0, 0x4000 ); - /*return 0;*/ - } - hpna_set_register( dev, 0x7e, 0x24 ); - hpna_set_register( dev, 0x7e, 0x27 ); - return 0; + __u8 data = 0x8; + int i; + + pegasus_set_register(dev, 1, data); + for (i = 0; i < 100; i++) { + pegasus_get_registers(dev, 1, 1, &data); + if (~data & 0x08) { + if (loopback & 1) + return 0; + if (loopback & 2) + pegasus_write_phy_word(dev, 0, 0x4000); + pegasus_set_register(dev, 0x7e, 0x24); + pegasus_set_register(dev, 0x7e, 0x27); + return 0; } } + return 1; } - -int start_net( struct net_device *dev, struct usb_device *usb_dev ) +static int pegasus_start_net(struct net_device *dev, struct usb_device *usb) { - __u16 partmedia, temp; - __u8 node_id[6]; - __u8 data[4]; - - if ( get_node_id(usb_dev, node_id) ) - return 1; - hpna_set_registers(usb_dev, 0x10, 6, node_id); + __u16 partmedia, temp; + __u8 node_id[6]; + __u8 data[4]; + + if (pegasus_get_node_id(usb, node_id)) + return 1; + + pegasus_set_registers(usb, 0x10, 6, node_id); memcpy(dev->dev_addr, node_id, 6); - if ( read_phy_word(usb_dev, 1, &temp) ) - return 2; - if ( !(temp & 4) ) { - if ( loopback ) - goto ok; + if (pegasus_read_phy_word(usb, 1, &temp)) + return 2; + + if ((~temp & 4) && !loopback) { err("link NOT established - %x", temp); - return 3; - } -ok: - if ( read_phy_word(usb_dev, 5, &partmedia) ) - return 4; - temp = partmedia; - partmedia &= 0x1f; - if ( partmedia != 1 ) { - err("party FAIL %x", temp); - return 5; + return 3; } - partmedia = temp; - if ( partmedia & 0x100 ) - data[1] = 0x30; - else { - if ( partmedia & 0x80 ) - data[1] = 0x10; - else - data[1] = 0; + + if (pegasus_read_phy_word(usb, 5, &partmedia)) + return 4; + + if ((partmedia & 0x1f) != 1) { + err("party FAIL %x", partmedia); + return 5; } - - data[0] = 0xc9; - data[2] = (loopback & 1) ? 0x08 : 0x00; - - hpna_set_registers(usb_dev, 0, 3, data); - - return 0; -} + data[0] = 0xc9; + data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0); + data[2] = (loopback & 1) ? 0x08 : 0x00; -static void hpna_read_irq( purb_t urb ) -{ - struct net_device *net_dev = urb->context; - usb_hpna_t *hpna = net_dev->priv; - int count = urb->actual_length, res; - int rx_status = *(int *)(hpna->rx_buff + count - 4); + pegasus_set_registers(usb, 0, 3, data); + return 0; +} - if ( urb->status ) { - info( "%s: RX status %d\n", net_dev->name, urb->status ); +static void pegasus_read_bulk(struct urb *urb) +{ + struct pegasus *pegasus = urb->context; + struct net_device *net = pegasus->net; + int count = urb->actual_length, res; + int rx_status = *(int *)(pegasus->rx_buff + count - 4); + struct sk_buff *skb; + __u16 pkt_len; + + if (urb->status) { + info("%s: RX status %d", net->name, urb->status); goto goon; } - if ( !count ) + if (!count) goto goon; -/* if ( rx_status & 0x00010000 ) +#if 0 + if (rx_status & 0x00010000) + goto goon; +#endif + if (rx_status & 0x000e0000) { + + dbg("%s: error receiving packet %x", net->name, rx_status & 0xe0000); + pegasus->stats.rx_errors++; + if(rx_status & 0x060000) pegasus->stats.rx_length_errors++; + if(rx_status & 0x080000) pegasus->stats.rx_crc_errors++; + if(rx_status & 0x100000) pegasus->stats.rx_frame_errors++; + goto goon; -*/ - if ( rx_status & 0x000e0000 ) { - dbg("%s: error receiving packet %x", - net_dev->name, rx_status & 0xe0000); - hpna->stats.rx_errors++; - if(rx_status & 0x060000) hpna->stats.rx_length_errors++; - if(rx_status & 0x080000) hpna->stats.rx_crc_errors++; - if(rx_status & 0x100000) hpna->stats.rx_frame_errors++; - } else { - struct sk_buff *skb; - __u16 pkt_len = (rx_status & 0xfff) - 8; - - - if((skb = dev_alloc_skb(pkt_len+2)) != NULL ) { - skb->dev = net_dev; - skb_reserve(skb, 2); - eth_copy_and_sum(skb, hpna->rx_buff, pkt_len, 0); - skb_put(skb, pkt_len); - } else - goto goon; - skb->protocol = eth_type_trans(skb, net_dev); - netif_rx(skb); - hpna->stats.rx_packets++; - hpna->stats.rx_bytes += pkt_len; } + + pkt_len = (rx_status & 0xfff) - 8; + + if(!(skb = dev_alloc_skb(pkt_len+2))) + goto goon; + + skb->dev = net; + skb_reserve(skb, 2); + eth_copy_and_sum(skb, pegasus->rx_buff, pkt_len, 0); + skb_put(skb, pkt_len); + + skb->protocol = eth_type_trans(skb, net); + netif_rx(skb); + pegasus->stats.rx_packets++; + pegasus->stats.rx_bytes += pkt_len; + goon: - if ( (res = usb_submit_urb( &hpna->rx_urb )) ) - warn("failed rx_urb %d", res); + if ((res = usb_submit_urb(&pegasus->rx_urb))) + warn("(prb)failed rx_urb %d", res); } - -static void hpna_irq( urb_t *urb) +static void pegasus_irq(urb_t *urb) { - if( urb->status ) { + if(urb->status) { __u8 *d = urb->transfer_buffer; printk("txst0 %x, txst1 %x, rxst %x, rxlst0 %x, rxlst1 %x, wakest %x", - d[0], d[1], d[2], d[3], d[4], d[5] ); + d[0], d[1], d[2], d[3], d[4], d[5]); } } - -static void hpna_write_irq( purb_t urb ) +static void pegasus_write_bulk(struct urb *urb) { - struct net_device *net_dev = urb->context; - usb_hpna_t *hpna = net_dev->priv; + struct pegasus *pegasus = urb->context; + spin_lock(&pegasus->pegasus_lock); - spin_lock( &hpna->hpna_lock ); - - if ( urb->status ) - info("%s: TX status %d\n", net_dev->name, urb->status); - netif_wake_queue( net_dev ); + if (urb->status) + info("%s: TX status %d", pegasus->net->name, urb->status); + netif_wake_queue(pegasus->net); - spin_unlock( &hpna->hpna_lock ); + spin_unlock(&pegasus->pegasus_lock); } - -static void tx_timeout( struct net_device *dev ) +static void pegasus_tx_timeout(struct net_device *net) { - usb_hpna_t *hpna = dev->priv; + struct pegasus *pegasus = net->priv; - warn( "%s: Tx timed out. Reseting...", dev->name ); - hpna->stats.tx_errors++; - dev->trans_start = jiffies; - netif_wake_queue( dev ); -} + warn("%s: Tx timed out. Reseting...", net->name); + pegasus->stats.tx_errors++; + net->trans_start = jiffies; + netif_wake_queue(net); +} -static int hpna_start_xmit( struct sk_buff *skb, struct net_device *net_dev ) +static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net) { - usb_hpna_t *hpna = (usb_hpna_t *)net_dev->priv; - int count = skb->len+2 % 64 ? skb->len+2 : skb->len+3; - int res; + struct pegasus *pegasus = net->priv; + int count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3; + int res; - spin_lock( &hpna->hpna_lock ); + spin_lock(&pegasus->pegasus_lock); - netif_stop_queue( net_dev ); - ((__u16 *)hpna->tx_buff)[0] = skb->len; - memcpy(hpna->tx_buff+2, skb->data, skb->len); - (&hpna->tx_urb)->transfer_buffer_length = count; - if ( (res = usb_submit_urb( &hpna->tx_urb )) ) { + netif_stop_queue(net); + + ((__u16 *)pegasus->tx_buff)[0] = skb->len; + memcpy(pegasus->tx_buff+2, skb->data, skb->len); + (&pegasus->tx_urb)->transfer_buffer_length = count; + + if ((res = usb_submit_urb(&pegasus->tx_urb))) { warn("failed tx_urb %d", res); - hpna->stats.tx_errors++; - netif_start_queue( net_dev ); + pegasus->stats.tx_errors++; + netif_start_queue(net); } else { - hpna->stats.tx_packets++; - hpna->stats.tx_bytes += skb->len; - net_dev->trans_start = jiffies; + pegasus->stats.tx_packets++; + pegasus->stats.tx_bytes += skb->len; + net->trans_start = jiffies; } - dev_kfree_skb( skb ); - spin_unlock( &hpna->hpna_lock ); - return 0; -} + dev_kfree_skb(skb); -static struct net_device_stats *hpna_netdev_stats( struct net_device *dev ) -{ - return &((usb_hpna_t *)dev->priv)->stats; + spin_unlock(&pegasus->pegasus_lock); + + return 0; } -static int hpna_open( struct net_device *net_dev ) +static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev) { - usb_hpna_t *hpna = (usb_hpna_t *)net_dev->priv; - int res; + return &((struct pegasus *)dev->priv)->stats; +} - if ( hpna->active ) - return -EBUSY; - else - hpna->active = 1; +static int pegasus_open(struct net_device *net) +{ + struct pegasus *pegasus = (struct pegasus *)net->priv; + int res; - if ( start_net(net_dev, hpna->usb_dev) ) { - err("can't start_net()"); - return -EIO; + if ((res = pegasus_start_net(net, pegasus->usb))) { + err("can't start_net() - %d", res); + return -EIO; } - if ( (res = usb_submit_urb( &hpna->rx_urb )) ) - warn("failed rx_urb %d", res); + if ((res = usb_submit_urb(&pegasus->rx_urb))) + warn("(open)failed rx_urb %d", res); -/* usb_submit_urb( &hpna->intr_urb );*/ - netif_start_queue( net_dev ); +/* usb_submit_urb(&pegasus->intr_urb);*/ + netif_start_queue(net); MOD_INC_USE_COUNT; return 0; } - -static int hpna_close( struct net_device *net_dev ) +static int pegasus_close(struct net_device *net) { - usb_hpna_t *hpna = net_dev->priv; - - - netif_stop_queue( net_dev ); + struct pegasus *pegasus = net->priv; - usb_unlink_urb( &hpna->rx_urb ); - usb_unlink_urb( &hpna->tx_urb ); -/* usb_unlink_urb( hpna->intr_urb );*/ + netif_stop_queue(net); - hpna->active = 0; + usb_unlink_urb(&pegasus->rx_urb); + usb_unlink_urb(&pegasus->tx_urb); +/* usb_unlink_urb(&pegasus->intr_urb); */ MOD_DEC_USE_COUNT; return 0; } - -static int hpna_ioctl( struct net_device *dev, struct ifreq *rq, int cmd ) +static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) { - __u16 *data = (__u16 *)&rq->ifr_data; - usb_hpna_t *hpna = dev->priv; + __u16 *data = (__u16 *)&rq->ifr_data; + struct pegasus *pegasus = net->priv; - switch( cmd ) { - case SIOCDEVPRIVATE: + switch(cmd) { + case SIOCDEVPRIVATE: data[0] = 1; case SIOCDEVPRIVATE+1: - read_phy_word(hpna->usb_dev, data[1] & 0x1f, &data[3]); - return 0; + pegasus_read_phy_word(pegasus->usb, data[1] & 0x1f, &data[3]); + return 0; case SIOCDEVPRIVATE+2: - if ( !capable(CAP_NET_ADMIN) ) - return -EPERM; - write_phy_word(hpna->usb_dev, data[1] & 0x1f, data[2]); - return 0; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + pegasus_write_phy_word(pegasus->usb, data[1] & 0x1f, data[2]); + return 0; default: - return -EOPNOTSUPP; + return -EOPNOTSUPP; } } - -static void set_rx_mode( struct net_device *net_dev ) +static void pegasus_set_rx_mode(struct net_device *net) { - usb_hpna_t *hpna=net_dev->priv; + struct pegasus *pegasus = net->priv; - netif_stop_queue( net_dev ); - - if ( net_dev->flags & IFF_PROMISC ) { - info("%s: Promiscuous mode enabled", net_dev->name); - hpna_set_register( hpna->usb_dev, 2, 0x04 ); - } else if ((net_dev->mc_count > multicast_filter_limit) || - (net_dev->flags & IFF_ALLMULTI)) { - hpna_set_register(hpna->usb_dev, 0, 0xfa); - hpna_set_register(hpna->usb_dev, 2, 0); + netif_stop_queue(net); + + if (net->flags & IFF_PROMISC) { + info("%s: Promiscuous mode enabled", net->name); + pegasus_set_register(pegasus->usb, 2, 0x04); + } else if ((net->mc_count > multicast_filter_limit) || + (net->flags & IFF_ALLMULTI)) { + pegasus_set_register(pegasus->usb, 0, 0xfa); + pegasus_set_register(pegasus->usb, 2, 0); } else { - dbg("%s: set Rx mode", net_dev->name); + dbg("%s: set Rx mode", net->name); } - netif_wake_queue( net_dev ); + netif_wake_queue(net); } - -static void * usb_hpna_probe( struct usb_device *dev, unsigned int ifnum ) +static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) { - struct net_device *net_dev; - usb_hpna_t *hpna = &usb_dev_hpna; - + struct net_device *net; + struct pegasus *pegasus; - - if ( dev->descriptor.idVendor != ADMTEK_VENDOR_ID || - dev->descriptor.idProduct != ADMTEK_HPNA_PEGASUS ) { - return NULL; + if (dev->descriptor.idVendor != ADMTEK_VENDOR_ID || + dev->descriptor.idProduct != ADMTEK_DEVICE_ID_PEGASUS) { + return NULL; } - printk("USB HPNA Pegasus found\n"); - - if ( usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { + if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { err("usb_set_configuration() failed"); return NULL; } - hpna->usb_dev = dev; - - hpna->rx_pipe = usb_rcvbulkpipe(hpna->usb_dev, 1); - hpna->tx_pipe = usb_sndbulkpipe(hpna->usb_dev, 2); - hpna->intr_pipe = usb_rcvintpipe(hpna->usb_dev, 0); - - if ( reset_mac(dev) ) { - err("can't reset MAC"); + if(!(pegasus = kmalloc(sizeof(struct pegasus), GFP_KERNEL))) { + err("out of memory allocating device structure"); + return NULL; } + memset(pegasus, 0, sizeof(struct pegasus)); - hpna->present = 1; - - if(!(hpna->rx_buff=kmalloc(MAX_MTU, GFP_KERNEL))) { - err("not enough mem for out buff"); - return NULL; - } - if(!(hpna->tx_buff=kmalloc(MAX_MTU, GFP_KERNEL))) { - kfree_s(hpna->rx_buff, MAX_MTU); - err("not enough mem for out buff"); - return NULL; + if (pegasus_reset_mac(dev)) { + err("can't reset MAC"); + kfree(pegasus); + return NULL; } - - net_dev = init_etherdev( 0, 0 ); - hpna->net_dev = net_dev; - net_dev->priv = hpna; - net_dev->open = hpna_open; - net_dev->stop = hpna_close; - net_dev->watchdog_timeo = TX_TIMEOUT; - net_dev->tx_timeout = tx_timeout; - net_dev->do_ioctl = hpna_ioctl; - net_dev->hard_start_xmit = hpna_start_xmit; - net_dev->set_multicast_list = set_rx_mode; - net_dev->get_stats = hpna_netdev_stats; - net_dev->mtu = HPNA_MTU; - hpna->hpna_lock = SPIN_LOCK_UNLOCKED; - - FILL_BULK_URB( &hpna->rx_urb, hpna->usb_dev, hpna->rx_pipe, - hpna->rx_buff, MAX_MTU, hpna_read_irq, net_dev ); - FILL_BULK_URB( &hpna->tx_urb, hpna->usb_dev, hpna->tx_pipe, - hpna->tx_buff, MAX_MTU, hpna_write_irq, net_dev ); - FILL_INT_URB( &hpna->intr_urb, hpna->usb_dev, hpna->intr_pipe, - hpna->intr_buff, 8, hpna_irq, net_dev, 250 ); -/* list_add( &hpna->list, &hpna_list );*/ - - return net_dev; + net = init_etherdev(0, 0); + net->priv = pegasus; + net->open = pegasus_open; + net->stop = pegasus_close; + net->watchdog_timeo = PEGASUS_TX_TIMEOUT; + net->tx_timeout = pegasus_tx_timeout; + net->do_ioctl = pegasus_ioctl; + net->hard_start_xmit = pegasus_start_xmit; + net->set_multicast_list = pegasus_set_rx_mode; + net->get_stats = pegasus_netdev_stats; + net->mtu = PEGASUS_MTU; + + pegasus->usb = dev; + pegasus->net = net; + pegasus->pegasus_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, 0), + pegasus->intr_buff, 8, pegasus_irq, pegasus, 250); + + + printk(KERN_INFO "%s: ADMtek AN986 Pegasus usb device\n", net->name); + + return pegasus; } - -static void usb_hpna_disconnect( struct usb_device *dev, void *ptr ) +static void pegasus_disconnect(struct usb_device *dev, void *ptr) { - struct net_device *net_dev = ptr; - struct usb_hpna *hpna = net_dev->priv; + struct pegasus *pegasus = ptr; + if (!pegasus) { + warn("unregistering non-existant device"); + return; + } - if ( net_dev->flags & IFF_UP ) - dev_close(net_dev); - - unregister_netdev( net_dev ); + if (pegasus->net->flags & IFF_UP) + dev_close(pegasus->net); - if ( !hpna ) /* should never happen */ - return; - - usb_unlink_urb( &hpna->rx_urb ); - usb_unlink_urb( &hpna->tx_urb ); -/* usb_unlink_urb( &hpna->intr_urb );*/ - kfree_s(hpna->rx_buff, MAX_MTU); - kfree_s(hpna->tx_buff, MAX_MTU); + unregister_netdev(pegasus->net); - hpna->usb_dev = NULL; - hpna->present = 0; + usb_unlink_urb(&pegasus->rx_urb); + usb_unlink_urb(&pegasus->tx_urb); +/* usb_unlink_urb(&pegasus->intr_urb);*/ - printk("USB HPNA disconnected\n"); + kfree(pegasus); } - -static struct usb_driver usb_hpna_driver = { - "ADMtek \"Pegasus\" USB Ethernet", - usb_hpna_probe, - usb_hpna_disconnect, - {NULL, NULL} +static struct usb_driver pegasus_driver = { + name: "pegasus", + probe: pegasus_probe, + disconnect: pegasus_disconnect, }; - - -static int __init start_hpna( void ) +int __init pegasus_init(void) { printk( version ); - return usb_register( &usb_hpna_driver ); + return usb_register(&pegasus_driver); } - -static void __exit stop_hpna( void ) +void __exit pegasus_exit(void) { - usb_deregister( &usb_hpna_driver ); + usb_deregister(&pegasus_driver); } - -module_init( start_hpna ); -module_exit( stop_hpna ); +module_init(pegasus_init); +module_exit(pegasus_exit); diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index d5c245bf1..20dc5dde5 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -6,14 +6,15 @@ SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) +MOD_IN_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) # The target object and module list name. -O_TARGET := usbdrv.o -M_OBJS := -O_OBJS := -MOD_LIST_NAME := USB_MODULES +O_TARGET := serial.o +M_OBJS := usb-serial.o +O_OBJS := usb-serial.o +#MOD_LIST_NAME := USB_MODULES # Objects that export symbols. diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index c3b4ebc2e..480c6252d 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -236,7 +236,7 @@ static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td) static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth) { struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct uhci_td *td, *prevtd = NULL; + struct uhci_td *td, *prevtd; if (!urbp) return; @@ -617,6 +617,8 @@ static int uhci_submit_control(urb_t *urb) return -EINPROGRESS; } +static int usb_control_retrigger_status(urb_t *urb); + static int uhci_result_control(urb_t *urb) { struct urb_priv *urbp = urb->hcpriv; @@ -630,6 +632,9 @@ static int uhci_result_control(urb_t *urb) if (!td) return -EINVAL; + if (urbp->short_control_packet) + goto status_phase; + /* The first TD is the SETUP phase, check the status, but skip */ /* the count */ status = uhci_status_bits(td->status); @@ -653,10 +658,9 @@ static int uhci_result_control(urb_t *urb) /* If SPD is set then we received a short packet */ /* There will be no status phase at the end */ - /* FIXME: Re-setup the queue to run the STATUS phase? */ if ((td->status & TD_CTRL_SPD) && (uhci_actual_length(td->status) < uhci_expected_length(td->info))) - return 0; + return usb_control_retrigger_status(urb); if (status) goto td_error; @@ -664,12 +668,13 @@ static int uhci_result_control(urb_t *urb) td = td->list.next; } +status_phase: /* Control status phase */ status = uhci_status_bits(td->status); /* APC BackUPS Pro kludge */ - /* It tries to send all of the descriptor instead of */ - /* the amount we requested */ + /* It tries to send all of the descriptor instead of the amount */ + /* we requested */ if (td->status & TD_CTRL_IOC && status & TD_CTRL_ACTIVE && status & TD_CTRL_NAK) @@ -700,6 +705,47 @@ td_error: return uhci_map_status(status, uhci_packetout(td->info)); } +static int usb_control_retrigger_status(urb_t *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci *uhci = urb->dev->bus->hcpriv; + struct uhci_td *td, *nexttd; + + urbp->short_control_packet = 1; + + /* Delete all of the TD's except for the status TD at the end */ + td = urbp->list.begin; + while (td && td->list.next) { + nexttd = td->list.next; + + uhci_remove_td_from_urb(urb, td); + + uhci_remove_td(uhci, td); + + uhci_free_td(td); + + td = nexttd; + } + + /* Create a new QH to avoid pointer overwriting problems */ + uhci_remove_qh(uhci, urbp->qh); + + urbp->qh = uhci_alloc_qh(urb->dev); + if (!urbp->qh) + return -ENOMEM; + + /* One TD, who cares about Breadth first? */ + uhci_insert_tds_in_qh(urbp->qh, urb, 0); + + /* Low speed or small transfers gets a different queue and treatment */ + if (urb->pipe & TD_CTRL_LS) + uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, urbp->qh); + else + uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, urbp->qh); + + return -EINPROGRESS; +} + /* * Interrupt transfers */ diff --git a/drivers/usb/uhci.h b/drivers/usb/uhci.h index 62d4e772e..9f4c45e96 100644 --- a/drivers/usb/uhci.h +++ b/drivers/usb/uhci.h @@ -338,7 +338,11 @@ struct uhci { struct urb_priv { struct uhci_qh *qh; /* QH for this URB */ - int fsbr; + int fsbr; /* Did this URB turn on FSBR? */ + + char short_control_packet; /* If we get a short packet during */ + /* a control transfer, retrigger */ + /* the status phase */ unsigned long inserttime; /* In jiffies */ diff --git a/drivers/usb/usb-core.c b/drivers/usb/usb-core.c index 2bba728d9..cb6a70621 100644 --- a/drivers/usb/usb-core.c +++ b/drivers/usb/usb-core.c @@ -31,7 +31,6 @@ void usb_major_cleanup(void); int usb_audio_init(void); int usb_cpia_init(void); int usb_ibmcam_init(void); -int usb_ov511_init(void); int dabusb_init(void); int plusb_init(void); @@ -78,12 +77,12 @@ int usb_init(void) #ifdef CONFIG_USB_IBMCAM usb_ibmcam_init(); #endif -#ifdef CONFIG_USB_OV511 - usb_ov511_init(); -#endif #ifdef CONFIG_USB_DABUSB dabusb_init(); #endif +#ifdef CONFIG_USB_DSBR + dsbr100_init(); +#endif #ifdef CONFIG_USB_PLUSB plusb_init(); #endif diff --git a/drivers/usb/usb-storage.c b/drivers/usb/usb-storage.c index 0e2ab20a4..956e80a0a 100644 --- a/drivers/usb/usb-storage.c +++ b/drivers/usb/usb-storage.c @@ -6,9 +6,9 @@ * Further reference: * This driver is based on the 'USB Mass Storage Class' document. This * describes in detail the protocol used to communicate with such - * devices. Clearly, the designers had SCSI commands in mind when they - * created this document. The commands are all similar to commands - * in the SCSI-II specification. + * devices. Clearly, the designers had SCSI and ATAPI commands in mind + * when they created this document. The commands are all very similar + * to commands in the SCSI-II and ATAPI specifications. * * It is important to note that in a number of cases this class exhibits * class-specific exemptions from the USB specification. Notably the @@ -65,8 +65,6 @@ unsigned char us_direction[256/8] = { static int my_host_number; -int usb_stor_debug = 1; - struct us_data; typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*); @@ -74,7 +72,7 @@ typedef int (*trans_reset)(struct us_data*); typedef void (*proto_cmnd)(Scsi_Cmnd*, struct us_data*); struct us_data { - struct us_data *next; /* next device */ + struct us_data *next; /* next device */ struct usb_device *pusb_dev; /* this usb_device */ unsigned int flags; /* from filter initially */ __u8 ifnum; /* interface number */ @@ -93,15 +91,17 @@ struct us_data { int host_number; /* to find us */ int host_no; /* allocated by scsi */ Scsi_Cmnd *srb; /* current srb */ + Scsi_Cmnd *queue_srb; /* the single queue slot */ int action; /* what to do */ - wait_queue_head_t waitq; /* thread waits */ - wait_queue_head_t ip_waitq; /* for CBI interrupts */ + struct semaphore ip_waitq; /* for CBI interrupts */ __u16 ip_data; /* interrupt data */ int ip_wanted; /* needed */ int pid; /* control thread */ struct semaphore *notify; /* wait for thread to begin */ void *irq_handle; /* for USB int requests */ unsigned int irqpipe; /* pipe for release_irq */ + struct semaphore sleeper; /* to sleep on */ + struct semaphore queue_exclusion; /* to protect data structs */ }; /* @@ -129,117 +129,100 @@ static struct usb_driver storage_driver = { * Data transfer routines ***********************************************************************/ -/* Transfer one buffer (breaking into packets if necessary) - * Note that this function is necessary because if the device NAKs, we - * need to know that information directly +/* FIXME: the names of these functions are poorly choosen. */ + +/* + * Transfer one SCSI scatter-gather buffer via bulk transfer + * + * Note that this function is necessary because we want the ability to + * use scatter-gather memory. Good performance is achived by a combination + * of scatter-gather and clustering (which makes each chunk bigger). * - * FIXME: is the above true? Or will the URB status show ETIMEDOUT after - * retrying several times allready? Perhaps this is the way we should - * be going anyway? + * Note that the lower layer will always retry when a NAK occurs, up to the + * timeout limit. Thus we don't have to worry about it for individual + * packets. */ -static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length) +static int us_bulk_transfer(struct us_data *us, int pipe, + char *buf, int length) { - int max_size; - int this_xfer; int result; int partial; - int maxtry; - - /* determine the maximum packet size for these transfers */ - max_size = usb_maxpacket(us->pusb_dev, - pipe, usb_pipeout(pipe)) * 16; - - /* while we have data left to transfer */ - while (length) { - - /* calculate how long this will be -- maximum or a remainder */ - this_xfer = length > max_size ? max_size : length; - length -= this_xfer; - - /* FIXME: this number is totally outrageous. We need to pick - * a better (smaller) number). - */ - - /* setup the retry counter */ - maxtry = 100; - - /* set up the transfer loop */ - do { - /* transfer the data */ - US_DEBUGP("Bulk xfer 0x%x(%d) try #%d\n", - (unsigned int)buf, this_xfer, 101 - maxtry); - result = usb_bulk_msg(us->pusb_dev, pipe, buf, - this_xfer, &partial, HZ*5); - US_DEBUGP("bulk_msg returned %d xferred %d/%d\n", - result, partial, this_xfer); - - /* 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); - } - - /* update to show what data was transferred */ - this_xfer -= partial; - buf += partial; - - /* NAK - we retry a few times */ - if (result == -ETIMEDOUT) { - US_DEBUGP("us_one_transfer: device NAKed\n"); - - /* if our try counter reaches 0, bail out */ - if (!maxtry--) - return -ETIMEDOUT; + /* transfer the data */ + US_DEBUGP("Bulk xfer 0x%x(%d)\n", (unsigned int)buf, length); + result = usb_bulk_msg(us->pusb_dev, pipe, buf, length, &partial, HZ*5); + US_DEBUGP("bulk_msg returned %d xferred %d/%d\n", + result, partial, 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); + } - /* just continue the while loop */ - continue; - } - - /* other errors (besides NAK) -- we just bail out*/ - if (result != 0) { - US_DEBUGP("us_one_transfer: device returned error %d\n", result); - return result; - } + /* did we send all the data? */ + if (partial == length) { + return US_BULK_TRANSFER_GOOD; + } - /* continue until this transfer is done */ - } while ( this_xfer ); + /* uh oh... we have an error code, so something went wrong. */ + if (result) { + /* NAK - that means we've retried a few times allready */ + if (result == -ETIMEDOUT) { + US_DEBUGP("us_bulk_transfer: device NAKed\n"); + } + return US_BULK_TRANSFER_FAILED; } - /* if we get here, we're done and successful */ - return 0; + /* no error code, so we must have transferred some data, + * just not all of it */ + return US_BULK_TRANSFER_SHORT; } -static unsigned int us_transfer_length(Scsi_Cmnd *srb); - -/* transfer one SCSI command, using scatter-gather if requested */ -/* FIXME: what do the return codes here mean? */ -static int us_transfer(Scsi_Cmnd *srb, int dir_in) +/* + * Transfer an entire SCSI command's worth of data payload over the bulk + * pipe. + * + * Note that this uses us_bulk_transfer to achive it's goals -- this + * function simply determines if we're going to use scatter-gather or not, + * and acts appropriately. For now, it also re-interprets the error codes. + */ +static void us_transfer(Scsi_Cmnd *srb, int dir_in) { - struct us_data *us = (struct us_data *)srb->host_scribble; + struct us_data *us; int i; int result = -1; - unsigned int pipe = dir_in ? usb_rcvbulkpipe(us->pusb_dev, us->ep_in) : - usb_sndbulkpipe(us->pusb_dev, us->ep_out); + unsigned int pipe; + struct scatterlist *sg; - /* FIXME: stop transferring data at us_transfer_length(), not - * bufflen */ + /* calculate the appropriate pipe information */ + us = (struct us_data*) srb->host_scribble; + if (dir_in) + pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + else + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + + /* are we scatter-gathering? */ if (srb->use_sg) { - struct scatterlist *sg = (struct scatterlist *) srb->request_buffer; + /* loop over all the scatter gather structures and + * make the appropriate requests for each, until done + */ + sg = (struct scatterlist *) srb->request_buffer; for (i = 0; i < srb->use_sg; i++) { - result = us_one_transfer(us, pipe, sg[i].address, sg[i].length); + result = us_bulk_transfer(us, pipe, sg[i].address, + sg[i].length); if (result) break; } } else - result = us_one_transfer(us, pipe, srb->request_buffer, - us_transfer_length(srb)); + /* no scatter-gather, just make the request */ + result = us_bulk_transfer(us, pipe, srb->request_buffer, + srb->request_bufflen); - if (result < 0) - US_DEBUGP("us_transfer returning error %d\n", result); - return result; + /* return the result in the data structure itself */ + srb->result = result; } /* calculate the length of the data transfer (not the command) for any @@ -265,6 +248,9 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb) case MODE_SENSE: return srb->cmnd[4]; + case READ_CAPACITY: + return 8; + case LOG_SENSE: case MODE_SENSE_10: return (srb->cmnd[7] << 8) + srb->cmnd[8]; @@ -274,8 +260,9 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb) } if (srb->use_sg) { - struct scatterlist *sg = (struct scatterlist *) srb->request_buffer; + struct scatterlist *sg; + sg = (struct scatterlist *) srb->request_buffer; for (i = 0; i < srb->use_sg; i++) { total += sg[i].length; } @@ -289,12 +276,148 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb) * Protocol routines ***********************************************************************/ -static int CB_transport(Scsi_Cmnd *srb, struct us_data *us); -static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us); +static void ATAPI_command(Scsi_Cmnd *srb, struct us_data *us) +{ + int old_cmnd = 0; + int result; + + /* Fix some commands -- this is a form of mode translation + * ATAPI devices only accept 12 byte long commands + * + * NOTE: This only works because a Scsi_Cmnd struct field contains + * a unsigned char cmnd[12], so we know we have storage available + */ + + /* set command length to 12 bytes */ + srb->cmd_len = 12; + + /* determine the correct (or minimum) data length for these commands */ + switch (us->srb->cmnd[0]) { + + /* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */ + case MODE_SENSE: + case MODE_SELECT: + /* save the command so we can tell what it was */ + old_cmnd = srb->cmnd[0]; + + srb->cmnd[11] = 0; + srb->cmnd[10] = 0; + srb->cmnd[9] = 0; + srb->cmnd[8] = srb->cmnd[4]; + srb->cmnd[7] = 0; + srb->cmnd[6] = 0; + srb->cmnd[5] = 0; + srb->cmnd[4] = 0; + srb->cmnd[3] = 0; + srb->cmnd[2] = srb->cmnd[2]; + srb->cmnd[1] = srb->cmnd[1]; + srb->cmnd[0] = srb->cmnd[0] | 0x40; + break; + + /* change READ_6/WRITE_6 to READ_10/WRITE_10, which + * are ATAPI commands */ + case WRITE_6: + case READ_6: + srb->cmnd[11] = 0; + srb->cmnd[10] = 0; + srb->cmnd[9] = 0; + srb->cmnd[8] = srb->cmnd[4]; + srb->cmnd[7] = 0; + srb->cmnd[6] = 0; + srb->cmnd[5] = srb->cmnd[3]; + srb->cmnd[4] = srb->cmnd[2]; + srb->cmnd[3] = srb->cmnd[1] & 0x1F; + srb->cmnd[2] = 0; + srb->cmnd[1] = srb->cmnd[1] & 0xE0; + srb->cmnd[0] = srb->cmnd[0] | 0x20; + break; + } /* end switch on cmnd[0] */ + + /* send the command to the transport layer */ + result = us->transport(srb, us); + + /* If we got a short transfer, but it was for a command that + * can have short transfers, we're actually okay + */ + if ((us->srb->result == US_BULK_TRANSFER_SHORT) && + ((us->srb->cmnd[0] == REQUEST_SENSE) || + (us->srb->cmnd[0] == INQUIRY) || + (us->srb->cmnd[0] == MODE_SENSE) || + (us->srb->cmnd[0] == LOG_SENSE) || + (us->srb->cmnd[0] == MODE_SENSE_10))) { + us->srb->result = DID_OK; + } + + /* + * If we have an error, we're going to do a + * REQUEST_SENSE automatically + */ + if (result != USB_STOR_TRANSPORT_GOOD) { + int temp_result; + void* old_request_buffer; + int old_sg; + + US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n"); + + us->srb->cmnd[0] = REQUEST_SENSE; + us->srb->cmnd[1] = 0; + us->srb->cmnd[2] = 0; + us->srb->cmnd[3] = 0; + us->srb->cmnd[4] = 18; + us->srb->cmnd[5] = 0; + + /* set the buffer length for transfer */ + old_request_buffer = us->srb->request_buffer; + old_sg = us->srb->use_sg; + us->srb->request_bufflen = 18; + us->srb->request_buffer = us->srb->sense_buffer; + + /* FIXME: what if this command fails? */ + temp_result = us->transport(us->srb, us); + US_DEBUGP("-- Result from auto-sense is %d\n", temp_result); + US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n", + us->srb->sense_buffer[2] & 0xf, + us->srb->sense_buffer[12], + us->srb->sense_buffer[13]); + + /* set the result so the higher layers expect this data */ + us->srb->result = CHECK_CONDITION; + + /* we're done here */ + us->srb->request_buffer = old_request_buffer; + us->srb->use_sg = old_sg; + return; + } + + /* Fix the MODE_SENSE data if we translated the command + */ + if (old_cmnd == MODE_SENSE) { + unsigned char *dta = (unsigned char *)us->srb->request_buffer; + + /* FIXME: we need to compress the entire data structure here + */ + dta[0] = dta[1]; /* data len */ + dta[1] = dta[2]; /* med type */ + dta[2] = dta[3]; /* dev-spec prm */ + dta[3] = dta[7]; /* block desc len */ + printk (KERN_DEBUG USB_STORAGE + "new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n", + dta[0], dta[1], dta[2], dta[3]); + } + + /* Fix-up the return data from an INQUIRY command to show + * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us + */ + if (us->srb->cmnd[0] == INQUIRY) { + ((unsigned char *)us->srb->request_buffer)[2] |= 0x2; + } +} + static void ufi_command(Scsi_Cmnd *srb, struct us_data *us) { int old_cmnd = 0; + int result; /* fix some commands -- this is a form of mode translation * UFI devices only accept 12 byte long commands @@ -372,23 +495,31 @@ static void ufi_command(Scsi_Cmnd *srb, struct us_data *us) } /* end switch on cmnd[0] */ /* send the command to the transport layer */ - us->srb->result = us->transport(srb, us); + result = us->transport(srb, us); - /* if we have an error, we're going to do a - * REQUEST_SENSE automatically */ + /* If we got a short transfer, but it was for a command that + * can have short transfers, we're actually okay + */ + if ((us->srb->result == US_BULK_TRANSFER_SHORT) && + ((us->srb->cmnd[0] == REQUEST_SENSE) || + (us->srb->cmnd[0] == INQUIRY) || + (us->srb->cmnd[0] == MODE_SENSE) || + (us->srb->cmnd[0] == LOG_SENSE) || + (us->srb->cmnd[0] == MODE_SENSE_10))) { + us->srb->result = DID_OK; + } - /* FIXME: we should only do this for device - * errors, not system errors */ - if (us->srb->result) { + /* + * If we have an error, we're going to do a + * REQUEST_SENSE automatically + */ + if (result != USB_STOR_TRANSPORT_GOOD) { int temp_result; - int count; void* old_request_buffer; + int old_sg; US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n"); - /* set the result so the higher layers expect this data */ - us->srb->result = CHECK_CONDITION; - us->srb->cmnd[0] = REQUEST_SENSE; us->srb->cmnd[1] = 0; us->srb->cmnd[2] = 0; @@ -398,49 +529,34 @@ static void ufi_command(Scsi_Cmnd *srb, struct us_data *us) /* set the buffer length for transfer */ old_request_buffer = us->srb->request_buffer; + old_sg = us->srb->use_sg; us->srb->request_bufflen = 18; - us->srb->request_buffer = kmalloc(18, GFP_KERNEL); + us->srb->request_buffer = us->srb->sense_buffer; /* FIXME: what if this command fails? */ temp_result = us->transport(us->srb, us); US_DEBUGP("-- Result from auto-sense is %d\n", temp_result); - - /* copy the data from the request buffer to the sense buffer */ - for(count = 0; count < 18; count++) - us->srb->sense_buffer[count] = - ((unsigned char *)(us->srb->request_buffer))[count]; - US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n", us->srb->sense_buffer[2] & 0xf, - us->srb->sense_buffer[12], us->srb->sense_buffer[13]); + us->srb->sense_buffer[12], + us->srb->sense_buffer[13]); + + /* set the result so the higher layers expect this data */ + us->srb->result = CHECK_CONDITION; /* we're done here */ - kfree(us->srb->request_buffer); us->srb->request_buffer = old_request_buffer; + us->srb->use_sg = old_sg; return; } - /* FIXME: if we need to send more data, or recieve data, we should - * do it here. Then, we can do status handling here also. - * - * This includes MODE_SENSE from above + /* Fix the MODE_SENSE data here if we had to translate the command */ if (old_cmnd == MODE_SENSE) { unsigned char *dta = (unsigned char *)us->srb->request_buffer; - /* calculate the new length */ - int length = (dta[0] << 8) + dta[1] + 2; - - /* copy the available data length into the structure */ - us->srb->cmnd[7] = length >> 8; - us->srb->cmnd[8] = length & 0xFF; - - /* send the command to the transport layer */ - us->srb->result = us->transport(srb, us); - - /* FIXME: this assumes that the 2nd attempt is always - * successful convert MODE_SENSE_10 return data format - * to MODE_SENSE_6 format */ + /* FIXME: we need to compress the entire data structure here + */ dta[0] = dta[1]; /* data len */ dta[1] = dta[2]; /* med type */ dta[2] = dta[3]; /* dev-spec prm */ @@ -450,126 +566,18 @@ static void ufi_command(Scsi_Cmnd *srb, struct us_data *us) dta[0], dta[1], dta[2], dta[3]); } - /* FIXME: if this was a TEST_UNIT_READY, and we get a NOT READY/ - * LOGICAL DRIVE NOT READY then we do a START_STOP, and retry - */ - - /* FIXME: here is where we need to fix-up the return data from - * an INQUIRY command to show ANSI SCSI rev 2 - */ - - /* FIXME: The rest of this is bogus. usb_control_msg() will only - * return an error if we've really honked things up. If it just - * needs a START_STOP, then we'll get some data back via - * REQUEST_SENSE -- either way, this belongs at a higher level + /* Fix-up the return data from an INQUIRY command to show + * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us */ - -#if 0 - /* For UFI, if this is the first time we've sent this TEST_UNIT_READY - * command, we can try again - */ - if (!done_start && (us->subclass == US_SC_UFI) - && (cmd[0] == TEST_UNIT_READY) && (result < 0)) { - - /* as per spec try a start command, wait and retry */ - wait_ms(100); - - done_start++; - memset(cmd, 0, sizeof(cmd)); - cmd[0] = START_STOP; - cmd[4] = 1; /* start */ - - result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), - US_CBI_ADSC, - USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, us->ifnum, - cmd, 12, HZ*5); - US_DEBUGP("Next usb_control_msg returns %d\n", result); - - /* allow another retry */ - retry++; - continue; + if (us->srb->cmnd[0] == INQUIRY) { + ((unsigned char *)us->srb->request_buffer)[2] |= 0x2; } -#endif } static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us) { - unsigned int savelen = us->srb->request_bufflen; - unsigned int saveallocation = 0; - -#if 0 - /* force attention on first command */ - if (!us->attention_done) { - if (us->srb->cmnd[0] == REQUEST_SENSE) { - US_DEBUGP("forcing unit attention\n"); - us->attention_done = 1; - - if (us->srb->result == USB_STOR_TRANSPORT_GOOD) { - unsigned char *p = (unsigned char *)us->srb->request_buffer; - - if ((p[2] & 0x0f) != UNIT_ATTENTION) { - p[2] = UNIT_ATTENTION; - p[12] = 0x29; /* power on, reset or bus-reset */ - p[13] = 0; - } /* if ((p[2] & 0x0f) != UNIT_ATTENTION) */ - } /* if (us->srb->result == USB_STORE_TRANSPORT_GOOD) */ - } - } /* if (!us->attention_done) */ -#endif - - /* If the command has a variable-length payload, then we do them - * in two steps -- first we do the minimum, then we recalculate - * then length, and re-issue the command - * - * we use savelen to remember how much buffer we really have - * we use savealloction to remember how much was really requested - */ + unsigned int result = 0; - /* FIXME: remove savelen based on mods to us_transfer_length() */ - switch (us->srb->cmnd[0]) { - case REQUEST_SENSE: - if (us->srb->request_bufflen > 18) - us->srb->request_bufflen = 18; - else - break; - saveallocation = us->srb->cmnd[4]; - us->srb->cmnd[4] = 18; - break; - - case INQUIRY: - if (us->srb->request_bufflen > 36) - us->srb->request_bufflen = 36; - else - break; - saveallocation = us->srb->cmnd[4]; - us->srb->cmnd[4] = 36; - break; - - case MODE_SENSE: - if (us->srb->request_bufflen > 4) - us->srb->request_bufflen = 4; - else - break; - saveallocation = us->srb->cmnd[4]; - us->srb->cmnd[4] = 4; - break; - - case LOG_SENSE: - case MODE_SENSE_10: - if (us->srb->request_bufflen > 8) - us->srb->request_bufflen = 8; - else - break; - saveallocation = (us->srb->cmnd[7] << 8) | us->srb->cmnd[8]; - us->srb->cmnd[7] = 0; - us->srb->cmnd[8] = 8; - break; - - default: - break; - } /* end switch on cmnd[0] */ - /* This code supports devices which do not support {READ|WRITE}_6 * Apparently, neither Windows or MacOS will use these commands, * so some devices do not support them @@ -631,25 +639,33 @@ static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us) US_DEBUGP("Changing WRITE_6 to WRITE_10\n"); US_DEBUG(us_show_command(us->srb)); } - } /* end if (us->flags & US_FL_MODE_XLATE) */ + } /* if (us->flags & US_FL_MODE_XLATE) */ /* send the command to the transport layer */ - us->srb->result = us->transport(us->srb, us); + result = us->transport(us->srb, us); + + /* If we got a short transfer, but it was for a command that + * can have short transfers, we're actually okay + */ + if ((us->srb->result == US_BULK_TRANSFER_SHORT) && + ((us->srb->cmnd[0] == REQUEST_SENSE) || + (us->srb->cmnd[0] == INQUIRY) || + (us->srb->cmnd[0] == MODE_SENSE) || + (us->srb->cmnd[0] == LOG_SENSE) || + (us->srb->cmnd[0] == MODE_SENSE_10))) { + us->srb->result = DID_OK; + } /* if we have an error, we're going to do a REQUEST_SENSE * automatically */ - /* FIXME: we should only do this for device errors, not - * system errors */ - if (us->srb->result) { + if (result != USB_STOR_TRANSPORT_GOOD) { int temp_result; - int count; + int old_sg; void* old_request_buffer; US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n"); - /* set the result so the higher layers expect this data */ - us->srb->result = CHECK_CONDITION; - + /* set up the REQUEST_SENSE command and parameters */ us->srb->cmnd[0] = REQUEST_SENSE; us->srb->cmnd[1] = 0; us->srb->cmnd[2] = 0; @@ -659,115 +675,32 @@ static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us) /* set the buffer length for transfer */ old_request_buffer = us->srb->request_buffer; + old_sg = us->srb->use_sg; us->srb->request_bufflen = 18; - us->srb->request_buffer = kmalloc(18, GFP_KERNEL); + us->srb->request_buffer = us->srb->sense_buffer; /* FIXME: what if this command fails? */ temp_result = us->transport(us->srb, us); US_DEBUGP("-- Result from auto-sense is %d\n", temp_result); - - /* copy the data from the request buffer to the sense buffer */ - for(count = 0; count < 18; count++) - us->srb->sense_buffer[count] = - ((unsigned char *)(us->srb->request_buffer))[count]; - US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n", us->srb->sense_buffer[2] & 0xf, - us->srb->sense_buffer[12], us->srb->sense_buffer[13]); + us->srb->sense_buffer[12], + us->srb->sense_buffer[13]); + + /* set the result so the higher layers expect this data */ + us->srb->result = CHECK_CONDITION; /* we're done here */ - kfree(us->srb->request_buffer); + us->srb->use_sg = old_sg; us->srb->request_buffer = old_request_buffer; return; } - if (savelen != us->srb->request_bufflen) { - unsigned char *p = (unsigned char *)us->srb->request_buffer; - unsigned int length = 0; - - /* set correct length and retry */ - switch (us->srb->cmnd[0]) { - - /* FIXME: we should try to get all the sense data */ - case REQUEST_SENSE: - /* simply return 18 bytes */ - p[7] = 10; - length = us->srb->request_bufflen; - break; - - case INQUIRY: - length = p[4] + 5 > savelen ? savelen : p[4] + 5; - us->srb->cmnd[4] = length; - break; - - case MODE_SENSE: - US_DEBUGP("MODE_SENSE Mode data length is %d\n", p[0]); - length = p[0] + 1 > savelen ? savelen : p[0] + 1; - us->srb->cmnd[4] = length; - break; - - case LOG_SENSE: - length = ((p[2] << 8) + p[3]) + 4 > savelen ? savelen : ((p[2] << 8) + p[3]) + 4; - us->srb->cmnd[7] = length >> 8; - us->srb->cmnd[8] = length; - break; - - case MODE_SENSE_10: - US_DEBUGP("MODE_SENSE_10 Mode data length is %d\n", - (p[0] << 8) + p[1]); - length = ((p[0] << 8) + p[1]) + 6 > savelen ? savelen : ((p[0] << 8) + p[1]) + 6; - us->srb->cmnd[7] = length >> 8; - us->srb->cmnd[8] = length; - break; - } /* end switch on cmnd[0] */ - - US_DEBUGP("Old/New length = %d/%d\n", - savelen, length); - - /* issue the new command */ - /* FIXME: this assumes that the second attempt is - * always successful */ - if (us->srb->request_bufflen != length) { - US_DEBUGP("redoing cmd with len=%d\n", length); - us->srb->request_bufflen = length; - us->srb->result = us->transport(us->srb, us); - } - - /* reset back to original values */ - us->srb->request_bufflen = savelen; - - /* fix data as necessary */ - switch (us->srb->cmnd[0]) { - case INQUIRY: - if ((((unsigned char*)us->srb->request_buffer)[2] & 0x7) == 0) { - US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n"); - ((unsigned char*)us->srb->request_buffer)[2] |= 2; - } - /* FALL THROUGH */ - case REQUEST_SENSE: - case MODE_SENSE: - if (us->srb->use_sg == 0 && length > 0) { - int i; - printk(KERN_DEBUG "Data is"); - for (i = 0; i < 32 && i < length; ++i) - printk(" %.2x", ((unsigned char *)us->srb->request_buffer)[i]); - if (i < length) - printk(" ..."); - printk("\n"); - } - - /* FIXME: is this really necessary? */ - us->srb->cmnd[4] = saveallocation; - break; - - case LOG_SENSE: - case MODE_SENSE_10: - /* FIXME: is this really necessary? */ - us->srb->cmnd[7] = saveallocation >> 8; - us->srb->cmnd[8] = saveallocation; - break; - } /* end switch on cmnd[0] */ - } /* if good command */ + /* fix the results of an INQUIRY */ + if (us->srb->cmnd[0] == INQUIRY) { + US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n"); + ((unsigned char*)us->srb->request_buffer)[2] |= 2; + } } /*********************************************************************** @@ -789,7 +722,7 @@ static int CBI_irq(int state, void *buffer, int len, void *dev_id) /* was this a wanted interrupt? */ if (us->ip_wanted) { us->ip_wanted = 0; - wake_up(&us->ip_waitq); + up(&(us->ip_waitq)); } else { US_DEBUGP("ERROR: Unwanted interrupt received!\n"); } @@ -801,9 +734,7 @@ static int CBI_irq(int state, void *buffer, int len, void *dev_id) return 0; } -/* FIXME: this reset function doesn't really reset the port, and it - * should. Actually it should probably do what it's doing here, and - * reset the port physically +/* This issues a CB[I] Reset to the device in question */ static int CB_reset(struct us_data *us) { @@ -816,41 +747,39 @@ static int CB_reset(struct us_data *us) cmd[0] = SEND_DIAGNOSTIC; cmd[1] = 4; result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), - US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, us->ifnum, cmd, sizeof(cmd), HZ*5); /* long wait for reset */ 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)); + 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)); US_DEBUGP("CB_reset done\n"); return 0; } -static int pop_CB_status(Scsi_Cmnd *srb); - -/* FIXME: we also need a CBI_command which sets up the completion - * interrupt, and waits for it +/* + * Control/Bulk/Interrupt transport */ -static int CB_transport(Scsi_Cmnd *srb, struct us_data *us) +static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us) { int result; US_DEBUGP("CBI gets a command:\n"); US_DEBUG(us_show_command(srb)); - /* FIXME: we aren't setting the ip_wanted indicator early enough, which - * causes some commands to never complete. This hangs the driver. - */ - + /* COMMAND STAGE */ /* let's send the command via the control pipe */ - result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), - US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, us->ifnum, - srb->cmnd, srb->cmd_len, HZ*5); + result = usb_control_msg(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, + us->ifnum, srb->cmnd, srb->cmd_len, HZ*5); /* check the return code for the command */ if (result < 0) { @@ -858,131 +787,160 @@ static int 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 detected. Clearing\n"); - + US_DEBUGP("-- Stall on control pipe. Clearing\n"); US_DEBUGP("-- Return from usb_clear_halt() is %d\n", usb_clear_halt(us->pusb_dev, - usb_sndctrlpipe(us->pusb_dev, 0))); + usb_sndctrlpipe(us->pusb_dev, + 0))); return USB_STOR_TRANSPORT_ERROR; } - /* FIXME: we need to handle NAKs here */ + /* FIXME: we need to handle NAKs here */ return USB_STOR_TRANSPORT_ERROR; } + /* Set up for status notification */ + us->ip_wanted = 1; + + /* DATA STAGE */ /* transfer the data payload for this command, if one exists*/ if (us_transfer_length(srb)) { - result = us_transfer(srb, US_DIRECTION(srb->cmnd[0])); - US_DEBUGP("CBI attempted to transfer data, result is 0x%x\n", result); - - /* FIXME: what do the return codes from us_transfer mean? */ - if ((result < 0) && - (result != USB_ST_DATAUNDERRUN) && - (result != USB_ST_STALL)) { - return DID_ERROR << 16; - } - } /* if (us_transfer_length(srb)) */ + us_transfer(srb, US_DIRECTION(srb->cmnd[0])); + US_DEBUGP("CBI data stage result is 0x%x\n", result); + } - /* get status and return it */ - return pop_CB_status(srb); + /* STATUS STAGE */ + + /* go to sleep until we get this interrup */ + /* FIXME: this should be changed to use a timeout */ + down(&(us->ip_waitq)); + + /* FIXME: currently this code is unreachable, but the idea is + * necessary. See above comment. + */ + if (us->ip_wanted) { + US_DEBUGP("Did not get interrupt on CBI\n"); + us->ip_wanted = 0; + return USB_STOR_TRANSPORT_ERROR; + } + + US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data); + + /* UFI gives us ASC and ASCQ, like a request sense */ + /* FIXME: is this right? Do REQUEST_SENSE and INQUIRY need special + * case handling? + */ + if (us->subclass == US_SC_UFI) { + if (srb->cmnd[0] == REQUEST_SENSE || + srb->cmnd[0] == INQUIRY) + return USB_STOR_TRANSPORT_GOOD; + else + if (us->ip_data) + return USB_STOR_TRANSPORT_FAILED; + else + return USB_STOR_TRANSPORT_GOOD; + } + + /* otherwise, we interpret the data normally */ + switch (us->ip_data) { + case 0x0001: + return USB_STOR_TRANSPORT_GOOD; + case 0x0002: + return USB_STOR_TRANSPORT_FAILED; + default: + return USB_STOR_TRANSPORT_ERROR; + } + + US_DEBUGP("CBI_transport() reached end of function\n"); + return USB_STOR_TRANSPORT_ERROR; } /* - * Control/Bulk status handler + * Control/Bulk transport */ - -static int pop_CB_status(Scsi_Cmnd *srb) +static int CB_transport(Scsi_Cmnd *srb, struct us_data *us) { - struct us_data *us = (struct us_data *)srb->host_scribble; - int result = 0; + int result; __u8 status[2]; - int retry = 5; - US_DEBUGP("pop_CB_status, proto=0x%x\n", us->protocol); - switch (us->protocol) { - case US_PR_CB: - /* get from control */ - - while (retry--) { - result = usb_control_msg(us->pusb_dev, usb_rcvctrlpipe(us->pusb_dev,0), - USB_REQ_GET_STATUS, USB_DIR_IN | - USB_TYPE_STANDARD | USB_RECIP_DEVICE, - 0, us->ifnum, status, sizeof(status), HZ*5); - if (result != USB_ST_TIMEOUT) - break; - } - if (result) { - US_DEBUGP("Bad AP status request %d\n", result); - return DID_ABORT << 16; - } - US_DEBUGP("Got AP status 0x%x 0x%x\n", status[0], status[1]); - if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY && - ( (status[0] & ~3) || status[1])) - return (DID_OK << 16) | 2; - else - return USB_STOR_TRANSPORT_GOOD; - break; + US_DEBUGP("CBC gets a command:\n"); + US_DEBUG(us_show_command(srb)); - /* FIXME: this should be in a separate function */ - case US_PR_CBI: - /* get from interrupt pipe */ + /* COMMAND STAGE */ + /* let's send the command via the control pipe */ + result = usb_control_msg(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, + us->ifnum, srb->cmnd, srb->cmd_len, HZ*5); - /* add interrupt transfer, marked for removal */ - us->ip_wanted = 1; + /* check the return code for the command */ + if (result < 0) { + US_DEBUGP("Call to usb_control_msg() returned %d\n", result); - /* go to sleep until we get this interrup */ - /* FIXME: this should be changed to use a timeout */ - sleep_on(&us->ip_waitq); - - if (us->ip_wanted) { - US_DEBUGP("Did not get interrupt on CBI\n"); - us->ip_wanted = 0; + /* a stall is a fatal condition from the device */ + if (result == -EPIPE) { + US_DEBUGP("-- Stall on control pipe. Clearing\n"); + US_DEBUGP("-- Return from usb_clear_halt() is %d\n", + usb_clear_halt(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev, + 0))); return USB_STOR_TRANSPORT_ERROR; } - - US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data); - /* UFI gives us ASC and ASCQ, like a request sense */ - /* FIXME: is this right? do REQUEST_SENSE and INQUIRY need special - * case handling? - */ - if (us->subclass == US_SC_UFI) { - if (srb->cmnd[0] == REQUEST_SENSE || - srb->cmnd[0] == INQUIRY) - return USB_STOR_TRANSPORT_GOOD; - else - if (us->ip_data) - return USB_STOR_TRANSPORT_FAILED; - else - return USB_STOR_TRANSPORT_GOOD; - } + /* FIXME: we need to handle NAKs here */ + return USB_STOR_TRANSPORT_ERROR; + } - /* otherwise, we interpret the data normally */ - switch (us->ip_data) { - case 0x0001: - return USB_STOR_TRANSPORT_GOOD; - case 0x0002: - return USB_STOR_TRANSPORT_FAILED; - default: - return USB_STOR_TRANSPORT_ERROR; - } + /* DATA STAGE */ + /* transfer the data payload for this command, if one exists*/ + if (us_transfer_length(srb)) { + us_transfer(srb, US_DIRECTION(srb->cmnd[0])); + US_DEBUGP("CBC data stage result is 0x%x\n", result); } - US_DEBUGP("pop_CB_status, reached end of function\n"); + + + /* STATUS STAGE */ + /* FIXME: this is wrong */ + result = usb_control_msg(us->pusb_dev, + usb_rcvctrlpipe(us->pusb_dev,0), + USB_REQ_GET_STATUS, USB_DIR_IN | + USB_TYPE_STANDARD | USB_RECIP_DEVICE, + 0, us->ifnum, status, sizeof(status), HZ*5); + + if (result < 0) { + US_DEBUGP("CBC Status stage returns %d\n", result); + return USB_STOR_TRANSPORT_ERROR; + } + + US_DEBUGP("Got CB status 0x%x 0x%x\n", status[0], status[1]); + if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY && + ( (status[0] & ~3) || status[1])) + return USB_STOR_TRANSPORT_FAILED; + else + return USB_STOR_TRANSPORT_GOOD; + + US_DEBUGP("CB_transport() reached end of function\n"); return USB_STOR_TRANSPORT_ERROR; } +/* FIXME: Does this work? */ static int Bulk_reset(struct us_data *us) { int result; - result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), - US_BULK_RESET, USB_TYPE_CLASS | USB_RECIP_INTERFACE, - US_BULK_RESET_HARD, us->ifnum, - NULL, 0, HZ*5); - if (result) + result = usb_control_msg(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev,0), + US_BULK_RESET, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + US_BULK_RESET_HARD, us->ifnum, NULL, 0, HZ*5); + + 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)); + + 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)); /* long wait for reset */ schedule_timeout(HZ*6); @@ -991,8 +949,7 @@ static int Bulk_reset(struct us_data *us) } /* - * The bulk only protocol handler. - * Uses the in and out endpoints to transfer commands and data + * Bulk only transport */ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) { @@ -1001,7 +958,7 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) int result; int pipe; int partial; - + /* set up the command wrapper */ bcb.Signature = US_BULK_CB_SIGN; bcb.DataTransferLength = us_transfer_length(srb); @@ -1009,14 +966,14 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) bcb.Tag = srb->serial_number; bcb.Lun = 0; bcb.Length = srb->cmd_len; - + /* construct the pipe handle */ pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); - + /* copy the command payload */ memset(bcb.CDB, 0, sizeof(bcb.CDB)); memcpy(bcb.CDB, srb->cmnd, bcb.Length); - + /* send it to out endpoint */ US_DEBUGP("Bulk command S 0x%x T 0x%x L %d F %d CL %d\n", bcb.Signature, bcb.Tag, bcb.DataTransferLength, @@ -1024,94 +981,83 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) result = usb_bulk_msg(us->pusb_dev, pipe, &bcb, US_BULK_CB_WRAP_LEN, &partial, HZ*5); US_DEBUGP("Bulk command transfer result=%d\n", result); - + /* 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); } - + /* if the command transfered well, then we go to the data stage */ - /* FIXME: Regardless of the status of the data stage, we go on to the - * status stage. Note that this implies that if a command is - * partially successful, we rely on the device reporting an error - * the CSW. The spec says that the device may just decide to short us. - */ if (result == 0) { /* send/receive data payload, if there is any */ if (bcb.DataTransferLength) { - result = us_transfer(srb, bcb.Flags); - US_DEBUGP("Bulk data transfer result 0x%x\n", result); -#if 0 - if ((result < 0) && (result != USB_ST_DATAUNDERRUN) - && (result != USB_ST_STALL)) { - US_DEBUGP("Bulk data transfer result 0x%x\n", result); - return DID_ABORT << 16; - } -#endif + us_transfer(srb, bcb.Flags); + US_DEBUGP("Bulk data transfer result 0x%x\n", + srb->result); } } - + /* See flow chart on pg 15 of the Bulk Only Transport spec for * an explanation of how this code works. */ - + /* construct the pipe handle */ pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); - + /* get CSW for device status */ result = usb_bulk_msg(us->pusb_dev, pipe, &bcs, US_BULK_CS_WRAP_LEN, &partial, HZ*5); - + /* 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); - + /* get the status again */ result = usb_bulk_msg(us->pusb_dev, pipe, &bcs, US_BULK_CS_WRAP_LEN, &partial, HZ*5); - + /* if it fails again, we need a reset and return an error*/ if (result == -EPIPE) { Bulk_reset(us); - return (DID_ABORT << 16); + return USB_STOR_TRANSPORT_ERROR; } } - + /* if we still have a failure at this point, we're in trouble */ if (result) { - US_DEBUGP("Bulk status result = 0x%x\n", result); - return DID_ABORT << 16; + US_DEBUGP("Bulk status result = %d\n", result); + return USB_STOR_TRANSPORT_ERROR; } - + /* check bulk status */ US_DEBUGP("Bulk status S 0x%x T 0x%x R %d V 0x%x\n", bcs.Signature, bcs.Tag, bcs.Residue, bcs.Status); if (bcs.Signature != US_BULK_CS_SIGN || bcs.Tag != bcb.Tag || bcs.Status > US_BULK_STAT_PHASE || partial != 13) { US_DEBUGP("Bulk logical error\n"); - return DID_ABORT << 16; + return USB_STOR_TRANSPORT_ERROR; } - + /* based on the status code, we report good or bad */ switch (bcs.Status) { case US_BULK_STAT_OK: - /* if there is residue, we really didn't finish the command */ - if (bcs.Residue) - return DID_ERROR << 16; - else - return DID_OK << 16; + /* command good -- note that we could be short on data */ + return USB_STOR_TRANSPORT_GOOD; case US_BULK_STAT_FAIL: - return DID_ERROR << 16; - + /* command failed */ + return USB_STOR_TRANSPORT_FAILED; + case US_BULK_STAT_PHASE: + /* phase error */ Bulk_reset(us); - return DID_ERROR << 16; + return USB_STOR_TRANSPORT_ERROR; } - - return DID_OK << 16; /* check sense required */ + + /* we should never get here, but if we do, we're in trouble */ + return USB_STOR_TRANSPORT_ERROR; } /*********************************************************************** @@ -1163,14 +1109,20 @@ static int us_release(struct Scsi_Host *psh) usb_release_irq(us->pusb_dev, us->irq_handle, us->irqpipe); us->irq_handle = NULL; } - if (us->pusb_dev) - usb_deregister(&storage_driver); + + /* FIXME: release the interface claim here? */ + // if (us->pusb_dev) + // usb_deregister(&storage_driver); /* FIXME - leaves hanging host template copy */ /* (because scsi layer uses it after removal !!!) */ - while (prev->next != us) - prev = prev->next; - prev->next = us->next; + if (us_list == us) + us_list = us->next; + else { + while (prev->next != us) + prev = prev->next; + prev->next = us->next; + } return 0; } @@ -1188,17 +1140,19 @@ static int us_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *)) struct us_data *us = (struct us_data *)srb->host->hostdata[0]; US_DEBUGP("Command wakeup\n"); - if (us->srb) { - /* busy */ - } srb->host_scribble = (unsigned char *)us; - us->srb = srb; + + /* get exclusive access to the structures we want */ + down(&(us->queue_exclusion)); + + /* enqueue the command */ + us->queue_srb = srb; srb->scsi_done = done; us->action = US_ACT_COMMAND; /* wake up the process task */ - - wake_up_interruptible(&us->waitq); + up(&(us->queue_exclusion)); + up(&(us->sleeper)); return 0; } @@ -1209,6 +1163,7 @@ static int us_abort( Scsi_Cmnd *srb ) return 0; } +/* FIXME: this doesn't do anything right now */ static int us_bus_reset( Scsi_Cmnd *srb ) { // struct us_data *us = (struct us_data *)srb->host->hostdata[0]; @@ -1340,11 +1295,11 @@ static Scsi_Host_Template my_host_template = { NULL, /* select_queue_depths */ 1, /* can_queue */ -1, /* this_id */ - SG_ALL, /* sg_tablesize */ + SG_ALL, /* sg_tablesize */ 1, /* cmd_per_lun */ 0, /* present */ - FALSE, /* unchecked_isa_dma */ - FALSE, /* use_clustering */ + FALSE, /* unchecked_isa_dma */ + TRUE, /* use_clustering */ TRUE, /* use_new_eh_code */ TRUE /* emulated */ }; @@ -1391,10 +1346,18 @@ static int usb_stor_control_thread(void * __us) siginfo_t info; int unsigned long signr; - interruptible_sleep_on(&us->waitq); + US_DEBUGP("*** thread sleeping.\n"); + down(&(us->sleeper)); + down(&(us->queue_exclusion)); + US_DEBUGP("*** thread awakened.\n"); + /* take the command off the queue */ action = us->action; us->action = 0; + us->srb = us-> queue_srb; + + /* release the queue lock as fast as possible */ + up(&(us->queue_exclusion)); /* FIXME: we need to examine placment of break; and * scsi_done() calls */ @@ -1460,29 +1423,20 @@ static int usb_stor_control_thread(void * __us) break; } /* end switch on action */ - + + /* FIXME: we ignore TERM and KILL... is this right? */ if (signal_pending(current)) { /* sending SIGUSR1 makes us print out some info */ spin_lock_irq(¤t->sigmask_lock); signr = dequeue_signal(¤t->blocked, &info); spin_unlock_irq(¤t->sigmask_lock); - - if (signr == SIGUSR2) { - usb_stor_debug = !usb_stor_debug; - printk(USB_STORAGE "debug toggle = %d\n", usb_stor_debug); - } else { - break; /* exit the loop on any other signal */ - } - } - } + } /* if (singal_pending(current)) */ + } /* for (;;) */ // MOD_DEC_USE_COUNT; printk("usb_stor_control_thread exiting\n"); - /* FIXME: this is a hack to allow for debugging */ - // scsi_unregister_module(MODULE_SCSI_HA, us->htmplt); - return 0; } @@ -1498,7 +1452,6 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) unsigned int flags = 0; GUID(guid); /* Global Unique Identifier */ struct us_data *prev; - Scsi_Host_Template *htmplt; int protocol = 0; int subclass = 0; struct usb_interface_descriptor *altsetting = @@ -1565,14 +1518,18 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) return NULL; } memset(ss, 0, sizeof(struct us_data)); + + /* Initialize the mutexes only when the struct is new */ + init_MUTEX_LOCKED(&(ss->sleeper)); + init_MUTEX(&(ss->queue_exclusion)); } - /* Initialize the us_data structure with some useful info */ + /* establish the connection to the new device */ interface = altsetting; ss->flags = flags; ss->ifnum = ifnum; - ss->pusb_dev = dev; ss->attention_done = 0; + ss->pusb_dev = dev; /* If the device has subclass and protocol, then use that. Otherwise, * take data from the specific interface. @@ -1596,7 +1553,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) case US_PR_CBI: US_DEBUGPX("Control/Bulk/Interrupt\n"); - ss->transport = CB_transport; + ss->transport = CBI_transport; ss->transport_reset = CB_reset; break; @@ -1620,7 +1577,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) */ for (i = 0; i < interface->bNumEndpoints; i++) { /* is it an BULK endpoint? */ - if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { if (interface->endpoint[i].bEndpointAddress & USB_DIR_IN) ss->ep_in = interface->endpoint[i].bEndpointAddress & @@ -1646,7 +1603,6 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) (ss->protocol == US_PR_CBI && ss->ep_int == 0)) { US_DEBUGP("Problems with device\n"); if (ss->host) { - scsi_unregister_module(MODULE_SCSI_HA, ss->htmplt); kfree(ss->htmplt->name); kfree(ss->htmplt); } @@ -1667,11 +1623,13 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) US_DEBUGP("Protocol: "); switch (ss->subclass) { case US_SC_RBC: - US_DEBUGPX("Reduced Block Commands\n"); + US_DEBUGPX("Reduced Block Commands (RBC)\n"); + ss->proto_handler = transparent_scsi_command; break; case US_SC_8020: - US_DEBUGPX("8020\n"); + US_DEBUGPX("8020i\n"); + ss->proto_handler = ATAPI_command; break; case US_SC_QIC: @@ -1679,7 +1637,8 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) break; case US_SC_8070: - US_DEBUGPX("8070\n"); + US_DEBUGPX("8070i\n"); + ss->proto_handler = ATAPI_command; break; case US_SC_SCSI: @@ -1697,22 +1656,9 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) break; } - /* We only handle certain protocols. Currently, these are - *the only ones that devices use. - */ - if ((ss->subclass != US_SC_SCSI) && (ss->subclass != US_SC_UFI)) { - US_DEBUGP("Sorry, we do not support that protocol yet.\n"); - US_DEBUGP("If you have a device which uses one of the unsupported\n"); - US_DEBUGP("protocols, please contact mdharm-usb@one-eyed-alien.net\n"); - - kfree(ss); - return NULL; - } - /* Allocate memory for the SCSI Host Template */ - if ((htmplt = (Scsi_Host_Template *) - kmalloc(sizeof(*ss->htmplt), GFP_KERNEL)) == NULL ) { - + if ((ss->htmplt = (Scsi_Host_Template *) + kmalloc(sizeof(Scsi_Host_Template),GFP_KERNEL))==NULL ) { printk(KERN_WARNING USB_STORAGE "Out of memory\n"); kfree(ss); @@ -1720,7 +1666,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) } /* Initialize the host template based on the default one */ - memcpy(htmplt, &my_host_template, sizeof(my_host_template)); + memcpy(ss->htmplt, &my_host_template, sizeof(my_host_template)); /* Grab the next host number */ ss->host_number = my_host_number++; @@ -1729,32 +1675,34 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) * can pass the ss pointer to the host controler thread * in us_detect */ - (struct us_data *)htmplt->proc_dir = ss; + (struct us_data *)ss->htmplt->proc_dir = ss; /* shuttle E-USB */ if (dev->descriptor.idVendor == 0x04e6 && dev->descriptor.idProduct == 0x0001) { __u8 qstat[2]; int result; - - result = usb_control_msg(ss->pusb_dev, usb_rcvctrlpipe(dev,0), + + result = usb_control_msg(ss->pusb_dev, + usb_rcvctrlpipe(dev,0), 1, 0xC0, 0, ss->ifnum, qstat, 2, HZ*5); US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]); - init_waitqueue_head(&ss->ip_waitq); + init_MUTEX_LOCKED(&(ss->ip_waitq)); ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int); - result = usb_request_irq(ss->pusb_dev, ss->irqpipe, CBI_irq, - 255, (void *)ss, &ss->irq_handle); - if (result) + result = usb_request_irq(ss->pusb_dev, ss->irqpipe, + CBI_irq, 255, (void *)ss, + &ss->irq_handle); + if (result < 0) return NULL; - - interruptible_sleep_on_timeout(&ss->ip_waitq, HZ*6); - } else if (ss->protocol == US_PR_CBI) - { + /* FIXME: what is this?? */ + down(&(ss->ip_waitq)); + } else if (ss->protocol == US_PR_CBI) { int result; - - init_waitqueue_head(&ss->ip_waitq); + + /* set up so we'll wait for notification */ + init_MUTEX_LOCKED(&(ss->ip_waitq)); /* set up the IRQ pipe and handler */ /* FIXME: This needs to get the period from the device */ @@ -1768,18 +1716,16 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) } - /* start up our thread */ + /* start up our thread */ { DECLARE_MUTEX_LOCKED(sem); - init_waitqueue_head(&ss->waitq); - ss->notify = &sem; ss->pid = kernel_thread(usb_stor_control_thread, ss, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); if (ss->pid < 0) { printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n"); - kfree(htmplt); + kfree(ss->htmplt); kfree(ss); return NULL; @@ -1790,17 +1736,16 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) } /* now register - our detect function will be called */ - scsi_register_module(MODULE_SCSI_HA, htmplt); + ss->htmplt->module = &__this_module; + scsi_register_module(MODULE_SCSI_HA, ss->htmplt); /* put us in the list */ - prev = (struct us_data *)&us_list; - while (prev->next) - prev = prev->next; - prev->next = ss; + ss->next = us_list; + us_list = ss; } - printk(KERN_INFO "WARNING: USB Mass Storage data integrity not assured\n"); - printk(KERN_INFO "USB Mass Storage device found at %d\n", dev->devnum); + printk(KERN_DEBUG "WARNING: USB Mass Storage data integrity not assured\n"); + printk(KERN_DEBUG "USB Mass Storage device found at %d\n", dev->devnum); return ss; } @@ -1814,7 +1759,6 @@ static void storage_disconnect(struct usb_device *dev, void *ptr) return; ss->pusb_dev = NULL; - // MOD_DEC_USE_COUNT; } @@ -1824,8 +1768,6 @@ static void storage_disconnect(struct usb_device *dev, void *ptr) int __init usb_stor_init(void) { - // MOD_INC_USE_COUNT; - if (sizeof(my_host_template) != SCSI_HOST_TEMPLATE_SIZE) { printk(KERN_ERR "usb-storage: SCSI_HOST_TEMPLATE_SIZE does not match\n") ; printk(KERN_ERR "usb-storage: expected %d bytes, got %d bytes\n", @@ -1844,6 +1786,14 @@ int __init usb_stor_init(void) void __exit usb_stor_exit(void) { + static struct us_data *ptr; + + // FIXME: this needs to be put back to free _all_ the hosts + // for (ptr = us_list; ptr != NULL; ptr = ptr->next) + // scsi_unregister_module(MODULE_SCSI_HA, ptr->htmplt); + printk("MDD: us_list->htmplt is 0x%x\n", (unsigned int)(us_list->htmplt)); + scsi_unregister_module(MODULE_SCSI_HA, us_list->htmplt); + usb_deregister(&storage_driver) ; } diff --git a/drivers/usb/usb-storage.h b/drivers/usb/usb-storage.h index 80a03f3c9..06e6d958b 100644 --- a/drivers/usb/usb-storage.h +++ b/drivers/usb/usb-storage.h @@ -9,13 +9,11 @@ #define USB_STORAGE "usb-storage: " -extern int usb_stor_debug; - #ifdef CONFIG_USB_STORAGE_DEBUG void us_show_command(Scsi_Cmnd *srb); -#define US_DEBUGP(x...) { if(usb_stor_debug) printk( KERN_DEBUG USB_STORAGE ## x ); } -#define US_DEBUGPX(x...) { if(usb_stor_debug) printk( ## x ); } -#define US_DEBUG(x) { if(usb_stor_debug) x; } +#define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE ## x ) +#define US_DEBUGPX(x...) printk( ## x ) +#define US_DEBUG(x) x #else #define US_DEBUGP(x...) #define US_DEBUGPX(x...) @@ -83,15 +81,22 @@ struct bulk_cs_wrap { #define US_BULK_RESET_HARD 0 /* + * us_bulk_transfer() return codes + */ +#define US_BULK_TRANSFER_GOOD 0 +#define US_BULK_TRANSFER_SHORT 1 +#define US_BULK_TRANSFER_FAILED 2 + +/* * Transport return codes */ -#define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */ -#define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */ -#define USB_STOR_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead */ +#define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */ +#define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */ +#define USB_STOR_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead) */ /* - * CBI style + * CBI accept device specific command */ #define US_CBI_ADSC 0 @@ -128,3 +133,4 @@ static inline void make_guid( __u32 *pg, __u16 vendor, __u16 product, char *seri #define US_FL_FIXED_COMMAND 0x00000002 /* expand commands to fixed size */ #define US_FL_MODE_XLATE 0x00000004 /* translate _6 to _10 comands for Win/MacOS compatibility */ + diff --git a/drivers/usb/usb-uhci-debug.h b/drivers/usb/usb-uhci-debug.h index 73d16937a..4eedc4183 100644 --- a/drivers/usb/usb-uhci-debug.h +++ b/drivers/usb/usb-uhci-debug.h @@ -1,41 +1,32 @@ #ifdef DEBUG - static void uhci_show_qh (puhci_desc_t qh) { if (qh->type != QH_TYPE) { dbg("qh has not QH_TYPE"); return; } - dbg("uhci_show_qh %p (%08lX):", qh, virt_to_bus (qh)); + dbg("QH @ %p/%08lX:", qh, virt_to_bus (qh)); if (qh->hw.qh.head & UHCI_PTR_TERM) - dbg("Head Terminate"); - else { - if (qh->hw.qh.head & UHCI_PTR_QH) - dbg("Head points to QH"); - else - dbg("Head points to TD"); - - dbg("head: %08X", qh->hw.qh.head & ~UHCI_PTR_BITS); - } + dbg(" Head Terminate"); + else + dbg(" Head: %s @ %08X", + (qh->hw.qh.head & UHCI_PTR_QH?"QH":"TD"), + qh->hw.qh.head & ~UHCI_PTR_BITS); + if (qh->hw.qh.element & UHCI_PTR_TERM) - dbg("Element Terminate"); - else { - - if (qh->hw.qh.element & UHCI_PTR_QH) - dbg("Element points to QH"); - else - dbg("Element points to TD"); - dbg("element: %08X", qh->hw.qh.element & ~UHCI_PTR_BITS); - } + dbg(" Element Terminate"); + else + dbg(" Element: %s @ %08X", + (qh->hw.qh.element & UHCI_PTR_QH?"QH":"TD"), + qh->hw.qh.element & ~UHCI_PTR_BITS); } #endif static void uhci_show_td (puhci_desc_t td) { char *spid; - warn("uhci_show_td %p (%08lX) ", td, virt_to_bus (td)); - + switch (td->hw.td.info & 0xff) { case USB_PID_SETUP: spid = "SETUP"; @@ -51,16 +42,16 @@ static void uhci_show_td (puhci_desc_t td) break; } - warn("MaxLen=%02x DT%d EndPt=%x Dev=%x, PID=%x(%s) (buf=%08x)", + warn(" TD @ %p/%08lX, MaxLen=%02x DT%d EP=%x Dev=%x PID=(%s) buf=%08x", + td, virt_to_bus (td), td->hw.td.info >> 21, ((td->hw.td.info >> 19) & 1), (td->hw.td.info >> 15) & 15, (td->hw.td.info >> 8) & 127, - (td->hw.td.info & 0xff), spid, td->hw.td.buffer); - warn("Len=%02x e%d %s%s%s%s%s%s%s%s%s%s", + warn(" Len=%02x e%d %s%s%s%s%s%s%s%s%s%s", td->hw.td.status & 0x7ff, ((td->hw.td.status >> 27) & 3), (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "", @@ -74,50 +65,41 @@ static void uhci_show_td (puhci_desc_t td) (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "", (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : "" ); -#if 1 + if (td->hw.td.link & UHCI_PTR_TERM) - warn("Link Terminate"); - else { - if (td->hw.td.link & UHCI_PTR_QH) - warn("%s, link points to QH @ %08x", - (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"), - td->hw.td.link & ~UHCI_PTR_BITS); - else - warn("%s, link points to TD @ %08x", - (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"), - td->hw.td.link & ~UHCI_PTR_BITS); - } -#endif + warn(" TD Link Terminate"); + else + warn(" Link points to %s @ %08x, %s", + (td->hw.td.link & UHCI_PTR_QH?"QH":"TD"), + td->hw.td.link & ~UHCI_PTR_BITS, + (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : "Breadth first")); } #ifdef DEBUG static void uhci_show_td_queue (puhci_desc_t td) { - dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td)); + //dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td)); while (1) { uhci_show_td (td); if (td->hw.td.link & UHCI_PTR_TERM) break; - //if(!(td->hw.td.link&UHCI_PTR_DEPTH)) - // break; if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS)) td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS); else { dbg("td points to itself!"); break; } -// schedule(); } } static void uhci_show_queue (puhci_desc_t qh) { + uhci_desc_t *start_qh=qh; + dbg("uhci_show_queue %p:", qh); while (1) { uhci_show_qh (qh); - if (qh->hw.qh.element & UHCI_PTR_QH) - dbg("Warning: qh->element points to qh!"); - else if (!(qh->hw.qh.element & UHCI_PTR_TERM)) + if (!(qh->hw.qh.element & UHCI_PTR_TERM)) uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS)); if (qh->hw.qh.head & UHCI_PTR_TERM) @@ -129,7 +111,12 @@ static void uhci_show_queue (puhci_desc_t qh) dbg("qh points to itself!"); break; } - } + + if (qh==start_qh) { // avoid loop + dbg("Loop detect"); + break; + } + } } static void uhci_show_sc (int port, unsigned short status) diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c index aed79f849..85a5cd476 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.197 2000/02/15 17:44:22 acher Exp $ + * $Id: usb-uhci.c,v 1.222 2000/03/13 21:18:02 fliegl Exp $ */ #include <linux/config.h> @@ -28,9 +28,9 @@ #include <linux/unistd.h> #include <linux/interrupt.h> /* for in_interrupt() */ #include <linux/init.h> -/* This enables debug printks */ -#define DEBUG -#include <linux/usb.h> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44) +#include <linux/pm.h> +#endif #include <asm/uaccess.h> #include <asm/io.h> @@ -40,51 +40,56 @@ /* This enables more detailed sanity checks in submit_iso */ //#define ISO_SANITY_CHECK +/* This enables debug printks */ +#define DEBUG + /* This enables all symbols to be exported, to ease debugging oopses */ //#define DEBUG_SYMBOLS /* This enables an extra UHCI slab for memory debugging */ #define DEBUG_SLAB +#include <linux/usb.h> #include "usb-uhci.h" #include "usb-uhci-debug.h" #undef DEBUG #undef dbg #define dbg(format, arg...) do {} while (0) - -#include <linux/pm.h> - +#define DEBUG_SYMBOLS #ifdef DEBUG_SYMBOLS #define _static #ifndef EXPORT_SYMTAB - #define EXPORT_SYMTAB + #define EXPORT_SYMTAB #endif #else #define _static static #endif +#define queue_dbg dbg //err +#define async_dbg dbg //err + #ifdef DEBUG_SLAB static kmem_cache_t *uhci_desc_kmem; static kmem_cache_t *urb_priv_kmem; #endif +#define SLAB_FLAG (in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL) +#define KMALLOC_FLAG (in_interrupt ()? GFP_ATOMIC : GFP_KERNEL) + +#define CONFIG_USB_UHCI_HIGH_BANDWIDTH #define USE_CTRL_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first #define USE_BULK_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first -#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH -#define USE_RECLAMATION_LOOP -#else -//#define USE_RECLAMATION_LOOP -#endif - -// stop bandwidth reclamation after (roughly) 50ms (depends also on -// hub polling interval) +// stop bandwidth reclamation after (roughly) 50ms #define IDLE_TIMEOUT (HZ/20) _static int rh_submit_urb (urb_t *urb); _static int rh_unlink_urb (urb_t *urb); _static int delete_qh (uhci_t *s, uhci_desc_t *qh); +_static int process_transfer (uhci_t *s, urb_t *urb, int mode); +_static int process_interrupt (uhci_t *s, urb_t *urb); +_static int process_iso (uhci_t *s, urb_t *urb, int force); static uhci_t *devs = NULL; @@ -105,58 +110,47 @@ void clean_descs(uhci_t *s, int force) qh = list_entry (q, uhci_desc_t, horizontal); if ((qh->last_used!=now) || force) delete_qh(s,qh); + q=qh->horizontal.prev; } } /*-------------------------------------------------------------------*/ -#ifdef USE_RECLAMATION_LOOP +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH _static void enable_desc_loop(uhci_t *s, urb_t *urb) { int flags; - - dbg("enable_desc_loop: enter"); - + spin_lock_irqsave (&s->qh_lock, flags); - s->chain_end->hw.qh.head=virt_to_bus(s->control_chain)|UHCI_PTR_QH; + s->chain_end->hw.qh.head&=~UHCI_PTR_TERM; + mb(); s->loop_usage++; ((urb_priv_t*)urb->hcpriv)->use_loop=1; spin_unlock_irqrestore (&s->qh_lock, flags); - - dbg("enable_desc_loop: finished"); } /*-------------------------------------------------------------------*/ _static void disable_desc_loop(uhci_t *s, urb_t *urb) { int flags; - - dbg("disable_desc_loop: enter\n"); - + spin_lock_irqsave (&s->qh_lock, flags); if (((urb_priv_t*)urb->hcpriv)->use_loop) { s->loop_usage--; - if (!s->loop_usage) - s->chain_end->hw.qh.head=UHCI_PTR_TERM; - + if (!s->loop_usage) { + s->chain_end->hw.qh.head|=UHCI_PTR_TERM; + mb(); + } ((urb_priv_t*)urb->hcpriv)->use_loop=0; } spin_unlock_irqrestore (&s->qh_lock, flags); - - dbg("disable_desc_loop: finished"); - } #endif /*-------------------------------------------------------------------*/ -_static void queue_urb (uhci_t *s, urb_t *urb) +_static void queue_urb_unlocked (uhci_t *s, urb_t *urb) { - unsigned long flags=0; struct list_head *p=&urb->urb_list; - - - spin_lock_irqsave (&s->urb_list_lock, flags); - -#ifdef USE_RECLAMATION_LOOP +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH { int type; type=usb_pipetype (urb->pipe); @@ -166,15 +160,21 @@ _static void queue_urb (uhci_t *s, urb_t *urb) } #endif ((urb_priv_t*)urb->hcpriv)->started=jiffies; - list_add_tail (p, &s->urb_list); - - spin_unlock_irqrestore (&s->urb_list_lock, flags); + list_add (p, &s->urb_list); } +/*-------------------------------------------------------------------*/ +_static void queue_urb (uhci_t *s, urb_t *urb) +{ + unsigned long flags=0; + spin_lock_irqsave (&s->urb_list_lock, flags); + queue_urb_unlocked(s,urb); + spin_unlock_irqrestore (&s->urb_list_lock, flags); +} /*-------------------------------------------------------------------*/ _static void dequeue_urb (uhci_t *s, urb_t *urb) { -#ifdef USE_RECLAMATION_LOOP +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH int type; type=usb_pipetype (urb->pipe); @@ -189,9 +189,9 @@ _static void dequeue_urb (uhci_t *s, urb_t *urb) _static int alloc_td (uhci_desc_t ** new, int flags) { #ifdef DEBUG_SLAB - *new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL); + *new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG); #else - *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL); + *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG); #endif if (!*new) return -ENOMEM; @@ -205,6 +205,19 @@ _static int alloc_td (uhci_desc_t ** new, int flags) return 0; } /*-------------------------------------------------------------------*/ +// append a qh to td.link physically, the SW linkage is not affected +_static void append_qh(uhci_t *s, uhci_desc_t *td, uhci_desc_t* qh, int flags) +{ + unsigned long xxx; + + spin_lock_irqsave (&s->td_lock, xxx); + + td->hw.td.link = virt_to_bus (qh) | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH; + + mb(); + spin_unlock_irqrestore (&s->td_lock, xxx); +} +/*-------------------------------------------------------------------*/ /* insert td at last position in td-list of qh (vertical) */ _static int insert_td (uhci_t *s, uhci_desc_t *qh, uhci_desc_t* new, int flags) { @@ -271,10 +284,9 @@ _static int unlink_td (uhci_t *s, uhci_desc_t *element, int phys_unlink) if (prev->type == TD_TYPE) prev->hw.td.link = element->hw.td.link; else - prev->hw.qh.element = element->hw.td.link; + prev->hw.qh.element = element->hw.td.link; } - element->hw.td.link=UHCI_PTR_TERM; mb (); if (dir == 0) @@ -302,9 +314,9 @@ _static int delete_desc (uhci_desc_t *element) _static int alloc_qh (uhci_desc_t ** new) { #ifdef DEBUG_SLAB - *new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL); + *new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG); #else - *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL); + *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG); #endif if (!*new) return -ENOMEM; @@ -350,9 +362,10 @@ _static int insert_qh (uhci_t *s, uhci_desc_t *pos, uhci_desc_t *new, int order) mb (); spin_unlock_irqrestore (&s->qh_lock, flags); - + return 0; } + /*-------------------------------------------------------------------*/ _static int unlink_qh (uhci_t *s, uhci_desc_t *element) { @@ -406,6 +419,14 @@ _static void clean_td_chain (uhci_desc_t *td) delete_desc (td); } + +/*-------------------------------------------------------------------*/ +_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer) +{ + td->hw.td.status = status; + td->hw.td.info = info; + td->hw.td.buffer = buffer; +} /*-------------------------------------------------------------------*/ // Removes ALL qhs in chain (paranoia!) _static void cleanup_skel (uhci_t *s) @@ -441,19 +462,20 @@ _static void cleanup_skel (uhci_t *s) qh = s->control_chain; while ((p = qh->horizontal.next) != &qh->horizontal) { qh1 = list_entry (p, uhci_desc_t, horizontal); - dbg("delete_qh @ %p",qh1); delete_qh (s, qh1); } - dbg("delete_qh last @ %p",qh); + delete_qh (s, qh); } else { + if (s->ls_control_chain) + delete_desc (s->ls_control_chain); if (s->control_chain) - kfree (s->control_chain); + delete_desc(s->control_chain); if (s->bulk_chain) - kfree (s->bulk_chain); + delete_desc (s->bulk_chain); if (s->chain_end) - kfree (s->chain_end); + delete_desc (s->chain_end); } dbg("cleanup_skel finished"); } @@ -480,6 +502,7 @@ _static int init_skel (uhci_t *s) if (!s->iso_td) goto init_skel_cleanup; + s->ls_control_chain = NULL; s->control_chain = NULL; s->bulk_chain = NULL; s->chain_end = NULL; @@ -499,26 +522,46 @@ _static int init_skel (uhci_t *s) if (ret) goto init_skel_cleanup; - + s->chain_end = qh; + ret = alloc_td (&td, 0); + + if (ret) + goto init_skel_cleanup; + + fill_td (td, TD_CTRL_IOC, 0, 0); // generate 1ms interrupt + insert_td (s, qh, td, 0); + dbg("allocating qh: bulk_chain"); ret = alloc_qh (&qh); - if (ret) goto init_skel_cleanup; insert_qh (s, s->chain_end, qh, 0); s->bulk_chain = qh; + dbg("allocating qh: control_chain"); ret = alloc_qh (&qh); - if (ret) goto init_skel_cleanup; insert_qh (s, s->bulk_chain, qh, 0); s->control_chain = qh; +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH + // disabled reclamation loop + s->chain_end->hw.qh.head=virt_to_bus(s->control_chain) | UHCI_PTR_QH | UHCI_PTR_TERM; +#endif + + dbg("allocating qh: ls_control_chain"); + ret = alloc_qh (&qh); + if (ret) + goto init_skel_cleanup; + + insert_qh (s, s->control_chain, qh, 0); + s->ls_control_chain = qh; + for (n = 0; n < 8; n++) s->int_chain[n] = 0; @@ -532,7 +575,7 @@ _static int init_skel (uhci_t *s) goto init_skel_cleanup; s->int_chain[n] = td; if (n == 0) { - s->int_chain[0]->hw.td.link = virt_to_bus (s->control_chain) | UHCI_PTR_QH; + s->int_chain[0]->hw.td.link = virt_to_bus (s->ls_control_chain) | UHCI_PTR_QH; } else { s->int_chain[n]->hw.td.link = virt_to_bus (s->int_chain[0]); @@ -547,20 +590,16 @@ _static int init_skel (uhci_t *s) dbg("framelist[%i]=%x",n,s->framelist[n]); if ((n&127)==127) ((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus(s->int_chain[0]); - else { - for (o = 1, m = 2; m <= 128; o++, m += m) { - // n&(m-1) = n%m - if ((n & (m - 1)) == ((m - 1) / 2)) { + else + for (o = 1, m = 2; m <= 128; o++, m += m) + if ((n & (m - 1)) == ((m - 1) / 2)) ((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus (s->int_chain[o]); - } - } - } } mb(); //uhci_show_queue(s->control_chain); dbg("init_skel exit"); - return 0; // OK + return 0; init_skel_cleanup: cleanup_skel (s); @@ -568,14 +607,6 @@ _static int init_skel (uhci_t *s) } /*-------------------------------------------------------------------*/ -_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer) -{ - td->hw.td.status = status; - td->hw.td.info = info; - td->hw.td.buffer = buffer; -} - -/*-------------------------------------------------------------------*/ // LOW LEVEL STUFF // assembles QHs und TDs for control, bulk and iso /*-------------------------------------------------------------------*/ @@ -586,10 +617,15 @@ _static int uhci_submit_control_urb (urb_t *urb) urb_priv_t *urb_priv = urb->hcpriv; unsigned long destination, status; int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); - unsigned long len, bytesrequested; + unsigned long len; char *data; int depth_first=USE_CTRL_DEPTH_FIRST; // UHCI descriptor chasing method + if (!maxsze) { + err("uhci_submit_control_urb: pipesize for pipe %x is zero", urb->pipe); + return -EINVAL; + } + dbg("uhci_submit_control start"); alloc_qh (&qh); // alloc qh for this request @@ -615,26 +651,21 @@ _static int uhci_submit_control_urb (urb_t *urb) insert_td (s, qh, td, 0); // queue 'setup stage'-td in qh #if 0 - dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe, - urb->setup_packet[0], urb->setup_packet[1], urb->setup_packet[2], urb->setup_packet[3], - urb->setup_packet[4], urb->setup_packet[5], urb->setup_packet[6], urb->setup_packet[7]); + { + char *sp=urb->setup_packet; + dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe, + sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]); + } //uhci_show_td(td); #endif - /* Build the DATA TD's */ len = urb->transfer_buffer_length; - bytesrequested = len; data = urb->transfer_buffer; /* If direction is "send", change the frame from SETUP (0x2D) to OUT (0xE1). Else change it from SETUP to IN (0x69). */ - destination &= ~UHCI_PID; - - if (usb_pipeout (urb->pipe)) - destination |= USB_PID_OUT; - else - destination |= USB_PID_IN; + destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN); while (len > 0) { int pktsze = len; @@ -664,7 +695,7 @@ _static int uhci_submit_control_urb (urb_t *urb) destination &= ~UHCI_PID; - if (usb_pipeout (urb->pipe) || (bytesrequested == 0)) + if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0)) destination |= USB_PID_IN; else destination |= USB_PID_OUT; @@ -690,32 +721,34 @@ _static int uhci_submit_control_urb (urb_t *urb) urb->status = -EINPROGRESS; queue_urb (s, urb); // queue before inserting in desc chain - qh->hw.qh.element&=~UHCI_PTR_TERM; + qh->hw.qh.element &= ~UHCI_PTR_TERM; //uhci_show_queue(qh); /* Start it up... put low speed first */ if (urb->pipe & TD_CTRL_LS) - insert_qh (s, s->control_chain, qh, 1); // insert after control chain + insert_qh (s, s->control_chain, qh, 0); else - insert_qh (s, s->bulk_chain, qh, 0); // insert before bulk chain - //uhci_show_queue(qh); + insert_qh (s, s->bulk_chain, qh, 0); dbg("uhci_submit_control end"); return 0; } /*-------------------------------------------------------------------*/ -_static int uhci_submit_bulk_urb (urb_t *urb) +// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh) +// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock! + +_static int uhci_submit_bulk_urb (urb_t *urb, urb_t *bulk_urb) { uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; urb_priv_t *urb_priv = urb->hcpriv; - uhci_desc_t *qh, *td; + uhci_desc_t *qh, *td, *nqh, *bqh; unsigned long destination, status; char *data; unsigned int pipe = urb->pipe; int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); int info, len; int depth_first=USE_BULK_DEPTH_FIRST; // UHCI descriptor chasing method - + urb_priv_t *upriv, *bpriv; if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) return -EPIPE; @@ -727,12 +760,55 @@ _static int uhci_submit_bulk_urb (urb_t *urb) if (!maxsze) return -EMSGSIZE; - /* FIXME: should tell the client that the endpoint is invalid, i.e. not in the descriptor */ + + queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i", + urb,bulk_urb,urb->pipe,urb->transfer_buffer_length); - alloc_qh (&qh); // get qh for this request + upriv=(urb_priv_t*)urb->hcpriv; - if (!qh) - return -ENOMEM; + if (!bulk_urb) { + alloc_qh (&qh); // get qh for this request + + if (!qh) + return -ENOMEM; + + if (urb->transfer_flags & USB_QUEUE_BULK) { + alloc_qh(&nqh); // placeholder for clean unlink + if (!nqh) { + delete_desc (qh); + return -ENOMEM; + } + upriv->next_qh = nqh; + queue_dbg("new next qh %p",nqh); + } + } + else { + bpriv = (urb_priv_t*)bulk_urb->hcpriv; + qh = bpriv->bottom_qh; // re-use bottom qh and next qh + nqh = bpriv->next_qh; + upriv->next_qh=nqh; + bpriv->next_queued_urb=urb; + upriv->prev_queued_urb=bulk_urb; + } + + queue_dbg("uhci_submit_bulk: qh=%p, nqh=%p\n",bqh,nqh); + + if (urb->transfer_flags & USB_QUEUE_BULK) { + alloc_qh (&bqh); // "bottom" QH, + + if (!bqh) { + if (!bulk_urb) { + delete_desc(qh); + delete_desc(nqh); + } + return -ENOMEM; + } + bqh->hw.qh.element = UHCI_PTR_TERM; + bqh->hw.qh.element = virt_to_bus(nqh)|UHCI_PTR_QH; + upriv->bottom_qh = bqh; + queue_dbg("uhci_submit_bulk: new bqh %p\n",bqh); + } + /* The "pipe" thing contains the destination in bits 8--18. */ destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe); @@ -744,7 +820,6 @@ _static int uhci_submit_bulk_urb (urb_t *urb) /* Build the TDs for the bulk request */ len = urb->transfer_buffer_length; data = urb->transfer_buffer; - dbg("uhci_submit_bulk_urb: pipe %x, len %d", pipe, len); do { // TBD: Really allow zero-length packets? int pktsze = len; @@ -770,54 +845,149 @@ _static int uhci_submit_bulk_urb (urb_t *urb) if (!len) td->hw.td.status |= TD_CTRL_IOC; // last one generates INT - //dbg("insert td %p, len %i",td,pktsze); insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first); - - /* Alternate Data0/1 (start with Data0) */ usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); + } while (len > 0); list_add (&qh->desc_list, &urb_priv->desc_list); + if (urb->transfer_flags & USB_QUEUE_BULK) { + qh->hw.qh.element&=~UHCI_PTR_TERM; + append_qh(s, td, bqh, UHCI_PTR_DEPTH * depth_first); + } + urb->status = -EINPROGRESS; - queue_urb (s, urb); + queue_urb_unlocked (s, urb); - qh->hw.qh.element&=~UHCI_PTR_TERM; + qh->hw.qh.element &= ~UHCI_PTR_TERM; - insert_qh (s, s->chain_end, qh, 0); // insert before end marker + if (!bulk_urb) { + if (urb->transfer_flags & USB_QUEUE_BULK) { + spin_lock (&s->td_lock); // both QHs in one go + insert_qh (s, s->chain_end, qh, 0); // Main QH + insert_qh (s, s->chain_end, nqh, 0); // Helper QH + spin_unlock (&s->td_lock); + } + else + insert_qh (s, s->chain_end, qh, 0); + } + //uhci_show_queue(s->bulk_chain); - - dbg("uhci_submit_bulk_urb: exit"); + //dbg("uhci_submit_bulk_urb: exit\n"); return 0; } - /*-------------------------------------------------------------------*/ -// unlinks an urb by dequeuing its qh, waits some frames and forgets it -// Problem: unlinking in interrupt requires waiting for one frame (udelay) -// to allow the whole structures to be safely removed -_static int uhci_unlink_urb (urb_t *urb) +_static void uhci_clean_iso_step1(uhci_t *s, urb_priv_t *urb_priv) { - uhci_t *s; - uhci_desc_t *qh; + struct list_head *p; uhci_desc_t *td; - urb_priv_t *urb_priv; - unsigned long flags=0; + for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) { + td = list_entry (p, uhci_desc_t, desc_list); + unlink_td (s, td, 1); + } +} +/*-------------------------------------------------------------------*/ +_static void uhci_clean_iso_step2(uhci_t *s, urb_priv_t *urb_priv) +{ struct list_head *p; + uhci_desc_t *td; - if (!urb || !urb->dev) // you never know... - return -EINVAL; + while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) { + td = list_entry (p, uhci_desc_t, desc_list); + list_del (p); + delete_desc (td); + } +} +/*-------------------------------------------------------------------*/ +// mode: 0: unlink + no deletion mark, 1: regular (unlink/delete-mark), 2: don't unlink +// looks a bit complicated because of all the bulk queueing goodies - s = (uhci_t*) urb->dev->bus->hcpriv; // get pointer to uhci struct +_static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mode) +{ + uhci_desc_t *bqh, *nqh, *prevqh; + int now; + urb_priv_t *priv=(urb_priv_t*)urb->hcpriv; - if (usb_pipedevice (urb->pipe) == s->rh.devnum) - return rh_unlink_urb (urb); + now=UHCI_GET_CURRENT_FRAME(s); - if (!urb->hcpriv) // you never know... - return -EINVAL; + dbg("clean transfer urb %p, qh %p, mode %i",urb,qh,mode); + bqh=priv->bottom_qh; - //dbg("unlink_urb called %p",urb); + if (!priv->next_queued_urb) { // no more appended bulk queues + + if (mode != 2) + unlink_qh (s, qh); + + if (priv->prev_queued_urb) { + urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv; + + ppriv->bottom_qh = priv->bottom_qh; + ppriv->next_queued_urb = NULL; + } + else if (bqh) { // queue dead + nqh=priv->next_qh; + + if (mode != 2) + unlink_qh(s, nqh); + + if (mode) { + nqh->last_used = bqh->last_used = now; + list_add_tail (&nqh->horizontal, &s->free_desc); + list_add_tail (&bqh->horizontal, &s->free_desc); + } + } + } + else { // there are queued urbs following + urb_t *nurb; + unsigned long flags; + + nurb=priv->next_queued_urb; + spin_lock_irqsave (&s->qh_lock, flags); + + if (!priv->prev_queued_urb) { // top + if (mode !=2) { + prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal); + prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH; + queue_dbg ("TOP relink of %p to %p-%p",qh,prevqh,bqh); + + list_del (&qh->horizontal); + list_add (&bqh->horizontal, &prevqh->horizontal); + } + } + else { //intermediate + urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv; + uhci_desc_t * bnqh; + + bnqh=list_entry (&((urb_priv_t*)(nurb->hcpriv))->desc_list.next, uhci_desc_t, desc_list); + ppriv->bottom_qh=bnqh; + ppriv->next_queued_urb=nurb; + + if (mode!=2) { + prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list); + prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH; + queue_dbg ("IM relink of %p to %p-%p",qh,prevqh,bqh); + } + } + mb(); + spin_unlock_irqrestore (&s->qh_lock, flags); + ((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb; + } + + if (mode) { + qh->last_used = now; + list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion + } +} +/*-------------------------------------------------------------------*/ +// unlinks an urb by dequeuing its qh, waits some frames and forgets it +_static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb) +{ + uhci_desc_t *qh; + urb_priv_t *urb_priv; + unsigned long flags=0; spin_lock_irqsave (&s->urb_list_lock, flags); @@ -833,32 +1003,22 @@ _static int uhci_unlink_urb (urb_t *urb) switch (usb_pipetype (urb->pipe)) { case PIPE_ISOCHRONOUS: case PIPE_INTERRUPT: - for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) { - td = list_entry (p, uhci_desc_t, desc_list); - unlink_td (s, td, 1); - } - // wait at least 1 Frame - uhci_wait_ms(1); - while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) { - td = list_entry (p, uhci_desc_t, desc_list); - list_del (p); - delete_desc (td); - } + uhci_clean_iso_step1(s, urb_priv); + uhci_wait_ms(1); + uhci_clean_iso_step2(s, urb_priv); break; case PIPE_BULK: case PIPE_CONTROL: qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list); - - unlink_qh (s, qh); // remove this qh from qh-list - qh->last_used=UHCI_GET_CURRENT_FRAME(s); - list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion - // wait at least 1 Frame + spin_lock_irqsave (&s->urb_list_lock, flags); + uhci_clean_transfer(s, urb, qh, 1); + spin_unlock_irqrestore (&s->urb_list_lock, flags); uhci_wait_ms(1); } #ifdef DEBUG_SLAB - kmem_cache_free(urb_priv_kmem, urb->hcpriv); + kmem_cache_free (urb_priv_kmem, urb->hcpriv); #else kfree (urb->hcpriv); #endif @@ -874,6 +1034,147 @@ _static int uhci_unlink_urb (urb_t *urb) return 0; } /*-------------------------------------------------------------------*/ +// async unlink_urb completion/cleanup work +// has to be protected by urb_list_lock! +// features: if set in transfer_flags, the resulting status of the killed +// transaction is not overwritten + +_static void uhci_cleanup_unlink(uhci_t *s, int force) +{ + struct list_head *q; + urb_t *urb; + struct usb_device *dev; + int pipe,now; + urb_priv_t *urb_priv; + + q=s->urb_unlinked.next; + now=UHCI_GET_CURRENT_FRAME(s); + + while (q != &s->urb_unlinked) { + + urb = list_entry (q, urb_t, urb_list); + + urb_priv = (urb_priv_t*)urb->hcpriv; + q = urb->urb_list.next; + + if (force || + ((urb_priv->started != 0xffffffff) && (urb_priv->started != now))) { + async_dbg("async cleanup %p",urb); + switch (usb_pipetype (urb->pipe)) { // process descriptors + case PIPE_CONTROL: + process_transfer (s, urb, 2); + break; + case PIPE_BULK: + if (!s->avoid_bulk.counter) + process_transfer (s, urb, 2); // don't unlink (already done) + else + continue; + break; + case PIPE_ISOCHRONOUS: + process_iso (s, urb, 1); // force, don't unlink + break; + case PIPE_INTERRUPT: + process_interrupt (s, urb); + break; + } + + if (!(urb->transfer_flags & USB_TIMEOUT_KILLED)) + urb->status = -ECONNRESET; // mark as asynchronously killed + + pipe = urb->pipe; // completion may destroy all... + dev = urb->dev; + urb_priv = urb->hcpriv; + + if (urb->complete) { + spin_unlock(&s->urb_list_lock); + urb->complete ((struct urb *) urb); + spin_lock(&s->urb_list_lock); + } + + if (!(urb->transfer_flags & USB_TIMEOUT_KILLED)) + urb->status = -ENOENT; // now the urb is really dead + + usb_dec_dev_use (dev); +#ifdef DEBUG_SLAB + kmem_cache_free (urb_priv_kmem, urb_priv); +#else + kfree (urb_priv); +#endif + switch (usb_pipetype (pipe)) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + uhci_clean_iso_step2(s, urb_priv); + break; + } + list_del (&urb->urb_list); + } + } +} + +/*-------------------------------------------------------------------*/ +_static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb) +{ + uhci_desc_t *qh; + urb_priv_t *urb_priv; + + async_dbg("unlink_urb_async called %p",urb); + + if (urb->status == -EINPROGRESS) { + ((urb_priv_t*)urb->hcpriv)->started = ~0; + dequeue_urb (s, urb); + list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb + + s->unlink_urb_done = 1; + + urb->status = -ECONNABORTED; // mark urb as "waiting to be killed" + urb_priv = (urb_priv_t*)urb->hcpriv; + + switch (usb_pipetype (urb->pipe)) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + uhci_clean_iso_step1 (s, urb_priv); + break; + + case PIPE_BULK: + case PIPE_CONTROL: + qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list); + uhci_clean_transfer (s, urb, qh, 0); + break; + } + ((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s); + } + + return -EINPROGRESS; +} +/*-------------------------------------------------------------------*/ +_static int uhci_unlink_urb (urb_t *urb) +{ + uhci_t *s; + unsigned long flags=0; + dbg("uhci_unlink_urb called for %p",urb); + if (!urb || !urb->dev) // you never know... + return -EINVAL; + + s = (uhci_t*) urb->dev->bus->hcpriv; + + if (usb_pipedevice (urb->pipe) == s->rh.devnum) + return rh_unlink_urb (urb); + + if (!urb->hcpriv) + return -EINVAL; + + if (urb->transfer_flags & USB_ASYNC_UNLINK) { + int ret; + + spin_lock_irqsave (&s->urb_list_lock, flags); + ret = uhci_unlink_urb_async(s, urb); + spin_unlock_irqrestore (&s->urb_list_lock, flags); + return ret; + } + else + return uhci_unlink_urb_sync(s, urb); +} +/*-------------------------------------------------------------------*/ // In case of ASAP iso transfer, search the URB-list for already queued URBs // for this EP and calculate the earliest start frame for the new // URB (easy seamless URB continuation!) @@ -886,9 +1187,9 @@ _static int find_iso_limits (urb_t *urb, unsigned int *start, unsigned int *end) unsigned long flags; spin_lock_irqsave (&s->urb_list_lock, flags); - p=s->urb_list.next; + p=s->urb_list.prev; - for (; p != &s->urb_list; p = p->next) { + for (; p != &s->urb_list; p = p->prev) { u = list_entry (p, urb_t, urb_list); // look for pending URBs with identical pipe handle // works only because iso doesn't toggle the data bit! @@ -906,8 +1207,7 @@ _static int find_iso_limits (urb_t *urb, unsigned int *start, unsigned int *end) spin_unlock_irqrestore(&s->urb_list_lock, flags); - return ret; // no previous urb found - + return ret; } /*-------------------------------------------------------------------*/ // adjust start_frame according to scheduling constraints (ASAP etc) @@ -940,35 +1240,7 @@ _static int iso_find_start (urb_t *urb) info("iso_find_start: gap in seamless isochronous scheduling"); dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x", now, urb->start_frame, urb->number_of_packets, urb->pipe); -// The following code is only for debugging purposes... -#if 0 - { - uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; - struct list_head *p; - urb_t *u; - int a = -1, b = -1; - unsigned long flags; - - spin_lock_irqsave (&s->urb_list_lock, flags); - p=s->urb_list.next; - - for (; p != &s->urb_list; p = p->next) { - u = list_entry (p, urb_t, urb_list); - if (urb->dev != u->dev) - continue; - dbg("urb: pipe 0x%08x status %d start_frame %u number_of_packets %u", - u->pipe, u->status, u->start_frame, u->number_of_packets); - if (!usb_pipeisoc (u->pipe)) - continue; - if (a == -1) - a = u->start_frame; - b = (u->start_frame + u->number_of_packets - 1) & 1023; - } - spin_unlock_irqrestore(&s->urb_list_lock, flags); - } -#endif urb->start_frame = (now + 5) & 1023; // 5ms setup should be enough //FIXME! - //return -EAGAIN; //FIXME } } } @@ -996,7 +1268,7 @@ _static int iso_find_start (urb_t *urb) /*-------------------------------------------------------------------*/ // submits USB interrupt (ie. polling ;-) // ASAP-flag set implicitely -// if period==0, the the transfer is only done once (usb_scsi need this...) +// if period==0, the the transfer is only done once _static int uhci_submit_int_urb (urb_t *urb) { @@ -1005,12 +1277,9 @@ _static int uhci_submit_int_urb (urb_t *urb) int nint, n, ret; uhci_desc_t *td; int status, destination; - int now; int info; unsigned int pipe = urb->pipe; - //dbg("SUBMIT INT"); - if (urb->interval < 0 || urb->interval >= 256) return -EINVAL; @@ -1029,8 +1298,7 @@ _static int uhci_submit_int_urb (urb_t *urb) dbg("Rounded interval to %i, chain %i", urb->interval, nint); - now = UHCI_GET_CURRENT_FRAME (s) & 1023; - urb->start_frame = now; // remember start frame, just in case... + urb->start_frame = UHCI_GET_CURRENT_FRAME (s) & 1023; // remember start frame, just in case... urb->number_of_packets = 1; @@ -1062,13 +1330,6 @@ _static int uhci_submit_int_urb (urb_t *urb) usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); -#if 0 - td = tdm[urb->number_of_packets]; - fill_td (td, TD_CTRL_IOC, 0, 0); - insert_td_horizontal (s, s->iso_td[(urb->start_frame + (urb->number_of_packets) * urb->interval + 1) & 1023], td); - list_add_tail (&td->desc_list, &urb_priv->desc_list); -#endif - return 0; } /*-------------------------------------------------------------------*/ @@ -1086,11 +1347,11 @@ _static int uhci_submit_iso_urb (urb_t *urb) __save_flags(flags); __cli(); // Disable IRQs to schedule all ISO-TDs in time ret = iso_find_start (urb); // adjusts urb->start_frame for later use - + if (ret) goto err; - tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL); + tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), KMALLOC_FLAG); if (!tdm) { ret = -ENOMEM; @@ -1105,14 +1366,16 @@ _static int uhci_submit_iso_urb (urb_t *urb) tdm[n] = 0; continue; } - #ifdef ISO_SANITY_CHECK + if(urb->iso_frame_desc[n].length > maxsze) { +#ifdef ISO_SANITY_CHECK err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze); tdm[n] = 0; ret=-EINVAL; goto inval; +#endif } - #endif + ret = alloc_td (&td, UHCI_PTR_DEPTH); inval: if (ret) { @@ -1120,7 +1383,7 @@ _static int uhci_submit_iso_urb (urb_t *urb) for (i = 0; i < n; n++) if (tdm[i]) - kfree (tdm[i]); + delete_desc(tdm[i]); kfree (tdm); goto err; } @@ -1165,15 +1428,16 @@ _static int uhci_submit_iso_urb (urb_t *urb) } /*-------------------------------------------------------------------*/ -_static int search_dev_ep (uhci_t *s, urb_t *urb) +// returns: 0 (no transfer queued), urb* (this urb already queued) + +_static urb_t* search_dev_ep (uhci_t *s, urb_t *urb) { - unsigned long flags; struct list_head *p; urb_t *tmp; unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0); dbg("search_dev_ep:"); - spin_lock_irqsave (&s->urb_list_lock, flags); + p=s->urb_list.next; for (; p != &s->urb_list; p = p->next) { @@ -1181,13 +1445,12 @@ _static int search_dev_ep (uhci_t *s, urb_t *urb) dbg("urb: %p", tmp); // we can accept this urb if it is not queued at this time // or if non-iso transfer requests should be scheduled for the same device and pipe - if ((!usb_pipeisoc(urb->pipe) && tmp->dev == urb->dev && !((tmp->pipe ^ urb->pipe) & mask)) || + if ((!usb_pipeisoc(urb->pipe) && (tmp->dev == urb->dev) && !((tmp->pipe ^ urb->pipe) & mask)) || (urb == tmp)) { - spin_unlock_irqrestore (&s->urb_list_lock, flags); - return 1; // found another urb already queued for processing + return tmp; // found another urb already queued for processing } } - spin_unlock_irqrestore (&s->urb_list_lock, flags); + return 0; } /*-------------------------------------------------------------------*/ @@ -1196,60 +1459,93 @@ _static int uhci_submit_urb (urb_t *urb) uhci_t *s; urb_priv_t *urb_priv; int ret = 0; - + unsigned long flags; + urb_t *bulk_urb=NULL; + if (!urb->dev || !urb->dev->bus) return -ENODEV; s = (uhci_t*) urb->dev->bus->hcpriv; //dbg("submit_urb: %p type %d",urb,usb_pipetype(urb->pipe)); - + + if (!s->running) + return -ENODEV; + if (usb_pipedevice (urb->pipe) == s->rh.devnum) return rh_submit_urb (urb); /* virtual root hub */ usb_inc_dev_use (urb->dev); - if (search_dev_ep (s, urb)) { - usb_dec_dev_use (urb->dev); - return -ENXIO; // urb already queued + spin_lock_irqsave (&s->urb_list_lock, flags); + + bulk_urb = search_dev_ep (s, urb); + if (bulk_urb) { + + queue_dbg("found bulk urb %p\n",bulk_urb); + + if ((usb_pipetype (urb->pipe) != PIPE_BULK) || + ((usb_pipetype (urb->pipe) == PIPE_BULK) && + (!(urb->transfer_flags & USB_QUEUE_BULK) || !(bulk_urb->transfer_flags & USB_QUEUE_BULK)))) { + spin_unlock_irqrestore (&s->urb_list_lock, flags); + usb_dec_dev_use (urb->dev); + err("ENXIO1 %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,bulk_urb); + return -ENXIO; // urb already queued + } } #ifdef DEBUG_SLAB - urb_priv = kmem_cache_alloc(urb_priv_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL); + urb_priv = kmem_cache_alloc(urb_priv_kmem, SLAB_FLAG); #else - urb_priv = kmalloc (sizeof (urb_priv_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL); + urb_priv = kmalloc (sizeof (urb_priv_t), KMALLOC_FLAG); #endif if (!urb_priv) { usb_dec_dev_use (urb->dev); + spin_unlock_irqrestore (&s->urb_list_lock, flags); return -ENOMEM; } urb->hcpriv = urb_priv; INIT_LIST_HEAD (&urb_priv->desc_list); - urb_priv->short_control_packet=0; + urb_priv->short_control_packet = 0; dbg("submit_urb: scheduling %p", urb); - - switch (usb_pipetype (urb->pipe)) { - case PIPE_ISOCHRONOUS: - ret = uhci_submit_iso_urb (urb); - break; - case PIPE_INTERRUPT: - ret = uhci_submit_int_urb (urb); - break; - case PIPE_CONTROL: - //dump_urb (urb); - ret = uhci_submit_control_urb (urb); - break; - case PIPE_BULK: - ret = uhci_submit_bulk_urb (urb); - break; - default: - ret = -EINVAL; + urb_priv->next_queued_urb = NULL; + urb_priv->prev_queued_urb = NULL; + urb_priv->bottom_qh = NULL; + urb_priv->next_qh = NULL; + + if (usb_pipetype (urb->pipe) == PIPE_BULK) { + + if (bulk_urb) { + while (((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb) // find last queued bulk + bulk_urb=((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb; + + ((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb=urb; + } + atomic_inc (&s->avoid_bulk); + ret = uhci_submit_bulk_urb (urb, bulk_urb); + atomic_dec (&s->avoid_bulk); + spin_unlock_irqrestore (&s->urb_list_lock, flags); + } + else { + spin_unlock_irqrestore (&s->urb_list_lock, flags); + switch (usb_pipetype (urb->pipe)) { + case PIPE_ISOCHRONOUS: + ret = uhci_submit_iso_urb (urb); + break; + case PIPE_INTERRUPT: + ret = uhci_submit_int_urb (urb); + break; + case PIPE_CONTROL: + ret = uhci_submit_control_urb (urb); + break; + default: + ret = -EINVAL; + } } dbg("submit_urb: scheduled with ret: %d", ret); - if (ret != 0) { usb_dec_dev_use (urb->dev); #ifdef DEBUG_SLAB @@ -1262,41 +1558,43 @@ _static int uhci_submit_urb (urb_t *urb) return 0; } -#ifdef USE_RECLAMATION_LOOP -// Removes bandwidth reclamation if URB idles too long -void check_idling_urbs(uhci_t *s) + +// Checks for URB timeout and removes bandwidth reclamation +// if URB idles too long +_static void uhci_check_timeouts(uhci_t *s) { struct list_head *p,*p2; urb_t *urb; int type; - //dbg("check_idling_urbs: enter i:%d",in_interrupt()); - - spin_lock (&s->urb_list_lock); p = s->urb_list.prev; while (p != &s->urb_list) { + urb_priv_t *hcpriv; + p2 = p; p = p->prev; - urb=list_entry (p2, urb_t, urb_list); - type=usb_pipetype (urb->pipe); - -#if 0 - err("URB timers: %li now: %li %i\n", - ((urb_priv_t*)urb->hcpriv)->started +IDLE_TIMEOUT, jiffies, - type); -#endif - if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) && - (((urb_priv_t*)urb->hcpriv)->use_loop) && - ((((urb_priv_t*)urb->hcpriv)->started +IDLE_TIMEOUT) < jiffies)) - disable_desc_loop(s,urb); + urb = list_entry (p2, urb_t, urb_list); + type = usb_pipetype (urb->pipe); + + hcpriv = (urb_priv_t*)urb->hcpriv; + + if ( urb->timeout && + ((hcpriv->started + urb->timeout) < jiffies)) { + urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK; + async_dbg("uhci_check_timeout: timeout for %p",urb); + uhci_unlink_urb_async(s, urb); + } +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH + else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) && + (hcpriv->use_loop) && + ((hcpriv->started + IDLE_TIMEOUT) < jiffies)) + disable_desc_loop(s, urb); +#endif } - spin_unlock (&s->urb_list_lock); - - //dbg("check_idling_urbs: finished"); } -#endif + /*------------------------------------------------------------------- Virtual Root Hub -------------------------------------------------------------------*/ @@ -1396,7 +1694,6 @@ _static int rh_send_irq (urb_t *urb) dbg("Root-Hub INT complete: port1: %x port2: %x data: %x", inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2), data); urb->complete (urb); - } return 0; } @@ -1411,10 +1708,6 @@ _static void rh_int_timer_do (unsigned long ptr) urb_t *urb = (urb_t*) ptr; uhci_t *uhci = urb->dev->bus->hcpriv; -#ifdef USE_RECLAMATION_LOOP - check_idling_urbs(uhci); -#endif - if (uhci->rh.send) { len = rh_send_irq (urb); if (len > 0) { @@ -1427,7 +1720,9 @@ _static void rh_int_timer_do (unsigned long ptr) } /*-------------------------------------------------------------------------*/ -/* Root Hub INTs are polled by this timer */ +/* Root Hub INTs are polled by this timer, polling interval 20ms */ +/* This time is also used for URB-timeout checking */ + _static int rh_init_int_timer (urb_t *urb) { uhci_t *uhci = urb->dev->bus->hcpriv; @@ -1436,7 +1731,7 @@ _static int rh_init_int_timer (urb_t *urb) init_timer (&uhci->rh.rh_int_timer); uhci->rh.rh_int_timer.function = rh_int_timer_do; uhci->rh.rh_int_timer.data = (unsigned long) urb; - uhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000; + uhci->rh.rh_int_timer.expires = jiffies + (HZ * 20) / 1000; add_timer (&uhci->rh.rh_int_timer); return 0; @@ -1640,7 +1935,6 @@ _static int rh_submit_urb (urb_t *urb) stat = -EPIPE; } - dbg("Root-Hub stat port1: %x port2: %x", inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2)); @@ -1716,13 +2010,19 @@ _static void uhci_unlink_urbs(uhci_t *s, struct usb_device *usb_dev, int remove_ p = s->urb_list.prev; while (p != &s->urb_list) { p2 = p; - p = p->prev; + p = p->prev ; urb = list_entry (p2, urb_t, urb_list); - dbg("urb: %p", urb); + dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev); + + //urb->transfer_flags |=USB_ASYNC_UNLINK; + if (remove_all || (usb_dev == urb->dev)) { + spin_unlock_irqrestore (&s->urb_list_lock, flags); warn("forced removing of queued URB %p due to disconnect",urb); uhci_unlink_urb(urb); - urb->dev = NULL; // avoid further processing of this URB + urb->dev = NULL; // avoid further processing of this UR + spin_lock_irqsave (&s->urb_list_lock, flags); + p = s->urb_list.prev; } } spin_unlock_irqrestore (&s->urb_list_lock, flags); @@ -1732,13 +2032,11 @@ _static int uhci_free_dev (struct usb_device *usb_dev) { uhci_t *s; - dbg("uhci_free_dev"); if(!usb_dev || !usb_dev->bus || !usb_dev->bus->hcpriv) return -EINVAL; - s=(uhci_t*) usb_dev->bus->hcpriv; - + s=(uhci_t*) usb_dev->bus->hcpriv; uhci_unlink_urbs(s, usb_dev, 0); return 0; @@ -1769,12 +2067,10 @@ struct usb_operations uhci_device_operations = * have announced. This leads to a queue abort due to the short packet, * the status stage is not executed. If this happens, the status stage * is manually re-executed. - * FIXME: Stall-condition may override 'nearly' successful CTRL-IN-transfer - * when the transfered length fits exactly in maxsze-packets. A bit - * more intelligence is needed to detect this and finish without error. + * mode: 0: QHs already unlinked */ -_static int process_transfer (uhci_t *s, urb_t *urb) +_static int process_transfer (uhci_t *s, urb_t *urb, int mode) { int ret = 0; urb_priv_t *urb_priv = urb->hcpriv; @@ -1784,25 +2080,21 @@ _static int process_transfer (uhci_t *s, urb_t *urb) uhci_desc_t *desc= list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list); uhci_desc_t *last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical); int data_toggle = usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); // save initial data_toggle - - - // extracted and remapped info from TD - int maxlength; + int maxlength; // extracted and remapped info from TD int actual_length; int status = 0; - dbg("process_transfer: urb contains bulk/control request"); - + //dbg("process_transfer: urb contains bulk/control request"); /* if the status phase has been retriggered and the queue is empty or the last status-TD is inactive, the retriggered status stage is completed */ -#if 1 + if (urb_priv->short_control_packet && ((qh->hw.qh.element == UHCI_PTR_TERM) ||(!(last_desc->hw.td.status & TD_CTRL_ACTIVE)))) goto transfer_finished; -#endif + urb->actual_length=0; for (; p != &qh->vertical; p = p->next) { @@ -1810,22 +2102,20 @@ _static int process_transfer (uhci_t *s, urb_t *urb) if (desc->hw.td.status & TD_CTRL_ACTIVE) // do not process active TDs return ret; - - // extract transfer parameters from TD - actual_length = (desc->hw.td.status + 1) & 0x7ff; + + actual_length = (desc->hw.td.status + 1) & 0x7ff; // extract transfer parameters from TD maxlength = (((desc->hw.td.info >> 21) & 0x7ff) + 1) & 0x7ff; status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (urb->pipe)); - // see if EP is stalled - if (status == -EPIPE) { + if (status == -EPIPE) { // see if EP is stalled // set up stalled condition usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); } - // if any error occured stop processing of further TDs - if (status != 0) { + if (status != 0) { // if any error occured stop processing of further TDs // only set ret if status returned an error - uhci_show_td (desc); + if (status != -EPIPE) + uhci_show_td (desc); ret = status; urb->error_count++; break; @@ -1833,11 +2123,6 @@ _static int process_transfer (uhci_t *s, urb_t *urb) else if ((desc->hw.td.info & 0xff) != USB_PID_SETUP) urb->actual_length += actual_length; -#if 0 - // if (i++==0) - uhci_show_td (desc); // show first TD of each transfer -#endif - // got less data than requested if ( (actual_length < maxlength)) { if (urb->transfer_flags & USB_DISABLE_SPD) { @@ -1852,8 +2137,8 @@ _static int process_transfer (uhci_t *s, urb_t *urb) qh->hw.qh.element = virt_to_bus (last_desc); // re-trigger status stage dbg("short packet during control transfer, retrigger status stage @ %p",last_desc); - uhci_show_td (desc); - uhci_show_td (last_desc); + //uhci_show_td (desc); + //uhci_show_td (last_desc); urb_priv->short_control_packet=1; return 0; } @@ -1864,34 +2149,24 @@ _static int process_transfer (uhci_t *s, urb_t *urb) } data_toggle = uhci_toggle (desc->hw.td.info); - //dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle); + queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle); } + usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle); - transfer_finished: - unlink_qh (s, qh); - //delete_qh (s, qh); - qh->last_used=UHCI_GET_CURRENT_FRAME(s); - list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion + transfer_finished: + + uhci_clean_transfer(s, urb, qh, (mode==0?2:1)); urb->status = status; -#ifdef USE_RECLAMATION_LOOP +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH disable_desc_loop(s,urb); #endif - dbg("process_transfer: urb %p, wanted len %d, len %d status %x err %d", + queue_dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d", urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count); - //dbg("process_transfer: exit"); -#if 0 - if (urb->actual_length){ - char *uu; - uu=urb->transfer_buffer; - dbg("%x %x %x %x %x %x %x %x", - *uu,*(uu+1),*(uu+2),*(uu+3),*(uu+4),*(uu+5),*(uu+6),*(uu+7)); - } -#endif return ret; } @@ -1934,15 +2209,13 @@ _static int process_interrupt (uhci_t *s, urb_t *urb) // if any error occured: ignore this td, and continue if (status != 0) { - uhci_show_td (desc); + //uhci_show_td (desc); urb->error_count++; goto recycle; } else urb->actual_length = actual_length; - // FIXME: SPD? - recycle: if (urb->complete) { //dbg("process_interrupt: calling completion, status %i",status); @@ -1962,6 +2235,7 @@ _static int process_interrupt (uhci_t *s, urb_t *urb) desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE); if (status==0) { + ((urb_priv_t*)urb->hcpriv)->started=jiffies; desc->hw.td.info |= (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE); usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); @@ -1981,8 +2255,8 @@ _static int process_interrupt (uhci_t *s, urb_t *urb) return ret; } - -_static int process_iso (uhci_t *s, urb_t *urb) +// mode: 1: force processing, don't unlink tds (already unlinked) +_static int process_iso (uhci_t *s, urb_t *urb, int mode) { int i; int ret = 0; @@ -1991,16 +2265,18 @@ _static int process_iso (uhci_t *s, urb_t *urb) uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list); dbg("urb contains iso request"); - if (desc->hw.td.status & TD_CTRL_ACTIVE) + if ((desc->hw.td.status & TD_CTRL_ACTIVE) && !mode) return -EXDEV; // last TD not finished urb->error_count = 0; urb->actual_length = 0; urb->status = 0; + dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s), + urb->number_of_packets,mode,desc->hw.td.status); for (i = 0; p != &urb_priv->desc_list; p = p->next, i++) { desc = list_entry (p, uhci_desc_t, desc_list); - + //uhci_show_td(desc); if (desc->hw.td.status & TD_CTRL_ACTIVE) { // means we have completed the last TD, but not the TDs before @@ -2013,7 +2289,8 @@ _static int process_iso (uhci_t *s, urb_t *urb) goto err; } - unlink_td (s, desc, 1); + if (!mode) + unlink_td (s, desc, 1); if (urb->number_of_packets <= i) { dbg("urb->number_of_packets (%d)<=(%d)", urb->number_of_packets, i); @@ -2038,13 +2315,14 @@ _static int process_iso (uhci_t *s, urb_t *urb) urb->error_count++; urb->status = urb->iso_frame_desc[i].status; } - dbg("process_iso: len:%d status:%x", - urb->iso_frame_desc[i].length, urb->iso_frame_desc[i].status); + dbg("process_iso: %i: len:%d %08x status:%x", + i, urb->iso_frame_desc[i].actual_length, desc->hw.td.status,urb->iso_frame_desc[i].status); delete_desc (desc); list_del (p); } - dbg("process_iso: exit %i (%d)", i, ret); + + dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length); return ret; } @@ -2056,15 +2334,20 @@ _static int process_urb (uhci_t *s, struct list_head *p) urb=list_entry (p, urb_t, urb_list); - dbg("found queued urb: %p", urb); + //dbg("process_urb: found queued urb: %p", urb); switch (usb_pipetype (urb->pipe)) { case PIPE_CONTROL: + ret = process_transfer (s, urb, 1); + break; case PIPE_BULK: - ret = process_transfer (s, urb); + if (!s->avoid_bulk.counter) + ret = process_transfer (s, urb, 1); + else + return 0; break; case PIPE_ISOCHRONOUS: - ret = process_iso (s, urb); + ret = process_iso (s, urb, 0); break; case PIPE_INTERRUPT: ret = process_interrupt (s, urb); @@ -2158,23 +2441,18 @@ _static void uhci_interrupt (int irq, void *__uhci, struct pt_regs *regs) if (status != 1) { warn("interrupt, status %x, frame# %i", status, UHCI_GET_CURRENT_FRAME(s)); - //uhci_show_queue(s->control_chain); + // remove host controller halted state if ((status&0x20) && (s->running)) { - // more to be done - check TDs for invalid entries - // but TDs are only invalid if somewhere else is a (memory ?) problem outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD); } //uhci_show_status (s); } - //beep(1000); /* - * the following is very subtle and was blatantly wrong before * traverse the list in *reverse* direction, because new entries * may be added at the end. * also, because process_urb may unlink the current urb, * we need to advance the list before - * - Thomas Sailer */ spin_lock (&s->urb_list_lock); @@ -2186,18 +2464,23 @@ restart: p2 = p; p = p->prev; process_urb (s, p2); - if(s->unlink_urb_done) - { + if (s->unlink_urb_done) { s->unlink_urb_done=0; goto restart; } } - spin_unlock (&s->urb_list_lock); - clean_descs(s,0); + if ((s->frame_counter & 63) == 0) + uhci_check_timeouts(s); + clean_descs(s,0); + uhci_cleanup_unlink(s, 0); + + spin_unlock (&s->urb_list_lock); + + s->frame_counter++; outw (status, io_addr + USBSTS); - dbg("done"); + //dbg("uhci_interrupt: done"); } _static void reset_hc (uhci_t *s) @@ -2249,15 +2532,19 @@ _static void __exit uhci_cleanup_dev(uhci_t *s) { struct usb_device *root_hub = s->bus->root_hub; + s->running = 0; // Don't allow submit_urb + if (root_hub) usb_disconnect (&root_hub); - uhci_unlink_urbs(s, 0, 1); // Forced unlink of remaining URBs + reset_hc (s); + wait_ms (1); + uhci_unlink_urbs (s, 0, 1); // Forced unlink of remaining URBs + uhci_cleanup_unlink (s, 1); // force cleanup of async killed URBs + usb_deregister_bus (s->bus); - s->running = 0; - reset_hc (s); release_region (s->io_addr, s->io_size); free_irq (s->irq, s); usb_free_bus (s->bus); @@ -2285,6 +2572,7 @@ _static int __init uhci_start_usb (uhci_t *s) return 0; } +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44) _static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data) { uhci_t *s = (uhci_t*) dev->data; @@ -2301,6 +2589,7 @@ _static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data) } return 0; } +#endif _static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size) { @@ -2315,14 +2604,17 @@ _static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_add memset (s, 0, sizeof (uhci_t)); INIT_LIST_HEAD (&s->free_desc); INIT_LIST_HEAD (&s->urb_list); + INIT_LIST_HEAD (&s->urb_unlinked); spin_lock_init (&s->urb_list_lock); spin_lock_init (&s->qh_lock); spin_lock_init (&s->td_lock); + atomic_set(&s->avoid_bulk, 0); s->irq = -1; s->io_addr = io_addr; s->io_size = io_size; s->next = devs; //chain new uhci device into global list - + s->frame_counter = 0; + bus = usb_alloc_bus (&uhci_device_operations); if (!bus) { kfree (s); @@ -2389,11 +2681,11 @@ _static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_add //chain new uhci device into global list devs = s; - +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44) pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(dev), handle_pm_event); if (pmdev) pmdev->data = s; - +#endif return 0; } @@ -2407,7 +2699,7 @@ _static int __init start_uhci (struct pci_dev *dev) unsigned int io_addr = dev->resource[i].start; unsigned int io_size = dev->resource[i].end - dev->resource[i].start + 1; - if (!(dev->resource[i].flags & IORESOURCE_IO)) + if (!(dev->resource[i].flags & 1)) continue; #else unsigned int io_addr = dev->base_address[i]; @@ -2422,6 +2714,10 @@ _static int __init start_uhci (struct pci_dev *dev) break; /* disable legacy emulation */ pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT); + if(dev->vendor==0x8086) { + info("Intel USB controller: setting latency timer to %d", UHCI_LATENCY_TIMER); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, UHCI_LATENCY_TIMER); + } return alloc_uhci(dev, dev->irq, io_addr, io_size); } return -1; @@ -2452,6 +2748,9 @@ int __init uhci_init (void) #endif info(VERSTR); +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH + info("High bandwidth mode enabled"); +#endif for (;;) { dev = pci_find_class (PCI_CLASS_SERIAL_USB << 8, dev); if (!dev) @@ -2506,7 +2805,9 @@ int init_module (void) void cleanup_module (void) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44) pm_unregister_all (handle_pm_event); +#endif uhci_cleanup (); } diff --git a/drivers/usb/usb-uhci.h b/drivers/usb/usb-uhci.h index e74e23bd8..93173f2c2 100644 --- a/drivers/usb/usb-uhci.h +++ b/drivers/usb/usb-uhci.h @@ -2,10 +2,11 @@ #define __LINUX_UHCI_H /* - $Id: usb-uhci.h,v 1.41 2000/02/13 21:37:38 acher Exp $ + $Id: usb-uhci.h,v 1.50 2000/03/13 21:18:04 fliegl Exp $ */ #define MODNAME "usb-uhci" -#define VERSTR "version v1.184 time " __TIME__ " " __DATE__ +#define VERSTR "$Revision: 1.50 $ time " __TIME__ " " __DATE__ +#define UHCI_LATENCY_TIMER 0 static __inline__ void uhci_wait_ms(unsigned int ms) { @@ -154,9 +155,13 @@ typedef struct { typedef struct { struct list_head desc_list; // list pointer to all corresponding TDs/QHs associated with this request - int short_control_packet; unsigned long started; - int use_loop; + urb_t *next_queued_urb; // next queued urb for this EP + urb_t *prev_queued_urb; + uhci_desc_t *bottom_qh; + uhci_desc_t *next_qh; // next helper QH + char use_loop; + char short_control_packet; } urb_priv_t, *purb_priv_t; struct virt_root_hub { @@ -186,12 +191,14 @@ typedef struct uhci { spinlock_t urb_list_lock; // lock to keep consistency int unlink_urb_done; + atomic_t avoid_bulk; struct usb_bus *bus; // our bus __u32 *framelist; uhci_desc_t **iso_td; uhci_desc_t *int_chain[8]; + uhci_desc_t *ls_control_chain; uhci_desc_t *control_chain; uhci_desc_t *bulk_chain; uhci_desc_t *chain_end; @@ -200,6 +207,9 @@ typedef struct uhci { spinlock_t td_lock; struct virt_root_hub rh; //private data of the virtual root hub int loop_usage; // URBs using bandwidth reclamation + + struct list_head urb_unlinked; // list of all unlinked urbs + int frame_counter; } uhci_t, *puhci_t; |