lkml.org 
[lkml]   [2014]   [Nov]   [3]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 06/10] added media agnostic (MA) UDC
    Date
    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@intel.com>
    Signed-off-by: Stephanie Wallick <stephanie.s.wallick@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.stalley@intel.com
    + * Stephanie Wallick, stephanie.s.wallick@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.stalley@intel.com
    + * Stephanie Wallick, stephanie.s.wallick@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


    \
     
     \ /
      Last update: 2014-11-03 22:01    [W:2.382 / U:0.212 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site