From 2159acc7d9492bb23888c5f4a8664d49ff91f371 Mon Sep 17 00:00:00 2001 From: Niels Sascha Reedijk Date: Mon, 25 Sep 2006 14:26:22 +0000 Subject: [PATCH] Implemented most of the roothub. There remains only one issue: reading the Port registers seem to return a bogus value (0). I need to find out what's causing it. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18918 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/busses/usb/ohci.cpp | 169 +++++++++-- src/add-ons/kernel/busses/usb/ohci.h | 26 +- src/add-ons/kernel/busses/usb/ohci_hardware.h | 14 +- src/add-ons/kernel/busses/usb/ohci_rh.cpp | 271 ++++++++++++++---- 4 files changed, 388 insertions(+), 92 deletions(-) diff --git a/src/add-ons/kernel/busses/usb/ohci.cpp b/src/add-ons/kernel/busses/usb/ohci.cpp index 8cc08d7a6e..b8bc69db90 100644 --- a/src/add-ons/kernel/busses/usb/ohci.cpp +++ b/src/add-ons/kernel/busses/usb/ohci.cpp @@ -108,9 +108,6 @@ ohci_add_to(Stack *stack) return status; } TRACE(("usb_ohci init_hardware(): Setting up hardware\n")); - // - // TODO: in the future we might want to support multiple host controllers. - // item = new pci_info; for ( i = 0 ; OHCI::pci_module->get_nth_pci_info( i , item ) == B_OK ; i++ ) { if ( ( item->class_base == PCI_serial_bus ) && ( item->class_sub == PCI_usb ) @@ -121,20 +118,28 @@ 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 ); - if ( bus->InitCheck() != B_OK ) { - delete bus; - break; + OHCI *bus = new(std::nothrow) OHCI(item, stack); + + if (!bus) { + delete item; + OHCI::pci_module = NULL; + put_module(B_PCI_MODULE_NAME); + return B_NO_MEMORY; } + if (bus->InitCheck() != B_OK) { + TRACE(("usb_ohci: bus failed to initialize...\n")); + delete bus; + continue; + } + + bus->Start(); stack->AddBusManager( bus ); - bus->Start(); found = true; - break; } } - if ( found == false ) { + if (!found) { TRACE(("USB OHCI: init hardware(): no devices found\n")); free( item ); put_module( B_PCI_MODULE_NAME ); @@ -184,7 +189,10 @@ OHCI::OHCI(pci_info *info, Stack *stack) fHccaArea(-1), fDummyControl(0), fDummyBulk(0), - fDummyIsochronous(0) + fDummyIsochronous(0), + fRootHub(0), + fRootHubAddress(0), + fNumPorts(0) { fPcii = info; fStack = stack; @@ -321,9 +329,7 @@ OHCI::OHCI(pci_info *info, Stack *stack) uint32 interval = OHCI_GET_IVAL(frameinterval); WriteReg(OHCI_PERIODIC_START, OHCI_PERIODIC(interval)); - - m_roothub_base = 255; //Invalidate the Root Hub address - + fInitOK = true; } OHCI::~OHCI() @@ -338,18 +344,140 @@ OHCI::~OHCI() FreeEndpoint(fDummyBulk); if (fDummyIsochronous) FreeEndpoint(fDummyIsochronous); + if (fRootHub) + delete fRootHub; for (int i = 0; i < OHCI_NO_EDS; i++) if (fInterruptEndpoints[i]) FreeEndpoint(fInterruptEndpoints[i]); } +status_t +OHCI::Start() +{ + if (!(ReadReg(OHCI_CONTROL) & OHCI_HCFS_OPERATIONAL)) { + TRACE(("USB OHCI::Start(): Controller not started. TODO: find out what happens.\n")); + return B_ERROR; + } + + fRootHubAddress = AllocateAddress(); + fNumPorts = OHCI_GET_PORT_COUNT(ReadReg(OHCI_RH_DESCRIPTOR_A)); + + fRootHub = new(std::nothrow) OHCIRootHub(this, fRootHubAddress); + if (!fRootHub) { + TRACE_ERROR(("USB OHCI::Start(): no memory to allocate root hub\n")); + return B_NO_MEMORY; + } + + if (fRootHub->InitCheck() < B_OK) { + TRACE_ERROR(("USB OHCI::Start(): root hub failed init check\n")); + return B_ERROR; + } + + SetRootHub(fRootHub); + TRACE(("USB OHCI::Start(): Succesful start\n")); + return B_OK; +} + +status_t OHCI::SubmitTransfer( Transfer *t ) +{ + TRACE(("usb OHCI::SubmitTransfer: called for device %d\n", t->TransferPipe()->DeviceAddress())); + + if (t->TransferPipe()->DeviceAddress() == fRootHubAddress) + return fRootHub->ProcessTransfer(t, this); + + return B_ERROR; +} + +status_t +OHCI::GetPortStatus(uint8 index, usb_port_status *status) +{ + if (index > fNumPorts) + return B_BAD_INDEX; + + status->status = status->change = 0; + uint32 portStatus = ReadReg(OHCI_RH_PORT_STATUS(index)); + + TRACE(("OHCIRootHub::GetPortStatus: Port %i Value 0x%lx\n", OHCI_RH_PORT_STATUS(index), portStatus)); + + // status + if (portStatus & OHCI_PORTSTATUS_CCS) + status->status |= PORT_STATUS_CONNECTION; + if (portStatus & OHCI_PORTSTATUS_PES) + status->status |= PORT_STATUS_ENABLE; + if (portStatus & OHCI_PORTSTATUS_PRS) + status->status |= PORT_STATUS_RESET; + if (portStatus & OHCI_PORTSTATUS_LSDA) + status->status |= PORT_STATUS_LOW_SPEED; + if (portStatus & OHCI_PORTSTATUS_PSS) + status->status |= PORT_STATUS_SUSPEND; + if (portStatus & OHCI_PORTSTATUS_POCI) + status->status |= PORT_STATUS_OVER_CURRENT; + if (portStatus & OHCI_PORTSTATUS_PPS) + status->status |= PORT_STATUS_POWER; + + + // change + if (portStatus & OHCI_PORTSTATUS_CSC) + status->change |= PORT_STATUS_CONNECTION; + if (portStatus & OHCI_PORTSTATUS_PESC) + status->change |= PORT_STATUS_ENABLE; + if (portStatus & OHCI_PORTSTATUS_PSSC) + status->change |= PORT_STATUS_SUSPEND; + if (portStatus & OHCI_PORTSTATUS_OCIC) + status->change |= PORT_STATUS_OVER_CURRENT; + if (portStatus & OHCI_PORTSTATUS_PRSC) + status->change |= PORT_STATUS_RESET; + + return B_OK; +} + +status_t +OHCI::SetPortFeature(uint8 index, uint16 feature) +{ + if (index > fNumPorts) + return B_BAD_INDEX; + + switch (feature) { + case PORT_RESET: + WriteReg(OHCI_RH_PORT_STATUS(index), OHCI_PORTSTATUS_PRS); + return B_OK; + + case PORT_POWER: + WriteReg(OHCI_RH_PORT_STATUS(index), OHCI_PORTSTATUS_PPS); + return B_OK; + } + + return B_BAD_VALUE; +} + +status_t +OHCI::ClearPortFeature(uint8 index, uint16 feature) +{ + if (index > fNumPorts) + return B_BAD_INDEX; + + switch (feature) { + case C_PORT_RESET: + WriteReg(OHCI_RH_PORT_STATUS(index), OHCI_PORTSTATUS_CSC); + return B_OK; + + case C_PORT_CONNECTION: + WriteReg(OHCI_RH_PORT_STATUS(index), OHCI_PORTSTATUS_CSC); + return B_OK; + } + + return B_BAD_VALUE; +} + Endpoint * OHCI::AllocateEndpoint() { Endpoint *endpoint = new Endpoint; void *phy; - if (fStack->AllocateChunk((void **)&endpoint->ed, &phy, sizeof(ohci_endpoint_descriptor)) != B_OK) + if (fStack->AllocateChunk((void **)&endpoint->ed, &phy, sizeof(ohci_endpoint_descriptor)) != B_OK) { + TRACE(("OHCI::AllocateEndpoint(): Error Allocating Endpoint\n")); return 0; + } endpoint->physicaladdress = (addr_t)phy; memset((void *)endpoint->ed, 0, sizeof(ohci_endpoint_descriptor)); @@ -364,17 +492,6 @@ OHCI::FreeEndpoint(Endpoint *end) delete end; } -//------------------------------------------------------------------------ -// OHCI:: Submit a transfer -// -// parameters: -// - t: pointer to a transfer instance -//------------------------------------------------------------------------ - -status_t OHCI::SubmitTransfer( Transfer *t ) -{ - return B_OK; -} pci_module_info *OHCI::pci_module = 0; diff --git a/src/add-ons/kernel/busses/usb/ohci.h b/src/add-ons/kernel/busses/usb/ohci.h index 5f591294d3..c23bb36eba 100644 --- a/src/add-ons/kernel/busses/usb/ohci.h +++ b/src/add-ons/kernel/busses/usb/ohci.h @@ -48,13 +48,20 @@ public: OHCI(pci_info *info, Stack *stack); ~OHCI(); + status_t Start(); status_t SubmitTransfer(Transfer *t); //Override from BusManager. static pci_module_info *pci_module; // Global data for the module. + status_t GetPortStatus(uint8 index, usb_port_status *status); + status_t ClearPortFeature(uint8 index, uint16 feature); + status_t SetPortFeature(uint8 index, uint16 feature); + + uint8 PortCount() { return fNumPorts; }; + +private: inline void WriteReg(uint32 reg, uint32 value); inline uint32 ReadReg(uint32 reg); - -private: + // Global pci_info *fPcii; // pci-info struct Stack *fStack; // Pointer to the stack @@ -69,8 +76,9 @@ private: Endpoint *fDummyBulk; Endpoint *fDummyIsochronous; // Root Hub - OHCIRootHub *m_roothub; // the root hub - addr_t m_roothub_base; // Base address of the root hub + OHCIRootHub *fRootHub; // the root hub + uint8 fRootHubAddress; // the usb address of the roothub + uint8 fNumPorts; // the number of ports // functions Endpoint *AllocateEndpoint(); // allocate memory for an endpoint void FreeEndpoint(Endpoint *end); //Free endpoint @@ -85,12 +93,8 @@ private: class OHCIRootHub : public Hub { public: - OHCIRootHub( OHCI *ohci , int8 devicenum ); - status_t SubmitTransfer( Transfer *t ); - void UpdatePortStatus(); - private: - usb_port_status m_hw_port_status[2]; // the port status (maximum of two) - OHCI *m_ohci; // needed because of internal data + OHCIRootHub(OHCI *ohci, int8 deviceAddress); + status_t ProcessTransfer(Transfer *t, OHCI *ohci); }; // @@ -110,6 +114,4 @@ struct Endpoint }; }; -#define OHCI_DEBUG - #endif // OHCI_H diff --git a/src/add-ons/kernel/busses/usb/ohci_hardware.h b/src/add-ons/kernel/busses/usb/ohci_hardware.h index dd22468233..b5276109fd 100644 --- a/src/add-ons/kernel/busses/usb/ohci_hardware.h +++ b/src/add-ons/kernel/busses/usb/ohci_hardware.h @@ -189,7 +189,7 @@ // -------------------------------- #define OHCI_RH_DESCRIPTOR_A 0x48 -#define OHCI_GET_NDP(s) ((s) & 0xff) +#define OHCI_GET_PORT_COUNT(s) ((s) & 0xff) #define OHCI_PSM 0x0100 // Power Switching Mode #define OHCI_NPS 0x0200 // No Power Switching #define OHCI_DT 0x0400 // Device Type @@ -220,6 +220,18 @@ // -------------------------------- #define OHCI_RH_PORT_STATUS(n) (0x50 + (n)*4) // 1 based indexing +#define OHCI_PORTSTATUS_CCS 0x00000001 // Current Connection Status +#define OHCI_PORTSTATUS_PES 0x00000002 // Port Enable Status +#define OHCI_PORTSTATUS_PSS 0x00000004 // Port Suspend Status +#define OHCI_PORTSTATUS_POCI 0x00000008 // Port Overcurrent Indicator +#define OHCI_PORTSTATUS_PRS 0x00000010 // Port Reset Status +#define OHCI_PORTSTATUS_PPS 0x00000100 // Port Power Status +#define OHCI_PORTSTATUS_LSDA 0x00000200 // Low Speed Device Attached +#define OHCI_PORTSTATUS_CSC 0x00010000 // Connection Status Change +#define OHCI_PORTSTATUS_PESC 0x00020000 // Port Enable Status Change +#define OHCI_PORTSTATUS_PSSC 0x00040000 // Port Suspend Status change +#define OHCI_PORTSTATUS_OCIC 0x00080000 // Port Overcurrent Change +#define OHCI_PORTSTATUS_PRSC 0x00100000 // Port Reset Status Change // -------------------------------- // ???? diff --git a/src/add-ons/kernel/busses/usb/ohci_rh.cpp b/src/add-ons/kernel/busses/usb/ohci_rh.cpp index b063bd9587..62b51d5c6b 100644 --- a/src/add-ons/kernel/busses/usb/ohci_rh.cpp +++ b/src/add-ons/kernel/busses/usb/ohci_rh.cpp @@ -22,9 +22,8 @@ #include "ohci.h" -#include -usb_device_descriptor ohci_devd = +static usb_device_descriptor sOHCIRootHubDevice = { 0x12, //Descriptor size USB_DESCRIPTOR_DEVICE , //Type of descriptor @@ -40,65 +39,231 @@ usb_device_descriptor ohci_devd = 1 //Number of configurations }; -usb_configuration_descriptor ohci_confd = -{ - 0x09, //Size - USB_DESCRIPTOR_CONFIGURATION , - 25 , //Total size (taken from BSD source) - 1 , //Number interfaces - 1 , //Value of configuration - 0 , //Number of configuration - 0x40 , //Self powered - 0 //Max power (0, because of self power) +struct ohci_root_hub_configuration_s { + usb_configuration_descriptor configuration; + usb_interface_descriptor interface; + usb_endpoint_descriptor endpoint; + usb_hub_descriptor hub; +} _PACKED; + +static ohci_root_hub_configuration_s sOHCIRootHubConfig = { + { // configuration descriptor + 9, //Size + USB_DESCRIPTOR_CONFIGURATION, + 34, //Total size of the configuration + 1, //Number interfaces + 1, //Value of configuration + 0, //Number of configuration + 0x40, //Self powered + 0 //Max power (0, because of self power) + }, + + { // interface descriptor + 9, //Size + USB_DESCRIPTOR_INTERFACE, + 0, //Interface number + 0, //Alternate setting + 1, //Num endpoints + 0x09, //Interface class + 0, //Interface subclass + 0, //Interface protocol + 0 //Interface + }, + + { // endpoint descriptor + 7, //Size + USB_DESCRIPTOR_ENDPOINT, + USB_REQTYPE_DEVICE_IN | 1, //1 from freebsd driver + 0x3, // Interrupt + 8, // Max packet size + 0xFF // Interval 256 + }, + + { + 9, //Including deprecated powerctrlmask + USB_DESCRIPTOR_HUB, + 0, //Number of ports + 0x0000, //Hub characteristics FIXME + 50, //Power on to power good + 0, // Current + 0x00, //Both ports are removable + 0xff // Depricated power control mask + } }; -usb_interface_descriptor ohci_intd = -{ - 0x09 , //Size - USB_DESCRIPTOR_INTERFACE , - 0 , //Interface number - 0 , //Alternate setting - 1 , //Num endpoints - 0x09 , //Interface class - 0 , //Interface subclass - 0 , //Interface protocol - 0 , //Interface +struct ohci_root_hub_string_s { + uint8 length; + uint8 descriptor_type; + uint16 unicode_string[12]; +} _PACKED; + + +static ohci_root_hub_string_s sOHCIRootHubStrings[3] = { + { + 4, // Descriptor length + USB_DESCRIPTOR_STRING, // Descriptor type + { + 0x0409 // Supported language IDs (English US) + } + }, + + { + 22, // Descriptor length + USB_DESCRIPTOR_STRING, // Descriptor type + { + 'H', 'A', 'I', 'K', 'U', // Characters + ' ', 'I', 'n', 'c', '.' + } + }, + + { + 26, // Descriptor length + USB_DESCRIPTOR_STRING, // Descriptor type + { + 'O', 'H', 'C', 'I', ' ', // Characters + 'R', 'o', 'o', 't', 'H', + 'u', 'b' + } + } }; -usb_endpoint_descriptor ohci_endd = -{ - 0x07 , //Size - USB_DESCRIPTOR_ENDPOINT, - USB_REQTYPE_DEVICE_IN | 1, //1 from freebsd driver - 0x3 , // Interrupt - 8 , // Max packet size - 0xFF // Interval 256 -}; -usb_hub_descriptor ohci_hubd = +OHCIRootHub::OHCIRootHub(OHCI *ohci, int8 deviceAddress) + : Hub(ohci->RootObject(), sOHCIRootHubDevice, deviceAddress , USB_SPEED_FULLSPEED ) { - 0x09 , //Including deprecated powerctrlmask - USB_DESCRIPTOR_HUB, - 1, //Number of ports - 0x02 | 0x01 , //Hub characteristics FIXME - 50 , //Power on to power good - 0, // Current - 0x00 //Both ports are removable -}; - -//Implementation -OHCIRootHub::OHCIRootHub( OHCI *ohci , int8 devicenum ) - : Hub( ohci->RootObject() , ohci_devd , devicenum , USB_SPEED_FULLSPEED ) -{ - m_ohci = ohci; } -status_t OHCIRootHub::SubmitTransfer( Transfer *t ) +status_t +OHCIRootHub::ProcessTransfer(Transfer *t, OHCI *ohci) { + if ((t->TransferPipe()->Type() & USB_OBJECT_CONTROL_PIPE) == 0) + return B_ERROR; + + usb_request_data *request = t->RequestData(); + TRACE(("OHCIRootHub::ProcessTransfer(): request: %d\n", request->Request)); + + uint32 status = B_USB_STATUS_DEVICE_TIMEOUT; + 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), + t->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. + // TODO (?) actually check for the value + memset(t->Data(), 0, actualLength); + status = B_USB_STATUS_SUCCESS; + break; + } + + usb_port_status portStatus; + if (ohci->GetPortStatus(request->Index, &portStatus) >= B_OK) { + actualLength = MIN(sizeof(usb_port_status), t->DataLength()); + memcpy(t->Data(), (void *)&portStatus, actualLength); + status = B_USB_STATUS_SUCCESS; + } + + break; + } + + case USB_REQUEST_SET_ADDRESS: + if (request->Value >= 128) { + status = B_USB_STATUS_DEVICE_TIMEOUT; + break; + } + + TRACE(("OHCIRootHub::ProcessTransfer(): set address: %d\n", request->Value)); + status = B_USB_STATUS_SUCCESS; + break; + + case USB_REQUEST_GET_DESCRIPTOR: + TRACE(("OHCIRootHub::ProcessTransfer(): get descriptor: %d\n", request->Value >> 8)); + + switch (request->Value >> 8) { + case USB_DESCRIPTOR_DEVICE: { + actualLength = MIN(sizeof(usb_device_descriptor), + t->DataLength()); + memcpy(t->Data(), (void *)&sOHCIRootHubDevice, + actualLength); + status = B_USB_STATUS_SUCCESS; + break; + } + + case USB_DESCRIPTOR_CONFIGURATION: { + //Make sure we have the correct number of ports + sOHCIRootHubConfig.hub.num_ports = ohci->PortCount(); + + actualLength = MIN(sizeof(sOHCIRootHubConfig), + t->DataLength()); + memcpy(t->Data(), (void *)&(sOHCIRootHubConfig), + actualLength); + status = B_USB_STATUS_SUCCESS; + break; + } + + case USB_DESCRIPTOR_STRING: { + uint8 index = request->Value & 0x00ff; + if (index > 2) + break; + + actualLength = MIN(sOHCIRootHubStrings[index].length, + t->DataLength()); + memcpy(t->Data(), (void *)&sOHCIRootHubStrings[index], + actualLength); + status = B_USB_STATUS_SUCCESS; + break; + } + + case USB_DESCRIPTOR_HUB: { + //Make sure we have the correct number of ports + sOHCIRootHubConfig.hub.num_ports = ohci->PortCount(); + + actualLength = MIN(sizeof(usb_hub_descriptor), + t->DataLength()); + memcpy(t->Data(), (void *)&sOHCIRootHubConfig.hub, + actualLength); + status = B_USB_STATUS_SUCCESS; + break; + } + } + break; + + case USB_REQUEST_SET_CONFIGURATION: + status = B_USB_STATUS_SUCCESS; + break; + + case USB_REQUEST_CLEAR_FEATURE: { + if (request->Index == 0) { + // we don't support any hub changes + TRACE_ERROR(("OHCIRootHub::ProcessTransfer(): clear feature: no hub changes\n")); + break; + } + + TRACE(("OHCIRootHub::ProcessTransfer(): clear feature: %d\n", request->Value)); + if (ohci->ClearPortFeature(request->Index, request->Value) >= B_OK) + status = B_USB_STATUS_SUCCESS; + break; + } + + case USB_REQUEST_SET_FEATURE: { + if (request->Index == 0) { + // we don't support any hub changes + TRACE_ERROR(("OHCIRootHub::ProcessTransfer(): set feature: no hub changes\n")); + break; + } + + TRACE(("OHCIRootHub::ProcessTransfer(): set feature: %d\n", request->Value)); + if (ohci->SetPortFeature(request->Index, request->Value) >= B_OK) + status = B_USB_STATUS_SUCCESS; + break; + } + } + + t->Finished(status, actualLength); + delete t; return B_OK; -} +} -void OHCIRootHub::UpdatePortStatus(void) -{ - // nothing yet -}