diff --git a/src/add-ons/kernel/bus_managers/usb/BusManager.cpp b/src/add-ons/kernel/bus_managers/usb/BusManager.cpp index cab9040bbe..26dee10af7 100644 --- a/src/add-ons/kernel/bus_managers/usb/BusManager.cpp +++ b/src/add-ons/kernel/bus_managers/usb/BusManager.cpp @@ -193,7 +193,7 @@ BusManager::Stop() status_t -BusManager::SubmitTransfer(Transfer *transfer) +BusManager::SubmitTransfer(Transfer *transfer, bigtime_t timeout) { // virtual function to be overridden return B_ERROR; diff --git a/src/add-ons/kernel/bus_managers/usb/Hub.cpp b/src/add-ons/kernel/bus_managers/usb/Hub.cpp index 35156aaa65..3e05f26927 100644 --- a/src/add-ons/kernel/bus_managers/usb/Hub.cpp +++ b/src/add-ons/kernel/bus_managers/usb/Hub.cpp @@ -102,6 +102,7 @@ Hub::Hub( BusManager *bus , Device *parent , usb_device_descriptor &desc , int8 //We're basically done now m_initok = true; + dprintf( "USB Hub: initialised ok\n" ); } void Hub::Explore() @@ -121,9 +122,10 @@ void Hub::Explore() &actual_length ); if ( actual_length < 4 ) { - dprintf( "USB Hub: ERROR getting port status" ); + dprintf( "USB Hub: ERROR getting port status\n" ); return; } + dprintf("status: 0x%04x; change: 0x%04x\n", m_port_status[i].status, m_port_status[i].change); //We need to test the port change against a number of things @@ -146,6 +148,19 @@ void Hub::Explore() if ( m_port_status[i].status & PORT_STATUS_CONNECTION ) { //New device attached! + + if ((m_port_status[i].status & PORT_STATUS_ENABLE) == 0) { + // enable the port if it isn't + m_defaultPipe->SendRequest( USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT , + USB_REQUEST_SET_FEATURE , + PORT_ENABLE , + i + 1 , //index + 0 , + NULL , + 0 , + &actual_length ); + } + dprintf( "USB Hub Explore(): New device connected\n" ); Device *newdev; if ( m_port_status[i].status & PORT_STATUS_LOW_SPEED ) diff --git a/src/add-ons/kernel/bus_managers/usb/Pipe.cpp b/src/add-ons/kernel/bus_managers/usb/Pipe.cpp index 72831cfff8..71111ac69c 100644 --- a/src/add-ons/kernel/bus_managers/usb/Pipe.cpp +++ b/src/add-ons/kernel/bus_managers/usb/Pipe.cpp @@ -1,96 +1,102 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2004, Niels S. Reedijk -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. +/* + * Copyright 2004-2006, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Niels S. Reedijk + */ #include "usb_p.h" -Pipe::Pipe( Device *dev , Direction &dir , uint8 &endpointaddress ) + +Pipe::Pipe(Device *device, pipeDirection &direction, uint8 &endpointAddress) { - m_direction = dir; - m_device = dev; - m_endpoint = endpointaddress; - if ( m_device != NULL ) - m_bus = m_device->GetBusManager(); + fDirection = direction; + fDevice = device; + fEndpoint = endpointAddress; + + fBus = NULL; + if (fDevice) + fBus = fDevice->GetBusManager(); } + Pipe::~Pipe() { } -int8 Pipe::GetDeviceAddress() + +int8 +Pipe::DeviceAddress() { - if ( m_device == NULL ) + if (!fDevice) return -1; - else - return m_device->GetAddress(); + + return fDevice->GetAddress(); } -ControlPipe::ControlPipe( Device *dev , Direction dir , uint8 endpointaddress ) - : Pipe( dev , dir , endpointaddress ) -{ - -} -ControlPipe::ControlPipe( BusManager *bus , int8 dev_address , Direction dir , Speed speed , uint8 endpointaddress ) - : Pipe( NULL , dir , endpointaddress ) +// +// #pragma mark - +// + + +ControlPipe::ControlPipe(Device *device, pipeDirection direction, + uint8 endpointAddress) + : Pipe(device, direction, endpointAddress) { - m_bus = bus; - m_deviceaddress = dev_address; - m_speed = speed; } -int8 ControlPipe::GetDeviceAddress() + +ControlPipe::ControlPipe(BusManager *bus, int8 deviceAddress, + pipeDirection direction, pipeSpeed speed, uint8 endpointAddress) + : Pipe(NULL, direction, endpointAddress) { - if ( m_device == NULL ) - return m_deviceaddress; - else - return m_device->GetAddress(); + fBus = bus; + fDeviceAddress = deviceAddress; + fSpeed = speed; } -status_t ControlPipe::SendRequest( uint8 request_type , uint8 request , uint16 value , - uint16 index , uint16 length , void *data , - size_t data_len , size_t *actual_len ) + +int8 +ControlPipe::DeviceAddress() { - usb_request_data req; - - req.RequestType = request_type; - req.Request = request; - req.Value = value; - req.Index = index; - req.Length = length; - - return SendControlMessage( &req , data , data_len , actual_len , 3 * 1000 * 1000 ); + if (!fDevice) + return fDeviceAddress; + + return fDevice->GetAddress(); } -status_t ControlPipe::SendControlMessage( usb_request_data *command , void *data , - size_t data_length , size_t *actual_length , - bigtime_t timeout ) + +status_t +ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value, + uint16 index, uint16 length, void *data, size_t dataLength, + size_t *actualLength) +{ + usb_request_data requestData; + + requestData.RequestType = requestType; + requestData.Request = request; + requestData.Value = value; + requestData.Index = index; + requestData.Length = length; + + return SendControlMessage(&requestData, data, dataLength, actualLength, + 3 * 1000 * 1000); +} + + +status_t +ControlPipe::SendControlMessage(usb_request_data *command, void *data, + size_t dataLength, size_t *actualLength, bigtime_t timeout) { // this method should build an usb packet (new class) with the needed data - Transfer *transfer = new Transfer( this , true ); - - transfer->SetRequestData( command ); - transfer->SetBuffer( (uint8 *)data ); - transfer->SetBufferLength( data_length ); - transfer->SetActualLength( actual_length ); - - status_t retval = m_bus->SubmitTransfer( transfer ); - return retval; + Transfer *transfer = new Transfer(this, true); + + transfer->SetRequestData(command); + transfer->SetBuffer((uint8 *)data); + transfer->SetBufferLength(dataLength); + transfer->SetActualLength(actualLength); + + return fBus->SubmitTransfer(transfer, timeout); } diff --git a/src/add-ons/kernel/bus_managers/usb/usb_p.h b/src/add-ons/kernel/bus_managers/usb/usb_p.h index dbebce13bf..4faed3656d 100644 --- a/src/add-ons/kernel/bus_managers/usb/usb_p.h +++ b/src/add-ons/kernel/bus_managers/usb/usb_p.h @@ -143,7 +143,8 @@ virtual status_t InitCheck(); virtual status_t Start(); virtual status_t Stop(); -virtual status_t SubmitTransfer(Transfer *transfer); +virtual status_t SubmitTransfer(Transfer *transfer, + bigtime_t timeout = 0); protected: void SetRootHub(Hub *hub) { fRootHub = hub; }; @@ -165,50 +166,70 @@ static int32 ExploreThread(void *data); thread_id fExploreThread; }; + /* * The Pipe class is the communication management between the hardware and\ * the stack. It creates packets, manages these and performs callbacks. */ -class Pipe -{ - friend class Transfer; +class Pipe { public: - enum Direction { In , Out , Default }; - enum Speed { LowSpeed , NormalSpeed }; - enum Type { Control , Bulk , Isochronous , Interrupt , Invalid }; +enum pipeDirection { In, Out, Default }; +enum pipeSpeed { LowSpeed, NormalSpeed }; +enum pipeType { Control, Bulk, Isochronous, Interrupt, Invalid }; + + Pipe(Device *device, + pipeDirection &direction, + uint8 &endpointAddress); +virtual ~Pipe(); + +virtual int8 DeviceAddress(); +virtual pipeType Type() { return Invalid; }; +virtual pipeSpeed Speed() { return LowSpeed; }; + //Provide a default: should never be called +virtual int8 EndpointAddress() { return fEndpoint; }; - Pipe( Device *dev , Direction &dir , uint8 &endpointaddress ); - virtual ~Pipe(); - - virtual int8 GetDeviceAddress(); - virtual Type GetType() { return Invalid; }; - virtual Speed GetSpeed() { return LowSpeed; }; //Provide a default: should never be called - virtual int8 GetEndpointAddress() { return m_endpoint; }; protected: - Device *m_device; - BusManager *m_bus; - Direction m_direction; - uint8 m_endpoint; +friend class Transfer; + + Device *fDevice; + BusManager *fBus; + pipeDirection fDirection; + uint8 fEndpoint; }; -class ControlPipe : public Pipe -{ + +class ControlPipe : public Pipe { public: - ControlPipe( Device *dev , Direction dir , uint8 endpointaddress ); - //Special constructor for default control pipe - ControlPipe( BusManager *bus , int8 dev_address , Direction dir , Speed speed , uint8 endpointaddress ); - virtual int8 GetDeviceAddress(); - virtual Type GetType() { return Control; }; - virtual Speed GetSpeed() { return m_speed; }; - status_t SendRequest( uint8 request_type , uint8 request , - uint16 value , uint16 index , uint16 length , void *data , - size_t data_len , size_t *actual_len ); - status_t SendControlMessage( usb_request_data *command , void *data , - size_t data_length , size_t *actual_length , bigtime_t timeout ); + ControlPipe(Device *device, + pipeDirection direction, + uint8 endpointAddress); + + // Constructor for default control pipe + ControlPipe(BusManager *bus, + int8 deviceAddress, + pipeDirection direction, + pipeSpeed speed, + uint8 endpointAddress); + +virtual int8 DeviceAddress(); +virtual pipeType Type() { return Control; }; +virtual pipeSpeed Speed() { return fSpeed; }; + + status_t SendRequest(uint8 requestType, + uint8 request, uint16 value, + uint16 index, uint16 length, + void *data, size_t dataLength, + size_t *actualLength); + + status_t SendControlMessage(usb_request_data *command, + void *data, size_t dataLength, + size_t *actualLength, + bigtime_t timeout); + private: - int8 m_deviceaddress; - Speed m_speed; + int8 fDeviceAddress; + pipeSpeed fSpeed; }; /* diff --git a/src/add-ons/kernel/busses/usb/uhci.cpp b/src/add-ons/kernel/busses/usb/uhci.cpp index 3f603ba29d..484c42f14c 100644 --- a/src/add-ons/kernel/busses/usb/uhci.cpp +++ b/src/add-ons/kernel/busses/usb/uhci.cpp @@ -68,6 +68,241 @@ module_info *modules[] = { // +Queue::Queue(Stack *stack) +{ + fStack = stack; + + void *physicalAddress; + fStatus = fStack->AllocateChunk((void **)&fQueueHead, &physicalAddress, 32); + if (fStatus < B_OK) + return; + + fQueueHead->this_phy = (addr_t)physicalAddress; + fQueueHead->element_phy = QH_TERMINATE; + fQueueHead->element_log = 0; + + fStrayDescriptor = NULL; +} + + +Queue::~Queue() +{ + fStack->FreeChunk(fQueueHead, (void *)fQueueHead->this_phy, 32); + + if (fStrayDescriptor) + fStack->FreeChunk(fStrayDescriptor, (void *)fStrayDescriptor->this_phy, 32); +} + + +status_t +Queue::InitCheck() +{ + return fStatus; +} + + +status_t +Queue::LinkTo(Queue *other) +{ + if (!other) + return B_BAD_VALUE; + + fQueueHead->link_phy = other->fQueueHead->this_phy | QH_NEXT_IS_QH; + fQueueHead->link_log = other->fQueueHead; + return B_OK; +} + + +status_t +Queue::TerminateByStrayDescriptor() +{ + // 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). + void *physicalAddress; + status_t result = fStack->AllocateChunk((void **)&fStrayDescriptor, + &physicalAddress, 32); + if (result < B_OK) { + TRACE(("usb_uhci: failed to allocate a stray transfer descriptor\n")); + return result; + } + + fStrayDescriptor->status = 0; + fStrayDescriptor->this_phy = (addr_t)physicalAddress; + fStrayDescriptor->link_phy = TD_TERMINATE; + fStrayDescriptor->link_log = 0; + fStrayDescriptor->buffer_phy = 0; + fStrayDescriptor->buffer_log = 0; + fStrayDescriptor->token = TD_TOKEN_NULL | (0x7f << TD_TOKEN_DEVADDR_SHIFT) + | 0x69; + + fQueueHead->link_phy = fStrayDescriptor->this_phy; + fQueueHead->link_log = fStrayDescriptor; + fQueueHead->element_phy = QH_TERMINATE; + fQueueHead->element_log = 0; + + return B_OK; +} + + +status_t +Queue::AppendDescriptor(uhci_td *descriptor) +{ + if (fQueueHead->element_phy & QH_TERMINATE) { + // the queue is empty, make this the first element + fQueueHead->element_phy = descriptor->this_phy; + fQueueHead->element_log = descriptor; + TRACE(("usb_uhci: first transfer in queue\n")); + } else { + // there are transfers linked, append to the queue + uhci_td *element = (uhci_td *)fQueueHead->element_log; + while ((element->link_phy & TD_TERMINATE) == 0) + element = (uhci_td *)element->link_log; + + element->link_phy = descriptor->this_phy; + element->link_log = descriptor; + TRACE(("usb_uhci: appended transfer to queue\n")); + } + + return B_OK; +} + + +status_t +Queue::AddTransfer(Transfer *transfer, bigtime_t timeout) +{ + // Allocate the transfer descriptor for the transfer + void *physicalAddress; + uhci_td *transferDescriptor; + if (fStack->AllocateChunk((void **)&transferDescriptor, &physicalAddress, 32) < B_OK) { + TRACE(("usb_uhci: failed to allocate a transfer descriptor\n")); + return ENOMEM; + } + + transferDescriptor->this_phy = (addr_t)physicalAddress; + transferDescriptor->status = TD_STATUS_ACTIVE | TD_STATUS_3_ERRORS; + if (transfer->GetPipe()->Speed() == Pipe::LowSpeed) + transferDescriptor->status |= TD_STATUS_LOWSPEED; + + transferDescriptor->token = ((sizeof(usb_request_data) - 1) << 21) + | (transfer->GetPipe()->EndpointAddress() << 15) + | (transfer->GetPipe()->DeviceAddress() << 8) | 0x2D; + + // Create a physical space for the 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 request buffer\n")); + fStack->FreeChunk(transferDescriptor, + (void *)transferDescriptor->this_phy, 32); + return ENOMEM; + } + + memcpy(transferDescriptor->buffer_log, transfer->GetRequestData(), + sizeof(usb_request_data)); + + // 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); + return ENOMEM; + } + + statusDescriptor->this_phy = (addr_t)physicalAddress; + statusDescriptor->status = TD_STATUS_IOC | TD_STATUS_ACTIVE | + TD_STATUS_3_ERRORS; + if (transfer->GetPipe()->Speed() == Pipe::LowSpeed) + statusDescriptor->status |= TD_STATUS_LOWSPEED; + + statusDescriptor->token = TD_TOKEN_NULL | TD_TOKEN_DATA1 + | (transfer->GetPipe()->EndpointAddress() << 15) + | (transfer->GetPipe()->DeviceAddress() << 8) | 0x2d; + + statusDescriptor->buffer_phy = statusDescriptor->buffer_log = 0; + statusDescriptor->link_phy = QH_TERMINATE; + statusDescriptor->link_log = 0; + + if (transfer->GetBuffer() && transfer->GetBufferLength() > 0) { + // ToDo: split up data to maxlen and create / link tds + } else { + // Link transfer and status descriptors directly + transferDescriptor->link_phy = statusDescriptor->this_phy | TD_DEPTH_FIRST; + transferDescriptor->link_log = statusDescriptor; + } + + status_t result = AppendDescriptor(transferDescriptor); + if (result < B_OK) { + TRACE(("usb_uhci: failed to append descriptors\n")); + fStack->FreeChunk(transferDescriptor->buffer_log, + (void *)transferDescriptor->buffer_phy, sizeof(usb_request_data)); + fStack->FreeChunk(transferDescriptor, + (void *)transferDescriptor->this_phy, 32); + fStack->FreeChunk(statusDescriptor, + (void *)statusDescriptor->this_phy, 32); + return result; + } + + if (timeout > 0) { + // wait for the transfer to finish + while (true) { + if ((statusDescriptor->status & TD_STATUS_ACTIVE) == 0) { + TRACE(("usb_uhci: transfer completed successfuly\n")); + return B_OK; + } + if (transferDescriptor->status & 0x7e00) { + TRACE(("usb_uhci: transfer failed: 0x%04x\n", transferDescriptor->status)); + return B_ERROR; + } + + TRACE(("usb_uhci: in progress\n")); + snooze(1000); + } + } + + return EINPROGRESS; +} + + +status_t +Queue::RemoveInactiveDescriptors() +{ + if (fQueueHead->element_phy & QH_TERMINATE) + return B_OK; + + uhci_td *element = (uhci_td *)fQueueHead->element_log; + while (element) { + if (element->status & TD_STATUS_ACTIVE) + break; + + fStack->FreeChunk(element, (void *)element->this_phy, 32); + + if (element->link_phy & TD_TERMINATE) { + fQueueHead->element_phy = QH_TERMINATE; + fQueueHead->element_log = NULL; + break; + } + + element = (uhci_td *)element->link_log; + } + + return B_OK; +} + + +addr_t +Queue::PhysicalAddress() +{ + return fQueueHead->this_phy; +} + + +// +// #pragma mark - +// + + UHCI::UHCI(pci_info *info, Stack *stack) : BusManager(), fPCIInfo(info), @@ -97,7 +332,7 @@ UHCI::UHCI(pci_info *info, Stack *stack) // do a global and host reset GlobalReset(); - if (Reset() < B_OK) { + if (ResetController() < B_OK) { TRACE(("usb_uhci: host failed to reset\n")); fInitOK = false; return; @@ -118,91 +353,24 @@ UHCI::UHCI(pci_info *info, Stack *stack) WriteReg32(UHCI_FRBASEADD, (uint32)physicalAddress); 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); - fInitOK = 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)); + for (int32 i = 0; i < 4; i++) { + fQueues[i] = new Queue(fStack); + if (fQueues[i]->InitCheck() < B_OK) { + TRACE(("usb_uhci: cannot create queues\n")); delete_area(fFrameArea); fInitOK = 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]; + if (i > 0) + fQueues[i - 1]->LinkTo(fQueues[i]); } - // Make sure the fQueueHeadTerminate terminates - fVirtualQueueHead[11]->link_phy = strayDescriptor->this_phy; - fVirtualQueueHead[11]->link_log = strayDescriptor; + // Make sure the last queue terminates + fQueues[3]->TerminateByStrayDescriptor(); - // 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; - } + for (int32 i = 0; i < 1024; i++) + fFrameList[i] = fQueues[0]->PhysicalAddress() | FRAMELIST_NEXT_IS_QH; TRACE(("usb_uhci: installing interrupt handler\n")); // Install the interrupt handler @@ -257,16 +425,16 @@ UHCI::Start() status_t -UHCI::SubmitTransfer(Transfer *transfer) +UHCI::SubmitTransfer(Transfer *transfer, bigtime_t timeout) { TRACE(("usb_uhci: submit packet called\n")); // Short circuit the root hub - if (transfer->GetPipe()->GetDeviceAddress() == fRootHubAddress) + if (transfer->GetPipe()->DeviceAddress() == fRootHubAddress) return fRootHub->SubmitTransfer(transfer); - if (transfer->GetPipe()->GetType() == Pipe::Control) - return InsertControl(transfer); + if (transfer->GetPipe()->Type() == Pipe::Control) + return fQueues[1]->AddTransfer(transfer, timeout); return B_ERROR; } @@ -283,7 +451,7 @@ UHCI::GlobalReset() status_t -UHCI::Reset() +UHCI::ResetController() { WriteReg16(UHCI_USBCMD, UHCI_USBCMD_HCRESET); @@ -298,6 +466,83 @@ UHCI::Reset() } +uint16 +UHCI::PortStatus(int32 index) +{ + if (index > 1) + return B_BAD_VALUE; + + TRACE(("usb_uhci: read port status of port: %d\n", index)); + return ReadReg16(UHCI_PORTSC1 + index * 2); +} + + +status_t +UHCI::SetPortStatus(int32 index, uint16 status) +{ + if (index > 1) + return B_BAD_VALUE; + + TRACE(("usb_uhci: set port status of port %d: 0x%04x\n", index, status)); + WriteReg16(UHCI_PORTSC1 + index * 2, status); + snooze(1000); + return B_OK; +} + + +status_t +UHCI::ResetPort(int32 index) +{ + if (index > 1) + return B_BAD_VALUE; + + TRACE(("usb_uhci: reset port %d\n", index)); + + uint32 port = UHCI_PORTSC1 + index * 2; + uint16 status = ReadReg16(port); + status &= UHCI_PORTSC_DATAMASK; + WriteReg16(port, status | UHCI_PORTSC_RESET); + snooze(250000); + + status = ReadReg16(port); + status &= UHCI_PORTSC_DATAMASK; + WriteReg16(port, status & ~UHCI_PORTSC_RESET); + snooze(1000); + + for (int32 i = 10; i > 0; i--) { + // try to enable the port + status = ReadReg16(port); + status &= UHCI_PORTSC_DATAMASK; + WriteReg16(port, status | UHCI_PORTSC_ENABLED); + snooze(50000); + + status = ReadReg16(port); + + if ((status & UHCI_PORTSC_CURSTAT) == 0) { + // no device connected. since we waited long enough we can assume + // that the port was reset and no device is connected. + break; + } + + if (status & (UHCI_PORTSC_STATCHA | UHCI_PORTSC_ENABCHA)) { + // port enabled changed or connection status were set. + // acknowledge either / both and wait again. + status &= UHCI_PORTSC_DATAMASK; + WriteReg16(port, status | UHCI_PORTSC_STATCHA | UHCI_PORTSC_ENABCHA); + continue; + } + + if (status & UHCI_PORTSC_ENABLED) { + // the port is enabled + break; + } + } + + TRACE(("usb_uhci: port was reset: 0x%04x\n", ReadReg16(port))); + return B_OK; +} + + int32 UHCI::InterruptHandler(void *data) { @@ -362,123 +607,6 @@ UHCI::Interrupt() } -status_t -UHCI::InsertControl(Transfer *transfer) -{ - TRACE(("usb_uhci: InsertControl() frnum %u, usbsts reg %u\n", - ReadReg16(UHCI_FRNUM), ReadReg16(UHCI_USBSTS))); - - // 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(transferDescriptor, - (void *)transferDescriptor->this_phy, 32); - fStack->FreeChunk(topQueueHead, (void *)topQueueHead->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 queue head 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 = 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 = topQueueHead; - TRACE(("usb_uhci: appended transfer to queue\n")); - } - - return EINPROGRESS; -} - - bool UHCI::AddTo(Stack &stack) { diff --git a/src/add-ons/kernel/busses/usb/uhci_rh.cpp b/src/add-ons/kernel/busses/usb/uhci_rh.cpp index 55696bf1b8..deb3dade0a 100644 --- a/src/add-ons/kernel/busses/usb/uhci_rh.cpp +++ b/src/add-ons/kernel/busses/usb/uhci_rh.cpp @@ -176,7 +176,7 @@ UHCIRootHub::SubmitTransfer(Transfer *transfer) result = B_OK; break; - case RH_CLEAR_FEATURE: + case RH_CLEAR_FEATURE: { if (request->Index == 0) { // We don't support any hub changes TRACE(("usb_uhci_roothub: RH_CLEAR_FEATURE no hub changes!\n")); @@ -190,28 +190,57 @@ UHCIRootHub::SubmitTransfer(Transfer *transfer) } TRACE(("usb_uhci_roothub: RH_CLEAR_FEATURE called. Feature: %u!\n", request->Value)); - uint16 port; + uint16 status; switch(request->Value) { case PORT_RESET: - port = UHCI::sPCIModule->read_io_16(fUHCI->fRegisterBase + UHCI_PORTSC1 + (request->Index - 1) * 2); - port &= ~UHCI_PORTSC_RESET; - TRACE(("usb_uhci_roothub: port %x Clear RESET\n", port)); - UHCI::sPCIModule->write_io_16(fUHCI->fRegisterBase + UHCI_PORTSC1 + (request->Index - 1) * 2, port); + status = fUHCI->PortStatus(request->Index - 1); + result = fUHCI->SetPortStatus(request->Index - 1, + status & UHCI_PORTSC_DATAMASK & ~UHCI_PORTSC_RESET); break; case C_PORT_CONNECTION: - port = UHCI::sPCIModule->read_io_16(fUHCI->fRegisterBase + UHCI_PORTSC1 + (request->Index - 1) * 2); - port = port & UHCI_PORTSC_DATAMASK; - port |= UHCI_PORTSC_STATCHA; - TRACE(("usb_uhci_roothub: port: %x\n", port)); - UHCI::sPCIModule->write_io_16(fUHCI->fRegisterBase + UHCI_PORTSC1 + (request->Index - 1) * 2, port); - result = B_OK; + status = fUHCI->PortStatus(request->Index - 1); + result = fUHCI->SetPortStatus(request->Index - 1, + (status & UHCI_PORTSC_DATAMASK) | UHCI_PORTSC_STATCHA); break; default: result = EINVAL; break; } break; + } + + case RH_SET_FEATURE: { + if (request->Index == 0) { + // We don't support any hub changes + TRACE(("usb_uhci_roothub: RH_SET_FEATURE no hub changes!\n")); + result = EINVAL; + break; + } else if (request->Index > uhci_hubd.bNbrPorts) { + // Invalid port number + TRACE(("usb_uhci_roothub: RH_SET_FEATURE invalid port!\n")); + result = EINVAL; + break; + } + + TRACE(("usb_uhci_roothub: RH_SET_FEATURE called. Feature: %u!\n", request->Value)); + uint16 status; + switch(request->Value) { + case PORT_RESET: + result = fUHCI->ResetPort(request->Index - 1); + break; + + case PORT_ENABLE: + status = fUHCI->PortStatus(request->Index - 1); + result = fUHCI->SetPortStatus(request->Index - 1, + (status & UHCI_PORTSC_DATAMASK) | UHCI_PORTSC_ENABLED); + break; + default: + result = EINVAL; + break; + } + break; + } default: result = EINVAL; @@ -232,7 +261,7 @@ UHCIRootHub::UpdatePortStatus() uint16 newStatus = 0; uint16 newChange = 0; - uint16 portStatus = UHCI::sPCIModule->read_io_16(fUHCI->fRegisterBase + UHCI_PORTSC1 + i * 2); + uint16 portStatus = fUHCI->PortStatus(i); TRACE(("usb_uhci_roothub: port: %d status: 0x%04x\n", i, portStatus)); // Set all individual bits