u8 uhci_reactivate_urb(struct uhci_host *host, struct usb_request_block *urb, struct uhci_td_meta *tdm) { struct uhci_td_meta *nexttdm, *comptdm; /* update the frame number that indicates issued time */ URB_UHCI(urb)->frnum_issued = uhci_current_frame_number(host); spinlock_lock(&host->lock_hfl); /* update tdm chain */ comptdm = URB_UHCI(urb)->tdm_head; URB_UHCI(urb)->tdm_head = tdm; /* update qh->element */ URB_UHCI(urb)->qh->element =(phys32_t)tdm->td_phys; /* reactivate the urb */ urb->status = URB_STATUS_RUN; spinlock_unlock(&host->lock_hfl); /* clean up completed TDs */ while (comptdm != tdm) { comptdm->td->link = UHCI_TD_LINK_TE; free(comptdm->td); nexttdm = comptdm->next; free(comptdm); comptdm = nexttdm; } return urb->status; }
u8 uhci_check_urb_advance(struct usb_host *usbhc, struct usb_request_block *urb) { struct uhci_host *host = (struct uhci_host *)usbhc->private; u16 ucfn; ucfn = uhci_current_frame_number (host); return uhci_check_urb_advance_sub (host, ucfn, usbhc, urb); }
/** * @brief check urb advance * @param host struct uhci_host * @param urb struct usb_request_block * @param usbsts u16 */ static u8 check_urb_advance(struct uhci_host *host, struct usb_request_block *urb, u16 usbsts) { struct uhci_td_meta *tdm; phys32_t qh_element; int elapse, len; elapse = (uhci_current_frame_number(host) + UHCI_NUM_FRAMES - URB_UHCI(urb)->frnum_issued) & (UHCI_NUM_FRAMES - 1); if (!elapse) return urb->status; spinlock_lock(&host->lock_hfl); recheck: urb->actlen = 0; qh_element = URB_UHCI(urb)->qh->element; /* atomic */ /* count up actual length of input/output data */ for (tdm = URB_UHCI(urb)->tdm_head; tdm; tdm = tdm->next) { if (!is_active_td(tdm->td)) { len = UHCI_TD_STAT_ACTLEN(tdm->td); if (!is_setup_td(tdm->td)) urb->actlen += len; } if ((phys32_t)tdm->td_phys == qh_element) { urb->status = UHCI_TD_STAT_STATUS(tdm->td); break; } } if (is_terminate(qh_element)) { urb->status = URB_STATUS_ADVANCED; } else { /* double check */ if (qh_element != URB_UHCI(urb)->qh->element) goto recheck; if (!(urb->status & URB_STATUS_RUN) && !(urb->status & URB_STATUS_ERRORS) && (uhci_td_actlen(tdm->td) == uhci_td_maxlen(tdm->td)) && tdm->next && is_active(tdm->next->status_copy)) urb->status = URB_STATUS_RUN; } URB_UHCI(urb)->qh_element_copy = qh_element; spinlock_unlock(&host->lock_hfl); return urb->status; }
/** * @brief chek_advance * @param host struct uhci_host */ int uhci_check_advance(struct usb_host *usbhc) { struct uhci_host *host = (struct uhci_host *)usbhc->private; struct usb_request_block *urb, *nexturb; int advance = 0; int ucfn = -1; if (cmpxchgl(&host->incheck, 0U, 1U)) return 0; #if 0 in16(host->iobase + UHCI_REG_USBSTS, &usbsts); if (usbsts) dprintft(2, "%04x: %s: usbsts = %04x\n", host->iobase, __FUNCTION__, usbsts); #endif /* 0 */ spinlock_lock(&host->lock_hfl); recheck: for (urb = LIST4_HEAD (host->inproc_urbs, list); urb; urb = nexturb) { urb->prevent_del = true; spinlock_unlock(&host->lock_hfl); /* update urb->status */ if (urb->status == URB_STATUS_RUN) { if (ucfn < 0) ucfn = uhci_current_frame_number (host); uhci_check_urb_advance_sub (host, ucfn, host->hc, urb); } switch (urb->status) { default: /* errors */ dprintft(2, "%04x: %s: got some errors(%s) " "for urb(%p).\n", host->iobase, __FUNCTION__, uhci_error_status_string(urb->status), urb); /* through */ case URB_STATUS_ADVANCED: if (urb->callback) (urb->callback) (host->hc, urb, urb->cb_arg); advance++; break; case URB_STATUS_NAK: dprintft(2, "%04x: %s: got an NAK for urb(%p).\n", host->iobase, __FUNCTION__, urb); urb->status = URB_STATUS_RUN; case URB_STATUS_RUN: case URB_STATUS_FINALIZED: case URB_STATUS_UNLINKED: break; } spinlock_lock(&host->lock_hfl); nexturb = LIST4_NEXT (urb, list); urb->prevent_del = false; if (urb->deferred_del) { urb->deferred_del = false; spinlock_unlock(&host->lock_hfl); uhci_deactivate_urb(host->hc, urb); spinlock_lock(&host->lock_hfl); goto recheck; } } spinlock_unlock(&host->lock_hfl); #if 0 if (advance) { dprintft(3, "%s: USBSTS register cleared.\n", __FUNCTION__); out16(host->iobase + UHCI_REG_USBSTS, usbsts); } #endif host->incheck = 0U; return advance; }
/** * @brief activate urb * @param host struct uhci_host *host * @param urb struct usb_request_block */ u8 uhci_activate_urb(struct uhci_host *host, struct usb_request_block *urb) { u8 status, type; int n; type = (urb->endpoint) ? USB_EP_TRANSTYPE(urb->endpoint) : USB_ENDPOINT_TYPE_CONTROL; spinlock_lock(&host->lock_hfl); switch (type) { case USB_ENDPOINT_TYPE_INTERRUPT: n = __ffs(urb->endpoint->bInterval | (1 << (UHCI_NUM_SKELTYPES - 1))); /* MEMO: a new interrupt urb must be inserted just after a skelton anytime. */ urb->link_prev = host->host_skelton[n]; if (host->host_skelton[n] == host->tailurb[URB_TAIL_CONTROL]) host->tailurb[URB_TAIL_CONTROL] = urb; if (host->host_skelton[n] == host->tailurb[URB_TAIL_BULK]) host->tailurb[URB_TAIL_BULK] = urb; break; case USB_ENDPOINT_TYPE_CONTROL: urb->link_prev = host->tailurb[URB_TAIL_CONTROL]; if (host->tailurb[URB_TAIL_CONTROL] == host->tailurb[URB_TAIL_BULK]) host->tailurb[URB_TAIL_BULK] = urb; host->tailurb[URB_TAIL_CONTROL] = urb; break; case USB_ENDPOINT_TYPE_BULK: urb->link_prev = host->tailurb[URB_TAIL_BULK]; host->tailurb[URB_TAIL_BULK] = urb; break; case USB_ENDPOINT_TYPE_ISOCHRONOUS: default: printf("%s: transfer type(%02x) unsupported.\n", __FUNCTION__, type); status = urb->status; return status; } /* initialize qh_element_copy for detecting advance after NAK */ URB_UHCI(urb)->qh_element_copy = URB_UHCI(urb)->qh->element; /* urb link */ urb->link_next = urb->link_prev->link_next; urb->link_prev->link_next = urb; if (urb->link_next) { /* make a backward pointer */ urb->link_next->link_prev = urb; } else if (type == USB_ENDPOINT_TYPE_BULK) { if (host->fsbr) { dprintft(2, "%04x: %s: append it to the " "FSBR loopback.\n", host->iobase, __FUNCTION__); host->fsbr_loop_tail = urb; } else { dprintft(2, "%04x: %s: make a FSBR loopback.\n", host->iobase, __FUNCTION__); host->fsbr = 1; host->fsbr_loop_head = urb; host->fsbr_loop_tail = urb; } } /* record the current frame number */ URB_UHCI(urb)->frnum_issued = uhci_current_frame_number(host); /* qh link */ URB_UHCI(urb)->qh->link = URB_UHCI(urb->link_prev)->qh->link; if (host->fsbr_loop_tail) URB_UHCI(host->fsbr_loop_tail)->qh->link = (phys32_t) URB_UHCI(host->fsbr_loop_head)->qh_phys | UHCI_QH_LINK_QH; URB_UHCI(urb->link_prev)->qh->link = URB_UHCI(urb)->qh_phys | UHCI_QH_LINK_QH; urb->status = URB_STATUS_RUN; dprintft(3, "%s: The urb link is %p <- %p -> %p.\n", __FUNCTION__, urb->link_prev, urb, urb->link_next); status = urb->status; LIST4_PUSH (host->inproc_urbs, list, urb); spinlock_unlock(&host->lock_hfl); return status; }