Files
nt-lite/code/drivers/bus/usb/usb_core.c
T

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;
}