/** * 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; SPIN_LOCK_IRQSAVE(&dwc_otg_hcd->lock, 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: SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags); return retval; }
/** * Sets the final status of an URB and returns it to the device driver. Any * required cleanup of the URB is performed. */ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, dwc_otg_hcd_urb_t * dwc_otg_urb, uint32_t status) { struct urb *urb = (struct urb *)urb_handle; #ifdef DEBUG if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n", __func__, urb, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe), usb_pipein(urb->pipe) ? "IN" : "OUT", status); if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { int i; for (i = 0; i < urb->number_of_packets; i++) { DWC_PRINTF(" ISO Desc %d status: %d\n", i, urb->iso_frame_desc[i].status); } } } #endif urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb); /* Convert status value. */ switch (status) { case -DWC_E_PROTOCOL: status = -EPROTO; break; case -DWC_E_IN_PROGRESS: status = -EINPROGRESS; break; case -DWC_E_PIPE: status = -EPIPE; break; case -DWC_E_IO: status = -EIO; break; case -DWC_E_TIMEOUT: status = -ETIMEDOUT; break; default: if (status) { /* alan.K * DWC_OTG IP don't know this status, so assumed to be a DWC_E_PROTOCOL. */ DWC_WARN("Unknown urb status %d, but assumed to be an EPROTO\n", status); status = -EPROTO; } } if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { int i; urb->error_count = dwc_otg_hcd_urb_get_error_count(dwc_otg_urb); for (i = 0; i < urb->number_of_packets; ++i) { urb->iso_frame_desc[i].actual_length = dwc_otg_hcd_urb_get_iso_desc_actual_length (dwc_otg_urb, i); urb->iso_frame_desc[i].status = dwc_otg_hcd_urb_get_iso_desc_actual_length (dwc_otg_urb, i); } } urb->status = status; urb->hcpriv = NULL; if (!status) { if ((urb->transfer_flags & URB_SHORT_NOT_OK) && (urb->actual_length < urb->transfer_buffer_length)) { urb->status = -EREMOTEIO; } } if ((usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) || (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) { struct usb_host_endpoint *ep = dwc_urb_to_endpoint(urb); if (ep) { free_bus_bandwidth(dwc_otg_hcd_to_hcd(hcd), dwc_otg_hcd_get_ep_bandwidth(hcd, ep->hcpriv), urb); } } dwc_free(dwc_otg_urb); usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status); return 0; }