diff options
-rw-r--r-- | README.md | 12 | ||||
-rw-r--r-- | uhubctl.c | 71 |
2 files changed, 51 insertions, 32 deletions
@@ -57,8 +57,9 @@ This is list of known compatible USB hubs: | Plugable | USB3-HUB7BC | 7 | 3.0 |`2109:0813`| 2015 | | | Plugable | USB3-HUB7C | 7 | 3.0 |`2109:0813`| 2015 | | | Plugable | USB3-HUB7-81X | 7 | 3.0 |`2109:0813`| 2012 | | -| Raspberry Pi | Model B+, 2 B, 3 B (port 2 only) | 4 | 2.0 | | 2011 | | -| Raspberry Pi | Model 3 B+ | 6 | 2.0 |`0424:2514`| 2018 | | +| Raspberry Pi | B+, 2B, 3B (port 2 only) | 4 | 2.0 | | 2011 | | +| Raspberry Pi | 3B+ | 6 | 2.0 |`0424:2514`| 2018 | | +| Raspberry Pi | 4B (ganged, must use `-p all` to cut off VBUS) | 4 | 3.0 |`2109:3431`| 2019 | | | Renesas | uPD720202 PCIe USB 3.0 host controller | 2 | 3.0 | | 2013 | | | Rosewill | RHUB-210 | 4 | 2.0 |`0409:005A`| 2011 | 2014 | | Sanwa Supply | USB-HUB14GPH | 4 | 1.1 | | 2001 | 2003 | @@ -267,11 +268,10 @@ For reference, Raspberry Pi models have following internal USB topology: Second hub `1-1.1` (daisy-chained to main): 3 independently controlled ports, `1` is used for Ethernet+wifi, and ports `2,3` are available with proper per-port power switching. In other words, 2 out of total 4 ports wired outside do support independent power switching on 3B+. -* 4B: Hardware supports power switching, but current firmware is reporting broken USB descriptors. - For more details see Raspberry Pi issue https://github.com/raspberrypi/linux/issues/3079. - USB3 hub `2`, 4 ports, per-port power switching. BOS `ContainerID` not reported (required for USB3). +* 4B: Hardware only supports ganged power switching, firmware is reporting inconsistent USB descriptors. + USB3 hub `2`, 4 ports, incorrectly reports per-port power switching. BOS `ContainerID` not reported (required for USB3). USB2 hub `1`, 1 port, no usable ports, connects hub `1-1` below. - USB2 hub `1-1`, 4 ports, dual to USB3 hub above. Hub descriptor incorrectly reports ganged power switching. + USB2 hub `1-1`, 4 ports, dual to USB3 hub above. Hub descriptor reports ganged power switching. USB2 hub `3`, 1 port, OTG controller, incorrectly reports ganged power switching. As a workaround, you can buy any external USB hub from supported list, @@ -408,9 +408,54 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info) strcat(info->location, s); } + /* Get container_id: */ + bzero(info->container_id, sizeof(info->container_id)); + struct libusb_bos_descriptor *bos; + rc = libusb_get_bos_descriptor(devh, &bos); + if (rc == 0) { + int cap; +#ifdef __FreeBSD__ + for (cap=0; cap < bos->bNumDeviceCapabilities; cap++) { +#else + for (cap=0; cap < bos->bNumDeviceCaps; cap++) { +#endif + if (bos->dev_capability[cap]->bDevCapabilityType == LIBUSB_BT_CONTAINER_ID) { + struct libusb_container_id_descriptor *container_id; + rc = libusb_get_container_id_descriptor(NULL, bos->dev_capability[cap], &container_id); + if (rc == 0) { + int i; + for (i=0; i<16; i++) { + sprintf(info->container_id+i*2, "%02x", container_id->ContainerID[i]); + } + info->container_id[i*2] = 0; + libusb_free_container_id_descriptor(container_id); + } + } + } + libusb_free_bos_descriptor(bos); + + /* Raspberry Pi 4 hack for USB3 root hub: */ + if (strlen(info->container_id)==0 && + strcasecmp(info->vendor, "1d6b:0003")==0 && + info->level==1 && + info->nports==4 && + bcd_usb==USB_SS_BCD) + { + strcpy(info->container_id, "5cf3ee30d5074925b001802d79434c30"); + } + } + info->ppps = 0; /* Logical Power Switching Mode */ int lpsm = uhd->wHubCharacteristics[0] & HUB_CHAR_LPSM; + if (lpsm == HUB_CHAR_COMMON_LPSM && info->nports == 1) { + /* For 1 port hubs, ganged power switching is the same as per-port: */ + lpsm = HUB_CHAR_INDV_PORT_LPSM; + } + /* Raspberry Pi 4 reports inconsistent descriptors, override: */ + if (lpsm == HUB_CHAR_COMMON_LPSM && strcasecmp(info->vendor, "2109:3431")==0) { + lpsm = HUB_CHAR_INDV_PORT_LPSM; + } /* Over-Current Protection Mode */ int ocpm = uhd->wHubCharacteristics[0] & HUB_CHAR_OCPM; /* LPSM must be supported per-port, and OCPM per port or ganged */ @@ -423,32 +468,6 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info) } else { rc = len; } - /* Get container_id: */ - bzero(info->container_id, sizeof(info->container_id)); - struct libusb_bos_descriptor *bos; - rc = libusb_get_bos_descriptor(devh, &bos); - if (rc == 0) { - int cap; -#ifdef __FreeBSD__ - for (cap=0; cap < bos->bNumDeviceCapabilities; cap++) { -#else - for (cap=0; cap < bos->bNumDeviceCaps; cap++) { -#endif - if (bos->dev_capability[cap]->bDevCapabilityType == LIBUSB_BT_CONTAINER_ID) { - struct libusb_container_id_descriptor *container_id; - rc = libusb_get_container_id_descriptor(NULL, bos->dev_capability[cap], &container_id); - if (rc == 0) { - int i; - for (i=0; i<16; i++) { - sprintf(info->container_id+i*2, "%02x", container_id->ContainerID[i]); - } - info->container_id[i*2] = 0; - libusb_free_container_id_descriptor(container_id); - } - } - } - libusb_free_bos_descriptor(bos); - } libusb_close(devh); } return rc; |