source: mainline/uspace/drv/bus/usb/ehci/hc.c@ f3ae58b

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f3ae58b was 4db49344, checked in by Ondřej Hlavatý <aearsis@…>, 8 years ago

usb: fix wrong design of transfer aborting

Apparently, we didn't do a good job in thinking through the problem.
In older HCs, it was done just wrong - the UHCI implementation commited
a batch that could have been already aborted, and EHCI+OHCI might miss
an interrupt because they commited the batch sooner than they added it
to their checked list.

This commit takes everything from the other end, which is probably the
only right one. Instead of an endpoint having an extra mutex, it
inherits a mutex from the outside. It never locks it though, it just
checks if the mutex is locked and uses it for waiting on condition
variables.

This mutex is supposed to be the one which the HC driver uses for
locking its structures in scheduling. This way, we avoid the ABBA
deadlock completely, while preserving the synchronization on an
endpoint.

The good thing is that this implementation is much easier to extend with
multiple active batches per endpoint.

  • Property mode set to 100644
File size: 15.3 KB
Line 
1/*
2 * Copyright (c) 2011 Jan Vesely
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 drvusbehcihc
30 * @{
31 */
32/** @file
33 * @brief EHCI Host controller driver routines
34 */
35
36#include <assert.h>
37#include <async.h>
38#include <errno.h>
39#include <macros.h>
40#include <mem.h>
41#include <stdlib.h>
42#include <stdint.h>
43#include <str_error.h>
44
45#include <usb/debug.h>
46#include <usb/usb.h>
47#include <usb/host/utility.h>
48
49#include "ehci_batch.h"
50
51#include "hc.h"
52
53#define EHCI_USED_INTERRUPTS \
54 (USB_INTR_IRQ_FLAG | USB_INTR_ERR_IRQ_FLAG | USB_INTR_PORT_CHANGE_FLAG | \
55 USB_INTR_ASYNC_ADVANCE_FLAG | USB_INTR_HOST_ERR_FLAG)
56
57static const irq_pio_range_t ehci_pio_ranges[] = {
58 {
59 .base = 0,
60 .size = sizeof(ehci_regs_t)
61 }
62};
63
64static const irq_cmd_t ehci_irq_commands[] = {
65 {
66 .cmd = CMD_PIO_READ_32,
67 .dstarg = 1,
68 .addr = NULL
69 },
70 {
71 .cmd = CMD_AND,
72 .srcarg = 1,
73 .dstarg = 2,
74 .value = 0
75 },
76 {
77 .cmd = CMD_PREDICATE,
78 .srcarg = 2,
79 .value = 2
80 },
81 {
82 .cmd = CMD_PIO_WRITE_A_32,
83 .srcarg = 1,
84 .addr = NULL
85 },
86 {
87 .cmd = CMD_ACCEPT
88 }
89};
90
91static int hc_init_memory(hc_t *instance);
92
93/** Generate IRQ code.
94 * @param[out] ranges PIO ranges buffer.
95 * @param[in] hw_res Device's resources.
96 *
97 * @return Error code.
98 */
99int hc_gen_irq_code(irq_code_t *code, hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
100{
101 assert(code);
102 assert(hw_res);
103 hc_t *instance = hcd_to_hc(hcd);
104
105 if (hw_res->irqs.count != 1 || hw_res->mem_ranges.count != 1)
106 return EINVAL;
107
108 addr_range_t regs = hw_res->mem_ranges.ranges[0];
109
110 if (RNGSZ(regs) < sizeof(ehci_regs_t))
111 return EOVERFLOW;
112
113 code->ranges = malloc(sizeof(ehci_pio_ranges));
114 if (code->ranges == NULL)
115 return ENOMEM;
116
117 code->cmds = malloc(sizeof(ehci_irq_commands));
118 if (code->cmds == NULL) {
119 free(code->ranges);
120 return ENOMEM;
121 }
122
123 code->rangecount = ARRAY_SIZE(ehci_pio_ranges);
124 code->cmdcount = ARRAY_SIZE(ehci_irq_commands);
125
126 memcpy(code->ranges, ehci_pio_ranges, sizeof(ehci_pio_ranges));
127 code->ranges[0].base = RNGABS(regs);
128
129 memcpy(code->cmds, ehci_irq_commands, sizeof(ehci_irq_commands));
130
131 ehci_regs_t *registers =
132 (ehci_regs_t *)(RNGABSPTR(regs) + EHCI_RD8(instance->caps->caplength));
133 code->cmds[0].addr = (void *) &registers->usbsts;
134 code->cmds[3].addr = (void *) &registers->usbsts;
135 EHCI_WR(code->cmds[1].value, EHCI_USED_INTERRUPTS);
136
137 usb_log_debug("Memory mapped regs at %p (size %zu), IRQ %d.",
138 RNGABSPTR(regs), RNGSZ(regs), hw_res->irqs.irqs[0]);
139
140 return hw_res->irqs.irqs[0];
141}
142
143/** Initialize EHCI hc driver structure
144 *
145 * @param[in] instance Memory place for the structure.
146 * @param[in] regs Device's I/O registers range.
147 * @param[in] interrupts True if w interrupts should be used
148 * @return Error code
149 */
150int hc_add(hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
151{
152 hc_t *instance = hcd_to_hc(hcd);
153 assert(hw_res);
154 if (hw_res->mem_ranges.count != 1 ||
155 hw_res->mem_ranges.ranges[0].size <
156 (sizeof(ehci_caps_regs_t) + sizeof(ehci_regs_t)))
157 return EINVAL;
158
159 int ret = pio_enable_range(&hw_res->mem_ranges.ranges[0],
160 (void **)&instance->caps);
161 if (ret != EOK) {
162 usb_log_error("HC(%p): Failed to gain access to device "
163 "registers: %s.", instance, str_error(ret));
164 return ret;
165 }
166
167 usb_log_info("HC(%p): Device registers at %"PRIx64" (%zuB) accessible.",
168 instance, hw_res->mem_ranges.ranges[0].address.absolute,
169 hw_res->mem_ranges.ranges[0].size);
170 instance->registers =
171 (void*)instance->caps + EHCI_RD8(instance->caps->caplength);
172 usb_log_info("HC(%p): Device control registers at %" PRIx64, instance,
173 hw_res->mem_ranges.ranges[0].address.absolute
174 + EHCI_RD8(instance->caps->caplength));
175
176 list_initialize(&instance->pending_endpoints);
177 fibril_mutex_initialize(&instance->guard);
178 fibril_condvar_initialize(&instance->async_doorbell);
179
180 ret = hc_init_memory(instance);
181 if (ret != EOK) {
182 usb_log_error("HC(%p): Failed to create EHCI memory structures:"
183 " %s.", instance, str_error(ret));
184 return ret;
185 }
186
187 usb_log_info("HC(%p): Initializing RH(%p).", instance, &instance->rh);
188 ehci_rh_init(
189 &instance->rh, instance->caps, instance->registers, "ehci rh");
190
191 ehci_bus_init(&instance->bus, instance);
192 hc_device_setup(hcd, (bus_t *) &instance->bus);
193 return EOK;
194}
195
196/** Safely dispose host controller internal structures
197 *
198 * @param[in] instance Host controller structure to use.
199 */
200int hc_gone(hc_device_t *hcd)
201{
202 hc_t *hc = hcd_to_hc(hcd);
203 endpoint_list_fini(&hc->async_list);
204 endpoint_list_fini(&hc->int_list);
205 dma_buffer_free(&hc->dma_buffer);
206 return EOK;
207};
208
209void hc_enqueue_endpoint(hc_t *instance, const endpoint_t *ep)
210{
211 assert(instance);
212 assert(ep);
213 ehci_endpoint_t *ehci_ep = ehci_endpoint_get(ep);
214 usb_log_debug("HC(%p) enqueue EP(%d:%d:%s:%s)", instance,
215 ep->device->address, ep->endpoint,
216 usb_str_transfer_type_short(ep->transfer_type),
217 usb_str_direction(ep->direction));
218 switch (ep->transfer_type)
219 {
220 case USB_TRANSFER_CONTROL:
221 case USB_TRANSFER_BULK:
222 endpoint_list_append_ep(&instance->async_list, ehci_ep);
223 break;
224 case USB_TRANSFER_INTERRUPT:
225 endpoint_list_append_ep(&instance->int_list, ehci_ep);
226 break;
227 case USB_TRANSFER_ISOCHRONOUS:
228 /* NOT SUPPORTED */
229 break;
230 }
231}
232
233void hc_dequeue_endpoint(hc_t *instance, const endpoint_t *ep)
234{
235 assert(instance);
236 assert(ep);
237 ehci_endpoint_t *ehci_ep = ehci_endpoint_get(ep);
238 usb_log_debug("HC(%p) dequeue EP(%d:%d:%s:%s)", instance,
239 ep->device->address, ep->endpoint,
240 usb_str_transfer_type_short(ep->transfer_type),
241 usb_str_direction(ep->direction));
242 switch (ep->transfer_type)
243 {
244 case USB_TRANSFER_INTERRUPT:
245 endpoint_list_remove_ep(&instance->int_list, ehci_ep);
246 /* Fall through */
247 case USB_TRANSFER_ISOCHRONOUS:
248 /* NOT SUPPORTED */
249 return;
250 case USB_TRANSFER_CONTROL:
251 case USB_TRANSFER_BULK:
252 endpoint_list_remove_ep(&instance->async_list, ehci_ep);
253 break;
254 }
255 fibril_mutex_lock(&instance->guard);
256 usb_log_debug("HC(%p): Waiting for doorbell", instance);
257 EHCI_SET(instance->registers->usbcmd, USB_CMD_IRQ_ASYNC_DOORBELL);
258 fibril_condvar_wait(&instance->async_doorbell, &instance->guard);
259 usb_log_debug2("HC(%p): Got doorbell", instance);
260 fibril_mutex_unlock(&instance->guard);
261}
262
263int ehci_hc_status(bus_t *bus_base, uint32_t *status)
264{
265 assert(bus_base);
266 assert(status);
267
268 ehci_bus_t *bus = (ehci_bus_t *) bus_base;
269 hc_t *hc = bus->hc;
270 assert(hc);
271
272 *status = 0;
273 if (hc->registers) {
274 *status = EHCI_RD(hc->registers->usbsts);
275 EHCI_WR(hc->registers->usbsts, *status);
276 }
277 usb_log_debug2("HC(%p): Read status: %x", hc, *status);
278 return EOK;
279}
280
281/** Add USB transfer to the schedule.
282 *
283 * @param[in] hcd HCD driver structure.
284 * @param[in] batch Batch representing the transfer.
285 * @return Error code.
286 */
287int ehci_hc_schedule(usb_transfer_batch_t *batch)
288{
289 assert(batch);
290
291 ehci_bus_t *bus = (ehci_bus_t *) endpoint_get_bus(batch->ep);
292 hc_t *hc = bus->hc;
293 assert(hc);
294
295 /* Check for root hub communication */
296 if (batch->target.address == ehci_rh_get_address(&hc->rh)) {
297 usb_log_debug("HC(%p): Scheduling BATCH(%p) for RH(%p)",
298 hc, batch, &hc->rh);
299 return ehci_rh_schedule(&hc->rh, batch);
300 }
301
302 endpoint_t * const ep = batch->ep;
303 ehci_endpoint_t * const ehci_ep = ehci_endpoint_get(ep);
304 ehci_transfer_batch_t *ehci_batch = ehci_transfer_batch_get(batch);
305
306 int err;
307
308 if ((err = ehci_transfer_batch_prepare(ehci_batch)))
309 return err;
310
311 fibril_mutex_lock(&hc->guard);
312
313 if ((err = endpoint_activate_locked(ep, batch))) {
314 fibril_mutex_unlock(&hc->guard);
315 return err;
316 }
317
318 usb_log_debug("HC(%p): Committing BATCH(%p)", hc, batch);
319 ehci_transfer_batch_commit(ehci_batch);
320
321 /* Enqueue endpoint to the checked list */
322 usb_log_debug2("HC(%p): Appending BATCH(%p)", hc, batch);
323 list_append(&ehci_ep->pending_link, &hc->pending_endpoints);
324
325 fibril_mutex_unlock(&hc->guard);
326 return EOK;
327}
328
329/** Interrupt handling routine
330 *
331 * @param[in] hcd HCD driver structure.
332 * @param[in] status Value of the status register at the time of interrupt.
333 */
334void ehci_hc_interrupt(bus_t *bus_base, uint32_t status)
335{
336 assert(bus_base);
337
338 ehci_bus_t *bus = (ehci_bus_t *) bus_base;
339 hc_t *hc = bus->hc;
340 assert(hc);
341
342 usb_log_debug2("HC(%p): Interrupt: %"PRIx32, hc, status);
343 if (status & USB_STS_PORT_CHANGE_FLAG) {
344 ehci_rh_interrupt(&hc->rh);
345 }
346
347 if (status & USB_STS_IRQ_ASYNC_ADVANCE_FLAG) {
348 fibril_mutex_lock(&hc->guard);
349 usb_log_debug2("HC(%p): Signaling doorbell", hc);
350 fibril_condvar_broadcast(&hc->async_doorbell);
351 fibril_mutex_unlock(&hc->guard);
352 }
353
354 if (status & (USB_STS_IRQ_FLAG | USB_STS_ERR_IRQ_FLAG)) {
355 fibril_mutex_lock(&hc->guard);
356
357 usb_log_debug2("HC(%p): Scanning %lu pending endpoints", hc,
358 list_count(&hc->pending_endpoints));
359 list_foreach_safe(hc->pending_endpoints, current, next) {
360 ehci_endpoint_t *ep
361 = list_get_instance(current, ehci_endpoint_t, pending_link);
362
363 ehci_transfer_batch_t *batch
364 = ehci_transfer_batch_get(ep->base.active_batch);
365 assert(batch);
366
367 if (ehci_transfer_batch_check_completed(batch)) {
368 endpoint_deactivate_locked(&ep->base);
369 list_remove(current);
370 hc_reset_toggles(&batch->base, &ehci_ep_toggle_reset);
371 usb_transfer_batch_finish(&batch->base);
372 }
373 }
374 fibril_mutex_unlock(&hc->guard);
375
376
377 }
378
379 if (status & USB_STS_HOST_ERROR_FLAG) {
380 usb_log_fatal("HCD(%p): HOST SYSTEM ERROR!", hc);
381 //TODO do something here
382 }
383}
384
385/** EHCI hw initialization routine.
386 *
387 * @param[in] instance EHCI hc driver structure.
388 */
389int hc_start(hc_device_t *hcd)
390{
391 hc_t *instance = hcd_to_hc(hcd);
392 usb_log_debug("HC(%p): Starting HW.", instance);
393
394 /* Turn off the HC if it's running, Reseting a running device is
395 * undefined */
396 if (!(EHCI_RD(instance->registers->usbsts) & USB_STS_HC_HALTED_FLAG)) {
397 /* disable all interrupts */
398 EHCI_WR(instance->registers->usbintr, 0);
399 /* ack all interrupts */
400 EHCI_WR(instance->registers->usbsts, 0x3f);
401 /* Stop HC hw */
402 EHCI_WR(instance->registers->usbcmd, 0);
403 /* Wait until hc is halted */
404 while ((EHCI_RD(instance->registers->usbsts) & USB_STS_HC_HALTED_FLAG) == 0) {
405 async_usleep(1);
406 }
407 usb_log_info("HC(%p): EHCI turned off.", instance);
408 } else {
409 usb_log_info("HC(%p): EHCI was not running.", instance);
410 }
411
412 /* Hw initialization sequence, see page 53 (pdf 63) */
413 EHCI_SET(instance->registers->usbcmd, USB_CMD_HC_RESET_FLAG);
414 usb_log_info("HC(%p): Waiting for HW reset.", instance);
415 while (EHCI_RD(instance->registers->usbcmd) & USB_CMD_HC_RESET_FLAG) {
416 async_usleep(1);
417 }
418 usb_log_debug("HC(%p): HW reset OK.", instance);
419
420 /* Use the lowest 4G segment */
421 EHCI_WR(instance->registers->ctrldssegment, 0);
422
423 /* Enable periodic list */
424 assert(instance->periodic_list);
425 uintptr_t phys_base =
426 addr_to_phys((void*)instance->periodic_list);
427 assert((phys_base & USB_PERIODIC_LIST_BASE_MASK) == phys_base);
428 EHCI_WR(instance->registers->periodiclistbase, phys_base);
429 EHCI_SET(instance->registers->usbcmd, USB_CMD_PERIODIC_SCHEDULE_FLAG);
430 usb_log_debug("HC(%p): Enabled periodic list.", instance);
431
432
433 /* Enable Async schedule */
434 phys_base = addr_to_phys((void*)instance->async_list.list_head);
435 assert((phys_base & USB_ASYNCLIST_MASK) == phys_base);
436 EHCI_WR(instance->registers->asynclistaddr, phys_base);
437 EHCI_SET(instance->registers->usbcmd, USB_CMD_ASYNC_SCHEDULE_FLAG);
438 usb_log_debug("HC(%p): Enabled async list.", instance);
439
440 /* Start hc and get all ports */
441 EHCI_SET(instance->registers->usbcmd, USB_CMD_RUN_FLAG);
442 EHCI_SET(instance->registers->configflag, USB_CONFIG_FLAG_FLAG);
443 usb_log_debug("HC(%p): HW started.", instance);
444
445 usb_log_debug2("HC(%p): Registers: "
446 "\tUSBCMD(%p): %x(0x00080000 = at least 1ms between interrupts)"
447 "\tUSBSTS(%p): %x(0x00001000 = HC halted)"
448 "\tUSBINT(%p): %x(0x0 = no interrupts)."
449 "\tCONFIG(%p): %x(0x0 = ports controlled by companion hc).",
450 instance,
451 &instance->registers->usbcmd, EHCI_RD(instance->registers->usbcmd),
452 &instance->registers->usbsts, EHCI_RD(instance->registers->usbsts),
453 &instance->registers->usbintr, EHCI_RD(instance->registers->usbintr),
454 &instance->registers->configflag, EHCI_RD(instance->registers->configflag));
455 /* Clear and Enable interrupts */
456 EHCI_WR(instance->registers->usbsts, EHCI_RD(instance->registers->usbsts));
457 EHCI_WR(instance->registers->usbintr, EHCI_USED_INTERRUPTS);
458
459 return EOK;
460}
461
462/**
463 * Setup roothub as a virtual hub.
464 */
465int hc_setup_roothub(hc_device_t *hcd)
466{
467 return hc_setup_virtual_root_hub(hcd, USB_SPEED_HIGH);
468}
469
470/** Initialize memory structures used by the EHCI hcd.
471 *
472 * @param[in] instance EHCI hc driver structure.
473 * @return Error code.
474 */
475int hc_init_memory(hc_t *instance)
476{
477 assert(instance);
478 usb_log_debug2("HC(%p): Initializing Async list(%p).", instance,
479 &instance->async_list);
480 int ret = endpoint_list_init(&instance->async_list, "ASYNC");
481 if (ret != EOK) {
482 usb_log_error("HC(%p): Failed to setup ASYNC list: %s",
483 instance, str_error(ret));
484 return ret;
485 }
486 /* Specs say "Software must set queue head horizontal pointer T-bits to
487 * a zero for queue heads in the asynchronous schedule" (4.4.0).
488 * So we must maintain circular buffer (all horizontal pointers
489 * have to be valid */
490 endpoint_list_chain(&instance->async_list, &instance->async_list);
491
492 usb_log_debug2("HC(%p): Initializing Interrupt list (%p).", instance,
493 &instance->int_list);
494 ret = endpoint_list_init(&instance->int_list, "INT");
495 if (ret != EOK) {
496 usb_log_error("HC(%p): Failed to setup INT list: %s",
497 instance, str_error(ret));
498 endpoint_list_fini(&instance->async_list);
499 return ret;
500 }
501
502 /* Take 1024 periodic list heads, we ignore low mem options */
503 if (dma_buffer_alloc(&instance->dma_buffer, PAGE_SIZE)) {
504 usb_log_error("HC(%p): Failed to get ISO schedule page.",
505 instance);
506 endpoint_list_fini(&instance->async_list);
507 endpoint_list_fini(&instance->int_list);
508 return ENOMEM;
509 }
510 instance->periodic_list = instance->dma_buffer.virt;
511
512 usb_log_debug2("HC(%p): Initializing Periodic list.", instance);
513 for (unsigned i = 0; i < PAGE_SIZE/sizeof(link_pointer_t); ++i)
514 {
515 /* Disable everything for now */
516 instance->periodic_list[i] =
517 LINK_POINTER_QH(addr_to_phys(instance->int_list.list_head));
518 }
519 return EOK;
520}
521
522/**
523 * @}
524 */
Note: See TracBrowser for help on using the repository browser.