Newer
Older
honey-os / src / userland / usb / main.c
#define ALLOC_MAIN
#include <hlib.h>

#include "../hlib/include/syscalls.h"
#include "xhci/trbRing.h"
#include <usb.h>

uint32_t serviceId;

XHCIController *controller;

CommandCompletionEvent *xhciCommand(XHCIController *controller,
                                    uint32_t dataLow, uint32_t dataHigh,
                                    uint32_t status, uint32_t control) {
    XHCITRB trb = {0};
    trb.dataLow = dataLow;
    trb.dataHigh = dataHigh;
    trb.status = status;
    trb.control = control;
    uint32_t commandAddress = U32(enqueueCommand(&controller->commands, &trb));
    controller->doorbells[0] = 0;
    uint32_t eventId = syscall(SYS_CREATE_EVENT, commandAddress, 0, 0, 0);
    return PTR(await(serviceId, eventId));
}

#define REQUEST(functionName, service, function)                               \
    uint32_t functionName(uint32_t data1, uint32_t data2) {                    \
        static uint32_t serviceId, functionId, initialized = false;            \
        if (!initialized) {                                                    \
            while (!serviceId) {                                               \
                serviceId = getService(service);                               \
                serviceId = getService(service);                               \
            }                                                                  \
            while (!functionId) {                                              \
                functionId = getFunction(serviceId, function);                 \
                functionId = getFunction(serviceId, function);                 \
            }                                                                  \
            initialized = true;                                                \
        }                                                                      \
        return request(serviceId, functionId, data1, data2);                   \
    }

REQUEST(getBaseAddress, "lspci", "getBaseAddress");
REQUEST(getDeviceClass, "lspci", "getDeviceClass");
REQUEST(enableBusMaster, "lspci", "enableBusMaster");
REQUEST(getPCIInterrupt, "lspci", "getInterrupt");

void restartXHCIController(XHCIController *controller) {
    printf("resetting controller...\n");
    controller->operational->command &= ~1;
    while (!(controller->operational->status & (1 << 0)))
        syscall(-1, 0, 0, 0, 0);
    controller->operational->command |= 1 << 1;
    while ((controller->operational->command & (1 << 1)))
        ;
    while ((controller->operational->status & (1 << 11)))
        ;
}

void setupRuntime(XHCIController *controller) {
    controller->runtime = OFFSET(controller->capabilities,
                                 controller->capabilities->runtimeOffset);
    controller->runtime->interrupters[0].eventRingSegmentTableSize = 1;
    controller->runtime->interrupters[0].eventRingSegmentTableAddress[0] =
        U32(controller->eventRingSegmentTablePhysical);
    controller->runtime->interrupters[0].eventRingSegmentTableAddress[1] = 0;
    controller->runtime->interrupters[0].eventRingDequeuePointer[0] =
        U32(&controller->events.physical[controller->events.dequeue]) |
        (1 << 3);
    controller->runtime->interrupters[0].eventRingDequeuePointer[1] = 0;

    controller->runtime->interrupters[0].management |= 3;
    sleep(100);
}

void setupEventRingSegmentTable(XHCIController *controller) {
    // todo: research necessary alignment
    controller->eventRingSegmentTable = requestMemory(1, 0, 0);
    controller->eventRingSegmentTablePhysical =
        getPhysicalAddress((void *)controller->eventRingSegmentTable);

    controller->eventRingSegmentTable[0].ringSegmentBaseAddress[0] =
        U32(controller->events.physical);
    controller->eventRingSegmentTable[0].ringSegmentSize =
        controller->events.size;
}

void readExtendedCapabilities(XHCIController *controller) {
    uintptr_t offset = (controller->capabilities->structuralParameters[0] >> 16)
                       << 2;

    volatile uint32_t *extendedCapabilities =
        OFFSET(controller->capabilities, offset);

    while (1) {
        uint32_t value = *extendedCapabilities;

        if ((value & 0xFF) == 2) {
            uint8_t revisionMinor = extendedCapabilities[0] >> 16;
            uint8_t revisionMajor = extendedCapabilities[0] >> 24;

            uint8_t portOffset = extendedCapabilities[2];
            uint8_t portCount = extendedCapabilities[2] >> 8;

            printf("protocol %i.%i %i port(s) starting from port %i\n",
                   revisionMajor, revisionMinor, portCount, portOffset);
        }

        if (value == 0xFFFFFFFF)
            break;
        if ((value & 0xFF00) == 0)
            break;
        extendedCapabilities =
            (void *)((uintptr_t)extendedCapabilities + ((value & 0xFF00) >> 6));
    }
}

void setupOperationalRegisters(XHCIController *controller) {
    controller->deviceContextBaseAddressArray = (void *)requestMemory(1, 0, 0);
    controller->operational->deviceContextBaseAddressArray[0] =
        U32(getPhysicalAddress(controller->deviceContextBaseAddressArray));
    controller->operational->deviceContextBaseAddressArray[1] = 0;

    controller->operational->config =
        (controller->operational->config & ~0xFF) | 32;

    controller->operational->commandRingControl[0] =
        U32(controller->commands.physical) | 1;
    controller->operational->commandRingControl[1] = 0;
    controller->operational->config =
        controller->capabilities->structuralParameters[0] & 0xFF;
}

void setupScratchpadBuffers(XHCIController *controller) {
    uint32_t hcs2 = controller->capabilities->structuralParameters[1];
    uint32_t scratchpadBufferCountHigh = (hcs2 >> 21) & 0x1f;
    uint32_t scratchpadBufferCountLow = (hcs2 >> 27) & 0x1f;
    uint32_t scratchpadBufferCount =
        (scratchpadBufferCountHigh << 5) | scratchpadBufferCountLow;

    if (scratchpadBufferCount) {
        printf("%i scratchpad buffers present\n", scratchpadBufferCount);

        uint64_t *scratchpadBuffer = (uint64_t *)requestMemory(1, 0, 0);
        void *scratchpadPhysical = getPhysicalAddress(scratchpadBuffer);
        for (unsigned int i = 0; i < scratchpadBufferCount; ++i) {
            void *sb_phys = getPhysicalAddress(requestMemory(1, 0, 0));
            scratchpadBuffer[i] = U32(sb_phys);
        }
        controller->deviceContextBaseAddressArray[0] = U32(scratchpadPhysical);
    }
}

XHCIController *initializeController(uint32_t deviceId) {
    XHCIController *controller = malloc(sizeof(XHCIController));
    controller->pciDevice = deviceId;

    controller->capabilities =
        requestMemory(4, 0, PTR(U32(getBaseAddress(deviceId, 0)) & ~0xF));
    controller->operational =
        OFFSET(controller->capabilities,
               controller->capabilities->capabilitiesLength & 0xFF);
    controller->doorbells = OFFSET(controller->capabilities,
                                   controller->capabilities->doorbellOffset);

    uint32_t slotInfo = controller->capabilities->structuralParameters[0];
    printf("%i available slots, %i available ports\n", slotInfo & 0xFF,
           slotInfo >> 24);
    controller->portCount = slotInfo >> 24;
    return controller;
}

void addressDevice(void *inputContext, uint32_t slotNumber, bool BSR) {
    uint32_t control =
        COMMAND_TYPE(11) | COMMAND_SLOT_ID(slotNumber) | COMMAND_BSR(BSR);
    xhciCommand(controller, U32(inputContext), 0, 0, control);
}

void configureEndpoint(void *inputContext, uint32_t slotNumber,
                       bool deconfigure) {
    uint32_t control = COMMAND_TYPE(12) | COMMAND_SLOT_ID(slotNumber) |
                       COMMAND_BSR(deconfigure);
    xhciCommand(controller, U32(inputContext), 0, 0, control);
}

void evaluateContext(void *inputContext, uint32_t slotNumber) {
    uint32_t control = COMMAND_TYPE(13) | COMMAND_SLOT_ID(slotNumber);
    xhciCommand(controller, U32(inputContext), 0, 0, control);
}

void *usbGetDeviceDescriptor(XHCIInputContext *inputContext, TrbRing *ring,
                             uint32_t slotIndex, uint32_t value, uint32_t index,
                             void *buffer) {
    XHCISetupStageTRB setup = {0};
    setup.requestType = 0x80;
    setup.request = 6;
    setup.value = value;
    setup.index = index;
    setup.length = 4096;
    setup.transferLength = 8;
    setup.interruptOnCompletion = 0;
    setup.interrupterTarget = 0;
    setup.type = 2;
    setup.transferType = 3;
    setup.immediateData = 1;

    XHCIDataStageTRB data = {0};
    data.dataBuffer[0] = U32(getPhysicalAddress(buffer));
    data.inDirection = 1;
    data.transferSize = 4096;
    data.type = 3;
    data.interrupterTarget = 0;

    XHCIStatusStageTRB status = {0};
    status.inDirection = 1;
    status.evaluateNext = 0;
    status.interruptOnCompletion = 1;
    status.type = 4;

    enqueueCommand(ring, (void *)&setup);
    enqueueCommand(ring, (void *)&data);
    uint32_t commandAddress = U32(enqueueCommand(ring, (void *)&status));
    uint32_t eventId = syscall(SYS_CREATE_EVENT, commandAddress, 0, 0, 0);
    controller->doorbells[slotIndex] = 1;
    CommandCompletionEvent *completionEvent = PTR(await(serviceId, eventId));
    return buffer;
}

XHCIInputContext *createInputContext(XHCIController *controller, XHCIPort *port,
                                     uint32_t portIndex, uint32_t slotIndex) {
    XHCIInputContext *inputContext = requestMemory(1, 0, 0);
    inputContext->inputControl.addContextFlags = 3;
    inputContext->deviceContext.slot.rootHubPort = portIndex;
    inputContext->deviceContext.slot.interrupterTarget = 0;
    inputContext->deviceContext.slot.multiTT = 0;
    inputContext->deviceContext.slot.deviceAddress = 0;
    inputContext->deviceContext.slot.contextEntryCount = 1;
    inputContext->deviceContext.slot.speed = (port->status >> 10) & 3;
    inputContext->deviceContext.endpoints[0].endpointType = 4;
    inputContext->deviceContext.endpoints[0].maxPacketSize = 8; // TODO
    inputContext->deviceContext.endpoints[0].maxBurstSize = 0;
    inputContext->deviceContext.endpoints[0].interval = 0;
    inputContext->deviceContext.endpoints[0].maxPrimaryStreams = 0;
    inputContext->deviceContext.endpoints[0].multiplier = 0;
    inputContext->deviceContext.endpoints[0].errorCount = 3;
    inputContext->deviceContext.endpoints[0].averageTRBLength = 8;
    return inputContext;
}

TrbRing *createTRB(XHCIController *controller, XHCIInputContext *inputContext,
                   uint32_t slotIndex) {
    TrbRing *ring = malloc(sizeof(TrbRing));
    setupTrbRing(ring, 256);
    inputContext->deviceContext.endpoints[0].transferDequeuePointerLow =
        U32(ring->physical) | 1;
    inputContext->deviceContext.endpoints[0].transferDequeuePointerHigh = 0;
    XHCIDevice *device = malloc(sizeof(XHCIDevice));
    controller->deviceContextBaseAddressArray[slotIndex] =
        U32(getPhysicalAddress((void *)device));
    return ring;
}

char *usbReadString(XHCIInputContext *inputContext, TrbRing *ring,
                    uint32_t slotIndex, uint32_t language,
                    uint32_t stringDescriptor, void *buffer) {
    usbGetDeviceDescriptor(inputContext, ring, slotIndex,
                           3 << 8 | stringDescriptor, language, buffer);
    uint32_t length = ((*(uint8_t *)buffer) - 2) / 2;
    char *string = malloc(length);
    for (uint32_t i = 0; i < length; i++) {
        string[i] = ((char *)buffer)[(i + 1) * 2];
    }
    return string;
}

