#include "DeviceUSB.h"
#include <new>
#include <sstream>
#include <stdlib.h>
#include <Catalog.h>
#include <Directory.h>
#include <Entry.h>
#include <Path.h>
#include <USBKit.h>
#include <bus/USB.h>
#include <usb/USB_hid.h>
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "DeviceUSB"
extern "C" {
#include "dm_wrapper.h"
#include "usb-utils.h"
}
#define USB_SELF_POWERED 0x40
#define USB_REMOTE_WAKEUP 0x20
static uint8
MajorVersion(uint16 version)
{
return version >> 8;
}
static uint8
MinorVersion(uint16 version)
{
return version & 0xff;
}
DeviceUSB::DeviceUSB(Device* parent)
:
Device(parent),
fVendorId(0),
fDeviceId(0)
{
}
DeviceUSB::~DeviceUSB()
{
}
static BString ToHex(uint16 num)
{
std::stringstream ss;
ss.flags(std::ios::hex | std::ios::showbase);
ss << num;
return BString(ss.str().c_str());
}
static BString
FindDevicePath(const char* currentPath, uint16 vendor, uint16 product)
{
BDirectory dir(currentPath);
if (dir.InitCheck() != B_OK)
return "";
BEntry entry;
while (dir.GetNextEntry(&entry) == B_OK) {
BPath pathObj;
entry.GetPath(&pathObj);
if (BString(pathObj.Leaf()) == "hub")
continue;
if (entry.IsDirectory()) {
BString result = FindDevicePath(pathObj.Path(), vendor, product);
if (result != "")
return result;
} else {
BUSBDevice testDevice(pathObj.Path());
if (testDevice.InitCheck() == B_OK && testDevice.VendorID() == vendor
&& testDevice.ProductID() == product) {
return BString(pathObj.Path());
}
}
}
return "";
}
static ssize_t
FetchReportDescriptor(BUSBDevice& device, uint16 interfaceNumber, uint8* buffer, uint16 length)
{
return device.ControlTransfer(USB_REQTYPE_INTERFACE_IN, USB_REQUEST_GET_DESCRIPTOR,
B_USB_HID_DESCRIPTOR_REPORT << 8, interfaceNumber, length, buffer);
}
static BString
GetHIDReportDescriptor(BUSBDevice& device, const BUSBInterface& interface)
{
uint8 buffer[256];
usb_hid_descriptor* hidDescriptor = NULL;
for (uint32 i = 0; i < 128; i++) {
usb_descriptor* generic = reinterpret_cast<usb_descriptor*>(buffer);
if (interface.OtherDescriptorAt(i, generic, sizeof(buffer)) != B_OK)
break;
if (generic->generic.descriptor_type == B_USB_HID_DESCRIPTOR_HID) {
hidDescriptor = reinterpret_cast<usb_hid_descriptor*>(buffer);
break;
}
}
if (hidDescriptor == NULL)
return "";
uint16 reportLength = 0;
for (uint8 i = 0; i < hidDescriptor->num_descriptors; i++) {
if (hidDescriptor->descriptor_info[i].descriptor_type == B_USB_HID_DESCRIPTOR_REPORT) {
reportLength = hidDescriptor->descriptor_info[i].descriptor_length;
break;
}
}
if (reportLength == 0)
return "";
uint8* reportBuffer = new(std::nothrow) uint8[reportLength];
if (reportBuffer == NULL)
return "";
BString result;
ssize_t read = FetchReportDescriptor(device, interface.Descriptor()->interface_number,
reportBuffer, reportLength);
if (read <= 0) {
delete[] reportBuffer;
return "";
}
for (ssize_t i = 0; i < read; i++) {
char hex[4];
snprintf(hex, sizeof(hex), "%02x ", reportBuffer[i]);
result << hex;
}
result.Trim();
delete[] reportBuffer;
return result;
}
static void
BuildUSBTree(BUSBDevice& device, DeviceUSB* node)
{
node->SetAttribute("usb/path", device.Location());
if (device.ManufacturerString())
node->SetAttribute("usb/manufacturer", device.ManufacturerString());
if (device.ProductString())
node->SetAttribute("usb/product", device.ProductString());
if (device.SerialNumberString())
node->SetAttribute("usb/serial_number", device.SerialNumberString());
BString usbVersion;
usbVersion.SetToFormat(B_TRANSLATE("%d.%d"),
MajorVersion(device.USBVersion()),
MinorVersion(device.USBVersion()));
node->SetAttribute("usb/version", usbVersion);
for (uint32 i = 0; i < device.CountConfigurations(); i++) {
const BUSBConfiguration* config = device.ConfigurationAt(i);
if (!config)
continue;
BString confPath;
confPath << "usb/config/" << i;
BString powerValue;
powerValue.SetToFormat(B_TRANSLATE("%d mA"), config->Descriptor()->max_power * 2);
node->SetAttribute(BString(confPath) << "/max_power", powerValue);
uint8 attr = config->Descriptor()->attributes;
if (attr & USB_SELF_POWERED)
node->SetAttribute(BString(confPath) << "/self_powered", "true");
if (attr & USB_REMOTE_WAKEUP)
node->SetAttribute(BString(confPath) << "/remote_wakeup", "true");
if (config->ConfigurationString())
node->SetAttribute(BString(confPath) << "/name", config->ConfigurationString());
for (uint32 j = 0; j < config->CountInterfaces(); j++) {
const BUSBInterface* interface = config->InterfaceAt(j);
if (!interface)
continue;
BString intfPath;
intfPath << confPath << "/interface/" << j;
for (uint32 k = 0; k < interface->CountAlternates(); k++) {
const BUSBInterface* alt = interface->AlternateAt(k);
if (!alt)
continue;
BString altPath;
altPath << intfPath << "/alt/" << k;
if (k == interface->AlternateIndex())
node->SetAttribute(BString(altPath) << "/active", "true");
char classInfo[128];
usb_get_class_info(alt->Class(), alt->Subclass(), alt->Protocol(), classInfo,
sizeof(classInfo));
BString classVal;
classVal.SetToFormat(B_TRANSLATE("0x%02x, Sub: 0x%02x (%s)"), alt->Class(),
alt->Subclass(), classInfo);
node->SetAttribute(BString(altPath) << "/class_info", classVal);
if (alt->InterfaceString())
node->SetAttribute(BString(altPath) << "/name", alt->InterfaceString());
for (uint32 e = 0; e < alt->CountEndpoints(); e++) {
const BUSBEndpoint* endpoint = alt->EndpointAt(e);
if (!endpoint)
continue;
BString endpointPath;
endpointPath << altPath << "/endpoint/" << e;
const usb_endpoint_descriptor* endpointDesc = endpoint->Descriptor();
BString endpointAddress;
endpointAddress.SetToFormat(B_TRANSLATE("0x%02x (%s)"),
endpointDesc->endpoint_address,
(endpointDesc->endpoint_address & USB_ENDPOINT_ADDR_DIR_MASK)
== USB_ENDPOINT_ADDR_DIR_IN
? B_TRANSLATE("IN")
: B_TRANSLATE("OUT"));
node->SetAttribute(BString(endpointPath) << "/address", endpointAddress);
const char* type = B_TRANSLATE("Unknown");
switch (endpointDesc->attributes & USB_ENDPOINT_ATTR_MASK) {
case USB_ENDPOINT_ATTR_CONTROL:
type = B_TRANSLATE("Control");
break;
case USB_ENDPOINT_ATTR_ISOCHRONOUS:
type = B_TRANSLATE("Isochronous");
break;
case USB_ENDPOINT_ATTR_BULK:
type = B_TRANSLATE("Bulk");
break;
case USB_ENDPOINT_ATTR_INTERRUPT:
type = B_TRANSLATE("Interrupt");
break;
}
node->SetAttribute(BString(endpointPath) << "/type", type);
BString maxPacket;
maxPacket << endpointDesc->max_packet_size;
node->SetAttribute(BString(endpointPath) << "/max_packet_size", maxPacket);
if ((endpointDesc->attributes & USB_ENDPOINT_ATTR_MASK)
== USB_ENDPOINT_ATTR_INTERRUPT) {
BString interval;
interval.SetToFormat(B_TRANSLATE("%u ms"), endpointDesc->interval);
node->SetAttribute(BString(endpointPath) << "/interval", interval);
}
}
if (alt->Class() == USB_HID_DEVICE_CLASS) {
BString reportDescriptor = GetHIDReportDescriptor(device, *alt);
if (reportDescriptor.Length() > 0) {
node->SetAttribute(BString(altPath) << "/hid_report_descriptor",
reportDescriptor);
}
}
}
}
}
}
void
DeviceUSB::InitFromAttributes()
{
fClassBaseId = atoi(fAttributeMap[USB_DEVICE_CLASS].String());
fClassSubId = atoi(fAttributeMap[USB_DEVICE_SUBCLASS].String());
fClassProtoId = atoi(fAttributeMap[USB_DEVICE_PROTOCOL].String());
fVendorId = atoi(fAttributeMap[B_DEVICE_VENDOR_ID].String());
fDeviceId = atoi(fAttributeMap[B_DEVICE_ID].String());
fAttributeMap[USB_DEVICE_CLASS] = ToHex(fClassBaseId);
fAttributeMap[USB_DEVICE_SUBCLASS] = ToHex(fClassSubId);
fAttributeMap[USB_DEVICE_PROTOCOL] = ToHex(fClassProtoId);
fAttributeMap[B_DEVICE_VENDOR_ID] = ToHex(fVendorId);
fAttributeMap[B_DEVICE_ID] = ToHex(fDeviceId);
char classInfo[128];
usb_get_class_info(fClassBaseId, fClassSubId, fClassProtoId, classInfo,
sizeof(classInfo));
const char* vendorName = NULL;
const char* deviceName = NULL;
BString deviceLabel;
BString manufacturerLabel;
usb_get_vendor_info(fVendorId, &vendorName);
usb_get_device_info(fVendorId, fDeviceId, &deviceName);
if (vendorName == NULL) {
manufacturerLabel << B_TRANSLATE("unknown");
} else {
manufacturerLabel << vendorName;
};
if (deviceName == NULL) {
deviceLabel << B_TRANSLATE("unknown");
} else {
deviceLabel << deviceName;
}
SetAttribute(B_TRANSLATE("Device name"), deviceLabel);
SetAttribute(B_TRANSLATE("Manufacturer"), manufacturerLabel);
SetAttribute(B_TRANSLATE("Class info"), classInfo);
switch (fClassBaseId) {
case 0x1:
fCategory = CAT_MULTIMEDIA; break;
case 0x2:
fCategory = CAT_COMM; break;
case 0x3:
fCategory = CAT_INPUT; break;
case 0x6:
fCategory = CAT_MULTIMEDIA; break;
case 0x7:
fCategory = CAT_MULTIMEDIA; break;
case 0x8:
fCategory = CAT_MASS; break;
case 0x9:
fCategory = CAT_GENERIC; break;
case 0xa:
fCategory = CAT_COMM; break;
case 0xe:
fCategory = CAT_MULTIMEDIA; break;
case 0xe0:
fCategory = CAT_WIRELESS; break;
}
BString outlineName;
outlineName << manufacturerLabel << " " << deviceLabel;
SetText(outlineName.String());
BString path = FindDevicePath("/dev/bus/usb/", fVendorId, fDeviceId);
if (path.Length() > 0) {
BUSBDevice device(path.String());
if (device.InitCheck() == B_OK)
BuildUSBTree(device, this);
}
}