blob: 5bef26fb8ecac9841496765e7f66c0a585d5f365 [file] [log] [blame]
Kevin O'Connor54671c12010-02-15 18:58:12 -05001// Code for handling standard USB hubs.
2//
3// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net>
4//
5// This file may be distributed under the terms of the GNU LGPLv3 license.
6
7#include "util.h" // dprintf
8#include "config.h" // CONFIG_USB_HUB
9#include "usb-hub.h" // struct usb_hub_descriptor
10#include "usb.h" // struct usb_s
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040011#include "string.h" // memset
Kevin O'Connor54671c12010-02-15 18:58:12 -050012
13static int
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050014get_hub_desc(struct usb_pipe *pipe, struct usb_hub_descriptor *desc)
Kevin O'Connor54671c12010-02-15 18:58:12 -050015{
16 struct usb_ctrlrequest req;
17 req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE;
18 req.bRequest = USB_REQ_GET_DESCRIPTOR;
19 req.wValue = USB_DT_HUB<<8;
20 req.wIndex = 0;
21 req.wLength = sizeof(*desc);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050022 return send_default_control(pipe, &req, desc);
Kevin O'Connor54671c12010-02-15 18:58:12 -050023}
24
25static int
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050026set_port_feature(struct usbhub_s *hub, int port, int feature)
Kevin O'Connor54671c12010-02-15 18:58:12 -050027{
28 struct usb_ctrlrequest req;
29 req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
30 req.bRequest = USB_REQ_SET_FEATURE;
31 req.wValue = feature;
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040032 req.wIndex = port + 1;
Kevin O'Connor54671c12010-02-15 18:58:12 -050033 req.wLength = 0;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050034 mutex_lock(&hub->lock);
Kevin O'Connorea274782012-03-08 07:49:09 -050035 int ret = send_default_control(hub->usbdev->defpipe, &req, NULL);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050036 mutex_unlock(&hub->lock);
37 return ret;
Kevin O'Connor54671c12010-02-15 18:58:12 -050038}
39
40static int
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050041clear_port_feature(struct usbhub_s *hub, int port, int feature)
Kevin O'Connor54671c12010-02-15 18:58:12 -050042{
43 struct usb_ctrlrequest req;
44 req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
45 req.bRequest = USB_REQ_CLEAR_FEATURE;
46 req.wValue = feature;
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040047 req.wIndex = port + 1;
Kevin O'Connor54671c12010-02-15 18:58:12 -050048 req.wLength = 0;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050049 mutex_lock(&hub->lock);
Kevin O'Connorea274782012-03-08 07:49:09 -050050 int ret = send_default_control(hub->usbdev->defpipe, &req, NULL);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050051 mutex_unlock(&hub->lock);
52 return ret;
Kevin O'Connor54671c12010-02-15 18:58:12 -050053}
54
55static int
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050056get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts)
Kevin O'Connor54671c12010-02-15 18:58:12 -050057{
58 struct usb_ctrlrequest req;
59 req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER;
60 req.bRequest = USB_REQ_GET_STATUS;
61 req.wValue = 0;
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040062 req.wIndex = port + 1;
Kevin O'Connor54671c12010-02-15 18:58:12 -050063 req.wLength = sizeof(*sts);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050064 mutex_lock(&hub->lock);
Kevin O'Connorea274782012-03-08 07:49:09 -050065 int ret = send_default_control(hub->usbdev->defpipe, &req, sts);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050066 mutex_unlock(&hub->lock);
67 return ret;
68}
69
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040070// Check if device attached to port
71static int
72usb_hub_detect(struct usbhub_s *hub, u32 port)
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050073{
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050074 // Turn on power to port.
75 int ret = set_port_feature(hub, port, USB_PORT_FEAT_POWER);
76 if (ret)
77 goto fail;
78
79 // Wait for port power to stabilize.
80 msleep(hub->powerwait);
81
82 // Check periodically for a device connect.
83 struct usb_port_status sts;
Kevin O'Connor018bdd72013-07-20 18:22:57 -040084 u32 end = timer_calc(USB_TIME_SIGATT);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050085 for (;;) {
86 ret = get_port_status(hub, port, &sts);
87 if (ret)
88 goto fail;
89 if (sts.wPortStatus & USB_PORT_STAT_CONNECTION)
90 // Device connected.
91 break;
Kevin O'Connor018bdd72013-07-20 18:22:57 -040092 if (timer_check(end))
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050093 // No device found.
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040094 return -1;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050095 msleep(5);
96 }
97
98 // XXX - wait USB_TIME_ATTDB time?
99
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400100 return 0;
101
102fail:
103 dprintf(1, "Failure on hub port %d detect\n", port);
104 return -1;
105}
106
107// Disable port
108static void
109usb_hub_disconnect(struct usbhub_s *hub, u32 port)
110{
111 int ret = clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500112 if (ret)
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400113 dprintf(1, "Failure on hub port %d disconnect\n", port);
114}
115
116// Reset device on port
117static int
118usb_hub_reset(struct usbhub_s *hub, u32 port)
119{
120 int ret = set_port_feature(hub, port, USB_PORT_FEAT_RESET);
121 if (ret)
122 goto fail;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500123
124 // Wait for reset to complete.
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400125 struct usb_port_status sts;
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400126 u32 end = timer_calc(USB_TIME_DRST * 2);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500127 for (;;) {
128 ret = get_port_status(hub, port, &sts);
129 if (ret)
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400130 goto fail;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500131 if (!(sts.wPortStatus & USB_PORT_STAT_RESET))
132 break;
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400133 if (timer_check(end)) {
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500134 warn_timeout();
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400135 goto fail;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500136 }
137 msleep(5);
138 }
139
140 // Reset complete.
141 if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
142 // Device no longer present
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400143 return -1;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500144
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400145 return ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK)
146 >> USB_PORT_STAT_SPEED_SHIFT);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500147
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500148fail:
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400149 dprintf(1, "Failure on hub port %d reset\n", port);
150 usb_hub_disconnect(hub, port);
151 return -1;
Kevin O'Connor54671c12010-02-15 18:58:12 -0500152}
153
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400154static struct usbhub_op_s HubOp = {
155 .detect = usb_hub_detect,
156 .reset = usb_hub_reset,
157 .disconnect = usb_hub_disconnect,
158};
159
Kevin O'Connor54671c12010-02-15 18:58:12 -0500160// Configure a usb hub and then find devices connected to it.
161int
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500162usb_hub_setup(struct usbdevice_s *usbdev)
Kevin O'Connor54671c12010-02-15 18:58:12 -0500163{
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500164 ASSERT32FLAT();
Kevin O'Connor54671c12010-02-15 18:58:12 -0500165 if (!CONFIG_USB_HUB)
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500166 return -1;
Kevin O'Connor54671c12010-02-15 18:58:12 -0500167
168 struct usb_hub_descriptor desc;
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500169 int ret = get_hub_desc(usbdev->defpipe, &desc);
Kevin O'Connor54671c12010-02-15 18:58:12 -0500170 if (ret)
171 return ret;
172
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500173 struct usbhub_s hub;
174 memset(&hub, 0, sizeof(hub));
Kevin O'Connorea274782012-03-08 07:49:09 -0500175 hub.usbdev = usbdev;
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500176 hub.cntl = usbdev->defpipe->cntl;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500177 hub.powerwait = desc.bPwrOn2PwrGood * 2;
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400178 hub.portcount = desc.bNbrPorts;
179 hub.op = &HubOp;
180 usb_enumerate(&hub);
Kevin O'Connor54671c12010-02-15 18:58:12 -0500181
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500182 dprintf(1, "Initialized USB HUB (%d ports used)\n", hub.devcount);
183 if (hub.devcount)
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500184 return 0;
185 return -1;
Kevin O'Connor54671c12010-02-15 18:58:12 -0500186}