From owner-p4-projects@FreeBSD.ORG Sun Aug 1 15:46:22 2010 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id BF308106567A; Sun, 1 Aug 2010 15:46:22 +0000 (UTC) Delivered-To: perforce@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 746D5106566C for ; Sun, 1 Aug 2010 15:46:22 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id 601138FC13 for ; Sun, 1 Aug 2010 15:46:22 +0000 (UTC) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.4/8.14.4) with ESMTP id o71FkM73006992 for ; Sun, 1 Aug 2010 15:46:22 GMT (envelope-from hselasky@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.4/8.14.4/Submit) id o71FkMK0006990 for perforce@freebsd.org; Sun, 1 Aug 2010 15:46:22 GMT (envelope-from hselasky@FreeBSD.org) Date: Sun, 1 Aug 2010 15:46:22 GMT Message-Id: <201008011546.o71FkMK0006990@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to hselasky@FreeBSD.org using -f From: Hans Petter Selasky To: Perforce Change Reviews Precedence: bulk Cc: Subject: PERFORCE change 181685 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 01 Aug 2010 15:46:23 -0000 http://p4web.freebsd.org/@@181685?ac=10 Change 181685 by hselasky@hselasky_laptop001 on 2010/08/01 15:45:30 USB CORE: - add full USB 3.0 (SuperSpeed) support to HUB class driver. Affected files ... .. //depot/projects/usb/src/sys/dev/usb/usb.h#53 edit .. //depot/projects/usb/src/sys/dev/usb/usb_hub.c#50 edit .. //depot/projects/usb/src/sys/dev/usb/usb_hub.h#17 edit .. //depot/projects/usb/src/sys/dev/usb/usb_request.c#35 edit .. //depot/projects/usb/src/sys/dev/usb/usb_request.h#12 edit Differences ... ==== //depot/projects/usb/src/sys/dev/usb/usb.h#53 (text+ko) ==== @@ -226,6 +226,7 @@ #define UR_GET_TT_STATE 0x0a #define UR_STOP_TT 0x0b #define UR_SET_HUB_DEPTH 0x0c +#define USB_SS_HUB_DEPTH_MAX 4 /* exclusive */ #define UR_GET_PORT_ERR_COUNT 0x0d /* Feature numbers */ ==== //depot/projects/usb/src/sys/dev/usb/usb_hub.c#50 (text+ko) ==== @@ -109,6 +109,7 @@ #define UHUB_PROTO(sc) ((sc)->sc_udev->ddesc.bDeviceProtocol) #define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB) #define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT) +#define UHUB_IS_SUPER_SPEED(sc) (UHUB_PROTO(sc) == UDPROTO_SSHUB) /* prototypes for type checking: */ @@ -267,14 +268,14 @@ } /* start control transfer, if device mode */ - if (child->flags.usb_mode == USB_MODE_DEVICE) { + if (child->flags.usb_mode == USB_MODE_DEVICE) usbd_ctrl_transfer_setup(child); - } + /* if a HUB becomes present, do a recursive HUB explore */ - if (child->hub) { + if (child->hub) err = (child->hub->explore) (child); - } + done: return (err); } @@ -373,11 +374,17 @@ DPRINTF("Port %d is in Host Mode\n", portno); if (sc->sc_st.port_status & UPS_SUSPEND) { + /* + * NOTE: Should not get here in SuperSpeed + * mode, because the HUB should report this + * bit as zero. + */ DPRINTF("Port %d was still " "suspended, clearing.\n", portno); - err = usbd_req_clear_port_feature(sc->sc_udev, + err = usbd_req_clear_port_feature(udev, NULL, portno, UHF_PORT_SUSPEND); } + /* USB Host Mode */ /* wait for maximum device power up time */ @@ -438,6 +445,27 @@ case USB_SPEED_LOW: speed = USB_SPEED_LOW; break; + case USB_SPEED_SUPER: + if (udev->parent_hub == NULL) { + /* Root HUB - special case */ + switch (sc->sc_st.port_status & UPS_OTHER_SPEED) { + case 0: + speed = USB_SPEED_FULL; + break; + case UPS_LOW_SPEED: + speed = USB_SPEED_LOW; + break; + case UPS_HIGH_SPEED: + speed = USB_SPEED_HIGH; + break; + default: + speed = USB_SPEED_SUPER; + break; + } + } else { + speed = USB_SPEED_SUPER; + } + break; default: /* same speed like parent */ speed = udev->speed; @@ -485,6 +513,28 @@ } /*------------------------------------------------------------------------* + * usb_device_20_compatible + * + * Returns: + * 0: HUB does not support suspend and resume + * Else: HUB supports suspend and resume + *------------------------------------------------------------------------*/ +static uint8_t +usb_device_20_compatible(struct usb_device *udev) +{ + if (udev == NULL) + return (0); + switch (udev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + return (1); + default: + return (0); + } +} + +/*------------------------------------------------------------------------* * uhub_suspend_resume_port * * Returns: @@ -507,8 +557,14 @@ /* first clear the port suspend change bit */ - err = usbd_req_clear_port_feature(udev, NULL, - portno, UHF_C_PORT_SUSPEND); + if (usb_device_20_compatible(udev)) { + err = usbd_req_clear_port_feature(udev, NULL, + portno, UHF_C_PORT_SUSPEND); + } else { + err = usbd_req_clear_port_feature(udev, NULL, + portno, UHF_C_PORT_LINK_STATE); + } + if (err) { DPRINTF("clearing suspend failed.\n"); goto done; @@ -520,12 +576,24 @@ DPRINTF("reading port status failed.\n"); goto done; } - /* get current state */ + /* convert current state */ - if (sc->sc_st.port_status & UPS_SUSPEND) { - is_suspend = 1; + if (usb_device_20_compatible(udev)) { + if (sc->sc_st.port_status & UPS_SUSPEND) { + is_suspend = 1; + } else { + is_suspend = 0; + } } else { - is_suspend = 0; + switch (UPS_PORT_LINK_STATE_GET(sc->sc_st.port_status)) { + case UPS_PORT_LS_U0: + case UPS_PORT_LS_U1: + is_suspend = 0; + break; + default: + is_suspend = 1; + break; + } } DPRINTF("suspended=%u\n", is_suspend); @@ -540,7 +608,8 @@ */ if (is_suspend == 0) usb_dev_resume_peer(child); - else if (child->flags.usb_mode == USB_MODE_DEVICE) + else if ((child->flags.usb_mode == USB_MODE_DEVICE) || + (usb_device_20_compatible(child) == 0)) usb_dev_suspend_peer(child); } done: @@ -562,6 +631,26 @@ usb_needs_explore(bus, 0); } +static uint8_t +uhub_is_too_deep(struct usb_device *udev) +{ + switch (udev->speed) { + case USB_SPEED_FULL: + case USB_SPEED_LOW: + case USB_SPEED_HIGH: + if (udev->depth > USB_HUB_MAX_DEPTH) + return (1); + break; + case USB_SPEED_SUPER: + if (udev->depth >= USB_SS_HUB_DEPTH_MAX) + return (1); + break; + default: + break; + } + return (0); +} + /*------------------------------------------------------------------------* * uhub_explore * @@ -584,11 +673,11 @@ DPRINTFN(11, "udev=%p addr=%d\n", udev, udev->address); - /* ignore hubs that are too deep */ - if (udev->depth > USB_HUB_MAX_DEPTH) { + /* ignore devices that are too deep */ + if (uhub_is_too_deep(udev)) return (USB_ERR_TOO_DEEP); - } + /* check if device is suspended */ if (udev->flags.self_suspended) { /* need to wait until the child signals resume */ DPRINTF("Device is suspended!\n"); @@ -655,7 +744,7 @@ break; } } - if (sc->sc_st.port_change & UPS_C_SUSPEND) { + if (sc->sc_st.port_change & (UPS_C_SUSPEND | UPS_C_PORT_LINK_STATE)) { err = uhub_suspend_resume_port(sc, portno); if (err) { /* most likely the HUB is gone */ @@ -683,17 +772,17 @@ { struct usb_attach_arg *uaa = device_get_ivars(dev); - if (uaa->usb_mode != USB_MODE_HOST) { + if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); - } + /* - * The subclass for USB HUBs is ignored because it is 0 for - * some and 1 for others. + * The subclass for USB HUBs is currently ignored because it + * is 0 for some and 1 for others. */ if ((uaa->info.bConfigIndex == 0) && - (uaa->info.bDeviceClass == UDCLASS_HUB)) { + (uaa->info.bDeviceClass == UDCLASS_HUB)) return (0); - } + return (ENXIO); } @@ -705,7 +794,8 @@ struct usb_device *udev = uaa->device; struct usb_device *parent_hub = udev->parent_hub; struct usb_hub *hub; - struct usb_hub_descriptor hubdesc; + struct usb_hub_descriptor hubdesc20; + struct usb_hub_ss_descriptor hubdesc30; uint16_t pwrdly; uint8_t x; uint8_t nports; @@ -732,38 +822,96 @@ parent_hub ? parent_hub->flags.self_powered : 0); - if (udev->depth > USB_HUB_MAX_DEPTH) { - DPRINTFN(0, "hub depth, %d, exceeded. HUB ignored\n", - USB_HUB_MAX_DEPTH); + if (uhub_is_too_deep(udev)) { + DPRINTFN(0, "HUB at depth %d, " + "exceeds maximum. HUB ignored\n", (int)udev->depth); goto error; } + if (!udev->flags.self_powered && parent_hub && (!parent_hub->flags.self_powered)) { - DPRINTFN(0, "bus powered HUB connected to " + DPRINTFN(0, "Bus powered HUB connected to " "bus powered HUB. HUB ignored\n"); goto error; } /* get HUB descriptor */ - DPRINTFN(2, "getting HUB descriptor\n"); + DPRINTFN(2, "Getting HUB descriptor\n"); - /* assuming that there is one port */ - err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc, 1); + switch (udev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + /* assuming that there is one port */ + err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, 1); + if (err) { + DPRINTFN(0, "getting USB 2.0 HUB descriptor failed," + "error=%s\n", usbd_errstr(err)); + goto error; + } + /* get number of ports */ + nports = hubdesc20.bNbrPorts; - nports = hubdesc.bNbrPorts; + /* get power delay */ + pwrdly = ((hubdesc20.bPwrOn2PwrGood * UHD_PWRON_FACTOR) + + USB_EXTRA_POWER_UP_TIME); - if (!err && (nports >= 8)) { /* get complete HUB descriptor */ - err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc, nports); - } - if (err) { - DPRINTFN(0, "getting hub descriptor failed," - "error=%s\n", usbd_errstr(err)); - goto error; - } - if (hubdesc.bNbrPorts != nports) { - DPRINTFN(0, "number of ports changed\n"); - goto error; + if (nports >= 8) { + /* check number of ports */ + if (nports > 127) { + DPRINTFN(0, "Invalid number of USB 2.0 ports," + "error=%s\n", usbd_errstr(err)); + goto error; + } + /* get complete HUB descriptor */ + err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, nports); + + if (err) { + DPRINTFN(0, "Getting USB 2.0 HUB descriptor failed," + "error=%s\n", usbd_errstr(err)); + goto error; + } + if (hubdesc20.bNbrPorts != nports) { + DPRINTFN(0, "Number of ports changed\n"); + goto error; + } + } + break; + case USB_SPEED_SUPER: + err = usbd_req_set_hub_depth(udev, NULL, udev->depth); + if (err) { + DPRINTFN(0, "Setting USB 3.0 HUB depth failed," + "error=%s\n", usbd_errstr(err)); + goto error; + } + err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30); + if (err) { + DPRINTFN(0, "Getting USB 3.0 HUB descriptor failed," + "error=%s\n", usbd_errstr(err)); + goto error; + } + /* get number of ports */ + nports = hubdesc30.bNbrPorts; + + /* get power delay */ + pwrdly = ((hubdesc30.bPwrOn2PwrGood * UHD_PWRON_FACTOR) + + USB_EXTRA_POWER_UP_TIME); + + /* check number of ports */ + if (nports > 15) { + DPRINTFN(0, "Invalid number of USB 3.0 ports," + "error=%s\n", usbd_errstr(err)); + goto error; + } + break; + default: + DPRINTF("Assuming HUB has only one port\n"); + /* default number of ports */ + nports = 1; + /* default power delay */ + pwrdly = ((10 * UHD_PWRON_FACTOR) + USB_EXTRA_POWER_UP_TIME); + break; } if (nports == 0) { DPRINTFN(0, "portless HUB\n"); @@ -784,7 +932,7 @@ /* initialize HUB structure */ hub->hubsoftc = sc; hub->explore = &uhub_explore; - hub->nports = hubdesc.bNbrPorts; + hub->nports = nports; hub->hubudev = udev; /* if self powered hub, give ports maximum current */ @@ -840,8 +988,6 @@ /* XXX should check for none, individual, or ganged power? */ removable = 0; - pwrdly = ((hubdesc.bPwrOn2PwrGood * UHD_PWRON_FACTOR) + - USB_EXTRA_POWER_UP_TIME); for (x = 0; x != nports; x++) { /* set up data structures */ @@ -852,8 +998,29 @@ portno = x + 1; /* check if port is removable */ - if (!UHD_NOT_REMOV(&hubdesc, portno)) { + switch (udev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + if (!UHD_NOT_REMOV(&hubdesc20, portno)) + removable++; + break; + case USB_SPEED_SUPER: + if (!err) { + err = usbd_req_set_hub_u1_timeout(udev, NULL, + portno, 128 - (2 * udev->depth)); + } + if (!err) { + err = usbd_req_set_hub_u2_timeout(udev, NULL, + portno, 128 - (2 * udev->depth)); + } + if (!UHD_NOT_REMOV(&hubdesc30, portno)) + removable++; + break; + default: + DPRINTF("Assuming removable port\n"); removable++; + break; } if (!err) { /* turn the power on */ @@ -914,9 +1081,8 @@ struct usb_device *child; uint8_t x; - if (hub == NULL) { /* must be partially working */ + if (hub == NULL) /* must be partially working */ return (0); - } /* Make sure interrupt transfer is gone. */ usbd_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER); @@ -1788,6 +1954,7 @@ (udev->pwr_save.write_refs != 0) || ((udev->pwr_save.read_refs != 0) && (udev->flags.usb_mode == USB_MODE_HOST) && + (usb_device_20_compatible(udev) != 0) && (usb_peer_can_wakeup(udev) == 0))); } @@ -1959,13 +2126,16 @@ /* reduce chance of instant resume failure by waiting a little bit */ usb_pause_mtx(NULL, USB_MS_TO_TICKS(20)); - /* resume current port (Valid in Host and Device Mode) */ - err = usbd_req_clear_port_feature(udev->parent_hub, - NULL, udev->port_no, UHF_PORT_SUSPEND); - if (err) { - DPRINTFN(0, "Resuming port failed\n"); - return; + if (usb_device_20_compatible(udev)) { + /* resume current port (Valid in Host and Device Mode) */ + err = usbd_req_clear_port_feature(udev->parent_hub, + NULL, udev->port_no, UHF_PORT_SUSPEND); + if (err) { + DPRINTFN(0, "Resuming port failed\n"); + return; + } } + /* resume settle time */ usb_pause_mtx(NULL, USB_MS_TO_TICKS(USB_PORT_RESUME_DELAY)); @@ -2005,7 +2175,8 @@ usbd_sr_unlock(udev); /* check if peer has wakeup capability */ - if (usb_peer_can_wakeup(udev)) { + if (usb_peer_can_wakeup(udev) && + usb_device_20_compatible(udev)) { /* clear remote wakeup */ err = usbd_req_clear_device_feature(udev, NULL, UF_DEVICE_REMOTE_WAKEUP); @@ -2015,7 +2186,6 @@ usbd_errstr(err)); } } - return; } /*------------------------------------------------------------------------* @@ -2110,7 +2280,8 @@ usbd_sr_unlock(udev); - if (usb_peer_can_wakeup(udev)) { + if (usb_peer_can_wakeup(udev) && + usb_device_20_compatible(udev)) { /* allow device to do remote wakeup */ err = usbd_req_set_device_feature(udev, NULL, UF_DEVICE_REMOTE_WAKEUP); @@ -2132,12 +2303,15 @@ usb_pause_mtx(NULL, USB_MS_TO_TICKS(temp)); } - /* suspend current port */ - err = usbd_req_set_port_feature(udev->parent_hub, - NULL, udev->port_no, UHF_PORT_SUSPEND); - if (err) { - DPRINTFN(0, "Suspending port failed\n"); - return; + + if (usb_device_20_compatible(udev)) { + /* suspend current port */ + err = usbd_req_set_port_feature(udev->parent_hub, + NULL, udev->port_no, UHF_PORT_SUSPEND); + if (err) { + DPRINTFN(0, "Suspending port failed\n"); + return; + } } udev = udev->parent_hub; ==== //depot/projects/usb/src/sys/dev/usb/usb_hub.h#17 (text+ko) ==== ==== //depot/projects/usb/src/sys/dev/usb/usb_request.c#35 (text+ko) ==== @@ -1293,6 +1293,27 @@ } /*------------------------------------------------------------------------* + * usbd_req_get_ss_hub_descriptor + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb_error_t +usbd_req_get_ss_hub_descriptor(struct usb_device *udev, struct mtx *mtx, + struct usb_hub_ss_descriptor *hd) +{ + struct usb_device_request req; + + req.bmRequestType = UT_READ_CLASS_DEVICE; + req.bRequest = UR_GET_DESCRIPTOR; + USETW2(req.wValue, UDESC_SS_HUB, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(*hd)); + return (usbd_do_request(udev, mtx, &req, hd)); +} + +/*------------------------------------------------------------------------* * usbd_req_get_hub_status * * Returns: @@ -1406,6 +1427,71 @@ } /*------------------------------------------------------------------------* + * usbd_req_set_hub_u1_timeout + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb_error_t +usbd_req_set_hub_u1_timeout(struct usb_device *udev, struct mtx *mtx, + uint8_t port, uint8_t timeout) +{ + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_CLASS_OTHER; + req.bRequest = UR_SET_FEATURE; + USETW(req.wValue, UHF_PORT_U1_TIMEOUT); + req.wIndex[0] = port; + req.wIndex[1] = timeout; + USETW(req.wLength, 0); + return (usbd_do_request(udev, mtx, &req, 0)); +} + +/*------------------------------------------------------------------------* + * usbd_req_set_hub_u2_timeout + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb_error_t +usbd_req_set_hub_u2_timeout(struct usb_device *udev, struct mtx *mtx, + uint8_t port, uint8_t timeout) +{ + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_CLASS_OTHER; + req.bRequest = UR_SET_FEATURE; + USETW(req.wValue, UHF_PORT_U2_TIMEOUT); + req.wIndex[0] = port; + req.wIndex[1] = timeout; + USETW(req.wLength, 0); + return (usbd_do_request(udev, mtx, &req, 0)); +} + +/*------------------------------------------------------------------------* + * usbd_req_set_hub_depth + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb_error_t +usbd_req_set_hub_depth(struct usb_device *udev, struct mtx *mtx, + uint16_t depth) +{ + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_CLASS_DEVICE; + req.bRequest = UR_SET_HUB_DEPTH; + USETW(req.wValue, depth); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + return (usbd_do_request(udev, mtx, &req, 0)); +} + +/*------------------------------------------------------------------------* * usbd_req_clear_port_feature * * Returns: ==== //depot/projects/usb/src/sys/dev/usb/usb_request.h#12 (text+ko) ==== @@ -56,6 +56,8 @@ usb_error_t usbd_req_get_hub_descriptor(struct usb_device *udev, struct mtx *mtx, struct usb_hub_descriptor *hd, uint8_t nports); +usb_error_t usbd_req_get_ss_hub_descriptor(struct usb_device *udev, + struct mtx *mtx, struct usb_hub_ss_descriptor *hd); usb_error_t usbd_req_get_hub_status(struct usb_device *udev, struct mtx *mtx, struct usb_hub_status *st); usb_error_t usbd_req_get_port_status(struct usb_device *udev, struct mtx *mtx, @@ -69,7 +71,15 @@ usb_error_t usbd_req_set_port_feature(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint16_t sel); usb_error_t usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx); -usb_error_t usbd_req_clear_device_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel); -usb_error_t usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel); +usb_error_t usbd_req_clear_device_feature(struct usb_device *udev, + struct mtx *mtx, uint16_t sel); +usb_error_t usbd_req_set_device_feature(struct usb_device *udev, + struct mtx *mtx, uint16_t sel); +usb_error_t usbd_req_set_hub_u1_timeout(struct usb_device *udev, + struct mtx *mtx, uint8_t port, uint8_t timeout); +usb_error_t usbd_req_set_hub_u2_timeout(struct usb_device *udev, + struct mtx *mtx, uint8_t port, uint8_t timeout); +usb_error_t usbd_req_set_hub_depth(struct usb_device *udev, + struct mtx *mtx, uint16_t depth); #endif /* _USB_REQUEST_H_ */