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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since bb784ae was f971e957, checked in by Michal Staruch <salmelu@…>, 8 years ago

Removed hardcoded target for doorbell

  • Property mode set to 100644
File size: 11.0 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
45static inline uint8_t get_transfer_type(xhci_trb_t* trb, uint8_t bmRequestType, uint16_t wLength)
46{
47 /* See Table 7 of xHCI specification */
48 if (bmRequestType & 0x80) {
49 /* Device-to-host transfer */
50 if (wLength) {
51 /* IN data stage */
52 return 3;
53 }
54 else {
55 /* No data stage */
56 return 0;
57 }
58 }
59 else {
60 /* Host-to-device transfer */
61 if (wLength) {
62 /* OUT data stage */
63 return 2;
64 }
65 else {
66 /* No data stage */
67 return 0;
68 }
69 }
70}
71
72static inline uint8_t get_data_direction(xhci_trb_t* trb, uint8_t bmRequestType, uint16_t wLength)
73{
74 /* See Table 7 of xHCI specification */
75 if (bmRequestType & 0x80) {
76 /* Device-to-host transfer */
77 return 1;
78 }
79 else {
80 /* Host-to-device transfer */
81 return 0;
82 }
83}
84
85static inline uint8_t get_status_direction(xhci_trb_t* trb, uint8_t bmRequestType, uint16_t wLength)
86{
87 /* See Table 7 of xHCI specification */
88 if (bmRequestType & 0x80) {
89 /* Device-to-host transfer */
90 if (wLength) {
91 /* Out direction */
92 return 0;
93 }
94 else {
95 /* In direction */
96 return 1;
97 }
98 }
99 else {
100 /* Host-to-device transfer, always IN direction */
101 return 1;
102 }
103}
104
105static inline bool configure_endpoint_needed(usb_device_request_setup_packet_t *setup)
106{
107 usb_request_type_t request_type = SETUP_REQUEST_TYPE_GET_TYPE(setup->request_type);
108
109 if (request_type == USB_REQUEST_TYPE_STANDARD) {
110 usb_stddevreq_t request = setup->request;
111
112 switch (request) {
113 case USB_DEVREQ_SET_CONFIGURATION:
114 case USB_DEVREQ_SET_INTERFACE:
115 return true;
116
117 default:
118 return false;
119 }
120 }
121
122 return false;
123}
124
125int xhci_init_transfers(xhci_hc_t *hc)
126{
127 assert(hc);
128
129 list_initialize(&hc->transfers);
130 return EOK;
131}
132
133void xhci_fini_transfers(xhci_hc_t *hc)
134{
135 // Note: Untested.
136 assert(hc);
137}
138
139xhci_transfer_t* xhci_transfer_alloc(usb_transfer_batch_t* batch) {
140 xhci_transfer_t* transfer = malloc(sizeof(xhci_transfer_t));
141 if (!transfer)
142 return NULL;
143
144 memset(transfer, 0, sizeof(xhci_transfer_t));
145 transfer->batch = batch;
146 link_initialize(&transfer->link);
147 transfer->hc_buffer = batch->buffer_size > 0 ? malloc32(batch->buffer_size) : NULL;
148
149 return transfer;
150}
151
152void xhci_transfer_fini(xhci_transfer_t* transfer) {
153 if (transfer) {
154 if (transfer->batch->buffer_size > 0)
155 free32(transfer->hc_buffer);
156
157 usb_transfer_batch_destroy(transfer->batch);
158
159 free(transfer);
160 }
161}
162
163int xhci_schedule_control_transfer(xhci_hc_t* hc, usb_transfer_batch_t* batch)
164{
165 if (!batch->setup_size) {
166 usb_log_error("Missing setup packet for the control transfer.");
167 return EINVAL;
168 }
169 if (batch->ep->transfer_type != USB_TRANSFER_CONTROL) {
170 /* This method only works for control transfers. */
171 usb_log_error("Attempted to schedule a control transfer to non control endpoint.");
172 return EINVAL;
173 }
174
175 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(batch->ep);
176
177 uint8_t slot_id = xhci_ep->device->slot_id;
178 xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[batch->ep->target.endpoint];
179
180 usb_device_request_setup_packet_t* setup =
181 (usb_device_request_setup_packet_t*) batch->setup_buffer;
182
183 /* For the TRB formats, see xHCI specification 6.4.1.2 */
184 xhci_transfer_t *transfer = xhci_transfer_alloc(batch);
185
186 if (!transfer->direction) {
187 // Sending stuff from host to device, we need to copy the actual data.
188 memcpy(transfer->hc_buffer, batch->buffer, batch->buffer_size);
189 }
190
191 xhci_trb_t trb_setup;
192 memset(&trb_setup, 0, sizeof(xhci_trb_t));
193
194 TRB_CTRL_SET_SETUP_WVALUE(trb_setup, setup->value);
195 TRB_CTRL_SET_SETUP_WLENGTH(trb_setup, setup->length);
196 TRB_CTRL_SET_SETUP_WINDEX(trb_setup, setup->index);
197 TRB_CTRL_SET_SETUP_BREQ(trb_setup, setup->request);
198 TRB_CTRL_SET_SETUP_BMREQTYPE(trb_setup, setup->request_type);
199
200 /* Size of the setup packet is always 8 */
201 TRB_CTRL_SET_XFER_LEN(trb_setup, 8);
202 // if we want an interrupt after this td is done, use
203 // TRB_CTRL_SET_IOC(trb_setup, 1);
204
205 /* Immediate data */
206 TRB_CTRL_SET_IDT(trb_setup, 1);
207 TRB_CTRL_SET_TRB_TYPE(trb_setup, XHCI_TRB_TYPE_SETUP_STAGE);
208 TRB_CTRL_SET_TRT(trb_setup, get_transfer_type(&trb_setup, setup->request_type, setup->length));
209
210 /* Data stage */
211 xhci_trb_t trb_data;
212 memset(&trb_data, 0, sizeof(xhci_trb_t));
213
214 if (setup->length > 0) {
215 trb_data.parameter = addr_to_phys(transfer->hc_buffer);
216
217 // data size (sent for OUT, or buffer size)
218 TRB_CTRL_SET_XFER_LEN(trb_data, batch->buffer_size);
219 // FIXME: TD size 4.11.2.4
220 TRB_CTRL_SET_TD_SIZE(trb_data, 1);
221
222 // if we want an interrupt after this td is done, use
223 // TRB_CTRL_SET_IOC(trb_data, 1);
224
225 // Some more fields here, no idea what they mean
226 TRB_CTRL_SET_TRB_TYPE(trb_data, XHCI_TRB_TYPE_DATA_STAGE);
227
228 transfer->direction = get_data_direction(&trb_setup, setup->request_type, setup->length);
229 TRB_CTRL_SET_DIR(trb_data, transfer->direction);
230 }
231
232 /* Status stage */
233 xhci_trb_t trb_status;
234 memset(&trb_status, 0, sizeof(xhci_trb_t));
235
236 // FIXME: Evaluate next TRB? 4.12.3
237 // TRB_CTRL_SET_ENT(trb_status, 1);
238
239 // if we want an interrupt after this td is done, use
240 TRB_CTRL_SET_IOC(trb_status, 1);
241
242 TRB_CTRL_SET_TRB_TYPE(trb_status, XHCI_TRB_TYPE_STATUS_STAGE);
243 TRB_CTRL_SET_DIR(trb_status, get_status_direction(&trb_setup, setup->request_type, setup->length));
244
245 uintptr_t dummy = 0;
246 xhci_trb_ring_enqueue(ring, &trb_setup, &dummy);
247 if (setup->length > 0) {
248 xhci_trb_ring_enqueue(ring, &trb_data, &dummy);
249 }
250 xhci_trb_ring_enqueue(ring, &trb_status, &transfer->interrupt_trb_phys);
251
252 list_append(&transfer->link, &hc->transfers);
253
254 /* For control transfers, the target is always 1. */
255 // FIXME: ignoring return code
256 const uint8_t target = xhci_endpoint_ctx_offset(xhci_ep);
257 hc_ring_doorbell(hc, slot_id, target);
258
259 // Issue a Configure Endpoint command, if needed.
260 if (configure_endpoint_needed(setup)) {
261 // TODO: figure out the best time to issue this command
262 // FIXME: ignoring return code
263 xhci_device_configure(xhci_ep->device, hc);
264 }
265
266 return EOK;
267}
268
269int xhci_schedule_bulk_transfer(xhci_hc_t* hc, usb_transfer_batch_t* batch)
270{
271 if (batch->setup_size) {
272 usb_log_warning("Setup packet present for a bulk transfer.");
273 }
274
275 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(batch->ep);
276 uint8_t slot_id = xhci_ep->device->slot_id;
277 xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[batch->ep->target.endpoint];
278
279 xhci_transfer_t *transfer = xhci_transfer_alloc(batch);
280 if (!transfer->direction) {
281 // Sending stuff from host to device, we need to copy the actual data.
282 memcpy(transfer->hc_buffer, batch->buffer, batch->buffer_size);
283 }
284
285 xhci_trb_t trb;
286 memset(&trb, 0, sizeof(xhci_trb_t));
287 trb.parameter = addr_to_phys(transfer->hc_buffer);
288
289 // data size (sent for OUT, or buffer size)
290 TRB_CTRL_SET_XFER_LEN(trb, batch->buffer_size);
291 // FIXME: TD size 4.11.2.4
292 TRB_CTRL_SET_TD_SIZE(trb, 1);
293
294 // we want an interrupt after this td is done
295 TRB_CTRL_SET_IOC(trb, 1);
296
297 TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
298
299 xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
300 list_append(&transfer->link, &hc->transfers);
301
302 // TODO: target = endpoint | stream_id << 16
303 const uint8_t target = xhci_endpoint_ctx_offset(xhci_ep);
304 hc_ring_doorbell(hc, slot_id, target);
305 return EOK;
306}
307
308int xhci_schedule_interrupt_transfer(xhci_hc_t* hc, usb_transfer_batch_t* batch)
309{
310 if (batch->setup_size) {
311 usb_log_warning("Setup packet present for a interrupt transfer.");
312 }
313
314 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(batch->ep);
315 uint8_t slot_id = xhci_ep->device->slot_id;
316 xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[batch->ep->target.endpoint];
317
318 xhci_transfer_t *transfer = xhci_transfer_alloc(batch);
319 if (!transfer->direction) {
320 // Sending stuff from host to device, we need to copy the actual data.
321 memcpy(transfer->hc_buffer, batch->buffer, batch->buffer_size);
322 }
323
324 xhci_trb_t trb;
325 memset(&trb, 0, sizeof(xhci_trb_t));
326 trb.parameter = addr_to_phys(transfer->hc_buffer);
327
328 // data size (sent for OUT, or buffer size)
329 TRB_CTRL_SET_XFER_LEN(trb, batch->buffer_size);
330 // FIXME: TD size 4.11.2.4
331 TRB_CTRL_SET_TD_SIZE(trb, 1);
332
333 // we want an interrupt after this td is done
334 TRB_CTRL_SET_IOC(trb, 1);
335
336 TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
337
338 xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
339 list_append(&transfer->link, &hc->transfers);
340
341 const uint8_t target = xhci_endpoint_ctx_offset(xhci_ep);
342 usb_log_debug("Ringing doorbell for slot_id = %d, target = %d", slot_id, target);
343 return hc_ring_doorbell(hc, slot_id, target);
344}
345
346int xhci_handle_transfer_event(xhci_hc_t* hc, xhci_trb_t* trb)
347{
348 uintptr_t addr = trb->parameter;
349 xhci_transfer_t *transfer = NULL;
350
351 link_t *transfer_link = list_first(&hc->transfers);
352 while (transfer_link) {
353 transfer = list_get_instance(transfer_link, xhci_transfer_t, link);
354
355 if (transfer->interrupt_trb_phys == addr)
356 break;
357
358 transfer_link = list_next(transfer_link, &hc->transfers);
359 }
360
361 if (!transfer_link) {
362 usb_log_warning("Transfer not found.");
363 return ENOENT;
364 }
365
366 list_remove(transfer_link);
367 usb_transfer_batch_t *batch = transfer->batch;
368
369 batch->error = (TRB_COMPLETION_CODE(*trb) == XHCI_TRBC_SUCCESS) ? EOK : ENAK;
370 batch->transfered_size = batch->buffer_size - TRB_TRANSFER_LENGTH(*trb);
371 if (transfer->direction) {
372 memcpy(batch->buffer, transfer->hc_buffer, batch->buffer_size);
373
374 /* Device-to-host, IN */
375 if (batch->callback_in)
376 batch->callback_in(batch->error, batch->transfered_size, batch->arg);
377 }
378 else {
379 /* Host-to-device, OUT */
380 if (batch->callback_out)
381 batch->callback_out(batch->error, batch->arg);
382 }
383
384 xhci_transfer_fini(transfer);
385 return EOK;
386}
Note: See TracBrowser for help on using the repository browser.