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

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

Overhauled command system, not a special structure is passed that can be kept and polled for completion.

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