source: mainline/uspace/drv/bus/usb/xhci/transfers.c@ fa4b12d5

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fa4b12d5 was 4db49344, checked in by Ondřej Hlavatý <aearsis@…>, 8 years ago

usb: fix wrong design of transfer aborting

Apparently, we didn't do a good job in thinking through the problem.
In older HCs, it was done just wrong - the UHCI implementation commited
a batch that could have been already aborted, and EHCI+OHCI might miss
an interrupt because they commited the batch sooner than they added it
to their checked list.

This commit takes everything from the other end, which is probably the
only right one. Instead of an endpoint having an extra mutex, it
inherits a mutex from the outside. It never locks it though, it just
checks if the mutex is locked and uses it for waiting on condition
variables.

This mutex is supposed to be the one which the HC driver uses for
locking its structures in scheduling. This way, we avoid the ABBA
deadlock completely, while preserving the synchronization on an
endpoint.

The good thing is that this implementation is much easier to extend with
multiple active batches per endpoint.

  • Property mode set to 100644
File size: 14.4 KB
Line 
1/*
2 * Copyright (c) 2017 Michal Staruch
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 transfer ring management
34 */
35
36#include <usb/debug.h>
37#include <usb/request.h>
38#include "endpoint.h"
39#include "hc.h"
40#include "hw_struct/trb.h"
41#include "streams.h"
42#include "transfers.h"
43#include "trb_ring.h"
44
45typedef enum {
46 STAGE_OUT,
47 STAGE_IN,
48} stage_dir_flag_t;
49
50#define REQUEST_TYPE_DTD (0x80)
51#define REQUEST_TYPE_IS_DEVICE_TO_HOST(rq) ((rq) & REQUEST_TYPE_DTD)
52
53
54/** Get direction flag of data stage.
55 * See Table 7 of xHCI specification.
56 */
57static inline stage_dir_flag_t get_status_direction_flag(xhci_trb_t* trb,
58 uint8_t bmRequestType, uint16_t wLength)
59{
60 /* See Table 7 of xHCI specification */
61 return REQUEST_TYPE_IS_DEVICE_TO_HOST(bmRequestType) && (wLength > 0)
62 ? STAGE_OUT
63 : STAGE_IN;
64}
65
66typedef enum {
67 DATA_STAGE_NO = 0,
68 DATA_STAGE_OUT = 2,
69 DATA_STAGE_IN = 3,
70} data_stage_type_t;
71
72/** Get transfer type flag.
73 * See Table 8 of xHCI specification.
74 */
75static inline data_stage_type_t get_transfer_type(xhci_trb_t* trb, uint8_t
76 bmRequestType, uint16_t wLength)
77{
78 if (wLength == 0)
79 return DATA_STAGE_NO;
80
81 /* See Table 7 of xHCI specification */
82 return REQUEST_TYPE_IS_DEVICE_TO_HOST(bmRequestType)
83 ? DATA_STAGE_IN
84 : DATA_STAGE_NO;
85}
86
87static inline bool configure_endpoint_needed(usb_device_request_setup_packet_t *setup)
88{
89 usb_request_type_t request_type = SETUP_REQUEST_TYPE_GET_TYPE(setup->request_type);
90
91 return request_type == USB_REQUEST_TYPE_STANDARD &&
92 (setup->request == USB_DEVREQ_SET_CONFIGURATION
93 || setup->request == USB_DEVREQ_SET_INTERFACE);
94}
95
96/**
97 * Create a xHCI-specific transfer batch.
98 *
99 * Bus callback.
100 */
101usb_transfer_batch_t * xhci_transfer_create(endpoint_t* ep)
102{
103 xhci_transfer_t *transfer = calloc(1, sizeof(xhci_transfer_t));
104 if (!transfer)
105 return NULL;
106
107 usb_transfer_batch_init(&transfer->batch, ep);
108 return &transfer->batch;
109}
110
111/**
112 * Destroy a xHCI transfer.
113 */
114void xhci_transfer_destroy(usb_transfer_batch_t* batch)
115{
116 xhci_transfer_t *transfer = xhci_transfer_from_batch(batch);
117
118 dma_buffer_free(&transfer->hc_buffer);
119 free(transfer);
120}
121
122static xhci_trb_ring_t *get_ring(xhci_transfer_t *transfer)
123{
124 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(transfer->batch.ep);
125 return xhci_endpoint_get_ring(xhci_ep, transfer->batch.target.stream);
126}
127
128static int schedule_control(xhci_hc_t* hc, xhci_transfer_t* transfer)
129{
130 usb_transfer_batch_t *batch = &transfer->batch;
131 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(transfer->batch.ep);
132
133 usb_device_request_setup_packet_t* setup = &batch->setup.packet;
134
135 xhci_trb_t trbs[3];
136 int trbs_used = 0;
137
138 xhci_trb_t *trb_setup = trbs + trbs_used++;
139 xhci_trb_clean(trb_setup);
140
141 TRB_CTRL_SET_SETUP_WVALUE(*trb_setup, setup->value);
142 TRB_CTRL_SET_SETUP_WLENGTH(*trb_setup, setup->length);
143 TRB_CTRL_SET_SETUP_WINDEX(*trb_setup, setup->index);
144 TRB_CTRL_SET_SETUP_BREQ(*trb_setup, setup->request);
145 TRB_CTRL_SET_SETUP_BMREQTYPE(*trb_setup, setup->request_type);
146
147 /* Size of the setup packet is always 8 */
148 TRB_CTRL_SET_XFER_LEN(*trb_setup, 8);
149
150 /* Immediate data */
151 TRB_CTRL_SET_IDT(*trb_setup, 1);
152 TRB_CTRL_SET_TRB_TYPE(*trb_setup, XHCI_TRB_TYPE_SETUP_STAGE);
153 TRB_CTRL_SET_TRT(*trb_setup,
154 get_transfer_type(trb_setup, setup->request_type, setup->length));
155
156 /* Data stage */
157 xhci_trb_t *trb_data = NULL;
158 if (setup->length > 0) {
159 trb_data = trbs + trbs_used++;
160 xhci_trb_clean(trb_data);
161
162 trb_data->parameter = host2xhci(64, transfer->hc_buffer.phys);
163
164 // data size (sent for OUT, or buffer size)
165 TRB_CTRL_SET_XFER_LEN(*trb_data, batch->buffer_size);
166 // FIXME: TD size 4.11.2.4
167 TRB_CTRL_SET_TD_SIZE(*trb_data, 1);
168
169 // Some more fields here, no idea what they mean
170 TRB_CTRL_SET_TRB_TYPE(*trb_data, XHCI_TRB_TYPE_DATA_STAGE);
171
172 int stage_dir = REQUEST_TYPE_IS_DEVICE_TO_HOST(setup->request_type)
173 ? STAGE_IN : STAGE_OUT;
174 TRB_CTRL_SET_DIR(*trb_data, stage_dir);
175 }
176
177 /* Status stage */
178 xhci_trb_t *trb_status = trbs + trbs_used++;
179 xhci_trb_clean(trb_status);
180
181 TRB_CTRL_SET_IOC(*trb_status, 1);
182 TRB_CTRL_SET_TRB_TYPE(*trb_status, XHCI_TRB_TYPE_STATUS_STAGE);
183 TRB_CTRL_SET_DIR(*trb_status, get_status_direction_flag(trb_setup,
184 setup->request_type, setup->length));
185
186 // Issue a Configure Endpoint command, if needed.
187 if (configure_endpoint_needed(setup)) {
188 const int err = hc_configure_device(xhci_ep_to_dev(xhci_ep));
189 if (err)
190 return err;
191 }
192
193 return xhci_trb_ring_enqueue_multiple(get_ring(transfer), trbs,
194 trbs_used, &transfer->interrupt_trb_phys);
195}
196
197static int schedule_bulk(xhci_hc_t* hc, xhci_transfer_t *transfer)
198{
199 xhci_trb_t trb;
200 xhci_trb_clean(&trb);
201 trb.parameter = host2xhci(64, transfer->hc_buffer.phys);
202
203 // data size (sent for OUT, or buffer size)
204 TRB_CTRL_SET_XFER_LEN(trb, transfer->batch.buffer_size);
205
206 /* The stream-enabled endpoints need to chain ED trb */
207 xhci_endpoint_t *ep = xhci_endpoint_get(transfer->batch.ep);
208 if (!ep->primary_stream_data_size) {
209 // FIXME: TD size 4.11.2.4
210 TRB_CTRL_SET_TD_SIZE(trb, 1);
211
212 // we want an interrupt after this td is done
213 TRB_CTRL_SET_IOC(trb, 1);
214 TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
215
216 xhci_trb_ring_t* ring = get_ring(transfer);
217 return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
218 }
219 else {
220 TRB_CTRL_SET_TD_SIZE(trb, 2);
221 TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
222 TRB_CTRL_SET_CHAIN(trb, 1);
223 TRB_CTRL_SET_ENT(trb, 1);
224
225 xhci_trb_ring_t* ring = get_ring(transfer);
226 if (!ring) {
227 return EINVAL;
228 }
229
230 int err = xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
231
232 if (err) {
233 return err;
234 }
235
236 xhci_trb_clean(&trb);
237 trb.parameter = host2xhci(64, (uintptr_t) transfer);
238 TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_EVENT_DATA);
239 TRB_CTRL_SET_IOC(trb, 1);
240
241 return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
242 }
243}
244
245static int schedule_interrupt(xhci_hc_t* hc, xhci_transfer_t* transfer)
246{
247 xhci_trb_t trb;
248 xhci_trb_clean(&trb);
249 trb.parameter = host2xhci(64, transfer->hc_buffer.phys);
250
251 // data size (sent for OUT, or buffer size)
252 TRB_CTRL_SET_XFER_LEN(trb, transfer->batch.buffer_size);
253 // FIXME: TD size 4.11.2.4
254 TRB_CTRL_SET_TD_SIZE(trb, 1);
255
256 // we want an interrupt after this td is done
257 TRB_CTRL_SET_IOC(trb, 1);
258
259 TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
260
261 xhci_trb_ring_t* ring = get_ring(transfer);
262
263 return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
264}
265
266static int schedule_isochronous(xhci_transfer_t* transfer)
267{
268 endpoint_t *ep = transfer->batch.ep;
269
270 return ep->direction == USB_DIRECTION_OUT
271 ? isoch_schedule_out(transfer)
272 : isoch_schedule_in(transfer);
273}
274
275int xhci_handle_transfer_event(xhci_hc_t* hc, xhci_trb_t* trb)
276{
277 uintptr_t addr = trb->parameter;
278 const unsigned slot_id = XHCI_DWORD_EXTRACT(trb->control, 31, 24);
279 const unsigned ep_dci = XHCI_DWORD_EXTRACT(trb->control, 20, 16);
280
281 xhci_device_t *dev = hc->bus.devices_by_slot[slot_id];
282 if (!dev) {
283 usb_log_error("Transfer event on disabled slot %u", slot_id);
284 return ENOENT;
285 }
286
287 const usb_endpoint_t ep_num = ep_dci / 2;
288 const usb_endpoint_t dir = ep_dci % 2 ? USB_DIRECTION_IN : USB_DIRECTION_OUT;
289 /* Creating temporary reference */
290 endpoint_t *ep_base = bus_find_endpoint(&dev->base, ep_num, dir);
291 if (!ep_base) {
292 usb_log_error("Transfer event on dropped endpoint %u %s of device "
293 XHCI_DEV_FMT, ep_num, usb_str_direction(dir), XHCI_DEV_ARGS(*dev));
294 return ENOENT;
295 }
296 xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
297
298 usb_transfer_batch_t *batch;
299 xhci_transfer_t *transfer;
300
301 if (TRB_EVENT_DATA(*trb)) {
302 /* We schedule those only when streams are involved */
303 assert(ep->primary_stream_ctx_array != NULL);
304
305 /* We are received transfer pointer instead - work with that */
306 transfer = (xhci_transfer_t *) addr;
307 xhci_trb_ring_update_dequeue(get_ring(transfer),
308 transfer->interrupt_trb_phys);
309 batch = &transfer->batch;
310 }
311 else {
312 xhci_trb_ring_update_dequeue(&ep->ring, addr);
313
314 if (ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS) {
315 isoch_handle_transfer_event(hc, ep, trb);
316 /* Dropping temporary reference */
317 endpoint_del_ref(&ep->base);
318 return EOK;
319 }
320
321 fibril_mutex_lock(&ep->guard);
322 batch = ep->base.active_batch;
323 endpoint_deactivate_locked(&ep->base);
324 fibril_mutex_unlock(&ep->guard);
325
326 if (!batch) {
327 /* Dropping temporary reference */
328 endpoint_del_ref(&ep->base);
329 return ENOENT;
330 }
331
332 transfer = xhci_transfer_from_batch(batch);
333 }
334
335 const xhci_trb_completion_code_t completion_code = TRB_COMPLETION_CODE(*trb);
336 switch (completion_code) {
337 case XHCI_TRBC_SHORT_PACKET:
338 case XHCI_TRBC_SUCCESS:
339 batch->error = EOK;
340 batch->transferred_size = batch->buffer_size - TRB_TRANSFER_LENGTH(*trb);
341 break;
342
343 case XHCI_TRBC_DATA_BUFFER_ERROR:
344 usb_log_warning("Transfer ended with data buffer error.");
345 batch->error = EAGAIN;
346 batch->transferred_size = 0;
347 break;
348
349 case XHCI_TRBC_BABBLE_DETECTED_ERROR:
350 usb_log_warning("Babble detected during the transfer.");
351 batch->error = EAGAIN;
352 batch->transferred_size = 0;
353 break;
354
355 case XHCI_TRBC_USB_TRANSACTION_ERROR:
356 usb_log_warning("USB Transaction error.");
357 batch->error = ESTALL;
358 batch->transferred_size = 0;
359 break;
360
361 case XHCI_TRBC_TRB_ERROR:
362 usb_log_error("Invalid transfer parameters.");
363 batch->error = EINVAL;
364 batch->transferred_size = 0;
365 break;
366
367 case XHCI_TRBC_STALL_ERROR:
368 usb_log_warning("Stall condition detected.");
369 batch->error = ESTALL;
370 batch->transferred_size = 0;
371 break;
372
373 case XHCI_TRBC_SPLIT_TRANSACTION_ERROR:
374 usb_log_error("Split transcation error detected.");
375 batch->error = EAGAIN;
376 batch->transferred_size = 0;
377 break;
378
379 default:
380 usb_log_warning("Transfer not successfull: %u", completion_code);
381 batch->error = EIO;
382 }
383
384 if (batch->dir == USB_DIRECTION_IN) {
385 assert(batch->buffer);
386 assert(batch->transferred_size <= batch->buffer_size);
387 memcpy(batch->buffer, transfer->hc_buffer.virt, batch->transferred_size);
388 }
389
390 usb_transfer_batch_finish(batch);
391 /* Dropping temporary reference */
392 endpoint_del_ref(&ep->base);
393 return EOK;
394}
395
396typedef int (*transfer_handler)(xhci_hc_t *, xhci_transfer_t *);
397
398static const transfer_handler transfer_handlers[] = {
399 [USB_TRANSFER_CONTROL] = schedule_control,
400 [USB_TRANSFER_ISOCHRONOUS] = NULL,
401 [USB_TRANSFER_BULK] = schedule_bulk,
402 [USB_TRANSFER_INTERRUPT] = schedule_interrupt,
403};
404
405/**
406 * Schedule a batch for xHC.
407 *
408 * Bus callback.
409 */
410int xhci_transfer_schedule(usb_transfer_batch_t *batch)
411{
412 endpoint_t *ep = batch->ep;
413
414 xhci_hc_t *hc = bus_to_hc(endpoint_get_bus(batch->ep));
415 xhci_transfer_t *transfer = xhci_transfer_from_batch(batch);
416 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
417 xhci_device_t *xhci_dev = xhci_ep_to_dev(xhci_ep);
418
419 if (!batch->target.address) {
420 usb_log_error("Attempted to schedule transfer to address 0.");
421 return EINVAL;
422 }
423
424 // FIXME: find a better way to check if the ring is not initialized
425 if (!xhci_ep->ring.segment_count) {
426 usb_log_error("Ring not initialized for endpoint " XHCI_EP_FMT,
427 XHCI_EP_ARGS(*xhci_ep));
428 return EINVAL;
429 }
430
431 // Isochronous transfer needs to be handled differently
432 if (batch->ep->transfer_type == USB_TRANSFER_ISOCHRONOUS) {
433 return schedule_isochronous(transfer);
434 }
435
436 const usb_transfer_type_t type = batch->ep->transfer_type;
437 assert(transfer_handlers[type]);
438
439 if (batch->buffer_size > 0) {
440 if (dma_buffer_alloc(&transfer->hc_buffer, batch->buffer_size))
441 return ENOMEM;
442 }
443
444 if (batch->dir != USB_DIRECTION_IN) {
445 // Sending stuff from host to device, we need to copy the actual data.
446 memcpy(transfer->hc_buffer.virt, batch->buffer, batch->buffer_size);
447 }
448
449 /*
450 * If this is a ClearFeature(ENDPOINT_HALT) request, we have to issue
451 * the Reset Endpoint command.
452 */
453 if (batch->ep->transfer_type == USB_TRANSFER_CONTROL
454 && batch->dir == USB_DIRECTION_OUT) {
455 const usb_device_request_setup_packet_t *request = &batch->setup.packet;
456 if (request->request == USB_DEVREQ_CLEAR_FEATURE
457 && request->request_type == USB_REQUEST_RECIPIENT_ENDPOINT
458 && request->value == USB_FEATURE_ENDPOINT_HALT) {
459 const uint16_t index = uint16_usb2host(request->index);
460 const usb_endpoint_t ep_num = index & 0xf;
461 const usb_direction_t dir = (index >> 7)
462 ? USB_DIRECTION_IN
463 : USB_DIRECTION_OUT;
464 endpoint_t *halted_ep = bus_find_endpoint(&xhci_dev->base, ep_num, dir);
465 if (halted_ep) {
466 /*
467 * TODO: Find out how to come up with stream_id. It might be
468 * possible that we have to clear all of them.
469 */
470 xhci_endpoint_clear_halt(xhci_endpoint_get(halted_ep), 0);
471 endpoint_del_ref(halted_ep);
472 } else {
473 usb_log_warning("Device(%u): Resetting unregistered endpoint"
474 " %u %s.", xhci_dev->base.address, ep_num,
475 usb_str_direction(dir));
476 }
477 }
478 }
479
480
481 int err;
482 fibril_mutex_lock(&xhci_ep->guard);
483
484 if ((err = endpoint_activate_locked(ep, batch))) {
485 fibril_mutex_unlock(&xhci_ep->guard);
486 return err;
487 }
488
489 if ((err = transfer_handlers[batch->ep->transfer_type](hc, transfer))) {
490 endpoint_deactivate_locked(ep);
491 fibril_mutex_unlock(&xhci_ep->guard);
492 return err;
493 }
494
495 hc_ring_ep_doorbell(xhci_ep, batch->target.stream);
496 fibril_mutex_unlock(&xhci_ep->guard);
497 return EOK;
498}
Note: See TracBrowser for help on using the repository browser.