You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
933 lines
27 KiB
933 lines
27 KiB
/**************************************************************************** |
|
* drivers/usbdev/composite.c |
|
* |
|
* Copyright (C) 2012 Gregory Nutt. All rights reserved. |
|
* Author: Gregory Nutt <gnutt@nuttx.org> |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. 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. |
|
* 3. Neither the name NuttX 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. |
|
* |
|
****************************************************************************/ |
|
|
|
/**************************************************************************** |
|
* Included Files |
|
****************************************************************************/ |
|
|
|
#include <nuttx/config.h> |
|
|
|
#include <sys/types.h> |
|
#include <stdint.h> |
|
#include <string.h> |
|
#include <errno.h> |
|
#include <debug.h> |
|
|
|
#include <nuttx/arch.h> |
|
#include <nuttx/kmalloc.h> |
|
#include <nuttx/usb/usb.h> |
|
#include <nuttx/usb/usbdev.h> |
|
#include <nuttx/usb/usbdev_trace.h> |
|
|
|
#include "composite.h" |
|
|
|
#ifdef CONFIG_USBDEV_COMPOSITE |
|
|
|
/**************************************************************************** |
|
* Pre-processor Definitions |
|
****************************************************************************/ |
|
|
|
/**************************************************************************** |
|
* Private Types |
|
****************************************************************************/ |
|
|
|
/* This structure describes the internal state of the driver */ |
|
|
|
struct composite_dev_s |
|
{ |
|
FAR struct usbdev_s *usbdev; /* usbdev driver pointer */ |
|
uint8_t config; /* Configuration number */ |
|
FAR struct usbdev_req_s *ctrlreq; /* Allocated control request */ |
|
struct usbdevclass_driver_s *dev1; /* Device 1 class object */ |
|
struct usbdevclass_driver_s *dev2; /* Device 2 class object */ |
|
}; |
|
|
|
/* The internal version of the class driver */ |
|
|
|
struct composite_driver_s |
|
{ |
|
struct usbdevclass_driver_s drvr; |
|
FAR struct composite_dev_s *dev; |
|
}; |
|
|
|
/* This is what is allocated */ |
|
|
|
struct composite_alloc_s |
|
{ |
|
struct composite_dev_s dev; |
|
struct composite_driver_s drvr; |
|
}; |
|
|
|
/**************************************************************************** |
|
* Private Function Prototypes |
|
****************************************************************************/ |
|
/* USB helps ****************************************************************/ |
|
|
|
static void composite_ep0incomplete(FAR struct usbdev_ep_s *ep, |
|
FAR struct usbdev_req_s *req); |
|
static int composite_classsetup(FAR struct composite_dev_s *priv, |
|
FAR struct usbdev_s *dev, |
|
FAR const struct usb_ctrlreq_s *ctrl, FAR uint8_t *dataout, |
|
size_t outlen); |
|
static struct usbdev_req_s *composite_allocreq(FAR struct usbdev_ep_s *ep, |
|
uint16_t len); |
|
static void composite_freereq(FAR struct usbdev_ep_s *ep, |
|
FAR struct usbdev_req_s *req); |
|
|
|
/* USB class device ********************************************************/ |
|
|
|
static int composite_bind(FAR struct usbdevclass_driver_s *driver, |
|
FAR struct usbdev_s *dev); |
|
static void composite_unbind(FAR struct usbdevclass_driver_s *driver, |
|
FAR struct usbdev_s *dev); |
|
static int composite_setup(FAR struct usbdevclass_driver_s *driver, |
|
FAR struct usbdev_s *dev, |
|
FAR const struct usb_ctrlreq_s *ctrl, FAR uint8_t *dataout, |
|
size_t outlen); |
|
static void composite_disconnect(FAR struct usbdevclass_driver_s *driver, |
|
FAR struct usbdev_s *dev); |
|
static void composite_suspend(FAR struct usbdevclass_driver_s *driver, |
|
FAR struct usbdev_s *dev); |
|
static void composite_resume(FAR struct usbdevclass_driver_s *driver, |
|
FAR struct usbdev_s *dev); |
|
|
|
/**************************************************************************** |
|
* Private Data |
|
****************************************************************************/ |
|
/* USB class device *********************************************************/ |
|
|
|
static const struct usbdevclass_driverops_s g_driverops = |
|
{ |
|
composite_bind, /* bind */ |
|
composite_unbind, /* unbind */ |
|
composite_setup, /* setup */ |
|
composite_disconnect, /* disconnect */ |
|
composite_suspend, /* suspend */ |
|
composite_resume, /* resume */ |
|
}; |
|
|
|
/**************************************************************************** |
|
* Public Data |
|
****************************************************************************/ |
|
|
|
const char g_compvendorstr[] = CONFIG_COMPOSITE_VENDORSTR; |
|
const char g_compproductstr[] = CONFIG_COMPOSITE_PRODUCTSTR; |
|
const char g_compserialstr[] = CONFIG_COMPOSITE_SERIALSTR; |
|
|
|
/**************************************************************************** |
|
* Private Functions |
|
****************************************************************************/ |
|
/**************************************************************************** |
|
* Helpers |
|
****************************************************************************/ |
|
|
|
/**************************************************************************** |
|
* Name: composite_ep0incomplete |
|
* |
|
* Description: |
|
* Handle completion of the composite driver's EP0 control operations |
|
* |
|
****************************************************************************/ |
|
|
|
static void composite_ep0incomplete(FAR struct usbdev_ep_s *ep, |
|
FAR struct usbdev_req_s *req) |
|
{ |
|
/* Just check the result of the transfer */ |
|
|
|
if (req->result || req->xfrd != req->len) |
|
{ |
|
usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_REQRESULT), (uint16_t)-req->result); |
|
} |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: composite_classsetup |
|
* |
|
* Description: |
|
* Forward a setup command to the appropriate component device |
|
* |
|
****************************************************************************/ |
|
|
|
static int composite_classsetup(FAR struct composite_dev_s *priv, |
|
FAR struct usbdev_s *dev, |
|
FAR const struct usb_ctrlreq_s *ctrl, |
|
FAR uint8_t *dataout, size_t outlen) |
|
{ |
|
uint16_t index; |
|
uint8_t interface; |
|
int ret = -EOPNOTSUPP; |
|
|
|
index = GETUINT16(ctrl->index); |
|
interface = (uint8_t)(index & 0xff); |
|
|
|
if (interface >= DEV1_FIRSTINTERFACE && interface < (DEV1_FIRSTINTERFACE + DEV1_NINTERFACES)) |
|
{ |
|
ret = CLASS_SETUP(priv->dev1, dev, ctrl, dataout, outlen); |
|
} |
|
else if (interface >= DEV2_FIRSTINTERFACE && interface < (DEV2_FIRSTINTERFACE + DEV2_NINTERFACES)) |
|
{ |
|
ret = CLASS_SETUP(priv->dev2, dev, ctrl, dataout, outlen); |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: composite_allocreq |
|
* |
|
* Description: |
|
* Allocate a request instance along with its buffer |
|
* |
|
****************************************************************************/ |
|
|
|
static struct usbdev_req_s *composite_allocreq(FAR struct usbdev_ep_s *ep, |
|
uint16_t len) |
|
{ |
|
FAR struct usbdev_req_s *req; |
|
|
|
req = EP_ALLOCREQ(ep); |
|
if (req != NULL) |
|
{ |
|
req->len = len; |
|
req->buf = EP_ALLOCBUFFER(ep, len); |
|
if (!req->buf) |
|
{ |
|
EP_FREEREQ(ep, req); |
|
req = NULL; |
|
} |
|
} |
|
return req; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: composite_freereq |
|
* |
|
* Description: |
|
* Free a request instance along with its buffer |
|
* |
|
****************************************************************************/ |
|
|
|
static void composite_freereq(FAR struct usbdev_ep_s *ep, |
|
FAR struct usbdev_req_s *req) |
|
{ |
|
if (ep != NULL && req != NULL) |
|
{ |
|
if (req->buf != NULL) |
|
{ |
|
EP_FREEBUFFER(ep, req->buf); |
|
} |
|
EP_FREEREQ(ep, req); |
|
} |
|
} |
|
|
|
/**************************************************************************** |
|
* USB Class Driver Methods |
|
****************************************************************************/ |
|
|
|
/**************************************************************************** |
|
* Name: composite_bind |
|
* |
|
* Description: |
|
* Invoked when the driver is bound to a USB device driver |
|
* |
|
****************************************************************************/ |
|
|
|
static int composite_bind(FAR struct usbdevclass_driver_s *driver, |
|
FAR struct usbdev_s *dev) |
|
{ |
|
FAR struct composite_dev_s *priv = ((FAR struct composite_driver_s*)driver)->dev; |
|
int ret; |
|
|
|
usbtrace(TRACE_CLASSBIND, 0); |
|
|
|
/* Bind the structures */ |
|
|
|
priv->usbdev = dev; |
|
|
|
/* Save the reference to our private data structure in EP0 so that it |
|
* can be recovered in ep0 completion events. |
|
*/ |
|
|
|
dev->ep0->priv = priv; |
|
|
|
/* Preallocate one control request */ |
|
|
|
priv->ctrlreq = composite_allocreq(dev->ep0, COMPOSITE_CFGDESCSIZE); |
|
if (priv->ctrlreq == NULL) |
|
{ |
|
usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_ALLOCCTRLREQ), 0); |
|
ret = -ENOMEM; |
|
goto errout; |
|
} |
|
|
|
/* Initialize the pre-allocated control request */ |
|
|
|
priv->ctrlreq->callback = composite_ep0incomplete; |
|
|
|
/* Then bind each of the constituent class drivers */ |
|
|
|
ret = CLASS_BIND(priv->dev1, dev); |
|
if (ret < 0) |
|
{ |
|
goto errout; |
|
} |
|
|
|
ret = CLASS_BIND(priv->dev2, dev); |
|
if (ret < 0) |
|
{ |
|
goto errout; |
|
} |
|
|
|
/* Report if we are selfpowered */ |
|
|
|
#ifdef CONFIG_USBDEV_SELFPOWERED |
|
DEV_SETSELFPOWERED(dev); |
|
#endif |
|
|
|
/* And pull-up the data line for the soft connect function */ |
|
|
|
DEV_CONNECT(dev); |
|
return OK; |
|
|
|
errout: |
|
composite_unbind(driver, dev); |
|
return ret; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: composite_unbind |
|
* |
|
* Description: |
|
* Invoked when the driver is unbound from a USB device driver |
|
* |
|
****************************************************************************/ |
|
|
|
static void composite_unbind(FAR struct usbdevclass_driver_s *driver, |
|
FAR struct usbdev_s *dev) |
|
{ |
|
FAR struct composite_dev_s *priv; |
|
irqstate_t flags; |
|
|
|
usbtrace(TRACE_CLASSUNBIND, 0); |
|
|
|
#ifdef CONFIG_DEBUG |
|
if (!driver || !dev || !dev->ep0) |
|
{ |
|
usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_INVALIDARG), 0); |
|
return; |
|
} |
|
#endif |
|
|
|
/* Extract reference to private data */ |
|
|
|
priv = ((FAR struct composite_driver_s*)driver)->dev; |
|
|
|
#ifdef CONFIG_DEBUG |
|
if (!priv) |
|
{ |
|
usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_EP0NOTBOUND), 0); |
|
return; |
|
} |
|
#endif |
|
|
|
/* Make sure that we are not already unbound */ |
|
|
|
if (priv != NULL) |
|
{ |
|
/* Unbind the constituent class drivers */ |
|
|
|
flags = irqsave(); |
|
CLASS_UNBIND(priv->dev1, dev); |
|
CLASS_UNBIND(priv->dev2, dev); |
|
|
|
/* Free the pre-allocated control request */ |
|
|
|
priv->config = COMPOSITE_CONFIGIDNONE; |
|
if (priv->ctrlreq != NULL) |
|
{ |
|
composite_freereq(dev->ep0, priv->ctrlreq); |
|
priv->ctrlreq = NULL; |
|
} |
|
irqrestore(flags); |
|
} |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: composite_setup |
|
* |
|
* Description: |
|
* Invoked for ep0 control requests. This function probably executes |
|
* in the context of an interrupt handler. |
|
* |
|
****************************************************************************/ |
|
|
|
static int composite_setup(FAR struct usbdevclass_driver_s *driver, |
|
FAR struct usbdev_s *dev, |
|
FAR const struct usb_ctrlreq_s *ctrl, |
|
FAR uint8_t *dataout, size_t outlen) |
|
{ |
|
FAR struct composite_dev_s *priv; |
|
FAR struct usbdev_req_s *ctrlreq; |
|
uint16_t value; |
|
uint16_t index; |
|
uint16_t len; |
|
bool dispatched = false; |
|
int ret = -EOPNOTSUPP; |
|
|
|
#ifdef CONFIG_DEBUG |
|
if (!driver || !dev || !dev->ep0 || !ctrl) |
|
{ |
|
usbtrace(TRACE_CLSERROR(COMPOSITE_TRACEERR_SETUPINVALIDARGS), 0); |
|
return -EIO; |
|
} |
|
#endif |
|
|
|
/* Extract a reference to private data */ |
|
|
|
usbtrace(TRACE_CLASSSETUP, ctrl->req); |
|
priv = ((FAR struct composite_driver_s*)driver)->dev; |
|
|
|
#ifdef CONFIG_DEBUG |
|
if (!priv) |
|
{ |
|
usbtrace(TRACE_CLSERROR(COMPOSITE_TRACEERR_EP0NOTBOUND2), 0); |
|
return -ENODEV; |
|
} |
|
#endif |
|
ctrlreq = priv->ctrlreq; |
|
|
|
/* Extract the little-endian 16-bit values to host order */ |
|
|
|
value = GETUINT16(ctrl->value); |
|
index = GETUINT16(ctrl->index); |
|
len = GETUINT16(ctrl->len); |
|
|
|
uvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n", |
|
ctrl->type, ctrl->req, value, index, len); |
|
|
|
if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD) |
|
{ |
|
/********************************************************************** |
|
* Standard Requests |
|
**********************************************************************/ |
|
|
|
switch (ctrl->req) |
|
{ |
|
case USB_REQ_GETDESCRIPTOR: |
|
{ |
|
/* The value field specifies the descriptor type in the MS byte and the |
|
* descriptor index in the LS byte (order is little endian) |
|
*/ |
|
|
|
switch (ctrl->value[1]) |
|
{ |
|
case USB_DESC_TYPE_DEVICE: |
|
{ |
|
ret = USB_SIZEOF_DEVDESC; |
|
memcpy(ctrlreq->buf, composite_getdevdesc(), ret); |
|
} |
|
break; |
|
|
|
#ifdef CONFIG_USBDEV_DUALSPEED |
|
case USB_DESC_TYPE_DEVICEQUALIFIER: |
|
{ |
|
ret = USB_SIZEOF_QUALDESC; |
|
memcpy(ctrlreq->buf, composite_getqualdesc(), ret); |
|
} |
|
break; |
|
|
|
case USB_DESC_TYPE_OTHERSPEEDCONFIG: |
|
#endif |
|
|
|
case USB_DESC_TYPE_CONFIG: |
|
{ |
|
#ifdef CONFIG_USBDEV_DUALSPEED |
|
ret = composite_mkcfgdesc(ctrlreq->buf, dev->speed, |
|
ctrl->value[1]); |
|
#else |
|
ret = composite_mkcfgdesc(ctrlreq->buf); |
|
#endif |
|
} |
|
break; |
|
|
|
case USB_DESC_TYPE_STRING: |
|
{ |
|
/* value == string index. Zero is the language ID. */ |
|
|
|
uint8_t strid = ctrl->value[0]; |
|
FAR struct usb_strdesc_s *buf = (FAR struct usb_strdesc_s *)ctrlreq->buf; |
|
|
|
if (strid <= COMPOSITE_NSTRIDS) |
|
{ |
|
ret = composite_mkstrdesc(strid, buf); |
|
} |
|
#if DEV1_NSTRIDS > 0 |
|
else if (strid <= DEV1_STRIDBASE + DEV1_NSTRIDS) |
|
{ |
|
ret = DEV1_MKSTRDESC(strid, buf); |
|
} |
|
#endif |
|
#if DEV2_NSTRIDS > 0 |
|
else if (strid <= DEV2_STRIDBASE + DEV2_NSTRIDS) |
|
{ |
|
ret = DEV2_MKSTRDESC(strid, buf); |
|
} |
|
#endif |
|
} |
|
break; |
|
|
|
default: |
|
{ |
|
usbtrace(TRACE_CLSERROR(COMPOSITE_TRACEERR_GETUNKNOWNDESC), value); |
|
} |
|
break; |
|
} |
|
} |
|
break; |
|
|
|
case USB_REQ_SETCONFIGURATION: |
|
{ |
|
if (ctrl->type == 0) |
|
{ |
|
/* Save the configuration and inform the constituent classes */ |
|
|
|
ret = CLASS_SETUP(priv->dev1, dev, ctrl, dataout, outlen); |
|
dispatched = true; |
|
|
|
if (ret >= 0) |
|
{ |
|
ret = CLASS_SETUP(priv->dev2, dev, ctrl, dataout, outlen); |
|
if (ret >= 0) |
|
{ |
|
priv->config = value; |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case USB_REQ_GETCONFIGURATION: |
|
{ |
|
if (ctrl->type == USB_DIR_IN) |
|
{ |
|
ctrlreq->buf[0] = priv->config; |
|
ret = 1; |
|
} |
|
} |
|
break; |
|
|
|
case USB_REQ_SETINTERFACE: |
|
{ |
|
if (ctrl->type == USB_REQ_RECIPIENT_INTERFACE && |
|
priv->config == COMPOSITE_CONFIGID) |
|
{ |
|
ret = composite_classsetup(priv, dev, ctrl, dataout, outlen); |
|
dispatched = true; |
|
} |
|
} |
|
break; |
|
|
|
case USB_REQ_GETINTERFACE: |
|
{ |
|
if (ctrl->type == (USB_DIR_IN|USB_REQ_RECIPIENT_INTERFACE) && |
|
priv->config == COMPOSITE_CONFIGIDNONE) |
|
{ |
|
ret = composite_classsetup(priv, dev, ctrl, dataout, outlen); |
|
dispatched = true; |
|
} |
|
} |
|
break; |
|
|
|
default: |
|
usbtrace(TRACE_CLSERROR(COMPOSITE_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req); |
|
break; |
|
} |
|
} |
|
else |
|
{ |
|
uint8_t recipient; |
|
|
|
/********************************************************************** |
|
* Non-Standard Class Requests |
|
**********************************************************************/ |
|
|
|
/* Class implementations should handle there own interface and endpoint |
|
* requests. |
|
*/ |
|
|
|
recipient = ctrl->type & USB_REQ_RECIPIENT_MASK; |
|
if (recipient == USB_REQ_RECIPIENT_INTERFACE || recipient == USB_REQ_RECIPIENT_ENDPOINT) |
|
{ |
|
ret = composite_classsetup(priv, dev, ctrl, dataout, outlen); |
|
dispatched = true; |
|
} |
|
} |
|
|
|
|
|
/* Respond to the setup command if (1) data was returned, and (2) the request was |
|
* NOT successfully dispatched to the component class driver. On an error return |
|
* value (ret < 0), the USB driver will stall EP0. |
|
*/ |
|
|
|
if (ret >= 0 && !dispatched) |
|
{ |
|
/* Setup the request */ |
|
|
|
ctrlreq->len = MIN(len, ret); |
|
ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT; |
|
|
|
/* And submit the request to the USB controller driver */ |
|
|
|
ret = EP_SUBMIT(dev->ep0, ctrlreq); |
|
if (ret < 0) |
|
{ |
|
usbtrace(TRACE_CLSERROR(COMPOSITE_TRACEERR_EPRESPQ), (uint16_t)-ret); |
|
ctrlreq->result = OK; |
|
composite_ep0incomplete(dev->ep0, ctrlreq); |
|
} |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: composite_disconnect |
|
* |
|
* Description: |
|
* Invoked after all transfers have been stopped, when the host is |
|
* disconnected. This function is probably called from the context of an |
|
* interrupt handler. |
|
* |
|
****************************************************************************/ |
|
|
|
static void composite_disconnect(FAR struct usbdevclass_driver_s *driver, |
|
FAR struct usbdev_s *dev) |
|
{ |
|
FAR struct composite_dev_s *priv; |
|
irqstate_t flags; |
|
|
|
usbtrace(TRACE_CLASSDISCONNECT, 0); |
|
|
|
#ifdef CONFIG_DEBUG |
|
if (!driver || !dev) |
|
{ |
|
usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_INVALIDARG), 0); |
|
return; |
|
} |
|
#endif |
|
|
|
/* Extract reference to private data */ |
|
|
|
priv = ((FAR struct composite_driver_s*)driver)->dev; |
|
|
|
#ifdef CONFIG_DEBUG |
|
if (!priv) |
|
{ |
|
usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_EP0NOTBOUND), 0); |
|
return; |
|
} |
|
#endif |
|
|
|
/* Reset the configuration and inform the constituent class drivers of |
|
* the disconnection. |
|
*/ |
|
|
|
flags = irqsave(); |
|
priv->config = COMPOSITE_CONFIGIDNONE; |
|
CLASS_DISCONNECT(priv->dev1, dev); |
|
CLASS_DISCONNECT(priv->dev2, dev); |
|
irqrestore(flags); |
|
|
|
/* Perform the soft connect function so that we will we can be |
|
* re-enumerated. |
|
*/ |
|
|
|
DEV_CONNECT(dev); |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: composite_suspend |
|
* |
|
* Description: |
|
* Invoked on a USB suspend event. |
|
* |
|
****************************************************************************/ |
|
|
|
static void composite_suspend(FAR struct usbdevclass_driver_s *driver, |
|
FAR struct usbdev_s *dev) |
|
{ |
|
FAR struct composite_dev_s *priv; |
|
irqstate_t flags; |
|
|
|
usbtrace(TRACE_CLASSSUSPEND, 0); |
|
|
|
#ifdef CONFIG_DEBUG |
|
if (!dev) |
|
{ |
|
usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_INVALIDARG), 0); |
|
return; |
|
} |
|
#endif |
|
|
|
/* Extract reference to private data */ |
|
|
|
priv = ((FAR struct composite_driver_s*)driver)->dev; |
|
|
|
#ifdef CONFIG_DEBUG |
|
if (!priv) |
|
{ |
|
usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_EP0NOTBOUND), 0); |
|
return; |
|
} |
|
#endif |
|
|
|
/* Forward the suspend event to the constituent devices */ |
|
|
|
flags = irqsave(); |
|
CLASS_SUSPEND(priv->dev1, priv->usbdev); |
|
CLASS_SUSPEND(priv->dev2, priv->usbdev); |
|
irqrestore(flags); |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: composite_resume |
|
* |
|
* Description: |
|
* Invoked on a USB resume event. |
|
* |
|
****************************************************************************/ |
|
|
|
static void composite_resume(FAR struct usbdevclass_driver_s *driver, |
|
FAR struct usbdev_s *dev) |
|
{ |
|
FAR struct composite_dev_s *priv = NULL; |
|
irqstate_t flags; |
|
|
|
#ifdef CONFIG_DEBUG |
|
if (!dev) |
|
{ |
|
usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_INVALIDARG), 0); |
|
return; |
|
} |
|
#endif |
|
|
|
/* Extract reference to private data */ |
|
|
|
priv = ((FAR struct composite_driver_s*)driver)->dev; |
|
|
|
#ifdef CONFIG_DEBUG |
|
if (!priv) |
|
{ |
|
usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_EP0NOTBOUND), 0); |
|
return; |
|
} |
|
#endif |
|
|
|
/* Forward the resume event to the constituent devices */ |
|
|
|
flags = irqsave(); |
|
CLASS_RESUME(priv->dev1, priv->usbdev); |
|
CLASS_RESUME(priv->dev2, priv->usbdev); |
|
irqrestore(flags); |
|
} |
|
|
|
/**************************************************************************** |
|
* Public Functions |
|
****************************************************************************/ |
|
/**************************************************************************** |
|
* Name: composite_initialize |
|
* |
|
* Description: |
|
* Register USB composite device as configured. This function will call |
|
* board-specific implementations in order to obtain the class objects for |
|
* each of the members of the composite (see board_mscclassobject(), |
|
* board_cdcclassobjec(), ...) |
|
* |
|
* Input Parameter: |
|
* None |
|
* |
|
* Returned Value: |
|
* A non-NULL "handle" is returned on success. This handle may be used |
|
* later with composite_uninitialize() in order to removed the composite |
|
* device. This handle is the (untyped) internal representation of the |
|
* the class driver instance. |
|
* |
|
* NULL is returned on any failure. |
|
* |
|
****************************************************************************/ |
|
|
|
FAR void *composite_initialize(void) |
|
{ |
|
FAR struct composite_alloc_s *alloc; |
|
FAR struct composite_dev_s *priv; |
|
FAR struct composite_driver_s *drvr; |
|
int ret; |
|
|
|
/* Allocate the structures needed */ |
|
|
|
alloc = (FAR struct composite_alloc_s*)kmalloc(sizeof(struct composite_alloc_s)); |
|
if (!alloc) |
|
{ |
|
usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_ALLOCDEVSTRUCT), 0); |
|
return NULL; |
|
} |
|
|
|
/* Convenience pointers into the allocated blob */ |
|
|
|
priv = &alloc->dev; |
|
drvr = &alloc->drvr; |
|
|
|
/* Initialize the USB composite driver structure */ |
|
|
|
memset(priv, 0, sizeof(struct composite_dev_s)); |
|
|
|
/* Get the constitueat class driver objects */ |
|
|
|
ret = DEV1_CLASSOBJECT(&priv->dev1); |
|
if (ret < 0) |
|
{ |
|
usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_CLASSOBJECT), (uint16_t)-ret); |
|
goto errout_with_alloc; |
|
} |
|
|
|
ret = DEV2_CLASSOBJECT(&priv->dev2); |
|
if (ret < 0) |
|
{ |
|
usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_CLASSOBJECT), (uint16_t)-ret); |
|
goto errout_with_alloc; |
|
} |
|
|
|
/* Initialize the USB class driver structure */ |
|
|
|
#ifdef CONFIG_USBDEV_DUALSPEED |
|
drvr->drvr.speed = USB_SPEED_HIGH; |
|
#else |
|
drvr->drvr.speed = USB_SPEED_FULL; |
|
#endif |
|
drvr->drvr.ops = &g_driverops; |
|
drvr->dev = priv; |
|
|
|
/* Register the USB composite class driver */ |
|
|
|
ret = usbdev_register(&drvr->drvr); |
|
if (ret) |
|
{ |
|
usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_DEVREGISTER), (uint16_t)-ret); |
|
goto errout_with_alloc; |
|
} |
|
|
|
return (FAR void *)alloc; |
|
|
|
errout_with_alloc: |
|
kfree(alloc); |
|
return NULL; |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: composite_uninitialize |
|
* |
|
* Description: |
|
* Un-initialize the USB composite driver. The handle is the USB composite |
|
* class' device object as was returned by composite_initialize(). This |
|
* function will call board-specific implementations in order to free the |
|
* class objects for each of the members of the composite (see |
|
* board_mscuninitialize(), board_cdcuninitialize(), ...) |
|
* |
|
* Input Parameters: |
|
* handle - The handle returned by a previous call to composite_initialize(). |
|
* |
|
* Returned Value: |
|
* None |
|
* |
|
***************************************************************************/ |
|
|
|
void composite_uninitialize(FAR void *handle) |
|
{ |
|
FAR struct composite_alloc_s *alloc = (FAR struct composite_alloc_s *)handle; |
|
FAR struct composite_dev_s *priv; |
|
|
|
DEBUGASSERT(alloc != NULL); |
|
|
|
/* Uninitialize each of the member classes */ |
|
|
|
priv = &alloc->dev; |
|
if (priv->dev1) |
|
{ |
|
DEV1_UNINITIALIZE(priv->dev1); |
|
priv->dev1 = NULL; |
|
} |
|
|
|
if (priv->dev2) |
|
{ |
|
DEV1_UNINITIALIZE(priv->dev2); |
|
priv->dev2 = NULL; |
|
} |
|
|
|
/* Then unregister and destroy the composite class */ |
|
|
|
usbdev_unregister(&alloc->drvr.drvr); |
|
|
|
/* Free any resources used by the composite driver */ |
|
/* None */ |
|
|
|
/* Then free the composite driver state structure itself */ |
|
|
|
kfree(priv); |
|
} |
|
|
|
/**************************************************************************** |
|
* Name: composite_ep0submit |
|
* |
|
* Description: |
|
* Members of the composite cannot send on EP0 directly because EP0 is |
|
* is "owned" by the composite device. Instead, when configured as members |
|
* of a composite device, those classes should call this method so that |
|
* the composite device can send on EP0 onbehalf of the class. |
|
* |
|
****************************************************************************/ |
|
|
|
int composite_ep0submit(FAR struct usbdevclass_driver_s *driver, |
|
FAR struct usbdev_s *dev, |
|
FAR struct usbdev_req_s *ctrlreq) |
|
{ |
|
/* This function is not really necessary in the current design. However, |
|
* keeping this will provide us a little flexibility in the future if |
|
* it becomes necessary to manage the completion callbacks. |
|
*/ |
|
|
|
return EP_SUBMIT(dev->ep0, ctrlreq); |
|
} |
|
|
|
#endif /* CONFIG_USBDEV_COMPOSITE */
|
|
|