12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813 |
- /****************************************************************************
- * drivers/usbhost/usbhost_cdcacm.c
- *
- * Copyright (C) 2015-2016 Gregory Nutt. All rights reserved.
- * Author: Gregory Nutt <gnutt@nuttx.org>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * 3. Neither the name NuttX nor the names of its contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- ****************************************************************************/
- /****************************************************************************
- * Included Files
- ****************************************************************************/
- #include <nuttx/config.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <semaphore.h>
- #include <assert.h>
- #include <errno.h>
- #include <debug.h>
- #include <nuttx/irq.h>
- #include <nuttx/kmalloc.h>
- #include <nuttx/arch.h>
- #include <nuttx/wqueue.h>
- #include <nuttx/clock.h>
- #include <nuttx/serial/serial.h>
- #include <nuttx/usb/usb.h>
- #include <nuttx/usb/usbhost.h>
- #include <nuttx/usb/cdc.h>
- #include <nuttx/usb/cdcacm.h>
- #include <nuttx/usb/usbhost_devaddr.h>
- #ifdef CONFIG_USBHOST_CDCACM
- /****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
- /* Configuration ************************************************************/
- #ifndef CONFIG_USBHOST
- # warning USB host support not enabled (CONFIG_USBHOST)
- #endif
- #ifdef CONFIG_USBHOST_BULK_DISABLE
- # warning USB bulk endpoint support is disabled (CONFIG_USBHOST_BULK_DISABLE)
- #endif
- #ifdef CONFIG_USBHOST_INT_DISABLE
- # warning USB interrupt endpoint support is disabled (CONFIG_USBHOST_INT_DISABLE)
- #endif
- #if !defined(CONFIG_SCHED_WORKQUEUE)
- # warning Worker thread support is required (CONFIG_SCHED_WORKQUEUE)
- #else
- # ifndef CONFIG_SCHED_HPWORK
- # warning High priority work thread support is required (CONFIG_SCHED_HPWORK)
- # endif
- # ifndef CONFIG_SCHED_LPWORK
- # warning Low priority work thread support is required (CONFIG_SCHED_LPWORK)
- # endif
- # if CONFIG_SCHED_LPNTHREADS < 2
- # warning Multiple low priority work threads recommended for performance (CONFIG_SCHED_LPNTHREADS > 1)
- # endif
- #endif
- #ifndef CONFIG_USBHOST_ASYNCH
- # warning Asynchronous transfer support is required (CONFIG_USBHOST_ASYNCH)
- #endif
- #ifndef CONFIG_SERIAL_REMOVABLE
- # warning Removable serial device support is required (CONFIG_SERIAL_REMOVABLE)
- #endif
- #ifdef CONFIG_USBHOST_CDCACM_NTDELAY
- # define USBHOST_CDCACM_NTDELAY MSEC2TICK(CONFIG_USBHOST_CDCACM_NTDELAY)
- #else
- # define USBHOST_CDCACM_NTDELAY MSEC2TICK(200)
- #endif
- #ifdef CONFIG_USBHOST_CDCACM_RXDELAY
- # define USBHOST_CDCACM_RXDELAY MSEC2TICK(CONFIG_USBHOST_CDCACM_RXDELAY)
- #else
- # define USBHOST_CDCACM_RXDELAY MSEC2TICK(200)
- #endif
- #ifdef CONFIG_USBHOST_CDCACM_TXDELAY
- # define USBHOST_CDCACM_TXDELAY MSEC2TICK(CONFIG_USBHOST_CDCACM_TXDELAY)
- #else
- # define USBHOST_CDCACM_TXDELAY MSEC2TICK(200)
- #endif
- /* Supported protocol */
- #define HAVE_CLASS_REQUESTS 1
- #define HAVE_INTIN_ENDPOINT 1
- #define HAVE_CTRL_INTERFACE 1
- #if defined(CONFIG_USBHOST_CDCACM_REDUCED)
- # undef CONFIG_USBHOST_CDCACM_BULKONLY
- # undef CONFIG_USBHOST_CDCACM_COMPLIANT
- # undef HAVE_INTIN_ENDPOINT
- #elif defined(CONFIG_USBHOST_CDCACM_BULKONLY)
- # undef CONFIG_USBHOST_CDCACM_COMPLIANT
- # undef HAVE_CLASS_REQUESTS
- # undef HAVE_INTIN_ENDPOINT
- # undef HAVE_CTRL_INTERFACE
- #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_CDCACM_NPREALLOC
- # define CONFIG_USBHOST_CDCACM_NPREALLOC 0
- #endif
- #if CONFIG_USBHOST_CDCACM_NPREALLOC > 32
- # error Currently limited to 32 devices /dev/ttyACM[n]
- #endif
- #ifndef CONFIG_USBHOST_CDCACM_RXBUFSIZE
- # define CONFIG_USBHOST_CDCACM_RXBUFSIZE 128
- #endif
- #ifndef CONFIG_USBHOST_CDCACM_TXBUFSIZE
- # define CONFIG_USBHOST_CDCACM_TXBUFSIZE 128
- #endif
- /* Initial line coding */
- #ifndef CONFIG_USBHOST_CDCACM_BAUD
- # define CONFIG_USBHOST_CDCACM_BAUD 115200
- #endif
- #ifndef CONFIG_USBHOST_CDCACM_PARITY
- # define CONFIG_USBHOST_CDCACM_PARITY 0
- #endif
- #ifndef CONFIG_USBHOST_CDCACM_BITS
- # define CONFIG_USBHOST_CDCACM_BITS 8
- #endif
- #ifndef CONFIG_USBHOST_CDCACM_2STOP
- # define CONFIG_USBHOST_CDCACM_2STOP 0
- #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/ttyACM%d"
- #define DEV_NAMELEN 16
- #define MAX_NOTIFICATION 32
- /* Used in usbhost_connect() */
- #define USBHOST_DATAIF_FOUND 0x01 /* Data interface found */
- #define USBHOST_BULKIN_FOUND 0x02 /* Bulk IN interface found */
- #define USBHOST_BULKOUT_FOUND 0x04 /* Bulk OUT interface found */
- #if defined(CONFIG_USBHOST_CDCACM_BULKONLY)
- # define USBHOST_MINFOUND 0x07 /* Minimum things needed */
- # define USBHOST_ALLFOUND 0x07 /* All configuration things */
- #elif defined(CONFIG_USBHOST_CDCACM_REDUCED)
- # define USBHOST_CTRLIF_FOUND 0x08 /* Control interface found */
- # define USBHOST_MINFOUND 0x07 /* Minimum things needed */
- # define USBHOST_HAVE_CTRLIF 0x08 /* Needed for control interface */
- # define USBHOST_ALLFOUND 0x0f /* All configuration things */
- #else
- # define USBHOST_CTRLIF_FOUND 0x08 /* Control interface found */
- # define USBHOST_INTIN_FOUND 0x10 /* Interrupt IN interface found */
- # define USBHOST_MINFOUND 0x07 /* Minimum things needed */
- # define USBHOST_HAVE_CTRLIF 0x18 /* Needed for control interface */
- # define USBHOST_ALLFOUND 0x1f /* All configuration things */
- #endif
- #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
- * CDC/ACM class.
- */
- struct usbhost_cdcacm_s
- {
- /* This is the externally visible portion of the state. The usbclass must
- * the first element of the structure. It is then cast compatible with
- * struct usbhost_cdcacm_s.
- */
- struct usbhost_class_s usbclass;
- /* This is the standard of the lower-half serial interface. It is not
- * the first element of the structure, but includes a pointer back to the
- * the beginning of this structure.
- */
- struct uart_dev_s uartdev;
- /* The remainder of the fields are provide to the CDC/ACM class */
- volatile bool disconnected; /* TRUE: Device has been disconnected */
- bool stop2; /* True: 2 stop bits (for line coding) */
- bool dsr; /* State of transmission carrier */
- bool dcd; /* State of receiver carrier detection */
- bool txena; /* True: TX "interrupts" enabled */
- bool rxena; /* True: RX "interrupts" enabled */
- #ifdef CONFIG_SERIAL_IFLOWCONTROL
- bool iflow; /* True: Input flow control (RTS) enabled */
- bool rts; /* True: Input flow control is in effect */
- #endif
- #ifdef CONFIG_SERIAL_OFLOWCONTROL
- bool oflow; /* True: Output flow control (CTS) enabled */
- #endif
- uint8_t minor; /* Minor number identifying the /dev/ttyACM[n] device */
- uint8_t dataif; /* Data interface number */
- #ifdef HAVE_CTRL_INTERFACE
- uint8_t ctrlif; /* Control interface number */
- #endif
- uint8_t nbits; /* Number of bits (for line encoding) */
- uint8_t parity; /* Parity (for line encoding) */
- uint16_t pktsize; /* Allocated size of transfer buffers */
- uint16_t nrxbytes; /* Number of bytes in the RX packet buffer */
- uint16_t rxndx; /* Index to the next byte in the RX packet buffer */
- int16_t crefs; /* Reference count on the driver instance */
- int16_t nbytes; /* The number of bytes actually transferred */
- sem_t exclsem; /* Used to maintain mutual exclusive access */
- struct work_s ntwork; /* For asynchronous notification work */
- struct work_s rxwork; /* For RX packet work */
- struct work_s txwork; /* For TX packet work */
- FAR uint8_t *ctrlreq; /* Allocated ctrl request structure */
- FAR uint8_t *linecode; /* The allocated buffer for line encoding */
- FAR uint8_t *notification; /* The allocated buffer for async notifications */
- FAR uint8_t *inbuf; /* Allocated RX buffer for the Bulk IN endpoint */
- FAR uint8_t *outbuf; /* Allocated TX buffer for the Bulk OUT endpoint */
- uint32_t baud; /* Current baud for line coding */
- usbhost_ep_t bulkin; /* Bulk IN endpoint */
- usbhost_ep_t bulkout; /* Bulk OUT endpoint */
- #ifdef HAVE_INTIN_ENDPOINT
- usbhost_ep_t intin; /* Interrupt IN endpoint (optional) */
- #endif
- /* This is the serial data buffer */
- char rxbuffer[CONFIG_USBHOST_CDCACM_RXBUFSIZE];
- char txbuffer[CONFIG_USBHOST_CDCACM_TXBUFSIZE];
- };
- /* This is how struct usbhost_cdcacm_s looks to the free list logic */
- struct usbhost_freestate_s
- {
- FAR struct usbhost_freestate_s *flink;
- };
- /****************************************************************************
- * Private Function Prototypes
- ****************************************************************************/
- /* Semaphores */
- static void usbhost_takesem(sem_t *sem);
- #define usbhost_givesem(s) sem_post(s);
- /* Memory allocation services */
- static FAR struct usbhost_cdcacm_s *usbhost_allocclass(void);
- static void usbhost_freeclass(FAR struct usbhost_cdcacm_s *usbclass);
- /* Device name management */
- static int usbhost_devno_alloc(FAR struct usbhost_cdcacm_s *priv);
- static void usbhost_devno_free(FAR struct usbhost_cdcacm_s *priv);
- static inline void usbhost_mkdevname(FAR struct usbhost_cdcacm_s *priv,
- FAR char *devname);
- /* CDC/ACM request helpers */
- #ifdef HAVE_CTRL_INTERFACE
- static int usbhost_linecoding_send(FAR struct usbhost_cdcacm_s *priv);
- #ifdef HAVE_INTIN_ENDPOINT
- static void usbhost_notification_work(FAR void *arg);
- static void usbhost_notification_callback(FAR void *arg, ssize_t nbytes);
- #endif
- #endif
- /* UART buffer data transfer */
- static void usbhost_txdata_work(FAR void *arg);
- static void usbhost_rxdata_work(FAR void *arg);
- /* Worker thread actions */
- static void usbhost_destroy(FAR void *arg);
- /* Helpers for usbhost_connect() */
- static int usbhost_cfgdesc(FAR struct usbhost_cdcacm_s *priv,
- FAR const uint8_t *configdesc, int desclen);
- /* (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);
- #ifdef HAVE_CTRL_INTERFACE
- static void usbhost_putle32(uint8_t *dest, uint32_t val);
- #endif
- /* Transfer descriptor memory management */
- static int usbhost_alloc_buffers(FAR struct usbhost_cdcacm_s *priv);
- static void usbhost_free_buffers(FAR struct usbhost_cdcacm_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);
- /* Serial driver lower-half interfaces */
- static int usbhost_setup(FAR struct uart_dev_s *uartdev);
- static void usbhost_shutdown(FAR struct uart_dev_s *uartdev);
- static int usbhost_attach(FAR struct uart_dev_s *uartdev);
- static void usbhost_detach(FAR struct uart_dev_s *uartdev);
- static int usbhost_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
- static void usbhost_rxint(FAR struct uart_dev_s *uartdev, bool enable);
- static bool usbhost_rxavailable(FAR struct uart_dev_s *uartdev);
- #ifdef CONFIG_SERIAL_IFLOWCONTROL
- static bool usbhost_rxflowcontrol(FAR struct uart_dev_s *uartdev,
- unsigned int nbuffered, bool upper);
- #endif
- static void usbhost_txint(FAR struct uart_dev_s *uartdev, bool enable);
- static bool usbhost_txready(FAR struct uart_dev_s *uartdev);
- static bool usbhost_txempty(FAR struct uart_dev_s *uartdev);
- /****************************************************************************
- * Private Data
- ****************************************************************************/
- /* This structure provides the registry entry ID information that will be
- * used to associate the USB host CDC/ACM class to a connected USB
- * device.
- */
- static const const struct usbhost_id_s g_id[2] =
- {
- {
- USB_CLASS_CDC, /* base */
- CDC_SUBCLASS_NONE, /* subclass */
- CDC_PROTO_NONE, /* proto */
- 0, /* vid */
- 0 /* pid */
- },
- {
- USB_CLASS_CDC, /* base */
- CDC_SUBCLASS_ACM, /* subclass */
- CDC_PROTO_ATM, /* proto */
- 0, /* vid */
- 0 /* pid */
- }
- };
- /* This is the USB host CDC/ACM class's registry entry */
- static struct usbhost_registry_s g_cdcacm =
- {
- NULL, /* flink */
- usbhost_create, /* create */
- 2, /* nids */
- &g_id[0] /* id[] */
- };
- /* Serial driver lower half interface */
- static const struct uart_ops_s g_uart_ops =
- {
- usbhost_setup, /* setup */
- usbhost_shutdown, /* shutdown */
- usbhost_attach, /* attach */
- usbhost_detach, /* detach */
- usbhost_ioctl, /* ioctl */
- NULL , /* receive */
- usbhost_rxint, /* rxinit */
- usbhost_rxavailable, /* rxavailable */
- #ifdef CONFIG_SERIAL_IFLOWCONTROL
- usbhost_rxflowcontrol, /* rxflowcontrol */
- #endif
- NULL, /* send */
- usbhost_txint, /* txinit */
- usbhost_txready, /* txready */
- usbhost_txempty /* txempty */
- };
- /* This is an array of pre-allocated USB host CDC/ACM class instances */
- #if CONFIG_USBHOST_CDCACM_NPREALLOC > 0
- static struct usbhost_cdcacm_s g_prealloc[CONFIG_USBHOST_CDCACM_NPREALLOC];
- #endif
- /* This is a list of free, pre-allocated USB host CDC/ACM class instances */
- #if CONFIG_USBHOST_CDCACM_NPREALLOC > 0
- static FAR struct usbhost_freestate_s *g_freelist;
- #endif
- /* This is a bitmap that is used to allocate device minor numbers /dev/ttyACM[n]. */
- 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 void usbhost_takesem(sem_t *sem)
- {
- /* Take the semaphore (perhaps waiting) */
- while (sem_wait(sem) != 0)
- {
- /* The only case that an error should occur here is if the wait was
- * awakened by a signal.
- */
- ASSERT(errno == EINTR);
- }
- }
- /****************************************************************************
- * 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_CDCACM_NPREALLOC > 0
- static FAR struct usbhost_cdcacm_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_cdcacm_s *)entry;
- }
- #else
- static FAR struct usbhost_cdcacm_s *usbhost_allocclass(void)
- {
- FAR struct usbhost_cdcacm_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_cdcacm_s *)kmm_malloc(sizeof(struct usbhost_cdcacm_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_CDCACM_NPREALLOC > 0
- static void usbhost_freeclass(FAR struct usbhost_cdcacm_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 void usbhost_freeclass(FAR struct usbhost_cdcacm_s *usbclass)
- {
- DEBUGASSERT(usbclass != NULL);
- /* Free the class instance (calling sched_kfree() in case we are executing
- * from an interrupt handler.
- */
- uinfo("Freeing: %p\n", usbclass);
- sched_kfree(usbclass);
- }
- #endif
- /****************************************************************************
- * Name: usbhost_devno_alloc
- *
- * Description:
- * Allocate a unique /dev/ttyACM[n] minor number in the range 0-31.
- *
- ****************************************************************************/
- static int usbhost_devno_alloc(FAR struct usbhost_cdcacm_s *priv)
- {
- irqstate_t flags;
- int devno;
- flags = enter_critical_section();
- for (devno = 0; devno < 32; devno++)
- {
- uint32_t bitno = 1 << devno;
- if ((g_devinuse & bitno) == 0)
- {
- g_devinuse |= bitno;
- priv->minor = devno;
- leave_critical_section(flags);
- return OK;
- }
- }
- leave_critical_section(flags);
- return -EMFILE;
- }
- /****************************************************************************
- * Name: usbhost_devno_free
- *
- * Description:
- * Free a /dev/ttyACM[n] minor number so that it can be used.
- *
- ****************************************************************************/
- static void usbhost_devno_free(FAR struct usbhost_cdcacm_s *priv)
- {
- int devno = priv->minor;
- if (devno >= 0 && devno < 32)
- {
- irqstate_t flags = enter_critical_section();
- g_devinuse &= ~(1 << devno);
- leave_critical_section(flags);
- }
- }
- /****************************************************************************
- * Name: usbhost_mkdevname
- *
- * Description:
- * Format a /dev/ttyACM[n] device name given a minor number.
- *
- ****************************************************************************/
- static inline void usbhost_mkdevname(FAR struct usbhost_cdcacm_s *priv,
- FAR char *devname)
- {
- (void)snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->minor);
- }
- /****************************************************************************
- * Name: usbhost_linecoding_send
- *
- * Description:
- * Format and send the on EP0.
- *
- * Input Parameters:
- * arg - A reference to the class instance to be destroyed.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- #ifdef HAVE_CTRL_INTERFACE
- static int usbhost_linecoding_send(FAR struct usbhost_cdcacm_s *priv)
- {
- FAR struct usbhost_hubport_s *hport;
- FAR struct cdc_linecoding_s *linecode;
- FAR struct usb_ctrlreq_s *ctrlreq;
- int ret;
- hport = priv->usbclass.hport;
- DEBUGASSERT(hport);
- /* Initialize the line coding structure */
- linecode = (FAR struct cdc_linecoding_s *)priv->linecode;
- usbhost_putle32(linecode->baud, priv->baud);
- linecode->stop = (priv->stop2) ? 2 : 0;
- linecode->parity = priv->parity;
- linecode->nbits = priv->nbits;
- /* Initialize the control request */
- ctrlreq = (FAR struct usb_ctrlreq_s *)priv->ctrlreq;
- ctrlreq->type = USB_DIR_OUT | USB_REQ_TYPE_CLASS | USB_REQ_RECIPIENT_INTERFACE;
- ctrlreq->req = ACM_SET_LINE_CODING;
- usbhost_putle16(ctrlreq->value, 0);
- usbhost_putle16(ctrlreq->index, priv->ctrlif);
- usbhost_putle16(ctrlreq->len, SIZEOF_CDC_LINECODING);
- /* And send the request */
- ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, priv->linecode);
- if (ret < 0)
- {
- uerr("ERROR: DRVR_CTRLOUT failed: %d\n", ret);
- }
- return ret;
- }
- #endif
- /****************************************************************************
- * Name: usbhost_notification_work
- *
- * Description:
- * Handle receipt of an asynchronous notification from the CDC/ACM device
- *
- * Input Parameters:
- * arg - The argument provided with the asynchronous I/O was setup
- *
- * Returned Value:
- * None
- *
- * Assumptions:
- * Probably called from an interrupt handler.
- *
- ****************************************************************************/
- #ifdef HAVE_INTIN_ENDPOINT
- static void usbhost_notification_work(FAR void *arg)
- {
- FAR struct usbhost_cdcacm_s *priv;
- FAR struct usbhost_hubport_s *hport;
- FAR struct cdc_notification_s *inmsg;
- uint16_t value;
- uint16_t index;
- uint16_t len;
- int ret;
- priv = (FAR struct usbhost_cdcacm_s *)arg;
- DEBUGASSERT(priv);
- hport = priv->usbclass.hport;
- DEBUGASSERT(hport);
- /* Are we still connected? */
- if (!priv->disconnected && priv->intin)
- {
- /* Yes.. Was an interrupt IN message received correctly? */
- if (priv->nbytes >= 0)
- {
- /* Yes.. decode the notification */
- inmsg = (FAR struct cdc_notification_s *)priv->notification;
- value = usbhost_getle16(inmsg->value);
- index = usbhost_getle16(inmsg->index);
- len = usbhost_getle16(inmsg->len);
- uinfo("type: %02x notification: %02x value: %04x index: %04x len: %04x\n",
- inmsg->type, inmsg->notification, value, index, len);
- /* We care only about the SerialState notification */
- if ((inmsg->type == (USB_REQ_DIR_IN | USB_REQ_TYPE_CLASS |
- USB_REQ_RECIPIENT_INTERFACE)) &&
- (inmsg->notification == ACM_SERIAL_STATE) &&
- (value == 0) &&
- (index == priv->ctrlif) &&
- (len == 2))
- {
- uint16_t state = usbhost_getle16(inmsg->data);
- /* And we care only about the state of the DCD and DSR in the
- * the notification.
- */
- priv->dcd = ((state & CDC_UART_RXCARRIER) != 0);
- priv->dsr = ((state & CDC_UART_TXCARRIER) != 0);
- uinfo("ACM_SERIAL_STATE: DCD=%d DSR=%d\n",
- priv->dcd, priv->dsr);
- }
- }
- /* Setup to receive the next line status change event */
- ret = DRVR_ASYNCH(hport->drvr, priv->intin,
- (FAR uint8_t *)priv->notification,
- MAX_NOTIFICATION, usbhost_notification_callback,
- priv);
- if (ret < 0)
- {
- uerr("ERROR: DRVR_ASYNCH failed: %d\n", ret);
- }
- }
- }
- #endif
- /****************************************************************************
- * Name: usbhost_notification_callback
- *
- * Description:
- * Handle receipt of line status from the CDC/ACM device
- *
- * Input Parameters:
- * arg - The argument provided with the asynchronous I/O was setup
- * nbytes - The number of bytes actually transferred (or a negated errno
- * value;
- *
- * Returned Value:
- * None
- *
- * Assumptions:
- * Probably called from an interrupt handler.
- *
- ****************************************************************************/
- #ifdef HAVE_INTIN_ENDPOINT
- static void usbhost_notification_callback(FAR void *arg, ssize_t nbytes)
- {
- FAR struct usbhost_cdcacm_s *priv = (FAR struct usbhost_cdcacm_s *)arg;
- uint32_t delay = 0;
- DEBUGASSERT(priv);
- /* Are we still connected? */
- if (!priv->disconnected)
- {
- /* Check for a failure. On higher end host controllers, the
- * asynchronous transfer will pend until data is available (OHCI and
- * EHCI). On lower end host controllers (like STM32 and EFM32), the
- * transfer will fail immediately when the device NAKs the first
- * attempted interrupt IN transfer (with nbytes == -EAGAIN). In that
- * case (or in the case of other errors), we must fall back to
- * polling.
- */
- DEBUGASSERT(nbytes >= INT16_MIN && nbytes <= INT16_MAX);
- priv->nbytes = (int16_t)nbytes;
- if (nbytes < 0)
- {
- /* This debug output is good to know, but really a nuisance for
- * those configurations where we have to fall back to polling.
- * FIX: Don't output the message is the result is -EAGAIN.
- */
- #if defined(CONFIG_DEBUG_USB) && !defined(CONFIG_DEBUG_INFO)
- if (nbytes != -EAGAIN)
- #endif
- {
- uerr("ERROR: Transfer failed: %d\n", nbytes);
- }
- /* We don't know the nature of the failure, but we need to do all
- * that we can do to avoid a CPU hog error loop.
- *
- * Use the low-priority work queue and delay polling for the next
- * event. We want to use as little CPU bandwidth as possible in
- * this case.
- */
- delay = USBHOST_CDCACM_NTDELAY;
- }
- /* Make sure that the work structure available. There is a remote
- * chance that this may collide with a device disconnection event.
- */
- if (work_available(&priv->ntwork))
- {
- (void)work_queue(HPWORK, &priv->ntwork,
- (worker_t)usbhost_notification_work,
- priv, delay);
- }
- }
- }
- #endif
- /************************************************************************************
- * UART buffer data transfer
- ************************************************************************************/
- /****************************************************************************
- * Name: usbhost_txdata_work
- *
- * Description:
- * Send more OUT data to the attached CDC/ACM device.
- *
- * Input Parameters:
- * arg - A reference to the CDC/ACM class private data
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static void usbhost_txdata_work(FAR void *arg)
- {
- FAR struct usbhost_cdcacm_s *priv;
- FAR struct usbhost_hubport_s *hport;
- FAR struct uart_dev_s *uartdev;
- FAR struct uart_buffer_s *txbuf;
- ssize_t nwritten;
- int txndx;
- int txtail;
- int ret;
- priv = (FAR struct usbhost_cdcacm_s *)arg;
- DEBUGASSERT(priv);
- hport = priv->usbclass.hport;
- DEBUGASSERT(hport);
- uartdev = &priv->uartdev;
- txbuf = &uartdev->xmit;
- /* Do nothing if TX transmission is disabled */
- if (!priv->txena)
- {
- /* Terminate the work now *without* rescheduling */
- return;
- }
- /* Loop until The UART TX buffer is empty (or we become disconnected) */
- txtail = txbuf->tail;
- txndx = 0;
- while (txtail != txbuf->head && priv->txena && !priv->disconnected)
- {
- /* Copy data from the UART TX buffer until either 1) the UART TX
- * buffer has been emptied, or 2) the Bulk OUT buffer is full.
- */
- txndx = 0;
- while (txtail != txbuf->head && txndx < priv->pktsize)
- {
- /* Copy the next byte */
- priv->outbuf[txndx] = txbuf->buffer[txtail];
- /* Increment counters and indices */
- txndx++;
- if (++txtail >= txbuf->size)
- {
- txtail = 0;
- }
- }
- /* Save the updated tail pointer so that it cannot be sent again */
- txbuf->tail = txtail;
- /* Bytes were removed from the TX buffer. Inform any waiters that
- * there is space available in the TX buffer.
- */
- uart_datasent(uartdev);
- /* Send the filled TX buffer to the CDC/ACM device */
- nwritten = DRVR_TRANSFER(hport->drvr, priv->bulkout,
- priv->outbuf, txndx);
- if (nwritten < 0)
- {
- /* The most likely reason for a failure is that CDC/ACM device
- * NAK'ed our packet OR that the device has been disconnected.
- *
- * Just break out of the loop, rescheduling the work (unless
- * the device is disconnected).
- */
- uerr("ERROR: DRVR_TRANSFER for packet failed: %d\n", (int)nwritten);
- break;
- }
- }
- /* We get here because: 1) the UART TX buffer is empty and there is
- * nothing more to send, 2) the CDC/ACM device was not ready to accept our
- * data, or the device is no longer available.
- *
- * If the last packet sent was and even multiple of the packet size, then
- * we need to send a zero length packet (ZLP).
- */
- if (txndx == priv->pktsize && !priv->disconnected)
- {
- /* Send the ZLP to the CDC/ACM device */
- nwritten = DRVR_TRANSFER(hport->drvr, priv->bulkout,
- priv->outbuf, 0);
- if (nwritten < 0)
- {
- /* The most likely reason for a failure is that CDC/ACM device
- * NAK'ed our packet.
- */
- uerr("ERROR: DRVR_TRANSFER for ZLP failed: %d\n", (int)nwritten);
- }
- }
- /* Check again if TX reception is enabled and that the device is still
- * connected. These states could have changed since we started the
- * transfer.
- */
- if (priv->txena && !priv->disconnected)
- {
- /* Schedule TX data work to occur after a delay. */
- ret = work_queue(LPWORK, &priv->txwork, usbhost_txdata_work, priv,
- USBHOST_CDCACM_TXDELAY);
- DEBUGASSERT(ret >= 0);
- UNUSED(ret);
- }
- }
- /****************************************************************************
- * Name: usbhost_rxdata_work
- *
- * Description:
- * Get more IN data from the attached CDC/ACM device.
- *
- * Input Parameters:
- * arg - A reference to the CDC/ACM class private data
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static void usbhost_rxdata_work(FAR void *arg)
- {
- FAR struct usbhost_cdcacm_s *priv;
- FAR struct usbhost_hubport_s *hport;
- FAR struct uart_dev_s *uartdev;
- FAR struct uart_buffer_s *rxbuf;
- ssize_t nread;
- int nxfrd;
- int nexthead;
- int rxndx;
- int ret;
- priv = (FAR struct usbhost_cdcacm_s *)arg;
- DEBUGASSERT(priv);
- hport = priv->usbclass.hport;
- DEBUGASSERT(hport);
- uartdev = &priv->uartdev;
- rxbuf = &uartdev->recv;
- /* Get the index in the RX packet buffer where we will take the first
- * byte of data.
- */
- rxndx = priv->rxndx;
- nxfrd = 0;
- /* Get the index to the value of the UART RX buffer head AFTER the
- * first character has been stored. We need to know this in order
- * to test if the UART RX buffer is full.
- */
- nexthead = rxbuf->head + 1;
- if (nexthead >= rxbuf->size)
- {
- nexthead = 0;
- }
- /* Loop until either:
- *
- * 1. The UART RX buffer is full
- * 2. There is no more data available from the CDC/ACM device
- * 3. RX rec
- */
- #ifdef CONFIG_SERIAL_IFLOWCONTROL
- while (priv->rxena && priv->rts && !priv->disconnected)
- #else
- while (priv->rxena && !priv->disconnected)
- #endif
- {
- /* Stop now if there is no room for another character in the RX buffer. */
- if (nexthead == rxbuf->tail)
- {
- /* Break out of the loop, rescheduling the work */
- break;
- }
- /* Do we have any buffer RX data to transfer? */
- if (priv->nrxbytes < 1)
- {
- /* No.. Read more data from the CDC/ACM device */
- nread = DRVR_TRANSFER(hport->drvr, priv->bulkin,
- priv->inbuf, priv->pktsize);
- if (nread < 0)
- {
- /* The most likely reason for a failure is that the has no
- * data available now and NAK'ed the IN token OR that the
- * transfer was cancelled because the device was disconnected.
- *
- * Just break out of the loop, rescheduling the work (if the
- * device was not disconnected.
- */
- uerr("ERROR: DRVR_TRANSFER for packet failed: %d\n", (int)nread);
- break;
- }
- /* Save the number of bytes read. This might be zero if
- * a Zero Length Packet (ZLP) is received. The ZLP is
- * part of the bulk transfer protocol, but otherwise of
- * no interest to us.
- */
- priv->nrxbytes = (uint16_t)nread;
- rxndx = 0;
- /* Ignore ZLPs */
- if (nread == 0)
- {
- continue;
- }
- }
- /* Transfer one byte from the RX packet buffer into UART RX buffer */
- rxbuf->buffer[rxbuf->head] = priv->inbuf[rxndx];
- nxfrd++;
- /* Save the updated indices */
- rxbuf->head = nexthead;
- priv->rxndx = rxndx;
- /* Update the head point for for the next pass through the loop
- * handling. If nexthead incremented to rxbuf->tail, then the
- * RX buffer will and we will exit the loop at the top.
- */
- if (++nexthead >= rxbuf->size)
- {
- nexthead = 0;
- }
- /* Increment the index in the USB IN packet buffer. If the
- * index becomes equal to the number of bytes in the buffer, then
- * we have consumed all of the RX data.
- */
- if (++rxndx >= priv->nrxbytes)
- {
- /* In that case set the number of bytes in the buffer to zero.
- * This will force re-reading on the next time through the loop.
- */
- priv->nrxbytes = 0;
- priv->rxndx = 0;
- /* Inform any waiters there there is new incoming data available. */
- uart_datareceived(uartdev);
- nxfrd = 0;
- }
- }
- /* We break out to here: 1) the UART RX buffer is full, 2) the CDC/ACM
- * device is not ready to provide us with more serial data, or 3) the
- * device has been disconnected.
- *
- * Check if the device is still available: RX enabled, no RX flow
- * control in effect, and that the device is not disconnected. These
- * states could have changed since we started the transfer.
- */
- #ifdef CONFIG_SERIAL_IFLOWCONTROL
- if (priv->rxena && priv->rts && work_available(&priv->rxwork) && !priv->disconnected)
- #else
- if (priv->rxena && work_available(&priv->rxwork) && !priv->disconnected)
- #endif
- {
- /* Schedule RX data reception work to occur after a delay. This will
- * affect our responsive in certain cases. The delayed work, however,
- * will be cancelled and replaced with immediate work when the upper
- * layer demands more data.
- */
- ret = work_queue(LPWORK, &priv->rxwork, usbhost_rxdata_work, priv,
- USBHOST_CDCACM_RXDELAY);
- DEBUGASSERT(ret >= 0);
- UNUSED(ret);
- }
- /* If any bytes were added to the buffer, inform any waiters there there
- * is new incoming data available.
- */
- if (nxfrd)
- {
- uart_datareceived(uartdev);
- }
- }
- /****************************************************************************
- * Name: usbhost_destroy
- *
- * Description:
- * The USB CDC/ACM 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_cdcacm_s *priv = (FAR struct usbhost_cdcacm_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 serial lower half driver */
- usbhost_mkdevname(priv, devname);
- #warning Missing logic
- /* Release the device name used by this connection */
- usbhost_devno_free(priv);
- /* Free the allocated endpoints */
- if (priv->bulkout)
- {
- DRVR_EPFREE(hport->drvr, priv->bulkout);
- }
- if (priv->bulkin)
- {
- DRVR_EPFREE(hport->drvr, priv->bulkin);
- }
- #ifdef HAVE_INTIN_ENDPOINT
- if (priv->intin)
- {
- DRVR_EPFREE(hport->drvr, priv->intin);
- }
- #endif
- /* Free any transfer buffers */
- usbhost_free_buffers(priv);
- /* Destroy the semaphores */
- sem_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 int usbhost_cfgdesc(FAR struct usbhost_cdcacm_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;
- FAR struct usbhost_epdesc_s iindesc;
- int remaining;
- uint8_t found = 0;
- uint8_t currif = 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));
- memset(&iindesc, 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 descriptors 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. The CDC/ACM device may include two
- * interfaces:
- *
- * 1) A data interface which consists of two endpoints (bulk in +
- * bulk out) and
- * 2) A control interface which consists of one interrupt in
- * endpoint.
- */
- case USB_DESC_TYPE_INTERFACE:
- {
- FAR struct usb_ifdesc_s *ifdesc = (FAR struct usb_ifdesc_s *)configdesc;
- uinfo("Interface descriptor: class: %d subclass: %d proto: %d\n",
- ifdesc->classid, ifdesc->subclass, ifdesc->protocol);
- DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC);
- /* Check for the CDC/ACM data interface */
- if (ifdesc->classid == USB_CLASS_CDC_DATA &&
- (found & USBHOST_DATAIF_FOUND) == 0)
- {
- /* Save the data interface number and mark that the data
- * interface found has been found.
- */
- priv->dataif = ifdesc->ifno;
- found |= USBHOST_DATAIF_FOUND;
- currif = USBHOST_DATAIF_FOUND;
- }
- #ifdef HAVE_CTRL_INTERFACE
- else if (ifdesc->classid == USB_CLASS_CDC &&
- (found & USBHOST_CTRLIF_FOUND) == 0)
- {
- /* Otherwise, tentatively assume that this is the control
- * interface.
- */
- priv->ctrlif = ifdesc->ifno;
- currif = USBHOST_CTRLIF_FOUND;
- }
- #endif
- else
- {
- /* Its something else */
- currif = 0;
- }
- }
- 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: currif: %02x attr: %02x\n",
- currif, epdesc->attr);
- DEBUGASSERT(remaining >= USB_SIZEOF_EPDESC);
- /* Check for a bulk endpoint. */
- if (currif == USBHOST_DATAIF_FOUND &&
- (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_BULKOUT_FOUND) != 0)
- {
- /* Oops.. more than one endpoint. We don't know
- * what to do with this.
- */
- return -EINVAL;
- }
- found |= USBHOST_BULKOUT_FOUND;
- /* 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_BULKIN_FOUND) != 0)
- {
- /* Oops.. more than one endpoint. We don't know
- * what to do with this.
- */
- return -EINVAL;
- }
- found |= USBHOST_BULKIN_FOUND;
- /* 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);
- }
- }
- #ifdef HAVE_CTRL_INTERFACE
- /* Check for an interrupt IN endpoint. */
- else if (currif == USBHOST_CTRLIF_FOUND &&
- (epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) == USB_EP_ATTR_XFER_INT)
- {
- /* Yes.. it is a interrupt endpoint. IN or OUT? */
- if (USB_ISEPIN(epdesc->addr))
- {
- #ifdef HAVE_INTIN_ENDPOINT
- /* It is an IN interrupt endpoint. There should be only one
- * interrupt IN endpoint.
- */
- if ((found & USBHOST_INTIN_FOUND) != 0)
- {
- /* Oops.. more than one. We don't know what to do
- * with this.
- */
- return -EINVAL;
- }
- /* Let's pick this interface as the one and only control
- * interface.
- */
- found |= (USBHOST_CTRLIF_FOUND | USBHOST_INTIN_FOUND);
- /* Save the bulk OUT endpoint information */
- iindesc.hport = hport;
- iindesc.addr = epdesc->addr & USB_EP_ADDR_NUMBER_MASK;
- iindesc.in = false;
- iindesc.xfrtype = USB_EP_ATTR_XFER_INT;
- iindesc.interval = epdesc->interval;
- iindesc.mxpacketsize = usbhost_getle16(epdesc->mxpacketsize);
- uinfo("Interrupt IN EP addr:%d mxpacketsize:%d\n",
- boutdesc.addr, boutdesc.mxpacketsize);
- #else
- found |= USBHOST_CTRLIF_FOUND;
- #endif
- }
- }
- #endif
- }
- 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 needed for the
- * basic CDC/ACM data itnerface? NOTE: that the Control interface with
- * the Interrupt IN endpoint is optional.
- */
- if ((found & USBHOST_MINFOUND) != USBHOST_MINFOUND)
- {
- uerr("ERROR: Found DATA IF:%s BULK IN:%s BULK OUT:%s\n",
- (found & USBHOST_DATAIF_FOUND) != 0 ? "YES" : "NO",
- (found & USBHOST_BULKIN_FOUND) != 0 ? "YES" : "NO",
- (found & USBHOST_BULKOUT_FOUND) != 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");
- (void)DRVR_EPFREE(hport->drvr, priv->bulkout);
- return ret;
- }
- #ifdef HAVE_INTIN_ENDPOINT
- /* The control interface with interrupt IN endpoint is optional */
- if ((found & USBHOST_HAVE_CTRLIF) == USBHOST_HAVE_CTRLIF)
- {
- ret = DRVR_EPALLOC(hport->drvr, &iindesc, &priv->intin);
- if (ret < 0)
- {
- uerr("ERROR: Failed to allocate Interrupt IN endpoint\n");
- priv->intin = NULL;
- }
- }
- #endif
- uinfo("Endpoints allocated\n");
- return OK;
- }
- /****************************************************************************
- * 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_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
- *
- ****************************************************************************/
- #ifdef HAVE_CTRL_INTERFACE
- 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));
- }
- #endif
- /****************************************************************************
- * Name: usbhost_alloc_buffers
- *
- * Description:
- * Allocate transfer buffer memory.
- *
- * Input Parameters:
- * priv - A reference to the class instance.
- *
- * Returned Value:
- * On sucess, zero (OK) is returned. On failure, an negated errno value
- * is returned to indicate the nature of the failure.
- *
- ****************************************************************************/
- static int usbhost_alloc_buffers(FAR struct usbhost_cdcacm_s *priv)
- {
- FAR struct usbhost_hubport_s *hport;
- size_t maxlen;
- int ret;
- DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL &&
- priv->linecode == NULL);
- hport = priv->usbclass.hport;
- /* Allocate memory for control requests */
- ret = DRVR_ALLOC(hport->drvr, (FAR uint8_t **)&priv->ctrlreq, &maxlen);
- if (ret < 0)
- {
- uerr("ERROR: DRVR_ALLOC of ctrlreq failed: %d\n", ret);
- goto errout;
- }
- DEBUGASSERT(maxlen >= sizeof(struct usb_ctrlreq_s));
- /* Allocate buffer for sending line coding data. */
- ret = DRVR_IOALLOC(hport->drvr, &priv->linecode,
- sizeof(struct cdc_linecoding_s));
- if (ret < 0)
- {
- uerr("ERROR: DRVR_IOALLOC of line coding failed: %d (%d bytes)\n",
- ret, sizeof(struct cdc_linecoding_s));
- goto errout;
- }
- #ifdef HAVE_INTIN_ENDPOINT
- /* Allocate (optional) buffer for receiving line status data. */
- if (priv->intin)
- {
- ret = DRVR_IOALLOC(hport->drvr, &priv->notification, MAX_NOTIFICATION);
- if (ret < 0)
- {
- uerr("ERROR: DRVR_IOALLOC of line status failed: %d (%d bytes)\n",
- ret, MAX_NOTIFICATION);
- goto errout;
- }
- }
- #endif
- /* Set the size of Bulk IN and OUT buffers to the max packet size */
- priv->pktsize = (hport->speed == USB_SPEED_HIGH) ? 512 : 64;
- /* Allocate a RX buffer for Bulk IN transfers */
- ret = DRVR_IOALLOC(hport->drvr, &priv->inbuf, priv->pktsize);
- if (ret < 0)
- {
- uerr("ERROR: DRVR_IOALLOC of Bulk IN buffer failed: %d (%d bytes)\n",
- ret, priv->pktsize);
- goto errout;
- }
- /* Allocate a TX buffer for Bulk IN transfers */
- ret = DRVR_IOALLOC(hport->drvr, &priv->outbuf, priv->pktsize);
- if (ret < 0)
- {
- uerr("ERROR: DRVR_IOALLOC of Bulk OUT buffer failed: %d (%d bytes)\n",
- ret, priv->pktsize);
- goto errout;
- }
- return OK;
- errout:
- usbhost_free_buffers(priv);
- return ret;
- }
- /****************************************************************************
- * Name: usbhost_free_buffers
- *
- * Description:
- * Free transfer buffer memory.
- *
- * Input Parameters:
- * priv - A reference to the class instance.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static void usbhost_free_buffers(FAR struct usbhost_cdcacm_s *priv)
- {
- FAR struct usbhost_hubport_s *hport;
- DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL);
- hport = priv->usbclass.hport;
- if (priv->ctrlreq)
- {
- (void)DRVR_FREE(hport->drvr, priv->ctrlreq);
- }
- if (priv->linecode)
- {
- (void)DRVR_IOFREE(hport->drvr, priv->linecode);
- }
- if (priv->notification)
- {
- (void)DRVR_IOFREE(hport->drvr, priv->notification);
- }
- if (priv->inbuf)
- {
- (void)DRVR_IOFREE(hport->drvr, priv->inbuf);
- }
- if (priv->outbuf)
- {
- (void)DRVR_IOFREE(hport->drvr, priv->outbuf);
- }
- priv->pktsize = 0;
- priv->ctrlreq = NULL;
- priv->linecode = NULL;
- priv->notification = NULL;
- priv->inbuf = NULL;
- priv->outbuf = NULL;
- }
- /****************************************************************************
- * 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 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_cdcacm_s *priv;
- FAR struct uart_dev_s *uartdev;
- /* Allocate a USB host CDC/ACM class instance */
- priv = usbhost_allocclass();
- if (priv)
- {
- /* Initialize the allocated CDC/ACM class instance */
- memset(priv, 0, sizeof(struct usbhost_cdcacm_s));
- /* Assign a device number to this class instance */
- if (usbhost_devno_alloc(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) */
- sem_init(&priv->exclsem, 0, 1);
- /* Set up the serial lower-half interface */
- uartdev = &priv->uartdev;
- uartdev->recv.size = CONFIG_USBHOST_CDCACM_RXBUFSIZE;
- uartdev->recv.buffer = priv->rxbuffer;
- uartdev->xmit.size = CONFIG_USBHOST_CDCACM_TXBUFSIZE;
- uartdev->xmit.buffer = priv->txbuffer;
- uartdev->ops = &g_uart_ops;
- uartdev->priv = priv;
- /* Set up the initial line status */
- priv->baud = CONFIG_USBHOST_CDCACM_BAUD;
- priv->nbits = CONFIG_USBHOST_CDCACM_BITS;
- priv->parity = CONFIG_USBHOST_CDCACM_PARITY;
- priv->stop2 = CONFIG_USBHOST_CDCACM_2STOP;
- #ifdef CONFIG_SERIAL_IFLOWCONTROL
- priv->rts = true;
- #endif
- /* Return the instance of the USB CDC/ACM class */
- 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_cdcacm_s *priv = (FAR struct usbhost_cdcacm_s *)usbclass;
- #ifdef HAVE_INTIN_ENDPOINT
- FAR struct usbhost_hubport_s *hport;
- #endif
- char devname[DEV_NAMELEN];
- int ret;
- DEBUGASSERT(priv != NULL &&
- configdesc != NULL &&
- desclen >= sizeof(struct usb_cfgdesc_s));
- #ifdef HAVE_INTIN_ENDPOINT
- hport = priv->usbclass.hport;
- DEBUGASSERT(hport);
- #endif
- /* Get exclusive access to the device structure */
- usbhost_takesem(&priv->exclsem);
- /* Increment the reference count. This will prevent usbhost_destroy() from
- * being called asynchronously if the device is removed.
- */
- priv->crefs++;
- DEBUGASSERT(priv->crefs == 2);
- /* 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);
- goto errout;
- }
- /* Set aside a transfer buffer for exclusive use by the CDC/ACM driver */
- ret = usbhost_alloc_buffers(priv);
- if (ret < 0)
- {
- uerr("ERROR: Failed to allocate transfer buffer\n");
- goto errout;
- }
- #ifdef HAVE_CTRL_INTERFACE
- /* Send the initial line encoding */
- ret = usbhost_linecoding_send(priv);
- if (ret < 0)
- {
- uerr("ERROR: usbhost_linecoding_send() failed: %d\n", ret);
- }
- #endif
- /* Register the lower half serial instance with the upper half serial
- * driver */
- usbhost_mkdevname(priv, devname);
- uinfo("Register device: %s\n", devname);
- ret = uart_register(devname, &priv->uartdev);
- if (ret < 0)
- {
- uerr("ERROR: uart_register() failed: %d\n", ret);
- goto errout;
- }
- #ifdef HAVE_INTIN_ENDPOINT
- /* Do we have an interrupt IN endpoint? */
- if (priv->intin)
- {
- /* Begin monitoring of port status change events */
- uinfo("Start notification monitoring\n");
- ret = DRVR_ASYNCH(hport->drvr, priv->intin,
- (FAR uint8_t *)priv->notification,
- MAX_NOTIFICATION, usbhost_notification_callback,
- priv);
- if (ret < 0)
- {
- uerr("ERROR: DRVR_ASYNCH failed: %d\n", ret);
- }
- }
- #endif
- errout:
- /* 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
- * serial driver has been registered.
- */
- DEBUGASSERT(priv->crefs >= 2);
- priv->crefs--;
- /* 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_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_cdcacm_s *priv = (FAR struct usbhost_cdcacm_s *)usbclass;
- FAR struct usbhost_hubport_s *hport;
- irqstate_t flags;
- int ret;
- DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL);
- hport = priv->usbclass.hport;
- /* Set an indication to any users of the CDC/ACM device that the device
- * is no longer available.
- */
- flags = enter_critical_section();
- priv->disconnected = true;
- /* Let the upper half driver know that serial device is no longer
- * connected.
- */
- uart_connected(&priv->uartdev, false);
- /* Cancel any ongoing Bulk transfers */
- ret = DRVR_CANCEL(hport->drvr, priv->bulkin);
- if (ret < 0)
- {
- uerr("ERROR: Bulk IN DRVR_CANCEL failed: %d\n", ret);
- }
- ret = DRVR_CANCEL(hport->drvr, priv->bulkout);
- if (ret < 0)
- {
- uerr("ERROR: Bulk OUT DRVR_CANCEL failed: %d\n", ret);
- }
- #ifdef HAVE_INTIN_ENDPOINT
- /* Cancel any pending asynchronous I/O */
- if (priv->intin)
- {
- int ret = DRVR_CANCEL(hport->drvr, priv->intin);
- if (ret < 0)
- {
- uerr("ERROR: Interrupt IN DRVR_CANCEL failed: %d\n", ret);
- }
- }
- #endif
- /* 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
- * serial 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->ntwork.worker, usbhost_destroy);
- DEBUGASSERT(work_available(&priv->ntwork));
- (void)work_queue(HPWORK, &priv->ntwork, usbhost_destroy, priv, 0);
- }
- else
- {
- /* Do the work now */
- usbhost_destroy(priv);
- }
- }
- leave_critical_section(flags);
- return OK;
- }
- /****************************************************************************
- * Serial Lower-Half Interfaces
- ****************************************************************************/
- /****************************************************************************
- * Name: usbhost_setup
- *
- * Description:
- * Configure the USART baud, bits, parity, etc. This method is called the
- * first time that the serial port is opened.
- *
- ****************************************************************************/
- static int usbhost_setup(FAR struct uart_dev_s *uartdev)
- {
- FAR struct usbhost_cdcacm_s *priv;
- irqstate_t flags;
- int ret;
- uinfo("Entry\n");
- DEBUGASSERT(uartdev && uartdev->priv);
- priv = (FAR struct usbhost_cdcacm_s *)uartdev->priv;
- /* Make sure that we have exclusive access to the private data structure */
- DEBUGASSERT(priv->crefs > 0 && priv->crefs < USBHOST_MAX_CREFS);
- usbhost_takesem(&priv->exclsem);
- /* Check if the CDC/ACM device is still connected. We need to disable
- * interrupts momentarily to assure that there are no asynchronous
- * isconnect events.
- */
- flags = enter_critical_section();
- if (priv->disconnected)
- {
- /* No... the block driver is no longer bound to the class. That means that
- * the USB CDC/ACM 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_shutdown
- *
- * Description:
- * Disable the USART. This method is called when the serial
- * port is closed
- *
- ****************************************************************************/
- static void usbhost_shutdown(FAR struct uart_dev_s *uartdev)
- {
- FAR struct usbhost_cdcacm_s *priv;
- irqstate_t flags;
- uinfo("Entry\n");
- DEBUGASSERT(uartdev && uartdev->priv);
- priv = (FAR struct usbhost_cdcacm_s *)uartdev->priv;
- /* Decrement the reference count on the block driver */
- DEBUGASSERT(priv->crefs > 1);
- usbhost_takesem(&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 CDC/ACM device is still connected. If the
- * CDC/ACM 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);
- }
- /****************************************************************************
- * Name: usbhost_attach
- *
- * Description:
- * Configure the USART to operation in interrupt driven mode. This method is
- * called when the serial port is opened. Normally, this is just after the
- * the setup() method is called, however, the serial console may operate in
- * a non-interrupt driven mode during the boot phase.
- *
- * RX and TX interrupts are not enabled when by the attach method (unless the
- * hardware supports multiple levels of interrupt enabling). The RX and TX
- * interrupts are not enabled until the txint() and rxint() methods are called.
- *
- ****************************************************************************/
- static int usbhost_attach(FAR struct uart_dev_s *uartdev)
- {
- return OK;
- }
- /****************************************************************************
- * Name: usbhost_detach
- *
- * Description:
- * Detach USART interrupts. This method is called when the serial port is
- * closed normally just before the shutdown method is called. The exception
- * is the serial console which is never shutdown.
- *
- ****************************************************************************/
- static void usbhost_detach(FAR struct uart_dev_s *uartdev)
- {
- }
- /****************************************************************************
- * Name: usbhost_ioctl
- *
- * Description:
- * All ioctl calls will be routed through this method
- *
- ****************************************************************************/
- static int usbhost_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
- {
- struct inode *inode;
- struct usbhost_cdcacm_s *priv;
- int ret;
- uinfo("Entry\n");
- DEBUGASSERT(filep && filep->f_inode);
- inode = filep->f_inode;
- DEBUGASSERT(inode && inode->i_private);
- priv = (FAR struct usbhost_cdcacm_s *)inode->i_private;
- /* Check if the CDC/ACM device is still connected */
- if (priv->disconnected)
- {
- /* No... the serial device has been disconnecgted. Refuse to process
- * any ioctl commands.
- */
- ret = -ENODEV;
- }
- else
- {
- /* Process the IOCTL by command */
- usbhost_takesem(&priv->exclsem);
- switch (cmd)
- {
- #ifdef CONFIG_SERIAL_TIOCSERGSTRUCT
- case TIOCSERGSTRUCT:
- {
- FAR struct usbhost_cdcacm_s *user =
- (FAR struct usbhost_cdcacm_s *)arg;
- if (!user)
- {
- ret = -EINVAL;
- }
- else
- {
- memcpy(user, uartdev, sizeof(struct usbhost_cdcacm_s));
- }
- }
- break;
- #endif
- #ifdef CONFIG_SERIAL_TERMIOS
- case TCGETS:
- {
- FAR struct termios *termiosp = (FAR struct termios *)arg;
- if (!termiosp)
- {
- ret = -EINVAL;
- break;
- }
- cfsetispeed(termiosp, priv->baud);
- termiosp->c_cflag =
- ((priv->parity != 0) ? PARENB : 0) |
- ((priv->parity == 1) ? PARODD : 0) |
- ((priv->stop2) ? CSTOPB : 0)
- #ifdef CONFIG_SERIAL_OFLOWCONTROL
- | ((priv->oflow) ? CCTS_OFLOW : 0)
- #endif
- #ifdef CONFIG_SERIAL_IFLOWCONTROL
- | ((priv->iflow) ? CRTS_IFLOW : 0)
- #endif
- ;
- switch (priv->bits)
- {
- case 5:
- termiosp->c_cflag |= CS5;
- break;
- case 6:
- termiosp->c_cflag |= CS6;
- break;
- case 7:
- termiosp->c_cflag |= CS7;
- break;
- default: /* 16-bits? */
- case 8:
- termiosp->c_cflag |= CS8;
- break;
- }
- }
- break;
- case TCSETS:
- {
- FAR struct termios *termiosp = (FAR struct termios *)arg;
- #ifdef CONFIG_SERIAL_IFLOWCONTROL
- bool iflow;
- #endif
- if (!termiosp)
- {
- ret = -EINVAL;
- break;
- }
- if (termiosp->c_cflag & PARENB)
- {
- priv->parity = (termiosp->c_cflag & PARODD) ? 1 : 2;
- }
- else
- {
- priv->parity = 0;
- }
- priv->stopbits2 = (termiosp->c_cflag & CSTOPB) != 0;
- #ifdef CONFIG_SERIAL_OFLOWCONTROL
- priv->oflow = (termiosp->c_cflag & CCTS_OFLOW) != 0;
- #endif
- #ifdef CONFIG_SERIAL_IFLOWCONTROL
- iflow = priv->iflow;
- priv->iflow = (termiosp->c_cflag & CRTS_IFLOW) != 0;
- #endif
- switch (termiosp->c_flag & CSIZE)
- {
- case C5:
- priv->bits = 5;
- break;
- case C6:
- priv->bits = 6;
- break;
- case C7:
- priv->bits = 7;
- break;
- default:
- case C8:
- priv->bits = 8;
- break;
- }
- /* Note that only cfgetispeed is used because we have knowledge
- * that only one speed is supported.
- */
- priv->baud = cfgetispeed(termiosp);
- #ifdef CONFIG_SERIAL_IFLOWCONTROL
- /* Set RTS if input flow control changed */
- if (iflow != !priv->iflow)
- {
- priv->rts = true;
- }
- #endif
- #ifdef HAVE_CTRL_INTERFACE
- /* Effect the changes immediately - note that we do not implement
- * TCSADRAIN / TCSAFLUSH
- */
- ret = usbhost_linecoding_send(priv);
- #endif
- }
- break;
- #endif /* CONFIG_SERIAL_TERMIOS */
- #ifdef CONFIG_USBHOST_CDCACM_BREAKS
- case TIOCSBRK: /* BSD compatibility: Turn break on, unconditionally */
- {
- #warning Missing logic
- }
- break;
- case TIOCCBRK: /* BSD compatibility: Turn break off, unconditionally */
- {
- #warning Missing logic
- }
- break;
- #endif
- default:
- ret = -ENOTTY;
- break;
- }
- usbhost_givesem(&priv->exclsem);
- }
- return ret;
- }
- /****************************************************************************
- * Name: usbhost_rxint
- *
- * Description:
- * Call to enable or disable RX interrupts
- *
- ****************************************************************************/
- static void usbhost_rxint(FAR struct uart_dev_s *uartdev, bool enable)
- {
- FAR struct usbhost_cdcacm_s *priv;
- int ret;
- DEBUGASSERT(uartdev && uartdev->priv);
- priv = (FAR struct usbhost_cdcacm_s *)uartdev->priv;
- /* Are we enabling or disabling RX reception? */
- if (enable && !priv->rxena)
- {
- /* Cancel any pending, delayed RX data reception work */
- (void)work_cancel(LPWORK, &priv->rxwork);
- /* Restart immediate RX data reception work (unless RX flow control
- * is in* effect.
- */
- #ifdef CONFIG_SERIAL_IFLOWCONTROL
- if (priv->rts)
- #endif
- {
- ret = work_queue(LPWORK, &priv->rxwork,
- usbhost_rxdata_work, priv, 0);
- DEBUGASSERT(ret >= 0);
- UNUSED(ret);
- }
- }
- else if (!enable && priv->rxena)
- {
- /* Cancel any pending RX data reception work */
- (void)work_cancel(LPWORK, &priv->rxwork);
- }
- /* Save the new RX enable state */
- priv->rxena = enable;
- }
- /****************************************************************************
- * Name: usbhost_rxavailable
- *
- * Description:
- * Return true if the receive buffer is not empty
- *
- ****************************************************************************/
- static bool usbhost_rxavailable(FAR struct uart_dev_s *uartdev)
- {
- FAR struct usbhost_cdcacm_s *priv;
- DEBUGASSERT(uartdev && uartdev->priv);
- priv = (FAR struct usbhost_cdcacm_s *)uartdev->priv;
- return (priv->nrxbytes > 0);
- }
- /****************************************************************************
- * Name: usbhost_rxflowcontrol
- *
- * Description:
- * Called when Rx buffer is full (or exceeds configured watermark levels
- * if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is defined).
- * Return true if UART activated RX flow control to block more incoming
- * data
- *
- * Input parameters:
- * uartdev - UART device instance
- * nbuffered - the number of characters currently buffered
- * (if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is
- * not defined the value will be 0 for an empty buffer or the
- * defined buffer size for a full buffer)
- * upper - true indicates the upper watermark was crossed where
- * false indicates the lower watermark has been crossed
- *
- * Returned Value:
- * true if RX flow control activated.
- *
- ****************************************************************************/
- #ifdef CONFIG_SERIAL_IFLOWCONTROL
- static bool usbhost_rxflowcontrol(FAR struct uart_dev_s *uartdev,
- unsigned int nbuffered, bool upper)
- {
- FAR struct usbhost_cdcacm_s *priv;
- bool newrts;
- int ret;
- DEBUGASSERT(uartdev && uartdev->priv);
- priv = (FAR struct usbhost_cdcacm_s *)uartdev->priv
- /* Is RX flow control enabled? */
- if (!priv->iflow)
- {
- /* Now.. make sure that RTS is set */
- priv->rts = true;
- return false;
- }
- /* Are we enabling or disabling RX flow control? */
- if (priv->rts && upper)
- {
- /* RX flow control is not in effect (RTS is true) but we have just
- * crossed the upper threshold, meaning that we should now clear
- * RTS.
- */
- priv->rts = false;
- /* Cancel any pending RX data reception work */
- (void)work_cancel(LPWORK, &priv->rxwork)
- return true;
- }
- else if (!priv->rts && !upper)
- {
- /* RX flow control is in effect (RTS is false) and we have just
- * crossed the lower threshold, meaning that we should now set
- * RTS.
- */
- priv->rts = true;
- /* Restart RX data reception work flow unless RX reception is
- * disabled.
- */
- if (priv->rxena && work_available(&priv->rxwork))
- {
- ret = work_queue(LPWORK, &priv->rxwork,
- usbhost_rxdata_work, priv, 0);
- DEBUGASSERT(ret >= 0);
- UNUSED(ret);
- }
- return false;
- }
- }
- #endif
- /****************************************************************************
- * Name: usbhost_txint
- *
- * Description:
- * Call to enable or disable TX interrupts
- *
- ****************************************************************************/
- static void usbhost_txint(FAR struct uart_dev_s *uartdev, bool enable)
- {
- FAR struct usbhost_cdcacm_s *priv;
- int ret;
- DEBUGASSERT(uartdev && uartdev->priv);
- priv = (FAR struct usbhost_cdcacm_s *)uartdev->priv;
- /* Are we enabling or disabling TX transmission? */
- if (enable && !priv->txena)
- {
- /* Cancel any pending, delayed TX data transmission work */
- (void)work_cancel(LPWORK, &priv->txwork);
- /* Restart immediate TX data transmission work */
- ret = work_queue(LPWORK, &priv->txwork,
- usbhost_txdata_work, priv, 0);
- DEBUGASSERT(ret >= 0);
- UNUSED(ret);
- }
- else if (!enable && priv->txena)
- {
- /* Cancel any pending TX data transmission work */
- (void)work_cancel(LPWORK, &priv->txwork);
- }
- /* Save the new RX enable state */
- priv->txena = enable;
- }
- /****************************************************************************
- * Name: usbhost_txready
- *
- * Description:
- * Return true if the transmit data register is not full.
- *
- ****************************************************************************/
- static bool usbhost_txready(FAR struct uart_dev_s *uartdev)
- {
- return true;
- }
- /****************************************************************************
- * Name: usbhost_txempty
- *
- * Description:
- * Return true if the transmit data buffer is empty
- *
- ****************************************************************************/
- static bool usbhost_txempty(FAR struct uart_dev_s *uartdev)
- {
- return true;
- }
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: usbhost_cdcacm_initialize
- *
- * Description:
- * Initialize the USB host CDC/ACM class. This function should be called
- * be platform-specific code in order to initialize and register support
- * for the USB host CDC/ACM 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_cdcacm_initialize(void)
- {
- /* If we have been configured to use pre-allocated CDC/ACM class instances,
- * then place all of the pre-allocated USB host CDC/ACM class instances
- * into a free list.
- */
- #if CONFIG_USBHOST_CDCACM_NPREALLOC > 0
- FAR struct usbhost_freestate_s *entry;
- int i;
- g_freelist = NULL;
- for (i = 0; i < CONFIG_USBHOST_CDCACM_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) CDC/ACM devices */
- return usbhost_registerclass(&g_cdcacm);
- }
- #endif /* CONFIG_USBHOST_CDCACM */
|