123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592 |
- /****************************************************************************
- * drivers/usbhost/usbhost_hidkbd.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 <sys/types.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <poll.h>
- #include <signal.h>
- #include <time.h>
- #include <fcntl.h>
- #include <assert.h>
- #include <errno.h>
- #include <debug.h>
- #include <nuttx/irq.h>
- #include <nuttx/kmalloc.h>
- #include <nuttx/kthread.h>
- #include <nuttx/arch.h>
- #include <nuttx/wqueue.h>
- #include <nuttx/signal.h>
- #include <nuttx/semaphore.h>
- #include <nuttx/fs/fs.h>
- #include <nuttx/usb/usb.h>
- #include <nuttx/usb/usbhost.h>
- #include <nuttx/usb/hid.h>
- #include <nuttx/usb/usbhost_devaddr.h>
- #ifdef CONFIG_HIDKBD_ENCODED
- # include <nuttx/streams.h>
- # include <nuttx/input/kbd_codec.h>
- #endif
- /* Don't compile if prerequisites are not met */
- #if defined(CONFIG_USBHOST) && !defined(CONFIG_USBHOST_INT_DISABLE)
- /****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
- /* Configuration ************************************************************/
- /* This determines how often the USB keyboard will be polled in units of
- * of microseconds. The default is 100MS.
- */
- #ifndef CONFIG_HIDKBD_POLLUSEC
- # define CONFIG_HIDKBD_POLLUSEC (100*1000)
- #endif
- /* Worker thread is needed, unfortunately, to handle some cornercase failure
- * conditions. This is kind of wasteful and begs for a re-design.
- */
- #ifndef CONFIG_SCHED_WORKQUEUE
- # warning "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
- #endif
- /* Provide some default values for other configuration settings */
- #ifndef CONFIG_HIDKBD_DEFPRIO
- # define CONFIG_HIDKBD_DEFPRIO 50
- #endif
- #ifndef CONFIG_HIDKBD_STACKSIZE
- # define CONFIG_HIDKBD_STACKSIZE 1024
- #endif
- #ifndef CONFIG_HIDKBD_BUFSIZE
- # define CONFIG_HIDKBD_BUFSIZE 64
- #endif
- #ifndef CONFIG_HIDKBD_NPOLLWAITERS
- # define CONFIG_HIDKBD_NPOLLWAITERS 2
- #endif
- /* The default is to support scancode mapping for the standard 104 key
- * keyboard. Setting CONFIG_HIDKBD_RAWSCANCODES will disable all scancode
- * mapping; Setting CONFIG_HIDKBD_ALLSCANCODES will enable mapping of all
- * scancodes;
- */
- #ifndef CONFIG_HIDKBD_RAWSCANCODES
- # ifdef CONFIG_HIDKBD_ALLSCANCODES
- # define USBHID_NUMSCANCODES (USBHID_KBDUSE_MAX+1)
- # else
- # define USBHID_NUMSCANCODES 104
- # endif
- #endif
- /* We can't support encoding of special characters of unless the Keyboard
- * CODEC is enabled.
- */
- #ifndef CONFIG_LIB_KBDCODEC
- # undef CONFIG_HIDKBD_ENCODED
- #endif
- /* If we are using raw scancodes, then we cannot support encoding of
- * special characters either.
- */
- #ifdef CONFIG_HIDKBD_RAWSCANCODES
- # undef CONFIG_HIDKBD_ENCODED
- #endif
- /* Driver support ***********************************************************/
- /* This format is used to construct the /dev/kbd[n] device driver path. It
- * defined here so that it will be used consistently in all places.
- */
- #define DEV_FORMAT "/dev/kbd%c"
- #define DEV_NAMELEN 11
- /* Used in usbhost_cfgdesc() */
- #define USBHOST_IFFOUND 0x01 /* Required I/F descriptor found */
- #define USBHOST_EPINFOUND 0x02 /* Required interrupt IN EP descriptor found */
- #define USBHOST_EPOUTFOUND 0x04 /* Optional interrupt OUT EP descriptor found */
- #define USBHOST_RQDFOUND (USBHOST_IFFOUND|USBHOST_EPINFOUND)
- #define USBHOST_ALLFOUND (USBHOST_RQDFOUND|USBHOST_EPOUTFOUND)
- #define USBHOST_MAX_CREFS 0x7fff
- /* Debug ********************************************************************/
- /* Both CONFIG_DEBUG_INPUT and CONFIG_DEBUG_USB could apply to this file.
- * We assume here that CONFIG_DEBUG_INPUT might be enabled separately, but
- * CONFIG_DEBUG_USB implies both.
- */
- #ifndef CONFIG_DEBUG_INPUT
- # undef ierr
- # define ierr uerr
- # undef iinfo
- # define iinfo uinfo
- #endif
- /****************************************************************************
- * Private Types
- ****************************************************************************/
- /* This structure contains the internal, private state of the USB host
- * keyboard 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 o the keyboard class driver */
- char devchar; /* Character identifying the /dev/kbd[n] device */
- volatile bool disconnected; /* TRUE: Device has been disconnected */
- volatile bool polling; /* TRUE: Poll thread is running */
- volatile bool open; /* TRUE: The keyboard device is open */
- volatile bool waiting; /* TRUE: waiting for keyboard data */
- uint8_t ifno; /* Interface number */
- int16_t crefs; /* Reference count on the driver instance */
- sem_t exclsem; /* Used to maintain mutual exclusive access */
- sem_t waitsem; /* Used to wait for keyboard data */
- FAR uint8_t *tbuffer; /* The allocated transfer buffer */
- size_t tbuflen; /* Size of the allocated transfer buffer */
- pid_t pollpid; /* PID of the poll task */
- struct work_s work; /* For cornercase error handling by the worker thread */
- /* Endpoints:
- * EP0 (Control):
- * - Receiving and responding to requests for USB control and class data.
- * - IN data when polled by the HID class driver (Get_Report)
- * - OUT data from the host.
- * EP Interrupt IN:
- * - Receiving asynchronous (unrequested) IN data from the device.
- * EP Interrupt OUT (optional):
- * - Transmitting low latency OUT data to the device.
- * - If not present, EP0 used.
- */
- usbhost_ep_t epin; /* Interrupt IN endpoint */
- usbhost_ep_t epout; /* Optional interrupt OUT endpoint */
- /* The following is a list if poll structures of threads waiting for
- * driver events. The 'struct pollfd' reference for each open is also
- * retained in the f_priv field of the 'struct file'.
- */
- struct pollfd *fds[CONFIG_HIDKBD_NPOLLWAITERS];
- /* Buffer used to collect and buffer incoming keyboard characters */
- volatile uint16_t headndx; /* Buffer head index */
- volatile uint16_t tailndx; /* Buffer tail index */
- uint8_t kbdbuffer[CONFIG_HIDKBD_BUFSIZE];
- };
- /* This type is used for encoding special characters */
- #ifdef CONFIG_HIDKBD_ENCODED
- struct usbhost_outstream_s
- {
- struct lib_outstream_s stream;
- FAR struct usbhost_state_s *priv;
- };
- #endif
- /****************************************************************************
- * 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);
- /* Polling support */
- static void usbhost_pollnotify(FAR struct usbhost_state_s *dev);
- /* 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);
- /* Keyboard polling thread */
- static void usbhost_destroy(FAR void *arg);
- static void usbhost_putbuffer(FAR struct usbhost_state_s *priv,
- uint8_t keycode);
- #ifdef CONFIG_HIDKBD_ENCODED
- static void usbhost_putstream(FAR struct lib_outstream_s *this, int ch);
- #endif
- static inline uint8_t usbhost_mapscancode(uint8_t scancode,
- uint8_t modifier);
- #ifdef CONFIG_HIDKBD_ENCODED
- static inline void usbhost_encodescancode(FAR struct usbhost_state_s *priv,
- uint8_t scancode, uint8_t modifier);
- #endif
- static int usbhost_kbdpoll(int argc, char *argv[]);
- /* Helpers for usbhost_connect() */
- static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv,
- FAR const uint8_t *configdesc, int desclen);
- static inline int usbhost_devinit(FAR struct usbhost_state_s *priv);
- /* (Little Endian) Data helpers */
- static inline uint16_t usbhost_getle16(const uint8_t *val);
- static inline void usbhost_putle16(uint8_t *dest, uint16_t val);
- static inline uint32_t usbhost_getle32(const uint8_t *val);
- #if 0 /* Not used */
- static void usbhost_putle32(uint8_t *dest, uint32_t val);
- #endif
- /* Transfer descriptor memory management */
- static inline int usbhost_tdalloc(FAR struct usbhost_state_s *priv);
- static inline int usbhost_tdfree(FAR struct usbhost_state_s *priv);
- /* struct usbhost_registry_s methods */
- static struct usbhost_class_s *usbhost_create(
- FAR struct usbhost_hubport_s *hport,
- FAR const struct usbhost_id_s *id);
- /* struct usbhost_class_s methods */
- static int usbhost_connect(FAR struct usbhost_class_s *usbclass,
- FAR const uint8_t *configdesc, int desclen);
- static int usbhost_disconnected(FAR struct usbhost_class_s *usbclass);
- /* Driver methods. We export the keyboard as a standard character driver */
- static int usbhost_open(FAR struct file *filep);
- static int usbhost_close(FAR struct file *filep);
- static ssize_t usbhost_read(FAR struct file *filep,
- FAR char *buffer, size_t len);
- static ssize_t usbhost_write(FAR struct file *filep,
- FAR const char *buffer, size_t len);
- static int usbhost_poll(FAR struct file *filep, FAR struct pollfd *fds,
- bool setup);
- /****************************************************************************
- * Private Data
- ****************************************************************************/
- /* This structure provides the registry entry ID information that will be
- * used to associate the USB host keyboard class driver to a connected USB
- * device.
- */
- static const struct usbhost_id_s g_hidkbd_id =
- {
- USB_CLASS_HID, /* base */
- USBHID_SUBCLASS_BOOTIF, /* subclass */
- USBHID_PROTOCOL_KEYBOARD, /* proto */
- 0, /* vid */
- 0 /* pid */
- };
- /* This is the USB host storage class's registry entry */
- static struct usbhost_registry_s g_hidkbd =
- {
- NULL, /* flink */
- usbhost_create, /* create */
- 1, /* nids */
- &g_hidkbd_id /* id[] */
- };
- static const struct file_operations g_hidkbd_fops =
- {
- usbhost_open, /* open */
- usbhost_close, /* close */
- usbhost_read, /* read */
- usbhost_write, /* write */
- NULL, /* seek */
- NULL, /* ioctl */
- usbhost_poll /* poll */
- };
- /* This is a bitmap that is used to allocate device names /dev/kbda-z. */
- static uint32_t g_devinuse;
- /* The following are used to managed the class creation operation */
- static sem_t g_exclsem; /* For mutually exclusive thread creation */
- static sem_t g_syncsem; /* Thread data passing interlock */
- static struct usbhost_state_s *g_priv; /* Data passed to thread */
- /* The following tables map keyboard scan codes to printable ASIC
- * characters. There is no support here for function keys or cursor
- * controls.
- */
- #ifndef CONFIG_HIDKBD_RAWSCANCODES
- #ifdef CONFIG_HIDKBD_ENCODED
- /* The first and last scancode values with encode-able values */
- #define FIRST_ENCODING USBHID_KBDUSE_ENTER /* 0x28 Keyboard Return (ENTER) */
- #ifndef CONFIG_HIDKBD_ALLSCANCODES
- # define LAST_ENCODING USBHID_KBDUSE_POWER /* 0x66 Keyboard Power */
- #else
- # define LAST_ENCODING USBHID_KBDUSE_KPDHEXADECIMAL /* 0xdd Keypad Hexadecimal */
- #endif
- #define USBHID_NUMENCODINGS (LAST_ENCODING - FIRST_ENCODING + 1)
- static const uint8_t encoding[USBHID_NUMENCODINGS] =
- {
- /* 0x28-0x2f: Enter,escape,del,back-tab,space,_,+,{ */
- KEYCODE_ENTER, 0,
- KEYCODE_FWDDEL, KEYCODE_BACKDEL,
- 0, 0,
- 0, 0,
- /* 0x30-0x37: },|,Non-US tilde,:,",grave tilde,<,> */
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- /* 0x38-0x3f: /,CapsLock,F1,F2,F3,F4,F5,F6 */
- 0, KEYCODE_CAPSLOCK,
- KEYCODE_F1, KEYCODE_F2,
- KEYCODE_F3, KEYCODE_F4,
- KEYCODE_F5, KEYCODE_F6,
- /* 0x40-0x47: F7,F8,F9,F10,F11,F12,PrtScn,ScrollLock */
- KEYCODE_F7, KEYCODE_F8,
- KEYCODE_F9, KEYCODE_F10,
- KEYCODE_F11, KEYCODE_F12,
- KEYCODE_PRTSCRN, KEYCODE_SCROLLLOCK,
- /* 0x48-0x4f: Pause,Insert,Home,PageUp,
- * DeleteForward,End,PageDown,RightArrow
- */
- KEYCODE_PAUSE, KEYCODE_INSERT,
- KEYCODE_HOME, KEYCODE_PAGEUP,
- KEYCODE_FWDDEL, KEYCODE_END,
- KEYCODE_PAGEDOWN, KEYCODE_RIGHT,
- /* 0x50-0x57: LeftArrow,DownArrow,UpArrow,Num Lock,/,*,-,+ */
- KEYCODE_LEFT, KEYCODE_DOWN,
- KEYCODE_UP, KEYCODE_NUMLOCK,
- 0, 0,
- 0, 0,
- /* 0x58-0x5f: Enter,1-7 */
- KEYCODE_ENTER, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- /* 0x60-0x66: 8-9,0,.,Non-US \,Application,Power */
- 0, 0,
- 0, 0,
- 0, 0,
- KEYCODE_POWER,
- #ifdef CONFIG_HIDKBD_ALLSCANCODES
- 0, /* 0x67 = */
- /* 0x68-0x6f: F13,F14,F15,F16,F17,F18,F19,F20 */
- KEYCODE_F13, KEYCODE_F14,
- KEYCODE_F15, KEYCODE_F16,
- KEYCODE_F17, KEYCODE_F18,
- KEYCODE_F19, KEYCODE_F20,
- /* 0x70-0x77: F21,F22,F23,F24,Execute,Help,Menu,Select */
- KEYCODE_F21, KEYCODE_F22,
- KEYCODE_F23, KEYCODE_F24,
- KEYCODE_EXECUTE, KEYCODE_HELP,
- KEYCODE_MENU, KEYCODE_SELECT,
- /* 0x78-0x7f: Stop,Again,Undo,Cut,Copy,Paste,Find,Mute */
- KEYCODE_STOP, KEYCODE_AGAIN,
- KEYCODE_UNDO, KEYCODE_CUT,
- KEYCODE_COPY, KEYCODE_PASTE,
- KEYCODE_FIND, KEYCODE_MUTE,
- /* 0x80-0x87: VolUp,VolDown,LCapsLock,lNumLock,LScrollLock,,,
- * =,International1
- */
- KEYCODE_VOLUP, KEYCODE_VOLDOWN,
- KEYCODE_LCAPSLOCK, KEYCODE_LNUMLOCK,
- KEYCODE_LSCROLLLOCK, 0,
- 0, 0,
- /* 0x88-0x8f: International 2-9 */
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- /* 0x90-0x97: LAN 1-8 */
- KEYCODE_LANG1, KEYCODE_LANG2,
- KEYCODE_LANG3, KEYCODE_LANG4,
- KEYCODE_LANG5, KEYCODE_LANG6,
- KEYCODE_LANG7, KEYCODE_LANG8,
- /* 0x98-0x9f: LAN 9,Erase,SysReq,Cancel,Clear,Prior,Return,Separator */
- 0, 0,
- KEYCODE_SYSREQ, KEYCODE_CANCEL,
- KEYCODE_CLEAR, 0,
- KEYCODE_ENTER, 0,
- /* 0xa0-0xa7: Out,Oper,Clear,CrSel,Excel,(reserved) */
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- /* 0xa8-0xaf: (reserved) */
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- /* 0xb0-0xb7: 00,000,ThouSeparator,DecSeparator,CurrencyUnit,SubUnit,(,) */
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- /* 0xb8-0xbf: {,},tab,backspace,A-D */
- 0, 0,
- 0, KEYCODE_BACKDEL,
- 0, 0,
- 0, 0,
- /* 0xc0-0xc7: E-F,XOR,^,%,<,>,& */
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- /* 0xc8-0xcf: &&,|,||,:,#, ,@,! */
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- /* 0xd0-0xd7: Memory Store,Recall,Clear,Add,Subtract,Muliply,Divide,+/- */
- KEYCODE_MEMSTORE, KEYCODE_MEMRECALL,
- KEYCODE_MEMCLEAR, KEYCODE_MEMADD,
- KEYCODE_MEMSUB, KEYCODE_MEMMUL,
- KEYCODE_MEMDIV, KEYCODE_NEGATE,
- /* 0xd8-0xdd: Clear,ClearEntry,Binary,Octal,Decimal,Hexadecimal */
- KEYCODE_CLEAR, KEYCODE_CLEARENTRY,
- KEYCODE_BINARY, KEYCODE_OCTAL,
- KEYCODE_DECIMAL, KEYCODE_HEXADECIMAL
- #endif
- };
- #endif
- static const uint8_t ucmap[USBHID_NUMSCANCODES] =
- {
- 0, 0, 0, 0, 'A', 'B', 'C', 'D', /* 0x00-0x07: Reserved, errors, A-D */
- 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', /* 0x08-0x0f: E-L */
- 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', /* 0x10-0x17: M-T */
- 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@', /* 0x18-0x1f: U-Z,!,@ */
- '#', '$', '%', '^', '&', '*', '(', ')', /* 0x20-0x27: #,$,%,^,&,*,(,) */
- '\n', '\033', '\177', 0, ' ', '_', '+', '{', /* 0x28-0x2f: Enter,escape,del,back-tab,space,_,+,{ */
- '}', '|', 0, ':', '"', '~', '<', '>', /* 0x30-0x37: },|,Non-US tilde,:,",grave tilde,<,> */
- '?', 0, 0, 0, 0, 0, 0, 0, /* 0x38-0x3f: /,CapsLock,F1,F2,F3,F4,F5,F6 */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40-0x47: F7,F8,F9,F10,F11,F12,PrtScn,ScrollLock */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x48-0x4f: Pause,Insert,Home,PageUp,DeleteForward,End,PageDown,RightArrow */
- 0, 0, 0, 0, '/', '*', '-', '+', /* 0x50-0x57: LeftArrow,DownArrow,UpArrow,Num Lock,/,*,-,+ */
- '\n', '1', '2', '3', '4', '5', '6', '7', /* 0x58-0x5f: Enter,1-7 */
- '8', '9', '0', '.', 0, 0, 0, '=', /* 0x60-0x67: 8-9,0,.,Non-US \,Application,Power,= */
- #ifdef CONFIG_HIDKBD_ALLSCANCODES
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x68-0x6f: F13,F14,F15,F16,F17,F18,F19,F20 */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77: F21,F22,F23,F24,Execute,Help,Menu,Select */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x78-0x7f: Stop,Again,Undo,Cut,Copy,Paste,Find,Mute */
- 0, 0, 0, 0, 0, ',', 0, 0, /* 0x80-0x87: VolUp,VolDown,LCapsLock,lNumLock,LScrollLock,,,=,International1 */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x88-0x8f: International 2-9 */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90-0x97: LAN 1-8 */
- 0, 0, 0, 0, 0, 0, '\n', 0, /* 0x98-0x9f: LAN 9,Erase,SysReq,Cancel,Clear,Prior,Return,Separator */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0-0xa7: Out,Oper,Clear,CrSel,Excel,(reserved) */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa8-0xaf: (reserved) */
- 0, 0, 0, 0, 0, 0, '(', ')', /* 0xb0-0xb7: 00,000,ThouSeparator,DecSeparator,CurrencyUnit,SubUnit,(,) */
- '{', '}', '\t', \177, 'A', 'B', 'C', 'D', /* 0xb8-0xbf: {,},tab,backspace,A-D */
- 'F', 'F', 0, '^', '%', '<', '>', '&', /* 0xc0-0xc7: E-F,XOR,^,%,<,>,& */
- 0, '|', 0, ':', '%', ' ', '@', '!', /* 0xc8-0xcf: &&,|,||,:,#, ,@,! */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0-0xd7: Memory Store,Recall,Clear,Add,Subtract,Muliply,Divide,+/- */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd8-0xdf: Clear,ClearEntry,Binary,Octal,Decimal,Hexadecimal */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0-0xe7: Left Ctrl,Shift,Alt,GUI, Right Ctrl,Shift,Alt,GUI */
- #endif
- };
- static const uint8_t lcmap[USBHID_NUMSCANCODES] =
- {
- 0, 0, 0, 0, 'a', 'b', 'c', 'd', /* 0x00-0x07: Reserved, errors, a-d */
- 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', /* 0x08-0x0f: e-l */
- 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', /* 0x10-0x17: m-t */
- 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', /* 0x18-0x1f: u-z,1-2 */
- '3', '4', '5', '6', '7', '8', '9', '0', /* 0x20-0x27: 3-9,0 */
- '\n', '\033', '\177', '\t', ' ', '-', '=', '[', /* 0x28-0x2f: Enter,escape,del,tab,space,-,=,[ */
- ']', '\\', '\234', ';', '\'', '`', ',', '.', /* 0x30-0x37: ],\,Non-US pound,;,',grave accent,,,. */
- '/', 0, 0, 0, 0, 0, 0, 0, /* 0x38-0x3f: /,CapsLock,F1,F2,F3,F4,F5,F6 */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40-0x47: F7,F8,F9,F10,F11,F12,PrtScn,ScrollLock */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x48-0x4f: Pause,Insert,Home,PageUp,DeleteForward,End,PageDown,RightArrow */
- 0, 0, 0, 0, '/', '*', '-', '+', /* 0x50-0x57: LeftArrow,DownArrow,UpArrow,Num Lock,/,*,-,+ */
- '\n', '1', '2', '3', '4', '5', '6', '7', /* 0x58-0x5f: Enter,1-7 */
- '8', '9', '0', '.', 0, 0, 0, '=', /* 0x60-0x67: 8-9,0,.,Non-US \,Application,Power,= */
- #ifdef CONFIG_HIDKBD_ALLSCANCODES
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x68-0x6f: F13,F14,F15,F16,F17,F18,F19,F20 */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77: F21,F22,F23,F24,Execute,Help,Menu,Select */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x78-0x7f: Stop,Again,Undo,Cut,Copy,Paste,Find,Mute */
- 0, 0, 0, 0, 0, ',', 0, 0, /* 0x80-0x87: VolUp,VolDown,LCapsLock,lNumLock,LScrollLock,,,=,International1 */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x88-0x8f: International 2-9 */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90-0x97: LAN 1-8 */
- 0, 0, 0, 0, 0, 0, '\n', 0, /* 0x98-0x9f: LAN 9,Erase,SysReq,Cancel,Clear,Prior,Return,Separator */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0-0xa7: Out,Oper,Clear,CrSel,Excel,(reserved) */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa8-0xaf: (reserved) */
- 0, 0, 0, 0, 0, 0, '(', ')', /* 0xb0-0xb7: 00,000,ThouSeparator,DecSeparator,CurrencyUnit,SubUnit,(,) */
- '{', '}', '\t', '\177', 'A', 'B', 'C', 'D', /* 0xb8-0xbf: {,},tab,backspace,A-D */
- 'F', 'F', 0, '^', '%', '<', '>', '&', /* 0xc0-0xc7: E-F,XOR,^,%,<,>,& */
- 0, '|', 0, ':', '%', ' ', '@', '!', /* 0xc8-0xcf: &&,|,||,:,#, ,@,! */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0-0xd7: Memory Store,Recall,Clear,Add,Subtract,Muliply,Divide,+/- */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd8-0xdf: Clear,ClearEntry,Binary,Octal,Decimal,Hexadecimal */
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0-0xe7: Left Ctrl,Shift,Alt,GUI, Right Ctrl,Shift,Alt,GUI */
- #endif
- };
- #endif /* CONFIG_HIDKBD_RAWSCANCODES */
- /****************************************************************************
- * 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_pollnotify
- ****************************************************************************/
- static void usbhost_pollnotify(FAR struct usbhost_state_s *priv)
- {
- int i;
- for (i = 0; i < CONFIG_HIDKBD_NPOLLWAITERS; i++)
- {
- struct pollfd *fds = priv->fds[i];
- if (fds)
- {
- fds->revents |= (fds->events & POLLIN);
- if (fds->revents != 0)
- {
- uinfo("Report events: %02x\n", fds->revents);
- nxsem_post(fds->sem);
- }
- }
- }
- }
- /****************************************************************************
- * Name: usbhost_allocclass
- *
- * Description:
- * This is really part of the logic that implements the create() method
- * of struct usbhost_registry_s. This function allocates memory for one
- * new class instance.
- *
- * Input Parameters:
- * None
- *
- * Returned Value:
- * On success, this function will return a non-NULL instance of struct
- * usbhost_class_s. NULL is returned on failure; this function will
- * will fail only if there are insufficient resources to create another
- * USB host class instance.
- *
- ****************************************************************************/
- static inline FAR struct usbhost_state_s *usbhost_allocclass(void)
- {
- FAR struct usbhost_state_s *priv;
- DEBUGASSERT(!up_interrupt_context());
- priv = (FAR struct usbhost_state_s *)
- kmm_malloc(sizeof(struct usbhost_state_s));
- uinfo("Allocated: %p\n", priv);
- return priv;
- }
- /****************************************************************************
- * Name: usbhost_freeclass
- *
- * Description:
- * Free a class instance previously allocated by usbhost_allocclass().
- *
- * Input Parameters:
- * usbclass - A reference to the class instance to be freed.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static inline void usbhost_freeclass(FAR struct usbhost_state_s *usbclass)
- {
- DEBUGASSERT(usbclass != NULL);
- /* Free the class instance. */
- uinfo("Freeing: %p\n", usbclass);
- kmm_free(usbclass);
- }
- /****************************************************************************
- * Name: Device name management
- *
- * Description:
- * Some tiny functions to coordinate management of device names.
- *
- ****************************************************************************/
- static int usbhost_allocdevno(FAR struct usbhost_state_s *priv)
- {
- irqstate_t flags;
- int devno;
- flags = enter_critical_section();
- for (devno = 0; devno < 26; devno++)
- {
- uint32_t bitno = 1 << devno;
- if ((g_devinuse & bitno) == 0)
- {
- g_devinuse |= bitno;
- priv->devchar = 'a' + devno;
- leave_critical_section(flags);
- return OK;
- }
- }
- leave_critical_section(flags);
- return -EMFILE;
- }
- static void usbhost_freedevno(FAR struct usbhost_state_s *priv)
- {
- int devno = 'a' - priv->devchar;
- if (devno >= 0 && devno < 26)
- {
- irqstate_t flags = enter_critical_section();
- g_devinuse &= ~(1 << devno);
- leave_critical_section(flags);
- }
- }
- static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv,
- FAR char *devname)
- {
- snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->devchar);
- }
- /****************************************************************************
- * Name: usbhost_destroy
- *
- * Description:
- * The USB device has been disconnected and the reference count on the USB
- * host class instance has gone to 1.. Time to destroy the USB host class
- * instance.
- *
- * Input Parameters:
- * arg - A reference to the class instance to be destroyed.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static void usbhost_destroy(FAR void *arg)
- {
- FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)arg;
- FAR struct usbhost_hubport_s *hport;
- char devname[DEV_NAMELEN];
- DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL);
- hport = priv->usbclass.hport;
- uinfo("crefs: %d\n", priv->crefs);
- /* Unregister the driver */
- uinfo("Unregister driver\n");
- usbhost_mkdevname(priv, devname);
- unregister_driver(devname);
- /* Release the device name used by this connection */
- usbhost_freedevno(priv);
- /* Free the interrupt endpoints */
- if (priv->epin)
- {
- DRVR_EPFREE(hport->drvr, priv->epin);
- }
- if (priv->epout)
- {
- DRVR_EPFREE(hport->drvr, priv->epout);
- }
- /* Free any transfer buffers */
- usbhost_tdfree(priv);
- /* Destroy the semaphores */
- nxsem_destroy(&priv->exclsem);
- nxsem_destroy(&priv->waitsem);
- /* 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_putbuffer
- *
- * Description:
- * Add one character to the user buffer.
- *
- * Input Parameters:
- * priv - Driver internal state
- * keycode - The value to add to the user buffer
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static void usbhost_putbuffer(FAR struct usbhost_state_s *priv,
- uint8_t keycode)
- {
- register unsigned int head;
- register unsigned int tail;
- /* Copy the next keyboard character into the user buffer. */
- head = priv->headndx;
- priv->kbdbuffer[head] = keycode;
- /* Increment the head index */
- if (++head >= CONFIG_HIDKBD_BUFSIZE)
- {
- head = 0;
- }
- /* If the buffer is full, then increment the tail index to make space. Is
- * it better to lose old keystrokes or new?
- */
- tail = priv->tailndx;
- if (tail == head)
- {
- if (++tail >= CONFIG_HIDKBD_BUFSIZE)
- {
- tail = 0;
- }
- /* Save the updated tail index */
- priv->tailndx = tail;
- }
- /* Save the updated head index */
- priv->headndx = head;
- }
- /****************************************************************************
- * Name: usbhost_putstream
- *
- * Description:
- * A wrapper for usbhost_putc that is compatible with the lib_outstream_s
- * putc methods.
- *
- * Input Parameters:
- * stream - The struct lib_outstream_s reference
- * ch - The character to add to the user buffer
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- #ifdef CONFIG_HIDKBD_ENCODED
- static void usbhost_putstream(FAR struct lib_outstream_s *stream, int ch)
- {
- FAR struct usbhost_outstream_s *privstream =
- (FAR struct usbhost_outstream_s *)stream;
- DEBUGASSERT(privstream && privstream->priv);
- usbhost_putbuffer(privstream->priv, (uint8_t)ch);
- stream->nput++;
- }
- #endif
- /****************************************************************************
- * Name: usbhost_mapscancode
- *
- * Description:
- * Map a keyboard scancode to a printable ASCII character. There is no
- * support here for function keys or cursor controls in this version of
- * the driver.
- *
- * Input Parameters:
- * scancode - Scan code to be mapped.
- * modifier - Ctrl,Alt,Shift,GUI modifier bits
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static inline uint8_t usbhost_mapscancode(uint8_t scancode, uint8_t modifier)
- {
- #ifndef CONFIG_HIDKBD_RAWSCANCODES
- /* Range check */
- if (scancode >= USBHID_NUMSCANCODES)
- {
- return 0;
- }
- /* Is either shift key pressed? */
- if ((modifier & (USBHID_MODIFER_LSHIFT | USBHID_MODIFER_RSHIFT)) != 0)
- {
- return ucmap[scancode];
- }
- else
- {
- return lcmap[scancode];
- }
- #else
- return scancode;
- #endif
- }
- /****************************************************************************
- * Name: usbhost_encodescancode
- *
- * Description:
- * Check if the key has a special function encoding and, if it does, add
- * the encoded value to the user buffer.
- *
- * Input Parameters:
- * priv - Driver internal state
- * scancode - Scan code to be mapped.
- * modifier - Ctrl, Alt, Shift, GUI modifier bits
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- #ifdef CONFIG_HIDKBD_ENCODED
- static inline void usbhost_encodescancode(FAR struct usbhost_state_s *priv,
- uint8_t scancode, uint8_t modifier)
- {
- uint8_t encoded;
- /* Check if the raw scancode is in a valid range */
- if (scancode >= FIRST_ENCODING && scancode <= LAST_ENCODING)
- {
- /* Yes the value is within range */
- encoded = encoding[scancode - FIRST_ENCODING];
- iinfo(" scancode: %02x modifier: %02x encoded: %d\n",
- scancode, modifier, encoded);
- if (encoded)
- {
- struct usbhost_outstream_s usbstream;
- /* And it does correspond to a special function key */
- usbstream.stream.put = usbhost_putstream;
- usbstream.stream.nput = 0;
- usbstream.priv = priv;
- /* Add the special function value to the user buffer */
- kbd_specpress((enum kbd_keycode_e)encoded,
- (FAR struct lib_outstream_s *)&usbstream);
- }
- }
- }
- #endif
- /****************************************************************************
- * Name: usbhost_kbdpoll
- *
- * Description:
- * Periodically check for new keyboard data.
- *
- * Input Parameters:
- * arg - A reference to the class instance to be destroyed.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static int usbhost_kbdpoll(int argc, char *argv[])
- {
- FAR struct usbhost_state_s *priv;
- FAR struct usbhost_hubport_s *hport;
- FAR struct usb_ctrlreq_s *ctrlreq;
- irqstate_t flags;
- #ifndef CONFIG_HIDKBD_NODEBOUNCE
- uint8_t lastkey[6] =
- {
- 0, 0, 0, 0, 0, 0
- };
- #endif
- #if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_INFO)
- unsigned int npolls = 0;
- #endif
- unsigned int nerrors = 0;
- useconds_t delay;
- bool empty = true;
- bool newstate;
- int ret;
- uinfo("Started\n");
- /* Synchronize with the start-up logic. Get the private instance, re-start
- * the start-up logic, and wait a bit to make sure that all of the class
- * creation logic has a chance to run to completion.
- *
- * NOTE: that the reference count is *not* incremented here. When the
- * driver structure was created, it was created with a reference count of
- * one. This thread is responsible for that count. The count will be
- * decrement when this thread exits.
- */
- priv = g_priv;
- DEBUGASSERT(priv != NULL && priv->usbclass.hport);
- hport = priv->usbclass.hport;
- priv->polling = true;
- usbhost_givesem(&g_syncsem);
- nxsig_sleep(1);
- /* Loop here until the device is disconnected */
- uinfo("Entering poll loop\n");
- while (!priv->disconnected)
- {
- /* Make sure that we have exclusive access to the private data
- * structure. There may now be other tasks with the character driver
- * open and actively trying to interact with the class driver.
- */
- ret = usbhost_takesem(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* Format the HID report request:
- *
- * bmRequestType 10100001
- * bRequest GET_REPORT (0x01)
- * wValue Report Type and Report Index
- * wIndex Interface Number
- * wLength Descriptor Length
- * Data Descriptor Data
- */
- ctrlreq = (struct usb_ctrlreq_s *)priv->tbuffer;
- ctrlreq->type = USB_REQ_DIR_IN | USB_REQ_TYPE_CLASS |
- USB_REQ_RECIPIENT_INTERFACE;
- ctrlreq->req = USBHID_REQUEST_GETREPORT;
- usbhost_putle16(ctrlreq->value, (USBHID_REPORTTYPE_INPUT << 8));
- usbhost_putle16(ctrlreq->index, priv->ifno);
- usbhost_putle16(ctrlreq->len, sizeof(struct usbhid_kbdreport_s));
- /* Send HID report request */
- ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, priv->tbuffer);
- usbhost_givesem(&priv->exclsem);
- /* Check for errors -- Bail if an excessive number of consecutive
- * errors are encountered.
- */
- if (ret < 0)
- {
- nerrors++;
- uerr("ERROR: GETREPORT/INPUT, DRVR_CTRLIN returned: %d/%d\n",
- ret, nerrors);
- if (nerrors > 200)
- {
- uerr(" Too many errors... aborting: %d\n", nerrors);
- break;
- }
- }
- /* The report was received correctly. But ignore the keystrokes if no
- * task has opened the driver.
- */
- else if (priv->open)
- {
- struct usbhid_kbdreport_s *rpt =
- (struct usbhid_kbdreport_s *)priv->tbuffer;
- uint8_t keycode;
- int i;
- /* Success, reset the error counter */
- nerrors = 0;
- /* Add the newly received keystrokes to our internal buffer */
- ret = usbhost_takesem(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- for (i = 0; i < 6; i++)
- {
- /* Is this key pressed? But not pressed last time?
- * HID spec: "The order of keycodes in array fields has no
- * significance. Order determination is done by the host
- * software comparing the contents of the previous report to
- * the current report. If two or more keys are reported in
- * one report, their order is indeterminate. Keyboards may
- * buffer events that would have otherwise resulted in
- * multiple event in a single report.
- *
- * "'Repeat Rate' and 'Delay Before First Repeat' are
- * implemented by the host and not in the keyboard (this
- * means the BIOS in legacy mode). The host may use the
- * device report rate and the number of reports to determine
- * how long a key is being held down. Alternatively, the host
- * may use its own clock or the idle request for the timing
- * of these features."
- */
- if (rpt->key[i] != USBHID_KBDUSE_NONE
- #ifndef CONFIG_HIDKBD_NODEBOUNCE
- && rpt->key[i] != lastkey[0]
- && rpt->key[i] != lastkey[1]
- && rpt->key[i] != lastkey[2]
- && rpt->key[i] != lastkey[3]
- && rpt->key[i] != lastkey[4]
- && rpt->key[i] != lastkey[5]
- #endif
- )
- {
- /* Yes.. Add it to the buffer. */
- /* Map the keyboard scancode to a printable ASCII
- * character. There is no support here for function keys
- * or cursor controls in this version of the driver.
- */
- keycode = usbhost_mapscancode(rpt->key[i], rpt->modifier);
- iinfo("Key %d: %02x keycode:%c modifier: %02x\n",
- i, rpt->key[i], keycode ? keycode : ' ',
- rpt->modifier);
- /* Zero at this point means that the key does not map to a
- * printable character.
- */
- if (keycode != 0)
- {
- /* Handle control characters. Zero after this means
- * a valid, NUL character.
- */
- if ((rpt->modifier & (USBHID_MODIFER_LCTRL |
- USBHID_MODIFER_RCTRL)) != 0)
- {
- keycode &= 0x1f;
- }
- /* Copy the next keyboard character into the user
- * buffer.
- */
- usbhost_putbuffer(priv, keycode);
- }
- /* The zero might, however, map to a special keyboard
- * action (such as a cursor movement or function key).
- * Attempt to encode the special key.
- */
- #ifdef CONFIG_HIDKBD_ENCODED
- else
- {
- usbhost_encodescancode(priv, rpt->key[i],
- rpt->modifier);
- }
- #endif
- }
- /* Save the scancode (or lack thereof) for key debouncing on
- * next keyboard report.
- */
- #ifndef CONFIG_HIDKBD_NODEBOUNCE
- lastkey[i] = rpt->key[i];
- #endif
- }
- /* Is there data available? */
- newstate = (priv->headndx == priv->tailndx);
- if (!newstate)
- {
- /* Did we just transition from no data available to data
- * available? If so, wake up any threads waiting for the
- * POLLIN event.
- */
- if (empty)
- {
- usbhost_pollnotify(priv);
- }
- /* Yes.. Is there a thread waiting for keyboard data now? */
- if (priv->waiting)
- {
- /* Yes.. wake it up */
- usbhost_givesem(&priv->waitsem);
- priv->waiting = false;
- }
- }
- empty = newstate;
- usbhost_givesem(&priv->exclsem);
- }
- /* If USB debug is on, then provide some periodic indication that
- * polling is still happening.
- */
- #if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_INFO)
- npolls++;
- if ((npolls & 31) == 0)
- {
- uinfo("Still polling: %d\n", npolls);
- }
- #endif
- /* Wait for the required amount (or until a signal is received). We
- * will wake up when either the delay elapses or we are signalled that
- * the device has been disconnected.
- *
- * If we are getting errors, then sleep longer. In the event that
- * the keyboard is connected via a hub, there may be a significant
- * amount of time after the keyboard is removed before we are stopped.
- */
- if (nerrors > 1)
- {
- delay = nerrors * CONFIG_HIDKBD_POLLUSEC;
- }
- else
- {
- delay = CONFIG_HIDKBD_POLLUSEC;
- }
- nxsig_usleep(delay);
- }
- /* We get here when the driver is removed.. or when too many errors have
- * been encountered.
- *
- * Make sure that we have exclusive access to the private data structure.
- * There may now be other tasks with the character driver open and actively
- * trying to interact with the class driver.
- */
- usbhost_forcetake(&priv->exclsem);
- /* Indicate that we are no longer running and decrement the reference
- * count held by this thread. If there are no other users of the class,
- * we can destroy it now. Otherwise, we have to wait until the all
- * of the file descriptors are closed.
- */
- uinfo("Keyboard removed, polling halted\n");
- flags = enter_critical_section();
- priv->polling = false;
- /* Decrement the reference count held by this thread. */
- DEBUGASSERT(priv->crefs > 0);
- priv->crefs--;
- /* There are two possibilities:
- * 1) The reference count is greater than zero. This means that there
- * are still open references to the keyboard driver. In this case
- * we need to wait until usbhost_close() is called and all of the
- * open driver references are decremented. Then usbhost_destroy() can
- * be called from usbhost_close().
- * 2) The reference count is now zero. This means that there are no
- * further open references and we can call usbhost_destroy() now.
- */
- if (priv->crefs < 1)
- {
- /* Unregister the driver and destroy the instance (while we hold
- * the semaphore!)
- */
- usbhost_destroy(priv);
- }
- else
- {
- /* No, we will destroy the driver instance when it is final open
- * reference is closed
- */
- usbhost_givesem(&priv->exclsem);
- }
- leave_critical_section(flags);
- return 0;
- }
- /****************************************************************************
- * 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 epindesc;
- FAR struct usbhost_epdesc_s epoutdesc;
- int remaining;
- uint8_t found = 0;
- bool done = false;
- int ret;
- DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL &&
- configdesc != NULL && desclen >= sizeof(struct usb_cfgdesc_s));
- hport = priv->usbclass.hport;
- /* Keep the compiler from complaining about uninitialized variables */
- memset(&epindesc, 0, sizeof(struct usbhost_epdesc_s));
- memset(&epoutdesc, 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) && !done)
- {
- /* 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);
- /* Did we already find what we needed
- * from a preceding interface?
- */
- if ((found & USBHOST_RQDFOUND) == USBHOST_RQDFOUND)
- {
- /* Yes.. then break out of the loop and use the preceding
- * interface.
- */
- done = true;
- }
- else
- {
- /* Otherwise, save the interface number and discard any
- * endpoints previously found
- */
- priv->ifno = ifdesc->ifno;
- found = USBHOST_IFFOUND;
- }
- }
- break;
- /* HID descriptor */
- case USBHID_DESCTYPE_HID:
- uinfo("HID descriptor\n");
- break;
- /* Endpoint descriptor. We expect one or two interrupt endpoints,
- * a required IN endpoint and an optional OUT endpoint.
- */
- 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 an interrupt endpoint. */
- if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) ==
- USB_EP_ATTR_XFER_INT)
- {
- /* Yes.. it is a interrupt endpoint. IN or OUT? */
- if (USB_ISEPOUT(epdesc->addr))
- {
- /* It is an interrupt OUT endpoint. There not be more
- * than one interrupt OUT endpoint.
- */
- if ((found & USBHOST_EPOUTFOUND) != 0)
- {
- /* Oops.. more than one endpoint. We don't know what
- * to do with this.
- */
- return -EINVAL;
- }
- found |= USBHOST_EPOUTFOUND;
- /* Save the interrupt OUT endpoint information */
- epoutdesc.hport = hport;
- epoutdesc.addr = epdesc->addr &
- USB_EP_ADDR_NUMBER_MASK;
- epoutdesc.in = false;
- epoutdesc.xfrtype = USB_EP_ATTR_XFER_INT;
- epoutdesc.interval = epdesc->interval;
- epoutdesc.mxpacketsize =
- usbhost_getle16(epdesc->mxpacketsize);
- uinfo("Interrupt OUT EP addr:%d mxpacketsize:%d\n",
- epoutdesc.addr, epoutdesc.mxpacketsize);
- }
- else
- {
- /* It is an interrupt IN endpoint. There should be only
- * one interrupt IN endpoint.
- */
- if ((found & USBHOST_EPINFOUND) != 0)
- {
- /* Oops.. more than one endpoint. We don't know what
- * to do with this.
- */
- return -EINVAL;
- }
- found |= USBHOST_EPINFOUND;
- /* Save the interrupt IN endpoint information */
- epindesc.hport = hport;
- epindesc.addr = epdesc->addr &
- USB_EP_ADDR_NUMBER_MASK;
- epindesc.in = 1;
- epindesc.xfrtype = USB_EP_ATTR_XFER_INT;
- epindesc.interval = epdesc->interval;
- epindesc.mxpacketsize =
- usbhost_getle16(epdesc->mxpacketsize);
- uinfo("Interrupt IN EP addr:%d mxpacketsize:%d\n",
- epindesc.addr, epindesc.mxpacketsize);
- }
- }
- }
- break;
- /* Other descriptors are just ignored for now */
- default:
- uinfo("Other descriptor: %d\n", desc->type);
- break;
- }
- /* What we found everything that we are going to find? */
- if (found == USBHOST_ALLFOUND)
- {
- /* Yes.. then break out of the loop
- * and use the preceding interface
- */
- done = true;
- }
- /* Increment the address of the next descriptor */
- configdesc += desc->len;
- remaining -= desc->len;
- }
- /* Sanity checking... did we find all of things that we need? */
- if ((found & USBHOST_RQDFOUND) != USBHOST_RQDFOUND)
- {
- uerr("ERROR: Found IF:%s EPIN:%s\n",
- (found & USBHOST_IFFOUND) != 0 ? "YES" : "NO",
- (found & USBHOST_EPINFOUND) != 0 ? "YES" : "NO");
- return -EINVAL;
- }
- /* We are good... Allocate the endpoints. First, the required interrupt
- * IN endpoint.
- */
- ret = DRVR_EPALLOC(hport->drvr, &epindesc, &priv->epin);
- if (ret < 0)
- {
- uerr("ERROR: Failed to allocate interrupt IN endpoint\n");
- return ret;
- }
- /* Then the optional interrupt OUT endpoint */
- uinfo("Found EPOOUT:%s\n",
- (found & USBHOST_EPOUTFOUND) != 0 ? "YES" : "NO");
- if ((found & USBHOST_EPOUTFOUND) != 0)
- {
- ret = DRVR_EPALLOC(hport->drvr, &epoutdesc, &priv->epout);
- if (ret < 0)
- {
- uerr("ERROR: Failed to allocate interrupt OUT endpoint\n");
- DRVR_EPFREE(hport->drvr, priv->epin);
- return ret;
- }
- }
- uinfo("Endpoints allocated\n");
- return OK;
- }
- /****************************************************************************
- * Name: usbhost_devinit
- *
- * Description:
- * The USB device has been successfully connected. This completes the
- * initialization operations. It is first called after the
- * configuration descriptor has been received.
- *
- * This function is called from the connect() method. This function always
- * executes on the thread of the caller of connect().
- *
- * Input Parameters:
- * priv - A reference to the class instance.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static inline int usbhost_devinit(FAR struct usbhost_state_s *priv)
- {
- char devname[DEV_NAMELEN];
- int ret;
- /* Set aside a transfer buffer for exclusive
- * use by the keyboard class driver
- */
- ret = usbhost_tdalloc(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);
- /* Start a worker task to poll the USB device. It would be nice to use
- * the NuttX worker thread to do this, but this task needs to wait for
- * events and activities on the worker thread should not involve
- * significant waiting. Having a dedicated thread is more efficient in
- * this sense, but requires more memory resources, primarily for the
- * dedicated stack (CONFIG_HIDKBD_STACKSIZE).
- */
- uinfo("Start poll task\n");
- /* The inputs to a task started by kthread_create() are very awkard for
- * this purpose. They are really designed for command line tasks
- * (argc/argv). So the following is kludge pass binary data when the
- * keyboard poll task is started.
- *
- * First, make sure we have exclusive access to g_priv (what is the
- * likelihood of this being used? About zero, but we protect it anyway).
- */
- ret = usbhost_takesem(&g_exclsem);
- if (ret < 0)
- {
- usbhost_tdfree(priv);
- goto errout;
- }
- g_priv = priv;
- priv->pollpid = kthread_create("kbdpoll", CONFIG_HIDKBD_DEFPRIO,
- CONFIG_HIDKBD_STACKSIZE,
- (main_t)usbhost_kbdpoll,
- (FAR char * const *)NULL);
- if (priv->pollpid < 0)
- {
- /* Failed to started the poll thread...
- * probably due to memory resources
- */
- usbhost_givesem(&g_exclsem);
- ret = (int)priv->pollpid;
- goto errout;
- }
- /* Now wait for the poll task to get properly initialized */
- ret = usbhost_takesem(&g_syncsem);
- usbhost_givesem(&g_exclsem);
- if (ret < 0)
- {
- goto errout;
- }
- /* Register the driver */
- uinfo("Register driver\n");
- usbhost_mkdevname(priv, devname);
- ret = register_driver(devname, &g_hidkbd_fops, 0666, priv);
- /* We now have to be concerned about asynchronous modification of crefs
- * because the driver has been registered.
- */
- errout:
- usbhost_forcetake(&priv->exclsem);
- priv->crefs--;
- usbhost_givesem(&priv->exclsem);
- return ret;
- }
- /****************************************************************************
- * Name: usbhost_getle16
- *
- * Description:
- * Get a (possibly unaligned) 16-bit little endian value.
- *
- * Input Parameters:
- * val - A pointer to the first byte of the little endian value.
- *
- * Returned Value:
- * A uint16_t representing the whole 16-bit integer value
- *
- ****************************************************************************/
- static inline uint16_t usbhost_getle16(const uint8_t *val)
- {
- return (uint16_t)val[1] << 8 | (uint16_t)val[0];
- }
- /****************************************************************************
- * Name: usbhost_putle16
- *
- * Description:
- * Put a (possibly unaligned) 16-bit little endian value.
- *
- * Input Parameters:
- * dest - A pointer to the first byte to save the little endian value.
- * val - The 16-bit value to be saved.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static void usbhost_putle16(uint8_t *dest, uint16_t val)
- {
- dest[0] = val & 0xff; /* Little endian means LS byte first in byte stream */
- dest[1] = val >> 8;
- }
- /****************************************************************************
- * Name: usbhost_getle32
- *
- * Description:
- * Get a (possibly unaligned) 32-bit little endian value.
- *
- * Input Parameters:
- * dest - A pointer to the first byte to save the big endian value.
- * val - The 32-bit value to be saved.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static inline uint32_t usbhost_getle32(const uint8_t *val)
- {
- /* Little endian means LS halfword first in byte stream */
- return (uint32_t)usbhost_getle16(&val[2]) << 16 |
- (uint32_t)usbhost_getle16(val);
- }
- /****************************************************************************
- * Name: usbhost_putle32
- *
- * Description:
- * Put a (possibly unaligned) 32-bit little endian value.
- *
- * Input Parameters:
- * dest - A pointer to the first byte to save the little endian value.
- * val - The 32-bit value to be saved.
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- #if 0 /* Not used */
- 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_tdalloc
- *
- * 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 int usbhost_tdalloc(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_tdfree
- *
- * 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 int usbhost_tdfree(FAR struct usbhost_state_s *priv)
- {
- FAR struct usbhost_hubport_s *hport;
- int result = OK;
- DEBUGASSERT(priv);
- if (priv->tbuffer)
- {
- DEBUGASSERT(priv->usbclass.hport);
- hport = priv->usbclass.hport;
- result = DRVR_FREE(hport->drvr, priv->tbuffer);
- priv->tbuffer = NULL;
- priv->tbuflen = 0;
- }
- return result;
- }
- /****************************************************************************
- * struct usbhost_registry_s methods
- ****************************************************************************/
- /****************************************************************************
- * Name: usbhost_create
- *
- * Description:
- * This function implements the create() method of struct
- * usbhost_registry_s. The create() method is a callback into the class
- * implementation. It is used to (1) create a new instance of the USB
- * host class state and to (2) bind a USB host driver "session" to the
- * class instance. Use of this create() method will support environments
- * where there may be multiple USB ports and multiple USB devices
- * simultaneously connected.
- *
- * Input Parameters:
- * hport - The hub 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 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's usbhost_kbdpoll() task.
- */
- priv->crefs = 1;
- /* Initialize semaphores */
- nxsem_init(&priv->exclsem, 0, 1);
- nxsem_init(&priv->waitsem, 0, 0);
- /* The waitsem semaphore is used for signaling and, hence, should
- * not have priority inheritance enabled.
- */
- nxsem_set_protocol(&priv->waitsem, SEM_PRIO_NONE);
- /* Return the instance of the USB keyboard class driver */
- 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 endpoints */
- ret = usbhost_cfgdesc(priv, configdesc, desclen);
- if (ret < 0)
- {
- uerr("ERROR: usbhost_cfgdesc() failed: %d\n", ret);
- }
- else
- {
- /* Now configure the device and register the NuttX driver */
- ret = usbhost_devinit(priv);
- if (ret < 0)
- {
- uerr("ERROR: usbhost_devinit() failed: %d\n", ret);
- }
- }
- /* ERROR handling: Do nothing. If we return and error during connection,
- * the driver is required to call the DISCONNECT method. Possibilities:
- *
- * - Failure occurred before the kbdpoll task was started successfully.
- * In this case, the disconnection will have to be handled on the worker
- * task.
- * - Failure occurred after the kbdpoll task was started successfully. In
- * this case, the disconnection can be performed on the kbdpoll thread.
- */
- 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;
- DEBUGASSERT(priv != NULL);
- /* Set an indication to any users of the keyboard device that the device
- * is no longer available.
- */
- priv->disconnected = true;
- uinfo("Disconnected\n");
- /* Is there a thread waiting for keyboard data that will never come? */
- if (priv->waiting)
- {
- /* Yes.. wake it up */
- usbhost_givesem(&priv->waitsem);
- priv->waiting = false;
- }
- /* Possibilities:
- *
- * - Failure occurred before the kbdpoll task was started successfully.
- * In this case, the disconnection will have to be handled on the worker
- * task.
- * - Failure occurred after the kbdpoll task was started successfully. In
- * this case, the disconnection can be performed on the kbdpoll thread.
- */
- if (priv->polling)
- {
- /* The polling task is still alive. Signal the keyboard polling task.
- * When that task wakes up, it will decrement the reference count and,
- * perhaps, destroy the class instance. Then it will exit.
- */
- nxsig_kill(priv->pollpid, SIGALRM);
- }
- else
- {
- /* In the case where the failure occurs before the polling task was
- * started. Now what? We are probably executing from an interrupt
- * handler here. We will use the worker thread. This is kind of
- * wasteful and begs for a re-design.
- */
- DEBUGASSERT(priv->work.worker == NULL);
- work_queue(HPWORK, &priv->work, usbhost_destroy, priv, 0);
- }
- return OK;
- }
- /****************************************************************************
- * Name: usbhost_open
- *
- * Description:
- * Standard character driver open method.
- *
- ****************************************************************************/
- static int usbhost_open(FAR struct file *filep)
- {
- FAR struct inode *inode;
- FAR struct usbhost_state_s *priv;
- irqstate_t flags;
- int ret;
- uinfo("Entry\n");
- DEBUGASSERT(filep && filep->f_inode);
- inode = filep->f_inode;
- priv = inode->i_private;
- /* Make sure that we have exclusive access to the private data structure */
- DEBUGASSERT(priv && priv->crefs > 0 && priv->crefs < USBHOST_MAX_CREFS);
- ret = usbhost_takesem(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* Check if the keyboard 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 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++;
- priv->open = true;
- ret = OK;
- }
- leave_critical_section(flags);
- usbhost_givesem(&priv->exclsem);
- return ret;
- }
- /****************************************************************************
- * Name: usbhost_close
- *
- * Description:
- * Standard character driver close method.
- *
- ****************************************************************************/
- static int usbhost_close(FAR struct file *filep)
- {
- FAR struct inode *inode;
- FAR struct usbhost_state_s *priv;
- irqstate_t flags;
- int ret;
- uinfo("Entry\n");
- DEBUGASSERT(filep && filep->f_inode);
- inode = filep->f_inode;
- priv = inode->i_private;
- /* Decrement the reference count on the driver */
- DEBUGASSERT(priv->crefs >= 1);
- ret = usbhost_takesem(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* We need to disable interrupts momentarily to assure that there are no
- * asynchronous poll or disconnect events.
- */
- flags = enter_critical_section();
- priv->crefs--;
- /* Check if the USB mouse device is still connected. If the device is
- * no longer connected, then unregister the driver and free the driver
- * class instance.
- */
- if (priv->disconnected)
- {
- /* If the reference count is one or less then there are two
- * possibilities:
- *
- * 1) It might be zero meaning that the polling thread has already
- * exited and decremented its count.
- * 2) If might be one meaning either that (a) the polling thread is
- * still running and still holds a count, or (b) the polling thread
- * has exited, but there is still an outstanding open reference.
- */
- if (priv->crefs == 0 || (priv->crefs == 1 && priv->polling))
- {
- /* Yes.. In either case, then the driver is no longer open */
- priv->open = false;
- priv->headndx = 0;
- priv->tailndx = 0;
- /* Check if the USB keyboard device is still connected. */
- if (priv->crefs == 0)
- {
- /* The polling thread is no longer running */
- DEBUGASSERT(!priv->polling);
- /* If the device is no longer connected, unregister the driver
- * and free the driver class instance.
- */
- usbhost_destroy(priv);
- /* Skip giving the semaphore... it is no longer valid */
- leave_critical_section(flags);
- return OK;
- }
- else /* if (priv->crefs == 1) */
- {
- /* The polling thread is still running. Signal it so that it
- * will wake up and call usbhost_destroy(). The particular
- * signal that we use does not matter in this case.
- */
- nxsig_kill(priv->pollpid, SIGALRM);
- }
- }
- }
- usbhost_givesem(&priv->exclsem);
- leave_critical_section(flags);
- return OK;
- }
- /****************************************************************************
- * Name: usbhost_read
- *
- * Description:
- * Standard character driver read method.
- *
- ****************************************************************************/
- static ssize_t usbhost_read(FAR struct file *filep, FAR char *buffer,
- size_t len)
- {
- FAR struct inode *inode;
- FAR struct usbhost_state_s *priv;
- size_t nbytes;
- unsigned int tail;
- int ret;
- uinfo("Entry\n");
- DEBUGASSERT(filep && filep->f_inode && buffer);
- inode = filep->f_inode;
- priv = inode->i_private;
- /* Make sure that we have exclusive access to the private data structure */
- DEBUGASSERT(priv && priv->crefs > 0 && priv->crefs < USBHOST_MAX_CREFS);
- ret = usbhost_takesem(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* Check if the keyboard is still connected. We need to disable interrupts
- * momentarily to assure that there are no asynchronous disconnect events.
- */
- if (priv->disconnected)
- {
- /* No... the driver is no longer bound to the class. That means that
- * the USB keyboard is no longer connected. Refuse any further
- * attempts to access the driver.
- */
- ret = -ENODEV;
- }
- else
- {
- /* Is there keyboard data now? */
- while (priv->tailndx == priv->headndx)
- {
- /* No.. were we open non-blocking? */
- if (filep->f_oflags & O_NONBLOCK)
- {
- /* Yes.. then return a failure */
- ret = -EAGAIN;
- goto errout;
- }
- /* Wait for data to be available */
- uinfo("Waiting...\n");
- priv->waiting = true;
- usbhost_givesem(&priv->exclsem);
- ret = usbhost_takesem(&priv->waitsem);
- if (ret < 0)
- {
- return ret;
- }
- ret = usbhost_takesem(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* Did the keyboard become disconnected while we were waiting */
- if (priv->disconnected)
- {
- ret = -ENODEV;
- goto errout;
- }
- }
- /* Read data from our internal buffer of received characters */
- for (tail = priv->tailndx, nbytes = 0;
- tail != priv->headndx && nbytes < len;
- nbytes++)
- {
- /* Copy the next keyboard character into the user buffer */
- *buffer++ = priv->kbdbuffer[tail];
- /* Handle wrap-around of the tail index */
- if (++tail >= CONFIG_HIDKBD_BUFSIZE)
- {
- tail = 0;
- }
- }
- ret = nbytes;
- /* Update the tail index (perhaps marking the buffer empty) */
- priv->tailndx = tail;
- }
- errout:
- usbhost_givesem(&priv->exclsem);
- return (ssize_t)ret;
- }
- /****************************************************************************
- * Name: usbhost_write
- *
- * Description:
- * Standard character driver write method.
- *
- ****************************************************************************/
- static ssize_t usbhost_write(FAR struct file *filep, FAR const char *buffer,
- size_t len)
- {
- /* We won't try to write to the keyboard */
- return -ENOSYS;
- }
- /****************************************************************************
- * Name: usbhost_poll
- *
- * Description:
- * Standard character driver poll method.
- *
- ****************************************************************************/
- static int usbhost_poll(FAR struct file *filep, FAR struct pollfd *fds,
- bool setup)
- {
- FAR struct inode *inode;
- FAR struct usbhost_state_s *priv;
- int ret;
- int i;
- uinfo("Entry\n");
- DEBUGASSERT(filep && filep->f_inode && fds);
- inode = filep->f_inode;
- priv = inode->i_private;
- /* Make sure that we have exclusive access to the private data structure */
- DEBUGASSERT(priv);
- ret = usbhost_takesem(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* Check if the keyboard is still connected. We need to disable interrupts
- * momentarily to assure that there are no asynchronous disconnect events.
- */
- if (priv->disconnected)
- {
- /* No... the driver is no longer bound to the class. That means that
- * the USB keyboard is no longer connected. Refuse any further
- * attempts to access the driver.
- */
- ret = -ENODEV;
- }
- else if (setup)
- {
- /* This is a request to set up the poll. Find an available slot for
- * the poll structure reference
- */
- for (i = 0; i < CONFIG_HIDKBD_NPOLLWAITERS; i++)
- {
- /* Find an available slot */
- if (!priv->fds[i])
- {
- /* Bind the poll structure and this slot */
- priv->fds[i] = fds;
- fds->priv = &priv->fds[i];
- break;
- }
- }
- if (i >= CONFIG_HIDKBD_NPOLLWAITERS)
- {
- fds->priv = NULL;
- ret = -EBUSY;
- goto errout;
- }
- /* Should we immediately notify on any of the requested events? Notify
- * the POLLIN event if there is buffered keyboard data.
- */
- if (priv->headndx != priv->tailndx)
- {
- usbhost_pollnotify(priv);
- }
- }
- else
- {
- /* This is a request to tear down the poll. */
- struct pollfd **slot = (struct pollfd **)fds->priv;
- DEBUGASSERT(slot);
- /* Remove all memory of the poll setup */
- *slot = NULL;
- fds->priv = NULL;
- }
- errout:
- nxsem_post(&priv->exclsem);
- return ret;
- }
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: usbhost_kbdinit
- *
- * Description:
- * Initialize the USB storage HID keyboard class driver. This function
- * should be called be platform-specific code in order to initialize and
- * register support for the USB host HID keyboard class device.
- *
- * Input Parameters:
- * None
- *
- * Returned Value:
- * On success this function will return zero (OK); A negated errno value
- * will be returned on failure.
- *
- ****************************************************************************/
- int usbhost_kbdinit(void)
- {
- /* Perform any one-time initialization of the class implementation */
- nxsem_init(&g_exclsem, 0, 1);
- nxsem_init(&g_syncsem, 0, 0);
- /* The g_syncsem semaphore is used for signaling and, hence, should not
- * have priority inheritance enabled.
- */
- nxsem_set_protocol(&g_syncsem, SEM_PRIO_NONE);
- /* Advertise our availability to support (certain) devices */
- return usbhost_registerclass(&g_hidkbd);
- }
- #endif /* CONFIG_USBHOST)&& !CONFIG_USBHOST_INT_DISABLE */
|