/**************************************************************************** * drivers/usbdev/adb.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you 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. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_USBADB_COMPOSITE # include # include "composite.h" #endif /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* FIXME use minor for char device npath */ #define USBADB_CHARDEV_PATH "/dev/adb0" /* USB Controller */ #ifdef CONFIG_USBDEV_SELFPOWERED # define USBADB_SELFPOWERED USB_CONFIG_ATTR_SELFPOWER #else # define USBADB_SELFPOWERED (0) #endif #ifdef CONFIG_USBDEV_REMOTEWAKEUP # define USBADB_REMOTEWAKEUP USB_CONFIG_ATTR_WAKEUP #else # define USBADB_REMOTEWAKEUP (0) #endif /* Buffer big enough for any of our descriptors (the config descriptor is the * biggest). */ #define USBADB_MXDESCLEN (64) #define USBADB_MAXSTRLEN (USBADB_MXDESCLEN-2) /* Device descriptor values */ #define USBADB_VERSIONNO (0x0101) /* Device version number 1.1 (BCD) */ /* String language */ #define USBADB_STR_LANGUAGE (0x0409) /* en-us */ /* Descriptor strings. If there serial device is part of a composite device * then the manufacturer, product, and serial number strings will be provided * by the composite logic. */ #ifndef CONFIG_USBADB_COMPOSITE # define USBADB_MANUFACTURERSTRID (1) # define USBADB_PRODUCTSTRID (2) # define USBADB_SERIALSTRID (3) # define USBADB_CONFIGSTRID (4) # define USBADB_INTERFACESTRID (5) #else # define USBADB_INTERFACESTRID (1) # define USBADB_NSTRIDS (1) #endif #define USBADB_NCONFIGS (1) #define USBADB_CONFIGID (1) #define USBADB_CONFIGIDNONE (0) /* Length of ADB descriptor */ #define USBADB_DESC_TOTALLEN 32 /**************************************************************************** * Private Types ****************************************************************************/ /* Manage char device non blocking io */ typedef struct adb_char_waiter_sem_s { sem_t sem; FAR struct adb_char_waiter_sem_s *next; } adb_char_waiter_sem_t; /* Container to support a list of requests */ struct usbadb_wrreq_s { FAR sq_entry_t node; /* Implements a singly linked list */ FAR struct usbdev_req_s *req; /* The contained request */ }; struct usbadb_rdreq_s { FAR sq_entry_t node; /* Implements a singly linked list */ FAR struct usbdev_req_s *req; /* The contained request */ uint16_t offset; /* Offset to valid data in the RX request */ }; /* This structure describes the internal state of the driver */ struct usbdev_adb_s { FAR struct usbdev_s *usbdev; /* usbdev driver pointer */ #ifdef CONFIG_USBADB_COMPOSITE struct usbdev_devinfo_s devinfo; #endif FAR struct usbdev_ep_s *epbulkin; /* Bulk IN endpoint structure */ FAR struct usbdev_ep_s *epbulkout; /* Bulk OUT endpoint structure */ FAR struct usbdev_req_s *ctrlreq; /* Preallocated control request */ uint8_t config; /* USB Configuration number */ struct sq_queue_s txfree; /* Available write request containers */ struct sq_queue_s rxpending; /* Pending read request containers */ /* Pre-allocated request containers. The write requests will be * linked in a free list (txfree), and used to send requests to * EPBULKIN; Read requests will be queued in the EBULKOUT. */ struct usbadb_wrreq_s wrreqs[CONFIG_USBADB_NWRREQS]; struct usbadb_rdreq_s rdreqs[CONFIG_USBADB_NRDREQS]; /* Char device driver */ sem_t exclsem; /* Enforces device exclusive access */ adb_char_waiter_sem_t *rdsems; /* List of blocking readers */ adb_char_waiter_sem_t *wrsems; /* List of blocking writers */ uint8_t crefs; /* Count of opened instances */ FAR struct pollfd *fds[CONFIG_USBADB_NPOLLWAITERS]; }; struct adb_driver_s { struct usbdevclass_driver_s drvr; struct usbdev_adb_s dev; }; struct adb_cfgdesc_s { #ifndef CONFIG_USBADB_COMPOSITE struct usb_cfgdesc_s cfgdesc; /* Configuration descriptor */ #endif struct usb_ifdesc_s ifdesc; /* ADB interface descriptor */ }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /* USB class device *********************************************************/ static int usbclass_bind(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev); static void usbclass_unbind(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev); static int usbclass_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 usbclass_disconnect(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev); static void usbclass_suspend(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev); static void usbclass_resume(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev); static FAR struct usbdev_req_s *usbclass_allocreq(FAR struct usbdev_ep_s *ep, uint16_t len); /* Char device Operations ***************************************************/ static int adb_char_open(FAR struct file *filep); static int adb_char_close(FAR struct file *filep); static ssize_t adb_char_read(FAR struct file *filep, FAR char *buffer, size_t len); static ssize_t adb_char_write(FAR struct file *filep, FAR const char *buffer, size_t len); static int adb_char_ioctl(FAR struct file *filep, int cmd, unsigned long arg); static int adb_char_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup); static void adb_char_notify_readers(FAR struct usbdev_adb_s *priv); static void adb_char_pollnotify(FAR struct usbdev_adb_s *dev, pollevent_t eventset); static void adb_char_on_connect(FAR struct usbdev_adb_s *priv, int connect); /**************************************************************************** * Private Data ****************************************************************************/ /* USB class device *********************************************************/ static const struct usbdevclass_driverops_s g_adb_driverops = { usbclass_bind, /* bind */ usbclass_unbind, /* unbind */ usbclass_setup, /* setup */ usbclass_disconnect, /* disconnect */ usbclass_suspend, /* suspend */ usbclass_resume /* resume */ }; /* Char device **************************************************************/ static const struct file_operations g_adb_fops = { adb_char_open, /* open */ adb_char_close, /* close */ adb_char_read, /* read */ adb_char_write, /* write */ 0, /* seek */ adb_char_ioctl, /* ioctl */ adb_char_poll /* poll */ }; /* USB descriptor ***********************************************************/ #ifndef CONFIG_USBADB_COMPOSITE static const struct usb_devdesc_s g_adb_devdesc = { .len = USB_SIZEOF_DEVDESC, /* Descriptor length */ .type = USB_DESC_TYPE_DEVICE, /* Descriptor type */ .usb = /* USB version */ { LSBYTE(0x0200), MSBYTE(0x0200) }, .classid = 0, /* Device class */ .subclass = 0, /* Device sub-class */ .protocol = 0, /* Device protocol */ .mxpacketsize = CONFIG_USBADB_EP0MAXPACKET, /* Max packet size (ep0) */ .vendor = /* Vendor ID */ { LSBYTE(CONFIG_USBADB_VENDORID), MSBYTE(CONFIG_USBADB_VENDORID) }, .product = /* Product ID */ { LSBYTE(CONFIG_USBADB_PRODUCTID), MSBYTE(CONFIG_USBADB_PRODUCTID) }, .device = /* Device ID */ { LSBYTE(USBADB_VERSIONNO), MSBYTE(USBADB_VERSIONNO) }, .imfgr = USBADB_MANUFACTURERSTRID, /* Manufacturer */ .iproduct = USBADB_PRODUCTSTRID, /* Product */ .serno = USBADB_SERIALSTRID, /* Serial number */ .nconfigs = 1 /* Number of configurations */ }; #endif static const struct adb_cfgdesc_s g_adb_cfgdesc = { #ifndef CONFIG_USBADB_COMPOSITE { .len = USB_SIZEOF_CFGDESC, /* Descriptor length */ .type = USB_DESC_TYPE_CONFIG, /* Descriptor type */ .totallen = { LSBYTE(USBADB_DESC_TOTALLEN), /* LS Total length */ MSBYTE(USBADB_DESC_TOTALLEN) /* MS Total length */ }, .ninterfaces = 1, /* Number of interfaces */ .cfgvalue = 1, /* Configuration value */ .icfg = USBADB_CONFIGSTRID, /* Configuration */ .attr = USB_CONFIG_ATTR_ONE | USBADB_SELFPOWERED | USBADB_REMOTEWAKEUP, /* Attributes */ .mxpower = (CONFIG_USBDEV_MAXPOWER + 1) / 2 /* Max power (mA/2) */ }, #endif { .len = USB_SIZEOF_IFDESC, .type = USB_DESC_TYPE_INTERFACE, .ifno = 0, .alt = 0, .neps = 2, .classid = USB_CLASS_VENDOR_SPEC, .subclass = 0x42, .protocol = 0x01, .iif = USBADB_INTERFACESTRID } }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: usbclass_allocreq * * Description: * Allocate request buffer for a specified endpoint. * ****************************************************************************/ static FAR struct usbdev_req_s *usbclass_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 == NULL) { EP_FREEREQ(ep, req); req = NULL; } } return req; } /**************************************************************************** * Name: usbclass_copy_epdesc * * Description: * Copies the requested Endpoint Description into the buffer given. * Returns the number of Bytes filled in ( sizeof(struct usb_epdesc_s) ). * ****************************************************************************/ static int usbclass_copy_epdesc(int epid, FAR struct usb_epdesc_s *epdesc, FAR struct usbdev_devinfo_s *devinfo, bool hispeed) { #ifndef CONFIG_USBDEV_DUALSPEED UNUSED(hispeed); #endif epdesc->len = USB_SIZEOF_EPDESC; /* Descriptor length */ epdesc->type = USB_DESC_TYPE_ENDPOINT; /* Descriptor type */ epdesc->attr = USB_EP_ATTR_XFER_BULK | /* Endpoint attributes */ USB_EP_ATTR_NO_SYNC | USB_EP_ATTR_USAGE_DATA; epdesc->interval = 0; /* Interval */ if (epid == USBADB_EP_BULKIN_IDX) /* Bulk IN endpoint */ { /* Endpoint address */ #ifdef CONFIG_USBADB_COMPOSITE epdesc->addr = USB_EPIN(devinfo->epno[USBADB_EP_BULKIN_IDX]); #else epdesc->addr = USB_EPIN(CONFIG_USBADB_EPBULKIN); #endif #ifdef CONFIG_USBDEV_DUALSPEED if (hispeed) { /* Maximum packet size (high speed) */ epdesc->mxpacketsize[0] = LSBYTE(CONFIG_USBADB_EPBULKIN_HSSIZE); epdesc->mxpacketsize[1] = MSBYTE(CONFIG_USBADB_EPBULKIN_HSSIZE); } else #endif { /* Maximum packet size (full speed) */ epdesc->mxpacketsize[0] = LSBYTE(CONFIG_USBADB_EPBULKIN_FSSIZE); epdesc->mxpacketsize[1] = MSBYTE(CONFIG_USBADB_EPBULKIN_FSSIZE); } } else /* USBADB_EP_BULKOUT_IDX: Bulk OUT endpoint */ { /* Endpoint address */ #ifdef CONFIG_USBADB_COMPOSITE epdesc->addr = USB_EPOUT(devinfo->epno[USBADB_EP_BULKOUT_IDX]); #else epdesc->addr = USB_EPOUT(CONFIG_USBADB_EPBULKOUT); #endif #ifdef CONFIG_USBDEV_DUALSPEED if (hispeed) { /* Maximum packet size (high speed) */ epdesc->mxpacketsize[0] = LSBYTE(CONFIG_USBADB_EPBULKOUT_HSSIZE); epdesc->mxpacketsize[1] = MSBYTE(CONFIG_USBADB_EPBULKOUT_HSSIZE); } else #endif { /* Maximum packet size (full speed) */ epdesc->mxpacketsize[0] = LSBYTE(CONFIG_USBADB_EPBULKOUT_FSSIZE); epdesc->mxpacketsize[1] = MSBYTE(CONFIG_USBADB_EPBULKOUT_FSSIZE); } } return sizeof(struct usb_epdesc_s); } /**************************************************************************** * Name: usb_adb_submit_rdreq * * Description: * Submits the bulk OUT read request. Takes care not to submit the request * when the RX packet buffer is already in use. * * Input Parameters: * priv: pointer to ADB device driver structure * * Returned Value: * The return value of the EP_SUBMIT operation * ****************************************************************************/ static int usb_adb_submit_rdreq(FAR struct usbdev_adb_s *priv, FAR struct usbadb_rdreq_s *rdcontainer) { FAR struct usbdev_req_s *req; FAR struct usbdev_ep_s *ep; int ret; DEBUGASSERT(priv != NULL && rdcontainer != NULL); req = rdcontainer->req; DEBUGASSERT(req != NULL); /* Requeue the read request */ ep = priv->epbulkout; req->len = ep->maxpacket; ret = EP_SUBMIT(ep, req); if (ret != OK) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSUBMIT), (uint16_t)-req->result); } return ret; } /**************************************************************************** * Name: usb_adb_wrcomplete * * Description: * Handle completion of write request. This function probably executes * in the context of an interrupt handler. * ****************************************************************************/ static void usb_adb_wrcomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) { FAR struct usbadb_wrreq_s *wrcontainer; FAR struct usbdev_adb_s *priv; irqstate_t flags; /* Sanity check */ #ifdef CONFIG_DEBUG_FEATURES if (!ep || !ep->priv || !req || !req->priv) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0); return; } #endif /* Extract references to private data */ priv = (FAR struct usbdev_adb_s *)ep->priv; wrcontainer = (FAR struct usbadb_wrreq_s *)req->priv; /* Return the write request to the free list */ flags = enter_critical_section(); sq_addlast(&wrcontainer->node, &priv->txfree); /* Check for termination condition */ switch (req->result) { case OK: /* Normal completion */ { usbtrace(TRACE_CLASSWRCOMPLETE, priv->nwrq); /* Notify all waiting writers that write req is available */ adb_char_waiter_sem_t *cur_sem = priv->wrsems; while (cur_sem != NULL) { nxsem_post(&cur_sem->sem); cur_sem = cur_sem->next; } priv->wrsems = NULL; /* Notify all poll/select waiters */ adb_char_pollnotify(priv, POLLOUT); } break; case -ESHUTDOWN: /* Disconnection */ { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRSHUTDOWN), priv->nwrq); } break; default: /* Some other error occurred */ { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRUNEXPECTED), (uint16_t)-req->result); } break; } leave_critical_section(flags); } /**************************************************************************** * Name: usb_adb_rdcomplete * * Description: * Handle completion of read request on the bulk OUT endpoint. * ****************************************************************************/ static void usb_adb_rdcomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) { FAR struct usbadb_rdreq_s *rdcontainer; FAR struct usbdev_adb_s *priv; irqstate_t flags; /* Sanity check */ #ifdef CONFIG_DEBUG_FEATURES if (!ep || !ep->priv || !req) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0); return; } #endif /* Extract references to private data */ priv = (FAR struct usbdev_adb_s *)ep->priv; rdcontainer = (FAR struct usbadb_rdreq_s *)req->priv; /* Process the received data unless this is some unusual condition */ switch (req->result) { case 0: /* Normal completion */ usbtrace(TRACE_CLASSRDCOMPLETE, priv->nrdq); /* Restart request due to either no reader or * empty frame received. */ if (priv->crefs == 0) { uwarn("drop frame\n"); goto restart_req; } if (req->xfrd <= 0) { goto restart_req; } /* Queue request and notify readers */ flags = enter_critical_section(); /* Put request on RX pending queue */ rdcontainer->offset = 0; sq_addlast(&rdcontainer->node, &priv->rxpending); adb_char_notify_readers(priv); leave_critical_section(flags); return; case -ESHUTDOWN: /* Disconnection */ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSHUTDOWN), 0); return; default: /* Some other error occurred */ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDUNEXPECTED), (uint16_t)-req->result); goto restart_req; }; restart_req: /* Restart request */ usb_adb_submit_rdreq(priv, rdcontainer); } /**************************************************************************** * Name: usbclass_resetconfig * * Description: * Mark the device as not configured and disable all endpoints. * ****************************************************************************/ static void usbclass_resetconfig(FAR struct usbdev_adb_s *priv) { /* Are we configured? */ if (priv->config != USBADB_CONFIGIDNONE) { /* Yes.. but not anymore */ adb_char_on_connect(priv, 0); /* Disable endpoints. This should force completion of all pending * transfers. */ EP_DISABLE(priv->epbulkin); EP_DISABLE(priv->epbulkout); } priv->config = USBADB_CONFIGIDNONE; } /**************************************************************************** * Name: usbclass_setconfig * * Description: * Set the device configuration by allocating and configuring endpoints and * by allocating and queue read and write requests. * ****************************************************************************/ static int usbclass_setconfig(FAR struct usbdev_adb_s *priv, uint8_t config) { struct usb_epdesc_s epdesc; bool hispeed = false; int i; int ret = 0; #ifdef CONFIG_DEBUG_FEATURES if (priv == NULL) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0); return -EIO; } #endif #ifdef CONFIG_USBDEV_DUALSPEED hispeed = (priv->usbdev->speed == USB_SPEED_HIGH); #endif if (config == priv->config) { /* Already configured -- Do nothing */ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_ALREADYCONFIGURED), 0); return 0; } /* Discard the previous configuration data */ usbclass_resetconfig(priv); /* Was this a request to simply discard the current configuration? */ if (config == USBADB_CONFIGIDNONE) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_CONFIGNONE), 0); return 0; } /* We only accept one configuration */ if (config != USBADB_CONFIGID) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_CONFIGIDBAD), 0); return -EINVAL; } /* Configure the IN bulk endpoint */ #ifdef CONFIG_USBADB_COMPOSITE usbclass_copy_epdesc(USBADB_EP_BULKIN_IDX, &epdesc, &priv->devinfo, hispeed); #else usbclass_copy_epdesc(USBADB_EP_BULKIN_IDX, &epdesc, NULL, hispeed); #endif ret = EP_CONFIGURE(priv->epbulkin, &epdesc, false); if (ret < 0) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKINCONFIGFAIL), 0); goto errout; } priv->epbulkin->priv = priv; /* Configure the OUT bulk endpoint */ #ifdef CONFIG_USBADB_COMPOSITE usbclass_copy_epdesc(USBADB_EP_BULKOUT_IDX, &epdesc, &priv->devinfo, hispeed); #else usbclass_copy_epdesc(USBADB_EP_BULKOUT_IDX, &epdesc, NULL, hispeed); #endif ret = EP_CONFIGURE(priv->epbulkout, &epdesc, true); if (ret < 0) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKOUTCONFIGFAIL), 0); goto errout; } priv->epbulkout->priv = priv; /* Queue read requests in the bulk OUT endpoint */ for (i = 0; i < CONFIG_USBADB_NRDREQS; i++) { priv->rdreqs[i].req->callback = usb_adb_rdcomplete; ret = usb_adb_submit_rdreq(priv, &priv->rdreqs[i]); if (ret != OK) { /* TODO cancel submitted requests */ goto errout; } } /* We are successfully configured. Char device is now active */ priv->config = config; adb_char_on_connect(priv, 1); return OK; errout: usbclass_resetconfig(priv); return ret; } /**************************************************************************** * Name: usbclass_ep0incomplete * * Description: * Handle completion of EP0 control operations * ****************************************************************************/ static void usbclass_ep0incomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) { if (req->result || req->xfrd != req->len) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_REQRESULT), (uint16_t)-req->result); } } #ifdef CONFIG_USBDEV_DUALSPEED static int16_t usbclass_mkcfgdesc(FAR uint8_t *buf, FAR struct usbdev_devinfo_s *devinfo, uint8_t speed, uint8_t type); #else static int16_t usbclass_mkcfgdesc(FAR uint8_t *buf, FAR struct usbdev_devinfo_s *devinfo) #endif { bool hispeed = false; FAR struct usb_epdesc_s *epdesc; FAR struct adb_cfgdesc_s *dest; #ifdef CONFIG_USBDEV_DUALSPEED hispeed = (speed == USB_SPEED_HIGH); /* Check for switches between high and full speed */ if (type == USB_DESC_TYPE_OTHERSPEEDCONFIG) { hispeed = !hispeed; } #endif dest = (FAR struct adb_cfgdesc_s *)buf; epdesc = (FAR struct usb_epdesc_s *)(buf + sizeof(g_adb_cfgdesc)); memcpy(dest, &g_adb_cfgdesc, sizeof(g_adb_cfgdesc)); #ifdef CONFIG_USBADB_COMPOSITE usbclass_copy_epdesc(USBADB_EP_BULKIN_IDX, &epdesc[0], devinfo, hispeed); usbclass_copy_epdesc(USBADB_EP_BULKOUT_IDX, &epdesc[1], devinfo, hispeed); #else usbclass_copy_epdesc(USBADB_EP_BULKIN_IDX, &epdesc[0], NULL, hispeed); usbclass_copy_epdesc(USBADB_EP_BULKOUT_IDX, &epdesc[1], NULL, hispeed); #endif #ifdef CONFIG_USBADB_COMPOSITE /* For composite device, apply possible offset to the interface numbers */ dest->ifdesc.ifno = devinfo->ifnobase; dest->ifdesc.iif = devinfo->strbase + USBADB_INTERFACESTRID; #endif return sizeof(g_adb_cfgdesc)+2*USB_SIZEOF_EPDESC; } static int usbclass_mkstrdesc(uint8_t id, FAR struct usb_strdesc_s *strdesc) { FAR const char *str; int len; int ndata; int i; switch (id) { #ifndef CONFIG_USBADB_COMPOSITE case 0: { /* Descriptor 0 is the language id */ strdesc->len = 4; strdesc->type = USB_DESC_TYPE_STRING; strdesc->data[0] = LSBYTE(USBADB_STR_LANGUAGE); strdesc->data[1] = MSBYTE(USBADB_STR_LANGUAGE); return 4; } case USBADB_MANUFACTURERSTRID: str = CONFIG_USBADB_VENDORSTR; break; case USBADB_PRODUCTSTRID: str = CONFIG_USBADB_PRODUCTSTR; break; case USBADB_SERIALSTRID: str = CONFIG_USBADB_SERIALSTR; break; case USBADB_CONFIGSTRID: str = CONFIG_USBADB_CONFIGSTR; break; #endif /* Composite driver removes offset before calling mkstrdesc() */ case USBADB_INTERFACESTRID: str = CONFIG_USBADB_INTERFACESTR; break; default: return -EINVAL; } /* The string is utf16-le. The poor man's utf-8 to utf16-le * conversion below will only handle 7-bit en-us ascii */ len = strlen(str); if (len > (USBADB_MAXSTRLEN / 2)) { len = (USBADB_MAXSTRLEN / 2); } for (i = 0, ndata = 0; i < len; i++, ndata += 2) { strdesc->data[ndata] = str[i]; strdesc->data[ndata + 1] = 0; } strdesc->len = ndata + 2; strdesc->type = USB_DESC_TYPE_STRING; return strdesc->len; } /**************************************************************************** * USB Class Driver Methods ****************************************************************************/ /**************************************************************************** * Name: usbclass_bind * * Description: * Invoked when the driver is bound to a USB device driver * ****************************************************************************/ static int usbclass_bind(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev) { int ret; int i; uint16_t reqlen; irqstate_t flags; FAR struct usbdev_adb_s *priv = &((FAR struct adb_driver_s *)driver)->dev; usbtrace(TRACE_CLASSBIND, 0); priv->ctrlreq = usbclass_allocreq(dev->ep0, USBADB_MXDESCLEN); if (priv->ctrlreq == NULL) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_ALLOCCTRLREQ), 0); return -ENOMEM; } priv->ctrlreq->callback = usbclass_ep0incomplete; /* Pre-allocate all endpoints... the endpoints will not be functional * until the SET CONFIGURATION request is processed in usbclass_setconfig. * This is done here because there may be calls to kmm_malloc and the SET * CONFIGURATION processing probably occurs within interrupt handling * logic where kmm_malloc calls will fail. */ /* Pre-allocate the IN bulk endpoint */ priv->epbulkin = DEV_ALLOCEP(dev, #ifdef CONFIG_USBADB_COMPOSITE USB_EPIN(priv->devinfo.epno[USBADB_EP_BULKIN_IDX]), #else USB_EPIN(CONFIG_USBADB_EPBULKIN), #endif true, USB_EP_ATTR_XFER_BULK); if (!priv->epbulkin) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKINALLOCFAIL), 0); ret = -ENODEV; goto errout; } priv->epbulkin->priv = priv; /* Pre-allocate the OUT bulk endpoint */ priv->epbulkout = DEV_ALLOCEP(dev, #ifdef CONFIG_USBADB_COMPOSITE USB_EPOUT(priv->devinfo.epno[USBADB_EP_BULKOUT_IDX]), #else USB_EPOUT(CONFIG_USBADB_EPBULKOUT), #endif false, USB_EP_ATTR_XFER_BULK); if (!priv->epbulkout) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKOUTALLOCFAIL), 0); ret = -ENODEV; goto errout; } priv->epbulkout->priv = priv; /* Pre-allocate read requests. The buffer size is one full packet. */ #ifdef CONFIG_USBDEV_DUALSPEED reqlen = CONFIG_USBADB_EPBULKOUT_HSSIZE; #else reqlen = CONFIG_USBADB_EPBULKOUT_FSSIZE; #endif for (i = 0; i < CONFIG_USBADB_NRDREQS; i++) { FAR struct usbadb_rdreq_s *rdcontainer; rdcontainer = &priv->rdreqs[i]; rdcontainer->req = usbclass_allocreq(priv->epbulkout, reqlen); if (rdcontainer->req == NULL) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDALLOCREQ), -ENOMEM); ret = -ENOMEM; goto errout; } rdcontainer->offset = 0; rdcontainer->req->priv = rdcontainer; rdcontainer->req->callback = usb_adb_rdcomplete; } /* Pre-allocate write requests. The buffer size is one full packet. */ #ifdef CONFIG_USBDEV_DUALSPEED reqlen = CONFIG_USBADB_EPBULKIN_HSSIZE; #else reqlen = CONFIG_USBADB_EPBULKIN_FSSIZE; #endif for (i = 0; i < CONFIG_USBADB_NWRREQS; i++) { FAR struct usbadb_wrreq_s *wrcontainer; wrcontainer = &priv->wrreqs[i]; wrcontainer->req = usbclass_allocreq(priv->epbulkin, reqlen); if (wrcontainer->req == NULL) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRALLOCREQ), -ENOMEM); ret = -ENOMEM; goto errout; } wrcontainer->req->priv = wrcontainer; wrcontainer->req->callback = usb_adb_wrcomplete; flags = enter_critical_section(); sq_addlast(&wrcontainer->node, &priv->txfree); leave_critical_section(flags); } /* Report if we are selfpowered (unless we are part of a * composite device) */ #ifndef CONFIG_USBADB_COMPOSITE #ifdef CONFIG_USBDEV_SELFPOWERED DEV_SETSELFPOWERED(dev); #endif /* And pull-up the data line for the soft connect function (unless we are * part of a composite device) */ DEV_CONNECT(dev); #endif return OK; errout: usbclass_unbind(driver, dev); return ret; } /**************************************************************************** * Name: usbclass_unbind * * Description: * Invoked when the driver is unbound from a USB device driver * ****************************************************************************/ static void usbclass_unbind(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev) { usbtrace(TRACE_CLASSUNBIND, 0); #warning Missing logic } /**************************************************************************** * Name: usbclass_setup * * Description: * Invoked for ep0 control requests. This function probably executes * in the context of an interrupt handler. * ****************************************************************************/ static int usbclass_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) { uint16_t value; uint16_t len; int ret = -EOPNOTSUPP; FAR struct usbdev_adb_s *priv; FAR struct usbdev_req_s *ctrlreq; #ifdef CONFIG_DEBUG_FEATURES if (!driver || !dev || !dev->ep0 || !ctrl) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0); return -EIO; } #endif /* Extract reference to private data */ usbtrace(TRACE_CLASSSETUP, ctrl->req); priv = &((FAR struct adb_driver_s *)driver)->dev; #ifdef CONFIG_DEBUG_FEATURES if (!priv || !priv->ctrlreq) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EP0NOTBOUND), 0); return -ENODEV; } #endif ctrlreq = priv->ctrlreq; /* Extract the little-endian 16-bit values to host order */ value = GETUINT16(ctrl->value); len = GETUINT16(ctrl->len); uinfo("type=%02x req=%02x value=%04x index=%04x len=%04x\n", ctrl->type, ctrl->req, value, GETUINT16(ctrl->index), len); switch (ctrl->type & USB_REQ_TYPE_MASK) { case USB_REQ_TYPE_STANDARD: { switch (ctrl->req) { #ifndef CONFIG_USBADB_COMPOSITE 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]) { /* If the device is used in as part of a composite * device, then the device descriptor is provided by logic * in the composite device implementation. */ case USB_DESC_TYPE_DEVICE: { ret = USB_SIZEOF_DEVDESC; memcpy(ctrlreq->buf, &g_adb_devdesc, ret); } break; case USB_DESC_TYPE_DEVICEQUALIFIER: break; case USB_DESC_TYPE_OTHERSPEEDCONFIG: break; /* If the serial device is used in as part of a composite * device, then the configuration descriptor is provided by * logic in the composite device implementation. */ case USB_DESC_TYPE_CONFIG: { ret = usbclass_mkcfgdesc(ctrlreq->buf, NULL); } break; /* If the serial device is used in as part of a composite * device, then the language string descriptor is provided * by logic in the composite device implementation. */ case USB_DESC_TYPE_STRING: { /* index == language code. */ ret = usbclass_mkstrdesc(ctrl->value[0], (FAR struct usb_strdesc_s *) ctrlreq->buf); } break; default: { usbtrace( TRACE_CLSERROR(USBSER_TRACEERR_GETUNKNOWNDESC), value); } break; } } break; /* If the serial device is used in as part of a composite device, * then the overall composite class configuration is managed by * logic in the composite device implementation. */ case USB_REQ_GETCONFIGURATION: { if (ctrl->type == USB_DIR_IN) { *(FAR uint8_t *)ctrlreq->buf = priv->config; ret = 1; } } break; #endif /* !CONFIG_USBADB_COMPOSITE */ case USB_REQ_SETCONFIGURATION: { if (ctrl->type == 0) { ret = usbclass_setconfig(priv, value); } } break; default: usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req); break; } } case USB_REQ_TYPE_CLASS: { /* ADB-Specific Requests */ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDCLASSREQ), ctrl->req); break; } default: { usbtrace( TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDTYPE), ctrl->type); } } #ifndef CONFIG_USBADB_COMPOSITE /* Respond to the setup command if data was returned. On an error return * value (ret < 0), the USB driver will stall. */ if (ret >= 0) { ctrlreq->len = (len < ret) ? len : ret; ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT; /* Send the response -- either directly to the USB controller or * indirectly in the case where this class is a member of a composite * device. */ ret = EP_SUBMIT(dev->ep0, ctrlreq); if (ret < 0) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPRESPQ), (uint16_t)-ret); ctrlreq->result = OK; usbclass_ep0incomplete(dev->ep0, ctrlreq); } } #else /* Composite should send only one request for USB_REQ_SETCONFIGURATION. * Hence ADB driver cannot submit to ep0; composite has to handle it. */ #warning composite_ep0submit() seems broken so skip it in case of composite #endif /* !CONFIG_USBADB_COMPOSITE */ /* Returning a negative value will cause a STALL */ return ret; } /**************************************************************************** * Name: usbclass_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 usbclass_disconnect(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev) { FAR struct usbdev_adb_s *priv; irqstate_t flags; usbtrace(TRACE_CLASSDISCONNECT, 0); #ifdef CONFIG_DEBUG_FEATURES if (!driver || !dev || !dev->ep0) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0); return; } #endif /* Extract reference to private data */ priv = &((FAR struct adb_driver_s *)driver)->dev; #ifdef CONFIG_DEBUG_FEATURES if (!priv) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EP0NOTBOUND), 0); return; } #endif /* FIXME do we have to lock interrupts here ? */ flags = enter_critical_section(); /* Reset the configuration */ usbclass_resetconfig(priv); leave_critical_section(flags); /* Perform the soft connect function so that we will we can be * re-enumerated (unless we are part of a composite device) */ #ifndef CONFIG_USBDEV_COMPOSITE DEV_CONNECT(dev); #endif } /**************************************************************************** * Name: usbclass_suspend * * Description: * Handle the USB suspend event. * ****************************************************************************/ static void usbclass_suspend(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev) { FAR struct usbdev_adb_s *priv = &((FAR struct adb_driver_s *)driver)->dev; usbtrace(TRACE_CLASSSUSPEND, 0); if (priv->config != USBADB_CONFIGIDNONE) { adb_char_on_connect(priv, 0); } } /**************************************************************************** * Name: usbclass_resume * * Description: * Handle the USB resume event. * ****************************************************************************/ static void usbclass_resume(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev) { FAR struct usbdev_adb_s *priv = &((FAR struct adb_driver_s *)driver)->dev; usbtrace(TRACE_CLASSRESUME, 0); if (priv->config != USBADB_CONFIGIDNONE) { adb_char_on_connect(priv, 1); } } /**************************************************************************** * Name: usbclass_classobject * * Description: * Register USB driver and return the class object. * * Returned Value: * 0 on success, negative error code on failure. * ****************************************************************************/ static int usbclass_classobject(int minor, FAR struct usbdev_devinfo_s *devinfo, FAR struct usbdevclass_driver_s **classdev) { int ret; FAR struct adb_driver_s *alloc; alloc = (FAR struct adb_driver_s *) kmm_zalloc(sizeof(struct adb_driver_s)); if (!alloc) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_ALLOCDEVSTRUCT), 0); return -ENOMEM; } /* Initialize the USB class driver structure */ #ifdef CONFIG_USBDEV_DUALSPEED alloc->drvr.speed = USB_SPEED_HIGH; #else alloc->drvr.speed = USB_SPEED_FULL; #endif alloc->drvr.ops = &g_adb_driverops; sq_init(&alloc->dev.rxpending); sq_init(&alloc->dev.txfree); #ifdef CONFIG_USBADB_COMPOSITE /* Save the caller provided device description (composite only) */ memcpy(&alloc->dev.devinfo, devinfo, sizeof(struct usbdev_devinfo_s)); #endif /* Initialize the char device structure */ nxsem_init(&alloc->dev.exclsem, 0, 1); alloc->dev.crefs = 0; /* Register char device driver */ /* FIXME use minor in device name */ ret = register_driver(USBADB_CHARDEV_PATH, &g_adb_fops, 0666, &alloc->dev); if (ret < 0) { uerr("Failed to register char device"); goto exit_free_driver; } *classdev = &alloc->drvr; return OK; exit_free_driver: kmm_free(alloc); return ret; } /**************************************************************************** * Name: usbclass_uninitialize * * Description: * Free allocated memory * * Returned Value: * 0 on success, negative error code on failure. * ****************************************************************************/ static void usbclass_uninitialize(FAR struct usbdevclass_driver_s *classdev) { FAR struct adb_driver_s *alloc = container_of( classdev, FAR struct adb_driver_s, drvr); #warning FIXME Maybe missing logic here unregister_driver(USBADB_CHARDEV_PATH); kmm_free(alloc); } /**************************************************************************** * Char Device Driver Methods ****************************************************************************/ /**************************************************************************** * Name: adb_char_notify_readers * * Description: * Notify threads waiting to read device. This function must be called * with interrupt disabled. * ****************************************************************************/ static void adb_char_notify_readers(FAR struct usbdev_adb_s *priv) { /* Notify all of the waiting readers */ adb_char_waiter_sem_t *cur_sem = priv->rdsems; while (cur_sem != NULL) { nxsem_post(&cur_sem->sem); cur_sem = cur_sem->next; } priv->rdsems = NULL; /* Notify all poll/select waiters */ adb_char_pollnotify(priv, POLLIN); } /**************************************************************************** * Name: adb_char_pollnotify * * Description: * Notify threads waiting for device event. This function must be called * with interrupt disabled. * ****************************************************************************/ static void adb_char_pollnotify(FAR struct usbdev_adb_s *dev, pollevent_t eventset) { FAR struct pollfd *fds; int i; for (i = 0; i < CONFIG_USBADB_NPOLLWAITERS; i++) { fds = dev->fds[i]; if (fds) { fds->revents |= eventset & (fds->events | POLLERR | POLLHUP); if (fds->revents != 0) { nxsem_post(fds->sem); } } } } /**************************************************************************** * Name: adb_char_open * * Description: * Open adb device. Only one open() instance is supported. * ****************************************************************************/ static int adb_char_open(FAR struct file *filep) { FAR struct inode *inode = filep->f_inode; FAR struct usbdev_adb_s *priv = inode->i_private; int ret; /* Get exclusive access to the device structures */ ret = nxsem_wait(&priv->exclsem); if (ret < 0) { return ret; } finfo("entry: <%s> %d\n", inode->i_name, priv->crefs); priv->crefs += 1; assert(priv->crefs != 0); nxsem_post(&priv->exclsem); return ret; } /**************************************************************************** * Name: adb_char_close * * Description: * Close adb device. * ****************************************************************************/ static int adb_char_close(FAR struct file *filep) { int ret; FAR struct inode *inode = filep->f_inode; FAR struct usbdev_adb_s *priv = inode->i_private; /* Get exclusive access to the device structures */ ret = nxsem_wait(&priv->exclsem); if (ret < 0) { return ret; } finfo("entry: <%s> %d\n", inode->i_name, priv->crefs); priv->crefs -= 1; assert(priv->crefs >= 0); nxsem_post(&priv->exclsem); return OK; } /**************************************************************************** * Name: adb_char_blocking_io * * Description: * Handle read/write blocking io. * ****************************************************************************/ static int adb_char_blocking_io(FAR struct usbdev_adb_s *priv, FAR adb_char_waiter_sem_t *sem, FAR adb_char_waiter_sem_t **slist, FAR struct sq_queue_s *queue) { int ret; irqstate_t flags; flags = enter_critical_section(); if (!sq_empty(queue)) { /* Queue not empty after all */ leave_critical_section(flags); return 0; } /* Register waiter semaphore */ sem->next = *slist; *slist = sem; leave_critical_section(flags); nxsem_post(&priv->exclsem); /* Wait for USB device to notify */ ret = nxsem_wait(&sem->sem); if (ret < 0) { /* Interrupted wait, unregister semaphore * TODO ensure that exclsem wait does not fail (ECANCELED) */ nxsem_wait_uninterruptible(&priv->exclsem); flags = enter_critical_section(); adb_char_waiter_sem_t *cur_sem = *slist; if (cur_sem == sem) { *slist = sem->next; } else { while (cur_sem) { if (cur_sem->next == sem) { cur_sem->next = sem->next; break; } } } leave_critical_section(flags); nxsem_post(&priv->exclsem); return ret; } return nxsem_wait(&priv->exclsem); } /**************************************************************************** * Name: adb_char_read * * Description: * Read adb device. * ****************************************************************************/ static ssize_t adb_char_read(FAR struct file *filep, FAR char *buffer, size_t len) { FAR struct inode *inode = filep->f_inode; FAR struct usbdev_adb_s *priv = inode->i_private; ssize_t ret; size_t retlen; irqstate_t flags; assert(len > 0 && buffer != NULL); if (priv->config == USBADB_CONFIGIDNONE) { /* USB device not connected */ return -EPIPE; } ret = nxsem_wait(&priv->exclsem); if (ret < 0) { return ret; } /* Check for available data */ if (sq_empty(&priv->rxpending)) { if (filep->f_oflags & O_NONBLOCK) { nxsem_post(&priv->exclsem); return -EAGAIN; } adb_char_waiter_sem_t sem; nxsem_init(&sem.sem, 0, 0); nxsem_set_protocol(&sem.sem, SEM_PRIO_NONE); do { /* RX queue seems empty. Check again with interrupts disabled */ ret = adb_char_blocking_io( priv, &sem, &priv->rdsems, &priv->rxpending); if (ret < 0) { nxsem_destroy(&sem.sem); return ret; } } while (sq_empty(&priv->rxpending)); /* RX queue not empty and exclsem locked so we are the only reader */ nxsem_destroy(&sem.sem); } /* Device ready for read */ retlen = 0; while (!sq_empty(&priv->rxpending) && len > 0) { FAR struct usbadb_rdreq_s *rdcontainer; uint16_t reqlen; /* Process each packet in the priv->rxpending list */ rdcontainer = container_of( sq_peek(&priv->rxpending), struct usbadb_rdreq_s, node); reqlen = rdcontainer->req->xfrd - rdcontainer->offset; if (reqlen > len) { /* Output buffer full */ memcpy(&buffer[retlen], &rdcontainer->req->buf[rdcontainer->offset], len); rdcontainer->offset += len; retlen += len; break; } memcpy(&buffer[retlen], &rdcontainer->req->buf[rdcontainer->offset], reqlen); retlen += reqlen; len -= reqlen; /* The entire packet was processed and may be removed from the * pending RX list. */ /* FIXME use atomic queue primitives ? */ flags = enter_critical_section(); sq_remfirst(&priv->rxpending); leave_critical_section(flags); ret = usb_adb_submit_rdreq(priv, rdcontainer); if (ret != OK) { /* TODO handle error */ PANIC(); } } nxsem_post(&priv->exclsem); return retlen; } /**************************************************************************** * Name: adb_char_write * * Description: * Write adb device. * ****************************************************************************/ static ssize_t adb_char_write(FAR struct file *filep, FAR const char *buffer, size_t len) { int ret; int wlen; FAR struct usbdev_req_s *req; FAR struct usbadb_wrreq_s *wrcontainer; FAR struct inode *inode = filep->f_inode; FAR struct usbdev_adb_s *priv = inode->i_private; irqstate_t flags; if (priv->config == USBADB_CONFIGIDNONE) { /* USB device not connected */ return -EPIPE; } ret = nxsem_wait(&priv->exclsem); if (ret < 0) { return ret; } /* Check for available write request */ if (sq_empty(&priv->txfree)) { if (filep->f_oflags & O_NONBLOCK) { ret = -EAGAIN; goto errout; } adb_char_waiter_sem_t sem; nxsem_init(&sem.sem, 0, 0); nxsem_set_protocol(&sem.sem, SEM_PRIO_NONE); do { /* TX queue seems empty. Check again with interrupts disabled */ ret = adb_char_blocking_io( priv, &sem, &priv->wrsems, &priv->txfree); if (ret < 0) { nxsem_destroy(&sem.sem); return ret; } } while (sq_empty(&priv->txfree)); nxsem_destroy(&sem.sem); } /* Device ready for write */ wlen = 0; while (len > 0 && !sq_empty(&priv->txfree)) { int cur_len; /* Get available TX request slot */ flags = enter_critical_section(); wrcontainer = container_of( sq_remfirst(&priv->txfree), struct usbadb_wrreq_s, node); leave_critical_section(flags); req = wrcontainer->req; /* Fill the request with data */ if (len > priv->epbulkin->maxpacket) { cur_len = priv->epbulkin->maxpacket; } else { cur_len = len; } memcpy(req->buf, &buffer[wlen], cur_len); /* Then submit the request to the endpoint */ req->len = cur_len; req->flags = 0; req->priv = wrcontainer; ret = EP_SUBMIT(priv->epbulkin, req); if (ret != OK) { /* TODO add tx request back in txfree queue */ usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_SUBMITFAIL), (uint16_t)-ret); PANIC(); break; } wlen += cur_len; len -= cur_len; } assert(wlen > 0); ret = wlen; errout: nxsem_post(&priv->exclsem); return ret; } static int adb_char_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { return -EINVAL; } static int adb_char_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) { FAR struct inode *inode = filep->f_inode; FAR struct usbdev_adb_s *priv = inode->i_private; int ret; int i; pollevent_t eventset; irqstate_t flags; ret = nxsem_wait(&priv->exclsem); if (ret < 0) { return ret; } ret = OK; if (!setup) { /* This is a request to tear down the poll. */ FAR struct pollfd **slot = (FAR struct pollfd **)fds->priv; /* Remove all memory of the poll setup */ *slot = NULL; fds->priv = NULL; goto errout; } /* FIXME only parts of this function required interrupt disabled */ flags = enter_critical_section(); /* This is a request to set up the poll. Find an available * slot for the poll structure reference */ for (i = 0; i < CONFIG_USBADB_NPOLLWAITERS; i++) { /* Find an available slot */ if (!priv->fds[i]) { /* Bind the poll structure and this slot */ priv->fds[i] = fds; fds->priv = &priv->fds[i]; break; } } if (i >= CONFIG_USBADB_NPOLLWAITERS) { fds->priv = NULL; ret = -EBUSY; goto exit_leave_critical; } eventset = 0; /* Notify the POLLOUT event if at least one request is available */ if (!sq_empty(&priv->txfree)) { eventset |= POLLOUT; } /* Notify the POLLIN event if at least one read request is pending */ if (!sq_empty(&priv->rxpending)) { eventset |= POLLIN; } if (eventset) { adb_char_pollnotify(priv, eventset); } exit_leave_critical: leave_critical_section(flags); errout: nxsem_post(&priv->exclsem); return ret; } static void adb_char_on_connect(FAR struct usbdev_adb_s *priv, int connect) { irqstate_t flags; adb_char_waiter_sem_t *cur_sem; flags = enter_critical_section(); if (connect) { /* Notify poll/select with POLLIN */ adb_char_pollnotify(priv, POLLIN); } else { /* Notify all of the char device waiting readers */ cur_sem = priv->rdsems; while (cur_sem != NULL) { nxsem_post(&cur_sem->sem); cur_sem = cur_sem->next; } priv->rdsems = NULL; /* Notify all of the char device waiting writers */ cur_sem = priv->wrsems; while (cur_sem != NULL) { nxsem_post(&cur_sem->sem); cur_sem = cur_sem->next; } priv->wrsems = NULL; /* Notify all poll/select waiters that a hangup occurred */ adb_char_pollnotify(priv, (POLLERR | POLLHUP)); } leave_critical_section(flags); } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: usbdev_adb_initialize * * Description: * Initialize the Android Debug Bridge USB device driver. * * Returned Value: * 0 on success, -errno on failure * ****************************************************************************/ #ifndef CONFIG_USBADB_COMPOSITE int usbdev_adb_initialize(void) { int ret; FAR struct usbdevclass_driver_s *classdev; FAR struct adb_driver_s *drvr; ret = usbclass_classobject(0, NULL, &classdev); if (ret) { nerr("usbclass_classobject failed: %d\n", ret); return ret; } drvr = (FAR struct adb_driver_s *)classdev; ret = usbdev_register(&drvr->drvr); if (ret) { nerr("usbdev_register failed: %d\n", ret); usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_DEVREGISTER), (uint16_t)-ret); usbclass_uninitialize(classdev); return ret; } return OK; } #endif /**************************************************************************** * Name: usbdev_adb_get_composite_devdesc * * Description: * Helper function to fill in some constants into the composite * configuration struct. * * Input Parameters: * dev - Pointer to the configuration struct we should fill * * Returned Value: * None * ****************************************************************************/ #if defined(CONFIG_USBDEV_COMPOSITE) && defined(CONFIG_USBADB_COMPOSITE) void usbdev_adb_get_composite_devdesc(struct composite_devdesc_s *dev) { memset(dev, 0, sizeof(struct composite_devdesc_s)); dev->mkconfdesc = usbclass_mkcfgdesc; dev->mkstrdesc = usbclass_mkstrdesc; dev->classobject = usbclass_classobject; dev->uninitialize = usbclass_uninitialize; dev->nconfigs = USBADB_NCONFIGS; dev->configid = 1; dev->cfgdescsize = sizeof(g_adb_cfgdesc)+2*USB_SIZEOF_EPDESC; dev->devinfo.ninterfaces = 1; dev->devinfo.nstrings = USBADB_NSTRIDS; dev->devinfo.nendpoints = USBADB_NUM_EPS; } #endif