362 lines
14 KiB
C
362 lines
14 KiB
C
// usb_core.c - USB descriptor/configuration parsing, interface publishing, and root-port enumeration.
|
|
#include "usb.h"
|
|
|
|
KSTATUS XhciSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
|
uint8_t configurationValue) {
|
|
USB_SETUP_PACKET setup;
|
|
setup.bmRequestType = USB_DIR_OUT;
|
|
setup.bRequest = USB_REQ_SET_CONFIGURATION;
|
|
setup.wValue = configurationValue;
|
|
setup.wIndex = 0;
|
|
setup.wLength = 0;
|
|
|
|
KSTATUS st = XhciControlNoData(xhci, dev, &setup);
|
|
if (!KSUCCESS(st)) {
|
|
DbgPrint("usb: SET_CONFIGURATION slot=%u value=%u failed st=0x%x\n",
|
|
dev->SlotId, configurationValue, st);
|
|
return st;
|
|
}
|
|
|
|
dev->ConfigurationValue = configurationValue;
|
|
dev->Configured = true;
|
|
UsbTrace("usb: SET_CONFIGURATION slot=%u value=%u complete\n",
|
|
dev->SlotId, configurationValue);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
void XhciLogConfigurationTree(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
|
const USB_CONFIGURATION_DESCRIPTOR* config,
|
|
uint16_t totalLength) {
|
|
(void)xhci;
|
|
|
|
memset(dev->Interfaces, 0, sizeof(dev->Interfaces));
|
|
dev->InterfaceCount = 0;
|
|
|
|
UsbTrace("usb: config slot=%u value=%u total=%u interfaces=%u attrs=%02x maxpower=%umA\n",
|
|
dev->SlotId,
|
|
config->bConfigurationValue,
|
|
totalLength,
|
|
config->bNumInterfaces,
|
|
config->bmAttributes,
|
|
(uint32_t)config->bMaxPower * 2u);
|
|
|
|
uint16_t off = config->bLength;
|
|
uint8_t currentInterface = 0xFFu;
|
|
uint8_t currentClass = 0;
|
|
uint8_t currentSubClass = 0;
|
|
uint8_t currentProtocol = 0;
|
|
bool hidCandidate = false;
|
|
bool massCandidate = false;
|
|
uint8_t hidInterruptIn = 0;
|
|
uint8_t massBulkIn = 0;
|
|
uint8_t massBulkOut = 0;
|
|
USB_TRACKED_INTERFACE* tracked = (USB_TRACKED_INTERFACE*)0;
|
|
|
|
while (off + 2u <= totalLength) {
|
|
const uint8_t* desc = dev->DescriptorBuffer + off;
|
|
uint8_t len = desc[0];
|
|
uint8_t type = desc[1];
|
|
|
|
if (len < 2 || off + len > totalLength) {
|
|
DbgPrint("usb: descriptor parse stopped slot=%u off=%u len=%u type=%u total=%u\n",
|
|
dev->SlotId, off, len, type, totalLength);
|
|
break;
|
|
}
|
|
|
|
if (type == USB_DESC_INTERFACE && len >= sizeof(USB_INTERFACE_DESCRIPTOR)) {
|
|
const USB_INTERFACE_DESCRIPTOR* iface = (const USB_INTERFACE_DESCRIPTOR*)desc;
|
|
currentInterface = iface->bInterfaceNumber;
|
|
currentClass = iface->bInterfaceClass;
|
|
currentSubClass = iface->bInterfaceSubClass;
|
|
currentProtocol = iface->bInterfaceProtocol;
|
|
hidCandidate = (currentClass == USB_CLASS_HID);
|
|
massCandidate = (currentClass == USB_CLASS_MASS_STORAGE &&
|
|
currentSubClass == USB_MASS_SUBCLASS_SCSI &&
|
|
currentProtocol == USB_MASS_PROTOCOL_BOT);
|
|
hidInterruptIn = 0;
|
|
massBulkIn = 0;
|
|
massBulkOut = 0;
|
|
tracked = (USB_TRACKED_INTERFACE*)0;
|
|
|
|
if (dev->InterfaceCount < USB_MAX_TRACKED_INTERFACES) {
|
|
tracked = &dev->Interfaces[dev->InterfaceCount++];
|
|
memset(tracked, 0, sizeof(*tracked));
|
|
tracked->Present = true;
|
|
memcpy(&tracked->Descriptor, iface, sizeof(*iface));
|
|
} else {
|
|
DbgPrint("usb: slot=%u too many interfaces, interface %u not published\n",
|
|
dev->SlotId, iface->bInterfaceNumber);
|
|
}
|
|
|
|
UsbTrace("usb: interface slot=%u if=%u alt=%u eps=%u class=%02x subclass=%02x proto=%02x (%s)\n",
|
|
dev->SlotId,
|
|
iface->bInterfaceNumber,
|
|
iface->bAlternateSetting,
|
|
iface->bNumEndpoints,
|
|
currentClass,
|
|
currentSubClass,
|
|
currentProtocol,
|
|
UsbInterfaceClassName(currentClass, currentSubClass, currentProtocol));
|
|
} else if (type == USB_DESC_ENDPOINT && len >= sizeof(USB_ENDPOINT_DESCRIPTOR)) {
|
|
const USB_ENDPOINT_DESCRIPTOR* ep = (const USB_ENDPOINT_DESCRIPTOR*)desc;
|
|
uint8_t epType = ep->bmAttributes & USB_ENDPOINT_ATTR_TYPE_MASK;
|
|
uint8_t epNumber = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
|
bool in = (ep->bEndpointAddress & USB_ENDPOINT_DIR_IN) != 0;
|
|
|
|
if (tracked && tracked->EndpointCount < USB_MAX_TRACKED_ENDPOINTS) {
|
|
memcpy(&tracked->Endpoints[tracked->EndpointCount++], ep, sizeof(*ep));
|
|
} else if (tracked) {
|
|
DbgPrint("usb: slot=%u if=%u too many endpoints, endpoint %02x not published\n",
|
|
dev->SlotId, currentInterface, ep->bEndpointAddress);
|
|
}
|
|
|
|
UsbTrace("usb: endpoint slot=%u if=%u addr=%02x ep=%u dir=%s type=%s maxpkt=%u interval=%u\n",
|
|
dev->SlotId,
|
|
currentInterface,
|
|
ep->bEndpointAddress,
|
|
epNumber,
|
|
UsbDirectionName(ep->bEndpointAddress),
|
|
UsbEndpointTypeName(epType),
|
|
ep->wMaxPacketSize,
|
|
ep->bInterval);
|
|
|
|
if (hidCandidate && in && epType == USB_ENDPOINT_TYPE_INTERRUPT && hidInterruptIn == 0) {
|
|
hidInterruptIn = ep->bEndpointAddress;
|
|
}
|
|
if (massCandidate && epType == USB_ENDPOINT_TYPE_BULK) {
|
|
if (in && massBulkIn == 0) massBulkIn = ep->bEndpointAddress;
|
|
if (!in && massBulkOut == 0) massBulkOut = ep->bEndpointAddress;
|
|
}
|
|
} else if (type == USB_DESC_HID) {
|
|
UsbTrace("usb: hid descriptor slot=%u if=%u len=%u\n",
|
|
dev->SlotId, currentInterface, len);
|
|
} else if (type == USB_DESC_CS_INTERFACE) {
|
|
UsbTrace("usb: class-specific interface descriptor slot=%u if=%u len=%u subtype=%u\n",
|
|
dev->SlotId, currentInterface, len, len >= 3 ? desc[2] : 0);
|
|
}
|
|
|
|
uint16_t next = (uint16_t)(off + len);
|
|
if (hidCandidate && hidInterruptIn &&
|
|
(next >= totalLength || (next + 1u < totalLength && dev->DescriptorBuffer[next + 1u] == USB_DESC_INTERFACE))) {
|
|
UsbTrace("usb: class candidate slot=%u if=%u hid interrupt-in=%02x subclass=%02x proto=%02x\n",
|
|
dev->SlotId,
|
|
currentInterface,
|
|
hidInterruptIn,
|
|
currentSubClass,
|
|
currentProtocol);
|
|
hidCandidate = false;
|
|
}
|
|
if (massCandidate && massBulkIn && massBulkOut &&
|
|
(next >= totalLength || (next + 1u < totalLength && dev->DescriptorBuffer[next + 1u] == USB_DESC_INTERFACE))) {
|
|
UsbTrace("usb: class candidate slot=%u if=%u mass-storage bulk-in=%02x bulk-out=%02x\n",
|
|
dev->SlotId,
|
|
currentInterface,
|
|
massBulkIn,
|
|
massBulkOut);
|
|
massCandidate = false;
|
|
}
|
|
|
|
off = next;
|
|
}
|
|
|
|
if (hidCandidate && hidInterruptIn) {
|
|
UsbTrace("usb: class candidate slot=%u if=%u hid interrupt-in=%02x subclass=%02x proto=%02x\n",
|
|
dev->SlotId,
|
|
currentInterface,
|
|
hidInterruptIn,
|
|
currentSubClass,
|
|
currentProtocol);
|
|
}
|
|
if (massCandidate && massBulkIn && massBulkOut) {
|
|
UsbTrace("usb: class candidate slot=%u if=%u mass-storage bulk-in=%02x bulk-out=%02x\n",
|
|
dev->SlotId,
|
|
currentInterface,
|
|
massBulkIn,
|
|
massBulkOut);
|
|
}
|
|
}
|
|
|
|
|
|
void XhciPublishUsbInterfaces(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
|
for (uint8_t i = 0; i < dev->InterfaceCount; i++) {
|
|
USB_TRACKED_INTERFACE* iface = &dev->Interfaces[i];
|
|
if (!iface->Present)
|
|
continue;
|
|
|
|
IO_PUBLISHED_DEVICE pub;
|
|
memset(&pub, 0, sizeof(pub));
|
|
pub.BusType = IoBusUsb;
|
|
pub.Name = "usb-interface";
|
|
pub.BusContext = xhci;
|
|
pub.DeviceContext = dev;
|
|
|
|
pub.u.Usb.VendorId = dev->VendorId;
|
|
pub.u.Usb.ProductId = dev->ProductId;
|
|
pub.u.Usb.BcdUsb = dev->BcdUsb;
|
|
pub.u.Usb.BcdDevice = dev->BcdDevice;
|
|
pub.u.Usb.DeviceClass = dev->DeviceClass;
|
|
pub.u.Usb.DeviceSubclass = dev->DeviceSubclass;
|
|
pub.u.Usb.DeviceProtocol = dev->DeviceProtocol;
|
|
pub.u.Usb.SlotId = dev->SlotId;
|
|
pub.u.Usb.Port = dev->Port;
|
|
pub.u.Usb.Speed = dev->Speed;
|
|
pub.u.Usb.ConfigurationValue = dev->ConfigurationValue;
|
|
pub.u.Usb.InterfaceNumber = iface->Descriptor.bInterfaceNumber;
|
|
pub.u.Usb.AlternateSetting = iface->Descriptor.bAlternateSetting;
|
|
pub.u.Usb.InterfaceClass = iface->Descriptor.bInterfaceClass;
|
|
pub.u.Usb.InterfaceSubclass = iface->Descriptor.bInterfaceSubClass;
|
|
pub.u.Usb.InterfaceProtocol = iface->Descriptor.bInterfaceProtocol;
|
|
pub.u.Usb.EndpointCount = iface->EndpointCount;
|
|
if (pub.u.Usb.EndpointCount > IO_PUBLISHED_USB_MAX_ENDPOINTS)
|
|
pub.u.Usb.EndpointCount = IO_PUBLISHED_USB_MAX_ENDPOINTS;
|
|
|
|
for (uint8_t ep = 0; ep < pub.u.Usb.EndpointCount; ep++) {
|
|
pub.u.Usb.Endpoints[ep].Address = iface->Endpoints[ep].bEndpointAddress;
|
|
pub.u.Usb.Endpoints[ep].Attributes = iface->Endpoints[ep].bmAttributes;
|
|
pub.u.Usb.Endpoints[ep].MaxPacketSize = iface->Endpoints[ep].wMaxPacketSize;
|
|
pub.u.Usb.Endpoints[ep].Interval = iface->Endpoints[ep].bInterval;
|
|
}
|
|
|
|
KSTATUS st = IoPublishDevice(&pub);
|
|
if (!KSUCCESS(st)) {
|
|
DbgPrint("usb: publish interface slot=%u if=%u failed st=0x%x\n",
|
|
dev->SlotId, iface->Descriptor.bInterfaceNumber, st);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
KSTATUS XhciDiscoverAndSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
|
const USB_DEVICE_DESCRIPTOR* devDesc) {
|
|
if (devDesc->bNumConfigurations == 0) {
|
|
DbgPrint("usb: slot=%u has no configurations\n", dev->SlotId);
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
USB_CONFIGURATION_DESCRIPTOR config;
|
|
memset(&config, 0, sizeof(config));
|
|
uint16_t totalLength = 0;
|
|
KSTATUS st = XhciGetConfigurationDescriptor(xhci, dev, 0, &config, &totalLength);
|
|
if (!KSUCCESS(st)) {
|
|
DbgPrint("usb: slot=%u configuration descriptor read failed st=0x%x\n",
|
|
dev->SlotId, st);
|
|
return st;
|
|
}
|
|
|
|
XhciLogConfigurationTree(xhci, dev, &config, totalLength);
|
|
|
|
if (config.bConfigurationValue == 0) {
|
|
DbgPrint("usb: slot=%u configuration value 0 is invalid for SET_CONFIGURATION\n",
|
|
dev->SlotId);
|
|
return STATUS_DEVICE_ERROR;
|
|
}
|
|
|
|
st = XhciSetConfiguration(xhci, dev, config.bConfigurationValue);
|
|
if (!KSUCCESS(st)) return st;
|
|
|
|
XhciPublishUsbInterfaces(xhci, dev);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
KSTATUS XhciEnumeratePort(XHCI_CONTROLLER* xhci, uint8_t port) {
|
|
uint8_t speed = 0;
|
|
KSTATUS st = XhciResetPort(xhci, port, &speed);
|
|
if (!KSUCCESS(st)) return st;
|
|
|
|
uint8_t slotId = 0;
|
|
st = XhciEnableSlot(xhci, &slotId);
|
|
if (!KSUCCESS(st)) return st;
|
|
|
|
XHCI_DEVICE* dev = &xhci->Devices[slotId];
|
|
memset(dev, 0, sizeof(*dev));
|
|
dev->Allocated = true;
|
|
dev->SlotId = slotId;
|
|
dev->Port = port;
|
|
dev->Speed = speed;
|
|
|
|
st = XhciAllocateDeviceMemory(xhci, dev);
|
|
if (!KSUCCESS(st)) return st;
|
|
|
|
XhciBuildDefaultControlContext(xhci, dev);
|
|
|
|
st = XhciAddressDevice(xhci, dev);
|
|
if (!KSUCCESS(st)) return st;
|
|
dev->Addressed = true;
|
|
|
|
USB_DEVICE_DESCRIPTOR desc;
|
|
memset(&desc, 0, sizeof(desc));
|
|
st = XhciGetDeviceDescriptor(xhci, dev, &desc);
|
|
if (!KSUCCESS(st)) {
|
|
DbgPrint("usb: port %u slot %u descriptor read failed st=0x%x\n",
|
|
port, slotId, st);
|
|
return st;
|
|
}
|
|
|
|
dev->MaxPacketSize0 = desc.bMaxPacketSize0;
|
|
dev->VendorId = desc.idVendor;
|
|
dev->ProductId = desc.idProduct;
|
|
dev->BcdUsb = desc.bcdUSB;
|
|
dev->BcdDevice = desc.bcdDevice;
|
|
dev->DeviceClass = desc.bDeviceClass;
|
|
dev->DeviceSubclass = desc.bDeviceSubClass;
|
|
dev->DeviceProtocol = desc.bDeviceProtocol;
|
|
|
|
DbgPrint("usb: device port=%u slot=%u speed=%s(%u) vid=%04x pid=%04x class=%02x subclass=%02x proto=%02x mps0=%u configs=%u usb=%04x\n",
|
|
port,
|
|
slotId,
|
|
XhciPortSpeedName(speed),
|
|
speed,
|
|
desc.idVendor,
|
|
desc.idProduct,
|
|
desc.bDeviceClass,
|
|
desc.bDeviceSubClass,
|
|
desc.bDeviceProtocol,
|
|
desc.bMaxPacketSize0,
|
|
desc.bNumConfigurations,
|
|
desc.bcdUSB);
|
|
|
|
st = XhciDiscoverAndSetConfiguration(xhci, dev, &desc);
|
|
if (!KSUCCESS(st)) return st;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
void XhciEnumerateRootPorts(XHCI_CONTROLLER* xhci) {
|
|
uint32_t attempted = 0;
|
|
uint32_t configured = 0;
|
|
|
|
for (uint8_t port = 1; port <= xhci->MaxPorts; port++) {
|
|
uint32_t portsc = XhciRead32(xhci->Op, XhciPortReg(port));
|
|
if (!(portsc & XHCI_PORTSC_CCS))
|
|
continue;
|
|
|
|
attempted++;
|
|
KSTATUS st = XhciEnumeratePort(xhci, port);
|
|
if (KSUCCESS(st)) configured++;
|
|
else DbgPrint("usb: root-port %u enumeration failed st=0x%x\n", port, st);
|
|
}
|
|
|
|
DbgPrint("usb: xHCI root-port enumeration complete; attempted=%u configured=%u\n",
|
|
attempted, configured);
|
|
}
|
|
|
|
|
|
KSTATUS UsbRegisterClassMatchers(void) {
|
|
memset(&gUsbStorageDriver, 0, sizeof(gUsbStorageDriver));
|
|
gUsbStorageDriver.Name = "usbstor";
|
|
gUsbStorageDriver.MajorFunction[IRP_MJ_READ] = UsbStorageDispatchReadWrite;
|
|
gUsbStorageDriver.MajorFunction[IRP_MJ_WRITE] = UsbStorageDispatchReadWrite;
|
|
|
|
KSTATUS st = IoRegisterDriverMatcher("usbhid", IoBusUsb, UsbHidMatch, UsbHidBind, NULL);
|
|
if (!KSUCCESS(st)) return st;
|
|
|
|
st = IoRegisterDriverMatcher("usbstor", IoBusUsb, UsbStorageMatch, UsbStorageBind, NULL);
|
|
if (!KSUCCESS(st)) return st;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|