/** * Deactivates a QH. For non-periodic QHs, removes the QH from the active * non-periodic schedule. The QH is added to the inactive non-periodic * schedule if any QTDs are still attached to the QH. * * For periodic QHs, the QH is removed from the periodic queued schedule. If * there are any QTDs still attached to the QH, the QH is added to either the * periodic inactive schedule or the periodic ready schedule and its next * scheduled frame is calculated. The QH is placed in the ready schedule if * the scheduled frame has been reached already. Otherwise it's placed in the * inactive schedule. If there are no QTDs attached to the QH, the QH is * completely removed from the periodic schedule. */ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, int sched_next_periodic_split) { unsigned long flags; SPIN_LOCK_IRQSAVE(&hcd->lock, flags); if (dwc_qh_is_non_per(qh)) { dwc_otg_hcd_qh_remove(hcd, qh); if (!list_empty(&qh->qtd_list)) { /* Add back to inactive non-periodic schedule. */ dwc_otg_hcd_qh_add(hcd, qh); } } else { uint16_t frame_number = dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(hcd)); if (qh->do_split) { /* Schedule the next continuing periodic split transfer */ if (sched_next_periodic_split) { qh->sched_frame = frame_number; if (dwc_frame_num_le(frame_number, dwc_frame_num_inc(qh->start_split_frame, 1))) { /* * Allow one frame to elapse after start * split microframe before scheduling * complete split, but DONT if we are * doing the next start split in the * same frame for an ISOC out. */ if ((qh->ep_type != USB_ENDPOINT_XFER_ISOC) || (qh->ep_is_in != 0)) { qh->sched_frame = dwc_frame_num_inc(qh->sched_frame, 1); } } } else { qh->sched_frame = dwc_frame_num_inc(qh->start_split_frame, qh->interval); if (dwc_frame_num_le(qh->sched_frame, frame_number)) { qh->sched_frame = frame_number; } qh->sched_frame |= 0x7; qh->start_split_frame = qh->sched_frame; } } else { qh->sched_frame = dwc_frame_num_inc(qh->sched_frame, qh->interval); if (dwc_frame_num_le(qh->sched_frame, frame_number)) { qh->sched_frame = frame_number; } } if (list_empty(&qh->qtd_list)) { dwc_otg_hcd_qh_remove(hcd, qh); } else { /* * Remove from periodic_sched_queued and move to * appropriate queue. */ if (qh->sched_frame == frame_number) { list_move(&qh->qh_list_entry, &hcd->periodic_sched_ready); } else { list_move(&qh->qh_list_entry, &hcd->periodic_sched_inactive); } } } SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags); }
/** Returns the current frame number. */ static int get_frame_number(struct usb_hcd *hcd) { dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); return dwc_otg_hcd_get_frame_number(dwc_otg_hcd); }