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

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

Removed hardcoded target for doorbell

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