diff --git a/sys/dev/usb/controller/usb_controller.c b/sys/dev/usb/controller/usb_controller.c index d1a3da2f4a33..71e7ef1da698 100644 --- a/sys/dev/usb/controller/usb_controller.c +++ b/sys/dev/usb/controller/usb_controller.c @@ -107,6 +107,7 @@ static driver_t usb_driver = { DRIVER_MODULE(usbus, ohci, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usbus, uhci, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usbus, ehci, usb_driver, usb_devclass, 0, 0); +DRIVER_MODULE(usbus, xhci, usb_driver, usb_devclass, 0, 0); /* Device Only Drivers */ DRIVER_MODULE(usbus, at91_udp, usb_driver, usb_devclass, 0, 0); @@ -359,6 +360,11 @@ usb_bus_attach(struct usb_proc_msg *pm) device_printf(bus->bdev, "480Mbps Wireless USB v2.5\n"); break; + case USB_REV_3_0: + speed = USB_SPEED_SUPER; + device_printf(bus->bdev, "4.8Gbps Super Speed USB v3.0\n"); + break; + default: device_printf(bus->bdev, "Unsupported USB revision\n"); usb_root_mount_rel(bus); diff --git a/sys/dev/usb/usb_controller.h b/sys/dev/usb/usb_controller.h index 0f7ffac5d210..6b15dab993d5 100644 --- a/sys/dev/usb/usb_controller.h +++ b/sys/dev/usb/usb_controller.h @@ -97,6 +97,9 @@ struct usb_bus_methods { void (*get_hw_ep_profile) (struct usb_device *udev, const struct usb_hw_ep_profile **ppf, uint8_t ep_addr); void (*set_stall) (struct usb_device *udev, struct usb_xfer *xfer, struct usb_endpoint *ep, uint8_t *did_stall); + + /* USB Device mode mandatory. USB Host mode optional. */ + void (*clear_stall) (struct usb_device *udev, struct usb_endpoint *ep); /* Optional transfer polling support */ @@ -106,6 +109,28 @@ struct usb_bus_methods { /* Optional fixed power mode support */ void (*get_power_mode) (struct usb_device *udev, int8_t *pmode); + + /* Optional endpoint uninit */ + + void (*endpoint_uninit) (struct usb_device *, struct usb_endpoint *); + + /* Optional device init */ + + usb_error_t (*device_init) (struct usb_device *); + + /* Optional device uninit */ + + void (*device_uninit) (struct usb_device *); + + /* Optional for device and host mode */ + + void (*start_dma_delay) (struct usb_xfer *); + + void (*device_state_change) (struct usb_device *); + + /* Optional for host mode */ + + usb_error_t (*set_address) (struct usb_device *, struct mtx *, uint16_t); }; /* diff --git a/sys/dev/usb/usb_device.c b/sys/dev/usb/usb_device.c index fcbd2c026230..b68092d0e53f 100644 --- a/sys/dev/usb/usb_device.c +++ b/sys/dev/usb/usb_device.c @@ -45,12 +45,16 @@ #include #include #include -#include #include #include #include #include + +#if USB_HAVE_UGEN +#include +#endif + #include "usbdevs.h" #define USB_DEBUG_VAR usb_debug @@ -79,7 +83,9 @@ /* function prototypes */ static void usb_init_endpoint(struct usb_device *, uint8_t, - struct usb_endpoint_descriptor *, struct usb_endpoint *); + struct usb_endpoint_descriptor *, + struct usb_endpoint_ss_comp_descriptor *, + struct usb_endpoint *); static void usb_unconfigure(struct usb_device *, uint8_t); static void usb_detach_device_sub(struct usb_device *, device_t *, uint8_t); @@ -90,7 +96,7 @@ static void usb_init_attach_arg(struct usb_device *, static void usb_suspend_resume_sub(struct usb_device *, device_t, uint8_t); static void usbd_clear_stall_proc(struct usb_proc_msg *_pm); -usb_error_t usb_config_parse(struct usb_device *, uint8_t, uint8_t); +static usb_error_t usb_config_parse(struct usb_device *, uint8_t, uint8_t); static void usbd_set_device_strings(struct usb_device *); #if USB_HAVE_UGEN static void usb_notify_addq(const char *type, struct usb_device *); @@ -360,7 +366,9 @@ usbd_interface_count(struct usb_device *udev, uint8_t *count) *------------------------------------------------------------------------*/ static void usb_init_endpoint(struct usb_device *udev, uint8_t iface_index, - struct usb_endpoint_descriptor *edesc, struct usb_endpoint *ep) + struct usb_endpoint_descriptor *edesc, + struct usb_endpoint_ss_comp_descriptor *ecomp, + struct usb_endpoint *ep) { struct usb_bus_methods *methods; @@ -370,6 +378,7 @@ usb_init_endpoint(struct usb_device *udev, uint8_t iface_index, /* initialise USB endpoint structure */ ep->edesc = edesc; + ep->ecomp = ecomp; ep->iface_index = iface_index; TAILQ_INIT(&ep->endpoint_q.head); ep->endpoint_q.command = &usbd_pipe_start; @@ -640,7 +649,7 @@ usbd_set_config_index(struct usb_device *udev, uint8_t index) * 0: Success * Else: Failure *------------------------------------------------------------------------*/ -usb_error_t +static usb_error_t usb_config_parse(struct usb_device *udev, uint8_t iface_index, uint8_t cmd) { struct usb_idesc_parse_state ips; @@ -763,8 +772,14 @@ usb_config_parse(struct usb_device *udev, uint8_t iface_index, uint8_t cmd) ep = udev->endpoints + temp; if (do_init) { + void *ecomp; + + ecomp = usb_ed_comp_foreach(udev->cdesc, (void *)ed); + if (ecomp != NULL) + DPRINTFN(5, "Found endpoint companion descriptor\n"); + usb_init_endpoint(udev, - ips.iface_index, ed, ep); + ips.iface_index, ed, ecomp, ep); } temp ++; @@ -904,8 +919,8 @@ usbd_set_alt_interface_index(struct usb_device *udev, /*------------------------------------------------------------------------* * usbd_set_endpoint_stall * - * This function is used to make a BULK or INTERRUPT endpoint - * send STALL tokens. + * This function is used to make a BULK or INTERRUPT endpoint send + * STALL tokens in USB device mode. * * Returns: * 0: Success @@ -1536,7 +1551,6 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus, udev->bus = bus; udev->address = USB_START_ADDR; /* default value */ udev->plugtime = (usb_ticks_t)ticks; - usb_set_device_state(udev, USB_STATE_POWERED); /* * We need to force the power mode to "on" because there are plenty * of USB devices out there that do not work very well with @@ -1555,6 +1569,11 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus, udev->ctrl_ep_desc.wMaxPacketSize[0] = USB_MAX_IPACKET; udev->ctrl_ep_desc.wMaxPacketSize[1] = 0; udev->ctrl_ep_desc.bInterval = 0; + + /* set up default endpoint companion descriptor */ + udev->ctrl_ep_comp_desc.bLength = sizeof(udev->ctrl_ep_comp_desc); + udev->ctrl_ep_comp_desc.bDescriptorType = UDESC_ENDPOINT_SS_COMP; + udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; udev->speed = speed; @@ -1579,6 +1598,7 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus, /* init the default endpoint */ usb_init_endpoint(udev, 0, &udev->ctrl_ep_desc, + &udev->ctrl_ep_comp_desc, &udev->ctrl_ep); /* set device index */ @@ -1597,13 +1617,29 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus, /* Create a link from /dev/ugenX.X to the default endpoint */ make_dev_alias(udev->ctrl_dev, "%s", udev->ugen_name); #endif + /* Initialise device */ + if (bus->methods->device_init != NULL) { + err = (bus->methods->device_init) (udev); + if (err != 0) { + DPRINTFN(0, "device init %d failed " + "(%s, ignored)\n", device_index, + usbd_errstr(err)); + goto done; + } + } + /* set powered device state after device init is complete */ + usb_set_device_state(udev, USB_STATE_POWERED); + if (udev->flags.usb_mode == USB_MODE_HOST) { err = usbd_req_set_address(udev, NULL, device_index); - /* This is the new USB device address from now on */ - - udev->address = device_index; + /* + * This is the new USB device address from now on, if + * the set address request didn't set it already. + */ + if (udev->address == USB_START_ADDR) + udev->address = device_index; /* * We ignore any set-address errors, hence there are @@ -1619,9 +1655,6 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus, "(%s, ignored)\n", udev->address, usbd_errstr(err)); } - /* allow device time to set new address */ - usb_pause_mtx(NULL, - USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE)); } else { /* We are not self powered */ udev->flags.self_powered = 0; @@ -1640,45 +1673,16 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus, } usb_set_device_state(udev, USB_STATE_ADDRESSED); - /* - * Get the first 8 bytes of the device descriptor ! - * - * NOTE: "usbd_do_request" will check the device descriptor - * next time we do a request to see if the maximum packet size - * changed! The 8 first bytes of the device descriptor - * contains the maximum packet size to use on control endpoint - * 0. If this value is different from "USB_MAX_IPACKET" a new - * USB control request will be setup! - */ - err = usbd_req_get_desc(udev, NULL, NULL, &udev->ddesc, - USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0); - if (err) { - DPRINTFN(0, "getting device descriptor " - "at addr %d failed, %s\n", udev->address, - usbd_errstr(err)); + /* setup the device descriptor and the initial "wMaxPacketSize" */ + err = usbd_setup_device_desc(udev, NULL); + + if (err != 0) { /* XXX try to re-enumerate the device */ err = usbd_req_re_enumerate(udev, NULL); - if (err) { + if (err) goto done; - } } - DPRINTF("adding unit addr=%d, rev=%02x, class=%d, " - "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n", - udev->address, UGETW(udev->ddesc.bcdUSB), - udev->ddesc.bDeviceClass, - udev->ddesc.bDeviceSubClass, - udev->ddesc.bDeviceProtocol, - udev->ddesc.bMaxPacketSize, - udev->ddesc.bLength, - udev->speed); - /* get the full device descriptor */ - err = usbd_req_get_device_desc(udev, NULL, &udev->ddesc); - if (err) { - DPRINTF("addr=%d, getting full desc failed\n", - udev->address); - goto done; - } /* * Setup temporary USB attach args so that we can figure out some * basic quirks for this device. @@ -2068,6 +2072,10 @@ usb_free_device(struct usb_device *udev, uint8_t flag) KASSERT(LIST_FIRST(&udev->pd_list) == NULL, ("leaked cdev entries")); #endif + /* Uninitialise device */ + if (bus->methods->device_uninit != NULL) + (bus->methods->device_uninit) (udev); + /* free device */ free(udev->serial, M_USB); free(udev->manufacturer, M_USB); @@ -2598,6 +2606,17 @@ usb_set_device_state(struct usb_device *udev, enum usb_dev_state state) DPRINTF("udev %p state %s -> %s\n", udev, usb_statestr(udev->state), usb_statestr(state)); udev->state = state; + + if (udev->bus->methods->device_state_change != NULL) + (udev->bus->methods->device_state_change) (udev); +} + +enum usb_dev_state +usb_get_device_state(struct usb_device *udev) +{ + if (udev == NULL) + return (USB_STATE_DETACHED); + return (udev->state); } uint8_t diff --git a/sys/dev/usb/usb_device.h b/sys/dev/usb/usb_device.h index e53afa251fd0..c8bc5eb95a18 100644 --- a/sys/dev/usb/usb_device.h +++ b/sys/dev/usb/usb_device.h @@ -151,6 +151,7 @@ struct usb_device { uint8_t address; /* device addess */ uint8_t device_index; /* device index in "bus->devices" */ + uint8_t controller_slot_id; /* controller specific value */ uint8_t curr_config_index; /* current configuration index */ uint8_t curr_config_no; /* current configuration number */ uint8_t depth; /* distance from root HUB */ @@ -169,11 +170,12 @@ struct usb_device { struct usb_device_flags flags; struct usb_endpoint_descriptor ctrl_ep_desc; /* for endpoint 0 */ + struct usb_endpoint_ss_comp_descriptor ctrl_ep_comp_desc; /* for endpoint 0 */ struct usb_device_descriptor ddesc; /* device descriptor */ - char *serial; /* serial number */ - char *manufacturer; /* manufacturer string */ - char *product; /* product string */ + char *serial; /* serial number, can be NULL */ + char *manufacturer; /* manufacturer string, can be NULL */ + char *product; /* product string, can be NULL */ #if USB_HAVE_COMPAT_LINUX /* Linux compat */ @@ -213,8 +215,9 @@ void usb_free_device(struct usb_device *, uint8_t); void usb_linux_free_device(struct usb_device *dev); uint8_t usb_peer_can_wakeup(struct usb_device *udev); struct usb_endpoint *usb_endpoint_foreach(struct usb_device *udev, struct usb_endpoint *ep); -void usb_set_device_state(struct usb_device *udev, - enum usb_dev_state state); +void usb_set_device_state(struct usb_device *, enum usb_dev_state); +enum usb_dev_state usb_get_device_state(struct usb_device *); + void usbd_enum_lock(struct usb_device *); void usbd_enum_unlock(struct usb_device *); void usbd_sr_lock(struct usb_device *); diff --git a/sys/dev/usb/usb_hub.c b/sys/dev/usb/usb_hub.c index b8c4d0c5ce80..afeadaa4c392 100644 --- a/sys/dev/usb/usb_hub.c +++ b/sys/dev/usb/usb_hub.c @@ -2,7 +2,7 @@ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. * Copyright (c) 1998 Lennart Augustsson. All rights reserved. - * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2008-2010 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -110,6 +110,7 @@ struct uhub_softc { #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: */ @@ -236,6 +237,7 @@ uhub_explore_sub(struct uhub_softc *sc, struct usb_port *up) /* nothing to do */ goto done; } + /* check if device should be re-enumerated */ if (child->flags.usb_mode == USB_MODE_HOST) { @@ -268,14 +270,14 @@ uhub_explore_sub(struct uhub_softc *sc, struct usb_port *up) } /* 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); } @@ -374,11 +376,17 @@ uhub_reattach_port(struct uhub_softc *sc, uint8_t portno) 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 */ @@ -439,11 +447,49 @@ uhub_reattach_port(struct uhub_softc *sc, uint8_t portno) 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; break; } + if (speed == USB_SPEED_SUPER) { + err = usbd_req_set_hub_u1_timeout(udev, NULL, + portno, 128 - (2 * udev->depth)); + if (err) { + DPRINTFN(0, "port %d U1 timeout " + "failed, error=%s\n", + portno, usbd_errstr(err)); + } + err = usbd_req_set_hub_u2_timeout(udev, NULL, + portno, 128 - (2 * udev->depth)); + if (err) { + DPRINTFN(0, "port %d U2 timeout " + "failed, error=%s\n", + portno, usbd_errstr(err)); + } + } + /* * Figure out the device mode * @@ -485,6 +531,28 @@ uhub_reattach_port(struct uhub_softc *sc, uint8_t portno) return (err); } +/*------------------------------------------------------------------------* + * 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 * @@ -508,8 +576,14 @@ uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno) /* 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; @@ -521,12 +595,24 @@ uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno) 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); @@ -541,7 +627,8 @@ uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno) */ 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: @@ -563,6 +650,26 @@ uhub_root_intr(struct usb_bus *bus, const uint8_t *ptr, uint8_t len) 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 * @@ -585,11 +692,11 @@ uhub_explore(struct usb_device *udev) 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"); @@ -656,7 +763,7 @@ uhub_explore(struct usb_device *udev) 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 */ @@ -684,20 +791,81 @@ uhub_probe(device_t dev) { 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)) { + if (uaa->info.bConfigIndex == 0 && + uaa->info.bDeviceClass == UDCLASS_HUB) return (0); - } + return (ENXIO); } +/* NOTE: The information returned by this function can be wrong. */ +usb_error_t +uhub_query_info(struct usb_device *udev, uint8_t *pnports, uint8_t *ptt) +{ + struct usb_hub_descriptor hubdesc20; + struct usb_hub_ss_descriptor hubdesc30; + usb_error_t err; + uint8_t nports; + uint8_t tt; + + if (udev->ddesc.bDeviceClass != UDCLASS_HUB) + return (USB_ERR_INVAL); + + nports = 0; + tt = 0; + + 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)); + break; + } + nports = hubdesc20.bNbrPorts; + if (nports > 127) + nports = 127; + + if (udev->speed == USB_SPEED_HIGH) + tt = (UGETW(hubdesc20.wHubCharacteristics) >> 5) & 3; + break; + + case USB_SPEED_SUPER: + err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, 1); + if (err) { + DPRINTFN(0, "Getting USB 3.0 HUB descriptor failed," + "error=%s\n", usbd_errstr(err)); + break; + } + nports = hubdesc30.bNbrPorts; + if (nports > 16) + nports = 16; + break; + + default: + err = USB_ERR_INVAL; + break; + } + + if (pnports != NULL) + *pnports = nports; + + if (ptt != NULL) + *ptt = tt; + + return (err); +} + static int uhub_attach(device_t dev) { @@ -706,7 +874,8 @@ uhub_attach(device_t dev) 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; @@ -733,38 +902,114 @@ uhub_attach(device_t dev) 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 " + !parent_hub->flags.self_powered) { + 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: + if (udev->parent_hub != NULL) { + err = usbd_req_set_hub_depth(udev, NULL, + udev->depth - 1); + 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, 1); + 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); + + /* get complete HUB descriptor */ + if (nports >= 8) { + /* check number of ports */ + if (nports > ((udev->parent_hub != NULL) ? 15 : 127)) { + DPRINTFN(0, "Invalid number of USB 3.0 ports," + "error=%s\n", usbd_errstr(err)); + goto error; + } + /* get complete HUB descriptor */ + err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, nports); + + if (err) { + DPRINTFN(0, "Getting USB 2.0 HUB descriptor failed," + "error=%s\n", usbd_errstr(err)); + goto error; + } + if (hubdesc30.bNbrPorts != nports) { + DPRINTFN(0, "Number of ports changed\n"); + 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"); @@ -785,7 +1030,7 @@ uhub_attach(device_t dev) /* 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 */ @@ -841,8 +1086,6 @@ uhub_attach(device_t dev) /* 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 */ @@ -853,8 +1096,21 @@ uhub_attach(device_t dev) 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 (!UHD_NOT_REMOV(&hubdesc30, portno)) + removable++; + break; + default: + DPRINTF("Assuming removable port\n"); removable++; + break; } if (!err) { /* turn the power on */ @@ -915,9 +1171,8 @@ uhub_detach(device_t dev) 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); @@ -1789,6 +2044,7 @@ usb_peer_should_wakeup(struct usb_device *udev) (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))); } @@ -1960,13 +2216,16 @@ usb_dev_resume_peer(struct usb_device *udev) /* 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)); @@ -2006,7 +2265,8 @@ usb_dev_resume_peer(struct usb_device *udev) 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); @@ -2016,7 +2276,6 @@ usb_dev_resume_peer(struct usb_device *udev) usbd_errstr(err)); } } - return; } /*------------------------------------------------------------------------* @@ -2054,7 +2313,6 @@ usb_dev_suspend_peer(struct usb_device *udev) /* check if all devices on the HUB are suspended */ for (x = 0; x != nports; x++) { - child = usb_bus_port_get_device(udev->bus, udev->hub->ports + x); @@ -2069,6 +2327,22 @@ usb_dev_suspend_peer(struct usb_device *udev) } } + if (usb_peer_can_wakeup(udev) && + usb_device_20_compatible(udev)) { + /* + * This request needs to be done before we set + * "udev->flags.self_suspended": + */ + + /* allow device to do remote wakeup */ + err = usbd_req_set_device_feature(udev, + NULL, UF_DEVICE_REMOTE_WAKEUP); + if (err) { + DPRINTFN(0, "Setting device " + "remote wakeup failed\n"); + } + } + USB_BUS_LOCK(udev->bus); /* * Checking for suspend condition and setting suspended bit @@ -2086,6 +2360,17 @@ usb_dev_suspend_peer(struct usb_device *udev) USB_BUS_UNLOCK(udev->bus); if (err != 0) { + if (usb_peer_can_wakeup(udev) && + usb_device_20_compatible(udev)) { + /* allow device to do remote wakeup */ + err = usbd_req_clear_device_feature(udev, + NULL, UF_DEVICE_REMOTE_WAKEUP); + if (err) { + DPRINTFN(0, "Setting device " + "remote wakeup failed\n"); + } + } + if (udev->flags.usb_mode == USB_MODE_DEVICE) { /* resume parent HUB first */ usb_dev_resume_peer(udev->parent_hub); @@ -2111,16 +2396,6 @@ usb_dev_suspend_peer(struct usb_device *udev) usbd_sr_unlock(udev); - if (usb_peer_can_wakeup(udev)) { - /* allow device to do remote wakeup */ - err = usbd_req_set_device_feature(udev, - NULL, UF_DEVICE_REMOTE_WAKEUP); - if (err) { - DPRINTFN(0, "Setting device " - "remote wakeup failed\n"); - } - } - if (udev->bus->methods->device_suspend != NULL) { usb_timeout_t temp; @@ -2133,12 +2408,15 @@ usb_dev_suspend_peer(struct usb_device *udev) 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; diff --git a/sys/dev/usb/usb_hub.h b/sys/dev/usb/usb_hub.h index 5b8dedf6e647..0f5959977084 100644 --- a/sys/dev/usb/usb_hub.h +++ b/sys/dev/usb/usb_hub.h @@ -78,5 +78,6 @@ void usb_needs_explore_all(void); void usb_bus_power_update(struct usb_bus *bus); void usb_bus_powerd(struct usb_bus *bus); void uhub_root_intr(struct usb_bus *, const uint8_t *, uint8_t); +usb_error_t uhub_query_info(struct usb_device *, uint8_t *, uint8_t *); #endif /* _USB_HUB_H_ */ diff --git a/sys/dev/usb/usb_parse.c b/sys/dev/usb/usb_parse.c index deb52ea50e83..8663c1e07832 100644 --- a/sys/dev/usb/usb_parse.c +++ b/sys/dev/usb/usb_parse.c @@ -180,7 +180,7 @@ usb_edesc_foreach(struct usb_config_descriptor *cd, } if (desc->bDescriptorType == UDESC_ENDPOINT) { if (desc->bLength < sizeof(*ped)) { - /* endpoint index is invalid */ + /* endpoint descriptor is invalid */ break; } return ((struct usb_endpoint_descriptor *)desc); @@ -189,6 +189,42 @@ usb_edesc_foreach(struct usb_config_descriptor *cd, return (NULL); } +/*------------------------------------------------------------------------* + * usb_ed_comp_foreach + * + * This function will iterate all the endpoint companion descriptors + * within an endpoint descriptor in an interface descriptor. Starting + * value for the "ped" argument should be a valid endpoint companion + * descriptor. + * + * Return values: + * NULL: End of descriptors + * Else: A valid endpoint companion descriptor + *------------------------------------------------------------------------*/ +struct usb_endpoint_ss_comp_descriptor * +usb_ed_comp_foreach(struct usb_config_descriptor *cd, + struct usb_endpoint_ss_comp_descriptor *ped) +{ + struct usb_descriptor *desc; + + desc = ((struct usb_descriptor *)ped); + + while ((desc = usb_desc_foreach(cd, desc))) { + if (desc->bDescriptorType == UDESC_INTERFACE) + break; + if (desc->bDescriptorType == UDESC_ENDPOINT) + break; + if (desc->bDescriptorType == UDESC_ENDPOINT_SS_COMP) { + if (desc->bLength < sizeof(*ped)) { + /* endpoint companion descriptor is invalid */ + break; + } + return ((struct usb_endpoint_ss_comp_descriptor *)desc); + } + } + return (NULL); +} + /*------------------------------------------------------------------------* * usbd_get_no_descriptors * diff --git a/sys/dev/usb/usb_request.c b/sys/dev/usb/usb_request.c index 9f1672592394..859af692f52a 100644 --- a/sys/dev/usb/usb_request.c +++ b/sys/dev/usb/usb_request.c @@ -245,6 +245,8 @@ usb_do_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) ep->is_stalled) { ep->toggle_next = 0; ep->is_stalled = 0; + /* some hardware needs a callback to clear the data toggle */ + usbd_clear_stall_locked(udev, ep); /* start up the current or next transfer, if any */ usb_command_wrapper(&ep->endpoint_q, ep->endpoint_q.curr); @@ -1299,6 +1301,28 @@ usbd_req_get_hub_descriptor(struct usb_device *udev, struct mtx *mtx, return (usbd_do_request(udev, mtx, &req, hd)); } +/*------------------------------------------------------------------------* + * 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, uint8_t nports) +{ + struct usb_device_request req; + uint16_t len = sizeof(*hd) - 32 + 1 + ((nports + 7) / 8); + + 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, len); + return (usbd_do_request(udev, mtx, &req, hd)); +} + /*------------------------------------------------------------------------* * usbd_req_get_hub_status * @@ -1334,6 +1358,7 @@ usb_error_t usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr) { struct usb_device_request req; + usb_error_t err; DPRINTFN(6, "setting device address=%d\n", addr); @@ -1343,9 +1368,25 @@ usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr) USETW(req.wIndex, 0); USETW(req.wLength, 0); + err = USB_ERR_INVAL; + + /* check if USB controller handles set address */ + if (udev->bus->methods->set_address != NULL) + err = (udev->bus->methods->set_address) (udev, mtx, addr); + + if (err != USB_ERR_INVAL) + goto done; + /* Setting the address should not take more than 1 second ! */ - return (usbd_do_request_flags(udev, mtx, &req, NULL, - USB_DELAY_STATUS_STAGE, NULL, 1000)); + err = usbd_do_request_flags(udev, mtx, &req, NULL, + USB_DELAY_STATUS_STAGE, NULL, 1000); + +done: + /* allow device time to set new address */ + usb_pause_mtx(mtx, + USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE)); + + return (err); } /*------------------------------------------------------------------------* @@ -1412,6 +1453,71 @@ usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx, return (usbd_do_request(udev, mtx, &req, 0)); } +/*------------------------------------------------------------------------* + * 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 * @@ -1644,6 +1750,68 @@ usbd_req_get_config(struct usb_device *udev, struct mtx *mtx, uint8_t *pconf) return (usbd_do_request(udev, mtx, &req, pconf)); } +/*------------------------------------------------------------------------* + * usbd_setup_device_desc + *------------------------------------------------------------------------*/ +usb_error_t +usbd_setup_device_desc(struct usb_device *udev, struct mtx *mtx) +{ + usb_error_t err; + + /* + * Get the first 8 bytes of the device descriptor ! + * + * NOTE: "usbd_do_request()" will check the device descriptor + * next time we do a request to see if the maximum packet size + * changed! The 8 first bytes of the device descriptor + * contains the maximum packet size to use on control endpoint + * 0. If this value is different from "USB_MAX_IPACKET" a new + * USB control request will be setup! + */ + switch (udev->speed) { + case USB_SPEED_FULL: + case USB_SPEED_LOW: + err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc, + USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0); + if (err != 0) { + DPRINTFN(0, "getting device descriptor " + "at addr %d failed, %s\n", udev->address, + usbd_errstr(err)); + return (err); + } + break; + default: + DPRINTF("Minimum MaxPacketSize is large enough " + "to hold the complete device descriptor\n"); + break; + } + + /* get the full device descriptor */ + err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); + + /* try one more time, if error */ + if (err) + err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); + + if (err) { + DPRINTF("addr=%d, getting full desc failed\n", + udev->address); + return (err); + } + + DPRINTF("adding unit addr=%d, rev=%02x, class=%d, " + "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n", + udev->address, UGETW(udev->ddesc.bcdUSB), + udev->ddesc.bDeviceClass, + udev->ddesc.bDeviceSubClass, + udev->ddesc.bDeviceProtocol, + udev->ddesc.bMaxPacketSize, + udev->ddesc.bLength, + udev->speed); + + return (err); +} + /*------------------------------------------------------------------------* * usbd_req_re_enumerate * @@ -1678,6 +1846,7 @@ usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx) old_addr, usbd_errstr(err)); goto done; } + /* * After that the port has been reset our device should be at * address zero: @@ -1687,6 +1856,9 @@ usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx) /* reset "bMaxPacketSize" */ udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; + /* reset USB state */ + usb_set_device_state(udev, USB_STATE_POWERED); + /* * Restore device address: */ @@ -1696,29 +1868,16 @@ usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx) DPRINTFN(0, "addr=%d, set address failed! (%s, ignored)\n", old_addr, usbd_errstr(err)); } - /* restore device address */ - udev->address = old_addr; + /* + * Restore device address, if the controller driver did not + * set a new one: + */ + if (udev->address == USB_START_ADDR) + udev->address = old_addr; - /* allow device time to set new address */ - usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE)); + /* setup the device descriptor and the initial "wMaxPacketSize" */ + err = usbd_setup_device_desc(udev, mtx); - /* get the device descriptor */ - err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc, - USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0); - if (err) { - DPRINTFN(0, "getting device descriptor " - "at addr %d failed, %s\n", udev->address, - usbd_errstr(err)); - goto done; - } - /* get the full device descriptor */ - err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); - if (err) { - DPRINTFN(0, "addr=%d, getting device " - "descriptor failed, %s\n", old_addr, - usbd_errstr(err)); - goto done; - } done: if (err && do_retry) { /* give the USB firmware some time to load */ @@ -1729,7 +1888,11 @@ usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx) goto retry; } /* restore address */ - udev->address = old_addr; + if (udev->address == USB_START_ADDR) + udev->address = old_addr; + /* update state, if successful */ + if (err == 0) + usb_set_device_state(udev, USB_STATE_ADDRESSED); return (err); } diff --git a/sys/dev/usb/usb_request.h b/sys/dev/usb/usb_request.h index 21585651c19f..1ce8b563b880 100644 --- a/sys/dev/usb/usb_request.h +++ b/sys/dev/usb/usb_request.h @@ -56,6 +56,9 @@ usb_error_t usbd_req_get_device_status(struct usb_device *udev, 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, + uint8_t nports); 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, @@ -68,8 +71,17 @@ usb_error_t usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel); 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_setup_device_desc(struct usb_device *udev, struct mtx *mtx); 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_ */ diff --git a/sys/dev/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c index af83e5a0b7f9..e0f5a3bc7cdd 100644 --- a/sys/dev/usb/usb_transfer.c +++ b/sys/dev/usb/usb_transfer.c @@ -109,7 +109,6 @@ static int usbd_setup_ctrl_transfer(struct usb_xfer *); static void usb_callback_proc(struct usb_proc_msg *); static void usbd_callback_ss_done_defer(struct usb_xfer *); static void usbd_callback_wrapper(struct usb_xfer_queue *); -static void usb_dma_delay_done_cb(void *); static void usbd_transfer_start_cb(void *); static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *); static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr, @@ -137,14 +136,10 @@ static void usbd_update_max_frame_size(struct usb_xfer *xfer) { /* compute maximum frame size */ + /* this computation should not overflow 16-bit */ + /* max = 15 * 1024 */ - if (xfer->max_packet_count == 2) { - xfer->max_frame_size = 2 * xfer->max_packet_size; - } else if (xfer->max_packet_count == 3) { - xfer->max_frame_size = 3 * xfer->max_packet_size; - } else { - xfer->max_frame_size = xfer->max_packet_size; - } + xfer->max_frame_size = xfer->max_packet_size * xfer->max_packet_count; } /*------------------------------------------------------------------------* @@ -320,6 +315,7 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm) }; struct usb_xfer *xfer = parm->curr_xfer; const struct usb_config *setup = parm->curr_setup; + struct usb_endpoint_ss_comp_descriptor *ecomp; struct usb_endpoint_descriptor *edesc; struct usb_std_packet_size std_size; usb_frcount_t n_frlengths; @@ -339,6 +335,7 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm) goto done; } edesc = xfer->endpoint->edesc; + ecomp = xfer->endpoint->ecomp; type = (edesc->bmAttributes & UE_XFERTYPE); @@ -355,9 +352,54 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm) parm->bufsize = setup->bufsize; - if (parm->speed == USB_SPEED_HIGH) { - xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; + switch (parm->speed) { + case USB_SPEED_HIGH: + switch (type) { + case UE_ISOCHRONOUS: + case UE_INTERRUPT: + xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; + + /* check for invalid max packet count */ + if (xfer->max_packet_count > 3) + xfer->max_packet_count = 3; + break; + default: + break; + } xfer->max_packet_size &= 0x7FF; + break; + case USB_SPEED_SUPER: + xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; + + if (ecomp != NULL) + xfer->max_packet_count += ecomp->bMaxBurst; + + if ((xfer->max_packet_count == 0) || + (xfer->max_packet_count > 16)) + xfer->max_packet_count = 16; + + switch (type) { + case UE_CONTROL: + xfer->max_packet_count = 1; + break; + case UE_ISOCHRONOUS: + if (ecomp != NULL) { + uint8_t mult; + + mult = (ecomp->bmAttributes & 3) + 1; + if (mult > 3) + mult = 3; + + xfer->max_packet_count *= mult; + } + break; + default: + break; + } + xfer->max_packet_size &= 0x7FF; + break; + default: + break; } /* range check "max_packet_count" */ @@ -450,41 +492,57 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm) } else { /* - * if a value is specified use that else check the endpoint - * descriptor + * If a value is specified use that else check the + * endpoint descriptor! */ - if (xfer->interval == 0) { + if (type == UE_INTERRUPT) { - if (type == UE_INTERRUPT) { + uint32_t temp; + + if (xfer->interval == 0) { xfer->interval = edesc->bInterval; switch (parm->speed) { - case USB_SPEED_SUPER: - case USB_SPEED_VARIABLE: + case USB_SPEED_LOW: + case USB_SPEED_FULL: + break; + default: /* 125us -> 1ms */ if (xfer->interval < 4) xfer->interval = 1; else if (xfer->interval > 16) - xfer->interval = (1<<(16-4)); + xfer->interval = (1 << (16 - 4)); else xfer->interval = - (1 << (xfer->interval-4)); - break; - case USB_SPEED_HIGH: - /* 125us -> 1ms */ - xfer->interval /= 8; - break; - default: + (1 << (xfer->interval - 4)); break; } - if (xfer->interval == 0) { - /* - * One millisecond is the smallest - * interval we support: - */ - xfer->interval = 1; - } + } + + if (xfer->interval == 0) { + /* + * One millisecond is the smallest + * interval we support: + */ + xfer->interval = 1; + } + + xfer->fps_shift = 0; + temp = 1; + + while ((temp != 0) && (temp < xfer->interval)) { + xfer->fps_shift++; + temp *= 2; + } + + switch (parm->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + break; + default: + xfer->fps_shift += 3; + break; } } } @@ -2185,11 +2243,9 @@ usbd_callback_wrapper(struct usb_xfer_queue *pq) * transfer. This code path is ususally only used when there is an USB * error like USB_ERR_CANCELLED. *------------------------------------------------------------------------*/ -static void -usb_dma_delay_done_cb(void *arg) +void +usb_dma_delay_done_cb(struct usb_xfer *xfer) { - struct usb_xfer *xfer = arg; - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTFN(3, "Completed %p\n", xfer); @@ -2555,14 +2611,17 @@ static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *xfer) { struct usb_endpoint *ep; + struct usb_bus *bus; usb_frcount_t x; + bus = xfer->xroot->bus; + if ((!xfer->flags_int.open) && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); - USB_BUS_LOCK(xfer->xroot->bus); + USB_BUS_LOCK(bus); (xfer->endpoint->methods->close) (xfer); - USB_BUS_UNLOCK(xfer->xroot->bus); + USB_BUS_UNLOCK(bus); /* only close once */ xfer->flags_int.did_close = 1; return (1); /* wait for new callback */ @@ -2571,9 +2630,10 @@ usbd_callback_wrapper_sub(struct usb_xfer *xfer) * If we have a non-hardware induced error we * need to do the DMA delay! */ - if (((xfer->error == USB_ERR_CANCELLED) || - (xfer->error == USB_ERR_TIMEOUT)) && - (!xfer->flags_int.did_dma_delay)) { + if (xfer->error != 0 && !xfer->flags_int.did_dma_delay && + (xfer->error == USB_ERR_CANCELLED || + xfer->error == USB_ERR_TIMEOUT || + bus->methods->start_dma_delay != NULL)) { usb_timeout_t temp; @@ -2589,10 +2649,20 @@ usbd_callback_wrapper_sub(struct usb_xfer *xfer) "on %p\n", temp, xfer); if (temp != 0) { - USB_BUS_LOCK(xfer->xroot->bus); - usbd_transfer_timeout_ms(xfer, - &usb_dma_delay_done_cb, temp); - USB_BUS_UNLOCK(xfer->xroot->bus); + USB_BUS_LOCK(bus); + /* + * Some hardware solutions have dedicated + * events when it is safe to free DMA'ed + * memory. For the other hardware platforms we + * use a static delay. + */ + if (bus->methods->start_dma_delay != NULL) { + (bus->methods->start_dma_delay) (xfer); + } else { + usbd_transfer_timeout_ms(xfer, + (void *)&usb_dma_delay_done_cb, temp); + } + USB_BUS_UNLOCK(bus); return (1); /* wait for new callback */ } } @@ -2684,7 +2754,7 @@ usbd_callback_wrapper_sub(struct usb_xfer *xfer) * If the current USB transfer is completing we need to start the * next one: */ - USB_BUS_LOCK(xfer->xroot->bus); + USB_BUS_LOCK(bus); if (ep->endpoint_q.curr == xfer) { usb_command_wrapper(&ep->endpoint_q, NULL); @@ -2696,7 +2766,7 @@ usbd_callback_wrapper_sub(struct usb_xfer *xfer) xfer->endpoint->is_synced = 0; } } - USB_BUS_UNLOCK(xfer->xroot->bus); + USB_BUS_UNLOCK(bus); done: return (0); } @@ -2842,12 +2912,34 @@ usbd_ctrl_transfer_setup(struct usb_device *udev) * data toggle. *------------------------------------------------------------------------*/ void +usbd_clear_stall_locked(struct usb_device *udev, struct usb_endpoint *ep) +{ + USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); + + /* check that we have a valid case */ + if (udev->flags.usb_mode == USB_MODE_HOST && + udev->parent_hub != NULL && + udev->bus->methods->clear_stall != NULL && + ep->methods != NULL) { + (udev->bus->methods->clear_stall) (udev, ep); + } +} + +/*------------------------------------------------------------------------* + * usbd_clear_data_toggle - factored out code + * + * NOTE: the intention of this function is not to reset the hardware + * data toggle on the USB device side. + *------------------------------------------------------------------------*/ +void usbd_clear_data_toggle(struct usb_device *udev, struct usb_endpoint *ep) { DPRINTFN(5, "udev=%p endpoint=%p\n", udev, ep); USB_BUS_LOCK(udev->bus); ep->toggle_next = 0; + /* some hardware needs a callback to clear the data toggle */ + usbd_clear_stall_locked(udev, ep); USB_BUS_UNLOCK(udev->bus); } diff --git a/sys/dev/usb/usb_transfer.h b/sys/dev/usb/usb_transfer.h index 2c5fe6f1ecef..71157ca71156 100644 --- a/sys/dev/usb/usb_transfer.h +++ b/sys/dev/usb/usb_transfer.h @@ -101,7 +101,7 @@ struct usb_setup_params { usb_frlength_t bufsize; usb_frlength_t bufsize_max; - uint16_t hc_max_frame_size; + uint32_t hc_max_frame_size; uint16_t hc_max_packet_size; uint8_t hc_max_packet_count; enum usb_dev_speed speed; @@ -114,6 +114,7 @@ struct usb_setup_params { uint8_t usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm, struct usb_page_cache **ppc, usb_size_t size, usb_size_t align, usb_size_t count); +void usb_dma_delay_done_cb(struct usb_xfer *); void usb_command_wrapper(struct usb_xfer_queue *pq, struct usb_xfer *xfer); void usbd_pipe_enter(struct usb_xfer *xfer); @@ -124,6 +125,8 @@ void usbd_transfer_enqueue(struct usb_xfer_queue *pq, struct usb_xfer *xfer); void usbd_transfer_setup_sub(struct usb_setup_params *parm); void usbd_ctrl_transfer_setup(struct usb_device *udev); +void usbd_clear_stall_locked(struct usb_device *udev, + struct usb_endpoint *ep); void usbd_clear_data_toggle(struct usb_device *udev, struct usb_endpoint *ep); usb_callback_t usbd_do_request_callback; diff --git a/sys/dev/usb/usbdi.h b/sys/dev/usb/usbdi.h index 79b73ca1735e..1c2d412b9f54 100644 --- a/sys/dev/usb/usbdi.h +++ b/sys/dev/usb/usbdi.h @@ -135,6 +135,7 @@ struct usb_endpoint { struct usb_xfer_queue endpoint_q; /* queue of USB transfers */ struct usb_endpoint_descriptor *edesc; + struct usb_endpoint_ss_comp_descriptor *ecomp; struct usb_pipe_methods *methods; /* set by HC driver */ uint16_t isoc_next; diff --git a/sys/dev/usb/usbdi_util.h b/sys/dev/usb/usbdi_util.h index 352f3ff1c716..1e450f8f4ff2 100644 --- a/sys/dev/usb/usbdi_util.h +++ b/sys/dev/usb/usbdi_util.h @@ -51,6 +51,9 @@ struct usb_interface_descriptor *usb_idesc_foreach( struct usb_endpoint_descriptor *usb_edesc_foreach( struct usb_config_descriptor *cd, struct usb_endpoint_descriptor *ped); +struct usb_endpoint_ss_comp_descriptor *usb_ed_comp_foreach( + struct usb_config_descriptor *cd, + struct usb_endpoint_ss_comp_descriptor *ped); uint8_t usbd_get_no_descriptors(struct usb_config_descriptor *cd, uint8_t type); uint8_t usbd_get_no_alts(struct usb_config_descriptor *cd,