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
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/host/utils/malloc32.h>
37#include <usb/debug.h>
38#include <usb/request.h>
39#include "endpoint.h"
40#include "hc.h"
41#include "hw_struct/trb.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 * 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)
102{
103 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
104 xhci_transfer_t *transfer = &xhci_ep->active_transfer;
105
106 /* Do not access the transfer yet, it may be still in use. */
107
108 usb_transfer_batch_init(&transfer->batch, ep);
109 assert(ep->active);
110
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));
114
115 return transfer;
116}
117
118void xhci_transfer_destroy(xhci_transfer_t* transfer)
119{
120 assert(transfer);
121
122 if (transfer->hc_buffer)
123 free32(transfer->hc_buffer);
124}
125
126static xhci_trb_ring_t *get_ring(xhci_hc_t *hc, xhci_transfer_t *transfer)
127{
128 return &xhci_endpoint_get(transfer->batch.ep)->ring;
129}
130
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);
136
137 usb_device_request_setup_packet_t* setup = &batch->setup.packet;
138
139 xhci_trb_t trbs[3];
140 int trbs_used = 0;
141
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);
150
151 /* Size of the setup packet is always 8 */
152 TRB_CTRL_SET_XFER_LEN(*trb_setup, 8);
153
154 /* Immediate data */
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));
158
159 /* Data stage */
160 xhci_trb_t *trb_data = NULL;
161 if (setup->length > 0) {
162 trb_data = trbs + trbs_used++;
163 xhci_trb_clean(trb_data);
164
165 trb_data->parameter = addr_to_phys(transfer->hc_buffer);
166
167 // data size (sent for OUT, or buffer size)
168 TRB_CTRL_SET_XFER_LEN(*trb_data, batch->buffer_size);
169 // FIXME: TD size 4.11.2.4
170 TRB_CTRL_SET_TD_SIZE(*trb_data, 1);
171
172 // Some more fields here, no idea what they mean
173 TRB_CTRL_SET_TRB_TYPE(*trb_data, XHCI_TRB_TYPE_DATA_STAGE);
174
175 int stage_dir = REQUEST_TYPE_IS_DEVICE_TO_HOST(setup->request_type)
176 ? STAGE_IN : STAGE_OUT;
177 TRB_CTRL_SET_DIR(*trb_data, stage_dir);
178 }
179
180 /* Status stage */
181 xhci_trb_t *trb_status = trbs + trbs_used++;
182 xhci_trb_clean(trb_status);
183
184 // FIXME: Evaluate next TRB? 4.12.3
185 // TRB_CTRL_SET_ENT(*trb_status, 1);
186
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));
190
191 // Issue a Configure Endpoint command, if needed.
192 if (configure_endpoint_needed(setup)) {
193 const int err = hc_configure_device(hc, xhci_ep_to_dev(xhci_ep)->slot_id);
194 if (err)
195 return err;
196 }
197
198 return xhci_trb_ring_enqueue_multiple(ring, trbs, trbs_used, &transfer->interrupt_trb_phys);
199}
200
201static int schedule_bulk(xhci_hc_t* hc, xhci_transfer_t *transfer)
202{
203 xhci_trb_t trb;
204 xhci_trb_clean(&trb);
205 trb.parameter = addr_to_phys(transfer->hc_buffer);
206
207 // data size (sent for OUT, or buffer size)
208 TRB_CTRL_SET_XFER_LEN(trb, transfer->batch.buffer_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
215 TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
216
217 xhci_trb_ring_t* ring = get_ring(hc, transfer);
218
219 return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
220}
221
222static int schedule_interrupt(xhci_hc_t* hc, xhci_transfer_t* transfer)
223{
224 xhci_trb_t trb;
225 xhci_trb_clean(&trb);
226 trb.parameter = addr_to_phys(transfer->hc_buffer);
227
228 // data size (sent for OUT, or buffer size)
229 TRB_CTRL_SET_XFER_LEN(trb, transfer->batch.buffer_size);
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
238 xhci_trb_ring_t* ring = get_ring(hc, transfer);
239
240 return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
241}
242
243static int schedule_isochronous(xhci_hc_t* hc, xhci_transfer_t* transfer)
244{
245 /* TODO: Implement me. */
246 usb_log_error("Isochronous transfers are not yet implemented!");
247 return ENOTSUP;
248}
249
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
259int xhci_handle_transfer_event(xhci_hc_t* hc, xhci_trb_t* trb)
260{
261 uintptr_t addr = trb->parameter;
262 const unsigned slot_id = XHCI_DWORD_EXTRACT(trb->control, 31, 24);
263 const unsigned ep_dci = XHCI_DWORD_EXTRACT(trb->control, 20, 16);
264
265 xhci_device_t *dev = hc->bus.devices_by_slot[slot_id];
266 if (!dev) {
267 usb_log_error("Transfer event on disabled slot %u", slot_id);
268 return ENOENT;
269 }
270
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) {
274 usb_log_error("Transfer event on dropped endpoint %u of device "
275 XHCI_DEV_FMT, ep_num, XHCI_DEV_ARGS(*dev));
276 return ENOENT;
277 }
278
279 xhci_transfer_t *transfer = &ep->active_transfer;
280
281 /** FIXME: This is racy. Do we care? */
282 ep->ring.dequeue = addr;
283
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);
288
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);
296 return EOK;
297}
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
316 xhci_device_t *xhci_dev = xhci_ep_to_dev(xhci_ep);
317
318 /* Offline devices don't schedule transfers other than on EP0. */
319 if (!xhci_dev->online && xhci_ep->base.endpoint) {
320 return EAGAIN;
321 }
322
323 // FIXME: find a better way to check if the ring is not initialized
324 if (!xhci_ep->ring.segment_count) {
325 usb_log_error("Ring not initialized for endpoint " XHCI_EP_FMT,
326 XHCI_EP_ARGS(*xhci_ep));
327 return EINVAL;
328 }
329
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
338
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
348 const uint8_t slot_id = xhci_dev->slot_id;
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.