* Copyright 2003-2014, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* Niels S. Reedijk
*/
#include "usb_private.h"
#include <StackOrHeapArray.h>
Device::Device(Object* parent, int8 hubAddress, uint8 hubPort,
usb_device_descriptor& desc, int8 deviceAddress, usb_speed speed,
bool isRootHub, void* controllerCookie)
:
Object(parent),
fDeviceDescriptor(desc),
fInitOK(false),
fAvailable(true),
fIsRootHub(isRootHub),
fConfigurations(NULL),
fCurrentConfiguration(NULL),
fSpeed(speed),
fDeviceAddress(deviceAddress),
fHubAddress(hubAddress),
fHubPort(hubPort),
fControllerCookie(controllerCookie),
fNode(NULL)
{
TRACE("creating device\n");
fDefaultPipe = new(std::nothrow) ControlPipe(this);
if (fDefaultPipe == NULL) {
TRACE_ERROR("could not allocate default pipe\n");
return;
}
fDefaultPipe->InitCommon(fDeviceAddress, 0, fSpeed, Pipe::Default,
fDeviceDescriptor.max_packet_size_0, 0, fHubAddress, fHubPort);
size_t actualLength;
status_t status = GetDescriptor(USB_DESCRIPTOR_DEVICE, 0, 0,
(void*)&fDeviceDescriptor, sizeof(fDeviceDescriptor), &actualLength);
if (status < B_OK || actualLength != sizeof(fDeviceDescriptor)) {
TRACE_ERROR("error while getting the device descriptor\n");
return;
}
TRACE("full device descriptor for device %d:\n", fDeviceAddress);
TRACE("\tlength:..............%d\n", fDeviceDescriptor.length);
TRACE("\tdescriptor_type:.....0x%04x\n", fDeviceDescriptor.descriptor_type);
TRACE("\tusb_version:.........0x%04x\n", fDeviceDescriptor.usb_version);
TRACE("\tdevice_class:........0x%02x\n", fDeviceDescriptor.device_class);
TRACE("\tdevice_subclass:.....0x%02x\n", fDeviceDescriptor.device_subclass);
TRACE("\tdevice_protocol:.....0x%02x\n", fDeviceDescriptor.device_protocol);
TRACE("\tmax_packet_size_0:...%d\n", fDeviceDescriptor.max_packet_size_0);
TRACE("\tvendor_id:...........0x%04x\n", fDeviceDescriptor.vendor_id);
TRACE("\tproduct_id:..........0x%04x\n", fDeviceDescriptor.product_id);
TRACE("\tdevice_version:......0x%04x\n", fDeviceDescriptor.device_version);
TRACE("\tmanufacturer:........0x%02x\n", fDeviceDescriptor.manufacturer);
TRACE("\tproduct:.............0x%02x\n", fDeviceDescriptor.product);
TRACE("\tserial_number:.......0x%02x\n", fDeviceDescriptor.serial_number);
TRACE("\tnum_configurations:..%d\n", fDeviceDescriptor.num_configurations);
fConfigurations = (usb_configuration_info*)malloc(
fDeviceDescriptor.num_configurations * sizeof(usb_configuration_info));
if (fConfigurations == NULL) {
TRACE_ERROR("out of memory during config creations!\n");
return;
}
memset(fConfigurations, 0, fDeviceDescriptor.num_configurations
* sizeof(usb_configuration_info));
for (int32 i = 0; i < fDeviceDescriptor.num_configurations; i++) {
usb_configuration_descriptor configDescriptor;
status = GetDescriptor(USB_DESCRIPTOR_CONFIGURATION, i, 0,
(void*)&configDescriptor, sizeof(usb_configuration_descriptor),
&actualLength);
if (status < B_OK
|| actualLength != sizeof(usb_configuration_descriptor)) {
TRACE_ERROR("error fetching configuration %" B_PRId32 "\n", i);
return;
}
TRACE("configuration %" B_PRId32 "\n", i);
TRACE("\tlength:..............%d\n", configDescriptor.length);
TRACE("\tdescriptor_type:.....0x%02x\n",
configDescriptor.descriptor_type);
TRACE("\ttotal_length:........%d\n", configDescriptor.total_length);
TRACE("\tnumber_interfaces:...%d\n",
configDescriptor.number_interfaces);
TRACE("\tconfiguration_value:.0x%02x\n",
configDescriptor.configuration_value);
TRACE("\tconfiguration:.......0x%02x\n",
configDescriptor.configuration);
TRACE("\tattributes:..........0x%02x\n", configDescriptor.attributes);
TRACE("\tmax_power:...........%d\n", configDescriptor.max_power);
uint8* configData = (uint8*)malloc(configDescriptor.total_length);
if (configData == NULL) {
TRACE_ERROR("out of memory when reading config\n");
return;
}
status = GetDescriptor(USB_DESCRIPTOR_CONFIGURATION, i, 0,
(void*)configData, configDescriptor.total_length, &actualLength);
if (status < B_OK || actualLength != configDescriptor.total_length) {
TRACE_ERROR("error fetching full configuration"
" descriptor %" B_PRId32 " got %" B_PRIuSIZE " expected %"
B_PRIu16 "\n", i, actualLength, configDescriptor.total_length);
free(configData);
return;
}
usb_configuration_descriptor* configuration
= (usb_configuration_descriptor*)configData;
fConfigurations[i].descr = configuration;
fConfigurations[i].interface_count = configuration->number_interfaces;
fConfigurations[i].interface = (usb_interface_list*)malloc(
configuration->number_interfaces * sizeof(usb_interface_list));
if (fConfigurations[i].interface == NULL) {
TRACE_ERROR("out of memory when creating interfaces\n");
return;
}
memset(fConfigurations[i].interface, 0,
configuration->number_interfaces * sizeof(usb_interface_list));
usb_interface_info* currentInterface = NULL;
uint32 descriptorStart = sizeof(usb_configuration_descriptor);
while (descriptorStart < actualLength) {
switch (configData[descriptorStart + 1]) {
case USB_DESCRIPTOR_INTERFACE:
{
TRACE("got interface descriptor\n");
usb_interface_descriptor* interfaceDescriptor
= (usb_interface_descriptor*)&configData[
descriptorStart];
TRACE("\tlength:.............%d\n",
interfaceDescriptor->length);
TRACE("\tdescriptor_type:....0x%02x\n",
interfaceDescriptor->descriptor_type);
TRACE("\tinterface_number:...%d\n",
interfaceDescriptor->interface_number);
TRACE("\talternate_setting:..%d\n",
interfaceDescriptor->alternate_setting);
TRACE("\tnum_endpoints:......%d\n",
interfaceDescriptor->num_endpoints);
TRACE("\tinterface_class:....0x%02x\n",
interfaceDescriptor->interface_class);
TRACE("\tinterface_subclass:.0x%02x\n",
interfaceDescriptor->interface_subclass);
TRACE("\tinterface_protocol:.0x%02x\n",
interfaceDescriptor->interface_protocol);
TRACE("\tinterface:..........%d\n",
interfaceDescriptor->interface);
if (interfaceDescriptor->interface_number
>= fConfigurations[i].interface_count) {
interfaceDescriptor->interface_number
= fConfigurations[i].interface_count - 1;
TRACE_ERROR("Corrected invalid interface_number!\n");
}
usb_interface_list* interfaceList = &fConfigurations[i]
.interface[interfaceDescriptor->interface_number];
interfaceList->alt_count++;
usb_interface_info* newAlternates
= (usb_interface_info*)realloc(interfaceList->alt,
interfaceList->alt_count * sizeof(usb_interface_info));
if (newAlternates == NULL) {
TRACE_ERROR("out of memory allocating"
" alternate interface\n");
interfaceList->alt_count--;
return;
}
interfaceList->alt = newAlternates;
interfaceList->active = interfaceList->alt;
usb_interface_info* interfaceInfo
= &interfaceList->alt[interfaceList->alt_count - 1];
interfaceInfo->descr = interfaceDescriptor;
interfaceInfo->endpoint_count = 0;
interfaceInfo->endpoint = NULL;
interfaceInfo->generic_count = 0;
interfaceInfo->generic = NULL;
Interface* interface = new(std::nothrow) Interface(this,
interfaceDescriptor->interface_number);
if (interface == NULL) {
TRACE_ERROR("failed to allocate"
" interface object\n");
return;
}
interfaceInfo->handle = interface->USBID();
currentInterface = interfaceInfo;
break;
}
case USB_DESCRIPTOR_ENDPOINT:
{
TRACE("got endpoint descriptor\n");
usb_endpoint_descriptor* endpointDescriptor
= (usb_endpoint_descriptor*)&configData[descriptorStart];
TRACE("\tlength:.............%d\n",
endpointDescriptor->length);
TRACE("\tdescriptor_type:....0x%02x\n",
endpointDescriptor->descriptor_type);
TRACE("\tendpoint_address:...0x%02x\n",
endpointDescriptor->endpoint_address);
TRACE("\tattributes:.........0x%02x\n",
endpointDescriptor->attributes);
TRACE("\tmax_packet_size:....%d\n",
endpointDescriptor->max_packet_size);
TRACE("\tinterval:...........%d\n",
endpointDescriptor->interval);
if (currentInterface == NULL)
break;
currentInterface->endpoint_count++;
usb_endpoint_info* newEndpoints = (usb_endpoint_info*)
realloc(currentInterface->endpoint,
currentInterface->endpoint_count
* sizeof(usb_endpoint_info));
if (newEndpoints == NULL) {
TRACE_ERROR("out of memory allocating new endpoint\n");
currentInterface->endpoint_count--;
return;
}
currentInterface->endpoint = newEndpoints;
usb_endpoint_info* endpointInfo = ¤tInterface
->endpoint[currentInterface->endpoint_count - 1];
endpointInfo->descr = endpointDescriptor;
endpointInfo->handle = 0;
break;
}
case USB_DESCRIPTOR_ENDPOINT_SS_COMPANION: {
if (currentInterface != NULL) {
usb_endpoint_descriptor* desc
= currentInterface->endpoint[
currentInterface->endpoint_count - 1].descr;
if ((uint8*)desc != (&configData[descriptorStart
- desc->length])) {
TRACE_ERROR("found endpoint companion descriptor "
"not immediately following endpoint "
"descriptor, ignoring!\n");
break;
}
}
}
default:
TRACE("got generic descriptor\n");
usb_generic_descriptor* genericDescriptor
= (usb_generic_descriptor*)&configData[descriptorStart];
TRACE("\tlength:.............%d\n",
genericDescriptor->length);
TRACE("\tdescriptor_type:....0x%02x\n",
genericDescriptor->descriptor_type);
if (currentInterface == NULL)
break;
currentInterface->generic_count++;
usb_descriptor** newGenerics = (usb_descriptor**)realloc(
currentInterface->generic,
currentInterface->generic_count
* sizeof(usb_descriptor*));
if (newGenerics == NULL) {
TRACE_ERROR("out of memory allocating"
" generic descriptor\n");
currentInterface->generic_count--;
return;
}
currentInterface->generic = newGenerics;
currentInterface->generic[
currentInterface->generic_count - 1]
= (usb_descriptor*)genericDescriptor;
break;
}
descriptorStart += configData[descriptorStart];
}
}
TRACE("setting default configuration\n");
if (SetConfigurationAt(0) != B_OK) {
TRACE_ERROR("failed to set default configuration\n");
return;
}
fInitOK = true;
}
Device::~Device()
{
if (fNode != NULL) {
status_t error = gDeviceManager->unregister_node(fNode);
if (error != B_OK && error != B_BUSY)
TRACE_ERROR("failed to unregister device node\n");
fNode = NULL;
}
Unconfigure(false);
for (int32 i = 0; fConfigurations != NULL
&& i < fDeviceDescriptor.num_configurations; i++) {
usb_configuration_info* configuration = &fConfigurations[i];
if (configuration == NULL || configuration->interface == NULL)
continue;
for (size_t j = 0; j < configuration->interface_count; j++) {
usb_interface_list* interfaceList = &configuration->interface[j];
if (interfaceList->alt == NULL)
continue;
for (size_t k = 0; k < interfaceList->alt_count; k++) {
usb_interface_info* interface = &interfaceList->alt[k];
Interface* interfaceObject =
(Interface*)GetStack()->GetObject(interface->handle);
interface->handle = 0;
if (interfaceObject != NULL)
interfaceObject->ReleaseReference();
delete interfaceObject;
}
}
}
if (fDefaultPipe != NULL) {
fDefaultPipe->PutUSBID(false);
PutUSBID(false);
fDefaultPipe->CancelQueuedTransfers(true);
fDefaultPipe->WaitForIdle();
}
PutUSBID(true);
delete fDefaultPipe;
if (fConfigurations == NULL) {
return;
}
for (int32 i = 0; i < fDeviceDescriptor.num_configurations; i++) {
usb_configuration_info* configuration = &fConfigurations[i];
if (configuration == NULL)
continue;
free(configuration->descr);
if (configuration->interface == NULL)
continue;
for (size_t j = 0; j < configuration->interface_count; j++) {
usb_interface_list* interfaceList = &configuration->interface[j];
if (interfaceList->alt == NULL)
continue;
for (size_t k = 0; k < interfaceList->alt_count; k++) {
usb_interface_info* interface = &interfaceList->alt[k];
free(interface->endpoint);
free(interface->generic);
}
free(interfaceList->alt);
}
free(configuration->interface);
}
free(fConfigurations);
}
status_t
Device::InitCheck()
{
if (fInitOK)
return B_OK;
return B_ERROR;
}
status_t
Device::Changed(change_item** changeList, bool added)
{
fAvailable = added;
change_item* changeItem = new(std::nothrow) change_item;
if (changeItem == NULL)
return B_NO_MEMORY;
changeItem->added = added;
changeItem->device = this;
changeItem->link = *changeList;
*changeList = changeItem;
return B_OK;
}
status_t
Device::GetDescriptor(uint8 descriptorType, uint8 index, uint16 languageID,
void* data, size_t dataLength, size_t* actualLength)
{
if (!fAvailable)
return B_ERROR;
return fDefaultPipe->SendRequest(
USB_REQTYPE_DEVICE_IN | USB_REQTYPE_STANDARD,
USB_REQUEST_GET_DESCRIPTOR, (descriptorType << 8) | index,
languageID, dataLength, data, dataLength, actualLength);
}
const usb_configuration_info*
Device::Configuration() const
{
return fCurrentConfiguration;
}
const usb_configuration_info*
Device::ConfigurationAt(uint8 index) const
{
if (index >= fDeviceDescriptor.num_configurations)
return NULL;
return &fConfigurations[index];
}
status_t
Device::SetConfiguration(const usb_configuration_info* configuration)
{
if (!configuration)
return Unconfigure(true);
for (uint8 i = 0; i < fDeviceDescriptor.num_configurations; i++) {
if (configuration->descr->configuration_value
== fConfigurations[i].descr->configuration_value)
return SetConfigurationAt(i);
}
return B_BAD_VALUE;
}
status_t
Device::SetConfigurationAt(uint8 index)
{
if (!fAvailable)
return B_ERROR;
if (index >= fDeviceDescriptor.num_configurations)
return B_BAD_VALUE;
if (&fConfigurations[index] == fCurrentConfiguration)
return B_OK;
Unconfigure(false);
status_t result = fDefaultPipe->SendRequest(
USB_REQTYPE_DEVICE_OUT | USB_REQTYPE_STANDARD,
USB_REQUEST_SET_CONFIGURATION,
fConfigurations[index].descr->configuration_value, 0, 0, NULL, 0, NULL);
if (result < B_OK)
return result;
fCurrentConfiguration = &fConfigurations[index];
InitEndpoints(-1);
if (!fIsRootHub)
snooze(USB_DELAY_SET_CONFIGURATION);
return B_OK;
}
void
Device::InitEndpoints(int32 interfaceIndex)
{
for (size_t j = 0; j < fCurrentConfiguration->interface_count; j++) {
if (interfaceIndex >= 0 && j != (size_t)interfaceIndex)
continue;
usb_interface_info* interfaceInfo
= fCurrentConfiguration->interface[j].active;
if (interfaceInfo == NULL)
continue;
for (size_t i = 0; i < interfaceInfo->endpoint_count; i++) {
usb_endpoint_info* endpoint = &interfaceInfo->endpoint[i];
Pipe* pipe = NULL;
usb_endpoint_ss_companion_descriptor* comp_descr = NULL;
if (fSpeed >= USB_SPEED_SUPERSPEED) {
size_t k = 0;
for (size_t j = 0; j < interfaceInfo->generic_count; j++) {
usb_descriptor* desc = interfaceInfo->generic[j];
if (desc->endpoint.descriptor_type
!= USB_DESCRIPTOR_ENDPOINT_SS_COMPANION) {
continue;
}
if (k == i) {
comp_descr =
(usb_endpoint_ss_companion_descriptor*)desc;
break;
}
k++;
}
if (comp_descr == NULL) {
TRACE_ERROR("SuperSpeed device without an endpoint companion "
"descriptor!");
}
}
Pipe::pipeDirection direction = Pipe::Out;
if ((endpoint->descr->endpoint_address & 0x80) != 0)
direction = Pipe::In;
switch (endpoint->descr->attributes & 0x03) {
case USB_ENDPOINT_ATTR_CONTROL:
pipe = new(std::nothrow) ControlPipe(this);
direction = Pipe::Default;
break;
case USB_ENDPOINT_ATTR_ISOCHRONOUS:
pipe = new(std::nothrow) IsochronousPipe(this);
break;
case USB_ENDPOINT_ATTR_BULK:
pipe = new(std::nothrow) BulkPipe(this);
break;
case USB_ENDPOINT_ATTR_INTERRUPT:
pipe = new(std::nothrow) InterruptPipe(this);
break;
}
if (pipe == NULL) {
TRACE_ERROR("failed to allocate pipe\n");
endpoint->handle = 0;
continue;
}
pipe->InitCommon(fDeviceAddress,
endpoint->descr->endpoint_address & 0x0f,
fSpeed, direction, endpoint->descr->max_packet_size,
endpoint->descr->interval, fHubAddress, fHubPort);
if (comp_descr != NULL) {
pipe->InitSuperSpeed(comp_descr->max_burst,
comp_descr->bytes_per_interval);
}
endpoint->handle = pipe->USBID();
}
}
}
status_t
Device::Unconfigure(bool atDeviceLevel)
{
if (atDeviceLevel && fAvailable) {
status_t result = fDefaultPipe->SendRequest(
USB_REQTYPE_DEVICE_OUT | USB_REQTYPE_STANDARD,
USB_REQUEST_SET_CONFIGURATION, 0, 0, 0, NULL, 0, NULL);
if (result < B_OK)
return result;
snooze(USB_DELAY_SET_CONFIGURATION);
}
if (!fCurrentConfiguration)
return B_OK;
ClearEndpoints(-1);
fCurrentConfiguration = NULL;
return B_OK;
}
void
Device::ClearEndpoints(int32 interfaceIndex)
{
if (fCurrentConfiguration == NULL
|| fCurrentConfiguration->interface == NULL)
return;
for (size_t j = 0; j < fCurrentConfiguration->interface_count; j++) {
if (interfaceIndex >= 0 && j != (size_t)interfaceIndex)
continue;
usb_interface_info* interfaceInfo
= fCurrentConfiguration->interface[j].active;
if (interfaceInfo == NULL || interfaceInfo->endpoint == NULL)
continue;
for (size_t i = 0; i < interfaceInfo->endpoint_count; i++) {
usb_endpoint_info* endpoint = &interfaceInfo->endpoint[i];
Pipe* pipe = (Pipe*)GetStack()->GetObject(endpoint->handle);
if (pipe != NULL)
pipe->ReleaseReference();
delete pipe;
endpoint->handle = 0;
}
}
}
status_t
Device::SetAltInterface(const usb_interface_info* interface)
{
uint8 interfaceNumber = interface->descr->interface_number;
status_t result = fDefaultPipe->SendRequest(
USB_REQTYPE_INTERFACE_OUT | USB_REQTYPE_STANDARD,
USB_REQUEST_SET_INTERFACE,
interface->descr->alternate_setting, interfaceNumber, 0, NULL, 0, NULL);
if (result < B_OK)
return result;
ClearEndpoints(interfaceNumber);
usb_interface_list* interfaceList
= &fCurrentConfiguration->interface[interfaceNumber];
interfaceList->active
= &interfaceList->alt[interface->descr->alternate_setting];
InitEndpoints(interfaceNumber);
return result;
}
const usb_device_descriptor*
Device::DeviceDescriptor() const
{
return &fDeviceDescriptor;
}
status_t
Device::ReportDevice(usb_support_descriptor* supportDescriptors,
uint32 supportDescriptorCount, const usb_notify_hooks* hooks,
usb_driver_cookie** cookies, bool added, bool recursive)
{
TRACE("reporting device\n");
bool supported = false;
if (supportDescriptorCount == 0 || supportDescriptors == NULL)
supported = true;
for (uint32 i = 0; !supported && i < supportDescriptorCount; i++) {
if ((supportDescriptors[i].vendor != 0
&& fDeviceDescriptor.vendor_id != supportDescriptors[i].vendor)
|| (supportDescriptors[i].product != 0
&& fDeviceDescriptor.product_id
!= supportDescriptors[i].product))
continue;
if ((supportDescriptors[i].dev_class == 0
|| fDeviceDescriptor.device_class
== supportDescriptors[i].dev_class)
&& (supportDescriptors[i].dev_subclass == 0
|| fDeviceDescriptor.device_subclass
== supportDescriptors[i].dev_subclass)
&& (supportDescriptors[i].dev_protocol == 0
|| fDeviceDescriptor.device_protocol
== supportDescriptors[i].dev_protocol)) {
supported = true;
}
for (uint32 j = 0;
!supported && j < fDeviceDescriptor.num_configurations; j++) {
for (uint32 k = 0;
!supported && k < fConfigurations[j].interface_count; k++) {
for (uint32 l = 0; !supported
&& l < fConfigurations[j].interface[k].alt_count; l++) {
usb_interface_descriptor* descriptor
= fConfigurations[j].interface[k].alt[l].descr;
if ((supportDescriptors[i].dev_class == 0
|| descriptor->interface_class
== supportDescriptors[i].dev_class)
&& (supportDescriptors[i].dev_subclass == 0
|| descriptor->interface_subclass
== supportDescriptors[i].dev_subclass)
&& (supportDescriptors[i].dev_protocol == 0
|| descriptor->interface_protocol
== supportDescriptors[i].dev_protocol)) {
supported = true;
}
}
}
}
}
if (!supported)
return B_UNSUPPORTED;
if ((added && hooks->device_added == NULL)
|| (!added && hooks->device_removed == NULL)) {
return B_OK;
}
usb_id id = USBID();
if (added) {
usb_driver_cookie* cookie = new(std::nothrow) usb_driver_cookie;
if (hooks->device_added(id, &cookie->cookie) >= B_OK) {
cookie->device = id;
cookie->link = *cookies;
*cookies = cookie;
} else
delete cookie;
} else {
usb_driver_cookie** pointer = cookies;
usb_driver_cookie* cookie = *cookies;
while (cookie != NULL) {
if (cookie->device == id)
break;
pointer = &cookie->link;
cookie = cookie->link;
}
if (cookie == NULL) {
return B_OK;
}
hooks->device_removed(cookie->cookie);
*pointer = cookie->link;
delete cookie;
}
return B_OK;
}
status_t
Device::BuildDeviceName(char* string, uint32* index, size_t bufferSize,
Device* device)
{
if (!Parent() || (Parent()->Type() & USB_OBJECT_HUB) == 0)
return B_ERROR;
((Hub*)Parent())->BuildDeviceName(string, index, bufferSize, this);
return B_OK;
}
status_t
Device::SetFeature(uint16 selector)
{
if (!fAvailable)
return B_ERROR;
TRACE("set feature %u\n", selector);
return fDefaultPipe->SendRequest(
USB_REQTYPE_STANDARD | USB_REQTYPE_DEVICE_OUT,
USB_REQUEST_SET_FEATURE, selector, 0, 0, NULL, 0, NULL);
}
status_t
Device::ClearFeature(uint16 selector)
{
if (!fAvailable)
return B_ERROR;
TRACE("clear feature %u\n", selector);
return fDefaultPipe->SendRequest(
USB_REQTYPE_STANDARD | USB_REQTYPE_DEVICE_OUT,
USB_REQUEST_CLEAR_FEATURE, selector, 0, 0, NULL, 0, NULL);
}
status_t
Device::GetStatus(uint16* status)
{
if (!fAvailable)
return B_ERROR;
TRACE("get status\n");
return fDefaultPipe->SendRequest(
USB_REQTYPE_STANDARD | USB_REQTYPE_DEVICE_IN,
USB_REQUEST_GET_STATUS, 0, 0, 2, (void*)status, 2, NULL);
}
device_node*
Device::RegisterNode(device_node *parent)
{
usb_id id = USBID();
if (parent == NULL)
parent = ((Device*)Parent())->Node();
uint32 deviceAttrTotal = 1;
for (uint32 j = 0; j < fDeviceDescriptor.num_configurations; j++) {
for (uint32 k = 0; k < fConfigurations[j].interface_count; k++) {
for (uint32 l = 0; l < fConfigurations[j].interface[k].alt_count; l++) {
deviceAttrTotal += 3;
}
}
}
BStackOrHeapArray<device_attr, 16> attrs(deviceAttrTotal + 4 + 5);
attrs[0] = { B_DEVICE_BUS, B_STRING_TYPE, { .string = "usb" } };
attrs[1] = { USB_DEVICE_ID_ITEM, B_UINT32_TYPE, { .ui32 = id } };
attrs[2] = { B_DEVICE_FLAGS, B_UINT32_TYPE, { .ui32 = B_FIND_MULTIPLE_CHILDREN } };
attrs[3] = { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "USB device" } };
uint32 attrCount = 4;
if (fDeviceDescriptor.vendor_id != 0) {
attrs[attrCount].name = B_DEVICE_VENDOR_ID;
attrs[attrCount].type = B_UINT16_TYPE;
attrs[attrCount].value.ui16 = fDeviceDescriptor.vendor_id;
attrCount++;
attrs[attrCount].name = B_DEVICE_ID;
attrs[attrCount].type = B_UINT16_TYPE;
attrs[attrCount].value.ui16 = fDeviceDescriptor.product_id;
attrCount++;
}
uint32 attrClassesIndex = attrCount;
if (fDeviceDescriptor.device_class != 0) {
attrs[attrCount].name = USB_DEVICE_CLASS;
attrs[attrCount].type = B_UINT8_TYPE;
attrs[attrCount].value.ui8 = fDeviceDescriptor.device_class;
attrCount++;
attrs[attrCount].name = USB_DEVICE_SUBCLASS;
attrs[attrCount].type = B_UINT8_TYPE;
attrs[attrCount].value.ui8 = fDeviceDescriptor.device_subclass;
attrCount++;
attrs[attrCount].name = USB_DEVICE_PROTOCOL;
attrs[attrCount].type = B_UINT8_TYPE;
attrs[attrCount].value.ui8 = fDeviceDescriptor.device_protocol;
attrCount++;
}
for (uint32 j = 0; j < fDeviceDescriptor.num_configurations; j++) {
for (uint32 k = 0; k < fConfigurations[j].interface_count; k++) {
for (uint32 l = 0; l < fConfigurations[j].interface[k].alt_count; l++) {
usb_interface_descriptor* descriptor
= fConfigurations[j].interface[k].alt[l].descr;
bool found = false;
for (uint32 i = attrClassesIndex; i < attrCount;) {
if (attrs[i++].value.ui8 != descriptor->interface_class)
continue;
if (attrs[i++].value.ui8 != descriptor->interface_subclass)
continue;
if (attrs[i++].value.ui8 != descriptor->interface_protocol)
continue;
found = true;
break;
}
if (found)
continue;
attrs[attrCount].name = USB_DEVICE_CLASS;
attrs[attrCount].type = B_UINT8_TYPE;
attrs[attrCount].value.ui8 = descriptor->interface_class;
attrCount++;
attrs[attrCount].name = USB_DEVICE_SUBCLASS;
attrs[attrCount].type = B_UINT8_TYPE;
attrs[attrCount].value.ui8 = descriptor->interface_subclass;
attrCount++;
attrs[attrCount].name = USB_DEVICE_PROTOCOL;
attrs[attrCount].type = B_UINT8_TYPE;
attrs[attrCount].value.ui8 = descriptor->interface_protocol;
attrCount++;
}
}
}
attrs[attrCount].name = NULL;
attrs[attrCount].type = 0;
attrs[attrCount].value.string = NULL;
attrCount++;
device_node* node = NULL;
if (gDeviceManager->register_node(parent, USB_DEVICE_MODULE_NAME, attrs,
NULL, &node) != B_OK) {
TRACE_ERROR("failed to register device node\n");
} else
fNode = node;
return node;
}