#include <asm/uaccess.h>
#include <asm/byteorder.h>
-
#include "hub.h"
+
+#ifdef CONFIG_MTK_ICUSB_SUPPORT
+int is_musbfsh_rh(struct usb_device *udev);
+void set_icusb_sts_disconnect_done(void);
+#endif
+
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+static struct usb_device *g_dsda_dev = NULL;
+
+#ifdef CONFIG_PM_RUNTIME
+struct usb_hub *usb11_hub = NULL;
+int is_musbfsh_rh(struct usb_device *udev);
+
+struct usb_device *get_usb11_child_udev(void)
+{
+ if(usb11_hub){
+ MYDBG("\n");
+ return usb11_hub->ports[0]->child;
+ }else{
+ MYDBG("\n");
+ return NULL;
+ }
+}
+#endif
+
+void dump_data(char *buf, int len)
+{
+ int i;
+ for(i =0 ; i< len ; i++)
+ {
+ MYDBG("data[%d]: %x\n", i, buf[i]);
+ }
+}
+
+void test_dsda_device_ep0(void)
+{
+
+ int ret;
+ char data_buf[256];
+ ret = usb_control_msg(g_dsda_dev, usb_rcvctrlpipe(g_dsda_dev, 0),
+ USB_REQ_GET_DESCRIPTOR,
+ USB_DIR_IN,
+ USB_DT_DEVICE << 8,
+ 0,
+ data_buf,
+ 64,
+ USB_CTRL_GET_TIMEOUT);
+
+
+
+ if (ret < 0) {
+ MYDBG("test ep fail, ret : %d\n", ret);
+ }
+ else
+ {
+ MYDBG("test ep0 ok, ret : %d\n", ret);
+ dump_data(data_buf, ret);
+ }
+
+}
+
+void release_usb11_wakelock(void);
+static ssize_t dsda_tmp_proc_entry(struct file *file_ptr, const char __user *user_buffer, size_t count, loff_t *position)
+{
+ char cmd[64];
+
+ int ret = copy_from_user((char *) &cmd, user_buffer, count);
+
+ if(ret != 0)
+ {
+ return -EFAULT;
+ }
+
+ /* apply action here */
+ if(cmd[0] == '0')
+ {
+ MYDBG("");
+ test_dsda_device_ep0();
+ }
+ if(cmd[0] == '1')
+ {
+ MYDBG("");
+ release_usb11_wakelock();
+ }
+
+ MYDBG("");
+
+ return count;
+}
+
+struct file_operations dsda_tmp_proc_fops = {
+ .write = dsda_tmp_proc_entry
+};
+
+
+void create_dsda_tmp_entry(void)
+{
+ struct proc_dir_entry *prEntry;
+
+ MYDBG("");
+
+ prEntry = proc_create("DSDA_TMP_ENTRY", 0660, 0, &dsda_tmp_proc_fops);
+ if (prEntry)
+ {
+ MYDBG("add /proc/DSDA_TMP_ENTRY ok\n");
+ }
+ else
+ {
+ MYDBG("add /proc/DSDA_TMP_ENTRY fail\n");
+ }
+}
+#endif
+
/* if we are in debug mode, always announce new devices */
#ifdef DEBUG
#ifndef CONFIG_USB_ANNOUNCE_NEW_DEVICES
#define USB_VENDOR_GENESYS_LOGIC 0x05e3
#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01
+#if defined(CONFIG_USBIF_COMPLIANCE) && defined(CONFIG_USB_XHCI_HCD)
+extern int usbif_u3h_send_event(char* event) ;
+#include "otg_whitelist.h"
+#endif
+
+
static inline int hub_is_superspeed(struct usb_device *hdev)
{
return (hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS);
#define HUB_DEBOUNCE_STEP 25
#define HUB_DEBOUNCE_STABLE 100
+static void hub_release(struct kref *kref);
static int usb_reset_and_verify_device(struct usb_device *udev);
+#define usb_sndaddr0pipe() (PIPE_CONTROL << 30)
+#define usb_rcvaddr0pipe() ((PIPE_CONTROL << 30) | USB_DIR_IN)
+
+
static inline char *portspeed(struct usb_hub *hub, int portstatus)
{
if (hub_is_superspeed(hub->hdev))
*/
static int set_port_feature(struct usb_device *hdev, int port1, int feature)
{
+ MYDBG("");
return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1,
NULL, 0, 1000);
"%s failed (err = %d)\n", __func__, ret);
} else {
*status = le16_to_cpu(hub->status->hub.wHubStatus);
- *change = le16_to_cpu(hub->status->hub.wHubChange);
+ *change = le16_to_cpu(hub->status->hub.wHubChange);
ret = 0;
}
mutex_unlock(&hub->status_mutex);
unsigned delay;
/* Continue a partial initialization */
- if (type == HUB_INIT2)
- goto init2;
- if (type == HUB_INIT3)
+ if (type == HUB_INIT2 || type == HUB_INIT3) {
+ device_lock(hub->intfdev);
+
+ /* Was the hub disconnected while we were waiting? */
+ if (hub->disconnected) {
+ device_unlock(hub->intfdev);
+ kref_put(&hub->kref, hub_release);
+ return;
+ }
+ if (type == HUB_INIT2)
+ goto init2;
+
goto init3;
+ }
+ kref_get(&hub->kref);
/* The superspeed hub except for root hub has to use Hub Depth
* value as an offset into the route string to locate the bits
PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func3);
schedule_delayed_work(&hub->init_work,
msecs_to_jiffies(delay));
+ device_unlock(hub->intfdev);
return; /* Continues at init3: below */
} else {
msleep(delay);
/* Allow autosuspend if it was suppressed */
if (type <= HUB_INIT3)
usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
+
+ if (type == HUB_INIT2 || type == HUB_INIT3)
+ device_unlock(hub->intfdev);
+
+ kref_put(&hub->kref, hub_release);
}
/* Implement the continuations for the delays above */
hub->mA_per_port = hdev->bus_mA;
hub->limited_power = 1;
}
- } else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
+ } else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) { // bus powered
int remaining = hdev->bus_mA -
hub->descriptor->bHubContrCurrent;
hub->descriptor->bHubContrCurrent);
hub->limited_power = 1;
- if (remaining < hdev->maxchild * unit_load)
+ if (remaining < hdev->maxchild * unit_load){
+#if defined(CONFIG_USBIF_COMPLIANCE) && defined(CONFIG_USB_XHCI_HCD)
+ usbif_u3h_send_event("DEV_OVER_CURRENT");
+#endif
dev_warn(hub_dev,
"insufficient power available "
"to use all downstream ports\n");
+ }
hub->mA_per_port = unit_load; /* 7.2.1 */
} else { /* Self-powered external hub */
struct usb_device *hdev;
struct usb_hub *hub;
+
desc = intf->cur_altsetting;
hdev = interface_to_usbdev(intf);
if (hdev->level == MAX_TOPO_LEVEL) {
dev_err(&intf->dev,
"Unsupported bus topology: hub nested too deep\n");
+#if defined(CONFIG_USBIF_COMPLIANCE) && defined(CONFIG_USB_XHCI_HCD)
+ usbif_u3h_send_event("MAX_HUB_TIER_EXCEED");
+#endif
return -E2BIG;
}
#ifdef CONFIG_USB_OTG_BLACKLIST_HUB
if (hdev->parent) {
dev_warn(&intf->dev, "ignoring external hub\n");
+#if defined(CONFIG_USBIF_COMPLIANCE) && defined(CONFIG_USB_XHCI_HCD)
+ usbif_u3h_send_event("HUB_NOT_SUPPORTED");
+#endif
return -ENODEV;
}
#endif
struct usb_device *udev = *pdev;
struct usb_hub *hub = usb_hub_to_struct_hub(udev);
int i;
+ struct timeval tv_begin, tv_end;
+ struct timeval tv_before, tv_after;
+ do_gettimeofday(&tv_begin);
+
+#ifdef CONFIG_MTK_ICUSB_SUPPORT
+ int is_icusb_rh;
+#endif
+
+#ifdef CONFIG_MTK_ICUSB_SUPPORT
+ is_icusb_rh = is_musbfsh_rh(udev->parent);
+#endif
+
/* mark the device as inactive, so any further urb submissions for
* this device (and any of its children) will fail immediately.
* so that the hardware is now fully quiesced.
*/
dev_dbg (&udev->dev, "unregistering device\n");
+
+ do_gettimeofday(&tv_before);
usb_disable_device(udev, 0);
+ do_gettimeofday(&tv_after);
+ MYDBG("usb_disable_device(), time spent, sec : %d, usec : %d\n", (unsigned int)(tv_after.tv_sec - tv_before.tv_sec), (unsigned int)(tv_after.tv_usec - tv_before.tv_usec));
+
usb_hcd_synchronize_unlinks(udev);
if (udev->parent) {
port_dev->did_runtime_put = false;
}
+ do_gettimeofday(&tv_before);
usb_remove_ep_devs(&udev->ep0);
+ do_gettimeofday(&tv_after);
+ MYDBG("usb_remove_ep_devs(), time spent, sec : %d, usec : %d\n", (unsigned int)(tv_after.tv_sec - tv_before.tv_sec), (unsigned int)(tv_after.tv_usec - tv_before.tv_usec));
+
usb_unlock_device(udev);
/* Unregister the device. The device driver is responsible
* for de-configuring the device and invoking the remove-device
* notifier chain (used by usbfs and possibly others).
*/
+ do_gettimeofday(&tv_before);
device_del(&udev->dev);
+ do_gettimeofday(&tv_after);
+ MYDBG("device_del(), time spent, sec : %d, usec : %d\n", (unsigned int)(tv_after.tv_sec - tv_before.tv_sec), (unsigned int)(tv_after.tv_usec - tv_before.tv_usec));
/* Free the device number and delete the parent's children[]
* (or root_hub) pointer.
hub_free_dev(udev);
put_device(&udev->dev);
+
+#ifdef CONFIG_MTK_ICUSB_SUPPORT
+ if (is_icusb_rh)
+ {
+ set_icusb_sts_disconnect_done();
+ MYDBG("ICUSB Disconnect\n");
+ }
+#endif
+ do_gettimeofday(&tv_end);
+ MYDBG("time spent, sec : %d, usec : %d\n", (unsigned int)(tv_end.tv_sec - tv_begin.tv_sec), (unsigned int)(tv_end.tv_usec - tv_begin.tv_usec));
}
#ifdef CONFIG_USB_ANNOUNCE_NEW_DEVICES
udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
err = usb_enumerate_device_otg(udev);
+
+#if defined(CONFIG_USBIF_COMPLIANCE) && defined(CONFIG_USB_XHCI_HCD)
+ if (udev->parent){ // we don't have to check ourself (roothub)
+ if (!is_targeted(udev)) {
+ usbif_u3h_send_event("DEV_NOT_SUPPORTED");
+ err = -ENOTSUPP;
+ }
+ }
+#endif
+
if (err < 0)
return err;
* sysfs power/wakeup controls wakeup enabled/disabled
*/
device_init_wakeup(&udev->dev, 0);
+ MYDBG("udev :%p\n", udev);
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+#ifdef CONFIG_PM_RUNTIME
+ if(is_musbfsh_rh(udev->parent)){
+ MYDBG("\n");
+ /*find out struct *usb_hub and hook it */
+ usb11_hub = usb_hub_to_struct_hub(udev->parent);
+ }
+#endif
+#endif
}
/* Tell the runtime-PM framework the device is active */
msleep(delay);
/* read and decode port status */
+ MYDBG("");
ret = hub_port_status(hub, port1, &portstatus, &portchange);
+ MYDBG("");
if (ret < 0)
return ret;
/* Reset the port */
for (i = 0; i < PORT_RESET_TRIES; i++) {
+ MYDBG("");
status = set_port_feature(hub->hdev, port1, (warm ?
USB_PORT_FEAT_BH_PORT_RESET :
USB_PORT_FEAT_RESET));
+ MYDBG("");
if (status == -ENODEV) {
+ MYDBG("");
; /* The hub is gone */
} else if (status) {
+ MYDBG("");
dev_err(hub->intfdev,
"cannot %sreset port %d (err = %d)\n",
warm ? "warm " : "", port1, status);
} else {
+ MYDBG("");
status = hub_port_wait_reset(hub, port1, udev, delay,
warm);
- if (status && status != -ENOTCONN && status != -ENODEV)
+ if (status && status != -ENOTCONN)
+ {
+ MYDBG("");
dev_dbg(hub->intfdev,
"port_wait_reset: err = %d\n",
status);
+ }
}
+ MYDBG("");
/* Check for disconnect or reset */
if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
+ MYDBG("");
hub_port_finish_reset(hub, port1, udev, &status);
+ MYDBG("");
if (!hub_is_superspeed(hub->hdev))
goto done;
warm = true;
}
}
+ MYDBG("");
dev_dbg (hub->intfdev,
"port %d not enabled, trying %sreset again...\n",
port1, warm ? "warm " : "");
delay = HUB_LONG_RESET_TIME;
}
+ MYDBG("");
+
dev_err (hub->intfdev,
"Cannot enable port %i. Maybe the USB cable is bad?\n",
done:
if (!hub_is_superspeed(hub->hdev))
+ {
+ MYDBG("");
up_read(&ehci_cf_port_reset_rwsem);
+ }
+
+ MYDBG("");
return status;
}
status);
/* bail if autosuspend is requested */
if (PMSG_IS_AUTO(msg))
+ {
+ MYDBG("");
goto err_wakeup;
+ }
}
}
if (usb_disable_ltm(udev)) {
dev_err(&udev->dev, "Failed to disable LTM before suspend\n.");
status = -ENOMEM;
+ MYDBG("");
if (PMSG_IS_AUTO(msg))
goto err_ltm;
}
if (usb_unlocked_disable_lpm(udev)) {
dev_err(&udev->dev, "Failed to disable LPM before suspend\n.");
status = -ENOMEM;
+ MYDBG("");
if (PMSG_IS_AUTO(msg))
goto err_lpm3;
}
/* see 7.1.7.6 */
if (hub_is_superspeed(hub->hdev))
+ {
+ MYDBG("");
status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3);
+#if 0 /* behavior for kernel 3.10 */
/*
* For system suspend, we do not need to enable the suspend feature
* on individual USB-2 ports. The devices will automatically go
* Therefore we will turn on the suspend feature if udev or any of its
* descendants is enabled for remote wakeup.
*/
- else if (PMSG_IS_AUTO(msg) || wakeup_enabled_descendants(udev) > 0)
+ } else if (PMSG_IS_AUTO(msg) || wakeup_enabled_descendants(udev) > 0) {
+ MYDBG("");
status = set_port_feature(hub->hdev, port1,
USB_PORT_FEAT_SUSPEND);
- else {
+ } else {
really_suspend = false;
status = 0;
}
+#else /*roll back behavior to kernel 3.4 */
+ }else{
+ MYDBG("");
+ status = set_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_SUSPEND);
+ }
+#endif
+
if (status) {
dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
port1, status);
+ MYDBG("");
/* Try to enable USB3 LPM and LTM again */
usb_unlocked_enable_lpm(udev);
*/
if (status == 0) {
devstatus = 0;
+ MYDBG("\n");
status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
+ MYDBG("%d\n", status);
if (status >= 0)
status = (status > 0 ? 0 : -ENODEV);
* Between connect detection and reset signaling there must be a delay
* of 100ms at least for debounce and power-settling. The corresponding
* timer shall restart whenever the downstream port detects a disconnect.
- *
+ *
* Apparently there are some bluetooth and irda-dongles and a number of
* low-speed devices for which this debounce period may last over a second.
* Not covered by the spec - but easy to deal with.
}
EXPORT_SYMBOL_GPL(usb_ep0_reinit);
-#define usb_sndaddr0pipe() (PIPE_CONTROL << 30)
-#define usb_rcvaddr0pipe() ((PIPE_CONTROL << 30) | USB_DIR_IN)
static int hub_set_address(struct usb_device *udev, int devnum)
{
const char *speed;
int devnum = udev->devnum;
+ dump_stack();
/* root hub ports have a slightly longer reset period
* (from USB 2.0 spec, section 7.1.7.5)
*/
/* Reset the device; full speed may morph to high speed */
/* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
+ MYDBG("");
retval = hub_port_reset(hub, port1, udev, delay, false);
+ MYDBG("");
if (retval < 0) /* error or disconnect */
goto fail;
/* success, speed is known */
default:
goto fail;
}
+ MYDBG("");
if (udev->speed == USB_SPEED_WIRELESS)
speed = "variable speed Wireless";
udev->tt = &hub->tt;
udev->ttport = port1;
}
-
+
/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
* Because device hardware and firmware is sometimes buggy in
* this area, and this is how Linux has done it for ages.
* value.
*/
for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
+ MYDBG("");
if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) {
struct usb_device_descriptor *buf;
int r = 0;
}
if (r == 0)
break;
+
+#if defined(CONFIG_USBIF_COMPLIANCE) && defined(CONFIG_USB_XHCI_HCD)
+ if (buf->bMaxPacketSize0 == 0) {
+ usbif_u3h_send_event("DEV_CONN_TMOUT");
+ }
+#endif
+
}
udev->descriptor.bMaxPacketSize0 =
buf->bMaxPacketSize0;
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
usb_ep0_reinit(udev);
}
-
+
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
if (retval < (signed)sizeof(udev->descriptor)) {
if (retval != -ENODEV)
remaining -= delta;
}
if (remaining < 0) {
+#if defined(CONFIG_USBIF_COMPLIANCE) && defined(CONFIG_USB_XHCI_HCD)
+ usbif_u3h_send_event("DEV_OVER_CURRENT");
+#endif
dev_warn(hub->intfdev, "%dmA over power budget!\n",
- remaining);
remaining = 0;
int status, i;
unsigned unit_load;
+ MYDBG("");
dev_dbg (hub_dev,
"port %d, status %04x, change %04x, %s\n",
port1, portstatus, portchange, portspeed(hub, portstatus));
}
/* reset (non-USB 3.0 devices) and get descriptor */
+ MYDBG("");
status = hub_port_init(hub, udev, port1, i);
if (status < 0)
+ {
+ MYDBG("");
goto loop;
+ }
+ MYDBG("");
usb_detect_quirks(udev);
if (udev->quirks & USB_QUIRK_DELAY_INIT)
goto loop_disable;
}
}
-
+
/* check for devices running slower than they could */
if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
&& udev->speed == USB_SPEED_FULL
hub->ports[port1 - 1]->child = NULL;
spin_unlock_irq(&device_state_lock);
}
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+ g_dsda_dev = udev;
+ MYDBG("get new device !!!, BUILD TIME : %s, g_dsda_dev : %p\n", __TIME__, g_dsda_dev);
+#endif
}
if (status)
dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
port1);
}
-
+
done:
hub_port_disable(hub, port1, 1);
if (hcd->driver->relinquish_port && !hub->hdev->parent)
dev_dbg (hub_dev, "resetting for error %d\n",
hub->error);
+ MYDBG("");
ret = usb_reset_device(hdev);
if (ret) {
dev_dbg (hub_dev,
* EM interference sometimes causes badly
* shielded USB devices to be shutdown by
* the hub, this hack enables them again.
- * Works at least with mouse driver.
+ * Works at least with mouse driver.
*/
if (!(portstatus & USB_PORT_STAT_ENABLE)
&& !connect_change
.supports_autosuspend = 1,
};
+#if defined(CONFIG_MTK_XHCI) && defined(CONFIG_USB_MTK_DUALMODE)
+extern void mtk_hub_event_steal(spinlock_t *lock, struct list_head* list);
+#endif
int usb_hub_init(void)
{
if (usb_register(&hub_driver) < 0) {
return -1;
}
+#if defined(CONFIG_MTK_XHCI) && defined(CONFIG_USB_MTK_DUALMODE)
+ mtk_hub_event_steal(&hub_event_lock, &hub_event_list);
+#endif
+
khubd_task = kthread_run(hub_thread, NULL, "khubd");
if (!IS_ERR(khubd_task))
return 0;
int i, ret = 0;
int port1 = udev->portnum;
+ MYDBG("");
if (udev->state == USB_STATE_NOTATTACHED ||
udev->state == USB_STATE_SUSPENDED) {
dev_dbg(&udev->dev, "device reset not allowed in state %d\n",
if (ret < 0)
goto re_enumerate;
-
+
/* Device might have changed firmware (DFU or similar) */
if (descriptors_changed(udev, &descriptor)) {
dev_info(&udev->dev, "device firmware changed\n");
usb_unlocked_enable_lpm(udev);
usb_enable_ltm(udev);
return 0;
-
+
re_enumerate:
/* LPM state doesn't matter when we're about to destroy the device. */
hub_port_logical_disconnect(parent_hub, port1);
unsigned int noio_flag;
struct usb_host_config *config = udev->actconfig;
+ MYDBG("");
if (udev->state == USB_STATE_NOTATTACHED ||
udev->state == USB_STATE_SUSPENDED) {
dev_dbg(&udev->dev, "device reset not allowed in state %d\n",