/* remove queue from device schedule, dropping all data that came in */ static void xhci_destroy_intr_queue(endpoint_t *const ep, void *const q) { xhci_t *const xhci = XHCI_INST(ep->dev->controller); const int slot_id = ep->dev->address; const int ep_id = xhci_ep_id(ep); transfer_ring_t *const tr = xhci->dev[slot_id].transfer_rings[ep_id]; intrq_t *const intrq = (intrq_t *)q; /* Make sure the endpoint is stopped */ if (EC_GET(STATE, xhci->dev[slot_id].ctx.ep[ep_id]) == 1) { const int cc = xhci_cmd_stop_endpoint(xhci, slot_id, ep_id); if (cc != CC_SUCCESS) xhci_debug("Warning: Failed to stop endpoint\n"); } /* Process all remaining transfer events */ xhci_handle_events(xhci); /* Free all pending transfers and the interrupt queue structure */ int i; for (i = 0; i < intrq->count; ++i) { free(phys_to_virt(intrq->next->ptr_low)); intrq->next = xhci_next_trb(intrq->next, NULL); } xhci->dev[slot_id].interrupt_queues[ep_id] = NULL; free((void *)intrq); /* Reset the controller's dequeue pointer and reinitialize the ring */ xhci_cmd_set_tr_dq(xhci, slot_id, ep_id, tr->ring, 1); xhci_init_cycle_ring(tr, TRANSFER_RING_SIZE); }
void xhci_dump_transfer_trbs(const trb_t *const first, const trb_t *const last) { const trb_t *cur; for (cur = first; cur; cur = xhci_next_trb(cur)) { xhci_dump_transfer_trb(cur); if (cur == last) break; } }
/* read one intr-packet from queue, if available. extend the queue for new input. return NULL if nothing new available. Recommended use: while (data=poll_intr_queue(q)) process(data); */ static u8 * xhci_poll_intr_queue(void *const q) { if (!q) return NULL; intrq_t *const intrq = (intrq_t *)q; endpoint_t *const ep = intrq->ep; xhci_t *const xhci = XHCI_INST(ep->dev->controller); /* TODO: Reset interrupt queue if it gets halted? */ xhci_handle_events(xhci); u8 *reqdata = NULL; while (!reqdata && intrq->ready) { const int ep_id = xhci_ep_id(ep); transfer_ring_t *const tr = xhci->dev[ep->dev->address].transfer_rings[ep_id]; /* Fetch the request's buffer */ reqdata = phys_to_virt(intrq->next->ptr_low); /* Enqueue the last (spare) TRB and ring doorbell */ xhci_enqueue_trb(tr); xhci->dbreg[ep->dev->address] = ep_id; /* Reuse the current buffer for the next spare TRB */ xhci_clear_trb(tr->cur, tr->pcs); tr->cur->ptr_low = virt_to_phys(reqdata); tr->cur->ptr_high = 0; TRB_SET(TL, tr->cur, intrq->size); TRB_SET(TT, tr->cur, TRB_NORMAL); TRB_SET(ISP, tr->cur, 1); TRB_SET(IOC, tr->cur, 1); /* Check if anything was transferred */ const size_t read = TRB_GET(TL, intrq->next); if (!read) reqdata = NULL; else if (read < intrq->size) /* At least zero it, poll interface is rather limited */ memset(reqdata + read, 0x00, intrq->size - read); /* Advance the interrupt queue */ if (intrq->ready == intrq->next) /* This was last TRB being ready */ intrq->ready = NULL; intrq->next = xhci_next_trb(intrq->next, NULL); } return reqdata; }
/* create and hook-up an intr queue into device schedule */ static void * xhci_create_intr_queue(endpoint_t *const ep, const int reqsize, const int reqcount, const int reqtiming) { /* reqtiming: We ignore it and use the interval from the endpoint descriptor configured earlier. */ xhci_t *const xhci = XHCI_INST(ep->dev->controller); const int slot_id = ep->dev->address; const int ep_id = xhci_ep_id(ep); transfer_ring_t *const tr = xhci->dev[slot_id].transfer_rings[ep_id]; if (reqcount > (TRANSFER_RING_SIZE - 2)) { xhci_debug("reqcount is too high, at most %d supported\n", TRANSFER_RING_SIZE - 2); return NULL; } if (reqsize > 0x10000) { xhci_debug("reqsize is too large, at most 64KiB supported\n"); return NULL; } if (xhci->dev[slot_id].interrupt_queues[ep_id]) { xhci_debug("Only one interrupt queue per endpoint supported\n"); return NULL; } /* Allocate intrq structure and reqdata chunks */ intrq_t *const intrq = malloc(sizeof(*intrq)); if (!intrq) { xhci_debug("Out of memory\n"); return NULL; } int i; int pcs = tr->pcs; trb_t *cur = tr->cur; for (i = 0; i < reqcount; ++i) { if (TRB_GET(C, cur) == pcs) { xhci_debug("Not enough empty TRBs\n"); goto _free_return; } void *const reqdata = xhci_align(1, reqsize); if (!reqdata) { xhci_debug("Out of memory\n"); goto _free_return; } xhci_clear_trb(cur, pcs); cur->ptr_low = virt_to_phys(reqdata); cur->ptr_high = 0; TRB_SET(TL, cur, reqsize); TRB_SET(TT, cur, TRB_NORMAL); TRB_SET(ISP, cur, 1); TRB_SET(IOC, cur, 1); cur = xhci_next_trb(cur, &pcs); } intrq->size = reqsize; intrq->count = reqcount; intrq->next = tr->cur; intrq->ready = NULL; intrq->ep = ep; xhci->dev[slot_id].interrupt_queues[ep_id] = intrq; /* Now enqueue all the prepared TRBs but the last and ring the doorbell. */ for (i = 0; i < (reqcount - 1); ++i) xhci_enqueue_trb(tr); xhci->dbreg[slot_id] = ep_id; return intrq; _free_return: cur = tr->cur; for (--i; i >= 0; --i) { free(phys_to_virt(cur->ptr_low)); cur = xhci_next_trb(cur, NULL); } free(intrq); return NULL; }