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

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

Doorbell target fix

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