[PATCH 06/10] added media agnostic (MA) UDC

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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.stalley@xxxxxxxxx>
Signed-off-by: Stephanie Wallick <stephanie.s.wallick@xxxxxxxxx>
---
 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.stalley@xxxxxxxxx
+ * Stephanie Wallick, stephanie.s.wallick@xxxxxxxxx
+ * 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.stalley@xxxxxxxxx
+ * Stephanie Wallick, stephanie.s.wallick@xxxxxxxxx
+ * 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


_______________________________________________
devel mailing list
devel@xxxxxxxxxxxxxxxxxxxxxx
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel




[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux