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
This commit is contained in:
Michael Lotz
2006-05-27 13:07:11 +00:00
parent 848b89034e
commit 12b0511534
4 changed files with 798 additions and 751 deletions
+496 -441
View File
@@ -1,23 +1,10 @@
//------------------------------------------------------------------------------ /*
// Copyright (c) 2004, Niels S. Reedijk * Copyright 2004-2006, Haiku Inc. All rights reserved.
// * Distributed under the terms of the MIT License.
// Permission is hereby granted, free of charge, to any person obtaining a *
// copy of this software and associated documentation files (the "Software"), * Authors:
// to deal in the Software without restriction, including without limitation * Niels S. Reedijk
// 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.
#include <module.h> #include <module.h>
#include <PCI.h> #include <PCI.h>
@@ -30,69 +17,501 @@
#include "usb_p.h" #include "usb_p.h"
/* ++++++++++ #define TRACE_UHCI
This is the implementation of the UHCI controller for the OpenBeOS USB stack #ifdef TRACE_UHCI
++++++++++ */ #define TRACE(x) dprintf x
#else
#define TRACE(x) /* nothing */
#endif
pci_module_info *UHCI::sPCIModule = NULL;
static int32 static int32
uhci_std_ops(int32 op, ...) uhci_std_ops(int32 op, ...)
{ {
switch (op) switch (op) {
{
case B_MODULE_INIT: case B_MODULE_INIT:
TRACE( "uhci_module: init the module\n" ); TRACE(("usb_uhci_module: init module\n"));
return B_OK; return B_OK;
case B_MODULE_UNINIT: case B_MODULE_UNINIT:
TRACE( "uhci_module: uninit the module\n" ); TRACE(("usb_uhci_module: uninit module\n"));
break; break;
default: default:
return EINVAL; return EINVAL;
} }
return B_OK; return B_OK;
} }
static bool
uhci_add_to( Stack &stack )
{
status_t status;
pci_info *item;
bool found = false;
int i;
#ifdef UHCI_DEBUG host_controller_info uhci_module = {
{
"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); set_dprintf_enabled(true);
load_driver_symbols("uhci"); load_driver_symbols("uhci");
#endif #endif
// Try if the PCI module is loaded (it would be weird if it wouldn't, but alas) status_t status = get_module(B_PCI_MODULE_NAME, (module_info **)&sPCIModule);
if( ( status = get_module( B_PCI_MODULE_NAME, (module_info **)&( UHCI::pci_module ) ) ) != B_OK) if (status < B_OK) {
{ TRACE(("usb_uhci: AddTo(): getting pci module failed! 0x%08x\n",
TRACE( "USB_ UHCI: init_hardware(): Get PCI module failed! %lu \n", status); status));
return 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. // TODO: in the future we might want to support multiple host controllers.
item = new pci_info; bool found = false;
for ( i = 0 ; UHCI::pci_module->get_nth_pci_info( i , item ) == B_OK ; i++ ) 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) //class_base = 0C (serial bus) class_sub = 03 (usb) prog_int: 00 (UHCI)
if ( ( item->class_base == 0x0C ) && ( item->class_sub == 0x03 ) && if (item->class_base == 0x0C && item->class_sub == 0x03
( item->class_api == 0x00 ) ) && item->class_api == 0x00) {
{ if (item->u.h0.interrupt_line == 0
if ((item->u.h0.interrupt_line == 0) || (item->u.h0.interrupt_line == 0xFF)) || item->u.h0.interrupt_line == 0xFF) {
{ TRACE(("usb_uhci: AddTo(): found with invalid IRQ - check IRQ assignement\n"));
TRACE( "USB UHCI: init_hardware(): found with invalid IRQ - check IRQ assignement\n");
continue; continue;
} }
TRACE("USB UHCI: init_hardware(): found at IRQ %u \n", item->u.h0.interrupt_line);
TRACE(("usb_uhci: AddTo(): found at IRQ %u\n", item->u.h0.interrupt_line));
UHCI *bus = new UHCI(item, &stack); UHCI *bus = new UHCI(item, &stack);
if ( bus->InitCheck() != B_OK ) if (bus->InitCheck() < B_OK) {
{ TRACE(("usb_uhci: AddTo(): InitCheck() failed 0x%08x\n", bus->InitCheck()));
TRACE( "USB UHCI::InitCheck() failed, error %li\n" , bus->InitCheck() );
delete bus; delete bus;
break; continue;
} }
stack.AddBusManager(bus); stack.AddBusManager(bus);
@@ -102,404 +521,40 @@ uhci_add_to( Stack &stack )
} }
} }
if ( found == false ) if (!found) {
{ TRACE(("usb_uhci: AddTo(): no devices found\n"));
TRACE( "USB UHCI: init hardware(): no devices found\n" ); delete item;
free( item );
put_module(B_PCI_MODULE_NAME); put_module(B_PCI_MODULE_NAME);
return ENODEV; 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<addr_t>(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<addr_t>(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<addr_t>(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; return B_OK;
} }
int32 UHCI::Interrupt()
{
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 inline void
if ( status | UHCI_USBSTS_USBINT ) UHCI::WriteReg16(uint32 reg, uint16 value)
{ {
//A transfer finished sPCIModule->write_io_16(fRegisterBase + reg, value);
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;
} }
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 ) ); sPCIModule->write_io_32(fRegisterBase + reg, value);
//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;
} }
pci_module_info *UHCI::pci_module = 0;
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);
}
+60 -69
View File
@@ -1,23 +1,10 @@
//------------------------------------------------------------------------------ /*
// Copyright (c) 2004, Niels S. Reedijk * Copyright 2004-2006, Haiku Inc. All rights reserved.
// * Distributed under the terms of the MIT License.
// Permission is hereby granted, free of charge, to any person obtaining a *
// copy of this software and associated documentation files (the "Software"), * Authors:
// to deal in the Software without restriction, including without limitation * Niels S. Reedijk
// 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.
#ifndef UHCI_H #ifndef UHCI_H
#define UHCI_H #define UHCI_H
@@ -29,84 +16,88 @@ struct pci_info;
struct pci_module_info; struct pci_module_info;
class UHCIRootHub; class UHCIRootHub;
class UHCI : public BusManager
{ class UHCI : public BusManager {
friend class UHCIRootHub;
friend int32 uhci_interrupt_handler( void *data );
public: public:
UHCI(pci_info *info, Stack *stack); UHCI(pci_info *info, Stack *stack);
//Override from BusManager
status_t Start(); status_t Start();
status_t SubmitTransfer( Transfer *t ); status_t SubmitTransfer(Transfer *transfer);
static bool AddTo(Stack &stack);
// Global data for the module.
static pci_module_info *pci_module;
private: private:
friend class UHCIRootHub;
// Utility functions // Utility functions
void GlobalReset(); void GlobalReset();
status_t Reset(); status_t Reset();
static int32 InterruptHandler(void *data);
int32 Interrupt(); int32 Interrupt();
//Functions for the actual functioning of transfers // Register functions
status_t InsertControl( Transfer *t ); inline void WriteReg16(uint32 reg, uint16 value);
inline void WriteReg32(uint32 reg, uint32 value);
inline uint16 ReadReg16(uint32 reg);
inline uint32 ReadReg32(uint32 reg);
uint32 m_reg_base; //Base address of the registers // Functions for the actual functioning of transfers
pci_info *m_pcii; //pci-info struct status_t InsertControl(Transfer *transfer);
Stack *m_stack; //Pointer to the stack
static pci_module_info *sPCIModule;
uint32 fRegisterBase;
pci_info *fPCIInfo;
Stack *fStack;
// Frame list memory // Frame list memory
area_id m_framearea; area_id fFrameArea;
addr_t m_framelist[1024]; //The frame list struct addr_t fFrameList[1024];
addr_t m_framelist_phy; //The physical pointer to the frame list addr_t fPhysicalFrameList;
// Virtual frame // Virtual frame
uhci_qh *m_qh_virtual[12]; // uhci_qh *fVirtualQueueHead[12];
#define m_qh_interrupt_256 m_qh_virtual[0]
#define m_qh_interrupt_128 m_qh_virtual[1] #define fQueueHeadInterrupt256 fVirtualQueueHead[0]
#define m_qh_interrupt_64 m_qh_virtual[2] #define fQueueHeadInterrupt128 fVirtualQueueHead[1]
#define m_qh_interrupt_32 m_qh_virtual[3] #define fQueueHeadInterrupt64 fVirtualQueueHead[2]
#define m_qh_interrupt_16 m_qh_virtual[4] #define fQueueHeadInterrupt32 fVirtualQueueHead[3]
#define m_qh_interrupt_8 m_qh_virtual[5] #define fQueueHeadInterrupt16 fVirtualQueueHead[4]
#define m_qh_interrupt_4 m_qh_virtual[6] #define fQueueHeadInterrupt8 fVirtualQueueHead[5]
#define m_qh_interrupt_2 m_qh_virtual[7] #define fQueueHeadInterrupt4 fVirtualQueueHead[6]
#define m_qh_interrupt_1 m_qh_virtual[8] #define fQueueHeadInterrupt2 fVirtualQueueHead[7]
#define m_qh_control m_qh_virtual[9] #define fQueueHeadInterrupt1 fVirtualQueueHead[8]
#define m_qh_bulk m_qh_virtual[10] #define fQueueHeadControl fVirtualQueueHead[9]
#define m_qh_terminate m_qh_virtual[11] #define fQueueHeadBulk fVirtualQueueHead[10]
#define fQueueHeadTerminate fVirtualQueueHead[11]
// Maintain a list of transfers // Maintain a list of transfers
Vector<Transfer *> m_transfers; Vector<Transfer *> fTransfers;
//Root hub: // Root hub
UHCIRootHub *m_rh; // the root hub UHCIRootHub *fRootHub;
uint8 m_rh_address; // the address of the root hub uint8 fRootHubAddress;
}; };
class UHCIRootHub : public Hub
{ class UHCIRootHub : public Hub {
public: public:
UHCIRootHub( UHCI *uhci , int8 devicenum ); UHCIRootHub(UHCI *uhci, int8 deviceNum);
status_t SubmitTransfer( Transfer *t );
status_t SubmitTransfer(Transfer *transfer);
void UpdatePortStatus(); void UpdatePortStatus();
private: 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_qh *topqh;
uhci_td *firsttd; uhci_td *firsttd;
uhci_td *lasttd; uhci_td *lasttd;
}; };
#define UHCI_DEBUG
#ifdef UHCI_DEBUG
#define TRACE dprintf
#else
#define TRACE silent
void silent( const char * , ... ) {}
#endif
#endif #endif
@@ -61,7 +61,7 @@
#define UHCI_USBSTS_HOSTERR 0x8 // Host System Error #define UHCI_USBSTS_HOSTERR 0x8 // Host System Error
#define UHCI_USBSTS_HCPRERR 0x10// Host Controller Process error #define UHCI_USBSTS_HCPRERR 0x10// Host Controller Process error
#define UHCI_USBSTS_HCHALT 0x20 // HCHalted #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 //USBINTR
#define UHCI_USBINTR_CRC 0x1 // Timeout/ CRC interrupt enable #define UHCI_USBINTR_CRC 0x1 // Timeout/ CRC interrupt enable
+121 -120
View File
@@ -1,28 +1,23 @@
//------------------------------------------------------------------------------ /*
// Copyright (c) 2004, Niels S. Reedijk * Copyright 2004-2006, Haiku Inc. All rights reserved.
// * Distributed under the terms of the MIT License.
// Permission is hereby granted, free of charge, to any person obtaining a *
// copy of this software and associated documentation files (the "Software"), * Authors:
// to deal in the Software without restriction, including without limitation * Niels S. Reedijk
// 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.
#include "uhci.h" #include "uhci.h"
#include <PCI.h> #include <PCI.h>
#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 = usb_device_descriptor uhci_devd =
{ {
0x12, //Descriptor size 0x12, //Descriptor size
@@ -39,6 +34,7 @@ usb_device_descriptor uhci_devd =
1 //Number of configurations 1 //Number of configurations
}; };
usb_configuration_descriptor uhci_confd = usb_configuration_descriptor uhci_confd =
{ {
0x09, //Size 0x09, //Size
@@ -51,6 +47,7 @@ usb_configuration_descriptor uhci_confd =
0 //Max power (0, because of self power) 0 //Max power (0, because of self power)
}; };
usb_interface_descriptor uhci_intd = usb_interface_descriptor uhci_intd =
{ {
0x09, //Size 0x09, //Size
@@ -64,6 +61,7 @@ usb_interface_descriptor uhci_intd =
0, //Interface 0, //Interface
}; };
usb_endpoint_descriptor uhci_endd = usb_endpoint_descriptor uhci_endd =
{ {
0x07, //Size 0x07, //Size
@@ -74,6 +72,7 @@ usb_endpoint_descriptor uhci_endd =
0xFF // Interval 256 0xFF // Interval 256
}; };
usb_hub_descriptor uhci_hubd = usb_hub_descriptor uhci_hubd =
{ {
0x09, //Including deprecated powerctrlmask 0x09, //Including deprecated powerctrlmask
@@ -85,183 +84,185 @@ usb_hub_descriptor uhci_hubd =
0x00 //Both ports are removable 0x00 //Both ports are removable
}; };
//Implementation
UHCIRootHub::UHCIRootHub(UHCI *uhci, int8 devicenum) UHCIRootHub::UHCIRootHub(UHCI *uhci, int8 devicenum)
: Hub(uhci, NULL, uhci_devd, devicenum, false) : 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 ); status_t
UHCIRootHub::SubmitTransfer(Transfer *transfer)
switch( request->Request )
{ {
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: case RH_GET_STATUS:
if ( request->Index == 0 ) if (request->Index == 0) {
{ // Get the hub status -- everything as 0 means all-right
//Get the hub status -- everything as 0 means that it is all-rigth memset(transfer->GetBuffer(), 0, sizeof(get_status_buffer));
memset( t->GetBuffer() , NULL , sizeof(get_status_buffer) ); result = B_OK;
retval = B_OK;
break; break;
} } else if (request->Index > uhci_hubd.bNbrPorts) {
else if (request->Index > uhci_hubd.bNbrPorts )
{
// This port doesn't exist // This port doesn't exist
retval = EINVAL; result = EINVAL;
break; break;
} }
// Get port status // Get port status
UpdatePortStatus(); UpdatePortStatus();
memcpy( t->GetBuffer() , (void *)&(m_hw_port_status[request->Index - 1]) , t->GetBufferLength()); memcpy(transfer->GetBuffer(),
*(t->GetActualLength()) = t->GetBufferLength(); (void *)&fPortStatus[request->Index - 1],
retval = B_OK; transfer->GetBufferLength());
*(transfer->GetActualLength()) = transfer->GetBufferLength();
result = B_OK;
break; break;
case RH_SET_ADDRESS: case RH_SET_ADDRESS:
if ( request->Value >= 128 ) if (request->Value >= 128) {
{ result = EINVAL;
retval = EINVAL;
break; break;
} }
TRACE( "USB UHCI: rh_submit_packet RH_ADDRESS: %d\n" , request->Value );
retval = B_OK; TRACE(("usb_uhci_roothub: rh_submit_packet RH_ADDRESS: %d\n", request->Value));
result = B_OK;
break; break;
case RH_GET_DESCRIPTOR: case RH_GET_DESCRIPTOR:
{ TRACE(("usb_uhci_roothub: rh_submit_packet GET_DESC: %d\n", request->Value));
TRACE( "USB UHCI: rh_submit_packet GET_DESC: %d\n" , request->Value );
switch ( request->Value ) switch (request->Value) {
{
case RH_DEVICE_DESCRIPTOR: case RH_DEVICE_DESCRIPTOR:
memcpy( t->GetBuffer() , (void *)&uhci_devd , t->GetBufferLength()); memcpy(transfer->GetBuffer(), (void *)&uhci_devd,
*(t->GetActualLength()) = t->GetBufferLength(); transfer->GetBufferLength());
retval = B_OK; *(transfer->GetActualLength()) = transfer->GetBufferLength();
result = B_OK;
break; break;
case RH_CONFIG_DESCRIPTOR: case RH_CONFIG_DESCRIPTOR:
memcpy( t->GetBuffer() , (void *)&uhci_confd , t->GetBufferLength()); memcpy(transfer->GetBuffer(), (void *)&uhci_confd,
*(t->GetActualLength()) = t->GetBufferLength(); transfer->GetBufferLength());
retval = B_OK; *(transfer->GetActualLength()) = transfer->GetBufferLength();
result = B_OK;
break; break;
case RH_INTERFACE_DESCRIPTOR: case RH_INTERFACE_DESCRIPTOR:
memcpy( t->GetBuffer() , (void *)&uhci_intd , t->GetBufferLength()); memcpy(transfer->GetBuffer(), (void *)&uhci_intd,
*(t->GetActualLength()) = t->GetBufferLength(); transfer->GetBufferLength());
retval = B_OK ; *(transfer->GetActualLength()) = transfer->GetBufferLength();
result = B_OK ;
break; break;
case RH_ENDPOINT_DESCRIPTOR: case RH_ENDPOINT_DESCRIPTOR:
memcpy( t->GetBuffer() , (void *)&uhci_endd , t->GetBufferLength()); memcpy(transfer->GetBuffer(), (void *)&uhci_endd,
*(t->GetActualLength()) = t->GetBufferLength(); transfer->GetBufferLength());
retval = B_OK ; *(transfer->GetActualLength()) = transfer->GetBufferLength();
result = B_OK ;
break; break;
case RH_HUB_DESCRIPTOR: case RH_HUB_DESCRIPTOR:
memcpy( t->GetBuffer() , (void *)&uhci_hubd , t->GetBufferLength()); memcpy(transfer->GetBuffer(), (void *)&uhci_hubd,
*(t->GetActualLength()) = t->GetBufferLength(); transfer->GetBufferLength());
retval = B_OK; *(transfer->GetActualLength()) = transfer->GetBufferLength();
result = B_OK;
break; break;
default: default:
retval = EINVAL; result = EINVAL;
break; break;
} }
break; break;
}
case RH_SET_CONFIG: case RH_SET_CONFIG:
retval = B_OK; result = B_OK;
break; break;
case RH_CLEAR_FEATURE: case RH_CLEAR_FEATURE:
if ( request->Index == 0 ) if (request->Index == 0) {
{
// We don't support any hub changes // We don't support any hub changes
TRACE( "UHCI: RH_CLEAR_FEATURE no hub changes!\n" ); TRACE(("usb_uhci_roothub: RH_CLEAR_FEATURE no hub changes!\n"));
retval = EINVAL; result = EINVAL;
break; break;
} } else if (request->Index > uhci_hubd.bNbrPorts) {
else if ( request->Index > uhci_hubd.bNbrPorts )
{
// Invalid port number // Invalid port number
TRACE( "UHCI: RH_CLEAR_FEATURE invalid port!\n" ); TRACE(("usb_uhci_roothub: RH_CLEAR_FEATURE invalid port!\n"));
retval = EINVAL; result = EINVAL;
break; break;
} }
TRACE("UHCI: RH_CLEAR_FEATURE called. Feature: %u!\n" , request->Value ); TRACE(("usb_uhci_roothub: RH_CLEAR_FEATURE called. Feature: %u!\n", request->Value));
switch( request->Value ) uint16 port;
{ switch(request->Value) {
case PORT_RESET: case PORT_RESET:
port = UHCI::pci_module->read_io_16( m_uhci->m_reg_base + UHCI_PORTSC1 + (request->Index - 1 ) * 2 ); port = UHCI::sPCIModule->read_io_16(fUHCI->fRegisterBase + UHCI_PORTSC1 + (request->Index - 1) * 2);
port &= ~UHCI_PORTSC_RESET; port &= ~UHCI_PORTSC_RESET;
TRACE( "UHCI rh: port %x Clear RESET\n" , port ); TRACE(("usb_uhci_roothub: port %x Clear RESET\n", port));
UHCI::pci_module->write_io_16( m_uhci->m_reg_base + UHCI_PORTSC1 + (request->Index - 1 ) * 2 , port ); UHCI::sPCIModule->write_io_16(fUHCI->fRegisterBase + UHCI_PORTSC1 + (request->Index - 1) * 2, port);
break;
case C_PORT_CONNECTION: case C_PORT_CONNECTION:
port = UHCI::pci_module->read_io_16( m_uhci->m_reg_base + UHCI_PORTSC1 + (request->Index - 1 ) * 2 ); port = UHCI::sPCIModule->read_io_16(fUHCI->fRegisterBase + UHCI_PORTSC1 + (request->Index - 1) * 2);
port = port & UHCI_PORTSC_DATAMASK; port = port & UHCI_PORTSC_DATAMASK;
port |= UHCI_PORTSC_STATCHA; port |= UHCI_PORTSC_STATCHA;
TRACE( "UHCI rh: port: %x\n" , port ); TRACE(("usb_uhci_roothub: port: %x\n", port));
UHCI::pci_module->write_io_16( m_uhci->m_reg_base + UHCI_PORTSC1 + (request->Index - 1 ) * 2 , port ); UHCI::sPCIModule->write_io_16(fUHCI->fRegisterBase + UHCI_PORTSC1 + (request->Index - 1) * 2, port);
retval = B_OK; result = B_OK;
break; break;
default: default:
retval = EINVAL; result = EINVAL;
break; break;
} //switch( t->value) }
break; break;
default: default:
retval = EINVAL; result = EINVAL;
break; break;
} }
// Clean up the transfer - we own it, so we clean it up // Clean up the transfer - we own it, so we clean it up
t->Finish(); transfer->Finish();
delete t; delete transfer;
return result;
return retval;
} }
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 ); void
dprintf( "USB UHCI: port: %x status: 0x%x\n" , UHCI_PORTSC1 + i * 2 , portsc ); 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 // Set all individual bits
if ( portsc & UHCI_PORTSC_CURSTAT ) if (portStatus & UHCI_PORTSC_CURSTAT)
newstatus |= PORT_STATUS_CONNECTION; newStatus |= PORT_STATUS_CONNECTION;
if ( portsc & UHCI_PORTSC_STATCHA ) if (portStatus & UHCI_PORTSC_STATCHA)
newchange |= PORT_STATUS_CONNECTION; newStatus |= PORT_STATUS_CONNECTION;
if ( portsc & UHCI_PORTSC_ENABLED ) if (portStatus & UHCI_PORTSC_ENABLED)
newstatus |= PORT_STATUS_ENABLE; newStatus |= PORT_STATUS_ENABLE;
if ( portsc & UHCI_PORTSC_ENABCHA ) if (portStatus & UHCI_PORTSC_ENABCHA)
newchange |= PORT_STATUS_ENABLE; newStatus |= PORT_STATUS_ENABLE;
// TODO: work out suspended/resume // TODO: work out suspended/resume
if ( portsc & UHCI_PORTSC_RESET ) if (portStatus & UHCI_PORTSC_RESET)
newstatus |= PORT_STATUS_RESET; newStatus |= PORT_STATUS_RESET;
//TODO: work out reset change... //TODO: work out reset change...
//The port is automagically powered on //The port is automagically powered on
newstatus |= PORT_POWER; newStatus |= PORT_POWER;
if ( portsc & UHCI_PORTSC_LOWSPEED ) if (portStatus & UHCI_PORTSC_LOWSPEED)
newstatus |= PORT_STATUS_LOW_SPEED; newStatus |= PORT_STATUS_LOW_SPEED;
//Update the stored port status //Update the stored port status
m_hw_port_status[i].status = newstatus; fPortStatus[i].status = newStatus;
m_hw_port_status[i].change = newchange; fPortStatus[i].change = newChange;
} }
} }