From 3c0b8e7fff9386da5223feeda97f461e714a4d30 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Tue, 23 Jan 2007 15:55:34 +0000 Subject: [PATCH] Commiting reimplemented USBKit that is compatible with the old Be sample code version. It is supposed to be cleaner and more object oriented. It also adds some features (like a string getter for the interface) that were appearantly left out in the Be implementation. It is currently built as a small static library from src/libs. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19913 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/libs/usb/USBKit.h | 316 +++++++++++++++++++++++++ src/libs/Jamfile | 1 + src/libs/usb/Jamfile | 16 ++ src/libs/usb/USBConfiguration.cpp | 99 ++++++++ src/libs/usb/USBDevice.cpp | 371 ++++++++++++++++++++++++++++++ src/libs/usb/USBEndpoint.cpp | 191 +++++++++++++++ src/libs/usb/USBInterface.cpp | 149 ++++++++++++ src/libs/usb/USBRoster.cpp | 269 ++++++++++++++++++++++ 8 files changed, 1412 insertions(+) create mode 100644 headers/libs/usb/USBKit.h create mode 100644 src/libs/usb/Jamfile create mode 100644 src/libs/usb/USBConfiguration.cpp create mode 100644 src/libs/usb/USBDevice.cpp create mode 100644 src/libs/usb/USBEndpoint.cpp create mode 100644 src/libs/usb/USBInterface.cpp create mode 100644 src/libs/usb/USBRoster.cpp diff --git a/headers/libs/usb/USBKit.h b/headers/libs/usb/USBKit.h new file mode 100644 index 0000000000..f83dffd05c --- /dev/null +++ b/headers/libs/usb/USBKit.h @@ -0,0 +1,316 @@ +/* + * Copyright 2007, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#ifndef _USBKIT_H +#define _USBKIT_H + +#include +#include + + +// Keep compatibility with original USBKit classes +#define USBRoster BUSBRoster +#define USBDevice BUSBDevice +#define USBConfiguration BUSBConfiguration +#define USBInterface BUSBInterface +#define USBEndpoint BUSBEndpoint + + +class BUSBRoster; +class BUSBDevice; +class BUSBConfiguration; +class BUSBInterface; +class BUSBEndpoint; + + +/* The BUSBRoster class can be used to watch for devices that get attached or + removed from the USB bus. + You subclass the roster and implement the pure virtual DeviceAdded() and + DeviceRemoved() hooks. */ +class BUSBRoster { +public: + BUSBRoster(); +virtual ~BUSBRoster(); + + // The DeviceAdded() hook will be called when a new device gets + // attached to the USB bus. The hook is called with an initialized + // BUSBDevice object. If you return B_OK from your hook the object + // will stay valid and the DeviceRemoved() hook will be called with + // for it. Otherwise the object is deleted and DeviceRemoved() + // is not called. +virtual status_t DeviceAdded(BUSBDevice *device) = 0; + + // When a device gets detached from the bus that you hold a + // BUSBDevice object for (gotten through DeviceAdded()) the + // DeviceRemoved() hook will be called. The device object gets + // invalid and will be deleted as soon as you return from the hook + // so be sure to remove all references to it. +virtual void DeviceRemoved(BUSBDevice *device) = 0; + + void Start(); + void Stop(); + +private: + void *fLooper; +}; + + +/* The BUSBDevice presents an interface to USB device. You can either get + it through the BUSBRoster or by creating one yourself and setting it to + a valid raw usb device (with a path of "/dev/bus/usb/x"). + The device class provides direct accessors for descriptor fields as well + as convenience functions to directly get string representations of fields. + The BUSBDevice also provides access for the BUSBConfiguration objects of + a device. These objects and all of their child objects depend on the + parent device and will be deleted as soon as the device object is + destroyed */ +class BUSBDevice { +public: + BUSBDevice(const char *path = NULL); +virtual ~BUSBDevice(); + +virtual status_t InitCheck(); + + status_t SetTo(const char *path); + void Unset(); + + // Returns the location on the bus represented as hub/device sequence + const char *Location() const; + bool IsHub() const; + + // These are direct accessors to descriptor fields + uint16 USBVersion() const; + uint8 Class() const; + uint8 Subclass() const; + uint8 Protocol() const; + uint8 MaxEndpoint0PacketSize() const; + uint16 VendorID() const; + uint16 ProductID() const; + uint16 Version() const; + + // The string functions return the string representation of the + // descriptor data. The strings are decoded to normal 0 terminated + // c strings and are cached and owned by the object. + // If a string is not available an empty string is returned. + const char *ManufacturerString() const; + const char *ProductString() const; + const char *SerialNumberString() const; + + const usb_device_descriptor *Descriptor() const; + + // GetStringDescriptor() can be used to retrieve the raw + // usb_string_descriptor with a given index. The strings contained + // in these descriptors are usually two-byte unicode encoded. + size_t GetStringDescriptor(uint32 index, + usb_string_descriptor *descriptor, + size_t length) const; + + // Use the DecodeStringDescriptor() convenience function to get a + // 0-terminated c string for a given string index. Note that this + // will allocate the string as "new char[];" and needs to be deleted + // like "delete[] string;" by the caller. + char *DecodeStringDescriptor(uint32 index) const; + + size_t GetDescriptor(uint8 type, uint8 index, + uint16 languageID, void *data, + size_t length) const; + + // With ConfigurationAt() or ActiveConfiguration() you can get an + // object that represents the configuration at a certain index or at + // the index that is currently configured. Note that the index does not + // necessarily correspond with the configuration value. + // Use the returned object as an argument to SetConfiguration() to + // change the active configuration of a device. + uint32 CountConfigurations() const; + const BUSBConfiguration *ConfigurationAt(uint32 index) const; + + const BUSBConfiguration *ActiveConfiguration() const; + status_t SetConfiguration( + const BUSBConfiguration *configuration); + + // ControlTransfer() sends requests using the default pipe + ssize_t ControlTransfer(uint8 requestType, + uint8 request, uint16 value, + uint16 index, uint16 length, + void *data) const; + +private: + char *fPath; + int fRawFD; + + usb_device_descriptor fDescriptor; + BUSBConfiguration **fConfigurations; + uint32 fActiveConfiguration; + +mutable char *fManufacturerString; +mutable char *fProductString; +mutable char *fSerialNumberString; +}; + + +/* A BUSBConfiguration object represents one of possibly multiple + configurations a device might have. A valid object can only be gotten + through the ConfigurationAt() and ActiveConfiguration() methods of a + BUSBDevice. + The BUSBConfiguration provides further access into the configuration by + providing CountInterfaces() and InterfaceAt() to retrieve BUSBInterface + objects. */ +class BUSBConfiguration { +public: + // Device() returns the parent device of this configuration. This + // configuration is located at the index returned by Index() within + // that parent device. + uint32 Index() const; + const BUSBDevice *Device() const; + + // Gets a describing string of this configuration if available. + // Otherwise an empty string is returned. + const char *ConfigurationString() const; + + const usb_configuration_descriptor + *Descriptor() const; + + // With CountInterfaces() and InterfaceAt() you can iterate through + // the child interfaces of this configuration. It is the only valid + // way to get a BUSBInterface object. + // Note that the interface objects retrieved using InterfaceAt() will + // be invalid and deleted as soon as this configuration gets deleted. + uint32 CountInterfaces() const; + const BUSBInterface *InterfaceAt(uint32 index) const; + +private: +friend class BUSBDevice; + BUSBConfiguration(BUSBDevice *device, + uint32 index, int rawFD); + ~BUSBConfiguration(); + + BUSBDevice *fDevice; + uint32 fIndex; + int fRawFD; + + usb_configuration_descriptor fDescriptor; + BUSBInterface **fInterfaces; + +mutable char *fConfigurationString; +}; + + +/* The BUSBInterface class can be used to access the descriptor fields of + an underleying USB interface. Most importantly though it can be used to + iterate over and retrieve BUSBEndpoint objects that can be used to + transfer data over the bus. */ +class BUSBInterface { +public: + // Configuration() returns the parent configuration of this interface. + // This interface is located at the index returned by Index() in that + // parent configuration. + // Device() is a convenience function to directly reach the parent + // device of this interface instead of going through the configuration. + uint32 Index() const; + const BUSBConfiguration *Configuration() const; + const BUSBDevice *Device() const; + + // These are accessors to descriptor fields. InterfaceString() tries + // to return a describing string of this interface. If no string is + // available an empty string will be returned. + uint8 Class() const; + uint8 Subclass() const; + uint8 Protocol() const; + const char *InterfaceString() const; + + const usb_interface_descriptor + *Descriptor() const; + + // Use OtherDescriptorAt() to get generic descriptors of an interface. + // These are usually vendor or device specific extensions. + status_t OtherDescriptorAt(uint32 index, + usb_descriptor *descriptor, + size_t length) const; + + // CountEndpoints() and EndpointAt() can be used to iterate over the + // available endpoints within an interface. EndpointAt() is the only + // valid way to get BUSBEndpoint object. Note that these objects will + // get invalid and deleted as soon as the parent interface is deleted. + uint32 CountEndpoints() const; + const BUSBEndpoint *EndpointAt(uint32 index) const; + +private: +friend class BUSBConfiguration; + BUSBInterface(BUSBConfiguration *config, + uint32 index, int rawFD); + ~BUSBInterface(); + + BUSBConfiguration *fConfiguration; + uint32 fIndex; + int fRawFD; + + usb_interface_descriptor fDescriptor; + BUSBEndpoint **fEndpoints; + +mutable char *fInterfaceString; +}; + + +/* The BUSBEndpoint represent a device endpoint that can be used to send or + receive data. It also alows to query endpoint characteristics like + endpoint type or direction. */ +class BUSBEndpoint { +public: + // Interface() returns the parent interface of this endpoint. + // This endpoint is located at the index returned by Index() in the + // parent interface. + // Configuration() and Device() are convenience functions to directly + // reach the parent configuration or device of this endpoint instead + // of going through the parent objects. + uint32 Index() const; + const BUSBInterface *Interface() const; + const BUSBConfiguration *Configuration() const; + const BUSBDevice *Device() const; + + // These methods can be used to check for endpoint characteristics. + bool IsBulk() const; + bool IsInterrupt() const; + bool IsIsochronous() const; + bool IsControl() const; + + bool IsInput() const; + bool IsOutput() const; + + uint16 MaxPacketSize() const; + uint8 Interval() const; + + const usb_endpoint_descriptor + *Descriptor() const; + + // These methods initiate transfers to or from the endpoint. All + // transfers are synchronous and the actually transfered amount of + // data is returned as a result. A negative value indicates an error. + // Which transfer type to use depends on the endpoint type. + ssize_t ControlTransfer(uint8 requestType, + uint8 request, uint16 value, + uint16 index, uint16 length, + void *data) const; + ssize_t InterruptTransfer(void *data, + size_t length) const; + ssize_t BulkTransfer(void *data, + size_t length) const; + +private: +friend class BUSBInterface; + BUSBEndpoint(BUSBInterface *interface, + uint32 index, int rawFD); + ~BUSBEndpoint(); + + BUSBInterface *fInterface; + uint32 fIndex; + int fRawFD; + + usb_endpoint_descriptor fDescriptor; +}; + +#endif diff --git a/src/libs/Jamfile b/src/libs/Jamfile index 7b80d1fafb..29095097a8 100644 --- a/src/libs/Jamfile +++ b/src/libs/Jamfile @@ -14,5 +14,6 @@ SubInclude HAIKU_TOP src libs pdflib ; SubInclude HAIKU_TOP src libs png ; SubInclude HAIKU_TOP src libs stdc++ ; SubInclude HAIKU_TOP src libs termcap ; +SubInclude HAIKU_TOP src libs usb ; SubInclude HAIKU_TOP src libs util ; SubInclude HAIKU_TOP src libs zlib ; diff --git a/src/libs/usb/Jamfile b/src/libs/usb/Jamfile new file mode 100644 index 0000000000..1ff166c8b3 --- /dev/null +++ b/src/libs/usb/Jamfile @@ -0,0 +1,16 @@ +SubDir HAIKU_TOP src libs usb ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +# for USBKit.h, usb_raw.h and lock.h +UseLibraryHeaders usb ; +UseHeaders [ FDirName $(HAIKU_TOP) src add-ons kernel drivers bus usb ] : true ; +UsePrivateHeaders kernel ; + +StaticLibrary USBKit.a : + USBRoster.cpp + USBDevice.cpp + USBConfiguration.cpp + USBInterface.cpp + USBEndpoint.cpp +; diff --git a/src/libs/usb/USBConfiguration.cpp b/src/libs/usb/USBConfiguration.cpp new file mode 100644 index 0000000000..77d17f1c20 --- /dev/null +++ b/src/libs/usb/USBConfiguration.cpp @@ -0,0 +1,99 @@ +/* + * Copyright 2007, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include +#include +#include +#include + + +BUSBConfiguration::BUSBConfiguration(BUSBDevice *device, uint32 index, int rawFD) + : fDevice(device), + fIndex(index), + fRawFD(rawFD), + fInterfaces(NULL), + fConfigurationString(NULL) +{ + raw_command command; + command.config.descriptor = &fDescriptor; + command.config.config_index = fIndex; + if (ioctl(fRawFD, RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR, &command, sizeof(command)) + || command.config.status != RAW_STATUS_SUCCESS) { + memset(&fDescriptor, 0, sizeof(fDescriptor)); + } + + fInterfaces = new BUSBInterface *[fDescriptor.number_interfaces]; + for (uint32 i = 0; i < fDescriptor.number_interfaces; i++) + fInterfaces[i] = new BUSBInterface(this, i, fRawFD); +} + + +BUSBConfiguration::~BUSBConfiguration() +{ + delete[] fConfigurationString; + for (int32 i = 0; i < fDescriptor.number_interfaces; i++) + delete fInterfaces[i]; + delete[] fInterfaces; +} + + +uint32 +BUSBConfiguration::Index() const +{ + return fIndex; +} + + +const BUSBDevice * +BUSBConfiguration::Device() const +{ + return fDevice; +} + + +const char * +BUSBConfiguration::ConfigurationString() const +{ + if (fDescriptor.configuration == 0) + return ""; + + if (fConfigurationString) + return fConfigurationString; + + fConfigurationString = Device()->DecodeStringDescriptor(fDescriptor.configuration); + if (!fConfigurationString) { + fConfigurationString = new char[1]; + fConfigurationString[0] = 0; + } + + return fConfigurationString; +} + + +const usb_configuration_descriptor * +BUSBConfiguration::Descriptor() const +{ + return &fDescriptor; +} + + +uint32 +BUSBConfiguration::CountInterfaces() const +{ + return fDescriptor.number_interfaces; +} + + +const BUSBInterface * +BUSBConfiguration::InterfaceAt(uint32 index) const +{ + if (index >= fDescriptor.number_interfaces) + return NULL; + + return fInterfaces[index]; +} diff --git a/src/libs/usb/USBDevice.cpp b/src/libs/usb/USBDevice.cpp new file mode 100644 index 0000000000..a29f44e948 --- /dev/null +++ b/src/libs/usb/USBDevice.cpp @@ -0,0 +1,371 @@ +/* + * Copyright 2007, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include +#include +#include +#include +#include + + +BUSBDevice::BUSBDevice(const char *path) + : fPath(NULL), + fRawFD(-1), + fConfigurations(NULL), + fActiveConfiguration(0), + fManufacturerString(NULL), + fProductString(NULL), + fSerialNumberString(NULL) +{ + memset(&fDescriptor, 0, sizeof(fDescriptor)); + + if (path) + SetTo(path); +} + + +BUSBDevice::~BUSBDevice() +{ + Unset(); +} + + +status_t +BUSBDevice::InitCheck() +{ + return (fRawFD >= 0 ? B_OK : B_ERROR); +} + + +status_t +BUSBDevice::SetTo(const char *path) +{ + if (!path) + return B_BAD_VALUE; + + fPath = strdup(path); + fRawFD = open(path, O_RDWR); + if (fRawFD < 0) { + Unset(); + return B_ERROR; + } + + raw_command command; + if (ioctl(fRawFD, RAW_COMMAND_GET_VERSION, &command, sizeof(command)) + || command.version.status != 0x0015) { + Unset(); + return B_ERROR; + } + + command.device.descriptor = &fDescriptor; + if (ioctl(fRawFD, RAW_COMMAND_GET_DEVICE_DESCRIPTOR, &command, sizeof(command)) + || command.device.status != RAW_STATUS_SUCCESS) { + Unset(); + return B_ERROR; + } + + fConfigurations = new BUSBConfiguration *[fDescriptor.num_configurations]; + for (uint32 i = 0; i < fDescriptor.num_configurations; i++) + fConfigurations[i] = new BUSBConfiguration(this, i, fRawFD); + + return B_OK; +} + + +void +BUSBDevice::Unset() +{ + if (fRawFD >= 0) + close(fRawFD); + fRawFD = -1; + + free(fPath); + fPath = NULL; + + delete[] fManufacturerString; + delete[] fProductString; + delete[] fSerialNumberString; + fManufacturerString = fProductString = fSerialNumberString = NULL; + + for (int32 i = 0; i < fDescriptor.num_configurations; i++) + delete fConfigurations[i]; + + delete[] fConfigurations; + memset(&fDescriptor, 0, sizeof(fDescriptor)); +} + + +const char * +BUSBDevice::Location() const +{ + if (!fPath || strlen(fPath) < 12) + return NULL; + + return &fPath[12]; +} + + +bool +BUSBDevice::IsHub() const +{ + return fDescriptor.device_class == 0x09; +} + + +uint16 +BUSBDevice::USBVersion() const +{ + return fDescriptor.usb_version; +} + + +uint8 +BUSBDevice::Class() const +{ + return fDescriptor.device_class; +} + + +uint8 +BUSBDevice::Subclass() const +{ + return fDescriptor.device_subclass; +} + + +uint8 +BUSBDevice::Protocol() const +{ + return fDescriptor.device_protocol; +} + + +uint8 +BUSBDevice::MaxEndpoint0PacketSize() const +{ + return fDescriptor.max_packet_size_0; +} + + +uint16 +BUSBDevice::VendorID() const +{ + return fDescriptor.vendor_id; +} + + +uint16 +BUSBDevice::ProductID() const +{ + return fDescriptor.product_id; +} + + +uint16 +BUSBDevice::Version() const +{ + return fDescriptor.device_version; +} + + +const char * +BUSBDevice::ManufacturerString() const +{ + if (fDescriptor.manufacturer == 0) + return ""; + + if (fManufacturerString) + return fManufacturerString; + + fManufacturerString = DecodeStringDescriptor(fDescriptor.manufacturer); + if (!fManufacturerString) { + fManufacturerString = new char[1]; + fManufacturerString[0] = 0; + } + + return fManufacturerString; +} + + +const char * +BUSBDevice::ProductString() const +{ + if (fDescriptor.product == 0) + return ""; + + if (fProductString) + return fProductString; + + fProductString = DecodeStringDescriptor(fDescriptor.product); + if (!fProductString) { + fProductString = new char[1]; + fProductString[0] = 0; + } + + return fProductString; +} + + +const char * +BUSBDevice::SerialNumberString() const +{ + if (fDescriptor.serial_number == 0) + return ""; + + if (fSerialNumberString) + return fSerialNumberString; + + fSerialNumberString = DecodeStringDescriptor(fDescriptor.serial_number); + if (!fSerialNumberString) { + fSerialNumberString = new char[1]; + fSerialNumberString[0] = 0; + } + + return fSerialNumberString; +} + + +const usb_device_descriptor * +BUSBDevice::Descriptor() const +{ + return &fDescriptor; +} + + +size_t +BUSBDevice::GetStringDescriptor(uint32 index, + usb_string_descriptor *descriptor, size_t length) const +{ + if (!descriptor) + return B_BAD_VALUE; + + raw_command command; + command.string.descriptor = descriptor; + command.string.string_index = index; + command.string.length = length; + + if (ioctl(fRawFD, RAW_COMMAND_GET_STRING_DESCRIPTOR, &command, sizeof(command)) + || command.string.status != RAW_STATUS_SUCCESS) { + return 0; + } + + return command.string.length; +} + + +char * +BUSBDevice::DecodeStringDescriptor(uint32 index) const +{ + char buffer[300]; + usb_string_descriptor *stringDescriptor; + stringDescriptor = (usb_string_descriptor *)&buffer; + + size_t stringLength = GetStringDescriptor(index, stringDescriptor, + sizeof(buffer) - sizeof(usb_string_descriptor)); + + if (stringLength < 3) + return NULL; + + // pseudo convert unicode string + stringLength = (stringLength - 2) / 2; + char *result = new char[stringLength + 1]; + for (size_t i = 0; i < stringLength; i++) + result[i] = stringDescriptor->string[i * 2]; + result[stringLength] = 0; + return result; +} + + +size_t +BUSBDevice::GetDescriptor(uint8 type, uint8 index, uint16 languageID, + void *data, size_t length) const +{ + if (length > 0 && data == NULL) + return B_BAD_VALUE; + + raw_command command; + command.descriptor.type = type; + command.descriptor.index = index; + command.descriptor.language_id = languageID; + command.descriptor.data = data; + command.descriptor.length = length; + + if (ioctl(fRawFD, RAW_COMMAND_GET_DESCRIPTOR, &command, sizeof(command)) + || command.descriptor.status != RAW_STATUS_SUCCESS) { + return 0; + } + + return command.descriptor.length; +} + + +uint32 +BUSBDevice::CountConfigurations() const +{ + return fDescriptor.num_configurations; +} + + +const BUSBConfiguration * +BUSBDevice::ConfigurationAt(uint32 index) const +{ + if (index >= fDescriptor.num_configurations) + return NULL; + + return fConfigurations[index]; +} + + +const BUSBConfiguration * +BUSBDevice::ActiveConfiguration() const +{ + return fConfigurations[fActiveConfiguration]; +} + + +status_t +BUSBDevice::SetConfiguration(const BUSBConfiguration *configuration) +{ + if (!configuration || configuration->Index() >= fDescriptor.num_configurations) + return B_BAD_VALUE; + + raw_command command; + command.config.config_index = configuration->Index(); + + if (ioctl(fRawFD, RAW_COMMAND_SET_CONFIGURATION, &command, sizeof(command)) + || command.config.status != RAW_STATUS_SUCCESS) { + return B_ERROR; + } + + fActiveConfiguration = configuration->Index(); + return B_OK; +} + + +ssize_t +BUSBDevice::ControlTransfer(uint8 requestType, uint8 request, uint16 value, + uint16 index, uint16 length, void *data) const +{ + if (length > 0 && data == NULL) + return B_BAD_VALUE; + + raw_command command; + command.control.request_type = requestType; + command.control.request = request; + command.control.value = value; + command.control.index = index; + command.control.length = length; + command.control.data = data; + + if (ioctl(fRawFD, RAW_COMMAND_CONTROL_TRANSFER, &command, sizeof(command)) + || command.control.status != RAW_STATUS_SUCCESS) { + return B_ERROR; + } + + return command.control.length; +} diff --git a/src/libs/usb/USBEndpoint.cpp b/src/libs/usb/USBEndpoint.cpp new file mode 100644 index 0000000000..5e8c37e2b0 --- /dev/null +++ b/src/libs/usb/USBEndpoint.cpp @@ -0,0 +1,191 @@ +/* + * Copyright 2007, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include +#include +#include +#include + + +BUSBEndpoint::BUSBEndpoint(BUSBInterface *interface, uint32 index, int rawFD) + : fInterface(interface), + fIndex(index), + fRawFD(rawFD) +{ + raw_command command; + command.endpoint.descriptor = &fDescriptor; + command.endpoint.config_index = fInterface->Configuration()->Index(); + command.endpoint.interface_index = fInterface->Index(); + command.endpoint.endpoint_index = fIndex; + if (ioctl(fRawFD, RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR, &command, sizeof(command)) + || command.config.status != RAW_STATUS_SUCCESS) { + memset(&fDescriptor, 0, sizeof(fDescriptor)); + } +} + + +BUSBEndpoint::~BUSBEndpoint() +{ +} + + +uint32 +BUSBEndpoint::Index() const +{ + return fIndex; +} + + +const BUSBInterface * +BUSBEndpoint::Interface() const +{ + return fInterface; +} + + +const BUSBConfiguration * +BUSBEndpoint::Configuration() const +{ + return fInterface->Configuration(); +} + + +const BUSBDevice * +BUSBEndpoint::Device() const +{ + return fInterface->Device(); +} + + +bool +BUSBEndpoint::IsBulk() const +{ + return (fDescriptor.attributes & USB_ENDPOINT_ATTR_MASK) == USB_ENDPOINT_ATTR_BULK; +} + + +bool +BUSBEndpoint::IsInterrupt() const +{ + return (fDescriptor.attributes & USB_ENDPOINT_ATTR_MASK) == USB_ENDPOINT_ATTR_INTERRUPT; +} + + +bool +BUSBEndpoint::IsIsochronous() const +{ + return (fDescriptor.attributes & USB_ENDPOINT_ATTR_MASK) == USB_ENDPOINT_ATTR_ISOCHRONOUS; +} + + +bool +BUSBEndpoint::IsControl() const +{ + return (fDescriptor.attributes & USB_ENDPOINT_ATTR_MASK) == USB_ENDPOINT_ATTR_CONTROL; +} + + +bool +BUSBEndpoint::IsInput() const +{ + return (fDescriptor.endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) == USB_ENDPOINT_ADDR_DIR_IN; +} + + +bool +BUSBEndpoint::IsOutput() const +{ + return (fDescriptor.endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) == USB_ENDPOINT_ADDR_DIR_OUT; +} + + +uint16 +BUSBEndpoint::MaxPacketSize() const +{ + return fDescriptor.max_packet_size; +} + + +uint8 +BUSBEndpoint::Interval() const +{ + return fDescriptor.interval; +} + + +const usb_endpoint_descriptor * +BUSBEndpoint::Descriptor() const +{ + return &fDescriptor; +} + + +ssize_t +BUSBEndpoint::ControlTransfer(uint8 requestType, uint8 request, uint16 value, + uint16 index, uint16 length, void *data) const +{ + if (length > 0 && data == NULL) + return B_BAD_VALUE; + + raw_command command; + command.control.request_type = requestType; + command.control.request = request; + command.control.value = value; + command.control.index = index; + command.control.length = length; + command.control.data = data; + + if (ioctl(fRawFD, RAW_COMMAND_CONTROL_TRANSFER, &command, sizeof(command)) + || command.control.status != RAW_STATUS_SUCCESS) { + return B_ERROR; + } + + return command.control.length; +} + + +ssize_t +BUSBEndpoint::InterruptTransfer(void *data, size_t length) const +{ + if (length > 0 && data == NULL) + return B_BAD_VALUE; + + raw_command command; + command.transfer.interface = fInterface->Index(); + command.transfer.endpoint = fIndex; + command.transfer.data = data; + command.transfer.length = length; + + if (ioctl(fRawFD, RAW_COMMAND_INTERRUPT_TRANSFER, &command, sizeof(command)) + || command.transfer.status != RAW_STATUS_SUCCESS) { + return B_ERROR; + } + + return command.transfer.length; +} + + +ssize_t +BUSBEndpoint::BulkTransfer(void *data, size_t length) const +{ + if (length > 0 && data == NULL) + return B_BAD_VALUE; + + raw_command command; + command.transfer.interface = fInterface->Index(); + command.transfer.endpoint = fIndex; + command.transfer.data = data; + command.transfer.length = length; + + if (ioctl(fRawFD, RAW_COMMAND_BULK_TRANSFER, &command, sizeof(command)) + || command.transfer.status != RAW_STATUS_SUCCESS) { + return B_ERROR; + } + + return command.transfer.length; +} diff --git a/src/libs/usb/USBInterface.cpp b/src/libs/usb/USBInterface.cpp new file mode 100644 index 0000000000..821c160785 --- /dev/null +++ b/src/libs/usb/USBInterface.cpp @@ -0,0 +1,149 @@ +/* + * Copyright 2007, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include +#include +#include +#include + + +BUSBInterface::BUSBInterface(BUSBConfiguration *config, uint32 index, int rawFD) + : fConfiguration(config), + fIndex(index), + fRawFD(rawFD), + fEndpoints(NULL), + fInterfaceString(NULL) +{ + raw_command command; + command.interface.descriptor = &fDescriptor; + command.interface.config_index = fConfiguration->Index(); + command.interface.interface_index = fIndex; + if (ioctl(fRawFD, RAW_COMMAND_GET_INTERFACE_DESCRIPTOR, &command, sizeof(command)) + || command.interface.status != RAW_STATUS_SUCCESS) { + memset(&fDescriptor, 0, sizeof(fDescriptor)); + } + + fEndpoints = new BUSBEndpoint *[fDescriptor.num_endpoints]; + for (int32 i = 0; i < fDescriptor.num_endpoints; i++) + fEndpoints[i] = new BUSBEndpoint(this, i, fRawFD); +} + + +BUSBInterface::~BUSBInterface() +{ + delete[] fInterfaceString; + for (int32 i = 0; i < fDescriptor.num_endpoints; i++) + delete fEndpoints[i]; + delete[] fEndpoints; +} + + +uint32 +BUSBInterface::Index() const +{ + return fIndex; +} + + +const BUSBConfiguration * +BUSBInterface::Configuration() const +{ + return fConfiguration; +} + + +const BUSBDevice * +BUSBInterface::Device() const +{ + return fConfiguration->Device(); +} + + +uint8 +BUSBInterface::Class() const +{ + return fDescriptor.interface_class; +} + + +uint8 +BUSBInterface::Subclass() const +{ + return fDescriptor.interface_subclass; +} + + +uint8 +BUSBInterface::Protocol() const +{ + return fDescriptor.interface_protocol; +} + + +const char * +BUSBInterface::InterfaceString() const +{ + if (fDescriptor.interface == 0) + return ""; + + if (fInterfaceString) + return fInterfaceString; + + fInterfaceString = Device()->DecodeStringDescriptor(fDescriptor.interface); + if (!fInterfaceString) { + fInterfaceString = new char[1]; + fInterfaceString[0] = 0; + } + + return fInterfaceString; +} + + +const usb_interface_descriptor * +BUSBInterface::Descriptor() const +{ + return &fDescriptor; +} + + +status_t +BUSBInterface::OtherDescriptorAt(uint32 index, usb_descriptor *descriptor, + size_t length) const +{ + if (length > 0 && descriptor == NULL) + return B_BAD_VALUE; + + raw_command command; + command.generic.descriptor = descriptor; + command.generic.config_index = fConfiguration->Index(); + command.generic.interface_index = fIndex; + command.generic.length = length; + if (ioctl(fRawFD, RAW_COMMAND_GET_GENERIC_DESCRIPTOR, &command, sizeof(command)) + || command.generic.status != RAW_STATUS_SUCCESS) { + return B_ERROR; + } + + return B_OK; +} + + +uint32 +BUSBInterface::CountEndpoints() const +{ + return fDescriptor.num_endpoints; +} + + +const BUSBEndpoint * +BUSBInterface::EndpointAt(uint32 index) const +{ + if (index >= fDescriptor.num_endpoints) + return NULL; + + return fEndpoints[index]; +} diff --git a/src/libs/usb/USBRoster.cpp b/src/libs/usb/USBRoster.cpp new file mode 100644 index 0000000000..5d40fa2cec --- /dev/null +++ b/src/libs/usb/USBRoster.cpp @@ -0,0 +1,269 @@ +/* + * Copyright 2007, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class WatchedEntry { +public: + WatchedEntry(BUSBRoster *roster, + BMessenger *messenger, entry_ref *ref); + ~WatchedEntry(); + + bool EntryCreated(entry_ref *ref); + bool EntryRemoved(ino_t node); + +private: + BUSBRoster *fRoster; + BMessenger *fMessenger; + + node_ref fNode; + bool fIsDirectory; + BUSBDevice *fDevice; + + WatchedEntry *fEntries; + WatchedEntry *fLink; +}; + + +class RosterLooper : public BLooper { +public: + RosterLooper(BUSBRoster *roster); + + void Stop(); + +virtual void MessageReceived(BMessage *message); + +private: + BUSBRoster *fRoster; + WatchedEntry *fRoot; + BMessenger *fMessenger; +}; + + +WatchedEntry::WatchedEntry(BUSBRoster *roster, BMessenger *messenger, + entry_ref *ref) + : fRoster(roster), + fMessenger(messenger), + fIsDirectory(false), + fDevice(NULL), + fEntries(NULL), + fLink(NULL) +{ + BEntry entry(ref); + entry.GetNodeRef(&fNode); + + BDirectory directory; + if (entry.IsDirectory() && directory.SetTo(ref) >= B_OK) { + fIsDirectory = true; + + while(directory.GetNextEntry(&entry) >= B_OK) { + if (entry.GetRef(ref) < B_OK) + continue; + + WatchedEntry *child = new WatchedEntry(fRoster, fMessenger, ref); + child->fLink = fEntries; + fEntries = child; + } + + watch_node(&fNode, B_WATCH_DIRECTORY, *fMessenger); + } else { + // filter pseudoentry that only handles ioctls + if (strncmp(ref->name, "raw", 3) == 0) + return; + + BPath path; + entry.GetPath(&path); + fDevice = new BUSBDevice(path.Path()); + if (fRoster->DeviceAdded(fDevice) != B_OK) { + delete fDevice; + fDevice = NULL; + } + } +} + + +WatchedEntry::~WatchedEntry() +{ + if (fIsDirectory) { + watch_node(&fNode, B_STOP_WATCHING, *fMessenger); + + WatchedEntry *child = fEntries; + while (child) { + WatchedEntry *next = child->fLink; + delete child; + child = next; + } + } + + if (fDevice) + fRoster->DeviceRemoved(fDevice); +} + + +bool +WatchedEntry::EntryCreated(entry_ref *ref) +{ + if (!fIsDirectory) + return false; + + if (ref->directory != fNode.node) { + WatchedEntry *child = fEntries; + while (child) { + if (child->EntryCreated(ref)) + return true; + child = child->fLink; + } + + return false; + } + + WatchedEntry *child = new WatchedEntry(fRoster, fMessenger, ref); + child->fLink = fEntries; + fEntries = child; + return true; +} + + +bool +WatchedEntry::EntryRemoved(ino_t node) +{ + if (!fIsDirectory) + return false; + + WatchedEntry *child = fEntries; + WatchedEntry *lastChild = NULL; + while (child) { + if (child->fNode.node == node) { + if (lastChild) + lastChild->fLink = child->fLink; + else + fEntries = child->fLink; + + delete child; + return true; + } + + if (child->EntryRemoved(node)) + return true; + + lastChild = child; + child = child->fLink; + } + + return false; +} + + +RosterLooper::RosterLooper(BUSBRoster *roster) + : fRoster(roster), + fRoot(NULL), + fMessenger(NULL) +{ + BEntry entry("/dev/bus/usb"); + if (!entry.Exists()) { + fprintf(stderr, "USBKit: usb_raw not published"); + return; + } + + Run(); + fMessenger = new BMessenger(this); + + if (Lock()) { + entry_ref ref; + entry.GetRef(&ref); + fRoot = new WatchedEntry(fRoster, fMessenger, &ref); + Unlock(); + } +} + + +void +RosterLooper::Stop() +{ + Lock(); + delete fRoot; + Quit(); +} + + +void +RosterLooper::MessageReceived(BMessage *message) +{ + int32 opcode; + if (message->FindInt32("opcode", &opcode) < B_OK) + return; + + switch (opcode) { + case B_ENTRY_CREATED: { + dev_t device; + ino_t directory; + const char *name; + if (message->FindInt32("device", &device) < B_OK + || message->FindInt64("directory", &directory) < B_OK + || message->FindString("name", &name) < B_OK) + break; + + entry_ref ref(device, directory, name); + fRoot->EntryCreated(&ref); + break; + } + + case B_ENTRY_REMOVED: { + ino_t node; + if (message->FindInt64("node", &node) < B_OK) + break; + + fRoot->EntryRemoved(node); + break; + } + } +} + + +BUSBRoster::BUSBRoster() + : fLooper(NULL) +{ +} + + +BUSBRoster::~BUSBRoster() +{ + Stop(); +} + + +void +BUSBRoster::Start() +{ + if (fLooper) + return; + + fLooper = new RosterLooper(this); +} + + +void +BUSBRoster::Stop() +{ + if (!fLooper) + return; + + ((RosterLooper *)fLooper)->Stop(); + fLooper = NULL; +}