diff options
Diffstat (limited to 'nuttx/drivers/usbdev/composite.c')
-rw-r--r-- | nuttx/drivers/usbdev/composite.c | 623 |
1 files changed, 559 insertions, 64 deletions
diff --git a/nuttx/drivers/usbdev/composite.c b/nuttx/drivers/usbdev/composite.c index 4c9582355..75f0fbed1 100644 --- a/nuttx/drivers/usbdev/composite.c +++ b/nuttx/drivers/usbdev/composite.c @@ -41,9 +41,14 @@ #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" @@ -58,17 +63,309 @@ * 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); +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 usbdev_s *dev, + FAR struct usbdevclass_driver_s *driver); +static void composite_unbind(FAR struct usbdev_s *dev); +static int composite_setup(FAR struct usbdev_s *dev, + FAR const struct usb_ctrlreq_s *ctrl); +static void composite_disconnect(FAR struct usbdev_s *dev); +static void composite_suspend(FAR struct usbdev_s *dev); +static void composite_resume(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 EP0 control operations + * + ****************************************************************************/ + +static void composite_ep0incomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + 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) +{ + 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); + } + else if (interface >= DEV2_FIRSTINTERFACE && interface <= (DEV2_FIRSTINTERFACE + DEV2_NINTERFACES)) + { + ret = CLASS_SETUP(priv->dev1, dev, ctrl); + } + + 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 usbdev_s *dev, FAR struct usbdevclass_driver_s *driver) +{ + FAR struct composite_dev_s *priv = ((struct composite_driver_s*)driver)->dev; + int ret; + + usbtrace(TRACE_CLASSBIND, 0); + + /* Bind the structures */ + + priv->usbdev = dev; + dev->ep0->priv = priv; + + /* Preallocate 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(dev); + return ret; +} + +/**************************************************************************** + * Name: composite_unbind + * + * Description: + * Invoked when the driver is unbound from a USB device driver + * + ****************************************************************************/ + +static void composite_unbind(FAR struct usbdev_s *dev) +{ + FAR struct composite_dev_s *priv; + irqstate_t flags; + + usbtrace(TRACE_CLASSUNBIND, 0); + +#ifdef CONFIG_DEBUG + if (!dev || !dev->ep0) + { + usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_INVALIDARG), 0); + return; + } +#endif + + /* Extract reference to private data */ + + priv = (FAR struct composite_dev_s *)dev->ep0->priv; + +#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 @@ -79,7 +376,6 @@ * ****************************************************************************/ - #if 0 static int composite_setup(FAR struct usbdev_s *dev, FAR const struct usb_ctrlreq_s *ctrl) { @@ -88,35 +384,36 @@ static int composite_setup(FAR struct usbdev_s *dev, uint16_t value; uint16_t index; uint16_t len; + bool dispatched = false; int ret = -EOPNOTSUPP; #ifdef CONFIG_DEBUG if (!dev || !dev->ep0 || !ctrl) { - usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_SETUPINVALIDARGS), 0); + usbtrace(TRACE_CLSERROR(COMPOSITE_TRACEERR_SETUPINVALIDARGS), 0); return -EIO; } #endif - /* Extract reference to private data */ + /* Extract a reference to private data */ usbtrace(TRACE_CLASSSETUP, ctrl->req); priv = (FAR struct composite_dev_s *)dev->ep0->priv; #ifdef CONFIG_DEBUG - if (!priv || !priv->ctrlreq) + if (!priv) { - usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_EP0NOTBOUND2), 0); + usbtrace(TRACE_CLSERROR(COMPOSITE_TRACEERR_EP0NOTBOUND2), 0); return -ENODEV; } #endif - ctrlreq = priv->ctrlreq; + 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); + 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); @@ -153,7 +450,7 @@ static int composite_setup(FAR struct usbdev_s *dev, break; case USB_DESC_TYPE_OTHERSPEEDCONFIG: -#endif /* CONFIG_USBDEV_DUALSPEED */ +#endif case USB_DESC_TYPE_CONFIG: { @@ -175,7 +472,7 @@ static int composite_setup(FAR struct usbdev_s *dev, default: { - usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_GETUNKNOWNDESC), value); + usbtrace(TRACE_CLSERROR(COMPOSITE_TRACEERR_GETUNKNOWNDESC), value); } break; } @@ -186,17 +483,19 @@ static int composite_setup(FAR struct usbdev_s *dev, { if (ctrl->type == 0) { - /* Signal the worker thread to instantiate the new configuration */ + /* Save the configuration and inform the constituent classes */ - priv->theventset |= USBSTRG_EVENT_CFGCHANGE; - priv->thvalue = value; - pthread_cond_signal(&priv->cond); + ret = CLASS_SETUP(priv->dev1, dev, ctrl); + dispatched = true; - /* Return here... the response will be provided later by the - * worker thread. - */ - - return OK; + if (ret >= 0) + { + ret = CLASS_SETUP(priv->dev1, dev, ctrl); + if (ret >= 0) + { + priv->config = value; + } + } } } break; @@ -213,23 +512,10 @@ static int composite_setup(FAR struct usbdev_s *dev, case USB_REQ_SETINTERFACE: { - if (ctrl->type == USB_REQ_RECIPIENT_INTERFACE) + if (ctrl->type == USB_REQ_RECIPIENT_INTERFACE && + priv->config == COMPOSITE_CONFIGID) { - if (priv->config == USBSTRG_CONFIGID && - index == USBSTRG_INTERFACEID && - value == USBSTRG_ALTINTERFACEID) - { - /* Signal to instantiate the interface change */ - - priv->theventset |= USBSTRG_EVENT_IFCHANGE; - pthread_cond_signal(&priv->cond); - - /* Return here... the response will be provided later by the - * worker thread. - */ - - return OK; - } + dispatched = (composite_classsetup(priv, dev, ctrl) >= 0); } } break; @@ -237,56 +523,265 @@ static int composite_setup(FAR struct usbdev_s *dev, case USB_REQ_GETINTERFACE: { if (ctrl->type == (USB_DIR_IN|USB_REQ_RECIPIENT_INTERFACE) && - priv->config == USBSTRG_CONFIGIDNONE) + priv->config == COMPOSITE_CONFIGIDNONE) { - if (index != USBSTRG_INTERFACEID) - { - ret = -EDOM; - } - else - { - ctrlreq->buf[0] = USBSTRG_ALTINTERFACEID; - ret = 1; - } + dispatched = (composite_classsetup(priv, dev, ctrl) >= 0); } } break; default: - usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req); + usbtrace(TRACE_CLSERROR(COMPOSITE_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req); break; } - - /* Respond to the setup command if data was returned. On an error return - * value (ret < 0), the USB driver will stall EP0. - */ - - if (ret >= 0) - { - ctrlreq->len = min(len, ret); - ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT; - ret = EP_SUBMIT(dev->ep0, ctrlreq); - if (ret < 0) - { - usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_EPRESPQ), (uint16_t)-ret); - ctrlreq->result = OK; - composite_ep0incomplete(dev->ep0, ctrlreq); - } } else { + uint8_t recipient; + /********************************************************************** * Non-Standard Class Requests **********************************************************************/ -#warning "Missing Logic" + + /* 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) + { + dispatched = (composite_classsetup(priv, dev, ctrl) >= 0); + } } + + /* 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) + { + ctrlreq->len = MIN(len, ret); + ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT; + 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 usbdev_s *dev) +{ + FAR struct composite_dev_s *priv; + irqstate_t flags; + + usbtrace(TRACE_CLASSDISCONNECT, 0); + +#ifdef CONFIG_DEBUG + if (!dev || !dev->ep0) + { + usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_INVALIDARG), 0); + return; + } +#endif + + /* Extract reference to private data */ + + priv = (FAR struct composite_dev_s *)dev->ep0->priv; + +#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 usbdev_s *dev) +{ + FAR struct composite_dev_s *priv; + irqstate_t flags; + + usbtrace(TRACE_CLASSSUSPEND, 0); + +#ifdef CONFIG_DEBUG + if (!dev || !dev->ep0 || !dev->ep0->priv) + { + usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_INVALIDARG), 0); + return; + } +#endif + + /* Extract reference to private data */ + + priv = (FAR struct composite_dev_s *)dev->ep0->priv; + + /* 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 usbdev_s *dev) +{ + FAR struct composite_dev_s *priv = NULL; + irqstate_t flags; + + if (!dev || !dev->ep0 || !dev->ep0->priv) + +#ifdef CONFIG_DEBUG + if (!dev || !dev->ep0) + { + usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_INVALIDARG), 0); + return; + } +#endif + + /* Extract reference to private data */ + + priv = (FAR struct composite_dev_s *)dev->ep0->priv; + + /* 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 port (and USB composite console if so configured). + * + * Input Parameter: + * Device minor number. E.g., minor 0 would correspond to /dev/ttyUSB0. + * + * Returned Value: + * Zero (OK) means that the driver was successfully registered. On any + * failure, a negated errno value is retured. + * + ****************************************************************************/ + +int composite_initialize(int minor) +{ + 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 -ENOMEM; + } + + /* 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 OK; + +errout_with_alloc: + kfree(alloc); + return ret; +} #endif /* CONFIG_USBDEV_COMPOSITE */ |