source: mainline/uspace/drv/bus/usb/xhci/endpoint.c@ 2cf28b9

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

xhci: connecting devices deeper than to roothub

It still does not work, because the address command fails, but there should not be any fundamental problem.

  • Property mode set to 100644
File size: 9.6 KB
Line 
1/*
2 * Copyright (c) 2017 Petr Manek
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 endpoint management.
34 */
35
36#include <usb/host/utils/malloc32.h>
37#include <usb/host/endpoint.h>
38#include <usb/descriptor.h>
39
40#include <errno.h>
41#include <macros.h>
42
43#include "hc.h"
44#include "bus.h"
45#include "commands.h"
46#include "endpoint.h"
47
48int xhci_endpoint_init(xhci_endpoint_t *xhci_ep, xhci_bus_t *xhci_bus)
49{
50 assert(xhci_ep);
51 assert(xhci_bus);
52
53 bus_t *bus = &xhci_bus->base;
54 endpoint_t *ep = &xhci_ep->base;
55
56 endpoint_init(ep, bus);
57
58 return EOK;
59}
60
61void xhci_endpoint_fini(xhci_endpoint_t *xhci_ep)
62{
63 assert(xhci_ep);
64
65 // TODO: Something missed?
66}
67
68static bool endpoint_uses_streams(xhci_endpoint_t *xhci_ep)
69{
70 return xhci_ep->base.transfer_type == USB_TRANSFER_BULK
71 && xhci_ep->max_streams;
72}
73
74static size_t primary_stream_ctx_array_size(xhci_endpoint_t *xhci_ep)
75{
76 if (!endpoint_uses_streams(xhci_ep))
77 return 0;
78
79 /* Section 6.2.3, Table 61 */
80 return 1 << (xhci_ep->max_streams + 1);
81}
82
83static bool primary_stream_ctx_has_secondary_array(xhci_stream_ctx_t *primary_ctx) {
84 /* Section 6.2.4.1, SCT values */
85 return XHCI_STREAM_SCT(*primary_ctx) >= 2;
86}
87
88static size_t secondary_stream_ctx_array_size(xhci_stream_ctx_t *primary_ctx) {
89 if (XHCI_STREAM_SCT(*primary_ctx) < 2) return 0;
90 return 2 << XHCI_STREAM_SCT(*primary_ctx);
91}
92
93int xhci_endpoint_alloc_transfer_ds(xhci_endpoint_t *xhci_ep)
94{
95 if (endpoint_uses_streams(xhci_ep)) {
96 /* Set up primary stream context array if needed. */
97 const size_t size = primary_stream_ctx_array_size(xhci_ep);
98 usb_log_debug2("Allocating primary stream context array of size %lu for endpoint " XHCI_EP_FMT,
99 size, XHCI_EP_ARGS(*xhci_ep));
100
101 xhci_ep->primary_stream_ctx_array = malloc32(size * sizeof(xhci_stream_ctx_t));
102 if (!xhci_ep->primary_stream_ctx_array) {
103 return ENOMEM;
104 }
105
106 memset(xhci_ep->primary_stream_ctx_array, 0, size * sizeof(xhci_stream_ctx_t));
107 } else {
108 usb_log_debug2("Allocating main transfer ring for endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
109
110 xhci_ep->primary_stream_ctx_array = NULL;
111
112 int err;
113 if ((err = xhci_trb_ring_init(&xhci_ep->ring))) {
114 return err;
115 }
116 }
117
118 return EOK;
119}
120
121int xhci_endpoint_free_transfer_ds(xhci_endpoint_t *xhci_ep)
122{
123 if (endpoint_uses_streams(xhci_ep)) {
124 usb_log_debug2("Freeing primary stream context array for endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
125
126 // maybe check if LSA, then skip?
127 for (size_t index = 0; index < primary_stream_ctx_array_size(xhci_ep); ++index) {
128 xhci_stream_ctx_t *primary_ctx = xhci_ep->primary_stream_ctx_array + index;
129 if (primary_stream_ctx_has_secondary_array(primary_ctx)) {
130 // uintptr_t phys = XHCI_STREAM_DEQ_PTR(*primary_ctx);
131 /* size_t size = */ secondary_stream_ctx_array_size(primary_ctx);
132 // TODO: somehow map the address to virtual and free the secondary array
133 }
134 }
135 free32(xhci_ep->primary_stream_ctx_array);
136 } else {
137 usb_log_debug2("Freeing main transfer ring for endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
138
139 int err;
140 if ((err = xhci_trb_ring_fini(&xhci_ep->ring))) {
141 return err;
142 }
143 }
144
145 return EOK;
146}
147
148/** See section 4.5.1 of the xHCI spec.
149 */
150uint8_t xhci_endpoint_dci(xhci_endpoint_t *ep)
151{
152 return (2 * ep->base.endpoint) +
153 (ep->base.transfer_type == USB_TRANSFER_CONTROL
154 || ep->base.direction == USB_DIRECTION_IN);
155}
156
157/** Return an index to the endpoint array. The indices are assigned as follows:
158 * 0 EP0 BOTH
159 * 1 EP1 OUT
160 * 2 EP1 IN
161 *
162 * For control endpoints >0, the IN endpoint index is used.
163 *
164 * The index returned must be usually offset by a number of contexts preceding
165 * the endpoint contexts themselves.
166 */
167uint8_t xhci_endpoint_index(xhci_endpoint_t *ep)
168{
169 return xhci_endpoint_dci(ep) - 1;
170}
171
172static int xhci_endpoint_type(xhci_endpoint_t *ep)
173{
174 const bool in = ep->base.direction == USB_DIRECTION_IN;
175
176 switch (ep->base.transfer_type) {
177 case USB_TRANSFER_CONTROL:
178 return EP_TYPE_CONTROL;
179
180 case USB_TRANSFER_ISOCHRONOUS:
181 return in ? EP_TYPE_ISOCH_IN
182 : EP_TYPE_ISOCH_OUT;
183
184 case USB_TRANSFER_BULK:
185 return in ? EP_TYPE_BULK_IN
186 : EP_TYPE_BULK_OUT;
187
188 case USB_TRANSFER_INTERRUPT:
189 return in ? EP_TYPE_INTERRUPT_IN
190 : EP_TYPE_INTERRUPT_OUT;
191 }
192
193 return EP_TYPE_INVALID;
194}
195
196static void setup_control_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
197{
198 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
199 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
200 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst);
201 XHCI_EP_MULT_SET(*ctx, ep->mult);
202 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
203 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
204 XHCI_EP_DCS_SET(*ctx, 1);
205}
206
207static void setup_bulk_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
208{
209 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
210 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
211 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst);
212 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
213
214 if (endpoint_uses_streams(ep)) {
215 XHCI_EP_MAX_P_STREAMS_SET(*ctx, ep->max_streams);
216 XHCI_EP_TR_DPTR_SET(*ctx, addr_to_phys(ep->primary_stream_ctx_array));
217 // TODO: set HID
218 // TODO: set LSA
219 } else {
220 XHCI_EP_MAX_P_STREAMS_SET(*ctx, 0);
221 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
222 XHCI_EP_DCS_SET(*ctx, 1);
223 }
224}
225
226static void setup_isoch_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
227{
228 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
229 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size & 0x07FF);
230 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst);
231 XHCI_EP_MULT_SET(*ctx, ep->mult);
232 XHCI_EP_ERROR_COUNT_SET(*ctx, 0);
233 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
234 XHCI_EP_DCS_SET(*ctx, 1);
235 // TODO: max ESIT payload
236}
237
238static void setup_interrupt_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
239{
240 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
241 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size & 0x07FF);
242 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst);
243 XHCI_EP_MULT_SET(*ctx, 0);
244 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
245 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
246 XHCI_EP_DCS_SET(*ctx, 1);
247 // TODO: max ESIT payload
248}
249
250typedef void (*setup_ep_ctx_helper)(xhci_endpoint_t *, xhci_ep_ctx_t *);
251
252static const setup_ep_ctx_helper setup_ep_ctx_helpers[] = {
253 [USB_TRANSFER_CONTROL] = setup_control_ep_ctx,
254 [USB_TRANSFER_ISOCHRONOUS] = setup_isoch_ep_ctx,
255 [USB_TRANSFER_BULK] = setup_bulk_ep_ctx,
256 [USB_TRANSFER_INTERRUPT] = setup_interrupt_ep_ctx,
257};
258
259void xhci_setup_endpoint_context(xhci_endpoint_t *ep, xhci_ep_ctx_t *ep_ctx)
260{
261 assert(ep);
262 assert(ep_ctx);
263
264 usb_transfer_type_t tt = ep->base.transfer_type;
265 assert(tt < ARRAY_SIZE(setup_ep_ctx_helpers));
266
267 memset(ep_ctx, 0, sizeof(*ep_ctx));
268 setup_ep_ctx_helpers[tt](ep, ep_ctx);
269}
270
271int xhci_device_add_endpoint(xhci_device_t *dev, xhci_endpoint_t *ep)
272{
273 assert(dev);
274 assert(ep);
275
276 /* Offline devices don't create new endpoints other than EP0. */
277 if (!dev->online) {
278 return EAGAIN;
279 }
280
281 const usb_endpoint_t ep_num = ep->base.endpoint;
282
283 assert(&dev->base == ep->base.device);
284
285 // TODO Do not fail hard on runtime conditions
286 assert(!dev->endpoints[ep_num]);
287
288 dev->endpoints[ep_num] = ep;
289 ++dev->active_endpoint_count;
290
291 if (ep_num == 0) {
292 /* EP 0 is initialized while setting up the device,
293 * so we must not issue the command now. */
294 return EOK;
295 }
296
297 /* Add endpoint. */
298 xhci_ep_ctx_t ep_ctx;
299 xhci_setup_endpoint_context(ep, &ep_ctx);
300
301 return hc_add_endpoint(dev->hc, dev->slot_id, xhci_endpoint_index(ep), &ep_ctx);
302}
303
304int xhci_device_remove_endpoint(xhci_device_t *dev, xhci_endpoint_t *ep)
305{
306 assert(&dev->base == ep->base.device);
307 assert(dev->endpoints[ep->base.endpoint]);
308
309 int err = ENOMEM;
310 const usb_endpoint_t ep_num = ep->base.endpoint;
311
312 dev->endpoints[ep->base.endpoint] = NULL;
313 --dev->active_endpoint_count;
314
315 if (ep_num == 0) {
316 /* EP 0 is finalized while releasing the device,
317 * so we must not issue the command now. */
318 return EOK;
319 }
320
321 /* Drop the endpoint. */
322 if ((err = hc_drop_endpoint(dev->hc, dev->slot_id, xhci_endpoint_index(ep)))) {
323 goto err;
324 }
325
326 /* Tear down TRB ring / PSA. */
327 /* FIXME: For some reason, this causes crash at xhci_trb_ring_fini.
328 if ((err = xhci_endpoint_free_transfer_ds(ep))) {
329 goto err_cmd;
330 }
331 */
332
333 return EOK;
334
335err:
336 dev->endpoints[ep_num] = ep;
337 ++dev->active_endpoint_count;
338 return err;
339}
340
341xhci_endpoint_t *xhci_device_get_endpoint(xhci_device_t *dev, usb_endpoint_t ep)
342{
343 return dev->endpoints[ep];
344}
345
346/**
347 * @}
348 */
Note: See TracBrowser for help on using the repository browser.