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:
@@ -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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user