summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/Config.in4
-rw-r--r--drivers/usb/Makefile18
-rw-r--r--drivers/usb/dsbr100.c353
-rw-r--r--drivers/usb/inode.c1
-rw-r--r--drivers/usb/ov511.c484
-rw-r--r--drivers/usb/ov511.h9
-rw-r--r--drivers/usb/pegasus.c714
-rw-r--r--drivers/usb/serial/Makefile9
-rw-r--r--drivers/usb/uhci.c56
-rw-r--r--drivers/usb/uhci.h6
-rw-r--r--drivers/usb/usb-core.c7
-rw-r--r--drivers/usb/usb-storage.c1124
-rw-r--r--drivers/usb/usb-storage.h24
-rw-r--r--drivers/usb/usb-uhci-debug.h79
-rw-r--r--drivers/usb/usb-uhci.c975
-rw-r--r--drivers/usb/usb-uhci.h18
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(&current->sigmask_lock);
signr = dequeue_signal(&current->blocked, &info);
spin_unlock_irq(&current->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;