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

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

Final change with respect to command timeouts. Because communication channels are inherently noisy.

  • Property mode set to 100644
File size: 9.3 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 xhci_ep->device = NULL;
56
57 return EOK;
58}
59
60void xhci_endpoint_fini(xhci_endpoint_t *xhci_ep)
61{
62 assert(xhci_ep);
63
64 /* FIXME: Tear down TR's? */
65}
66
67int xhci_device_init(xhci_device_t *dev, xhci_bus_t *bus, usb_address_t address)
68{
69 memset(&dev->endpoints, 0, sizeof(dev->endpoints));
70 dev->active_endpoint_count = 0;
71 dev->address = address;
72 dev->slot_id = 0;
73
74 return EOK;
75}
76
77void xhci_device_fini(xhci_device_t *dev)
78{
79 // TODO: Check that all endpoints are dead.
80 assert(dev);
81}
82
83/** Return an index to the endpoint array. The indices are assigned as follows:
84 * 0 EP0 BOTH
85 * 1 EP1 OUT
86 * 2 EP1 IN
87 *
88 * For control endpoints >0, the IN endpoint index is used.
89 *
90 * The index returned must be usually offset by a number of contexts preceding
91 * the endpoint contexts themselves.
92 */
93uint8_t xhci_endpoint_index(xhci_endpoint_t *ep)
94{
95 return (2 * ep->base.target.endpoint)
96 - (ep->base.direction == USB_DIRECTION_OUT);
97}
98
99static int xhci_endpoint_type(xhci_endpoint_t *ep)
100{
101 const bool in = ep->base.direction == USB_DIRECTION_IN;
102
103 switch (ep->base.transfer_type) {
104 case USB_TRANSFER_CONTROL:
105 return EP_TYPE_CONTROL;
106
107 case USB_TRANSFER_ISOCHRONOUS:
108 return in ? EP_TYPE_ISOCH_IN
109 : EP_TYPE_ISOCH_OUT;
110
111 case USB_TRANSFER_BULK:
112 return in ? EP_TYPE_BULK_IN
113 : EP_TYPE_BULK_OUT;
114
115 case USB_TRANSFER_INTERRUPT:
116 return in ? EP_TYPE_INTERRUPT_IN
117 : EP_TYPE_INTERRUPT_OUT;
118 }
119
120 return EP_TYPE_INVALID;
121}
122
123static void setup_control_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx,
124 xhci_trb_ring_t *ring)
125{
126 // EP0 is configured elsewhere.
127 assert(ep->base.target.endpoint > 0);
128
129 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
130 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
131 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
132 XHCI_EP_TR_DPTR_SET(*ctx, ring->dequeue);
133 XHCI_EP_DCS_SET(*ctx, 1);
134}
135
136static void setup_bulk_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx,
137 xhci_trb_ring_t *ring, usb_superspeed_endpoint_companion_descriptor_t *ss_desc)
138{
139
140 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
141 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
142 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->device->usb3 ? ss_desc->max_burst : 0);
143 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
144
145 // FIXME: Get maxStreams and other things from ss_desc
146 const uint8_t maxStreams = 0;
147 if (maxStreams > 0) {
148 // TODO: allocate and clear primary stream array
149 // TODO: XHCI_EP_MAX_P_STREAMS_SET(ctx, psa_size);
150 // TODO: XHCI_EP_TR_DPTR_SET(ctx, psa_start_phys_addr);
151 // TODO: set HID
152 // TODO: set LSA
153 } else {
154 XHCI_EP_MAX_P_STREAMS_SET(*ctx, 0);
155 /* FIXME physical pointer? */
156 XHCI_EP_TR_DPTR_SET(*ctx, ring->dequeue);
157 XHCI_EP_DCS_SET(*ctx, 1);
158 }
159}
160
161static void setup_isoch_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx,
162 xhci_trb_ring_t *ring, usb_superspeed_endpoint_companion_descriptor_t *ss_desc)
163{
164 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
165 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size & 0x07FF);
166 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ss_desc->max_burst);
167 // FIXME: get Mult field from SS companion descriptor somehow
168 XHCI_EP_MULT_SET(*ctx, 0);
169 XHCI_EP_ERROR_COUNT_SET(*ctx, 0);
170 /* FIXME physical pointer? */
171 XHCI_EP_TR_DPTR_SET(*ctx, ring->dequeue);
172 XHCI_EP_DCS_SET(*ctx, 1);
173 // TODO: max ESIT payload
174}
175
176static void setup_interrupt_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx,
177 xhci_trb_ring_t *ring, usb_superspeed_endpoint_companion_descriptor_t *ss_desc)
178{
179 XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
180 XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size & 0x07FF);
181 XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ss_desc->max_burst);
182 XHCI_EP_MULT_SET(*ctx, 0);
183 XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
184 /* FIXME physical pointer? */
185 XHCI_EP_TR_DPTR_SET(*ctx, ring->dequeue);
186 XHCI_EP_DCS_SET(*ctx, 1);
187 // TODO: max ESIT payload
188}
189
190int xhci_device_add_endpoint(xhci_device_t *dev, xhci_endpoint_t *ep)
191{
192 assert(dev->address == ep->base.target.address);
193 assert(!dev->endpoints[ep->base.target.endpoint]);
194 assert(!ep->device);
195
196 int err;
197 xhci_input_ctx_t *ictx = NULL;
198 xhci_trb_ring_t *ep_ring = NULL;
199 if (ep->base.target.endpoint > 0) {
200 // FIXME: Retrieve this from somewhere, if applicable.
201 usb_superspeed_endpoint_companion_descriptor_t ss_desc;
202 memset(&ss_desc, 0, sizeof(ss_desc));
203
204 // Prepare input context.
205 ictx = malloc32(sizeof(xhci_input_ctx_t));
206 if (!ictx) {
207 return ENOMEM;
208 }
209
210 memset(ictx, 0, sizeof(xhci_input_ctx_t));
211
212 // Quoting sec. 4.6.6: A1, D0, D1 are down, A0 is up.
213 XHCI_INPUT_CTRL_CTX_ADD_CLEAR(ictx->ctrl_ctx, 1);
214 XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 0);
215 XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 1);
216 XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 0);
217
218 const uint8_t ep_idx = xhci_endpoint_index(ep);
219 XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, ep_idx + 1); /* Preceded by slot ctx */
220
221 ep_ring = malloc(sizeof(xhci_trb_ring_t));
222 if (!ep_ring) {
223 err = ENOMEM;
224 goto err_ictx;
225 }
226
227 // FIXME: This ring need not be allocated all the time.
228 err = xhci_trb_ring_init(ep_ring);
229 if (err)
230 goto err_ring;
231
232 switch (ep->base.transfer_type) {
233 case USB_TRANSFER_CONTROL:
234 setup_control_ep_ctx(ep, &ictx->endpoint_ctx[ep_idx], ep_ring);
235 break;
236
237 case USB_TRANSFER_BULK:
238 setup_bulk_ep_ctx(ep, &ictx->endpoint_ctx[ep_idx], ep_ring, &ss_desc);
239 break;
240
241 case USB_TRANSFER_ISOCHRONOUS:
242 setup_isoch_ep_ctx(ep, &ictx->endpoint_ctx[ep_idx], ep_ring, &ss_desc);
243 break;
244
245 case USB_TRANSFER_INTERRUPT:
246 setup_interrupt_ep_ctx(ep, &ictx->endpoint_ctx[ep_idx], ep_ring, &ss_desc);
247 break;
248
249 }
250
251 dev->hc->dcbaa_virt[dev->slot_id].trs[ep->base.target.endpoint] = ep_ring;
252
253 // Issue configure endpoint command (sec 4.3.5).
254 xhci_cmd_t cmd;
255 xhci_cmd_init(&cmd);
256
257 cmd.slot_id = dev->slot_id;
258 xhci_send_configure_endpoint_command(dev->hc, &cmd, ictx);
259 if ((err = xhci_cmd_wait(&cmd, XHCI_DEFAULT_TIMEOUT)) != EOK)
260 goto err_cmd;
261
262 xhci_cmd_fini(&cmd);
263 }
264
265 ep->device = dev;
266 dev->endpoints[ep->base.target.endpoint] = ep;
267 ++dev->active_endpoint_count;
268 return EOK;
269
270err_cmd:
271err_ring:
272 if (ep_ring) {
273 xhci_trb_ring_fini(ep_ring);
274 free(ep_ring);
275 }
276err_ictx:
277 free(ictx);
278 return err;
279}
280
281int xhci_device_remove_endpoint(xhci_device_t *dev, xhci_endpoint_t *ep)
282{
283 assert(dev->address == ep->base.target.address);
284 assert(dev->endpoints[ep->base.target.endpoint]);
285 assert(dev == ep->device);
286
287 // TODO: Issue configure endpoint command to drop this endpoint.
288
289 ep->device = NULL;
290 dev->endpoints[ep->base.target.endpoint] = NULL;
291 --dev->active_endpoint_count;
292 return EOK;
293}
294
295xhci_endpoint_t * xhci_device_get_endpoint(xhci_device_t *dev, usb_endpoint_t ep)
296{
297 return dev->endpoints[ep];
298}
299
300int xhci_device_configure(xhci_device_t *dev, xhci_hc_t *hc)
301{
302 int err;
303
304 // Prepare input context.
305 xhci_input_ctx_t *ictx = malloc(sizeof(xhci_input_ctx_t));
306 if (!ictx) {
307 return ENOMEM;
308 }
309
310 memset(ictx, 0, sizeof(xhci_input_ctx_t));
311
312 // Quoting sec. 4.6.6: A1, D0, D1 are down, A0 is up.
313 XHCI_INPUT_CTRL_CTX_ADD_CLEAR(ictx->ctrl_ctx, 1);
314 XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 0);
315 XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 1);
316 XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 0);
317
318 // TODO: Set slot context and other flags. (probably forgot a lot of 'em)
319
320 // Issue configure endpoint command (sec 4.3.5).
321 xhci_cmd_t cmd;
322 xhci_cmd_init(&cmd);
323
324 cmd.slot_id = dev->slot_id;
325 xhci_send_configure_endpoint_command(hc, &cmd, ictx);
326 if ((err = xhci_cmd_wait(&cmd, XHCI_DEFAULT_TIMEOUT)) != EOK)
327 goto err_cmd;
328
329 xhci_cmd_fini(&cmd);
330 return EOK;
331
332err_cmd:
333 free(ictx);
334 return err;
335}
336
337/**
338 * @}
339 */
Note: See TracBrowser for help on using the repository browser.