Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


imported SEABIOS source tree
[palacios.git] / bios / seabios / src / usb-hub.c
1 // 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
12 static int
13 get_hub_desc(struct usb_pipe *pipe, struct usb_hub_descriptor *desc)
14 {
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);
21     return send_default_control(pipe, &req, desc);
22 }
23
24 static int
25 set_port_feature(struct usbhub_s *hub, int port, int feature)
26 {
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;
31     req.wIndex = port + 1;
32     req.wLength = 0;
33     mutex_lock(&hub->lock);
34     int ret = send_default_control(hub->pipe, &req, NULL);
35     mutex_unlock(&hub->lock);
36     return ret;
37 }
38
39 static int
40 clear_port_feature(struct usbhub_s *hub, int port, int feature)
41 {
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;
46     req.wIndex = port + 1;
47     req.wLength = 0;
48     mutex_lock(&hub->lock);
49     int ret = send_default_control(hub->pipe, &req, NULL);
50     mutex_unlock(&hub->lock);
51     return ret;
52 }
53
54 static int
55 get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts)
56 {
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;
61     req.wIndex = port + 1;
62     req.wLength = sizeof(*sts);
63     mutex_lock(&hub->lock);
64     int ret = send_default_control(hub->pipe, &req, sts);
65     mutex_unlock(&hub->lock);
66     return ret;
67 }
68
69 // Check if device attached to port
70 static int
71 usb_hub_detect(struct usbhub_s *hub, u32 port)
72 {
73     // 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;
91         if (check_tsc(end))
92             // No device found.
93             return -1;
94         msleep(5);
95     }
96
97     // XXX - wait USB_TIME_ATTDB time?
98
99     return 0;
100
101 fail:
102     dprintf(1, "Failure on hub port %d detect\n", port);
103     return -1;
104 }
105
106 // Disable port
107 static void
108 usb_hub_disconnect(struct usbhub_s *hub, u32 port)
109 {
110     int ret = clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE);
111     if (ret)
112         dprintf(1, "Failure on hub port %d disconnect\n", port);
113 }
114
115 // Reset device on port
116 static int
117 usb_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;
122
123     // Wait for reset to complete.
124     struct usb_port_status sts;
125     u64 end = calc_future_tsc(USB_TIME_DRST * 2);
126     for (;;) {
127         ret = get_port_status(hub, port, &sts);
128         if (ret)
129             goto fail;
130         if (!(sts.wPortStatus & USB_PORT_STAT_RESET))
131             break;
132         if (check_tsc(end)) {
133             warn_timeout();
134             goto fail;
135         }
136         msleep(5);
137     }
138
139     // Reset complete.
140     if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
141         // Device no longer present
142         return -1;
143
144     return ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK)
145             >> USB_PORT_STAT_SPEED_SHIFT);
146
147 fail:
148     dprintf(1, "Failure on hub port %d reset\n", port);
149     usb_hub_disconnect(hub, port);
150     return -1;
151 }
152
153 static struct usbhub_op_s HubOp = {
154     .detect = usb_hub_detect,
155     .reset = usb_hub_reset,
156     .disconnect = usb_hub_disconnect,
157 };
158
159 // Configure a usb hub and then find devices connected to it.
160 int
161 usb_hub_init(struct usb_pipe *pipe)
162 {
163     ASSERT32FLAT();
164     if (!CONFIG_USB_HUB)
165         return -1;
166
167     struct usb_hub_descriptor desc;
168     int ret = get_hub_desc(pipe, &desc);
169     if (ret)
170         return ret;
171
172     struct usbhub_s hub;
173     memset(&hub, 0, sizeof(hub));
174     hub.pipe = pipe;
175     hub.cntl = pipe->cntl;
176     hub.powerwait = desc.bPwrOn2PwrGood * 2;
177     hub.portcount = desc.bNbrPorts;
178     hub.op = &HubOp;
179     usb_enumerate(&hub);
180
181     dprintf(1, "Initialized USB HUB (%d ports used)\n", hub.devcount);
182     if (hub.devcount)
183         return 0;
184     return -1;
185 }