static void uhci_driver_fini(hcd_t *hcd) { assert(hcd); hc_t *hc = hcd_get_driver_data(hcd); if (hc) hc_fini(hc); hcd_set_implementation(hcd, NULL, NULL); free(hc); }
/** Take action based on the interrupt cause. * * @param[in] instance UHCI structure to use. * @param[in] status Value of the status register at the time of interrupt. * * Interrupt might indicate: * - transaction completed, either by triggering IOC, SPD, or an error * - some kind of device error * - resume from suspend state (not implemented) */ void hc_interrupt(hc_t *instance, uint16_t status) { assert(instance); /* Lower 2 bits are transaction error and transaction complete */ if (status & (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)) { LIST_INITIALIZE(done); transfer_list_remove_finished( &instance->transfers_interrupt, &done); transfer_list_remove_finished( &instance->transfers_control_slow, &done); transfer_list_remove_finished( &instance->transfers_control_full, &done); transfer_list_remove_finished( &instance->transfers_bulk_full, &done); while (!list_empty(&done)) { link_t *item = list_first(&done); list_remove(item); uhci_transfer_batch_t *batch = uhci_transfer_batch_from_link(item); uhci_transfer_batch_finish_dispose(batch); } } /* Resume interrupts are not supported */ if (status & UHCI_STATUS_RESUME) { usb_log_error("Resume interrupt!\n"); } /* Bits 4 and 5 indicate hc error */ if (status & (UHCI_STATUS_PROCESS_ERROR | UHCI_STATUS_SYSTEM_ERROR)) { usb_log_error("UHCI hardware failure!.\n"); ++instance->hw_failures; transfer_list_abort_all(&instance->transfers_interrupt); transfer_list_abort_all(&instance->transfers_control_slow); transfer_list_abort_all(&instance->transfers_control_full); transfer_list_abort_all(&instance->transfers_bulk_full); if (instance->hw_failures < UHCI_ALLOWED_HW_FAIL) { /* reinitialize hw, this triggers virtual disconnect*/ hc_init_hw(instance); } else { usb_log_fatal("Too many UHCI hardware failures!.\n"); hc_fini(instance); } } }