I now got the idea but it doesn't seem to work yet. I have the following function for creating queue heads:
Code:
static EhciQueue* ehciCreateQueue(uint8_t devaddr, uint8_t endpoint, int speed, size_t maxPacketLen, uint32_t flags)
{
EhciQueue *queue = NEW(EhciQueue);
queue->next = NULL;
if (dmaCreateBuffer(&queue->dmabuf, sizeof(EhciQH), DMA_32BIT) != 0)
{
kfree(queue);
return NULL;
};
queue->qh = (EhciQH*) dmaGetPtr(&queue->dmabuf);
queue->physQH = (uint32_t) dmaGetPhys(&queue->dmabuf);
// zero out the queue head
memset(queue->qh, 0, sizeof(EhciQH));
// initialize "horptr" to point to nothing, just in case
queue->qh->horptr = EHCI_HORPTR_TERM;
queue->qh->endpointInfo = devaddr /* low 7 bits = device address */
/* bit 7 = invalidate bit (initialize to 0) */
| (endpoint << 8) /* bits 11:8 = endpoint number */
| (speed << 12) /* bits 13:12 = endpoint speed */
| (maxPacketLen << 16) /* bits 26:16 = maximum packet length */
| flags; /* any extra flags */
queue->qh->endpointCaps = (1 << 30); // TODO: hubs and stuff
// no transfer in progress
queue->qh->overlay.horptr = EHCI_HORPTR_TERM;
queue->qh->overlay.altHorptr = EHCI_HORPTR_TERM;
return queue;
};
The horizontal pointer (horptr) isn't set by ehciCreateQueue() because i'll have another function, ehciAddQueue(), which adds it to a list (async or periodic).
And I create the queue for the default address and default endpoint, so that I may send SET_ADDRESS requests (it is also the head of the list, so I set the H bit):
Code:
qAsync = ehciCreateQueue(0, 0, EHCI_SPEED_HIGH, 64, EHCI_QH_HEAD);
And I set up the horizontal pointer and the asynchronous schedule:
Code:
qAsync->qh->horptr = qAsync->physQH | EHCI_HORPTR_QH;
qAsync->next = qAsync;
ehciRegs->asyncListAddr = qAsync->physQH;
ehciRegs->usbcmd |= EHCI_USBCMD_ASYNC_ENABLE;
while ((ehciRegs->usbsts & EHCI_USBSTS_ASYNC) == 0) __sync_synchronize(); // wait until enabled
Then I have the following structure that I want to allocate in a DMA buffer (which is basically physically contiguous memory):
Code:
typedef struct
{
EhciTD tdSetup;
EhciTD tdData;
EhciTD tdStatus;
USBSetupPacket setupData;
} EhciSetAddr;
Which I then use as follows to set up a SET_ADDRESS request (right after restting the port):
Code:
sleep(100);
ehciRegs->ports[i] |= EHCI_PORT_RESET;
sleep(50);
ehciRegs->ports[i] &= ~EHCI_PORT_RESET;
// allocate an address for the new device
usb_addr_t addr = usbAllocAddr();
if (addr == 0)
{
kprintf_debug("ehci: error: out of USB addresses\n");
continue;
};
// set up the transaction
uint32_t transPhys = dmaGetPhys(&dmaSetAddr);
memset(reqSetAddr, 0, sizeof(EhciSetAddr));
reqSetAddr->tdSetup.horptr = transPhys + offsetof(EhciSetAddr, tdData);
reqSetAddr->tdSetup.altHorptr = EHCI_HORPTR_TERM;
reqSetAddr->tdSetup.token = 0
| (8 << 16) /* 8 bytes to transfer */
| (EHCI_PID_SETUP << 8) /* SETUP packet */
| EHCI_TD_ACTIVE;
reqSetAddr->tdSetup.bufs[0] = transPhys + offsetof(EhciSetAddr, setupData);
reqSetAddr->tdData.horptr = transPhys + offsetof(EhciSetAddr, tdStatus);
reqSetAddr->tdData.altHorptr = EHCI_HORPTR_TERM;
reqSetAddr->tdData.token = 0
| (EHCI_PID_OUT << 8) /* OUT packet */
| EHCI_TD_ACTIVE;
reqSetAddr->tdStatus.horptr = EHCI_HORPTR_TERM;
reqSetAddr->tdStatus.altHorptr = EHCI_HORPTR_TERM;
reqSetAddr->tdStatus.token = 0
| (EHCI_PID_IN << 8) /* IN packet */
| EHCI_TD_ACTIVE;
reqSetAddr->setupData.bmRequestType = 0;
reqSetAddr->setupData.bRequest = USB_REQ_SET_ADDRESS;
reqSetAddr->setupData.wValue = addr;
reqSetAddr->setupData.wIndex = 0;
reqSetAddr->setupData.wLength = 0;
uint32_t tokenOrg = reqSetAddr->tdSetup.token;
// send the request
qAsync->qh->overlay.horptr = transPhys;
while (1)
{
kprintf("TOKEN WAS: 0x%08X, TOKEN IS: 0x%08X\n", tokenOrg, reqSetAddr->tdSetup.token);
};
The kprintf() indicates that for the tdSetup (SETUP stage) transfer descriptor, EHCI clears the "Active" bit (bit 7) and sets the "Transmission error" bit (bit 3).
As shwon above, according to what Korona said I assumed the default endpoint to be high-speed, 64 max packet size. I tried other combinations too (full-speed, "Control" bit set) but none of them seem to work. Where coudlthe problem lie?