From aa42efa31bbd43297be222afd5d5fe5006f55a1c Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 30 Jul 2014 17:01:51 -0600 Subject: [PATCH] dd support for a USB keyboard as the NxConsole and text widget input. Not fully functional as of this initial check-in. Basic functionality is there, but there are conditions were the keyboard gets lost. --- Kconfig | 23 +++- nxwm/include/ckeyboard.hxx | 50 ++++++- nxwm/src/ckeyboard.cxx | 259 +++++++++++++++++++++++++++++-------- nxwm/src/cnxconsole.cxx | 7 +- 4 files changed, 274 insertions(+), 65 deletions(-) diff --git a/Kconfig b/Kconfig index 52aa90da1..3ee3bd123 100644 --- a/Kconfig +++ b/Kconfig @@ -855,13 +855,24 @@ config NXWM_KEYBOARD_DEVPATH string "Keyboard Device Path" default "/dev/console" ---help--- - The full path to the touchscreen device. Default: "/dev/console" + The full path to the keyboard device. Default: "/dev/console" + +config NXWM_KEYBOARD_USBHOST + bool "USB Keyboard Device" + default n + ---help--- + This setting indicates that NXWM_KEYBOARD_DEVPATH is a USB keyboard. + A USB Keyboard is a removable device. When it is inserted, then + device at NXWM_KEYBOARD_DEVPATH will appear; when it removed, + keyboard reads will fail and the device at NXWM_KEYBOARD_DEVPATH + will disappear. Selecting this option builds addtional logic into + the keyboard listener thread in order to handle this case. config NXWM_KEYBOARD_SIGNO int "Keyboard Task Signal Number" default 6 ---help--- - The realtime signal used to wake up the touchscreen listener thread. + The realtime signal used to wake up the keyboard listener thread. Default: 6 config NXWM_KEYBOARD_BUFSIZE @@ -874,10 +885,10 @@ config NXWM_KEYBOARD_LISTENERPRIO int "Keyboard Listener Task Priority" default 120 ---help--- - Priority of the touchscreen listener thread.This listener should - have a higher priority than most display-related tsks otherwise it - may miss touchscreen events (not really very likely because keyboard - input is relatively slow). Default: 120 + Priority of the keyboard listener thread. This listener should + have a higher priority than most display-related tasks otherwise it + may miss keyboard input (not really very likely because keyboard + input is relatively slow). Default: 120 config NXWM_KEYBOARD_LISTENERSTACK int "Keyboard Listener Task Stack Size" diff --git a/nxwm/include/ckeyboard.hxx b/nxwm/include/ckeyboard.hxx index 7036472fb..e224fd11c 100644 --- a/nxwm/include/ckeyboard.hxx +++ b/nxwm/include/ckeyboard.hxx @@ -1,7 +1,7 @@ /**************************************************************************** * NxWidgets/nxwm/include/keyboard.hxx * - * Copyright (C) 2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2012, 2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -33,8 +33,8 @@ * ****************************************************************************/ -#ifndef __INCLUDE_CKEYBOARD_HXX -#define __INCLUDE_CKEYBOARD_HXX +#ifndef __NXWM_INCLUDE_CKEYBOARD_HXX +#define __NXWM_INCLUDE_CKEYBOARD_HXX /**************************************************************************** * Included Files @@ -93,8 +93,46 @@ namespace NxWM sem_t m_waitSem; /**< Used to synchronize with the listener thread */ /** - * The keyboard listener thread. This is the entry point of a thread that - * listeners for and dispatches keyboard events to the NX server. + * Open the keyboard device. Not very interesting for the case of + * standard device but much more interesting for a USB keyboard device + * that may disappear when the keyboard is disconnect but later reappear + * when the keyboard is reconnected. In this case, this function will + * not return until the keyboard device was successfully opened (or + * until an irrecoverable error occurs. + * + * Opens the keyboard device specified by CONFIG_NXWM_KEYBOARD_DEVPATH. + * + * @return On success, then method returns a valid file descriptor that + * can be used to redirect stdin. A negated errno value is returned + * if an irrecoverable error occurs. + */ + + inline int open(void); + + /** + * This is the heart of the keyboard listener thread. It contains the + * actual logic that listeners for and dispatches keyboard events to the + * NX server. + * + * @return If the session terminates gracefully (i.e., because >m_state + * is no longer equal to LISTENER_RUNNING, then method returns OK. A + * negated errno value is returned if an error occurs while reading from + * the keyboard device. A read error, depending upon the type of the + * error, may simply indicate that a USB keyboard was removed and we + * should wait for the keyboard to be connected. + */ + + inline int session(void); + + /** + * The keyboard listener thread. This is the entry point of a thread + * that listeners for and dispatches keyboard events to the NX server. + * It simply opens the keyboard device (using CKeyboard::open()) and + * executes the session (via CKeyboard::session()). + * + * If an errors while reading from the keyboard device AND we are + * configured to use a USB keyboard, then this function will wait for + * the USB keyboard to be re-connected. * * @param arg. The CKeyboard 'this' pointer cast to a void*. * @return This function normally does not return but may return NULL on @@ -130,4 +168,4 @@ namespace NxWM }; } -#endif // __INCLUDE_CKEYBOARD_HXX +#endif // __NXWM_INCLUDE_CKEYBOARD_HXX diff --git a/nxwm/src/ckeyboard.cxx b/nxwm/src/ckeyboard.cxx index 0f3f11d37..f643fbf6b 100644 --- a/nxwm/src/ckeyboard.cxx +++ b/nxwm/src/ckeyboard.cxx @@ -156,9 +156,166 @@ bool CKeyboard::start(void) return m_state == LISTENER_RUNNING; } - /** - * The keyboard listener thread. This is the entry point of a thread that - * listeners for and dispatches keyboard events to the NX server. +/** + * Open the keyboard device. Not very interesting for the case of + * standard device but much more interesting for a USB keyboard device + * that may disappear when the keyboard is disconnect but later reappear + * when the keyboard is reconnected. In this case, this function will + * not return until the keyboard device was successfully opened (or + * until an irrecoverable error occurs. + * + * Opens the keyboard device specified by CONFIG_NXWM_KEYBOARD_DEVPATH. + * + * @return On success, then method returns a valid file descriptor that + * can be used to redirect stdin. A negated errno value is returned + * if an irrecoverable error occurs. + */ + +int CKeyboard::open(void) +{ + int fd; + + // Loop until we have successfully opened the USB keyboard (or until some + // irrecoverable error occurs). + + do + { + // Try to open the keyboard device + + fd = std::open(CONFIG_NXWM_KEYBOARD_DEVPATH, O_RDONLY); + if (fd < 0) + { + int errcode = errno; + DEBUGASSERT(errcode > 0); + + // EINTR should be ignored because it is not really an error at + // all. We should retry immediately + + if (errcode != EINTR) + { +#ifdef CONFIG_NXWM_KEYBOARD_USBHOST + // ENOENT means that the USB device is not yet connected and, + // hence, has no entry under /dev. If the USB driver still + // exists under /dev (because other threads still have the driver + // open), then we might also get ENODEV. + + if (errcode == ENOENT || errcode == ENODEV) + { + // REVIST: Can we inject a constant string here to let the + // user know that we are waiting for a USB keyboard to be + // connected? + + // Sleep a bit and try again + + gvdbg("WAITING for a USB device\n"); + std::sleep(2); + } + + // Anything else would be really bad. + + else +#endif + { + // Let the top-level logic decide what it wants to do + // about all really bad things + + gdbg("ERROR: Failed to open %s for reading: %d\n", + CONFIG_NXWM_KEYBOARD_DEVPATH, errcode); + return -errcode; + } + } + } + } + while (fd < 0); + + return fd; +} + +/** + * This is the heart of the keyboard listener thread. It contains the + * actual logic that listeners for and dispatches keyboard events to the + * NX server. + * + * @return If the session terminates gracefully (i.e., because >m_state + * is no longer equal to LISTENER_RUNNING), then method returns OK. A + * negated errno value is returned if an error occurs while reading from + * the keyboard device. A read error, depending upon the type of the + * error, may simply indicate that a USB keyboard was removed and we + * should wait for the keyboard to be connected. + */ + +int CKeyboard::session(void) +{ + gvdbg("Session started\n"); + + // Loop, reading and dispatching keyboard data + + while (m_state == LISTENER_RUNNING) + { + // Read one keyboard sample + + gvdbg("Listening for keyboard input\n"); + + uint8_t rxbuffer[CONFIG_NXWM_KEYBOARD_BUFSIZE]; + ssize_t nbytes = read(m_kbdFd, rxbuffer, + CONFIG_NXWM_KEYBOARD_BUFSIZE); + + // Check for errors + + if (nbytes < 0) + { + int errcode = errno; + DEBUGASSERT(errcode > 0); + + // EINTR is not really an error, it simply means that something is + // trying to get our attention. We need to check m_state to see + // if we were asked to terminate + + if (errcode != EINTR) + { + // Let the top-level listener logic decide what to do about + // the read failure. + + gdbg("ERROR: read %s failed: %d\n", + CONFIG_NXWM_KEYBOARD_DEVPATH, errcode); + return -errcode; + } + + fdbg("Awakened with EINTR\n"); + } + + // Give the keyboard input to NX + + else if (nbytes > 0) + { + // Looks like good keyboard input... process it. + // First, get the server handle + + NXHANDLE handle = m_server->getServer(); + + // Then inject the keyboard input into NX + + int ret = nx_kbdin(handle, (uint8_t)nbytes, rxbuffer); + if (ret < 0) + { + gdbg("ERROR: nx_kbdin failed: %d\n", ret); + //break; ignore the error + } + } + } + + return OK; +} + +/** + * The keyboard listener thread. This is the entry point of a thread + * that listeners for and dispatches keyboard events to the NX server. + * It simply opens the keyboard device (using CKeyboard::open()) and + * executes the session (via CKeyboard::session()). + * + * If an errors while reading from the keyboard device AND we are + * configured to use a USB keyboard, then this function will wait for + * the USB keyboard to be re-connected. * * @param arg. The CKeyboard 'this' pointer cast to a void*. * @return This function normally does not return but may return NULL on @@ -171,68 +328,66 @@ FAR void *CKeyboard::listener(FAR void *arg) gvdbg("Listener started\n"); - // Open the keyboard device - - This->m_kbdFd = std::open(CONFIG_NXWM_KEYBOARD_DEVPATH, O_RDONLY); - if (This->m_kbdFd < 0) - { - gdbg("ERROR Failed to open %s for reading: %d\n", - CONFIG_NXWM_KEYBOARD_DEVPATH, errno); - This->m_state = LISTENER_FAILED; - sem_post(&This->m_waitSem); - return (FAR void *)0; - } - - // Indicate that we have successfully initialized +#ifdef CONFIG_NXWM_KEYBOARD_USBHOST + // Indicate that we have successfully started. We might be stuck waiting + // for a USB keyboard to be connected, but we are technically running This->m_state = LISTENER_RUNNING; sem_post(&This->m_waitSem); - // Now loop, reading and dispatching keyboard data + // Loop until we are told to quit while (This->m_state == LISTENER_RUNNING) - { - // Read one keyboard sample - - gvdbg("Listening for keyboard input\n"); - - uint8_t rxbuffer[CONFIG_NXWM_KEYBOARD_BUFSIZE]; - ssize_t nbytes = read(This->m_kbdFd, rxbuffer, CONFIG_NXWM_KEYBOARD_BUFSIZE); - - // Check for errors - - if (nbytes < 0) - { - // The only expect error is to be interrupt by a signal -#ifdef CONFIG_DEBUG - int errval = errno; - - gdbg("ERROR: read %s failed: %d\n", CONFIG_NXWM_KEYBOARD_DEVPATH, errval); - DEBUGASSERT(errval == EINTR); #endif - } + { + // Open/Re-open the keyboard device - // Give the keyboard input to NX - - else if (nbytes > 0) + This->m_kbdFd = This->open(); + if (This->m_kbdFd < 0) { - // Looks like good keyboard input... process it. - // First, get the server handle - - NXHANDLE handle = This->m_server->getServer(); - - // Then inject the keyboard input into NX - - int ret = nx_kbdin(handle, (uint8_t)nbytes, rxbuffer); - if (ret < 0) - { - gdbg("ERROR: nx_kbdin failed\n"); - } + gdbg("ERROR: open failed: %d\n", This->m_kbdFd); + This->m_state = LISTENER_FAILED; + sem_post(&This->m_waitSem); + return (FAR void *)0; } + +#ifndef CONFIG_NXWM_KEYBOARD_USBHOST + // Indicate that we have successfully initialized + + This->m_state = LISTENER_RUNNING; + sem_post(&This->m_waitSem); +#endif + + // Now execute the session. The session will run until either (1) we + // were asked to terminate gracefully (with m_state !=LISTENER_RUNNING), + // of if an error occurred while reading from the keyboard device. If + // we are configured to use a USB keyboard, then this error, depending + // upon what the error is, may indicate that the USB keyboard has been + // removed. In that case, we need to continue looping and, hopefully, + // the USB keyboard will be reconnected. + + int ret = This->session(); +#ifdef CONFIG_NXWM_KEYBOARD_USBHOST + if (ret < 0) + { + fdbg("ERROR: CKeyboard::session() returned %d\n", ret); + } +#else + // No errors from session() are expected + + DEBUGASSERT(ret == OK); + UNUSED(ret); +#endif + + // Close the keyboard device + + (void)std::close(This->m_kbdFd); + This->m_kbdFd = -1; } // We should get here only if we were asked to terminate via - // m_state = LISTENER_STOPREQUESTED + // m_state = LISTENER_STOPREQUESTED (or perhaps if some irrecoverable + // error has occurred). gvdbg("Listener exiting\n"); This->m_state = LISTENER_TERMINATED; diff --git a/nxwm/src/cnxconsole.cxx b/nxwm/src/cnxconsole.cxx index e78e91631..d9bab9cfd 100644 --- a/nxwm/src/cnxconsole.cxx +++ b/nxwm/src/cnxconsole.cxx @@ -1,7 +1,7 @@ /******************************************************************************************** * NxWidgets/nxwm/src/cnxconsole.cxx * - * Copyright (C) 2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2012. 2104 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -60,6 +60,11 @@ /******************************************************************************************** * Pre-Processor Definitions ********************************************************************************************/ +/* Configuration ****************************************************************************/ + +#ifdef CONFIG_NSH_USBKBD +# warning You probably do not really want CONFIG_NSH_USBKBD, try CONFIG_NXWM_KEYBOARD_USBHOST +#endif /******************************************************************************************** * Private Types