status_t OHCI::AddTo(Stack *stack) { #ifdef TRACE_USB set_dprintf_enabled(true); #ifndef ANTARES_TARGET_PLATFORM_ANTARES load_driver_symbols("ohci"); #endif #endif if (!sPCIModule) { status_t status = get_module(B_PCI_MODULE_NAME, (module_info **)&sPCIModule); if (status < B_OK) { TRACE_MODULE_ERROR("getting pci module failed! 0x%08lx\n", status); return status; } } TRACE_MODULE("searching devices\n"); bool found = false; pci_info *item = new(std::nothrow) pci_info; if (!item) { sPCIModule = NULL; put_module(B_PCI_MODULE_NAME); return B_NO_MEMORY; } for (uint32 i = 0 ; sPCIModule->get_nth_pci_info(i, item) >= B_OK; i++) { if (item->class_base == PCI_serial_bus && item->class_sub == PCI_usb && item->class_api == PCI_usb_ohci) { if (item->u.h0.interrupt_line == 0 || item->u.h0.interrupt_line == 0xFF) { TRACE_MODULE_ERROR("found device with invalid IRQ -" " check IRQ assignement\n"); continue; } TRACE_MODULE("found device at IRQ %u\n", item->u.h0.interrupt_line); OHCI *bus = new(std::nothrow) OHCI(item, stack); if (!bus) { delete item; sPCIModule = NULL; put_module(B_PCI_MODULE_NAME); return B_NO_MEMORY; } if (bus->InitCheck() < B_OK) { TRACE_MODULE_ERROR("bus failed init check\n"); delete bus; continue; } // the bus took it away item = new(std::nothrow) pci_info; bus->Start(); stack->AddBusManager(bus); found = true; } } if (!found) { TRACE_MODULE_ERROR("no devices found\n"); delete item; sPCIModule = NULL; put_module(B_PCI_MODULE_NAME); return ENODEV; } delete item; return B_OK; }
int32 OHCI::_Interrupt() { static spinlock lock = B_SPINLOCK_INITIALIZER; acquire_spinlock(&lock); uint32 status = 0; uint32 acknowledge = 0; bool finishTransfers = false; int32 result = B_HANDLED_INTERRUPT; // The LSb of done_head is used to inform the HCD that an interrupt // condition exists for both the done list and for another event recorded in // the HcInterruptStatus register. If done_head is 0, then the interrupt // was caused by other than the HccaDoneHead update and the // HcInterruptStatus register needs to be accessed to determine that exact // interrupt cause. If HccDoneHead is nonzero, then a done list update // interrupt is indicated and if the LSb of the Dword is nonzero, then an // additional interrupt event is indicated and HcInterruptStatus should be // checked to determine its cause. uint32 doneHead = fHcca->done_head; if (doneHead != 0) { status = OHCI_WRITEBACK_DONE_HEAD; if (doneHead & OHCI_DONE_INTERRUPTS) status |= _ReadReg(OHCI_INTERRUPT_STATUS) & _ReadReg(OHCI_INTERRUPT_ENABLE); } else { status = _ReadReg(OHCI_INTERRUPT_STATUS) & _ReadReg(OHCI_INTERRUPT_ENABLE) & ~OHCI_WRITEBACK_DONE_HEAD; if (status == 0) { // Nothing to be done (PCI shared interrupt) release_spinlock(&lock); return B_UNHANDLED_INTERRUPT; } } if (status & OHCI_SCHEDULING_OVERRUN) { TRACE_MODULE("scheduling overrun occured\n"); acknowledge |= OHCI_SCHEDULING_OVERRUN; } if (status & OHCI_WRITEBACK_DONE_HEAD) { TRACE_MODULE("transfer descriptors processed\n"); fHcca->done_head = 0; acknowledge |= OHCI_WRITEBACK_DONE_HEAD; result = B_INVOKE_SCHEDULER; finishTransfers = true; } if (status & OHCI_RESUME_DETECTED) { TRACE_MODULE("resume detected\n"); acknowledge |= OHCI_RESUME_DETECTED; } if (status & OHCI_UNRECOVERABLE_ERROR) { TRACE_MODULE_ERROR("unrecoverable error - controller halted\n"); _WriteReg(OHCI_CONTROL, OHCI_HC_FUNCTIONAL_STATE_RESET); // TODO: clear all pending transfers, reset and resetup the controller } if (status & OHCI_ROOT_HUB_STATUS_CHANGE) { TRACE_MODULE("root hub status change\n"); // Disable the interrupt as it will otherwise be retriggered until the // port has been reset and the change is cleared explicitly. // TODO: renable it once we use status changes instead of polling _WriteReg(OHCI_INTERRUPT_DISABLE, OHCI_ROOT_HUB_STATUS_CHANGE); acknowledge |= OHCI_ROOT_HUB_STATUS_CHANGE; } if (acknowledge != 0) _WriteReg(OHCI_INTERRUPT_STATUS, acknowledge); release_spinlock(&lock); if (finishTransfers) release_sem_etc(fFinishTransfersSem, 1, B_DO_NOT_RESCHEDULE); return result; }
status_t OHCIRootHub::ProcessTransfer(OHCI *ohci, Transfer *transfer) { if ((transfer->TransferPipe()->Type() & USB_OBJECT_CONTROL_PIPE) == 0) return B_ERROR; usb_request_data *request = transfer->RequestData(); TRACE_MODULE("request: %d\n", request->Request); status_t status = B_TIMED_OUT; size_t actualLength = 0; switch (request->Request) { case USB_REQUEST_GET_STATUS: { if (request->Index == 0) { // get hub status actualLength = MIN(sizeof(usb_port_status), transfer->DataLength()); // the hub reports whether the local power failed (bit 0) // and if there is a over-current condition (bit 1). // everything as 0 means all is ok. memset(transfer->Data(), 0, actualLength); status = B_OK; break; } usb_port_status portStatus; if (ohci->GetPortStatus(request->Index - 1, &portStatus) >= B_OK) { actualLength = MIN(sizeof(usb_port_status), transfer->DataLength()); memcpy(transfer->Data(), (void *)&portStatus, actualLength); status = B_OK; } break; } case USB_REQUEST_SET_ADDRESS: if (request->Value >= 128) { status = B_TIMED_OUT; break; } TRACE_MODULE("set address: %d\n", request->Value); status = B_OK; break; case USB_REQUEST_GET_DESCRIPTOR: TRACE_MODULE("get descriptor: %d\n", request->Value >> 8); switch (request->Value >> 8) { case USB_DESCRIPTOR_DEVICE: { actualLength = MIN(sizeof(usb_device_descriptor), transfer->DataLength()); memcpy(transfer->Data(), (void *)&sOHCIRootHubDevice, actualLength); status = B_OK; break; } case USB_DESCRIPTOR_CONFIGURATION: { actualLength = MIN(sizeof(ohci_root_hub_configuration_s), transfer->DataLength()); sOHCIRootHubConfig.hub.num_ports = ohci->PortCount(); memcpy(transfer->Data(), (void *)&sOHCIRootHubConfig, actualLength); status = B_OK; break; } case USB_DESCRIPTOR_STRING: { uint8 index = request->Value & 0x00ff; if (index > 2) break; actualLength = MIN(sOHCIRootHubStrings[index].length, transfer->DataLength()); memcpy(transfer->Data(), (void *)&sOHCIRootHubStrings[index], actualLength); status = B_OK; break; } case USB_DESCRIPTOR_HUB: { actualLength = MIN(sizeof(usb_hub_descriptor), transfer->DataLength()); sOHCIRootHubConfig.hub.num_ports = ohci->PortCount(); memcpy(transfer->Data(), (void *)&sOHCIRootHubConfig.hub, actualLength); status = B_OK; break; } } break; case USB_REQUEST_SET_CONFIGURATION: status = B_OK; break; case USB_REQUEST_CLEAR_FEATURE: { if (request->Index == 0) { // we don't support any hub changes TRACE_MODULE_ERROR("clear feature: no hub changes\n"); break; } TRACE_MODULE("clear feature: %d\n", request->Value); if (ohci->ClearPortFeature(request->Index - 1, request->Value) >= B_OK) status = B_OK; break; } case USB_REQUEST_SET_FEATURE: { if (request->Index == 0) { // we don't support any hub changes TRACE_MODULE_ERROR("set feature: no hub changes\n"); break; } TRACE_MODULE("set feature: %d\n", request->Value); if (ohci->SetPortFeature(request->Index - 1, request->Value) >= B_OK) status = B_OK; break; } } transfer->Finished(status, actualLength); delete transfer; return B_OK; }