blob: 6f1aaa10482494d0f8d4ff605925f34c9ef1d88a [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
11
12static int
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050013get_hub_desc(struct usb_pipe *pipe, struct usb_hub_descriptor *desc)
Kevin O'Connor54671c12010-02-15 18:58:12 -050014{
15 struct usb_ctrlrequest req;
16 req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE;
17 req.bRequest = USB_REQ_GET_DESCRIPTOR;
18 req.wValue = USB_DT_HUB<<8;
19 req.wIndex = 0;
20 req.wLength = sizeof(*desc);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050021 return send_default_control(pipe, &req, desc);
Kevin O'Connor54671c12010-02-15 18:58:12 -050022}
23
24static int
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050025set_port_feature(struct usbhub_s *hub, int port, int feature)
Kevin O'Connor54671c12010-02-15 18:58:12 -050026{
27 struct usb_ctrlrequest req;
28 req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
29 req.bRequest = USB_REQ_SET_FEATURE;
30 req.wValue = feature;
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040031 req.wIndex = port + 1;
Kevin O'Connor54671c12010-02-15 18:58:12 -050032 req.wLength = 0;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050033 mutex_lock(&hub->lock);
Kevin O'Connorea274782012-03-08 07:49:09 -050034 int ret = send_default_control(hub->usbdev->defpipe, &req, NULL);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050035 mutex_unlock(&hub->lock);
36 return ret;
Kevin O'Connor54671c12010-02-15 18:58:12 -050037}
38
39static int
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050040clear_port_feature(struct usbhub_s *hub, int port, int feature)
Kevin O'Connor54671c12010-02-15 18:58:12 -050041{
42 struct usb_ctrlrequest req;
43 req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
44 req.bRequest = USB_REQ_CLEAR_FEATURE;
45 req.wValue = feature;
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040046 req.wIndex = port + 1;
Kevin O'Connor54671c12010-02-15 18:58:12 -050047 req.wLength = 0;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050048 mutex_lock(&hub->lock);
Kevin O'Connorea274782012-03-08 07:49:09 -050049 int ret = send_default_control(hub->usbdev->defpipe, &req, NULL);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050050 mutex_unlock(&hub->lock);
51 return ret;
Kevin O'Connor54671c12010-02-15 18:58:12 -050052}
53
54static int
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050055get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts)
Kevin O'Connor54671c12010-02-15 18:58:12 -050056{
57 struct usb_ctrlrequest req;
58 req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER;
59 req.bRequest = USB_REQ_GET_STATUS;
60 req.wValue = 0;
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040061 req.wIndex = port + 1;
Kevin O'Connor54671c12010-02-15 18:58:12 -050062 req.wLength = sizeof(*sts);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050063 mutex_lock(&hub->lock);
Kevin O'Connorea274782012-03-08 07:49:09 -050064 int ret = send_default_control(hub->usbdev->defpipe, &req, sts);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050065 mutex_unlock(&hub->lock);
66 return ret;
67}
68
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040069// Check if device attached to port
70static int
71usb_hub_detect(struct usbhub_s *hub, u32 port)
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050072{
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050073 // Turn on power to port.
74 int ret = set_port_feature(hub, port, USB_PORT_FEAT_POWER);
75 if (ret)
76 goto fail;
77
78 // Wait for port power to stabilize.
79 msleep(hub->powerwait);
80
81 // Check periodically for a device connect.
82 struct usb_port_status sts;
83 u64 end = calc_future_tsc(USB_TIME_SIGATT);
84 for (;;) {
85 ret = get_port_status(hub, port, &sts);
86 if (ret)
87 goto fail;
88 if (sts.wPortStatus & USB_PORT_STAT_CONNECTION)
89 // Device connected.
90 break;
Kevin O'Connor144817b2010-05-23 10:46:49 -040091 if (check_tsc(end))
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050092 // No device found.
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040093 return -1;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050094 msleep(5);
95 }
96
97 // XXX - wait USB_TIME_ATTDB time?
98
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040099 return 0;
100
101fail:
102 dprintf(1, "Failure on hub port %d detect\n", port);
103 return -1;
104}
105
106// Disable port
107static void
108usb_hub_disconnect(struct usbhub_s *hub, u32 port)
109{
110 int ret = clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500111 if (ret)
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400112 dprintf(1, "Failure on hub port %d disconnect\n", port);
113}
114
115// Reset device on port
116static int
117usb_hub_reset(struct usbhub_s *hub, u32 port)
118{
119 int ret = set_port_feature(hub, port, USB_PORT_FEAT_RESET);
120 if (ret)
121 goto fail;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500122
123 // Wait for reset to complete.
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400124 struct usb_port_status sts;
125 u64 end = calc_future_tsc(USB_TIME_DRST * 2);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500126 for (;;) {
127 ret = get_port_status(hub, port, &sts);
128 if (ret)
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400129 goto fail;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500130 if (!(sts.wPortStatus & USB_PORT_STAT_RESET))
131 break;
Kevin O'Connor144817b2010-05-23 10:46:49 -0400132 if (check_tsc(end)) {
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500133 warn_timeout();
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400134 goto fail;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500135 }
136 msleep(5);
137 }
138
139 // Reset complete.
140 if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
141 // Device no longer present
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400142 return -1;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500143
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400144 return ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK)
145 >> USB_PORT_STAT_SPEED_SHIFT);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500146
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500147fail:
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400148 dprintf(1, "Failure on hub port %d reset\n", port);
149 usb_hub_disconnect(hub, port);
150 return -1;
Kevin O'Connor54671c12010-02-15 18:58:12 -0500151}
152
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400153static struct usbhub_op_s HubOp = {
154 .detect = usb_hub_detect,
155 .reset = usb_hub_reset,
156 .disconnect = usb_hub_disconnect,
157};
158
Kevin O'Connor54671c12010-02-15 18:58:12 -0500159// Configure a usb hub and then find devices connected to it.
160int
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500161usb_hub_init(struct usbdevice_s *usbdev)
Kevin O'Connor54671c12010-02-15 18:58:12 -0500162{
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500163 ASSERT32FLAT();
Kevin O'Connor54671c12010-02-15 18:58:12 -0500164 if (!CONFIG_USB_HUB)
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500165 return -1;
Kevin O'Connor54671c12010-02-15 18:58:12 -0500166
167 struct usb_hub_descriptor desc;
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500168 int ret = get_hub_desc(usbdev->defpipe, &desc);
Kevin O'Connor54671c12010-02-15 18:58:12 -0500169 if (ret)
170 return ret;
171
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500172 struct usbhub_s hub;
173 memset(&hub, 0, sizeof(hub));
Kevin O'Connorea274782012-03-08 07:49:09 -0500174 hub.usbdev = usbdev;
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500175 hub.cntl = usbdev->defpipe->cntl;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500176 hub.powerwait = desc.bPwrOn2PwrGood * 2;
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400177 hub.portcount = desc.bNbrPorts;
178 hub.op = &HubOp;
179 usb_enumerate(&hub);
Kevin O'Connor54671c12010-02-15 18:58:12 -0500180
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500181 dprintf(1, "Initialized USB HUB (%d ports used)\n", hub.devcount);
182 if (hub.devcount)
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500183 return 0;
184 return -1;
Kevin O'Connor54671c12010-02-15 18:58:12 -0500185}