summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/Kconfig4
-rw-r--r--drivers/usb/Makefile4
-rw-r--r--drivers/usb/atm/Kconfig30
-rw-r--r--drivers/usb/atm/Makefile7
-rw-r--r--drivers/usb/atm/speedtch.c866
-rw-r--r--drivers/usb/atm/usb_atm.c1205
-rw-r--r--drivers/usb/atm/usb_atm.h159
-rw-r--r--drivers/usb/class/Kconfig3
-rw-r--r--drivers/usb/class/audio.c37
-rw-r--r--drivers/usb/class/bluetty.c12
-rw-r--r--drivers/usb/class/cdc-acm.c33
-rw-r--r--drivers/usb/class/cdc-acm.h1
-rw-r--r--drivers/usb/class/usb-midi.c19
-rw-r--r--drivers/usb/class/usblp.c4
-rw-r--r--drivers/usb/core/devices.c18
-rw-r--r--drivers/usb/core/devio.c57
-rw-r--r--drivers/usb/core/hcd-pci.c6
-rw-r--r--drivers/usb/core/hcd.c33
-rw-r--r--drivers/usb/core/hcd.h4
-rw-r--r--drivers/usb/core/hub.c397
-rw-r--r--drivers/usb/core/inode.c121
-rw-r--r--drivers/usb/core/message.c50
-rw-r--r--drivers/usb/core/sysfs.c64
-rw-r--r--drivers/usb/core/urb.c5
-rw-r--r--drivers/usb/core/usb.c200
-rw-r--r--drivers/usb/core/usb.h10
-rw-r--r--drivers/usb/gadget/Kconfig15
-rw-r--r--drivers/usb/gadget/dummy_hcd.c39
-rw-r--r--drivers/usb/gadget/ether.c46
-rw-r--r--drivers/usb/gadget/file_storage.c43
-rw-r--r--drivers/usb/gadget/gadget_chips.h6
-rw-r--r--drivers/usb/gadget/goku_udc.c14
-rw-r--r--drivers/usb/gadget/lh7a40x_udc.c7
-rw-r--r--drivers/usb/gadget/net2280.c10
-rw-r--r--drivers/usb/gadget/omap_udc.c146
-rw-r--r--drivers/usb/gadget/omap_udc.h11
-rw-r--r--drivers/usb/gadget/pxa2xx_udc.c17
-rw-r--r--drivers/usb/gadget/rndis.c51
-rw-r--r--drivers/usb/gadget/zero.c2
-rw-r--r--drivers/usb/host/ehci-hcd.c5
-rw-r--r--drivers/usb/host/ehci-hub.c2
-rw-r--r--drivers/usb/host/hc_sl811.c8
-rw-r--r--drivers/usb/host/ohci-dbg.c4
-rw-r--r--drivers/usb/host/ohci-hcd.c254
-rw-r--r--drivers/usb/host/ohci-hub.c63
-rw-r--r--drivers/usb/host/ohci-lh7a404.c30
-rw-r--r--drivers/usb/host/ohci-omap.c30
-rw-r--r--drivers/usb/host/ohci-pci.c37
-rw-r--r--drivers/usb/host/ohci-pxa27x.c460
-rw-r--r--drivers/usb/host/ohci-sa1111.c25
-rw-r--r--drivers/usb/host/ohci.h34
-rw-r--r--drivers/usb/host/uhci-hcd.c253
-rw-r--r--drivers/usb/host/uhci-hcd.h18
-rw-r--r--drivers/usb/host/uhci-hub.c134
-rw-r--r--drivers/usb/image/Kconfig7
-rw-r--r--drivers/usb/image/hpusbscsi.c12
-rw-r--r--drivers/usb/image/mdc800.c18
-rw-r--r--drivers/usb/image/microtek.c17
-rw-r--r--drivers/usb/image/microtek.h1
-rw-r--r--drivers/usb/input/aiptek.c4
-rw-r--r--drivers/usb/input/ati_remote.c7
-rw-r--r--drivers/usb/input/hid-core.c22
-rw-r--r--drivers/usb/input/kbtab.c4
-rw-r--r--drivers/usb/input/mtouchusb.c4
-rw-r--r--drivers/usb/input/pid.c2
-rw-r--r--drivers/usb/input/powermate.c2
-rw-r--r--drivers/usb/input/touchkitusb.c4
-rw-r--r--drivers/usb/input/usbkbd.c4
-rw-r--r--drivers/usb/input/usbmouse.c4
-rw-r--r--drivers/usb/input/wacom.c4
-rw-r--r--drivers/usb/input/xpad.c4
-rw-r--r--drivers/usb/media/Kconfig6
-rw-r--r--drivers/usb/media/dabusb.c6
-rw-r--r--drivers/usb/media/konicawc.c9
-rw-r--r--drivers/usb/media/ov511.c2
-rw-r--r--drivers/usb/media/se401.c6
-rw-r--r--drivers/usb/media/sn9c102.h19
-rw-r--r--drivers/usb/media/sn9c102_core.c148
-rw-r--r--drivers/usb/media/sn9c102_pas106b.c14
-rw-r--r--drivers/usb/media/sn9c102_pas202bcb.c21
-rw-r--r--drivers/usb/media/sn9c102_sensor.h61
-rw-r--r--drivers/usb/media/sn9c102_tas5110c1b.c45
-rw-r--r--drivers/usb/media/sn9c102_tas5130d1b.c60
-rw-r--r--drivers/usb/media/stv680.c4
-rw-r--r--drivers/usb/media/usbvideo.c4
-rw-r--r--drivers/usb/misc/Kconfig12
-rw-r--r--drivers/usb/misc/Makefile1
-rw-r--r--drivers/usb/misc/auerswald.c14
-rw-r--r--drivers/usb/misc/legousbtower.c4
-rw-r--r--drivers/usb/misc/speedtch.c1373
-rw-r--r--drivers/usb/misc/tiglusb.c1
-rw-r--r--drivers/usb/misc/uss720.c4
-rw-r--r--drivers/usb/net/catc.c8
-rw-r--r--drivers/usb/net/kaweth.c22
-rw-r--r--drivers/usb/net/pegasus.c12
-rw-r--r--drivers/usb/net/rtl8150.c10
-rw-r--r--drivers/usb/net/usbnet.c21
-rw-r--r--drivers/usb/serial/Kconfig10
-rw-r--r--drivers/usb/serial/Makefile1
-rw-r--r--drivers/usb/serial/belkin_sa.c8
-rw-r--r--drivers/usb/serial/cyberjack.c6
-rw-r--r--drivers/usb/serial/digi_acceleport.c6
-rw-r--r--drivers/usb/serial/empeg.c8
-rw-r--r--drivers/usb/serial/ftdi_sio.c24
-rw-r--r--drivers/usb/serial/ftdi_sio.h15
-rw-r--r--drivers/usb/serial/generic.c4
-rw-r--r--drivers/usb/serial/io_edgeport.c6
-rw-r--r--drivers/usb/serial/io_ti.c6
-rw-r--r--drivers/usb/serial/ipaq.c23
-rw-r--r--drivers/usb/serial/ipw.c496
-rw-r--r--drivers/usb/serial/ir-usb.c2
-rw-r--r--drivers/usb/serial/keyspan_pda.c6
-rw-r--r--drivers/usb/serial/kl5kusb105.c12
-rw-r--r--drivers/usb/serial/kobil_sct.c14
-rw-r--r--drivers/usb/serial/mct_u232.c6
-rw-r--r--drivers/usb/serial/omninet.c4
-rw-r--r--drivers/usb/serial/pl2303.c430
-rw-r--r--drivers/usb/serial/usb-serial.c125
-rw-r--r--drivers/usb/serial/usb-serial.h1
-rw-r--r--drivers/usb/serial/visor.c6
-rw-r--r--drivers/usb/serial/whiteheat.c8
-rw-r--r--drivers/usb/storage/isd200.c6
-rw-r--r--drivers/usb/storage/protocol.c49
-rw-r--r--drivers/usb/storage/scsiglue.c31
-rw-r--r--drivers/usb/storage/transport.c25
-rw-r--r--drivers/usb/storage/unusual_devs.h136
-rw-r--r--drivers/usb/storage/usb.c70
-rw-r--r--drivers/usb/storage/usb.h3
128 files changed, 5658 insertions, 3009 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 0e805e4f3aa1..7bb3fa4c38b6 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -7,7 +7,7 @@ menu "USB support"
# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
config USB
tristate "Support for Host-side USB"
- depends on PCI || SA1111 || ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_LH7A404
+ depends on PCI || SA1111 || ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_LH7A404 || PXA27x
---help---
Universal Serial Bus (USB) is a specification for a serial bus
subsystem which offers higher speeds and more features than the
@@ -91,6 +91,8 @@ source "drivers/usb/serial/Kconfig"
source "drivers/usb/misc/Kconfig"
+source "drivers/usb/atm/Kconfig"
+
source "drivers/usb/gadget/Kconfig"
endmenu
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index e3787f35cd21..63a7f2d80f83 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -62,8 +62,10 @@ obj-$(CONFIG_USB_LCD) += misc/
obj-$(CONFIG_USB_LED) += misc/
obj-$(CONFIG_USB_LEGOTOWER) += misc/
obj-$(CONFIG_USB_RIO500) += misc/
-obj-$(CONFIG_USB_SPEEDTOUCH) += misc/
obj-$(CONFIG_USB_TEST) += misc/
obj-$(CONFIG_USB_TIGL) += misc/
obj-$(CONFIG_USB_USS720) += misc/
obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
+
+obj-$(CONFIG_USB_ATM) += atm/
+obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
diff --git a/drivers/usb/atm/Kconfig b/drivers/usb/atm/Kconfig
new file mode 100644
index 000000000000..0d9f5379b8cf
--- /dev/null
+++ b/drivers/usb/atm/Kconfig
@@ -0,0 +1,30 @@
+#
+# USB ATM driver configuration
+#
+comment "USB ATM/DSL drivers"
+ depends on USB
+
+config USB_ATM
+ tristate "Generic USB ATM/DSL core I/O support"
+ depends on USB && ATM
+ select CRC32
+ default n
+ help
+ This provides a library which is used for packet I/O by USB DSL
+ modems, such as the SpeedTouch driver below.
+
+ To compile this driver as a module, choose M here: the
+ module will be called usb_atm.
+
+config USB_SPEEDTOUCH
+ tristate "Alcatel Speedtouch USB support"
+ depends on USB && ATM
+ select USB_ATM
+ help
+ Say Y here if you have an Alcatel SpeedTouch USB or SpeedTouch 330
+ modem. In order to use your modem you will need to install the
+ two parts of the firmware, extracted by the user space tools; see
+ <http://www.linux-usb.org/SpeedTouch/> for details.
+
+ To compile this driver as a module, choose M here: the
+ module will be called speedtch.
diff --git a/drivers/usb/atm/Makefile b/drivers/usb/atm/Makefile
new file mode 100644
index 000000000000..9213b8b97587
--- /dev/null
+++ b/drivers/usb/atm/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the rest of the USB drivers
+# (the ones that don't fit into any other categories)
+#
+
+obj-$(CONFIG_USB_ATM) += usb_atm.o
+obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o
diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c
new file mode 100644
index 000000000000..f17e576d12ec
--- /dev/null
+++ b/drivers/usb/atm/speedtch.c
@@ -0,0 +1,866 @@
+/******************************************************************************
+ * speedtch.c - Alcatel SpeedTouch USB xDSL modem driver
+ *
+ * Copyright (C) 2001, Alcatel
+ * Copyright (C) 2003, Duncan Sands
+ * Copyright (C) 2004, David Woodhouse
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/crc32.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "usb_atm.h"
+
+/*
+#define DEBUG
+#define VERBOSE_DEBUG
+*/
+
+#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
+# define DEBUG
+#endif
+
+#include <linux/usb.h>
+
+#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
+# define USE_FW_LOADER
+#endif
+
+#ifdef VERBOSE_DEBUG
+static int udsl_print_packet(const unsigned char *data, int len);
+#define PACKETDEBUG(arg...) udsl_print_packet (arg)
+#define vdbg(arg...) dbg (arg)
+#else
+#define PACKETDEBUG(arg...)
+#define vdbg(arg...)
+#endif
+
+#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
+#define DRIVER_VERSION "1.8"
+#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
+
+static const char speedtch_driver_name[] = "speedtch";
+
+#define SPEEDTOUCH_VENDORID 0x06b9
+#define SPEEDTOUCH_PRODUCTID 0x4061
+
+/* Timeout in jiffies */
+#define CTRL_TIMEOUT (2*HZ)
+#define DATA_TIMEOUT (2*HZ)
+
+#define OFFSET_7 0 /* size 1 */
+#define OFFSET_b 1 /* size 8 */
+#define OFFSET_d 9 /* size 4 */
+#define OFFSET_e 13 /* size 1 */
+#define OFFSET_f 14 /* size 1 */
+#define TOTAL 15
+
+#define SIZE_7 1
+#define SIZE_b 8
+#define SIZE_d 4
+#define SIZE_e 1
+#define SIZE_f 1
+
+static int dl_512_first = 0;
+static int sw_buffering = 0;
+
+module_param(dl_512_first, bool, 0444);
+MODULE_PARM_DESC(dl_512_first, "Read 512 bytes before sending firmware");
+
+module_param(sw_buffering, uint, 0444);
+MODULE_PARM_DESC(sw_buffering, "Enable software buffering");
+
+#define UDSL_IOCTL_LINE_UP 1
+#define UDSL_IOCTL_LINE_DOWN 2
+
+#define SPEEDTCH_ENDPOINT_INT 0x81
+#define SPEEDTCH_ENDPOINT_DATA 0x07
+#define SPEEDTCH_ENDPOINT_FIRMWARE 0x05
+
+#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) )
+
+static struct usb_device_id speedtch_usb_ids[] = {
+ {USB_DEVICE(SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, speedtch_usb_ids);
+
+struct speedtch_instance_data {
+ struct udsl_instance_data u;
+
+ /* Status */
+ struct urb *int_urb;
+ unsigned char int_data[16];
+ struct work_struct poll_work;
+ struct timer_list poll_timer;
+};
+/* USB */
+
+static int speedtch_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id);
+static void speedtch_usb_disconnect(struct usb_interface *intf);
+static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code,
+ void *user_data);
+static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs);
+static void speedtch_poll_status(struct speedtch_instance_data *instance);
+
+static struct usb_driver speedtch_usb_driver = {
+ .owner = THIS_MODULE,
+ .name = speedtch_driver_name,
+ .probe = speedtch_usb_probe,
+ .disconnect = speedtch_usb_disconnect,
+ .ioctl = speedtch_usb_ioctl,
+ .id_table = speedtch_usb_ids,
+};
+
+/***************
+** firmware **
+***************/
+
+static void speedtch_got_firmware(struct speedtch_instance_data *instance,
+ int got_it)
+{
+ int err;
+ struct usb_interface *intf;
+
+ down(&instance->u.serialize); /* vs self, speedtch_firmware_start */
+ if (instance->u.status == UDSL_LOADED_FIRMWARE)
+ goto out;
+ if (!got_it) {
+ instance->u.status = UDSL_NO_FIRMWARE;
+ goto out;
+ }
+ if ((err = usb_set_interface(instance->u.usb_dev, 1, 1)) < 0) {
+ dbg("speedtch_got_firmware: usb_set_interface returned %d!", err);
+ instance->u.status = UDSL_NO_FIRMWARE;
+ goto out;
+ }
+
+ /* Set up interrupt endpoint */
+ intf = usb_ifnum_to_if(instance->u.usb_dev, 0);
+ if (intf && !usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) {
+
+ instance->int_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (instance->int_urb) {
+
+ usb_fill_int_urb(instance->int_urb, instance->u.usb_dev,
+ usb_rcvintpipe(instance->u.usb_dev, SPEEDTCH_ENDPOINT_INT),
+ instance->int_data,
+ sizeof(instance->int_data),
+ speedtch_handle_int, instance, 50);
+ err = usb_submit_urb(instance->int_urb, GFP_KERNEL);
+ if (err) {
+ /* Doesn't matter; we'll poll anyway */
+ dbg("speedtch_got_firmware: Submission of interrupt URB failed %d", err);
+ usb_free_urb(instance->int_urb);
+ instance->int_urb = NULL;
+ usb_driver_release_interface(&speedtch_usb_driver, intf);
+ }
+ }
+ }
+ /* Start status polling */
+ mod_timer(&instance->poll_timer, jiffies + (1 * HZ));
+
+ instance->u.status = UDSL_LOADED_FIRMWARE;
+ tasklet_schedule(&instance->u.receive_tasklet);
+ out:
+ up(&instance->u.serialize);
+ wake_up_interruptible(&instance->u.firmware_waiters);
+}
+
+static int speedtch_set_swbuff(struct speedtch_instance_data *instance,
+ int state)
+{
+ struct usb_device *dev = instance->u.usb_dev;
+ int ret;
+
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x32, 0x40, state ? 0x01 : 0x00,
+ 0x00, NULL, 0, 100);
+ if (ret < 0) {
+ printk("Warning: %sabling SW buffering: usb_control_msg returned %d\n",
+ state ? "En" : "Dis", ret);
+ return ret;
+ }
+
+ dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis");
+ return 0;
+}
+
+static void speedtch_test_sequence(struct speedtch_instance_data *instance)
+{
+ struct usb_device *dev = instance->u.usb_dev;
+ unsigned char buf[10];
+ int ret;
+
+ /* URB 147 */
+ buf[0] = 0x1c;
+ buf[1] = 0x50;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x01, 0x40, 0x0b, 0x00, buf, 2, 100);
+ if (ret < 0)
+ printk(KERN_WARNING "%s failed on URB147: %d\n", __func__, ret);
+
+ /* URB 148 */
+ buf[0] = 0x32;
+ buf[1] = 0x00;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x01, 0x40, 0x02, 0x00, buf, 2, 100);
+ if (ret < 0)
+ printk(KERN_WARNING "%s failed on URB148: %d\n", __func__, ret);
+
+ /* URB 149 */
+ buf[0] = 0x01;
+ buf[1] = 0x00;
+ buf[2] = 0x01;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x01, 0x40, 0x03, 0x00, buf, 3, 100);
+ if (ret < 0)
+ printk(KERN_WARNING "%s failed on URB149: %d\n", __func__, ret);
+
+ /* URB 150 */
+ buf[0] = 0x01;
+ buf[1] = 0x00;
+ buf[2] = 0x01;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x01, 0x40, 0x04, 0x00, buf, 3, 100);
+ if (ret < 0)
+ printk(KERN_WARNING "%s failed on URB150: %d\n", __func__, ret);
+}
+
+static int speedtch_start_synchro(struct speedtch_instance_data *instance)
+{
+ struct usb_device *dev = instance->u.usb_dev;
+ unsigned char buf[2];
+ int ret;
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x12, 0xc0, 0x04, 0x00,
+ buf, sizeof(buf), CTRL_TIMEOUT);
+ if (ret < 0) {
+ printk(KERN_WARNING "SpeedTouch: Failed to start ADSL synchronisation: %d\n", ret);
+ return ret;
+ }
+
+ dbg("speedtch_start_synchro: modem prodded. %d Bytes returned: %02x %02x", ret, buf[0], buf[1]);
+ return 0;
+}
+
+static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs)
+{
+ struct speedtch_instance_data *instance = urb->context;
+ unsigned int count = urb->actual_length;
+ int ret;
+
+ /* The magic interrupt for "up state" */
+ const static unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 };
+ /* The magic interrupt for "down state" */
+ const static unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated; clean up */
+ dbg("%s - urb shutting down with status: %d", __func__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __func__, urb->status);
+ goto exit;
+ }
+
+ if (count < 6) {
+ dbg("%s - int packet too short", __func__);
+ goto exit;
+ }
+
+ if (!memcmp(up_int, instance->int_data, 6)) {
+ del_timer(&instance->poll_timer);
+ printk(KERN_NOTICE "DSL line goes up\n");
+ } else if (!memcmp(down_int, instance->int_data, 6)) {
+ printk(KERN_NOTICE "DSL line goes down\n");
+ } else {
+ int i;
+
+ printk(KERN_DEBUG "Unknown interrupt packet of %d bytes:", count);
+ for (i = 0; i < count; i++)
+ printk(" %02x", instance->int_data[i]);
+ printk("\n");
+ }
+ schedule_work(&instance->poll_work);
+
+ exit:
+ rmb();
+ if (!instance->int_urb)
+ return;
+
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret)
+ err("%s - usb_submit_urb failed with result %d", __func__, ret);
+}
+
+static int speedtch_get_status(struct speedtch_instance_data *instance,
+ unsigned char *buf)
+{
+ struct usb_device *dev = instance->u.usb_dev;
+ int ret;
+
+ memset(buf, 0, TOTAL);
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7,
+ CTRL_TIMEOUT);
+ if (ret < 0) {
+ dbg("MSG 7 failed");
+ return ret;
+ }
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b,
+ CTRL_TIMEOUT);
+ if (ret < 0) {
+ dbg("MSG B failed");
+ return ret;
+ }
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d,
+ CTRL_TIMEOUT);
+ if (ret < 0) {
+ dbg("MSG D failed");
+ return ret;
+ }
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e,
+ CTRL_TIMEOUT);
+ if (ret < 0) {
+ dbg("MSG E failed");
+ return ret;
+ }
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f,
+ CTRL_TIMEOUT);
+ if (ret < 0) {
+ dbg("MSG F failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void speedtch_poll_status(struct speedtch_instance_data *instance)
+{
+ unsigned char buf[TOTAL];
+ int ret;
+
+ ret = speedtch_get_status(instance, buf);
+ if (ret) {
+ printk(KERN_WARNING
+ "SpeedTouch: Error %d fetching device status\n", ret);
+ return;
+ }
+
+ dbg("Line state %02x", buf[OFFSET_7]);
+
+ switch (buf[OFFSET_7]) {
+ case 0:
+ if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) {
+ instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
+ printk(KERN_NOTICE "ADSL line is down\n");
+ }
+ break;
+
+ case 0x08:
+ if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) {
+ instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+ printk(KERN_NOTICE "ADSL line is blocked?\n");
+ }
+ break;
+
+ case 0x10:
+ if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) {
+ instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
+ printk(KERN_NOTICE "ADSL line is synchronising\n");
+ }
+ break;
+
+ case 0x20:
+ if (instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND) {
+ int down_speed = buf[OFFSET_b] | (buf[OFFSET_b + 1] << 8)
+ | (buf[OFFSET_b + 2] << 16) | (buf[OFFSET_b + 3] << 24);
+ int up_speed = buf[OFFSET_b + 4] | (buf[OFFSET_b + 5] << 8)
+ | (buf[OFFSET_b + 6] << 16) | (buf[OFFSET_b + 7] << 24);
+
+ if (!(down_speed & 0x0000ffff) &&
+ !(up_speed & 0x0000ffff)) {
+ down_speed >>= 16;
+ up_speed >>= 16;
+ }
+ instance->u.atm_dev->link_rate = down_speed * 1000 / 424;
+ instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND;
+
+ printk(KERN_NOTICE
+ "ADSL line is up (%d Kib/s down | %d Kib/s up)\n",
+ down_speed, up_speed);
+ }
+ break;
+
+ default:
+ if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) {
+ instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+ printk(KERN_NOTICE "Unknown line state %02x\n", buf[OFFSET_7]);
+ }
+ break;
+ }
+}
+
+static void speedtch_timer_poll(unsigned long data)
+{
+ struct speedtch_instance_data *instance = (void *)data;
+
+ schedule_work(&instance->poll_work);
+ mod_timer(&instance->poll_timer, jiffies + (5 * HZ));
+}
+
+#ifdef USE_FW_LOADER
+static void speedtch_upload_firmware(struct speedtch_instance_data *instance,
+ const struct firmware *fw1,
+ const struct firmware *fw2)
+{
+ unsigned char *buffer;
+ struct usb_device *usb_dev = instance->u.usb_dev;
+ struct usb_interface *intf;
+ int actual_length, ret;
+ int offset;
+
+ dbg("speedtch_upload_firmware");
+
+ if (!(intf = usb_ifnum_to_if(usb_dev, 2))) {
+ dbg("speedtch_upload_firmware: interface not found!");
+ goto fail;
+ }
+
+ if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) {
+ dbg("speedtch_upload_firmware: no memory for buffer!");
+ goto fail;
+ }
+
+ /* A user-space firmware loader may already have claimed interface #2 */
+ if ((ret =
+ usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) < 0) {
+ dbg("speedtch_upload_firmware: interface in use (%d)!", ret);
+ goto fail_free;
+ }
+
+ /* URB 7 */
+ if (dl_512_first) { /* some modems need a read before writing the firmware */
+ ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+ buffer, 0x200, &actual_length, 2 * HZ);
+
+ if (ret < 0 && ret != -ETIMEDOUT)
+ dbg("speedtch_upload_firmware: read BLOCK0 from modem failed (%d)!", ret);
+ else
+ dbg("speedtch_upload_firmware: BLOCK0 downloaded (%d bytes)", ret);
+ }
+
+ /* URB 8 : both leds are static green */
+ for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) {
+ int thislen = min_t(int, PAGE_SIZE, fw1->size - offset);
+ memcpy(buffer, fw1->data + offset, thislen);
+
+ ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+ buffer, thislen, &actual_length, DATA_TIMEOUT);
+
+ if (ret < 0) {
+ dbg("speedtch_upload_firmware: write BLOCK1 to modem failed (%d)!", ret);
+ goto fail_release;
+ }
+ dbg("speedtch_upload_firmware: BLOCK1 uploaded (%d bytes)", fw1->size);
+ }
+
+ /* USB led blinking green, ADSL led off */
+
+ /* URB 11 */
+ ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+ buffer, 0x200, &actual_length, DATA_TIMEOUT);
+
+ if (ret < 0) {
+ dbg("speedtch_upload_firmware: read BLOCK2 from modem failed (%d)!", ret);
+ goto fail_release;
+ }
+ dbg("speedtch_upload_firmware: BLOCK2 downloaded (%d bytes)", actual_length);
+
+ /* URBs 12 to 139 - USB led blinking green, ADSL led off */
+ for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) {
+ int thislen = min_t(int, PAGE_SIZE, fw2->size - offset);
+ memcpy(buffer, fw2->data + offset, thislen);
+
+ ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+ buffer, thislen, &actual_length, DATA_TIMEOUT);
+
+ if (ret < 0) {
+ dbg("speedtch_upload_firmware: write BLOCK3 to modem failed (%d)!", ret);
+ goto fail_release;
+ }
+ }
+ dbg("speedtch_upload_firmware: BLOCK3 uploaded (%d bytes)", fw2->size);
+
+ /* USB led static green, ADSL led static red */
+
+ /* URB 142 */
+ ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+ buffer, 0x200, &actual_length, DATA_TIMEOUT);
+
+ if (ret < 0) {
+ dbg("speedtch_upload_firmware: read BLOCK4 from modem failed (%d)!", ret);
+ goto fail_release;
+ }
+
+ /* success */
+ dbg("speedtch_upload_firmware: BLOCK4 downloaded (%d bytes)", actual_length);
+
+ /* Delay to allow firmware to start up. We can do this here
+ because we're in our own kernel thread anyway. */
+ msleep(1000);
+
+ /* Enable software buffering, if requested */
+ if (sw_buffering)
+ speedtch_set_swbuff(instance, 1);
+
+ /* Magic spell; don't ask us what this does */
+ speedtch_test_sequence(instance);
+
+ /* Start modem synchronisation */
+ if (speedtch_start_synchro(instance))
+ dbg("speedtch_start_synchro: failed");
+
+ speedtch_got_firmware(instance, 1);
+
+ free_page((unsigned long)buffer);
+ return;
+
+ fail_release:
+ /* Only release interface #2 if uploading failed; we don't release it
+ we succeeded. This prevents the userspace tools from trying to load
+ the firmware themselves */
+ usb_driver_release_interface(&speedtch_usb_driver, intf);
+ fail_free:
+ free_page((unsigned long)buffer);
+ fail:
+ speedtch_got_firmware(instance, 0);
+}
+
+static int speedtch_find_firmware(struct speedtch_instance_data
+ *instance, int phase,
+ const struct firmware **fw_p)
+{
+ char buf[24];
+ const u16 bcdDevice = instance->u.usb_dev->descriptor.bcdDevice;
+ const u8 major_revision = bcdDevice >> 8;
+ const u8 minor_revision = bcdDevice & 0xff;
+
+ sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision);
+ dbg("speedtch_find_firmware: looking for %s", buf);
+
+ if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
+ sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision);
+ dbg("speedtch_find_firmware: looking for %s", buf);
+
+ if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
+ sprintf(buf, "speedtch-%d.bin", phase);
+ dbg("speedtch_find_firmware: looking for %s", buf);
+
+ if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
+ dev_warn(&instance->u.usb_dev->dev, "no stage %d firmware found!", phase);
+ return -ENOENT;
+ }
+ }
+ }
+
+ dev_info(&instance->u.usb_dev->dev, "found stage %d firmware %s\n", phase, buf);
+
+ return 0;
+}
+
+static int speedtch_load_firmware(void *arg)
+{
+ const struct firmware *fw1, *fw2;
+ struct speedtch_instance_data *instance = arg;
+
+ BUG_ON(!instance);
+
+ daemonize("firmware/speedtch");
+
+ if (!speedtch_find_firmware(instance, 1, &fw1)) {
+ if (!speedtch_find_firmware(instance, 2, &fw2)) {
+ speedtch_upload_firmware(instance, fw1, fw2);
+ release_firmware(fw2);
+ }
+ release_firmware(fw1);
+ }
+
+ /* In case we failed, set state back to NO_FIRMWARE so that
+ another later attempt may work. Otherwise, we never actually
+ manage to recover if, for example, the firmware is on /usr and
+ we look for it too early. */
+ speedtch_got_firmware(instance, 0);
+
+ module_put(THIS_MODULE);
+ udsl_put_instance(&instance->u);
+ return 0;
+}
+#endif /* USE_FW_LOADER */
+
+static void speedtch_firmware_start(struct speedtch_instance_data *instance)
+{
+#ifdef USE_FW_LOADER
+ int ret;
+#endif
+
+ dbg("speedtch_firmware_start");
+
+ down(&instance->u.serialize); /* vs self, speedtch_got_firmware */
+
+ if (instance->u.status >= UDSL_LOADING_FIRMWARE) {
+ up(&instance->u.serialize);
+ return;
+ }
+
+ instance->u.status = UDSL_LOADING_FIRMWARE;
+ up(&instance->u.serialize);
+
+#ifdef USE_FW_LOADER
+ udsl_get_instance(&instance->u);
+ try_module_get(THIS_MODULE);
+
+ ret = kernel_thread(speedtch_load_firmware, instance,
+ CLONE_FS | CLONE_FILES);
+
+ if (ret >= 0)
+ return; /* OK */
+
+ dbg("speedtch_firmware_start: kernel_thread failed (%d)!", ret);
+
+ module_put(THIS_MODULE);
+ udsl_put_instance(&instance->u);
+ /* Just pretend it never happened... hope modem_run happens */
+#endif /* USE_FW_LOADER */
+
+ speedtch_got_firmware(instance, 0);
+}
+
+static int speedtch_firmware_wait(struct udsl_instance_data *instance)
+{
+ speedtch_firmware_start((void *)instance);
+
+ if (wait_event_interruptible(instance->firmware_waiters, instance->status != UDSL_LOADING_FIRMWARE) < 0)
+ return -ERESTARTSYS;
+
+ return (instance->status == UDSL_LOADED_FIRMWARE) ? 0 : -EAGAIN;
+}
+
+/**********
+** USB **
+**********/
+
+static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code,
+ void *user_data)
+{
+ struct speedtch_instance_data *instance = usb_get_intfdata(intf);
+
+ dbg("speedtch_usb_ioctl entered");
+
+ if (!instance) {
+ dbg("speedtch_usb_ioctl: NULL instance!");
+ return -ENODEV;
+ }
+
+ switch (code) {
+ case UDSL_IOCTL_LINE_UP:
+ instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND;
+ speedtch_got_firmware(instance, 1);
+ return (instance->u.status == UDSL_LOADED_FIRMWARE) ? 0 : -EIO;
+ case UDSL_IOCTL_LINE_DOWN:
+ instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
+ return 0;
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int speedtch_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ int ifnum = intf->altsetting->desc.bInterfaceNumber;
+ struct speedtch_instance_data *instance;
+ unsigned char mac_str[13];
+ int ret, i;
+ char buf7[SIZE_7];
+
+ dbg("speedtch_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d", dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum);
+
+ if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) ||
+ (dev->descriptor.idVendor != SPEEDTOUCH_VENDORID) ||
+ (dev->descriptor.idProduct != SPEEDTOUCH_PRODUCTID) || (ifnum != 1))
+ return -ENODEV;
+
+ dbg("speedtch_usb_probe: device accepted");
+
+ /* instance init */
+ instance = kmalloc(sizeof(*instance), GFP_KERNEL);
+ if (!instance) {
+ dbg("speedtch_usb_probe: no memory for instance data!");
+ return -ENOMEM;
+ }
+
+ memset(instance, 0, sizeof(struct speedtch_instance_data));
+
+ if ((ret = usb_set_interface(dev, 0, 0)) < 0)
+ goto fail;
+
+ if ((ret = usb_set_interface(dev, 2, 0)) < 0)
+ goto fail;
+
+ instance->u.data_endpoint = SPEEDTCH_ENDPOINT_DATA;
+ instance->u.firmware_wait = speedtch_firmware_wait;
+ instance->u.driver_name = speedtch_driver_name;
+
+ ret = udsl_instance_setup(dev, &instance->u);
+ if (ret)
+ goto fail;
+
+ init_timer(&instance->poll_timer);
+ instance->poll_timer.function = speedtch_timer_poll;
+ instance->poll_timer.data = (unsigned long)instance;
+
+ INIT_WORK(&instance->poll_work, (void *)speedtch_poll_status, instance);
+
+ /* set MAC address, it is stored in the serial number */
+ memset(instance->u.atm_dev->esi, 0, sizeof(instance->u.atm_dev->esi));
+ if (usb_string(dev, dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) {
+ for (i = 0; i < 6; i++)
+ instance->u.atm_dev->esi[i] =
+ (hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1]));
+ }
+
+ /* First check whether the modem already seems to be alive */
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x12, 0xc0, 0x07, 0x00, buf7, SIZE_7, HZ / 2);
+
+ if (ret == SIZE_7) {
+ dbg("firmware appears to be already loaded");
+ speedtch_got_firmware(instance, 1);
+ speedtch_poll_status(instance);
+ } else {
+ speedtch_firmware_start(instance);
+ }
+
+ usb_set_intfdata(intf, instance);
+
+ return 0;
+
+ fail:
+ kfree(instance);
+
+ return -ENOMEM;
+}
+
+static void speedtch_usb_disconnect(struct usb_interface *intf)
+{
+ struct speedtch_instance_data *instance = usb_get_intfdata(intf);
+
+ dbg("speedtch_usb_disconnect entered");
+
+ if (!instance) {
+ dbg("speedtch_usb_disconnect: NULL instance!");
+ return;
+ }
+
+/*QQ need to handle disconnects on interface #2 while uploading firmware */
+/*QQ and what about interface #1? */
+
+ if (instance->int_urb) {
+ struct urb *int_urb = instance->int_urb;
+ instance->int_urb = NULL;
+ wmb();
+ usb_unlink_urb(int_urb);
+ usb_free_urb(int_urb);
+ }
+
+ instance->int_data[0] = 1;
+ del_timer_sync(&instance->poll_timer);
+ wmb();
+ flush_scheduled_work();
+
+ udsl_instance_disconnect(&instance->u);
+
+ /* clean up */
+ usb_set_intfdata(intf, NULL);
+ udsl_put_instance(&instance->u);
+}
+
+/***********
+** init **
+***********/
+
+static int __init speedtch_usb_init(void)
+{
+ dbg("speedtch_usb_init: driver version " DRIVER_VERSION);
+
+ return usb_register(&speedtch_usb_driver);
+}
+
+static void __exit speedtch_usb_cleanup(void)
+{
+ dbg("speedtch_usb_cleanup entered");
+
+ usb_deregister(&speedtch_usb_driver);
+}
+
+module_init(speedtch_usb_init);
+module_exit(speedtch_usb_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/usb/atm/usb_atm.c b/drivers/usb/atm/usb_atm.c
new file mode 100644
index 000000000000..8d1d365846a1
--- /dev/null
+++ b/drivers/usb/atm/usb_atm.c
@@ -0,0 +1,1205 @@
+/******************************************************************************
+ * usb_atm.c - Generic USB xDSL driver core
+ *
+ * Copyright (C) 2001, Alcatel
+ * Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas
+ * Copyright (C) 2004, David Woodhouse
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+/*
+ * Written by Johan Verrept, maintained by Duncan Sands (duncan.sands@free.fr)
+ *
+ * 1.7+: - See the check-in logs
+ *
+ * 1.6: - No longer opens a connection if the firmware is not loaded
+ * - Added support for the speedtouch 330
+ * - Removed the limit on the number of devices
+ * - Module now autoloads on device plugin
+ * - Merged relevant parts of sarlib
+ * - Replaced the kernel thread with a tasklet
+ * - New packet transmission code
+ * - Changed proc file contents
+ * - Fixed all known SMP races
+ * - Many fixes and cleanups
+ * - Various fixes by Oliver Neukum (oliver@neukum.name)
+ *
+ * 1.5A: - Version for inclusion in 2.5 series kernel
+ * - Modifications by Richard Purdie (rpurdie@rpsys.net)
+ * - made compatible with kernel 2.5.6 onwards by changing
+ * udsl_usb_send_data_context->urb to a pointer and adding code
+ * to alloc and free it
+ * - remove_wait_queue() added to udsl_atm_processqueue_thread()
+ *
+ * 1.5: - fixed memory leak when atmsar_decode_aal5 returned NULL.
+ * (reported by stephen.robinson@zen.co.uk)
+ *
+ * 1.4: - changed the spin_lock() under interrupt to spin_lock_irqsave()
+ * - unlink all active send urbs of a vcc that is being closed.
+ *
+ * 1.3.1: - added the version number
+ *
+ * 1.3: - Added multiple send urb support
+ * - fixed memory leak and vcc->tx_inuse starvation bug
+ * when not enough memory left in vcc.
+ *
+ * 1.2: - Fixed race condition in udsl_usb_send_data()
+ * 1.1: - Turned off packet debugging
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/crc32.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "usb_atm.h"
+
+/*
+#define DEBUG
+#define VERBOSE_DEBUG
+*/
+
+#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
+# define DEBUG
+#endif
+
+#include <linux/usb.h>
+
+#ifdef DEBUG
+#define UDSL_ASSERT(x) BUG_ON(!(x))
+#else
+#define UDSL_ASSERT(x) do { if (!(x)) warn("failed assertion '" #x "' at line %d", __LINE__); } while(0)
+#endif
+
+#ifdef VERBOSE_DEBUG
+static int udsl_print_packet(const unsigned char *data, int len);
+#define PACKETDEBUG(arg...) udsl_print_packet (arg)
+#define vdbg(arg...) dbg (arg)
+#else
+#define PACKETDEBUG(arg...)
+#define vdbg(arg...)
+#endif
+
+#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
+#define DRIVER_VERSION "1.8"
+#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
+
+static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS;
+static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS;
+static unsigned int num_rcv_bufs = UDSL_DEFAULT_RCV_BUFS;
+static unsigned int num_snd_bufs = UDSL_DEFAULT_SND_BUFS;
+static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE;
+static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE;
+
+module_param(num_rcv_urbs, uint, 0444);
+MODULE_PARM_DESC(num_rcv_urbs,
+ "Number of urbs used for reception (range: 0-"
+ __MODULE_STRING(UDSL_MAX_RCV_URBS) ", default: "
+ __MODULE_STRING(UDSL_DEFAULT_RCV_URBS) ")");
+
+module_param(num_snd_urbs, uint, 0444);
+MODULE_PARM_DESC(num_snd_urbs,
+ "Number of urbs used for transmission (range: 0-"
+ __MODULE_STRING(UDSL_MAX_SND_URBS) ", default: "
+ __MODULE_STRING(UDSL_DEFAULT_SND_URBS) ")");
+
+module_param(num_rcv_bufs, uint, 0444);
+MODULE_PARM_DESC(num_rcv_bufs,
+ "Number of buffers used for reception (range: 0-"
+ __MODULE_STRING(UDSL_MAX_RCV_BUFS) ", default: "
+ __MODULE_STRING(UDSL_DEFAULT_RCV_BUFS) ")");
+
+module_param(num_snd_bufs, uint, 0444);
+MODULE_PARM_DESC(num_snd_bufs,
+ "Number of buffers used for transmission (range: 0-"
+ __MODULE_STRING(UDSL_MAX_SND_BUFS) ", default: "
+ __MODULE_STRING(UDSL_DEFAULT_SND_BUFS) ")");
+
+module_param(rcv_buf_size, uint, 0444);
+MODULE_PARM_DESC(rcv_buf_size,
+ "Size of the buffers used for reception (range: 0-"
+ __MODULE_STRING(UDSL_MAX_RCV_BUF_SIZE) ", default: "
+ __MODULE_STRING(UDSL_DEFAULT_RCV_BUF_SIZE) ")");
+
+module_param(snd_buf_size, uint, 0444);
+MODULE_PARM_DESC(snd_buf_size,
+ "Size of the buffers used for transmission (range: 0-"
+ __MODULE_STRING(UDSL_MAX_SND_BUF_SIZE) ", default: "
+ __MODULE_STRING(UDSL_DEFAULT_SND_BUF_SIZE) ")");
+
+/* ATM */
+
+static void udsl_atm_dev_close(struct atm_dev *dev);
+static int udsl_atm_open(struct atm_vcc *vcc);
+static void udsl_atm_close(struct atm_vcc *vcc);
+static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg);
+static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb);
+static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page);
+
+static struct atmdev_ops udsl_atm_devops = {
+ .dev_close = udsl_atm_dev_close,
+ .open = udsl_atm_open,
+ .close = udsl_atm_close,
+ .ioctl = udsl_atm_ioctl,
+ .send = udsl_atm_send,
+ .proc_read = udsl_atm_proc_read,
+ .owner = THIS_MODULE,
+};
+
+/***********
+** misc **
+***********/
+
+static inline void udsl_pop(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb(skb);
+}
+
+/*************
+** decode **
+*************/
+
+static inline struct udsl_vcc_data *udsl_find_vcc(struct udsl_instance_data *instance,
+ short vpi, int vci)
+{
+ struct udsl_vcc_data *vcc;
+
+ list_for_each_entry(vcc, &instance->vcc_list, list)
+ if ((vcc->vci == vci) && (vcc->vpi == vpi))
+ return vcc;
+ return NULL;
+}
+
+static void udsl_extract_cells(struct udsl_instance_data *instance,
+ unsigned char *source, unsigned int howmany)
+{
+ struct udsl_vcc_data *cached_vcc = NULL;
+ struct atm_vcc *vcc;
+ struct sk_buff *sarb;
+ struct udsl_vcc_data *vcc_data;
+ int cached_vci = 0;
+ unsigned int i;
+ int pti;
+ int vci;
+ short cached_vpi = 0;
+ short vpi;
+
+ for (i = 0; i < howmany;
+ i++, source += ATM_CELL_SIZE + instance->rcv_padding) {
+ vpi = ((source[0] & 0x0f) << 4) | (source[1] >> 4);
+ vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4);
+ pti = (source[3] & 0x2) != 0;
+
+ vdbg("udsl_extract_cells: vpi %hd, vci %d, pti %d", vpi, vci, pti);
+
+ if (cached_vcc && (vci == cached_vci) && (vpi == cached_vpi))
+ vcc_data = cached_vcc;
+ else if ((vcc_data = udsl_find_vcc(instance, vpi, vci))) {
+ cached_vcc = vcc_data;
+ cached_vpi = vpi;
+ cached_vci = vci;
+ } else {
+ dbg("udsl_extract_cells: unknown vpi/vci (%hd/%d)!", vpi, vci);
+ continue;
+ }
+
+ vcc = vcc_data->vcc;
+ sarb = vcc_data->sarb;
+
+ if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) {
+ dbg("udsl_extract_cells: buffer overrun (sarb->len %u, vcc: 0x%p)!", sarb->len, vcc);
+ /* discard cells already received */
+ skb_trim(sarb, 0);
+ }
+
+ memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
+ __skb_put(sarb, ATM_CELL_PAYLOAD);
+
+ if (pti) {
+ struct sk_buff *skb;
+ unsigned int length;
+ unsigned int pdu_length;
+
+ length = (source[ATM_CELL_SIZE - 6] << 8) + source[ATM_CELL_SIZE - 5];
+
+ /* guard against overflow */
+ if (length > ATM_MAX_AAL5_PDU) {
+ dbg("udsl_extract_cells: bogus length %u (vcc: 0x%p)!", length, vcc);
+ atomic_inc(&vcc->stats->rx_err);
+ goto out;
+ }
+
+ pdu_length = UDSL_NUM_CELLS(length) * ATM_CELL_PAYLOAD;
+
+ if (sarb->len < pdu_length) {
+ dbg("udsl_extract_cells: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!", pdu_length, sarb->len, vcc);
+ atomic_inc(&vcc->stats->rx_err);
+ goto out;
+ }
+
+ if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) {
+ dbg("udsl_extract_cells: packet failed crc check (vcc: 0x%p)!", vcc);
+ atomic_inc(&vcc->stats->rx_err);
+ goto out;
+ }
+
+ vdbg("udsl_extract_cells: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", length, pdu_length, vcc);
+
+ if (!(skb = dev_alloc_skb(length))) {
+ dbg("udsl_extract_cells: no memory for skb (length: %u)!", length);
+ atomic_inc(&vcc->stats->rx_drop);
+ goto out;
+ }
+
+ vdbg("udsl_extract_cells: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", skb, skb->truesize);
+
+ if (!atm_charge(vcc, skb->truesize)) {
+ dbg("udsl_extract_cells: failed atm_charge (skb->truesize: %u)!", skb->truesize);
+ dev_kfree_skb(skb);
+ goto out; /* atm_charge increments rx_drop */
+ }
+
+ memcpy(skb->data, sarb->tail - pdu_length, length);
+ __skb_put(skb, length);
+
+ vdbg("udsl_extract_cells: sending skb 0x%p, skb->len %u, skb->truesize %u", skb, skb->len, skb->truesize);
+
+ PACKETDEBUG(skb->data, skb->len);
+
+ vcc->push(vcc, skb);
+
+ atomic_inc(&vcc->stats->rx);
+ out:
+ skb_trim(sarb, 0);
+ }
+ }
+}
+
+/*************
+** encode **
+*************/
+
+static inline void udsl_fill_cell_header(unsigned char *target, struct atm_vcc *vcc)
+{
+ target[0] = vcc->vpi >> 4;
+ target[1] = (vcc->vpi << 4) | (vcc->vci >> 12);
+ target[2] = vcc->vci >> 4;
+ target[3] = vcc->vci << 4;
+ target[4] = 0xec;
+}
+
+static const unsigned char zeros[ATM_CELL_PAYLOAD];
+
+static void udsl_groom_skb(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ struct udsl_control *ctrl = UDSL_SKB(skb);
+ unsigned int zero_padding;
+ u32 crc;
+
+ ctrl->atm_data.vcc = vcc;
+
+ ctrl->num_cells = UDSL_NUM_CELLS(skb->len);
+ ctrl->num_entire = skb->len / ATM_CELL_PAYLOAD;
+
+ zero_padding = ctrl->num_cells * ATM_CELL_PAYLOAD - skb->len - ATM_AAL5_TRAILER;
+
+ if (ctrl->num_entire + 1 < ctrl->num_cells)
+ ctrl->pdu_padding = zero_padding - (ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER);
+ else
+ ctrl->pdu_padding = zero_padding;
+
+ ctrl->aal5_trailer[0] = 0; /* UU = 0 */
+ ctrl->aal5_trailer[1] = 0; /* CPI = 0 */
+ ctrl->aal5_trailer[2] = skb->len >> 8;
+ ctrl->aal5_trailer[3] = skb->len;
+
+ crc = crc32_be(~0, skb->data, skb->len);
+ crc = crc32_be(crc, zeros, zero_padding);
+ crc = crc32_be(crc, ctrl->aal5_trailer, 4);
+ crc = ~crc;
+
+ ctrl->aal5_trailer[4] = crc >> 24;
+ ctrl->aal5_trailer[5] = crc >> 16;
+ ctrl->aal5_trailer[6] = crc >> 8;
+ ctrl->aal5_trailer[7] = crc;
+}
+
+static unsigned int udsl_write_cells(struct udsl_instance_data *instance,
+ unsigned int howmany, struct sk_buff *skb,
+ unsigned char **target_p)
+{
+ struct udsl_control *ctrl = UDSL_SKB(skb);
+ unsigned char *target = *target_p;
+ unsigned int nc, ne, i;
+
+ vdbg("udsl_write_cells: howmany=%u, skb->len=%d, num_cells=%u, num_entire=%u, pdu_padding=%u", howmany, skb->len, ctrl->num_cells, ctrl->num_entire, ctrl->pdu_padding);
+
+ nc = ctrl->num_cells;
+ ne = min(howmany, ctrl->num_entire);
+
+ for (i = 0; i < ne; i++) {
+ udsl_fill_cell_header(target, ctrl->atm_data.vcc);
+ target += ATM_CELL_HEADER;
+ memcpy(target, skb->data, ATM_CELL_PAYLOAD);
+ target += ATM_CELL_PAYLOAD;
+ if (instance->snd_padding) {
+ memset(target, 0, instance->snd_padding);
+ target += instance->snd_padding;
+ }
+ __skb_pull(skb, ATM_CELL_PAYLOAD);
+ }
+
+ ctrl->num_entire -= ne;
+
+ if (!(ctrl->num_cells -= ne) || !(howmany -= ne))
+ goto out;
+
+ if (instance->snd_padding) {
+ memset(target, 0, instance->snd_padding);
+ target += instance->snd_padding;
+ }
+ udsl_fill_cell_header(target, ctrl->atm_data.vcc);
+ target += ATM_CELL_HEADER;
+ memcpy(target, skb->data, skb->len);
+ target += skb->len;
+ __skb_pull(skb, skb->len);
+ memset(target, 0, ctrl->pdu_padding);
+ target += ctrl->pdu_padding;
+
+ if (--ctrl->num_cells) {
+ if (!--howmany) {
+ ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER;
+ goto out;
+ }
+
+ udsl_fill_cell_header(target, ctrl->atm_data.vcc);
+ target += ATM_CELL_HEADER;
+ memset(target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER);
+ target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER;
+
+ --ctrl->num_cells;
+ UDSL_ASSERT(!ctrl->num_cells);
+ }
+
+ memcpy(target, ctrl->aal5_trailer, ATM_AAL5_TRAILER);
+ target += ATM_AAL5_TRAILER;
+ /* set pti bit in last cell */
+ *(target + 3 - ATM_CELL_SIZE) |= 0x2;
+ if (instance->snd_padding) {
+ memset(target, 0, instance->snd_padding);
+ target += instance->snd_padding;
+ }
+ out:
+ *target_p = target;
+ return nc - ctrl->num_cells;
+}
+
+/**************
+** receive **
+**************/
+
+static void udsl_complete_receive(struct urb *urb, struct pt_regs *regs)
+{
+ struct udsl_receive_buffer *buf;
+ struct udsl_instance_data *instance;
+ struct udsl_receiver *rcv;
+ unsigned long flags;
+
+ if (!urb || !(rcv = urb->context)) {
+ dbg("udsl_complete_receive: bad urb!");
+ return;
+ }
+
+ instance = rcv->instance;
+ buf = rcv->buffer;
+
+ buf->filled_cells = urb->actual_length / (ATM_CELL_SIZE + instance->rcv_padding);
+
+ vdbg("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf);
+
+ UDSL_ASSERT(buf->filled_cells <= rcv_buf_size);
+
+ /* may not be in_interrupt() */
+ spin_lock_irqsave(&instance->receive_lock, flags);
+ list_add(&rcv->list, &instance->spare_receivers);
+ list_add_tail(&buf->list, &instance->filled_receive_buffers);
+ if (likely(!urb->status))
+ tasklet_schedule(&instance->receive_tasklet);
+ spin_unlock_irqrestore(&instance->receive_lock, flags);
+}
+
+static void udsl_process_receive(unsigned long data)
+{
+ struct udsl_receive_buffer *buf;
+ struct udsl_instance_data *instance = (struct udsl_instance_data *)data;
+ struct udsl_receiver *rcv;
+ int err;
+
+ made_progress:
+ while (!list_empty(&instance->spare_receive_buffers)) {
+ spin_lock_irq(&instance->receive_lock);
+ if (list_empty(&instance->spare_receivers)) {
+ spin_unlock_irq(&instance->receive_lock);
+ break;
+ }
+ rcv = list_entry(instance->spare_receivers.next,
+ struct udsl_receiver, list);
+ list_del(&rcv->list);
+ spin_unlock_irq(&instance->receive_lock);
+
+ buf = list_entry(instance->spare_receive_buffers.next,
+ struct udsl_receive_buffer, list);
+ list_del(&buf->list);
+
+ rcv->buffer = buf;
+
+ usb_fill_bulk_urb(rcv->urb, instance->usb_dev,
+ usb_rcvbulkpipe(instance->usb_dev, instance->data_endpoint),
+ buf->base,
+ rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding),
+ udsl_complete_receive, rcv);
+
+ vdbg("udsl_process_receive: sending urb 0x%p, rcv 0x%p, buf 0x%p",
+ rcv->urb, rcv, buf);
+
+ if ((err = usb_submit_urb(rcv->urb, GFP_ATOMIC)) < 0) {
+ dbg("udsl_process_receive: urb submission failed (%d)!", err);
+ list_add(&buf->list, &instance->spare_receive_buffers);
+ spin_lock_irq(&instance->receive_lock);
+ list_add(&rcv->list, &instance->spare_receivers);
+ spin_unlock_irq(&instance->receive_lock);
+ break;
+ }
+ }
+
+ spin_lock_irq(&instance->receive_lock);
+ if (list_empty(&instance->filled_receive_buffers)) {
+ spin_unlock_irq(&instance->receive_lock);
+ return; /* done - no more buffers */
+ }
+ buf = list_entry(instance->filled_receive_buffers.next,
+ struct udsl_receive_buffer, list);
+ list_del(&buf->list);
+ spin_unlock_irq(&instance->receive_lock);
+
+ vdbg("udsl_process_receive: processing buf 0x%p", buf);
+ udsl_extract_cells(instance, buf->base, buf->filled_cells);
+ list_add(&buf->list, &instance->spare_receive_buffers);
+ goto made_progress;
+}
+
+/***********
+** send **
+***********/
+
+static void udsl_complete_send(struct urb *urb, struct pt_regs *regs)
+{
+ struct udsl_instance_data *instance;
+ struct udsl_sender *snd;
+ unsigned long flags;
+
+ if (!urb || !(snd = urb->context) || !(instance = snd->instance)) {
+ dbg("udsl_complete_send: bad urb!");
+ return;
+ }
+
+ vdbg("udsl_complete_send: urb 0x%p, status %d, snd 0x%p, buf 0x%p", urb,
+ urb->status, snd, snd->buffer);
+
+ /* may not be in_interrupt() */
+ spin_lock_irqsave(&instance->send_lock, flags);
+ list_add(&snd->list, &instance->spare_senders);
+ list_add(&snd->buffer->list, &instance->spare_send_buffers);
+ tasklet_schedule(&instance->send_tasklet);
+ spin_unlock_irqrestore(&instance->send_lock, flags);
+}
+
+static void udsl_process_send(unsigned long data)
+{
+ struct udsl_send_buffer *buf;
+ struct udsl_instance_data *instance = (struct udsl_instance_data *)data;
+ struct sk_buff *skb;
+ struct udsl_sender *snd;
+ int err;
+ unsigned int num_written;
+
+ made_progress:
+ spin_lock_irq(&instance->send_lock);
+ while (!list_empty(&instance->spare_senders)) {
+ if (!list_empty(&instance->filled_send_buffers)) {
+ buf = list_entry(instance->filled_send_buffers.next,
+ struct udsl_send_buffer, list);
+ list_del(&buf->list);
+ } else if ((buf = instance->current_buffer)) {
+ instance->current_buffer = NULL;
+ } else /* all buffers empty */
+ break;
+
+ snd = list_entry(instance->spare_senders.next,
+ struct udsl_sender, list);
+ list_del(&snd->list);
+ spin_unlock_irq(&instance->send_lock);
+
+ snd->buffer = buf;
+ usb_fill_bulk_urb(snd->urb, instance->usb_dev,
+ usb_sndbulkpipe(instance->usb_dev, instance->data_endpoint),
+ buf->base,
+ (snd_buf_size - buf->free_cells) * (ATM_CELL_SIZE + instance->snd_padding),
+ udsl_complete_send, snd);
+
+ vdbg("udsl_process_send: submitting urb 0x%p (%d cells), snd 0x%p, buf 0x%p",
+ snd->urb, snd_buf_size - buf->free_cells, snd, buf);
+
+ if ((err = usb_submit_urb(snd->urb, GFP_ATOMIC)) < 0) {
+ dbg("udsl_process_send: urb submission failed (%d)!", err);
+ spin_lock_irq(&instance->send_lock);
+ list_add(&snd->list, &instance->spare_senders);
+ spin_unlock_irq(&instance->send_lock);
+ list_add(&buf->list, &instance->filled_send_buffers);
+ return; /* bail out */
+ }
+
+ spin_lock_irq(&instance->send_lock);
+ } /* while */
+ spin_unlock_irq(&instance->send_lock);
+
+ if (!instance->current_skb)
+ instance->current_skb = skb_dequeue(&instance->sndqueue);
+ if (!instance->current_skb)
+ return; /* done - no more skbs */
+
+ skb = instance->current_skb;
+
+ if (!(buf = instance->current_buffer)) {
+ spin_lock_irq(&instance->send_lock);
+ if (list_empty(&instance->spare_send_buffers)) {
+ instance->current_buffer = NULL;
+ spin_unlock_irq(&instance->send_lock);
+ return; /* done - no more buffers */
+ }
+ buf = list_entry(instance->spare_send_buffers.next,
+ struct udsl_send_buffer, list);
+ list_del(&buf->list);
+ spin_unlock_irq(&instance->send_lock);
+
+ buf->free_start = buf->base;
+ buf->free_cells = snd_buf_size;
+
+ instance->current_buffer = buf;
+ }
+
+ num_written = udsl_write_cells(instance, buf->free_cells, skb, &buf->free_start);
+
+ vdbg("udsl_process_send: wrote %u cells from skb 0x%p to buffer 0x%p",
+ num_written, skb, buf);
+
+ if (!(buf->free_cells -= num_written)) {
+ list_add_tail(&buf->list, &instance->filled_send_buffers);
+ instance->current_buffer = NULL;
+ }
+
+ vdbg("udsl_process_send: buffer contains %d cells, %d left",
+ snd_buf_size - buf->free_cells, buf->free_cells);
+
+ if (!UDSL_SKB(skb)->num_cells) {
+ struct atm_vcc *vcc = UDSL_SKB(skb)->atm_data.vcc;
+
+ udsl_pop(vcc, skb);
+ instance->current_skb = NULL;
+
+ atomic_inc(&vcc->stats->tx);
+ }
+
+ goto made_progress;
+}
+
+static void udsl_cancel_send(struct udsl_instance_data *instance,
+ struct atm_vcc *vcc)
+{
+ struct sk_buff *skb, *n;
+
+ dbg("udsl_cancel_send entered");
+ spin_lock_irq(&instance->sndqueue.lock);
+ for (skb = instance->sndqueue.next, n = skb->next;
+ skb != (struct sk_buff *)&instance->sndqueue;
+ skb = n, n = skb->next)
+ if (UDSL_SKB(skb)->atm_data.vcc == vcc) {
+ dbg("udsl_cancel_send: popping skb 0x%p", skb);
+ __skb_unlink(skb, &instance->sndqueue);
+ udsl_pop(vcc, skb);
+ }
+ spin_unlock_irq(&instance->sndqueue.lock);
+
+ tasklet_disable(&instance->send_tasklet);
+ if ((skb = instance->current_skb) && (UDSL_SKB(skb)->atm_data.vcc == vcc)) {
+ dbg("udsl_cancel_send: popping current skb (0x%p)", skb);
+ instance->current_skb = NULL;
+ udsl_pop(vcc, skb);
+ }
+ tasklet_enable(&instance->send_tasklet);
+ dbg("udsl_cancel_send done");
+}
+
+static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ struct udsl_instance_data *instance = vcc->dev->dev_data;
+ int err;
+
+ vdbg("udsl_atm_send called (skb 0x%p, len %u)", skb, skb->len);
+
+ if (!instance) {
+ dbg("udsl_atm_send: NULL data!");
+ err = -ENODEV;
+ goto fail;
+ }
+
+ if (vcc->qos.aal != ATM_AAL5) {
+ dbg("udsl_atm_send: unsupported ATM type %d!", vcc->qos.aal);
+ err = -EINVAL;
+ goto fail;
+ }
+
+ if (skb->len > ATM_MAX_AAL5_PDU) {
+ dbg("udsl_atm_send: packet too long (%d vs %d)!", skb->len,
+ ATM_MAX_AAL5_PDU);
+ err = -EINVAL;
+ goto fail;
+ }
+
+ PACKETDEBUG(skb->data, skb->len);
+
+ udsl_groom_skb(vcc, skb);
+ skb_queue_tail(&instance->sndqueue, skb);
+ tasklet_schedule(&instance->send_tasklet);
+
+ return 0;
+
+ fail:
+ udsl_pop(vcc, skb);
+ return err;
+}
+
+/********************
+** bean counting **
+********************/
+
+static void udsl_destroy_instance(struct kref *kref)
+{
+ struct udsl_instance_data *instance =
+ container_of(kref, struct udsl_instance_data, refcount);
+
+ tasklet_kill(&instance->receive_tasklet);
+ tasklet_kill(&instance->send_tasklet);
+ usb_put_dev(instance->usb_dev);
+ kfree(instance);
+}
+
+void udsl_get_instance(struct udsl_instance_data *instance)
+{
+ kref_get(&instance->refcount);
+}
+
+void udsl_put_instance(struct udsl_instance_data *instance)
+{
+ kref_put(&instance->refcount, udsl_destroy_instance);
+}
+
+/**********
+** ATM **
+**********/
+
+static void udsl_atm_dev_close(struct atm_dev *dev)
+{
+ struct udsl_instance_data *instance = dev->dev_data;
+
+ dev->dev_data = NULL;
+ udsl_put_instance(instance);
+}
+
+static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page)
+{
+ struct udsl_instance_data *instance = atm_dev->dev_data;
+ int left = *pos;
+
+ if (!instance) {
+ dbg("udsl_atm_proc_read: NULL instance!");
+ return -ENODEV;
+ }
+
+ if (!left--)
+ return sprintf(page, "%s\n", instance->description);
+
+ if (!left--)
+ return sprintf(page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ atm_dev->esi[0], atm_dev->esi[1],
+ atm_dev->esi[2], atm_dev->esi[3],
+ atm_dev->esi[4], atm_dev->esi[5]);
+
+ if (!left--)
+ return sprintf(page,
+ "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n",
+ atomic_read(&atm_dev->stats.aal5.tx),
+ atomic_read(&atm_dev->stats.aal5.tx_err),
+ atomic_read(&atm_dev->stats.aal5.rx),
+ atomic_read(&atm_dev->stats.aal5.rx_err),
+ atomic_read(&atm_dev->stats.aal5.rx_drop));
+
+ if (!left--) {
+ switch (atm_dev->signal) {
+ case ATM_PHY_SIG_FOUND:
+ sprintf(page, "Line up");
+ break;
+ case ATM_PHY_SIG_LOST:
+ sprintf(page, "Line down");
+ break;
+ default:
+ sprintf(page, "Line state unknown");
+ break;
+ }
+
+ if (instance->usb_dev->state == USB_STATE_NOTATTACHED)
+ strcat(page, ", disconnected\n");
+ else {
+ if (instance->status == UDSL_LOADED_FIRMWARE)
+ strcat(page, ", firmware loaded\n");
+ else if (instance->status == UDSL_LOADING_FIRMWARE)
+ strcat(page, ", firmware loading\n");
+ else
+ strcat(page, ", no firmware\n");
+ }
+
+ return strlen(page);
+ }
+
+ return 0;
+}
+
+static int udsl_atm_open(struct atm_vcc *vcc)
+{
+ struct udsl_instance_data *instance = vcc->dev->dev_data;
+ struct udsl_vcc_data *new;
+ unsigned int max_pdu;
+ int vci = vcc->vci;
+ short vpi = vcc->vpi;
+ int err;
+
+ dbg("udsl_atm_open: vpi %hd, vci %d", vpi, vci);
+
+ if (!instance) {
+ dbg("udsl_atm_open: NULL data!");
+ return -ENODEV;
+ }
+
+ /* only support AAL5 */
+ if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0)
+ || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) {
+ dbg("udsl_atm_open: unsupported ATM type %d!", vcc->qos.aal);
+ return -EINVAL;
+ }
+
+ if (instance->firmware_wait &&
+ (err = instance->firmware_wait(instance)) < 0) {
+ dbg("udsl_atm_open: firmware not loaded (%d)!", err);
+ return err;
+ }
+
+ down(&instance->serialize); /* vs self, udsl_atm_close */
+
+ if (udsl_find_vcc(instance, vpi, vci)) {
+ dbg("udsl_atm_open: %hd/%d already in use!", vpi, vci);
+ up(&instance->serialize);
+ return -EADDRINUSE;
+ }
+
+ if (!(new = kmalloc(sizeof(struct udsl_vcc_data), GFP_KERNEL))) {
+ dbg("udsl_atm_open: no memory for vcc_data!");
+ up(&instance->serialize);
+ return -ENOMEM;
+ }
+
+ memset(new, 0, sizeof(struct udsl_vcc_data));
+ new->vcc = vcc;
+ new->vpi = vpi;
+ new->vci = vci;
+
+ /* udsl_extract_cells requires at least one cell */
+ max_pdu = max(1, UDSL_NUM_CELLS(vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD;
+ if (!(new->sarb = alloc_skb(max_pdu, GFP_KERNEL))) {
+ dbg("udsl_atm_open: no memory for SAR buffer!");
+ kfree(new);
+ up(&instance->serialize);
+ return -ENOMEM;
+ }
+
+ vcc->dev_data = new;
+
+ tasklet_disable(&instance->receive_tasklet);
+ list_add(&new->list, &instance->vcc_list);
+ tasklet_enable(&instance->receive_tasklet);
+
+ set_bit(ATM_VF_ADDR, &vcc->flags);
+ set_bit(ATM_VF_PARTIAL, &vcc->flags);
+ set_bit(ATM_VF_READY, &vcc->flags);
+
+ up(&instance->serialize);
+
+ tasklet_schedule(&instance->receive_tasklet);
+
+ dbg("udsl_atm_open: allocated vcc data 0x%p (max_pdu: %u)", new, max_pdu);
+
+ return 0;
+}
+
+static void udsl_atm_close(struct atm_vcc *vcc)
+{
+ struct udsl_instance_data *instance = vcc->dev->dev_data;
+ struct udsl_vcc_data *vcc_data = vcc->dev_data;
+
+ dbg("udsl_atm_close called");
+
+ if (!instance || !vcc_data) {
+ dbg("udsl_atm_close: NULL data!");
+ return;
+ }
+
+ dbg("udsl_atm_close: deallocating vcc 0x%p with vpi %d vci %d",
+ vcc_data, vcc_data->vpi, vcc_data->vci);
+
+ udsl_cancel_send(instance, vcc);
+
+ down(&instance->serialize); /* vs self, udsl_atm_open */
+
+ tasklet_disable(&instance->receive_tasklet);
+ list_del(&vcc_data->list);
+ tasklet_enable(&instance->receive_tasklet);
+
+ kfree_skb(vcc_data->sarb);
+ vcc_data->sarb = NULL;
+
+ kfree(vcc_data);
+ vcc->dev_data = NULL;
+
+ vcc->vpi = ATM_VPI_UNSPEC;
+ vcc->vci = ATM_VCI_UNSPEC;
+ clear_bit(ATM_VF_READY, &vcc->flags);
+ clear_bit(ATM_VF_PARTIAL, &vcc->flags);
+ clear_bit(ATM_VF_ADDR, &vcc->flags);
+
+ up(&instance->serialize);
+
+ dbg("udsl_atm_close successful");
+}
+
+static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd,
+ void __user * arg)
+{
+ switch (cmd) {
+ case ATM_QUERYLOOP:
+ return put_user(ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0;
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+/**********
+** USB **
+**********/
+
+int udsl_instance_setup(struct usb_device *dev,
+ struct udsl_instance_data *instance)
+{
+ char *buf;
+ int i, length;
+
+ kref_init(&instance->refcount); /* one for USB */
+ udsl_get_instance(instance); /* one for ATM */
+
+ init_MUTEX(&instance->serialize);
+
+ instance->usb_dev = dev;
+
+ INIT_LIST_HEAD(&instance->vcc_list);
+
+ instance->status = UDSL_NO_FIRMWARE;
+ init_waitqueue_head(&instance->firmware_waiters);
+
+ spin_lock_init(&instance->receive_lock);
+ INIT_LIST_HEAD(&instance->spare_receivers);
+ INIT_LIST_HEAD(&instance->filled_receive_buffers);
+
+ tasklet_init(&instance->receive_tasklet, udsl_process_receive, (unsigned long)instance);
+ INIT_LIST_HEAD(&instance->spare_receive_buffers);
+
+ skb_queue_head_init(&instance->sndqueue);
+
+ spin_lock_init(&instance->send_lock);
+ INIT_LIST_HEAD(&instance->spare_senders);
+ INIT_LIST_HEAD(&instance->spare_send_buffers);
+
+ tasklet_init(&instance->send_tasklet, udsl_process_send,
+ (unsigned long)instance);
+ INIT_LIST_HEAD(&instance->filled_send_buffers);
+
+ /* receive init */
+ for (i = 0; i < num_rcv_urbs; i++) {
+ struct udsl_receiver *rcv = &(instance->receivers[i]);
+
+ if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
+ dbg("udsl_usb_probe: no memory for receive urb %d!", i);
+ goto fail;
+ }
+
+ rcv->instance = instance;
+
+ list_add(&rcv->list, &instance->spare_receivers);
+ }
+
+ for (i = 0; i < num_rcv_bufs; i++) {
+ struct udsl_receive_buffer *buf =
+ &(instance->receive_buffers[i]);
+
+ buf->base = kmalloc(rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding),
+ GFP_KERNEL);
+ if (!buf->base) {
+ dbg("udsl_usb_probe: no memory for receive buffer %d!", i);
+ goto fail;
+ }
+
+ list_add(&buf->list, &instance->spare_receive_buffers);
+ }
+
+ /* send init */
+ for (i = 0; i < num_snd_urbs; i++) {
+ struct udsl_sender *snd = &(instance->senders[i]);
+
+ if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) {
+ dbg("udsl_usb_probe: no memory for send urb %d!", i);
+ goto fail;
+ }
+
+ snd->instance = instance;
+
+ list_add(&snd->list, &instance->spare_senders);
+ }
+
+ for (i = 0; i < num_snd_bufs; i++) {
+ struct udsl_send_buffer *buf = &(instance->send_buffers[i]);
+
+ buf->base = kmalloc(snd_buf_size * (ATM_CELL_SIZE + instance->snd_padding),
+ GFP_KERNEL);
+ if (!buf->base) {
+ dbg("udsl_usb_probe: no memory for send buffer %d!", i);
+ goto fail;
+ }
+
+ list_add(&buf->list, &instance->spare_send_buffers);
+ }
+
+ /* ATM init */
+ instance->atm_dev = atm_dev_register(instance->driver_name,
+ &udsl_atm_devops, -1, NULL);
+ if (!instance->atm_dev) {
+ dbg("udsl_usb_probe: failed to register ATM device!");
+ goto fail;
+ }
+
+ instance->atm_dev->ci_range.vpi_bits = ATM_CI_MAX;
+ instance->atm_dev->ci_range.vci_bits = ATM_CI_MAX;
+ instance->atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+
+ /* temp init ATM device, set to 128kbit */
+ instance->atm_dev->link_rate = 128 * 1000 / 424;
+
+ /* device description */
+ buf = instance->description;
+ length = sizeof(instance->description);
+
+ if ((i = usb_string(dev, dev->descriptor.iProduct, buf, length)) < 0)
+ goto finish;
+
+ buf += i;
+ length -= i;
+
+ i = scnprintf(buf, length, " (");
+ buf += i;
+ length -= i;
+
+ if (length <= 0 || (i = usb_make_path(dev, buf, length)) < 0)
+ goto finish;
+
+ buf += i;
+ length -= i;
+
+ snprintf(buf, length, ")");
+
+ finish:
+ /* ready for ATM callbacks */
+ wmb();
+ instance->atm_dev->dev_data = instance;
+
+ usb_get_dev(dev);
+
+ return 0;
+
+ fail:
+ for (i = 0; i < num_snd_bufs; i++)
+ kfree(instance->send_buffers[i].base);
+
+ for (i = 0; i < num_snd_urbs; i++)
+ usb_free_urb(instance->senders[i].urb);
+
+ for (i = 0; i < num_rcv_bufs; i++)
+ kfree(instance->receive_buffers[i].base);
+
+ for (i = 0; i < num_rcv_urbs; i++)
+ usb_free_urb(instance->receivers[i].urb);
+
+ return -ENOMEM;
+}
+
+void udsl_instance_disconnect(struct udsl_instance_data *instance)
+{
+ int i;
+
+ dbg("udsl_instance_disconnect entered");
+
+ if (!instance) {
+ dbg("udsl_instance_disconnect: NULL instance!");
+ return;
+ }
+
+ /* receive finalize */
+ tasklet_disable(&instance->receive_tasklet);
+
+ for (i = 0; i < num_rcv_urbs; i++)
+ usb_kill_urb(instance->receivers[i].urb);
+
+ /* no need to take the spinlock */
+ INIT_LIST_HEAD(&instance->filled_receive_buffers);
+ INIT_LIST_HEAD(&instance->spare_receive_buffers);
+
+ tasklet_enable(&instance->receive_tasklet);
+
+ for (i = 0; i < num_rcv_urbs; i++)
+ usb_free_urb(instance->receivers[i].urb);
+
+ for (i = 0; i < num_rcv_bufs; i++)
+ kfree(instance->receive_buffers[i].base);
+
+ /* send finalize */
+ tasklet_disable(&instance->send_tasklet);
+
+ for (i = 0; i < num_snd_urbs; i++)
+ usb_kill_urb(instance->senders[i].urb);
+
+ /* no need to take the spinlock */
+ INIT_LIST_HEAD(&instance->spare_senders);
+ INIT_LIST_HEAD(&instance->spare_send_buffers);
+ instance->current_buffer = NULL;
+
+ tasklet_enable(&instance->send_tasklet);
+
+ for (i = 0; i < num_snd_urbs; i++)
+ usb_free_urb(instance->senders[i].urb);
+
+ for (i = 0; i < num_snd_bufs; i++)
+ kfree(instance->send_buffers[i].base);
+
+ /* ATM finalize */
+ shutdown_atm_dev(instance->atm_dev);
+}
+
+EXPORT_SYMBOL_GPL(udsl_get_instance);
+EXPORT_SYMBOL_GPL(udsl_put_instance);
+EXPORT_SYMBOL_GPL(udsl_instance_setup);
+EXPORT_SYMBOL_GPL(udsl_instance_disconnect);
+
+/***********
+** init **
+***********/
+
+static int __init udsl_usb_init(void)
+{
+ dbg("udsl_usb_init: driver version " DRIVER_VERSION);
+
+ if (sizeof(struct udsl_control) > sizeof(((struct sk_buff *) 0)->cb)) {
+ printk(KERN_ERR __FILE__ ": unusable with this kernel!\n");
+ return -EIO;
+ }
+
+ if ((num_rcv_urbs > UDSL_MAX_RCV_URBS)
+ || (num_snd_urbs > UDSL_MAX_SND_URBS)
+ || (num_rcv_bufs > UDSL_MAX_RCV_BUFS)
+ || (num_snd_bufs > UDSL_MAX_SND_BUFS)
+ || (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE)
+ || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void __exit udsl_usb_exit(void)
+{
+}
+
+module_init(udsl_usb_init);
+module_exit(udsl_usb_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+/************
+** debug **
+************/
+
+#ifdef VERBOSE_DEBUG
+static int udsl_print_packet(const unsigned char *data, int len)
+{
+ unsigned char buffer[256];
+ int i = 0, j = 0;
+
+ for (i = 0; i < len;) {
+ buffer[0] = '\0';
+ sprintf(buffer, "%.3d :", i);
+ for (j = 0; (j < 16) && (i < len); j++, i++) {
+ sprintf(buffer, "%s %2.2x", buffer, data[i]);
+ }
+ dbg("%s", buffer);
+ }
+ return i;
+}
+#endif
diff --git a/drivers/usb/atm/usb_atm.h b/drivers/usb/atm/usb_atm.h
new file mode 100644
index 000000000000..219763cc3242
--- /dev/null
+++ b/drivers/usb/atm/usb_atm.h
@@ -0,0 +1,159 @@
+/******************************************************************************
+ * usb_atm.h - Generic USB xDSL driver core
+ *
+ * Copyright (C) 2001, Alcatel
+ * Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas
+ * Copyright (C) 2004, David Woodhouse
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/kref.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <asm/semaphore.h>
+
+#define UDSL_MAX_RCV_URBS 4
+#define UDSL_MAX_SND_URBS 4
+#define UDSL_MAX_RCV_BUFS 8
+#define UDSL_MAX_SND_BUFS 8
+#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */
+#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */
+#define UDSL_DEFAULT_RCV_URBS 2
+#define UDSL_DEFAULT_SND_URBS 2
+#define UDSL_DEFAULT_RCV_BUFS 4
+#define UDSL_DEFAULT_SND_BUFS 4
+#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */
+#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */
+
+#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
+#define UDSL_NUM_CELLS(x) (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD)
+
+/* receive */
+
+struct udsl_receive_buffer {
+ struct list_head list;
+ unsigned char *base;
+ unsigned int filled_cells;
+};
+
+struct udsl_receiver {
+ struct list_head list;
+ struct udsl_receive_buffer *buffer;
+ struct urb *urb;
+ struct udsl_instance_data *instance;
+};
+
+struct udsl_vcc_data {
+ /* vpi/vci lookup */
+ struct list_head list;
+ short vpi;
+ int vci;
+ struct atm_vcc *vcc;
+
+ /* raw cell reassembly */
+ struct sk_buff *sarb;
+};
+
+/* send */
+
+struct udsl_send_buffer {
+ struct list_head list;
+ unsigned char *base;
+ unsigned char *free_start;
+ unsigned int free_cells;
+};
+
+struct udsl_sender {
+ struct list_head list;
+ struct udsl_send_buffer *buffer;
+ struct urb *urb;
+ struct udsl_instance_data *instance;
+};
+
+struct udsl_control {
+ struct atm_skb_data atm_data;
+ unsigned int num_cells;
+ unsigned int num_entire;
+ unsigned int pdu_padding;
+ unsigned char aal5_trailer[ATM_AAL5_TRAILER];
+};
+
+#define UDSL_SKB(x) ((struct udsl_control *)(x)->cb)
+
+/* main driver data */
+
+enum udsl_status {
+ UDSL_NO_FIRMWARE,
+ UDSL_LOADING_FIRMWARE,
+ UDSL_LOADED_FIRMWARE
+};
+
+struct udsl_instance_data {
+ struct kref refcount;
+ struct semaphore serialize;
+
+ /* USB device part */
+ struct usb_device *usb_dev;
+ char description[64];
+ int data_endpoint;
+ int snd_padding;
+ int rcv_padding;
+ const char *driver_name;
+
+ /* ATM device part */
+ struct atm_dev *atm_dev;
+ struct list_head vcc_list;
+
+ /* firmware */
+ int (*firmware_wait) (struct udsl_instance_data *);
+ enum udsl_status status;
+ wait_queue_head_t firmware_waiters;
+
+ /* receive */
+ struct udsl_receiver receivers[UDSL_MAX_RCV_URBS];
+ struct udsl_receive_buffer receive_buffers[UDSL_MAX_RCV_BUFS];
+
+ spinlock_t receive_lock;
+ struct list_head spare_receivers;
+ struct list_head filled_receive_buffers;
+
+ struct tasklet_struct receive_tasklet;
+ struct list_head spare_receive_buffers;
+
+ /* send */
+ struct udsl_sender senders[UDSL_MAX_SND_URBS];
+ struct udsl_send_buffer send_buffers[UDSL_MAX_SND_BUFS];
+
+ struct sk_buff_head sndqueue;
+
+ spinlock_t send_lock;
+ struct list_head spare_senders;
+ struct list_head spare_send_buffers;
+
+ struct tasklet_struct send_tasklet;
+ struct sk_buff *current_skb; /* being emptied */
+ struct udsl_send_buffer *current_buffer; /* being filled */
+ struct list_head filled_send_buffers;
+};
+
+extern int udsl_instance_setup(struct usb_device *dev,
+ struct udsl_instance_data *instance);
+extern void udsl_instance_disconnect(struct udsl_instance_data *instance);
+extern void udsl_get_instance(struct udsl_instance_data *instance);
+extern void udsl_put_instance(struct udsl_instance_data *instance);
diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig
index 3d49e9d8cb67..0561d0234f23 100644
--- a/drivers/usb/class/Kconfig
+++ b/drivers/usb/class/Kconfig
@@ -9,7 +9,8 @@ config USB_AUDIO
depends on USB && SOUND
help
Say Y here if you want to connect USB audio equipment such as
- speakers to your computer's USB port.
+ speakers to your computer's USB port. You only need this if you use
+ the OSS sound driver; ALSA has its own option for usb audio support.
To compile this driver as a module, choose M here: the
module will be called audio.
diff --git a/drivers/usb/class/audio.c b/drivers/usb/class/audio.c
index 17a6dd74713f..88628d85dff2 100644
--- a/drivers/usb/class/audio.c
+++ b/drivers/usb/class/audio.c
@@ -635,13 +635,13 @@ static void usbin_stop(struct usb_audiodev *as)
spin_unlock_irqrestore(&as->lock, flags);
if (notkilled && signal_pending(current)) {
if (i & FLG_URB0RUNNING)
- usb_unlink_urb(u->durb[0].urb);
+ usb_kill_urb(u->durb[0].urb);
if (i & FLG_URB1RUNNING)
- usb_unlink_urb(u->durb[1].urb);
+ usb_kill_urb(u->durb[1].urb);
if (i & FLG_SYNC0RUNNING)
- usb_unlink_urb(u->surb[0].urb);
+ usb_kill_urb(u->surb[0].urb);
if (i & FLG_SYNC1RUNNING)
- usb_unlink_urb(u->surb[1].urb);
+ usb_kill_urb(u->surb[1].urb);
notkilled = 0;
}
}
@@ -1114,13 +1114,13 @@ static void usbout_stop(struct usb_audiodev *as)
spin_unlock_irqrestore(&as->lock, flags);
if (notkilled && signal_pending(current)) {
if (i & FLG_URB0RUNNING)
- usb_unlink_urb(u->durb[0].urb);
+ usb_kill_urb(u->durb[0].urb);
if (i & FLG_URB1RUNNING)
- usb_unlink_urb(u->durb[1].urb);
+ usb_kill_urb(u->durb[1].urb);
if (i & FLG_SYNC0RUNNING)
- usb_unlink_urb(u->surb[0].urb);
+ usb_kill_urb(u->surb[0].urb);
if (i & FLG_SYNC1RUNNING)
- usb_unlink_urb(u->surb[1].urb);
+ usb_kill_urb(u->surb[1].urb);
notkilled = 0;
}
}
@@ -1949,15 +1949,12 @@ static inline int prog_dmabuf_out(struct usb_audiodev *as)
static int usb_audio_open_mixdev(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
- struct list_head *devs, *mdevs;
struct usb_mixerdev *ms;
struct usb_audio_state *s;
down(&open_sem);
- list_for_each(devs, &audiodevs) {
- s = list_entry(devs, struct usb_audio_state, audiodev);
- list_for_each(mdevs, &s->mixerlist) {
- ms = list_entry(mdevs, struct usb_mixerdev, list);
+ list_for_each_entry(s, &audiodevs, audiodev) {
+ list_for_each_entry(ms, &s->mixerlist, list) {
if (ms->dev_mixer == minor)
goto mixer_found;
}
@@ -2634,16 +2631,13 @@ static int usb_audio_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
DECLARE_WAITQUEUE(wait, current);
- struct list_head *devs, *adevs;
struct usb_audiodev *as;
struct usb_audio_state *s;
for (;;) {
down(&open_sem);
- list_for_each(devs, &audiodevs) {
- s = list_entry(devs, struct usb_audio_state, audiodev);
- list_for_each(adevs, &s->audiolist) {
- as = list_entry(adevs, struct usb_audiodev, list);
+ list_for_each_entry(s, &audiodevs, audiodev) {
+ list_for_each_entry(as, &s->audiolist, list) {
if (!((as->dev_audio ^ minor) & ~0xf))
goto device_found;
}
@@ -3809,7 +3803,6 @@ static int usb_audio_probe(struct usb_interface *intf,
static void usb_audio_disconnect(struct usb_interface *intf)
{
struct usb_audio_state *s = usb_get_intfdata (intf);
- struct list_head *list;
struct usb_audiodev *as;
struct usb_mixerdev *ms;
@@ -3831,8 +3824,7 @@ static void usb_audio_disconnect(struct usb_interface *intf)
usb_set_intfdata (intf, NULL);
/* deregister all audio and mixer devices, so no new processes can open this device */
- list_for_each(list, &s->audiolist) {
- as = list_entry(list, struct usb_audiodev, list);
+ list_for_each_entry(as, &s->audiolist, list) {
usbin_disc(as);
usbout_disc(as);
wake_up(&as->usbin.dma.wait);
@@ -3843,8 +3835,7 @@ static void usb_audio_disconnect(struct usb_interface *intf)
}
as->dev_audio = -1;
}
- list_for_each(list, &s->mixerlist) {
- ms = list_entry(list, struct usb_mixerdev, list);
+ list_for_each_entry(ms, &s->mixerlist, list) {
if (ms->dev_mixer >= 0) {
unregister_sound_mixer(ms->dev_mixer);
printk(KERN_INFO "usbaudio: unregister mixer 14,%d\n", ms->dev_mixer);
diff --git a/drivers/usb/class/bluetty.c b/drivers/usb/class/bluetty.c
index 4d8e01895ce9..6baf68badc06 100644
--- a/drivers/usb/class/bluetty.c
+++ b/drivers/usb/class/bluetty.c
@@ -426,8 +426,8 @@ static void bluetooth_close (struct tty_struct *tty, struct file * filp)
bluetooth->open_count = 0;
/* shutdown any in-flight urbs that we know about */
- usb_unlink_urb (bluetooth->read_urb);
- usb_unlink_urb (bluetooth->interrupt_in_urb);
+ usb_kill_urb (bluetooth->read_urb);
+ usb_kill_urb (bluetooth->interrupt_in_urb);
}
up(&bluetooth->lock);
}
@@ -705,7 +705,7 @@ void btusb_disable_bulk_read(struct tty_struct *tty){
}
if ((bluetooth->read_urb) && (bluetooth->read_urb->actual_length))
- usb_unlink_urb(bluetooth->read_urb);
+ usb_kill_urb(bluetooth->read_urb);
}
#endif
@@ -1179,14 +1179,14 @@ static void usb_bluetooth_disconnect(struct usb_interface *intf)
bluetooth->open_count = 0;
if (bluetooth->read_urb) {
- usb_unlink_urb (bluetooth->read_urb);
+ usb_kill_urb (bluetooth->read_urb);
usb_free_urb (bluetooth->read_urb);
}
if (bluetooth->bulk_in_buffer)
kfree (bluetooth->bulk_in_buffer);
if (bluetooth->interrupt_in_urb) {
- usb_unlink_urb (bluetooth->interrupt_in_urb);
+ usb_kill_urb (bluetooth->interrupt_in_urb);
usb_free_urb (bluetooth->interrupt_in_urb);
}
if (bluetooth->interrupt_in_buffer)
@@ -1196,7 +1196,7 @@ static void usb_bluetooth_disconnect(struct usb_interface *intf)
for (i = 0; i < NUM_CONTROL_URBS; ++i) {
if (bluetooth->control_urb_pool[i]) {
- usb_unlink_urb (bluetooth->control_urb_pool[i]);
+ usb_kill_urb (bluetooth->control_urb_pool[i]);
if (bluetooth->control_urb_pool[i]->transfer_buffer)
kfree (bluetooth->control_urb_pool[i]->transfer_buffer);
usb_free_urb (bluetooth->control_urb_pool[i]);
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index a929a521242b..0d0d49923bee 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -301,9 +301,9 @@ done:
return 0;
full_bailout:
- usb_unlink_urb(acm->readurb);
+ usb_kill_urb(acm->readurb);
bail_out_and_unlink:
- usb_unlink_urb(acm->ctrlurb);
+ usb_kill_urb(acm->ctrlurb);
bail_out:
up(&open_sem);
return -EIO;
@@ -320,9 +320,9 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
if (!--acm->used) {
if (acm->dev) {
acm_set_control(acm, acm->ctrlout = 0);
- usb_unlink_urb(acm->ctrlurb);
- usb_unlink_urb(acm->writeurb);
- usb_unlink_urb(acm->readurb);
+ usb_kill_urb(acm->ctrlurb);
+ usb_kill_urb(acm->writeurb);
+ usb_kill_urb(acm->readurb);
} else {
tty_unregister_device(acm_tty_driver, acm->minor);
acm_table[acm->minor] = NULL;
@@ -542,6 +542,17 @@ static int acm_probe (struct usb_interface *intf,
return -EINVAL;
}
+ if (!buflen) {
+ if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
+ dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint");
+ buflen = intf->cur_altsetting->endpoint->extralen;
+ buffer = intf->cur_altsetting->endpoint->extra;
+ } else {
+ err("Zero length descriptor references");
+ return -EINVAL;
+ }
+ }
+
while (buflen > 0) {
if (buffer [1] != USB_DT_CS_INTERFACE) {
err("skipping garbage");
@@ -558,6 +569,8 @@ static int acm_probe (struct usb_interface *intf,
break;
case CDC_COUNTRY_TYPE: /* maybe somehow export */
break; /* for now we ignore it */
+ case CDC_HEADER_TYPE: /* maybe check version */
+ break; /* for now we ignore it */
case CDC_AC_MANAGEMENT_TYPE:
ac_management_function = buffer[3];
break;
@@ -569,7 +582,7 @@ static int acm_probe (struct usb_interface *intf,
break;
default:
- err("Ignoring extra header");
+ err("Ignoring extra header, type %d, length %d", buffer[2], buffer[0]);
break;
}
next_desc:
@@ -637,7 +650,7 @@ next_desc:
dbg("interfaces are valid");
for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
- if (acm_table[minor]) {
+ if (minor == ACM_TTY_MINORS) {
err("no more free acm devices");
return -ENODEV;
}
@@ -762,9 +775,9 @@ static void acm_disconnect(struct usb_interface *intf)
acm->dev = NULL;
usb_set_intfdata (intf, NULL);
- usb_unlink_urb(acm->ctrlurb);
- usb_unlink_urb(acm->readurb);
- usb_unlink_urb(acm->writeurb);
+ usb_kill_urb(acm->ctrlurb);
+ usb_kill_urb(acm->readurb);
+ usb_kill_urb(acm->writeurb);
flush_scheduled_work(); /* wait for acm_softint */
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index cc26d9517398..da945b367a85 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -117,6 +117,7 @@ struct union_desc {
} __attribute__ ((packed));
/* class specific descriptor types */
+#define CDC_HEADER_TYPE 0x00
#define CDC_CALL_MANAGEMENT_TYPE 0x01
#define CDC_AC_MANAGEMENT_TYPE 0x02
#define CDC_UNION_TYPE 0x06
diff --git a/drivers/usb/class/usb-midi.c b/drivers/usb/class/usb-midi.c
index a9dc047a04a7..1dc952e9bc81 100644
--- a/drivers/usb/class/usb-midi.c
+++ b/drivers/usb/class/usb-midi.c
@@ -805,7 +805,6 @@ static int usb_midi_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
DECLARE_WAITQUEUE(wait, current);
- struct list_head *devs, *mdevs;
struct usb_midi_state *s;
struct usb_mididev *m;
unsigned long flags;
@@ -817,10 +816,8 @@ static int usb_midi_open(struct inode *inode, struct file *file)
for(;;) {
down(&open_sem);
- list_for_each(devs, &mididevs) {
- s = list_entry(devs, struct usb_midi_state, mididev);
- list_for_each(mdevs, &s->midiDevList) {
- m = list_entry(mdevs, struct usb_mididev, list);
+ list_for_each_entry(s, &mididevs, mididev) {
+ list_for_each_entry(m, &s->midiDevList, list) {
if ( !((m->dev_midi ^ minor) & ~0xf) )
goto device_found;
}
@@ -939,7 +936,7 @@ static int usb_midi_release(struct inode *inode, struct file *file)
if ( m->open_mode & FMODE_WRITE ) {
m->open_mode &= ~FMODE_WRITE;
- usb_unlink_urb( m->mout.ep->urb );
+ usb_kill_urb( m->mout.ep->urb );
}
if ( m->open_mode & FMODE_READ ) {
@@ -951,7 +948,7 @@ static int usb_midi_release(struct inode *inode, struct file *file)
if ( m->min.ep->readers == 0 &&
m->min.ep->urbSubmitted ) {
m->min.ep->urbSubmitted = 0;
- usb_unlink_urb(m->min.ep->urb);
+ usb_kill_urb(m->min.ep->urb);
}
spin_unlock_irqrestore( &m->min.ep->lock, flagsep );
}
@@ -1042,7 +1039,7 @@ static struct midi_in_endpoint *alloc_midi_in_endpoint( struct usb_device *d, in
static int remove_midi_in_endpoint( struct midi_in_endpoint *min )
{
- usb_unlink_urb( min->urb );
+ usb_kill_urb( min->urb );
usb_free_urb( min->urb );
kfree( min->recvBuf );
kfree( min );
@@ -1102,7 +1099,7 @@ static struct midi_out_endpoint *alloc_midi_out_endpoint( struct usb_device *d,
static int remove_midi_out_endpoint( struct midi_out_endpoint *mout )
{
- usb_unlink_urb( mout->urb );
+ usb_kill_urb( mout->urb );
usb_free_urb( mout->urb );
kfree( mout->buf );
kfree( mout );
@@ -1994,7 +1991,6 @@ static int usb_midi_probe(struct usb_interface *intf,
static void usb_midi_disconnect(struct usb_interface *intf)
{
struct usb_midi_state *s = usb_get_intfdata (intf);
- struct list_head *list;
struct usb_mididev *m;
if ( !s )
@@ -2012,8 +2008,7 @@ static void usb_midi_disconnect(struct usb_interface *intf)
s->usbdev = NULL;
usb_set_intfdata (intf, NULL);
- list_for_each(list, &s->midiDevList) {
- m = list_entry(list, struct usb_mididev, list);
+ list_for_each_entry(m, &s->midiDevList, list) {
wake_up(&(m->min.ep->wait));
wake_up(&(m->mout.ep->wait));
if ( m->dev_midi >= 0 ) {
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index da91bbd6735c..da145c91c1fa 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -406,9 +406,9 @@ static void usblp_cleanup (struct usblp *usblp)
static void usblp_unlink_urbs(struct usblp *usblp)
{
- usb_unlink_urb(usblp->writeurb);
+ usb_kill_urb(usblp->writeurb);
if (usblp->bidir)
- usb_unlink_urb(usblp->readurb);
+ usb_kill_urb(usblp->readurb);
}
static int usblp_release(struct inode *inode, struct file *file)
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index 8e535789fce7..50009ed51e8d 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -149,7 +149,7 @@ static const struct class_info clas_info[] =
/*****************************************************************/
-void usbdevfs_conn_disc_event(void)
+void usbfs_conn_disc_event(void)
{
conndiscevcnt++;
wake_up(&deviceconndiscwq);
@@ -451,7 +451,7 @@ static char *usb_dump_string(char *start, char *end, const struct usb_device *de
* nbytes - the maximum number of bytes to write
* skip_bytes - the number of bytes to skip before writing anything
* file_offset - the offset into the devices file on completion
- * The caller must own the usbdev->serialize semaphore.
+ * The caller must own the device lock.
*/
static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *skip_bytes, loff_t *file_offset,
struct usb_device *usbdev, struct usb_bus *bus, int level, int index, int count)
@@ -569,7 +569,6 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *ski
static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
- struct list_head *buslist;
struct usb_bus *bus;
ssize_t ret, total_written = 0;
loff_t skip_bytes = *ppos;
@@ -581,18 +580,15 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbyte
if (!access_ok(VERIFY_WRITE, buf, nbytes))
return -EFAULT;
- /* enumerate busses */
down (&usb_bus_list_lock);
- list_for_each(buslist, &usb_bus_list) {
- /* print devices for this bus */
- bus = list_entry(buslist, struct usb_bus, bus_list);
-
+ /* print devices for all busses */
+ list_for_each_entry(bus, &usb_bus_list, bus_list) {
/* recurse through all children of the root hub */
if (!bus->root_hub)
continue;
- down(&bus->root_hub->serialize);
+ usb_lock_device(bus->root_hub);
ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0);
- up(&bus->root_hub->serialize);
+ usb_unlock_device(bus->root_hub);
if (ret < 0) {
up(&usb_bus_list_lock);
return ret;
@@ -682,7 +678,7 @@ static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig)
return ret;
}
-struct file_operations usbdevfs_devices_fops = {
+struct file_operations usbfs_devices_fops = {
.llseek = usb_device_lseek,
.read = usb_device_read,
.poll = usb_device_poll,
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 776c1bf0df9b..0e0ea0806606 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -21,7 +21,7 @@
*
* $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $
*
- * This file implements the usbdevfs/x/y files, where
+ * This file implements the usbfs/x/y files, where
* x is the bus number and y the device number.
*
* It allows user space programs/"drivers" to communicate directly
@@ -113,7 +113,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l
int i;
pos = *ppos;
- down(&dev->serialize);
+ usb_lock_device(dev);
if (!connected(dev)) {
ret = -ENODEV;
goto err;
@@ -175,7 +175,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l
}
err:
- up(&dev->serialize);
+ usb_unlock_device(dev);
return ret;
}
@@ -286,9 +286,10 @@ static void destroy_async (struct dev_state *ps, struct list_head *list)
while (!list_empty(list)) {
as = list_entry(list->next, struct async, asynclist);
list_del_init(&as->asynclist);
+
+ /* drop the spinlock so the completion handler can run */
spin_unlock_irqrestore(&ps->lock, flags);
- /* usb_unlink_urb calls the completion handler with status == -ENOENT */
- usb_unlink_urb(as->urb);
+ usb_kill_urb(as->urb);
spin_lock_irqsave(&ps->lock, flags);
}
spin_unlock_irqrestore(&ps->lock, flags);
@@ -353,7 +354,7 @@ static void driver_disconnect(struct usb_interface *intf)
destroy_async_on_interface(ps, ifnum);
}
-struct usb_driver usbdevfs_driver = {
+struct usb_driver usbfs_driver = {
.owner = THIS_MODULE,
.name = "usbfs",
.probe = driver_probe,
@@ -378,7 +379,7 @@ static int claimintf(struct dev_state *ps, unsigned int ifnum)
if (!intf)
err = -ENOENT;
else
- err = usb_driver_claim_interface(&usbdevfs_driver, intf, ps);
+ err = usb_driver_claim_interface(&usbfs_driver, intf, ps);
up_write(&usb_bus_type.subsys.rwsem);
if (err == 0)
set_bit(ifnum, &ps->ifclaimed);
@@ -401,7 +402,7 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum)
if (!intf)
err = -ENOENT;
else if (test_and_clear_bit(ifnum, &ps->ifclaimed)) {
- usb_driver_release_interface(&usbdevfs_driver, intf);
+ usb_driver_release_interface(&usbfs_driver, intf);
err = 0;
}
up_write(&usb_bus_type.subsys.rwsem);
@@ -516,7 +517,7 @@ static int usbdev_release(struct inode *inode, struct file *file)
struct usb_device *dev = ps->dev;
unsigned int ifnum;
- down(&dev->serialize);
+ usb_lock_device(dev);
list_del_init(&ps->list);
if (connected(dev)) {
@@ -525,7 +526,7 @@ static int usbdev_release(struct inode *inode, struct file *file)
releaseintf(ps, ifnum);
destroy_all_async(ps);
}
- up(&dev->serialize);
+ usb_unlock_device(dev);
usb_put_dev(dev);
ps->dev = NULL;
kfree(ps);
@@ -557,10 +558,10 @@ static int proc_control(struct dev_state *ps, void __user *arg)
snoop(&dev->dev, "control read: bRequest=%02x bRrequestType=%02x wValue=%04x wIndex=%04x\n",
ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex);
- up(&dev->serialize);
+ usb_unlock_device(dev);
i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType,
ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo);
- down(&dev->serialize);
+ usb_lock_device(dev);
if ((i > 0) && ctrl.wLength) {
if (usbfs_snoop) {
dev_info(&dev->dev, "control read: data ");
@@ -588,10 +589,10 @@ static int proc_control(struct dev_state *ps, void __user *arg)
printk ("%02x ", (unsigned char)(tbuf)[j]);
printk("\n");
}
- up(&dev->serialize);
+ usb_unlock_device(dev);
i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType,
ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo);
- down(&dev->serialize);
+ usb_lock_device(dev);
}
free_page((unsigned long)tbuf);
if (i<0) {
@@ -635,9 +636,9 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
kfree(tbuf);
return -EINVAL;
}
- up(&dev->serialize);
+ usb_unlock_device(dev);
i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
- down(&dev->serialize);
+ usb_lock_device(dev);
if (!i && len2) {
if (copy_to_user(bulk.data, tbuf, len2)) {
kfree(tbuf);
@@ -651,9 +652,9 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
return -EFAULT;
}
}
- up(&dev->serialize);
+ usb_unlock_device(dev);
i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
- down(&dev->serialize);
+ usb_lock_device(dev);
}
kfree(tbuf);
if (i < 0) {
@@ -734,7 +735,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg)
static int proc_resetdevice(struct dev_state *ps)
{
- return __usb_reset_device(ps->dev);
+ return usb_reset_device(ps->dev);
}
@@ -976,7 +977,7 @@ static int proc_unlinkurb(struct dev_state *ps, void __user *arg)
as = async_getpending(ps, arg);
if (!as)
return -EINVAL;
- usb_unlink_urb(as->urb);
+ usb_kill_urb(as->urb);
return 0;
}
@@ -1024,9 +1025,9 @@ static int proc_reapurb(struct dev_state *ps, void __user *arg)
break;
if (signal_pending(current))
break;
- up(&dev->serialize);
+ usb_unlock_device(dev);
schedule();
- down(&dev->serialize);
+ usb_lock_device(dev);
}
remove_wait_queue(&ps->wait, &wait);
set_current_state(TASK_RUNNING);
@@ -1149,7 +1150,11 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg)
/* let kernel drivers try to (re)bind to the interface */
case USBDEVFS_CONNECT:
+ usb_unlock_device(ps->dev);
+ usb_lock_all_devices();
bus_rescan_devices(intf->dev.bus);
+ usb_unlock_all_devices();
+ usb_lock_device(ps->dev);
break;
/* talk directly to the interface's driver */
@@ -1192,9 +1197,9 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
if (!(file->f_mode & FMODE_WRITE))
return -EPERM;
- down(&dev->serialize);
+ usb_lock_device(dev);
if (!connected(dev)) {
- up(&dev->serialize);
+ usb_unlock_device(dev);
return -ENODEV;
}
@@ -1294,7 +1299,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
ret = proc_ioctl(ps, p);
break;
}
- up(&dev->serialize);
+ usb_unlock_device(dev);
if (ret >= 0)
inode->i_atime = CURRENT_TIME;
return ret;
@@ -1314,7 +1319,7 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai
return mask;
}
-struct file_operations usbdevfs_device_file_operations = {
+struct file_operations usbfs_device_file_operations = {
.llseek = usbdev_lseek,
.read = usbdev_read,
.poll = usbdev_poll,
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 5fdaae079cf6..837bb267f194 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -188,9 +188,9 @@ clean_3:
}
hcd->irq = dev->irq;
- dev_info (hcd->self.controller, "irq %s, %s %p\n", bufp,
+ dev_info (hcd->self.controller, "irq %s, %s 0x%lx\n", bufp,
(driver->flags & HCD_MEMORY) ? "pci mem" : "io base",
- base);
+ resource);
usb_bus_init (&hcd->self);
hcd->self.op = &usb_hcd_operations;
@@ -260,6 +260,8 @@ void usb_hcd_pci_remove (struct pci_dev *dev)
}
usb_deregister_bus (&hcd->self);
+
+ pci_disable_device(dev);
}
EXPORT_SYMBOL (usb_hcd_pci_remove);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index d970e2c0c066..8ab1163e59b9 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -798,9 +798,9 @@ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev
return (retval < 0) ? retval : -EMSGSIZE;
}
- down (&usb_dev->serialize);
+ usb_lock_device (usb_dev);
retval = usb_new_device (usb_dev);
- up (&usb_dev->serialize);
+ usb_unlock_device (usb_dev);
if (retval) {
usb_dev->bus->root_hub = NULL;
dev_err (parent_dev, "can't register root hub for %s, %d\n",
@@ -1264,7 +1264,7 @@ static int hcd_unlink_urb (struct urb *urb, int status)
* never get completion IRQs ... maybe even the ones we need to
* finish unlinking the initial failed usb_set_address().
*/
- if (!hcd->saw_irq) {
+ if (!hcd->saw_irq && hcd->rh_timer.data != (unsigned long) urb) {
dev_warn (hcd->self.controller, "Unlink after no-IRQ? "
"Different ACPI or APIC settings may help."
"\n");
@@ -1573,13 +1573,12 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r)
struct usb_hcd *hcd = __hcd;
int start = hcd->state;
- if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */
+ if (start == USB_STATE_HALT)
return IRQ_NONE;
-
- hcd->saw_irq = 1;
if (hcd->driver->irq (hcd, r) == IRQ_NONE)
return IRQ_NONE;
+ hcd->saw_irq = 1;
if (hcd->state != start && hcd->state == USB_STATE_HALT)
usb_hc_died (hcd);
return IRQ_HANDLED;
@@ -1588,22 +1587,6 @@ EXPORT_SYMBOL (usb_hcd_irq);
/*-------------------------------------------------------------------------*/
-static void hcd_panic (void *_hcd)
-{
- struct usb_hcd *hcd = _hcd;
- struct usb_device *hub = hcd->self.root_hub;
- unsigned i;
-
- /* hc's root hub is removed later removed in hcd->stop() */
- down (&hub->serialize);
- usb_set_device_state(hub, USB_STATE_NOTATTACHED);
- for (i = 0; i < hub->maxchild; i++) {
- if (hub->children [i])
- usb_disconnect (&hub->children [i]);
- }
- up (&hub->serialize);
-}
-
/**
* usb_hc_died - report abnormal shutdown of a host controller (bus glue)
* @hcd: pointer to the HCD representing the controller
@@ -1616,9 +1599,9 @@ void usb_hc_died (struct usb_hcd *hcd)
{
dev_err (hcd->self.controller, "HC died; cleaning up\n");
- /* clean up old urbs and devices; needs a task context */
- INIT_WORK (&hcd->work, hcd_panic, hcd);
- (void) schedule_work (&hcd->work);
+ /* make khubd clean up old urbs and devices */
+ usb_set_device_state(hcd->self.root_hub, USB_STATE_NOTATTACHED);
+ mod_timer(&hcd->rh_timer, jiffies);
}
EXPORT_SYMBOL (usb_hc_died);
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 340977b72925..eb8ae109096e 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -67,7 +67,6 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */
struct timer_list rh_timer; /* drives root hub */
struct list_head dev_list; /* devices on this bus */
- struct work_struct work;
/*
* hardware info/state
@@ -363,6 +362,9 @@ static inline int hcd_register_root (struct usb_device *usb_dev,
return usb_register_root_hub (usb_dev, hcd->self.controller);
}
+extern void usb_set_device_state(struct usb_device *udev,
+ enum usb_device_state new_state);
+
/*-------------------------------------------------------------------------*/
/* exported only within usbcore */
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 7f72ee96c7fe..10baa8f52566 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -36,15 +36,18 @@
#include "hcd.h"
#include "hub.h"
-/* Protect struct usb_device state and children members */
+/* Protect struct usb_device->state and ->children members
+ * Note: Both are also protected by ->serialize, except that ->state can
+ * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
static spinlock_t device_state_lock = SPIN_LOCK_UNLOCKED;
-/* Wakes up khubd */
+/* khubd's worklist and its lock */
static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED;
-
static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */
+/* Wakes up khubd */
static DECLARE_WAIT_QUEUE_HEAD(khubd_wait);
+
static pid_t khubd_pid = 0; /* PID of khubd */
static DECLARE_COMPLETION(khubd_exited);
@@ -226,6 +229,19 @@ static int get_port_status(struct usb_device *hdev, int port,
data, sizeof(*data), HZ * USB_CTRL_GET_TIMEOUT);
}
+static void kick_khubd(struct usb_hub *hub)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hub_event_lock, flags);
+ if (list_empty(&hub->event_list)) {
+ list_add_tail(&hub->event_list, &hub_event_list);
+ wake_up(&khubd_wait);
+ }
+ spin_unlock_irqrestore(&hub_event_lock, flags);
+}
+
+
/* completion function, fires on port status changes and various faults */
static void hub_irq(struct urb *urb, struct pt_regs *regs)
{
@@ -261,12 +277,7 @@ static void hub_irq(struct urb *urb, struct pt_regs *regs)
hub->nerrors = 0;
/* Something happened, let khubd figure it out */
- spin_lock(&hub_event_lock);
- if (list_empty(&hub->event_list)) {
- list_add_tail(&hub->event_list, &hub_event_list);
- wake_up(&khubd_wait);
- }
- spin_unlock(&hub_event_lock);
+ kick_khubd(hub);
resubmit:
if (hub->quiescing)
@@ -384,6 +395,33 @@ static void hub_power_on(struct usb_hub *hub)
msleep(hub->descriptor->bPwrOn2PwrGood * 2);
}
+static void hub_quiesce(struct usb_hub *hub)
+{
+ /* stop khubd and related activity */
+ hub->quiescing = 1;
+ usb_kill_urb(hub->urb);
+ if (hub->has_indicators)
+ cancel_delayed_work(&hub->leds);
+ if (hub->has_indicators || hub->tt.hub)
+ flush_scheduled_work();
+}
+
+static void hub_activate(struct usb_hub *hub)
+{
+ int status;
+
+ hub->quiescing = 0;
+ status = usb_submit_urb(hub->urb, GFP_NOIO);
+ if (status < 0)
+ dev_err(&hub->intf->dev, "activate --> %d\n", status);
+ if (hub->has_indicators && blinkenlights)
+ schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
+
+ /* scan all ports ASAP */
+ hub->event_bits[0] = ~0;
+ kick_khubd(hub);
+}
+
static int hub_hub_status(struct usb_hub *hub,
u16 *status, u16 *change)
{
@@ -579,7 +617,7 @@ static int hub_configure(struct usb_hub *hub,
dev_dbg(hub_dev, "%sover-current condition exists\n",
(hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");
- /* Start the interrupt endpoint */
+ /* set up the interrupt endpoint */
pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
@@ -597,24 +635,13 @@ static int hub_configure(struct usb_hub *hub,
hub, endpoint->bInterval);
hub->urb->transfer_dma = hub->buffer_dma;
hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- ret = usb_submit_urb(hub->urb, GFP_KERNEL);
- if (ret) {
- message = "couldn't submit status urb";
- goto fail;
- }
-
- /* Wake up khubd */
- wake_up(&khubd_wait);
- /* maybe start cycling the hub leds */
- if (hub->has_indicators && blinkenlights) {
- set_port_led(hdev, 1, HUB_LED_GREEN);
+ /* maybe cycle the hub leds */
+ if (hub->has_indicators && blinkenlights)
hub->indicator [0] = INDICATOR_CYCLE;
- schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
- }
hub_power_on(hub);
-
+ hub_activate(hub);
return 0;
fail:
@@ -626,33 +653,6 @@ fail:
static unsigned highspeed_hubs;
-static void hub_quiesce(struct usb_hub *hub)
-{
- /* stop khubd and related activity */
- hub->quiescing = 1;
- usb_kill_urb(hub->urb);
- if (hub->has_indicators)
- cancel_delayed_work(&hub->leds);
- if (hub->has_indicators || hub->tt.hub)
- flush_scheduled_work();
-}
-
-#ifdef CONFIG_USB_SUSPEND
-
-static void hub_reactivate(struct usb_hub *hub)
-{
- int status;
-
- hub->quiescing = 0;
- status = usb_submit_urb(hub->urb, GFP_NOIO);
- if (status < 0)
- dev_err(&hub->intf->dev, "reactivate --> %d\n", status);
- if (hub->has_indicators && blinkenlights)
- schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
-}
-
-#endif
-
static void hub_disconnect(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata (intf);
@@ -794,68 +794,29 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
}
}
-/* caller has locked the hub and must own the device lock */
-static int hub_reset(struct usb_hub *hub)
+/* caller has locked the hub device */
+static void hub_pre_reset(struct usb_device *hdev)
{
- struct usb_device *hdev = hub->hdev;
+ struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]);
int i;
- /* Disconnect any attached devices */
- for (i = 0; i < hub->descriptor->bNbrPorts; i++) {
+ for (i = 0; i < hdev->maxchild; ++i) {
if (hdev->children[i])
usb_disconnect(&hdev->children[i]);
}
-
- /* Attempt to reset the hub */
- if (hub->urb)
- usb_kill_urb(hub->urb);
- else
- return -1;
-
- if (__usb_reset_device(hdev))
- return -1;
-
- hub->urb->dev = hdev;
- if (usb_submit_urb(hub->urb, GFP_KERNEL))
- return -1;
-
- hub_power_on(hub);
-
- return 0;
+ hub_quiesce(hub);
}
-/* caller has locked the hub */
-/* FIXME! This routine should be subsumed into hub_reset */
-static void hub_start_disconnect(struct usb_device *hdev)
+/* caller has locked the hub device */
+static void hub_post_reset(struct usb_device *hdev)
{
- struct usb_device *parent = hdev->parent;
- int i;
-
- /* Find the device pointer to disconnect */
- if (parent) {
- for (i = 0; i < parent->maxchild; i++) {
- if (parent->children[i] == hdev) {
- usb_disconnect(&parent->children[i]);
- return;
- }
- }
- }
+ struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]);
- dev_err(&hdev->dev, "cannot disconnect hub!\n");
+ hub_activate(hub);
+ hub_power_on(hub);
}
-static void recursively_mark_NOTATTACHED(struct usb_device *udev)
-{
- int i;
-
- for (i = 0; i < udev->maxchild; ++i) {
- if (udev->children[i])
- recursively_mark_NOTATTACHED(udev->children[i]);
- }
- udev->state = USB_STATE_NOTATTACHED;
-}
-
/* grab device/port lock, returning index of that port (zero based).
* protects the upstream link used by this device from concurrent
* tree operations like suspend, resume, reset, and disconnect, which
@@ -872,21 +833,16 @@ static int locktree(struct usb_device *udev)
/* root hub is always the first lock in the series */
hdev = udev->parent;
if (!hdev) {
- down(&udev->serialize);
+ usb_lock_device(udev);
return 0;
}
/* on the path from root to us, lock everything from
* top down, dropping parent locks when not needed
- *
- * NOTE: if disconnect were to ignore the locking, we'd need
- * to get extra refcounts to everything since hdev->children
- * and udev->parent could be invalidated while we work...
*/
t = locktree(hdev);
if (t < 0)
return t;
- spin_lock_irq(&device_state_lock);
for (t = 0; t < hdev->maxchild; t++) {
if (hdev->children[t] == udev) {
/* everything is fail-fast once disconnect
@@ -898,33 +854,45 @@ static int locktree(struct usb_device *udev)
/* when everyone grabs locks top->bottom,
* non-overlapping work may be concurrent
*/
- spin_unlock_irq(&device_state_lock);
down(&udev->serialize);
up(&hdev->serialize);
return t;
}
}
- spin_unlock_irq(&device_state_lock);
- up(&hdev->serialize);
+ usb_unlock_device(hdev);
return -ENODEV;
}
+static void recursively_mark_NOTATTACHED(struct usb_device *udev)
+{
+ int i;
+
+ for (i = 0; i < udev->maxchild; ++i) {
+ if (udev->children[i])
+ recursively_mark_NOTATTACHED(udev->children[i]);
+ }
+ udev->state = USB_STATE_NOTATTACHED;
+}
+
/**
- * usb_set_device_state - change a device's current state (usbcore-internal)
+ * usb_set_device_state - change a device's current state (usbcore, hcds)
* @udev: pointer to device whose state should be changed
* @new_state: new state value to be stored
*
- * udev->state is _not_ protected by the device lock. This
+ * udev->state is _not_ fully protected by the device lock. Although
+ * most transitions are made only while holding the lock, the state can
+ * can change to USB_STATE_NOTATTACHED at almost any time. This
* is so that devices can be marked as disconnected as soon as possible,
- * without having to wait for the semaphore to be released. Instead,
- * changes to the state must be protected by the device_state_lock spinlock.
+ * without having to wait for any semaphores to be released. As a result,
+ * all changes to any device's state must be protected by the
+ * device_state_lock spinlock.
*
* Once a device has been added to the device tree, all changes to its state
* should be made using this routine. The state should _not_ be set directly.
*
* If udev->state is already USB_STATE_NOTATTACHED then no change is made.
* Otherwise udev->state is set to new_state, and if new_state is
- * USB_STATE_NOTATTACHED then all of udev's descendant's states are also set
+ * USB_STATE_NOTATTACHED then all of udev's descendants' states are also set
* to USB_STATE_NOTATTACHED.
*/
void usb_set_device_state(struct usb_device *udev,
@@ -941,6 +909,7 @@ void usb_set_device_state(struct usb_device *udev,
recursively_mark_NOTATTACHED(udev);
spin_unlock_irqrestore(&device_state_lock, flags);
}
+EXPORT_SYMBOL(usb_set_device_state);
static void choose_address(struct usb_device *udev)
@@ -974,11 +943,12 @@ static void release_address(struct usb_device *udev)
/**
* usb_disconnect - disconnect a device (usbcore-internal)
- * @pdev: pointer to device being disconnected, into a locked hub
+ * @pdev: pointer to device being disconnected
* Context: !in_interrupt ()
*
- * Something got disconnected. Get rid of it, and all of its children.
- * If *pdev is a normal device then the parent hub should be locked.
+ * Something got disconnected. Get rid of it and all of its children.
+ *
+ * If *pdev is a normal device then the parent hub must already be locked.
* If *pdev is a root hub then this routine will acquire the
* usb_bus_list_lock on behalf of the caller.
*
@@ -1004,9 +974,11 @@ void usb_disconnect(struct usb_device **pdev)
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
/* lock the bus list on behalf of HCDs unregistering their root hubs */
- if (!udev->parent)
+ if (!udev->parent) {
down(&usb_bus_list_lock);
- down(&udev->serialize);
+ usb_lock_device(udev);
+ } else
+ down(&udev->serialize);
dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum);
@@ -1031,14 +1003,16 @@ void usb_disconnect(struct usb_device **pdev)
usbfs_remove_device(udev);
usb_remove_sysfs_dev_files(udev);
- /* Avoid races with recursively_mark_NOTATTACHED() and locktree() */
+ /* Avoid races with recursively_mark_NOTATTACHED() */
spin_lock_irq(&device_state_lock);
*pdev = NULL;
spin_unlock_irq(&device_state_lock);
- up(&udev->serialize);
- if (!udev->parent)
+ if (!udev->parent) {
+ usb_unlock_device(udev);
up(&usb_bus_list_lock);
+ } else
+ up(&udev->serialize);
device_unregister(&udev->dev);
}
@@ -1061,11 +1035,19 @@ static int choose_configuration(struct usb_device *udev)
->altsetting->desc;
if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC)
continue;
- /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS */
+ /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS.
+ * MSFT needs this to be the first config; never use
+ * it as the default unless Linux has host-side RNDIS.
+ * A second config would ideally be CDC-Ethernet, but
+ * may instead be the "vendor specific" CDC subset
+ * long used by ARM Linux for sa1100 or pxa255.
+ */
if (desc->bInterfaceClass == USB_CLASS_COMM
&& desc->bInterfaceSubClass == 2
- && desc->bInterfaceProtocol == 0xff)
+ && desc->bInterfaceProtocol == 0xff) {
+ c = udev->config[1].desc.bConfigurationValue;
continue;
+ }
c = udev->config[i].desc.bConfigurationValue;
break;
}
@@ -1384,7 +1366,6 @@ static int hub_port_disable(struct usb_device *hdev, int port)
int ret;
if (hdev->children[port]) {
- /* FIXME need disconnect() for NOTATTACHED device */
usb_set_device_state(hdev->children[port],
USB_STATE_NOTATTACHED);
}
@@ -1396,6 +1377,33 @@ static int hub_port_disable(struct usb_device *hdev, int port)
return ret;
}
+/*
+ * Disable a port and mark a logical connnect-change event, so that some
+ * time later khubd will disconnect() any existing usb_device on the port
+ * and will re-enumerate if there actually is a device attached.
+ */
+static void hub_port_logical_disconnect(struct usb_device *hdev, int port)
+{
+ struct usb_hub *hub;
+
+ dev_dbg(hubdev(hdev), "logical disconnect on port %d\n", port + 1);
+ hub_port_disable(hdev, port);
+
+ /* FIXME let caller ask to power down the port:
+ * - some devices won't enumerate without a VBUS power cycle
+ * - SRP saves power that way
+ * - usb_suspend_device(dev,PM_SUSPEND_DISK)
+ * That's easy if this hub can switch power per-port, and
+ * khubd reactivates the port later (timer, SRP, etc).
+ * Powerdown must be optional, because of reset/DFU.
+ */
+
+ hub = usb_get_intfdata(hdev->actconfig->interface[0]);
+ set_bit(port, hub->change_bits);
+ kick_khubd(hub);
+}
+
+
#ifdef CONFIG_USB_SUSPEND
/*
@@ -1413,8 +1421,8 @@ static int hub_port_suspend(struct usb_device *hdev, int port)
int status;
struct usb_device *udev;
- udev = hdev->children[port - 1];
- // dev_dbg(hubdev(hdev), "suspend port %d\n", port);
+ udev = hdev->children[port];
+ // dev_dbg(hubdev(hdev), "suspend port %d\n", port + 1);
/* enable remote wakeup when appropriate; this lets the device
* wake up the upstream hub (including maybe the root hub).
@@ -1439,11 +1447,11 @@ static int hub_port_suspend(struct usb_device *hdev, int port)
}
/* see 7.1.7.6 */
- status = set_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND);
+ status = set_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND);
if (status) {
dev_dbg(hubdev(hdev),
"can't suspend port %d, status %d\n",
- port, status);
+ port + 1, status);
/* paranoia: "should not happen" */
(void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
@@ -1477,16 +1485,14 @@ static int __usb_suspend_device (struct usb_device *udev, int port, u32 state)
{
int status;
+ /* caller owns the udev device lock */
if (port < 0)
return port;
- /* NOTE: udev->serialize released on all real returns! */
-
if (state <= udev->dev.power.power_state
|| state < PM_SUSPEND_MEM
|| udev->state == USB_STATE_SUSPENDED
|| udev->state == USB_STATE_NOTATTACHED) {
- up(&udev->serialize);
return 0;
}
@@ -1562,11 +1568,10 @@ static int __usb_suspend_device (struct usb_device *udev, int port, u32 state)
else
status = -EOPNOTSUPP;
} else
- status = hub_port_suspend(udev->parent, port + 1);
+ status = hub_port_suspend(udev->parent, port);
if (status == 0)
udev->dev.power.power_state = state;
- up(&udev->serialize);
return status;
}
@@ -1590,7 +1595,15 @@ static int __usb_suspend_device (struct usb_device *udev, int port, u32 state)
*/
int usb_suspend_device(struct usb_device *udev, u32 state)
{
- return __usb_suspend_device(udev, locktree(udev), state);
+ int port, status;
+
+ port = locktree(udev);
+ if (port < 0)
+ return port;
+
+ status = __usb_suspend_device(udev, port, state);
+ usb_unlock_device(udev);
+ return status;
}
/*
@@ -1603,7 +1616,7 @@ static int finish_port_resume(struct usb_device *udev)
int status;
u16 devstatus;
- /* caller owns udev->serialize */
+ /* caller owns the udev device lock */
dev_dbg(&udev->dev, "usb resume\n");
udev->dev.power.power_state = PM_SUSPEND_ON;
@@ -1687,15 +1700,15 @@ hub_port_resume(struct usb_device *hdev, int port)
int status;
struct usb_device *udev;
- udev = hdev->children[port - 1];
- // dev_dbg(hubdev(hdev), "resume port %d\n", port);
+ udev = hdev->children[port];
+ // dev_dbg(hubdev(hdev), "resume port %d\n", port + 1);
/* see 7.1.7.7; affects power usage, but not budgeting */
- status = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND);
+ status = clear_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND);
if (status) {
dev_dbg(&hdev->actconfig->interface[0]->dev,
"can't resume port %d, status %d\n",
- port, status);
+ port + 1, status);
} else {
u16 devstatus;
u16 portchange;
@@ -1713,7 +1726,7 @@ hub_port_resume(struct usb_device *hdev, int port)
* sequence.
*/
devstatus = portchange = 0;
- status = hub_port_status(hdev, port - 1,
+ status = hub_port_status(hdev, port,
&devstatus, &portchange);
if (status < 0
|| (devstatus & LIVE_FLAGS) != LIVE_FLAGS
@@ -1721,7 +1734,7 @@ hub_port_resume(struct usb_device *hdev, int port)
) {
dev_dbg(&hdev->actconfig->interface[0]->dev,
"port %d status %04x.%04x after resume, %d\n",
- port, portchange, devstatus, status);
+ port + 1, portchange, devstatus, status);
} else {
/* TRSMRCY = 10 msec */
msleep(10);
@@ -1729,7 +1742,7 @@ hub_port_resume(struct usb_device *hdev, int port)
}
}
if (status < 0)
- status = hub_port_disable(hdev, port);
+ hub_port_logical_disconnect(hdev, port);
return status;
}
@@ -1773,7 +1786,7 @@ int usb_resume_device(struct usb_device *udev)
->actconfig->interface[0]);
}
} else if (udev->state == USB_STATE_SUSPENDED) {
- status = hub_port_resume(udev->parent, port + 1);
+ status = hub_port_resume(udev->parent, port);
} else {
status = 0;
udev->dev.power.power_state = PM_SUSPEND_ON;
@@ -1783,10 +1796,12 @@ int usb_resume_device(struct usb_device *udev)
status);
}
- up(&udev->serialize);
+ usb_unlock_device(udev);
/* rebind drivers that had no suspend() */
+ usb_lock_all_devices();
bus_rescan_devices(&usb_bus_type);
+ usb_unlock_all_devices();
return status;
}
@@ -1828,6 +1843,7 @@ static int hub_suspend(struct usb_interface *intf, u32 state)
continue;
down(&udev->serialize);
status = __usb_suspend_device(udev, port, state);
+ up(&udev->serialize);
if (status < 0)
dev_dbg(&intf->dev, "suspend port %d --> %d\n",
port, status);
@@ -1866,20 +1882,20 @@ static int hub_resume(struct usb_interface *intf)
continue;
down (&udev->serialize);
if (portstat & USB_PORT_STAT_SUSPEND)
- status = hub_port_resume(hdev, port + 1);
+ status = hub_port_resume(hdev, port);
else {
status = finish_port_resume(udev);
- if (status < 0)
- status = hub_port_disable(hdev, port);
- if (status < 0)
+ if (status < 0) {
dev_dbg(&intf->dev, "resume port %d --> %d\n",
- port, status);
+ port + 1, status);
+ hub_port_logical_disconnect(hdev, port);
+ }
}
up(&udev->serialize);
}
intf->dev.power.power_state = PM_SUSPEND_ON;
- hub_reactivate(hub);
+ hub_activate(hub);
return 0;
}
@@ -2011,7 +2027,7 @@ hub_port_init (struct usb_device *hdev, struct usb_device *udev, int port)
hdev->bus->b_hnp_enable = 0;
}
- retval = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND);
+ retval = clear_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND);
if (retval < 0 && retval != -EPIPE)
dev_dbg(&udev->dev, "can't clear suspend; %d\n", retval);
@@ -2443,10 +2459,10 @@ static void hub_events(void)
dev_dbg (hub_dev, "resetting for error %d\n",
hub->error);
- if (hub_reset(hub)) {
+ ret = usb_reset_device(hdev);
+ if (ret) {
dev_dbg (hub_dev,
- "can't reset; disconnecting\n");
- hub_start_disconnect(hdev);
+ "error resetting hub: %d\n", ret);
goto loop;
}
@@ -2502,15 +2518,17 @@ static void hub_events(void)
if (portchange & USB_PORT_STAT_C_SUSPEND) {
clear_port_feature(hdev, i + 1,
USB_PORT_FEAT_C_SUSPEND);
- if (hdev->children[i])
+ if (hdev->children[i]) {
ret = remote_wakeup(hdev->children[i]);
- else
+ if (ret < 0)
+ connect_change = 1;
+ } else {
ret = -ENODEV;
+ hub_port_disable(hdev, i);
+ }
dev_dbg (hub_dev,
"resume on port %d, status %d\n",
i + 1, ret);
- if (ret < 0)
- ret = hub_port_disable(hdev, i);
}
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
@@ -2554,7 +2572,7 @@ static void hub_events(void)
}
loop:
- up(&hdev->serialize);
+ usb_unlock_device(hdev);
usb_put_dev(hdev);
} /* end while (1) */
@@ -2707,13 +2725,15 @@ static int config_descriptors_changed(struct usb_device *udev)
*
* The caller must own the device lock. For example, it's safe to use
* this from a driver probe() routine after downloading new firmware.
+ * For calls that might not occur during probe(), drivers should lock
+ * the device using usb_lock_device_for_reset().
*/
-int __usb_reset_device(struct usb_device *udev)
+int usb_reset_device(struct usb_device *udev)
{
struct usb_device *parent = udev->parent;
struct usb_device_descriptor descriptor = udev->descriptor;
int i, ret, port = -1;
- struct usb_hub *hub;
+ int udev_is_a_hub = 0;
if (udev->state == USB_STATE_NOTATTACHED ||
udev->state == USB_STATE_SUSPENDED) {
@@ -2722,13 +2742,9 @@ int __usb_reset_device(struct usb_device *udev)
return -EINVAL;
}
- /* FIXME: This should be legal for regular hubs. Root hubs may
- * have special requirements. */
- if (udev->maxchild) {
- /* this requires hub- or hcd-specific logic;
- * see hub_reset() and OHCI hc_restart()
- */
- dev_dbg(&udev->dev, "%s for hub!\n", __FUNCTION__);
+ if (!parent) {
+ /* this requires hcd-specific logic; see OHCI hc_restart() */
+ dev_dbg(&udev->dev, "%s for root hub!\n", __FUNCTION__);
return -EISDIR;
}
@@ -2744,6 +2760,19 @@ int __usb_reset_device(struct usb_device *udev)
return -ENOENT;
}
+ /* If we're resetting an active hub, take some special actions */
+ if (udev->actconfig &&
+ udev->actconfig->interface[0]->dev.driver ==
+ &hub_driver.driver) {
+ udev_is_a_hub = 1;
+ hub_pre_reset(udev);
+ }
+
+ /* ep0 maxpacket size may change; let the HCD know about it.
+ * Other endpoints will be handled by re-enumeration. */
+ usb_disable_endpoint(udev, 0);
+ usb_disable_endpoint(udev, 0 + USB_DIR_IN);
+
ret = hub_port_init(parent, udev, port);
if (ret < 0)
goto re_enumerate;
@@ -2757,7 +2786,7 @@ int __usb_reset_device(struct usb_device *udev)
}
if (!udev->actconfig)
- return 0;
+ goto done;
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_SET_CONFIGURATION, 0,
@@ -2791,32 +2820,12 @@ int __usb_reset_device(struct usb_device *udev)
}
}
+done:
+ if (udev_is_a_hub)
+ hub_post_reset(udev);
return 0;
re_enumerate:
- hub_port_disable(parent, port);
-
- hub = usb_get_intfdata(parent->actconfig->interface[0]);
- set_bit(port, hub->change_bits);
-
- spin_lock_irq(&hub_event_lock);
- if (list_empty(&hub->event_list)) {
- list_add_tail(&hub->event_list, &hub_event_list);
- wake_up(&khubd_wait);
- }
- spin_unlock_irq(&hub_event_lock);
-
+ hub_port_logical_disconnect(parent, port);
return -ENODEV;
}
-EXPORT_SYMBOL(__usb_reset_device);
-
-int usb_reset_device(struct usb_device *udev)
-{
- int r;
-
- down(&udev->serialize);
- r = __usb_reset_device(udev);
- up(&udev->serialize);
-
- return r;
-}
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index c9f57e334a4f..24452d66d576 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -4,7 +4,7 @@
* inode.c -- Inode/Dentry functions for the USB device file system.
*
* Copyright (C) 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
- * Copyright (C) 2001,2002 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2002,2004 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -40,17 +40,15 @@
#include <linux/smp_lock.h>
#include <linux/parser.h>
#include <asm/byteorder.h>
+#include "usb.h"
static struct super_operations usbfs_ops;
static struct file_operations default_file_operations;
static struct inode_operations usbfs_dir_inode_operations;
-static struct vfsmount *usbdevfs_mount;
static struct vfsmount *usbfs_mount;
-static int usbdevfs_mount_count; /* = 0 */
static int usbfs_mount_count; /* = 0 */
static int ignore_mount = 0;
-static struct dentry *devices_usbdevfs_dentry;
static struct dentry *devices_usbfs_dentry;
static int num_buses; /* = 0 */
@@ -240,9 +238,6 @@ static int remount(struct super_block *sb, int *flags, char *data)
if (usbfs_mount && usbfs_mount->mnt_sb)
update_sb(usbfs_mount->mnt_sb);
- if (usbdevfs_mount && usbdevfs_mount->mnt_sb)
- update_sb(usbdevfs_mount->mnt_sb);
-
return 0;
}
@@ -561,28 +556,12 @@ static void fs_remove_file (struct dentry *dentry)
/* --------------------------------------------------------------------- */
-
-
-/*
- * The usbdevfs name is now deprecated (as of 2.5.1).
- * It will be removed when the 2.7.x development cycle is started.
- * You have been warned :)
- */
-static struct file_system_type usbdevice_fs_type;
-
static struct super_block *usb_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return get_sb_single(fs_type, flags, data, usbfs_fill_super);
}
-static struct file_system_type usbdevice_fs_type = {
- .owner = THIS_MODULE,
- .name = "usbdevfs",
- .get_sb = usb_get_sb,
- .kill_sb = kill_litter_super,
-};
-
static struct file_system_type usb_fs_type = {
.owner = THIS_MODULE,
.name = "usbfs",
@@ -603,16 +582,10 @@ static int create_special_files (void)
ignore_mount = 1;
/* create the devices special file */
- retval = simple_pin_fs("usbdevfs", &usbdevfs_mount, &usbdevfs_mount_count);
- if (retval) {
- err ("Unable to get usbdevfs mount");
- goto exit;
- }
-
retval = simple_pin_fs("usbfs", &usbfs_mount, &usbfs_mount_count);
if (retval) {
err ("Unable to get usbfs mount");
- goto error_clean_usbdevfs_mount;
+ goto exit;
}
ignore_mount = 0;
@@ -620,7 +593,7 @@ static int create_special_files (void)
parent = usbfs_mount->mnt_sb->s_root;
devices_usbfs_dentry = fs_create_file ("devices",
listmode | S_IFREG, parent,
- NULL, &usbdevfs_devices_fops,
+ NULL, &usbfs_devices_fops,
listuid, listgid);
if (devices_usbfs_dentry == NULL) {
err ("Unable to create devices usbfs file");
@@ -628,42 +601,19 @@ static int create_special_files (void)
goto error_clean_mounts;
}
- parent = usbdevfs_mount->mnt_sb->s_root;
- devices_usbdevfs_dentry = fs_create_file ("devices",
- listmode | S_IFREG, parent,
- NULL, &usbdevfs_devices_fops,
- listuid, listgid);
- if (devices_usbdevfs_dentry == NULL) {
- err ("Unable to create devices usbfs file");
- retval = -ENODEV;
- goto error_remove_file;
- }
-
goto exit;
-error_remove_file:
- fs_remove_file (devices_usbfs_dentry);
- devices_usbfs_dentry = NULL;
-
error_clean_mounts:
simple_release_fs(&usbfs_mount, &usbfs_mount_count);
-
-error_clean_usbdevfs_mount:
- simple_release_fs(&usbdevfs_mount, &usbdevfs_mount_count);
-
exit:
return retval;
}
static void remove_special_files (void)
{
- if (devices_usbdevfs_dentry)
- fs_remove_file (devices_usbdevfs_dentry);
if (devices_usbfs_dentry)
fs_remove_file (devices_usbfs_dentry);
- devices_usbdevfs_dentry = NULL;
devices_usbfs_dentry = NULL;
- simple_release_fs(&usbdevfs_mount, &usbdevfs_mount_count);
simple_release_fs(&usbfs_mount, &usbfs_mount_count);
}
@@ -671,11 +621,6 @@ void usbfs_update_special (void)
{
struct inode *inode;
- if (devices_usbdevfs_dentry) {
- inode = devices_usbdevfs_dentry->d_inode;
- if (inode)
- inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- }
if (devices_usbfs_dentry) {
inode = devices_usbfs_dentry->d_inode;
if (inode)
@@ -707,29 +652,16 @@ void usbfs_add_bus(struct usb_bus *bus)
return;
}
- parent = usbdevfs_mount->mnt_sb->s_root;
- bus->usbdevfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent,
- bus, NULL, busuid, busgid);
- if (bus->usbdevfs_dentry == NULL) {
- err ("error creating usbdevfs bus entry");
- return;
- }
-
usbfs_update_special();
- usbdevfs_conn_disc_event();
+ usbfs_conn_disc_event();
}
-
void usbfs_remove_bus(struct usb_bus *bus)
{
if (bus->usbfs_dentry) {
fs_remove_file (bus->usbfs_dentry);
bus->usbfs_dentry = NULL;
}
- if (bus->usbdevfs_dentry) {
- fs_remove_file (bus->usbdevfs_dentry);
- bus->usbdevfs_dentry = NULL;
- }
--num_buses;
if (num_buses <= 0) {
@@ -738,7 +670,7 @@ void usbfs_remove_bus(struct usb_bus *bus)
}
usbfs_update_special();
- usbdevfs_conn_disc_event();
+ usbfs_conn_disc_event();
}
void usbfs_add_device(struct usb_device *dev)
@@ -750,20 +682,12 @@ void usbfs_add_device(struct usb_device *dev)
sprintf (name, "%03d", dev->devnum);
dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG,
dev->bus->usbfs_dentry, dev,
- &usbdevfs_device_file_operations,
+ &usbfs_device_file_operations,
devuid, devgid);
if (dev->usbfs_dentry == NULL) {
err ("error creating usbfs device entry");
return;
}
- dev->usbdevfs_dentry = fs_create_file (name, devmode | S_IFREG,
- dev->bus->usbdevfs_dentry, dev,
- &usbdevfs_device_file_operations,
- devuid, devgid);
- if (dev->usbdevfs_dentry == NULL) {
- err ("error creating usbdevfs device entry");
- return;
- }
/* Set the size of the device's file to be
* equal to the size of the device descriptors. */
@@ -775,11 +699,9 @@ void usbfs_add_device(struct usb_device *dev)
}
if (dev->usbfs_dentry->d_inode)
dev->usbfs_dentry->d_inode->i_size = i_size;
- if (dev->usbdevfs_dentry->d_inode)
- dev->usbdevfs_dentry->d_inode->i_size = i_size;
usbfs_update_special();
- usbdevfs_conn_disc_event();
+ usbfs_conn_disc_event();
}
void usbfs_remove_device(struct usb_device *dev)
@@ -791,10 +713,6 @@ void usbfs_remove_device(struct usb_device *dev)
fs_remove_file (dev->usbfs_dentry);
dev->usbfs_dentry = NULL;
}
- if (dev->usbdevfs_dentry) {
- fs_remove_file (dev->usbdevfs_dentry);
- dev->usbdevfs_dentry = NULL;
- }
while (!list_empty(&dev->filelist)) {
ds = list_entry(dev->filelist.next, struct dev_state, list);
list_del_init(&ds->list);
@@ -807,51 +725,38 @@ void usbfs_remove_device(struct usb_device *dev)
}
}
usbfs_update_special();
- usbdevfs_conn_disc_event();
+ usbfs_conn_disc_event();
}
/* --------------------------------------------------------------------- */
-#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *usbdir = NULL;
-#endif
int __init usbfs_init(void)
{
int retval;
- retval = usb_register(&usbdevfs_driver);
+ retval = usb_register(&usbfs_driver);
if (retval)
return retval;
retval = register_filesystem(&usb_fs_type);
if (retval) {
- usb_deregister(&usbdevfs_driver);
- return retval;
- }
- retval = register_filesystem(&usbdevice_fs_type);
- if (retval) {
- unregister_filesystem(&usb_fs_type);
- usb_deregister(&usbdevfs_driver);
+ usb_deregister(&usbfs_driver);
return retval;
}
-#ifdef CONFIG_PROC_FS
- /* create mount point for usbdevfs */
+ /* create mount point for usbfs */
usbdir = proc_mkdir("usb", proc_bus);
-#endif
return 0;
}
void usbfs_cleanup(void)
{
- usb_deregister(&usbdevfs_driver);
+ usb_deregister(&usbfs_driver);
unregister_filesystem(&usb_fs_type);
- unregister_filesystem(&usbdevice_fs_type);
-#ifdef CONFIG_PROC_FS
if (usbdir)
remove_proc_entry("usb", proc_bus);
-#endif
}
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 98695c68d687..f2cd4770eb4f 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -17,6 +17,8 @@
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/timer.h>
+#include <linux/ctype.h>
+#include <linux/device.h>
#include <asm/byteorder.h>
#include "hcd.h" /* for usbcore internals */
@@ -623,6 +625,20 @@ int usb_get_string(struct usb_device *dev, unsigned short langid,
return result;
}
+static void usb_try_string_workarounds(unsigned char *buf, int *length)
+{
+ int newlength, oldlength = *length;
+
+ for (newlength = 2; newlength + 1 < oldlength; newlength += 2)
+ if (!isprint(buf[newlength]) || buf[newlength + 1])
+ break;
+
+ if (newlength > 2) {
+ buf[0] = newlength;
+ *length = newlength;
+ }
+}
+
static int usb_string_sub(struct usb_device *dev, unsigned int langid,
unsigned int index, unsigned char *buf)
{
@@ -634,19 +650,26 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid,
/* If that failed try to read the descriptor length, then
* ask for just that many bytes */
- if (rc < 0) {
+ if (rc < 2) {
rc = usb_get_string(dev, langid, index, buf, 2);
if (rc == 2)
rc = usb_get_string(dev, langid, index, buf, buf[0]);
}
- if (rc >= 0) {
+ if (rc >= 2) {
+ if (!buf[0] && !buf[1])
+ usb_try_string_workarounds(buf, &rc);
+
/* There might be extra junk at the end of the descriptor */
if (buf[0] < rc)
rc = buf[0];
- if (rc < 2)
- rc = -EINVAL;
+
+ rc = rc - (rc & 1); /* force a multiple of two */
}
+
+ if (rc < 2)
+ rc = (rc < 0 ? rc : -EINVAL);
+
return rc;
}
@@ -724,6 +747,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
buf[idx] = 0;
err = idx;
+ if (tbuf[1] != USB_DT_STRING)
+ dev_dbg(&dev->dev, "wrong descriptor type %02x for string %d (\"%s\")\n", tbuf[1], index, buf);
+
errout:
kfree(tbuf);
return err;
@@ -1132,6 +1158,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
* use usb_set_interface() on the interfaces it claims. Resetting the whole
* configuration would affect other drivers' interfaces.
*
+ * The caller must own the device lock.
+ *
* Returns zero on success, else a negative error code.
*/
int usb_reset_configuration(struct usb_device *dev)
@@ -1142,9 +1170,9 @@ int usb_reset_configuration(struct usb_device *dev)
if (dev->state == USB_STATE_SUSPENDED)
return -EHOSTUNREACH;
- /* caller must own dev->serialize (config won't change)
- * and the usb bus readlock (so driver bindings are stable);
- * so calls during probe() are fine
+ /* caller must have locked the device and must own
+ * the usb bus readlock (so driver bindings are stable);
+ * calls during probe() are fine
*/
for (i = 1; i < 16; ++i) {
@@ -1199,7 +1227,7 @@ static void release_interface(struct device *dev)
* usb_set_configuration - Makes a particular device setting be current
* @dev: the device whose configuration is being updated
* @configuration: the configuration being chosen.
- * Context: !in_interrupt(), caller holds dev->serialize
+ * Context: !in_interrupt(), caller owns the device lock
*
* This is used to enable non-default device modes. Not all devices
* use this kind of configurability; many devices only have one
@@ -1220,8 +1248,8 @@ static void release_interface(struct device *dev)
* usb_set_interface().
*
* This call is synchronous. The calling context must be able to sleep,
- * and must not hold the driver model lock for USB; usb device driver
- * probe() methods may not use this routine.
+ * must own the device lock, and must not hold the driver model's USB
+ * bus rwsem; usb device driver probe() methods cannot use this routine.
*
* Returns zero on success, or else the status code returned by the
* underlying call that failed. On succesful completion, each interface
@@ -1236,8 +1264,6 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
struct usb_interface **new_interfaces = NULL;
int n, nintf;
- /* dev->serialize guards all config changes */
-
for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
if (dev->config[i].desc.bConfigurationValue == configuration) {
cp = &dev->config[i];
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 78c5ca2f1051..bae974d587ae 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -27,11 +27,13 @@
static ssize_t show_##field (struct device *dev, char *buf) \
{ \
struct usb_device *udev; \
+ struct usb_host_config *actconfig; \
\
udev = to_usb_device (dev); \
- if (udev->actconfig) \
+ actconfig = udev->actconfig; \
+ if (actconfig) \
return sprintf (buf, format_string, \
- udev->actconfig->desc.field * multiplier); \
+ actconfig->desc.field * multiplier); \
else \
return 0; \
} \
@@ -44,6 +46,28 @@ usb_actconfig_attr (bNumInterfaces, 1, "%2d\n")
usb_actconfig_attr (bmAttributes, 1, "%2x\n")
usb_actconfig_attr (bMaxPower, 2, "%3dmA\n")
+#define usb_actconfig_str(name, field) \
+static ssize_t show_##name(struct device *dev, char *buf) \
+{ \
+ struct usb_device *udev; \
+ struct usb_host_config *actconfig; \
+ int len; \
+ \
+ udev = to_usb_device (dev); \
+ actconfig = udev->actconfig; \
+ if (!actconfig) \
+ return 0; \
+ len = usb_string(udev, actconfig->desc.field, buf, PAGE_SIZE); \
+ if (len < 0) \
+ return 0; \
+ buf[len] = '\n'; \
+ buf[len+1] = 0; \
+ return len+1; \
+} \
+static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
+
+usb_actconfig_str (configuration, iConfiguration)
+
/* configuration value is always present, and r/w */
usb_actconfig_show(bConfigurationValue, 1, "%u\n");
@@ -55,9 +79,9 @@ set_bConfigurationValue (struct device *dev, const char *buf, size_t count)
if (sscanf (buf, "%u", &config) != 1 || config > 255)
return -EINVAL;
- down(&udev->serialize);
+ usb_lock_device(udev);
value = usb_set_configuration (udev, config);
- up(&udev->serialize);
+ usb_unlock_device(udev);
return (value < 0) ? value : count;
}
@@ -198,6 +222,7 @@ void usb_create_sysfs_dev_files (struct usb_device *udev)
device_create_file (dev, &dev_attr_product);
if (udev->descriptor.iSerialNumber)
device_create_file (dev, &dev_attr_serial);
+ device_create_file (dev, &dev_attr_configuration);
}
void usb_remove_sysfs_dev_files (struct usb_device *udev)
@@ -212,6 +237,7 @@ void usb_remove_sysfs_dev_files (struct usb_device *udev)
device_remove_file(dev, &dev_attr_product);
if (udev->descriptor.iSerialNumber)
device_remove_file(dev, &dev_attr_serial);
+ device_remove_file (dev, &dev_attr_configuration);
}
/* Interface fields */
@@ -231,7 +257,26 @@ usb_intf_attr (bNumEndpoints, "%02x\n")
usb_intf_attr (bInterfaceClass, "%02x\n")
usb_intf_attr (bInterfaceSubClass, "%02x\n")
usb_intf_attr (bInterfaceProtocol, "%02x\n")
-usb_intf_attr (iInterface, "%02x\n")
+
+#define usb_intf_str(name, field) \
+static ssize_t show_##name(struct device *dev, char *buf) \
+{ \
+ struct usb_interface *intf; \
+ struct usb_device *udev; \
+ int len; \
+ \
+ intf = to_usb_interface (dev); \
+ udev = interface_to_usbdev (intf); \
+ len = usb_string(udev, intf->cur_altsetting->desc.field, buf, PAGE_SIZE);\
+ if (len < 0) \
+ return 0; \
+ buf[len] = '\n'; \
+ buf[len+1] = 0; \
+ return len+1; \
+} \
+static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
+
+usb_intf_str (interface, iInterface);
static struct attribute *intf_attrs[] = {
&dev_attr_bInterfaceNumber.attr,
@@ -240,7 +285,6 @@ static struct attribute *intf_attrs[] = {
&dev_attr_bInterfaceClass.attr,
&dev_attr_bInterfaceSubClass.attr,
&dev_attr_bInterfaceProtocol.attr,
- &dev_attr_iInterface.attr,
NULL,
};
static struct attribute_group intf_attr_grp = {
@@ -250,9 +294,17 @@ static struct attribute_group intf_attr_grp = {
void usb_create_sysfs_intf_files (struct usb_interface *intf)
{
sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
+
+ if (intf->cur_altsetting->desc.iInterface)
+ device_create_file(&intf->dev, &dev_attr_interface);
+
}
void usb_remove_sysfs_intf_files (struct usb_interface *intf)
{
sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
+
+ if (intf->cur_altsetting->desc.iInterface)
+ device_remove_file(&intf->dev, &dev_attr_interface);
+
}
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 3c14361bbeb3..f57de4dca2bf 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -451,6 +451,11 @@ int usb_unlink_urb(struct urb *urb)
if (!urb)
return -EINVAL;
if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) {
+#ifdef CONFIG_DEBUG_KERNEL
+ printk(KERN_NOTICE "usb_unlink_urb() is deprecated for "
+ "synchronous unlinks. Use usb_kill_urb() instead.\n");
+ WARN_ON(1);
+#endif
usb_kill_urb(urb);
return 0;
}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 138541225604..ca0b29d0ac1f 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -39,6 +39,7 @@
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/smp_lock.h>
+#include <linux/rwsem.h>
#include <linux/usb.h>
#include <asm/io.h>
@@ -62,6 +63,8 @@ const char *usbcore_name = "usbcore";
int nousb; /* Disable USB when built into kernel image */
/* Not honored on modular build */
+static DECLARE_RWSEM(usb_all_devices_rwsem);
+
static int generic_probe (struct device *dev)
{
@@ -100,7 +103,10 @@ int usb_probe_interface(struct device *dev)
id = usb_match_id (intf, driver->id_table);
if (id) {
dev_dbg (dev, "%s - got id\n", __FUNCTION__);
+ intf->condition = USB_INTERFACE_BINDING;
error = driver->probe (intf, id);
+ intf->condition = error ? USB_INTERFACE_UNBOUND :
+ USB_INTERFACE_BOUND;
}
return error;
@@ -112,6 +118,8 @@ int usb_unbind_interface(struct device *dev)
struct usb_interface *intf = to_usb_interface(dev);
struct usb_driver *driver = to_usb_driver(intf->dev.driver);
+ intf->condition = USB_INTERFACE_UNBINDING;
+
/* release all urbs for this interface */
usb_disable_interface(interface_to_usbdev(intf), intf);
@@ -123,6 +131,7 @@ int usb_unbind_interface(struct device *dev)
intf->altsetting[0].desc.bInterfaceNumber,
0);
usb_set_intfdata(intf, NULL);
+ intf->condition = USB_INTERFACE_UNBOUND;
return 0;
}
@@ -153,7 +162,9 @@ int usb_register(struct usb_driver *new_driver)
new_driver->driver.remove = usb_unbind_interface;
new_driver->driver.owner = new_driver->owner;
+ usb_lock_all_devices();
retval = driver_register(&new_driver->driver);
+ usb_unlock_all_devices();
if (!retval) {
pr_info("%s: registered new driver %s\n",
@@ -182,7 +193,9 @@ void usb_deregister(struct usb_driver *driver)
{
pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name);
+ usb_lock_all_devices();
driver_unregister (&driver->driver);
+ usb_unlock_all_devices();
usbfs_update_special();
}
@@ -204,7 +217,7 @@ void usb_deregister(struct usb_driver *driver)
* alternate settings available for this interfaces.
*
* Don't call this function unless you are bound to one of the interfaces
- * on this device or you own the dev->serialize semaphore!
+ * on this device or you have locked the device!
*/
struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
{
@@ -237,7 +250,7 @@ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
* drivers avoid such mistakes.
*
* Don't call this function unless you are bound to the intf interface
- * or you own the device's ->serialize semaphore!
+ * or you have locked the device!
*/
struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,
unsigned int altnum)
@@ -305,11 +318,12 @@ usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum)
* way to bind to an interface is to return the private data from
* the driver's probe() method.
*
- * Callers must own the driver model's usb bus writelock. So driver
- * probe() entries don't need extra locking, but other call contexts
- * may need to explicitly claim that lock.
+ * Callers must own the device lock and the driver model's usb_bus_type.subsys
+ * writelock. So driver probe() entries don't need extra locking,
+ * but other call contexts may need to explicitly claim those locks.
*/
-int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv)
+int usb_driver_claim_interface(struct usb_driver *driver,
+ struct usb_interface *iface, void* priv)
{
struct device *dev = &iface->dev;
@@ -318,6 +332,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *
dev->driver = &driver->driver;
usb_set_intfdata(iface, priv);
+ iface->condition = USB_INTERFACE_BOUND;
/* if interface was already added, bind now; else let
* the future device_add() bind it, bypassing probe()
@@ -338,8 +353,8 @@ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *
* also causes the driver disconnect() method to be called.
*
* This call is synchronous, and may not be used in an interrupt context.
- * Callers must own the usb_device serialize semaphore and the driver model's
- * usb bus writelock. So driver disconnect() entries don't need extra locking,
+ * Callers must own the device lock and the driver model's usb_bus_type.subsys
+ * writelock. So driver disconnect() entries don't need extra locking,
* but other call contexts may need to explicitly claim those locks.
*/
void usb_driver_release_interface(struct usb_driver *driver,
@@ -357,6 +372,7 @@ void usb_driver_release_interface(struct usb_driver *driver,
dev->driver = NULL;
usb_set_intfdata(iface, NULL);
+ iface->condition = USB_INTERFACE_UNBOUND;
}
/**
@@ -748,7 +764,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port)
init_MUTEX(&dev->serialize);
if (dev->bus->op->allocate)
- dev->bus->op->allocate(dev);
+ if (dev->bus->op->allocate(dev)) {
+ kfree(dev);
+ return NULL;
+ }
return dev;
}
@@ -819,6 +838,160 @@ void usb_put_intf(struct usb_interface *intf)
put_device(&intf->dev);
}
+
+/* USB device locking
+ *
+ * Although locking USB devices should be straightforward, it is
+ * complicated by the way the driver-model core works. When a new USB
+ * driver is registered or unregistered, the core will automatically
+ * probe or disconnect all matching interfaces on all USB devices while
+ * holding the USB subsystem writelock. There's no good way for us to
+ * tell which devices will be used or to lock them beforehand; our only
+ * option is to effectively lock all the USB devices.
+ *
+ * We do that by using a private rw-semaphore, usb_all_devices_rwsem.
+ * When locking an individual device you must first acquire the rwsem's
+ * readlock. When a driver is registered or unregistered the writelock
+ * must be held. These actions are encapsulated in the subroutines
+ * below, so all a driver needs to do is call usb_lock_device() and
+ * usb_unlock_device().
+ *
+ * Complications arise when several devices are to be locked at the same
+ * time. Only hub-aware drivers that are part of usbcore ever have to
+ * do this; nobody else needs to worry about it. The problem is that
+ * usb_lock_device() must not be called to lock a second device since it
+ * would acquire the rwsem's readlock reentrantly, leading to deadlock if
+ * another thread was waiting for the writelock. The solution is simple:
+ *
+ * When locking more than one device, call usb_lock_device()
+ * to lock the first one. Lock the others by calling
+ * down(&udev->serialize) directly.
+ *
+ * When unlocking multiple devices, use up(&udev->serialize)
+ * to unlock all but the last one. Unlock the last one by
+ * calling usb_unlock_device().
+ *
+ * When locking both a device and its parent, always lock the
+ * the parent first.
+ */
+
+/**
+ * usb_lock_device - acquire the lock for a usb device structure
+ * @udev: device that's being locked
+ *
+ * Use this routine when you don't hold any other device locks;
+ * to acquire nested inner locks call down(&udev->serialize) directly.
+ * This is necessary for proper interaction with usb_lock_all_devices().
+ */
+void usb_lock_device(struct usb_device *udev)
+{
+ down_read(&usb_all_devices_rwsem);
+ down(&udev->serialize);
+}
+
+/**
+ * usb_trylock_device - attempt to acquire the lock for a usb device structure
+ * @udev: device that's being locked
+ *
+ * Don't use this routine if you already hold a device lock;
+ * use down_trylock(&udev->serialize) instead.
+ * This is necessary for proper interaction with usb_lock_all_devices().
+ *
+ * Returns 1 if successful, 0 if contention.
+ */
+int usb_trylock_device(struct usb_device *udev)
+{
+ if (!down_read_trylock(&usb_all_devices_rwsem))
+ return 0;
+ if (down_trylock(&udev->serialize)) {
+ up_read(&usb_all_devices_rwsem);
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * usb_lock_device_for_reset - cautiously acquire the lock for a
+ * usb device structure
+ * @udev: device that's being locked
+ * @iface: interface bound to the driver making the request (optional)
+ *
+ * Attempts to acquire the device lock, but fails if the device is
+ * NOTATTACHED or SUSPENDED, or if iface is specified and the interface
+ * is neither BINDING nor BOUND. Rather than sleeping to wait for the
+ * lock, the routine polls repeatedly. This is to prevent deadlock with
+ * disconnect; in some drivers (such as usb-storage) the disconnect()
+ * callback will block waiting for a device reset to complete.
+ *
+ * Returns a negative error code for failure, otherwise 1 or 0 to indicate
+ * that the device will or will not have to be unlocked. (0 can be
+ * returned when an interface is given and is BINDING, because in that
+ * case the driver already owns the device lock.)
+ */
+int usb_lock_device_for_reset(struct usb_device *udev,
+ struct usb_interface *iface)
+{
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return -ENODEV;
+ if (udev->state == USB_STATE_SUSPENDED)
+ return -EHOSTUNREACH;
+ if (iface) {
+ switch (iface->condition) {
+ case USB_INTERFACE_BINDING:
+ return 0;
+ case USB_INTERFACE_BOUND:
+ break;
+ default:
+ return -EINTR;
+ }
+ }
+
+ while (!usb_trylock_device(udev)) {
+ msleep(15);
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return -ENODEV;
+ if (udev->state == USB_STATE_SUSPENDED)
+ return -EHOSTUNREACH;
+ if (iface && iface->condition != USB_INTERFACE_BOUND)
+ return -EINTR;
+ }
+ return 1;
+}
+
+/**
+ * usb_unlock_device - release the lock for a usb device structure
+ * @udev: device that's being unlocked
+ *
+ * Use this routine when releasing the only device lock you hold;
+ * to release inner nested locks call up(&udev->serialize) directly.
+ * This is necessary for proper interaction with usb_lock_all_devices().
+ */
+void usb_unlock_device(struct usb_device *udev)
+{
+ up(&udev->serialize);
+ up_read(&usb_all_devices_rwsem);
+}
+
+/**
+ * usb_lock_all_devices - acquire the lock for all usb device structures
+ *
+ * This is necessary when registering a new driver or probing a bus,
+ * since the driver-model core may try to use any usb_device.
+ */
+void usb_lock_all_devices(void)
+{
+ down_write(&usb_all_devices_rwsem);
+}
+
+/**
+ * usb_unlock_all_devices - release the lock for all usb device structures
+ */
+void usb_unlock_all_devices(void)
+{
+ up_write(&usb_all_devices_rwsem);
+}
+
+
static struct usb_device *match_device(struct usb_device *dev,
u16 vendor_id, u16 product_id)
{
@@ -840,8 +1013,10 @@ static struct usb_device *match_device(struct usb_device *dev,
/* look through all of the children of this device */
for (child = 0; child < dev->maxchild; ++child) {
if (dev->children[child]) {
+ down(&dev->children[child]->serialize);
ret_dev = match_device(dev->children[child],
vendor_id, product_id);
+ up(&dev->children[child]->serialize);
if (ret_dev)
goto exit;
}
@@ -876,7 +1051,9 @@ struct usb_device *usb_find_device(u16 vendor_id, u16 product_id)
bus = container_of(buslist, struct usb_bus, bus_list);
if (!bus->root_hub)
continue;
+ usb_lock_device(bus->root_hub);
dev = match_device(bus->root_hub, vendor_id, product_id);
+ usb_unlock_device(bus->root_hub);
if (dev)
goto exit;
}
@@ -1362,6 +1539,11 @@ EXPORT_SYMBOL(usb_put_dev);
EXPORT_SYMBOL(usb_get_dev);
EXPORT_SYMBOL(usb_hub_tt_clear_buffer);
+EXPORT_SYMBOL(usb_lock_device);
+EXPORT_SYMBOL(usb_trylock_device);
+EXPORT_SYMBOL(usb_lock_device_for_reset);
+EXPORT_SYMBOL(usb_unlock_device);
+
EXPORT_SYMBOL(usb_driver_claim_interface);
EXPORT_SYMBOL(usb_driver_release_interface);
EXPORT_SYMBOL(usb_match_id);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index f1dff4f4d5d6..30d2cf7dd762 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -22,8 +22,14 @@ extern int usb_get_device_descriptor(struct usb_device *dev,
unsigned int size);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
-extern void usb_set_device_state(struct usb_device *udev,
- enum usb_device_state new_state);
+extern void usb_lock_all_devices(void);
+extern void usb_unlock_all_devices(void);
/* for labeling diagnostics */
extern const char *usbcore_name;
+
+/* usbfs stuff */
+extern struct usb_driver usbfs_driver;
+extern struct file_operations usbfs_devices_fops;
+extern struct file_operations usbfs_device_file_operations;
+extern void usbfs_conn_disc_event(void);
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index decbca335389..c6e0693ed1d6 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -39,6 +39,17 @@ config USB_GADGET
If in doubt, say "N" and don't enable these drivers; most people
don't have this kind of hardware (except maybe inside Linux PDAs).
+config USB_GADGET_DEBUG_FILES
+ boolean "Debugging information files"
+ depends on USB_GADGET && PROC_FS
+ help
+ Some of the drivers in the "gadget" framework can expose
+ debugging information in files such as /proc/driver/udc
+ (for a peripheral controller). The information in these
+ files may help when you're troubleshooting or bringing up a
+ driver on a new board. Enable these files by choosing "Y"
+ here. If in doubt, or to conserve kernel memory, say "N".
+
#
# USB Peripheral Controller Support
#
@@ -206,10 +217,6 @@ config USB_OTG
Select this only if your OMAP board has a Mini-AB connector.
-config USB_OMAP_PROC
- boolean "/proc/driver/udc file"
- depends on USB_GADGET_OMAP
-
endchoice
config USB_GADGET_DUALSPEED
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index 91e878642b9f..ed532f7176f9 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -770,7 +770,8 @@ usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
spin_lock_irqsave (&dum->lock, flags);
stop_activity (dum, driver);
- dum->port_status &= ~USB_PORT_STAT_CONNECTION;
+ dum->port_status &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE |
+ USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED);
dum->port_status |= (1 << USB_PORT_FEAT_C_CONNECTION);
spin_unlock_irqrestore (&dum->lock, flags);
@@ -815,8 +816,8 @@ static int dummy_urb_enqueue (
struct dummy *dum;
unsigned long flags;
- /* patch to usb_sg_init() is in 2.5.60 */
- BUG_ON (!urb->transfer_buffer && urb->transfer_buffer_length);
+ if (!urb->transfer_buffer && urb->transfer_buffer_length)
+ return -EINVAL;
dum = container_of (hcd, struct dummy, hcd);
spin_lock_irqsave (&dum->lock, flags);
@@ -1102,10 +1103,10 @@ restart:
ep = find_endpoint(dum, address);
if (!ep) {
/* set_configuration() disagreement */
- dev_err (hardware,
+ dev_dbg (hardware,
"no ep configured for urb %p\n",
urb);
- maybe_set_status (urb, -ETIMEDOUT);
+ maybe_set_status (urb, -EPROTO);
goto return_urb;
}
@@ -1409,9 +1410,12 @@ static int dummy_hub_control (
case ClearPortFeature:
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- /* 20msec resume signaling */
- dum->resuming = 1;
- dum->re_timeout = jiffies + ((HZ * 20)/1000);
+ if (dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)) {
+ /* 20msec resume signaling */
+ dum->resuming = 1;
+ dum->re_timeout = jiffies +
+ msecs_to_jiffies(20);
+ }
break;
case USB_PORT_FEAT_POWER:
dum->port_status = 0;
@@ -1440,7 +1444,7 @@ static int dummy_hub_control (
dum->port_status &= ~(1 << USB_PORT_FEAT_SUSPEND);
dum->resuming = 0;
dum->re_timeout = 0;
- if (dum->driver->resume) {
+ if (dum->driver && dum->driver->resume) {
spin_unlock (&dum->lock);
dum->driver->resume (&dum->gadget);
spin_lock (&dum->lock);
@@ -1481,11 +1485,15 @@ static int dummy_hub_control (
case SetPortFeature:
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- dum->port_status |= (1 << USB_PORT_FEAT_SUSPEND);
- if (dum->driver->suspend) {
- spin_unlock (&dum->lock);
- dum->driver->suspend (&dum->gadget);
- spin_lock (&dum->lock);
+ if ((dum->port_status & (1 << USB_PORT_FEAT_SUSPEND))
+ == 0) {
+ dum->port_status |=
+ (1 << USB_PORT_FEAT_SUSPEND);
+ if (dum->driver && dum->driver->suspend) {
+ spin_unlock (&dum->lock);
+ dum->driver->suspend (&dum->gadget);
+ spin_lock (&dum->lock);
+ }
}
break;
case USB_PORT_FEAT_RESET:
@@ -1502,7 +1510,7 @@ static int dummy_hub_control (
/* FIXME test that code path! */
}
/* 50msec reset signaling */
- dum->re_timeout = jiffies + ((HZ * 50)/1000);
+ dum->re_timeout = jiffies + msecs_to_jiffies(50);
/* FALLTHROUGH */
default:
dum->port_status |= (1 << wValue);
@@ -1790,4 +1798,3 @@ static void __exit cleanup (void)
the_controller = 0;
}
module_exit (cleanup);
-
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 763d0552146a..e3fec4a5a7f8 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -84,7 +84,7 @@
*/
#define DRIVER_DESC "Ethernet Gadget"
-#define DRIVER_VERSION "St Patrick's Day 2004"
+#define DRIVER_VERSION "Equinox 2004"
static const char shortname [] = "ether";
static const char driver_desc [] = DRIVER_DESC;
@@ -231,6 +231,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
#define DEV_CONFIG_CDC
#endif
+#ifdef CONFIG_USB_GADGET_N9604
+#define DEV_CONFIG_CDC
+#endif
+
/* For CDC-incapable hardware, choose the simple cdc subset.
* Anything that talks bulk (without notable bugs) can do this.
@@ -387,7 +391,7 @@ eth_config = {
.bConfigurationValue = DEV_CONFIG_VALUE,
.iConfiguration = STRING_CDC,
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
- .bMaxPower = 1,
+ .bMaxPower = 50,
};
#ifdef CONFIG_USB_ETH_RNDIS
@@ -401,7 +405,7 @@ rndis_config = {
.bConfigurationValue = DEV_RNDIS_CONFIG_VALUE,
.iConfiguration = STRING_RNDIS,
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
- .bMaxPower = 1,
+ .bMaxPower = 50,
};
#endif
@@ -1198,13 +1202,20 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags)
result = -EINVAL;
/* FALL THROUGH */
case 0:
- return result;
+ break;
}
- if (result)
- eth_reset_config (dev);
- else {
+ if (result) {
+ if (number)
+ eth_reset_config (dev);
+ usb_gadget_vbus_draw(dev->gadget,
+ dev->gadget->is_otg ? 8 : 100);
+ } else {
char *speed;
+ unsigned power;
+
+ power = 2 * eth_config.bMaxPower;
+ usb_gadget_vbus_draw(dev->gadget, power);
switch (gadget->speed) {
case USB_SPEED_FULL: speed = "full"; break;
@@ -1215,8 +1226,8 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags)
}
dev->config = number;
- INFO (dev, "%s speed config #%d: %s, using %s\n",
- speed, number, driver_desc,
+ INFO (dev, "%s speed config #%d: %d mA, %s, using %s\n",
+ speed, number, power, driver_desc,
dev->rndis
? "RNDIS"
: (dev->cdc
@@ -1375,8 +1386,9 @@ static void eth_setup_complete (struct usb_ep *ep, struct usb_request *req)
static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req)
{
if (req->status || req->actual != req->length)
- DEBUG (dev, "rndis response complete --> %d, %d/%d\n",
- req->status, req->actual, req->length);
+ DEBUG ((struct eth_dev *) ep->driver_data,
+ "rndis response complete --> %d, %d/%d\n",
+ req->status, req->actual, req->length);
/* done sending after CDC_GET_ENCAPSULATED_RESPONSE */
}
@@ -2098,11 +2110,13 @@ static void rndis_send_media_state (struct eth_dev *dev, int connect)
}
}
-static void rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req)
+static void
+rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req)
{
if (req->status || req->actual != req->length)
- DEBUG (dev, "rndis control ack complete --> %d, %d/%d\n",
- req->status, req->actual, req->length);
+ DEBUG ((struct eth_dev *) ep->driver_data,
+ "rndis control ack complete --> %d, %d/%d\n",
+ req->status, req->actual, req->length);
usb_ep_free_buffer(ep, req->buf, req->dma, 8);
usb_ep_free_request(ep, req);
@@ -2334,6 +2348,8 @@ eth_bind (struct usb_gadget *gadget)
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208);
} else if (gadget_is_lh7a40x(gadget)) {
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209);
+ } else if (gadget_is_n9604(gadget)) {
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x020a);
} else {
/* can't assume CDC works. don't want to default to
* anything less functional on CDC-capable hardware,
@@ -2466,8 +2482,10 @@ autoconf_fail:
if (gadget->is_otg) {
otg_descriptor.bmAttributes |= USB_OTG_HNP,
eth_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ eth_config.bMaxPower = 4;
#ifdef CONFIG_USB_ETH_RNDIS
rndis_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ rndis_config.bMaxPower = 4;
#endif
}
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index 6e8008fa43cb..d8c8ab7750ec 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -217,6 +217,7 @@
#include <linux/compiler.h>
#include <linux/completion.h>
#include <linux/dcache.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fcntl.h>
#include <linux/file.h>
@@ -234,6 +235,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
+#include <linux/suspend.h>
#include <linux/uts.h>
#include <linux/version.h>
#include <linux/wait.h>
@@ -248,7 +250,7 @@
#define DRIVER_DESC "File-backed Storage Gadget"
#define DRIVER_NAME "g_file_storage"
-#define DRIVER_VERSION "28 July 2004"
+#define DRIVER_VERSION "31 August 2004"
static const char longname[] = DRIVER_DESC;
static const char shortname[] = DRIVER_NAME;
@@ -866,6 +868,14 @@ config_desc = {
.bMaxPower = 1, // self-powered
};
+static struct usb_otg_descriptor
+otg_desc = {
+ .bLength = sizeof(otg_desc),
+ .bDescriptorType = USB_DT_OTG,
+
+ .bmAttributes = USB_OTG_SRP,
+};
+
/* There is only one interface. */
static struct usb_interface_descriptor
@@ -914,12 +924,14 @@ fs_intr_in_desc = {
};
static const struct usb_descriptor_header *fs_function[] = {
+ (struct usb_descriptor_header *) &otg_desc,
(struct usb_descriptor_header *) &intf_desc,
(struct usb_descriptor_header *) &fs_bulk_in_desc,
(struct usb_descriptor_header *) &fs_bulk_out_desc,
(struct usb_descriptor_header *) &fs_intr_in_desc,
NULL,
};
+#define FS_FUNCTION_PRE_EP_ENTRIES 2
#ifdef CONFIG_USB_GADGET_DUALSPEED
@@ -976,12 +988,14 @@ hs_intr_in_desc = {
};
static const struct usb_descriptor_header *hs_function[] = {
+ (struct usb_descriptor_header *) &otg_desc,
(struct usb_descriptor_header *) &intf_desc,
(struct usb_descriptor_header *) &hs_bulk_in_desc,
(struct usb_descriptor_header *) &hs_bulk_out_desc,
(struct usb_descriptor_header *) &hs_intr_in_desc,
NULL,
};
+#define HS_FUNCTION_PRE_EP_ENTRIES 2
/* Maxpacket and other transfer characteristics vary by speed. */
#define ep_desc(g,fs,hs) (((g)->speed==USB_SPEED_HIGH) ? (hs) : (fs))
@@ -1018,9 +1032,10 @@ static struct usb_gadget_strings stringtab = {
* and with code managing interfaces and their altsettings. They must
* also handle different speeds and other-speed requests.
*/
-static int populate_config_buf(enum usb_device_speed speed,
+static int populate_config_buf(struct usb_gadget *gadget,
u8 *buf, u8 type, unsigned index)
{
+ enum usb_device_speed speed = gadget->speed;
int len;
const struct usb_descriptor_header **function;
@@ -1036,6 +1051,10 @@ static int populate_config_buf(enum usb_device_speed speed,
#endif
function = fs_function;
+ /* for now, don't advertise srp-only devices */
+ if (!gadget->is_otg)
+ function++;
+
len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function);
((struct usb_config_descriptor *) buf)->bDescriptorType = type;
return len;
@@ -1366,7 +1385,7 @@ static int standard_setup_req(struct fsg_dev *fsg,
#ifdef CONFIG_USB_GADGET_DUALSPEED
get_config:
#endif
- value = populate_config_buf(fsg->gadget->speed,
+ value = populate_config_buf(fsg->gadget,
req->buf,
ctrl->wValue >> 8,
ctrl->wValue & 0xff);
@@ -1523,6 +1542,8 @@ static int sleep_thread(struct fsg_dev *fsg)
rc = wait_event_interruptible(fsg->thread_wqh,
fsg->thread_wakeup_needed);
fsg->thread_wakeup_needed = 0;
+ if (current->flags & PF_FREEZE)
+ refrigerator(PF_FREEZE);
return (rc ? -EINTR : 0);
}
@@ -2280,8 +2301,7 @@ static int halt_bulk_in_endpoint(struct fsg_dev *fsg)
}
/* Wait for a short time and then try again */
- set_current_state(TASK_INTERRUPTIBLE);
- if (schedule_timeout(HZ / 10) != 0)
+ if (msleep_interruptible(100) != 0)
return -EINTR;
rc = usb_ep_set_halt(fsg->bulk_in);
}
@@ -3713,8 +3733,10 @@ static int __init check_parameters(struct fsg_dev *fsg)
mod_data.release = __constant_cpu_to_le16(0x0307);
else if (gadget_is_omap(fsg->gadget))
mod_data.release = __constant_cpu_to_le16(0x0308);
- else if (gadget_is_lh7a40x(gadget))
+ else if (gadget_is_lh7a40x(fsg->gadget))
mod_data.release = __constant_cpu_to_le16 (0x0309);
+ else if (gadget_is_n9604(fsg->gadget))
+ mod_data.release = __constant_cpu_to_le16 (0x030a);
else {
WARN(fsg, "controller '%s' not recognized\n",
fsg->gadget->name);
@@ -3882,10 +3904,10 @@ static int __init fsg_bind(struct usb_gadget *gadget)
intf_desc.bNumEndpoints = i;
intf_desc.bInterfaceSubClass = mod_data.protocol_type;
intf_desc.bInterfaceProtocol = mod_data.transport_type;
- fs_function[i+1] = NULL;
+ fs_function[i + FS_FUNCTION_PRE_EP_ENTRIES] = NULL;
#ifdef CONFIG_USB_GADGET_DUALSPEED
- hs_function[i+1] = NULL;
+ hs_function[i + HS_FUNCTION_PRE_EP_ENTRIES] = NULL;
/* Assume ep0 uses the same maxpacket value for both speeds */
dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket;
@@ -3896,6 +3918,11 @@ static int __init fsg_bind(struct usb_gadget *gadget)
hs_intr_in_desc.bEndpointAddress = fs_intr_in_desc.bEndpointAddress;
#endif
+ if (gadget->is_otg) {
+ otg_desc.bmAttributes |= USB_OTG_HNP,
+ config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
rc = -ENOMEM;
/* Allocate the request and buffer for endpoint 0 */
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index e24e2f9ad28e..f6273701fec6 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -62,6 +62,12 @@
#define gadget_is_omap(g) 0
#endif
+#ifdef CONFIG_USB_GADGET_N9604
+#define gadget_is_n9604(g) !strcmp("n9604_udc", (g)->name)
+#else
+#define gadget_is_n9604(g) 0
+#endif
+
// CONFIG_USB_GADGET_AT91RM9200
// CONFIG_USB_GADGET_SX2
// CONFIG_USB_GADGET_AU1X00
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index 61ce5b2eea41..f9cdb23da15b 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -1092,13 +1092,7 @@ static inline char *dmastr(void)
return "(dma IN)";
}
-/* if we're trying to save space, don't bother with this proc file */
-
-#if defined(CONFIG_PROC_FS) && !defined(CONFIG_EMBEDDED)
-# define UDC_PROC_FILE
-#endif
-
-#ifdef UDC_PROC_FILE
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static const char proc_node_name [] = "driver/udc";
@@ -1312,7 +1306,7 @@ done:
return count - size;
}
-#endif /* UDC_PROC_FILE */
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
/*-------------------------------------------------------------------------*/
@@ -1815,7 +1809,7 @@ static void goku_remove(struct pci_dev *pdev)
usb_gadget_unregister_driver(dev->driver);
}
-#ifdef UDC_PROC_FILE
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
remove_proc_entry(proc_node_name, NULL);
#endif
if (dev->regs)
@@ -1933,7 +1927,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pci_set_master(pdev);
-#ifdef UDC_PROC_FILE
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev);
#endif
diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c
index 772627e97a00..0def9f70e889 100644
--- a/drivers/usb/gadget/lh7a40x_udc.c
+++ b/drivers/usb/gadget/lh7a40x_udc.c
@@ -54,7 +54,6 @@ static const char ep0name[] = "ep0-control";
/*
Local definintions.
*/
-#define UDC_PROC_FILE
#ifndef NO_STATES
static char *state_names[] = {
@@ -192,7 +191,7 @@ static __inline__ void usb_clear(u32 val, u32 port)
*/
#define is_usb_connected() get_portc_pdr(2)
-#ifdef UDC_PROC_FILE
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static const char proc_node_name[] = "driver/udc";
@@ -248,12 +247,12 @@ udc_proc_read(char *page, char **start, off_t off, int count,
#define create_proc_files() create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)
#define remove_proc_files() remove_proc_entry(proc_node_name, NULL)
-#else /* !UDC_PROC_FILE */
+#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
#define create_proc_files() do {} while (0)
#define remove_proc_files() do {} while (0)
-#endif /* UDC_PROC_FILE */
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
/*
* udc_disable - disable USB device controller
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
index d28de0b8ceba..ed6711d54c32 100644
--- a/drivers/usb/gadget/net2280.c
+++ b/drivers/usb/gadget/net2280.c
@@ -76,7 +76,6 @@
#define EP_DONTUSE 13 /* nonzero */
#define USE_RDK_LEDS /* GPIO pins control three LEDs */
-#define USE_SYSFS_DEBUG_FILES
static const char driver_name [] = "net2280";
@@ -117,7 +116,7 @@ module_param (fifo_mode, ushort, 0644);
#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
-#if defined(USE_SYSFS_DEBUG_FILES) || defined (DEBUG)
+#if defined(CONFIG_USB_GADGET_DEBUG_FILES) || defined (DEBUG)
static char *type_string (u8 bmAttributes)
{
switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) {
@@ -1450,7 +1449,12 @@ static const struct usb_gadget_ops net2280_ops = {
/*-------------------------------------------------------------------------*/
-#ifdef USE_SYSFS_DEBUG_FILES
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+/* FIXME move these into procfs, and use seq_file.
+ * Sysfs _still_ doesn't behave for arbitrarily sized files,
+ * and also doesn't help products using this with 2.4 kernels.
+ */
/* "function" sysfs attribute */
static ssize_t
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index e40089d79365..c321c542f0df 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -1200,7 +1200,8 @@ static void pullup_enable(struct omap_udc *udc)
{
UDC_SYSCON1_REG |= UDC_PULLUP_EN;
#ifndef CONFIG_USB_OTG
- OTG_CTRL_REG |= OTG_BSESSVLD;
+ if (!cpu_is_omap15xx())
+ OTG_CTRL_REG |= OTG_BSESSVLD;
#endif
UDC_IRQ_EN_REG = UDC_DS_CHG_IE;
}
@@ -1208,7 +1209,8 @@ static void pullup_enable(struct omap_udc *udc)
static void pullup_disable(struct omap_udc *udc)
{
#ifndef CONFIG_USB_OTG
- OTG_CTRL_REG &= ~OTG_BSESSVLD;
+ if (!cpu_is_omap15xx())
+ OTG_CTRL_REG &= ~OTG_BSESSVLD;
#endif
UDC_IRQ_EN_REG = UDC_DS_CHG_IE;
UDC_SYSCON1_REG &= ~UDC_PULLUP_EN;
@@ -1688,7 +1690,7 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src)
}
change &= ~UDC_SUS;
}
- if (change & OTG_FLAGS) {
+ if (!cpu_is_omap15xx() && (change & OTG_FLAGS)) {
update_otg(udc);
change &= ~OTG_FLAGS;
}
@@ -1974,7 +1976,7 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver);
/*-------------------------------------------------------------------------*/
-#ifdef CONFIG_USB_OMAP_PROC
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
#include <linux/seq_file.h>
@@ -2036,34 +2038,14 @@ static char *trx_mode(unsigned m)
}
}
-static int proc_udc_show(struct seq_file *s, void *_)
+static int proc_otg_show(struct seq_file *s)
{
u32 tmp;
- struct omap_ep *ep;
- unsigned long flags;
-
- spin_lock_irqsave(&udc->lock, flags);
- seq_printf(s, "%s, version: " DRIVER_VERSION
-#ifdef USE_ISO
- " (iso)"
-#endif
- "%s\n",
- driver_desc,
- use_dma ? " (dma)" : "");
-
- tmp = UDC_REV_REG & 0xff;
- seq_printf(s,
- "UDC rev %d.%d, OTG rev %d.%d, fifo mode %d, gadget %s\n"
- "hmc %d, transceiver %08x %s\n",
+ tmp = OTG_REV_REG;
+ seq_printf(s, "OTG rev %d.%d, transceiver_ctrl %08x\n",
tmp >> 4, tmp & 0xf,
- OTG_REV_REG >> 4, OTG_REV_REG & 0xf,
- fifo_mode,
- udc->driver ? udc->driver->driver.name : "(none)",
- HMC, USB_TRANSCEIVER_CTRL_REG,
- udc->transceiver ? udc->transceiver->label : "");
-
- /* OTG controller registers */
+ USB_TRANSCEIVER_CTRL_REG);
tmp = OTG_SYSCON_1_REG;
seq_printf(s, "otg_syscon1 %08x usb2 %s, usb1 %s, usb0 %s,"
FOURBITS "\n", tmp,
@@ -2117,6 +2099,37 @@ static int proc_udc_show(struct seq_file *s, void *_)
seq_printf(s, "otg_outctrl %04x" "\n", tmp);
tmp = OTG_TEST_REG;
seq_printf(s, "otg_test %04x" "\n", tmp);
+}
+
+static int proc_udc_show(struct seq_file *s, void *_)
+{
+ u32 tmp;
+ struct omap_ep *ep;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ seq_printf(s, "%s, version: " DRIVER_VERSION
+#ifdef USE_ISO
+ " (iso)"
+#endif
+ "%s\n",
+ driver_desc,
+ use_dma ? " (dma)" : "");
+
+ tmp = UDC_REV_REG & 0xff;
+ seq_printf(s,
+ "UDC rev %d.%d, fifo mode %d, gadget %s\n"
+ "hmc %d, transceiver %s\n",
+ tmp >> 4, tmp & 0xf,
+ fifo_mode,
+ udc->driver ? udc->driver->driver.name : "(none)",
+ HMC,
+ udc->transceiver ? udc->transceiver->label : "");
+
+ /* OTG controller registers */
+ if (!cpu_is_omap15xx())
+ proc_otg_show(s);
tmp = UDC_SYSCON1_REG;
seq_printf(s, "\nsyscon1 %04x" EIGHTBITS "\n", tmp,
@@ -2496,41 +2509,51 @@ static int __init omap_udc_probe(struct device *dev)
return -EBUSY;
}
- INFO("OMAP UDC rev %d.%d, OTG rev %d.%d, %s receptacle\n",
+ INFO("OMAP UDC rev %d.%d, %s receptacle\n",
UDC_REV_REG >> 4, UDC_REV_REG & 0xf,
- OTG_REV_REG >> 4, OTG_REV_REG & 0xf,
config->otg ? "Mini-AB" : "B/Mini-B");
/* use the mode given to us by board init code */
- hmc = HMC;
- switch (hmc) {
- case 3:
- case 11:
- case 19:
- case 25:
- xceiv = otg_get_transceiver();
- if (!xceiv) {
- DBG("external transceiver not registered!\n");
- goto cleanup0;
- }
- type = xceiv->label;
- break;
- case 0: /* POWERUP DEFAULT == 0 */
- case 4:
- case 12:
- case 20:
- type = "INTEGRATED";
- break;
- case 21: /* internal loopback */
- type = "(loopback)";
- break;
- case 14: /* transceiverless */
- type = "(none)";
- break;
+ if (cpu_is_omap15xx()) {
+ hmc = HMC_1510;
+ type = "(unknown)";
- default:
- ERR("unrecognized UDC HMC mode %d\n", hmc);
- return -ENODEV;
+ /* FIXME may need a GPIO-0 handler to call
+ * usb_gadget_vbus_{dis,}connect() on us...
+ */
+ } else {
+ hmc = HMC_1610;
+ switch (hmc) {
+ case 3:
+ case 11:
+ case 19:
+ case 25:
+ xceiv = otg_get_transceiver();
+ if (!xceiv) {
+ DBG("external transceiver not registered!\n");
+ if (config->otg)
+ goto cleanup0;
+ type = "(unknown external)";
+ } else
+ type = xceiv->label;
+ break;
+ case 0: /* POWERUP DEFAULT == 0 */
+ case 4:
+ case 12:
+ case 20:
+ type = "INTEGRATED";
+ break;
+ case 21: /* internal loopback */
+ type = "(loopback)";
+ break;
+ case 14: /* transceiverless */
+ type = "(none)";
+ break;
+
+ default:
+ ERR("unrecognized UDC HMC mode %d\n", hmc);
+ return -ENODEV;
+ }
}
INFO("hmc mode %d, transceiver %s\n", hmc, type);
@@ -2671,13 +2694,6 @@ static struct device_driver udc_driver = {
static int __init udc_init(void)
{
- /* should work on many OMAP systems with at most minor changes,
- * but the 1510 doesn't have an OTG controller.
- */
- if (cpu_is_omap1510()) {
- DBG("no OMAP1510 support yet\n");
- return -ENODEV;
- }
INFO("%s, version: " DRIVER_VERSION "%s\n", driver_desc,
use_dma ? " (dma)" : "");
return driver_register(&udc_driver);
diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h
index bd5420cd0b05..ca8572314f95 100644
--- a/drivers/usb/gadget/omap_udc.h
+++ b/drivers/usb/gadget/omap_udc.h
@@ -193,7 +193,14 @@ struct omap_udc {
/*-------------------------------------------------------------------------*/
-// #define HMC_1510 ((MOD_CONF_CTRL_0_REG >> 1) & 0x3f)
+#define MOD_CONF_CTRL_0_REG __REG32(MOD_CONF_CTRL_0)
+#define VBUS_W2FC_1510 (1 << 17) /* 0 gpio0, 1 dvdd2 pin */
+
+#define FUNC_MUX_CTRL_0_REG __REG32(FUNC_MUX_CTRL_0)
+#define VBUS_CTRL_1510 (1 << 19) /* 1 connected (software) */
+#define VBUS_MODE_1510 (1 << 18) /* 0 hardware, 1 software */
+
+#define HMC_1510 ((MOD_CONF_CTRL_0_REG >> 1) & 0x3f)
#define HMC_1610 (OTG_SYSCON_2_REG & 0x3f)
-#define HMC HMC_1610
+#define HMC (cpu_is_omap15xx() ? HMC_1510 : HMC_1610)
diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c
index c1139b51db46..710f7a435f9e 100644
--- a/drivers/usb/gadget/pxa2xx_udc.c
+++ b/drivers/usb/gadget/pxa2xx_udc.c
@@ -92,10 +92,6 @@ static const char ep0name [] = "ep0";
// #define USE_OUT_DMA
// #define DISABLE_TEST_MODE
-#ifdef CONFIG_PROC_FS
-#define UDC_PROC_FILE
-#endif
-
#ifdef CONFIG_ARCH_IXP4XX
#undef USE_DMA
@@ -109,12 +105,6 @@ static const char ep0name [] = "ep0";
#include "pxa2xx_udc.h"
-#ifdef CONFIG_EMBEDDED
-/* few strings, and little code to use them */
-#undef DEBUG
-#undef UDC_PROC_FILE
-#endif
-
#ifdef USE_DMA
static int use_dma = 1;
module_param(use_dma, bool, 0);
@@ -1212,7 +1202,7 @@ static const struct usb_gadget_ops pxa2xx_udc_ops = {
/*-------------------------------------------------------------------------*/
-#ifdef UDC_PROC_FILE
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static const char proc_node_name [] = "driver/udc";
@@ -1368,11 +1358,12 @@ done:
#define remove_proc_files() \
remove_proc_entry(proc_node_name, NULL)
-#else /* !UDC_PROC_FILE */
+#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
+
#define create_proc_files() do {} while (0)
#define remove_proc_files() do {} while (0)
-#endif /* UDC_PROC_FILE */
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
/* "function" sysfs attribute */
static ssize_t
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
index 1cd445a01d13..561ca545e4e5 100644
--- a/drivers/usb/gadget/rndis.c
+++ b/drivers/usb/gadget/rndis.c
@@ -70,8 +70,6 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging");
#define RNDIS_MAX_CONFIGS 1
-static struct proc_dir_entry *rndis_connect_dir;
-static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS];
@@ -1275,6 +1273,8 @@ int rndis_rm_hdr (u8 *buf, u32 *length)
return 0;
}
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof,
void *data)
{
@@ -1365,43 +1365,40 @@ int rndis_proc_write (struct file *file, const char __user *buffer,
return count;
}
+#define NAME_TEMPLATE "driver/rndis-%03d"
+
+static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+
int __init rndis_init (void)
{
u8 i;
- char name [4];
- /* FIXME this should probably be /proc/driver/rndis,
- * and only if debugging is enabled
- */
-
- if (!(rndis_connect_dir = proc_mkdir ("rndis", NULL))) {
- printk (KERN_ERR "%s: couldn't create /proc/rndis entry",
- __FUNCTION__);
- return -EIO;
- }
-
for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
- sprintf (name, "%03d", i);
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+ char name [20];
+
+ sprintf (name, NAME_TEMPLATE, i);
if (!(rndis_connect_state [i]
- = create_proc_entry (name, 0660,
- rndis_connect_dir)))
+ = create_proc_entry (name, 0660, NULL)))
{
DEBUG ("%s :remove entries", __FUNCTION__);
- for (i--; i > 0; i--) {
- sprintf (name, "%03d", i);
- remove_proc_entry (name, rndis_connect_dir);
+ while (i) {
+ sprintf (name, NAME_TEMPLATE, --i);
+ remove_proc_entry (name, NULL);
}
DEBUG ("\n");
-
- remove_proc_entry ("000", rndis_connect_dir);
- remove_proc_entry ("rndis", NULL);
return -EIO;
}
+
rndis_connect_state [i]->nlink = 1;
rndis_connect_state [i]->write_proc = rndis_proc_write;
rndis_connect_state [i]->read_proc = rndis_proc_read;
rndis_connect_state [i]->data = (void *)
(rndis_per_dev_params + i);
+#endif
rndis_per_dev_params [i].confignr = i;
rndis_per_dev_params [i].used = 0;
rndis_per_dev_params [i].state = RNDIS_UNINITIALIZED;
@@ -1415,14 +1412,14 @@ int __init rndis_init (void)
void rndis_exit (void)
{
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
u8 i;
- char name [4];
+ char name [20];
for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
- sprintf (name, "%03d", i);
- remove_proc_entry (name, rndis_connect_dir);
+ sprintf (name, NAME_TEMPLATE, i);
+ remove_proc_entry (name, NULL);
}
- remove_proc_entry ("rndis", NULL);
- return;
+#endif
}
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index a415a33ed117..69962c30a3a9 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -1188,6 +1188,8 @@ autoconf_fail:
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208);
} else if (gadget_is_lh7a40x(gadget)) {
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209);
+ } else if (gadget_is_n9604(gadget)) {
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x020a);
} else {
/* gadget zero is so simple (for now, no altsettings) that
* it SHOULD NOT have problems with bulk-capable hardware.
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index e25adf501967..03d427a672a4 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -155,7 +155,7 @@ MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
* before driver shutdown. But it also seems to be caused by bugs in cardbus
* bridge shutdown: shutting down the bridge before the devices using it.
*/
-static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec)
+static int handshake (void __iomem *ptr, u32 mask, u32 done, int usec)
{
u32 result;
@@ -341,8 +341,7 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
spin_lock_init (&ehci->lock);
ehci->caps = hcd->regs;
- ehci->regs = (hcd->regs +
- HC_LENGTH (readl (&ehci->caps->hc_capbase)));
+ ehci->regs = hcd->regs + HC_LENGTH (readl (&ehci->caps->hc_capbase));
dbg_hcs_params (ehci, "reset");
dbg_hcc_params (ehci, "reset");
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 4ff7f868b157..86872c8877cb 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -81,7 +81,7 @@ static int ehci_hub_suspend (struct usb_hcd *hcd)
}
-/* caller owns root->serialize, and should reset/reinit on error */
+/* caller has locked the root hub, and should reset/reinit on error */
static int ehci_hub_resume (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
diff --git a/drivers/usb/host/hc_sl811.c b/drivers/usb/host/hc_sl811.c
index b57f1fe8258d..baf0e8086352 100644
--- a/drivers/usb/host/hc_sl811.c
+++ b/drivers/usb/host/hc_sl811.c
@@ -1343,15 +1343,11 @@ static int __init hci_hcd_init (void)
*****************************************************************/
static void __exit hci_hcd_cleanup (void)
{
- struct list_head *hci_l;
- hci_t *hci;
+ hci_t *hci, *tmp;
DBGFUNC ("Enter hci_hcd_cleanup\n");
- for (hci_l = hci_hcd_list.next; hci_l != &hci_hcd_list;) {
- hci = list_entry (hci_l, hci_t, hci_hcd_list);
- hci_l = hci_l->next;
+ list_for_each_entry_safe(hci, tmp, &hci_hcd_list, hci_hcd_list)
hc_release_hci (hci);
- }
}
module_init (hci_hcd_init);
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
index 947bf4a5ea03..ff1f80fd59c8 100644
--- a/drivers/usb/host/ohci-dbg.c
+++ b/drivers/usb/host/ohci-dbg.c
@@ -640,14 +640,14 @@ show_registers (struct class_device *class_dev, char *buf)
rdata = ohci_readl (&regs->fminterval);
temp = scnprintf (next, size,
"fmintvl 0x%08x %sFSMPS=0x%04x FI=0x%04x\n",
- rdata, (rdata >> 31) ? " FIT" : "",
+ rdata, (rdata >> 31) ? "FIT " : "",
(rdata >> 16) & 0xefff, rdata & 0xffff);
size -= temp;
next += temp;
rdata = ohci_readl (&regs->fmremaining);
temp = scnprintf (next, size, "fmremaining 0x%08x %sFR=0x%04x\n",
- rdata, (rdata >> 31) ? " FRT" : "",
+ rdata, (rdata >> 31) ? "FRT " : "",
rdata & 0x3fff);
size -= temp;
next += temp;
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 0609e12efeb2..09f37cce2c75 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -2,7 +2,7 @@
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
- * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
*
* [ Initialisation is based on Linus' ]
* [ uhci code and gregs ohci fragments ]
@@ -122,12 +122,27 @@
#define OHCI_INTR_INIT \
(OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
+#ifdef __hppa__
+/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
+#define IR_DISABLE
+#endif
+
+#ifdef CONFIG_ARCH_OMAP
+/* OMAP doesn't support IR (no SMM; not needed) */
+#define IR_DISABLE
+#endif
+
/*-------------------------------------------------------------------------*/
static const char hcd_name [] = "ohci_hcd";
#include "ohci.h"
+static void ohci_dump (struct ohci_hcd *ohci, int verbose);
+static int ohci_init (struct ohci_hcd *ohci);
+static int ohci_restart (struct ohci_hcd *ohci);
+static void ohci_stop (struct usb_hcd *hcd);
+
#include "ohci-hub.c"
#include "ohci-dbg.c"
#include "ohci-mem.c"
@@ -387,30 +402,30 @@ static int ohci_get_frame (struct usb_hcd *hcd)
return OHCI_FRAME_NO(ohci->hcca);
}
+static void ohci_usb_reset (struct ohci_hcd *ohci)
+{
+ ohci->hc_control = ohci_readl (&ohci->regs->control);
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ writel (ohci->hc_control, &ohci->regs->control);
+}
+
/*-------------------------------------------------------------------------*
* HC functions
*-------------------------------------------------------------------------*/
-/* reset the HC and BUS */
+/* init memory, and kick BIOS/SMM off */
-static int hc_reset (struct ohci_hcd *ohci)
+static int ohci_init (struct ohci_hcd *ohci)
{
u32 temp;
+ int ret;
- /* boot firmware should have set this up (5.1.1.3.1) */
- if (!ohci->fminterval) {
- temp = ohci_readl (&ohci->regs->fminterval);
- if (temp & 0x3fff0000)
- ohci->fminterval = temp;
- else
- ohci->fminterval = DEFAULT_FMINTERVAL;
- /* also: power/overcurrent flags in roothub.a */
- }
+ disable (ohci);
+ ohci->regs = ohci->hcd.regs;
+ ohci->next_statechange = jiffies;
- /* SMM owns the HC? not for long!
- * On PA-RISC, PDC can leave IR set incorrectly; ignore it there.
- */
-#ifndef __hppa__
+#ifndef IR_DISABLE
+ /* SMM owns the HC? not for long! */
if (ohci_readl (&ohci->regs->control) & OHCI_CTRL_IR) {
ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n");
@@ -426,27 +441,95 @@ static int hc_reset (struct ohci_hcd *ohci)
msleep (10);
if (--temp == 0) {
ohci_err (ohci, "USB HC TakeOver failed!\n");
- return -1;
+ return -EBUSY;
}
}
+ ohci_usb_reset (ohci);
}
#endif
/* Disable HC interrupts */
writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
+ // flush the writes
+ (void) ohci_readl (&ohci->regs->control);
+
+ if (ohci->hcca)
+ return 0;
+
+ ohci->hcca = dma_alloc_coherent (ohci->hcd.self.controller,
+ sizeof *ohci->hcca, &ohci->hcca_dma, 0);
+ if (!ohci->hcca)
+ return -ENOMEM;
+
+ if ((ret = ohci_mem_init (ohci)) < 0)
+ ohci_stop (&ohci->hcd);
- ohci_dbg (ohci, "reset, control = 0x%x\n",
- ohci_readl (&ohci->regs->control));
+ return ret;
- /* Reset USB (needed by some controllers); RemoteWakeupConnected
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Start an OHCI controller, set the BUS operational
+ * resets USB and controller
+ * enable interrupts
+ * connect the virtual root hub
+ */
+static int ohci_run (struct ohci_hcd *ohci)
+{
+ u32 mask, temp;
+ struct usb_device *udev;
+ struct usb_bus *bus;
+ int first = ohci->fminterval == 0;
+
+ disable (ohci);
+
+ /* boot firmware should have set this up (5.1.1.3.1) */
+ if (first) {
+
+ temp = ohci_readl (&ohci->regs->fminterval);
+ ohci->fminterval = temp & 0x3fff;
+ if (ohci->fminterval != FI)
+ ohci_dbg (ohci, "fminterval delta %d\n",
+ ohci->fminterval - FI);
+ ohci->fminterval |= FSMP (ohci->fminterval) << 16;
+ /* also: power/overcurrent flags in roothub.a */
+ }
+
+ /* Reset USB nearly "by the book". RemoteWakeupConnected
* saved if boot firmware (BIOS/SMM/...) told us it's connected
* (for OHCI integrated on mainboard, it normally is)
*/
ohci->hc_control = ohci_readl (&ohci->regs->control);
- ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */
- if (ohci->hc_control)
+ ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n",
+ hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
+ ohci->hc_control);
+
+ if (ohci->hc_control & OHCI_CTRL_RWC
+ && !(ohci->flags & OHCI_QUIRK_AMD756))
ohci->hcd.can_wakeup = 1;
+
+ switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_OPER:
+ temp = 0;
+ break;
+ case OHCI_USB_SUSPEND:
+ case OHCI_USB_RESUME:
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ ohci->hc_control |= OHCI_USB_RESUME;
+ temp = 10 /* msec wait */;
+ break;
+ // case OHCI_USB_RESET:
+ default:
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ ohci->hc_control |= OHCI_USB_RESET;
+ temp = 50 /* msec wait */;
+ break;
+ }
writel (ohci->hc_control, &ohci->regs->control);
+ // flush the writes
+ (void) ohci_readl (&ohci->regs->control);
+ msleep(temp);
if (power_switching) {
unsigned ports = roothub_a (ohci) & RH_A_NDP;
@@ -455,15 +538,20 @@ static int hc_reset (struct ohci_hcd *ohci)
writel (RH_PS_LSDA,
&ohci->regs->roothub.portstatus [temp]);
}
- // flush those pci writes
+ // flush those writes
(void) ohci_readl (&ohci->regs->control);
- msleep (50);
+ memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
+
+ /* 2msec timelimit here means no irqs/preempt */
+ spin_lock_irq (&ohci->lock);
+retry:
/* HC Reset requires max 10 us delay */
writel (OHCI_HCR, &ohci->regs->cmdstatus);
temp = 30; /* ... allow extra time */
while ((ohci_readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
if (--temp == 0) {
+ spin_unlock_irq (&ohci->lock);
ohci_err (ohci, "USB HC reset timed out!\n");
return -1;
}
@@ -476,27 +564,15 @@ static int hc_reset (struct ohci_hcd *ohci)
* ... but some hardware won't init fmInterval "by the book"
* (SiS, OPTi ...), so reset again instead. SiS doesn't need
* this if we write fmInterval after we're OPERATIONAL.
+ * Unclear about ALi, ServerWorks, and others ... this could
+ * easily be a longstanding bug in chip init on Linux.
*/
- writel (ohci->hc_control, &ohci->regs->control);
- // flush those pci writes
- (void) ohci_readl (&ohci->regs->control);
-
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Start an OHCI controller, set the BUS operational
- * enable interrupts
- * connect the virtual root hub
- */
-static int hc_start (struct ohci_hcd *ohci)
-{
- u32 mask, tmp;
- struct usb_device *udev;
- struct usb_bus *bus;
-
- disable (ohci);
+ if (ohci->flags & OHCI_QUIRK_INITRESET) {
+ writel (ohci->hc_control, &ohci->regs->control);
+ // flush those writes
+ (void) ohci_readl (&ohci->regs->control);
+ }
+ writel (ohci->fminterval, &ohci->regs->fminterval);
/* Tell the controller where the control and bulk lists are
* The lists are empty now. */
@@ -513,7 +589,15 @@ static int hc_start (struct ohci_hcd *ohci)
*/
if ((ohci_readl (&ohci->regs->fminterval) & 0x3fff0000) == 0
|| !ohci_readl (&ohci->regs->periodicstart)) {
- ohci_err (ohci, "init err\n");
+ if (!(ohci->flags & OHCI_QUIRK_INITRESET)) {
+ ohci->flags |= OHCI_QUIRK_INITRESET;
+ ohci_dbg (ohci, "enabling initreset quirk\n");
+ goto retry;
+ }
+ spin_unlock_irq (&ohci->lock);
+ ohci_err (ohci, "init err (%08x %04x)\n",
+ ohci_readl (&ohci->regs->fminterval),
+ ohci_readl (&ohci->regs->periodicstart));
return -EOVERFLOW;
}
@@ -532,42 +616,48 @@ static int hc_start (struct ohci_hcd *ohci)
writel (mask, &ohci->regs->intrenable);
/* handle root hub init quirks ... */
- tmp = roothub_a (ohci);
- tmp &= ~(RH_A_PSM | RH_A_OCPM);
+ temp = roothub_a (ohci);
+ temp &= ~(RH_A_PSM | RH_A_OCPM);
if (ohci->flags & OHCI_QUIRK_SUPERIO) {
/* NSC 87560 and maybe others */
- tmp |= RH_A_NOCP;
- tmp &= ~(RH_A_POTPGT | RH_A_NPS);
+ temp |= RH_A_NOCP;
+ temp &= ~(RH_A_POTPGT | RH_A_NPS);
} else if (power_switching) {
/* act like most external hubs: use per-port power
* switching and overcurrent reporting.
*/
- tmp &= ~(RH_A_NPS | RH_A_NOCP);
- tmp |= RH_A_PSM | RH_A_OCPM;
+ temp &= ~(RH_A_NPS | RH_A_NOCP);
+ temp |= RH_A_PSM | RH_A_OCPM;
} else {
/* hub power always on; required for AMD-756 and some
* Mac platforms. ganged overcurrent reporting, if any.
*/
- tmp |= RH_A_NPS;
+ temp |= RH_A_NPS;
}
- writel (tmp, &ohci->regs->roothub.a);
+ writel (temp, &ohci->regs->roothub.a);
writel (RH_HS_LPSC, &ohci->regs->roothub.status);
writel (power_switching ? RH_B_PPCM : 0, &ohci->regs->roothub.b);
- // flush those pci writes
+ // flush those writes
(void) ohci_readl (&ohci->regs->control);
+ spin_unlock_irq (&ohci->lock);
+
// POTPGT delay is bits 24-31, in 2 ms units.
mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
bus = hcd_to_bus (&ohci->hcd);
+ ohci->hcd.state = USB_STATE_RUNNING;
+
+ ohci_dump (ohci, 1);
- if (bus->root_hub) {
- ohci->hcd.state = USB_STATE_RUNNING;
+ udev = hcd_to_bus (&ohci->hcd)->root_hub;
+ if (udev) {
+ udev->dev.power.power_state = 0;
+ usb_set_device_state (udev, USB_STATE_CONFIGURED);
return 0;
}
/* connect the virtual root hub */
udev = usb_alloc_dev (NULL, bus, 0);
- ohci->hcd.state = USB_STATE_RUNNING;
if (!udev) {
disable (ohci);
ohci->hc_control &= ~OHCI_CTRL_HCFS;
@@ -583,7 +673,10 @@ static int hc_start (struct ohci_hcd *ohci)
writel (ohci->hc_control, &ohci->regs->control);
return -ENODEV;
}
+ if (ohci->power_budget)
+ hub_set_power_budget(udev, ohci->power_budget);
+ create_debug_files (ohci);
return 0;
}
@@ -620,7 +713,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
// e.g. due to PCI Master/Target Abort
ohci_dump (ohci, 1);
- hc_reset (ohci);
+ ohci_usb_reset (ohci);
}
if (ints & OHCI_INTR_RD) {
@@ -655,7 +748,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
if (HCD_IS_RUNNING(ohci->hcd.state)) {
writel (ints, &regs->intrstatus);
writel (OHCI_INTR_MIE, &regs->intrenable);
- // flush those pci writes
+ // flush those writes
(void) ohci_readl (&ohci->regs->control);
}
@@ -674,10 +767,9 @@ static void ohci_stop (struct usb_hcd *hcd)
ohci_dump (ohci, 1);
flush_scheduled_work();
- if (HCD_IS_RUNNING(ohci->hcd.state))
- hc_reset (ohci);
- else
- writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
+
+ ohci_usb_reset (ohci);
+ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
remove_debug_files (ohci);
ohci_mem_cleanup (ohci);
@@ -696,19 +788,7 @@ static void ohci_stop (struct usb_hcd *hcd)
#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
-static void mark_children_gone (struct usb_device *dev)
-{
- unsigned i;
-
- for (i = 0; i < dev->maxchild; i++) {
- if (dev->children [i] == 0)
- continue;
- dev->children [i]->state = USB_STATE_NOTATTACHED;
- mark_children_gone (dev->children [i]);
- }
-}
-
-static int hc_restart (struct ohci_hcd *ohci)
+static int ohci_restart (struct ohci_hcd *ohci)
{
int temp;
int i;
@@ -721,7 +801,7 @@ static int hc_restart (struct ohci_hcd *ohci)
*/
spin_lock_irq(&ohci->lock);
disable (ohci);
- mark_children_gone (ohci->hcd.self.root_hub);
+ usb_set_device_state (ohci->hcd.self.root_hub, USB_STATE_NOTATTACHED);
if (!list_empty (&ohci->pending))
ohci_dbg(ohci, "abort schedule...\n");
list_for_each_entry (priv, &ohci->pending, pending) {
@@ -765,7 +845,7 @@ static int hc_restart (struct ohci_hcd *ohci)
ohci->ed_controltail = NULL;
ohci->ed_bulktail = NULL;
- if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
+ if ((temp = ohci_run (ohci)) < 0) {
ohci_err (ohci, "can't restart, %d\n", temp);
return temp;
} else {
@@ -777,10 +857,7 @@ static int hc_restart (struct ohci_hcd *ohci)
while (i--)
writel (RH_PS_PSS,
&ohci->regs->roothub.portstatus [temp]);
- ohci->hcd.self.root_hub->dev.power.power_state = 0;
- ohci->hcd.state = USB_STATE_RUNNING;
ohci_dbg (ohci, "restart complete\n");
- ohci_dump (ohci, 1);
}
return 0;
}
@@ -810,10 +887,25 @@ MODULE_LICENSE ("GPL");
#include "ohci-lh7a404.c"
#endif
+#ifdef CONFIG_PXA27x
+#include "ohci-pxa27x.c"
+#endif
+
#if !(defined(CONFIG_PCI) \
|| defined(CONFIG_SA1111) \
|| defined(CONFIG_ARCH_OMAP) \
|| defined (CONFIG_ARCH_LH7A404) \
+ || defined (CONFIG_PXA27x) \
)
#error "missing bus glue for ohci-hcd"
#endif
+
+#if !defined(HAVE_HNP) && defined(CONFIG_USB_OTG)
+
+#warning non-OTG configuration, too many HCDs
+
+static void start_hnp(struct ohci_hcd *ohci)
+{
+ /* "can't happen" */
+}
+#endif
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 424971c4bc27..84b133304edf 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -2,7 +2,7 @@
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
- * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under GPL
*/
@@ -11,34 +11,8 @@
/*
* OHCI Root Hub ... the nonsharable stuff
- *
- * Registers don't need cpu_to_le32, that happens transparently
*/
-/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
- * The erratum (#4) description is incorrect. AMD's workaround waits
- * till some bits (mostly reserved) are clear; ok for all revs.
- */
-#define read_roothub(hc, register, mask) ({ \
- u32 temp = ohci_readl (&hc->regs->roothub.register); \
- if (temp == -1) \
- disable (hc); \
- else if (hc->flags & OHCI_QUIRK_AMD756) \
- while (temp & mask) \
- temp = ohci_readl (&hc->regs->roothub.register); \
- temp; })
-
-static u32 roothub_a (struct ohci_hcd *hc)
- { return read_roothub (hc, a, 0xfc0fe000); }
-static inline u32 roothub_b (struct ohci_hcd *hc)
- { return ohci_readl (&hc->regs->roothub.b); }
-static inline u32 roothub_status (struct ohci_hcd *hc)
- { return ohci_readl (&hc->regs->roothub.status); }
-static u32 roothub_portstatus (struct ohci_hcd *hc, int i)
- { return read_roothub (hc, portstatus [i], 0xffe0fce0); }
-
-/*-------------------------------------------------------------------------*/
-
#define dbg_port(hc,label,num,value) \
ohci_dbg (hc, \
"%s roothub.portstatus [%d] " \
@@ -146,10 +120,11 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
ohci->next_statechange = jiffies + msecs_to_jiffies (5);
succeed:
- /* it's not USB_STATE_SUSPENDED unless access to this
+ /* it's not HCD_STATE_SUSPENDED unless access to this
* hub from the non-usb side (PCI, SOC, etc) stopped
*/
root->dev.power.power_state = 3;
+ usb_set_device_state (root, USB_STATE_SUSPENDED);
done:
spin_unlock_irq (&ohci->lock);
return status;
@@ -163,9 +138,7 @@ static inline struct ed *find_head (struct ed *ed)
return ed;
}
-static int hc_restart (struct ohci_hcd *ohci);
-
-/* caller owns root->serialize */
+/* caller has locked the root hub */
static int ohci_hub_resume (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
@@ -180,7 +153,12 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
spin_lock_irq (&ohci->lock);
ohci->hc_control = ohci_readl (&ohci->regs->control);
- switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+ if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
+ /* this can happen after suspend-to-disk */
+ ohci_dbg (ohci, "BIOS/SMM active, control %03x\n",
+ ohci->hc_control);
+ status = -EBUSY;
+ } else switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_SUSPEND:
ohci->hc_control &= ~(OHCI_CTRL_HCFS|OHCI_SCHED_ENABLES);
ohci->hc_control |= OHCI_USB_RESUME;
@@ -202,8 +180,10 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
status = -EBUSY;
}
spin_unlock_irq (&ohci->lock);
- if (status == -EBUSY)
- return hc_restart (ohci);
+ if (status == -EBUSY) {
+ (void) ohci_init (ohci);
+ return ohci_restart (ohci);
+ }
if (status != -EINPROGRESS)
return status;
@@ -260,6 +240,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
/* TRSMRCY */
msleep (10);
root->dev.power.power_state = 0;
+ usb_set_device_state (root, USB_STATE_CONFIGURED);
/* keep it alive for ~5x suspend + resume costs */
ohci->next_statechange = jiffies + msecs_to_jiffies (250);
@@ -289,7 +270,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
ohci->hc_control |= enables;
writel (ohci->hc_control, &ohci->regs->control);
if (temp)
- writel (status, &ohci->regs->cmdstatus);
+ writel (temp, &ohci->regs->cmdstatus);
(void) ohci_readl (&ohci->regs->control);
}
@@ -301,9 +282,9 @@ static void ohci_rh_resume (void *_hcd)
{
struct usb_hcd *hcd = _hcd;
- down (&hcd->self.root_hub->serialize);
+ usb_lock_device (hcd->self.root_hub);
(void) ohci_hub_resume (hcd);
- up (&hcd->self.root_hub->serialize);
+ usb_unlock_device (hcd->self.root_hub);
}
#else
@@ -381,12 +362,12 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
&& ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
& ohci->hc_control)
== OHCI_USB_OPER
- && down_trylock (&hcd->self.root_hub->serialize) == 0
+ && usb_trylock_device (hcd->self.root_hub)
) {
ohci_vdbg (ohci, "autosuspend\n");
(void) ohci_hub_suspend (&ohci->hcd);
ohci->hcd.state = USB_STATE_RUNNING;
- up (&hcd->self.root_hub->serialize);
+ usb_unlock_device (hcd->self.root_hub);
}
#endif
@@ -481,8 +462,8 @@ static void start_hnp(struct ohci_hcd *ohci);
/* this timer value might be vendor-specific ... */
#define PORT_RESET_HW_MSEC 10
-/* wrap-aware logic stolen from <linux/jiffies.h> */
-#define tick_before(t1,t2) ((((s16)(t1))-((s16)(t2))) < 0)
+/* wrap-aware logic morphed from <linux/jiffies.h> */
+#define tick_before(t1,t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0)
/* called from some task, normally khubd */
static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port)
diff --git a/drivers/usb/host/ohci-lh7a404.c b/drivers/usb/host/ohci-lh7a404.c
index 4e11a8eae481..1594d1e635da 100644
--- a/drivers/usb/host/ohci-lh7a404.c
+++ b/drivers/usb/host/ohci-lh7a404.c
@@ -229,38 +229,14 @@ ohci_lh7a404_start (struct usb_hcd *hcd)
int ret;
ohci_dbg (ohci, "ohci_lh7a404_start, ohci:%p", ohci);
-
- ohci->hcca = dma_alloc_coherent (hcd->self.controller,
- sizeof *ohci->hcca, &ohci->hcca_dma, 0);
- if (!ohci->hcca)
- return -ENOMEM;
-
- ohci_dbg (ohci, "ohci_lh7a404_start, ohci->hcca:%p",
- ohci->hcca);
-
- memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
-
- if ((ret = ohci_mem_init (ohci)) < 0) {
- ohci_stop (hcd);
+ if ((ret = ohci_init(ohci)) < 0)
return ret;
- }
- ohci->regs = hcd->regs;
-
- if (hc_reset (ohci) < 0) {
- ohci_stop (hcd);
- return -ENODEV;
- }
- if (hc_start (ohci) < 0) {
+ if ((ret = ohci_run (ohci)) < 0) {
err ("can't start %s", ohci->hcd.self.bus_name);
ohci_stop (hcd);
- return -EBUSY;
+ return ret;
}
- create_debug_files (ohci);
-
-#ifdef DEBUG
- ohci_dump (ohci, 1);
-#endif /*DEBUG*/
return 0;
}
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index d133ff22a4a7..a8e641f595bb 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -428,40 +428,18 @@ ohci_omap_start (struct usb_hcd *hcd)
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
- config = hcd->self.controller->platform_data;
- ohci->hcca = dma_alloc_coherent (hcd->self.controller,
- sizeof *ohci->hcca, &ohci->hcca_dma, 0);
- if (!ohci->hcca)
- return -ENOMEM;
-
- memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
- if ((ret = ohci_mem_init (ohci)) < 0) {
- ohci_stop (hcd);
+ if ((ret = ohci_init(ohci)) < 0)
return ret;
- }
- ohci->regs = hcd->regs;
+ config = hcd->self.controller->platform_data;
if (config->otg || config->rwc)
writel(OHCI_CTRL_RWC, &ohci->regs->control);
- if (hc_reset (ohci) < 0) {
- ohci_stop (hcd);
- return -ENODEV;
- }
-
- if (hc_start (ohci) < 0) {
+ if ((ret = ohci_run (ohci)) < 0) {
err ("can't start %s", ohci->hcd.self.bus_name);
ohci_stop (hcd);
- return -EBUSY;
+ return ret;
}
- if (ohci->power_budget)
- hub_set_power_budget(ohci->hcd.self.root_hub,
- ohci->power_budget);
- create_debug_files (ohci);
-
-#ifdef DEBUG
- ohci_dump (ohci, 1);
-#endif
return 0;
}
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index 73d9aee657d5..2211a69e0b9d 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -35,9 +35,7 @@ ohci_pci_reset (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
- ohci->regs = hcd->regs;
- ohci->next_statechange = jiffies;
- return hc_reset (ohci);
+ return ohci_init (ohci);
}
static int __devinit
@@ -46,11 +44,6 @@ ohci_pci_start (struct usb_hcd *hcd)
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
- ohci->hcca = dma_alloc_coherent (hcd->self.controller,
- sizeof *ohci->hcca, &ohci->hcca_dma, 0);
- if (!ohci->hcca)
- return -ENOMEM;
-
if(hcd->self.controller && hcd->self.controller->bus == &pci_bus_type) {
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
@@ -61,6 +54,7 @@ ohci_pci_start (struct usb_hcd *hcd)
&& pdev->device == 0x740c) {
ohci->flags = OHCI_QUIRK_AMD756;
ohci_info (ohci, "AMD756 erratum 4 workaround\n");
+ // also somewhat erratum 10 (suspend/resume issues)
}
/* FIXME for some of the early AMD 760 southbridges, OHCI
@@ -92,25 +86,16 @@ ohci_pci_start (struct usb_hcd *hcd)
ohci_info (ohci, "Using NSC SuperIO setup\n");
}
}
-
- }
-
- memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
- if ((ret = ohci_mem_init (ohci)) < 0) {
- ohci_stop (hcd);
- return ret;
}
- if (hc_start (ohci) < 0) {
+ /* NOTE: there may have already been a first reset, to
+ * keep bios/smm irqs from making trouble
+ */
+ if ((ret = ohci_run (ohci)) < 0) {
ohci_err (ohci, "can't start\n");
ohci_stop (hcd);
- return -EBUSY;
+ return ret;
}
- create_debug_files (ohci);
-
-#ifdef DEBUG
- ohci_dump (ohci, 1);
-#endif
return 0;
}
@@ -127,9 +112,9 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state)
#ifdef CONFIG_USB_SUSPEND
(void) usb_suspend_device (hcd->self.root_hub, state);
#else
- down (&hcd->self.root_hub->serialize);
+ usb_lock_device (hcd->self.root_hub);
(void) ohci_hub_suspend (hcd);
- up (&hcd->self.root_hub->serialize);
+ usb_unlock_device (hcd->self.root_hub);
#endif
/* let things settle down a bit */
@@ -175,9 +160,9 @@ static int ohci_pci_resume (struct usb_hcd *hcd)
/* get extra cleanup even if remote wakeup isn't in use */
retval = usb_resume_device (hcd->self.root_hub);
#else
- down (&hcd->self.root_hub->serialize);
+ usb_lock_device (hcd->self.root_hub);
retval = ohci_hub_resume (hcd);
- up (&hcd->self.root_hub->serialize);
+ usb_unlock_device (hcd->self.root_hub);
#endif
if (retval == 0) {
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
new file mode 100644
index 000000000000..4bfe74123373
--- /dev/null
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -0,0 +1,460 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ *
+ * Bus Glue for pxa27x
+ *
+ * Written by Christopher Hoover <ch@hpl.hp.com>
+ * Based on fragments of previous driver by Russell King et al.
+ *
+ * Modified for LH7A404 from ohci-sa1111.c
+ * by Durgesh Pattamatta <pattamattad@sharpsec.com>
+ *
+ * Modified for pxa27x from ohci-lh7a404.c
+ * by Nick Bane <nick@cecomputing.co.uk> 26-8-2004
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/device.h>
+#include <asm/mach-types.h>
+#include <asm/hardware.h>
+
+
+#define PMM_NPS_MODE 1
+#define PMM_GLOBAL_MODE 2
+#define PMM_PERPORT_MODE 3
+
+#define PXA_UHC_MAX_PORTNUM 3
+
+#define UHCRHPS(x) __REG2( 0x4C000050, (x)<<2 )
+
+static int pxa27x_ohci_pmm_state;
+
+/*
+ PMM_NPS_MODE -- PMM Non-power switching mode
+ Ports are powered continuously.
+
+ PMM_GLOBAL_MODE -- PMM global switching mode
+ All ports are powered at the same time.
+
+ PMM_PERPORT_MODE -- PMM per port switching mode
+ Ports are powered individually.
+ */
+static int pxa27x_ohci_select_pmm( int mode )
+{
+ pxa27x_ohci_pmm_state = mode;
+
+ switch ( mode ) {
+ case PMM_NPS_MODE:
+ UHCRHDA |= RH_A_NPS;
+ break;
+ case PMM_GLOBAL_MODE:
+ UHCRHDA &= ~(RH_A_NPS & RH_A_PSM);
+ break;
+ case PMM_PERPORT_MODE:
+ UHCRHDA &= ~(RH_A_NPS);
+ UHCRHDA |= RH_A_PSM;
+
+ /* Set port power control mask bits, only 3 ports. */
+ UHCRHDB |= (0x7<<17);
+ break;
+ default:
+ printk( KERN_ERR
+ "Invalid mode %d, set to non-power switch mode.\n",
+ mode );
+
+ pxa27x_ohci_pmm_state = PMM_NPS_MODE;
+ UHCRHDA |= RH_A_NPS;
+ }
+
+ return 0;
+}
+
+/*
+ If you select PMM_PERPORT_MODE, you should set the port power
+ */
+static int pxa27x_ohci_set_port_power( int port )
+{
+ if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE)
+ && (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) {
+ UHCRHPS(port) |= 0x100;
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ If you select PMM_PERPORT_MODE, you should set the port power
+ */
+static int pxa27x_ohci_clear_port_power( int port )
+{
+ if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE)
+ && (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) {
+ UHCRHPS(port) |= 0x200;
+ return 0;
+ }
+
+ return -1;
+}
+
+extern int usb_disabled(void);
+
+/*-------------------------------------------------------------------------*/
+
+static void pxa27x_start_hc(struct platform_device *dev)
+{
+ pxa_set_cken(CKEN10_USBHOST, 1);
+
+ UHCHR |= UHCHR_FHR;
+ udelay(11);
+ UHCHR &= ~UHCHR_FHR;
+
+ UHCHR |= UHCHR_FSBIR;
+ while (UHCHR & UHCHR_FSBIR)
+ cpu_relax();
+
+ /* This could be properly abstracted away through the
+ device data the day more machines are supported and
+ their differences can be figured out correctly. */
+ if (machine_is_mainstone()) {
+ /* setup Port1 GPIO pin. */
+ pxa_gpio_mode( 88 | GPIO_ALT_FN_1_IN); /* USBHPWR1 */
+ pxa_gpio_mode( 89 | GPIO_ALT_FN_2_OUT); /* USBHPEN1 */
+
+ /* Set the Power Control Polarity Low and Power Sense
+ Polarity Low to active low. Supply power to USB ports. */
+ UHCHR = (UHCHR | UHCHR_PCPL | UHCHR_PSPL) &
+ ~(UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSEP3 | UHCHR_SSE);
+ }
+
+ UHCHR &= ~UHCHR_SSE;
+
+ UHCHIE = (UHCHIE_UPRIE | UHCHIE_RWIE);
+}
+
+static void pxa27x_stop_hc(struct platform_device *dev)
+{
+ UHCHR |= UHCHR_FHR;
+ udelay(11);
+ UHCHR &= ~UHCHR_FHR;
+
+ UHCCOMS |= 1;
+ udelay(10);
+
+ pxa_set_cken(CKEN10_USBHOST, 0);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+void usb_hcd_pxa27x_remove (struct usb_hcd *, struct platform_device *);
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+
+/**
+ * usb_hcd_pxa27x_probe - initialize pxa27x-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+int usb_hcd_pxa27x_probe (const struct hc_driver *driver,
+ struct usb_hcd **hcd_out,
+ struct platform_device *dev)
+{
+ int retval;
+ struct usb_hcd *hcd = 0;
+
+ unsigned int *addr = NULL;
+
+ if (!request_mem_region(dev->resource[0].start,
+ dev->resource[0].end
+ - dev->resource[0].start + 1, hcd_name)) {
+ pr_debug("request_mem_region failed");
+ return -EBUSY;
+ }
+
+ pxa27x_start_hc(dev);
+
+ /* Select Power Management Mode */
+ pxa27x_ohci_select_pmm( PMM_PERPORT_MODE );
+
+ /* If choosing PMM_PERPORT_MODE, we should set the port power before we use it. */
+ if (pxa27x_ohci_set_port_power(1) < 0)
+ printk(KERN_ERR "Setting port 1 power failed.\n");
+
+ if (pxa27x_ohci_clear_port_power(2) < 0)
+ printk(KERN_ERR "Setting port 2 power failed.\n");
+
+ if (pxa27x_ohci_clear_port_power(3) < 0)
+ printk(KERN_ERR "Setting port 3 power failed.\n");
+
+ addr = ioremap(dev->resource[0].start,
+ dev->resource[0].end - dev->resource[0].start + 1);
+ if (!addr) {
+ pr_debug("ioremap failed");
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ hcd = driver->hcd_alloc ();
+ if (hcd == NULL){
+ pr_debug ("hcd_alloc failed");
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ if(dev->resource[1].flags != IORESOURCE_IRQ){
+ pr_debug ("resource[1] is not IORESOURCE_IRQ");
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ hcd->driver = (struct hc_driver *) driver;
+ hcd->description = driver->description;
+ hcd->irq = dev->resource[1].start;
+ hcd->regs = addr;
+ hcd->self.controller = &dev->dev;
+
+ retval = hcd_buffer_create (hcd);
+ if (retval != 0) {
+ pr_debug ("pool alloc fail");
+ goto err1;
+ }
+
+ retval = request_irq (hcd->irq, usb_hcd_irq, SA_INTERRUPT,
+ hcd->description, hcd);
+ if (retval != 0) {
+ pr_debug("request_irq(%d) failed with retval %d\n",hcd->irq,retval);
+ retval = -EBUSY;
+ goto err2;
+ }
+
+ pr_debug ("%s (pxa27x) at 0x%p, irq %d",
+ hcd->description, hcd->regs, hcd->irq);
+
+ usb_bus_init (&hcd->self);
+ hcd->self.op = &usb_hcd_operations;
+ hcd->self.hcpriv = (void *) hcd;
+ hcd->self.bus_name = "pxa27x";
+ hcd->product_desc = "PXA27x OHCI";
+
+ INIT_LIST_HEAD (&hcd->dev_list);
+
+ usb_register_bus (&hcd->self);
+
+ if ((retval = driver->start (hcd)) < 0) {
+ usb_hcd_pxa27x_remove(hcd, dev);
+ return retval;
+ }
+
+ *hcd_out = hcd;
+ return 0;
+
+ err2:
+ hcd_buffer_destroy (hcd);
+ if (hcd)
+ driver->hcd_free(hcd);
+ err1:
+ pxa27x_stop_hc(dev);
+ release_mem_region(dev->resource[0].start,
+ dev->resource[0].end
+ - dev->resource[0].start + 1);
+ return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_pxa27x_remove - shutdown processing for pxa27x-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_pxa27x_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *dev)
+{
+ void *base;
+
+ pr_debug ("remove: %s, state %x", hcd->self.bus_name, hcd->state);
+
+ if (in_interrupt ())
+ BUG ();
+
+ hcd->state = USB_STATE_QUIESCING;
+
+ pr_debug ("%s: roothub graceful disconnect", hcd->self.bus_name);
+ usb_disconnect (&hcd->self.root_hub);
+
+ hcd->driver->stop (hcd);
+ hcd->state = USB_STATE_HALT;
+
+ free_irq (hcd->irq, hcd);
+ hcd_buffer_destroy (hcd);
+
+ usb_deregister_bus (&hcd->self);
+
+ base = hcd->regs;
+ hcd->driver->hcd_free (hcd);
+
+ pxa27x_stop_hc(dev);
+ release_mem_region(dev->resource[0].start,
+ dev->resource[0].end - dev->resource[0].start + 1);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __devinit
+ohci_pxa27x_start (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ret;
+
+ ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci);
+
+ if ((ret = ohci_init(ohci)) < 0)
+ return ret;
+
+ if ((ret = ohci_run (ohci)) < 0) {
+ err ("can't start %s", ohci->hcd.self.bus_name);
+ ohci_stop (hcd);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ohci_pxa27x_hc_driver = {
+ .description = hcd_name,
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11,
+
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_pxa27x_start,
+ .stop = ohci_stop,
+
+ /*
+ * memory lifecycle (except per-request)
+ */
+ .hcd_alloc = ohci_hcd_alloc,
+ .hcd_free = ohci_hcd_free,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+#ifdef CONFIG_USB_SUSPEND
+ .hub_suspend = ohci_hub_suspend,
+ .hub_resume = ohci_hub_resume,
+#endif
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hcd_pxa27x_drv_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = NULL;
+ int ret;
+
+ pr_debug ("In ohci_hcd_pxa27x_drv_probe");
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ ret = usb_hcd_pxa27x_probe(&ohci_pxa27x_hc_driver, &hcd, pdev);
+
+ if (ret == 0)
+ dev_set_drvdata(dev, hcd);
+
+ return ret;
+}
+
+static int ohci_hcd_pxa27x_drv_remove(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ usb_hcd_pxa27x_remove(hcd, pdev);
+ dev_set_drvdata(dev, NULL);
+ return 0;
+}
+
+static int ohci_hcd_pxa27x_drv_suspend(struct device *dev, u32 state, u32 level)
+{
+// struct platform_device *pdev = to_platform_device(dev);
+// struct usb_hcd *hcd = dev_get_drvdata(dev);
+ printk("%s: not implemented yet\n", __FUNCTION__);
+
+ return 0;
+}
+
+static int ohci_hcd_pxa27x_drv_resume(struct device *dev, u32 state)
+{
+// struct platform_device *pdev = to_platform_device(dev);
+// struct usb_hcd *hcd = dev_get_drvdata(dev);
+ printk("%s: not implemented yet\n", __FUNCTION__);
+
+ return 0;
+}
+
+
+static struct device_driver ohci_hcd_pxa27x_driver = {
+ .name = "pxa27x-ohci",
+ .bus = &platform_bus_type,
+ .probe = ohci_hcd_pxa27x_drv_probe,
+ .remove = ohci_hcd_pxa27x_drv_remove,
+ .suspend = ohci_hcd_pxa27x_drv_suspend,
+ .resume = ohci_hcd_pxa27x_drv_resume,
+};
+
+static int __init ohci_hcd_pxa27x_init (void)
+{
+ pr_debug (DRIVER_INFO " (pxa27x)");
+ pr_debug ("block sizes: ed %d td %d\n",
+ sizeof (struct ed), sizeof (struct td));
+
+ return driver_register(&ohci_hcd_pxa27x_driver);
+}
+
+static void __exit ohci_hcd_pxa27x_cleanup (void)
+{
+ driver_unregister(&ohci_hcd_pxa27x_driver);
+}
+
+module_init (ohci_hcd_pxa27x_init);
+module_exit (ohci_hcd_pxa27x_cleanup);
diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c
index c3cd76aba163..35d71aceff63 100644
--- a/drivers/usb/host/ohci-sa1111.c
+++ b/drivers/usb/host/ohci-sa1111.c
@@ -272,33 +272,14 @@ ohci_sa1111_start (struct usb_hcd *hcd)
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
- ohci->hcca = dma_alloc_coherent (hcd->self.controller,
- sizeof *ohci->hcca, &ohci->hcca_dma, 0);
- if (!ohci->hcca)
- return -ENOMEM;
-
- memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
- if ((ret = ohci_mem_init (ohci)) < 0) {
- ohci_stop (hcd);
+ if ((ret = ohci_init(ohci)) < 0)
return ret;
- }
- ohci->regs = hcd->regs;
-
- if (hc_reset (ohci) < 0) {
- ohci_stop (hcd);
- return -ENODEV;
- }
- if (hc_start (ohci) < 0) {
+ if ((ret = ohci_run (ohci)) < 0) {
err ("can't start %s", ohci->hcd.self.bus_name);
ohci_stop (hcd);
- return -EBUSY;
+ return ret;
}
- create_debug_files (ohci);
-
-#ifdef DEBUG
- ohci_dump (ohci, 1);
-#endif
return 0;
}
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index 19436cdd7a11..b2756dee9507 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -42,7 +42,6 @@ struct ed {
/* create --> IDLE --> OPER --> ... --> IDLE --> destroy
* usually: OPER --> UNLINK --> (IDLE | OPER) --> ...
- * some special cases : OPER --> IDLE ...
*/
u8 state; /* ED_{IDLE,UNLINK,OPER} */
#define ED_IDLE 0x00 /* NOT linked to HC */
@@ -387,6 +386,7 @@ struct ohci_hcd {
unsigned long flags; /* for HC bugs */
#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
#define OHCI_QUIRK_SUPERIO 0x02 /* natsemi */
+#define OHCI_QUIRK_INITRESET 0x04 /* SiS, OPTi, ... */
// there are also chip quirks/bugs in init logic
/*
@@ -405,14 +405,14 @@ static inline void disable (struct ohci_hcd *ohci)
}
#define FI 0x2edf /* 12000 bits per frame (-1) */
-#define DEFAULT_FMINTERVAL ((((6 * (FI - 210)) / 7) << 16) | FI)
+#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7))
#define LSTHRESH 0x628 /* lowspeed bit threshold */
static inline void periodic_reinit (struct ohci_hcd *ohci)
{
- writel (ohci->fminterval, &ohci->regs->fminterval);
- writel (((9 * FI) / 10) & 0x3fff, &ohci->regs->periodicstart);
- writel (LSTHRESH, &ohci->regs->lsthresh);
+ u32 fi = ohci->fminterval & 0x0ffff;
+
+ writel (((9 * fi) / 10) & 0x3fff, &ohci->regs->periodicstart);
}
/*-------------------------------------------------------------------------*/
@@ -436,6 +436,8 @@ static inline void periodic_reinit (struct ohci_hcd *ohci)
# define ohci_vdbg(ohci, fmt, args...) do { } while (0)
#endif
+/*-------------------------------------------------------------------------*/
+
#ifdef CONFIG_ARCH_LH7A404
/* Marc Singer: at the time this code was written, the LH7A404
* had a problem reading the USB host registers. This
@@ -455,3 +457,25 @@ static inline unsigned int ohci_readl (void __iomem * regs)
return readl (regs);
}
#endif
+
+/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
+ * The erratum (#4) description is incorrect. AMD's workaround waits
+ * till some bits (mostly reserved) are clear; ok for all revs.
+ */
+#define read_roothub(hc, register, mask) ({ \
+ u32 temp = ohci_readl (&hc->regs->roothub.register); \
+ if (temp == -1) \
+ disable (hc); \
+ else if (hc->flags & OHCI_QUIRK_AMD756) \
+ while (temp & mask) \
+ temp = ohci_readl (&hc->regs->roothub.register); \
+ temp; })
+
+static u32 roothub_a (struct ohci_hcd *hc)
+ { return read_roothub (hc, a, 0xfc0fe000); }
+static inline u32 roothub_b (struct ohci_hcd *hc)
+ { return ohci_readl (&hc->regs->roothub.b); }
+static inline u32 roothub_status (struct ohci_hcd *hc)
+ { return ohci_readl (&hc->regs->roothub.status); }
+static u32 roothub_portstatus (struct ohci_hcd *hc, int i)
+ { return read_roothub (hc, portstatus [i], 0xffe0fce0); }
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 20a42f914684..99b706b64052 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -230,42 +230,22 @@ static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td)
}
/*
- * Inserts a td into qh list at the top.
+ * Inserts a td list into qh.
*/
static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, __le32 breadth)
{
- struct list_head *tmp, *head;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct uhci_td *td, *ptd;
-
- if (list_empty(&urbp->td_list))
- return;
-
- head = &urbp->td_list;
- tmp = head->next;
+ struct uhci_td *td;
+ u32 *plink;
/* Ordering isn't important here yet since the QH hasn't been */
- /* inserted into the schedule yet */
- td = list_entry(tmp, struct uhci_td, list);
-
- /* Add the first TD to the QH element pointer */
- qh->element = cpu_to_le32(td->dma_handle) | breadth;
-
- ptd = td;
-
- /* Then link the rest of the TD's */
- tmp = tmp->next;
- while (tmp != head) {
- td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
-
- ptd->link = cpu_to_le32(td->dma_handle) | breadth;
-
- ptd = td;
+ /* inserted into the schedule yet */
+ plink = &qh->element;
+ list_for_each_entry(td, &urbp->td_list, list) {
+ *plink = cpu_to_le32(td->dma_handle) | breadth;
+ plink = &td->link;
}
-
- ptd->link = UHCI_PTR_TERM;
+ *plink = UHCI_PTR_TERM;
}
static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
@@ -330,7 +310,7 @@ static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct list_head *tmp;
+ struct urb_priv *turbp;
struct uhci_qh *lqh;
/* Grab the last QH */
@@ -358,12 +338,8 @@ static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct
*/
lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
if (lqh->urbp) {
- list_for_each (tmp, &lqh->urbp->queue_list) {
- struct urb_priv *turbp =
- list_entry(tmp, struct urb_priv, queue_list);
-
+ list_for_each_entry(turbp, &lqh->urbp->queue_list, queue_list)
turbp->qh->link = lqh->link;
- }
}
list_add_tail(&urbp->qh->list, &skelqh->list);
@@ -405,18 +381,11 @@ static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
pqh = list_entry(qh->list.prev, struct uhci_qh, list);
pqh->link = newlink;
if (pqh->urbp) {
- struct list_head *head, *tmp;
-
- head = &pqh->urbp->queue_list;
- tmp = head->next;
- while (head != tmp) {
- struct urb_priv *turbp =
- list_entry(tmp, struct urb_priv, queue_list);
-
- tmp = tmp->next;
+ struct urb_priv *turbp;
+ list_for_each_entry(turbp, &pqh->urbp->queue_list,
+ queue_list)
turbp->qh->link = newlink;
- }
}
wmb();
@@ -447,21 +416,14 @@ static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct list_head *head, *tmp;
-
- head = &urbp->td_list;
- tmp = head->next;
- while (head != tmp) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
+ struct uhci_td *td;
+ list_for_each_entry(td, &urbp->td_list, list) {
if (toggle)
td->token |= cpu_to_le32(TD_TOKEN_TOGGLE);
else
td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE);
-
toggle ^= 1;
}
@@ -473,30 +435,19 @@ static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb)
{
struct urb_priv *eurbp, *urbp, *furbp, *lurbp;
- struct list_head *tmp;
struct uhci_td *lltd;
eurbp = eurb->hcpriv;
urbp = urb->hcpriv;
/* Find the first URB in the queue */
+ furbp = eurbp;
if (eurbp->queued) {
- struct list_head *head = &eurbp->queue_list;
-
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *turbp =
- list_entry(tmp, struct urb_priv, queue_list);
-
- if (!turbp->queued)
+ list_for_each_entry(furbp, &eurbp->queue_list, queue_list)
+ if (!furbp->queued)
break;
+ }
- tmp = tmp->next;
- }
- } else
- tmp = &eurbp->queue_list;
-
- furbp = list_entry(tmp, struct urb_priv, queue_list);
lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list);
lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
@@ -522,9 +473,7 @@ static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, stru
static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb)
{
- struct urb_priv *urbp, *nurbp;
- struct list_head *head, *tmp;
- struct urb_priv *purbp;
+ struct urb_priv *urbp, *nurbp, *purbp, *turbp;
struct uhci_td *pltd;
unsigned int toggle;
@@ -556,14 +505,7 @@ static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb)
toggle = uhci_toggle(td_token(pltd)) ^ 1;
}
- head = &urbp->queue_list;
- tmp = head->next;
- while (head != tmp) {
- struct urb_priv *turbp;
-
- turbp = list_entry(tmp, struct urb_priv, queue_list);
- tmp = tmp->next;
-
+ list_for_each_entry(turbp, &urbp->queue_list, queue_list) {
if (!turbp->queued)
break;
toggle = uhci_fixup_toggle(turbp->urb, toggle);
@@ -637,7 +579,7 @@ static void uhci_remove_td_from_urb(struct uhci_td *td)
static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
{
- struct list_head *head, *tmp;
+ struct uhci_td *td, *tmp;
struct urb_priv *urbp;
unsigned int age;
@@ -660,13 +602,7 @@ static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
if (list_empty(&uhci->td_remove_list))
uhci_set_next_interrupt(uhci);
- head = &urbp->td_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
-
+ list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
uhci_remove_td_from_urb(td);
uhci_remove_td(uhci, td);
list_add(&td->remove_list, &uhci->td_remove_list);
@@ -1083,7 +1019,6 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb
*/
static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
{
- struct list_head *tmp, *head;
struct urb_priv *urbp = urb->hcpriv;
struct uhci_td *td;
unsigned int status = 0;
@@ -1091,13 +1026,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
urb->actual_length = 0;
- head = &urbp->td_list;
- tmp = head->next;
- while (tmp != head) {
- td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
-
+ list_for_each_entry(td, &urbp->td_list, list) {
status = uhci_status_bits(td_status(td));
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
@@ -1176,17 +1105,12 @@ static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end)
{
struct urb *last_urb = NULL;
- struct list_head *tmp, *head;
+ struct urb_priv *up;
int ret = 0;
- head = &uhci->urb_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
+ list_for_each_entry(up, &uhci->urb_list, urb_list) {
struct urb *u = up->urb;
- tmp = tmp->next;
-
/* look for pending URB's with identical pipe handle */
if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
(u->status == -EINPROGRESS) && (u != urb)) {
@@ -1272,7 +1196,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
{
- struct list_head *tmp, *head;
+ struct uhci_td *td;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
int status;
int i, ret = 0;
@@ -1280,14 +1204,9 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
urb->actual_length = 0;
i = 0;
- head = &urbp->td_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+ list_for_each_entry(td, &urbp->td_list, list) {
int actlength;
- tmp = tmp->next;
-
if (td_status(td) & TD_CTRL_ACTIVE)
return -EINPROGRESS;
@@ -1311,20 +1230,15 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb)
{
- struct list_head *tmp, *head;
+ struct urb_priv *up;
/* We don't match Isoc transfers since they are special */
if (usb_pipeisoc(urb->pipe))
return NULL;
- head = &uhci->urb_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
+ list_for_each_entry(up, &uhci->urb_list, urb_list) {
struct urb *u = up->urb;
- tmp = tmp->next;
-
if (u->dev == urb->dev && u->status == -EINPROGRESS) {
/* For control, ignore the direction */
if (usb_pipecontrol(urb->pipe) &&
@@ -1475,9 +1389,10 @@ out:
static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb)
{
- struct list_head *head, *tmp;
+ struct list_head *head;
+ struct uhci_td *td;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- int prevactive = 1;
+ int prevactive = 0;
uhci_dec_fsbr(uhci, urb); /* Safe since it checks */
@@ -1485,25 +1400,28 @@ static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb)
* Now we need to find out what the last successful toggle was
* so we can update the local data toggle for the next transfer
*
- * There's 3 way's the last successful completed TD is found:
+ * There are 2 ways the last successful completed TD is found:
*
* 1) The TD is NOT active and the actual length < expected length
* 2) The TD is NOT active and it's the last TD in the chain
+ *
+ * and a third way the first uncompleted TD is found:
+ *
* 3) The TD is active and the previous TD is NOT active
*
* Control and Isochronous ignore the toggle, so this is safe
* for all types
+ *
+ * FIXME: The toggle fixups won't be 100% reliable until we
+ * change over to using a single queue for each endpoint and
+ * stop the queue before unlinking.
*/
head = &urbp->td_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
-
+ list_for_each_entry(td, head, list) {
if (!(td_status(td) & TD_CTRL_ACTIVE) &&
- (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td)) ||
- tmp == head))
+ (uhci_actual_length(td_status(td)) <
+ uhci_expected_length(td_token(td)) ||
+ td->list.next == head))
usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
uhci_packetout(td_token(td)),
uhci_toggle(td_token(td)) ^ 1);
@@ -1556,7 +1474,8 @@ done:
static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct list_head *head, *tmp;
+ struct list_head *head;
+ struct uhci_td *td;
int count = 0;
uhci_dec_fsbr(uhci, urb);
@@ -1570,18 +1489,14 @@ static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb)
*/
head = &urbp->td_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
-
+ list_for_each_entry(td, head, list) {
/*
* Make sure we don't do the last one (since it'll have the
* TERM bit set) as well as we skip every so many TD's to
* make sure it doesn't hog the bandwidth
*/
- if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1))
+ if (td->list.next != head && (count % DEPTH_INTERVAL) ==
+ (DEPTH_INTERVAL - 1))
td->link |= UHCI_PTR_DEPTH;
count++;
@@ -1606,12 +1521,10 @@ static void stall_callback(unsigned long ptr)
{
struct usb_hcd *hcd = (struct usb_hcd *)ptr;
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- struct list_head list, *tmp, *head;
+ struct urb_priv *up;
unsigned long flags;
int called_uhci_finish_completion = 0;
- INIT_LIST_HEAD(&list);
-
spin_lock_irqsave(&uhci->schedule_lock, flags);
if (!list_empty(&uhci->urb_remove_list) &&
uhci_get_current_frame_number(uhci) != uhci->urb_remove_age) {
@@ -1620,14 +1533,9 @@ static void stall_callback(unsigned long ptr)
called_uhci_finish_completion = 1;
}
- head = &uhci->urb_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
+ list_for_each_entry(up, &uhci->urb_list, urb_list) {
struct urb *u = up->urb;
- tmp = tmp->next;
-
spin_lock(&u->lock);
/* Check if the FSBR timed out */
@@ -1642,17 +1550,6 @@ static void stall_callback(unsigned long ptr)
if (called_uhci_finish_completion)
wake_up_all(&uhci->waitqh);
- head = &list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
- struct urb *u = up->urb;
-
- tmp = tmp->next;
-
- uhci_urb_dequeue(hcd, u);
- }
-
/* Really disable FSBR */
if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
uhci->fsbrtimeout = 0;
@@ -1661,6 +1558,8 @@ static void stall_callback(unsigned long ptr)
/* Poll for and perform state transitions */
hc_state_transitions(uhci);
+ if (unlikely(uhci->suspended_ports && uhci->state != UHCI_SUSPENDED))
+ uhci_check_resume(uhci);
init_stall_timer(hcd);
}
@@ -1680,15 +1579,9 @@ static int init_stall_timer(struct usb_hcd *hcd)
static void uhci_free_pending_qhs(struct uhci_hcd *uhci)
{
- struct list_head *tmp, *head;
-
- head = &uhci->qh_remove_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list);
-
- tmp = tmp->next;
+ struct uhci_qh *qh, *tmp;
+ list_for_each_entry_safe(qh, tmp, &uhci->qh_remove_list, remove_list) {
list_del_init(&qh->remove_list);
uhci_free_qh(uhci, qh);
@@ -1697,15 +1590,9 @@ static void uhci_free_pending_qhs(struct uhci_hcd *uhci)
static void uhci_free_pending_tds(struct uhci_hcd *uhci)
{
- struct list_head *tmp, *head;
-
- head = &uhci->td_remove_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, remove_list);
-
- tmp = tmp->next;
+ struct uhci_td *td, *tmp;
+ list_for_each_entry_safe(td, tmp, &uhci->td_remove_list, remove_list) {
list_del_init(&td->remove_list);
uhci_free_td(uhci, td);
@@ -1726,19 +1613,13 @@ static void uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs
static void uhci_finish_completion(struct usb_hcd *hcd, struct pt_regs *regs)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- struct list_head *tmp, *head;
+ struct urb_priv *urbp, *tmp;
- head = &uhci->complete_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
+ list_for_each_entry_safe(urbp, tmp, &uhci->complete_list, urb_list) {
struct urb *urb = urbp->urb;
list_del_init(&urbp->urb_list);
uhci_finish_urb(hcd, urb, regs);
-
- head = &uhci->complete_list;
- tmp = head->next;
}
}
@@ -1754,7 +1635,7 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned long io_addr = uhci->io_addr;
unsigned short status;
- struct list_head *tmp, *head;
+ struct urb_priv *urbp, *tmp;
unsigned int age;
/*
@@ -1801,15 +1682,11 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
else
uhci_set_next_interrupt(uhci);
- /* Walk the list of pending URB's to see which ones completed */
- head = &uhci->urb_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
+ /* Walk the list of pending URBs to see which ones completed
+ * (must be _safe because uhci_transfer_result() dequeues URBs) */
+ list_for_each_entry_safe(urbp, tmp, &uhci->urb_list, urb_list) {
struct urb *urb = urbp->urb;
- tmp = tmp->next;
-
/* Checks the status and does all of the magic necessary */
uhci_transfer_result(uhci, urb);
}
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
index 785c0c2cca12..ea9bd98b5edc 100644
--- a/drivers/usb/host/uhci-hcd.h
+++ b/drivers/usb/host/uhci-hcd.h
@@ -352,6 +352,12 @@ struct uhci_hcd {
int resume_detect; /* Need a Global Resume */
unsigned int saved_framenumber; /* Save during PM suspend */
+ /* Support for port suspend/resume */
+ unsigned long port_c_suspend; /* Bit-arrays of ports */
+ unsigned long suspended_ports;
+ unsigned long resuming_ports;
+ unsigned long resume_timeout; /* Time to stop signalling */
+
/* Main list of URB's currently controlled by this HC */
struct list_head urb_list; /* P: uhci->schedule_lock */
@@ -385,12 +391,12 @@ struct urb_priv {
struct uhci_qh *qh; /* QH for this URB */
struct list_head td_list; /* P: urb->lock */
- int fsbr : 1; /* URB turned on FSBR */
- int fsbr_timeout : 1; /* URB timed out on FSBR */
- int queued : 1; /* QH was queued (not linked in) */
- int short_control_packet : 1; /* If we get a short packet during */
- /* a control transfer, retrigger */
- /* the status phase */
+ unsigned fsbr : 1; /* URB turned on FSBR */
+ unsigned fsbr_timeout : 1; /* URB timed out on FSBR */
+ unsigned queued : 1; /* QH was queued (not linked in) */
+ unsigned short_control_packet : 1; /* If we get a short packet during */
+ /* a control transfer, retrigger */
+ /* the status phase */
unsigned long inserttime; /* In jiffies */
unsigned long fsbrtime; /* In jiffies */
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c
index 22bbd70063ce..f44d90e23df2 100644
--- a/drivers/usb/host/uhci-hub.c
+++ b/drivers/usb/host/uhci-hub.c
@@ -36,13 +36,13 @@ static __u8 root_hub_hub_des[] =
static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- unsigned long io_addr = uhci->io_addr;
- int i;
+ int port;
*buf = 0;
- for (i = 0; i < uhci->rh_numports; i++) {
- if (inw(io_addr + USBPORTSC1 + i * 2) & RWC_BITS)
- *buf |= (1 << (i + 1));
+ for (port = 0; port < uhci->rh_numports; ++port) {
+ if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & RWC_BITS) ||
+ test_bit(port, &uhci->port_c_suspend))
+ *buf |= (1 << (port + 1));
}
return !!*buf;
}
@@ -62,31 +62,67 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
status &= ~(RWC_BITS|WZ_BITS); \
outw(status, port_addr)
+/* UHCI controllers don't automatically stop resume signalling after 20 msec,
+ * so we have to poll and check timeouts in order to take care of it.
+ * FIXME: Synchronize access to these fields by a spinlock.
+ */
+static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
+ unsigned int port_addr)
+{
+ int status;
+
+ if (test_bit(port, &uhci->suspended_ports)) {
+ CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD);
+ clear_bit(port, &uhci->suspended_ports);
+ clear_bit(port, &uhci->resuming_ports);
+ set_bit(port, &uhci->port_c_suspend);
+ }
+}
+
+static void uhci_check_resume(struct uhci_hcd *uhci)
+{
+ unsigned int port;
+ unsigned int port_addr;
+
+ for (port = 0; port < uhci->rh_numports; ++port) {
+ port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
+ if (unlikely(inw(port_addr) & USBPORTSC_RD)) {
+ if (!test_bit(port, &uhci->resuming_ports)) {
+
+ /* Port received a wakeup request */
+ set_bit(port, &uhci->resuming_ports);
+ uhci->resume_timeout = jiffies +
+ msecs_to_jiffies(20);
+ } else if (time_after_eq(jiffies,
+ uhci->resume_timeout)) {
+ uhci_finish_suspend(uhci, port, port_addr);
+ }
+ }
+ }
+}
/* size of returned buffer is part of USB spec */
static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- int status, retval = 0, len = 0;
- unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * (wIndex-1);
- __u16 wPortChange, wPortStatus;
+ int status, lstatus, retval = 0, len = 0;
+ unsigned int port = wIndex - 1;
+ unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
+ u16 wPortChange, wPortStatus;
switch (typeReq) {
- /* Request Destination:
- without flags: Device,
- RH_INTERFACE: interface,
- RH_ENDPOINT: endpoint,
- RH_CLASS means HUB here,
- RH_OTHER | RH_CLASS almost ever means HUB_PORT here
- */
case GetHubStatus:
*(__le32 *)buf = cpu_to_le32(0);
OK(4); /* hub power */
case GetPortStatus:
- if (!wIndex || wIndex > uhci->rh_numports)
+ if (port >= uhci->rh_numports)
goto err;
+
+ if (uhci->resuming_ports)
+ uhci_check_resume(uhci);
+
status = inw(port_addr);
/* Intel controllers report the OverCurrent bit active on.
@@ -97,34 +133,43 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
PCI_VENDOR_ID_VIA)
status ^= USBPORTSC_OC;
- /* UHCI doesn't support C_SUSPEND and C_RESET (always false) */
- wPortChange = 0;
+ /* UHCI doesn't support C_RESET (always false) */
+ wPortChange = lstatus = 0;
if (status & USBPORTSC_CSC)
- wPortChange |= 1 << (USB_PORT_FEAT_C_CONNECTION - 16);
+ wPortChange |= USB_PORT_STAT_C_CONNECTION;
if (status & USBPORTSC_PEC)
- wPortChange |= 1 << (USB_PORT_FEAT_C_ENABLE - 16);
+ wPortChange |= USB_PORT_STAT_C_ENABLE;
if (status & USBPORTSC_OCC)
- wPortChange |= 1 << (USB_PORT_FEAT_C_OVER_CURRENT - 16);
+ wPortChange |= USB_PORT_STAT_C_OVERCURRENT;
+
+ if (test_bit(port, &uhci->port_c_suspend)) {
+ wPortChange |= USB_PORT_STAT_C_SUSPEND;
+ lstatus |= 1;
+ }
+ if (test_bit(port, &uhci->suspended_ports))
+ lstatus |= 2;
+ if (test_bit(port, &uhci->resuming_ports))
+ lstatus |= 4;
/* UHCI has no power switching (always on) */
- wPortStatus = 1 << USB_PORT_FEAT_POWER;
+ wPortStatus = USB_PORT_STAT_POWER;
if (status & USBPORTSC_CCS)
- wPortStatus |= 1 << USB_PORT_FEAT_CONNECTION;
+ wPortStatus |= USB_PORT_STAT_CONNECTION;
if (status & USBPORTSC_PE) {
- wPortStatus |= 1 << USB_PORT_FEAT_ENABLE;
+ wPortStatus |= USB_PORT_STAT_ENABLE;
if (status & (USBPORTSC_SUSP | USBPORTSC_RD))
- wPortStatus |= 1 << USB_PORT_FEAT_SUSPEND;
+ wPortStatus |= USB_PORT_STAT_SUSPEND;
}
if (status & USBPORTSC_OC)
- wPortStatus |= 1 << USB_PORT_FEAT_OVER_CURRENT;
+ wPortStatus |= USB_PORT_STAT_OVERCURRENT;
if (status & USBPORTSC_PR)
- wPortStatus |= 1 << USB_PORT_FEAT_RESET;
+ wPortStatus |= USB_PORT_STAT_RESET;
if (status & USBPORTSC_LSDA)
- wPortStatus |= 1 << USB_PORT_FEAT_LOWSPEED;
+ wPortStatus |= USB_PORT_STAT_LOW_SPEED;
if (wPortChange)
- dev_dbg(uhci_dev(uhci), "port %d portsc %04x\n",
- wIndex, status);
+ dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x\n",
+ wIndex, status, lstatus);
*(__le16 *)buf = cpu_to_le16(wPortStatus);
*(__le16 *)(buf + 2) = cpu_to_le16(wPortChange);
@@ -140,11 +185,12 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
}
break;
case SetPortFeature:
- if (!wIndex || wIndex > uhci->rh_numports)
+ if (port >= uhci->rh_numports)
goto err;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
+ set_bit(port, &uhci->suspended_ports);
SET_RH_PORTSTAT(USBPORTSC_SUSP);
OK(0);
case USB_PORT_FEAT_RESET:
@@ -152,6 +198,9 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
mdelay(50); /* USB v1.1 7.1.7.3 */
CLR_RH_PORTSTAT(USBPORTSC_PR);
udelay(10);
+
+ /* Reset terminates Resume signalling */
+ uhci_finish_suspend(uhci, port, port_addr);
SET_RH_PORTSTAT(USBPORTSC_PE);
mdelay(10);
CLR_RH_PORTSTAT(USBPORTSC_PEC|USBPORTSC_CSC);
@@ -164,21 +213,38 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
}
break;
case ClearPortFeature:
- if (!wIndex || wIndex > uhci->rh_numports)
+ if (port >= uhci->rh_numports)
goto err;
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
CLR_RH_PORTSTAT(USBPORTSC_PE);
+
+ /* Disable terminates Resume signalling */
+ uhci_finish_suspend(uhci, port, port_addr);
OK(0);
case USB_PORT_FEAT_C_ENABLE:
CLR_RH_PORTSTAT(USBPORTSC_PEC);
OK(0);
case USB_PORT_FEAT_SUSPEND:
- CLR_RH_PORTSTAT(USBPORTSC_SUSP);
+ if (test_bit(port, &uhci->suspended_ports) &&
+ !test_and_set_bit(port,
+ &uhci->resuming_ports)) {
+ uhci->resume_timeout = jiffies +
+ msecs_to_jiffies(20);
+ SET_RH_PORTSTAT(USBPORTSC_RD);
+
+ /* The controller won't allow RD to be set
+ * if the port is disabled. When this happens
+ * just skip the Resume signalling.
+ */
+ if (!(inw(port_addr) & USBPORTSC_RD))
+ uhci_finish_suspend(uhci, port,
+ port_addr);
+ }
OK(0);
case USB_PORT_FEAT_C_SUSPEND:
- /* this driver won't report these */
+ clear_bit(port, &uhci->port_c_suspend);
OK(0);
case USB_PORT_FEAT_POWER:
/* UHCI has no power switching */
diff --git a/drivers/usb/image/Kconfig b/drivers/usb/image/Kconfig
index 0b0f80e4f9a7..b541b67d2eb6 100644
--- a/drivers/usb/image/Kconfig
+++ b/drivers/usb/image/Kconfig
@@ -30,11 +30,12 @@ config USB_MICROTEK
This driver can be compiled as a module, called microtek.
config USB_HPUSBSCSI
- tristate "HP53xx USB scanner support (EXPERIMENTAL)"
- depends on USB && SCSI && EXPERIMENTAL
+ tristate "HP53xx USB scanner support"
+ depends on USB && SCSI
help
Say Y here if you want support for the HP 53xx series of scanners
- and the Minolta Scan Dual. This driver is experimental.
+ and the Minolta Scan Dual.
The scanner will be accessible as a SCSI device.
+ Please note that recent versions of SANE use usbfs, not this driver.
This can be compiled as a module, called hpusbscsi.
diff --git a/drivers/usb/image/hpusbscsi.c b/drivers/usb/image/hpusbscsi.c
index dabc3666aee7..47a864b29f3d 100644
--- a/drivers/usb/image/hpusbscsi.c
+++ b/drivers/usb/image/hpusbscsi.c
@@ -106,7 +106,7 @@ hpusbscsi_usb_probe(struct usb_interface *intf,
/* In host->hostdata we store a pointer to desc */
new->host = scsi_host_alloc(&hpusbscsi_scsi_host_template, sizeof(new));
if (!new->host)
- goto out_unlink_controlurb;
+ goto out_kill_controlurb;
new->host->hostdata[0] = (unsigned long)new;
scsi_add_host(new->host, &intf->dev); /* XXX handle failure */
@@ -118,8 +118,8 @@ hpusbscsi_usb_probe(struct usb_interface *intf,
usb_set_intfdata(intf, new);
return 0;
- out_unlink_controlurb:
- usb_unlink_urb(new->controlurb);
+ out_kill_controlurb:
+ usb_kill_urb(new->controlurb);
out_free_controlurb:
usb_free_urb(new->controlurb);
out_free_dataurb:
@@ -137,7 +137,7 @@ hpusbscsi_usb_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
scsi_remove_host(desc->host);
- usb_unlink_urb(desc->controlurb);
+ usb_kill_urb(desc->controlurb);
scsi_host_put(desc->host);
usb_free_urb(desc->controlurb);
@@ -280,8 +280,8 @@ static int hpusbscsi_scsi_abort (Scsi_Cmnd *srb)
struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->device->host->hostdata[0]);
printk(KERN_DEBUG"Requested is canceled.\n");
- usb_unlink_urb(hpusbscsi->dataurb);
- usb_unlink_urb(hpusbscsi->controlurb);
+ usb_kill_urb(hpusbscsi->dataurb);
+ usb_kill_urb(hpusbscsi->controlurb);
hpusbscsi->state = HP_STATE_FREE;
return SCSI_ABORT_PENDING;
diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c
index f401557f0356..66bb13307df5 100644
--- a/drivers/usb/image/mdc800.c
+++ b/drivers/usb/image/mdc800.c
@@ -317,7 +317,6 @@ static int mdc800_usb_waitForIRQ (int mode, int msec)
mdc800->camera_request_ready=1+mode;
add_wait_queue(&mdc800->irq_wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
timeout = msec*HZ/1000;
while (!mdc800->irq_woken && timeout)
{
@@ -325,7 +324,6 @@ static int mdc800_usb_waitForIRQ (int mode, int msec)
timeout = schedule_timeout (timeout);
}
remove_wait_queue(&mdc800->irq_wait, &wait);
- set_current_state(TASK_RUNNING);
mdc800->irq_woken = 0;
if (mdc800->camera_request_ready>0)
@@ -543,9 +541,9 @@ static void mdc800_usb_disconnect (struct usb_interface *intf)
mdc800->state=NOT_CONNECTED;
- usb_unlink_urb (mdc800->irq_urb);
- usb_unlink_urb (mdc800->write_urb);
- usb_unlink_urb (mdc800->download_urb);
+ usb_kill_urb(mdc800->irq_urb);
+ usb_kill_urb(mdc800->write_urb);
+ usb_kill_urb(mdc800->download_urb);
mdc800->dev = NULL;
usb_set_intfdata(intf, NULL);
@@ -649,9 +647,9 @@ static int mdc800_device_release (struct inode* inode, struct file *file)
down (&mdc800->io_lock);
if (mdc800->open && (mdc800->state != NOT_CONNECTED))
{
- usb_unlink_urb (mdc800->irq_urb);
- usb_unlink_urb (mdc800->write_urb);
- usb_unlink_urb (mdc800->download_urb);
+ usb_kill_urb(mdc800->irq_urb);
+ usb_kill_urb(mdc800->write_urb);
+ usb_kill_urb(mdc800->download_urb);
mdc800->open=0;
}
else
@@ -725,7 +723,6 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l
set_current_state(TASK_UNINTERRUPTIBLE);
timeout = schedule_timeout (timeout);
}
- set_current_state(TASK_RUNNING);
remove_wait_queue(&mdc800->download_wait, &wait);
mdc800->downloaded = 0;
if (mdc800->download_urb->status != 0)
@@ -851,12 +848,11 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s
set_current_state(TASK_UNINTERRUPTIBLE);
timeout = schedule_timeout (timeout);
}
- set_current_state(TASK_RUNNING);
remove_wait_queue(&mdc800->write_wait, &wait);
mdc800->written = 0;
if (mdc800->state == WORKING)
{
- usb_unlink_urb (mdc800->write_urb);
+ usb_kill_urb(mdc800->write_urb);
up (&mdc800->io_lock);
return -EIO;
}
diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c
index 9d3aa3075086..2a18c35629eb 100644
--- a/drivers/usb/image/microtek.c
+++ b/drivers/usb/image/microtek.c
@@ -324,7 +324,7 @@ static inline void mts_urb_abort(struct mts_desc* desc) {
MTS_DEBUG_GOT_HERE();
mts_debug_dump(desc);
- usb_unlink_urb( desc->urb );
+ usb_kill_urb( desc->urb );
}
static int mts_scsi_abort (Scsi_Cmnd *srb)
@@ -341,12 +341,18 @@ static int mts_scsi_abort (Scsi_Cmnd *srb)
static int mts_scsi_host_reset (Scsi_Cmnd *srb)
{
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
+ int result, rc;
MTS_DEBUG_GOT_HERE();
mts_debug_dump(desc);
- usb_reset_device(desc->usb_dev); /*FIXME: untested on new reset code */
- return 0; /* RANT why here 0 and not SUCCESS */
+ rc = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf);
+ if (rc < 0)
+ return FAILED;
+ result = usb_reset_device(desc->usb_dev);;
+ if (rc)
+ usb_unlock_device(desc->usb_dev);
+ return result ? FAILED : SUCCESS;
}
static
@@ -777,6 +783,7 @@ static int mts_usb_probe(struct usb_interface *intf,
goto out_kfree;
new_desc->usb_dev = dev;
+ new_desc->usb_intf = intf;
init_MUTEX(&new_desc->lock);
/* endpoints */
@@ -822,10 +829,10 @@ static void mts_usb_disconnect (struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
+ usb_kill_urb(desc->urb);
scsi_remove_host(desc->host);
- usb_unlink_urb(desc->urb);
- scsi_host_put(desc->host);
+ scsi_host_put(desc->host);
usb_free_urb(desc->urb);
kfree(desc);
}
diff --git a/drivers/usb/image/microtek.h b/drivers/usb/image/microtek.h
index 206994ddcedf..3271deb8c001 100644
--- a/drivers/usb/image/microtek.h
+++ b/drivers/usb/image/microtek.h
@@ -31,6 +31,7 @@ struct mts_desc {
struct mts_desc *prev;
struct usb_device *usb_dev;
+ struct usb_interface *usb_intf;
/* Endpoint addresses */
u8 ep_out;
diff --git a/drivers/usb/input/aiptek.c b/drivers/usb/input/aiptek.c
index 44b8faee6536..67a5b70a6daf 100644
--- a/drivers/usb/input/aiptek.c
+++ b/drivers/usb/input/aiptek.c
@@ -837,7 +837,7 @@ static void aiptek_close(struct input_dev *inputdev)
struct aiptek *aiptek = inputdev->private;
if (--aiptek->openCount == 0) {
- usb_unlink_urb(aiptek->urb);
+ usb_kill_urb(aiptek->urb);
}
}
@@ -2258,7 +2258,7 @@ static void aiptek_disconnect(struct usb_interface *intf)
if (aiptek != NULL) {
/* Free & unhook everything from the system.
*/
- usb_unlink_urb(aiptek->urb);
+ usb_kill_urb(aiptek->urb);
input_unregister_device(&aiptek->inputdev);
aiptek_delete_files(&intf->dev);
usb_free_urb(aiptek->urb);
diff --git a/drivers/usb/input/ati_remote.c b/drivers/usb/input/ati_remote.c
index 61a42bdae1d8..91f7f434dd48 100644
--- a/drivers/usb/input/ati_remote.c
+++ b/drivers/usb/input/ati_remote.c
@@ -418,13 +418,14 @@ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigne
while (timeout && (ati_remote->out_urb->status == -EINPROGRESS)
&& !(ati_remote->send_flags & SEND_FLAG_COMPLETE)) {
+ set_current_state(TASK_INTERRUPTIBLE);
timeout = schedule_timeout(timeout);
rmb();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&ati_remote->wait, &wait);
- usb_unlink_urb(ati_remote->out_urb);
+ usb_kill_urb(ati_remote->out_urb);
return retval;
}
@@ -624,10 +625,10 @@ static void ati_remote_delete(struct ati_remote *ati_remote)
if (!ati_remote) return;
if (ati_remote->irq_urb)
- usb_unlink_urb(ati_remote->irq_urb);
+ usb_kill_urb(ati_remote->irq_urb);
if (ati_remote->out_urb)
- usb_unlink_urb(ati_remote->out_urb);
+ usb_kill_urb(ati_remote->out_urb);
input_unregister_device(&ati_remote->idev);
diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
index 35baacd7706d..c38e7fccf564 100644
--- a/drivers/usb/input/hid-core.c
+++ b/drivers/usb/input/hid-core.c
@@ -1260,8 +1260,10 @@ int hid_wait_io(struct hid_device *hid)
add_wait_queue(&hid->wait, &wait);
while (timeout && (test_bit(HID_CTRL_RUNNING, &hid->iofl) ||
- test_bit(HID_OUT_RUNNING, &hid->iofl)))
+ test_bit(HID_OUT_RUNNING, &hid->iofl))) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
timeout = schedule_timeout(timeout);
+ }
set_current_state(TASK_RUNNING);
remove_wait_queue(&hid->wait, &wait);
@@ -1350,9 +1352,9 @@ void hid_init_reports(struct hid_device *hid)
while (ret) {
err |= ret;
if (test_bit(HID_CTRL_RUNNING, &hid->iofl))
- usb_unlink_urb(hid->urbctrl);
+ usb_kill_urb(hid->urbctrl);
if (test_bit(HID_OUT_RUNNING, &hid->iofl))
- usb_unlink_urb(hid->urbout);
+ usb_kill_urb(hid->urbout);
ret = hid_wait_io(hid);
}
@@ -1455,10 +1457,11 @@ void hid_init_reports(struct hid_device *hid)
#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
-#define USB_VENDOR_ID_CODEMERCS 0x07c0
-#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500
-#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501
-
+#define USB_VENDOR_ID_CODEMERCS 0x07c0
+#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500
+#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501
+#define USB_DEVICE_ID_CODEMERCS_IOW48 0x1502
+#define USB_DEVICE_ID_CODEMERCS_IOW28 0x1503
static struct hid_blacklist {
__u16 idVendor;
@@ -1542,6 +1545,11 @@ static struct hid_blacklist {
{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
+ { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW48, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW28, HID_QUIRK_IGNORE },
+
{ 0, 0 }
};
diff --git a/drivers/usb/input/kbtab.c b/drivers/usb/input/kbtab.c
index 39c0d38aefa4..2e4c0d246030 100644
--- a/drivers/usb/input/kbtab.c
+++ b/drivers/usb/input/kbtab.c
@@ -122,7 +122,7 @@ static void kbtab_close(struct input_dev *dev)
struct kbtab *kbtab = dev->private;
if (!--kbtab->open)
- usb_unlink_urb(kbtab->irq);
+ usb_kill_urb(kbtab->irq);
}
static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id)
@@ -205,7 +205,7 @@ static void kbtab_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
if (kbtab) {
- usb_unlink_urb(kbtab->irq);
+ usb_kill_urb(kbtab->irq);
input_unregister_device(&kbtab->dev);
usb_free_urb(kbtab->irq);
usb_buffer_free(interface_to_usbdev(intf), 10, kbtab->data, kbtab->data_dma);
diff --git a/drivers/usb/input/mtouchusb.c b/drivers/usb/input/mtouchusb.c
index ae5713876d4c..9dcfd7e2ffbf 100644
--- a/drivers/usb/input/mtouchusb.c
+++ b/drivers/usb/input/mtouchusb.c
@@ -155,7 +155,7 @@ static void mtouchusb_close (struct input_dev *input)
struct mtouch_usb *mtouch = input->private;
if (!--mtouch->open)
- usb_unlink_urb (mtouch->irq);
+ usb_kill_urb (mtouch->irq);
}
static int mtouchusb_alloc_buffers(struct usb_device *udev, struct mtouch_usb *mtouch)
@@ -320,7 +320,7 @@ static void mtouchusb_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
if (mtouch) {
dbg("%s - mtouch is initialized, cleaning up", __FUNCTION__);
- usb_unlink_urb(mtouch->irq);
+ usb_kill_urb(mtouch->irq);
input_unregister_device(&mtouch->input);
usb_free_urb(mtouch->irq);
mtouchusb_free_buffers(interface_to_usbdev(intf), mtouch);
diff --git a/drivers/usb/input/pid.c b/drivers/usb/input/pid.c
index a38b0db8dcea..d1ea1f056599 100644
--- a/drivers/usb/input/pid.c
+++ b/drivers/usb/input/pid.c
@@ -56,7 +56,7 @@ static void hid_pid_exit(struct hid_device* hid)
struct hid_ff_pid *private = hid->ff_private;
if (private->urbffout) {
- usb_unlink_urb(private->urbffout);
+ usb_kill_urb(private->urbffout);
usb_free_urb(private->urbffout);
}
}
diff --git a/drivers/usb/input/powermate.c b/drivers/usb/input/powermate.c
index 0ba1bcbfb431..852ebf8574a8 100644
--- a/drivers/usb/input/powermate.c
+++ b/drivers/usb/input/powermate.c
@@ -417,7 +417,7 @@ static void powermate_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
if (pm) {
pm->requires_update = 0;
- usb_unlink_urb(pm->irq);
+ usb_kill_urb(pm->irq);
input_unregister_device(&pm->input);
usb_free_urb(pm->irq);
usb_free_urb(pm->config);
diff --git a/drivers/usb/input/touchkitusb.c b/drivers/usb/input/touchkitusb.c
index 4917b042ef9a..14443f926ab0 100644
--- a/drivers/usb/input/touchkitusb.c
+++ b/drivers/usb/input/touchkitusb.c
@@ -141,7 +141,7 @@ static void touchkit_close(struct input_dev *input)
struct touchkit_usb *touchkit = input->private;
if (!--touchkit->open)
- usb_unlink_urb(touchkit->irq);
+ usb_kill_urb(touchkit->irq);
}
static int touchkit_alloc_buffers(struct usb_device *udev,
@@ -276,7 +276,7 @@ static void touchkit_disconnect(struct usb_interface *intf)
dbg("%s - touchkit is initialized, cleaning up", __FUNCTION__);
usb_set_intfdata(intf, NULL);
input_unregister_device(&touchkit->input);
- usb_unlink_urb(touchkit->irq);
+ usb_kill_urb(touchkit->irq);
usb_free_urb(touchkit->irq);
touchkit_free_buffers(interface_to_usbdev(intf), touchkit);
kfree(touchkit);
diff --git a/drivers/usb/input/usbkbd.c b/drivers/usb/input/usbkbd.c
index 0c51b1eb84cf..1700f405b00b 100644
--- a/drivers/usb/input/usbkbd.c
+++ b/drivers/usb/input/usbkbd.c
@@ -196,7 +196,7 @@ static void usb_kbd_close(struct input_dev *dev)
struct usb_kbd *kbd = dev->private;
if (!--kbd->open)
- usb_unlink_urb(kbd->irq);
+ usb_kill_urb(kbd->irq);
}
static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
@@ -343,7 +343,7 @@ static void usb_kbd_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
if (kbd) {
- usb_unlink_urb(kbd->irq);
+ usb_kill_urb(kbd->irq);
input_unregister_device(&kbd->dev);
usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
kfree(kbd);
diff --git a/drivers/usb/input/usbmouse.c b/drivers/usb/input/usbmouse.c
index 5bf27306ea3c..8c7381b74499 100644
--- a/drivers/usb/input/usbmouse.c
+++ b/drivers/usb/input/usbmouse.c
@@ -118,7 +118,7 @@ static void usb_mouse_close(struct input_dev *dev)
struct usb_mouse *mouse = dev->private;
if (!--mouse->open)
- usb_unlink_urb(mouse->irq);
+ usb_kill_urb(mouse->irq);
}
static int usb_mouse_probe(struct usb_interface * intf, const struct usb_device_id * id)
@@ -223,7 +223,7 @@ static void usb_mouse_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
if (mouse) {
- usb_unlink_urb(mouse->irq);
+ usb_kill_urb(mouse->irq);
input_unregister_device(&mouse->dev);
usb_free_urb(mouse->irq);
usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);
diff --git a/drivers/usb/input/wacom.c b/drivers/usb/input/wacom.c
index 492a5cb12af4..471d1bf68bf0 100644
--- a/drivers/usb/input/wacom.c
+++ b/drivers/usb/input/wacom.c
@@ -608,7 +608,7 @@ static void wacom_close(struct input_dev *dev)
struct wacom *wacom = dev->private;
if (!--wacom->open)
- usb_unlink_urb(wacom->irq);
+ usb_kill_urb(wacom->irq);
}
static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
@@ -729,7 +729,7 @@ static void wacom_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
if (wacom) {
- usb_unlink_urb(wacom->irq);
+ usb_kill_urb(wacom->irq);
input_unregister_device(&wacom->dev);
usb_free_urb(wacom->irq);
usb_buffer_free(interface_to_usbdev(intf), 10, wacom->data, wacom->data_dma);
diff --git a/drivers/usb/input/xpad.c b/drivers/usb/input/xpad.c
index 1956eb06b75c..c5fa87edb667 100644
--- a/drivers/usb/input/xpad.c
+++ b/drivers/usb/input/xpad.c
@@ -214,7 +214,7 @@ static void xpad_close (struct input_dev *dev)
struct usb_xpad *xpad = dev->private;
if (!--xpad->open_count)
- usb_unlink_urb(xpad->irq_in);
+ usb_kill_urb(xpad->irq_in);
}
static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
@@ -325,7 +325,7 @@ static void xpad_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
if (xpad) {
- usb_unlink_urb(xpad->irq_in);
+ usb_kill_urb(xpad->irq_in);
input_unregister_device(&xpad->dev);
usb_free_urb(xpad->irq_in);
usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
diff --git a/drivers/usb/media/Kconfig b/drivers/usb/media/Kconfig
index 4ac7ac4f3b1d..678e5fcbe29a 100644
--- a/drivers/usb/media/Kconfig
+++ b/drivers/usb/media/Kconfig
@@ -123,11 +123,11 @@ config USB_SE401
module will be called se401.
config USB_SN9C102
- tristate "USB SN9C10[12] PC Camera Controller support"
+ tristate "USB SN9C10x PC Camera Controller support"
depends on USB && VIDEO_DEV
---help---
- Say Y here if you want support for cameras based on SONiX SN9C101
- or SN9C102 PC Camera Controllers.
+ Say Y here if you want support for cameras based on SONiX SN9C101,
+ SN9C102 or SN9C103 PC Camera Controllers.
See <file:Documentation/usb/sn9c102.txt> for more informations.
diff --git a/drivers/usb/media/dabusb.c b/drivers/usb/media/dabusb.c
index 0e5425f328d0..0aae6ecc7ad6 100644
--- a/drivers/usb/media/dabusb.c
+++ b/drivers/usb/media/dabusb.c
@@ -109,16 +109,13 @@ static void dump_urb (struct urb *urb)
static int dabusb_cancel_queue (pdabusb_t s, struct list_head *q)
{
unsigned long flags;
- struct list_head *p;
pbuff_t b;
dbg("dabusb_cancel_queue");
spin_lock_irqsave (&s->lock, flags);
- for (p = q->next; p != q; p = p->next) {
- b = list_entry (p, buff_t, buff_list);
-
+ list_for_each_entry(b, q, buff_list) {
#ifdef DEBUG
dump_urb(b->purb);
#endif
@@ -598,6 +595,7 @@ static int dabusb_open (struct inode *inode, struct file *file)
if (file->f_flags & O_NONBLOCK) {
return -EBUSY;
}
+ set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout (HZ / 2);
if (signal_pending (current)) {
diff --git a/drivers/usb/media/konicawc.c b/drivers/usb/media/konicawc.c
index 3376654ca051..aca3093c2b87 100644
--- a/drivers/usb/media/konicawc.c
+++ b/drivers/usb/media/konicawc.c
@@ -474,13 +474,8 @@ static void konicawc_stop_data(struct uvd *uvd)
/* Unschedule all of the iso td's */
for (i=0; i < USBVIDEO_NUMSBUF; i++) {
- j = usb_unlink_urb(uvd->sbuf[i].urb);
- if (j < 0)
- err("usb_unlink_urb() error %d.", j);
-
- j = usb_unlink_urb(cam->sts_urb[i]);
- if (j < 0)
- err("usb_unlink_urb() error %d.", j);
+ usb_kill_urb(uvd->sbuf[i].urb);
+ usb_kill_urb(cam->sts_urb[i]);
}
if (!uvd->remove_pending) {
diff --git a/drivers/usb/media/ov511.c b/drivers/usb/media/ov511.c
index d040c8e30b6b..7de03331289e 100644
--- a/drivers/usb/media/ov511.c
+++ b/drivers/usb/media/ov511.c
@@ -3830,7 +3830,7 @@ ov51x_unlink_isoc(struct usb_ov511 *ov)
/* Unschedule all of the iso td's */
for (n = OV511_NUMSBUF - 1; n >= 0; n--) {
if (ov->sbuf[n].urb) {
- usb_unlink_urb(ov->sbuf[n].urb);
+ usb_kill_urb(ov->sbuf[n].urb);
usb_free_urb(ov->sbuf[n].urb);
ov->sbuf[n].urb = NULL;
}
diff --git a/drivers/usb/media/se401.c b/drivers/usb/media/se401.c
index c694caaac447..37d28640c790 100644
--- a/drivers/usb/media/se401.c
+++ b/drivers/usb/media/se401.c
@@ -514,7 +514,7 @@ static int se401_stop_stream(struct usb_se401 *se401)
se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0);
for (i=0; i<SE401_NUMSBUF; i++) if (se401->urb[i]) {
- usb_unlink_urb(se401->urb[i]);
+ usb_kill_urb(se401->urb[i]);
usb_free_urb(se401->urb[i]);
se401->urb[i]=NULL;
kfree(se401->sbuf[i].data);
@@ -883,7 +883,7 @@ static void usb_se401_remove_disconnected (struct usb_se401 *se401)
se401->dev = NULL;
for (i=0; i<SE401_NUMSBUF; i++) if (se401->urb[i]) {
- usb_unlink_urb(se401->urb[i]);
+ usb_kill_urb(se401->urb[i]);
usb_free_urb(se401->urb[i]);
se401->urb[i] = NULL;
kfree(se401->sbuf[i].data);
@@ -892,7 +892,7 @@ static void usb_se401_remove_disconnected (struct usb_se401 *se401)
kfree(se401->scratch[i].data);
}
if (se401->inturb) {
- usb_unlink_urb(se401->inturb);
+ usb_kill_urb(se401->inturb);
usb_free_urb(se401->inturb);
}
info("%s disconnected", se401->camera_name);
diff --git a/drivers/usb/media/sn9c102.h b/drivers/usb/media/sn9c102.h
index 62ff2144392c..a682dfc2f747 100644
--- a/drivers/usb/media/sn9c102.h
+++ b/drivers/usb/media/sn9c102.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * V4L2 driver for SN9C10[12] PC Camera Controllers *
+ * V4L2 driver for SN9C10x PC Camera Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
@@ -49,15 +49,21 @@
/*****************************************************************************/
-#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10[12] PC Camera Controllers"
+#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10x PC Camera Controllers"
#define SN9C102_MODULE_AUTHOR "(C) 2004 Luca Risolia"
#define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define SN9C102_MODULE_LICENSE "GPL"
-#define SN9C102_MODULE_VERSION "1:1.08"
-#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 8)
+#define SN9C102_MODULE_VERSION "1:1.12"
+#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 12)
-SN9C102_ID_TABLE;
-SN9C102_SENSOR_TABLE;
+enum sn9c102_bridge {
+ BRIDGE_SN9C101 = 0x01,
+ BRIDGE_SN9C102 = 0x02,
+ BRIDGE_SN9C103 = 0x04,
+};
+
+SN9C102_ID_TABLE
+SN9C102_SENSOR_TABLE
enum sn9c102_frame_state {
F_UNUSED,
@@ -105,6 +111,7 @@ struct sn9c102_device {
struct video_device* v4ldev;
+ enum sn9c102_bridge bridge;
struct sn9c102_sensor* sensor;
struct usb_device* usbdev;
diff --git a/drivers/usb/media/sn9c102_core.c b/drivers/usb/media/sn9c102_core.c
index 9f775f74002c..6d428a5b8d03 100644
--- a/drivers/usb/media/sn9c102_core.c
+++ b/drivers/usb/media/sn9c102_core.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * V4L2 driver for SN9C10[12] PC Camera Controllers *
+ * V4L2 driver for SN9C10x PC Camera Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
@@ -169,15 +169,15 @@ static u32 sn9c102_request_buffers(struct sn9c102_device* cam, u32 count)
cam->nbuffers = count;
while (cam->nbuffers > 0) {
- if ((buff = rvmalloc(cam->nbuffers * imagesize)))
+ if ((buff = rvmalloc(cam->nbuffers * PAGE_ALIGN(imagesize))))
break;
cam->nbuffers--;
}
for (i = 0; i < cam->nbuffers; i++) {
- cam->frame[i].bufmem = buff + i*imagesize;
+ cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize);
cam->frame[i].buf.index = i;
- cam->frame[i].buf.m.offset = i*imagesize;
+ cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize);
cam->frame[i].buf.length = imagesize;
cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
cam->frame[i].buf.sequence = 0;
@@ -388,7 +388,7 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
data[4] = data3;
data[5] = data4;
data[6] = data5;
- data[7] = 0x10;
+ data[7] = 0x14;
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
if (res < 0)
@@ -400,7 +400,7 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
if (err)
DBG(3, "I2C write failed for %s image sensor", sensor->name)
- PDBGG("I2C write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, "
+ PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, "
"data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X",
n, data0, data1, data2, data3, data4, data5)
@@ -634,7 +634,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam)
struct usb_device *udev = cam->usbdev;
struct urb* urb;
const unsigned int wMaxPacketSize[] = {0, 128, 256, 384, 512,
- 680, 800, 900, 1023};
+ 680, 800, 900, 1023};
const unsigned int psz = wMaxPacketSize[SN9C102_ALTERNATE_SETTING];
s8 i, j;
int err = 0;
@@ -965,6 +965,11 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf)
return -ENODEV;
}
+ if (cam->sensor->slave_read_id == SN9C102_I2C_SLAVEID_UNAVAILABLE) {
+ up(&sn9c102_sysfs_lock);
+ return -ENOSYS;
+ }
+
if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) {
up(&sn9c102_sysfs_lock);
return -EIO;
@@ -1022,15 +1027,79 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
static ssize_t
sn9c102_store_green(struct class_device* cd, const char* buf, size_t len)
{
+ struct sn9c102_device* cam;
+ enum sn9c102_bridge bridge;
ssize_t res = 0;
u8 value;
ssize_t count;
+ if (down_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ up(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ bridge = cam->bridge;
+
+ up(&sn9c102_sysfs_lock);
+
value = sn9c102_strtou8(buf, len, &count);
- if (!count || value > 0x0f)
+ if (!count)
return -EINVAL;
- if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0)
+ switch (bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ if (value > 0x0f)
+ return -EINVAL;
+ if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0)
+ res = sn9c102_store_val(cd, buf, len);
+ break;
+ case BRIDGE_SN9C103:
+ if (value > 0x7f)
+ return -EINVAL;
+ if ((res = sn9c102_store_reg(cd, "0x04", 4)) >= 0)
+ res = sn9c102_store_val(cd, buf, len);
+ break;
+ }
+
+ return res;
+}
+
+
+static ssize_t
+sn9c102_store_blue(struct class_device* cd, const char* buf, size_t len)
+{
+ ssize_t res = 0;
+ u8 value;
+ ssize_t count;
+
+ value = sn9c102_strtou8(buf, len, &count);
+ if (!count || value > 0x7f)
+ return -EINVAL;
+
+ if ((res = sn9c102_store_reg(cd, "0x06", 4)) >= 0)
+ res = sn9c102_store_val(cd, buf, len);
+
+ return res;
+}
+
+
+static ssize_t
+sn9c102_store_red(struct class_device* cd, const char* buf, size_t len)
+{
+ ssize_t res = 0;
+ u8 value;
+ ssize_t count;
+
+ value = sn9c102_strtou8(buf, len, &count);
+ if (!count || value > 0x7f)
+ return -EINVAL;
+
+ if ((res = sn9c102_store_reg(cd, "0x05", 4)) >= 0)
res = sn9c102_store_val(cd, buf, len);
return res;
@@ -1046,6 +1115,8 @@ static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
sn9c102_show_i2c_val, sn9c102_store_i2c_val);
static CLASS_DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green);
+static CLASS_DEVICE_ATTR(blue, S_IWUGO, NULL, sn9c102_store_blue);
+static CLASS_DEVICE_ATTR(red, S_IWUGO, NULL, sn9c102_store_red);
static void sn9c102_create_sysfs(struct sn9c102_device* cam)
@@ -1054,8 +1125,14 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam)
video_device_create_file(v4ldev, &class_device_attr_reg);
video_device_create_file(v4ldev, &class_device_attr_val);
- video_device_create_file(v4ldev, &class_device_attr_green);
- if (cam->sensor->slave_write_id && cam->sensor->slave_read_id) {
+ if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102)
+ video_device_create_file(v4ldev, &class_device_attr_green);
+ else if (cam->bridge == BRIDGE_SN9C103) {
+ video_device_create_file(v4ldev, &class_device_attr_blue);
+ video_device_create_file(v4ldev, &class_device_attr_red);
+ }
+ if (cam->sensor->slave_write_id != SN9C102_I2C_SLAVEID_UNAVAILABLE ||
+ cam->sensor->slave_read_id != SN9C102_I2C_SLAVEID_UNAVAILABLE) {
video_device_create_file(v4ldev, &class_device_attr_i2c_reg);
video_device_create_file(v4ldev, &class_device_attr_i2c_val);
}
@@ -1092,21 +1169,13 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
u8 h_start = (u8)(rect->left - s->cropcap.bounds.left),
v_start = (u8)(rect->top - s->cropcap.bounds.top),
h_size = (u8)(rect->width / 16),
- v_size = (u8)(rect->height / 16),
- ae_strx = 0x00,
- ae_stry = 0x00,
- ae_endx = h_size / 2,
- ae_endy = v_size / 2;
+ v_size = (u8)(rect->height / 16);
int err = 0;
err += sn9c102_write_reg(cam, h_start, 0x12);
err += sn9c102_write_reg(cam, v_start, 0x13);
err += sn9c102_write_reg(cam, h_size, 0x15);
err += sn9c102_write_reg(cam, v_size, 0x16);
- err += sn9c102_write_reg(cam, ae_strx, 0x1c);
- err += sn9c102_write_reg(cam, ae_stry, 0x1d);
- err += sn9c102_write_reg(cam, ae_endx, 0x1e);
- err += sn9c102_write_reg(cam, ae_endy, 0x1f);
if (err)
return -EIO;
@@ -1636,16 +1705,21 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
return -EFAULT;
- if ((err = s->set_ctrl(cam, &ctrl)))
- return err;
-
n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
for (i = 0; i < n; i++)
if (ctrl.id == s->qctrl[i].id) {
- s->_qctrl[i].default_value = ctrl.value;
+ if (ctrl.value < s->qctrl[i].minimum ||
+ ctrl.value > s->qctrl[i].maximum)
+ return -ERANGE;
+ ctrl.value -= ctrl.value % s->qctrl[i].step;
break;
}
+ if ((err = s->set_ctrl(cam, &ctrl)))
+ return err;
+
+ s->_qctrl[i].default_value = ctrl.value;
+
return 0;
}
@@ -1776,7 +1850,7 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
DBG(1, "VIDIOC_S_CROP failed because of hardware "
"problems. To use the camera, close and open "
"/dev/video%d again.", cam->v4ldev->minor)
- return err;
+ return -EIO;
}
s->pix_format.width = rect->width/scale;
@@ -1951,7 +2025,7 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
DBG(1, "VIDIOC_S_FMT failed because of hardware "
"problems. To use the camera, close and open "
"/dev/video%d again.", cam->v4ldev->minor)
- return err;
+ return -EIO;
}
memcpy(pfmt, pix, sizeof(*pix));
@@ -2286,16 +2360,28 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
r = sn9c102_read_reg(cam, 0x00);
if (r < 0 || r != 0x10) {
- DBG(1, "Sorry, this is not a SN9C10[12] based camera "
+ DBG(1, "Sorry, this is not a SN9C10x based camera "
"(vid/pid 0x%04X/0x%04X)",
sn9c102_id_table[i].idVendor,sn9c102_id_table[i].idProduct)
err = -ENODEV;
goto fail;
}
- DBG(2, "SN9C10[12] PC Camera Controller detected "
- "(vid/pid 0x%04X/0x%04X)",
- sn9c102_id_table[i].idVendor, sn9c102_id_table[i].idProduct)
+ cam->bridge = (sn9c102_id_table[i].idProduct & 0xffc0) == 0x6080 ?
+ BRIDGE_SN9C103 : BRIDGE_SN9C102;
+ switch (cam->bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ DBG(2, "SN9C10[12] PC Camera Controller detected "
+ "(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor,
+ sn9c102_id_table[i].idProduct)
+ break;
+ case BRIDGE_SN9C103:
+ DBG(2, "SN9C103 PC Camera Controller detected "
+ "(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor,
+ sn9c102_id_table[i].idProduct)
+ break;
+ }
for (i = 0; sn9c102_sensor_table[i]; i++) {
err = sn9c102_sensor_table[i](cam);
@@ -2318,7 +2404,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
cam->state |= DEV_MISCONFIGURED;
}
- strcpy(cam->v4ldev->name, "SN9C10[12] PC Camera");
+ strcpy(cam->v4ldev->name, "SN9C10x PC Camera");
cam->v4ldev->owner = THIS_MODULE;
cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
cam->v4ldev->hardware = VID_HARDWARE_SN9C102;
diff --git a/drivers/usb/media/sn9c102_pas106b.c b/drivers/usb/media/sn9c102_pas106b.c
index a302a4845893..8453409ea125 100644
--- a/drivers/usb/media/sn9c102_pas106b.c
+++ b/drivers/usb/media/sn9c102_pas106b.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Driver for PAS106B image sensor connected to the SN9C10[12] PC Camera *
+ * Driver for PAS106B image sensor connected to the SN9C10x PC Camera *
* Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
@@ -100,26 +100,26 @@ static int pas106b_set_ctrl(struct sn9c102_device* cam,
switch (ctrl->id) {
case V4L2_CID_RED_BALANCE:
- err += sn9c102_i2c_write(cam, 0x0c, ctrl->value & 0x1f);
+ err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
break;
case V4L2_CID_BLUE_BALANCE:
- err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x1f);
+ err += sn9c102_i2c_write(cam, 0x09, ctrl->value);
break;
case V4L2_CID_GAIN:
- err += sn9c102_i2c_write(cam, 0x0e, ctrl->value & 0x1f);
+ err += sn9c102_i2c_write(cam, 0x0e, ctrl->value);
break;
case V4L2_CID_BRIGHTNESS:
- err += sn9c102_i2c_write(cam, 0x0d, 0x1f-(ctrl->value & 0x1f));
+ err += sn9c102_i2c_write(cam, 0x0d, 0x1f - ctrl->value);
break;
case V4L2_CID_CONTRAST:
- err += sn9c102_i2c_write(cam, 0x0f, ctrl->value & 0x03);
+ err += sn9c102_i2c_write(cam, 0x0f, ctrl->value);
break;
default:
return -EINVAL;
}
err += sn9c102_i2c_write(cam, 0x13, 0x01);
- return err;
+ return err ? -EIO : 0;
}
diff --git a/drivers/usb/media/sn9c102_pas202bcb.c b/drivers/usb/media/sn9c102_pas202bcb.c
index 26944eaf85d0..72063e885871 100644
--- a/drivers/usb/media/sn9c102_pas202bcb.c
+++ b/drivers/usb/media/sn9c102_pas202bcb.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Driver for PAS202BCB image sensor connected to the SN9C10[12] PC Camera *
+ * Driver for PAS202BCB image sensor connected to the SN9C10x PC Camera *
* Controllers *
* *
* Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio *
@@ -36,18 +36,19 @@ static int pas202bcb_init(struct sn9c102_device* cam)
err += sn9c102_write_reg(cam, 0x00, 0x11);
err += sn9c102_write_reg(cam, 0x00, 0x14);
err += sn9c102_write_reg(cam, 0x20, 0x17);
- err += sn9c102_write_reg(cam, 0x20, 0x19);
+ err += sn9c102_write_reg(cam, 0x30, 0x19);
err += sn9c102_write_reg(cam, 0x09, 0x18);
- err += sn9c102_i2c_write(cam, 0x02, 0x0c);
+ err += sn9c102_i2c_write(cam, 0x02, 0x14);
err += sn9c102_i2c_write(cam, 0x03, 0x40);
err += sn9c102_i2c_write(cam, 0x04, 0x07);
err += sn9c102_i2c_write(cam, 0x05, 0x25);
err += sn9c102_i2c_write(cam, 0x0d, 0x2c);
err += sn9c102_i2c_write(cam, 0x0e, 0x01);
err += sn9c102_i2c_write(cam, 0x0f, 0xa9);
- err += sn9c102_i2c_write(cam, 0x08, 0x01);
+ err += sn9c102_i2c_write(cam, 0x10, 0x08);
err += sn9c102_i2c_write(cam, 0x0b, 0x01);
+ err += sn9c102_i2c_write(cam, 0x0c, 0x04);
err += sn9c102_i2c_write(cam, 0x13, 0x63);
err += sn9c102_i2c_write(cam, 0x15, 0x70);
err += sn9c102_i2c_write(cam, 0x11, 0x01);
@@ -95,23 +96,23 @@ static int pas202bcb_set_ctrl(struct sn9c102_device* cam,
switch (ctrl->id) {
case V4L2_CID_RED_BALANCE:
- err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x0f);
+ err += sn9c102_i2c_write(cam, 0x09, ctrl->value);
break;
case V4L2_CID_BLUE_BALANCE:
- err += sn9c102_i2c_write(cam, 0x07, ctrl->value & 0x0f);
+ err += sn9c102_i2c_write(cam, 0x07, ctrl->value);
break;
case V4L2_CID_GAIN:
- err += sn9c102_i2c_write(cam, 0x10, ctrl->value & 0x1f);
+ err += sn9c102_i2c_write(cam, 0x10, ctrl->value);
break;
case V4L2_CID_BRIGHTNESS:
- err += sn9c102_i2c_write(cam, 0x06, 0x0f-(ctrl->value & 0x0f));
+ err += sn9c102_i2c_write(cam, 0x06, 0x0f - ctrl->value);
break;
default:
return -EINVAL;
}
err += sn9c102_i2c_write(cam, 0x11, 0x01);
- return err;
+ return err ? -EIO : 0;
}
@@ -217,7 +218,7 @@ int sn9c102_probe_pas202bcb(struct sn9c102_device* cam)
* NOTE: do NOT change the values!
*/
err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */
- err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */
+ err += sn9c102_write_reg(cam, 0x40, 0x01); /* sensor power on */
err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */
if (err)
return -EIO;
diff --git a/drivers/usb/media/sn9c102_sensor.h b/drivers/usb/media/sn9c102_sensor.h
index 3e7e4a2578bc..01bcad14ca6d 100644
--- a/drivers/usb/media/sn9c102_sensor.h
+++ b/drivers/usb/media/sn9c102_sensor.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * API for image sensors connected to the SN9C10[12] PC Camera Controllers *
+ * API for image sensors connected to the SN9C10x PC Camera Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
@@ -89,17 +89,44 @@ sn9c102_attach_sensor(struct sn9c102_device* cam,
/* Each SN9C10X camera has proper PID/VID identifiers. Add them here in case.*/
#define SN9C102_ID_TABLE \
static const struct usb_device_id sn9c102_id_table[] = { \
- { USB_DEVICE(0xc45, 0x6001), }, /* TAS5110C1B */ \
- { USB_DEVICE(0xc45, 0x6005), }, /* TAS5110C1B */ \
- { USB_DEVICE(0xc45, 0x6009), }, /* PAS106B */ \
- { USB_DEVICE(0xc45, 0x600d), }, /* PAS106B */ \
- { USB_DEVICE(0xc45, 0x6024), }, \
- { USB_DEVICE(0xc45, 0x6025), }, /* TAS5130D1B and TAS5110C1B */ \
- { USB_DEVICE(0xc45, 0x6028), }, /* PAS202BCB */ \
- { USB_DEVICE(0xc45, 0x6029), }, /* PAS106B */ \
- { USB_DEVICE(0xc45, 0x602a), }, /* HV7131[D|E1] */ \
- { USB_DEVICE(0xc45, 0x602c), }, /* OV7620 */ \
- { USB_DEVICE(0xc45, 0x6030), }, /* MI03 */ \
+ { USB_DEVICE(0x0c45, 0x6001), }, /* TAS5110C1B */ \
+ { USB_DEVICE(0x0c45, 0x6005), }, /* TAS5110C1B */ \
+ { USB_DEVICE(0x0c45, 0x6009), }, /* PAS106B */ \
+ { USB_DEVICE(0x0c45, 0x600d), }, /* PAS106B */ \
+ { USB_DEVICE(0x0c45, 0x6024), }, \
+ { USB_DEVICE(0x0c45, 0x6025), }, /* TAS5130D1B and TAS5110C1B */ \
+ { USB_DEVICE(0x0c45, 0x6028), }, /* PAS202BCB */ \
+ { USB_DEVICE(0x0c45, 0x6029), }, /* PAS106B */ \
+ { USB_DEVICE(0x0c45, 0x602a), }, /* HV7131[D|E1] */ \
+ { USB_DEVICE(0x0c45, 0x602b), }, \
+ { USB_DEVICE(0x0c45, 0x602c), }, /* OV7620 */ \
+ { USB_DEVICE(0x0c45, 0x6030), }, /* MI03x */ \
+ { USB_DEVICE(0x0c45, 0x6080), }, \
+ { USB_DEVICE(0x0c45, 0x6082), }, /* MI0343 and MI0360 */ \
+ { USB_DEVICE(0x0c45, 0x6083), }, /* HV7131[D|E1] */ \
+ { USB_DEVICE(0x0c45, 0x6088), }, \
+ { USB_DEVICE(0x0c45, 0x608a), }, \
+ { USB_DEVICE(0x0c45, 0x608b), }, \
+ { USB_DEVICE(0x0c45, 0x608c), }, /* HV7131x */ \
+ { USB_DEVICE(0x0c45, 0x608e), }, /* CIS-VF10 */ \
+ { USB_DEVICE(0x0c45, 0x608f), }, /* OV7630 */ \
+ { USB_DEVICE(0x0c45, 0x60a0), }, \
+ { USB_DEVICE(0x0c45, 0x60a2), }, \
+ { USB_DEVICE(0x0c45, 0x60a3), }, \
+ { USB_DEVICE(0x0c45, 0x60a8), }, /* PAS106B */ \
+ { USB_DEVICE(0x0c45, 0x60aa), }, /* TAS5130D1B */ \
+ { USB_DEVICE(0x0c45, 0x60ab), }, /* TAS5110C1B */ \
+ { USB_DEVICE(0x0c45, 0x60ac), }, \
+ { USB_DEVICE(0x0c45, 0x60ae), }, \
+ { USB_DEVICE(0x0c45, 0x60af), }, /* PAS202BCB */ \
+ { USB_DEVICE(0x0c45, 0x60b0), }, \
+ { USB_DEVICE(0x0c45, 0x60b2), }, \
+ { USB_DEVICE(0x0c45, 0x60b3), }, \
+ { USB_DEVICE(0x0c45, 0x60b8), }, \
+ { USB_DEVICE(0x0c45, 0x60ba), }, \
+ { USB_DEVICE(0x0c45, 0x60bb), }, \
+ { USB_DEVICE(0x0c45, 0x60bc), }, \
+ { USB_DEVICE(0x0c45, 0x60be), }, \
{ } \
};
@@ -159,6 +186,9 @@ enum sn9c102_i2c_interface {
SN9C102_I2C_3WIRES,
};
+#define SN9C102_I2C_SLAVEID_FICTITIOUS 0xff
+#define SN9C102_I2C_SLAVEID_UNAVAILABLE 0x00
+
struct sn9c102_sensor {
char name[32], /* sensor name */
maintainer[64]; /* name of the mantainer <email> */
@@ -173,9 +203,7 @@ struct sn9c102_sensor {
/*
These identifiers must be provided if the image sensor implements
- the standard I2C protocol. TASC sensors don't, although they have a
- serial interface: so this is a case where the "raw" I2C version
- could be helpful.
+ the standard I2C protocol.
*/
u8 slave_read_id, slave_write_id; /* reg. 0x09 */
@@ -214,7 +242,8 @@ struct sn9c102_sensor {
the list above. The returned value must follow the V4L2
specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER
are not supported by this driver, so do not implement them. Also,
- passed values are NOT checked to see if they are out of bounds.
+ you don't have to check whether the passed values are out of bounds,
+ given that this is done by the core module.
*/
struct v4l2_cropcap cropcap;
diff --git a/drivers/usb/media/sn9c102_tas5110c1b.c b/drivers/usb/media/sn9c102_tas5110c1b.c
index 68e1b2e0ce18..ce8b47b59a75 100644
--- a/drivers/usb/media/sn9c102_tas5110c1b.c
+++ b/drivers/usb/media/sn9c102_tas5110c1b.c
@@ -1,6 +1,6 @@
/***************************************************************************
- * Driver for TAS5110C1B image sensor connected to the SN9C10[12] PC *
- * Camera Controllers *
+ * Driver for TAS5110C1B image sensor connected to the SN9C10x PC Camera *
+ * Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
@@ -24,6 +24,8 @@
static struct sn9c102_sensor tas5110c1b;
+static struct v4l2_control tas5110c1b_gain;
+
static int tas5110c1b_init(struct sn9c102_device* cam)
{
@@ -38,25 +40,42 @@ static int tas5110c1b_init(struct sn9c102_device* cam)
err += sn9c102_write_reg(cam, 0x06, 0x18);
err += sn9c102_write_reg(cam, 0xfb, 0x19);
- err += sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11, 0x00, 0xc0,
- 0x80, 0, 0);
+ err += sn9c102_i2c_write(cam, 0xc0, 0x80);
return err;
}
+static int tas5110c1b_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ ctrl->value = tas5110c1b_gain.value;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
static int tas5110c1b_set_ctrl(struct sn9c102_device* cam,
const struct v4l2_control* ctrl)
{
+ int err = 0;
+
switch (ctrl->id) {
case V4L2_CID_GAIN:
- return sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11,
- 0x02, 0x20,
- 0xff - (ctrl->value & 0xff),
- 0, 0);
+ if (!(err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value)))
+ tas5110c1b_gain.value = ctrl->value;
+ break;
default:
return -EINVAL;
}
+
+ return err ? -EIO : 0;
}
@@ -85,6 +104,8 @@ static struct sn9c102_sensor tas5110c1b = {
.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
.frequency = SN9C102_I2C_100KHZ,
.interface = SN9C102_I2C_3WIRES,
+ .slave_read_id = SN9C102_I2C_SLAVEID_UNAVAILABLE,
+ .slave_write_id = SN9C102_I2C_SLAVEID_FICTITIOUS,
.init = &tas5110c1b_init,
.qctrl = {
{
@@ -92,9 +113,9 @@ static struct sn9c102_sensor tas5110c1b = {
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "global gain",
.minimum = 0x00,
- .maximum = 0xff,
+ .maximum = 0xf6,
.step = 0x01,
- .default_value = 0x48,
+ .default_value = 0x40,
.flags = 0,
},
},
@@ -113,6 +134,7 @@ static struct sn9c102_sensor tas5110c1b = {
.height = 288,
},
},
+ .get_ctrl = &tas5110c1b_get_ctrl,
.set_crop = &tas5110c1b_set_crop,
.pix_format = {
.width = 352,
@@ -130,7 +152,8 @@ int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam)
/* At the moment, sensor detection is based on USB pid/vid */
if (tas5110c1b.usbdev->descriptor.idProduct != 0x6001 &&
- tas5110c1b.usbdev->descriptor.idProduct != 0x6005)
+ tas5110c1b.usbdev->descriptor.idProduct != 0x6005 &&
+ tas5110c1b.usbdev->descriptor.idProduct != 0x60ab)
return -ENODEV;
return 0;
diff --git a/drivers/usb/media/sn9c102_tas5130d1b.c b/drivers/usb/media/sn9c102_tas5130d1b.c
index 0bab19435399..0048e9c20d80 100644
--- a/drivers/usb/media/sn9c102_tas5130d1b.c
+++ b/drivers/usb/media/sn9c102_tas5130d1b.c
@@ -1,6 +1,6 @@
/***************************************************************************
- * Driver for TAS5130D1B image sensor connected to the SN9C10[12] PC *
- * Camera Controllers *
+ * Driver for TAS5130D1B image sensor connected to the SN9C10x PC Camera *
+ * Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
@@ -24,6 +24,8 @@
static struct sn9c102_sensor tas5130d1b;
+static struct v4l2_control tas5130d1b_gain, tas5130d1b_exposure;
+
static int tas5130d1b_init(struct sn9c102_device* cam)
{
@@ -38,25 +40,47 @@ static int tas5130d1b_init(struct sn9c102_device* cam)
err += sn9c102_write_reg(cam, 0x60, 0x17);
err += sn9c102_write_reg(cam, 0x07, 0x18);
- err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x40,
- 0x47, 0, 0);
-
return err;
}
+static int tas5130d1b_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ ctrl->value = tas5130d1b_gain.value;
+ break;
+ case V4L2_CID_EXPOSURE:
+ ctrl->value = tas5130d1b_exposure.value;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
static int tas5130d1b_set_ctrl(struct sn9c102_device* cam,
const struct v4l2_control* ctrl)
{
+ int err = 0;
+
switch (ctrl->id) {
case V4L2_CID_GAIN:
- return sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11,
- 0x02, 0x20,
- 0xff - (ctrl->value & 0xff),
- 0, 0);
+ if (!(err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value)))
+ tas5130d1b_gain.value = ctrl->value;
+ break;
+ case V4L2_CID_EXPOSURE:
+ if (!(err += sn9c102_i2c_write(cam, 0x40, 0x47 - ctrl->value)))
+ tas5130d1b_exposure.value = ctrl->value;
+ break;
default:
return -EINVAL;
}
+
+ return err ? -EIO : 0;
}
@@ -85,6 +109,8 @@ static struct sn9c102_sensor tas5130d1b = {
.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
.frequency = SN9C102_I2C_100KHZ,
.interface = SN9C102_I2C_3WIRES,
+ .slave_read_id = SN9C102_I2C_SLAVEID_UNAVAILABLE,
+ .slave_write_id = SN9C102_I2C_SLAVEID_FICTITIOUS,
.init = &tas5130d1b_init,
.qctrl = {
{
@@ -92,12 +118,23 @@ static struct sn9c102_sensor tas5130d1b = {
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "global gain",
.minimum = 0x00,
- .maximum = 0xff,
+ .maximum = 0xf6,
+ .step = 0x02,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0x47,
.step = 0x01,
.default_value = 0x00,
.flags = 0,
},
},
+ .get_ctrl = &tas5130d1b_get_ctrl,
.set_ctrl = &tas5130d1b_set_ctrl,
.cropcap = {
.bounds = {
@@ -129,7 +166,8 @@ int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam)
sn9c102_attach_sensor(cam, &tas5130d1b);
/* At the moment, sensor detection is based on USB pid/vid */
- if (tas5130d1b.usbdev->descriptor.idProduct != 0x6025)
+ if (tas5130d1b.usbdev->descriptor.idProduct != 0x6025 &&
+ tas5130d1b.usbdev->descriptor.idProduct != 0x60aa)
return -ENODEV;
return 0;
diff --git a/drivers/usb/media/stv680.c b/drivers/usb/media/stv680.c
index cd73a45d0dd0..a91870f0b412 100644
--- a/drivers/usb/media/stv680.c
+++ b/drivers/usb/media/stv680.c
@@ -725,7 +725,7 @@ static int stv680_stop_stream (struct usb_stv *stv680)
for (i = 0; i < STV680_NUMSBUF; i++)
if (stv680->urb[i]) {
- usb_unlink_urb (stv680->urb[i]);
+ usb_kill_urb (stv680->urb[i]);
usb_free_urb (stv680->urb[i]);
stv680->urb[i] = NULL;
kfree (stv680->sbuf[i].data);
@@ -1457,7 +1457,7 @@ static inline void usb_stv680_remove_disconnected (struct usb_stv *stv680)
for (i = 0; i < STV680_NUMSBUF; i++)
if (stv680->urb[i]) {
- usb_unlink_urb (stv680->urb[i]);
+ usb_kill_urb (stv680->urb[i]);
usb_free_urb (stv680->urb[i]);
stv680->urb[i] = NULL;
kfree (stv680->sbuf[i].data);
diff --git a/drivers/usb/media/usbvideo.c b/drivers/usb/media/usbvideo.c
index 1107e398d6f3..122b7accf1e1 100644
--- a/drivers/usb/media/usbvideo.c
+++ b/drivers/usb/media/usbvideo.c
@@ -1910,9 +1910,7 @@ static void usbvideo_StopDataPump(struct uvd *uvd)
/* Unschedule all of the iso td's */
for (i=0; i < USBVIDEO_NUMSBUF; i++) {
- j = usb_unlink_urb(uvd->sbuf[i].urb);
- if (j < 0)
- err("%s: usb_unlink_urb() error %d.", __FUNCTION__, j);
+ usb_kill_urb(uvd->sbuf[i].urb);
}
if (uvd->debug > 1)
info("%s: streaming=0", __FUNCTION__);
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 8c958af4a1d3..d6401c0a80d5 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -121,18 +121,6 @@ config USB_CYTHERM
To compile this driver as a module, choose M here: the
module will be called cytherm.
-config USB_SPEEDTOUCH
- tristate "Alcatel Speedtouch USB support"
- depends on USB && ATM
- select CRC32
- help
- Say Y here if you have an Alcatel SpeedTouch USB or SpeedTouch 330
- modem. In order to use your modem you will need to install some user
- space tools, see <http://www.linux-usb.org/SpeedTouch/> for details.
-
- To compile this driver as a module, choose M here: the
- module will be called speedtch.
-
config USB_PHIDGETSERVO
tristate "USB PhidgetServo support"
depends on USB
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index d641a470e8d7..074a1d66250b 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -11,7 +11,6 @@ obj-$(CONFIG_USB_LCD) += usblcd.o
obj-$(CONFIG_USB_LED) += usbled.o
obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o
obj-$(CONFIG_USB_RIO500) += rio500.o
-obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o
obj-$(CONFIG_USB_TEST) += usbtest.o
obj-$(CONFIG_USB_TIGL) += tiglusb.o
obj-$(CONFIG_USB_USS720) += uss720.o
diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c
index c4bebdacda5c..94842c34a4c4 100644
--- a/drivers/usb/misc/auerswald.c
+++ b/drivers/usb/misc/auerswald.c
@@ -516,7 +516,7 @@ static void auerchain_unlink_all (pauerchain_t acp)
urbp = acep->urbp;
urbp->transfer_flags &= ~URB_ASYNC_UNLINK;
dbg ("unlink active urb");
- usb_unlink_urb (urbp);
+ usb_kill_urb (urbp);
}
}
@@ -1171,22 +1171,16 @@ intoend:
endpoint. This function returns 0 if successful or an error code.
NOTE: no mutex please!
*/
-static int auerswald_int_release (pauerswald_t cp)
+static void auerswald_int_release (pauerswald_t cp)
{
- int ret = 0;
dbg ("auerswald_int_release");
/* stop the int endpoint */
- if (cp->inturbp) {
- ret = usb_unlink_urb (cp->inturbp);
- if (ret)
- dbg ("nonzero int unlink result received: %d", ret);
- }
+ if (cp->inturbp)
+ usb_kill_urb (cp->inturbp);
/* deallocate memory */
auerswald_int_free (cp);
-
- return ret;
}
/* --------------------------------------------------------------------- */
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 06c4bfa2f4b9..e83e91148970 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -505,12 +505,12 @@ static void tower_abort_transfers (struct lego_usb_tower *dev)
dev->interrupt_in_running = 0;
mb();
if (dev->interrupt_in_urb != NULL && dev->udev) {
- usb_unlink_urb (dev->interrupt_in_urb);
+ usb_kill_urb (dev->interrupt_in_urb);
}
}
if (dev->interrupt_out_busy) {
if (dev->interrupt_out_urb != NULL && dev->udev) {
- usb_unlink_urb (dev->interrupt_out_urb);
+ usb_kill_urb (dev->interrupt_out_urb);
}
}
diff --git a/drivers/usb/misc/speedtch.c b/drivers/usb/misc/speedtch.c
deleted file mode 100644
index 667e2d1b227c..000000000000
--- a/drivers/usb/misc/speedtch.c
+++ /dev/null
@@ -1,1373 +0,0 @@
-/******************************************************************************
- * speedtouch.c - Alcatel SpeedTouch USB xDSL modem driver
- *
- * Copyright (C) 2001, Alcatel
- * Copyright (C) 2003, Duncan Sands
- *
- * 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.
- *
- ******************************************************************************/
-
-/*
- * Written by Johan Verrept, maintained by Duncan Sands (duncan.sands@free.fr)
- *
- * 1.7+: - See the check-in logs
- *
- * 1.6: - No longer opens a connection if the firmware is not loaded
- * - Added support for the speedtouch 330
- * - Removed the limit on the number of devices
- * - Module now autoloads on device plugin
- * - Merged relevant parts of sarlib
- * - Replaced the kernel thread with a tasklet
- * - New packet transmission code
- * - Changed proc file contents
- * - Fixed all known SMP races
- * - Many fixes and cleanups
- * - Various fixes by Oliver Neukum (oliver@neukum.name)
- *
- * 1.5A: - Version for inclusion in 2.5 series kernel
- * - Modifications by Richard Purdie (rpurdie@rpsys.net)
- * - made compatible with kernel 2.5.6 onwards by changing
- * udsl_usb_send_data_context->urb to a pointer and adding code
- * to alloc and free it
- * - remove_wait_queue() added to udsl_atm_processqueue_thread()
- *
- * 1.5: - fixed memory leak when atmsar_decode_aal5 returned NULL.
- * (reported by stephen.robinson@zen.co.uk)
- *
- * 1.4: - changed the spin_lock() under interrupt to spin_lock_irqsave()
- * - unlink all active send urbs of a vcc that is being closed.
- *
- * 1.3.1: - added the version number
- *
- * 1.3: - Added multiple send urb support
- * - fixed memory leak and vcc->tx_inuse starvation bug
- * when not enough memory left in vcc.
- *
- * 1.2: - Fixed race condition in udsl_usb_send_data()
- * 1.1: - Turned off packet debugging
- *
- */
-
-#include <asm/semaphore.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/errno.h>
-#include <linux/proc_fs.h>
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <asm/uaccess.h>
-#include <linux/smp_lock.h>
-#include <linux/interrupt.h>
-#include <linux/atm.h>
-#include <linux/atmdev.h>
-#include <linux/crc32.h>
-#include <linux/init.h>
-
-/*
-#define DEBUG
-#define VERBOSE_DEBUG
-*/
-
-#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
-# define DEBUG
-#endif
-
-#include <linux/usb.h>
-
-#ifdef DEBUG
-#define DEBUG_ON(x) BUG_ON(x)
-#else
-#define DEBUG_ON(x) do { if (x); } while (0)
-#endif
-
-#ifdef VERBOSE_DEBUG
-static int udsl_print_packet (const unsigned char *data, int len);
-#define PACKETDEBUG(arg...) udsl_print_packet (arg)
-#define vdbg(arg...) dbg (arg)
-#else
-#define PACKETDEBUG(arg...)
-#define vdbg(arg...)
-#endif
-
-#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
-#define DRIVER_VERSION "1.8"
-#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
-
-static const char udsl_driver_name [] = "speedtch";
-
-#define SPEEDTOUCH_VENDORID 0x06b9
-#define SPEEDTOUCH_PRODUCTID 0x4061
-
-#define UDSL_MAX_RCV_URBS 4
-#define UDSL_MAX_SND_URBS 4
-#define UDSL_MAX_RCV_BUFS 8
-#define UDSL_MAX_SND_BUFS 8
-#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */
-#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */
-#define UDSL_DEFAULT_RCV_URBS 2
-#define UDSL_DEFAULT_SND_URBS 2
-#define UDSL_DEFAULT_RCV_BUFS 4
-#define UDSL_DEFAULT_SND_BUFS 4
-#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */
-#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */
-
-static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS;
-static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS;
-static unsigned int num_rcv_bufs = UDSL_DEFAULT_RCV_BUFS;
-static unsigned int num_snd_bufs = UDSL_DEFAULT_SND_BUFS;
-static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE;
-static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE;
-
-module_param (num_rcv_urbs, uint, 0444);
-MODULE_PARM_DESC (num_rcv_urbs, "Number of urbs used for reception (range: 0-" __MODULE_STRING (UDSL_MAX_RCV_URBS) ", default: " __MODULE_STRING (UDSL_DEFAULT_RCV_URBS) ")");
-
-module_param (num_snd_urbs, uint, 0444);
-MODULE_PARM_DESC (num_snd_urbs, "Number of urbs used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_URBS) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_URBS) ")");
-
-module_param (num_rcv_bufs, uint, 0444);
-MODULE_PARM_DESC (num_rcv_bufs, "Number of buffers used for reception (range: 0-" __MODULE_STRING (UDSL_MAX_RCV_BUFS) ", default: " __MODULE_STRING (UDSL_DEFAULT_RCV_BUFS) ")");
-
-module_param (num_snd_bufs, uint, 0444);
-MODULE_PARM_DESC (num_snd_bufs, "Number of buffers used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_BUFS) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_BUFS) ")");
-
-module_param (rcv_buf_size, uint, 0444);
-MODULE_PARM_DESC (rcv_buf_size, "Size of the buffers used for reception (range: 0-" __MODULE_STRING (UDSL_MAX_RCV_BUF_SIZE) ", default: " __MODULE_STRING (UDSL_DEFAULT_RCV_BUF_SIZE) ")");
-
-module_param (snd_buf_size, uint, 0444);
-MODULE_PARM_DESC (snd_buf_size, "Size of the buffers used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_BUF_SIZE) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_BUF_SIZE) ")");
-
-#define UDSL_IOCTL_LINE_UP 1
-#define UDSL_IOCTL_LINE_DOWN 2
-
-#define UDSL_ENDPOINT_DATA_OUT 0x07
-#define UDSL_ENDPOINT_DATA_IN 0x87
-
-#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
-#define UDSL_NUM_CELLS(x) (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD)
-
-#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) )
-
-static struct usb_device_id udsl_usb_ids [] = {
- { USB_DEVICE (SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID) },
- { }
-};
-
-MODULE_DEVICE_TABLE (usb, udsl_usb_ids);
-
-/* receive */
-
-struct udsl_receive_buffer {
- struct list_head list;
- unsigned char *base;
- unsigned int filled_cells;
-};
-
-struct udsl_receiver {
- struct list_head list;
- struct udsl_receive_buffer *buffer;
- struct urb *urb;
- struct udsl_instance_data *instance;
-};
-
-struct udsl_vcc_data {
- /* vpi/vci lookup */
- struct list_head list;
- short vpi;
- int vci;
- struct atm_vcc *vcc;
-
- /* raw cell reassembly */
- struct sk_buff *sarb;
-};
-
-/* send */
-
-struct udsl_send_buffer {
- struct list_head list;
- unsigned char *base;
- unsigned char *free_start;
- unsigned int free_cells;
-};
-
-struct udsl_sender {
- struct list_head list;
- struct udsl_send_buffer *buffer;
- struct urb *urb;
- struct udsl_instance_data *instance;
-};
-
-struct udsl_control {
- struct atm_skb_data atm_data;
- unsigned int num_cells;
- unsigned int num_entire;
- unsigned int pdu_padding;
- unsigned char cell_header [ATM_CELL_HEADER];
- unsigned char aal5_trailer [ATM_AAL5_TRAILER];
-};
-
-#define UDSL_SKB(x) ((struct udsl_control *)(x)->cb)
-
-/* main driver data */
-
-struct udsl_instance_data {
- struct semaphore serialize;
-
- /* USB device part */
- struct usb_device *usb_dev;
- char description [64];
- int firmware_loaded;
-
- /* ATM device part */
- struct atm_dev *atm_dev;
- struct list_head vcc_list;
-
- /* receive */
- struct udsl_receiver receivers [UDSL_MAX_RCV_URBS];
- struct udsl_receive_buffer receive_buffers [UDSL_MAX_RCV_BUFS];
-
- spinlock_t receive_lock;
- struct list_head spare_receivers;
- struct list_head filled_receive_buffers;
-
- struct tasklet_struct receive_tasklet;
- struct list_head spare_receive_buffers;
-
- /* send */
- struct udsl_sender senders [UDSL_MAX_SND_URBS];
- struct udsl_send_buffer send_buffers [UDSL_MAX_SND_BUFS];
-
- struct sk_buff_head sndqueue;
-
- spinlock_t send_lock;
- struct list_head spare_senders;
- struct list_head spare_send_buffers;
-
- struct tasklet_struct send_tasklet;
- struct sk_buff *current_skb; /* being emptied */
- struct udsl_send_buffer *current_buffer; /* being filled */
- struct list_head filled_send_buffers;
-};
-
-/* ATM */
-
-static void udsl_atm_dev_close (struct atm_dev *dev);
-static int udsl_atm_open (struct atm_vcc *vcc);
-static void udsl_atm_close (struct atm_vcc *vcc);
-static int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void __user *arg);
-static int udsl_atm_send (struct atm_vcc *vcc, struct sk_buff *skb);
-static int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t *pos, char *page);
-
-static struct atmdev_ops udsl_atm_devops = {
- .dev_close = udsl_atm_dev_close,
- .open = udsl_atm_open,
- .close = udsl_atm_close,
- .ioctl = udsl_atm_ioctl,
- .send = udsl_atm_send,
- .proc_read = udsl_atm_proc_read,
- .owner = THIS_MODULE,
-};
-
-/* USB */
-
-static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_id *id);
-static void udsl_usb_disconnect (struct usb_interface *intf);
-static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data);
-
-static struct usb_driver udsl_usb_driver = {
- .owner = THIS_MODULE,
- .name = udsl_driver_name,
- .probe = udsl_usb_probe,
- .disconnect = udsl_usb_disconnect,
- .ioctl = udsl_usb_ioctl,
- .id_table = udsl_usb_ids,
-};
-
-
-/***********
-** misc **
-***********/
-
-static inline void udsl_pop (struct atm_vcc *vcc, struct sk_buff *skb)
-{
- if (vcc->pop)
- vcc->pop (vcc, skb);
- else
- dev_kfree_skb (skb);
-}
-
-
-/*************
-** decode **
-*************/
-
-static inline struct udsl_vcc_data *udsl_find_vcc (struct udsl_instance_data *instance, short vpi, int vci)
-{
- struct udsl_vcc_data *vcc;
-
- list_for_each_entry (vcc, &instance->vcc_list, list)
- if ((vcc->vci == vci) && (vcc->vpi == vpi))
- return vcc;
- return NULL;
-}
-
-static void udsl_extract_cells (struct udsl_instance_data *instance, unsigned char *source, unsigned int howmany)
-{
- struct udsl_vcc_data *cached_vcc = NULL;
- struct atm_vcc *vcc;
- struct sk_buff *sarb;
- struct udsl_vcc_data *vcc_data;
- int cached_vci = 0;
- unsigned int i;
- int pti;
- int vci;
- short cached_vpi = 0;
- short vpi;
-
- for (i = 0; i < howmany; i++, source += ATM_CELL_SIZE) {
- vpi = ((source [0] & 0x0f) << 4) | (source [1] >> 4);
- vci = ((source [1] & 0x0f) << 12) | (source [2] << 4) | (source [3] >> 4);
- pti = (source [3] & 0x2) != 0;
-
- vdbg ("udsl_extract_cells: vpi %hd, vci %d, pti %d", vpi, vci, pti);
-
- if (cached_vcc && (vci == cached_vci) && (vpi == cached_vpi))
- vcc_data = cached_vcc;
- else if ((vcc_data = udsl_find_vcc (instance, vpi, vci))) {
- cached_vcc = vcc_data;
- cached_vpi = vpi;
- cached_vci = vci;
- } else {
- dbg ("udsl_extract_cells: unknown vpi/vci (%hd/%d)!", vpi, vci);
- continue;
- }
-
- vcc = vcc_data->vcc;
- sarb = vcc_data->sarb;
-
- if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) {
- dbg ("udsl_extract_cells: buffer overrun (sarb->len %u, vcc: 0x%p)!", sarb->len, vcc);
- /* discard cells already received */
- skb_trim (sarb, 0);
- }
-
- memcpy (sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
- __skb_put (sarb, ATM_CELL_PAYLOAD);
-
- if (pti) {
- struct sk_buff *skb;
- unsigned int length;
- unsigned int pdu_length;
-
- length = (source [ATM_CELL_SIZE - 6] << 8) + source [ATM_CELL_SIZE - 5];
-
- /* guard against overflow */
- if (length > ATM_MAX_AAL5_PDU) {
- dbg ("udsl_extract_cells: bogus length %u (vcc: 0x%p)!", length, vcc);
- atomic_inc (&vcc->stats->rx_err);
- goto out;
- }
-
- pdu_length = UDSL_NUM_CELLS (length) * ATM_CELL_PAYLOAD;
-
- if (sarb->len < pdu_length) {
- dbg ("udsl_extract_cells: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!", pdu_length, sarb->len, vcc);
- atomic_inc (&vcc->stats->rx_err);
- goto out;
- }
-
- if (crc32_be (~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) {
- dbg ("udsl_extract_cells: packet failed crc check (vcc: 0x%p)!", vcc);
- atomic_inc (&vcc->stats->rx_err);
- goto out;
- }
-
- vdbg ("udsl_extract_cells: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", length, pdu_length, vcc);
-
- if (!(skb = dev_alloc_skb (length))) {
- dbg ("udsl_extract_cells: no memory for skb (length: %u)!", length);
- atomic_inc (&vcc->stats->rx_drop);
- goto out;
- }
-
- vdbg ("udsl_extract_cells: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", skb, skb->truesize);
-
- if (!atm_charge (vcc, skb->truesize)) {
- dbg ("udsl_extract_cells: failed atm_charge (skb->truesize: %u)!", skb->truesize);
- dev_kfree_skb (skb);
- goto out; /* atm_charge increments rx_drop */
- }
-
- memcpy (skb->data, sarb->tail - pdu_length, length);
- __skb_put (skb, length);
-
- vdbg ("udsl_extract_cells: sending skb 0x%p, skb->len %u, skb->truesize %u", skb, skb->len, skb->truesize);
-
- PACKETDEBUG (skb->data, skb->len);
-
- vcc->push (vcc, skb);
-
- atomic_inc (&vcc->stats->rx);
-out:
- skb_trim (sarb, 0);
- }
- }
-}
-
-
-/*************
-** encode **
-*************/
-
-static const unsigned char zeros [ATM_CELL_PAYLOAD];
-
-static void udsl_groom_skb (struct atm_vcc *vcc, struct sk_buff *skb)
-{
- struct udsl_control *ctrl = UDSL_SKB (skb);
- unsigned int zero_padding;
- u32 crc;
-
- ctrl->atm_data.vcc = vcc;
- ctrl->cell_header [0] = vcc->vpi >> 4;
- ctrl->cell_header [1] = (vcc->vpi << 4) | (vcc->vci >> 12);
- ctrl->cell_header [2] = vcc->vci >> 4;
- ctrl->cell_header [3] = vcc->vci << 4;
- ctrl->cell_header [4] = 0xec;
-
- ctrl->num_cells = UDSL_NUM_CELLS (skb->len);
- ctrl->num_entire = skb->len / ATM_CELL_PAYLOAD;
-
- zero_padding = ctrl->num_cells * ATM_CELL_PAYLOAD - skb->len - ATM_AAL5_TRAILER;
-
- if (ctrl->num_entire + 1 < ctrl->num_cells)
- ctrl->pdu_padding = zero_padding - (ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER);
- else
- ctrl->pdu_padding = zero_padding;
-
- ctrl->aal5_trailer [0] = 0; /* UU = 0 */
- ctrl->aal5_trailer [1] = 0; /* CPI = 0 */
- ctrl->aal5_trailer [2] = skb->len >> 8;
- ctrl->aal5_trailer [3] = skb->len;
-
- crc = crc32_be (~0, skb->data, skb->len);
- crc = crc32_be (crc, zeros, zero_padding);
- crc = crc32_be (crc, ctrl->aal5_trailer, 4);
- crc = ~crc;
-
- ctrl->aal5_trailer [4] = crc >> 24;
- ctrl->aal5_trailer [5] = crc >> 16;
- ctrl->aal5_trailer [6] = crc >> 8;
- ctrl->aal5_trailer [7] = crc;
-}
-
-static unsigned int udsl_write_cells (unsigned int howmany, struct sk_buff *skb, unsigned char **target_p)
-{
- struct udsl_control *ctrl = UDSL_SKB (skb);
- unsigned char *target = *target_p;
- unsigned int nc, ne, i;
-
- vdbg ("udsl_write_cells: howmany=%u, skb->len=%d, num_cells=%u, num_entire=%u, pdu_padding=%u", howmany, skb->len, ctrl->num_cells, ctrl->num_entire, ctrl->pdu_padding);
-
- nc = ctrl->num_cells;
- ne = min (howmany, ctrl->num_entire);
-
- for (i = 0; i < ne; i++) {
- memcpy (target, ctrl->cell_header, ATM_CELL_HEADER);
- target += ATM_CELL_HEADER;
- memcpy (target, skb->data, ATM_CELL_PAYLOAD);
- target += ATM_CELL_PAYLOAD;
- __skb_pull (skb, ATM_CELL_PAYLOAD);
- }
-
- ctrl->num_entire -= ne;
-
- if (!(ctrl->num_cells -= ne) || !(howmany -= ne))
- goto out;
-
- memcpy (target, ctrl->cell_header, ATM_CELL_HEADER);
- target += ATM_CELL_HEADER;
- memcpy (target, skb->data, skb->len);
- target += skb->len;
- __skb_pull (skb, skb->len);
- memset (target, 0, ctrl->pdu_padding);
- target += ctrl->pdu_padding;
-
- if (--ctrl->num_cells) {
- if (!--howmany) {
- ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER;
- goto out;
- }
-
- memcpy (target, ctrl->cell_header, ATM_CELL_HEADER);
- target += ATM_CELL_HEADER;
- memset (target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER);
- target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER;
-
- DEBUG_ON (--ctrl->num_cells);
- }
-
- memcpy (target, ctrl->aal5_trailer, ATM_AAL5_TRAILER);
- target += ATM_AAL5_TRAILER;
- /* set pti bit in last cell */
- *(target + 3 - ATM_CELL_SIZE) |= 0x2;
-
-out:
- *target_p = target;
- return nc - ctrl->num_cells;
-}
-
-
-/**************
-** receive **
-**************/
-
-static void udsl_complete_receive (struct urb *urb, struct pt_regs *regs)
-{
- struct udsl_receive_buffer *buf;
- struct udsl_instance_data *instance;
- struct udsl_receiver *rcv;
- unsigned long flags;
-
- if (!urb || !(rcv = urb->context)) {
- dbg ("udsl_complete_receive: bad urb!");
- return;
- }
-
- instance = rcv->instance;
- buf = rcv->buffer;
-
- buf->filled_cells = urb->actual_length / ATM_CELL_SIZE;
-
- vdbg ("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf);
-
- DEBUG_ON (buf->filled_cells > rcv_buf_size);
-
- /* may not be in_interrupt() */
- spin_lock_irqsave (&instance->receive_lock, flags);
- list_add (&rcv->list, &instance->spare_receivers);
- list_add_tail (&buf->list, &instance->filled_receive_buffers);
- if (likely (!urb->status))
- tasklet_schedule (&instance->receive_tasklet);
- spin_unlock_irqrestore (&instance->receive_lock, flags);
-}
-
-static void udsl_process_receive (unsigned long data)
-{
- struct udsl_receive_buffer *buf;
- struct udsl_instance_data *instance = (struct udsl_instance_data *) data;
- struct udsl_receiver *rcv;
- int err;
-
-made_progress:
- while (!list_empty (&instance->spare_receive_buffers)) {
- spin_lock_irq (&instance->receive_lock);
- if (list_empty (&instance->spare_receivers)) {
- spin_unlock_irq (&instance->receive_lock);
- break;
- }
- rcv = list_entry (instance->spare_receivers.next, struct udsl_receiver, list);
- list_del (&rcv->list);
- spin_unlock_irq (&instance->receive_lock);
-
- buf = list_entry (instance->spare_receive_buffers.next, struct udsl_receive_buffer, list);
- list_del (&buf->list);
-
- rcv->buffer = buf;
-
- usb_fill_bulk_urb (rcv->urb,
- instance->usb_dev,
- usb_rcvbulkpipe (instance->usb_dev, UDSL_ENDPOINT_DATA_IN),
- buf->base,
- rcv_buf_size * ATM_CELL_SIZE,
- udsl_complete_receive,
- rcv);
-
- vdbg ("udsl_process_receive: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
-
- if ((err = usb_submit_urb(rcv->urb, GFP_ATOMIC)) < 0) {
- dbg ("udsl_process_receive: urb submission failed (%d)!", err);
- list_add (&buf->list, &instance->spare_receive_buffers);
- spin_lock_irq (&instance->receive_lock);
- list_add (&rcv->list, &instance->spare_receivers);
- spin_unlock_irq (&instance->receive_lock);
- break;
- }
- }
-
- spin_lock_irq (&instance->receive_lock);
- if (list_empty (&instance->filled_receive_buffers)) {
- spin_unlock_irq (&instance->receive_lock);
- return; /* done - no more buffers */
- }
- buf = list_entry (instance->filled_receive_buffers.next, struct udsl_receive_buffer, list);
- list_del (&buf->list);
- spin_unlock_irq (&instance->receive_lock);
- vdbg ("udsl_process_receive: processing buf 0x%p", buf);
- udsl_extract_cells (instance, buf->base, buf->filled_cells);
- list_add (&buf->list, &instance->spare_receive_buffers);
- goto made_progress;
-}
-
-
-/***********
-** send **
-***********/
-
-static void udsl_complete_send (struct urb *urb, struct pt_regs *regs)
-{
- struct udsl_instance_data *instance;
- struct udsl_sender *snd;
- unsigned long flags;
-
- if (!urb || !(snd = urb->context) || !(instance = snd->instance)) {
- dbg ("udsl_complete_send: bad urb!");
- return;
- }
-
- vdbg ("udsl_complete_send: urb 0x%p, status %d, snd 0x%p, buf 0x%p", urb, urb->status, snd, snd->buffer);
-
- /* may not be in_interrupt() */
- spin_lock_irqsave (&instance->send_lock, flags);
- list_add (&snd->list, &instance->spare_senders);
- list_add (&snd->buffer->list, &instance->spare_send_buffers);
- tasklet_schedule (&instance->send_tasklet);
- spin_unlock_irqrestore (&instance->send_lock, flags);
-}
-
-static void udsl_process_send (unsigned long data)
-{
- struct udsl_send_buffer *buf;
- struct udsl_instance_data *instance = (struct udsl_instance_data *) data;
- struct sk_buff *skb;
- struct udsl_sender *snd;
- int err;
- unsigned int num_written;
-
-made_progress:
- spin_lock_irq (&instance->send_lock);
- while (!list_empty (&instance->spare_senders)) {
- if (!list_empty (&instance->filled_send_buffers)) {
- buf = list_entry (instance->filled_send_buffers.next, struct udsl_send_buffer, list);
- list_del (&buf->list);
- } else if ((buf = instance->current_buffer)) {
- instance->current_buffer = NULL;
- } else /* all buffers empty */
- break;
-
- snd = list_entry (instance->spare_senders.next, struct udsl_sender, list);
- list_del (&snd->list);
- spin_unlock_irq (&instance->send_lock);
-
- snd->buffer = buf;
- usb_fill_bulk_urb (snd->urb,
- instance->usb_dev,
- usb_sndbulkpipe (instance->usb_dev, UDSL_ENDPOINT_DATA_OUT),
- buf->base,
- (snd_buf_size - buf->free_cells) * ATM_CELL_SIZE,
- udsl_complete_send,
- snd);
-
- vdbg ("udsl_process_send: submitting urb 0x%p (%d cells), snd 0x%p, buf 0x%p", snd->urb, snd_buf_size - buf->free_cells, snd, buf);
-
- if ((err = usb_submit_urb(snd->urb, GFP_ATOMIC)) < 0) {
- dbg ("udsl_process_send: urb submission failed (%d)!", err);
- spin_lock_irq (&instance->send_lock);
- list_add (&snd->list, &instance->spare_senders);
- spin_unlock_irq (&instance->send_lock);
- list_add (&buf->list, &instance->filled_send_buffers);
- return; /* bail out */
- }
-
- spin_lock_irq (&instance->send_lock);
- } /* while */
- spin_unlock_irq (&instance->send_lock);
-
- if (!instance->current_skb && !(instance->current_skb = skb_dequeue (&instance->sndqueue)))
- return; /* done - no more skbs */
-
- skb = instance->current_skb;
-
- if (!(buf = instance->current_buffer)) {
- spin_lock_irq (&instance->send_lock);
- if (list_empty (&instance->spare_send_buffers)) {
- instance->current_buffer = NULL;
- spin_unlock_irq (&instance->send_lock);
- return; /* done - no more buffers */
- }
- buf = list_entry (instance->spare_send_buffers.next, struct udsl_send_buffer, list);
- list_del (&buf->list);
- spin_unlock_irq (&instance->send_lock);
-
- buf->free_start = buf->base;
- buf->free_cells = snd_buf_size;
-
- instance->current_buffer = buf;
- }
-
- num_written = udsl_write_cells (buf->free_cells, skb, &buf->free_start);
-
- vdbg ("udsl_process_send: wrote %u cells from skb 0x%p to buffer 0x%p", num_written, skb, buf);
-
- if (!(buf->free_cells -= num_written)) {
- list_add_tail (&buf->list, &instance->filled_send_buffers);
- instance->current_buffer = NULL;
- }
-
- vdbg ("udsl_process_send: buffer contains %d cells, %d left", snd_buf_size - buf->free_cells, buf->free_cells);
-
- if (!UDSL_SKB (skb)->num_cells) {
- struct atm_vcc *vcc = UDSL_SKB (skb)->atm_data.vcc;
-
- udsl_pop (vcc, skb);
- instance->current_skb = NULL;
-
- atomic_inc (&vcc->stats->tx);
- }
-
- goto made_progress;
-}
-
-static void udsl_cancel_send (struct udsl_instance_data *instance, struct atm_vcc *vcc)
-{
- struct sk_buff *skb, *n;
-
- dbg ("udsl_cancel_send entered");
- spin_lock_irq (&instance->sndqueue.lock);
- for (skb = instance->sndqueue.next, n = skb->next; skb != (struct sk_buff *)&instance->sndqueue; skb = n, n = skb->next)
- if (UDSL_SKB (skb)->atm_data.vcc == vcc) {
- dbg ("udsl_cancel_send: popping skb 0x%p", skb);
- __skb_unlink (skb, &instance->sndqueue);
- udsl_pop (vcc, skb);
- }
- spin_unlock_irq (&instance->sndqueue.lock);
-
- tasklet_disable (&instance->send_tasklet);
- if ((skb = instance->current_skb) && (UDSL_SKB (skb)->atm_data.vcc == vcc)) {
- dbg ("udsl_cancel_send: popping current skb (0x%p)", skb);
- instance->current_skb = NULL;
- udsl_pop (vcc, skb);
- }
- tasklet_enable (&instance->send_tasklet);
- dbg ("udsl_cancel_send done");
-}
-
-static int udsl_atm_send (struct atm_vcc *vcc, struct sk_buff *skb)
-{
- struct udsl_instance_data *instance = vcc->dev->dev_data;
- int err;
-
- vdbg ("udsl_atm_send called (skb 0x%p, len %u)", skb, skb->len);
-
- if (!instance || !instance->usb_dev) {
- dbg ("udsl_atm_send: NULL data!");
- err = -ENODEV;
- goto fail;
- }
-
- if (vcc->qos.aal != ATM_AAL5) {
- dbg ("udsl_atm_send: unsupported ATM type %d!", vcc->qos.aal);
- err = -EINVAL;
- goto fail;
- }
-
- if (skb->len > ATM_MAX_AAL5_PDU) {
- dbg ("udsl_atm_send: packet too long (%d vs %d)!", skb->len, ATM_MAX_AAL5_PDU);
- err = -EINVAL;
- goto fail;
- }
-
- PACKETDEBUG (skb->data, skb->len);
-
- udsl_groom_skb (vcc, skb);
- skb_queue_tail (&instance->sndqueue, skb);
- tasklet_schedule (&instance->send_tasklet);
-
- return 0;
-
-fail:
- udsl_pop (vcc, skb);
- return err;
-}
-
-
-/**********
-** ATM **
-**********/
-
-static void udsl_atm_dev_close (struct atm_dev *dev)
-{
- struct udsl_instance_data *instance = dev->dev_data;
-
- if (!instance) {
- dbg ("udsl_atm_dev_close: NULL instance!");
- return;
- }
-
- dbg ("udsl_atm_dev_close: queue has %u elements", instance->sndqueue.qlen);
-
- tasklet_kill (&instance->receive_tasklet);
- tasklet_kill (&instance->send_tasklet);
- kfree (instance);
- dev->dev_data = NULL;
-}
-
-static int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t *pos, char *page)
-{
- struct udsl_instance_data *instance = atm_dev->dev_data;
- int left = *pos;
-
- if (!instance) {
- dbg ("udsl_atm_proc_read: NULL instance!");
- return -ENODEV;
- }
-
- if (!left--)
- return sprintf (page, "%s\n", instance->description);
-
- if (!left--)
- return sprintf (page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
- atm_dev->esi [0], atm_dev->esi [1], atm_dev->esi [2],
- atm_dev->esi [3], atm_dev->esi [4], atm_dev->esi [5]);
-
- if (!left--)
- return sprintf (page, "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n",
- atomic_read (&atm_dev->stats.aal5.tx),
- atomic_read (&atm_dev->stats.aal5.tx_err),
- atomic_read (&atm_dev->stats.aal5.rx),
- atomic_read (&atm_dev->stats.aal5.rx_err),
- atomic_read (&atm_dev->stats.aal5.rx_drop));
-
- if (!left--) {
- switch (atm_dev->signal) {
- case ATM_PHY_SIG_FOUND:
- sprintf (page, "Line up");
- break;
- case ATM_PHY_SIG_LOST:
- sprintf (page, "Line down");
- break;
- default:
- sprintf (page, "Line state unknown");
- break;
- }
-
- if (instance->usb_dev) {
- if (!instance->firmware_loaded)
- strcat (page, ", no firmware\n");
- else
- strcat (page, ", firmware loaded\n");
- } else
- strcat (page, ", disconnected\n");
-
- return strlen (page);
- }
-
- return 0;
-}
-
-static int udsl_atm_open (struct atm_vcc *vcc)
-{
- struct udsl_instance_data *instance = vcc->dev->dev_data;
- struct udsl_vcc_data *new;
- unsigned int max_pdu;
- int vci = vcc->vci;
- short vpi = vcc->vpi;
-
- dbg ("udsl_atm_open: vpi %hd, vci %d", vpi, vci);
-
- if (!instance || !instance->usb_dev) {
- dbg ("udsl_atm_open: NULL data!");
- return -ENODEV;
- }
-
- /* only support AAL5 */
- if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0) || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) {
- dbg ("udsl_atm_open: unsupported ATM type %d!", vcc->qos.aal);
- return -EINVAL;
- }
-
- if (!instance->firmware_loaded) {
- dbg ("udsl_atm_open: firmware not loaded!");
- return -EAGAIN;
- }
-
- down (&instance->serialize); /* vs self, udsl_atm_close */
-
- if (udsl_find_vcc (instance, vpi, vci)) {
- dbg ("udsl_atm_open: %hd/%d already in use!", vpi, vci);
- up (&instance->serialize);
- return -EADDRINUSE;
- }
-
- if (!(new = kmalloc (sizeof (struct udsl_vcc_data), GFP_KERNEL))) {
- dbg ("udsl_atm_open: no memory for vcc_data!");
- up (&instance->serialize);
- return -ENOMEM;
- }
-
- memset (new, 0, sizeof (struct udsl_vcc_data));
- new->vcc = vcc;
- new->vpi = vpi;
- new->vci = vci;
-
- /* udsl_extract_cells requires at least one cell */
- max_pdu = max (1, UDSL_NUM_CELLS (vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD;
- if (!(new->sarb = alloc_skb (max_pdu, GFP_KERNEL))) {
- dbg ("udsl_atm_open: no memory for SAR buffer!");
- kfree (new);
- up (&instance->serialize);
- return -ENOMEM;
- }
-
- vcc->dev_data = new;
-
- tasklet_disable (&instance->receive_tasklet);
- list_add (&new->list, &instance->vcc_list);
- tasklet_enable (&instance->receive_tasklet);
-
- set_bit (ATM_VF_ADDR, &vcc->flags);
- set_bit (ATM_VF_PARTIAL, &vcc->flags);
- set_bit (ATM_VF_READY, &vcc->flags);
-
- up (&instance->serialize);
-
- tasklet_schedule (&instance->receive_tasklet);
-
- dbg ("udsl_atm_open: allocated vcc data 0x%p (max_pdu: %u)", new, max_pdu);
-
- return 0;
-}
-
-static void udsl_atm_close (struct atm_vcc *vcc)
-{
- struct udsl_instance_data *instance = vcc->dev->dev_data;
- struct udsl_vcc_data *vcc_data = vcc->dev_data;
-
- dbg ("udsl_atm_close called");
-
- if (!instance || !vcc_data) {
- dbg ("udsl_atm_close: NULL data!");
- return;
- }
-
- dbg ("udsl_atm_close: deallocating vcc 0x%p with vpi %d vci %d", vcc_data, vcc_data->vpi, vcc_data->vci);
-
- udsl_cancel_send (instance, vcc);
-
- down (&instance->serialize); /* vs self, udsl_atm_open */
-
- tasklet_disable (&instance->receive_tasklet);
- list_del (&vcc_data->list);
- tasklet_enable (&instance->receive_tasklet);
-
- kfree_skb (vcc_data->sarb);
- vcc_data->sarb = NULL;
-
- kfree (vcc_data);
- vcc->dev_data = NULL;
-
- vcc->vpi = ATM_VPI_UNSPEC;
- vcc->vci = ATM_VCI_UNSPEC;
- clear_bit (ATM_VF_READY, &vcc->flags);
- clear_bit (ATM_VF_PARTIAL, &vcc->flags);
- clear_bit (ATM_VF_ADDR, &vcc->flags);
-
- up (&instance->serialize);
-
- dbg ("udsl_atm_close successful");
-}
-
-static int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void __user *arg)
-{
- switch (cmd) {
- case ATM_QUERYLOOP:
- return put_user (ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0;
- default:
- return -ENOIOCTLCMD;
- }
-}
-
-
-/**********
-** USB **
-**********/
-
-static int udsl_set_alternate (struct udsl_instance_data *instance)
-{
- down (&instance->serialize); /* vs self */
- if (!instance->firmware_loaded) {
- int ret;
-
- if ((ret = usb_set_interface (instance->usb_dev, 1, 1)) < 0) {
- dbg ("udsl_set_alternate: usb_set_interface returned %d!", ret);
- up (&instance->serialize);
- return ret;
- }
- instance->firmware_loaded = 1;
- }
- up (&instance->serialize);
-
- tasklet_schedule (&instance->receive_tasklet);
-
- return 0;
-}
-
-static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data)
-{
- struct udsl_instance_data *instance = usb_get_intfdata (intf);
-
- dbg ("udsl_usb_ioctl entered");
-
- if (!instance) {
- dbg ("udsl_usb_ioctl: NULL instance!");
- return -ENODEV;
- }
-
- switch (code) {
- case UDSL_IOCTL_LINE_UP:
- instance->atm_dev->signal = ATM_PHY_SIG_FOUND;
- return udsl_set_alternate (instance);
- case UDSL_IOCTL_LINE_DOWN:
- instance->atm_dev->signal = ATM_PHY_SIG_LOST;
- return 0;
- default:
- return -ENOTTY;
- }
-}
-
-static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_id *id)
-{
- struct usb_device *dev = interface_to_usbdev(intf);
- int ifnum = intf->altsetting->desc.bInterfaceNumber;
- struct udsl_instance_data *instance;
- unsigned char mac_str [13];
- int i, length;
- char *buf;
-
- dbg ("udsl_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d",
- dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum);
-
- if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) ||
- (dev->descriptor.idVendor != SPEEDTOUCH_VENDORID) ||
- (dev->descriptor.idProduct != SPEEDTOUCH_PRODUCTID) || (ifnum != 1))
- return -ENODEV;
-
- dbg ("udsl_usb_probe: device accepted");
-
- /* instance init */
- if (!(instance = kmalloc (sizeof (struct udsl_instance_data), GFP_KERNEL))) {
- dbg ("udsl_usb_probe: no memory for instance data!");
- return -ENOMEM;
- }
-
- memset (instance, 0, sizeof (struct udsl_instance_data));
-
- init_MUTEX (&instance->serialize);
-
- instance->usb_dev = dev;
-
- INIT_LIST_HEAD (&instance->vcc_list);
-
- spin_lock_init (&instance->receive_lock);
- INIT_LIST_HEAD (&instance->spare_receivers);
- INIT_LIST_HEAD (&instance->filled_receive_buffers);
-
- tasklet_init (&instance->receive_tasklet, udsl_process_receive, (unsigned long) instance);
- INIT_LIST_HEAD (&instance->spare_receive_buffers);
-
- skb_queue_head_init (&instance->sndqueue);
-
- spin_lock_init (&instance->send_lock);
- INIT_LIST_HEAD (&instance->spare_senders);
- INIT_LIST_HEAD (&instance->spare_send_buffers);
-
- tasklet_init (&instance->send_tasklet, udsl_process_send, (unsigned long) instance);
- INIT_LIST_HEAD (&instance->filled_send_buffers);
-
- /* receive init */
- for (i = 0; i < num_rcv_urbs; i++) {
- struct udsl_receiver *rcv = &(instance->receivers [i]);
-
- if (!(rcv->urb = usb_alloc_urb (0, GFP_KERNEL))) {
- dbg ("udsl_usb_probe: no memory for receive urb %d!", i);
- goto fail;
- }
-
- rcv->instance = instance;
-
- list_add (&rcv->list, &instance->spare_receivers);
- }
-
- for (i = 0; i < num_rcv_bufs; i++) {
- struct udsl_receive_buffer *buf = &(instance->receive_buffers [i]);
-
- if (!(buf->base = kmalloc (rcv_buf_size * ATM_CELL_SIZE, GFP_KERNEL))) {
- dbg ("udsl_usb_probe: no memory for receive buffer %d!", i);
- goto fail;
- }
-
- list_add (&buf->list, &instance->spare_receive_buffers);
- }
-
- /* send init */
- for (i = 0; i < num_snd_urbs; i++) {
- struct udsl_sender *snd = &(instance->senders [i]);
-
- if (!(snd->urb = usb_alloc_urb (0, GFP_KERNEL))) {
- dbg ("udsl_usb_probe: no memory for send urb %d!", i);
- goto fail;
- }
-
- snd->instance = instance;
-
- list_add (&snd->list, &instance->spare_senders);
- }
-
- for (i = 0; i < num_snd_bufs; i++) {
- struct udsl_send_buffer *buf = &(instance->send_buffers [i]);
-
- if (!(buf->base = kmalloc (snd_buf_size * ATM_CELL_SIZE, GFP_KERNEL))) {
- dbg ("udsl_usb_probe: no memory for send buffer %d!", i);
- goto fail;
- }
-
- list_add (&buf->list, &instance->spare_send_buffers);
- }
-
- /* ATM init */
- if (!(instance->atm_dev = atm_dev_register (udsl_driver_name, &udsl_atm_devops, -1, NULL))) {
- dbg ("udsl_usb_probe: failed to register ATM device!");
- goto fail;
- }
-
- instance->atm_dev->ci_range.vpi_bits = ATM_CI_MAX;
- instance->atm_dev->ci_range.vci_bits = ATM_CI_MAX;
- instance->atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
-
- /* temp init ATM device, set to 128kbit */
- instance->atm_dev->link_rate = 128 * 1000 / 424;
-
- /* set MAC address, it is stored in the serial number */
- memset (instance->atm_dev->esi, 0, sizeof (instance->atm_dev->esi));
- if (usb_string (dev, dev->descriptor.iSerialNumber, mac_str, sizeof (mac_str)) == 12)
- for (i = 0; i < 6; i++)
- instance->atm_dev->esi [i] = (hex2int (mac_str [i * 2]) * 16) + (hex2int (mac_str [i * 2 + 1]));
-
- /* device description */
- buf = instance->description;
- length = sizeof (instance->description);
-
- if ((i = usb_string (dev, dev->descriptor.iProduct, buf, length)) < 0)
- goto finish;
-
- buf += i;
- length -= i;
-
- i = scnprintf (buf, length, " (");
- buf += i;
- length -= i;
-
- if (length <= 0 || (i = usb_make_path (dev, buf, length)) < 0)
- goto finish;
-
- buf += i;
- length -= i;
-
- snprintf (buf, length, ")");
-
-finish:
- /* ready for ATM callbacks */
- wmb ();
- instance->atm_dev->dev_data = instance;
-
- usb_set_intfdata (intf, instance);
-
- return 0;
-
-fail:
- for (i = 0; i < num_snd_bufs; i++)
- kfree (instance->send_buffers [i].base);
-
- for (i = 0; i < num_snd_urbs; i++)
- usb_free_urb (instance->senders [i].urb);
-
- for (i = 0; i < num_rcv_bufs; i++)
- kfree (instance->receive_buffers [i].base);
-
- for (i = 0; i < num_rcv_urbs; i++)
- usb_free_urb (instance->receivers [i].urb);
-
- kfree (instance);
-
- return -ENOMEM;
-}
-
-static void udsl_usb_disconnect (struct usb_interface *intf)
-{
- struct udsl_instance_data *instance = usb_get_intfdata (intf);
- struct list_head *pos;
- unsigned int count;
- int result, i;
-
- dbg ("udsl_usb_disconnect entered");
-
- usb_set_intfdata (intf, NULL);
-
- if (!instance) {
- dbg ("udsl_usb_disconnect: NULL instance!");
- return;
- }
-
- /* receive finalize */
- tasklet_disable (&instance->receive_tasklet);
-
- for (i = 0; i < num_rcv_urbs; i++)
- if ((result = usb_unlink_urb (instance->receivers [i].urb)) < 0)
- dbg ("udsl_usb_disconnect: usb_unlink_urb on receive urb %d returned %d!", i, result);
-
- /* wait for completion handlers to finish */
- do {
- count = 0;
- spin_lock_irq (&instance->receive_lock);
- list_for_each (pos, &instance->spare_receivers)
- DEBUG_ON (++count > num_rcv_urbs);
- spin_unlock_irq (&instance->receive_lock);
-
- dbg ("udsl_usb_disconnect: found %u spare receivers", count);
-
- if (count == num_rcv_urbs)
- break;
-
- set_current_state (TASK_RUNNING);
- schedule ();
- } while (1);
-
- /* no need to take the spinlock */
- INIT_LIST_HEAD (&instance->filled_receive_buffers);
- INIT_LIST_HEAD (&instance->spare_receive_buffers);
-
- tasklet_enable (&instance->receive_tasklet);
-
- for (i = 0; i < num_rcv_urbs; i++)
- usb_free_urb (instance->receivers [i].urb);
-
- for (i = 0; i < num_rcv_bufs; i++)
- kfree (instance->receive_buffers [i].base);
-
- /* send finalize */
- tasklet_disable (&instance->send_tasklet);
-
- for (i = 0; i < num_snd_urbs; i++)
- if ((result = usb_unlink_urb (instance->senders [i].urb)) < 0)
- dbg ("udsl_usb_disconnect: usb_unlink_urb on send urb %d returned %d!", i, result);
-
- /* wait for completion handlers to finish */
- do {
- count = 0;
- spin_lock_irq (&instance->send_lock);
- list_for_each (pos, &instance->spare_senders)
- DEBUG_ON (++count > num_snd_urbs);
- spin_unlock_irq (&instance->send_lock);
-
- dbg ("udsl_usb_disconnect: found %u spare senders", count);
-
- if (count == num_snd_urbs)
- break;
-
- set_current_state (TASK_RUNNING);
- schedule ();
- } while (1);
-
- /* no need to take the spinlock */
- INIT_LIST_HEAD (&instance->spare_senders);
- INIT_LIST_HEAD (&instance->spare_send_buffers);
- instance->current_buffer = NULL;
-
- tasklet_enable (&instance->send_tasklet);
-
- for (i = 0; i < num_snd_urbs; i++)
- usb_free_urb (instance->senders [i].urb);
-
- for (i = 0; i < num_snd_bufs; i++)
- kfree (instance->send_buffers [i].base);
-
- wmb ();
- instance->usb_dev = NULL;
-
- /* ATM finalize */
- shutdown_atm_dev (instance->atm_dev); /* frees instance, kills tasklets */
-}
-
-
-/***********
-** init **
-***********/
-
-static int __init udsl_usb_init (void)
-{
- dbg ("udsl_usb_init: driver version " DRIVER_VERSION);
-
- if (sizeof (struct udsl_control) > sizeof (((struct sk_buff *)0)->cb)) {
- printk (KERN_ERR __FILE__ ": unusable with this kernel!\n");
- return -EIO;
- }
-
- if ((num_rcv_urbs > UDSL_MAX_RCV_URBS) || (num_snd_urbs > UDSL_MAX_SND_URBS) ||
- (num_rcv_bufs > UDSL_MAX_RCV_BUFS) || (num_snd_bufs > UDSL_MAX_SND_BUFS) ||
- (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE) || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE))
- return -EINVAL;
-
- return usb_register (&udsl_usb_driver);
-}
-
-static void __exit udsl_usb_cleanup (void)
-{
- dbg ("udsl_usb_cleanup entered");
-
- usb_deregister (&udsl_usb_driver);
-}
-
-module_init (udsl_usb_init);
-module_exit (udsl_usb_cleanup);
-
-MODULE_AUTHOR (DRIVER_AUTHOR);
-MODULE_DESCRIPTION (DRIVER_DESC);
-MODULE_LICENSE ("GPL");
-MODULE_VERSION (DRIVER_VERSION);
-
-
-/************
-** debug **
-************/
-
-#ifdef VERBOSE_DEBUG
-static int udsl_print_packet (const unsigned char *data, int len)
-{
- unsigned char buffer [256];
- int i = 0, j = 0;
-
- for (i = 0; i < len;) {
- buffer [0] = '\0';
- sprintf (buffer, "%.3d :", i);
- for (j = 0; (j < 16) && (i < len); j++, i++) {
- sprintf (buffer, "%s %2.2x", buffer, data [i]);
- }
- dbg ("%s", buffer);
- }
- return i;
-}
-#endif
diff --git a/drivers/usb/misc/tiglusb.c b/drivers/usb/misc/tiglusb.c
index 0f9c5753772d..f902884a7bbb 100644
--- a/drivers/usb/misc/tiglusb.c
+++ b/drivers/usb/misc/tiglusb.c
@@ -115,6 +115,7 @@ tiglusb_open (struct inode *inode, struct file *filp)
return -EBUSY;
}
+ set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout (HZ / 2);
if (signal_pending (current)) {
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
index 0bc5ccc244ba..8b22320d5ea6 100644
--- a/drivers/usb/misc/uss720.c
+++ b/drivers/usb/misc/uss720.c
@@ -44,6 +44,7 @@
#include <linux/parport.h>
#include <linux/init.h>
#include <linux/usb.h>
+#include <linux/delay.h>
/*
* Version Information
@@ -159,8 +160,7 @@ static int change_mode(struct parport *pp, int m)
if (time_after_eq (jiffies, expire))
/* The FIFO is stuck. */
return -EBUSY;
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout((HZ + 99) / 100);
+ msleep_interruptible(10);
if (signal_pending (current))
break;
}
diff --git a/drivers/usb/net/catc.c b/drivers/usb/net/catc.c
index 455fe6e3be06..66d0a70a8210 100644
--- a/drivers/usb/net/catc.c
+++ b/drivers/usb/net/catc.c
@@ -765,10 +765,10 @@ static int catc_stop(struct net_device *netdev)
if (!catc->is_f5u011)
del_timer_sync(&catc->timer);
- usb_unlink_urb(catc->rx_urb);
- usb_unlink_urb(catc->tx_urb);
- usb_unlink_urb(catc->irq_urb);
- usb_unlink_urb(catc->ctrl_urb);
+ usb_kill_urb(catc->rx_urb);
+ usb_kill_urb(catc->tx_urb);
+ usb_kill_urb(catc->irq_urb);
+ usb_kill_urb(catc->ctrl_urb);
return 0;
}
diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c
index 40e921ade8c8..2092cb9cb33f 100644
--- a/drivers/usb/net/kaweth.c
+++ b/drivers/usb/net/kaweth.c
@@ -668,13 +668,13 @@ static int kaweth_open(struct net_device *net)
INTBUFFERSIZE,
int_callback,
kaweth,
- 8);
+ 250); /* overriding the descriptor */
kaweth->irq_urb->transfer_dma = kaweth->intbufferhandle;
kaweth->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
res = usb_submit_urb(kaweth->irq_urb, GFP_KERNEL);
if (res) {
- usb_unlink_urb(kaweth->rx_urb);
+ usb_kill_urb(kaweth->rx_urb);
return -EIO;
}
@@ -695,15 +695,15 @@ static int kaweth_close(struct net_device *net)
kaweth->status |= KAWETH_STATUS_CLOSING;
- usb_unlink_urb(kaweth->irq_urb);
- usb_unlink_urb(kaweth->rx_urb);
+ usb_kill_urb(kaweth->irq_urb);
+ usb_kill_urb(kaweth->rx_urb);
flush_scheduled_work();
/* a scheduled work may have resubmitted,
we hit them again */
- usb_unlink_urb(kaweth->irq_urb);
- usb_unlink_urb(kaweth->rx_urb);
+ usb_kill_urb(kaweth->irq_urb);
+ usb_kill_urb(kaweth->rx_urb);
kaweth->status &= ~KAWETH_STATUS_CLOSING;
@@ -1173,8 +1173,8 @@ static void kaweth_disconnect(struct usb_interface *intf)
}
kaweth->removed = 1;
- usb_unlink_urb(kaweth->irq_urb);
- usb_unlink_urb(kaweth->rx_urb);
+ usb_kill_urb(kaweth->irq_urb);
+ usb_kill_urb(kaweth->rx_urb);
/* we need to wait for the urb to be cancelled, if it is active */
spin_lock(&kaweth->device_lock);
@@ -1250,19 +1250,17 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
return status;
}
- set_current_state(TASK_UNINTERRUPTIBLE);
while (timeout && !awd.done) {
- timeout = schedule_timeout(timeout);
set_current_state(TASK_UNINTERRUPTIBLE);
+ timeout = schedule_timeout(timeout);
}
- set_current_state(TASK_RUNNING);
remove_wait_queue(&awd.wqh, &wait);
if (!timeout) {
// timeout
kaweth_warn("usb_control/bulk_msg: timeout");
- usb_unlink_urb(urb); // remove urb safely
+ usb_kill_urb(urb); // remove urb safely
status = -ETIMEDOUT;
}
else {
diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c
index c515916e37a7..98cb5ae3f6f1 100644
--- a/drivers/usb/net/pegasus.c
+++ b/drivers/usb/net/pegasus.c
@@ -854,10 +854,10 @@ static void free_all_urbs(pegasus_t * pegasus)
static void unlink_all_urbs(pegasus_t * pegasus)
{
- usb_unlink_urb(pegasus->intr_urb);
- usb_unlink_urb(pegasus->tx_urb);
- usb_unlink_urb(pegasus->rx_urb);
- usb_unlink_urb(pegasus->ctrl_urb);
+ usb_kill_urb(pegasus->intr_urb);
+ usb_kill_urb(pegasus->tx_urb);
+ usb_kill_urb(pegasus->rx_urb);
+ usb_kill_urb(pegasus->ctrl_urb);
}
static int alloc_urbs(pegasus_t * pegasus)
@@ -920,8 +920,8 @@ static int pegasus_open(struct net_device *net)
if ((res = enable_net_traffic(net, pegasus->usb))) {
err("can't enable_net_traffic() - %d", res);
res = -EIO;
- usb_unlink_urb(pegasus->rx_urb);
- usb_unlink_urb(pegasus->intr_urb);
+ usb_kill_urb(pegasus->rx_urb);
+ usb_kill_urb(pegasus->intr_urb);
free_skb_pool(pegasus);
goto exit;
}
diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c
index 640aa5b68095..5cd2c9c5987c 100644
--- a/drivers/usb/net/rtl8150.c
+++ b/drivers/usb/net/rtl8150.c
@@ -20,7 +20,7 @@
#include <asm/uaccess.h>
/* Version Information */
-#define DRIVER_VERSION "v0.6.1 (2004/03/13)"
+#define DRIVER_VERSION "v0.6.2 (2004/08/27)"
#define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
#define DRIVER_DESC "rtl8150 based usb-ethernet driver"
@@ -392,10 +392,10 @@ static void free_all_urbs(rtl8150_t * dev)
static void unlink_all_urbs(rtl8150_t * dev)
{
- usb_unlink_urb(dev->rx_urb);
- usb_unlink_urb(dev->tx_urb);
- usb_unlink_urb(dev->intr_urb);
- usb_unlink_urb(dev->ctrl_urb);
+ usb_kill_urb(dev->rx_urb);
+ usb_kill_urb(dev->tx_urb);
+ usb_kill_urb(dev->intr_urb);
+ usb_kill_urb(dev->ctrl_urb);
}
static inline struct sk_buff *pull_skb(rtl8150_t *dev)
diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
index cca6e72cf2fa..ad65e741fa44 100644
--- a/drivers/usb/net/usbnet.c
+++ b/drivers/usb/net/usbnet.c
@@ -825,7 +825,7 @@ static void ax8817x_unbind(struct usbnet *dev, struct usb_interface *intf)
{
struct ax8817x_data *data = (struct ax8817x_data *)dev->data;
- usb_unlink_urb(data->int_urb);
+ usb_kill_urb(data->int_urb);
usb_free_urb(data->int_urb);
kfree(data->int_buf);
}
@@ -1437,7 +1437,7 @@ static int genelink_free (struct usbnet *dev)
// handling needs to be generic)
// cancel irq urb first
- usb_unlink_urb (priv->irq_urb);
+ usb_kill_urb (priv->irq_urb);
// free irq urb
usb_free_urb (priv->irq_urb);
@@ -3252,6 +3252,10 @@ static const struct usb_device_id products [] = {
// Sitecom LN-029 "USB 2.0 10/100 Ethernet adapter"
USB_DEVICE (0x6189, 0x182d),
.driver_info = (unsigned long) &ax8817x_info,
+}, {
+ // Surecom EP-1427X-2
+ USB_DEVICE (0x1189, 0x0893),
+ .driver_info = (unsigned long) &ax8817x_info,
},
#endif
@@ -3308,11 +3312,18 @@ static const struct usb_device_id products [] = {
*
* PXA25x or PXA210 ... these use a "usb-eth" driver much like
* the sa1100 one, but hardware uses different endpoint numbers.
+ *
+ * Or the Linux "Ethernet" gadget on hardware that can't talk
+ * CDC Ethernet (e.g., no altsettings), in either of two modes:
+ * - acting just like the old "usb-eth" firmware, though
+ * the implementation is different
+ * - supporting RNDIS as the first/default configuration for
+ * MS-Windows interop; Linux needs to use the other config
*/
{
// 1183 = 0x049F, both used as hex values?
// Compaq "Itsy" vendor/product id
- USB_DEVICE (0x049F, 0x505A),
+ USB_DEVICE (0x049F, 0x505A), // usb-eth, or compatible
.driver_info = (unsigned long) &linuxdev_info,
}, {
USB_DEVICE (0x0E7E, 0x1001), // G.Mate "Yopy"
@@ -3320,6 +3331,10 @@ static const struct usb_device_id products [] = {
}, {
USB_DEVICE (0x8086, 0x07d3), // "blob" bootloader
.driver_info = (unsigned long) &blob_info,
+}, {
+ // Linux Ethernet/RNDIS gadget on pxa210/25x/26x
+ USB_DEVICE_VER (0x0525, 0xa4a2, 0x0203, 0x0203),
+ .driver_info = (unsigned long) &linuxdev_info,
},
#endif
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 88fede7b447e..1499bf2fcd13 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -187,6 +187,16 @@ config USB_SERIAL_EDGEPORT_TI
To compile this driver as a module, choose M here: the
module will be called io_ti.
+config USB_SERIAL_IPW
+ tristate "USB IPWireless (3G UMTS TDD) Driver (EXPERIMENTAL)"
+ depends on USB_SERIAL && EXPERIMENTAL
+ help
+ Say Y here if you want to use a IPWireless USB modem such as
+ the ones supplied by Axity3G/Sentech South Africa.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ipw.
+
config USB_SERIAL_KEYSPAN_PDA
tristate "USB Keyspan PDA Single Port Serial Driver"
depends on USB_SERIAL
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 6cfbe3108ccb..dfd43ec80ff3 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_USB_SERIAL_EDGEPORT_TI) += io_ti.o
obj-$(CONFIG_USB_SERIAL_EMPEG) += empeg.o
obj-$(CONFIG_USB_SERIAL_FTDI_SIO) += ftdi_sio.o
obj-$(CONFIG_USB_SERIAL_IPAQ) += ipaq.o
+obj-$(CONFIG_USB_SERIAL_IPW) += ipw.o
obj-$(CONFIG_USB_SERIAL_IR) += ir-usb.o
obj-$(CONFIG_USB_SERIAL_KEYSPAN) += keyspan.o
obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o
diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c
index bcf56011ec22..a44cb9d96113 100644
--- a/drivers/usb/serial/belkin_sa.c
+++ b/drivers/usb/serial/belkin_sa.c
@@ -228,7 +228,7 @@ static int belkin_sa_open (struct usb_serial_port *port, struct file *filp)
port->interrupt_in_urb->dev = port->serial->dev;
retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (retval) {
- usb_unlink_urb(port->read_urb);
+ usb_kill_urb(port->read_urb);
err(" usb_submit_urb(read int) failed");
}
@@ -242,9 +242,9 @@ static void belkin_sa_close (struct usb_serial_port *port, struct file *filp)
dbg("%s port %d", __FUNCTION__, port->number);
/* shutdown our bulk reads and writes */
- usb_unlink_urb (port->write_urb);
- usb_unlink_urb (port->read_urb);
- usb_unlink_urb (port->interrupt_in_urb);
+ usb_kill_urb(port->write_urb);
+ usb_kill_urb(port->read_urb);
+ usb_kill_urb(port->interrupt_in_urb);
} /* belkin_sa_close */
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index 33613b008557..6b5ec2af32ad 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -149,7 +149,7 @@ static void cyberjack_shutdown (struct usb_serial *serial)
dbg("%s", __FUNCTION__);
for (i=0; i < serial->num_ports; ++i) {
- usb_unlink_urb (serial->port[i]->interrupt_in_urb);
+ usb_kill_urb(serial->port[i]->interrupt_in_urb);
/* My special items, the standard routines free my urbs */
kfree(usb_get_serial_port_data(serial->port[i]));
usb_set_serial_port_data(serial->port[i], NULL);
@@ -189,8 +189,8 @@ static void cyberjack_close (struct usb_serial_port *port, struct file *filp)
if (port->serial->dev) {
/* shutdown any bulk reads that might be going on */
- usb_unlink_urb (port->write_urb);
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->write_urb);
+ usb_kill_urb(port->read_urb);
}
}
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 3ac69f2c2510..b904bfcbffcf 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -1615,7 +1615,7 @@ dbg( "digi_close: TOP: port=%d, open_count=%d", priv->dp_port_num, port->open_co
DIGI_CLOSE_TIMEOUT );
/* shutdown any outstanding bulk writes */
- usb_unlink_urb (port->write_urb);
+ usb_kill_urb(port->write_urb);
}
tty->closing = 0;
@@ -1754,8 +1754,8 @@ dbg( "digi_shutdown: TOP, in_interrupt()=%ld", in_interrupt() );
/* stop reads and writes on all ports */
for( i=0; i<serial->type->num_ports+1; i++ ) {
- usb_unlink_urb( serial->port[i]->read_urb );
- usb_unlink_urb( serial->port[i]->write_urb );
+ usb_kill_urb(serial->port[i]->read_urb);
+ usb_kill_urb(serial->port[i]->write_urb);
}
/* free the private data structures for all ports */
diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c
index 251a50fdab86..e94113e0dd43 100644
--- a/drivers/usb/serial/empeg.c
+++ b/drivers/usb/serial/empeg.c
@@ -185,7 +185,7 @@ static void empeg_close (struct usb_serial_port *port, struct file * filp)
dbg("%s - port %d", __FUNCTION__, port->number);
/* shutdown our bulk read */
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
/* Uncomment the following line if you want to see some statistics in your syslog */
/* dev_info (&port->dev, "Bytes In = %d Bytes Out = %d\n", bytes_in, bytes_out); */
}
@@ -406,7 +406,7 @@ static void empeg_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
static void empeg_throttle (struct usb_serial_port *port)
{
dbg("%s - port %d", __FUNCTION__, port->number);
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
}
@@ -579,10 +579,10 @@ static void __exit empeg_exit (void)
for (i = 0; i < NUM_URBS; ++i) {
if (write_urb_pool[i]) {
- /* FIXME - uncomment the following usb_unlink_urb call when
+ /* FIXME - uncomment the following usb_kill_urb call when
* the host controllers get fixed to set urb->dev = NULL after
* the urb is finished. Otherwise this call oopses. */
- /* usb_unlink_urb(write_urb_pool[i]); */
+ /* usb_kill_urb(write_urb_pool[i]); */
if (write_urb_pool[i]->transfer_buffer)
kfree(write_urb_pool[i]->transfer_buffer);
usb_free_urb (write_urb_pool[i]);
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 9073ea84d8d3..b1a985470e42 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -368,6 +368,10 @@ static struct usb_device_id id_table_8U232AM [] = {
{ USB_DEVICE_VER(INTREPID_VID, INTREPID_NEOVI_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FALCOM_VID, FALCOM_TWIST_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_SUUNTO_SPORTS_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(FTDI_RM_VID, FTDI_RMCANVIEW_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USOTL4_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USTL4_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USO9ML2_PID, 0, 0x3ff) },
{ } /* Terminating entry */
};
@@ -478,6 +482,10 @@ static struct usb_device_id id_table_FT232BM [] = {
{ USB_DEVICE_VER(INTREPID_VID, INTREPID_NEOVI_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FALCOM_VID, FALCOM_TWIST_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_SUUNTO_SPORTS_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(FTDI_RM_VID, FTDI_RMCANVIEW_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USOTL4_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USTL4_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USO9ML2_PID, 0x400, 0xffff) },
{ } /* Terminating entry */
};
@@ -595,6 +603,10 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) },
{ USB_DEVICE(FALCOM_VID, FALCOM_TWIST_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_SUUNTO_SPORTS_PID) },
+ { USB_DEVICE(FTDI_RM_VID, FTDI_RMCANVIEW_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_USO9ML2_PID) },
{ } /* Terminating entry */
};
@@ -1479,16 +1491,8 @@ static void ftdi_close (struct usb_serial_port *port, struct file *filp)
} /* Note change no line if hupcl is off */
/* shutdown our bulk read */
- if (port->read_urb) {
- if (usb_unlink_urb (port->read_urb) < 0) {
- /* Generally, this isn't an error. If the previous
- read bulk callback occurred (or is about to occur)
- while the port was being closed or was throtted
- (and is still throttled), the read urb will not
- have been submitted. */
- dbg("%s - failed to unlink read urb (generally not an error)", __FUNCTION__);
- }
- }
+ if (port->read_urb)
+ usb_kill_urb(port->read_urb);
} /* ftdi_close */
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index 232213b02860..e7a7e0aba4f2 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -225,6 +225,21 @@
*/
#define FTDI_SUUNTO_SPORTS_PID 0xF680 /* Suunto Sports instrument */
+/*
+ * Definitions for B&B Electronics products.
+ */
+#define BANDB_VID 0x0856 /* B&B Electronics Vendor ID */
+#define BANDB_USOTL4_PID 0xAC01 /* USOTL4 Isolated RS-485 Converter */
+#define BANDB_USTL4_PID 0xAC02 /* USTL4 RS-485 Converter */
+#define BANDB_USO9ML2_PID 0xAC03 /* USO9ML2 Isolated RS-232 Converter */
+
+/*
+ * RM Michaelides CANview USB (http://www.rmcan.com)
+ * CAN filedbus interface adapter, addad by port GmbH www.port.de)
+ */
+#define FTDI_RM_VID 0x0403 /* Vendor Id */
+#define FTDI_RMCANVIEW_PID 0xfd60 /* Product Id */
+
/* Commands */
#define FTDI_SIO_RESET 0 /* Reset the port */
#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 196fea084f21..c88885f8ca05 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -147,9 +147,9 @@ static void generic_cleanup (struct usb_serial_port *port)
if (serial->dev) {
/* shutdown any bulk reads that might be going on */
if (serial->num_bulk_out)
- usb_unlink_urb (port->write_urb);
+ usb_kill_urb(port->write_urb);
if (serial->num_bulk_in)
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
}
}
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 1ef47613b958..b5d18c699a0e 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -1238,7 +1238,7 @@ static void edge_close (struct usb_serial_port *port, struct file * filp)
edge_port->openPending = FALSE;
if (edge_port->write_urb) {
- usb_unlink_urb (edge_port->write_urb);
+ usb_kill_urb(edge_port->write_urb);
}
if (edge_port->write_urb) {
@@ -2443,8 +2443,8 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer
if (status) {
/* something went wrong */
dbg("%s - usb_submit_urb(write bulk) failed", __FUNCTION__);
- usb_unlink_urb (urb);
- usb_free_urb (urb);
+ usb_kill_urb(urb);
+ usb_free_urb(urb);
return status;
}
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index ace42c870727..359d3c0b8a15 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -1972,7 +1972,7 @@ static void edge_close (struct usb_serial_port *port, struct file * filp)
/* chase the port close */
TIChasePort (edge_port);
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
/* assuming we can still talk to the device,
* send a close port command to it */
@@ -1987,7 +1987,7 @@ static void edge_close (struct usb_serial_port *port, struct file * filp)
--edge_port->edge_serial->num_ports_open;
if (edge_port->edge_serial->num_ports_open <= 0) {
/* last port is now closed, let's shut down our interrupt urb */
- usb_unlink_urb (port->serial->port[0]->interrupt_in_urb);
+ usb_kill_urb(port->serial->port[0]->interrupt_in_urb);
edge_port->edge_serial->num_ports_open = 0;
}
edge_port->close_pending = 0;
@@ -2121,7 +2121,7 @@ static void edge_throttle (struct usb_serial_port *port)
status = TIClearRts (edge_port);
}
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
}
static void edge_unthrottle (struct usb_serial_port *port)
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index 8fbce2732c51..1a748b515112 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -288,8 +288,8 @@ static void ipaq_close(struct usb_serial_port *port, struct file *filp)
/*
* shut down bulk read and write
*/
- usb_unlink_urb(port->write_urb);
- usb_unlink_urb(port->read_urb);
+ usb_kill_urb(port->write_urb);
+ usb_kill_urb(port->read_urb);
ipaq_destroy_lists(port);
kfree(priv);
usb_set_serial_port_data(port, NULL);
@@ -419,9 +419,8 @@ static void ipaq_write_gather(struct usb_serial_port *port)
struct ipaq_private *priv = usb_get_serial_port_data(port);
struct usb_serial *serial = port->serial;
int count, room;
- struct ipaq_packet *pkt;
+ struct ipaq_packet *pkt, *tmp;
struct urb *urb = port->write_urb;
- struct list_head *tmp;
if (urb->status == -EINPROGRESS) {
/* Should never happen */
@@ -429,9 +428,7 @@ static void ipaq_write_gather(struct usb_serial_port *port)
return;
}
room = URBDATA_SIZE;
- for (tmp = priv->queue.next; tmp != &priv->queue;) {
- pkt = list_entry(tmp, struct ipaq_packet, list);
- tmp = tmp->next;
+ list_for_each_entry_safe(pkt, tmp, &priv->queue, list) {
count = min(room, (int)(pkt->len - pkt->written));
memcpy(urb->transfer_buffer + (URBDATA_SIZE - room),
pkt->data + pkt->written, count);
@@ -503,22 +500,16 @@ static int ipaq_chars_in_buffer(struct usb_serial_port *port)
static void ipaq_destroy_lists(struct usb_serial_port *port)
{
struct ipaq_private *priv = usb_get_serial_port_data(port);
- struct list_head *tmp;
- struct ipaq_packet *pkt;
+ struct ipaq_packet *pkt, *tmp;
- for (tmp = priv->queue.next; tmp != &priv->queue;) {
- pkt = list_entry(tmp, struct ipaq_packet, list);
- tmp = tmp->next;
+ list_for_each_entry_safe(pkt, tmp, &priv->queue, list) {
kfree(pkt->data);
kfree(pkt);
}
- for (tmp = priv->freelist.next; tmp != &priv->freelist;) {
- pkt = list_entry(tmp, struct ipaq_packet, list);
- tmp = tmp->next;
+ list_for_each_entry_safe(pkt, tmp, &priv->freelist, list) {
kfree(pkt->data);
kfree(pkt);
}
- return;
}
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
new file mode 100644
index 000000000000..2fc04f905a5c
--- /dev/null
+++ b/drivers/usb/serial/ipw.c
@@ -0,0 +1,496 @@
+/*
+ * IPWireless 3G UMTS TDD Modem driver (USB connected)
+ *
+ * Copyright (C) 2004 Roelf Diedericks <roelfd@inet.co.za>
+ * Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * All information about the device was acquired using SnoopyPro
+ * on MSFT's O/S, and examing the MSFT drivers' debug output
+ * (insanely left _on_ in the enduser version)
+ *
+ * It was written out of frustration with the IPWireless USB modem
+ * supplied by Axity3G/Sentech South Africa not supporting
+ * Linux whatsoever.
+ *
+ * Nobody provided any proprietary information that was not already
+ * available for this device.
+ *
+ * The modem adheres to the "3GPP TS 27.007 AT command set for 3G
+ * User Equipment (UE)" standard, available from
+ * http://www.3gpp.org/ftp/Specs/html-info/27007.htm
+ *
+ * The code was only tested the IPWireless handheld modem distributed
+ * in South Africa by Sentech.
+ *
+ * It may work for Woosh Inc in .nz too, as it appears they use the
+ * same kit.
+ *
+ * There is still some work to be done in terms of handling
+ * DCD, DTR, RTS, CTS which are currently faked.
+ * It's good enough for PPP at this point. It's based off all kinds of
+ * code found in usb/serial and usb/class
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+#include "usb-serial.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.3"
+#define DRIVER_AUTHOR "Roelf Diedericks"
+#define DRIVER_DESC "IPWireless tty driver"
+
+#define IPW_TTY_MAJOR 240 /* real device node major id, experimental range */
+#define IPW_TTY_MINORS 256 /* we support 256 devices, dunno why, it'd be insane :) */
+
+#define USB_IPW_MAGIC 0x6d02 /* magic number for ipw struct */
+
+
+/* Message sizes */
+#define EVENT_BUFFER_SIZE 0xFF
+#define CHAR2INT16(c1,c0) (((u32)((c1) & 0xff) << 8) + (u32)((c0) & 0xff))
+#define NUM_BULK_URBS 24
+#define NUM_CONTROL_URBS 16
+
+/* vendor/product pairs that are known work with this driver*/
+#define IPW_VID 0x0bc3
+#define IPW_PID 0x0001
+
+
+/* Vendor commands: */
+
+/* baud rates */
+enum {
+ ipw_sio_b256000 = 0x000e,
+ ipw_sio_b128000 = 0x001d,
+ ipw_sio_b115200 = 0x0020,
+ ipw_sio_b57600 = 0x0040,
+ ipw_sio_b56000 = 0x0042,
+ ipw_sio_b38400 = 0x0060,
+ ipw_sio_b19200 = 0x00c0,
+ ipw_sio_b14400 = 0x0100,
+ ipw_sio_b9600 = 0x0180,
+ ipw_sio_b4800 = 0x0300,
+ ipw_sio_b2400 = 0x0600,
+ ipw_sio_b1200 = 0x0c00,
+ ipw_sio_b600 = 0x1800
+};
+
+/* data bits */
+#define ipw_dtb_7 0x700
+#define ipw_dtb_8 0x810 // ok so the define is misleading, I know, but forces 8,n,1
+ // I mean, is there a point to any other setting these days? :)
+
+/* usb control request types : */
+#define IPW_SIO_RXCTL 0x00 // control bulk rx channel transmissions, value=1/0 (on/off)
+#define IPW_SIO_SET_BAUD 0x01 // set baud, value=requested ipw_sio_bxxxx
+#define IPW_SIO_SET_LINE 0x03 // set databits, parity. value=ipw_dtb_x
+#define IPW_SIO_SET_PIN 0x03 // set/clear dtr/rts value=ipw_pin_xxx
+#define IPW_SIO_POLL 0x08 // get serial port status byte, call with value=0
+#define IPW_SIO_INIT 0x11 // initializes ? value=0 (appears as first thing todo on open)
+#define IPW_SIO_PURGE 0x12 // purge all transmissions?, call with value=numchar_to_purge
+#define IPW_SIO_HANDFLOW 0x13 // set xon/xoff limits value=0, and a buffer of 0x10 bytes
+#define IPW_SIO_SETCHARS 0x13 // set the flowcontrol special chars, value=0, buf=6 bytes,
+ // last 2 bytes contain flowcontrol chars e.g. 00 00 00 00 11 13
+
+/* values used for request IPW_SIO_SET_PIN */
+#define IPW_PIN_SETDTR 0x101
+#define IPW_PIN_SETRTS 0x202
+#define IPW_PIN_CLRDTR 0x100
+#define IPW_PIN_CLRRTS 0x200 // unconfirmed
+
+/* values used for request IPW_SIO_RXCTL */
+#define IPW_RXBULK_ON 1
+#define IPW_RXBULK_OFF 0
+
+/* various 16 byte hardcoded transferbuffers used by flow control */
+#define IPW_BYTES_FLOWINIT { 0x01, 0, 0, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+/* Interpretation of modem status lines */
+/* These need sorting out by individually connecting pins and checking
+ * results. FIXME!
+ * When data is being sent we see 0x30 in the lower byte; this must
+ * contain DSR and CTS ...
+ */
+#define IPW_DSR ((1<<4) | (1<<5))
+#define IPW_CTS ((1<<5) | (1<<4))
+
+#define IPW_WANTS_TO_SEND 0x30
+//#define IPW_DTR /* Data Terminal Ready */
+//#define IPW_CTS /* Clear To Send */
+//#define IPW_CD /* Carrier Detect */
+//#define IPW_DSR /* Data Set Ready */
+//#define IPW_RxD /* Receive pin */
+
+//#define IPW_LE
+//#define IPW_RTS
+//#define IPW_ST
+//#define IPW_SR
+//#define IPW_RI /* Ring Indicator */
+
+static struct usb_device_id usb_ipw_ids[] = {
+ { USB_DEVICE(IPW_VID, IPW_PID) },
+ { },
+};
+
+MODULE_DEVICE_TABLE(usb, usb_ipw_ids);
+
+static struct usb_driver usb_ipw_driver = {
+ .owner = THIS_MODULE,
+ .name = "ipwtty",
+ .probe = usb_serial_probe,
+ .disconnect = usb_serial_disconnect,
+ .id_table = usb_ipw_ids,
+};
+
+static int debug;
+
+static void ipw_read_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_serial_port *port = urb->context;
+ unsigned char *data = urb->transfer_buffer;
+ struct tty_struct *tty;
+ int i;
+ int result;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (urb->status) {
+ dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+ return;
+ }
+
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
+
+ tty = port->tty;
+ if (tty && urb->actual_length) {
+ for (i = 0; i < urb->actual_length ; ++i) {
+ /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
+ if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ tty_flip_buffer_push(tty);
+ }
+ /* this doesn't actually push the data through unless tty->low_latency is set */
+ tty_insert_flip_char(tty, data[i], 0);
+ }
+ tty_flip_buffer_push(tty);
+ }
+
+ /* Continue trying to always read */
+ usb_fill_bulk_urb (port->read_urb, port->serial->dev,
+ usb_rcvbulkpipe(port->serial->dev,
+ port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer,
+ port->read_urb->transfer_buffer_length,
+ ipw_read_bulk_callback, port);
+ result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+ if (result)
+ dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
+ return;
+}
+
+static int ipw_open(struct usb_serial_port *port, struct file *filp)
+{
+ struct usb_device *dev = port->serial->dev;
+ u8 buf_flow_static[16] = IPW_BYTES_FLOWINIT;
+ u8 *buf_flow_init;
+ int result;
+
+ dbg("%s", __FUNCTION__);
+
+ buf_flow_init = kmalloc(16, GFP_KERNEL);
+ if (!buf_flow_init)
+ return -ENOMEM;
+ memcpy(buf_flow_init, buf_flow_static, 16);
+
+ if (port->tty)
+ port->tty->low_latency = 1;
+
+ /* --1: Tell the modem to initialize (we think) From sniffs this is always the
+ * first thing that gets sent to the modem during opening of the device */
+ dbg("%s: Sending SIO_INIT (we guess)",__FUNCTION__);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev,0),
+ IPW_SIO_INIT,
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ 0,
+ 0, /* index */
+ NULL,
+ 0,
+ 100*HZ);
+ if (result < 0)
+ dev_err(&port->dev, "Init of modem failed (error = %d)", result);
+
+ /* reset the bulk pipes */
+ usb_clear_halt(dev, usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress));
+ usb_clear_halt(dev, usb_sndbulkpipe(dev, port->bulk_out_endpointAddress));
+
+ /*--2: Start reading from the device */
+ dbg("%s: setting up bulk read callback",__FUNCTION__);
+ usb_fill_bulk_urb(port->read_urb, dev,
+ usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress),
+ port->bulk_in_buffer,
+ port->bulk_in_size,
+ ipw_read_bulk_callback, port);
+ result = usb_submit_urb(port->read_urb, GFP_KERNEL);
+ if (result < 0)
+ dbg("%s - usb_submit_urb(read bulk) failed with status %d", __FUNCTION__, result);
+
+ /*--3: Tell the modem to open the floodgates on the rx bulk channel */
+ dbg("%s:asking modem for RxRead (RXBULK_ON)",__FUNCTION__);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ IPW_SIO_RXCTL,
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ IPW_RXBULK_ON,
+ 0, /* index */
+ NULL,
+ 0,
+ 100*HZ);
+ if (result < 0)
+ dev_err(&port->dev, "Enabling bulk RxRead failed (error = %d)", result);
+
+ /*--4: setup the initial flowcontrol */
+ dbg("%s:setting init flowcontrol (%s)",__FUNCTION__,buf_flow_init);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ IPW_SIO_HANDFLOW,
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ 0,
+ 0,
+ buf_flow_init,
+ 0x10,
+ 200*HZ);
+ if (result < 0)
+ dev_err(&port->dev, "initial flowcontrol failed (error = %d)", result);
+
+
+ /*--5: raise the dtr */
+ dbg("%s:raising dtr",__FUNCTION__);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ IPW_SIO_SET_PIN,
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ IPW_PIN_SETDTR,
+ 0,
+ NULL,
+ 0,
+ 200*HZ);
+ if (result < 0)
+ dev_err(&port->dev, "setting dtr failed (error = %d)", result);
+
+ /*--6: raise the rts */
+ dbg("%s:raising rts",__FUNCTION__);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ IPW_SIO_SET_PIN,
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ IPW_PIN_SETRTS,
+ 0,
+ NULL,
+ 0,
+ 200*HZ);
+ if (result < 0)
+ dev_err(&port->dev, "setting dtr failed (error = %d)", result);
+
+ kfree(buf_flow_init);
+ return 0;
+}
+
+static void ipw_close(struct usb_serial_port *port, struct file * filp)
+{
+ struct usb_device *dev = port->serial->dev;
+ int result;
+
+ if (tty_hung_up_p(filp)) {
+ dbg("%s: tty_hung_up_p ...", __FUNCTION__);
+ return;
+ }
+
+ /*--1: drop the dtr */
+ dbg("%s:dropping dtr",__FUNCTION__);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ IPW_SIO_SET_PIN,
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ IPW_PIN_CLRDTR,
+ 0,
+ NULL,
+ 0,
+ 200*HZ);
+ if (result < 0)
+ dev_err(&port->dev, "dropping dtr failed (error = %d)", result);
+
+ /*--2: drop the rts */
+ dbg("%s:dropping rts",__FUNCTION__);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ IPW_SIO_SET_PIN, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ IPW_PIN_CLRRTS,
+ 0,
+ NULL,
+ 0,
+ 200*HZ);
+ if (result < 0)
+ dev_err(&port->dev, "dropping rts failed (error = %d)", result);
+
+
+ /*--3: purge */
+ dbg("%s:sending purge",__FUNCTION__);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ IPW_SIO_PURGE, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ 0x03,
+ 0,
+ NULL,
+ 0,
+ 200*HZ);
+ if (result < 0)
+ dev_err(&port->dev, "purge failed (error = %d)", result);
+
+
+ /* send RXBULK_off (tell modem to stop transmitting bulk data on rx chan) */
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ IPW_SIO_RXCTL,
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ IPW_RXBULK_OFF,
+ 0, /* index */
+ NULL,
+ 0,
+ 100*HZ);
+
+ if (result < 0)
+ dev_err(&port->dev, "Disabling bulk RxRead failed (error = %d)", result);
+
+ /* shutdown any in-flight urbs that we know about */
+ usb_kill_urb(port->read_urb);
+ usb_kill_urb(port->write_urb);
+}
+
+static void ipw_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_serial_port *port = urb->context;
+
+ dbg("%s", __FUNCTION__);
+
+ if (urb->status)
+ dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+
+ schedule_work(&port->work);
+}
+
+static int ipw_write(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
+{
+ struct usb_device *dev = port->serial->dev;
+ int ret;
+
+ dbg("%s: TOP: count=%d, from_user=%d, in_interrupt=%ld", __FUNCTION__,
+ count, from_user, in_interrupt() );
+
+ if (count == 0) {
+ dbg("%s - write request of 0 bytes", __FUNCTION__);
+ return 0;
+ }
+
+ /* Racy and broken, FIXME properly! */
+ if (port->write_urb->status == -EINPROGRESS)
+ return 0;
+
+ count = min(count, port->bulk_out_size);
+ if (from_user) {
+ if (copy_from_user(port->bulk_out_buffer, buf, count))
+ return -EFAULT;
+ } else {
+ memcpy(port->bulk_out_buffer, buf, count);
+ }
+
+ dbg("%s count now:%d", __FUNCTION__, count);
+
+ usb_fill_bulk_urb(port->write_urb, dev,
+ usb_sndbulkpipe(dev, port->bulk_out_endpointAddress),
+ port->write_urb->transfer_buffer,
+ count,
+ ipw_write_bulk_callback,
+ port);
+
+ ret = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+ if (ret != 0) {
+ dbg("%s - usb_submit_urb(write bulk) failed with error = %d", __FUNCTION__, ret);
+ return ret;
+ }
+
+ dbg("%s returning %d", __FUNCTION__, count);
+ return count;
+}
+
+static int ipw_probe(struct usb_serial_port *port)
+{
+ return 0;
+}
+
+static int ipw_disconnect(struct usb_serial_port *port)
+{
+ usb_set_serial_port_data(port, NULL);
+ return 0;
+}
+
+static struct usb_serial_device_type ipw_device = {
+ .owner = THIS_MODULE,
+ .name = "IPWireless converter",
+ .short_name = "ipw",
+ .id_table = usb_ipw_ids,
+ .num_interrupt_in = NUM_DONT_CARE,
+ .num_bulk_in = 1,
+ .num_bulk_out = 1,
+ .num_ports = 1,
+ .open = ipw_open,
+ .close = ipw_close,
+ .port_probe = ipw_probe,
+ .port_remove = ipw_disconnect,
+ .write = ipw_write,
+ .write_bulk_callback = ipw_write_bulk_callback,
+ .read_bulk_callback = ipw_read_bulk_callback,
+};
+
+
+
+int usb_ipw_init(void)
+{
+ int retval;
+
+ retval = usb_serial_register(&ipw_device);
+ if (retval)
+ return retval;
+ retval = usb_register(&usb_ipw_driver);
+ if (retval) {
+ usb_serial_deregister(&ipw_device);
+ return retval;
+ }
+ info(DRIVER_DESC " " DRIVER_VERSION);
+ return 0;
+}
+
+void usb_ipw_exit(void)
+{
+ usb_deregister(&usb_ipw_driver);
+ usb_serial_deregister(&ipw_device);
+}
+
+module_init(usb_ipw_init);
+module_exit(usb_ipw_exit);
+
+/* Module information */
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
index 3d1571695bde..c2c536f194e0 100644
--- a/drivers/usb/serial/ir-usb.c
+++ b/drivers/usb/serial/ir-usb.c
@@ -322,7 +322,7 @@ static void ir_close (struct usb_serial_port *port, struct file * filp)
dbg("%s - port %d", __FUNCTION__, port->number);
/* shutdown our bulk read */
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
}
static int ir_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index cd441d783cbe..0a979e208167 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -285,7 +285,7 @@ static void keyspan_pda_rx_throttle (struct usb_serial_port *port)
upon the device too. */
dbg("keyspan_pda_rx_throttle port %d", port->number);
- usb_unlink_urb(port->interrupt_in_urb);
+ usb_kill_urb(port->interrupt_in_urb);
}
@@ -706,8 +706,8 @@ static void keyspan_pda_close(struct usb_serial_port *port, struct file *filp)
keyspan_pda_set_modem_info(serial, 0);
/* shutdown our bulk reads and writes */
- usb_unlink_urb (port->write_urb);
- usb_unlink_urb (port->interrupt_in_urb);
+ usb_kill_urb(port->write_urb);
+ usb_kill_urb(port->interrupt_in_urb);
}
}
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index c5207d19401a..9e60b3476775 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -336,12 +336,12 @@ static void klsi_105_shutdown (struct usb_serial *serial)
for (j = 0; j < NUM_URBS; j++) {
if (write_urbs[j]) {
/* FIXME - uncomment the following
- * usb_unlink_urb call when the host
+ * usb_kill_urb call when the host
* controllers get fixed to set
* urb->dev = NULL after the urb is
* finished. Otherwise this call
* oopses. */
- /* usb_unlink_urb(write_urbs[j]); */
+ /* usb_kill_urb(write_urbs[j]); */
if (write_urbs[j]->transfer_buffer)
kfree(write_urbs[j]->transfer_buffer);
usb_free_urb (write_urbs[j]);
@@ -467,12 +467,12 @@ static void klsi_105_close (struct usb_serial_port *port, struct file *filp)
err("Disabling read failed (error = %d)", rc);
/* shutdown our bulk reads and writes */
- usb_unlink_urb (port->write_urb);
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->write_urb);
+ usb_kill_urb(port->read_urb);
/* unlink our write pool */
/* FIXME */
/* wgg - do I need this? I think so. */
- usb_unlink_urb (port->interrupt_in_urb);
+ usb_kill_urb(port->interrupt_in_urb);
info("kl5kusb105 port stats: %ld bytes in, %ld bytes out", priv->bytes_in, priv->bytes_out);
} /* klsi_105_close */
@@ -994,7 +994,7 @@ static int klsi_105_ioctl (struct usb_serial_port *port, struct file * file,
static void klsi_105_throttle (struct usb_serial_port *port)
{
dbg("%s - port %d", __FUNCTION__, port->number);
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
}
static void klsi_105_unthrottle (struct usb_serial_port *port)
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index b479916ce269..33539874bd0c 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -350,14 +350,13 @@ static void kobil_close (struct usb_serial_port *port, struct file *filp)
{
dbg("%s - port %d", __FUNCTION__, port->number);
- if (port->write_urb){
- usb_unlink_urb( port->write_urb );
+ if (port->write_urb) {
+ usb_kill_urb(port->write_urb);
usb_free_urb( port->write_urb );
port->write_urb = NULL;
}
- if (port->interrupt_in_urb){
- usb_unlink_urb (port->interrupt_in_urb);
- }
+ if (port->interrupt_in_urb)
+ usb_kill_urb(port->interrupt_in_urb);
}
@@ -458,9 +457,8 @@ static int kobil_write (struct usb_serial_port *port, int from_user,
((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4))) ) {
// stop reading (except TWIN and KAAN SIM)
- if ( (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) ) {
- usb_unlink_urb( port->interrupt_in_urb );
- }
+ if ( (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) )
+ usb_kill_urb(port->interrupt_in_urb);
todo = priv->filled - priv->cur_pos;
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index 212906c74f25..1d7c682aed7a 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -480,9 +480,9 @@ static void mct_u232_close (struct usb_serial_port *port, struct file *filp)
if (port->serial->dev) {
/* shutdown our urbs */
- usb_unlink_urb (port->write_urb);
- usb_unlink_urb (port->read_urb);
- usb_unlink_urb (port->interrupt_in_urb);
+ usb_kill_urb(port->write_urb);
+ usb_kill_urb(port->read_urb);
+ usb_kill_urb(port->interrupt_in_urb);
}
} /* mct_u232_close */
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index 371aa2e8e3f1..7032d5f7c6e1 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -183,8 +183,8 @@ static void omninet_close (struct usb_serial_port *port, struct file * filp)
dbg("%s - port %d", __FUNCTION__, port->number);
wport = serial->port[1];
- usb_unlink_urb(wport->write_urb);
- usb_unlink_urb(port->read_urb);
+ usb_kill_urb(wport->write_urb);
+ usb_kill_urb(port->read_urb);
od = usb_get_serial_port_data(port);
if (od)
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index c61e56829a96..5be9c370313b 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -55,11 +55,26 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v0.11"
+#define DRIVER_VERSION "v0.12"
#define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver"
static int debug;
+#define PL2303_CLOSING_WAIT (30*HZ)
+
+#define PL2303_BUF_SIZE 1024
+#define PL2303_TMP_BUF_SIZE 1024
+
+static char pl2303_tmp_buf[PL2303_TMP_BUF_SIZE];
+static DECLARE_MUTEX(pl2303_tmp_buf_sem);
+
+struct pl2303_buf {
+ unsigned int buf_size;
+ char *buf_buf;
+ char *buf_get;
+ char *buf_put;
+};
+
static struct usb_device_id id_table [] = {
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
@@ -134,12 +149,24 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs);
static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs);
static int pl2303_write (struct usb_serial_port *port, int from_user,
const unsigned char *buf, int count);
+static void pl2303_send (struct usb_serial_port *port);
+static int pl2303_write_room(struct usb_serial_port *port);
+static int pl2303_chars_in_buffer(struct usb_serial_port *port);
static void pl2303_break_ctl(struct usb_serial_port *port,int break_state);
static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file);
static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
unsigned int set, unsigned int clear);
static int pl2303_startup (struct usb_serial *serial);
static void pl2303_shutdown (struct usb_serial *serial);
+static struct pl2303_buf *pl2303_buf_alloc(unsigned int size);
+static void pl2303_buf_free(struct pl2303_buf *pb);
+static void pl2303_buf_clear(struct pl2303_buf *pb);
+static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb);
+static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb);
+static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
+ unsigned int count);
+static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
+ unsigned int count);
/* All of the device info needed for the PL2303 SIO serial converter */
@@ -162,6 +189,8 @@ static struct usb_serial_device_type pl2303_device = {
.read_bulk_callback = pl2303_read_bulk_callback,
.read_int_callback = pl2303_read_int_callback,
.write_bulk_callback = pl2303_write_bulk_callback,
+ .write_room = pl2303_write_room,
+ .chars_in_buffer = pl2303_chars_in_buffer,
.attach = pl2303_startup,
.shutdown = pl2303_shutdown,
};
@@ -174,6 +203,8 @@ enum pl2303_type {
struct pl2303_private {
spinlock_t lock;
+ struct pl2303_buf *buf;
+ int write_urb_in_use;
wait_queue_head_t delta_msr_wait;
u8 line_control;
u8 line_status;
@@ -201,14 +232,28 @@ static int pl2303_startup (struct usb_serial *serial)
for (i = 0; i < serial->num_ports; ++i) {
priv = kmalloc (sizeof (struct pl2303_private), GFP_KERNEL);
if (!priv)
- return -ENOMEM;
+ goto cleanup;
memset (priv, 0x00, sizeof (struct pl2303_private));
spin_lock_init(&priv->lock);
+ priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE);
+ if (priv->buf == NULL) {
+ kfree(priv);
+ goto cleanup;
+ }
init_waitqueue_head(&priv->delta_msr_wait);
priv->type = type;
usb_set_serial_port_data(serial->port[i], priv);
}
return 0;
+
+cleanup:
+ for (--i; i>=0; --i) {
+ priv = usb_get_serial_port_data(serial->port[i]);
+ pl2303_buf_free(priv->buf);
+ kfree(priv);
+ usb_set_serial_port_data(serial->port[i], NULL);
+ }
+ return -ENOMEM;
}
static int set_control_lines (struct usb_device *dev, u8 value)
@@ -224,40 +269,109 @@ static int set_control_lines (struct usb_device *dev, u8 value)
static int pl2303_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
{
- int result;
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count);
if (!count)
return count;
- if (port->write_urb->status == -EINPROGRESS) {
- dbg("%s - already writing", __FUNCTION__);
- return 0;
- }
-
- count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
if (from_user) {
- if (copy_from_user (port->write_urb->transfer_buffer, buf, count))
+ if (count > PL2303_TMP_BUF_SIZE)
+ count = PL2303_TMP_BUF_SIZE;
+ down(&pl2303_tmp_buf_sem);
+ if (copy_from_user(pl2303_tmp_buf, buf, count)) {
+ up(&pl2303_tmp_buf_sem);
return -EFAULT;
- } else {
- memcpy (port->write_urb->transfer_buffer, buf, count);
+ }
+ buf = pl2303_tmp_buf;
}
-
+
+ spin_lock_irqsave(&priv->lock, flags);
+ count = pl2303_buf_put(priv->buf, buf, count);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ if (from_user)
+ up(&pl2303_tmp_buf_sem);
+
+ pl2303_send(port);
+
+ return count;
+}
+
+static void pl2303_send(struct usb_serial_port *port)
+{
+ int count, result;
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (priv->write_urb_in_use) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return;
+ }
+
+ count = pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer,
+ port->bulk_out_size);
+
+ if (count == 0) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return;
+ }
+
+ priv->write_urb_in_use = 1;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->write_urb->transfer_buffer);
port->write_urb->transfer_buffer_length = count;
port->write_urb->dev = port->serial->dev;
result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
- if (result)
+ if (result) {
dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result);
- else
- result = count;
+ priv->write_urb_in_use = 0;
+ // TODO: reschedule pl2303_send
+ }
- return result;
+ schedule_work(&port->work);
}
+static int pl2303_write_room(struct usb_serial_port *port)
+{
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
+ int room = 0;
+ unsigned long flags;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ room = pl2303_buf_space_avail(priv->buf);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ dbg("%s - returns %d", __FUNCTION__, room);
+ return room;
+}
+
+static int pl2303_chars_in_buffer(struct usb_serial_port *port)
+{
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
+ int chars = 0;
+ unsigned long flags;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+ spin_lock_irqsave(&priv->lock, flags);
+ chars = pl2303_buf_data_avail(priv->buf);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ dbg("%s - returns %d", __FUNCTION__, chars);
+ return chars;
+}
static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios)
{
@@ -422,7 +536,7 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
}
kfree (buf);
-}
+}
static int pl2303_open (struct usb_serial_port *port, struct file *filp)
{
@@ -461,7 +575,7 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp)
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0);
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0, 1);
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 1, 0);
-
+
if (priv->type == HX) {
/* HX chip */
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 2, 0x44);
@@ -504,45 +618,67 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp)
static void pl2303_close (struct usb_serial_port *port, struct file *filp)
{
- struct pl2303_private *priv;
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
unsigned int c_cflag;
- int result;
+ int bps;
+ long timeout;
+ wait_queue_t wait; \
dbg("%s - port %d", __FUNCTION__, port->number);
- /* shutdown our urbs */
- dbg("%s - shutting down urbs", __FUNCTION__);
- result = usb_unlink_urb (port->write_urb);
- if (result)
- dbg("%s - usb_unlink_urb (write_urb)"
- " failed with reason: %d", __FUNCTION__,
- result);
+ /* wait for data to drain from the buffer */
+ spin_lock_irqsave(&priv->lock, flags);
+ timeout = PL2303_CLOSING_WAIT;
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&port->tty->write_wait, &wait);
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (pl2303_buf_data_avail(priv->buf) == 0
+ || timeout == 0 || signal_pending(current)
+ || !usb_get_intfdata(port->serial->interface)) /* disconnect */
+ break;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ timeout = schedule_timeout(timeout);
+ spin_lock_irqsave(&priv->lock, flags);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&port->tty->write_wait, &wait);
+ /* clear out any remaining data in the buffer */
+ pl2303_buf_clear(priv->buf);
+ spin_unlock_irqrestore(&priv->lock, flags);
- result = usb_unlink_urb (port->read_urb);
- if (result)
- dbg("%s - usb_unlink_urb (read_urb) "
- "failed with reason: %d", __FUNCTION__,
- result);
+ /* wait for characters to drain from the device */
+ /* (this is long enough for the entire 256 byte */
+ /* pl2303 hardware buffer to drain with no flow */
+ /* control for data rates of 1200 bps or more, */
+ /* for lower rates we should really know how much */
+ /* data is in the buffer to compute a delay */
+ /* that is not unnecessarily long) */
+ bps = tty_get_baud_rate(port->tty);
+ if (bps > 1200)
+ timeout = max((HZ*2560)/bps,HZ/10);
+ else
+ timeout = 2*HZ;
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(timeout);
- result = usb_unlink_urb (port->interrupt_in_urb);
- if (result)
- dbg("%s - usb_unlink_urb (interrupt_in_urb)"
- " failed with reason: %d", __FUNCTION__,
- result);
+ /* shutdown our urbs */
+ dbg("%s - shutting down urbs", __FUNCTION__);
+ usb_kill_urb(port->write_urb);
+ usb_kill_urb(port->read_urb);
+ usb_kill_urb(port->interrupt_in_urb);
if (port->tty) {
c_cflag = port->tty->termios->c_cflag;
if (c_cflag & HUPCL) {
/* drop DTR and RTS */
- priv = usb_get_serial_port_data(port);
spin_lock_irqsave(&priv->lock, flags);
priv->line_control = 0;
spin_unlock_irqrestore (&priv->lock, flags);
set_control_lines (port->serial->dev, 0);
}
}
-
}
static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
@@ -672,12 +808,17 @@ static void pl2303_break_ctl (struct usb_serial_port *port, int break_state)
static void pl2303_shutdown (struct usb_serial *serial)
{
int i;
+ struct pl2303_private *priv;
dbg("%s", __FUNCTION__);
for (i = 0; i < serial->num_ports; ++i) {
- kfree (usb_get_serial_port_data(serial->port[i]));
- usb_set_serial_port_data(serial->port[i], NULL);
+ priv = usb_get_serial_port_data(serial->port[i]);
+ if (priv) {
+ pl2303_buf_free(priv->buf);
+ kfree(priv);
+ usb_set_serial_port_data(serial->port[i], NULL);
+ }
}
}
@@ -815,11 +956,23 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
{
struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
int result;
dbg("%s - port %d", __FUNCTION__, port->number);
-
- if (urb->status) {
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+ priv->write_urb_in_use = 0;
+ return;
+ default:
/* error in the urb, so we have to resubmit it */
dbg("%s - Overflow in write", __FUNCTION__);
dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
@@ -828,14 +981,199 @@ static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
if (result)
dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", __FUNCTION__, result);
+ else
+ return;
+ }
- return;
+ priv->write_urb_in_use = 0;
+
+ /* send any buffered data */
+ pl2303_send(port);
+}
+
+
+/*
+ * pl2303_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+
+static struct pl2303_buf *pl2303_buf_alloc(unsigned int size)
+{
+
+ struct pl2303_buf *pb;
+
+
+ if (size == 0)
+ return NULL;
+
+ pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL);
+ if (pb == NULL)
+ return NULL;
+
+ pb->buf_buf = kmalloc(size, GFP_KERNEL);
+ if (pb->buf_buf == NULL) {
+ kfree(pb);
+ return NULL;
}
- schedule_work(&port->work);
+ pb->buf_size = size;
+ pb->buf_get = pb->buf_put = pb->buf_buf;
+
+ return pb;
+
+}
+
+
+/*
+ * pl2303_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+
+static void pl2303_buf_free(struct pl2303_buf *pb)
+{
+ if (pb != NULL) {
+ if (pb->buf_buf != NULL)
+ kfree(pb->buf_buf);
+ kfree(pb);
+ }
+}
+
+
+/*
+ * pl2303_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+
+static void pl2303_buf_clear(struct pl2303_buf *pb)
+{
+ if (pb != NULL)
+ pb->buf_get = pb->buf_put;
+ /* equivalent to a get of all data available */
+}
+
+
+/*
+ * pl2303_buf_data_avail
+ *
+ * Return the number of bytes of data available in the circular
+ * buffer.
+ */
+
+static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb)
+{
+ if (pb != NULL)
+ return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size);
+ else
+ return 0;
+}
+
+
+/*
+ * pl2303_buf_space_avail
+ *
+ * Return the number of bytes of space available in the circular
+ * buffer.
+ */
+
+static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb)
+{
+ if (pb != NULL)
+ return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size);
+ else
+ return 0;
+}
+
+
+/*
+ * pl2303_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+
+static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
+ unsigned int count)
+{
+
+ unsigned int len;
+
+
+ if (pb == NULL)
+ return 0;
+
+ len = pl2303_buf_space_avail(pb);
+ if (count > len)
+ count = len;
+
+ if (count == 0)
+ return 0;
+
+ len = pb->buf_buf + pb->buf_size - pb->buf_put;
+ if (count > len) {
+ memcpy(pb->buf_put, buf, len);
+ memcpy(pb->buf_buf, buf+len, count - len);
+ pb->buf_put = pb->buf_buf + count - len;
+ } else {
+ memcpy(pb->buf_put, buf, count);
+ if (count < len)
+ pb->buf_put += count;
+ else /* count == len */
+ pb->buf_put = pb->buf_buf;
+ }
+
+ return count;
+
}
+/*
+ * pl2303_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+
+static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
+ unsigned int count)
+{
+
+ unsigned int len;
+
+
+ if (pb == NULL)
+ return 0;
+
+ len = pl2303_buf_data_avail(pb);
+ if (count > len)
+ count = len;
+
+ if (count == 0)
+ return 0;
+
+ len = pb->buf_buf + pb->buf_size - pb->buf_get;
+ if (count > len) {
+ memcpy(buf, pb->buf_get, len);
+ memcpy(buf+len, pb->buf_buf, count - len);
+ pb->buf_get = pb->buf_buf + count - len;
+ } else {
+ memcpy(buf, pb->buf_get, count);
+ if (count < len)
+ pb->buf_get += count;
+ else /* count == len */
+ pb->buf_get = pb->buf_buf;
+ }
+
+ return count;
+
+}
+
static int __init pl2303_init (void)
{
int retval;
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 03e8a7e1c8ee..e3b7c1dd2f4c 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -388,7 +388,7 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po
good_spot = 1;
for (j = 1; j <= num_ports-1; ++j)
- if ((serial_table[i+j]) || (i+j >= SERIAL_TTY_MINORS)) {
+ if ((i+j >= SERIAL_TTY_MINORS) || (serial_table[i+j])) {
good_spot = 0;
i += j;
break;
@@ -405,7 +405,7 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po
return NULL;
}
-static void return_serial (struct usb_serial *serial)
+static void return_serial(struct usb_serial *serial)
{
int i;
@@ -417,8 +417,6 @@ static void return_serial (struct usb_serial *serial)
for (i = 0; i < serial->num_ports; ++i) {
serial_table[serial->minor + i] = NULL;
}
-
- return;
}
static void destroy_serial(struct kref *kref)
@@ -455,15 +453,15 @@ static void destroy_serial(struct kref *kref)
if (!port)
continue;
if (port->read_urb) {
- usb_unlink_urb(port->read_urb);
+ usb_kill_urb(port->read_urb);
usb_free_urb(port->read_urb);
}
if (port->write_urb) {
- usb_unlink_urb(port->write_urb);
+ usb_kill_urb(port->write_urb);
usb_free_urb(port->write_urb);
}
if (port->interrupt_in_urb) {
- usb_unlink_urb(port->interrupt_in_urb);
+ usb_kill_urb(port->interrupt_in_urb);
usb_free_urb(port->interrupt_in_urb);
}
kfree(port->bulk_in_buffer);
@@ -621,15 +619,12 @@ static void serial_throttle (struct tty_struct * tty)
if (!port->open_count) {
dbg ("%s - port not open", __FUNCTION__);
- goto exit;
+ return;
}
/* pass on to the driver specific version of this function */
if (port->serial->type->throttle)
port->serial->type->throttle(port);
-
-exit:
- ;
}
static void serial_unthrottle (struct tty_struct * tty)
@@ -640,15 +635,12 @@ static void serial_unthrottle (struct tty_struct * tty)
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
- goto exit;
+ return;
}
/* pass on to the driver specific version of this function */
if (port->serial->type->unthrottle)
port->serial->type->unthrottle(port);
-
-exit:
- ;
}
static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
@@ -681,15 +673,12 @@ static void serial_set_termios (struct tty_struct *tty, struct termios * old)
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
- goto exit;
+ return;
}
/* pass on to the driver specific version of this function if it is available */
if (port->serial->type->set_termios)
port->serial->type->set_termios(port, old);
-
-exit:
- ;
}
static void serial_break (struct tty_struct *tty, int break_state)
@@ -700,15 +689,12 @@ static void serial_break (struct tty_struct *tty, int break_state)
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
- goto exit;
+ return;
}
/* pass on to the driver specific version of this function if it is available */
if (port->serial->type->break_ctl)
port->serial->type->break_ctl(port, break_state);
-
-exit:
- ;
}
static int serial_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data)
@@ -814,15 +800,15 @@ static void port_release(struct device *dev)
dbg ("%s - %s", __FUNCTION__, dev->bus_id);
if (port->read_urb) {
- usb_unlink_urb(port->read_urb);
+ usb_kill_urb(port->read_urb);
usb_free_urb(port->read_urb);
}
if (port->write_urb) {
- usb_unlink_urb(port->write_urb);
+ usb_kill_urb(port->write_urb);
usb_free_urb(port->write_urb);
}
if (port->interrupt_in_urb) {
- usb_unlink_urb(port->interrupt_in_urb);
+ usb_kill_urb(port->interrupt_in_urb);
usb_free_urb(port->interrupt_in_urb);
}
kfree(port->bulk_in_buffer);
@@ -853,6 +839,25 @@ static struct usb_serial * create_serial (struct usb_device *dev,
return serial;
}
+static struct usb_serial_device_type *search_serial_device(struct usb_interface *iface)
+{
+ struct list_head *p;
+ const struct usb_device_id *id;
+ struct usb_serial_device_type *t;
+
+ /* List trough know devices and see if the usb id matches */
+ list_for_each(p, &usb_serial_driver_list) {
+ t = list_entry(p, struct usb_serial_device_type, driver_list);
+ id = usb_match_id(iface, t->id_table);
+ if (id != NULL) {
+ dbg("descriptor matches");
+ return t;
+ }
+ }
+
+ return NULL;
+}
+
int usb_serial_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
@@ -865,9 +870,7 @@ int usb_serial_probe(struct usb_interface *interface,
struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];
struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];
struct usb_serial_device_type *type = NULL;
- struct list_head *tmp;
int retval;
- int found;
int minor;
int buffer_size;
int i;
@@ -876,22 +879,9 @@ int usb_serial_probe(struct usb_interface *interface,
int num_bulk_out = 0;
int num_ports = 0;
int max_endpoints;
- const struct usb_device_id *id_pattern = NULL;
-
- /* loop through our list of known serial converters, and see if this
- device matches. */
- found = 0;
- list_for_each (tmp, &usb_serial_driver_list) {
- type = list_entry(tmp, struct usb_serial_device_type, driver_list);
- id_pattern = usb_match_id(interface, type->id_table);
- if (id_pattern != NULL) {
- dbg("descriptor matches");
- found = 1;
- break;
- }
- }
- if (!found) {
- /* no match */
+
+ type = search_serial_device(interface);
+ if (!type) {
dbg("none matched");
return -ENODEV;
}
@@ -899,17 +889,21 @@ int usb_serial_probe(struct usb_interface *interface,
serial = create_serial (dev, interface, type);
if (!serial) {
dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__);
- return -ENODEV;
+ return -ENOMEM;
}
/* if this device type has a probe function, call it */
if (type->probe) {
+ const struct usb_device_id *id;
+
if (!try_module_get(type->owner)) {
dev_err(&interface->dev, "module get failed, exiting\n");
kfree (serial);
return -EIO;
}
- retval = type->probe (serial, id_pattern);
+
+ id = usb_match_id(interface, type->id_table);
+ retval = type->probe(serial, id);
module_put(type->owner);
if (retval) {
@@ -1053,6 +1047,7 @@ int usb_serial_probe(struct usb_interface *interface,
goto probe_error;
}
buffer_size = endpoint->wMaxPacketSize;
+ port->bulk_in_size = buffer_size;
port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
if (!port->bulk_in_buffer) {
@@ -1224,7 +1219,7 @@ struct tty_driver *usb_serial_tty_driver;
static int __init usb_serial_init(void)
{
int i;
- int result = 0;
+ int result;
usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);
if (!usb_serial_tty_driver)
@@ -1235,13 +1230,17 @@ static int __init usb_serial_init(void)
serial_table[i] = NULL;
}
- bus_register(&usb_serial_bus_type);
+ result = bus_register(&usb_serial_bus_type);
+ if (result) {
+ err("%s - registering bus driver failed", __FUNCTION__);
+ goto exit_bus;
+ }
/* register the generic driver, if we should */
result = usb_serial_generic_register(debug);
if (result < 0) {
err("%s - registering generic driver failed", __FUNCTION__);
- goto exit;
+ goto exit_generic;
}
usb_serial_tty_driver->owner = THIS_MODULE;
@@ -1259,7 +1258,7 @@ static int __init usb_serial_init(void)
result = tty_register_driver(usb_serial_tty_driver);
if (result) {
err("%s - tty_register_driver failed", __FUNCTION__);
- goto exit_generic;
+ goto exit_reg_driver;
}
/* register the USB driver */
@@ -1276,10 +1275,13 @@ static int __init usb_serial_init(void)
exit_tty:
tty_unregister_driver(usb_serial_tty_driver);
-exit_generic:
+exit_reg_driver:
usb_serial_generic_deregister();
-exit:
+exit_generic:
+ bus_unregister(&usb_serial_bus_type);
+
+exit_bus:
err ("%s - returning with error %d", __FUNCTION__, result);
put_tty_driver(usb_serial_tty_driver);
return result;
@@ -1332,17 +1334,13 @@ int usb_serial_register(struct usb_serial_device_type *new_device)
/* Add this device to our list of devices */
list_add(&new_device->driver_list, &usb_serial_driver_list);
- retval = usb_serial_bus_register (new_device);
-
- if (retval)
- goto error;
-
- info("USB Serial support registered for %s", new_device->name);
-
- return retval;
-error:
- err("problem %d when registering driver %s", retval, new_device->name);
- list_del(&new_device->driver_list);
+ retval = usb_serial_bus_register(new_device);
+ if (retval) {
+ err("problem %d when registering driver %s", retval, new_device->name);
+ list_del(&new_device->driver_list);
+ }
+ else
+ info("USB Serial support registered for %s", new_device->name);
return retval;
}
@@ -1369,6 +1367,7 @@ EXPORT_SYMBOL(usb_serial_port_softint);
/* Module information */
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_VERSION( DRIVER_VERSION );
MODULE_LICENSE("GPL");
module_param(debug, bool, S_IRUGO | S_IWUSR);
diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h
index e2e59560d7fb..d3702957b609 100644
--- a/drivers/usb/serial/usb-serial.h
+++ b/drivers/usb/serial/usb-serial.h
@@ -100,6 +100,7 @@ struct usb_serial_port {
__u8 interrupt_in_endpointAddress;
unsigned char * bulk_in_buffer;
+ int bulk_in_size;
struct urb * read_urb;
__u8 bulk_in_endpointAddress;
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index 8a0a75533e70..0e5d90552789 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -446,9 +446,9 @@ static void visor_close (struct usb_serial_port *port, struct file * filp)
dbg("%s - port %d", __FUNCTION__, port->number);
/* shutdown our urbs */
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
if (port->interrupt_in_urb)
- usb_unlink_urb (port->interrupt_in_urb);
+ usb_kill_urb(port->interrupt_in_urb);
/* Try to send shutdown message, if the device is gone, this will just fail. */
transfer_buffer = kmalloc (0x12, GFP_KERNEL);
@@ -655,7 +655,7 @@ exit:
static void visor_throttle (struct usb_serial_port *port)
{
dbg("%s - port %d", __FUNCTION__, port->number);
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
}
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index 1b759018e8b8..179a671a2770 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -679,7 +679,7 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp)
list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
urb = wrap->urb;
- usb_unlink_urb(urb);
+ usb_kill_urb(urb);
list_del(tmp);
list_add(tmp, &info->rx_urbs_free);
}
@@ -690,7 +690,7 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp)
list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) {
wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
urb = wrap->urb;
- usb_unlink_urb(urb);
+ usb_kill_urb(urb);
list_del(tmp);
list_add(tmp, &info->tx_urbs_free);
}
@@ -1343,7 +1343,7 @@ static void stop_command_port(struct usb_serial *serial)
spin_lock_irqsave(&command_info->lock, flags);
command_info->port_running--;
if (!command_info->port_running)
- usb_unlink_urb(command_port->read_urb);
+ usb_kill_urb(command_port->read_urb);
spin_unlock_irqrestore(&command_info->lock, flags);
}
@@ -1371,7 +1371,7 @@ static int start_port_read(struct usb_serial_port *port)
list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
urb = wrap->urb;
- usb_unlink_urb(urb);
+ usb_kill_urb(urb);
list_del(tmp);
list_add(tmp, &info->rx_urbs_free);
}
diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
index 8aa136b4b380..d599362bebcc 100644
--- a/drivers/usb/storage/isd200.c
+++ b/drivers/usb/storage/isd200.c
@@ -1053,12 +1053,6 @@ static int isd200_get_inquiry_data( struct us_data *us )
/* Standard IDE interface only supports disks */
info->InquiryData.DeviceType = DIRECT_ACCESS_DEVICE;
- /* 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
- * in Linux.
- */
- info->InquiryData.Versions = 0x2;
-
/* The length must be at least 36 (5 + 31) */
info->InquiryData.AdditionalLength = 0x1F;
diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c
index 99ed4d9bc490..9d3d77452525 100644
--- a/drivers/usb/storage/protocol.c
+++ b/drivers/usb/storage/protocol.c
@@ -58,38 +58,6 @@
***********************************************************************/
/*
- * 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
- */
-static void fix_inquiry_data(struct scsi_cmnd *srb)
-{
- unsigned char databuf[3];
- unsigned int index, offset;
-
- /* verify that it's an INQUIRY command */
- if (srb->cmnd[0] != INQUIRY)
- return;
-
- index = offset = 0;
- if (usb_stor_access_xfer_buf(databuf, sizeof(databuf), srb,
- &index, &offset, FROM_XFER_BUF) != sizeof(databuf))
- return;
-
- if ((databuf[2] & 7) == 2)
- return;
-
- US_DEBUGP("Fixing INQUIRY data to show SCSI rev 2 - was %d\n",
- databuf[2] & 7);
-
- /* Change the SCSI revision number */
- databuf[2] = (databuf[2] & ~7) | 2;
-
- index = offset = 0;
- usb_stor_access_xfer_buf(databuf, sizeof(databuf), srb,
- &index, &offset, TO_XFER_BUF);
-}
-
-/*
* Fix-up the return data from a READ CAPACITY command. My Feiya reader
* returns a value that is 1 too large.
*/
@@ -137,10 +105,6 @@ void usb_stor_qic157_command(struct scsi_cmnd *srb, struct us_data *us)
/* send the command to the transport layer */
usb_stor_invoke_transport(srb, us);
- if (srb->result == SAM_STAT_GOOD) {
- /* fix the INQUIRY data if necessary */
- fix_inquiry_data(srb);
- }
}
void usb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us)
@@ -160,11 +124,6 @@ void usb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us)
/* send the command to the transport layer */
usb_stor_invoke_transport(srb, us);
-
- if (srb->result == SAM_STAT_GOOD) {
- /* fix the INQUIRY data if necessary */
- fix_inquiry_data(srb);
- }
}
@@ -208,11 +167,6 @@ void usb_stor_ufi_command(struct scsi_cmnd *srb, struct us_data *us)
/* send the command to the transport layer */
usb_stor_invoke_transport(srb, us);
-
- if (srb->result == SAM_STAT_GOOD) {
- /* Fix the data for an INQUIRY, if necessary */
- fix_inquiry_data(srb);
- }
}
void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb,
@@ -222,9 +176,6 @@ void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb,
usb_stor_invoke_transport(srb, us);
if (srb->result == SAM_STAT_GOOD) {
- /* Fix the INQUIRY data if necessary */
- fix_inquiry_data(srb);
-
/* Fix the READ CAPACITY result if necessary */
if (us->flags & US_FL_FIX_CAPACITY)
fix_read_capacity(srb);
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index c18cd06ea31c..83d7003861ff 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -98,6 +98,23 @@ static int slave_configure(struct scsi_device *sdev)
* the end, scatter-gather buffers follow page boundaries. */
blk_queue_dma_alignment(sdev->request_queue, (512 - 1));
+ /* Set the SCSI level to at least 2. We'll leave it at 3 if that's
+ * what is originally reported. We need this to avoid confusing
+ * the SCSI layer with devices that report 0 or 1, but need 10-byte
+ * commands (ala ATAPI devices behind certain bridges, or devices
+ * which simply have broken INQUIRY data).
+ *
+ * NOTE: This means /dev/sg programs (ala cdrecord) will get the
+ * actual information. This seems to be the preference for
+ * programs like that.
+ *
+ * NOTE: This also means that /proc/scsi/scsi and sysfs may report
+ * the actual value or the modified one, depending on where the
+ * data comes from.
+ */
+ if (sdev->scsi_level < SCSI_2)
+ sdev->scsi_level = SCSI_2;
+
/* According to the technical support people at Genesys Logic,
* devices using their chips have problems transferring more than
* 32 KB at a time. In practice people have found that 64 KB
@@ -266,7 +283,7 @@ static int device_reset(struct scsi_cmnd *srb)
static int bus_reset(struct scsi_cmnd *srb)
{
struct us_data *us = (struct us_data *)srb->device->host->hostdata[0];
- int result;
+ int result, rc;
US_DEBUGP("%s called\n", __FUNCTION__);
if (us->sm_state != US_STATE_IDLE) {
@@ -291,8 +308,16 @@ static int bus_reset(struct scsi_cmnd *srb)
result = -EBUSY;
US_DEBUGP("Refusing to reset a multi-interface device\n");
} else {
- result = usb_reset_device(us->pusb_dev);
- US_DEBUGP("usb_reset_device returns %d\n", result);
+ rc = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
+ if (rc < 0) {
+ US_DEBUGP("unable to lock device for reset: %d\n", rc);
+ result = rc;
+ } else {
+ result = usb_reset_device(us->pusb_dev);
+ if (rc)
+ usb_unlock_device(us->pusb_dev);
+ US_DEBUGP("usb_reset_device returns %d\n", result);
+ }
}
up(&(us->dev_semaphore));
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 42f784321607..07b919d800a1 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -911,7 +911,6 @@ int usb_stor_Bulk_max_lun(struct us_data *us)
int result;
/* issue the command */
- us->iobuf[0] = 0;
result = usb_stor_control_msg(us, us->recv_ctrl_pipe,
US_BULK_GET_MAX_LUN,
USB_DIR_IN | USB_TYPE_CLASS |
@@ -922,7 +921,7 @@ int usb_stor_Bulk_max_lun(struct us_data *us)
result, us->iobuf[0]);
/* if we have a successful request, return the result */
- if (result >= 0)
+ if (result > 0)
return us->iobuf[0];
/*
@@ -934,13 +933,16 @@ int usb_stor_Bulk_max_lun(struct us_data *us)
if (result == -EPIPE) {
usb_stor_clear_halt(us, us->recv_bulk_pipe);
usb_stor_clear_halt(us, us->send_bulk_pipe);
- /* return the default -- no LUNs */
- return 0;
}
- /* An answer or a STALL are the only valid responses. If we get
- * something else, return an indication of error */
- return -1;
+ /*
+ * Some devices don't like GetMaxLUN. They may STALL the control
+ * pipe, they may return a zero-length result, they may do nothing at
+ * all and timeout, or they may fail in even more bizarrely creative
+ * ways. In these cases the best approach is to use the default
+ * value: only one LUN.
+ */
+ return 0;
}
int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
@@ -1055,8 +1057,13 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
/* try to compute the actual residue, based on how much data
* was really transferred and what the device tells us */
- residue = min(residue, transfer_length);
- srb->resid = max(srb->resid, (int) residue);
+ if (residue) {
+ if (!(us->flags & US_FL_IGNORE_RESIDUE) ||
+ srb->sc_data_direction == DMA_TO_DEVICE) {
+ residue = min(residue, transfer_length);
+ srb->resid = max(srb->resid, (int) residue);
+ }
+ }
/* based on the status code, we report good or bad */
switch (bcs->Status) {
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 4fe25f6b0432..1e0eb58f0c09 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -36,13 +36,16 @@
/* If you edit this file, please try to keep it sorted first by VendorID,
* then by ProductID.
*
- * If you want to add an entry for this file, please send the following
- * to greg@kroah.com:
- * - patch that adds the entry for your device which includes your
- * email address right above the entry.
+ * If you want to add an entry for this file, be sure to include the
+ * following information:
+ * - a patch that adds the entry for your device, including your
+ * email address right above the entry (plus maybe a brief
+ * explanation of the reason for the entry),
* - a copy of /proc/bus/usb/devices with your device plugged in
* running with this patch.
- *
+ * Send your submission to either Phil Dibowitz <phil@ipom.com> or
+ * Alan Stern <stern@rowland.harvard.edu>, and don't forget to CC: the
+ * USB development list <linux-usb-devel@lists.sourceforge.net>.
*/
UNUSUAL_DEV( 0x03ee, 0x6901, 0x0000, 0x0100,
@@ -68,16 +71,6 @@ UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001,
US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0),
#endif
-/* <torsten.scherer@uni-bielefeld.de>: I don't know the name of the bridge
- * manufacturer, but I've got an external USB drive by the Revoltec company
- * that needs this. otherwise the drive is recognized as /dev/sda, but any
- * access to it blocks indefinitely.
- */
-UNUSUAL_DEV( 0x0402, 0x5621, 0x0103, 0x0103,
- "Revoltec",
- "USB/IDE Bridge (ATA/ATAPI)",
- US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY),
-
/* Deduced by Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
* Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message
* always fails and confuses drive.
@@ -95,12 +88,6 @@ UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100,
US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ),
#endif
-/* Patch submitted by Alessandro Fracchetti <al.fracchetti@tin.it> */
-UNUSUAL_DEV( 0x0482, 0x0105, 0x0100, 0x0100,
- "Kyocera",
- "Finecam L3",
- US_SC_SCSI, US_PR_BULK, NULL, US_FL_FIX_INQUIRY),
-
/* Patch submitted by Philipp Friedrich <philipp@void.at> */
UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100,
"Kyocera",
@@ -121,6 +108,7 @@ UNUSUAL_DEV( 0x0482, 0x0103, 0x0100, 0x0100,
/* Patch for Kyocera Finecam L3
* Submitted by Michael Krauth <michael.krauth@web.de>
+ * and Alessandro Fracchetti <al.fracchetti@tin.it>
*/
UNUSUAL_DEV( 0x0482, 0x0105, 0x0100, 0x0100,
"Kyocera",
@@ -149,10 +137,13 @@ UNUSUAL_DEV( 0x04b8, 0x0602, 0x0110, 0x0110,
"785EPX Storage",
US_SC_SCSI, US_PR_BULK, NULL, US_FL_SINGLE_LUN),
+/* Not sure who reported this originally but
+ * Pavel Machek <pavel@ucw.cz> reported that the extra US_FL_SINGLE_LUN
+ * flag be added */
UNUSUAL_DEV( 0x04cb, 0x0100, 0x0000, 0x2210,
"Fujifilm",
"FinePix 1400Zoom",
- US_SC_UFI, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY),
+ US_SC_UFI, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY | US_FL_SINGLE_LUN),
/* Reported by Peter Wächtler <pwaechtler@loewe-komp.de>
* The device needs the flags only.
@@ -180,6 +171,16 @@ UNUSUAL_DEV( 0x04da, 0x0d05, 0x0000, 0x0000,
"CD-R/RW Drive",
US_SC_8070, US_PR_CB, NULL, 0),
+/* Reported by Adriaan Penning <a.penning@luon.net>
+ * Note that these cameras report "Medium not present" after
+ * ALLOW_MEDIUM_REMOVAL, so they also need to be marked
+ * NOT_LOCKABLE in the SCSI blacklist (and the vendor is MATSHITA). */
+UNUSUAL_DEV( 0x04da, 0x2372, 0x0000, 0x9999,
+ "Panasonic",
+ "DMC-LCx Camera",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY ),
+
/* Most of the following entries were developed with the help of
* Shuttle/SCM directly.
*/
@@ -265,6 +266,21 @@ UNUSUAL_DEV( 0x0525, 0xa140, 0x0100, 0x0100,
US_SC_8070, US_PR_BULK, NULL,
US_FL_FIX_INQUIRY ),
+/* Reported by Iacopo Spalletti <avvisi@spalletti.it> */
+UNUSUAL_DEV( 0x052b, 0x1807, 0x0100, 0x0100,
+ "Tekom Technologies, Inc",
+ "300_CAMERA",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
+/* Yakumo Mega Image 37
+ * Submitted by Stephan Fuhrmann <atomenergie@t-online.de> */
+UNUSUAL_DEV( 0x052b, 0x1801, 0x0100, 0x0100,
+ "Tekom Technologies, Inc",
+ "300_CAMERA",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
/* This entry is needed because the device reports Sub=ff */
UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0450,
"Sony",
@@ -383,10 +399,17 @@ UNUSUAL_DEV( 0x0595, 0x4343, 0x0000, 0x2210,
"Digital Camera EX-20 DSC",
US_SC_8070, US_PR_DEVICE, NULL, 0 ),
+/* The entry was here before I took over, and had US_SC_RBC. It turns
+ * out that isn't needed. Additionally, Torsten Eriksson
+ * <Torsten.Eriksson@bergianska.se> is able to use his device fine
+ * without this entry at all - but I don't suspect that will be true
+ * for all users (the protocol is likely needed), so is staying at
+ * this time. - Phil Dibowitz <phil@ipom.com>
+ */
UNUSUAL_DEV( 0x059f, 0xa601, 0x0200, 0x0200,
"LaCie",
"USB Hard Disk",
- US_SC_RBC, US_PR_CB, NULL, 0 ),
+ US_SC_DEVICE, US_PR_CB, NULL, 0 ),
/* Submitted by Joel Bourquard <numlock@freesurf.ch>
* Some versions of this device need the SubClass and Protocol overrides
@@ -439,36 +462,6 @@ UNUSUAL_DEV( 0x05dc, 0xb002, 0x0000, 0x0113,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_INQUIRY ),
-/* Reported by Carlos Villegas <cav@uniscope.co.jp>
- * This device needs an INQUIRY of exactly 36-bytes to function.
- * That is the only reason this entry is needed.
- */
-UNUSUAL_DEV( 0x05e3, 0x0700, 0x0000, 0xffff,
- "Genesys Logic",
- "USB to IDE Card Reader",
- US_SC_DEVICE, US_PR_DEVICE, NULL,
- US_FL_FIX_INQUIRY ),
-
-/* Submitted Alexander Oltu <alexander@all-2.com> */
-UNUSUAL_DEV( 0x05e3, 0x0701, 0x0000, 0xffff,
- "Genesys Logic",
- "USB to IDE Optical",
- US_SC_DEVICE, US_PR_DEVICE, NULL,
- US_FL_MODE_XLATE ),
-
-/* Reported by Peter Marks <peter.marks@turner.com>
- * Like the SIIG unit above, this unit needs an INQUIRY to ask for exactly
- * 36 bytes of data. No more, no less. That is the only reason this entry
- * is needed.
- *
- * ST818 slim drives (rev 0.02) don't need special care.
-*/
-UNUSUAL_DEV( 0x05e3, 0x0702, 0x0000, 0xffff,
- "Genesys Logic",
- "USB to IDE Disk",
- US_SC_DEVICE, US_PR_DEVICE, NULL,
- US_FL_FIX_INQUIRY ),
-
/* Reported by Hanno Boeck <hanno@gmx.de>
* Taken from the Lycoris Kernel */
UNUSUAL_DEV( 0x0636, 0x0003, 0x0000, 0x9999,
@@ -554,6 +547,13 @@ UNUSUAL_DEV( 0x07ab, 0xfc01, 0x0000, 0x9999,
US_SC_QIC, US_PR_FREECOM, freecom_init, 0),
#endif
+/* Reported by Eero Volotinen <eero@ping-viini.org> */
+UNUSUAL_DEV( 0x07ab, 0xfccd, 0x0406, 0x0406,
+ "Freecom Technologies",
+ "FHD-Classic",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY),
+
UNUSUAL_DEV( 0x07af, 0x0004, 0x0100, 0x0133,
"Microtech",
"USB-SCSI-DB25",
@@ -724,12 +724,6 @@ UNUSUAL_DEV( 0x097a, 0x0001, 0x0000, 0x0001,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_MODE_XLATE ),
-UNUSUAL_DEV( 0x0a16, 0x8888, 0x0100, 0x0100,
- "IBM",
- "IBM USB Memory Key",
- US_SC_DEVICE, US_PR_DEVICE, NULL,
- US_FL_FIX_INQUIRY ),
-
/* This Pentax still camera is not conformant
* to the USB storage specification: -
* - It does not like the INQUIRY command. So we must handle this command
@@ -802,13 +796,28 @@ UNUSUAL_DEV( 0x0dd8, 0x1060, 0x0000, 0xffff,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_INQUIRY ),
+/* Patch by Stephan Walter <stephan.walter@epfl.ch>
+ * I don't know why, but it works... */
+UNUSUAL_DEV( 0x0dda, 0x0001, 0x0012, 0x0012,
+ "WINWARD",
+ "Music Disk",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
/* Submitted by Antoine Mairesse <antoine.mairesse@free.fr> */
UNUSUAL_DEV( 0x0ed1, 0x6660, 0x0100, 0x0300,
"USB",
"Solid state disk",
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_INQUIRY ),
-
+
+/* Reported by Rastislav Stanik <rs_kernel@yahoo.com> */
+UNUSUAL_DEV( 0x0ea0, 0x6828, 0x0110, 0x0110,
+ "USB",
+ "Flash Disk",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
/* Reported by Kevin Cernekee <kpc-usbdev@gelato.uiuc.edu>
* Tested on hardware version 1.10.
* Entry is needed only for the initializer function override.
@@ -830,6 +839,13 @@ UNUSUAL_DEV( 0x1065, 0x2136, 0x0000, 0x9999,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_MODE_XLATE ),
+/* Reported by Kotrla Vitezslav <kotrla@ceb.cz> */
+UNUSUAL_DEV( 0x1370, 0x6828, 0x0110, 0x0110,
+ "SWISSBIT",
+ "Black Silver",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
#ifdef CONFIG_USB_STORAGE_SDDR55
UNUSUAL_DEV( 0x55aa, 0xa103, 0x0000, 0x9999,
"Sandisk",
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 9579bdf4969a..32e2f1482c57 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -97,6 +97,11 @@ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
MODULE_DESCRIPTION("USB Mass Storage driver for Linux");
MODULE_LICENSE("GPL");
+static unsigned int delay_use = 5;
+module_param(delay_use, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
+
+
static int storage_probe(struct usb_interface *iface,
const struct usb_device_id *id);
@@ -882,6 +887,42 @@ static void dissociate_dev(struct us_data *us)
kfree(us);
}
+/* Thread to carry out delayed SCSI-device scanning */
+static int usb_stor_scan_thread(void * __us)
+{
+ struct us_data *us = (struct us_data *)__us;
+
+ /*
+ * This thread doesn't need any user-level access,
+ * so get rid of all our resources.
+ */
+ lock_kernel();
+ daemonize("usb-stor");
+ current->flags |= PF_NOFREEZE;
+ unlock_kernel();
+
+ printk(KERN_DEBUG
+ "usb-storage: device found at %d\n", us->pusb_dev->devnum);
+
+ /* Wait for the timeout to expire or for a disconnect */
+ if (delay_use > 0) {
+ printk(KERN_DEBUG "usb-storage: waiting for device "
+ "to settle before scanning\n");
+ wait_event_interruptible_timeout(us->scsi_scan_wait,
+ test_bit(US_FLIDX_DISCONNECTING, &us->flags),
+ delay_use * HZ);
+ }
+
+ /* If the device is still connected, perform the scanning */
+ if (!test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+ scsi_scan_host(us->host);
+ printk(KERN_DEBUG "usb-storage: device scan complete\n");
+ }
+
+ complete_and_exit(&us->scsi_scan_done, 0);
+}
+
+
/* Probe to see if we can drive a newly-connected USB device */
static int storage_probe(struct usb_interface *intf,
const struct usb_device_id *id)
@@ -903,6 +944,8 @@ static int storage_probe(struct usb_interface *intf,
init_MUTEX_LOCKED(&(us->sema));
init_completion(&(us->notify));
init_waitqueue_head(&us->dev_reset_wait);
+ init_waitqueue_head(&us->scsi_scan_wait);
+ init_completion(&us->scsi_scan_done);
/* Associate the us_data structure with the USB device */
result = associate_dev(us, intf);
@@ -951,12 +994,10 @@ static int storage_probe(struct usb_interface *intf,
if (result)
goto BadDevice;
- /* Acquire all the other resources */
+ /* Acquire all the other resources and add the host */
result = usb_stor_acquire_resources(us);
if (result)
goto BadDevice;
-
- /* Finally, add the host (this does SCSI device scanning) */
result = scsi_add_host(us->host, &intf->dev);
if (result) {
printk(KERN_WARNING USB_STORAGE
@@ -964,10 +1005,15 @@ static int storage_probe(struct usb_interface *intf,
goto BadDevice;
}
- scsi_scan_host(us->host);
+ /* Start up the thread for delayed SCSI-device scanning */
+ result = kernel_thread(usb_stor_scan_thread, us, CLONE_VM);
+ if (result < 0) {
+ printk(KERN_WARNING USB_STORAGE
+ "Unable to start the device-scanning thread\n");
+ scsi_remove_host(us->host);
+ goto BadDevice;
+ }
- printk(KERN_DEBUG
- "USB Mass Storage device found at %d\n", us->pusb_dev->devnum);
return 0;
/* We come here if there are any problems */
@@ -991,6 +1037,11 @@ static void storage_disconnect(struct usb_interface *intf)
usb_stor_stop_transport(us);
wake_up(&us->dev_reset_wait);
+ /* Interrupt the SCSI-device-scanning thread's time delay, and
+ * wait for the thread to finish */
+ wake_up(&us->scsi_scan_wait);
+ wait_for_completion(&us->scsi_scan_done);
+
/* Wait for the current command to finish, then remove the host */
down(&us->dev_semaphore);
up(&us->dev_semaphore);
@@ -1012,12 +1063,9 @@ static int __init usb_stor_init(void)
/* register the driver, return usb_register return code if error */
retval = usb_register(&usb_storage_driver);
- if (retval)
- goto out;
+ if (retval == 0)
+ printk(KERN_INFO "USB Mass Storage support registered.\n");
- /* we're all set */
- printk(KERN_INFO "USB Mass Storage support registered.\n");
-out:
return retval;
}
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
index 785f30be68c5..199594967870 100644
--- a/drivers/usb/storage/usb.h
+++ b/drivers/usb/storage/usb.h
@@ -73,6 +73,7 @@ struct us_unusual_dev {
#define US_FL_SCM_MULT_TARG 0x00000020 /* supports multiple targets */
#define US_FL_FIX_INQUIRY 0x00000040 /* INQUIRY response needs faking */
#define US_FL_FIX_CAPACITY 0x00000080 /* READ CAPACITY response too big */
+#define US_FL_IGNORE_RESIDUE 0x00000100 /* reported residue is wrong */
/* Dynamic flag definitions: used in set_bit() etc. */
#define US_FLIDX_URB_ACTIVE 18 /* 0x00040000 current_urb is in use */
@@ -161,6 +162,8 @@ struct us_data {
struct semaphore sema; /* to sleep thread on */
struct completion notify; /* thread begin/end */
wait_queue_head_t dev_reset_wait; /* wait during reset */
+ wait_queue_head_t scsi_scan_wait; /* wait before scanning */
+ struct completion scsi_scan_done; /* scan thread end */
/* subdriver information */
void *extra; /* Any extra data */