--- zzzz-none-000/linux-3.10.107/drivers/usb/core/hub.c 2017-06-27 09:49:32.000000000 +0000 +++ vr9-7490-729/linux-3.10.107/drivers/usb/core/hub.c 2021-11-10 11:53:55.000000000 +0000 @@ -33,6 +33,12 @@ #include "hub.h" +#ifdef CONFIG_AVM_POWERMETER +#include +#include +#endif /*--- #ifdef CONFIG_AVM_POWERMETER ---*/ + + /* if we are in debug mode, always announce new devices */ #ifdef DEBUG #ifndef CONFIG_USB_ANNOUNCE_NEW_DEVICES @@ -43,6 +49,43 @@ #define USB_VENDOR_GENESYS_LOGIC 0x05e3 #define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01 +/* 20160707 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 supported in every VR9 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 + static inline int hub_is_superspeed(struct usb_device *hdev) { return (hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS); @@ -1516,6 +1559,14 @@ } le16_to_cpus(&hubstatus); hcd = bus_to_hcd(hdev->bus); + +/* 20160809 AVM/VGJ USB KPI - Init overcurrent_error_count only for XHCI hardware */ +#if defined (CONFIG_AVM_KERNEL) + if ( (hcd->driver->flags & HCD_USB3) && (overcurrent_error_count == -1) ) { + overcurrent_error_count = 0; + } +#endif + if (hdev == hdev->bus->root_hub) { if (hcd->power_budget > 0) hdev->bus_mA = hcd->power_budget; @@ -2101,9 +2152,80 @@ usb_set_device_state(udev, USB_STATE_NOTATTACHED); dev_info(&udev->dev, "USB disconnect, device number %d\n", udev->devnum); + +#if defined(CONFIG_AVM_POWERMETER) + { + unsigned nextmA = 0; + unsigned avm_powerdevice = 0; + int hub_port; + enum _avm_hw_param param; + +#if defined (CONFIG_MACH_AR934x) || defined (CONFIG_MACH_QCA955x) + //check if internal hub is not present + if (avm_get_hw_config(AVM_HW_CONFIG_VERSION, "usb_hub_external_port1", &hub_port, ¶m) != 0) { +#else + if (1) { +#endif + 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); + } + } else { + if (udev->level == 2) { + if (udev->portnum == hub_port) { + avm_powerdevice = powerdevice_usb_host; + } else { + if (avm_get_hw_config(AVM_HW_CONFIG_VERSION, "usb_hub_external_port2", &hub_port, ¶m) == 0) { + if (udev->portnum == hub_port) { + avm_powerdevice = powerdevice_usb_host2; + } + } + } + if (avm_powerdevice) { + printk (KERN_INFO "HubPort#%u disconnect: AVM Powermeter changed to %u mA\n", udev->portnum, nextmA); + PowerManagmentRessourceInfo(avm_powerdevice, nextmA); + } + } + } + } +#endif // CONFIG_AVM_POWERMETER + +/* 20160707 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 usb_lock_device(udev); +#ifdef CONFIG_AVM_USB_SUSPEND + /* == 20180220 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); + } + + /* == 20170209 AVM/VGJ - Deletes suspend timer and disables suspend == */ + avm_usb_suspend_stop(udev); +#endif + /* Free up all the children before we remove this device */ for (i = 0; i < udev->maxchild; i++) { if (hub->ports[i]->child) @@ -2391,6 +2513,73 @@ /* Tell the world! */ announce_device(udev); +#if defined(CONFIG_AVM_POWERMETER) + { + unsigned nextmA = (udev->speed == USB_SPEED_SUPER)? 150: 100; + unsigned avm_powerdevice = 0; + int hub_port; + enum _avm_hw_param param; + +#if defined (CONFIG_MACH_AR934x) || defined (CONFIG_MACH_QCA955x) + //check if internal hub is not present + if (avm_get_hw_config(AVM_HW_CONFIG_VERSION, "usb_hub_external_port1", &hub_port, ¶m) != 0) { +#else + if (1) { +#endif + 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); + } + } else { + if (udev->level == 2) { + if (udev->portnum == hub_port) { + avm_powerdevice = powerdevice_usb_host; + } else { + if (avm_get_hw_config(AVM_HW_CONFIG_VERSION, "usb_hub_external_port2", &hub_port, ¶m) == 0) { + if (udev->portnum == hub_port) { + avm_powerdevice = powerdevice_usb_host2; + } + } + } + if (avm_powerdevice) { + printk (KERN_INFO "HubPort#%u connect: AVM Powermeter changed to %u mA\n", udev->portnum, nextmA); + PowerManagmentRessourceInfo(avm_powerdevice, nextmA); + } + } + } + } +#endif // CONFIG_AVM_POWERMETER + +/* 20160707 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) @@ -2787,7 +2976,8 @@ return ret; } -#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) @@ -2814,11 +3004,24 @@ int status, unsigned portchange, unsigned portstatus) { /* Is the device still present? */ +#if defined (CONFIG_AVM_KERNEL) + /* AVM/VGJ 20180420 Do not treat port_is_suspended as No Device Error */ + if (status || +#else if (status || port_is_suspended(hub, portstatus) || +#endif !port_is_power_on(hub, portstatus) || !(portstatus & USB_PORT_STAT_CONNECTION)) { 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 } /* Can't do a normal resume if the port isn't enabled, @@ -2849,6 +3052,7 @@ return status; } +#if !defined (CONFIG_AVM_USB_SUSPEND) int usb_disable_ltm(struct usb_device *udev) { struct usb_hcd *hcd = bus_to_hcd(udev->bus); @@ -2892,8 +3096,10 @@ USB_CTRL_SET_TIMEOUT); } EXPORT_SYMBOL_GPL(usb_enable_ltm); +#endif /* !defined (CONFIG_AVM_USB_SUSPEND) */ -#ifdef CONFIG_PM +/* AVM/VGJ 20170203 Redundant Config Flag */ +//#ifdef CONFIG_PM /* * usb_disable_function_remotewakeup - disable usb3.0 * device's function remote wakeup @@ -2912,6 +3118,15 @@ USB_CTRL_SET_TIMEOUT); } +#ifdef CONFIG_AVM_USB_SUSPEND + +static inline unsigned wakeup_enabled_descendants(struct usb_device *udev) +{ + return 0; +} + +#else + /* Count of wakeup-enabled devices at or below udev */ static unsigned wakeup_enabled_descendants(struct usb_device *udev) { @@ -2921,6 +3136,8 @@ (hub ? hub->wakeup_enabled_descendants : 0); } +#endif + /* * usb_port_suspend - suspend a usb device's upstream port * @udev: device that's no longer in active use, not a root hub @@ -3086,12 +3303,60 @@ if (!PMSG_IS_AUTO(msg)) status = 0; } else { - dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n", + /* == 20170209 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; + int hub_port; + enum _avm_hw_param param; + +#if defined (CONFIG_MACH_AR934x) || defined (CONFIG_MACH_QCA955x) + //check if internal hub is not present + if (avm_get_hw_config(AVM_HW_CONFIG_VERSION, "usb_hub_external_port1", &hub_port, ¶m) != 0) { +#else + if (1) { +#endif + 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); + } + } else { + if (udev->level == 2) { + if (udev->portnum == hub_port) { + avm_powerdevice = powerdevice_usb_host; + } else { + if (avm_get_hw_config(AVM_HW_CONFIG_VERSION, "usb_hub_external_port2", &hub_port, ¶m) == 0) { + if (udev->portnum == hub_port) { + avm_powerdevice = powerdevice_usb_host2; + } + } + } + if (avm_powerdevice) { + printk (KERN_INFO "HubPort#%u suspend: AVM Powermeter changed to %u mA\n", udev->portnum, 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); } @@ -3124,7 +3389,8 @@ u16 devstatus = 0; /* caller owns the udev device lock */ - dev_dbg(&udev->dev, "%s\n", + /* == 20170209 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 @@ -3163,6 +3429,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); @@ -3311,7 +3583,8 @@ port1, status); } else { /* drive resume for USB_RESUME_TIMEOUT msec */ - dev_dbg(&udev->dev, "usb %sresume\n", + /* == 20170209 AVM/VGJ - Show also without CONFIG_USB_DEBUG == */ + dev_info(&udev->dev, "usb %sresume\n", (PMSG_IS_AUTO(msg) ? "auto-" : "")); msleep(USB_RESUME_TIMEOUT); @@ -3337,9 +3610,74 @@ usb_clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_SUSPEND); } + +#if defined(CONFIG_AVM_POWERMETER) + { + int hub_port; + enum _avm_hw_param param; +#if defined (CONFIG_MACH_AR934x) + //check if internal hub is not present + if (avm_get_hw_config(AVM_HW_CONFIG_VERSION, "usb_hub_external_port1", &hub_port, ¶m) != 0) { +#else + if (1) { +#endif + 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); + } + } else { + if (udev->level == 2) { + unsigned avm_powerdevice = 0; + if (udev->portnum == hub_port) { + avm_powerdevice = powerdevice_usb_host; + } else { + if (avm_get_hw_config(AVM_HW_CONFIG_VERSION, "usb_hub_external_port2", &hub_port, ¶m) == 0) { + if (udev->portnum == hub_port) { + avm_powerdevice = powerdevice_usb_host2; + } + } + } + if (avm_powerdevice) { + unsigned nextmA = (udev->config->desc.bMaxPower * 2); + + /* A device should display at least 100 mA in AVM_POWERMETER */ + if (nextmA < 100) { + nextmA = 100; + } + printk (KERN_INFO "HubPort#%u resume: AVM Powermeter changed to %u mA\n", udev->portnum, nextmA); + PowerManagmentRessourceInfo(avm_powerdevice, nextmA); + } + } + } + } +#endif // CONFIG_AVM_POWERMETER } - clear_bit(port1, hub->busy_bits); + /* 20170328 AVM/WK Resume WA: keep port busy flag until resume is really done */ + // clear_bit(port1, hub->busy_bits); if (udev->persist_enabled && hub_is_superspeed(hub->hdev)) status = wait_for_ss_port_enable(udev, hub, &port1, &portchange, @@ -3349,8 +3687,17 @@ hub, port1, status, portchange, portstatus); if (status == 0) status = finish_port_resume(udev); + + /* 20170328 AVM/WK Resume WA: busy bit moved */ + clear_bit(port1, hub->busy_bits); + 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 + /* == 20170228 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 */ @@ -3364,8 +3711,8 @@ return status; } - -#endif /* CONFIG_PM */ +/* AVM/VGJ 20170203 Redundant Config Flag */ +//#endif /* CONFIG_PM */ #ifdef CONFIG_PM_RUNTIME @@ -3387,6 +3734,9 @@ #endif +#endif /* CONFIG_PM || CONFIG_AVM_USB_SUSPEND */ + +#if defined (CONFIG_PM) static int check_ports_changed(struct usb_hub *hub) { int port1; @@ -4655,9 +5005,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(hub_dev, "unable to enumerate USB device on port %d\n", port1); +/* 20160707 AVM/VGJ increment enumeration error */ +#if defined (CONFIG_AVM_KERNEL) + enum_error_count++; +#endif + } } done: @@ -4856,9 +5211,16 @@ msleep(100); /* Cool down */ hub_power_on(hub, true); hub_port_status(hub, i, &status, &unused); - if (status & USB_PORT_STAT_OVERCURRENT) + if (status & USB_PORT_STAT_OVERCURRENT) { dev_err(hub_dev, "over-current " "condition on port %d\n", i); +/* 20160707 AVM/VGJ increment overcurrent error */ +#if defined (CONFIG_AVM_KERNEL) + // Only count overcurrent events for root-hub + if(hdev->level == 0) + overcurrent_error_count++; +#endif + } } if (portchange & USB_PORT_STAT_C_RESET) { @@ -4908,6 +5270,8 @@ } else { usb_lock_device(udev); status = usb_reset_device(udev); + if (status) + dev_err(&udev->dev, "Error reset usb_read_device\n"); usb_unlock_device(udev); connect_change = 0; } @@ -4942,9 +5306,16 @@ msleep(500); /* Cool down */ hub_power_on(hub, true); hub_hub_status(hub, &status, &unused); - if (status & HUB_STATUS_OVERCURRENT) + if (status & HUB_STATUS_OVERCURRENT) { dev_err(hub_dev, "over-current " "condition\n"); +/* 20160707 AVM/VGJ increment overcurrent error */ +#if defined (CONFIG_AVM_KERNEL) + // Only count overcurrent events for root-hub + if(hdev->level == 0) + overcurrent_error_count++; +#endif + } } }