summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorDmitry Torokhov <dtor@insightbb.com>2007-05-01 00:24:54 -0400
committerDmitry Torokhov <dtor@insightbb.com>2007-05-01 00:24:54 -0400
commitbc95f3669f5e6f63cf0b84fe4922c3c6dd4aa775 (patch)
tree427fcf2a7287c16d4b5aa6cbf494d59579a6a8b1 /drivers/usb
parent3d29cdff999c37b3876082278a8134a0642a02cd (diff)
parentdc87c3985e9b442c60994308a96f887579addc39 (diff)
Merge master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6
Conflicts: drivers/usb/input/Makefile drivers/usb/input/gtco.c
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/Makefile5
-rw-r--r--drivers/usb/atm/cxacru.c411
-rw-r--r--drivers/usb/atm/ueagle-atm.c1
-rw-r--r--drivers/usb/atm/usbatm.c27
-rw-r--r--drivers/usb/class/cdc-acm.c114
-rw-r--r--drivers/usb/class/cdc-acm.h3
-rw-r--r--drivers/usb/class/usblp.c6
-rw-r--r--drivers/usb/core/Kconfig25
-rw-r--r--drivers/usb/core/Makefile2
-rw-r--r--drivers/usb/core/devices.c11
-rw-r--r--drivers/usb/core/devio.c126
-rw-r--r--drivers/usb/core/driver.c331
-rw-r--r--drivers/usb/core/endpoint.c2
-rw-r--r--drivers/usb/core/generic.c2
-rw-r--r--drivers/usb/core/hcd.c34
-rw-r--r--drivers/usb/core/hcd.h3
-rw-r--r--drivers/usb/core/hub.c70
-rw-r--r--drivers/usb/core/inode.c2
-rw-r--r--drivers/usb/core/message.c120
-rw-r--r--drivers/usb/core/otg_whitelist.h2
-rw-r--r--drivers/usb/core/quirks.c78
-rw-r--r--drivers/usb/core/sysfs.c201
-rw-r--r--drivers/usb/core/usb.c56
-rw-r--r--drivers/usb/core/usb.h35
-rw-r--r--drivers/usb/gadget/Kconfig22
-rw-r--r--drivers/usb/gadget/Makefile1
-rw-r--r--drivers/usb/gadget/at91_udc.c23
-rw-r--r--drivers/usb/gadget/ether.c7
-rw-r--r--drivers/usb/gadget/fsl_usb2_udc.c2500
-rw-r--r--drivers/usb/gadget/fsl_usb2_udc.h579
-rw-r--r--drivers/usb/gadget/gadget_chips.h8
-rw-r--r--drivers/usb/gadget/goku_udc.c29
-rw-r--r--drivers/usb/gadget/inode.c5
-rw-r--r--drivers/usb/gadget/omap_udc.c103
-rw-r--r--drivers/usb/gadget/pxa2xx_udc.c114
-rw-r--r--drivers/usb/gadget/pxa2xx_udc.h15
-rw-r--r--drivers/usb/gadget/rndis.h2
-rw-r--r--drivers/usb/gadget/serial.c1
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/ehci-dbg.c2
-rw-r--r--drivers/usb/host/ehci-fsl.h4
-rw-r--r--drivers/usb/host/ehci-hcd.c37
-rw-r--r--drivers/usb/host/ehci-hub.c9
-rw-r--r--drivers/usb/host/hc_crisv10.c4550
-rw-r--r--drivers/usb/host/hc_crisv10.h289
-rw-r--r--drivers/usb/host/isp116x-hcd.c2
-rw-r--r--drivers/usb/host/ohci-at91.c50
-rw-r--r--drivers/usb/host/ohci-hcd.c24
-rw-r--r--drivers/usb/host/ohci-pci.c32
-rw-r--r--drivers/usb/host/uhci-debug.c73
-rw-r--r--drivers/usb/host/uhci-hcd.c59
-rw-r--r--drivers/usb/host/uhci-hcd.h82
-rw-r--r--drivers/usb/host/uhci-hub.c11
-rw-r--r--drivers/usb/host/uhci-q.c191
-rw-r--r--drivers/usb/input/Kconfig145
-rw-r--r--drivers/usb/input/Makefile30
-rw-r--r--drivers/usb/input/gtco.c2
-rw-r--r--drivers/usb/input/hid-core.c1459
-rw-r--r--drivers/usb/input/hid-ff.c89
-rw-r--r--drivers/usb/input/hid-lgff.c150
-rw-r--r--drivers/usb/input/hid-pidff.c1331
-rw-r--r--drivers/usb/input/hid-plff.c129
-rw-r--r--drivers/usb/input/hid-tmff.c147
-rw-r--r--drivers/usb/input/hid-zpff.c111
-rw-r--r--drivers/usb/input/hiddev.c847
-rw-r--r--drivers/usb/input/usbhid.h87
-rw-r--r--drivers/usb/input/usbkbd.c360
-rw-r--r--drivers/usb/input/usbmouse.c243
-rw-r--r--drivers/usb/input/wacom_wac.c103
-rw-r--r--drivers/usb/input/wacom_wac.h1
-rw-r--r--drivers/usb/misc/Kconfig25
-rw-r--r--drivers/usb/misc/Makefile2
-rw-r--r--drivers/usb/misc/adutux.c48
-rw-r--r--drivers/usb/misc/appledisplay.c22
-rw-r--r--drivers/usb/misc/berry_charge.c140
-rw-r--r--drivers/usb/misc/cypress_cy7c63.c4
-rw-r--r--drivers/usb/misc/ftdi-elan.c25
-rw-r--r--drivers/usb/misc/iowarrior.c925
-rw-r--r--drivers/usb/misc/ldusb.c3
-rw-r--r--drivers/usb/misc/usblcd.c7
-rw-r--r--drivers/usb/mon/mon_bin.c16
-rw-r--r--drivers/usb/mon/mon_main.c158
-rw-r--r--drivers/usb/mon/mon_text.c317
-rw-r--r--drivers/usb/mon/usb_mon.h10
-rw-r--r--drivers/usb/net/Kconfig20
-rw-r--r--drivers/usb/net/Makefile1
-rw-r--r--drivers/usb/net/asix.c24
-rw-r--r--drivers/usb/net/catc.c30
-rw-r--r--drivers/usb/net/cdc_subset.c21
-rw-r--r--drivers/usb/net/dm9601.c619
-rw-r--r--drivers/usb/net/gl620a.c2
-rw-r--r--drivers/usb/net/kaweth.c2
-rw-r--r--drivers/usb/net/net1080.c2
-rw-r--r--drivers/usb/net/pegasus.c14
-rw-r--r--drivers/usb/net/rndis_host.c114
-rw-r--r--drivers/usb/net/rtl8150.c1
-rw-r--r--drivers/usb/net/usbnet.c38
-rw-r--r--drivers/usb/net/usbnet.h1
-rw-r--r--drivers/usb/serial/Kconfig6
-rw-r--r--drivers/usb/serial/aircable.c7
-rw-r--r--drivers/usb/serial/airprime.c87
-rw-r--r--drivers/usb/serial/ark3116.c3
-rw-r--r--drivers/usb/serial/cp2101.c5
-rw-r--r--drivers/usb/serial/ftdi_sio.c120
-rw-r--r--drivers/usb/serial/ftdi_sio.h28
-rw-r--r--drivers/usb/serial/generic.c109
-rw-r--r--drivers/usb/serial/io_edgeport.c139
-rw-r--r--drivers/usb/serial/io_edgeport.h6
-rw-r--r--drivers/usb/serial/ipaq.c3
-rw-r--r--drivers/usb/serial/kl5kusb105.c28
-rw-r--r--drivers/usb/serial/mct_u232.c12
-rw-r--r--drivers/usb/serial/mos7720.c35
-rw-r--r--drivers/usb/serial/mos7840.c233
-rw-r--r--drivers/usb/serial/omninet.c40
-rw-r--r--drivers/usb/serial/option.c157
-rw-r--r--drivers/usb/serial/pl2303.c1
-rw-r--r--drivers/usb/serial/pl2303.h5
-rw-r--r--drivers/usb/serial/sierra.c25
-rw-r--r--drivers/usb/serial/usb-serial.c11
-rw-r--r--drivers/usb/serial/visor.c22
-rw-r--r--drivers/usb/serial/whiteheat.c8
-rw-r--r--drivers/usb/serial/whiteheat.h4
-rw-r--r--drivers/usb/storage/libusual.c3
-rw-r--r--drivers/usb/storage/scsiglue.c6
-rw-r--r--drivers/usb/storage/unusual_devs.h49
-rw-r--r--drivers/usb/storage/usb.c4
-rw-r--r--drivers/usb/usb-skeleton.c51
127 files changed, 8142 insertions, 11382 deletions
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 825bf884537..f5de58a63f2 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -15,7 +15,6 @@ obj-$(CONFIG_USB_OHCI_HCD) += host/
obj-$(CONFIG_USB_UHCI_HCD) += host/
obj-$(CONFIG_USB_SL811_HCD) += host/
obj-$(CONFIG_USB_U132_HCD) += host/
-obj-$(CONFIG_ETRAX_USB_HOST) += host/
obj-$(CONFIG_USB_OHCI_AT91) += host/
obj-$(CONFIG_USB_ACM) += class/
@@ -27,10 +26,7 @@ obj-$(CONFIG_USB) += storage/
obj-$(CONFIG_USB_ACECAD) += input/
obj-$(CONFIG_USB_AIPTEK) += input/
obj-$(CONFIG_USB_ATI_REMOTE) += input/
-obj-$(CONFIG_USB_HID) += input/
-obj-$(CONFIG_USB_KBD) += input/
obj-$(CONFIG_USB_KBTAB) += input/
-obj-$(CONFIG_USB_MOUSE) += input/
obj-$(CONFIG_USB_MTOUCH) += input/
obj-$(CONFIG_USB_POWERMATE) += input/
obj-$(CONFIG_USB_WACOM) += input/
@@ -51,6 +47,7 @@ obj-$(CONFIG_USB_SERIAL) += serial/
obj-$(CONFIG_USB_ADUTUX) += misc/
obj-$(CONFIG_USB_APPLEDISPLAY) += misc/
obj-$(CONFIG_USB_AUERSWALD) += misc/
+obj-$(CONFIG_USB_BERRY_CHARGE) += misc/
obj-$(CONFIG_USB_CYPRESS_CY7C63)+= misc/
obj-$(CONFIG_USB_CYTHERM) += misc/
obj-$(CONFIG_USB_EMI26) += misc/
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 3dfa3e40e14..30b7bfbc985 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -4,6 +4,7 @@
*
* Copyright (C) 2004 David Woodhouse, Duncan Sands, Roman Kagan
* Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru)
+ * Copyright (C) 2007 Simon Arlott
*
* 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
@@ -34,14 +35,14 @@
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/init.h>
-#include <linux/device.h> /* FIXME: linux/firmware.h should include it itself */
+#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/mutex.h>
#include "usbatm.h"
-#define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands"
-#define DRIVER_VERSION "0.2"
+#define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands, Simon Arlott"
+#define DRIVER_VERSION "0.3"
#define DRIVER_DESC "Conexant AccessRunner ADSL USB modem driver"
static const char cxacru_driver_name[] = "cxacru";
@@ -64,7 +65,7 @@ static const char cxacru_driver_name[] = "cxacru";
#define SDRAM_ENA 0x1
#define CMD_TIMEOUT 2000 /* msecs */
-#define POLL_INTERVAL 5000 /* msecs */
+#define POLL_INTERVAL 1 /* secs */
/* commands for interaction with the modem through the control channel before
* firmware is loaded */
@@ -146,6 +147,13 @@ enum cxacru_info_idx {
CXINF_MAX = 0x1c,
};
+enum cxacru_poll_state {
+ CXPOLL_STOPPING,
+ CXPOLL_STOPPED,
+ CXPOLL_POLLING,
+ CXPOLL_SHUTDOWN
+};
+
struct cxacru_modem_type {
u32 pll_f_clk;
u32 pll_b_clk;
@@ -158,7 +166,12 @@ struct cxacru_data {
const struct cxacru_modem_type *modem_type;
int line_status;
+ struct mutex adsl_state_serialize;
+ int adsl_status;
struct delayed_work poll_work;
+ u32 card_info[CXINF_MAX];
+ struct mutex poll_state_serialize;
+ int poll_state;
/* contol handles */
struct mutex cm_serialize;
@@ -170,6 +183,275 @@ struct cxacru_data {
struct completion snd_done;
};
+static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
+ u8 *wdata, int wsize, u8 *rdata, int rsize);
+static void cxacru_poll_status(struct work_struct *work);
+
+/* Card info exported through sysfs */
+#define CXACRU__ATTR_INIT(_name) \
+static DEVICE_ATTR(_name, S_IRUGO, cxacru_sysfs_show_##_name, NULL)
+
+#define CXACRU_CMD_INIT(_name) \
+static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, \
+ cxacru_sysfs_show_##_name, cxacru_sysfs_store_##_name)
+
+#define CXACRU_ATTR_INIT(_value, _type, _name) \
+static ssize_t cxacru_sysfs_show_##_name(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct usb_interface *intf = to_usb_interface(dev); \
+ struct usbatm_data *usbatm_instance = usb_get_intfdata(intf); \
+ struct cxacru_data *instance = usbatm_instance->driver_data; \
+ return cxacru_sysfs_showattr_##_type(instance->card_info[_value], buf); \
+} \
+CXACRU__ATTR_INIT(_name)
+
+#define CXACRU_ATTR_CREATE(_v, _t, _name) CXACRU_DEVICE_CREATE_FILE(_name)
+#define CXACRU_CMD_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name)
+#define CXACRU__ATTR_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name)
+
+#define CXACRU_ATTR_REMOVE(_v, _t, _name) CXACRU_DEVICE_REMOVE_FILE(_name)
+#define CXACRU_CMD_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name)
+#define CXACRU__ATTR_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name)
+
+static ssize_t cxacru_sysfs_showattr_u32(u32 value, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", value);
+}
+
+static ssize_t cxacru_sysfs_showattr_s8(s8 value, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", value);
+}
+
+static ssize_t cxacru_sysfs_showattr_dB(s16 value, char *buf)
+{
+ if (unlikely(value < 0)) {
+ return snprintf(buf, PAGE_SIZE, "%d.%02u\n",
+ value / 100, -value % 100);
+ } else {
+ return snprintf(buf, PAGE_SIZE, "%d.%02u\n",
+ value / 100, value % 100);
+ }
+}
+
+static ssize_t cxacru_sysfs_showattr_bool(u32 value, char *buf)
+{
+ switch (value) {
+ case 0: return snprintf(buf, PAGE_SIZE, "no\n");
+ case 1: return snprintf(buf, PAGE_SIZE, "yes\n");
+ default: return 0;
+ }
+}
+
+static ssize_t cxacru_sysfs_showattr_LINK(u32 value, char *buf)
+{
+ switch (value) {
+ case 1: return snprintf(buf, PAGE_SIZE, "not connected\n");
+ case 2: return snprintf(buf, PAGE_SIZE, "connected\n");
+ case 3: return snprintf(buf, PAGE_SIZE, "lost\n");
+ default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
+ }
+}
+
+static ssize_t cxacru_sysfs_showattr_LINE(u32 value, char *buf)
+{
+ switch (value) {
+ case 0: return snprintf(buf, PAGE_SIZE, "down\n");
+ case 1: return snprintf(buf, PAGE_SIZE, "attempting to activate\n");
+ case 2: return snprintf(buf, PAGE_SIZE, "training\n");
+ case 3: return snprintf(buf, PAGE_SIZE, "channel analysis\n");
+ case 4: return snprintf(buf, PAGE_SIZE, "exchange\n");
+ case 5: return snprintf(buf, PAGE_SIZE, "up\n");
+ case 6: return snprintf(buf, PAGE_SIZE, "waiting\n");
+ case 7: return snprintf(buf, PAGE_SIZE, "initialising\n");
+ default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
+ }
+}
+
+static ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf)
+{
+ switch (value) {
+ case 0: return 0;
+ case 1: return snprintf(buf, PAGE_SIZE, "ANSI T1.413\n");
+ case 2: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.1 (G.DMT)\n");
+ case 3: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.2 (G.LITE)\n");
+ default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
+ }
+}
+
+/*
+ * This could use MAC_ADDRESS_HIGH and MAC_ADDRESS_LOW, but since
+ * this data is already in atm_dev there's no point.
+ *
+ * MAC_ADDRESS_HIGH = 0x????5544
+ * MAC_ADDRESS_LOW = 0x33221100
+ * Where 00-55 are bytes 0-5 of the MAC.
+ */
+static ssize_t cxacru_sysfs_show_mac_address(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
+ struct atm_dev *atm_dev = usbatm_instance->atm_dev;
+
+ return snprintf(buf, PAGE_SIZE, "%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]);
+}
+
+static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
+ struct cxacru_data *instance = usbatm_instance->driver_data;
+ u32 value = instance->card_info[CXINF_LINE_STARTABLE];
+
+ switch (value) {
+ case 0: return snprintf(buf, PAGE_SIZE, "running\n");
+ case 1: return snprintf(buf, PAGE_SIZE, "stopped\n");
+ default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
+ }
+}
+
+static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
+ struct cxacru_data *instance = usbatm_instance->driver_data;
+ int ret;
+ int poll = -1;
+ char str_cmd[8];
+ int len = strlen(buf);
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+
+ ret = sscanf(buf, "%7s", str_cmd);
+ if (ret != 1)
+ return -EINVAL;
+ ret = 0;
+
+ if (mutex_lock_interruptible(&instance->adsl_state_serialize))
+ return -ERESTARTSYS;
+
+ if (!strcmp(str_cmd, "stop") || !strcmp(str_cmd, "restart")) {
+ ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_STOP, NULL, 0, NULL, 0);
+ if (ret < 0) {
+ atm_err(usbatm_instance, "change adsl state:"
+ " CHIP_ADSL_LINE_STOP returned %d\n", ret);
+
+ ret = -EIO;
+ } else {
+ ret = len;
+ poll = CXPOLL_STOPPED;
+ }
+ }
+
+ /* Line status is only updated every second
+ * and the device appears to only react to
+ * START/STOP every second too. Wait 1.5s to
+ * be sure that restart will have an effect. */
+ if (!strcmp(str_cmd, "restart"))
+ msleep(1500);
+
+ if (!strcmp(str_cmd, "start") || !strcmp(str_cmd, "restart")) {
+ ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
+ if (ret < 0) {
+ atm_err(usbatm_instance, "change adsl state:"
+ " CHIP_ADSL_LINE_START returned %d\n", ret);
+
+ ret = -EIO;
+ } else {
+ ret = len;
+ poll = CXPOLL_POLLING;
+ }
+ }
+
+ if (!strcmp(str_cmd, "poll")) {
+ ret = len;
+ poll = CXPOLL_POLLING;
+ }
+
+ if (ret == 0) {
+ ret = -EINVAL;
+ poll = -1;
+ }
+
+ if (poll == CXPOLL_POLLING) {
+ mutex_lock(&instance->poll_state_serialize);
+ switch (instance->poll_state) {
+ case CXPOLL_STOPPED:
+ /* start polling */
+ instance->poll_state = CXPOLL_POLLING;
+ break;
+
+ case CXPOLL_STOPPING:
+ /* abort stop request */
+ instance->poll_state = CXPOLL_POLLING;
+ case CXPOLL_POLLING:
+ case CXPOLL_SHUTDOWN:
+ /* don't start polling */
+ poll = -1;
+ }
+ mutex_unlock(&instance->poll_state_serialize);
+ } else if (poll == CXPOLL_STOPPED) {
+ mutex_lock(&instance->poll_state_serialize);
+ /* request stop */
+ if (instance->poll_state == CXPOLL_POLLING)
+ instance->poll_state = CXPOLL_STOPPING;
+ mutex_unlock(&instance->poll_state_serialize);
+ }
+
+ mutex_unlock(&instance->adsl_state_serialize);
+
+ if (poll == CXPOLL_POLLING)
+ cxacru_poll_status(&instance->poll_work.work);
+
+ return ret;
+}
+
+/*
+ * All device attributes are included in CXACRU_ALL_FILES
+ * so that the same list can be used multiple times:
+ * INIT (define the device attributes)
+ * CREATE (create all the device files)
+ * REMOVE (remove all the device files)
+ *
+ * With the last two being defined as needed in the functions
+ * they are used in before calling CXACRU_ALL_FILES()
+ */
+#define CXACRU_ALL_FILES(_action) \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_RATE, u32, downstream_rate); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_RATE, u32, upstream_rate); \
+CXACRU_ATTR_##_action(CXINF_LINK_STATUS, LINK, link_status); \
+CXACRU_ATTR_##_action(CXINF_LINE_STATUS, LINE, line_status); \
+CXACRU__ATTR_##_action( mac_address); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_SNR_MARGIN, dB, upstream_snr_margin); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_SNR_MARGIN, dB, downstream_snr_margin); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_ATTENUATION, dB, upstream_attenuation); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_ATTENUATION, dB, downstream_attenuation); \
+CXACRU_ATTR_##_action(CXINF_TRANSMITTER_POWER, s8, transmitter_power); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_BITS_PER_FRAME, u32, upstream_bits_per_frame); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_BITS_PER_FRAME, u32, downstream_bits_per_frame); \
+CXACRU_ATTR_##_action(CXINF_STARTUP_ATTEMPTS, u32, startup_attempts); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_CRC_ERRORS, u32, upstream_crc_errors); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_CRC_ERRORS, u32, downstream_crc_errors); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_FEC_ERRORS, u32, upstream_fec_errors); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_FEC_ERRORS, u32, downstream_fec_errors); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_HEC_ERRORS, u32, upstream_hec_errors); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_HEC_ERRORS, u32, downstream_hec_errors); \
+CXACRU_ATTR_##_action(CXINF_LINE_STARTABLE, bool, line_startable); \
+CXACRU_ATTR_##_action(CXINF_MODULATION, MODU, modulation); \
+CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND, u32, adsl_headend); \
+CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND_ENVIRONMENT, u32, adsl_headend_environment); \
+CXACRU_ATTR_##_action(CXINF_CONTROLLER_VERSION, u32, adsl_controller_version); \
+CXACRU_CMD_##_action( adsl_state);
+
+CXACRU_ALL_FILES(INIT);
+
/* the following three functions are stolen from drivers/usb/core/message.c */
static void cxacru_blocking_completion(struct urb *urb)
{
@@ -347,8 +629,6 @@ static int cxacru_card_status(struct cxacru_data *instance)
return 0;
}
-static void cxacru_poll_status(struct work_struct *work);
-
static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
struct atm_dev *atm_dev)
{
@@ -357,6 +637,7 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
struct atm_dev *atm_dev = usbatm_instance->atm_dev;
*/
int ret;
+ int start_polling = 1;
dbg("cxacru_atm_start");
@@ -369,14 +650,35 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
}
/* start ADSL */
+ mutex_lock(&instance->adsl_state_serialize);
ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
if (ret < 0) {
atm_err(usbatm_instance, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret);
+ mutex_unlock(&instance->adsl_state_serialize);
return ret;
}
/* Start status polling */
- cxacru_poll_status(&instance->poll_work.work);
+ mutex_lock(&instance->poll_state_serialize);
+ switch (instance->poll_state) {
+ case CXPOLL_STOPPED:
+ /* start polling */
+ instance->poll_state = CXPOLL_POLLING;
+ break;
+
+ case CXPOLL_STOPPING:
+ /* abort stop request */
+ instance->poll_state = CXPOLL_POLLING;
+ case CXPOLL_POLLING:
+ case CXPOLL_SHUTDOWN:
+ /* don't start polling */
+ start_polling = 0;
+ }
+ mutex_unlock(&instance->poll_state_serialize);
+ mutex_unlock(&instance->adsl_state_serialize);
+
+ if (start_polling)
+ cxacru_poll_status(&instance->poll_work.work);
return 0;
}
@@ -387,14 +689,46 @@ static void cxacru_poll_status(struct work_struct *work)
u32 buf[CXINF_MAX] = {};
struct usbatm_data *usbatm = instance->usbatm;
struct atm_dev *atm_dev = usbatm->atm_dev;
+ int keep_polling = 1;
int ret;
ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX);
if (ret < 0) {
- atm_warn(usbatm, "poll status: error %d\n", ret);
+ if (ret != -ESHUTDOWN)
+ atm_warn(usbatm, "poll status: error %d\n", ret);
+
+ mutex_lock(&instance->poll_state_serialize);
+ if (instance->poll_state != CXPOLL_SHUTDOWN) {
+ instance->poll_state = CXPOLL_STOPPED;
+
+ if (ret != -ESHUTDOWN)
+ atm_warn(usbatm, "polling disabled, set adsl_state"
+ " to 'start' or 'poll' to resume\n");
+ }
+ mutex_unlock(&instance->poll_state_serialize);
goto reschedule;
}
+ memcpy(instance->card_info, buf, sizeof(instance->card_info));
+
+ if (instance->adsl_status != buf[CXINF_LINE_STARTABLE]) {
+ instance->adsl_status = buf[CXINF_LINE_STARTABLE];
+
+ switch (instance->adsl_status) {
+ case 0:
+ atm_printk(KERN_INFO, usbatm, "ADSL state: running\n");
+ break;
+
+ case 1:
+ atm_printk(KERN_INFO, usbatm, "ADSL state: stopped\n");
+ break;
+
+ default:
+ atm_printk(KERN_INFO, usbatm, "Unknown adsl status %02x\n", instance->adsl_status);
+ break;
+ }
+ }
+
if (instance->line_status == buf[CXINF_LINE_STATUS])
goto reschedule;
@@ -449,7 +783,20 @@ static void cxacru_poll_status(struct work_struct *work)
break;
}
reschedule:
- schedule_delayed_work(&instance->poll_work, msecs_to_jiffies(POLL_INTERVAL));
+
+ mutex_lock(&instance->poll_state_serialize);
+ if (instance->poll_state == CXPOLL_STOPPING &&
+ instance->adsl_status == 1 && /* stopped */
+ instance->line_status == 0) /* down */
+ instance->poll_state = CXPOLL_STOPPED;
+
+ if (instance->poll_state == CXPOLL_STOPPED)
+ keep_polling = 0;
+ mutex_unlock(&instance->poll_state_serialize);
+
+ if (keep_polling)
+ schedule_delayed_work(&instance->poll_work,
+ round_jiffies_relative(POLL_INTERVAL*HZ));
}
static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,
@@ -684,6 +1031,14 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
instance->usbatm = usbatm_instance;
instance->modem_type = (struct cxacru_modem_type *) id->driver_info;
+ memset(instance->card_info, 0, sizeof(instance->card_info));
+
+ mutex_init(&instance->poll_state_serialize);
+ instance->poll_state = CXPOLL_STOPPED;
+ instance->line_status = -1;
+ instance->adsl_status = -1;
+
+ mutex_init(&instance->adsl_state_serialize);
instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL);
if (!instance->rcv_buf) {
@@ -710,6 +1065,13 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
goto fail;
}
+ #define CXACRU_DEVICE_CREATE_FILE(_name) \
+ ret = device_create_file(&intf->dev, &dev_attr_##_name); \
+ if (unlikely(ret)) \
+ goto fail_sysfs;
+ CXACRU_ALL_FILES(CREATE);
+ #undef CXACRU_DEVICE_CREATE_FILE
+
usb_fill_int_urb(instance->rcv_urb,
usb_dev, usb_rcvintpipe(usb_dev, CXACRU_EP_CMD),
instance->rcv_buf, PAGE_SIZE,
@@ -730,6 +1092,14 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
return 0;
+ fail_sysfs:
+ dbg("cxacru_bind: device_create_file failed (%d)\n", ret);
+
+ #define CXACRU_DEVICE_REMOVE_FILE(_name) \
+ device_remove_file(&intf->dev, &dev_attr_##_name);
+ CXACRU_ALL_FILES(REMOVE);
+ #undef CXACRU_DEVICE_REVOVE_FILE
+
fail:
free_page((unsigned long) instance->snd_buf);
free_page((unsigned long) instance->rcv_buf);
@@ -744,6 +1114,7 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance,
struct usb_interface *intf)
{
struct cxacru_data *instance = usbatm_instance->driver_data;
+ int is_polling = 1;
dbg("cxacru_unbind entered");
@@ -752,8 +1123,20 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance,
return;
}
- while (!cancel_delayed_work(&instance->poll_work))
- flush_scheduled_work();
+ mutex_lock(&instance->poll_state_serialize);
+ BUG_ON(instance->poll_state == CXPOLL_SHUTDOWN);
+
+ /* ensure that status polling continues unless
+ * it has already stopped */
+ if (instance->poll_state == CXPOLL_STOPPED)
+ is_polling = 0;
+
+ /* stop polling from being stopped or started */
+ instance->poll_state = CXPOLL_SHUTDOWN;
+ mutex_unlock(&instance->poll_state_serialize);
+
+ if (is_polling)
+ cancel_rearming_delayed_work(&instance->poll_work);
usb_kill_urb(instance->snd_urb);
usb_kill_urb(instance->rcv_urb);
@@ -762,6 +1145,12 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance,
free_page((unsigned long) instance->snd_buf);
free_page((unsigned long) instance->rcv_buf);
+
+ #define CXACRU_DEVICE_REMOVE_FILE(_name) \
+ device_remove_file(&intf->dev, &dev_attr_##_name);
+ CXACRU_ALL_FILES(REMOVE);
+ #undef CXACRU_DEVICE_REVOVE_FILE
+
kfree(instance);
usbatm_instance->driver_data = NULL;
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index dae4ef1e8fe..4973e147bc7 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -61,6 +61,7 @@
#include <linux/usb.h>
#include <linux/firmware.h>
#include <linux/ctype.h>
+#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/version.h>
#include <linux/mutex.h>
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
index ec63b0ee074..b3f779f5933 100644
--- a/drivers/usb/atm/usbatm.c
+++ b/drivers/usb/atm/usbatm.c
@@ -274,6 +274,9 @@ static void usbatm_complete(struct urb *urb)
(!(channel->usbatm->flags & UDSL_IGNORE_EILSEQ) ||
urb->status != -EILSEQ ))
{
+ if (urb->status == -ESHUTDOWN)
+ return;
+
if (printk_ratelimit())
atm_warn(channel->usbatm, "%s: urb 0x%p failed (%d)!\n",
__func__, urb, urb->status);
@@ -343,7 +346,7 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char
UDSL_ASSERT(sarb->tail + ATM_CELL_PAYLOAD <= sarb->end);
}
- memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
+ memcpy(skb_tail_pointer(sarb), source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
__skb_put(sarb, ATM_CELL_PAYLOAD);
if (pti & 1) {
@@ -370,7 +373,7 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char
goto out;
}
- if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) {
+ if (crc32_be(~0, skb_tail_pointer(sarb) - pdu_length, pdu_length) != 0xc704dd7b) {
atm_rldbg(instance, "%s: packet failed crc check (vcc: 0x%p)!\n",
__func__, vcc);
atomic_inc(&vcc->stats->rx_err);
@@ -396,7 +399,9 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char
goto out; /* atm_charge increments rx_drop */
}
- memcpy(skb->data, sarb->tail - pdu_length, length);
+ skb_copy_to_linear_data(skb,
+ skb_tail_pointer(sarb) - pdu_length,
+ length);
__skb_put(skb, length);
vdbg("%s: sending skb 0x%p, skb->len %u, skb->truesize %u",
@@ -484,7 +489,7 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,
ptr[4] = 0xec;
ptr += ATM_CELL_HEADER;
- memcpy(ptr, skb->data, data_len);
+ skb_copy_from_linear_data(skb, ptr, data_len);
ptr += data_len;
__skb_pull(skb, data_len);
@@ -966,6 +971,14 @@ static int usbatm_atm_init(struct usbatm_data *instance)
/* temp init ATM device, set to 128kbit */
atm_dev->link_rate = 128 * 1000 / 424;
+ ret = sysfs_create_link(&atm_dev->class_dev.kobj,
+ &instance->usb_intf->dev.kobj, "device");
+ if (ret) {
+ atm_err(instance, "%s: sysfs_create_link failed: %d\n",
+ __func__, ret);
+ goto fail_sysfs;
+ }
+
if (instance->driver->atm_start && ((ret = instance->driver->atm_start(instance, atm_dev)) < 0)) {
atm_err(instance, "%s: atm_start failed: %d!\n", __func__, ret);
goto fail;
@@ -984,6 +997,8 @@ static int usbatm_atm_init(struct usbatm_data *instance)
return 0;
fail:
+ sysfs_remove_link(&atm_dev->class_dev.kobj, "device");
+ fail_sysfs:
instance->atm_dev = NULL;
atm_dev_deregister(atm_dev); /* usbatm_atm_dev_close will eventually be called */
return ret;
@@ -1316,8 +1331,10 @@ void usbatm_usb_disconnect(struct usb_interface *intf)
kfree(instance->cell_buf);
/* ATM finalize */
- if (instance->atm_dev)
+ if (instance->atm_dev) {
+ sysfs_remove_link(&instance->atm_dev->class_dev.kobj, "device");
atm_dev_deregister(instance->atm_dev);
+ }
usbatm_put_instance(instance); /* taken in usbatm_usb_probe */
}
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 98199628e39..14de3b1b6a2 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -212,7 +212,41 @@ static int acm_write_start(struct acm *acm)
}
return rc;
}
+/*
+ * attributes exported through sysfs
+ */
+static ssize_t show_caps
+(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct acm *acm = usb_get_intfdata(intf);
+
+ return sprintf(buf, "%d", acm->ctrl_caps);
+}
+static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
+
+static ssize_t show_country_codes
+(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct acm *acm = usb_get_intfdata(intf);
+
+ memcpy(buf, acm->country_codes, acm->country_code_size);
+ return acm->country_code_size;
+}
+static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
+
+static ssize_t show_country_rel_date
+(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct acm *acm = usb_get_intfdata(intf);
+
+ return sprintf(buf, "%d", acm->country_rel_date);
+}
+
+static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
/*
* Interrupt handlers for various ACM device responses
*/
@@ -326,10 +360,16 @@ static void acm_rx_tasklet(unsigned long _acm)
struct tty_struct *tty = acm->tty;
struct acm_ru *rcv;
unsigned long flags;
- int i = 0;
+ unsigned char throttled;
dbg("Entering acm_rx_tasklet");
- if (!ACM_READY(acm) || acm->throttle)
+ if (!ACM_READY(acm))
+ return;
+
+ spin_lock_irqsave(&acm->throttle_lock, flags);
+ throttled = acm->throttle;
+ spin_unlock_irqrestore(&acm->throttle_lock, flags);
+ if (throttled)
return;
next_buffer:
@@ -346,22 +386,20 @@ next_buffer:
dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
tty_buffer_request_room(tty, buf->size);
- if (!acm->throttle)
+ spin_lock_irqsave(&acm->throttle_lock, flags);
+ throttled = acm->throttle;
+ spin_unlock_irqrestore(&acm->throttle_lock, flags);
+ if (!throttled)
tty_insert_flip_string(tty, buf->base, buf->size);
tty_flip_buffer_push(tty);
- spin_lock(&acm->throttle_lock);
- if (acm->throttle) {
- dbg("Throtteling noticed");
- memmove(buf->base, buf->base + i, buf->size - i);
- buf->size -= i;
- spin_unlock(&acm->throttle_lock);
+ if (throttled) {
+ dbg("Throttling noticed");
spin_lock_irqsave(&acm->read_lock, flags);
list_add(&buf->list, &acm->filled_read_bufs);
spin_unlock_irqrestore(&acm->read_lock, flags);
return;
}
- spin_unlock(&acm->throttle_lock);
spin_lock_irqsave(&acm->read_lock, flags);
list_add(&buf->list, &acm->spare_read_bufs);
@@ -467,7 +505,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
goto bail_out;
}
- if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS))
+ if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
+ (acm->ctrl_caps & USB_CDC_CAP_LINE))
goto full_bailout;
INIT_LIST_HEAD(&acm->spare_read_urbs);
@@ -480,6 +519,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
}
+ acm->throttle = 0;
+
tasklet_schedule(&acm->urb_task);
done:
@@ -507,6 +548,7 @@ static void acm_tty_unregister(struct acm *acm)
usb_free_urb(acm->writeurb);
for (i = 0; i < nr; i++)
usb_free_urb(acm->ru[i].urb);
+ kfree(acm->country_codes);
kfree(acm);
}
@@ -754,6 +796,7 @@ static int acm_probe (struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_cdc_union_desc *union_header = NULL;
+ struct usb_cdc_country_functional_desc *cfd = NULL;
char *buffer = intf->altsetting->extra;
int buflen = intf->altsetting->extralen;
struct usb_interface *control_interface;
@@ -817,8 +860,9 @@ static int acm_probe (struct usb_interface *intf,
union_header = (struct usb_cdc_union_desc *)
buffer;
break;
- case USB_CDC_COUNTRY_TYPE: /* maybe somehow export */
- break; /* for now we ignore it */
+ case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
+ cfd = (struct usb_cdc_country_functional_desc *)buffer;
+ break;
case USB_CDC_HEADER_TYPE: /* maybe check version */
break; /* for now we ignore it */
case USB_CDC_ACM_TYPE:
@@ -976,6 +1020,34 @@ skip_normal_probe:
goto alloc_fail7;
}
+ usb_set_intfdata (intf, acm);
+
+ i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
+ if (i < 0)
+ goto alloc_fail8;
+
+ if (cfd) { /* export the country data */
+ acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
+ if (!acm->country_codes)
+ goto skip_countries;
+ acm->country_code_size = cfd->bLength - 4;
+ memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4);
+ acm->country_rel_date = cfd->iCountryCodeRelDate;
+
+ i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
+ if (i < 0) {
+ kfree(acm->country_codes);
+ goto skip_countries;
+ }
+
+ i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
+ if (i < 0) {
+ kfree(acm->country_codes);
+ goto skip_countries;
+ }
+ }
+
+skip_countries:
usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
@@ -999,9 +1071,10 @@ skip_normal_probe:
tty_register_device(acm_tty_driver, minor, &control_interface->dev);
acm_table[minor] = acm;
- usb_set_intfdata (intf, acm);
- return 0;
+ return 0;
+alloc_fail8:
+ usb_free_urb(acm->writeurb);
alloc_fail7:
for (i = 0; i < num_rx_buf; i++)
usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
@@ -1020,7 +1093,7 @@ alloc_fail:
static void acm_disconnect(struct usb_interface *intf)
{
- struct acm *acm = usb_get_intfdata (intf);
+ struct acm *acm = usb_get_intfdata(intf);
struct usb_device *usb_dev = interface_to_usbdev(intf);
int i;
@@ -1034,6 +1107,11 @@ static void acm_disconnect(struct usb_interface *intf)
mutex_unlock(&open_mutex);
return;
}
+ if (acm->country_codes){
+ device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
+ device_remove_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
+ }
+ device_remove_file(&intf->dev, &dev_attr_bmCapabilities);
acm->dev = NULL;
usb_set_intfdata(acm->control, NULL);
usb_set_intfdata(acm->data, NULL);
@@ -1092,6 +1170,10 @@ static struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
.driver_info = SINGLE_RX_URB, /* firmware bug */
},
+ { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
+ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+ },
+
/* control interfaces with various AT-command sets */
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_ACM_PROTO_AT_V25TER) },
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 1bcaea32cfc..09f7765dbf8 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -91,6 +91,9 @@ struct acm {
struct urb *ctrlurb, *writeurb; /* urbs */
u8 *ctrl_buffer; /* buffers of urbs */
dma_addr_t ctrl_dma; /* dma handles of buffers */
+ u8 *country_codes; /* country codes from device */
+ unsigned int country_code_size; /* size of this buffer */
+ unsigned int country_rel_date; /* release date of version */
struct acm_wb wb[ACM_NW];
struct acm_ru ru[ACM_NR];
struct acm_rb rb[ACM_NR];
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index 63e50a1f139..6584cf00f7f 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -202,6 +202,7 @@ struct quirk_printer_struct {
#define USBLP_QUIRK_BIDIR 0x1 /* reports bidir but requires unidirectional mode (no INs/reads) */
#define USBLP_QUIRK_USB_INIT 0x2 /* needs vendor USB init string */
+#define USBLP_QUIRK_BAD_CLASS 0x4 /* descriptor uses vendor-specific Class or SubClass */
static const struct quirk_printer_struct quirk_printers[] = {
{ 0x03f0, 0x0004, USBLP_QUIRK_BIDIR }, /* HP DeskJet 895C */
@@ -218,6 +219,7 @@ static const struct quirk_printer_struct quirk_printers[] = {
{ 0x0409, 0xf0be, USBLP_QUIRK_BIDIR }, /* NEC Picty920 (HP OEM) */
{ 0x0409, 0xf1be, USBLP_QUIRK_BIDIR }, /* NEC Picty800 (HP OEM) */
{ 0x0482, 0x0010, USBLP_QUIRK_BIDIR }, /* Kyocera Mita FS 820, by zut <kernel@zut.de> */
+ { 0x04b8, 0x0202, USBLP_QUIRK_BAD_CLASS }, /* Seiko Epson Receipt Printer M129C */
{ 0, 0 }
};
@@ -1048,7 +1050,8 @@ static int usblp_select_alts(struct usblp *usblp)
ifd = &if_alt->altsetting[i];
if (ifd->desc.bInterfaceClass != 7 || ifd->desc.bInterfaceSubClass != 1)
- continue;
+ if (!(usblp->quirks & USBLP_QUIRK_BAD_CLASS))
+ continue;
if (ifd->desc.bInterfaceProtocol < USBLP_FIRST_PROTOCOL ||
ifd->desc.bInterfaceProtocol > USBLP_LAST_PROTOCOL)
@@ -1232,6 +1235,7 @@ static struct usb_device_id usblp_ids [] = {
{ USB_INTERFACE_INFO(7, 1, 1) },
{ USB_INTERFACE_INFO(7, 1, 2) },
{ USB_INTERFACE_INFO(7, 1, 3) },
+ { USB_DEVICE(0x04b8, 0x0202) }, /* Seiko Epson Receipt Printer M129C */
{ } /* Terminating entry */
};
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index 2fc0f88a3d8..f493fb1eaa2 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -31,7 +31,30 @@ config USB_DEVICEFS
For the format of the various /proc/bus/usb/ files, please read
<file:Documentation/usb/proc_usb_info.txt>.
- Most users want to say Y here.
+ Usbfs files can't handle Access Control Lists (ACL), which are the
+ default way to grant access to USB devices for untrusted users of a
+ desktop system. The usbfs functionality is replaced by real
+ device-nodes managed by udev. These nodes live in /dev/bus/usb and
+ are used by libusb.
+
+config USB_DEVICE_CLASS
+ bool "USB device class-devices (DEPRECATED)"
+ depends on USB
+ default n
+ ---help---
+ Userspace access to USB devices is granted by device-nodes exported
+ directly from the usbdev in sysfs. Old versions of the driver
+ core and udev needed additional class devices to export device nodes.
+
+ These additional devices are difficult to handle in userspace, if
+ information about USB interfaces must be available. One device contains
+ the device node, the other device contains the interface data. Both
+ devices are at the same level in sysfs (siblings) and one can't access
+ the other. The device node created directly by the usbdev is the parent
+ device of the interface and therefore easily accessible from the interface
+ event.
+
+ This option provides backward compatibility if needed.
config USB_DYNAMIC_MINORS
bool "Dynamic USB minor allocation (EXPERIMENTAL)"
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
index 34e9bac319b..b6078706fb9 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -4,7 +4,7 @@
usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \
config.o file.o buffer.o sysfs.o endpoint.o \
- devio.o notify.o generic.o
+ devio.o notify.o generic.o quirks.o
ifeq ($(CONFIG_PCI),y)
usbcore-objs += hcd-pci.o
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index a47c30b2d76..6753ca059ee 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -246,7 +246,6 @@ static char *usb_dump_interface_descriptor(char *start, char *end,
if (start > end)
return start;
- down_read(&usb_bus_type.subsys.rwsem);
if (iface) {
driver_name = (iface->dev.driver
? iface->dev.driver->name
@@ -263,7 +262,6 @@ static char *usb_dump_interface_descriptor(char *start, char *end,
desc->bInterfaceSubClass,
desc->bInterfaceProtocol,
driver_name);
- up_read(&usb_bus_type.subsys.rwsem);
return start;
}
@@ -604,10 +602,6 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct
lock_kernel();
if (!st) {
st = kmalloc(sizeof(struct usb_device_status), GFP_KERNEL);
- if (!st) {
- unlock_kernel();
- return POLLIN;
- }
/* we may have dropped BKL - need to check for having lost the race */
if (file->private_data) {
@@ -615,6 +609,11 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct
st = file->private_data;
goto lost_race;
}
+ /* we haven't lost - check for allocation failure now */
+ if (!st) {
+ unlock_kernel();
+ return POLLIN;
+ }
/*
* need to prevent the module from being unloaded, since
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 2087766f9e8..927a181120a 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -57,7 +57,6 @@
#define USB_MAXBUS 64
#define USB_DEVICE_MAX USB_MAXBUS * 128
-static struct class *usb_device_class;
/* Mutual exclusion for removal, open, and release */
DEFINE_MUTEX(usbfs_mutex);
@@ -421,14 +420,11 @@ static int claimintf(struct dev_state *ps, unsigned int ifnum)
if (test_bit(ifnum, &ps->ifclaimed))
return 0;
- /* lock against other changes to driver bindings */
- down_write(&usb_bus_type.subsys.rwsem);
intf = usb_ifnum_to_if(dev, ifnum);
if (!intf)
err = -ENOENT;
else
err = usb_driver_claim_interface(&usbfs_driver, intf, ps);
- up_write(&usb_bus_type.subsys.rwsem);
if (err == 0)
set_bit(ifnum, &ps->ifclaimed);
return err;
@@ -444,8 +440,6 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum)
if (ifnum >= 8*sizeof(ps->ifclaimed))
return err;
dev = ps->dev;
- /* lock against other changes to driver bindings */
- down_write(&usb_bus_type.subsys.rwsem);
intf = usb_ifnum_to_if(dev, ifnum);
if (!intf)
err = -ENOENT;
@@ -453,7 +447,6 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum)
usb_driver_release_interface(&usbfs_driver, intf);
err = 0;
}
- up_write(&usb_bus_type.subsys.rwsem);
return err;
}
@@ -520,22 +513,25 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig
return ret;
}
-static struct usb_device *usbdev_lookup_minor(int minor)
+static int __match_minor(struct device *dev, void *data)
{
- struct device *device;
- struct usb_device *udev = NULL;
+ int minor = *((int *)data);
- down(&usb_device_class->sem);
- list_for_each_entry(device, &usb_device_class->devices, node) {
- if (device->devt == MKDEV(USB_DEVICE_MAJOR, minor)) {
- udev = device->platform_data;
- break;
- }
- }
- up(&usb_device_class->sem);
+ if (dev->devt == MKDEV(USB_DEVICE_MAJOR, minor))
+ return 1;
+ return 0;
+}
- return udev;
-};
+static struct usb_device *usbdev_lookup_by_minor(int minor)
+{
+ struct device *dev;
+
+ dev = bus_find_device(&usb_bus_type, NULL, &minor, __match_minor);
+ if (!dev)
+ return NULL;
+ put_device(dev);
+ return container_of(dev, struct usb_device, dev);
+}
/*
* file operations
@@ -554,11 +550,14 @@ static int usbdev_open(struct inode *inode, struct file *file)
goto out;
ret = -ENOENT;
- /* check if we are called from a real node or usbfs */
+ /* usbdev device-node */
if (imajor(inode) == USB_DEVICE_MAJOR)
- dev = usbdev_lookup_minor(iminor(inode));
+ dev = usbdev_lookup_by_minor(iminor(inode));
+#ifdef CONFIG_USB_DEVICEFS
+ /* procfs file */
if (!dev)
dev = inode->i_private;
+#endif
if (!dev)
goto out;
ret = usb_autoresume_device(dev);
@@ -581,7 +580,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
ps->disccontext = NULL;
ps->ifclaimed = 0;
security_task_getsecid(current, &ps->secid);
- wmb();
+ smp_wmb();
list_add_tail(&ps->list, &dev->filelist);
file->private_data = ps;
out:
@@ -813,7 +812,6 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg)
if (copy_from_user(&gd, arg, sizeof(gd)))
return -EFAULT;
- down_read(&usb_bus_type.subsys.rwsem);
intf = usb_ifnum_to_if(ps->dev, gd.interface);
if (!intf || !intf->dev.driver)
ret = -ENODATA;
@@ -822,7 +820,6 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg)
sizeof(gd.driver));
ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0);
}
- up_read(&usb_bus_type.subsys.rwsem);
return ret;
}
@@ -857,11 +854,11 @@ static int proc_setintf(struct dev_state *ps, void __user *arg)
static int proc_setconfig(struct dev_state *ps, void __user *arg)
{
- unsigned int u;
+ int u;
int status = 0;
struct usb_host_config *actconfig;
- if (get_user(u, (unsigned int __user *)arg))
+ if (get_user(u, (int __user *)arg))
return -EFAULT;
actconfig = ps->dev->actconfig;
@@ -912,7 +909,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
struct async *as;
struct usb_ctrlrequest *dr = NULL;
unsigned int u, totlen, isofrmlen;
- int ret, interval = 0, ifnum = -1;
+ int ret, ifnum = -1;
if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_SHORT_NOT_OK|
URB_NO_FSBR|URB_ZERO_PACKET))
@@ -992,7 +989,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
!= USB_ENDPOINT_XFER_ISOC)
return -EINVAL;
- interval = 1 << min (15, ep->desc.bInterval - 1);
isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb->number_of_packets;
if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL)))
return -ENOMEM;
@@ -1021,10 +1017,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
!= USB_ENDPOINT_XFER_INT)
return -EINVAL;
- if (ps->dev->speed == USB_SPEED_HIGH)
- interval = 1 << min (15, ep->desc.bInterval - 1);
- else
- interval = ep->desc.bInterval;
if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
return -EINVAL;
if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length))
@@ -1053,7 +1045,11 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
as->urb->setup_packet = (unsigned char*)dr;
as->urb->start_frame = uurb->start_frame;
as->urb->number_of_packets = uurb->number_of_packets;
- as->urb->interval = interval;
+ if (uurb->type == USBDEVFS_URB_TYPE_ISO ||
+ ps->dev->speed == USB_SPEED_HIGH)
+ as->urb->interval = 1 << min(15, ep->desc.bInterval - 1);
+ else
+ as->urb->interval = ep->desc.bInterval;
as->urb->context = as;
as->urb->complete = async_completed;
for (totlen = u = 0; u < uurb->number_of_packets; u++) {
@@ -1352,15 +1348,12 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
/* disconnect kernel driver from interface */
case USBDEVFS_DISCONNECT:
-
- down_write(&usb_bus_type.subsys.rwsem);
if (intf->dev.driver) {
driver = to_usb_driver(intf->dev.driver);
dev_dbg (&intf->dev, "disconnect by usbfs\n");
usb_driver_release_interface(driver, intf);
} else
retval = -ENODATA;
- up_write(&usb_bus_type.subsys.rwsem);
break;
/* let kernel drivers try to (re)bind to the interface */
@@ -1372,7 +1365,6 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
/* talk directly to the interface's driver */
default:
- down_read(&usb_bus_type.subsys.rwsem);
if (intf->dev.driver)
driver = to_usb_driver(intf->dev.driver);
if (driver == NULL || driver->ioctl == NULL) {
@@ -1382,7 +1374,6 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
if (retval == -ENOIOCTLCMD)
retval = -ENOTTY;
}
- up_read(&usb_bus_type.subsys.rwsem);
}
/* cleanup and return */
@@ -1584,7 +1575,7 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai
return mask;
}
-const struct file_operations usbfs_device_file_operations = {
+const struct file_operations usbdev_file_operations = {
.llseek = usbdev_lseek,
.read = usbdev_read,
.poll = usbdev_poll,
@@ -1593,50 +1584,53 @@ const struct file_operations usbfs_device_file_operations = {
.release = usbdev_release,
};
-static int usbdev_add(struct usb_device *dev)
+#ifdef CONFIG_USB_DEVICE_CLASS
+static struct class *usb_classdev_class;
+
+static int usb_classdev_add(struct usb_device *dev)
{
int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1);
- dev->usbfs_dev = device_create(usb_device_class, &dev->dev,
+ dev->usb_classdev = device_create(usb_classdev_class, &dev->dev,
MKDEV(USB_DEVICE_MAJOR, minor),
"usbdev%d.%d", dev->bus->busnum, dev->devnum);
- if (IS_ERR(dev->usbfs_dev))
- return PTR_ERR(dev->usbfs_dev);
+ if (IS_ERR(dev->usb_classdev))
+ return PTR_ERR(dev->usb_classdev);
- dev->usbfs_dev->platform_data = dev;
return 0;
}
-static void usbdev_remove(struct usb_device *dev)
+static void usb_classdev_remove(struct usb_device *dev)
{
- device_unregister(dev->usbfs_dev);
+ device_unregister(dev->usb_classdev);
}
-static int usbdev_notify(struct notifier_block *self, unsigned long action,
- void *dev)
+static int usb_classdev_notify(struct notifier_block *self,
+ unsigned long action, void *dev)
{
switch (action) {
case USB_DEVICE_ADD:
- if (usbdev_add(dev))
+ if (usb_classdev_add(dev))
return NOTIFY_BAD;
break;
case USB_DEVICE_REMOVE:
- usbdev_remove(dev);
+ usb_classdev_remove(dev);
break;
}
return NOTIFY_OK;
}
static struct notifier_block usbdev_nb = {
- .notifier_call = usbdev_notify,
+ .notifier_call = usb_classdev_notify,
};
+#endif
static struct cdev usb_device_cdev = {
.kobj = {.name = "usb_device", },
.owner = THIS_MODULE,
};
-int __init usbdev_init(void)
+int __init usb_devio_init(void)
{
int retval;
@@ -1646,38 +1640,38 @@ int __init usbdev_init(void)
err("unable to register minors for usb_device");
goto out;
}
- cdev_init(&usb_device_cdev, &usbfs_device_file_operations);
+ cdev_init(&usb_device_cdev, &usbdev_file_operations);
retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
if (retval) {
err("unable to get usb_device major %d", USB_DEVICE_MAJOR);
goto error_cdev;
}
- usb_device_class = class_create(THIS_MODULE, "usb_device");
- if (IS_ERR(usb_device_class)) {
+#ifdef CONFIG_USB_DEVICE_CLASS
+ usb_classdev_class = class_create(THIS_MODULE, "usb_device");
+ if (IS_ERR(usb_classdev_class)) {
err("unable to register usb_device class");
- retval = PTR_ERR(usb_device_class);
- goto error_class;
+ retval = PTR_ERR(usb_classdev_class);
+ cdev_del(&usb_device_cdev);
+ usb_classdev_class = NULL;
+ goto out;
}
usb_register_notify(&usbdev_nb);
-
+#endif
out:
return retval;
-error_class:
- usb_device_class = NULL;
- cdev_del(&usb_device_cdev);
-
error_cdev:
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
goto out;
}
-void usbdev_cleanup(void)
+void usb_devio_cleanup(void)
{
+#ifdef CONFIG_USB_DEVICE_CLASS
usb_unregister_notify(&usbdev_nb);
- class_destroy(usb_device_class);
+ class_destroy(usb_classdev_class);
+#endif
cdev_del(&usb_device_cdev);
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
}
-
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 600d1bc8272..b9f7f90aef8 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -287,9 +287,9 @@ static int usb_unbind_interface(struct device *dev)
* way to bind to an interface is to return the private data from
* the driver's probe() method.
*
- * 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.
+ * Callers must own the device lock, so driver probe() entries don't need
+ * extra locking, but other call contexts may need to explicitly claim that
+ * lock.
*/
int usb_driver_claim_interface(struct usb_driver *driver,
struct usb_interface *iface, void* priv)
@@ -330,9 +330,9 @@ EXPORT_SYMBOL(usb_driver_claim_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 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.
+ * Callers must own the device lock, so driver disconnect() entries don't
+ * need extra locking, but other call contexts may need to explicitly claim
+ * that lock.
*/
void usb_driver_release_interface(struct usb_driver *driver,
struct usb_interface *iface)
@@ -366,19 +366,8 @@ void usb_driver_release_interface(struct usb_driver *driver,
EXPORT_SYMBOL(usb_driver_release_interface);
/* returns 0 if no match, 1 if match */
-int usb_match_one_id(struct usb_interface *interface,
- const struct usb_device_id *id)
+int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
{
- struct usb_host_interface *intf;
- struct usb_device *dev;
-
- /* proc_connectinfo in devio.c may call us with id == NULL. */
- if (id == NULL)
- return 0;
-
- intf = interface->cur_altsetting;
- dev = interface_to_usbdev(interface);
-
if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
return 0;
@@ -409,6 +398,26 @@ int usb_match_one_id(struct usb_interface *interface,
(id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
return 0;
+ return 1;
+}
+
+/* returns 0 if no match, 1 if match */
+int usb_match_one_id(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_host_interface *intf;
+ struct usb_device *dev;
+
+ /* proc_connectinfo in devio.c may call us with id == NULL. */
+ if (id == NULL)
+ return 0;
+
+ intf = interface->cur_altsetting;
+ dev = interface_to_usbdev(interface);
+
+ if (!usb_match_device(dev, id))
+ return 0;
+
/* The interface class, subclass, and protocol should never be
* checked for a match if the device class is Vendor Specific,
* unless the match record specifies the Vendor ID. */
@@ -565,23 +574,10 @@ static int usb_device_match(struct device *dev, struct device_driver *drv)
}
#ifdef CONFIG_HOTPLUG
-
-/*
- * This sends an uevent to userspace, typically helping to load driver
- * or other modules, configure the device, and more. Drivers can provide
- * a MODULE_DEVICE_TABLE to help with module loading subtasks.
- *
- * We're called either from khubd (the typical case) or from root hub
- * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
- * delays in event delivery. Use sysfs (and DEVPATH) to make sure the
- * device (and this configuration!) are still present.
- */
static int usb_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
{
- struct usb_interface *intf;
struct usb_device *usb_dev;
- struct usb_host_interface *alt;
int i = 0;
int length = 0;
@@ -591,13 +587,11 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp,
/* driver is often null here; dev_dbg() would oops */
pr_debug ("usb %s: uevent\n", dev->bus_id);
- if (is_usb_device(dev)) {
+ if (is_usb_device(dev))
usb_dev = to_usb_device(dev);
- alt = NULL;
- } else {
- intf = to_usb_interface(dev);
+ else {
+ struct usb_interface *intf = to_usb_interface(dev);
usb_dev = interface_to_usbdev(intf);
- alt = intf->cur_altsetting;
}
if (usb_dev->devnum < 0) {
@@ -612,9 +606,7 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp,
#ifdef CONFIG_USB_DEVICEFS
/* If this is available, userspace programs can directly read
* all the device descriptors we don't tell them about. Or
- * even act as usermode drivers.
- *
- * FIXME reduce hardwired intelligence here
+ * act as usermode drivers.
*/
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
@@ -641,44 +633,29 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp,
usb_dev->descriptor.bDeviceProtocol))
return -ENOMEM;
- if (!is_usb_device(dev)) {
-
- if (add_uevent_var(envp, num_envp, &i,
+ if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
- "INTERFACE=%d/%d/%d",
- alt->desc.bInterfaceClass,
- alt->desc.bInterfaceSubClass,
- alt->desc.bInterfaceProtocol))
- return -ENOMEM;
+ "BUSNUM=%03d",
+ usb_dev->bus->busnum))
+ return -ENOMEM;
- if (add_uevent_var(envp, num_envp, &i,
+ if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
- "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
- le16_to_cpu(usb_dev->descriptor.idVendor),
- le16_to_cpu(usb_dev->descriptor.idProduct),
- le16_to_cpu(usb_dev->descriptor.bcdDevice),
- usb_dev->descriptor.bDeviceClass,
- usb_dev->descriptor.bDeviceSubClass,
- usb_dev->descriptor.bDeviceProtocol,
- alt->desc.bInterfaceClass,
- alt->desc.bInterfaceSubClass,
- alt->desc.bInterfaceProtocol))
- return -ENOMEM;
- }
+ "DEVNUM=%03d",
+ usb_dev->devnum))
+ return -ENOMEM;
envp[i] = NULL;
-
return 0;
}
#else
static int usb_uevent(struct device *dev, char **envp,
- int num_envp, char *buffer, int buffer_size)
+ int num_envp, char *buffer, int buffer_size)
{
return -ENODEV;
}
-
#endif /* CONFIG_HOTPLUG */
/**
@@ -743,6 +720,7 @@ EXPORT_SYMBOL_GPL(usb_deregister_device_driver);
* usb_register_driver - register a USB interface driver
* @new_driver: USB operations for the interface driver
* @owner: module owner of this driver.
+ * @mod_name: module name string
*
* Registers a USB interface driver with the USB core. The list of
* unattached interfaces will be rescanned whenever a new driver is
@@ -862,8 +840,10 @@ static int usb_resume_device(struct usb_device *udev)
done:
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
- if (status == 0)
+ if (status == 0) {
+ udev->autoresume_disabled = 0;
udev->dev.power.power_state.event = PM_EVENT_ON;
+ }
return status;
}
@@ -952,13 +932,19 @@ static int autosuspend_check(struct usb_device *udev)
{
int i;
struct usb_interface *intf;
+ unsigned long suspend_time;
- /* For autosuspend, fail fast if anything is in use.
- * Also fail if any interfaces require remote wakeup but it
- * isn't available. */
+ /* For autosuspend, fail fast if anything is in use or autosuspend
+ * is disabled. Also fail if any interfaces require remote wakeup
+ * but it isn't available.
+ */
udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
if (udev->pm_usage_cnt > 0)
return -EBUSY;
+ if (udev->autosuspend_delay < 0 || udev->autosuspend_disabled)
+ return -EPERM;
+
+ suspend_time = udev->last_busy + udev->autosuspend_delay;
if (udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i];
@@ -974,6 +960,24 @@ static int autosuspend_check(struct usb_device *udev)
}
}
}
+
+ /* If everything is okay but the device hasn't been idle for long
+ * enough, queue a delayed autosuspend request.
+ */
+ if (time_after(suspend_time, jiffies)) {
+ if (!timer_pending(&udev->autosuspend.timer)) {
+
+ /* The value of jiffies may change between the
+ * time_after() comparison above and the subtraction
+ * below. That's okay; the system behaves sanely
+ * when a timer is registered for the present moment
+ * or for the past.
+ */
+ queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
+ suspend_time - jiffies);
+ }
+ return -EAGAIN;
+ }
return 0;
}
@@ -981,7 +985,7 @@ static int autosuspend_check(struct usb_device *udev)
#define autosuspend_check(udev) 0
-#endif
+#endif /* CONFIG_USB_SUSPEND */
/**
* usb_suspend_both - suspend a USB device and its interfaces
@@ -1019,26 +1023,25 @@ static int autosuspend_check(struct usb_device *udev)
*
* This routine can run only in process context.
*/
-int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
+static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
{
int status = 0;
int i = 0;
struct usb_interface *intf;
struct usb_device *parent = udev->parent;
- cancel_delayed_work(&udev->autosuspend);
- if (udev->state == USB_STATE_NOTATTACHED)
- return 0;
- if (udev->state == USB_STATE_SUSPENDED)
- return 0;
+ if (udev->state == USB_STATE_NOTATTACHED ||
+ udev->state == USB_STATE_SUSPENDED)
+ goto done;
udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
if (udev->auto_pm) {
status = autosuspend_check(udev);
if (status < 0)
- return status;
+ goto done;
}
+ cancel_delayed_work(&udev->autosuspend);
/* Suspend all the interfaces and then udev itself */
if (udev->actconfig) {
@@ -1063,6 +1066,7 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
} else if (parent)
usb_autosuspend_device(parent);
+ done:
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
return status;
}
@@ -1095,7 +1099,7 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
*
* This routine can run only in process context.
*/
-int usb_resume_both(struct usb_device *udev)
+static int usb_resume_both(struct usb_device *udev)
{
int status = 0;
int i;
@@ -1103,11 +1107,17 @@ int usb_resume_both(struct usb_device *udev)
struct usb_device *parent = udev->parent;
cancel_delayed_work(&udev->autosuspend);
- if (udev->state == USB_STATE_NOTATTACHED)
- return -ENODEV;
+ if (udev->state == USB_STATE_NOTATTACHED) {
+ status = -ENODEV;
+ goto done;
+ }
/* Propagate the resume up the tree, if necessary */
if (udev->state == USB_STATE_SUSPENDED) {
+ if (udev->auto_pm && udev->autoresume_disabled) {
+ status = -EPERM;
+ goto done;
+ }
if (parent) {
status = usb_autoresume_device(parent);
if (status == 0) {
@@ -1153,6 +1163,7 @@ int usb_resume_both(struct usb_device *udev)
}
}
+ done:
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
return status;
}
@@ -1167,20 +1178,34 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt)
int status = 0;
usb_pm_lock(udev);
+ udev->auto_pm = 1;
udev->pm_usage_cnt += inc_usage_cnt;
WARN_ON(udev->pm_usage_cnt < 0);
if (inc_usage_cnt >= 0 && udev->pm_usage_cnt > 0) {
- udev->auto_pm = 1;
- status = usb_resume_both(udev);
+ if (udev->state == USB_STATE_SUSPENDED)
+ status = usb_resume_both(udev);
if (status != 0)
udev->pm_usage_cnt -= inc_usage_cnt;
- } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0)
- queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
- USB_AUTOSUSPEND_DELAY);
+ else if (inc_usage_cnt)
+ udev->last_busy = jiffies;
+ } else if (inc_usage_cnt <= 0 && udev->pm_usage_cnt <= 0) {
+ if (inc_usage_cnt)
+ udev->last_busy = jiffies;
+ status = usb_suspend_both(udev, PMSG_SUSPEND);
+ }
usb_pm_unlock(udev);
return status;
}
+/* usb_autosuspend_work - callback routine to autosuspend a USB device */
+void usb_autosuspend_work(struct work_struct *work)
+{
+ struct usb_device *udev =
+ container_of(work, struct usb_device, autosuspend.work);
+
+ usb_autopm_do_device(udev, 0);
+}
+
/**
* usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces
* @udev: the usb_device to autosuspend
@@ -1211,6 +1236,26 @@ void usb_autosuspend_device(struct usb_device *udev)
}
/**
+ * usb_try_autosuspend_device - attempt an autosuspend of a USB device and its interfaces
+ * @udev: the usb_device to autosuspend
+ *
+ * This routine should be called when a core subsystem thinks @udev may
+ * be ready to autosuspend.
+ *
+ * @udev's usage counter left unchanged. If it or any of the usage counters
+ * for an active interface is greater than 0, or autosuspend is not allowed
+ * for any other reason, no autosuspend request will be queued.
+ *
+ * This routine can run only in process context.
+ */
+void usb_try_autosuspend_device(struct usb_device *udev)
+{
+ usb_autopm_do_device(udev, 0);
+ // dev_dbg(&udev->dev, "%s: cnt %d\n",
+ // __FUNCTION__, udev->pm_usage_cnt);
+}
+
+/**
* usb_autoresume_device - immediately autoresume a USB device and its interfaces
* @udev: the usb_device to autoresume
*
@@ -1252,15 +1297,20 @@ static int usb_autopm_do_interface(struct usb_interface *intf,
if (intf->condition == USB_INTERFACE_UNBOUND)
status = -ENODEV;
else {
+ udev->auto_pm = 1;
intf->pm_usage_cnt += inc_usage_cnt;
if (inc_usage_cnt >= 0 && intf->pm_usage_cnt > 0) {
- udev->auto_pm = 1;
- status = usb_resume_both(udev);
+ if (udev->state == USB_STATE_SUSPENDED)
+ status = usb_resume_both(udev);
if (status != 0)
intf->pm_usage_cnt -= inc_usage_cnt;
- } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0)
- queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
- USB_AUTOSUSPEND_DELAY);
+ else if (inc_usage_cnt)
+ udev->last_busy = jiffies;
+ } else if (inc_usage_cnt <= 0 && intf->pm_usage_cnt <= 0) {
+ if (inc_usage_cnt)
+ udev->last_busy = jiffies;
+ status = usb_suspend_both(udev, PMSG_SUSPEND);
+ }
}
usb_pm_unlock(udev);
return status;
@@ -1319,11 +1369,14 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
* or @intf is unbound. A typical example would be a character-device
* driver when its device file is opened.
*
- * The routine increments @intf's usage counter. So long as the counter
- * is greater than 0, autosuspend will not be allowed for @intf or its
- * usb_device. When the driver is finished using @intf it should call
- * usb_autopm_put_interface() to decrement the usage counter and queue
- * a delayed autosuspend request (if the counter is <= 0).
+ *
+ * The routine increments @intf's usage counter. (However if the
+ * autoresume fails then the counter is re-decremented.) So long as the
+ * counter is greater than 0, autosuspend will not be allowed for @intf
+ * or its usb_device. When the driver is finished using @intf it should
+ * call usb_autopm_put_interface() to decrement the usage counter and
+ * queue a delayed autosuspend request (if the counter is <= 0).
+ *
*
* Note that @intf->pm_usage_cnt is owned by the interface driver. The
* core will not change its value other than the increment and decrement
@@ -1371,50 +1424,96 @@ int usb_autopm_set_interface(struct usb_interface *intf)
}
EXPORT_SYMBOL_GPL(usb_autopm_set_interface);
+#else
+
+void usb_autosuspend_work(struct work_struct *work)
+{}
+
#endif /* CONFIG_USB_SUSPEND */
-static int usb_suspend(struct device *dev, pm_message_t message)
+/**
+ * usb_external_suspend_device - external suspend of a USB device and its interfaces
+ * @udev: the usb_device to suspend
+ * @msg: Power Management message describing this state transition
+ *
+ * This routine handles external suspend requests: ones not generated
+ * internally by a USB driver (autosuspend) but rather coming from the user
+ * (via sysfs) or the PM core (system sleep). The suspend will be carried
+ * out regardless of @udev's usage counter or those of its interfaces,
+ * and regardless of whether or not remote wakeup is enabled. Of course,
+ * interface drivers still have the option of failing the suspend (if
+ * there are unsuspended children, for example).
+ *
+ * The caller must hold @udev's device lock.
+ */
+int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
{
int status;
- if (is_usb_device(dev)) {
- struct usb_device *udev = to_usb_device(dev);
-
- usb_pm_lock(udev);
- udev->auto_pm = 0;
- status = usb_suspend_both(udev, message);
- usb_pm_unlock(udev);
- } else
- status = 0;
+ usb_pm_lock(udev);
+ udev->auto_pm = 0;
+ status = usb_suspend_both(udev, msg);
+ usb_pm_unlock(udev);
return status;
}
-static int usb_resume(struct device *dev)
+/**
+ * usb_external_resume_device - external resume of a USB device and its interfaces
+ * @udev: the usb_device to resume
+ *
+ * This routine handles external resume requests: ones not generated
+ * internally by a USB driver (autoresume) but rather coming from the user
+ * (via sysfs), the PM core (system resume), or the device itself (remote
+ * wakeup). @udev's usage counter is unaffected.
+ *
+ * The caller must hold @udev's device lock.
+ */
+int usb_external_resume_device(struct usb_device *udev)
{
int status;
- if (is_usb_device(dev)) {
- struct usb_device *udev = to_usb_device(dev);
-
- usb_pm_lock(udev);
- udev->auto_pm = 0;
- status = usb_resume_both(udev);
- usb_pm_unlock(udev);
+ usb_pm_lock(udev);
+ udev->auto_pm = 0;
+ status = usb_resume_both(udev);
+ usb_pm_unlock(udev);
- /* Rebind drivers that had no suspend method? */
- } else
- status = 0;
+ /* Now that the device is awake, we can start trying to autosuspend
+ * it again. */
+ if (status == 0)
+ usb_try_autosuspend_device(udev);
return status;
}
+static int usb_suspend(struct device *dev, pm_message_t message)
+{
+ if (!is_usb_device(dev)) /* Ignore PM for interfaces */
+ return 0;
+ return usb_external_suspend_device(to_usb_device(dev), message);
+}
+
+static int usb_resume(struct device *dev)
+{
+ struct usb_device *udev;
+
+ if (!is_usb_device(dev)) /* Ignore PM for interfaces */
+ return 0;
+ udev = to_usb_device(dev);
+ if (udev->autoresume_disabled)
+ return -EPERM;
+ return usb_external_resume_device(udev);
+}
+
+#else
+
+#define usb_suspend NULL
+#define usb_resume NULL
+
#endif /* CONFIG_PM */
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
-#ifdef CONFIG_PM
.suspend = usb_suspend,
.resume = usb_resume,
-#endif
};
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index 5e628ae3aec..e0ec7045e86 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -229,7 +229,7 @@ static int init_endpoint_class(void)
kref_init(&ep_class->kref);
ep_class->class = class_create(THIS_MODULE, "usb_endpoint");
if (IS_ERR(ep_class->class)) {
- result = IS_ERR(ep_class->class);
+ result = PTR_ERR(ep_class->class);
goto class_create_error;
}
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index b531a4fd30c..9bbcb20e2d9 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -184,7 +184,7 @@ static void generic_disconnect(struct usb_device *udev)
/* if this is only an unbind, not a physical disconnect, then
* unconfigure the device */
if (udev->actconfig)
- usb_set_configuration(udev, 0);
+ usb_set_configuration(udev, -1);
usb_remove_sysfs_dev_files(udev);
}
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index b26c19e8d19..40cf882293e 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -37,6 +37,7 @@
#include <asm/irq.h>
#include <asm/byteorder.h>
#include <linux/platform_device.h>
+#include <linux/workqueue.h>
#include <linux/usb.h>
@@ -544,6 +545,8 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
unsigned long flags;
char buffer[4]; /* Any root hubs with > 31 ports? */
+ if (unlikely(!hcd->rh_registered))
+ return;
if (!hcd->uses_new_polling && !hcd->status_urb)
return;
@@ -1296,14 +1299,26 @@ int hcd_bus_resume (struct usb_bus *bus)
return status;
}
+/* Workqueue routine for root-hub remote wakeup */
+static void hcd_resume_work(struct work_struct *work)
+{
+ struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work);
+ struct usb_device *udev = hcd->self.root_hub;
+
+ usb_lock_device(udev);
+ usb_mark_last_busy(udev);
+ usb_external_resume_device(udev);
+ usb_unlock_device(udev);
+}
+
/**
* usb_hcd_resume_root_hub - called by HCD to resume its root hub
* @hcd: host controller for this root hub
*
* The USB host controller calls this function when its root hub is
* suspended (with the remote wakeup feature enabled) and a remote
- * wakeup request is received. It queues a request for khubd to
- * resume the root hub (that is, manage its downstream ports again).
+ * wakeup request is received. The routine submits a workqueue request
+ * to resume the root hub (that is, manage its downstream ports again).
*/
void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
{
@@ -1311,7 +1326,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
spin_lock_irqsave (&hcd_root_hub_lock, flags);
if (hcd->rh_registered)
- usb_resume_root_hub (hcd->self.root_hub);
+ queue_work(ksuspend_usb_wq, &hcd->wakeup_work);
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
}
EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
@@ -1500,6 +1515,9 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
init_timer(&hcd->rh_timer);
hcd->rh_timer.function = rh_timer_func;
hcd->rh_timer.data = (unsigned long) hcd;
+#ifdef CONFIG_PM
+ INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
+#endif
hcd->driver = driver;
hcd->product_desc = (driver->product_desc) ? driver->product_desc :
@@ -1666,16 +1684,20 @@ void usb_remove_hcd(struct usb_hcd *hcd)
hcd->rh_registered = 0;
spin_unlock_irq (&hcd_root_hub_lock);
+#ifdef CONFIG_PM
+ flush_workqueue(ksuspend_usb_wq);
+#endif
+
mutex_lock(&usb_bus_list_lock);
usb_disconnect(&hcd->self.root_hub);
mutex_unlock(&usb_bus_list_lock);
- hcd->poll_rh = 0;
- del_timer_sync(&hcd->rh_timer);
-
hcd->driver->stop(hcd);
hcd->state = HC_STATE_HALT;
+ hcd->poll_rh = 0;
+ del_timer_sync(&hcd->rh_timer);
+
if (hcd->irq >= 0)
free_irq(hcd->irq, hcd);
usb_deregister_bus(&hcd->self);
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 2a269ca2051..ef50fa494e4 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -68,6 +68,9 @@ struct usb_hcd {
struct timer_list rh_timer; /* drives root-hub polling */
struct urb *status_urb; /* the current status urb */
+#ifdef CONFIG_PM
+ struct work_struct wakeup_work; /* for remote wakeup */
+#endif
/*
* hardware info/state
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 590ec82d051..bde29ab2b50 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -44,6 +44,7 @@ struct usb_hub {
struct usb_hub_status hub;
struct usb_port_status port;
} *status; /* buffer for status reports */
+ struct mutex status_mutex; /* for the status buffer */
int error; /* last reported error */
int nerrors; /* track consecutive errors */
@@ -118,8 +119,7 @@ MODULE_PARM_DESC(use_both_schemes,
"first one fails");
-#ifdef DEBUG
-static inline char *portspeed (int portstatus)
+static inline char *portspeed(int portstatus)
{
if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED))
return "480 Mb/s";
@@ -128,7 +128,6 @@ static inline char *portspeed (int portstatus)
else
return "12 Mb/s";
}
-#endif
/* Note that hdev or one of its children must be locked! */
static inline struct usb_hub *hdev_to_hub(struct usb_device *hdev)
@@ -535,6 +534,7 @@ static int hub_hub_status(struct usb_hub *hub,
{
int ret;
+ mutex_lock(&hub->status_mutex);
ret = get_hub_status(hub->hdev, &hub->status->hub);
if (ret < 0)
dev_err (hub->intfdev,
@@ -544,6 +544,7 @@ static int hub_hub_status(struct usb_hub *hub,
*change = le16_to_cpu(hub->status->hub.wHubChange);
ret = 0;
}
+ mutex_unlock(&hub->status_mutex);
return ret;
}
@@ -617,6 +618,7 @@ static int hub_configure(struct usb_hub *hub,
ret = -ENOMEM;
goto fail;
}
+ mutex_init(&hub->status_mutex);
hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
if (!hub->descriptor) {
@@ -1277,11 +1279,8 @@ int usb_new_device(struct usb_device *udev)
{
int err;
- /* Lock ourself into memory in order to keep a probe sequence
- * sleeping in a new thread from allowing us to be unloaded.
- */
- if (!try_module_get(THIS_MODULE))
- return -EINVAL;
+ /* Determine quirks */
+ usb_detect_quirks(udev);
err = usb_get_configuration(udev);
if (err < 0) {
@@ -1368,11 +1367,15 @@ int usb_new_device(struct usb_device *udev)
}
#endif
+ /* export the usbdev device-node for libusb */
+ udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
+ (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
+
/* Register the device. The device driver is responsible
- * for adding the device files to usbfs and sysfs and for
- * configuring the device.
+ * for adding the device files to sysfs and for configuring
+ * the device.
*/
- err = device_add (&udev->dev);
+ err = device_add(&udev->dev);
if (err) {
dev_err(&udev->dev, "can't device_add, error %d\n", err);
goto fail;
@@ -1383,7 +1386,6 @@ int usb_new_device(struct usb_device *udev)
usb_autoresume_device(udev->parent);
exit:
- module_put(THIS_MODULE);
return err;
fail:
@@ -1396,6 +1398,7 @@ static int hub_port_status(struct usb_hub *hub, int port1,
{
int ret;
+ mutex_lock(&hub->status_mutex);
ret = get_port_status(hub->hdev, port1, &hub->status->port);
if (ret < 4) {
dev_err (hub->intfdev,
@@ -1407,6 +1410,7 @@ static int hub_port_status(struct usb_hub *hub, int port1,
*change = le16_to_cpu(hub->status->port.wPortChange);
ret = 0;
}
+ mutex_unlock(&hub->status_mutex);
return ret;
}
@@ -1855,12 +1859,8 @@ static int remote_wakeup(struct usb_device *udev)
usb_lock_device(udev);
if (udev->state == USB_STATE_SUSPENDED) {
dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
- status = usb_autoresume_device(udev);
-
- /* Give the interface drivers a chance to do something,
- * then autosuspend the device again. */
- if (status == 0)
- usb_autosuspend_device(udev);
+ usb_mark_last_busy(udev);
+ status = usb_external_resume_device(udev);
}
usb_unlock_device(udev);
return status;
@@ -1904,6 +1904,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
struct usb_hub *hub = usb_get_intfdata (intf);
struct usb_device *hdev = hub->hdev;
unsigned port1;
+ int status = 0;
/* fail if children aren't already suspended */
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
@@ -1927,24 +1928,18 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
dev_dbg(&intf->dev, "%s\n", __FUNCTION__);
+ /* stop khubd and related activity */
+ hub_quiesce(hub);
+
/* "global suspend" of the downstream HC-to-USB interface */
if (!hdev->parent) {
- struct usb_bus *bus = hdev->bus;
- if (bus) {
- int status = hcd_bus_suspend (bus);
-
- if (status != 0) {
- dev_dbg(&hdev->dev, "'global' suspend %d\n",
- status);
- return status;
- }
- } else
- return -EOPNOTSUPP;
+ status = hcd_bus_suspend(hdev->bus);
+ if (status != 0) {
+ dev_dbg(&hdev->dev, "'global' suspend %d\n", status);
+ hub_activate(hub);
+ }
}
-
- /* stop khubd and related activity */
- hub_quiesce(hub);
- return 0;
+ return status;
}
static int hub_resume(struct usb_interface *intf)
@@ -1989,13 +1984,6 @@ static inline int remote_wakeup(struct usb_device *udev)
#define hub_resume NULL
#endif
-void usb_resume_root_hub(struct usb_device *hdev)
-{
- struct usb_hub *hub = hdev_to_hub(hdev);
-
- kick_khubd(hub);
-}
-
/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
*
@@ -2439,7 +2427,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
if (portchange & USB_PORT_STAT_C_CONNECTION) {
status = hub_port_debounce(hub, port1);
- if (status < 0) {
+ if (status < 0 && printk_ratelimit()) {
dev_err (hub_dev,
"connect-debounce failed, port %d disabled\n",
port1);
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index 11dad22da41..cddfc62c461 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -662,7 +662,7 @@ static 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,
- &usbfs_device_file_operations,
+ &usbdev_file_operations,
devuid, devgid);
if (dev->usbfs_dentry == NULL) {
err ("error creating usbfs device entry");
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 8aca3574c2b..b7434787db5 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -11,6 +11,7 @@
#include <linux/timer.h>
#include <linux/ctype.h>
#include <linux/device.h>
+#include <linux/usb/quirks.h>
#include <asm/byteorder.h>
#include <asm/scatterlist.h>
@@ -220,10 +221,15 @@ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_INT) {
+ int interval;
+
+ if (usb_dev->speed == USB_SPEED_HIGH)
+ interval = 1 << min(15, ep->desc.bInterval - 1);
+ else
+ interval = ep->desc.bInterval;
pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
usb_fill_int_urb(urb, usb_dev, pipe, data, len,
- usb_api_blocking_completion, NULL,
- ep->desc.bInterval);
+ usb_api_blocking_completion, NULL, interval);
} else
usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
usb_api_blocking_completion, NULL);
@@ -406,10 +412,24 @@ int usb_sg_init (
io->urbs [i]->status = -EINPROGRESS;
io->urbs [i]->actual_length = 0;
+ /*
+ * Some systems need to revert to PIO when DMA is temporarily
+ * unavailable. For their sakes, both transfer_buffer and
+ * transfer_dma are set when possible. However this can only
+ * work on systems without HIGHMEM, since DMA buffers located
+ * in high memory are not directly addressable by the CPU for
+ * PIO ... so when HIGHMEM is in use, transfer_buffer is NULL
+ * to prevent stale pointers and to help spot bugs.
+ */
if (dma) {
- /* hc may use _only_ transfer_dma */
io->urbs [i]->transfer_dma = sg_dma_address (sg + i);
len = sg_dma_len (sg + i);
+#ifdef CONFIG_HIGHMEM
+ io->urbs[i]->transfer_buffer = NULL;
+#else
+ io->urbs[i]->transfer_buffer =
+ page_address(sg[i].page) + sg[i].offset;
+#endif
} else {
/* hc may use _only_ transfer_buffer */
io->urbs [i]->transfer_buffer =
@@ -685,7 +705,10 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid,
/* Try to read the string descriptor by asking for the maximum
* possible number of bytes */
- rc = usb_get_string(dev, langid, index, buf, 255);
+ if (dev->quirks & USB_QUIRK_STRING_FETCH_255)
+ rc = -EIO;
+ else
+ rc = usb_get_string(dev, langid, index, buf, 255);
/* If that failed try to read the descriptor length, then
* ask for just that many bytes */
@@ -1296,7 +1319,7 @@ int usb_reset_configuration(struct usb_device *dev)
return 0;
}
-static void release_interface(struct device *dev)
+void usb_release_interface(struct device *dev)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_interface_cache *intfc =
@@ -1306,6 +1329,67 @@ static void release_interface(struct device *dev)
kfree(intf);
}
+#ifdef CONFIG_HOTPLUG
+static int usb_if_uevent(struct device *dev, char **envp, int num_envp,
+ char *buffer, int buffer_size)
+{
+ struct usb_device *usb_dev;
+ struct usb_interface *intf;
+ struct usb_host_interface *alt;
+ int i = 0;
+ int length = 0;
+
+ if (!dev)
+ return -ENODEV;
+
+ /* driver is often null here; dev_dbg() would oops */
+ pr_debug ("usb %s: uevent\n", dev->bus_id);
+
+ intf = to_usb_interface(dev);
+ usb_dev = interface_to_usbdev(intf);
+ alt = intf->cur_altsetting;
+
+ if (add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "INTERFACE=%d/%d/%d",
+ alt->desc.bInterfaceClass,
+ alt->desc.bInterfaceSubClass,
+ alt->desc.bInterfaceProtocol))
+ return -ENOMEM;
+
+ if (add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
+ le16_to_cpu(usb_dev->descriptor.idVendor),
+ le16_to_cpu(usb_dev->descriptor.idProduct),
+ le16_to_cpu(usb_dev->descriptor.bcdDevice),
+ usb_dev->descriptor.bDeviceClass,
+ usb_dev->descriptor.bDeviceSubClass,
+ usb_dev->descriptor.bDeviceProtocol,
+ alt->desc.bInterfaceClass,
+ alt->desc.bInterfaceSubClass,
+ alt->desc.bInterfaceProtocol))
+ return -ENOMEM;
+
+ envp[i] = NULL;
+ return 0;
+}
+
+#else
+
+static int usb_if_uevent(struct device *dev, char **envp,
+ int num_envp, char *buffer, int buffer_size)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_HOTPLUG */
+
+struct device_type usb_if_device_type = {
+ .name = "usb_interface",
+ .release = usb_release_interface,
+ .uevent = usb_if_uevent,
+};
+
/*
* usb_set_configuration - Makes a particular device setting be current
* @dev: the device whose configuration is being updated
@@ -1316,6 +1400,14 @@ static void release_interface(struct device *dev)
* use this kind of configurability; many devices only have one
* configuration.
*
+ * @configuration is the value of the configuration to be installed.
+ * According to the USB spec (e.g. section 9.1.1.5), configuration values
+ * must be non-zero; a value of zero indicates that the device in
+ * unconfigured. However some devices erroneously use 0 as one of their
+ * configuration values. To help manage such devices, this routine will
+ * accept @configuration = -1 as indicating the device should be put in
+ * an unconfigured state.
+ *
* USB device configurations may affect Linux interoperability,
* power consumption and the functionality available. For example,
* the default configuration is limited to using 100mA of bus power,
@@ -1332,7 +1424,7 @@ static void release_interface(struct device *dev)
*
* This call is synchronous. The calling context must be able to sleep,
* 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.
+ * bus mutex; 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 successful completion, each interface
@@ -1347,10 +1439,15 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
struct usb_interface **new_interfaces = NULL;
int n, nintf;
- for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
- if (dev->config[i].desc.bConfigurationValue == configuration) {
- cp = &dev->config[i];
- break;
+ if (configuration == -1)
+ configuration = 0;
+ else {
+ for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+ if (dev->config[i].desc.bConfigurationValue ==
+ configuration) {
+ cp = &dev->config[i];
+ break;
+ }
}
}
if ((!cp && configuration != 0))
@@ -1359,6 +1456,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
/* The USB spec says configuration 0 means unconfigured.
* But if a device includes a configuration numbered 0,
* we will accept it as a correctly configured state.
+ * Use -1 if you really want to unconfigure the device.
*/
if (cp && configuration == 0)
dev_warn(&dev->dev, "config 0 descriptor??\n");
@@ -1455,8 +1553,8 @@ free_interfaces:
intf->dev.parent = &dev->dev;
intf->dev.driver = NULL;
intf->dev.bus = &usb_bus_type;
+ intf->dev.type = &usb_if_device_type;
intf->dev.dma_mask = dev->dev.dma_mask;
- intf->dev.release = release_interface;
device_initialize (&intf->dev);
mark_quiesced(intf);
sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h
index 627a5a2fc9c..7f31a495a25 100644
--- a/drivers/usb/core/otg_whitelist.h
+++ b/drivers/usb/core/otg_whitelist.h
@@ -31,7 +31,7 @@ static struct usb_device_id whitelist_table [] = {
{ USB_DEVICE_INFO(7, 1, 3) },
#endif
-#ifdef CONFIG_USB_CDCETHER
+#ifdef CONFIG_USB_NET_CDCETHER
/* Linux-USB CDC Ethernet gadget */
{ USB_DEVICE(0x0525, 0xa4a1), },
/* Linux-USB CDC Ethernet + RNDIS gadget */
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
new file mode 100644
index 00000000000..739f520908a
--- /dev/null
+++ b/drivers/usb/core/quirks.c
@@ -0,0 +1,78 @@
+/*
+ * USB device quirk handling logic and table
+ *
+ * Copyright (c) 2007 Oliver Neukum
+ * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * 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, version 2.
+ *
+ *
+ */
+
+#include <linux/usb.h>
+#include <linux/usb/quirks.h>
+#include "usb.h"
+
+/* List of quirky USB devices. Please keep this list ordered by:
+ * 1) Vendor ID
+ * 2) Product ID
+ * 3) Class ID
+ *
+ * as we want specific devices to be overridden first, and only after that, any
+ * class specific quirks.
+ *
+ * Right now the logic aborts if it finds a valid device in the table, we might
+ * want to change that in the future if it turns out that a whole class of
+ * devices is broken...
+ */
+static const struct usb_device_id usb_quirk_list[] = {
+ /* HP 5300/5370C scanner */
+ { USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 },
+ /* Seiko Epson Corp - Perfection 1670 */
+ { USB_DEVICE(0x04b8, 0x011f), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
+ /* Elsa MicroLink 56k (V.250) */
+ { USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
+
+ { } /* terminating entry must be last */
+};
+
+static void usb_autosuspend_quirk(struct usb_device *udev)
+{
+#ifdef CONFIG_USB_SUSPEND
+ /* disable autosuspend, but allow the user to re-enable it via sysfs */
+ udev->autosuspend_disabled = 1;
+#endif
+}
+
+static const struct usb_device_id *find_id(struct usb_device *udev)
+{
+ const struct usb_device_id *id = usb_quirk_list;
+
+ for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass ||
+ id->driver_info; id++) {
+ if (usb_match_device(udev, id))
+ return id;
+ }
+ return NULL;
+}
+
+/*
+ * Detect any quirks the device has, and do any housekeeping for it if needed.
+ */
+void usb_detect_quirks(struct usb_device *udev)
+{
+ const struct usb_device_id *id = usb_quirk_list;
+
+ id = find_id(udev);
+ if (id)
+ udev->quirks = (u32)(id->driver_info);
+ if (udev->quirks)
+ dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
+ udev->quirks);
+
+ /* do any special quirk handling here if needed */
+ if (udev->quirks & USB_QUIRK_NO_AUTOSUSPEND)
+ usb_autosuspend_quirk(udev);
+}
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 4eaa0ee8e72..e7c98237748 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
+#include <linux/string.h>
#include <linux/usb.h>
#include "usb.h"
@@ -63,7 +64,7 @@ set_bConfigurationValue(struct device *dev, struct device_attribute *attr,
struct usb_device *udev = to_usb_device(dev);
int config, value;
- if (sscanf(buf, "%u", &config) != 1 || config > 255)
+ if (sscanf(buf, "%d", &config) != 1 || config < -1 || config > 255)
return -EINVAL;
usb_lock_device(udev);
value = usb_set_configuration(udev, config);
@@ -117,6 +118,16 @@ show_speed(struct device *dev, struct device_attribute *attr, char *buf)
static DEVICE_ATTR(speed, S_IRUGO, show_speed, NULL);
static ssize_t
+show_busnum(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct usb_device *udev;
+
+ udev = to_usb_device(dev);
+ return sprintf(buf, "%d\n", udev->bus->busnum);
+}
+static DEVICE_ATTR(busnum, S_IRUGO, show_busnum, NULL);
+
+static ssize_t
show_devnum(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_device *udev;
@@ -148,6 +159,151 @@ show_maxchild(struct device *dev, struct device_attribute *attr, char *buf)
}
static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL);
+static ssize_t
+show_quirks(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct usb_device *udev;
+
+ udev = to_usb_device(dev);
+ return sprintf(buf, "0x%x\n", udev->quirks);
+}
+static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL);
+
+#ifdef CONFIG_USB_SUSPEND
+
+static ssize_t
+show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct usb_device *udev = to_usb_device(dev);
+
+ return sprintf(buf, "%d\n", udev->autosuspend_delay / HZ);
+}
+
+static ssize_t
+set_autosuspend(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_device *udev = to_usb_device(dev);
+ int value;
+
+ if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/HZ ||
+ value <= - INT_MAX/HZ)
+ return -EINVAL;
+ value *= HZ;
+
+ udev->autosuspend_delay = value;
+ if (value >= 0)
+ usb_try_autosuspend_device(udev);
+ else {
+ if (usb_autoresume_device(udev) == 0)
+ usb_autosuspend_device(udev);
+ }
+ return count;
+}
+
+static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR,
+ show_autosuspend, set_autosuspend);
+
+static const char on_string[] = "on";
+static const char auto_string[] = "auto";
+static const char suspend_string[] = "suspend";
+
+static ssize_t
+show_level(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct usb_device *udev = to_usb_device(dev);
+ const char *p = auto_string;
+
+ if (udev->state == USB_STATE_SUSPENDED) {
+ if (udev->autoresume_disabled)
+ p = suspend_string;
+ } else {
+ if (udev->autosuspend_disabled)
+ p = on_string;
+ }
+ return sprintf(buf, "%s\n", p);
+}
+
+static ssize_t
+set_level(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_device *udev = to_usb_device(dev);
+ int len = count;
+ char *cp;
+ int rc = 0;
+
+ cp = memchr(buf, '\n', count);
+ if (cp)
+ len = cp - buf;
+
+ usb_lock_device(udev);
+
+ /* Setting the flags without calling usb_pm_lock is a subject to
+ * races, but who cares...
+ */
+ if (len == sizeof on_string - 1 &&
+ strncmp(buf, on_string, len) == 0) {
+ udev->autosuspend_disabled = 1;
+ udev->autoresume_disabled = 0;
+ rc = usb_external_resume_device(udev);
+
+ } else if (len == sizeof auto_string - 1 &&
+ strncmp(buf, auto_string, len) == 0) {
+ udev->autosuspend_disabled = 0;
+ udev->autoresume_disabled = 0;
+ rc = usb_external_resume_device(udev);
+
+ } else if (len == sizeof suspend_string - 1 &&
+ strncmp(buf, suspend_string, len) == 0) {
+ udev->autosuspend_disabled = 0;
+ udev->autoresume_disabled = 1;
+ rc = usb_external_suspend_device(udev, PMSG_SUSPEND);
+
+ } else
+ rc = -EINVAL;
+
+ usb_unlock_device(udev);
+ return (rc < 0 ? rc : count);
+}
+
+static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level);
+
+static char power_group[] = "power";
+
+static int add_power_attributes(struct device *dev)
+{
+ int rc = 0;
+
+ if (is_usb_device(dev)) {
+ rc = sysfs_add_file_to_group(&dev->kobj,
+ &dev_attr_autosuspend.attr,
+ power_group);
+ if (rc == 0)
+ rc = sysfs_add_file_to_group(&dev->kobj,
+ &dev_attr_level.attr,
+ power_group);
+ }
+ return rc;
+}
+
+static void remove_power_attributes(struct device *dev)
+{
+ sysfs_remove_file_from_group(&dev->kobj,
+ &dev_attr_level.attr,
+ power_group);
+ sysfs_remove_file_from_group(&dev->kobj,
+ &dev_attr_autosuspend.attr,
+ power_group);
+}
+
+#else
+
+#define add_power_attributes(dev) 0
+#define remove_power_attributes(dev) do {} while (0)
+
+#endif /* CONFIG_USB_SUSPEND */
+
/* Descriptor fields */
#define usb_descriptor_attr_le16(field, format_string) \
static ssize_t \
@@ -201,9 +357,11 @@ static struct attribute *dev_attrs[] = {
&dev_attr_bNumConfigurations.attr,
&dev_attr_bMaxPacketSize0.attr,
&dev_attr_speed.attr,
+ &dev_attr_busnum.attr,
&dev_attr_devnum.attr,
&dev_attr_version.attr,
&dev_attr_maxchild.attr,
+ &dev_attr_quirks.attr,
NULL,
};
static struct attribute_group dev_attr_grp = {
@@ -219,6 +377,10 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
if (retval)
return retval;
+ retval = add_power_attributes(dev);
+ if (retval)
+ goto error;
+
if (udev->manufacturer) {
retval = device_create_file(dev, &dev_attr_manufacturer);
if (retval)
@@ -239,10 +401,7 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
goto error;
return 0;
error:
- usb_remove_ep_files(&udev->ep0);
- device_remove_file(dev, &dev_attr_manufacturer);
- device_remove_file(dev, &dev_attr_product);
- device_remove_file(dev, &dev_attr_serial);
+ usb_remove_sysfs_dev_files(udev);
return retval;
}
@@ -251,14 +410,11 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev)
struct device *dev = &udev->dev;
usb_remove_ep_files(&udev->ep0);
+ device_remove_file(dev, &dev_attr_manufacturer);
+ device_remove_file(dev, &dev_attr_product);
+ device_remove_file(dev, &dev_attr_serial);
+ remove_power_attributes(dev);
sysfs_remove_group(&dev->kobj, &dev_attr_grp);
-
- if (udev->manufacturer)
- device_remove_file(dev, &dev_attr_manufacturer);
- if (udev->product)
- device_remove_file(dev, &dev_attr_product);
- if (udev->serial)
- device_remove_file(dev, &dev_attr_serial);
}
/* Interface fields */
@@ -362,33 +518,28 @@ static inline void usb_remove_intf_ep_files(struct usb_interface *intf)
int usb_create_sysfs_intf_files(struct usb_interface *intf)
{
+ struct device *dev = &intf->dev;
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_host_interface *alt = intf->cur_altsetting;
int retval;
- retval = sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
+ retval = sysfs_create_group(&dev->kobj, &intf_attr_grp);
if (retval)
- goto error;
+ return retval;
if (alt->string == NULL)
alt->string = usb_cache_string(udev, alt->desc.iInterface);
if (alt->string)
- retval = device_create_file(&intf->dev, &dev_attr_interface);
+ retval = device_create_file(dev, &dev_attr_interface);
usb_create_intf_ep_files(intf, udev);
return 0;
-error:
- if (alt->string)
- device_remove_file(&intf->dev, &dev_attr_interface);
- sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
- usb_remove_intf_ep_files(intf);
- return retval;
}
void usb_remove_sysfs_intf_files(struct usb_interface *intf)
{
- usb_remove_intf_ep_files(intf);
- sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
+ struct device *dev = &intf->dev;
- if (intf->cur_altsetting->string)
- device_remove_file(&intf->dev, &dev_attr_interface);
+ usb_remove_intf_ep_files(intf);
+ device_remove_file(dev, &dev_attr_interface);
+ sysfs_remove_group(&dev->kobj, &intf_attr_grp);
}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 3db721cd557..dfd1b5c87ca 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -22,6 +22,7 @@
*/
#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/string.h>
#include <linux/bitops.h>
#include <linux/slab.h>
@@ -48,7 +49,18 @@ const char *usbcore_name = "usbcore";
static int nousb; /* Disable USB when built into kernel image */
-struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */
+/* Workqueue for autosuspend and for remote wakeup of root hubs */
+struct workqueue_struct *ksuspend_usb_wq;
+
+#ifdef CONFIG_USB_SUSPEND
+static int usb_autosuspend_delay = 2; /* Default delay value,
+ * in seconds */
+module_param_named(autosuspend, usb_autosuspend_delay, int, 0644);
+MODULE_PARM_DESC(autosuspend, "default autosuspend delay");
+
+#else
+#define usb_autosuspend_delay 0
+#endif
/**
@@ -185,6 +197,11 @@ static void usb_release_dev(struct device *dev)
kfree(udev);
}
+struct device_type usb_device_type = {
+ .name = "usb_device",
+ .release = usb_release_dev,
+};
+
#ifdef CONFIG_PM
static int ksuspend_usb_init(void)
@@ -200,27 +217,6 @@ static void ksuspend_usb_cleanup(void)
destroy_workqueue(ksuspend_usb_wq);
}
-#ifdef CONFIG_USB_SUSPEND
-
-/* usb_autosuspend_work - callback routine to autosuspend a USB device */
-static void usb_autosuspend_work(struct work_struct *work)
-{
- struct usb_device *udev =
- container_of(work, struct usb_device, autosuspend.work);
-
- usb_pm_lock(udev);
- udev->auto_pm = 1;
- usb_suspend_both(udev, PMSG_SUSPEND);
- usb_pm_unlock(udev);
-}
-
-#else
-
-static void usb_autosuspend_work(struct work_struct *work)
-{}
-
-#endif /* CONFIG_USB_SUSPEND */
-
#else
#define ksuspend_usb_init() 0
@@ -256,13 +252,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
device_initialize(&dev->dev);
dev->dev.bus = &usb_bus_type;
+ dev->dev.type = &usb_device_type;
dev->dev.dma_mask = bus->controller->dma_mask;
- dev->dev.release = usb_release_dev;
dev->state = USB_STATE_ATTACHED;
- /* This magic assignment distinguishes devices from interfaces */
- dev->dev.platform_data = &usb_generic_driver;
-
INIT_LIST_HEAD(&dev->ep0.urb_list);
dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
@@ -306,6 +299,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
#ifdef CONFIG_PM
mutex_init(&dev->pm_mutex);
INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work);
+ dev->autosuspend_delay = usb_autosuspend_delay * HZ;
#endif
return dev;
}
@@ -890,9 +884,9 @@ static int __init usb_init(void)
retval = usb_register(&usbfs_driver);
if (retval)
goto driver_register_failed;
- retval = usbdev_init();
+ retval = usb_devio_init();
if (retval)
- goto usbdevice_init_failed;
+ goto usb_devio_init_failed;
retval = usbfs_init();
if (retval)
goto fs_init_failed;
@@ -907,8 +901,8 @@ static int __init usb_init(void)
hub_init_failed:
usbfs_cleanup();
fs_init_failed:
- usbdev_cleanup();
-usbdevice_init_failed:
+ usb_devio_cleanup();
+usb_devio_init_failed:
usb_deregister(&usbfs_driver);
driver_register_failed:
usb_major_cleanup();
@@ -935,7 +929,7 @@ static void __exit usb_exit(void)
usb_major_cleanup();
usbfs_cleanup();
usb_deregister(&usbfs_driver);
- usbdev_cleanup();
+ usb_devio_cleanup();
usb_hub_cleanup();
usb_host_cleanup();
bus_unregister(&usb_bus_type);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 17830a81be1..bf2eb0dae2e 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -13,6 +13,7 @@ extern void usb_disable_interface (struct usb_device *dev,
struct usb_interface *intf);
extern void usb_release_interface_cache(struct kref *ref);
extern void usb_disable_device (struct usb_device *dev, int skip_ep0);
+extern void usb_detect_quirks(struct usb_device *udev);
extern int usb_get_device_descriptor(struct usb_device *dev,
unsigned int size);
@@ -20,7 +21,8 @@ extern char *usb_cache_string(struct usb_device *udev, int index);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
extern void usb_kick_khubd(struct usb_device *dev);
-extern void usb_resume_root_hub(struct usb_device *dev);
+extern int usb_match_device(struct usb_device *dev,
+ const struct usb_device_id *id);
extern int usb_hub_init(void);
extern void usb_hub_cleanup(void);
@@ -31,10 +33,12 @@ extern void usb_host_cleanup(void);
#ifdef CONFIG_PM
-extern int usb_suspend_both(struct usb_device *udev, pm_message_t msg);
-extern int usb_resume_both(struct usb_device *udev);
+extern void usb_autosuspend_work(struct work_struct *work);
extern int usb_port_suspend(struct usb_device *dev);
extern int usb_port_resume(struct usb_device *dev);
+extern int usb_external_suspend_device(struct usb_device *udev,
+ pm_message_t msg);
+extern int usb_external_resume_device(struct usb_device *udev);
static inline void usb_pm_lock(struct usb_device *udev)
{
@@ -48,11 +52,6 @@ static inline void usb_pm_unlock(struct usb_device *udev)
#else
-#define usb_suspend_both(udev, msg) 0
-static inline int usb_resume_both(struct usb_device *udev)
-{
- return 0;
-}
#define usb_port_suspend(dev) 0
#define usb_port_resume(dev) 0
static inline void usb_pm_lock(struct usb_device *udev) {}
@@ -62,14 +61,14 @@ static inline void usb_pm_unlock(struct usb_device *udev) {}
#ifdef CONFIG_USB_SUSPEND
-#define USB_AUTOSUSPEND_DELAY (HZ*2)
-
extern void usb_autosuspend_device(struct usb_device *udev);
+extern void usb_try_autosuspend_device(struct usb_device *udev);
extern int usb_autoresume_device(struct usb_device *udev);
#else
-#define usb_autosuspend_device(udev) do {} while (0)
+#define usb_autosuspend_device(udev) do {} while (0)
+#define usb_try_autosuspend_device(udev) do {} while (0)
static inline int usb_autoresume_device(struct usb_device *udev)
{
return 0;
@@ -79,15 +78,13 @@ static inline int usb_autoresume_device(struct usb_device *udev)
extern struct workqueue_struct *ksuspend_usb_wq;
extern struct bus_type usb_bus_type;
+extern struct device_type usb_device_type;
+extern struct device_type usb_if_device_type;
extern struct usb_device_driver usb_generic_driver;
-/* Here's how we tell apart devices and interfaces. Luckily there's
- * no such thing as a platform USB device, so we can steal the use
- * of the platform_data field. */
-
static inline int is_usb_device(const struct device *dev)
{
- return dev->platform_data == &usb_generic_driver;
+ return dev->type == &usb_device_type;
}
/* Do the same for device drivers and interface drivers. */
@@ -123,11 +120,11 @@ extern const char *usbcore_name;
extern struct mutex usbfs_mutex;
extern struct usb_driver usbfs_driver;
extern const struct file_operations usbfs_devices_fops;
-extern const struct file_operations usbfs_device_file_operations;
+extern const struct file_operations usbdev_file_operations;
extern void usbfs_conn_disc_event(void);
-extern int usbdev_init(void);
-extern void usbdev_cleanup(void);
+extern int usb_devio_init(void);
+extern void usb_devio_cleanup(void);
struct dev_state {
struct list_head list; /* state list */
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 4097a86c4b5..8065f2b5370 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -68,6 +68,27 @@ choice
Many controller drivers are platform-specific; these
often need board-specific hooks.
+config USB_GADGET_FSL_USB2
+ boolean "Freescale Highspeed USB DR Peripheral Controller"
+ depends on MPC834x || PPC_MPC831x
+ select USB_GADGET_DUALSPEED
+ help
+ Some of Freescale PowerPC processors have a High Speed
+ Dual-Role(DR) USB controller, which supports device mode.
+
+ The number of programmable endpoints is different through
+ SOC revisions.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "fsl_usb2_udc" and force
+ all gadget drivers to also be dynamically linked.
+
+config USB_FSL_USB2
+ tristate
+ depends on USB_GADGET_FSL_USB2
+ default USB_GADGET
+ select USB_GADGET_SELECTED
+
config USB_GADGET_NET2280
boolean "NetChip 228x"
depends on PCI
@@ -370,6 +391,7 @@ config USB_GADGETFS
config USB_FILE_STORAGE
tristate "File-backed Storage Gadget"
+ depends on BLOCK
help
The File-backed Storage Gadget acts as a USB Mass Storage
disk drive. As its storage repository it can use a regular
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index e71e086a1cf..5db19396631 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
obj-$(CONFIG_USB_AT91) += at91_udc.o
+obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
#
# USB gadget drivers
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index 36b36e0175f..2a6e3163d94 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -784,7 +784,7 @@ static int at91_ep_set_halt(struct usb_ep *_ep, int value)
return status;
}
-static struct usb_ep_ops at91_ep_ops = {
+static const struct usb_ep_ops at91_ep_ops = {
.enable = at91_ep_enable,
.disable = at91_ep_disable,
.alloc_request = at91_ep_alloc_request,
@@ -912,7 +912,7 @@ static void pullup(struct at91_udc *udc, int is_on)
at91_udp_write(udc, AT91_UDP_TXVC, 0);
if (cpu_is_at91rm9200())
at91_set_gpio_value(udc->board.pullup_pin, 1);
- else if (cpu_is_at91sam9260()) {
+ else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) {
u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC);
txvc |= AT91_UDP_TXVC_PUON;
@@ -929,7 +929,7 @@ static void pullup(struct at91_udc *udc, int is_on)
at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
if (cpu_is_at91rm9200())
at91_set_gpio_value(udc->board.pullup_pin, 0);
- else if (cpu_is_at91sam9260()) {
+ else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) {
u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC);
txvc &= ~AT91_UDP_TXVC_PUON;
@@ -1651,7 +1651,7 @@ static void at91udc_shutdown(struct platform_device *dev)
pullup(platform_get_drvdata(dev), 0);
}
-static int __devinit at91udc_probe(struct platform_device *pdev)
+static int __init at91udc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct at91_udc *udc;
@@ -1762,7 +1762,7 @@ fail0:
return retval;
}
-static int __devexit at91udc_remove(struct platform_device *pdev)
+static int __exit at91udc_remove(struct platform_device *pdev)
{
struct at91_udc *udc = platform_get_drvdata(pdev);
struct resource *res;
@@ -1835,9 +1835,8 @@ static int at91udc_resume(struct platform_device *pdev)
#define at91udc_resume NULL
#endif
-static struct platform_driver at91_udc = {
- .probe = at91udc_probe,
- .remove = __devexit_p(at91udc_remove),
+static struct platform_driver at91_udc_driver = {
+ .remove = __exit_p(at91udc_remove),
.shutdown = at91udc_shutdown,
.suspend = at91udc_suspend,
.resume = at91udc_resume,
@@ -1847,15 +1846,15 @@ static struct platform_driver at91_udc = {
},
};
-static int __devinit udc_init_module(void)
+static int __init udc_init_module(void)
{
- return platform_driver_register(&at91_udc);
+ return platform_driver_probe(&at91_udc_driver, at91udc_probe);
}
module_init(udc_init_module);
-static void __devexit udc_exit_module(void)
+static void __exit udc_exit_module(void)
{
- platform_driver_unregister(&at91_udc);
+ platform_driver_unregister(&at91_udc_driver);
}
module_exit(udc_exit_module);
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 04e6b8508fb..1dd8b57f442 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -282,6 +282,9 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
#define DEV_CONFIG_CDC
#endif
+#ifdef CONFIG_USB_GADGET_FSL_USB2
+#define DEV_CONFIG_CDC
+#endif
/* For CDC-incapable hardware, choose the simple cdc subset.
* Anything that talks bulk (without notable bugs) can do this.
@@ -1735,7 +1738,8 @@ enomem:
defer_kevent (dev, WORK_RX_MEMORY);
if (retval) {
DEBUG (dev, "rx submit --> %d\n", retval);
- dev_kfree_skb_any (skb);
+ if (skb)
+ dev_kfree_skb_any(skb);
spin_lock(&dev->req_lock);
list_add (&req->list, &dev->rx_reqs);
spin_unlock(&dev->req_lock);
@@ -1766,7 +1770,6 @@ static void rx_complete (struct usb_ep *ep, struct usb_request *req)
break;
}
- skb->dev = dev->net;
skb->protocol = eth_type_trans (skb, dev->net);
dev->stats.rx_packets++;
dev->stats.rx_bytes += skb->len;
diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c
new file mode 100644
index 00000000000..157054ea397
--- /dev/null
+++ b/drivers/usb/gadget/fsl_usb2_udc.c
@@ -0,0 +1,2500 @@
+/*
+ * Copyright (C) 2004-2007 Freescale Semicondutor, Inc. All rights reserved.
+ *
+ * Author: Li Yang <leoli@freescale.com>
+ * Jiang Bo <tanya.jiang@freescale.com>
+ *
+ * Description:
+ * Freescale high-speed USB SOC DR module device controller driver.
+ * This can be found on MPC8349E/MPC8313E cpus.
+ * The driver is previously named as mpc_udc. Based on bare board
+ * code from Dave Liu and Shlomi Gridish.
+ *
+ * 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.
+ */
+
+#undef VERBOSE
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb_gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/dmapool.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/dma.h>
+#include <asm/cacheflush.h>
+
+#include "fsl_usb2_udc.h"
+
+#define DRIVER_DESC "Freescale High-Speed USB SOC Device Controller driver"
+#define DRIVER_AUTHOR "Li Yang/Jiang Bo"
+#define DRIVER_VERSION "Apr 20, 2007"
+
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+
+static const char driver_name[] = "fsl-usb2-udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+volatile static struct usb_dr_device *dr_regs = NULL;
+volatile static struct usb_sys_interface *usb_sys_regs = NULL;
+
+/* it is initialized in probe() */
+static struct fsl_udc *udc_controller = NULL;
+
+static const struct usb_endpoint_descriptor
+fsl_ep0_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+ .wMaxPacketSize = USB_MAX_CTRL_PAYLOAD,
+};
+
+static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state);
+static int fsl_udc_resume(struct platform_device *pdev);
+static void fsl_ep_fifo_flush(struct usb_ep *_ep);
+
+#ifdef CONFIG_PPC32
+#define fsl_readl(addr) in_le32(addr)
+#define fsl_writel(addr, val32) out_le32(val32, addr)
+#else
+#define fsl_readl(addr) readl(addr)
+#define fsl_writel(addr, val32) writel(addr, val32)
+#endif
+
+/********************************************************************
+ * Internal Used Function
+********************************************************************/
+/*-----------------------------------------------------------------
+ * done() - retire a request; caller blocked irqs
+ * @status : request status to be set, only works when
+ * request is still in progress.
+ *--------------------------------------------------------------*/
+static void done(struct fsl_ep *ep, struct fsl_req *req, int status)
+{
+ struct fsl_udc *udc = NULL;
+ unsigned char stopped = ep->stopped;
+ struct ep_td_struct *curr_td, *next_td;
+ int j;
+
+ udc = (struct fsl_udc *)ep->udc;
+ /* Removed the req from fsl_ep->queue */
+ list_del_init(&req->queue);
+
+ /* req.status should be set as -EINPROGRESS in ep_queue() */
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ /* Free dtd for the request */
+ next_td = req->head;
+ for (j = 0; j < req->dtd_count; j++) {
+ curr_td = next_td;
+ if (j != req->dtd_count - 1) {
+ next_td = curr_td->next_td_virt;
+ }
+ dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);
+ }
+
+ if (req->mapped) {
+ dma_unmap_single(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ep_is_in(ep)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ req->req.dma = DMA_ADDR_INVALID;
+ req->mapped = 0;
+ } else
+ dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ep_is_in(ep)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+
+ if (status && (status != -ESHUTDOWN))
+ VDBG("complete %s req %p stat %d len %u/%u",
+ ep->ep.name, &req->req, status,
+ req->req.actual, req->req.length);
+
+ ep->stopped = 1;
+
+ spin_unlock(&ep->udc->lock);
+ /* complete() is from gadget layer,
+ * eg fsg->bulk_in_complete() */
+ if (req->req.complete)
+ req->req.complete(&ep->ep, &req->req);
+
+ spin_lock(&ep->udc->lock);
+ ep->stopped = stopped;
+}
+
+/*-----------------------------------------------------------------
+ * nuke(): delete all requests related to this ep
+ * called with spinlock held
+ *--------------------------------------------------------------*/
+static void nuke(struct fsl_ep *ep, int status)
+{
+ ep->stopped = 1;
+
+ /* Flush fifo */
+ fsl_ep_fifo_flush(&ep->ep);
+
+ /* Whether this eq has request linked */
+ while (!list_empty(&ep->queue)) {
+ struct fsl_req *req = NULL;
+
+ req = list_entry(ep->queue.next, struct fsl_req, queue);
+ done(ep, req, status);
+ }
+}
+
+/*------------------------------------------------------------------
+ Internal Hardware related function
+ ------------------------------------------------------------------*/
+
+static int dr_controller_setup(struct fsl_udc *udc)
+{
+ unsigned int tmp = 0, portctrl = 0, ctrl = 0;
+ unsigned long timeout;
+#define FSL_UDC_RESET_TIMEOUT 1000
+
+ /* before here, make sure dr_regs has been initialized */
+ if (!udc)
+ return -EINVAL;
+
+ /* Stop and reset the usb controller */
+ tmp = fsl_readl(&dr_regs->usbcmd);
+ tmp &= ~USB_CMD_RUN_STOP;
+ fsl_writel(tmp, &dr_regs->usbcmd);
+
+ tmp = fsl_readl(&dr_regs->usbcmd);
+ tmp |= USB_CMD_CTRL_RESET;
+ fsl_writel(tmp, &dr_regs->usbcmd);
+
+ /* Wait for reset to complete */
+ timeout = jiffies + FSL_UDC_RESET_TIMEOUT;
+ while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) {
+ if (time_after(jiffies, timeout)) {
+ ERR("udc reset timeout! \n");
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
+ }
+
+ /* Set the controller as device mode */
+ tmp = fsl_readl(&dr_regs->usbmode);
+ tmp |= USB_MODE_CTRL_MODE_DEVICE;
+ /* Disable Setup Lockout */
+ tmp |= USB_MODE_SETUP_LOCK_OFF;
+ fsl_writel(tmp, &dr_regs->usbmode);
+
+ /* Clear the setup status */
+ fsl_writel(0, &dr_regs->usbsts);
+
+ tmp = udc->ep_qh_dma;
+ tmp &= USB_EP_LIST_ADDRESS_MASK;
+ fsl_writel(tmp, &dr_regs->endpointlistaddr);
+
+ VDBG("vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x",
+ (int)udc->ep_qh, (int)tmp,
+ fsl_readl(&dr_regs->endpointlistaddr));
+
+ /* Config PHY interface */
+ portctrl = fsl_readl(&dr_regs->portsc1);
+ portctrl &= ~PORTSCX_PHY_TYPE_SEL;
+ switch (udc->phy_mode) {
+ case FSL_USB2_PHY_ULPI:
+ portctrl |= PORTSCX_PTS_ULPI;
+ break;
+ case FSL_USB2_PHY_UTMI:
+ case FSL_USB2_PHY_UTMI_WIDE:
+ portctrl |= PORTSCX_PTS_UTMI;
+ break;
+ case FSL_USB2_PHY_SERIAL:
+ portctrl |= PORTSCX_PTS_FSLS;
+ break;
+ default:
+ return -EINVAL;
+ }
+ fsl_writel(portctrl, &dr_regs->portsc1);
+
+ /* Config control enable i/o output, cpu endian register */
+ ctrl = __raw_readl(&usb_sys_regs->control);
+ ctrl |= USB_CTRL_IOENB;
+ __raw_writel(ctrl, &usb_sys_regs->control);
+
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+ /* Turn on cache snooping hardware, since some PowerPC platforms
+ * wholly rely on hardware to deal with cache coherent. */
+
+ /* Setup Snooping for all the 4GB space */
+ tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */
+ __raw_writel(tmp, &usb_sys_regs->snoop1);
+ tmp |= 0x80000000; /* starts from 0x8000000, size 2G */
+ __raw_writel(tmp, &usb_sys_regs->snoop2);
+#endif
+
+ return 0;
+}
+
+/* Enable DR irq and set controller to run state */
+static void dr_controller_run(struct fsl_udc *udc)
+{
+ u32 temp;
+
+ /* Enable DR irq reg */
+ temp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN
+ | USB_INTR_PTC_DETECT_EN | USB_INTR_RESET_EN
+ | USB_INTR_DEVICE_SUSPEND | USB_INTR_SYS_ERR_EN;
+
+ fsl_writel(temp, &dr_regs->usbintr);
+
+ /* Clear stopped bit */
+ udc->stopped = 0;
+
+ /* Set the controller as device mode */
+ temp = fsl_readl(&dr_regs->usbmode);
+ temp |= USB_MODE_CTRL_MODE_DEVICE;
+ fsl_writel(temp, &dr_regs->usbmode);
+
+ /* Set controller to Run */
+ temp = fsl_readl(&dr_regs->usbcmd);
+ temp |= USB_CMD_RUN_STOP;
+ fsl_writel(temp, &dr_regs->usbcmd);
+
+ return;
+}
+
+static void dr_controller_stop(struct fsl_udc *udc)
+{
+ unsigned int tmp;
+
+ /* disable all INTR */
+ fsl_writel(0, &dr_regs->usbintr);
+
+ /* Set stopped bit for isr */
+ udc->stopped = 1;
+
+ /* disable IO output */
+/* usb_sys_regs->control = 0; */
+
+ /* set controller to Stop */
+ tmp = fsl_readl(&dr_regs->usbcmd);
+ tmp &= ~USB_CMD_RUN_STOP;
+ fsl_writel(tmp, &dr_regs->usbcmd);
+
+ return;
+}
+
+void dr_ep_setup(unsigned char ep_num, unsigned char dir, unsigned char ep_type)
+{
+ unsigned int tmp_epctrl = 0;
+
+ tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+ if (dir) {
+ if (ep_num)
+ tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
+ tmp_epctrl |= EPCTRL_TX_ENABLE;
+ tmp_epctrl |= ((unsigned int)(ep_type)
+ << EPCTRL_TX_EP_TYPE_SHIFT);
+ } else {
+ if (ep_num)
+ tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
+ tmp_epctrl |= EPCTRL_RX_ENABLE;
+ tmp_epctrl |= ((unsigned int)(ep_type)
+ << EPCTRL_RX_EP_TYPE_SHIFT);
+ }
+
+ fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);
+}
+
+static void
+dr_ep_change_stall(unsigned char ep_num, unsigned char dir, int value)
+{
+ u32 tmp_epctrl = 0;
+
+ tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+
+ if (value) {
+ /* set the stall bit */
+ if (dir)
+ tmp_epctrl |= EPCTRL_TX_EP_STALL;
+ else
+ tmp_epctrl |= EPCTRL_RX_EP_STALL;
+ } else {
+ /* clear the stall bit and reset data toggle */
+ if (dir) {
+ tmp_epctrl &= ~EPCTRL_TX_EP_STALL;
+ tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
+ } else {
+ tmp_epctrl &= ~EPCTRL_RX_EP_STALL;
+ tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
+ }
+ }
+ fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);
+}
+
+/* Get stall status of a specific ep
+ Return: 0: not stalled; 1:stalled */
+static int dr_ep_get_stall(unsigned char ep_num, unsigned char dir)
+{
+ u32 epctrl;
+
+ epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+ if (dir)
+ return (epctrl & EPCTRL_TX_EP_STALL) ? 1 : 0;
+ else
+ return (epctrl & EPCTRL_RX_EP_STALL) ? 1 : 0;
+}
+
+/********************************************************************
+ Internal Structure Build up functions
+********************************************************************/
+
+/*------------------------------------------------------------------
+* struct_ep_qh_setup(): set the Endpoint Capabilites field of QH
+ * @zlt: Zero Length Termination Select (1: disable; 0: enable)
+ * @mult: Mult field
+ ------------------------------------------------------------------*/
+static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num,
+ unsigned char dir, unsigned char ep_type,
+ unsigned int max_pkt_len,
+ unsigned int zlt, unsigned char mult)
+{
+ struct ep_queue_head *p_QH = &udc->ep_qh[2 * ep_num + dir];
+ unsigned int tmp = 0;
+
+ /* set the Endpoint Capabilites in QH */
+ switch (ep_type) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ /* Interrupt On Setup (IOS). for control ep */
+ tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
+ | EP_QUEUE_HEAD_IOS;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
+ | (mult << EP_QUEUE_HEAD_MULT_POS);
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+ tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS;
+ break;
+ default:
+ VDBG("error ep type is %d", ep_type);
+ return;
+ }
+ if (zlt)
+ tmp |= EP_QUEUE_HEAD_ZLT_SEL;
+ p_QH->max_pkt_length = cpu_to_le32(tmp);
+
+ return;
+}
+
+/* Setup qh structure and ep register for ep0. */
+static void ep0_setup(struct fsl_udc *udc)
+{
+ /* the intialization of an ep includes: fields in QH, Regs,
+ * fsl_ep struct */
+ struct_ep_qh_setup(udc, 0, USB_RECV, USB_ENDPOINT_XFER_CONTROL,
+ USB_MAX_CTRL_PAYLOAD, 0, 0);
+ struct_ep_qh_setup(udc, 0, USB_SEND, USB_ENDPOINT_XFER_CONTROL,
+ USB_MAX_CTRL_PAYLOAD, 0, 0);
+ dr_ep_setup(0, USB_RECV, USB_ENDPOINT_XFER_CONTROL);
+ dr_ep_setup(0, USB_SEND, USB_ENDPOINT_XFER_CONTROL);
+
+ return;
+
+}
+
+/***********************************************************************
+ Endpoint Management Functions
+***********************************************************************/
+
+/*-------------------------------------------------------------------------
+ * when configurations are set, or when interface settings change
+ * for example the do_set_interface() in gadget layer,
+ * the driver will enable or disable the relevant endpoints
+ * ep0 doesn't use this routine. It is always enabled.
+-------------------------------------------------------------------------*/
+static int fsl_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct fsl_udc *udc = NULL;
+ struct fsl_ep *ep = NULL;
+ unsigned short max = 0;
+ unsigned char mult = 0, zlt;
+ int retval = -EINVAL;
+ unsigned long flags = 0;
+
+ ep = container_of(_ep, struct fsl_ep, ep);
+
+ /* catch various bogus parameters */
+ if (!_ep || !desc || ep->desc
+ || (desc->bDescriptorType != USB_DT_ENDPOINT))
+ return -EINVAL;
+
+ udc = ep->udc;
+
+ if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN))
+ return -ESHUTDOWN;
+
+ max = le16_to_cpu(desc->wMaxPacketSize);
+
+ /* Disable automatic zlp generation. Driver is reponsible to indicate
+ * explicitly through req->req.zero. This is needed to enable multi-td
+ * request. */
+ zlt = 1;
+
+ /* Assume the max packet size from gadget is always correct */
+ switch (desc->bmAttributes & 0x03) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+ /* mult = 0. Execute N Transactions as demonstrated by
+ * the USB variable length packet protocol where N is
+ * computed using the Maximum Packet Length (dQH) and
+ * the Total Bytes field (dTD) */
+ mult = 0;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ /* Calculate transactions needed for high bandwidth iso */
+ mult = (unsigned char)(1 + ((max >> 11) & 0x03));
+ max = max & 0x8ff; /* bit 0~10 */
+ /* 3 transactions at most */
+ if (mult > 3)
+ goto en_done;
+ break;
+ default:
+ goto en_done;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+ ep->ep.maxpacket = max;
+ ep->desc = desc;
+ ep->stopped = 0;
+
+ /* Controller related setup */
+ /* Init EPx Queue Head (Ep Capabilites field in QH
+ * according to max, zlt, mult) */
+ struct_ep_qh_setup(udc, (unsigned char) ep_index(ep),
+ (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN)
+ ? USB_SEND : USB_RECV),
+ (unsigned char) (desc->bmAttributes
+ & USB_ENDPOINT_XFERTYPE_MASK),
+ max, zlt, mult);
+
+ /* Init endpoint ctrl register */
+ dr_ep_setup((unsigned char) ep_index(ep),
+ (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN)
+ ? USB_SEND : USB_RECV),
+ (unsigned char) (desc->bmAttributes
+ & USB_ENDPOINT_XFERTYPE_MASK));
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ retval = 0;
+
+ VDBG("enabled %s (ep%d%s) maxpacket %d",ep->ep.name,
+ ep->desc->bEndpointAddress & 0x0f,
+ (desc->bEndpointAddress & USB_DIR_IN)
+ ? "in" : "out", max);
+en_done:
+ return retval;
+}
+
+/*---------------------------------------------------------------------
+ * @ep : the ep being unconfigured. May not be ep0
+ * Any pending and uncomplete req will complete with status (-ESHUTDOWN)
+*---------------------------------------------------------------------*/
+static int fsl_ep_disable(struct usb_ep *_ep)
+{
+ struct fsl_udc *udc = NULL;
+ struct fsl_ep *ep = NULL;
+ unsigned long flags = 0;
+ u32 epctrl;
+ int ep_num;
+
+ ep = container_of(_ep, struct fsl_ep, ep);
+ if (!_ep || !ep->desc) {
+ VDBG("%s not enabled", _ep ? ep->ep.name : NULL);
+ return -EINVAL;
+ }
+
+ /* disable ep on controller */
+ ep_num = ep_index(ep);
+ epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+ if (ep_is_in(ep))
+ epctrl &= ~EPCTRL_TX_ENABLE;
+ else
+ epctrl &= ~EPCTRL_RX_ENABLE;
+ fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+
+ udc = (struct fsl_udc *)ep->udc;
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* nuke all pending requests (does flush) */
+ nuke(ep, -ESHUTDOWN);
+
+ ep->desc = 0;
+ ep->stopped = 1;
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ VDBG("disabled %s OK", _ep->name);
+ return 0;
+}
+
+/*---------------------------------------------------------------------
+ * allocate a request object used by this endpoint
+ * the main operation is to insert the req->queue to the eq->queue
+ * Returns the request, or null if one could not be allocated
+*---------------------------------------------------------------------*/
+static struct usb_request *
+fsl_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+ struct fsl_req *req = NULL;
+
+ req = kzalloc(sizeof *req, gfp_flags);
+ if (!req)
+ return NULL;
+
+ req->req.dma = DMA_ADDR_INVALID;
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->req;
+}
+
+static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct fsl_req *req = NULL;
+
+ req = container_of(_req, struct fsl_req, req);
+
+ if (_req)
+ kfree(req);
+}
+
+/*------------------------------------------------------------------
+ * Allocate an I/O buffer
+*---------------------------------------------------------------------*/
+static void *fsl_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
+ dma_addr_t *dma, gfp_t gfp_flags)
+{
+ struct fsl_ep *ep;
+
+ if (!_ep)
+ return NULL;
+
+ ep = container_of(_ep, struct fsl_ep, ep);
+
+ return dma_alloc_coherent(ep->udc->gadget.dev.parent,
+ bytes, dma, gfp_flags);
+}
+
+/*------------------------------------------------------------------
+ * frees an i/o buffer
+*---------------------------------------------------------------------*/
+static void fsl_free_buffer(struct usb_ep *_ep, void *buf,
+ dma_addr_t dma, unsigned bytes)
+{
+ struct fsl_ep *ep;
+
+ if (!_ep)
+ return NULL;
+
+ ep = container_of(_ep, struct fsl_ep, ep);
+
+ dma_free_coherent(ep->udc->gadget.dev.parent, bytes, buf, dma);
+}
+
+/*-------------------------------------------------------------------------*/
+static int fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
+{
+ int i = ep_index(ep) * 2 + ep_is_in(ep);
+ u32 temp, bitmask, tmp_stat;
+ struct ep_queue_head *dQH = &ep->udc->ep_qh[i];
+
+ /* VDBG("QH addr Register 0x%8x", dr_regs->endpointlistaddr);
+ VDBG("ep_qh[%d] addr is 0x%8x", i, (u32)&(ep->udc->ep_qh[i])); */
+
+ bitmask = ep_is_in(ep)
+ ? (1 << (ep_index(ep) + 16))
+ : (1 << (ep_index(ep)));
+
+ /* check if the pipe is empty */
+ if (!(list_empty(&ep->queue))) {
+ /* Add td to the end */
+ struct fsl_req *lastreq;
+ lastreq = list_entry(ep->queue.prev, struct fsl_req, queue);
+ lastreq->tail->next_td_ptr =
+ cpu_to_le32(req->head->td_dma & DTD_ADDR_MASK);
+ /* Read prime bit, if 1 goto done */
+ if (fsl_readl(&dr_regs->endpointprime) & bitmask)
+ goto out;
+
+ do {
+ /* Set ATDTW bit in USBCMD */
+ temp = fsl_readl(&dr_regs->usbcmd);
+ fsl_writel(temp | USB_CMD_ATDTW, &dr_regs->usbcmd);
+
+ /* Read correct status bit */
+ tmp_stat = fsl_readl(&dr_regs->endptstatus) & bitmask;
+
+ } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_ATDTW));
+
+ /* Write ATDTW bit to 0 */
+ temp = fsl_readl(&dr_regs->usbcmd);
+ fsl_writel(temp & ~USB_CMD_ATDTW, &dr_regs->usbcmd);
+
+ if (tmp_stat)
+ goto out;
+ }
+
+ /* Write dQH next pointer and terminate bit to 0 */
+ temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
+ dQH->next_dtd_ptr = cpu_to_le32(temp);
+
+ /* Clear active and halt bit */
+ temp = cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE
+ | EP_QUEUE_HEAD_STATUS_HALT));
+ dQH->size_ioc_int_sts &= temp;
+
+ /* Prime endpoint by writing 1 to ENDPTPRIME */
+ temp = ep_is_in(ep)
+ ? (1 << (ep_index(ep) + 16))
+ : (1 << (ep_index(ep)));
+ fsl_writel(temp, &dr_regs->endpointprime);
+out:
+ return 0;
+}
+
+/* Fill in the dTD structure
+ * @req: request that the transfer belongs to
+ * @length: return actually data length of the dTD
+ * @dma: return dma address of the dTD
+ * @is_last: return flag if it is the last dTD of the request
+ * return: pointer to the built dTD */
+static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,
+ dma_addr_t *dma, int *is_last)
+{
+ u32 swap_temp;
+ struct ep_td_struct *dtd;
+
+ /* how big will this transfer be? */
+ *length = min(req->req.length - req->req.actual,
+ (unsigned)EP_MAX_LENGTH_TRANSFER);
+
+ dtd = dma_pool_alloc(udc_controller->td_pool, GFP_KERNEL, dma);
+ if (dtd == NULL)
+ return dtd;
+
+ dtd->td_dma = *dma;
+ /* Clear reserved field */
+ swap_temp = cpu_to_le32(dtd->size_ioc_sts);
+ swap_temp &= ~DTD_RESERVED_FIELDS;
+ dtd->size_ioc_sts = cpu_to_le32(swap_temp);
+
+ /* Init all of buffer page pointers */
+ swap_temp = (u32) (req->req.dma + req->req.actual);
+ dtd->buff_ptr0 = cpu_to_le32(swap_temp);
+ dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000);
+ dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000);
+ dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000);
+ dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000);
+
+ req->req.actual += *length;
+
+ /* zlp is needed if req->req.zero is set */
+ if (req->req.zero) {
+ if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0)
+ *is_last = 1;
+ else
+ *is_last = 0;
+ } else if (req->req.length == req->req.actual)
+ *is_last = 1;
+ else
+ *is_last = 0;
+
+ if ((*is_last) == 0)
+ VDBG("multi-dtd request!\n");
+ /* Fill in the transfer size; set active bit */
+ swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE);
+
+ /* Enable interrupt for the last dtd of a request */
+ if (*is_last && !req->req.no_interrupt)
+ swap_temp |= DTD_IOC;
+
+ dtd->size_ioc_sts = cpu_to_le32(swap_temp);
+
+ mb();
+
+ VDBG("length = %d address= 0x%x", *length, (int)*dma);
+
+ return dtd;
+}
+
+/* Generate dtd chain for a request */
+static int fsl_req_to_dtd(struct fsl_req *req)
+{
+ unsigned count;
+ int is_last;
+ int is_first =1;
+ struct ep_td_struct *last_dtd = NULL, *dtd;
+ dma_addr_t dma;
+
+ do {
+ dtd = fsl_build_dtd(req, &count, &dma, &is_last);
+ if (dtd == NULL)
+ return -ENOMEM;
+
+ if (is_first) {
+ is_first = 0;
+ req->head = dtd;
+ } else {
+ last_dtd->next_td_ptr = cpu_to_le32(dma);
+ last_dtd->next_td_virt = dtd;
+ }
+ last_dtd = dtd;
+
+ req->dtd_count++;
+ } while (!is_last);
+
+ dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE);
+
+ req->tail = dtd;
+
+ return 0;
+}
+
+/* queues (submits) an I/O request to an endpoint */
+static int
+fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+ struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
+ struct fsl_req *req = container_of(_req, struct fsl_req, req);
+ struct fsl_udc *udc;
+ unsigned long flags;
+ int is_iso = 0;
+
+ /* catch various bogus parameters */
+ if (!_req || !req->req.complete || !req->req.buf
+ || !list_empty(&req->queue)) {
+ VDBG("%s, bad params\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ if (!_ep || (!ep->desc && ep_index(ep))) {
+ VDBG("%s, bad ep\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+ if (req->req.length > ep->ep.maxpacket)
+ return -EMSGSIZE;
+ is_iso = 1;
+ }
+
+ udc = ep->udc;
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ req->ep = ep;
+
+ /* map virtual address to hardware */
+ if (req->req.dma == DMA_ADDR_INVALID) {
+ req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
+ req->req.buf,
+ req->req.length, ep_is_in(ep)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ req->mapped = 1;
+ } else {
+ dma_sync_single_for_device(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ep_is_in(ep)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ req->mapped = 0;
+ }
+
+ req->req.status = -EINPROGRESS;
+ req->req.actual = 0;
+ req->dtd_count = 0;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* build dtds and push them to device queue */
+ if (!fsl_req_to_dtd(req)) {
+ fsl_queue_td(ep, req);
+ } else {
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return -ENOMEM;
+ }
+
+ /* Update ep0 state */
+ if ((ep_index(ep) == 0))
+ udc->ep0_state = DATA_STATE_XMIT;
+
+ /* irq handler advances the queue */
+ if (req != NULL)
+ list_add_tail(&req->queue, &ep->queue);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/* dequeues (cancels, unlinks) an I/O request from an endpoint */
+static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
+ struct fsl_req *req;
+ unsigned long flags;
+ int ep_num, stopped, ret = 0;
+ u32 epctrl;
+
+ if (!_ep || !_req)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->udc->lock, flags);
+ stopped = ep->stopped;
+
+ /* Stop the ep before we deal with the queue */
+ ep->stopped = 1;
+ ep_num = ep_index(ep);
+ epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+ if (ep_is_in(ep))
+ epctrl &= ~EPCTRL_TX_ENABLE;
+ else
+ epctrl &= ~EPCTRL_RX_ENABLE;
+ fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+
+ /* make sure it's actually queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->req == _req)
+ break;
+ }
+ if (&req->req != _req) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* The request is in progress, or completed but not dequeued */
+ if (ep->queue.next == &req->queue) {
+ _req->status = -ECONNRESET;
+ fsl_ep_fifo_flush(_ep); /* flush current transfer */
+
+ /* The request isn't the last request in this ep queue */
+ if (req->queue.next != &ep->queue) {
+ struct ep_queue_head *qh;
+ struct fsl_req *next_req;
+
+ qh = ep->qh;
+ next_req = list_entry(req->queue.next, struct fsl_req,
+ queue);
+
+ /* Point the QH to the first TD of next request */
+ fsl_writel((u32) next_req->head, &qh->curr_dtd_ptr);
+ }
+
+ /* The request hasn't been processed, patch up the TD chain */
+ } else {
+ struct fsl_req *prev_req;
+
+ prev_req = list_entry(req->queue.prev, struct fsl_req, queue);
+ fsl_writel(fsl_readl(&req->tail->next_td_ptr),
+ &prev_req->tail->next_td_ptr);
+
+ }
+
+ done(ep, req, -ECONNRESET);
+
+ /* Enable EP */
+out: epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+ if (ep_is_in(ep))
+ epctrl |= EPCTRL_TX_ENABLE;
+ else
+ epctrl |= EPCTRL_RX_ENABLE;
+ fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+ ep->stopped = stopped;
+
+ spin_unlock_irqrestore(&ep->udc->lock, flags);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------
+ * modify the endpoint halt feature
+ * @ep: the non-isochronous endpoint being stalled
+ * @value: 1--set halt 0--clear halt
+ * Returns zero, or a negative error code.
+*----------------------------------------------------------------*/
+static int fsl_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ struct fsl_ep *ep = NULL;
+ unsigned long flags = 0;
+ int status = -EOPNOTSUPP; /* operation not supported */
+ unsigned char ep_dir = 0, ep_num = 0;
+ struct fsl_udc *udc = NULL;
+
+ ep = container_of(_ep, struct fsl_ep, ep);
+ udc = ep->udc;
+ if (!_ep || !ep->desc) {
+ status = -EINVAL;
+ goto out;
+ }
+
+ if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+ status = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* Attempt to halt IN ep will fail if any transfer requests
+ * are still queue */
+ if (value && ep_is_in(ep) && !list_empty(&ep->queue)) {
+ status = -EAGAIN;
+ goto out;
+ }
+
+ status = 0;
+ ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV;
+ ep_num = (unsigned char)(ep_index(ep));
+ spin_lock_irqsave(&ep->udc->lock, flags);
+ dr_ep_change_stall(ep_num, ep_dir, value);
+ spin_unlock_irqrestore(&ep->udc->lock, flags);
+
+ if (ep_index(ep) == 0) {
+ udc->ep0_state = WAIT_FOR_SETUP;
+ udc->ep0_dir = 0;
+ }
+out:
+ VDBG(" %s %s halt stat %d", ep->ep.name,
+ value ? "set" : "clear", status);
+
+ return status;
+}
+
+static void fsl_ep_fifo_flush(struct usb_ep *_ep)
+{
+ struct fsl_ep *ep;
+ int ep_num, ep_dir;
+ u32 bits;
+ unsigned long timeout;
+#define FSL_UDC_FLUSH_TIMEOUT 1000
+
+ if (!_ep) {
+ return;
+ } else {
+ ep = container_of(_ep, struct fsl_ep, ep);
+ if (!ep->desc)
+ return;
+ }
+ ep_num = ep_index(ep);
+ ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV;
+
+ if (ep_num == 0)
+ bits = (1 << 16) | 1;
+ else if (ep_dir == USB_SEND)
+ bits = 1 << (16 + ep_num);
+ else
+ bits = 1 << ep_num;
+
+ timeout = jiffies + FSL_UDC_FLUSH_TIMEOUT;
+ do {
+ fsl_writel(bits, &dr_regs->endptflush);
+
+ /* Wait until flush complete */
+ while (fsl_readl(&dr_regs->endptflush)) {
+ if (time_after(jiffies, timeout)) {
+ ERR("ep flush timeout\n");
+ return;
+ }
+ cpu_relax();
+ }
+ /* See if we need to flush again */
+ } while (fsl_readl(&dr_regs->endptstatus) & bits);
+}
+
+static struct usb_ep_ops fsl_ep_ops = {
+ .enable = fsl_ep_enable,
+ .disable = fsl_ep_disable,
+
+ .alloc_request = fsl_alloc_request,
+ .free_request = fsl_free_request,
+
+ .alloc_buffer = fsl_alloc_buffer,
+ .free_buffer = fsl_free_buffer,
+
+ .queue = fsl_ep_queue,
+ .dequeue = fsl_ep_dequeue,
+
+ .set_halt = fsl_ep_set_halt,
+ .fifo_flush = fsl_ep_fifo_flush, /* flush fifo */
+};
+
+/*-------------------------------------------------------------------------
+ Gadget Driver Layer Operations
+-------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------
+ * Get the current frame number (from DR frame_index Reg )
+ *----------------------------------------------------------------------*/
+static int fsl_get_frame(struct usb_gadget *gadget)
+{
+ return (int)(fsl_readl(&dr_regs->frindex) & USB_FRINDEX_MASKS);
+}
+
+/*-----------------------------------------------------------------------
+ * Tries to wake up the host connected to this gadget
+ -----------------------------------------------------------------------*/
+static int fsl_wakeup(struct usb_gadget *gadget)
+{
+ struct fsl_udc *udc = container_of(gadget, struct fsl_udc, gadget);
+ u32 portsc;
+
+ /* Remote wakeup feature not enabled by host */
+ if (!udc->remote_wakeup)
+ return -ENOTSUPP;
+
+ portsc = fsl_readl(&dr_regs->portsc1);
+ /* not suspended? */
+ if (!(portsc & PORTSCX_PORT_SUSPEND))
+ return 0;
+ /* trigger force resume */
+ portsc |= PORTSCX_PORT_FORCE_RESUME;
+ fsl_writel(portsc, &dr_regs->portsc1);
+ return 0;
+}
+
+static int can_pullup(struct fsl_udc *udc)
+{
+ return udc->driver && udc->softconnect && udc->vbus_active;
+}
+
+/* Notify controller that VBUS is powered, Called by whatever
+ detects VBUS sessions */
+static int fsl_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+ struct fsl_udc *udc;
+ unsigned long flags;
+
+ udc = container_of(gadget, struct fsl_udc, gadget);
+ spin_lock_irqsave(&udc->lock, flags);
+ VDBG("VBUS %s\n", is_active ? "on" : "off");
+ udc->vbus_active = (is_active != 0);
+ if (can_pullup(udc))
+ fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
+ &dr_regs->usbcmd);
+ else
+ fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP),
+ &dr_regs->usbcmd);
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+/* constrain controller's VBUS power usage
+ * This call is used by gadget drivers during SET_CONFIGURATION calls,
+ * reporting how much power the device may consume. For example, this
+ * could affect how quickly batteries are recharged.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+#ifdef CONFIG_USB_OTG
+ struct fsl_udc *udc;
+
+ udc = container_of(gadget, struct fsl_udc, gadget);
+
+ if (udc->transceiver)
+ return otg_set_power(udc->transceiver, mA);
+#endif
+ return -ENOTSUPP;
+}
+
+/* Change Data+ pullup status
+ * this func is used by usb_gadget_connect/disconnet
+ */
+static int fsl_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct fsl_udc *udc;
+
+ udc = container_of(gadget, struct fsl_udc, gadget);
+ udc->softconnect = (is_on != 0);
+ if (can_pullup(udc))
+ fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
+ &dr_regs->usbcmd);
+ else
+ fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP),
+ &dr_regs->usbcmd);
+
+ return 0;
+}
+
+/* defined in usb_gadget.h */
+static struct usb_gadget_ops fsl_gadget_ops = {
+ .get_frame = fsl_get_frame,
+ .wakeup = fsl_wakeup,
+/* .set_selfpowered = fsl_set_selfpowered, */ /* Always selfpowered */
+ .vbus_session = fsl_vbus_session,
+ .vbus_draw = fsl_vbus_draw,
+ .pullup = fsl_pullup,
+};
+
+/* Set protocol stall on ep0, protocol stall will automatically be cleared
+ on new transaction */
+static void ep0stall(struct fsl_udc *udc)
+{
+ u32 tmp;
+
+ /* must set tx and rx to stall at the same time */
+ tmp = fsl_readl(&dr_regs->endptctrl[0]);
+ tmp |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL;
+ fsl_writel(tmp, &dr_regs->endptctrl[0]);
+ udc->ep0_state = WAIT_FOR_SETUP;
+ udc->ep0_dir = 0;
+}
+
+/* Prime a status phase for ep0 */
+static int ep0_prime_status(struct fsl_udc *udc, int direction)
+{
+ struct fsl_req *req = udc->status_req;
+ struct fsl_ep *ep;
+ int status = 0;
+
+ if (direction == EP_DIR_IN)
+ udc->ep0_dir = USB_DIR_IN;
+ else
+ udc->ep0_dir = USB_DIR_OUT;
+
+ ep = &udc->eps[0];
+ udc->ep0_state = WAIT_FOR_OUT_STATUS;
+
+ req->ep = ep;
+ req->req.length = 0;
+ req->req.status = -EINPROGRESS;
+ req->req.actual = 0;
+ req->req.complete = NULL;
+ req->dtd_count = 0;
+
+ if (fsl_req_to_dtd(req) == 0)
+ status = fsl_queue_td(ep, req);
+ else
+ return -ENOMEM;
+
+ if (status)
+ ERR("Can't queue ep0 status request \n");
+ list_add_tail(&req->queue, &ep->queue);
+
+ return status;
+}
+
+static inline int udc_reset_ep_queue(struct fsl_udc *udc, u8 pipe)
+{
+ struct fsl_ep *ep = get_ep_by_pipe(udc, pipe);
+
+ if (!ep->name)
+ return 0;
+
+ nuke(ep, -ESHUTDOWN);
+
+ return 0;
+}
+
+/*
+ * ch9 Set address
+ */
+static void ch9setaddress(struct fsl_udc *udc, u16 value, u16 index, u16 length)
+{
+ /* Save the new address to device struct */
+ udc->device_address = (u8) value;
+ /* Update usb state */
+ udc->usb_state = USB_STATE_ADDRESS;
+ /* Status phase */
+ if (ep0_prime_status(udc, EP_DIR_IN))
+ ep0stall(udc);
+}
+
+/*
+ * ch9 Get status
+ */
+static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
+ u16 index, u16 length)
+{
+ u16 tmp = 0; /* Status, cpu endian */
+
+ struct fsl_req *req;
+ struct fsl_ep *ep;
+ int status = 0;
+
+ ep = &udc->eps[0];
+
+ if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
+ /* Get device status */
+ tmp = 1 << USB_DEVICE_SELF_POWERED;
+ tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
+ } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
+ /* Get interface status */
+ /* We don't have interface information in udc driver */
+ tmp = 0;
+ } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
+ /* Get endpoint status */
+ struct fsl_ep *target_ep;
+
+ target_ep = get_ep_by_pipe(udc, get_pipe_by_windex(index));
+
+ /* stall if endpoint doesn't exist */
+ if (!target_ep->desc)
+ goto stall;
+ tmp = dr_ep_get_stall(ep_index(target_ep), ep_is_in(target_ep))
+ << USB_ENDPOINT_HALT;
+ }
+
+ udc->ep0_dir = USB_DIR_IN;
+ /* Borrow the per device status_req */
+ req = udc->status_req;
+ /* Fill in the reqest structure */
+ *((u16 *) req->req.buf) = cpu_to_le16(tmp);
+ req->ep = ep;
+ req->req.length = 2;
+ req->req.status = -EINPROGRESS;
+ req->req.actual = 0;
+ req->req.complete = NULL;
+ req->dtd_count = 0;
+
+ /* prime the data phase */
+ if ((fsl_req_to_dtd(req) == 0))
+ status = fsl_queue_td(ep, req);
+ else /* no mem */
+ goto stall;
+
+ if (status) {
+ ERR("Can't respond to getstatus request \n");
+ goto stall;
+ }
+ list_add_tail(&req->queue, &ep->queue);
+ udc->ep0_state = DATA_STATE_XMIT;
+ return;
+stall:
+ ep0stall(udc);
+}
+
+static void setup_received_irq(struct fsl_udc *udc,
+ struct usb_ctrlrequest *setup)
+{
+ u16 wValue = le16_to_cpu(setup->wValue);
+ u16 wIndex = le16_to_cpu(setup->wIndex);
+ u16 wLength = le16_to_cpu(setup->wLength);
+
+ udc_reset_ep_queue(udc, 0);
+
+ switch (setup->bRequest) {
+ /* Request that need Data+Status phase from udc */
+ case USB_REQ_GET_STATUS:
+ if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_STANDARD))
+ != (USB_DIR_IN | USB_TYPE_STANDARD))
+ break;
+ ch9getstatus(udc, setup->bRequestType, wValue, wIndex, wLength);
+ break;
+
+ /* Requests that need Status phase from udc */
+ case USB_REQ_SET_ADDRESS:
+ if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD
+ | USB_RECIP_DEVICE))
+ break;
+ ch9setaddress(udc, wValue, wIndex, wLength);
+ break;
+
+ /* Handled by udc, no data, status by udc */
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ { /* status transaction */
+ int rc = -EOPNOTSUPP;
+
+ if ((setup->bRequestType & USB_RECIP_MASK)
+ == USB_RECIP_ENDPOINT) {
+ int pipe = get_pipe_by_windex(wIndex);
+ struct fsl_ep *ep;
+
+ if (wValue != 0 || wLength != 0 || pipe > udc->max_ep)
+ break;
+ ep = get_ep_by_pipe(udc, pipe);
+
+ spin_unlock(&udc->lock);
+ rc = fsl_ep_set_halt(&ep->ep,
+ (setup->bRequest == USB_REQ_SET_FEATURE)
+ ? 1 : 0);
+ spin_lock(&udc->lock);
+
+ } else if ((setup->bRequestType & USB_RECIP_MASK)
+ == USB_RECIP_DEVICE) {
+ /* Note: The driver has not include OTG support yet.
+ * This will be set when OTG support is added */
+ if (!udc->gadget.is_otg)
+ break;
+ else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE)
+ udc->gadget.b_hnp_enable = 1;
+ else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT)
+ udc->gadget.a_hnp_support = 1;
+ else if (setup->bRequest ==
+ USB_DEVICE_A_ALT_HNP_SUPPORT)
+ udc->gadget.a_alt_hnp_support = 1;
+ rc = 0;
+ }
+ if (rc == 0) {
+ if (ep0_prime_status(udc, EP_DIR_IN))
+ ep0stall(udc);
+ }
+ break;
+ }
+ /* Requests handled by gadget */
+ default:
+ if (wLength) {
+ /* Data phase from gadget, status phase from udc */
+ udc->ep0_dir = (setup->bRequestType & USB_DIR_IN)
+ ? USB_DIR_IN : USB_DIR_OUT;
+ spin_unlock(&udc->lock);
+ if (udc->driver->setup(&udc->gadget,
+ &udc->local_setup_buff) < 0)
+ ep0stall(udc);
+ spin_lock(&udc->lock);
+ udc->ep0_state = (setup->bRequestType & USB_DIR_IN)
+ ? DATA_STATE_XMIT : DATA_STATE_RECV;
+
+ } else {
+ /* No data phase, IN status from gadget */
+ udc->ep0_dir = USB_DIR_IN;
+ spin_unlock(&udc->lock);
+ if (udc->driver->setup(&udc->gadget,
+ &udc->local_setup_buff) < 0)
+ ep0stall(udc);
+ spin_lock(&udc->lock);
+ udc->ep0_state = WAIT_FOR_OUT_STATUS;
+ }
+ break;
+ }
+}
+
+/* Process request for Data or Status phase of ep0
+ * prime status phase if needed */
+static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0,
+ struct fsl_req *req)
+{
+ if (udc->usb_state == USB_STATE_ADDRESS) {
+ /* Set the new address */
+ u32 new_address = (u32) udc->device_address;
+ fsl_writel(new_address << USB_DEVICE_ADDRESS_BIT_POS,
+ &dr_regs->deviceaddr);
+ }
+
+ done(ep0, req, 0);
+
+ switch (udc->ep0_state) {
+ case DATA_STATE_XMIT:
+ /* receive status phase */
+ if (ep0_prime_status(udc, EP_DIR_OUT))
+ ep0stall(udc);
+ break;
+ case DATA_STATE_RECV:
+ /* send status phase */
+ if (ep0_prime_status(udc, EP_DIR_IN))
+ ep0stall(udc);
+ break;
+ case WAIT_FOR_OUT_STATUS:
+ udc->ep0_state = WAIT_FOR_SETUP;
+ break;
+ case WAIT_FOR_SETUP:
+ ERR("Unexpect ep0 packets \n");
+ break;
+ default:
+ ep0stall(udc);
+ break;
+ }
+}
+
+/* Tripwire mechanism to ensure a setup packet payload is extracted without
+ * being corrupted by another incoming setup packet */
+static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr)
+{
+ u32 temp;
+ struct ep_queue_head *qh;
+
+ qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT];
+
+ /* Clear bit in ENDPTSETUPSTAT */
+ temp = fsl_readl(&dr_regs->endptsetupstat);
+ fsl_writel(temp | (1 << ep_num), &dr_regs->endptsetupstat);
+
+ /* while a hazard exists when setup package arrives */
+ do {
+ /* Set Setup Tripwire */
+ temp = fsl_readl(&dr_regs->usbcmd);
+ fsl_writel(temp | USB_CMD_SUTW, &dr_regs->usbcmd);
+
+ /* Copy the setup packet to local buffer */
+ memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8);
+ } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_SUTW));
+
+ /* Clear Setup Tripwire */
+ temp = fsl_readl(&dr_regs->usbcmd);
+ fsl_writel(temp & ~USB_CMD_SUTW, &dr_regs->usbcmd);
+}
+
+/* process-ep_req(): free the completed Tds for this req */
+static int process_ep_req(struct fsl_udc *udc, int pipe,
+ struct fsl_req *curr_req)
+{
+ struct ep_td_struct *curr_td;
+ int td_complete, actual, remaining_length, j, tmp;
+ int status = 0;
+ int errors = 0;
+ struct ep_queue_head *curr_qh = &udc->ep_qh[pipe];
+ int direction = pipe % 2;
+
+ curr_td = curr_req->head;
+ td_complete = 0;
+ actual = curr_req->req.length;
+
+ for (j = 0; j < curr_req->dtd_count; j++) {
+ remaining_length = (le32_to_cpu(curr_td->size_ioc_sts)
+ & DTD_PACKET_SIZE)
+ >> DTD_LENGTH_BIT_POS;
+ actual -= remaining_length;
+
+ if ((errors = le32_to_cpu(curr_td->size_ioc_sts) &
+ DTD_ERROR_MASK)) {
+ if (errors & DTD_STATUS_HALTED) {
+ ERR("dTD error %08x QH=%d\n", errors, pipe);
+ /* Clear the errors and Halt condition */
+ tmp = le32_to_cpu(curr_qh->size_ioc_int_sts);
+ tmp &= ~errors;
+ curr_qh->size_ioc_int_sts = cpu_to_le32(tmp);
+ status = -EPIPE;
+ /* FIXME: continue with next queued TD? */
+
+ break;
+ }
+ if (errors & DTD_STATUS_DATA_BUFF_ERR) {
+ VDBG("Transfer overflow");
+ status = -EPROTO;
+ break;
+ } else if (errors & DTD_STATUS_TRANSACTION_ERR) {
+ VDBG("ISO error");
+ status = -EILSEQ;
+ break;
+ } else
+ ERR("Unknown error has occured (0x%x)!\r\n",
+ errors);
+
+ } else if (le32_to_cpu(curr_td->size_ioc_sts)
+ & DTD_STATUS_ACTIVE) {
+ VDBG("Request not complete");
+ status = REQ_UNCOMPLETE;
+ return status;
+ } else if (remaining_length) {
+ if (direction) {
+ VDBG("Transmit dTD remaining length not zero");
+ status = -EPROTO;
+ break;
+ } else {
+ td_complete++;
+ break;
+ }
+ } else {
+ td_complete++;
+ VDBG("dTD transmitted successful ");
+ }
+
+ if (j != curr_req->dtd_count - 1)
+ curr_td = (struct ep_td_struct *)curr_td->next_td_virt;
+ }
+
+ if (status)
+ return status;
+
+ curr_req->req.actual = actual;
+
+ return 0;
+}
+
+/* Process a DTD completion interrupt */
+static void dtd_complete_irq(struct fsl_udc *udc)
+{
+ u32 bit_pos;
+ int i, ep_num, direction, bit_mask, status;
+ struct fsl_ep *curr_ep;
+ struct fsl_req *curr_req, *temp_req;
+
+ /* Clear the bits in the register */
+ bit_pos = fsl_readl(&dr_regs->endptcomplete);
+ fsl_writel(bit_pos, &dr_regs->endptcomplete);
+
+ if (!bit_pos)
+ return;
+
+ for (i = 0; i < udc->max_ep * 2; i++) {
+ ep_num = i >> 1;
+ direction = i % 2;
+
+ bit_mask = 1 << (ep_num + 16 * direction);
+
+ if (!(bit_pos & bit_mask))
+ continue;
+
+ curr_ep = get_ep_by_pipe(udc, i);
+
+ /* If the ep is configured */
+ if (curr_ep->name == NULL) {
+ WARN("Invalid EP?");
+ continue;
+ }
+
+ /* process the req queue until an uncomplete request */
+ list_for_each_entry_safe(curr_req, temp_req, &curr_ep->queue,
+ queue) {
+ status = process_ep_req(udc, i, curr_req);
+
+ VDBG("status of process_ep_req= %d, ep = %d",
+ status, ep_num);
+ if (status == REQ_UNCOMPLETE)
+ break;
+ /* write back status to req */
+ curr_req->req.status = status;
+
+ if (ep_num == 0) {
+ ep0_req_complete(udc, curr_ep, curr_req);
+ break;
+ } else
+ done(curr_ep, curr_req, status);
+ }
+ }
+}
+
+/* Process a port change interrupt */
+static void port_change_irq(struct fsl_udc *udc)
+{
+ u32 speed;
+
+ if (udc->bus_reset)
+ udc->bus_reset = 0;
+
+ /* Bus resetting is finished */
+ if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) {
+ /* Get the speed */
+ speed = (fsl_readl(&dr_regs->portsc1)
+ & PORTSCX_PORT_SPEED_MASK);
+ switch (speed) {
+ case PORTSCX_PORT_SPEED_HIGH:
+ udc->gadget.speed = USB_SPEED_HIGH;
+ break;
+ case PORTSCX_PORT_SPEED_FULL:
+ udc->gadget.speed = USB_SPEED_FULL;
+ break;
+ case PORTSCX_PORT_SPEED_LOW:
+ udc->gadget.speed = USB_SPEED_LOW;
+ break;
+ default:
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ break;
+ }
+ }
+
+ /* Update USB state */
+ if (!udc->resume_state)
+ udc->usb_state = USB_STATE_DEFAULT;
+}
+
+/* Process suspend interrupt */
+static void suspend_irq(struct fsl_udc *udc)
+{
+ udc->resume_state = udc->usb_state;
+ udc->usb_state = USB_STATE_SUSPENDED;
+
+ /* report suspend to the driver, serial.c does not support this */
+ if (udc->driver->suspend)
+ udc->driver->suspend(&udc->gadget);
+}
+
+static void bus_resume(struct fsl_udc *udc)
+{
+ udc->usb_state = udc->resume_state;
+ udc->resume_state = 0;
+
+ /* report resume to the driver, serial.c does not support this */
+ if (udc->driver->resume)
+ udc->driver->resume(&udc->gadget);
+}
+
+/* Clear up all ep queues */
+static int reset_queues(struct fsl_udc *udc)
+{
+ u8 pipe;
+
+ for (pipe = 0; pipe < udc->max_pipes; pipe++)
+ udc_reset_ep_queue(udc, pipe);
+
+ /* report disconnect; the driver is already quiesced */
+ udc->driver->disconnect(&udc->gadget);
+
+ return 0;
+}
+
+/* Process reset interrupt */
+static void reset_irq(struct fsl_udc *udc)
+{
+ u32 temp;
+ unsigned long timeout;
+
+ /* Clear the device address */
+ temp = fsl_readl(&dr_regs->deviceaddr);
+ fsl_writel(temp & ~USB_DEVICE_ADDRESS_MASK, &dr_regs->deviceaddr);
+
+ udc->device_address = 0;
+
+ /* Clear usb state */
+ udc->resume_state = 0;
+ udc->ep0_dir = 0;
+ udc->ep0_state = WAIT_FOR_SETUP;
+ udc->remote_wakeup = 0; /* default to 0 on reset */
+ udc->gadget.b_hnp_enable = 0;
+ udc->gadget.a_hnp_support = 0;
+ udc->gadget.a_alt_hnp_support = 0;
+
+ /* Clear all the setup token semaphores */
+ temp = fsl_readl(&dr_regs->endptsetupstat);
+ fsl_writel(temp, &dr_regs->endptsetupstat);
+
+ /* Clear all the endpoint complete status bits */
+ temp = fsl_readl(&dr_regs->endptcomplete);
+ fsl_writel(temp, &dr_regs->endptcomplete);
+
+ timeout = jiffies + 100;
+ while (fsl_readl(&dr_regs->endpointprime)) {
+ /* Wait until all endptprime bits cleared */
+ if (time_after(jiffies, timeout)) {
+ ERR("Timeout for reset\n");
+ break;
+ }
+ cpu_relax();
+ }
+
+ /* Write 1s to the flush register */
+ fsl_writel(0xffffffff, &dr_regs->endptflush);
+
+ if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {
+ VDBG("Bus reset");
+ /* Bus is reseting */
+ udc->bus_reset = 1;
+ /* Reset all the queues, include XD, dTD, EP queue
+ * head and TR Queue */
+ reset_queues(udc);
+ udc->usb_state = USB_STATE_DEFAULT;
+ } else {
+ VDBG("Controller reset");
+ /* initialize usb hw reg except for regs for EP, not
+ * touch usbintr reg */
+ dr_controller_setup(udc);
+
+ /* Reset all internal used Queues */
+ reset_queues(udc);
+
+ ep0_setup(udc);
+
+ /* Enable DR IRQ reg, Set Run bit, change udc state */
+ dr_controller_run(udc);
+ udc->usb_state = USB_STATE_ATTACHED;
+ }
+}
+
+/*
+ * USB device controller interrupt handler
+ */
+static irqreturn_t fsl_udc_irq(int irq, void *_udc)
+{
+ struct fsl_udc *udc = _udc;
+ u32 irq_src;
+ irqreturn_t status = IRQ_NONE;
+ unsigned long flags;
+
+ /* Disable ISR for OTG host mode */
+ if (udc->stopped)
+ return IRQ_NONE;
+ spin_lock_irqsave(&udc->lock, flags);
+ irq_src = fsl_readl(&dr_regs->usbsts) & fsl_readl(&dr_regs->usbintr);
+ /* Clear notification bits */
+ fsl_writel(irq_src, &dr_regs->usbsts);
+
+ /* VDBG("irq_src [0x%8x]", irq_src); */
+
+ /* Need to resume? */
+ if (udc->usb_state == USB_STATE_SUSPENDED)
+ if ((fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_SUSPEND) == 0)
+ bus_resume(udc);
+
+ /* USB Interrupt */
+ if (irq_src & USB_STS_INT) {
+ VDBG("Packet int");
+ /* Setup package, we only support ep0 as control ep */
+ if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) {
+ tripwire_handler(udc, 0,
+ (u8 *) (&udc->local_setup_buff));
+ setup_received_irq(udc, &udc->local_setup_buff);
+ status = IRQ_HANDLED;
+ }
+
+ /* completion of dtd */
+ if (fsl_readl(&dr_regs->endptcomplete)) {
+ dtd_complete_irq(udc);
+ status = IRQ_HANDLED;
+ }
+ }
+
+ /* SOF (for ISO transfer) */
+ if (irq_src & USB_STS_SOF) {
+ status = IRQ_HANDLED;
+ }
+
+ /* Port Change */
+ if (irq_src & USB_STS_PORT_CHANGE) {
+ port_change_irq(udc);
+ status = IRQ_HANDLED;
+ }
+
+ /* Reset Received */
+ if (irq_src & USB_STS_RESET) {
+ reset_irq(udc);
+ status = IRQ_HANDLED;
+ }
+
+ /* Sleep Enable (Suspend) */
+ if (irq_src & USB_STS_SUSPEND) {
+ suspend_irq(udc);
+ status = IRQ_HANDLED;
+ }
+
+ if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) {
+ VDBG("Error IRQ %x ", irq_src);
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return status;
+}
+
+/*----------------------------------------------------------------*
+ * Hook to gadget drivers
+ * Called by initialization code of gadget drivers
+*----------------------------------------------------------------*/
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ int retval = -ENODEV;
+ unsigned long flags = 0;
+
+ if (!udc_controller)
+ return -ENODEV;
+
+ if (!driver || (driver->speed != USB_SPEED_FULL
+ && driver->speed != USB_SPEED_HIGH)
+ || !driver->bind || !driver->disconnect
+ || !driver->setup)
+ return -EINVAL;
+
+ if (udc_controller->driver)
+ return -EBUSY;
+
+ /* lock is needed but whether should use this lock or another */
+ spin_lock_irqsave(&udc_controller->lock, flags);
+
+ driver->driver.bus = 0;
+ /* hook up the driver */
+ udc_controller->driver = driver;
+ udc_controller->gadget.dev.driver = &driver->driver;
+ spin_unlock_irqrestore(&udc_controller->lock, flags);
+
+ /* bind udc driver to gadget driver */
+ retval = driver->bind(&udc_controller->gadget);
+ if (retval) {
+ VDBG("bind to %s --> %d", driver->driver.name, retval);
+ udc_controller->gadget.dev.driver = 0;
+ udc_controller->driver = 0;
+ goto out;
+ }
+
+ /* Enable DR IRQ reg and Set usbcmd reg Run bit */
+ dr_controller_run(udc_controller);
+ udc_controller->usb_state = USB_STATE_ATTACHED;
+ udc_controller->ep0_state = WAIT_FOR_SETUP;
+ udc_controller->ep0_dir = 0;
+ printk(KERN_INFO "%s: bind to driver %s \n",
+ udc_controller->gadget.name, driver->driver.name);
+
+out:
+ if (retval)
+ printk("retval %d \n", retval);
+ return retval;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+/* Disconnect from gadget driver */
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct fsl_ep *loop_ep;
+ unsigned long flags;
+
+ if (!udc_controller)
+ return -ENODEV;
+
+ if (!driver || driver != udc_controller->driver || !driver->unbind)
+ return -EINVAL;
+
+#ifdef CONFIG_USB_OTG
+ if (udc_controller->transceiver)
+ (void)otg_set_peripheral(udc_controller->transceiver, 0);
+#endif
+
+ /* stop DR, disable intr */
+ dr_controller_stop(udc_controller);
+
+ /* in fact, no needed */
+ udc_controller->usb_state = USB_STATE_ATTACHED;
+ udc_controller->ep0_state = WAIT_FOR_SETUP;
+ udc_controller->ep0_dir = 0;
+
+ /* stand operation */
+ spin_lock_irqsave(&udc_controller->lock, flags);
+ udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
+ nuke(&udc_controller->eps[0], -ESHUTDOWN);
+ list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list,
+ ep.ep_list)
+ nuke(loop_ep, -ESHUTDOWN);
+ spin_unlock_irqrestore(&udc_controller->lock, flags);
+
+ /* unbind gadget and unhook driver. */
+ driver->unbind(&udc_controller->gadget);
+ udc_controller->gadget.dev.driver = 0;
+ udc_controller->driver = 0;
+
+ printk("unregistered gadget driver '%s'\r\n", driver->driver.name);
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*-------------------------------------------------------------------------
+ PROC File System Support
+-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+#include <linux/seq_file.h>
+
+static const char proc_filename[] = "driver/fsl_usb2_udc";
+
+static int fsl_proc_read(char *page, char **start, off_t off, int count,
+ int *eof, void *_dev)
+{
+ char *buf = page;
+ char *next = buf;
+ unsigned size = count;
+ unsigned long flags;
+ int t, i;
+ u32 tmp_reg;
+ struct fsl_ep *ep = NULL;
+ struct fsl_req *req;
+
+ struct fsl_udc *udc = udc_controller;
+ if (off != 0)
+ return 0;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* ------basic driver infomation ---- */
+ t = scnprintf(next, size,
+ DRIVER_DESC "\n"
+ "%s version: %s\n"
+ "Gadget driver: %s\n\n",
+ driver_name, DRIVER_VERSION,
+ udc->driver ? udc->driver->driver.name : "(none)");
+ size -= t;
+ next += t;
+
+ /* ------ DR Registers ----- */
+ tmp_reg = fsl_readl(&dr_regs->usbcmd);
+ t = scnprintf(next, size,
+ "USBCMD reg:\n"
+ "SetupTW: %d\n"
+ "Run/Stop: %s\n\n",
+ (tmp_reg & USB_CMD_SUTW) ? 1 : 0,
+ (tmp_reg & USB_CMD_RUN_STOP) ? "Run" : "Stop");
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->usbsts);
+ t = scnprintf(next, size,
+ "USB Status Reg:\n"
+ "Dr Suspend: %d" "Reset Received: %d" "System Error: %s"
+ "USB Error Interrupt: %s\n\n",
+ (tmp_reg & USB_STS_SUSPEND) ? 1 : 0,
+ (tmp_reg & USB_STS_RESET) ? 1 : 0,
+ (tmp_reg & USB_STS_SYS_ERR) ? "Err" : "Normal",
+ (tmp_reg & USB_STS_ERR) ? "Err detected" : "No err");
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->usbintr);
+ t = scnprintf(next, size,
+ "USB Intrrupt Enable Reg:\n"
+ "Sleep Enable: %d" "SOF Received Enable: %d"
+ "Reset Enable: %d\n"
+ "System Error Enable: %d"
+ "Port Change Dectected Enable: %d\n"
+ "USB Error Intr Enable: %d" "USB Intr Enable: %d\n\n",
+ (tmp_reg & USB_INTR_DEVICE_SUSPEND) ? 1 : 0,
+ (tmp_reg & USB_INTR_SOF_EN) ? 1 : 0,
+ (tmp_reg & USB_INTR_RESET_EN) ? 1 : 0,
+ (tmp_reg & USB_INTR_SYS_ERR_EN) ? 1 : 0,
+ (tmp_reg & USB_INTR_PTC_DETECT_EN) ? 1 : 0,
+ (tmp_reg & USB_INTR_ERR_INT_EN) ? 1 : 0,
+ (tmp_reg & USB_INTR_INT_EN) ? 1 : 0);
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->frindex);
+ t = scnprintf(next, size,
+ "USB Frame Index Reg:" "Frame Number is 0x%x\n\n",
+ (tmp_reg & USB_FRINDEX_MASKS));
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->deviceaddr);
+ t = scnprintf(next, size,
+ "USB Device Address Reg:" "Device Addr is 0x%x\n\n",
+ (tmp_reg & USB_DEVICE_ADDRESS_MASK));
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->endpointlistaddr);
+ t = scnprintf(next, size,
+ "USB Endpoint List Address Reg:"
+ "Device Addr is 0x%x\n\n",
+ (tmp_reg & USB_EP_LIST_ADDRESS_MASK));
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->portsc1);
+ t = scnprintf(next, size,
+ "USB Port Status&Control Reg:\n"
+ "Port Transceiver Type : %s" "Port Speed: %s \n"
+ "PHY Low Power Suspend: %s" "Port Reset: %s"
+ "Port Suspend Mode: %s \n" "Over-current Change: %s"
+ "Port Enable/Disable Change: %s\n"
+ "Port Enabled/Disabled: %s"
+ "Current Connect Status: %s\n\n", ( {
+ char *s;
+ switch (tmp_reg & PORTSCX_PTS_FSLS) {
+ case PORTSCX_PTS_UTMI:
+ s = "UTMI"; break;
+ case PORTSCX_PTS_ULPI:
+ s = "ULPI "; break;
+ case PORTSCX_PTS_FSLS:
+ s = "FS/LS Serial"; break;
+ default:
+ s = "None"; break;
+ }
+ s;} ), ( {
+ char *s;
+ switch (tmp_reg & PORTSCX_PORT_SPEED_UNDEF) {
+ case PORTSCX_PORT_SPEED_FULL:
+ s = "Full Speed"; break;
+ case PORTSCX_PORT_SPEED_LOW:
+ s = "Low Speed"; break;
+ case PORTSCX_PORT_SPEED_HIGH:
+ s = "High Speed"; break;
+ default:
+ s = "Undefined"; break;
+ }
+ s;
+ } ),
+ (tmp_reg & PORTSCX_PHY_LOW_POWER_SPD) ?
+ "Normal PHY mode" : "Low power mode",
+ (tmp_reg & PORTSCX_PORT_RESET) ? "In Reset" :
+ "Not in Reset",
+ (tmp_reg & PORTSCX_PORT_SUSPEND) ? "In " : "Not in",
+ (tmp_reg & PORTSCX_OVER_CURRENT_CHG) ? "Dected" :
+ "No",
+ (tmp_reg & PORTSCX_PORT_EN_DIS_CHANGE) ? "Disable" :
+ "Not change",
+ (tmp_reg & PORTSCX_PORT_ENABLE) ? "Enable" :
+ "Not correct",
+ (tmp_reg & PORTSCX_CURRENT_CONNECT_STATUS) ?
+ "Attached" : "Not-Att");
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->usbmode);
+ t = scnprintf(next, size,
+ "USB Mode Reg:" "Controller Mode is : %s\n\n", ( {
+ char *s;
+ switch (tmp_reg & USB_MODE_CTRL_MODE_HOST) {
+ case USB_MODE_CTRL_MODE_IDLE:
+ s = "Idle"; break;
+ case USB_MODE_CTRL_MODE_DEVICE:
+ s = "Device Controller"; break;
+ case USB_MODE_CTRL_MODE_HOST:
+ s = "Host Controller"; break;
+ default:
+ s = "None"; break;
+ }
+ s;
+ } ));
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->endptsetupstat);
+ t = scnprintf(next, size,
+ "Endpoint Setup Status Reg:" "SETUP on ep 0x%x\n\n",
+ (tmp_reg & EP_SETUP_STATUS_MASK));
+ size -= t;
+ next += t;
+
+ for (i = 0; i < udc->max_ep / 2; i++) {
+ tmp_reg = fsl_readl(&dr_regs->endptctrl[i]);
+ t = scnprintf(next, size, "EP Ctrl Reg [0x%x]: = [0x%x]\n",
+ i, tmp_reg);
+ size -= t;
+ next += t;
+ }
+ tmp_reg = fsl_readl(&dr_regs->endpointprime);
+ t = scnprintf(next, size, "EP Prime Reg = [0x%x]\n", tmp_reg);
+ size -= t;
+ next += t;
+
+ tmp_reg = usb_sys_regs->snoop1;
+ t = scnprintf(next, size, "\nSnoop1 Reg : = [0x%x]\n\n", tmp_reg);
+ size -= t;
+ next += t;
+
+ tmp_reg = usb_sys_regs->control;
+ t = scnprintf(next, size, "General Control Reg : = [0x%x]\n\n",
+ tmp_reg);
+ size -= t;
+ next += t;
+
+ /* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
+ ep = &udc->eps[0];
+ t = scnprintf(next, size, "For %s Maxpkt is 0x%x index is 0x%x\n",
+ ep->ep.name, ep_maxpacket(ep), ep_index(ep));
+ size -= t;
+ next += t;
+
+ if (list_empty(&ep->queue)) {
+ t = scnprintf(next, size, "its req queue is empty\n\n");
+ size -= t;
+ next += t;
+ } else {
+ list_for_each_entry(req, &ep->queue, queue) {
+ t = scnprintf(next, size,
+ "req %p actual 0x%x length 0x%x buf %p\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf);
+ size -= t;
+ next += t;
+ }
+ }
+ /* other gadget->eplist ep */
+ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+ if (ep->desc) {
+ t = scnprintf(next, size,
+ "\nFor %s Maxpkt is 0x%x "
+ "index is 0x%x\n",
+ ep->ep.name, ep_maxpacket(ep),
+ ep_index(ep));
+ size -= t;
+ next += t;
+
+ if (list_empty(&ep->queue)) {
+ t = scnprintf(next, size,
+ "its req queue is empty\n\n");
+ size -= t;
+ next += t;
+ } else {
+ list_for_each_entry(req, &ep->queue, queue) {
+ t = scnprintf(next, size,
+ "req %p actual 0x%x length"
+ "0x%x buf %p\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf);
+ size -= t;
+ next += t;
+ } /* end for each_entry of ep req */
+ } /* end for else */
+ } /* end for if(ep->queue) */
+ } /* end (ep->desc) */
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ *eof = 1;
+ return count - size;
+}
+
+#define create_proc_file() create_proc_read_entry(proc_filename, \
+ 0, NULL, fsl_proc_read, NULL)
+
+#define remove_proc_file() remove_proc_entry(proc_filename, NULL)
+
+#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
+
+#define create_proc_file() do {} while (0)
+#define remove_proc_file() do {} while (0)
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+/*-------------------------------------------------------------------------*/
+
+/* Release udc structures */
+static void fsl_udc_release(struct device *dev)
+{
+ complete(udc_controller->done);
+ dma_free_coherent(dev, udc_controller->ep_qh_size,
+ udc_controller->ep_qh, udc_controller->ep_qh_dma);
+ kfree(udc_controller);
+}
+
+/******************************************************************
+ Internal structure setup functions
+*******************************************************************/
+/*------------------------------------------------------------------
+ * init resource for globle controller
+ * Return the udc handle on success or NULL on failure
+ ------------------------------------------------------------------*/
+static struct fsl_udc *__init struct_udc_setup(struct platform_device *pdev)
+{
+ struct fsl_udc *udc;
+ struct fsl_usb2_platform_data *pdata;
+ size_t size;
+
+ udc = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL);
+ if (udc == NULL) {
+ ERR("malloc udc failed\n");
+ return NULL;
+ }
+
+ pdata = pdev->dev.platform_data;
+ udc->phy_mode = pdata->phy_mode;
+ /* max_ep_nr is bidirectional ep number, max_ep doubles the number */
+ udc->max_ep = pdata->max_ep_nr * 2;
+
+ udc->eps = kzalloc(sizeof(struct fsl_ep) * udc->max_ep, GFP_KERNEL);
+ if (!udc->eps) {
+ ERR("malloc fsl_ep failed\n");
+ goto cleanup;
+ }
+
+ /* initialized QHs, take care of alignment */
+ size = udc->max_ep * sizeof(struct ep_queue_head);
+ if (size < QH_ALIGNMENT)
+ size = QH_ALIGNMENT;
+ else if ((size % QH_ALIGNMENT) != 0) {
+ size += QH_ALIGNMENT + 1;
+ size &= ~(QH_ALIGNMENT - 1);
+ }
+ udc->ep_qh = dma_alloc_coherent(&pdev->dev, size,
+ &udc->ep_qh_dma, GFP_KERNEL);
+ if (!udc->ep_qh) {
+ ERR("malloc QHs for udc failed\n");
+ kfree(udc->eps);
+ goto cleanup;
+ }
+
+ udc->ep_qh_size = size;
+
+ /* Initialize ep0 status request structure */
+ /* FIXME: fsl_alloc_request() ignores ep argument */
+ udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL),
+ struct fsl_req, req);
+ /* allocate a small amount of memory to get valid address */
+ udc->status_req->req.buf = kmalloc(8, GFP_KERNEL);
+ udc->status_req->req.dma = virt_to_phys(udc->status_req->req.buf);
+
+ udc->resume_state = USB_STATE_NOTATTACHED;
+ udc->usb_state = USB_STATE_POWERED;
+ udc->ep0_dir = 0;
+ udc->remote_wakeup = 0; /* default to 0 on reset */
+ spin_lock_init(&udc->lock);
+
+ return udc;
+
+cleanup:
+ kfree(udc);
+ return NULL;
+}
+
+/*----------------------------------------------------------------
+ * Setup the fsl_ep struct for eps
+ * Link fsl_ep->ep to gadget->ep_list
+ * ep0out is not used so do nothing here
+ * ep0in should be taken care
+ *--------------------------------------------------------------*/
+static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index,
+ char *name, int link)
+{
+ struct fsl_ep *ep = &udc->eps[index];
+
+ ep->udc = udc;
+ strcpy(ep->name, name);
+ ep->ep.name = ep->name;
+
+ ep->ep.ops = &fsl_ep_ops;
+ ep->stopped = 0;
+
+ /* for ep0: maxP defined in desc
+ * for other eps, maxP is set by epautoconfig() called by gadget layer
+ */
+ ep->ep.maxpacket = (unsigned short) ~0;
+
+ /* the queue lists any req for this ep */
+ INIT_LIST_HEAD(&ep->queue);
+
+ /* gagdet.ep_list used for ep_autoconfig so no ep0 */
+ if (link)
+ list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+ ep->gadget = &udc->gadget;
+ ep->qh = &udc->ep_qh[index];
+
+ return 0;
+}
+
+/* Driver probe function
+ * all intialize operations implemented here except enabling usb_intr reg
+ */
+static int __init fsl_udc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret = -ENODEV;
+ unsigned int i;
+
+ if (strcmp(pdev->name, driver_name)) {
+ VDBG("Wrong device\n");
+ return -ENODEV;
+ }
+
+ /* board setup should have been done in the platform code */
+
+ /* Initialize the udc structure including QH member and other member */
+ udc_controller = struct_udc_setup(pdev);
+ if (!udc_controller) {
+ VDBG("udc_controller is NULL \n");
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+
+ if (!request_mem_region(res->start, res->end - res->start + 1,
+ driver_name)) {
+ ERR("request mem region for %s failed \n", pdev->name);
+ return -EBUSY;
+ }
+
+ dr_regs = ioremap(res->start, res->end - res->start + 1);
+ if (!dr_regs) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ usb_sys_regs = (struct usb_sys_interface *)
+ ((u32)dr_regs + USB_DR_SYS_OFFSET);
+
+ udc_controller->irq = platform_get_irq(pdev, 0);
+ if (!udc_controller->irq) {
+ ret = -ENODEV;
+ goto err2;
+ }
+
+ ret = request_irq(udc_controller->irq, fsl_udc_irq, SA_SHIRQ,
+ driver_name, udc_controller);
+ if (ret != 0) {
+ ERR("cannot request irq %d err %d \n",
+ udc_controller->irq, ret);
+ goto err2;
+ }
+
+ /* initialize usb hw reg except for regs for EP,
+ * leave usbintr reg untouched */
+ dr_controller_setup(udc_controller);
+
+ /* Setup gadget structure */
+ udc_controller->gadget.ops = &fsl_gadget_ops;
+ udc_controller->gadget.is_dualspeed = 1;
+ udc_controller->gadget.ep0 = &udc_controller->eps[0].ep;
+ INIT_LIST_HEAD(&udc_controller->gadget.ep_list);
+ udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
+ udc_controller->gadget.name = driver_name;
+
+ /* Setup gadget.dev and register with kernel */
+ strcpy(udc_controller->gadget.dev.bus_id, "gadget");
+ udc_controller->gadget.dev.release = fsl_udc_release;
+ udc_controller->gadget.dev.parent = &pdev->dev;
+ ret = device_register(&udc_controller->gadget.dev);
+ if (ret < 0)
+ goto err3;
+
+ /* setup QH and epctrl for ep0 */
+ ep0_setup(udc_controller);
+
+ /* setup udc->eps[] for ep0 */
+ struct_ep_setup(udc_controller, 0, "ep0", 0);
+ /* for ep0: the desc defined here;
+ * for other eps, gadget layer called ep_enable with defined desc
+ */
+ udc_controller->eps[0].desc = &fsl_ep0_desc;
+ udc_controller->eps[0].ep.maxpacket = USB_MAX_CTRL_PAYLOAD;
+
+ /* setup the udc->eps[] for non-control endpoints and link
+ * to gadget.ep_list */
+ for (i = 1; i < (int)(udc_controller->max_ep / 2); i++) {
+ char name[14];
+
+ sprintf(name, "ep%dout", i);
+ struct_ep_setup(udc_controller, i * 2, name, 1);
+ sprintf(name, "ep%din", i);
+ struct_ep_setup(udc_controller, i * 2 + 1, name, 1);
+ }
+
+ /* use dma_pool for TD management */
+ udc_controller->td_pool = dma_pool_create("udc_td", &pdev->dev,
+ sizeof(struct ep_td_struct),
+ DTD_ALIGNMENT, UDC_DMA_BOUNDARY);
+ if (udc_controller->td_pool == NULL) {
+ ret = -ENOMEM;
+ goto err4;
+ }
+ create_proc_file();
+ return 0;
+
+err4:
+ device_unregister(&udc_controller->gadget.dev);
+err3:
+ free_irq(udc_controller->irq, udc_controller);
+err2:
+ iounmap(dr_regs);
+err1:
+ release_mem_region(res->start, res->end - res->start + 1);
+ return ret;
+}
+
+/* Driver removal function
+ * Free resources and finish pending transactions
+ */
+static int __exit fsl_udc_remove(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ DECLARE_COMPLETION(done);
+
+ if (!udc_controller)
+ return -ENODEV;
+ udc_controller->done = &done;
+
+ /* DR has been stopped in usb_gadget_unregister_driver() */
+ remove_proc_file();
+
+ /* Free allocated memory */
+ kfree(udc_controller->status_req->req.buf);
+ kfree(udc_controller->status_req);
+ kfree(udc_controller->eps);
+
+ dma_pool_destroy(udc_controller->td_pool);
+ free_irq(udc_controller->irq, udc_controller);
+ iounmap(dr_regs);
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ device_unregister(&udc_controller->gadget.dev);
+ /* free udc --wait for the release() finished */
+ wait_for_completion(&done);
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------
+ * Modify Power management attributes
+ * Used by OTG statemachine to disable gadget temporarily
+ -----------------------------------------------------------------*/
+static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ dr_controller_stop(udc_controller);
+ return 0;
+}
+
+/*-----------------------------------------------------------------
+ * Invoked on USB resume. May be called in_interrupt.
+ * Here we start the DR controller and enable the irq
+ *-----------------------------------------------------------------*/
+static int fsl_udc_resume(struct platform_device *pdev)
+{
+ /* Enable DR irq reg and set controller Run */
+ if (udc_controller->stopped) {
+ dr_controller_setup(udc_controller);
+ dr_controller_run(udc_controller);
+ }
+ udc_controller->usb_state = USB_STATE_ATTACHED;
+ udc_controller->ep0_state = WAIT_FOR_SETUP;
+ udc_controller->ep0_dir = 0;
+ return 0;
+}
+
+/*-------------------------------------------------------------------------
+ Register entry point for the peripheral controller driver
+--------------------------------------------------------------------------*/
+
+static struct platform_driver udc_driver = {
+ .remove = __exit_p(fsl_udc_remove),
+ /* these suspend and resume are not usb suspend and resume */
+ .suspend = fsl_udc_suspend,
+ .resume = fsl_udc_resume,
+ .driver = {
+ .name = (char *)driver_name,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init udc_init(void)
+{
+ printk(KERN_INFO "%s (%s)\n", driver_desc, DRIVER_VERSION);
+ return platform_driver_probe(&udc_driver, fsl_udc_probe);
+}
+
+module_init(udc_init);
+
+static void __exit udc_exit(void)
+{
+ platform_driver_unregister(&udc_driver);
+ printk("%s unregistered \n", driver_desc);
+}
+
+module_exit(udc_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h
new file mode 100644
index 00000000000..c6291e04650
--- /dev/null
+++ b/drivers/usb/gadget/fsl_usb2_udc.h
@@ -0,0 +1,579 @@
+/*
+ * Freescale USB device/endpoint management registers
+ */
+#ifndef __FSL_USB2_UDC_H
+#define __FSL_USB2_UDC_H
+
+/* ### define USB registers here
+ */
+#define USB_MAX_CTRL_PAYLOAD 64
+#define USB_DR_SYS_OFFSET 0x400
+
+ /* USB DR device mode registers (Little Endian) */
+struct usb_dr_device {
+ /* Capability register */
+ u8 res1[256];
+ u16 caplength; /* Capability Register Length */
+ u16 hciversion; /* Host Controller Interface Version */
+ u32 hcsparams; /* Host Controller Structual Parameters */
+ u32 hccparams; /* Host Controller Capability Parameters */
+ u8 res2[20];
+ u32 dciversion; /* Device Controller Interface Version */
+ u32 dccparams; /* Device Controller Capability Parameters */
+ u8 res3[24];
+ /* Operation register */
+ u32 usbcmd; /* USB Command Register */
+ u32 usbsts; /* USB Status Register */
+ u32 usbintr; /* USB Interrupt Enable Register */
+ u32 frindex; /* Frame Index Register */
+ u8 res4[4];
+ u32 deviceaddr; /* Device Address */
+ u32 endpointlistaddr; /* Endpoint List Address Register */
+ u8 res5[4];
+ u32 burstsize; /* Master Interface Data Burst Size Register */
+ u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */
+ u8 res6[24];
+ u32 configflag; /* Configure Flag Register */
+ u32 portsc1; /* Port 1 Status and Control Register */
+ u8 res7[28];
+ u32 otgsc; /* On-The-Go Status and Control */
+ u32 usbmode; /* USB Mode Register */
+ u32 endptsetupstat; /* Endpoint Setup Status Register */
+ u32 endpointprime; /* Endpoint Initialization Register */
+ u32 endptflush; /* Endpoint Flush Register */
+ u32 endptstatus; /* Endpoint Status Register */
+ u32 endptcomplete; /* Endpoint Complete Register */
+ u32 endptctrl[6]; /* Endpoint Control Registers */
+};
+
+ /* USB DR host mode registers (Little Endian) */
+struct usb_dr_host {
+ /* Capability register */
+ u8 res1[256];
+ u16 caplength; /* Capability Register Length */
+ u16 hciversion; /* Host Controller Interface Version */
+ u32 hcsparams; /* Host Controller Structual Parameters */
+ u32 hccparams; /* Host Controller Capability Parameters */
+ u8 res2[20];
+ u32 dciversion; /* Device Controller Interface Version */
+ u32 dccparams; /* Device Controller Capability Parameters */
+ u8 res3[24];
+ /* Operation register */
+ u32 usbcmd; /* USB Command Register */
+ u32 usbsts; /* USB Status Register */
+ u32 usbintr; /* USB Interrupt Enable Register */
+ u32 frindex; /* Frame Index Register */
+ u8 res4[4];
+ u32 periodiclistbase; /* Periodic Frame List Base Address Register */
+ u32 asynclistaddr; /* Current Asynchronous List Address Register */
+ u8 res5[4];
+ u32 burstsize; /* Master Interface Data Burst Size Register */
+ u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */
+ u8 res6[24];
+ u32 configflag; /* Configure Flag Register */
+ u32 portsc1; /* Port 1 Status and Control Register */
+ u8 res7[28];
+ u32 otgsc; /* On-The-Go Status and Control */
+ u32 usbmode; /* USB Mode Register */
+ u32 endptsetupstat; /* Endpoint Setup Status Register */
+ u32 endpointprime; /* Endpoint Initialization Register */
+ u32 endptflush; /* Endpoint Flush Register */
+ u32 endptstatus; /* Endpoint Status Register */
+ u32 endptcomplete; /* Endpoint Complete Register */
+ u32 endptctrl[6]; /* Endpoint Control Registers */
+};
+
+ /* non-EHCI USB system interface registers (Big Endian) */
+struct usb_sys_interface {
+ u32 snoop1;
+ u32 snoop2;
+ u32 age_cnt_thresh; /* Age Count Threshold Register */
+ u32 pri_ctrl; /* Priority Control Register */
+ u32 si_ctrl; /* System Interface Control Register */
+ u8 res[236];
+ u32 control; /* General Purpose Control Register */
+};
+
+/* ep0 transfer state */
+#define WAIT_FOR_SETUP 0
+#define DATA_STATE_XMIT 1
+#define DATA_STATE_NEED_ZLP 2
+#define WAIT_FOR_OUT_STATUS 3
+#define DATA_STATE_RECV 4
+
+/* Frame Index Register Bit Masks */
+#define USB_FRINDEX_MASKS 0x3fff
+/* USB CMD Register Bit Masks */
+#define USB_CMD_RUN_STOP 0x00000001
+#define USB_CMD_CTRL_RESET 0x00000002
+#define USB_CMD_PERIODIC_SCHEDULE_EN 0x00000010
+#define USB_CMD_ASYNC_SCHEDULE_EN 0x00000020
+#define USB_CMD_INT_AA_DOORBELL 0x00000040
+#define USB_CMD_ASP 0x00000300
+#define USB_CMD_ASYNC_SCH_PARK_EN 0x00000800
+#define USB_CMD_SUTW 0x00002000
+#define USB_CMD_ATDTW 0x00004000
+#define USB_CMD_ITC 0x00FF0000
+
+/* bit 15,3,2 are frame list size */
+#define USB_CMD_FRAME_SIZE_1024 0x00000000
+#define USB_CMD_FRAME_SIZE_512 0x00000004
+#define USB_CMD_FRAME_SIZE_256 0x00000008
+#define USB_CMD_FRAME_SIZE_128 0x0000000C
+#define USB_CMD_FRAME_SIZE_64 0x00008000
+#define USB_CMD_FRAME_SIZE_32 0x00008004
+#define USB_CMD_FRAME_SIZE_16 0x00008008
+#define USB_CMD_FRAME_SIZE_8 0x0000800C
+
+/* bit 9-8 are async schedule park mode count */
+#define USB_CMD_ASP_00 0x00000000
+#define USB_CMD_ASP_01 0x00000100
+#define USB_CMD_ASP_10 0x00000200
+#define USB_CMD_ASP_11 0x00000300
+#define USB_CMD_ASP_BIT_POS 8
+
+/* bit 23-16 are interrupt threshold control */
+#define USB_CMD_ITC_NO_THRESHOLD 0x00000000
+#define USB_CMD_ITC_1_MICRO_FRM 0x00010000
+#define USB_CMD_ITC_2_MICRO_FRM 0x00020000
+#define USB_CMD_ITC_4_MICRO_FRM 0x00040000
+#define USB_CMD_ITC_8_MICRO_FRM 0x00080000
+#define USB_CMD_ITC_16_MICRO_FRM 0x00100000
+#define USB_CMD_ITC_32_MICRO_FRM 0x00200000
+#define USB_CMD_ITC_64_MICRO_FRM 0x00400000
+#define USB_CMD_ITC_BIT_POS 16
+
+/* USB STS Register Bit Masks */
+#define USB_STS_INT 0x00000001
+#define USB_STS_ERR 0x00000002
+#define USB_STS_PORT_CHANGE 0x00000004
+#define USB_STS_FRM_LST_ROLL 0x00000008
+#define USB_STS_SYS_ERR 0x00000010
+#define USB_STS_IAA 0x00000020
+#define USB_STS_RESET 0x00000040
+#define USB_STS_SOF 0x00000080
+#define USB_STS_SUSPEND 0x00000100
+#define USB_STS_HC_HALTED 0x00001000
+#define USB_STS_RCL 0x00002000
+#define USB_STS_PERIODIC_SCHEDULE 0x00004000
+#define USB_STS_ASYNC_SCHEDULE 0x00008000
+
+/* USB INTR Register Bit Masks */
+#define USB_INTR_INT_EN 0x00000001
+#define USB_INTR_ERR_INT_EN 0x00000002
+#define USB_INTR_PTC_DETECT_EN 0x00000004
+#define USB_INTR_FRM_LST_ROLL_EN 0x00000008
+#define USB_INTR_SYS_ERR_EN 0x00000010
+#define USB_INTR_ASYN_ADV_EN 0x00000020
+#define USB_INTR_RESET_EN 0x00000040
+#define USB_INTR_SOF_EN 0x00000080
+#define USB_INTR_DEVICE_SUSPEND 0x00000100
+
+/* Device Address bit masks */
+#define USB_DEVICE_ADDRESS_MASK 0xFE000000
+#define USB_DEVICE_ADDRESS_BIT_POS 25
+
+/* endpoint list address bit masks */
+#define USB_EP_LIST_ADDRESS_MASK 0xfffff800
+
+/* PORTSCX Register Bit Masks */
+#define PORTSCX_CURRENT_CONNECT_STATUS 0x00000001
+#define PORTSCX_CONNECT_STATUS_CHANGE 0x00000002
+#define PORTSCX_PORT_ENABLE 0x00000004
+#define PORTSCX_PORT_EN_DIS_CHANGE 0x00000008
+#define PORTSCX_OVER_CURRENT_ACT 0x00000010
+#define PORTSCX_OVER_CURRENT_CHG 0x00000020
+#define PORTSCX_PORT_FORCE_RESUME 0x00000040
+#define PORTSCX_PORT_SUSPEND 0x00000080
+#define PORTSCX_PORT_RESET 0x00000100
+#define PORTSCX_LINE_STATUS_BITS 0x00000C00
+#define PORTSCX_PORT_POWER 0x00001000
+#define PORTSCX_PORT_INDICTOR_CTRL 0x0000C000
+#define PORTSCX_PORT_TEST_CTRL 0x000F0000
+#define PORTSCX_WAKE_ON_CONNECT_EN 0x00100000
+#define PORTSCX_WAKE_ON_CONNECT_DIS 0x00200000
+#define PORTSCX_WAKE_ON_OVER_CURRENT 0x00400000
+#define PORTSCX_PHY_LOW_POWER_SPD 0x00800000
+#define PORTSCX_PORT_FORCE_FULL_SPEED 0x01000000
+#define PORTSCX_PORT_SPEED_MASK 0x0C000000
+#define PORTSCX_PORT_WIDTH 0x10000000
+#define PORTSCX_PHY_TYPE_SEL 0xC0000000
+
+/* bit 11-10 are line status */
+#define PORTSCX_LINE_STATUS_SE0 0x00000000
+#define PORTSCX_LINE_STATUS_JSTATE 0x00000400
+#define PORTSCX_LINE_STATUS_KSTATE 0x00000800
+#define PORTSCX_LINE_STATUS_UNDEF 0x00000C00
+#define PORTSCX_LINE_STATUS_BIT_POS 10
+
+/* bit 15-14 are port indicator control */
+#define PORTSCX_PIC_OFF 0x00000000
+#define PORTSCX_PIC_AMBER 0x00004000
+#define PORTSCX_PIC_GREEN 0x00008000
+#define PORTSCX_PIC_UNDEF 0x0000C000
+#define PORTSCX_PIC_BIT_POS 14
+
+/* bit 19-16 are port test control */
+#define PORTSCX_PTC_DISABLE 0x00000000
+#define PORTSCX_PTC_JSTATE 0x00010000
+#define PORTSCX_PTC_KSTATE 0x00020000
+#define PORTSCX_PTC_SEQNAK 0x00030000
+#define PORTSCX_PTC_PACKET 0x00040000
+#define PORTSCX_PTC_FORCE_EN 0x00050000
+#define PORTSCX_PTC_BIT_POS 16
+
+/* bit 27-26 are port speed */
+#define PORTSCX_PORT_SPEED_FULL 0x00000000
+#define PORTSCX_PORT_SPEED_LOW 0x04000000
+#define PORTSCX_PORT_SPEED_HIGH 0x08000000
+#define PORTSCX_PORT_SPEED_UNDEF 0x0C000000
+#define PORTSCX_SPEED_BIT_POS 26
+
+/* bit 28 is parallel transceiver width for UTMI interface */
+#define PORTSCX_PTW 0x10000000
+#define PORTSCX_PTW_8BIT 0x00000000
+#define PORTSCX_PTW_16BIT 0x10000000
+
+/* bit 31-30 are port transceiver select */
+#define PORTSCX_PTS_UTMI 0x00000000
+#define PORTSCX_PTS_ULPI 0x80000000
+#define PORTSCX_PTS_FSLS 0xC0000000
+#define PORTSCX_PTS_BIT_POS 30
+
+/* otgsc Register Bit Masks */
+#define OTGSC_CTRL_VUSB_DISCHARGE 0x00000001
+#define OTGSC_CTRL_VUSB_CHARGE 0x00000002
+#define OTGSC_CTRL_OTG_TERM 0x00000008
+#define OTGSC_CTRL_DATA_PULSING 0x00000010
+#define OTGSC_STS_USB_ID 0x00000100
+#define OTGSC_STS_A_VBUS_VALID 0x00000200
+#define OTGSC_STS_A_SESSION_VALID 0x00000400
+#define OTGSC_STS_B_SESSION_VALID 0x00000800
+#define OTGSC_STS_B_SESSION_END 0x00001000
+#define OTGSC_STS_1MS_TOGGLE 0x00002000
+#define OTGSC_STS_DATA_PULSING 0x00004000
+#define OTGSC_INTSTS_USB_ID 0x00010000
+#define OTGSC_INTSTS_A_VBUS_VALID 0x00020000
+#define OTGSC_INTSTS_A_SESSION_VALID 0x00040000
+#define OTGSC_INTSTS_B_SESSION_VALID 0x00080000
+#define OTGSC_INTSTS_B_SESSION_END 0x00100000
+#define OTGSC_INTSTS_1MS 0x00200000
+#define OTGSC_INTSTS_DATA_PULSING 0x00400000
+#define OTGSC_INTR_USB_ID 0x01000000
+#define OTGSC_INTR_A_VBUS_VALID 0x02000000
+#define OTGSC_INTR_A_SESSION_VALID 0x04000000
+#define OTGSC_INTR_B_SESSION_VALID 0x08000000
+#define OTGSC_INTR_B_SESSION_END 0x10000000
+#define OTGSC_INTR_1MS_TIMER 0x20000000
+#define OTGSC_INTR_DATA_PULSING 0x40000000
+
+/* USB MODE Register Bit Masks */
+#define USB_MODE_CTRL_MODE_IDLE 0x00000000
+#define USB_MODE_CTRL_MODE_DEVICE 0x00000002
+#define USB_MODE_CTRL_MODE_HOST 0x00000003
+#define USB_MODE_CTRL_MODE_RSV 0x00000001
+#define USB_MODE_SETUP_LOCK_OFF 0x00000008
+#define USB_MODE_STREAM_DISABLE 0x00000010
+/* Endpoint Flush Register */
+#define EPFLUSH_TX_OFFSET 0x00010000
+#define EPFLUSH_RX_OFFSET 0x00000000
+
+/* Endpoint Setup Status bit masks */
+#define EP_SETUP_STATUS_MASK 0x0000003F
+#define EP_SETUP_STATUS_EP0 0x00000001
+
+/* ENDPOINTCTRLx Register Bit Masks */
+#define EPCTRL_TX_ENABLE 0x00800000
+#define EPCTRL_TX_DATA_TOGGLE_RST 0x00400000 /* Not EP0 */
+#define EPCTRL_TX_DATA_TOGGLE_INH 0x00200000 /* Not EP0 */
+#define EPCTRL_TX_TYPE 0x000C0000
+#define EPCTRL_TX_DATA_SOURCE 0x00020000 /* Not EP0 */
+#define EPCTRL_TX_EP_STALL 0x00010000
+#define EPCTRL_RX_ENABLE 0x00000080
+#define EPCTRL_RX_DATA_TOGGLE_RST 0x00000040 /* Not EP0 */
+#define EPCTRL_RX_DATA_TOGGLE_INH 0x00000020 /* Not EP0 */
+#define EPCTRL_RX_TYPE 0x0000000C
+#define EPCTRL_RX_DATA_SINK 0x00000002 /* Not EP0 */
+#define EPCTRL_RX_EP_STALL 0x00000001
+
+/* bit 19-18 and 3-2 are endpoint type */
+#define EPCTRL_EP_TYPE_CONTROL 0
+#define EPCTRL_EP_TYPE_ISO 1
+#define EPCTRL_EP_TYPE_BULK 2
+#define EPCTRL_EP_TYPE_INTERRUPT 3
+#define EPCTRL_TX_EP_TYPE_SHIFT 18
+#define EPCTRL_RX_EP_TYPE_SHIFT 2
+
+/* SNOOPn Register Bit Masks */
+#define SNOOP_ADDRESS_MASK 0xFFFFF000
+#define SNOOP_SIZE_ZERO 0x00 /* snooping disable */
+#define SNOOP_SIZE_4KB 0x0B /* 4KB snoop size */
+#define SNOOP_SIZE_8KB 0x0C
+#define SNOOP_SIZE_16KB 0x0D
+#define SNOOP_SIZE_32KB 0x0E
+#define SNOOP_SIZE_64KB 0x0F
+#define SNOOP_SIZE_128KB 0x10
+#define SNOOP_SIZE_256KB 0x11
+#define SNOOP_SIZE_512KB 0x12
+#define SNOOP_SIZE_1MB 0x13
+#define SNOOP_SIZE_2MB 0x14
+#define SNOOP_SIZE_4MB 0x15
+#define SNOOP_SIZE_8MB 0x16
+#define SNOOP_SIZE_16MB 0x17
+#define SNOOP_SIZE_32MB 0x18
+#define SNOOP_SIZE_64MB 0x19
+#define SNOOP_SIZE_128MB 0x1A
+#define SNOOP_SIZE_256MB 0x1B
+#define SNOOP_SIZE_512MB 0x1C
+#define SNOOP_SIZE_1GB 0x1D
+#define SNOOP_SIZE_2GB 0x1E /* 2GB snoop size */
+
+/* pri_ctrl Register Bit Masks */
+#define PRI_CTRL_PRI_LVL1 0x0000000C
+#define PRI_CTRL_PRI_LVL0 0x00000003
+
+/* si_ctrl Register Bit Masks */
+#define SI_CTRL_ERR_DISABLE 0x00000010
+#define SI_CTRL_IDRC_DISABLE 0x00000008
+#define SI_CTRL_RD_SAFE_EN 0x00000004
+#define SI_CTRL_RD_PREFETCH_DISABLE 0x00000002
+#define SI_CTRL_RD_PREFEFETCH_VAL 0x00000001
+
+/* control Register Bit Masks */
+#define USB_CTRL_IOENB 0x00000004
+#define USB_CTRL_ULPI_INT0EN 0x00000001
+
+/* Endpoint Queue Head data struct
+ * Rem: all the variables of qh are LittleEndian Mode
+ * and NEXT_POINTER_MASK should operate on a LittleEndian, Phy Addr
+ */
+struct ep_queue_head {
+ u32 max_pkt_length; /* Mult(31-30) , Zlt(29) , Max Pkt len
+ and IOS(15) */
+ u32 curr_dtd_ptr; /* Current dTD Pointer(31-5) */
+ u32 next_dtd_ptr; /* Next dTD Pointer(31-5), T(0) */
+ u32 size_ioc_int_sts; /* Total bytes (30-16), IOC (15),
+ MultO(11-10), STS (7-0) */
+ u32 buff_ptr0; /* Buffer pointer Page 0 (31-12) */
+ u32 buff_ptr1; /* Buffer pointer Page 1 (31-12) */
+ u32 buff_ptr2; /* Buffer pointer Page 2 (31-12) */
+ u32 buff_ptr3; /* Buffer pointer Page 3 (31-12) */
+ u32 buff_ptr4; /* Buffer pointer Page 4 (31-12) */
+ u32 res1;
+ u8 setup_buffer[8]; /* Setup data 8 bytes */
+ u32 res2[4];
+};
+
+/* Endpoint Queue Head Bit Masks */
+#define EP_QUEUE_HEAD_MULT_POS 30
+#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000
+#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16
+#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff)
+#define EP_QUEUE_HEAD_IOS 0x00008000
+#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001
+#define EP_QUEUE_HEAD_IOC 0x00008000
+#define EP_QUEUE_HEAD_MULTO 0x00000C00
+#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040
+#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080
+#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF
+#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0
+#define EP_QUEUE_FRINDEX_MASK 0x000007FF
+#define EP_MAX_LENGTH_TRANSFER 0x4000
+
+/* Endpoint Transfer Descriptor data struct */
+/* Rem: all the variables of td are LittleEndian Mode */
+struct ep_td_struct {
+ u32 next_td_ptr; /* Next TD pointer(31-5), T(0) set
+ indicate invalid */
+ u32 size_ioc_sts; /* Total bytes (30-16), IOC (15),
+ MultO(11-10), STS (7-0) */
+ u32 buff_ptr0; /* Buffer pointer Page 0 */
+ u32 buff_ptr1; /* Buffer pointer Page 1 */
+ u32 buff_ptr2; /* Buffer pointer Page 2 */
+ u32 buff_ptr3; /* Buffer pointer Page 3 */
+ u32 buff_ptr4; /* Buffer pointer Page 4 */
+ u32 res;
+ /* 32 bytes */
+ dma_addr_t td_dma; /* dma address for this td */
+ /* virtual address of next td specified in next_td_ptr */
+ struct ep_td_struct *next_td_virt;
+};
+
+/* Endpoint Transfer Descriptor bit Masks */
+#define DTD_NEXT_TERMINATE 0x00000001
+#define DTD_IOC 0x00008000
+#define DTD_STATUS_ACTIVE 0x00000080
+#define DTD_STATUS_HALTED 0x00000040
+#define DTD_STATUS_DATA_BUFF_ERR 0x00000020
+#define DTD_STATUS_TRANSACTION_ERR 0x00000008
+#define DTD_RESERVED_FIELDS 0x80007300
+#define DTD_ADDR_MASK 0xFFFFFFE0
+#define DTD_PACKET_SIZE 0x7FFF0000
+#define DTD_LENGTH_BIT_POS 16
+#define DTD_ERROR_MASK (DTD_STATUS_HALTED | \
+ DTD_STATUS_DATA_BUFF_ERR | \
+ DTD_STATUS_TRANSACTION_ERR)
+/* Alignment requirements; must be a power of two */
+#define DTD_ALIGNMENT 0x20
+#define QH_ALIGNMENT 2048
+
+/* Controller dma boundary */
+#define UDC_DMA_BOUNDARY 0x1000
+
+/* -----------------------------------------------------------------------*/
+/* ##### enum data
+*/
+typedef enum {
+ e_ULPI,
+ e_UTMI_8BIT,
+ e_UTMI_16BIT,
+ e_SERIAL
+} e_PhyInterface;
+
+/*-------------------------------------------------------------------------*/
+
+/* ### driver private data
+ */
+struct fsl_req {
+ struct usb_request req;
+ struct list_head queue;
+ /* ep_queue() func will add
+ a request->queue into a udc_ep->queue 'd tail */
+ struct fsl_ep *ep;
+ unsigned mapped:1;
+
+ struct ep_td_struct *head, *tail; /* For dTD List
+ cpu endian Virtual addr */
+ unsigned int dtd_count;
+};
+
+#define REQ_UNCOMPLETE 1
+
+struct fsl_ep {
+ struct usb_ep ep;
+ struct list_head queue;
+ struct fsl_udc *udc;
+ struct ep_queue_head *qh;
+ const struct usb_endpoint_descriptor *desc;
+ struct usb_gadget *gadget;
+
+ char name[14];
+ unsigned stopped:1;
+};
+
+#define EP_DIR_IN 1
+#define EP_DIR_OUT 0
+
+struct fsl_udc {
+
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+ struct fsl_ep *eps;
+ unsigned int max_ep;
+ unsigned int irq;
+
+ struct usb_ctrlrequest local_setup_buff;
+ spinlock_t lock;
+ struct otg_transceiver *transceiver;
+ unsigned softconnect:1;
+ unsigned vbus_active:1;
+ unsigned stopped:1;
+ unsigned remote_wakeup:1;
+
+ struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */
+ struct fsl_req *status_req; /* ep0 status request */
+ struct dma_pool *td_pool; /* dma pool for DTD */
+ enum fsl_usb2_phy_modes phy_mode;
+
+ size_t ep_qh_size; /* size after alignment adjustment*/
+ dma_addr_t ep_qh_dma; /* dma address of QH */
+
+ u32 max_pipes; /* Device max pipes */
+ u32 max_use_endpts; /* Max endpointes to be used */
+ u32 bus_reset; /* Device is bus reseting */
+ u32 resume_state; /* USB state to resume */
+ u32 usb_state; /* USB current state */
+ u32 usb_next_state; /* USB next state */
+ u32 ep0_state; /* Endpoint zero state */
+ u32 ep0_dir; /* Endpoint zero direction: can be
+ USB_DIR_IN or USB_DIR_OUT */
+ u32 usb_sof_count; /* SOF count */
+ u32 errors; /* USB ERRORs count */
+ u8 device_address; /* Device USB address */
+
+ struct completion *done; /* to make sure release() is done */
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+#define DBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt "\n", \
+ __FUNCTION__, ## args)
+#else
+#define DBG(fmt, args...) do{}while(0)
+#endif
+
+#if 0
+static void dump_msg(const char *label, const u8 * buf, unsigned int length)
+{
+ unsigned int start, num, i;
+ char line[52], *p;
+
+ if (length >= 512)
+ return;
+ DBG("%s, length %u:\n", label, length);
+ start = 0;
+ while (length > 0) {
+ num = min(length, 16u);
+ p = line;
+ for (i = 0; i < num; ++i) {
+ if (i == 8)
+ *p++ = ' ';
+ sprintf(p, " %02x", buf[i]);
+ p += 3;
+ }
+ *p = 0;
+ printk(KERN_DEBUG "%6x: %s\n", start, line);
+ buf += num;
+ start += num;
+ length -= num;
+ }
+}
+#endif
+
+#ifdef VERBOSE
+#define VDBG DBG
+#else
+#define VDBG(stuff...) do{}while(0)
+#endif
+
+#define ERR(stuff...) printk(KERN_ERR "udc: " stuff)
+#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff)
+#define INFO(stuff...) printk(KERN_INFO "udc: " stuff)
+
+/*-------------------------------------------------------------------------*/
+
+/* ### Add board specific defines here
+ */
+
+/*
+ * ### pipe direction macro from device view
+ */
+#define USB_RECV 0 /* OUT EP */
+#define USB_SEND 1 /* IN EP */
+
+/*
+ * ### internal used help routines.
+ */
+#define ep_index(EP) ((EP)->desc->bEndpointAddress&0xF)
+#define ep_maxpacket(EP) ((EP)->ep.maxpacket)
+#define ep_is_in(EP) ( (ep_index(EP) == 0) ? (EP->udc->ep0_dir == \
+ USB_DIR_IN ):((EP)->desc->bEndpointAddress \
+ & USB_DIR_IN)==USB_DIR_IN)
+#define get_ep_by_pipe(udc, pipe) ((pipe == 1)? &udc->eps[0]: \
+ &udc->eps[pipe])
+#define get_pipe_by_windex(windex) ((windex & USB_ENDPOINT_NUMBER_MASK) \
+ * 2 + ((windex & USB_DIR_IN) ? 1 : 0))
+#define get_pipe_by_ep(EP) (ep_index(EP) * 2 + ep_is_in(EP))
+
+#endif
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index 2e3d6620d21..d041b919e7b 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -99,6 +99,12 @@
#define gadget_is_imx(g) 0
#endif
+#ifdef CONFIG_USB_GADGET_FSL_USB2
+#define gadget_is_fsl_usb2(g) !strcmp("fsl-usb2-udc", (g)->name)
+#else
+#define gadget_is_fsl_usb2(g) 0
+#endif
+
/* Mentor high speed function controller */
#ifdef CONFIG_USB_GADGET_MUSBHSFC
#define gadget_is_musbhsfc(g) !strcmp("musbhsfc_udc", (g)->name)
@@ -177,5 +183,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x17;
else if (gadget_is_husb2dev(gadget))
return 0x18;
+ else if (gadget_is_fsl_usb2(gadget))
+ return 0x19;
return -ENOENT;
}
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index 7b3a326b57a..65c91d3735d 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -297,27 +297,6 @@ goku_free_request(struct usb_ep *_ep, struct usb_request *_req)
/*-------------------------------------------------------------------------*/
-#undef USE_KMALLOC
-
-/* many common platforms have dma-coherent caches, which means that it's
- * safe to use kmalloc() memory for all i/o buffers without using any
- * cache flushing calls. (unless you're trying to share cache lines
- * between dma and non-dma activities, which is a slow idea in any case.)
- *
- * other platforms need more care, with 2.6 having a moderately general
- * solution except for the common "buffer is smaller than a page" case.
- */
-#if defined(CONFIG_X86)
-#define USE_KMALLOC
-
-#elif defined(CONFIG_MIPS) && !defined(CONFIG_DMA_NONCOHERENT)
-#define USE_KMALLOC
-
-#elif defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE)
-#define USE_KMALLOC
-
-#endif
-
/* allocating buffers this way eliminates dma mapping overhead, which
* on some platforms will mean eliminating a per-io buffer copy. with
* some kinds of system caches, further tweaks may still be needed.
@@ -334,11 +313,6 @@ goku_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
return NULL;
*dma = DMA_ADDR_INVALID;
-#if defined(USE_KMALLOC)
- retval = kmalloc(bytes, gfp_flags);
- if (retval)
- *dma = virt_to_phys(retval);
-#else
if (ep->dma) {
/* the main problem with this call is that it wastes memory
* on typical 1/N page allocations: it allocates 1-N pages.
@@ -348,7 +322,6 @@ goku_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
bytes, dma, gfp_flags);
} else
retval = kmalloc(bytes, gfp_flags);
-#endif
return retval;
}
@@ -356,7 +329,6 @@ static void
goku_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes)
{
/* free memory into the right allocator */
-#ifndef USE_KMALLOC
if (dma != DMA_ADDR_INVALID) {
struct goku_ep *ep;
@@ -365,7 +337,6 @@ goku_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes)
return;
dma_free_coherent(&ep->dev->pdev->dev, bytes, buf, dma);
} else
-#endif
kfree (buf);
}
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index 34296e79edc..188c74a9521 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -553,6 +553,7 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb)
{
struct kiocb_priv *priv = iocb->private;
ssize_t len, total;
+ void *to_copy;
int i;
/* we "retry" to get the right mm context for this: */
@@ -560,10 +561,11 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb)
/* copy stuff into user buffers */
total = priv->actual;
len = 0;
+ to_copy = priv->buf;
for (i=0; i < priv->nr_segs; i++) {
ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total);
- if (copy_to_user(priv->iv[i].iov_base, priv->buf, this)) {
+ if (copy_to_user(priv->iv[i].iov_base, to_copy, this)) {
if (len == 0)
len = -EFAULT;
break;
@@ -571,6 +573,7 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb)
total -= this;
len += this;
+ to_copy += this;
if (total == 0)
break;
}
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index 8f9a2b61542..b394e63894d 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -296,6 +296,15 @@ omap_free_request(struct usb_ep *ep, struct usb_request *_req)
/*-------------------------------------------------------------------------*/
+/*
+ * dma-coherent memory allocation (for dma-capable endpoints)
+ *
+ * NOTE: the dma_*_coherent() API calls suck. Most implementations are
+ * (a) page-oriented, so small buffers lose big; and (b) asymmetric with
+ * respect to calls with irqs disabled: alloc is safe, free is not.
+ * We currently work around (b), but not (a).
+ */
+
static void *
omap_alloc_buffer(
struct usb_ep *_ep,
@@ -307,6 +316,9 @@ omap_alloc_buffer(
void *retval;
struct omap_ep *ep;
+ if (!_ep)
+ return NULL;
+
ep = container_of(_ep, struct omap_ep, ep);
if (use_dma && ep->has_dma) {
static int warned;
@@ -326,6 +338,35 @@ omap_alloc_buffer(
return retval;
}
+static DEFINE_SPINLOCK(buflock);
+static LIST_HEAD(buffers);
+
+struct free_record {
+ struct list_head list;
+ struct device *dev;
+ unsigned bytes;
+ dma_addr_t dma;
+};
+
+static void do_free(unsigned long ignored)
+{
+ spin_lock_irq(&buflock);
+ while (!list_empty(&buffers)) {
+ struct free_record *buf;
+
+ buf = list_entry(buffers.next, struct free_record, list);
+ list_del(&buf->list);
+ spin_unlock_irq(&buflock);
+
+ dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma);
+
+ spin_lock_irq(&buflock);
+ }
+ spin_unlock_irq(&buflock);
+}
+
+static DECLARE_TASKLET(deferred_free, do_free, 0);
+
static void omap_free_buffer(
struct usb_ep *_ep,
void *buf,
@@ -333,13 +374,29 @@ static void omap_free_buffer(
unsigned bytes
)
{
- struct omap_ep *ep;
+ if (!_ep) {
+ WARN_ON(1);
+ return;
+ }
- ep = container_of(_ep, struct omap_ep, ep);
- if (use_dma && _ep && ep->has_dma)
- dma_free_coherent(ep->udc->gadget.dev.parent, bytes, buf, dma);
- else
- kfree (buf);
+ /* free memory into the right allocator */
+ if (dma != DMA_ADDR_INVALID) {
+ struct omap_ep *ep;
+ struct free_record *rec = buf;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct omap_ep, ep);
+
+ rec->dev = ep->udc->gadget.dev.parent;
+ rec->bytes = bytes;
+ rec->dma = dma;
+
+ spin_lock_irqsave(&buflock, flags);
+ list_add_tail(&rec->list, &buffers);
+ tasklet_schedule(&deferred_free);
+ spin_unlock_irqrestore(&buflock, flags);
+ } else
+ kfree(buf);
}
/*-------------------------------------------------------------------------*/
@@ -1691,12 +1748,38 @@ ep0out_status_stage:
udc->ep0_pending = 0;
break;
case USB_REQ_GET_STATUS:
+ /* USB_ENDPOINT_HALT status? */
+ if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT))
+ goto intf_status;
+
+ /* ep0 never stalls */
+ if (!(w_index & 0xf))
+ goto zero_status;
+
+ /* only active endpoints count */
+ ep = &udc->ep[w_index & 0xf];
+ if (w_index & USB_DIR_IN)
+ ep += 16;
+ if (!ep->desc)
+ goto do_stall;
+
+ /* iso never stalls */
+ if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)
+ goto zero_status;
+
+ /* FIXME don't assume non-halted endpoints!! */
+ ERR("%s status, can't report\n", ep->ep.name);
+ goto do_stall;
+
+intf_status:
/* return interface status. if we were pedantic,
* we'd detect non-existent interfaces, and stall.
*/
if (u.r.bRequestType
!= (USB_DIR_IN|USB_RECIP_INTERFACE))
goto delegate;
+
+zero_status:
/* return two zero bytes */
UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR;
UDC_DATA_REG = 0;
@@ -2068,7 +2151,7 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev)
/*-------------------------------------------------------------------------*/
-static inline int machine_needs_vbus_session(void)
+static inline int machine_without_vbus_sense(void)
{
return (machine_is_omap_innovator()
|| machine_is_omap_osk()
@@ -2156,7 +2239,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver)
/* boards that don't have VBUS sensing can't autogate 48MHz;
* can't enter deep sleep while a gadget driver is active.
*/
- if (machine_needs_vbus_session())
+ if (machine_without_vbus_sense())
omap_vbus_session(&udc->gadget, 1);
done:
@@ -2179,7 +2262,7 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
if (udc->dc_clk != NULL)
omap_udc_enable_clock(1);
- if (machine_needs_vbus_session())
+ if (machine_without_vbus_sense())
omap_vbus_session(&udc->gadget, 0);
if (udc->transceiver)
@@ -2822,7 +2905,7 @@ static int __init omap_udc_probe(struct platform_device *pdev)
hmc = HMC_1510;
type = "(unknown)";
- if (machine_is_omap_innovator() || machine_is_sx1()) {
+ if (machine_without_vbus_sense()) {
/* just set up software VBUS detect, and then
* later rig it so we always report VBUS.
* FIXME without really sensing VBUS, we can't
diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c
index 27904a56494..2c043a1ea15 100644
--- a/drivers/usb/gadget/pxa2xx_udc.c
+++ b/drivers/usb/gadget/pxa2xx_udc.c
@@ -71,7 +71,7 @@
* by the host to interact with this device, and allocates endpoints to
* the different protocol interfaces. The controller driver virtualizes
* usb hardware so that the gadget drivers will be more portable.
- *
+ *
* This UDC hardware wants to implement a bit too much USB protocol, so
* it constrains the sorts of USB configuration change events that work.
* The errata for these chips are misleading; some "fixed" bugs from
@@ -141,7 +141,7 @@ MODULE_PARM_DESC (fifo_mode, "pxa2xx udc fifo mode");
#endif
/* ---------------------------------------------------------------------------
- * endpoint related parts of the api to the usb controller hardware,
+ * endpoint related parts of the api to the usb controller hardware,
* used by gadget driver; and the inner talker-to-hardware core.
* ---------------------------------------------------------------------------
*/
@@ -155,7 +155,7 @@ static int is_vbus_present(void)
struct pxa2xx_udc_mach_info *mach = the_controller->mach;
if (mach->gpio_vbus)
- return pxa_gpio_get(mach->gpio_vbus);
+ return udc_gpio_get(mach->gpio_vbus);
if (mach->udc_is_connected)
return mach->udc_is_connected();
return 1;
@@ -167,7 +167,7 @@ static void pullup_off(void)
struct pxa2xx_udc_mach_info *mach = the_controller->mach;
if (mach->gpio_pullup)
- pxa_gpio_set(mach->gpio_pullup, 0);
+ udc_gpio_set(mach->gpio_pullup, 0);
else if (mach->udc_command)
mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
}
@@ -177,7 +177,7 @@ static void pullup_on(void)
struct pxa2xx_udc_mach_info *mach = the_controller->mach;
if (mach->gpio_pullup)
- pxa_gpio_set(mach->gpio_pullup, 1);
+ udc_gpio_set(mach->gpio_pullup, 1);
else if (mach->udc_command)
mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
}
@@ -293,7 +293,7 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep,
#ifdef USE_DMA
/* for (some) bulk and ISO endpoints, try to get a DMA channel and
- * bind it to the endpoint. otherwise use PIO.
+ * bind it to the endpoint. otherwise use PIO.
*/
switch (ep->bmAttributes) {
case USB_ENDPOINT_XFER_ISOC:
@@ -304,7 +304,7 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep,
if (!use_dma || !ep->reg_drcmr)
break;
ep->dma = pxa_request_dma ((char *)_ep->name,
- (le16_to_cpu (desc->wMaxPacketSize) > 64)
+ (le16_to_cpu (desc->wMaxPacketSize) > 64)
? DMA_PRIO_MEDIUM /* some iso */
: DMA_PRIO_LOW,
dma_nodesc_handler, ep);
@@ -361,7 +361,7 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep)
*/
/*
- * pxa2xx_ep_alloc_request - allocate a request data structure
+ * pxa2xx_ep_alloc_request - allocate a request data structure
*/
static struct usb_request *
pxa2xx_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
@@ -378,7 +378,7 @@ pxa2xx_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
/*
- * pxa2xx_ep_free_request - deallocate a request data structure
+ * pxa2xx_ep_free_request - deallocate a request data structure
*/
static void
pxa2xx_ep_free_request (struct usb_ep *_ep, struct usb_request *_req)
@@ -1031,7 +1031,7 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
/*
- * nuke - dequeue ALL requests
+ * nuke - dequeue ALL requests
*/
static void nuke(struct pxa2xx_ep *ep, int status)
{
@@ -1136,16 +1136,16 @@ static int pxa2xx_ep_set_halt(struct usb_ep *_ep, int value)
ep->dev->req_pending = 0;
ep->dev->ep0state = EP0_STALL;
- /* and bulk/intr endpoints like dropping stalls too */
- } else {
- unsigned i;
- for (i = 0; i < 1000; i += 20) {
- if (*ep->reg_udccs & UDCCS_BI_SST)
- break;
- udelay(20);
- }
- }
- local_irq_restore(flags);
+ /* and bulk/intr endpoints like dropping stalls too */
+ } else {
+ unsigned i;
+ for (i = 0; i < 1000; i += 20) {
+ if (*ep->reg_udccs & UDCCS_BI_SST)
+ break;
+ udelay(20);
+ }
+ }
+ local_irq_restore(flags);
DBG(DBG_VERBOSE, "%s halt\n", _ep->name);
return 0;
@@ -1216,7 +1216,7 @@ static struct usb_ep_ops pxa2xx_ep_ops = {
/* ---------------------------------------------------------------------------
- * device-scoped parts of the api to the usb controller hardware
+ * device-scoped parts of the api to the usb controller hardware
* ---------------------------------------------------------------------------
*/
@@ -1239,7 +1239,7 @@ static void udc_enable (struct pxa2xx_udc *);
static void udc_disable(struct pxa2xx_udc *);
/* We disable the UDC -- and its 48 MHz clock -- whenever it's not
- * in active use.
+ * in active use.
*/
static int pullup(struct pxa2xx_udc *udc, int is_active)
{
@@ -1464,24 +1464,10 @@ done:
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
-/* "function" sysfs attribute */
-static ssize_t
-show_function (struct device *_dev, struct device_attribute *attr, char *buf)
-{
- struct pxa2xx_udc *dev = dev_get_drvdata (_dev);
-
- if (!dev->driver
- || !dev->driver->function
- || strlen (dev->driver->function) > PAGE_SIZE)
- return 0;
- return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);
-}
-static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
-
/*-------------------------------------------------------------------------*/
/*
- * udc_disable - disable USB device controller
+ * udc_disable - disable USB device controller
*/
static void udc_disable(struct pxa2xx_udc *dev)
{
@@ -1507,7 +1493,7 @@ static void udc_disable(struct pxa2xx_udc *dev)
/*
- * udc_reinit - initialize software state
+ * udc_reinit - initialize software state
*/
static void udc_reinit(struct pxa2xx_udc *dev)
{
@@ -1635,18 +1621,20 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
dev->gadget.dev.driver = &driver->driver;
dev->pullup = 1;
- device_add (&dev->gadget.dev);
+ retval = device_add (&dev->gadget.dev);
+ if (retval) {
+fail:
+ dev->driver = NULL;
+ dev->gadget.dev.driver = NULL;
+ return retval;
+ }
retval = driver->bind(&dev->gadget);
if (retval) {
DMSG("bind to driver %s --> error %d\n",
driver->driver.name, retval);
device_del (&dev->gadget.dev);
-
- dev->driver = NULL;
- dev->gadget.dev.driver = NULL;
- return retval;
+ goto fail;
}
- device_create_file(dev->dev, &dev_attr_function);
/* ... then enable host detection and ep0; and we're ready
* for set_configuration as well as eventual disconnect.
@@ -1704,7 +1692,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
dev->driver = NULL;
device_del (&dev->gadget.dev);
- device_remove_file(dev->dev, &dev_attr_function);
DMSG("unregistered gadget driver '%s'\n", driver->driver.name);
dump_state(dev);
@@ -1755,7 +1742,7 @@ lubbock_vbus_irq(int irq, void *_dev)
static irqreturn_t udc_vbus_irq(int irq, void *_dev)
{
struct pxa2xx_udc *dev = _dev;
- int vbus = pxa_gpio_get(dev->mach->gpio_vbus);
+ int vbus = udc_gpio_get(dev->mach->gpio_vbus);
pxa2xx_udc_vbus_session(&dev->gadget, vbus);
return IRQ_HANDLED;
@@ -2474,12 +2461,12 @@ static struct pxa2xx_udc memory = {
#define IXP465_AD 0x00000200
/*
- * probe - binds to the platform device
+ * probe - binds to the platform device
*/
static int __init pxa2xx_udc_probe(struct platform_device *pdev)
{
struct pxa2xx_udc *dev = &memory;
- int retval, out_dma = 1, vbus_irq;
+ int retval, out_dma = 1, vbus_irq, irq;
u32 chiprev;
/* insist on Intel/ARM/XScale */
@@ -2522,7 +2509,11 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
return -ENODEV;
}
- pr_debug("%s: IRQ %d%s%s%s\n", driver_name, IRQ_USB,
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -ENODEV;
+
+ pr_debug("%s: IRQ %d%s%s%s\n", driver_name, irq,
dev->has_cfr ? "" : " (!cfr)",
out_dma ? "" : " (broken dma-out)",
SIZE_STR DMASTR
@@ -2545,15 +2536,13 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
dev->dev = &pdev->dev;
dev->mach = pdev->dev.platform_data;
if (dev->mach->gpio_vbus) {
- vbus_irq = IRQ_GPIO(dev->mach->gpio_vbus & GPIO_MD_MASK_NR);
- pxa_gpio_mode((dev->mach->gpio_vbus & GPIO_MD_MASK_NR)
- | GPIO_IN);
+ udc_gpio_init_vbus(dev->mach->gpio_vbus);
+ vbus_irq = udc_gpio_to_irq(dev->mach->gpio_vbus);
set_irq_type(vbus_irq, IRQT_BOTHEDGE);
} else
vbus_irq = 0;
if (dev->mach->gpio_pullup)
- pxa_gpio_mode((dev->mach->gpio_pullup & GPIO_MD_MASK_NR)
- | GPIO_OUT | GPIO_DFLT_LOW);
+ udc_gpio_init_pullup(dev->mach->gpio_pullup);
init_timer(&dev->timer);
dev->timer.function = udc_watchdog;
@@ -2572,11 +2561,11 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
dev->vbus = is_vbus_present();
/* irq setup after old hardware state is cleaned up */
- retval = request_irq(IRQ_USB, pxa2xx_udc_irq,
+ retval = request_irq(irq, pxa2xx_udc_irq,
IRQF_DISABLED, driver_name, dev);
if (retval != 0) {
- printk(KERN_ERR "%s: can't get irq %i, err %d\n",
- driver_name, IRQ_USB, retval);
+ printk(KERN_ERR "%s: can't get irq %d, err %d\n",
+ driver_name, irq, retval);
return -EBUSY;
}
dev->got_irq = 1;
@@ -2591,7 +2580,7 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
printk(KERN_ERR "%s: can't get irq %i, err %d\n",
driver_name, LUBBOCK_USB_DISC_IRQ, retval);
lubbock_fail0:
- free_irq(IRQ_USB, dev);
+ free_irq(irq, dev);
return -EBUSY;
}
retval = request_irq(LUBBOCK_USB_IRQ,
@@ -2618,7 +2607,7 @@ lubbock_fail0:
if (retval != 0) {
printk(KERN_ERR "%s: can't get irq %i, err %d\n",
driver_name, vbus_irq, retval);
- free_irq(IRQ_USB, dev);
+ free_irq(irq, dev);
return -EBUSY;
}
}
@@ -2643,7 +2632,7 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
remove_proc_files();
if (dev->got_irq) {
- free_irq(IRQ_USB, dev);
+ free_irq(platform_get_irq(pdev, 0), dev);
dev->got_irq = 0;
}
#ifdef CONFIG_ARCH_LUBBOCK
@@ -2670,7 +2659,7 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
*
* For now, we punt and forcibly disconnect from the USB host when PXA
* enters any suspend state. While we're disconnected, we always disable
- * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states.
+ * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states.
* Boards without software pullup control shouldn't use those states.
* VBUS IRQs should probably be ignored so that the PXA device just acts
* "dead" to USB hosts until system resume.
@@ -2703,7 +2692,6 @@ static int pxa2xx_udc_resume(struct platform_device *dev)
/*-------------------------------------------------------------------------*/
static struct platform_driver udc_driver = {
- .probe = pxa2xx_udc_probe,
.shutdown = pxa2xx_udc_shutdown,
.remove = __exit_p(pxa2xx_udc_remove),
.suspend = pxa2xx_udc_suspend,
@@ -2717,7 +2705,7 @@ static struct platform_driver udc_driver = {
static int __init udc_init(void)
{
printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION);
- return platform_driver_register(&udc_driver);
+ return platform_driver_probe(&udc_driver, pxa2xx_udc_probe);
}
module_init(udc_init);
diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h
index 8e598c8bf4e..773e549aff3 100644
--- a/drivers/usb/gadget/pxa2xx_udc.h
+++ b/drivers/usb/gadget/pxa2xx_udc.h
@@ -177,21 +177,6 @@ struct pxa2xx_udc {
static struct pxa2xx_udc *the_controller;
-static inline int pxa_gpio_get(unsigned gpio)
-{
- return (GPLR(gpio) & GPIO_bit(gpio)) != 0;
-}
-
-static inline void pxa_gpio_set(unsigned gpio, int is_on)
-{
- int mask = GPIO_bit(gpio);
-
- if (is_on)
- GPSR(gpio) = mask;
- else
- GPCR(gpio) = mask;
-}
-
/*-------------------------------------------------------------------------*/
/*
diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h
index 4c3c7259f01..397b149f3ca 100644
--- a/drivers/usb/gadget/rndis.h
+++ b/drivers/usb/gadget/rndis.h
@@ -195,7 +195,7 @@ struct rndis_packet_msg_type
__le32 PerPacketInfoLength;
__le32 VcHandle;
__le32 Reserved;
-};
+} __attribute__ ((packed));
struct rndis_config_parameter
{
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index e6c19aa4bef..e552668d36b 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -1699,6 +1699,7 @@ static int gs_setup_class(struct usb_gadget *gadget,
memcpy(&port->port_line_coding, req->buf, ret);
spin_unlock(&port->port_lock);
}
+ ret = 0;
break;
case USB_CDC_REQ_GET_LINE_CODING:
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index a2e58c86849..2ff396bd180 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -15,4 +15,3 @@ obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
-obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 246afea9e83..43eddaecc3d 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -322,7 +322,7 @@ static inline void remove_debug_files (struct ehci_hcd *bus) { }
#else
-/* troubleshooting help: expose state in driverfs */
+/* troubleshooting help: expose state in sysfs */
#define speed_char(info1) ({ char tmp; \
switch (info1 & (3 << 12)) { \
diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h
index caac0d1967d..f28736a917e 100644
--- a/drivers/usb/host/ehci-fsl.h
+++ b/drivers/usb/host/ehci-fsl.h
@@ -31,7 +31,7 @@
#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */
#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */
#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */
-#define FSL_SOC_USB_SICTRL 0x40c /* NOTE: big-endian */
-#define FSL_SOC_USB_PRICTRL 0x410 /* NOTE: big-endian */
+#define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */
+#define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */
#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */
#endif /* _EHCI_FSL_H */
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 185721dba42..c7458f7e56c 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -42,6 +42,9 @@
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
+#ifdef CONFIG_PPC_PS3
+#include <asm/firmware.h>
+#endif
/*-------------------------------------------------------------------------*/
@@ -299,6 +302,19 @@ static void ehci_watchdog (unsigned long param)
spin_unlock_irqrestore (&ehci->lock, flags);
}
+/* On some systems, leaving remote wakeup enabled prevents system shutdown.
+ * The firmware seems to think that powering off is a wakeup event!
+ * This routine turns off remote wakeup and everything else, on all ports.
+ */
+static void ehci_turn_off_all_ports(struct ehci_hcd *ehci)
+{
+ int port = HCS_N_PORTS(ehci->hcs_params);
+
+ while (port--)
+ ehci_writel(ehci, PORT_RWC_BITS,
+ &ehci->regs->port_status[port]);
+}
+
/* ehci_shutdown kick in for silicon on any bus (not just pci, etc).
* This forcibly disables dma and IRQs, helping kexec and other cases
* where the next system software may expect clean state.
@@ -310,9 +326,13 @@ ehci_shutdown (struct usb_hcd *hcd)
ehci = hcd_to_ehci (hcd);
(void) ehci_halt (ehci);
+ ehci_turn_off_all_ports(ehci);
/* make BIOS/etc use companion controller during reboot */
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
+
+ /* unblock posted writes */
+ ehci_readl(ehci, &ehci->regs->configured_flag);
}
static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
@@ -649,6 +669,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
*/
ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
+ mod_timer(&hcd->rh_timer, ehci->reset_done[i]);
}
}
@@ -951,15 +972,18 @@ static int __init ehci_hcd_init(void)
#endif
#ifdef PS3_SYSTEM_BUS_DRIVER
- retval = ps3_system_bus_driver_register(&PS3_SYSTEM_BUS_DRIVER);
- if (retval < 0) {
+ if (firmware_has_feature(FW_FEATURE_PS3_LV1)) {
+ retval = ps3_system_bus_driver_register(
+ &PS3_SYSTEM_BUS_DRIVER);
+ if (retval < 0) {
#ifdef PLATFORM_DRIVER
- platform_driver_unregister(&PLATFORM_DRIVER);
+ platform_driver_unregister(&PLATFORM_DRIVER);
#endif
#ifdef PCI_DRIVER
- pci_unregister_driver(&PCI_DRIVER);
+ pci_unregister_driver(&PCI_DRIVER);
#endif
- return retval;
+ return retval;
+ }
}
#endif
@@ -976,7 +1000,8 @@ static void __exit ehci_hcd_cleanup(void)
pci_unregister_driver(&PCI_DRIVER);
#endif
#ifdef PS3_SYSTEM_BUS_DRIVER
- ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
+ if (firmware_has_feature(FW_FEATURE_PS3_LV1))
+ ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
#endif
}
module_exit(ehci_hcd_cleanup);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 0d83c6df1a3..f4d301bc83b 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -36,6 +36,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
int port;
int mask;
+ ehci_dbg(ehci, "suspend root hub\n");
+
if (time_before (jiffies, ehci->next_statechange))
msleep(5);
@@ -134,6 +136,10 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
/* restore CMD_RUN, framelist size, and irq threshold */
ehci_writel(ehci, ehci->command, &ehci->regs->command);
+ /* Some controller/firmware combinations need a delay during which
+ * they set up the port statuses. See Bugzilla #8190. */
+ mdelay(8);
+
/* manually resume the ports we suspended during bus_suspend() */
i = HCS_N_PORTS (ehci->hcs_params);
while (i--) {
@@ -651,8 +657,7 @@ static int ehci_hub_control (
if (status & ~0xffff) /* only if wPortChange is interesting */
#endif
dbg_port (ehci, "GetStatus", wIndex + 1, temp);
- // we "know" this alignment is good, caller used kmalloc()...
- *((__le32 *) buf) = cpu_to_le32 (status);
+ put_unaligned(cpu_to_le32 (status), (__le32 *) buf);
break;
case SetHubFeature:
switch (wValue) {
diff --git a/drivers/usb/host/hc_crisv10.c b/drivers/usb/host/hc_crisv10.c
deleted file mode 100644
index 32f7caf2474..00000000000
--- a/drivers/usb/host/hc_crisv10.c
+++ /dev/null
@@ -1,4550 +0,0 @@
-/*
- * usb-host.c: ETRAX 100LX USB Host Controller Driver (HCD)
- *
- * Copyright (c) 2002, 2003 Axis Communications AB.
- */
-
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/unistd.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/spinlock.h>
-
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/dma.h>
-#include <asm/system.h>
-#include <asm/arch/svinto.h>
-
-#include <linux/usb.h>
-/* Ugly include because we don't live with the other host drivers. */
-#include <../drivers/usb/core/hcd.h>
-#include <../drivers/usb/core/usb.h>
-
-#include "hc_crisv10.h"
-
-#define ETRAX_USB_HC_IRQ USB_HC_IRQ_NBR
-#define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR
-#define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBR
-
-static const char *usb_hcd_version = "$Revision: 1.2 $";
-
-#undef KERN_DEBUG
-#define KERN_DEBUG ""
-
-
-#undef USB_DEBUG_RH
-#undef USB_DEBUG_EPID
-#undef USB_DEBUG_SB
-#undef USB_DEBUG_DESC
-#undef USB_DEBUG_URB
-#undef USB_DEBUG_TRACE
-#undef USB_DEBUG_BULK
-#undef USB_DEBUG_CTRL
-#undef USB_DEBUG_INTR
-#undef USB_DEBUG_ISOC
-
-#ifdef USB_DEBUG_RH
-#define dbg_rh(format, arg...) printk(KERN_DEBUG __FILE__ ": (RH) " format "\n" , ## arg)
-#else
-#define dbg_rh(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_EPID
-#define dbg_epid(format, arg...) printk(KERN_DEBUG __FILE__ ": (EPID) " format "\n" , ## arg)
-#else
-#define dbg_epid(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_SB
-#define dbg_sb(format, arg...) printk(KERN_DEBUG __FILE__ ": (SB) " format "\n" , ## arg)
-#else
-#define dbg_sb(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_CTRL
-#define dbg_ctrl(format, arg...) printk(KERN_DEBUG __FILE__ ": (CTRL) " format "\n" , ## arg)
-#else
-#define dbg_ctrl(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_BULK
-#define dbg_bulk(format, arg...) printk(KERN_DEBUG __FILE__ ": (BULK) " format "\n" , ## arg)
-#else
-#define dbg_bulk(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_INTR
-#define dbg_intr(format, arg...) printk(KERN_DEBUG __FILE__ ": (INTR) " format "\n" , ## arg)
-#else
-#define dbg_intr(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_ISOC
-#define dbg_isoc(format, arg...) printk(KERN_DEBUG __FILE__ ": (ISOC) " format "\n" , ## arg)
-#else
-#define dbg_isoc(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_TRACE
-#define DBFENTER (printk(": Entering: %s\n", __FUNCTION__))
-#define DBFEXIT (printk(": Exiting: %s\n", __FUNCTION__))
-#else
-#define DBFENTER do {} while (0)
-#define DBFEXIT do {} while (0)
-#endif
-
-#define usb_pipeslow(pipe) (((pipe) >> 26) & 1)
-
-/*-------------------------------------------------------------------
- Virtual Root Hub
- -------------------------------------------------------------------*/
-
-static __u8 root_hub_dev_des[] =
-{
- 0x12, /* __u8 bLength; */
- 0x01, /* __u8 bDescriptorType; Device */
- 0x00, /* __le16 bcdUSB; v1.0 */
- 0x01,
- 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
- 0x00, /* __u8 bDeviceSubClass; */
- 0x00, /* __u8 bDeviceProtocol; */
- 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
- 0x00, /* __le16 idVendor; */
- 0x00,
- 0x00, /* __le16 idProduct; */
- 0x00,
- 0x00, /* __le16 bcdDevice; */
- 0x00,
- 0x00, /* __u8 iManufacturer; */
- 0x02, /* __u8 iProduct; */
- 0x01, /* __u8 iSerialNumber; */
- 0x01 /* __u8 bNumConfigurations; */
-};
-
-/* Configuration descriptor */
-static __u8 root_hub_config_des[] =
-{
- 0x09, /* __u8 bLength; */
- 0x02, /* __u8 bDescriptorType; Configuration */
- 0x19, /* __le16 wTotalLength; */
- 0x00,
- 0x01, /* __u8 bNumInterfaces; */
- 0x01, /* __u8 bConfigurationValue; */
- 0x00, /* __u8 iConfiguration; */
- 0x40, /* __u8 bmAttributes; Bit 7: Bus-powered */
- 0x00, /* __u8 MaxPower; */
-
- /* interface */
- 0x09, /* __u8 if_bLength; */
- 0x04, /* __u8 if_bDescriptorType; Interface */
- 0x00, /* __u8 if_bInterfaceNumber; */
- 0x00, /* __u8 if_bAlternateSetting; */
- 0x01, /* __u8 if_bNumEndpoints; */
- 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
- 0x00, /* __u8 if_bInterfaceSubClass; */
- 0x00, /* __u8 if_bInterfaceProtocol; */
- 0x00, /* __u8 if_iInterface; */
-
- /* endpoint */
- 0x07, /* __u8 ep_bLength; */
- 0x05, /* __u8 ep_bDescriptorType; Endpoint */
- 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
- 0x03, /* __u8 ep_bmAttributes; Interrupt */
- 0x08, /* __le16 ep_wMaxPacketSize; 8 Bytes */
- 0x00,
- 0xff /* __u8 ep_bInterval; 255 ms */
-};
-
-static __u8 root_hub_hub_des[] =
-{
- 0x09, /* __u8 bLength; */
- 0x29, /* __u8 bDescriptorType; Hub-descriptor */
- 0x02, /* __u8 bNbrPorts; */
- 0x00, /* __u16 wHubCharacteristics; */
- 0x00,
- 0x01, /* __u8 bPwrOn2pwrGood; 2ms */
- 0x00, /* __u8 bHubContrCurrent; 0 mA */
- 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */
- 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */
-};
-
-static DEFINE_TIMER(bulk_start_timer, NULL, 0, 0);
-static DEFINE_TIMER(bulk_eot_timer, NULL, 0, 0);
-
-/* We want the start timer to expire before the eot timer, because the former might start
- traffic, thus making it unnecessary for the latter to time out. */
-#define BULK_START_TIMER_INTERVAL (HZ/10) /* 100 ms */
-#define BULK_EOT_TIMER_INTERVAL (HZ/10+2) /* 120 ms */
-
-#define OK(x) len = (x); dbg_rh("OK(%d): line: %d", x, __LINE__); break
-#define CHECK_ALIGN(x) if (((__u32)(x)) & 0x00000003) \
-{panic("Alignment check (DWORD) failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);}
-
-#define SLAB_FLAG (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
-#define KMALLOC_FLAG (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
-
-/* Most helpful debugging aid */
-#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__))))
-
-/* Alternative assert define which stops after a failed assert. */
-/*
-#define assert(expr) \
-{ \
- if (!(expr)) { \
- err("assert failed at line %d",__LINE__); \
- while (1); \
- } \
-}
-*/
-
-
-/* FIXME: Should RX_BUF_SIZE be a config option, or maybe we should adjust it dynamically?
- To adjust it dynamically we would have to get an interrupt when we reach the end
- of the rx descriptor list, or when we get close to the end, and then allocate more
- descriptors. */
-
-#define NBR_OF_RX_DESC 512
-#define RX_DESC_BUF_SIZE 1024
-#define RX_BUF_SIZE (NBR_OF_RX_DESC * RX_DESC_BUF_SIZE)
-
-/* The number of epids is, among other things, used for pre-allocating
- ctrl, bulk and isoc EP descriptors (one for each epid).
- Assumed to be > 1 when initiating the DMA lists. */
-#define NBR_OF_EPIDS 32
-
-/* Support interrupt traffic intervals up to 128 ms. */
-#define MAX_INTR_INTERVAL 128
-
-/* If periodic traffic (intr or isoc) is to be used, then one entry in the EP table
- must be "invalid". By this we mean that we shouldn't care about epid attentions
- for this epid, or at least handle them differently from epid attentions for "valid"
- epids. This define determines which one to use (don't change it). */
-#define INVALID_EPID 31
-/* A special epid for the bulk dummys. */
-#define DUMMY_EPID 30
-
-/* This is just a software cache for the valid entries in R_USB_EPT_DATA. */
-static __u32 epid_usage_bitmask;
-
-/* A bitfield to keep information on in/out traffic is needed to uniquely identify
- an endpoint on a device, since the most significant bit which indicates traffic
- direction is lacking in the ep_id field (ETRAX epids can handle both in and
- out traffic on endpoints that are otherwise identical). The USB framework, however,
- relies on them to be handled separately. For example, bulk IN and OUT urbs cannot
- be queued in the same list, since they would block each other. */
-static __u32 epid_out_traffic;
-
-/* DMA IN cache bug. Align the DMA IN buffers to 32 bytes, i.e. a cache line.
- Since RX_DESC_BUF_SIZE is 1024 is a multiple of 32, all rx buffers will be cache aligned. */
-static volatile unsigned char RxBuf[RX_BUF_SIZE] __attribute__ ((aligned (32)));
-static volatile USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4)));
-
-/* Pointers into RxDescList. */
-static volatile USB_IN_Desc_t *myNextRxDesc;
-static volatile USB_IN_Desc_t *myLastRxDesc;
-static volatile USB_IN_Desc_t *myPrevRxDesc;
-
-/* EP descriptors must be 32-bit aligned. */
-static volatile USB_EP_Desc_t TxCtrlEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
-static volatile USB_EP_Desc_t TxBulkEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
-/* After each enabled bulk EP (IN or OUT) we put two disabled EP descriptors with the eol flag set,
- causing the DMA to stop the DMA channel. The first of these two has the intr flag set, which
- gives us a dma8_sub0_descr interrupt. When we receive this, we advance the DMA one step in the
- EP list and then restart the bulk channel, thus forcing a switch between bulk EP descriptors
- in each frame. */
-static volatile USB_EP_Desc_t TxBulkDummyEPList[NBR_OF_EPIDS][2] __attribute__ ((aligned (4)));
-
-static volatile USB_EP_Desc_t TxIsocEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
-static volatile USB_SB_Desc_t TxIsocSB_zout __attribute__ ((aligned (4)));
-
-static volatile USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4)));
-static volatile USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4)));
-
-/* A zout transfer makes a memory access at the address of its buf pointer, which means that setting
- this buf pointer to 0 will cause an access to the flash. In addition to this, setting sw_len to 0
- results in a 16/32 bytes (depending on DMA burst size) transfer. Instead, we set it to 1, and point
- it to this buffer. */
-static int zout_buffer[4] __attribute__ ((aligned (4)));
-
-/* Cache for allocating new EP and SB descriptors. */
-static struct kmem_cache *usb_desc_cache;
-
-/* Cache for the registers allocated in the top half. */
-static struct kmem_cache *top_half_reg_cache;
-
-/* Cache for the data allocated in the isoc descr top half. */
-static struct kmem_cache *isoc_compl_cache;
-
-static struct usb_bus *etrax_usb_bus;
-
-/* This is a circular (double-linked) list of the active urbs for each epid.
- The head is never removed, and new urbs are linked onto the list as
- urb_entry_t elements. Don't reference urb_list directly; use the wrapper
- functions instead. Note that working with these lists might require spinlock
- protection. */
-static struct list_head urb_list[NBR_OF_EPIDS];
-
-/* Read about the need and usage of this lock in submit_ctrl_urb. */
-static spinlock_t urb_list_lock;
-
-/* Used when unlinking asynchronously. */
-static struct list_head urb_unlink_list;
-
-/* for returning string descriptors in UTF-16LE */
-static int ascii2utf (char *ascii, __u8 *utf, int utfmax)
-{
- int retval;
-
- for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) {
- *utf++ = *ascii++ & 0x7f;
- *utf++ = 0;
- }
- return retval;
-}
-
-static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
-{
- char buf [30];
-
- // assert (len > (2 * (sizeof (buf) + 1)));
- // assert (strlen (type) <= 8);
-
- // language ids
- if (id == 0) {
- *data++ = 4; *data++ = 3; /* 4 bytes data */
- *data++ = 0; *data++ = 0; /* some language id */
- return 4;
-
- // serial number
- } else if (id == 1) {
- sprintf (buf, "%x", serial);
-
- // product description
- } else if (id == 2) {
- sprintf (buf, "USB %s Root Hub", type);
-
- // id 3 == vendor description
-
- // unsupported IDs --> "stall"
- } else
- return 0;
-
- data [0] = 2 + ascii2utf (buf, data + 2, len - 2);
- data [1] = 3;
- return data [0];
-}
-
-/* Wrappers around the list functions (include/linux/list.h). */
-
-static inline int urb_list_empty(int epid)
-{
- return list_empty(&urb_list[epid]);
-}
-
-/* Returns first urb for this epid, or NULL if list is empty. */
-static inline struct urb *urb_list_first(int epid)
-{
- struct urb *first_urb = 0;
-
- if (!urb_list_empty(epid)) {
- /* Get the first urb (i.e. head->next). */
- urb_entry_t *urb_entry = list_entry((&urb_list[epid])->next, urb_entry_t, list);
- first_urb = urb_entry->urb;
- }
- return first_urb;
-}
-
-/* Adds an urb_entry last in the list for this epid. */
-static inline void urb_list_add(struct urb *urb, int epid)
-{
- urb_entry_t *urb_entry = kmalloc(sizeof(urb_entry_t), KMALLOC_FLAG);
- assert(urb_entry);
-
- urb_entry->urb = urb;
- list_add_tail(&urb_entry->list, &urb_list[epid]);
-}
-
-/* Search through the list for an element that contains this urb. (The list
- is expected to be short and the one we are about to delete will often be
- the first in the list.) */
-static inline urb_entry_t *__urb_list_entry(struct urb *urb, int epid)
-{
- struct list_head *entry;
- struct list_head *tmp;
- urb_entry_t *urb_entry;
-
- list_for_each_safe(entry, tmp, &urb_list[epid]) {
- urb_entry = list_entry(entry, urb_entry_t, list);
- assert(urb_entry);
- assert(urb_entry->urb);
-
- if (urb_entry->urb == urb) {
- return urb_entry;
- }
- }
- return 0;
-}
-
-/* Delete an urb from the list. */
-static inline void urb_list_del(struct urb *urb, int epid)
-{
- urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
- assert(urb_entry);
-
- /* Delete entry and free. */
- list_del(&urb_entry->list);
- kfree(urb_entry);
-}
-
-/* Move an urb to the end of the list. */
-static inline void urb_list_move_last(struct urb *urb, int epid)
-{
- urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
- assert(urb_entry);
-
- list_move_tail(&urb_entry->list, &urb_list[epid]);
-}
-
-/* Get the next urb in the list. */
-static inline struct urb *urb_list_next(struct urb *urb, int epid)
-{
- urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
-
- assert(urb_entry);
-
- if (urb_entry->list.next != &urb_list[epid]) {
- struct list_head *elem = urb_entry->list.next;
- urb_entry = list_entry(elem, urb_entry_t, list);
- return urb_entry->urb;
- } else {
- return NULL;
- }
-}
-
-
-
-/* For debug purposes only. */
-static inline void urb_list_dump(int epid)
-{
- struct list_head *entry;
- struct list_head *tmp;
- urb_entry_t *urb_entry;
- int i = 0;
-
- info("Dumping urb list for epid %d", epid);
-
- list_for_each_safe(entry, tmp, &urb_list[epid]) {
- urb_entry = list_entry(entry, urb_entry_t, list);
- info(" entry %d, urb = 0x%lx", i, (unsigned long)urb_entry->urb);
- }
-}
-
-static void init_rx_buffers(void);
-static int etrax_rh_unlink_urb(struct urb *urb);
-static void etrax_rh_send_irq(struct urb *urb);
-static void etrax_rh_init_int_timer(struct urb *urb);
-static void etrax_rh_int_timer_do(unsigned long ptr);
-
-static int etrax_usb_setup_epid(struct urb *urb);
-static int etrax_usb_lookup_epid(struct urb *urb);
-static int etrax_usb_allocate_epid(void);
-static void etrax_usb_free_epid(int epid);
-
-static int etrax_remove_from_sb_list(struct urb *urb);
-
-static void* etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size,
- unsigned mem_flags, dma_addr_t *dma);
-static void etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma);
-
-static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid);
-static void etrax_usb_add_to_ctrl_sb_list(struct urb *urb, int epid);
-static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid);
-static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid);
-
-static int etrax_usb_submit_bulk_urb(struct urb *urb);
-static int etrax_usb_submit_ctrl_urb(struct urb *urb);
-static int etrax_usb_submit_intr_urb(struct urb *urb);
-static int etrax_usb_submit_isoc_urb(struct urb *urb);
-
-static int etrax_usb_submit_urb(struct urb *urb, unsigned mem_flags);
-static int etrax_usb_unlink_urb(struct urb *urb, int status);
-static int etrax_usb_get_frame_number(struct usb_device *usb_dev);
-
-static irqreturn_t etrax_usb_tx_interrupt(int irq, void *vhc);
-static irqreturn_t etrax_usb_rx_interrupt(int irq, void *vhc);
-static irqreturn_t etrax_usb_hc_interrupt_top_half(int irq, void *vhc);
-static void etrax_usb_hc_interrupt_bottom_half(void *data);
-
-static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data);
-
-
-/* The following is a list of interrupt handlers for the host controller interrupts we use.
- They are called from etrax_usb_hc_interrupt_bottom_half. */
-static void etrax_usb_hc_isoc_eof_interrupt(void);
-static void etrax_usb_hc_bulk_eot_interrupt(int timer_induced);
-static void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg);
-static void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg);
-static void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg);
-
-static int etrax_rh_submit_urb (struct urb *urb);
-
-/* Forward declaration needed because they are used in the rx interrupt routine. */
-static void etrax_usb_complete_urb(struct urb *urb, int status);
-static void etrax_usb_complete_bulk_urb(struct urb *urb, int status);
-static void etrax_usb_complete_ctrl_urb(struct urb *urb, int status);
-static void etrax_usb_complete_intr_urb(struct urb *urb, int status);
-static void etrax_usb_complete_isoc_urb(struct urb *urb, int status);
-
-static int etrax_usb_hc_init(void);
-static void etrax_usb_hc_cleanup(void);
-
-static struct usb_operations etrax_usb_device_operations =
-{
- .get_frame_number = etrax_usb_get_frame_number,
- .submit_urb = etrax_usb_submit_urb,
- .unlink_urb = etrax_usb_unlink_urb,
- .buffer_alloc = etrax_usb_buffer_alloc,
- .buffer_free = etrax_usb_buffer_free
-};
-
-/* Note that these functions are always available in their "__" variants, for use in
- error situations. The "__" missing variants are controlled by the USB_DEBUG_DESC/
- USB_DEBUG_URB macros. */
-static void __dump_urb(struct urb* purb)
-{
- printk("\nurb :0x%08lx\n", (unsigned long)purb);
- printk("dev :0x%08lx\n", (unsigned long)purb->dev);
- printk("pipe :0x%08x\n", purb->pipe);
- printk("status :%d\n", purb->status);
- printk("transfer_flags :0x%08x\n", purb->transfer_flags);
- printk("transfer_buffer :0x%08lx\n", (unsigned long)purb->transfer_buffer);
- printk("transfer_buffer_length:%d\n", purb->transfer_buffer_length);
- printk("actual_length :%d\n", purb->actual_length);
- printk("setup_packet :0x%08lx\n", (unsigned long)purb->setup_packet);
- printk("start_frame :%d\n", purb->start_frame);
- printk("number_of_packets :%d\n", purb->number_of_packets);
- printk("interval :%d\n", purb->interval);
- printk("error_count :%d\n", purb->error_count);
- printk("context :0x%08lx\n", (unsigned long)purb->context);
- printk("complete :0x%08lx\n\n", (unsigned long)purb->complete);
-}
-
-static void __dump_in_desc(volatile USB_IN_Desc_t *in)
-{
- printk("\nUSB_IN_Desc at 0x%08lx\n", (unsigned long)in);
- printk(" sw_len : 0x%04x (%d)\n", in->sw_len, in->sw_len);
- printk(" command : 0x%04x\n", in->command);
- printk(" next : 0x%08lx\n", in->next);
- printk(" buf : 0x%08lx\n", in->buf);
- printk(" hw_len : 0x%04x (%d)\n", in->hw_len, in->hw_len);
- printk(" status : 0x%04x\n\n", in->status);
-}
-
-static void __dump_sb_desc(volatile USB_SB_Desc_t *sb)
-{
- char tt = (sb->command & 0x30) >> 4;
- char *tt_string;
-
- switch (tt) {
- case 0:
- tt_string = "zout";
- break;
- case 1:
- tt_string = "in";
- break;
- case 2:
- tt_string = "out";
- break;
- case 3:
- tt_string = "setup";
- break;
- default:
- tt_string = "unknown (weird)";
- }
-
- printk("\n USB_SB_Desc at 0x%08lx\n", (unsigned long)sb);
- printk(" command : 0x%04x\n", sb->command);
- printk(" rem : %d\n", (sb->command & 0x3f00) >> 8);
- printk(" full : %d\n", (sb->command & 0x40) >> 6);
- printk(" tt : %d (%s)\n", tt, tt_string);
- printk(" intr : %d\n", (sb->command & 0x8) >> 3);
- printk(" eot : %d\n", (sb->command & 0x2) >> 1);
- printk(" eol : %d\n", sb->command & 0x1);
- printk(" sw_len : 0x%04x (%d)\n", sb->sw_len, sb->sw_len);
- printk(" next : 0x%08lx\n", sb->next);
- printk(" buf : 0x%08lx\n\n", sb->buf);
-}
-
-
-static void __dump_ep_desc(volatile USB_EP_Desc_t *ep)
-{
- printk("\nUSB_EP_Desc at 0x%08lx\n", (unsigned long)ep);
- printk(" command : 0x%04x\n", ep->command);
- printk(" ep_id : %d\n", (ep->command & 0x1f00) >> 8);
- printk(" enable : %d\n", (ep->command & 0x10) >> 4);
- printk(" intr : %d\n", (ep->command & 0x8) >> 3);
- printk(" eof : %d\n", (ep->command & 0x2) >> 1);
- printk(" eol : %d\n", ep->command & 0x1);
- printk(" hw_len : 0x%04x (%d)\n", ep->hw_len, ep->hw_len);
- printk(" next : 0x%08lx\n", ep->next);
- printk(" sub : 0x%08lx\n\n", ep->sub);
-}
-
-static inline void __dump_ep_list(int pipe_type)
-{
- volatile USB_EP_Desc_t *ep;
- volatile USB_EP_Desc_t *first_ep;
- volatile USB_SB_Desc_t *sb;
-
- switch (pipe_type)
- {
- case PIPE_BULK:
- first_ep = &TxBulkEPList[0];
- break;
- case PIPE_CONTROL:
- first_ep = &TxCtrlEPList[0];
- break;
- case PIPE_INTERRUPT:
- first_ep = &TxIntrEPList[0];
- break;
- case PIPE_ISOCHRONOUS:
- first_ep = &TxIsocEPList[0];
- break;
- default:
- warn("Cannot dump unknown traffic type");
- return;
- }
- ep = first_ep;
-
- printk("\n\nDumping EP list...\n\n");
-
- do {
- __dump_ep_desc(ep);
- /* Cannot phys_to_virt on 0 as it turns into 80000000, which is != 0. */
- sb = ep->sub ? phys_to_virt(ep->sub) : 0;
- while (sb) {
- __dump_sb_desc(sb);
- sb = sb->next ? phys_to_virt(sb->next) : 0;
- }
- ep = (volatile USB_EP_Desc_t *)(phys_to_virt(ep->next));
-
- } while (ep != first_ep);
-}
-
-static inline void __dump_ept_data(int epid)
-{
- unsigned long flags;
- __u32 r_usb_ept_data;
-
- if (epid < 0 || epid > 31) {
- printk("Cannot dump ept data for invalid epid %d\n", epid);
- return;
- }
-
- save_flags(flags);
- cli();
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
- r_usb_ept_data = *R_USB_EPT_DATA;
- restore_flags(flags);
-
- printk("\nR_USB_EPT_DATA = 0x%x for epid %d :\n", r_usb_ept_data, epid);
- if (r_usb_ept_data == 0) {
- /* No need for more detailed printing. */
- return;
- }
- printk(" valid : %d\n", (r_usb_ept_data & 0x80000000) >> 31);
- printk(" hold : %d\n", (r_usb_ept_data & 0x40000000) >> 30);
- printk(" error_count_in : %d\n", (r_usb_ept_data & 0x30000000) >> 28);
- printk(" t_in : %d\n", (r_usb_ept_data & 0x08000000) >> 27);
- printk(" low_speed : %d\n", (r_usb_ept_data & 0x04000000) >> 26);
- printk(" port : %d\n", (r_usb_ept_data & 0x03000000) >> 24);
- printk(" error_code : %d\n", (r_usb_ept_data & 0x00c00000) >> 22);
- printk(" t_out : %d\n", (r_usb_ept_data & 0x00200000) >> 21);
- printk(" error_count_out : %d\n", (r_usb_ept_data & 0x00180000) >> 19);
- printk(" max_len : %d\n", (r_usb_ept_data & 0x0003f800) >> 11);
- printk(" ep : %d\n", (r_usb_ept_data & 0x00000780) >> 7);
- printk(" dev : %d\n", (r_usb_ept_data & 0x0000003f));
-}
-
-static inline void __dump_ept_data_list(void)
-{
- int i;
-
- printk("Dumping the whole R_USB_EPT_DATA list\n");
-
- for (i = 0; i < 32; i++) {
- __dump_ept_data(i);
- }
-}
-#ifdef USB_DEBUG_DESC
-#define dump_in_desc(...) __dump_in_desc(...)
-#define dump_sb_desc(...) __dump_sb_desc(...)
-#define dump_ep_desc(...) __dump_ep_desc(...)
-#else
-#define dump_in_desc(...) do {} while (0)
-#define dump_sb_desc(...) do {} while (0)
-#define dump_ep_desc(...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_URB
-#define dump_urb(x) __dump_urb(x)
-#else
-#define dump_urb(x) do {} while (0)
-#endif
-
-static void init_rx_buffers(void)
-{
- int i;
-
- DBFENTER;
-
- for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) {
- RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
- RxDescList[i].command = 0;
- RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]);
- RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE));
- RxDescList[i].hw_len = 0;
- RxDescList[i].status = 0;
-
- /* DMA IN cache bug. (struct etrax_dma_descr has the same layout as USB_IN_Desc
- for the relevant fields.) */
- prepare_rx_descriptor((struct etrax_dma_descr*)&RxDescList[i]);
-
- }
-
- RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
- RxDescList[i].command = IO_STATE(USB_IN_command, eol, yes);
- RxDescList[i].next = virt_to_phys(&RxDescList[0]);
- RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE));
- RxDescList[i].hw_len = 0;
- RxDescList[i].status = 0;
-
- myNextRxDesc = &RxDescList[0];
- myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
- myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
-
- *R_DMA_CH9_FIRST = virt_to_phys(myNextRxDesc);
- *R_DMA_CH9_CMD = IO_STATE(R_DMA_CH9_CMD, cmd, start);
-
- DBFEXIT;
-}
-
-static void init_tx_bulk_ep(void)
-{
- int i;
-
- DBFENTER;
-
- for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
- CHECK_ALIGN(&TxBulkEPList[i]);
- TxBulkEPList[i].hw_len = 0;
- TxBulkEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
- TxBulkEPList[i].sub = 0;
- TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[i + 1]);
-
- /* Initiate two EPs, disabled and with the eol flag set. No need for any
- preserved epid. */
-
- /* The first one has the intr flag set so we get an interrupt when the DMA
- channel is about to become disabled. */
- CHECK_ALIGN(&TxBulkDummyEPList[i][0]);
- TxBulkDummyEPList[i][0].hw_len = 0;
- TxBulkDummyEPList[i][0].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) |
- IO_STATE(USB_EP_command, eol, yes) |
- IO_STATE(USB_EP_command, intr, yes));
- TxBulkDummyEPList[i][0].sub = 0;
- TxBulkDummyEPList[i][0].next = virt_to_phys(&TxBulkDummyEPList[i][1]);
-
- /* The second one. */
- CHECK_ALIGN(&TxBulkDummyEPList[i][1]);
- TxBulkDummyEPList[i][1].hw_len = 0;
- TxBulkDummyEPList[i][1].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) |
- IO_STATE(USB_EP_command, eol, yes));
- TxBulkDummyEPList[i][1].sub = 0;
- /* The last dummy's next pointer is the same as the current EP's next pointer. */
- TxBulkDummyEPList[i][1].next = virt_to_phys(&TxBulkEPList[i + 1]);
- }
-
- /* Configure the last one. */
- CHECK_ALIGN(&TxBulkEPList[i]);
- TxBulkEPList[i].hw_len = 0;
- TxBulkEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
- IO_FIELD(USB_EP_command, epid, i));
- TxBulkEPList[i].sub = 0;
- TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[0]);
-
- /* No need configuring dummy EPs for the last one as it will never be used for
- bulk traffic (i == INVALD_EPID at this point). */
-
- /* Set up to start on the last EP so we will enable it when inserting traffic
- for the first time (imitating the situation where the DMA has stopped
- because there was no more traffic). */
- *R_DMA_CH8_SUB0_EP = virt_to_phys(&TxBulkEPList[i]);
- /* No point in starting the bulk channel yet.
- *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); */
- DBFEXIT;
-}
-
-static void init_tx_ctrl_ep(void)
-{
- int i;
-
- DBFENTER;
-
- for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
- CHECK_ALIGN(&TxCtrlEPList[i]);
- TxCtrlEPList[i].hw_len = 0;
- TxCtrlEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
- TxCtrlEPList[i].sub = 0;
- TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[i + 1]);
- }
-
- CHECK_ALIGN(&TxCtrlEPList[i]);
- TxCtrlEPList[i].hw_len = 0;
- TxCtrlEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
- IO_FIELD(USB_EP_command, epid, i));
-
- TxCtrlEPList[i].sub = 0;
- TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[0]);
-
- *R_DMA_CH8_SUB1_EP = virt_to_phys(&TxCtrlEPList[0]);
- *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
-
- DBFEXIT;
-}
-
-
-static void init_tx_intr_ep(void)
-{
- int i;
-
- DBFENTER;
-
- /* Read comment at zout_buffer declaration for an explanation to this. */
- TxIntrSB_zout.sw_len = 1;
- TxIntrSB_zout.next = 0;
- TxIntrSB_zout.buf = virt_to_phys(&zout_buffer[0]);
- TxIntrSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
- IO_STATE(USB_SB_command, tt, zout) |
- IO_STATE(USB_SB_command, full, yes) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, eol, yes));
-
- for (i = 0; i < (MAX_INTR_INTERVAL - 1); i++) {
- CHECK_ALIGN(&TxIntrEPList[i]);
- TxIntrEPList[i].hw_len = 0;
- TxIntrEPList[i].command =
- (IO_STATE(USB_EP_command, eof, yes) |
- IO_STATE(USB_EP_command, enable, yes) |
- IO_FIELD(USB_EP_command, epid, INVALID_EPID));
- TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
- TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[i + 1]);
- }
-
- CHECK_ALIGN(&TxIntrEPList[i]);
- TxIntrEPList[i].hw_len = 0;
- TxIntrEPList[i].command =
- (IO_STATE(USB_EP_command, eof, yes) |
- IO_STATE(USB_EP_command, eol, yes) |
- IO_STATE(USB_EP_command, enable, yes) |
- IO_FIELD(USB_EP_command, epid, INVALID_EPID));
- TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
- TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[0]);
-
- *R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]);
- *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
- DBFEXIT;
-}
-
-static void init_tx_isoc_ep(void)
-{
- int i;
-
- DBFENTER;
-
- /* Read comment at zout_buffer declaration for an explanation to this. */
- TxIsocSB_zout.sw_len = 1;
- TxIsocSB_zout.next = 0;
- TxIsocSB_zout.buf = virt_to_phys(&zout_buffer[0]);
- TxIsocSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
- IO_STATE(USB_SB_command, tt, zout) |
- IO_STATE(USB_SB_command, full, yes) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, eol, yes));
-
- /* The last isochronous EP descriptor is a dummy. */
-
- for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
- CHECK_ALIGN(&TxIsocEPList[i]);
- TxIsocEPList[i].hw_len = 0;
- TxIsocEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
- TxIsocEPList[i].sub = 0;
- TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[i + 1]);
- }
-
- CHECK_ALIGN(&TxIsocEPList[i]);
- TxIsocEPList[i].hw_len = 0;
-
- /* Must enable the last EP descr to get eof interrupt. */
- TxIsocEPList[i].command = (IO_STATE(USB_EP_command, enable, yes) |
- IO_STATE(USB_EP_command, eof, yes) |
- IO_STATE(USB_EP_command, eol, yes) |
- IO_FIELD(USB_EP_command, epid, INVALID_EPID));
- TxIsocEPList[i].sub = virt_to_phys(&TxIsocSB_zout);
- TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[0]);
-
- *R_DMA_CH8_SUB3_EP = virt_to_phys(&TxIsocEPList[0]);
- *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
-
- DBFEXIT;
-}
-
-static void etrax_usb_unlink_intr_urb(struct urb *urb)
-{
- volatile USB_EP_Desc_t *first_ep; /* First EP in the list. */
- volatile USB_EP_Desc_t *curr_ep; /* Current EP, the iterator. */
- volatile USB_EP_Desc_t *next_ep; /* The EP after current. */
- volatile USB_EP_Desc_t *unlink_ep; /* The one we should remove from the list. */
-
- int epid;
-
- /* Read 8.8.4 in Designer's Reference, "Removing an EP Descriptor from the List". */
-
- DBFENTER;
-
- epid = ((etrax_urb_priv_t *)urb->hcpriv)->epid;
-
- first_ep = &TxIntrEPList[0];
- curr_ep = first_ep;
-
-
- /* Note that this loop removes all EP descriptors with this epid. This assumes
- that all EP descriptors belong to the one and only urb for this epid. */
-
- do {
- next_ep = (USB_EP_Desc_t *)phys_to_virt(curr_ep->next);
-
- if (IO_EXTRACT(USB_EP_command, epid, next_ep->command) == epid) {
-
- dbg_intr("Found EP to unlink for epid %d", epid);
-
- /* This is the one we should unlink. */
- unlink_ep = next_ep;
-
- /* Actually unlink the EP from the DMA list. */
- curr_ep->next = unlink_ep->next;
-
- /* Wait until the DMA is no longer at this descriptor. */
- while (*R_DMA_CH8_SUB2_EP == virt_to_phys(unlink_ep));
-
- /* Now we are free to remove it and its SB descriptor.
- Note that it is assumed here that there is only one sb in the
- sb list for this ep. */
- kmem_cache_free(usb_desc_cache, phys_to_virt(unlink_ep->sub));
- kmem_cache_free(usb_desc_cache, (USB_EP_Desc_t *)unlink_ep);
- }
-
- curr_ep = phys_to_virt(curr_ep->next);
-
- } while (curr_ep != first_ep);
- urb->hcpriv = NULL;
-}
-
-void etrax_usb_do_intr_recover(int epid)
-{
- USB_EP_Desc_t *first_ep, *tmp_ep;
-
- DBFENTER;
-
- first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP);
- tmp_ep = first_ep;
-
- /* What this does is simply to walk the list of interrupt
- ep descriptors and enable those that are disabled. */
-
- do {
- if (IO_EXTRACT(USB_EP_command, epid, tmp_ep->command) == epid &&
- !(tmp_ep->command & IO_MASK(USB_EP_command, enable))) {
- tmp_ep->command |= IO_STATE(USB_EP_command, enable, yes);
- }
-
- tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next);
-
- } while (tmp_ep != first_ep);
-
-
- DBFEXIT;
-}
-
-static int etrax_rh_unlink_urb (struct urb *urb)
-{
- etrax_hc_t *hc;
-
- DBFENTER;
-
- hc = urb->dev->bus->hcpriv;
-
- if (hc->rh.urb == urb) {
- hc->rh.send = 0;
- del_timer(&hc->rh.rh_int_timer);
- }
-
- DBFEXIT;
- return 0;
-}
-
-static void etrax_rh_send_irq(struct urb *urb)
-{
- __u16 data = 0;
- etrax_hc_t *hc = urb->dev->bus->hcpriv;
- DBFENTER;
-
-/*
- dbg_rh("R_USB_FM_NUMBER : 0x%08X", *R_USB_FM_NUMBER);
- dbg_rh("R_USB_FM_REMAINING: 0x%08X", *R_USB_FM_REMAINING);
-*/
-
- data |= (hc->rh.wPortChange_1) ? (1 << 1) : 0;
- data |= (hc->rh.wPortChange_2) ? (1 << 2) : 0;
-
- *((__u16 *)urb->transfer_buffer) = cpu_to_le16(data);
- /* FIXME: Why is actual_length set to 1 when data is 2 bytes?
- Since only 1 byte is used, why not declare data as __u8? */
- urb->actual_length = 1;
- urb->status = 0;
-
- if (hc->rh.send && urb->complete) {
- dbg_rh("wPortChange_1: 0x%04X", hc->rh.wPortChange_1);
- dbg_rh("wPortChange_2: 0x%04X", hc->rh.wPortChange_2);
-
- urb->complete(urb, NULL);
- }
-
- DBFEXIT;
-}
-
-static void etrax_rh_init_int_timer(struct urb *urb)
-{
- etrax_hc_t *hc;
-
- DBFENTER;
-
- hc = urb->dev->bus->hcpriv;
- hc->rh.interval = urb->interval;
- init_timer(&hc->rh.rh_int_timer);
- hc->rh.rh_int_timer.function = etrax_rh_int_timer_do;
- hc->rh.rh_int_timer.data = (unsigned long)urb;
- /* FIXME: Is the jiffies resolution enough? All intervals < 10 ms will be mapped
- to 0, and the rest to the nearest lower 10 ms. */
- hc->rh.rh_int_timer.expires = jiffies + ((HZ * hc->rh.interval) / 1000);
- add_timer(&hc->rh.rh_int_timer);
-
- DBFEXIT;
-}
-
-static void etrax_rh_int_timer_do(unsigned long ptr)
-{
- struct urb *urb;
- etrax_hc_t *hc;
-
- DBFENTER;
-
- urb = (struct urb*)ptr;
- hc = urb->dev->bus->hcpriv;
-
- if (hc->rh.send) {
- etrax_rh_send_irq(urb);
- }
-
- DBFEXIT;
-}
-
-static int etrax_usb_setup_epid(struct urb *urb)
-{
- int epid;
- char devnum, endpoint, out_traffic, slow;
- int maxlen;
- unsigned long flags;
-
- DBFENTER;
-
- epid = etrax_usb_lookup_epid(urb);
- if ((epid != -1)){
- /* An epid that fits this urb has been found. */
- DBFEXIT;
- return epid;
- }
-
- /* We must find and initiate a new epid for this urb. */
- epid = etrax_usb_allocate_epid();
-
- if (epid == -1) {
- /* Failed to allocate a new epid. */
- DBFEXIT;
- return epid;
- }
-
- /* We now have a new epid to use. Initiate it. */
- set_bit(epid, (void *)&epid_usage_bitmask);
-
- devnum = usb_pipedevice(urb->pipe);
- endpoint = usb_pipeendpoint(urb->pipe);
- slow = usb_pipeslow(urb->pipe);
- maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
- if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
- /* We want both IN and OUT control traffic to be put on the same EP/SB list. */
- out_traffic = 1;
- } else {
- out_traffic = usb_pipeout(urb->pipe);
- }
-
- save_flags(flags);
- cli();
-
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
-
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- *R_USB_EPT_DATA_ISO = IO_STATE(R_USB_EPT_DATA_ISO, valid, yes) |
- /* FIXME: Change any to the actual port? */
- IO_STATE(R_USB_EPT_DATA_ISO, port, any) |
- IO_FIELD(R_USB_EPT_DATA_ISO, max_len, maxlen) |
- IO_FIELD(R_USB_EPT_DATA_ISO, ep, endpoint) |
- IO_FIELD(R_USB_EPT_DATA_ISO, dev, devnum);
- } else {
- *R_USB_EPT_DATA = IO_STATE(R_USB_EPT_DATA, valid, yes) |
- IO_FIELD(R_USB_EPT_DATA, low_speed, slow) |
- /* FIXME: Change any to the actual port? */
- IO_STATE(R_USB_EPT_DATA, port, any) |
- IO_FIELD(R_USB_EPT_DATA, max_len, maxlen) |
- IO_FIELD(R_USB_EPT_DATA, ep, endpoint) |
- IO_FIELD(R_USB_EPT_DATA, dev, devnum);
- }
-
- restore_flags(flags);
-
- if (out_traffic) {
- set_bit(epid, (void *)&epid_out_traffic);
- } else {
- clear_bit(epid, (void *)&epid_out_traffic);
- }
-
- dbg_epid("Setting up epid %d with devnum %d, endpoint %d and max_len %d (%s)",
- epid, devnum, endpoint, maxlen, out_traffic ? "OUT" : "IN");
-
- DBFEXIT;
- return epid;
-}
-
-static void etrax_usb_free_epid(int epid)
-{
- unsigned long flags;
-
- DBFENTER;
-
- if (!test_bit(epid, (void *)&epid_usage_bitmask)) {
- warn("Trying to free unused epid %d", epid);
- DBFEXIT;
- return;
- }
-
- save_flags(flags);
- cli();
-
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
- while (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold));
- /* This will, among other things, set the valid field to 0. */
- *R_USB_EPT_DATA = 0;
- restore_flags(flags);
-
- clear_bit(epid, (void *)&epid_usage_bitmask);
-
-
- dbg_epid("Freed epid %d", epid);
-
- DBFEXIT;
-}
-
-static int etrax_usb_lookup_epid(struct urb *urb)
-{
- int i;
- __u32 data;
- char devnum, endpoint, slow, out_traffic;
- int maxlen;
- unsigned long flags;
-
- DBFENTER;
-
- devnum = usb_pipedevice(urb->pipe);
- endpoint = usb_pipeendpoint(urb->pipe);
- slow = usb_pipeslow(urb->pipe);
- maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
- if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
- /* We want both IN and OUT control traffic to be put on the same EP/SB list. */
- out_traffic = 1;
- } else {
- out_traffic = usb_pipeout(urb->pipe);
- }
-
- /* Step through att epids. */
- for (i = 0; i < NBR_OF_EPIDS; i++) {
- if (test_bit(i, (void *)&epid_usage_bitmask) &&
- test_bit(i, (void *)&epid_out_traffic) == out_traffic) {
-
- save_flags(flags);
- cli();
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, i);
- nop();
-
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- data = *R_USB_EPT_DATA_ISO;
- restore_flags(flags);
-
- if ((IO_MASK(R_USB_EPT_DATA_ISO, valid) & data) &&
- (IO_EXTRACT(R_USB_EPT_DATA_ISO, dev, data) == devnum) &&
- (IO_EXTRACT(R_USB_EPT_DATA_ISO, ep, data) == endpoint) &&
- (IO_EXTRACT(R_USB_EPT_DATA_ISO, max_len, data) == maxlen)) {
- dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
- i, devnum, endpoint, out_traffic ? "OUT" : "IN");
- DBFEXIT;
- return i;
- }
- } else {
- data = *R_USB_EPT_DATA;
- restore_flags(flags);
-
- if ((IO_MASK(R_USB_EPT_DATA, valid) & data) &&
- (IO_EXTRACT(R_USB_EPT_DATA, dev, data) == devnum) &&
- (IO_EXTRACT(R_USB_EPT_DATA, ep, data) == endpoint) &&
- (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) &&
- (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxlen)) {
- dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
- i, devnum, endpoint, out_traffic ? "OUT" : "IN");
- DBFEXIT;
- return i;
- }
- }
- }
- }
-
- DBFEXIT;
- return -1;
-}
-
-static int etrax_usb_allocate_epid(void)
-{
- int i;
-
- DBFENTER;
-
- for (i = 0; i < NBR_OF_EPIDS; i++) {
- if (!test_bit(i, (void *)&epid_usage_bitmask)) {
- dbg_epid("Found free epid %d", i);
- DBFEXIT;
- return i;
- }
- }
-
- dbg_epid("Found no free epids");
- DBFEXIT;
- return -1;
-}
-
-static int etrax_usb_submit_urb(struct urb *urb, unsigned mem_flags)
-{
- etrax_hc_t *hc;
- int ret = -EINVAL;
-
- DBFENTER;
-
- if (!urb->dev || !urb->dev->bus) {
- return -ENODEV;
- }
- if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) <= 0) {
- info("Submit urb to pipe with maxpacketlen 0, pipe 0x%X\n", urb->pipe);
- return -EMSGSIZE;
- }
-
- if (urb->timeout) {
- /* FIXME. */
- warn("urb->timeout specified, ignoring.");
- }
-
- hc = (etrax_hc_t*)urb->dev->bus->hcpriv;
-
- if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
- /* This request is for the Virtual Root Hub. */
- ret = etrax_rh_submit_urb(urb);
-
- } else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
-
- ret = etrax_usb_submit_bulk_urb(urb);
-
- } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-
- ret = etrax_usb_submit_ctrl_urb(urb);
-
- } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
- int bustime;
-
- if (urb->bandwidth == 0) {
- bustime = usb_check_bandwidth(urb->dev, urb);
- if (bustime < 0) {
- ret = bustime;
- } else {
- ret = etrax_usb_submit_intr_urb(urb);
- if (ret == 0)
- usb_claim_bandwidth(urb->dev, urb, bustime, 0);
- }
- } else {
- /* Bandwidth already set. */
- ret = etrax_usb_submit_intr_urb(urb);
- }
-
- } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- int bustime;
-
- if (urb->bandwidth == 0) {
- bustime = usb_check_bandwidth(urb->dev, urb);
- if (bustime < 0) {
- ret = bustime;
- } else {
- ret = etrax_usb_submit_isoc_urb(urb);
- if (ret == 0)
- usb_claim_bandwidth(urb->dev, urb, bustime, 0);
- }
- } else {
- /* Bandwidth already set. */
- ret = etrax_usb_submit_isoc_urb(urb);
- }
- }
-
- DBFEXIT;
-
- if (ret != 0)
- printk("Submit URB error %d\n", ret);
-
- return ret;
-}
-
-static int etrax_usb_unlink_urb(struct urb *urb, int status)
-{
- etrax_hc_t *hc;
- etrax_urb_priv_t *urb_priv;
- int epid;
- unsigned int flags;
-
- DBFENTER;
-
- if (!urb) {
- return -EINVAL;
- }
-
- /* Disable interrupts here since a descriptor interrupt for the isoc epid
- will modify the sb list. This could possibly be done more granular, but
- unlink_urb should not be used frequently anyway.
- */
-
- save_flags(flags);
- cli();
-
- if (!urb->dev || !urb->dev->bus) {
- restore_flags(flags);
- return -ENODEV;
- }
- if (!urb->hcpriv) {
- /* This happens if a device driver calls unlink on an urb that
- was never submitted (lazy driver) or if the urb was completed
- while unlink was being called. */
- restore_flags(flags);
- return 0;
- }
- if (urb->transfer_flags & URB_ASYNC_UNLINK) {
- /* FIXME. */
- /* If URB_ASYNC_UNLINK is set:
- unlink
- move to a separate urb list
- call complete at next sof with ECONNRESET
-
- If not:
- wait 1 ms
- unlink
- call complete with ENOENT
- */
- warn("URB_ASYNC_UNLINK set, ignoring.");
- }
-
- /* One might think that urb->status = -EINPROGRESS would be a requirement for unlinking,
- but that doesn't work for interrupt and isochronous traffic since they are completed
- repeatedly, and urb->status is set then. That may in itself be a bug though. */
-
- hc = urb->dev->bus->hcpriv;
- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- epid = urb_priv->epid;
-
- /* Set the urb status (synchronous unlink). */
- urb->status = -ENOENT;
- urb_priv->urb_state = UNLINK;
-
- if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
- int ret;
- ret = etrax_rh_unlink_urb(urb);
- DBFEXIT;
- restore_flags(flags);
- return ret;
-
- } else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
-
- dbg_bulk("Unlink of bulk urb (0x%lx)", (unsigned long)urb);
-
- if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
- /* The EP was enabled, disable it and wait. */
- TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-
- /* Ah, the luxury of busy-wait. */
- while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid]));
- }
- /* Kicking dummy list out of the party. */
- TxBulkEPList[epid].next = virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
-
- } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-
- dbg_ctrl("Unlink of ctrl urb (0x%lx)", (unsigned long)urb);
-
- if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
- /* The EP was enabled, disable it and wait. */
- TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-
- /* Ah, the luxury of busy-wait. */
- while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid]));
- }
-
- } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
-
- dbg_intr("Unlink of intr urb (0x%lx)", (unsigned long)urb);
-
- /* Separate function because it's a tad more complicated. */
- etrax_usb_unlink_intr_urb(urb);
-
- } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-
- dbg_isoc("Unlink of isoc urb (0x%lx)", (unsigned long)urb);
-
- if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
- /* The EP was enabled, disable it and wait. */
- TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-
- /* Ah, the luxury of busy-wait. */
- while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
- }
- }
-
- /* Note that we need to remove the urb from the urb list *before* removing its SB
- descriptors. (This means that the isoc eof handler might get a null urb when we
- are unlinking the last urb.) */
-
- if (usb_pipetype(urb->pipe) == PIPE_BULK) {
-
- urb_list_del(urb, epid);
- TxBulkEPList[epid].sub = 0;
- etrax_remove_from_sb_list(urb);
-
- } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-
- urb_list_del(urb, epid);
- TxCtrlEPList[epid].sub = 0;
- etrax_remove_from_sb_list(urb);
-
- } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
-
- urb_list_del(urb, epid);
- /* Sanity check (should never happen). */
- assert(urb_list_empty(epid));
-
- /* Release allocated bandwidth. */
- usb_release_bandwidth(urb->dev, urb, 0);
-
- } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-
- if (usb_pipeout(urb->pipe)) {
-
- USB_SB_Desc_t *iter_sb, *prev_sb, *next_sb;
-
- if (__urb_list_entry(urb, epid)) {
-
- urb_list_del(urb, epid);
- iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0;
- prev_sb = 0;
- while (iter_sb && (iter_sb != urb_priv->first_sb)) {
- prev_sb = iter_sb;
- iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
- }
-
- if (iter_sb == 0) {
- /* Unlink of the URB currently being transmitted. */
- prev_sb = 0;
- iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0;
- }
-
- while (iter_sb && (iter_sb != urb_priv->last_sb)) {
- iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
- }
- if (iter_sb) {
- next_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
- } else {
- /* This should only happen if the DMA has completed
- processing the SB list for this EP while interrupts
- are disabled. */
- dbg_isoc("Isoc urb not found, already sent?");
- next_sb = 0;
- }
- if (prev_sb) {
- prev_sb->next = next_sb ? virt_to_phys(next_sb) : 0;
- } else {
- TxIsocEPList[epid].sub = next_sb ? virt_to_phys(next_sb) : 0;
- }
-
- etrax_remove_from_sb_list(urb);
- if (urb_list_empty(epid)) {
- TxIsocEPList[epid].sub = 0;
- dbg_isoc("Last isoc out urb epid %d", epid);
- } else if (next_sb || prev_sb) {
- dbg_isoc("Re-enable isoc out epid %d", epid);
-
- TxIsocEPList[epid].hw_len = 0;
- TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
- } else {
- TxIsocEPList[epid].sub = 0;
- dbg_isoc("URB list non-empty and no SB list, EP disabled");
- }
- } else {
- dbg_isoc("Urb 0x%p not found, completed already?", urb);
- }
- } else {
-
- urb_list_del(urb, epid);
-
- /* For in traffic there is only one SB descriptor for each EP even
- though there may be several urbs (all urbs point at the same SB). */
- if (urb_list_empty(epid)) {
- /* No more urbs, remove the SB. */
- TxIsocEPList[epid].sub = 0;
- etrax_remove_from_sb_list(urb);
- } else {
- TxIsocEPList[epid].hw_len = 0;
- TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
- }
- }
- /* Release allocated bandwidth. */
- usb_release_bandwidth(urb->dev, urb, 1);
- }
- /* Free the epid if urb list is empty. */
- if (urb_list_empty(epid)) {
- etrax_usb_free_epid(epid);
- }
- restore_flags(flags);
-
- /* Must be done before calling completion handler. */
- kfree(urb_priv);
- urb->hcpriv = 0;
-
- if (urb->complete) {
- urb->complete(urb, NULL);
- }
-
- DBFEXIT;
- return 0;
-}
-
-static int etrax_usb_get_frame_number(struct usb_device *usb_dev)
-{
- DBFENTER;
- DBFEXIT;
- return (*R_USB_FM_NUMBER & 0x7ff);
-}
-
-static irqreturn_t etrax_usb_tx_interrupt(int irq, void *vhc)
-{
- DBFENTER;
-
- /* This interrupt handler could be used when unlinking EP descriptors. */
-
- if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) {
- USB_EP_Desc_t *ep;
-
- //dbg_bulk("dma8_sub0_descr (BULK) intr.");
-
- /* It should be safe clearing the interrupt here, since we don't expect to get a new
- one until we restart the bulk channel. */
- *R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do);
-
- /* Wait while the DMA is running (though we don't expect it to be). */
- while (*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd));
-
- /* Advance the DMA to the next EP descriptor. */
- ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP);
-
- //dbg_bulk("descr intr: DMA is at 0x%lx", (unsigned long)ep);
-
- /* ep->next is already a physical address; no need for a virt_to_phys. */
- *R_DMA_CH8_SUB0_EP = ep->next;
-
- /* Start the DMA bulk channel again. */
- *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
- }
- if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) {
- struct urb *urb;
- int epid;
- etrax_urb_priv_t *urb_priv;
- unsigned long int flags;
-
- dbg_ctrl("dma8_sub1_descr (CTRL) intr.");
- *R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do);
-
- /* The complete callback gets called so we cli. */
- save_flags(flags);
- cli();
-
- for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
- if ((TxCtrlEPList[epid].sub == 0) ||
- (epid == DUMMY_EPID) ||
- (epid == INVALID_EPID)) {
- /* Nothing here to see. */
- continue;
- }
-
- /* Get the first urb (if any). */
- urb = urb_list_first(epid);
-
- if (urb) {
-
- /* Sanity check. */
- assert(usb_pipetype(urb->pipe) == PIPE_CONTROL);
-
- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- assert(urb_priv);
-
- if (urb_priv->urb_state == WAITING_FOR_DESCR_INTR) {
- assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
-
- etrax_usb_complete_urb(urb, 0);
- }
- }
- }
- restore_flags(flags);
- }
- if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) {
- dbg_intr("dma8_sub2_descr (INTR) intr.");
- *R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do);
- }
- if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) {
- struct urb *urb;
- int epid;
- int epid_done;
- etrax_urb_priv_t *urb_priv;
- USB_SB_Desc_t *sb_desc;
-
- usb_isoc_complete_data_t *comp_data = NULL;
-
- /* One or more isoc out transfers are done. */
- dbg_isoc("dma8_sub3_descr (ISOC) intr.");
-
- /* For each isoc out EP search for the first sb_desc with the intr flag
- set. This descriptor must be the last packet from an URB. Then
- traverse the URB list for the EP until the URB with urb_priv->last_sb
- matching the intr-marked sb_desc is found. All URBs before this have
- been sent.
- */
-
- for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
- /* Skip past epids with no SB lists, epids used for in traffic,
- and special (dummy, invalid) epids. */
- if ((TxIsocEPList[epid].sub == 0) ||
- (test_bit(epid, (void *)&epid_out_traffic) == 0) ||
- (epid == DUMMY_EPID) ||
- (epid == INVALID_EPID)) {
- /* Nothing here to see. */
- continue;
- }
- sb_desc = phys_to_virt(TxIsocEPList[epid].sub);
-
- /* Find the last descriptor of the currently active URB for this ep.
- This is the first descriptor in the sub list marked for a descriptor
- interrupt. */
- while (sb_desc && !IO_EXTRACT(USB_SB_command, intr, sb_desc->command)) {
- sb_desc = sb_desc->next ? phys_to_virt(sb_desc->next) : 0;
- }
- assert(sb_desc);
-
- dbg_isoc("Check epid %d, sub 0x%p, SB 0x%p",
- epid,
- phys_to_virt(TxIsocEPList[epid].sub),
- sb_desc);
-
- epid_done = 0;
-
- /* Get the first urb (if any). */
- urb = urb_list_first(epid);
- assert(urb);
-
- while (urb && !epid_done) {
-
- /* Sanity check. */
- assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
-
- if (!usb_pipeout(urb->pipe)) {
- /* descr interrupts are generated only for out pipes. */
- epid_done = 1;
- continue;
- }
-
- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- assert(urb_priv);
-
- if (sb_desc != urb_priv->last_sb) {
-
- /* This urb has been sent. */
- dbg_isoc("out URB 0x%p sent", urb);
-
- urb_priv->urb_state = TRANSFER_DONE;
-
- } else if ((sb_desc == urb_priv->last_sb) &&
- !(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
-
- assert((sb_desc->command & IO_MASK(USB_SB_command, eol)) == IO_STATE(USB_SB_command, eol, yes));
- assert(sb_desc->next == 0);
-
- dbg_isoc("out URB 0x%p last in list, epid disabled", urb);
- TxIsocEPList[epid].sub = 0;
- TxIsocEPList[epid].hw_len = 0;
- urb_priv->urb_state = TRANSFER_DONE;
-
- epid_done = 1;
-
- } else {
- epid_done = 1;
- }
- if (!epid_done) {
- urb = urb_list_next(urb, epid);
- }
- }
-
- }
-
- *R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do);
-
- comp_data = (usb_isoc_complete_data_t*)kmem_cache_alloc(isoc_compl_cache, GFP_ATOMIC);
- assert(comp_data != NULL);
-
- INIT_WORK(&comp_data->usb_bh, etrax_usb_isoc_descr_interrupt_bottom_half, comp_data);
- schedule_work(&comp_data->usb_bh);
- }
-
- DBFEXIT;
- return IRQ_HANDLED;
-}
-
-static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data)
-{
- usb_isoc_complete_data_t *comp_data = (usb_isoc_complete_data_t*)data;
-
- struct urb *urb;
- int epid;
- int epid_done;
- etrax_urb_priv_t *urb_priv;
-
- DBFENTER;
-
- dbg_isoc("dma8_sub3_descr (ISOC) bottom half.");
-
- for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- epid_done = 0;
-
- /* The descriptor interrupt handler has marked all transmitted isoch. out
- URBs with TRANSFER_DONE. Now we traverse all epids and for all that
- have isoch. out traffic traverse its URB list and complete the
- transmitted URB.
- */
-
- while (!epid_done) {
-
- /* Get the first urb (if any). */
- urb = urb_list_first(epid);
- if (urb == 0) {
- epid_done = 1;
- continue;
- }
-
- if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {
- epid_done = 1;
- continue;
- }
-
- if (!usb_pipeout(urb->pipe)) {
- /* descr interrupts are generated only for out pipes. */
- epid_done = 1;
- continue;
- }
-
- dbg_isoc("Check epid %d, SB 0x%p", epid, (char*)TxIsocEPList[epid].sub);
-
- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- assert(urb_priv);
-
- if (urb_priv->urb_state == TRANSFER_DONE) {
- int i;
- struct usb_iso_packet_descriptor *packet;
-
- /* This urb has been sent. */
- dbg_isoc("Completing isoc out URB 0x%p", urb);
-
- for (i = 0; i < urb->number_of_packets; i++) {
- packet = &urb->iso_frame_desc[i];
- packet->status = 0;
- packet->actual_length = packet->length;
- }
-
- etrax_usb_complete_isoc_urb(urb, 0);
-
- if (urb_list_empty(epid)) {
- etrax_usb_free_epid(epid);
- epid_done = 1;
- }
- } else {
- epid_done = 1;
- }
- }
- restore_flags(flags);
-
- }
- kmem_cache_free(isoc_compl_cache, comp_data);
-
- DBFEXIT;
-}
-
-
-
-static irqreturn_t etrax_usb_rx_interrupt(int irq, void *vhc)
-{
- struct urb *urb;
- etrax_urb_priv_t *urb_priv;
- int epid = 0;
- unsigned long flags;
-
- /* Isoc diagnostics. */
- static int curr_fm = 0;
- static int prev_fm = 0;
-
- DBFENTER;
-
- /* Clear this interrupt. */
- *R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do);
-
- /* Note that this while loop assumes that all packets span only
- one rx descriptor. */
-
- /* The reason we cli here is that we call the driver's callback functions. */
- save_flags(flags);
- cli();
-
- while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) {
-
- epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status);
- urb = urb_list_first(epid);
-
- //printk("eop for epid %d, first urb 0x%lx\n", epid, (unsigned long)urb);
-
- if (!urb) {
- err("No urb for epid %d in rx interrupt", epid);
- __dump_ept_data(epid);
- goto skip_out;
- }
-
- /* Note that we cannot indescriminately assert(usb_pipein(urb->pipe)) since
- ctrl pipes are not. */
-
- if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) {
- __u32 r_usb_ept_data;
- int no_error = 0;
-
- assert(test_bit(epid, (void *)&epid_usage_bitmask));
-
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- r_usb_ept_data = *R_USB_EPT_DATA_ISO;
-
- if ((r_usb_ept_data & IO_MASK(R_USB_EPT_DATA_ISO, valid)) &&
- (IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data) == 0) &&
- (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata))) {
- /* Not an error, just a failure to receive an expected iso
- in packet in this frame. This is not documented
- in the designers reference.
- */
- no_error++;
- } else {
- warn("R_USB_EPT_DATA_ISO for epid %d = 0x%x", epid, r_usb_ept_data);
- }
- } else {
- r_usb_ept_data = *R_USB_EPT_DATA;
- warn("R_USB_EPT_DATA for epid %d = 0x%x", epid, r_usb_ept_data);
- }
-
- if (!no_error){
- warn("error in rx desc->status, epid %d, first urb = 0x%lx",
- epid, (unsigned long)urb);
- __dump_in_desc(myNextRxDesc);
-
- warn("R_USB_STATUS = 0x%x", *R_USB_STATUS);
-
- /* Check that ept was disabled when error occurred. */
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_BULK:
- assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
- break;
- case PIPE_CONTROL:
- assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
- break;
- case PIPE_INTERRUPT:
- assert(!(TxIntrEPList[epid].command & IO_MASK(USB_EP_command, enable)));
- break;
- case PIPE_ISOCHRONOUS:
- assert(!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)));
- break;
- default:
- warn("etrax_usb_rx_interrupt: bad pipetype %d in urb 0x%p",
- usb_pipetype(urb->pipe),
- urb);
- }
- etrax_usb_complete_urb(urb, -EPROTO);
- goto skip_out;
- }
- }
-
- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- assert(urb_priv);
-
- if ((usb_pipetype(urb->pipe) == PIPE_BULK) ||
- (usb_pipetype(urb->pipe) == PIPE_CONTROL) ||
- (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
-
- if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
- /* We get nodata for empty data transactions, and the rx descriptor's
- hw_len field is not valid in that case. No data to copy in other
- words. */
- } else {
- /* Make sure the data fits in the buffer. */
- assert(urb_priv->rx_offset + myNextRxDesc->hw_len
- <= urb->transfer_buffer_length);
-
- memcpy(urb->transfer_buffer + urb_priv->rx_offset,
- phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len);
- urb_priv->rx_offset += myNextRxDesc->hw_len;
- }
-
- if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) {
- if ((usb_pipetype(urb->pipe) == PIPE_CONTROL) &&
- ((TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)) ==
- IO_STATE(USB_EP_command, enable, yes))) {
- /* The EP is still enabled, so the OUT packet used to ack
- the in data is probably not processed yet. If the EP
- sub pointer has not moved beyond urb_priv->last_sb mark
- it for a descriptor interrupt and complete the urb in
- the descriptor interrupt handler.
- */
- USB_SB_Desc_t *sub = TxCtrlEPList[urb_priv->epid].sub ? phys_to_virt(TxCtrlEPList[urb_priv->epid].sub) : 0;
-
- while ((sub != NULL) && (sub != urb_priv->last_sb)) {
- sub = sub->next ? phys_to_virt(sub->next) : 0;
- }
- if (sub != NULL) {
- /* The urb has not been fully processed. */
- urb_priv->urb_state = WAITING_FOR_DESCR_INTR;
- } else {
- warn("(CTRL) epid enabled and urb (0x%p) processed, ep->sub=0x%p", urb, (char*)TxCtrlEPList[urb_priv->epid].sub);
- etrax_usb_complete_urb(urb, 0);
- }
- } else {
- etrax_usb_complete_urb(urb, 0);
- }
- }
-
- } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-
- struct usb_iso_packet_descriptor *packet;
-
- if (urb_priv->urb_state == UNLINK) {
- info("Ignoring rx data for urb being unlinked.");
- goto skip_out;
- } else if (urb_priv->urb_state == NOT_STARTED) {
- info("What? Got rx data for urb that isn't started?");
- goto skip_out;
- }
-
- packet = &urb->iso_frame_desc[urb_priv->isoc_packet_counter];
- packet->status = 0;
-
- if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
- /* We get nodata for empty data transactions, and the rx descriptor's
- hw_len field is not valid in that case. We copy 0 bytes however to
- stay in synch. */
- packet->actual_length = 0;
- } else {
- packet->actual_length = myNextRxDesc->hw_len;
- /* Make sure the data fits in the buffer. */
- assert(packet->actual_length <= packet->length);
- memcpy(urb->transfer_buffer + packet->offset,
- phys_to_virt(myNextRxDesc->buf), packet->actual_length);
- }
-
- /* Increment the packet counter. */
- urb_priv->isoc_packet_counter++;
-
- /* Note that we don't care about the eot field in the rx descriptor's status.
- It will always be set for isoc traffic. */
- if (urb->number_of_packets == urb_priv->isoc_packet_counter) {
-
- /* Out-of-synch diagnostics. */
- curr_fm = (*R_USB_FM_NUMBER & 0x7ff);
- if (((prev_fm + urb_priv->isoc_packet_counter) % (0x7ff + 1)) != curr_fm) {
- /* This test is wrong, if there is more than one isoc
- in endpoint active it will always calculate wrong
- since prev_fm is shared by all endpoints.
-
- FIXME Make this check per URB using urb->start_frame.
- */
- dbg_isoc("Out of synch? Previous frame = %d, current frame = %d",
- prev_fm, curr_fm);
-
- }
- prev_fm = curr_fm;
-
- /* Complete the urb with status OK. */
- etrax_usb_complete_isoc_urb(urb, 0);
- }
- }
-
- skip_out:
-
- /* DMA IN cache bug. Flush the DMA IN buffer from the cache. (struct etrax_dma_descr
- has the same layout as USB_IN_Desc for the relevant fields.) */
- prepare_rx_descriptor((struct etrax_dma_descr*)myNextRxDesc);
-
- myPrevRxDesc = myNextRxDesc;
- myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol);
- myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol);
- myLastRxDesc = myPrevRxDesc;
-
- myNextRxDesc->status = 0;
- myNextRxDesc = phys_to_virt(myNextRxDesc->next);
- }
-
- restore_flags(flags);
-
- DBFEXIT;
-
- return IRQ_HANDLED;
-}
-
-
-/* This function will unlink the SB descriptors associated with this urb. */
-static int etrax_remove_from_sb_list(struct urb *urb)
-{
- USB_SB_Desc_t *next_sb, *first_sb, *last_sb;
- etrax_urb_priv_t *urb_priv;
- int i = 0;
-
- DBFENTER;
-
- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- assert(urb_priv);
-
- /* Just a sanity check. Since we don't fiddle with the DMA list the EP descriptor
- doesn't really need to be disabled, it's just that we expect it to be. */
- if (usb_pipetype(urb->pipe) == PIPE_BULK) {
- assert(!(TxBulkEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
- } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
- assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
- }
-
- first_sb = urb_priv->first_sb;
- last_sb = urb_priv->last_sb;
-
- assert(first_sb);
- assert(last_sb);
-
- while (first_sb != last_sb) {
- next_sb = (USB_SB_Desc_t *)phys_to_virt(first_sb->next);
- kmem_cache_free(usb_desc_cache, first_sb);
- first_sb = next_sb;
- i++;
- }
- kmem_cache_free(usb_desc_cache, last_sb);
- i++;
- dbg_sb("%d SB descriptors freed", i);
- /* Compare i with urb->number_of_packets for Isoc traffic.
- Should be same when calling unlink_urb */
-
- DBFEXIT;
-
- return i;
-}
-
-static int etrax_usb_submit_bulk_urb(struct urb *urb)
-{
- int epid;
- int empty;
- unsigned long flags;
- etrax_urb_priv_t *urb_priv;
-
- DBFENTER;
-
- /* Epid allocation, empty check and list add must be protected.
- Read about this in etrax_usb_submit_ctrl_urb. */
-
- spin_lock_irqsave(&urb_list_lock, flags);
- epid = etrax_usb_setup_epid(urb);
- if (epid == -1) {
- DBFEXIT;
- spin_unlock_irqrestore(&urb_list_lock, flags);
- return -ENOMEM;
- }
- empty = urb_list_empty(epid);
- urb_list_add(urb, epid);
- spin_unlock_irqrestore(&urb_list_lock, flags);
-
- dbg_bulk("Adding bulk %s urb 0x%lx to %s list, epid %d",
- usb_pipein(urb->pipe) ? "IN" : "OUT", (unsigned long)urb, empty ? "empty" : "", epid);
-
- /* Mark the urb as being in progress. */
- urb->status = -EINPROGRESS;
-
- /* Setup the hcpriv data. */
- urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
- assert(urb_priv != NULL);
- /* This sets rx_offset to 0. */
- urb_priv->urb_state = NOT_STARTED;
- urb->hcpriv = urb_priv;
-
- if (empty) {
- etrax_usb_add_to_bulk_sb_list(urb, epid);
- }
-
- DBFEXIT;
-
- return 0;
-}
-
-static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid)
-{
- USB_SB_Desc_t *sb_desc;
- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- unsigned long flags;
- char maxlen;
-
- DBFENTER;
-
- dbg_bulk("etrax_usb_add_to_bulk_sb_list, urb 0x%lx", (unsigned long)urb);
-
- maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
-
- sb_desc = kmem_cache_zalloc(usb_desc_cache, SLAB_FLAG);
- assert(sb_desc != NULL);
-
-
- if (usb_pipeout(urb->pipe)) {
-
- dbg_bulk("Grabbing bulk OUT, urb 0x%lx, epid %d", (unsigned long)urb, epid);
-
- /* This is probably a sanity check of the bulk transaction length
- not being larger than 64 kB. */
- if (urb->transfer_buffer_length > 0xffff) {
- panic("urb->transfer_buffer_length > 0xffff");
- }
-
- sb_desc->sw_len = urb->transfer_buffer_length;
-
- /* The rem field is don't care if it's not a full-length transfer, so setting
- it shouldn't hurt. Also, rem isn't used for OUT traffic. */
- sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) |
- IO_STATE(USB_SB_command, tt, out) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, eol, yes));
-
- /* The full field is set to yes, even if we don't actually check that this is
- a full-length transfer (i.e., that transfer_buffer_length % maxlen = 0).
- Setting full prevents the USB controller from sending an empty packet in
- that case. However, if URB_ZERO_PACKET was set we want that. */
- if (!(urb->transfer_flags & URB_ZERO_PACKET)) {
- sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
- }
-
- sb_desc->buf = virt_to_phys(urb->transfer_buffer);
- sb_desc->next = 0;
-
- } else if (usb_pipein(urb->pipe)) {
-
- dbg_bulk("Grabbing bulk IN, urb 0x%lx, epid %d", (unsigned long)urb, epid);
-
- sb_desc->sw_len = urb->transfer_buffer_length ?
- (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
-
- /* The rem field is don't care if it's not a full-length transfer, so setting
- it shouldn't hurt. */
- sb_desc->command =
- (IO_FIELD(USB_SB_command, rem,
- urb->transfer_buffer_length % maxlen) |
- IO_STATE(USB_SB_command, tt, in) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, eol, yes));
-
- sb_desc->buf = 0;
- sb_desc->next = 0;
- }
-
- urb_priv->first_sb = sb_desc;
- urb_priv->last_sb = sb_desc;
- urb_priv->epid = epid;
-
- urb->hcpriv = urb_priv;
-
- /* Reset toggle bits and reset error count. */
- save_flags(flags);
- cli();
-
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
-
- /* FIXME: Is this a special case since the hold field is checked,
- or should we check hold in a lot of other cases as well? */
- if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
- panic("Hold was set in %s", __FUNCTION__);
- }
-
- /* Reset error counters (regardless of which direction this traffic is). */
- *R_USB_EPT_DATA &=
- ~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
- IO_MASK(R_USB_EPT_DATA, error_count_out));
-
- /* Software must preset the toggle bits. */
- if (usb_pipeout(urb->pipe)) {
- char toggle =
- usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
- *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_out);
- *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_out, toggle);
- } else {
- char toggle =
- usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
- *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_in);
- *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_in, toggle);
- }
-
- /* Assert that the EP descriptor is disabled. */
- assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
-
- /* The reason we set the EP's sub pointer directly instead of
- walking the SB list and linking it last in the list is that we only
- have one active urb at a time (the rest are queued). */
-
- /* Note that we cannot have interrupts running when we have set the SB descriptor
- but the EP is not yet enabled. If a bulk eot happens for another EP, we will
- find this EP disabled and with a SB != 0, which will make us think that it's done. */
- TxBulkEPList[epid].sub = virt_to_phys(sb_desc);
- TxBulkEPList[epid].hw_len = 0;
- /* Note that we don't have to fill in the ep_id field since this
- was done when we allocated the EP descriptors in init_tx_bulk_ep. */
-
- /* Check if the dummy list is already with us (if several urbs were queued). */
- if (TxBulkEPList[epid].next != virt_to_phys(&TxBulkDummyEPList[epid][0])) {
-
- dbg_bulk("Inviting dummy list to the party for urb 0x%lx, epid %d",
- (unsigned long)urb, epid);
-
- /* The last EP in the dummy list already has its next pointer set to
- TxBulkEPList[epid].next. */
-
- /* We don't need to check if the DMA is at this EP or not before changing the
- next pointer, since we will do it in one 32-bit write (EP descriptors are
- 32-bit aligned). */
- TxBulkEPList[epid].next = virt_to_phys(&TxBulkDummyEPList[epid][0]);
- }
- /* Enable the EP descr. */
- dbg_bulk("Enabling bulk EP for urb 0x%lx, epid %d", (unsigned long)urb, epid);
- TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
-
- /* Everything is set up, safe to enable interrupts again. */
- restore_flags(flags);
-
- /* If the DMA bulk channel isn't running, we need to restart it if it
- has stopped at the last EP descriptor (DMA stopped because there was
- no more traffic) or if it has stopped at a dummy EP with the intr flag
- set (DMA stopped because we were too slow in inserting new traffic). */
- if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
-
- USB_EP_Desc_t *ep;
- ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP);
- dbg_bulk("DMA channel not running in add");
- dbg_bulk("DMA is at 0x%lx", (unsigned long)ep);
-
- if (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[NBR_OF_EPIDS - 1]) ||
- (ep->command & 0x8) >> 3) {
- *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
- /* Update/restart the bulk start timer since we just started the channel. */
- mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
- /* Update/restart the bulk eot timer since we just inserted traffic. */
- mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
- }
- }
-
- DBFEXIT;
-}
-
-static void etrax_usb_complete_bulk_urb(struct urb *urb, int status)
-{
- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- int epid = urb_priv->epid;
- unsigned long flags;
-
- DBFENTER;
-
- if (status)
- warn("Completing bulk urb with status %d.", status);
-
- dbg_bulk("Completing bulk urb 0x%lx for epid %d", (unsigned long)urb, epid);
-
- /* Update the urb list. */
- urb_list_del(urb, epid);
-
- /* For an IN pipe, we always set the actual length, regardless of whether there was
- an error or not (which means the device driver can use the data if it wants to). */
- if (usb_pipein(urb->pipe)) {
- urb->actual_length = urb_priv->rx_offset;
- } else {
- /* Set actual_length for OUT urbs also; the USB mass storage driver seems
- to want that. We wouldn't know of any partial writes if there was an error. */
- if (status == 0) {
- urb->actual_length = urb->transfer_buffer_length;
- } else {
- urb->actual_length = 0;
- }
- }
-
- /* FIXME: Is there something of the things below we shouldn't do if there was an error?
- Like, maybe we shouldn't toggle the toggle bits, or maybe we shouldn't insert more traffic. */
-
- save_flags(flags);
- cli();
-
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
-
- /* We need to fiddle with the toggle bits because the hardware doesn't do it for us. */
- if (usb_pipeout(urb->pipe)) {
- char toggle =
- IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA);
- usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
- usb_pipeout(urb->pipe), toggle);
- } else {
- char toggle =
- IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA);
- usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
- usb_pipeout(urb->pipe), toggle);
- }
- restore_flags(flags);
-
- /* Remember to free the SBs. */
- etrax_remove_from_sb_list(urb);
- kfree(urb_priv);
- urb->hcpriv = 0;
-
- /* If there are any more urb's in the list we'd better start sending */
- if (!urb_list_empty(epid)) {
-
- struct urb *new_urb;
-
- /* Get the first urb. */
- new_urb = urb_list_first(epid);
- assert(new_urb);
-
- dbg_bulk("More bulk for epid %d", epid);
-
- etrax_usb_add_to_bulk_sb_list(new_urb, epid);
- }
-
- urb->status = status;
-
- /* We let any non-zero status from the layer above have precedence. */
- if (status == 0) {
- /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
- is to be treated as an error. */
- if (urb->transfer_flags & URB_SHORT_NOT_OK) {
- if (usb_pipein(urb->pipe) &&
- (urb->actual_length !=
- usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
- urb->status = -EREMOTEIO;
- }
- }
- }
-
- if (urb->complete) {
- urb->complete(urb, NULL);
- }
-
- if (urb_list_empty(epid)) {
- /* This means that this EP is now free, deconfigure it. */
- etrax_usb_free_epid(epid);
-
- /* No more traffic; time to clean up.
- Must set sub pointer to 0, since we look at the sub pointer when handling
- the bulk eot interrupt. */
-
- dbg_bulk("No bulk for epid %d", epid);
-
- TxBulkEPList[epid].sub = 0;
-
- /* Unlink the dummy list. */
-
- dbg_bulk("Kicking dummy list out of party for urb 0x%lx, epid %d",
- (unsigned long)urb, epid);
-
- /* No need to wait for the DMA before changing the next pointer.
- The modulo NBR_OF_EPIDS isn't actually necessary, since we will never use
- the last one (INVALID_EPID) for actual traffic. */
- TxBulkEPList[epid].next =
- virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
- }
-
- DBFEXIT;
-}
-
-static int etrax_usb_submit_ctrl_urb(struct urb *urb)
-{
- int epid;
- int empty;
- unsigned long flags;
- etrax_urb_priv_t *urb_priv;
-
- DBFENTER;
-
- /* FIXME: Return -ENXIO if there is already a queued urb for this endpoint? */
-
- /* Epid allocation, empty check and list add must be protected.
-
- Epid allocation because if we find an existing epid for this endpoint an urb might be
- completed (emptying the list) before we add the new urb to the list, causing the epid
- to be de-allocated. We would then start the transfer with an invalid epid -> epid attn.
-
- Empty check and add because otherwise we might conclude that the list is not empty,
- after which it becomes empty before we add the new urb to the list, causing us not to
- insert the new traffic into the SB list. */
-
- spin_lock_irqsave(&urb_list_lock, flags);
- epid = etrax_usb_setup_epid(urb);
- if (epid == -1) {
- spin_unlock_irqrestore(&urb_list_lock, flags);
- DBFEXIT;
- return -ENOMEM;
- }
- empty = urb_list_empty(epid);
- urb_list_add(urb, epid);
- spin_unlock_irqrestore(&urb_list_lock, flags);
-
- dbg_ctrl("Adding ctrl urb 0x%lx to %s list, epid %d",
- (unsigned long)urb, empty ? "empty" : "", epid);
-
- /* Mark the urb as being in progress. */
- urb->status = -EINPROGRESS;
-
- /* Setup the hcpriv data. */
- urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
- assert(urb_priv != NULL);
- /* This sets rx_offset to 0. */
- urb_priv->urb_state = NOT_STARTED;
- urb->hcpriv = urb_priv;
-
- if (empty) {
- etrax_usb_add_to_ctrl_sb_list(urb, epid);
- }
-
- DBFEXIT;
-
- return 0;
-}
-
-static void etrax_usb_add_to_ctrl_sb_list(struct urb *urb, int epid)
-{
- USB_SB_Desc_t *sb_desc_setup;
- USB_SB_Desc_t *sb_desc_data;
- USB_SB_Desc_t *sb_desc_status;
-
- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-
- unsigned long flags;
- char maxlen;
-
- DBFENTER;
-
- maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
-
- sb_desc_setup = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
- assert(sb_desc_setup != NULL);
- sb_desc_status = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
- assert(sb_desc_status != NULL);
-
- /* Initialize the mandatory setup SB descriptor (used only in control transfers) */
- sb_desc_setup->sw_len = 8;
- sb_desc_setup->command = (IO_FIELD(USB_SB_command, rem, 0) |
- IO_STATE(USB_SB_command, tt, setup) |
- IO_STATE(USB_SB_command, full, yes) |
- IO_STATE(USB_SB_command, eot, yes));
-
- sb_desc_setup->buf = virt_to_phys(urb->setup_packet);
-
- if (usb_pipeout(urb->pipe)) {
- dbg_ctrl("Transfer for epid %d is OUT", epid);
-
- /* If this Control OUT transfer has an optional data stage we add an OUT token
- before the mandatory IN (status) token, hence the reordered SB list */
-
- sb_desc_setup->next = virt_to_phys(sb_desc_status);
- if (urb->transfer_buffer) {
-
- dbg_ctrl("This OUT transfer has an extra data stage");
-
- sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
- assert(sb_desc_data != NULL);
-
- sb_desc_setup->next = virt_to_phys(sb_desc_data);
-
- sb_desc_data->sw_len = urb->transfer_buffer_length;
- sb_desc_data->command = (IO_STATE(USB_SB_command, tt, out) |
- IO_STATE(USB_SB_command, full, yes) |
- IO_STATE(USB_SB_command, eot, yes));
- sb_desc_data->buf = virt_to_phys(urb->transfer_buffer);
- sb_desc_data->next = virt_to_phys(sb_desc_status);
- }
-
- sb_desc_status->sw_len = 1;
- sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
- IO_STATE(USB_SB_command, tt, in) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, intr, yes) |
- IO_STATE(USB_SB_command, eol, yes));
-
- sb_desc_status->buf = 0;
- sb_desc_status->next = 0;
-
- } else if (usb_pipein(urb->pipe)) {
-
- dbg_ctrl("Transfer for epid %d is IN", epid);
- dbg_ctrl("transfer_buffer_length = %d", urb->transfer_buffer_length);
- dbg_ctrl("rem is calculated to %d", urb->transfer_buffer_length % maxlen);
-
- sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
- assert(sb_desc_data != NULL);
-
- sb_desc_setup->next = virt_to_phys(sb_desc_data);
-
- sb_desc_data->sw_len = urb->transfer_buffer_length ?
- (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
- dbg_ctrl("sw_len got %d", sb_desc_data->sw_len);
-
- sb_desc_data->command =
- (IO_FIELD(USB_SB_command, rem,
- urb->transfer_buffer_length % maxlen) |
- IO_STATE(USB_SB_command, tt, in) |
- IO_STATE(USB_SB_command, eot, yes));
-
- sb_desc_data->buf = 0;
- sb_desc_data->next = virt_to_phys(sb_desc_status);
-
- /* Read comment at zout_buffer declaration for an explanation to this. */
- sb_desc_status->sw_len = 1;
- sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
- IO_STATE(USB_SB_command, tt, zout) |
- IO_STATE(USB_SB_command, full, yes) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, intr, yes) |
- IO_STATE(USB_SB_command, eol, yes));
-
- sb_desc_status->buf = virt_to_phys(&zout_buffer[0]);
- sb_desc_status->next = 0;
- }
-
- urb_priv->first_sb = sb_desc_setup;
- urb_priv->last_sb = sb_desc_status;
- urb_priv->epid = epid;
-
- urb_priv->urb_state = STARTED;
-
- /* Reset toggle bits and reset error count, remember to di and ei */
- /* Warning: it is possible that this locking doesn't work with bottom-halves */
-
- save_flags(flags);
- cli();
-
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
- if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
- panic("Hold was set in %s", __FUNCTION__);
- }
-
-
- /* FIXME: Compare with etrax_usb_add_to_bulk_sb_list where the toggle bits
- are set to a specific value. Why the difference? Read "Transfer and Toggle Bits
- in Designer's Reference, p. 8 - 11. */
- *R_USB_EPT_DATA &=
- ~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
- IO_MASK(R_USB_EPT_DATA, error_count_out) |
- IO_MASK(R_USB_EPT_DATA, t_in) |
- IO_MASK(R_USB_EPT_DATA, t_out));
-
- /* Since we use the rx interrupt to complete ctrl urbs, we can enable interrupts now
- (i.e. we don't check the sub pointer on an eot interrupt like we do for bulk traffic). */
- restore_flags(flags);
-
- /* Assert that the EP descriptor is disabled. */
- assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
-
- /* Set up and enable the EP descriptor. */
- TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_setup);
- TxCtrlEPList[epid].hw_len = 0;
- TxCtrlEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
-
- /* We start the DMA sub channel without checking if it's running or not, because:
- 1) If it's already running, issuing the start command is a nop.
- 2) We avoid a test-and-set race condition. */
- *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
-
- DBFEXIT;
-}
-
-static void etrax_usb_complete_ctrl_urb(struct urb *urb, int status)
-{
- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- int epid = urb_priv->epid;
-
- DBFENTER;
-
- if (status)
- warn("Completing ctrl urb with status %d.", status);
-
- dbg_ctrl("Completing ctrl epid %d, urb 0x%lx", epid, (unsigned long)urb);
-
- /* Remove this urb from the list. */
- urb_list_del(urb, epid);
-
- /* For an IN pipe, we always set the actual length, regardless of whether there was
- an error or not (which means the device driver can use the data if it wants to). */
- if (usb_pipein(urb->pipe)) {
- urb->actual_length = urb_priv->rx_offset;
- }
-
- /* FIXME: Is there something of the things below we shouldn't do if there was an error?
- Like, maybe we shouldn't insert more traffic. */
-
- /* Remember to free the SBs. */
- etrax_remove_from_sb_list(urb);
- kfree(urb_priv);
- urb->hcpriv = 0;
-
- /* If there are any more urbs in the list we'd better start sending. */
- if (!urb_list_empty(epid)) {
- struct urb *new_urb;
-
- /* Get the first urb. */
- new_urb = urb_list_first(epid);
- assert(new_urb);
-
- dbg_ctrl("More ctrl for epid %d, first urb = 0x%lx", epid, (unsigned long)new_urb);
-
- etrax_usb_add_to_ctrl_sb_list(new_urb, epid);
- }
-
- urb->status = status;
-
- /* We let any non-zero status from the layer above have precedence. */
- if (status == 0) {
- /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
- is to be treated as an error. */
- if (urb->transfer_flags & URB_SHORT_NOT_OK) {
- if (usb_pipein(urb->pipe) &&
- (urb->actual_length !=
- usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
- urb->status = -EREMOTEIO;
- }
- }
- }
-
- if (urb->complete) {
- urb->complete(urb, NULL);
- }
-
- if (urb_list_empty(epid)) {
- /* No more traffic. Time to clean up. */
- etrax_usb_free_epid(epid);
- /* Must set sub pointer to 0. */
- dbg_ctrl("No ctrl for epid %d", epid);
- TxCtrlEPList[epid].sub = 0;
- }
-
- DBFEXIT;
-}
-
-static int etrax_usb_submit_intr_urb(struct urb *urb)
-{
-
- int epid;
-
- DBFENTER;
-
- if (usb_pipeout(urb->pipe)) {
- /* Unsupported transfer type.
- We don't support interrupt out traffic. (If we do, we can't support
- intervals for neither in or out traffic, but are forced to schedule all
- interrupt traffic in one frame.) */
- return -EINVAL;
- }
-
- epid = etrax_usb_setup_epid(urb);
- if (epid == -1) {
- DBFEXIT;
- return -ENOMEM;
- }
-
- if (!urb_list_empty(epid)) {
- /* There is already a queued urb for this endpoint. */
- etrax_usb_free_epid(epid);
- return -ENXIO;
- }
-
- urb->status = -EINPROGRESS;
-
- dbg_intr("Add intr urb 0x%lx, to list, epid %d", (unsigned long)urb, epid);
-
- urb_list_add(urb, epid);
- etrax_usb_add_to_intr_sb_list(urb, epid);
-
- return 0;
-
- DBFEXIT;
-}
-
-static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid)
-{
-
- volatile USB_EP_Desc_t *tmp_ep;
- volatile USB_EP_Desc_t *first_ep;
-
- char maxlen;
- int interval;
- int i;
-
- etrax_urb_priv_t *urb_priv;
-
- DBFENTER;
-
- maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
- interval = urb->interval;
-
- urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
- assert(urb_priv != NULL);
- urb->hcpriv = urb_priv;
-
- first_ep = &TxIntrEPList[0];
-
- /* Round of the interval to 2^n, it is obvious that this code favours
- smaller numbers, but that is actually a good thing */
- /* FIXME: The "rounding error" for larger intervals will be quite
- large. For in traffic this shouldn't be a problem since it will only
- mean that we "poll" more often. */
- for (i = 0; interval; i++) {
- interval = interval >> 1;
- }
- interval = 1 << (i - 1);
-
- dbg_intr("Interval rounded to %d", interval);
-
- tmp_ep = first_ep;
- i = 0;
- do {
- if (tmp_ep->command & IO_MASK(USB_EP_command, eof)) {
- if ((i % interval) == 0) {
- /* Insert the traffic ep after tmp_ep */
- USB_EP_Desc_t *ep_desc;
- USB_SB_Desc_t *sb_desc;
-
- dbg_intr("Inserting EP for epid %d", epid);
-
- ep_desc = (USB_EP_Desc_t *)
- kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
- sb_desc = (USB_SB_Desc_t *)
- kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
- assert(ep_desc != NULL);
- CHECK_ALIGN(ep_desc);
- assert(sb_desc != NULL);
-
- ep_desc->sub = virt_to_phys(sb_desc);
- ep_desc->hw_len = 0;
- ep_desc->command = (IO_FIELD(USB_EP_command, epid, epid) |
- IO_STATE(USB_EP_command, enable, yes));
-
-
- /* Round upwards the number of packets of size maxlen
- that this SB descriptor should receive. */
- sb_desc->sw_len = urb->transfer_buffer_length ?
- (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
- sb_desc->next = 0;
- sb_desc->buf = 0;
- sb_desc->command =
- (IO_FIELD(USB_SB_command, rem, urb->transfer_buffer_length % maxlen) |
- IO_STATE(USB_SB_command, tt, in) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, eol, yes));
-
- ep_desc->next = tmp_ep->next;
- tmp_ep->next = virt_to_phys(ep_desc);
- }
- i++;
- }
- tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next);
- } while (tmp_ep != first_ep);
-
-
- /* Note that first_sb/last_sb doesn't apply to interrupt traffic. */
- urb_priv->epid = epid;
-
- /* We start the DMA sub channel without checking if it's running or not, because:
- 1) If it's already running, issuing the start command is a nop.
- 2) We avoid a test-and-set race condition. */
- *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
-
- DBFEXIT;
-}
-
-
-
-static void etrax_usb_complete_intr_urb(struct urb *urb, int status)
-{
- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- int epid = urb_priv->epid;
-
- DBFENTER;
-
- if (status)
- warn("Completing intr urb with status %d.", status);
-
- dbg_intr("Completing intr epid %d, urb 0x%lx", epid, (unsigned long)urb);
-
- urb->status = status;
- urb->actual_length = urb_priv->rx_offset;
-
- dbg_intr("interrupt urb->actual_length = %d", urb->actual_length);
-
- /* We let any non-zero status from the layer above have precedence. */
- if (status == 0) {
- /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
- is to be treated as an error. */
- if (urb->transfer_flags & URB_SHORT_NOT_OK) {
- if (urb->actual_length !=
- usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
- urb->status = -EREMOTEIO;
- }
- }
- }
-
- /* The driver will resubmit the URB so we need to remove it first */
- etrax_usb_unlink_urb(urb, 0);
- if (urb->complete) {
- urb->complete(urb, NULL);
- }
-
- DBFEXIT;
-}
-
-
-static int etrax_usb_submit_isoc_urb(struct urb *urb)
-{
- int epid;
- unsigned long flags;
-
- DBFENTER;
-
- dbg_isoc("Submitting isoc urb = 0x%lx", (unsigned long)urb);
-
- /* Epid allocation, empty check and list add must be protected.
- Read about this in etrax_usb_submit_ctrl_urb. */
-
- spin_lock_irqsave(&urb_list_lock, flags);
- /* Is there an active epid for this urb ? */
- epid = etrax_usb_setup_epid(urb);
- if (epid == -1) {
- DBFEXIT;
- spin_unlock_irqrestore(&urb_list_lock, flags);
- return -ENOMEM;
- }
-
- /* Ok, now we got valid endpoint, lets insert some traffic */
-
- urb->status = -EINPROGRESS;
-
- /* Find the last urb in the URB_List and add this urb after that one.
- Also add the traffic, that is do an etrax_usb_add_to_isoc_sb_list. This
- is important to make this in "real time" since isochronous traffic is
- time sensitive. */
-
- dbg_isoc("Adding isoc urb to (possibly empty) list");
- urb_list_add(urb, epid);
- etrax_usb_add_to_isoc_sb_list(urb, epid);
- spin_unlock_irqrestore(&urb_list_lock, flags);
-
- DBFEXIT;
-
- return 0;
-}
-
-static void etrax_usb_check_error_isoc_ep(const int epid)
-{
- unsigned long int flags;
- int error_code;
- __u32 r_usb_ept_data;
-
- /* We can't read R_USB_EPID_ATTN here since it would clear the iso_eof,
- bulk_eot and epid_attn interrupts. So we just check the status of
- the epid without testing if for it in R_USB_EPID_ATTN. */
-
-
- save_flags(flags);
- cli();
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
- /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
- registers, they are located at the same address and are of the same size.
- In other words, this read should be ok for isoc also. */
- r_usb_ept_data = *R_USB_EPT_DATA;
- restore_flags(flags);
-
- error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
-
- if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
- warn("Hold was set for epid %d.", epid);
- return;
- }
-
- if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, no_error)) {
-
- /* This indicates that the SB list of the ept was completed before
- new data was appended to it. This is not an error, but indicates
- large system or USB load and could possibly cause trouble for
- very timing sensitive USB device drivers so we log it.
- */
- info("Isoc. epid %d disabled with no error", epid);
- return;
-
- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, stall)) {
- /* Not really a protocol error, just says that the endpoint gave
- a stall response. Note that error_code cannot be stall for isoc. */
- panic("Isoc traffic cannot stall");
-
- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, bus_error)) {
- /* Two devices responded to a transaction request. Must be resolved
- by software. FIXME: Reset ports? */
- panic("Bus error for epid %d."
- " Two devices responded to transaction request",
- epid);
-
- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
- /* DMA overrun or underrun. */
- warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
-
- /* It seems that error_code = buffer_error in
- R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
- are the same error. */
- }
-}
-
-
-static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid)
-{
-
- int i = 0;
-
- etrax_urb_priv_t *urb_priv;
- USB_SB_Desc_t *prev_sb_desc, *next_sb_desc, *temp_sb_desc;
-
- DBFENTER;
-
- prev_sb_desc = next_sb_desc = temp_sb_desc = NULL;
-
- urb_priv = kzalloc(sizeof(etrax_urb_priv_t), GFP_ATOMIC);
- assert(urb_priv != NULL);
-
- urb->hcpriv = urb_priv;
- urb_priv->epid = epid;
-
- if (usb_pipeout(urb->pipe)) {
-
- if (urb->number_of_packets == 0) panic("etrax_usb_add_to_isoc_sb_list 0 packets\n");
-
- dbg_isoc("Transfer for epid %d is OUT", epid);
- dbg_isoc("%d packets in URB", urb->number_of_packets);
-
- /* Create one SB descriptor for each packet and link them together. */
- for (i = 0; i < urb->number_of_packets; i++) {
- if (!urb->iso_frame_desc[i].length)
- continue;
-
- next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_ATOMIC);
- assert(next_sb_desc != NULL);
-
- if (urb->iso_frame_desc[i].length > 0) {
-
- next_sb_desc->command = (IO_STATE(USB_SB_command, tt, out) |
- IO_STATE(USB_SB_command, eot, yes));
-
- next_sb_desc->sw_len = urb->iso_frame_desc[i].length;
- next_sb_desc->buf = virt_to_phys((char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset);
-
- /* Check if full length transfer. */
- if (urb->iso_frame_desc[i].length ==
- usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
- next_sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
- }
- } else {
- dbg_isoc("zero len packet");
- next_sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) |
- IO_STATE(USB_SB_command, tt, zout) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, full, yes));
-
- next_sb_desc->sw_len = 1;
- next_sb_desc->buf = virt_to_phys(&zout_buffer[0]);
- }
-
- /* First SB descriptor that belongs to this urb */
- if (i == 0)
- urb_priv->first_sb = next_sb_desc;
- else
- prev_sb_desc->next = virt_to_phys(next_sb_desc);
-
- prev_sb_desc = next_sb_desc;
- }
-
- next_sb_desc->command |= (IO_STATE(USB_SB_command, intr, yes) |
- IO_STATE(USB_SB_command, eol, yes));
- next_sb_desc->next = 0;
- urb_priv->last_sb = next_sb_desc;
-
- } else if (usb_pipein(urb->pipe)) {
-
- dbg_isoc("Transfer for epid %d is IN", epid);
- dbg_isoc("transfer_buffer_length = %d", urb->transfer_buffer_length);
- dbg_isoc("rem is calculated to %d", urb->iso_frame_desc[urb->number_of_packets - 1].length);
-
- /* Note that in descriptors for periodic traffic are not consumed. This means that
- the USB controller never propagates in the SB list. In other words, if there already
- is an SB descriptor in the list for this EP we don't have to do anything. */
- if (TxIsocEPList[epid].sub == 0) {
- dbg_isoc("Isoc traffic not already running, allocating SB");
-
- next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_ATOMIC);
- assert(next_sb_desc != NULL);
-
- next_sb_desc->command = (IO_STATE(USB_SB_command, tt, in) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, eol, yes));
-
- next_sb_desc->next = 0;
- next_sb_desc->sw_len = 1; /* Actual number of packets is not relevant
- for periodic in traffic as long as it is more
- than zero. Set to 1 always. */
- next_sb_desc->buf = 0;
-
- /* The rem field is don't care for isoc traffic, so we don't set it. */
-
- /* Only one SB descriptor that belongs to this urb. */
- urb_priv->first_sb = next_sb_desc;
- urb_priv->last_sb = next_sb_desc;
-
- } else {
-
- dbg_isoc("Isoc traffic already running, just setting first/last_sb");
-
- /* Each EP for isoc in will have only one SB descriptor, setup when submitting the
- already active urb. Note that even though we may have several first_sb/last_sb
- pointing at the same SB descriptor, they are freed only once (when the list has
- become empty). */
- urb_priv->first_sb = phys_to_virt(TxIsocEPList[epid].sub);
- urb_priv->last_sb = phys_to_virt(TxIsocEPList[epid].sub);
- return;
- }
-
- }
-
- /* Find the spot to insert this urb and add it. */
- if (TxIsocEPList[epid].sub == 0) {
- /* First SB descriptor inserted in this list (in or out). */
- dbg_isoc("Inserting SB desc first in list");
- TxIsocEPList[epid].hw_len = 0;
- TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb);
-
- } else {
- /* Isochronous traffic is already running, insert new traffic last (only out). */
- dbg_isoc("Inserting SB desc last in list");
- temp_sb_desc = phys_to_virt(TxIsocEPList[epid].sub);
- while ((temp_sb_desc->command & IO_MASK(USB_SB_command, eol)) !=
- IO_STATE(USB_SB_command, eol, yes)) {
- assert(temp_sb_desc->next);
- temp_sb_desc = phys_to_virt(temp_sb_desc->next);
- }
- dbg_isoc("Appending list on desc 0x%p", temp_sb_desc);
-
- /* Next pointer must be set before eol is removed. */
- temp_sb_desc->next = virt_to_phys(urb_priv->first_sb);
- /* Clear the previous end of list flag since there is a new in the
- added SB descriptor list. */
- temp_sb_desc->command &= ~IO_MASK(USB_SB_command, eol);
-
- if (!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
- /* 8.8.5 in Designer's Reference says we should check for and correct
- any errors in the EP here. That should not be necessary if epid_attn
- is handled correctly, so we assume all is ok. */
- dbg_isoc("EP disabled");
- etrax_usb_check_error_isoc_ep(epid);
-
- /* The SB list was exhausted. */
- if (virt_to_phys(urb_priv->last_sb) != TxIsocEPList[epid].sub) {
- /* The new sublist did not get processed before the EP was
- disabled. Setup the EP again. */
- dbg_isoc("Set EP sub to new list");
- TxIsocEPList[epid].hw_len = 0;
- TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb);
- }
- }
- }
-
- if (urb->transfer_flags & URB_ISO_ASAP) {
- /* The isoc transfer should be started as soon as possible. The start_frame
- field is a return value if URB_ISO_ASAP was set. Comparing R_USB_FM_NUMBER
- with a USB Chief trace shows that the first isoc IN token is sent 2 frames
- later. I'm not sure how this affects usage of the start_frame field by the
- device driver, or how it affects things when USB_ISO_ASAP is not set, so
- therefore there's no compensation for the 2 frame "lag" here. */
- urb->start_frame = (*R_USB_FM_NUMBER & 0x7ff);
- TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
- urb_priv->urb_state = STARTED;
- dbg_isoc("URB_ISO_ASAP set, urb->start_frame set to %d", urb->start_frame);
- } else {
- /* Not started yet. */
- urb_priv->urb_state = NOT_STARTED;
- dbg_isoc("urb_priv->urb_state set to NOT_STARTED");
- }
-
- /* We start the DMA sub channel without checking if it's running or not, because:
- 1) If it's already running, issuing the start command is a nop.
- 2) We avoid a test-and-set race condition. */
- *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
-
- DBFEXIT;
-}
-
-static void etrax_usb_complete_isoc_urb(struct urb *urb, int status)
-{
- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- int epid = urb_priv->epid;
- int auto_resubmit = 0;
-
- DBFENTER;
- dbg_isoc("complete urb 0x%p, status %d", urb, status);
-
- if (status)
- warn("Completing isoc urb with status %d.", status);
-
- if (usb_pipein(urb->pipe)) {
- int i;
-
- /* Make that all isoc packets have status and length set before
- completing the urb. */
- for (i = urb_priv->isoc_packet_counter; i < urb->number_of_packets; i++) {
- urb->iso_frame_desc[i].actual_length = 0;
- urb->iso_frame_desc[i].status = -EPROTO;
- }
-
- urb_list_del(urb, epid);
-
- if (!list_empty(&urb_list[epid])) {
- ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
- } else {
- unsigned long int flags;
- if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
- /* The EP was enabled, disable it and wait. */
- TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-
- /* Ah, the luxury of busy-wait. */
- while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
- }
-
- etrax_remove_from_sb_list(urb);
- TxIsocEPList[epid].sub = 0;
- TxIsocEPList[epid].hw_len = 0;
-
- save_flags(flags);
- cli();
- etrax_usb_free_epid(epid);
- restore_flags(flags);
- }
-
- urb->hcpriv = 0;
- kfree(urb_priv);
-
- /* Release allocated bandwidth. */
- usb_release_bandwidth(urb->dev, urb, 0);
- } else if (usb_pipeout(urb->pipe)) {
- int freed_descr;
-
- dbg_isoc("Isoc out urb complete 0x%p", urb);
-
- /* Update the urb list. */
- urb_list_del(urb, epid);
-
- freed_descr = etrax_remove_from_sb_list(urb);
- dbg_isoc("freed %d descriptors of %d packets", freed_descr, urb->number_of_packets);
- assert(freed_descr == urb->number_of_packets);
- urb->hcpriv = 0;
- kfree(urb_priv);
-
- /* Release allocated bandwidth. */
- usb_release_bandwidth(urb->dev, urb, 0);
- }
-
- urb->status = status;
- if (urb->complete) {
- urb->complete(urb, NULL);
- }
-
- if (auto_resubmit) {
- /* Check that urb was not unlinked by the complete callback. */
- if (__urb_list_entry(urb, epid)) {
- /* Move this one down the list. */
- urb_list_move_last(urb, epid);
-
- /* Mark the now first urb as started (may already be). */
- ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
-
- /* Must set this to 0 since this urb is still active after
- completion. */
- urb_priv->isoc_packet_counter = 0;
- } else {
- warn("(ISOC) automatic resubmit urb 0x%p removed by complete.", urb);
- }
- }
-
- DBFEXIT;
-}
-
-static void etrax_usb_complete_urb(struct urb *urb, int status)
-{
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_BULK:
- etrax_usb_complete_bulk_urb(urb, status);
- break;
- case PIPE_CONTROL:
- etrax_usb_complete_ctrl_urb(urb, status);
- break;
- case PIPE_INTERRUPT:
- etrax_usb_complete_intr_urb(urb, status);
- break;
- case PIPE_ISOCHRONOUS:
- etrax_usb_complete_isoc_urb(urb, status);
- break;
- default:
- err("Unknown pipetype");
- }
-}
-
-
-
-static irqreturn_t etrax_usb_hc_interrupt_top_half(int irq, void *vhc)
-{
- usb_interrupt_registers_t *reg;
- unsigned long flags;
- __u32 irq_mask;
- __u8 status;
- __u32 epid_attn;
- __u16 port_status_1;
- __u16 port_status_2;
- __u32 fm_number;
-
- DBFENTER;
-
- /* Read critical registers into local variables, do kmalloc afterwards. */
- save_flags(flags);
- cli();
-
- irq_mask = *R_USB_IRQ_MASK_READ;
- /* Reading R_USB_STATUS clears the ctl_status interrupt. Note that R_USB_STATUS
- must be read before R_USB_EPID_ATTN since reading the latter clears the
- ourun and perror fields of R_USB_STATUS. */
- status = *R_USB_STATUS;
-
- /* Reading R_USB_EPID_ATTN clears the iso_eof, bulk_eot and epid_attn interrupts. */
- epid_attn = *R_USB_EPID_ATTN;
-
- /* Reading R_USB_RH_PORT_STATUS_1 and R_USB_RH_PORT_STATUS_2 clears the
- port_status interrupt. */
- port_status_1 = *R_USB_RH_PORT_STATUS_1;
- port_status_2 = *R_USB_RH_PORT_STATUS_2;
-
- /* Reading R_USB_FM_NUMBER clears the sof interrupt. */
- /* Note: the lower 11 bits contain the actual frame number, sent with each sof. */
- fm_number = *R_USB_FM_NUMBER;
-
- restore_flags(flags);
-
- reg = (usb_interrupt_registers_t *)kmem_cache_alloc(top_half_reg_cache, GFP_ATOMIC);
-
- assert(reg != NULL);
-
- reg->hc = (etrax_hc_t *)vhc;
-
- /* Now put register values into kmalloc'd area. */
- reg->r_usb_irq_mask_read = irq_mask;
- reg->r_usb_status = status;
- reg->r_usb_epid_attn = epid_attn;
- reg->r_usb_rh_port_status_1 = port_status_1;
- reg->r_usb_rh_port_status_2 = port_status_2;
- reg->r_usb_fm_number = fm_number;
-
- INIT_WORK(&reg->usb_bh, etrax_usb_hc_interrupt_bottom_half, reg);
- schedule_work(&reg->usb_bh);
-
- DBFEXIT;
-
- return IRQ_HANDLED;
-}
-
-static void etrax_usb_hc_interrupt_bottom_half(void *data)
-{
- usb_interrupt_registers_t *reg = (usb_interrupt_registers_t *)data;
- __u32 irq_mask = reg->r_usb_irq_mask_read;
-
- DBFENTER;
-
- /* Interrupts are handled in order of priority. */
- if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, epid_attn)) {
- etrax_usb_hc_epid_attn_interrupt(reg);
- }
- if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) {
- etrax_usb_hc_port_status_interrupt(reg);
- }
- if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, ctl_status)) {
- etrax_usb_hc_ctl_status_interrupt(reg);
- }
- if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, iso_eof)) {
- etrax_usb_hc_isoc_eof_interrupt();
- }
- if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, bulk_eot)) {
- /* Update/restart the bulk start timer since obviously the channel is running. */
- mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
- /* Update/restart the bulk eot timer since we just received an bulk eot interrupt. */
- mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
-
- etrax_usb_hc_bulk_eot_interrupt(0);
- }
-
- kmem_cache_free(top_half_reg_cache, reg);
-
- DBFEXIT;
-}
-
-
-void etrax_usb_hc_isoc_eof_interrupt(void)
-{
- struct urb *urb;
- etrax_urb_priv_t *urb_priv;
- int epid;
- unsigned long flags;
-
- DBFENTER;
-
- /* Do not check the invalid epid (it has a valid sub pointer). */
- for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
-
- /* Do not check the invalid epid (it has a valid sub pointer). */
- if ((epid == DUMMY_EPID) || (epid == INVALID_EPID))
- continue;
-
- /* Disable interrupts to block the isoc out descriptor interrupt handler
- from being called while the isoc EPID list is being checked.
- */
- save_flags(flags);
- cli();
-
- if (TxIsocEPList[epid].sub == 0) {
- /* Nothing here to see. */
- restore_flags(flags);
- continue;
- }
-
- /* Get the first urb (if any). */
- urb = urb_list_first(epid);
- if (urb == 0) {
- warn("Ignoring NULL urb");
- restore_flags(flags);
- continue;
- }
- if (usb_pipein(urb->pipe)) {
-
- /* Sanity check. */
- assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
-
- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- assert(urb_priv);
-
- if (urb_priv->urb_state == NOT_STARTED) {
-
- /* If ASAP is not set and urb->start_frame is the current frame,
- start the transfer. */
- if (!(urb->transfer_flags & URB_ISO_ASAP) &&
- (urb->start_frame == (*R_USB_FM_NUMBER & 0x7ff))) {
-
- dbg_isoc("Enabling isoc IN EP descr for epid %d", epid);
- TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
-
- /* This urb is now active. */
- urb_priv->urb_state = STARTED;
- continue;
- }
- }
- }
- restore_flags(flags);
- }
-
- DBFEXIT;
-
-}
-
-void etrax_usb_hc_bulk_eot_interrupt(int timer_induced)
-{
- int epid;
-
- /* The technique is to run one urb at a time, wait for the eot interrupt at which
- point the EP descriptor has been disabled. */
-
- DBFENTER;
- dbg_bulk("bulk eot%s", timer_induced ? ", called by timer" : "");
-
- for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
-
- if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) &&
- (TxBulkEPList[epid].sub != 0)) {
-
- struct urb *urb;
- etrax_urb_priv_t *urb_priv;
- unsigned long flags;
- __u32 r_usb_ept_data;
-
- /* Found a disabled EP descriptor which has a non-null sub pointer.
- Verify that this ctrl EP descriptor got disabled no errors.
- FIXME: Necessary to check error_code? */
- dbg_bulk("for epid %d?", epid);
-
- /* Get the first urb. */
- urb = urb_list_first(epid);
-
- /* FIXME: Could this happen for valid reasons? Why did it disappear? Because of
- wrong unlinking? */
- if (!urb) {
- warn("NULL urb for epid %d", epid);
- continue;
- }
-
- assert(urb);
- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- assert(urb_priv);
-
- /* Sanity checks. */
- assert(usb_pipetype(urb->pipe) == PIPE_BULK);
- if (phys_to_virt(TxBulkEPList[epid].sub) != urb_priv->last_sb) {
- err("bulk endpoint got disabled before reaching last sb");
- }
-
- /* For bulk IN traffic, there seems to be a race condition between
- between the bulk eot and eop interrupts, or rather an uncertainty regarding
- the order in which they happen. Normally we expect the eop interrupt from
- DMA channel 9 to happen before the eot interrupt.
-
- Therefore, we complete the bulk IN urb in the rx interrupt handler instead. */
-
- if (usb_pipein(urb->pipe)) {
- dbg_bulk("in urb, continuing");
- continue;
- }
-
- save_flags(flags);
- cli();
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
- r_usb_ept_data = *R_USB_EPT_DATA;
- restore_flags(flags);
-
- if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) ==
- IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
- /* This means that the endpoint has no error, is disabled
- and had inserted traffic, i.e. transfer successfully completed. */
- etrax_usb_complete_bulk_urb(urb, 0);
- } else {
- /* Shouldn't happen. We expect errors to be caught by epid attention. */
- err("Found disabled bulk EP desc, error_code != no_error");
- }
- }
- }
-
- /* Normally, we should find (at least) one disabled EP descriptor with a valid sub pointer.
- However, because of the uncertainty in the deliverance of the eop/eot interrupts, we may
- not. Also, we might find two disabled EPs when handling an eot interrupt, and then find
- none the next time. */
-
- DBFEXIT;
-
-}
-
-void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg)
-{
- /* This function handles the epid attention interrupt. There are a variety of reasons
- for this interrupt to happen (Designer's Reference, p. 8 - 22 for the details):
-
- invalid ep_id - Invalid epid in an EP (EP disabled).
- stall - Not strictly an error condition (EP disabled).
- 3rd error - Three successive transaction errors (EP disabled).
- buffer ourun - Buffer overrun or underrun (EP disabled).
- past eof1 - Intr or isoc transaction proceeds past EOF1.
- near eof - Intr or isoc transaction would not fit inside the frame.
- zout transfer - If zout transfer for a bulk endpoint (EP disabled).
- setup transfer - If setup transfer for a non-ctrl endpoint (EP disabled). */
-
- int epid;
-
-
- DBFENTER;
-
- assert(reg != NULL);
-
- /* Note that we loop through all epids. We still want to catch errors for
- the invalid one, even though we might handle them differently. */
- for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
-
- if (test_bit(epid, (void *)&reg->r_usb_epid_attn)) {
-
- struct urb *urb;
- __u32 r_usb_ept_data;
- unsigned long flags;
- int error_code;
-
- save_flags(flags);
- cli();
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
- /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
- registers, they are located at the same address and are of the same size.
- In other words, this read should be ok for isoc also. */
- r_usb_ept_data = *R_USB_EPT_DATA;
- restore_flags(flags);
-
- /* First some sanity checks. */
- if (epid == INVALID_EPID) {
- /* FIXME: What if it became disabled? Could seriously hurt interrupt
- traffic. (Use do_intr_recover.) */
- warn("Got epid_attn for INVALID_EPID (%d).", epid);
- err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
- err("R_USB_STATUS = 0x%x", reg->r_usb_status);
- continue;
- } else if (epid == DUMMY_EPID) {
- /* We definitely don't care about these ones. Besides, they are
- always disabled, so any possible disabling caused by the
- epid attention interrupt is irrelevant. */
- warn("Got epid_attn for DUMMY_EPID (%d).", epid);
- continue;
- }
-
- /* Get the first urb in the urb list for this epid. We blatantly assume
- that only the first urb could have caused the epid attention.
- (For bulk and ctrl, only one urb is active at any one time. For intr
- and isoc we remove them once they are completed.) */
- urb = urb_list_first(epid);
-
- if (urb == NULL) {
- err("Got epid_attn for epid %i with no urb.", epid);
- err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
- err("R_USB_STATUS = 0x%x", reg->r_usb_status);
- continue;
- }
-
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_BULK:
- warn("Got epid attn for bulk endpoint, epid %d", epid);
- break;
- case PIPE_CONTROL:
- warn("Got epid attn for control endpoint, epid %d", epid);
- break;
- case PIPE_INTERRUPT:
- warn("Got epid attn for interrupt endpoint, epid %d", epid);
- break;
- case PIPE_ISOCHRONOUS:
- warn("Got epid attn for isochronous endpoint, epid %d", epid);
- break;
- }
-
- if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {
- if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
- warn("Hold was set for epid %d.", epid);
- continue;
- }
- }
-
- /* Even though error_code occupies bits 22 - 23 in both R_USB_EPT_DATA and
- R_USB_EPT_DATA_ISOC, we separate them here so we don't forget in other places. */
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
- } else {
- error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data);
- }
-
- /* Using IO_STATE_VALUE on R_USB_EPT_DATA should be ok for isoc also. */
- if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
-
- /* Isoc traffic doesn't have error_count_in/error_count_out. */
- if ((usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) &&
- (IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3 ||
- IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3)) {
- /* 3rd error. */
- warn("3rd error for epid %i", epid);
- etrax_usb_complete_urb(urb, -EPROTO);
-
- } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
-
- warn("Perror for epid %d", epid);
-
- if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) {
- /* invalid ep_id */
- panic("Perror because of invalid epid."
- " Deconfigured too early?");
- } else {
- /* past eof1, near eof, zout transfer, setup transfer */
-
- /* Dump the urb and the relevant EP descriptor list. */
-
- __dump_urb(urb);
- __dump_ept_data(epid);
- __dump_ep_list(usb_pipetype(urb->pipe));
-
- panic("Something wrong with DMA descriptor contents."
- " Too much traffic inserted?");
- }
- } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
- /* buffer ourun */
- panic("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
- }
-
- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) {
- /* Not really a protocol error, just says that the endpoint gave
- a stall response. Note that error_code cannot be stall for isoc. */
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- panic("Isoc traffic cannot stall");
- }
-
- warn("Stall for epid %d", epid);
- etrax_usb_complete_urb(urb, -EPIPE);
-
- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) {
- /* Two devices responded to a transaction request. Must be resolved
- by software. FIXME: Reset ports? */
- panic("Bus error for epid %d."
- " Two devices responded to transaction request",
- epid);
-
- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
- /* DMA overrun or underrun. */
- warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
-
- /* It seems that error_code = buffer_error in
- R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
- are the same error. */
- etrax_usb_complete_urb(urb, -EPROTO);
- }
- }
- }
-
- DBFEXIT;
-
-}
-
-void etrax_usb_bulk_start_timer_func(unsigned long dummy)
-{
-
- /* We might enable an EP descriptor behind the current DMA position when it's about
- to decide that there are no more bulk traffic and it should stop the bulk channel.
- Therefore we periodically check if the bulk channel is stopped and there is an
- enabled bulk EP descriptor, in which case we start the bulk channel. */
- dbg_bulk("bulk_start_timer timed out.");
-
- if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
- int epid;
-
- dbg_bulk("Bulk DMA channel not running.");
-
- for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
- if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
- dbg_bulk("Found enabled EP for epid %d, starting bulk channel.\n",
- epid);
- *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
-
- /* Restart the bulk eot timer since we just started the bulk channel. */
- mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
-
- /* No need to search any further. */
- break;
- }
- }
- } else {
- dbg_bulk("Bulk DMA channel running.");
- }
-}
-
-void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg)
-{
- etrax_hc_t *hc = reg->hc;
- __u16 r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1;
- __u16 r_usb_rh_port_status_2 = reg->r_usb_rh_port_status_2;
-
- DBFENTER;
-
- /* The Etrax RH does not include a wPortChange register, so this has to be handled in software
- (by saving the old port status value for comparison when the port status interrupt happens).
- See section 11.16.2.6.2 in the USB 1.1 spec for details. */
-
- dbg_rh("hc->rh.prev_wPortStatus_1 = 0x%x", hc->rh.prev_wPortStatus_1);
- dbg_rh("hc->rh.prev_wPortStatus_2 = 0x%x", hc->rh.prev_wPortStatus_2);
- dbg_rh("r_usb_rh_port_status_1 = 0x%x", r_usb_rh_port_status_1);
- dbg_rh("r_usb_rh_port_status_2 = 0x%x", r_usb_rh_port_status_2);
-
- /* C_PORT_CONNECTION is set on any transition. */
- hc->rh.wPortChange_1 |=
- ((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) !=
- (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ?
- (1 << RH_PORT_CONNECTION) : 0;
-
- hc->rh.wPortChange_2 |=
- ((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) !=
- (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ?
- (1 << RH_PORT_CONNECTION) : 0;
-
- /* C_PORT_ENABLE is _only_ set on a one to zero transition, i.e. when
- the port is disabled, not when it's enabled. */
- hc->rh.wPortChange_1 |=
- ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE))
- && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ?
- (1 << RH_PORT_ENABLE) : 0;
-
- hc->rh.wPortChange_2 |=
- ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE))
- && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ?
- (1 << RH_PORT_ENABLE) : 0;
-
- /* C_PORT_SUSPEND is set to one when the device has transitioned out
- of the suspended state, i.e. when suspend goes from one to zero. */
- hc->rh.wPortChange_1 |=
- ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_SUSPEND))
- && !(r_usb_rh_port_status_1 & (1 << RH_PORT_SUSPEND))) ?
- (1 << RH_PORT_SUSPEND) : 0;
-
- hc->rh.wPortChange_2 |=
- ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_SUSPEND))
- && !(r_usb_rh_port_status_2 & (1 << RH_PORT_SUSPEND))) ?
- (1 << RH_PORT_SUSPEND) : 0;
-
-
- /* C_PORT_RESET is set when reset processing on this port is complete. */
- hc->rh.wPortChange_1 |=
- ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET))
- && !(r_usb_rh_port_status_1 & (1 << RH_PORT_RESET))) ?
- (1 << RH_PORT_RESET) : 0;
-
- hc->rh.wPortChange_2 |=
- ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET))
- && !(r_usb_rh_port_status_2 & (1 << RH_PORT_RESET))) ?
- (1 << RH_PORT_RESET) : 0;
-
- /* Save the new values for next port status change. */
- hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1;
- hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2;
-
- dbg_rh("hc->rh.wPortChange_1 set to 0x%x", hc->rh.wPortChange_1);
- dbg_rh("hc->rh.wPortChange_2 set to 0x%x", hc->rh.wPortChange_2);
-
- DBFEXIT;
-
-}
-
-void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg)
-{
- DBFENTER;
-
- /* FIXME: What should we do if we get ourun or perror? Dump the EP and SB
- list for the corresponding epid? */
- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
- panic("USB controller got ourun.");
- }
- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
-
- /* Before, etrax_usb_do_intr_recover was called on this epid if it was
- an interrupt pipe. I don't see how re-enabling all EP descriptors
- will help if there was a programming error. */
- panic("USB controller got perror.");
- }
-
- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, device_mode)) {
- /* We should never operate in device mode. */
- panic("USB controller in device mode.");
- }
-
- /* These if-statements could probably be nested. */
- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, host_mode)) {
- info("USB controller in host mode.");
- }
- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, started)) {
- info("USB controller started.");
- }
- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, running)) {
- info("USB controller running.");
- }
-
- DBFEXIT;
-
-}
-
-
-static int etrax_rh_submit_urb(struct urb *urb)
-{
- struct usb_device *usb_dev = urb->dev;
- etrax_hc_t *hc = usb_dev->bus->hcpriv;
- unsigned int pipe = urb->pipe;
- struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet;
- void *data = urb->transfer_buffer;
- int leni = urb->transfer_buffer_length;
- int len = 0;
- int stat = 0;
-
- __u16 bmRType_bReq;
- __u16 wValue;
- __u16 wIndex;
- __u16 wLength;
-
- DBFENTER;
-
- /* FIXME: What is this interrupt urb that is sent to the root hub? */
- if (usb_pipetype (pipe) == PIPE_INTERRUPT) {
- dbg_rh("Root-Hub submit IRQ: every %d ms", urb->interval);
- hc->rh.urb = urb;
- hc->rh.send = 1;
- /* FIXME: We could probably remove this line since it's done
- in etrax_rh_init_int_timer. (Don't remove it from
- etrax_rh_init_int_timer though.) */
- hc->rh.interval = urb->interval;
- etrax_rh_init_int_timer(urb);
- DBFEXIT;
-
- return 0;
- }
-
- bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8);
- wValue = le16_to_cpu(cmd->wValue);
- wIndex = le16_to_cpu(cmd->wIndex);
- wLength = le16_to_cpu(cmd->wLength);
-
- dbg_rh("bmRType_bReq : 0x%04x (%d)", bmRType_bReq, bmRType_bReq);
- dbg_rh("wValue : 0x%04x (%d)", wValue, wValue);
- dbg_rh("wIndex : 0x%04x (%d)", wIndex, wIndex);
- dbg_rh("wLength : 0x%04x (%d)", wLength, wLength);
-
- switch (bmRType_bReq) {
-
- /* 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 RH_GET_STATUS:
- *(__u16 *) data = cpu_to_le16 (1);
- OK (2);
-
- case RH_GET_STATUS | RH_INTERFACE:
- *(__u16 *) data = cpu_to_le16 (0);
- OK (2);
-
- case RH_GET_STATUS | RH_ENDPOINT:
- *(__u16 *) data = cpu_to_le16 (0);
- OK (2);
-
- case RH_GET_STATUS | RH_CLASS:
- *(__u32 *) data = cpu_to_le32 (0);
- OK (4); /* hub power ** */
-
- case RH_GET_STATUS | RH_OTHER | RH_CLASS:
- if (wIndex == 1) {
- *((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_1);
- *((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_1);
- } else if (wIndex == 2) {
- *((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_2);
- *((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_2);
- } else {
- dbg_rh("RH_GET_STATUS whith invalid wIndex!");
- OK(0);
- }
-
- OK(4);
-
- case RH_CLEAR_FEATURE | RH_ENDPOINT:
- switch (wValue) {
- case (RH_ENDPOINT_STALL):
- OK (0);
- }
- break;
-
- case RH_CLEAR_FEATURE | RH_CLASS:
- switch (wValue) {
- case (RH_C_HUB_OVER_CURRENT):
- OK (0); /* hub power over current ** */
- }
- break;
-
- case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
- switch (wValue) {
- case (RH_PORT_ENABLE):
- if (wIndex == 1) {
-
- dbg_rh("trying to do disable port 1");
-
- *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, yes);
-
- while (hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes));
- *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
- dbg_rh("Port 1 is disabled");
-
- } else if (wIndex == 2) {
-
- dbg_rh("trying to do disable port 2");
-
- *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, yes);
-
- while (hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes));
- *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
- dbg_rh("Port 2 is disabled");
-
- } else {
- dbg_rh("RH_CLEAR_FEATURE->RH_PORT_ENABLE "
- "with invalid wIndex == %d!", wIndex);
- }
-
- OK (0);
- case (RH_PORT_SUSPEND):
- /* Opposite to suspend should be resume, so we'll do a resume. */
- /* FIXME: USB 1.1, 11.16.2.2 says:
- "Clearing the PORT_SUSPEND feature causes a host-initiated resume
- on the specified port. If the port is not in the Suspended state,
- the hub should treat this request as a functional no-operation."
- Shouldn't we check if the port is in a suspended state before
- resuming? */
-
- /* Make sure the controller isn't busy. */
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- if (wIndex == 1) {
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, port1) |
- IO_STATE(R_USB_COMMAND, port_cmd, resume) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
- } else if (wIndex == 2) {
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, port2) |
- IO_STATE(R_USB_COMMAND, port_cmd, resume) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
- } else {
- dbg_rh("RH_CLEAR_FEATURE->RH_PORT_SUSPEND "
- "with invalid wIndex == %d!", wIndex);
- }
-
- OK (0);
- case (RH_PORT_POWER):
- OK (0); /* port power ** */
- case (RH_C_PORT_CONNECTION):
- if (wIndex == 1) {
- hc->rh.wPortChange_1 &= ~(1 << RH_PORT_CONNECTION);
- } else if (wIndex == 2) {
- hc->rh.wPortChange_2 &= ~(1 << RH_PORT_CONNECTION);
- } else {
- dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_CONNECTION "
- "with invalid wIndex == %d!", wIndex);
- }
-
- OK (0);
- case (RH_C_PORT_ENABLE):
- if (wIndex == 1) {
- hc->rh.wPortChange_1 &= ~(1 << RH_PORT_ENABLE);
- } else if (wIndex == 2) {
- hc->rh.wPortChange_2 &= ~(1 << RH_PORT_ENABLE);
- } else {
- dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_ENABLE "
- "with invalid wIndex == %d!", wIndex);
- }
- OK (0);
- case (RH_C_PORT_SUSPEND):
-/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
- OK (0);
- case (RH_C_PORT_OVER_CURRENT):
- OK (0); /* port power over current ** */
- case (RH_C_PORT_RESET):
- if (wIndex == 1) {
- hc->rh.wPortChange_1 &= ~(1 << RH_PORT_RESET);
- } else if (wIndex == 2) {
- hc->rh.wPortChange_2 &= ~(1 << RH_PORT_RESET);
- } else {
- dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_RESET "
- "with invalid index == %d!", wIndex);
- }
-
- OK (0);
-
- }
- break;
-
- case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
- switch (wValue) {
- case (RH_PORT_SUSPEND):
-
- /* Make sure the controller isn't busy. */
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- if (wIndex == 1) {
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, port1) |
- IO_STATE(R_USB_COMMAND, port_cmd, suspend) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
- } else if (wIndex == 2) {
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, port2) |
- IO_STATE(R_USB_COMMAND, port_cmd, suspend) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
- } else {
- dbg_rh("RH_SET_FEATURE->RH_PORT_SUSPEND "
- "with invalid wIndex == %d!", wIndex);
- }
-
- OK (0);
- case (RH_PORT_RESET):
- if (wIndex == 1) {
-
- port_1_reset:
- dbg_rh("Doing reset of port 1");
-
- /* Make sure the controller isn't busy. */
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, port1) |
- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
-
- /* We must wait at least 10 ms for the device to recover.
- 15 ms should be enough. */
- udelay(15000);
-
- /* Wait for reset bit to go low (should be done by now). */
- while (hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, reset, yes));
-
- /* If the port status is
- 1) connected and enabled then there is a device and everything is fine
- 2) neither connected nor enabled then there is no device, also fine
- 3) connected and not enabled then we try again
- (Yes, there are other port status combinations besides these.) */
-
- if ((hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
- (hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
- dbg_rh("Connected device on port 1, but port not enabled?"
- " Trying reset again.");
- goto port_2_reset;
- }
-
- /* Diagnostic printouts. */
- if ((hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, connected, no)) &&
- (hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
- dbg_rh("No connected device on port 1");
- } else if ((hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
- (hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes))) {
- dbg_rh("Connected device on port 1, port 1 enabled");
- }
-
- } else if (wIndex == 2) {
-
- port_2_reset:
- dbg_rh("Doing reset of port 2");
-
- /* Make sure the controller isn't busy. */
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- /* Issue the reset command. */
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, port2) |
- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
-
- /* We must wait at least 10 ms for the device to recover.
- 15 ms should be enough. */
- udelay(15000);
-
- /* Wait for reset bit to go low (should be done by now). */
- while (hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, reset, yes));
-
- /* If the port status is
- 1) connected and enabled then there is a device and everything is fine
- 2) neither connected nor enabled then there is no device, also fine
- 3) connected and not enabled then we try again
- (Yes, there are other port status combinations besides these.) */
-
- if ((hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
- (hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
- dbg_rh("Connected device on port 2, but port not enabled?"
- " Trying reset again.");
- goto port_2_reset;
- }
-
- /* Diagnostic printouts. */
- if ((hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, connected, no)) &&
- (hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
- dbg_rh("No connected device on port 2");
- } else if ((hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
- (hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes))) {
- dbg_rh("Connected device on port 2, port 2 enabled");
- }
-
- } else {
- dbg_rh("RH_SET_FEATURE->RH_PORT_RESET with invalid wIndex = %d", wIndex);
- }
-
- /* Make sure the controller isn't busy. */
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- /* If all enabled ports were disabled the host controller goes down into
- started mode, so we need to bring it back into the running state.
- (This is safe even if it's already in the running state.) */
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, nop) |
- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
-
- dbg_rh("...Done");
- OK(0);
-
- case (RH_PORT_POWER):
- OK (0); /* port power ** */
- case (RH_PORT_ENABLE):
- /* There is no port enable command in the host controller, so if the
- port is already enabled, we do nothing. If not, we reset the port
- (with an ugly goto). */
-
- if (wIndex == 1) {
- if (hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no)) {
- goto port_1_reset;
- }
- } else if (wIndex == 2) {
- if (hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no)) {
- goto port_2_reset;
- }
- } else {
- dbg_rh("RH_SET_FEATURE->RH_GET_STATUS with invalid wIndex = %d", wIndex);
- }
- OK (0);
- }
- break;
-
- case RH_SET_ADDRESS:
- hc->rh.devnum = wValue;
- dbg_rh("RH address set to: %d", hc->rh.devnum);
- OK (0);
-
- case RH_GET_DESCRIPTOR:
- switch ((wValue & 0xff00) >> 8) {
- case (0x01): /* device descriptor */
- len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_dev_des), wLength));
- memcpy (data, root_hub_dev_des, len);
- OK (len);
- case (0x02): /* configuration descriptor */
- len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_config_des), wLength));
- memcpy (data, root_hub_config_des, len);
- OK (len);
- case (0x03): /* string descriptors */
- len = usb_root_hub_string (wValue & 0xff,
- 0xff, "ETRAX 100LX",
- data, wLength);
- if (len > 0) {
- OK(min(leni, len));
- } else {
- stat = -EPIPE;
- }
-
- }
- break;
-
- case RH_GET_DESCRIPTOR | RH_CLASS:
- root_hub_hub_des[2] = hc->rh.numports;
- len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_hub_des), wLength));
- memcpy (data, root_hub_hub_des, len);
- OK (len);
-
- case RH_GET_CONFIGURATION:
- *(__u8 *) data = 0x01;
- OK (1);
-
- case RH_SET_CONFIGURATION:
- OK (0);
-
- default:
- stat = -EPIPE;
- }
-
- urb->actual_length = len;
- urb->status = stat;
- urb->dev = NULL;
- if (urb->complete) {
- urb->complete(urb, NULL);
- }
- DBFEXIT;
-
- return 0;
-}
-
-static void
-etrax_usb_bulk_eot_timer_func(unsigned long dummy)
-{
- /* Because of a race condition in the top half, we might miss a bulk eot.
- This timer "simulates" a bulk eot if we don't get one for a while, hopefully
- correcting the situation. */
- dbg_bulk("bulk_eot_timer timed out.");
- etrax_usb_hc_bulk_eot_interrupt(1);
-}
-
-static void*
-etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size,
- unsigned mem_flags, dma_addr_t *dma)
-{
- return kmalloc(size, mem_flags);
-}
-
-static void
-etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma)
-{
- kfree(addr);
-}
-
-
-static struct device fake_device;
-
-static int __init etrax_usb_hc_init(void)
-{
- static etrax_hc_t *hc;
- struct usb_bus *bus;
- struct usb_device *usb_rh;
- int i;
-
- DBFENTER;
-
- info("ETRAX 100LX USB-HCD %s (c) 2001-2003 Axis Communications AB\n", usb_hcd_version);
-
- hc = kmalloc(sizeof(etrax_hc_t), GFP_KERNEL);
- assert(hc != NULL);
-
- /* We use kmem_cache_* to make sure that all DMA desc. are dword aligned */
- /* Note that we specify sizeof(USB_EP_Desc_t) as the size, but also allocate
- SB descriptors from this cache. This is ok since sizeof(USB_EP_Desc_t) ==
- sizeof(USB_SB_Desc_t). */
-
- usb_desc_cache = kmem_cache_create("usb_desc_cache", sizeof(USB_EP_Desc_t), 0,
- SLAB_HWCACHE_ALIGN, 0, 0);
- assert(usb_desc_cache != NULL);
-
- top_half_reg_cache = kmem_cache_create("top_half_reg_cache",
- sizeof(usb_interrupt_registers_t),
- 0, SLAB_HWCACHE_ALIGN, 0, 0);
- assert(top_half_reg_cache != NULL);
-
- isoc_compl_cache = kmem_cache_create("isoc_compl_cache",
- sizeof(usb_isoc_complete_data_t),
- 0, SLAB_HWCACHE_ALIGN, 0, 0);
- assert(isoc_compl_cache != NULL);
-
- etrax_usb_bus = bus = usb_alloc_bus(&etrax_usb_device_operations);
- hc->bus = bus;
- bus->bus_name="ETRAX 100LX";
- bus->hcpriv = hc;
-
- /* Initialize RH to the default address.
- And make sure that we have no status change indication */
- hc->rh.numports = 2; /* The RH has two ports */
- hc->rh.devnum = 1;
- hc->rh.wPortChange_1 = 0;
- hc->rh.wPortChange_2 = 0;
-
- /* Also initate the previous values to zero */
- hc->rh.prev_wPortStatus_1 = 0;
- hc->rh.prev_wPortStatus_2 = 0;
-
- /* Initialize the intr-traffic flags */
- /* FIXME: This isn't used. (Besides, the error field isn't initialized.) */
- hc->intr.sleeping = 0;
- hc->intr.wq = NULL;
-
- epid_usage_bitmask = 0;
- epid_out_traffic = 0;
-
- /* Mark the invalid epid as being used. */
- set_bit(INVALID_EPID, (void *)&epid_usage_bitmask);
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, INVALID_EPID);
- nop();
- /* The valid bit should still be set ('invalid' is in our world; not the hardware's). */
- *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, yes) |
- IO_FIELD(R_USB_EPT_DATA, max_len, 1));
-
- /* Mark the dummy epid as being used. */
- set_bit(DUMMY_EPID, (void *)&epid_usage_bitmask);
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, DUMMY_EPID);
- nop();
- *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, no) |
- IO_FIELD(R_USB_EPT_DATA, max_len, 1));
-
- /* Initialize the urb list by initiating a head for each list. */
- for (i = 0; i < NBR_OF_EPIDS; i++) {
- INIT_LIST_HEAD(&urb_list[i]);
- }
- spin_lock_init(&urb_list_lock);
-
- INIT_LIST_HEAD(&urb_unlink_list);
-
-
- /* Initiate the bulk start timer. */
- init_timer(&bulk_start_timer);
- bulk_start_timer.expires = jiffies + BULK_START_TIMER_INTERVAL;
- bulk_start_timer.function = etrax_usb_bulk_start_timer_func;
- add_timer(&bulk_start_timer);
-
-
- /* Initiate the bulk eot timer. */
- init_timer(&bulk_eot_timer);
- bulk_eot_timer.expires = jiffies + BULK_EOT_TIMER_INTERVAL;
- bulk_eot_timer.function = etrax_usb_bulk_eot_timer_func;
- add_timer(&bulk_eot_timer);
-
- /* Set up the data structures for USB traffic. Note that this must be done before
- any interrupt that relies on sane DMA list occurrs. */
- init_rx_buffers();
- init_tx_bulk_ep();
- init_tx_ctrl_ep();
- init_tx_intr_ep();
- init_tx_isoc_ep();
-
- device_initialize(&fake_device);
- kobject_set_name(&fake_device.kobj, "etrax_usb");
- kobject_add(&fake_device.kobj);
- kobject_uevent(&fake_device.kobj, KOBJ_ADD);
- hc->bus->controller = &fake_device;
- usb_register_bus(hc->bus);
-
- *R_IRQ_MASK2_SET =
- /* Note that these interrupts are not used. */
- IO_STATE(R_IRQ_MASK2_SET, dma8_sub0_descr, set) |
- /* Sub channel 1 (ctrl) descr. interrupts are used. */
- IO_STATE(R_IRQ_MASK2_SET, dma8_sub1_descr, set) |
- IO_STATE(R_IRQ_MASK2_SET, dma8_sub2_descr, set) |
- /* Sub channel 3 (isoc) descr. interrupts are used. */
- IO_STATE(R_IRQ_MASK2_SET, dma8_sub3_descr, set);
-
- /* Note that the dma9_descr interrupt is not used. */
- *R_IRQ_MASK2_SET =
- IO_STATE(R_IRQ_MASK2_SET, dma9_eop, set) |
- IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set);
-
- /* FIXME: Enable iso_eof only when isoc traffic is running. */
- *R_USB_IRQ_MASK_SET =
- IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set) |
- IO_STATE(R_USB_IRQ_MASK_SET, bulk_eot, set) |
- IO_STATE(R_USB_IRQ_MASK_SET, epid_attn, set) |
- IO_STATE(R_USB_IRQ_MASK_SET, port_status, set) |
- IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set);
-
-
- if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_interrupt_top_half, 0,
- "ETRAX 100LX built-in USB (HC)", hc)) {
- err("Could not allocate IRQ %d for USB", ETRAX_USB_HC_IRQ);
- etrax_usb_hc_cleanup();
- DBFEXIT;
- return -1;
- }
-
- if (request_irq(ETRAX_USB_RX_IRQ, etrax_usb_rx_interrupt, 0,
- "ETRAX 100LX built-in USB (Rx)", hc)) {
- err("Could not allocate IRQ %d for USB", ETRAX_USB_RX_IRQ);
- etrax_usb_hc_cleanup();
- DBFEXIT;
- return -1;
- }
-
- if (request_irq(ETRAX_USB_TX_IRQ, etrax_usb_tx_interrupt, 0,
- "ETRAX 100LX built-in USB (Tx)", hc)) {
- err("Could not allocate IRQ %d for USB", ETRAX_USB_TX_IRQ);
- etrax_usb_hc_cleanup();
- DBFEXIT;
- return -1;
- }
-
- /* R_USB_COMMAND:
- USB commands in host mode. The fields in this register should all be
- written to in one write. Do not read-modify-write one field at a time. A
- write to this register will trigger events in the USB controller and an
- incomplete command may lead to unpredictable results, and in worst case
- even to a deadlock in the controller.
- (Note however that the busy field is read-only, so no need to write to it.) */
-
- /* Check the busy bit before writing to R_USB_COMMAND. */
-
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- /* Reset the USB interface. */
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, nop) |
- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, reset);
-
- /* Designer's Reference, p. 8 - 10 says we should Initate R_USB_FM_PSTART to 0x2A30 (10800),
- to guarantee that control traffic gets 10% of the bandwidth, and periodic transfer may
- allocate the rest (90%). This doesn't work though. Read on for a lenghty explanation.
-
- While there is a difference between rev. 2 and rev. 3 of the ETRAX 100LX regarding the NAK
- behaviour, it doesn't solve this problem. What happens is that a control transfer will not
- be interrupted in its data stage when PSTART happens (the point at which periodic traffic
- is started). Thus, if PSTART is set to 10800 and its IN or OUT token is NAKed until just before
- PSTART happens, it will continue the IN/OUT transfer as long as it's ACKed. After it's done,
- there may be too little time left for an isochronous transfer, causing an epid attention
- interrupt due to perror. The work-around for this is to let the control transfers run at the
- end of the frame instead of at the beginning, and will be interrupted just fine if it doesn't
- fit into the frame. However, since there will *always* be a control transfer at the beginning
- of the frame, regardless of what we set PSTART to, that transfer might be a 64-byte transfer
- which consumes up to 15% of the frame, leaving only 85% for periodic traffic. The solution to
- this would be to 'dummy allocate' 5% of the frame with the usb_claim_bandwidth function to make
- sure that the periodic transfers that are inserted will always fit in the frame.
-
- The idea was suggested that a control transfer could be split up into several 8 byte transfers,
- so that it would be interrupted by PSTART, but since this can't be done for an IN transfer this
- hasn't been implemented.
-
- The value 11960 is chosen to be just after the SOF token, with a couple of bit times extra
- for possible bit stuffing. */
-
- *R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 11960);
-
-#ifdef CONFIG_ETRAX_USB_HOST_PORT1
- *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
-#endif
-
-#ifdef CONFIG_ETRAX_USB_HOST_PORT2
- *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
-#endif
-
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- /* Configure the USB interface as a host controller. */
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, nop) |
- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, host_config);
-
- /* Note: Do not reset any ports here. Await the port status interrupts, to have a controlled
- sequence of resetting the ports. If we reset both ports now, and there are devices
- on both ports, we will get a bus error because both devices will answer the set address
- request. */
-
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- /* Start processing of USB traffic. */
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, nop) |
- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
-
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- usb_rh = usb_alloc_dev(NULL, hc->bus, 0);
- hc->bus->root_hub = usb_rh;
- usb_rh->state = USB_STATE_ADDRESS;
- usb_rh->speed = USB_SPEED_FULL;
- usb_rh->devnum = 1;
- hc->bus->devnum_next = 2;
- usb_rh->ep0.desc.wMaxPacketSize = __const_cpu_to_le16(64);
- usb_get_device_descriptor(usb_rh, USB_DT_DEVICE_SIZE);
- usb_new_device(usb_rh);
-
- DBFEXIT;
-
- return 0;
-}
-
-static void etrax_usb_hc_cleanup(void)
-{
- DBFENTER;
-
- free_irq(ETRAX_USB_HC_IRQ, NULL);
- free_irq(ETRAX_USB_RX_IRQ, NULL);
- free_irq(ETRAX_USB_TX_IRQ, NULL);
-
- usb_deregister_bus(etrax_usb_bus);
-
- /* FIXME: call kmem_cache_destroy here? */
-
- DBFEXIT;
-}
-
-module_init(etrax_usb_hc_init);
-module_exit(etrax_usb_hc_cleanup);
diff --git a/drivers/usb/host/hc_crisv10.h b/drivers/usb/host/hc_crisv10.h
deleted file mode 100644
index 62f77111d41..00000000000
--- a/drivers/usb/host/hc_crisv10.h
+++ /dev/null
@@ -1,289 +0,0 @@
-#ifndef __LINUX_ETRAX_USB_H
-#define __LINUX_ETRAX_USB_H
-
-#include <linux/types.h>
-#include <linux/list.h>
-
-typedef struct USB_IN_Desc {
- volatile __u16 sw_len;
- volatile __u16 command;
- volatile unsigned long next;
- volatile unsigned long buf;
- volatile __u16 hw_len;
- volatile __u16 status;
-} USB_IN_Desc_t;
-
-typedef struct USB_SB_Desc {
- volatile __u16 sw_len;
- volatile __u16 command;
- volatile unsigned long next;
- volatile unsigned long buf;
- __u32 dummy;
-} USB_SB_Desc_t;
-
-typedef struct USB_EP_Desc {
- volatile __u16 hw_len;
- volatile __u16 command;
- volatile unsigned long sub;
- volatile unsigned long next;
- __u32 dummy;
-} USB_EP_Desc_t;
-
-struct virt_root_hub {
- int devnum;
- void *urb;
- void *int_addr;
- int send;
- int interval;
- int numports;
- struct timer_list rh_int_timer;
- volatile __u16 wPortChange_1;
- volatile __u16 wPortChange_2;
- volatile __u16 prev_wPortStatus_1;
- volatile __u16 prev_wPortStatus_2;
-};
-
-struct etrax_usb_intr_traffic {
- int sleeping;
- int error;
- struct wait_queue *wq;
-};
-
-typedef struct etrax_usb_hc {
- struct usb_bus *bus;
- struct virt_root_hub rh;
- struct etrax_usb_intr_traffic intr;
-} etrax_hc_t;
-
-typedef enum {
- STARTED,
- NOT_STARTED,
- UNLINK,
- TRANSFER_DONE,
- WAITING_FOR_DESCR_INTR
-} etrax_usb_urb_state_t;
-
-
-
-typedef struct etrax_usb_urb_priv {
- /* The first_sb field is used for freeing all SB descriptors belonging
- to an urb. The corresponding ep descriptor's sub pointer cannot be
- used for this since the DMA advances the sub pointer as it processes
- the sb list. */
- USB_SB_Desc_t *first_sb;
- /* The last_sb field referes to the last SB descriptor that belongs to
- this urb. This is important to know so we can free the SB descriptors
- that ranges between first_sb and last_sb. */
- USB_SB_Desc_t *last_sb;
-
- /* The rx_offset field is used in ctrl and bulk traffic to keep track
- of the offset in the urb's transfer_buffer where incoming data should be
- copied to. */
- __u32 rx_offset;
-
- /* Counter used in isochronous transfers to keep track of the
- number of packets received/transmitted. */
- __u32 isoc_packet_counter;
-
- /* This field is used to pass information about the urb's current state between
- the various interrupt handlers (thus marked volatile). */
- volatile etrax_usb_urb_state_t urb_state;
-
- /* Connection between the submitted urb and ETRAX epid number */
- __u8 epid;
-
- /* The rx_data_list field is used for periodic traffic, to hold
- received data for later processing in the the complete_urb functions,
- where the data us copied to the urb's transfer_buffer. Basically, we
- use this intermediate storage because we don't know when it's safe to
- reuse the transfer_buffer (FIXME?). */
- struct list_head rx_data_list;
-} etrax_urb_priv_t;
-
-/* This struct is for passing data from the top half to the bottom half. */
-typedef struct usb_interrupt_registers
-{
- etrax_hc_t *hc;
- __u32 r_usb_epid_attn;
- __u8 r_usb_status;
- __u16 r_usb_rh_port_status_1;
- __u16 r_usb_rh_port_status_2;
- __u32 r_usb_irq_mask_read;
- __u32 r_usb_fm_number;
- struct work_struct usb_bh;
-} usb_interrupt_registers_t;
-
-/* This struct is for passing data from the isoc top half to the isoc bottom half. */
-typedef struct usb_isoc_complete_data
-{
- struct urb *urb;
- struct work_struct usb_bh;
-} usb_isoc_complete_data_t;
-
-/* This struct holds data we get from the rx descriptors for DMA channel 9
- for periodic traffic (intr and isoc). */
-typedef struct rx_data
-{
- void *data;
- int length;
- struct list_head list;
-} rx_data_t;
-
-typedef struct urb_entry
-{
- struct urb *urb;
- struct list_head list;
-} urb_entry_t;
-
-/* ---------------------------------------------------------------------------
- Virtual Root HUB
- ------------------------------------------------------------------------- */
-/* destination of request */
-#define RH_INTERFACE 0x01
-#define RH_ENDPOINT 0x02
-#define RH_OTHER 0x03
-
-#define RH_CLASS 0x20
-#define RH_VENDOR 0x40
-
-/* Requests: bRequest << 8 | bmRequestType */
-#define RH_GET_STATUS 0x0080
-#define RH_CLEAR_FEATURE 0x0100
-#define RH_SET_FEATURE 0x0300
-#define RH_SET_ADDRESS 0x0500
-#define RH_GET_DESCRIPTOR 0x0680
-#define RH_SET_DESCRIPTOR 0x0700
-#define RH_GET_CONFIGURATION 0x0880
-#define RH_SET_CONFIGURATION 0x0900
-#define RH_GET_STATE 0x0280
-#define RH_GET_INTERFACE 0x0A80
-#define RH_SET_INTERFACE 0x0B00
-#define RH_SYNC_FRAME 0x0C80
-/* Our Vendor Specific Request */
-#define RH_SET_EP 0x2000
-
-
-/* Hub port features */
-#define RH_PORT_CONNECTION 0x00
-#define RH_PORT_ENABLE 0x01
-#define RH_PORT_SUSPEND 0x02
-#define RH_PORT_OVER_CURRENT 0x03
-#define RH_PORT_RESET 0x04
-#define RH_PORT_POWER 0x08
-#define RH_PORT_LOW_SPEED 0x09
-#define RH_C_PORT_CONNECTION 0x10
-#define RH_C_PORT_ENABLE 0x11
-#define RH_C_PORT_SUSPEND 0x12
-#define RH_C_PORT_OVER_CURRENT 0x13
-#define RH_C_PORT_RESET 0x14
-
-/* Hub features */
-#define RH_C_HUB_LOCAL_POWER 0x00
-#define RH_C_HUB_OVER_CURRENT 0x01
-
-#define RH_DEVICE_REMOTE_WAKEUP 0x00
-#define RH_ENDPOINT_STALL 0x01
-
-/* Our Vendor Specific feature */
-#define RH_REMOVE_EP 0x00
-
-
-#define RH_ACK 0x01
-#define RH_REQ_ERR -1
-#define RH_NACK 0x00
-
-/* Field definitions for */
-
-#define USB_IN_command__eol__BITNR 0 /* command macros */
-#define USB_IN_command__eol__WIDTH 1
-#define USB_IN_command__eol__no 0
-#define USB_IN_command__eol__yes 1
-
-#define USB_IN_command__intr__BITNR 3
-#define USB_IN_command__intr__WIDTH 1
-#define USB_IN_command__intr__no 0
-#define USB_IN_command__intr__yes 1
-
-#define USB_IN_status__eop__BITNR 1 /* status macros. */
-#define USB_IN_status__eop__WIDTH 1
-#define USB_IN_status__eop__no 0
-#define USB_IN_status__eop__yes 1
-
-#define USB_IN_status__eot__BITNR 5
-#define USB_IN_status__eot__WIDTH 1
-#define USB_IN_status__eot__no 0
-#define USB_IN_status__eot__yes 1
-
-#define USB_IN_status__error__BITNR 6
-#define USB_IN_status__error__WIDTH 1
-#define USB_IN_status__error__no 0
-#define USB_IN_status__error__yes 1
-
-#define USB_IN_status__nodata__BITNR 7
-#define USB_IN_status__nodata__WIDTH 1
-#define USB_IN_status__nodata__no 0
-#define USB_IN_status__nodata__yes 1
-
-#define USB_IN_status__epid__BITNR 8
-#define USB_IN_status__epid__WIDTH 5
-
-#define USB_EP_command__eol__BITNR 0
-#define USB_EP_command__eol__WIDTH 1
-#define USB_EP_command__eol__no 0
-#define USB_EP_command__eol__yes 1
-
-#define USB_EP_command__eof__BITNR 1
-#define USB_EP_command__eof__WIDTH 1
-#define USB_EP_command__eof__no 0
-#define USB_EP_command__eof__yes 1
-
-#define USB_EP_command__intr__BITNR 3
-#define USB_EP_command__intr__WIDTH 1
-#define USB_EP_command__intr__no 0
-#define USB_EP_command__intr__yes 1
-
-#define USB_EP_command__enable__BITNR 4
-#define USB_EP_command__enable__WIDTH 1
-#define USB_EP_command__enable__no 0
-#define USB_EP_command__enable__yes 1
-
-#define USB_EP_command__hw_valid__BITNR 5
-#define USB_EP_command__hw_valid__WIDTH 1
-#define USB_EP_command__hw_valid__no 0
-#define USB_EP_command__hw_valid__yes 1
-
-#define USB_EP_command__epid__BITNR 8
-#define USB_EP_command__epid__WIDTH 5
-
-#define USB_SB_command__eol__BITNR 0 /* command macros. */
-#define USB_SB_command__eol__WIDTH 1
-#define USB_SB_command__eol__no 0
-#define USB_SB_command__eol__yes 1
-
-#define USB_SB_command__eot__BITNR 1
-#define USB_SB_command__eot__WIDTH 1
-#define USB_SB_command__eot__no 0
-#define USB_SB_command__eot__yes 1
-
-#define USB_SB_command__intr__BITNR 3
-#define USB_SB_command__intr__WIDTH 1
-#define USB_SB_command__intr__no 0
-#define USB_SB_command__intr__yes 1
-
-#define USB_SB_command__tt__BITNR 4
-#define USB_SB_command__tt__WIDTH 2
-#define USB_SB_command__tt__zout 0
-#define USB_SB_command__tt__in 1
-#define USB_SB_command__tt__out 2
-#define USB_SB_command__tt__setup 3
-
-
-#define USB_SB_command__rem__BITNR 8
-#define USB_SB_command__rem__WIDTH 6
-
-#define USB_SB_command__full__BITNR 6
-#define USB_SB_command__full__WIDTH 1
-#define USB_SB_command__full__no 0
-#define USB_SB_command__full__yes 1
-
-#endif
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index 2718b5dc4ec..46873f2534b 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -1577,7 +1577,7 @@ static int isp116x_remove(struct platform_device *pdev)
#define resource_len(r) (((r)->end - (r)->start) + 1)
-static int __init isp116x_probe(struct platform_device *pdev)
+static int __devinit isp116x_probe(struct platform_device *pdev)
{
struct usb_hcd *hcd;
struct isp116x *isp116x;
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index 93034648727..d849c809acb 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -18,19 +18,38 @@
#include <asm/mach-types.h>
#include <asm/hardware.h>
#include <asm/arch/board.h>
+#include <asm/arch/cpu.h>
#ifndef CONFIG_ARCH_AT91
#error "CONFIG_ARCH_AT91 must be defined."
#endif
-/* interface and function clocks */
-static struct clk *iclk, *fclk;
+/* interface and function clocks; sometimes also an AHB clock */
+static struct clk *iclk, *fclk, *hclk;
static int clocked;
extern int usb_disabled(void);
/*-------------------------------------------------------------------------*/
+static void at91_start_clock(void)
+{
+ if (cpu_is_at91sam9261())
+ clk_enable(hclk);
+ clk_enable(iclk);
+ clk_enable(fclk);
+ clocked = 1;
+}
+
+static void at91_stop_clock(void)
+{
+ clk_disable(fclk);
+ clk_disable(iclk);
+ if (cpu_is_at91sam9261())
+ clk_disable(hclk);
+ clocked = 0;
+}
+
static void at91_start_hc(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
@@ -41,9 +60,7 @@ static void at91_start_hc(struct platform_device *pdev)
/*
* Start the USB clocks.
*/
- clk_enable(iclk);
- clk_enable(fclk);
- clocked = 1;
+ at91_start_clock();
/*
* The USB host controller must remain in reset.
@@ -66,9 +83,7 @@ static void at91_stop_hc(struct platform_device *pdev)
/*
* Stop the USB clocks.
*/
- clk_disable(fclk);
- clk_disable(iclk);
- clocked = 0;
+ at91_stop_clock();
}
@@ -126,6 +141,8 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
iclk = clk_get(&pdev->dev, "ohci_clk");
fclk = clk_get(&pdev->dev, "uhpck");
+ if (cpu_is_at91sam9261())
+ hclk = clk_get(&pdev->dev, "hck0");
at91_start_hc(pdev);
ohci_hcd_init(hcd_to_ohci(hcd));
@@ -137,6 +154,8 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
/* Error handling */
at91_stop_hc(pdev);
+ if (cpu_is_at91sam9261())
+ clk_put(hclk);
clk_put(fclk);
clk_put(iclk);
@@ -171,9 +190,11 @@ static int usb_hcd_at91_remove(struct usb_hcd *hcd,
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ if (cpu_is_at91sam9261())
+ clk_put(hclk);
clk_put(fclk);
clk_put(iclk);
- fclk = iclk = NULL;
+ fclk = iclk = hclk = NULL;
dev_set_drvdata(&pdev->dev, NULL);
return 0;
@@ -280,9 +301,7 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg)
*/
if (at91_suspend_entering_slow_clock()) {
ohci_usb_reset (ohci);
- clk_disable(fclk);
- clk_disable(iclk);
- clocked = 0;
+ at91_stop_clock();
}
return 0;
@@ -295,11 +314,8 @@ static int ohci_hcd_at91_drv_resume(struct platform_device *pdev)
if (device_may_wakeup(&pdev->dev))
disable_irq_wake(hcd->irq);
- if (!clocked) {
- clk_enable(iclk);
- clk_enable(fclk);
- clocked = 1;
- }
+ if (!clocked)
+ at91_start_clock();
return 0;
}
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index fa6a7ceaa0d..e8bbe8bc259 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -42,6 +42,9 @@
#include <asm/system.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
+#ifdef CONFIG_PPC_PS3
+#include <asm/firmware.h>
+#endif
#include "../core/hcd.h"
@@ -483,9 +486,6 @@ static int ohci_run (struct ohci_hcd *ohci)
* or if bus glue did the same (e.g. for PCI add-in cards with
* PCI PM support).
*/
- ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n",
- hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
- ohci_readl (ohci, &ohci->regs->control));
if ((ohci->hc_control & OHCI_CTRL_RWC) != 0
&& !device_may_wakeup(hcd->self.controller))
device_init_wakeup(hcd->self.controller, 1);
@@ -741,9 +741,6 @@ static void ohci_stop (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
- ohci_dbg (ohci, "stop %s controller (state 0x%02x)\n",
- hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
- hcd->state);
ohci_dump (ohci, 1);
flush_scheduled_work();
@@ -944,9 +941,12 @@ static int __init ohci_hcd_mod_init(void)
sizeof (struct ed), sizeof (struct td));
#ifdef PS3_SYSTEM_BUS_DRIVER
- retval = ps3_system_bus_driver_register(&PS3_SYSTEM_BUS_DRIVER);
- if (retval < 0)
- goto error_ps3;
+ if (firmware_has_feature(FW_FEATURE_PS3_LV1)) {
+ retval = ps3_system_bus_driver_register(
+ &PS3_SYSTEM_BUS_DRIVER);
+ if (retval < 0)
+ goto error_ps3;
+ }
#endif
#ifdef PLATFORM_DRIVER
@@ -992,7 +992,8 @@ static int __init ohci_hcd_mod_init(void)
error_platform:
#endif
#ifdef PS3_SYSTEM_BUS_DRIVER
- ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
+ if (firmware_has_feature(FW_FEATURE_PS3_LV1))
+ ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
error_ps3:
#endif
return retval;
@@ -1014,7 +1015,8 @@ static void __exit ohci_hcd_mod_exit(void)
platform_driver_unregister(&PLATFORM_DRIVER);
#endif
#ifdef PS3_SYSTEM_BUS_DRIVER
- ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
+ if (firmware_has_feature(FW_FEATURE_PS3_LV1))
+ ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
#endif
}
module_exit(ohci_hcd_mod_exit);
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index b331ac4d0d6..79705609fd0 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -20,10 +20,16 @@
/*-------------------------------------------------------------------------*/
+static int broken_suspend(struct usb_hcd *hcd)
+{
+ device_init_wakeup(&hcd->self.root_hub->dev, 0);
+ return 0;
+}
+
/* AMD 756, for most chips (early revs), corrupts register
* values on read ... so enable the vendor workaround.
*/
-static int __devinit ohci_quirk_amd756(struct usb_hcd *hcd)
+static int ohci_quirk_amd756(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
@@ -31,16 +37,14 @@ static int __devinit ohci_quirk_amd756(struct usb_hcd *hcd)
ohci_dbg (ohci, "AMD756 erratum 4 workaround\n");
/* also erratum 10 (suspend/resume issues) */
- device_init_wakeup(&hcd->self.root_hub->dev, 0);
-
- return 0;
+ return broken_suspend(hcd);
}
/* Apple's OHCI driver has a lot of bizarre workarounds
* for this chip. Evidently control and bulk lists
* can get confused. (B&W G3 models, and ...)
*/
-static int __devinit ohci_quirk_opti(struct usb_hcd *hcd)
+static int ohci_quirk_opti(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
@@ -53,7 +57,7 @@ static int __devinit ohci_quirk_opti(struct usb_hcd *hcd)
* identify the USB (fn2). This quirk might apply to more or
* even all NSC stuff.
*/
-static int __devinit ohci_quirk_ns(struct usb_hcd *hcd)
+static int ohci_quirk_ns(struct usb_hcd *hcd)
{
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
struct pci_dev *b;
@@ -75,7 +79,7 @@ static int __devinit ohci_quirk_ns(struct usb_hcd *hcd)
* delays before control or bulk queues get re-activated
* in finish_unlinks()
*/
-static int __devinit ohci_quirk_zfmicro(struct usb_hcd *hcd)
+static int ohci_quirk_zfmicro(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
@@ -88,7 +92,7 @@ static int __devinit ohci_quirk_zfmicro(struct usb_hcd *hcd)
/* Check for Toshiba SCC OHCI which has big endian registers
* and little endian in memory data structures
*/
-static int __devinit ohci_quirk_toshiba_scc(struct usb_hcd *hcd)
+static int ohci_quirk_toshiba_scc(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
@@ -129,6 +133,18 @@ static const struct pci_device_id ohci_pci_quirks[] = {
PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, 0x01b6),
.driver_data = (unsigned long)ohci_quirk_toshiba_scc,
},
+ {
+ /* Toshiba portege 4000 */
+ .vendor = PCI_VENDOR_ID_AL,
+ .device = 0x5237,
+ .subvendor = PCI_VENDOR_ID_TOSHIBA_2,
+ .subdevice = 0x0004,
+ .driver_data = (unsigned long) broken_suspend,
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_ITE, 0x8152),
+ .driver_data = (unsigned long) broken_suspend,
+ },
/* FIXME for some of the early AMD 760 southbridges, OHCI
* won't work at all. blacklist them.
*/
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c
index 5d6c06bc452..1497371583b 100644
--- a/drivers/usb/host/uhci-debug.c
+++ b/drivers/usb/host/uhci-debug.c
@@ -145,7 +145,8 @@ static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space)
return out - buf;
}
-static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
+static int uhci_show_qh(struct uhci_hcd *uhci,
+ struct uhci_qh *qh, char *buf, int len, int space)
{
char *out = buf;
int i, nurbs;
@@ -190,13 +191,16 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
if (list_empty(&qh->queue)) {
out += sprintf(out, "%*s queue is empty\n", space, "");
+ if (qh == uhci->skel_async_qh)
+ out += uhci_show_td(uhci->term_td, out,
+ len - (out - buf), 0);
} else {
struct urb_priv *urbp = list_entry(qh->queue.next,
struct urb_priv, node);
struct uhci_td *td = list_entry(urbp->td_list.next,
struct uhci_td, list);
- if (cpu_to_le32(td->dma_handle) != (element & ~UHCI_PTR_BITS))
+ if (element != LINK_TO_TD(td))
out += sprintf(out, "%*s Element != First TD\n",
space, "");
i = nurbs = 0;
@@ -220,16 +224,6 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
return out - buf;
}
-static const char * const qh_names[] = {
- "skel_unlink_qh", "skel_iso_qh",
- "skel_int128_qh", "skel_int64_qh",
- "skel_int32_qh", "skel_int16_qh",
- "skel_int8_qh", "skel_int4_qh",
- "skel_int2_qh", "skel_int1_qh",
- "skel_ls_control_qh", "skel_fs_control_qh",
- "skel_bulk_qh", "skel_term_qh"
-};
-
static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
{
char *out = buf;
@@ -352,6 +346,13 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
struct uhci_td *td;
struct list_head *tmp, *head;
int nframes, nerrs;
+ __le32 link;
+ __le32 fsbr_link;
+
+ static const char * const qh_names[] = {
+ "unlink", "iso", "int128", "int64", "int32", "int16",
+ "int8", "int4", "int2", "async", "term"
+ };
out += uhci_show_root_hub_state(uhci, out, len - (out - buf));
out += sprintf(out, "HC status\n");
@@ -374,7 +375,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
nframes = 10;
nerrs = 0;
for (i = 0; i < UHCI_NUMFRAMES; ++i) {
- __le32 link, qh_dma;
+ __le32 qh_dma;
j = 0;
td = uhci->frame_cpu[i];
@@ -393,7 +394,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
do {
td = list_entry(tmp, struct uhci_td, fl_list);
tmp = tmp->next;
- if (cpu_to_le32(td->dma_handle) != link) {
+ if (link != LINK_TO_TD(td)) {
if (nframes > 0)
out += sprintf(out, " link does "
"not match list entry!\n");
@@ -428,25 +429,24 @@ check_link:
out += sprintf(out, "Skeleton QHs\n");
+ fsbr_link = 0;
for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
int cnt = 0;
qh = uhci->skelqh[i];
- out += sprintf(out, "- %s\n", qh_names[i]); \
- out += uhci_show_qh(qh, out, len - (out - buf), 4);
+ out += sprintf(out, "- skel_%s_qh\n", qh_names[i]); \
+ out += uhci_show_qh(uhci, qh, out, len - (out - buf), 4);
/* Last QH is the Terminating QH, it's different */
- if (i == UHCI_NUM_SKELQH - 1) {
- if (qh->link != UHCI_PTR_TERM)
- out += sprintf(out, " bandwidth reclamation on!\n");
-
- if (qh_element(qh) != cpu_to_le32(uhci->term_td->dma_handle))
+ if (i == SKEL_TERM) {
+ if (qh_element(qh) != LINK_TO_TD(uhci->term_td))
out += sprintf(out, " skel_term_qh element is not set to term_td!\n");
-
- continue;
+ link = fsbr_link;
+ if (!link)
+ link = LINK_TO_QH(uhci->skel_term_qh);
+ goto check_qh_link;
}
- j = (i < 9) ? 9 : i+1; /* Next skeleton */
head = &qh->node;
tmp = head->next;
@@ -454,17 +454,26 @@ check_link:
qh = list_entry(tmp, struct uhci_qh, node);
tmp = tmp->next;
if (++cnt <= 10)
- out += uhci_show_qh(qh, out,
+ out += uhci_show_qh(uhci, qh, out,
len - (out - buf), 4);
+ if (!fsbr_link && qh->skel >= SKEL_FSBR)
+ fsbr_link = LINK_TO_QH(qh);
}
if ((cnt -= 10) > 0)
out += sprintf(out, " Skipped %d QHs\n", cnt);
- if (i > 1 && i < UHCI_NUM_SKELQH - 1) {
- if (qh->link !=
- (cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH))
- out += sprintf(out, " last QH not linked to next skeleton!\n");
- }
+ link = UHCI_PTR_TERM;
+ if (i <= SKEL_ISO)
+ ;
+ else if (i < SKEL_ASYNC)
+ link = LINK_TO_QH(uhci->skel_async_qh);
+ else if (!uhci->fsbr_is_on)
+ ;
+ else
+ link = LINK_TO_QH(uhci->skel_term_qh);
+check_qh_link:
+ if (qh->link != link)
+ out += sprintf(out, " last QH not linked to next skeleton!\n");
}
return out - buf;
@@ -568,8 +577,8 @@ static const struct file_operations uhci_debug_operations = {
static inline void lprintk(char *buf)
{}
-static inline int uhci_show_qh(struct uhci_qh *qh, char *buf,
- int len, int space)
+static inline int uhci_show_qh(struct uhci_hcd *uhci,
+ struct uhci_qh *qh, char *buf, int len, int space)
{
return 0;
}
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index ded4df30a63..d22da26ff16 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -13,7 +13,7 @@
* (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
* support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
* (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
- * (C) Copyright 2004-2006 Alan Stern, stern@rowland.harvard.edu
+ * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
*
* Intel documents this fairly well, and as far as I know there
* are no royalties or anything like that, but even so there are
@@ -107,16 +107,16 @@ static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame)
* interrupt QHs, which will help spread out bandwidth utilization.
*
* ffs (Find First bit Set) does exactly what we need:
- * 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[8],
- * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[7], etc.
+ * 1,3,5,... => ffs = 0 => use period-2 QH = skelqh[8],
+ * 2,6,10,... => ffs = 1 => use period-4 QH = skelqh[7], etc.
* ffs >= 7 => not on any high-period queue, so use
- * skel_int1_qh = skelqh[9].
+ * period-1 QH = skelqh[9].
* Add in UHCI_NUMFRAMES to insure at least one bit is set.
*/
skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES);
if (skelnum <= 1)
skelnum = 9;
- return UHCI_PTR_QH | cpu_to_le32(uhci->skelqh[skelnum]->dma_handle);
+ return LINK_TO_QH(uhci->skelqh[skelnum]);
}
#include "uhci-debug.c"
@@ -540,16 +540,18 @@ static void uhci_shutdown(struct pci_dev *pdev)
*
* The hardware doesn't really know any difference
* in the queues, but the order does matter for the
- * protocols higher up. The order is:
+ * protocols higher up. The order in which the queues
+ * are encountered by the hardware is:
*
- * - any isochronous events handled before any
+ * - All isochronous events are handled before any
* of the queues. We don't do that here, because
* we'll create the actual TD entries on demand.
- * - The first queue is the interrupt queue.
- * - The second queue is the control queue, split into low- and full-speed
- * - The third queue is bulk queue.
- * - The fourth queue is the bandwidth reclamation queue, which loops back
- * to the full-speed control queue.
+ * - The first queue is the high-period interrupt queue.
+ * - The second queue is the period-1 interrupt and async
+ * (low-speed control, full-speed control, then bulk) queue.
+ * - The third queue is the terminating bandwidth reclamation queue,
+ * which contains no members, loops back to itself, and is present
+ * only when FSBR is on and there are no full-speed control or bulk QHs.
*/
static int uhci_start(struct usb_hcd *hcd)
{
@@ -626,34 +628,19 @@ static int uhci_start(struct usb_hcd *hcd)
}
/*
- * 8 Interrupt queues; link all higher int queues to int1,
- * then link int1 to control and control to bulk
+ * 8 Interrupt queues; link all higher int queues to int1 = async
*/
- uhci->skel_int128_qh->link =
- uhci->skel_int64_qh->link =
- uhci->skel_int32_qh->link =
- uhci->skel_int16_qh->link =
- uhci->skel_int8_qh->link =
- uhci->skel_int4_qh->link =
- uhci->skel_int2_qh->link = UHCI_PTR_QH |
- cpu_to_le32(uhci->skel_int1_qh->dma_handle);
-
- uhci->skel_int1_qh->link = UHCI_PTR_QH |
- cpu_to_le32(uhci->skel_ls_control_qh->dma_handle);
- uhci->skel_ls_control_qh->link = UHCI_PTR_QH |
- cpu_to_le32(uhci->skel_fs_control_qh->dma_handle);
- uhci->skel_fs_control_qh->link = UHCI_PTR_QH |
- cpu_to_le32(uhci->skel_bulk_qh->dma_handle);
- uhci->skel_bulk_qh->link = UHCI_PTR_QH |
- cpu_to_le32(uhci->skel_term_qh->dma_handle);
+ for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i)
+ uhci->skelqh[i]->link = LINK_TO_QH(uhci->skel_async_qh);
+ uhci->skel_async_qh->link = UHCI_PTR_TERM;
+ uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh);
/* This dummy TD is to work around a bug in Intel PIIX controllers */
uhci_fill_td(uhci->term_td, 0, uhci_explen(0) |
- (0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
- uhci->term_td->link = cpu_to_le32(uhci->term_td->dma_handle);
-
- uhci->skel_term_qh->link = UHCI_PTR_TERM;
- uhci->skel_term_qh->element = cpu_to_le32(uhci->term_td->dma_handle);
+ (0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
+ uhci->term_td->link = UHCI_PTR_TERM;
+ uhci->skel_async_qh->element = uhci->skel_term_qh->element =
+ LINK_TO_TD(uhci->term_td);
/*
* Fill the frame list: make all entries point to the proper
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
index 74469b5bcb6..1b3d23406ac 100644
--- a/drivers/usb/host/uhci-hcd.h
+++ b/drivers/usb/host/uhci-hcd.h
@@ -129,11 +129,12 @@ struct uhci_qh {
__le32 element; /* Queue element (TD) pointer */
/* Software fields */
+ dma_addr_t dma_handle;
+
struct list_head node; /* Node in the list of QHs */
struct usb_host_endpoint *hep; /* Endpoint information */
struct usb_device *udev;
struct list_head queue; /* Queue of urbps for this QH */
- struct uhci_qh *skel; /* Skeleton for this QH */
struct uhci_td *dummy_td; /* Dummy TD to end the queue */
struct uhci_td *post_td; /* Last TD completed */
@@ -149,8 +150,7 @@ struct uhci_qh {
int state; /* QH_STATE_xxx; see above */
int type; /* Queue type (control, bulk, etc) */
-
- dma_addr_t dma_handle;
+ int skel; /* Skeleton queue number */
unsigned int initial_toggle:1; /* Endpoint's current toggle value */
unsigned int needs_fixup:1; /* Must fix the TD toggle values */
@@ -171,6 +171,8 @@ static inline __le32 qh_element(struct uhci_qh *qh) {
return element;
}
+#define LINK_TO_QH(qh) (UHCI_PTR_QH | cpu_to_le32((qh)->dma_handle))
+
/*
* Transfer Descriptors
@@ -264,6 +266,8 @@ static inline u32 td_status(struct uhci_td *td) {
return le32_to_cpu(status);
}
+#define LINK_TO_TD(td) (cpu_to_le32((td)->dma_handle))
+
/*
* Skeleton Queue Headers
@@ -272,12 +276,13 @@ static inline u32 td_status(struct uhci_td *td) {
/*
* The UHCI driver uses QHs with Interrupt, Control and Bulk URBs for
* automatic queuing. To make it easy to insert entries into the schedule,
- * we have a skeleton of QHs for each predefined Interrupt latency,
- * low-speed control, full-speed control, bulk, and terminating QH
- * (see explanation for the terminating QH below).
+ * we have a skeleton of QHs for each predefined Interrupt latency.
+ * Asynchronous QHs (low-speed control, full-speed control, and bulk)
+ * go onto the period-1 interrupt list, since they all get accessed on
+ * every frame.
*
- * When we want to add a new QH, we add it to the end of the list for the
- * skeleton QH. For instance, the schedule list can look like this:
+ * When we want to add a new QH, we add it to the list starting from the
+ * appropriate skeleton QH. For instance, the schedule can look like this:
*
* skel int128 QH
* dev 1 interrupt QH
@@ -285,50 +290,47 @@ static inline u32 td_status(struct uhci_td *td) {
* skel int64 QH
* skel int32 QH
* ...
- * skel int1 QH
- * skel low-speed control QH
- * dev 5 control QH
- * skel full-speed control QH
- * skel bulk QH
+ * skel int1 + async QH
+ * dev 5 low-speed control QH
* dev 1 bulk QH
* dev 2 bulk QH
- * skel terminating QH
*
- * The terminating QH is used for 2 reasons:
- * - To place a terminating TD which is used to workaround a PIIX bug
- * (see Intel errata for explanation), and
- * - To loop back to the full-speed control queue for full-speed bandwidth
- * reclamation.
+ * There is a special terminating QH used to keep full-speed bandwidth
+ * reclamation active when no full-speed control or bulk QHs are linked
+ * into the schedule. It has an inactive TD (to work around a PIIX bug,
+ * see the Intel errata) and it points back to itself.
*
- * There's a special skeleton QH for Isochronous QHs. It never appears
- * on the schedule, and Isochronous TDs go on the schedule before the
+ * There's a special skeleton QH for Isochronous QHs which never appears
+ * on the schedule. Isochronous TDs go on the schedule before the
* the skeleton QHs. The hardware accesses them directly rather than
* through their QH, which is used only for bookkeeping purposes.
* While the UHCI spec doesn't forbid the use of QHs for Isochronous,
* it doesn't use them either. And the spec says that queues never
* advance on an error completion status, which makes them totally
* unsuitable for Isochronous transfers.
+ *
+ * There's also a special skeleton QH used for QHs which are in the process
+ * of unlinking and so may still be in use by the hardware. It too never
+ * appears on the schedule.
*/
-#define UHCI_NUM_SKELQH 14
-#define skel_unlink_qh skelqh[0]
-#define skel_iso_qh skelqh[1]
-#define skel_int128_qh skelqh[2]
-#define skel_int64_qh skelqh[3]
-#define skel_int32_qh skelqh[4]
-#define skel_int16_qh skelqh[5]
-#define skel_int8_qh skelqh[6]
-#define skel_int4_qh skelqh[7]
-#define skel_int2_qh skelqh[8]
-#define skel_int1_qh skelqh[9]
-#define skel_ls_control_qh skelqh[10]
-#define skel_fs_control_qh skelqh[11]
-#define skel_bulk_qh skelqh[12]
-#define skel_term_qh skelqh[13]
-
-/* Find the skelqh entry corresponding to an interval exponent */
-#define UHCI_SKEL_INDEX(exponent) (9 - exponent)
-
+#define UHCI_NUM_SKELQH 11
+#define SKEL_UNLINK 0
+#define skel_unlink_qh skelqh[SKEL_UNLINK]
+#define SKEL_ISO 1
+#define skel_iso_qh skelqh[SKEL_ISO]
+ /* int128, int64, ..., int1 = 2, 3, ..., 9 */
+#define SKEL_INDEX(exponent) (9 - exponent)
+#define SKEL_ASYNC 9
+#define skel_async_qh skelqh[SKEL_ASYNC]
+#define SKEL_TERM 10
+#define skel_term_qh skelqh[SKEL_TERM]
+
+/* The following entries refer to sublists of skel_async_qh */
+#define SKEL_LS_CONTROL 20
+#define SKEL_FS_CONTROL 21
+#define SKEL_FSBR SKEL_FS_CONTROL
+#define SKEL_BULK 22
/*
* The UHCI controller and root hub
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c
index bacc25c53ba..8e4427aebb1 100644
--- a/drivers/usb/host/uhci-hub.c
+++ b/drivers/usb/host/uhci-hub.c
@@ -33,6 +33,9 @@ static __u8 root_hub_hub_des[] =
/* status change bits: nonzero writes will clear */
#define RWC_BITS (USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC)
+/* suspend/resume bits: port suspended or port resuming */
+#define SUSPEND_BITS (USBPORTSC_SUSP | USBPORTSC_RD)
+
/* A port that either is connected or has a changed-bit set will prevent
* us from AUTO_STOPPING.
*/
@@ -96,8 +99,8 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
int status;
int i;
- if (inw(port_addr) & (USBPORTSC_SUSP | USBPORTSC_RD)) {
- CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD);
+ if (inw(port_addr) & SUSPEND_BITS) {
+ CLR_RH_PORTSTAT(SUSPEND_BITS);
if (test_bit(port, &uhci->resuming_ports))
set_bit(port, &uhci->port_c_suspend);
@@ -107,7 +110,7 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
* Experiments show that some controllers take longer, so
* we'll poll for completion. */
for (i = 0; i < 10; ++i) {
- if (!(inw(port_addr) & USBPORTSC_RD))
+ if (!(inw(port_addr) & SUSPEND_BITS))
break;
udelay(1);
}
@@ -289,7 +292,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
wPortStatus |= USB_PORT_STAT_CONNECTION;
if (status & USBPORTSC_PE) {
wPortStatus |= USB_PORT_STAT_ENABLE;
- if (status & (USBPORTSC_SUSP | USBPORTSC_RD))
+ if (status & SUSPEND_BITS)
wPortStatus |= USB_PORT_STAT_SUSPEND;
}
if (status & USBPORTSC_OC)
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index 68e66b33e72..4aed305982e 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -13,7 +13,7 @@
* (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
* support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
* (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
- * (C) Copyright 2004-2006 Alan Stern, stern@rowland.harvard.edu
+ * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
*/
@@ -45,15 +45,27 @@ static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci)
*/
static void uhci_fsbr_on(struct uhci_hcd *uhci)
{
+ struct uhci_qh *lqh;
+
+ /* The terminating skeleton QH always points back to the first
+ * FSBR QH. Make the last async QH point to the terminating
+ * skeleton QH. */
uhci->fsbr_is_on = 1;
- uhci->skel_term_qh->link = cpu_to_le32(
- uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
+ lqh = list_entry(uhci->skel_async_qh->node.prev,
+ struct uhci_qh, node);
+ lqh->link = LINK_TO_QH(uhci->skel_term_qh);
}
static void uhci_fsbr_off(struct uhci_hcd *uhci)
{
+ struct uhci_qh *lqh;
+
+ /* Remove the link from the last async QH to the terminating
+ * skeleton QH. */
uhci->fsbr_is_on = 0;
- uhci->skel_term_qh->link = UHCI_PTR_TERM;
+ lqh = list_entry(uhci->skel_async_qh->node.prev,
+ struct uhci_qh, node);
+ lqh->link = UHCI_PTR_TERM;
}
static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb)
@@ -111,10 +123,14 @@ static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci)
static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
{
- if (!list_empty(&td->list))
+ if (!list_empty(&td->list)) {
dev_warn(uhci_dev(uhci), "td %p still in list!\n", td);
- if (!list_empty(&td->fl_list))
+ WARN_ON(1);
+ }
+ if (!list_empty(&td->fl_list)) {
dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td);
+ WARN_ON(1);
+ }
dma_pool_free(uhci->td_pool, td, td->dma_handle);
}
@@ -158,11 +174,11 @@ static inline void uhci_insert_td_in_frame_list(struct uhci_hcd *uhci,
td->link = ltd->link;
wmb();
- ltd->link = cpu_to_le32(td->dma_handle);
+ ltd->link = LINK_TO_TD(td);
} else {
td->link = uhci->frame[framenum];
wmb();
- uhci->frame[framenum] = cpu_to_le32(td->dma_handle);
+ uhci->frame[framenum] = LINK_TO_TD(td);
uhci->frame_cpu[framenum] = td;
}
}
@@ -184,7 +200,7 @@ static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci,
struct uhci_td *ntd;
ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list);
- uhci->frame[td->frame] = cpu_to_le32(ntd->dma_handle);
+ uhci->frame[td->frame] = LINK_TO_TD(ntd);
uhci->frame_cpu[td->frame] = ntd;
}
} else {
@@ -279,8 +295,10 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
WARN_ON(qh->state != QH_STATE_IDLE && qh->udev);
- if (!list_empty(&qh->queue))
+ if (!list_empty(&qh->queue)) {
dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh);
+ WARN_ON(1);
+ }
list_del(&qh->node);
if (qh->udev) {
@@ -405,12 +423,66 @@ static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first)
}
/*
- * Put a QH on the schedule in both hardware and software
+ * Link an Isochronous QH into its skeleton's list
*/
-static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+static inline void link_iso(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ list_add_tail(&qh->node, &uhci->skel_iso_qh->node);
+
+ /* Isochronous QHs aren't linked by the hardware */
+}
+
+/*
+ * Link a high-period interrupt QH into the schedule at the end of its
+ * skeleton's list
+ */
+static void link_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
struct uhci_qh *pqh;
+ list_add_tail(&qh->node, &uhci->skelqh[qh->skel]->node);
+
+ pqh = list_entry(qh->node.prev, struct uhci_qh, node);
+ qh->link = pqh->link;
+ wmb();
+ pqh->link = LINK_TO_QH(qh);
+}
+
+/*
+ * Link a period-1 interrupt or async QH into the schedule at the
+ * correct spot in the async skeleton's list, and update the FSBR link
+ */
+static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ struct uhci_qh *pqh;
+ __le32 link_to_new_qh;
+
+ /* Find the predecessor QH for our new one and insert it in the list.
+ * The list of QHs is expected to be short, so linear search won't
+ * take too long. */
+ list_for_each_entry_reverse(pqh, &uhci->skel_async_qh->node, node) {
+ if (pqh->skel <= qh->skel)
+ break;
+ }
+ list_add(&qh->node, &pqh->node);
+
+ /* Link it into the schedule */
+ qh->link = pqh->link;
+ wmb();
+ link_to_new_qh = LINK_TO_QH(qh);
+ pqh->link = link_to_new_qh;
+
+ /* If this is now the first FSBR QH, link the terminating skeleton
+ * QH to it. */
+ if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR)
+ uhci->skel_term_qh->link = link_to_new_qh;
+}
+
+/*
+ * Put a QH on the schedule in both hardware and software
+ */
+static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
WARN_ON(list_empty(&qh->queue));
/* Set the element pointer if it isn't set already.
@@ -421,7 +493,7 @@ static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
struct uhci_td *td = list_entry(urbp->td_list.next,
struct uhci_td, list);
- qh->element = cpu_to_le32(td->dma_handle);
+ qh->element = LINK_TO_TD(td);
}
/* Treat the queue as if it has just advanced */
@@ -432,36 +504,68 @@ static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
return;
qh->state = QH_STATE_ACTIVE;
- /* Move the QH from its old list to the end of the appropriate
+ /* Move the QH from its old list to the correct spot in the appropriate
* skeleton's list */
if (qh == uhci->next_qh)
uhci->next_qh = list_entry(qh->node.next, struct uhci_qh,
node);
- list_move_tail(&qh->node, &qh->skel->node);
+ list_del(&qh->node);
+
+ if (qh->skel == SKEL_ISO)
+ link_iso(uhci, qh);
+ else if (qh->skel < SKEL_ASYNC)
+ link_interrupt(uhci, qh);
+ else
+ link_async(uhci, qh);
+}
+
+/*
+ * Unlink a high-period interrupt QH from the schedule
+ */
+static void unlink_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ struct uhci_qh *pqh;
- /* Link it into the schedule */
pqh = list_entry(qh->node.prev, struct uhci_qh, node);
- qh->link = pqh->link;
- wmb();
- pqh->link = UHCI_PTR_QH | cpu_to_le32(qh->dma_handle);
+ pqh->link = qh->link;
+ mb();
}
/*
- * Take a QH off the hardware schedule
+ * Unlink a period-1 interrupt or async QH from the schedule
*/
-static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+static void unlink_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
struct uhci_qh *pqh;
+ __le32 link_to_next_qh = qh->link;
+ pqh = list_entry(qh->node.prev, struct uhci_qh, node);
+ pqh->link = link_to_next_qh;
+
+ /* If this was the old first FSBR QH, link the terminating skeleton
+ * QH to the next (new first FSBR) QH. */
+ if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR)
+ uhci->skel_term_qh->link = link_to_next_qh;
+ mb();
+}
+
+/*
+ * Take a QH off the hardware schedule
+ */
+static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
if (qh->state == QH_STATE_UNLINKING)
return;
WARN_ON(qh->state != QH_STATE_ACTIVE || !qh->udev);
qh->state = QH_STATE_UNLINKING;
/* Unlink the QH from the schedule and record when we did it */
- pqh = list_entry(qh->node.prev, struct uhci_qh, node);
- pqh->link = qh->link;
- mb();
+ if (qh->skel == SKEL_ISO)
+ ;
+ else if (qh->skel < SKEL_ASYNC)
+ unlink_interrupt(uhci, qh);
+ else
+ unlink_async(uhci, qh);
uhci_get_current_frame_number(uhci);
qh->unlink_frame = uhci->frame_number;
@@ -642,9 +746,11 @@ static void uhci_free_urb_priv(struct uhci_hcd *uhci,
{
struct uhci_td *td, *tmp;
- if (!list_empty(&urbp->node))
+ if (!list_empty(&urbp->node)) {
dev_warn(uhci_dev(uhci), "urb %p still on QH's list!\n",
urbp->urb);
+ WARN_ON(1);
+ }
list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
uhci_remove_td_from_urbp(td);
@@ -697,6 +803,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
dma_addr_t data = urb->transfer_dma;
__le32 *plink;
struct urb_priv *urbp = urb->hcpriv;
+ int skel;
/* The "pipe" thing contains the destination in bits 8--18 */
destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
@@ -737,7 +844,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
td = uhci_alloc_td(uhci);
if (!td)
goto nomem;
- *plink = cpu_to_le32(td->dma_handle);
+ *plink = LINK_TO_TD(td);
/* Alternate Data0/1 (start with Data1) */
destination ^= TD_TOKEN_TOGGLE;
@@ -757,7 +864,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
td = uhci_alloc_td(uhci);
if (!td)
goto nomem;
- *plink = cpu_to_le32(td->dma_handle);
+ *plink = LINK_TO_TD(td);
/*
* It's IN if the pipe is an output pipe or we're not expecting
@@ -784,7 +891,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
td = uhci_alloc_td(uhci);
if (!td)
goto nomem;
- *plink = cpu_to_le32(td->dma_handle);
+ *plink = LINK_TO_TD(td);
uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0);
wmb();
@@ -797,11 +904,13 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
* isn't in the CONFIGURED state. */
if (urb->dev->speed == USB_SPEED_LOW ||
urb->dev->state != USB_STATE_CONFIGURED)
- qh->skel = uhci->skel_ls_control_qh;
+ skel = SKEL_LS_CONTROL;
else {
- qh->skel = uhci->skel_fs_control_qh;
+ skel = SKEL_FS_CONTROL;
uhci_add_fsbr(uhci, urb);
}
+ if (qh->state != QH_STATE_ACTIVE)
+ qh->skel = skel;
urb->actual_length = -8; /* Account for the SETUP packet */
return 0;
@@ -860,7 +969,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
td = uhci_alloc_td(uhci);
if (!td)
goto nomem;
- *plink = cpu_to_le32(td->dma_handle);
+ *plink = LINK_TO_TD(td);
}
uhci_add_td_to_urbp(td, urbp);
uhci_fill_td(td, status,
@@ -888,7 +997,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
td = uhci_alloc_td(uhci);
if (!td)
goto nomem;
- *plink = cpu_to_le32(td->dma_handle);
+ *plink = LINK_TO_TD(td);
uhci_add_td_to_urbp(td, urbp);
uhci_fill_td(td, status,
@@ -914,7 +1023,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
td = uhci_alloc_td(uhci);
if (!td)
goto nomem;
- *plink = cpu_to_le32(td->dma_handle);
+ *plink = LINK_TO_TD(td);
uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0);
wmb();
@@ -931,7 +1040,7 @@ nomem:
return -ENOMEM;
}
-static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
+static int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
struct uhci_qh *qh)
{
int ret;
@@ -940,7 +1049,8 @@ static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
if (urb->dev->speed == USB_SPEED_LOW)
return -EINVAL;
- qh->skel = uhci->skel_bulk_qh;
+ if (qh->state != QH_STATE_ACTIVE)
+ qh->skel = SKEL_BULK;
ret = uhci_submit_common(uhci, urb, qh);
if (ret == 0)
uhci_add_fsbr(uhci, urb);
@@ -968,7 +1078,7 @@ static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
if (exponent < 0)
return -EINVAL;
qh->period = 1 << exponent;
- qh->skel = uhci->skelqh[UHCI_SKEL_INDEX(exponent)];
+ qh->skel = SKEL_INDEX(exponent);
/* For now, interrupt phase is fixed by the layout
* of the QH lists. */
@@ -1005,7 +1115,7 @@ static int uhci_fixup_short_transfer(struct uhci_hcd *uhci,
* the queue at the status stage transaction, which is
* the last TD. */
WARN_ON(list_empty(&urbp->td_list));
- qh->element = cpu_to_le32(td->dma_handle);
+ qh->element = LINK_TO_TD(td);
tmp = td->list.prev;
ret = -EINPROGRESS;
@@ -1069,7 +1179,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
if (debug > 1 && errbuf) {
/* Print the chain for debugging */
- uhci_show_qh(urbp->qh, errbuf,
+ uhci_show_qh(uhci, urbp->qh, errbuf,
ERRBUF_LEN, 0);
lprintk(errbuf);
}
@@ -1216,7 +1326,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
qh->iso_status = 0;
}
- qh->skel = uhci->skel_iso_qh;
+ qh->skel = SKEL_ISO;
if (!qh->bandwidth_reserved)
uhci_reserve_bandwidth(uhci, qh);
return 0;
@@ -1566,8 +1676,7 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
/* Detect the Intel bug and work around it */
- if (qh->post_td && qh_element(qh) ==
- cpu_to_le32(qh->post_td->dma_handle)) {
+ if (qh->post_td && qh_element(qh) == LINK_TO_TD(qh->post_td)) {
qh->element = qh->post_td->link;
qh->advance_jiffies = jiffies;
ret = 1;
diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig
index 69a9f3b6d0a..a792e42f58a 100644
--- a/drivers/usb/input/Kconfig
+++ b/drivers/usb/input/Kconfig
@@ -4,151 +4,6 @@
comment "USB Input Devices"
depends on USB
-config USB_HID
- tristate "USB Human Interface Device (full HID) support"
- default y
- depends on USB && INPUT
- select HID
- ---help---
- Say Y here if you want full HID support to connect USB keyboards,
- mice, joysticks, graphic tablets, or any other HID based devices
- to your computer via USB, as well as Uninterruptible Power Supply
- (UPS) and monitor control devices.
-
- You can't use this driver and the HIDBP (Boot Protocol) keyboard
- and mouse drivers at the same time. More information is available:
- <file:Documentation/input/input.txt>.
-
- If unsure, say Y.
-
- To compile this driver as a module, choose M here: the
- module will be called usbhid.
-
-comment "Input core support is needed for USB HID input layer or HIDBP support"
- depends on USB_HID && INPUT=n
-
-config USB_HIDINPUT_POWERBOOK
- bool "Enable support for iBook/PowerBook special keys"
- default n
- depends on USB_HID
- help
- Say Y here if you want support for the special keys (Fn, Numlock) on
- Apple iBooks and PowerBooks.
-
- If unsure, say N.
-
-config HID_FF
- bool "Force feedback support (EXPERIMENTAL)"
- depends on USB_HID && EXPERIMENTAL
- help
- Say Y here is you want force feedback support for a few HID devices.
- See below for a list of supported devices.
-
- See <file:Documentation/input/ff.txt> for a description of the force
- feedback API.
-
- If unsure, say N.
-
-config HID_PID
- bool "PID device support"
- depends on HID_FF
- help
- Say Y here if you have a PID-compliant device and wish to enable force
- feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such
- devices.
-
-config LOGITECH_FF
- bool "Logitech devices support"
- depends on HID_FF
- select INPUT_FF_MEMLESS if USB_HID
- help
- Say Y here if you have one of these devices:
- - Logitech WingMan Cordless RumblePad
- - Logitech WingMan Cordless RumblePad 2
- - Logitech WingMan Force 3D
- - Logitech Formula Force EX
- - Logitech MOMO Force wheel
-
- and if you want to enable force feedback for them.
- Note: if you say N here, this device will still be supported, but without
- force feedback.
-
-config PANTHERLORD_FF
- bool "PantherLord USB/PS2 2in1 Adapter support"
- depends on HID_FF
- select INPUT_FF_MEMLESS if USB_HID
- help
- Say Y here if you have a PantherLord USB/PS2 2in1 Adapter and want
- to enable force feedback support for it.
-
-config THRUSTMASTER_FF
- bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)"
- depends on HID_FF && EXPERIMENTAL
- select INPUT_FF_MEMLESS if USB_HID
- help
- Say Y here if you have a THRUSTMASTER FireStore Dual Power 2,
- and want to enable force feedback support for it.
- Note: if you say N here, this device will still be supported, but without
- force feedback.
-
-config ZEROPLUS_FF
- bool "Zeroplus based game controller support"
- depends on HID_FF
- select INPUT_FF_MEMLESS if USB_HID
- help
- Say Y here if you have a Zeroplus based game controller and want to
- enable force feedback for it.
-
-config USB_HIDDEV
- bool "/dev/hiddev raw HID device support"
- depends on USB_HID
- help
- Say Y here if you want to support HID devices (from the USB
- specification standpoint) that aren't strictly user interface
- devices, like monitor controls and Uninterruptable Power Supplies.
-
- This module supports these devices separately using a separate
- event interface on /dev/usb/hiddevX (char 180:96 to 180:111).
-
- If unsure, say Y.
-
-menu "USB HID Boot Protocol drivers"
- depends on USB!=n && USB_HID!=y
-
-config USB_KBD
- tristate "USB HIDBP Keyboard (simple Boot) support"
- depends on USB && INPUT
- ---help---
- Say Y here only if you are absolutely sure that you don't want
- to use the generic HID driver for your USB keyboard and prefer
- to use the keyboard in its limited Boot Protocol mode instead.
-
- This is almost certainly not what you want. This is mostly
- useful for embedded applications or simple keyboards.
-
- To compile this driver as a module, choose M here: the
- module will be called usbkbd.
-
- If even remotely unsure, say N.
-
-config USB_MOUSE
- tristate "USB HIDBP Mouse (simple Boot) support"
- depends on USB && INPUT
- ---help---
- Say Y here only if you are absolutely sure that you don't want
- to use the generic HID driver for your USB mouse and prefer
- to use the mouse in its limited Boot Protocol mode instead.
-
- This is almost certainly not what you want. This is mostly
- useful for embedded applications or simple mice.
-
- To compile this driver as a module, choose M here: the
- module will be called usbmouse.
-
- If even remotely unsure, say N.
-
-endmenu
-
config USB_AIPTEK
tristate "Aiptek 6000U/8000U tablet support"
depends on USB && INPUT
diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile
index f8c35b66c46..284a0734e0c 100644
--- a/drivers/usb/input/Makefile
+++ b/drivers/usb/input/Makefile
@@ -4,40 +4,12 @@
# Multipart objects.
wacom-objs := wacom_wac.o wacom_sys.o
-usbhid-objs := hid-core.o
-
-# Optional parts of multipart objects.
-
-ifeq ($(CONFIG_USB_HIDDEV),y)
- usbhid-objs += hiddev.o
-endif
-ifeq ($(CONFIG_HID_PID),y)
- usbhid-objs += hid-pidff.o
-endif
-ifeq ($(CONFIG_LOGITECH_FF),y)
- usbhid-objs += hid-lgff.o
-endif
-ifeq ($(CONFIG_PANTHERLORD_FF),y)
- usbhid-objs += hid-plff.o
-endif
-ifeq ($(CONFIG_THRUSTMASTER_FF),y)
- usbhid-objs += hid-tmff.o
-endif
-ifeq ($(CONFIG_ZEROPLUS_FF),y)
- usbhid-objs += hid-zpff.o
-endif
-ifeq ($(CONFIG_HID_FF),y)
- usbhid-objs += hid-ff.o
-endif
obj-$(CONFIG_USB_AIPTEK) += aiptek.o
obj-$(CONFIG_USB_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_USB_ATI_REMOTE2) += ati_remote2.o
-obj-$(CONFIG_USB_HID) += usbhid.o
-obj-$(CONFIG_USB_KBD) += usbkbd.o
obj-$(CONFIG_USB_KBTAB) += kbtab.o
obj-$(CONFIG_USB_KEYSPAN_REMOTE) += keyspan_remote.o
-obj-$(CONFIG_USB_MOUSE) += usbmouse.o
obj-$(CONFIG_USB_TOUCHSCREEN) += usbtouchscreen.o
obj-$(CONFIG_USB_POWERMATE) += powermate.o
obj-$(CONFIG_USB_WACOM) += wacom.o
@@ -45,7 +17,7 @@ obj-$(CONFIG_USB_ACECAD) += acecad.o
obj-$(CONFIG_USB_YEALINK) += yealink.o
obj-$(CONFIG_USB_XPAD) += xpad.o
obj-$(CONFIG_USB_APPLETOUCH) += appletouch.o
-obj-$(CONFIG_USB_GTCO) += gtco.o
+obj-$(CONFIG_USB_GTCO) += gtco.o
ifeq ($(CONFIG_USB_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/usb/input/gtco.c b/drivers/usb/input/gtco.c
index dbd207efb8f..b2ca10f2fe0 100644
--- a/drivers/usb/input/gtco.c
+++ b/drivers/usb/input/gtco.c
@@ -223,7 +223,7 @@ static void parse_hid_report_descriptor(struct gtco *device, char * report,
__u32 oldval[TAG_GLOB_MAX];
/* Debug stuff */
- char maintype='x';
+ char maintype = 'x';
char globtype[12];
int indent = 0;
char indentstr[10] = "";
diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
deleted file mode 100644
index 4d8ed3d71a1..00000000000
--- a/drivers/usb/input/hid-core.c
+++ /dev/null
@@ -1,1459 +0,0 @@
-/*
- * USB HID support for Linux
- *
- * Copyright (c) 1999 Andreas Gal
- * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
- * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- * Copyright (c) 2006 Jiri Kosina
- */
-
-/*
- * 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.
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/mm.h>
-#include <linux/smp_lock.h>
-#include <linux/spinlock.h>
-#include <asm/unaligned.h>
-#include <asm/byteorder.h>
-#include <linux/input.h>
-#include <linux/wait.h>
-
-#undef DEBUG
-#undef DEBUG_DATA
-
-#include <linux/usb.h>
-
-#include <linux/hid.h>
-#include <linux/hiddev.h>
-#include <linux/hid-debug.h>
-#include "usbhid.h"
-
-/*
- * Version Information
- */
-
-#define DRIVER_VERSION "v2.6"
-#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik"
-#define DRIVER_DESC "USB HID core driver"
-#define DRIVER_LICENSE "GPL"
-
-static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick",
- "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"};
-/*
- * Module parameters.
- */
-
-static unsigned int hid_mousepoll_interval;
-module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644);
-MODULE_PARM_DESC(mousepoll, "Polling interval of mice");
-
-/*
- * Input submission and I/O error handler.
- */
-
-static void hid_io_error(struct hid_device *hid);
-
-/* Start up the input URB */
-static int hid_start_in(struct hid_device *hid)
-{
- unsigned long flags;
- int rc = 0;
- struct usbhid_device *usbhid = hid->driver_data;
-
- spin_lock_irqsave(&usbhid->inlock, flags);
- if (hid->open > 0 && !test_bit(HID_SUSPENDED, &usbhid->iofl) &&
- !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
- rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
- if (rc != 0)
- clear_bit(HID_IN_RUNNING, &usbhid->iofl);
- }
- spin_unlock_irqrestore(&usbhid->inlock, flags);
- return rc;
-}
-
-/* I/O retry timer routine */
-static void hid_retry_timeout(unsigned long _hid)
-{
- struct hid_device *hid = (struct hid_device *) _hid;
- struct usbhid_device *usbhid = hid->driver_data;
-
- dev_dbg(&usbhid->intf->dev, "retrying intr urb\n");
- if (hid_start_in(hid))
- hid_io_error(hid);
-}
-
-/* Workqueue routine to reset the device or clear a halt */
-static void hid_reset(struct work_struct *work)
-{
- struct usbhid_device *usbhid =
- container_of(work, struct usbhid_device, reset_work);
- struct hid_device *hid = usbhid->hid;
- int rc_lock, rc = 0;
-
- if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) {
- dev_dbg(&usbhid->intf->dev, "clear halt\n");
- rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->urbin->pipe);
- clear_bit(HID_CLEAR_HALT, &usbhid->iofl);
- hid_start_in(hid);
- }
-
- else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
- dev_dbg(&usbhid->intf->dev, "resetting device\n");
- rc = rc_lock = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf);
- if (rc_lock >= 0) {
- rc = usb_reset_composite_device(hid_to_usb_dev(hid), usbhid->intf);
- if (rc_lock)
- usb_unlock_device(hid_to_usb_dev(hid));
- }
- clear_bit(HID_RESET_PENDING, &usbhid->iofl);
- }
-
- switch (rc) {
- case 0:
- if (!test_bit(HID_IN_RUNNING, &usbhid->iofl))
- hid_io_error(hid);
- break;
- default:
- err("can't reset device, %s-%s/input%d, status %d",
- hid_to_usb_dev(hid)->bus->bus_name,
- hid_to_usb_dev(hid)->devpath,
- usbhid->ifnum, rc);
- /* FALLTHROUGH */
- case -EHOSTUNREACH:
- case -ENODEV:
- case -EINTR:
- break;
- }
-}
-
-/* Main I/O error handler */
-static void hid_io_error(struct hid_device *hid)
-{
- unsigned long flags;
- struct usbhid_device *usbhid = hid->driver_data;
-
- spin_lock_irqsave(&usbhid->inlock, flags);
-
- /* Stop when disconnected */
- if (usb_get_intfdata(usbhid->intf) == NULL)
- goto done;
-
- /* When an error occurs, retry at increasing intervals */
- if (usbhid->retry_delay == 0) {
- usbhid->retry_delay = 13; /* Then 26, 52, 104, 104, ... */
- usbhid->stop_retry = jiffies + msecs_to_jiffies(1000);
- } else if (usbhid->retry_delay < 100)
- usbhid->retry_delay *= 2;
-
- if (time_after(jiffies, usbhid->stop_retry)) {
-
- /* Retries failed, so do a port reset */
- if (!test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) {
- schedule_work(&usbhid->reset_work);
- goto done;
- }
- }
-
- mod_timer(&usbhid->io_retry,
- jiffies + msecs_to_jiffies(usbhid->retry_delay));
-done:
- spin_unlock_irqrestore(&usbhid->inlock, flags);
-}
-
-/*
- * Input interrupt completion handler.
- */
-
-static void hid_irq_in(struct urb *urb)
-{
- struct hid_device *hid = urb->context;
- struct usbhid_device *usbhid = hid->driver_data;
- int status;
-
- switch (urb->status) {
- case 0: /* success */
- usbhid->retry_delay = 0;
- hid_input_report(urb->context, HID_INPUT_REPORT,
- urb->transfer_buffer,
- urb->actual_length, 1);
- break;
- case -EPIPE: /* stall */
- clear_bit(HID_IN_RUNNING, &usbhid->iofl);
- set_bit(HID_CLEAR_HALT, &usbhid->iofl);
- schedule_work(&usbhid->reset_work);
- return;
- case -ECONNRESET: /* unlink */
- case -ENOENT:
- case -ESHUTDOWN: /* unplug */
- clear_bit(HID_IN_RUNNING, &usbhid->iofl);
- return;
- case -EILSEQ: /* protocol error or unplug */
- case -EPROTO: /* protocol error or unplug */
- case -ETIME: /* protocol error or unplug */
- case -ETIMEDOUT: /* Should never happen, but... */
- clear_bit(HID_IN_RUNNING, &usbhid->iofl);
- hid_io_error(hid);
- return;
- default: /* error */
- warn("input irq status %d received", urb->status);
- }
-
- status = usb_submit_urb(urb, GFP_ATOMIC);
- if (status) {
- clear_bit(HID_IN_RUNNING, &usbhid->iofl);
- if (status != -EPERM) {
- err("can't resubmit intr, %s-%s/input%d, status %d",
- hid_to_usb_dev(hid)->bus->bus_name,
- hid_to_usb_dev(hid)->devpath,
- usbhid->ifnum, status);
- hid_io_error(hid);
- }
- }
-}
-
-static int hid_submit_out(struct hid_device *hid)
-{
- struct hid_report *report;
- struct usbhid_device *usbhid = hid->driver_data;
-
- report = usbhid->out[usbhid->outtail];
-
- hid_output_report(report, usbhid->outbuf);
- usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
- usbhid->urbout->dev = hid_to_usb_dev(hid);
-
- dbg("submitting out urb");
-
- if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) {
- err("usb_submit_urb(out) failed");
- return -1;
- }
-
- return 0;
-}
-
-static int hid_submit_ctrl(struct hid_device *hid)
-{
- struct hid_report *report;
- unsigned char dir;
- int len;
- struct usbhid_device *usbhid = hid->driver_data;
-
- report = usbhid->ctrl[usbhid->ctrltail].report;
- dir = usbhid->ctrl[usbhid->ctrltail].dir;
-
- len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
- if (dir == USB_DIR_OUT) {
- hid_output_report(report, usbhid->ctrlbuf);
- usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
- usbhid->urbctrl->transfer_buffer_length = len;
- } else {
- int maxpacket, padlen;
-
- usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0);
- maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0);
- if (maxpacket > 0) {
- padlen = (len + maxpacket - 1) / maxpacket;
- padlen *= maxpacket;
- if (padlen > usbhid->bufsize)
- padlen = usbhid->bufsize;
- } else
- padlen = 0;
- usbhid->urbctrl->transfer_buffer_length = padlen;
- }
- usbhid->urbctrl->dev = hid_to_usb_dev(hid);
-
- usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
- usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT;
- usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id);
- usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
- usbhid->cr->wLength = cpu_to_le16(len);
-
- dbg("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u",
- usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report",
- usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);
-
- if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) {
- err("usb_submit_urb(ctrl) failed");
- return -1;
- }
-
- return 0;
-}
-
-/*
- * Output interrupt completion handler.
- */
-
-static void hid_irq_out(struct urb *urb)
-{
- struct hid_device *hid = urb->context;
- struct usbhid_device *usbhid = hid->driver_data;
- unsigned long flags;
- int unplug = 0;
-
- switch (urb->status) {
- case 0: /* success */
- break;
- case -ESHUTDOWN: /* unplug */
- unplug = 1;
- case -EILSEQ: /* protocol error or unplug */
- case -EPROTO: /* protocol error or unplug */
- case -ECONNRESET: /* unlink */
- case -ENOENT:
- break;
- default: /* error */
- warn("output irq status %d received", urb->status);
- }
-
- spin_lock_irqsave(&usbhid->outlock, flags);
-
- if (unplug)
- usbhid->outtail = usbhid->outhead;
- else
- usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
-
- if (usbhid->outhead != usbhid->outtail) {
- if (hid_submit_out(hid)) {
- clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
- wake_up(&hid->wait);
- }
- spin_unlock_irqrestore(&usbhid->outlock, flags);
- return;
- }
-
- clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
- spin_unlock_irqrestore(&usbhid->outlock, flags);
- wake_up(&hid->wait);
-}
-
-/*
- * Control pipe completion handler.
- */
-
-static void hid_ctrl(struct urb *urb)
-{
- struct hid_device *hid = urb->context;
- struct usbhid_device *usbhid = hid->driver_data;
- unsigned long flags;
- int unplug = 0;
-
- spin_lock_irqsave(&usbhid->ctrllock, flags);
-
- switch (urb->status) {
- case 0: /* success */
- if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
- hid_input_report(urb->context, usbhid->ctrl[usbhid->ctrltail].report->type,
- urb->transfer_buffer, urb->actual_length, 0);
- break;
- case -ESHUTDOWN: /* unplug */
- unplug = 1;
- case -EILSEQ: /* protocol error or unplug */
- case -EPROTO: /* protocol error or unplug */
- case -ECONNRESET: /* unlink */
- case -ENOENT:
- case -EPIPE: /* report not available */
- break;
- default: /* error */
- warn("ctrl urb status %d received", urb->status);
- }
-
- if (unplug)
- usbhid->ctrltail = usbhid->ctrlhead;
- else
- usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
-
- if (usbhid->ctrlhead != usbhid->ctrltail) {
- if (hid_submit_ctrl(hid)) {
- clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
- wake_up(&hid->wait);
- }
- spin_unlock_irqrestore(&usbhid->ctrllock, flags);
- return;
- }
-
- clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
- spin_unlock_irqrestore(&usbhid->ctrllock, flags);
- wake_up(&hid->wait);
-}
-
-void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
-{
- int head;
- unsigned long flags;
- struct usbhid_device *usbhid = hid->driver_data;
-
- if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN)
- return;
-
- if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) {
-
- spin_lock_irqsave(&usbhid->outlock, flags);
-
- if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) {
- spin_unlock_irqrestore(&usbhid->outlock, flags);
- warn("output queue full");
- return;
- }
-
- usbhid->out[usbhid->outhead] = report;
- usbhid->outhead = head;
-
- if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl))
- if (hid_submit_out(hid))
- clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
-
- spin_unlock_irqrestore(&usbhid->outlock, flags);
- return;
- }
-
- spin_lock_irqsave(&usbhid->ctrllock, flags);
-
- if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) {
- spin_unlock_irqrestore(&usbhid->ctrllock, flags);
- warn("control queue full");
- return;
- }
-
- usbhid->ctrl[usbhid->ctrlhead].report = report;
- usbhid->ctrl[usbhid->ctrlhead].dir = dir;
- usbhid->ctrlhead = head;
-
- if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl))
- if (hid_submit_ctrl(hid))
- clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
-
- spin_unlock_irqrestore(&usbhid->ctrllock, flags);
-}
-
-static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
-{
- struct hid_device *hid = dev->private;
- struct hid_field *field;
- int offset;
-
- if (type == EV_FF)
- return input_ff_event(dev, type, code, value);
-
- if (type != EV_LED)
- return -1;
-
- if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) {
- warn("event field not found");
- return -1;
- }
-
- hid_set_field(field, offset, value);
- usbhid_submit_report(hid, field->report, USB_DIR_OUT);
-
- return 0;
-}
-
-int usbhid_wait_io(struct hid_device *hid)
-{
- struct usbhid_device *usbhid = hid->driver_data;
-
- if (!wait_event_timeout(hid->wait, (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl) &&
- !test_bit(HID_OUT_RUNNING, &usbhid->iofl)),
- 10*HZ)) {
- dbg("timeout waiting for ctrl or out queue to clear");
- return -1;
- }
-
- return 0;
-}
-
-static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, (idle << 8) | report,
- ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT);
-}
-
-static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
- unsigned char type, void *buf, int size)
-{
- int result, retries = 4;
-
- memset(buf, 0, size);
-
- do {
- result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN,
- (type << 8), ifnum, buf, size, USB_CTRL_GET_TIMEOUT);
- retries--;
- } while (result < size && retries);
- return result;
-}
-
-int usbhid_open(struct hid_device *hid)
-{
- ++hid->open;
- if (hid_start_in(hid))
- hid_io_error(hid);
- return 0;
-}
-
-void usbhid_close(struct hid_device *hid)
-{
- struct usbhid_device *usbhid = hid->driver_data;
-
- if (!--hid->open)
- usb_kill_urb(usbhid->urbin);
-}
-
-#define USB_VENDOR_ID_PANJIT 0x134c
-
-#define USB_VENDOR_ID_TURBOX 0x062a
-#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201
-
-/*
- * Initialize all reports
- */
-
-void usbhid_init_reports(struct hid_device *hid)
-{
- struct hid_report *report;
- struct usbhid_device *usbhid = hid->driver_data;
- int err, ret;
-
- list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list)
- usbhid_submit_report(hid, report, USB_DIR_IN);
-
- list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
- usbhid_submit_report(hid, report, USB_DIR_IN);
-
- err = 0;
- ret = usbhid_wait_io(hid);
- while (ret) {
- err |= ret;
- if (test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
- usb_kill_urb(usbhid->urbctrl);
- if (test_bit(HID_OUT_RUNNING, &usbhid->iofl))
- usb_kill_urb(usbhid->urbout);
- ret = usbhid_wait_io(hid);
- }
-
- if (err)
- warn("timeout initializing reports");
-}
-
-#define USB_VENDOR_ID_GTCO 0x078c
-#define USB_VENDOR_ID_GTCO_IPANEL_2 0x5543
-#define USB_DEVICE_ID_GTCO_90 0x0090
-#define USB_DEVICE_ID_GTCO_100 0x0100
-#define USB_DEVICE_ID_GTCO_101 0x0101
-#define USB_DEVICE_ID_GTCO_103 0x0103
-#define USB_DEVICE_ID_GTCO_104 0x0104
-#define USB_DEVICE_ID_GTCO_105 0x0105
-#define USB_DEVICE_ID_GTCO_106 0x0106
-#define USB_DEVICE_ID_GTCO_107 0x0107
-#define USB_DEVICE_ID_GTCO_108 0x0108
-#define USB_DEVICE_ID_GTCO_200 0x0200
-#define USB_DEVICE_ID_GTCO_201 0x0201
-#define USB_DEVICE_ID_GTCO_202 0x0202
-#define USB_DEVICE_ID_GTCO_203 0x0203
-#define USB_DEVICE_ID_GTCO_204 0x0204
-#define USB_DEVICE_ID_GTCO_205 0x0205
-#define USB_DEVICE_ID_GTCO_206 0x0206
-#define USB_DEVICE_ID_GTCO_207 0x0207
-#define USB_DEVICE_ID_GTCO_300 0x0300
-#define USB_DEVICE_ID_GTCO_301 0x0301
-#define USB_DEVICE_ID_GTCO_302 0x0302
-#define USB_DEVICE_ID_GTCO_303 0x0303
-#define USB_DEVICE_ID_GTCO_304 0x0304
-#define USB_DEVICE_ID_GTCO_305 0x0305
-#define USB_DEVICE_ID_GTCO_306 0x0306
-#define USB_DEVICE_ID_GTCO_307 0x0307
-#define USB_DEVICE_ID_GTCO_308 0x0308
-#define USB_DEVICE_ID_GTCO_309 0x0309
-#define USB_DEVICE_ID_GTCO_400 0x0400
-#define USB_DEVICE_ID_GTCO_401 0x0401
-#define USB_DEVICE_ID_GTCO_402 0x0402
-#define USB_DEVICE_ID_GTCO_403 0x0403
-#define USB_DEVICE_ID_GTCO_404 0x0404
-#define USB_DEVICE_ID_GTCO_405 0x0405
-#define USB_DEVICE_ID_GTCO_500 0x0500
-#define USB_DEVICE_ID_GTCO_501 0x0501
-#define USB_DEVICE_ID_GTCO_502 0x0502
-#define USB_DEVICE_ID_GTCO_503 0x0503
-#define USB_DEVICE_ID_GTCO_504 0x0504
-#define USB_DEVICE_ID_GTCO_1000 0x1000
-#define USB_DEVICE_ID_GTCO_1001 0x1001
-#define USB_DEVICE_ID_GTCO_1002 0x1002
-#define USB_DEVICE_ID_GTCO_1003 0x1003
-#define USB_DEVICE_ID_GTCO_1004 0x1004
-#define USB_DEVICE_ID_GTCO_1005 0x1005
-#define USB_DEVICE_ID_GTCO_1006 0x1006
-#define USB_DEVICE_ID_GTCO_8 0x0008
-#define USB_DEVICE_ID_GTCO_d 0x000d
-
-#define USB_VENDOR_ID_WACOM 0x056a
-
-#define USB_VENDOR_ID_ACECAD 0x0460
-#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004
-#define USB_DEVICE_ID_ACECAD_302 0x0008
-
-#define USB_VENDOR_ID_KBGEAR 0x084e
-#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001
-
-#define USB_VENDOR_ID_AIPTEK 0x08ca
-#define USB_DEVICE_ID_AIPTEK_01 0x0001
-#define USB_DEVICE_ID_AIPTEK_10 0x0010
-#define USB_DEVICE_ID_AIPTEK_20 0x0020
-#define USB_DEVICE_ID_AIPTEK_21 0x0021
-#define USB_DEVICE_ID_AIPTEK_22 0x0022
-#define USB_DEVICE_ID_AIPTEK_23 0x0023
-#define USB_DEVICE_ID_AIPTEK_24 0x0024
-
-#define USB_VENDOR_ID_GRIFFIN 0x077d
-#define USB_DEVICE_ID_POWERMATE 0x0410
-#define USB_DEVICE_ID_SOUNDKNOB 0x04AA
-
-#define USB_VENDOR_ID_ATEN 0x0557
-#define USB_DEVICE_ID_ATEN_UC100KM 0x2004
-#define USB_DEVICE_ID_ATEN_CS124U 0x2202
-#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204
-#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205
-#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208
-
-#define USB_VENDOR_ID_TOPMAX 0x0663
-#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103
-
-#define USB_VENDOR_ID_HAPP 0x078b
-#define USB_DEVICE_ID_UGCI_DRIVING 0x0010
-#define USB_DEVICE_ID_UGCI_FLYING 0x0020
-#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030
-
-#define USB_VENDOR_ID_MGE 0x0463
-#define USB_DEVICE_ID_MGE_UPS 0xffff
-#define USB_DEVICE_ID_MGE_UPS1 0x0001
-
-#define USB_VENDOR_ID_ONTRAK 0x0a07
-#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064
-
-#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f
-#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
-
-#define USB_VENDOR_ID_A4TECH 0x09da
-#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006
-
-#define USB_VENDOR_ID_AASHIMA 0x06d6
-#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025
-#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026
-
-#define USB_VENDOR_ID_CYPRESS 0x04b4
-#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001
-#define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500
-#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417
-
-#define USB_VENDOR_ID_BERKSHIRE 0x0c98
-#define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140
-
-#define USB_VENDOR_ID_ALPS 0x0433
-#define USB_DEVICE_ID_IBM_GAMEPAD 0x1101
-
-#define USB_VENDOR_ID_SAITEK 0x06a3
-#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
-
-#define USB_VENDOR_ID_NEC 0x073e
-#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301
-
-#define USB_VENDOR_ID_CHIC 0x05fe
-#define USB_DEVICE_ID_CHIC_GAMEPAD 0x0014
-
-#define USB_VENDOR_ID_GLAB 0x06c2
-#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
-#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039
-#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040
-#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044
-#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045
-#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051
-#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053
-#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058
-
-#define USB_VENDOR_ID_WISEGROUP 0x0925
-#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
-#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
-#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201
-#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866
-
-#define USB_VENDOR_ID_WISEGROUP_LTD 0x6677
-#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802
-
-#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
-
-#define USB_VENDOR_ID_DELORME 0x1163
-#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
-#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200
-
-#define USB_VENDOR_ID_MCC 0x09db
-#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076
-#define USB_DEVICE_ID_MCC_PMD1208LS 0x007a
-
-#define USB_VENDOR_ID_VERNIER 0x08f7
-#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001
-#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002
-#define USB_DEVICE_ID_VERNIER_SKIP 0x0003
-#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004
-
-#define USB_VENDOR_ID_LD 0x0f11
-#define USB_DEVICE_ID_LD_CASSY 0x1000
-#define USB_DEVICE_ID_LD_POCKETCASSY 0x1010
-#define USB_DEVICE_ID_LD_MOBILECASSY 0x1020
-#define USB_DEVICE_ID_LD_JWM 0x1080
-#define USB_DEVICE_ID_LD_DMMP 0x1081
-#define USB_DEVICE_ID_LD_UMIP 0x1090
-#define USB_DEVICE_ID_LD_XRAY1 0x1100
-#define USB_DEVICE_ID_LD_XRAY2 0x1101
-#define USB_DEVICE_ID_LD_VIDEOCOM 0x1200
-#define USB_DEVICE_ID_LD_COM3LAB 0x2000
-#define USB_DEVICE_ID_LD_TELEPORT 0x2010
-#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020
-#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030
-#define USB_DEVICE_ID_LD_MACHINETEST 0x2040
-
-#define USB_VENDOR_ID_APPLE 0x05ac
-#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304
-#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e
-#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f
-#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214
-#define USB_DEVICE_ID_APPLE_GEYSER_ISO 0x0215
-#define USB_DEVICE_ID_APPLE_GEYSER_JIS 0x0216
-#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI 0x0217
-#define USB_DEVICE_ID_APPLE_GEYSER3_ISO 0x0218
-#define USB_DEVICE_ID_APPLE_GEYSER3_JIS 0x0219
-#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI 0x021a
-#define USB_DEVICE_ID_APPLE_GEYSER4_ISO 0x021b
-#define USB_DEVICE_ID_APPLE_GEYSER4_JIS 0x021c
-#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
-#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
-#define USB_DEVICE_ID_APPLE_IR 0x8240
-
-#define USB_VENDOR_ID_CHERRY 0x046a
-#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023
-
-#define USB_VENDOR_ID_YEALINK 0x6993
-#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001
-
-#define USB_VENDOR_ID_ALCOR 0x058f
-#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720
-
-#define USB_VENDOR_ID_SUN 0x0430
-#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab
-
-#define USB_VENDOR_ID_AIRCABLE 0x16CA
-#define USB_DEVICE_ID_AIRCABLE1 0x1502
-
-#define USB_VENDOR_ID_LOGITECH 0x046d
-#define USB_DEVICE_ID_LOGITECH_USB_RECEIVER 0xc101
-
-#define USB_VENDOR_ID_IMATION 0x0718
-#define USB_DEVICE_ID_DISC_STAKKA 0xd000
-
-#define USB_VENDOR_ID_PANTHERLORD 0x0810
-#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK 0x0001
-
-#define USB_VENDOR_ID_SONY 0x054c
-#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268
-
-/*
- * Alphabetically sorted blacklist by quirk type.
- */
-
-static const struct hid_blacklist {
- __u16 idVendor;
- __u16 idProduct;
- unsigned quirks;
-} hid_blacklist[] = {
-
- { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE },
- { 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 },
- { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO_IPANEL_2, USB_DEVICE_ID_GTCO_8, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GTCO_IPANEL_2, USB_DEVICE_ID_GTCO_d, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE },
-
- { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE },
-
- { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
- { USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
-
- { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL },
- { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 },
- { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 },
-
- { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD },
- { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD },
- { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD },
- { USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD },
- { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
- { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
- { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
- { USB_VENDOR_ID_NEC, USB_DEVICE_ID_NEC_USB_GAME_PAD, HID_QUIRK_BADPAD },
- { 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_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION },
-
- { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
- { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
- { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
- { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
- { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
- { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
- { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
- { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
- { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
- { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
- { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
- { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
- { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-
- { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IR, HID_QUIRK_IGNORE },
-
- { USB_VENDOR_ID_PANJIT, 0x0001, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_PANJIT, 0x0002, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_PANJIT, 0x0003, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_PANJIT, 0x0004, HID_QUIRK_IGNORE },
-
- { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET },
-
- { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_USB_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS },
-
- { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
-
- { USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER },
-
- { 0, 0 }
-};
-
-/*
- * Traverse the supplied list of reports and find the longest
- */
-static void hid_find_max_report(struct hid_device *hid, unsigned int type, int *max)
-{
- struct hid_report *report;
- int size;
-
- list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
- size = ((report->size - 1) >> 3) + 1;
- if (type == HID_INPUT_REPORT && hid->report_enum[type].numbered)
- size++;
- if (*max < size)
- *max = size;
- }
-}
-
-static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid)
-{
- struct usbhid_device *usbhid = hid->driver_data;
-
- if (!(usbhid->inbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->inbuf_dma)))
- return -1;
- if (!(usbhid->outbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->outbuf_dma)))
- return -1;
- if (!(usbhid->cr = usb_buffer_alloc(dev, sizeof(*(usbhid->cr)), GFP_ATOMIC, &usbhid->cr_dma)))
- return -1;
- if (!(usbhid->ctrlbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->ctrlbuf_dma)))
- return -1;
-
- return 0;
-}
-
-static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
-{
- struct usbhid_device *usbhid = hid->driver_data;
-
- if (usbhid->inbuf)
- usb_buffer_free(dev, usbhid->bufsize, usbhid->inbuf, usbhid->inbuf_dma);
- if (usbhid->outbuf)
- usb_buffer_free(dev, usbhid->bufsize, usbhid->outbuf, usbhid->outbuf_dma);
- if (usbhid->cr)
- usb_buffer_free(dev, sizeof(*(usbhid->cr)), usbhid->cr, usbhid->cr_dma);
- if (usbhid->ctrlbuf)
- usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma);
-}
-
-/*
- * Cherry Cymotion keyboard have an invalid HID report descriptor,
- * that needs fixing before we can parse it.
- */
-
-static void hid_fixup_cymotion_descriptor(char *rdesc, int rsize)
-{
- if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
- info("Fixing up Cherry Cymotion report descriptor");
- rdesc[11] = rdesc[16] = 0xff;
- rdesc[12] = rdesc[17] = 0x03;
- }
-}
-
-/*
- * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
- * to "operational". Without this, the ps3 controller will not report any
- * events.
- */
-static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum)
-{
- int result;
- char *buf = kmalloc(18, GFP_KERNEL);
-
- if (!buf)
- return;
-
- result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- HID_REQ_GET_REPORT,
- USB_DIR_IN | USB_TYPE_CLASS |
- USB_RECIP_INTERFACE,
- (3 << 8) | 0xf2, ifnum, buf, 17,
- USB_CTRL_GET_TIMEOUT);
-
- if (result < 0)
- err("%s failed: %d\n", __func__, result);
-
- kfree(buf);
-}
-
-static struct hid_device *usb_hid_configure(struct usb_interface *intf)
-{
- struct usb_host_interface *interface = intf->cur_altsetting;
- struct usb_device *dev = interface_to_usbdev (intf);
- struct hid_descriptor *hdesc;
- struct hid_device *hid;
- unsigned quirks = 0, rsize = 0;
- char *rdesc;
- int n, len, insize = 0;
- struct usbhid_device *usbhid;
-
- /* Ignore all Wacom devices */
- if (le16_to_cpu(dev->descriptor.idVendor) == USB_VENDOR_ID_WACOM)
- return NULL;
-
- for (n = 0; hid_blacklist[n].idVendor; n++)
- if ((hid_blacklist[n].idVendor == le16_to_cpu(dev->descriptor.idVendor)) &&
- (hid_blacklist[n].idProduct == le16_to_cpu(dev->descriptor.idProduct)))
- quirks = hid_blacklist[n].quirks;
-
- /* Many keyboards and mice don't like to be polled for reports,
- * so we will always set the HID_QUIRK_NOGET flag for them. */
- if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) {
- if (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD ||
- interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)
- quirks |= HID_QUIRK_NOGET;
- }
-
- if (quirks & HID_QUIRK_IGNORE)
- return NULL;
-
- if ((quirks & HID_QUIRK_IGNORE_MOUSE) &&
- (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE))
- return NULL;
-
-
- if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
- (!interface->desc.bNumEndpoints ||
- usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
- dbg("class descriptor not present\n");
- return NULL;
- }
-
- for (n = 0; n < hdesc->bNumDescriptors; n++)
- if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
- rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
-
- if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
- dbg("weird size of report descriptor (%u)", rsize);
- return NULL;
- }
-
- if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
- dbg("couldn't allocate rdesc memory");
- return NULL;
- }
-
- hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);
-
- if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
- dbg("reading report descriptor failed");
- kfree(rdesc);
- return NULL;
- }
-
- if ((quirks & HID_QUIRK_CYMOTION))
- hid_fixup_cymotion_descriptor(rdesc, rsize);
-
-#ifdef DEBUG_DATA
- printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n);
- for (n = 0; n < rsize; n++)
- printk(" %02x", (unsigned char) rdesc[n]);
- printk("\n");
-#endif
-
- if (!(hid = hid_parse_report(rdesc, n))) {
- dbg("parsing report descriptor failed");
- kfree(rdesc);
- return NULL;
- }
-
- kfree(rdesc);
- hid->quirks = quirks;
-
- if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL)))
- goto fail;
-
- hid->driver_data = usbhid;
- usbhid->hid = hid;
-
- usbhid->bufsize = HID_MIN_BUFFER_SIZE;
- hid_find_max_report(hid, HID_INPUT_REPORT, &usbhid->bufsize);
- hid_find_max_report(hid, HID_OUTPUT_REPORT, &usbhid->bufsize);
- hid_find_max_report(hid, HID_FEATURE_REPORT, &usbhid->bufsize);
-
- if (usbhid->bufsize > HID_MAX_BUFFER_SIZE)
- usbhid->bufsize = HID_MAX_BUFFER_SIZE;
-
- hid_find_max_report(hid, HID_INPUT_REPORT, &insize);
-
- if (insize > HID_MAX_BUFFER_SIZE)
- insize = HID_MAX_BUFFER_SIZE;
-
- if (hid_alloc_buffers(dev, hid)) {
- hid_free_buffers(dev, hid);
- goto fail;
- }
-
- for (n = 0; n < interface->desc.bNumEndpoints; n++) {
-
- struct usb_endpoint_descriptor *endpoint;
- int pipe;
- int interval;
-
- endpoint = &interface->endpoint[n].desc;
- if ((endpoint->bmAttributes & 3) != 3) /* Not an interrupt endpoint */
- continue;
-
- interval = endpoint->bInterval;
-
- /* Change the polling interval of mice. */
- if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)
- interval = hid_mousepoll_interval;
-
- if (usb_endpoint_dir_in(endpoint)) {
- if (usbhid->urbin)
- continue;
- if (!(usbhid->urbin = usb_alloc_urb(0, GFP_KERNEL)))
- goto fail;
- pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
- usb_fill_int_urb(usbhid->urbin, dev, pipe, usbhid->inbuf, insize,
- hid_irq_in, hid, interval);
- usbhid->urbin->transfer_dma = usbhid->inbuf_dma;
- usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- } else {
- if (usbhid->urbout)
- continue;
- if (!(usbhid->urbout = usb_alloc_urb(0, GFP_KERNEL)))
- goto fail;
- pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress);
- usb_fill_int_urb(usbhid->urbout, dev, pipe, usbhid->outbuf, 0,
- hid_irq_out, hid, interval);
- usbhid->urbout->transfer_dma = usbhid->outbuf_dma;
- usbhid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- }
- }
-
- if (!usbhid->urbin) {
- err("couldn't find an input interrupt endpoint");
- goto fail;
- }
-
- init_waitqueue_head(&hid->wait);
-
- INIT_WORK(&usbhid->reset_work, hid_reset);
- setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
-
- spin_lock_init(&usbhid->inlock);
- spin_lock_init(&usbhid->outlock);
- spin_lock_init(&usbhid->ctrllock);
-
- hid->version = le16_to_cpu(hdesc->bcdHID);
- hid->country = hdesc->bCountryCode;
- hid->dev = &intf->dev;
- usbhid->intf = intf;
- usbhid->ifnum = interface->desc.bInterfaceNumber;
-
- hid->name[0] = 0;
-
- if (dev->manufacturer)
- strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
-
- if (dev->product) {
- if (dev->manufacturer)
- strlcat(hid->name, " ", sizeof(hid->name));
- strlcat(hid->name, dev->product, sizeof(hid->name));
- }
-
- if (!strlen(hid->name))
- snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
- le16_to_cpu(dev->descriptor.idVendor),
- le16_to_cpu(dev->descriptor.idProduct));
-
- hid->bus = BUS_USB;
- hid->vendor = dev->descriptor.idVendor;
- hid->product = dev->descriptor.idProduct;
-
- usb_make_path(dev, hid->phys, sizeof(hid->phys));
- strlcat(hid->phys, "/input", sizeof(hid->phys));
- len = strlen(hid->phys);
- if (len < sizeof(hid->phys) - 1)
- snprintf(hid->phys + len, sizeof(hid->phys) - len,
- "%d", intf->altsetting[0].desc.bInterfaceNumber);
-
- if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
- hid->uniq[0] = 0;
-
- usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
- if (!usbhid->urbctrl)
- goto fail;
-
- usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
- usbhid->ctrlbuf, 1, hid_ctrl, hid);
- usbhid->urbctrl->setup_dma = usbhid->cr_dma;
- usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
- usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
- hid->hidinput_input_event = usb_hidinput_input_event;
- hid->hid_open = usbhid_open;
- hid->hid_close = usbhid_close;
-#ifdef CONFIG_USB_HIDDEV
- hid->hiddev_hid_event = hiddev_hid_event;
- hid->hiddev_report_event = hiddev_report_event;
-#endif
- return hid;
-
-fail:
- usb_free_urb(usbhid->urbin);
- usb_free_urb(usbhid->urbout);
- usb_free_urb(usbhid->urbctrl);
- hid_free_buffers(dev, hid);
- hid_free_device(hid);
-
- return NULL;
-}
-
-static void hid_disconnect(struct usb_interface *intf)
-{
- struct hid_device *hid = usb_get_intfdata (intf);
- struct usbhid_device *usbhid;
-
- if (!hid)
- return;
-
- usbhid = hid->driver_data;
-
- spin_lock_irq(&usbhid->inlock); /* Sync with error handler */
- usb_set_intfdata(intf, NULL);
- spin_unlock_irq(&usbhid->inlock);
- usb_kill_urb(usbhid->urbin);
- usb_kill_urb(usbhid->urbout);
- usb_kill_urb(usbhid->urbctrl);
-
- del_timer_sync(&usbhid->io_retry);
- flush_scheduled_work();
-
- if (hid->claimed & HID_CLAIMED_INPUT)
- hidinput_disconnect(hid);
- if (hid->claimed & HID_CLAIMED_HIDDEV)
- hiddev_disconnect(hid);
-
- usb_free_urb(usbhid->urbin);
- usb_free_urb(usbhid->urbctrl);
- usb_free_urb(usbhid->urbout);
-
- hid_free_buffers(hid_to_usb_dev(hid), hid);
- hid_free_device(hid);
-}
-
-static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
-{
- struct hid_device *hid;
- char path[64];
- int i;
- char *c;
-
- dbg("HID probe called for ifnum %d",
- intf->altsetting->desc.bInterfaceNumber);
-
- if (!(hid = usb_hid_configure(intf)))
- return -ENODEV;
-
- usbhid_init_reports(hid);
- hid_dump_device(hid);
-
- if (!hidinput_connect(hid))
- hid->claimed |= HID_CLAIMED_INPUT;
- if (!hiddev_connect(hid))
- hid->claimed |= HID_CLAIMED_HIDDEV;
-
- usb_set_intfdata(intf, hid);
-
- if (!hid->claimed) {
- printk ("HID device not claimed by input or hiddev\n");
- hid_disconnect(intf);
- return -ENODEV;
- }
-
- if ((hid->claimed & HID_CLAIMED_INPUT))
- hid_ff_init(hid);
-
- if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER)
- hid_fixup_sony_ps3_controller(interface_to_usbdev(intf),
- intf->cur_altsetting->desc.bInterfaceNumber);
-
- printk(KERN_INFO);
-
- if (hid->claimed & HID_CLAIMED_INPUT)
- printk("input");
- if (hid->claimed == (HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV))
- printk(",");
- if (hid->claimed & HID_CLAIMED_HIDDEV)
- printk("hiddev%d", hid->minor);
-
- c = "Device";
- for (i = 0; i < hid->maxcollection; i++) {
- if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
- (hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
- (hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) {
- c = hid_types[hid->collection[i].usage & 0xffff];
- break;
- }
- }
-
- usb_make_path(interface_to_usbdev(intf), path, 63);
-
- printk(": USB HID v%x.%02x %s [%s] on %s\n",
- hid->version >> 8, hid->version & 0xff, c, hid->name, path);
-
- return 0;
-}
-
-static int hid_suspend(struct usb_interface *intf, pm_message_t message)
-{
- struct hid_device *hid = usb_get_intfdata (intf);
- struct usbhid_device *usbhid = hid->driver_data;
-
- spin_lock_irq(&usbhid->inlock); /* Sync with error handler */
- set_bit(HID_SUSPENDED, &usbhid->iofl);
- spin_unlock_irq(&usbhid->inlock);
- del_timer(&usbhid->io_retry);
- usb_kill_urb(usbhid->urbin);
- dev_dbg(&intf->dev, "suspend\n");
- return 0;
-}
-
-static int hid_resume(struct usb_interface *intf)
-{
- struct hid_device *hid = usb_get_intfdata (intf);
- struct usbhid_device *usbhid = hid->driver_data;
- int status;
-
- clear_bit(HID_SUSPENDED, &usbhid->iofl);
- usbhid->retry_delay = 0;
- status = hid_start_in(hid);
- dev_dbg(&intf->dev, "resume status %d\n", status);
- return status;
-}
-
-/* Treat USB reset pretty much the same as suspend/resume */
-static void hid_pre_reset(struct usb_interface *intf)
-{
- /* FIXME: What if the interface is already suspended? */
- hid_suspend(intf, PMSG_ON);
-}
-
-static void hid_post_reset(struct usb_interface *intf)
-{
- struct usb_device *dev = interface_to_usbdev (intf);
-
- hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
- /* FIXME: Any more reinitialization needed? */
-
- hid_resume(intf);
-}
-
-static struct usb_device_id hid_usb_ids [] = {
- { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
- .bInterfaceClass = USB_INTERFACE_CLASS_HID },
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, hid_usb_ids);
-
-static struct usb_driver hid_driver = {
- .name = "usbhid",
- .probe = hid_probe,
- .disconnect = hid_disconnect,
- .suspend = hid_suspend,
- .resume = hid_resume,
- .pre_reset = hid_pre_reset,
- .post_reset = hid_post_reset,
- .id_table = hid_usb_ids,
-};
-
-static int __init hid_init(void)
-{
- int retval;
- retval = hiddev_init();
- if (retval)
- goto hiddev_init_fail;
- retval = usb_register(&hid_driver);
- if (retval)
- goto usb_register_fail;
- info(DRIVER_VERSION ":" DRIVER_DESC);
-
- return 0;
-usb_register_fail:
- hiddev_exit();
-hiddev_init_fail:
- return retval;
-}
-
-static void __exit hid_exit(void)
-{
- usb_deregister(&hid_driver);
- hiddev_exit();
-}
-
-module_init(hid_init);
-module_exit(hid_exit);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE(DRIVER_LICENSE);
diff --git a/drivers/usb/input/hid-ff.c b/drivers/usb/input/hid-ff.c
deleted file mode 100644
index e431faaa6ab..00000000000
--- a/drivers/usb/input/hid-ff.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * $Id: hid-ff.c,v 1.2 2002/04/18 22:02:47 jdeneux Exp $
- *
- * Force feedback support for hid devices.
- * Not all hid devices use the same protocol. For example, some use PID,
- * other use their own proprietary procotol.
- *
- * Copyright (c) 2002-2004 Johann Deneux
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Should you need to contact me, the author, you can do so by
- * e-mail - mail your message to <johann.deneux@it.uu.se>
- */
-
-#include <linux/input.h>
-
-#undef DEBUG
-#include <linux/usb.h>
-
-#include <linux/hid.h>
-#include "usbhid.h"
-
-/*
- * This table contains pointers to initializers. To add support for new
- * devices, you need to add the USB vendor and product ids here.
- */
-struct hid_ff_initializer {
- u16 idVendor;
- u16 idProduct;
- int (*init)(struct hid_device*);
-};
-
-/*
- * We try pidff when no other driver is found because PID is the
- * standards compliant way of implementing force feedback in HID.
- * pidff_init() will quickly abort if the device doesn't appear to
- * be a PID device
- */
-static struct hid_ff_initializer inits[] = {
-#ifdef CONFIG_LOGITECH_FF
- { 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */
- { 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */
- { 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */
- { 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */
- { 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */
- { 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */
-#endif
-#ifdef CONFIG_PANTHERLORD_FF
- { 0x810, 0x0001, hid_plff_init },
-#endif
-#ifdef CONFIG_THRUSTMASTER_FF
- { 0x44f, 0xb304, hid_tmff_init },
-#endif
-#ifdef CONFIG_ZEROPLUS_FF
- { 0xc12, 0x0005, hid_zpff_init },
- { 0xc12, 0x0030, hid_zpff_init },
-#endif
- { 0, 0, hid_pidff_init} /* Matches anything */
-};
-
-int hid_ff_init(struct hid_device* hid)
-{
- struct hid_ff_initializer *init;
- int vendor = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idVendor);
- int product = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idProduct);
-
- for (init = inits; init->idVendor; init++)
- if (init->idVendor == vendor && init->idProduct == product)
- break;
-
- return init->init(hid);
-}
-EXPORT_SYMBOL_GPL(hid_ff_init);
-
diff --git a/drivers/usb/input/hid-lgff.c b/drivers/usb/input/hid-lgff.c
deleted file mode 100644
index e6f3af3e66d..00000000000
--- a/drivers/usb/input/hid-lgff.c
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Force feedback support for hid-compliant for some of the devices from
- * Logitech, namely:
- * - WingMan Cordless RumblePad
- * - WingMan Force 3D
- *
- * Copyright (c) 2002-2004 Johann Deneux
- * Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Should you need to contact me, the author, you can do so by
- * e-mail - mail your message to <johann.deneux@it.uu.se>
- */
-
-#include <linux/input.h>
-#include <linux/usb.h>
-#include <linux/hid.h>
-#include "usbhid.h"
-
-struct dev_type {
- u16 idVendor;
- u16 idProduct;
- const signed short *ff;
-};
-
-static const signed short ff_rumble[] = {
- FF_RUMBLE,
- -1
-};
-
-static const signed short ff_joystick[] = {
- FF_CONSTANT,
- -1
-};
-
-static const struct dev_type devices[] = {
- { 0x046d, 0xc211, ff_rumble },
- { 0x046d, 0xc219, ff_rumble },
- { 0x046d, 0xc283, ff_joystick },
- { 0x046d, 0xc294, ff_joystick },
- { 0x046d, 0xc295, ff_joystick },
- { 0x046d, 0xca03, ff_joystick },
-};
-
-static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
-{
- struct hid_device *hid = dev->private;
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
- int x, y;
- unsigned int left, right;
-
-#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
-
- switch (effect->type) {
- case FF_CONSTANT:
- x = effect->u.ramp.start_level + 0x7f; /* 0x7f is center */
- y = effect->u.ramp.end_level + 0x7f;
- CLAMP(x);
- CLAMP(y);
- report->field[0]->value[0] = 0x51;
- report->field[0]->value[1] = 0x08;
- report->field[0]->value[2] = x;
- report->field[0]->value[3] = y;
- dbg("(x, y)=(%04x, %04x)", x, y);
- usbhid_submit_report(hid, report, USB_DIR_OUT);
- break;
-
- case FF_RUMBLE:
- right = effect->u.rumble.strong_magnitude;
- left = effect->u.rumble.weak_magnitude;
- right = right * 0xff / 0xffff;
- left = left * 0xff / 0xffff;
- CLAMP(left);
- CLAMP(right);
- report->field[0]->value[0] = 0x42;
- report->field[0]->value[1] = 0x00;
- report->field[0]->value[2] = left;
- report->field[0]->value[3] = right;
- dbg("(left, right)=(%04x, %04x)", left, right);
- usbhid_submit_report(hid, report, USB_DIR_OUT);
- break;
- }
- return 0;
-}
-
-int hid_lgff_init(struct hid_device* hid)
-{
- struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct input_dev *dev = hidinput->input;
- struct hid_report *report;
- struct hid_field *field;
- const signed short *ff_bits = ff_joystick;
- int error;
- int i;
-
- /* Find the report to use */
- if (list_empty(report_list)) {
- err("No output report found");
- return -1;
- }
-
- /* Check that the report looks ok */
- report = list_entry(report_list->next, struct hid_report, list);
- if (!report) {
- err("NULL output report");
- return -1;
- }
-
- field = report->field[0];
- if (!field) {
- err("NULL field");
- return -1;
- }
-
- for (i = 0; i < ARRAY_SIZE(devices); i++) {
- if (dev->id.vendor == devices[i].idVendor &&
- dev->id.product == devices[i].idProduct) {
- ff_bits = devices[i].ff;
- break;
- }
- }
-
- for (i = 0; ff_bits[i] >= 0; i++)
- set_bit(ff_bits[i], dev->ffbit);
-
- error = input_ff_create_memless(dev, NULL, hid_lgff_play);
- if (error)
- return error;
-
- printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n");
-
- return 0;
-}
diff --git a/drivers/usb/input/hid-pidff.c b/drivers/usb/input/hid-pidff.c
deleted file mode 100644
index f5a90e950e6..00000000000
--- a/drivers/usb/input/hid-pidff.c
+++ /dev/null
@@ -1,1331 +0,0 @@
-/*
- * Force feedback driver for USB HID PID compliant devices
- *
- * Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.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.
- *
- * 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
- */
-
-/* #define DEBUG */
-
-#define debug(format, arg...) pr_debug("hid-pidff: " format "\n" , ## arg)
-
-#include <linux/input.h>
-#include <linux/usb.h>
-
-#include <linux/hid.h>
-
-#include "usbhid.h"
-
-#define PID_EFFECTS_MAX 64
-
-/* Report usage table used to put reports into an array */
-
-#define PID_SET_EFFECT 0
-#define PID_EFFECT_OPERATION 1
-#define PID_DEVICE_GAIN 2
-#define PID_POOL 3
-#define PID_BLOCK_LOAD 4
-#define PID_BLOCK_FREE 5
-#define PID_DEVICE_CONTROL 6
-#define PID_CREATE_NEW_EFFECT 7
-
-#define PID_REQUIRED_REPORTS 7
-
-#define PID_SET_ENVELOPE 8
-#define PID_SET_CONDITION 9
-#define PID_SET_PERIODIC 10
-#define PID_SET_CONSTANT 11
-#define PID_SET_RAMP 12
-static const u8 pidff_reports[] = {
- 0x21, 0x77, 0x7d, 0x7f, 0x89, 0x90, 0x96, 0xab,
- 0x5a, 0x5f, 0x6e, 0x73, 0x74
-};
-
-/* device_control is really 0x95, but 0x96 specified as it is the usage of
-the only field in that report */
-
-/* Value usage tables used to put fields and values into arrays */
-
-#define PID_EFFECT_BLOCK_INDEX 0
-
-#define PID_DURATION 1
-#define PID_GAIN 2
-#define PID_TRIGGER_BUTTON 3
-#define PID_TRIGGER_REPEAT_INT 4
-#define PID_DIRECTION_ENABLE 5
-#define PID_START_DELAY 6
-static const u8 pidff_set_effect[] = {
- 0x22, 0x50, 0x52, 0x53, 0x54, 0x56, 0xa7
-};
-
-#define PID_ATTACK_LEVEL 1
-#define PID_ATTACK_TIME 2
-#define PID_FADE_LEVEL 3
-#define PID_FADE_TIME 4
-static const u8 pidff_set_envelope[] = { 0x22, 0x5b, 0x5c, 0x5d, 0x5e };
-
-#define PID_PARAM_BLOCK_OFFSET 1
-#define PID_CP_OFFSET 2
-#define PID_POS_COEFFICIENT 3
-#define PID_NEG_COEFFICIENT 4
-#define PID_POS_SATURATION 5
-#define PID_NEG_SATURATION 6
-#define PID_DEAD_BAND 7
-static const u8 pidff_set_condition[] = {
- 0x22, 0x23, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65
-};
-
-#define PID_MAGNITUDE 1
-#define PID_OFFSET 2
-#define PID_PHASE 3
-#define PID_PERIOD 4
-static const u8 pidff_set_periodic[] = { 0x22, 0x70, 0x6f, 0x71, 0x72 };
-static const u8 pidff_set_constant[] = { 0x22, 0x70 };
-
-#define PID_RAMP_START 1
-#define PID_RAMP_END 2
-static const u8 pidff_set_ramp[] = { 0x22, 0x75, 0x76 };
-
-#define PID_RAM_POOL_AVAILABLE 1
-static const u8 pidff_block_load[] = { 0x22, 0xac };
-
-#define PID_LOOP_COUNT 1
-static const u8 pidff_effect_operation[] = { 0x22, 0x7c };
-
-static const u8 pidff_block_free[] = { 0x22 };
-
-#define PID_DEVICE_GAIN_FIELD 0
-static const u8 pidff_device_gain[] = { 0x7e };
-
-#define PID_RAM_POOL_SIZE 0
-#define PID_SIMULTANEOUS_MAX 1
-#define PID_DEVICE_MANAGED_POOL 2
-static const u8 pidff_pool[] = { 0x80, 0x83, 0xa9 };
-
-/* Special field key tables used to put special field keys into arrays */
-
-#define PID_ENABLE_ACTUATORS 0
-#define PID_RESET 1
-static const u8 pidff_device_control[] = { 0x97, 0x9a };
-
-#define PID_CONSTANT 0
-#define PID_RAMP 1
-#define PID_SQUARE 2
-#define PID_SINE 3
-#define PID_TRIANGLE 4
-#define PID_SAW_UP 5
-#define PID_SAW_DOWN 6
-#define PID_SPRING 7
-#define PID_DAMPER 8
-#define PID_INERTIA 9
-#define PID_FRICTION 10
-static const u8 pidff_effect_types[] = {
- 0x26, 0x27, 0x30, 0x31, 0x32, 0x33, 0x34,
- 0x40, 0x41, 0x42, 0x43
-};
-
-#define PID_BLOCK_LOAD_SUCCESS 0
-#define PID_BLOCK_LOAD_FULL 1
-static const u8 pidff_block_load_status[] = { 0x8c, 0x8d };
-
-#define PID_EFFECT_START 0
-#define PID_EFFECT_STOP 1
-static const u8 pidff_effect_operation_status[] = { 0x79, 0x7b };
-
-struct pidff_usage {
- struct hid_field *field;
- s32 *value;
-};
-
-struct pidff_device {
- struct hid_device *hid;
-
- struct hid_report *reports[sizeof(pidff_reports)];
-
- struct pidff_usage set_effect[sizeof(pidff_set_effect)];
- struct pidff_usage set_envelope[sizeof(pidff_set_envelope)];
- struct pidff_usage set_condition[sizeof(pidff_set_condition)];
- struct pidff_usage set_periodic[sizeof(pidff_set_periodic)];
- struct pidff_usage set_constant[sizeof(pidff_set_constant)];
- struct pidff_usage set_ramp[sizeof(pidff_set_ramp)];
-
- struct pidff_usage device_gain[sizeof(pidff_device_gain)];
- struct pidff_usage block_load[sizeof(pidff_block_load)];
- struct pidff_usage pool[sizeof(pidff_pool)];
- struct pidff_usage effect_operation[sizeof(pidff_effect_operation)];
- struct pidff_usage block_free[sizeof(pidff_block_free)];
-
- /* Special field is a field that is not composed of
- usage<->value pairs that pidff_usage values are */
-
- /* Special field in create_new_effect */
- struct hid_field *create_new_effect_type;
-
- /* Special fields in set_effect */
- struct hid_field *set_effect_type;
- struct hid_field *effect_direction;
-
- /* Special field in device_control */
- struct hid_field *device_control;
-
- /* Special field in block_load */
- struct hid_field *block_load_status;
-
- /* Special field in effect_operation */
- struct hid_field *effect_operation_status;
-
- int control_id[sizeof(pidff_device_control)];
- int type_id[sizeof(pidff_effect_types)];
- int status_id[sizeof(pidff_block_load_status)];
- int operation_id[sizeof(pidff_effect_operation_status)];
-
- int pid_id[PID_EFFECTS_MAX];
-};
-
-/*
- * Scale an unsigned value with range 0..max for the given field
- */
-static int pidff_rescale(int i, int max, struct hid_field *field)
-{
- return i * (field->logical_maximum - field->logical_minimum) / max +
- field->logical_minimum;
-}
-
-/*
- * Scale a signed value in range -0x8000..0x7fff for the given field
- */
-static int pidff_rescale_signed(int i, struct hid_field *field)
-{
- return i == 0 ? 0 : i >
- 0 ? i * field->logical_maximum / 0x7fff : i *
- field->logical_minimum / -0x8000;
-}
-
-static void pidff_set(struct pidff_usage *usage, u16 value)
-{
- usage->value[0] = pidff_rescale(value, 0xffff, usage->field);
- debug("calculated from %d to %d", value, usage->value[0]);
-}
-
-static void pidff_set_signed(struct pidff_usage *usage, s16 value)
-{
- if (usage->field->logical_minimum < 0)
- usage->value[0] = pidff_rescale_signed(value, usage->field);
- else {
- if (value < 0)
- usage->value[0] =
- pidff_rescale(-value, 0x8000, usage->field);
- else
- usage->value[0] =
- pidff_rescale(value, 0x7fff, usage->field);
- }
- debug("calculated from %d to %d", value, usage->value[0]);
-}
-
-/*
- * Send envelope report to the device
- */
-static void pidff_set_envelope_report(struct pidff_device *pidff,
- struct ff_envelope *envelope)
-{
- pidff->set_envelope[PID_EFFECT_BLOCK_INDEX].value[0] =
- pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
-
- pidff->set_envelope[PID_ATTACK_LEVEL].value[0] =
- pidff_rescale(envelope->attack_level >
- 0x7fff ? 0x7fff : envelope->attack_level, 0x7fff,
- pidff->set_envelope[PID_ATTACK_LEVEL].field);
- pidff->set_envelope[PID_FADE_LEVEL].value[0] =
- pidff_rescale(envelope->fade_level >
- 0x7fff ? 0x7fff : envelope->fade_level, 0x7fff,
- pidff->set_envelope[PID_FADE_LEVEL].field);
-
- pidff->set_envelope[PID_ATTACK_TIME].value[0] = envelope->attack_length;
- pidff->set_envelope[PID_FADE_TIME].value[0] = envelope->fade_length;
-
- debug("attack %u => %d", envelope->attack_level,
- pidff->set_envelope[PID_ATTACK_LEVEL].value[0]);
-
- usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_ENVELOPE],
- USB_DIR_OUT);
-}
-
-/*
- * Test if the new envelope differs from old one
- */
-static int pidff_needs_set_envelope(struct ff_envelope *envelope,
- struct ff_envelope *old)
-{
- return envelope->attack_level != old->attack_level ||
- envelope->fade_level != old->fade_level ||
- envelope->attack_length != old->attack_length ||
- envelope->fade_length != old->fade_length;
-}
-
-/*
- * Send constant force report to the device
- */
-static void pidff_set_constant_force_report(struct pidff_device *pidff,
- struct ff_effect *effect)
-{
- pidff->set_constant[PID_EFFECT_BLOCK_INDEX].value[0] =
- pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
- pidff_set_signed(&pidff->set_constant[PID_MAGNITUDE],
- effect->u.constant.level);
-
- usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONSTANT],
- USB_DIR_OUT);
-}
-
-/*
- * Test if the constant parameters have changed between effects
- */
-static int pidff_needs_set_constant(struct ff_effect *effect,
- struct ff_effect *old)
-{
- return effect->u.constant.level != old->u.constant.level;
-}
-
-/*
- * Send set effect report to the device
- */
-static void pidff_set_effect_report(struct pidff_device *pidff,
- struct ff_effect *effect)
-{
- pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] =
- pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
- pidff->set_effect_type->value[0] =
- pidff->create_new_effect_type->value[0];
- pidff->set_effect[PID_DURATION].value[0] = effect->replay.length;
- pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = effect->trigger.button;
- pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] =
- effect->trigger.interval;
- pidff->set_effect[PID_GAIN].value[0] =
- pidff->set_effect[PID_GAIN].field->logical_maximum;
- pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1;
- pidff->effect_direction->value[0] =
- pidff_rescale(effect->direction, 0xffff,
- pidff->effect_direction);
- pidff->set_effect[PID_START_DELAY].value[0] = effect->replay.delay;
-
- usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT],
- USB_DIR_OUT);
-}
-
-/*
- * Test if the values used in set_effect have changed
- */
-static int pidff_needs_set_effect(struct ff_effect *effect,
- struct ff_effect *old)
-{
- return effect->replay.length != old->replay.length ||
- effect->trigger.interval != old->trigger.interval ||
- effect->trigger.button != old->trigger.button ||
- effect->direction != old->direction ||
- effect->replay.delay != old->replay.delay;
-}
-
-/*
- * Send periodic effect report to the device
- */
-static void pidff_set_periodic_report(struct pidff_device *pidff,
- struct ff_effect *effect)
-{
- pidff->set_periodic[PID_EFFECT_BLOCK_INDEX].value[0] =
- pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
- pidff_set_signed(&pidff->set_periodic[PID_MAGNITUDE],
- effect->u.periodic.magnitude);
- pidff_set_signed(&pidff->set_periodic[PID_OFFSET],
- effect->u.periodic.offset);
- pidff_set(&pidff->set_periodic[PID_PHASE], effect->u.periodic.phase);
- pidff->set_periodic[PID_PERIOD].value[0] = effect->u.periodic.period;
-
- usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_PERIODIC],
- USB_DIR_OUT);
-
-}
-
-/*
- * Test if periodic effect parameters have changed
- */
-static int pidff_needs_set_periodic(struct ff_effect *effect,
- struct ff_effect *old)
-{
- return effect->u.periodic.magnitude != old->u.periodic.magnitude ||
- effect->u.periodic.offset != old->u.periodic.offset ||
- effect->u.periodic.phase != old->u.periodic.phase ||
- effect->u.periodic.period != old->u.periodic.period;
-}
-
-/*
- * Send condition effect reports to the device
- */
-static void pidff_set_condition_report(struct pidff_device *pidff,
- struct ff_effect *effect)
-{
- int i;
-
- pidff->set_condition[PID_EFFECT_BLOCK_INDEX].value[0] =
- pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
-
- for (i = 0; i < 2; i++) {
- pidff->set_condition[PID_PARAM_BLOCK_OFFSET].value[0] = i;
- pidff_set_signed(&pidff->set_condition[PID_CP_OFFSET],
- effect->u.condition[i].center);
- pidff_set_signed(&pidff->set_condition[PID_POS_COEFFICIENT],
- effect->u.condition[i].right_coeff);
- pidff_set_signed(&pidff->set_condition[PID_NEG_COEFFICIENT],
- effect->u.condition[i].left_coeff);
- pidff_set(&pidff->set_condition[PID_POS_SATURATION],
- effect->u.condition[i].right_saturation);
- pidff_set(&pidff->set_condition[PID_NEG_SATURATION],
- effect->u.condition[i].left_saturation);
- pidff_set(&pidff->set_condition[PID_DEAD_BAND],
- effect->u.condition[i].deadband);
- usbhid_wait_io(pidff->hid);
- usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION],
- USB_DIR_OUT);
- }
-}
-
-/*
- * Test if condition effect parameters have changed
- */
-static int pidff_needs_set_condition(struct ff_effect *effect,
- struct ff_effect *old)
-{
- int i;
- int ret = 0;
-
- for (i = 0; i < 2; i++) {
- struct ff_condition_effect *cond = &effect->u.condition[i];
- struct ff_condition_effect *old_cond = &old->u.condition[i];
-
- ret |= cond->center != old_cond->center ||
- cond->right_coeff != old_cond->right_coeff ||
- cond->left_coeff != old_cond->left_coeff ||
- cond->right_saturation != old_cond->right_saturation ||
- cond->left_saturation != old_cond->left_saturation ||
- cond->deadband != old_cond->deadband;
- }
-
- return ret;
-}
-
-/*
- * Send ramp force report to the device
- */
-static void pidff_set_ramp_force_report(struct pidff_device *pidff,
- struct ff_effect *effect)
-{
- pidff->set_ramp[PID_EFFECT_BLOCK_INDEX].value[0] =
- pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
- pidff_set_signed(&pidff->set_ramp[PID_RAMP_START],
- effect->u.ramp.start_level);
- pidff_set_signed(&pidff->set_ramp[PID_RAMP_END],
- effect->u.ramp.end_level);
- usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_RAMP],
- USB_DIR_OUT);
-}
-
-/*
- * Test if ramp force parameters have changed
- */
-static int pidff_needs_set_ramp(struct ff_effect *effect, struct ff_effect *old)
-{
- return effect->u.ramp.start_level != old->u.ramp.start_level ||
- effect->u.ramp.end_level != old->u.ramp.end_level;
-}
-
-/*
- * Send a request for effect upload to the device
- *
- * Returns 0 if device reported success, -ENOSPC if the device reported memory
- * is full. Upon unknown response the function will retry for 60 times, if
- * still unsuccessful -EIO is returned.
- */
-static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum)
-{
- int j;
-
- pidff->create_new_effect_type->value[0] = efnum;
- usbhid_submit_report(pidff->hid, pidff->reports[PID_CREATE_NEW_EFFECT],
- USB_DIR_OUT);
- debug("create_new_effect sent, type: %d", efnum);
-
- pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = 0;
- pidff->block_load_status->value[0] = 0;
- usbhid_wait_io(pidff->hid);
-
- for (j = 0; j < 60; j++) {
- debug("pid_block_load requested");
- usbhid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_LOAD],
- USB_DIR_IN);
- usbhid_wait_io(pidff->hid);
- if (pidff->block_load_status->value[0] ==
- pidff->status_id[PID_BLOCK_LOAD_SUCCESS]) {
- debug("device reported free memory: %d bytes",
- pidff->block_load[PID_RAM_POOL_AVAILABLE].value ?
- pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1);
- return 0;
- }
- if (pidff->block_load_status->value[0] ==
- pidff->status_id[PID_BLOCK_LOAD_FULL]) {
- debug("not enough memory free: %d bytes",
- pidff->block_load[PID_RAM_POOL_AVAILABLE].value ?
- pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1);
- return -ENOSPC;
- }
- }
- printk(KERN_ERR "hid-pidff: pid_block_load failed 60 times\n");
- return -EIO;
-}
-
-/*
- * Play the effect with PID id n times
- */
-static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n)
-{
- pidff->effect_operation[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id;
-
- if (n == 0) {
- pidff->effect_operation_status->value[0] =
- pidff->operation_id[PID_EFFECT_STOP];
- } else {
- pidff->effect_operation_status->value[0] =
- pidff->operation_id[PID_EFFECT_START];
- pidff->effect_operation[PID_LOOP_COUNT].value[0] = n;
- }
-
- usbhid_wait_io(pidff->hid);
- usbhid_submit_report(pidff->hid, pidff->reports[PID_EFFECT_OPERATION],
- USB_DIR_OUT);
-}
-
-/**
- * Play the effect with effect id @effect_id for @value times
- */
-static int pidff_playback(struct input_dev *dev, int effect_id, int value)
-{
- struct pidff_device *pidff = dev->ff->private;
-
- pidff_playback_pid(pidff, pidff->pid_id[effect_id], value);
-
- return 0;
-}
-
-/*
- * Erase effect with PID id
- */
-static void pidff_erase_pid(struct pidff_device *pidff, int pid_id)
-{
- pidff->block_free[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id;
- usbhid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_FREE],
- USB_DIR_OUT);
-}
-
-/*
- * Stop and erase effect with effect_id
- */
-static int pidff_erase_effect(struct input_dev *dev, int effect_id)
-{
- struct pidff_device *pidff = dev->ff->private;
- int pid_id = pidff->pid_id[effect_id];
-
- debug("starting to erase %d/%d", effect_id, pidff->pid_id[effect_id]);
- pidff_playback_pid(pidff, pid_id, 0);
- pidff_erase_pid(pidff, pid_id);
-
- return 0;
-}
-
-/*
- * Effect upload handler
- */
-static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect,
- struct ff_effect *old)
-{
- struct pidff_device *pidff = dev->ff->private;
- int type_id;
- int error;
-
- switch (effect->type) {
- case FF_CONSTANT:
- if (!old) {
- error = pidff_request_effect_upload(pidff,
- pidff->type_id[PID_CONSTANT]);
- if (error)
- return error;
- }
- if (!old || pidff_needs_set_effect(effect, old))
- pidff_set_effect_report(pidff, effect);
- if (!old || pidff_needs_set_constant(effect, old))
- pidff_set_constant_force_report(pidff, effect);
- if (!old ||
- pidff_needs_set_envelope(&effect->u.constant.envelope,
- &old->u.constant.envelope))
- pidff_set_envelope_report(pidff,
- &effect->u.constant.envelope);
- break;
-
- case FF_PERIODIC:
- if (!old) {
- switch (effect->u.periodic.waveform) {
- case FF_SQUARE:
- type_id = PID_SQUARE;
- break;
- case FF_TRIANGLE:
- type_id = PID_TRIANGLE;
- break;
- case FF_SINE:
- type_id = PID_SINE;
- break;
- case FF_SAW_UP:
- type_id = PID_SAW_UP;
- break;
- case FF_SAW_DOWN:
- type_id = PID_SAW_DOWN;
- break;
- default:
- printk(KERN_ERR
- "hid-pidff: invalid waveform\n");
- return -EINVAL;
- }
-
- error = pidff_request_effect_upload(pidff,
- pidff->type_id[type_id]);
- if (error)
- return error;
- }
- if (!old || pidff_needs_set_effect(effect, old))
- pidff_set_effect_report(pidff, effect);
- if (!old || pidff_needs_set_periodic(effect, old))
- pidff_set_periodic_report(pidff, effect);
- if (!old ||
- pidff_needs_set_envelope(&effect->u.periodic.envelope,
- &old->u.periodic.envelope))
- pidff_set_envelope_report(pidff,
- &effect->u.periodic.envelope);
- break;
-
- case FF_RAMP:
- if (!old) {
- error = pidff_request_effect_upload(pidff,
- pidff->type_id[PID_RAMP]);
- if (error)
- return error;
- }
- if (!old || pidff_needs_set_effect(effect, old))
- pidff_set_effect_report(pidff, effect);
- if (!old || pidff_needs_set_ramp(effect, old))
- pidff_set_ramp_force_report(pidff, effect);
- if (!old ||
- pidff_needs_set_envelope(&effect->u.ramp.envelope,
- &old->u.ramp.envelope))
- pidff_set_envelope_report(pidff,
- &effect->u.ramp.envelope);
- break;
-
- case FF_SPRING:
- if (!old) {
- error = pidff_request_effect_upload(pidff,
- pidff->type_id[PID_SPRING]);
- if (error)
- return error;
- }
- if (!old || pidff_needs_set_effect(effect, old))
- pidff_set_effect_report(pidff, effect);
- if (!old || pidff_needs_set_condition(effect, old))
- pidff_set_condition_report(pidff, effect);
- break;
-
- case FF_FRICTION:
- if (!old) {
- error = pidff_request_effect_upload(pidff,
- pidff->type_id[PID_FRICTION]);
- if (error)
- return error;
- }
- if (!old || pidff_needs_set_effect(effect, old))
- pidff_set_effect_report(pidff, effect);
- if (!old || pidff_needs_set_condition(effect, old))
- pidff_set_condition_report(pidff, effect);
- break;
-
- case FF_DAMPER:
- if (!old) {
- error = pidff_request_effect_upload(pidff,
- pidff->type_id[PID_DAMPER]);
- if (error)
- return error;
- }
- if (!old || pidff_needs_set_effect(effect, old))
- pidff_set_effect_report(pidff, effect);
- if (!old || pidff_needs_set_condition(effect, old))
- pidff_set_condition_report(pidff, effect);
- break;
-
- case FF_INERTIA:
- if (!old) {
- error = pidff_request_effect_upload(pidff,
- pidff->type_id[PID_INERTIA]);
- if (error)
- return error;
- }
- if (!old || pidff_needs_set_effect(effect, old))
- pidff_set_effect_report(pidff, effect);
- if (!old || pidff_needs_set_condition(effect, old))
- pidff_set_condition_report(pidff, effect);
- break;
-
- default:
- printk(KERN_ERR "hid-pidff: invalid type\n");
- return -EINVAL;
- }
-
- if (!old)
- pidff->pid_id[effect->id] =
- pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
-
- debug("uploaded");
-
- return 0;
-}
-
-/*
- * set_gain() handler
- */
-static void pidff_set_gain(struct input_dev *dev, u16 gain)
-{
- struct pidff_device *pidff = dev->ff->private;
-
- pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], gain);
- usbhid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN],
- USB_DIR_OUT);
-}
-
-static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude)
-{
- struct hid_field *field =
- pidff->block_load[PID_EFFECT_BLOCK_INDEX].field;
-
- if (!magnitude) {
- pidff_playback_pid(pidff, field->logical_minimum, 0);
- return;
- }
-
- pidff_playback_pid(pidff, field->logical_minimum, 1);
-
- pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] =
- pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum;
- pidff->set_effect_type->value[0] = pidff->type_id[PID_SPRING];
- pidff->set_effect[PID_DURATION].value[0] = 0;
- pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = 0;
- pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = 0;
- pidff_set(&pidff->set_effect[PID_GAIN], magnitude);
- pidff->set_effect[PID_START_DELAY].value[0] = 0;
-
- usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT],
- USB_DIR_OUT);
-}
-
-/*
- * pidff_set_autocenter() handler
- */
-static void pidff_set_autocenter(struct input_dev *dev, u16 magnitude)
-{
- struct pidff_device *pidff = dev->ff->private;
-
- pidff_autocenter(pidff, magnitude);
-}
-
-/*
- * Find fields from a report and fill a pidff_usage
- */
-static int pidff_find_fields(struct pidff_usage *usage, const u8 *table,
- struct hid_report *report, int count, int strict)
-{
- int i, j, k, found;
-
- for (k = 0; k < count; k++) {
- found = 0;
- for (i = 0; i < report->maxfield; i++) {
- if (report->field[i]->maxusage !=
- report->field[i]->report_count) {
- debug("maxusage and report_count do not match, "
- "skipping");
- continue;
- }
- for (j = 0; j < report->field[i]->maxusage; j++) {
- if (report->field[i]->usage[j].hid ==
- (HID_UP_PID | table[k])) {
- debug("found %d at %d->%d", k, i, j);
- usage[k].field = report->field[i];
- usage[k].value =
- &report->field[i]->value[j];
- found = 1;
- break;
- }
- }
- if (found)
- break;
- }
- if (!found && strict) {
- debug("failed to locate %d", k);
- return -1;
- }
- }
- return 0;
-}
-
-/*
- * Return index into pidff_reports for the given usage
- */
-static int pidff_check_usage(int usage)
-{
- int i;
-
- for (i = 0; i < sizeof(pidff_reports); i++)
- if (usage == (HID_UP_PID | pidff_reports[i]))
- return i;
-
- return -1;
-}
-
-/*
- * Find the reports and fill pidff->reports[]
- * report_type specifies either OUTPUT or FEATURE reports
- */
-static void pidff_find_reports(struct hid_device *hid, int report_type,
- struct pidff_device *pidff)
-{
- struct hid_report *report;
- int i, ret;
-
- list_for_each_entry(report,
- &hid->report_enum[report_type].report_list, list) {
- if (report->maxfield < 1)
- continue;
- ret = pidff_check_usage(report->field[0]->logical);
- if (ret != -1) {
- debug("found usage 0x%02x from field->logical",
- pidff_reports[ret]);
- pidff->reports[ret] = report;
- continue;
- }
-
- /*
- * Sometimes logical collections are stacked to indicate
- * different usages for the report and the field, in which
- * case we want the usage of the parent. However, Linux HID
- * implementation hides this fact, so we have to dig it up
- * ourselves
- */
- i = report->field[0]->usage[0].collection_index;
- if (i <= 0 ||
- hid->collection[i - 1].type != HID_COLLECTION_LOGICAL)
- continue;
- ret = pidff_check_usage(hid->collection[i - 1].usage);
- if (ret != -1 && !pidff->reports[ret]) {
- debug("found usage 0x%02x from collection array",
- pidff_reports[ret]);
- pidff->reports[ret] = report;
- }
- }
-}
-
-/*
- * Test if the required reports have been found
- */
-static int pidff_reports_ok(struct pidff_device *pidff)
-{
- int i;
-
- for (i = 0; i <= PID_REQUIRED_REPORTS; i++) {
- if (!pidff->reports[i]) {
- debug("%d missing", i);
- return 0;
- }
- }
-
- return 1;
-}
-
-/*
- * Find a field with a specific usage within a report
- */
-static struct hid_field *pidff_find_special_field(struct hid_report *report,
- int usage, int enforce_min)
-{
- int i;
-
- for (i = 0; i < report->maxfield; i++) {
- if (report->field[i]->logical == (HID_UP_PID | usage) &&
- report->field[i]->report_count > 0) {
- if (!enforce_min ||
- report->field[i]->logical_minimum == 1)
- return report->field[i];
- else {
- printk(KERN_ERR "hid-pidff: logical_minimum "
- "is not 1 as it should be\n");
- return NULL;
- }
- }
- }
- return NULL;
-}
-
-/*
- * Fill a pidff->*_id struct table
- */
-static int pidff_find_special_keys(int *keys, struct hid_field *fld,
- const u8 *usagetable, int count)
-{
-
- int i, j;
- int found = 0;
-
- for (i = 0; i < count; i++) {
- for (j = 0; j < fld->maxusage; j++) {
- if (fld->usage[j].hid == (HID_UP_PID | usagetable[i])) {
- keys[i] = j + 1;
- found++;
- break;
- }
- }
- }
- return found;
-}
-
-#define PIDFF_FIND_SPECIAL_KEYS(keys, field, name) \
- pidff_find_special_keys(pidff->keys, pidff->field, pidff_ ## name, \
- sizeof(pidff_ ## name))
-
-/*
- * Find and check the special fields
- */
-static int pidff_find_special_fields(struct pidff_device *pidff)
-{
- debug("finding special fields");
-
- pidff->create_new_effect_type =
- pidff_find_special_field(pidff->reports[PID_CREATE_NEW_EFFECT],
- 0x25, 1);
- pidff->set_effect_type =
- pidff_find_special_field(pidff->reports[PID_SET_EFFECT],
- 0x25, 1);
- pidff->effect_direction =
- pidff_find_special_field(pidff->reports[PID_SET_EFFECT],
- 0x57, 0);
- pidff->device_control =
- pidff_find_special_field(pidff->reports[PID_DEVICE_CONTROL],
- 0x96, 1);
- pidff->block_load_status =
- pidff_find_special_field(pidff->reports[PID_BLOCK_LOAD],
- 0x8b, 1);
- pidff->effect_operation_status =
- pidff_find_special_field(pidff->reports[PID_EFFECT_OPERATION],
- 0x78, 1);
-
- debug("search done");
-
- if (!pidff->create_new_effect_type || !pidff->set_effect_type) {
- printk(KERN_ERR "hid-pidff: effect lists not found\n");
- return -1;
- }
-
- if (!pidff->effect_direction) {
- printk(KERN_ERR "hid-pidff: direction field not found\n");
- return -1;
- }
-
- if (!pidff->device_control) {
- printk(KERN_ERR "hid-pidff: device control field not found\n");
- return -1;
- }
-
- if (!pidff->block_load_status) {
- printk(KERN_ERR
- "hid-pidff: block load status field not found\n");
- return -1;
- }
-
- if (!pidff->effect_operation_status) {
- printk(KERN_ERR
- "hid-pidff: effect operation field not found\n");
- return -1;
- }
-
- pidff_find_special_keys(pidff->control_id, pidff->device_control,
- pidff_device_control,
- sizeof(pidff_device_control));
-
- PIDFF_FIND_SPECIAL_KEYS(control_id, device_control, device_control);
-
- if (!PIDFF_FIND_SPECIAL_KEYS(type_id, create_new_effect_type,
- effect_types)) {
- printk(KERN_ERR "hid-pidff: no effect types found\n");
- return -1;
- }
-
- if (PIDFF_FIND_SPECIAL_KEYS(status_id, block_load_status,
- block_load_status) !=
- sizeof(pidff_block_load_status)) {
- printk(KERN_ERR
- "hidpidff: block load status identifiers not found\n");
- return -1;
- }
-
- if (PIDFF_FIND_SPECIAL_KEYS(operation_id, effect_operation_status,
- effect_operation_status) !=
- sizeof(pidff_effect_operation_status)) {
- printk(KERN_ERR
- "hidpidff: effect operation identifiers not found\n");
- return -1;
- }
-
- return 0;
-}
-
-/**
- * Find the implemented effect types
- */
-static int pidff_find_effects(struct pidff_device *pidff,
- struct input_dev *dev)
-{
- int i;
-
- for (i = 0; i < sizeof(pidff_effect_types); i++) {
- int pidff_type = pidff->type_id[i];
- if (pidff->set_effect_type->usage[pidff_type].hid !=
- pidff->create_new_effect_type->usage[pidff_type].hid) {
- printk(KERN_ERR "hid-pidff: "
- "effect type number %d is invalid\n", i);
- return -1;
- }
- }
-
- if (pidff->type_id[PID_CONSTANT])
- set_bit(FF_CONSTANT, dev->ffbit);
- if (pidff->type_id[PID_RAMP])
- set_bit(FF_RAMP, dev->ffbit);
- if (pidff->type_id[PID_SQUARE]) {
- set_bit(FF_SQUARE, dev->ffbit);
- set_bit(FF_PERIODIC, dev->ffbit);
- }
- if (pidff->type_id[PID_SINE]) {
- set_bit(FF_SINE, dev->ffbit);
- set_bit(FF_PERIODIC, dev->ffbit);
- }
- if (pidff->type_id[PID_TRIANGLE]) {
- set_bit(FF_TRIANGLE, dev->ffbit);
- set_bit(FF_PERIODIC, dev->ffbit);
- }
- if (pidff->type_id[PID_SAW_UP]) {
- set_bit(FF_SAW_UP, dev->ffbit);
- set_bit(FF_PERIODIC, dev->ffbit);
- }
- if (pidff->type_id[PID_SAW_DOWN]) {
- set_bit(FF_SAW_DOWN, dev->ffbit);
- set_bit(FF_PERIODIC, dev->ffbit);
- }
- if (pidff->type_id[PID_SPRING])
- set_bit(FF_SPRING, dev->ffbit);
- if (pidff->type_id[PID_DAMPER])
- set_bit(FF_DAMPER, dev->ffbit);
- if (pidff->type_id[PID_INERTIA])
- set_bit(FF_INERTIA, dev->ffbit);
- if (pidff->type_id[PID_FRICTION])
- set_bit(FF_FRICTION, dev->ffbit);
-
- return 0;
-
-}
-
-#define PIDFF_FIND_FIELDS(name, report, strict) \
- pidff_find_fields(pidff->name, pidff_ ## name, \
- pidff->reports[report], \
- sizeof(pidff_ ## name), strict)
-
-/*
- * Fill and check the pidff_usages
- */
-static int pidff_init_fields(struct pidff_device *pidff, struct input_dev *dev)
-{
- int envelope_ok = 0;
-
- if (PIDFF_FIND_FIELDS(set_effect, PID_SET_EFFECT, 1)) {
- printk(KERN_ERR
- "hid-pidff: unknown set_effect report layout\n");
- return -ENODEV;
- }
-
- PIDFF_FIND_FIELDS(block_load, PID_BLOCK_LOAD, 0);
- if (!pidff->block_load[PID_EFFECT_BLOCK_INDEX].value) {
- printk(KERN_ERR
- "hid-pidff: unknown pid_block_load report layout\n");
- return -ENODEV;
- }
-
- if (PIDFF_FIND_FIELDS(effect_operation, PID_EFFECT_OPERATION, 1)) {
- printk(KERN_ERR
- "hid-pidff: unknown effect_operation report layout\n");
- return -ENODEV;
- }
-
- if (PIDFF_FIND_FIELDS(block_free, PID_BLOCK_FREE, 1)) {
- printk(KERN_ERR
- "hid-pidff: unknown pid_block_free report layout\n");
- return -ENODEV;
- }
-
- if (!PIDFF_FIND_FIELDS(set_envelope, PID_SET_ENVELOPE, 1))
- envelope_ok = 1;
-
- if (pidff_find_special_fields(pidff) || pidff_find_effects(pidff, dev))
- return -ENODEV;
-
- if (!envelope_ok) {
- if (test_and_clear_bit(FF_CONSTANT, dev->ffbit))
- printk(KERN_WARNING "hid-pidff: "
- "has constant effect but no envelope\n");
- if (test_and_clear_bit(FF_RAMP, dev->ffbit))
- printk(KERN_WARNING "hid-pidff: "
- "has ramp effect but no envelope\n");
-
- if (test_and_clear_bit(FF_PERIODIC, dev->ffbit))
- printk(KERN_WARNING "hid-pidff: "
- "has periodic effect but no envelope\n");
- }
-
- if (test_bit(FF_CONSTANT, dev->ffbit) &&
- PIDFF_FIND_FIELDS(set_constant, PID_SET_CONSTANT, 1)) {
- printk(KERN_WARNING
- "hid-pidff: unknown constant effect layout\n");
- clear_bit(FF_CONSTANT, dev->ffbit);
- }
-
- if (test_bit(FF_RAMP, dev->ffbit) &&
- PIDFF_FIND_FIELDS(set_ramp, PID_SET_RAMP, 1)) {
- printk(KERN_WARNING "hid-pidff: unknown ramp effect layout\n");
- clear_bit(FF_RAMP, dev->ffbit);
- }
-
- if ((test_bit(FF_SPRING, dev->ffbit) ||
- test_bit(FF_DAMPER, dev->ffbit) ||
- test_bit(FF_FRICTION, dev->ffbit) ||
- test_bit(FF_INERTIA, dev->ffbit)) &&
- PIDFF_FIND_FIELDS(set_condition, PID_SET_CONDITION, 1)) {
- printk(KERN_WARNING
- "hid-pidff: unknown condition effect layout\n");
- clear_bit(FF_SPRING, dev->ffbit);
- clear_bit(FF_DAMPER, dev->ffbit);
- clear_bit(FF_FRICTION, dev->ffbit);
- clear_bit(FF_INERTIA, dev->ffbit);
- }
-
- if (test_bit(FF_PERIODIC, dev->ffbit) &&
- PIDFF_FIND_FIELDS(set_periodic, PID_SET_PERIODIC, 1)) {
- printk(KERN_WARNING
- "hid-pidff: unknown periodic effect layout\n");
- clear_bit(FF_PERIODIC, dev->ffbit);
- }
-
- PIDFF_FIND_FIELDS(pool, PID_POOL, 0);
-
- if (!PIDFF_FIND_FIELDS(device_gain, PID_DEVICE_GAIN, 1))
- set_bit(FF_GAIN, dev->ffbit);
-
- return 0;
-}
-
-/*
- * Reset the device
- */
-static void pidff_reset(struct pidff_device *pidff)
-{
- struct hid_device *hid = pidff->hid;
- int i = 0;
-
- pidff->device_control->value[0] = pidff->control_id[PID_RESET];
- /* We reset twice as sometimes hid_wait_io isn't waiting long enough */
- usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT);
- usbhid_wait_io(hid);
- usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT);
- usbhid_wait_io(hid);
-
- pidff->device_control->value[0] =
- pidff->control_id[PID_ENABLE_ACTUATORS];
- usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT);
- usbhid_wait_io(hid);
-
- /* pool report is sometimes messed up, refetch it */
- usbhid_submit_report(hid, pidff->reports[PID_POOL], USB_DIR_IN);
- usbhid_wait_io(hid);
-
- if (pidff->pool[PID_SIMULTANEOUS_MAX].value) {
- int sim_effects = pidff->pool[PID_SIMULTANEOUS_MAX].value[0];
- while (sim_effects < 2) {
- if (i++ > 20) {
- printk(KERN_WARNING "hid-pidff: device reports "
- "%d simultaneous effects\n",
- sim_effects);
- break;
- }
- debug("pid_pool requested again");
- usbhid_submit_report(hid, pidff->reports[PID_POOL],
- USB_DIR_IN);
- usbhid_wait_io(hid);
- }
- }
-}
-
-/*
- * Test if autocenter modification is using the supported method
- */
-static int pidff_check_autocenter(struct pidff_device *pidff,
- struct input_dev *dev)
-{
- int error;
-
- /*
- * Let's find out if autocenter modification is supported
- * Specification doesn't specify anything, so we request an
- * effect upload and cancel it immediately. If the approved
- * effect id was one above the minimum, then we assume the first
- * effect id is a built-in spring type effect used for autocenter
- */
-
- error = pidff_request_effect_upload(pidff, 1);
- if (error) {
- printk(KERN_ERR "hid-pidff: upload request failed\n");
- return error;
- }
-
- if (pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] ==
- pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + 1) {
- pidff_autocenter(pidff, 0xffff);
- set_bit(FF_AUTOCENTER, dev->ffbit);
- } else {
- printk(KERN_NOTICE "hid-pidff: "
- "device has unknown autocenter control method\n");
- }
-
- pidff_erase_pid(pidff,
- pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]);
-
- return 0;
-
-}
-
-/*
- * Check if the device is PID and initialize it
- */
-int hid_pidff_init(struct hid_device *hid)
-{
- struct pidff_device *pidff;
- struct hid_input *hidinput = list_entry(hid->inputs.next,
- struct hid_input, list);
- struct input_dev *dev = hidinput->input;
- struct ff_device *ff;
- int max_effects;
- int error;
-
- debug("starting pid init");
-
- if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) {
- debug("not a PID device, no output report");
- return -ENODEV;
- }
-
- pidff = kzalloc(sizeof(*pidff), GFP_KERNEL);
- if (!pidff)
- return -ENOMEM;
-
- pidff->hid = hid;
-
- pidff_find_reports(hid, HID_OUTPUT_REPORT, pidff);
- pidff_find_reports(hid, HID_FEATURE_REPORT, pidff);
-
- if (!pidff_reports_ok(pidff)) {
- debug("reports not ok, aborting");
- error = -ENODEV;
- goto fail;
- }
-
- error = pidff_init_fields(pidff, dev);
- if (error)
- goto fail;
-
- pidff_reset(pidff);
-
- if (test_bit(FF_GAIN, dev->ffbit)) {
- pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], 0xffff);
- usbhid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN],
- USB_DIR_OUT);
- }
-
- error = pidff_check_autocenter(pidff, dev);
- if (error)
- goto fail;
-
- max_effects =
- pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_maximum -
- pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum +
- 1;
- debug("max effects is %d", max_effects);
-
- if (max_effects > PID_EFFECTS_MAX)
- max_effects = PID_EFFECTS_MAX;
-
- if (pidff->pool[PID_SIMULTANEOUS_MAX].value)
- debug("max simultaneous effects is %d",
- pidff->pool[PID_SIMULTANEOUS_MAX].value[0]);
-
- if (pidff->pool[PID_RAM_POOL_SIZE].value)
- debug("device memory size is %d bytes",
- pidff->pool[PID_RAM_POOL_SIZE].value[0]);
-
- if (pidff->pool[PID_DEVICE_MANAGED_POOL].value &&
- pidff->pool[PID_DEVICE_MANAGED_POOL].value[0] == 0) {
- printk(KERN_NOTICE "hid-pidff: "
- "device does not support device managed pool\n");
- goto fail;
- }
-
- error = input_ff_create(dev, max_effects);
- if (error)
- goto fail;
-
- ff = dev->ff;
- ff->private = pidff;
- ff->upload = pidff_upload_effect;
- ff->erase = pidff_erase_effect;
- ff->set_gain = pidff_set_gain;
- ff->set_autocenter = pidff_set_autocenter;
- ff->playback = pidff_playback;
-
- printk(KERN_INFO "Force feedback for USB HID PID devices by "
- "Anssi Hannula <anssi.hannula@gmail.com>\n");
-
- return 0;
-
- fail:
- kfree(pidff);
- return error;
-}
diff --git a/drivers/usb/input/hid-plff.c b/drivers/usb/input/hid-plff.c
deleted file mode 100644
index 76d2e6e14db..00000000000
--- a/drivers/usb/input/hid-plff.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Force feedback support for PantherLord USB/PS2 2in1 Adapter devices
- *
- * Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.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.
- *
- * 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
- */
-
-
-/* #define DEBUG */
-
-#define debug(format, arg...) pr_debug("hid-plff: " format "\n" , ## arg)
-
-#include <linux/input.h>
-#include <linux/usb.h>
-#include <linux/hid.h>
-#include "usbhid.h"
-
-struct plff_device {
- struct hid_report *report;
-};
-
-static int hid_plff_play(struct input_dev *dev, void *data,
- struct ff_effect *effect)
-{
- struct hid_device *hid = dev->private;
- struct plff_device *plff = data;
- int left, right;
-
- left = effect->u.rumble.strong_magnitude;
- right = effect->u.rumble.weak_magnitude;
- debug("called with 0x%04x 0x%04x", left, right);
-
- left = left * 0x7f / 0xffff;
- right = right * 0x7f / 0xffff;
-
- plff->report->field[0]->value[2] = left;
- plff->report->field[0]->value[3] = right;
- debug("running with 0x%02x 0x%02x", left, right);
- usbhid_submit_report(hid, plff->report, USB_DIR_OUT);
-
- return 0;
-}
-
-int hid_plff_init(struct hid_device *hid)
-{
- struct plff_device *plff;
- struct hid_report *report;
- struct hid_input *hidinput;
- struct list_head *report_list =
- &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct list_head *report_ptr = report_list;
- struct input_dev *dev;
- int error;
-
- /* The device contains 2 output reports (one for each
- HID_QUIRK_MULTI_INPUT device), both containing 1 field, which
- contains 4 ff00.0002 usages and 4 16bit absolute values.
-
- The 2 input reports also contain a field which contains
- 8 ff00.0001 usages and 8 boolean values. Their meaning is
- currently unknown. */
-
- if (list_empty(report_list)) {
- printk(KERN_ERR "hid-plff: no output reports found\n");
- return -ENODEV;
- }
-
- list_for_each_entry(hidinput, &hid->inputs, list) {
-
- report_ptr = report_ptr->next;
-
- if (report_ptr == report_list) {
- printk(KERN_ERR "hid-plff: required output report is missing\n");
- return -ENODEV;
- }
-
- report = list_entry(report_ptr, struct hid_report, list);
- if (report->maxfield < 1) {
- printk(KERN_ERR "hid-plff: no fields in the report\n");
- return -ENODEV;
- }
-
- if (report->field[0]->report_count < 4) {
- printk(KERN_ERR "hid-plff: not enough values in the field\n");
- return -ENODEV;
- }
-
- plff = kzalloc(sizeof(struct plff_device), GFP_KERNEL);
- if (!plff)
- return -ENOMEM;
-
- dev = hidinput->input;
-
- set_bit(FF_RUMBLE, dev->ffbit);
-
- error = input_ff_create_memless(dev, plff, hid_plff_play);
- if (error) {
- kfree(plff);
- return error;
- }
-
- plff->report = report;
- plff->report->field[0]->value[0] = 0x00;
- plff->report->field[0]->value[1] = 0x00;
- plff->report->field[0]->value[2] = 0x00;
- plff->report->field[0]->value[3] = 0x00;
- usbhid_submit_report(hid, plff->report, USB_DIR_OUT);
- }
-
- printk(KERN_INFO "hid-plff: Force feedback for PantherLord USB/PS2 "
- "2in1 Adapters by Anssi Hannula <anssi.hannula@gmail.com>\n");
-
- return 0;
-}
diff --git a/drivers/usb/input/hid-tmff.c b/drivers/usb/input/hid-tmff.c
deleted file mode 100644
index ab67331620d..00000000000
--- a/drivers/usb/input/hid-tmff.c
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Force feedback support for various HID compliant devices by ThrustMaster:
- * ThrustMaster FireStorm Dual Power 2
- * and possibly others whose device ids haven't been added.
- *
- * Modified to support ThrustMaster devices by Zinx Verituse
- * on 2003-01-25 from the Logitech force feedback driver,
- * which is by Johann Deneux.
- *
- * Copyright (c) 2003 Zinx Verituse <zinx@epicsol.org>
- * Copyright (c) 2002 Johann Deneux
- */
-
-/*
- * 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/input.h>
-
-#undef DEBUG
-#include <linux/usb.h>
-
-#include <linux/hid.h>
-#include "usbhid.h"
-
-/* Usages for thrustmaster devices I know about */
-#define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb)
-
-
-struct tmff_device {
- struct hid_report *report;
- struct hid_field *rumble;
-};
-
-/* Changes values from 0 to 0xffff into values from minimum to maximum */
-static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum)
-{
- int ret;
-
- ret = (in * (maximum - minimum) / 0xffff) + minimum;
- if (ret < minimum)
- return minimum;
- if (ret > maximum)
- return maximum;
- return ret;
-}
-
-static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
-{
- struct hid_device *hid = dev->private;
- struct tmff_device *tmff = data;
- int left, right; /* Rumbling */
-
- left = hid_tmff_scale(effect->u.rumble.weak_magnitude,
- tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
- right = hid_tmff_scale(effect->u.rumble.strong_magnitude,
- tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
-
- tmff->rumble->value[0] = left;
- tmff->rumble->value[1] = right;
- dbg("(left,right)=(%08x, %08x)", left, right);
- usbhid_submit_report(hid, tmff->report, USB_DIR_OUT);
-
- return 0;
-}
-
-int hid_tmff_init(struct hid_device *hid)
-{
- struct tmff_device *tmff;
- struct list_head *pos;
- struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- struct input_dev *input_dev = hidinput->input;
- int error;
-
- tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
- if (!tmff)
- return -ENOMEM;
-
- /* Find the report to use */
- __list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) {
- struct hid_report *report = (struct hid_report *)pos;
- int fieldnum;
-
- for (fieldnum = 0; fieldnum < report->maxfield; ++fieldnum) {
- struct hid_field *field = report->field[fieldnum];
-
- if (field->maxusage <= 0)
- continue;
-
- switch (field->usage[0].hid) {
- case THRUSTMASTER_USAGE_RUMBLE_LR:
- if (field->report_count < 2) {
- warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with report_count < 2");
- continue;
- }
-
- if (field->logical_maximum == field->logical_minimum) {
- warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with logical_maximum == logical_minimum");
- continue;
- }
-
- if (tmff->report && tmff->report != report) {
- warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report");
- continue;
- }
-
- if (tmff->rumble && tmff->rumble != field) {
- warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR");
- continue;
- }
-
- tmff->report = report;
- tmff->rumble = field;
-
- set_bit(FF_RUMBLE, input_dev->ffbit);
- break;
-
- default:
- warn("ignoring unknown output usage %08x", field->usage[0].hid);
- continue;
- }
- }
- }
-
- error = input_ff_create_memless(input_dev, tmff, hid_tmff_play);
- if (error) {
- kfree(tmff);
- return error;
- }
-
- info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse <zinx@epicsol.org>");
-
- return 0;
-}
-
diff --git a/drivers/usb/input/hid-zpff.c b/drivers/usb/input/hid-zpff.c
deleted file mode 100644
index 7bd8238ca21..00000000000
--- a/drivers/usb/input/hid-zpff.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Force feedback support for Zeroplus based devices
- *
- * Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.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.
- *
- * 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
- */
-
-
-/* #define DEBUG */
-
-#define debug(format, arg...) pr_debug("hid-zpff: " format "\n" , ## arg)
-
-#include <linux/input.h>
-#include <linux/usb.h>
-#include <linux/hid.h>
-#include "usbhid.h"
-
-struct zpff_device {
- struct hid_report *report;
-};
-
-static int hid_zpff_play(struct input_dev *dev, void *data,
- struct ff_effect *effect)
-{
- struct hid_device *hid = dev->private;
- struct zpff_device *zpff = data;
- int left, right;
-
- /*
- * The following is specified the other way around in the Zeroplus
- * datasheet but the order below is correct for the XFX Executioner;
- * however it is possible that the XFX Executioner is an exception
- */
-
- left = effect->u.rumble.strong_magnitude;
- right = effect->u.rumble.weak_magnitude;
- debug("called with 0x%04x 0x%04x", left, right);
-
- left = left * 0x7f / 0xffff;
- right = right * 0x7f / 0xffff;
-
- zpff->report->field[2]->value[0] = left;
- zpff->report->field[3]->value[0] = right;
- debug("running with 0x%02x 0x%02x", left, right);
- usbhid_submit_report(hid, zpff->report, USB_DIR_OUT);
-
- return 0;
-}
-
-int hid_zpff_init(struct hid_device *hid)
-{
- struct zpff_device *zpff;
- struct hid_report *report;
- struct hid_input *hidinput = list_entry(hid->inputs.next,
- struct hid_input, list);
- struct list_head *report_list =
- &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct input_dev *dev = hidinput->input;
- int error;
-
- if (list_empty(report_list)) {
- printk(KERN_ERR "hid-zpff: no output report found\n");
- return -ENODEV;
- }
-
- report = list_entry(report_list->next, struct hid_report, list);
-
- if (report->maxfield < 4) {
- printk(KERN_ERR "hid-zpff: not enough fields in report\n");
- return -ENODEV;
- }
-
- zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL);
- if (!zpff)
- return -ENOMEM;
-
- set_bit(FF_RUMBLE, dev->ffbit);
-
- error = input_ff_create_memless(dev, zpff, hid_zpff_play);
- if (error) {
- kfree(zpff);
- return error;
- }
-
- zpff->report = report;
- zpff->report->field[0]->value[0] = 0x00;
- zpff->report->field[1]->value[0] = 0x02;
- zpff->report->field[2]->value[0] = 0x00;
- zpff->report->field[3]->value[0] = 0x00;
- usbhid_submit_report(hid, zpff->report, USB_DIR_OUT);
-
- printk(KERN_INFO "Force feedback for Zeroplus based devices by "
- "Anssi Hannula <anssi.hannula@gmail.com>\n");
-
- return 0;
-}
diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c
deleted file mode 100644
index a8b3d66cd49..00000000000
--- a/drivers/usb/input/hiddev.c
+++ /dev/null
@@ -1,847 +0,0 @@
-/*
- * Copyright (c) 2001 Paul Stewart
- * Copyright (c) 2001 Vojtech Pavlik
- *
- * HID char devices, giving access to raw HID device events.
- *
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to Paul Stewart <stewart@wetlogic.net>
- */
-
-#include <linux/poll.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/smp_lock.h>
-#include <linux/input.h>
-#include <linux/usb.h>
-#include <linux/hid.h>
-#include <linux/hiddev.h>
-#include "usbhid.h"
-
-#ifdef CONFIG_USB_DYNAMIC_MINORS
-#define HIDDEV_MINOR_BASE 0
-#define HIDDEV_MINORS 256
-#else
-#define HIDDEV_MINOR_BASE 96
-#define HIDDEV_MINORS 16
-#endif
-#define HIDDEV_BUFFER_SIZE 64
-
-struct hiddev {
- int exist;
- int open;
- wait_queue_head_t wait;
- struct hid_device *hid;
- struct list_head list;
-};
-
-struct hiddev_list {
- struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE];
- int head;
- int tail;
- unsigned flags;
- struct fasync_struct *fasync;
- struct hiddev *hiddev;
- struct list_head node;
-};
-
-static struct hiddev *hiddev_table[HIDDEV_MINORS];
-
-/*
- * Find a report, given the report's type and ID. The ID can be specified
- * indirectly by REPORT_ID_FIRST (which returns the first report of the given
- * type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the
- * given type which follows old_id.
- */
-static struct hid_report *
-hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo)
-{
- unsigned int flags = rinfo->report_id & ~HID_REPORT_ID_MASK;
- unsigned int rid = rinfo->report_id & HID_REPORT_ID_MASK;
- struct hid_report_enum *report_enum;
- struct hid_report *report;
- struct list_head *list;
-
- if (rinfo->report_type < HID_REPORT_TYPE_MIN ||
- rinfo->report_type > HID_REPORT_TYPE_MAX)
- return NULL;
-
- report_enum = hid->report_enum +
- (rinfo->report_type - HID_REPORT_TYPE_MIN);
-
- switch (flags) {
- case 0: /* Nothing to do -- report_id is already set correctly */
- break;
-
- case HID_REPORT_ID_FIRST:
- if (list_empty(&report_enum->report_list))
- return NULL;
-
- list = report_enum->report_list.next;
- report = list_entry(list, struct hid_report, list);
- rinfo->report_id = report->id;
- break;
-
- case HID_REPORT_ID_NEXT:
- report = report_enum->report_id_hash[rid];
- if (!report)
- return NULL;
-
- list = report->list.next;
- if (list == &report_enum->report_list)
- return NULL;
-
- report = list_entry(list, struct hid_report, list);
- rinfo->report_id = report->id;
- break;
-
- default:
- return NULL;
- }
-
- return report_enum->report_id_hash[rinfo->report_id];
-}
-
-/*
- * Perform an exhaustive search of the report table for a usage, given its
- * type and usage id.
- */
-static struct hid_field *
-hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref)
-{
- int i, j;
- struct hid_report *report;
- struct hid_report_enum *report_enum;
- struct hid_field *field;
-
- if (uref->report_type < HID_REPORT_TYPE_MIN ||
- uref->report_type > HID_REPORT_TYPE_MAX)
- return NULL;
-
- report_enum = hid->report_enum +
- (uref->report_type - HID_REPORT_TYPE_MIN);
-
- list_for_each_entry(report, &report_enum->report_list, list) {
- for (i = 0; i < report->maxfield; i++) {
- field = report->field[i];
- for (j = 0; j < field->maxusage; j++) {
- if (field->usage[j].hid == uref->usage_code) {
- uref->report_id = report->id;
- uref->field_index = i;
- uref->usage_index = j;
- return field;
- }
- }
- }
- }
-
- return NULL;
-}
-
-static void hiddev_send_event(struct hid_device *hid,
- struct hiddev_usage_ref *uref)
-{
- struct hiddev *hiddev = hid->hiddev;
- struct hiddev_list *list;
-
- list_for_each_entry(list, &hiddev->list, node) {
- if (uref->field_index != HID_FIELD_INDEX_NONE ||
- (list->flags & HIDDEV_FLAG_REPORT) != 0) {
- list->buffer[list->head] = *uref;
- list->head = (list->head + 1) &
- (HIDDEV_BUFFER_SIZE - 1);
- kill_fasync(&list->fasync, SIGIO, POLL_IN);
- }
- }
-
- wake_up_interruptible(&hiddev->wait);
-}
-
-/*
- * This is where hid.c calls into hiddev to pass an event that occurred over
- * the interrupt pipe
- */
-void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
- struct hid_usage *usage, __s32 value)
-{
- unsigned type = field->report_type;
- struct hiddev_usage_ref uref;
-
- uref.report_type =
- (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
- ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT :
- ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0));
- uref.report_id = field->report->id;
- uref.field_index = field->index;
- uref.usage_index = (usage - field->usage);
- uref.usage_code = usage->hid;
- uref.value = value;
-
- hiddev_send_event(hid, &uref);
-}
-EXPORT_SYMBOL_GPL(hiddev_hid_event);
-
-void hiddev_report_event(struct hid_device *hid, struct hid_report *report)
-{
- unsigned type = report->type;
- struct hiddev_usage_ref uref;
-
- memset(&uref, 0, sizeof(uref));
- uref.report_type =
- (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
- ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT :
- ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0));
- uref.report_id = report->id;
- uref.field_index = HID_FIELD_INDEX_NONE;
-
- hiddev_send_event(hid, &uref);
-}
-
-/*
- * fasync file op
- */
-static int hiddev_fasync(int fd, struct file *file, int on)
-{
- int retval;
- struct hiddev_list *list = file->private_data;
-
- retval = fasync_helper(fd, file, on, &list->fasync);
-
- return retval < 0 ? retval : 0;
-}
-
-
-/*
- * release file op
- */
-static int hiddev_release(struct inode * inode, struct file * file)
-{
- struct hiddev_list *list = file->private_data;
-
- hiddev_fasync(-1, file, 0);
- list_del(&list->node);
-
- if (!--list->hiddev->open) {
- if (list->hiddev->exist)
- usbhid_close(list->hiddev->hid);
- else
- kfree(list->hiddev);
- }
-
- kfree(list);
-
- return 0;
-}
-
-/*
- * open file op
- */
-static int hiddev_open(struct inode *inode, struct file *file)
-{
- struct hiddev_list *list;
-
- int i = iminor(inode) - HIDDEV_MINOR_BASE;
-
- if (i >= HIDDEV_MINORS || !hiddev_table[i])
- return -ENODEV;
-
- if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
- return -ENOMEM;
-
- list->hiddev = hiddev_table[i];
- list_add_tail(&list->node, &hiddev_table[i]->list);
- file->private_data = list;
-
- if (!list->hiddev->open++)
- if (list->hiddev->exist)
- usbhid_open(hiddev_table[i]->hid);
-
- return 0;
-}
-
-/*
- * "write" file op
- */
-static ssize_t hiddev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
-{
- return -EINVAL;
-}
-
-/*
- * "read" file op
- */
-static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
-{
- DECLARE_WAITQUEUE(wait, current);
- struct hiddev_list *list = file->private_data;
- int event_size;
- int retval = 0;
-
- event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ?
- sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event);
-
- if (count < event_size)
- return 0;
-
- while (retval == 0) {
- if (list->head == list->tail) {
- add_wait_queue(&list->hiddev->wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
-
- while (list->head == list->tail) {
- if (file->f_flags & O_NONBLOCK) {
- retval = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
- if (!list->hiddev->exist) {
- retval = -EIO;
- break;
- }
-
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
- }
-
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&list->hiddev->wait, &wait);
- }
-
- if (retval)
- return retval;
-
-
- while (list->head != list->tail &&
- retval + event_size <= count) {
- if ((list->flags & HIDDEV_FLAG_UREF) == 0) {
- if (list->buffer[list->tail].field_index !=
- HID_FIELD_INDEX_NONE) {
- struct hiddev_event event;
- event.hid = list->buffer[list->tail].usage_code;
- event.value = list->buffer[list->tail].value;
- if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event)))
- return -EFAULT;
- retval += sizeof(struct hiddev_event);
- }
- } else {
- if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE ||
- (list->flags & HIDDEV_FLAG_REPORT) != 0) {
- if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref)))
- return -EFAULT;
- retval += sizeof(struct hiddev_usage_ref);
- }
- }
- list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1);
- }
-
- }
-
- return retval;
-}
-
-/*
- * "poll" file op
- * No kernel lock - fine
- */
-static unsigned int hiddev_poll(struct file *file, poll_table *wait)
-{
- struct hiddev_list *list = file->private_data;
-
- poll_wait(file, &list->hiddev->wait, wait);
- if (list->head != list->tail)
- return POLLIN | POLLRDNORM;
- if (!list->hiddev->exist)
- return POLLERR | POLLHUP;
- return 0;
-}
-
-/*
- * "ioctl" file op
- */
-static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct hiddev_list *list = file->private_data;
- struct hiddev *hiddev = list->hiddev;
- struct hid_device *hid = hiddev->hid;
- struct usb_device *dev = hid_to_usb_dev(hid);
- struct hiddev_collection_info cinfo;
- struct hiddev_report_info rinfo;
- struct hiddev_field_info finfo;
- struct hiddev_usage_ref_multi *uref_multi = NULL;
- struct hiddev_usage_ref *uref;
- struct hiddev_devinfo dinfo;
- struct hid_report *report;
- struct hid_field *field;
- struct usbhid_device *usbhid = hid->driver_data;
- void __user *user_arg = (void __user *)arg;
- int i;
-
- if (!hiddev->exist)
- return -EIO;
-
- switch (cmd) {
-
- case HIDIOCGVERSION:
- return put_user(HID_VERSION, (int __user *)arg);
-
- case HIDIOCAPPLICATION:
- if (arg < 0 || arg >= hid->maxapplication)
- return -EINVAL;
-
- for (i = 0; i < hid->maxcollection; i++)
- if (hid->collection[i].type ==
- HID_COLLECTION_APPLICATION && arg-- == 0)
- break;
-
- if (i == hid->maxcollection)
- return -EINVAL;
-
- return hid->collection[i].usage;
-
- case HIDIOCGDEVINFO:
- dinfo.bustype = BUS_USB;
- dinfo.busnum = dev->bus->busnum;
- dinfo.devnum = dev->devnum;
- dinfo.ifnum = usbhid->ifnum;
- dinfo.vendor = le16_to_cpu(dev->descriptor.idVendor);
- dinfo.product = le16_to_cpu(dev->descriptor.idProduct);
- dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice);
- dinfo.num_applications = hid->maxapplication;
- if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))
- return -EFAULT;
-
- return 0;
-
- case HIDIOCGFLAG:
- if (put_user(list->flags, (int __user *)arg))
- return -EFAULT;
-
- return 0;
-
- case HIDIOCSFLAG:
- {
- int newflags;
- if (get_user(newflags, (int __user *)arg))
- return -EFAULT;
-
- if ((newflags & ~HIDDEV_FLAGS) != 0 ||
- ((newflags & HIDDEV_FLAG_REPORT) != 0 &&
- (newflags & HIDDEV_FLAG_UREF) == 0))
- return -EINVAL;
-
- list->flags = newflags;
-
- return 0;
- }
-
- case HIDIOCGSTRING:
- {
- int idx, len;
- char *buf;
-
- if (get_user(idx, (int __user *)arg))
- return -EFAULT;
-
- if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL)
- return -ENOMEM;
-
- if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) {
- kfree(buf);
- return -EINVAL;
- }
-
- if (copy_to_user(user_arg+sizeof(int), buf, len+1)) {
- kfree(buf);
- return -EFAULT;
- }
-
- kfree(buf);
-
- return len;
- }
-
- case HIDIOCINITREPORT:
- usbhid_init_reports(hid);
-
- return 0;
-
- case HIDIOCGREPORT:
- if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
- return -EFAULT;
-
- if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT)
- return -EINVAL;
-
- if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
- return -EINVAL;
-
- usbhid_submit_report(hid, report, USB_DIR_IN);
- usbhid_wait_io(hid);
-
- return 0;
-
- case HIDIOCSREPORT:
- if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
- return -EFAULT;
-
- if (rinfo.report_type == HID_REPORT_TYPE_INPUT)
- return -EINVAL;
-
- if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
- return -EINVAL;
-
- usbhid_submit_report(hid, report, USB_DIR_OUT);
- usbhid_wait_io(hid);
-
- return 0;
-
- case HIDIOCGREPORTINFO:
- if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
- return -EFAULT;
-
- if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
- return -EINVAL;
-
- rinfo.num_fields = report->maxfield;
-
- if (copy_to_user(user_arg, &rinfo, sizeof(rinfo)))
- return -EFAULT;
-
- return 0;
-
- case HIDIOCGFIELDINFO:
- if (copy_from_user(&finfo, user_arg, sizeof(finfo)))
- return -EFAULT;
- rinfo.report_type = finfo.report_type;
- rinfo.report_id = finfo.report_id;
- if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
- return -EINVAL;
-
- if (finfo.field_index >= report->maxfield)
- return -EINVAL;
-
- field = report->field[finfo.field_index];
- memset(&finfo, 0, sizeof(finfo));
- finfo.report_type = rinfo.report_type;
- finfo.report_id = rinfo.report_id;
- finfo.field_index = field->report_count - 1;
- finfo.maxusage = field->maxusage;
- finfo.flags = field->flags;
- finfo.physical = field->physical;
- finfo.logical = field->logical;
- finfo.application = field->application;
- finfo.logical_minimum = field->logical_minimum;
- finfo.logical_maximum = field->logical_maximum;
- finfo.physical_minimum = field->physical_minimum;
- finfo.physical_maximum = field->physical_maximum;
- finfo.unit_exponent = field->unit_exponent;
- finfo.unit = field->unit;
-
- if (copy_to_user(user_arg, &finfo, sizeof(finfo)))
- return -EFAULT;
-
- return 0;
-
- case HIDIOCGUCODE:
- uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
- if (!uref_multi)
- return -ENOMEM;
- uref = &uref_multi->uref;
- if (copy_from_user(uref, user_arg, sizeof(*uref)))
- goto fault;
-
- rinfo.report_type = uref->report_type;
- rinfo.report_id = uref->report_id;
- if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
- goto inval;
-
- if (uref->field_index >= report->maxfield)
- goto inval;
-
- field = report->field[uref->field_index];
- if (uref->usage_index >= field->maxusage)
- goto inval;
-
- uref->usage_code = field->usage[uref->usage_index].hid;
-
- if (copy_to_user(user_arg, uref, sizeof(*uref)))
- goto fault;
-
- kfree(uref_multi);
- return 0;
-
- case HIDIOCGUSAGE:
- case HIDIOCSUSAGE:
- case HIDIOCGUSAGES:
- case HIDIOCSUSAGES:
- case HIDIOCGCOLLECTIONINDEX:
- uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
- if (!uref_multi)
- return -ENOMEM;
- uref = &uref_multi->uref;
- if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
- if (copy_from_user(uref_multi, user_arg,
- sizeof(*uref_multi)))
- goto fault;
- } else {
- if (copy_from_user(uref, user_arg, sizeof(*uref)))
- goto fault;
- }
-
- if (cmd != HIDIOCGUSAGE &&
- cmd != HIDIOCGUSAGES &&
- uref->report_type == HID_REPORT_TYPE_INPUT)
- goto inval;
-
- if (uref->report_id == HID_REPORT_ID_UNKNOWN) {
- field = hiddev_lookup_usage(hid, uref);
- if (field == NULL)
- goto inval;
- } else {
- rinfo.report_type = uref->report_type;
- rinfo.report_id = uref->report_id;
- if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
- goto inval;
-
- if (uref->field_index >= report->maxfield)
- goto inval;
-
- field = report->field[uref->field_index];
-
- if (cmd == HIDIOCGCOLLECTIONINDEX) {
- if (uref->usage_index >= field->maxusage)
- goto inval;
- } else if (uref->usage_index >= field->report_count)
- goto inval;
-
- else if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) &&
- (uref_multi->num_values > HID_MAX_MULTI_USAGES ||
- uref->usage_index + uref_multi->num_values > field->report_count))
- goto inval;
- }
-
- switch (cmd) {
- case HIDIOCGUSAGE:
- uref->value = field->value[uref->usage_index];
- if (copy_to_user(user_arg, uref, sizeof(*uref)))
- goto fault;
- goto goodreturn;
-
- case HIDIOCSUSAGE:
- field->value[uref->usage_index] = uref->value;
- goto goodreturn;
-
- case HIDIOCGCOLLECTIONINDEX:
- kfree(uref_multi);
- return field->usage[uref->usage_index].collection_index;
- case HIDIOCGUSAGES:
- for (i = 0; i < uref_multi->num_values; i++)
- uref_multi->values[i] =
- field->value[uref->usage_index + i];
- if (copy_to_user(user_arg, uref_multi,
- sizeof(*uref_multi)))
- goto fault;
- goto goodreturn;
- case HIDIOCSUSAGES:
- for (i = 0; i < uref_multi->num_values; i++)
- field->value[uref->usage_index + i] =
- uref_multi->values[i];
- goto goodreturn;
- }
-
-goodreturn:
- kfree(uref_multi);
- return 0;
-fault:
- kfree(uref_multi);
- return -EFAULT;
-inval:
- kfree(uref_multi);
- return -EINVAL;
-
- case HIDIOCGCOLLECTIONINFO:
- if (copy_from_user(&cinfo, user_arg, sizeof(cinfo)))
- return -EFAULT;
-
- if (cinfo.index >= hid->maxcollection)
- return -EINVAL;
-
- cinfo.type = hid->collection[cinfo.index].type;
- cinfo.usage = hid->collection[cinfo.index].usage;
- cinfo.level = hid->collection[cinfo.index].level;
-
- if (copy_to_user(user_arg, &cinfo, sizeof(cinfo)))
- return -EFAULT;
- return 0;
-
- default:
-
- if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ)
- return -EINVAL;
-
- if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) {
- int len;
- if (!hid->name)
- return 0;
- len = strlen(hid->name) + 1;
- if (len > _IOC_SIZE(cmd))
- len = _IOC_SIZE(cmd);
- return copy_to_user(user_arg, hid->name, len) ?
- -EFAULT : len;
- }
-
- if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) {
- int len;
- if (!hid->phys)
- return 0;
- len = strlen(hid->phys) + 1;
- if (len > _IOC_SIZE(cmd))
- len = _IOC_SIZE(cmd);
- return copy_to_user(user_arg, hid->phys, len) ?
- -EFAULT : len;
- }
- }
- return -EINVAL;
-}
-
-static const struct file_operations hiddev_fops = {
- .owner = THIS_MODULE,
- .read = hiddev_read,
- .write = hiddev_write,
- .poll = hiddev_poll,
- .open = hiddev_open,
- .release = hiddev_release,
- .ioctl = hiddev_ioctl,
- .fasync = hiddev_fasync,
-};
-
-static struct usb_class_driver hiddev_class = {
- .name = "hiddev%d",
- .fops = &hiddev_fops,
- .minor_base = HIDDEV_MINOR_BASE,
-};
-
-/*
- * This is where hid.c calls us to connect a hid device to the hiddev driver
- */
-int hiddev_connect(struct hid_device *hid)
-{
- struct hiddev *hiddev;
- struct usbhid_device *usbhid = hid->driver_data;
- int i;
- int retval;
-
- for (i = 0; i < hid->maxcollection; i++)
- if (hid->collection[i].type ==
- HID_COLLECTION_APPLICATION &&
- !IS_INPUT_APPLICATION(hid->collection[i].usage))
- break;
-
- if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0)
- return -1;
-
- if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL)))
- return -1;
-
- retval = usb_register_dev(usbhid->intf, &hiddev_class);
- if (retval) {
- err("Not able to get a minor for this device.");
- kfree(hiddev);
- return -1;
- }
-
- init_waitqueue_head(&hiddev->wait);
- INIT_LIST_HEAD(&hiddev->list);
- hiddev->hid = hid;
- hiddev->exist = 1;
-
- hid->minor = usbhid->intf->minor;
- hid->hiddev = hiddev;
-
- hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;
-
- return 0;
-}
-
-/*
- * This is where hid.c calls us to disconnect a hiddev device from the
- * corresponding hid device (usually because the usb device has disconnected)
- */
-static struct usb_class_driver hiddev_class;
-void hiddev_disconnect(struct hid_device *hid)
-{
- struct hiddev *hiddev = hid->hiddev;
- struct usbhid_device *usbhid = hid->driver_data;
-
- hiddev->exist = 0;
-
- hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL;
- usb_deregister_dev(usbhid->intf, &hiddev_class);
-
- if (hiddev->open) {
- usbhid_close(hiddev->hid);
- wake_up_interruptible(&hiddev->wait);
- } else {
- kfree(hiddev);
- }
-}
-
-/* Currently this driver is a USB driver. It's not a conventional one in
- * the sense that it doesn't probe at the USB level. Instead it waits to
- * be connected by HID through the hiddev_connect / hiddev_disconnect
- * routines. The reason to register as a USB device is to gain part of the
- * minor number space from the USB major.
- *
- * In theory, should the HID code be generalized to more than one physical
- * medium (say, IEEE 1384), this driver will probably need to register its
- * own major number, and in doing so, no longer need to register with USB.
- * At that point the probe routine and hiddev_driver struct below will no
- * longer be useful.
- */
-
-
-/* We never attach in this manner, and rely on HID to connect us. This
- * is why there is no disconnect routine defined in the usb_driver either.
- */
-static int hiddev_usbd_probe(struct usb_interface *intf,
- const struct usb_device_id *hiddev_info)
-{
- return -ENODEV;
-}
-
-
-static /* const */ struct usb_driver hiddev_driver = {
- .name = "hiddev",
- .probe = hiddev_usbd_probe,
-};
-
-int __init hiddev_init(void)
-{
- return usb_register(&hiddev_driver);
-}
-
-void hiddev_exit(void)
-{
- usb_deregister(&hiddev_driver);
-}
diff --git a/drivers/usb/input/usbhid.h b/drivers/usb/input/usbhid.h
deleted file mode 100644
index 0023f96d429..00000000000
--- a/drivers/usb/input/usbhid.h
+++ /dev/null
@@ -1,87 +0,0 @@
-#ifndef __USBHID_H
-#define __USBHID_H
-
-/*
- * Copyright (c) 1999 Andreas Gal
- * Copyright (c) 2000-2001 Vojtech Pavlik
- * Copyright (c) 2006 Jiri Kosina
- */
-
-/*
- * 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/types.h>
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <linux/timer.h>
-#include <linux/workqueue.h>
-#include <linux/input.h>
-
-/* API provided by hid-core.c for USB HID drivers */
-int usbhid_wait_io(struct hid_device* hid);
-void usbhid_close(struct hid_device *hid);
-int usbhid_open(struct hid_device *hid);
-void usbhid_init_reports(struct hid_device *hid);
-void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir);
-
-/*
- * USB-specific HID struct, to be pointed to
- * from struct hid_device->driver_data
- */
-
-struct usbhid_device {
- struct hid_device *hid; /* pointer to corresponding HID dev */
-
- struct usb_interface *intf; /* USB interface */
- int ifnum; /* USB interface number */
-
- unsigned int bufsize; /* URB buffer size */
-
- struct urb *urbin; /* Input URB */
- char *inbuf; /* Input buffer */
- dma_addr_t inbuf_dma; /* Input buffer dma */
- spinlock_t inlock; /* Input fifo spinlock */
-
- struct urb *urbctrl; /* Control URB */
- struct usb_ctrlrequest *cr; /* Control request struct */
- dma_addr_t cr_dma; /* Control request struct dma */
- struct hid_control_fifo ctrl[HID_CONTROL_FIFO_SIZE]; /* Control fifo */
- unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */
- char *ctrlbuf; /* Control buffer */
- dma_addr_t ctrlbuf_dma; /* Control buffer dma */
- spinlock_t ctrllock; /* Control fifo spinlock */
-
- struct urb *urbout; /* Output URB */
- struct hid_report *out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */
- unsigned char outhead, outtail; /* Output pipe fifo head & tail */
- char *outbuf; /* Output buffer */
- dma_addr_t outbuf_dma; /* Output buffer dma */
- spinlock_t outlock; /* Output fifo spinlock */
-
- unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
- struct timer_list io_retry; /* Retry timer */
- unsigned long stop_retry; /* Time to give up, in jiffies */
- unsigned int retry_delay; /* Delay length in ms */
- struct work_struct reset_work; /* Task context for resets */
-
-};
-
-#define hid_to_usb_dev(hid_dev) \
- container_of(hid_dev->dev->parent, struct usb_device, dev)
-
-#endif
-
diff --git a/drivers/usb/input/usbkbd.c b/drivers/usb/input/usbkbd.c
deleted file mode 100644
index 8505824848f..00000000000
--- a/drivers/usb/input/usbkbd.c
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * $Id: usbkbd.c,v 1.27 2001/12/27 10:37:41 vojtech Exp $
- *
- * Copyright (c) 1999-2001 Vojtech Pavlik
- *
- * USB HIDBP Keyboard support
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
- * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/usb/input.h>
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION ""
-#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
-#define DRIVER_DESC "USB HID Boot Protocol keyboard driver"
-#define DRIVER_LICENSE "GPL"
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE(DRIVER_LICENSE);
-
-static unsigned char usb_kbd_keycode[256] = {
- 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
- 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
- 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
- 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
- 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
- 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
- 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
- 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
- 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
- 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
- 150,158,159,128,136,177,178,176,142,152,173,140
-};
-
-struct usb_kbd {
- struct input_dev *dev;
- struct usb_device *usbdev;
- unsigned char old[8];
- struct urb *irq, *led;
- unsigned char newleds;
- char name[128];
- char phys[64];
-
- unsigned char *new;
- struct usb_ctrlrequest *cr;
- unsigned char *leds;
- dma_addr_t cr_dma;
- dma_addr_t new_dma;
- dma_addr_t leds_dma;
-};
-
-static void usb_kbd_irq(struct urb *urb)
-{
- struct usb_kbd *kbd = urb->context;
- int i;
-
- switch (urb->status) {
- case 0: /* success */
- break;
- case -ECONNRESET: /* unlink */
- case -ENOENT:
- case -ESHUTDOWN:
- return;
- /* -EPIPE: should clear the halt */
- default: /* error */
- goto resubmit;
- }
-
- for (i = 0; i < 8; i++)
- input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
-
- for (i = 2; i < 8; i++) {
-
- if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
- if (usb_kbd_keycode[kbd->old[i]])
- input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
- else
- info("Unknown key (scancode %#x) released.", kbd->old[i]);
- }
-
- if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
- if (usb_kbd_keycode[kbd->new[i]])
- input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
- else
- info("Unknown key (scancode %#x) pressed.", kbd->new[i]);
- }
- }
-
- input_sync(kbd->dev);
-
- memcpy(kbd->old, kbd->new, 8);
-
-resubmit:
- i = usb_submit_urb (urb, GFP_ATOMIC);
- if (i)
- err ("can't resubmit intr, %s-%s/input0, status %d",
- kbd->usbdev->bus->bus_name,
- kbd->usbdev->devpath, i);
-}
-
-static int usb_kbd_event(struct input_dev *dev, unsigned int type,
- unsigned int code, int value)
-{
- struct usb_kbd *kbd = dev->private;
-
- if (type != EV_LED)
- return -1;
-
-
- kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
- (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |
- (!!test_bit(LED_NUML, dev->led));
-
- if (kbd->led->status == -EINPROGRESS)
- return 0;
-
- if (*(kbd->leds) == kbd->newleds)
- return 0;
-
- *(kbd->leds) = kbd->newleds;
- kbd->led->dev = kbd->usbdev;
- if (usb_submit_urb(kbd->led, GFP_ATOMIC))
- err("usb_submit_urb(leds) failed");
-
- return 0;
-}
-
-static void usb_kbd_led(struct urb *urb)
-{
- struct usb_kbd *kbd = urb->context;
-
- if (urb->status)
- warn("led urb status %d received", urb->status);
-
- if (*(kbd->leds) == kbd->newleds)
- return;
-
- *(kbd->leds) = kbd->newleds;
- kbd->led->dev = kbd->usbdev;
- if (usb_submit_urb(kbd->led, GFP_ATOMIC))
- err("usb_submit_urb(leds) failed");
-}
-
-static int usb_kbd_open(struct input_dev *dev)
-{
- struct usb_kbd *kbd = dev->private;
-
- kbd->irq->dev = kbd->usbdev;
- if (usb_submit_urb(kbd->irq, GFP_KERNEL))
- return -EIO;
-
- return 0;
-}
-
-static void usb_kbd_close(struct input_dev *dev)
-{
- struct usb_kbd *kbd = dev->private;
-
- usb_kill_urb(kbd->irq);
-}
-
-static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
-{
- if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))
- return -1;
- if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))
- return -1;
- if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma)))
- return -1;
- if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma)))
- return -1;
- if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))
- return -1;
-
- return 0;
-}
-
-static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)
-{
- usb_free_urb(kbd->irq);
- usb_free_urb(kbd->led);
- if (kbd->new)
- usb_buffer_free(dev, 8, kbd->new, kbd->new_dma);
- if (kbd->cr)
- usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma);
- if (kbd->leds)
- usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma);
-}
-
-static int usb_kbd_probe(struct usb_interface *iface,
- const struct usb_device_id *id)
-{
- struct usb_device *dev = interface_to_usbdev(iface);
- struct usb_host_interface *interface;
- struct usb_endpoint_descriptor *endpoint;
- struct usb_kbd *kbd;
- struct input_dev *input_dev;
- int i, pipe, maxp;
-
- interface = iface->cur_altsetting;
-
- if (interface->desc.bNumEndpoints != 1)
- return -ENODEV;
-
- endpoint = &interface->endpoint[0].desc;
- if (!usb_endpoint_is_int_in(endpoint))
- return -ENODEV;
-
- pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
- maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
-
- kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!kbd || !input_dev)
- goto fail1;
-
- if (usb_kbd_alloc_mem(dev, kbd))
- goto fail2;
-
- kbd->usbdev = dev;
- kbd->dev = input_dev;
-
- if (dev->manufacturer)
- strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
-
- if (dev->product) {
- if (dev->manufacturer)
- strlcat(kbd->name, " ", sizeof(kbd->name));
- strlcat(kbd->name, dev->product, sizeof(kbd->name));
- }
-
- if (!strlen(kbd->name))
- snprintf(kbd->name, sizeof(kbd->name),
- "USB HIDBP Keyboard %04x:%04x",
- le16_to_cpu(dev->descriptor.idVendor),
- le16_to_cpu(dev->descriptor.idProduct));
-
- usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
- strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));
-
- input_dev->name = kbd->name;
- input_dev->phys = kbd->phys;
- usb_to_input_id(dev, &input_dev->id);
- input_dev->cdev.dev = &iface->dev;
- input_dev->private = kbd;
-
- input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
- input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);
-
- for (i = 0; i < 255; i++)
- set_bit(usb_kbd_keycode[i], input_dev->keybit);
- clear_bit(0, input_dev->keybit);
-
- input_dev->event = usb_kbd_event;
- input_dev->open = usb_kbd_open;
- input_dev->close = usb_kbd_close;
-
- usb_fill_int_urb(kbd->irq, dev, pipe,
- kbd->new, (maxp > 8 ? 8 : maxp),
- usb_kbd_irq, kbd, endpoint->bInterval);
- kbd->irq->transfer_dma = kbd->new_dma;
- kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-
- kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- kbd->cr->bRequest = 0x09;
- kbd->cr->wValue = cpu_to_le16(0x200);
- kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
- kbd->cr->wLength = cpu_to_le16(1);
-
- usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
- (void *) kbd->cr, kbd->leds, 1,
- usb_kbd_led, kbd);
- kbd->led->setup_dma = kbd->cr_dma;
- kbd->led->transfer_dma = kbd->leds_dma;
- kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
-
- input_register_device(kbd->dev);
-
- usb_set_intfdata(iface, kbd);
- return 0;
-
-fail2: usb_kbd_free_mem(dev, kbd);
-fail1: input_free_device(input_dev);
- kfree(kbd);
- return -ENOMEM;
-}
-
-static void usb_kbd_disconnect(struct usb_interface *intf)
-{
- struct usb_kbd *kbd = usb_get_intfdata (intf);
-
- usb_set_intfdata(intf, NULL);
- if (kbd) {
- usb_kill_urb(kbd->irq);
- input_unregister_device(kbd->dev);
- usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
- kfree(kbd);
- }
-}
-
-static struct usb_device_id usb_kbd_id_table [] = {
- { USB_INTERFACE_INFO(3, 1, 1) },
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);
-
-static struct usb_driver usb_kbd_driver = {
- .name = "usbkbd",
- .probe = usb_kbd_probe,
- .disconnect = usb_kbd_disconnect,
- .id_table = usb_kbd_id_table,
-};
-
-static int __init usb_kbd_init(void)
-{
- int result = usb_register(&usb_kbd_driver);
- if (result == 0)
- info(DRIVER_VERSION ":" DRIVER_DESC);
- return result;
-}
-
-static void __exit usb_kbd_exit(void)
-{
- usb_deregister(&usb_kbd_driver);
-}
-
-module_init(usb_kbd_init);
-module_exit(usb_kbd_exit);
diff --git a/drivers/usb/input/usbmouse.c b/drivers/usb/input/usbmouse.c
deleted file mode 100644
index 64a33e420cf..00000000000
--- a/drivers/usb/input/usbmouse.c
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * $Id: usbmouse.c,v 1.15 2001/12/27 10:37:41 vojtech Exp $
- *
- * Copyright (c) 1999-2001 Vojtech Pavlik
- *
- * USB HIDBP Mouse support
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
- * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/usb/input.h>
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v1.6"
-#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
-#define DRIVER_DESC "USB HID Boot Protocol mouse driver"
-#define DRIVER_LICENSE "GPL"
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE(DRIVER_LICENSE);
-
-struct usb_mouse {
- char name[128];
- char phys[64];
- struct usb_device *usbdev;
- struct input_dev *dev;
- struct urb *irq;
-
- signed char *data;
- dma_addr_t data_dma;
-};
-
-static void usb_mouse_irq(struct urb *urb)
-{
- struct usb_mouse *mouse = urb->context;
- signed char *data = mouse->data;
- struct input_dev *dev = mouse->dev;
- int status;
-
- switch (urb->status) {
- case 0: /* success */
- break;
- case -ECONNRESET: /* unlink */
- case -ENOENT:
- case -ESHUTDOWN:
- return;
- /* -EPIPE: should clear the halt */
- default: /* error */
- goto resubmit;
- }
-
- input_report_key(dev, BTN_LEFT, data[0] & 0x01);
- input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
- input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);
- input_report_key(dev, BTN_SIDE, data[0] & 0x08);
- input_report_key(dev, BTN_EXTRA, data[0] & 0x10);
-
- input_report_rel(dev, REL_X, data[1]);
- input_report_rel(dev, REL_Y, data[2]);
- input_report_rel(dev, REL_WHEEL, data[3]);
-
- input_sync(dev);
-resubmit:
- status = usb_submit_urb (urb, GFP_ATOMIC);
- if (status)
- err ("can't resubmit intr, %s-%s/input0, status %d",
- mouse->usbdev->bus->bus_name,
- mouse->usbdev->devpath, status);
-}
-
-static int usb_mouse_open(struct input_dev *dev)
-{
- struct usb_mouse *mouse = dev->private;
-
- mouse->irq->dev = mouse->usbdev;
- if (usb_submit_urb(mouse->irq, GFP_KERNEL))
- return -EIO;
-
- return 0;
-}
-
-static void usb_mouse_close(struct input_dev *dev)
-{
- struct usb_mouse *mouse = dev->private;
-
- usb_kill_urb(mouse->irq);
-}
-
-static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
-{
- struct usb_device *dev = interface_to_usbdev(intf);
- struct usb_host_interface *interface;
- struct usb_endpoint_descriptor *endpoint;
- struct usb_mouse *mouse;
- struct input_dev *input_dev;
- int pipe, maxp;
-
- interface = intf->cur_altsetting;
-
- if (interface->desc.bNumEndpoints != 1)
- return -ENODEV;
-
- endpoint = &interface->endpoint[0].desc;
- if (!usb_endpoint_is_int_in(endpoint))
- return -ENODEV;
-
- pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
- maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
-
- mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!mouse || !input_dev)
- goto fail1;
-
- mouse->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse->data_dma);
- if (!mouse->data)
- goto fail1;
-
- mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
- if (!mouse->irq)
- goto fail2;
-
- mouse->usbdev = dev;
- mouse->dev = input_dev;
-
- if (dev->manufacturer)
- strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));
-
- if (dev->product) {
- if (dev->manufacturer)
- strlcat(mouse->name, " ", sizeof(mouse->name));
- strlcat(mouse->name, dev->product, sizeof(mouse->name));
- }
-
- if (!strlen(mouse->name))
- snprintf(mouse->name, sizeof(mouse->name),
- "USB HIDBP Mouse %04x:%04x",
- le16_to_cpu(dev->descriptor.idVendor),
- le16_to_cpu(dev->descriptor.idProduct));
-
- usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
- strlcat(mouse->phys, "/input0", sizeof(mouse->phys));
-
- input_dev->name = mouse->name;
- input_dev->phys = mouse->phys;
- usb_to_input_id(dev, &input_dev->id);
- input_dev->cdev.dev = &intf->dev;
-
- input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
- input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
- input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
- input_dev->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
- input_dev->relbit[0] |= BIT(REL_WHEEL);
-
- input_dev->private = mouse;
- input_dev->open = usb_mouse_open;
- input_dev->close = usb_mouse_close;
-
- usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
- (maxp > 8 ? 8 : maxp),
- usb_mouse_irq, mouse, endpoint->bInterval);
- mouse->irq->transfer_dma = mouse->data_dma;
- mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-
- input_register_device(mouse->dev);
-
- usb_set_intfdata(intf, mouse);
- return 0;
-
-fail2: usb_buffer_free(dev, 8, mouse->data, mouse->data_dma);
-fail1: input_free_device(input_dev);
- kfree(mouse);
- return -ENOMEM;
-}
-
-static void usb_mouse_disconnect(struct usb_interface *intf)
-{
- struct usb_mouse *mouse = usb_get_intfdata (intf);
-
- usb_set_intfdata(intf, NULL);
- if (mouse) {
- 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);
- kfree(mouse);
- }
-}
-
-static struct usb_device_id usb_mouse_id_table [] = {
- { USB_INTERFACE_INFO(3, 1, 2) },
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
-
-static struct usb_driver usb_mouse_driver = {
- .name = "usbmouse",
- .probe = usb_mouse_probe,
- .disconnect = usb_mouse_disconnect,
- .id_table = usb_mouse_id_table,
-};
-
-static int __init usb_mouse_init(void)
-{
- int retval = usb_register(&usb_mouse_driver);
- if (retval == 0)
- info(DRIVER_VERSION ":" DRIVER_DESC);
- return retval;
-}
-
-static void __exit usb_mouse_exit(void)
-{
- usb_deregister(&usb_mouse_driver);
-}
-
-module_init(usb_mouse_init);
-module_exit(usb_mouse_exit);
diff --git a/drivers/usb/input/wacom_wac.c b/drivers/usb/input/wacom_wac.c
index 4142e36730f..4f3e9bc7177 100644
--- a/drivers/usb/input/wacom_wac.c
+++ b/drivers/usb/input/wacom_wac.c
@@ -163,7 +163,7 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
}
id = STYLUS_DEVICE_ID;
- if (data[1] & 0x10) { /* in prox */
+ if (data[1] & 0x80) { /* in prox */
switch ((data[1] >> 5) & 3) {
@@ -196,9 +196,6 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
wacom_report_abs(wcombo, ABS_DISTANCE, data[7] & 0x3f);
break;
}
- }
-
- if (data[1] & 0x90) {
x = wacom_le16_to_cpu(&data[2]);
y = wacom_le16_to_cpu(&data[4]);
wacom_report_abs(wcombo, ABS_X, x);
@@ -210,19 +207,28 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x04);
}
wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
- }
- else
- wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
-
- if (data[1] & 0x10) /* only report prox-in when in area */
wacom_report_key(wcombo, wacom->tool[0], 1);
- if (!(data[1] & 0x90)) /* report prox-out when physically out */
+ } else if (!(data[1] & 0x90)) {
+ wacom_report_abs(wcombo, ABS_X, 0);
+ wacom_report_abs(wcombo, ABS_Y, 0);
+ if (wacom->tool[0] == BTN_TOOL_MOUSE) {
+ wacom_report_key(wcombo, BTN_LEFT, 0);
+ wacom_report_key(wcombo, BTN_RIGHT, 0);
+ wacom_report_abs(wcombo, ABS_DISTANCE, 0);
+ } else {
+ wacom_report_abs(wcombo, ABS_PRESSURE, 0);
+ wacom_report_key(wcombo, BTN_TOUCH, 0);
+ wacom_report_key(wcombo, BTN_STYLUS, 0);
+ wacom_report_key(wcombo, BTN_STYLUS2, 0);
+ }
+ wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
wacom_report_key(wcombo, wacom->tool[0], 0);
- wacom_input_sync(wcombo);
+ }
/* send pad data */
if (wacom->features->type == WACOM_G4) {
- if ( (wacom->serial[1] & 0xc0) != (data[7] & 0xf8) ) {
+ if (data[7] & 0xf8) {
+ wacom_input_sync(wcombo); /* sync last event */
wacom->id[1] = 1;
wacom->serial[1] = (data[7] & 0xf8);
wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
@@ -230,10 +236,15 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
wacom_report_rel(wcombo, REL_WHEEL, rw);
wacom_report_key(wcombo, BTN_TOOL_FINGER, 0xf0);
+ wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID);
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
} else if (wacom->id[1]) {
+ wacom_input_sync(wcombo); /* sync last event */
wacom->id[1] = 0;
+ wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
+ wacom_report_key(wcombo, BTN_4, (data[7] & 0x80));
wacom_report_key(wcombo, BTN_TOOL_FINGER, 0);
+ wacom_report_abs(wcombo, ABS_MISC, 0);
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
}
}
@@ -304,28 +315,35 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
default: /* Unknown tool */
wacom->tool[idx] = BTN_TOOL_PEN;
}
- /* only large I3 support Lens Cursor */
- if(!((wacom->tool[idx] == BTN_TOOL_LENS)
- && ((wacom->features->type == INTUOS3)
- || (wacom->features->type == INTUOS3S)))) {
- wacom_report_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */
- wacom_report_key(wcombo, wacom->tool[idx], 1);
- wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
- return 2;
- }
return 1;
}
/* Exit report */
if ((data[1] & 0xfe) == 0x80) {
- if(!((wacom->tool[idx] == BTN_TOOL_LENS)
- && ((wacom->features->type == INTUOS3)
- || (wacom->features->type == INTUOS3S)))) {
- wacom_report_key(wcombo, wacom->tool[idx], 0);
- wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
- wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
- return 2;
+ wacom_report_abs(wcombo, ABS_X, 0);
+ wacom_report_abs(wcombo, ABS_Y, 0);
+ wacom_report_abs(wcombo, ABS_DISTANCE, 0);
+ if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
+ wacom_report_key(wcombo, BTN_LEFT, 0);
+ wacom_report_key(wcombo, BTN_MIDDLE, 0);
+ wacom_report_key(wcombo, BTN_RIGHT, 0);
+ wacom_report_key(wcombo, BTN_SIDE, 0);
+ wacom_report_key(wcombo, BTN_EXTRA, 0);
+ wacom_report_abs(wcombo, ABS_THROTTLE, 0);
+ wacom_report_abs(wcombo, ABS_RZ, 0);
+ } else {
+ wacom_report_abs(wcombo, ABS_PRESSURE, 0);
+ wacom_report_abs(wcombo, ABS_TILT_X, 0);
+ wacom_report_abs(wcombo, ABS_TILT_Y, 0);
+ wacom_report_key(wcombo, BTN_STYLUS, 0);
+ wacom_report_key(wcombo, BTN_STYLUS2, 0);
+ wacom_report_key(wcombo, BTN_TOUCH, 0);
+ wacom_report_abs(wcombo, ABS_WHEEL, 0);
}
+ wacom_report_key(wcombo, wacom->tool[idx], 0);
+ wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+ return 2;
}
return 0;
}
@@ -394,6 +412,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
wacom_report_key(wcombo, wacom->tool[1], 1);
else
wacom_report_key(wcombo, wacom->tool[1], 0);
+ wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID);
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xffffffff);
return 1;
}
@@ -403,6 +422,12 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
if (result)
return result-1;
+ /* Only large I3 and I1 & I2 support Lense Cursor */
+ if((wacom->tool[idx] == BTN_TOOL_LENS)
+ && ((wacom->features->type == INTUOS3)
+ || (wacom->features->type == INTUOS3S)))
+ return 0;
+
/* Cintiq doesn't send data when RDY bit isn't set */
if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40))
return 0;
@@ -554,11 +579,11 @@ static struct wacom_features wacom_features[] = {
{ "Wacom Volito2 4x5", 8, 5104, 3712, 511, 63, GRAPHIRE },
{ "Wacom Volito2 2x3", 8, 3248, 2320, 511, 63, GRAPHIRE },
{ "Wacom PenPartner2", 8, 3250, 2320, 255, 63, GRAPHIRE },
- { "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 63, INTUOS },
- { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 63, INTUOS },
- { "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 63, INTUOS },
- { "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 63, INTUOS },
- { "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 63, INTUOS },
+ { "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 31, INTUOS },
+ { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
+ { "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 31, INTUOS },
+ { "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 31, INTUOS },
+ { "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 31, INTUOS },
{ "Wacom PL400", 8, 5408, 4056, 255, 0, PL },
{ "Wacom PL500", 8, 6144, 4608, 255, 0, PL },
{ "Wacom PL600", 8, 6126, 4604, 255, 0, PL },
@@ -571,11 +596,11 @@ static struct wacom_features wacom_features[] = {
{ "Wacom DTF521", 8, 6282, 4762, 511, 0, PL },
{ "Wacom DTF720", 8, 6858, 5506, 511, 0, PL },
{ "Wacom Cintiq Partner",8, 20480, 15360, 511, 0, PTU },
- { "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 63, INTUOS },
- { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 63, INTUOS },
- { "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 63, INTUOS },
- { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 63, INTUOS },
- { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 63, INTUOS },
+ { "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 31, INTUOS },
+ { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
+ { "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 31, INTUOS },
+ { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 31, INTUOS },
+ { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 31, INTUOS },
{ "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 63, INTUOS3S },
{ "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 63, INTUOS3 },
{ "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 63, INTUOS3 },
@@ -584,7 +609,7 @@ static struct wacom_features wacom_features[] = {
{ "Wacom Intuos3 6x11", 10, 54204, 31750, 1023, 63, INTUOS3 },
{ "Wacom Intuos3 4x6", 10, 31496, 19685, 1023, 63, INTUOS3S },
{ "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 63, CINTIQ },
- { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 63, INTUOS },
+ { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
{ }
};
diff --git a/drivers/usb/input/wacom_wac.h b/drivers/usb/input/wacom_wac.h
index a1d9ce00797..a2302228724 100644
--- a/drivers/usb/input/wacom_wac.h
+++ b/drivers/usb/input/wacom_wac.h
@@ -12,6 +12,7 @@
#define STYLUS_DEVICE_ID 0x02
#define CURSOR_DEVICE_ID 0x06
#define ERASER_DEVICE_ID 0x0A
+#define PAD_DEVICE_ID 0x0F
enum {
PENPARTNER = 0,
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index a74bf8617e7..9c7eb6144d0 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -88,6 +88,17 @@ config USB_LCD
To compile this driver as a module, choose M here: the
module will be called usblcd.
+config USB_BERRY_CHARGE
+ tristate "USB BlackBerry recharge support"
+ depends on USB
+ help
+ Say Y here if you want to connect a BlackBerry device to your
+ computer's USB port and have it automatically switch to "recharge"
+ mode.
+
+ To compile this driver as a module, choose M here: the
+ module will be called berry_charge.
+
config USB_LED
tristate "USB LED driver support"
depends on USB
@@ -233,6 +244,20 @@ config USB_TRANCEVIBRATOR
To compile this driver as a module, choose M here: the
module will be called trancevibrator.
+config USB_IOWARRIOR
+ tristate "IO Warrior driver support"
+ depends on USB
+ help
+ Say Y here if you want to support the IO Warrior devices from Code
+ Mercenaries. This includes support for the following devices:
+ IO Warrior 40
+ IO Warrior 24
+ IO Warrior 56
+ IO Warrior 24 Power Vampire
+
+ To compile this driver as a module, choose M here: the
+ module will be called iowarrior.
+
config USB_TEST
tristate "USB testing driver (DEVELOPMENT)"
depends on USB && USB_DEVICEFS && EXPERIMENTAL
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 2cba07d3197..b68e6b774f1 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -6,12 +6,14 @@
obj-$(CONFIG_USB_ADUTUX) += adutux.o
obj-$(CONFIG_USB_APPLEDISPLAY) += appledisplay.o
obj-$(CONFIG_USB_AUERSWALD) += auerswald.o
+obj-$(CONFIG_USB_BERRY_CHARGE) += berry_charge.o
obj-$(CONFIG_USB_CYPRESS_CY7C63)+= cypress_cy7c63.o
obj-$(CONFIG_USB_CYTHERM) += cytherm.o
obj-$(CONFIG_USB_EMI26) += emi26.o
obj-$(CONFIG_USB_EMI62) += emi62.o
obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o
obj-$(CONFIG_USB_IDMOUSE) += idmouse.o
+obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o
obj-$(CONFIG_USB_LCD) += usblcd.o
obj-$(CONFIG_USB_LD) += ldusb.o
obj-$(CONFIG_USB_LED) += usbled.o
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
index 75bfab95ab3..77145f9db04 100644
--- a/drivers/usb/misc/adutux.c
+++ b/drivers/usb/misc/adutux.c
@@ -285,23 +285,24 @@ static int adu_open(struct inode *inode, struct file *file)
/* save device in the file's private structure */
file->private_data = dev;
- /* initialize in direction */
- dev->read_buffer_length = 0;
-
- /* fixup first read by having urb waiting for it */
- usb_fill_int_urb(dev->interrupt_in_urb,dev->udev,
- usb_rcvintpipe(dev->udev,
- dev->interrupt_in_endpoint->bEndpointAddress),
- dev->interrupt_in_buffer,
- le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
- adu_interrupt_in_callback, dev,
- dev->interrupt_in_endpoint->bInterval);
- /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */
- dev->read_urb_finished = 0;
- usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
- /* we ignore failure */
- /* end of fixup for first read */
+ if (dev->open_count == 1) {
+ /* initialize in direction */
+ dev->read_buffer_length = 0;
+ /* fixup first read by having urb waiting for it */
+ usb_fill_int_urb(dev->interrupt_in_urb,dev->udev,
+ usb_rcvintpipe(dev->udev,
+ dev->interrupt_in_endpoint->bEndpointAddress),
+ dev->interrupt_in_buffer,
+ le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
+ adu_interrupt_in_callback, dev,
+ dev->interrupt_in_endpoint->bInterval);
+ /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */
+ dev->read_urb_finished = 0;
+ retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+ if (retval)
+ --dev->open_count;
+ }
up(&dev->sem);
exit_no_device:
@@ -469,7 +470,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count,
adu_interrupt_in_callback,
dev,
dev->interrupt_in_endpoint->bInterval);
- retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+ retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC);
if (!retval) {
spin_unlock_irqrestore(&dev->buflock, flags);
dbg(2," %s : submitted OK", __FUNCTION__);
@@ -539,7 +540,7 @@ static ssize_t adu_write(struct file *file, const __user char *buffer,
size_t bytes_written = 0;
size_t bytes_to_write;
size_t buffer_size;
- int retval = 0;
+ int retval;
int timeout = 0;
dbg(2," %s : enter, count = %Zd", __FUNCTION__, count);
@@ -547,7 +548,9 @@ static ssize_t adu_write(struct file *file, const __user char *buffer,
dev = file->private_data;
/* lock this object */
- down_interruptible(&dev->sem);
+ retval = down_interruptible(&dev->sem);
+ if (retval)
+ goto exit_nolock;
/* verify that the device wasn't unplugged */
if (dev->udev == NULL || dev->minor == 0) {
@@ -575,7 +578,11 @@ static ssize_t adu_write(struct file *file, const __user char *buffer,
}
up(&dev->sem);
timeout = interruptible_sleep_on_timeout(&dev->write_wait, timeout);
- down_interruptible(&dev->sem);
+ retval = down_interruptible(&dev->sem);
+ if (retval) {
+ retval = bytes_written ? bytes_written : retval;
+ goto exit_nolock;
+ }
if (timeout > 0) {
break;
}
@@ -637,6 +644,7 @@ static ssize_t adu_write(struct file *file, const __user char *buffer,
exit:
/* unlock the device */
up(&dev->sem);
+exit_nolock:
dbg(2," %s : leave, return value %d", __FUNCTION__, retval);
diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c
index 32f0e3a5b02..cf70c16f0e3 100644
--- a/drivers/usb/misc/appledisplay.c
+++ b/drivers/usb/misc/appledisplay.c
@@ -141,7 +141,7 @@ static int appledisplay_bl_update_status(struct backlight_device *bd)
int retval;
pdata->msgdata[0] = 0x10;
- pdata->msgdata[1] = bd->props->brightness;
+ pdata->msgdata[1] = bd->props.brightness;
retval = usb_control_msg(
pdata->udev,
@@ -177,11 +177,9 @@ static int appledisplay_bl_get_brightness(struct backlight_device *bd)
return pdata->msgdata[1];
}
-static struct backlight_properties appledisplay_bl_data = {
- .owner = THIS_MODULE,
+static struct backlight_ops appledisplay_bl_data = {
.get_brightness = appledisplay_bl_get_brightness,
.update_status = appledisplay_bl_update_status,
- .max_brightness = 0xFF
};
static void appledisplay_work(struct work_struct *work)
@@ -190,11 +188,9 @@ static void appledisplay_work(struct work_struct *work)
container_of(work, struct appledisplay, work.work);
int retval;
- up(&pdata->bd->sem);
retval = appledisplay_bl_get_brightness(pdata->bd);
if (retval >= 0)
- pdata->bd->props->brightness = retval;
- down(&pdata->bd->sem);
+ pdata->bd->props.brightness = retval;
/* Poll again in about 125ms if there's still a button pressed */
if (pdata->button_pressed)
@@ -281,17 +277,17 @@ static int appledisplay_probe(struct usb_interface *iface,
/* Register backlight device */
snprintf(bl_name, sizeof(bl_name), "appledisplay%d",
atomic_inc_return(&count_displays) - 1);
- pdata->bd = backlight_device_register(bl_name, NULL,
- pdata, &appledisplay_bl_data);
+ pdata->bd = backlight_device_register(bl_name, NULL, pdata,
+ &appledisplay_bl_data);
if (IS_ERR(pdata->bd)) {
err("appledisplay: Backlight registration failed");
goto error;
}
+ pdata->bd->props.max_brightness = 0xff;
+
/* Try to get brightness */
- up(&pdata->bd->sem);
brightness = appledisplay_bl_get_brightness(pdata->bd);
- down(&pdata->bd->sem);
if (brightness < 0) {
retval = brightness;
@@ -300,9 +296,7 @@ static int appledisplay_probe(struct usb_interface *iface,
}
/* Set brightness in backlight device */
- up(&pdata->bd->sem);
- pdata->bd->props->brightness = brightness;
- down(&pdata->bd->sem);
+ pdata->bd->props.brightness = brightness;
/* save our data pointer in the interface device */
usb_set_intfdata(iface, pdata);
diff --git a/drivers/usb/misc/berry_charge.c b/drivers/usb/misc/berry_charge.c
new file mode 100644
index 00000000000..b15f2fd8dab
--- /dev/null
+++ b/drivers/usb/misc/berry_charge.c
@@ -0,0 +1,140 @@
+/*
+ * USB BlackBerry charging module
+ *
+ * Copyright (C) 2007 Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * 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, version 2.
+ *
+ * Information on how to switch configs was taken by the bcharge.cc file
+ * created by the barry.sf.net project.
+ *
+ * bcharge.cc has the following copyright:
+ * Copyright (C) 2006, Net Direct Inc. (http://www.netdirect.ca/)
+ * and is released under the GPLv2.
+ *
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#define RIM_VENDOR 0x0fca
+#define BLACKBERRY 0x0001
+
+static int debug;
+
+#ifdef dbg
+#undef dbg
+#endif
+#define dbg(dev, format, arg...) \
+ if (debug) \
+ dev_printk(KERN_DEBUG , dev , format , ## arg)
+
+static struct usb_device_id id_table [] = {
+ { USB_DEVICE(RIM_VENDOR, BLACKBERRY) },
+ { }, /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int magic_charge(struct usb_device *udev)
+{
+ char *dummy_buffer = kzalloc(2, GFP_KERNEL);
+ int retval;
+
+ if (!dummy_buffer)
+ return -ENOMEM;
+
+ /* send two magic commands and then set the configuration. The device
+ * will then reset itself with the new power usage and should start
+ * charging. */
+
+ /* Note, with testing, it only seems that the first message is really
+ * needed (at least for the 8700c), but to be safe, we emulate what
+ * other operating systems seem to be sending to their device. We
+ * really need to get some specs for this device to be sure about what
+ * is going on here.
+ */
+ dbg(&udev->dev, "Sending first magic command\n");
+ retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0xa5, 0xc0, 0, 1, dummy_buffer, 2, 100);
+ if (retval != 2) {
+ dev_err(&udev->dev, "First magic command failed: %d.\n",
+ retval);
+ return retval;
+ }
+
+ dbg(&udev->dev, "Sending second magic command\n");
+ retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0xa2, 0x40, 0, 1, dummy_buffer, 0, 100);
+ if (retval != 0) {
+ dev_err(&udev->dev, "Second magic command failed: %d.\n",
+ retval);
+ return retval;
+ }
+
+ dbg(&udev->dev, "Calling set_configuration\n");
+ retval = usb_driver_set_configuration(udev, 1);
+ if (retval)
+ dev_err(&udev->dev, "Set Configuration failed :%d.\n", retval);
+
+ return retval;
+}
+
+static int berry_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+
+ dbg(&udev->dev, "Power is set to %dmA\n",
+ udev->actconfig->desc.bMaxPower * 2);
+
+ /* check the power usage so we don't try to enable something that is
+ * already enabled */
+ if ((udev->actconfig->desc.bMaxPower * 2) == 500) {
+ dbg(&udev->dev, "device is already charging, power is "
+ "set to %dmA\n", udev->actconfig->desc.bMaxPower * 2);
+ return -ENODEV;
+ }
+
+ /* turn the power on */
+ magic_charge(udev);
+
+ /* we don't really want to bind to the device, userspace programs can
+ * handle the syncing just fine, so get outta here. */
+ return -ENODEV;
+}
+
+static void berry_disconnect(struct usb_interface *intf)
+{
+}
+
+static struct usb_driver berry_driver = {
+ .name = "berry_charge",
+ .probe = berry_probe,
+ .disconnect = berry_disconnect,
+ .id_table = id_table,
+};
+
+static int __init berry_init(void)
+{
+ return usb_register(&berry_driver);
+}
+
+static void __exit berry_exit(void)
+{
+ usb_deregister(&berry_driver);
+}
+
+module_init(berry_init);
+module_exit(berry_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c
index b63b5f34b2a..d721380b242 100644
--- a/drivers/usb/misc/cypress_cy7c63.c
+++ b/drivers/usb/misc/cypress_cy7c63.c
@@ -246,11 +246,13 @@ static void cypress_disconnect(struct usb_interface *interface)
struct cypress *dev;
dev = usb_get_intfdata(interface);
- usb_set_intfdata(interface, NULL);
/* remove device attribute files */
device_remove_file(&interface->dev, &dev_attr_port0);
device_remove_file(&interface->dev, &dev_attr_port1);
+ /* the intfdata can be set to NULL only after the
+ * device files have been removed */
+ usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c
index 0c1d66ddb81..e2172e5cf15 100644
--- a/drivers/usb/misc/ftdi-elan.c
+++ b/drivers/usb/misc/ftdi-elan.c
@@ -2304,7 +2304,6 @@ static int ftdi_elan_checkingPCI(struct usb_ftdi *ftdi)
#define OHCI_QUIRK_SUPERIO 0x02
#define OHCI_QUIRK_INITRESET 0x04
#define OHCI_BIG_ENDIAN 0x08
-#define OHCI_QUIRK_ZFMICRO 0x10
#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
#define OHCI_INTR_INIT (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | \
OHCI_INTR_WDH)
@@ -2905,17 +2904,35 @@ static int __init ftdi_elan_init(void)
{
int result;
printk(KERN_INFO "driver %s built at %s on %s\n", ftdi_elan_driver.name,
- __TIME__, __DATE__);
+ __TIME__, __DATE__);
init_MUTEX(&ftdi_module_lock);
INIT_LIST_HEAD(&ftdi_static_list);
status_queue = create_singlethread_workqueue("ftdi-status-control");
+ if (!status_queue)
+ goto err_status_queue;
command_queue = create_singlethread_workqueue("ftdi-command-engine");
+ if (!command_queue)
+ goto err_command_queue;
respond_queue = create_singlethread_workqueue("ftdi-respond-engine");
+ if (!respond_queue)
+ goto err_respond_queue;
result = usb_register(&ftdi_elan_driver);
- if (result)
+ if (result) {
+ destroy_workqueue(status_queue);
+ destroy_workqueue(command_queue);
+ destroy_workqueue(respond_queue);
printk(KERN_ERR "usb_register failed. Error number %d\n",
- result);
+ result);
+ }
return result;
+
+ err_respond_queue:
+ destroy_workqueue(command_queue);
+ err_command_queue:
+ destroy_workqueue(status_queue);
+ err_status_queue:
+ printk(KERN_ERR "%s couldn't create workqueue\n", ftdi_elan_driver.name);
+ return -ENOMEM;
}
static void __exit ftdi_elan_exit(void)
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
new file mode 100644
index 00000000000..fc51207b71b
--- /dev/null
+++ b/drivers/usb/misc/iowarrior.c
@@ -0,0 +1,925 @@
+/*
+ * Native support for the I/O-Warrior USB devices
+ *
+ * Copyright (c) 2003-2005 Code Mercenaries GmbH
+ * written by Christian Lucht <lucht@codemercs.com>
+ *
+ * based on
+
+ * usb-skeleton.c by Greg Kroah-Hartman <greg@kroah.com>
+ * brlvger.c by Stephane Dalton <sdalton@videotron.ca>
+ * and St�hane Doyon <s.doyon@videotron.ca>
+ *
+ * Released under the GPLv2.
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/version.h>
+#include <linux/usb/iowarrior.h>
+
+/* Version Information */
+#define DRIVER_VERSION "v0.4.0"
+#define DRIVER_AUTHOR "Christian Lucht <lucht@codemercs.com>"
+#define DRIVER_DESC "USB IO-Warrior driver (Linux 2.6.x)"
+
+#define USB_VENDOR_ID_CODEMERCS 1984
+/* low speed iowarrior */
+#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500
+#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501
+#define USB_DEVICE_ID_CODEMERCS_IOWPV1 0x1511
+#define USB_DEVICE_ID_CODEMERCS_IOWPV2 0x1512
+/* full speed iowarrior */
+#define USB_DEVICE_ID_CODEMERCS_IOW56 0x1503
+
+/* Get a minor range for your devices from the usb maintainer */
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define IOWARRIOR_MINOR_BASE 0
+#else
+#define IOWARRIOR_MINOR_BASE 208 // SKELETON_MINOR_BASE 192 + 16, not offical yet
+#endif
+
+/* interrupt input queue size */
+#define MAX_INTERRUPT_BUFFER 16
+/*
+ maximum number of urbs that are submitted for writes at the same time,
+ this applies to the IOWarrior56 only!
+ IOWarrior24 and IOWarrior40 use synchronous usb_control_msg calls.
+*/
+#define MAX_WRITES_IN_FLIGHT 4
+
+/* Use our own dbg macro */
+#undef dbg
+#define dbg( format, arg... ) do { if( debug ) printk( KERN_DEBUG __FILE__ ": " format "\n" , ## arg ); } while ( 0 )
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/* Module parameters */
+static int debug = 0;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "debug=1 enables debugging messages");
+
+static struct usb_driver iowarrior_driver;
+
+/*--------------*/
+/* data */
+/*--------------*/
+
+/* Structure to hold all of our device specific stuff */
+struct iowarrior {
+ struct mutex mutex; /* locks this structure */
+ struct usb_device *udev; /* save off the usb device pointer */
+ struct usb_interface *interface; /* the interface for this device */
+ unsigned char minor; /* the starting minor number for this device */
+ struct usb_endpoint_descriptor *int_out_endpoint; /* endpoint for reading (needed for IOW56 only) */
+ struct usb_endpoint_descriptor *int_in_endpoint; /* endpoint for reading */
+ struct urb *int_in_urb; /* the urb for reading data */
+ unsigned char *int_in_buffer; /* buffer for data to be read */
+ unsigned char serial_number; /* to detect lost packages */
+ unsigned char *read_queue; /* size is MAX_INTERRUPT_BUFFER * packet size */
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait; /* wait-queue for writing to the device */
+ atomic_t write_busy; /* number of write-urbs submitted */
+ atomic_t read_idx;
+ atomic_t intr_idx;
+ spinlock_t intr_idx_lock; /* protects intr_idx */
+ atomic_t overflow_flag; /* signals an index 'rollover' */
+ int present; /* this is 1 as long as the device is connected */
+ int opened; /* this is 1 if the device is currently open */
+ char chip_serial[9]; /* the serial number string of the chip connected */
+ int report_size; /* number of bytes in a report */
+ u16 product_id;
+};
+
+/*--------------*/
+/* globals */
+/*--------------*/
+/* prevent races between open() and disconnect() */
+static DECLARE_MUTEX(disconnect_sem);
+
+/*
+ * USB spec identifies 5 second timeouts.
+ */
+#define GET_TIMEOUT 5
+#define USB_REQ_GET_REPORT 0x01
+//#if 0
+static int usb_get_report(struct usb_device *dev,
+ struct usb_host_interface *inter, unsigned char type,
+ unsigned char id, void *buf, int size)
+{
+ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_REPORT,
+ USB_DIR_IN | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE, (type << 8) + id,
+ inter->desc.bInterfaceNumber, buf, size,
+ GET_TIMEOUT*HZ);
+}
+//#endif
+
+#define USB_REQ_SET_REPORT 0x09
+
+static int usb_set_report(struct usb_interface *intf, unsigned char type,
+ unsigned char id, void *buf, int size)
+{
+ return usb_control_msg(interface_to_usbdev(intf),
+ usb_sndctrlpipe(interface_to_usbdev(intf), 0),
+ USB_REQ_SET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ (type << 8) + id,
+ intf->cur_altsetting->desc.bInterfaceNumber, buf,
+ size, HZ);
+}
+
+/*---------------------*/
+/* driver registration */
+/*---------------------*/
+/* table of devices that work with this driver */
+static struct usb_device_id iowarrior_ids[] = {
+ {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40)},
+ {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24)},
+ {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV1)},
+ {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV2)},
+ {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW56)},
+ {} /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, iowarrior_ids);
+
+/*
+ * USB callback handler for reading data
+ */
+static void iowarrior_callback(struct urb *urb)
+{
+ struct iowarrior *dev = (struct iowarrior *)urb->context;
+ int intr_idx;
+ int read_idx;
+ int aux_idx;
+ int offset;
+ int status;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default:
+ goto exit;
+ }
+
+ spin_lock(&dev->intr_idx_lock);
+ intr_idx = atomic_read(&dev->intr_idx);
+ /* aux_idx become previous intr_idx */
+ aux_idx = (intr_idx == 0) ? (MAX_INTERRUPT_BUFFER - 1) : (intr_idx - 1);
+ read_idx = atomic_read(&dev->read_idx);
+
+ /* queue is not empty and it's interface 0 */
+ if ((intr_idx != read_idx)
+ && (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0)) {
+ /* + 1 for serial number */
+ offset = aux_idx * (dev->report_size + 1);
+ if (!memcmp
+ (dev->read_queue + offset, urb->transfer_buffer,
+ dev->report_size)) {
+ /* equal values on interface 0 will be ignored */
+ spin_unlock(&dev->intr_idx_lock);
+ goto exit;
+ }
+ }
+
+ /* aux_idx become next intr_idx */
+ aux_idx = (intr_idx == (MAX_INTERRUPT_BUFFER - 1)) ? 0 : (intr_idx + 1);
+ if (read_idx == aux_idx) {
+ /* queue full, dropping oldest input */
+ read_idx = (++read_idx == MAX_INTERRUPT_BUFFER) ? 0 : read_idx;
+ atomic_set(&dev->read_idx, read_idx);
+ atomic_set(&dev->overflow_flag, 1);
+ }
+
+ /* +1 for serial number */
+ offset = intr_idx * (dev->report_size + 1);
+ memcpy(dev->read_queue + offset, urb->transfer_buffer,
+ dev->report_size);
+ *(dev->read_queue + offset + (dev->report_size)) = dev->serial_number++;
+
+ atomic_set(&dev->intr_idx, aux_idx);
+ spin_unlock(&dev->intr_idx_lock);
+ /* tell the blocking read about the new data */
+ wake_up_interruptible(&dev->read_wait);
+
+exit:
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status)
+ dev_err(&dev->interface->dev, "%s - usb_submit_urb failed with result %d",
+ __FUNCTION__, status);
+
+}
+
+/*
+ * USB Callback handler for write-ops
+ */
+static void iowarrior_write_callback(struct urb *urb)
+{
+ struct iowarrior *dev;
+ dev = (struct iowarrior *)urb->context;
+ /* sync/async unlink faults aren't errors */
+ if (urb->status &&
+ !(urb->status == -ENOENT ||
+ urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) {
+ dbg("%s - nonzero write bulk status received: %d",
+ __func__, urb->status);
+ }
+ /* free up our allocated buffer */
+ usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+ urb->transfer_buffer, urb->transfer_dma);
+ /* tell a waiting writer the interrupt-out-pipe is available again */
+ atomic_dec(&dev->write_busy);
+ wake_up_interruptible(&dev->write_wait);
+}
+
+/**
+ * iowarrior_delete
+ */
+static inline void iowarrior_delete(struct iowarrior *dev)
+{
+ dbg("%s - minor %d", __func__, dev->minor);
+ kfree(dev->int_in_buffer);
+ usb_free_urb(dev->int_in_urb);
+ kfree(dev->read_queue);
+ kfree(dev);
+}
+
+/*---------------------*/
+/* fops implementation */
+/*---------------------*/
+
+static int read_index(struct iowarrior *dev)
+{
+ int intr_idx, read_idx;
+
+ read_idx = atomic_read(&dev->read_idx);
+ intr_idx = atomic_read(&dev->intr_idx);
+
+ return (read_idx == intr_idx ? -1 : read_idx);
+}
+
+/**
+ * iowarrior_read
+ */
+static ssize_t iowarrior_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct iowarrior *dev;
+ int read_idx;
+ int offset;
+
+ dev = (struct iowarrior *)file->private_data;
+
+ /* verify that the device wasn't unplugged */
+ if (dev == NULL || !dev->present)
+ return -ENODEV;
+
+ dbg("%s - minor %d, count = %zd", __func__, dev->minor, count);
+
+ /* read count must be packet size (+ time stamp) */
+ if ((count != dev->report_size)
+ && (count != (dev->report_size + 1)))
+ return -EINVAL;
+
+ /* repeat until no buffer overrun in callback handler occur */
+ do {
+ atomic_set(&dev->overflow_flag, 0);
+ if ((read_idx = read_index(dev)) == -1) {
+ /* queue emty */
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ else {
+ //next line will return when there is either new data, or the device is unplugged
+ int r = wait_event_interruptible(dev->read_wait,
+ (!dev->present
+ || (read_idx =
+ read_index
+ (dev)) !=
+ -1));
+ if (r) {
+ //we were interrupted by a signal
+ return -ERESTART;
+ }
+ if (!dev->present) {
+ //The device was unplugged
+ return -ENODEV;
+ }
+ if (read_idx == -1) {
+ // Can this happen ???
+ return 0;
+ }
+ }
+ }
+
+ offset = read_idx * (dev->report_size + 1);
+ if (copy_to_user(buffer, dev->read_queue + offset, count)) {
+ return -EFAULT;
+ }
+ } while (atomic_read(&dev->overflow_flag));
+
+ read_idx = ++read_idx == MAX_INTERRUPT_BUFFER ? 0 : read_idx;
+ atomic_set(&dev->read_idx, read_idx);
+ return count;
+}
+
+/*
+ * iowarrior_write
+ */
+static ssize_t iowarrior_write(struct file *file,
+ const char __user *user_buffer,
+ size_t count, loff_t *ppos)
+{
+ struct iowarrior *dev;
+ int retval = 0;
+ char *buf = NULL; /* for IOW24 and IOW56 we need a buffer */
+ struct urb *int_out_urb = NULL;
+
+ dev = (struct iowarrior *)file->private_data;
+
+ mutex_lock(&dev->mutex);
+ /* verify that the device wasn't unplugged */
+ if (dev == NULL || !dev->present) {
+ retval = -ENODEV;
+ goto exit;
+ }
+ dbg("%s - minor %d, count = %zd", __func__, dev->minor, count);
+ /* if count is 0 we're already done */
+ if (count == 0) {
+ retval = 0;
+ goto exit;
+ }
+ /* We only accept full reports */
+ if (count != dev->report_size) {
+ retval = -EINVAL;
+ goto exit;
+ }
+ switch (dev->product_id) {
+ case USB_DEVICE_ID_CODEMERCS_IOW24:
+ case USB_DEVICE_ID_CODEMERCS_IOWPV1:
+ case USB_DEVICE_ID_CODEMERCS_IOWPV2:
+ case USB_DEVICE_ID_CODEMERCS_IOW40:
+ /* IOW24 and IOW40 use a synchronous call */
+ buf = kmalloc(8, GFP_KERNEL); /* 8 bytes are enough for both products */
+ if (!buf) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+ if (copy_from_user(buf, user_buffer, count)) {
+ retval = -EFAULT;
+ kfree(buf);
+ goto exit;
+ }
+ retval = usb_set_report(dev->interface, 2, 0, buf, count);
+ kfree(buf);
+ goto exit;
+ break;
+ case USB_DEVICE_ID_CODEMERCS_IOW56:
+ /* The IOW56 uses asynchronous IO and more urbs */
+ if (atomic_read(&dev->write_busy) == MAX_WRITES_IN_FLIGHT) {
+ /* Wait until we are below the limit for submitted urbs */
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ goto exit;
+ } else {
+ retval = wait_event_interruptible(dev->write_wait,
+ (!dev->present || (atomic_read (&dev-> write_busy) < MAX_WRITES_IN_FLIGHT)));
+ if (retval) {
+ /* we were interrupted by a signal */
+ retval = -ERESTART;
+ goto exit;
+ }
+ if (!dev->present) {
+ /* The device was unplugged */
+ retval = -ENODEV;
+ goto exit;
+ }
+ if (!dev->opened) {
+ /* We were closed while waiting for an URB */
+ retval = -ENODEV;
+ goto exit;
+ }
+ }
+ }
+ atomic_inc(&dev->write_busy);
+ int_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!int_out_urb) {
+ retval = -ENOMEM;
+ dbg("%s Unable to allocate urb ", __func__);
+ goto error_no_urb;
+ }
+ buf = usb_buffer_alloc(dev->udev, dev->report_size,
+ GFP_KERNEL, &int_out_urb->transfer_dma);
+ if (!buf) {
+ retval = -ENOMEM;
+ dbg("%s Unable to allocate buffer ", __func__);
+ goto error_no_buffer;
+ }
+ usb_fill_int_urb(int_out_urb, dev->udev,
+ usb_sndintpipe(dev->udev,
+ dev->int_out_endpoint->bEndpointAddress),
+ buf, dev->report_size,
+ iowarrior_write_callback, dev,
+ dev->int_out_endpoint->bInterval);
+ int_out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ if (copy_from_user(buf, user_buffer, count)) {
+ retval = -EFAULT;
+ goto error;
+ }
+ retval = usb_submit_urb(int_out_urb, GFP_KERNEL);
+ if (retval) {
+ dbg("%s submit error %d for urb nr.%d", __func__,
+ retval, atomic_read(&dev->write_busy));
+ goto error;
+ }
+ /* submit was ok */
+ retval = count;
+ usb_free_urb(int_out_urb);
+ goto exit;
+ break;
+ default:
+ /* what do we have here ? An unsupported Product-ID ? */
+ dev_err(&dev->interface->dev, "%s - not supported for product=0x%x",
+ __FUNCTION__, dev->product_id);
+ retval = -EFAULT;
+ goto exit;
+ break;
+ }
+error:
+ usb_buffer_free(dev->udev, dev->report_size, buf,
+ int_out_urb->transfer_dma);
+error_no_buffer:
+ usb_free_urb(int_out_urb);
+error_no_urb:
+ atomic_dec(&dev->write_busy);
+ wake_up_interruptible(&dev->write_wait);
+exit:
+ mutex_unlock(&dev->mutex);
+ return retval;
+}
+
+/**
+ * iowarrior_ioctl
+ */
+static int iowarrior_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct iowarrior *dev = NULL;
+ __u8 *buffer;
+ __u8 __user *user_buffer;
+ int retval;
+ int io_res; /* checks for bytes read/written and copy_to/from_user results */
+
+ dev = (struct iowarrior *)file->private_data;
+ if (dev == NULL) {
+ return -ENODEV;
+ }
+
+ buffer = kzalloc(dev->report_size, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ /* lock this object */
+ mutex_lock(&dev->mutex);
+
+ /* verify that the device wasn't unplugged */
+ if (!dev->present) {
+ mutex_unlock(&dev->mutex);
+ return -ENODEV;
+ }
+
+ dbg("%s - minor %d, cmd 0x%.4x, arg %ld", __func__, dev->minor, cmd,
+ arg);
+
+ retval = 0;
+ io_res = 0;
+ switch (cmd) {
+ case IOW_WRITE:
+ if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW24 ||
+ dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV1 ||
+ dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV2 ||
+ dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW40) {
+ user_buffer = (__u8 __user *)arg;
+ io_res = copy_from_user(buffer, user_buffer,
+ dev->report_size);
+ if (io_res) {
+ retval = -EFAULT;
+ } else {
+ io_res = usb_set_report(dev->interface, 2, 0,
+ buffer,
+ dev->report_size);
+ if (io_res < 0)
+ retval = io_res;
+ }
+ } else {
+ retval = -EINVAL;
+ dev_err(&dev->interface->dev,
+ "ioctl 'IOW_WRITE' is not supported for product=0x%x.",
+ dev->product_id);
+ }
+ break;
+ case IOW_READ:
+ user_buffer = (__u8 __user *)arg;
+ io_res = usb_get_report(dev->udev,
+ dev->interface->cur_altsetting, 1, 0,
+ buffer, dev->report_size);
+ if (io_res < 0)
+ retval = io_res;
+ else {
+ io_res = copy_to_user(user_buffer, buffer, dev->report_size);
+ if (io_res < 0)
+ retval = -EFAULT;
+ }
+ break;
+ case IOW_GETINFO:
+ {
+ /* Report available information for the device */
+ struct iowarrior_info info;
+ /* needed for power consumption */
+ struct usb_config_descriptor *cfg_descriptor = &dev->udev->actconfig->desc;
+
+ /* directly from the descriptor */
+ info.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+ info.product = dev->product_id;
+ info.revision = le16_to_cpu(dev->udev->descriptor.bcdDevice);
+
+ /* 0==UNKNOWN, 1==LOW(usb1.1) ,2=FULL(usb1.1), 3=HIGH(usb2.0) */
+ info.speed = le16_to_cpu(dev->udev->speed);
+ info.if_num = dev->interface->cur_altsetting->desc.bInterfaceNumber;
+ info.report_size = dev->report_size;
+
+ /* serial number string has been read earlier 8 chars or empty string */
+ memcpy(info.serial, dev->chip_serial,
+ sizeof(dev->chip_serial));
+ if (cfg_descriptor == NULL) {
+ info.power = -1; /* no information available */
+ } else {
+ /* the MaxPower is stored in units of 2mA to make it fit into a byte-value */
+ info.power = cfg_descriptor->bMaxPower * 2;
+ }
+ io_res = copy_to_user((struct iowarrior_info __user *)arg, &info,
+ sizeof(struct iowarrior_info));
+ if (io_res < 0)
+ retval = -EFAULT;
+ break;
+ }
+ default:
+ /* return that we did not understand this ioctl call */
+ retval = -ENOTTY;
+ break;
+ }
+
+ /* unlock the device */
+ mutex_unlock(&dev->mutex);
+ return retval;
+}
+
+/**
+ * iowarrior_open
+ */
+static int iowarrior_open(struct inode *inode, struct file *file)
+{
+ struct iowarrior *dev = NULL;
+ struct usb_interface *interface;
+ int subminor;
+ int retval = 0;
+
+ dbg("%s", __func__);
+
+ subminor = iminor(inode);
+
+ /* prevent disconnects */
+ down(&disconnect_sem);
+
+ interface = usb_find_interface(&iowarrior_driver, subminor);
+ if (!interface) {
+ err("%s - error, can't find device for minor %d", __FUNCTION__,
+ subminor);
+ retval = -ENODEV;
+ goto out;
+ }
+
+ dev = usb_get_intfdata(interface);
+ if (!dev) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ /* Only one process can open each device, no sharing. */
+ if (dev->opened) {
+ retval = -EBUSY;
+ goto out;
+ }
+
+ /* setup interrupt handler for receiving values */
+ if ((retval = usb_submit_urb(dev->int_in_urb, GFP_KERNEL)) < 0) {
+ dev_err(&interface->dev, "Error %d while submitting URB\n", retval);
+ retval = -EFAULT;
+ goto out;
+ }
+ /* increment our usage count for the driver */
+ ++dev->opened;
+ /* save our object in the file's private structure */
+ file->private_data = dev;
+ retval = 0;
+
+out:
+ up(&disconnect_sem);
+ return retval;
+}
+
+/**
+ * iowarrior_release
+ */
+static int iowarrior_release(struct inode *inode, struct file *file)
+{
+ struct iowarrior *dev;
+ int retval = 0;
+
+ dev = (struct iowarrior *)file->private_data;
+ if (dev == NULL) {
+ return -ENODEV;
+ }
+
+ dbg("%s - minor %d", __func__, dev->minor);
+
+ /* lock our device */
+ mutex_lock(&dev->mutex);
+
+ if (dev->opened <= 0) {
+ retval = -ENODEV; /* close called more than once */
+ mutex_unlock(&dev->mutex);
+ } else {
+ dev->opened = 0; /* we're closeing now */
+ retval = 0;
+ if (dev->present) {
+ /*
+ The device is still connected so we only shutdown
+ pending read-/write-ops.
+ */
+ usb_kill_urb(dev->int_in_urb);
+ wake_up_interruptible(&dev->read_wait);
+ wake_up_interruptible(&dev->write_wait);
+ mutex_unlock(&dev->mutex);
+ } else {
+ /* The device was unplugged, cleanup resources */
+ mutex_unlock(&dev->mutex);
+ iowarrior_delete(dev);
+ }
+ }
+ return retval;
+}
+
+static unsigned iowarrior_poll(struct file *file, poll_table * wait)
+{
+ struct iowarrior *dev = file->private_data;
+ unsigned int mask = 0;
+
+ if (!dev->present)
+ return POLLERR | POLLHUP;
+
+ poll_wait(file, &dev->read_wait, wait);
+ poll_wait(file, &dev->write_wait, wait);
+
+ if (!dev->present)
+ return POLLERR | POLLHUP;
+
+ if (read_index(dev) != -1)
+ mask |= POLLIN | POLLRDNORM;
+
+ if (atomic_read(&dev->write_busy) < MAX_WRITES_IN_FLIGHT)
+ mask |= POLLOUT | POLLWRNORM;
+ return mask;
+}
+
+/*
+ * File operations needed when we register this driver.
+ * This assumes that this driver NEEDS file operations,
+ * of course, which means that the driver is expected
+ * to have a node in the /dev directory. If the USB
+ * device were for a network interface then the driver
+ * would use "struct net_driver" instead, and a serial
+ * device would use "struct tty_driver".
+ */
+static struct file_operations iowarrior_fops = {
+ .owner = THIS_MODULE,
+ .write = iowarrior_write,
+ .read = iowarrior_read,
+ .ioctl = iowarrior_ioctl,
+ .open = iowarrior_open,
+ .release = iowarrior_release,
+ .poll = iowarrior_poll,
+};
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with devfs and the driver core
+ */
+static struct usb_class_driver iowarrior_class = {
+ .name = "iowarrior%d",
+ .fops = &iowarrior_fops,
+ .minor_base = IOWARRIOR_MINOR_BASE,
+};
+
+/*---------------------------------*/
+/* probe and disconnect functions */
+/*---------------------------------*/
+/**
+ * iowarrior_probe
+ *
+ * Called by the usb core when a new device is connected that it thinks
+ * this driver might be interested in.
+ */
+static int iowarrior_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct iowarrior *dev = NULL;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+ int retval = -ENOMEM;
+
+ /* allocate memory for our device state and intialize it */
+ dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL);
+ if (dev == NULL) {
+ dev_err(&interface->dev, "Out of memory");
+ return retval;
+ }
+
+ mutex_init(&dev->mutex);
+
+ atomic_set(&dev->intr_idx, 0);
+ atomic_set(&dev->read_idx, 0);
+ spin_lock_init(&dev->intr_idx_lock);
+ atomic_set(&dev->overflow_flag, 0);
+ init_waitqueue_head(&dev->read_wait);
+ atomic_set(&dev->write_busy, 0);
+ init_waitqueue_head(&dev->write_wait);
+
+ dev->udev = udev;
+ dev->interface = interface;
+
+ iface_desc = interface->cur_altsetting;
+ dev->product_id = le16_to_cpu(udev->descriptor.idProduct);
+
+ /* set up the endpoint information */
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_int_in(endpoint))
+ dev->int_in_endpoint = endpoint;
+ if (usb_endpoint_is_int_out(endpoint))
+ /* this one will match for the IOWarrior56 only */
+ dev->int_out_endpoint = endpoint;
+ }
+ /* we have to check the report_size often, so remember it in the endianess suitable for our machine */
+ dev->report_size = le16_to_cpu(dev->int_in_endpoint->wMaxPacketSize);
+ if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) &&
+ (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56))
+ /* IOWarrior56 has wMaxPacketSize different from report size */
+ dev->report_size = 7;
+
+ /* create the urb and buffer for reading */
+ dev->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->int_in_urb) {
+ dev_err(&interface->dev, "Couldn't allocate interrupt_in_urb\n");
+ goto error;
+ }
+ dev->int_in_buffer = kmalloc(dev->report_size, GFP_KERNEL);
+ if (!dev->int_in_buffer) {
+ dev_err(&interface->dev, "Couldn't allocate int_in_buffer\n");
+ goto error;
+ }
+ usb_fill_int_urb(dev->int_in_urb, dev->udev,
+ usb_rcvintpipe(dev->udev,
+ dev->int_in_endpoint->bEndpointAddress),
+ dev->int_in_buffer, dev->report_size,
+ iowarrior_callback, dev,
+ dev->int_in_endpoint->bInterval);
+ /* create an internal buffer for interrupt data from the device */
+ dev->read_queue =
+ kmalloc(((dev->report_size + 1) * MAX_INTERRUPT_BUFFER),
+ GFP_KERNEL);
+ if (!dev->read_queue) {
+ dev_err(&interface->dev, "Couldn't allocate read_queue\n");
+ goto error;
+ }
+ /* Get the serial-number of the chip */
+ memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial));
+ usb_string(udev, udev->descriptor.iSerialNumber, dev->chip_serial,
+ sizeof(dev->chip_serial));
+ if (strlen(dev->chip_serial) != 8)
+ memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial));
+
+ /* Set the idle timeout to 0, if this is interface 0 */
+ if (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) {
+ usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x0A,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
+ 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ }
+ /* allow device read and ioctl */
+ dev->present = 1;
+
+ /* we can register the device now, as it is ready */
+ usb_set_intfdata(interface, dev);
+
+ retval = usb_register_dev(interface, &iowarrior_class);
+ if (retval) {
+ /* something prevented us from registering this driver */
+ dev_err(&interface->dev, "Not able to get a minor for this device.\n");
+ usb_set_intfdata(interface, NULL);
+ goto error;
+ }
+
+ dev->minor = interface->minor;
+
+ /* let the user know what node this device is now attached to */
+ dev_info(&interface->dev, "IOWarrior product=0x%x, serial=%s interface=%d "
+ "now attached to iowarrior%d\n", dev->product_id, dev->chip_serial,
+ iface_desc->desc.bInterfaceNumber, dev->minor - IOWARRIOR_MINOR_BASE);
+ return retval;
+
+error:
+ iowarrior_delete(dev);
+ return retval;
+}
+
+/**
+ * iowarrior_disconnect
+ *
+ * Called by the usb core when the device is removed from the system.
+ */
+static void iowarrior_disconnect(struct usb_interface *interface)
+{
+ struct iowarrior *dev;
+ int minor;
+
+ /* prevent races with open() */
+ down(&disconnect_sem);
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ mutex_lock(&dev->mutex);
+
+ minor = dev->minor;
+
+ /* give back our minor */
+ usb_deregister_dev(interface, &iowarrior_class);
+
+ /* prevent device read, write and ioctl */
+ dev->present = 0;
+
+ mutex_unlock(&dev->mutex);
+
+ if (dev->opened) {
+ /* There is a process that holds a filedescriptor to the device ,
+ so we only shutdown read-/write-ops going on.
+ Deleting the device is postponed until close() was called.
+ */
+ usb_kill_urb(dev->int_in_urb);
+ wake_up_interruptible(&dev->read_wait);
+ wake_up_interruptible(&dev->write_wait);
+ } else {
+ /* no process is using the device, cleanup now */
+ iowarrior_delete(dev);
+ }
+ up(&disconnect_sem);
+
+ dev_info(&interface->dev, "I/O-Warror #%d now disconnected\n",
+ minor - IOWARRIOR_MINOR_BASE);
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver iowarrior_driver = {
+ .name = "iowarrior",
+ .probe = iowarrior_probe,
+ .disconnect = iowarrior_disconnect,
+ .id_table = iowarrior_ids,
+};
+
+static int __init iowarrior_init(void)
+{
+ return usb_register(&iowarrior_driver);
+}
+
+static void __exit iowarrior_exit(void)
+{
+ usb_deregister(&iowarrior_driver);
+}
+
+module_init(iowarrior_init);
+module_exit(iowarrior_exit);
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index 788a11e6772..11555bde655 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -62,6 +62,8 @@
#define USB_DEVICE_ID_VERNIER_SKIP 0x0003
#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004
+#define USB_VENDOR_ID_MICROCHIP 0x04d8
+#define USB_DEVICE_ID_PICDEM 0x000c
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define USB_LD_MINOR_BASE 0
@@ -89,6 +91,7 @@ static struct usb_device_id ld_usb_table [] = {
{ USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
{ USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
{ USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
+ { USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICDEM) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, ld_usb_table);
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
index ada2ebc464a..887ef953f3d 100644
--- a/drivers/usb/misc/usblcd.c
+++ b/drivers/usb/misc/usblcd.c
@@ -47,6 +47,7 @@ struct usb_lcd {
#define to_lcd_dev(d) container_of(d, struct usb_lcd, kref)
static struct usb_driver lcd_driver;
+static DEFINE_MUTEX(usb_lcd_open_mutex);
static void lcd_delete(struct kref *kref)
@@ -68,6 +69,7 @@ static int lcd_open(struct inode *inode, struct file *file)
subminor = iminor(inode);
+ mutex_lock(&usb_lcd_open_mutex);
interface = usb_find_interface(&lcd_driver, subminor);
if (!interface) {
err ("USBLCD: %s - error, can't find device for minor %d",
@@ -89,6 +91,7 @@ static int lcd_open(struct inode *inode, struct file *file)
file->private_data = dev;
exit:
+ mutex_unlock(&usb_lcd_open_mutex);
return retval;
}
@@ -347,7 +350,7 @@ static void lcd_disconnect(struct usb_interface *interface)
int minor = interface->minor;
/* prevent skel_open() from racing skel_disconnect() */
- lock_kernel();
+ mutex_lock(&usb_lcd_open_mutex);
dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
@@ -355,7 +358,7 @@ static void lcd_disconnect(struct usb_interface *interface)
/* give back our minor */
usb_deregister_dev(interface, &lcd_class);
- unlock_kernel();
+ mutex_unlock(&usb_lcd_open_mutex);
/* decrement our usage count */
kref_put(&dev->kref, lcd_delete);
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index c01dfe60367..0af11a66207 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -356,8 +356,10 @@ static inline char mon_bin_get_setup(unsigned char *setupb,
if (!usb_pipecontrol(urb->pipe) || ev_type != 'S')
return '-';
- if (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)
+ if (urb->dev->bus->uses_dma &&
+ (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) {
return mon_dmapeek(setupb, urb->setup_dma, SETUP_LEN);
+ }
if (urb->setup_packet == NULL)
return 'Z';
@@ -369,7 +371,8 @@ static char mon_bin_get_data(const struct mon_reader_bin *rp,
unsigned int offset, struct urb *urb, unsigned int length)
{
- if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) {
+ if (urb->dev->bus->uses_dma &&
+ (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
mon_dmapeek_vec(rp, offset, urb->transfer_dma, length);
return 0;
}
@@ -440,7 +443,7 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
/* We use the fact that usb_pipein() returns 0x80 */
ep->epnum = usb_pipeendpoint(urb->pipe) | usb_pipein(urb->pipe);
ep->devnum = usb_pipedevice(urb->pipe);
- ep->busnum = rp->r.m_bus->u_bus->busnum;
+ ep->busnum = urb->dev->bus->busnum;
ep->id = (unsigned long) urb;
ep->ts_sec = ts.tv_sec;
ep->ts_usec = ts.tv_usec;
@@ -500,7 +503,7 @@ static void mon_bin_error(void *data, struct urb *urb, int error)
/* We use the fact that usb_pipein() returns 0x80 */
ep->epnum = usb_pipeendpoint(urb->pipe) | usb_pipein(urb->pipe);
ep->devnum = usb_pipedevice(urb->pipe);
- ep->busnum = rp->r.m_bus->u_bus->busnum;
+ ep->busnum = urb->dev->bus->busnum;
ep->id = (unsigned long) urb;
ep->status = error;
@@ -515,7 +518,6 @@ static void mon_bin_error(void *data, struct urb *urb, int error)
static int mon_bin_open(struct inode *inode, struct file *file)
{
struct mon_bus *mbus;
- struct usb_bus *ubus;
struct mon_reader_bin *rp;
size_t size;
int rc;
@@ -525,7 +527,7 @@ static int mon_bin_open(struct inode *inode, struct file *file)
mutex_unlock(&mon_lock);
return -ENODEV;
}
- if ((ubus = mbus->u_bus) == NULL) {
+ if (mbus != &mon_bus0 && mbus->u_bus == NULL) {
printk(KERN_ERR TAG ": consistency error on open\n");
mutex_unlock(&mon_lock);
return -ENODEV;
@@ -1165,7 +1167,7 @@ err_dev:
return rc;
}
-void __exit mon_bin_exit(void)
+void mon_bin_exit(void)
{
cdev_del(&mon_bin_cdev);
unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR);
diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c
index c9739e7b35e..8a1df2c9c73 100644
--- a/drivers/usb/mon/mon_main.c
+++ b/drivers/usb/mon/mon_main.c
@@ -16,8 +16,6 @@
#include "usb_mon.h"
#include "../core/hcd.h"
-static void mon_submit(struct usb_bus *ubus, struct urb *urb);
-static void mon_complete(struct usb_bus *ubus, struct urb *urb);
static void mon_stop(struct mon_bus *mbus);
static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus);
static void mon_bus_drop(struct kref *r);
@@ -25,6 +23,7 @@ static void mon_bus_init(struct usb_bus *ubus);
DEFINE_MUTEX(mon_lock);
+struct mon_bus mon_bus0; /* Pseudo bus meaning "all buses" */
static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */
/*
@@ -35,22 +34,19 @@ static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */
void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r)
{
unsigned long flags;
- struct usb_bus *ubus;
+ struct list_head *p;
spin_lock_irqsave(&mbus->lock, flags);
if (mbus->nreaders == 0) {
- ubus = mbus->u_bus;
- if (ubus->monitored) {
- /*
- * Something is really broken, refuse to go on and
- * possibly corrupt ops pointers or worse.
- */
- printk(KERN_ERR TAG ": bus %d is already monitored\n",
- ubus->busnum);
- spin_unlock_irqrestore(&mbus->lock, flags);
- return;
+ if (mbus == &mon_bus0) {
+ list_for_each (p, &mon_buses) {
+ struct mon_bus *m1;
+ m1 = list_entry(p, struct mon_bus, bus_link);
+ m1->u_bus->monitored = 1;
+ }
+ } else {
+ mbus->u_bus->monitored = 1;
}
- ubus->monitored = 1;
}
mbus->nreaders++;
list_add_tail(&r->r_link, &mbus->r_list);
@@ -80,77 +76,79 @@ void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r)
/*
*/
-static void mon_submit(struct usb_bus *ubus, struct urb *urb)
+static void mon_bus_submit(struct mon_bus *mbus, struct urb *urb)
{
- struct mon_bus *mbus;
unsigned long flags;
struct list_head *pos;
struct mon_reader *r;
- mbus = ubus->mon_bus;
- if (mbus == NULL)
- goto out_unlocked;
-
spin_lock_irqsave(&mbus->lock, flags);
- if (mbus->nreaders == 0)
- goto out_locked;
-
mbus->cnt_events++;
list_for_each (pos, &mbus->r_list) {
r = list_entry(pos, struct mon_reader, r_link);
r->rnf_submit(r->r_data, urb);
}
-
spin_unlock_irqrestore(&mbus->lock, flags);
return;
+}
-out_locked:
- spin_unlock_irqrestore(&mbus->lock, flags);
-out_unlocked:
- return;
+static void mon_submit(struct usb_bus *ubus, struct urb *urb)
+{
+ struct mon_bus *mbus;
+
+ if ((mbus = ubus->mon_bus) != NULL)
+ mon_bus_submit(mbus, urb);
+ mon_bus_submit(&mon_bus0, urb);
}
/*
*/
-static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error)
+static void mon_bus_submit_error(struct mon_bus *mbus, struct urb *urb, int error)
{
- struct mon_bus *mbus;
unsigned long flags;
struct list_head *pos;
struct mon_reader *r;
- mbus = ubus->mon_bus;
- if (mbus == NULL)
- goto out_unlocked;
-
spin_lock_irqsave(&mbus->lock, flags);
- if (mbus->nreaders == 0)
- goto out_locked;
-
mbus->cnt_events++;
list_for_each (pos, &mbus->r_list) {
r = list_entry(pos, struct mon_reader, r_link);
r->rnf_error(r->r_data, urb, error);
}
-
spin_unlock_irqrestore(&mbus->lock, flags);
return;
+}
-out_locked:
- spin_unlock_irqrestore(&mbus->lock, flags);
-out_unlocked:
- return;
+static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error)
+{
+ struct mon_bus *mbus;
+
+ if ((mbus = ubus->mon_bus) != NULL)
+ mon_bus_submit_error(mbus, urb, error);
+ mon_bus_submit_error(&mon_bus0, urb, error);
}
/*
*/
-static void mon_complete(struct usb_bus *ubus, struct urb *urb)
+static void mon_bus_complete(struct mon_bus *mbus, struct urb *urb)
{
- struct mon_bus *mbus;
unsigned long flags;
struct list_head *pos;
struct mon_reader *r;
+ spin_lock_irqsave(&mbus->lock, flags);
+ mbus->cnt_events++;
+ list_for_each (pos, &mbus->r_list) {
+ r = list_entry(pos, struct mon_reader, r_link);
+ r->rnf_complete(r->r_data, urb);
+ }
+ spin_unlock_irqrestore(&mbus->lock, flags);
+}
+
+static void mon_complete(struct usb_bus *ubus, struct urb *urb)
+{
+ struct mon_bus *mbus;
+
mbus = ubus->mon_bus;
if (mbus == NULL) {
/*
@@ -162,13 +160,8 @@ static void mon_complete(struct usb_bus *ubus, struct urb *urb)
return;
}
- spin_lock_irqsave(&mbus->lock, flags);
- mbus->cnt_events++;
- list_for_each (pos, &mbus->r_list) {
- r = list_entry(pos, struct mon_reader, r_link);
- r->rnf_complete(r->r_data, urb);
- }
- spin_unlock_irqrestore(&mbus->lock, flags);
+ mon_bus_complete(mbus, urb);
+ mon_bus_complete(&mon_bus0, urb);
}
/* int (*unlink_urb) (struct urb *urb, int status); */
@@ -179,14 +172,26 @@ static void mon_complete(struct usb_bus *ubus, struct urb *urb)
static void mon_stop(struct mon_bus *mbus)
{
struct usb_bus *ubus = mbus->u_bus;
+ struct list_head *p;
- /*
- * A stop can be called for a dissolved mon_bus in case of
- * a reader staying across an rmmod foo_hcd.
- */
- if (ubus != NULL) {
- ubus->monitored = 0;
- mb();
+ if (mbus == &mon_bus0) {
+ list_for_each (p, &mon_buses) {
+ mbus = list_entry(p, struct mon_bus, bus_link);
+ /*
+ * We do not change nreaders here, so rely on mon_lock.
+ */
+ if (mbus->nreaders == 0 && (ubus = mbus->u_bus) != NULL)
+ ubus->monitored = 0;
+ }
+ } else {
+ /*
+ * A stop can be called for a dissolved mon_bus in case of
+ * a reader staying across an rmmod foo_hcd, so test ->u_bus.
+ */
+ if (mon_bus0.nreaders == 0 && (ubus = mbus->u_bus) != NULL) {
+ ubus->monitored = 0;
+ mb();
+ }
}
}
@@ -199,6 +204,10 @@ static void mon_stop(struct mon_bus *mbus)
static void mon_bus_add(struct usb_bus *ubus)
{
mon_bus_init(ubus);
+ mutex_lock(&mon_lock);
+ if (mon_bus0.nreaders != 0)
+ ubus->monitored = 1;
+ mutex_unlock(&mon_lock);
}
/*
@@ -250,12 +259,7 @@ static struct usb_mon_operations mon_ops_0 = {
static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus)
{
- /*
- * Never happens, but...
- */
if (ubus->monitored) {
- printk(KERN_ERR TAG ": bus %d is dissolved while monitored\n",
- ubus->busnum);
ubus->monitored = 0;
mb();
}
@@ -263,6 +267,8 @@ static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus)
ubus->mon_bus = NULL;
mbus->u_bus = NULL;
mb();
+
+ /* We want synchronize_irq() here, but that needs an argument. */
}
/*
@@ -295,9 +301,8 @@ static void mon_bus_init(struct usb_bus *ubus)
*/
mbus->u_bus = ubus;
ubus->mon_bus = mbus;
- mbus->uses_dma = ubus->uses_dma;
- mbus->text_inited = mon_text_add(mbus, ubus);
+ mbus->text_inited = mon_text_add(mbus, ubus->busnum);
// mon_bin_add(...)
mutex_lock(&mon_lock);
@@ -309,6 +314,18 @@ err_alloc:
return;
}
+static void mon_bus0_init(void)
+{
+ struct mon_bus *mbus = &mon_bus0;
+
+ kref_init(&mbus->ref);
+ spin_lock_init(&mbus->lock);
+ INIT_LIST_HEAD(&mbus->r_list);
+
+ mbus->text_inited = mon_text_add(mbus, 0);
+ // mbus->bin_inited = mon_bin_add(mbus, 0);
+}
+
/*
* Search a USB bus by number. Notice that USB bus numbers start from one,
* which we may later use to identify "all" with zero.
@@ -322,6 +339,9 @@ struct mon_bus *mon_bus_lookup(unsigned int num)
struct list_head *p;
struct mon_bus *mbus;
+ if (num == 0) {
+ return &mon_bus0;
+ }
list_for_each (p, &mon_buses) {
mbus = list_entry(p, struct mon_bus, bus_link);
if (mbus->u_bus->busnum == num) {
@@ -341,6 +361,8 @@ static int __init mon_init(void)
if ((rc = mon_bin_init()) != 0)
goto err_bin;
+ mon_bus0_init();
+
if (usb_mon_register(&mon_ops_0) != 0) {
printk(KERN_NOTICE TAG ": unable to register with the core\n");
rc = -ENODEV;
@@ -374,6 +396,7 @@ static void __exit mon_exit(void)
usb_mon_deregister();
mutex_lock(&mon_lock);
+
while (!list_empty(&mon_buses)) {
p = mon_buses.next;
mbus = list_entry(p, struct mon_bus, bus_link);
@@ -397,6 +420,11 @@ static void __exit mon_exit(void)
mon_dissolve(mbus, mbus->u_bus);
kref_put(&mbus->ref, mon_bus_drop);
}
+
+ mbus = &mon_bus0;
+ if (mbus->text_inited)
+ mon_text_del(mbus);
+
mutex_unlock(&mon_lock);
mon_text_exit();
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index d38a1279d9d..ec0cc51e39a 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -31,9 +31,21 @@
* to a local DoS. But we have to keep to root in order to prevent
* password sniffing from HID devices.
*/
-#define EVENT_MAX (2*PAGE_SIZE / sizeof(struct mon_event_text))
+#define EVENT_MAX (4*PAGE_SIZE / sizeof(struct mon_event_text))
-#define PRINTF_DFL 160
+/*
+ * Potentially unlimited number; we limit it for similar allocations.
+ * The usbfs limits this to 128, but we're not quite as generous.
+ */
+#define ISODESC_MAX 5
+
+#define PRINTF_DFL 250 /* with 5 ISOs segs */
+
+struct mon_iso_desc {
+ int status;
+ unsigned int offset;
+ unsigned int length; /* Unsigned here, signed in URB. Historic. */
+};
struct mon_event_text {
struct list_head e_link;
@@ -41,10 +53,16 @@ struct mon_event_text {
unsigned int pipe; /* Pipe */
unsigned long id; /* From pointer, most of the time */
unsigned int tstamp;
+ int busnum;
int length; /* Depends on type: xfer length or act length */
int status;
+ int interval;
+ int start_frame;
+ int error_count;
char setup_flag;
char data_flag;
+ int numdesc; /* Full number */
+ struct mon_iso_desc isodesc[ISODESC_MAX];
unsigned char setup[SETUP_MAX];
unsigned char data[DATA_MAX];
};
@@ -68,6 +86,28 @@ static struct dentry *mon_dir; /* Usually /sys/kernel/debug/usbmon */
static void mon_text_ctor(void *, struct kmem_cache *, unsigned long);
+struct mon_text_ptr {
+ int cnt, limit;
+ char *pbuf;
+};
+
+static struct mon_event_text *
+ mon_text_read_wait(struct mon_reader_text *rp, struct file *file);
+static void mon_text_read_head_t(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_head_u(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_statset(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_intstat(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_isostat(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_isodesc(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_data(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep);
+
/*
* mon_text_submit
* mon_text_complete
@@ -84,8 +124,10 @@ static inline char mon_text_get_setup(struct mon_event_text *ep,
if (!usb_pipecontrol(urb->pipe) || ev_type != 'S')
return '-';
- if (mbus->uses_dma && (urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
+ if (urb->dev->bus->uses_dma &&
+ (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) {
return mon_dmapeek(ep->setup, urb->setup_dma, SETUP_MAX);
+ }
if (urb->setup_packet == NULL)
return 'Z'; /* '0' would be not as pretty. */
@@ -104,10 +146,10 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
len = DATA_MAX;
if (usb_pipein(pipe)) {
- if (ev_type == 'S')
+ if (ev_type != 'C')
return '<';
} else {
- if (ev_type == 'C')
+ if (ev_type != 'S')
return '>';
}
@@ -120,8 +162,10 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
* contain non-NULL garbage in case the upper level promised to
* set DMA for the HCD.
*/
- if (mbus->uses_dma && (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
+ if (urb->dev->bus->uses_dma &&
+ (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
return mon_dmapeek(ep->data, urb->transfer_dma, len);
+ }
if (urb->transfer_buffer == NULL)
return 'Z'; /* '0' would be not as pretty. */
@@ -146,6 +190,9 @@ static void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
{
struct mon_event_text *ep;
unsigned int stamp;
+ struct usb_iso_packet_descriptor *fp;
+ struct mon_iso_desc *dp;
+ int i, ndesc;
stamp = mon_get_timestamp();
@@ -158,12 +205,36 @@ static void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
ep->type = ev_type;
ep->pipe = urb->pipe;
ep->id = (unsigned long) urb;
+ ep->busnum = urb->dev->bus->busnum;
ep->tstamp = stamp;
ep->length = (ev_type == 'S') ?
urb->transfer_buffer_length : urb->actual_length;
/* Collecting status makes debugging sense for submits, too */
ep->status = urb->status;
+ if (usb_pipeint(urb->pipe)) {
+ ep->interval = urb->interval;
+ } else if (usb_pipeisoc(urb->pipe)) {
+ ep->interval = urb->interval;
+ ep->start_frame = urb->start_frame;
+ ep->error_count = urb->error_count;
+ }
+ ep->numdesc = urb->number_of_packets;
+ if (usb_pipeisoc(urb->pipe) && urb->number_of_packets > 0) {
+ if ((ndesc = urb->number_of_packets) > ISODESC_MAX)
+ ndesc = ISODESC_MAX;
+ fp = urb->iso_frame_desc;
+ dp = ep->isodesc;
+ for (i = 0; i < ndesc; i++) {
+ dp->status = fp->status;
+ dp->offset = fp->offset;
+ dp->length = (ev_type == 'S') ?
+ fp->length : fp->actual_length;
+ fp++;
+ dp++;
+ }
+ }
+
ep->setup_flag = mon_text_get_setup(ep, urb, ev_type, rp->r.m_bus);
ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type,
rp->r.m_bus);
@@ -199,6 +270,7 @@ static void mon_text_error(void *data, struct urb *urb, int error)
ep->type = 'E';
ep->pipe = urb->pipe;
ep->id = (unsigned long) urb;
+ ep->busnum = 0;
ep->tstamp = 0;
ep->length = 0;
ep->status = error;
@@ -237,13 +309,11 @@ static struct mon_event_text *mon_text_fetch(struct mon_reader_text *rp,
static int mon_text_open(struct inode *inode, struct file *file)
{
struct mon_bus *mbus;
- struct usb_bus *ubus;
struct mon_reader_text *rp;
int rc;
mutex_lock(&mon_lock);
mbus = inode->i_private;
- ubus = mbus->u_bus;
rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL);
if (rp == NULL) {
@@ -267,8 +337,7 @@ static int mon_text_open(struct inode *inode, struct file *file)
rp->r.rnf_error = mon_text_error;
rp->r.rnf_complete = mon_text_complete;
- snprintf(rp->slab_name, SLAB_NAME_SZ, "mon%dt_%lx", ubus->busnum,
- (long)rp);
+ snprintf(rp->slab_name, SLAB_NAME_SZ, "mon_text_%p", rp);
rp->e_slab = kmem_cache_create(rp->slab_name,
sizeof(struct mon_event_text), sizeof(long), 0,
mon_text_ctor, NULL);
@@ -300,17 +369,75 @@ err_alloc:
* dd if=/dbg/usbmon/0t bs=10
* Also, we do not allow seeks and do not bother advancing the offset.
*/
-static ssize_t mon_text_read(struct file *file, char __user *buf,
+static ssize_t mon_text_read_t(struct file *file, char __user *buf,
size_t nbytes, loff_t *ppos)
{
struct mon_reader_text *rp = file->private_data;
+ struct mon_event_text *ep;
+ struct mon_text_ptr ptr;
+
+ if (IS_ERR(ep = mon_text_read_wait(rp, file)))
+ return PTR_ERR(ep);
+ mutex_lock(&rp->printf_lock);
+ ptr.cnt = 0;
+ ptr.pbuf = rp->printf_buf;
+ ptr.limit = rp->printf_size;
+
+ mon_text_read_head_t(rp, &ptr, ep);
+ mon_text_read_statset(rp, &ptr, ep);
+ ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
+ " %d", ep->length);
+ mon_text_read_data(rp, &ptr, ep);
+
+ if (copy_to_user(buf, rp->printf_buf, ptr.cnt))
+ ptr.cnt = -EFAULT;
+ mutex_unlock(&rp->printf_lock);
+ kmem_cache_free(rp->e_slab, ep);
+ return ptr.cnt;
+}
+
+static ssize_t mon_text_read_u(struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct mon_reader_text *rp = file->private_data;
+ struct mon_event_text *ep;
+ struct mon_text_ptr ptr;
+
+ if (IS_ERR(ep = mon_text_read_wait(rp, file)))
+ return PTR_ERR(ep);
+ mutex_lock(&rp->printf_lock);
+ ptr.cnt = 0;
+ ptr.pbuf = rp->printf_buf;
+ ptr.limit = rp->printf_size;
+
+ mon_text_read_head_u(rp, &ptr, ep);
+ if (ep->type == 'E') {
+ mon_text_read_statset(rp, &ptr, ep);
+ } else if (usb_pipeisoc(ep->pipe)) {
+ mon_text_read_isostat(rp, &ptr, ep);
+ mon_text_read_isodesc(rp, &ptr, ep);
+ } else if (usb_pipeint(ep->pipe)) {
+ mon_text_read_intstat(rp, &ptr, ep);
+ } else {
+ mon_text_read_statset(rp, &ptr, ep);
+ }
+ ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
+ " %d", ep->length);
+ mon_text_read_data(rp, &ptr, ep);
+
+ if (copy_to_user(buf, rp->printf_buf, ptr.cnt))
+ ptr.cnt = -EFAULT;
+ mutex_unlock(&rp->printf_lock);
+ kmem_cache_free(rp->e_slab, ep);
+ return ptr.cnt;
+}
+
+static struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp,
+ struct file *file)
+{
struct mon_bus *mbus = rp->r.m_bus;
DECLARE_WAITQUEUE(waita, current);
struct mon_event_text *ep;
- int cnt, limit;
- char *pbuf;
- char udir, utype;
- int data_len, i;
add_wait_queue(&rp->wait, &waita);
set_current_state(TASK_INTERRUPTIBLE);
@@ -318,7 +445,7 @@ static ssize_t mon_text_read(struct file *file, char __user *buf,
if (file->f_flags & O_NONBLOCK) {
set_current_state(TASK_RUNNING);
remove_wait_queue(&rp->wait, &waita);
- return -EWOULDBLOCK; /* Same as EAGAIN in Linux */
+ return ERR_PTR(-EWOULDBLOCK);
}
/*
* We do not count nwaiters, because ->release is supposed
@@ -327,17 +454,19 @@ static ssize_t mon_text_read(struct file *file, char __user *buf,
schedule();
if (signal_pending(current)) {
remove_wait_queue(&rp->wait, &waita);
- return -EINTR;
+ return ERR_PTR(-EINTR);
}
set_current_state(TASK_INTERRUPTIBLE);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&rp->wait, &waita);
+ return ep;
+}
- mutex_lock(&rp->printf_lock);
- cnt = 0;
- pbuf = rp->printf_buf;
- limit = rp->printf_size;
+static void mon_text_read_head_t(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+ char udir, utype;
udir = usb_pipein(ep->pipe) ? 'i' : 'o';
switch (usb_pipetype(ep->pipe)) {
@@ -346,13 +475,38 @@ static ssize_t mon_text_read(struct file *file, char __user *buf,
case PIPE_CONTROL: utype = 'C'; break;
default: /* PIPE_BULK */ utype = 'B';
}
- cnt += snprintf(pbuf + cnt, limit - cnt,
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
"%lx %u %c %c%c:%03u:%02u",
ep->id, ep->tstamp, ep->type,
- utype, udir, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe));
+ utype, udir,
+ usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe));
+}
+
+static void mon_text_read_head_u(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+ char udir, utype;
+
+ udir = usb_pipein(ep->pipe) ? 'i' : 'o';
+ switch (usb_pipetype(ep->pipe)) {
+ case PIPE_ISOCHRONOUS: utype = 'Z'; break;
+ case PIPE_INTERRUPT: utype = 'I'; break;
+ case PIPE_CONTROL: utype = 'C'; break;
+ default: /* PIPE_BULK */ utype = 'B';
+ }
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ "%lx %u %c %c%c:%d:%03u:%u",
+ ep->id, ep->tstamp, ep->type,
+ utype, udir,
+ ep->busnum, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe));
+}
+
+static void mon_text_read_statset(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
if (ep->setup_flag == 0) { /* Setup packet is present and captured */
- cnt += snprintf(pbuf + cnt, limit - cnt,
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
" s %02x %02x %04x %04x %04x",
ep->setup[0],
ep->setup[1],
@@ -360,40 +514,86 @@ static ssize_t mon_text_read(struct file *file, char __user *buf,
(ep->setup[5] << 8) | ep->setup[4],
(ep->setup[7] << 8) | ep->setup[6]);
} else if (ep->setup_flag != '-') { /* Unable to capture setup packet */
- cnt += snprintf(pbuf + cnt, limit - cnt,
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
" %c __ __ ____ ____ ____", ep->setup_flag);
} else { /* No setup for this kind of URB */
- cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->status);
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ " %d", ep->status);
}
- cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->length);
+}
+
+static void mon_text_read_intstat(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ " %d:%d", ep->status, ep->interval);
+}
+
+static void mon_text_read_isostat(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+ if (ep->type == 'S') {
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ " %d:%d:%d", ep->status, ep->interval, ep->start_frame);
+ } else {
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ " %d:%d:%d:%d",
+ ep->status, ep->interval, ep->start_frame, ep->error_count);
+ }
+}
+
+static void mon_text_read_isodesc(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+ int ndesc; /* Display this many */
+ int i;
+ const struct mon_iso_desc *dp;
+
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ " %d", ep->numdesc);
+ ndesc = ep->numdesc;
+ if (ndesc > ISODESC_MAX)
+ ndesc = ISODESC_MAX;
+ if (ndesc < 0)
+ ndesc = 0;
+ dp = ep->isodesc;
+ for (i = 0; i < ndesc; i++) {
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ " %d:%u:%u", dp->status, dp->offset, dp->length);
+ dp++;
+ }
+}
+
+static void mon_text_read_data(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+ int data_len, i;
if ((data_len = ep->length) > 0) {
if (ep->data_flag == 0) {
- cnt += snprintf(pbuf + cnt, limit - cnt, " =");
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ " =");
if (data_len >= DATA_MAX)
data_len = DATA_MAX;
for (i = 0; i < data_len; i++) {
if (i % 4 == 0) {
- cnt += snprintf(pbuf + cnt, limit - cnt,
+ p->cnt += snprintf(p->pbuf + p->cnt,
+ p->limit - p->cnt,
" ");
}
- cnt += snprintf(pbuf + cnt, limit - cnt,
+ p->cnt += snprintf(p->pbuf + p->cnt,
+ p->limit - p->cnt,
"%02x", ep->data[i]);
}
- cnt += snprintf(pbuf + cnt, limit - cnt, "\n");
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ "\n");
} else {
- cnt += snprintf(pbuf + cnt, limit - cnt,
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
" %c\n", ep->data_flag);
}
} else {
- cnt += snprintf(pbuf + cnt, limit - cnt, "\n");
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, "\n");
}
-
- if (copy_to_user(buf, rp->printf_buf, cnt))
- cnt = -EFAULT;
- mutex_unlock(&rp->printf_lock);
- kmem_cache_free(rp->e_slab, ep);
- return cnt;
}
static int mon_text_release(struct inode *inode, struct file *file)
@@ -439,34 +639,46 @@ static int mon_text_release(struct inode *inode, struct file *file)
return 0;
}
-static const struct file_operations mon_fops_text = {
+static const struct file_operations mon_fops_text_t = {
.owner = THIS_MODULE,
.open = mon_text_open,
.llseek = no_llseek,
- .read = mon_text_read,
- /* .write = mon_text_write, */
- /* .poll = mon_text_poll, */
- /* .ioctl = mon_text_ioctl, */
+ .read = mon_text_read_t,
.release = mon_text_release,
};
-int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus)
+static const struct file_operations mon_fops_text_u = {
+ .owner = THIS_MODULE,
+ .open = mon_text_open,
+ .llseek = no_llseek,
+ .read = mon_text_read_u,
+ .release = mon_text_release,
+};
+
+int mon_text_add(struct mon_bus *mbus, int busnum)
{
struct dentry *d;
enum { NAMESZ = 10 };
char name[NAMESZ];
int rc;
- rc = snprintf(name, NAMESZ, "%dt", ubus->busnum);
+ rc = snprintf(name, NAMESZ, "%dt", busnum);
if (rc <= 0 || rc >= NAMESZ)
goto err_print_t;
- d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text);
+ d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_t);
if (d == NULL)
goto err_create_t;
mbus->dent_t = d;
- /* XXX The stats do not belong to here (text API), but oh well... */
- rc = snprintf(name, NAMESZ, "%ds", ubus->busnum);
+ rc = snprintf(name, NAMESZ, "%du", busnum);
+ if (rc <= 0 || rc >= NAMESZ)
+ goto err_print_u;
+ d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_u);
+ if (d == NULL)
+ goto err_create_u;
+ mbus->dent_u = d;
+
+ rc = snprintf(name, NAMESZ, "%ds", busnum);
if (rc <= 0 || rc >= NAMESZ)
goto err_print_s;
d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_stat);
@@ -478,6 +690,10 @@ int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus)
err_create_s:
err_print_s:
+ debugfs_remove(mbus->dent_u);
+ mbus->dent_u = NULL;
+err_create_u:
+err_print_u:
debugfs_remove(mbus->dent_t);
mbus->dent_t = NULL;
err_create_t:
@@ -487,6 +703,7 @@ err_print_t:
void mon_text_del(struct mon_bus *mbus)
{
+ debugfs_remove(mbus->dent_u);
debugfs_remove(mbus->dent_t);
debugfs_remove(mbus->dent_s);
}
@@ -520,7 +737,7 @@ int __init mon_text_init(void)
return 0;
}
-void __exit mon_text_exit(void)
+void mon_text_exit(void)
{
debugfs_remove(mon_dir);
}
diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h
index 4f949ce8a7f..13d63255283 100644
--- a/drivers/usb/mon/usb_mon.h
+++ b/drivers/usb/mon/usb_mon.h
@@ -22,7 +22,7 @@ struct mon_bus {
int text_inited;
struct dentry *dent_s; /* Debugging file */
struct dentry *dent_t; /* Text interface file */
- int uses_dma;
+ struct dentry *dent_u; /* Second text interface file */
/* Ref */
int nreaders; /* Under mon_lock AND mbus->lock */
@@ -52,14 +52,14 @@ void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r);
struct mon_bus *mon_bus_lookup(unsigned int num);
-int /*bool*/ mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus);
+int /*bool*/ mon_text_add(struct mon_bus *mbus, int busnum);
void mon_text_del(struct mon_bus *mbus);
// void mon_bin_add(struct mon_bus *);
int __init mon_text_init(void);
-void __exit mon_text_exit(void);
+void mon_text_exit(void);
int __init mon_bin_init(void);
-void __exit mon_bin_exit(void);
+void mon_bin_exit(void);
/*
* DMA interface.
@@ -81,4 +81,6 @@ extern struct mutex mon_lock;
extern const struct file_operations mon_fops_stat;
+extern struct mon_bus mon_bus0; /* Only for redundant checks */
+
#endif /* __USB_MON_H */
diff --git a/drivers/usb/net/Kconfig b/drivers/usb/net/Kconfig
index a2b94ef512b..3de564b2314 100644
--- a/drivers/usb/net/Kconfig
+++ b/drivers/usb/net/Kconfig
@@ -84,6 +84,7 @@ config USB_PEGASUS
config USB_RTL8150
tristate "USB RTL8150 based ethernet device support (EXPERIMENTAL)"
depends on EXPERIMENTAL
+ select MII
help
Say Y here if you have RTL8150 based usb-ethernet adapter.
Send me <petkan@users.sourceforge.net> any comments you may have.
@@ -98,7 +99,7 @@ config USB_USBNET_MII
config USB_USBNET
tristate "Multi-purpose USB Networking Framework"
- select MII if USBNET_MII != n
+ select MII if USB_USBNET_MII != n
---help---
This driver supports several kinds of network links over USB,
with "minidrivers" built around a common network driver core
@@ -185,6 +186,15 @@ config USB_NET_CDCETHER
IEEE 802 "local assignment" bit is set in the address, a "usbX"
name is used instead.
+config USB_NET_DM9601
+ tristate "Davicom DM9601 based USB 1.1 10/100 ethernet devices"
+ depends on USB_USBNET
+ select CRC32
+ select USB_USBNET_MII
+ help
+ This option adds support for Davicom DM9601 based USB 1.1
+ 10/100 Ethernet adapters.
+
config USB_NET_GL620A
tristate "GeneSys GL620USB-A based cables"
depends on USB_USBNET
@@ -239,6 +249,7 @@ config USB_NET_RNDIS_HOST
config USB_NET_CDC_SUBSET
tristate "Simple USB Network Links (CDC Ethernet subset)"
depends on USB_USBNET
+ default y
help
This driver module supports USB network devices that can work
without any device-specific information. Select it if you have
@@ -298,6 +309,13 @@ config USB_EPSON2888
Choose this option to support the usb networking links used
by some sample firmware from Epson.
+config USB_KC2190
+ boolean "KT Technology KC2190 based cables (InstaNet)"
+ depends on USB_NET_CDC_SUBSET && EXPERIMENTAL
+ help
+  Choose this option if you're using a host-to-host cable
+  with one of these chips.
+
config USB_NET_ZAURUS
tristate "Sharp Zaurus (stock ROMs) and compatible"
depends on USB_USBNET
diff --git a/drivers/usb/net/Makefile b/drivers/usb/net/Makefile
index 7b51964de17..595a539f838 100644
--- a/drivers/usb/net/Makefile
+++ b/drivers/usb/net/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_USB_PEGASUS) += pegasus.o
obj-$(CONFIG_USB_RTL8150) += rtl8150.o
obj-$(CONFIG_USB_NET_AX8817X) += asix.o
obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o
+obj-$(CONFIG_USB_NET_DM9601) += dm9601.o
obj-$(CONFIG_USB_NET_GL620A) += gl620a.o
obj-$(CONFIG_USB_NET_NET1080) += net1080.o
obj-$(CONFIG_USB_NET_PLUSB) += plusb.o
diff --git a/drivers/usb/net/asix.c b/drivers/usb/net/asix.c
index bd357e178e5..d5ef97bc4d0 100644
--- a/drivers/usb/net/asix.c
+++ b/drivers/usb/net/asix.c
@@ -298,7 +298,7 @@ static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
if (ax_skb) {
ax_skb->len = size;
ax_skb->data = packet;
- ax_skb->tail = packet + size;
+ skb_set_tail_pointer(ax_skb, size);
usbnet_skb_return(dev, ax_skb);
} else {
return 0;
@@ -338,7 +338,7 @@ static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
&& ((headroom + tailroom) >= (4 + padlen))) {
if ((headroom < 4) || (tailroom < padlen)) {
skb->data = memmove(skb->head + 4, skb->data, skb->len);
- skb->tail = skb->data + skb->len;
+ skb_set_tail_pointer(skb, skb->len);
}
} else {
struct sk_buff *skb2;
@@ -351,10 +351,12 @@ static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
skb_push(skb, 4);
packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
- memcpy(skb->data, &packet_len, sizeof(packet_len));
+ cpu_to_le32s(&packet_len);
+ skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len));
if ((skb->len % 512) == 0) {
- memcpy( skb->tail, &padbytes, sizeof(padbytes));
+ cpu_to_le32s(&padbytes);
+ memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes));
skb_put(skb, sizeof(padbytes));
}
return skb;
@@ -1393,9 +1395,9 @@ static const struct usb_device_id products [] = {
USB_DEVICE (0x07b8, 0x420a),
.driver_info = (unsigned long) &hawking_uf200_info,
}, {
- // Billionton Systems, USB2AR
- USB_DEVICE (0x08dd, 0x90ff),
- .driver_info = (unsigned long) &ax8817x_info,
+ // Billionton Systems, USB2AR
+ USB_DEVICE (0x08dd, 0x90ff),
+ .driver_info = (unsigned long) &ax8817x_info,
}, {
// ATEN UC210T
USB_DEVICE (0x0557, 0x2009),
@@ -1421,9 +1423,13 @@ static const struct usb_device_id products [] = {
USB_DEVICE (0x1631, 0x6200),
.driver_info = (unsigned long) &ax8817x_info,
}, {
+ // JVC MP-PRX1 Port Replicator
+ USB_DEVICE (0x04f1, 0x3008),
+ .driver_info = (unsigned long) &ax8817x_info,
+}, {
// ASIX AX88772 10/100
- USB_DEVICE (0x0b95, 0x7720),
- .driver_info = (unsigned long) &ax88772_info,
+ USB_DEVICE (0x0b95, 0x7720),
+ .driver_info = (unsigned long) &ax88772_info,
}, {
// ASIX AX88178 10/100/1000
USB_DEVICE (0x0b95, 0x1780),
diff --git a/drivers/usb/net/catc.c b/drivers/usb/net/catc.c
index 4852012735f..86e90c59d55 100644
--- a/drivers/usb/net/catc.c
+++ b/drivers/usb/net/catc.c
@@ -255,7 +255,6 @@ static void catc_rx_done(struct urb *urb)
if (!(skb = dev_alloc_skb(pkt_len)))
return;
- skb->dev = catc->netdev;
eth_copy_and_sum(skb, pkt_start + pkt_offset, pkt_len, 0);
skb_put(skb, pkt_len);
@@ -356,7 +355,7 @@ resubmit:
* Transmit routines.
*/
-static void catc_tx_run(struct catc *catc)
+static int catc_tx_run(struct catc *catc)
{
int status;
@@ -374,12 +373,14 @@ static void catc_tx_run(struct catc *catc)
catc->tx_ptr = 0;
catc->netdev->trans_start = jiffies;
+ return status;
}
static void catc_tx_done(struct urb *urb)
{
struct catc *catc = urb->context;
unsigned long flags;
+ int r;
if (urb->status == -ECONNRESET) {
dbg("Tx Reset.");
@@ -398,10 +399,13 @@ static void catc_tx_done(struct urb *urb)
spin_lock_irqsave(&catc->tx_lock, flags);
- if (catc->tx_ptr)
- catc_tx_run(catc);
- else
+ if (catc->tx_ptr) {
+ r = catc_tx_run(catc);
+ if (unlikely(r < 0))
+ clear_bit(TX_RUNNING, &catc->flags);
+ } else {
clear_bit(TX_RUNNING, &catc->flags);
+ }
netif_wake_queue(catc->netdev);
@@ -412,6 +416,7 @@ static int catc_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct catc *catc = netdev_priv(netdev);
unsigned long flags;
+ int r = 0;
char *tx_buf;
spin_lock_irqsave(&catc->tx_lock, flags);
@@ -419,11 +424,14 @@ static int catc_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev)
catc->tx_ptr = (((catc->tx_ptr - 1) >> 6) + 1) << 6;
tx_buf = catc->tx_buf[catc->tx_idx] + catc->tx_ptr;
*((u16*)tx_buf) = (catc->is_f5u011) ? cpu_to_be16((u16)skb->len) : cpu_to_le16((u16)skb->len);
- memcpy(tx_buf + 2, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, tx_buf + 2, skb->len);
catc->tx_ptr += skb->len + 2;
- if (!test_and_set_bit(TX_RUNNING, &catc->flags))
- catc_tx_run(catc);
+ if (!test_and_set_bit(TX_RUNNING, &catc->flags)) {
+ r = catc_tx_run(catc);
+ if (r < 0)
+ clear_bit(TX_RUNNING, &catc->flags);
+ }
if ((catc->is_f5u011 && catc->tx_ptr)
|| (catc->tx_ptr >= ((TX_MAX_BURST - 1) * (PKT_SZ + 2))))
@@ -431,8 +439,10 @@ static int catc_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev)
spin_unlock_irqrestore(&catc->tx_lock, flags);
- catc->stats.tx_bytes += skb->len;
- catc->stats.tx_packets++;
+ if (r >= 0) {
+ catc->stats.tx_bytes += skb->len;
+ catc->stats.tx_packets++;
+ }
dev_kfree_skb(skb);
diff --git a/drivers/usb/net/cdc_subset.c b/drivers/usb/net/cdc_subset.c
index ae8fb06cf38..bc62b012602 100644
--- a/drivers/usb/net/cdc_subset.c
+++ b/drivers/usb/net/cdc_subset.c
@@ -79,13 +79,19 @@ static int always_connected (struct usbnet *dev)
*
* ALi M5632 driver ... does high speed
*
+ * NOTE that the MS-Windows drivers for this chip use some funky and
+ * (naturally) undocumented 7-byte prefix to each packet, so this is a
+ * case where we don't currently interoperate. Also, once you unplug
+ * one end of the cable, you need to replug the other end too ... since
+ * chip docs are unavailable, there's no way to reset the relevant state
+ * short of a power cycle.
+ *
*-------------------------------------------------------------------------*/
static const struct driver_info ali_m5632_info = {
.description = "ALi M5632",
};
-
#endif
@@ -159,6 +165,11 @@ static const struct driver_info epson2888_info = {
#endif /* CONFIG_USB_EPSON2888 */
+/*-------------------------------------------------------------------------
+ *
+ * info from Jonathan McDowell <noodles@earth.li>
+ *
+ *-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_KC2190
#define HAVE_HARDWARE
static const struct driver_info kc2190_info = {
@@ -223,6 +234,10 @@ static const struct usb_device_id products [] = {
USB_DEVICE (0x0402, 0x5632), // ALi defaults
.driver_info = (unsigned long) &ali_m5632_info,
},
+{
+ USB_DEVICE (0x182d,0x207c), // SiteCom CN-124
+ .driver_info = (unsigned long) &ali_m5632_info,
+},
#endif
#ifdef CONFIG_USB_AN2720
@@ -314,13 +329,13 @@ static struct usb_driver cdc_subset_driver = {
static int __init cdc_subset_init(void)
{
- return usb_register(&cdc_subset_driver);
+ return usb_register(&cdc_subset_driver);
}
module_init(cdc_subset_init);
static void __exit cdc_subset_exit(void)
{
- usb_deregister(&cdc_subset_driver);
+ usb_deregister(&cdc_subset_driver);
}
module_exit(cdc_subset_exit);
diff --git a/drivers/usb/net/dm9601.c b/drivers/usb/net/dm9601.c
new file mode 100644
index 00000000000..a6763860147
--- /dev/null
+++ b/drivers/usb/net/dm9601.c
@@ -0,0 +1,619 @@
+/*
+ * Davicom DM9601 USB 1.1 10/100Mbps ethernet devices
+ *
+ * Peter Korsgaard <jacmet@sunsite.dk>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+//#define DEBUG
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+
+#include "usbnet.h"
+
+/* datasheet:
+ http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9601-DS-P01-930914.pdf
+*/
+
+/* control requests */
+#define DM_READ_REGS 0x00
+#define DM_WRITE_REGS 0x01
+#define DM_READ_MEMS 0x02
+#define DM_WRITE_REG 0x03
+#define DM_WRITE_MEMS 0x05
+#define DM_WRITE_MEM 0x07
+
+/* registers */
+#define DM_NET_CTRL 0x00
+#define DM_RX_CTRL 0x05
+#define DM_SHARED_CTRL 0x0b
+#define DM_SHARED_ADDR 0x0c
+#define DM_SHARED_DATA 0x0d /* low + high */
+#define DM_PHY_ADDR 0x10 /* 6 bytes */
+#define DM_MCAST_ADDR 0x16 /* 8 bytes */
+#define DM_GPR_CTRL 0x1e
+#define DM_GPR_DATA 0x1f
+
+#define DM_MAX_MCAST 64
+#define DM_MCAST_SIZE 8
+#define DM_EEPROM_LEN 256
+#define DM_TX_OVERHEAD 2 /* 2 byte header */
+#define DM_RX_OVERHEAD 7 /* 3 byte header + 4 byte crc tail */
+#define DM_TIMEOUT 1000
+
+
+static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data)
+{
+ devdbg(dev, "dm_read() reg=0x%02x length=%d", reg, length);
+ return usb_control_msg(dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0),
+ DM_READ_REGS,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, reg, data, length, USB_CTRL_SET_TIMEOUT);
+}
+
+static int dm_read_reg(struct usbnet *dev, u8 reg, u8 *value)
+{
+ return dm_read(dev, reg, 1, value);
+}
+
+static int dm_write(struct usbnet *dev, u8 reg, u16 length, void *data)
+{
+ devdbg(dev, "dm_write() reg=0x%02x, length=%d", reg, length);
+ return usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ DM_WRITE_REGS,
+ USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE,
+ 0, reg, data, length, USB_CTRL_SET_TIMEOUT);
+}
+
+static int dm_write_reg(struct usbnet *dev, u8 reg, u8 value)
+{
+ devdbg(dev, "dm_write_reg() reg=0x%02x, value=0x%02x", reg, value);
+ return usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ DM_WRITE_REG,
+ USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE,
+ value, reg, NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+static void dm_write_async_callback(struct urb *urb)
+{
+ struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
+
+ if (urb->status < 0)
+ printk(KERN_DEBUG "dm_write_async_callback() failed with %d",
+ urb->status);
+
+ kfree(req);
+ usb_free_urb(urb);
+}
+
+static void dm_write_async(struct usbnet *dev, u8 reg, u16 length, void *data)
+{
+ struct usb_ctrlrequest *req;
+ struct urb *urb;
+ int status;
+
+ devdbg(dev, "dm_write_async() reg=0x%02x length=%d", reg, length);
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ deverr(dev, "Error allocating URB in dm_write_async!");
+ return;
+ }
+
+ req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
+ if (!req) {
+ deverr(dev, "Failed to allocate memory for control request");
+ usb_free_urb(urb);
+ return;
+ }
+
+ req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ req->bRequest = DM_WRITE_REGS;
+ req->wValue = 0;
+ req->wIndex = cpu_to_le16(reg);
+ req->wLength = cpu_to_le16(length);
+
+ usb_fill_control_urb(urb, dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ (void *)req, data, length,
+ dm_write_async_callback, req);
+
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status < 0) {
+ deverr(dev, "Error submitting the control message: status=%d",
+ status);
+ kfree(req);
+ usb_free_urb(urb);
+ }
+}
+
+static void dm_write_reg_async(struct usbnet *dev, u8 reg, u8 value)
+{
+ struct usb_ctrlrequest *req;
+ struct urb *urb;
+ int status;
+
+ devdbg(dev, "dm_write_reg_async() reg=0x%02x value=0x%02x",
+ reg, value);
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ deverr(dev, "Error allocating URB in dm_write_async!");
+ return;
+ }
+
+ req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
+ if (!req) {
+ deverr(dev, "Failed to allocate memory for control request");
+ usb_free_urb(urb);
+ return;
+ }
+
+ req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ req->bRequest = DM_WRITE_REG;
+ req->wValue = cpu_to_le16(value);
+ req->wIndex = cpu_to_le16(reg);
+ req->wLength = 0;
+
+ usb_fill_control_urb(urb, dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ (void *)req, NULL, 0, dm_write_async_callback, req);
+
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status < 0) {
+ deverr(dev, "Error submitting the control message: status=%d",
+ status);
+ kfree(req);
+ usb_free_urb(urb);
+ }
+}
+
+static int dm_read_shared_word(struct usbnet *dev, int phy, u8 reg, u16 *value)
+{
+ int ret, i;
+
+ mutex_lock(&dev->phy_mutex);
+
+ dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg);
+ dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0xc : 0x4);
+
+ for (i = 0; i < DM_TIMEOUT; i++) {
+ u8 tmp;
+
+ udelay(1);
+ ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp);
+ if (ret < 0)
+ goto out;
+
+ /* ready */
+ if ((tmp & 1) == 0)
+ break;
+ }
+
+ if (i == DM_TIMEOUT) {
+ deverr(dev, "%s read timed out!", phy ? "phy" : "eeprom");
+ ret = -EIO;
+ goto out;
+ }
+
+ dm_write_reg(dev, DM_SHARED_CTRL, 0x0);
+ ret = dm_read(dev, DM_SHARED_DATA, 2, value);
+
+ devdbg(dev, "read shared %d 0x%02x returned 0x%04x, %d",
+ phy, reg, *value, ret);
+
+ out:
+ mutex_unlock(&dev->phy_mutex);
+ return ret;
+}
+
+static int dm_write_shared_word(struct usbnet *dev, int phy, u8 reg, u16 value)
+{
+ int ret, i;
+
+ mutex_lock(&dev->phy_mutex);
+
+ ret = dm_write(dev, DM_SHARED_DATA, 2, &value);
+ if (ret < 0)
+ goto out;
+
+ dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg);
+ dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0x1c : 0x14);
+
+ for (i = 0; i < DM_TIMEOUT; i++) {
+ u8 tmp;
+
+ udelay(1);
+ ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp);
+ if (ret < 0)
+ goto out;
+
+ /* ready */
+ if ((tmp & 1) == 0)
+ break;
+ }
+
+ if (i == DM_TIMEOUT) {
+ deverr(dev, "%s write timed out!", phy ? "phy" : "eeprom");
+ ret = -EIO;
+ goto out;
+ }
+
+ dm_write_reg(dev, DM_SHARED_CTRL, 0x0);
+
+out:
+ mutex_unlock(&dev->phy_mutex);
+ return ret;
+}
+
+static int dm_read_eeprom_word(struct usbnet *dev, u8 offset, void *value)
+{
+ return dm_read_shared_word(dev, 0, offset, value);
+}
+
+
+
+static int dm9601_get_eeprom_len(struct net_device *dev)
+{
+ return DM_EEPROM_LEN;
+}
+
+static int dm9601_get_eeprom(struct net_device *net,
+ struct ethtool_eeprom *eeprom, u8 * data)
+{
+ struct usbnet *dev = netdev_priv(net);
+ u16 *ebuf = (u16 *) data;
+ int i;
+
+ /* access is 16bit */
+ if ((eeprom->offset % 2) || (eeprom->len % 2))
+ return -EINVAL;
+
+ for (i = 0; i < eeprom->len / 2; i++) {
+ if (dm_read_eeprom_word(dev, eeprom->offset / 2 + i,
+ &ebuf[i]) < 0)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int dm9601_mdio_read(struct net_device *netdev, int phy_id, int loc)
+{
+ struct usbnet *dev = netdev_priv(netdev);
+
+ u16 res;
+
+ if (phy_id) {
+ devdbg(dev, "Only internal phy supported");
+ return 0;
+ }
+
+ dm_read_shared_word(dev, 1, loc, &res);
+
+ devdbg(dev,
+ "dm9601_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x",
+ phy_id, loc, le16_to_cpu(res));
+
+ return le16_to_cpu(res);
+}
+
+static void dm9601_mdio_write(struct net_device *netdev, int phy_id, int loc,
+ int val)
+{
+ struct usbnet *dev = netdev_priv(netdev);
+ u16 res = cpu_to_le16(val);
+
+ if (phy_id) {
+ devdbg(dev, "Only internal phy supported");
+ return;
+ }
+
+ devdbg(dev,"dm9601_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x",
+ phy_id, loc, val);
+
+ dm_write_shared_word(dev, 1, loc, res);
+}
+
+static void dm9601_get_drvinfo(struct net_device *net,
+ struct ethtool_drvinfo *info)
+{
+ /* Inherit standard device info */
+ usbnet_get_drvinfo(net, info);
+ info->eedump_len = DM_EEPROM_LEN;
+}
+
+static u32 dm9601_get_link(struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ return mii_link_ok(&dev->mii);
+}
+
+static int dm9601_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+}
+
+static struct ethtool_ops dm9601_ethtool_ops = {
+ .get_drvinfo = dm9601_get_drvinfo,
+ .get_link = dm9601_get_link,
+ .get_msglevel = usbnet_get_msglevel,
+ .set_msglevel = usbnet_set_msglevel,
+ .get_eeprom_len = dm9601_get_eeprom_len,
+ .get_eeprom = dm9601_get_eeprom,
+ .get_settings = usbnet_get_settings,
+ .set_settings = usbnet_set_settings,
+ .nway_reset = usbnet_nway_reset,
+};
+
+static void dm9601_set_multicast(struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+ /* We use the 20 byte dev->data for our 8 byte filter buffer
+ * to avoid allocating memory that is tricky to free later */
+ u8 *hashes = (u8 *) & dev->data;
+ u8 rx_ctl = 0x01;
+
+ memset(hashes, 0x00, DM_MCAST_SIZE);
+ hashes[DM_MCAST_SIZE - 1] |= 0x80; /* broadcast address */
+
+ if (net->flags & IFF_PROMISC) {
+ rx_ctl |= 0x02;
+ } else if (net->flags & IFF_ALLMULTI || net->mc_count > DM_MAX_MCAST) {
+ rx_ctl |= 0x04;
+ } else if (net->mc_count) {
+ struct dev_mc_list *mc_list = net->mc_list;
+ int i;
+
+ for (i = 0; i < net->mc_count; i++) {
+ u32 crc = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26;
+ hashes[crc >> 3] |= 1 << (crc & 0x7);
+ }
+ }
+
+ dm_write_async(dev, DM_MCAST_ADDR, DM_MCAST_SIZE, hashes);
+ dm_write_reg_async(dev, DM_RX_CTRL, rx_ctl);
+}
+
+static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+ int ret;
+
+ ret = usbnet_get_endpoints(dev, intf);
+ if (ret)
+ goto out;
+
+ dev->net->do_ioctl = dm9601_ioctl;
+ dev->net->set_multicast_list = dm9601_set_multicast;
+ dev->net->ethtool_ops = &dm9601_ethtool_ops;
+ dev->net->hard_header_len += DM_TX_OVERHEAD;
+ dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
+ dev->rx_urb_size = dev->net->mtu + DM_RX_OVERHEAD;
+
+ dev->mii.dev = dev->net;
+ dev->mii.mdio_read = dm9601_mdio_read;
+ dev->mii.mdio_write = dm9601_mdio_write;
+ dev->mii.phy_id_mask = 0x1f;
+ dev->mii.reg_num_mask = 0x1f;
+
+ /* reset */
+ ret = dm_write_reg(dev, DM_NET_CTRL, 1);
+ udelay(20);
+
+ /* read MAC */
+ ret = dm_read(dev, DM_PHY_ADDR, ETH_ALEN, dev->net->dev_addr);
+ if (ret < 0) {
+ printk(KERN_ERR "Error reading MAC address\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+
+ /* power up phy */
+ dm_write_reg(dev, DM_GPR_CTRL, 1);
+ dm_write_reg(dev, DM_GPR_DATA, 0);
+
+ /* receive broadcast packets */
+ dm9601_set_multicast(dev->net);
+
+ dm9601_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+ dm9601_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+ mii_nway_restart(&dev->mii);
+
+out:
+ return ret;
+}
+
+static int dm9601_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+ u8 status;
+ int len;
+
+ /* format:
+ b0: rx status
+ b1: packet length (incl crc) low
+ b2: packet length (incl crc) high
+ b3..n-4: packet data
+ bn-3..bn: ethernet crc
+ */
+
+ if (unlikely(skb->len < DM_RX_OVERHEAD)) {
+ dev_err(&dev->udev->dev, "unexpected tiny rx frame\n");
+ return 0;
+ }
+
+ status = skb->data[0];
+ len = (skb->data[1] | (skb->data[2] << 8)) - 4;
+
+ if (unlikely(status & 0xbf)) {
+ if (status & 0x01) dev->stats.rx_fifo_errors++;
+ if (status & 0x02) dev->stats.rx_crc_errors++;
+ if (status & 0x04) dev->stats.rx_frame_errors++;
+ if (status & 0x20) dev->stats.rx_missed_errors++;
+ if (status & 0x90) dev->stats.rx_length_errors++;
+ return 0;
+ }
+
+ skb_pull(skb, 3);
+ skb_trim(skb, len);
+
+ return 1;
+}
+
+static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
+ gfp_t flags)
+{
+ int len;
+
+ /* format:
+ b0: packet length low
+ b1: packet length high
+ b3..n: packet data
+ */
+
+ if (skb_headroom(skb) < DM_TX_OVERHEAD) {
+ struct sk_buff *skb2;
+
+ skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, 0, flags);
+ dev_kfree_skb_any(skb);
+ skb = skb2;
+ if (!skb)
+ return NULL;
+ }
+
+ __skb_push(skb, DM_TX_OVERHEAD);
+
+ len = skb->len;
+ /* usbnet adds padding if length is a multiple of packet size
+ if so, adjust length value in header */
+ if ((len % dev->maxpacket) == 0)
+ len++;
+
+ skb->data[0] = len;
+ skb->data[1] = len >> 8;
+
+ return skb;
+}
+
+static void dm9601_status(struct usbnet *dev, struct urb *urb)
+{
+ int link;
+ u8 *buf;
+
+ /* format:
+ b0: net status
+ b1: tx status 1
+ b2: tx status 2
+ b3: rx status
+ b4: rx overflow
+ b5: rx count
+ b6: tx count
+ b7: gpr
+ */
+
+ if (urb->actual_length < 8)
+ return;
+
+ buf = urb->transfer_buffer;
+
+ link = !!(buf[0] & 0x40);
+ if (netif_carrier_ok(dev->net) != link) {
+ if (link) {
+ netif_carrier_on(dev->net);
+ usbnet_defer_kevent (dev, EVENT_LINK_RESET);
+ }
+ else
+ netif_carrier_off(dev->net);
+ devdbg(dev, "Link Status is: %d", link);
+ }
+}
+
+static int dm9601_link_reset(struct usbnet *dev)
+{
+ struct ethtool_cmd ecmd;
+
+ mii_check_media(&dev->mii, 1, 1);
+ mii_ethtool_gset(&dev->mii, &ecmd);
+
+ devdbg(dev, "link_reset() speed: %d duplex: %d",
+ ecmd.speed, ecmd.duplex);
+
+ return 0;
+}
+
+static const struct driver_info dm9601_info = {
+ .description = "Davicom DM9601 USB Ethernet",
+ .flags = FLAG_ETHER,
+ .bind = dm9601_bind,
+ .rx_fixup = dm9601_rx_fixup,
+ .tx_fixup = dm9601_tx_fixup,
+ .status = dm9601_status,
+ .link_reset = dm9601_link_reset,
+ .reset = dm9601_link_reset,
+};
+
+static const struct usb_device_id products[] = {
+ {
+ USB_DEVICE(0x07aa, 0x9601), /* Corega FEther USB-TXC */
+ .driver_info = (unsigned long)&dm9601_info,
+ },
+ {
+ USB_DEVICE(0x0a46, 0x9601), /* Davicom USB-100 */
+ .driver_info = (unsigned long)&dm9601_info,
+ },
+ {
+ USB_DEVICE(0x0a46, 0x6688), /* ZT6688 USB NIC */
+ .driver_info = (unsigned long)&dm9601_info,
+ },
+ {
+ USB_DEVICE(0x0a46, 0x0268), /* ShanTou ST268 USB NIC */
+ .driver_info = (unsigned long)&dm9601_info,
+ },
+ {}, // END
+};
+
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver dm9601_driver = {
+ .name = "dm9601",
+ .id_table = products,
+ .probe = usbnet_probe,
+ .disconnect = usbnet_disconnect,
+ .suspend = usbnet_suspend,
+ .resume = usbnet_resume,
+};
+
+static int __init dm9601_init(void)
+{
+ return usb_register(&dm9601_driver);
+}
+
+static void __exit dm9601_exit(void)
+{
+ usb_deregister(&dm9601_driver);
+}
+
+module_init(dm9601_init);
+module_exit(dm9601_exit);
+
+MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
+MODULE_DESCRIPTION("Davicom DM9601 USB 1.1 ethernet devices");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/net/gl620a.c b/drivers/usb/net/gl620a.c
index d257a8e026d..031cf5ca4db 100644
--- a/drivers/usb/net/gl620a.c
+++ b/drivers/usb/net/gl620a.c
@@ -157,7 +157,7 @@ genelink_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
if ((headroom < (4 + 4*1)) || (tailroom < padlen)) {
skb->data = memmove(skb->head + (4 + 4*1),
skb->data, skb->len);
- skb->tail = skb->data + skb->len;
+ skb_set_tail_pointer(skb, skb->len);
}
} else {
struct sk_buff *skb2;
diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c
index de95268ae4b..a0cc05d21a6 100644
--- a/drivers/usb/net/kaweth.c
+++ b/drivers/usb/net/kaweth.c
@@ -636,8 +636,6 @@ static void kaweth_usb_receive(struct urb *urb)
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
- skb->dev = net;
-
eth_copy_and_sum(skb, kaweth->rx_buf + 2, pkt_len, 0);
skb_put(skb, pkt_len);
diff --git a/drivers/usb/net/net1080.c b/drivers/usb/net/net1080.c
index ccebfdef475..19bf8dae70c 100644
--- a/drivers/usb/net/net1080.c
+++ b/drivers/usb/net/net1080.c
@@ -520,7 +520,7 @@ net1080_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
skb->data = memmove(skb->head
+ sizeof (struct nc_header),
skb->data, skb->len);
- skb->tail = skb->data + len;
+ skb_set_tail_pointer(skb, len);
goto encapsulate;
}
}
diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c
index d48c024cff5..a05fd97e5bc 100644
--- a/drivers/usb/net/pegasus.c
+++ b/drivers/usb/net/pegasus.c
@@ -316,6 +316,7 @@ static int update_eth_regs_async(pegasus_t * pegasus)
return ret;
}
+/* Returns 0 on success, error on failure */
static int read_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 * regd)
{
int i;
@@ -574,7 +575,6 @@ static void fill_skb_pool(pegasus_t * pegasus)
*/
if (pegasus->rx_pool[i] == NULL)
return;
- pegasus->rx_pool[i]->dev = pegasus->net;
skb_reserve(pegasus->rx_pool[i], 2);
}
}
@@ -847,10 +847,6 @@ static void intr_callback(struct urb *urb)
* d[0].NO_CARRIER kicks in only with failed TX.
* ... so monitoring with MII may be safest.
*/
- if (d[0] & NO_CARRIER)
- netif_carrier_off(net);
- else
- netif_carrier_on(net);
/* bytes 3-4 == rx_lostpkt, reg 2E/2F */
pegasus->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4];
@@ -883,7 +879,7 @@ static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net)
netif_stop_queue(net);
((__le16 *) pegasus->tx_buff)[0] = cpu_to_le16(l16);
- memcpy(pegasus->tx_buff + 2, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, pegasus->tx_buff + 2, skb->len);
usb_fill_bulk_urb(pegasus->tx_urb, pegasus->usb,
usb_sndbulkpipe(pegasus->usb, 2),
pegasus->tx_buff, count,
@@ -950,7 +946,7 @@ static void set_carrier(struct net_device *net)
pegasus_t *pegasus = netdev_priv(net);
u16 tmp;
- if (!read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp))
+ if (read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp))
return;
if (tmp & BMSR_LSTATUS)
@@ -1408,8 +1404,10 @@ static void pegasus_disconnect(struct usb_interface *intf)
unlink_all_urbs(pegasus);
free_all_urbs(pegasus);
free_skb_pool(pegasus);
- if (pegasus->rx_skb)
+ if (pegasus->rx_skb != NULL) {
dev_kfree_skb(pegasus->rx_skb);
+ pegasus->rx_skb = NULL;
+ }
free_netdev(pegasus->net);
}
diff --git a/drivers/usb/net/rndis_host.c b/drivers/usb/net/rndis_host.c
index 39a21c74fdf..980e4aaa97a 100644
--- a/drivers/usb/net/rndis_host.c
+++ b/drivers/usb/net/rndis_host.c
@@ -253,6 +253,7 @@ struct rndis_keepalive_c { /* IN (optionally OUT) */
* of that mess as possible.
*/
#define OID_802_3_PERMANENT_ADDRESS ccpu2(0x01010101)
+#define OID_GEN_MAXIMUM_FRAME_SIZE ccpu2(0x00010106)
#define OID_GEN_CURRENT_PACKET_FILTER ccpu2(0x0001010e)
/*
@@ -349,7 +350,7 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
case RNDIS_MSG_INDICATE: { /* fault */
// struct rndis_indicate *msg = (void *)buf;
dev_info(&info->control->dev,
- "rndis fault indication\n");
+ "rndis fault indication\n");
}
break;
case RNDIS_MSG_KEEPALIVE: { /* ping */
@@ -387,6 +388,71 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
return -ETIMEDOUT;
}
+/*
+ * rndis_query:
+ *
+ * Performs a query for @oid along with 0 or more bytes of payload as
+ * specified by @in_len. If @reply_len is not set to -1 then the reply
+ * length is checked against this value, resulting in an error if it
+ * doesn't match.
+ *
+ * NOTE: Adding a payload exactly or greater than the size of the expected
+ * response payload is an evident requirement MSFT added for ActiveSync.
+ *
+ * The only exception is for OIDs that return a variably sized response,
+ * in which case no payload should be added. This undocumented (and
+ * nonsensical!) issue was found by sniffing protocol requests from the
+ * ActiveSync 4.1 Windows driver.
+ */
+static int rndis_query(struct usbnet *dev, struct usb_interface *intf,
+ void *buf, u32 oid, u32 in_len,
+ void **reply, int *reply_len)
+{
+ int retval;
+ union {
+ void *buf;
+ struct rndis_msg_hdr *header;
+ struct rndis_query *get;
+ struct rndis_query_c *get_c;
+ } u;
+ u32 off, len;
+
+ u.buf = buf;
+
+ memset(u.get, 0, sizeof *u.get + in_len);
+ u.get->msg_type = RNDIS_MSG_QUERY;
+ u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len);
+ u.get->oid = oid;
+ u.get->len = cpu_to_le32(in_len);
+ u.get->offset = ccpu2(20);
+
+ retval = rndis_command(dev, u.header);
+ if (unlikely(retval < 0)) {
+ dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n",
+ oid, retval);
+ return retval;
+ }
+
+ off = le32_to_cpu(u.get_c->offset);
+ len = le32_to_cpu(u.get_c->len);
+ if (unlikely((8 + off + len) > CONTROL_BUFFER_SIZE))
+ goto response_error;
+
+ if (*reply_len != -1 && len != *reply_len)
+ goto response_error;
+
+ *reply = (unsigned char *) &u.get_c->request_id + off;
+ *reply_len = len;
+
+ return retval;
+
+response_error:
+ dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) "
+ "invalid response - off %d len %d\n",
+ oid, off, len);
+ return -EDOM;
+}
+
static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
{
int retval;
@@ -403,6 +469,8 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
struct rndis_set_c *set_c;
} u;
u32 tmp;
+ int reply_len;
+ unsigned char *bp;
/* we can't rely on i/o from stack working, or stack allocation */
u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
@@ -421,6 +489,12 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
* TX we'll stick to one Ethernet packet plus RNDIS framing.
* For RX we handle drivers that zero-pad to end-of-packet.
* Don't let userspace change these settings.
+ *
+ * NOTE: there still seems to be wierdness here, as if we need
+ * to do some more things to make sure WinCE targets accept this.
+ * They default to jumbograms of 8KB or 16KB, which is absurd
+ * for such low data rates and which is also more than Linux
+ * can usually expect to allocate for SKB data...
*/
net->hard_header_len += sizeof (struct rndis_data_hdr);
dev->hard_mtu = net->mtu + net->hard_header_len;
@@ -434,7 +508,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
if (unlikely(retval < 0)) {
/* it might not even be an RNDIS device!! */
dev_err(&intf->dev, "RNDIS init failed, %d\n", retval);
- goto fail_and_release;
+ goto fail_and_release;
}
tmp = le32_to_cpu(u.init_c->max_transfer_size);
if (tmp < dev->hard_mtu) {
@@ -450,34 +524,15 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
dev->hard_mtu, tmp, dev->rx_urb_size,
1 << le32_to_cpu(u.init_c->packet_alignment));
- /* Get designated host ethernet address.
- *
- * Adding a payload exactly the same size as the expected response
- * payload is an evident requirement MSFT added for ActiveSync.
- * This undocumented (and nonsensical) issue was found by sniffing
- * protocol requests from the ActiveSync 4.1 Windows driver.
- */
- memset(u.get, 0, sizeof *u.get + 48);
- u.get->msg_type = RNDIS_MSG_QUERY;
- u.get->msg_len = ccpu2(sizeof *u.get + 48);
- u.get->oid = OID_802_3_PERMANENT_ADDRESS;
- u.get->len = ccpu2(48);
- u.get->offset = ccpu2(20);
-
- retval = rndis_command(dev, u.header);
- if (unlikely(retval < 0)) {
+ /* Get designated host ethernet address */
+ reply_len = ETH_ALEN;
+ retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS,
+ 48, (void **) &bp, &reply_len);
+ if (unlikely(retval< 0)) {
dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
goto fail_and_release;
}
- tmp = le32_to_cpu(u.get_c->offset);
- if (unlikely((tmp + 8) > (CONTROL_BUFFER_SIZE - ETH_ALEN)
- || u.get_c->len != ccpu2(ETH_ALEN))) {
- dev_err(&intf->dev, "rndis ethaddr off %d len %d ?\n",
- tmp, le32_to_cpu(u.get_c->len));
- retval = -EDOM;
- goto fail_and_release;
- }
- memcpy(net->dev_addr, tmp + (char *)&u.get_c->request_id, ETH_ALEN);
+ memcpy(net->dev_addr, bp, ETH_ALEN);
/* set a nonzero filter to enable data transfers */
memset(u.set, 0, sizeof *u.set);
@@ -502,6 +557,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
fail_and_release:
usb_set_intfdata(info->data, NULL);
usb_driver_release_interface(driver_of(intf), info->data);
+ info->data = NULL;
fail:
kfree(u.buf);
return retval;
@@ -588,7 +644,7 @@ rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
if (likely((sizeof *hdr) <= room)) {
skb->data = memmove(skb->head + sizeof *hdr,
skb->data, len);
- skb->tail = skb->data + len;
+ skb_set_tail_pointer(skb, len);
goto fill;
}
}
@@ -618,7 +674,7 @@ fill:
static const struct driver_info rndis_info = {
.description = "RNDIS device",
- .flags = FLAG_ETHER | FLAG_FRAMING_RN,
+ .flags = FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
.bind = rndis_bind,
.unbind = rndis_unbind,
.status = rndis_status,
diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c
index ea153dc9b0a..fa598f0340c 100644
--- a/drivers/usb/net/rtl8150.c
+++ b/drivers/usb/net/rtl8150.c
@@ -646,7 +646,6 @@ static void fill_skb_pool(rtl8150_t *dev)
if (!skb) {
return;
}
- skb->dev = dev->netdev;
skb_reserve(skb, 2);
dev->rx_skb_pool[i] = skb;
}
diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
index 43ba61abfcc..f9cd42d058b 100644
--- a/drivers/usb/net/usbnet.c
+++ b/drivers/usb/net/usbnet.c
@@ -147,7 +147,7 @@ int usbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf)
if (tmp < 0)
return tmp;
}
-
+
dev->in = usb_rcvbulkpipe (dev->udev,
in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
dev->out = usb_sndbulkpipe (dev->udev,
@@ -203,7 +203,6 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
{
int status;
- skb->dev = dev->net;
skb->protocol = eth_type_trans (skb, dev->net);
dev->stats.rx_packets++;
dev->stats.rx_bytes += skb->len;
@@ -327,7 +326,7 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
if (netif_running (dev->net)
&& netif_device_present (dev->net)
&& !test_bit (EVENT_RX_HALT, &dev->flags)) {
- switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){
+ switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){
case -EPIPE:
usbnet_defer_kevent (dev, EVENT_RX_HALT);
break;
@@ -443,7 +442,7 @@ block:
case -EOVERFLOW:
dev->stats.rx_over_errors++;
// FALLTHROUGH
-
+
default:
entry->state = rx_cleanup;
dev->stats.rx_errors++;
@@ -560,7 +559,7 @@ static int usbnet_stop (struct net_device *net)
if (netif_msg_ifdown (dev))
devinfo (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld",
- dev->stats.rx_packets, dev->stats.tx_packets,
+ dev->stats.rx_packets, dev->stats.tx_packets,
dev->stats.rx_errors, dev->stats.tx_errors
);
@@ -578,7 +577,7 @@ static int usbnet_stop (struct net_device *net)
devdbg (dev, "waited for %d urb completions", temp);
}
dev->wait = NULL;
- remove_wait_queue (&unlink_wakeup, &wait);
+ remove_wait_queue (&unlink_wakeup, &wait);
usb_kill_urb(dev->interrupt);
@@ -735,8 +734,7 @@ void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
{
struct usbnet *dev = netdev_priv(net);
- /* REVISIT don't always return "usbnet" */
- strncpy (info->driver, driver_name, sizeof info->driver);
+ strncpy (info->driver, dev->driver_name, sizeof info->driver);
strncpy (info->version, DRIVER_VERSION, sizeof info->version);
strncpy (info->fw_version, dev->driver_info->description,
sizeof info->fw_version);
@@ -834,7 +832,7 @@ kevent (struct work_struct *work)
}
if (test_bit (EVENT_LINK_RESET, &dev->flags)) {
- struct driver_info *info = dev->driver_info;
+ struct driver_info *info = dev->driver_info;
int retval = 0;
clear_bit (EVENT_LINK_RESET, &dev->flags);
@@ -1066,7 +1064,7 @@ static void usbnet_bh (unsigned long param)
* USB Device Driver support
*
*-------------------------------------------------------------------------*/
-
+
// precondition: never called in_interrupt
void usbnet_disconnect (struct usb_interface *intf)
@@ -1087,7 +1085,7 @@ void usbnet_disconnect (struct usb_interface *intf)
intf->dev.driver->name,
xdev->bus->bus_name, xdev->devpath,
dev->driver_info->description);
-
+
net = dev->net;
unregister_netdev (net);
@@ -1111,15 +1109,17 @@ int
usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
{
struct usbnet *dev;
- struct net_device *net;
+ struct net_device *net;
struct usb_host_interface *interface;
struct driver_info *info;
struct usb_device *xdev;
int status;
+ const char *name;
+ name = udev->dev.driver->name;
info = (struct driver_info *) prod->driver_info;
if (!info) {
- dev_dbg (&udev->dev, "blacklisted by %s\n", driver_name);
+ dev_dbg (&udev->dev, "blacklisted by %s\n", name);
return -ENODEV;
}
xdev = interface_to_usbdev (udev);
@@ -1139,6 +1139,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
dev = netdev_priv(net);
dev->udev = xdev;
dev->driver_info = info;
+ dev->driver_name = name;
dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV
| NETIF_MSG_PROBE | NETIF_MSG_LINK);
skb_queue_head_init (&dev->rxq);
@@ -1181,6 +1182,9 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
// NOTE net->name still not usable ...
if (info->bind) {
status = info->bind (dev, udev);
+ if (status < 0)
+ goto out1;
+
// heuristic: "usb%d" for links we know are two-host,
// else "eth%d" when there's reasonable doubt. userspace
// can rename the link if it knows better.
@@ -1207,12 +1211,12 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
if (status == 0 && dev->status)
status = init_status (dev, udev);
if (status < 0)
- goto out1;
+ goto out3;
if (!dev->rx_urb_size)
dev->rx_urb_size = dev->hard_mtu;
dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1);
-
+
SET_NETDEV_DEV(net, &udev->dev);
status = register_netdev (net);
if (status)
@@ -1255,7 +1259,7 @@ EXPORT_SYMBOL_GPL(usbnet_probe);
int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
{
struct usbnet *dev = usb_get_intfdata(intf);
-
+
/* accelerate emptying of the rx and queues, to avoid
* having everything error out.
*/
@@ -1286,7 +1290,7 @@ static int __init usbnet_init(void)
< sizeof (struct skb_data));
random_ether_addr(node_id);
- return 0;
+ return 0;
}
module_init(usbnet_init);
diff --git a/drivers/usb/net/usbnet.h b/drivers/usb/net/usbnet.h
index 07c70abbe0e..cbb53e065d6 100644
--- a/drivers/usb/net/usbnet.h
+++ b/drivers/usb/net/usbnet.h
@@ -29,6 +29,7 @@ struct usbnet {
/* housekeeping */
struct usb_device *udev;
struct driver_info *driver_info;
+ const char *driver_name;
wait_queue_head_t *wait;
struct mutex phy_mutex;
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 2f4d303ee36..ba5d1dc0303 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -423,11 +423,11 @@ config USB_SERIAL_MCT_U232
module will be called mct_u232.
config USB_SERIAL_MOS7720
- tristate "USB Moschip 7720 Single Port Serial Driver"
+ tristate "USB Moschip 7720 Serial Driver"
depends on USB_SERIAL
---help---
- Say Y here if you want to use a USB Serial single port adapter from
- Moschip Semiconductor Tech.
+ Say Y here if you want to use USB Serial single and double
+ port adapters from Moschip Semiconductor Tech.
To compile this driver as a module, choose M here: the
module will be called mos7720.
diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c
index 11dad42c3c6..b675735bfbe 100644
--- a/drivers/usb/serial/aircable.c
+++ b/drivers/usb/serial/aircable.c
@@ -209,6 +209,7 @@ static void aircable_send(struct usb_serial_port *port)
int count, result;
struct aircable_private *priv = usb_get_serial_port_data(port);
unsigned char* buf;
+ u16 *dbuf;
dbg("%s - port %d", __FUNCTION__, port->number);
if (port->write_urb_busy)
return;
@@ -226,8 +227,8 @@ static void aircable_send(struct usb_serial_port *port)
buf[0] = TX_HEADER_0;
buf[1] = TX_HEADER_1;
- buf[2] = (unsigned char)count;
- buf[3] = (unsigned char)(count >> 8);
+ dbuf = (u16 *)&buf[2];
+ *dbuf = cpu_to_le16((u16)count);
serial_buf_get(priv->tx_buf,buf + HCI_HEADER_LENGTH, MAX_HCI_FRAMESIZE);
memcpy(port->write_urb->transfer_buffer, buf,
@@ -434,7 +435,7 @@ static void aircable_write_bulk_callback(struct urb *urb)
__FUNCTION__, urb->status);
port->write_urb->transfer_buffer_length = 1;
port->write_urb->dev = port->serial->dev;
- result = usb_submit_urb(port->write_urb, GFP_KERNEL);
+ result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (result)
dev_err(&urb->dev->dev,
"%s - failed resubmitting write urb, error %d\n",
diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c
index 0af42e32fa0..39a49836259 100644
--- a/drivers/usb/serial/airprime.c
+++ b/drivers/usb/serial/airprime.c
@@ -18,11 +18,6 @@
static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x0c88, 0x17da) }, /* Kyocera Wireless KPC650/Passport */
- { USB_DEVICE(0x1410, 0x1110) }, /* Novatel Wireless Merlin CDMA */
- { USB_DEVICE(0x1410, 0x1130) }, /* Novatel Wireless S720 CDMA/EV-DO */
- { USB_DEVICE(0x1410, 0x2110) }, /* Novatel Wireless U720 CDMA/EV-DO */
- { USB_DEVICE(0x1410, 0x1430) }, /* Novatel Merlin XU870 HSDPA/3G */
- { USB_DEVICE(0x1410, 0x1100) }, /* ExpressCard34 Qualcomm 3G CDMA */
{ USB_DEVICE(0x413c, 0x8115) }, /* Dell Wireless HSDPA 5500 */
{ },
};
@@ -44,8 +39,43 @@ struct airprime_private {
int outstanding_urbs;
int throttled;
struct urb *read_urbp[NUM_READ_URBS];
+
+ /* Settings for the port */
+ int rts_state; /* Handshaking pins (outputs) */
+ int dtr_state;
+ int cts_state; /* Handshaking pins (inputs) */
+ int dsr_state;
+ int dcd_state;
+ int ri_state;
};
+static int airprime_send_setup(struct usb_serial_port *port)
+{
+ struct usb_serial *serial = port->serial;
+ struct airprime_private *priv;
+
+ dbg("%s", __FUNCTION__);
+
+ if (port->number != 0)
+ return 0;
+
+ priv = usb_get_serial_port_data(port);
+
+ if (port->tty) {
+ int val = 0;
+ if (priv->dtr_state)
+ val |= 0x01;
+ if (priv->rts_state)
+ val |= 0x02;
+
+ return usb_control_msg(serial->dev,
+ usb_rcvctrlpipe(serial->dev, 0),
+ 0x22,0x21,val,0,NULL,0,USB_CTRL_SET_TIMEOUT);
+ }
+
+ return 0;
+}
+
static void airprime_read_bulk_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
@@ -58,11 +88,6 @@ static void airprime_read_bulk_callback(struct urb *urb)
if (urb->status) {
dbg("%s - nonzero read bulk status received: %d",
__FUNCTION__, urb->status);
- /* something happened, so free up the memory for this urb */
- if (urb->transfer_buffer) {
- kfree (urb->transfer_buffer);
- urb->transfer_buffer = NULL;
- }
return;
}
usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
@@ -123,6 +148,10 @@ static int airprime_open(struct usb_serial_port *port, struct file *filp)
usb_set_serial_port_data(port, priv);
}
+ /* Set some sane defaults */
+ priv->rts_state = 1;
+ priv->dtr_state = 1;
+
for (i = 0; i < NUM_READ_URBS; ++i) {
buffer = kmalloc(buffer_size, GFP_KERNEL);
if (!buffer) {
@@ -146,6 +175,8 @@ static int airprime_open(struct usb_serial_port *port, struct file *filp)
airprime_read_bulk_callback, port);
result = usb_submit_urb(urb, GFP_KERNEL);
if (result) {
+ usb_free_urb(urb);
+ kfree(buffer);
dev_err(&port->dev,
"%s - failed submitting read urb %d for port %d, error %d\n",
__FUNCTION__, i, port->number, result);
@@ -154,33 +185,21 @@ static int airprime_open(struct usb_serial_port *port, struct file *filp)
/* remember this urb so we can kill it when the port is closed */
priv->read_urbp[i] = urb;
}
+
+ airprime_send_setup(port);
+
goto out;
errout:
/* some error happened, cancel any submitted urbs and clean up anything that
got allocated successfully */
- for ( ; i >= 0; --i) {
+ while (i-- != 0) {
urb = priv->read_urbp[i];
- if (urb) {
- /* This urb was submitted successfully. So we have to
- cancel it.
- Unlinking the urb will invoke read_bulk_callback()
- with an error status, so its transfer buffer will
- be freed there */
- if (usb_unlink_urb (urb) != -EINPROGRESS) {
- /* comments in drivers/usb/core/urb.c say this
- can only happen if the urb was never submitted,
- or has completed already.
- Either way we may have to free the transfer
- buffer here. */
- if (urb->transfer_buffer) {
- kfree (urb->transfer_buffer);
- urb->transfer_buffer = NULL;
- }
- }
- usb_free_urb (urb);
- }
+ buffer = urb->transfer_buffer;
+ usb_kill_urb (urb);
+ usb_free_urb (urb);
+ kfree (buffer);
}
out:
@@ -194,10 +213,14 @@ static void airprime_close(struct usb_serial_port *port, struct file * filp)
dbg("%s - port %d", __FUNCTION__, port->number);
- /* killing the urb will invoke read_bulk_callback() with an error status,
- so the transfer buffer will be freed there */
+ priv->rts_state = 0;
+ priv->dtr_state = 0;
+
+ airprime_send_setup(port);
+
for (i = 0; i < NUM_READ_URBS; ++i) {
usb_kill_urb (priv->read_urbp[i]);
+ kfree (priv->read_urbp[i]->transfer_buffer);
usb_free_urb (priv->read_urbp[i]);
}
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
index edd685791a6..ea2175bb227 100644
--- a/drivers/usb/serial/ark3116.c
+++ b/drivers/usb/serial/ark3116.c
@@ -341,7 +341,7 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp)
result = usb_serial_generic_open(port, filp);
if (result)
- return result;
+ goto err_out;
/* open */
ARK3116_RCV(serial, 111, 0xFE, 0xC0, 0x0000, 0x0003, 0x02, buf);
@@ -372,6 +372,7 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp)
if (port->tty)
ark3116_set_termios(port, &tmp_termios);
+err_out:
kfree(buf);
return result;
diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c
index 3ec24870bca..e831cb7f64f 100644
--- a/drivers/usb/serial/cp2101.c
+++ b/drivers/usb/serial/cp2101.c
@@ -58,17 +58,22 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
{ USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */
{ USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */
+ { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */
{ USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */
{ USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */
{ USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */
+ { USB_DEVICE(0x10C4, 0x80DD) }, /* Tracient RFID */
{ USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */
{ USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */
+ { USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */
+ { USB_DEVICE(0x10C4, 0x814B) }, /* West Mountain Radio RIGtalk */
{ USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */
{ USB_DEVICE(0x10C4, 0x81C8) }, /* Lipowsky Industrie Elektronik GmbH, Baby-JTAG */
{ USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */
{ USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */
{ USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
{ USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */
+ { USB_DEVICE(0x10C5, 0xEA61) }, /* Silicon Labs MobiData GPRS USB Modem */
{ USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */
{ USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */
{ } /* Terminating Entry */
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 4695952b647..95a1805b064 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -315,6 +315,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_SIO_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_232RL_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_MICRO_CHAMELEON_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) },
@@ -341,6 +342,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_USBX_707_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2101_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) },
@@ -420,6 +422,14 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_ELV_ALC8500_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_PYRAMID_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1000PC_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_IBS_US485_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_IBS_PICPRO_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_IBS_PCMCIA_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_IBS_PK1_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_IBS_RS232MON_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_IBS_APP70_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) },
/*
* These will probably use user-space drivers. Uncomment them if
* you need them or use the user-specified vendor/product module
@@ -459,6 +469,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FALCOM_VID, FALCOM_TWIST_PID) },
{ USB_DEVICE(FALCOM_VID, FALCOM_SAMBA_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_SUUNTO_SPORTS_PID) },
+ { USB_DEVICE(TTI_VID, TTI_QL355P_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_RM_CANVIEW_PID) },
{ USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) },
{ USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) },
@@ -513,6 +524,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) },
{ USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) },
+ { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) },
{ }, /* Optional parameter entry */
{ } /* Terminating entry */
};
@@ -532,6 +544,7 @@ static const char *ftdi_chip_name[] = {
[FT8U232AM] = "FT8U232AM",
[FT232BM] = "FT232BM",
[FT2232C] = "FT2232C",
+ [FT232RL] = "FT232RL",
};
@@ -587,6 +600,8 @@ struct ftdi_private {
static int ftdi_sio_probe (struct usb_serial *serial, const struct usb_device_id *id);
static int ftdi_sio_attach (struct usb_serial *serial);
static void ftdi_shutdown (struct usb_serial *serial);
+static int ftdi_sio_port_probe (struct usb_serial_port *port);
+static int ftdi_sio_port_remove (struct usb_serial_port *port);
static int ftdi_open (struct usb_serial_port *port, struct file *filp);
static void ftdi_close (struct usb_serial_port *port, struct file *filp);
static int ftdi_write (struct usb_serial_port *port, const unsigned char *buf, int count);
@@ -621,6 +636,8 @@ static struct usb_serial_driver ftdi_sio_device = {
.num_bulk_out = 1,
.num_ports = 1,
.probe = ftdi_sio_probe,
+ .port_probe = ftdi_sio_port_probe,
+ .port_remove = ftdi_sio_port_remove,
.open = ftdi_open,
.close = ftdi_close,
.throttle = ftdi_throttle,
@@ -863,6 +880,7 @@ static __u32 get_ftdi_divisor(struct usb_serial_port * port)
break;
case FT232BM: /* FT232BM chip */
case FT2232C: /* FT2232C chip */
+ case FT232RL:
if (baud <= 3000000) {
div_value = ftdi_232bm_baud_to_divisor(baud);
} else {
@@ -1005,9 +1023,12 @@ static void ftdi_determine_type(struct usb_serial_port *port)
/* (It might be a BM because of the iSerialNumber bug,
* but it will still work as an AM device.) */
priv->chip_type = FT8U232AM;
- } else {
+ } else if (version < 0x600) {
/* Assume its an FT232BM (or FT245BM) */
priv->chip_type = FT232BM;
+ } else {
+ /* Assume its an FT232R */
+ priv->chip_type = FT232RL;
}
info("Detected %s", ftdi_chip_name[priv->chip_type]);
}
@@ -1023,11 +1044,10 @@ static ssize_t show_latency_timer(struct device *dev, struct device_attribute *a
{
struct usb_serial_port *port = to_usb_serial_port(dev);
struct ftdi_private *priv = usb_get_serial_port_data(port);
- struct usb_device *udev;
+ struct usb_device *udev = port->serial->dev;
unsigned short latency = 0;
int rv = 0;
- udev = to_usb_device(dev);
dbg("%s",__FUNCTION__);
@@ -1051,13 +1071,11 @@ static ssize_t store_latency_timer(struct device *dev, struct device_attribute *
{
struct usb_serial_port *port = to_usb_serial_port(dev);
struct ftdi_private *priv = usb_get_serial_port_data(port);
- struct usb_device *udev;
+ struct usb_device *udev = port->serial->dev;
char buf[1];
int v = simple_strtoul(valbuf, NULL, 10);
int rv = 0;
- udev = to_usb_device(dev);
-
dbg("%s: setting latency timer = %i", __FUNCTION__, v);
rv = usb_control_msg(udev,
@@ -1082,13 +1100,11 @@ static ssize_t store_event_char(struct device *dev, struct device_attribute *att
{
struct usb_serial_port *port = to_usb_serial_port(dev);
struct ftdi_private *priv = usb_get_serial_port_data(port);
- struct usb_device *udev;
+ struct usb_device *udev = port->serial->dev;
char buf[1];
int v = simple_strtoul(valbuf, NULL, 10);
int rv = 0;
- udev = to_usb_device(dev);
-
dbg("%s: setting event char = %i", __FUNCTION__, v);
rv = usb_control_msg(udev,
@@ -1109,46 +1125,38 @@ static ssize_t store_event_char(struct device *dev, struct device_attribute *att
static DEVICE_ATTR(latency_timer, S_IWUSR | S_IRUGO, show_latency_timer, store_latency_timer);
static DEVICE_ATTR(event_char, S_IWUSR, NULL, store_event_char);
-static int create_sysfs_attrs(struct usb_serial *serial)
+static int create_sysfs_attrs(struct usb_serial_port *port)
{
- struct ftdi_private *priv;
- struct usb_device *udev;
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
int retval = 0;
dbg("%s",__FUNCTION__);
- priv = usb_get_serial_port_data(serial->port[0]);
- udev = serial->dev;
-
/* XXX I've no idea if the original SIO supports the event_char
* sysfs parameter, so I'm playing it safe. */
if (priv->chip_type != SIO) {
dbg("sysfs attributes for %s", ftdi_chip_name[priv->chip_type]);
- retval = device_create_file(&udev->dev, &dev_attr_event_char);
+ retval = device_create_file(&port->dev, &dev_attr_event_char);
if ((!retval) &&
(priv->chip_type == FT232BM || priv->chip_type == FT2232C)) {
- retval = device_create_file(&udev->dev,
+ retval = device_create_file(&port->dev,
&dev_attr_latency_timer);
}
}
return retval;
}
-static void remove_sysfs_attrs(struct usb_serial *serial)
+static void remove_sysfs_attrs(struct usb_serial_port *port)
{
- struct ftdi_private *priv;
- struct usb_device *udev;
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
dbg("%s",__FUNCTION__);
- priv = usb_get_serial_port_data(serial->port[0]);
- udev = serial->dev;
-
/* XXX see create_sysfs_attrs */
if (priv->chip_type != SIO) {
- device_remove_file(&udev->dev, &dev_attr_event_char);
+ device_remove_file(&port->dev, &dev_attr_event_char);
if (priv->chip_type == FT232BM || priv->chip_type == FT2232C) {
- device_remove_file(&udev->dev, &dev_attr_latency_timer);
+ device_remove_file(&port->dev, &dev_attr_latency_timer);
}
}
@@ -1168,13 +1176,9 @@ static int ftdi_sio_probe (struct usb_serial *serial, const struct usb_device_id
return (0);
}
-/* attach subroutine */
-static int ftdi_sio_attach (struct usb_serial *serial)
+static int ftdi_sio_port_probe(struct usb_serial_port *port)
{
- struct usb_serial_port *port = serial->port[0];
struct ftdi_private *priv;
- struct ftdi_sio_quirk *quirk;
- int retval;
dbg("%s",__FUNCTION__);
@@ -1214,19 +1218,21 @@ static int ftdi_sio_attach (struct usb_serial *serial)
kfree(port->bulk_out_buffer);
port->bulk_out_buffer = NULL;
- usb_set_serial_port_data(serial->port[0], priv);
+ usb_set_serial_port_data(port, priv);
- ftdi_determine_type (serial->port[0]);
- retval = create_sysfs_attrs(serial);
- if (retval)
- dev_err(&serial->dev->dev, "Error creating sysfs files, "
- "continuing\n");
+ ftdi_determine_type (port);
+ create_sysfs_attrs(port);
+ return 0;
+}
+/* attach subroutine */
+static int ftdi_sio_attach (struct usb_serial *serial)
+{
/* Check for device requiring special set up. */
- quirk = (struct ftdi_sio_quirk *)usb_get_serial_data(serial);
- if (quirk && quirk->setup) {
+ struct ftdi_sio_quirk *quirk = usb_get_serial_data(serial);
+
+ if (quirk && quirk->setup)
quirk->setup(serial);
- }
return 0;
} /* ftdi_sio_attach */
@@ -1270,17 +1276,18 @@ static void ftdi_HE_TIRA1_setup (struct usb_serial *serial)
* calls __serial_close for each open of the port
* shutdown is called then (ie ftdi_shutdown)
*/
-
-
static void ftdi_shutdown (struct usb_serial *serial)
-{ /* ftdi_shutdown */
+{
+ dbg("%s", __FUNCTION__);
+}
- struct usb_serial_port *port = serial->port[0];
+static int ftdi_sio_port_remove(struct usb_serial_port *port)
+{
struct ftdi_private *priv = usb_get_serial_port_data(port);
dbg("%s", __FUNCTION__);
- remove_sysfs_attrs(serial);
+ remove_sysfs_attrs(port);
/* all open ports are closed at this point
* (by usbserial.c:__serial_close, which calls ftdi_close)
@@ -1290,8 +1297,9 @@ static void ftdi_shutdown (struct usb_serial *serial)
usb_set_serial_port_data(port, NULL);
kfree(priv);
}
-} /* ftdi_shutdown */
+ return 0;
+}
static int ftdi_open (struct usb_serial_port *port, struct file *filp)
{ /* ftdi_open */
@@ -1426,6 +1434,7 @@ static int ftdi_write (struct usb_serial_port *port,
dbg("%s - write limit hit\n", __FUNCTION__);
return 0;
}
+ priv->tx_outstanding_urbs++;
spin_unlock_irqrestore(&priv->tx_lock, flags);
data_offset = priv->write_offset;
@@ -1443,14 +1452,15 @@ static int ftdi_write (struct usb_serial_port *port,
buffer = kmalloc (transfer_size, GFP_ATOMIC);
if (!buffer) {
err("%s ran out of kernel memory for urb ...", __FUNCTION__);
- return -ENOMEM;
+ count = -ENOMEM;
+ goto error_no_buffer;
}
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) {
err("%s - no more free urbs", __FUNCTION__);
- kfree (buffer);
- return -ENOMEM;
+ count = -ENOMEM;
+ goto error_no_urb;
}
/* Copy data */
@@ -1492,10 +1502,9 @@ static int ftdi_write (struct usb_serial_port *port,
if (status) {
err("%s - failed submitting write urb, error %d", __FUNCTION__, status);
count = status;
- kfree (buffer);
+ goto error;
} else {
spin_lock_irqsave(&priv->tx_lock, flags);
- ++priv->tx_outstanding_urbs;
priv->tx_outstanding_bytes += count;
priv->tx_bytes += count;
spin_unlock_irqrestore(&priv->tx_lock, flags);
@@ -1503,10 +1512,19 @@ static int ftdi_write (struct usb_serial_port *port,
/* we are done with this urb, so let the host driver
* really free it when it is finished with it */
- usb_free_urb (urb);
+ usb_free_urb(urb);
dbg("%s write returning: %d", __FUNCTION__, count);
return count;
+error:
+ usb_free_urb(urb);
+error_no_urb:
+ kfree (buffer);
+error_no_buffer:
+ spin_lock_irqsave(&priv->tx_lock, flags);
+ priv->tx_outstanding_urbs--;
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+ return count;
} /* ftdi_write */
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index 7eff1c03ba8..77ad0a09b38 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -27,9 +27,11 @@
#define FTDI_8U232AM_PID 0x6001 /* Similar device to SIO above */
#define FTDI_8U232AM_ALT_PID 0x6006 /* FTDI's alternate PID for above */
#define FTDI_8U2232C_PID 0x6010 /* Dual channel device */
+#define FTDI_232RL_PID 0xFBFA /* Product ID for FT232RL */
#define FTDI_RELAIS_PID 0xFA10 /* Relais device from Rudolf Gugler */
#define FTDI_NF_RIC_VID 0x0DCD /* Vendor Id */
#define FTDI_NF_RIC_PID 0x0001 /* Product Id */
+#define FTDI_USBX_707_PID 0xF857 /* ADSTech IR Blaster USBX-707 */
/* www.canusb.com Lawicel CANUSB device */
@@ -339,6 +341,12 @@
#define FTDI_SUUNTO_SPORTS_PID 0xF680 /* Suunto Sports instrument */
/*
+ * TTi (Thurlby Thandar Instruments)
+ */
+#define TTI_VID 0x103E /* Vendor Id */
+#define TTI_QL355P_PID 0x03E8 /* TTi QL355P power supply */
+
+/*
* Definitions for B&B Electronics products.
*/
#define BANDB_VID 0x0856 /* B&B Electronics Vendor ID */
@@ -491,6 +499,25 @@
#define FTDI_TACTRIX_OPENPORT_13S_PID 0xCC49 /* OpenPort 1.3 Subaru */
#define FTDI_TACTRIX_OPENPORT_13U_PID 0xCC4A /* OpenPort 1.3 Universal */
+/*
+ * Telldus Technologies
+ */
+#define TELLDUS_VID 0x1781 /* Vendor ID */
+#define TELLDUS_TELLSTICK_PID 0x0C30 /* RF control dongle 433 MHz using FT232RL */
+
+/*
+ * IBS elektronik product ids
+ * Submitted by Thomas Schleusener
+ */
+#define FTDI_IBS_US485_PID 0xff38 /* IBS US485 (USB<-->RS422/485 interface) */
+#define FTDI_IBS_PICPRO_PID 0xff39 /* IBS PIC-Programmer */
+#define FTDI_IBS_PCMCIA_PID 0xff3a /* IBS Card reader for PCMCIA SRAM-cards */
+#define FTDI_IBS_PK1_PID 0xff3b /* IBS PK1 - Particel counter */
+#define FTDI_IBS_RS232MON_PID 0xff3c /* IBS RS232 - Monitor */
+#define FTDI_IBS_APP70_PID 0xff3d /* APP 70 (dust monitoring system) */
+#define FTDI_IBS_PEDO_PID 0xff3e /* IBS PEDO-Modem (RF modem 868.35 MHz) */
+#define FTDI_IBS_PROD_PID 0xff3f /* future device */
+
/* Commands */
#define FTDI_SIO_RESET 0 /* Reset the port */
#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
@@ -614,6 +641,7 @@ typedef enum {
FT8U232AM = 2,
FT232BM = 3,
FT2232C = 4,
+ FT232RL = 5,
} ftdi_chip_type_t;
typedef enum {
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 601e0648dec..4f8282ad772 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -20,13 +20,14 @@
#include <linux/usb/serial.h>
#include <asm/uaccess.h>
-static int generic_probe(struct usb_interface *interface,
- const struct usb_device_id *id);
-
static int debug;
#ifdef CONFIG_USB_SERIAL_GENERIC
+
+static int generic_probe(struct usb_interface *interface,
+ const struct usb_device_id *id);
+
static __u16 vendor = 0x05f9;
static __u16 product = 0xffff;
@@ -66,6 +67,8 @@ struct usb_serial_driver usb_serial_generic_device = {
.num_bulk_out = NUM_DONT_CARE,
.num_ports = 1,
.shutdown = usb_serial_generic_shutdown,
+ .throttle = usb_serial_generic_throttle,
+ .unthrottle = usb_serial_generic_unthrottle,
};
static int generic_probe(struct usb_interface *interface,
@@ -115,6 +118,7 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp)
{
struct usb_serial *serial = port->serial;
int result = 0;
+ unsigned long flags;
dbg("%s - port %d", __FUNCTION__, port->number);
@@ -124,7 +128,13 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp)
if (port->tty)
port->tty->low_latency = 1;
- /* if we have a bulk interrupt, start reading from it */
+ /* clear the throttle flags */
+ spin_lock_irqsave(&port->lock, flags);
+ port->throttled = 0;
+ port->throttle_req = 0;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ /* if we have a bulk endpoint, start reading from it */
if (serial->num_bulk_in) {
/* Start reading from the device */
usb_fill_bulk_urb (port->read_urb, serial->dev,
@@ -253,31 +263,22 @@ int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port)
return (chars);
}
-void usb_serial_generic_read_bulk_callback (struct urb *urb)
+/* Push data to tty layer and resubmit the bulk read URB */
+static void flush_and_resubmit_read_urb (struct usb_serial_port *port)
{
- struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = port->serial;
- struct tty_struct *tty;
- unsigned char *data = urb->transfer_buffer;
+ struct urb *urb = port->read_urb;
+ struct tty_struct *tty = port->tty;
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;
+ /* Push data to tty */
if (tty && urb->actual_length) {
tty_buffer_request_room(tty, urb->actual_length);
- tty_insert_flip_string(tty, data, urb->actual_length);
- tty_flip_buffer_push(tty);
+ tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length);
+ tty_flip_buffer_push(tty); /* is this allowed from an URB callback ? */
}
- /* Continue trying to always read */
+ /* Continue reading from device */
usb_fill_bulk_urb (port->read_urb, serial->dev,
usb_rcvbulkpipe (serial->dev,
port->bulk_in_endpointAddress),
@@ -290,6 +291,40 @@ void usb_serial_generic_read_bulk_callback (struct urb *urb)
if (result)
dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
}
+
+void usb_serial_generic_read_bulk_callback (struct urb *urb)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ unsigned char *data = urb->transfer_buffer;
+ int is_throttled;
+ unsigned long flags;
+
+ 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);
+
+ /* Throttle the device if requested by tty */
+ if (urb->actual_length) {
+ spin_lock_irqsave(&port->lock, flags);
+ is_throttled = port->throttled = port->throttle_req;
+ spin_unlock_irqrestore(&port->lock, flags);
+ if (is_throttled) {
+ /* Let the received data linger in the read URB;
+ * usb_serial_generic_unthrottle() will pick it
+ * up later. */
+ dbg("%s - throttling device", __FUNCTION__);
+ return;
+ }
+ }
+
+ /* Handle data and continue reading from device */
+ flush_and_resubmit_read_urb(port);
+}
EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback);
void usb_serial_generic_write_bulk_callback (struct urb *urb)
@@ -308,6 +343,38 @@ void usb_serial_generic_write_bulk_callback (struct urb *urb)
}
EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback);
+void usb_serial_generic_throttle (struct usb_serial_port *port)
+{
+ unsigned long flags;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ /* Set the throttle request flag. It will be picked up
+ * by usb_serial_generic_read_bulk_callback(). */
+ spin_lock_irqsave(&port->lock, flags);
+ port->throttle_req = 1;
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+void usb_serial_generic_unthrottle (struct usb_serial_port *port)
+{
+ int was_throttled;
+ unsigned long flags;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ /* Clear the throttle flags */
+ spin_lock_irqsave(&port->lock, flags);
+ was_throttled = port->throttled;
+ port->throttled = port->throttle_req = 0;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ if (was_throttled) {
+ /* Handle pending data and resume reading from device */
+ flush_and_resubmit_read_urb(port);
+ }
+}
+
void usb_serial_generic_shutdown (struct usb_serial *serial)
{
int i;
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 6a26a2e683a..18f74ac7656 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -111,7 +111,7 @@ struct edgeport_port {
struct TxFifo txfifo; /* transmit fifo -- size will be maxTxCredits */
struct urb *write_urb; /* write URB for this port */
- char write_in_progress; /* TRUE while a write URB is outstanding */
+ bool write_in_progress; /* 'true' while a write URB is outstanding */
spinlock_t ep_lock;
__u8 shadowLCR; /* last LCR value received */
@@ -123,11 +123,11 @@ struct edgeport_port {
__u8 validDataMask;
__u32 baudRate;
- char open;
- char openPending;
- char commandPending;
- char closePending;
- char chaseResponsePending;
+ bool open;
+ bool openPending;
+ bool commandPending;
+ bool closePending;
+ bool chaseResponsePending;
wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */
wait_queue_head_t wait_open; /* for handling sleeping while waiting for open to finish */
@@ -156,7 +156,7 @@ struct edgeport_serial {
__u8 bulk_in_endpoint; /* the bulk in endpoint handle */
unsigned char * bulk_in_buffer; /* the buffer we use for the bulk in endpoint */
struct urb * read_urb; /* our bulk read urb */
- int read_in_progress;
+ bool read_in_progress;
spinlock_t es_lock;
__u8 bulk_out_endpoint; /* the bulk out endpoint handle */
@@ -212,7 +212,7 @@ static int debug;
static int low_latency = 1; /* tty low latency flag, on by default */
-static int CmdUrbs = 0; /* Number of outstanding Command Write Urbs */
+static atomic_t CmdUrbs; /* Number of outstanding Command Write Urbs */
/* local function prototypes */
@@ -631,14 +631,14 @@ static void edge_interrupt_callback (struct urb *urb)
if (edge_serial->rxBytesAvail > 0 &&
!edge_serial->read_in_progress) {
dbg("%s - posting a read", __FUNCTION__);
- edge_serial->read_in_progress = TRUE;
+ edge_serial->read_in_progress = true;
/* we have pending bytes on the bulk in pipe, send a request */
edge_serial->read_urb->dev = edge_serial->serial->dev;
result = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
if (result) {
dev_err(&edge_serial->serial->dev->dev, "%s - usb_submit_urb(read bulk) failed with result = %d\n", __FUNCTION__, result);
- edge_serial->read_in_progress = FALSE;
+ edge_serial->read_in_progress = false;
}
}
spin_unlock(&edge_serial->es_lock);
@@ -695,13 +695,13 @@ static void edge_bulk_in_callback (struct urb *urb)
if (urb->status) {
dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
- edge_serial->read_in_progress = FALSE;
+ edge_serial->read_in_progress = false;
return;
}
if (urb->actual_length == 0) {
dbg("%s - read bulk callback with no data", __FUNCTION__);
- edge_serial->read_in_progress = FALSE;
+ edge_serial->read_in_progress = false;
return;
}
@@ -725,10 +725,10 @@ static void edge_bulk_in_callback (struct urb *urb)
status = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
if (status) {
dev_err(&urb->dev->dev, "%s - usb_submit_urb(read bulk) failed, status = %d\n", __FUNCTION__, status);
- edge_serial->read_in_progress = FALSE;
+ edge_serial->read_in_progress = false;
}
} else {
- edge_serial->read_in_progress = FALSE;
+ edge_serial->read_in_progress = false;
}
spin_unlock(&edge_serial->es_lock);
@@ -759,7 +759,7 @@ static void edge_bulk_out_data_callback (struct urb *urb)
}
// Release the Write URB
- edge_port->write_in_progress = FALSE;
+ edge_port->write_in_progress = false;
// Check if more data needs to be sent
send_more_port_data((struct edgeport_serial *)(usb_get_serial_data(edge_port->port->serial)), edge_port);
@@ -779,8 +779,8 @@ static void edge_bulk_out_cmd_callback (struct urb *urb)
dbg("%s", __FUNCTION__);
- CmdUrbs--;
- dbg("%s - FREE URB %p (outstanding %d)", __FUNCTION__, urb, CmdUrbs);
+ atomic_dec(&CmdUrbs);
+ dbg("%s - FREE URB %p (outstanding %d)", __FUNCTION__, urb, atomic_read(&CmdUrbs));
/* clean up the transfer buffer */
@@ -802,7 +802,7 @@ static void edge_bulk_out_cmd_callback (struct urb *urb)
tty_wakeup(tty);
/* we have completed the command */
- edge_port->commandPending = FALSE;
+ edge_port->commandPending = false;
wake_up(&edge_port->wait_command);
}
@@ -868,7 +868,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp)
port0->bulk_in_buffer,
edge_serial->read_urb->transfer_buffer_length,
edge_bulk_in_callback, edge_serial);
- edge_serial->read_in_progress = FALSE;
+ edge_serial->read_in_progress = false;
/* start interrupt read for this edgeport
* this interrupt will continue as long as the edgeport is connected */
@@ -890,26 +890,26 @@ static int edge_open (struct usb_serial_port *port, struct file * filp)
/* initialize our port settings */
edge_port->txCredits = 0; /* Can't send any data yet */
edge_port->shadowMCR = MCR_MASTER_IE; /* Must always set this bit to enable ints! */
- edge_port->chaseResponsePending = FALSE;
+ edge_port->chaseResponsePending = false;
/* send a open port command */
- edge_port->openPending = TRUE;
- edge_port->open = FALSE;
+ edge_port->openPending = true;
+ edge_port->open = false;
response = send_iosp_ext_cmd (edge_port, IOSP_CMD_OPEN_PORT, 0);
if (response < 0) {
dev_err(&port->dev, "%s - error sending open port command\n", __FUNCTION__);
- edge_port->openPending = FALSE;
+ edge_port->openPending = false;
return -ENODEV;
}
/* now wait for the port to be completely opened */
- wait_event_timeout(edge_port->wait_open, (edge_port->openPending != TRUE), OPEN_TIMEOUT);
+ wait_event_timeout(edge_port->wait_open, !edge_port->openPending, OPEN_TIMEOUT);
- if (edge_port->open == FALSE) {
+ if (!edge_port->open) {
/* open timed out */
dbg("%s - open timedout", __FUNCTION__);
- edge_port->openPending = FALSE;
+ edge_port->openPending = false;
return -ENODEV;
}
@@ -928,7 +928,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp)
/* Allocate a URB for the write */
edge_port->write_urb = usb_alloc_urb (0, GFP_KERNEL);
- edge_port->write_in_progress = FALSE;
+ edge_port->write_in_progress = false;
if (!edge_port->write_urb) {
dbg("%s - no memory", __FUNCTION__);
@@ -966,7 +966,7 @@ static void block_until_chase_response(struct edgeport_port *edge_port)
lastCredits = edge_port->txCredits;
// Did we get our Chase response
- if (edge_port->chaseResponsePending == FALSE) {
+ if (!edge_port->chaseResponsePending) {
dbg("%s - Got Chase Response", __FUNCTION__);
// did we get all of our credit back?
@@ -985,7 +985,7 @@ static void block_until_chase_response(struct edgeport_port *edge_port)
// No activity.. count down.
loop--;
if (loop == 0) {
- edge_port->chaseResponsePending = FALSE;
+ edge_port->chaseResponsePending = false;
dbg("%s - Chase TIMEOUT", __FUNCTION__);
return;
}
@@ -1068,13 +1068,13 @@ static void edge_close (struct usb_serial_port *port, struct file * filp)
// block until tx is empty
block_until_tx_empty(edge_port);
- edge_port->closePending = TRUE;
+ edge_port->closePending = true;
if ((!edge_serial->is_epic) ||
((edge_serial->is_epic) &&
(edge_serial->epic_descriptor.Supports.IOSPChase))) {
/* flush and chase */
- edge_port->chaseResponsePending = TRUE;
+ edge_port->chaseResponsePending = true;
dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__);
status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0);
@@ -1082,7 +1082,7 @@ static void edge_close (struct usb_serial_port *port, struct file * filp)
// block until chase finished
block_until_chase_response(edge_port);
} else {
- edge_port->chaseResponsePending = FALSE;
+ edge_port->chaseResponsePending = false;
}
}
@@ -1094,10 +1094,10 @@ static void edge_close (struct usb_serial_port *port, struct file * filp)
send_iosp_ext_cmd (edge_port, IOSP_CMD_CLOSE_PORT, 0);
}
- //port->close = TRUE;
- edge_port->closePending = FALSE;
- edge_port->open = FALSE;
- edge_port->openPending = FALSE;
+ //port->close = true;
+ edge_port->closePending = false;
+ edge_port->open = false;
+ edge_port->openPending = false;
usb_kill_urb(edge_port->write_urb);
@@ -1247,7 +1247,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, struct edge
}
// lock this write
- edge_port->write_in_progress = TRUE;
+ edge_port->write_in_progress = true;
// get a pointer to the write_urb
urb = edge_port->write_urb;
@@ -1261,7 +1261,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, struct edge
buffer = kmalloc (count+2, GFP_ATOMIC);
if (buffer == NULL) {
dev_err(&edge_port->port->dev, "%s - no more kernel memory...\n", __FUNCTION__);
- edge_port->write_in_progress = FALSE;
+ edge_port->write_in_progress = false;
goto exit_send;
}
buffer[0] = IOSP_BUILD_DATA_HDR1 (edge_port->port->number - edge_port->port->serial->minor, count);
@@ -1301,7 +1301,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, struct edge
if (status) {
/* something went wrong */
dev_err(&edge_port->port->dev, "%s - usb_submit_urb(write bulk) failed, status = %d, data lost\n", __FUNCTION__, status);
- edge_port->write_in_progress = FALSE;
+ edge_port->write_in_progress = false;
/* revert the credits as something bad happened. */
edge_port->txCredits += count;
@@ -1332,7 +1332,7 @@ static int edge_write_room (struct usb_serial_port *port)
if (edge_port == NULL)
return -ENODEV;
- if (edge_port->closePending == TRUE)
+ if (edge_port->closePending)
return -ENODEV;
dbg("%s - port %d", __FUNCTION__, port->number);
@@ -1371,7 +1371,7 @@ static int edge_chars_in_buffer (struct usb_serial_port *port)
if (edge_port == NULL)
return -ENODEV;
- if (edge_port->closePending == TRUE)
+ if (edge_port->closePending)
return -ENODEV;
if (!edge_port->open) {
@@ -1762,7 +1762,7 @@ static void edge_break (struct usb_serial_port *port, int break_state)
((edge_serial->is_epic) &&
(edge_serial->epic_descriptor.Supports.IOSPChase))) {
/* flush and chase */
- edge_port->chaseResponsePending = TRUE;
+ edge_port->chaseResponsePending = true;
dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__);
status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0);
@@ -1770,7 +1770,7 @@ static void edge_break (struct usb_serial_port *port, int break_state)
// block until chase finished
block_until_chase_response(edge_port);
} else {
- edge_port->chaseResponsePending = FALSE;
+ edge_port->chaseResponsePending = false;
}
}
@@ -1952,13 +1952,13 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2
// Also, we currently clear flag and close the port regardless of content of above's Byte3.
// We could choose to do something else when Byte3 says Timeout on Chase from Edgeport,
// like wait longer in block_until_chase_response, but for now we don't.
- edge_port->chaseResponsePending = FALSE;
+ edge_port->chaseResponsePending = false;
wake_up (&edge_port->wait_chase);
return;
case IOSP_EXT_STATUS_RX_CHECK_RSP:
dbg("%s ========== Port %u CHECK_RSP Sequence = %02x =============\n", __FUNCTION__, edge_serial->rxPort, byte3 );
- //Port->RxCheckRsp = TRUE;
+ //Port->RxCheckRsp = true;
return;
}
}
@@ -1974,8 +1974,8 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2
change_port_settings (edge_port, edge_port->port->tty->termios);
/* we have completed the open */
- edge_port->openPending = FALSE;
- edge_port->open = TRUE;
+ edge_port->openPending = false;
+ edge_port->open = true;
wake_up(&edge_port->wait_open);
return;
}
@@ -1983,7 +1983,7 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2
// If port is closed, silently discard all rcvd status. We can
// have cases where buffered status is received AFTER the close
// port command is sent to the Edgeport.
- if ((!edge_port->open ) || (edge_port->closePending)) {
+ if (!edge_port->open || edge_port->closePending) {
return;
}
@@ -1991,14 +1991,14 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2
// Not currently sent by Edgeport
case IOSP_STATUS_LSR:
dbg("%s - Port %u LSR Status = %02x", __FUNCTION__, edge_serial->rxPort, byte2);
- handle_new_lsr (edge_port, FALSE, byte2, 0);
+ handle_new_lsr(edge_port, false, byte2, 0);
break;
case IOSP_STATUS_LSR_DATA:
dbg("%s - Port %u LSR Status = %02x, Data = %02x", __FUNCTION__, edge_serial->rxPort, byte2, byte3);
// byte2 is LSR Register
// byte3 is broken data byte
- handle_new_lsr (edge_port, TRUE, byte2, byte3);
+ handle_new_lsr(edge_port, true, byte2, byte3);
break;
//
// case IOSP_EXT_4_STATUS:
@@ -2317,14 +2317,14 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer
if (!urb)
return -ENOMEM;
- CmdUrbs++;
- dbg("%s - ALLOCATE URB %p (outstanding %d)", __FUNCTION__, urb, CmdUrbs);
+ atomic_inc(&CmdUrbs);
+ dbg("%s - ALLOCATE URB %p (outstanding %d)", __FUNCTION__, urb, atomic_read(&CmdUrbs));
usb_fill_bulk_urb (urb, edge_serial->serial->dev,
usb_sndbulkpipe(edge_serial->serial->dev, edge_serial->bulk_out_endpoint),
buffer, length, edge_bulk_out_cmd_callback, edge_port);
- edge_port->commandPending = TRUE;
+ edge_port->commandPending = true;
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status) {
@@ -2332,16 +2332,16 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer
dev_err(&edge_port->port->dev, "%s - usb_submit_urb(write command) failed, status = %d\n", __FUNCTION__, status);
usb_kill_urb(urb);
usb_free_urb(urb);
- CmdUrbs--;
+ atomic_dec(&CmdUrbs);
return status;
}
// wait for command to finish
timeout = COMMAND_TIMEOUT;
#if 0
- wait_event (&edge_port->wait_command, (edge_port->commandPending == FALSE));
+ wait_event (&edge_port->wait_command, !edge_port->commandPending);
- if (edge_port->commandPending == TRUE) {
+ if (edge_port->commandPending) {
/* command timed out */
dbg("%s - command timed out", __FUNCTION__);
status = -EINVAL;
@@ -2524,8 +2524,8 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi
dbg("%s - port %d", __FUNCTION__, edge_port->port->number);
- if ((!edge_port->open) &&
- (!edge_port->openPending)) {
+ if (!edge_port->open &&
+ !edge_port->openPending) {
dbg("%s - port not opened", __FUNCTION__);
return;
}
@@ -2836,9 +2836,9 @@ static int edge_startup (struct usb_serial *serial)
struct usb_device *dev;
int i, j;
int response;
- int interrupt_in_found;
- int bulk_in_found;
- int bulk_out_found;
+ bool interrupt_in_found;
+ bool bulk_in_found;
+ bool bulk_out_found;
static __u32 descriptor[3] = { EDGE_COMPATIBILITY_MASK0,
EDGE_COMPATIBILITY_MASK1,
EDGE_COMPATIBILITY_MASK2 };
@@ -2936,14 +2936,14 @@ static int edge_startup (struct usb_serial *serial)
if (edge_serial->is_epic) {
/* EPIC thing, set up our interrupt polling now and our read urb, so
* that the device knows it really is connected. */
- interrupt_in_found = bulk_in_found = bulk_out_found = FALSE;
+ interrupt_in_found = bulk_in_found = bulk_out_found = false;
for (i = 0; i < serial->interface->altsetting[0].desc.bNumEndpoints; ++i) {
struct usb_endpoint_descriptor *endpoint;
int buffer_size;
endpoint = &serial->interface->altsetting[0].endpoint[i].desc;
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
- if ((!interrupt_in_found) &&
+ if (!interrupt_in_found &&
(usb_endpoint_is_int_in(endpoint))) {
/* we found a interrupt in endpoint */
dbg("found interrupt in");
@@ -2972,10 +2972,10 @@ static int edge_startup (struct usb_serial *serial)
edge_serial,
endpoint->bInterval);
- interrupt_in_found = TRUE;
+ interrupt_in_found = true;
}
- if ((!bulk_in_found) &&
+ if (!bulk_in_found &&
(usb_endpoint_is_bulk_in(endpoint))) {
/* we found a bulk in endpoint */
dbg("found bulk in");
@@ -3001,19 +3001,19 @@ static int edge_startup (struct usb_serial *serial)
endpoint->wMaxPacketSize,
edge_bulk_in_callback,
edge_serial);
- bulk_in_found = TRUE;
+ bulk_in_found = true;
}
- if ((!bulk_out_found) &&
+ if (!bulk_out_found &&
(usb_endpoint_is_bulk_out(endpoint))) {
/* we found a bulk out endpoint */
dbg("found bulk out");
edge_serial->bulk_out_endpoint = endpoint->bEndpointAddress;
- bulk_out_found = TRUE;
+ bulk_out_found = true;
}
}
- if ((!interrupt_in_found) || (!bulk_in_found) || (!bulk_out_found)) {
+ if (!interrupt_in_found || !bulk_in_found || !bulk_out_found) {
err ("Error - the proper endpoints were not found!");
return -ENODEV;
}
@@ -3083,6 +3083,7 @@ static int __init edgeport_init(void)
retval = usb_register(&io_driver);
if (retval)
goto failed_usb_register;
+ atomic_set(&CmdUrbs, 0);
info(DRIVER_DESC " " DRIVER_VERSION);
return 0;
diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h
index 29a913a6dac..cb201c1f67f 100644
--- a/drivers/usb/serial/io_edgeport.h
+++ b/drivers/usb/serial/io_edgeport.h
@@ -19,12 +19,6 @@
#define MAX_RS232_PORTS 8 /* Max # of RS-232 ports per device */
/* typedefs that the insideout headers need */
-#ifndef TRUE
- #define TRUE (1)
-#endif
-#ifndef FALSE
- #define FALSE (0)
-#endif
#ifndef LOW8
#define LOW8(a) ((unsigned char)(a & 0xff))
#endif
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index a408184334e..4df0ec74e0b 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -247,12 +247,15 @@ static struct usb_device_id ipaq_id_table [] = {
{ USB_DEVICE(0x04AD, 0x0301) }, /* USB Sync 0301 */
{ USB_DEVICE(0x04AD, 0x0302) }, /* USB Sync 0302 */
{ USB_DEVICE(0x04AD, 0x0303) }, /* USB Sync 0303 */
+ { USB_DEVICE(0x04AD, 0x0306) }, /* GPS Pocket PC USB Sync */
+ { USB_DEVICE(0x04B7, 0x0531) }, /* MyGuide 7000 XL USB Sync */
{ USB_DEVICE(0x04C5, 0x1058) }, /* FUJITSU USB Sync */
{ USB_DEVICE(0x04C5, 0x1079) }, /* FUJITSU USB Sync */
{ USB_DEVICE(0x04DA, 0x2500) }, /* Panasonic USB Sync */
{ USB_DEVICE(0x04DD, 0x9102) }, /* SHARP WS003SH USB Modem */
{ USB_DEVICE(0x04DD, 0x9121) }, /* SHARP WS004SH USB Modem */
{ USB_DEVICE(0x04DD, 0x9123) }, /* SHARP WS007SH USB Modem */
+ { USB_DEVICE(0x04DD, 0x9151) }, /* SHARP S01SH USB Modem */
{ USB_DEVICE(0x04E8, 0x5F00) }, /* Samsung NEXiO USB Sync */
{ USB_DEVICE(0x04E8, 0x5F01) }, /* Samsung NEXiO USB Sync */
{ USB_DEVICE(0x04E8, 0x5F02) }, /* Samsung NEXiO USB Sync */
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index b2097c45a23..7b085f334ce 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -238,7 +238,7 @@ static int klsi_105_get_line_state(struct usb_serial_port *port,
if (rc < 0)
err("Reading line status failed (error = %d)", rc);
else {
- status = status_buf[0] + (status_buf[1]<<8);
+ status = le16_to_cpu(*(u16 *)status_buf);
info("%s - read status %x %x", __FUNCTION__,
status_buf[0], status_buf[1]);
@@ -257,7 +257,7 @@ static int klsi_105_get_line_state(struct usb_serial_port *port,
static int klsi_105_startup (struct usb_serial *serial)
{
struct klsi_105_private *priv;
- int i;
+ int i, j;
/* check if we support the product id (see keyspan.c)
* FIXME
@@ -265,12 +265,12 @@ static int klsi_105_startup (struct usb_serial *serial)
/* allocate the private data structure */
for (i=0; i<serial->num_ports; i++) {
- int j;
priv = kmalloc(sizeof(struct klsi_105_private),
GFP_KERNEL);
if (!priv) {
dbg("%skmalloc for klsi_105_private failed.", __FUNCTION__);
- return -ENOMEM;
+ i--;
+ goto err_cleanup;
}
/* set initial values for control structures */
priv->cfg.pktlen = 5;
@@ -292,15 +292,14 @@ static int klsi_105_startup (struct usb_serial *serial)
priv->write_urb_pool[j] = urb;
if (urb == NULL) {
err("No more urbs???");
- continue;
+ goto err_cleanup;
}
- urb->transfer_buffer = NULL;
urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE,
GFP_KERNEL);
if (!urb->transfer_buffer) {
err("%s - out of memory for urb buffers.", __FUNCTION__);
- continue;
+ goto err_cleanup;
}
}
@@ -308,7 +307,20 @@ static int klsi_105_startup (struct usb_serial *serial)
init_waitqueue_head(&serial->port[i]->write_wait);
}
- return (0);
+ return 0;
+
+err_cleanup:
+ for (; i >= 0; i--) {
+ priv = usb_get_serial_port_data(serial->port[i]);
+ for (j=0; j < NUM_URBS; j++) {
+ if (priv->write_urb_pool[j]) {
+ kfree(priv->write_urb_pool[j]->transfer_buffer);
+ usb_free_urb(priv->write_urb_pool[j]);
+ }
+ }
+ usb_set_serial_port_data(serial->port[i], NULL);
+ }
+ return -ENOMEM;
} /* klsi_105_startup */
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index 4cd839b1407..3db1adc25f8 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -438,17 +438,21 @@ static int mct_u232_open (struct usb_serial_port *port, struct file *filp)
if (retval) {
err("usb_submit_urb(read bulk) failed pipe 0x%x err %d",
port->read_urb->pipe, retval);
- goto exit;
+ goto error;
}
port->interrupt_in_urb->dev = port->serial->dev;
retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
- if (retval)
+ if (retval) {
+ usb_kill_urb(port->read_urb);
err(" usb_submit_urb(read int) failed pipe 0x%x err %d",
port->interrupt_in_urb->pipe, retval);
-
-exit:
+ goto error;
+ }
return 0;
+
+error:
+ return retval;
} /* mct_u232_open */
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 2d588fb8257..b563e2ad872 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -103,11 +103,9 @@ static void mos7720_interrupt_callback(struct urb *urb)
{
int result;
int length;
- __u32 *data;
- unsigned int status;
+ __u8 *data;
__u8 sp1;
__u8 sp2;
- __u8 st;
dbg("%s"," : Entering\n");
@@ -141,18 +139,19 @@ static void mos7720_interrupt_callback(struct urb *urb)
* Byte 2 IIR Port 2 (port.number is 1)
* Byte 3 --------------
* Byte 4 FIFO status for both */
- if (length && length > 4) {
+
+ /* the above description is inverted
+ * oneukum 2007-03-14 */
+
+ if (unlikely(length != 4)) {
dbg("Wrong data !!!");
return;
}
- status = *data;
-
- sp1 = (status & 0xff000000)>>24;
- sp2 = (status & 0x00ff0000)>>16;
- st = status & 0x000000ff;
+ sp1 = data[3];
+ sp2 = data[2];
- if ((sp1 & 0x01) || (sp2 & 0x01)) {
+ if ((sp1 | sp2) & 0x01) {
/* No Interrupt Pending in both the ports */
dbg("No Interrupt !!!");
} else {
@@ -333,6 +332,7 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp)
int response;
int port_number;
char data;
+ int allocated_urbs = 0;
int j;
serial = port->serial;
@@ -353,7 +353,7 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp)
/* Initialising the write urb pool */
for (j = 0; j < NUM_URBS; ++j) {
- urb = usb_alloc_urb(0,GFP_ATOMIC);
+ urb = usb_alloc_urb(0,GFP_KERNEL);
mos7720_port->write_urb_pool[j] = urb;
if (urb == NULL) {
@@ -365,10 +365,16 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp)
GFP_KERNEL);
if (!urb->transfer_buffer) {
err("%s-out of memory for urb buffers.", __FUNCTION__);
+ usb_free_urb(mos7720_port->write_urb_pool[j]);
+ mos7720_port->write_urb_pool[j] = NULL;
continue;
}
+ allocated_urbs++;
}
+ if (!allocated_urbs)
+ return -ENOMEM;
+
/* Initialize MCS7720 -- Write Init values to corresponding Registers
*
* Register Index
@@ -526,7 +532,7 @@ static int mos7720_chars_in_buffer(struct usb_serial_port *port)
}
for (i = 0; i < NUM_URBS; ++i) {
- if (mos7720_port->write_urb_pool[i]->status == -EINPROGRESS)
+ if (mos7720_port->write_urb_pool[i] && mos7720_port->write_urb_pool[i]->status == -EINPROGRESS)
chars += URB_TRANSFER_BUFFER_SIZE;
}
dbg("%s - returns %d", __FUNCTION__, chars);
@@ -629,7 +635,7 @@ static int mos7720_write_room(struct usb_serial_port *port)
}
for (i = 0; i < NUM_URBS; ++i) {
- if (mos7720_port->write_urb_pool[i]->status != -EINPROGRESS)
+ if (mos7720_port->write_urb_pool[i] && mos7720_port->write_urb_pool[i]->status != -EINPROGRESS)
room += URB_TRANSFER_BUFFER_SIZE;
}
@@ -664,7 +670,7 @@ static int mos7720_write(struct usb_serial_port *port,
urb = NULL;
for (i = 0; i < NUM_URBS; ++i) {
- if (mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) {
+ if (mos7720_port->write_urb_pool[i] && mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) {
urb = mos7720_port->write_urb_pool[i];
dbg("URB:%d",i);
break;
@@ -1628,6 +1634,7 @@ static struct usb_serial_driver moschip7720_2port_driver = {
.chars_in_buffer = mos7720_chars_in_buffer,
.break_ctl = mos7720_break,
.read_bulk_callback = mos7720_bulk_in_callback,
+ .read_int_callback = mos7720_interrupt_callback,
};
static int __init moschip7720_init(void)
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index c6cca859af4..2366e7b63ec 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -176,9 +176,12 @@ struct moschip_port {
int port_num; /*Actual port number in the device(1,2,etc) */
struct urb *write_urb; /* write URB for this port */
struct urb *read_urb; /* read URB for this port */
+ struct urb *int_urb;
__u8 shadowLCR; /* last LCR value received */
__u8 shadowMCR; /* last MCR value received */
char open;
+ char open_ports;
+ char zombie;
wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */
wait_queue_head_t delta_msr_wait; /* for handling sleeping while waiting for msr change to happen */
int delta_msr_cond;
@@ -191,17 +194,17 @@ struct moschip_port {
__u8 DcrRegOffset;
//for processing control URBS in interrupt context
struct urb *control_urb;
+ struct usb_ctrlrequest *dr;
char *ctrl_buf;
int MsrLsr;
+ spinlock_t pool_lock;
struct urb *write_urb_pool[NUM_URBS];
+ char busy[NUM_URBS];
};
static int debug;
-static int mos7840_num_ports; //this says the number of ports in the device
-static int mos7840_num_open_ports;
-
/*
* mos7840_set_reg_sync
@@ -254,7 +257,7 @@ static int mos7840_set_uart_reg(struct usb_serial_port *port, __u16 reg,
struct usb_device *dev = port->serial->dev;
val = val & 0x00ff;
// For the UART control registers, the application number need to be Or'ed
- if (mos7840_num_ports == 4) {
+ if (port->serial->num_ports == 4) {
val |=
(((__u16) port->number - (__u16) (port->serial->minor)) +
1) << 8;
@@ -294,7 +297,7 @@ static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg,
//dbg("application number is %4x \n",(((__u16)port->number - (__u16)(port->serial->minor))+1)<<8);
/*Wval is same as application number */
- if (mos7840_num_ports == 4) {
+ if (port->serial->num_ports == 4) {
Wval =
(((__u16) port->number - (__u16) (port->serial->minor)) +
1) << 8;
@@ -352,7 +355,7 @@ static inline struct moschip_port *mos7840_get_port_private(struct
return (struct moschip_port *)usb_get_serial_port_data(port);
}
-static int mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr)
+static void mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr)
{
struct moschip_port *mos7840_port;
struct async_icount *icount;
@@ -366,22 +369,24 @@ static int mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr)
/* update input line counters */
if (new_msr & MOS_MSR_DELTA_CTS) {
icount->cts++;
+ smp_wmb();
}
if (new_msr & MOS_MSR_DELTA_DSR) {
icount->dsr++;
+ smp_wmb();
}
if (new_msr & MOS_MSR_DELTA_CD) {
icount->dcd++;
+ smp_wmb();
}
if (new_msr & MOS_MSR_DELTA_RI) {
icount->rng++;
+ smp_wmb();
}
}
-
- return 0;
}
-static int mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr)
+static void mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr)
{
struct async_icount *icount;
@@ -400,18 +405,20 @@ static int mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr)
icount = &port->icount;
if (new_lsr & SERIAL_LSR_BI) {
icount->brk++;
+ smp_wmb();
}
if (new_lsr & SERIAL_LSR_OE) {
icount->overrun++;
+ smp_wmb();
}
if (new_lsr & SERIAL_LSR_PE) {
icount->parity++;
+ smp_wmb();
}
if (new_lsr & SERIAL_LSR_FE) {
icount->frame++;
+ smp_wmb();
}
-
- return 0;
}
/************************************************************************/
@@ -426,12 +433,15 @@ static void mos7840_control_callback(struct urb *urb)
unsigned char *data;
struct moschip_port *mos7840_port;
__u8 regval = 0x0;
+ int result = 0;
if (!urb) {
dbg("%s", "Invalid Pointer !!!!:\n");
return;
}
+ mos7840_port = (struct moschip_port *)urb->context;
+
switch (urb->status) {
case 0:
/* success */
@@ -449,8 +459,6 @@ static void mos7840_control_callback(struct urb *urb)
goto exit;
}
- mos7840_port = (struct moschip_port *)urb->context;
-
dbg("%s urb buffer size is %d\n", __FUNCTION__, urb->actual_length);
dbg("%s mos7840_port->MsrLsr is %d port %d\n", __FUNCTION__,
mos7840_port->MsrLsr, mos7840_port->port_num);
@@ -462,21 +470,26 @@ static void mos7840_control_callback(struct urb *urb)
else if (mos7840_port->MsrLsr == 1)
mos7840_handle_new_lsr(mos7840_port, regval);
- exit:
- return;
+exit:
+ spin_lock(&mos7840_port->pool_lock);
+ if (!mos7840_port->zombie)
+ result = usb_submit_urb(mos7840_port->int_urb, GFP_ATOMIC);
+ spin_unlock(&mos7840_port->pool_lock);
+ if (result) {
+ dev_err(&urb->dev->dev,
+ "%s - Error %d submitting interrupt urb\n",
+ __FUNCTION__, result);
+ }
}
static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
__u16 * val)
{
struct usb_device *dev = mcs->port->serial->dev;
- struct usb_ctrlrequest *dr = NULL;
- unsigned char *buffer = NULL;
- int ret = 0;
- buffer = (__u8 *) mcs->ctrl_buf;
+ struct usb_ctrlrequest *dr = mcs->dr;
+ unsigned char *buffer = mcs->ctrl_buf;
+ int ret;
-// dr=(struct usb_ctrlrequest *)(buffer);
- dr = (void *)(buffer + 2);
dr->bRequestType = MCS_RD_RTYPE;
dr->bRequest = MCS_RDREQ;
dr->wValue = cpu_to_le16(Wval); //0;
@@ -506,8 +519,8 @@ static void mos7840_interrupt_callback(struct urb *urb)
__u16 Data;
unsigned char *data;
__u8 sp[5], st;
- int i;
- __u16 wval;
+ int i, rv = 0;
+ __u16 wval, wreg = 0;
dbg("%s", " : Entering\n");
if (!urb) {
@@ -569,31 +582,34 @@ static void mos7840_interrupt_callback(struct urb *urb)
dbg("Serial Port %d: Receiver status error or ", i);
dbg("address bit detected in 9-bit mode\n");
mos7840_port->MsrLsr = 1;
- mos7840_get_reg(mos7840_port, wval,
- LINE_STATUS_REGISTER,
- &Data);
+ wreg = LINE_STATUS_REGISTER;
break;
case SERIAL_IIR_MS:
dbg("Serial Port %d: Modem status change\n", i);
mos7840_port->MsrLsr = 0;
- mos7840_get_reg(mos7840_port, wval,
- MODEM_STATUS_REGISTER,
- &Data);
+ wreg = MODEM_STATUS_REGISTER;
break;
}
+ spin_lock(&mos7840_port->pool_lock);
+ if (!mos7840_port->zombie) {
+ rv = mos7840_get_reg(mos7840_port, wval, wreg, &Data);
+ } else {
+ spin_unlock(&mos7840_port->pool_lock);
+ return;
+ }
+ spin_unlock(&mos7840_port->pool_lock);
}
}
}
- exit:
+ if (!(rv < 0)) /* the completion handler for the control urb will resubmit */
+ return;
+exit:
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result) {
dev_err(&urb->dev->dev,
"%s - Error %d submitting interrupt urb\n",
__FUNCTION__, result);
}
-
- return;
-
}
static int mos7840_port_paranoia_check(struct usb_serial_port *port,
@@ -634,7 +650,8 @@ static struct usb_serial *mos7840_get_usb_serial(struct usb_serial_port *port,
if (!port ||
mos7840_port_paranoia_check(port, function) ||
mos7840_serial_paranoia_check(port->serial, function)) {
- /* then say that we don't have a valid usb_serial thing, which will * end up genrating -ENODEV return values */
+ /* then say that we don't have a valid usb_serial thing, which will
+ * end up genrating -ENODEV return values */
return NULL;
}
@@ -699,6 +716,7 @@ static void mos7840_bulk_in_callback(struct urb *urb)
tty_flip_buffer_push(tty);
}
mos7840_port->icount.rx += urb->actual_length;
+ smp_wmb();
dbg("mos7840_port->icount.rx is %d:\n",
mos7840_port->icount.rx);
}
@@ -708,15 +726,14 @@ static void mos7840_bulk_in_callback(struct urb *urb)
return;
}
- if (mos7840_port->read_urb->status != -EINPROGRESS) {
- mos7840_port->read_urb->dev = serial->dev;
- status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+ mos7840_port->read_urb->dev = serial->dev;
- if (status) {
- dbg(" usb_submit_urb(read bulk) failed, status = %d",
- status);
- }
+ status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+
+ if (status) {
+ dbg(" usb_submit_urb(read bulk) failed, status = %d",
+ status);
}
}
@@ -730,17 +747,28 @@ static void mos7840_bulk_out_data_callback(struct urb *urb)
{
struct moschip_port *mos7840_port;
struct tty_struct *tty;
+ int i;
+
if (!urb) {
dbg("%s", "Invalid Pointer !!!!:\n");
return;
}
+ mos7840_port = (struct moschip_port *)urb->context;
+ spin_lock(&mos7840_port->pool_lock);
+ for (i = 0; i < NUM_URBS; i++) {
+ if (urb == mos7840_port->write_urb_pool[i]) {
+ mos7840_port->busy[i] = 0;
+ break;
+ }
+ }
+ spin_unlock(&mos7840_port->pool_lock);
+
if (urb->status) {
dbg("nonzero write bulk status received:%d\n", urb->status);
return;
}
- mos7840_port = (struct moschip_port *)urb->context;
if (!mos7840_port) {
dbg("%s", "NULL mos7840_port pointer \n");
return;
@@ -792,13 +820,13 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
__u16 Data;
int status;
struct moschip_port *mos7840_port;
+ struct moschip_port *port0;
if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
dbg("%s", "Port Paranoia failed \n");
return -ENODEV;
}
- mos7840_num_open_ports++;
serial = port->serial;
if (mos7840_serial_paranoia_check(serial, __FUNCTION__)) {
@@ -807,16 +835,18 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
}
mos7840_port = mos7840_get_port_private(port);
+ port0 = mos7840_get_port_private(serial->port[0]);
- if (mos7840_port == NULL)
+ if (mos7840_port == NULL || port0 == NULL)
return -ENODEV;
usb_clear_halt(serial->dev, port->write_urb->pipe);
usb_clear_halt(serial->dev, port->read_urb->pipe);
+ port0->open_ports++;
/* Initialising the write urb pool */
for (j = 0; j < NUM_URBS; ++j) {
- urb = usb_alloc_urb(0, GFP_ATOMIC);
+ urb = usb_alloc_urb(0, GFP_KERNEL);
mos7840_port->write_urb_pool[j] = urb;
if (urb == NULL) {
@@ -824,10 +854,10 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
continue;
}
- urb->transfer_buffer = NULL;
- urb->transfer_buffer =
- kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+ urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
if (!urb->transfer_buffer) {
+ usb_free_urb(urb);
+ mos7840_port->write_urb_pool[j] = NULL;
err("%s-out of memory for urb buffers.", __FUNCTION__);
continue;
}
@@ -879,9 +909,7 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
}
Data |= 0x08; //Driver done bit
Data |= 0x20; //rx_disable
- status = 0;
- status =
- mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data);
+ status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data);
if (status < 0) {
dbg("writing Controlreg failed\n");
return -1;
@@ -893,7 +921,6 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
////////////////////////////////////
Data = 0x00;
- status = 0;
status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
if (status < 0) {
dbg("disableing interrupts failed\n");
@@ -901,7 +928,6 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
}
// Set FIFO_CONTROL_REGISTER to the default value
Data = 0x00;
- status = 0;
status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
if (status < 0) {
dbg("Writing FIFO_CONTROL_REGISTER failed\n");
@@ -909,7 +935,6 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
}
Data = 0xcf;
- status = 0;
status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
if (status < 0) {
dbg("Writing FIFO_CONTROL_REGISTER failed\n");
@@ -917,22 +942,18 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
}
Data = 0x03;
- status = 0;
status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
mos7840_port->shadowLCR = Data;
Data = 0x0b;
- status = 0;
status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
mos7840_port->shadowMCR = Data;
Data = 0x00;
- status = 0;
status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
mos7840_port->shadowLCR = Data;
Data |= SERIAL_LCR_DLAB; //data latch enable in LCR 0x80
- status = 0;
status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
Data = 0x0c;
@@ -999,7 +1020,7 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
/* Check to see if we've set up our endpoint info yet *
* (can't set it up in mos7840_startup as the structures *
* were not set up at that time.) */
- if (mos7840_num_open_ports == 1) {
+ if (port0->open_ports == 1) {
if (serial->port[0]->interrupt_in_buffer == NULL) {
/* set up interrupt urb */
@@ -1097,6 +1118,7 @@ static int mos7840_chars_in_buffer(struct usb_serial_port *port)
{
int i;
int chars = 0;
+ unsigned long flags;
struct moschip_port *mos7840_port;
dbg("%s \n", " mos7840_chars_in_buffer:entering ...........");
@@ -1112,13 +1134,15 @@ static int mos7840_chars_in_buffer(struct usb_serial_port *port)
return -1;
}
+ spin_lock_irqsave(&mos7840_port->pool_lock,flags);
for (i = 0; i < NUM_URBS; ++i) {
- if (mos7840_port->write_urb_pool[i]->status == -EINPROGRESS) {
+ if (mos7840_port->busy[i]) {
chars += URB_TRANSFER_BUFFER_SIZE;
}
}
+ spin_unlock_irqrestore(&mos7840_port->pool_lock,flags);
dbg("%s - returns %d", __FUNCTION__, chars);
- return (chars);
+ return chars;
}
@@ -1172,6 +1196,7 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp)
{
struct usb_serial *serial;
struct moschip_port *mos7840_port;
+ struct moschip_port *port0;
int j;
__u16 Data;
@@ -1189,10 +1214,10 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp)
}
mos7840_port = mos7840_get_port_private(port);
+ port0 = mos7840_get_port_private(serial->port[0]);
- if (mos7840_port == NULL) {
+ if (mos7840_port == NULL || port0 == NULL)
return;
- }
for (j = 0; j < NUM_URBS; ++j)
usb_kill_urb(mos7840_port->write_urb_pool[j]);
@@ -1234,12 +1259,13 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp)
}
// if(mos7840_port->ctrl_buf != NULL)
// kfree(mos7840_port->ctrl_buf);
- mos7840_num_open_ports--;
+ port0->open_ports--;
dbg("mos7840_num_open_ports in close%d:in port%d\n",
- mos7840_num_open_ports, port->number);
- if (mos7840_num_open_ports == 0) {
+ port0->open_ports, port->number);
+ if (port0->open_ports == 0) {
if (serial->port[0]->interrupt_in_urb) {
dbg("%s", "Shutdown interrupt_in_urb\n");
+ usb_kill_urb(serial->port[0]->interrupt_in_urb);
}
}
@@ -1368,6 +1394,7 @@ static int mos7840_write_room(struct usb_serial_port *port)
{
int i;
int room = 0;
+ unsigned long flags;
struct moschip_port *mos7840_port;
dbg("%s \n", " mos7840_write_room:entering ...........");
@@ -1384,14 +1411,17 @@ static int mos7840_write_room(struct usb_serial_port *port)
return -1;
}
+ spin_lock_irqsave(&mos7840_port->pool_lock, flags);
for (i = 0; i < NUM_URBS; ++i) {
- if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) {
+ if (!mos7840_port->busy[i]) {
room += URB_TRANSFER_BUFFER_SIZE;
}
}
+ spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
+ room = (room == 0) ? 0 : room - URB_TRANSFER_BUFFER_SIZE + 1;
dbg("%s - returns %d", __FUNCTION__, room);
- return (room);
+ return room;
}
@@ -1410,6 +1440,7 @@ static int mos7840_write(struct usb_serial_port *port,
int i;
int bytes_sent = 0;
int transfer_size;
+ unsigned long flags;
struct moschip_port *mos7840_port;
struct usb_serial *serial;
@@ -1476,13 +1507,16 @@ static int mos7840_write(struct usb_serial_port *port,
/* try to find a free urb in the list */
urb = NULL;
+ spin_lock_irqsave(&mos7840_port->pool_lock, flags);
for (i = 0; i < NUM_URBS; ++i) {
- if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) {
+ if (!mos7840_port->busy[i]) {
+ mos7840_port->busy[i] = 1;
urb = mos7840_port->write_urb_pool[i];
dbg("\nURB:%d", i);
break;
}
}
+ spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
if (urb == NULL) {
dbg("%s - no more free urbs", __FUNCTION__);
@@ -1518,6 +1552,7 @@ static int mos7840_write(struct usb_serial_port *port,
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status) {
+ mos7840_port->busy[i] = 0;
err("%s - usb_submit_urb(write bulk) failed with status = %d",
__FUNCTION__, status);
bytes_sent = status;
@@ -1525,6 +1560,7 @@ static int mos7840_write(struct usb_serial_port *port,
}
bytes_sent = transfer_size;
mos7840_port->icount.tx += transfer_size;
+ smp_wmb();
dbg("mos7840_port->icount.tx is %d:\n", mos7840_port->icount.tx);
exit:
@@ -2490,6 +2526,7 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file,
if (signal_pending(current))
return -ERESTARTSYS;
cnow = mos7840_port->icount;
+ smp_rmb();
if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
return -EIO; /* no change => error */
@@ -2506,6 +2543,7 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file,
case TIOCGICOUNT:
cnow = mos7840_port->icount;
+ smp_rmb();
icount.cts = cnow.cts;
icount.dsr = cnow.dsr;
icount.rng = cnow.rng;
@@ -2535,19 +2573,18 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file,
static int mos7840_calc_num_ports(struct usb_serial *serial)
{
+ int mos7840_num_ports = 0;
dbg("numberofendpoints: %d \n",
(int)serial->interface->cur_altsetting->desc.bNumEndpoints);
dbg("numberofendpoints: %d \n",
(int)serial->interface->altsetting->desc.bNumEndpoints);
if (serial->interface->cur_altsetting->desc.bNumEndpoints == 5) {
- mos7840_num_ports = 2;
- serial->type->num_ports = 2;
+ mos7840_num_ports = serial->num_ports = 2;
} else if (serial->interface->cur_altsetting->desc.bNumEndpoints == 9) {
- mos7840_num_ports = 4;
- serial->type->num_bulk_in = 4;
- serial->type->num_bulk_out = 4;
- serial->type->num_ports = 4;
+ serial->num_bulk_in = 4;
+ serial->num_bulk_out = 4;
+ mos7840_num_ports = serial->num_ports = 4;
}
return mos7840_num_ports;
@@ -2583,7 +2620,9 @@ static int mos7840_startup(struct usb_serial *serial)
mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL);
if (mos7840_port == NULL) {
err("%s - Out of memory", __FUNCTION__);
- return -ENOMEM;
+ status = -ENOMEM;
+ i--; /* don't follow NULL pointer cleaning up */
+ goto error;
}
/* Initialize all port interrupt end point to port 0 int endpoint *
@@ -2591,6 +2630,7 @@ static int mos7840_startup(struct usb_serial *serial)
mos7840_port->port = serial->port[i];
mos7840_set_port_private(serial->port[i], mos7840_port);
+ spin_lock_init(&mos7840_port->pool_lock);
mos7840_port->port_num = ((serial->port[i]->number -
(serial->port[i]->serial->minor)) +
@@ -2601,22 +2641,22 @@ static int mos7840_startup(struct usb_serial *serial)
mos7840_port->ControlRegOffset = 0x1;
mos7840_port->DcrRegOffset = 0x4;
} else if ((mos7840_port->port_num == 2)
- && (mos7840_num_ports == 4)) {
+ && (serial->num_ports == 4)) {
mos7840_port->SpRegOffset = 0x8;
mos7840_port->ControlRegOffset = 0x9;
mos7840_port->DcrRegOffset = 0x16;
} else if ((mos7840_port->port_num == 2)
- && (mos7840_num_ports == 2)) {
+ && (serial->num_ports == 2)) {
mos7840_port->SpRegOffset = 0xa;
mos7840_port->ControlRegOffset = 0xb;
mos7840_port->DcrRegOffset = 0x19;
} else if ((mos7840_port->port_num == 3)
- && (mos7840_num_ports == 4)) {
+ && (serial->num_ports == 4)) {
mos7840_port->SpRegOffset = 0xa;
mos7840_port->ControlRegOffset = 0xb;
mos7840_port->DcrRegOffset = 0x19;
} else if ((mos7840_port->port_num == 4)
- && (mos7840_num_ports == 4)) {
+ && (serial->num_ports == 4)) {
mos7840_port->SpRegOffset = 0xc;
mos7840_port->ControlRegOffset = 0xd;
mos7840_port->DcrRegOffset = 0x1c;
@@ -2701,21 +2741,19 @@ static int mos7840_startup(struct usb_serial *serial)
dbg("CLK_START_VALUE_REGISTER Writing success status%d\n", status);
Data = 0x20;
- status = 0;
status =
mos7840_set_reg_sync(serial->port[i], CLK_MULTI_REGISTER,
Data);
if (status < 0) {
dbg("Writing CLK_MULTI_REGISTER failed status-0x%x\n",
status);
- break;
+ goto error;
} else
dbg("CLK_MULTI_REGISTER Writing success status%d\n",
status);
//write value 0x0 to scratchpad register
Data = 0x00;
- status = 0;
status =
mos7840_set_uart_reg(serial->port[i], SCRATCH_PAD_REGISTER,
Data);
@@ -2729,7 +2767,7 @@ static int mos7840_startup(struct usb_serial *serial)
//Zero Length flag register
if ((mos7840_port->port_num != 1)
- && (mos7840_num_ports == 2)) {
+ && (serial->num_ports == 2)) {
Data = 0xff;
status = 0;
@@ -2770,14 +2808,17 @@ static int mos7840_startup(struct usb_serial *serial)
i + 1, status);
}
- mos7840_port->control_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ mos7840_port->control_urb = usb_alloc_urb(0, GFP_KERNEL);
mos7840_port->ctrl_buf = kmalloc(16, GFP_KERNEL);
-
+ mos7840_port->dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+ if (!mos7840_port->control_urb || !mos7840_port->ctrl_buf || !mos7840_port->dr) {
+ status = -ENOMEM;
+ goto error;
+ }
}
//Zero Length flag enable
Data = 0x0f;
- status = 0;
status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data);
if (status < 0) {
dbg("Writing ZLP_REG5 failed status-0x%x\n", status);
@@ -2789,6 +2830,17 @@ static int mos7840_startup(struct usb_serial *serial)
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
(__u8) 0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5 * HZ);
return 0;
+error:
+ for (/* nothing */; i >= 0; i--) {
+ mos7840_port = mos7840_get_port_private(serial->port[i]);
+
+ kfree(mos7840_port->dr);
+ kfree(mos7840_port->ctrl_buf);
+ usb_free_urb(mos7840_port->control_urb);
+ kfree(mos7840_port);
+ serial->port[i] = NULL;
+ }
+ return status;
}
/****************************************************************************
@@ -2799,6 +2851,7 @@ static int mos7840_startup(struct usb_serial *serial)
static void mos7840_shutdown(struct usb_serial *serial)
{
int i;
+ unsigned long flags;
struct moschip_port *mos7840_port;
dbg("%s \n", " shutdown :entering..........");
@@ -2814,8 +2867,12 @@ static void mos7840_shutdown(struct usb_serial *serial)
for (i = 0; i < serial->num_ports; ++i) {
mos7840_port = mos7840_get_port_private(serial->port[i]);
- kfree(mos7840_port->ctrl_buf);
+ spin_lock_irqsave(&mos7840_port->pool_lock, flags);
+ mos7840_port->zombie = 1;
+ spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
usb_kill_urb(mos7840_port->control_urb);
+ kfree(mos7840_port->ctrl_buf);
+ kfree(mos7840_port->dr);
kfree(mos7840_port);
mos7840_set_port_private(serial->port[i], NULL);
}
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index 0216ac12a27..4adfab988e8 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -69,6 +69,7 @@ static void omninet_write_bulk_callback (struct urb *urb);
static int omninet_write (struct usb_serial_port *port, const unsigned char *buf, int count);
static int omninet_write_room (struct usb_serial_port *port);
static void omninet_shutdown (struct usb_serial *serial);
+static int omninet_attach (struct usb_serial *serial);
static struct usb_device_id id_table [] = {
{ USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) },
@@ -99,6 +100,7 @@ static struct usb_serial_driver zyxel_omninet_device = {
.num_bulk_in = 1,
.num_bulk_out = 2,
.num_ports = 1,
+ .attach = omninet_attach,
.open = omninet_open,
.close = omninet_close,
.write = omninet_write,
@@ -145,22 +147,30 @@ struct omninet_data
__u8 od_outseq; // Sequence number for bulk_out URBs
};
+static int omninet_attach (struct usb_serial *serial)
+{
+ struct omninet_data *od;
+ struct usb_serial_port *port = serial->port[0];
+
+ od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL );
+ if( !od ) {
+ err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct omninet_data));
+ return -ENOMEM;
+ }
+ usb_set_serial_port_data(port, od);
+ return 0;
+}
+
static int omninet_open (struct usb_serial_port *port, struct file *filp)
{
struct usb_serial *serial = port->serial;
struct usb_serial_port *wport;
- struct omninet_data *od;
+ struct omninet_data *od = usb_get_serial_port_data(port);
int result = 0;
dbg("%s - port %d", __FUNCTION__, port->number);
od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL );
- if( !od ) {
- err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct omninet_data));
- return -ENOMEM;
- }
-
- usb_set_serial_port_data(port, od);
wport = serial->port[1];
wport->tty = port->tty;
@@ -170,24 +180,17 @@ static int omninet_open (struct usb_serial_port *port, struct file *filp)
port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
omninet_read_bulk_callback, port);
result = usb_submit_urb(port->read_urb, GFP_KERNEL);
- if (result)
+ if (result) {
err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
+ }
return result;
}
static void omninet_close (struct usb_serial_port *port, struct file * filp)
{
- struct usb_serial *serial = port->serial;
- struct usb_serial_port *wport;
-
dbg("%s - port %d", __FUNCTION__, port->number);
-
- wport = serial->port[1];
- usb_kill_urb(wport->write_urb);
usb_kill_urb(port->read_urb);
-
- kfree(usb_get_serial_port_data(port));
}
@@ -326,7 +329,12 @@ static void omninet_write_bulk_callback (struct urb *urb)
static void omninet_shutdown (struct usb_serial *serial)
{
+ struct usb_serial_port *wport = serial->port[1];
+ struct usb_serial_port *port = serial->port[0];
dbg ("%s", __FUNCTION__);
+
+ usb_kill_urb(wport->write_urb);
+ kfree(usb_get_serial_port_data(port));
}
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index ced9f32b29d..8c3f55b080b 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -67,54 +67,115 @@ static int option_tiocmset(struct usb_serial_port *port, struct file *file,
static int option_send_setup(struct usb_serial_port *port);
/* Vendor and product IDs */
-#define OPTION_VENDOR_ID 0x0AF0
-#define HUAWEI_VENDOR_ID 0x12D1
-#define AUDIOVOX_VENDOR_ID 0x0F3D
-#define NOVATELWIRELESS_VENDOR_ID 0x1410
-#define ANYDATA_VENDOR_ID 0x16d5
-
-#define OPTION_PRODUCT_OLD 0x5000
-#define OPTION_PRODUCT_FUSION 0x6000
-#define OPTION_PRODUCT_FUSION2 0x6300
-#define OPTION_PRODUCT_COBRA 0x6500
-#define OPTION_PRODUCT_COBRA2 0x6600
-#define OPTION_PRODUCT_GTMAX36 0x6701
-#define HUAWEI_PRODUCT_E600 0x1001
-#define HUAWEI_PRODUCT_E220 0x1003
-#define AUDIOVOX_PRODUCT_AIRCARD 0x0112
-#define NOVATELWIRELESS_PRODUCT_U740 0x1400
-#define ANYDATA_PRODUCT_ID 0x6501
+#define OPTION_VENDOR_ID 0x0AF0
+#define OPTION_PRODUCT_COLT 0x5000
+#define OPTION_PRODUCT_RICOLA 0x6000
+#define OPTION_PRODUCT_RICOLA_LIGHT 0x6100
+#define OPTION_PRODUCT_RICOLA_QUAD 0x6200
+#define OPTION_PRODUCT_RICOLA_QUAD_LIGHT 0x6300
+#define OPTION_PRODUCT_RICOLA_NDIS 0x6050
+#define OPTION_PRODUCT_RICOLA_NDIS_LIGHT 0x6150
+#define OPTION_PRODUCT_RICOLA_NDIS_QUAD 0x6250
+#define OPTION_PRODUCT_RICOLA_NDIS_QUAD_LIGHT 0x6350
+#define OPTION_PRODUCT_COBRA 0x6500
+#define OPTION_PRODUCT_COBRA_BUS 0x6501
+#define OPTION_PRODUCT_VIPER 0x6600
+#define OPTION_PRODUCT_VIPER_BUS 0x6601
+#define OPTION_PRODUCT_GT_MAX_READY 0x6701
+#define OPTION_PRODUCT_GT_MAX 0x6711
+#define OPTION_PRODUCT_FUJI_MODEM_LIGHT 0x6721
+#define OPTION_PRODUCT_FUJI_MODEM_GT 0x6741
+#define OPTION_PRODUCT_FUJI_MODEM_EX 0x6761
+#define OPTION_PRODUCT_FUJI_NETWORK_LIGHT 0x6731
+#define OPTION_PRODUCT_FUJI_NETWORK_GT 0x6751
+#define OPTION_PRODUCT_FUJI_NETWORK_EX 0x6771
+#define OPTION_PRODUCT_KOI_MODEM 0x6800
+#define OPTION_PRODUCT_KOI_NETWORK 0x6811
+#define OPTION_PRODUCT_SCORPION_MODEM 0x6901
+#define OPTION_PRODUCT_SCORPION_NETWORK 0x6911
+#define OPTION_PRODUCT_ETNA_MODEM 0x7001
+#define OPTION_PRODUCT_ETNA_NETWORK 0x7011
+#define OPTION_PRODUCT_ETNA_MODEM_LITE 0x7021
+#define OPTION_PRODUCT_ETNA_MODEM_GT 0x7041
+#define OPTION_PRODUCT_ETNA_MODEM_EX 0x7061
+#define OPTION_PRODUCT_ETNA_NETWORK_LITE 0x7031
+#define OPTION_PRODUCT_ETNA_NETWORK_GT 0x7051
+#define OPTION_PRODUCT_ETNA_NETWORK_EX 0x7071
+#define OPTION_PRODUCT_ETNA_KOI_MODEM 0x7100
+#define OPTION_PRODUCT_ETNA_KOI_NETWORK 0x7111
+
+#define HUAWEI_VENDOR_ID 0x12D1
+#define HUAWEI_PRODUCT_E600 0x1001
+#define HUAWEI_PRODUCT_E220 0x1003
+
+#define NOVATELWIRELESS_VENDOR_ID 0x1410
+
+#define ANYDATA_VENDOR_ID 0x16d5
+#define ANYDATA_PRODUCT_ID 0x6501
+
+#define BANDRICH_VENDOR_ID 0x1A8D
+#define BANDRICH_PRODUCT_C100_1 0x1002
+#define BANDRICH_PRODUCT_C100_2 0x1003
+
+#define DELL_VENDOR_ID 0x413C
static struct usb_device_id option_ids[] = {
- { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
- { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
- { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_LIGHT) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD_LIGHT) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_LIGHT) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_QUAD) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_QUAD_LIGHT) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
- { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
- { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GTMAX36) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA_BUS) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER_BUS) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GT_MAX_READY) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GT_MAX) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_LIGHT) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_GT) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_EX) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_LIGHT) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_GT) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_EX) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_KOI_MODEM) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_KOI_NETWORK) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_SCORPION_MODEM) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_SCORPION_NETWORK) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_LITE) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_GT) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_EX) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_LITE) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_GT) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_EX) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_MODEM) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_NETWORK) },
{ USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
{ USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) },
- { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) },
- { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1100) }, /* Novatel Merlin XS620/S640 */
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1110) }, /* Novatel Merlin S620 */
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1120) }, /* Novatel Merlin EX720 */
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1130) }, /* Novatel Merlin S720 */
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1400) }, /* Novatel U730 */
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1410) }, /* Novatel U740 */
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1420) }, /* Novatel EU870 */
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1430) }, /* Novatel Merlin XU870 HSDPA/3G */
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1430) }, /* Novatel XU870 */
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2100) }, /* Novatel EV620 CDMA/EV-DO */
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2110) }, /* Novatel Merlin ES620 / Merlin ES720 / Ovation U720 */
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2130) }, /* Novatel Merlin ES620 SM Bus */
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2410) }, /* Novatel EU740 */
{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
+ { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) },
+ { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) },
+ { USB_DEVICE(DELL_VENDOR_ID, 0x8118) }, /* Dell Wireless 5510 Mobile Broadband HSDPA ExpressCard */
{ } /* Terminating entry */
};
-
-static struct usb_device_id option_ids1[] = {
- { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
- { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
- { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
- { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
- { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
- { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GTMAX36) },
- { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
- { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) },
- { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) },
- { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
- { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
- { } /* Terminating entry */
-};
-
MODULE_DEVICE_TABLE(usb, option_ids);
static struct usb_driver option_driver = {
@@ -136,7 +197,7 @@ static struct usb_serial_driver option_1port_device = {
},
.description = "GSM modem (1-port)",
.usb_driver = &option_driver,
- .id_table = option_ids1,
+ .id_table = option_ids,
.num_interrupt_in = NUM_DONT_CARE,
.num_bulk_in = NUM_DONT_CARE,
.num_bulk_out = NUM_DONT_CARE,
@@ -539,12 +600,6 @@ static int option_open(struct usb_serial_port *port, struct file *filp)
return (0);
}
-static inline void stop_urb(struct urb *urb)
-{
- if (urb && urb->status == -EINPROGRESS)
- usb_kill_urb(urb);
-}
-
static void option_close(struct usb_serial_port *port, struct file *filp)
{
int i;
@@ -562,9 +617,9 @@ static void option_close(struct usb_serial_port *port, struct file *filp)
/* Stop reading/writing urbs */
for (i = 0; i < N_IN_URB; i++)
- stop_urb(portdata->in_urbs[i]);
+ usb_kill_urb(portdata->in_urbs[i]);
for (i = 0; i < N_OUT_URB; i++)
- stop_urb(portdata->out_urbs[i]);
+ usb_kill_urb(portdata->out_urbs[i]);
}
port->tty = NULL;
}
@@ -695,9 +750,9 @@ static void option_shutdown(struct usb_serial *serial)
port = serial->port[i];
portdata = usb_get_serial_port_data(port);
for (j = 0; j < N_IN_URB; j++)
- stop_urb(portdata->in_urbs[j]);
+ usb_kill_urb(portdata->in_urbs[j]);
for (j = 0; j < N_OUT_URB; j++)
- stop_urb(portdata->out_urbs[j]);
+ usb_kill_urb(portdata->out_urbs[j]);
}
/* Now free them */
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 6c083d4e2c9..83dfae93a45 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -83,6 +83,7 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) },
{ USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) },
{ USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ID) },
+ { USB_DEVICE(WS002IN_VENDOR_ID, WS002IN_PRODUCT_ID) },
{ } /* Terminating entry */
};
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index 65a5039665e..f9a71d0c102 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -97,3 +97,8 @@
/* Huawei E620 UMTS/HSDPA card (ID: 12d1:1001) */
#define HUAWEI_VENDOR_ID 0x12d1
#define HUAWEI_PRODUCT_ID 0x1001
+
+/* Willcom WS002IN Data Driver (by NetIndex Inc.) */
+#define WS002IN_VENDOR_ID 0x11f6
+#define WS002IN_PRODUCT_ID 0x2001
+
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index ecedd833818..644607de4c1 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -456,12 +456,6 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp)
return (0);
}
-static inline void stop_urb(struct urb *urb)
-{
- if (urb && urb->status == -EINPROGRESS)
- usb_kill_urb(urb);
-}
-
static void sierra_close(struct usb_serial_port *port, struct file *filp)
{
int i;
@@ -479,9 +473,9 @@ static void sierra_close(struct usb_serial_port *port, struct file *filp)
/* Stop reading/writing urbs */
for (i = 0; i < N_IN_URB; i++)
- stop_urb(portdata->in_urbs[i]);
+ usb_unlink_urb(portdata->in_urbs[i]);
for (i = 0; i < N_OUT_URB; i++)
- stop_urb(portdata->out_urbs[i]);
+ usb_unlink_urb(portdata->out_urbs[i]);
}
port->tty = NULL;
}
@@ -583,17 +577,26 @@ static void sierra_shutdown(struct usb_serial *serial)
/* Stop reading/writing urbs */
for (i = 0; i < serial->num_ports; ++i) {
port = serial->port[i];
+ if (!port)
+ continue;
portdata = usb_get_serial_port_data(port);
+ if (!portdata)
+ continue;
+
for (j = 0; j < N_IN_URB; j++)
- stop_urb(portdata->in_urbs[j]);
+ usb_unlink_urb(portdata->in_urbs[j]);
for (j = 0; j < N_OUT_URB; j++)
- stop_urb(portdata->out_urbs[j]);
+ usb_unlink_urb(portdata->out_urbs[j]);
}
/* Now free them */
for (i = 0; i < serial->num_ports; ++i) {
port = serial->port[i];
+ if (!port)
+ continue;
portdata = usb_get_serial_port_data(port);
+ if (!portdata)
+ continue;
for (j = 0; j < N_IN_URB; j++) {
if (portdata->in_urbs[j]) {
@@ -612,6 +615,8 @@ static void sierra_shutdown(struct usb_serial *serial)
/* Now free per port private data */
for (i = 0; i < serial->num_ports; i++) {
port = serial->port[i];
+ if (!port)
+ continue;
kfree(usb_get_serial_port_data(port));
}
}
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 6bf22a28adb..7639022cdf8 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -99,9 +99,12 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po
continue;
*minor = i;
+ j = 0;
dbg("%s - minor base = %d", __FUNCTION__, *minor);
- for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i)
+ for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) {
serial_table[i] = serial;
+ serial->port[j++]->number = i;
+ }
spin_unlock(&table_lock);
return serial;
}
@@ -826,7 +829,6 @@ int usb_serial_probe(struct usb_interface *interface,
num_ports = type->num_ports;
}
- serial->minor = minor;
serial->num_ports = num_ports;
serial->num_bulk_in = num_bulk_in;
serial->num_bulk_out = num_bulk_out;
@@ -847,7 +849,6 @@ int usb_serial_probe(struct usb_interface *interface,
port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL);
if (!port)
goto probe_error;
- port->number = i + serial->minor;
port->serial = serial;
spin_lock_init(&port->lock);
mutex_init(&port->mutex);
@@ -980,6 +981,7 @@ int usb_serial_probe(struct usb_interface *interface,
dev_err(&interface->dev, "No more free serial devices\n");
goto probe_error;
}
+ serial->minor = minor;
/* register all of the individual ports with the driver core */
for (i = 0; i < num_ports; ++i) {
@@ -1034,9 +1036,6 @@ probe_error:
kfree(port->interrupt_out_buffer);
}
- /* return the minor range that this device had */
- return_serial (serial);
-
/* free up any memory that we allocated */
for (i = 0; i < serial->num_port_pointers; ++i)
kfree(serial->port[i]);
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index 2f59ff226e2..ffbe601cde2 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -384,19 +384,21 @@ static int visor_write (struct usb_serial_port *port, const unsigned char *buf,
dbg("%s - write limit hit\n", __FUNCTION__);
return 0;
}
+ priv->outstanding_urbs++;
spin_unlock_irqrestore(&priv->lock, flags);
buffer = kmalloc (count, GFP_ATOMIC);
if (!buffer) {
dev_err(&port->dev, "out of memory\n");
- return -ENOMEM;
+ count = -ENOMEM;
+ goto error_no_buffer;
}
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) {
dev_err(&port->dev, "no more free urbs\n");
- kfree (buffer);
- return -ENOMEM;
+ count = -ENOMEM;
+ goto error_no_urb;
}
memcpy (buffer, buf, count);
@@ -415,19 +417,27 @@ static int visor_write (struct usb_serial_port *port, const unsigned char *buf,
dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed with status = %d\n",
__FUNCTION__, status);
count = status;
- kfree (buffer);
+ goto error;
} else {
spin_lock_irqsave(&priv->lock, flags);
- ++priv->outstanding_urbs;
priv->bytes_out += count;
spin_unlock_irqrestore(&priv->lock, flags);
}
/* we are done with this urb, so let the host driver
* really free it when it is finished with it */
- usb_free_urb (urb);
+ usb_free_urb(urb);
return count;
+error:
+ usb_free_urb(urb);
+error_no_urb:
+ kfree(buffer);
+error_no_buffer:
+ spin_lock_irqsave(&priv->lock, flags);
+ --priv->outstanding_urbs;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return count;
}
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index bf16e9e1d84..27c5f8f9a2d 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -1109,7 +1109,7 @@ static int firm_send_command (struct usb_serial_port *port, __u8 command, __u8 *
command_port = port->serial->port[COMMAND_PORT];
command_info = usb_get_serial_port_data(command_port);
spin_lock_irqsave(&command_info->lock, flags);
- command_info->command_finished = FALSE;
+ command_info->command_finished = false;
transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer;
transfer_buffer[0] = command;
@@ -1124,12 +1124,12 @@ static int firm_send_command (struct usb_serial_port *port, __u8 command, __u8 *
spin_unlock_irqrestore(&command_info->lock, flags);
/* wait for the command to complete */
- wait_event_interruptible_timeout(command_info->wait_command,
- (command_info->command_finished != FALSE), COMMAND_TIMEOUT);
+ wait_event_interruptible_timeout(command_info->wait_command,
+ (bool)command_info->command_finished, COMMAND_TIMEOUT);
spin_lock_irqsave(&command_info->lock, flags);
- if (command_info->command_finished == FALSE) {
+ if (command_info->command_finished == false) {
dbg("%s - command timed out.", __FUNCTION__);
retval = -ETIMEDOUT;
goto exit;
diff --git a/drivers/usb/serial/whiteheat.h b/drivers/usb/serial/whiteheat.h
index d714eff58dc..f1607970566 100644
--- a/drivers/usb/serial/whiteheat.h
+++ b/drivers/usb/serial/whiteheat.h
@@ -20,10 +20,6 @@
#define __LINUX_USB_SERIAL_WHITEHEAT_H
-#define FALSE 0
-#define TRUE 1
-
-
/* WhiteHEAT commands */
#define WHITEHEAT_OPEN 1 /* open the port */
#define WHITEHEAT_CLOSE 2 /* close the port */
diff --git a/drivers/usb/storage/libusual.c b/drivers/usb/storage/libusual.c
index 599ad10a761..06d1107dbd4 100644
--- a/drivers/usb/storage/libusual.c
+++ b/drivers/usb/storage/libusual.c
@@ -117,6 +117,7 @@ EXPORT_SYMBOL_GPL(usb_usual_check_type);
static int usu_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
+ int rc;
unsigned long type;
struct task_struct* task;
unsigned long flags;
@@ -135,7 +136,7 @@ static int usu_probe(struct usb_interface *intf,
task = kthread_run(usu_probe_thread, (void*)type, "libusual_%d", type);
if (IS_ERR(task)) {
- int rc = PTR_ERR(task);
+ rc = PTR_ERR(task);
printk(KERN_WARNING "libusual: "
"Unable to start the thread for %s: %d\n",
bias_names[type], rc);
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 70234f5dbee..e227f64d564 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -153,6 +153,12 @@ static int slave_configure(struct scsi_device *sdev)
if (us->flags & US_FL_FIX_CAPACITY)
sdev->fix_capacity = 1;
+ /* A few disks have two indistinguishable version, one of
+ * which reports the correct capacity and the other does not.
+ * The sd driver has to guess which is the case. */
+ if (us->flags & US_FL_CAPACITY_HEURISTICS)
+ sdev->guess_capacity = 1;
+
/* Some devices report a SCSI revision level above 2 but are
* unable to handle the REPORT LUNS command (for which
* support is mandatory at level 3). Since we already have
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index f49a62fc32d..8b3145ab775 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -146,6 +146,13 @@ UNUSUAL_DEV( 0x0420, 0x0001, 0x0100, 0x0100,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_IGNORE_RESIDUE ),
+/* Reported by Andrew Nayenko <relan@bk.ru> */
+UNUSUAL_DEV( 0x0421, 0x0019, 0x0592, 0x0592,
+ "Nokia",
+ "Nokia 6288",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_MAX_SECTORS_64 ),
+
/* Reported by Mario Rettig <mariorettig@web.de> */
UNUSUAL_DEV( 0x0421, 0x042e, 0x0100, 0x0100,
"Nokia",
@@ -320,6 +327,13 @@ UNUSUAL_DEV( 0x04b0, 0x040d, 0x0100, 0x0100,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_CAPACITY),
+/* Reported by Emil Larsson <emil@swip.net> */
+UNUSUAL_DEV( 0x04b0, 0x0411, 0x0100, 0x0100,
+ "NIKON",
+ "NIKON DSC D80",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY),
+
/* BENQ DC5330
* Reported by Manuel Fombuena <mfombuena@ya.com> and
* Frank Copeland <fjc@thingy.apana.org.au> */
@@ -1101,6 +1115,15 @@ UNUSUAL_DEV( 0x08bd, 0x1100, 0x0000, 0x0000,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_SINGLE_LUN),
+/* Submitted by Dylan Taft <d13f00l@gmail.com>
+ * US_FL_IGNORE_RESIDUE Needed
+ */
+UNUSUAL_DEV( 0x08ca, 0x3103, 0x0100, 0x0100,
+ "AIPTEK",
+ "Aiptek USB Keychain MP3 Player",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE),
+
/* Entry needed for flags. Moreover, all devices with this ID use
* bulk-only transport, but _some_ falsely report Control/Bulk instead.
* One example is "Trumpion Digital Research MYMP3".
@@ -1311,12 +1334,13 @@ UNUSUAL_DEV( 0x0fce, 0xd008, 0x0000, 0x0000,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_NO_WP_DETECT ),
-/* Reported by Jan Mate <mate@fiit.stuba.sk> */
+/* Reported by Jan Mate <mate@fiit.stuba.sk>
+ * and by Soeren Sonnenburg <kernel@nn7.de> */
UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000,
"Sony Ericsson",
"P990i",
US_SC_DEVICE, US_PR_DEVICE, NULL,
- US_FL_FIX_CAPACITY ),
+ US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ),
/* Reported by Emmanuel Vasilakis <evas@forthnet.gr> */
UNUSUAL_DEV( 0x0fce, 0xe031, 0x0000, 0x0000,
@@ -1347,15 +1371,6 @@ UNUSUAL_DEV( 0x1210, 0x0003, 0x0100, 0x0100,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_IGNORE_RESIDUE ),
-/* This prevents the kernel from detecting the virtual cd-drive with the
- * Windows drivers. <johann.wilhelm@student.tugraz.at>
-*/
-UNUSUAL_DEV( 0x12d1, 0x1003, 0x0000, 0xffff,
- "HUAWEI",
- "E220 USB-UMTS Install",
- US_SC_DEVICE, US_PR_DEVICE, NULL,
- US_FL_IGNORE_DEVICE),
-
/* Reported by Vilius Bilinkevicius <vilisas AT xxx DOT lt) */
UNUSUAL_DEV( 0x132b, 0x000b, 0x0001, 0x0001,
"Minolta",
@@ -1394,6 +1409,16 @@ UNUSUAL_DEV( 0x22b8, 0x3010, 0x0001, 0x0001,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ),
+/*
+ * Patch by Pete Zaitcev <zaitcev@redhat.com>
+ * Report by Mark Patton. Red Hat bz#208928.
+ */
+UNUSUAL_DEV( 0x22b8, 0x4810, 0x0001, 0x0001,
+ "Motorola",
+ "RAZR V3i",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY),
+
/* Reported by Radovan Garabik <garabik@kassiopeia.juls.savba.sk> */
UNUSUAL_DEV( 0x2735, 0x100b, 0x0000, 0x9999,
"MPIO",
@@ -1423,7 +1448,7 @@ UNUSUAL_DEV( 0xed06, 0x4500, 0x0001, 0x0001,
"DataStor",
"USB4500 FW1.04",
US_SC_DEVICE, US_PR_DEVICE, NULL,
- US_FL_FIX_CAPACITY),
+ US_FL_CAPACITY_HEURISTICS),
/* Control/Bulk transport for all SubClass values */
USUAL_DEV(US_SC_RBC, US_PR_CB, USB_US_TYPE_STOR),
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 7e7ec29782f..8e898e3d861 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -55,7 +55,7 @@
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
-#include <linux/utsrelease.h>
+#include <linux/utsname.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -547,7 +547,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id)
idesc->bInterfaceSubClass,
idesc->bInterfaceProtocol,
msgs[msg],
- UTS_RELEASE);
+ utsname()->release);
}
return 0;
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
index 296b091cf16..8432bf171d2 100644
--- a/drivers/usb/usb-skeleton.c
+++ b/drivers/usb/usb-skeleton.c
@@ -34,18 +34,25 @@ static struct usb_device_id skel_table [] = {
};
MODULE_DEVICE_TABLE(usb, skel_table);
+/* to prevent a race between open and disconnect */
+static DEFINE_MUTEX(skel_open_lock);
+
/* Get a minor range for your devices from the usb maintainer */
#define USB_SKEL_MINOR_BASE 192
/* our private defines. if this grows any larger, use your own .h file */
#define MAX_TRANSFER (PAGE_SIZE - 512)
+/* MAX_TRANSFER is chosen so that the VM is not stressed by
+ allocations > PAGE_SIZE and the number of packets in a page
+ is an integer 512 is the largest possible packet on EHCI */
#define WRITES_IN_FLIGHT 8
+/* arbitrarily chosen */
/* Structure to hold all of our device specific stuff */
struct usb_skel {
- struct usb_device *dev; /* the usb device for this device */
- struct usb_interface *interface; /* the interface for this device */
+ struct usb_device *udev; /* the usb device for this device */
+ struct usb_interface *interface; /* the interface for this device */
struct semaphore limit_sem; /* limiting the number of writes in progress */
unsigned char *bulk_in_buffer; /* the buffer to receive data */
size_t bulk_in_size; /* the size of the receive buffer */
@@ -76,8 +83,10 @@ static int skel_open(struct inode *inode, struct file *file)
subminor = iminor(inode);
+ mutex_lock(&skel_open_lock);
interface = usb_find_interface(&skel_driver, subminor);
if (!interface) {
+ mutex_unlock(&skel_open_lock);
err ("%s - error, can't find device for minor %d",
__FUNCTION__, subminor);
retval = -ENODEV;
@@ -86,17 +95,22 @@ static int skel_open(struct inode *inode, struct file *file)
dev = usb_get_intfdata(interface);
if (!dev) {
+ mutex_unlock(&skel_open_lock);
retval = -ENODEV;
goto exit;
}
+ /* increment our usage count for the device */
+ kref_get(&dev->kref);
+ /* now we can drop the lock */
+ mutex_unlock(&skel_open_lock);
+
/* prevent the device from being autosuspended */
retval = usb_autopm_get_interface(interface);
- if (retval)
+ if (retval) {
+ kref_put(&dev->kref, skel_delete);
goto exit;
-
- /* increment our usage count for the device */
- kref_get(&dev->kref);
+ }
/* save our object in the file's private structure */
file->private_data = dev;
@@ -199,12 +213,6 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
goto exit;
}
- mutex_lock(&dev->io_mutex);
- if (!dev->interface) { /* disconnect() was called */
- retval = -ENODEV;
- goto error;
- }
-
/* create a urb, and a buffer for it, and copy the data to the urb */
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
@@ -223,6 +231,14 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
goto error;
}
+ /* this lock makes sure we don't submit URBs to gone devices */
+ mutex_lock(&dev->io_mutex);
+ if (!dev->interface) { /* disconnect() was called */
+ mutex_unlock(&dev->io_mutex);
+ retval = -ENODEV;
+ goto error;
+ }
+
/* initialize the urb properly */
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
@@ -231,6 +247,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
/* send the data out the bulk port */
retval = usb_submit_urb(urb, GFP_KERNEL);
+ mutex_unlock(&dev->io_mutex);
if (retval) {
err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
goto error;
@@ -239,7 +256,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
/* release our reference to this urb, the USB core will eventually free it entirely */
usb_free_urb(urb);
- mutex_unlock(&dev->io_mutex);
+
return writesize;
error:
@@ -247,7 +264,6 @@ error:
usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
usb_free_urb(urb);
}
- mutex_unlock(&dev->io_mutex);
up(&dev->limit_sem);
exit:
@@ -342,6 +358,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
error:
if (dev)
+ /* this frees allocated memory */
kref_put(&dev->kref, skel_delete);
return retval;
}
@@ -352,20 +369,21 @@ static void skel_disconnect(struct usb_interface *interface)
int minor = interface->minor;
/* prevent skel_open() from racing skel_disconnect() */
- lock_kernel();
+ mutex_lock(&skel_open_lock);
dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
/* give back our minor */
usb_deregister_dev(interface, &skel_class);
+ mutex_unlock(&skel_open_lock);
/* prevent more I/O from starting */
mutex_lock(&dev->io_mutex);
dev->interface = NULL;
mutex_unlock(&dev->io_mutex);
- unlock_kernel();
+
/* decrement our usage count */
kref_put(&dev->kref, skel_delete);
@@ -378,6 +396,7 @@ static struct usb_driver skel_driver = {
.probe = skel_probe,
.disconnect = skel_disconnect,
.id_table = skel_table,
+ .supports_autosuspend = 1,
};
static int __init usb_skel_init(void)