int ehci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize , void *data, int datasize) { ASSERT32FLAT(); if (! CONFIG_USB_EHCI) return -1; dprintf(5, "ehci_control %p (dir=%d cmd=%d data=%d)\n" , p, dir, cmdsize, datasize); if (datasize > 4*4096 || cmdsize > 4*4096) { // XXX - should support larger sizes. warn_noalloc(); return -1; } struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); // Setup transfer descriptors struct ehci_qtd *tds = memalign_tmphigh(EHCI_QTD_ALIGN, sizeof(*tds) * 3); if (!tds) { warn_noalloc(); return -1; } memset(tds, 0, sizeof(*tds) * 3); struct ehci_qtd *td = tds; td->qtd_next = (u32)&td[1]; td->alt_next = EHCI_PTR_TERM; td->token = (ehci_explen(cmdsize) | QTD_STS_ACTIVE | QTD_PID_SETUP | ehci_maxerr(3)); u16 maxpacket = pipe->pipe.maxpacket; fillTDbuffer(td, maxpacket, cmd, cmdsize); td++; if (datasize) { td->qtd_next = (u32)&td[1]; td->alt_next = EHCI_PTR_TERM; td->token = (QTD_TOGGLE | ehci_explen(datasize) | QTD_STS_ACTIVE | (dir ? QTD_PID_IN : QTD_PID_OUT) | ehci_maxerr(3)); fillTDbuffer(td, maxpacket, data, datasize); td++; } td->qtd_next = EHCI_PTR_TERM; td->alt_next = EHCI_PTR_TERM; td->token = (QTD_TOGGLE | QTD_STS_ACTIVE | (dir ? QTD_PID_OUT : QTD_PID_IN) | ehci_maxerr(3)); // Transfer data barrier(); pipe->qh.qtd_next = (u32)tds; int i, ret=0; for (i=0; i<3; i++) { struct ehci_qtd *td = &tds[i]; ret = ehci_wait_td(pipe, td, 500); if (ret) break; } free(tds); return ret; }
int ehci_poll_intr(struct usb_pipe *p, void *data) { ASSERT16(); if (! CONFIG_USB_EHCI) return -1; struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); struct ehci_qtd *td = GET_LOWFLAT(pipe->next_td); u32 token = GET_LOWFLAT(td->token); if (token & QTD_STS_ACTIVE) // No intrs found. return -1; // XXX - check for errors. // Copy data. int maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket); int pos = td - GET_LOWFLAT(pipe->tds); void *tddata = GET_LOWFLAT(pipe->data) + maxpacket * pos; memcpy_far(GET_SEG(SS), data, SEG_LOW, LOWFLAT2LOW(tddata), maxpacket); // Reenable this td. struct ehci_qtd *next = (void*)(GET_LOWFLAT(td->qtd_next) & ~EHCI_PTR_BITS); SET_LOWFLAT(pipe->next_td, next); SET_LOWFLAT(td->buf[0], (u32)tddata); barrier(); SET_LOWFLAT(td->token, (ehci_explen(maxpacket) | QTD_STS_ACTIVE | QTD_PID_IN | ehci_maxerr(3))); return 0; }
int ehci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) { if (! CONFIG_USB_EHCI) return -1; struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); dprintf(7, "ehci_send_bulk qh=%p dir=%d data=%p size=%d\n" , &pipe->qh, dir, data, datasize); // Allocate 4 tds on stack (16byte aligned) u8 tdsbuf[sizeof(struct ehci_qtd) * STACKQTDS + EHCI_QTD_ALIGN - 1]; struct ehci_qtd *tds = (void*)ALIGN((u32)tdsbuf, EHCI_QTD_ALIGN); memset(tds, 0, sizeof(*tds) * STACKQTDS); // Setup fields in qh u16 maxpacket = GET_FLATPTR(pipe->pipe.maxpacket); SET_FLATPTR(pipe->qh.info1 , ((1 << QH_MULT_SHIFT) | (maxpacket << QH_MAXPACKET_SHIFT) | (GET_FLATPTR(pipe->pipe.speed) << QH_SPEED_SHIFT) | (GET_FLATPTR(pipe->pipe.ep) << QH_EP_SHIFT) | (GET_FLATPTR(pipe->pipe.devaddr) << QH_DEVADDR_SHIFT))); SET_FLATPTR(pipe->qh.info2 , ((1 << QH_MULT_SHIFT) | (GET_FLATPTR(pipe->pipe.tt_port) << QH_HUBPORT_SHIFT) | (GET_FLATPTR(pipe->pipe.tt_devaddr) << QH_HUBADDR_SHIFT))); barrier(); SET_FLATPTR(pipe->qh.qtd_next, (u32)MAKE_FLATPTR(GET_SEG(SS), tds)); int tdpos = 0; while (datasize) { struct ehci_qtd *td = &tds[tdpos++ % STACKQTDS]; int ret = ehci_wait_td(pipe, td, 5000); if (ret) return -1; struct ehci_qtd *nexttd_fl = MAKE_FLATPTR(GET_SEG(SS) , &tds[tdpos % STACKQTDS]); int transfer = fillTDbuffer(td, maxpacket, data, datasize); td->qtd_next = (transfer==datasize ? EHCI_PTR_TERM : (u32)nexttd_fl); td->alt_next = EHCI_PTR_TERM; barrier(); td->token = (ehci_explen(transfer) | QTD_STS_ACTIVE | (dir ? QTD_PID_IN : QTD_PID_OUT) | ehci_maxerr(3)); data += transfer; datasize -= transfer; } int i; for (i=0; i<STACKQTDS; i++) { struct ehci_qtd *td = &tds[tdpos++ % STACKQTDS]; int ret = ehci_wait_td(pipe, td, 5000); if (ret) return -1; } return 0; }
int ehci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) { if (! CONFIG_USB_EHCI) return -1; struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); dprintf(7, "ehci_send_bulk qh=%p dir=%d data=%p size=%d\n" , &pipe->qh, dir, data, datasize); // Allocate 4 tds on stack (with required alignment) u8 tdsbuf[sizeof(struct ehci_qtd) * STACKQTDS + EHCI_QTD_ALIGN - 1]; struct ehci_qtd *tds = (void*)ALIGN((u32)tdsbuf, EHCI_QTD_ALIGN); memset(tds, 0, sizeof(*tds) * STACKQTDS); barrier(); SET_LOWFLAT(pipe->qh.qtd_next, (u32)MAKE_FLATPTR(GET_SEG(SS), tds)); u16 maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket); int tdpos = 0; while (datasize) { struct ehci_qtd *td = &tds[tdpos++ % STACKQTDS]; int ret = ehci_wait_td(pipe, td, 5000); if (ret) return -1; struct ehci_qtd *nexttd_fl = MAKE_FLATPTR(GET_SEG(SS) , &tds[tdpos % STACKQTDS]); int transfer = fillTDbuffer(td, maxpacket, data, datasize); td->qtd_next = (transfer==datasize ? EHCI_PTR_TERM : (u32)nexttd_fl); td->alt_next = EHCI_PTR_TERM; barrier(); td->token = (ehci_explen(transfer) | QTD_STS_ACTIVE | (dir ? QTD_PID_IN : QTD_PID_OUT) | ehci_maxerr(3)); data += transfer; datasize -= transfer; } int i; for (i=0; i<STACKQTDS; i++) { struct ehci_qtd *td = &tds[tdpos++ % STACKQTDS]; int ret = ehci_wait_td(pipe, td, 5000); if (ret) return -1; } return 0; }
static struct usb_pipe * ehci_alloc_intr_pipe(struct usbdevice_s *usbdev , struct usb_endpoint_descriptor *epdesc) { struct usb_ehci_s *cntl = container_of( usbdev->hub->cntl, struct usb_ehci_s, usb); int frameexp = usb_getFrameExp(usbdev, epdesc); dprintf(7, "ehci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp); if (frameexp > 10) frameexp = 10; int maxpacket = epdesc->wMaxPacketSize; // Determine number of entries needed for 2 timer ticks. int ms = 1<<frameexp; int count = DIV_ROUND_UP(PIT_TICK_INTERVAL * 1000 * 2, PIT_TICK_RATE * ms); struct ehci_pipe *pipe = memalign_low(EHCI_QH_ALIGN, sizeof(*pipe)); struct ehci_qtd *tds = memalign_low(EHCI_QTD_ALIGN, sizeof(*tds) * count); void *data = malloc_low(maxpacket * count); if (!pipe || !tds || !data) { warn_noalloc(); goto fail; } memset(pipe, 0, sizeof(*pipe)); ehci_desc2pipe(pipe, usbdev, epdesc); pipe->next_td = pipe->tds = tds; pipe->data = data; pipe->qh.qtd_next = (u32)tds; int i; for (i=0; i<count; i++) { struct ehci_qtd *td = &tds[i]; td->qtd_next = (i==count-1 ? (u32)tds : (u32)&td[1]); td->alt_next = EHCI_PTR_TERM; td->token = (ehci_explen(maxpacket) | QTD_STS_ACTIVE | QTD_PID_IN | ehci_maxerr(3)); td->buf[0] = (u32)data + maxpacket * i; } // Add to interrupt schedule. struct ehci_framelist *fl = (void*)readl(&cntl->regs->periodiclistbase); if (frameexp == 0) { // Add to existing interrupt entry. struct ehci_qh *intr_qh = (void*)(fl->links[0] & ~EHCI_PTR_BITS); pipe->qh.next = intr_qh->next; barrier(); intr_qh->next = (u32)&pipe->qh | EHCI_PTR_QH; } else { int startpos = 1<<(frameexp-1); pipe->qh.next = fl->links[startpos]; barrier(); for (i=startpos; i<ARRAY_SIZE(fl->links); i+=ms) fl->links[i] = (u32)&pipe->qh | EHCI_PTR_QH; } return &pipe->pipe; fail: free(pipe); free(tds); free(data); return NULL; }
struct usb_pipe * ehci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp) { if (! CONFIG_USB_EHCI) return NULL; struct usb_ehci_s *cntl = container_of( dummy->cntl, struct usb_ehci_s, usb); dprintf(7, "ehci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp); if (frameexp > 10) frameexp = 10; int maxpacket = dummy->maxpacket; // Determine number of entries needed for 2 timer ticks. int ms = 1<<frameexp; int count = DIV_ROUND_UP(PIT_TICK_INTERVAL * 1000 * 2, PIT_TICK_RATE * ms); struct ehci_pipe *pipe = memalign_low(EHCI_QH_ALIGN, sizeof(*pipe)); struct ehci_qtd *tds = memalign_low(EHCI_QTD_ALIGN, sizeof(*tds) * count); void *data = malloc_low(maxpacket * count); if (!pipe || !tds || !data) { warn_noalloc(); goto fail; } memset(pipe, 0, sizeof(*pipe)); memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe)); pipe->next_td = pipe->tds = tds; pipe->data = data; pipe->qh.info1 = ( (1 << QH_MULT_SHIFT) | (maxpacket << QH_MAXPACKET_SHIFT) | (pipe->pipe.speed << QH_SPEED_SHIFT) | (pipe->pipe.ep << QH_EP_SHIFT) | (pipe->pipe.devaddr << QH_DEVADDR_SHIFT)); pipe->qh.info2 = ((1 << QH_MULT_SHIFT) | (pipe->pipe.tt_port << QH_HUBPORT_SHIFT) | (pipe->pipe.tt_devaddr << QH_HUBADDR_SHIFT) | (0x01 << QH_SMASK_SHIFT) | (0x1c << QH_CMASK_SHIFT)); pipe->qh.qtd_next = (u32)tds; int i; for (i=0; i<count; i++) { struct ehci_qtd *td = &tds[i]; td->qtd_next = (i==count-1 ? (u32)tds : (u32)&td[1]); td->alt_next = EHCI_PTR_TERM; td->token = (ehci_explen(maxpacket) | QTD_STS_ACTIVE | QTD_PID_IN | ehci_maxerr(3)); td->buf[0] = (u32)data + maxpacket * i; } // Add to interrupt schedule. struct ehci_framelist *fl = (void*)readl(&cntl->regs->periodiclistbase); if (frameexp == 0) { // Add to existing interrupt entry. struct ehci_qh *intr_qh = (void*)(fl->links[0] & ~EHCI_PTR_BITS); pipe->qh.next = intr_qh->next; barrier(); intr_qh->next = (u32)&pipe->qh | EHCI_PTR_QH; } else { int startpos = 1<<(frameexp-1); pipe->qh.next = fl->links[startpos]; barrier(); for (i=startpos; i<ARRAY_SIZE(fl->links); i+=ms) fl->links[i] = (u32)&pipe->qh | EHCI_PTR_QH; } return &pipe->pipe; fail: free(pipe); free(tds); free(data); return NULL; }