This is where we implement the behavior of a USB device controller for
the MA USB device-side driver. The MA UDC interfaces with a gadget driver
and appears to the driver as a regular UDC. However, instead of sending
USB packets over a wired USB bus, the MA UDC hands MA USB packets off to
a media specific layer for transport.

Signed-off-by: Sean O. Stalley <sean.stal...@intel.com>
Signed-off-by: Stephanie Wallick <stephanie.s.wall...@intel.com>
---
 drivers/staging/mausb/drivers/mausb_udc.c | 1488 +++++++++++++++++++++++++++++
 drivers/staging/mausb/drivers/mausb_udc.h |  147 +++
 2 files changed, 1635 insertions(+)
 create mode 100644 drivers/staging/mausb/drivers/mausb_udc.c
 create mode 100644 drivers/staging/mausb/drivers/mausb_udc.h

diff --git a/drivers/staging/mausb/drivers/mausb_udc.c 
b/drivers/staging/mausb/drivers/mausb_udc.c
new file mode 100644
index 0000000..dd8bd02
--- /dev/null
+++ b/drivers/staging/mausb/drivers/mausb_udc.c
@@ -0,0 +1,1488 @@
+/* name:       mausb_udc.c
+ * description: Implements a USB device controller(UDC) for interfacing with
+ *             gadget drivers. The gadget driver uses this interface to return
+ *             descriptors and to implement configuration and data transfer
+ *             protocols with the pHCD. The UDC also allocates and initializes
+ *             endpoints to support gadget/pHCD interfacing.
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * Contact Information:
+ * Sean Stalley, sean.o.stal...@intel.com
+ * Stephanie Wallick, stephanie.s.wall...@intel.com
+ * 2111 NE 25th Avenue
+ * Hillsboro, Oregon 97124
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2014 Intel Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in
+      the documentation and/or other materials provided with the
+      distribution.
+    * Neither the name of Intel Corporation nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ */
+
+#define DEBUG
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ch11.h>
+#include <linux/usb/gadget.h>
+#include <linux/device.h>
+#include <linux/usb/composite.h>
+#include <linux/spinlock.h>
+
+#include "mausb_udc.h"
+#include "mausb_mgmt.h"
+#include "mausb_mem.h"
+#include "mausb_msapi.h"
+#include "mausb_tx.h"
+
+static const char       ep0[] = "ep0";
+static const char *const ep_name[] = {ep0, "ep1", "ep2"};
+
+static struct platform_device udc_pdev;
+
+void udc_dev_release(struct device *dev)
+{
+       /* free any dynamically allocated structures here */
+}
+
+static inline struct mausb_udc *gadget_to_udc(struct usb_gadget *gadget)
+{
+       return container_of(gadget, struct mausb_udc, gadget);
+}
+
+static inline struct mausb_udc *gadget_dev_to_mausb_udc(struct device *dev)
+{
+       return container_of(dev, struct mausb_udc, gadget.dev);
+}
+
+static inline struct mausb_request *usb_req_to_mausb_req(
+               struct usb_request *req)
+{
+       return container_of(req, struct mausb_request, req);
+}
+
+static inline struct mausb_udc *mausb_ma_dev_to_udc(struct ma_dev *ma_dev)
+{
+       return container_of(ma_dev, struct mausb_udc, ma_dev);
+}
+
+/*
+ * forward declarations
+ */
+static int set_or_clear_gadget_feature(struct usb_ctrlrequest *ctrlreq,
+               struct mausb_udc *udc, int set);
+static int get_gadget_status(struct usb_ctrlrequest *ctrlreq,
+               struct mausb_udc *udc);
+
+/**
+ * Finds a matching endpoint for a given address.
+ */
+static struct usb_ep *get_ep(struct mausb_udc *udc, u8 address)
+{
+       int                     i;
+       struct usb_ep           *ep;
+
+       /* address is 0 (ignore direction bit flag) */
+       if ((address & ~USB_DIR_IN) == 0)
+               return &udc->usb_ep[0].dev_ep;
+
+       /* otherwise look for match */
+       for (i = 1; i < MAUDC_MAX_NUM_EP; i++) {
+
+               ep = &udc->usb_ep[i].dev_ep;
+
+               if (ep->address == address)
+                       return ep;
+       }
+
+       maudc_dbg(udc, "%s: no endpoint found for address 0x%x\n",
+               __func__, address);
+
+       return NULL;
+}
+
+/**
+ * Called by gadget driver to enable endpoint.
+ */
+static int mausb_ep_enable(struct usb_ep *ep,
+               const struct usb_endpoint_descriptor *desc)
+{
+       struct mausb_udc                *udc = platform_get_drvdata(&udc_pdev);
+       struct mausb_host_ep            *ma_ep = mausb_find_ep_by_desc(desc,
+                                               udc->mausb_dev);
+
+       maudc_dbg(udc, "%s: enabling gadget endpoint %s\n",
+               __func__, ep->name);
+
+       /* ep0 is control endpoint, don't try and configure it */
+       if (ep->name == ep0) {
+               maudc_err(udc, "%s: cannot configure ep0\n", __func__);
+               return -EINVAL;
+       }
+
+       /*
+        * Get maximum packet size from descriptor and set for ep.
+        * Bits 0->10 of wMaxPacketSize = maximum packet size for HS and
+        * FS devices maximum packet size for SS devices = 1024.
+        */
+       ep->maxpacket = EP_MAX_PACKET;
+
+       if (NULL != ma_ep)
+               mausb_link_ma_ep_to_usb_ep(ma_ep, ep);
+
+       return 0;
+}
+
+/**
+ * Called by gadget driver to disable endpoint.
+ */
+static int mausb_ep_disable(struct usb_ep *ep)
+{
+       struct mausb_udc *udc = platform_get_drvdata(&udc_pdev);
+
+       if (!ep) {
+               maudc_dbg(udc, "%s: no endpoint\n", __func__);
+               return -EINVAL;
+       }
+
+       maudc_dbg(udc, "%s: disabling gadget endpoint %s\n",
+               __func__, ep->name);
+
+       return 0;
+}
+
+/**
+ * Called by gadget driver to allocate a request packet.
+ */
+static struct usb_request *mausb_ep_alloc_request(struct usb_ep *ep,
+               gfp_t mem_flags)
+{
+       struct mausb_request *req;
+
+       if (!ep)
+               return NULL;
+
+       req = kzalloc(sizeof(*req), mem_flags);
+       if (!req)
+               return NULL;
+
+       INIT_LIST_HEAD(&req->usb_req_list);
+
+       return &req->req;
+}
+
+/**
+ * Called by gadget driver to free a request packet.
+ */
+static void mausb_ep_free_request(struct usb_ep *ep, struct usb_request *req)
+{
+       struct mausb_request *mreq;
+
+       if (!ep || !req)
+               return;
+
+       mreq = usb_req_to_mausb_req(req);
+       kfree(mreq);
+}
+
+/**
+ * Called by gadget driver to queue a request.
+ */
+static int mausb_ep_queue(struct usb_ep *ep, struct usb_request *req,
+               gfp_t mem_flags)
+{
+       struct mausb_udc                *udc = platform_get_drvdata(&udc_pdev);
+       struct mausb_request            *ma_req;
+       struct mausb_gadget_ep          *gadget_ep;
+
+       if (!ep) {
+               maudc_err(udc, "%s: no endpoint\n", __func__);
+               return -EINVAL;
+       }
+
+       if (!req) {
+               maudc_err(udc, "%s: no USB request\n", __func__);
+               return -EINVAL;
+       }
+
+       ma_req = usb_req_to_mausb_req(req);
+       gadget_ep = usb_ep_to_mausb_gadget_ep(ep);
+
+       req->status = -EINPROGRESS;
+       req->actual = 0;
+
+       maudc_dbg(udc, "%s: queueing request to ep %i at 0x%p with data at "
+               "0x%p\n", __func__, usb_endpoint_num(ep->desc), ep, req->buf);
+
+       list_add_tail(&ma_req->usb_req_list, &gadget_ep->usb_req_list);
+
+       return 0;
+}
+
+/**
+ * Called by gadget driver to dequeue a request.
+ */
+static int mausb_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+       struct mausb_udc                *udc = platform_get_drvdata(&udc_pdev);
+       struct mausb_request            *ma_req;
+
+       if (!ep) {
+               maudc_err(udc, "%s: no endpoint\n", __func__);
+               return -EINVAL;
+       }
+
+       if (!req) {
+               maudc_err(udc, "%s: no USB request\n", __func__);
+               return -EINVAL;
+       }
+
+       ma_req = usb_req_to_mausb_req(req);
+
+       list_del(&ma_req->usb_req_list);
+
+       maudc_dbg(udc, "%s: dequeueing request\n", __func__);
+
+       return 0;
+}
+
+/**
+ * Halts or clears a halt of an endpoint.
+ */
+static int mausb_ep_set_halt(struct usb_ep *ep, int value)
+{
+       struct mausb_udc        *udc = platform_get_drvdata(&udc_pdev);
+       struct mausb_host_ep    *ma_ep = mausb_find_ep_dev(ep, udc->mausb_dev);
+
+       if (NULL == ma_ep) {
+               maudc_err(udc, "%s: no MA USB endpoint\n", __func__);
+               return -EINVAL;
+       }
+
+       /* probably don't want to halt default control ep */
+       if (ma_ep->dev_ep->name == ep0) {
+               maudc_err(udc, "%s: cannot halt ep0!\n", __func__);
+               return -EINVAL;
+       }
+
+       if (value) {
+               maudc_dbg(udc, "%s: halting ep\n", __func__);
+               ma_ep->halted = 1;
+       }
+
+       else {
+               maudc_dbg(udc, "%s: clearing ep halt\n", __func__);
+               ma_ep->halted = 0;
+       }
+
+       return 0;
+}
+
+/**
+ * Wedges an endpoint; wedge causes request to clear halt from host to be
+ * ignored.
+ */
+static int mausb_ep_set_wedge(struct usb_ep *ep)
+{
+       struct mausb_udc        *udc = platform_get_drvdata(&udc_pdev);
+       struct mausb_host_ep    *ma_ep = mausb_find_ep_dev(ep, udc->mausb_dev);
+
+       if (NULL == ma_ep) {
+               maudc_err(udc, "%s: no MA USB endpoint\n", __func__);
+               return -EINVAL;
+       }
+
+       /* wedging default control ep is probably bad */
+       if (ma_ep->dev_ep->name == ep0) {
+               maudc_dbg(udc, "%s: cannot wedge ep0\n", __func__);
+               return -EINVAL;
+       }
+
+       ma_ep->halted = 1;
+       ma_ep->wedged = 1;
+
+       return 0;
+}
+
+static struct usb_ep_ops mausb_ep_ops = {
+       .enable = mausb_ep_enable,
+       .disable = mausb_ep_disable,
+       .alloc_request = mausb_ep_alloc_request,
+       .free_request = mausb_ep_free_request,
+       .queue = mausb_ep_queue,
+       .dequeue = mausb_ep_dequeue,
+       .set_halt = mausb_ep_set_halt,
+       .set_wedge = mausb_ep_set_wedge
+};
+
+/*---------------------- transfer operations ------------------------------*/
+
+/**
+ * Handles control requests from usb core. Returns 0 if request is fully
+ * handled, otherwise returns the length of the transfer
+ */
+static int handle_ma_control_request(struct mausb_host_ep *ep,
+               struct usb_ctrlrequest *ctrlreq)
+{
+       int                     ret = 0;
+       struct mausb_udc        *udc = platform_get_drvdata(&udc_pdev);
+
+       if (!ctrlreq) {
+               maudc_err(udc, "%s: no control request\n", __func__);
+               return -EINVAL;
+       }
+
+       /*
+        * UDC handles set/clear feature, get status, and set address
+        * requests, anything else is passed to gadget driver.
+        */
+       switch (ctrlreq->bRequest) {
+       case USB_REQ_CLEAR_FEATURE:
+               maudc_dbg(udc, "USB_REQ: clearing feature\n");
+               ret = set_or_clear_gadget_feature(ctrlreq, udc, 0);
+               break;
+
+       case USB_REQ_GET_STATUS:
+               maudc_dbg(udc, "USB_REQ: getting status\n");
+               ret = get_gadget_status(ctrlreq, udc);
+               break;
+
+       case USB_REQ_SET_FEATURE:
+               maudc_dbg(udc, "USB_REQ: setting feature\n");
+               ret = set_or_clear_gadget_feature(ctrlreq, udc, 1);
+               break;
+
+       default: /* pass the request to the gadget driver */
+               maudc_dbg(udc, "USB_REQ: forwarding REQ #%i to gadget driver\n",
+                               ctrlreq->bRequest);
+               if (NULL != udc->driver)
+                       ret = udc->driver->setup(&udc->gadget, ctrlreq);
+               else
+                       maudc_err(udc, "gadget driver not found\n");
+
+               if (ret >= 0)
+                       ret = ctrlreq->wLength;
+               break;
+       }
+
+       return ret;
+}
+
+/**
+ * Transfers contents of request and endpoint buffers.
+ */
+int do_ma_transfer(struct mausb_host_ep *ep, struct mausb_pkt *tx_req,
+               bool dir_in)
+{
+       int                             i = 0;
+       int                             out_copy_length = 0;
+       int                             ret, host_len, device_len;
+       int                             status = -EINPROGRESS;
+       int                             length = 0;
+       struct mausb_transfer_state     *tx_state = &ep->active_transfer->state;
+       struct mausb_request            *req, *next;
+       struct mausb_udc                *udc = platform_get_drvdata(&udc_pdev);
+       void                            *epbuf, *rbuf;
+
+       /* look for setup data */
+       if (tx_req->setup)
+               ret = handle_ma_control_request(ep, tx_req->setup);
+
+       if (NULL == ep->usb_req_list)
+               return -ENOTCONN;
+
+       if (list_empty(ep->usb_req_list) && !in_interrupt()) {
+               maudc_dbg(udc, "wait gadget function driver submit request\n");
+               while (list_empty(ep->usb_req_list) &&
+                      i <= MAUSB_GADGET_TIMEOUT) {
+                       msleep(1);
+                       i++;
+               }
+       }
+       if (i > MAUSB_GADGET_TIMEOUT) {
+               maudc_dbg(udc, "Wait gadget request time out\n");
+               return -ETIMEDOUT;
+       }
+
+ loop_request_queue:
+       /* take care of each request in request queue */
+       list_for_each_entry_safe(req, next, ep->usb_req_list, usb_req_list) {
+
+               if (dir_in)
+                       host_len = tx_state->rem_size - ep->actual_length;
+               else
+                       host_len = tx_req->buffer_length - out_copy_length;
+               device_len = req->req.length - req->req.actual;
+               length = min(host_len, device_len);
+
+               /* there is something to transfer so do transfer */
+               if (length > 0) {
+
+                       rbuf = req->req.buf + req->req.actual;
+
+                       /*
+                        * If IN transfer, copy req buffer contents into
+                        * ep buffer. Vice versa for OUT transfer.
+                        */
+                       if (dir_in) {
+                               epbuf = ep->buffer + ep->actual_length;
+                               if (epbuf)
+                                       memcpy(epbuf, rbuf, length);
+                               else
+                                       maudc_err(udc, "%s: no ep buffer\n",
+                                               __func__);
+                       } else {
+                               /*
+                                * For bulk OUT, we can't use ep->actual_length
+                                * as indicator because ep->buffer for every
+                                * transfer request is only "rx_buf_size" bytes
+                                * length.
+                                */
+                               epbuf = ep->buffer + out_copy_length;
+                               memcpy(rbuf, epbuf, length);
+                               out_copy_length += length;
+                       }
+                       ep->actual_length += length;
+                       req->req.actual += length;
+               }
+               /* zero length packet ends a read */
+               else {
+                       req->req.status = 0;
+                       status = 0;
+               }
+
+               /*
+                * A transfer usually ends when buffer contents are completely
+                * transferred. But, look out for for the zero packet transfer
+                * flag. If flag is set, bulk OUT transfers need to wait for
+                * zero length packet (short packet) for completion.
+                */
+               if ((req->req.length == req->req.actual) ||
+                   /* bulk IN short packet */
+                   (dir_in && (req->req.actual % ep->dev_ep->maxpacket)) ||
+                   /* bulk OUT transfer all data requested by host urb*/
+                   (!dir_in && ep->active_transfer->transfer_size
+                    == ep->actual_length) ||
+                   /* bulk OUT last request */
+                   (!dir_in && req->req.zero)) {
+                       req->req.status = 0;
+                       /* device completion */
+                       list_del_init(&req->usb_req_list);
+                       req->req.complete(ep->dev_ep, &req->req);
+               }
+
+               if (tx_state->rem_size == 0)
+                       status = 0;
+
+               /*
+                * Host completion - end when nothing left to read/write,
+                * otherwise keep going through queue.
+                */
+               if (status != -EINPROGRESS)
+                       break;
+               /* bulk IN/OUT transfers last request serves for one host urb */
+               if (tx_state->rem_size == req->req.actual) {
+                       maudc_dbg(udc, "bulk IN/OUT last request return\n");
+                       break;
+               }
+               /* bulk IN short packet */
+               if (dir_in && (req->req.actual % ep->dev_ep->maxpacket)) {
+                       maudc_dbg(udc, "bulk IN short packet return\n");
+                       break;
+               }
+               /*
+                * Bulk OUT transfer contains multiple bulk OUT MA requests.
+                * So for bulk out MA request just return to serve subsequent
+                * bulk out MA request.
+                */
+               if (!dir_in && host_len == length) {
+                       maudc_dbg(udc, "normal bulk OUT MA request return\n");
+                       break;
+               }
+               /*
+                * Gadget driver should transfer more data to
+                * serve bulk IN transfer, then wait for more requests.
+                */
+               if (dir_in && (req->req.actual % ep->dev_ep->maxpacket == 0) &&
+                   (tx_state->rem_size - ep->actual_length > 0)
+                   && !req->req.zero) {
+                       if (list_empty(ep->usb_req_list)) {
+                               maudc_dbg(udc, "bulk IN: gadget request queue"
+                                       " is empty - waiting . . .\n");
+                               while (list_empty(ep->usb_req_list))
+                                       msleep(1);
+                       }
+                       goto loop_request_queue;
+               }
+               /*
+                * Doesn't copy all data in MA USB bulk OUT request.
+                * Wait for additional gadget requests
+                * to serve this MA USB bulk OUT request.
+                */
+               if (!dir_in && (host_len > length)) {
+                       if (list_empty(ep->usb_req_list)) {
+                               maudc_dbg(udc, "bulk OUT: gadget request"
+                                       " is queue empty - waiting . . .\n");
+                               while (list_empty(ep->usb_req_list))
+                                       msleep(1);
+                       }
+                       goto loop_request_queue;
+               }
+       }
+
+       return ep->actual_length;
+}
+
+
+/*------------------------- gadget operations -------------------------*/
+
+/**
+ * Handles control request to get device status.
+ */
+static int get_gadget_status(struct usb_ctrlrequest *ctrlreq,
+               struct mausb_udc *udc)
+{
+       int                     ret = 0;
+       char                    *buf;
+       unsigned int            address = le16_to_cpu(ctrlreq->wIndex);
+       struct usb_ep           *usb_ep = get_ep(udc, address);
+       struct mausb_host_ep    *ma_ep = mausb_find_ep_dev(usb_ep,
+                                       udc->mausb_dev);
+
+       if (!ma_ep) {
+               maudc_err(udc, "%s: cannot find endpoint\n", __func__);
+               return -EINVAL;
+       }
+
+       buf = (char *)ma_ep->buffer;
+
+       maudc_dbg(udc, "%s: responding to request type %i\n",
+                       __func__, ctrlreq->bRequestType);
+
+       /* requesting device status */
+       switch (ctrlreq->bRequestType) {
+       case DEV_IN_REQ:
+               buf[0] = udc->dev_status;
+               ma_ep->actual_length = 1;
+               break;
+
+       /* requesting an endpoint status */
+       case EP_IN_REQ:
+               buf[0] = ma_ep->halted;
+               ma_ep->actual_length = 1;
+               break;
+
+       /* requesting interface status */
+       case INTF_IN_REQ:
+               buf[0] = 0;
+               break;
+
+       default:
+               maudc_dbg(udc, "%s: invalid request type %i\n",
+                       __func__, ctrlreq->bRequestType);
+               ret = -EOPNOTSUPP;
+               break;
+       }
+
+       return ret;
+}
+
+/**
+ * Handles control request to set a gadget device feature.
+ */
+static int set_or_clear_gadget_feature(struct usb_ctrlrequest *ctrlreq,
+               struct mausb_udc *udc, int set)
+{
+       int                     ret = 0;
+       unsigned int            address = le16_to_cpu(ctrlreq->wIndex);
+       struct usb_ep           *usb_ep = get_ep(udc, address);
+       struct mausb_host_ep    *ma_ep  = mausb_find_ep_dev(usb_ep,
+                                       udc->mausb_dev);
+
+       if (ctrlreq->bRequestType == EP_REQ) {
+
+               if (!ma_ep)
+                       ret = -EINVAL;
+               else if (ma_ep->dev_ep->name == udc->gadget.ep0->name) {
+                       maudc_err(udc, "%s: cannot halt ep0!\n", __func__);
+                       ret = -EINVAL;
+               } else {
+                       ma_ep->halted = set;
+                       maudc_dbg(udc, "%s: %s halt %s\n", __func__,
+                               ma_ep->dev_ep->name, set ? "set" : "cleared");
+               }
+       }
+
+       return ret;
+}
+
+/**
+ * Handles incoming management packets. Used to pass an MA USB management
+ * packet to the device.
+ */
+static int maudc_transfer_mgmt_packet(struct ms_pkt *ms_pkt, void *context)
+{
+       int                     ret;
+       struct mausb_pkt        *pkt;
+       struct mausb_udc        *udc  = platform_get_drvdata(&udc_pdev);
+       struct mausb_mgmt       *mgmt;
+
+       if ((NULL == context) || (NULL == ms_pkt)) {
+               maudc_err(udc, "%s: received NULL %s\n", __func__,
+                       context ? "packet" : "context");
+               return -EFAULT;
+       }
+
+       mgmt = (struct mausb_mgmt *) context;
+
+       pkt = mausb_pkt_from_ms_pkt(ms_pkt,
+                       udc->ma_dev.ms_driver->ops->pkt_destructor, GFP_ATOMIC);
+
+       maudc_dbg(udc, "%s: received %s packet\n", __func__,
+               mausb_type_to_string(pkt->common->pkt_type));
+
+       ret = mausb_rx_mgmt(pkt, mgmt);
+
+       return ret;
+}
+
+/**
+ * Suspends gadget..Called when a MAUSBDeviceSleepReq management packet is
+ * received.
+ */
+static void mausb_device_suspend(void)
+{
+       struct mausb_udc *udc = platform_get_drvdata(&udc_pdev);
+
+       if (udc->driver)
+               udc->driver->suspend(&udc->gadget);
+}
+
+/**
+ * Resumes gadget. Called by mausb_bus_resume in hcd to resume a connected
+ * device.
+ */
+static void mausb_device_resume(void)
+{
+       struct mausb_udc *udc = platform_get_drvdata(&udc_pdev);
+
+       if (udc->driver)
+               udc->driver->resume(&udc->gadget);
+}
+
+/**
+ * Manages USB device connection to host. Called for connect and disconnect
+ * events.
+ */
+static int mausb_udc_pullup(struct usb_gadget *gadget, int is_on)
+{
+       struct mausb_udc *udc = gadget_to_udc(gadget);
+
+       udc->usb_ep[0].dev_ep.maxpacket = 64;
+       udc->gadget.speed = udc->driver->max_speed;
+       udc->pullup = is_on;
+
+       return 0;
+}
+
+/**
+ * Assigns an MA USB ep handle to the given endpoint.
+ */
+static void maudc_assign_ep_handle(struct mausb_host_ep *ma_ep,
+               struct usb_endpoint_descriptor *ep_des)
+{
+       struct mausb_ep_handle *handle = &ma_ep->ep_handle;
+
+       handle->dir = usb_endpoint_dir_in(ep_des) ? 1 : 0;
+       handle->ep_num = usb_endpoint_num(ep_des);
+       handle->dev_addr = MAUDC_DEV_ADDR;
+       handle->bus_num = MAUSB_VIRTUAL_BUS_NUM;
+
+       ma_ep->ep_handle_state = MAUSB_EP_HANDLE_ACTIVE;
+}
+
+/**
+ * Adds a media-agnostic endpoint datastructure to the UDC for the given
+ * endpoint.
+ */
+static int maudc_add_ma_ep(struct usb_endpoint_descriptor *ep_des,
+               struct mausb_dev *mausb_dev, struct mausb_host_ep **mausb_ep)
+{
+       int                     ret = -EINVAL;
+       struct mausb_host_ep    *ma_ep;
+       struct mausb_ms_drv     *drv = mausb_dev->ma_dev->ms_driver;
+       struct mausb_udc        *udc = platform_get_drvdata(&udc_pdev);
+       struct usb_ep           *usb_ep = get_ep(udc, ep_des->bEndpointAddress);
+       unsigned long           irq_flags;
+       int (*transfer_pkt)(struct ms_pkt *pkt, void *context);
+
+       if (USB_ENDPOINT_XFER_CONTROL == usb_endpoint_type(ep_des)) {
+
+               maudc_dbg(udc, "%s: adding control endpoint %i...\n",
+                       __func__, usb_endpoint_num(ep_des));
+               transfer_pkt = &receive_ma_packet_control;
+
+       } else if (usb_endpoint_dir_in(ep_des)) {
+               maudc_dbg(udc, "%s: adding IN endpoint %i...\n",
+                       __func__, usb_endpoint_num(ep_des));
+               transfer_pkt = &receive_ma_packet_IN;
+
+       } else if (usb_endpoint_dir_out(ep_des)) {
+               maudc_dbg(udc, "%s: adding OUT endpoint %i...\n",
+                       __func__, usb_endpoint_num(ep_des));
+               transfer_pkt = &receive_ma_packet_OUT;
+
+       } else {
+               maudc_dbg(udc, "%s: unknown endpoint type\n", __func__);
+               return -EINVAL;
+       }
+
+       mausb_internal_add_ep(mausb_dev, NULL, usb_ep, &ma_ep,
+               &device_transfer_timeout, GFP_ATOMIC);
+
+       if (NULL == ma_ep)
+               return -ENOMEM;
+
+       maudc_dbg(udc, "%s: added endpoint at 0x%p\n", __func__, ma_ep);
+
+       spin_lock_irqsave(&ma_ep->ep_lock, irq_flags);
+
+       maudc_assign_ep_handle(ma_ep, ep_des);
+       ret = mausb_add_data_channel(drv, ma_ep, transfer_pkt);
+
+       ma_ep->gadget = mausb_dev->gadget;
+
+       /*
+        * Note: The UDC uses a single MA URB to track the transfer
+        * State variables for the active transfer.
+        */
+       ma_ep->active_transfer = mausb_alloc_maurb(ma_ep, GFP_ATOMIC);
+
+       if (NULL != mausb_ep)
+               *mausb_ep = ma_ep;
+
+       spin_unlock_irqrestore(&ma_ep->ep_lock, irq_flags);
+
+       return ret;
+}
+
+/**
+ * Registers a media specific driver with the media agnostic driver.
+ */
+struct mausb_ma_drv *maudc_register_ms_driver(struct mausb_ms_drv *drv)
+{
+       struct mausb_udc        *udc = platform_get_drvdata(&udc_pdev);
+       unsigned long           irq_flags;
+
+       spin_lock_irqsave(&udc->udc_lock, irq_flags);
+       udc->ma_dev.ms_driver = drv;
+       spin_unlock_irqrestore(&udc->udc_lock, irq_flags);
+
+       /* open the management channel */
+       maudc_dbg(udc, "%s: adding Managment channel...\n", __func__);
+       mausb_add_mgmt_channel(&udc->ma_dev.mgmt, drv,
+               &maudc_transfer_mgmt_packet, &mausb_mgmt_pkt_sent);
+
+       return &udc->ma_dev.ma_drv;
+}
+EXPORT_SYMBOL(maudc_register_ms_driver);
+
+int check_for_device(int dont_use)
+{
+       struct mausb_udc *udc = platform_get_drvdata(&udc_pdev);
+
+       if (udc->driver != NULL)
+               return 1;
+
+       return 0;
+}
+
+static int mausb_udc_start(struct usb_gadget *gadget,
+               struct usb_gadget_driver *driver)
+{
+       struct mausb_udc        *udc = gadget_to_udc(gadget);
+
+       maudc_dbg(udc, "%s: binding gadget '%s' driver\n",
+               __func__, driver->driver.name);
+
+       udc->driver = driver;
+
+       return 0;
+}
+
+static int mausb_udc_stop(struct usb_gadget *gadget,
+               struct usb_gadget_driver *driver)
+{
+       struct mausb_udc        *udc = gadget_to_udc(gadget);
+
+       maudc_dbg(udc, "%s: stopping udc\n", __func__);
+
+       udc->driver = NULL;
+
+       return 0;
+}
+
+/**
+ * API to UDC operations that do not involve endpoints or i/o.
+ */
+static struct usb_gadget_ops mudc_ops = {
+       .pullup = &mausb_udc_pullup,
+       .udc_start = &mausb_udc_start,
+       .udc_stop = &mausb_udc_stop
+};
+
+/**
+ * Appends a Capabilities Descriptor to the end of a CapResp packet.
+ *
+ * @resp:           CapResp to that will carry descriptor.
+ * @type:           Descriptor type to add.
+ *                  Note: make sure to use values from MA USB Spec, Table 14.
+ * @offset:          Total length of other Device Capability Descriptors
+ *                  in resp.
+ *
+ * Returns length of added descriptor.
+ */
+static int add_cap_desc(struct mausb_mgmt_pkt *resp, int type, int offset)
+{
+       int                     ret = 0;
+       int                     ttl_offset;
+       struct                  mausb_dev_cap_desc *desc;
+       struct mausb_udc        *udc = platform_get_drvdata(&udc_pdev);
+
+       ttl_offset = MAUSB_PKT_HEADER_SIZE +
+                       sizeof(struct mausb_CapResp_flds) + offset;
+       desc = cap_desc_ptr_increment(resp, ttl_offset);
+
+       switch (type) {
+       case MAUSB_DEV_CAP_SPEED:
+               desc->length = MAUSB_DEV_CAP_SPEED_LENGTH;
+               desc->cap_type = MAUSB_DEV_CAP_SPEED;
+               desc->speed.speed = MAUSB_DEV_CAP_SPEED_SUPER;
+               /* TODO: assign speed fields meaningful values */
+               desc->speed.lse = 1;
+               desc->speed.st = 2;
+               desc->speed.lane_count = 3;
+               desc->speed.link_protocol = 3;
+               desc->speed.lsm = 234;
+               ret = desc->length;
+               break;
+       case MAUSB_DEV_CAP_POUT:
+               break;
+       case MAUSB_DEV_CAP_ISO:
+               break;
+       case MAUSB_DEV_CAP_SYNC:
+               break;
+       case MAUSB_DEV_CAP_CONT_ID:
+               break;
+       case MAUSB_DEV_CAP_LINK_SLEEP:
+               desc->length = MAUSB_DEV_CAP_LINK_SLEEP_LENGTH;
+               desc->cap_type = MAUSB_DEV_CAP_LINK_SLEEP;
+               desc->link_sleep.link_sleep_cap = 0;
+               ret = desc->length;
+               break;
+       default:
+               maudc_err(udc, "%s: invalid Device Capability Type %i\n",
+                       __func__, type);
+               break;
+       }
+
+       return ret;
+}
+
+/**
+ * Finds an MA USB endpoint descriptor inside of an endpoint handle response.
+ */
+static void maudc_fill_ma_ep_desc(struct mausb_ep_des *ma_ep_des,
+               struct usb_endpoint_descriptor *ep_des,
+               struct mausb_host_ep *ma_ep)
+{
+       ma_ep_des->ep_handle.handle = ma_ep->ep_handle.handle;
+       ma_ep_des->dir = usb_endpoint_dir_in(ep_des) ? 1 : 0;
+       ma_ep_des->iso = usb_endpoint_xfer_isoc(ep_des) ? 1 : 0;
+       ma_ep_des->l_man = 0;
+       ma_ep_des->valid = 0;
+       ma_ep_des->rsvd_0 = 0;
+
+       if ((usb_endpoint_dir_out(ep_des) && !usb_endpoint_xfer_isoc(ep_des))
+                       || usb_endpoint_xfer_control(ep_des)) {
+               ma_ep_des->ccu = 1;
+       } else
+               ma_ep_des->ccu = 0;
+
+       ma_ep_des->rsvd_1 = 0;
+
+       if (usb_endpoint_dir_out(ep_des) || usb_endpoint_xfer_control(ep_des))
+               ma_ep_des->buffer_size = ma_ep->state.rx_buf_size;
+       else
+               ma_ep_des->buffer_size = 0;
+
+       if (usb_endpoint_xfer_isoc(ep_des)) {
+               /* TODO: iso transfers */
+               /* ma_ep_des->iso_prog_dly = 0; */
+               /* ma_ep_des->iso_resp_dly = 0; */
+       } else {
+               ma_ep_des->iso_prog_dly = 0;
+               ma_ep_des->iso_resp_dly = 0;
+       }
+}
+
+/**
+ * Handles incoming EPHandleReq packets and fills EPHandleResp fields.
+ */
+static enum mausb_pkt_status maudc_handle_ep_req(struct mausb_dev *mausb_dev,
+               struct mausb_EPHandleReq_flds *req,
+               struct mausb_EPHandleResp_flds *resp)
+{
+       int                             i;
+       enum mausb_pkt_status           status = UNSUCCESSFUL;
+       struct mausb_udc                *udc = platform_get_drvdata(&udc_pdev);
+       struct usb_endpoint_descriptor  *ep_des;
+       struct usb_ep                   *usb_ep;
+       struct mausb_host_ep            *ma_ep;
+
+       resp->num_ep_des = req->num_ep_des;
+
+       /* TODO: Sanity check; make sure we dont look past end of packet */
+       for (i = 0; i < req->num_ep_des; ++i) {
+
+               ep_des = mausb_ep_handle_req_get_ep_des(req, i);
+
+               if (NULL != ep_des) {
+
+                       ma_ep = mausb_find_ep_by_address(
+                               ep_des->bEndpointAddress, mausb_dev);
+
+                       if (NULL != ma_ep) {
+                               maudc_warn(udc, "%s: endpoint already exists"
+                                       "  resetting . . .\n", __func__);
+                               mausb_internal_drop_ep(ma_ep);
+                       }
+
+                       maudc_add_ma_ep(ep_des, mausb_dev, &ma_ep);
+
+                       if (NULL == ma_ep) {
+                               maudc_err(udc, "%s: failed to add endpoint"
+                                       " for entry %i\n", __func__, i);
+                       }
+
+                       maudc_fill_ma_ep_desc(&resp->ep_des[i], ep_des, ma_ep);
+
+                       usb_ep = get_ep(udc, ep_des->bEndpointAddress);
+
+                       if (NULL != usb_ep)
+                               mausb_link_ma_ep_to_usb_ep(ma_ep, usb_ep);
+
+                       /* Per spec, if at least 1 endpoint assignment
+                        * is sucessful, the packet returns success.
+                        */
+                       status = SUCCESS;
+               }
+       }
+
+       return status;
+}
+
+/**
+ * Handles incoming EPHandleDeleteReq packets and fills EPHandleDeleteResp
+ * fields.
+ */
+static enum mausb_pkt_status maudc_handle_ep_del_req(
+               struct mausb_dev *mausb_dev,
+               struct mausb_EPHandleDelete_flds *req,
+               struct mausb_EPHandleDelete_flds *resp)
+{
+       int                             i;
+       int                             ret = 0;
+       enum mausb_pkt_status           status = UNSUCCESSFUL;
+       struct mausb_udc                *udc = platform_get_drvdata(&udc_pdev);
+       struct mausb_host_ep            *ma_ep = NULL;
+       struct mausb_ep_handle          *req_handle;
+
+       resp->num_ep = 0;
+
+       /* TODO: Sanity check; make sure we dont look past end of packet */
+       for (i = 0; i < req->num_ep; ++i) {
+               req_handle = &req->ep_handle[i];
+
+               ma_ep = mausb_find_ep_by_handle(req_handle, mausb_dev);
+
+               ret = mausb_internal_drop_ep(ma_ep);
+
+               if (0 <= ret) {
+                       /*
+                        * per spec, the packet returns sucess if it can
+                        * delete at least 1 endpoint
+                        */
+                       status = SUCCESS;
+
+               } else {
+                       resp->ep_handle[resp->num_ep].handle
+                                               = req_handle->handle;
+                       resp->num_ep++;
+
+                       maudc_err(udc, "%s: could not delete endpoint with"
+                               " handle %x\n", __func__, req_handle->handle);
+               }
+       }
+
+       return status;
+}
+
+/**
+ * Inactivate endpoints in EPInactivateReq packet and fill
+ * EPInactivateResp fields.
+ */
+static enum mausb_pkt_status maudc_handle_ep_inactivate(
+               struct mausb_EPInactivateReq_flds *req,
+               struct mausb_EPInactivateResp_flds *resp,
+               struct mausb_dev *dev)
+{
+       int                     i = 0;
+       int                     failures = 0;
+       enum                    mausb_pkt_status status = SUCCESS;
+       struct mausb_host_ep    *ma_ep = NULL;
+       struct mausb_ep_handle  *req_handle;
+
+       for (i = 0; i < req->num_ep_handles; ++i) {
+               req_handle = &req->ep_handle[i];
+               ma_ep = mausb_find_ep_by_handle(req_handle, dev);
+
+               if (!ma_ep) {
+                       resp->ep_handle[failures] = *req_handle;
+                       status = UNSUCCESSFUL;
+                       failures++;
+               } else
+                       ma_ep->ep_handle_state = MAUSB_EP_HANDLE_INACTIVE;
+       }
+
+       resp->num_ep_with_error = failures;
+
+       return status;
+}
+
+/**
+ * This function handles management requests to the UDC. It parses the packet
+ * and makes the necessary function calls into the system.
+ *
+ * @req:       The incoming request to handle.
+ * @resp:      The response packet to send out.
+ * @mgmt:      The MA USB management structure associated with transfer.
+ *
+ * Note: this function only fills the packet type, status and
+ * request-specific data-fields. The remainder of the fields are set by
+ * mausb_rx_mgmt_req.
+ */
+static int udc_mgmt_req_switch(struct mausb_mgmt_pkt *req,
+               struct mausb_mgmt_pkt *resp, struct mausb_mgmt *mgmt)
+{
+       int                     ret = -EINVAL;
+       int                     desc_count = 0;
+       int                     offset = 0;
+       struct ma_dev           *ma_dev = mausb_mgmt_to_ma_dev(mgmt);
+       struct mausb_udc        *udc = mausb_ma_dev_to_udc(ma_dev);
+
+       /* always set the device handle */
+       req->common.dev_handle = udc->mausb_dev->dev_handle;
+
+       switch (req->common.pkt_type) {
+
+       case CapReq:
+               /* non-hub devices require a Speed Capability Descriptor */
+               ret = add_cap_desc(resp, MAUSB_DEV_CAP_SPEED, offset);
+               if (ret <= 0) {
+                       maudc_err(udc, "%s: could not add speed capability "
+                               "descriptor\n", __func__);
+               } else {
+                       desc_count++;
+                       offset += ret;
+               }
+
+               ret = add_cap_desc(resp, MAUSB_DEV_CAP_LINK_SLEEP, offset);
+               if (ret <= 0) {
+                       maudc_err(udc, "%s; could not add link sleep"
+                                " capability descriptor\n", __func__);
+               } else {
+                       desc_count++;
+                       offset += ret;
+               }
+
+               /*
+                * TODO: if want to add more device cap descriptors, this would
+                * be a good place to do it.
+                */
+
+               resp->common.pkt_type = CapResp;
+               resp->common.pkt_status = NO_ERROR;
+               resp->cap_resp.num_ep = CAP_MAX_EP_NUM;
+               resp->cap_resp.num_dev = CAP_MAX_DEV_NUM;
+               resp->cap_resp.num_stream = EP_MAX_STREAMS;
+               resp->cap_resp.dev_type = CAP_DEV_TYPE;
+               resp->cap_resp.desc_count = desc_count;
+               resp->cap_resp.desc_length = offset;
+               resp->cap_resp.max_tx_reqs = CAP_MAX_TX_REQS;
+               resp->cap_resp.max_mgmt_reqs = CAP_MAX_MGMT_REQS;
+
+               ret = 0;
+               break;
+       case USBDevHandleReq:
+               udc->mausb_dev->dev_handle = MAUDC_DEV_HANDLE;
+
+               resp->common.pkt_type     = USBDevHandleResp;
+               resp->common.pkt_status   = NO_ERROR;
+               resp->usb_dev_handle_resp = MAUDC_DEV_HANDLE;
+
+               ret = 0;
+               break;
+       case EPHandleReq:
+               resp->common.pkt_type   = EPHandleResp;
+               resp->usb_dev_handle_resp = udc->mausb_dev->dev_handle;
+               resp->common.pkt_status = maudc_handle_ep_req(udc->mausb_dev,
+                       &req->ep_handle_req, &resp->ep_handle_resp);
+               ret = 0;
+               break;
+       case EPActivateReq:
+               break;
+       case EPInactivateReq:
+               resp->common.pkt_type   = EPInactivateResp;
+               resp->common.pkt_status = maudc_handle_ep_inactivate(
+                                               &req->ep_inactivate_req,
+                                               &resp->ep_inactivate_resp,
+                                               udc->mausb_dev);
+               ret = 0;
+               break;
+       case EPResetReq:
+               break;
+       case EPClearTransferReq:
+               break;
+       case EPHandleDeleteReq:
+               resp->common.pkt_type   = EPHandleDeleteResp;
+
+               resp->common.pkt_status = maudc_handle_ep_del_req(
+                       udc->mausb_dev, &req->ep_handle_delete,
+                                       &resp->ep_handle_delete);
+               ret = 0;
+               break;
+       case DevResetReq:
+               resp->common.pkt_type   = DevResetResp;
+
+               ma_dev->ma_dev_addr = req->common.ma_dev_addr;
+               ma_dev->mass_id = req->common.mass_id;
+
+               ret = 0;
+
+               if (0 == ret) {
+                       resp->common.pkt_status = NO_ERROR;
+               } else {
+                       resp->common.pkt_status =
+                               mausb_errno_to_ma_status(ret);
+               }
+               break;
+       case ModifyEP0Req:
+               resp->common.pkt_type   = ModifyEP0Resp;
+               resp->common.pkt_status = SUCCESS;
+
+               /*
+                * TODO: fix so to spec - for now we don't track USB device
+                * state and can't do proper checks.
+                */
+               if (req->modify_ep0_req.ep_handle.dev_addr == 0) {
+                       /* && USB device is in addressed state) { */
+                       resp->modify_ep0_resp.ep_handle.handle =
+                               req->modify_ep0_req.ep_handle.handle
+                                       | (MAUDC_DEV_ADDR << 5);
+               } else {
+                       resp->modify_ep0_resp.ep_handle.handle = 0;
+               }
+
+               ret = 0;
+               break;
+       case SetUSBDevAddrReq:
+               resp->common.pkt_type   = SetUSBDevAddrResp;
+               resp->common.pkt_status = NO_ERROR;
+               udc->mausb_dev->dev_address = MAUDC_DEV_ADDR;
+
+               resp->set_dev_addr_resp.dev_addr = udc->mausb_dev->dev_address;
+               ret = 0;
+               break;
+       case UpdateDevReq:
+               break;
+       case USBDevDisconnectReq:
+               resp->common.pkt_type   = USBDevDisconnectResp;
+               resp->common.dev_handle = req->common.dev_handle;
+               resp->common.pkt_status = NO_ERROR;
+               ret = 0;
+               break;
+       case DevDisconnectReq:
+               resp->common.pkt_type   = DevDisconnectResp;
+               resp->common.dev_handle = 0;
+               resp->common.pkt_status = NO_ERROR;
+               ret = 0;
+               break;
+       case SleepReq:
+               resp->common.pkt_type   = SleepResp;
+               resp->common.pkt_status = NO_ERROR;
+               mausb_device_suspend();
+               ret = 0;
+               break;
+       case WakeReq:
+               resp->common.pkt_type   = WakeResp;
+               resp->common.pkt_status = NO_ERROR;
+               mausb_device_resume();
+               ret = 0;
+               break;
+       case PingReq:
+               break;
+       case SyncReq:
+               break;
+       case CancelTransferReq:
+               resp->common.pkt_type   = CancelTransferResp;
+               resp->common.pkt_status = NO_ERROR;
+               resp->cancel_transfer_resp.ep_handle =
+                               req->cancel_transfer_req.ep_handle;
+               resp->cancel_transfer_resp.stream_id =
+                               req->cancel_transfer_req.stream_id;
+               resp->cancel_transfer_resp.req_id =
+                               req->cancel_transfer_req.req_id;
+               /* TODO: cancel transfer and set fields below accordingly */
+               resp->cancel_transfer_resp.cancel_status = 1;
+               resp->cancel_transfer_resp.deliv_seq_num = 0;
+               resp->cancel_transfer_resp.deliv_byte_offset = 0;
+               ret = 0;
+               break;
+       case EPOpenStreamReq:
+               break;
+       case EPCloseStreamReq:
+               break;
+       case USBDevResetReq:
+               break;
+       case VendorSpecificReq:
+               break;
+
+       /* Invalid packet type */
+       default:
+               maudc_err(udc, "%s: invalid packet type\n", __func__);
+               ret = -EBADMSG;
+               break;
+       }
+
+       return ret;
+}
+
+/**
+ * MA USB ep0 endpoint descriptor for a super speed endpoint
+ * per spec section 7.3.2.2.
+ */
+static struct usb_endpoint_descriptor mausb_ep0_desc = {
+
+       .bLength = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType = USB_DT_ENDPOINT,
+       .bEndpointAddress = 0x00,
+       .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+       .wMaxPacketSize = 512,
+       .bInterval = 0
+};
+
+/**
+ * UDC probe function
+ */
+int mausb_udc_probe(struct platform_device *pdev)
+{
+       int                             ret = 0;
+       int                             i;
+       struct mausb_udc                *udc;
+       struct usb_ep                   *usb_ep;
+
+       udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+       if (!udc)
+               return -ENOMEM;
+
+       udc->udc_lock = __SPIN_LOCK_UNLOCKED(udc->udc_lock);
+       /* initialize gadget structure */
+       udc->gadget.name = UDC_NAME;
+       udc->gadget.ops = &mudc_ops;
+       udc->gadget.speed = USB_SPEED_UNKNOWN;
+       udc->gadget.max_speed = USB_SPEED_SUPER;
+       udc->gadget.dev.parent = &pdev->dev;
+
+       /* set head for list of gadget endpoints   */
+       INIT_LIST_HEAD(&udc->gadget.ep_list);
+
+       /* initalize the ma_dev structure */
+       mausb_init_ma_device(&udc->ma_dev, 0, 0,
+               NULL, udc, &udc_mgmt_req_switch, &check_for_device);
+
+       udc->mausb_dev = mausb_internal_alloc_dev(&udc->ma_dev, NULL,
+               &udc->gadget);
+
+       for (i = 0; i < MAUDC_MAX_NUM_EP; i++) {
+               usb_ep = &udc->usb_ep[i].dev_ep;
+
+               usb_ep->name = ep_name[i];
+               usb_ep->ops = &mausb_ep_ops;
+               usb_ep->maxpacket = EP_MAX_PACKET;
+               usb_ep->max_streams = EP_MAX_STREAMS;
+
+               list_add_tail(&usb_ep->ep_list, &udc->gadget.ep_list);
+
+               INIT_LIST_HEAD(&udc->usb_ep[i].usb_req_list);
+       }
+
+       /* give gadget driver ep0 then remove from list*/
+       udc->usb_ep[0].dev_ep.desc = &mausb_ep0_desc;
+       udc->gadget.ep0 = &udc->usb_ep[0].dev_ep;
+       list_del_init(&udc->usb_ep[0].dev_ep.ep_list);
+
+       /* add a new gadget to the udc class driver list */
+       ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "%s: could not add gadget UDC, error %d\n",
+                       __func__, ret);
+       } else
+               platform_set_drvdata(pdev, udc);
+
+       return ret;
+}
+
+int mausb_udc_remove(struct platform_device *pdev)
+{
+       struct mausb_udc *udc = platform_get_drvdata(pdev);
+
+       dev_dbg(&pdev->dev, "%s: removing udc\n", __func__);
+
+       usb_del_gadget_udc(&udc->gadget);
+
+       return 0;
+}
+
+int mausb_udc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct mausb_udc *udc = platform_get_drvdata(pdev);
+
+       dev_dbg(&pdev->dev, "%s: suspending udc\n", __func__);
+
+       udc->suspended = 1;
+
+       return 0;
+}
+
+int mausb_udc_resume(struct platform_device *pdev)
+{
+       struct mausb_udc *udc = platform_get_drvdata(pdev);
+
+       dev_dbg(&pdev->dev, "%s: resuming udc\n", __func__);
+
+       udc->suspended = 0;
+
+       return 0;
+}
+
+/**
+ * platform structures for USB Device Controller (UDC)
+ */
+static struct platform_device udc_pdev = {
+       .name = UDC_NAME,
+       .id = 0
+};
+
+static struct platform_driver udc_driver = {
+       .probe = mausb_udc_probe,
+       .remove = mausb_udc_remove,
+       .suspend = mausb_udc_suspend,
+       .resume = mausb_udc_resume,
+       .driver = {
+               .name = UDC_NAME,
+               .owner = THIS_MODULE
+       }
+};
+
+/**
+ * initialization function
+ */
+static int mausb_udc_init(void)
+{
+       int ret;
+
+       printk(KERN_DEBUG "loading MA USB UDC . . .\n");
+
+       /* register UDC driver */
+       ret = platform_driver_register(&udc_driver);
+       if (ret < 0) {
+               printk(KERN_DEBUG "failed to register UDC driver - "
+                       "error # %d\n", ret);
+       } else {
+               /* register UDC device */
+               ret = platform_device_register(&udc_pdev);
+               if (ret < 0) {
+                       printk(KERN_DEBUG "failed to register UDC device - "
+                               "error # %d\n", ret);
+                       platform_driver_unregister(&udc_driver);
+
+               } else {
+                       /* direct the release function (for exiting) */
+                       udc_pdev.dev.release = &udc_dev_release;
+               }
+       }
+
+       return ret;
+}
+module_init(mausb_udc_init);
+
+/**
+ * exit function
+ */
+static void mausb_udc_exit(void)
+{
+       /* deregister UDC device */
+       platform_device_unregister(&udc_pdev);
+
+       /* deregister UDC driver */
+       platform_driver_unregister(&udc_driver);
+
+}
+module_exit(mausb_udc_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Intel");
+MODULE_DESCRIPTION("mausb_udc");
diff --git a/drivers/staging/mausb/drivers/mausb_udc.h 
b/drivers/staging/mausb/drivers/mausb_udc.h
new file mode 100644
index 0000000..b900b2d
--- /dev/null
+++ b/drivers/staging/mausb/drivers/mausb_udc.h
@@ -0,0 +1,147 @@
+/* name:       mausb_udc.h
+ * description:        header file for mausb_udc.h
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * Contact Information:
+ * Sean Stalley, sean.o.stal...@intel.com
+ * Stephanie Wallick, stephanie.s.wall...@intel.com
+ * 2111 NE 25th Avenue
+ * Hillsboro, Oregon 97124
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2014 Intel Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in
+      the documentation and/or other materials provided with the
+      distribution.
+    * Neither the name of Intel Corporation nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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 __MAUSB_UDC_H
+#define __MAUSB_UDC_H
+
+#include <linux/timer.h>
+#include <linux/usb/gadget.h>
+#include "mausb_state.h"
+#include "mausb_pkt.h"
+#include "mausb_msapi.h"
+#include "mausb_mem.h" /* for mgmt structure */
+#include "mausb_const.h"
+
+#define UDC_NAME "mausb udc"
+#define MAUDC_MAX_NUM_EP 3
+#define EP_MAX_PACKET  64  /* max number of packets ep can handle */
+#define EP_MAX_STREAMS 16 /* max number of streams ep can handle */
+#define REQ_BUF_SIZE   64
+#define CAP_MAX_EP_NUM 3 /* max number of endpoints state can be tracked for */
+#define CAP_MAX_DEV_NUM 1 /* max number of devices that can be managed
+                          * note this is 1 because only hubs can have >1 */
+#define CAP_DEV_TYPE   0 /* device type: 0 = not an MA USB hub
+                          *              1 = USB 2.0 hub
+                          *              2 = USB 3.1 hub */
+#define CAP_MAX_TX_REQS 0x01FF /* max number of outstanding transfer requests
+                               * device can track - arbitrarily chosen */
+#define CAP_MAX_MGMT_REQS 0x01F /* max number of outstanding management 
requests
+                                * device can track - arbitrarily chosen */
+
+#define MAUDC_DEV_HANDLE 0x1359 /* MA Device handle for the USB device */
+
+#define MAUDC_DEV_ADDR 0x7D /* USB Device address for the USB device */
+                       /* Note: MAUDC_DEV_ADDR is arbitrary */
+
+#define MAUDC_BUS_NUM 0xD /* Bus number to be used in the endpoint handle */
+
+#define MAUSB_GADGET_TIMEOUT 3 /* time out for gadget driver enqueue request */
+
+#define maudc_info(udc, fmt, args...) \
+       dev_info(udc->gadget.dev.parent , fmt , ## args)
+#define maudc_dbg(udc, fmt, args...) \
+       dev_dbg(udc->gadget.dev.parent , fmt , ## args)
+#define maudc_warn(udc, fmt, args...) \
+       dev_warn(udc->gadget.dev.parent , fmt , ## args)
+#define maudc_err(udc, fmt, args...) \
+       dev_err(udc->gadget.dev.parent , fmt , ## args)
+
+/* function declarations */
+void udc_dev_release(struct device *dev);
+int mausb_udc_probe(struct platform_device *pdev);
+int mausb_udc_remove(struct platform_device *pdev);
+int mausb_udc_suspend(struct platform_device *pdev, pm_message_t state);
+int mausb_udc_resume(struct platform_device *pdev);
+int check_for_device(int dont_use);
+
+
+/* function declarations */
+int do_ma_transfer(struct mausb_host_ep *ep, struct mausb_pkt *tx_req,
+               bool dir_in);
+/**
+ * UDC structure
+ *
+ * The UDC structure holds all of the per-device data. This includes both the
+ * media agnostic device data as well as the USB device data. The USB device
+ * connected to the UDC is considered an integrated USB Device.
+ *
+ * @mausb_gadget_ep:   An array of all the endpoints for this device.
+ * @usb_gadget:                The device's gadget structure.
+ * @usb_gadget_driver: the device's driver.
+ * @ma_dev:            The media agnostic device structure. Holds all the
+ *                     data common to all media agnostic devices.
+ * @mausb_dev:         Holds media agnostic data that represents a USB device.
+ * @ms_driver:         The MAUSB driver's interface with the media specific
+ *                     driver.
+ * @udc_lock:          The 'big' lock. Used to protect this structure's data
+ *                     from concurrent access.
+ * @dev_status:                Holds USB device status.
+ * @suspended:         1 when UDC is suspended, otherwise 0.
+ * @pullup:            1 when a USB device is connecting to UDC, otherwise 0.
+ */
+struct mausb_udc {
+       struct mausb_gadget_ep          usb_ep[MAUDC_MAX_NUM_EP];
+       struct usb_gadget               gadget;
+       struct usb_gadget_driver        *driver;
+       struct ma_dev                   ma_dev;
+       struct mausb_dev                *mausb_dev;
+       spinlock_t                      udc_lock;
+       u16                             dev_status;
+       unsigned                        suspended:1;
+       unsigned                        pullup:1;
+};
+
+#endif
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to