source: mainline/uspace/drv/bus/usb/xhci/endpoint.c@ 81487c4a

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 81487c4a was 81487c4a, checked in by Petr Manek <petr.manek@…>, 8 years ago

Changed PSA allocation to conform with specification.

  • Property mode set to 100644
File size: 9.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
42#include "bus.h"
43#include "commands.h"
44#include "endpoint.h"
45
46int xhci_endpoint_init(xhci_endpoint_t *xhci_ep, xhci_bus_t *xhci_bus)
47{
48 assert(xhci_ep);
49 assert(xhci_bus);
50
51 bus_t *bus = &xhci_bus->base;
52 endpoint_t *ep = &xhci_ep->base;
53
54 endpoint_init(ep, bus);
55
56 return EOK;
57}
58
59void xhci_endpoint_fini(xhci_endpoint_t *xhci_ep)
60{
61 assert(xhci_ep);
62
63 // TODO: Something missed?
64}
65
66static bool endpoint_uses_streams(xhci_endpoint_t *xhci_ep)
67{
68 return xhci_ep->base.transfer_type == USB_TRANSFER_BULK
69 && xhci_ep->max_streams;
70}
71
72static size_t primary_stream_ctx_array_size(xhci_endpoint_t *xhci_ep)
73{
74 if (!endpoint_uses_streams(xhci_ep))
75 return 0;
76
77 /* Section 6.2.3, Table 61 */
78 return 1 << (xhci_ep->max_streams + 1);
79}
80
81int xhci_endpoint_alloc_transfer_ds(xhci_endpoint_t *xhci_ep)
82{
83 int err;
84
85 if (endpoint_uses_streams(xhci_ep)) {
86 /* Set up primary stream context array if needed. */
87 const size_t size = primary_stream_ctx_array_size(xhci_ep);
88
89 xhci_ep->primary_stream_ctx_array = malloc32(size * sizeof(xhci_stream_ctx_t));
90 if (!xhci_ep->primary_stream_ctx_array) {
91 return ENOMEM;
92 }
93
94 memset(xhci_ep->primary_stream_ctx_array, 0, size * sizeof(xhci_stream_ctx_t));
95 } else {
96 xhci_ep->primary_stream_ctx_array = NULL;
97 if ((err = xhci_trb_ring_init(&xhci_ep->ring))) {
98 return err;
99 }
100 }
101
102 return EOK;
103}
104
105int xhci_endpoint_free_transfer_ds(xhci_endpoint_t *xhci_ep)
106{
107 int err;
108
109 if (endpoint_uses_streams(xhci_ep)) {
110 // TODO: What about secondaries?
111 free32(xhci_ep->primary_stream_ctx_array);
112 } else {
113 if ((err = xhci_trb_ring_fini(&xhci_ep->ring))) {
114 return err;
115 }
116 }
117
118 return EOK;
119}
120
121/** See section 4.5.1 of the xHCI spec.
122 */
123uint8_t xhci_endpoint_dci(xhci_endpoint_t *ep)
124{
125 return (2 * ep->base.target.endpoint) +
126 (ep->base.transfer_type == USB_TRANSFER_CONTROL
127 || ep->base.direction == USB_DIRECTION_IN);
128}
129
130/** Return an index to the endpoint array. The indices are assigned as follows:
131 * 0 EP0 BOTH
132 * 1 EP1 OUT
133 * 2 EP1 IN
134 *
135 * For control endpoints >0, the IN endpoint index is used.
136 *
137 * The index returned must be usually offset by a number of contexts preceding
138 * the endpoint contexts themselves.
139 */
140uint8_t xhci_endpoint_index(xhci_endpoint_t *ep)
141{
142 return xhci_endpoint_dci(ep) - 1;
143}
144
145static int xhci_endpoint_type(xhci_endpoint_t *ep)
146{
147 const bool in = ep->base.direction == USB_DIRECTION_IN;
148
149 switch (ep->base.transfer_type) {
150 case USB_TRANSFER_CONTROL:
151 return EP_TYPE_CONTROL;
152
153 case USB_TRANSFER_ISOCHRONOUS:
154 return in ? EP_TYPE_ISOCH_IN
155 : EP_TYPE_ISOCH_OUT;
156
157 case USB_TRANSFER_BULK:
158 return in ? EP_TYPE_BULK_IN
159 : EP_TYPE_BULK_OUT;
160
161 case USB_TRANSFER_INTERRUPT:
162 return in ? EP_TYPE_INTERRUPT_IN
163 : EP_TYPE_INTERRUPT_OUT;
164 }
165
166 return EP_TYPE_INVALID;
167}
168
169static void setup_control_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
170{
171 // EP0 is configured elsewhere.
172 assert(ep->base.target.endpoint > 0);
173
174 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
175 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
176 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
177 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
178 XHCI_EP_DCS_SET(*ctx, 1);
179}
180
181static void setup_bulk_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
182{
183 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
184 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
185 XHCI_EP_MAX_BURST_SIZE_SET(*ctx,
186 xhci_device_get(ep->base.device)->usb3 ? ep->max_burst : 0);
187 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
188
189 if (endpoint_uses_streams(ep)) {
190 XHCI_EP_MAX_P_STREAMS_SET(*ctx, ep->max_streams);
191 XHCI_EP_TR_DPTR_SET(*ctx, addr_to_phys(ep->primary_stream_ctx_array));
192 // TODO: set HID
193 // TODO: set LSA
194 } else {
195 XHCI_EP_MAX_P_STREAMS_SET(*ctx, 0);
196 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
197 XHCI_EP_DCS_SET(*ctx, 1);
198 }
199}
200
201static void setup_isoch_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
202{
203 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
204 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size & 0x07FF);
205 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst);
206 XHCI_EP_MULT_SET(*ctx, ep->mult);
207 XHCI_EP_ERROR_COUNT_SET(*ctx, 0);
208 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
209 XHCI_EP_DCS_SET(*ctx, 1);
210 // TODO: max ESIT payload
211}
212
213static void setup_interrupt_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
214{
215 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
216 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size & 0x07FF);
217 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst);
218 XHCI_EP_MULT_SET(*ctx, 0);
219 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
220 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
221 XHCI_EP_DCS_SET(*ctx, 1);
222 // TODO: max ESIT payload
223}
224
225typedef void (*setup_ep_ctx_helper)(xhci_endpoint_t *, xhci_ep_ctx_t *);
226
227static const setup_ep_ctx_helper setup_ep_ctx_helpers[] = {
228 [USB_TRANSFER_CONTROL] = setup_control_ep_ctx,
229 [USB_TRANSFER_ISOCHRONOUS] = setup_isoch_ep_ctx,
230 [USB_TRANSFER_BULK] = setup_bulk_ep_ctx,
231 [USB_TRANSFER_INTERRUPT] = setup_interrupt_ep_ctx,
232};
233
234int xhci_device_add_endpoint(xhci_device_t *dev, xhci_endpoint_t *ep)
235{
236 assert(dev);
237 assert(ep);
238
239 /* Offline devices don't create new endpoints other than EP0. */
240 if (!dev->online) {
241 return EAGAIN;
242 }
243
244 int err = ENOMEM;
245 const usb_endpoint_t ep_num = ep->base.target.endpoint;
246
247 assert(&dev->base == ep->base.device);
248 assert(dev->base.address == ep->base.target.address);
249 assert(!dev->endpoints[ep_num]);
250
251 dev->endpoints[ep_num] = ep;
252 ++dev->active_endpoint_count;
253
254 if (ep_num == 0)
255 /* EP 0 is initialized while setting up the device,
256 * so we must not issue the command now. */
257 return EOK;
258
259 // FIXME: Set these from usb_superspeed_endpoint_companion_descriptor_t:
260 ep->max_streams = 0;
261 ep->max_burst = 0;
262 ep->mult = 0;
263
264 xhci_endpoint_alloc_transfer_ds(ep);
265
266 // Prepare input context.
267 xhci_input_ctx_t *ictx = malloc32(sizeof(xhci_input_ctx_t));
268 if (!ictx)
269 goto err;
270
271 memset(ictx, 0, sizeof(xhci_input_ctx_t));
272
273 // Quoting sec. 4.6.6: A1, D0, D1 are down, A0 is up.
274 XHCI_INPUT_CTRL_CTX_ADD_CLEAR(ictx->ctrl_ctx, 1);
275 XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 0);
276 XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 1);
277 XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 0);
278
279 const unsigned ep_idx = xhci_endpoint_index(ep);
280 XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, ep_idx + 1); /* Preceded by slot ctx */
281
282 xhci_ep_ctx_t *ep_ctx = &ictx->endpoint_ctx[ep_idx];
283 setup_ep_ctx_helpers[ep->base.transfer_type](ep, ep_ctx);
284
285 // Issue configure endpoint command (sec 4.3.5).
286 xhci_cmd_t cmd;
287 xhci_cmd_init(&cmd);
288
289 cmd.slot_id = dev->slot_id;
290 xhci_send_configure_endpoint_command(dev->hc, &cmd, ictx);
291 if ((err = xhci_cmd_wait(&cmd, XHCI_DEFAULT_TIMEOUT)) != EOK)
292 goto err_ictx;
293
294 xhci_cmd_fini(&cmd);
295
296 free32(ictx);
297 return EOK;
298
299err_ictx:
300 free32(ictx);
301err:
302 dev->endpoints[ep_num] = NULL;
303 dev->active_endpoint_count--;
304 return err;
305}
306
307int xhci_device_remove_endpoint(xhci_device_t *dev, xhci_endpoint_t *ep)
308{
309 assert(&dev->base == ep->base.device);
310 assert(dev->base.address == ep->base.target.address);
311 assert(dev->endpoints[ep->base.target.endpoint]);
312
313 // TODO: Issue configure endpoint command to drop this endpoint.
314
315 // FIXME: Ignoring return code.
316 xhci_endpoint_free_transfer_ds(ep);
317
318 dev->endpoints[ep->base.target.endpoint] = NULL;
319 --dev->active_endpoint_count;
320 return EOK;
321}
322
323xhci_endpoint_t * xhci_device_get_endpoint(xhci_device_t *dev, usb_endpoint_t ep)
324{
325 return dev->endpoints[ep];
326}
327
328int xhci_device_configure(xhci_device_t *dev, xhci_hc_t *hc)
329{
330 int err;
331
332 // Prepare input context.
333 xhci_input_ctx_t *ictx = malloc(sizeof(xhci_input_ctx_t));
334 if (!ictx) {
335 return ENOMEM;
336 }
337
338 memset(ictx, 0, sizeof(xhci_input_ctx_t));
339
340 // Quoting sec. 4.6.6: A1, D0, D1 are down, A0 is up.
341 XHCI_INPUT_CTRL_CTX_ADD_CLEAR(ictx->ctrl_ctx, 1);
342 XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 0);
343 XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 1);
344 XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 0);
345
346 // TODO: Set slot context and other flags. (probably forgot a lot of 'em)
347
348 // Issue configure endpoint command (sec 4.3.5).
349 xhci_cmd_t cmd;
350 xhci_cmd_init(&cmd);
351
352 cmd.slot_id = dev->slot_id;
353 xhci_send_configure_endpoint_command(hc, &cmd, ictx);
354 if ((err = xhci_cmd_wait(&cmd, XHCI_DEFAULT_TIMEOUT)) != EOK)
355 goto err_cmd;
356
357 xhci_cmd_fini(&cmd);
358 return EOK;
359
360err_cmd:
361 free(ictx);
362 return err;
363}
364
365/**
366 * @}
367 */
Note: See TracBrowser for help on using the repository browser.