source: mainline/uspace/drv/bus/usb/xhci/rh.c@ 8afb485

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

Decreased buffer size. Printing warning when interrupt fails.

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