source: mainline/uspace/drv/bus/usb/xhci/hc.c@ c3d926f3

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

Big command refactoring. Unified and encapsulated command function API. Removed explicit heap command (de)allocation functions. Added three functions for (a)synchronous command issuing and neat inline macro with syntax sugar.

  • Property mode set to 100644
File size: 20.7 KB
Line 
1/*
2 * Copyright (c) 2017 Ondrej Hlavaty
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 host controller data bookkeeping.
34 */
35
36#include <errno.h>
37#include <str_error.h>
38#include <usb/debug.h>
39#include <usb/host/endpoint.h>
40#include <usb/host/utils/malloc32.h>
41#include "debug.h"
42#include "hc.h"
43#include "rh.h"
44#include "hw_struct/trb.h"
45#include "hw_struct/context.h"
46#include "endpoint.h"
47#include "commands.h"
48#include "transfers.h"
49#include "trb_ring.h"
50
51/**
52 * Default USB Speed ID mapping: Table 157
53 */
54#define PSI_TO_BPS(psie, psim) (((uint64_t) psim) << (10 * psie))
55#define PORT_SPEED(mjr, psie, psim) { \
56 .name = "USB ", \
57 .major = mjr, \
58 .minor = 0, \
59 .rx_bps = PSI_TO_BPS(psie, psim), \
60 .tx_bps = PSI_TO_BPS(psie, psim) \
61}
62static const xhci_port_speed_t ps_default_full = PORT_SPEED(2, 2, 12);
63static const xhci_port_speed_t ps_default_low = PORT_SPEED(2, 1, 1500);
64static const xhci_port_speed_t ps_default_high = PORT_SPEED(2, 2, 480);
65static const xhci_port_speed_t ps_default_super = PORT_SPEED(3, 3, 5);
66
67/**
68 * Walk the list of extended capabilities.
69 */
70static int hc_parse_ec(xhci_hc_t *hc)
71{
72 unsigned psic, major, minor;
73 xhci_sp_name_t name;
74
75 xhci_port_speed_t *speeds = hc->rh.speeds;
76
77 for (xhci_extcap_t *ec = hc->xecp; ec; ec = xhci_extcap_next(ec)) {
78 xhci_dump_extcap(ec);
79 switch (XHCI_REG_RD(ec, XHCI_EC_CAP_ID)) {
80 case XHCI_EC_USB_LEGACY:
81 assert(hc->legsup == NULL);
82 hc->legsup = (xhci_legsup_t *) ec;
83 break;
84 case XHCI_EC_SUPPORTED_PROTOCOL:
85 psic = XHCI_REG_RD(ec, XHCI_EC_SP_PSIC);
86 major = XHCI_REG_RD(ec, XHCI_EC_SP_MAJOR);
87 minor = XHCI_REG_RD(ec, XHCI_EC_SP_MINOR);
88 name.packed = host2uint32_t_le(XHCI_REG_RD(ec, XHCI_EC_SP_NAME));
89
90 if (name.packed != xhci_name_usb.packed) {
91 /**
92 * The detection of such protocol would work,
93 * but the rest of the implementation is made
94 * for the USB protocol only.
95 */
96 usb_log_error("Unknown protocol %.4s.", name.str);
97 return ENOTSUP;
98 }
99
100 // "Implied" speed
101 if (psic == 0) {
102 assert(minor == 0);
103
104 if (major == 2) {
105 speeds[1] = ps_default_full;
106 speeds[2] = ps_default_low;
107 speeds[3] = ps_default_high;
108 } else if (major == 3) {
109 speeds[4] = ps_default_super;
110 } else {
111 return EINVAL;
112 }
113
114 usb_log_debug2("Implied speed of USB %u.0 set up.", major);
115 } else {
116 for (unsigned i = 0; i < psic; i++) {
117 xhci_psi_t *psi = xhci_extcap_psi(ec, i);
118 unsigned sim = XHCI_REG_RD(psi, XHCI_PSI_PSIM);
119 unsigned psiv = XHCI_REG_RD(psi, XHCI_PSI_PSIV);
120 unsigned psie = XHCI_REG_RD(psi, XHCI_PSI_PSIE);
121 unsigned psim = XHCI_REG_RD(psi, XHCI_PSI_PSIM);
122
123 speeds[psiv].major = major;
124 speeds[psiv].minor = minor;
125 str_ncpy(speeds[psiv].name, 4, name.str, 4);
126
127 uint64_t bps = PSI_TO_BPS(psie, psim);
128
129 if (sim == XHCI_PSI_PLT_SYMM || sim == XHCI_PSI_PLT_RX)
130 speeds[psiv].rx_bps = bps;
131 if (sim == XHCI_PSI_PLT_SYMM || sim == XHCI_PSI_PLT_TX) {
132 speeds[psiv].tx_bps = bps;
133 usb_log_debug2("Speed %u set up for bps %" PRIu64 " / %" PRIu64 ".", psiv, speeds[psiv].rx_bps, speeds[psiv].tx_bps);
134 }
135 }
136 }
137 }
138 }
139 return EOK;
140}
141
142int hc_init_mmio(xhci_hc_t *hc, const hw_res_list_parsed_t *hw_res)
143{
144 int err;
145
146 if (hw_res->mem_ranges.count != 1) {
147 usb_log_error("Unexpected MMIO area, bailing out.");
148 return EINVAL;
149 }
150
151 hc->mmio_range = hw_res->mem_ranges.ranges[0];
152
153 usb_log_debug("MMIO area at %p (size %zu), IRQ %d.\n",
154 RNGABSPTR(hc->mmio_range), RNGSZ(hc->mmio_range), hw_res->irqs.irqs[0]);
155
156 if (RNGSZ(hc->mmio_range) < sizeof(xhci_cap_regs_t))
157 return EOVERFLOW;
158
159 void *base;
160 if ((err = pio_enable_range(&hc->mmio_range, &base)))
161 return err;
162
163 hc->reg_base = base;
164 hc->cap_regs = (xhci_cap_regs_t *) base;
165 hc->op_regs = (xhci_op_regs_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_LENGTH));
166 hc->rt_regs = (xhci_rt_regs_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_RTSOFF));
167 hc->db_arry = (xhci_doorbell_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_DBOFF));
168
169 uintptr_t xec_offset = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_XECP) * sizeof(xhci_dword_t);
170 if (xec_offset > 0)
171 hc->xecp = (xhci_extcap_t *) (base + xec_offset);
172
173 usb_log_debug2("Initialized MMIO reg areas:");
174 usb_log_debug2("\tCapability regs: %p", hc->cap_regs);
175 usb_log_debug2("\tOperational regs: %p", hc->op_regs);
176 usb_log_debug2("\tRuntime regs: %p", hc->rt_regs);
177 usb_log_debug2("\tDoorbell array base: %p", hc->db_arry);
178
179 xhci_dump_cap_regs(hc->cap_regs);
180
181 hc->ac64 = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_AC64);
182 hc->max_slots = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_SLOTS);
183
184 if ((err = hc_parse_ec(hc))) {
185 pio_disable(hc->reg_base, RNGSZ(hc->mmio_range));
186 return err;
187 }
188
189 return EOK;
190}
191
192int hc_init_memory(xhci_hc_t *hc, ddf_dev_t *device)
193{
194 int err;
195
196 hc->dcbaa = malloc32((1 + hc->max_slots) * sizeof(uint64_t));
197 if (!hc->dcbaa)
198 return ENOMEM;
199
200 if ((err = xhci_trb_ring_init(&hc->command_ring)))
201 goto err_dcbaa;
202
203 if ((err = xhci_event_ring_init(&hc->event_ring)))
204 goto err_cmd_ring;
205
206 if ((err = xhci_scratchpad_alloc(hc)))
207 goto err_event_ring;
208
209 if ((err = xhci_init_commands(hc)))
210 goto err_scratch;
211
212 if ((err = xhci_rh_init(&hc->rh, hc, device)))
213 goto err_cmd;
214
215 if ((err = xhci_bus_init(&hc->bus, hc)))
216 goto err_rh;
217
218
219 return EOK;
220
221err_rh:
222 xhci_rh_fini(&hc->rh);
223err_cmd:
224 xhci_fini_commands(hc);
225err_scratch:
226 xhci_scratchpad_free(hc);
227err_event_ring:
228 xhci_event_ring_fini(&hc->event_ring);
229err_cmd_ring:
230 xhci_trb_ring_fini(&hc->command_ring);
231err_dcbaa:
232 free32(hc->dcbaa);
233 return err;
234}
235
236/*
237 * Pseudocode:
238 * ip = read(intr[0].iman)
239 * if (ip) {
240 * status = read(usbsts)
241 * assert status
242 * assert ip
243 * accept (passing status)
244 * }
245 * decline
246 */
247static const irq_cmd_t irq_commands[] = {
248 {
249 .cmd = CMD_PIO_READ_32,
250 .dstarg = 3,
251 .addr = NULL /* intr[0].iman */
252 },
253 {
254 .cmd = CMD_AND,
255 .srcarg = 3,
256 .dstarg = 4,
257 .value = 0 /* host2xhci(32, 1) */
258 },
259 {
260 .cmd = CMD_PREDICATE,
261 .srcarg = 4,
262 .value = 5
263 },
264 {
265 .cmd = CMD_PIO_READ_32,
266 .dstarg = 1,
267 .addr = NULL /* usbsts */
268 },
269 {
270 .cmd = CMD_AND,
271 .srcarg = 1,
272 .dstarg = 2,
273 .value = 0 /* host2xhci(32, XHCI_STATUS_ACK_MASK) */
274 },
275 {
276 .cmd = CMD_PIO_WRITE_A_32,
277 .srcarg = 2,
278 .addr = NULL /* usbsts */
279 },
280 {
281 .cmd = CMD_PIO_WRITE_A_32,
282 .srcarg = 3,
283 .addr = NULL /* intr[0].iman */
284 },
285 {
286 .cmd = CMD_ACCEPT
287 },
288 {
289 .cmd = CMD_DECLINE
290 }
291};
292
293
294/**
295 * Generates code to accept interrupts. The xHCI is designed primarily for
296 * MSI/MSI-X, but we use PCI Interrupt Pin. In this mode, all the Interrupters
297 * (except 0) are disabled.
298 */
299int hc_irq_code_gen(irq_code_t *code, xhci_hc_t *hc, const hw_res_list_parsed_t *hw_res)
300{
301 assert(code);
302 assert(hw_res);
303
304 if (hw_res->irqs.count != 1) {
305 usb_log_info("Unexpected HW resources to enable interrupts.");
306 return EINVAL;
307 }
308
309 code->ranges = malloc(sizeof(irq_pio_range_t));
310 if (code->ranges == NULL)
311 return ENOMEM;
312
313 code->cmds = malloc(sizeof(irq_commands));
314 if (code->cmds == NULL) {
315 free(code->ranges);
316 return ENOMEM;
317 }
318
319 code->rangecount = 1;
320 code->ranges[0] = (irq_pio_range_t) {
321 .base = RNGABS(hc->mmio_range),
322 .size = RNGSZ(hc->mmio_range),
323 };
324
325 code->cmdcount = ARRAY_SIZE(irq_commands);
326 memcpy(code->cmds, irq_commands, sizeof(irq_commands));
327
328 void *intr0_iman = RNGABSPTR(hc->mmio_range) + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_RTSOFF) + offsetof(xhci_rt_regs_t, ir[0]);
329 void *usbsts = RNGABSPTR(hc->mmio_range) + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_LENGTH) + offsetof(xhci_op_regs_t, usbsts);
330 code->cmds[0].addr = intr0_iman;
331 code->cmds[1].value = host2xhci(32, 1);
332 code->cmds[3].addr = usbsts;
333 code->cmds[4].value = host2xhci(32, XHCI_STATUS_ACK_MASK);
334 code->cmds[5].addr = usbsts;
335 code->cmds[6].addr = intr0_iman;
336
337 return hw_res->irqs.irqs[0];
338}
339
340int hc_claim(xhci_hc_t *hc, ddf_dev_t *dev)
341{
342 /* No legacy support capability, the controller is solely for us */
343 if (!hc->legsup)
344 return EOK;
345
346 /* Section 4.22.1 */
347 /* TODO: Test this with USB3-aware BIOS */
348 usb_log_debug2("LEGSUP: bios: %x, os: %x", hc->legsup->sem_bios, hc->legsup->sem_os);
349 XHCI_REG_WR(hc->legsup, XHCI_LEGSUP_SEM_OS, 1);
350 for (int i = 0; i <= (XHCI_LEGSUP_BIOS_TIMEOUT_US / XHCI_LEGSUP_POLLING_DELAY_1MS); i++) {
351 usb_log_debug2("LEGSUP: elapsed: %i ms, bios: %x, os: %x", i,
352 XHCI_REG_RD(hc->legsup, XHCI_LEGSUP_SEM_BIOS),
353 XHCI_REG_RD(hc->legsup, XHCI_LEGSUP_SEM_OS));
354 if (XHCI_REG_RD(hc->legsup, XHCI_LEGSUP_SEM_BIOS) == 0) {
355 assert(XHCI_REG_RD(hc->legsup, XHCI_LEGSUP_SEM_OS) == 1);
356 return EOK;
357 }
358 async_usleep(XHCI_LEGSUP_POLLING_DELAY_1MS);
359 }
360 usb_log_error("BIOS did not release XHCI legacy hold!\n");
361
362 return ENOTSUP;
363}
364
365static int hc_reset(xhci_hc_t *hc)
366{
367 /* Stop the HC: set R/S to 0 */
368 XHCI_REG_CLR(hc->op_regs, XHCI_OP_RS, 1);
369
370 /* Wait 16 ms until the HC is halted */
371 async_usleep(16000);
372 assert(XHCI_REG_RD(hc->op_regs, XHCI_OP_HCH));
373
374 /* Reset */
375 XHCI_REG_SET(hc->op_regs, XHCI_OP_HCRST, 1);
376
377 /* Wait until the reset is complete */
378 while (XHCI_REG_RD(hc->op_regs, XHCI_OP_HCRST))
379 async_usleep(1000);
380
381 return EOK;
382}
383
384/**
385 * Initialize the HC: section 4.2
386 */
387int hc_start(xhci_hc_t *hc, bool irq)
388{
389 int err;
390
391 if ((err = hc_reset(hc)))
392 return err;
393
394 while (XHCI_REG_RD(hc->op_regs, XHCI_OP_CNR))
395 async_usleep(1000);
396
397 uint64_t dcbaaptr = addr_to_phys(hc->dcbaa);
398 XHCI_REG_WR(hc->op_regs, XHCI_OP_DCBAAP_LO, LOWER32(dcbaaptr));
399 XHCI_REG_WR(hc->op_regs, XHCI_OP_DCBAAP_HI, UPPER32(dcbaaptr));
400 XHCI_REG_WR(hc->op_regs, XHCI_OP_MAX_SLOTS_EN, 0);
401
402 uint64_t crptr = xhci_trb_ring_get_dequeue_ptr(&hc->command_ring);
403 XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_LO, LOWER32(crptr) >> 6);
404 XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_HI, UPPER32(crptr));
405
406 uint64_t erstptr = addr_to_phys(hc->event_ring.erst);
407 uint64_t erdp = hc->event_ring.dequeue_ptr;
408 xhci_interrupter_regs_t *intr0 = &hc->rt_regs->ir[0];
409 XHCI_REG_WR(intr0, XHCI_INTR_ERSTSZ, hc->event_ring.segment_count);
410 XHCI_REG_WR(intr0, XHCI_INTR_ERDP_LO, LOWER32(erdp));
411 XHCI_REG_WR(intr0, XHCI_INTR_ERDP_HI, UPPER32(erdp));
412 XHCI_REG_WR(intr0, XHCI_INTR_ERSTBA_LO, LOWER32(erstptr));
413 XHCI_REG_WR(intr0, XHCI_INTR_ERSTBA_HI, UPPER32(erstptr));
414
415 if (irq) {
416 XHCI_REG_SET(intr0, XHCI_INTR_IE, 1);
417 XHCI_REG_SET(hc->op_regs, XHCI_OP_INTE, 1);
418 }
419
420 XHCI_REG_SET(hc->op_regs, XHCI_OP_RS, 1);
421
422 /* The reset changed status of all ports, and SW originated reason does
423 * not cause an interrupt.
424 */
425 xhci_rh_handle_port_change(&hc->rh);
426
427 return EOK;
428}
429
430/**
431 * Used only when polling. Shall supplement the irq_commands.
432 */
433int hc_status(xhci_hc_t *hc, uint32_t *status)
434{
435 int ip = XHCI_REG_RD(hc->rt_regs->ir, XHCI_INTR_IP);
436 if (ip) {
437 *status = XHCI_REG_RD(hc->op_regs, XHCI_OP_STATUS);
438 XHCI_REG_WR(hc->op_regs, XHCI_OP_STATUS, *status & XHCI_STATUS_ACK_MASK);
439 XHCI_REG_WR(hc->rt_regs->ir, XHCI_INTR_IP, 1);
440
441 /* interrupt handler expects status from irq_commands, which is
442 * in xhci order. */
443 *status = host2xhci(32, *status);
444 }
445
446 usb_log_debug2("HC(%p): Polled status: %x", hc, *status);
447 return EOK;
448}
449
450int hc_schedule(xhci_hc_t *hc, usb_transfer_batch_t *batch)
451{
452 assert(batch);
453 assert(batch->ep);
454
455 usb_log_debug2("Endpoint(%d:%d) started %s transfer of size %lu.",
456 batch->ep->target.address, batch->ep->target.endpoint,
457 usb_str_transfer_type(batch->ep->transfer_type),
458 batch->buffer_size);
459
460 if (!batch->ep->target.address) {
461 usb_log_error("Attempted to schedule transfer to address 0.");
462 return EINVAL;
463 }
464
465 return xhci_transfer_schedule(hc, batch);
466}
467
468typedef int (*event_handler) (xhci_hc_t *, xhci_trb_t *trb);
469
470static event_handler event_handlers [] = {
471 [XHCI_TRB_TYPE_COMMAND_COMPLETION_EVENT] = &xhci_handle_command_completion,
472 [XHCI_TRB_TYPE_PORT_STATUS_CHANGE_EVENT] = &xhci_rh_handle_port_status_change_event,
473 [XHCI_TRB_TYPE_TRANSFER_EVENT] = &xhci_handle_transfer_event,
474};
475
476static int hc_handle_event(xhci_hc_t *hc, xhci_trb_t *trb, xhci_interrupter_regs_t *intr)
477{
478 unsigned type = TRB_TYPE(*trb);
479 if (type >= ARRAY_SIZE(event_handlers) || !event_handlers[type])
480 return ENOTSUP;
481
482 return event_handlers[type](hc, trb);
483}
484
485static void hc_run_event_ring(xhci_hc_t *hc, xhci_event_ring_t *event_ring, xhci_interrupter_regs_t *intr)
486{
487 int err;
488 ssize_t size = 16;
489 xhci_trb_t *queue = malloc(sizeof(xhci_trb_t) * size);
490 if (!queue) {
491 usb_log_error("Not enough memory to run the event ring.");
492 return;
493 }
494
495 xhci_trb_t *head = queue;
496
497 while ((err = xhci_event_ring_dequeue(event_ring, head)) != ENOENT) {
498 if (err != EOK) {
499 usb_log_warning("Error while accessing event ring: %s", str_error(err));
500 break;
501 }
502
503 usb_log_debug2("Dequeued trb from event ring: %s", xhci_trb_str_type(TRB_TYPE(*head)));
504 head++;
505
506 /* Expand the array if needed. */
507 if (head - queue >= size) {
508 size *= 2;
509 xhci_trb_t *new_queue = realloc(queue, size);
510 if (new_queue == NULL)
511 break; /* Will process only those TRBs we have memory for. */
512
513 head = new_queue + (head - queue);
514 }
515 }
516
517 /* Update the ERDP to make room in the ring. */
518 usb_log_debug2("Copying from ring finished, updating ERDP.");
519 uint64_t erdp = hc->event_ring.dequeue_ptr;
520 XHCI_REG_WR(intr, XHCI_INTR_ERDP_LO, LOWER32(erdp));
521 XHCI_REG_WR(intr, XHCI_INTR_ERDP_HI, UPPER32(erdp));
522 XHCI_REG_SET(intr, XHCI_INTR_ERDP_EHB, 1);
523
524 /* Handle all of the collected events if possible. */
525 if (head == queue)
526 usb_log_warning("No events to be handled!");
527
528 for (xhci_trb_t *tail = queue; tail != head; tail++) {
529 if ((err = hc_handle_event(hc, tail, intr)) != EOK) {
530 usb_log_error("Failed to handle event: %s", str_error(err));
531 }
532 }
533
534 free(queue);
535 usb_log_debug2("Event ring run finished.");
536}
537
538void hc_interrupt(xhci_hc_t *hc, uint32_t status)
539{
540 status = xhci2host(32, status);
541
542 if (status & XHCI_REG_MASK(XHCI_OP_PCD)) {
543 usb_log_debug2("Root hub interrupt.");
544 xhci_rh_handle_port_change(&hc->rh);
545 status &= ~XHCI_REG_MASK(XHCI_OP_PCD);
546 }
547
548 if (status & XHCI_REG_MASK(XHCI_OP_HSE)) {
549 usb_log_error("Host controller error occured. Bad things gonna happen...");
550 status &= ~XHCI_REG_MASK(XHCI_OP_HSE);
551 }
552
553 if (status & XHCI_REG_MASK(XHCI_OP_EINT)) {
554 usb_log_debug2("Event interrupt, running the event ring.");
555 hc_run_event_ring(hc, &hc->event_ring, &hc->rt_regs->ir[0]);
556 status &= ~XHCI_REG_MASK(XHCI_OP_EINT);
557 }
558
559 if (status & XHCI_REG_MASK(XHCI_OP_SRE)) {
560 usb_log_error("Save/Restore error occured. WTF, S/R mechanism not implemented!");
561 status &= ~XHCI_REG_MASK(XHCI_OP_SRE);
562 }
563
564 if (status) {
565 usb_log_error("Non-zero status after interrupt handling (%08x) - missing something?", status);
566 }
567}
568
569static void hc_dcbaa_fini(xhci_hc_t *hc)
570{
571 xhci_scratchpad_free(hc);
572 free32(hc->dcbaa);
573}
574
575void hc_fini(xhci_hc_t *hc)
576{
577 xhci_bus_fini(&hc->bus);
578 xhci_trb_ring_fini(&hc->command_ring);
579 xhci_event_ring_fini(&hc->event_ring);
580 hc_dcbaa_fini(hc);
581 xhci_fini_commands(hc);
582 xhci_rh_fini(&hc->rh);
583 pio_disable(hc->reg_base, RNGSZ(hc->mmio_range));
584 usb_log_info("HC(%p): Finalized.", hc);
585}
586
587int hc_ring_doorbell(xhci_hc_t *hc, unsigned doorbell, unsigned target)
588{
589 assert(hc);
590 uint32_t v = host2xhci(32, target & BIT_RRANGE(uint32_t, 7));
591 pio_write_32(&hc->db_arry[doorbell], v);
592 usb_log_debug2("Ringing doorbell %d (target: %d)", doorbell, target);
593 return EOK;
594}
595
596int hc_enable_slot(xhci_hc_t *hc, uint32_t *slot_id)
597{
598 assert(hc);
599
600 int err;
601 xhci_cmd_t cmd;
602 xhci_cmd_init(&cmd, XHCI_CMD_ENABLE_SLOT);
603
604 if ((err = xhci_cmd_sync(hc, &cmd))) {
605 goto end;
606 }
607
608 if (slot_id) {
609 *slot_id = cmd.slot_id;
610 }
611
612end:
613 xhci_cmd_fini(&cmd);
614 return err;
615}
616
617int hc_disable_slot(xhci_hc_t *hc, uint32_t slot_id)
618{
619 assert(hc);
620
621 int err;
622 if ((err = xhci_cmd_sync_inline(hc, DISABLE_SLOT, .slot_id = slot_id))) {
623 return err;
624 }
625
626 return EOK;
627}
628
629static int create_valid_input_ctx(xhci_input_ctx_t **out_ictx)
630{
631 xhci_input_ctx_t *ictx = malloc32(sizeof(xhci_input_ctx_t));
632 if (!ictx) {
633 return ENOMEM;
634 }
635
636 memset(ictx, 0, sizeof(xhci_input_ctx_t));
637
638 // Quoting sec. 4.6.6: A1, D0, D1 are down, A0 is up.
639 XHCI_INPUT_CTRL_CTX_ADD_CLEAR(ictx->ctrl_ctx, 1);
640 XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 0);
641 XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 1);
642 XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 0);
643
644 if (out_ictx) {
645 *out_ictx = ictx;
646 }
647
648 return EOK;
649}
650
651// TODO: This currently assumes the device is attached to rh directly
652// -> calculate route string
653int hc_address_device(xhci_hc_t *hc, xhci_device_t *dev, xhci_endpoint_t *ep0)
654{
655 int err = ENOMEM;
656
657 /* Setup and register device context */
658 dev->dev_ctx = malloc32(sizeof(xhci_device_ctx_t));
659 if (!dev->dev_ctx)
660 goto err;
661 memset(dev->dev_ctx, 0, sizeof(xhci_device_ctx_t));
662
663 hc->dcbaa[dev->slot_id] = addr_to_phys(dev->dev_ctx);
664
665 /* Issue configure endpoint command (sec 4.3.5). */
666 xhci_input_ctx_t *ictx;
667 if ((err = create_valid_input_ctx(&ictx))) {
668 goto err_dev_ctx;
669 }
670
671 /* Initialize slot_ctx according to section 4.3.3 point 3. */
672 XHCI_SLOT_ROOT_HUB_PORT_SET(ictx->slot_ctx, dev->base.port); // FIXME: This should be port at RH
673 XHCI_SLOT_CTX_ENTRIES_SET(ictx->slot_ctx, 1);
674
675 /* Attaching to root hub port, root string equals to 0. */
676 XHCI_SLOT_ROUTE_STRING_SET(ictx->slot_ctx, 0); // FIXME: This is apparently valid in limited cases
677
678 /* Copy endpoint 0 context and set A1 flag. */
679 XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 1);
680 xhci_setup_endpoint_context(ep0, &ictx->endpoint_ctx[0]);
681
682 /* Issue Address Device command. */
683 if ((err = xhci_cmd_sync_inline(hc, ADDRESS_DEVICE, .slot_id = dev->slot_id, .input_ctx = ictx))) {
684 goto err_dev_ctx;
685 }
686
687 dev->base.address = XHCI_SLOT_DEVICE_ADDRESS(dev->dev_ctx->slot_ctx);
688 usb_log_debug2("Obtained USB address: %d.\n", dev->base.address);
689
690 /* From now on, the device is officially online, yay! */
691 fibril_mutex_lock(&dev->base.guard);
692 dev->online = true;
693 fibril_mutex_unlock(&dev->base.guard);
694
695 return EOK;
696
697err_dev_ctx:
698 free32(dev->dev_ctx);
699 hc->dcbaa[dev->slot_id] = 0;
700err:
701 return err;
702}
703
704int hc_configure_device(xhci_hc_t *hc, uint32_t slot_id)
705{
706 int err;
707
708 /* Issue configure endpoint command (sec 4.3.5). */
709 xhci_input_ctx_t *ictx;
710 if ((err = create_valid_input_ctx(&ictx))) {
711 return err;
712 }
713 // TODO: Set slot context and other flags. (probably forgot a lot of 'em)
714
715 if ((err = xhci_cmd_sync_inline(hc, CONFIGURE_ENDPOINT, .slot_id = slot_id, .input_ctx = ictx))) {
716 return err;
717 }
718
719 return EOK;
720}
721
722int hc_deconfigure_device(xhci_hc_t *hc, uint32_t slot_id)
723{
724 int err;
725
726 /* Issue configure endpoint command (sec 4.3.5) with the DC flag. */
727 if ((err = xhci_cmd_sync_inline(hc, CONFIGURE_ENDPOINT, .slot_id = slot_id, .deconfigure = true))) {
728 return err;
729 }
730
731 return EOK;
732}
733
734int hc_add_endpoint(xhci_hc_t *hc, uint32_t slot_id, uint8_t ep_idx, xhci_ep_ctx_t *ep_ctx)
735{
736 int err;
737
738 /* Issue configure endpoint command (sec 4.3.5). */
739 xhci_input_ctx_t *ictx;
740 if ((err = create_valid_input_ctx(&ictx))) {
741 return err;
742 }
743
744 XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, ep_idx + 1); /* Preceded by slot ctx */
745 memcpy(&ictx->endpoint_ctx[ep_idx], ep_ctx, sizeof(xhci_ep_ctx_t));
746 // TODO: Set slot context and other flags. (probably forgot a lot of 'em)
747
748 if ((err = xhci_cmd_sync_inline(hc, CONFIGURE_ENDPOINT, .slot_id = slot_id, .input_ctx = ictx))) {
749 return err;
750 }
751
752 return EOK;
753}
754
755int hc_drop_endpoint(xhci_hc_t *hc, uint32_t slot_id, uint8_t ep_idx)
756{
757 int err;
758
759 /* Issue configure endpoint command (sec 4.3.5). */
760 xhci_input_ctx_t *ictx;
761 if ((err = create_valid_input_ctx(&ictx))) {
762 return err;
763 }
764
765 XHCI_INPUT_CTRL_CTX_DROP_SET(ictx->ctrl_ctx, ep_idx + 1); /* Preceded by slot ctx */
766 // TODO: Set slot context and other flags. (probably forgot a lot of 'em)
767
768 if ((err = xhci_cmd_sync_inline(hc, CONFIGURE_ENDPOINT, .slot_id = slot_id, .input_ctx = ictx))) {
769 return err;
770 }
771
772 return EOK;
773}
774
775/**
776 * @}
777 */
Note: See TracBrowser for help on using the repository browser.