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

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

Added setter/getter macro for command trbs to eliminate repeated literals identifying bit ranges/flags.

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