* Copyright 2005-2013, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jan-Rixt Van Hoye
* Salvatore Benedetto <salvatore.benedetto@gmail.com>
* Michael Lotz <mmlr@mlotz.ch>
* Siarzhuk Zharski <imker@gmx.li>
*/
#include <stdio.h>
#include <module.h>
#include <bus/PCI.h>
#include <USB3.h>
#include <KernelExport.h>
#include <util/AutoLock.h>
#include "ohci.h"
#define CALLED(x...) TRACE_MODULE("CALLED %s\n", __PRETTY_FUNCTION__)
#define USB_MODULE_NAME "ohci"
device_manager_info* gDeviceManager;
static usb_for_controller_interface* gUSB;
#define OHCI_PCI_DEVICE_MODULE_NAME "busses/usb/ohci/pci/driver_v1"
#define OHCI_PCI_USB_BUS_MODULE_NAME "busses/usb/ohci/device_v1"
typedef struct {
OHCI* ohci;
pci_device_module_info* pci;
pci_device* device;
pci_info pciinfo;
device_node* node;
device_node* driver_node;
} ohci_pci_sim_info;
static status_t
init_bus(device_node* node, void** bus_cookie)
{
CALLED();
driver_module_info* driver;
ohci_pci_sim_info* bus;
device_node* parent = gDeviceManager->get_parent_node(node);
gDeviceManager->get_driver(parent, &driver, (void**)&bus);
gDeviceManager->put_node(parent);
Stack *stack;
if (gUSB->get_stack((void**)&stack) != B_OK)
return B_ERROR;
OHCI *ohci = new(std::nothrow) OHCI(&bus->pciinfo, bus->pci, bus->device, stack, node);
if (ohci == NULL) {
return B_NO_MEMORY;
}
if (ohci->InitCheck() < B_OK) {
TRACE_MODULE_ERROR("bus failed init check\n");
delete ohci;
return B_ERROR;
}
if (ohci->Start() != B_OK) {
delete ohci;
return B_ERROR;
}
*bus_cookie = ohci;
return B_OK;
}
static void
uninit_bus(void* bus_cookie)
{
CALLED();
OHCI* ohci = (OHCI*)bus_cookie;
delete ohci;
}
static status_t
register_child_devices(void* cookie)
{
CALLED();
ohci_pci_sim_info* bus = (ohci_pci_sim_info*)cookie;
device_node* node = bus->driver_node;
char prettyName[25];
sprintf(prettyName, "OHCI Controller %" B_PRIu16, 0);
device_attr attrs[] = {
{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
{ .string = prettyName }},
{ B_DEVICE_FIXED_CHILD, B_STRING_TYPE,
{ .string = USB_FOR_CONTROLLER_MODULE_NAME }},
{ NULL }
};
return gDeviceManager->register_node(node, OHCI_PCI_USB_BUS_MODULE_NAME,
attrs, NULL, NULL);
}
static status_t
init_device(device_node* node, void** device_cookie)
{
CALLED();
ohci_pci_sim_info* bus = (ohci_pci_sim_info*)calloc(1,
sizeof(ohci_pci_sim_info));
if (bus == NULL)
return B_NO_MEMORY;
pci_device_module_info* pci;
pci_device* device;
{
device_node* pciParent = gDeviceManager->get_parent_node(node);
gDeviceManager->get_driver(pciParent, (driver_module_info**)&pci,
(void**)&device);
gDeviceManager->put_node(pciParent);
}
bus->pci = pci;
bus->device = device;
bus->driver_node = node;
pci_info *pciInfo = &bus->pciinfo;
pci->get_pci_info(device, pciInfo);
*device_cookie = bus;
return B_OK;
}
static void
uninit_device(void* device_cookie)
{
CALLED();
ohci_pci_sim_info* bus = (ohci_pci_sim_info*)device_cookie;
free(bus);
}
static status_t
register_device(device_node* parent)
{
CALLED();
device_attr attrs[] = {
{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "OHCI PCI"}},
{}
};
return gDeviceManager->register_node(parent,
OHCI_PCI_DEVICE_MODULE_NAME, attrs, NULL, NULL);
}
static float
supports_device(device_node* parent)
{
CALLED();
const char* bus;
uint16 type, subType, api;
if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)
< B_OK) {
return -1;
}
if (strcmp(bus, "pci") != 0)
return 0.0f;
if (gDeviceManager->get_attr_uint16(parent, B_DEVICE_SUB_TYPE, &subType,
false) < B_OK
|| gDeviceManager->get_attr_uint16(parent, B_DEVICE_TYPE, &type,
false) < B_OK
|| gDeviceManager->get_attr_uint16(parent, B_DEVICE_INTERFACE, &api,
false) < B_OK) {
TRACE_MODULE("Could not find type/subtype/interface attributes\n");
return -1;
}
if (type == PCI_serial_bus && subType == PCI_usb && api == PCI_usb_ohci) {
pci_device_module_info* pci;
pci_device* device;
gDeviceManager->get_driver(parent, (driver_module_info**)&pci,
(void**)&device);
TRACE_MODULE("OHCI Device found!\n");
return 0.8f;
}
return 0.0f;
}
module_dependency module_dependencies[] = {
{ USB_FOR_CONTROLLER_MODULE_NAME, (module_info**)&gUSB },
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
{}
};
static usb_bus_interface gOHCIPCIDeviceModule = {
{
{
OHCI_PCI_USB_BUS_MODULE_NAME,
0,
NULL
},
NULL,
NULL,
init_bus,
uninit_bus,
NULL,
NULL,
NULL,
},
};
static driver_module_info sOHCIDevice = {
{
OHCI_PCI_DEVICE_MODULE_NAME,
0,
NULL
},
supports_device,
register_device,
init_device,
uninit_device,
register_child_devices,
NULL,
NULL,
};
module_info* modules[] = {
(module_info* )&sOHCIDevice,
(module_info* )&gOHCIPCIDeviceModule,
NULL
};
OHCI::OHCI(pci_info *info, pci_device_module_info* pci, pci_device* device, Stack *stack,
device_node* node)
: BusManager(stack, node),
fPCIInfo(info),
fPci(pci),
fDevice(device),
fStack(stack),
fOperationalRegisters(NULL),
fRegisterArea(-1),
fHccaArea(-1),
fHcca(NULL),
fInterruptEndpoints(NULL),
fDummyControl(NULL),
fDummyBulk(NULL),
fDummyIsochronous(NULL),
fFirstTransfer(NULL),
fLastTransfer(NULL),
fFinishTransfersSem(-1),
fFinishThread(-1),
fStopFinishThread(false),
fProcessingPipe(NULL),
fFrameBandwidth(NULL),
fRootHub(NULL),
fRootHubAddress(0),
fPortCount(0),
fIRQ(0),
fUseMSI(false)
{
if (!fInitOK) {
TRACE_ERROR("bus manager failed to init\n");
return;
}
TRACE("constructing new OHCI host controller driver\n");
fInitOK = false;
mutex_init(&fEndpointLock, "ohci endpoint lock");
uint16 command = fPci->read_pci_config(fDevice, PCI_command, 2);
command &= ~PCI_command_io;
command |= PCI_command_master | PCI_command_memory;
fPci->write_pci_config(fDevice, PCI_command, 2, command);
uint32 offset = fPci->read_pci_config(fDevice, PCI_base_registers, 4);
offset &= PCI_address_memory_32_mask;
TRACE_ALWAYS("iospace offset: 0x%" B_PRIx32 "\n", offset);
fRegisterArea = map_physical_memory("OHCI memory mapped registers",
offset, B_PAGE_SIZE, B_ANY_KERNEL_BLOCK_ADDRESS,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
(void **)&fOperationalRegisters);
if (fRegisterArea < B_OK) {
TRACE_ERROR("failed to map register memory\n");
return;
}
TRACE("mapped operational registers: %p\n", fOperationalRegisters);
uint32 revision = _ReadReg(OHCI_REVISION) & 0xff;
TRACE("version %" B_PRId32 ".%" B_PRId32 "%s\n",
OHCI_REVISION_HIGH(revision), OHCI_REVISION_LOW(revision),
OHCI_REVISION_LEGACY(revision) ? ", legacy support" : "");
if (OHCI_REVISION_HIGH(revision) != 1 || OHCI_REVISION_LOW(revision) != 0) {
TRACE_ERROR("unsupported OHCI revision\n");
return;
}
phys_addr_t hccaPhysicalAddress;
fHccaArea = fStack->AllocateArea((void **)&fHcca, &hccaPhysicalAddress,
sizeof(ohci_hcca), "USB OHCI Host Controller Communication Area");
if (fHccaArea < B_OK) {
TRACE_ERROR("unable to create the HCCA block area\n");
return;
}
memset(fHcca, 0, sizeof(ohci_hcca));
fDummyControl = _AllocateEndpoint();
if (!fDummyControl)
return;
fDummyBulk = _AllocateEndpoint();
if (!fDummyBulk) {
_FreeEndpoint(fDummyControl);
return;
}
fDummyIsochronous = _AllocateEndpoint();
if (!fDummyIsochronous) {
_FreeEndpoint(fDummyControl);
_FreeEndpoint(fDummyBulk);
return;
}
fInterruptEndpoints = new(std::nothrow)
ohci_endpoint_descriptor *[OHCI_STATIC_ENDPOINT_COUNT];
if (!fInterruptEndpoints) {
TRACE_ERROR("failed to allocate memory for interrupt endpoints\n");
_FreeEndpoint(fDummyControl);
_FreeEndpoint(fDummyBulk);
_FreeEndpoint(fDummyIsochronous);
return;
}
for (int32 i = 0; i < OHCI_STATIC_ENDPOINT_COUNT; i++) {
fInterruptEndpoints[i] = _AllocateEndpoint();
if (!fInterruptEndpoints[i]) {
TRACE_ERROR("failed to allocate interrupt endpoint %" B_PRId32 "\n",
i);
while (--i >= 0)
_FreeEndpoint(fInterruptEndpoints[i]);
_FreeEndpoint(fDummyBulk);
_FreeEndpoint(fDummyControl);
_FreeEndpoint(fDummyIsochronous);
return;
}
}
uint32 interval = OHCI_BIGGEST_INTERVAL;
uint32 intervalIndex = OHCI_STATIC_ENDPOINT_COUNT - 1;
while (interval > 1) {
uint32 insertIndex = interval / 2;
while (insertIndex < OHCI_BIGGEST_INTERVAL) {
fHcca->interrupt_table[insertIndex]
= fInterruptEndpoints[intervalIndex]->physical_address;
insertIndex += interval;
}
intervalIndex--;
interval /= 2;
}
fHcca->interrupt_table[0] = fInterruptEndpoints[0]->physical_address;
for (int32 i = 1; i < OHCI_STATIC_ENDPOINT_COUNT; i++) {
fInterruptEndpoints[i]->next_physical_endpoint
= fInterruptEndpoints[0]->physical_address;
fInterruptEndpoints[i]->next_logical_endpoint
= fInterruptEndpoints[0];
}
fInterruptEndpoints[0]->next_physical_endpoint
= fDummyIsochronous->physical_address;
_WriteReg(OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTERRUPTS &
~OHCI_OWNERSHIP_CHANGE) ;
uint32 control = _ReadReg(OHCI_CONTROL);
if (control & OHCI_INTERRUPT_ROUTING) {
TRACE_ALWAYS("smm is in control of the host controller\n");
uint32 status = _ReadReg(OHCI_COMMAND_STATUS);
_WriteReg(OHCI_COMMAND_STATUS, status | OHCI_OWNERSHIP_CHANGE_REQUEST);
for (uint32 i = 0; i < 100 && (control & OHCI_INTERRUPT_ROUTING); i++) {
snooze(1000);
control = _ReadReg(OHCI_CONTROL);
}
if ((control & OHCI_INTERRUPT_ROUTING) != 0) {
TRACE_ERROR("smm does not respond.\n");
} else
TRACE_ALWAYS("ownership change successful\n");
} else {
TRACE("cold started\n");
snooze(USB_DELAY_BUS_RESET);
}
_WriteReg(OHCI_CONTROL, OHCI_HC_FUNCTIONAL_STATE_RESET);
snooze(USB_DELAY_BUS_RESET);
uint32 frameInterval = _ReadReg(OHCI_FRAME_INTERVAL);
uint32 intervalValue = OHCI_GET_INTERVAL_VALUE(frameInterval);
_WriteReg(OHCI_COMMAND_STATUS, OHCI_HOST_CONTROLLER_RESET);
uint32 reset = 0;
for (uint32 i = 0; i < 10; i++) {
spin(10);
reset = _ReadReg(OHCI_COMMAND_STATUS) & OHCI_HOST_CONTROLLER_RESET;
if (reset == 0)
break;
}
if (reset) {
TRACE_ERROR("error resetting the host controller (timeout)\n");
return;
}
_WriteReg(OHCI_HCCA, (uint32)hccaPhysicalAddress);
_WriteReg(OHCI_CONTROL_HEAD_ED, (uint32)fDummyControl->physical_address);
_WriteReg(OHCI_BULK_HEAD_ED, (uint32)fDummyBulk->physical_address);
control = _ReadReg(OHCI_CONTROL);
control &= ~(OHCI_CONTROL_BULK_SERVICE_RATIO_MASK | OHCI_ENABLE_LIST
| OHCI_HC_FUNCTIONAL_STATE_MASK | OHCI_INTERRUPT_ROUTING);
control |= OHCI_ENABLE_LIST | OHCI_CONTROL_BULK_RATIO_1_4
| OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL;
_WriteReg(OHCI_CONTROL, control);
frameInterval = (_ReadReg(OHCI_FRAME_INTERVAL) & OHCI_FRAME_INTERVAL_TOGGLE)
^ OHCI_FRAME_INTERVAL_TOGGLE;
frameInterval |= OHCI_FSMPS(intervalValue) | intervalValue;
_WriteReg(OHCI_FRAME_INTERVAL, frameInterval);
uint32 periodic = OHCI_PERIODIC(intervalValue);
_WriteReg(OHCI_PERIODIC_START, periodic);
uint32 desca = _ReadReg(OHCI_RH_DESCRIPTOR_A);
_WriteReg(OHCI_RH_DESCRIPTOR_A, desca | OHCI_RH_NO_OVER_CURRENT_PROTECTION);
_WriteReg(OHCI_RH_STATUS, OHCI_RH_LOCAL_POWER_STATUS_CHANGE);
snooze(OHCI_ENABLE_POWER_DELAY);
_WriteReg(OHCI_RH_DESCRIPTOR_A, desca);
uint32 numberOfPorts = 0;
for (uint32 i = 0; i < 10 && numberOfPorts == 0; i++) {
snooze(OHCI_READ_DESC_DELAY);
uint32 descriptor = _ReadReg(OHCI_RH_DESCRIPTOR_A);
numberOfPorts = OHCI_RH_GET_PORT_COUNT(descriptor);
}
if (numberOfPorts > OHCI_MAX_PORT_COUNT)
numberOfPorts = OHCI_MAX_PORT_COUNT;
fPortCount = numberOfPorts;
TRACE("port count is %d\n", fPortCount);
fFrameBandwidth = new(std::nothrow) uint16[NUMBER_OF_FRAMES];
for (int32 i = 0; i < NUMBER_OF_FRAMES; i++)
fFrameBandwidth[i] = MAX_AVAILABLE_BANDWIDTH;
fFinishTransfersSem = create_sem(0, "OHCI Finish Transfers");
if (fFinishTransfersSem < B_OK) {
TRACE_ERROR("failed to create semaphore\n");
return;
}
fFinishThread = spawn_kernel_thread(_FinishThread, "ohci finish thread",
B_URGENT_DISPLAY_PRIORITY, (void *)this);
resume_thread(fFinishThread);
fIRQ = fPCIInfo->u.h0.interrupt_line;
if (fIRQ == 0xFF)
fIRQ = 0;
if (fPci->get_msi_count(fDevice) >= 1) {
uint32 msiVector = 0;
if (fPci->configure_msi(fDevice, 1, &msiVector) == B_OK
&& fPci->enable_msi(fDevice) == B_OK) {
TRACE_ALWAYS("using message signaled interrupts\n");
fIRQ = msiVector;
fUseMSI = true;
}
}
if (fIRQ == 0) {
TRACE_MODULE_ERROR("device PCI:%d:%d:%d was assigned an invalid IRQ\n",
fPCIInfo->bus, fPCIInfo->device, fPCIInfo->function);
return;
}
TRACE("installing interrupt handler\n");
install_io_interrupt_handler(fIRQ, _InterruptHandler, (void *)this, 0);
_WriteReg(OHCI_INTERRUPT_ENABLE, OHCI_NORMAL_INTERRUPTS
| OHCI_MASTER_INTERRUPT_ENABLE);
TRACE("OHCI host controller driver constructed\n");
fInitOK = true;
}
OHCI::~OHCI()
{
int32 result = 0;
fStopFinishThread = true;
delete_sem(fFinishTransfersSem);
wait_for_thread(fFinishThread, &result);
remove_io_interrupt_handler(fIRQ, _InterruptHandler, (void *)this);
_LockEndpoints();
mutex_destroy(&fEndpointLock);
if (fHccaArea >= B_OK)
delete_area(fHccaArea);
if (fRegisterArea >= B_OK)
delete_area(fRegisterArea);
_FreeEndpoint(fDummyControl);
_FreeEndpoint(fDummyBulk);
_FreeEndpoint(fDummyIsochronous);
if (fInterruptEndpoints != NULL) {
for (int i = 0; i < OHCI_STATIC_ENDPOINT_COUNT; i++)
_FreeEndpoint(fInterruptEndpoints[i]);
}
delete [] fFrameBandwidth;
delete [] fInterruptEndpoints;
delete fRootHub;
if (fUseMSI) {
fPci->disable_msi(fDevice);
fPci->unconfigure_msi(fDevice);
}
}
status_t
OHCI::Start()
{
TRACE("starting OHCI host controller\n");
uint32 control = _ReadReg(OHCI_CONTROL);
if ((control & OHCI_HC_FUNCTIONAL_STATE_MASK)
!= OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL) {
TRACE_ERROR("controller not started (0x%08" B_PRIx32 ")!\n", control);
return B_ERROR;
} else
TRACE("controller is operational!\n");
fRootHubAddress = AllocateAddress();
fRootHub = new(std::nothrow) OHCIRootHub(RootObject(), fRootHubAddress);
if (!fRootHub) {
TRACE_ERROR("no memory to allocate root hub\n");
return B_NO_MEMORY;
}
if (fRootHub->InitCheck() < B_OK) {
TRACE_ERROR("root hub failed init check\n");
return B_ERROR;
}
SetRootHub(fRootHub);
fRootHub->RegisterNode(Node());
TRACE_ALWAYS("successfully started the controller\n");
return BusManager::Start();
}
status_t
OHCI::SubmitTransfer(Transfer *transfer)
{
if (transfer->TransferPipe()->DeviceAddress() == fRootHubAddress)
return fRootHub->ProcessTransfer(this, transfer);
uint32 type = transfer->TransferPipe()->Type();
if (type & USB_OBJECT_CONTROL_PIPE) {
TRACE("submitting request\n");
return _SubmitRequest(transfer);
}
if ((type & USB_OBJECT_BULK_PIPE) || (type & USB_OBJECT_INTERRUPT_PIPE)) {
TRACE("submitting %s transfer\n",
(type & USB_OBJECT_BULK_PIPE) ? "bulk" : "interrupt");
return _SubmitTransfer(transfer);
}
if (type & USB_OBJECT_ISO_PIPE) {
TRACE("submitting isochronous transfer\n");
return _SubmitIsochronousTransfer(transfer);
}
TRACE_ERROR("tried to submit transfer for unknown pipe type %" B_PRIu32 "\n",
type);
return B_ERROR;
}
status_t
OHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
{
if (!Lock())
return B_ERROR;
struct transfer_entry {
Transfer * transfer;
transfer_entry * next;
};
transfer_entry *list = NULL;
transfer_data *current = fFirstTransfer;
while (current) {
if (current->transfer && current->transfer->TransferPipe() == pipe) {
if (!(current->endpoint->flags & OHCI_ENDPOINT_SKIP)) {
current->endpoint->flags |= OHCI_ENDPOINT_SKIP;
snooze(1000);
}
current->endpoint->head_physical_descriptor
= current->endpoint->tail_physical_descriptor;
if (pipe->Type() & USB_OBJECT_ISO_PIPE) {
ohci_isochronous_td *descriptor
= (ohci_isochronous_td *)current->first_descriptor;
while (descriptor) {
uint16 frame = OHCI_ITD_GET_STARTING_FRAME(
descriptor->flags);
_ReleaseIsochronousBandwidth(frame,
OHCI_ITD_GET_FRAME_COUNT(descriptor->flags));
if (descriptor
== (ohci_isochronous_td*)current->last_descriptor)
break;
descriptor
= (ohci_isochronous_td *)
descriptor->next_done_descriptor;
}
}
transfer_entry *entry
= (transfer_entry *)malloc(sizeof(transfer_entry));
if (entry != NULL) {
entry->transfer = current->transfer;
current->transfer = NULL;
entry->next = list;
list = entry;
}
current->canceled = true;
}
current = current->link;
}
Unlock();
while (list != NULL) {
transfer_entry *next = list->next;
if (!force)
list->transfer->Finished(B_CANCELED, 0);
delete list->transfer;
free(list);
list = next;
}
while (fProcessingPipe == pipe)
snooze(1000);
release_sem_etc(fFinishTransfersSem, 1, B_DO_NOT_RESCHEDULE);
return B_OK;
}
status_t
OHCI::NotifyPipeChange(Pipe *pipe, usb_change change)
{
TRACE("pipe change %d for pipe %p\n", change, pipe);
if (pipe->DeviceAddress() == fRootHubAddress) {
return B_OK;
}
switch (change) {
case USB_CHANGE_CREATED:
return _InsertEndpointForPipe(pipe);
case USB_CHANGE_DESTROYED:
return _RemoveEndpointForPipe(pipe);
case USB_CHANGE_PIPE_POLICY_CHANGED:
TRACE("pipe policy changing unhandled!\n");
break;
default:
TRACE_ERROR("unknown pipe change!\n");
return B_ERROR;
}
return B_OK;
}
status_t
OHCI::GetPortStatus(uint8 index, usb_port_status *status)
{
if (index >= fPortCount) {
TRACE_ERROR("get port status for invalid port %u\n", index);
return B_BAD_INDEX;
}
status->status = status->change = 0;
uint32 portStatus = _ReadReg(OHCI_RH_PORT_STATUS(index));
if (portStatus & OHCI_RH_PORTSTATUS_CCS)
status->status |= PORT_STATUS_CONNECTION;
if (portStatus & OHCI_RH_PORTSTATUS_PES)
status->status |= PORT_STATUS_ENABLE;
if (portStatus & OHCI_RH_PORTSTATUS_PSS)
status->status |= PORT_STATUS_SUSPEND;
if (portStatus & OHCI_RH_PORTSTATUS_POCI)
status->status |= PORT_STATUS_OVER_CURRENT;
if (portStatus & OHCI_RH_PORTSTATUS_PRS)
status->status |= PORT_STATUS_RESET;
if (portStatus & OHCI_RH_PORTSTATUS_PPS)
status->status |= PORT_STATUS_POWER;
if (portStatus & OHCI_RH_PORTSTATUS_LSDA)
status->status |= PORT_STATUS_LOW_SPEED;
if (portStatus & OHCI_RH_PORTSTATUS_CSC)
status->change |= PORT_STATUS_CONNECTION;
if (portStatus & OHCI_RH_PORTSTATUS_PESC)
status->change |= PORT_STATUS_ENABLE;
if (portStatus & OHCI_RH_PORTSTATUS_PSSC)
status->change |= PORT_STATUS_SUSPEND;
if (portStatus & OHCI_RH_PORTSTATUS_OCIC)
status->change |= PORT_STATUS_OVER_CURRENT;
if (portStatus & OHCI_RH_PORTSTATUS_PRSC)
status->change |= PORT_STATUS_RESET;
TRACE("port %u status 0x%04x change 0x%04x\n", index,
status->status, status->change);
return B_OK;
}
status_t
OHCI::SetPortFeature(uint8 index, uint16 feature)
{
TRACE("set port feature index %u feature %u\n", index, feature);
if (index > fPortCount)
return B_BAD_INDEX;
switch (feature) {
case PORT_ENABLE:
_WriteReg(OHCI_RH_PORT_STATUS(index), OHCI_RH_PORTSTATUS_PES);
return B_OK;
case PORT_SUSPEND:
_WriteReg(OHCI_RH_PORT_STATUS(index), OHCI_RH_PORTSTATUS_PSS);
return B_OK;
case PORT_RESET:
_WriteReg(OHCI_RH_PORT_STATUS(index), OHCI_RH_PORTSTATUS_PRS);
return B_OK;
case PORT_POWER:
_WriteReg(OHCI_RH_PORT_STATUS(index), OHCI_RH_PORTSTATUS_PPS);
return B_OK;
}
return B_BAD_VALUE;
}
status_t
OHCI::ClearPortFeature(uint8 index, uint16 feature)
{
TRACE("clear port feature index %u feature %u\n", index, feature);
if (index > fPortCount)
return B_BAD_INDEX;
switch (feature) {
case PORT_ENABLE:
_WriteReg(OHCI_RH_PORT_STATUS(index), OHCI_RH_PORTSTATUS_CCS);
return B_OK;
case PORT_SUSPEND:
_WriteReg(OHCI_RH_PORT_STATUS(index), OHCI_RH_PORTSTATUS_POCI);
return B_OK;
case PORT_POWER:
_WriteReg(OHCI_RH_PORT_STATUS(index), OHCI_RH_PORTSTATUS_LSDA);
return B_OK;
case C_PORT_CONNECTION:
_WriteReg(OHCI_RH_PORT_STATUS(index), OHCI_RH_PORTSTATUS_CSC);
return B_OK;
case C_PORT_ENABLE:
_WriteReg(OHCI_RH_PORT_STATUS(index), OHCI_RH_PORTSTATUS_PESC);
return B_OK;
case C_PORT_SUSPEND:
_WriteReg(OHCI_RH_PORT_STATUS(index), OHCI_RH_PORTSTATUS_PSSC);
return B_OK;
case C_PORT_OVER_CURRENT:
_WriteReg(OHCI_RH_PORT_STATUS(index), OHCI_RH_PORTSTATUS_OCIC);
return B_OK;
case C_PORT_RESET:
_WriteReg(OHCI_RH_PORT_STATUS(index), OHCI_RH_PORTSTATUS_PRSC);
return B_OK;
}
return B_BAD_VALUE;
}
int32
OHCI::_InterruptHandler(void *data)
{
return ((OHCI *)data)->_Interrupt();
}
int32
OHCI::_Interrupt()
{
static spinlock lock = B_SPINLOCK_INITIALIZER;
acquire_spinlock(&lock);
uint32 status = 0;
uint32 acknowledge = 0;
bool finishTransfers = false;
int32 result = B_HANDLED_INTERRUPT;
uint32 doneHead = fHcca->done_head;
if (doneHead != 0) {
status = OHCI_WRITEBACK_DONE_HEAD;
if (doneHead & OHCI_DONE_INTERRUPTS)
status |= _ReadReg(OHCI_INTERRUPT_STATUS)
& _ReadReg(OHCI_INTERRUPT_ENABLE);
} else {
status = _ReadReg(OHCI_INTERRUPT_STATUS) & _ReadReg(OHCI_INTERRUPT_ENABLE)
& ~OHCI_WRITEBACK_DONE_HEAD;
if (status == 0) {
release_spinlock(&lock);
return B_UNHANDLED_INTERRUPT;
}
}
if (status & OHCI_SCHEDULING_OVERRUN) {
TRACE_MODULE("scheduling overrun occured\n");
acknowledge |= OHCI_SCHEDULING_OVERRUN;
}
if (status & OHCI_WRITEBACK_DONE_HEAD) {
TRACE_MODULE("transfer descriptors processed\n");
fHcca->done_head = 0;
acknowledge |= OHCI_WRITEBACK_DONE_HEAD;
result = B_INVOKE_SCHEDULER;
finishTransfers = true;
}
if (status & OHCI_RESUME_DETECTED) {
TRACE_MODULE("resume detected\n");
acknowledge |= OHCI_RESUME_DETECTED;
}
if (status & OHCI_UNRECOVERABLE_ERROR) {
TRACE_MODULE_ERROR("unrecoverable error - controller halted\n");
_WriteReg(OHCI_CONTROL, OHCI_HC_FUNCTIONAL_STATE_RESET);
}
if (status & OHCI_ROOT_HUB_STATUS_CHANGE) {
TRACE_MODULE("root hub status change\n");
_WriteReg(OHCI_INTERRUPT_DISABLE, OHCI_ROOT_HUB_STATUS_CHANGE);
acknowledge |= OHCI_ROOT_HUB_STATUS_CHANGE;
}
if (acknowledge != 0)
_WriteReg(OHCI_INTERRUPT_STATUS, acknowledge);
release_spinlock(&lock);
if (finishTransfers)
release_sem_etc(fFinishTransfersSem, 1, B_DO_NOT_RESCHEDULE);
return result;
}
status_t
OHCI::_AddPendingTransfer(Transfer *transfer,
ohci_endpoint_descriptor *endpoint, ohci_general_td *firstDescriptor,
ohci_general_td *dataDescriptor, ohci_general_td *lastDescriptor,
bool directionIn)
{
if (!transfer || !endpoint || !lastDescriptor)
return B_BAD_VALUE;
transfer_data *data = new(std::nothrow) transfer_data;
if (!data)
return B_NO_MEMORY;
status_t result = transfer->InitKernelAccess();
if (result < B_OK) {
delete data;
return result;
}
data->transfer = transfer;
data->endpoint = endpoint;
data->incoming = directionIn;
data->canceled = false;
data->link = NULL;
data->first_descriptor = (ohci_general_td *)endpoint->tail_logical_descriptor;
if (dataDescriptor == firstDescriptor)
data->data_descriptor = data->first_descriptor;
else
data->data_descriptor = dataDescriptor;
if (lastDescriptor == firstDescriptor)
data->last_descriptor = data->first_descriptor;
else
data->last_descriptor = lastDescriptor;
if (!Lock()) {
delete data;
return B_ERROR;
}
if (fLastTransfer)
fLastTransfer->link = data;
else
fFirstTransfer = data;
fLastTransfer = data;
Unlock();
return B_OK;
}
status_t
OHCI::_AddPendingIsochronousTransfer(Transfer *transfer,
ohci_endpoint_descriptor *endpoint, ohci_isochronous_td *firstDescriptor,
ohci_isochronous_td *lastDescriptor, bool directionIn)
{
if (!transfer || !endpoint || !lastDescriptor)
return B_BAD_VALUE;
transfer_data *data = new(std::nothrow) transfer_data;
if (!data)
return B_NO_MEMORY;
status_t result = transfer->InitKernelAccess();
if (result < B_OK) {
delete data;
return result;
}
data->transfer = transfer;
data->endpoint = endpoint;
data->incoming = directionIn;
data->canceled = false;
data->link = NULL;
data->first_descriptor = (ohci_general_td*)endpoint->tail_logical_descriptor;
data->data_descriptor = data->first_descriptor;
if (lastDescriptor == firstDescriptor)
data->last_descriptor = data->first_descriptor;
else
data->last_descriptor = (ohci_general_td*)lastDescriptor;
if (!Lock()) {
delete data;
return B_ERROR;
}
if (fLastTransfer)
fLastTransfer->link = data;
else
fFirstTransfer = data;
fLastTransfer = data;
Unlock();
return B_OK;
}
int32
OHCI::_FinishThread(void *data)
{
((OHCI *)data)->_FinishTransfers();
return B_OK;
}
void
OHCI::_FinishTransfers()
{
while (!fStopFinishThread) {
if (acquire_sem(fFinishTransfersSem) < B_OK)
continue;
int32 semCount = 0;
get_sem_count(fFinishTransfersSem, &semCount);
if (semCount > 0)
acquire_sem_etc(fFinishTransfersSem, semCount, B_RELATIVE_TIMEOUT, 0);
if (!Lock())
continue;
TRACE("finishing transfers (first transfer: %p; last"
" transfer: %p)\n", fFirstTransfer, fLastTransfer);
transfer_data *lastTransfer = NULL;
transfer_data *transfer = fFirstTransfer;
Unlock();
while (transfer) {
bool transferDone = false;
ohci_general_td *descriptor = transfer->first_descriptor;
ohci_endpoint_descriptor *endpoint = transfer->endpoint;
status_t callbackStatus = B_OK;
if (endpoint->flags & OHCI_ENDPOINT_ISOCHRONOUS_FORMAT) {
transfer_data *next = transfer->link;
if (_FinishIsochronousTransfer(transfer, &lastTransfer)) {
delete transfer->transfer;
delete transfer;
}
transfer = next;
continue;
}
MutexLocker endpointLocker(endpoint->lock);
if ((endpoint->head_physical_descriptor & OHCI_ENDPOINT_HEAD_MASK)
!= endpoint->tail_physical_descriptor
&& (endpoint->head_physical_descriptor
& OHCI_ENDPOINT_HALTED) == 0) {
TRACE("endpoint %p still has active tds\n", endpoint);
lastTransfer = transfer;
transfer = transfer->link;
continue;
}
endpointLocker.Unlock();
while (descriptor && !transfer->canceled) {
uint32 status = OHCI_TD_GET_CONDITION_CODE(descriptor->flags);
if (status == OHCI_TD_CONDITION_NOT_ACCESSED) {
TRACE("td %p still active\n", descriptor);
break;
}
if (status != OHCI_TD_CONDITION_NO_ERROR) {
if (endpoint->head_physical_descriptor & OHCI_ENDPOINT_HALTED) {
TRACE_ERROR("td error: 0x%08" B_PRIx32 "\n", status);
callbackStatus = _GetStatusOfConditionCode(status);
transferDone = true;
break;
} else {
TRACE("td %p active with error\n", descriptor);
break;
}
}
TRACE("td %p done\n", descriptor);
if (descriptor == transfer->last_descriptor
|| descriptor->buffer_physical != 0) {
callbackStatus = B_OK;
transferDone = true;
break;
}
descriptor
= (ohci_general_td *)descriptor->next_logical_descriptor;
}
if (transfer->canceled) {
callbackStatus = B_OK;
transferDone = true;
}
if (!transferDone) {
lastTransfer = transfer;
transfer = transfer->link;
continue;
}
transfer_data *next = transfer->link;
if (Lock()) {
if (lastTransfer)
lastTransfer->link = transfer->link;
if (transfer == fFirstTransfer)
fFirstTransfer = transfer->link;
if (transfer == fLastTransfer)
fLastTransfer = lastTransfer;
if (!transfer->canceled)
fProcessingPipe = transfer->transfer->TransferPipe();
transfer->link = NULL;
Unlock();
}
transfer->last_descriptor->next_logical_descriptor = NULL;
TRACE("transfer %p done with status 0x%08" B_PRIx32 "\n",
transfer, callbackStatus);
if (!transfer->canceled) {
size_t actualLength = 0;
if (callbackStatus == B_OK) {
if (transfer->data_descriptor && transfer->incoming) {
generic_io_vec *vector = transfer->transfer->Vector();
size_t vectorCount = transfer->transfer->VectorCount();
transfer->transfer->PrepareKernelAccess();
actualLength = _ReadDescriptorChain(
transfer->data_descriptor,
vector, vectorCount, transfer->transfer->IsPhysical());
} else if (transfer->data_descriptor) {
actualLength = _ReadActualLength(
transfer->data_descriptor);
}
transfer->transfer->TransferPipe()->SetDataToggle(
(endpoint->head_physical_descriptor
& OHCI_ENDPOINT_TOGGLE_CARRY) != 0);
if (transfer->transfer->IsFragmented()) {
TRACE("advancing fragmented transfer\n");
transfer->transfer->AdvanceByFragment(actualLength);
if (transfer->transfer->FragmentLength() > 0) {
TRACE("still %ld bytes left on transfer\n",
transfer->transfer->FragmentLength());
}
actualLength = 0;
}
}
transfer->transfer->Finished(callbackStatus, actualLength);
fProcessingPipe = NULL;
}
if (callbackStatus != B_OK) {
_RemoveTransferFromEndpoint(transfer);
}
_FreeDescriptorChain(transfer->first_descriptor);
delete transfer->transfer;
delete transfer;
transfer = next;
}
}
}
bool
OHCI::_FinishIsochronousTransfer(transfer_data *transfer,
transfer_data **_lastTransfer)
{
status_t callbackStatus = B_OK;
size_t actualLength = 0;
uint32 packet = 0;
if (transfer->canceled)
callbackStatus = B_CANCELED;
else {
ohci_isochronous_td *descriptor
= (ohci_isochronous_td *)transfer->first_descriptor;
while (descriptor) {
if (OHCI_TD_GET_CONDITION_CODE(descriptor->flags)
== OHCI_TD_CONDITION_NOT_ACCESSED) {
TRACE("ITD %p still active\n", descriptor);
*_lastTransfer = transfer;
return false;
}
if (descriptor == (ohci_isochronous_td*)transfer->last_descriptor) {
descriptor = (ohci_isochronous_td *)transfer->first_descriptor;
break;
}
descriptor
= (ohci_isochronous_td *)descriptor->next_done_descriptor;
}
while (descriptor) {
uint32 status = OHCI_TD_GET_CONDITION_CODE(descriptor->flags);
if (status != OHCI_TD_CONDITION_NO_ERROR) {
TRACE_ERROR("ITD error: 0x%08" B_PRIx32 "\n", status);
if (callbackStatus == B_OK)
callbackStatus = _GetStatusOfConditionCode(status);
}
usb_isochronous_data *isochronousData
= transfer->transfer->IsochronousData();
uint32 frameCount = OHCI_ITD_GET_FRAME_COUNT(descriptor->flags);
for (size_t i = 0; i < frameCount; i++, packet++) {
usb_iso_packet_descriptor* packet_descriptor
= &isochronousData->packet_descriptors[packet];
uint16 offset = descriptor->offset[OHCI_ITD_OFFSET_IDX(i)];
uint8 code = OHCI_ITD_GET_BUFFER_CONDITION_CODE(offset);
packet_descriptor->status = _GetStatusOfConditionCode(code);
if (packet_descriptor->status == B_DEV_TOO_LATE)
continue;
size_t len = OHCI_ITD_GET_BUFFER_LENGTH(offset);
if (!transfer->incoming)
len = packet_descriptor->request_length - len;
packet_descriptor->actual_length = len;
actualLength += len;
}
uint16 frame = OHCI_ITD_GET_STARTING_FRAME(descriptor->flags);
_ReleaseIsochronousBandwidth(frame,
OHCI_ITD_GET_FRAME_COUNT(descriptor->flags));
TRACE("ITD %p done\n", descriptor);
if (descriptor == (ohci_isochronous_td*)transfer->last_descriptor)
break;
descriptor
= (ohci_isochronous_td *)descriptor->next_done_descriptor;
}
}
if (Lock()) {
if (*_lastTransfer)
(*_lastTransfer)->link = transfer->link;
if (transfer == fFirstTransfer)
fFirstTransfer = transfer->link;
if (transfer == fLastTransfer)
fLastTransfer = *_lastTransfer;
if (!transfer->canceled)
fProcessingPipe = transfer->transfer->TransferPipe();
transfer->link = NULL;
Unlock();
}
transfer->last_descriptor->next_logical_descriptor = NULL;
TRACE("iso.transfer %p done with status 0x%08" B_PRIx32 " len:%ld\n",
transfer, callbackStatus, actualLength);
if (!transfer->canceled) {
if (callbackStatus == B_OK && actualLength > 0) {
if (transfer->data_descriptor && transfer->incoming) {
generic_io_vec *vector = transfer->transfer->Vector();
size_t vectorCount = transfer->transfer->VectorCount();
transfer->transfer->PrepareKernelAccess();
_ReadIsochronousDescriptorChain(
(ohci_isochronous_td*)transfer->data_descriptor,
vector, vectorCount, transfer->transfer->IsPhysical());
}
}
transfer->transfer->Finished(callbackStatus, actualLength);
fProcessingPipe = NULL;
}
_FreeIsochronousDescriptorChain(
(ohci_isochronous_td*)transfer->first_descriptor);
return true;
}
status_t
OHCI::_SubmitRequest(Transfer *transfer)
{
usb_request_data *requestData = transfer->RequestData();
bool directionIn = (requestData->RequestType & USB_REQTYPE_DEVICE_IN) != 0;
ohci_general_td *setupDescriptor
= _CreateGeneralDescriptor(sizeof(usb_request_data));
if (!setupDescriptor) {
TRACE_ERROR("failed to allocate setup descriptor\n");
return B_NO_MEMORY;
}
setupDescriptor->flags = OHCI_TD_DIRECTION_PID_SETUP
| OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED)
| OHCI_TD_TOGGLE_0
| OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE);
ohci_general_td *statusDescriptor = _CreateGeneralDescriptor(0);
if (!statusDescriptor) {
TRACE_ERROR("failed to allocate status descriptor\n");
_FreeGeneralDescriptor(setupDescriptor);
return B_NO_MEMORY;
}
statusDescriptor->flags
= (directionIn ? OHCI_TD_DIRECTION_PID_OUT : OHCI_TD_DIRECTION_PID_IN)
| OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED)
| OHCI_TD_TOGGLE_1
| OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE);
generic_io_vec vector;
vector.base = (generic_addr_t)requestData;
vector.length = sizeof(usb_request_data);
_WriteDescriptorChain(setupDescriptor, &vector, 1, false);
status_t result;
ohci_general_td *dataDescriptor = NULL;
if (transfer->VectorCount() > 0) {
ohci_general_td *lastDescriptor = NULL;
result = _CreateDescriptorChain(&dataDescriptor, &lastDescriptor,
directionIn ? OHCI_TD_DIRECTION_PID_IN : OHCI_TD_DIRECTION_PID_OUT,
transfer->FragmentLength());
if (result < B_OK) {
_FreeGeneralDescriptor(setupDescriptor);
_FreeGeneralDescriptor(statusDescriptor);
return result;
}
if (!directionIn) {
_WriteDescriptorChain(dataDescriptor, transfer->Vector(),
transfer->VectorCount(), transfer->IsPhysical());
}
_LinkDescriptors(setupDescriptor, dataDescriptor);
_LinkDescriptors(lastDescriptor, statusDescriptor);
} else {
_LinkDescriptors(setupDescriptor, statusDescriptor);
}
ohci_endpoint_descriptor *endpoint
= (ohci_endpoint_descriptor *)transfer->TransferPipe()->ControllerCookie();
MutexLocker endpointLocker(endpoint->lock);
result = _AddPendingTransfer(transfer, endpoint, setupDescriptor,
dataDescriptor, statusDescriptor, directionIn);
if (result < B_OK) {
TRACE_ERROR("failed to add pending transfer\n");
_FreeDescriptorChain(setupDescriptor);
return result;
}
_SwitchEndpointTail(endpoint, setupDescriptor, statusDescriptor);
endpointLocker.Unlock();
endpoint->flags &= ~OHCI_ENDPOINT_SKIP;
_WriteReg(OHCI_COMMAND_STATUS, OHCI_CONTROL_LIST_FILLED);
return B_OK;
}
status_t
OHCI::_SubmitTransfer(Transfer *transfer)
{
Pipe *pipe = transfer->TransferPipe();
bool directionIn = (pipe->Direction() == Pipe::In);
ohci_general_td *firstDescriptor = NULL;
ohci_general_td *lastDescriptor = NULL;
status_t result = _CreateDescriptorChain(&firstDescriptor, &lastDescriptor,
directionIn ? OHCI_TD_DIRECTION_PID_IN : OHCI_TD_DIRECTION_PID_OUT,
transfer->FragmentLength());
if (result < B_OK)
return result;
firstDescriptor->flags &= ~OHCI_TD_TOGGLE_CARRY;
firstDescriptor->flags |= pipe->DataToggle() ? OHCI_TD_TOGGLE_1
: OHCI_TD_TOGGLE_0;
lastDescriptor->flags &= ~OHCI_TD_INTERRUPT_MASK;
lastDescriptor->flags |=
OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE);
if (!directionIn) {
_WriteDescriptorChain(firstDescriptor, transfer->Vector(),
transfer->VectorCount(), transfer->IsPhysical());
}
ohci_endpoint_descriptor *endpoint
= (ohci_endpoint_descriptor *)pipe->ControllerCookie();
MutexLocker endpointLocker(endpoint->lock);
transfer_data *it = fFirstTransfer;
while (it) {
if (it->transfer && it->transfer->TransferPipe() == pipe && it->transfer->IsFragmented()) {
TRACE_ERROR("cannot submit transfer: a fragmented transfer is queued\n");
_FreeDescriptorChain(firstDescriptor);
return B_DEV_RESOURCE_CONFLICT;
}
it = it->link;
}
result = _AddPendingTransfer(transfer, endpoint, firstDescriptor,
firstDescriptor, lastDescriptor, directionIn);
if (result < B_OK) {
TRACE_ERROR("failed to add pending transfer\n");
_FreeDescriptorChain(firstDescriptor);
return result;
}
_SwitchEndpointTail(endpoint, firstDescriptor, lastDescriptor);
endpointLocker.Unlock();
endpoint->flags &= ~OHCI_ENDPOINT_SKIP;
if (pipe->Type() & USB_OBJECT_BULK_PIPE) {
_WriteReg(OHCI_COMMAND_STATUS, OHCI_BULK_LIST_FILLED);
}
return B_OK;
}
status_t
OHCI::_SubmitIsochronousTransfer(Transfer *transfer)
{
Pipe *pipe = transfer->TransferPipe();
bool directionIn = (pipe->Direction() == Pipe::In);
ohci_isochronous_td *firstDescriptor = NULL;
ohci_isochronous_td *lastDescriptor = NULL;
status_t result = _CreateIsochronousDescriptorChain(&firstDescriptor,
&lastDescriptor, transfer);
if (firstDescriptor == 0 || lastDescriptor == 0)
return B_ERROR;
if (result < B_OK)
return result;
lastDescriptor->flags &= ~OHCI_ITD_INTERRUPT_MASK;
lastDescriptor->flags |= OHCI_ITD_SET_DELAY_INTERRUPT(1);
if (pipe->Direction() == Pipe::Out)
_WriteIsochronousDescriptorChain(firstDescriptor,
transfer->Vector(), transfer->VectorCount(), transfer->IsPhysical());
ohci_endpoint_descriptor *endpoint
= (ohci_endpoint_descriptor *)pipe->ControllerCookie();
MutexLocker endpointLocker(endpoint->lock);
result = _AddPendingIsochronousTransfer(transfer, endpoint,
firstDescriptor, lastDescriptor, directionIn);
if (result < B_OK) {
TRACE_ERROR("failed to add pending iso.transfer:"
"0x%08" B_PRIx32 "\n", result);
_FreeIsochronousDescriptorChain(firstDescriptor);
return result;
}
_SwitchIsochronousEndpointTail(endpoint, firstDescriptor, lastDescriptor);
endpointLocker.Unlock();
endpoint->flags &= ~OHCI_ENDPOINT_SKIP;
return B_OK;
}
void
OHCI::_SwitchEndpointTail(ohci_endpoint_descriptor *endpoint,
ohci_general_td *first, ohci_general_td *last)
{
ohci_general_td *tail = (ohci_general_td *)endpoint->tail_logical_descriptor;
tail->flags = first->flags;
tail->buffer_physical = first->buffer_physical;
tail->next_physical_descriptor = first->next_physical_descriptor;
tail->last_physical_byte_address = first->last_physical_byte_address;
tail->buffer_size = first->buffer_size;
tail->buffer_logical = first->buffer_logical;
tail->next_logical_descriptor = first->next_logical_descriptor;
first->flags = 0;
first->buffer_physical = 0;
first->next_physical_descriptor = 0;
first->last_physical_byte_address = 0;
first->buffer_size = 0;
first->buffer_logical = NULL;
first->next_logical_descriptor = NULL;
if (first == last)
_LinkDescriptors(tail, first);
else
_LinkDescriptors(last, first);
endpoint->tail_logical_descriptor = first;
endpoint->tail_physical_descriptor = (uint32)first->physical_address;
TRACE("switched tail from %p to %p\n", tail, first);
#if 0
_PrintEndpoint(endpoint);
_PrintDescriptorChain(tail);
#endif
}
void
OHCI::_SwitchIsochronousEndpointTail(ohci_endpoint_descriptor *endpoint,
ohci_isochronous_td *first, ohci_isochronous_td *last)
{
ohci_isochronous_td *tail
= (ohci_isochronous_td*)endpoint->tail_logical_descriptor;
tail->flags = first->flags;
tail->buffer_page_byte_0 = first->buffer_page_byte_0;
tail->next_physical_descriptor = first->next_physical_descriptor;
tail->last_byte_address = first->last_byte_address;
tail->buffer_size = first->buffer_size;
tail->buffer_logical = first->buffer_logical;
tail->next_logical_descriptor = first->next_logical_descriptor;
tail->next_done_descriptor = first->next_done_descriptor;
first->flags = 0;
first->buffer_page_byte_0 = 0;
first->next_physical_descriptor = 0;
first->last_byte_address = 0;
first->buffer_size = 0;
first->buffer_logical = NULL;
first->next_logical_descriptor = NULL;
first->next_done_descriptor = NULL;
for (int i = 0; i < OHCI_ITD_NOFFSET; i++) {
tail->offset[i] = first->offset[i];
first->offset[i] = 0;
}
if (first == last)
_LinkIsochronousDescriptors(tail, first, NULL);
else
_LinkIsochronousDescriptors(last, first, NULL);
endpoint->tail_logical_descriptor = first;
endpoint->tail_physical_descriptor = (uint32)first->physical_address;
TRACE("switched tail from %p to %p\n", tail, first);
#if 0
_PrintEndpoint(endpoint);
_PrintDescriptorChain(tail);
#endif
}
void
OHCI::_RemoveTransferFromEndpoint(transfer_data *transfer)
{
ohci_endpoint_descriptor *endpoint = transfer->endpoint;
ohci_general_td *descriptor = transfer->first_descriptor;
while (descriptor) {
if ((endpoint->head_physical_descriptor & OHCI_ENDPOINT_HEAD_MASK)
== descriptor->physical_address) {
endpoint->head_physical_descriptor
= transfer->last_descriptor->next_physical_descriptor;
return;
}
descriptor = (ohci_general_td *)descriptor->next_logical_descriptor;
}
}
ohci_endpoint_descriptor *
OHCI::_AllocateEndpoint()
{
ohci_endpoint_descriptor *endpoint;
phys_addr_t physicalAddress;
mutex *lock = (mutex *)malloc(sizeof(mutex));
if (lock == NULL) {
TRACE_ERROR("no memory to allocate endpoint lock\n");
return NULL;
}
if (fStack->AllocateChunk((void **)&endpoint, &physicalAddress,
sizeof(ohci_endpoint_descriptor)) < B_OK) {
TRACE_ERROR("failed to allocate endpoint descriptor\n");
free(lock);
return NULL;
}
mutex_init(lock, "ohci endpoint lock");
endpoint->flags = OHCI_ENDPOINT_SKIP;
endpoint->physical_address = (uint32)physicalAddress;
endpoint->head_physical_descriptor = 0;
endpoint->tail_logical_descriptor = NULL;
endpoint->tail_physical_descriptor = 0;
endpoint->next_logical_endpoint = NULL;
endpoint->next_physical_endpoint = 0;
endpoint->lock = lock;
return endpoint;
}
void
OHCI::_FreeEndpoint(ohci_endpoint_descriptor *endpoint)
{
if (!endpoint)
return;
mutex_destroy(endpoint->lock);
free(endpoint->lock);
fStack->FreeChunk((void *)endpoint, endpoint->physical_address,
sizeof(ohci_endpoint_descriptor));
}
status_t
OHCI::_InsertEndpointForPipe(Pipe *pipe)
{
TRACE("inserting endpoint for device %u endpoint %u\n",
pipe->DeviceAddress(), pipe->EndpointAddress());
ohci_endpoint_descriptor *endpoint = _AllocateEndpoint();
if (!endpoint) {
TRACE_ERROR("cannot allocate memory for endpoint\n");
return B_NO_MEMORY;
}
uint32 flags = OHCI_ENDPOINT_SKIP;
flags |= OHCI_ENDPOINT_SET_DEVICE_ADDRESS(pipe->DeviceAddress())
| OHCI_ENDPOINT_SET_ENDPOINT_NUMBER(pipe->EndpointAddress());
switch (pipe->Direction()) {
case Pipe::In:
flags |= OHCI_ENDPOINT_DIRECTION_IN;
break;
case Pipe::Out:
flags |= OHCI_ENDPOINT_DIRECTION_OUT;
break;
case Pipe::Default:
flags |= OHCI_ENDPOINT_DIRECTION_DESCRIPTOR;
break;
default:
TRACE_ERROR("direction unknown\n");
_FreeEndpoint(endpoint);
return B_ERROR;
}
switch (pipe->Speed()) {
case USB_SPEED_LOWSPEED:
flags |= OHCI_ENDPOINT_LOW_SPEED;
break;
case USB_SPEED_FULLSPEED:
flags |= OHCI_ENDPOINT_FULL_SPEED;
break;
default:
TRACE_ERROR("unacceptable speed\n");
_FreeEndpoint(endpoint);
return B_ERROR;
}
flags |= OHCI_ENDPOINT_SET_MAX_PACKET_SIZE(pipe->MaxPacketSize());
endpoint->flags = flags;
uint32 type = pipe->Type();
ohci_endpoint_descriptor *head = NULL;
if (type & USB_OBJECT_CONTROL_PIPE)
head = fDummyControl;
else if (type & USB_OBJECT_BULK_PIPE)
head = fDummyBulk;
else if (type & USB_OBJECT_INTERRUPT_PIPE)
head = _FindInterruptEndpoint(pipe->Interval());
else if (type & USB_OBJECT_ISO_PIPE)
head = fDummyIsochronous;
else
TRACE_ERROR("unknown pipe type\n");
if (head == NULL) {
TRACE_ERROR("no list found for endpoint\n");
_FreeEndpoint(endpoint);
return B_ERROR;
}
if (pipe->Type() & USB_OBJECT_ISO_PIPE) {
endpoint->flags |= OHCI_ENDPOINT_ISOCHRONOUS_FORMAT;
ohci_isochronous_td *tail = _CreateIsochronousDescriptor(0);
tail->flags = 0;
endpoint->tail_logical_descriptor = tail;
endpoint->head_physical_descriptor = tail->physical_address;
endpoint->tail_physical_descriptor = tail->physical_address;
} else {
ohci_general_td *tail = _CreateGeneralDescriptor(0);
tail->flags = 0;
endpoint->tail_logical_descriptor = tail;
endpoint->head_physical_descriptor = tail->physical_address;
endpoint->tail_physical_descriptor = tail->physical_address;
}
if (!_LockEndpoints()) {
if (endpoint->tail_logical_descriptor) {
_FreeGeneralDescriptor(
(ohci_general_td *)endpoint->tail_logical_descriptor);
}
_FreeEndpoint(endpoint);
return B_ERROR;
}
pipe->SetControllerCookie((void *)endpoint);
endpoint->next_logical_endpoint = head->next_logical_endpoint;
endpoint->next_physical_endpoint = head->next_physical_endpoint;
head->next_logical_endpoint = (void *)endpoint;
head->next_physical_endpoint = (uint32)endpoint->physical_address;
_UnlockEndpoints();
return B_OK;
}
status_t
OHCI::_RemoveEndpointForPipe(Pipe *pipe)
{
TRACE("removing endpoint for device %u endpoint %u\n",
pipe->DeviceAddress(), pipe->EndpointAddress());
ohci_endpoint_descriptor *endpoint
= (ohci_endpoint_descriptor *)pipe->ControllerCookie();
if (endpoint == NULL)
return B_OK;
endpoint->flags |= OHCI_ENDPOINT_SKIP;
return B_OK;
}
ohci_endpoint_descriptor *
OHCI::_FindInterruptEndpoint(uint8 interval)
{
uint32 index = 0;
uint32 power = 1;
while (power <= OHCI_BIGGEST_INTERVAL / 2) {
if (power * 2 > interval)
break;
power *= 2;
index++;
}
return fInterruptEndpoints[index];
}
ohci_general_td *
OHCI::_CreateGeneralDescriptor(size_t bufferSize)
{
ohci_general_td *descriptor;
phys_addr_t physicalAddress;
if (fStack->AllocateChunk((void **)&descriptor, &physicalAddress,
sizeof(ohci_general_td)) != B_OK) {
TRACE_ERROR("failed to allocate general descriptor\n");
return NULL;
}
descriptor->physical_address = (uint32)physicalAddress;
descriptor->next_physical_descriptor = 0;
descriptor->next_logical_descriptor = NULL;
descriptor->buffer_size = bufferSize;
if (bufferSize == 0) {
descriptor->buffer_physical = 0;
descriptor->buffer_logical = NULL;
descriptor->last_physical_byte_address = 0;
return descriptor;
}
if (fStack->AllocateChunk(&descriptor->buffer_logical,
&physicalAddress, bufferSize) != B_OK) {
TRACE_ERROR("failed to allocate space for buffer\n");
fStack->FreeChunk(descriptor, descriptor->physical_address,
sizeof(ohci_general_td));
return NULL;
}
descriptor->buffer_physical = physicalAddress;
descriptor->last_physical_byte_address
= descriptor->buffer_physical + bufferSize - 1;
return descriptor;
}
void
OHCI::_FreeGeneralDescriptor(ohci_general_td *descriptor)
{
if (!descriptor)
return;
if (descriptor->buffer_logical) {
fStack->FreeChunk(descriptor->buffer_logical,
descriptor->buffer_physical, descriptor->buffer_size);
}
fStack->FreeChunk((void *)descriptor, descriptor->physical_address,
sizeof(ohci_general_td));
}
status_t
OHCI::_CreateDescriptorChain(ohci_general_td **_firstDescriptor,
ohci_general_td **_lastDescriptor, uint32 direction, size_t bufferSize)
{
size_t blockSize = 8192;
int32 descriptorCount = (bufferSize + blockSize - 1) / blockSize;
if (descriptorCount == 0)
descriptorCount = 1;
ohci_general_td *firstDescriptor = NULL;
ohci_general_td *lastDescriptor = *_firstDescriptor;
for (int32 i = 0; i < descriptorCount; i++) {
ohci_general_td *descriptor = _CreateGeneralDescriptor(
min_c(blockSize, bufferSize));
if (!descriptor) {
_FreeDescriptorChain(firstDescriptor);
return B_NO_MEMORY;
}
descriptor->flags = direction
| OHCI_TD_BUFFER_ROUNDING
| OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED)
| OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE)
| OHCI_TD_TOGGLE_CARRY;
if (lastDescriptor)
_LinkDescriptors(lastDescriptor, descriptor);
bufferSize -= blockSize;
lastDescriptor = descriptor;
if (!firstDescriptor)
firstDescriptor = descriptor;
}
*_firstDescriptor = firstDescriptor;
*_lastDescriptor = lastDescriptor;
return B_OK;
}
void
OHCI::_FreeDescriptorChain(ohci_general_td *topDescriptor)
{
ohci_general_td *current = topDescriptor;
ohci_general_td *next = NULL;
while (current) {
next = (ohci_general_td *)current->next_logical_descriptor;
_FreeGeneralDescriptor(current);
current = next;
}
}
ohci_isochronous_td *
OHCI::_CreateIsochronousDescriptor(size_t bufferSize)
{
ohci_isochronous_td *descriptor = NULL;
phys_addr_t physicalAddress;
if (fStack->AllocateChunk((void **)&descriptor, &physicalAddress,
sizeof(ohci_isochronous_td)) != B_OK) {
TRACE_ERROR("failed to allocate isochronous descriptor\n");
return NULL;
}
descriptor->physical_address = (uint32)physicalAddress;
descriptor->next_physical_descriptor = 0;
descriptor->next_logical_descriptor = NULL;
descriptor->next_done_descriptor = NULL;
descriptor->buffer_size = bufferSize;
if (bufferSize == 0) {
descriptor->buffer_page_byte_0 = 0;
descriptor->buffer_logical = NULL;
descriptor->last_byte_address = 0;
return descriptor;
}
if (fStack->AllocateChunk(&descriptor->buffer_logical,
&physicalAddress, bufferSize) != B_OK) {
TRACE_ERROR("failed to allocate space for iso.buffer\n");
fStack->FreeChunk(descriptor, descriptor->physical_address,
sizeof(ohci_isochronous_td));
return NULL;
}
descriptor->buffer_page_byte_0 = (uint32)physicalAddress;
descriptor->last_byte_address
= descriptor->buffer_page_byte_0 + bufferSize - 1;
return descriptor;
}
void
OHCI::_FreeIsochronousDescriptor(ohci_isochronous_td *descriptor)
{
if (!descriptor)
return;
if (descriptor->buffer_logical) {
fStack->FreeChunk(descriptor->buffer_logical,
descriptor->buffer_page_byte_0, descriptor->buffer_size);
}
fStack->FreeChunk((void *)descriptor, descriptor->physical_address,
sizeof(ohci_general_td));
}
status_t
OHCI::_CreateIsochronousDescriptorChain(ohci_isochronous_td **_firstDescriptor,
ohci_isochronous_td **_lastDescriptor, Transfer *transfer)
{
Pipe *pipe = transfer->TransferPipe();
usb_isochronous_data *isochronousData = transfer->IsochronousData();
size_t dataLength = transfer->FragmentLength();
size_t packet_count = isochronousData->packet_count;
if (packet_count == 0) {
TRACE_ERROR("isochronous packet_count should not be equal to zero.");
return B_BAD_VALUE;
}
size_t packetSize = dataLength / packet_count;
if (dataLength % packet_count != 0)
packetSize++;
if (packetSize > pipe->MaxPacketSize()) {
TRACE_ERROR("isochronous packetSize %ld is bigger"
" than pipe MaxPacketSize %ld.", packetSize, pipe->MaxPacketSize());
return B_BAD_VALUE;
}
uint16 bandwidth = transfer->Bandwidth() / packet_count;
if (transfer->Bandwidth() % packet_count != 0)
bandwidth++;
ohci_isochronous_td *firstDescriptor = NULL;
ohci_isochronous_td *lastDescriptor = *_firstDescriptor;
uint16 currentFrame = fHcca->current_frame_number & 0xFFFF;
uint16 safeFrames = 5;
if (isochronousData->flags & USB_ISO_ASAP ||
isochronousData->starting_frame_number == NULL)
currentFrame += safeFrames;
else
currentFrame = *isochronousData->starting_frame_number;
uint16 packets = packet_count;
uint16 frameOffset = 0;
while (packets > 0) {
uint16 frameCount = 0;
while (frameCount < min_c(OHCI_ITD_NOFFSET, packets)
&& _AllocateIsochronousBandwidth(frameOffset + currentFrame
+ frameCount, bandwidth))
frameCount++;
if (frameCount == 0) {
if (++frameOffset >= 0xFFFF) {
TRACE_ERROR("failed to allocate bandwidth\n");
_FreeIsochronousDescriptorChain(firstDescriptor);
return B_NO_MEMORY;
}
continue;
}
ohci_isochronous_td *descriptor = _CreateIsochronousDescriptor(
packetSize * frameCount);
if (!descriptor) {
TRACE_ERROR("failed to allocate ITD\n");
_ReleaseIsochronousBandwidth(currentFrame + frameOffset, frameCount);
_FreeIsochronousDescriptorChain(firstDescriptor);
return B_NO_MEMORY;
}
uint16 pageOffset = descriptor->buffer_page_byte_0 & 0xfff;
descriptor->buffer_page_byte_0 &= ~0xfff;
for (uint16 i = 0; i < frameCount; i++) {
descriptor->offset[OHCI_ITD_OFFSET_IDX(i)]
= OHCI_ITD_MK_OFFS(pageOffset + packetSize * i);
}
descriptor->flags = OHCI_ITD_SET_FRAME_COUNT(frameCount)
| OHCI_ITD_SET_CONDITION_CODE(OHCI_ITD_CONDITION_NOT_ACCESSED)
| OHCI_ITD_SET_DELAY_INTERRUPT(OHCI_ITD_INTERRUPT_NONE)
| OHCI_ITD_SET_STARTING_FRAME(currentFrame + frameOffset);
if (packets <= OHCI_ITD_NOFFSET)
descriptor->last_byte_address
+= dataLength - packetSize * (packet_count);
if (lastDescriptor)
_LinkIsochronousDescriptors(lastDescriptor, descriptor, descriptor);
lastDescriptor = descriptor;
if (!firstDescriptor)
firstDescriptor = descriptor;
packets -= frameCount;
frameOffset += frameCount;
if (packets == 0 && isochronousData->starting_frame_number)
*isochronousData->starting_frame_number = currentFrame + frameOffset;
}
*_firstDescriptor = firstDescriptor;
*_lastDescriptor = lastDescriptor;
return B_OK;
}
void
OHCI::_FreeIsochronousDescriptorChain(ohci_isochronous_td *topDescriptor)
{
ohci_isochronous_td *current = topDescriptor;
ohci_isochronous_td *next = NULL;
while (current) {
next = (ohci_isochronous_td *)current->next_done_descriptor;
_FreeIsochronousDescriptor(current);
current = next;
}
}
size_t
OHCI::_WriteDescriptorChain(ohci_general_td *topDescriptor, generic_io_vec *vector,
size_t vectorCount, bool physical)
{
ohci_general_td *current = topDescriptor;
size_t actualLength = 0;
size_t vectorIndex = 0;
size_t vectorOffset = 0;
size_t bufferOffset = 0;
while (current) {
if (!current->buffer_logical)
break;
while (true) {
size_t length = min_c(current->buffer_size - bufferOffset,
vector[vectorIndex].length - vectorOffset);
TRACE("copying %ld bytes to bufferOffset %ld from"
" vectorOffset %ld at index %ld of %ld\n", length, bufferOffset,
vectorOffset, vectorIndex, vectorCount);
status_t status = generic_memcpy(
(generic_addr_t)current->buffer_logical + bufferOffset, false,
vector[vectorIndex].base + vectorOffset, physical, length);
ASSERT_ALWAYS(status == B_OK);
actualLength += length;
vectorOffset += length;
bufferOffset += length;
if (vectorOffset >= vector[vectorIndex].length) {
if (++vectorIndex >= vectorCount) {
TRACE("wrote descriptor chain (%ld bytes, no"
" more vectors)\n", actualLength);
return actualLength;
}
vectorOffset = 0;
}
if (bufferOffset >= current->buffer_size) {
bufferOffset = 0;
break;
}
}
if (!current->next_logical_descriptor)
break;
current = (ohci_general_td *)current->next_logical_descriptor;
}
TRACE("wrote descriptor chain (%ld bytes)\n", actualLength);
return actualLength;
}
size_t
OHCI::_WriteIsochronousDescriptorChain(ohci_isochronous_td *topDescriptor,
generic_io_vec *vector, size_t vectorCount, bool physical)
{
ohci_isochronous_td *current = topDescriptor;
size_t actualLength = 0;
size_t vectorIndex = 0;
size_t vectorOffset = 0;
size_t bufferOffset = 0;
while (current) {
if (!current->buffer_logical)
break;
while (true) {
size_t length = min_c(current->buffer_size - bufferOffset,
vector[vectorIndex].length - vectorOffset);
TRACE("copying %ld bytes to bufferOffset %ld from"
" vectorOffset %ld at index %ld of %ld\n", length, bufferOffset,
vectorOffset, vectorIndex, vectorCount);
status_t status = generic_memcpy(
(generic_addr_t)current->buffer_logical + bufferOffset, false,
vector[vectorIndex].base + vectorOffset, physical, length);
ASSERT_ALWAYS(status == B_OK);
actualLength += length;
vectorOffset += length;
bufferOffset += length;
if (vectorOffset >= vector[vectorIndex].length) {
if (++vectorIndex >= vectorCount) {
TRACE("wrote descriptor chain (%ld bytes, no"
" more vectors)\n", actualLength);
return actualLength;
}
vectorOffset = 0;
}
if (bufferOffset >= current->buffer_size) {
bufferOffset = 0;
break;
}
}
if (!current->next_logical_descriptor)
break;
current = (ohci_isochronous_td *)current->next_logical_descriptor;
}
TRACE("wrote descriptor chain (%ld bytes)\n", actualLength);
return actualLength;
}
size_t
OHCI::_ReadDescriptorChain(ohci_general_td *topDescriptor, generic_io_vec *vector,
size_t vectorCount, bool physical)
{
ohci_general_td *current = topDescriptor;
size_t actualLength = 0;
size_t vectorIndex = 0;
size_t vectorOffset = 0;
size_t bufferOffset = 0;
while (current && OHCI_TD_GET_CONDITION_CODE(current->flags)
!= OHCI_TD_CONDITION_NOT_ACCESSED) {
if (!current->buffer_logical)
break;
size_t bufferSize = current->buffer_size;
if (current->buffer_physical != 0) {
bufferSize -= current->last_physical_byte_address
- current->buffer_physical + 1;
}
while (true) {
size_t length = min_c(bufferSize - bufferOffset,
vector[vectorIndex].length - vectorOffset);
TRACE("copying %ld bytes to vectorOffset %ld from"
" bufferOffset %ld at index %ld of %ld\n", length, vectorOffset,
bufferOffset, vectorIndex, vectorCount);
status_t status = generic_memcpy(
vector[vectorIndex].base + vectorOffset, physical,
(generic_addr_t)current->buffer_logical + bufferOffset, false, length);
ASSERT_ALWAYS(status == B_OK);
actualLength += length;
vectorOffset += length;
bufferOffset += length;
if (vectorOffset >= vector[vectorIndex].length) {
if (++vectorIndex >= vectorCount) {
TRACE("read descriptor chain (%ld bytes, no more vectors)\n",
actualLength);
return actualLength;
}
vectorOffset = 0;
}
if (bufferOffset >= bufferSize) {
bufferOffset = 0;
break;
}
}
current = (ohci_general_td *)current->next_logical_descriptor;
}
TRACE("read descriptor chain (%ld bytes)\n", actualLength);
return actualLength;
}
void
OHCI::_ReadIsochronousDescriptorChain(ohci_isochronous_td *topDescriptor,
generic_io_vec *vector, size_t vectorCount, bool physical)
{
ohci_isochronous_td *current = topDescriptor;
size_t actualLength = 0;
size_t vectorIndex = 0;
size_t vectorOffset = 0;
size_t bufferOffset = 0;
while (current && OHCI_ITD_GET_CONDITION_CODE(current->flags)
!= OHCI_ITD_CONDITION_NOT_ACCESSED) {
size_t bufferSize = current->buffer_size;
if (current->buffer_logical != NULL && bufferSize > 0) {
while (true) {
size_t length = min_c(bufferSize - bufferOffset,
vector[vectorIndex].length - vectorOffset);
TRACE("copying %ld bytes to vectorOffset %ld from bufferOffset"
" %ld at index %ld of %ld\n", length, vectorOffset,
bufferOffset, vectorIndex, vectorCount);
status_t status = generic_memcpy(
vector[vectorIndex].base + vectorOffset, physical,
(generic_addr_t)current->buffer_logical + bufferOffset, false, length);
ASSERT_ALWAYS(status == B_OK);
actualLength += length;
vectorOffset += length;
bufferOffset += length;
if (vectorOffset >= vector[vectorIndex].length) {
if (++vectorIndex >= vectorCount) {
TRACE("read descriptor chain (%ld bytes, "
"no more vectors)\n", actualLength);
return;
}
vectorOffset = 0;
}
if (bufferOffset >= bufferSize) {
bufferOffset = 0;
break;
}
}
}
current = (ohci_isochronous_td *)current->next_done_descriptor;
}
TRACE("read descriptor chain (%ld bytes)\n", actualLength);
return;
}
size_t
OHCI::_ReadActualLength(ohci_general_td *topDescriptor)
{
ohci_general_td *current = topDescriptor;
size_t actualLength = 0;
while (current && OHCI_TD_GET_CONDITION_CODE(current->flags)
!= OHCI_TD_CONDITION_NOT_ACCESSED) {
size_t length = current->buffer_size;
if (current->buffer_physical != 0) {
length -= current->last_physical_byte_address
- current->buffer_physical + 1;
}
actualLength += length;
current = (ohci_general_td *)current->next_logical_descriptor;
}
TRACE("read actual length (%ld bytes)\n", actualLength);
return actualLength;
}
void
OHCI::_LinkDescriptors(ohci_general_td *first, ohci_general_td *second)
{
first->next_physical_descriptor = second->physical_address;
first->next_logical_descriptor = second;
}
void
OHCI::_LinkIsochronousDescriptors(ohci_isochronous_td *first,
ohci_isochronous_td *second, ohci_isochronous_td *nextDone)
{
first->next_physical_descriptor = second->physical_address;
first->next_logical_descriptor = second;
first->next_done_descriptor = nextDone;
}
bool
OHCI::_AllocateIsochronousBandwidth(uint16 frame, uint16 size)
{
frame %= NUMBER_OF_FRAMES;
if (size > fFrameBandwidth[frame])
return false;
fFrameBandwidth[frame]-= size;
return true;
}
void
OHCI::_ReleaseIsochronousBandwidth(uint16 startFrame, uint16 frameCount)
{
for (size_t index = 0; index < frameCount; index++) {
uint16 frame = (startFrame + index) % NUMBER_OF_FRAMES;
fFrameBandwidth[frame] = MAX_AVAILABLE_BANDWIDTH;
}
}
status_t
OHCI::_GetStatusOfConditionCode(uint8 conditionCode)
{
switch (conditionCode) {
case OHCI_TD_CONDITION_NO_ERROR:
return B_OK;
case OHCI_TD_CONDITION_CRC_ERROR:
case OHCI_TD_CONDITION_BIT_STUFFING:
case OHCI_TD_CONDITION_TOGGLE_MISMATCH:
return B_DEV_CRC_ERROR;
case OHCI_TD_CONDITION_STALL:
return B_DEV_STALLED;
case OHCI_TD_CONDITION_NO_RESPONSE:
return B_TIMED_OUT;
case OHCI_TD_CONDITION_PID_CHECK_FAILURE:
return B_DEV_BAD_PID;
case OHCI_TD_CONDITION_UNEXPECTED_PID:
return B_DEV_UNEXPECTED_PID;
case OHCI_TD_CONDITION_DATA_OVERRUN:
return B_DEV_DATA_OVERRUN;
case OHCI_TD_CONDITION_DATA_UNDERRUN:
return B_DEV_DATA_UNDERRUN;
case OHCI_TD_CONDITION_BUFFER_OVERRUN:
return B_DEV_WRITE_ERROR;
case OHCI_TD_CONDITION_BUFFER_UNDERRUN:
return B_DEV_READ_ERROR;
case OHCI_TD_CONDITION_NOT_ACCESSED:
return B_DEV_PENDING;
case 0x0E:
return B_DEV_TOO_LATE;
default:
break;
}
return B_ERROR;
}
bool
OHCI::_LockEndpoints()
{
return (mutex_lock(&fEndpointLock) == B_OK);
}
void
OHCI::_UnlockEndpoints()
{
mutex_unlock(&fEndpointLock);
}
inline void
OHCI::_WriteReg(uint32 reg, uint32 value)
{
*(volatile uint32 *)(fOperationalRegisters + reg) = value;
}
inline uint32
OHCI::_ReadReg(uint32 reg)
{
return *(volatile uint32 *)(fOperationalRegisters + reg);
}
void
OHCI::_PrintEndpoint(ohci_endpoint_descriptor *endpoint)
{
dprintf("endpoint %p\n", endpoint);
dprintf("\tflags........... 0x%08" B_PRIx32 "\n", endpoint->flags);
dprintf("\ttail_physical... 0x%08" B_PRIx32 "\n", endpoint->tail_physical_descriptor);
dprintf("\thead_physical... 0x%08" B_PRIx32 "\n", endpoint->head_physical_descriptor);
dprintf("\tnext_physical... 0x%08" B_PRIx32 "\n", endpoint->next_physical_endpoint);
dprintf("\tphysical........ 0x%08" B_PRIx32 "\n", endpoint->physical_address);
dprintf("\ttail_logical.... %p\n", endpoint->tail_logical_descriptor);
dprintf("\tnext_logical.... %p\n", endpoint->next_logical_endpoint);
}
void
OHCI::_PrintDescriptorChain(ohci_general_td *topDescriptor)
{
while (topDescriptor) {
dprintf("descriptor %p\n", topDescriptor);
dprintf("\tflags........... 0x%08" B_PRIx32 "\n", topDescriptor->flags);
dprintf("\tbuffer_physical. 0x%08" B_PRIx32 "\n", topDescriptor->buffer_physical);
dprintf("\tnext_physical... 0x%08" B_PRIx32 "\n", topDescriptor->next_physical_descriptor);
dprintf("\tlast_byte....... 0x%08" B_PRIx32 "\n", topDescriptor->last_physical_byte_address);
dprintf("\tphysical........ 0x%08" B_PRIx32 "\n", topDescriptor->physical_address);
dprintf("\tbuffer_size..... %lu\n", topDescriptor->buffer_size);
dprintf("\tbuffer_logical.. %p\n", topDescriptor->buffer_logical);
dprintf("\tnext_logical.... %p\n", topDescriptor->next_logical_descriptor);
topDescriptor = (ohci_general_td *)topDescriptor->next_logical_descriptor;
}
}
void
OHCI::_PrintDescriptorChain(ohci_isochronous_td *topDescriptor)
{
while (topDescriptor) {
dprintf("iso.descriptor %p\n", topDescriptor);
dprintf("\tflags........... 0x%08" B_PRIx32 "\n", topDescriptor->flags);
dprintf("\tbuffer_pagebyte0 0x%08" B_PRIx32 "\n", topDescriptor->buffer_page_byte_0);
dprintf("\tnext_physical... 0x%08" B_PRIx32 "\n", topDescriptor->next_physical_descriptor);
dprintf("\tlast_byte....... 0x%08" B_PRIx32 "\n", topDescriptor->last_byte_address);
dprintf("\toffset:\n\t0x%04x 0x%04x 0x%04x 0x%04x\n"
"\t0x%04x 0x%04x 0x%04x 0x%04x\n",
topDescriptor->offset[0], topDescriptor->offset[1],
topDescriptor->offset[2], topDescriptor->offset[3],
topDescriptor->offset[4], topDescriptor->offset[5],
topDescriptor->offset[6], topDescriptor->offset[7]);
dprintf("\tphysical........ 0x%08" B_PRIx32 "\n", topDescriptor->physical_address);
dprintf("\tbuffer_size..... %lu\n", topDescriptor->buffer_size);
dprintf("\tbuffer_logical.. %p\n", topDescriptor->buffer_logical);
dprintf("\tnext_logical.... %p\n", topDescriptor->next_logical_descriptor);
dprintf("\tnext_done....... %p\n", topDescriptor->next_done_descriptor);
topDescriptor = (ohci_isochronous_td *)topDescriptor->next_done_descriptor;
}
}