From 18d196e968d87664cd03176dd4f2dbdbb18df3e0 Mon Sep 17 00:00:00 2001 From: Lwazi Dube Date: Tue, 28 Mar 2023 11:02:43 -0400 Subject: [PATCH] usbhost_hidkbd: Add the option to use interrupt transfers. Using the interrupt pipe is recommended in the Get_Report request section of the HID standard. This option has been added to support some keyboards that refuse to return valid keys when polled using the Get_Report request. Support for the Caps Lock key, including LED, has also been added. --- drivers/usbhost/Kconfig | 6 + drivers/usbhost/usbhost_hidkbd.c | 765 ++++++++++++++++++++++++------- 2 files changed, 611 insertions(+), 160 deletions(-) diff --git a/drivers/usbhost/Kconfig b/drivers/usbhost/Kconfig index a8e575d505..08fb8e93e7 100644 --- a/drivers/usbhost/Kconfig +++ b/drivers/usbhost/Kconfig @@ -361,6 +361,12 @@ config HIDKBD_NODEBOUNCE If set to y normal debouncing is disabled. Default: Debounce enabled (No repeat keys). +config HIDKBD_NOGETREPORT + bool "Use Interrupt pipe to get keys" + default n + ---help--- + Use the INTERRUPT IN pipe to get keyboard input reports. + endif config USBHOST_HIDMOUSE diff --git a/drivers/usbhost/usbhost_hidkbd.c b/drivers/usbhost/usbhost_hidkbd.c index 73d062c8b5..bf622d2b1c 100644 --- a/drivers/usbhost/usbhost_hidkbd.c +++ b/drivers/usbhost/usbhost_hidkbd.c @@ -179,7 +179,7 @@ struct usbhost_state_s struct usbhost_class_s usbclass; - /* The remainder of the fields are provide o the keyboard class driver */ + /* The remainder of the fields are provided to the keyboard class driver */ char devchar; /* Character identifying the /dev/kbd[n] device */ volatile bool disconnected; /* TRUE: Device has been disconnected */ @@ -222,6 +222,21 @@ struct usbhost_state_s volatile uint16_t headndx; /* Buffer head index */ volatile uint16_t tailndx; /* Buffer tail index */ uint8_t kbdbuffer[CONFIG_HIDKBD_BUFSIZE]; + + FAR uint8_t *ctrlreq; /* Allocated ctrl request structure */ + size_t ctrllen; /* Size of the allocated control buffer */ + bool caps_lock; /* Private caps lock status */ + bool sync_led; /* For LED update */ + bool empty; /* Keep track of data availability */ + sem_t syncsem; /* Semaphore to wait for a poll thread */ + +#ifdef CONFIG_HIDKBD_NOGETREPORT + struct work_s rwork; /* For interrupt transfer work */ + int16_t nbytes; /* # of bytes actually transferred */ +#endif +#ifndef CONFIG_HIDKBD_NODEBOUNCE + uint8_t lastkey[6]; /* For debouncing */ +#endif }; /* This type is used for encoding special characters */ @@ -266,6 +281,20 @@ static inline void usbhost_encodescancode(FAR struct usbhost_state_s *priv, #endif static int usbhost_kbdpoll(int argc, char *argv[]); +#ifdef CONFIG_HIDKBD_NOGETREPORT +static void usbhost_kbd_work(FAR void *arg); +static void usbhost_kbd_callback(FAR void *arg, ssize_t nbytes); +#endif + +static int usbhost_extract_keys(FAR struct usbhost_state_s *priv); + +static int usbhost_send_request(FAR struct usbhost_state_s *priv, + uint8_t dir, uint8_t req, uint16_t value, + uint16_t index, uint16_t len, uint8_t *data); + +static inline bool usbhost_get_capslock(void); +static inline void usbhost_toggle_capslock(void); + /* Helpers for usbhost_connect() */ static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv, @@ -282,6 +311,11 @@ static inline void usbhost_putle16(uint8_t *dest, uint16_t val); static inline int usbhost_tdalloc(FAR struct usbhost_state_s *priv); static inline int usbhost_tdfree(FAR struct usbhost_state_s *priv); +/* Control request memory management */ + +static int usbhost_cralloc(FAR struct usbhost_state_s *priv); +static int usbhost_crfree(FAR struct usbhost_state_s *priv); + /* struct usbhost_registry_s methods */ static FAR struct usbhost_class_s *usbhost_create( @@ -353,10 +387,13 @@ static uint32_t g_devinuse; /* The following are used to managed the class creation operation */ static mutex_t g_lock = NXMUTEX_INITIALIZER; -static sem_t g_syncsem = SEM_INITIALIZER(0); static FAR struct usbhost_state_s *g_priv; -/* The following tables map keyboard scan codes to printable ASIC +/* Global caps lock status */ + +static bool g_caps_lock = false; + +/* The following tables map keyboard scan codes to printable ASCII * characters. There is no support here for function keys or cursor * controls. */ @@ -532,7 +569,7 @@ static const uint8_t encoding[USBHID_NUMENCODINGS] = 0, 0, 0, 0, - /* 0xd0-0xd7: Memory Store,Recall,Clear,Add,Subtract,Muliply,Divide,+/- */ + /* 0xd0-0xd7: Memory Store,Recall,Clear,Add,Subtract,Multiply,Divide,+/- */ KEYCODE_MEMSTORE, KEYCODE_MEMRECALL, KEYCODE_MEMCLEAR, KEYCODE_MEMADD, @@ -578,7 +615,7 @@ static const uint8_t ucmap[USBHID_NUMSCANCODES] = '{', '}', '\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, /* 0xd0-0xd7: Memory Store,Recall,Clear,Add,Subtract,Multiply,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 @@ -613,7 +650,7 @@ static const uint8_t lcmap[USBHID_NUMSCANCODES] = '{', '}', '\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, /* 0xd0-0xd7: Memory Store,Recall,Clear,Add,Subtract,Multiply,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 @@ -781,6 +818,10 @@ static void usbhost_destroy(FAR void *arg) usbhost_tdfree(priv); + /* Free any control request buffers */ + + usbhost_crfree(priv); + /* Destroy the mutex & semaphores */ nxmutex_destroy(&priv->lock); @@ -902,6 +943,11 @@ static void usbhost_putstream(FAR struct lib_outstream_s *stream, int ch) static inline uint8_t usbhost_mapscancode(uint8_t scancode, uint8_t modifier) { + if (usbhost_get_capslock()) + { + modifier |= (USBHID_MODIFER_LSHIFT | USBHID_MODIFER_RSHIFT); + } + #ifndef CONFIG_HIDKBD_RAWSCANCODES /* Range check */ @@ -977,6 +1023,332 @@ static inline void usbhost_encodescancode(FAR struct usbhost_state_s *priv, } #endif +/**************************************************************************** + * Name: usbhost_get_capslock + * + * Description: + * Get the global value of caps lock. + * + * Input Parameters: + * None + * + * Returned Value: + * The value of g_caps_lock + * + ****************************************************************************/ + +static inline bool usbhost_get_capslock(void) +{ + irqstate_t flags; + bool retval; + + flags = enter_critical_section(); + retval = g_caps_lock; + leave_critical_section(flags); + + return retval; +} + +/**************************************************************************** + * Name: usbhost_toggle_capslock + * + * Description: + * Toggle g_caps_lock. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void usbhost_toggle_capslock(void) +{ + irqstate_t flags; + + flags = enter_critical_section(); + g_caps_lock = !g_caps_lock; + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: usbhost_extract_keys + * + * 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 + * + ****************************************************************************/ + +static int usbhost_extract_keys(FAR struct usbhost_state_s *priv) +{ + struct usbhid_kbdreport_s *rpt = + (struct usbhid_kbdreport_s *)priv->tbuffer; + uint8_t keycode; + int i; + int ret; + bool newstate; + + /* Add the newly received keystrokes to our internal buffer */ + + ret = nxmutex_lock(&priv->lock); + 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] != priv->lastkey[0] + && rpt->key[i] != priv->lastkey[1] + && rpt->key[i] != priv->lastkey[2] + && rpt->key[i] != priv->lastkey[3] + && rpt->key[i] != priv->lastkey[4] + && rpt->key[i] != priv->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); + + if (rpt->key[i] == 57) + { + usbhost_toggle_capslock(); + } + + /* 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 + priv->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 (priv->empty) + { + poll_notify(priv->fds, CONFIG_HIDKBD_NPOLLWAITERS, POLLIN); + } + + /* Yes.. Is there a thread waiting for keyboard data now? */ + + if (priv->waiting) + { + /* Yes.. wake it up */ + + nxsem_post(&priv->waitsem); + priv->waiting = false; + } + } + + priv->empty = newstate; + nxmutex_unlock(&priv->lock); + + return 0; +} + +/**************************************************************************** + * Name: usbhost_kbd_work + * + * Description: + * Handle receipt of an asynchronous notification from the HID device + * + * Input Parameters: + * arg - The argument provided when the asynchronous I/O was setup + * + * Returned Value: + * None + * + * Assumptions: + * Probably called from an interrupt handler. + * + ****************************************************************************/ + +#ifdef CONFIG_HIDKBD_NOGETREPORT +static void usbhost_kbd_work(FAR void *arg) +{ + FAR struct usbhost_state_s *priv; + FAR struct usbhost_hubport_s *hport; + int ret; + + priv = (FAR struct usbhost_state_s *)arg; + DEBUGASSERT(priv); + + hport = priv->usbclass.hport; + DEBUGASSERT(hport); + + /* Are we still connected? */ + + if (!priv->disconnected && priv->epin) + { + /* Yes.. Was an interrupt IN message received correctly? */ + + if (priv->open && priv->nbytes >= 0) + { + /* Add the newly received keystrokes to our internal buffer */ + + usbhost_extract_keys(priv); + + /* No bytes left to process */ + + priv->nbytes = 0; + } + + /* Is the caps lock status in sync? */ + + if (priv->caps_lock != usbhost_get_capslock()) + { + /* No.. The LED needs to be changed by the polling thread. */ + + priv->sync_led = true; + } + else + { + /* Setup to receive the next report */ + + ret = DRVR_ASYNCH(hport->drvr, priv->epin, + (FAR uint8_t *)priv->tbuffer, + sizeof(struct usbhid_kbdreport_s), + usbhost_kbd_callback, + priv); + + if (ret < 0) + { + uerr("ERROR: DRVR_ASYNCH failed: %d\n", ret); + } + } + } +} + +/**************************************************************************** + * Name: usbhost_kbd_callback + * + * Description: + * Handle receipt of data + * + * Input Parameters: + * arg - The argument provided when 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. + * + ****************************************************************************/ + +static void usbhost_kbd_callback(FAR void *arg, ssize_t nbytes) +{ + FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)arg; + + DEBUGASSERT(priv); + + /* Are we still connected? */ + + if (!priv->disconnected) + { + DEBUGASSERT(nbytes >= INT16_MIN && nbytes <= INT16_MAX); + + /* Save nbytes */ + + priv->nbytes = (int16_t)nbytes; + + if (work_available(&priv->rwork)) + { + work_queue(HPWORK, &priv->rwork, usbhost_kbd_work, priv, 0); + } + } +} + +#endif + /**************************************************************************** * Name: usbhost_kbdpoll * @@ -994,24 +1366,15 @@ static inline void usbhost_encodescancode(FAR struct usbhost_state_s *priv, 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; + uint8_t leds; uinfo("Started\n"); @@ -1027,10 +1390,9 @@ static int usbhost_kbdpoll(int argc, char *argv[]) priv = g_priv; DEBUGASSERT(priv != NULL && priv->usbclass.hport); - hport = priv->usbclass.hport; priv->polling = true; - nxsem_post(&g_syncsem); + nxsem_post(&priv->syncsem); nxsig_sleep(1); /* Loop here until the device is disconnected */ @@ -1050,28 +1412,44 @@ static int usbhost_kbdpoll(int argc, char *argv[]) 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 - */ + /* Keep the Caps Lock LED in sync */ - 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; + if (priv->sync_led) + { + priv->sync_led = false; + priv->caps_lock = usbhost_get_capslock(); - usbhost_putle16(ctrlreq->value, (USBHID_REPORTTYPE_INPUT << 8)); - usbhost_putle16(ctrlreq->index, priv->ifno); - usbhost_putle16(ctrlreq->len, sizeof(struct usbhid_kbdreport_s)); + leds = usbhost_get_capslock() ? USBHID_KBDOUT_CAPSLOCK : 0x00; + /* Send a report request to change the LED */ + + usbhost_send_request(priv, USB_REQ_DIR_OUT, + USBHID_REQUEST_SETREPORT, + USBHID_REPORTTYPE_OUTPUT << 8, 0, 1, + &leds); + +#ifdef CONFIG_HIDKBD_NOGETREPORT + /* Setup to receive the next report */ + + if (work_available(&priv->rwork)) + { + work_queue(HPWORK, &priv->rwork, usbhost_kbd_work, priv, 0); + } +#endif + } + +#ifdef CONFIG_HIDKBD_NOGETREPORT + nxmutex_unlock(&priv->lock); +#else /* Send HID report request */ - ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, priv->tbuffer); + ret = usbhost_send_request(priv, USB_REQ_DIR_IN, + USBHID_REQUEST_GETREPORT, + USBHID_REPORTTYPE_INPUT << 8, + priv->ifno, + sizeof(struct usbhid_kbdreport_s), + priv->tbuffer); + nxmutex_unlock(&priv->lock); /* Check for errors -- Bail if an excessive number of consecutive @@ -1097,141 +1475,26 @@ static int usbhost_kbdpoll(int argc, char *argv[]) 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 = nxmutex_lock(&priv->lock); + ret = usbhost_extract_keys(priv); if (ret < 0) { return ret; } - for (i = 0; i < 6; i++) + /* Make sure the LED is in sync */ + + if (priv->caps_lock != usbhost_get_capslock()) { - /* 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 + priv->sync_led = true; } - - /* 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) - { - poll_notify(priv->fds, CONFIG_HIDKBD_NPOLLWAITERS, POLLIN); - } - - /* Yes.. Is there a thread waiting for keyboard data now? */ - - if (priv->waiting) - { - /* Yes.. wake it up */ - - nxsem_post(&priv->waitsem); - priv->waiting = false; - } - } - - empty = newstate; - nxmutex_unlock(&priv->lock); } +#endif /* If USB debug is on, then provide some periodic indication that * polling is still happening. @@ -1305,14 +1568,14 @@ static int usbhost_kbdpoll(int argc, char *argv[]) if (priv->crefs < 1) { /* Unregister the driver and destroy the instance (while we hold - * the semaphore!) + * the mutex!) */ usbhost_destroy(priv); } else { - /* No, we will destroy the driver instance when it is final open + /* No, we will destroy the driver instance when its final open * reference is closed */ @@ -1612,7 +1875,11 @@ static inline int usbhost_devinit(FAR struct usbhost_state_s *priv) { char devname[DEV_NAMELEN]; int ret; - + uint8_t leds; +#ifdef CONFIG_HIDKBD_NOGETREPORT + FAR struct usbhost_hubport_s *hport; + hport = priv->usbclass.hport; +#endif /* Set aside a transfer buffer for exclusive * use by the keyboard class driver */ @@ -1624,6 +1891,55 @@ static inline int usbhost_devinit(FAR struct usbhost_state_s *priv) return ret; } + /* Allocate memory for control requests */ + + ret = usbhost_cralloc(priv); + if (ret < 0) + { + uerr("ERROR: Failed to allocate control buffer\n"); + return ret; + } + + /* Set the idle rate */ + + usbhost_send_request(priv, USB_REQ_DIR_OUT, USBHID_REQUEST_SETIDLE, + 25 << 8, priv->ifno, 0, NULL); + + /* Choose the boot protocol */ + + usbhost_send_request(priv, USB_REQ_DIR_OUT, USBHID_REQUEST_SETPROTOCOL, + 0, 0, 0, NULL); + + /* Initialize the Caps Lock LED */ + + priv->caps_lock = usbhost_get_capslock(); + + leds = usbhost_get_capslock() ? USBHID_KBDOUT_CAPSLOCK : 0x00; + usbhost_send_request(priv, USB_REQ_DIR_OUT, USBHID_REQUEST_SETREPORT, + USBHID_REPORTTYPE_OUTPUT << 8, 0, 1, + &leds); + +#ifdef CONFIG_HIDKBD_NOGETREPORT + + /* Do we have an interrupt IN endpoint? */ + + if (priv->epin) + { + /* Use interrupt tranfers to get reports. */ + + uinfo("Start waiting for key reports\n"); + ret = DRVR_ASYNCH(hport->drvr, priv->epin, + (FAR uint8_t *)priv->tbuffer, + sizeof(struct usbhid_kbdreport_s), + usbhost_kbd_callback, + priv); + if (ret < 0) + { + uerr("ERROR: DRVR_ASYNCH failed: %d\n", ret); + } + } +#endif + /* Increment the reference count. This will prevent usbhost_destroy() from * being called asynchronously if the device is removed. */ @@ -1675,7 +1991,7 @@ static inline int usbhost_devinit(FAR struct usbhost_state_s *priv) /* Now wait for the poll task to get properly initialized */ - ret = nxsem_wait_uninterruptible(&g_syncsem); + ret = nxsem_wait_uninterruptible(&priv->syncsem); nxmutex_unlock(&g_lock); if (ret < 0) @@ -1801,6 +2117,132 @@ static int usbhost_tdfree(FAR struct usbhost_state_s *priv) return result; } +/**************************************************************************** + * Name: usbhost_cralloc + * + * Description: + * Allocate control request 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_cralloc(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->ctrlreq, &priv->ctrllen); +} + +/**************************************************************************** + * Name: usbhost_crfree + * + * Description: + * Free control request 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_crfree(FAR struct usbhost_state_s *priv) +{ + FAR struct usbhost_hubport_s *hport; + int result = OK; + + DEBUGASSERT(priv); + + if (priv->ctrlreq) + { + DEBUGASSERT(priv->usbclass.hport); + hport = priv->usbclass.hport; + result = DRVR_FREE(hport->drvr, priv->ctrlreq); + priv->ctrlreq = NULL; + priv->ctrllen = 0; + } + + return result; +} + +/**************************************************************************** + * Name: usbhost_send_request + * + * Description: + * Send a request. + * + * Input Parameters: + * priv - A reference to the class instance. + * dir - USB_REQ_DIR_IN or USB_REQ_DIR_OUT + * req - request identifier + * value - request value + * index - request index + * len - request length + * + * 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_send_request(FAR struct usbhost_state_s *priv, + uint8_t dir, + uint8_t req, + uint16_t value, + uint16_t index, + uint16_t len, + uint8_t *data) +{ + FAR struct usbhost_hubport_s *hport; + int ret = OK; + FAR struct usb_ctrlreq_s *ctrlreq; + + DEBUGASSERT(priv); + + hport = priv->usbclass.hport; + + /* Initialize the control request */ + + ctrlreq = (FAR struct usb_ctrlreq_s *)priv->ctrlreq; + ctrlreq->type = dir | USB_REQ_TYPE_CLASS | + USB_REQ_RECIPIENT_INTERFACE; + ctrlreq->req = req; + + usbhost_putle16(ctrlreq->value, value); + usbhost_putle16(ctrlreq->index, index); + usbhost_putle16(ctrlreq->len, len); + + /* And send the request */ + + if (dir == USB_REQ_DIR_IN) + { + ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, data); + } + else + { + ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, data); + } + + if (ret < 0) + { + uerr("ERROR: Control request failed: %d\n", ret); + } + + return ret; +} + /**************************************************************************** * struct usbhost_registry_s methods ****************************************************************************/ @@ -1866,6 +2308,9 @@ usbhost_create(FAR struct usbhost_hubport_s *hport, nxmutex_init(&priv->lock); nxsem_init(&priv->waitsem, 0, 0); + nxsem_init(&priv->syncsem, 0, 0); + + priv->empty = true; /* Return the instance of the USB keyboard class driver */