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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d33dc780 was 9620a54, checked in by Petr Manek <petr.manek@…>, 8 years ago

Small changes. Temporarily fixed no device problem for endpoint logging. Added similar macro for device logging. Changed log messages to adopt these macros. TRB rings can be freed again. Made ring finalizers noexcept. Upon detach, the entire slot is disabled prior to unregistering endpoints in order to prevent invalid HC commands. Removed active endpoints count from XHCI device. Device context is freed in HC, so DCBAA is not touched from anywhere else.

  • Property mode set to 100644
File size: 10.5 KB
RevLine 
[e9e24f2]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/host/utils/malloc32.h>
37#include <usb/debug.h>
[dcf0597]38#include <usb/request.h>
[41924f30]39#include "endpoint.h"
[e9e24f2]40#include "hc.h"
41#include "hw_struct/trb.h"
42#include "transfers.h"
43#include "trb_ring.h"
44
[dbf32b1]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
[e9e24f2]53
[dbf32b1]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)
[e9e24f2]59{
60 /* See Table 7 of xHCI specification */
[dbf32b1]61 return REQUEST_TYPE_IS_DEVICE_TO_HOST(bmRequestType) && (wLength > 0)
62 ? STAGE_OUT
63 : STAGE_IN;
[e9e24f2]64}
65
[dbf32b1]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)
[e9e24f2]77{
[dbf32b1]78 if (wLength == 0)
79 return DATA_STAGE_NO;
80
[e9e24f2]81 /* See Table 7 of xHCI specification */
[dbf32b1]82 return REQUEST_TYPE_IS_DEVICE_TO_HOST(bmRequestType)
83 ? DATA_STAGE_IN
84 : DATA_STAGE_NO;
[e9e24f2]85}
86
[d7869d7e]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
[dbf32b1]91 return request_type == USB_REQUEST_TYPE_STANDARD &&
92 (setup->request == USB_DEVREQ_SET_CONFIGURATION
93 || setup->request == USB_DEVREQ_SET_INTERFACE);
[d7869d7e]94}
95
[2b61945]96/**
97 * There can currently be only one active transfer, because
98 * usb_transfer_batch_init locks the endpoint by endpoint_use.
99 * Therefore, we store the only active transfer per endpoint there.
100 */
101xhci_transfer_t* xhci_transfer_create(endpoint_t* ep)
[e9e24f2]102{
[2b61945]103 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
104 xhci_transfer_t *transfer = &xhci_ep->active_transfer;
[e9e24f2]105
[2b61945]106 /* Do not access the transfer yet, it may be still in use. */
[e9e24f2]107
[5fd9c30]108 usb_transfer_batch_init(&transfer->batch, ep);
[2b61945]109 assert(ep->active);
[5fd9c30]110
[2b61945]111 /* Clean just our data. */
112 memset(((void *) transfer) + sizeof(usb_transfer_batch_t), 0,
113 sizeof(xhci_transfer_t) - sizeof(usb_transfer_batch_t));
[7e74911]114
[e9e24f2]115 return transfer;
116}
117
[5fd9c30]118void xhci_transfer_destroy(xhci_transfer_t* transfer)
119{
120 assert(transfer);
[2770b66]121
[5fd9c30]122 if (transfer->hc_buffer)
123 free32(transfer->hc_buffer);
[e9e24f2]124}
125
[5fd9c30]126static xhci_trb_ring_t *get_ring(xhci_hc_t *hc, xhci_transfer_t *transfer)
[e9e24f2]127{
[2b61945]128 return &xhci_endpoint_get(transfer->batch.ep)->ring;
[5fd9c30]129}
[e9e24f2]130
[5fd9c30]131static int schedule_control(xhci_hc_t* hc, xhci_transfer_t* transfer)
132{
133 usb_transfer_batch_t *batch = &transfer->batch;
134 xhci_trb_ring_t *ring = get_ring(hc, transfer);
135 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(transfer->batch.ep);
[2770b66]136
[5fd9c30]137 usb_device_request_setup_packet_t* setup = &batch->setup.packet;
[e9e24f2]138
[5fd9c30]139 xhci_trb_t trbs[3];
140 int trbs_used = 0;
[e9e24f2]141
[d1d7a92]142 xhci_trb_t *trb_setup = trbs + trbs_used++;
143 xhci_trb_clean(trb_setup);
144
145 TRB_CTRL_SET_SETUP_WVALUE(*trb_setup, setup->value);
146 TRB_CTRL_SET_SETUP_WLENGTH(*trb_setup, setup->length);
147 TRB_CTRL_SET_SETUP_WINDEX(*trb_setup, setup->index);
148 TRB_CTRL_SET_SETUP_BREQ(*trb_setup, setup->request);
149 TRB_CTRL_SET_SETUP_BMREQTYPE(*trb_setup, setup->request_type);
[e9e24f2]150
[eaf5e86]151 /* Size of the setup packet is always 8 */
[d1d7a92]152 TRB_CTRL_SET_XFER_LEN(*trb_setup, 8);
[e9e24f2]153
[eaf5e86]154 /* Immediate data */
[d1d7a92]155 TRB_CTRL_SET_IDT(*trb_setup, 1);
156 TRB_CTRL_SET_TRB_TYPE(*trb_setup, XHCI_TRB_TYPE_SETUP_STAGE);
157 TRB_CTRL_SET_TRT(*trb_setup, get_transfer_type(trb_setup, setup->request_type, setup->length));
[e9e24f2]158
[eaf5e86]159 /* Data stage */
[d1d7a92]160 xhci_trb_t *trb_data = NULL;
[eaf5e86]161 if (setup->length > 0) {
[5fd9c30]162 trb_data = trbs + trbs_used++;
163 xhci_trb_clean(trb_data);
[d1d7a92]164
165 trb_data->parameter = addr_to_phys(transfer->hc_buffer);
[e9e24f2]166
[eaf5e86]167 // data size (sent for OUT, or buffer size)
[d1d7a92]168 TRB_CTRL_SET_XFER_LEN(*trb_data, batch->buffer_size);
[eaf5e86]169 // FIXME: TD size 4.11.2.4
[d1d7a92]170 TRB_CTRL_SET_TD_SIZE(*trb_data, 1);
[e9e24f2]171
[eaf5e86]172 // Some more fields here, no idea what they mean
[d1d7a92]173 TRB_CTRL_SET_TRB_TYPE(*trb_data, XHCI_TRB_TYPE_DATA_STAGE);
[e9e24f2]174
[5fd9c30]175 int stage_dir = REQUEST_TYPE_IS_DEVICE_TO_HOST(setup->request_type)
[dbf32b1]176 ? STAGE_IN : STAGE_OUT;
[5fd9c30]177 TRB_CTRL_SET_DIR(*trb_data, stage_dir);
[eaf5e86]178 }
[e9e24f2]179
[eaf5e86]180 /* Status stage */
[d1d7a92]181 xhci_trb_t *trb_status = trbs + trbs_used++;
182 xhci_trb_clean(trb_status);
[e9e24f2]183
[eaf5e86]184 // FIXME: Evaluate next TRB? 4.12.3
[d1d7a92]185 // TRB_CTRL_SET_ENT(*trb_status, 1);
[e9e24f2]186
[d1d7a92]187 TRB_CTRL_SET_IOC(*trb_status, 1);
188 TRB_CTRL_SET_TRB_TYPE(*trb_status, XHCI_TRB_TYPE_STATUS_STAGE);
189 TRB_CTRL_SET_DIR(*trb_status, get_status_direction_flag(trb_setup, setup->request_type, setup->length));
[e9e24f2]190
[d7869d7e]191 // Issue a Configure Endpoint command, if needed.
192 if (configure_endpoint_needed(setup)) {
[b724494]193 const int err = hc_configure_device(hc, xhci_ep_to_dev(xhci_ep)->slot_id);
[5fd9c30]194 if (err)
195 return err;
[d7869d7e]196 }
197
[5fd9c30]198 return xhci_trb_ring_enqueue_multiple(ring, trbs, trbs_used, &transfer->interrupt_trb_phys);
[e9e24f2]199}
200
[5fd9c30]201static int schedule_bulk(xhci_hc_t* hc, xhci_transfer_t *transfer)
[9b2f69e]202{
[42bc933]203 xhci_trb_t trb;
[dbf32b1]204 xhci_trb_clean(&trb);
[7e74911]205 trb.parameter = addr_to_phys(transfer->hc_buffer);
[42bc933]206
207 // data size (sent for OUT, or buffer size)
[5fd9c30]208 TRB_CTRL_SET_XFER_LEN(trb, transfer->batch.buffer_size);
[eaf5e86]209 // FIXME: TD size 4.11.2.4
210 TRB_CTRL_SET_TD_SIZE(trb, 1);
[42bc933]211
212 // we want an interrupt after this td is done
213 TRB_CTRL_SET_IOC(trb, 1);
214
215 TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
216
[2b61945]217 xhci_trb_ring_t* ring = get_ring(hc, transfer);
[42bc933]218
[5fd9c30]219 return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
[42bc933]220}
221
[5fd9c30]222static int schedule_interrupt(xhci_hc_t* hc, xhci_transfer_t* transfer)
[9b2f69e]223{
224 xhci_trb_t trb;
[dbf32b1]225 xhci_trb_clean(&trb);
[9b2f69e]226 trb.parameter = addr_to_phys(transfer->hc_buffer);
227
228 // data size (sent for OUT, or buffer size)
[5fd9c30]229 TRB_CTRL_SET_XFER_LEN(trb, transfer->batch.buffer_size);
[9b2f69e]230 // FIXME: TD size 4.11.2.4
231 TRB_CTRL_SET_TD_SIZE(trb, 1);
232
233 // we want an interrupt after this td is done
234 TRB_CTRL_SET_IOC(trb, 1);
235
236 TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
237
[2b61945]238 xhci_trb_ring_t* ring = get_ring(hc, transfer);
[9b2f69e]239
[5fd9c30]240 return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
[9b2f69e]241}
242
[5fd9c30]243static int schedule_isochronous(xhci_hc_t* hc, xhci_transfer_t* transfer)
[1252e81]244{
[5fd9c30]245 /* TODO: Implement me. */
246 usb_log_error("Isochronous transfers are not yet implemented!");
247 return ENOTSUP;
[1252e81]248}
249
[a4e26882]250int xhci_transfer_abort(xhci_transfer_t *transfer)
251{
252 usb_transfer_batch_t *batch = &transfer->batch;
253 batch->error = EAGAIN;
254 batch->transfered_size = 0;
255 usb_transfer_batch_finish(batch);
256 return EOK;
257}
258
[e9e24f2]259int xhci_handle_transfer_event(xhci_hc_t* hc, xhci_trb_t* trb)
260{
261 uintptr_t addr = trb->parameter;
[2b61945]262 const unsigned slot_id = XHCI_DWORD_EXTRACT(trb->control, 31, 24);
263 const unsigned ep_dci = XHCI_DWORD_EXTRACT(trb->control, 20, 16);
[e9e24f2]264
[2b61945]265 xhci_device_t *dev = hc->bus.devices_by_slot[slot_id];
266 if (!dev) {
[9620a54]267 usb_log_error("Transfer event on disabled slot %u", slot_id);
[2b61945]268 return ENOENT;
[e9e24f2]269 }
270
[2b61945]271 const usb_endpoint_t ep_num = ep_dci / 2;
272 xhci_endpoint_t *ep = xhci_device_get_endpoint(dev, ep_num);
273 if (!ep) {
[9620a54]274 usb_log_error("Transfer event on dropped endpoint %u of device "
275 XHCI_DEV_FMT, ep_num, XHCI_DEV_ARGS(*dev));
[e9e24f2]276 return ENOENT;
277 }
278
[2b61945]279 xhci_transfer_t *transfer = &ep->active_transfer;
280
281 /** FIXME: This is racy. Do we care? */
282 ep->ring.dequeue = addr;
283
[5fd9c30]284 usb_transfer_batch_t *batch = &transfer->batch;
285
286 batch->error = (TRB_COMPLETION_CODE(*trb) == XHCI_TRBC_SUCCESS) ? EOK : ENAK;
287 batch->transfered_size = batch->buffer_size - TRB_TRANSFER_LENGTH(*trb);
[e9e24f2]288
[5fd9c30]289 if (batch->dir == USB_DIRECTION_IN) {
290 assert(batch->buffer);
291 assert(batch->transfered_size <= batch->buffer_size);
292 memcpy(batch->buffer, transfer->hc_buffer, batch->transfered_size);
293 }
294
295 usb_transfer_batch_finish(batch);
[e9e24f2]296 return EOK;
297}
[5fd9c30]298
299typedef int (*transfer_handler)(xhci_hc_t *, xhci_transfer_t *);
300
301static const transfer_handler transfer_handlers[] = {
302 [USB_TRANSFER_CONTROL] = schedule_control,
303 [USB_TRANSFER_ISOCHRONOUS] = schedule_isochronous,
304 [USB_TRANSFER_BULK] = schedule_bulk,
305 [USB_TRANSFER_INTERRUPT] = schedule_interrupt,
306};
307
308int xhci_transfer_schedule(xhci_hc_t *hc, usb_transfer_batch_t *batch)
309{
310 assert(hc);
311
312 xhci_transfer_t *transfer = xhci_transfer_from_batch(batch);
313 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(batch->ep);
314 assert(xhci_ep);
315
[a4e26882]316 xhci_device_t *xhci_dev = xhci_ep_to_dev(xhci_ep);
317
318 /* Offline devices don't schedule transfers other than on EP0. */
[a5b3de6]319 if (!xhci_dev->online && xhci_ep->base.endpoint) {
[a4e26882]320 return EAGAIN;
321 }
322
[7010861]323 // FIXME: find a better way to check if the ring is not initialized
324 if (!xhci_ep->ring.segment_count) {
[9620a54]325 usb_log_error("Ring not initialized for endpoint " XHCI_EP_FMT,
326 XHCI_EP_ARGS(*xhci_ep));
[7010861]327 return EINVAL;
328 }
329
[5fd9c30]330 const usb_transfer_type_t type = batch->ep->transfer_type;
331 assert(type >= 0 && type < ARRAY_SIZE(transfer_handlers));
332 assert(transfer_handlers[type]);
333
334 if (batch->buffer_size > 0) {
335 transfer->hc_buffer = malloc32(batch->buffer_size);
336 }
337
[7010861]338
[5fd9c30]339 if (batch->dir != USB_DIRECTION_IN) {
340 // Sending stuff from host to device, we need to copy the actual data.
341 memcpy(transfer->hc_buffer, batch->buffer, batch->buffer_size);
342 }
343
344 const int err = transfer_handlers[batch->ep->transfer_type](hc, transfer);
345 if (err)
346 return err;
347
[a4e26882]348 const uint8_t slot_id = xhci_dev->slot_id;
[5fd9c30]349 const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */
350 return hc_ring_doorbell(hc, slot_id, target);
351}
Note: See TracBrowser for help on using the repository browser.