1 // Code for handling standard USB hubs.
3 // Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net>
5 // This file may be distributed under the terms of the GNU LGPLv3 license.
7 #include "config.h" // CONFIG_USB_HUB
8 #include "output.h" // dprintf
9 #include "string.h" // memset
10 #include "usb.h" // struct usb_s
11 #include "usb-hub.h" // struct usb_hub_descriptor
12 #include "util.h" // timer_calc
15 get_hub_desc(struct usb_pipe *pipe, struct usb_hub_descriptor *desc)
17 struct usb_ctrlrequest req;
18 req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE;
19 req.bRequest = USB_REQ_GET_DESCRIPTOR;
20 if (pipe->speed == USB_SUPERSPEED)
21 req.wValue = USB_DT_HUB3<<8;
23 req.wValue = USB_DT_HUB<<8;
25 req.wLength = sizeof(*desc);
26 return usb_send_default_control(pipe, &req, desc);
30 set_hub_depth(struct usb_pipe *pipe, u16 depth)
32 struct usb_ctrlrequest req;
33 req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE;
34 req.bRequest = HUB_REQ_SET_HUB_DEPTH;
38 return usb_send_default_control(pipe, &req, NULL);
42 set_port_feature(struct usbhub_s *hub, int port, int feature)
44 struct usb_ctrlrequest req;
45 req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
46 req.bRequest = USB_REQ_SET_FEATURE;
48 req.wIndex = port + 1;
50 mutex_lock(&hub->lock);
51 int ret = usb_send_default_control(hub->usbdev->defpipe, &req, NULL);
52 mutex_unlock(&hub->lock);
57 clear_port_feature(struct usbhub_s *hub, int port, int feature)
59 struct usb_ctrlrequest req;
60 req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
61 req.bRequest = USB_REQ_CLEAR_FEATURE;
63 req.wIndex = port + 1;
65 mutex_lock(&hub->lock);
66 int ret = usb_send_default_control(hub->usbdev->defpipe, &req, NULL);
67 mutex_unlock(&hub->lock);
72 get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts)
74 struct usb_ctrlrequest req;
75 req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER;
76 req.bRequest = USB_REQ_GET_STATUS;
78 req.wIndex = port + 1;
79 req.wLength = sizeof(*sts);
80 mutex_lock(&hub->lock);
81 int ret = usb_send_default_control(hub->usbdev->defpipe, &req, sts);
82 mutex_unlock(&hub->lock);
86 // Check if device attached to port
88 usb_hub_detect(struct usbhub_s *hub, u32 port)
90 struct usb_port_status sts;
91 int ret = get_port_status(hub, port, &sts);
93 dprintf(1, "Failure on hub port %d detect\n", port);
96 return (sts.wPortStatus & USB_PORT_STAT_CONNECTION) ? 1 : 0;
101 usb_hub_disconnect(struct usbhub_s *hub, u32 port)
103 int ret = clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE);
105 dprintf(1, "Failure on hub port %d disconnect\n", port);
108 // Reset device on port
110 usb_hub_reset(struct usbhub_s *hub, u32 port)
112 int ret = set_port_feature(hub, port, USB_PORT_FEAT_RESET);
116 // Wait for reset to complete.
117 struct usb_port_status sts;
118 u32 end = timer_calc(USB_TIME_DRST * 2);
120 ret = get_port_status(hub, port, &sts);
123 if (!(sts.wPortStatus & USB_PORT_STAT_RESET)
124 && (hub->usbdev->speed != USB_SUPERSPEED
125 || !(sts.wPortStatus & USB_PORT_STAT_LINK_MASK)))
127 if (timer_check(end)) {
135 if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
136 // Device no longer present
139 if (hub->usbdev->speed == USB_SUPERSPEED)
140 return USB_SUPERSPEED;
141 return ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK)
142 >> USB_PORT_STAT_SPEED_SHIFT);
145 dprintf(1, "Failure on hub port %d reset\n", port);
146 usb_hub_disconnect(hub, port);
150 static struct usbhub_op_s HubOp = {
151 .detect = usb_hub_detect,
152 .reset = usb_hub_reset,
153 .disconnect = usb_hub_disconnect,
156 // Configure a usb hub and then find devices connected to it.
158 usb_hub_setup(struct usbdevice_s *usbdev)
164 struct usb_hub_descriptor desc;
165 int ret = get_hub_desc(usbdev->defpipe, &desc);
170 memset(&hub, 0, sizeof(hub));
172 hub.cntl = usbdev->defpipe->cntl;
173 hub.portcount = desc.bNbrPorts;
176 if (usbdev->speed == USB_SUPERSPEED) {
178 struct usbdevice_s *parent = usbdev->hub->usbdev;
181 parent = parent->hub->usbdev;
184 ret = set_hub_depth(usbdev->defpipe, depth);
189 // Turn on power to ports.
191 for (port=0; port<desc.bNbrPorts; port++) {
192 ret = set_port_feature(&hub, port, USB_PORT_FEAT_POWER);
196 // Wait for port power to stabilize.
197 msleep(desc.bPwrOn2PwrGood * 2);
201 dprintf(1, "Initialized USB HUB (%d ports used)\n", hub.devcount);