text: /****************************************************************************
   *
   * Copyright 2016 Samsung Electronics All Rights Reserved.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
   * either express or implied. See the License for the specific
   * language governing permissions and limitations under the License.
   *
   ****************************************************************************/
  /****************************************************************************
   * 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 <tinyara/config.h>
  
  #include <sys/types.h>
  #include <stdint.h>
  #include <string.h>
  #include <errno.h>
  #include <debug.h>
  
  #include <tinyara/arch.h>
  #include <tinyara/kmalloc.h>
  #include <tinyara/usb/usb.h>
  #include <tinyara/usb/usbdev.h>
  #include <tinyara/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(USBCOMPOSITE_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(USBCOMPOSITE_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);
  	UNUSED(index);
  
  	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(USBCOMPOSITE_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(USBCOMPOSITE_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(USBCOMPOSITE_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 *)kmm_malloc(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:
  	kmm_free(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);
  
  	/* First phase uninitialization each of the member classes */
  
  	priv = &alloc->dev;
  
  	DEV1_UNINITIALIZE(priv->dev1);
  	DEV2_UNINITIALIZE(priv->dev2);
  
  	/* Then unregister and destroy the composite class */
  
  	usbdev_unregister(&alloc->drvr.drvr);
  
  	/* Free any resources used by the composite driver */
  	/* None */
  
  	/* Second phase uninitialization:  Clean up all memory resources */
  
  	DEV1_UNINITIALIZE(priv->dev1);
  	DEV2_UNINITIALIZE(priv->dev2);
  
  	/* Then free the composite driver state structure itself */
  
  	kmm_free(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 */
  