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

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

Added command list deallocation during hc finalization.

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