Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index a428aa0..5442297 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -24,12 +24,38 @@
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/freezer.h>
+#include <linux/usb/otg.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
#include "usb.h"
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+#include <linux/usb/hcd.h>
+#include <linux/usb/ch11.h>
+
+int portno;
+int No_Data_Phase;
+EXPORT_SYMBOL(No_Data_Phase);
+int No_Status_Phase;
+EXPORT_SYMBOL(No_Status_Phase);
+unsigned char hub_tier;
+
+#define PDC_HOST_NOTIFY 0x8001 /*completion from core */
+#define UNSUPPORTED_DEVICE 0x8099
+#define UNWANTED_SUSPEND 0x8098
+#define PDC_POWERMANAGEMENT 0x8097
+
+int Unwanted_SecondReset;
+EXPORT_SYMBOL(Unwanted_SecondReset);
+int HostComplianceTest;
+EXPORT_SYMBOL(HostComplianceTest);
+int HostTest;
+EXPORT_SYMBOL(HostTest);
+#endif
+
+
/* if we are in debug mode, always announce new devices */
#ifdef DEBUG
#ifndef CONFIG_USB_ANNOUNCE_NEW_DEVICES
@@ -356,8 +382,11 @@
{
int i, status = -ETIMEDOUT;
+ /* ISP1763A HUB sometimes returns 2 bytes instead of 4 bytes, retry
+ * if this happens
+ */
for (i = 0; i < USB_STS_RETRIES &&
- (status == -ETIMEDOUT || status == -EPIPE); i++) {
+ (status == -ETIMEDOUT || status == -EPIPE || status == 2); i++) {
status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1,
data, sizeof(*data), USB_STS_TIMEOUT);
@@ -724,6 +753,10 @@
*/
if (type == HUB_INIT) {
delay = hub_power_on(hub, false);
+#ifdef CONFIG_USB_OTG
+ if (hdev->bus->is_b_host)
+ goto init2;
+#endif
PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2);
schedule_delayed_work(&hub->init_work,
msecs_to_jiffies(delay));
@@ -858,6 +891,11 @@
* will see them later and handle them normally.
*/
if (need_debounce_delay) {
+#ifdef CONFIG_USB_OTG
+ if (hdev->bus->is_b_host && type == HUB_INIT)
+ goto init3;
+#endif
+
delay = HUB_DEBOUNCE_STABLE;
/* Don't do a long sleep inside a workqueue routine */
@@ -1301,6 +1339,7 @@
#ifdef CONFIG_USB_OTG_BLACKLIST_HUB
if (hdev->parent) {
dev_warn(&intf->dev, "ignoring external hub\n");
+ otg_send_event(OTG_EVENT_HUB_NOT_SUPPORTED);
return -ENODEV;
}
#endif
@@ -1649,6 +1688,13 @@
dev_info(&udev->dev, "USB disconnect, device number %d\n",
udev->devnum);
+#ifdef CONFIG_USB_OTG
+ if (udev->bus->hnp_support && udev->portnum == udev->bus->otg_port) {
+ cancel_delayed_work(&udev->bus->hnp_polling);
+ udev->bus->hnp_support = 0;
+ }
+#endif
+
usb_lock_device(udev);
/* Free up all the children before we remove this device */
@@ -1755,15 +1801,30 @@
(port1 == bus->otg_port)
? "" : "non-");
+ /* a_alt_hnp_support is obsoleted */
+ if (port1 != bus->otg_port)
+ goto out;
+
+ bus->hnp_support = 1;
+
+ /* a_hnp_support is not required for devices
+ * compliant to revision 2.0 or subsequent
+ * versions.
+ */
+ if (le16_to_cpu(desc->bcdOTG) >= 0x0200)
+ goto out;
+
+ /* Legacy B-device i.e compliant to spec
+ * revision 1.3 expect A-device to set
+ * a_hnp_support or b_hnp_enable before
+ * selecting configuration.
+ */
+
/* enable HNP before suspend, it's simpler */
- if (port1 == bus->otg_port)
- bus->b_hnp_enable = 1;
err = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
USB_REQ_SET_FEATURE, 0,
- bus->b_hnp_enable
- ? USB_DEVICE_B_HNP_ENABLE
- : USB_DEVICE_A_ALT_HNP_SUPPORT,
+ USB_DEVICE_A_HNP_SUPPORT,
0, NULL, 0, USB_CTRL_SET_TIMEOUT);
if (err < 0) {
/* OTG MESSAGE: report errors here,
@@ -1772,26 +1833,35 @@
dev_info(&udev->dev,
"can't set HNP mode: %d\n",
err);
- bus->b_hnp_enable = 0;
+ bus->hnp_support = 0;
}
}
}
}
-
+out:
if (!is_targeted(udev)) {
+ otg_send_event(OTG_EVENT_DEV_NOT_SUPPORTED);
+
/* Maybe it can talk to us, though we can't talk to it.
* (Includes HNP test device.)
*/
- if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
+ if (udev->bus->hnp_support) {
err = usb_port_suspend(udev, PMSG_SUSPEND);
if (err < 0)
dev_dbg(&udev->dev, "HNP fail, %d\n", err);
}
err = -ENOTSUPP;
- goto fail;
+ } else if (udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port) {
+ /* HNP polling is introduced in OTG supplement Rev 2.0
+ * and older devices may not support. Work is not
+ * re-armed if device returns STALL. B-Host also perform
+ * HNP polling.
+ */
+ schedule_delayed_work(&udev->bus->hnp_polling,
+ msecs_to_jiffies(THOST_REQ_POLL));
}
-fail:
#endif
return err;
}
@@ -2346,6 +2416,22 @@
return status;
}
}
+#ifdef CONFIG_USB_OTG
+ if (!udev->bus->is_b_host && udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port) {
+ status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_SET_FEATURE, 0,
+ USB_DEVICE_B_HNP_ENABLE,
+ 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (status < 0) {
+ otg_send_event(OTG_EVENT_NO_RESP_FOR_HNP_ENABLE);
+ dev_dbg(&udev->dev, "can't enable HNP on port %d, "
+ "status %d\n", port1, status);
+ } else {
+ udev->bus->b_hnp_enable = 1;
+ }
+ }
+#endif
/* see 7.1.7.6 */
if (hub_is_superspeed(hub->hdev))
@@ -2949,14 +3035,22 @@
buf->bMaxPacketSize0;
kfree(buf);
- retval = hub_port_reset(hub, port1, udev, delay);
- if (retval < 0) /* error or disconnect */
- goto fail;
- if (oldspeed != udev->speed) {
- dev_dbg(&udev->dev,
- "device reset changed speed!\n");
- retval = -ENODEV;
- goto fail;
+ /*
+ * If it is a HSET Test device, we don't issue a
+ * second reset which results in failure due to
+ * speed change.
+ */
+ if (le16_to_cpu(buf->idVendor) != 0x1a0a) {
+ retval = hub_port_reset(hub, port1, udev,
+ delay);
+ if (retval < 0) /* error or disconnect */
+ goto fail;
+ if (oldspeed != udev->speed) {
+ dev_dbg(&udev->dev,
+ "device reset changed speed!\n");
+ retval = -ENODEV;
+ goto fail;
+ }
}
if (r) {
dev_err(&udev->dev,
@@ -3199,6 +3293,9 @@
(portchange & USB_PORT_STAT_C_CONNECTION))
clear_bit(port1, hub->removed_bits);
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+ if (Unwanted_SecondReset == 0) /*stericsson*/
+#endif
if (portchange & (USB_PORT_STAT_C_CONNECTION |
USB_PORT_STAT_C_ENABLE)) {
status = hub_port_debounce(hub, port1);
@@ -3337,7 +3434,32 @@
status = hub_power_remaining(hub);
if (status)
dev_dbg(hub_dev, "%dmA power budget left\n", status);
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+ if (HostComplianceTest == 1 && udev->devnum > 1) {
+ if (HostTest == 7) { /*SINGLE_STEP_GET_DEV_DESC */
+ dev_info(hub_dev, "Testing "
+ "SINGLE_STEP_GET_DEV_DESC\n");
+ /* Test the Single Step Get Device Descriptor ,
+ * take care it should not get status phase
+ */
+ No_Data_Phase = 1;
+ No_Status_Phase = 1;
+ usb_get_device_descriptor(udev, 8);
+ No_Data_Phase = 0;
+ No_Status_Phase = 0;
+ }
+
+ if (HostTest == 8) {
+ dev_info(hub_dev, "Testing "
+ "SINGLE_STEP_SET_FEATURE\n");
+ /* Test Single Step Set Feature */
+ No_Status_Phase = 1;
+ usb_get_device_descriptor(udev, 8);
+ No_Status_Phase = 0;
+ }
+ }
+#endif
return;
loop_disable:
@@ -3375,7 +3497,11 @@
u16 portchange;
int i, ret;
int connect_change;
-
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+ int j;
+ int otgport = 0;
+ struct usb_port_status port_status;
+#endif
/*
* We restart the list every time to avoid a deadlock with
* deleting hubs downstream from this one. This should be
@@ -3450,6 +3576,171 @@
/* deal with port status changes */
for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+ struct usb_port_status portsts;
+
+ /*if we have something to do on
+ * otg port
+ * */
+ if ((hdev->otgstate & USB_OTG_SUSPEND) ||
+ (hdev->otgstate & USB_OTG_ENUMERATE) ||
+ (hdev->otgstate & USB_OTG_DISCONNECT) ||
+ (hdev->otgstate & USB_OTG_RESUME)) {
+ otgport = 1;
+ }
+
+
+ if (hdev->otgstate & USB_OTG_RESUME) {
+ ret = clear_port_feature(hdev, i,
+ USB_PORT_FEAT_SUSPEND);
+ if (ret < 0) {
+ dev_err(hub_dev, "usb otg port Resume"
+ " fails, %d\n", ret);
+ }
+ hdev->otgstate &= ~USB_OTG_RESUME;
+ }
+ if ((hdev->otgstate & USB_OTG_SUSPEND)
+ && (hdev->children[0])) {
+ hdev->otgstate &= ~USB_OTG_SUSPEND;
+
+ ret = set_port_feature(hdev, 1,
+ USB_PORT_FEAT_SUSPEND);
+ if (ret < 0) {
+ dev_err(hub_dev, "usb otg port suspend"
+ " fails, %d\n", ret);
+ break;
+ }
+ msleep(1);
+ ret = get_port_status(hdev, i, &portsts);
+ if (ret < 0) {
+ dev_err(hub_dev, "usb otg get port"
+ " status fails, %d\n", ret);
+ break;
+ }
+ portchange = le16_to_cpu(portsts.wPortChange);
+ if (portchange & USB_PORT_STAT_C_SUSPEND) {
+ clear_port_feature(hdev, i,
+ USB_PORT_FEAT_C_SUSPEND);
+ }
+ break;
+ }
+
+ if (hdev->otgstate & USB_OTG_REMOTEWAKEUP) {
+
+ for (j = 1; j <= hub->descriptor->bNbrPorts;
+ j++) {
+ if (hdev->children[j - 1]) {
+ dev_dbg(hub_dev, "child"
+ " found at port %d\n", j);
+ ret = usb_control_msg(hdev->
+ children[j - 1],
+ usb_sndctrlpipe(hdev->
+ children[j - 1],
+ 0),
+ USB_REQ_SET_FEATURE,
+ USB_RECIP_DEVICE,
+ USB_DEVICE_REMOTE_WAKEUP,
+ 0, NULL,
+ 0,
+ USB_CTRL_SET_TIMEOUT);
+ if (ret < 0) {
+ dev_err(hub_dev, "Port"
+ " %d doesn't support"
+ "remote wakeup\n", j);
+ } else {
+ dev_dbg(hub_dev, "Port"
+ " %d supports"
+ "remote wakeup\n", j);
+ }
+ ret = set_port_feature(hdev, j,
+ USB_PORT_FEAT_SUSPEND);
+ if (ret < 0) {
+ dev_err(hub_dev, "Port"
+ " %d NOT ABLE TO"
+ " SUSPEND\n", j);
+ } else {
+ dev_dbg(hub_dev, "Port"
+ " %d is ABLE TO"
+ " SUSPEND\n", j);
+ }
+ }
+ }
+ ret = usb_control_msg(hdev,
+ usb_sndctrlpipe(hdev, 0),
+ USB_REQ_SET_FEATURE,
+ USB_RECIP_DEVICE,
+ USB_DEVICE_REMOTE_WAKEUP,
+ 0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+ if (ret < 0) {
+ dev_err(hub_dev, "HUB doesn't support"
+ " REMOTE WAKEUP\n");
+ } else {
+ dev_dbg(hub_dev, "HUB supports"
+ " REMOTE WAKEUP\n");
+ }
+ ret = 0;
+ msleep(10);
+ if (hdev->parent == hdev->bus->root_hub) {
+ if (hdev->hcd_suspend &&
+ hdev->hcd_priv) {
+ dev_dbg(hub_dev, "calling"
+ " suspend after remote wakeup"
+ " command is issued\n");
+ hdev->hcd_suspend(hdev->
+ hcd_priv);
+ }
+ if (hdev->otg_notif)
+ hdev->otg_notif(hdev->otgpriv,
+ PDC_POWERMANAGEMENT, 10);
+ }
+ }
+
+ if (hdev->otgstate & USB_OTG_WAKEUP_ALL) {
+ (void) usb_control_msg(hdev,
+ usb_sndctrlpipe(hdev, 0),
+ USB_REQ_CLEAR_FEATURE,
+ USB_RECIP_DEVICE,
+ USB_DEVICE_REMOTE_WAKEUP,
+ 0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+ dev_dbg(hub_dev, "Hub CLEARED REMOTE WAKEUP\n");
+ for (j = 1; j <= hub->descriptor->bNbrPorts;
+ j++) {
+ if (hdev->children[j - 1]) {
+ dev_dbg(hub_dev, "PORT %d"
+ " SUSPEND IS CLEARD\n", j);
+ clear_port_feature(hdev, j,
+ USB_PORT_FEAT_C_SUSPEND);
+ msleep(50);
+ (void) usb_control_msg(hdev->
+ children[j - 1],
+ usb_sndctrlpipe(
+ hdev->children[j - 1],
+ 0),
+ USB_REQ_CLEAR_FEATURE,
+ USB_RECIP_DEVICE,
+ USB_DEVICE_REMOTE_WAKEUP,
+ 0, NULL,
+ 0,
+ USB_CTRL_SET_TIMEOUT);
+ dev_dbg(hub_dev, "PORT %d "
+ "REMOTE WAKEUP IS "
+ "CLEARD\n", j);
+ msleep(10);
+ }
+ }
+
+
+ }
+
+
+ /*
+ * reset the state of otg device,
+ * regardless of otg device
+ */
+ hdev->otgstate = 0;
+#endif
if (test_bit(i, hub->busy_bits))
continue;
connect_change = test_bit(i, hub->change_bits);
@@ -3573,9 +3864,19 @@
hub_port_warm_reset(hub, i);
}
- if (connect_change)
+ if (connect_change) {
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+ if (hdev->parent == hdev->bus->root_hub)
+ if (hdev->otg_notif
+ && (HostComplianceTest == 0))
+ hdev->otg_notif(hdev->otgpriv,
+ PDC_HOST_NOTIFY,
+ 5);
+ portno = i;
+#endif
hub_port_connect_change(hub, i,
portstatus, portchange);
+ }
} /* end for i */
/* deal with hub status changes */
@@ -3607,7 +3908,105 @@
"condition\n");
}
}
+#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
+ /* if we have something on otg */
+ if (otgport) {
+ otgport = 0;
+ /* notify otg controller about it */
+ if (hdev->parent == hdev->bus->root_hub)
+ if (hdev->otg_notif)
+ hdev->otg_notif(hdev->otgpriv,
+ PDC_HOST_NOTIFY, 0);
+ }
+ if (HostComplianceTest && hdev->devnum > 1) {
+ /* TEST_SE0_NAK */
+ if (HostTest == 1) {
+ dev_info(hub_dev, "Testing for TEST_SE0_NAK\n");
+ ret = clear_port_feature(hdev, portno,
+ USB_PORT_FEAT_C_CONNECTION);
+ ret = set_port_feature(hdev, portno,
+ USB_PORT_FEAT_SUSPEND);
+ ret = set_port_feature(hdev, portno | 0x300,
+ USB_PORT_FEAT_TEST);
+ ret = get_port_status(hdev, portno,
+ &port_status);
+ }
+ /*TEST_J*/
+ if (HostTest == 2) {
+ dev_info(hub_dev, "Testing TEST_J\n");
+ ret = clear_port_feature(hdev, portno,
+ USB_PORT_FEAT_C_CONNECTION);
+ ret = set_port_feature(hdev, portno,
+ USB_PORT_FEAT_SUSPEND);
+ ret = set_port_feature(hdev, portno | 0x100,
+ USB_PORT_FEAT_TEST);
+ ret = get_port_status(hdev, portno,
+ &port_status);
+ }
+ if (HostTest == 3) {
+ dev_info(hub_dev, "Testing TEST_K\n");
+ ret = clear_port_feature(hdev, portno,
+ USB_PORT_FEAT_C_CONNECTION);
+ ret = set_port_feature(hdev, portno,
+ USB_PORT_FEAT_SUSPEND);
+ ret = set_port_feature(hdev, portno | 0x200,
+ USB_PORT_FEAT_TEST);
+ ret = get_port_status(hdev, portno,
+ &port_status);
+ }
+ if (HostTest == 4) {
+ dev_info(hub_dev, "Testing TEST_PACKET at Port"
+ " %d\n", portno);
+ ret = clear_port_feature(hdev, portno,
+ USB_PORT_FEAT_C_CONNECTION);
+ if (ret < 0)
+ dev_err(hub_dev, "Clear port feature"
+ " C_CONNECTION failed\n");
+
+ ret = set_port_feature(hdev, portno,
+ USB_PORT_FEAT_SUSPEND);
+ if (ret < 0)
+ dev_err(hub_dev, "Clear port feature"
+ " SUSPEND failed\n");
+
+ ret = set_port_feature(hdev, portno | 0x400,
+ USB_PORT_FEAT_TEST);
+ if (ret < 0)
+ dev_err(hub_dev, "Clear port feature"
+ " TEST failed\n");
+
+ ret = get_port_status(hdev, portno,
+ &port_status);
+ if (ret < 0)
+ dev_err(hub_dev, "Get port status"
+ " failed\n");
+ }
+ if (HostTest == 5) {
+ dev_info(hub_dev, "Testing TEST_FORCE_ENBLE\n");
+ ret = clear_port_feature(hdev, portno,
+ USB_PORT_FEAT_C_CONNECTION);
+ ret = set_port_feature(hdev, portno,
+ USB_PORT_FEAT_SUSPEND);
+ ret = set_port_feature(hdev, portno | 0x500,
+ USB_PORT_FEAT_TEST);
+ ret = get_port_status(hdev, portno,
+ &port_status);
+ }
+ if (HostTest == 6) {
+ dev_info(hub_dev, "Testing "
+ "HS_HOST_PORT_SUSPEND_RESUME\n");
+ ret = clear_port_feature(hdev, portno,
+ USB_PORT_FEAT_C_CONNECTION);
+ ret = set_port_feature(hdev, portno,
+ USB_PORT_FEAT_SUSPEND);
+ msleep(3000);
+ ret = clear_port_feature(hdev, portno,
+ USB_PORT_FEAT_SUSPEND);
+ HostTest = 0;
+ }
+ }
+#endif
loop_autopm:
/* Balance the usb_autopm_get_interface() above */
usb_autopm_put_interface_no_suspend(intf);