source: mainline/uspace/drv/bus/usb/xhci/rh.c@ 9876e34

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9876e34 was 9876e34, checked in by Petr Manek <petr.manek@…>, 8 years ago

Removed previous implementation, forwarding all events to hub, generating status change bitmap, reporting USB features and clearing them.

  • Property mode set to 100644
File size: 16.4 KB
Line 
1/*
2 * Copyright (c) 2017 Michal Staruch
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/** @addtogroup drvusbxhci
30 * @{
31 */
32/** @file
33 * @brief The roothub structures abstraction.
34 */
35
36#include <errno.h>
37#include <str_error.h>
38#include <usb/debug.h>
39#include <usb/host/utils/malloc32.h>
40#include "debug.h"
41#include "commands.h"
42#include "endpoint.h"
43#include "hc.h"
44#include "hw_struct/trb.h"
45#include "rh.h"
46
47enum {
48 HUB_STATUS_CHANGE_PIPE = 1,
49};
50
51static usbvirt_device_ops_t ops;
52
53/* This mask only lists registers, which imply port change. */
54static const uint32_t port_change_mask =
55 XHCI_REG_MASK(XHCI_PORT_CSC) |
56 XHCI_REG_MASK(XHCI_PORT_PEC) |
57 XHCI_REG_MASK(XHCI_PORT_WRC) |
58 XHCI_REG_MASK(XHCI_PORT_OCC) |
59 XHCI_REG_MASK(XHCI_PORT_PRC) |
60 XHCI_REG_MASK(XHCI_PORT_PLC) |
61 XHCI_REG_MASK(XHCI_PORT_CEC);
62
63int xhci_rh_init(xhci_rh_t *rh, xhci_hc_t *hc)
64{
65 assert(rh);
66 assert(hc);
67
68 rh->hc = hc;
69 rh->max_ports = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PORTS);
70
71 usb_hub_descriptor_header_t *header = &rh->hub_descriptor.header;
72 header->length = sizeof(usb_hub_descriptor_header_t);
73 header->descriptor_type = USB_DESCTYPE_HUB;
74 header->port_count = rh->max_ports;
75 header->characteristics =
76 HUB_CHAR_NO_POWER_SWITCH_FLAG | HUB_CHAR_NO_OC_FLAG;
77 header->power_good_time = 10; /* XHCI section 5.4.9 says 20ms max */
78 header->max_current = 0;
79
80 return virthub_base_init(&rh->base, "xhci", &ops, rh, NULL,
81 header, HUB_STATUS_CHANGE_PIPE);
82}
83
84// TODO: Check device deallocation, we free device_ctx in hc.c, not
85// sure about the other structs.
86// static int alloc_dev(xhci_hc_t *hc, uint8_t port, uint32_t route_str)
87// {
88// int err;
89//
90// xhci_cmd_t cmd;
91// xhci_cmd_init(&cmd);
92//
93// xhci_send_enable_slot_command(hc, &cmd);
94// if ((err = xhci_cmd_wait(&cmd, 100000)) != EOK)
95// return err;
96//
97// uint32_t slot_id = cmd.slot_id;
98//
99// usb_log_debug2("Obtained slot ID: %u.\n", slot_id);
100// xhci_cmd_fini(&cmd);
101//
102// xhci_input_ctx_t *ictx = malloc32(sizeof(xhci_input_ctx_t));
103// if (!ictx) {
104// return ENOMEM;
105// }
106//
107// memset(ictx, 0, sizeof(xhci_input_ctx_t));
108//
109// XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 0);
110// XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 1);
111//
112// /* Initialize slot_ctx according to section 4.3.3 point 3. */
113// /* Attaching to root hub port, root string equals to 0. */
114// XHCI_SLOT_ROOT_HUB_PORT_SET(ictx->slot_ctx, port);
115// XHCI_SLOT_CTX_ENTRIES_SET(ictx->slot_ctx, 1);
116// XHCI_SLOT_ROUTE_STRING_SET(ictx->slot_ctx, route_str);
117//
118// xhci_trb_ring_t *ep_ring = malloc32(sizeof(xhci_trb_ring_t));
119// if (!ep_ring) {
120// err = ENOMEM;
121// goto err_ictx;
122// }
123//
124// err = xhci_trb_ring_init(ep_ring, hc);
125// if (err)
126// goto err_ring;
127//
128// XHCI_EP_TYPE_SET(ictx->endpoint_ctx[0], EP_TYPE_CONTROL);
129// // TODO: must be changed with a command after USB descriptor is read
130// // See 4.6.5 in XHCI specification, first note
131// XHCI_EP_MAX_PACKET_SIZE_SET(ictx->endpoint_ctx[0],
132// xhci_is_usb3_port(&hc->rh, port) ? 512 : 8);
133// XHCI_EP_MAX_BURST_SIZE_SET(ictx->endpoint_ctx[0], 0);
134// /* FIXME physical pointer? */
135// XHCI_EP_TR_DPTR_SET(ictx->endpoint_ctx[0], ep_ring->dequeue);
136// XHCI_EP_DCS_SET(ictx->endpoint_ctx[0], 1);
137// XHCI_EP_INTERVAL_SET(ictx->endpoint_ctx[0], 0);
138// XHCI_EP_MAX_P_STREAMS_SET(ictx->endpoint_ctx[0], 0);
139// XHCI_EP_MULT_SET(ictx->endpoint_ctx[0], 0);
140// XHCI_EP_ERROR_COUNT_SET(ictx->endpoint_ctx[0], 3);
141//
142// // TODO: What's the alignment?
143// xhci_device_ctx_t *dctx = malloc32(sizeof(xhci_device_ctx_t));
144// if (!dctx) {
145// err = ENOMEM;
146// goto err_ring;
147// }
148// memset(dctx, 0, sizeof(xhci_device_ctx_t));
149//
150// hc->dcbaa[slot_id] = addr_to_phys(dctx);
151//
152// memset(&hc->dcbaa_virt[slot_id], 0, sizeof(xhci_virt_device_ctx_t));
153// hc->dcbaa_virt[slot_id].dev_ctx = dctx;
154// hc->dcbaa_virt[slot_id].trs[0] = ep_ring;
155//
156// xhci_cmd_init(&cmd);
157// cmd.slot_id = slot_id;
158// xhci_send_address_device_command(hc, &cmd, ictx);
159// if ((err = xhci_cmd_wait(&cmd, 100000)) != EOK)
160// goto err_dctx;
161//
162// xhci_cmd_fini(&cmd);
163//
164// // TODO: Issue configure endpoint commands (sec 4.3.5).
165//
166// return EOK;
167//
168// err_dctx:
169// if (dctx) {
170// free32(dctx);
171// hc->dcbaa[slot_id] = 0;
172// memset(&hc->dcbaa_virt[slot_id], 0, sizeof(xhci_virt_device_ctx_t));
173// }
174// err_ring:
175// if (ep_ring) {
176// xhci_trb_ring_fini(ep_ring);
177// free32(ep_ring);
178// }
179// err_ictx:
180// free32(ictx);
181// return err;
182// }
183
184// static int handle_connected_device(xhci_hc_t* hc, xhci_port_regs_t* regs, uint8_t port_id)
185// {
186// uint8_t link_state = XHCI_REG_RD(regs, XHCI_PORT_PLS);
187// const xhci_port_speed_t *speed = xhci_get_port_speed(&hc->rh, port_id);
188//
189// usb_log_info("Detected new %.4s%u.%u device on port %u.", speed->name, speed->major, speed->minor, port_id);
190//
191// if (speed->major == 3) {
192// if(link_state == 0) {
193// /* USB3 is automatically advanced to enabled. */
194// return alloc_dev(hc, port_id, 0);
195// }
196// else if (link_state == 5) {
197// /* USB 3 failed to enable. */
198// usb_log_error("USB 3 port couldn't be enabled.");
199// return EAGAIN;
200// }
201// else {
202// usb_log_error("USB 3 port is in invalid state %u.", link_state);
203// return EINVAL;
204// }
205// }
206// else {
207// usb_log_debug("USB 2 device attached, issuing reset.");
208// xhci_reset_hub_port(hc, port_id);
209// /*
210// FIXME: we need to wait for the event triggered by the reset
211// and then alloc_dev()... can't it be done directly instead of
212// going around?
213// */
214// return EOK;
215// }
216// }
217
218int xhci_handle_port_status_change_event(xhci_hc_t *hc, xhci_trb_t *trb)
219{
220 int err;
221
222 uint8_t port_id = xhci_get_hub_port(trb);
223 usb_log_debug("Port status change event detected for port %u.", port_id);
224
225 // Interrupt on the virtual hub status change pipe.
226 err = xhci_rh_interrupt(&hc->rh);
227 if (err != EOK) {
228 usb_log_warning("Invoking interrupt on virtual hub failed: %s",
229 str_error(err));
230 }
231
232 return EOK;
233}
234
235const xhci_port_speed_t *xhci_get_port_speed(xhci_rh_t *rh, uint8_t port)
236{
237 xhci_port_regs_t *port_regs = &rh->hc->op_regs->portrs[port - 1];
238
239 unsigned psiv = XHCI_REG_RD(port_regs, XHCI_PORT_PS);
240 return &rh->speeds[psiv];
241}
242
243int xhci_get_hub_port(xhci_trb_t *trb)
244{
245 assert(trb);
246 uint8_t port_id = XHCI_QWORD_EXTRACT(trb->parameter, 31, 24);
247
248 return port_id;
249}
250
251int xhci_reset_hub_port(xhci_hc_t* hc, uint8_t port)
252{
253 usb_log_debug2("Resetting port %u.", port);
254 xhci_port_regs_t *regs = &hc->op_regs->portrs[port-1];
255 XHCI_REG_WR(regs, XHCI_PORT_PR, 1);
256
257 return EOK;
258}
259
260int xhci_rh_schedule(xhci_rh_t *rh, usb_transfer_batch_t *batch)
261{
262 assert(rh);
263 assert(batch);
264 const usb_target_t target = {{
265 .address = batch->ep->address,
266 .endpoint = batch->ep->endpoint,
267 }};
268 batch->error = virthub_base_request(&rh->base, target,
269 usb_transfer_batch_direction(batch), (void*)batch->setup_buffer,
270 batch->buffer, batch->buffer_size, &batch->transfered_size);
271 if (batch->error == ENAK) {
272 /* This is safe because only status change interrupt transfers
273 * return NAK. The assertion holds true because the batch
274 * existence prevents communication with that ep */
275 assert(rh->unfinished_interrupt_transfer == NULL);
276 rh->unfinished_interrupt_transfer = batch;
277 } else {
278 usb_transfer_batch_finish(batch, NULL);
279 usb_transfer_batch_destroy(batch);
280 }
281 return EOK;
282}
283
284int xhci_rh_interrupt(xhci_rh_t *rh)
285{
286 usb_log_debug2("Called xhci_rh_interrupt().");
287
288 /* TODO: atomic swap needed */
289 usb_transfer_batch_t *batch = rh->unfinished_interrupt_transfer;
290 rh->unfinished_interrupt_transfer = NULL;
291 if (batch) {
292 const usb_target_t target = {{
293 .address = batch->ep->address,
294 .endpoint = batch->ep->endpoint,
295 }};
296 batch->error = virthub_base_request(&rh->base, target,
297 usb_transfer_batch_direction(batch),
298 (void*)batch->setup_buffer,
299 batch->buffer, batch->buffer_size, &batch->transfered_size);
300 usb_transfer_batch_finish(batch, NULL);
301 usb_transfer_batch_destroy(batch);
302 }
303 return EOK;
304}
305
306/** Hub set feature request handler.
307 * @param device Virtual hub device
308 * @param setup_packet USB setup stage data.
309 * @param[out] data destination data buffer, size must be at least
310 * setup_packet->length bytes
311 * @param[out] act_size Sized of the valid response part of the buffer.
312 * @return Error code.
313 */
314static int req_clear_hub_feature(usbvirt_device_t *device,
315 const usb_device_request_setup_packet_t *setup_packet,
316 uint8_t *data, size_t *act_size)
317{
318 /* TODO: Implement me! */
319 usb_log_debug2("Called req_clear_hub_feature().");
320 return EOK;
321}
322
323#define XHCI_TO_USB(usb_feat, reg_set, ...) \
324 (((XHCI_REG_RD(reg_set, ##__VA_ARGS__)) ? 1 : 0) << (usb_feat))
325
326/** Port status request handler.
327 * @param device Virtual hub device
328 * @param setup_packet USB setup stage data.
329 * @param[out] data destination data buffer, size must be at least
330 * setup_packet->length bytes
331 * @param[out] act_size Sized of the valid response part of the buffer.
332 * @return Error code.
333 */
334static int req_get_port_status(usbvirt_device_t *device,
335 const usb_device_request_setup_packet_t *setup_packet,
336 uint8_t *data, size_t *act_size)
337{
338 xhci_rh_t *hub = virthub_get_data(device);
339 assert(hub);
340
341 if (!setup_packet->index || setup_packet->index > hub->max_ports) {
342 return ESTALL;
343 }
344
345 /* The index is 1-based. */
346 xhci_port_regs_t* regs = &hub->hc->op_regs->portrs[setup_packet->index - 1];
347
348 const uint32_t status = uint32_host2usb(
349 XHCI_TO_USB(USB_HUB_FEATURE_C_PORT_CONNECTION, regs, XHCI_PORT_CSC) |
350 XHCI_TO_USB(USB_HUB_FEATURE_C_PORT_ENABLE, regs, XHCI_PORT_PEC) |
351 XHCI_TO_USB(USB_HUB_FEATURE_C_PORT_OVER_CURRENT, regs, XHCI_PORT_OCC) |
352 XHCI_TO_USB(USB_HUB_FEATURE_C_PORT_RESET, regs, XHCI_PORT_PRC) |
353 XHCI_TO_USB(USB_HUB_FEATURE_PORT_CONNECTION, regs, XHCI_PORT_CCS) |
354 XHCI_TO_USB(USB_HUB_FEATURE_PORT_ENABLE, regs, XHCI_PORT_PED) |
355 XHCI_TO_USB(USB_HUB_FEATURE_PORT_OVER_CURRENT, regs, XHCI_PORT_OCA) |
356 XHCI_TO_USB(USB_HUB_FEATURE_PORT_RESET, regs, XHCI_PORT_PR) |
357 XHCI_TO_USB(USB_HUB_FEATURE_PORT_POWER, regs, XHCI_PORT_PP)
358 );
359
360 usb_log_debug2("RH: GetPortStatus(%hu) = %u.", setup_packet->index,
361 uint32_usb2host(status));
362
363 memcpy(data, &status, sizeof(status));
364 *act_size = sizeof(status);
365
366 return EOK;
367}
368
369/** Port clear feature request handler.
370 * @param device Virtual hub device
371 * @param setup_packet USB setup stage data.
372 * @param[out] data destination data buffer, size must be at least
373 * setup_packet->length bytes
374 * @param[out] act_size Sized of the valid response part of the buffer.
375 * @return Error code.
376 */
377static int req_clear_port_feature(usbvirt_device_t *device,
378 const usb_device_request_setup_packet_t *setup_packet,
379 uint8_t *data, size_t *act_size)
380{
381 xhci_rh_t *hub = virthub_get_data(device);
382 assert(hub);
383
384 if (!setup_packet->index || setup_packet->index > hub->max_ports) {
385 return ESTALL;
386 }
387
388 /* The index is 1-based. */
389 xhci_port_regs_t* regs = &hub->hc->op_regs->portrs[setup_packet->index - 1];
390
391#define USB_MAP_XHCI(a, b) [USB_HUB_FEATURE_##a] = XHCI_REG_MASK(XHCI_PORT_##b)
392
393 const usb_hub_class_feature_t feature = uint16_usb2host(setup_packet->value);
394 static const ioport32_t masks[] = {
395 USB_MAP_XHCI(C_PORT_CONNECTION, CSC),
396 USB_MAP_XHCI(C_PORT_ENABLE, PEC),
397 USB_MAP_XHCI(C_PORT_OVER_CURRENT, OCC),
398 USB_MAP_XHCI(C_PORT_RESET, PRC)
399 };
400
401#undef USB_MAP_XHCI
402
403 usb_log_debug2("RH: ClearPortFeature(%hu) = %d.", setup_packet->index,
404 feature);
405
406 /* Clear the register by writing 1. */
407 XHCI_REG_WR_FIELD(&regs->portsc, masks[feature], 32);
408
409 return EOK;
410}
411
412/** Port set feature request handler.
413 * @param device Virtual hub device
414 * @param setup_packet USB setup stage data.
415 * @param[out] data destination data buffer, size must be at least
416 * setup_packet->length bytes
417 * @param[out] act_size Sized of the valid response part of the buffer.
418 * @return Error code.
419 */
420static int req_set_port_feature(usbvirt_device_t *device,
421 const usb_device_request_setup_packet_t *setup_packet,
422 uint8_t *data, size_t *act_size)
423{
424 /* TODO: Implement me! */
425 usb_log_debug2("Called req_set_port_feature().");
426 return EOK;
427}
428
429/** Status change handler.
430 * @param device Virtual hub device
431 * @param endpoint Endpoint number
432 * @param tr_type Transfer type
433 * @param buffer Response destination
434 * @param buffer_size Bytes available in buffer
435 * @param actual_size Size us the used part of the dest buffer.
436 *
437 * Produces status mask. Bit 0 indicates hub status change the other bits
438 * represent port status change.
439 */
440static int req_status_change_handler(usbvirt_device_t *device,
441 usb_endpoint_t endpoint, usb_transfer_type_t tr_type,
442 void *buffer, size_t buffer_size, size_t *actual_size)
443{
444 usb_log_debug2("Called req_status_change_handler().");
445 xhci_rh_t *hub = virthub_get_data(device);
446 assert(hub);
447
448 uint8_t status[STATUS_BYTES(hub->max_ports)];
449 memset(status, 0, sizeof(status));
450
451 if (buffer_size < sizeof(status))
452 return ESTALL;
453
454 bool change = false;
455 for (size_t i = 1; i <= hub->max_ports; ++i) {
456 xhci_port_regs_t *regs = &hub->hc->op_regs->portrs[i - 1];
457
458 if (XHCI_REG_RD_FIELD(&regs->portsc, 32) & port_change_mask) {
459 status[i / 8] |= (1 << (i % 8));
460 change = true;
461 }
462 }
463
464 memcpy(buffer, &status, sizeof(status));
465 *actual_size = sizeof(status);
466 return change ? EOK : ENAK;
467}
468
469int xhci_rh_fini(xhci_rh_t *rh)
470{
471 /* TODO: Implement me! */
472 usb_log_debug2("Called xhci_rh_fini().");
473 return EOK;
474}
475
476/** XHCI root hub request handlers */
477static const usbvirt_control_request_handler_t control_transfer_handlers[] = {
478 {
479 STD_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
480 .name = "GetDescriptor",
481 .callback = virthub_base_get_hub_descriptor,
482 },
483 {
484 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
485 .name = "GetDescriptor",
486 .callback = virthub_base_get_hub_descriptor,
487 },
488 {
489 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_GET_DESCRIPTOR),
490 .name = "GetHubDescriptor",
491 .callback = virthub_base_get_hub_descriptor,
492 },
493 {
494 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATUS),
495 .name = "GetPortStatus",
496 .callback = req_get_port_status,
497 },
498 {
499 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_CLEAR_FEATURE),
500 .name = "ClearHubFeature",
501 .callback = req_clear_hub_feature,
502 },
503 {
504 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_CLEAR_FEATURE),
505 .name = "ClearPortFeature",
506 .callback = req_clear_port_feature,
507 },
508 {
509 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_GET_STATUS),
510 .name = "GetHubStatus",
511 /* XHCI root hub has no power source,
512 * over-current is reported by port */
513 .callback = virthub_base_get_null_status,
514 },
515 {
516 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATUS),
517 .name = "GetPortStatus",
518 .callback = req_get_port_status,
519 },
520 {
521 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_SET_FEATURE),
522 .name = "SetHubFeature",
523 .callback = req_nop,
524 },
525 {
526 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_SET_FEATURE),
527 .name = "SetPortFeature",
528 .callback = req_set_port_feature,
529 },
530 {
531 .callback = NULL
532 }
533};
534
535/** Virtual XHCI root hub ops */
536static usbvirt_device_ops_t ops = {
537 .control = control_transfer_handlers,
538 .data_in[HUB_STATUS_CHANGE_PIPE] = req_status_change_handler,
539};
540
541
542/**
543 * @}
544 */
Note: See TracBrowser for help on using the repository browser.