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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3061bc1 was e0a5d4c, checked in by Ondřej Hlavatý <aearsis@…>, 7 years ago

usb: update copyrights

The data was generated by a script, guided manually. If you feel your
name is missing somewhere, please add it!

The semi-automated process was roughly:

1) Changes per file and author (limited to our team) were counted
2) Trivial numbers were thrown away
3) Authors were sorted by lines added to file
4) All previous copyrights were replaced by the newly generated one
5) Hunks changing only year were discarded

It seems that a lot of my copyrights were added. It is due to me being
both sticking my nose everywhere and lazy to update the copyright right
away :)

  • 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 <sys/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 async_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 async_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 /* PIO delay, should not be longer than 3ms as the device might
155 * enter suspend state. */
156 udelay(10);
157 /* Drop ConnectionChange as some UHCI hw
158 * sets this bit after reset, that is incorrect */
159 port_status &= ~STATUS_WC_BITS;
160 pio_write_16(port, port_status | STATUS_ENABLED | STATUS_CONNECTED_CHANGED);
161}
162#define TEST_SIZE_INIT(size, port, hub) \
163do { \
164 if (uint16_usb2host(setup_packet->length) != size) \
165 return ESTALL; \
166 port = uint16_usb2host(setup_packet->index) - 1; \
167 if (port != 0 && port != 1) \
168 return EINVAL; \
169 hub = virthub_get_data(device); \
170 assert(hub);\
171} while (0)
172
173#define RH_DEBUG(hub, port, msg, ...) \
174do { \
175 if ((int)port >= 0) \
176 usb_log_debug("RH(%p-%d): " msg, hub, port, ##__VA_ARGS__); \
177 else \
178 usb_log_debug("RH(%p):" msg, hub, ##__VA_ARGS__); \
179} while (0)
180
181/** USB HUB port state request handler.
182 * @param device Virtual hub device
183 * @param setup_packet USB setup stage data.
184 * @param[out] data destination data buffer, size must be at least
185 * setup_packet->length bytes
186 * @param[out] act_size Sized of the valid response part of the buffer.
187 * @return Error code.
188 *
189 * Do not confuse with port status. Port state reports data line states,
190 * it is usefull for debuging purposes only.
191 */
192static errno_t req_get_port_state(usbvirt_device_t *device,
193 const usb_device_request_setup_packet_t *setup_packet,
194 uint8_t *data, size_t *act_size)
195{
196 uhci_rh_t *hub;
197 unsigned port;
198 TEST_SIZE_INIT(1, port, hub);
199 if (setup_packet->value != 0)
200 return EINVAL;
201
202 const uint16_t value = pio_read_16(hub->ports[port]);
203 data[0] = ((value & STATUS_LINE_D_MINUS) ? 1 : 0)
204 | ((value & STATUS_LINE_D_PLUS) ? 2 : 0);
205 RH_DEBUG(hub, port, "Bus state %" PRIx8 "(source %" PRIx16")",
206 data[0], value);
207 *act_size = 1;
208 return EOK;
209}
210
211#define BIT_VAL(val, bit) \
212 ((val & bit) ? 1 : 0)
213#define UHCI2USB(val, bit, mask) \
214 (BIT_VAL(val, bit) ? (mask) : 0)
215
216/** Port status request handler.
217 * @param device Virtual hub device
218 * @param setup_packet USB setup stage data.
219 * @param[out] data destination data buffer, size must be at least
220 * setup_packet->length bytes
221 * @param[out] act_size Sized of the valid response part of the buffer.
222 * @return Error code.
223 *
224 * Converts status reported via ioport to USB format.
225 * @note: reset change status needs to be handled in sw.
226 */
227static errno_t req_get_port_status(usbvirt_device_t *device,
228 const usb_device_request_setup_packet_t *setup_packet,
229 uint8_t *data, size_t *act_size)
230{
231 uhci_rh_t *hub;
232 unsigned port;
233 TEST_SIZE_INIT(4, port, hub);
234 if (setup_packet->value != 0)
235 return EINVAL;
236
237 const uint16_t val = pio_read_16(hub->ports[port]);
238 const uint32_t status = uint32_host2usb(
239 UHCI2USB(val, STATUS_CONNECTED, USB_HUB_PORT_STATUS_CONNECTION) |
240 UHCI2USB(val, STATUS_ENABLED, USB_HUB_PORT_STATUS_ENABLE) |
241 UHCI2USB(val, STATUS_SUSPEND, USB2_HUB_PORT_STATUS_SUSPEND) |
242 UHCI2USB(val, STATUS_IN_RESET, USB_HUB_PORT_STATUS_RESET) |
243 UHCI2USB(val, STATUS_ALWAYS_ONE, USB2_HUB_PORT_STATUS_POWER) |
244 UHCI2USB(val, STATUS_LOW_SPEED, USB2_HUB_PORT_STATUS_LOW_SPEED) |
245 UHCI2USB(val, STATUS_CONNECTED_CHANGED, USB_HUB_PORT_STATUS_C_CONNECTION) |
246 UHCI2USB(val, STATUS_ENABLED_CHANGED, USB2_HUB_PORT_STATUS_C_ENABLE) |
247// UHCI2USB(val, STATUS_SUSPEND, USB2_HUB_PORT_STATUS_C_SUSPEND) |
248 (hub->reset_changed[port] ? USB_HUB_PORT_STATUS_C_RESET : 0)
249 );
250 RH_DEBUG(hub, port, "Port status %" PRIx32 " (source %" PRIx16
251 "%s)", uint32_usb2host(status), val,
252 hub->reset_changed[port] ? "-reset" : "");
253 memcpy(data, &status, sizeof(status));
254 *act_size = sizeof(status);;
255 return EOK;
256}
257
258/** Port clear feature request handler.
259 * @param device Virtual hub device
260 * @param setup_packet USB setup stage data.
261 * @param[out] data destination data buffer, size must be at least
262 * setup_packet->length bytes
263 * @param[out] act_size Sized of the valid response part of the buffer.
264 * @return Error code.
265 */
266static errno_t req_clear_port_feature(usbvirt_device_t *device,
267 const usb_device_request_setup_packet_t *setup_packet,
268 uint8_t *data, size_t *act_size)
269{
270 uhci_rh_t *hub;
271 unsigned port;
272 TEST_SIZE_INIT(0, port, hub);
273 const unsigned feature = uint16_usb2host(setup_packet->value);
274 const uint16_t status = pio_read_16(hub->ports[port]);
275 const uint16_t val = status & (~STATUS_WC_BITS);
276 switch (feature) {
277 case USB2_HUB_FEATURE_PORT_ENABLE:
278 RH_DEBUG(hub, port, "Clear port enable (status %"
279 PRIx16 ")", status);
280 pio_write_16(hub->ports[port], val & ~STATUS_ENABLED);
281 break;
282 case USB2_HUB_FEATURE_PORT_SUSPEND:
283 RH_DEBUG(hub, port, "Clear port suspend (status %"
284 PRIx16 ")", status);
285 pio_write_16(hub->ports[port], val & ~STATUS_SUSPEND);
286 // TODO we should do resume magic
287 usb_log_warning("Resume is not implemented on port %u", port);
288 break;
289 case USB_HUB_FEATURE_PORT_POWER:
290 RH_DEBUG(hub, port, "Clear port power (status %" PRIx16 ")",
291 status);
292 /* We are always powered */
293 usb_log_warning("Tried to power off port %u", port);
294 break;
295 case USB_HUB_FEATURE_C_PORT_CONNECTION:
296 RH_DEBUG(hub, port, "Clear port conn change (status %"
297 PRIx16 ")", status);
298 pio_write_16(hub->ports[port], val | STATUS_CONNECTED_CHANGED);
299 break;
300 case USB_HUB_FEATURE_C_PORT_RESET:
301 RH_DEBUG(hub, port, "Clear port reset change (status %"
302 PRIx16 ")", status);
303 hub->reset_changed[port] = false;
304 break;
305 case USB2_HUB_FEATURE_C_PORT_ENABLE:
306 RH_DEBUG(hub, port, "Clear port enable change (status %"
307 PRIx16 ")", status);
308 pio_write_16(hub->ports[port], status | STATUS_ENABLED_CHANGED);
309 break;
310 case USB2_HUB_FEATURE_C_PORT_SUSPEND:
311 RH_DEBUG(hub, port, "Clear port suspend change (status %"
312 PRIx16 ")", status);
313 //TODO
314 return ENOTSUP;
315 case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
316 RH_DEBUG(hub, port, "Clear port OC change (status %"
317 PRIx16 ")", status);
318 /* UHCI Does not report over current */
319 //TODO: newer chips do, but some have broken wiring
320 break;
321 default:
322 RH_DEBUG(hub, port, "Clear unknown feature %d (status %"
323 PRIx16 ")", feature, status);
324 usb_log_warning("Clearing feature %d is unsupported",
325 feature);
326 return ESTALL;
327 }
328 return EOK;
329}
330
331/** Port set feature request handler.
332 * @param device Virtual hub device
333 * @param setup_packet USB setup stage data.
334 * @param[out] data destination data buffer, size must be at least
335 * setup_packet->length bytes
336 * @param[out] act_size Sized of the valid response part of the buffer.
337 * @return Error code.
338 */
339static errno_t req_set_port_feature(usbvirt_device_t *device,
340 const usb_device_request_setup_packet_t *setup_packet,
341 uint8_t *data, size_t *act_size)
342{
343 uhci_rh_t *hub;
344 unsigned port;
345 TEST_SIZE_INIT(0, port, hub);
346 const unsigned feature = uint16_usb2host(setup_packet->value);
347 const uint16_t status = pio_read_16(hub->ports[port]);
348 switch (feature) {
349 case USB_HUB_FEATURE_PORT_RESET:
350 RH_DEBUG(hub, port, "Set port reset before (status %" PRIx16
351 ")", status);
352 uhci_port_reset_enable(hub->ports[port]);
353 hub->reset_changed[port] = true;
354 RH_DEBUG(hub, port, "Set port reset after (status %" PRIx16
355 ")", pio_read_16(hub->ports[port]));
356 break;
357 case USB2_HUB_FEATURE_PORT_SUSPEND:
358 RH_DEBUG(hub, port, "Set port suspend (status %" PRIx16
359 ")", status);
360 pio_write_16(hub->ports[port],
361 (status & ~STATUS_WC_BITS) | STATUS_SUSPEND);
362 usb_log_warning("Suspend is not implemented on port %u", port);
363 break;
364 case USB_HUB_FEATURE_PORT_POWER:
365 RH_DEBUG(hub, port, "Set port power (status %" PRIx16
366 ")", status);
367 /* We are always powered */
368 usb_log_warning("Tried to power port %u", port);
369 break;
370 case USB_HUB_FEATURE_C_PORT_CONNECTION:
371 case USB2_HUB_FEATURE_C_PORT_ENABLE:
372 case USB2_HUB_FEATURE_C_PORT_SUSPEND:
373 case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
374 RH_DEBUG(hub, port, "Set port change flag (status %" PRIx16
375 ")", status);
376 /* These are voluntary and don't have to be set
377 * there is no way we could do it on UHCI anyway */
378 break;
379 default:
380 RH_DEBUG(hub, port, "Set unknown feature %d (status %" PRIx16
381 ")", feature, status);
382 usb_log_warning("Setting feature %d is unsupported",
383 feature);
384 return ESTALL;
385 }
386 return EOK;
387}
388
389/** Status change handler.
390 * @param device Virtual hub device
391 * @param endpoint Endpoint number
392 * @param tr_type Transfer type
393 * @param buffer Response destination
394 * @param buffer_size Bytes available in buffer
395 * @param actual_size Size us the used part of the dest buffer.
396 *
397 * Produces status mask. Bit 0 indicates hub status change the other bits
398 * represent port status change. Endian does not matter as UHCI root hubs
399 * only need 1 byte.
400 */
401static errno_t req_status_change_handler(usbvirt_device_t *device,
402 usb_endpoint_t endpoint, usb_transfer_type_t tr_type,
403 void *buffer, size_t buffer_size, size_t *actual_size)
404{
405 uhci_rh_t *hub = virthub_get_data(device);
406 assert(hub);
407
408 if (buffer_size < 1)
409 return ESTALL;
410
411 const uint16_t status_a = pio_read_16(hub->ports[0]);
412 const uint16_t status_b = pio_read_16(hub->ports[1]);
413 const uint8_t status =
414 ((((status_a & STATUS_CHANGE_BITS) != 0) || hub->reset_changed[0]) ?
415 0x2 : 0) |
416 ((((status_b & STATUS_CHANGE_BITS) != 0) || hub->reset_changed[1]) ?
417 0x4 : 0);
418 if (status)
419 RH_DEBUG(hub, -1, "Event mask %" PRIx8
420 " (status_a %" PRIx16 "%s),"
421 " (status_b %" PRIx16 "%s)", status,
422 status_a, hub->reset_changed[0] ? "-reset" : "",
423 status_b, hub->reset_changed[1] ? "-reset" : "" );
424 ((uint8_t *)buffer)[0] = status;
425 *actual_size = 1;
426 return (status != 0 ? EOK : ENAK);
427}
428
429/** UHCI root hub request handlers */
430static const usbvirt_control_request_handler_t control_transfer_handlers[] = {
431 {
432 STD_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
433 .name = "GetDescriptor",
434 .callback = virthub_base_get_hub_descriptor,
435 },
436 {
437 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
438 .name = "GetDescriptor",
439 .callback = virthub_base_get_hub_descriptor,
440 },
441 {
442 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_GET_DESCRIPTOR),
443 .name = "GetHubDescriptor",
444 .callback = virthub_base_get_hub_descriptor,
445 },
446 {
447 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATE),
448 .name = "GetBusState",
449 .callback = req_get_port_state,
450 },
451 {
452 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATUS),
453 .name = "GetPortStatus",
454 .callback = req_get_port_status
455 },
456 {
457 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_CLEAR_FEATURE),
458 .name = "ClearHubFeature",
459 /* Hub features are overcurrent and supply good,
460 * this request may only clear changes that we never report*/
461 .callback = req_nop,
462 },
463 {
464 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_CLEAR_FEATURE),
465 .name = "ClearPortFeature",
466 .callback = req_clear_port_feature
467 },
468 {
469 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_GET_STATUS),
470 .name = "GetHubStatus",
471 /* UHCI can't report OC condition or,
472 * lose power source */
473 .callback = virthub_base_get_null_status,
474 },
475 {
476 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATUS),
477 .name = "GetPortStatus",
478 .callback = req_get_port_status
479 },
480 {
481 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_SET_FEATURE),
482 .name = "SetHubFeature",
483 /* Hub features are overcurrent and supply good,
484 * this request may only set changes that we never report*/
485 .callback = req_nop,
486 },
487 {
488 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_SET_FEATURE),
489 .name = "SetPortFeature",
490 .callback = req_set_port_feature
491 },
492 {
493 .callback = NULL
494 }
495};
496
497static usbvirt_device_ops_t ops = {
498 .control = control_transfer_handlers,
499 .data_in[HUB_STATUS_CHANGE_PIPE] = req_status_change_handler,
500};
Note: See TracBrowser for help on using the repository browser.