?? dwc_otg_hcd_queue.c
字號(hào):
status = periodic_channel_available(_hcd); if (status) { DWC_NOTICE("%s: No host channel available for periodic " "transfer.\n", __func__); return status; } status = check_periodic_bandwidth(_hcd, _qh); if (status) { DWC_NOTICE("%s: Insufficient periodic bandwidth for " "periodic transfer.\n", __func__); return status; } status = check_max_xfer_size(_hcd, _qh); if (status) { DWC_NOTICE("%s: Channel max transfer size too small " "for periodic transfer.\n", __func__); return status; } /* Always start in the inactive schedule. */ list_add_tail(&_qh->qh_list_entry, &_hcd->periodic_sched_inactive); /* Reserve the periodic channel. */ _hcd->periodic_channels++; /* Update claimed usecs per (micro)frame. */ _hcd->periodic_usecs += _qh->usecs; /* Update average periodic bandwidth claimed and # periodic reqs for usbfs. */ hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_allocated += _qh->usecs / _qh->interval; if (_qh->ep_type == USB_ENDPOINT_XFER_INT) { hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_int_reqs++; DWC_DEBUGPL(DBG_HCD, "Scheduled intr: qh %p, usecs %d, period %d\n", _qh, _qh->usecs, _qh->interval); } else { hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_isoc_reqs++; DWC_DEBUGPL(DBG_HCD, "Scheduled isoc: qh %p, usecs %d, period %d\n", _qh, _qh->usecs, _qh->interval); } return status;}/** * This function adds a QH to either the non periodic or periodic schedule if * it is not already in the schedule. If the QH is already in the schedule, no * action is taken. * * @return 0 if successful, negative error code otherwise. */int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh){ unsigned long flags; int status = 0; local_irq_save(flags); if (!list_empty(&_qh->qh_list_entry)) { /* QH already in a schedule. */ goto done; } /* Add the new QH to the appropriate schedule */ if (dwc_qh_is_non_per(_qh)) { /* Always start in the inactive schedule. */ list_add_tail(&_qh->qh_list_entry, &_hcd->non_periodic_sched_inactive); } else { status = schedule_periodic(_hcd, _qh); } done: local_irq_restore(flags); return status;}/** * Removes an interrupt or isochronous transfer from the periodic schedule. * * @param _hcd The HCD state structure for the DWC OTG controller. * @param _qh QH for the periodic transfer. */static void deschedule_periodic(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh){ list_del_init(&_qh->qh_list_entry); /* Release the periodic channel reservation. */ _hcd->periodic_channels--; /* Update claimed usecs per (micro)frame. */ _hcd->periodic_usecs -= _qh->usecs; /* Update average periodic bandwidth claimed and # periodic reqs for usbfs. */ hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_allocated -= _qh->usecs / _qh->interval; if (_qh->ep_type == USB_ENDPOINT_XFER_INT) { hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_int_reqs--; DWC_DEBUGPL(DBG_HCD, "Descheduled intr: qh %p, usecs %d, period %d\n", _qh, _qh->usecs, _qh->interval); } else { hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_isoc_reqs--; DWC_DEBUGPL(DBG_HCD, "Descheduled isoc: qh %p, usecs %d, period %d\n", _qh, _qh->usecs, _qh->interval); }}/** * Removes a QH from either the non-periodic or periodic schedule. Memory is * not freed. * * @param[in] _hcd The HCD state structure. * @param[in] _qh QH to remove from schedule. */void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh){ unsigned long flags; local_irq_save(flags); if (list_empty(&_qh->qh_list_entry)) { /* QH is not in a schedule. */ goto done; } if (dwc_qh_is_non_per(_qh)) { if (_hcd->non_periodic_qh_ptr == &_qh->qh_list_entry) { _hcd->non_periodic_qh_ptr = _hcd->non_periodic_qh_ptr->next; } list_del_init(&_qh->qh_list_entry); } else { deschedule_periodic(_hcd, _qh); } done: local_irq_restore(flags);}/** * 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; local_irq_save(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); } } } local_irq_restore(flags);}#if 0/** Allocates memory for a QTD structure. * @return Returns the memory allocate or NULL on error. */static inline dwc_otg_qtd_t *dwc_otg_hcd_qtd_alloc (void){ return (dwc_otg_qtd_t *) kmalloc (sizeof(dwc_otg_qtd_t), GFP_KERNEL);}#endif/** * Initializes a QTD structure. * * @param[in] _qtd The QTD to initialize. * @param[in] _urb The URB to use for initialization. */static void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * _qtd, struct urb *_urb){ memset(_qtd, 0, sizeof(dwc_otg_qtd_t)); _qtd->urb = _urb; if (usb_pipecontrol(_urb->pipe)) { /* * The only time the QTD data toggle is used is on the data * phase of control transfers. This phase always starts with * DATA1. */ _qtd->data_toggle = DWC_OTG_HC_PID_DATA1; _qtd->control_phase = DWC_OTG_CONTROL_SETUP; } /* start split */ _qtd->complete_split = 0; _qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL; _qtd->isoc_split_offset = 0; /* Store the qtd ptr in the urb to reference what QTD. */ _urb->hcpriv = _qtd; return;}/** * This function allocates and initializes a QTD. * * @param[in] _urb The URB to create a QTD from. Each URB-QTD pair will end up * pointing to each other so each pair should have a unique correlation. * * @return Returns pointer to the newly allocated QTD, or NULL on error. */dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(struct urb *_urb){ dwc_otg_qtd_t *qtd;#if 0 qtd = dwc_otg_hcd_qtd_alloc();#else qtd = (dwc_otg_qtd_t *) kmalloc (sizeof(dwc_otg_qtd_t), GFP_KERNEL);#endif if (qtd == NULL) { return NULL; } dwc_otg_hcd_qtd_init(qtd, _urb); return qtd;}/** * This function adds a QTD to the QTD-list of a QH. It will find the correct * QH to place the QTD into. If it does not find a QH, then it will create a * new QH. If the QH to which the QTD is added is not currently scheduled, it * is placed into the proper schedule based on its EP type. * * @param[in] _qtd The QTD to add * @param[in] _dwc_otg_hcd The DWC HCD structure * * @return 0 if successful, negative error code otherwise. */int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * _qtd, dwc_otg_hcd_t * _dwc_otg_hcd){ struct usb_host_endpoint *ep; dwc_otg_qh_t *qh; unsigned long flags; int retval = 0; struct urb *urb = _qtd->urb; local_irq_save(flags); /* * Get the QH which holds the QTD-list to insert to. Create QH if it * doesn't exist. */ ep = dwc_urb_to_endpoint(urb); qh = (dwc_otg_qh_t *) ep->hcpriv; if (qh == NULL) { qh = dwc_otg_hcd_qh_create(_dwc_otg_hcd, urb); if (qh == NULL) { goto done; } ep->hcpriv = qh; } retval = dwc_otg_hcd_qh_add(_dwc_otg_hcd, qh); if (retval == 0) { list_add_tail(&_qtd->qtd_list_entry, &qh->qtd_list); }done: local_irq_restore(flags); return retval;}#endif /* DWC_DEVICE_ONLY */
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -