source: mainline/uspace/drv/bus/usb/xhci/endpoint.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.8 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 int xhci_endpoint_type(xhci_endpoint_t *ep)
69{
70 const bool in = ep->base.direction == USB_DIRECTION_IN;
71
72 switch (ep->base.transfer_type) {
73 case USB_TRANSFER_CONTROL:
74 return EP_TYPE_CONTROL;
75
76 case USB_TRANSFER_ISOCHRONOUS:
77 return in ? EP_TYPE_ISOCH_IN
78 : EP_TYPE_ISOCH_OUT;
79
80 case USB_TRANSFER_BULK:
81 return in ? EP_TYPE_BULK_IN
82 : EP_TYPE_BULK_OUT;
83
84 case USB_TRANSFER_INTERRUPT:
85 return in ? EP_TYPE_INTERRUPT_IN
86 : EP_TYPE_INTERRUPT_OUT;
87 }
88
89 return EP_TYPE_INVALID;
90}
91
92static bool endpoint_using_streams(xhci_endpoint_t *xhci_ep)
93{
94 return xhci_ep->primary_stream_ctx_array != NULL;
95}
96
97static size_t primary_stream_ctx_array_max_size(xhci_endpoint_t *xhci_ep)
98{
99 if (!xhci_ep->max_streams)
100 return 0;
101
102 /* Section 6.2.3, Table 61 */
103 return 1 << (xhci_ep->max_streams + 1);
104}
105
106// static bool primary_stream_ctx_has_secondary_array(xhci_stream_ctx_t *primary_ctx) {
107// /* Section 6.2.4.1, SCT values */
108// return XHCI_STREAM_SCT(*primary_ctx) >= 2;
109// }
110//
111// static size_t secondary_stream_ctx_array_size(xhci_stream_ctx_t *primary_ctx) {
112// if (XHCI_STREAM_SCT(*primary_ctx) < 2) return 0;
113// return 2 << XHCI_STREAM_SCT(*primary_ctx);
114// }
115
116static void initialize_primary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep, unsigned count) {
117 for (size_t index = 0; index < count; ++index) {
118 // Create trb ring for every primary stream
119 // Store it somewhere
120 // Set the dequeue pointer in stream context structure
121
122 // Set to linear stream array
123 XHCI_STREAM_SCT_SET(xhci_ep->primary_stream_ctx_array[index], 1);
124 }
125}
126
127static void setup_stream_context(xhci_endpoint_t *xhci_ep, xhci_ep_ctx_t *ctx, unsigned pstreams) {
128 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(xhci_ep));
129 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, xhci_ep->base.max_packet_size);
130 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, xhci_ep->max_burst);
131 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
132
133 XHCI_EP_MAX_P_STREAMS_SET(*ctx, pstreams);
134 XHCI_EP_TR_DPTR_SET(*ctx, addr_to_phys(xhci_ep->primary_stream_ctx_array));
135 // TODO: set HID?
136 XHCI_EP_LSA_SET(*ctx, 1);
137}
138
139int xhci_endpoint_request_streams(xhci_hc_t *hc, xhci_device_t *dev, xhci_endpoint_t *xhci_ep, unsigned count) {
140 if (xhci_ep->base.transfer_type != USB_TRANSFER_BULK
141 || xhci_ep->base.speed != USB_SPEED_SUPER) {
142 usb_log_error("Streams are only supported by superspeed bulk endpoints.");
143 return EINVAL;
144 }
145
146 if (!primary_stream_ctx_array_max_size(xhci_ep)) {
147 usb_log_error("Streams are not supported by endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
148 return EINVAL;
149 }
150
151 uint8_t max_psa_size = 2 << XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PSA_SIZE);
152 if (count > max_psa_size) {
153 // We don't support secondary stream arrays yet, so we just give up for this
154 return ENOTSUP;
155 }
156
157 if (count > (unsigned) (1 << xhci_ep->max_streams)) {
158 usb_log_error("Endpoint " XHCI_EP_FMT " supports only %u streams.",
159 XHCI_EP_ARGS(*xhci_ep), (1 << xhci_ep->max_streams));
160 return EINVAL;
161 }
162
163 if (count <= 1024) {
164 usb_log_debug2("Allocating primary stream context array of size %u for endpoint " XHCI_EP_FMT,
165 count, XHCI_EP_ARGS(*xhci_ep));
166 xhci_ep->primary_stream_ctx_array = malloc32(count * sizeof(xhci_stream_ctx_t));
167 if (!xhci_ep->primary_stream_ctx_array) {
168 return ENOMEM;
169 }
170
171 // FIXME: count should be rounded to nearest power of 2 for xHC, workaround for now
172 count = 1024;
173 // FIXME: pstreams are "log2(count) - 1"
174 const size_t pstreams = 9;
175 xhci_ep->primary_stream_ctx_array_size = count;
176
177 memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
178 initialize_primary_streams(hc, xhci_ep, count);
179
180 xhci_ep_ctx_t ep_ctx;
181 setup_stream_context(xhci_ep, &ep_ctx, pstreams);
182 return hc_add_endpoint(hc, dev->slot_id, xhci_endpoint_index(xhci_ep), &ep_ctx);
183 }
184 // Complex stuff not supported yet
185 return ENOTSUP;
186}
187
188int xhci_endpoint_alloc_transfer_ds(xhci_endpoint_t *xhci_ep)
189{
190 /* Can't use XHCI_EP_FMT because the endpoint may not have device. */
191 usb_log_debug2("Allocating main transfer ring for endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
192
193 xhci_ep->primary_stream_ctx_array = NULL;
194
195 int err;
196 if ((err = xhci_trb_ring_init(&xhci_ep->ring))) {
197 return err;
198 }
199
200 return EOK;
201}
202
203void xhci_endpoint_free_transfer_ds(xhci_endpoint_t *xhci_ep)
204{
205 if (endpoint_using_streams(xhci_ep)) {
206 usb_log_debug2("Freeing primary stream context array of endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
207
208 // maybe check if LSA, then skip?
209 // for (size_t index = 0; index < primary_stream_ctx_array_size(xhci_ep); ++index) {
210 // xhci_stream_ctx_t *primary_ctx = xhci_ep->primary_stream_ctx_array + index;
211 // if (primary_stream_ctx_has_secondary_array(primary_ctx)) {
212 // // uintptr_t phys = XHCI_STREAM_DEQ_PTR(*primary_ctx);
213 // /* size_t size = */ secondary_stream_ctx_array_size(primary_ctx);
214 // // TODO: somehow map the address to virtual and free the secondary array
215 // }
216 // }
217 for (size_t index = 0; index < xhci_ep->primary_stream_ctx_array_size; ++index) {
218 // FIXME: Get the trb ring associated with stream [index] and fini it
219 }
220 free32(xhci_ep->primary_stream_ctx_array);
221 } else {
222 usb_log_debug2("Freeing main transfer ring of endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
223
224 xhci_trb_ring_fini(&xhci_ep->ring);
225 }
226}
227
228/** See section 4.5.1 of the xHCI spec.
229 */
230uint8_t xhci_endpoint_dci(xhci_endpoint_t *ep)
231{
232 return (2 * ep->base.endpoint) +
233 (ep->base.transfer_type == USB_TRANSFER_CONTROL
234 || ep->base.direction == USB_DIRECTION_IN);
235}
236
237/** Return an index to the endpoint array. The indices are assigned as follows:
238 * 0 EP0 BOTH
239 * 1 EP1 OUT
240 * 2 EP1 IN
241 *
242 * For control endpoints >0, the IN endpoint index is used.
243 *
244 * The index returned must be usually offset by a number of contexts preceding
245 * the endpoint contexts themselves.
246 */
247uint8_t xhci_endpoint_index(xhci_endpoint_t *ep)
248{
249 return xhci_endpoint_dci(ep) - 1;
250}
251
252static void setup_control_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
253{
254 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
255 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
256 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst);
257 XHCI_EP_MULT_SET(*ctx, ep->mult);
258 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
259 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
260 XHCI_EP_DCS_SET(*ctx, 1);
261}
262
263static void setup_bulk_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
264{
265 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
266 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
267 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst);
268 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
269
270 XHCI_EP_MAX_P_STREAMS_SET(*ctx, 0);
271 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
272 XHCI_EP_DCS_SET(*ctx, 1);
273}
274
275static void setup_isoch_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
276{
277 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
278 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size & 0x07FF);
279 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst);
280 XHCI_EP_MULT_SET(*ctx, ep->mult);
281 XHCI_EP_ERROR_COUNT_SET(*ctx, 0);
282 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
283 XHCI_EP_DCS_SET(*ctx, 1);
284 // TODO: max ESIT payload
285}
286
287static void setup_interrupt_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
288{
289 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
290 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size & 0x07FF);
291 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst);
292 XHCI_EP_MULT_SET(*ctx, 0);
293 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
294 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
295 XHCI_EP_DCS_SET(*ctx, 1);
296 // TODO: max ESIT payload
297}
298
299typedef void (*setup_ep_ctx_helper)(xhci_endpoint_t *, xhci_ep_ctx_t *);
300
301static const setup_ep_ctx_helper setup_ep_ctx_helpers[] = {
302 [USB_TRANSFER_CONTROL] = setup_control_ep_ctx,
303 [USB_TRANSFER_ISOCHRONOUS] = setup_isoch_ep_ctx,
304 [USB_TRANSFER_BULK] = setup_bulk_ep_ctx,
305 [USB_TRANSFER_INTERRUPT] = setup_interrupt_ep_ctx,
306};
307
308void xhci_setup_endpoint_context(xhci_endpoint_t *ep, xhci_ep_ctx_t *ep_ctx)
309{
310 assert(ep);
311 assert(ep_ctx);
312
313 usb_transfer_type_t tt = ep->base.transfer_type;
314 assert(tt < ARRAY_SIZE(setup_ep_ctx_helpers));
315
316 memset(ep_ctx, 0, sizeof(*ep_ctx));
317 setup_ep_ctx_helpers[tt](ep, ep_ctx);
318}
319
320int xhci_device_add_endpoint(xhci_device_t *dev, xhci_endpoint_t *ep)
321{
322 assert(dev);
323 assert(ep);
324
325 /* Offline devices don't create new endpoints other than EP0. */
326 if (!dev->online && ep->base.endpoint > 0) {
327 return EAGAIN;
328 }
329
330 const usb_endpoint_t ep_num = ep->base.endpoint;
331
332 if (dev->endpoints[ep_num])
333 return EEXIST;
334
335 /* Device reference */
336 endpoint_add_ref(&ep->base);
337 ep->base.device = &dev->base;
338 dev->endpoints[ep_num] = ep;
339
340 return EOK;
341}
342
343void xhci_device_remove_endpoint(xhci_endpoint_t *ep)
344{
345 assert(ep);
346 xhci_device_t *dev = xhci_device_get(ep->base.device);
347
348 assert(dev->endpoints[ep->base.endpoint]);
349 dev->endpoints[ep->base.endpoint] = NULL;
350 ep->base.device = NULL;
351
352 endpoint_del_ref(&ep->base);
353}
354
355xhci_endpoint_t *xhci_device_get_endpoint(xhci_device_t *dev, usb_endpoint_t ep)
356{
357 return dev->endpoints[ep];
358}
359
360/**
361 * @}
362 */
Note: See TracBrowser for help on using the repository browser.