blob: b697b0fd0f9aca2ff2f80161e4ae0623776b389c [file] [log] [blame]
Nico Huber90292652013-06-13 14:37:15 +02001/*
2 * This file is part of the libpayload project.
3 *
4 * Copyright (C) 2013 secunet Security Networks AG
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. 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 * 3. 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 AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30//#define XHCI_SPEW_DEBUG
31
32#include <arch/virtual.h>
Julius Wernere00ba212013-09-24 20:03:54 -070033#include <usb/usb.h>
Nico Huber90292652013-06-13 14:37:15 +020034#include "xhci_private.h"
35
36static u32
37xhci_gen_route(xhci_t *const xhci, const int hubport, const int hubaddr)
38{
39 if (!hubaddr)
40 return 0;
Julius Werner1f864342013-09-03 17:15:31 -070041 u32 route_string = SC_GET(ROUTE, xhci->dev[hubaddr].ctx.slot);
Nico Huber90292652013-06-13 14:37:15 +020042 int i;
43 for (i = 0; i < 20; i += 4) {
44 if (!(route_string & (0xf << i))) {
45 route_string |= (hubport & 0xf) << i;
46 break;
47 }
48 }
49 return route_string;
50}
51
52static int
53xhci_get_rh_port(xhci_t *const xhci, const int hubport, const int hubaddr)
54{
55 if (!hubaddr)
56 return hubport;
Julius Werner1f864342013-09-03 17:15:31 -070057 return SC_GET(RHPORT, xhci->dev[hubaddr].ctx.slot);
Nico Huber90292652013-06-13 14:37:15 +020058}
59
60static int
Julius Wernere00ba212013-09-24 20:03:54 -070061xhci_get_tt(xhci_t *const xhci, const usb_speed speed,
Nico Huber90292652013-06-13 14:37:15 +020062 const int hubport, const int hubaddr,
63 int *const tt, int *const tt_port)
64{
65 if (!hubaddr)
66 return 0;
Julius Werner1f864342013-09-03 17:15:31 -070067 const slotctx_t *const slot = xhci->dev[hubaddr].ctx.slot;
68 if ((*tt = SC_GET(TTID, slot))) {
69 *tt_port = SC_GET(TTPORT, slot);
Julius Wernere00ba212013-09-24 20:03:54 -070070 } else if (speed < HIGH_SPEED &&
71 SC_GET(SPEED1, slot) - 1 == HIGH_SPEED) {
Nico Huber90292652013-06-13 14:37:15 +020072 *tt = hubaddr;
73 *tt_port = hubport;
74 }
75 return *tt != 0;
76}
77
Julius Wernere44a4e82015-07-08 22:36:00 -070078static void
79xhci_reap_slots(xhci_t *const xhci, int skip_slot)
80{
81 int i;
82
83 xhci_debug("xHC resource shortage, trying to reap old slots...\n");
84 for (i = 1; i <= xhci->max_slots_en; i++) {
85 if (i == skip_slot)
86 continue; /* don't reap slot we were working on */
87 if (xhci->dev[i].transfer_rings[1])
88 continue; /* slot still in use */
89 if (!xhci->dev[i].ctx.raw)
90 continue; /* slot already disabled */
91
92 const int cc = xhci_cmd_disable_slot(xhci, i);
93 if (cc != CC_SUCCESS)
94 xhci_debug("Failed to disable slot %d: %d\n", i, cc);
95 else
96 xhci_spew("Successfully reaped slot %d\n", i);
97 xhci->dcbaa[i] = 0;
98 free(xhci->dev[i].ctx.raw);
99 xhci->dev[i].ctx.raw = NULL;
100 }
101}
102
Julius Werner1f864342013-09-03 17:15:31 -0700103static inputctx_t *
104xhci_make_inputctx(const size_t ctxsize)
105{
106 int i;
107 const size_t size = (1 + NUM_EPS) * ctxsize;
108 inputctx_t *const ic = malloc(sizeof(*ic));
109 void *dma_buffer = dma_memalign(64, size);
110
111 if (!ic || !dma_buffer) {
112 free(ic);
113 free(dma_buffer);
114 return NULL;
115 }
116
117 memset(dma_buffer, 0, size);
118 ic->drop = dma_buffer + 0;
119 ic->add = dma_buffer + 4;
120 dma_buffer += ctxsize;
121 for (i = 0; i < NUM_EPS; i++, dma_buffer += ctxsize)
122 ic->dev.ep[i] = dma_buffer;
123
124 return ic;
125}
126
Julius Wernerd13e2c42013-09-17 22:16:04 -0700127usbdev_t *
Julius Wernere00ba212013-09-24 20:03:54 -0700128xhci_set_address (hci_t *controller, usb_speed speed, int hubport, int hubaddr)
Nico Huber90292652013-06-13 14:37:15 +0200129{
130 xhci_t *const xhci = XHCI_INST(controller);
Julius Werner1f864342013-09-03 17:15:31 -0700131 const size_t ctxsize = CTXSIZE(xhci);
132 devinfo_t *di = NULL;
Julius Wernerd13e2c42013-09-17 22:16:04 -0700133 usbdev_t *dev = NULL;
134 int i;
Nico Huber90292652013-06-13 14:37:15 +0200135
Julius Werner1f864342013-09-03 17:15:31 -0700136 inputctx_t *const ic = xhci_make_inputctx(ctxsize);
Nico Huber90292652013-06-13 14:37:15 +0200137 transfer_ring_t *const tr = malloc(sizeof(*tr));
138 if (tr)
139 tr->ring = xhci_align(16, TRANSFER_RING_SIZE * sizeof(trb_t));
Julius Werner1f864342013-09-03 17:15:31 -0700140 if (!ic || !tr || !tr->ring) {
Nico Huber90292652013-06-13 14:37:15 +0200141 xhci_debug("Out of memory\n");
142 goto _free_return;
143 }
144
145 int slot_id;
146 int cc = xhci_cmd_enable_slot(xhci, &slot_id);
Julius Wernere44a4e82015-07-08 22:36:00 -0700147 if (cc == CC_NO_SLOTS_AVAILABLE) {
148 xhci_reap_slots(xhci, 0);
149 cc = xhci_cmd_enable_slot(xhci, &slot_id);
150 }
Nico Huber90292652013-06-13 14:37:15 +0200151 if (cc != CC_SUCCESS) {
152 xhci_debug("Enable slot failed: %d\n", cc);
153 goto _free_return;
154 } else {
155 xhci_debug("Enabled slot %d\n", slot_id);
156 }
157
Julius Werner1f864342013-09-03 17:15:31 -0700158 di = &xhci->dev[slot_id];
159 void *dma_buffer = dma_memalign(64, NUM_EPS * ctxsize);
160 if (!dma_buffer)
Julius Wernerd609e892013-09-25 12:30:07 -0700161 goto _disable_return;
Julius Werner1f864342013-09-03 17:15:31 -0700162 memset(dma_buffer, 0, NUM_EPS * ctxsize);
163 for (i = 0; i < NUM_EPS; i++, dma_buffer += ctxsize)
164 di->ctx.ep[i] = dma_buffer;
165
166 *ic->add = (1 << 0) /* Slot Context */ | (1 << 1) /* EP0 Context */ ;
Nico Huber90292652013-06-13 14:37:15 +0200167
168 SC_SET(ROUTE, ic->dev.slot, xhci_gen_route(xhci, hubport, hubaddr));
Julius Wernere00ba212013-09-24 20:03:54 -0700169 SC_SET(SPEED1, ic->dev.slot, speed + 1);
Nico Huber90292652013-06-13 14:37:15 +0200170 SC_SET(CTXENT, ic->dev.slot, 1); /* the endpoint 0 context */
171 SC_SET(RHPORT, ic->dev.slot, xhci_get_rh_port(xhci, hubport, hubaddr));
172
173 int tt, tt_port;
Julius Wernere00ba212013-09-24 20:03:54 -0700174 if (xhci_get_tt(xhci, speed, hubport, hubaddr, &tt, &tt_port)) {
Nico Huber90292652013-06-13 14:37:15 +0200175 xhci_debug("TT for %d: %d[%d]\n", slot_id, tt, tt_port);
Julius Werner1f864342013-09-03 17:15:31 -0700176 SC_SET(MTT, ic->dev.slot, SC_GET(MTT, xhci->dev[tt].ctx.slot));
Nico Huber90292652013-06-13 14:37:15 +0200177 SC_SET(TTID, ic->dev.slot, tt);
178 SC_SET(TTPORT, ic->dev.slot, tt_port);
179 }
180
Nico Huber90292652013-06-13 14:37:15 +0200181 di->transfer_rings[1] = tr;
182 xhci_init_cycle_ring(tr, TRANSFER_RING_SIZE);
183
Julius Werner1f864342013-09-03 17:15:31 -0700184 ic->dev.ep0->tr_dq_low = virt_to_phys(tr->ring);
185 ic->dev.ep0->tr_dq_high = 0;
Nico Huber90292652013-06-13 14:37:15 +0200186 EC_SET(TYPE, ic->dev.ep0, EP_CONTROL);
187 EC_SET(AVRTRB, ic->dev.ep0, 8);
Varadarajan Narayanan8e0ffe22016-03-17 14:43:35 +0530188 EC_SET(MPS, ic->dev.ep0, speed_to_default_mps(speed));
Nico Huber90292652013-06-13 14:37:15 +0200189 EC_SET(CERR, ic->dev.ep0, 3);
190 EC_SET(DCS, ic->dev.ep0, 1);
191
Julius Werner1f864342013-09-03 17:15:31 -0700192 xhci->dcbaa[slot_id] = virt_to_phys(di->ctx.raw);
Nico Huber90292652013-06-13 14:37:15 +0200193
194 cc = xhci_cmd_address_device(xhci, slot_id, ic);
Julius Wernere44a4e82015-07-08 22:36:00 -0700195 if (cc == CC_RESOURCE_ERROR) {
196 xhci_reap_slots(xhci, slot_id);
197 cc = xhci_cmd_address_device(xhci, slot_id, ic);
198 }
Nico Huber90292652013-06-13 14:37:15 +0200199 if (cc != CC_SUCCESS) {
200 xhci_debug("Address device failed: %d\n", cc);
201 goto _disable_return;
202 } else {
203 xhci_debug("Addressed device %d (USB: %d)\n",
Julius Werner1f864342013-09-03 17:15:31 -0700204 slot_id, SC_GET(UADDR, di->ctx.slot));
Nico Huber90292652013-06-13 14:37:15 +0200205 }
Julius Wernere00ba212013-09-24 20:03:54 -0700206 mdelay(SET_ADDRESS_MDELAY);
Nico Huber90292652013-06-13 14:37:15 +0200207
Julius Wernerd13e2c42013-09-17 22:16:04 -0700208 dev = init_device_entry(controller, slot_id);
209 if (!dev)
Nico Huber90292652013-06-13 14:37:15 +0200210 goto _disable_return;
Julius Wernerd13e2c42013-09-17 22:16:04 -0700211
212 dev->address = slot_id;
213 dev->hub = hubaddr;
214 dev->port = hubport;
215 dev->speed = speed;
216 dev->endpoints[0].dev = dev;
217 dev->endpoints[0].endpoint = 0;
218 dev->endpoints[0].toggle = 0;
219 dev->endpoints[0].direction = SETUP;
220 dev->endpoints[0].type = CONTROL;
221
222 u8 buf[8];
223 if (get_descriptor(dev, gen_bmRequestType(device_to_host, standard_type,
224 dev_recp), DT_DEV, 0, buf, sizeof(buf)) != sizeof(buf)) {
225 usb_debug("first get_descriptor(DT_DEV) failed\n");
226 goto _disable_return;
227 }
228
229 dev->endpoints[0].maxpacketsize = usb_decode_mps0(speed, buf[7]);
230 if (dev->endpoints[0].maxpacketsize != 8) {
Julius Werner1f864342013-09-03 17:15:31 -0700231 memset((void *)ic->dev.ep0, 0x00, ctxsize);
232 *ic->add = (1 << 1); /* EP0 Context */
Julius Wernerd13e2c42013-09-17 22:16:04 -0700233 EC_SET(MPS, ic->dev.ep0, dev->endpoints[0].maxpacketsize);
Nico Huber90292652013-06-13 14:37:15 +0200234 cc = xhci_cmd_evaluate_context(xhci, slot_id, ic);
Julius Wernere44a4e82015-07-08 22:36:00 -0700235 if (cc == CC_RESOURCE_ERROR) {
236 xhci_reap_slots(xhci, slot_id);
237 cc = xhci_cmd_evaluate_context(xhci, slot_id, ic);
238 }
Nico Huber90292652013-06-13 14:37:15 +0200239 if (cc != CC_SUCCESS) {
240 xhci_debug("Context evaluation failed: %d\n", cc);
241 goto _disable_return;
Nico Huber90292652013-06-13 14:37:15 +0200242 }
243 }
244
Nico Huber90292652013-06-13 14:37:15 +0200245 goto _free_ic_return;
246
247_disable_return:
248 xhci_cmd_disable_slot(xhci, slot_id);
249 xhci->dcbaa[slot_id] = 0;
Julius Wernerd609e892013-09-25 12:30:07 -0700250 usb_detach_device(controller, slot_id);
Julius Wernerd13e2c42013-09-17 22:16:04 -0700251 dev = NULL;
Nico Huber90292652013-06-13 14:37:15 +0200252_free_return:
253 if (tr)
254 free((void *)tr->ring);
255 free(tr);
Julius Wernere44a4e82015-07-08 22:36:00 -0700256 if (di) {
Julius Werner1f864342013-09-03 17:15:31 -0700257 free(di->ctx.raw);
Julius Wernere44a4e82015-07-08 22:36:00 -0700258 di->ctx.raw = 0;
259 }
Nico Huber90292652013-06-13 14:37:15 +0200260_free_ic_return:
Julius Werner1f864342013-09-03 17:15:31 -0700261 if (ic)
262 free(ic->raw);
Nico Huber90292652013-06-13 14:37:15 +0200263 free(ic);
Julius Wernerd13e2c42013-09-17 22:16:04 -0700264 return dev;
Nico Huber90292652013-06-13 14:37:15 +0200265}
266
267static int
268xhci_finish_hub_config(usbdev_t *const dev, inputctx_t *const ic)
269{
Julius Werner752fba72015-07-09 16:29:10 -0700270 int type = dev->speed == SUPER_SPEED ? 0x2a : 0x29; /* similar enough */
Julius Wernerd13e2c42013-09-17 22:16:04 -0700271 hub_descriptor_t desc;
272
273 if (get_descriptor(dev, gen_bmRequestType(device_to_host, class_type,
Julius Werner752fba72015-07-09 16:29:10 -0700274 dev_recp), type, 0, &desc, sizeof(desc)) != sizeof(desc)) {
Nico Huber90292652013-06-13 14:37:15 +0200275 xhci_debug("Failed to fetch hub descriptor\n");
276 return COMMUNICATION_ERROR;
277 }
278
279 SC_SET(HUB, ic->dev.slot, 1);
280 SC_SET(MTT, ic->dev.slot, 0); /* No support for Multi-TT */
Julius Wernerd13e2c42013-09-17 22:16:04 -0700281 SC_SET(NPORTS, ic->dev.slot, desc.bNbrPorts);
Nico Huber90292652013-06-13 14:37:15 +0200282 if (dev->speed == HIGH_SPEED)
283 SC_SET(TTT, ic->dev.slot,
Julius Wernerd13e2c42013-09-17 22:16:04 -0700284 (desc.wHubCharacteristics >> 5) & 0x0003);
Nico Huber90292652013-06-13 14:37:15 +0200285
Nico Huber90292652013-06-13 14:37:15 +0200286 return 0;
287}
288
289static size_t
290xhci_bound_interval(const endpoint_t *const ep)
291{
292 if ( (ep->dev->speed == LOW_SPEED &&
293 (ep->type == ISOCHRONOUS ||
294 ep->type == INTERRUPT)) ||
295 (ep->dev->speed == FULL_SPEED &&
296 ep->type == INTERRUPT))
297 {
298 if (ep->interval < 3)
299 return 3;
300 else if (ep->interval > 11)
301 return 11;
302 else
303 return ep->interval;
304 } else {
305 if (ep->interval < 0)
306 return 0;
307 else if (ep->interval > 15)
308 return 15;
309 else
310 return ep->interval;
311 }
312}
313
314static int
315xhci_finish_ep_config(const endpoint_t *const ep, inputctx_t *const ic)
316{
317 xhci_t *const xhci = XHCI_INST(ep->dev->controller);
Nico Huber90292652013-06-13 14:37:15 +0200318 const int ep_id = xhci_ep_id(ep);
319 xhci_debug("ep_id: %d\n", ep_id);
320 if (ep_id <= 1 || 32 <= ep_id)
321 return DRIVER_ERROR;
322
323 transfer_ring_t *const tr = malloc(sizeof(*tr));
324 if (tr)
325 tr->ring = xhci_align(16, TRANSFER_RING_SIZE * sizeof(trb_t));
326 if (!tr || !tr->ring) {
327 free(tr);
328 xhci_debug("Out of memory\n");
329 return OUT_OF_MEMORY;
330 }
Julius Werner1f864342013-09-03 17:15:31 -0700331 xhci->dev[ep->dev->address].transfer_rings[ep_id] = tr;
Nico Huber90292652013-06-13 14:37:15 +0200332 xhci_init_cycle_ring(tr, TRANSFER_RING_SIZE);
333
Julius Werner1f864342013-09-03 17:15:31 -0700334 *ic->add |= (1 << ep_id);
Nico Huber90292652013-06-13 14:37:15 +0200335 if (SC_GET(CTXENT, ic->dev.slot) < ep_id)
336 SC_SET(CTXENT, ic->dev.slot, ep_id);
337
Julius Werner1f864342013-09-03 17:15:31 -0700338 epctx_t *const epctx = ic->dev.ep[ep_id];
Nico Huber90292652013-06-13 14:37:15 +0200339 xhci_debug("Filling epctx (@%p)\n", epctx);
340 epctx->tr_dq_low = virt_to_phys(tr->ring);
341 epctx->tr_dq_high = 0;
Julius Werner1f864342013-09-03 17:15:31 -0700342 EC_SET(INTVAL, epctx, xhci_bound_interval(ep));
343 EC_SET(CERR, epctx, 3);
344 EC_SET(TYPE, epctx, ep->type | ((ep->direction != OUT) << 2));
345 EC_SET(MPS, epctx, ep->maxpacketsize);
346 EC_SET(DCS, epctx, 1);
Nico Huber90292652013-06-13 14:37:15 +0200347 size_t avrtrb;
348 switch (ep->type) {
349 case BULK: case ISOCHRONOUS: avrtrb = 3 * 1024; break;
350 case INTERRUPT: avrtrb = 1024; break;
351 default: avrtrb = 8; break;
352 }
Julius Werner1f864342013-09-03 17:15:31 -0700353 EC_SET(AVRTRB, epctx, avrtrb);
354 EC_SET(MXESIT, epctx, EC_GET(MPS, epctx) * EC_GET(MBS, epctx));
Nico Huber90292652013-06-13 14:37:15 +0200355
Yidi Lind42ee152015-05-07 15:36:04 +0800356 if (IS_ENABLED(CONFIG_LP_USB_XHCI_MTK_QUIRK)) {
357 /* The MTK xHCI defines some extra SW parameters which are
358 * put into reserved DWs in Slot and Endpoint Contexts for
359 * synchronous endpoints. But for non-isochronous transfers,
360 * it is enough to set the following two fields to 1, and others
361 * are set to 0.
362 */
363 EC_SET(BPKTS, epctx, 1);
364 EC_SET(BBM, epctx, 1);
365 }
Nico Huber90292652013-06-13 14:37:15 +0200366 return 0;
367}
368
369int
370xhci_finish_device_config(usbdev_t *const dev)
371{
372 xhci_t *const xhci = XHCI_INST(dev->controller);
Julius Wernere44a4e82015-07-08 22:36:00 -0700373 int slot_id = dev->address;
374 devinfo_t *const di = &xhci->dev[slot_id];
Nico Huber90292652013-06-13 14:37:15 +0200375
376 int i, ret = 0;
377
Julius Werner1f864342013-09-03 17:15:31 -0700378 inputctx_t *const ic = xhci_make_inputctx(CTXSIZE(xhci));
Nico Huber90292652013-06-13 14:37:15 +0200379 if (!ic) {
380 xhci_debug("Out of memory\n");
381 return OUT_OF_MEMORY;
382 }
Nico Huber90292652013-06-13 14:37:15 +0200383
Julius Werner1f864342013-09-03 17:15:31 -0700384 *ic->add = (1 << 0); /* Slot Context */
Nico Huber90292652013-06-13 14:37:15 +0200385
Julius Werner1f864342013-09-03 17:15:31 -0700386 xhci_dump_slotctx(di->ctx.slot);
387 ic->dev.slot->f1 = di->ctx.slot->f1;
388 ic->dev.slot->f2 = di->ctx.slot->f2;
389 ic->dev.slot->f3 = di->ctx.slot->f3;
Julius Werner752fba72015-07-09 16:29:10 -0700390 /* f4 *must* be 0 in the Input Context... yeah, it's weird, I know. */
Nico Huber90292652013-06-13 14:37:15 +0200391
Julius Werner752fba72015-07-09 16:29:10 -0700392 if (dev->descriptor->bDeviceClass == 0x09) {
Nico Huber90292652013-06-13 14:37:15 +0200393 ret = xhci_finish_hub_config(dev, ic);
394 if (ret)
395 goto _free_return;
396 }
397
398 for (i = 1; i < dev->num_endp; ++i) {
399 ret = xhci_finish_ep_config(&dev->endpoints[i], ic);
400 if (ret)
401 goto _free_ep_ctx_return;
402 }
403
404 xhci_dump_inputctx(ic);
405
Julius Wernerd13e2c42013-09-17 22:16:04 -0700406 const int config_id = dev->configuration->bConfigurationValue;
Nico Huber90292652013-06-13 14:37:15 +0200407 xhci_debug("config_id: %d\n", config_id);
Julius Wernere44a4e82015-07-08 22:36:00 -0700408 int cc = xhci_cmd_configure_endpoint(xhci, slot_id, config_id, ic);
409 if (cc == CC_RESOURCE_ERROR || cc == CC_BANDWIDTH_ERROR) {
410 xhci_reap_slots(xhci, slot_id);
411 cc = xhci_cmd_configure_endpoint(xhci, slot_id, config_id, ic);
412 }
Nico Huber90292652013-06-13 14:37:15 +0200413 if (cc != CC_SUCCESS) {
414 xhci_debug("Configure endpoint failed: %d\n", cc);
415 ret = CONTROLLER_ERROR;
416 goto _free_ep_ctx_return;
417 } else {
418 xhci_debug("Endpoints configured\n");
419 }
420
421 goto _free_return;
422
423_free_ep_ctx_return:
424 for (i = 2; i < 31; ++i) {
425 if (di->transfer_rings[i])
426 free((void *)di->transfer_rings[i]->ring);
427 free(di->transfer_rings[i]);
428 di->transfer_rings[i] = NULL;
429 }
430_free_return:
Julius Werner1f864342013-09-03 17:15:31 -0700431 free(ic->raw);
Nico Huber90292652013-06-13 14:37:15 +0200432 free(ic);
433 return ret;
434}
435
436void
437xhci_destroy_dev(hci_t *const controller, const int slot_id)
438{
439 xhci_t *const xhci = XHCI_INST(controller);
440
Julius Wernerd609e892013-09-25 12:30:07 -0700441 if (slot_id <= 0 || slot_id > xhci->max_slots_en)
Nico Huber90292652013-06-13 14:37:15 +0200442 return;
443
Julius Wernere44a4e82015-07-08 22:36:00 -0700444 inputctx_t *const ic = xhci_make_inputctx(CTXSIZE(xhci));
445 if (!ic) {
446 xhci_debug("Out of memory, leaking resources!\n");
447 return;
448 }
449 const int num_eps = controller->devices[slot_id]->num_endp;
450 *ic->add = 0; /* Leave Slot/EP0 state as it is for now. */
451 *ic->drop = (1 << num_eps) - 1; /* Drop all endpoints we can. */
452 *ic->drop &= ~(1 << 1 | 1 << 0); /* Not allowed to drop EP0 or Slot. */
453 int cc = xhci_cmd_evaluate_context(xhci, slot_id, ic);
Patrick Georgia370ae82017-01-04 22:08:10 +0100454 free(ic);
Nico Huber90292652013-06-13 14:37:15 +0200455 if (cc != CC_SUCCESS)
Julius Wernere44a4e82015-07-08 22:36:00 -0700456 xhci_debug("Failed to quiesce slot %d: %d\n", slot_id, cc);
457 cc = xhci_cmd_stop_endpoint(xhci, slot_id, 1);
458 if (cc != CC_SUCCESS)
459 xhci_debug("Failed to stop EP0 on slot %d: %d\n", slot_id, cc);
Nico Huber90292652013-06-13 14:37:15 +0200460
Julius Wernere44a4e82015-07-08 22:36:00 -0700461 int i;
Julius Werner1f864342013-09-03 17:15:31 -0700462 devinfo_t *const di = &xhci->dev[slot_id];
Julius Wernere44a4e82015-07-08 22:36:00 -0700463 for (i = 1; i < num_eps; ++i) {
Nico Huber90292652013-06-13 14:37:15 +0200464 if (di->transfer_rings[i])
465 free((void *)di->transfer_rings[i]->ring);
466 free(di->transfer_rings[i]);
Nico Huber90292652013-06-13 14:37:15 +0200467 free(di->interrupt_queues[i]);
468 }
Julius Wernere44a4e82015-07-08 22:36:00 -0700469
470 xhci_spew("Stopped slot %d, but not disabling it yet.\n", slot_id);
471 di->transfer_rings[1] = NULL;
Nico Huber90292652013-06-13 14:37:15 +0200472}