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.
This commit is contained in:
parent
b058f37353
commit
18d196e968
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
@ -978,133 +1024,79 @@ static inline void usbhost_encodescancode(FAR struct usbhost_state_s *priv,
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: usbhost_kbdpoll
|
||||
* Name: usbhost_get_capslock
|
||||
*
|
||||
* Description:
|
||||
* Periodically check for new keyboard data.
|
||||
* Get the global value of caps lock.
|
||||
*
|
||||
* Input Parameters:
|
||||
* arg - A reference to the class instance to be destroyed.
|
||||
* 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 int usbhost_kbdpoll(int argc, char *argv[])
|
||||
static inline void usbhost_toggle_capslock(void)
|
||||
{
|
||||
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;
|
||||
flags = enter_critical_section();
|
||||
g_caps_lock = !g_caps_lock;
|
||||
leave_critical_section(flags);
|
||||
}
|
||||
|
||||
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.
|
||||
/****************************************************************************
|
||||
* Name: usbhost_extract_keys
|
||||
*
|
||||
* 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;
|
||||
nxsem_post(&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 = nxmutex_lock(&priv->lock);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Format the HID report request:
|
||||
* Description:
|
||||
* Check if the key has a special function encoding and, if it does, add
|
||||
* the encoded value to the user buffer.
|
||||
*
|
||||
* bmRequestType 10100001
|
||||
* bRequest GET_REPORT (0x01)
|
||||
* wValue Report Type and Report Index
|
||||
* wIndex Interface Number
|
||||
* wLength Descriptor Length
|
||||
* Data Descriptor Data
|
||||
*/
|
||||
* Input Parameters:
|
||||
* priv - Driver internal state
|
||||
* scancode - Scan code to be mapped.
|
||||
* modifier - Ctrl, Alt, Shift, GUI modifier bits
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
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);
|
||||
nxmutex_unlock(&priv->lock);
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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;
|
||||
|
||||
/* Success, reset the error counter */
|
||||
|
||||
nerrors = 0;
|
||||
int ret;
|
||||
bool newstate;
|
||||
|
||||
/* Add the newly received keystrokes to our internal buffer */
|
||||
|
||||
@ -1136,12 +1128,12 @@ static int usbhost_kbdpoll(int argc, char *argv[])
|
||||
|
||||
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]
|
||||
&& 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
|
||||
)
|
||||
{
|
||||
@ -1157,6 +1149,11 @@ static int usbhost_kbdpoll(int argc, char *argv[])
|
||||
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.
|
||||
*/
|
||||
@ -1199,7 +1196,7 @@ static int usbhost_kbdpoll(int argc, char *argv[])
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_HIDKBD_NODEBOUNCE
|
||||
lastkey[i] = rpt->key[i];
|
||||
priv->lastkey[i] = rpt->key[i];
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1213,7 +1210,7 @@ static int usbhost_kbdpoll(int argc, char *argv[])
|
||||
* POLLIN event.
|
||||
*/
|
||||
|
||||
if (empty)
|
||||
if (priv->empty)
|
||||
{
|
||||
poll_notify(priv->fds, CONFIG_HIDKBD_NPOLLWAITERS, POLLIN);
|
||||
}
|
||||
@ -1229,10 +1226,276 @@ static int usbhost_kbdpoll(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
empty = newstate;
|
||||
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
|
||||
*
|
||||
* 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;
|
||||
irqstate_t flags;
|
||||
|
||||
#if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_INFO)
|
||||
unsigned int npolls = 0;
|
||||
#endif
|
||||
unsigned int nerrors = 0;
|
||||
useconds_t delay;
|
||||
int ret;
|
||||
uint8_t leds;
|
||||
|
||||
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);
|
||||
|
||||
priv->polling = true;
|
||||
nxsem_post(&priv->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 = nxmutex_lock(&priv->lock);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Keep the Caps Lock LED in sync */
|
||||
|
||||
if (priv->sync_led)
|
||||
{
|
||||
priv->sync_led = false;
|
||||
priv->caps_lock = usbhost_get_capslock();
|
||||
|
||||
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 = 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
|
||||
* 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)
|
||||
{
|
||||
/* Success, reset the error counter */
|
||||
|
||||
nerrors = 0;
|
||||
|
||||
/* Add the newly received keystrokes to our internal buffer */
|
||||
|
||||
ret = usbhost_extract_keys(priv);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Make sure the LED is in sync */
|
||||
|
||||
if (priv->caps_lock != usbhost_get_capslock())
|
||||
{
|
||||
priv->sync_led = true;
|
||||
}
|
||||
}
|
||||
#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 */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user