From 12b05115348a8efc0d8dcf4332dcd38f4eefe5fd Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Sat, 27 May 2006 13:07:11 +0000 Subject: [PATCH] Picking up work on the USB stack. First of all adapting the style of the UHCI driver. Not yet working any more than until now. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@17607 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/busses/usb/uhci.cpp | 965 +++++++++--------- src/add-ons/kernel/busses/usb/uhci.h | 167 ++- src/add-ons/kernel/busses/usb/uhci_hardware.h | 2 +- src/add-ons/kernel/busses/usb/uhci_rh.cpp | 415 ++++---- 4 files changed, 798 insertions(+), 751 deletions(-) diff --git a/src/add-ons/kernel/busses/usb/uhci.cpp b/src/add-ons/kernel/busses/usb/uhci.cpp index 5c4e7d8c2e..c2758db806 100644 --- a/src/add-ons/kernel/busses/usb/uhci.cpp +++ b/src/add-ons/kernel/busses/usb/uhci.cpp @@ -1,23 +1,10 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2004, Niels S. Reedijk -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. +/* + * Copyright 2004-2006, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Niels S. Reedijk + */ #include #include @@ -30,476 +17,544 @@ #include "usb_p.h" -/* ++++++++++ -This is the implementation of the UHCI controller for the OpenBeOS USB stack -++++++++++ */ +#define TRACE_UHCI +#ifdef TRACE_UHCI +#define TRACE(x) dprintf x +#else +#define TRACE(x) /* nothing */ +#endif + + +pci_module_info *UHCI::sPCIModule = NULL; + static int32 -uhci_std_ops( int32 op , ... ) +uhci_std_ops(int32 op, ...) { - switch (op) - { + switch (op) { case B_MODULE_INIT: - TRACE( "uhci_module: init the module\n" ); + TRACE(("usb_uhci_module: init module\n")); return B_OK; case B_MODULE_UNINIT: - TRACE( "uhci_module: uninit the module\n" ); + TRACE(("usb_uhci_module: uninit module\n")); break; default: return EINVAL; } + return B_OK; } -static bool -uhci_add_to( Stack &stack ) -{ - status_t status; - pci_info *item; - bool found = false; - int i; - - #ifdef UHCI_DEBUG - set_dprintf_enabled( true ); - load_driver_symbols( "uhci" ); - #endif - - // Try if the PCI module is loaded (it would be weird if it wouldn't, but alas) - if( ( status = get_module( B_PCI_MODULE_NAME, (module_info **)&( UHCI::pci_module ) ) ) != B_OK) + +host_controller_info uhci_module = { { - TRACE( "USB_ UHCI: init_hardware(): Get PCI module failed! %lu \n", status); + "busses/usb/uhci/nielx", + NULL, + uhci_std_ops + }, + NULL, + UHCI::AddTo +}; + + +module_info *modules[] = { + (module_info *)&uhci_module, + NULL +}; + + +// +// #pragma mark - +// + + +UHCI::UHCI(pci_info *info, Stack *stack) + : BusManager(), + fPCIInfo(info), + fStack(stack) +{ + TRACE(("usb_uhci: constructing new UHCI BusManager\n")); + + fRegisterBase = sPCIModule->read_pci_config(fPCIInfo->bus, + fPCIInfo->device, fPCIInfo->function, PCI_memory_base, 4); + fRegisterBase &= PCI_address_io_mask; + TRACE(("usb_uhci: iospace offset: 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 gain controll of the UHCI controller instead of the BIOS + sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device, 2, + PCI_LEGSUP, 2, PCI_LEGSUP_USBPIRQDEN); + + // disable interrupts + WriteReg16(UHCI_USBINTR, 0); + + // do a global and host reset + GlobalReset(); + if (Reset() < B_OK) { + TRACE(("usb_uhci: host failed to reset\n")); + m_initok = false; + return; + } + + // Setup the frame list + void *physicalAddress; + fFrameArea = fStack->AllocateArea((void **)&fFrameList[0], + (void **)&fPhysicalFrameList, 4096, "USB UHCI framelist"); + + if (fFrameArea < B_OK) { + TRACE(("usb_uhci: unable to create an area for the frame pointer list\n")); + m_initok = false; + return; + } + + // Set base pointer and reset frame number + WriteReg32(UHCI_FRBASEADD, (uint32)fPhysicalFrameList); + WriteReg16(UHCI_FRNUM, 0); + + /* + According to the *BSD USB sources, there needs to be a stray transfer + descriptor in order to get some chipset to work nicely (like the PIIX). + */ + uhci_td *strayDescriptor; + if (fStack->AllocateChunk((void **)&strayDescriptor, &physicalAddress, 32) != B_OK) { + TRACE(("usb_uhci: failed to allocate a stray transfer descriptor\n")); + delete_area(fFrameArea); + m_initok = false; + return; + } + + strayDescriptor->status = 0; + strayDescriptor->this_phy = (addr_t)physicalAddress; + strayDescriptor->link_phy = TD_TERMINATE; + strayDescriptor->link_log = 0; + strayDescriptor->buffer_phy = 0; + strayDescriptor->buffer_log = 0; + strayDescriptor->token = TD_TOKEN_NULL | (0x7f << TD_TOKEN_DEVADDR_SHIFT) + | 0x69; + + /* + Setup the virtual structure. I stole this idea from the linux usb stack, + the idea is that for every interrupt interval there is a queue head. + These things all link together and eventually point to the control and + bulk virtual queue heads. + */ + for (int32 i = 0; i < 12; i++) { + // must be aligned on 16-byte boundaries + if (fStack->AllocateChunk((void **)&fVirtualQueueHead[i], + &physicalAddress, 32) != B_OK) { + TRACE(("usb_uhci: failed allocation of skeleton queue head %i, aborting\n", i)); + delete_area(fFrameArea); + m_initok = false; + return; + } + + fVirtualQueueHead[i]->this_phy = (addr_t)physicalAddress; + fVirtualQueueHead[i]->element_phy = QH_TERMINATE; + fVirtualQueueHead[i]->element_log = 0; + + if (i == 0) + continue; + + // link this queue head to the previous queue head + fVirtualQueueHead[i - 1]->link_phy = fVirtualQueueHead[i]->this_phy | QH_NEXT_IS_QH; + fVirtualQueueHead[i - 1]->link_log = fVirtualQueueHead[i]; + } + + // Make sure the fQueueHeadTerminate terminates + fVirtualQueueHead[11]->link_phy = strayDescriptor->this_phy; + fVirtualQueueHead[11]->link_log = strayDescriptor; + + // Insert the queues in the frame list. The linux developers mentioned + // in a comment that they used some magic to distribute the elements all + // over the place, but I don't really think that it is useful right now + // (nor do I know how I should do that), instead, I just take the frame + // number and determine where it should begin + + // NOTE, in c++ this is butt-ugly. We have a addr_t *array (because with + // an addr_t *array we can apply pointer arithmetic), uhci_qh *pointers + // that need to be put through the logical | to make sure the pointer is + // invalid for the hc. The result of that needs to be converted into a + // addr_t. Get it? + for (int32 i = 0; i < 1024; i++) { + int32 frame = i + 1; + if (frame % 256 == 0) + fFrameList[i] = fQueueHeadInterrupt256->this_phy | FRAMELIST_NEXT_IS_QH; + else if (frame % 128 == 0) + fFrameList[i] = fQueueHeadInterrupt128->this_phy | FRAMELIST_NEXT_IS_QH; + else if (frame % 64 == 0) + fFrameList[i] = fQueueHeadInterrupt64->this_phy | FRAMELIST_NEXT_IS_QH; + else if (frame % 32 == 0) + fFrameList[i] = fQueueHeadInterrupt32->this_phy | FRAMELIST_NEXT_IS_QH; + else if (frame % 16 == 0) + fFrameList[i] = fQueueHeadInterrupt16->this_phy | FRAMELIST_NEXT_IS_QH; + else if (frame % 8 == 0) + fFrameList[i] = fQueueHeadInterrupt8->this_phy | FRAMELIST_NEXT_IS_QH; + else if (frame % 4 == 0) + fFrameList[i] = fQueueHeadInterrupt4->this_phy | FRAMELIST_NEXT_IS_QH; + else if (frame % 2 == 0) + fFrameList[i] = fQueueHeadInterrupt2->this_phy | FRAMELIST_NEXT_IS_QH; + else + fFrameList[i] = fQueueHeadInterrupt1->this_phy | FRAMELIST_NEXT_IS_QH; + } + + // Set up the root hub + fRootHubAddress = AllocateAddress(); + fRootHub = new UHCIRootHub(this, fRootHubAddress); + SetRootHub(fRootHub); + + // Install the interrupt handler + install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line, + InterruptHandler, (void *)this, 0); + + // Acknowledge any possible pending interrupts + WriteReg16(UHCI_USBSTS, 0xffff); +} + + +status_t +UHCI::Start() +{ + //Start the host controller, then start the Busmanager + TRACE(("usb_uhci: usbcmd reg %u, usbsts reg %u\n", ReadReg16(UHCI_USBCMD), + ReadReg16(UHCI_USBSTS))); + + WriteReg16(UHCI_USBCMD, ReadReg16(UHCI_USBCMD) | UHCI_USBCMD_RS); + + bool running = false; + for (int32 i = 0; i < 10; i++) { + uint16 status = ReadReg16(UHCI_USBSTS); + TRACE(("usb_uhci: current loop %u, status %u\n", i, status)); + + if (status & UHCI_USBSTS_HCHALT) + snooze(1000); + else { + running = true; + break; + } + } + + if (!running) { + TRACE(("usb_uhci: controller won't start running\n")); + return B_ERROR; + } + + // Enable interrupts + WriteReg16(UHCI_USBINTR, UHCI_USBINTR_CRC | UHCI_USBINTR_RESUME + | UHCI_USBINTR_IOC | UHCI_USBINTR_SHORT); + + TRACE(("usb_uhci: controller is started. status: %u curframe: %u\n", + ReadReg16(UHCI_USBSTS), ReadReg16(UHCI_FRNUM))); + return BusManager::Start(); +} + + +status_t +UHCI::SubmitTransfer(Transfer *transfer) +{ + TRACE(("usb_uhci: submit packet called\n")); + + // Short circuit the root hub + if (transfer->GetPipe()->GetDeviceAddress() == fRootHubAddress) + return fRootHub->SubmitTransfer(transfer); + + if (transfer->GetPipe()->GetType() == Pipe::Control) + return InsertControl(transfer); + + return B_ERROR; +} + + +void +UHCI::GlobalReset() +{ + WriteReg16(UHCI_USBCMD, UHCI_USBCMD_GRESET); + snooze(100000); + WriteReg16(UHCI_USBCMD, 0); + snooze(10000); +} + + +status_t +UHCI::Reset() +{ + WriteReg16(UHCI_USBCMD, UHCI_USBCMD_HCRESET); + + int32 tries = 5; + while (ReadReg16(UHCI_USBCMD) & UHCI_USBCMD_HCRESET) { + snooze(10000); + if (tries-- < 0) + return B_ERROR; + } + + return B_OK; +} + + +int32 +UHCI::InterruptHandler(void *data) +{ + cpu_status status = disable_interrupts(); + spinlock lock = 0; + acquire_spinlock(&lock); + + int32 result = ((UHCI *)data)->Interrupt(); + + release_spinlock(&lock); + restore_interrupts(status); + return result; +} + + +int32 +UHCI::Interrupt() +{ + TRACE(("usb_uhci: Interrupt()\n")); + uint16 status = ReadReg16(UHCI_USBSTS); + uint16 acknowledge = 0; + TRACE(("usb_uhci: status: 0x%04x\n", status)); + + // Check if we really had an interrupt + if (status & UHCI_INTERRUPT_MASK == 0) + return B_UNHANDLED_INTERRUPT; + + if (status & UHCI_USBSTS_USBINT) { + TRACE(("usb_uhci: transfer finished\n")); + acknowledge |= UHCI_USBSTS_USBINT; + } + + if (status & UHCI_USBSTS_ERRINT) { + TRACE(("usb_uhci: transfer error\n")); + acknowledge |= UHCI_USBSTS_ERRINT; + } + + if (status & UHCI_USBSTS_RESDET) { + TRACE(("usb_uhci: resume detected\n")); + acknowledge |= UHCI_USBSTS_RESDET; + } + + if (status & UHCI_USBSTS_HOSTERR) { + TRACE(("usb_uhci: host system error\n")); + acknowledge |= UHCI_USBSTS_HOSTERR; + } + + if (status & UHCI_USBSTS_HCPRERR) { + TRACE(("usb_uhci: process error\n")); + acknowledge |= UHCI_USBSTS_HCPRERR; + } + + if (status & UHCI_USBSTS_HCHALT) { + TRACE(("usb_uhci: host controller halted\n")); + // acknowledge not needed + } + + WriteReg16(UHCI_USBSTS, acknowledge); + return B_HANDLED_INTERRUPT; +} + + +status_t +UHCI::InsertControl(Transfer *transfer) +{ + TRACE(("usb_uhci: InsertControl() frnum %u, usbsts reg %u\n", + ReadReg16(UHCI_FRNUM), ReadReg16(UHCI_USBSTS))); + + // HACK: this one is to prevent rogue transfers from happening + if (!transfer->GetBuffer()) + return B_ERROR; + + // Please note that any data structures must be aligned on a 16 byte boundary + // Also, due to the strange ways of C++' void* handling, this code is much messier + // than it actually should be. Forgive me. Or blame the compiler. + // First, set up a Queue Head for the transfer + uhci_qh *topQueueHead; + void *physicalAddress; + if (fStack->AllocateChunk((void **)&topQueueHead, &physicalAddress, 32) < B_OK) { + TRACE(("usb_uhci: failed to allocate a queue head\n")); + return ENOMEM; + } + + topQueueHead->link_phy = QH_TERMINATE; + topQueueHead->link_log = 0; + topQueueHead->this_phy = (addr_t)physicalAddress; + + // Allocate the transfer descriptor for the transfer + uhci_td *transferDescriptor; + if (fStack->AllocateChunk((void **)&transferDescriptor, &physicalAddress, 32) < B_OK) { + TRACE(("usb_uhci: failed to allocate the transfer descriptor\n")); + fStack->FreeChunk(topQueueHead, (void *)topQueueHead->this_phy, 32); + return ENOMEM; + } + + transferDescriptor->this_phy = (addr_t)physicalAddress; + transferDescriptor->status = TD_STATUS_ACTIVE; + if (transfer->GetPipe()->GetSpeed() == Pipe::LowSpeed) + transferDescriptor->status |= TD_STATUS_LOWSPEED; + + transferDescriptor->token = ((sizeof(usb_request_data) - 1) << 21) + | (transfer->GetPipe()->GetEndpointAddress() << 15) + | (transfer->GetPipe()->GetDeviceAddress() << 8) | 0x2D; + + // Create a physical space for the setup request + if (fStack->AllocateChunk(&transferDescriptor->buffer_log, + &transferDescriptor->buffer_phy, sizeof(usb_request_data)) < B_OK) { + TRACE(("usb_uhci: unable to allocate space for the setup buffer\n")); + fStack->FreeChunk(topQueueHead, (void *)topQueueHead->this_phy, 32); + fStack->FreeChunk(transferDescriptor, (void *)transferDescriptor->this_phy, 32); + return ENOMEM; + } + + memcpy(transferDescriptor->buffer_log, transfer->GetRequestData(), sizeof(usb_request_data)); + + // Link this to the queue head + topQueueHead->element_phy = transferDescriptor->this_phy; + topQueueHead->element_log = transferDescriptor; + + // TODO: split the buffer into max transfer sizes + + // Finally, create a status transfer descriptor + uhci_td *statusDescriptor; + if (fStack->AllocateChunk((void **)&statusDescriptor, &physicalAddress, 32) < B_OK) { + TRACE(("usb_uhci: failed to allocate the status descriptor\n")); + fStack->FreeChunk(transferDescriptor->buffer_log, (void *)transferDescriptor->buffer_phy, sizeof(usb_request_data)); + fStack->FreeChunk(transferDescriptor, (void *)transferDescriptor->this_phy, 32); + fStack->FreeChunk(topQueueHead, (void *)topQueueHead->this_phy, 32); + return ENOMEM; + } + + statusDescriptor->this_phy = (addr_t)physicalAddress; + statusDescriptor->status = TD_STATUS_IOC; + if (transfer->GetPipe()->GetSpeed() == Pipe::LowSpeed) + statusDescriptor->status |= TD_STATUS_LOWSPEED; + + statusDescriptor->token = TD_TOKEN_NULL | TD_TOKEN_DATA1 + | (transfer->GetPipe()->GetEndpointAddress() << 15) + | (transfer->GetPipe()->GetDeviceAddress() << 8) | 0x69; + + // Invalidate the buffer field + statusDescriptor->buffer_phy = statusDescriptor->buffer_log = 0; + + // Link to the previous transfer descriptor + transferDescriptor->link_phy = statusDescriptor->this_phy | TD_DEPTH_FIRST; + transferDescriptor->link_log = statusDescriptor; + + // This is the end of this chain, so don't link to any next QH/TD + statusDescriptor->link_phy = QH_TERMINATE; + statusDescriptor->link_log = 0; + + // First, add the transfer to the list of transfers + transfer->SetHostPrivate(new hostcontroller_priv); + transfer->GetHostPrivate()->topqh = topQueueHead; + transfer->GetHostPrivate()->firsttd = transferDescriptor; + transfer->GetHostPrivate()->lasttd = statusDescriptor; + fTransfers.PushBack(transfer); + + // Secondly, append the qh to the control list + if (fQueueHeadControl->element_phy & QH_TERMINATE) { + // the control queue is empty, make this the first element + fQueueHeadControl->element_phy = topQueueHead->this_phy; + fQueueHeadControl->link_log = (void *)topQueueHead; + TRACE(("usb_uhci: first transfer in queue\n")); + } else { + // there are control transfers linked, append to the queue + uhci_qh *queueHead = (uhci_qh *)fQueueHeadControl->link_log; + while (queueHead->link_phy & QH_TERMINATE == 0) + queueHead = (uhci_qh *)queueHead->link_log; + + queueHead->link_phy = topQueueHead->this_phy; + queueHead->link_log = (void *)topQueueHead; + TRACE(("usb_uhci: appended transfer to queue\n")); + } + + return EINPROGRESS; +} + + +bool +UHCI::AddTo(Stack &stack) +{ +#ifdef TRACE_UHCI + set_dprintf_enabled(true); + load_driver_symbols("uhci"); +#endif + + status_t status = get_module(B_PCI_MODULE_NAME, (module_info **)&sPCIModule); + if (status < B_OK) { + TRACE(("usb_uhci: AddTo(): getting pci module failed! 0x%08x\n", + status)); return status; } - - TRACE( "usb_uhci init_hardware(): Setting up hardware\n" ); - + + TRACE(("usb_uhci: AddTo(): setting up hardware\n")); + // TODO: in the future we might want to support multiple host controllers. - item = new pci_info; - for ( i = 0 ; UHCI::pci_module->get_nth_pci_info( i , item ) == B_OK ; i++ ) - { + bool found = false; + pci_info *item = new pci_info; + for (int32 i = 0; sPCIModule->get_nth_pci_info(i, item) >= B_OK; i++) { //class_base = 0C (serial bus) class_sub = 03 (usb) prog_int: 00 (UHCI) - if ( ( item->class_base == 0x0C ) && ( item->class_sub == 0x03 ) && - ( item->class_api == 0x00 ) ) - { - if ((item->u.h0.interrupt_line == 0) || (item->u.h0.interrupt_line == 0xFF)) - { - TRACE( "USB UHCI: init_hardware(): found with invalid IRQ - check IRQ assignement\n"); + if (item->class_base == 0x0C && item->class_sub == 0x03 + && item->class_api == 0x00) { + if (item->u.h0.interrupt_line == 0 + || item->u.h0.interrupt_line == 0xFF) { + TRACE(("usb_uhci: AddTo(): found with invalid IRQ - check IRQ assignement\n")); continue; } - TRACE("USB UHCI: init_hardware(): found at IRQ %u \n", item->u.h0.interrupt_line); - UHCI *bus = new UHCI( item , &stack ); - if ( bus->InitCheck() != B_OK ) - { - TRACE( "USB UHCI::InitCheck() failed, error %li\n" , bus->InitCheck() ); + + TRACE(("usb_uhci: AddTo(): found at IRQ %u\n", item->u.h0.interrupt_line)); + UHCI *bus = new UHCI(item, &stack); + if (bus->InitCheck() < B_OK) { + TRACE(("usb_uhci: AddTo(): InitCheck() failed 0x%08x\n", bus->InitCheck())); delete bus; - break; + continue; } - - stack.AddBusManager( bus ); + + stack.AddBusManager(bus); bus->Start(); found = true; break; } } - - if ( found == false ) - { - TRACE( "USB UHCI: init hardware(): no devices found\n" ); - free( item ); - put_module( B_PCI_MODULE_NAME ); + + if (!found) { + TRACE(("usb_uhci: AddTo(): no devices found\n")); + delete item; + put_module(B_PCI_MODULE_NAME); return ENODEV; } - return B_OK; //Hardware found -} - - - -host_controller_info uhci_module = { - { - "busses/usb/uhci/nielx", - NULL, // No flag like B_KEEP_LOADED : the usb module does that - uhci_std_ops - }, - NULL , - uhci_add_to -}; - -module_info *modules[] = -{ - (module_info *) &uhci_module, - NULL -}; - -/* ++++++++++ -This is the implementation of the UHCI controller for the OpenBeOS USB stack -++++++++++ */ - -int32 uhci_interrupt_handler( void *data ) -{ - int32 retval; - spinlock slock = 0; - cpu_status status = disable_interrupts(); - acquire_spinlock( &slock ); - retval = ((UHCI*)data)->Interrupt(); - release_spinlock( &slock ); - restore_interrupts( status ); - return retval; -} - -UHCI::UHCI( pci_info *info , Stack *stack ) -{ - //Do nothing yet - dprintf( "UHCI: constructing new BusManager\n" ); - m_pcii = info; - m_stack = stack; - m_reg_base = UHCI::pci_module->read_pci_config(m_pcii->bus, m_pcii->device, m_pcii->function, PCI_memory_base, 4); - m_reg_base &= PCI_address_io_mask; - TRACE( "USB UHCI: iospace offset: %lx\n" , m_reg_base ); - m_rh_address = 255; //Invalidate the RH address - { - /* enable pci address access */ - uint16 cmd; - cmd = UHCI::pci_module->read_pci_config(m_pcii->bus, m_pcii->device, m_pcii->function, PCI_command, 2); - cmd = cmd | PCI_command_io | PCI_command_master | PCI_command_memory; - UHCI::pci_module->write_pci_config(m_pcii->bus, m_pcii->device, m_pcii->function, PCI_command, 2, cmd ); - /* make sure we gain controll of the UHCI controller instead of the BIOS - function 2 */ - UHCI::pci_module->write_pci_config(m_pcii->bus, m_pcii->device, 2, PCI_LEGSUP, 2, PCI_LEGSUP_USBPIRQDEN ); - } - - //Do a host reset - GlobalReset(); - if ( Reset() != B_OK ) - { - TRACE( "USB UHCI: init_hardare(): host failed to reset\n" ); - m_initok = false; - return; - } - - // Poll the status of the two ports -// rh_update_port_status(); -// TRACE( "USB UHCI: init_hardware(): port1: %x port2: %x\n", -// m_data->port_status[0].status , m_data->port_status[1].status ); - - //Set up the frame list - void *phy; - m_framearea = stack->AllocateArea( (void **)&(m_framelist[0]) , &(phy) , - 4096 , "uhci framelist" ); - m_framelist_phy = reinterpret_cast(phy); - if ( m_framearea < B_OK ) - { - TRACE( "USB UHCI: init_hardware(): unable to create an area for the frame pointer list\n" ); - m_initok = false; - return; - } - - /* - According tot the *BSD usb sources, there needs to be a stray transfer - descriptor in order to get some chipset to work nicely (PIIX or something - like that). - */ - uhci_td *straytd; - if ( m_stack->AllocateChunk( (void **)&(straytd) , &phy , 32 ) != B_OK ) - { - dprintf( "USB UHCI::UHCI() Failed to allocate a stray transfer descriptor\n" ); - delete_area( m_framearea ); - m_initok = false; - return; - } - straytd->link_phy = TD_TERMINATE; - straytd->this_phy = reinterpret_cast(phy); - straytd->link_log = 0; - straytd->buffer_log = 0; - straytd->status = 0; - straytd->token = TD_TOKEN_NULL | 0x7f << TD_TOKEN_DEVADDR_SHIFT | 0x69; - straytd->buffer_phy = 0; - - /* - Set up the virtual structure. I stole this idea from the linux usb stack, - the idea is that for every interrupt interval there is a queue head. These - things all link together and eventually point to the control and bulk - virtual queue heads. - */ - - for( int i = 0 ; i < 12 ; i++ ) - { - void *phy; - //Must be aligned on 16-byte boundaries - if ( m_stack->AllocateChunk( (void **)&(m_qh_virtual[i]) , - &phy , 32 ) != B_OK ) - { - dprintf( "USB UHCI: init_hardware(): failed allocation of skeleton qh %i, aborting\n", i ); - delete_area( m_framearea ); - m_initok = false; - return; - } - //chunk allocated - m_qh_virtual[i]->this_phy = reinterpret_cast(phy); - m_qh_virtual[i]->element_phy = QH_TERMINATE; - m_qh_virtual[i]->element_log = 0; - - //Link this qh to its previous qh - if ( i != 0 ) - { - m_qh_virtual[i-1]->link_phy = m_qh_virtual[i]->this_phy | QH_NEXT_IS_QH ; - m_qh_virtual[i-1]->link_log = m_qh_virtual[i]; - } - } - // Make sure the qh_terminate terminates - m_qh_virtual[11]->link_phy = straytd->this_phy; - m_qh_virtual[11]->link_log = straytd; - - //Insert the queues in the frame list. The linux developers mentioned - // in a comment that they used some magic to distribute the elements all - // over the place, but I don't really think that it is useful right now - // (or do I know how I should do that), instead, I just take the frame - // number and determine where it should begin - - //NOTE, in c++ this is butt-ugly. We have a addr_t *array (because with - //an addr_t *array we can apply pointer arithmetic), uhci_qh *pointers - //that need to be put through the logical | to make sure the pointer is - //invalid for the hc. The result of that needs to be converted into a - //addr_t. Get it? - for( int i = 0 ; i < 1024 ; i++ ) - { - int frame = i+1; - if ( ( frame % 256 ) == 0 ) - m_framelist[i] = m_qh_interrupt_256->this_phy | FRAMELIST_NEXT_IS_QH; - else if ( ( frame % 128 ) == 0 ) - m_framelist[i] = m_qh_interrupt_128->this_phy | FRAMELIST_NEXT_IS_QH; - else if ( ( frame % 64 ) == 0 ) - m_framelist[i] = m_qh_interrupt_64->this_phy | FRAMELIST_NEXT_IS_QH; - else if ( ( frame % 32 ) == 0 ) - m_framelist[i] = m_qh_interrupt_32->this_phy | FRAMELIST_NEXT_IS_QH; - else if ( ( frame % 16 ) == 0 ) - m_framelist[i] = m_qh_interrupt_16->this_phy | FRAMELIST_NEXT_IS_QH; - else if ( ( frame % 8 ) == 0 ) - m_framelist[i] = m_qh_interrupt_8->this_phy | FRAMELIST_NEXT_IS_QH; - else if ( ( frame % 4 ) == 0 ) - m_framelist[i] = m_qh_interrupt_4->this_phy | FRAMELIST_NEXT_IS_QH; - else if ( ( frame % 2 ) == 0 ) - m_framelist[i] = m_qh_interrupt_2->this_phy | FRAMELIST_NEXT_IS_QH; - else - m_framelist[i] = m_qh_interrupt_1->this_phy | FRAMELIST_NEXT_IS_QH; - } - - //Set base pointer - UHCI::pci_module->write_io_32( m_reg_base + UHCI_FRBASEADD , (int32)(m_framelist_phy) ); - UHCI::pci_module->write_io_16( m_reg_base + UHCI_FRNUM , 0 ); - - //Set up the root hub - m_rh_address = AllocateAddress(); - m_rh = new UHCIRootHub( this , m_rh_address ); - SetRootHub( m_rh ); - - //Install the interrupt handler - install_io_interrupt_handler( m_pcii->u.h0.interrupt_line , uhci_interrupt_handler , (void *)this , 0 ); - UHCI::pci_module->write_io_16( m_reg_base + UHCI_USBSTS , 0xffff ); - UHCI::pci_module->write_io_16( m_reg_base + UHCI_USBINTR , UHCI_USBINTR_CRC | UHCI_USBINTR_RESUME | UHCI_USBINTR_IOC | UHCI_USBINTR_SHORT ); -} - -status_t UHCI::Start() -{ - //Start the host controller, then start the Busmanager - TRACE("USB UCHI::STart() usbcmd reg %u, usbsts reg %u\n" , UHCI::pci_module->read_io_16( m_reg_base + UHCI_USBCMD ) , UHCI::pci_module->read_io_16( m_reg_base + UHCI_USBSTS ) ); - UHCI::pci_module->write_io_16( m_reg_base + UHCI_USBCMD , UHCI_USBCMD_RS ); - - bool running = false; - uint16 status = 0; - for ( int i = 0 ; i <= 10 ; i++ ) - { - status = UHCI::pci_module->read_io_16( m_reg_base + UHCI_USBSTS ); - dprintf( "UHCI::Start() current loop %u, status %u\n" , i , status ); - if ( status & UHCI_USBSTS_HCHALT ) - snooze( 1000 ); - else - { - running = true; - break; - } - } - - if (!running) - { - TRACE( "UHCI::Start() Controller won't start running\n" ); - return B_ERROR; - } - - TRACE( "UHCI::Start() Controller is started. USBSTS: %u curframe: %u \n" , UHCI::pci_module->read_io_16( m_reg_base + UHCI_USBSTS ) , UHCI::pci_module->read_io_16( m_reg_base + UHCI_FRNUM ) ); - return BusManager::Start(); -} - -status_t UHCI::SubmitTransfer( Transfer *t ) -{ - dprintf( "UHCI::SubmitPacket( Transfer &t ) called!!!\n" ); - - //Short circuit the root hub - if ( m_rh_address == t->GetPipe()->GetDeviceAddress() ) - return m_rh->SubmitTransfer( t ); - - if ( t->GetPipe()->GetType() == Pipe::Control ) - return InsertControl( t ); - - return B_ERROR; -} - -void UHCI::GlobalReset() -{ - UHCI::pci_module->write_io_16( m_reg_base + UHCI_USBCMD , UHCI_USBCMD_GRESET ); - snooze( 100000 ); - UHCI::pci_module->write_io_16( m_reg_base + UHCI_USBCMD , 0 ); -} - -status_t UHCI::Reset() -{ - UHCI::pci_module->write_io_16( m_reg_base + UHCI_USBCMD , UHCI_USBCMD_HCRESET ); - snooze( 100000 ); - if ( UHCI::pci_module->read_io_16( m_reg_base + UHCI_USBCMD ) & UHCI_USBCMD_HCRESET ) - return B_ERROR; return B_OK; } -int32 UHCI::Interrupt() + +inline void +UHCI::WriteReg16(uint32 reg, uint16 value) { - uint16 status = UHCI::pci_module->read_io_16( m_reg_base + UHCI_USBSTS ); - TRACE( "USB UHCI::Interrupt()\n" ); - //Check if we really had an interrupt - if ( !( status | UHCI_INTERRUPT_MASK ) ) - return B_UNHANDLED_INTERRUPT; - - //Get funky - if ( status | UHCI_USBSTS_USBINT ) - { - //A transfer finished - TRACE( "USB UHCI::Interrupt() transfer finished! [party]\n" ); - } - else if ( status | UHCI_USBSTS_ERRINT ) - { - TRACE( "USB UHCI::Interrupt() transfer error! [cry]\n" ); - } - return B_HANDLED_INTERRUPT; + sPCIModule->write_io_16(fRegisterBase + reg, value); } -status_t UHCI::InsertControl( Transfer *t ) + +inline void +UHCI::WriteReg32(uint32 reg, uint32 value) { - TRACE("USB UCHI::InsertControl() frnum %u , usbsts reg %u\n" , UHCI::pci_module->read_io_16( m_reg_base + UHCI_FRNUM ), UHCI::pci_module->read_io_16( m_reg_base + UHCI_USBSTS ) ); - - //HACK: this one is to prevent rogue transfers from happening - if ( t->GetBuffer() != 0 ) - return B_ERROR; - - //Please note that any data structures must be aligned on a 16 byte boundary - //Also, due to the strange ways of C++' void* handling, this code is much messier - //than it actually should be. Forgive me. Or blame the compiler. - //First, set up a Queue Head for the transfer - uhci_qh *topqh; - void *topqh_phy; - if ( m_stack->AllocateChunk( (void **)&topqh , &topqh_phy , 32 ) < B_OK ) - { - TRACE( "UHCI::InsertControl(): Failed to allocate a QH\n" ); - return ENOMEM; - } - topqh->link_phy = QH_TERMINATE; - topqh->link_log = 0; - topqh->this_phy = (addr_t)topqh_phy; - - - //Allocate the transfer descriptor for the transfer - uhci_td *firsttd; - void *firsttd_phy; - if ( m_stack->AllocateChunk( (void**)&firsttd , &firsttd_phy , 32 ) < B_OK ) - { - TRACE( "UHCI::InsertControl(): Failed to allocate the first TD\n" ); - m_stack->FreeChunk( topqh , topqh_phy , 32 ); - return ENOMEM; - } - firsttd->this_phy = (addr_t)firsttd_phy; - //Set the 'status' field of the td - if ( t->GetPipe()->GetSpeed() == Pipe::LowSpeed ) - firsttd->status = TD_STATUS_LOWSPEED | TD_STATUS_ACTIVE; - else - firsttd->status = TD_STATUS_ACTIVE; - - //Set the 'token' field of the td - firsttd->token = ( ( sizeof(usb_request_data) - 1 ) << 21 ) | ( t->GetPipe()->GetEndpointAddress() << 15 ) - | ( t->GetPipe()->GetDeviceAddress() << 8 ) | ( 0x2D ); - //Create a physical space for the setup request - if ( m_stack->AllocateChunk( &(firsttd->buffer_log) , &(firsttd->buffer_phy) , sizeof (usb_request_data) ) ) - { - TRACE( "UHCI::InsertControl(): Unable to allocate space for the SETUP buffer\n" ); - m_stack->FreeChunk( topqh , topqh_phy , 32 ); - m_stack->FreeChunk( firsttd , firsttd_phy , 32 ); - return ENOMEM; - } - memcpy( t->GetRequestData() , firsttd->buffer_log , sizeof(usb_request_data) ); - - //Link this thing in the queue head - topqh->element_phy = (addr_t)firsttd_phy; - topqh->element_log = firsttd; - - - //TODO: split the buffer into max transfer sizes - - - //Finally, create a status td - uhci_td *statustd; - void *statustd_phy; - if ( m_stack->AllocateChunk( (void **)&statustd , &statustd_phy , 32 ) < B_OK ) - { - TRACE( "UHCI::InsertControl(): Failed to allocate the status TD\n" ); - return ENOMEM; - } - //Set the 'status' field of the td to interrupt on complete - if ( t->GetPipe()->GetSpeed() == Pipe::LowSpeed ) - statustd->status = TD_STATUS_LOWSPEED | TD_STATUS_IOC; - else - statustd->status = TD_STATUS_IOC; - - //Set the 'token' field of the td (always DATA1) and a null buffer - statustd->token = TD_TOKEN_NULL | TD_TOKEN_DATA1 | ( t->GetPipe()->GetEndpointAddress() << 15 ) - | ( t->GetPipe()->GetDeviceAddress() << 8 ) | 0x69 ; - - //Invalidate the buffer field - statustd->buffer_phy = statustd->buffer_log = 0; - - //Link into the previous transfer descriptor - firsttd->link_phy = (addr_t)statustd_phy | TD_DEPTH_FIRST; - firsttd->link_log = statustd; - - //This is the end of this chain, so don't link to any next QH/TD - statustd->link_phy = QH_TERMINATE; - statustd->link_log = 0; - - - //First, add the transfer to the list of transfers - t->SetHostPrivate( new hostcontroller_priv ); - t->GetHostPrivate()->topqh = topqh; - t->GetHostPrivate()->firsttd = firsttd; - t->GetHostPrivate()->lasttd = statustd; - m_transfers.PushBack( t ); - - //Secondly, append the qh to the control list - //a) if the control queue is empty, make this the first element - if ( ( m_qh_control->element_phy & QH_TERMINATE ) != 0 ) - { - m_qh_control->element_phy = topqh->this_phy; - m_qh_control->link_log = (void *)topqh; - TRACE( "USB UHCI::InsertControl() First transfer in QUeue\n" ); - } - //b) there are control transfers linked, append to the queue - else - { - uhci_qh *qh = (uhci_qh *)(m_qh_control->link_log); - while ( ( qh->link_phy & QH_TERMINATE ) == 0 ) - { - TRACE( "USB UHCI::InsertControl() Looping\n" ); - qh = (uhci_qh *)(qh->link_log); - } - qh->link_phy = topqh->this_phy; - qh->link_log = (void *)topqh; - TRACE( "USB UHCI::InsertControl() Appended transfers in queue\n" ); - } - return EINPROGRESS; + sPCIModule->write_io_32(fRegisterBase + reg, value); +} + + +inline uint16 +UHCI::ReadReg16(uint32 reg) +{ + return sPCIModule->read_io_16(fRegisterBase + reg); +} + + +inline uint32 +UHCI::ReadReg32(uint32 reg) +{ + return sPCIModule->read_io_32(fRegisterBase + reg); } - -pci_module_info *UHCI::pci_module = 0; diff --git a/src/add-ons/kernel/busses/usb/uhci.h b/src/add-ons/kernel/busses/usb/uhci.h index ef7cfe29d6..7bffdc7d69 100644 --- a/src/add-ons/kernel/busses/usb/uhci.h +++ b/src/add-ons/kernel/busses/usb/uhci.h @@ -1,23 +1,10 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2004, Niels S. Reedijk -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. +/* + * Copyright 2004-2006, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Niels S. Reedijk + */ #ifndef UHCI_H #define UHCI_H @@ -29,84 +16,88 @@ struct pci_info; struct pci_module_info; class UHCIRootHub; -class UHCI : public BusManager -{ - friend class UHCIRootHub; - friend int32 uhci_interrupt_handler( void *data ); -public: - UHCI( pci_info *info , Stack *stack ); - - //Override from BusManager - status_t Start(); - status_t SubmitTransfer( Transfer *t ); - - // Global data for the module. - static pci_module_info *pci_module; -private: - //Utility functions - void GlobalReset(); - status_t Reset(); - int32 Interrupt(); - - //Functions for the actual functioning of transfers - status_t InsertControl( Transfer *t ); - uint32 m_reg_base; //Base address of the registers - pci_info *m_pcii; //pci-info struct - Stack *m_stack; //Pointer to the stack - - //Frame list memory - area_id m_framearea; - addr_t m_framelist[1024]; //The frame list struct - addr_t m_framelist_phy; //The physical pointer to the frame list - - // Virtual frame - uhci_qh *m_qh_virtual[12]; // -#define m_qh_interrupt_256 m_qh_virtual[0] -#define m_qh_interrupt_128 m_qh_virtual[1] -#define m_qh_interrupt_64 m_qh_virtual[2] -#define m_qh_interrupt_32 m_qh_virtual[3] -#define m_qh_interrupt_16 m_qh_virtual[4] -#define m_qh_interrupt_8 m_qh_virtual[5] -#define m_qh_interrupt_4 m_qh_virtual[6] -#define m_qh_interrupt_2 m_qh_virtual[7] -#define m_qh_interrupt_1 m_qh_virtual[8] -#define m_qh_control m_qh_virtual[9] -#define m_qh_bulk m_qh_virtual[10] -#define m_qh_terminate m_qh_virtual[11] - - //Maintain a list of transfers - Vector m_transfers; - - //Root hub: - UHCIRootHub *m_rh; // the root hub - uint8 m_rh_address; // the address of the root hub +class UHCI : public BusManager { +public: + UHCI(pci_info *info, Stack *stack); + + status_t Start(); + status_t SubmitTransfer(Transfer *transfer); + +static bool AddTo(Stack &stack); + +private: +friend class UHCIRootHub; + + // Utility functions + void GlobalReset(); + status_t Reset(); +static int32 InterruptHandler(void *data); + int32 Interrupt(); + + // Register functions +inline void WriteReg16(uint32 reg, uint16 value); +inline void WriteReg32(uint32 reg, uint32 value); +inline uint16 ReadReg16(uint32 reg); +inline uint32 ReadReg32(uint32 reg); + + // Functions for the actual functioning of transfers + status_t InsertControl(Transfer *transfer); + +static pci_module_info *sPCIModule; + + uint32 fRegisterBase; + pci_info *fPCIInfo; + Stack *fStack; + + // Frame list memory + area_id fFrameArea; + addr_t fFrameList[1024]; + addr_t fPhysicalFrameList; + + // Virtual frame + uhci_qh *fVirtualQueueHead[12]; + +#define fQueueHeadInterrupt256 fVirtualQueueHead[0] +#define fQueueHeadInterrupt128 fVirtualQueueHead[1] +#define fQueueHeadInterrupt64 fVirtualQueueHead[2] +#define fQueueHeadInterrupt32 fVirtualQueueHead[3] +#define fQueueHeadInterrupt16 fVirtualQueueHead[4] +#define fQueueHeadInterrupt8 fVirtualQueueHead[5] +#define fQueueHeadInterrupt4 fVirtualQueueHead[6] +#define fQueueHeadInterrupt2 fVirtualQueueHead[7] +#define fQueueHeadInterrupt1 fVirtualQueueHead[8] +#define fQueueHeadControl fVirtualQueueHead[9] +#define fQueueHeadBulk fVirtualQueueHead[10] +#define fQueueHeadTerminate fVirtualQueueHead[11] + + // Maintain a list of transfers + Vector fTransfers; + + // Root hub + UHCIRootHub *fRootHub; + uint8 fRootHubAddress; }; -class UHCIRootHub : public Hub -{ + +class UHCIRootHub : public Hub { public: - UHCIRootHub( UHCI *uhci , int8 devicenum ); - status_t SubmitTransfer( Transfer *t ); - void UpdatePortStatus(); + UHCIRootHub(UHCI *uhci, int8 deviceNum); + + status_t SubmitTransfer(Transfer *transfer); + void UpdatePortStatus(); + private: - usb_port_status m_hw_port_status[2]; // the port status (maximum of two) - UHCI *m_uhci; // needed because of internal data + + usb_port_status fPortStatus[2]; + UHCI *fUHCI; }; -struct hostcontroller_priv -{ + +struct hostcontroller_priv { uhci_qh *topqh; uhci_td *firsttd; uhci_td *lasttd; }; -#define UHCI_DEBUG -#ifdef UHCI_DEBUG -#define TRACE dprintf -#else -#define TRACE silent -void silent( const char * , ... ) {} -#endif - #endif diff --git a/src/add-ons/kernel/busses/usb/uhci_hardware.h b/src/add-ons/kernel/busses/usb/uhci_hardware.h index 6930aa3bd1..62b07b903e 100644 --- a/src/add-ons/kernel/busses/usb/uhci_hardware.h +++ b/src/add-ons/kernel/busses/usb/uhci_hardware.h @@ -61,7 +61,7 @@ #define UHCI_USBSTS_HOSTERR 0x8 // Host System Error #define UHCI_USBSTS_HCPRERR 0x10// Host Controller Process error #define UHCI_USBSTS_HCHALT 0x20 // HCHalted -#define UHCI_INTERRUPT_MASK 0x1F //Mask for all the interrupts +#define UHCI_INTERRUPT_MASK 0x3F //Mask for all the interrupts //USBINTR #define UHCI_USBINTR_CRC 0x1 // Timeout/ CRC interrupt enable diff --git a/src/add-ons/kernel/busses/usb/uhci_rh.cpp b/src/add-ons/kernel/busses/usb/uhci_rh.cpp index dc992348bb..74c8189e22 100644 --- a/src/add-ons/kernel/busses/usb/uhci_rh.cpp +++ b/src/add-ons/kernel/busses/usb/uhci_rh.cpp @@ -1,267 +1,268 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2004, Niels S. Reedijk -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - +/* + * Copyright 2004-2006, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Niels S. Reedijk + */ #include "uhci.h" #include + +#define TRACE_UHCI_ROOT_HUB +#ifdef TRACE_UHCI_ROOT_HUB +#define TRACE(x) dprintf x +#else +#define TRACE(x) /* nothing */ +#endif + + usb_device_descriptor uhci_devd = { 0x12, //Descriptor size - USB_DESCRIPTOR_DEVICE , //Type of descriptor + USB_DESCRIPTOR_DEVICE, //Type of descriptor 0x110, //USB 1.1 - 0x09 , //Hub type - 0 , //Subclass - 0 , //Protocol - 64 , //Max packet size - 0 , //Vendor - 0 , //Product - 0x110 , //Version - 1 , 2 , 0 , //Other data + 0x09, //Hub type + 0, //Subclass + 0, //Protocol + 64, //Max packet size + 0, //Vendor + 0, //Product + 0x110, //Version + 1, 2, 0, //Other data 1 //Number of configurations }; + usb_configuration_descriptor uhci_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 + 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) }; + usb_interface_descriptor uhci_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 + 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 }; + usb_endpoint_descriptor uhci_endd = { - 0x07 , //Size + 0x07, //Size USB_DESCRIPTOR_ENDPOINT, USB_REQTYPE_DEVICE_IN | 1, //1 from freebsd driver - 0x3 , // Interrupt - 8 , // Max packet size + 0x3, // Interrupt + 8, // Max packet size 0xFF // Interval 256 }; + usb_hub_descriptor uhci_hubd = { - 0x09 , //Including deprecated powerctrlmask + 0x09, //Including deprecated powerctrlmask USB_DESCRIPTOR_HUB, 1, //Number of ports - 0x02 | 0x01 , //Hub characteristics FIXME - 50 , //Power on to power good + 0x02 | 0x01, //Hub characteristics FIXME + 50, //Power on to power good 0, // Current 0x00 //Both ports are removable }; -//Implementation -UHCIRootHub::UHCIRootHub( UHCI *uhci , int8 devicenum ) - : Hub( uhci , NULL , uhci_devd , devicenum , false ) + +UHCIRootHub::UHCIRootHub(UHCI *uhci, int8 devicenum) + : Hub(uhci, NULL, uhci_devd, devicenum, false) { - m_uhci = uhci; + fUHCI = uhci; } -status_t UHCIRootHub::SubmitTransfer( Transfer *t ) -{ - status_t retval; - usb_request_data *request = t->GetRequestData(); - uint16 port; //used in RH_CLEAR/SET_FEATURE - - TRACE( "USB UHCI: rh_submit_packet called. Request: %u\n" , t->GetRequestData()->Request ); - - switch( request->Request ) - { - case RH_GET_STATUS: - if ( request->Index == 0 ) - { - //Get the hub status -- everything as 0 means that it is all-rigth - memset( t->GetBuffer() , NULL , sizeof(get_status_buffer) ); - retval = B_OK; - break; - } - else if (request->Index > uhci_hubd.bNbrPorts ) - { - //This port doesn't exist - retval = EINVAL; - break; - } - //Get port status - UpdatePortStatus(); - memcpy( t->GetBuffer() , (void *)&(m_hw_port_status[request->Index - 1]) , t->GetBufferLength()); - *(t->GetActualLength()) = t->GetBufferLength(); - retval = B_OK; - break; - case RH_SET_ADDRESS: - if ( request->Value >= 128 ) - { - retval = EINVAL; - break; - } - TRACE( "USB UHCI: rh_submit_packet RH_ADDRESS: %d\n" , request->Value ); - retval = B_OK; - break; - - case RH_GET_DESCRIPTOR: - { - TRACE( "USB UHCI: rh_submit_packet GET_DESC: %d\n" , request->Value ); - switch ( request->Value ) - { - case RH_DEVICE_DESCRIPTOR: - memcpy( t->GetBuffer() , (void *)&uhci_devd , t->GetBufferLength()); - *(t->GetActualLength()) = t->GetBufferLength(); - retval = B_OK; +status_t +UHCIRootHub::SubmitTransfer(Transfer *transfer) +{ + usb_request_data *request = transfer->GetRequestData(); + TRACE(("usb_uhci_roothub: rh_submit_packet called. request: %u\n", request->Request)); + + status_t result = B_ERROR; + switch(request->Request) { + case RH_GET_STATUS: + if (request->Index == 0) { + // Get the hub status -- everything as 0 means all-right + memset(transfer->GetBuffer(), 0, sizeof(get_status_buffer)); + result = B_OK; break; - case RH_CONFIG_DESCRIPTOR: - memcpy( t->GetBuffer() , (void *)&uhci_confd , t->GetBufferLength()); - *(t->GetActualLength()) = t->GetBufferLength(); - retval = B_OK; - break; - case RH_INTERFACE_DESCRIPTOR: - memcpy( t->GetBuffer() , (void *)&uhci_intd , t->GetBufferLength()); - *(t->GetActualLength()) = t->GetBufferLength(); - retval = B_OK ; - break; - case RH_ENDPOINT_DESCRIPTOR: - memcpy( t->GetBuffer() , (void *)&uhci_endd , t->GetBufferLength()); - *(t->GetActualLength()) = t->GetBufferLength(); - retval = B_OK ; - break; - case RH_HUB_DESCRIPTOR: - memcpy( t->GetBuffer() , (void *)&uhci_hubd , t->GetBufferLength()); - *(t->GetActualLength()) = t->GetBufferLength(); - retval = B_OK; - break; - default: - retval = EINVAL; + } else if (request->Index > uhci_hubd.bNbrPorts) { + // This port doesn't exist + result = EINVAL; break; } - break; - } - case RH_SET_CONFIG: - retval = B_OK; - break; + // Get port status + UpdatePortStatus(); + memcpy(transfer->GetBuffer(), + (void *)&fPortStatus[request->Index - 1], + transfer->GetBufferLength()); - case RH_CLEAR_FEATURE: - if ( request->Index == 0 ) - { - //We don't support any hub changes - TRACE( "UHCI: RH_CLEAR_FEATURE no hub changes!\n" ); - retval = EINVAL; + *(transfer->GetActualLength()) = transfer->GetBufferLength(); + result = B_OK; break; - } - else if ( request->Index > uhci_hubd.bNbrPorts ) - { - //Invalid port number - TRACE( "UHCI: RH_CLEAR_FEATURE invalid port!\n" ); - retval = EINVAL; + + case RH_SET_ADDRESS: + if (request->Value >= 128) { + result = EINVAL; + break; + } + + TRACE(("usb_uhci_roothub: rh_submit_packet RH_ADDRESS: %d\n", request->Value)); + result = B_OK; break; - } - - TRACE("UHCI: RH_CLEAR_FEATURE called. Feature: %u!\n" , request->Value ); - switch( request->Value ) - { - case PORT_RESET: - port = UHCI::pci_module->read_io_16( m_uhci->m_reg_base + UHCI_PORTSC1 + (request->Index - 1 ) * 2 ); - port &= ~UHCI_PORTSC_RESET; - TRACE( "UHCI rh: port %x Clear RESET\n" , port ); - UHCI::pci_module->write_io_16( m_uhci->m_reg_base + UHCI_PORTSC1 + (request->Index - 1 ) * 2 , port ); - case C_PORT_CONNECTION: - port = UHCI::pci_module->read_io_16( m_uhci->m_reg_base + UHCI_PORTSC1 + (request->Index - 1 ) * 2 ); - port = port & UHCI_PORTSC_DATAMASK; - port |= UHCI_PORTSC_STATCHA; - TRACE( "UHCI rh: port: %x\n" , port ); - UHCI::pci_module->write_io_16( m_uhci->m_reg_base + UHCI_PORTSC1 + (request->Index - 1 ) * 2 , port ); - retval = B_OK; + + case RH_GET_DESCRIPTOR: + TRACE(("usb_uhci_roothub: rh_submit_packet GET_DESC: %d\n", request->Value)); + + switch (request->Value) { + case RH_DEVICE_DESCRIPTOR: + memcpy(transfer->GetBuffer(), (void *)&uhci_devd, + transfer->GetBufferLength()); + *(transfer->GetActualLength()) = transfer->GetBufferLength(); + result = B_OK; + break; + case RH_CONFIG_DESCRIPTOR: + memcpy(transfer->GetBuffer(), (void *)&uhci_confd, + transfer->GetBufferLength()); + *(transfer->GetActualLength()) = transfer->GetBufferLength(); + result = B_OK; + break; + case RH_INTERFACE_DESCRIPTOR: + memcpy(transfer->GetBuffer(), (void *)&uhci_intd, + transfer->GetBufferLength()); + *(transfer->GetActualLength()) = transfer->GetBufferLength(); + result = B_OK ; + break; + case RH_ENDPOINT_DESCRIPTOR: + memcpy(transfer->GetBuffer(), (void *)&uhci_endd, + transfer->GetBufferLength()); + *(transfer->GetActualLength()) = transfer->GetBufferLength(); + result = B_OK ; + break; + case RH_HUB_DESCRIPTOR: + memcpy(transfer->GetBuffer(), (void *)&uhci_hubd, + transfer->GetBufferLength()); + *(transfer->GetActualLength()) = transfer->GetBufferLength(); + result = B_OK; + break; + default: + result = EINVAL; + break; + } break; - default: - retval = EINVAL; + + case RH_SET_CONFIG: + result = B_OK; + break; + + case RH_CLEAR_FEATURE: + if (request->Index == 0) { + // We don't support any hub changes + TRACE(("usb_uhci_roothub: RH_CLEAR_FEATURE no hub changes!\n")); + result = EINVAL; + break; + } else if (request->Index > uhci_hubd.bNbrPorts) { + // Invalid port number + TRACE(("usb_uhci_roothub: RH_CLEAR_FEATURE invalid port!\n")); + result = EINVAL; + break; + } + + TRACE(("usb_uhci_roothub: RH_CLEAR_FEATURE called. Feature: %u!\n", request->Value)); + uint16 port; + switch(request->Value) { + case PORT_RESET: + port = UHCI::sPCIModule->read_io_16(fUHCI->fRegisterBase + UHCI_PORTSC1 + (request->Index - 1) * 2); + port &= ~UHCI_PORTSC_RESET; + TRACE(("usb_uhci_roothub: port %x Clear RESET\n", port)); + UHCI::sPCIModule->write_io_16(fUHCI->fRegisterBase + UHCI_PORTSC1 + (request->Index - 1) * 2, port); + break; + + case C_PORT_CONNECTION: + port = UHCI::sPCIModule->read_io_16(fUHCI->fRegisterBase + UHCI_PORTSC1 + (request->Index - 1) * 2); + port = port & UHCI_PORTSC_DATAMASK; + port |= UHCI_PORTSC_STATCHA; + TRACE(("usb_uhci_roothub: port: %x\n", port)); + UHCI::sPCIModule->write_io_16(fUHCI->fRegisterBase + UHCI_PORTSC1 + (request->Index - 1) * 2, port); + result = B_OK; + break; + default: + result = EINVAL; + break; + } + break; + + default: + result = EINVAL; break; - } //switch( t->value) - break; - - default: - retval = EINVAL; - break; } - + // Clean up the transfer - we own it, so we clean it up - t->Finish(); - delete t; - - return retval; + transfer->Finish(); + delete transfer; + return result; } -void UHCIRootHub::UpdatePortStatus(void) -{ - int i; - for ( i = 0; i <= 1 ; i++ ) - { - uint16 newstatus = 0; - uint16 newchange = 0; - - uint16 portsc = UHCI::pci_module->read_io_16( m_uhci->m_reg_base + UHCI_PORTSC1 + i * 2 ); - dprintf( "USB UHCI: port: %x status: 0x%x\n" , UHCI_PORTSC1 + i * 2 , portsc ); - //Set all individual bits - if ( portsc & UHCI_PORTSC_CURSTAT ) - newstatus |= PORT_STATUS_CONNECTION; - if ( portsc & UHCI_PORTSC_STATCHA ) - newchange |= PORT_STATUS_CONNECTION; - - if ( portsc & UHCI_PORTSC_ENABLED ) - newstatus |= PORT_STATUS_ENABLE; - - if ( portsc & UHCI_PORTSC_ENABCHA ) - newchange |= PORT_STATUS_ENABLE; - +void +UHCIRootHub::UpdatePortStatus() +{ + for (int32 i = 0; i < 2; i++) { + uint16 newStatus = 0; + uint16 newChange = 0; + + uint16 portStatus = UHCI::sPCIModule->read_io_16(fUHCI->fRegisterBase + UHCI_PORTSC1 + i * 2); + TRACE(("usb_uhci_roothub: port: %d status: 0x%04x\n", UHCI_PORTSC1 + i * 2, portStatus)); + + // Set all individual bits + if (portStatus & UHCI_PORTSC_CURSTAT) + newStatus |= PORT_STATUS_CONNECTION; + + if (portStatus & UHCI_PORTSC_STATCHA) + newStatus |= PORT_STATUS_CONNECTION; + + if (portStatus & UHCI_PORTSC_ENABLED) + newStatus |= PORT_STATUS_ENABLE; + + if (portStatus & UHCI_PORTSC_ENABCHA) + newStatus |= PORT_STATUS_ENABLE; + // TODO: work out suspended/resume - - if ( portsc & UHCI_PORTSC_RESET ) - newstatus |= PORT_STATUS_RESET; - + + if (portStatus & UHCI_PORTSC_RESET) + newStatus |= PORT_STATUS_RESET; + //TODO: work out reset change... - + //The port is automagically powered on - newstatus |= PORT_POWER; - - if ( portsc & UHCI_PORTSC_LOWSPEED ) - newstatus |= PORT_STATUS_LOW_SPEED; - + newStatus |= PORT_POWER; + + if (portStatus & UHCI_PORTSC_LOWSPEED) + newStatus |= PORT_STATUS_LOW_SPEED; + //Update the stored port status - m_hw_port_status[i].status = newstatus; - m_hw_port_status[i].change = newchange; + fPortStatus[i].status = newStatus; + fPortStatus[i].change = newChange; } }