aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/susb/dwc_otg_pcd_linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/susb/dwc_otg_pcd_linux.c')
-rw-r--r--drivers/usb/susb/dwc_otg_pcd_linux.c1245
1 files changed, 1245 insertions, 0 deletions
diff --git a/drivers/usb/susb/dwc_otg_pcd_linux.c b/drivers/usb/susb/dwc_otg_pcd_linux.c
new file mode 100644
index 00000000000..0d66c35fdfe
--- /dev/null
+++ b/drivers/usb/susb/dwc_otg_pcd_linux.c
@@ -0,0 +1,1245 @@
+ /* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_linux.c $
+ * $Revision: #16 $
+ * $Date: 2010/11/29 $
+ * $Change: 1636033 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys 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 Synopsys 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
+ * Synopsys. 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 SYNOPSYS 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 SYNOPSYS 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 DWC_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 DWC_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
+ * <code><linux/usb_gadget.h></code>. The USB EP operations API is
+ * defined in the structure <code>usb_ep_ops</code> and the USB
+ * Controller API is defined in the structure
+ * <code>usb_gadget_ops</code>.
+ *
+ */
+
+#include "dwc_otg_os_dep.h"
+#include "dwc_otg_pcd_if.h"
+#include "dwc_otg_pcd.h"
+#include "dwc_otg_driver.h"
+#include "dwc_otg_dbg.h"
+
+static struct gadget_wrapper {
+ dwc_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];
+
+} *dwc_gadget;
+
+/* Display the contents of the buffer */
+extern void dump_msg(const u8 * buf, unsigned int length);
+
+static int dwc_otg_pcd_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+static int dwc_otg_pcd_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+
+/**
+ * Get the dwc_otg_pcd_ep_t* from usb_ep* pointer - NULL in case
+ * if the endpoint is not found
+ */
+static struct dwc_otg_pcd_ep *ep_from_handle(dwc_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 DWC_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 DWC_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 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 == &dwc_gadget->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 (!dwc_gadget->driver ||
+ dwc_gadget->gadget.speed == USB_SPEED_UNKNOWN) {
+ DWC_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 = dwc_otg_pcd_ep_enable(dwc_gadget->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;
+}
+
+/**
+ * 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 ep_disable(struct usb_ep *usb_ep)
+{
+ int retval;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, usb_ep);
+ if (!usb_ep) {
+ DWC_DEBUGPL(DBG_PCD, "%s, %s not enabled\n", __func__,
+ usb_ep ? usb_ep->name : NULL);
+ return -EINVAL;
+ }
+
+ retval = dwc_otg_pcd_ep_disable(dwc_gadget->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 *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 = kzalloc(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_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 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);
+}
+
+
+/**
+ * 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;
+ struct dwc_otg_pcd_ep *ep = NULL;
+ int retval = 0, is_isoc_ep = 0;
+ dma_addr_t dma_addr = DWC_DMA_ADDR_INVALID;
+
+ struct lm_device *dev = dwc_gadget->pcd->otg_dev->os_dep.lmdev;
+
+ if (!usb_req || !usb_req->complete || !usb_req->buf) {
+ DWC_WARN("bad params\n");
+ return -EINVAL;
+ }
+
+ if (!usb_ep) {
+ DWC_WARN("bad ep\n");
+ return -EINVAL;
+ }
+
+ pcd = dwc_gadget->pcd;
+ if (!dwc_gadget->driver ||
+ dwc_gadget->gadget.speed == USB_SPEED_UNKNOWN) {
+ DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n",
+ dwc_gadget->gadget.speed);
+ DWC_WARN("bogus device state\n");
+ return -ESHUTDOWN;
+ }
+
+ DWC_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->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) ? 1 : 0;
+
+ if (!ep) {
+ return -EINVAL;
+ }
+
+#ifdef DWC_UTE_PER_IO
+
+ if (is_isoc_ep == 1) {
+ retval = dwc_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 (GET_CORE_IF(pcd)->dma_enable) {
+ if (usb_req->length != 0 && usb_req->dma == DWC_DMA_ADDR_INVALID) {
+ dma_addr = dma_map_single((struct device *)(&(dev->dev)), usb_req->buf, usb_req->length,
+ ep->dwc_ep.is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ usb_req->dma = dma_addr;
+ }
+ }
+
+ retval = dwc_otg_pcd_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);
+ if (retval){
+ if (GET_CORE_IF(pcd)->dma_enable) {
+ if (usb_req->length != 0 && usb_req->dma != DWC_DMA_ADDR_INVALID) {
+ dma_unmap_single((struct device *)(&(dev->dev)), usb_req->dma, usb_req->length,
+ ep->dwc_ep.
+ is_in ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE);
+ usb_req->dma = DWC_DMA_ADDR_INVALID;
+ }
+ }
+
+ 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)
+{
+ 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 (!dwc_gadget->driver ||
+ dwc_gadget->gadget.speed == USB_SPEED_UNKNOWN) {
+ DWC_WARN("bogus device state\n");
+ return -ESHUTDOWN;
+ }
+ if (dwc_otg_pcd_ep_dequeue(dwc_gadget->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;
+
+ 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(dwc_gadget->pcd, usb_ep, value);
+ if (retval == -DWC_E_AGAIN) {
+ return -EAGAIN;
+ } else if (retval) {
+ retval = -EINVAL;
+ }
+
+ return retval;
+}
+
+#ifdef DWC_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) {
+ DWC_WARN("bad params\n");
+ return -EINVAL;
+ }
+
+ if (!usb_ep) {
+ DWC_PRINTF("bad params\n");
+ return -EINVAL;
+ }
+
+ req->status = -EINPROGRESS;
+
+ retval =
+ dwc_otg_pcd_iso_ep_start(dwc_gadget->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) {
+ DWC_WARN("bad ep\n");
+ }
+
+ if (!dwc_gadget->driver ||
+ dwc_gadget->gadget.speed == USB_SPEED_UNKNOWN) {
+ DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n",
+ dwc_gadget->gadget.speed);
+ DWC_WARN("bogus device state\n");
+ }
+
+ dwc_otg_pcd_iso_ep_stop(dwc_gadget->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) {
+ DWC_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 dwc_otg_pcd_ep_ops = {
+ .ep_ops = {
+ .enable = ep_enable,
+ .disable = ep_disable,
+
+ .alloc_request = dwc_otg_pcd_alloc_request,
+ .free_request = dwc_otg_pcd_free_request,
+
+ .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 int ep_fifo_status(struct usb_ep *ep)
+{
+ //printk("enter ep_fifo_status.\n");
+ return 0;
+}
+
+static void ep_fifo_flush(struct usb_ep *ep)
+{
+ //printk("enter ep_fifo_flush.\n");
+}
+
+static struct usb_ep_ops dwc_otg_pcd_ep_ops = {
+ .enable = ep_enable,
+ .disable = ep_disable,
+
+ .alloc_request = dwc_otg_pcd_alloc_request,
+ .free_request = dwc_otg_pcd_free_request,
+
+ .queue = ep_queue,
+ .dequeue = ep_dequeue,
+
+ .set_halt = ep_halt,
+ .fifo_status = ep_fifo_status,
+ .fifo_flush = ep_fifo_flush,
+
+};
+
+#endif /* _EN_ISOC_ */
+/* Gadget Operations */
+/**
+ * The following gadget operations will be implemented in the DWC_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 DWC_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;
+
+ 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);
+}
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+static int test_lpm_enabled(struct usb_gadget *gadget)
+{
+ struct gadget_wrapper *d;
+ d = container_of(gadget, struct gadget_wrapper, gadget);
+ return dwc_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;
+
+ 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;
+}
+
+static int pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct gadget_wrapper *d;
+ printk("is_on: %d\n", is_on);
+
+ is_on = !!is_on;
+
+ 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_pullup(d->pcd,is_on);
+
+ return 0;
+}
+
+static const struct usb_gadget_ops dwc_otg_pcd_ops = {
+ .get_frame = get_frame_number,
+
+ .pullup = pullup,
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ .lpm_support = test_lpm_enabled,
+#endif
+
+ .udc_start = dwc_otg_pcd_udc_start,
+ .udc_stop = dwc_otg_pcd_udc_stop,
+};
+
+static int _setup(dwc_otg_pcd_t * pcd, uint8_t * bytes)
+{
+ int retval = -DWC_E_NOT_SUPPORTED;
+ //printk("_setup++,request_config:%d\n",pcd->request_config);
+ if (dwc_gadget->driver && dwc_gadget->driver->setup) {
+ retval = dwc_gadget->driver->setup(&dwc_gadget->gadget,
+ (struct usb_ctrlrequest *)bytes);
+ }
+
+ if (retval == -ENOTSUPP) {
+ retval = -DWC_E_NOT_SUPPORTED;
+ } else if (retval < 0) {
+ retval = -DWC_E_INVALID;
+ }
+ //printk("_setup--,request_config:%d\n",pcd->request_config);
+
+ return retval;
+}
+
+#ifdef DWC_EN_ISOC
+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_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 /* DWC_EN_ISOC */
+
+#ifdef DWC_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(dwc_otg_pcd_t * pcd, void *ep_handle,
+ void *req_handle, int32_t status, void *ereq_port)
+{
+ struct dwc_ute_iso_req_ext *ereqorg = NULL;
+ struct dwc_iso_xreq_port *ereqport = NULL;
+ struct dwc_ute_iso_packet_descriptor *desc_org = NULL;
+ int i;
+ struct usb_request *req;
+ //struct dwc_ute_iso_packet_descriptor *
+ //int status = 0;
+
+ req = (struct usb_request *)req_handle;
+ ereqorg = &req->ext_req;
+ ereqport = (struct dwc_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 -DWC_E_SHUTDOWN:
+ req->status = -ESHUTDOWN;
+ break;
+ case -DWC_E_RESTART:
+ req->status = -ECONNRESET;
+ break;
+ case -DWC_E_INVALID:
+ req->status = -EINVAL;
+ break;
+ case -DWC_E_TIMEOUT:
+ req->status = -ETIMEDOUT;
+ break;
+ default:
+ req->status = status;
+ }
+
+ /* And call the gadget's completion */
+ req->complete(ep_handle, req);
+ }
+
+ return 0;
+}
+#endif /* DWC_UTE_PER_IO */
+
+static int _complete(dwc_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;
+ struct dwc_otg_pcd_ep *ep = NULL;
+ struct lm_device *dev = NULL;
+
+ dev = dwc_gadget->pcd->otg_dev->os_dep.lmdev;
+ ep = ep_from_handle(pcd, ep_handle);
+
+ if (!ep)
+ return -1;
+
+ if (GET_CORE_IF(pcd)->dma_enable) {
+ if ((req->length != 0)&& (req->dma != DWC_DMA_ADDR_INVALID))
+ {
+ dma_unmap_single((struct device *)(&(dev->dev)), req->dma, req->length,
+ ep->dwc_ep.
+ is_in ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE);
+ req->dma = DWC_DMA_ADDR_INVALID;
+ }
+ }
+
+ if (req && req->complete) {
+ switch (status) {
+ case -DWC_E_SHUTDOWN:
+ req->status = -ESHUTDOWN;
+ break;
+ case -DWC_E_RESTART:
+ req->status = -ECONNRESET;
+ break;
+ case -DWC_E_INVALID:
+ req->status = -EINVAL;
+ break;
+ case -DWC_E_TIMEOUT:
+ req->status = -ETIMEDOUT;
+ break;
+ default:
+ req->status = status;
+
+ }
+
+ req->actual = actual;
+ DWC_SPINUNLOCK(pcd->lock);
+ req->complete(ep_handle, req);
+ DWC_SPINLOCK(pcd->lock);
+
+ }
+
+ return 0;
+}
+
+static int _connect(dwc_otg_pcd_t * pcd, int speed)
+{
+ dwc_gadget->gadget.speed = speed;
+ return 0;
+}
+
+static int _disconnect(dwc_otg_pcd_t * pcd)
+{
+ if (dwc_gadget->driver && dwc_gadget->driver->disconnect) {
+ dwc_gadget->driver->disconnect(&dwc_gadget->gadget);
+ }
+ return 0;
+}
+
+static int _resume(dwc_otg_pcd_t * pcd)
+{
+ if (dwc_gadget->driver && dwc_gadget->driver->resume) {
+ dwc_gadget->driver->resume(&dwc_gadget->gadget);
+ }
+
+ return 0;
+}
+
+static int _suspend(dwc_otg_pcd_t * pcd)
+{
+ if (dwc_gadget->driver && dwc_gadget->driver->suspend) {
+ dwc_gadget->driver->suspend(&dwc_gadget->gadget);
+ }
+ return 0;
+}
+
+/**
+ * This function updates the otg values in the gadget structure.
+ */
+static int _hnp_changed(dwc_otg_pcd_t * pcd)
+{
+
+ if (!dwc_gadget->gadget.is_otg)
+ return 0;
+
+ dwc_gadget->gadget.b_hnp_enable = get_b_hnp_enable(pcd);
+ dwc_gadget->gadget.a_hnp_support = get_a_hnp_support(pcd);
+ dwc_gadget->gadget.a_alt_hnp_support = get_a_alt_hnp_support(pcd);
+ return 0;
+}
+
+static int _reset(dwc_otg_pcd_t * pcd)
+{
+ return 0;
+}
+
+#ifdef DWC_UTE_CFI
+static int _cfi_setup(dwc_otg_pcd_t * pcd, void *cfi_req)
+{
+ int retval = -DWC_E_INVALID;
+ if (dwc_gadget->driver->cfi_feature_setup) {
+ retval =
+ dwc_gadget->driver->
+ cfi_feature_setup(&dwc_gadget->gadget,
+ (struct cfi_usb_ctrlrequest *)cfi_req);
+ }
+
+ return retval;
+}
+#endif
+
+static const struct dwc_otg_pcd_function_ops fops = {
+ .complete = _complete,
+#ifdef DWC_EN_ISOC
+ .isoc_complete = _isoc_complete,
+#endif
+ .setup = _setup,
+ .disconnect = _disconnect,
+ .connect = _connect,
+ .resume = _resume,
+ .suspend = _suspend,
+ .hnp_changed = _hnp_changed,
+ .reset = _reset,
+#ifdef DWC_UTE_CFI
+ .cfi_setup = _cfi_setup,
+#endif
+#ifdef DWC_UTE_PER_IO
+ .xisoc_complete = _xisoc_complete,
+#endif
+};
+
+/**
+ * This function is the top level PCD interrupt handler.
+ */
+static irqreturn_t dwc_otg_pcd_irq(int irq, void *dev)
+{
+ dwc_otg_pcd_t *pcd = dev;
+ int32_t retval = IRQ_NONE;
+
+ retval = dwc_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;
+
+ 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.
+ */
+ 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].dwc_ep.num];
+ 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);
+ }
+
+ 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].dwc_ep.num];
+ 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);
+}
+
+static struct gadget_wrapper *alloc_wrapper(struct lm_device *_dev)
+{
+ static char pcd_name[] = "dwc_otg_pcd";
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev);
+
+ struct gadget_wrapper *d;
+ int retval;
+
+ d = DWC_ALLOC(sizeof(*d));
+ if (d == NULL) {
+ return NULL;
+ }
+
+ memset(d, 0, sizeof(*d));
+
+ d->gadget.name = pcd_name;
+ d->pcd = otg_dev->pcd;
+
+ dev_set_name(&d->gadget.dev, "%s", "gadget");
+
+ d->gadget.dev.parent = &_dev->dev;
+ d->gadget.dev.release = dwc_otg_pcd_gadget_release;
+ d->gadget.ops = &dwc_otg_pcd_ops;
+ /*modified by k3v2*/
+ d->gadget.sg_supported = 0;
+ d->gadget.max_speed = USB_SPEED_HIGH;
+ /*modified by k3v2*/
+ d->gadget.is_otg = dwc_otg_pcd_is_otg(otg_dev->pcd);
+
+ d->driver = 0;
+ /* Register the gadget device */
+ retval = device_register(&d->gadget.dev);
+ if (retval != 0) {
+ DWC_ERROR("device_register failed\n");
+ DWC_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 */
+ DWC_WARN("driver '%s' is still registered\n",
+ d->driver->driver.name);
+ usb_gadget_unregister_driver(d->driver);
+ }
+
+ device_unregister(&d->gadget.dev);
+ DWC_FREE(d);
+}
+
+/**
+ * This function initialized the PCD portion of the driver.
+ *
+ */
+int pcd_init(struct lm_device *_dev)
+{
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev);
+ int retval = 0;
+
+ printk(KERN_INFO ">>>>[%s]+\n", __func__);
+
+ otg_dev->pcd = dwc_otg_pcd_init(otg_dev->core_if);
+
+ if (!otg_dev->pcd) {
+ DWC_ERROR("dwc_otg_pcd_init failed\n");
+ return -ENOMEM;
+ }
+
+ otg_dev->pcd->otg_dev = otg_dev;
+ dwc_gadget = alloc_wrapper(_dev);
+
+ /*
+ * Initialize EP structures
+ */
+ gadget_add_eps(dwc_gadget);
+
+ /*
+ * Setup interupt handler
+ */
+ retval = request_irq(_dev->irq, dwc_otg_pcd_irq,
+ IRQF_SHARED | IRQF_DISABLED,
+ dwc_gadget->gadget.name, otg_dev->pcd);
+ if (retval != 0) {
+ DWC_ERROR("request of irq%d failed\n", _dev->irq);
+ free_wrapper(dwc_gadget);
+ return -EBUSY;
+ }
+
+ dwc_otg_pcd_start(dwc_gadget->pcd, &fops);
+
+ usb_add_gadget_udc(&_dev->dev, &dwc_gadget->gadget);
+
+ printk(KERN_INFO ">>>>[%s]-\n", __func__);
+
+ return retval;
+}
+
+/**
+ * Cleanup the PCD.
+ */
+void pcd_remove(struct lm_device *_dev)
+{
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev);
+ dwc_otg_pcd_t *pcd = otg_dev->pcd;
+ usb_del_gadget_udc(&dwc_gadget->gadget);
+ /*
+ * Free the IRQ
+ */
+ free_irq(_dev->irq, pcd);
+ dwc_otg_pcd_remove(otg_dev->pcd);
+ free_wrapper(dwc_gadget);
+ 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
+ */
+
+static int dwc_otg_pcd_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
+{
+ int retval;
+
+ if (g != &dwc_gadget->gadget) {
+ printk("gadget error!\n");
+ return -EINVAL;
+ }
+
+ if (!driver || driver->max_speed == USB_SPEED_UNKNOWN ||
+ !driver->disconnect || !driver->setup) {
+ printk(KERN_ERR "dwc_otg_pcd_start_up EINVAL\n");
+ return -EINVAL;
+ }
+
+ if (dwc_gadget == 0) {
+ printk(KERN_ERR "dwc_otg_pcd_start_up ENODEV\n");
+ return -ENODEV;
+ }
+ if (dwc_gadget->driver != 0) {
+ printk(KERN_ERR "dwc_otg_pcd_start_up EBUSY\n");
+ return -EBUSY;
+ }
+
+ printk(KERN_INFO "registering gadget driver '%s'\n",
+ driver->driver.name);
+
+ /* hook up the driver */
+ dwc_gadget->driver = driver;
+ dwc_gadget->gadget.dev.driver = &driver->driver;
+
+ return 0;
+}
+
+
+/**
+ * This function unregisters a gadget driver
+ *
+ * @param driver The driver being unregistered
+ */
+static int dwc_otg_pcd_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
+{
+
+ if (dwc_gadget == 0) {
+ return -ENODEV;
+ }
+ if (driver == 0 || driver != dwc_gadget->driver) {
+ return -EINVAL;
+ }
+ dwc_gadget->driver = 0;
+ return 0;
+}
+
+#endif /* DWC_HOST_ONLY */