NXWidgets  1.19
ckeyboard.cxx
Go to the documentation of this file.
1 /********************************************************************************************
2  * NxWidgets/nxwm/src/ckeyboard.cxx
3  *
4  * Copyright (C) 2012 Gregory Nutt. All rights reserved.
5  * Author: Gregory Nutt <gnutt@nuttx.org>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  * 3. Neither the name NuttX, NxWidgets, nor the names of its contributors
18  * me be used to endorse or promote products derived from this software
19  * without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  ********************************************************************************************/
35 
36 /********************************************************************************************
37  * Included Files
38  ********************************************************************************************/
39 
40 #include <nuttx/config.h>
41 
42 #include <cunistd>
43 #include <cerrno>
44 #include <cfcntl>
45 
46 #include <sched.h>
47 #include <pthread.h>
48 #include <assert.h>
49 #include <debug.h>
50 
51 #include "nxwmconfig.hxx"
52 #include "ckeyboard.hxx"
53 
54 /********************************************************************************************
55  * Pre-Processor Definitions
56  ********************************************************************************************/
57 
58 /********************************************************************************************
59  * CKeyboard Method Implementations
60  ********************************************************************************************/
61 
62 using namespace NxWM;
63 
64 /**
65  * CKeyboard Constructor
66  *
67  * @param server. An instance of the NX server. This will be needed for
68  * injecting mouse data.
69  */
70 
72 {
73  m_server = server; // Save the NX server
74  m_kbdFd = -1; // Device driver is not opened
75  m_state = LISTENER_NOTRUNNING; // The listener thread is not running yet
76 
77  // Initialize the semaphore used to synchronize with the listener thread
78 
79  sem_init(&m_waitSem, 0, 0);
80 }
81 
82 /**
83  * CKeyboard Destructor
84  */
85 
87 {
88  // Stop the listener thread
89 
91 
92  // Wake up the listener thread so that it will use our buffer
93  // to receive data
94  // REVISIT: Need wait here for the listener thread to terminate
95 
96  (void)pthread_kill(m_thread, CONFIG_NXWM_KEYBOARD_SIGNO);
97 
98  // Close the keyboard device (or should these be done when the thread exits?)
99 
100  if (m_kbdFd >= 0)
101  {
102  std::close(m_kbdFd);
103  }
104 }
105 
106 /**
107  * Start the keyboard listener thread.
108  *
109  * @return True if the keyboard listener thread was correctly started.
110  */
111 
113 {
114  pthread_attr_t attr;
115 
116  ginfo("Starting listener\n");
117 
118  // Start a separate thread to listen for keyboard events
119 
120  (void)pthread_attr_init(&attr);
121 
122  struct sched_param param;
123  param.sched_priority = CONFIG_NXWM_KEYBOARD_LISTENERPRIO;
124  (void)pthread_attr_setschedparam(&attr, &param);
125 
126  (void)pthread_attr_setstacksize(&attr, CONFIG_NXWM_KEYBOARD_LISTENERSTACK);
127 
128  m_state = LISTENER_STARTED; // The listener thread has been started, but is not yet running
129 
130  int ret = pthread_create(&m_thread, &attr, listener, (FAR void *)this);
131  if (ret != 0)
132  {
133  gerr("ERROR: CKeyboard::start: pthread_create failed: %d\n", ret);
134  return false;
135  }
136 
137  // Detach from the thread
138 
139  (void)pthread_detach(m_thread);
140 
141  // Don't return until we are sure that the listener thread is running
142  // (or until it reports an error).
143 
144  while (m_state == LISTENER_STARTED)
145  {
146  // Wait for the listener thread to wake us up when we really
147  // are connected.
148 
149  (void)sem_wait(&m_waitSem);
150  }
151 
152  // Then return true only if the listener thread reported successful
153  // initialization.
154 
155  ginfo("Listener m_state=%d\n", (int)m_state);
156  return m_state == LISTENER_RUNNING;
157 }
158 
159 /**
160  * Open the keyboard device. Not very interesting for the case of
161  * standard device but much more interesting for a USB keyboard device
162  * that may disappear when the keyboard is disconnect but later reappear
163  * when the keyboard is reconnected. In this case, this function will
164  * not return until the keyboard device was successfully opened (or
165  * until an irrecoverable error occurs.
166  *
167  * Opens the keyboard device specified by CONFIG_NXWM_KEYBOARD_DEVPATH.
168  *
169  * @return On success, then method returns a valid file descriptor that
170  * can be used to redirect stdin. A negated errno value is returned
171  * if an irrecoverable error occurs.
172  */
173 
175 {
176  int fd;
177 
178  // Loop until we have successfully opened the USB keyboard (or until some
179  // irrecoverable error occurs).
180 
181  do
182  {
183  // Try to open the keyboard device
184 
185  fd = std::open(CONFIG_NXWM_KEYBOARD_DEVPATH, O_RDONLY);
186  if (fd < 0)
187  {
188  int errcode = errno;
189  DEBUGASSERT(errcode > 0);
190 
191  // EINTR should be ignored because it is not really an error at
192  // all. We should retry immediately
193 
194  if (errcode != EINTR)
195  {
196 #ifdef CONFIG_NXWM_KEYBOARD_USBHOST
197  // ENOENT means that the USB device is not yet connected and,
198  // hence, has no entry under /dev. If the USB driver still
199  // exists under /dev (because other threads still have the driver
200  // open), then we might also get ENODEV.
201 
202  if (errcode == ENOENT || errcode == ENODEV)
203  {
204  // REVIST: Can we inject a constant string here to let the
205  // user know that we are waiting for a USB keyboard to be
206  // connected?
207 
208  // Sleep a bit and try again
209 
210  ginfo("WAITING for a USB device\n");
211  std::sleep(2);
212  }
213 
214  // Anything else would be really bad.
215 
216  else
217 #endif
218  {
219  // Let the top-level logic decide what it wants to do
220  // about all really bad things
221 
222  gerr("ERROR: Failed to open %s for reading: %d\n",
223  CONFIG_NXWM_KEYBOARD_DEVPATH, errcode);
224  return -errcode;
225  }
226  }
227  }
228  }
229  while (fd < 0);
230 
231  return fd;
232 }
233 
234 /**
235  * This is the heart of the keyboard listener thread. It contains the
236  * actual logic that listeners for and dispatches keyboard events to the
237  * NX server.
238  *
239  * @return If the session terminates gracefully (i.e., because >m_state
240  * is no longer equal to LISTENER_RUNNING), then method returns OK. A
241  * negated errno value is returned if an error occurs while reading from
242  * the keyboard device. A read error, depending upon the type of the
243  * error, may simply indicate that a USB keyboard was removed and we
244  * should wait for the keyboard to be connected.
245  */
246 
248 {
249  ginfo("Session started\n");
250 
251  // Loop, reading and dispatching keyboard data
252 
253  while (m_state == LISTENER_RUNNING)
254  {
255  // Read one keyboard sample
256 
257  ginfo("Listening for keyboard input\n");
258 
259  uint8_t rxbuffer[CONFIG_NXWM_KEYBOARD_BUFSIZE];
260  ssize_t nbytes = read(m_kbdFd, rxbuffer,
261  CONFIG_NXWM_KEYBOARD_BUFSIZE);
262 
263  // Check for errors
264 
265  if (nbytes < 0)
266  {
267  int errcode = errno;
268  DEBUGASSERT(errcode > 0);
269 
270  // EINTR is not really an error, it simply means that something is
271  // trying to get our attention. We need to check m_state to see
272  // if we were asked to terminate
273 
274  if (errcode != EINTR)
275  {
276  // Let the top-level listener logic decide what to do about
277  // the read failure.
278 
279  gerr("ERROR: read %s failed: %d\n",
280  CONFIG_NXWM_KEYBOARD_DEVPATH, errcode);
281  return -errcode;
282  }
283 
284  fwarn("WARNING: Awakened with EINTR\n");
285  }
286 
287  // Give the keyboard input to NX
288 
289  else if (nbytes > 0)
290  {
291  // Looks like good keyboard input... process it.
292  // First, get the server handle
293 
294  NXHANDLE handle = m_server->getServer();
295 
296  // Then inject the keyboard input into NX
297 
298  int ret = nx_kbdin(handle, (uint8_t)nbytes, rxbuffer);
299  if (ret < 0)
300  {
301  gerr("ERROR: nx_kbdin failed: %d\n", ret);
302  //break; ignore the error
303  }
304  }
305  }
306 
307  return OK;
308 }
309 
310 /**
311  * The keyboard listener thread. This is the entry point of a thread
312  * that listeners for and dispatches keyboard events to the NX server.
313  * It simply opens the keyboard device (using CKeyboard::open()) and
314  * executes the session (via CKeyboard::session()).
315  *
316  * If an errors while reading from the keyboard device AND we are
317  * configured to use a USB keyboard, then this function will wait for
318  * the USB keyboard to be re-connected.
319  *
320  * @param arg. The CKeyboard 'this' pointer cast to a void*.
321  * @return This function normally does not return but may return NULL on
322  * error conditions.
323  */
324 
325 FAR void *CKeyboard::listener(FAR void *arg)
326 {
327  CKeyboard *This = (CKeyboard *)arg;
328 
329  ginfo("Listener started\n");
330 
331 #ifdef CONFIG_NXWM_KEYBOARD_USBHOST
332  // Indicate that we have successfully started. We might be stuck waiting
333  // for a USB keyboard to be connected, but we are technically running
334 
335  This->m_state = LISTENER_RUNNING;
336  sem_post(&This->m_waitSem);
337 
338  // Loop until we are told to quit
339 
340  while (This->m_state == LISTENER_RUNNING)
341 #endif
342  {
343  // Open/Re-open the keyboard device
344 
345  This->m_kbdFd = This->open();
346  if (This->m_kbdFd < 0)
347  {
348  gerr("ERROR: open failed: %d\n", This->m_kbdFd);
349  This->m_state = LISTENER_FAILED;
350  sem_post(&This->m_waitSem);
351  return (FAR void *)0;
352  }
353 
354 #ifndef CONFIG_NXWM_KEYBOARD_USBHOST
355  // Indicate that we have successfully initialized
356 
357  This->m_state = LISTENER_RUNNING;
358  sem_post(&This->m_waitSem);
359 #endif
360 
361  // Now execute the session. The session will run until either (1) we
362  // were asked to terminate gracefully (with m_state !=LISTENER_RUNNING),
363  // of if an error occurred while reading from the keyboard device. If
364  // we are configured to use a USB keyboard, then this error, depending
365  // upon what the error is, may indicate that the USB keyboard has been
366  // removed. In that case, we need to continue looping and, hopefully,
367  // the USB keyboard will be reconnected.
368 
369  int ret = This->session();
370 #ifdef CONFIG_NXWM_KEYBOARD_USBHOST
371  if (ret < 0)
372  {
373  ferr("ERROR: CKeyboard::session() returned %d\n", ret);
374  }
375 #else
376  // No errors from session() are expected
377 
378  DEBUGASSERT(ret == OK);
379  UNUSED(ret);
380 #endif
381 
382  // Close the keyboard device
383 
384  (void)std::close(This->m_kbdFd);
385  This->m_kbdFd = -1;
386  }
387 
388  // We should get here only if we were asked to terminate via
389  // m_state = LISTENER_STOPREQUESTED (or perhaps if some irrecoverable
390  // error has occurred).
391 
392  ginfo("Listener exiting\n");
394  return (FAR void *)0;
395 }
enum EListenerState m_state
Definition: ckeyboard.hxx:92
CKeyboard(NXWidgets::CNxServer *server)
Definition: ckeyboard.cxx:71
int session(void)
Definition: ckeyboard.cxx:247
NXHANDLE getServer(void)
Definition: cnxserver.hxx:133
static FAR void * listener(FAR void *arg)
Definition: ckeyboard.cxx:325
bool start(void)
Definition: ckeyboard.cxx:112
NXWidgets::CNxServer * m_server
Definition: ckeyboard.hxx:89
int open(void)
Definition: ckeyboard.cxx:174
pthread_t m_thread
Definition: ckeyboard.hxx:91