From 9e857d24ca41743874f6b3c08bb0c35f53df5763 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Fri, 25 Aug 2006 21:43:21 +0000 Subject: [PATCH] * Adding skeleton EHCI driver. Some parts filled from the specs and the UHCI driver. * Corrected some errors I found while reading UHCI. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18626 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/bus_managers/usb/Stack.cpp | 2 +- src/add-ons/kernel/bus_managers/usb/usb_p.h | 2 +- src/add-ons/kernel/busses/usb/Jamfile | 6 + src/add-ons/kernel/busses/usb/ehci.cpp | 575 ++++++++++++++++++ src/add-ons/kernel/busses/usb/ehci.h | 136 +++++ src/add-ons/kernel/busses/usb/ehci.rdef | 15 + src/add-ons/kernel/busses/usb/ehci_hardware.h | 225 +++++++ src/add-ons/kernel/busses/usb/ehci_rh.cpp | 139 +++++ src/add-ons/kernel/busses/usb/ohci.cpp | 8 +- src/add-ons/kernel/busses/usb/uhci.cpp | 14 +- src/add-ons/kernel/busses/usb/uhci.h | 11 +- src/add-ons/kernel/busses/usb/uhci_hardware.h | 4 +- src/add-ons/kernel/busses/usb/uhci_rh.cpp | 6 +- 13 files changed, 1117 insertions(+), 26 deletions(-) create mode 100644 src/add-ons/kernel/busses/usb/ehci.cpp create mode 100644 src/add-ons/kernel/busses/usb/ehci.h create mode 100644 src/add-ons/kernel/busses/usb/ehci.rdef create mode 100644 src/add-ons/kernel/busses/usb/ehci_hardware.h create mode 100644 src/add-ons/kernel/busses/usb/ehci_rh.cpp diff --git a/src/add-ons/kernel/bus_managers/usb/Stack.cpp b/src/add-ons/kernel/bus_managers/usb/Stack.cpp index 9bb5e66cc0..6a19a5f1e8 100644 --- a/src/add-ons/kernel/bus_managers/usb/Stack.cpp +++ b/src/add-ons/kernel/bus_managers/usb/Stack.cpp @@ -133,7 +133,7 @@ Stack::Stack() if (get_module(moduleName, (module_info **)&module) != B_OK) continue; - if (module->add_to(*this) != B_OK) + if (module->add_to(this) < B_OK) continue; TRACE(("usb stack: module %s successfully loaded\n", moduleName)); diff --git a/src/add-ons/kernel/bus_managers/usb/usb_p.h b/src/add-ons/kernel/bus_managers/usb/usb_p.h index 77110f5fa9..f968378517 100644 --- a/src/add-ons/kernel/bus_managers/usb/usb_p.h +++ b/src/add-ons/kernel/bus_managers/usb/usb_p.h @@ -37,7 +37,7 @@ class Object; struct host_controller_info { module_info info; status_t (*control)(uint32 op, void *data, size_t length); - bool (*add_to)(Stack &stack); + status_t (*add_to)(Stack *stack); }; diff --git a/src/add-ons/kernel/busses/usb/Jamfile b/src/add-ons/kernel/busses/usb/Jamfile index 0d07d11a24..589833efad 100644 --- a/src/add-ons/kernel/busses/usb/Jamfile +++ b/src/add-ons/kernel/busses/usb/Jamfile @@ -24,3 +24,9 @@ KernelAddon ohci : [ FDirName kernel busses usb ] : : ohci.rdef ; +KernelAddon ehci : [ FDirName kernel busses usb ] : + ehci.cpp + ehci_rh.cpp + : libusb.a + : ehci.rdef + ; diff --git a/src/add-ons/kernel/busses/usb/ehci.cpp b/src/add-ons/kernel/busses/usb/ehci.cpp new file mode 100644 index 0000000000..e644afb5a0 --- /dev/null +++ b/src/add-ons/kernel/busses/usb/ehci.cpp @@ -0,0 +1,575 @@ +/* + * Copyright 2006, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include +#include +#include +#include + +#include "ehci.h" + +pci_module_info *EHCI::sPCIModule = NULL; + + +static int32 +ehci_std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + TRACE(("usb_ehci_module: init module\n")); + return B_OK; + case B_MODULE_UNINIT: + TRACE(("usb_ehci_module: uninit module\n")); + return B_OK; + } + + return EINVAL; +} + + +host_controller_info ehci_module = { + { + "busses/usb/ehci", + NULL, + ehci_std_ops + }, + NULL, + EHCI::AddTo +}; + + +module_info *modules[] = { + (module_info *)&ehci_module, + NULL +}; + + +// +// #pragma mark - +// + + +EHCI::EHCI(pci_info *info, Stack *stack) + : BusManager(stack), + fPCIInfo(info), + fStack(stack), + fPeriodicFrameListArea(-1), + fPeriodicFrameList(NULL), + fAsyncFrameListArea(-1), + fAsyncFrameList(NULL), + fFirstTransfer(NULL), + fLastTransfer(NULL), + fFinishTransfers(false), + fFinishThread(-1), + fStopFinishThread(false), + fRootHub(NULL), + fRootHubAddress(0) +{ + if (!fInitOK) { + TRACE_ERROR(("usb_ehci: bus manager failed to init\n")); + return; + } + + TRACE(("usb_ehci: constructing new EHCI Host Controller Driver\n")); + fInitOK = false; + + fRegisterBase = sPCIModule->read_pci_config(fPCIInfo->bus, + fPCIInfo->device, fPCIInfo->function, PCI_memory_base, 4); + fRegisterBase &= PCI_address_io_mask; + TRACE(("usb_ehci: register base: 0x%08x\n", fRegisterBase)); + + // enable pci address access + uint16 command = PCI_command_io | PCI_command_master | PCI_command_memory; + command |= sPCIModule->read_pci_config(fPCIInfo->bus, fPCIInfo->device, + fPCIInfo->function, PCI_command, 2); + + sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device, + fPCIInfo->function, PCI_command, 2, command); + + // make sure we take the controller away from BIOS + sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device, 2, + PCI_LEGSUP, 2, PCI_LEGSUP_USBPIRQDEN); + + // disable interrupts + WriteReg16(EHCI_USBINTR, 0); + + // reset the host controller + // ToDo... + + // allocate the periodic frame list + void *physicalAddress; + fPeriodicFrameListArea = fStack->AllocateArea((void **)&fPeriodicFrameList, + &physicalAddress, B_PAGE_SIZE, "USB EHCI Periodic Framelist"); + if (fPeriodicFrameListArea < B_OK) { + TRACE_ERROR(("usb_ehci: unable to allocate periodic framelist\n")); + return; + } + + WriteReg32(EHCI_PERIODICLISTBASE, (uint32)physicalAddress); + + // allocate the async frame list + fAsyncFrameListArea = fStack->AllocateArea((void **)&fAsyncFrameList, + &physicalAddress, B_PAGE_SIZE, "USB EHCI Async Framelist"); + if (fAsyncFrameListArea < B_OK) { + TRACE_ERROR(("usb_ehci: unable to allocate async framelist\n")); + } + + WriteReg32(EHCI_ASYNCLISTADDR, (uint32)physicalAddress); + + // create finisher service thread + fFinishThread = spawn_kernel_thread(FinishThread, "ehci finish thread", + B_NORMAL_PRIORITY, (void *)this); + resume_thread(fFinishThread); + + // install the interrupt handler and enable interrupts + install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line, + InterruptHandler, (void *)this, 0); + WriteReg16(EHCI_USBINTR, EHCI_USBINTR_HOSTSYSERR + | EHCI_USBINTR_USBERRINT | EHCI_USBINTR_USBINT); + + TRACE(("usb_ehci: EHCI Host Controller Driver constructed\n")); + fInitOK = true; +} + + +EHCI::~EHCI() +{ + TRACE(("usb_ehci: tear down EHCI Host Controller Driver\n")); + + int32 result = 0; + fStopFinishThread = true; + wait_for_thread(fFinishThread, &result); + + CancelAllPendingTransfers(); + + delete fRootHub; + delete_area(fPeriodicFrameListArea); + delete_area(fAsyncFrameListArea); + + put_module(B_PCI_MODULE_NAME); +} + + +status_t +EHCI::Start() +{ + TRACE(("usb_ehci: starting EHCI Host Controller\n")); + TRACE(("usb_ehci: usbcmd: 0x%08x; usbsts: 0x%08x\n", ReadReg32(EHCI_USBCMD), ReadReg32(EHCI_USBSTS))); + + WriteReg32(EHCI_USBCMD, ReadReg32(EHCI_USBCMD) | EHCI_USBCMD_RUNSTOP); + + bool running = false; + for (int32 i = 0; i < 10; i++) { + uint32 status = ReadReg32(EHCI_USBSTS); + TRACE(("usb_ehci: try %ld: status 0x%08x\n", i, status)); + + if (status & EHCI_USBSTS_HCHALTED) { + snooze(10000); + } else { + running = true; + break; + } + } + + if (!running) { + TRACE(("usb_ehci: Host Controller didn't start\n")); + return B_ERROR; + } + + fRootHubAddress = AllocateAddress(); + fRootHub = new(std::nothrow) EHCIRootHub(this, fRootHubAddress); + if (!fRootHub) { + TRACE_ERROR(("usb_ehci: no memory to allocate root hub\n")); + return B_NO_MEMORY; + } + + if (fRootHub->InitCheck() < B_OK) { + TRACE_ERROR(("usb_ehci: root hub failed init check\n")); + return fRootHub->InitCheck(); + } + + SetRootHub(fRootHub); + TRACE(("usb_ehci: Host Controller started\n")); + return BusManager::Start(); +} + + +status_t +EHCI::SubmitTransfer(Transfer *transfer) +{ + return B_ERROR; +} + + +status_t +EHCI::SubmitRequest(Transfer *transfer) +{ + return B_ERROR; +} + + +status_t +EHCI::AddTo(Stack *stack) +{ +#ifdef TRACE_USB + set_dprintf_enabled(true); + load_driver_symbols("ehci"); +#endif + + if (!sPCIModule) { + status_t status = get_module(B_PCI_MODULE_NAME, (module_info **)&sPCIModule); + if (status < B_OK) { + TRACE_ERROR(("usb_ehci: getting pci module failed! 0x%08lx\n", status)); + return status; + } + } + + TRACE(("usb_ehci: 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 (int32 i = 0; sPCIModule->get_nth_pci_info(i, item) >= B_OK; i++) { + // class_base: 0C (Serial Bus); class_sub: 03 (USB); class_api: 20 (EHCI) + if (item->class_base == 0x0C && item->class_sub == 0x03 + && item->class_api == 0x20) { + if (item->u.h0.interrupt_line == 0 + || item->u.h0.interrupt_line == 0xFF) { + TRACE_ERROR(("usb_ehci: found device with invalid IRQ - check IRQ assignement\n")); + continue; + } + + TRACE(("usb_ehci: found device at IRQ %u\n", item->u.h0.interrupt_line)); + EHCI *bus = new(std::nothrow) EHCI(item, stack); + if (!bus) { + delete item; + sPCIModule = NULL; + put_module(B_PCI_MODULE_NAME); + return B_NO_MEMORY; + } + + if (bus->InitCheck() < B_OK) { + TRACE_ERROR(("usb_ehci: 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_ERROR(("usb_ehci: no devices found\n")); + delete item; + sPCIModule = NULL; + put_module(B_PCI_MODULE_NAME); + return ENODEV; + } + + delete item; + return B_OK; +} + + +status_t +EHCI::ResetPort(int32 index) +{ + return B_ERROR; +} + + +status_t +EHCI::ControllerReset() +{ + return B_ERROR; +} + + +status_t +EHCI::LightReset() +{ + return B_ERROR; +} + + +int32 +EHCI::InterruptHandler(void *data) +{ + cpu_status status = disable_interrupts(); + spinlock lock = 0; + acquire_spinlock(&lock); + + int32 result = ((EHCI *)data)->Interrupt(); + + release_spinlock(&lock); + restore_interrupts(status); + return result; +} + + +int32 +EHCI::Interrupt() +{ + return B_UNHANDLED_INTERRUPT; +} + + +status_t +EHCI::AddPendingTransfer(Transfer *transfer, ehci_qh *queueHead, + ehci_qtd *firstDescriptor, ehci_qtd *dataDescriptor, + ehci_qtd *lastDescriptor, bool directionIn) +{ + transfer_data *data = new(std::nothrow) transfer_data(); + if (!data) + return B_NO_MEMORY; + + data->transfer = transfer; + data->queue_head = queueHead; + data->first_descriptor = firstDescriptor; + data->data_descriptor = dataDescriptor; + data->last_descriptor = lastDescriptor; + data->incoming = directionIn; + data->link = NULL; + + if (!Lock()) { + delete data; + return B_ERROR; + } + + if (fLastTransfer) + fLastTransfer->link = data; + else + fFirstTransfer = data; + + fLastTransfer = data; + Unlock(); + + return B_OK; +} + + +status_t +EHCI::CancelPendingTransfer(Transfer *transfer) +{ + if (!Lock()) + return B_ERROR; + + transfer_data *last = NULL; + transfer_data *current = fFirstTransfer; + while (current) { + if (current->transfer == transfer) { + current->transfer->Finished(B_USB_STATUS_IRP_CANCELLED_BY_REQUEST, 0); + delete current->transfer; + + if (last) + last->link = current->link; + else + fFirstTransfer = current->link; + + if (fLastTransfer == current) + fLastTransfer = last; + + delete current; + Unlock(); + return B_OK; + } + + last = current; + current = current->link; + } + + Unlock(); + return B_BAD_VALUE; +} + + +status_t +EHCI::CancelAllPendingTransfers() +{ + if (!Lock()) + return B_ERROR; + + transfer_data *transfer = fFirstTransfer; + while (transfer) { + transfer->transfer->Finished(B_USB_STATUS_IRP_CANCELLED_BY_REQUEST, 0); + delete transfer->transfer; + + transfer_data *next = transfer->link; + delete transfer; + transfer = next; + } + + Unlock(); + return B_OK; +} + + +int32 +EHCI::FinishThread(void *data) +{ + ((EHCI *)data)->FinishTransfers(); + return B_OK; +} + + +void +EHCI::FinishTransfers() +{ + while (!fStopFinishThread) { + while (!fFinishTransfers) { + if (fStopFinishThread) + return; + snooze(1000); + } + + fFinishTransfers = false; + if (!Lock()) + continue; + + TRACE(("usb_ehci: finishing transfers\n")); + transfer_data *lastTransfer = NULL; + transfer_data *transfer = fFirstTransfer; + Unlock(); + + while (transfer) { + bool transferDone = false; + + // ToDo... + + if (transferDone) { + if (Lock()) { + if (lastTransfer) + lastTransfer->link = transfer->link; + + if (transfer == fFirstTransfer) + fFirstTransfer = transfer->link; + if (transfer == fLastTransfer) + fLastTransfer = lastTransfer; + + transfer_data *next = transfer->link; + delete transfer->transfer; + delete transfer; + transfer = next; + Unlock(); + } + } else { + lastTransfer = transfer; + transfer = transfer->link; + } + } + } +} + + +ehci_qtd * +EHCI::CreateDescriptor(Pipe *pipe, size_t bufferSize) +{ + return NULL; +} + + +status_t +EHCI::CreateDescriptorChain(Pipe *pipe, ehci_qtd **firstDescriptor, + ehci_qtd **lastDescriptor, size_t bufferSize) +{ + *firstDescriptor = NULL; + *lastDescriptor = NULL; + return B_ERROR; +} + + +void +EHCI::FreeDescriptor(ehci_qtd *descriptor) +{ +} + + +void +EHCI::FreeDescriptorChain(ehci_qtd *topDescriptor) +{ +} + + +void +EHCI::LinkDescriptors(ehci_qtd *first, ehci_qtd *last) +{ +} + + +size_t +EHCI::WriteDescriptorChain(ehci_qtd *topDescriptor, iovec *vector, + size_t vectorCount) +{ + return 0; +} + + +size_t +EHCI::ReadDescriptorChain(ehci_qtd *topDescriptor, iovec *vector, + size_t vectorCount, uint8 *lastDataToggle) +{ + return 0; +} + + +size_t +EHCI::ReadActualLength(ehci_qtd *topDescriptor, uint8 *lastDataToggle) +{ + return 0; +} + + +inline void +EHCI::WriteReg8(uint32 reg, uint8 value) +{ + sPCIModule->write_io_8(fRegisterBase + reg, value); +} + + +inline void +EHCI::WriteReg16(uint32 reg, uint16 value) +{ + sPCIModule->write_io_16(fRegisterBase + reg, value); +} + + +inline void +EHCI::WriteReg32(uint32 reg, uint32 value) +{ + sPCIModule->write_io_32(fRegisterBase + reg, value); +} + + +inline uint8 +EHCI::ReadReg8(uint32 reg) +{ + return sPCIModule->read_io_8(fRegisterBase + reg); +} + + +inline uint16 +EHCI::ReadReg16(uint32 reg) +{ + return sPCIModule->read_io_16(fRegisterBase + reg); +} + + +inline uint32 +EHCI::ReadReg32(uint32 reg) +{ + return sPCIModule->read_io_32(fRegisterBase + reg); +} diff --git a/src/add-ons/kernel/busses/usb/ehci.h b/src/add-ons/kernel/busses/usb/ehci.h new file mode 100644 index 0000000000..41dcccbe1a --- /dev/null +++ b/src/add-ons/kernel/busses/usb/ehci.h @@ -0,0 +1,136 @@ +/* + * Copyright 2006, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#ifndef EHCI_H +#define EHCI_H + +#include "usb_p.h" +#include "ehci_hardware.h" + + +struct pci_info; +struct pci_module_info; +class EHCIRootHub; + + +typedef struct transfer_data_s { + Transfer *transfer; + ehci_qh *queue_head; + ehci_qtd *first_descriptor; + ehci_qtd *data_descriptor; + ehci_qtd *last_descriptor; + bool incoming; + transfer_data_s *link; +} transfer_data; + + +class EHCI : public BusManager { +public: + EHCI(pci_info *info, Stack *stack); + ~EHCI(); + + status_t Start(); +virtual status_t SubmitTransfer(Transfer *transfer); +virtual status_t SubmitRequest(Transfer *transfer); + +static status_t AddTo(Stack *stack); + + // Port operations for root hub + status_t ResetPort(int32 index); + +private: + // Controller resets + status_t ControllerReset(); + status_t LightReset(); + + // Interrupt functions +static int32 InterruptHandler(void *data); + int32 Interrupt(); + + // Transfer management + status_t AddPendingTransfer(Transfer *transfer, + ehci_qh *queueHead, + ehci_qtd *firstDescriptor, + ehci_qtd *dataDescriptor, + ehci_qtd *lastDescriptor, + bool directionIn); + status_t CancelPendingTransfer(Transfer *transfer); + status_t CancelAllPendingTransfers(); + +static int32 FinishThread(void *data); + void FinishTransfers(); + + // Descriptor functions + ehci_qtd *CreateDescriptor(Pipe *pipe, + size_t bufferSizeToAllocate); + status_t CreateDescriptorChain(Pipe *pipe, + ehci_qtd **firstDescriptor, + ehci_qtd **lastDescriptor, + size_t bufferSizeToAllocate); + + void FreeDescriptor(ehci_qtd *descriptor); + void FreeDescriptorChain(ehci_qtd *topDescriptor); + + void LinkDescriptors(ehci_qtd *first, + ehci_qtd *last); + + size_t WriteDescriptorChain(ehci_qtd *topDescriptor, + iovec *vector, size_t vectorCount); + size_t ReadDescriptorChain(ehci_qtd *topDescriptor, + iovec *vector, size_t vectorCount, + uint8 *lastDataToggle); + size_t ReadActualLength(ehci_qtd *topDescriptor, + uint8 *lastDataToggle); + + // Register functions +inline void WriteReg8(uint32 reg, uint8 value); +inline void WriteReg16(uint32 reg, uint16 value); +inline void WriteReg32(uint32 reg, uint32 value); +inline uint8 ReadReg8(uint32 reg); +inline uint16 ReadReg16(uint32 reg); +inline uint32 ReadReg32(uint32 reg); + +static pci_module_info *sPCIModule; + + uint32 fRegisterBase; + pci_info *fPCIInfo; + Stack *fStack; + + // Framelist memory + area_id fPeriodicFrameListArea; + addr_t *fPeriodicFrameList; + area_id fAsyncFrameListArea; + addr_t *fAsyncFrameList; + + // Maintain a linked list of transfers + transfer_data *fFirstTransfer; + transfer_data *fLastTransfer; + bool fFinishTransfers; + thread_id fFinishThread; + bool fStopFinishThread; + + // Root Hub + EHCIRootHub *fRootHub; + uint8 fRootHubAddress; +}; + + +class EHCIRootHub : public Hub { +public: + EHCIRootHub(EHCI *ehci, int8 deviceAddress); + + status_t SubmitTransfer(Transfer *transfer); + void UpdatePortStatus(); + +private: + usb_port_status fPortStatus[2]; + EHCI *fEHCI; +}; + + +#endif // !EHCI_H diff --git a/src/add-ons/kernel/busses/usb/ehci.rdef b/src/add-ons/kernel/busses/usb/ehci.rdef new file mode 100644 index 0000000000..92f9ba4eb4 --- /dev/null +++ b/src/add-ons/kernel/busses/usb/ehci.rdef @@ -0,0 +1,15 @@ +/* + * ehci.rdef + */ + +resource app_signature "application/x-vnd.haiku-ehci"; + +resource app_version { + major = 0, + middle = 0, + minor = 1, + variety = 0, + internal = 0, + short_info = "EHCI host controller driver", + long_info = "Haiku EHCI HCD - Copyright 2006, Haiku Inc." +}; diff --git a/src/add-ons/kernel/busses/usb/ehci_hardware.h b/src/add-ons/kernel/busses/usb/ehci_hardware.h new file mode 100644 index 0000000000..942152c82d --- /dev/null +++ b/src/add-ons/kernel/busses/usb/ehci_hardware.h @@ -0,0 +1,225 @@ +/* + * Copyright 2006, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#ifndef EHCI_HARDWARE_H +#define EHCI_HARDWARE_H + +// Host Controller Operational Registers (EHCI Spec 2.3) +#define EHCI_USBCMD 0x00 // USB Command +#define EHCI_USBSTS 0x04 // USB Status +#define EHCI_USBINTR 0x08 // USB Interrupt Enable +#define EHCI_FRINDEX 0x0c // USB Frame Index +#define EHCI_CTRDSSEGMENT 0x10 // 4GB Segment Selector +#define EHCI_PERIODICLISTBASE 0x14 // Frame List Base Address +#define EHCI_ASYNCLISTADDR 0x18 // Next Asynchronous List Address +#define EHCI_CONFIGFLAG 0x40 // Configured Flag Register +#define EHCI_PORTSC 0x44 // Port Status/Control + + +// USB Command Register (EHCI Spec 2.3.1) +#define EHCI_USBCMD_ITC_SHIFT 16 // Interrupt Threshold Control +#define EHCI_USBCMD_ITC_MASK 0xff +#define EHCI_USBCMD_ASPME (1 << 11) // Async Schedule Park Mode Enable +#define EHCI_USBCMD_ASPMC_SHIFT 8 // Async Schedule Park Mode Count +#define EHCI_USBCMD_ASPMC_MASK 0x03 +#define EHCI_USBCMD_LHCRESET (1 << 7) // Light Host Controller Reset +#define EHCI_USBCMD_INTONAAD (1 << 6) // Interrupt on Async Advance Dorbell +#define EHCI_USBCMD_ASENABLE (1 << 5) // Asynchronous Schedule Enable +#define EHCI_USBCMD_PSENABLE (1 << 4) // Periodic Schedule Enable +#define EHCI_USBCMD_FLS_SHIFT 2 // Frame List Size +#define EHCI_USBCMD_FLS_MASK 0x03 +#define EHCI_USBCMD_HCRESET (1 << 1) // Host Controller Reset +#define EHCI_USBCMD_RUNSTOP (1 << 0) // Run/Stop + + +// USB Status Register (EHCI Spec 2.3.2) +#define EHCI_USBSTS_ASSTATUS (1 << 15) // Asynchronous Schedule Status +#define EHCI_USBSTS_PSSTATUS (1 << 14) // Periodic Schedule Status +#define EHCI_USBSTS_RECLAMATION (1 << 13) // Reclamation +#define EHCI_USBSTS_HCHALTED (1 << 12) // Host Controller Halted +#define EHCI_USBSTS_INTONAA (1 << 5) // Interrupt on Async Advance +#define EHCI_USBSTS_HOSTSYSERR (1 << 4) // Host System Error +#define EHCI_USBSTS_FLROLLOVER (1 << 3) // Frame List Rollover +#define EHCI_USBSTS_PORTCHANGE (1 << 2) // Port Change Detected +#define EHCI_USBSTS_USBERRINT (1 << 1) // USB Error Interrupt +#define EHCI_USBSTS_USBINT (1 << 0) // USB Interrupt + + +// USB Interrupt Enable Register (EHCI Spec 2.3.3) +#define EHCI_USBINTR_INTONAA (1 << 5) // Interrupt on Async Advance Enable +#define EHCI_USBINTR_HOSTSYSERR (1 << 4) // Host System Error Enable +#define EHCI_USBINTR_FLROLLOVER (1 << 3) // Frame List Rollover Enable +#define EHCI_USBINTR_PORTCHANGE (1 << 2) // Port Change Interrupt Enable +#define EHCI_USBINTR_USBERRINT (1 << 1) // USB Error Interrupt Enable +#define EHCI_USBINTR_USBINT (1 << 0) // USB Interrupt Enable + + +// Configure Flag Register (EHCI Spec 2.3.8) +#define EHCI_CONFIGFLAG_FLAG (1 << 0) // Configure Flag + + +// Port Status and Control (EHCI Spec 2.3.9) +#define EHCI_PORTSC_WAKEOVERCUR (1 << 22) // Wake on Over-Current Enable +#define EHCI_PORTSC_WAKEDISCON (1 << 21) // Wake on Disconnect Enable +#define EHCI_PORTSC_WAKECONNECT (1 << 20) // Wake on Connect Enable +#define EHCI_PORTSC_PTC_SHIFT 16 // Port Test Control +#define EHCI_PORTSC_PTC_MASK 0x07 +#define EHCI_PORTSC_PIC_SHIFT 14 // Port Indicator Control +#define EHCI_PORTSC_PIC_MASK 0x03 +#define EHCI_PORTSC_PORTOWNER (1 << 13) // Port Owner +#define EHCI_PORTSC_PORTPOWER (1 << 12) // Port Power +#define EHCI_PORTSC_DPLUS (1 << 11) // Logical Level of D+ +#define EHCI_PORTSC_DMINUS (1 << 10) // Logical Level of D- +#define EHCI_PORTSC_PORTRESET (1 << 8) // Port Reset +#define EHCI_PORTSC_SUSPEND (1 << 7) // Suspend +#define EHCI_PORTSC_FORCERESUME (1 << 6) // Force Port Resume +#define EHCI_PORTSC_OCCHANGE (1 << 5) // Over-Current Change +#define EHCI_PORTSC_OCACTIVE (1 << 4) // Over-Current Active +#define EHCI_PORTSC_ENDISCHANGE (1 << 3) // Port Enable/Disable Change +#define EHCI_PORTSC_ENDIS (1 << 2) // Port Enabled/Disabled +#define EHCI_PORTSC_CONNCHANGE (1 << 1) // Connect Status Change +#define EHCI_PORTSC_CONNSTATUS (1 << 0) // Current Connect Status + + +// PCI Registers +#define PCI_LEGSUP 0xc0 // PCI Legacy Support +#define PCI_LEGSUP_USBPIRQDEN 0x2000 // USBP IRQ Deny + + +// Data Structures (EHCI Spec 3) + +// Periodic Frame List Element Flags (EHCI Spec 3.1) +#define EHCI_PFRAMELIST_ITD 0x00 // Isochronous Transfer Descriptor +#define EHCI_PFRAMELIST_QH 0x01 // Queue Head +#define EHCI_PFRAMELIST_SITD 0x10 // Split Transaction Isochronous TD +#define EHCI_PFRAMELIST_FSTN 0x11 // Frame Span Traversal Node + + +// ToDo: Isochronous (High-Speed) Transfer Descriptors (iTD, EHCI Spec 3.2) +// ToDo: Split Transaction Isochronous Transfer Descriptors (siTD, EHCI Spec 3.3) + +// Queue Element Transfer Descriptors (qTD, EHCI Spec 3.5) +typedef struct { + // Hardware Part + addr_t next_phy; + addr_t alt_next_phy; + uint32 status_token; + addr_t buffer_phy[5]; + + // Software Part + addr_t this_phy; + void *next_log; + void *alt_next_log; + size_t buffer_size; + void *buffer_log; +} ehci_qtd; + + +#define EHCI_QTD_TERMINATE (1 << 0) +#define EHCI_QTD_BYTES_SHIFT 16 +#define EHCI_QTD_BYTES_MASK 0xffff +#define EHCI_QTD_IOC (1 << 15) +#define EHCI_QTD_PAGE_SHIFT 12 +#define EHCI_QTD_PAGE_MASK 0x07 +#define EHCI_QTD_ERRCOUNT_SHIFT 10 +#define EHCI_QTD_ERRCOUNT_MASK 0x03 +#define EHCI_QTD_PID_SHIFT 8 +#define EHCI_QTD_PID_MASK 0x03 +#define EHCI_QTD_STATUS_SHIFT 0 +#define EHCI_QTD_STATUS_MASK 0x7f +#define EHCI_QTD_STATUS_ACTIVE (1 << 7) // Active +#define EHCI_QTD_STATUS_HALTED (1 << 6) // Halted +#define EHCI_QTD_STATUS_BUFFER (1 << 5) // Data Buffer Error +#define EHCI_QTD_STATUS_BABBLE (1 << 4) // Babble Detected +#define EHCI_QTD_STATUS_TERROR (1 << 3) // Transaction Error +#define EHCI_QTD_STATUS_MISSED (1 << 2) // Missed Micro-Frame +#define EHCI_QTD_STATUS_SPLIT (1 << 1) // Split Transaction State +#define EHCI_QTD_STATUS_PING (1 << 0) // Ping State + + +// Queue Head (QH, EHCI Spec 3.6) +typedef struct { + // Hardware Part + addr_t link_phy; + uint32 endpoint_chars; + uint32 endpoint_caps; + addr_t current_qtd_phy; + uint32 overlay[8]; + // Software Part + addr_t this_phy; + void *link_log; + void *element_log; +} ehci_qh; + + +// Applies to ehci_qh.link_phy +#define EHCI_QH_TYPE_ITD (0 << 1) +#define EHCI_QH_TYPE_QH (1 << 1) +#define EHCI_QH_TYPE_SITD (2 << 1) +#define EHCI_QH_TYPE_FSTN (3 << 1) +#define EHCI_QH_TERMINATE (1 << 0) + +// Applies to ehci_qh.endpoint_chars +#define EHCI_QH_CHARS_RL_SHIFT 28 // NAK Count Reload +#define EHCI_QH_CHARS_RL_MASK 0x07 +#define EHCI_QH_CHARS_CONTROL (1 << 27) // Control Endpoint Flag +#define EHCI_QH_CHARS_MPL_SHIFT 16 // Max Packet Length +#define EHCI_QH_CHARS_MPL_MASK 0x03ff +#define EHCI_QH_CHARS_RECHEAD (1 << 15) // Head of Reclamation List Flag +#define EHCI_QH_CHARS_TOGGLE (1 << 14) // Data Toggle Control +#define EHCI_QH_CHARS_EPS_FULL (0 << 12) // Endpoint is Full-Speed +#define EHCI_QH_CHARS_EPS_LOW (1 << 12) // Endpoint is Low-Speed +#define EHCI_QH_CHARS_EPS_HIGH (2 << 12) // Endpoint is High-Speed +#define EHCI_QH_CHARS_EPT_SHIFT 8 // Endpoint Number +#define EHCI_QH_CHARS_EPT_MASK 0x0f +#define EHCI_QH_CHARS_INACTIVE (1 << 7) // Inactive on Next Transaction +#define EHCI_QH_CHARS_DEV_SHIFT 0 // Device Address +#define EHCI_QH_CHARS_DEV_MASK 0x7f + + +// Applies to ehci_qh.endpoint_caps +#define EHCI_QH_CAPS_MULT_SHIFT 30 // Transactions per Micro-Frame +#define EHCI_QH_CAPS_MULT_MASK 0x03 +#define EHCI_QH_CAPS_PORT_SHIFT 23 // Port Number +#define EHCI_QH_CAPS_PORT_MASK 0x7f +#define EHCI_QH_CAPS_HUB_SHIFT 16 // Hub Address +#define EHCI_QH_CAPS_HUB_MASK 0x7f +#define EHCI_QH_CAPS_SCM_SHIFT 8 // Split Completion Mask +#define EHCI_QH_CAPS_SCM_MASK 0xff +#define EHCI_QH_CAPS_ISM_SHIFT 0 // Interrupt Schedule Mask +#define EHCI_QH_CAPS_ISM_MASK 0xff + + +// Applies to ehci_qh.overlay[EHCI_QH_OL_*_INDEX] +#define EHCI_QH_OL_NAK_INDEX 1 // NAK Counter +#define EHCI_QH_OL_NAK_SHIFT 1 +#define EHCI_QH_OL_NAK_MASK 0x0f +#define EHCI_QH_OL_TOGGLE_INDEX 2 // Data Toggle +#define EHCI_QH_OL_TOGGLE (1 << 31) +#define EHCI_QH_OL_IOC_INDEX 2 // Interrupt on Complete +#define EHCI_QH_OL_IOC (1 << 15) +#define EHCI_QH_OL_ERRC_INDEX 2 // Error Counter +#define EHCI_QH_OL_ERRC_SHIFT 10 +#define EHCI_QH_OL_ERRC_MASK 0x03 +#define EHCI_QH_OL_PING_INDEX 2 // Ping State +#define EHCI_QH_OL_PING (1 << 0) +#define EHCI_QH_OL_CPROG_INDEX 4 // Split-Transaction Complete-Split Progress +#define EHCI_QH_OL_CPROG_SHIFT 0 +#define EHCI_QH_OL_CPROG_MASK 0xff +#define EHCI_QH_OL_FTAG_INDEX 5 // Split-Transaction Frame Tag +#define EHCI_QH_OL_FTAG_SHIFT 0 +#define EHCI_QH_OL_FTAG_MASK 0x0f +#define EHCI_QH_OL_BYTES_INDEX 5 // Transfered Bytes +#define EHCI_QH_OL_BYTES_SHIFT 5 +#define EHCI_QH_OL_BYTES_MASK 0x7f + + +// ToDo: Periodic Frame Span Traversal Node (FSTN, EHCI Spec 3.7) + + +#endif // !EHCI_HARDWARE_H diff --git a/src/add-ons/kernel/busses/usb/ehci_rh.cpp b/src/add-ons/kernel/busses/usb/ehci_rh.cpp new file mode 100644 index 0000000000..b438b351ac --- /dev/null +++ b/src/add-ons/kernel/busses/usb/ehci_rh.cpp @@ -0,0 +1,139 @@ +/* + * Copyright 2006, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include "ehci.h" + + +static usb_device_descriptor sEHCIRootHubDevice = +{ + 18, // Descriptor length + USB_DESCRIPTOR_DEVICE, // Descriptor type + 0x200, // USB 2.0 + 0x09, // Class (9 = Hub) + 0, // Subclass + 0, // Protocol + 64, // Max packet size on endpoint 0 + 0, // Vendor ID + 0, // Product ID + 0x200, // Version + 1, // Index of manufacturer string + 2, // Index of product string + 0, // Index of serial number string + 1 // Number of configurations +}; + + +struct ehci_root_hub_configuration_s { + usb_configuration_descriptor configuration; + usb_interface_descriptor interface; + usb_endpoint_descriptor endpoint; + usb_hub_descriptor hub; +} _PACKED; + + +static ehci_root_hub_configuration_s sEHCIRootHubConfig = +{ + { // configuration descriptor + 9, // Descriptor length + USB_DESCRIPTOR_CONFIGURATION, // Descriptor type + 34, // Total length of configuration (including + // interface, endpoint and hub descriptors) + 1, // Number of interfaces + 1, // Value of this configuration + 0, // Index of configuration string + 0x40, // Attributes (0x40 = self powered) + 0 // Max power (0, since self powered) + }, + + { // interface descriptor + 9, // Descriptor length + USB_DESCRIPTOR_INTERFACE, // Descriptor type + 0, // Interface number + 0, // Alternate setting + 1, // Number of endpoints + 0x09, // Interface class (9 = Hub) + 0, // Interface subclass + 0, // Interface protocol + 0, // Index of interface string + }, + + { // endpoint descriptor + 7, // Descriptor length + USB_DESCRIPTOR_ENDPOINT, // Descriptor type + USB_REQTYPE_DEVICE_IN | 1, // Endpoint address (first in IN endpoint) + 0x03, // Attributes (0x03 = interrupt endpoint) + 8, // Max packet size + 0xFF // Interval + }, + + { // hub descriptor + 9, // Descriptor length (including + // deprecated power control mask) + USB_DESCRIPTOR_HUB, // Descriptor type + 2, // Number of ports + 0x0000, // Hub characteristics + 50, // Power on to power good (in 2ms units) + 0, // Maximum current (in mA) + 0x00, // Both ports are removable + 0xff // Depricated power control mask + } +}; + + +struct ehci_root_hub_string_s { + uint8 length; + uint8 descriptor_type; + uint16 unicode_string[12]; +} _PACKED; + + +static ehci_root_hub_string_s sEHCIRootHubStrings[3] = { + { + 4, // Descriptor length + USB_DESCRIPTOR_STRING, // Descriptor type + 0x0409 // Supported language IDs (English US) + }, + + { + 12, // Descriptor length + USB_DESCRIPTOR_STRING, // Descriptor type + 'H', 'A', 'I', 'K', 'U', ' ', // Characters + 'I', 'n', 'c', '.' + }, + + { + 26, // Descriptor length + USB_DESCRIPTOR_STRING, // Descriptor type + 'E', 'H', 'C', 'I', ' ', 'R', // Characters + 'o', 'o', 't', 'H', 'u', 'b' + } +}; + + +EHCIRootHub::EHCIRootHub(EHCI *ehci, int8 deviceAddress) + : Hub(ehci, NULL, sEHCIRootHubDevice, deviceAddress, false) +{ + fEHCI = ehci; +} + + +status_t +EHCIRootHub::SubmitTransfer(Transfer *transfer) +{ + return B_ERROR; +} + + +void +EHCIRootHub::UpdatePortStatus() +{ + for (int32 i = 0; i < sEHCIRootHubConfig.hub.num_ports; i++) { + fPortStatus[i].status = 0; + fPortStatus[i].change = 0; + } +} diff --git a/src/add-ons/kernel/busses/usb/ohci.cpp b/src/add-ons/kernel/busses/usb/ohci.cpp index a388bb4ce4..e31a0947e9 100644 --- a/src/add-ons/kernel/busses/usb/ohci.cpp +++ b/src/add-ons/kernel/busses/usb/ohci.cpp @@ -87,8 +87,8 @@ ohci_std_ops( int32 op , ... ) // - &stack: reference to a stack instance form stack.cpp //------------------------------------------------------------------------ -static bool -ohci_add_to( Stack &stack ) +static status_t +ohci_add_to( Stack *stack ) { status_t status; pci_info *item; @@ -126,14 +126,14 @@ ohci_add_to( Stack &stack ) continue; } TRACE("USB OHCI: init_hardware(): found at IRQ %u \n", item->u.h0.interrupt_line); - OHCI *bus = new OHCI( item , &stack ); + OHCI *bus = new OHCI( item , stack ); if ( bus->InitCheck() != B_OK ) { delete bus; break; } - stack.AddBusManager( bus ); + stack->AddBusManager( bus ); bus->Start(); found = true; break; diff --git a/src/add-ons/kernel/busses/usb/uhci.cpp b/src/add-ons/kernel/busses/usb/uhci.cpp index 47a1126de5..f2801f0bc2 100644 --- a/src/add-ons/kernel/busses/usb/uhci.cpp +++ b/src/add-ons/kernel/busses/usb/uhci.cpp @@ -792,8 +792,6 @@ UHCI::FinishTransfers() transfer = transfer->link; } } - - Unlock(); } } @@ -988,8 +986,8 @@ UHCI::Interrupt() } -bool -UHCI::AddTo(Stack &stack) +status_t +UHCI::AddTo(Stack *stack) { #ifdef TRACE_USB set_dprintf_enabled(true); @@ -1026,7 +1024,7 @@ UHCI::AddTo(Stack &stack) } TRACE(("usb_uhci: AddTo(): found at IRQ %u\n", item->u.h0.interrupt_line)); - UHCI *bus = new(std::nothrow) UHCI(item, &stack); + UHCI *bus = new(std::nothrow) UHCI(item, stack); if (!bus) { delete item; sPCIModule = NULL; @@ -1040,8 +1038,11 @@ UHCI::AddTo(Stack &stack) continue; } + // the bus took it away + item = new(std::nothrow) pci_info; + bus->Start(); - stack.AddBusManager(bus); + stack->AddBusManager(bus); found = true; } } @@ -1054,6 +1055,7 @@ UHCI::AddTo(Stack &stack) return ENODEV; } + delete item; return B_OK; } diff --git a/src/add-ons/kernel/busses/usb/uhci.h b/src/add-ons/kernel/busses/usb/uhci.h index 33a0d4c154..1123a6641f 100644 --- a/src/add-ons/kernel/busses/usb/uhci.h +++ b/src/add-ons/kernel/busses/usb/uhci.h @@ -71,7 +71,7 @@ public: virtual status_t SubmitTransfer(Transfer *transfer); virtual status_t SubmitRequest(Transfer *transfer); -static bool AddTo(Stack &stack); +static status_t AddTo(Stack *stack); // Port operations uint16 PortStatus(int32 index); @@ -161,22 +161,15 @@ static pci_module_info *sPCIModule; class UHCIRootHub : public Hub { public: - UHCIRootHub(UHCI *uhci, int8 deviceNum); + UHCIRootHub(UHCI *uhci, int8 deviceAddress); status_t SubmitTransfer(Transfer *transfer); void UpdatePortStatus(); private: - usb_port_status fPortStatus[2]; UHCI *fUHCI; }; -struct hostcontroller_priv { - uhci_qh *topqh; - uhci_td *firsttd; - uhci_td *lasttd; -}; - #endif diff --git a/src/add-ons/kernel/busses/usb/uhci_hardware.h b/src/add-ons/kernel/busses/usb/uhci_hardware.h index f0afb4d2de..ebc9942bc2 100644 --- a/src/add-ons/kernel/busses/usb/uhci_hardware.h +++ b/src/add-ons/kernel/busses/usb/uhci_hardware.h @@ -74,7 +74,7 @@ * Hardware structs * ************************************************************/ -//A framelist thingie +// Framelist flags #define FRAMELIST_TERMINATE 0x1 #define FRAMELIST_NEXT_IS_QH 0x2 @@ -82,7 +82,7 @@ // Represents a Transfer Descriptor (TD) typedef struct { - //Hardware part + // Hardware part addr_t link_phy; // Link to the next TD/QH uint32 status; // Status field uint32 token; // Contains the packet header (where it needs to be sent) diff --git a/src/add-ons/kernel/busses/usb/uhci_rh.cpp b/src/add-ons/kernel/busses/usb/uhci_rh.cpp index 10925ca95b..60f5ea4a1e 100644 --- a/src/add-ons/kernel/busses/usb/uhci_rh.cpp +++ b/src/add-ons/kernel/busses/usb/uhci_rh.cpp @@ -91,7 +91,7 @@ struct uhci_root_hub_string_s { uint8 length; uint8 descriptor_type; uint16 unicode_string[12]; -}; +} _PACKED; static uhci_root_hub_string_s sUHCIRootHubStrings[3] = { @@ -117,8 +117,8 @@ static uhci_root_hub_string_s sUHCIRootHubStrings[3] = { }; -UHCIRootHub::UHCIRootHub(UHCI *uhci, int8 devicenum) - : Hub(uhci, NULL, sUHCIRootHubDevice, devicenum, false) +UHCIRootHub::UHCIRootHub(UHCI *uhci, int8 deviceAddress) + : Hub(uhci, NULL, sUHCIRootHubDevice, deviceAddress, false) { fUHCI = uhci; }