diff --git a/src/add-ons/kernel/busses/usb/ohci.cpp b/src/add-ons/kernel/busses/usb/ohci.cpp index 0c7d3d7d21..73f4b4d2b9 100644 --- a/src/add-ons/kernel/busses/usb/ohci.cpp +++ b/src/add-ons/kernel/busses/usb/ohci.cpp @@ -88,23 +88,22 @@ ohci_std_ops( int32 op , ... ) //------------------------------------------------------------------------ static status_t -ohci_add_to( Stack *stack ) +ohci_add_to(Stack *stack) { status_t status; pci_info *item; bool found = false; int i; - #ifdef OHCI_DEBUG - set_dprintf_enabled( true ); - load_driver_symbols( "ohci" ); - #endif +#ifdef TRACE_USB + set_dprintf_enabled(true); + load_driver_symbols("ohci"); +#endif // // Try if the PCI module is loaded // - if( ( status = get_module( B_PCI_MODULE_NAME, (module_info **)&( OHCI::pci_module ) ) ) != B_OK) - { + if( ( status = get_module( B_PCI_MODULE_NAME, (module_info **)&( OHCI::pci_module ) ) ) != B_OK) { TRACE(("USB_ OHCI: init_hardware(): Get PCI module failed! %lu \n", status)); return status; } @@ -113,20 +112,17 @@ ohci_add_to( Stack *stack ) // 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 ) && - ( item->class_api == PCI_usb_ohci ) ) + 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 ) + && ( item->class_api == PCI_usb_ohci ) ) { - if ((item->u.h0.interrupt_line == 0) || (item->u.h0.interrupt_line == 0xFF)) - { + if ((item->u.h0.interrupt_line == 0) || (item->u.h0.interrupt_line == 0xFF)) { TRACE(("USB OHCI: init_hardware(): found with invalid IRQ - check IRQ assignement\n")); 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 ) - { + if ( bus->InitCheck() != B_OK ) { delete bus; break; } @@ -138,8 +134,7 @@ ohci_add_to( Stack *stack ) } } - if ( found == false ) - { + if ( found == false ) { TRACE(("USB OHCI: init hardware(): no devices found\n")); free( item ); put_module( B_PCI_MODULE_NAME ); @@ -156,7 +151,7 @@ ohci_add_to( Stack *stack ) host_controller_info ohci_module = { { - "busses/usb/ohci/jixt", + "busses/usb/ohci", NULL, // No flag like B_KEEP_LOADED : the usb module does that ohci_std_ops }, @@ -170,8 +165,7 @@ host_controller_info ohci_module = { // parameters: none //------------------------------------------------------------------------ -module_info *modules[] = -{ +module_info *modules[] = { (module_info *) &ohci_module, NULL }; @@ -184,59 +178,67 @@ module_info *modules[] = // - stack: pointer to a stack instance //------------------------------------------------------------------------ -OHCI::OHCI( pci_info *info , Stack *stack ) - : BusManager(stack) +OHCI::OHCI(pci_info *info, Stack *stack) + : BusManager(stack), + fRegisterArea(-1), + fHccaArea(-1), + fDummyControl(0), + fDummyBulk(0), + fDummyIsochronous(0) { - m_pcii = info; - m_stack = stack; + fPcii = info; + fStack = stack; int i; - hcd_soft_endpoint *sed, *psed; TRACE(("USB OHCI: constructing new BusManager\n")); fInitOK = false; + + for(i = 0; i < OHCI_NO_EDS; i++) //Clear the interrupt list + fInterruptEndpoints[i] = 0; // enable busmaster and memory mapped access - uint16 cmd = OHCI::pci_module->read_pci_config(m_pcii->bus, m_pcii->device, m_pcii->function, PCI_command, 2); + uint16 cmd = OHCI::pci_module->read_pci_config(fPcii->bus, fPcii->device, fPcii->function, PCI_command, 2); cmd &= ~PCI_command_io; cmd |= PCI_command_master | PCI_command_memory; - OHCI::pci_module->write_pci_config(m_pcii->bus, m_pcii->device, m_pcii->function, PCI_command, 2, cmd ); + OHCI::pci_module->write_pci_config(fPcii->bus, fPcii->device, fPcii->function, PCI_command, 2, cmd ); + // // 5.1.1.2 map the registers + // addr_t registeroffset = pci_module->read_pci_config(info->bus, info->device, info->function, PCI_base_registers, 4); registeroffset &= PCI_address_memory_32_mask; TRACE(("OHCI: iospace offset: %lx\n" , registeroffset)); - m_register_area = map_physical_memory("OHCI base registers", (void *)registeroffset, - B_PAGE_SIZE, B_ANY_KERNEL_BLOCK_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA, (void **)&m_registers); - if (m_register_area < B_OK) { + fRegisterArea = map_physical_memory("OHCI base registers", (void *)registeroffset, + B_PAGE_SIZE, B_ANY_KERNEL_BLOCK_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA, (void **)&fRegisters); + if (fRegisterArea < B_OK) { TRACE(("USB OHCI: error mapping the registers\n")); return; } // Get the revision of the controller - uint32 rev = ReadReg(OHCI_REVISION) & 0xff; // Check the revision of the controller. The revision should be 10xh - TRACE((" OHCI: Version %ld.%ld%s\n", OHCI_REV_HI(rev), OHCI_REV_LO(rev),OHCI_REV_LEGACY(rev) ? ", legacy support" : "")); if (OHCI_REV_HI(rev) != 1 || OHCI_REV_LO(rev) != 0) { TRACE(("USB OHCI: Unsupported OHCI revision of the ohci device\n")); - delete_area(m_register_area); return; } // Set up the Host Controller Communications Area void *hcca_phy; - m_hcca_area = m_stack->AllocateArea((void**)&m_hcca, &hcca_phy, + fHccaArea = fStack->AllocateArea((void**)&fHcca, &hcca_phy, B_PAGE_SIZE, "OHCI HCCA"); - if (m_hcca_area < B_OK) { + if (fHccaArea < B_OK) { TRACE(("USB OHCI: Error allocating HCCA block\n")); - delete_area(m_register_area); return; } + memset((void*)fHcca, 0, sizeof(ohci_hcca)); + // // 5.1.1.3 Take control of the host controller + // if (ReadReg(OHCI_CONTROL) & OHCI_IR) { TRACE(("USB OHCI: SMM is in control of the host controller\n")); WriteReg(OHCI_COMMAND_STATUS, OHCI_OCR); @@ -252,44 +254,114 @@ OHCI::OHCI( pci_info *info , Stack *stack ) } } else if (ReadReg(OHCI_CONTROL) & OHCI_HCFS_RESET) //Only if no BIOS/SMM control snooze(USB_DELAY_BUS_RESET); - + + // // 5.1.1.4 Set Up Host controller - // initialize the data structures for the HCCA + // + // Dummy endpoints + fDummyControl = AllocateEndpoint(); + if (!fDummyControl) + return; + fDummyBulk = AllocateEndpoint(); + if (!fDummyBulk) + return; + fDummyIsochronous = AllocateEndpoint(); + if (!fDummyIsochronous) + return; + //Create the interrupt tree + //Algorhythm kindly borrowed from NetBSD code + for(i = 0; i < OHCI_NO_EDS; i++) { + fInterruptEndpoints[i] = AllocateEndpoint(); + if (!fInterruptEndpoints[i]) + return; + if (i != 0) + fInterruptEndpoints[i]->SetNext(fInterruptEndpoints[(i-1) / 2]); + else + fInterruptEndpoints[i]->SetNext(fDummyIsochronous); + } + for (i = 0; i < OHCI_NO_INTRS; i++) + fHcca->hcca_interrupt_table[revbits[i]] = + fInterruptEndpoints[OHCI_NO_EDS-OHCI_NO_INTRS+i]->physicaladdress; + + //Go to the hardware part of the initialisation + uint32 frameinterval = ReadReg(OHCI_FM_INTERVAL); + + WriteReg(OHCI_COMMAND_STATUS, OHCI_HCR); + for (i = 0; i < 10; i++){ + snooze(10); //Should be okay in one run: 10 microseconds for reset + if (!(ReadReg(OHCI_COMMAND_STATUS) & OHCI_HCR)) + break; + } + if (ReadReg(OHCI_COMMAND_STATUS) & OHCI_HCR) { + TRACE(("USB OHCI: Error resetting the host controller\n")); + return; + } + + WriteReg(OHCI_FM_INTERVAL, frameinterval); + //We now have 2 ms to finish the following sequence. + //TODO: maybe add spinlock protection??? + + WriteReg(OHCI_CONTROL_HEAD_ED, (uint32)fDummyControl->physicaladdress); + WriteReg(OHCI_BULK_HEAD_ED, (uint32)fDummyBulk->physicaladdress); + WriteReg(OHCI_HCCA, (uint32)hcca_phy); + + WriteReg(OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); + WriteReg(OHCI_INTERRUPT_ENABLE, OHCI_NORMAL_INTRS); + + // + // 5.1.1.5 Begin Sending SOFs + // + uint32 control = ReadReg(OHCI_CONTROL); + control &= ~(OHCI_CBSR_MASK | OHCI_LES | OHCI_HCFS_MASK | OHCI_IR); + control |= OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE | + OHCI_RATIO_1_4 | OHCI_HCFS_OPERATIONAL; + WriteReg(OHCI_CONTROL, control); + + //The controller is now operational, end of 2ms block. + uint32 interval = OHCI_GET_IVAL(frameinterval); + WriteReg(OHCI_PERIODIC_START, OHCI_PERIODIC(interval)); + - - uint32 framevalue = ReadReg(OHCI_FM_NUMBER); - - m_roothub_base = 255; //Invalidate the Root Hub address } -hcd_soft_endpoint * OHCI::ohci_alloc_soft_endpoint() +OHCI::~OHCI() { - hcd_soft_endpoint *sed; - int i; + if (fHccaArea > 0) + delete_area(fHccaArea); + if (fRegisterArea > 0) + delete_area(fRegisterArea); + if (fDummyControl) + FreeEndpoint(fDummyControl); + if (fDummyBulk) + FreeEndpoint(fDummyBulk); + if (fDummyIsochronous) + FreeEndpoint(fDummyIsochronous); + for (int i = 0; i < OHCI_NO_EDS; i++) + if (fInterruptEndpoints[i]) + FreeEndpoint(fInterruptEndpoints[i]); +} + +Endpoint * +OHCI::AllocateEndpoint() +{ + Endpoint *endpoint = new Endpoint; void *phy; - if (sc_freeeds == NULL) - { - dprintf("OHCI: ohci_alloc_soft_endpoint(): allocating chunk\n"); - for(i = 0; i < OHCI_SED_CHUNK; i++) - { - if(m_stack->AllocateChunk( (void **)&(sed) ,&phy , OHCI_ED_ALIGN )!= B_OK) - { - dprintf( "USB OHCI: ohci_alloc_soft_endpoint(): failed allocation of skeleton qh %i, aborting\n", i ); - return (0); - } - // chunk has been allocated - sed->physaddr = reinterpret_cast(phy); - sed->next = sc_freeeds; - sc_freeeds = sed; - } - } - sed = sc_freeeds; - sc_freeeds = sed->next; - memset(&sed->ed, 0, sizeof(hc_endpoint_descriptor)); - sed->next = 0; - return (sed); + if (fStack->AllocateChunk((void **)&endpoint->ed, &phy, sizeof(ohci_endpoint_descriptor)) != B_OK) + return 0; + endpoint->physicaladdress = (addr_t)phy; + + memset((void *)endpoint->ed, 0, sizeof(ohci_endpoint_descriptor)); + endpoint->ed->ed_flags = OHCI_ED_SKIP; + return endpoint; +} + +void +OHCI::FreeEndpoint(Endpoint *end) +{ + fStack->FreeChunk((void *)end->ed, (void *) end->physicaladdress, sizeof(ohci_endpoint_descriptor)); + delete end; } //------------------------------------------------------------------------ @@ -309,11 +381,11 @@ pci_module_info *OHCI::pci_module = 0; void OHCI::WriteReg(uint32 reg, uint32 value) { - *(volatile uint32 *)(m_registers + reg) = value; + *(volatile uint32 *)(fRegisters + reg) = value; } uint32 OHCI::ReadReg(uint32 reg) { - return *(volatile uint32 *)(m_registers + reg); + return *(volatile uint32 *)(fRegisters + reg); } diff --git a/src/add-ons/kernel/busses/usb/ohci.h b/src/add-ons/kernel/busses/usb/ohci.h index af5f5486c5..5f591294d3 100644 --- a/src/add-ons/kernel/busses/usb/ohci.h +++ b/src/add-ons/kernel/busses/usb/ohci.h @@ -34,6 +34,7 @@ struct pci_info; struct pci_module_info; class OHCIRootHub; +class Endpoint; // -------------------------------- // OHCI: The OHCI class derived @@ -45,8 +46,9 @@ class OHCI : public BusManager friend class OHCIRootHub; public: - OHCI( pci_info *info , Stack *stack ); - status_t SubmitTransfer( Transfer *t ); //Override from BusManager. + OHCI(pci_info *info, Stack *stack); + ~OHCI(); + status_t SubmitTransfer(Transfer *t); //Override from BusManager. static pci_module_info *pci_module; // Global data for the module. inline void WriteReg(uint32 reg, uint32 value); @@ -54,34 +56,24 @@ public: private: // Global - pci_info *m_pcii; // pci-info struct - Stack *m_stack; // Pointer to the stack - uint8 *m_registers; // Base address of the operational registers - area_id m_register_area; // Area id of the + pci_info *fPcii; // pci-info struct + Stack *fStack; // Pointer to the stack + uint8 *fRegisters; // Base address of the operational registers + area_id fRegisterArea; // Area id of the // HCCA - area_id m_hcca_area; - struct hc_hcca *m_hcca; // The HCCA structure for the interupt communication - hcd_soft_endpoint *sc_eds[OHCI_NO_EDS]; // number of interupt endpiont descriptors - // Registers - struct hcd_soft_endpoint *ed_bulk_tail; // last in the bulk list - struct hcd_soft_endpoint *ed_control_tail; // last in the control list - struct hcd_soft_endpoint *ed_isoch_tail; // lest in the isochrnonous list - struct hcd_soft_endpoint *ed_periodic[32]; // shadow of the interupt table - // free list structures - struct hcd_soft_endpoint *sc_freeeds; // list of free endpoint descriptors - struct hcd_soft_transfer *sc_freetds; // list of free general transfer descriptors - struct hcd_soft_itransfer *sc_freeitds; // list of free isonchronous transfer descriptors - // Done queue - struct hcd_soft_transfer *sc_sdone; // list of done general transfer descriptors - struct hcd_soft_itransfer *sc_sidone; // list of done isonchronous transefer descriptors - // memory management for queue data structures. - struct hcd_soft_transfer sc_hash_tds[OHCI_HASH_SIZE]; - struct hcd_soft_itransfer sc_hash_itds[OHCI_HASH_SIZE]; + area_id fHccaArea; + struct ohci_hcca *fHcca; // The HCCA structure for the interupt communication + Endpoint *fInterruptEndpoints[OHCI_NO_EDS]; // The interrupt endpoint list + // Dummy endpoints + Endpoint *fDummyControl; + Endpoint *fDummyBulk; + Endpoint *fDummyIsochronous; // Root Hub - OHCIRootHub *m_roothub; // the root hub - addr_t m_roothub_base; // Base address of the root hub + OHCIRootHub *m_roothub; // the root hub + addr_t m_roothub_base; // Base address of the root hub // functions - hcd_soft_endpoint *ohci_alloc_soft_endpoint(); // allocate memory for an endpoint + Endpoint *AllocateEndpoint(); // allocate memory for an endpoint + void FreeEndpoint(Endpoint *end); //Free endpoint }; // -------------------------------- @@ -101,6 +93,23 @@ class OHCIRootHub : public Hub OHCI *m_ohci; // needed because of internal data }; +// +// Endpoint: wrapper around the hardware endpoints +// + +struct Endpoint +{ + addr_t physicaladdress;//Point to the physical address + ohci_endpoint_descriptor *ed; //Logical 'endpoint' + Endpoint *next; //Pointer to the 'next' endpoint + + //Utility functions + void SetNext(Endpoint *end) { + next = end; + ed->next_endpoint = end->physicaladdress; + }; +}; + #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 51db2d9567..dd22468233 100644 --- a/src/add-ons/kernel/busses/usb/ohci_hardware.h +++ b/src/add-ons/kernel/busses/usb/ohci_hardware.h @@ -263,7 +263,7 @@ typedef uint32 ohci_physaddr_t; #define OHCI_NO_INTRS 32 -struct hc_hcca +struct ohci_hcca { addr_t hcca_interrupt_table[OHCI_NO_INTRS]; uint32 hcca_frame_number; @@ -282,12 +282,12 @@ struct hc_hcca // Endpoint descriptor structure (section 4.2) // -------------------------------- -typedef struct hc_endpoint_descriptor +typedef struct ohci_endpoint_descriptor { uint32 ed_flags; - addr_t ed_qtailp; // Queue tail pointer - addr_t ed_qheadp; // Queue head pointer - addr_t ed_nexted; // Next endpoint in the list + addr_t ed_qtailp; // Queue tail pointer + addr_t ed_qheadp; // Queue head pointer + addr_t next_endpoint; // Next endpoint in the list }; #define OHCI_ED_GET_FA(s) ((s) & 0x7f) diff --git a/src/add-ons/kernel/busses/usb/ohci_software.h b/src/add-ons/kernel/busses/usb/ohci_software.h index 4fcf571e0b..fb8dfdda03 100644 --- a/src/add-ons/kernel/busses/usb/ohci_software.h +++ b/src/add-ons/kernel/busses/usb/ohci_software.h @@ -31,19 +31,6 @@ #include "usb_p.h" #include "ohci_hardware.h" -// ------------------------------------ -// OHCI:: Software endpoint descriptor -// ------------------------------------ - -typedef struct hcd_soft_endpoint -{ - addr_t physaddr; // physical address of the host controller endpoint - hc_endpoint_descriptor ed; // host controller endpoint descriptor - struct hcd_soft_endpoint *next; // next software endpoint descriptor -}hcd_soft_endpoint; - -#define OHCI_SED_SIZE ((sizeof (struct hcd_soft_endpoint) + OHCI_ED_ALIGN - 1) / OHCI_ED_ALIGN * OHCI_ED_ALIGN) -#define OHCI_SED_CHUNK 128 // ------------------------------------ // OHCI:: Software general @@ -99,51 +86,4 @@ typedef struct hcd_soft_itransfer #define OHCI_HASH_SIZE 128 -// ------------------------------ -// OHCI:: OHCI device structure -// ------------------------------ -/* -typedef struct -{ - usb_dma_t sc_hccadma; - struct ohci_hcca *sc_hcca; - ohci_soft_endpoint *sc_eds[OHCI_NO_EDS]; - int sc_bws[OHCI_NO_INTRS]; - - uint32 sc_eintrs; - ohci_soft_endpoint *sc_isoc_head; - ohci_soft_endpoint *sc_ctrl_head; - ohci_soft_endpoint *sc_bulk_head; - - LIST_HEAD(, ohci_soft_transfer) sc_hash_tds[OHCI_HASH_SIZE]; - LIST_HEAD(, ohci_soft_itransfer) sc_hash_itds[OHCI_HASH_SIZE]; - - int sc_noport; - uint8 sc_addr; // device address - uint8 sc_conf; // device configuration - -#ifdef USB_USE_SOFTINTR - char sc_softwake; -#endif // USB_USE_SOFTINTR - - ohci_soft_endpoint *sc_freeeds; - ohci_soft_transfer *sc_freetds; - ohci_soft_itransfer *sc_freeitds; - - usbd_xfer_handle sc_intrxfer; - - ohci_soft_itransfer *sc_sidone; - ohci_soft_transfer *sc_sdone; - - char sc_vendor[16]; - int sc_id_vendor; - - void *sc_powerhook; // cookie from power hook - void *sc_shutdownhook; // cookie from shutdown hook - - uint32 sc_control; // Preserved during suspend/standby - uint32 sc_intre; - -} ohci_soft; -*/ #endif // OHCI_SOFT