--- zzzz-none-000/linux-4.1.52/drivers/usb/core/hub.c 2018-05-28 02:26:45.000000000 +0000 +++ bcm63-7530ax-731/linux-4.1.52/drivers/usb/core/hub.c 2022-03-02 11:37:13.000000000 +0000 @@ -33,9 +33,51 @@ #include "hub.h" #include "otg_whitelist.h" +#ifdef CONFIG_AVM_POWERMETER +#include +#include +#endif /*--- #ifdef CONFIG_AVM_POWERMETER ---*/ + #define USB_VENDOR_GENESYS_LOGIC 0x05e3 #define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01 +/* 20160801 AVM/VGJ USB KPI */ +#if defined (CONFIG_AVM_KERNEL) + +#define AVM_RECONNECT_ERROR_MS_THRESHOLD 2500 + +unsigned long disconnect_time[2]; +unsigned int disconnect_device_id[2]; + +int reconnect_count = 0; +module_param(reconnect_count, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(reconnect_count, "Count of reconnects"); + +int enum_error_count = 0; +module_param(enum_error_count, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(enum_error_count, "Count of enumeration errors"); + +// Overcurrent detection not yet supported in Broadcom Hardware +int overcurrent_error_count = -1; +module_param(overcurrent_error_count, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(overcurrent_error_count, "Count of overcurrent errors"); + +#if defined (CONFIG_AVM_USB_SUSPEND) +int suspend_count = 0; +module_param(suspend_count, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(suspend_count, "Count of suspends"); + +int reset_resume_count = 0; +module_param(reset_resume_count, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(reset_resume_count, "Count of resets during resume"); + +int resume_error_count = 0; +module_param(resume_error_count, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(resume_error_count, "Count of failed resumes"); +#endif + +#endif + /* Protect struct usb_device->state and ->children members * Note: Both are also protected by ->dev.sem, except that ->state can * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ @@ -2120,6 +2162,41 @@ dev_info(&udev->dev, "USB disconnect, device number %d\n", udev->devnum); +#if defined(CONFIG_AVM_POWERMETER) + { + unsigned nextmA = 0; + unsigned avm_powerdevice = 0; + + if (udev->level == 1) { + if (udev->parent->maxchild > 1) { + avm_powerdevice = (udev->portnum & 1)? powerdevice_usb_host : powerdevice_usb_host2; + printk (KERN_INFO "Port#%u disconnect: AVM Powermeter changed to %u mA\n", udev->portnum, nextmA); + } else { + avm_powerdevice = (udev->bus->busnum & 1)? powerdevice_usb_host : powerdevice_usb_host2; + printk (KERN_INFO "Bus#%u disconnect: AVM Powermeter changed to %u mA\n", udev->bus->busnum, nextmA); + } + PowerManagmentRessourceInfo(avm_powerdevice, nextmA); + } + } +#endif // CONFIG_AVM_POWERMETER + +/* 20160801 AVM/VGJ store disconnect timestamp to check possible reconnects */ +#if defined (CONFIG_AVM_KERNEL) + // Only check reconnects of devices directly connected to root-hub + if (udev->level == 1) { + int port_index; + unsigned int device_id; + if (udev->parent->maxchild > 1) { + port_index = udev->portnum & 1 ? 0 : 1; + } else { + port_index = udev->bus->busnum & 1 ? 0 : 1; + } + disconnect_time[port_index] = jiffies; + device_id = (udev->descriptor.idVendor << 16) | udev->descriptor.idProduct; + disconnect_device_id[port_index] = device_id; + } +#endif + /* * Ensure that the pm runtime code knows that the USB device * is in the process of being disconnected. @@ -2130,6 +2207,17 @@ hub_disconnect_children(udev); +#ifdef CONFIG_AVM_USB_SUSPEND + /* == 20180410 AVM/VGJ - Blacklist device, if disconnected recently after suspend (fixed) == */ + if (udev->port_is_suspended && ((jiffies - udev->avm_usb_suspend_timestamp) < msecs_to_jiffies(1000))) { + suspend_error_count++; + avm_usb_suspend_add_to_blacklist(udev); + } + + /* == 20180328 AVM/VGJ - Deletes suspend timer and disables suspend == */ + avm_usb_suspend_stop(udev); +#endif + /* deallocate hcd/hardware state ... nuking all pending urbs and * cleaning up all state associated with the current configuration * so that the hardware is now fully quiesced. @@ -2235,7 +2323,7 @@ /* descriptor may appear anywhere in config */ if (__usb_get_extra_descriptor (udev->rawdescriptors[0], le16_to_cpu(udev->config[0].desc.wTotalLength), - USB_DT_OTG, (void **) &desc) == 0) { + USB_DT_OTG, (void **) &desc, sizeof(*desc)) == 0) { if (desc->bmAttributes & USB_OTG_HNP) { unsigned port1 = udev->portnum; @@ -2435,6 +2523,48 @@ /* Tell the world! */ announce_device(udev); +#if defined(CONFIG_AVM_POWERMETER) + { + unsigned nextmA = (udev->speed == USB_SPEED_SUPER)? 150: 100; + unsigned avm_powerdevice = 0; + + if (udev->level == 1) { + if (udev->parent->maxchild > 1) { + avm_powerdevice = (udev->portnum & 1)? powerdevice_usb_host : powerdevice_usb_host2; + printk (KERN_INFO "Port#%u connect: AVM Powermeter changed to %u mA\n", udev->portnum, nextmA); + } else { + avm_powerdevice = (udev->bus->busnum & 1)? powerdevice_usb_host : powerdevice_usb_host2; + printk (KERN_INFO "Bus#%u connect: AVM Powermeter changed to %u mA\n", udev->bus->busnum, nextmA); + } + PowerManagmentRessourceInfo(avm_powerdevice, nextmA); + } + } +#endif // CONFIG_AVM_POWERMETER + +/* 20160801 AVM/VGJ If connect comes too fast after disconnect -> reconnect error */ +#if defined (CONFIG_AVM_KERNEL) + // Only check reconnects of devices directly connected to root-hub + if (udev->level == 1) { + unsigned long jiffies_diff; + unsigned int device_id; + int port_index; + if (udev->parent->maxchild > 1) { + port_index = udev->portnum & 1 ? 0 : 1; + } else { + port_index = udev->bus->busnum & 1 ? 0 : 1; + } + // Check device_id to make sure that the stored time belongs to the same device + device_id = (udev->descriptor.idVendor << 16) | udev->descriptor.idProduct; + if ( device_id == disconnect_device_id[port_index] ) { + jiffies_diff = jiffies - disconnect_time[port_index]; + if ( jiffies_diff < msecs_to_jiffies(AVM_RECONNECT_ERROR_MS_THRESHOLD) ) { + printk(KERN_ERR "Spontaneous reconnect\n"); + reconnect_count++; + } + } + } +#endif + if (udev->serial) add_device_randomness(udev->serial, strlen(udev->serial)); if (udev->product) @@ -2767,8 +2897,16 @@ USB_PORT_FEAT_C_BH_PORT_RESET); usb_clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_PORT_LINK_STATE); - usb_clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_CONNECTION); + +#if defined(CONFIG_BCM_KF_USB_HOSTS) + /* during reboot some times warm reset is seen and clearing this bit + * is causing device detection issues*/ + if(warm) + printk("+++++ BRCM skipping port_feat_c_connection for warm reset\n"); + else +#endif + usb_clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_CONNECTION); /* * If a USB 3.0 device migrates from reset to an error @@ -2858,7 +2996,8 @@ __release(&port_dev->status_lock); } -#ifdef CONFIG_PM +#if defined (CONFIG_PM) || defined (CONFIG_AVM_USB_SUSPEND) +//#ifdef CONFIG_PM /* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */ static int port_is_suspended(struct usb_hub *hub, unsigned portstatus) @@ -2894,10 +3033,23 @@ /* pass */; } /* Is the device still present? */ +#if defined (CONFIG_AVM_KERNEL) + /* AVM/VGJ 20180420 Do not treat port_is_suspended as No Device Error */ + else if (status || +#else else if (status || port_is_suspended(hub, portstatus) || +#endif !port_is_power_on(hub, portstatus)) { if (status >= 0) status = -ENODEV; +#if defined (CONFIG_AVM_KERNEL) + /* AVM/VGJ 20180420 follow up port_is_suspended with reset resume */ + } else if (port_is_suspended(hub, portstatus)) { + dev_dbg(hub->intfdev, + "port %d status %04x.%04x still suspended\n", + port1, portchange, portstatus); + udev->reset_resume = 1; +#endif } else if (!(portstatus & USB_PORT_STAT_CONNECTION)) { if (retries--) { usleep_range(200, 300); @@ -3172,12 +3324,36 @@ if (!PMSG_IS_AUTO(msg)) status = 0; } else { - dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n", + /* == 20180328 AVM/VGJ - Show also without CONFIG_USB_DEBUG == */ + dev_info(&udev->dev, "usb %ssuspend, wakeup %d\n", (PMSG_IS_AUTO(msg) ? "auto-" : ""), udev->do_remote_wakeup); if (really_suspend) { udev->port_is_suspended = 1; +#if defined(CONFIG_AVM_POWERMETER) + { + unsigned nextmA = 50; + unsigned avm_powerdevice = 0; + + if (udev->level == 1) { + if (udev->parent->maxchild > 1) { + avm_powerdevice = (udev->portnum & 1)? powerdevice_usb_host : powerdevice_usb_host2; + printk (KERN_INFO "Port#%u suspend: AVM Powermeter changed to %u mA\n", udev->portnum, nextmA); + } else { + avm_powerdevice = (udev->bus->busnum & 1)? powerdevice_usb_host : powerdevice_usb_host2; + printk (KERN_INFO "Bus#%u suspend: AVM Powermeter changed to %u mA\n", udev->bus->busnum, nextmA); + } + PowerManagmentRessourceInfo(avm_powerdevice, nextmA); + } + } +#endif // CONFIG_AVM_POWERMETER + +#if defined (CONFIG_AVM_USB_SUSPEND) + udev->avm_usb_suspend_timestamp = jiffies; + suspend_count++; +#endif + /* device has up to 10 msec to fully suspend */ msleep(10); } @@ -3211,7 +3387,8 @@ u16 devstatus = 0; /* caller owns the udev device lock */ - dev_dbg(&udev->dev, "%s\n", + /* == 20180328 AVM/VGJ - Show also without CONFIG_USB_DEBUG == */ + dev_info(&udev->dev, "%s\n", udev->reset_resume ? "finish reset-resume" : "finish resume"); /* usb ch9 identifies four variants of SUSPENDED, based on what @@ -3258,6 +3435,12 @@ } } +#if defined (CONFIG_AVM_USB_SUSPEND) + if (udev->reset_resume) { + reset_resume_count++; + } +#endif + if (status) { dev_dbg(&udev->dev, "gone after usb resume? status %d\n", status); @@ -3396,7 +3579,8 @@ dev_dbg(&port_dev->dev, "can't resume, status %d\n", status); } else { /* drive resume for USB_RESUME_TIMEOUT msec */ - dev_dbg(&udev->dev, "usb %sresume\n", + /* == 20180328 AVM/VGJ - Show also without CONFIG_USB_DEBUG == */ + dev_info(&udev->dev, "usb %sresume\n", (PMSG_IS_AUTO(msg) ? "auto-" : "")); msleep(USB_RESUME_TIMEOUT); @@ -3422,6 +3606,37 @@ usb_clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_SUSPEND); } + +#if defined(CONFIG_AVM_POWERMETER) + { + if (udev->level == 1) { + unsigned nextmA; + unsigned avm_powerdevice; + + if (udev->speed == USB_SPEED_SUPER) { + nextmA = (udev->config->desc.bMaxPower * 8); + if (nextmA <= 150) { + nextmA = 150; + } + } else { + nextmA = (udev->config->desc.bMaxPower * 2); + /* A device should display at least 100 mA in AVM_POWERMETER */ + if (nextmA < 100) { + nextmA = 100; + } + } + + if (udev->parent->maxchild > 1) { + avm_powerdevice = (udev->portnum & 1)? powerdevice_usb_host : powerdevice_usb_host2; + printk (KERN_INFO "Port#%u resume: AVM Powermeter changed to %u mA\n", udev->portnum, nextmA); + } else { + avm_powerdevice = ((udev->bus->busnum &1) == 1)? powerdevice_usb_host : powerdevice_usb_host2; + printk (KERN_INFO "Bus#%u resume: AVM Powermeter changed to %u mA\n", udev->bus->busnum, nextmA); + } + PowerManagmentRessourceInfo(avm_powerdevice, nextmA); + } + } +#endif // CONFIG_AVM_POWERMETER } if (udev->persist_enabled && hub_is_superspeed(hub->hdev)) @@ -3433,7 +3648,12 @@ if (status == 0) status = finish_port_resume(udev); if (status < 0) { - dev_dbg(&udev->dev, "can't resume, status %d\n", status); +#if defined (CONFIG_AVM_USB_SUSPEND) + avm_usb_suspend_add_to_blacklist(udev); + resume_error_count++; +#endif + /* == 20180328 AVM/VGJ - Show also without CONFIG_USB_DEBUG == */ + dev_info(&udev->dev, "can't resume, status %d\n", status); hub_port_logical_disconnect(hub, port1); } else { /* Try to enable USB2 hardware LPM */ @@ -3467,6 +3687,9 @@ return status; } +#endif /* CONFIG_PM */ + +#if defined (CONFIG_PM) /* Returns 1 if there was a remote wakeup and a connect status change. */ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, u16 portstatus, u16 portchange) @@ -4071,6 +4294,8 @@ void usb_unlocked_enable_lpm(struct usb_device *udev) { } EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm); +#if !defined (CONFIG_AVM_USB_SUSPEND) + int usb_disable_ltm(struct usb_device *udev) { return 0; @@ -4080,6 +4305,8 @@ void usb_enable_ltm(struct usb_device *udev) { } EXPORT_SYMBOL_GPL(usb_enable_ltm); +#endif + static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, u16 portstatus, u16 portchange) { @@ -4836,9 +5063,14 @@ if (hub->hdev->parent || !hcd->driver->port_handed_over || !(hcd->driver->port_handed_over)(hcd, port1)) { - if (status != -ENOTCONN && status != -ENODEV) + if (status != -ENOTCONN && status != -ENODEV) { dev_err(&port_dev->dev, "unable to enumerate USB device\n"); +/* 20170316 AVM/VGJ increment enumeration error */ +#if defined (CONFIG_AVM_KERNEL) + enum_error_count++; +#endif + } } done: