Page 1 of 1

UHCI not process the queue

Posted: Mon May 28, 2018 2:37 am
by MrLolthe1st
Hi!
So, im have UHCI initialisation code:

Code: Select all

void _uhci_init(unsigned int id, PciDeviceInfo *info)
{
	if (!(((info->classCode << 8) | info->subclass) == PCI_SERIAL_USB &&
        info->progIntf == PCI_SERIAL_USB_UHCI))
    {
        return;
    }
	printTextToWindow(4,mywin,"UHCI Initializing...\n");
	PciBar bar;
    PciGetBar(&bar, id, 4);
	if (~bar.flags & PCI_BAR_IO)
    {
        // Only Port I/O supported
        return;
    }
	unsigned int ioAddr = bar.u.port;
	printTextToWindow(4,mywin,"I/O Address: %x\n",ioAddr);
	UhciController *hc = VMAlloc(sizeof(UhciController));
    hc->ioAddr = ioAddr;
    hc->frameList = VMAlloc(1024 * sizeof(u32) + 8192);
	hc->frameList = ((int)hc->frameList/4096)*4096 + 4096;
    hc->qhPool = (UhciQH *)VMAlloc(sizeof(UhciQH) * MAX_QH + 8192);
	hc->qhPool =((int)hc->qhPool/4096)*4096 + 4096;
    hc->tdPool = (UhciTD *)VMAlloc(sizeof(UhciTD) * MAX_TD + 8192);
	hc->tdPool =((int)hc->tdPool/4096)*4096 + 4096;

    memset(hc->qhPool, 0, sizeof(UhciQH) * MAX_QH);
    memset(hc->tdPool, 0, sizeof(UhciTD) * MAX_TD);
	memset(hc->frameList, 0, 4*1024);
//	Wait(1000);
    // Frame list setup
    UhciQH *qh = UhciAllocQH(hc);
	//return;
	//UhciQH *qh = 0;
    qh->head = TD_PTR_TERMINATE;
    qh->element = TD_PTR_TERMINATE;
    qh->transfer = 0;
    qh->qhLink.prev = &qh->qhLink;
    qh->qhLink.next = &qh->qhLink;
	char m[33];
	
	unsigned int qq=qh;
	//qq=1;
	__itoa(qq, 16, &m);
    OutTextXYV(10+6*oo, 10, &m, 0xFF00FF, 1, 1024, videoMemory);
    hc->asyncQH = qh;
    for (uint i = 0; i < 1024; ++i)
    {
        hc->frameList[i] = 2| (u32)(uintptr_t)qh;
    }
	IoWrite16(hc->ioAddr + REG_INTR, 0);
    IoWrite16(hc->ioAddr + REG_CMD, IoRead16(hc->ioAddr + REG_CMD)&(~1));
	unsigned short cfg = PciRead16(id, 4);
	PciWrite16(id, 4, cfg & (~1));
	PciWrite16(id, 0x20, (short)-1);
	PciWrite16(id, 0x20, hc->ioAddr);
	PciWrite16(id, 4, cfg |5);
	
	 // Disable Legacy Support
    IoWrite16(hc->ioAddr + REG_LEGSUP, 0x8f00);

    // Disable interrupts
    IoWrite16(hc->ioAddr + REG_INTR, 0);

    // Assign frame list
	IoWrite16(hc->ioAddr + REG_FRNUM, 0);
    IoWrite32(hc->ioAddr + REG_FRBASEADD, (int)hc->frameList);
    IoWrite16(hc->ioAddr + REG_SOFMOD, 0x40);

    // Clear status
    IoWrite16(hc->ioAddr + REG_STS, 0xffff);
	
    // Enable controller
    IoWrite16(hc->ioAddr + REG_CMD, 0xc1);

    // Probe devices
    UhciProbe(hc);
Then im scan port count and for every port execute port reset:

Code: Select all

static uint UhciResetPort(UhciController *hc, uint port)
{
    uint reg = REG_PORT1 + port * 2;

    // Reset the port
    UhciPortSet(hc->ioAddr + reg, PORT_RESET);
	ConsolePrint("a");
	//return 0;
    PitWait(50);
    UhciPortClr(hc->ioAddr + reg, PORT_RESET);

    // Wait 100ms for port to enable (TODO - what is appropriate length of time?)
    uint status = 0;
    for (uint i = 0; i < 10; ++i)
    {
        // Delay
        PitWait(10);
		
        // Get current status
        status = IoRead16(hc->ioAddr + reg);
		
        // Check if device is attached to port
        if (~status & PORT_CONNECTION)
        {
            break;
        }

        // Acknowledge change in status
        if (status & (PORT_ENABLE_CHANGE | PORT_CONNECTION_CHANGE))
        {
			
            UhciPortClr(hc->ioAddr + reg, PORT_ENABLE_CHANGE | PORT_CONNECTION_CHANGE);
            continue;
        }

        // Check if device is enabled
        if (status & PORT_ENABLE)
        {
            break;
        }
        // Enable the port
        UhciPortSet(hc->ioAddr + reg, PORT_ENABLE);
    }

    return status;
}
then im going to send device request to get a device descriptor:

Code: Select all

 UsbDeviceDesc devDesc;
    if (!UsbDevRequest(dev,
        RT_DEV_TO_HOST | RT_STANDARD | RT_DEV,
        REQ_GET_DESC, (USB_DESC_DEVICE << 8) | 0, 0,
        8, &devDesc))
    {
        return false;
    }
UsbDevRequest runs UHCIDevCuntrol:

Code: Select all

static void UhciDevControl(UsbDevice *dev, UsbTransfer *t)
{
    UhciController *hc = (UhciController *)dev->hc;
    UsbDevReq *req = t->req;

    // Determine transfer properties
    uint speed = dev->speed;
    uint addr = dev->addr;
    uint endp = 0;
    uint maxSize = dev->maxPacketSize;
    uint type = req->type;
    uint len = req->len;

    // Create queue of transfer descriptors
    UhciTD *td = UhciAllocTD(hc);
    if (!td)
    {
        return;
    }

    UhciTD *head = td;
    UhciTD *prev = 0;

    // Setup packet
    uint toggle = 0;
    uint packetType = TD_PACKET_SETUP;
    uint packetSize = sizeof(UsbDevReq);
    UhciInitTD(td, prev, speed, addr, endp, toggle, packetType, packetSize, req);
    prev = td;

    // Data in/out packets
    packetType = type & RT_DEV_TO_HOST ? TD_PACKET_IN : TD_PACKET_OUT;

    u8 *it = (u8 *)t->data;
    u8 *end = it + len;
    while (it < end)
    {
        td = UhciAllocTD(hc);
        if (!td)
        {
            return;
        }

        toggle ^= 1;
        packetSize = end - it;
        if (packetSize > maxSize)
        {
            packetSize = maxSize;
        }

        UhciInitTD(td, prev, speed, addr, endp, toggle, packetType, packetSize, it);

        it += packetSize;
        prev = td;
    }

    // Status packet
    td = UhciAllocTD(hc);
    if (!td)
    {
        return;
    }

    toggle = 1;
    packetType = type & RT_DEV_TO_HOST ? TD_PACKET_OUT : TD_PACKET_IN;
    UhciInitTD(td, prev, speed, addr, endp, toggle, packetType, 0, 0);

    // Initialize queue head
    UhciQH *qh = UhciAllocQH(hc);
    UhciInitQH(qh, t, head);
	
    // Wait until queue has been processed
    UhciInsertQH(hc, qh);
    UhciWaitForQH(hc, qh);
and UhciWaitForQH must wait for queue will be processed:

Code: Select all

UsbTransfer *t = qh->transfer;
	
    while (!t->complete)
    {
        UhciProcessQH(hc, qh);
    }

Code: Select all

static void UhciProcessQH(UhciController *hc, UhciQH *qh)
{
    UsbTransfer *t = qh->transfer;
	
    UhciTD *td = (UhciTD *)(uintptr_t)(qh->element);
	//oo++;
    if (!td)
    {
        t->success = true;
        t->complete = true;
    }
    else if (~td->cs & TD_CS_ACTIVE)
    {
        if (td->cs & TD_CS_NAK)
        {
            ConsolePrint("NAK\n");
        }

        if (td->cs & TD_CS_STALLED)
        {
            ConsolePrint("TD is stalled\n");
            t->success = false;
            t->complete = true;
        }

        if (td->cs & TD_CS_DATABUFFER)
        {
            ConsolePrint("TD data buffer error\n");
        }
        if (td->cs & TD_CS_BABBLE)
        {
            ConsolePrint("TD babble error\n");
        }
        if (td->cs & TD_CS_CRC_TIMEOUT)
        {
            ConsolePrint("TD timeout error\n");
        }
        if (td->cs & TD_CS_BITSTUFF)
        {
            ConsolePrint("TD bitstuff error\n");
        }
    }

    if (t->complete)
    {
        // Clear transfer from queue
        qh->transfer = 0;

        // Update endpoint toggle state
        if (t->success && t->endp)
        {
            t->endp->toggle ^= 1;
        }

        // Remove queue from schedule
        UhciRemoveQH(qh);

        // Free transfer descriptors
        UhciTD *td = (UhciTD *)(uintptr_t)qh->tdHead;
        while (td)
        {
            UhciTD *next = (UhciTD *)(uintptr_t)td->tdNext;
            UhciFreeTD(td);
            td = next;
        }

        // Free queue head
        UhciFreeQH(qh);
    }
}
on Bochs it may be works, and queue have been processed(im don't know, but may be)
there is allocations and defines:

Code: Select all

// ------------------------------------------------------------------------------------------------
// Limits
#define MAX_QH                          8
#define MAX_TD                          32

// ------------------------------------------------------------------------------------------------
// UHCI Controller I/O Registers

#define REG_CMD                         0x00        // USB Command
#define REG_STS                         0x02        // USB Status
#define REG_INTR                        0x04        // USB Interrupt Enable
#define REG_FRNUM                       0x06        // Frame Number
#define REG_FRBASEADD                   0x08        // Frame List Base Address
#define REG_SOFMOD                      0x0C        // Start of Frame Modify
#define REG_PORT1                       0x10        // Port 1 Status/Control
#define REG_PORT2                       0x12        // Port 2 Status/Control
#define REG_LEGSUP                      0xc0        // Legacy Support

// ------------------------------------------------------------------------------------------------
// USB Command Register

#define CMD_RS                          (1 << 0)    // Run/Stop
#define CMD_HCRESET                     (1 << 1)    // Host Controller Reset
#define CMD_GRESET                      (1 << 2)    // Global Reset
#define CMD_EGSM                        (1 << 3)    // Enter Global Suspend Resume
#define CMD_FGR                         (1 << 4)    // Force Global Resume
#define CMD_SWDBG                       (1 << 5)    // Software Debug
#define CMD_CF                          (1 << 6)    // Configure Flag
#define CMD_MAXP                        (1 << 7)    // Max Packet (0 = 32, 1 = 64)

// ------------------------------------------------------------------------------------------------
// USB Status Register

#define STS_USBINT                      (1 << 0)    // USB Interrupt
#define STS_ERROR                       (1 << 1)    // USB Error Interrupt
#define STS_RD                          (1 << 2)    // Resume Detect
#define STS_HSE                         (1 << 3)    // Host System Error
#define STS_HCPE                        (1 << 4)    // Host Controller Process Error
#define STS_HCH                         (1 << 5)    // HC Halted

// ------------------------------------------------------------------------------------------------
// USB Interrupt Enable Register

#define INTR_TIMEOUT                    (1 << 0)    // Timeout/CRC Interrupt Enable
#define INTR_RESUME                     (1 << 1)    // Resume Interrupt Enable
#define INTR_IOC                        (1 << 2)    // Interrupt on Complete Enable
#define INTR_SP                         (1 << 3)    // Short Packet Interrupt Enable

// ------------------------------------------------------------------------------------------------
// Port Status and Control Registers

#define PORT_CONNECTION                 (1 << 0)    // Current Connect Status
#define PORT_CONNECTION_CHANGE          (1 << 1)    // Connect Status Change
#define PORT_ENABLE                     (1 << 2)    // Port Enabled
#define PORT_ENABLE_CHANGE              (1 << 3)    // Port Enable Change
#define PORT_LS                         (3 << 4)    // Line Status
#define PORT_RD                         (1 << 6)    // Resume Detect
#define PORT_LSDA                       (1 << 8)    // Low Speed Device Attached
#define PORT_RESET                      (1 << 9)    // Port Reset
#define PORT_SUSP                       (1 << 12)   // Suspend
#define PORT_RWC                        (PORT_CONNECTION_CHANGE | PORT_ENABLE_CHANGE)

// ------------------------------------------------------------------------------------------------
// Transfer Descriptor

typedef struct UhciTD
{
    u32 link;
    u32 cs;
    u32 token;
    u32 buffer;

    // internal fields
    u32 tdNext;
    u8 active;
    u8 pad[11];
} UhciTD;

// TD Link Pointer
#define TD_PTR_TERMINATE                (1 << 0)
#define TD_PTR_QH                       (1 << 1)
#define TD_PTR_DEPTH                    (1 << 2)

// TD Control and Status
#define TD_CS_ACTLEN                    0x000007ff
#define TD_CS_BITSTUFF                  (1 << 17)     // Bitstuff Error
#define TD_CS_CRC_TIMEOUT               (1 << 18)     // CRC/Time Out Error
#define TD_CS_NAK                       (1 << 19)     // NAK Received
#define TD_CS_BABBLE                    (1 << 20)     // Babble Detected
#define TD_CS_DATABUFFER                (1 << 21)     // Data Buffer Error
#define TD_CS_STALLED                   (1 << 22)     // Stalled
#define TD_CS_ACTIVE                    (1 << 23)     // Active
#define TD_CS_IOC                       (1 << 24)     // Interrupt on Complete
#define TD_CS_IOS                       (1 << 25)     // Isochronous Select
#define TD_CS_LOW_SPEED                 (1 << 26)     // Low Speed Device
#define TD_CS_ERROR_MASK                (3 << 27)     // Error counter
#define TD_CS_ERROR_SHIFT               27
#define TD_CS_SPD                       (1 << 29)     // Short Packet Detect

// TD Token
#define TD_TOK_PID_MASK                 0x000000ff    // Packet Identification
#define TD_TOK_DEVADDR_MASK             0x00007f00    // Device Address
#define TD_TOK_DEVADDR_SHIFT            8
#define TD_TOK_ENDP_MASK                00x0078000    // Endpoint
#define TD_TOK_ENDP_SHIFT               15
#define TD_TOK_D                        0x00080000    // Data Toggle
#define TD_TOK_D_SHIFT                  19
#define TD_TOK_MAXLEN_MASK              0xffe00000    // Maximum Length
#define TD_TOK_MAXLEN_SHIFT             21

#define TD_PACKET_IN                    0x69
#define TD_PACKET_OUT                   0xe1
#define TD_PACKET_SETUP                 0x2d

// ------------------------------------------------------------------------------------------------
// Queue Head

typedef struct UhciQH
{
    u32 head;
    u32 element;

    // internal fields
    UsbTransfer *transfer;
    Link qhLink;
    u32 tdHead;
    u32 active;
    u8 pad[24];
} UhciQH;

// ------------------------------------------------------------------------------------------------
// Device

typedef struct UhciController
{
    uint ioAddr;
    u32 *frameList;
    UhciQH *qhPool;
    UhciTD *tdPool;
    UhciQH *asyncQH;
} UhciController;

static UhciTD *UhciAllocTD(UhciController *hc)
{
    // TODO - better memory management
    UhciTD *end = hc->tdPool + MAX_TD;
    for (UhciTD *td = hc->tdPool; td != end; td=(int)td + sizeof(UhciTD))
    {
        if (!td->active)
        {
            //ConsolePrint("UhciAllocTD 0x%08x\n", td);
            td->active = 1;
            return td;
        }
    }

    ConsolePrint("UhciAllocTD failed\n");
    return 0;
}

// ------------------------------------------------------------------------------------------------
static UhciQH *UhciAllocQH(UhciController *hc)
{
    // TODO - better memory management
    UhciQH *end = hc->qhPool + MAX_QH;
    for (UhciQH *qh = hc->qhPool; qh != end; qh=(int)qh + sizeof(UhciQH))
    {
        if (!qh->active)
        {
            //ConsolePrint("UhciAllocQH 0x%08x\n", qh);
            qh->active = 1;
            return qh;
        }
    }

    ConsolePrint("UhciAllocQH failed\n");
    return 0;
}

// ------------------------------------------------------------------------------------------------
static void UhciFreeTD(UhciTD *td)
{
    //ConsolePrint("UhciFreeTD 0x%08x\n", td);
    td->active = 0;
}

// ------------------------------------------------------------------------------------------------
static void UhciFreeQH(UhciQH *qh)
{
    //ConsolePrint("UhciFreeQH 0x%08x\n", qh);
    qh->active = 0;
}

// ------------------------------------------------------------------------------------------------
static void UhciInsertQH(UhciController *hc, UhciQH *qh)
{
    UhciQH *list = hc->asyncQH;
    UhciQH *end = LinkData(list->qhLink.prev, UhciQH, qhLink);

    qh->head = TD_PTR_TERMINATE;
    end->head = (u32)(uintptr_t)qh | TD_PTR_QH;

    LinkBefore(&list->qhLink, &qh->qhLink);
}

// ------------------------------------------------------------------------------------------------
static void UhciRemoveQH(UhciQH *qh)
{
    UhciQH *prev = LinkData(qh->qhLink.prev, UhciQH, qhLink);

    prev->head = qh->head;
    LinkRemove(&qh->qhLink);
}

// ------------------------------------------------------------------------------------------------
static void UhciPortSet(uint port, u16 data)
{
    uint status = IoRead16(port);
    status |= data;
    status &= ~PORT_RWC;
    IoWrite16(port, status);
}

// ------------------------------------------------------------------------------------------------
static void UhciPortClr(uint port, u16 data)
{
    uint status = IoRead16(port);
    status &= ~PORT_RWC;
    status &= ~data;
    status |= PORT_RWC & data;
    IoWrite16(port, status);
}

// ------------------------------------------------------------------------------------------------
static void UhciInitTD(UhciTD *td, UhciTD *prev,
                       uint speed, uint addr, uint endp, uint toggle, uint packetType,
                       uint len, const void *data)
{
    len = (len - 1) & 0x7ff;

    if (prev)
    {
        prev->link = (u32)(uintptr_t)td | TD_PTR_DEPTH;
        prev->tdNext = (u32)(uintptr_t)td;
    }

    td->link = TD_PTR_TERMINATE;
    td->tdNext = 0;

    td->cs = (3 << TD_CS_ERROR_SHIFT) | TD_CS_ACTIVE;
    if (speed == USB_LOW_SPEED)
    {
        td->cs |= TD_CS_LOW_SPEED;
    }

    td->token =
        (len << TD_TOK_MAXLEN_SHIFT) |
        (toggle << TD_TOK_D_SHIFT) |
        (endp << TD_TOK_ENDP_SHIFT) |
        (addr << TD_TOK_DEVADDR_SHIFT) |
        packetType;

    td->buffer = (u32)(uintptr_t)data;
}

// ------------------------------------------------------------------------------------------------
static void UhciInitQH(UhciQH *qh, UsbTransfer *t, UhciTD *td)
{
    qh->transfer = t;
    qh->tdHead = (u32)(uintptr_t)td;
    qh->element = (u32)(uintptr_t)td;
}
What i'm needs to do?
Cheers,
Aleksandr

Re: UHCI not process the queue

Posted: Mon May 28, 2018 6:41 am
by MrLolthe1st
UPD: Ok, processing, now fixing other bugs.

Re: UHCI not process the queue

Posted: Mon May 28, 2018 6:54 am
by MrLolthe1st
UPD: UHCI Works!!!!!!!!!! Thanks at all!
Image