123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472 |
- /****************************************************************************
- * drivers/usbhost/usbhost_storage.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 <unistd.h>
- #include <assert.h>
- #include <errno.h>
- #include <debug.h>
- #include <nuttx/irq.h>
- #include <nuttx/kmalloc.h>
- #include <nuttx/signal.h>
- #include <nuttx/arch.h>
- #include <nuttx/wqueue.h>
- #include <nuttx/scsi.h>
- #include <nuttx/fs/fs.h>
- #include <nuttx/semaphore.h>
- #include <nuttx/usb/usb.h>
- #include <nuttx/usb/usbhost.h>
- #include <nuttx/usb/storage.h>
- #include <nuttx/usb/usbhost_devaddr.h>
- /* Don't compile if prerequisites are not met */
- #if defined(CONFIG_USBHOST) && !defined(CONFIG_USBHOST_BULK_DISABLE) && \
- !defined(CONFIG_DISABLE_MOUNTPOINT)
- /****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
- /* Configuration ************************************************************/
- #ifndef CONFIG_SCHED_WORKQUEUE
- # warning "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
- #endif
- /* If the create() method is called by the USB host device driver from an
- * interrupt handler, then it will be unable to call kmm_malloc() in order to
- * allocate a new class instance. If the create() method is called from the
- * interrupt level, then class instances must be pre-allocated.
- */
- #ifndef CONFIG_USBHOST_NPREALLOC
- # define CONFIG_USBHOST_NPREALLOC 0
- #endif
- #if CONFIG_USBHOST_NPREALLOC > 26
- # error "Currently limited to 26 devices /dev/sda-z"
- #endif
- /* Driver support ***********************************************************/
- /* This format is used to construct the /dev/sd[n] device driver path. It
- * defined here so that it will be used consistently in all places.
- */
- #define DEV_FORMAT "/dev/sd%c"
- #define DEV_NAMELEN 10
- /* Used in usbhost_connect() */
- #define USBHOST_IFFOUND 0x01
- #define USBHOST_BINFOUND 0x02
- #define USBHOST_BOUTFOUND 0x04
- #define USBHOST_ALLFOUND 0x07
- #define USBHOST_RETRY_USEC (50*1000) /* Retry each 50 milliseconds */
- #define USBHOST_MAX_RETRIES 100 /* Give up after 5 seconds */
- #define USBHOST_MAX_CREFS INT16_MAX /* Max cref count before signed overflow */
- /****************************************************************************
- * Private Types
- ****************************************************************************/
- /* This structure contains the internal, private state of the USB host mass
- * storage class.
- */
- 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 mass storage class */
- char sdchar; /* Character identifying the /dev/sd[n] device */
- volatile bool disconnected; /* TRUE: Device has been disconnected */
- uint8_t ifno; /* Interface number */
- int16_t crefs; /* Reference count on the driver instance */
- uint16_t blocksize; /* Block size of USB mass storage device */
- uint32_t nblocks; /* Number of blocks on the USB mass storage device */
- 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 bulkin; /* Bulk IN endpoint */
- usbhost_ep_t bulkout; /* Bulk OUT endpoint */
- };
- /* This is how struct usbhost_state_s looks to the free list logic */
- struct usbhost_freestate_s
- {
- FAR struct usbhost_freestate_s *flink;
- };
- /****************************************************************************
- * 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);
- /* CBW/CSW debug helpers */
- #if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_INFO)
- static void usbhost_dumpcbw(FAR struct usbmsc_cbw_s *cbw);
- static void usbhost_dumpcsw(FAR struct usbmsc_csw_s *csw);
- #else
- # define usbhost_dumpcbw(cbw);
- # define usbhost_dumpcsw(csw);
- #endif
- /* CBW helpers */
- static inline void usbhost_requestsensecbw(FAR struct usbmsc_cbw_s *cbw);
- static inline void usbhost_testunitreadycbw(FAR struct usbmsc_cbw_s *cbw);
- static inline void usbhost_readcapacitycbw(FAR struct usbmsc_cbw_s *cbw);
- static inline void usbhost_inquirycbw (FAR struct usbmsc_cbw_s *cbw);
- static inline void usbhost_readcbw (blkcnt_t startsector, uint16_t blocksize,
- unsigned int nsectors,
- FAR struct usbmsc_cbw_s *cbw);
- static inline void usbhost_writecbw(blkcnt_t startsector, uint16_t blocksize,
- unsigned int nsectors,
- FAR struct usbmsc_cbw_s *cbw);
- /* Command helpers */
- static inline int usbhost_maxlunreq(FAR struct usbhost_state_s *priv);
- static inline int usbhost_testunitready(FAR struct usbhost_state_s *priv);
- static inline int usbhost_requestsense(FAR struct usbhost_state_s *priv);
- static inline int usbhost_readcapacity(FAR struct usbhost_state_s *priv);
- static inline int usbhost_inquiry(FAR struct usbhost_state_s *priv);
- /* 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_initvolume(FAR struct usbhost_state_s *priv);
- /* (Little Endian) Data helpers */
- static inline uint16_t usbhost_getle16(const uint8_t *val);
- static inline uint16_t usbhost_getbe16(const uint8_t *val);
- static inline void usbhost_putle16(uint8_t *dest, uint16_t val);
- static inline void usbhost_putbe16(uint8_t *dest, uint16_t val);
- static inline uint32_t usbhost_getle32(const uint8_t *val);
- static inline uint32_t usbhost_getbe32(const uint8_t *val);
- static void usbhost_putle32(uint8_t *dest, uint32_t val);
- static void usbhost_putbe32(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);
- static FAR struct usbmsc_cbw_s *
- usbhost_cbwalloc(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);
- /* struct block_operations methods */
- static int usbhost_open(FAR struct inode *inode);
- static int usbhost_close(FAR struct inode *inode);
- static ssize_t usbhost_read(FAR struct inode *inode,
- FAR unsigned char *buffer, blkcnt_t startsector,
- unsigned int nsectors);
- static ssize_t usbhost_write(FAR struct inode *inode,
- FAR const unsigned char *buffer,
- blkcnt_t startsector, unsigned int nsectors);
- static int usbhost_geometry(FAR struct inode *inode,
- FAR struct geometry *geometry);
- static int usbhost_ioctl(FAR struct inode *inode, int cmd,
- unsigned long arg);
- /****************************************************************************
- * Private Data
- ****************************************************************************/
- /* This structure provides the registry entry ID information that will be
- * used to associate the USB host mass storage class to a connected USB
- * device.
- */
- static const struct usbhost_id_s g_id =
- {
- USB_CLASS_MASS_STORAGE, /* base */
- USBMSC_SUBCLASS_SCSI, /* subclass */
- USBMSC_PROTO_BULKONLY, /* proto */
- 0, /* vid */
- 0 /* pid */
- };
- /* This is the USB host storage class's registry entry */
- static struct usbhost_registry_s g_storage =
- {
- NULL, /* flink */
- usbhost_create, /* create */
- 1, /* nids */
- &g_id /* id[] */
- };
- /* Block driver operations. This is the interface exposed to NuttX by the
- * class that permits it to behave like a block driver.
- */
- static const struct block_operations g_bops =
- {
- usbhost_open, /* open */
- usbhost_close, /* close */
- usbhost_read, /* read */
- usbhost_write, /* write */
- usbhost_geometry, /* geometry */
- usbhost_ioctl /* ioctl */
- };
- /* This is an array of pre-allocated USB host storage class instances */
- #if CONFIG_USBHOST_NPREALLOC > 0
- static struct usbhost_state_s g_prealloc[CONFIG_USBHOST_NPREALLOC];
- #endif
- /* This is a list of free, pre-allocated USB host storage class instances */
- #if CONFIG_USBHOST_NPREALLOC > 0
- static FAR struct usbhost_freestate_s *g_freelist;
- #endif
- /* This is a bitmap that is used to allocate device names /dev/sda-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.
- *
- ****************************************************************************/
- #if CONFIG_USBHOST_NPREALLOC > 0
- static inline FAR struct usbhost_state_s *usbhost_allocclass(void)
- {
- FAR struct usbhost_freestate_s *entry;
- irqstate_t flags;
- /* We may be executing from an interrupt handler so we need to take one of
- * our pre-allocated class instances from the free list.
- */
- flags = enter_critical_section();
- entry = g_freelist;
- if (entry)
- {
- g_freelist = entry->flink;
- }
- leave_critical_section(flags);
- uinfo("Allocated: %p\n", entry);
- return (FAR struct usbhost_state_s *)entry;
- }
- #else
- static inline FAR struct usbhost_state_s *usbhost_allocclass(void)
- {
- FAR struct usbhost_state_s *priv;
- /* We are not executing from an interrupt handler so we can just call
- * kmm_malloc() to get memory for the class instance.
- */
- DEBUGASSERT(!up_interrupt_context());
- priv = (FAR struct usbhost_state_s *)
- kmm_malloc(sizeof(struct usbhost_state_s));
- uinfo("Allocated: %p\n", priv);
- return priv;
- }
- #endif
- /****************************************************************************
- * 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
- *
- ****************************************************************************/
- #if CONFIG_USBHOST_NPREALLOC > 0
- static inline void usbhost_freeclass(FAR struct usbhost_state_s *usbclass)
- {
- FAR struct usbhost_freestate_s *entry =
- (FAR struct usbhost_freestate_s *)usbclass;
- irqstate_t flags;
- DEBUGASSERT(entry != NULL);
- uinfo("Freeing: %p\n", entry);
- /* Just put the pre-allocated class structure back on the freelist */
- flags = enter_critical_section();
- entry->flink = g_freelist;
- g_freelist = entry;
- leave_critical_section(flags);
- }
- #else
- static inline void usbhost_freeclass(FAR struct usbhost_state_s *usbclass)
- {
- DEBUGASSERT(usbclass != NULL);
- /* Free the class instance (calling kmm_free() in case we are executing
- * from an interrupt handler.
- */
- uinfo("Freeing: %p\n", usbclass);
- kmm_free(usbclass);
- }
- #endif
- /****************************************************************************
- * Name: Device name management
- *
- * Description:
- * Some tiny functions to coordinate management of mass storage 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->sdchar = '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 = priv->sdchar - 'a';
- 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->sdchar);
- }
- /****************************************************************************
- * Name: CBW/CSW debug helpers
- *
- * Description:
- * The following functions are helper functions used to dump CBWs and CSWs.
- *
- * Input Parameters:
- * cbw/csw - A reference to the CBW/CSW to dump.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- #if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_INFO)
- static void usbhost_dumpcbw(FAR struct usbmsc_cbw_s *cbw)
- {
- int i;
- uinfo("CBW:\n");
- uinfo(" signature: %08x\n", usbhost_getle32(cbw->signature));
- uinfo(" tag: %08x\n", usbhost_getle32(cbw->tag));
- uinfo(" datlen: %08x\n", usbhost_getle32(cbw->datlen));
- uinfo(" flags: %02x\n", cbw->flags);
- uinfo(" lun: %02x\n", cbw->lun);
- uinfo(" cdblen: %02x\n", cbw->cdblen);
- uinfo("CDB:\n");
- for (i = 0; i < cbw->cdblen; i += 8)
- {
- uinfo(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
- cbw->cdb[i], cbw->cdb[i + 1], cbw->cdb[i + 2],
- cbw->cdb[i + 3], cbw->cdb[i + 4], cbw->cdb[i + 5],
- cbw->cdb[i + 6], cbw->cdb[i + 7]);
- }
- }
- static void usbhost_dumpcsw(FAR struct usbmsc_csw_s *csw)
- {
- uinfo("CSW:\n");
- uinfo(" signature: %08x\n", usbhost_getle32(csw->signature));
- uinfo(" tag: %08x\n", usbhost_getle32(csw->tag));
- uinfo(" residue: %08x\n", usbhost_getle32(csw->residue));
- uinfo(" status: %02x\n", csw->status);
- }
- #endif
- /****************************************************************************
- * Name: CBW helpers
- *
- * Description:
- * The following functions are helper functions used to format CBWs.
- *
- * Input Parameters:
- * cbw - A reference to allocated and initialized CBW to be built.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static inline void usbhost_requestsensecbw(FAR struct usbmsc_cbw_s *cbw)
- {
- FAR struct scsicmd_requestsense_s *reqsense;
- /* Format the CBW */
- usbhost_putle32(cbw->datlen, SCSIRESP_FIXEDSENSEDATA_SIZEOF);
- cbw->flags = USBMSC_CBWFLAG_IN;
- cbw->cdblen = SCSICMD_REQUESTSENSE_SIZEOF;
- /* Format the CDB */
- reqsense = (FAR struct scsicmd_requestsense_s *)cbw->cdb;
- reqsense->opcode = SCSI_CMD_REQUESTSENSE;
- reqsense->alloclen = SCSIRESP_FIXEDSENSEDATA_SIZEOF;
- usbhost_dumpcbw(cbw);
- }
- static inline void usbhost_testunitreadycbw(FAR struct usbmsc_cbw_s *cbw)
- {
- /* Format the CBW */
- cbw->cdblen = SCSICMD_TESTUNITREADY_SIZEOF;
- /* Format the CDB */
- cbw->cdb[0] = SCSI_CMD_TESTUNITREADY;
- usbhost_dumpcbw(cbw);
- }
- static inline void usbhost_readcapacitycbw(FAR struct usbmsc_cbw_s *cbw)
- {
- FAR struct scsicmd_readcapacity10_s *rcap10;
- /* Format the CBW */
- usbhost_putle32(cbw->datlen, SCSIRESP_READCAPACITY10_SIZEOF);
- cbw->flags = USBMSC_CBWFLAG_IN;
- cbw->cdblen = SCSICMD_READCAPACITY10_SIZEOF;
- /* Format the CDB */
- rcap10 = (FAR struct scsicmd_readcapacity10_s *)cbw->cdb;
- rcap10->opcode = SCSI_CMD_READCAPACITY10;
- usbhost_dumpcbw(cbw);
- }
- static inline void usbhost_inquirycbw (FAR struct usbmsc_cbw_s *cbw)
- {
- FAR struct scscicmd_inquiry_s *inq;
- /* Format the CBW */
- usbhost_putle32(cbw->datlen, SCSIRESP_INQUIRY_SIZEOF);
- cbw->flags = USBMSC_CBWFLAG_IN;
- cbw->cdblen = SCSICMD_INQUIRY_SIZEOF;
- /* Format the CDB */
- inq = (FAR struct scscicmd_inquiry_s *)cbw->cdb;
- inq->opcode = SCSI_CMD_INQUIRY;
- usbhost_putbe16(inq->alloclen, SCSIRESP_INQUIRY_SIZEOF);
- usbhost_dumpcbw(cbw);
- }
- static inline void
- usbhost_readcbw (blkcnt_t startsector, uint16_t blocksize,
- unsigned int nsectors, FAR struct usbmsc_cbw_s *cbw)
- {
- FAR struct scsicmd_read10_s *rd10;
- /* Format the CBW */
- usbhost_putle32(cbw->datlen, blocksize * nsectors);
- cbw->flags = USBMSC_CBWFLAG_IN;
- cbw->cdblen = SCSICMD_READ10_SIZEOF;
- /* Format the CDB */
- rd10 = (FAR struct scsicmd_read10_s *)cbw->cdb;
- rd10->opcode = SCSI_CMD_READ10;
- usbhost_putbe32(rd10->lba, startsector);
- usbhost_putbe16(rd10->xfrlen, nsectors);
- usbhost_dumpcbw(cbw);
- }
- static inline void
- usbhost_writecbw(blkcnt_t startsector, uint16_t blocksize,
- unsigned int nsectors, FAR struct usbmsc_cbw_s *cbw)
- {
- FAR struct scsicmd_write10_s *wr10;
- /* Format the CBW */
- usbhost_putle32(cbw->datlen, blocksize * nsectors);
- cbw->cdblen = SCSICMD_WRITE10_SIZEOF;
- /* Format the CDB */
- wr10 = (FAR struct scsicmd_write10_s *)cbw->cdb;
- wr10->opcode = SCSI_CMD_WRITE10;
- usbhost_putbe32(wr10->lba, startsector);
- usbhost_putbe16(wr10->xfrlen, nsectors);
- usbhost_dumpcbw(cbw);
- }
- /****************************************************************************
- * Name: Command helpers
- *
- * Description:
- * The following functions are helper functions used to send commands.
- *
- * Input Parameters:
- * priv - A reference to the class instance.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static inline int usbhost_maxlunreq(FAR struct usbhost_state_s *priv)
- {
- FAR struct usb_ctrlreq_s *req = (FAR struct usb_ctrlreq_s *)priv->tbuffer;
- FAR struct usbhost_hubport_s *hport;
- DEBUGASSERT(priv && priv->tbuffer);
- int ret;
- /* Request maximum logical unit number. NOTE: On an IN transaction, The
- * req and buffer pointers passed to DRVR_CTRLIN may refer to the same
- * allocated memory.
- */
- uinfo("Request maximum logical unit number\n");
- memset(req, 0, sizeof(struct usb_ctrlreq_s));
- req->type = USB_DIR_IN | USB_REQ_TYPE_CLASS |
- USB_REQ_RECIPIENT_INTERFACE;
- req->req = USBMSC_REQ_GETMAXLUN;
- usbhost_putle16(req->len, 1);
- DEBUGASSERT(priv->usbclass.hport);
- hport = priv->usbclass.hport;
- ret = DRVR_CTRLIN(hport->drvr, hport->ep0, req, priv->tbuffer);
- if (ret < 0)
- {
- /* Devices that do not support multiple LUNs may stall this command.
- * On a failure, a single LUN is assumed.
- */
- *(priv->tbuffer) = 0;
- }
- return OK;
- }
- static inline int usbhost_testunitready(FAR struct usbhost_state_s *priv)
- {
- FAR struct usbhost_hubport_s *hport;
- FAR struct usbmsc_cbw_s *cbw;
- ssize_t nbytes;
- DEBUGASSERT(priv->usbclass.hport);
- hport = priv->usbclass.hport;
- /* Initialize a CBW (re-using the allocated transfer buffer) */
- cbw = usbhost_cbwalloc(priv);
- if (!cbw)
- {
- uerr("ERROR: Failed to create CBW\n");
- return -ENOMEM;
- }
- /* Construct and send the CBW */
- usbhost_testunitreadycbw(cbw);
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkout,
- (FAR uint8_t *)cbw, USBMSC_CBW_SIZEOF);
- if (nbytes >= 0)
- {
- /* Receive the CSW */
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkin,
- priv->tbuffer, USBMSC_CSW_SIZEOF);
- if (nbytes >= 0)
- {
- usbhost_dumpcsw((FAR struct usbmsc_csw_s *)priv->tbuffer);
- }
- }
- return nbytes < 0 ? (int)nbytes : OK;
- }
- static inline int usbhost_requestsense(FAR struct usbhost_state_s *priv)
- {
- FAR struct usbhost_hubport_s *hport;
- FAR struct usbmsc_cbw_s *cbw;
- ssize_t nbytes;
- DEBUGASSERT(priv->usbclass.hport);
- hport = priv->usbclass.hport;
- /* Initialize a CBW (re-using the allocated transfer buffer) */
- cbw = usbhost_cbwalloc(priv);
- if (!cbw)
- {
- uerr("ERROR: Failed to create CBW\n");
- return -ENOMEM;
- }
- /* Construct and send the CBW */
- usbhost_requestsensecbw(cbw);
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkout,
- (FAR uint8_t *)cbw, USBMSC_CBW_SIZEOF);
- if (nbytes >= 0)
- {
- /* Receive the sense data response */
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkin,
- priv->tbuffer, SCSIRESP_FIXEDSENSEDATA_SIZEOF);
- if (nbytes >= 0)
- {
- /* Receive the CSW */
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkin,
- priv->tbuffer, USBMSC_CSW_SIZEOF);
- if (nbytes >= 0)
- {
- usbhost_dumpcsw((FAR struct usbmsc_csw_s *)priv->tbuffer);
- }
- }
- }
- return nbytes < 0 ? (int)nbytes : OK;
- }
- static inline int usbhost_readcapacity(FAR struct usbhost_state_s *priv)
- {
- FAR struct usbhost_hubport_s *hport;
- FAR struct usbmsc_cbw_s *cbw;
- FAR struct scsiresp_readcapacity10_s *resp;
- ssize_t nbytes;
- DEBUGASSERT(priv->usbclass.hport);
- hport = priv->usbclass.hport;
- /* Initialize a CBW (re-using the allocated transfer buffer) */
- cbw = usbhost_cbwalloc(priv);
- if (!cbw)
- {
- uerr("ERROR: Failed to create CBW\n");
- return -ENOMEM;
- }
- /* Construct and send the CBW */
- usbhost_readcapacitycbw(cbw);
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkout,
- (FAR uint8_t *)cbw, USBMSC_CBW_SIZEOF);
- if (nbytes >= 0)
- {
- /* Receive the read capacity CBW IN response */
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkin,
- priv->tbuffer, SCSIRESP_READCAPACITY10_SIZEOF);
- if (nbytes >= 0)
- {
- /* Save the capacity information */
- resp = (FAR struct scsiresp_readcapacity10_s *)
- priv->tbuffer;
- priv->nblocks = usbhost_getbe32(resp->lba) + 1;
- priv->blocksize = usbhost_getbe32(resp->blklen);
- /* Receive the CSW */
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkin,
- priv->tbuffer, USBMSC_CSW_SIZEOF);
- if (nbytes >= 0)
- {
- usbhost_dumpcsw((FAR struct usbmsc_csw_s *)priv->tbuffer);
- }
- }
- }
- return nbytes < 0 ? (int)nbytes : OK;
- }
- static inline int usbhost_inquiry(FAR struct usbhost_state_s *priv)
- {
- FAR struct usbhost_hubport_s *hport;
- FAR struct usbmsc_cbw_s *cbw;
- ssize_t nbytes;
- DEBUGASSERT(priv->usbclass.hport);
- hport = priv->usbclass.hport;
- /* Initialize a CBW (re-using the allocated transfer buffer) */
- cbw = usbhost_cbwalloc(priv);
- if (!cbw)
- {
- uerr("ERROR: Failed to create CBW\n");
- return -ENOMEM;
- }
- /* Construct and send the CBW */
- usbhost_inquirycbw(cbw);
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkout,
- (FAR uint8_t *)cbw, USBMSC_CBW_SIZEOF);
- if (nbytes >= 0)
- {
- /* Receive the CBW IN response */
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkin,
- priv->tbuffer, SCSIRESP_INQUIRY_SIZEOF);
- if (nbytes >= 0)
- {
- #if 0
- FAR struct scsiresp_inquiry_s *resp;
- /* TODO: If USB debug is enabled, dump the response data here */
- resp = (FAR struct scsiresp_inquiry_s *)priv->tbuffer;
- #endif
- /* Receive the CSW */
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkin,
- priv->tbuffer, USBMSC_CSW_SIZEOF);
- if (nbytes >= 0)
- {
- usbhost_dumpcsw((FAR struct usbmsc_csw_s *)priv->tbuffer);
- }
- }
- }
- return nbytes < 0 ? (int)nbytes : OK;
- }
- /****************************************************************************
- * Name: usbhost_destroy
- *
- * Description:
- * The USB mass storage 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;
- char devname[DEV_NAMELEN];
- DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL);
- hport = priv->usbclass.hport;
- uinfo("crefs: %d\n", priv->crefs);
- /* Unregister the block driver */
- usbhost_mkdevname(priv, devname);
- unregister_blockdriver(devname);
- /* Release the device name used by this connection */
- usbhost_freedevno(priv);
- /* Free the bulk endpoints */
- if (priv->bulkout)
- {
- DRVR_EPFREE(hport->drvr, priv->bulkout);
- }
- if (priv->bulkin)
- {
- DRVR_EPFREE(hport->drvr, priv->bulkin);
- }
- /* Free any transfer buffers */
- usbhost_tfree(priv);
- /* Destroy the semaphores */
- nxsem_destroy(&priv->exclsem);
- /* Disconnect the USB host device */
- DRVR_DISCONNECT(hport->drvr, hport);
- /* Free the function address assigned to this device */
- usbhost_devaddr_destroy(hport, hport->funcaddr);
- hport->funcaddr = 0;
- /* And free the class instance. */
- 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;
- /* Keep the compiler from complaining about uninitialized variables */
- memset(&bindesc, 0, sizeof(struct usbhost_epdesc_s));
- memset(&boutdesc, 0, sizeof(struct usbhost_epdesc_s));
- /* 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. 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. We only support the bulk-only
- * protocol so I suppose anything else should really be an error.
- */
- 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? Hmmm..
- * I wonder.. can we work read-only or write-only if only one bulk
- * endpoint found?
- */
- 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->bulkout);
- if (ret < 0)
- {
- uerr("ERROR: Failed to allocate Bulk OUT endpoint\n");
- return ret;
- }
- ret = DRVR_EPALLOC(hport->drvr, &bindesc, &priv->bulkin);
- if (ret < 0)
- {
- uerr("ERROR: Failed to allocate Bulk IN endpoint\n");
- DRVR_EPFREE(hport->drvr, priv->bulkout);
- return ret;
- }
- uinfo("Endpoints allocated\n");
- return OK;
- }
- /****************************************************************************
- * Name: usbhost_initvolume
- *
- * Description:
- * The USB mass storage 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_initvolume(FAR struct usbhost_state_s *priv)
- {
- FAR struct usbmsc_csw_s *csw;
- unsigned int retries;
- int ret = OK;
- DEBUGASSERT(priv != NULL);
- /* Set aside a transfer buffer for exclusive
- * use by the mass storage driver
- */
- ret = usbhost_talloc(priv);
- if (ret < 0)
- {
- uerr("ERROR: Failed to allocate transfer buffer\n");
- return ret;
- }
- /* Increment the reference count. This will prevent usbhost_destroy() from
- * being called asynchronously if the device is removed.
- */
- priv->crefs++;
- DEBUGASSERT(priv->crefs == 2);
- /* Request the maximum logical unit number */
- uinfo("Get max LUN\n");
- ret = usbhost_maxlunreq(priv);
- for (retries = 0; retries < USBHOST_MAX_RETRIES; retries++)
- {
- uinfo("Test unit ready, retries=%d\n", retries);
- /* Wait just a bit */
- nxsig_usleep(USBHOST_RETRY_USEC);
- /* Send TESTUNITREADY to see if the unit is ready. The most likely
- * error error that can occur here is a a stall which simply means
- * that the the device is not yet able to respond.
- */
- ret = usbhost_testunitready(priv);
- if (ret >= 0)
- {
- /* Is the unit is ready */
- csw = (FAR struct usbmsc_csw_s *)priv->tbuffer;
- if (csw->status == 0)
- {
- /* Yes... break out of the loop */
- break;
- }
- /* No.. Request mode sense information. The REQUEST SENSE command
- * is sent only "to clear interlocked unit attention conditions."
- * The returned status is ignored here.
- */
- uinfo("Request sense\n");
- ret = usbhost_requestsense(priv);
- }
- /* It is acceptable for a mass storage device to respond to the
- * Test Unit Ready and Request Sense commands with a stall if it is
- * unable to respond. But other failures mean that something is
- * wrong and a device reset is in order. The transfer functions will
- * return -EPERM if the transfer failed due to a stall.
- */
- if (ret < 0 && ret != -EPERM)
- {
- uerr("ERROR: DRVR_TRANSFER returned: %d\n", ret);
- break;
- }
- }
- /* Did the unit become ready? Did an error occur? Or did we time out? */
- if (retries >= USBHOST_MAX_RETRIES)
- {
- uerr("ERROR: Timeout!\n");
- ret = -ETIMEDOUT;
- }
- if (ret >= 0)
- {
- /* Get the capacity of the volume */
- uinfo("Read capacity\n");
- ret = usbhost_readcapacity(priv);
- if (ret >= 0)
- {
- /* Check the CSW for errors */
- csw = (FAR struct usbmsc_csw_s *)priv->tbuffer;
- if (csw->status != 0)
- {
- uerr("ERROR: CSW status error: %d\n", csw->status);
- ret = -ENODEV;
- }
- }
- }
- /* Get information about the volume */
- if (ret >= 0)
- {
- /* Inquiry */
- uinfo("Inquiry\n");
- ret = usbhost_inquiry(priv);
- if (ret >= 0)
- {
- /* Check the CSW for errors */
- csw = (FAR struct usbmsc_csw_s *)priv->tbuffer;
- if (csw->status != 0)
- {
- uerr("ERROR: CSW status error: %d\n", csw->status);
- ret = -ENODEV;
- }
- }
- }
- /* Register the block driver */
- if (ret >= 0)
- {
- char devname[DEV_NAMELEN];
- uinfo("Register block driver\n");
- usbhost_mkdevname(priv, devname);
- ret = register_blockdriver(devname, &g_bops, 0, priv);
- }
- /* Decrement the reference count. We incremented the reference count
- * above so that usbhost_destroy() could not be called. We now have to
- * be concerned about asynchronous modification of crefs because the block
- * driver has been registered.
- */
- usbhost_forcetake(&priv->exclsem);
- DEBUGASSERT(priv->crefs >= 2);
- /* Decrement the reference count */
- priv->crefs--;
- /* Check if we successfully initialized. If so, handle a corner case
- * where (1) open() has been called so the reference count was > 2, but
- * the device has been disconnected. In this case, the class instance
- * needs to persist until close()
- * is called.
- */
- if (ret >= 0 && priv->crefs <= 1 && priv->disconnected)
- {
- /* The will cause the enumeration logic to disconnect the class
- * driver.
- */
- ret = -ENODEV;
- }
- # ifdef CONFIG_USBHOST_MSC_NOTIFIER
- else
- {
- /* Signal the connect */
- usbhost_msc_notifier_signal(WORK_USB_MSC_CONNECT, priv->sdchar);
- }
- # endif
- /* Release the semaphore... there is a race condition here.
- * Decrementing the reference count and releasing the semaphore
- * allows usbhost_destroy() to execute (on the worker thread);
- * the class driver instance could get destroyed before we are
- * ready to handle it!
- */
- 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_getbe16
- *
- * Description:
- * Get a (possibly unaligned) 16-bit big endian value.
- *
- * Input Parameters:
- * val - A pointer to the first byte of the big endian value.
- *
- * Returned Value:
- * A uint16_t representing the whole 16-bit integer value
- *
- ****************************************************************************/
- static inline uint16_t usbhost_getbe16(const uint8_t *val)
- {
- return (uint16_t)val[0] << 8 | (uint16_t)val[1];
- }
- /****************************************************************************
- * 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_putbe16
- *
- * Description:
- * Put a (possibly unaligned) 16-bit big endian value.
- *
- * Input Parameters:
- * dest - A pointer to the first byte to save the big endian value.
- * val - The 16-bit value to be saved.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static void usbhost_putbe16(uint8_t *dest, uint16_t val)
- {
- dest[0] = val >> 8; /* Big endian means MS byte first in byte stream */
- dest[1] = val & 0xff;
- }
- /****************************************************************************
- * 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_getbe32
- *
- * Description:
- * Get a (possibly unaligned) 32-bit big 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_getbe32(const uint8_t *val)
- {
- /* Big endian means MS halfword first in byte stream */
- return (uint32_t)usbhost_getbe16(val) << 16 |
- (uint32_t)usbhost_getbe16(&val[2]);
- }
- /****************************************************************************
- * 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_putbe32
- *
- * Description:
- * Put a (possibly unaligned) 32-bit big 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 void usbhost_putbe32(uint8_t *dest, uint32_t val)
- {
- /* Big endian means MS halfword first in byte stream */
- usbhost_putbe16(dest, (uint16_t)(val >> 16));
- usbhost_putbe16(dest + 2, (uint16_t)(val & 0xffff));
- }
- /****************************************************************************
- * 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;
- }
- /****************************************************************************
- * Name: usbhost_cbwalloc
- *
- * Description:
- * Initialize a CBW (re-using the allocated transfer buffer). Upon
- * successful return, the CBW is cleared and has the CBW signature in
- * place.
- *
- * Input Parameters:
- * priv - A reference to the class instance.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static FAR struct usbmsc_cbw_s *
- usbhost_cbwalloc(FAR struct usbhost_state_s *priv)
- {
- FAR struct usbmsc_cbw_s *cbw = NULL;
- DEBUGASSERT(priv->tbuffer && priv->tbuflen >= sizeof(struct usbmsc_cbw_s));
- /* Initialize the CBW structure */
- cbw = (FAR struct usbmsc_cbw_s *)priv->tbuffer;
- memset(cbw, 0, sizeof(struct usbmsc_cbw_s));
- usbhost_putle32(cbw->signature, USBMSC_CBW_SIGNATURE);
- return cbw;
- }
- /****************************************************************************
- * 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 port that 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 mass storage 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);
- /* NOTE: We do not yet know the geometry of the USB mass storage
- * device.
- */
- /* Return the instance of the USB mass storage class */
- return &priv->usbclass;
- }
- }
- /* An error occurred. Free the allocation and return NULL on all failures */
- if (priv)
- {
- usbhost_freeclass(priv);
- }
- return NULL;
- }
- /****************************************************************************
- * 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 bulk I/O endpoints */
- ret = usbhost_cfgdesc(priv, configdesc, desclen);
- if (ret < 0)
- {
- uerr("ERROR: usbhost_cfgdesc() failed: %d\n", ret);
- }
- else
- {
- /* Now configure the LUNs and register the block driver(s) */
- ret = usbhost_initvolume(priv);
- if (ret < 0)
- {
- uerr("ERROR: usbhost_initvolume() 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);
- # ifdef CONFIG_USBHOST_MSC_NOTIFIER
- /* Signal the disconnect */
- usbhost_msc_notifier_signal(WORK_USB_MSC_DISCONNECT, priv->sdchar);
- # endif
- /* Set an indication to any users of the mass storage 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;
- }
- /****************************************************************************
- * Name: usbhost_open
- *
- * Description: Open the block device
- *
- ****************************************************************************/
- static int usbhost_open(FAR struct inode *inode)
- {
- FAR struct usbhost_state_s *priv;
- irqstate_t flags;
- int ret;
- uinfo("Entry\n");
- DEBUGASSERT(inode && inode->i_private);
- priv = (FAR struct usbhost_state_s *)inode->i_private;
- /* Make sure that we have exclusive access to the private data structure */
- DEBUGASSERT(priv->crefs > 0 && priv->crefs < USBHOST_MAX_CREFS);
- ret = usbhost_takesem(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* Check if the mass storage device is still connected. We need to
- * disable interrupts momentarily to assure that there are no asynchronous
- * disconnect events.
- */
- flags = enter_critical_section();
- if (priv->disconnected)
- {
- /* No... the block driver is no longer bound to the class. That means
- * that the USB storage device is no longer connected. Refuse any
- * further attempts to open the driver.
- */
- ret = -ENODEV;
- }
- else
- {
- /* Otherwise, just increment the reference count on the driver */
- priv->crefs++;
- ret = OK;
- }
- leave_critical_section(flags);
- usbhost_givesem(&priv->exclsem);
- return ret;
- }
- /****************************************************************************
- * Name: usbhost_close
- *
- * Description: close the block device
- *
- ****************************************************************************/
- static int usbhost_close(FAR struct inode *inode)
- {
- FAR struct usbhost_state_s *priv;
- irqstate_t flags;
- uinfo("Entry\n");
- DEBUGASSERT(inode && inode->i_private);
- priv = (FAR struct usbhost_state_s *)inode->i_private;
- /* Decrement the reference count on the block driver */
- DEBUGASSERT(priv->crefs > 1);
- usbhost_forcetake(&priv->exclsem);
- priv->crefs--;
- /* Release the semaphore. The following operations when crefs == 1 are
- * safe because we know that there is no outstanding open references to
- * the block driver.
- */
- usbhost_givesem(&priv->exclsem);
- /* We need to disable interrupts momentarily to assure that there are
- * no asynchronous disconnect events.
- */
- flags = enter_critical_section();
- /* Check if the USB mass storage device is still connected. If the
- * storage device is not connected and the reference count just
- * decremented to one, then unregister the block driver and free
- * the class instance.
- */
- if (priv->crefs <= 1 && priv->disconnected)
- {
- /* Destroy the class instance */
- DEBUGASSERT(priv->crefs == 1);
- usbhost_destroy(priv);
- }
- leave_critical_section(flags);
- return OK;
- }
- /****************************************************************************
- * Name: usbhost_read
- *
- * Description:
- * Read the specified number of sectors from the read-ahead buffer or from
- * the physical device.
- *
- ****************************************************************************/
- static ssize_t usbhost_read(FAR struct inode *inode, unsigned char *buffer,
- blkcnt_t startsector, unsigned int nsectors)
- {
- FAR struct usbhost_state_s *priv;
- FAR struct usbhost_hubport_s *hport;
- ssize_t nbytes = 0;
- int ret;
- DEBUGASSERT(inode && inode->i_private);
- priv = (FAR struct usbhost_state_s *)inode->i_private;
- DEBUGASSERT(priv->usbclass.hport);
- hport = priv->usbclass.hport;
- uinfo("startsector: %" PRIu32 " nsectors: %u sectorsize: %" PRIu16 "\n",
- startsector, nsectors, priv->blocksize);
- /* Check if the mass storage device is still connected */
- if (priv->disconnected)
- {
- /* No... the block driver is no longer bound to the class. That means
- * that the USB storage device is no longer connected. Refuse any
- * attempt to read from the device.
- */
- nbytes = -ENODEV;
- }
- else if (nsectors > 0)
- {
- FAR struct usbmsc_cbw_s *cbw;
- ret = usbhost_takesem(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* Assume allocation failure */
- nbytes = -ENOMEM;
- /* Initialize a CBW (re-using the allocated transfer buffer) */
- cbw = usbhost_cbwalloc(priv);
- if (cbw)
- {
- /* Loop in the event that EAGAIN is returned (mean that the
- * transaction was NAKed and we should try again.
- */
- do
- {
- /* Assume some device failure */
- nbytes = -ENODEV;
- /* Construct and send the CBW */
- usbhost_readcbw(startsector, priv->blocksize, nsectors, cbw);
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkout,
- (FAR uint8_t *)cbw, USBMSC_CBW_SIZEOF);
- if (nbytes >= 0)
- {
- /* Receive the user data */
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkin,
- buffer, priv->blocksize * nsectors);
- if (nbytes >= 0)
- {
- /* Receive the CSW */
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkin,
- priv->tbuffer,
- USBMSC_CSW_SIZEOF);
- if (nbytes >= 0)
- {
- FAR struct usbmsc_csw_s *csw;
- /* Check the CSW status */
- csw = (FAR struct usbmsc_csw_s *)priv->tbuffer;
- if (csw->status != 0)
- {
- uerr("ERROR: CSW status error: %d\n",
- csw->status);
- nbytes = -ENODEV;
- }
- }
- }
- }
- }
- while (nbytes == -EAGAIN);
- }
- usbhost_givesem(&priv->exclsem);
- }
- /* On success, return the number of blocks read */
- return nbytes < 0 ? (int)nbytes : nsectors;
- }
- /****************************************************************************
- * Name: usbhost_write
- *
- * Description:
- * Write the specified number of sectors to the write buffer or to the
- * physical device.
- *
- ****************************************************************************/
- static ssize_t usbhost_write(FAR struct inode *inode,
- FAR const unsigned char *buffer,
- blkcnt_t startsector, unsigned int nsectors)
- {
- FAR struct usbhost_state_s *priv;
- FAR struct usbhost_hubport_s *hport;
- ssize_t nbytes;
- int ret;
- uinfo("sector: %" PRIu32 " nsectors: %u\n", startsector, nsectors);
- DEBUGASSERT(inode && inode->i_private);
- priv = (FAR struct usbhost_state_s *)inode->i_private;
- DEBUGASSERT(priv->usbclass.hport);
- hport = priv->usbclass.hport;
- /* Check if the mass storage device is still connected */
- if (priv->disconnected)
- {
- /* No... the block driver is no longer bound to the class. That means
- * that the USB storage device is no longer connected. Refuse any
- * attempt to write to the device.
- */
- nbytes = -ENODEV;
- }
- else
- {
- FAR struct usbmsc_cbw_s *cbw;
- ret = usbhost_takesem(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* Assume allocation failure */
- nbytes = -ENOMEM;
- /* Initialize a CBW (re-using the allocated transfer buffer) */
- cbw = usbhost_cbwalloc(priv);
- if (cbw)
- {
- /* Assume some device failure */
- nbytes = -ENODEV;
- /* Construct and send the CBW */
- usbhost_writecbw(startsector, priv->blocksize, nsectors, cbw);
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkout,
- (FAR uint8_t *)cbw, USBMSC_CBW_SIZEOF);
- if (nbytes >= 0)
- {
- /* Send the user data */
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkout,
- (FAR uint8_t *)buffer,
- priv->blocksize * nsectors);
- if (nbytes >= 0)
- {
- /* Receive the CSW */
- nbytes = DRVR_TRANSFER(hport->drvr, priv->bulkin,
- priv->tbuffer, USBMSC_CSW_SIZEOF);
- if (nbytes >= 0)
- {
- FAR struct usbmsc_csw_s *csw;
- /* Check the CSW status */
- csw = (FAR struct usbmsc_csw_s *)priv->tbuffer;
- if (csw->status != 0)
- {
- uerr("ERROR: CSW status error: %d\n", csw->status);
- nbytes = -ENODEV;
- }
- }
- }
- }
- }
- usbhost_givesem(&priv->exclsem);
- }
- /* On success, return the number of blocks written */
- return nbytes < 0 ? (int)nbytes : nsectors;
- }
- /****************************************************************************
- * Name: usbhost_geometry
- *
- * Description: Return device geometry
- *
- ****************************************************************************/
- static int usbhost_geometry(FAR struct inode *inode,
- FAR struct geometry *geometry)
- {
- FAR struct usbhost_state_s *priv;
- int ret = -EINVAL;
- uinfo("Entry\n");
- DEBUGASSERT(inode && inode->i_private);
- /* Check if the mass storage device is still connected */
- priv = (FAR struct usbhost_state_s *)inode->i_private;
- if (priv->disconnected)
- {
- /* No... the block driver is no longer bound to the class. That means
- * that the USB storage device is no longer connected. Refuse to
- * return any geometry info.
- */
- ret = -ENODEV;
- }
- else if (geometry)
- {
- /* Return the geometry of the USB mass storage device */
- ret = usbhost_takesem(&priv->exclsem);
- if (ret >= 0)
- {
- geometry->geo_available = true;
- geometry->geo_mediachanged = false;
- geometry->geo_writeenabled = true;
- geometry->geo_nsectors = priv->nblocks;
- geometry->geo_sectorsize = priv->blocksize;
- usbhost_givesem(&priv->exclsem);
- uinfo("nsectors: %ld sectorsize: %" PRIi16 "n",
- (long)geometry->geo_nsectors, geometry->geo_sectorsize);
- }
- }
- return ret;
- }
- /****************************************************************************
- * Name: usbhost_ioctl
- *
- * Description: Return device geometry
- *
- ****************************************************************************/
- static int usbhost_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
- {
- FAR struct usbhost_state_s *priv;
- int ret;
- uinfo("Entry\n");
- DEBUGASSERT(inode && inode->i_private);
- priv = (FAR struct usbhost_state_s *)inode->i_private;
- /* Check if the mass storage device is still connected */
- if (priv->disconnected)
- {
- /* No... the block driver is no longer bound to the class. That means
- * that the USB storage device is no longer connected. Refuse to
- * process any ioctl commands.
- */
- ret = -ENODEV;
- }
- else
- {
- /* Process the IOCTL by command */
- ret = usbhost_takesem(&priv->exclsem);
- if (ret >= 0)
- {
- switch (cmd)
- {
- /* Add support for ioctl commands here */
- default:
- ret = -ENOTTY;
- break;
- }
- usbhost_givesem(&priv->exclsem);
- }
- }
- return ret;
- }
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: usbhost_msc_initialize
- *
- * Description:
- * Initialize the USB host storage class. This function should be called
- * be platform-specific code in order to initialize and register support
- * for the USB host storage class.
- *
- * Input Parameters:
- * None
- *
- * Returned Value:
- * On success this function will return zero (OK); A negated errno value
- * will be returned on failure.
- *
- ****************************************************************************/
- int usbhost_msc_initialize(void)
- {
- /* If we have been configured to use pre-allocated storage class instances,
- * then place all of the pre-allocated USB host storage class instances
- * into a free list.
- */
- #if CONFIG_USBHOST_NPREALLOC > 0
- FAR struct usbhost_freestate_s *entry;
- int i;
- g_freelist = NULL;
- for (i = 0; i < CONFIG_USBHOST_NPREALLOC; i++)
- {
- entry = (FAR struct usbhost_freestate_s *)&g_prealloc[i];
- entry->flink = g_freelist;
- g_freelist = entry;
- }
- #endif
- /* Advertise our availability to support (certain) mass storage devices */
- return usbhost_registerclass(&g_storage);
- }
- # ifdef CONFIG_USBHOST_MSC_NOTIFIER
- /****************************************************************************
- * Name: usbhost_msc_notifier_setup
- *
- * Description:
- * Set up to perform a callback to the worker function when a mass storage
- * device is attached.
- *
- * Input Parameters:
- * worker - The worker function to execute on the low priority work queue
- * when the event occurs.
- * event - Currently only USBHOST_MSC_DISCONNECT and USBHOST_MSC_CONNECT
- * sdchar - sdchar of the connected or disconnected block device
- * arg - A user-defined argument that will be available to the worker
- * function when it runs.
- *
- * Returned Value:
- * > 0 - The notification is in place. The returned value is a key that
- * may be used later in a call to
- * usbmsc_attach_notifier_teardown().
- * == 0 - Not used.
- * < 0 - An unexpected error occurred and no notification will occur. The
- * returned value is a negated errno value that indicates the
- * nature of the failure.
- *
- ****************************************************************************/
- int usbhost_msc_notifier_setup(worker_t worker, uint8_t event, char sdchar,
- FAR void *arg)
- {
- struct work_notifier_s info;
- DEBUGASSERT(worker != NULL);
- info.evtype = event;
- info.qid = LPWORK;
- info.qualifier = (FAR void *)(uintptr_t)sdchar;
- info.arg = arg;
- info.worker = worker;
- return work_notifier_setup(&info);
- }
- /****************************************************************************
- * Name: usbhost_msc_notifier_teardown
- *
- * Description:
- * Eliminate an USB MSC notification previously setup by
- * usbhost_msc_notifier_setup().
- * This function should only be called if the notification should be
- * aborted prior to the notification. The notification will automatically
- * be torn down after the notification.
- *
- * Input Parameters:
- * key - The key value returned from a previous call to
- * usbhost_msc_notifier_setup().
- *
- * Returned Value:
- * Zero (OK) is returned on success; a negated errno value is returned on
- * any failure.
- *
- ****************************************************************************/
- int usbhost_msc_notifier_teardown(int key)
- {
- /* This is just a simple wrapper around work_notifier_teardown(). */
- return work_notifier_teardown(key);
- }
- /****************************************************************************
- * Name: usbhost_msc_notifier_signal
- *
- * Description:
- * An USB mass storage device has been connected or disconnected.
- * Signal all threads.
- *
- * Input Parameters:
- * event - Currently only USBHOST_MSC_DISCONNECT and USBHOST_MSC_CONNECT
- * sdchar - sdchar of the connected or disconnected block device
- *
- * Returned Value:
- * None.
- *
- ****************************************************************************/
- void usbhost_msc_notifier_signal(uint8_t event, char sdchar)
- {
- work_notifier_signal(event, (FAR void *)(uintptr_t)sdchar);
- }
- # endif /* CONFIG_USBHOST_MSC_NOTIFIER */
- #endif /* CONFIG_USBHOST && !CONFIG_USBHOST_BULK_DISABLE && !CONFIG_DISABLE_MOUNTPOINT */
|