diff options
Diffstat (limited to 'nuttx/drivers/usbdev/composite.c')
-rw-r--r-- | nuttx/drivers/usbdev/composite.c | 933 |
1 files changed, 933 insertions, 0 deletions
diff --git a/nuttx/drivers/usbdev/composite.c b/nuttx/drivers/usbdev/composite.c new file mode 100644 index 000000000..4cad8af86 --- /dev/null +++ b/nuttx/drivers/usbdev/composite.c @@ -0,0 +1,933 @@ +/**************************************************************************** + * 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); + dispatched = true; + + if (ret >= 0) + { + ret = CLASS_SETUP(priv->dev2, dev, ctrl); + 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 */ |