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

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

xhci: whitespace fixes

  • Property mode set to 100644
File size: 8.2 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 "hc.h"
39#include "hw_struct/trb.h"
40#include "transfers.h"
41#include "trb_ring.h"
42
43static inline uint8_t get_transfer_type(xhci_trb_t* trb, uint8_t bmRequestType, uint16_t wLength)
44{
45 /* See Table 7 of xHCI specification */
46 if (bmRequestType & 0x80) {
47 /* Device-to-host transfer */
48 if (wLength) {
49 /* IN data stage */
50 return 3;
51 }
52 else {
53 /* No data stage */
54 return 0;
55 }
56 }
57 else {
58 /* Host-to-device transfer */
59 if (wLength) {
60 /* OUT data stage */
61 return 2;
62 }
63 else {
64 /* No data stage */
65 return 0;
66 }
67 }
68}
69
70static inline uint8_t get_data_direction(xhci_trb_t* trb, uint8_t bmRequestType, uint16_t wLength)
71{
72 /* See Table 7 of xHCI specification */
73 if (bmRequestType & 0x80) {
74 /* Device-to-host transfer */
75 return 1;
76 }
77 else {
78 /* Host-to-device transfer */
79 return 0;
80 }
81}
82
83static inline uint8_t get_status_direction(xhci_trb_t* trb, uint8_t bmRequestType, uint16_t wLength)
84{
85 /* See Table 7 of xHCI specification */
86 if (bmRequestType & 0x80) {
87 /* Device-to-host transfer */
88 if (wLength) {
89 /* Out direction */
90 return 0;
91 }
92 else {
93 /* In direction */
94 return 1;
95 }
96 }
97 else {
98 /* Host-to-device transfer, always IN direction */
99 return 1;
100 }
101}
102
103int xhci_init_transfers(xhci_hc_t *hc)
104{
105 assert(hc);
106
107 list_initialize(&hc->transfers);
108 return EOK;
109}
110
111void xhci_fini_transfers(xhci_hc_t *hc)
112{
113 // Note: Untested.
114 assert(hc);
115}
116
117xhci_transfer_t* xhci_transfer_alloc(usb_transfer_batch_t* batch) {
118 xhci_transfer_t* transfer = malloc(sizeof(xhci_transfer_t));
119 if (!transfer)
120 return NULL;
121
122 memset(transfer, 0, sizeof(xhci_transfer_t));
123 transfer->batch = batch;
124 link_initialize(&transfer->link);
125 return transfer;
126}
127
128void xhci_transfer_fini(xhci_transfer_t* transfer) {
129 if (transfer) {
130 free(transfer);
131 }
132}
133
134int xhci_schedule_control_transfer(xhci_hc_t* hc, usb_transfer_batch_t* batch)
135{
136 if (!batch->setup_size) {
137 usb_log_error("Missing setup packet for the control transfer.");
138 return EINVAL;
139 }
140 if (batch->ep->endpoint != 0 || batch->ep->transfer_type != USB_TRANSFER_CONTROL) {
141 /* This method only works for control transfers. */
142 usb_log_error("Attempted to schedule control transfer to non 0 endpoint.");
143 return EINVAL;
144 }
145
146 uint8_t slot_id = batch->ep->hc_data.slot_id;
147 xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[0];
148
149 usb_device_request_setup_packet_t* setup =
150 (usb_device_request_setup_packet_t*) batch->setup_buffer;
151
152 /* For the TRB formats, see xHCI specification 6.4.1.2 */
153 xhci_transfer_t *transfer = xhci_transfer_alloc(batch);
154
155 xhci_trb_t trb_setup;
156 memset(&trb_setup, 0, sizeof(xhci_trb_t));
157
158 TRB_CTRL_SET_SETUP_WVALUE(trb_setup, setup->value);
159 TRB_CTRL_SET_SETUP_WLENGTH(trb_setup, setup->length);
160 TRB_CTRL_SET_SETUP_WINDEX(trb_setup, setup->index);
161 TRB_CTRL_SET_SETUP_BREQ(trb_setup, setup->request);
162 TRB_CTRL_SET_SETUP_BMREQTYPE(trb_setup, setup->request_type);
163
164 /* Size of the setup packet is always 8 */
165 TRB_CTRL_SET_XFER_LEN(trb_setup, 8);
166 // if we want an interrupt after this td is done, use
167 // TRB_CTRL_SET_IOC(trb_setup, 1);
168
169 /* Immediate data */
170 TRB_CTRL_SET_IDT(trb_setup, 1);
171 TRB_CTRL_SET_TRB_TYPE(trb_setup, XHCI_TRB_TYPE_SETUP_STAGE);
172 TRB_CTRL_SET_TRT(trb_setup, get_transfer_type(&trb_setup, setup->request_type, setup->length));
173
174 /* Data stage */
175 xhci_trb_t trb_data;
176 memset(&trb_data, 0, sizeof(xhci_trb_t));
177
178 if (setup->length > 0) {
179 trb_data.parameter = (uintptr_t) addr_to_phys(batch->buffer);
180
181 // data size (sent for OUT, or buffer size)
182 TRB_CTRL_SET_XFER_LEN(trb_data, batch->buffer_size);
183 // FIXME: TD size 4.11.2.4
184 TRB_CTRL_SET_TD_SIZE(trb_data, 1);
185
186 // if we want an interrupt after this td is done, use
187 // TRB_CTRL_SET_IOC(trb_data, 1);
188
189 // Some more fields here, no idea what they mean
190 TRB_CTRL_SET_TRB_TYPE(trb_data, XHCI_TRB_TYPE_DATA_STAGE);
191
192 transfer->direction = get_data_direction(&trb_setup, setup->request_type, setup->length);
193 TRB_CTRL_SET_DIR(trb_data, transfer->direction);
194 }
195
196 /* Status stage */
197 xhci_trb_t trb_status;
198 memset(&trb_status, 0, sizeof(xhci_trb_t));
199
200 // FIXME: Evaluate next TRB? 4.12.3
201 // TRB_CTRL_SET_ENT(trb_status, 1);
202
203 // if we want an interrupt after this td is done, use
204 TRB_CTRL_SET_IOC(trb_status, 1);
205
206 TRB_CTRL_SET_TRB_TYPE(trb_status, XHCI_TRB_TYPE_STATUS_STAGE);
207 TRB_CTRL_SET_DIR(trb_status, get_status_direction(&trb_setup, setup->request_type, setup->length));
208
209 uintptr_t dummy = 0;
210 xhci_trb_ring_enqueue(ring, &trb_setup, &dummy);
211 if (setup->length > 0) {
212 xhci_trb_ring_enqueue(ring, &trb_data, &dummy);
213 }
214 xhci_trb_ring_enqueue(ring, &trb_status, &transfer->interrupt_trb_phys);
215
216 list_append(&transfer->link, &hc->transfers);
217
218 /* For control transfers, the target is always 1. */
219 hc_ring_doorbell(hc, slot_id, 1);
220 return EOK;
221}
222
223int xhci_schedule_bulk_transfer(xhci_hc_t* hc, usb_transfer_batch_t* batch) {
224 if (batch->setup_size) {
225 usb_log_warning("Setup packet present for a bulk transfer.");
226 }
227
228 uint8_t slot_id = batch->ep->hc_data.slot_id;
229 xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[batch->ep->endpoint];
230
231 xhci_transfer_t *transfer = xhci_transfer_alloc(batch);
232
233 xhci_trb_t trb;
234 memset(&trb, 0, sizeof(xhci_trb_t));
235 trb.parameter = (uintptr_t) addr_to_phys(batch->buffer);
236
237 // data size (sent for OUT, or buffer size)
238 TRB_CTRL_SET_XFER_LEN(trb, batch->buffer_size);
239 // FIXME: TD size 4.11.2.4
240 TRB_CTRL_SET_TD_SIZE(trb, 1);
241
242 // we want an interrupt after this td is done
243 TRB_CTRL_SET_IOC(trb, 1);
244
245 TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
246
247 xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
248 list_append(&transfer->link, &hc->transfers);
249
250 /* For control transfers, the target is always 1. */
251 hc_ring_doorbell(hc, slot_id, 1);
252 return EOK;
253}
254
255int xhci_handle_transfer_event(xhci_hc_t* hc, xhci_trb_t* trb)
256{
257 uintptr_t addr = trb->parameter;
258 xhci_transfer_t *transfer = NULL;
259
260 link_t *transfer_link = list_first(&hc->transfers);
261 while (transfer_link) {
262 transfer = list_get_instance(transfer_link, xhci_transfer_t, link);
263
264 if (transfer->interrupt_trb_phys == addr)
265 break;
266
267 transfer_link = list_next(transfer_link, &hc->transfers);
268 }
269
270 if (!transfer_link) {
271 usb_log_warning("Transfer not found.");
272 return ENOENT;
273 }
274
275 list_remove(transfer_link);
276 usb_transfer_batch_t *batch = transfer->batch;
277
278 batch->error = (TRB_COMPLETION_CODE(*trb) == XHCI_TRBC_SUCCESS) ? EOK : ENAK;
279 batch->transfered_size = batch->buffer_size - TRB_TRANSFER_LENGTH(*trb);
280 if (transfer->direction) {
281 /* Device-to-host, IN */
282 if (batch->callback_in)
283 batch->callback_in(batch->error, batch->transfered_size, batch->arg);
284 }
285 else {
286 /* Host-to-device, OUT */
287 if (batch->callback_out)
288 batch->callback_out(batch->error, batch->arg);
289 }
290
291 xhci_transfer_fini(transfer);
292 return EOK;
293}
Note: See TracBrowser for help on using the repository browser.