static int ehci_wait_td(struct ehci_pipe *pipe, struct ehci_qtd *td, int timeout) { u64 end = calc_future_tsc(timeout); u32 status; for (;;) { status = td->token; if (!(status & QTD_STS_ACTIVE)) break; if (check_tsc(end)) { u32 cur = GET_LOWFLAT(pipe->qh.current); u32 tok = GET_LOWFLAT(pipe->qh.token); u32 next = GET_LOWFLAT(pipe->qh.qtd_next); warn_timeout(); dprintf(1, "ehci pipe=%p cur=%08x tok=%08x next=%x td=%p status=%x\n" , pipe, cur, tok, next, td, status); ehci_reset_pipe(pipe); struct usb_ehci_s *cntl = container_of( GET_LOWFLAT(pipe->pipe.cntl), struct usb_ehci_s, usb); ehci_waittick(cntl); return -1; } yield(); } if (status & QTD_STS_HALT) { dprintf(1, "ehci_wait_td error - status=%x\n", status); ehci_reset_pipe(pipe); return -2; } return 0; }
static void ehci_reset_pipe(struct ehci_pipe *pipe) { SET_LOWFLAT(pipe->qh.qtd_next, EHCI_PTR_TERM); SET_LOWFLAT(pipe->qh.alt_next, EHCI_PTR_TERM); barrier(); SET_LOWFLAT(pipe->qh.token, GET_LOWFLAT(pipe->qh.token) & QTD_TOGGLE); }
// Maximum time (in ms) a data transfer should take int usb_xfer_time(struct usb_pipe *pipe, int datalen) { // Use the maximum command time (5 seconds), except for // set_address commands where we don't want to stall the boot if // the device doesn't actually exist. Add 100ms to account for // any controller delays. if (!GET_LOWFLAT(pipe->devaddr)) return USB_TIME_STATUS + 100; return USB_TIME_COMMAND + 100; }
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 (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; }
int usb_poll_intr(struct usb_pipe *pipe_fl, void *data) { ASSERT16(); switch (GET_LOWFLAT(pipe_fl->type)) { default: case USB_TYPE_UHCI: return uhci_poll_intr(pipe_fl, data); case USB_TYPE_OHCI: return ohci_poll_intr(pipe_fl, data); case USB_TYPE_EHCI: return ehci_poll_intr(pipe_fl, data); case USB_TYPE_XHCI: ; return call32_params(xhci_poll_intr, pipe_fl , MAKE_FLATPTR(GET_SEG(SS), data), 0, -1); } }
int usb_poll_intr(struct usb_pipe *pipe_fl, void *data) { ASSERT16(); switch (GET_LOWFLAT(pipe_fl->type)) { default: case USB_TYPE_UHCI: return uhci_poll_intr(pipe_fl, data); case USB_TYPE_OHCI: return ohci_poll_intr(pipe_fl, data); case USB_TYPE_EHCI: return ehci_poll_intr(pipe_fl, data); case USB_TYPE_XHCI: ; extern void _cfunc32flat_xhci_poll_intr(void); return call32_params(_cfunc32flat_xhci_poll_intr, (u32)pipe_fl , (u32)MAKE_FLATPTR(GET_SEG(SS), (u32)data), 0, -1); } }
// Send a message on a control pipe using the default control descriptor. static int usb_send_pipe(struct usb_pipe *pipe_fl, int dir, const void *cmd , void *data, int datasize) { switch (GET_LOWFLAT(pipe_fl->type)) { default: case USB_TYPE_UHCI: return uhci_send_pipe(pipe_fl, dir, cmd, data, datasize); case USB_TYPE_OHCI: if (MODESEGMENT) return -1; return ohci_send_pipe(pipe_fl, dir, cmd, data, datasize); case USB_TYPE_EHCI: return ehci_send_pipe(pipe_fl, dir, cmd, data, datasize); case USB_TYPE_XHCI: if (MODESEGMENT) return -1; return xhci_send_pipe(pipe_fl, dir, cmd, data, datasize); } }
int usb_32bit_pipe(struct usb_pipe *pipe_fl) { return (CONFIG_USB_XHCI && GET_LOWFLAT(pipe_fl->type) == USB_TYPE_XHCI) || (CONFIG_USB_OHCI && GET_LOWFLAT(pipe_fl->type) == USB_TYPE_OHCI); }