OSDev.org https://forum.osdev.org/ |
|
EHCI didn't works at real PC and VirtualBox! https://forum.osdev.org/viewtopic.php?f=1&t=33061 |
Page 1 of 4 |
Author: | MrLolthe1st [ Sat Jul 07, 2018 4:11 pm ] |
Post subject: | EHCI didn't works at real PC and VirtualBox! |
Hi all, i'm write EHCI driver for my own OS. Sorry for my english, firstly. On QEMU my driver successfully works, but on VirtualBox and real hardware -no. On QEMU: On Virtual box: Frame index rolls over 1024x8 micro frames and didn't processing the all types of queues. My initialization code here: Code: // Base I/O Address PciBar bar; PciGetBar(&bar, id, 0); if (bar.flags & PCI_BAR_IO) { // Only Memory Mapped I/O supported return; } // Controller initialization EhciController *hc = VMAlloc(sizeof(EhciController)); hc->capRegs = (EhciCapRegs *)bar.u.address; hc->opRegs = (EhciOpRegs *)(bar.u.address + hc->capRegs->capLength); hc->frameList = (u32 *)VMAlloc(1024 * sizeof(u32) + 8292); hc->frameList = ((int)hc->frameList/4096)*4096 + 4096; hc->qhPool = (EhciQH *)VMAlloc(sizeof(EhciQH) * MAX_QH + 8292); hc->qhPool =((int)hc->qhPool/4096)*4096 + 4096; hc->tdPool = (EhciTD *)VMAlloc(sizeof(EhciTD) * MAX_TD + 8292); hc->tdPool =((int)hc->tdPool/4096)*4096 + 4096; memset(hc->qhPool, 0, sizeof(EhciQH) * MAX_QH); memset(hc->tdPool, 0, sizeof(EhciTD) * MAX_TD); // Asynchronous queue setup EhciQH *qh = EhciAllocQH(hc); qh->qhlp = (u32)qh | PTR_QH; qh->ch = QH_CH_H; qh->caps = 0; qh->curLink = 0; qh->nextLink = PTR_TERMINATE; qh->altLink = 0; qh->token = 1<<6; for (uint i = 0; i < 5; ++i) { qh->buffer[i] = 0; qh->extBuffer[i] = 0; } qh->transfer = 0; qh->qhLink.prev = &qh->qhLink; qh->qhLink.next = &qh->qhLink; hc->asyncQH = qh; // Periodic list queue setup qh = EhciAllocQH(hc); qh->qhlp = PTR_TERMINATE; qh->ch = 0; qh->caps = 0; qh->curLink = 0; qh->nextLink = PTR_TERMINATE; qh->altLink = 0; qh->token = 1<<6; for (uint i = 0; i < 5; ++i) { qh->buffer[i] = 0; qh->extBuffer[i] = 0; } qh->transfer = 0; qh->qhLink.prev = &qh->qhLink; qh->qhLink.next = &qh->qhLink; hc->periodicQH = qh; for (uint i = 0; i < 1024; ++i) { hc->frameList[i] = 2 | (u32)qh; } // Check extended capabilities uint eecp = (hc->capRegs->hccParams & HCCPARAMS_EECP_MASK) >> HCCPARAMS_EECP_SHIFT; if (eecp >= 0x40) { // Disable BIOS legacy support uint legsup = PciRead32(id, eecp + USBLEGSUP); if (legsup & USBLEGSUP_HC_BIOS) { PciWrite32(id, eecp + USBLEGSUP, legsup | USBLEGSUP_HC_OS); for (;;) { legsup = PciRead32(id, eecp + USBLEGSUP); if (~legsup & USBLEGSUP_HC_BIOS && legsup & USBLEGSUP_HC_OS) { break; } } } } // Disable interrupts hc->opRegs->usbIntr = 0; // Setup frame list hc->opRegs->frameIndex = 0; hc->opRegs->periodicListBase = (u32)hc->frameList; hc->opRegs->asyncListAddr = (u32)hc->asyncQH; hc->opRegs->ctrlDsSegment = 0; // Clear status hc->opRegs->usbSts = 0xffff; // Enable controller hc->opRegs->usbCmd = (0x8 << CMD_ITC_SHIFT) | CMD_PSE | CMD_ASE | CMD_RS; while (hc->opRegs->usbSts & STS_HCHALTED) // TODO - remove after dynamic port detection ; kprintf("In"); // Configure all devices to be managed by the EHCI hc->opRegs->configFlag = 1; PitWait(5); // TODO - remove after dynamic port detection // Probe devices EhciProbe(hc); // Register controller UsbController *controller = (UsbController *)VMAlloc(sizeof(UsbController)); controller->next = g_usbControllerList; controller->hc = hc; controller->poll = EhciControllerPoll; g_usbControllerList = controller; Please, try to help me! I'm troubleshooting that about a month, and no one knows, why my driver didn't works! With best regards, Aleksandr |
Author: | BenLunt [ Sun Jul 08, 2018 2:38 pm ] |
Post subject: | Re: EHCI didn't works at real PC and VirtualBox! |
Hi, Looking over your code, nothing wrong jumps out at me, though I didn't look extremely close. However, one thing I noticed a few times is that with QEMU, compared to other emulators, all memory is initialized to zero. i.e.: any unused register or a register's initial state is zero instead of undefined. Therefore, if a 64-bit register (or memory operand) is used and only the bottom 32-bits are initialized, the implementation still works fine. However, on other emulators and real hardware, this is not the case. The upper 32 bits of the 64-bit field/register/etc is undefined and could be anything. Make sure that all your 64-bit registers, memory pointers, etc are zero when using a 64-bit controller. Ben - http://www.fysnet.net/the_universal_serial_bus.htm |
Author: | MrLolthe1st [ Mon Jul 09, 2018 3:35 am ] |
Post subject: | Re: EHCI didn't works at real PC and VirtualBox! |
Ben, i'm working in protected mode. i'm set some memory fragment to zero: Code: EhciController *hc = VMAlloc(sizeof(EhciController));//That's from last code, that are at top hc->capRegs = (EhciCapRegs *)bar.u.address;//That's from last code, that are at top hc->opRegs = (EhciOpRegs *)(bar.u.address + hc->capRegs->capLength);//That's from last code, that are at top memset(hc->capRegs, 0, hc->capRegs->capLength + 160); There are in my malloc realization memset all bytes of malloced region to zero. I'm don't know what to do, or u can help with code to set all registers to zero at start? What i'm need to do? |
Author: | BenLunt [ Mon Jul 09, 2018 2:29 pm ] |
Post subject: | Re: EHCI didn't works at real PC and VirtualBox! |
I am talking about all of the 64-bit address that may pertain to the card. 1) What are the bottom four bits of the Base IO address at BAR0 (0x10)? Do they indicate that the address can be anywhere in the 64-bit address range? If so, you have to get the next BAR address as well, using it as the top 32 bits of the 64-bit address. 2) The CTRLDSSEGMENT register (Opregs + 0x10) is used for 64-bit addresses. If the card is a 64-bit capable card, you must have all addresses within a 4-gig range and use this register as the top 32 bits of all other (memory access) registers. For example, on 64-bit machines, the PeriodicListBase register (offset 0x14) will be combined with the CTRLDSSegment register to create a 64-bit address: Code: Physical address = ((CTRLDSSegment << 32) | PeriodicListBase); Same goes for all other registers that access memory. (only one other at the moment ) Code: Physical address = ((CTRLDSSegment << 32) | AsyncListAddr); 3) Appendix B shows the format of a 64-bit Data Structure. If you are assuming 32-bit structures and place another 32-bit structure 96 bytes after the last one, on a 64-bit card, it will take the "undefined" data from the second structure as the Extended Buffer Pointer Page[x] address members. It looks like in your code you account for the Extended Buffer entries, but do you make sure that the second one doesn't overlap the first one? Another thing to look at. You say that the controller rolled over the frame list in QEMU but not real hardware. QEMU will have a 1024-entry frame. Real hardware may have a frame with fewer entries. Real hardware may have 1024-, 512-, 256-, and in some cases, a 128-entry frame list. Check the capabilities flag to see how many. Don't assume 1024. Also, please explain in more detail, "it doesn't work!". What isn't working? Ben |
Author: | MrLolthe1st [ Wed Jul 11, 2018 1:42 am ] |
Post subject: | Re: EHCI didn't works at real PC and VirtualBox! |
Ben, thanks to u, but i'm sayed, that frame index rolls over 1024 frames on Virtual Box, on QEMU all working ok. "It doesn't work" - i'm sayed, that host controller didn't process the queue. |
Author: | BenLunt [ Thu Jul 12, 2018 12:20 pm ] |
Post subject: | Re: EHCI didn't works at real PC and VirtualBox! |
So you are saying that as far as you know, the controller (one real hardware) did not process the queue. There are a few things that you can do. 1) If the Queue was actually not processed, then it will still be identical to what you created. i.e.: There will not be any status update. This means that you don't have a valid pointer to the Queue, or haven't started the Queue, or many other reasons. The point is, now you have an idea where to look. 2) If the Queue was actually processed, the status will be updated. Look at the status to see what is going on. Start with a small list of things to check, then check each one until you find that something is wrong at that point. Process of elimination. Do you use or know how to use Bochs? Bochs has a much better and detailed reporting mechanism. If you tell it to print all debug (BXDEBUG) strings, it will give you an idea of what is happening. Ben - http://www.fysnet.net/the_universal_serial_bus.htm |
Author: | MrLolthe1st [ Mon Jul 30, 2018 12:23 pm ] |
Post subject: | Re: EHCI didn't works at real PC and VirtualBox! |
Hi, Ben! Now i'm recieving status F018 on real hardware and С00С/E00C on VirtualBox. I'm sets pointer to queue, but VBox and real PC says, that queue is empty. I'm set all top part of 64-bit registers to zero(There is one: OpRegs + 0x10(CTRLDSSEGMENT)). I'm tried all methods, that you give to me. Why sometimes VirtualBox says, that queue isn't empty and frameIndex are more, than 1024*8(1024 frames and 8 microFrames). (FRI - FRame Index) With best regards, Aleksandr |
Author: | MrLolthe1st [ Tue Jul 31, 2018 4:46 pm ] |
Post subject: | Re: EHCI didn't works at real PC and VirtualBox! |
UP: Ben, there is my code DevControl: Code: static void EhciDevControl(UsbDevice *dev, UsbTransfer *t) { EhciController *hc = (EhciController *)dev->hc; UsbDevReq *req = t->req; // Determine transfer properties uint speed = dev->speed; uint addr = dev->addr; uint maxSize = dev->maxPacketSize; uint type = req->type; uint len = req->len; // Create queue of transfer descriptors EhciTD *td = EhciAllocTD(hc); if (!td) { return; } EhciTD *head = td; EhciTD *prev = 0; // Setup packet uint toggle = 0; uint packetType = USB_PACKET_SETUP; uint packetSize = sizeof(UsbDevReq); EhciInitTD(td, prev, toggle, packetType, packetSize, req); prev = td; // Data in/out packets packetType = type & RT_DEV_TO_HOST ? USB_PACKET_IN : USB_PACKET_OUT; u8 *it = (u8 *)t->data; u8 *end = it + len; while (it < end) { td = EhciAllocTD(hc); if (!td) { return; } toggle ^= 1; packetSize = end - it; if (packetSize > maxSize) { packetSize = maxSize; } EhciInitTD(td, prev, toggle, packetType, packetSize, it); it += packetSize; prev = td; } // Status packet td = EhciAllocTD(hc); if (!td) { return; } toggle = 1; packetType = type & RT_DEV_TO_HOST ? USB_PACKET_OUT : USB_PACKET_IN; EhciInitTD(td, prev, toggle, packetType, 0, 0); // Initialize queue head EhciQH *qh = EhciAllocQH(hc); EhciInitQH(qh, t, head, dev->parent, false, speed, addr, 0, maxSize); // Wait until queue has been processed EhciInsertAsyncQH(hc->asyncQH, qh); EhciWaitForQH(hc, qh); } also code to init td,qh and etc: Code: static void EhciInsertAsyncQH(EhciQH *list, EhciQH *qh) { EhciQH *end = LinkData(list->qhLink.prev, EhciQH, qhLink); qh->qhlp = (u32)(uintptr_t)list | PTR_QH; end->qhlp = (u32)(uintptr_t)qh | PTR_QH; LinkBefore(&list->qhLink, &qh->qhLink); } // ------------------------------------------------------------------------------------------------ static void EhciInsertPeriodicQH(EhciQH *list, EhciQH *qh) { EhciQH *end = LinkData(list->qhLink.prev, EhciQH, qhLink); qh->qhlp = PTR_TERMINATE; end->qhlp = (u32)(uintptr_t)qh | PTR_QH; LinkBefore(&list->qhLink, &qh->qhLink); } static void EhciRemoveQH(EhciQH *qh) { EhciQH *prev = LinkData(qh->qhLink.prev, EhciQH, qhLink); prev->qhlp = qh->qhlp; LinkRemove(&qh->qhLink); } static void EhciPortSet(volatile u32 *portReg, u32 data) { u32 status = *portReg; status |= data; status &= ~PORT_RWC; *portReg = status; } static void EhciPortClr(volatile u32 *portReg, u32 data) { u32 status = *portReg; status &= ~PORT_RWC; status &= ~data; status |= PORT_RWC & data; *portReg = status; } static void EhciInitTD(EhciTD *td, EhciTD *prev, uint toggle, uint packetType, uint len, const void *data) { if (prev) { prev->link = (u32)(uintptr_t)td; prev->tdNext = (u32)(uintptr_t)td; } td->link = PTR_TERMINATE; td->altLink = PTR_TERMINATE; td->tdNext = 0; td->token = (toggle << TD_TOK_D_SHIFT) | (len << TD_TOK_LEN_SHIFT) | (3 << TD_TOK_CERR_SHIFT) | (packetType << TD_TOK_PID_SHIFT) | TD_TOK_ACTIVE; // Data buffer (not necessarily page aligned) uintptr_t p = (uintptr_t)data; td->buffer[0] = (u32)p; td->extBuffer[0] = (u32)(p >> 32); p &= ~0xfff; // Remaining pages of buffer memory. for (uint i = 1; i < 4; ++i) { p += 0x1000; td->buffer[i] = (u32)(p); td->extBuffer[i] = (u32)(p >> 32); } } static void EhciInitQH(EhciQH *qh, UsbTransfer *t, EhciTD *td, UsbDevice *parent, bool interrupt, uint speed, uint addr, uint endp, uint maxSize) { qh->transfer = t; uint ch = (maxSize << QH_CH_MPL_SHIFT) | QH_CH_DTC | (speed << QH_CH_EPS_SHIFT) | (endp << QH_CH_ENDP_SHIFT) | addr; uint caps = (1 << QH_CAP_MULT_SHIFT); if (!interrupt) { ch |= 5 << QH_CH_NAK_RL_SHIFT; } if (speed != USB_HIGH_SPEED && parent) { if (interrupt) { // split completion mask - complete on frames 2, 3, or 4 caps |= (0x1c << QH_CAP_SPLIT_C_SHIFT); } else { ch |= QH_CH_CONTROL; } caps |= (parent->port << QH_CAP_PORT_SHIFT) | (parent->addr << QH_CAP_HUB_ADDR_SHIFT); } if (interrupt) { // interrupt schedule mask - start on frame 0 caps |= (0x01 << QH_CAP_INT_SCHED_SHIFT); } qh->ch = ch; qh->caps = caps; qh->tdHead = (u32)td; qh->nextLink = (u32)td; qh->token = 0; } May be smth corrupting here? |
Author: | BenLunt [ Tue Jul 31, 2018 7:31 pm ] |
Post subject: | Re: EHCI didn't works at real PC and VirtualBox! |
Hi Aleksandr, Sorry I haven't replied yet. I have been quite busy on other things. However, I should have some time tomorrow. Watch for something then. Real quick though, just a thought, could be nothing: Is your Host machine (real hardware and VirtualBox) little-endian or big-endian? Ben |
Author: | MrLolthe1st [ Wed Aug 01, 2018 4:04 am ] |
Post subject: | Re: EHCI didn't works at real PC and VirtualBox! |
Hi, Ben. Little-endian. May you see all my code?: Code: // ------------------------------------------------------------------------------------------------ // Limits #define MAX_QH 8 #define MAX_TD 32 // ------------------------------------------------------------------------------------------------ // PCI Configuration Registers // EECP-based #define USBLEGSUP 0x00 // USB Legacy Support Extended Capability #define UBSLEGCTLSTS 0x04 // USB Legacy Support Control/Status // ------------------------------------------------------------------------------------------------ // USB Legacy Support Register #define USBLEGSUP_HC_OS 0x01000000 // HC OS Owned Semaphore #define USBLEGSUP_HC_BIOS 0x00010000 // HC BIOS Owned Semaphore #define USBLEGSUP_NCP_MASK 0x0000ff00 // Next EHCI Extended Capability Pointer #define USBLEGSUP_CAPID 0x000000ff // Capability ID // ------------------------------------------------------------------------------------------------ // Host Controller Capability Registers typedef struct EhciCapRegs { u8 capLength; u8 reserved; u16 hciVersion; u32 hcsParams; u32 hccParams; u64 hcspPortRoute; } PACKED EhciCapRegs; // ------------------------------------------------------------------------------------------------ // Host Controller Structural Parameters Register #define HCSPARAMS_N_PORTS_MASK (15 << 0) // Number of Ports #define HCSPARAMS_PPC (1 << 4) // Port Power Control #define HCSPARAMS_PORT_ROUTE (1 << 7) // Port Routing Rules #define HCSPARAMS_N_PCC_MASK (15 << 8) // Number of Ports per Companion Controller #define HCSPARAMS_N_PCC_SHIFT 8 #define HCSPARAMS_N_CC_MASK (15 << 12) // Number of Companion Controllers #define HCSPARAMS_N_CC_SHIFT 12 #define HCSPARAMS_P_INDICATOR (1 << 16) // Port Indicator #define HCSPARAMS_DPN_MASK (15 << 20) // Debug Port Number #define HCSPARAMS_DPN_SHIFT 20 // ------------------------------------------------------------------------------------------------ // Host Controller Capability Parameters Register #define HCCPARAMS_64_BIT (1 << 0) // 64-bit Addressing Capability #define HCCPARAMS_PFLF (1 << 1) // Programmable Frame List Flag #define HCCPARAMS_ASPC (1 << 2) // Asynchronous Schedule Park Capability #define HCCPARAMS_IST_MASK (15 << 4) // Isochronous Sheduling Threshold #define HCCPARAMS_EECP_MASK (255 << 8) // EHCI Extended Capabilities Pointer #define HCCPARAMS_EECP_SHIFT 8 // ------------------------------------------------------------------------------------------------ // Host Controller Operational Registers typedef struct EhciOpRegs { volatile u32 usbCmd; volatile u32 usbSts; volatile u32 usbIntr; volatile u32 frameIndex; volatile u32 ctrlDsSegment; volatile u32 periodicListBase; volatile u32 asyncListAddr; volatile u32 reserved[9]; volatile u32 configFlag; volatile u32 ports[]; } EhciOpRegs; // ------------------------------------------------------------------------------------------------ // USB Command Register #define CMD_RS (1 << 0) // Run/Stop #define CMD_HCRESET (1 << 1) // Host Controller Reset #define CMD_FLS_MASK (3 << 2) // Frame List Size #define CMD_FLS_SHIFT 2 #define CMD_PSE (1 << 4) // Periodic Schedule Enable #define CMD_ASE (1 << 5) // Asynchronous Schedule Enable #define CMD_IOAAD (1 << 6) // Interrupt on Async Advance Doorbell #define CMD_LHCR (1 << 7) // Light Host Controller Reset #define CMD_ASPMC_MASK (3 << 8) // Asynchronous Schedule Park Mode Count #define CMD_ASPMC_SHIFT 8 #define CMD_ASPME (1 << 11) // Asynchronous Schedule Park Mode Enable #define CMD_ITC_MASK (255 << 16) // Interrupt Threshold Control #define CMD_ITC_SHIFT 16 // ------------------------------------------------------------------------------------------------ // USB Status Register #define STS_USBINT (1 << 0) // USB Interrupt #define STS_ERROR (1 << 1) // USB Error Interrupt #define STS_PCD (1 << 2) // Port Change Detect #define STS_FLR (1 << 3) // Frame List Rollover #define STS_HSE (1 << 4) // Host System Error #define STS_IOAA (1 << 5) // Interrupt on Async Advance #define STS_HCHALTED (1 << 12) // Host Controller Halted #define STS_RECLAMATION (1 << 13) // Reclamation #define STS_PSS (1 << 14) // Periodic Schedule Status #define STS_ASS (1 << 15) // Asynchronous Schedule Status // ------------------------------------------------------------------------------------------------ // USB Interrupt Enable Register #define INTR_USBINT (1 << 0) // USB Interrupt Enable #define INTR_ERROR (1 << 1) // USB Error Interrupt Enable #define INTR_PCD (1 << 2) // Port Change Interrupt Enable #define INTR_FLR (1 << 3) // Frame List Rollover Enable #define INTR_HSE (1 << 4) // Host System Error Enable #define INTR_IOAA (1 << 5) // Interrupt on Async Advance Enable // ------------------------------------------------------------------------------------------------ // Frame Index Register #define FR_MASK 0x3fff // ------------------------------------------------------------------------------------------------ // Configure Flag Register #define CF_PORT_ROUTE (1 << 0) // Configure Flag (CF) // ------------------------------------------------------------------------------------------------ // 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_OVER_CURRENT (1 << 4) // Over-current Active #define PORT_OVER_CURRENT_CHANGE (1 << 5) // Over-current Change #define PORT_FPR (1 << 6) // Force Port Resume #define PORT_SUSPEND (1 << 7) // Suspend #define PORT_RESET (1 << 8) // Port Reset #define PORT_LS_MASK (3 << 10) // Line Status #define PORT_LS_SHIFT 10 #define PORT_POWER (1 << 12) // Port Power #define PORT_OWNER (1 << 13) // Port Owner #define PORT_IC_MASK (3 << 14) // Port Indicator Control #define PORT_IC_SHIFT 14 #define PORT_TC_MASK (15 << 16) // Port Test Control #define PORT_TC_SHIFT 16 #define PORT_WKCNNT_E (1 << 20) // Wake on Connect Enable #define PORT_WKDSCNNT_E (1 << 21) // Wake on Disconnect Enable #define PORT_WKOC_E (1 << 22) // Wake on Over-current Enable #define PORT_RWC (PORT_CONNECTION_CHANGE | PORT_ENABLE_CHANGE | PORT_OVER_CURRENT_CHANGE) // ------------------------------------------------------------------------------------------------ // Transfer Descriptor typedef struct EhciTD { volatile u32 link; volatile u32 altLink; volatile u32 token; volatile u32 buffer[5]; volatile u32 extBuffer[5]; // internal fields u32 tdNext; u32 active; u8 pad[4 + (128-0x40)]; } EhciTD; // TD Link Pointer #define PTR_TERMINATE (1 << 0) #define PTR_TYPE_MASK (3 << 1) #define PTR_ITD (0 << 1) #define PTR_QH (1 << 1) #define PTR_SITD (2 << 1) #define PTR_FSTN (3 << 1) // TD Token #define TD_TOK_PING (1 << 0) // Ping State #define TD_TOK_STS (1 << 1) // Split Transaction State #define TD_TOK_MMF (1 << 2) // Missed Micro-Frame #define TD_TOK_XACT (1 << 3) // Transaction Error #define TD_TOK_BABBLE (1 << 4) // Babble Detected #define TD_TOK_DATABUFFER (1 << 5) // Data Buffer Error #define TD_TOK_HALTED (1 << 6) // Halted #define TD_TOK_ACTIVE (1 << 7) // Active #define TD_TOK_PID_MASK (3 << 8) // PID Code #define TD_TOK_PID_SHIFT 8 #define TD_TOK_CERR_MASK (3 << 10) // Error Counter #define TD_TOK_CERR_SHIFT 10 #define TD_TOK_C_PAGE_MASK (7 << 12) // Current Page #define TD_TOK_C_PAGE_SHIFT 12 #define TD_TOK_IOC (1 << 15) // Interrupt on Complete #define TD_TOK_LEN_MASK 0x7fff0000 // Total Bytes to Transfer #define TD_TOK_LEN_SHIFT 16 #define TD_TOK_D (1 << 31) // Data Toggle #define TD_TOK_D_SHIFT 31 #define USB_PACKET_OUT 0 // token 0xe1 #define USB_PACKET_IN 1 // token 0x69 #define USB_PACKET_SETUP 2 // token 0x2d // ------------------------------------------------------------------------------------------------ // Queue Head typedef struct EhciQH { u32 qhlp; // Queue Head Horizontal Link Pointer u32 ch; // Endpoint Characteristics u32 caps; // Endpoint Capabilities volatile u32 curLink; // matches a transfer descriptor volatile u32 nextLink; volatile u32 altLink; volatile u32 token; volatile u32 buffer[5]; volatile u32 extBuffer[5]; // internal fields UsbTransfer *transfer; Link qhLink; u32 tdHead; u32 active; u8 pad[40]; } EhciQH; // Endpoint Characteristics #define QH_CH_DEVADDR_MASK 0x0000007f // Device Address #define QH_CH_INACTIVE 0x00000080 // Inactive on Next Transaction #define QH_CH_ENDP_MASK 0x00000f00 // Endpoint Number #define QH_CH_ENDP_SHIFT 8 #define QH_CH_EPS_MASK 0x00003000 // Endpoint Speed #define QH_CH_EPS_SHIFT 12 #define QH_CH_DTC 0x00004000 // Data Toggle Control #define QH_CH_H 0x00008000 // Head of Reclamation List Flag #define QH_CH_MPL_MASK 0x07ff0000 // Maximum Packet Length #define QH_CH_MPL_SHIFT 16 #define QH_CH_CONTROL 0x08000000 // Control Endpoint Flag #define QH_CH_NAK_RL_MASK 0xf0000000 // Nak Count Reload #define QH_CH_NAK_RL_SHIFT 28 // Endpoint Capabilities #define QH_CAP_INT_SCHED_SHIFT 0 #define QH_CAP_INT_SCHED_MASK 0x000000ff // Interrupt Schedule Mask #define QH_CAP_SPLIT_C_SHIFT 8 #define QH_CAP_SPLIT_C_MASK 0x0000ff00 // Split Completion Mask #define QH_CAP_HUB_ADDR_SHIFT 16 #define QH_CAP_HUB_ADDR_MASK 0x007f0000 // Hub Address #define QH_CAP_PORT_MASK 0x3f800000 // Port Number #define QH_CAP_PORT_SHIFT 23 #define QH_CAP_MULT_MASK 0xc0000000 // High-Bandwidth Pipe Multiplier #define QH_CAP_MULT_SHIFT 30 // ------------------------------------------------------------------------------------------------ // Device typedef struct EhciController { EhciCapRegs *capRegs; EhciOpRegs *opRegs; u32 *frameList; EhciQH *qhPool; EhciTD *tdPool; EhciQH *asyncQH; EhciQH *periodicQH; } EhciController; #if 0 // ------------------------------------------------------------------------------------------------ static void EhciPrintTD(EhciTD *td) { kprintf("td=0x%08x\n", td); kprintf(" link=0x%08x\n", td->link); kprintf(" altLink=0x%08x\n", td->altLink); kprintf(" token=0x%08x\n", td->token); kprintf(" buffer=0x%08x\n", td->buffer[0]); } // ------------------------------------------------------------------------------------------------ static void EhciPrintQH(EhciQH *qh) { kprintf("qh=0x%08x\n", qh); kprintf(" qhlp=0x%08x\n", qh->qhlp); kprintf(" ch=0x%08x\n", qh->ch); kprintf(" caps=0x%08x\n", qh->caps); kprintf(" curLink=0x%08x\n", qh->curLink); kprintf(" nextLink=0x%08x\n", qh->nextLink); kprintf(" altLink=0x%08x\n", qh->altLink); kprintf(" token=0x%08x\n", qh->token); kprintf(" buffer=0x%08x\n", qh->buffer[0]); kprintf(" qhPrev=0x%08x\n", qh->qhPrev); kprintf(" qhNext=0x%08x\n", qh->qhNext); } #endif // ------------------------------------------------------------------------------------------------ static EhciTD *EhciAllocTD(EhciController *hc) { // TODO - better memory management EhciTD *end = hc->tdPool + MAX_TD; for (EhciTD *td = hc->tdPool; td != end; ++td) { if (!td->active) { //kprintf("EhciAllocTD 0x%08x\n", td); td->active = 1; return td; } } kprintf("EhciAllocTD failed\n"); return 0; } // ------------------------------------------------------------------------------------------------ static EhciQH *EhciAllocQH(EhciController *hc) { // TODO - better memory management EhciQH *end = hc->qhPool + MAX_QH; for (EhciQH *qh = hc->qhPool; qh != end; ++qh) { if (!qh->active) { //kprintf("EhciAllocQH 0x%08x\n", qh); qh->active = 1; return qh; } } kprintf("EhciAllocQH failed\n"); return 0; } // ------------------------------------------------------------------------------------------------ static void EhciFreeTD(EhciTD *td) { //kprintf("EhciFreeTD 0x%08x\n", td); td->active = 0; } // ------------------------------------------------------------------------------------------------ static void EhciFreeQH(EhciQH *qh) { //kprintf("EhciFreeQH 0x%08x\n", qh); qh->active = 0; } // ------------------------------------------------------------------------------------------------ static void EhciInsertAsyncQH(EhciQH *list, EhciQH *qh) { EhciQH *end = LinkData(list->qhLink.prev, EhciQH, qhLink); qh->qhlp = (u32)(uintptr_t)list | PTR_QH; end->qhlp = (u32)(uintptr_t)qh | PTR_QH; LinkBefore(&list->qhLink, &qh->qhLink); } // ------------------------------------------------------------------------------------------------ static void EhciInsertPeriodicQH(EhciQH *list, EhciQH *qh) { EhciQH *end = LinkData(list->qhLink.prev, EhciQH, qhLink); qh->qhlp = PTR_TERMINATE; end->qhlp = (u32)(uintptr_t)qh | PTR_QH; LinkBefore(&list->qhLink, &qh->qhLink); } // ------------------------------------------------------------------------------------------------ static void EhciRemoveQH(EhciQH *qh) { EhciQH *prev = LinkData(qh->qhLink.prev, EhciQH, qhLink); prev->qhlp = qh->qhlp; LinkRemove(&qh->qhLink); } // ------------------------------------------------------------------------------------------------ static void EhciPortSet(volatile u32 *portReg, u32 data) { u32 status = *portReg; status |= data; status &= ~PORT_RWC; *portReg = status; } // ------------------------------------------------------------------------------------------------ static void EhciPortClr(volatile u32 *portReg, u32 data) { u32 status = *portReg; status &= ~PORT_RWC; status &= ~data; status |= PORT_RWC & data; *portReg = status; } // ------------------------------------------------------------------------------------------------ static void EhciInitTD(EhciTD *td, EhciTD *prev, uint toggle, uint packetType, uint len, const void *data) { if (prev) { prev->link = (u32)(uintptr_t)td; prev->tdNext = (u32)(uintptr_t)td; } td->link = PTR_TERMINATE; td->altLink = PTR_TERMINATE; td->tdNext = 0; td->token = (toggle << TD_TOK_D_SHIFT) | (len << TD_TOK_LEN_SHIFT) | (3 << TD_TOK_CERR_SHIFT) | (packetType << TD_TOK_PID_SHIFT) | TD_TOK_ACTIVE; // Data buffer (not necessarily page aligned) uintptr_t p = (uintptr_t)data; td->buffer[0] = (u32)p; td->extBuffer[0] = (u32)(p >> 32); p &= ~0xfff; // Remaining pages of buffer memory. for (uint i = 1; i < 4; ++i) { p += 0x1000; td->buffer[i] = (u32)(p); td->extBuffer[i] = (u32)(p >> 32); } } // ------------------------------------------------------------------------------------------------ static void EhciInitQH(EhciQH *qh, UsbTransfer *t, EhciTD *td, UsbDevice *parent, bool interrupt, uint speed, uint addr, uint endp, uint maxSize) { qh->transfer = t; uint ch = (maxSize << QH_CH_MPL_SHIFT) | QH_CH_DTC | (speed << QH_CH_EPS_SHIFT) | (endp << QH_CH_ENDP_SHIFT) | addr; uint caps = (1 << QH_CAP_MULT_SHIFT); if (!interrupt) { ch |= 5 << QH_CH_NAK_RL_SHIFT; } if (speed != USB_HIGH_SPEED && parent) { if (interrupt) { // split completion mask - complete on frames 2, 3, or 4 caps |= (0x1c << QH_CAP_SPLIT_C_SHIFT); } else { ch |= QH_CH_CONTROL; } caps |= (parent->port << QH_CAP_PORT_SHIFT) | (parent->addr << QH_CAP_HUB_ADDR_SHIFT); } if (interrupt) { // interrupt schedule mask - start on frame 0 caps |= (0x01 << QH_CAP_INT_SCHED_SHIFT); } qh->ch = ch; qh->caps = caps; qh->tdHead = (u32)td; qh->nextLink = (u32)td; qh->token = 0; } // ------------------------------------------------------------------------------------------------ static void EhciProcessQH(EhciController *hc, EhciQH *qh) { UsbTransfer *t = qh->transfer; kprintf("%x", hc->opRegs->usbSts); if (qh->token & TD_TOK_HALTED) { t->success = false; t->complete = true; } else if (qh->nextLink & PTR_TERMINATE) { if (~qh->token & TD_TOK_ACTIVE) { if (qh->token & TD_TOK_DATABUFFER) { kprintf(" Data Buffer Error\n"); } if (qh->token & TD_TOK_BABBLE) { kprintf(" Babble Detected\n"); } if (qh->token & TD_TOK_XACT) { kprintf(" Transaction Error\n"); } if (qh->token & TD_TOK_MMF) { kprintf(" Missed Micro-Frame\n"); } t->success = true; t->complete = true; } } 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 EhciRemoveQH(qh); // Free transfer descriptors EhciTD *td = (EhciTD *)(uintptr_t)qh->tdHead; while (td) { EhciTD *next = (EhciTD *)(uintptr_t)td->tdNext; EhciFreeTD(td); td = next; } // Free queue head EhciFreeQH(qh); } } // ------------------------------------------------------------------------------------------------ static void EhciWaitForQH(EhciController *hc, EhciQH *qh) { UsbTransfer *t = qh->transfer; while (!t->complete) { EhciProcessQH(hc, qh); } } // ------------------------------------------------------------------------------------------------ static uint EhciResetPort(EhciController *hc, uint port) { volatile u32 *reg = &hc->opRegs->ports[port]; // Reset the port EhciPortSet(reg, PORT_RESET); PitWait(50); EhciPortClr(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 = *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)) { EhciPortClr(reg, PORT_ENABLE_CHANGE | PORT_CONNECTION_CHANGE); continue; } // Check if device is enabled if (status & PORT_ENABLE) { break; } } return status; } // ------------------------------------------------------------------------------------------------ static void EhciDevControl(UsbDevice *dev, UsbTransfer *t) { EhciController *hc = (EhciController *)dev->hc; UsbDevReq *req = t->req; // Determine transfer properties uint speed = dev->speed; uint addr = dev->addr; uint maxSize = dev->maxPacketSize; uint type = req->type; uint len = req->len; // Create queue of transfer descriptors EhciTD *td = EhciAllocTD(hc); if (!td) { return; } EhciTD *head = td; EhciTD *prev = 0; // Setup packet uint toggle = 0; uint packetType = USB_PACKET_SETUP; uint packetSize = sizeof(UsbDevReq); EhciInitTD(td, prev, toggle, packetType, packetSize, req); prev = td; // Data in/out packets packetType = type & RT_DEV_TO_HOST ? USB_PACKET_IN : USB_PACKET_OUT; u8 *it = (u8 *)t->data; u8 *end = it + len; while (it < end) { td = EhciAllocTD(hc); if (!td) { return; } toggle ^= 1; packetSize = end - it; if (packetSize > maxSize) { packetSize = maxSize; } EhciInitTD(td, prev, toggle, packetType, packetSize, it); it += packetSize; prev = td; } // Status packet td = EhciAllocTD(hc); if (!td) { return; } toggle = 1; packetType = type & RT_DEV_TO_HOST ? USB_PACKET_OUT : USB_PACKET_IN; EhciInitTD(td, prev, toggle, packetType, 0, 0); // Initialize queue head EhciQH *qh = EhciAllocQH(hc); EhciInitQH(qh, t, head, dev->parent, false, speed, addr, 0, maxSize); // Wait until queue has been processed EhciInsertAsyncQH(hc->asyncQH, qh); EhciWaitForQH(hc, qh); } // ------------------------------------------------------------------------------------------------ static void EhciDevIntr(UsbDevice *dev, UsbTransfer *t) { EhciController *hc = (EhciController *)dev->hc; // Determine transfer properties uint speed = dev->speed; uint addr = dev->addr; uint maxSize = dev->maxPacketSize; uint endp = t->endp->desc->addr & 0xf; // Create queue of transfer descriptors EhciTD *td = EhciAllocTD(hc); if (!td) { t->success = false; t->complete = true; return; } EhciTD *head = td; EhciTD *prev = 0; // Data in/out packets uint toggle = t->endp->toggle; uint packetType = USB_PACKET_IN; uint packetSize = t->len; EhciInitTD(td, prev, toggle, packetType, packetSize, t->data); // Initialize queue head EhciQH *qh = EhciAllocQH(hc); EhciInitQH(qh, t, head, dev->parent, true, speed, addr, endp, maxSize); // Schedule queue EhciInsertPeriodicQH(hc->periodicQH, qh); } // ------------------------------------------------------------------------------------------------ static void EhciProbe(EhciController *hc) { // Port setup uint portCount = hc->capRegs->hcsParams & HCSPARAMS_N_PORTS_MASK; for (uint port = 0; port < portCount; ++port) { // Reset port uint status = EhciResetPort(hc, port); kprintf("%x", status); if (status & PORT_ENABLE) { uint speed = USB_HIGH_SPEED; UsbDevice *dev = UsbDevCreate(); if (dev) { dev->parent = 0; dev->hc = hc; dev->port = port; dev->speed = speed; dev->maxPacketSize = 8; dev->hcControl = EhciDevControl; dev->hcIntr = EhciDevIntr; if (!UsbDevInit(dev)) { // TODO - cleanup } } } } } // ------------------------------------------------------------------------------------------------ static void EhciControllerPollList(EhciController *hc, Link *list) { EhciQH *qh; EhciQH *next; ListForEachSafe(qh, next, *list, qhLink) { if (qh->transfer) { EhciProcessQH(hc, qh); } } } // ------------------------------------------------------------------------------------------------ static void EhciControllerPoll(UsbController *controller) { EhciController *hc = (EhciController *)controller->hc; EhciControllerPollList(hc, &hc->asyncQH->qhLink); EhciControllerPollList(hc, &hc->periodicQH->qhLink); } // ------------------------------------------------------------------------------------------------ void _ehci_init(uint id, PciDeviceInfo *info) { if (!(((info->classCode << 8) | info->subclass) == PCI_SERIAL_USB && info->progIntf == PCI_SERIAL_USB_EHCI)) { return; } if (sizeof(EhciQH) != 128) { kprintf("Unexpected EhciQH size: %d\n", sizeof(EhciQH)); return; } kprintf("Initializing EHCI\n"); // Base I/O Address PciBar bar; PciGetBar(&bar, id, 0); if (bar.flags & PCI_BAR_IO) { // Only Memory Mapped I/O supported return; } // Controller initialization EhciController *hc = VMAlloc(sizeof(EhciController)); hc->capRegs = (EhciCapRegs *)(uintptr_t)bar.u.address; hc->opRegs = (EhciOpRegs *)(uintptr_t)(bar.u.address + hc->capRegs->capLength); hc->frameList = (u32 *)VMAlloc(1024 * sizeof(u32) + 8192*4); hc->frameList = (((uint)hc->frameList) / 16384) * 16384 + 16384; hc->qhPool = (EhciQH *)VMAlloc(sizeof(EhciQH) * MAX_QH + 8192*4); hc->tdPool = (EhciTD *)VMAlloc(sizeof(EhciTD) * MAX_TD + 8192*4); hc->qhPool = (((uint)hc->qhPool) / 16384) * 16384 + 16384; hc->tdPool = (((uint)hc->tdPool) / 16384) * 16384 + 16384; // Asynchronous queue setup EhciQH *qh = EhciAllocQH(hc); qh->qhlp = (u32)(uintptr_t)qh | PTR_QH; qh->ch = QH_CH_H; qh->caps = 0; qh->curLink = 0; qh->nextLink = PTR_TERMINATE; qh->altLink = 0; qh->token = 0; for (uint i = 0; i < 5; ++i) { qh->buffer[i] = 0; qh->extBuffer[i] = 0; } qh->transfer = 0; qh->qhLink.prev = &qh->qhLink; qh->qhLink.next = &qh->qhLink; hc->asyncQH = qh; // Periodic list queue setup qh = EhciAllocQH(hc); qh->qhlp = PTR_TERMINATE; qh->ch = 0; qh->caps = 0; qh->curLink = 0; qh->nextLink = PTR_TERMINATE; qh->altLink = 0; qh->token = 0; for (uint i = 0; i < 5; ++i) { qh->buffer[i] = 0; qh->extBuffer[i] = 0; } qh->transfer = 0; qh->qhLink.prev = &qh->qhLink; qh->qhLink.next = &qh->qhLink; hc->periodicQH = qh; for (uint i = 0; i < 1024; ++i) { hc->frameList[i] = PTR_QH | (u32)(uintptr_t)qh; } /* // Check extended capabilities uint eecp = (hc->capRegs->hccParams & HCCPARAMS_EECP_MASK) >> HCCPARAMS_EECP_SHIFT; if (eecp >= 0x40) { // Disable BIOS legacy support uint legsup = PciRead32(id, eecp + USBLEGSUP); if (legsup & USBLEGSUP_HC_BIOS) { PciWrite32(id, eecp + USBLEGSUP, legsup | USBLEGSUP_HC_OS); for (;;) { legsup = PciRead32(id, eecp + USBLEGSUP); if (~legsup & USBLEGSUP_HC_BIOS && legsup & USBLEGSUP_HC_OS) { break; } } } } */ // Disable interrupts hc->opRegs->usbIntr = 0; //return; // Setup frame list hc->opRegs->frameIndex = 0; hc->opRegs->periodicListBase = (u32)(uintptr_t)hc->frameList; hc->opRegs->asyncListAddr = (u32)(uintptr_t)hc->asyncQH; hc->opRegs->ctrlDsSegment = 0; // Clear status hc->opRegs->usbSts = ~0; // Enable controller hc->opRegs->usbCmd = (8 << CMD_ITC_SHIFT) | CMD_PSE | CMD_ASE | CMD_RS; while (hc->opRegs->usbSts & STS_HCHALTED) // TODO - remove after dynamic port detection ; // Configure all devices to be managed by the EHCI hc->opRegs->configFlag = 1; PitWait(5); // TODO - remove after dynamic port detection // Probe devices EhciProbe(hc); // Register controller UsbController *controller = (UsbController *)VMAlloc(sizeof(UsbController)); controller->next = g_usbControllerList; controller->hc = hc; controller->poll = EhciControllerPoll; g_usbControllerList = controller; } |
Author: | BenLunt [ Wed Aug 01, 2018 12:50 pm ] |
Post subject: | Re: EHCI didn't works at real PC and VirtualBox! |
Okay, here is what I think is happening. I had this happen to me years ago. The optimizer of your compiler is probably not allowing you to read and write in full dwords. For example, the Operational Registers *must* be read and written as full dwords. Here is the code that got me so long ago. Code: // There is a bug with the (name omitted) compiler version 13.10.4035.0 // Even though the op_regs-> is volatile, it still optimizes out the read dword // and reads only a byte on the following statement: // if ((op_regs->HcRevision & 0x000000FF) != 0x10) return FALSE; // So by making it a function call, with the volatile, and external, it will always // read or write a dword. // Version 15.0.30729.1 of the (name omitted) compiler does not have this problem The optimizer changed the dword read of HcRevision and a mask to a single byte read and no mask. Change your code for all reads and writes to make sure it actually reads and writes as dwords and see what happens. I have a simple function of: (assuming 32-bit addresses) Code: bit32u mem_read_io_regs(const bit32u base, const bit32u offset) { return * ((volatile bit32u *) (base + offset)); } as an external function. External so that the compiler can't "inline" it, nor optimize the read. For example, your code uses a pointer to the mem-mapped I/O address: Code: // typedef struct EhciOpRegs hc->opRegs->periodicListBase = ??? The compiler has no idea that it is actually mem-mapped I/O. It might think that it is simply physical memory and will optimize it as it sees fit. On writes, it isn't so bad because a 32-bit memory operand should be written as 32-bits. However, a read from a 32-bit address can be optimized to a 16-bit or even an 8-bit read access by the optimizer. This will return undefined results for the read. Change your code and see what happens. Ben |
Author: | MrLolthe1st [ Wed Aug 01, 2018 3:29 pm ] |
Post subject: | Re: EHCI didn't works at real PC and VirtualBox! |
I'm changed compiler, do as you say and there aren't any effect |
Author: | BenLunt [ Wed Aug 01, 2018 4:36 pm ] |
Post subject: | Re: EHCI didn't works at real PC and VirtualBox! |
MrLolthe1st wrote: I'm changed compiler, do as you say and there aren't any effect Simply changing to another compiler may or may not solve your problem. Did you modify your code so that *all* mem-mapped I/O access guaranties that it is read and written as dword quantities? Do you have an image of your OS? Do you have a link to it so that I may test it on my test-bed? Ben |
Author: | MrLolthe1st [ Thu Aug 02, 2018 4:46 am ] |
Post subject: | Re: EHCI didn't works at real PC and VirtualBox! |
Yeah, https://yadi.sk/d/c1ygH4DZ3Zpufd. It's simply bootable raw image. |
Author: | MrLolthe1st [ Thu Aug 02, 2018 4:11 pm ] |
Post subject: | Re: EHCI didn't works at real PC and VirtualBox! |
Hi, Ben. I'm rewrite code and nothing changed. Code: #define usbCmdO 0 #define usbStsO 4 #define usbIntrO 8 #define frameIndexO 12 #define ctrlDsSegmentO 16 #define periodicListBaseO 20 #define asyncListAddrO 24 //+36 + 4 #define configFlagO 64 #define portsO 68 #define bit32u uint bit32u MRIR(const bit32u base, const bit32u offset) { return *((volatile bit32u *)(base + offset)); } void MWIR(const bit32u base, const bit32u offset, const bit32u val) { volatile bit32u *ptr = (volatile bit32u *)(base + offset); *ptr = val; } .... MWIR(ehcibase, usbIntrO, 0); //return; // Setup frame list //hc->opRegs->frameIndex = 0; MWIR(ehcibase, frameIndexO, 0); //hc->opRegs->periodicListBase = (u32)(uintptr_t)hc->frameList; MWIR(ehcibase, periodicListBaseO, (u32)(uintptr_t)hc->frameList); //hc->opRegs->asyncListAddr = (u32)(uintptr_t)hc->asyncQH; MWIR(ehcibase, asyncListAddrO, (u32)(uintptr_t)hc->asyncQH); // kprintf("[%x,%x]", &hc->opRegs->asyncListAddr, (uintptr_t)(bar.u.address + hc->capRegs->capLength)); // moveee((uintptr_t)(bar.u.address + hc->capRegs->capLength)+0x14, 0x4, (u32)(uintptr_t)hc->asyncQH); //hc->opRegs->ctrlDsSegment = 0; MWIR(ehcibase, ctrlDsSegmentO, 0); //PitWait(100); // Clear status //hc->opRegs->usbSts = ~0; MWIR(ehcibase, usbStsO, ~0); // Enable controller //hc->opRegs->usbCmd = (8 << CMD_ITC_SHIFT) | CMD_PSE | CMD_ASE | CMD_RS; MWIR(ehcibase, usbCmdO, (8 << CMD_ITC_SHIFT) | CMD_PSE | CMD_ASE | CMD_RS); //while (hc->opRegs->usbSts & STS_HCHALTED) // TODO - remove after dynamic port detection // ; // Configure all devices to be managed by the EHCI //hc->opRegs->configFlag = 1; MWIR(ehcibase, configFlagO, 1); With best Regards, Aleksandr |
Page 1 of 4 | All times are UTC - 6 hours |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |