mirror of https://gitlab.com/nakst/essence
345 lines
11 KiB
C++
345 lines
11 KiB
C++
// This file is part of the Essence operating system.
|
|
// It is released under the terms of the MIT license -- see LICENSE.md.
|
|
// Written by: nakst.
|
|
|
|
#include <module.h>
|
|
|
|
#define SETUP_FLAG_D2H (0x80)
|
|
|
|
#define DESCRIPTOR_DEVICE (1)
|
|
#define DESCRIPTOR_CONFIGURATION (2)
|
|
#define DESCRIPTOR_STRING (3)
|
|
#define DESCRIPTOR_INTERFACE (4)
|
|
#define DESCRIPTOR_ENDPOINT (5)
|
|
|
|
KUSBDescriptorHeader *KUSBDevice::GetCommonDescriptor(uint8_t type, uintptr_t index) {
|
|
uintptr_t position = selectedConfigurationOffset;
|
|
|
|
while (position < configurationDescriptorsBytes) {
|
|
KUSBDescriptorHeader *header = (KUSBDescriptorHeader *) (configurationDescriptors + position);
|
|
|
|
if (header->descriptorType == DESCRIPTOR_INTERFACE || header->descriptorType == DESCRIPTOR_CONFIGURATION) {
|
|
return nullptr;
|
|
} else if (header->descriptorType == type) {
|
|
if (index) {
|
|
index--;
|
|
} else {
|
|
return header;
|
|
}
|
|
}
|
|
|
|
position += header->length;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
struct SynchronousTransfer {
|
|
KEvent complete;
|
|
size_t *bytesNotTransferred;
|
|
bool success;
|
|
};
|
|
|
|
void SynchronousTransferCallback(ptrdiff_t bytesNotTransferred, EsGeneric context) {
|
|
SynchronousTransfer *transfer = (SynchronousTransfer *) context.p;
|
|
|
|
if (bytesNotTransferred != -1) {
|
|
if (transfer->bytesNotTransferred) {
|
|
transfer->success = true;
|
|
*transfer->bytesNotTransferred = bytesNotTransferred;
|
|
} else if (!bytesNotTransferred) {
|
|
transfer->success = true;
|
|
}
|
|
}
|
|
|
|
KEventSet(&transfer->complete);
|
|
}
|
|
|
|
bool KUSBDevice::RunTransfer(KUSBEndpointDescriptor *endpoint, void *buffer, size_t bufferBytes, size_t *bytesNotTransferred) {
|
|
SynchronousTransfer transfer = {};
|
|
transfer.bytesNotTransferred = bytesNotTransferred;
|
|
queueTransfer(this, endpoint, SynchronousTransferCallback, buffer, bufferBytes, &transfer);
|
|
KEventWait(&transfer.complete);
|
|
return transfer.success;
|
|
}
|
|
|
|
bool KUSBDevice::GetString(uint8_t index, char *buffer, size_t bufferBytes) {
|
|
uint16_t wideBuffer[127];
|
|
|
|
if (!bufferBytes || !index) {
|
|
return false;
|
|
}
|
|
|
|
uint16_t transferred = 0;
|
|
|
|
if (!controlTransfer(this, SETUP_FLAG_D2H, 0x06 /* get descriptor */,
|
|
((uint16_t) DESCRIPTOR_STRING << 8) | (uint16_t) index, 0,
|
|
wideBuffer, sizeof(wideBuffer), K_ACCESS_READ, &transferred)) {
|
|
return false;
|
|
}
|
|
|
|
if (transferred < 4) {
|
|
return false;
|
|
}
|
|
|
|
size_t inputCharactersRemaining = (*(uint8_t *) wideBuffer) / 2 - 1;
|
|
|
|
if ((size_t) (transferred / 2 - 1) < inputCharactersRemaining) {
|
|
inputCharactersRemaining = transferred / 2 - 1;
|
|
}
|
|
|
|
uint16_t *inputPosition = wideBuffer + 1;
|
|
uintptr_t bufferPosition = 0;
|
|
|
|
while (inputCharactersRemaining) {
|
|
uint32_t c = *inputPosition;
|
|
inputCharactersRemaining--;
|
|
inputPosition++;
|
|
|
|
if (c >= 0xD800 && c < 0xDC00 && inputCharactersRemaining) {
|
|
uint32_t c2 = *inputPosition;
|
|
|
|
if (c2 >= 0xDC00 && c2 < 0xE000) {
|
|
inputCharactersRemaining--;
|
|
inputPosition++;
|
|
|
|
c = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
|
|
}
|
|
}
|
|
|
|
size_t encodedBytes = utf8_encode(c, nullptr);
|
|
|
|
if (bufferPosition + encodedBytes < bufferBytes) {
|
|
utf8_encode(c, buffer + bufferPosition);
|
|
bufferPosition += encodedBytes;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
buffer[bufferPosition] = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool USBInterfaceClassCheck(KInstalledDriver *driver, KDevice *device) {
|
|
KUSBInterfaceDescriptor *descriptor = &((KUSBDevice *) device)->interfaceDescriptor;
|
|
|
|
int classCode = -1, subclassCode = -1, protocol = -1;
|
|
|
|
EsINIState s = {};
|
|
s.buffer = driver->config, s.bytes = driver->configBytes;
|
|
|
|
while (EsINIParse(&s)) {
|
|
if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("classCode"))) classCode = EsIntegerParse(s.value, s.valueBytes);
|
|
if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("subclassCode"))) subclassCode = EsIntegerParse(s.value, s.valueBytes);
|
|
if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("protocol"))) protocol = EsIntegerParse(s.value, s.valueBytes);
|
|
}
|
|
|
|
if (classCode == -1 && subclassCode == -1 && protocol == -1) {
|
|
return false;
|
|
}
|
|
|
|
if (classCode != -1 && descriptor->interfaceClass != classCode) return false;
|
|
if (subclassCode != -1 && descriptor->interfaceSubclass != subclassCode) return false;
|
|
if (protocol != -1 && descriptor->interfaceProtocol != protocol) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool USBProductIDCheck(KInstalledDriver *driver, KDevice *device) {
|
|
KUSBDeviceDescriptor *descriptor = &((KUSBDevice *) device)->deviceDescriptor;
|
|
|
|
int productID = -1, vendorID = -1, version = -1;
|
|
|
|
EsINIState s = {};
|
|
s.buffer = driver->config, s.bytes = driver->configBytes;
|
|
|
|
while (EsINIParse(&s)) {
|
|
if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("productID"))) productID = EsIntegerParse(s.value, s.valueBytes);
|
|
if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("vendorID"))) vendorID = EsIntegerParse(s.value, s.valueBytes);
|
|
if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("version"))) version = EsIntegerParse(s.value, s.valueBytes);
|
|
}
|
|
|
|
if (productID == -1 && vendorID == -1 && version == -1) {
|
|
return false;
|
|
}
|
|
|
|
if (productID != -1 && descriptor->productID != productID) return false;
|
|
if (vendorID != -1 && descriptor->vendorID != vendorID) return false;
|
|
if (version != -1 && descriptor->deviceVersion != version) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void KRegisterUSBDevice(KUSBDevice *device) {
|
|
EsDefer(KDeviceCloseHandle(device));
|
|
|
|
bool foundInterfaceDescriptor = false;
|
|
|
|
{
|
|
// Get the device descriptor.
|
|
|
|
uint16_t transferred;
|
|
|
|
if (!device->controlTransfer(device, SETUP_FLAG_D2H, 0x06 /* get descriptor */,
|
|
(DESCRIPTOR_DEVICE << 8) | 0x00, 0, &device->deviceDescriptor,
|
|
sizeof(KUSBDeviceDescriptor), K_ACCESS_READ, &transferred)
|
|
|| transferred != sizeof(KUSBDeviceDescriptor)) {
|
|
return;
|
|
}
|
|
|
|
if (device->deviceDescriptor.length < sizeof(KUSBDeviceDescriptor)
|
|
|| device->deviceDescriptor.descriptorType != DESCRIPTOR_DEVICE
|
|
|| device->deviceDescriptor.configurationCount == 0) {
|
|
KernelLog(LOG_ERROR, "USB", "invalid device descriptor", "Device descriptor is invalid or unsupported.\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
KernelLog(LOG_INFO, "USB", "device identification", "Device has identification %W/%W/%W.\n",
|
|
device->deviceDescriptor.vendorID, device->deviceDescriptor.productID, device->deviceDescriptor.deviceVersion);
|
|
|
|
{
|
|
// Get strings.
|
|
|
|
char buffer[256];
|
|
|
|
if (device->GetString(device->deviceDescriptor.manufacturerString, buffer, sizeof(buffer))) {
|
|
KernelLog(LOG_INFO, "USB", "device manufacturer", "Device manufacturer string: '%z'.\n", buffer);
|
|
} else {
|
|
goto skipStrings;
|
|
}
|
|
|
|
if (device->GetString(device->deviceDescriptor.productString, buffer, sizeof(buffer))) {
|
|
KernelLog(LOG_INFO, "USB", "device product", "Device product string: '%z'.\n", buffer);
|
|
} else {
|
|
goto skipStrings;
|
|
}
|
|
|
|
if (device->GetString(device->deviceDescriptor.serialNumberString, buffer, sizeof(buffer))) {
|
|
KernelLog(LOG_INFO, "USB", "device serial number", "Device serial number string: '%z'.\n", buffer);
|
|
} else {
|
|
goto skipStrings;
|
|
}
|
|
|
|
skipStrings:;
|
|
}
|
|
|
|
{
|
|
// Get the configuration descriptor.
|
|
|
|
uint16_t transferred;
|
|
|
|
if (!device->controlTransfer(device, SETUP_FLAG_D2H, 0x06 /* get descriptor */,
|
|
(DESCRIPTOR_CONFIGURATION << 8) | 0x00, 0, &device->configurationDescriptor,
|
|
sizeof(KUSBConfigurationDescriptor), K_ACCESS_READ, &transferred)
|
|
|| transferred != sizeof(KUSBConfigurationDescriptor)) {
|
|
return;
|
|
}
|
|
|
|
if (device->configurationDescriptor.totalLength < sizeof(KUSBConfigurationDescriptor)
|
|
|| device->configurationDescriptor.descriptorType != DESCRIPTOR_CONFIGURATION
|
|
|| !device->configurationDescriptor.interfaceCount
|
|
|| !device->configurationDescriptor.configurationIndex) {
|
|
KernelLog(LOG_ERROR, "USB", "invalid configuration descriptor", "Invalid field in configuration descriptor.\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
{
|
|
// Read the rest of the configuration descriptors.
|
|
|
|
uint8_t *buffer = (uint8_t *) EsHeapAllocate(device->configurationDescriptor.totalLength, false, K_FIXED);
|
|
|
|
if (!buffer) {
|
|
KernelLog(LOG_ERROR, "USB", "allocation failure", "Could not allocate buffer to read all configuration descriptors.\n");
|
|
return;
|
|
}
|
|
|
|
uint16_t transferred;
|
|
|
|
if (!device->controlTransfer(device, SETUP_FLAG_D2H, 0x06 /* get descriptor */,
|
|
(DESCRIPTOR_CONFIGURATION << 8) | 0x00, 0, buffer,
|
|
device->configurationDescriptor.totalLength, K_ACCESS_READ, &transferred)
|
|
|| transferred != device->configurationDescriptor.totalLength) {
|
|
return;
|
|
}
|
|
|
|
device->configurationDescriptors = buffer;
|
|
device->configurationDescriptorsBytes = device->configurationDescriptor.totalLength;
|
|
}
|
|
|
|
{
|
|
// Check the configuration descriptors are valid.
|
|
|
|
uintptr_t position = 0;
|
|
uintptr_t configurationsSeen = 0;
|
|
|
|
while (position < device->configurationDescriptorsBytes) {
|
|
if (position >= device->configurationDescriptorsBytes - sizeof(KUSBDescriptorHeader)) {
|
|
KernelLog(LOG_ERROR, "USB", "descriptor invalid length", "Remaining %D, too small for descriptor.\n",
|
|
device->configurationDescriptorsBytes - position);
|
|
return;
|
|
}
|
|
|
|
KUSBDescriptorHeader *header = (KUSBDescriptorHeader *) (device->configurationDescriptors + position);
|
|
|
|
if (header->length < sizeof(KUSBDescriptorHeader) || header->length > device->configurationDescriptorsBytes - position) {
|
|
KernelLog(LOG_ERROR, "USB", "descriptor invalid length", "Given length %D, remaining %D.\n",
|
|
header->length, device->configurationDescriptorsBytes - position);
|
|
return;
|
|
}
|
|
|
|
if (header->descriptorType == DESCRIPTOR_CONFIGURATION
|
|
&& header->length >= sizeof(KUSBConfigurationDescriptor)) {
|
|
configurationsSeen++;
|
|
}
|
|
|
|
if (header->descriptorType == DESCRIPTOR_INTERFACE
|
|
&& header->length >= sizeof(KUSBInterfaceDescriptor)
|
|
&& !foundInterfaceDescriptor
|
|
&& configurationsSeen == 1) {
|
|
device->interfaceDescriptor = *(KUSBInterfaceDescriptor *) header;
|
|
device->selectedConfigurationOffset = position + header->length;
|
|
foundInterfaceDescriptor = true;
|
|
}
|
|
|
|
position += header->length;
|
|
}
|
|
}
|
|
|
|
{
|
|
// Look for a driver that matches the vendor/product.
|
|
|
|
if (KDeviceAttach(device, "USB", USBProductIDCheck)) {
|
|
return;
|
|
}
|
|
|
|
// Otherwise, pick the default configuration and interface.
|
|
|
|
if (!foundInterfaceDescriptor) {
|
|
KernelLog(LOG_ERROR, "USB", "no interface descriptor",
|
|
"The device does not have any interface descriptors for the default configuration.");
|
|
}
|
|
|
|
KernelLog(LOG_INFO, "USB", "device interface", "Device has interface %d with identification %X/%X/%X.\n",
|
|
device->interfaceDescriptor.interfaceIndex, device->interfaceDescriptor.interfaceClass,
|
|
device->interfaceDescriptor.interfaceSubclass, device->interfaceDescriptor.interfaceProtocol);
|
|
|
|
if (!device->selectConfigurationAndInterface(device)) {
|
|
KernelLog(LOG_ERROR, "USB", "select interface failure", "Could not select configuration and interface for device.\n");
|
|
return;
|
|
}
|
|
|
|
if (KDeviceAttach(device, "USB", USBInterfaceClassCheck)) {
|
|
return;
|
|
}
|
|
|
|
KernelLog(LOG_ERROR, "USB", "no driver", "No driver could be found for the device.\n");
|
|
// TODO Show an error message to the user.
|
|
}
|
|
}
|
|
|
|
KDriver driverUSB;
|