/* ========================================================================== * BLX HS OTG Linux Software Driver and documentation (hereinafter, * "Software") is an Unsupported proprietary work of BLX, Inc. unless * otherwise expressly agreed to in writing between BLX and you. * * The Software IS NOT an item of Licensed Software or Licensed Product under * any End User Software License Agreement or Agreement for Licensed Product * with BLX or any supplement thereto. You are permitted to use and * redistribute this Software in source and binary forms, with or without * modification, provided that redistributions of source code must retain this * notice. You may not view, use, disclose, copy or distribute this file or * any information contained herein except pursuant to this license grant from * BLX. If you do not agree with this notice, including the disclaimer * below, then you are not authorized to use the Software. * * THIS SOFTWARE IS BEING DISTRIBUTED BY BLX SOLELY ON AN "AS IS" BASIS * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL BLX BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * ========================================================================== */ #ifndef GSC3280_HOST_ONLY /** @file * This file implements the Peripheral Controller Driver. * * The Peripheral Controller Driver (PCD) is responsible for * translating requests from the Function Driver into the appropriate * actions on the GSC3280_otg controller. It isolates the Function Driver * from the specifics of the controller by providing an API to the * Function Driver. * * The Peripheral Controller Driver for Linux will implement the * Gadget API, so that the existing Gadget drivers can be used. * (Gadget Driver is the Linux terminology for a Function Driver.) * * The Linux Gadget API is defined in the header file * . The USB EP operations API is * defined in the structure usb_ep_ops and the USB * Controller API is defined in the structure * usb_gadget_ops. * */ #include "gsc3280_otg_os_dep.h" #include "gsc3280_otg_pcd_if.h" #include "gsc3280_otg_pcd.h" #include "gsc3280_otg_driver.h" #include "gsc3280_otg_dbg.h" #include static struct gadget_wrapper { gsc3280_otg_pcd_t *pcd; struct usb_gadget gadget; struct usb_gadget_driver *driver; struct usb_ep ep0; struct usb_ep in_ep[16]; struct usb_ep out_ep[16]; } *gadget_wrapper; struct usb_gadget_driver *gadget_driver = 0 ; struct semaphore gadget_sem; int gadget_init = 0 ; void gadget_sem_init(void) { if (gadget_init == 0) { gadget_init = 1; sema_init(&gadget_sem,0); } } /* Display the contents of the buffer */ extern void dump_msg(const u8 * buf, unsigned int length); /** * Get the gsc3280_otg_pcd_ep_t* from usb_ep* pointer - NULL in case * if the endpoint is not found */ static struct gsc3280_otg_pcd_ep *ep_from_handle(gsc3280_otg_pcd_t * pcd, void *handle) { int i; if (pcd->ep0.priv == handle) { return &pcd->ep0; } for (i = 0; i < MAX_EPS_CHANNELS - 1; i++) { if (pcd->in_ep[i].priv == handle) return &pcd->in_ep[i]; if (pcd->out_ep[i].priv == handle) return &pcd->out_ep[i]; } return NULL; } /* USB Endpoint Operations */ /* * The following sections briefly describe the behavior of the Gadget * API endpoint operations implemented in the GSC3280_otg driver * software. Detailed descriptions of the generic behavior of each of * these functions can be found in the Linux header file * include/linux/usb_gadget.h. * * The Gadget API provides wrapper functions for each of the function * pointers defined in usb_ep_ops. The Gadget Driver calls the wrapper * function, which then calls the underlying PCD function. The * following sections are named according to the wrapper * functions. Within each section, the corresponding GSC3280_otg PCD * function name is specified. * */ /** * This function is called by the Gadget Driver for each EP to be * configured for the current configuration (SET_CONFIGURATION). * * This function initializes the gsc3280_otg_ep_t data structure, and then * calls gsc3280_otg_ep_activate. */ static int ep_enable(struct usb_ep *usb_ep, const struct usb_endpoint_descriptor *ep_desc) { int retval; GSC3280_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, usb_ep, ep_desc); if (!usb_ep || !ep_desc || ep_desc->bDescriptorType != USB_DT_ENDPOINT) { GSC3280_WARN("%s, bad ep or descriptor\n", __func__); return -EINVAL; } if (usb_ep == &gadget_wrapper->ep0) { GSC3280_WARN("%s, bad ep(0)\n", __func__); return -EINVAL; } /* Check FIFO size? */ if (!ep_desc->wMaxPacketSize) { GSC3280_WARN("%s, bad %s maxpacket\n", __func__, usb_ep->name); return -ERANGE; } if (!gadget_wrapper->driver || gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { GSC3280_WARN("%s, bogus device state\n", __func__); return -ESHUTDOWN; } /* Delete after check - MAS */ #if 0 nat = (uint32_t) ep_desc->wMaxPacketSize; printk(KERN_ALERT "%s: nat (before) =%d\n", __func__, nat); nat = (nat >> 11) & 0x03; printk(KERN_ALERT "%s: nat (after) =%d\n", __func__, nat); #endif retval = gsc3280_otg_pcd_ep_enable(gadget_wrapper->pcd, (const uint8_t *)ep_desc, (void *)usb_ep); if (retval) { GSC3280_WARN("gsc3280_otg_pcd_ep_enable failed\n"); return -EINVAL; } usb_ep->maxpacket = le16_to_cpu(ep_desc->wMaxPacketSize); return 0; } /** * 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 gsc3280_otg_ep_t data structure for this EP, * and then calls gsc3280_otg_ep_deactivate. */ static int ep_disable(struct usb_ep *usb_ep) { int retval; GSC3280_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, usb_ep); if (!usb_ep) { GSC3280_DEBUGPL(DBG_PCD, "%s, %s not enabled\n", __func__, usb_ep ? usb_ep->name : NULL); return -EINVAL; } retval = gsc3280_otg_pcd_ep_disable(gadget_wrapper->pcd, usb_ep); 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 *gsc3280_otg_pcd_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) { struct usb_request *usb_req; GSC3280_DEBUGPL(DBG_PCDV, "%s(%p,%d)\n", __func__, ep, gfp_flags); if (0 == ep) { GSC3280_WARN("%s() %s\n", __func__, "Invalid EP!\n"); return 0; } usb_req = kmalloc(sizeof(*usb_req), gfp_flags); if (0 == usb_req) { GSC3280_WARN("%s() %s\n", __func__, "request allocation failed!\n"); return 0; } memset(usb_req, 0, sizeof(*usb_req)); usb_req->dma = GSC3280_DMA_ADDR_INVALID; return usb_req; } /** * This function frees a request object. * * @param ep The endpoint associated with the request * @param req The request being freed */ static void gsc3280_otg_pcd_free_request(struct usb_ep *ep, struct usb_request *req) { GSC3280_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, ep, req); if (0 == ep || 0 == req) { GSC3280_WARN("%s() %s\n", __func__, "Invalid ep or req argument!\n"); return; } kfree(req); } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) /** * This function allocates an I/O buffer to be used for a transfer * to/from the specified endpoint. * * @param usb_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 *gsc3280_otg_pcd_alloc_buffer(struct usb_ep *usb_ep, unsigned bytes, dma_addr_t * dma, gfp_t gfp_flags) { void *buf; gsc3280_otg_pcd_t *pcd = 0; pcd = gadget_wrapper->pcd; GSC3280_DEBUGPL(DBG_PCDV, "%s(%p,%d,%p,%0x)\n", __func__, usb_ep, bytes, dma, gfp_flags); /* Check dword alignment */ if ((bytes & 0x3UL) != 0) { GSC3280_WARN("%s() Buffer size is not a multiple of" "DWORD size (%d)", __func__, bytes); } buf = dma_alloc_coherent(NULL, bytes, dma, gfp_flags); /* Check dword alignment */ if (((int)buf & 0x3UL) != 0) { GSC3280_WARN("%s() Buffer is not DWORD aligned (%p)", __func__, buf); } return buf; } /** * This function frees an I/O buffer that was allocated by alloc_buffer. * * @param usb_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 gsc3280_otg_pcd_free_buffer(struct usb_ep *usb_ep, void *buf, dma_addr_t dma, unsigned bytes) { gsc3280_otg_pcd_t *pcd = 0; pcd = gadget_wrapper->pcd; GSC3280_DEBUGPL(DBG_PCDV, "%s(%p,%0x,%d)\n", __func__, buf, dma, bytes); dma_free_coherent(NULL, bytes, buf, dma); } #endif /** * 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) { gsc3280_otg_pcd_t *pcd; struct gsc3280_otg_pcd_ep *ep = NULL; int retval = 0, is_isoc_ep = 0; dma_addr_t dma_addr = GSC3280_DMA_ADDR_INVALID; GSC3280_DEBUGPL(DBG_PCDV, "%s(%p,%p,%d)\n", __func__, usb_ep, usb_req, gfp_flags); if (!usb_req || !usb_req->complete || !usb_req->buf) { GSC3280_WARN("bad params\n"); return -EINVAL; } if (!usb_ep) { GSC3280_WARN("bad ep\n"); return -EINVAL; } pcd = gadget_wrapper->pcd; if (!gadget_wrapper->driver || gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { GSC3280_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", gadget_wrapper->gadget.speed); GSC3280_WARN("bogus device state\n"); return -ESHUTDOWN; } GSC3280_DEBUGPL(DBG_PCD, "%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; ep = ep_from_handle(pcd, usb_ep); if (ep == NULL) is_isoc_ep = 0; else is_isoc_ep = (ep->gsc3280_ep.type == GSC3280_OTG_EP_TYPE_ISOC) ? 1 : 0; #ifdef GSC3280_UTE_PER_IO if (is_isoc_ep == 1) { retval = gsc3280_otg_pcd_xiso_ep_queue(pcd, usb_ep, usb_req->buf, usb_req->dma, usb_req->length, usb_req->zero, usb_req, gfp_flags == GFP_ATOMIC ? 1 : 0, &usb_req->ext_req); if (retval) return -EINVAL; return 0; } #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) dma_addr = usb_req->dma; #else usb_req->dma = virt_to_phys((void*)usb_req->buf); dma_addr = usb_req->dma; if(ep->gsc3280_ep.is_in) { if(usb_req->length) dma_cache_wback_inv((unsigned long)usb_req->buf, usb_req->length + 2); } /* if (GET_CORE_IF(pcd)->dma_enable) { struct pci_dev *dev = gadget_wrapper->pcd->otg_dev->os_dep.pcidev; if (usb_req->length != 0 && usb_req->dma == GSC3280_DMA_ADDR_INVALID) { dma_addr = pci_map_single(dev, usb_req->buf, usb_req->length, ep->gsc3280_ep.is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); } } */ //printk("dma_addr 0x%x\n",dma_addr); #endif retval = gsc3280_otg_pcd_ep_queue(pcd, usb_ep, usb_req->buf, dma_addr, usb_req->length, usb_req->zero, usb_req, gfp_flags == GFP_ATOMIC ? 1 : 0); if (retval) { return -EINVAL; } return 0; } /** * This function cancels an I/O request from an EP. */ static int ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) { GSC3280_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, usb_ep, usb_req); if (!usb_ep || !usb_req) { GSC3280_WARN("bad argument\n"); return -EINVAL; } if (!gadget_wrapper->driver || gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { GSC3280_WARN("bogus device state\n"); return -ESHUTDOWN; } if (gsc3280_otg_pcd_ep_dequeue(gadget_wrapper->pcd, usb_ep, usb_req)) { return -EINVAL; } return 0; } /** * 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; GSC3280_DEBUGPL(DBG_PCD, "HALT %s %d\n", usb_ep->name, value); if (!usb_ep) { GSC3280_WARN("bad ep\n"); return -EINVAL; } retval = gsc3280_otg_pcd_ep_halt(gadget_wrapper->pcd, usb_ep, value); if (retval == -GSC3280_E_AGAIN) { return -EAGAIN; } else if (retval) { retval = -EINVAL; } return retval; } #ifdef GSC3280_EN_ISOC /** * This function is used to submit an ISOC Transfer Request to an EP. * * - Every time a sync period completes the request's completion callback * is called to provide data to the gadget driver. * - Once submitted the request cannot be modified. * - Each request is turned into periodic data packets untill ISO * Transfer is stopped.. */ static int iso_ep_start(struct usb_ep *usb_ep, struct usb_iso_request *req, gfp_t gfp_flags) { int retval = 0; if (!req || !req->process_buffer || !req->buf0 || !req->buf1) { GSC3280_WARN("bad params\n"); return -EINVAL; } if (!usb_ep) { GSC3280_PRINTF("bad params\n"); return -EINVAL; } req->status = -EINPROGRESS; retval = gsc3280_otg_pcd_iso_ep_start(gadget_wrapper->pcd, usb_ep, req->buf0, req->buf1, req->dma0, req->dma1, req->sync_frame, req->data_pattern_frame, req->data_per_frame, req->flags & USB_REQ_ISO_ASAP ? -1 : req-> start_frame, req->buf_proc_intrvl, req, gfp_flags == GFP_ATOMIC ? 1 : 0); if (retval) { return -EINVAL; } return retval; } /** * 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) { GSC3280_WARN("bad ep\n"); } if (!gadget_wrapper->driver || gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { GSC3280_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", gadget_wrapper->gadget.speed); GSC3280_WARN("bogus device state\n"); } gsc3280_otg_pcd_iso_ep_stop(gadget_wrapper->pcd, usb_ep, req); if (retval) { retval = -EINVAL; } return retval; } static struct usb_iso_request *alloc_iso_request(struct usb_ep *ep, int packets, gfp_t gfp_flags) { struct usb_iso_request *pReq = NULL; uint32_t req_size; req_size = sizeof(struct usb_iso_request); req_size += (2 * packets * (sizeof(struct usb_gadget_iso_packet_descriptor))); pReq = kmalloc(req_size, gfp_flags); if (!pReq) { GSC3280_WARN("Can't allocate Iso Request\n"); return 0; } pReq->iso_packet_desc0 = (void *)(pReq + 1); pReq->iso_packet_desc1 = pReq->iso_packet_desc0 + packets; return pReq; } static void free_iso_request(struct usb_ep *ep, struct usb_iso_request *req) { kfree(req); } static struct usb_isoc_ep_ops gsc3280_otg_pcd_ep_ops = { .ep_ops = { .enable = ep_enable, .disable = ep_disable, .alloc_request = gsc3280_otg_pcd_alloc_request, .free_request = gsc3280_otg_pcd_free_request, #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) .alloc_buffer = gsc3280_otg_pcd_alloc_buffer, .free_buffer = gsc3280_otg_pcd_free_buffer, #endif .queue = ep_queue, .dequeue = ep_dequeue, .set_halt = ep_halt, .fifo_status = 0, .fifo_flush = 0, }, .iso_ep_start = iso_ep_start, .iso_ep_stop = iso_ep_stop, .alloc_iso_request = alloc_iso_request, .free_iso_request = free_iso_request, }; #else static struct usb_ep_ops gsc3280_otg_pcd_ep_ops = { .enable = ep_enable, .disable = ep_disable, .alloc_request = gsc3280_otg_pcd_alloc_request, .free_request = gsc3280_otg_pcd_free_request, #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) .alloc_buffer = gsc3280_otg_pcd_alloc_buffer, .free_buffer = gsc3280_otg_pcd_free_buffer, #endif .queue = ep_queue, .dequeue = ep_dequeue, .set_halt = ep_halt, .fifo_status = 0, .fifo_flush = 0, }; #endif /* _EN_ISOC_ */ /* Gadget Operations */ /** * The following gadget operations will be implemented in the GSC3280_otg * PCD. Functions in the API that are not described below are not * implemented. * * The Gadget API provides wrapper functions for each of the function * pointers defined in usb_gadget_ops. The Gadget Driver calls the * wrapper function, which then calls the underlying PCD function. The * following sections are named according to the wrapper functions * (except for ioctl, which doesn't have a wrapper function). Within * each section, the corresponding GSC3280_otg PCD function name is * specified. * */ /** *Gets the USB Frame number of the last SOF. */ static int get_frame_number(struct usb_gadget *gadget) { struct gadget_wrapper *d; GSC3280_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget); if (gadget == 0) { return -ENODEV; } d = container_of(gadget, struct gadget_wrapper, gadget); return gsc3280_otg_pcd_get_frame_number(d->pcd); } #ifdef CONFIG_USB_GSC3280_OTG_LPM static int test_lpm_enabled(struct usb_gadget *gadget) { struct gadget_wrapper *d; d = container_of(gadget, struct gadget_wrapper, gadget); return gsc3280_otg_pcd_is_lpm_enabled(d->pcd); } #endif /** * 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; GSC3280_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget); if (gadget == 0) { return -ENODEV; } else { d = container_of(gadget, struct gadget_wrapper, gadget); } gsc3280_otg_pcd_wakeup(d->pcd); return 0; } static const struct usb_gadget_ops gsc3280_otg_pcd_ops = { .get_frame = get_frame_number, .wakeup = wakeup, #ifdef CONFIG_USB_GSC3280_OTG_LPM .lpm_support = test_lpm_enabled, #endif // current versions must always be self-powered }; static int _setup(gsc3280_otg_pcd_t * pcd, uint8_t * bytes) { int retval = -GSC3280_E_NOT_SUPPORTED; if (gadget_wrapper->driver && gadget_wrapper->driver->setup) { retval = gadget_wrapper->driver->setup(&gadget_wrapper->gadget, (struct usb_ctrlrequest *)bytes); } if (retval == -ENOTSUPP) { retval = -GSC3280_E_NOT_SUPPORTED; } else if (retval < 0) { retval = -GSC3280_E_INVALID; } return retval; } #ifdef GSC3280_EN_ISOC static int _isoc_complete(gsc3280_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 = gsc3280_otg_pcd_get_iso_packet_count(pcd, ep_handle, req_handle); for (i = 0; i < packet_count; ++i) { int status; int actual; int offset; gsc3280_otg_pcd_get_iso_packet_params(pcd, ep_handle, req_handle, i, &status, &actual, &offset); switch (status) { case -GSC3280_E_NO_DATA: status = -ENODATA; break; default: if (status) { GSC3280_PRINTF("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; } #endif /* GSC3280_EN_ISOC */ #ifdef GSC3280_UTE_PER_IO /** * Copy the contents of the extended request to the Linux usb_request's * extended part and call the gadget's completion. * * @param pcd Pointer to the pcd structure * @param ep_handle Void pointer to the usb_ep structure * @param req_handle Void pointer to the usb_request structure * @param status Request status returned from the portable logic * @param ereq_port Void pointer to the extended request structure * created in the the portable part that contains the * results of the processed iso packets. */ static int _xisoc_complete(gsc3280_otg_pcd_t * pcd, void *ep_handle, void *req_handle, int32_t status, void *ereq_port) { struct gsc3280_ute_iso_req_ext *ereqorg = NULL; struct gsc3280_iso_xreq_port *ereqport = NULL; struct gsc3280_ute_iso_packet_descriptor *desc_org = NULL; int i; struct usb_request *req; //struct gsc3280_ute_iso_packet_descriptor * //int status = 0; req = (struct usb_request *)req_handle; ereqorg = &req->ext_req; ereqport = (struct gsc3280_iso_xreq_port *)ereq_port; desc_org = ereqorg->per_io_frame_descs; if (req && req->complete) { /* Copy the request data from the portable logic to our request */ for (i = 0; i < ereqport->pio_pkt_count; i++) { desc_org[i].actual_length = ereqport->per_io_frame_descs[i].actual_length; desc_org[i].status = ereqport->per_io_frame_descs[i].status; } switch (status) { case -GSC3280_E_SHUTDOWN: req->status = -ESHUTDOWN; break; case -GSC3280_E_RESTART: req->status = -ECONNRESET; break; case -GSC3280_E_INVALID: req->status = -EINVAL; break; case -GSC3280_E_TIMEOUT: req->status = -ETIMEDOUT; break; default: req->status = status; } /* And call the gadget's completion */ req->complete(ep_handle, req); } return 0; } #endif /* GSC3280_UTE_PER_IO */ static int _complete(gsc3280_otg_pcd_t * pcd, void *ep_handle, void *req_handle, int32_t status, uint32_t actual) { struct usb_request *req = (struct usb_request *)req_handle; #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27) struct gsc3280_otg_pcd_ep *ep = NULL; #endif #ifdef PCI_INTERFACE struct pci_dev *dev = NULL; #endif if (req && req->complete) { switch (status) { case -GSC3280_E_SHUTDOWN: req->status = -ESHUTDOWN; break; case -GSC3280_E_RESTART: req->status = -ECONNRESET; break; case -GSC3280_E_INVALID: req->status = -EINVAL; break; case -GSC3280_E_TIMEOUT: req->status = -ETIMEDOUT; break; default: req->status = status; } req->actual = actual; GSC3280_SPINUNLOCK(pcd->lock); req->complete(ep_handle, req); GSC3280_SPINLOCK(pcd->lock); } #ifdef PCI_INTERFACE dev = gadget_wrapper->pcd->otg_dev->os_dep.pcidev; ep = ep_from_handle(pcd, ep_handle); if (GET_CORE_IF(pcd)->dma_enable) { if (req->length != 0) pci_unmap_single(dev, req->dma, req->length, ep->gsc3280_ep. is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); } #endif if(req->length) dma_cache_wback_inv((unsigned long)req->buf, req->length + 2); return 0; } static int _connect(gsc3280_otg_pcd_t * pcd, int speed) { gadget_wrapper->gadget.speed = speed; return 0; } static int _disconnect(gsc3280_otg_pcd_t * pcd) { if (gadget_wrapper->driver && gadget_wrapper->driver->disconnect) { gadget_wrapper->driver->disconnect(&gadget_wrapper->gadget); } return 0; } static int _resume(gsc3280_otg_pcd_t * pcd) { if (gadget_wrapper->driver && gadget_wrapper->driver->resume) { gadget_wrapper->driver->resume(&gadget_wrapper->gadget); } return 0; } static int _suspend(gsc3280_otg_pcd_t * pcd) { if (gadget_wrapper->driver && gadget_wrapper->driver->suspend) { gadget_wrapper->driver->suspend(&gadget_wrapper->gadget); } return 0; } /** * This function updates the otg values in the gadget structure. */ static int _hnp_changed(gsc3280_otg_pcd_t * pcd) { if (!gadget_wrapper->gadget.is_otg) return 0; gadget_wrapper->gadget.b_hnp_enable = get_b_hnp_enable(pcd); gadget_wrapper->gadget.a_hnp_support = get_a_hnp_support(pcd); gadget_wrapper->gadget.a_alt_hnp_support = get_a_alt_hnp_support(pcd); return 0; } static int _reset(gsc3280_otg_pcd_t * pcd) { return 0; } #ifdef GSC3280_UTE_CFI static int _cfi_setup(gsc3280_otg_pcd_t * pcd, void *cfi_req) { int retval = -GSC3280_E_INVALID; if (gadget_wrapper->driver->cfi_feature_setup) { retval = gadget_wrapper->driver-> cfi_feature_setup(&gadget_wrapper->gadget, (struct cfi_usb_ctrlrequest *)cfi_req); } return retval; } #endif static const struct gsc3280_otg_pcd_function_ops fops = { .complete = _complete, #ifdef GSC3280_EN_ISOC .isoc_complete = _isoc_complete, #endif .setup = _setup, .disconnect = _disconnect, .connect = _connect, .resume = _resume, .suspend = _suspend, .hnp_changed = _hnp_changed, .reset = _reset, #ifdef GSC3280_UTE_CFI .cfi_setup = _cfi_setup, #endif #ifdef GSC3280_UTE_PER_IO .xisoc_complete = _xisoc_complete, #endif }; /** * This function is the top level PCD interrupt handler. */ static irqreturn_t gsc3280_otg_pcd_irq(int irq, void *dev) { gsc3280_otg_pcd_t *pcd = dev; int32_t retval = IRQ_NONE; retval = gsc3280_otg_pcd_handle_intr(pcd); if (retval != 0) { S3C2410X_CLEAR_EINTPEND(); } return IRQ_RETVAL(retval); } /** * 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; int8_t dev_endpoints; GSC3280_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 *)&gsc3280_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; gsc3280_otg_pcd_ep_enable(d->pcd, NULL, ep); list_add_tail(&ep->ep_list, &d->gadget.ep_list); /** * Initialize the EP structures. */ dev_endpoints = d->pcd->core_if->dev_if->num_in_eps; for (i = 0; i < dev_endpoints; i++) { ep = &d->in_ep[i]; /* Init the usb_ep structure. */ ep->name = names[d->pcd->in_ep[i].gsc3280_ep.num]; ep->ops = (struct usb_ep_ops *)&gsc3280_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); } dev_endpoints = d->pcd->core_if->dev_if->num_out_eps; for (i = 0; i < dev_endpoints; i++) { ep = &d->out_ep[i]; /* Init the usb_ep structure. */ ep->name = names[15 + d->pcd->out_ep[i].gsc3280_ep.num]; ep->ops = (struct usb_ep_ops *)&gsc3280_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 gsc3280_otg_pcd_gadget_release(struct device *dev) { GSC3280_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, dev); } static struct gadget_wrapper *alloc_wrapper(struct platform_device *pdev) { static char pcd_name[] = "gsc3280_otg_pcd"; gsc3280_otg_device_t *otg_dev = platform_get_drvdata(pdev); struct gadget_wrapper *d; int retval; d = GSC3280_ALLOC(sizeof(*d)); if (d == NULL) { return NULL; } memset(d, 0, sizeof(*d)); d->gadget.name = pcd_name; d->pcd = otg_dev->pcd; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) strcpy(d->gadget.dev.bus_id, "gadget"); #else dev_set_name(&d->gadget.dev, "%s", "gadget"); #endif d->gadget.dev.parent = &pdev->dev; d->gadget.dev.release = gsc3280_otg_pcd_gadget_release; d->gadget.ops = &gsc3280_otg_pcd_ops; d->gadget.is_dualspeed = gsc3280_otg_pcd_is_dualspeed(otg_dev->pcd); d->gadget.is_otg = gsc3280_otg_pcd_is_otg(otg_dev->pcd); d->driver = 0; /* Register the gadget device */ retval = device_register(&d->gadget.dev); if (retval != 0) { GSC3280_ERROR("device_register failed\n"); GSC3280_FREE(d); return NULL; } return d; } static void free_wrapper(struct gadget_wrapper *d) { if (d->driver) { /* should have been done already by driver model core */ GSC3280_WARN("driver '%s' is still registered\n", d->driver->driver.name); usb_gadget_unregister_driver(d->driver); } device_unregister(&d->gadget.dev); GSC3280_FREE(d); } /** * This function initialized the PCD portion of the driver. * */ int pcd_init(struct platform_device *pdev) { gsc3280_otg_device_t *otg_dev = platform_get_drvdata(pdev); int retval = 0; GSC3280_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, pdev); otg_dev->pcd = gsc3280_otg_pcd_init(otg_dev->core_if); if (!otg_dev->pcd) { GSC3280_ERROR("gsc3280_otg_pcd_init failed\n"); return -ENOMEM; } otg_dev->pcd->otg_dev = otg_dev; gadget_wrapper = alloc_wrapper(pdev); #if 0 //hejiasheng add if (gadget_driver != 0 ) { gadget_wrapper->driver = gadget_driver; gadget_wrapper->gadget.dev.driver = &gadget_driver->driver; } #endif /* * Initialize EP structures */ gadget_add_eps(gadget_wrapper); /* * Setup interupt handler */ GSC3280_DEBUGPL(DBG_ANY, "registering handler for irq%d\n",otg_dev->irq); retval = request_irq(otg_dev->irq, gsc3280_otg_pcd_irq, IRQF_SHARED | IRQF_DISABLED, gadget_wrapper->gadget.name, otg_dev->pcd); if (retval != 0) { GSC3280_ERROR("request of irq%d failed\n", otg_dev->irq); free_wrapper(gadget_wrapper); return -EBUSY; } gsc3280_otg_pcd_start(gadget_wrapper->pcd, &fops); return retval; } /** * Cleanup the PCD. */ void pcd_remove(struct platform_device *pdev) { gsc3280_otg_device_t *otg_dev = platform_get_drvdata(pdev); gsc3280_otg_pcd_t *pcd = otg_dev->pcd; GSC3280_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__,otg_dev); /* * Free the IRQ */ free_irq(otg_dev->irq, pcd); gsc3280_otg_pcd_remove(otg_dev->pcd); free_wrapper(gadget_wrapper); otg_dev->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_register_driver(struct usb_gadget_driver *driver) int usb_gadget_probe_driver(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { int retval; GSC3280_DEBUGPL(DBG_PCD, "GSC3280:registering gadget driver '%s'\n", driver->driver.name); if (!driver || driver->speed == USB_SPEED_UNKNOWN || !driver->unbind || !driver->disconnect || !driver->setup) { GSC3280_DEBUGPL(DBG_PCDV, "EINVAL\n"); return -EINVAL; } if (gadget_wrapper == 0) { GSC3280_DEBUGPL(DBG_PCDV, "ENODEV\n"); return -ENODEV; } { gadget_wrapper->driver = driver; gadget_wrapper->gadget.dev.driver = &driver->driver; } /* if (gadget_wrapper->driver != 0) { GSC3280_DEBUGPL(DBG_PCDV, "EBUSY (%p)\n", gadget_wrapper->driver); return -EBUSY; } */ /* hook up the driver */ #if 1 //hejiasheng GSC3280_DEBUGPL(DBG_PCD, "bind to driver %s\n", driver->driver.name); retval = bind(&gadget_wrapper->gadget); if (retval) { GSC3280_ERROR("bind to driver %s --> error %d\n", driver->driver.name, retval); // gadget_wrapper->driver = 0; // gadget_wrapper->gadget.dev.driver = 0; return retval; } #endif GSC3280_DEBUGPL(DBG_ANY, "registered gadget driver '%s'\n", driver->driver.name); return 0; } //EXPORT_SYMBOL(usb_gadget_register_driver); EXPORT_SYMBOL(usb_gadget_probe_driver); /** * This function unregisters a gadget driver * * @param driver The driver being unregistered */ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { //GSC3280_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, _driver); if (gadget_wrapper == 0) { GSC3280_DEBUGPL(DBG_ANY, "%s Return(%d): s_pcd==0\n", __func__, -ENODEV); return -ENODEV; } if (driver == 0 || driver != gadget_wrapper->driver) { GSC3280_DEBUGPL(DBG_ANY, "%s Return(%d): driver?\n", __func__, -EINVAL); return -EINVAL; } driver->unbind(&gadget_wrapper->gadget); gadget_wrapper->driver = 0; GSC3280_DEBUGPL(DBG_ANY, "unregistered driver '%s'\n", driver->driver.name); return 0; } EXPORT_SYMBOL(usb_gadget_unregister_driver); #endif /* GSC3280_HOST_ONLY */