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

Last change on this file was bd41ac52, checked in by Jakub Jermar <jakub@…>, 7 years ago

Get rid of sys/time.h

This commit moves the POSIX-like time functionality from libc's
sys/time.h to libposix and introduces C11-like or HelenOS-specific
interfaces to libc.

Specifically, use of sys/time.h, struct timeval, suseconds_t and
gettimeofday is replaced by time.h (C11), struct timespec (C11), usec_t
(HelenOS) and getuptime / getrealtime (HelenOS).

Also attempt to fix the implementation of clock() to return microseconds
(clocks) rather than processor cycles and move it to libc.

  • Property mode set to 100644
File size: 16.5 KB
Line 
1/*
2 * Copyright (c) 2013 Jan Vesely
3 * Copyright (c) 2018 Ondrej Hlavaty
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <assert.h>
31#include <async.h>
32#include <ddi.h>
33#include <errno.h>
34#include <macros.h>
35#include <mem.h>
36#include <time.h>
37
38#include <usb/debug.h>
39#include <usb/descriptor.h>
40#include <usb/classes/hub.h>
41#include <usb/request.h>
42#include <usb/host/endpoint.h>
43#include <usb/usb.h>
44
45#include "uhci_rh.h"
46
47enum {
48 UHCI_RH_PORT_COUNT = 2,
49 UHCI_PORT_BYTES = STATUS_BYTES(UHCI_RH_PORT_COUNT),
50};
51
52/** Hub descriptor. */
53static const struct {
54 /** Common hub descriptor header */
55 usb_hub_descriptor_header_t header;
56 /** Port removable status bits */
57 uint8_t removable[UHCI_PORT_BYTES];
58 /** Port powered status bits */
59 uint8_t powered[UHCI_PORT_BYTES];
60} __attribute__((packed)) hub_descriptor = {
61 .header = {
62 .length = sizeof(hub_descriptor),
63 .descriptor_type = USB_DESCTYPE_HUB,
64 .port_count = UHCI_RH_PORT_COUNT,
65 .characteristics =
66 HUB_CHAR_NO_POWER_SWITCH_FLAG | HUB_CHAR_NO_OC_FLAG,
67 .power_good_time = 50,
68 .max_current = 0,
69 },
70 .removable = { 0 },
71 .powered = { 0xFF },
72};
73
74static usbvirt_device_ops_t ops;
75
76/** Initialize uhci rh structure.
77 * @param instance Memory place to initialize.
78 * @param ports Pointer to TWO UHCI RH port registers.
79 * @param name device name, passed to virthub init
80 * @return Error code, EOK on success.
81 */
82errno_t uhci_rh_init(uhci_rh_t *instance, ioport16_t *ports, const char *name)
83{
84 assert(instance);
85 instance->ports[0] = ports;
86 instance->ports[1] = ports + 1;
87 instance->reset_changed[0] = false;
88 instance->reset_changed[1] = false;
89 return virthub_base_init(&instance->base, name, &ops, instance,
90 NULL, &hub_descriptor.header, HUB_STATUS_CHANGE_PIPE);
91}
92
93/** Schedule USB batch for the root hub.
94 *
95 * @param instance UHCI rh instance
96 * @param batch USB communication batch
97 * @return EOK.
98 *
99 * The result of scheduling is always EOK. The result of communication does
100 * not have to be.
101 */
102errno_t uhci_rh_schedule(uhci_rh_t *instance, usb_transfer_batch_t *batch)
103{
104 assert(instance);
105 assert(batch);
106
107 do {
108 batch->error = virthub_base_request(&instance->base, batch->target,
109 batch->dir, (void *) batch->setup.buffer,
110 batch->dma_buffer.virt, batch->size, &batch->transferred_size);
111 if (batch->error == ENAK)
112 fibril_usleep(instance->base.endpoint_descriptor.poll_interval * 1000);
113 //TODO This is flimsy, but we can't exit early because
114 //ENAK is technically an error condition
115 } while (batch->error == ENAK);
116 usb_transfer_batch_finish(batch);
117 return EOK;
118}
119
120/** UHCI port register bits */
121enum {
122 STATUS_CONNECTED = (1 << 0),
123 STATUS_CONNECTED_CHANGED = (1 << 1),
124 STATUS_ENABLED = (1 << 2),
125 STATUS_ENABLED_CHANGED = (1 << 3),
126 STATUS_LINE_D_PLUS = (1 << 4),
127 STATUS_LINE_D_MINUS = (1 << 5),
128 STATUS_RESUME = (1 << 6),
129 STATUS_ALWAYS_ONE = (1 << 7),
130
131 STATUS_LOW_SPEED = (1 << 8),
132 STATUS_IN_RESET = (1 << 9),
133 STATUS_SUSPEND = (1 << 12),
134
135 STATUS_CHANGE_BITS = STATUS_CONNECTED_CHANGED | STATUS_ENABLED_CHANGED,
136 STATUS_WC_BITS = STATUS_CHANGE_BITS,
137};
138
139/* HUB ROUTINES IMPLEMENTATION */
140
141static void uhci_port_reset_enable(ioport16_t *port)
142{
143 assert(port);
144 uint16_t port_status = pio_read_16(port);
145 /* We don't wan to remove changes, that's hub drivers work */
146 port_status &= ~STATUS_WC_BITS;
147 port_status |= STATUS_IN_RESET;
148 pio_write_16(port, port_status);
149 fibril_usleep(50000);
150 port_status = pio_read_16(port);
151 port_status &= ~STATUS_IN_RESET;
152 pio_write_16(port, port_status);
153 while ((port_status = pio_read_16(port)) & STATUS_IN_RESET)
154 ;
155 /*
156 * PIO delay, should not be longer than 3ms as the device might
157 * enter suspend state.
158 */
159 udelay(10);
160 /*
161 * Drop ConnectionChange as some UHCI hw
162 * sets this bit after reset, that is incorrect
163 */
164 port_status &= ~STATUS_WC_BITS;
165 pio_write_16(port, port_status | STATUS_ENABLED | STATUS_CONNECTED_CHANGED);
166}
167#define TEST_SIZE_INIT(size, port, hub) \
168do { \
169 if (uint16_usb2host(setup_packet->length) != size) \
170 return ESTALL; \
171 port = uint16_usb2host(setup_packet->index) - 1; \
172 if (port != 0 && port != 1) \
173 return EINVAL; \
174 hub = virthub_get_data(device); \
175 assert(hub);\
176} while (0)
177
178#define RH_DEBUG(hub, port, msg, ...) \
179do { \
180 if ((int)port >= 0) \
181 usb_log_debug("RH(%p-%d): " msg, hub, port, ##__VA_ARGS__); \
182 else \
183 usb_log_debug("RH(%p):" msg, hub, ##__VA_ARGS__); \
184} while (0)
185
186/** USB HUB port state request handler.
187 * @param device Virtual hub device
188 * @param setup_packet USB setup stage data.
189 * @param[out] data destination data buffer, size must be at least
190 * setup_packet->length bytes
191 * @param[out] act_size Sized of the valid response part of the buffer.
192 * @return Error code.
193 *
194 * Do not confuse with port status. Port state reports data line states,
195 * it is usefull for debuging purposes only.
196 */
197static errno_t req_get_port_state(usbvirt_device_t *device,
198 const usb_device_request_setup_packet_t *setup_packet,
199 uint8_t *data, size_t *act_size)
200{
201 uhci_rh_t *hub;
202 unsigned port;
203 TEST_SIZE_INIT(1, port, hub);
204 if (setup_packet->value != 0)
205 return EINVAL;
206
207 const uint16_t value = pio_read_16(hub->ports[port]);
208 data[0] = ((value & STATUS_LINE_D_MINUS) ? 1 : 0) |
209 ((value & STATUS_LINE_D_PLUS) ? 2 : 0);
210 RH_DEBUG(hub, port, "Bus state %" PRIx8 "(source %" PRIx16 ")",
211 data[0], value);
212 *act_size = 1;
213 return EOK;
214}
215
216#define BIT_VAL(val, bit) \
217 ((val & bit) ? 1 : 0)
218#define UHCI2USB(val, bit, mask) \
219 (BIT_VAL(val, bit) ? (mask) : 0)
220
221/** Port status request handler.
222 * @param device Virtual hub device
223 * @param setup_packet USB setup stage data.
224 * @param[out] data destination data buffer, size must be at least
225 * setup_packet->length bytes
226 * @param[out] act_size Sized of the valid response part of the buffer.
227 * @return Error code.
228 *
229 * Converts status reported via ioport to USB format.
230 * @note: reset change status needs to be handled in sw.
231 */
232static errno_t req_get_port_status(usbvirt_device_t *device,
233 const usb_device_request_setup_packet_t *setup_packet,
234 uint8_t *data, size_t *act_size)
235{
236 uhci_rh_t *hub;
237 unsigned port;
238 TEST_SIZE_INIT(4, port, hub);
239 if (setup_packet->value != 0)
240 return EINVAL;
241
242 const uint16_t val = pio_read_16(hub->ports[port]);
243 const uint32_t status = uint32_host2usb(
244 UHCI2USB(val, STATUS_CONNECTED, USB_HUB_PORT_STATUS_CONNECTION) |
245 UHCI2USB(val, STATUS_ENABLED, USB_HUB_PORT_STATUS_ENABLE) |
246 UHCI2USB(val, STATUS_SUSPEND, USB2_HUB_PORT_STATUS_SUSPEND) |
247 UHCI2USB(val, STATUS_IN_RESET, USB_HUB_PORT_STATUS_RESET) |
248 UHCI2USB(val, STATUS_ALWAYS_ONE, USB2_HUB_PORT_STATUS_POWER) |
249 UHCI2USB(val, STATUS_LOW_SPEED, USB2_HUB_PORT_STATUS_LOW_SPEED) |
250 UHCI2USB(val, STATUS_CONNECTED_CHANGED, USB_HUB_PORT_STATUS_C_CONNECTION) |
251 UHCI2USB(val, STATUS_ENABLED_CHANGED, USB2_HUB_PORT_STATUS_C_ENABLE) |
252#if 0
253 UHCI2USB(val, STATUS_SUSPEND, USB2_HUB_PORT_STATUS_C_SUSPEND) |
254#endif
255 (hub->reset_changed[port] ? USB_HUB_PORT_STATUS_C_RESET : 0));
256 RH_DEBUG(hub, port, "Port status %" PRIx32 " (source %" PRIx16
257 "%s)", uint32_usb2host(status), val,
258 hub->reset_changed[port] ? "-reset" : "");
259 memcpy(data, &status, sizeof(status));
260 *act_size = sizeof(status);
261 return EOK;
262}
263
264/** Port clear feature request handler.
265 * @param device Virtual hub device
266 * @param setup_packet USB setup stage data.
267 * @param[out] data destination data buffer, size must be at least
268 * setup_packet->length bytes
269 * @param[out] act_size Sized of the valid response part of the buffer.
270 * @return Error code.
271 */
272static errno_t req_clear_port_feature(usbvirt_device_t *device,
273 const usb_device_request_setup_packet_t *setup_packet,
274 uint8_t *data, size_t *act_size)
275{
276 uhci_rh_t *hub;
277 unsigned port;
278 TEST_SIZE_INIT(0, port, hub);
279 const unsigned feature = uint16_usb2host(setup_packet->value);
280 const uint16_t status = pio_read_16(hub->ports[port]);
281 const uint16_t val = status & (~STATUS_WC_BITS);
282 switch (feature) {
283 case USB2_HUB_FEATURE_PORT_ENABLE:
284 RH_DEBUG(hub, port, "Clear port enable (status %"
285 PRIx16 ")", status);
286 pio_write_16(hub->ports[port], val & ~STATUS_ENABLED);
287 break;
288 case USB2_HUB_FEATURE_PORT_SUSPEND:
289 RH_DEBUG(hub, port, "Clear port suspend (status %"
290 PRIx16 ")", status);
291 pio_write_16(hub->ports[port], val & ~STATUS_SUSPEND);
292 // TODO we should do resume magic
293 usb_log_warning("Resume is not implemented on port %u", port);
294 break;
295 case USB_HUB_FEATURE_PORT_POWER:
296 RH_DEBUG(hub, port, "Clear port power (status %" PRIx16 ")",
297 status);
298 /* We are always powered */
299 usb_log_warning("Tried to power off port %u", port);
300 break;
301 case USB_HUB_FEATURE_C_PORT_CONNECTION:
302 RH_DEBUG(hub, port, "Clear port conn change (status %"
303 PRIx16 ")", status);
304 pio_write_16(hub->ports[port], val | STATUS_CONNECTED_CHANGED);
305 break;
306 case USB_HUB_FEATURE_C_PORT_RESET:
307 RH_DEBUG(hub, port, "Clear port reset change (status %"
308 PRIx16 ")", status);
309 hub->reset_changed[port] = false;
310 break;
311 case USB2_HUB_FEATURE_C_PORT_ENABLE:
312 RH_DEBUG(hub, port, "Clear port enable change (status %"
313 PRIx16 ")", status);
314 pio_write_16(hub->ports[port], status | STATUS_ENABLED_CHANGED);
315 break;
316 case USB2_HUB_FEATURE_C_PORT_SUSPEND:
317 RH_DEBUG(hub, port, "Clear port suspend change (status %"
318 PRIx16 ")", status);
319 //TODO
320 return ENOTSUP;
321 case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
322 RH_DEBUG(hub, port, "Clear port OC change (status %"
323 PRIx16 ")", status);
324 /* UHCI Does not report over current */
325 //TODO: newer chips do, but some have broken wiring
326 break;
327 default:
328 RH_DEBUG(hub, port, "Clear unknown feature %d (status %"
329 PRIx16 ")", feature, status);
330 usb_log_warning("Clearing feature %d is unsupported",
331 feature);
332 return ESTALL;
333 }
334 return EOK;
335}
336
337/** Port set feature request handler.
338 * @param device Virtual hub device
339 * @param setup_packet USB setup stage data.
340 * @param[out] data destination data buffer, size must be at least
341 * setup_packet->length bytes
342 * @param[out] act_size Sized of the valid response part of the buffer.
343 * @return Error code.
344 */
345static errno_t req_set_port_feature(usbvirt_device_t *device,
346 const usb_device_request_setup_packet_t *setup_packet,
347 uint8_t *data, size_t *act_size)
348{
349 uhci_rh_t *hub;
350 unsigned port;
351 TEST_SIZE_INIT(0, port, hub);
352 const unsigned feature = uint16_usb2host(setup_packet->value);
353 const uint16_t status = pio_read_16(hub->ports[port]);
354 switch (feature) {
355 case USB_HUB_FEATURE_PORT_RESET:
356 RH_DEBUG(hub, port, "Set port reset before (status %" PRIx16
357 ")", status);
358 uhci_port_reset_enable(hub->ports[port]);
359 hub->reset_changed[port] = true;
360 RH_DEBUG(hub, port, "Set port reset after (status %" PRIx16
361 ")", pio_read_16(hub->ports[port]));
362 break;
363 case USB2_HUB_FEATURE_PORT_SUSPEND:
364 RH_DEBUG(hub, port, "Set port suspend (status %" PRIx16
365 ")", status);
366 pio_write_16(hub->ports[port],
367 (status & ~STATUS_WC_BITS) | STATUS_SUSPEND);
368 usb_log_warning("Suspend is not implemented on port %u", port);
369 break;
370 case USB_HUB_FEATURE_PORT_POWER:
371 RH_DEBUG(hub, port, "Set port power (status %" PRIx16
372 ")", status);
373 /* We are always powered */
374 usb_log_warning("Tried to power port %u", port);
375 break;
376 case USB_HUB_FEATURE_C_PORT_CONNECTION:
377 case USB2_HUB_FEATURE_C_PORT_ENABLE:
378 case USB2_HUB_FEATURE_C_PORT_SUSPEND:
379 case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
380 RH_DEBUG(hub, port, "Set port change flag (status %" PRIx16
381 ")", status);
382 /*
383 * These are voluntary and don't have to be set
384 * there is no way we could do it on UHCI anyway
385 */
386 break;
387 default:
388 RH_DEBUG(hub, port, "Set unknown feature %d (status %" PRIx16
389 ")", feature, status);
390 usb_log_warning("Setting feature %d is unsupported",
391 feature);
392 return ESTALL;
393 }
394 return EOK;
395}
396
397/** Status change handler.
398 * @param device Virtual hub device
399 * @param endpoint Endpoint number
400 * @param tr_type Transfer type
401 * @param buffer Response destination
402 * @param buffer_size Bytes available in buffer
403 * @param actual_size Size us the used part of the dest buffer.
404 *
405 * Produces status mask. Bit 0 indicates hub status change the other bits
406 * represent port status change. Endian does not matter as UHCI root hubs
407 * only need 1 byte.
408 */
409static errno_t req_status_change_handler(usbvirt_device_t *device,
410 usb_endpoint_t endpoint, usb_transfer_type_t tr_type,
411 void *buffer, size_t buffer_size, size_t *actual_size)
412{
413 uhci_rh_t *hub = virthub_get_data(device);
414 assert(hub);
415
416 if (buffer_size < 1)
417 return ESTALL;
418
419 const uint16_t status_a = pio_read_16(hub->ports[0]);
420 const uint16_t status_b = pio_read_16(hub->ports[1]);
421 const uint8_t status =
422 ((((status_a & STATUS_CHANGE_BITS) != 0) || hub->reset_changed[0]) ?
423 0x2 : 0) |
424 ((((status_b & STATUS_CHANGE_BITS) != 0) || hub->reset_changed[1]) ?
425 0x4 : 0);
426 if (status)
427 RH_DEBUG(hub, -1, "Event mask %" PRIx8
428 " (status_a %" PRIx16 "%s),"
429 " (status_b %" PRIx16 "%s)", status,
430 status_a, hub->reset_changed[0] ? "-reset" : "",
431 status_b, hub->reset_changed[1] ? "-reset" : "");
432 ((uint8_t *)buffer)[0] = status;
433 *actual_size = 1;
434 return (status != 0 ? EOK : ENAK);
435}
436
437/** UHCI root hub request handlers */
438static const usbvirt_control_request_handler_t control_transfer_handlers[] = {
439 {
440 STD_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
441 .name = "GetDescriptor",
442 .callback = virthub_base_get_hub_descriptor,
443 },
444 {
445 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
446 .name = "GetDescriptor",
447 .callback = virthub_base_get_hub_descriptor,
448 },
449 {
450 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_GET_DESCRIPTOR),
451 .name = "GetHubDescriptor",
452 .callback = virthub_base_get_hub_descriptor,
453 },
454 {
455 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATE),
456 .name = "GetBusState",
457 .callback = req_get_port_state,
458 },
459 {
460 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATUS),
461 .name = "GetPortStatus",
462 .callback = req_get_port_status
463 },
464 {
465 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_CLEAR_FEATURE),
466 .name = "ClearHubFeature",
467 /*
468 * Hub features are overcurrent and supply good,
469 * this request may only clear changes that we never report
470 */
471 .callback = req_nop,
472 },
473 {
474 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_CLEAR_FEATURE),
475 .name = "ClearPortFeature",
476 .callback = req_clear_port_feature
477 },
478 {
479 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_GET_STATUS),
480 .name = "GetHubStatus",
481 /*
482 * UHCI can't report OC condition or,
483 * lose power source
484 */
485 .callback = virthub_base_get_null_status,
486 },
487 {
488 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATUS),
489 .name = "GetPortStatus",
490 .callback = req_get_port_status
491 },
492 {
493 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_SET_FEATURE),
494 .name = "SetHubFeature",
495 /*
496 * Hub features are overcurrent and supply good,
497 * this request may only set changes that we never report
498 */
499 .callback = req_nop,
500 },
501 {
502 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_SET_FEATURE),
503 .name = "SetPortFeature",
504 .callback = req_set_port_feature
505 },
506 {
507 .callback = NULL
508 }
509};
510
511static usbvirt_device_ops_t ops = {
512 .control = control_transfer_handlers,
513 .data_in[HUB_STATUS_CHANGE_PIPE] = req_status_change_handler,
514};
Note: See TracBrowser for help on using the repository browser.