void resetPort(XHCIController *controller, uint32_t portIndex) {
    XHCIPort *port = &controller->operational->ports[portIndex - 1];
    printf("port %i: connected, resetting and setting it up now ...\n",
           portIndex - 1);
    port->status |= 1 << 4;
    await(serviceId, syscall(SYS_CREATE_EVENT, portIndex << 24, 0, 0, 0));
    if (!(port->status & 1 << 1)) {
        return printf("port %i reset could not be done, aborting\n",
                      portIndex - 1);
    }
    uint32_t slotIndex =
        xhciCommand(controller, 0, 0, 0, COMMAND_TYPE(9))->slotId;
    printf("port %i: reset done, now connecting to slot %i\n", portIndex - 1,
           slotIndex);
    XHCIInputContext *inputContext =
        createInputContext(controller, port, portIndex, slotIndex);
    TrbRing *ring = createTRB(controller, inputContext, slotIndex);
    printf("port %i: addressing slot\n", portIndex - 1);
    addressDevice(getPhysicalAddress((void *)&inputContext->inputControl),
                  slotIndex, true);
    void *buffer = requestMemory(1, 0, 0);
    UsbDeviceDescriptor *data = malloc(sizeof(UsbDeviceDescriptor));
    usbGetDeviceDescriptor(inputContext, ring, slotIndex, 1 << 8, 0, buffer);
    memcpy(buffer, (void *)data, sizeof(UsbDeviceDescriptor));
    printf("port %i: type: %i, version %x\n", portIndex - 1, data->size,
           data->descriptorType, data->usbVersion);
    printf("port %i: class: %i, subclass: %i, protocol %i, maxPacketSize: %i\n",
           portIndex - 1, data->deviceClass, data->deviceSubclass,
           data->deviceProtocol, data->maxPacketSize);
    inputContext->deviceContext.endpoints[0].maxPacketSize =
        data->maxPacketSize == 9 ? 512 : data->maxPacketSize;
    usbGetDeviceDescriptor(inputContext, ring, slotIndex, 3 << 8, 0, buffer);
    uint32_t language = *((uint16_t *)(buffer + 2));
    char *manufacturer =
        usbReadString(inputContext, ring, slotIndex, language,
                      data->manufacturerStringDescriptor, buffer);
    char *device = usbReadString(inputContext, ring, slotIndex, language,
                                 data->deviceStringDescriptor, buffer);
    char *serial = usbReadString(inputContext, ring, slotIndex, language,
                                 data->serialNumberStringDescriptor, buffer);
    printf("manufacturer: %s, device: %s, serial: %s\n", manufacturer, device,
           serial);
    printf("--------\n");
}

void xhciInterrupt() {
    if (controller->runtime->interrupters[0].management & 1) {
        controller->runtime->interrupters[0].management |= 1;
        XHCITRB *trb;
        uint32_t index;
        while ((trb = trbRingFetch(&controller->events, &index))) {
            controller->runtime->interrupters[0].eventRingDequeuePointer[0] =
                U32(&controller->events.physical[controller->events.dequeue]) |
                (1 << 3);
            uint32_t eventId =
                syscall(SYS_GET_EVENT, serviceId, trb->dataLow, 0, 0);
            printf("event %i [%x %x %x %x]: %i\n", controller->events.dequeue,
                   trb->dataLow, trb->dataHigh, trb->status, trb->control,
                   trb->control >> 10 & 0x3F);
            if (eventId) {
                fireEvent(eventId, U32(trb));
            }
        }
    }
}

void initializeUSB(uint32_t deviceId) {
    controller = initializeController(deviceId);
    restartXHCIController(controller);

    setupTrbRing(&controller->commands, 256);
    setupTrbRing(&controller->events, 256);
    readExtendedCapabilities(controller);
    setupOperationalRegisters(controller);
    setupEventRingSegmentTable(controller);
    setupRuntime(controller);
    setupScratchpadBuffers(controller);

    controller->operational->status |= (1 << 3);
    printf("using irq no. %i\n", getPCIInterrupt(deviceId, 0));
    int pic = getService("pic");
    subscribeEvent(pic, getEvent(pic, "irq11"), xhciInterrupt);
    controller->operational->command |= (1 << 0) | (1 << 2);
    sleep(100);

    if (controller->operational->status & (1 << 2)) {
        return printf("critical XHCI problem\n");
    }

    for (uint32_t i = 0; i < controller->portCount; i++) {
        if (!(controller->operational->ports[i].status & 1 << 0)) {
            continue;
        }
        resetPort(controller, i + 1);
        sleep(5000);
    }
    return;
}

int32_t main() {
    serviceId = getServiceId();
    // this is needed so we can decide wether or not sys_get_event
    // returns no event or an event TODO: start event indexing
    // with index 1 or add a sensible default event
    createEvent("unused");
    uint32_t i = 0, class = 0;
    while ((class = getDeviceClass(i, 0))) {
        if (class == 0x0C0330) {
            printf("found XHCI host controller at pci device no. %i\n", i);
            enableBusMaster(i, 0);
            initializeUSB(i);
        }
        i++;
    }
}