+// Wait for next USB frame to start - for ensuring safe memory release.
+static void
+ohci_waittick(struct usb_ohci_s *cntl)
+{
+ barrier();
+ struct ohci_hcca *hcca = (void*)cntl->regs->hcca;
+ u32 startframe = hcca->frame_no;
+ u64 end = calc_future_tsc(1000 * 5);
+ for (;;) {
+ if (hcca->frame_no != startframe)
+ break;
+ if (check_tsc(end)) {
+ warn_timeout();
+ return;
+ }
+ yield();
+ }
+}
+
+static void
+signal_freelist(struct usb_ohci_s *cntl)
+{
+ u32 v = readl(&cntl->regs->control);
+ if (v & OHCI_CTRL_CLE) {
+ writel(&cntl->regs->control, v & ~(OHCI_CTRL_CLE|OHCI_CTRL_BLE));
+ ohci_waittick(cntl);
+ writel(&cntl->regs->ed_controlcurrent, 0);
+ writel(&cntl->regs->ed_bulkcurrent, 0);
+ writel(&cntl->regs->control, v);
+ } else {
+ ohci_waittick(cntl);
+ }
+}
+
+struct ohci_pipe {
+ struct ohci_ed ed;
+ struct usb_pipe pipe;
+ void *data;
+ int count;
+ struct ohci_td *tds;
+};
+
+void
+ohci_free_pipe(struct usb_pipe *p)
+{
+ if (! CONFIG_USB_OHCI)
+ return;
+ dprintf(7, "ohci_free_pipe %p\n", p);
+ struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe);
+ struct usb_ohci_s *cntl = container_of(
+ pipe->pipe.cntl, struct usb_ohci_s, usb);
+
+ u32 *pos = &cntl->regs->ed_controlhead;
+ for (;;) {
+ struct ohci_ed *next = (void*)*pos;
+ if (!next) {
+ // Not found?! Exit without freeing.
+ warn_internalerror();
+ return;
+ }
+ if (next == &pipe->ed) {
+ *pos = next->hwNextED;
+ signal_freelist(cntl);
+ free(pipe);
+ return;
+ }
+ pos = &next->hwNextED;
+ }
+}
+
+struct usb_pipe *
+ohci_alloc_control_pipe(struct usb_pipe *dummy)
+{
+ if (! CONFIG_USB_OHCI)
+ return NULL;
+ struct usb_ohci_s *cntl = container_of(
+ dummy->cntl, struct usb_ohci_s, usb);
+ dprintf(7, "ohci_alloc_control_pipe %p\n", &cntl->usb);
+
+ // Allocate a queue head.
+ struct ohci_pipe *pipe = malloc_tmphigh(sizeof(*pipe));
+ if (!pipe) {
+ warn_noalloc();
+ return NULL;
+ }
+ memset(pipe, 0, sizeof(*pipe));
+ memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe));
+ pipe->ed.hwINFO = ED_SKIP;
+
+ // Add queue head to controller list.
+ pipe->ed.hwNextED = cntl->regs->ed_controlhead;
+ barrier();
+ cntl->regs->ed_controlhead = (u32)&pipe->ed;
+ return &pipe->pipe;
+}
+