diff options
Diffstat (limited to 'payloads')
-rw-r--r-- | payloads/libpayload/drivers/usb/ehci.c | 24 | ||||
-rw-r--r-- | payloads/libpayload/drivers/usb/ehci_private.h | 1 |
2 files changed, 22 insertions, 3 deletions
diff --git a/payloads/libpayload/drivers/usb/ehci.c b/payloads/libpayload/drivers/usb/ehci.c index aaadf2dd4e..8d84f8dd60 100644 --- a/payloads/libpayload/drivers/usb/ehci.c +++ b/payloads/libpayload/drivers/usb/ehci.c @@ -83,6 +83,9 @@ static void ehci_shutdown (hci_t *controller) /* Free periodic frame list */ free(phys_to_virt(EHCI_INST(controller)->operation->periodiclistbase)); + /* Free dummy QH */ + free(EHCI_INST(controller)->dummy_qh); + EHCI_INST(controller)->operation->configflag = 0; } @@ -521,9 +524,11 @@ static void *ehci_create_intr_queue( int nothing_placed = 1; u32 *const ps = (u32 *)phys_to_virt(EHCI_INST(ep->dev->controller) ->operation->periodiclistbase); + const u32 dummy_ptr = virt_to_phys(EHCI_INST( + ep->dev->controller)->dummy_qh) | PS_TYPE_QH; for (i = 0; i < 1024; i += reqtiming) { /* advance to the next free position */ - while ((i < 1024) && !(ps[i] & PS_TERMINATE)) ++i; + while ((i < 1024) && (ps[i] != dummy_ptr)) ++i; if (i < 1024) { ps[i] = virt_to_phys(&intrq->qh) | PS_TYPE_QH; nothing_placed = 0; @@ -547,9 +552,11 @@ static void ehci_destroy_intr_queue(endpoint_t *const ep, void *const queue) int i; u32 *const ps = (u32 *)phys_to_virt(EHCI_INST( ep->dev->controller)->operation->periodiclistbase); + const u32 dummy_ptr = virt_to_phys(EHCI_INST( + ep->dev->controller)->dummy_qh) | PS_TYPE_QH; for (i = 0; i < 1024; ++i) { if ((ps[i] & PS_PTR_MASK) == virt_to_phys(&intrq->qh)) - ps[i] = PS_TERMINATE; + ps[i] = dummy_ptr; } /* wait 1ms for frame to end */ @@ -661,8 +668,19 @@ ehci_init (pcidev_t addr) u32 *const periodic_list = (u32 *)memalign(4096, 1024 * sizeof(u32)); if (!periodic_list) fatal("Not enough memory creating EHCI periodic frame list.\n"); + + /* + * Insert dummy QH in periodic frame list + * This helps with broken host controllers + * and doesn't violate the standard. + */ + EHCI_INST(controller)->dummy_qh = (ehci_qh_t *)memalign(32, sizeof(ehci_qh_t)); + memset(EHCI_INST(controller)->dummy_qh, 0, + sizeof(*EHCI_INST(controller)->dummy_qh)); + EHCI_INST(controller)->dummy_qh->horiz_link_ptr = QH_TERMINATE; for (i = 0; i < 1024; ++i) - periodic_list[i] = PS_TERMINATE; + periodic_list[i] = virt_to_phys(EHCI_INST(controller)->dummy_qh) + | PS_TYPE_QH; /* Make sure periodic schedule is disabled */ ehci_set_periodic_schedule(EHCI_INST(controller), 0); diff --git a/payloads/libpayload/drivers/usb/ehci_private.h b/payloads/libpayload/drivers/usb/ehci_private.h index a97336b492..3276e23edc 100644 --- a/payloads/libpayload/drivers/usb/ehci_private.h +++ b/payloads/libpayload/drivers/usb/ehci_private.h @@ -132,6 +132,7 @@ typedef volatile struct { typedef struct ehci { hc_cap_t *capabilities; hc_op_t *operation; + ehci_qh_t *dummy_qh; } ehci_t; #define PS_TERMINATE 1 |