add toggle for disabling newly added USB devices
[GitHub/LineageOS/android_kernel_motorola_exynos9610.git] / drivers / usb / core / hub.c
index c66e888dff9a9df704b5156d092a5ac2e3f64d80..1f3aa1b96e3745a43d0a293437530c2cb59c267e 100644 (file)
@@ -38,6 +38,8 @@
 #define USB_VENDOR_GENESYS_LOGIC               0x05e3
 #define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND       0x01
 
+extern int deny_new_usb;
+
 /* 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. */
@@ -1110,6 +1112,16 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                                                   USB_PORT_FEAT_ENABLE);
                }
 
+               /*
+                * Add debounce if USB3 link is in polling/link training state.
+                * Link will automatically transition to Enabled state after
+                * link training completes.
+                */
+               if (hub_is_superspeed(hdev) &&
+                   ((portstatus & USB_PORT_STAT_LINK_STATE) ==
+                                               USB_SS_PORT_LS_POLLING))
+                       need_debounce_delay = true;
+
                /* Clear status-change flags; we'll debounce later */
                if (portchange & USB_PORT_STAT_C_CONNECTION) {
                        need_debounce_delay = true;
@@ -1141,10 +1153,14 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
 
                if (!udev || udev->state == USB_STATE_NOTATTACHED) {
                        /* Tell hub_wq to disconnect the device or
-                        * check for a new connection
+                        * check for a new connection or over current condition.
+                        * Based on USB2.0 Spec Section 11.12.5,
+                        * C_PORT_OVER_CURRENT could be set while
+                        * PORT_OVER_CURRENT is not. So check for any of them.
                         */
                        if (udev || (portstatus & USB_PORT_STAT_CONNECTION) ||
-                           (portstatus & USB_PORT_STAT_OVERCURRENT))
+                           (portstatus & USB_PORT_STAT_OVERCURRENT) ||
+                           (portchange & USB_PORT_STAT_C_OVERCURRENT))
                                set_bit(port1, hub->change_bits);
 
                } else if (portstatus & USB_PORT_STAT_ENABLE) {
@@ -2234,7 +2250,7 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
                /* descriptor may appear anywhere in config */
                err = __usb_get_extra_descriptor(udev->rawdescriptors[0],
                                le16_to_cpu(udev->config[0].desc.wTotalLength),
-                               USB_DT_OTG, (void **) &desc);
+                               USB_DT_OTG, (void **) &desc, sizeof(*desc));
                if (err || !(desc->bmAttributes & USB_OTG_HNP))
                        return 0;
 
@@ -2818,7 +2834,9 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                                        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,
+
+                       if (udev)
+                               usb_clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_CONNECTION);
 
                        /*
@@ -3364,6 +3382,10 @@ static int wait_for_connected(struct usb_device *udev,
        while (delay_ms < 2000) {
                if (status || *portstatus & USB_PORT_STAT_CONNECTION)
                        break;
+               if (!port_is_power_on(hub, *portstatus)) {
+                       status = -ENODEV;
+                       break;
+               }
                msleep(20);
                delay_ms += 20;
                status = hub_port_status(hub, *port1, portstatus, portchange);
@@ -4815,6 +4837,12 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
                        goto done;
                return;
        }
+
+       if (deny_new_usb) {
+               dev_err(&port_dev->dev, "denied insert of USB device on port %d\n", port1);
+               goto done;
+       }
+
        if (hub_is_superspeed(hub->hdev))
                unit_load = 150;
        else