* Copyright 2006, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
*/
#include "ehci.h"
#define USB_MODULE_NAME "ehci roothub"
static usb_device_descriptor sEHCIRootHubDevice =
{
18,
USB_DESCRIPTOR_DEVICE,
0x200,
0x09,
0,
0,
64,
0,
0,
0x200,
1,
2,
0,
1
};
struct ehci_root_hub_configuration_s {
usb_configuration_descriptor configuration;
usb_interface_descriptor interface;
usb_endpoint_descriptor endpoint;
usb_hub_descriptor hub;
} _PACKED;
static ehci_root_hub_configuration_s sEHCIRootHubConfig =
{
{
9,
USB_DESCRIPTOR_CONFIGURATION,
34,
1,
1,
0,
0x40,
0
},
{
9,
USB_DESCRIPTOR_INTERFACE,
0,
0,
1,
0x09,
0,
0,
0
},
{
7,
USB_DESCRIPTOR_ENDPOINT,
USB_REQTYPE_DEVICE_IN | 1,
0x03,
8,
0xff
},
{
9,
USB_DESCRIPTOR_HUB,
0x0f,
0x0000,
0,
0,
0x00,
0xff
}
};
struct ehci_root_hub_string_s {
uint8 length;
uint8 descriptor_type;
uint16 unicode_string[12];
} _PACKED;
static ehci_root_hub_string_s sEHCIRootHubStrings[3] = {
{
4,
USB_DESCRIPTOR_STRING,
{
0x0409
}
},
{
22,
USB_DESCRIPTOR_STRING,
{
'H', 'A', 'I', 'K', 'U',
' ', 'I', 'n', 'c', '.'
}
},
{
26,
USB_DESCRIPTOR_STRING,
{
'E', 'H', 'C', 'I', ' ',
'R', 'o', 'o', 't', 'H',
'u', 'b'
}
}
};
EHCIRootHub::EHCIRootHub(Object *rootObject, int8 deviceAddress)
: Hub(rootObject, 0, rootObject->GetStack()->IndexOfBusManager(rootObject->GetBusManager()),
sEHCIRootHubDevice, deviceAddress, USB_SPEED_HIGHSPEED, true)
{
}
status_t
EHCIRootHub::ProcessTransfer(EHCI *ehci, Transfer *transfer)
{
if ((transfer->TransferPipe()->Type() & USB_OBJECT_CONTROL_PIPE) == 0)
return B_ERROR;
usb_request_data *request = transfer->RequestData();
TRACE_MODULE("request: %d\n", request->Request);
status_t status = B_TIMED_OUT;
size_t actualLength = 0;
switch (request->Request) {
case USB_REQUEST_GET_STATUS: {
if (request->Index == 0) {
actualLength = MIN(sizeof(usb_port_status),
transfer->DataLength());
memset(transfer->Data(), 0, actualLength);
status = B_OK;
break;
}
usb_port_status portStatus;
if (ehci->GetPortStatus(request->Index - 1, &portStatus) >= B_OK) {
actualLength = MIN(sizeof(usb_port_status), transfer->DataLength());
memcpy(transfer->Data(), (void *)&portStatus, actualLength);
status = B_OK;
}
break;
}
case USB_REQUEST_SET_ADDRESS:
if (request->Value >= 128) {
status = B_TIMED_OUT;
break;
}
TRACE_MODULE("set address: %d\n", request->Value);
status = B_OK;
break;
case USB_REQUEST_GET_DESCRIPTOR:
TRACE_MODULE("get descriptor: %d\n", request->Value >> 8);
switch (request->Value >> 8) {
case USB_DESCRIPTOR_DEVICE: {
actualLength = MIN(sizeof(usb_device_descriptor),
transfer->DataLength());
memcpy(transfer->Data(), (void *)&sEHCIRootHubDevice,
actualLength);
status = B_OK;
break;
}
case USB_DESCRIPTOR_CONFIGURATION: {
actualLength = MIN(sizeof(ehci_root_hub_configuration_s),
transfer->DataLength());
sEHCIRootHubConfig.hub.num_ports = ehci->PortCount();
memcpy(transfer->Data(), (void *)&sEHCIRootHubConfig,
actualLength);
status = B_OK;
break;
}
case USB_DESCRIPTOR_STRING: {
uint8 index = request->Value & 0x00ff;
if (index > 2)
break;
actualLength = MIN(sEHCIRootHubStrings[index].length,
transfer->DataLength());
memcpy(transfer->Data(), (void *)&sEHCIRootHubStrings[index],
actualLength);
status = B_OK;
break;
}
case USB_DESCRIPTOR_HUB: {
actualLength = MIN(sizeof(usb_hub_descriptor),
transfer->DataLength());
sEHCIRootHubConfig.hub.num_ports = ehci->PortCount();
memcpy(transfer->Data(), (void *)&sEHCIRootHubConfig.hub,
actualLength);
status = B_OK;
break;
}
}
break;
case USB_REQUEST_SET_CONFIGURATION:
status = B_OK;
break;
case USB_REQUEST_CLEAR_FEATURE: {
if (request->Index == 0) {
TRACE_MODULE_ERROR("clear feature: no hub changes\n");
break;
}
TRACE_MODULE("clear feature: %d\n", request->Value);
if (ehci->ClearPortFeature(request->Index - 1, request->Value) >= B_OK)
status = B_OK;
break;
}
case USB_REQUEST_SET_FEATURE: {
if (request->Index == 0) {
TRACE_MODULE_ERROR("set feature: no hub changes\n");
break;
}
TRACE_MODULE("set feature: %d\n", request->Value);
if (ehci->SetPortFeature(request->Index - 1, request->Value) >= B_OK)
status = B_OK;
break;
}
}
transfer->Finished(status, actualLength);
delete transfer;
return B_OK;
}