12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112 |
- /****************************************************************************
- * drivers/usbhost/usbhost_skeleton.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 <nuttx/config.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <assert.h>
- #include <errno.h>
- #include <debug.h>
- #include <nuttx/irq.h>
- #include <nuttx/kmalloc.h>
- #include <nuttx/fs/fs.h>
- #include <nuttx/arch.h>
- #include <nuttx/semaphore.h>
- #include <nuttx/wqueue.h>
- #include <nuttx/usb/usb.h>
- #include <nuttx/usb/usbhost.h>
- /****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
- /* Configuration ************************************************************/
- #ifndef CONFIG_SCHED_WORKQUEUE
- # warning "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
- #endif
- /* Driver support ***********************************************************/
- /* This format is used to construct the /dev/skel[n] device driver path. It
- * defined here so that it will be used consistently in all places.
- */
- #define DEV_FORMAT "/dev/skel%c"
- #define DEV_NAMELEN 12
- /* Used in usbhost_cfgdesc() */
- #define USBHOST_IFFOUND 0x01
- #define USBHOST_BINFOUND 0x02
- #define USBHOST_BOUTFOUND 0x04
- #define USBHOST_ALLFOUND 0x07
- #define USBHOST_MAX_CREFS 0x7fff
- /****************************************************************************
- * Private Types
- ****************************************************************************/
- /* This structure contains the internal, private state of the USB host class
- * driver.
- */
- struct usbhost_state_s
- {
- /* This is the externally visible portion of the state */
- struct usbhost_class_s usbclass;
- /* The remainder of the fields are provide to the class driver */
- char devchar; /* Character identifying the /dev/skel[n] device */
- volatile bool disconnected; /* TRUE: Device has been disconnected */
- uint8_t ifno; /* Interface number */
- int16_t crefs; /* Reference count on the driver instance */
- sem_t exclsem; /* Used to maintain mutual exclusive access */
- struct work_s work; /* For interacting with the worker thread */
- FAR uint8_t *tbuffer; /* The allocated transfer buffer */
- size_t tbuflen; /* Size of the allocated transfer buffer */
- usbhost_ep_t epin; /* IN endpoint */
- usbhost_ep_t epout; /* OUT endpoint */
- };
- /****************************************************************************
- * Private Function Prototypes
- ****************************************************************************/
- /* Semaphores */
- static int usbhost_takesem(FAR sem_t *sem);
- static void usbhost_forcetake(FAR sem_t *sem);
- #define usbhost_givesem(s) nxsem_post(s);
- /* Memory allocation services */
- static inline FAR struct usbhost_state_s *usbhost_allocclass(void);
- static inline void usbhost_freeclass(FAR struct usbhost_state_s *usbclass);
- /* Device name management */
- static int usbhost_allocdevno(FAR struct usbhost_state_s *priv);
- static void usbhost_freedevno(FAR struct usbhost_state_s *priv);
- static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv,
- FAR char *devname);
- /* Worker thread actions */
- static void usbhost_destroy(FAR void *arg);
- /* Helpers for usbhost_connect() */
- static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv,
- FAR const uint8_t *configdesc,
- int desclen);
- static inline int usbhost_devinit(FAR struct usbhost_state_s *priv);
- /* (Little Endian) Data helpers */
- static inline uint16_t usbhost_getle16(const uint8_t *val);
- static inline void usbhost_putle16(uint8_t *dest, uint16_t val);
- static inline uint32_t usbhost_getle32(const uint8_t *val);
- static void usbhost_putle32(uint8_t *dest, uint32_t val);
- /* Transfer descriptor memory management */
- static inline int usbhost_talloc(FAR struct usbhost_state_s *priv);
- static inline int usbhost_tfree(FAR struct usbhost_state_s *priv);
- /* struct usbhost_registry_s methods */
- static struct usbhost_class_s *
- usbhost_create(FAR struct usbhost_hubport_s *hport,
- FAR const struct usbhost_id_s *id);
- /* struct usbhost_class_s methods */
- static int usbhost_connect(FAR struct usbhost_class_s *usbclass,
- FAR const uint8_t *configdesc, int desclen);
- static int usbhost_disconnected(FAR struct usbhost_class_s *usbclass);
- /* Driver methods --
- * depend upon the type of NuttX driver interface exported
- */
- /****************************************************************************
- * Private Data
- ****************************************************************************/
- /* This structure provides the registry entry ID information that will be
- * used to associate the USB class driver to a connected USB device.
- */
- static const struct usbhost_id_s g_id =
- {
- 0, /* base -- Must be one of the USB_CLASS_* definitions in usb.h */
- 0, /* subclass -- depends on the device */
- 0, /* proto -- depends on the device */
- 0, /* vid */
- 0 /* pid */
- };
- /* This is the USB host storage class's registry entry */
- static struct usbhost_registry_s g_skeleton =
- {
- NULL, /* flink */
- usbhost_create, /* create */
- 1, /* nids */
- &g_id /* id[] */
- };
- /* This is a bitmap that is used to allocate device names /dev/skela-z. */
- static uint32_t g_devinuse;
- /****************************************************************************
- * Private Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: usbhost_takesem
- *
- * Description:
- * This is just a wrapper to handle the annoying behavior of semaphore
- * waits that return due to the receipt of a signal.
- *
- ****************************************************************************/
- static int usbhost_takesem(FAR sem_t *sem)
- {
- return nxsem_wait_uninterruptible(sem);
- }
- /****************************************************************************
- * Name: usbhost_forcetake
- *
- * Description:
- * This is just another wrapper but this one continues even if the thread
- * is canceled. This must be done in certain conditions where were must
- * continue in order to clean-up resources.
- *
- ****************************************************************************/
- static void usbhost_forcetake(FAR sem_t *sem)
- {
- int ret;
- do
- {
- ret = nxsem_wait_uninterruptible(sem);
- /* The only expected error would -ECANCELED meaning that the
- * parent thread has been canceled. We have to continue and
- * terminate the poll in this case.
- */
- DEBUGASSERT(ret == OK || ret == -ECANCELED);
- }
- while (ret < 0);
- }
- /****************************************************************************
- * Name: usbhost_allocclass
- *
- * Description:
- * This is really part of the logic that implements the create() method
- * of struct usbhost_registry_s. This function allocates memory for one
- * new class instance.
- *
- * Input Parameters:
- * None
- *
- * Returned Value:
- * On success, this function will return a non-NULL instance of struct
- * usbhost_class_s. NULL is returned on failure; this function will
- * will fail only if there are insufficient resources to create another
- * USB host class instance.
- *
- ****************************************************************************/
- static inline FAR struct usbhost_state_s *usbhost_allocclass(void)
- {
- FAR struct usbhost_state_s *priv;
- DEBUGASSERT(!up_interrupt_context());
- priv = (FAR struct usbhost_state_s *)
- kmm_malloc(sizeof(struct usbhost_state_s));
- uinfo("Allocated: %p\n", priv);
- return priv;
- }
- /****************************************************************************
- * Name: usbhost_freeclass
- *
- * Description:
- * Free a class instance previously allocated by usbhost_allocclass().
- *
- * Input Parameters:
- * usbclass - A reference to the class instance to be freed.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static inline void usbhost_freeclass(FAR struct usbhost_state_s *usbclass)
- {
- DEBUGASSERT(usbclass != NULL);
- /* Free the class instance (perhaps calling sched_kmm_free() in case we are
- * executing from an interrupt handler.
- */
- uinfo("Freeing: %p\n", usbclass);
- kmm_free(usbclass);
- }
- /****************************************************************************
- * Name: Device name management
- *
- * Description:
- * Some tiny functions to coordinate management of device names.
- *
- ****************************************************************************/
- static int usbhost_allocdevno(FAR struct usbhost_state_s *priv)
- {
- irqstate_t flags;
- int devno;
- flags = enter_critical_section();
- for (devno = 0; devno < 26; devno++)
- {
- uint32_t bitno = 1 << devno;
- if ((g_devinuse & bitno) == 0)
- {
- g_devinuse |= bitno;
- priv->devchar = 'a' + devno;
- leave_critical_section(flags);
- return OK;
- }
- }
- leave_critical_section(flags);
- return -EMFILE;
- }
- static void usbhost_freedevno(FAR struct usbhost_state_s *priv)
- {
- int devno = 'a' - priv->devchar;
- if (devno >= 0 && devno < 26)
- {
- irqstate_t flags = enter_critical_section();
- g_devinuse &= ~(1 << devno);
- leave_critical_section(flags);
- }
- }
- static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv,
- FAR char *devname)
- {
- snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->devchar);
- }
- /****************************************************************************
- * Name: usbhost_destroy
- *
- * Description:
- * The USB device has been disconnected and the reference count on the USB
- * host class instance has gone to 1.. Time to destroy the USB host class
- * instance.
- *
- * Input Parameters:
- * arg - A reference to the class instance to be destroyed.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static void usbhost_destroy(FAR void *arg)
- {
- FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)arg;
- FAR struct usbhost_hubport_s *hport;
- FAR struct usbhost_driver_s *drvr;
- DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL);
- hport = priv->usbclass.hport;
- DEBUGASSERT(hport->drvr);
- drvr = hport->drvr;
- uinfo("crefs: %d\n", priv->crefs);
- /* Unregister the driver */
- /* Release the device name used by this connection */
- usbhost_freedevno(priv);
- /* Free the endpoints */
- /* Free any transfer buffers */
- /* Free the function address assigned to this device */
- usbhost_devaddr_destroy(hport, hport->funcaddr);
- hport->funcaddr = 0;
- /* Destroy the semaphores */
- /* Disconnect the USB host device */
- DRVR_DISCONNECT(drvr, hport);
- /* And free the class instance. Hmmm.. this may execute on the worker
- * thread and the work structure is part of what is getting freed. That
- * should be okay because once the work contained is removed from the
- * queue, it should not longer be accessed by the worker thread.
- */
- usbhost_freeclass(priv);
- }
- /****************************************************************************
- * Name: usbhost_cfgdesc
- *
- * Description:
- * This function implements the connect() method of struct
- * usbhost_class_s. This method is a callback into the class
- * implementation. It is used to provide the device's configuration
- * descriptor to the class so that the class may initialize properly
- *
- * Input Parameters:
- * priv - The USB host class instance.
- * configdesc - A pointer to a uint8_t buffer container the configuration
- * descriptor.
- * desclen - The length in bytes of the configuration descriptor.
- *
- * Returned Value:
- * On success, zero (OK) is returned. On a failure, a negated errno value
- * is returned indicating the nature of the failure
- *
- * Assumptions:
- * This function will *not* be called from an interrupt handler.
- *
- ****************************************************************************/
- static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv,
- FAR const uint8_t *configdesc, int desclen)
- {
- FAR struct usbhost_hubport_s *hport;
- FAR struct usb_cfgdesc_s *cfgdesc;
- FAR struct usb_desc_s *desc;
- FAR struct usbhost_epdesc_s bindesc;
- FAR struct usbhost_epdesc_s boutdesc;
- int remaining;
- uint8_t found = 0;
- int ret;
- DEBUGASSERT(priv != NULL && priv->usbclass.hport &&
- configdesc != NULL && desclen >= sizeof(struct usb_cfgdesc_s));
- hport = priv->usbclass.hport;
- /* Verify that we were passed a configuration descriptor */
- cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc;
- if (cfgdesc->type != USB_DESC_TYPE_CONFIG)
- {
- return -EINVAL;
- }
- /* Get the total length of the configuration descriptor (little endian).
- * It might be a good check to get the number of interfaces here too.
- */
- remaining = (int)usbhost_getle16(cfgdesc->totallen);
- /* Skip to the next entry descriptor */
- configdesc += cfgdesc->len;
- remaining -= cfgdesc->len;
- /* Loop where there are more dscriptors to examine */
- while (remaining >= sizeof(struct usb_desc_s))
- {
- /* What is the next descriptor? */
- desc = (FAR struct usb_desc_s *)configdesc;
- switch (desc->type)
- {
- /* Interface descriptor. We really should get the number of endpoints
- * from this descriptor too.
- */
- case USB_DESC_TYPE_INTERFACE:
- {
- FAR struct usb_ifdesc_s *ifdesc =
- (FAR struct usb_ifdesc_s *)configdesc;
- uinfo("Interface descriptor\n");
- DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC);
- /* Save the interface number and mark ONLY the interface found */
- priv->ifno = ifdesc->ifno;
- found = USBHOST_IFFOUND;
- }
- break;
- /* Endpoint descriptor. Here, we expect two bulk endpoints, an IN
- * and an OUT.
- */
- case USB_DESC_TYPE_ENDPOINT:
- {
- FAR struct usb_epdesc_s *epdesc =
- (FAR struct usb_epdesc_s *)configdesc;
- uinfo("Endpoint descriptor\n");
- DEBUGASSERT(remaining >= USB_SIZEOF_EPDESC);
- /* Check for a bulk endpoint. */
- if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) ==
- USB_EP_ATTR_XFER_BULK)
- {
- /* Yes.. it is a bulk endpoint. IN or OUT? */
- if (USB_ISEPOUT(epdesc->addr))
- {
- /* It is an OUT bulk endpoint. There should be only one
- * bulk OUT endpoint.
- */
- if ((found & USBHOST_BOUTFOUND) != 0)
- {
- /* Oops.. more than one endpoint. We don't know
- * what to do with this.
- */
- return -EINVAL;
- }
- found |= USBHOST_BOUTFOUND;
- /* Save the bulk OUT endpoint information */
- boutdesc.hport = hport;
- boutdesc.addr = epdesc->addr &
- USB_EP_ADDR_NUMBER_MASK;
- boutdesc.in = false;
- boutdesc.xfrtype = USB_EP_ATTR_XFER_BULK;
- boutdesc.interval = epdesc->interval;
- boutdesc.mxpacketsize =
- usbhost_getle16(epdesc->mxpacketsize);
- uinfo("Bulk OUT EP addr:%d mxpacketsize:%d\n",
- boutdesc.addr, boutdesc.mxpacketsize);
- }
- else
- {
- /* It is an IN bulk endpoint. There should be only one
- * bulk IN endpoint.
- */
- if ((found & USBHOST_BINFOUND) != 0)
- {
- /* Oops.. more than one endpoint. We don't know
- * what to do with this.
- */
- return -EINVAL;
- }
- found |= USBHOST_BINFOUND;
- /* Save the bulk IN endpoint information */
- bindesc.hport = hport;
- bindesc.addr = epdesc->addr &
- USB_EP_ADDR_NUMBER_MASK;
- bindesc.in = 1;
- bindesc.xfrtype = USB_EP_ATTR_XFER_BULK;
- bindesc.interval = epdesc->interval;
- bindesc.mxpacketsize =
- usbhost_getle16(epdesc->mxpacketsize);
- uinfo("Bulk IN EP addr:%d mxpacketsize:%d\n",
- bindesc.addr, bindesc.mxpacketsize);
- }
- }
- }
- break;
- /* Other descriptors are just ignored for now */
- default:
- break;
- }
- /* If we found everything we need with this interface, then break out
- * of the loop early.
- */
- if (found == USBHOST_ALLFOUND)
- {
- break;
- }
- /* Increment the address of the next descriptor */
- configdesc += desc->len;
- remaining -= desc->len;
- }
- /* Sanity checking... did we find all of things that we need? */
- if (found != USBHOST_ALLFOUND)
- {
- uerr("ERROR: Found IF:%s BIN:%s BOUT:%s\n",
- (found & USBHOST_IFFOUND) != 0 ? "YES" : "NO",
- (found & USBHOST_BINFOUND) != 0 ? "YES" : "NO",
- (found & USBHOST_BOUTFOUND) != 0 ? "YES" : "NO");
- return -EINVAL;
- }
- /* We are good... Allocate the endpoints */
- ret = DRVR_EPALLOC(hport->drvr, &boutdesc, &priv->epout);
- if (ret < 0)
- {
- uerr("ERROR: Failed to allocate Bulk OUT endpoint\n");
- return ret;
- }
- ret = DRVR_EPALLOC(hport->drvr, &bindesc, &priv->epin);
- if (ret < 0)
- {
- uerr("ERROR: Failed to allocate Bulk IN endpoint\n");
- DRVR_EPFREE(hport->drvr, priv->epout);
- return ret;
- }
- uinfo("Endpoints allocated\n");
- return OK;
- }
- /****************************************************************************
- * Name: usbhost_devinit
- *
- * Description:
- * The USB device has been successfully connected. This completes the
- * initialization operations. It is first called after the
- * configuration descriptor has been received.
- *
- * This function is called from the connect() method. This function always
- * executes on the thread of the caller of connect().
- *
- * Input Parameters:
- * priv - A reference to the class instance.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static inline int usbhost_devinit(FAR struct usbhost_state_s *priv)
- {
- int ret = OK;
- /* Set aside a transfer buffer for exclusive use by the class driver */
- /* Increment the reference count. This will prevent usbhost_destroy() from
- * being called asynchronously if the device is removed.
- */
- priv->crefs++;
- DEBUGASSERT(priv->crefs == 2);
- /* Configure the device */
- /* Register the driver */
- if (ret >= 0)
- {
- char devname[DEV_NAMELEN];
- uinfo("Register block driver\n");
- usbhost_mkdevname(priv, devname);
- #if 0 /* Finish me */
- ret = register_blockdriver(devname, &g_bops, 0, priv);
- #endif
- }
- /* Check if we successfully initialized. We now have to be concerned
- * about asynchronous modification of crefs because the block
- * driver has been registered.
- */
- if (ret >= 0)
- {
- ret = usbhost_takesem(&priv->exclsem);
- if (ret < 0)
- {
- return ert;
- }
- DEBUGASSERT(priv->crefs >= 2);
- /* Handle a corner case where (1) open() has been called so the
- * reference count is > 2, but the device has been disconnected.
- * In this case, the class instance needs to persist until close()
- * is called.
- */
- if (priv->crefs <= 2 && priv->disconnected)
- {
- /* We don't have to give the semaphore because it will be
- * destroyed when usb_destroy is called.
- */
- ret = -ENODEV;
- }
- else
- {
- /* Ready for normal operation as a block device driver */
- uinfo("Successfully initialized\n");
- priv->crefs--;
- usbhost_givesem(&priv->exclsem);
- }
- }
- return ret;
- }
- /****************************************************************************
- * Name: usbhost_getle16
- *
- * Description:
- * Get a (possibly unaligned) 16-bit little endian value.
- *
- * Input Parameters:
- * val - A pointer to the first byte of the little endian value.
- *
- * Returned Value:
- * A uint16_t representing the whole 16-bit integer value
- *
- ****************************************************************************/
- static inline uint16_t usbhost_getle16(const uint8_t *val)
- {
- return (uint16_t)val[1] << 8 | (uint16_t)val[0];
- }
- /****************************************************************************
- * Name: usbhost_putle16
- *
- * Description:
- * Put a (possibly unaligned) 16-bit little endian value.
- *
- * Input Parameters:
- * dest - A pointer to the first byte to save the little endian value.
- * val - The 16-bit value to be saved.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static void usbhost_putle16(uint8_t *dest, uint16_t val)
- {
- dest[0] = val & 0xff; /* Little endian means LS byte first in byte stream */
- dest[1] = val >> 8;
- }
- /****************************************************************************
- * Name: usbhost_getle32
- *
- * Description:
- * Get a (possibly unaligned) 32-bit little endian value.
- *
- * Input Parameters:
- * dest - A pointer to the first byte to save the big endian value.
- * val - The 32-bit value to be saved.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static inline uint32_t usbhost_getle32(const uint8_t *val)
- {
- /* Little endian means LS halfword first in byte stream */
- return (uint32_t)usbhost_getle16(&val[2]) << 16 |
- (uint32_t)usbhost_getle16(val);
- }
- /****************************************************************************
- * Name: usbhost_putle32
- *
- * Description:
- * Put a (possibly unaligned) 32-bit little endian value.
- *
- * Input Parameters:
- * dest - A pointer to the first byte to save the little endian value.
- * val - The 32-bit value to be saved.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static void usbhost_putle32(uint8_t *dest, uint32_t val)
- {
- /* Little endian means LS halfword first in byte stream */
- usbhost_putle16(dest, (uint16_t)(val & 0xffff));
- usbhost_putle16(dest + 2, (uint16_t)(val >> 16));
- }
- /****************************************************************************
- * Name: usbhost_talloc
- *
- * Description:
- * Allocate transfer buffer memory.
- *
- * Input Parameters:
- * priv - A reference to the class instance.
- *
- * Returned Value:
- * On success, zero (OK) is returned. On failure, an negated errno value
- * is returned to indicate the nature of the failure.
- *
- ****************************************************************************/
- static inline int usbhost_talloc(FAR struct usbhost_state_s *priv)
- {
- FAR struct usbhost_hubport_s *hport;
- DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL &&
- priv->tbuffer == NULL);
- hport = priv->usbclass.hport;
- return DRVR_ALLOC(hport->drvr, &priv->tbuffer, &priv->tbuflen);
- }
- /****************************************************************************
- * Name: usbhost_tfree
- *
- * Description:
- * Free transfer buffer memory.
- *
- * Input Parameters:
- * priv - A reference to the class instance.
- *
- * Returned Value:
- * On success, zero (OK) is returned. On failure, an negated errno value
- * is returned to indicate the nature of the failure.
- *
- ****************************************************************************/
- static inline int usbhost_tfree(FAR struct usbhost_state_s *priv)
- {
- FAR struct usbhost_hubport_s *hport;
- int result = OK;
- DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL);
- if (priv->tbuffer)
- {
- hport = priv->usbclass.hport;
- result = DRVR_FREE(hport->drvr, priv->tbuffer);
- priv->tbuffer = NULL;
- priv->tbuflen = 0;
- }
- return result;
- }
- /****************************************************************************
- * struct usbhost_registry_s methods
- ****************************************************************************/
- /****************************************************************************
- * Name: usbhost_create
- *
- * Description:
- * This function implements the create() method of struct
- * usbhost_registry_s. The create() method is a callback into the class
- * implementation. It is used to (1) create a new instance of the USB
- * host class state and to (2) bind a USB host driver "session" to the
- * class instance. Use of this create() method will support environments
- * where there may be multiple USB ports and multiple USB devices
- * simultaneously connected.
- *
- * Input Parameters:
- * hport - The hub hat manages the new class instance.
- * id - In the case where the device supports multiple base classes,
- * subclasses, or protocols, this specifies which to configure for.
- *
- * Returned Value:
- * On success, this function will return a non-NULL instance of struct
- * usbhost_class_s that can be used by the USB host driver to communicate
- * with the USB host class. NULL is returned on failure; this function
- * will fail only if the hport input parameter is NULL or if there are
- * insufficient resources to create another USB host class instance.
- *
- ****************************************************************************/
- static FAR struct usbhost_class_s *
- usbhost_create(FAR struct usbhost_hubport_s *hport,
- FAR const struct usbhost_id_s *id)
- {
- FAR struct usbhost_state_s *priv;
- /* Allocate a USB host class instance */
- priv = usbhost_allocclass();
- if (priv)
- {
- /* Initialize the allocated storage class instance */
- memset(priv, 0, sizeof(struct usbhost_state_s));
- /* Assign a device number to this class instance */
- if (usbhost_allocdevno(priv) == OK)
- {
- /* Initialize class method function pointers */
- priv->usbclass.hport = hport;
- priv->usbclass.connect = usbhost_connect;
- priv->usbclass.disconnected = usbhost_disconnected;
- /* The initial reference count is 1...
- * One reference is held by the driver
- */
- priv->crefs = 1;
- /* Initialize semaphores
- * (this works okay in the interrupt context)
- */
- nxsem_init(&priv->exclsem, 0, 1);
- /* Return the instance of the USB class driver */
- return &priv->usbclass;
- }
- }
- /* An error occurred. Free the allocation and return NULL on all failures */
- if (priv)
- {
- usbhost_freeclass(priv);
- }
- return NULL;
- }
- /****************************************************************************
- * struct usbhost_class_s methods
- ****************************************************************************/
- /****************************************************************************
- * Name: usbhost_connect
- *
- * Description:
- * This function implements the connect() method of struct
- * usbhost_class_s. This method is a callback into the class
- * implementation. It is used to provide the device's configuration
- * descriptor to the class so that the class may initialize properly
- *
- * Input Parameters:
- * usbclass - The USB host class entry previously obtained from a call to
- * create().
- * configdesc - A pointer to a uint8_t buffer container the configuration
- * descriptor.
- * desclen - The length in bytes of the configuration descriptor.
- *
- * Returned Value:
- * On success, zero (OK) is returned. On a failure, a negated errno value
- * is returned indicating the nature of the failure
- *
- * NOTE that the class instance remains valid upon return with a failure.
- * It is the responsibility of the higher level enumeration logic to call
- * CLASS_DISCONNECTED to free up the class driver resources.
- *
- * Assumptions:
- * - This function will *not* be called from an interrupt handler.
- * - If this function returns an error, the USB host controller driver
- * must call to DISCONNECTED method to recover from the error
- *
- ****************************************************************************/
- static int usbhost_connect(FAR struct usbhost_class_s *usbclass,
- FAR const uint8_t *configdesc, int desclen)
- {
- FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)usbclass;
- int ret;
- DEBUGASSERT(priv != NULL &&
- configdesc != NULL &&
- desclen >= sizeof(struct usb_cfgdesc_s));
- /* Parse the configuration descriptor to get the endpoints */
- ret = usbhost_cfgdesc(priv, configdesc, desclen);
- if (ret < 0)
- {
- uerr("ERROR: usbhost_cfgdesc() failed: %d\n", ret);
- }
- else
- {
- /* Now configure the device and register the NuttX driver */
- ret = usbhost_devinit(priv);
- if (ret < 0)
- {
- uerr("ERROR: usbhost_devinit() failed: %d\n", ret);
- }
- }
- return ret;
- }
- /****************************************************************************
- * Name: usbhost_disconnected
- *
- * Description:
- * This function implements the disconnected() method of struct
- * usbhost_class_s. This method is a callback into the class
- * implementation. It is used to inform the class that the USB device has
- * been disconnected.
- *
- * Input Parameters:
- * usbclass - The USB host class entry previously obtained from a call to
- * create().
- *
- * Returned Value:
- * On success, zero (OK) is returned. On a failure, a negated errno value
- * is returned indicating the nature of the failure
- *
- * Assumptions:
- * This function may be called from an interrupt handler.
- *
- ****************************************************************************/
- static int usbhost_disconnected(struct usbhost_class_s *usbclass)
- {
- FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)usbclass;
- irqstate_t flags;
- DEBUGASSERT(priv != NULL);
- /* Set an indication to any users of the device that the device is no
- * longer available.
- */
- flags = enter_critical_section();
- priv->disconnected = true;
- /* Now check the number of references on the class instance. If it is one,
- * then we can free the class instance now. Otherwise, we will have to
- * wait until the holders of the references free them by closing the
- * block driver.
- */
- uinfo("crefs: %d\n", priv->crefs);
- if (priv->crefs == 1)
- {
- /* Destroy the class instance. If we are executing from an interrupt
- * handler, then defer the destruction to the worker thread.
- * Otherwise, destroy the instance now.
- */
- if (up_interrupt_context())
- {
- /* Destroy the instance on the worker thread. */
- uinfo("Queuing destruction: worker %p->%p\n",
- priv->work.worker, usbhost_destroy);
- DEBUGASSERT(priv->work.worker == NULL);
- work_queue(HPWORK, &priv->work, usbhost_destroy, priv, 0);
- }
- else
- {
- /* Do the work now */
- usbhost_destroy(priv);
- }
- }
- leave_critical_section(flags);
- return OK;
- }
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: usbhost_skelinit
- *
- * Description:
- * Initialize the USB class driver. This function should be called
- * be platform-specific code in order to initialize and register support
- * for the USB host class device.
- *
- * Input Parameters:
- * None
- *
- * Returned Value:
- * On success this function will return zero (OK); A negated errno value
- * will be returned on failure.
- *
- ****************************************************************************/
- int usbhost_skelinit(void)
- {
- /* Perform any one-time initialization of the class implementation */
- /* Advertise our availability to support (certain) devices */
- return usbhost_registerclass(&g_skeleton);
- }
|