source: mainline/uspace/drv/bus/usb/uhci/uhci_rh.c@ c95c00e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c95c00e was c95c00e, checked in by Jan Vesely <jano.vesely@…>, 13 years ago

uhci: Add root hub emulation.

This device uses libusbvirt and si controlled by standard usbhub driver.

  • Property mode set to 100644
File size: 11.4 KB
Line 
1/*
2 * Copyright (c) 2013 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <assert.h>
30#include <macros.h>
31#include <usb/debug.h>
32#include <ddi.h>
33
34#include "uhci_rh.h"
35
36enum {
37 UHCI_RH_PORT_COUNT = 2,
38 /* 1 byte for hub status bit and 2 port status bits */
39 UHCI_PORT_BYTES = 1,
40};
41
42/** Hub descriptor. */
43static const struct {
44 usb_hub_descriptor_header_t header;
45 uint8_t removable[UHCI_PORT_BYTES];
46 uint8_t powered[UHCI_PORT_BYTES];
47} __attribute__((packed)) hub_descriptor = {
48 .header = {
49 .length = sizeof(hub_descriptor),
50 .descriptor_type = USB_DESCTYPE_HUB,
51 .port_count = UHCI_RH_PORT_COUNT,
52 .characteristics =
53 HUB_CHAR_NO_POWER_SWITCH_FLAG | HUB_CHAR_NO_OC_FLAG,
54 .power_good_time = 50,
55 .max_current = 0,
56 },
57 .removable = { 0 },
58 .powered = { 0xFF },
59};
60
61
62static usbvirt_device_ops_t ops;
63
64int uhci_rh_init(uhci_rh_t *instance, ioport16_t *ports)
65{
66 assert(instance);
67 instance->ports[0] = ports;
68 instance->ports[1] = ports + 1;
69 instance->reset_changed[0] = false;
70 instance->reset_changed[1] = false;
71 return virthub_base_init(&instance->base, "uhci_rh", &ops, instance,
72 NULL, &hub_descriptor.header);
73}
74
75int uhci_rh_schedule(uhci_rh_t *instance, usb_transfer_batch_t *batch)
76{
77 assert(instance);
78 assert(batch);
79
80 const usb_target_t target = {{
81 .address = batch->ep->address,
82 .endpoint = batch->ep->endpoint
83 }};
84 batch->error = virthub_base_request(&instance->base, target,
85 usb_transfer_batch_direction(batch),
86 (void*)batch->setup_buffer, batch->buffer,
87 batch->buffer_size, &batch->transfered_size);
88 usb_transfer_batch_finish(batch, NULL);
89 usb_transfer_batch_destroy(batch);
90 return EOK;
91}
92enum {
93 STATUS_CONNECTED = (1 << 0),
94 STATUS_CONNECTED_CHANGED = (1 << 1),
95 STATUS_ENABLED = (1 << 2),
96 STATUS_ENABLED_CHANGED = (1 << 3),
97 STATUS_LINE_D_PLUS = (1 << 4),
98 STATUS_LINE_D_MINUS = (1 << 5),
99 STATUS_RESUME = (1 << 6),
100 STATUS_ALWAYS_ONE = (1 << 7),
101
102 STATUS_LOW_SPEED = (1 << 8),
103 STATUS_IN_RESET = (1 << 9),
104 STATUS_SUSPEND = (1 << 12),
105
106 STATUS_CHANGE_BITS = STATUS_CONNECTED_CHANGED | STATUS_ENABLED_CHANGED,
107 STATUS_WC_BITS = STATUS_CHANGE_BITS,
108};
109/* HUB ROUTINES IMPLEMENTATION */
110static void uhci_port_reset_enable(ioport16_t *port)
111{
112 assert(port);
113 uint16_t port_status = pio_read_16(port);
114 /* We don't wan to remove changes, that's hub drivers work */
115 port_status &= ~STATUS_WC_BITS;
116 port_status |= STATUS_IN_RESET;
117 pio_write_16(port, port_status);
118 async_usleep(50000);
119 port_status = pio_read_16(port);
120 port_status &= ~STATUS_IN_RESET;
121 pio_write_16(port, port_status);
122 while ((port_status = pio_read_16(port)) & STATUS_IN_RESET);
123 /* PIO delay, should not be longer than 3ms as the device might
124 * enter suspend state. */
125 udelay(10);
126 /* Enable the port. */
127 port_status &= ~STATUS_WC_BITS;
128 pio_write_16(port, port_status | STATUS_ENABLED);
129}
130#define TEST_SIZE_INIT(size, port, hub) \
131do { \
132 if (uint16_usb2host(setup_packet->length) != size) \
133 return ESTALL; \
134 port = uint16_usb2host(setup_packet->index) - 1; \
135 if (port != 0 && port != 1) \
136 return EINVAL; \
137 hub = virthub_get_data(device); \
138 assert(hub);\
139} while (0)
140
141static int req_get_port_state(usbvirt_device_t *device,
142 const usb_device_request_setup_packet_t *setup_packet,
143 uint8_t *data, size_t *act_size)
144{
145 uhci_rh_t *hub;
146 unsigned port;
147 TEST_SIZE_INIT(1, port, hub);
148 if (setup_packet->value != 0)
149 return EINVAL;
150
151 const uint16_t value = pio_read_16(hub->ports[port]);
152 data[0] = ((value & STATUS_LINE_D_MINUS) ? 1 : 0)
153 | ((value & STATUS_LINE_D_PLUS) ? 2 : 0);
154 *act_size = 1;
155 return EOK;
156}
157
158#define BIT_VAL(val, bit) \
159 ((val & bit) ? 1 : 0)
160#define UHCI2USB(val, bit, feat) \
161 (BIT_VAL(val, bit) << feat)
162
163static int req_get_port_status(usbvirt_device_t *device,
164 const usb_device_request_setup_packet_t *setup_packet,
165 uint8_t *data, size_t *act_size)
166{
167 uhci_rh_t *hub;
168 unsigned port;
169 TEST_SIZE_INIT(4, port, hub);
170 if (setup_packet->value != 0)
171 return EINVAL;
172
173 const uint16_t val = pio_read_16(hub->ports[port]);
174 const uint32_t status = uint16_usb2host(
175 UHCI2USB(val, STATUS_CONNECTED, USB_HUB_FEATURE_PORT_CONNECTION) |
176 UHCI2USB(val, STATUS_ENABLED, USB_HUB_FEATURE_PORT_ENABLE) |
177 UHCI2USB(val, STATUS_SUSPEND, USB_HUB_FEATURE_PORT_SUSPEND) |
178 UHCI2USB(val, STATUS_IN_RESET, USB_HUB_FEATURE_PORT_RESET) |
179 UHCI2USB(val, STATUS_ALWAYS_ONE, USB_HUB_FEATURE_PORT_POWER) |
180 UHCI2USB(val, STATUS_LOW_SPEED, USB_HUB_FEATURE_PORT_LOW_SPEED) |
181 UHCI2USB(val, STATUS_CONNECTED_CHANGED, USB_HUB_FEATURE_C_PORT_CONNECTION) |
182 UHCI2USB(val, STATUS_ENABLED_CHANGED, USB_HUB_FEATURE_C_PORT_ENABLE) |
183// UHCI2USB(val, STATUS_SUSPEND, USB_HUB_FEATURE_C_PORT_SUSPEND) |
184 ((hub->reset_changed[port] ? 1 : 0) << USB_HUB_FEATURE_C_PORT_RESET)
185 );
186 memcpy(data, &status, sizeof(status));
187 *act_size = sizeof(status);;
188 return EOK;
189}
190
191static int req_clear_port_feature(usbvirt_device_t *device,
192 const usb_device_request_setup_packet_t *setup_packet,
193 uint8_t *data, size_t *act_size)
194{
195 uhci_rh_t *hub;
196 unsigned port;
197 TEST_SIZE_INIT(0, port, hub);
198 const unsigned feature = uint16_usb2host(setup_packet->value);
199 const uint16_t status = pio_read_16(hub->ports[port]) & ~STATUS_WC_BITS;
200 switch (feature) {
201 case USB_HUB_FEATURE_PORT_ENABLE:
202 pio_write_16(hub->ports[port], status & ~STATUS_ENABLED);
203 break;
204 case USB_HUB_FEATURE_PORT_SUSPEND:
205 pio_write_16(hub->ports[port], status & ~STATUS_SUSPEND);
206 // TODO we should do resume magic
207 usb_log_warning("Resume is not implemented on port %u\n", port);
208 break;
209 case USB_HUB_FEATURE_PORT_POWER:
210 /* We are always powered */
211 usb_log_warning("Tried to power off port %u\n", port);
212 break;
213 case USB_HUB_FEATURE_C_PORT_CONNECTION:
214 pio_write_16(hub->ports[port], status | STATUS_CONNECTED_CHANGED);
215 break;
216 case USB_HUB_FEATURE_C_PORT_RESET:
217 hub->reset_changed[port] = 0;
218 break;
219 case USB_HUB_FEATURE_C_PORT_ENABLE:
220 pio_write_16(hub->ports[port], status | STATUS_ENABLED_CHANGED);
221 break;
222 case USB_HUB_FEATURE_C_PORT_SUSPEND:
223 //TODO
224 return ENOTSUP;
225 case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
226 /* UHCI Does not report over current */
227 break;
228 default:
229 return ESTALL;
230 }
231 return EOK;
232}
233
234static int req_set_port_feature(usbvirt_device_t *device,
235 const usb_device_request_setup_packet_t *setup_packet,
236 uint8_t *data, size_t *act_size)
237{
238 uhci_rh_t *hub;
239 unsigned port;
240 TEST_SIZE_INIT(0, port, hub);
241 const unsigned feature = uint16_usb2host(setup_packet->value);
242 const uint16_t status = pio_read_16(hub->ports[port]);
243 switch (feature) {
244 case USB_HUB_FEATURE_PORT_RESET:
245 uhci_port_reset_enable(hub->ports[port]);
246 hub->reset_changed[port] = true;
247 break;
248 case USB_HUB_FEATURE_PORT_SUSPEND:
249 pio_write_16(hub->ports[port],
250 (status & ~STATUS_WC_BITS) | STATUS_SUSPEND);
251 usb_log_warning("Suspend is not implemented on port %u\n", port);
252 break;
253 case USB_HUB_FEATURE_PORT_POWER:
254 /* We are always powered */
255 usb_log_warning("Tried to power port %u\n", port);
256 case USB_HUB_FEATURE_C_PORT_CONNECTION:
257 case USB_HUB_FEATURE_C_PORT_ENABLE:
258 case USB_HUB_FEATURE_C_PORT_SUSPEND:
259 case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
260 /* These are voluntary and don't have to be set
261 * there is no way we could do it on UHCI anyway */
262 break;
263 default:
264 return ESTALL;
265 }
266 return EOK;
267}
268
269static usbvirt_control_request_handler_t control_transfer_handlers[] = {
270 {
271 STD_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
272 .name = "GetDescriptor",
273 .callback = virthub_base_get_hub_descriptor,
274 },
275 {
276 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
277 .name = "GetDescriptor",
278 .callback = virthub_base_get_hub_descriptor,
279 },
280 {
281 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_GET_DESCRIPTOR),
282 .name = "GetHubDescriptor",
283 .callback = virthub_base_get_hub_descriptor,
284 },
285 {
286 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATE),
287 .name = "GetBusState",
288 .callback = req_get_port_state,
289 },
290 {
291 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATUS),
292 .name = "GetPortStatus",
293 .callback = req_get_port_status
294 },
295 {
296 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_CLEAR_FEATURE),
297 .name = "ClearHubFeature",
298 /* Hub features are overcurrent and supply good,
299 * this request may only set changes that we never report*/
300 .callback = req_nop,
301 },
302 {
303 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_CLEAR_FEATURE),
304 .name = "ClearPortFeature",
305 .callback = req_clear_port_feature
306 },
307 {
308 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_GET_STATUS),
309 .name = "GetHubStatus",
310 /* UHCI can't report OC condition or,
311 * lose power source */
312 .callback = virthub_base_get_null_status,
313 },
314 {
315 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATUS),
316 .name = "GetPortStatus",
317 .callback = req_get_port_status
318 },
319 {
320 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_SET_FEATURE),
321 .name = "SetHubFeature",
322 /* Hub features are overcurrent and supply good,
323 * this request may only set changes that we never report*/
324 .callback = req_nop,
325 },
326 {
327 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_SET_FEATURE),
328 .name = "SetPortFeature",
329 .callback = req_set_port_feature
330 },
331 {
332 .callback = NULL
333 }
334};
335
336static int req_status_change_handler(usbvirt_device_t *dev,
337 usb_endpoint_t endpoint, usb_transfer_type_t tr_type,
338 void *buffer, size_t buffer_size, size_t *actual_size);
339
340static usbvirt_device_ops_t ops = {
341 .control = control_transfer_handlers,
342 .data_in[HUB_STATUS_CHANGE_PIPE] = req_status_change_handler,
343};
344
345static int req_status_change_handler(usbvirt_device_t *dev,
346 usb_endpoint_t endpoint, usb_transfer_type_t tr_type,
347 void *buffer, size_t buffer_size, size_t *actual_size)
348{
349 if (buffer_size < 1)
350 return ESTALL;
351 uhci_rh_t *hub = virthub_get_data(dev);
352 assert(hub);
353
354 const uint8_t status =
355 ((((pio_read_16(hub->ports[0]) & STATUS_CHANGE_BITS) != 0)
356 || hub->reset_changed[0]) ? 0x2 : 0) |
357 ((((pio_read_16(hub->ports[1]) & STATUS_CHANGE_BITS) != 0)
358 || hub->reset_changed[1]) ? 0x4 : 0);
359 ((uint8_t *)buffer)[0] = status;
360 *actual_size = 1;
361 return EOK;
362}
Note: See TracBrowser for help on using the repository browser.