/** * This function unregisters a gadget driver * * @param driver The driver being unregistered */ static int dwc_usb_gadget_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { //DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, _driver); if (gadget_wrapper == 0) { DWC_DEBUGPL(DBG_ANY, "%s Return(%d): s_pcd==0\n", __func__, -ENODEV); return -ENODEV; } if (driver == 0 || driver != gadget_wrapper->driver) { DWC_DEBUGPL(DBG_ANY, "%s Return(%d): driver?\n", __func__, -EINVAL); return -EINVAL; } gadget_wrapper->driver = 0; gadget_wrapper->enabled = 0; DWC_DEBUGPL(DBG_ANY, "unregistered driver '%s'\n", driver->driver.name); return 0; }
/** * This function unregisters a gadget driver * * @param driver The driver being unregistered */ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { //DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, _driver); if (gadget_wrapper == 0) { DWC_DEBUGPL(DBG_ANY, "%s Return(%d): s_pcd==0\n", __func__, -ENODEV); return -ENODEV; } if (driver == 0 || driver != gadget_wrapper->driver) { DWC_DEBUGPL(DBG_ANY, "%s Return(%d): driver?\n", __func__, -EINVAL); return -EINVAL; } driver->unbind(&gadget_wrapper->gadget); gadget_wrapper->driver = 0; DWC_DEBUGPL(DBG_ANY, "unregistered driver \n"); return 0; }
/** * This function frees a request object. * * @param ep The endpoint associated with the request * @param req The request being freed */ static void dwc_otg_pcd_free_request(struct usb_ep *ep, struct usb_request *req) { DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, ep, req); if (0 == ep || 0 == req) { DWC_WARN("%s() %s\n", __func__, "Invalid ep or req argument!\n"); return; } kfree(req); }
/** Initializes the DWC_otg controller and its root hub and prepares it for host * mode operation. Activates the root port. Returns 0 on success and a negative * error code on failure. */ int hcd_start(struct usb_hcd *hcd) { dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); struct usb_bus *bus; DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD START\n"); bus = hcd_to_bus(hcd); hcd->state = HC_STATE_RUNNING; if (dwc_otg_hcd_start(dwc_otg_hcd, &hcd_fops)) { return 0; } /* Initialize and connect root hub if one is not already attached */ if (bus->root_hub) { DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Has Root Hub\n"); /* Inform the HUB driver to resume. */ usb_hcd_resume_root_hub(hcd); } return 0; }
/** *Gets the USB Frame number of the last SOF. */ static int get_frame_number(struct usb_gadget *gadget) { struct gadget_wrapper *d; DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget); if (gadget == 0) { return -ENODEV; } d = container_of(gadget, struct gadget_wrapper, gadget); return dwc_otg_pcd_get_frame_number(d->pcd); }
/** * This function completes a request. It call's the request call back. */ void request_done(dwc_otg_pcd_ep_t * _ep, dwc_otg_pcd_request_t * _req, int _status) { unsigned stopped = _ep->stopped; dwc_otg_core_if_t *core_if = GET_CORE_IF(_ep->pcd); DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _ep); if(_ep->dwc_ep.num && _ep->dwc_ep.is_in) list_del_init(&_req->pcd_queue); list_del_init(&_req->queue); if (_req->req.status == -EINPROGRESS) { _req->req.status = _status; } else { _status = _req->req.status; } /* don't modify queue heads during completion callback */ _ep->stopped = 1; SPIN_UNLOCK(&_ep->pcd->lock); _req->req.complete(&_ep->ep, &_req->req); SPIN_LOCK(&_ep->pcd->lock); if (_ep->pcd->request_pending > 0) { --_ep->pcd->request_pending; } _ep->stopped = stopped; if(_ep->dwc_ep.is_in && _ep->dwc_ep.num){ DWC_DEBUGPL(DBG_PCDV, "ep%d,len=%d\n",_ep->dwc_ep.num,_req->req.actual); _ep->pcd->ep_in_sync = 0; } if(core_if->dma_enable) dwc_otg_pcd_dma_unmap(&_ep->dwc_ep); }
/** * 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); } }
/** * Initiates Session Request Protocol (SRP) to wakeup the host if no * session is in progress. If a session is already in progress, but * the device is suspended, remote wakeup signaling is started. * */ static int wakeup(struct usb_gadget *gadget) { struct gadget_wrapper *d; DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget); if (gadget == 0) { return -ENODEV; } else { d = container_of(gadget, struct gadget_wrapper, gadget); } dwc_otg_pcd_wakeup(d->pcd); return 0; }
/** * This function is used to submit an I/O Request to an EP. * * - When the request completes the request's completion callback * is called to return the request to the driver. * - An EP, except control EPs, may have multiple requests * pending. * - Once submitted the request cannot be examined or modified. * - Each request is turned into one or more packets. * - A BULK EP can queue any amount of data; the transfer is * packetized. * - Zero length Packets are specified with the request 'zero' * flag. */ static int ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req, gfp_t gfp_flags) { dwc_otg_pcd_t *pcd; int retval = 0; //trace_printk("(%p,%p,%d)\n", // usb_ep, usb_req, gfp_flags); if (!usb_req || !usb_req->complete || (!gadget_wrapper->gadget.sg_supported && !usb_req->buf)) { DWC_WARN("bad params\n"); return -EINVAL; } if (!usb_ep) { DWC_WARN("bad ep\n"); return -EINVAL; } pcd = gadget_wrapper->pcd; if (!gadget_wrapper->driver || gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", gadget_wrapper->gadget.speed); DWC_WARN("bogus device state\n"); return -ESHUTDOWN; } //trace_printk( "%s queue req %p, len %d buf %p\n", // usb_ep->name, usb_req, usb_req->length, usb_req->buf); usb_req->status = -EINPROGRESS; usb_req->actual = 0; retval = dwc_otg_pcd_ep_queue(pcd, usb_ep, usb_req->buf, usb_req->dma/*dma_addr*/, usb_req->length, usb_req->zero, usb_req->num_sgs, usb_req->sg, usb_req, gfp_flags == GFP_ATOMIC ? 1 : 0); if (retval) { pr_err("%s, cannot enqueue a renquest, err :%d\n", __func__, retval); pr_info( "%s queue req %p, len %d buf %p\n", usb_ep->name, usb_req, usb_req->length, usb_req->buf); return -EINVAL; } return 0; }
/** * This function frees a request object. * * @param _ep The endpoint associated with the request * @param _req The request being freed */ static void dwc_otg_pcd_free_request(struct usb_ep *_ep, struct usb_request *_req) { dwc_otg_pcd_request_t *req; DWC_DEBUGPL(DBG_PCDV,"%s(%p,%p)\n", __func__, _ep, _req); if (0 == _ep || 0 == _req) { DWC_WARN("%s() %s\n", __func__, "Invalid ep or req argument!\n"); return; } req = container_of(_req, dwc_otg_pcd_request_t, req); kfree(req); }
/** * This function is called when an EP is disabled due to disconnect or * change in configuration. Any pending requests will terminate with a * status of -ESHUTDOWN. * * This function modifies the dwc_otg_ep_t data structure for this EP, * and then calls dwc_otg_ep_deactivate. */ static int dwc_otg_pcd_ep_disable(struct usb_ep *_ep) { dwc_otg_pcd_ep_t * ep; unsigned long flags; DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _ep); ep = container_of(_ep, dwc_otg_pcd_ep_t, ep); if (!_ep || !ep->desc) { DWC_DEBUGPL(DBG_PCD, "%s, %s not enabled\n", __func__, _ep ? ep->ep.name : NULL); return -EINVAL; } SPIN_LOCK_IRQSAVE(&ep->pcd->lock, flags); request_nuke(ep); dwc_otg_ep_deactivate(GET_CORE_IF(ep->pcd), &ep->dwc_ep); ep->desc = 0; ep->stopped = 1; if (ep->dwc_ep.is_in) { release_perio_tx_fifo(GET_CORE_IF(ep->pcd),ep->dwc_ep.tx_fifo_num); release_tx_fifo(GET_CORE_IF(ep->pcd), ep->dwc_ep.tx_fifo_num); } SPIN_UNLOCK_IRQRESTORE(&ep->pcd->lock, flags); DWC_DEBUGPL(DBG_PCD, "%s disabled\n", _ep->name); return 0; }
/** * This function completes a request. It call's the request call back. */ void request_done(dwc_otg_pcd_ep_t * _ep, dwc_otg_pcd_request_t * _req, int _status) { unsigned stopped = _ep->stopped; DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _ep); if (_req->mapped) { dma_unmap_single(_ep->pcd->gadget.dev.parent, _req->req.dma, _req->req.length, _ep->dwc_ep.is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); _req->req.dma = DMA_ADDR_INVALID; _req->mapped = 0; } else dma_sync_single_for_cpu(_ep->pcd->gadget.dev.parent, _req->req.dma, _req->req.length, _ep->dwc_ep.is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); list_del_init(&_req->queue); if (_req->req.status == -EINPROGRESS) { _req->req.status = _status; } else { _status = _req->req.status; } /* don't modify queue heads during completion callback */ _ep->stopped = 1; SPIN_UNLOCK(&_ep->pcd->lock); _req->req.complete(&_ep->ep, &_req->req); SPIN_LOCK(&_ep->pcd->lock); if (_ep->pcd->request_pending > 0) { --_ep->pcd->request_pending; } _ep->stopped = stopped; #ifdef CONFIG_405EZ /* * Added-sr: 2007-07-26 * * Finally, when the current request is done, mark this endpoint * as not active, so that new requests can be processed. */ _ep->dwc_ep.active = 0; #endif }
void pcd_remove( struct dwc_otg_device *_dev ) { dwc_otg_device_t *otg_dev = _dev; dwc_otg_pcd_t *pcd = otg_dev->pcd; DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev); /* * Free the IRQ */ //free_irq(_dev->irq, pcd); dwc_otg_pcd_remove(otg_dev->pcd); free_wrapper(gadget_wrapper); otg_dev->pcd = 0; }
/** * This function frees an I/O buffer that was allocated by alloc_buffer. * * @param _ep the endpoint associated with the buffer * @param _buf address of the buffer * @param _dma The buffer's DMA address * @param _bytes The number of bytes of the buffer */ static void dwc_otg_pcd_free_buffer(struct usb_ep *_ep, void *_buf, dma_addr_t _dma, unsigned _bytes) { dwc_otg_pcd_ep_t *ep; dwc_otg_pcd_t *pcd = 0; ep = container_of(_ep, dwc_otg_pcd_ep_t, ep); pcd = ep->pcd; DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p,%0x,%d)\n", __func__, _ep, _buf, _dma, _bytes); if (GET_CORE_IF(pcd)->dma_enable) { dma_free_coherent(NULL, _bytes, _buf, _dma); } else { kfree(_buf); } }
/** * This function cancels an I/O request from an EP. */ static int ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) { DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, usb_ep, usb_req); if (!usb_ep || !usb_req) { DWC_WARN("bad argument\n"); return -EINVAL; } if (!gadget_wrapper->driver || gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { DWC_WARN("bogus device state\n"); return -ESHUTDOWN; } if (dwc_otg_pcd_ep_dequeue(gadget_wrapper->pcd, usb_ep, usb_req)) { return -EINVAL; } return 0; }
/** * This function allocates a request object to use with the specified * endpoint. * * @param _ep The endpoint to be used with with the request * @param _gfp_flags the GFP_* flags to use. */ static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *_ep, gfp_t _gfp_flags) { dwc_otg_pcd_request_t * req; DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d)\n", __func__, _ep, _gfp_flags); if (0 == _ep) { DWC_WARN("%s() %s\n", __func__, "Invalid EP!\n"); return 0; } req = kmalloc(sizeof(dwc_otg_pcd_request_t), _gfp_flags); if (0 == req) { DWC_WARN("%s() %s\n", __func__,"request allocation failed!\n"); return 0; } memset(req, 0, sizeof(dwc_otg_pcd_request_t)); req->req.dma = DMA_ADDR_INVALID; INIT_LIST_HEAD(&req->queue); return &req->req; }
/** * usb_ep_set_halt stalls an endpoint. * * usb_ep_clear_halt clears an endpoint halt and resets its data * toggle. * * Both of these functions are implemented with the same underlying * function. The behavior depends on the value argument. * * @param[in] usb_ep the Endpoint to halt or clear halt. * @param[in] value * - 0 means clear_halt. * - 1 means set_halt, * - 2 means clear stall lock flag. * - 3 means set stall lock flag. */ static int ep_halt(struct usb_ep *usb_ep, int value) { int retval = 0; DWC_DEBUGPL(DBG_PCD, "HALT %s %d\n", usb_ep->name, value); if (!usb_ep) { DWC_WARN("bad ep\n"); return -EINVAL; } retval = dwc_otg_pcd_ep_halt(gadget_wrapper->pcd, usb_ep, value); if (retval == -DWC_E_AGAIN) { return -EAGAIN; } else if (retval) { retval = -EINVAL; } return retval; }
/** * Cleanup the PCD. */ void pcd_remove(struct platform_device *_dev) { dwc_otg_device_t *otg_dev = platform_get_otgdata(_dev); dwc_otg_pcd_t *pcd = otg_dev->pcd; int irq; DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev); /* * Free the IRQ */ irq = platform_get_irq(_dev, 0); if(irq >= 0) { free_irq(irq, pcd); } dwc_otg_pcd_remove(otg_dev->pcd); free_wrapper(gadget_wrapper); otg_dev->pcd = 0; }
/** * ep_wedge: sets the halt feature and ignores clear requests * * @usb_ep: the endpoint being wedged * * Use this to stall an endpoint and ignore CLEAR_FEATURE(HALT_ENDPOINT) * requests. If the gadget driver clears the halt status, it will * automatically unwedge the endpoint. * * Returns zero on success, else negative errno. * * Check usb_ep_set_wedge() at "usb_gadget.h" for details */ static int ep_wedge(struct usb_ep *usb_ep) { int retval = 0; DWC_DEBUGPL(DBG_PCD, "WEDGE %s\n", usb_ep->name); if (!usb_ep) { DWC_WARN("bad ep\n"); return -EINVAL; } retval = dwc_otg_pcd_ep_wedge(gadget_wrapper->pcd, usb_ep); if (retval == -DWC_E_AGAIN) { retval = -EAGAIN; } else if (retval) { retval = -EINVAL; } return retval; }
/** * This function allocates a request object to use with the specified * endpoint. * * @param ep The endpoint to be used with with the request * @param gfp_flags the GFP_* flags to use. */ static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) { struct usb_request *usb_req; DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d)\n", __func__, ep, gfp_flags); if (0 == ep) { DWC_WARN("%s() %s\n", __func__, "Invalid EP!\n"); return 0; } usb_req = kmalloc(sizeof(*usb_req), gfp_flags); if (0 == usb_req) { DWC_WARN("%s() %s\n", __func__, "request allocation failed!\n"); return 0; } memset(usb_req, 0, sizeof(*usb_req)); usb_req->dma = DWC_INVALID_DMA_ADDR; return usb_req; }
/** * This function stops ISO EP Periodic Data Transfer. */ static int iso_ep_stop(struct usb_ep *usb_ep, struct usb_iso_request *req) { int retval = 0; if (!usb_ep) { DWC_WARN("bad ep\n"); } if (!gadget_wrapper->driver || gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", gadget_wrapper->gadget.speed); DWC_WARN("bogus device state\n"); } dwc_otg_pcd_iso_ep_stop(gadget_wrapper->pcd, usb_ep, req); if (retval) { retval = -EINVAL; } return retval; }
/** * This function is called by the Gadget Driver for each EP to be * configured for the current configuration (SET_CONFIGURATION). * * This function initializes the dwc_otg_ep_t data structure, and then * calls dwc_otg_ep_activate. */ static int ep_enable(struct usb_ep *usb_ep, const struct usb_endpoint_descriptor *ep_desc) { int retval; DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, usb_ep, ep_desc); if (!usb_ep || !ep_desc || ep_desc->bDescriptorType != USB_DT_ENDPOINT) { DWC_WARN("%s, bad ep or descriptor\n", __func__); return -EINVAL; } if (usb_ep == &gadget_wrapper->ep0) { DWC_WARN("%s, bad ep(0)\n", __func__); return -EINVAL; } /* Check FIFO size? */ if (!ep_desc->wMaxPacketSize) { DWC_WARN("%s, bad %s maxpacket\n", __func__, usb_ep->name); return -ERANGE; } if (!gadget_wrapper->driver || gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { DWC_WARN("%s, bogus device state\n", __func__); return -ESHUTDOWN; } retval = dwc_otg_pcd_ep_enable(gadget_wrapper->pcd, (const uint8_t *)ep_desc, (void *)usb_ep); if (retval) { DWC_WARN("dwc_otg_pcd_ep_enable failed\n"); return -EINVAL; } usb_ep->maxpacket = le16_to_cpu(ep_desc->wMaxPacketSize); return 0; }
static int _isoc_complete(dwc_otg_pcd_t * pcd, void *ep_handle, void *req_handle, int proc_buf_num) { int i, packet_count; struct usb_gadget_iso_packet_descriptor *iso_packet = 0; struct usb_iso_request *iso_req = req_handle; if (proc_buf_num) { iso_packet = iso_req->iso_packet_desc1; } else { iso_packet = iso_req->iso_packet_desc0; } packet_count = dwc_otg_pcd_get_iso_packet_count(pcd, ep_handle, req_handle); for (i = 0; i < packet_count; ++i) { int status; int actual; int offset; dwc_otg_pcd_get_iso_packet_params(pcd, ep_handle, req_handle, i, &status, &actual, &offset); switch (status) { case -DWC_E_NO_DATA: status = -ENODATA; break; default: if (status) { DWC_DEBUGPL(DBG_CIL,"unknown status in isoc packet\n"); } } iso_packet[i].status = status; iso_packet[i].offset = offset; iso_packet[i].actual_length = actual; } iso_req->status = 0; iso_req->process_buffer(ep_handle, iso_req); return 0; }
/** * This function allocates an I/O buffer to be used for a transfer * to/from the specified endpoint. * * @param _ep The endpoint to be used with with the request * @param _bytes The desired number of bytes for the buffer * @param _dma Pointer to the buffer's DMA address; must be valid * @param _gfp_flags the GFP_* flags to use. * @return address of a new buffer or null is buffer could not be allocated. */ static void *dwc_otg_pcd_alloc_buffer(struct usb_ep *_ep, unsigned _bytes, dma_addr_t *_dma, int _gfp_flags) { void *buf; dwc_otg_pcd_ep_t *ep; dwc_otg_pcd_t *pcd = 0; ep = container_of(_ep, dwc_otg_pcd_ep_t, ep); pcd = ep->pcd; DWC_DEBUGPL(DBG_PCDV,"%s(%p,%d,%p,%0x)\n", __func__, _ep, _bytes, _dma, _gfp_flags); /* Check dword alignment */ if ((_bytes & 0x3UL) != 0) { DWC_WARN("%s() Buffer size is not a multiple of" "DWORD size (%d)",__func__, _bytes); } if (GET_CORE_IF(pcd)->dma_enable) { buf = dma_alloc_coherent (NULL, _bytes, _dma, _gfp_flags); } else { buf = kmalloc( _bytes, _gfp_flags); } /* Check dword alignment */ if (((int)buf & 0x3UL) != 0) { DWC_WARN("%s() Buffer is not DWORD aligned (%p)", __func__, buf); } return buf; }
/** * Cleanup the PCD. */ void pcd_remove( struct platform_device *_dev ) { dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev); dwc_otg_pcd_t *pcd = otg_dev->pcd; int plug_irq; DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev); /* * Free the IRQ */ //free_irq(_dev->irq, pcd); plug_irq = usb_get_vbus_irq(); usb_free_vbus_irq(plug_irq); dwc_otg_pcd_remove(pcd); destroy_workqueue(gadget_wrapper->detect_wq); wake_lock_destroy(&usb_wake_lock); switch_dev_unregister(&gadget_wrapper->sdev); free_wrapper(gadget_wrapper); pcd = 0; }
/** * This function registers a gadget driver with the PCD. * * When a driver is successfully registered, it will receive control * requests including set_configuration(), which enables non-control * requests. then usb traffic follows until a disconnect is reported. * then a host may connect again, or the driver might get unbound. * * @param driver The driver being registered */ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { int retval; pr_info("%s\n", __func__); if (!driver || driver->speed == USB_SPEED_UNKNOWN || !driver->disconnect || !driver->setup) { //!driver->unbind || !driver->disconnect || !driver->setup) { DWC_DEBUGPL(DBG_PCDV, "EINVAL\n"); return -EINVAL; } DWC_DEBUGPL(DBG_PCD, "registering gadget driver '%s'\n", driver->driver.name); if (gadget_wrapper == 0) { DWC_DEBUGPL(DBG_PCDV, "ENODEV\n"); return -ENODEV; } if (gadget_wrapper->driver != 0) { DWC_DEBUGPL(DBG_PCDV, "EBUSY (%p)\n", gadget_wrapper->driver); return -EBUSY; } /* hook up the driver */ gadget_wrapper->driver = driver; gadget_wrapper->gadget.dev.driver = &driver->driver; DWC_DEBUGPL(DBG_PCD, "bind to driver %s\n", driver->driver.name); retval = bind(&gadget_wrapper->gadget); gadget_wrapper->enabled = 1; if (retval) { DWC_ERROR("bind to driver %s --> error %d\n", driver->driver.name, retval); gadget_wrapper->driver = 0; gadget_wrapper->gadget.dev.driver = 0; gadget_wrapper->enabled = 0; return retval; } DWC_DEBUGPL(DBG_ANY, "registered gadget driver '%s'\n", driver->driver.name); return 0; }
/** 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;})) ;
/** * This function initialized the usb_ep structures to there default * state. * * @param d Pointer on gadget_wrapper. */ void gadget_add_eps(struct gadget_wrapper *d) { static const char *names[] = { "ep0", "ep1in", "ep2in", "ep3in", "ep4in", "ep5in", "ep6in", "ep7in", "ep8in", "ep9in", "ep10in", "ep11in", "ep12in", "ep13in", "ep14in", "ep15in", "ep1out", "ep2out", "ep3out", "ep4out", "ep5out", "ep6out", "ep7out", "ep8out", "ep9out", "ep10out", "ep11out", "ep12out", "ep13out", "ep14out", "ep15out" }; int i; struct usb_ep *ep; DWC_DEBUGPL(DBG_PCDV, "%s\n", __func__); INIT_LIST_HEAD(&d->gadget.ep_list); d->gadget.ep0 = &d->ep0; d->gadget.speed = USB_SPEED_UNKNOWN; INIT_LIST_HEAD(&d->gadget.ep0->ep_list); /** * Initialize the EP0 structure. */ ep = &d->ep0; /* Init the usb_ep structure. */ ep->name = names[0]; ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops; /** * @todo NGS: What should the max packet size be set to * here? Before EP type is set? */ ep->maxpacket = MAX_PACKET_SIZE; dwc_otg_pcd_ep_enable(d->pcd, NULL, ep); list_add_tail(&ep->ep_list, &d->gadget.ep_list); /** * Initialize the EP structures. */ for (i = 0; i < 15; i++) { ep = &d->in_ep[i]; /* Init the usb_ep structure. */ ep->name = names[i + 1]; ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops; /** * @todo NGS: What should the max packet size be set to * here? Before EP type is set? */ ep->maxpacket = MAX_PACKET_SIZE; list_add_tail(&ep->ep_list, &d->gadget.ep_list); } for (i = 0; i < 15; i++) { ep = &d->out_ep[i]; /* Init the usb_ep structure. */ ep->name = names[15 + i + 1]; ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops; /** * @todo NGS: What should the max packet size be set to * here? Before EP type is set? */ ep->maxpacket = MAX_PACKET_SIZE; list_add_tail(&ep->ep_list, &d->gadget.ep_list); } /* remove ep0 from the list. There is a ep0 pointer. */ list_del_init(&d->ep0.ep_list); d->ep0.maxpacket = MAX_EP0_SIZE; }
/** * This function releases the Gadget device. * required by device_unregister(). * * @todo Should this do something? Should it free the PCD? */ static void dwc_otg_pcd_gadget_release(struct device *dev) { DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, dev); }
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; }