/** * Checks that the max transfer size allowed in a host channel is large enough * to handle the maximum data transfer in a single (micro)frame for a periodic * transfer. * * @param hcd The HCD state structure for the DWC OTG controller. * @param qh QH for a periodic endpoint. * * @return 0 if successful, negative error code otherwise. */ static int check_max_xfer_size(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) { int status; uint32_t max_xfer_size; uint32_t max_channel_xfer_size; status = 0; max_xfer_size = dwc_max_packet(qh->maxp) * dwc_hb_mult(qh->maxp); max_channel_xfer_size = hcd->core_if->core_params->max_transfer_size; if (max_xfer_size > max_channel_xfer_size) { DWC_NOTICE("%s: Periodic xfer length %d > " "max xfer length for channel %d\n", __func__, max_xfer_size, max_channel_xfer_size); status = -ENOSPC; } return status; }
/** Initializes a QH structure. * * @param[in] _hcd The HCD state structure for the DWC OTG controller. * @param[in] _qh The QH to init. * @param[in] _urb Holds the information about the device/endpoint that we need * to initialize the QH. */ #define SCHEDULE_SLOP 10 #define SCHEDULE_SPLIT_SLOP 10 /* 1 == 125us, 10 -> 1.25ms, 20 -> 2.5ms, */ void dwc_otg_hcd_qh_init(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh, struct urb *_urb) { memset(_qh, 0, sizeof(dwc_otg_qh_t)); /* Initialize QH */ switch (usb_pipetype(_urb->pipe)) { case PIPE_CONTROL: _qh->ep_type = USB_ENDPOINT_XFER_CONTROL; break; case PIPE_BULK: _qh->ep_type = USB_ENDPOINT_XFER_BULK; break; case PIPE_ISOCHRONOUS: _qh->ep_type = USB_ENDPOINT_XFER_ISOC; break; case PIPE_INTERRUPT: _qh->ep_type = USB_ENDPOINT_XFER_INT; break; } _qh->ep_is_in = usb_pipein(_urb->pipe) ? 1 : 0; _qh->data_toggle = DWC_OTG_HC_PID_DATA0; _qh->maxp = usb_maxpacket(_urb->dev, _urb->pipe, !(usb_pipein(_urb->pipe))); INIT_LIST_HEAD(&_qh->qtd_list); INIT_LIST_HEAD(&_qh->qh_list_entry); _qh->channel = NULL; /* FS/LS Enpoint on HS Hub * NOT virtual root hub */ _qh->do_split = 0; if (((_urb->dev->speed == USB_SPEED_LOW) || (_urb->dev->speed == USB_SPEED_FULL)) && (_urb->dev->tt) && (_urb->dev->tt->hub) && (_urb->dev->tt->hub->devnum != 1)) { DWC_DEBUGPL(DBG_HCD, "QH init: EP %d: TT found at hub addr %d, for port %d\n", usb_pipeendpoint(_urb->pipe), _urb->dev->tt->hub->devnum, _urb->dev->ttport); _qh->do_split = 1; } if (_qh->ep_type == USB_ENDPOINT_XFER_INT || _qh->ep_type == USB_ENDPOINT_XFER_ISOC) { /* Compute scheduling parameters once and save them. */ hprt0_data_t hprt; /** @todo Account for split transfers in the bus time. */ int bytecount = dwc_hb_mult(_qh->maxp) * dwc_max_packet(_qh->maxp); int usecs = /*FIXME: hardcode to highspeed, to fix Full/Low speed device via Hub*/ usb_calc_bus_time(/*_urb->dev->speed*/USB_SPEED_HIGH, usb_pipein(_urb->pipe), (_qh->ep_type == USB_ENDPOINT_XFER_ISOC), bytecount); _qh->usecs = NS_TO_US(usecs); /* Start in a slightly future (micro)frame. */ _qh->sched_frame = dwc_frame_num_inc(_hcd->frame_number, SCHEDULE_SLOP); _qh->interval = _urb->interval; #if 0 /* Increase interrupt polling rate for debugging. */ if (_qh->ep_type == USB_ENDPOINT_XFER_INT) { _qh->interval = 8; } #endif hprt.d32 = dwc_read_reg32(_hcd->core_if->host_if->hprt0); if ((hprt.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) && ((_urb->dev->speed == USB_SPEED_LOW) || (_urb->dev->speed == USB_SPEED_FULL))) { _qh->interval *= 8; _qh->sched_frame |= 0x7; _qh->start_split_frame = _qh->sched_frame; } }else{ if(_qh->do_split){ _qh->interval = SCHEDULE_SPLIT_SLOP; _qh->sched_frame = dwc_frame_num_inc(_hcd->frame_number, _qh->interval); }; } DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD QH Initialized\n"); DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - qh = %p\n", _qh); DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Device Address = %d\n", _urb->dev->devnum); DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Endpoint %d, %s\n", usb_pipeendpoint(_urb->pipe), usb_pipein(_urb->pipe) == USB_DIR_IN ? "IN" : "OUT"); DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Speed = %s\n", ( { char *speed; switch(_urb->dev->speed) { case USB_SPEED_LOW: speed = "low"; break; case USB_SPEED_FULL: speed = "full"; break; case USB_SPEED_HIGH: speed = "high"; break; default: speed = "?"; break;}; speed;})) ;
void dwc_otg_hcd_qh_init(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, struct urb *urb) { char *speed, *type; memset (qh, 0, sizeof (dwc_otg_qh_t)); /* Initialize QH */ switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: qh->ep_type = USB_ENDPOINT_XFER_CONTROL; break; case PIPE_BULK: qh->ep_type = USB_ENDPOINT_XFER_BULK; break; case PIPE_ISOCHRONOUS: qh->ep_type = USB_ENDPOINT_XFER_ISOC; break; case PIPE_INTERRUPT: qh->ep_type = USB_ENDPOINT_XFER_INT; break; } qh->ep_is_in = usb_pipein(urb->pipe) ? 1 : 0; qh->data_toggle = DWC_OTG_HC_PID_DATA0; qh->maxp = usb_maxpacket(urb->dev, urb->pipe, !(usb_pipein(urb->pipe))); INIT_LIST_HEAD(&qh->qtd_list); INIT_LIST_HEAD(&qh->qh_list_entry); qh->channel = NULL; /* FS/LS Enpoint on HS Hub * NOT virtual root hub */ qh->do_split = 0; if (((urb->dev->speed == USB_SPEED_LOW) || (urb->dev->speed == USB_SPEED_FULL)) && (urb->dev->tt) && (urb->dev->tt->hub) && (urb->dev->tt->hub->devnum != 1)) { DWC_DEBUGPL(DBG_HCD, "QH init: EP %d: TT found at hub addr %d, for port %d\n", usb_pipeendpoint(urb->pipe), urb->dev->tt->hub->devnum, urb->dev->ttport); qh->do_split = 1; } if (qh->ep_type == USB_ENDPOINT_XFER_INT || qh->ep_type == USB_ENDPOINT_XFER_ISOC) { /* Compute scheduling parameters once and save them. */ hprt0_data_t hprt; /** @todo Account for split transfers in the bus time. */ int bytecount = dwc_hb_mult(qh->maxp) * dwc_max_packet(qh->maxp); /* FIXME: work-around patch by Steven */ qh->usecs = NS_TO_US(usb_calc_bus_time(urb->dev->speed, usb_pipein(urb->pipe), (qh->ep_type == USB_ENDPOINT_XFER_ISOC), bytecount)); /* Start in a slightly future (micro)frame. */ qh->sched_frame = dwc_frame_num_inc(hcd->frame_number, SCHEDULE_SLOP); qh->interval = urb->interval; #if 0 /* Increase interrupt polling rate for debugging. */ if (qh->ep_type == USB_ENDPOINT_XFER_INT) { qh->interval = 8; } #endif hprt.d32 = dwc_read_reg32(hcd->core_if->host_if->hprt0); if ((hprt.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) && ((urb->dev->speed == USB_SPEED_LOW) || (urb->dev->speed == USB_SPEED_FULL))) { qh->interval *= 8; qh->sched_frame |= 0x7; qh->start_split_frame = qh->sched_frame; } } DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD QH Initialized\n"); DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - qh = %p\n", qh); DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Device Address = %d\n", urb->dev->devnum); DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Endpoint %d, %s\n", usb_pipeendpoint(urb->pipe), usb_pipein(urb->pipe) == USB_DIR_IN ? "IN" : "OUT"); switch(urb->dev->speed) { case USB_SPEED_LOW: speed = "low"; break; case USB_SPEED_FULL: speed = "full"; break; case USB_SPEED_HIGH: speed = "high"; break; default: speed = "?"; break; } DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Speed = %s\n", speed); switch (qh->ep_type) { case USB_ENDPOINT_XFER_ISOC: type = "isochronous"; break; case USB_ENDPOINT_XFER_INT: type = "interrupt"; break; case USB_ENDPOINT_XFER_CONTROL: type = "control"; break; case USB_ENDPOINT_XFER_BULK: type = "bulk"; break; default: type = "?"; break; } DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Type = %s\n",type); #ifdef DEBUG if (qh->ep_type == USB_ENDPOINT_XFER_INT) { DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - usecs = %d\n", qh->usecs); DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - interval = %d\n", qh->interval); } #endif qh->dw_align_buf = NULL; return; }