source: mainline/uspace/drv/bus/usb/xhci/commands.c@ d32d51d

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d32d51d was c9bec1c, checked in by Jaroslav Jindrak <dzejrou@…>, 8 years ago

Refactored the add_command function, the return of the cmd was meant to be used for post config if cmd passed into the command sender was NULL, however the only sender that uses this is no_op sender and that has no post config of the command.

  • Property mode set to 100644
File size: 13.6 KB
Line 
1/*
2 * Copyright (c) 2017 Jaroslav Jindrak
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 Command sending functions.
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 "commands.h"
41#include "debug.h"
42#include "hc.h"
43#include "hw_struct/context.h"
44#include "hw_struct/trb.h"
45
46#define TRB_SET_TCS(trb, tcs) (trb).control |= host2xhci(32, ((tcs &0x1) << 9))
47#define TRB_SET_TYPE(trb, type) (trb).control |= host2xhci(32, (type) << 10)
48#define TRB_SET_EP(trb, ep) (trb).control |= host2xhci(32, ((ep) & 0x5) << 16)
49#define TRB_SET_STREAM(trb, st) (trb).control |= host2xhci(32, ((st) & 0xFFFF) << 16)
50#define TRB_SET_SUSP(trb, susp) (trb).control |= host2xhci(32, ((susp) & 0x1) << 23)
51#define TRB_SET_SLOT(trb, slot) (trb).control |= host2xhci(32, (slot) << 24)
52
53/**
54 * TODO: Not sure about SCT and DCS (see section 6.4.3.9).
55 */
56#define TRB_SET_DEQUEUE_PTR(trb, dptr) (trb).parameter |= host2xhci(64, (dptr))
57#define TRB_SET_ICTX(trb, phys) (trb).parameter |= host2xhci(32, phys_addr & (~0xF))
58
59#define TRB_GET_CODE(trb) XHCI_DWORD_EXTRACT((trb).status, 31, 24)
60#define TRB_GET_SLOT(trb) XHCI_DWORD_EXTRACT((trb).control, 31, 24)
61#define TRB_GET_PHYS(trb) (XHCI_QWORD_EXTRACT((trb).parameter, 63, 4) << 4)
62
63int xhci_init_commands(xhci_hc_t *hc)
64{
65 assert(hc);
66
67 list_initialize(&hc->commands);
68 return EOK;
69}
70
71void xhci_fini_commands(xhci_hc_t *hc)
72{
73 // Note: Untested.
74 assert(hc);
75
76 // We assume that the hc is dying/stopping, so we ignore
77 // the ownership of the commands.
78 list_foreach(hc->commands, link, xhci_cmd_t, cmd) {
79 xhci_free_command(cmd);
80 }
81}
82
83int xhci_wait_for_command(xhci_cmd_t *cmd, uint32_t timeout)
84{
85 uint32_t time = 0;
86 while (!cmd->completed) {
87 async_usleep(1000);
88 time += 1000;
89
90 if (time > timeout)
91 return ETIMEOUT;
92 }
93
94 return EOK;
95}
96
97xhci_cmd_t *xhci_alloc_command(void)
98{
99 xhci_cmd_t *cmd = malloc32(sizeof(xhci_cmd_t));
100 memset(cmd, 0, sizeof(xhci_cmd_t));
101
102 link_initialize(&cmd->link);
103
104 /**
105 * Internal functions will set this to false, other are implicit
106 * owners unless they overwrite this field.
107 * TODO: Is this wise?
108 */
109 cmd->has_owner = true;
110
111 return cmd;
112}
113
114void xhci_free_command(xhci_cmd_t *cmd)
115{
116 if (cmd->ictx)
117 free32(cmd->ictx);
118 if (cmd->trb)
119 free32(cmd->trb);
120
121 free32(cmd);
122}
123
124static inline xhci_cmd_t *get_command(xhci_hc_t *hc, uint64_t phys)
125{
126 link_t *cmd_link = list_first(&hc->commands);
127
128 while (cmd_link != NULL) {
129 xhci_cmd_t *cmd = list_get_instance(cmd_link, xhci_cmd_t, link);
130
131 if (addr_to_phys(cmd->trb) == phys)
132 break;
133
134 cmd_link = list_next(cmd_link, &hc->commands);
135 }
136
137 if (cmd_link != NULL) {
138 list_remove(cmd_link);
139
140 return list_get_instance(cmd_link, xhci_cmd_t, link);
141 }
142
143 return NULL;
144}
145
146static inline int ring_doorbell(xhci_hc_t *hc, unsigned doorbell, unsigned target)
147{
148 assert(hc);
149 uint32_t v = host2xhci(32, target & BIT_RRANGE(uint32_t, 7));
150 pio_write_32(&hc->db_arry[doorbell], v);
151 return EOK;
152}
153
154static inline int enqueue_trb(xhci_hc_t *hc, xhci_trb_t *trb,
155 unsigned doorbell, unsigned target)
156{
157 assert(hc);
158 assert(trb);
159
160 xhci_trb_ring_enqueue(&hc->command_ring, trb);
161 ring_doorbell(hc, doorbell, target);
162
163 xhci_dump_trb(trb);
164 usb_log_debug2("HC(%p): Sent TRB", hc);
165
166 return EOK;
167}
168
169static inline int add_cmd(xhci_hc_t *hc, xhci_cmd_t *cmd)
170{
171 if (cmd == NULL) {
172 cmd = xhci_alloc_command();
173 if (cmd == NULL)
174 return ENOMEM;
175
176 cmd->has_owner = false;
177 }
178
179 list_append(&cmd->link, &hc->commands);
180 cmd->trb = hc->command_ring.enqueue_trb;
181
182 return EOK;
183}
184
185void xhci_stop_command_ring(xhci_hc_t *hc)
186{
187 assert(hc);
188
189 XHCI_REG_SET(hc->op_regs, XHCI_OP_CS, 1);
190
191 /**
192 * Note: There is a bug in qemu that checks CS only when CRCR_HI
193 * is written, this (and the read/write in abort) ensures
194 * the command rings stops.
195 */
196 XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_HI, XHCI_REG_RD(hc->op_regs, XHCI_OP_CRCR_HI));
197}
198
199void xhci_abort_command_ring(xhci_hc_t *hc)
200{
201 assert(hc);
202
203 XHCI_REG_WR(hc->op_regs, XHCI_OP_CA, 1);
204 XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_HI, XHCI_REG_RD(hc->op_regs, XHCI_OP_CRCR_HI));
205}
206
207void xhci_start_command_ring(xhci_hc_t *hc)
208{
209 assert(hc);
210
211 XHCI_REG_WR(hc->op_regs, XHCI_OP_CRR, 1);
212 ring_doorbell(hc, 0, 0);
213}
214
215static const char *trb_codes [] = {
216#define TRBC(t) [XHCI_TRBC_##t] = #t
217 TRBC(INVALID),
218 TRBC(SUCCESS),
219 TRBC(DATA_BUFFER_ERROR),
220 TRBC(BABBLE_DETECTED_ERROR),
221 TRBC(USB_TRANSACTION_ERROR),
222 TRBC(TRB_ERROR),
223 TRBC(STALL_ERROR),
224 TRBC(RESOURCE_ERROR),
225 TRBC(BANDWIDTH_ERROR),
226 TRBC(NO_SLOTS_ERROR),
227 TRBC(INVALID_STREAM_ERROR),
228 TRBC(SLOT_NOT_ENABLED_ERROR),
229 TRBC(EP_NOT_ENABLED_ERROR),
230 TRBC(SHORT_PACKET),
231 TRBC(RING_UNDERRUN),
232 TRBC(RING_OVERRUN),
233 TRBC(VF_EVENT_RING_FULL),
234 TRBC(PARAMETER_ERROR),
235 TRBC(BANDWIDTH_OVERRUN_ERROR),
236 TRBC(CONTEXT_STATE_ERROR),
237 TRBC(NO_PING_RESPONSE_ERROR),
238 TRBC(EVENT_RING_FULL_ERROR),
239 TRBC(INCOMPATIBLE_DEVICE_ERROR),
240 TRBC(MISSED_SERVICE_ERROR),
241 TRBC(COMMAND_RING_STOPPED),
242 TRBC(COMMAND_ABORTED),
243 TRBC(STOPPED),
244 TRBC(STOPPED_LENGTH_INVALID),
245 TRBC(STOPPED_SHORT_PACKET),
246 TRBC(MAX_EXIT_LATENCY_TOO_LARGE_ERROR),
247 [30] = "<reserved>",
248 TRBC(ISOCH_BUFFER_OVERRUN),
249 TRBC(EVENT_LOST_ERROR),
250 TRBC(UNDEFINED_ERROR),
251 TRBC(INVALID_STREAM_ID_ERROR),
252 TRBC(SECONDARY_BANDWIDTH_ERROR),
253 TRBC(SPLIT_TRANSACTION_ERROR),
254 [XHCI_TRBC_MAX] = NULL
255#undef TRBC
256};
257
258static void report_error(int code)
259{
260 if (code < XHCI_TRBC_MAX && trb_codes[code] != NULL)
261 usb_log_error("Command resulted in error: %s.", trb_codes[code]);
262 else
263 usb_log_error("Command resulted in reserved or vendor specific error.");
264}
265
266int xhci_send_no_op_command(xhci_hc_t *hc, xhci_cmd_t *cmd)
267{
268 assert(hc);
269
270 xhci_trb_t trb;
271 memset(&trb, 0, sizeof(trb));
272
273 TRB_SET_TYPE(trb, XHCI_TRB_TYPE_NO_OP_CMD);
274
275 add_cmd(hc, cmd);
276
277 return enqueue_trb(hc, &trb, 0, 0);
278}
279
280int xhci_send_enable_slot_command(xhci_hc_t *hc, xhci_cmd_t *cmd)
281{
282 assert(hc);
283
284 xhci_trb_t trb;
285 memset(&trb, 0, sizeof(trb));
286
287 TRB_SET_TYPE(trb, XHCI_TRB_TYPE_ENABLE_SLOT_CMD);
288 trb.control |= host2xhci(32, XHCI_REG_RD(hc->xecp, XHCI_EC_SP_SLOT_TYPE) << 16);
289
290 add_cmd(hc, cmd);
291
292 return enqueue_trb(hc, &trb, 0, 0);
293}
294
295int xhci_send_disable_slot_command(xhci_hc_t *hc, xhci_cmd_t *cmd)
296{
297 assert(hc);
298 assert(cmd);
299
300 xhci_trb_t trb;
301 memset(&trb, 0, sizeof(trb));
302
303 TRB_SET_TYPE(trb, XHCI_TRB_TYPE_DISABLE_SLOT_CMD);
304 TRB_SET_SLOT(trb, cmd->slot_id);
305
306 add_cmd(hc, cmd);
307
308 return enqueue_trb(hc, &trb, 0, 0);
309}
310
311int xhci_send_address_device_command(xhci_hc_t *hc, xhci_cmd_t *cmd)
312{
313 assert(hc);
314 assert(cmd);
315 assert(cmd->ictx);
316
317 /**
318 * TODO: Requirements for this command:
319 * dcbaa[slot_id] is properly sized and initialized
320 * ictx has valids slot context and endpoint 0, all
321 * other should be ignored at this point (see section 4.6.5).
322 */
323 xhci_trb_t trb;
324 memset(&trb, 0, sizeof(trb));
325
326 uint64_t phys_addr = (uint64_t) addr_to_phys(cmd->ictx);
327 TRB_SET_ICTX(trb, phys_addr);
328
329 /**
330 * Note: According to section 6.4.3.4, we can set the 9th bit
331 * of the control field of the trb (BSR) to 1 and then the xHC
332 * will not issue the SET_ADDRESS request to the USB device.
333 * This can be used to provide compatibility with legacy USB devices
334 * that require their device descriptor to be read before such request.
335 */
336 TRB_SET_TYPE(trb, XHCI_TRB_TYPE_ADDRESS_DEVICE_CMD);
337 TRB_SET_SLOT(trb, cmd->slot_id);
338
339 add_cmd(hc, cmd);
340
341 return enqueue_trb(hc, &trb, 0, 0);
342}
343
344int xhci_send_configure_endpoint_command(xhci_hc_t *hc, xhci_cmd_t *cmd)
345{
346 assert(hc);
347 assert(cmd);
348 assert(cmd->ictx);
349
350 xhci_trb_t trb;
351 memset(&trb, 0, sizeof(trb));
352
353 uint64_t phys_addr = (uint64_t) addr_to_phys(cmd->ictx);
354 TRB_SET_ICTX(trb, phys_addr);
355
356 TRB_SET_TYPE(trb, XHCI_TRB_TYPE_CONFIGURE_ENDPOINT_CMD);
357 TRB_SET_SLOT(trb, cmd->slot_id);
358
359 add_cmd(hc, cmd);
360
361 return enqueue_trb(hc, &trb, 0, 0);
362}
363
364int xhci_send_evaluate_context_command(xhci_hc_t *hc, xhci_cmd_t *cmd)
365{
366 assert(hc);
367 assert(cmd);
368 assert(cmd->ictx);
369
370 /**
371 * Note: All Drop Context flags of the input context shall be 0,
372 * all Add Context flags shall be initialize to indicate IDs
373 * of the contexts affected by the command.
374 * Refer to sections 6.2.2.3 and 6.3.3.3 for further info.
375 */
376 xhci_trb_t trb;
377 memset(&trb, 0, sizeof(trb));
378
379 uint64_t phys_addr = (uint64_t) addr_to_phys(cmd->ictx);
380 TRB_SET_ICTX(trb, phys_addr);
381
382 TRB_SET_TYPE(trb, XHCI_TRB_TYPE_EVALUATE_CONTEXT_CMD);
383 TRB_SET_SLOT(trb, cmd->slot_id);
384
385 add_cmd(hc, cmd);
386
387 return enqueue_trb(hc, &trb, 0, 0);
388}
389
390int xhci_send_reset_endpoint_command(xhci_hc_t *hc, xhci_cmd_t *cmd, uint32_t ep_id, uint8_t tcs)
391{
392 assert(hc);
393 assert(cmd);
394
395 /**
396 * Note: TCS can have values 0 or 1. If it is set to 0, see sectuon 4.5.8 for
397 * information about this flag.
398 */
399 xhci_trb_t trb;
400 memset(&trb, 0, sizeof(trb));
401
402 TRB_SET_TYPE(trb, XHCI_TRB_TYPE_RESET_ENDPOINT_CMD);
403 TRB_SET_TCS(trb, tcs);
404 TRB_SET_EP(trb, ep_id);
405 TRB_SET_SLOT(trb, cmd->slot_id);
406
407 add_cmd(hc, cmd);
408
409 return enqueue_trb(hc, &trb, 0, 0);
410}
411
412int xhci_send_stop_endpoint_command(xhci_hc_t *hc, xhci_cmd_t *cmd, uint32_t ep_id, uint8_t susp)
413{
414 assert(hc);
415 assert(cmd);
416
417 xhci_trb_t trb;
418 memset(&trb, 0, sizeof(trb));
419
420 TRB_SET_TYPE(trb, XHCI_TRB_TYPE_STOP_ENDPOINT_CMD);
421 TRB_SET_EP(trb, ep_id);
422 TRB_SET_SUSP(trb, susp);
423 TRB_SET_SLOT(trb, cmd->slot_id);
424
425 add_cmd(hc, cmd);
426
427 return enqueue_trb(hc, &trb, 0, 0);
428}
429
430int xhci_send_set_dequeue_ptr_command(xhci_hc_t *hc, xhci_cmd_t *cmd,
431 uintptr_t dequeue_ptr, uint16_t stream_id,
432 uint32_t ep_id)
433{
434 assert(hc);
435 assert(cmd);
436
437 xhci_trb_t trb;
438 memset(&trb, 0, sizeof(trb));
439
440 TRB_SET_TYPE(trb, XHCI_TRB_TYPE_SET_TR_DEQUEUE_POINTER_CMD);
441 TRB_SET_EP(trb, ep_id);
442 TRB_SET_STREAM(trb, stream_id);
443 TRB_SET_SLOT(trb, cmd->slot_id);
444 TRB_SET_DEQUEUE_PTR(trb, dequeue_ptr);
445
446 /**
447 * TODO: Set DCS (see section 4.6.10).
448 */
449
450 add_cmd(hc, cmd);
451
452 return enqueue_trb(hc, &trb, 0, 0);
453}
454
455int xhci_send_reset_device_command(xhci_hc_t *hc, xhci_cmd_t *cmd)
456{
457 assert(hc);
458 assert(cmd);
459
460 xhci_trb_t trb;
461 memset(&trb, 0, sizeof(trb));
462
463 TRB_SET_TYPE(trb, XHCI_TRB_TYPE_RESET_DEVICE_CMD);
464 TRB_SET_SLOT(trb, cmd->slot_id);
465
466 add_cmd(hc, cmd);
467
468 return enqueue_trb(hc, &trb, 0, 0);
469}
470
471int xhci_handle_command_completion(xhci_hc_t *hc, xhci_trb_t *trb)
472{
473 // TODO: Update dequeue ptrs.
474 assert(hc);
475 assert(trb);
476
477 usb_log_debug("HC(%p) Command completed.", hc);
478
479 int code;
480 uint64_t phys;
481 xhci_cmd_t *command;
482 xhci_trb_t *command_trb;
483
484 code = TRB_GET_CODE(*trb);
485 phys = TRB_GET_PHYS(*trb);;
486 command = get_command(hc, phys);
487 if (command == NULL) {
488 // TODO: STOP & ABORT may not have command structs in the list!
489 usb_log_error("No command struct for this completion event");
490
491 if (code != XHCI_TRBC_SUCCESS)
492 report_error(code);
493
494 return EOK;
495 }
496
497 command_trb = command->trb;
498 command->status = code;
499 command->slot_id = TRB_GET_SLOT(*trb);
500
501 usb_log_debug2("Completed command trb: %s", xhci_trb_str_type(TRB_TYPE(*command_trb)));
502 if (TRB_TYPE(*command_trb) != XHCI_TRB_TYPE_NO_OP_CMD) {
503 if (code != XHCI_TRBC_SUCCESS) {
504 report_error(code);
505 xhci_dump_trb(command_trb);
506 }
507 }
508
509 switch (TRB_TYPE(*command_trb)) {
510 case XHCI_TRB_TYPE_NO_OP_CMD:
511 assert(code == XHCI_TRBC_TRB_ERROR);
512 break;
513 case XHCI_TRB_TYPE_ENABLE_SLOT_CMD:
514 break;
515 case XHCI_TRB_TYPE_DISABLE_SLOT_CMD:
516 break;
517 case XHCI_TRB_TYPE_ADDRESS_DEVICE_CMD:
518 break;
519 case XHCI_TRB_TYPE_CONFIGURE_ENDPOINT_CMD:
520 break;
521 case XHCI_TRB_TYPE_EVALUATE_CONTEXT_CMD:
522 break;
523 case XHCI_TRB_TYPE_RESET_ENDPOINT_CMD:
524 break;
525 case XHCI_TRB_TYPE_STOP_ENDPOINT_CMD:
526 // Note: If the endpoint was in the middle of a transfer, then the xHC
527 // will add a Transfer TRB before the Event TRB, research that and
528 // handle it appropriately!
529 break;
530 case XHCI_TRB_TYPE_RESET_DEVICE_CMD:
531 break;
532 default:
533 usb_log_debug2("Unsupported command trb: %s", xhci_trb_str_type(TRB_TYPE(*command_trb)));
534
535 command->completed = true;
536 return ENAK;
537 }
538
539 command->completed = true;
540
541 if (!command->has_owner) {
542 usb_log_debug2("Command has no owner, deallocating.");
543 command->trb = NULL; // It was statically allocated.
544 xhci_free_command(command);
545 } else {
546 usb_log_debug2("Command has owner, don't forget to deallocate!");
547 /* Copy the trb for later use so that we can free space on the cmd ring. */
548 command->trb = malloc32(sizeof(xhci_trb_t));
549 xhci_trb_copy(command->trb, command_trb);
550 }
551
552 return EOK;
553}
554
555
556/**
557 * @}
558 */
Note: See TracBrowser for help on using the repository browser.