diff options
author | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2011-01-16 19:08:16 +0000 |
---|---|---|
committer | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2011-01-16 19:08:16 +0000 |
commit | f66aa23286252a911e122349249b2def1d9c5810 (patch) | |
tree | e04f35197a66308d724047934187bbabc1b57457 /nuttx/drivers/usbhost/usbhost_hidkbd.c | |
parent | b7145b509d269f4a5ffcecad9a969c6a0a480f20 (diff) | |
download | nuttx-f66aa23286252a911e122349249b2def1d9c5810.tar.gz nuttx-f66aa23286252a911e122349249b2def1d9c5810.tar.bz2 nuttx-f66aa23286252a911e122349249b2def1d9c5810.zip |
More HID keyboard support
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3255 42af7a65-404d-4744-a932-0658087f49c3
Diffstat (limited to 'nuttx/drivers/usbhost/usbhost_hidkbd.c')
-rw-r--r-- | nuttx/drivers/usbhost/usbhost_hidkbd.c | 668 |
1 files changed, 465 insertions, 203 deletions
diff --git a/nuttx/drivers/usbhost/usbhost_hidkbd.c b/nuttx/drivers/usbhost/usbhost_hidkbd.c index 7cdc41b0b..cf07ccc4e 100644 --- a/nuttx/drivers/usbhost/usbhost_hidkbd.c +++ b/nuttx/drivers/usbhost/usbhost_hidkbd.c @@ -43,6 +43,7 @@ #include <stdbool.h> #include <stdio.h> #include <stdlib.h> +#include <unistd.h> #include <string.h> #include <poll.h> #include <semaphore.h> @@ -53,7 +54,6 @@ #include <nuttx/fs.h> #include <nuttx/arch.h> -#include <nuttx/wqueue.h> #include <nuttx/usb/usb.h> #include <nuttx/usb/usbhost.h> @@ -67,32 +67,32 @@ * Pre-processor Definitions ****************************************************************************/ /* Configuration ************************************************************/ -/* Worker thread support is required */ +/* This determines how often the USB keyboard will be polled in units of + * of microseconds. The default is 100MS. + */ -#ifndef CONFIG_SCHED_WORKQUEUE -# error "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)" +#ifndef CONFIG_HIDKBD_POLLUSEC +# define CONFIG_HIDKBD_POLLUSEC (100*1000) #endif -/* This determines how often the USB keyboard will be polled in units of - * of clock ticks. The default is 100MS. - */ +/* Signals must not be disabled as they are needed by usleep */ -#ifndef CONFIG_HIDKBD_POLLTICKS - /* The value CLK_TCK gives the frequency in HZ of the system timer. This - * is, the number of ticks in one second. So one tenth of this would give - * is the number of ticks required for a 100MS delay between polls. - */ +/* Provide some default values for other configuration settings */ -# define CONFIG_HIDKBD_POLLTICKS (CLK_TCK/10) +#ifndef CONFIG_HIDKBD_DEFPRIO +# define CONFIG_HIDKBD_DEFPRIO 50 +#endif +#ifndef CONFIG_HIDKBD_STACKSIZE +# define CONFIG_HIDKBD_STACKSIZE 1024 #endif /* Driver support ***********************************************************/ -/* This format is used to construct the /dev/sd[n] device driver path. It +/* 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/sd%c" -#define DEV_NAMELEN 10 +#define DEV_FORMAT "/dev/kbd%c" +#define DEV_NAMELEN 11 /* Used in usbhost_cfgdesc() */ @@ -107,8 +107,8 @@ * Private Types ****************************************************************************/ -/* This structure contains the internal, private state of the USB host mass - * storage class. +/* This structure contains the internal, private state of the USB host + * keyboard storage class. */ struct usbhost_state_s @@ -121,15 +121,16 @@ struct usbhost_state_s struct usbhost_driver_s *drvr; - /* The remainder of the fields are provide o the mass storage class */ + /* The remainder of the fields are provide o the keyboard class driver */ - char sdchar; /* Character identifying the /dev/sd[n] device */ + 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 */ int16_t crefs; /* Reference count on the driver instance */ sem_t exclsem; /* Used to maintain mutual exclusive access */ - struct work_s work; /* For interacting with the worker thread */ - FAR uint8_t *tdbuffer; /* The allocated transfer descriptor buffer */ - size_t tdbuflen; /* Size of the allocated transfer buffer */ + 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 */ /* Endpoints: * EP0 (Control): @@ -167,10 +168,10 @@ 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, char *devname); -/* Worker thread actions */ +/* Keyboard polling thread */ -static void usbhost_kbdpoll(FAR void *arg); -static void usbhost_destroy(FAR void *arg); +static void usbhost_destroy(FAR struct usbhost_state_s *priv); +static int usbhost_kbdpoll(int argc, char *argv[]); /* Helpers for usbhost_connect() */ @@ -205,12 +206,14 @@ static int usbhost_disconnected(FAR struct usbhost_class_s *class); /* Driver methods. We export the keyboard as a standard character driver */ -static ssize_t usbhost_read(FAR struct file *filp, +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 *filp, +static ssize_t usbhost_write(FAR struct file *filep, FAR const char *buffer, size_t len); #ifndef CONFIG_DISABLE_POLL -static int usbhost_poll(FAR struct file *filp, FAR struct pollfd *fds, +static int usbhost_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup); #endif @@ -219,7 +222,7 @@ static int usbhost_poll(FAR struct file *filp, FAR struct pollfd *fds, ****************************************************************************/ /* This structure provides the registry entry ID informatino that will be - * used to associate the USB host mass storage class to a connected USB + * used to associate the USB host keyboard class driver to a connected USB * device. */ @@ -244,8 +247,8 @@ static struct usbhost_registry_s g_skeleton = static const struct file_operations usbhost_fops = { - 0, /* open */ - 0, /* close */ + usbhost_open, /* open */ + usbhost_close, /* close */ usbhost_read, /* read */ usbhost_write, /* write */ 0, /* seek */ @@ -255,10 +258,16 @@ static const struct file_operations usbhost_fops = #endif }; -/* This is a bitmap that is used to allocate device names /dev/sda-z. */ +/* 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 */ + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -361,7 +370,7 @@ static int usbhost_allocdevno(FAR struct usbhost_state_s *priv) if ((g_devinuse & bitno) == 0) { g_devinuse |= bitno; - priv->sdchar = 'a' + devno; + priv->devchar = 'a' + devno; irqrestore(flags); return OK; } @@ -373,7 +382,7 @@ static int usbhost_allocdevno(FAR struct usbhost_state_s *priv) static void usbhost_freedevno(FAR struct usbhost_state_s *priv) { - int devno = 'a' - priv->sdchar; + int devno = 'a' - priv->devchar; if (devno >= 0 && devno < 26) { @@ -385,80 +394,7 @@ static void usbhost_freedevno(FAR struct usbhost_state_s *priv) static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv, char *devname) { - (void)snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->sdchar); -} - -/**************************************************************************** - * Name: usbhost_kbdpoll - * - * Description: - * Periodically check for new keyboard data. - * - * Input Parameters: - * arg - A reference to the class instance to be destroyed. - * - * Returned Values: - * None - * - ****************************************************************************/ - -static void usbhost_kbdpoll(FAR void *arg) -{ - FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)arg; - irqstate_t flags; -#ifdef CONFIG_DEBUG_USB - static unsigned int npolls = 0; -#endif - int ret; - - /* Poll the keyboard */ -#warning "Missing logic" - - /* Setup for the next poll. We have to be careful here because the work - * structure may be used by the interrupt handler if the USB device is - * disconnected. - */ - - flags = irqsave(); - - /* Is the device still connected? If not, we do not reschedule any further - * polling of the device. - */ - - if (priv->disconnected) - { - udbg("Keyboard removed, polling halted\n"); - } - else - { - /* Otherwise, just setup the next poll. */ - - ret = work_queue(&priv->work, usbhost_kbdpoll, priv, CONFIG_HIDKBD_POLLTICKS); - if (ret != 0) - { - udbg("ERROR: Failed to re-schedule keyboard poll: %d\n", ret); - } - - /* If USB debug is on, then provide some periodic indication that - * polling is still happening. - */ - -#ifdef CONFIG_DEBUG_USB - npolls++; -#endif - } - irqrestore(flags); - - /* If USB debug is on, then provide some periodic indication that - * polling is still happening. - */ - -#ifdef CONFIG_DEBUG_USB - if (!priv->disconnected && (npolls & ~31) == 0) - { - udbg("Still polling: %d\n", npolls); - } -#endif + (void)snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->devchar); } /**************************************************************************** @@ -477,9 +413,8 @@ static void usbhost_kbdpoll(FAR void *arg) * ****************************************************************************/ -static void usbhost_destroy(FAR void *arg) +static void usbhost_destroy(FAR struct usbhost_state_s *priv) { - FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)arg; char devname[DEV_NAMELEN]; DEBUGASSERT(priv != NULL); @@ -529,6 +464,191 @@ static void usbhost_destroy(FAR void *arg) } /**************************************************************************** + * Name: usbhost_dumprpt + * + * Description: + * Dump the interesting context of the keyboard report that we just + * received. + * + * Input Parameters: + * arg - A reference to the class instance to be destroyed. + * + * Returned Values: + * None + * + ****************************************************************************/ + +#if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_VERBOSE) +static inline void usbhost_dumprpt(uint8_t *buffer) +{ + struct usbhid_kbdreport_s *rpt = (struct usbhid_kbdreport_s *)buffer; + int i; + for (i = 0; i < 6; i++) + { + if (rpt->key[i]) + { + uvdbg("Key %d: %08x modifier: %08x\n", rpt->key[i], rpt->modifier); + } + } +} +#else +# define usbhost_dumprpt(buffer) +#endif + +/**************************************************************************** + * Name: usbhost_kbdpoll + * + * Description: + * Periodically check for new keyboard data. + * + * Input Parameters: + * arg - A reference to the class instance to be destroyed. + * + * Returned Values: + * None + * + ****************************************************************************/ + +static int usbhost_kbdpoll(int argc, char *argv[]) +{ + FAR struct usbhost_state_s *priv; + FAR struct usb_ctrlreq_s *ctrlreq; +#ifdef CONFIG_DEBUG_USB + static unsigned int npolls = 0; +#endif + static unsigned int nerrors; + int ret; + + uvdbg("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 incremented here. Therefore, we know + * that the driver data structure will remain stable while this thread is + * running. + */ + + priv = g_priv; + DEBUGASSERT(priv != NULL); + + priv->polling = true; + priv->crefs++; + usbhost_givesem(&g_syncsem); + sleep(1); + + /* Loop here until the device is disconnected */ + + uvdbg("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. + */ + + usbhost_takesem(&priv->exclsem); + + /* Format the hid report request: + * + * bmRequestType 10000001 + * bRequest GET_DESCRIPTOR (0x06) + * wValue Descriptor Type and Descriptor 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_RECIPIENT_INTERFACE; + ctrlreq->req = USB_REQ_GETDESCRIPTOR; + usbhost_putle16(ctrlreq->value, (USBHID_DESCTYPE_REPORT << 8)); + usbhost_putle16(ctrlreq->index, 0); + usbhost_putle16(ctrlreq->len, 8); + + /* Send the report */ + + ret = DRVR_CTRLIN(priv->drvr, ctrlreq, priv->tbuffer); + usbhost_givesem(&priv->exclsem); + + if (ret != OK) + { + nerrors++; + udbg("ERROR: GETDESCRIPTOR/REPORT, DRVR_CTRLIN returned: %d/%d\n", + ret, nerrors); + + if (nerrors > 200) + { + udbg("Too man errors... aborting: %d\n", nerrors); + break; + } + } + else + { + /* If debug is enabled, then dump the interesting poarts of the + * report that we just received. + */ + + usbhost_dumprpt(priv->tbuffer); + + /* Add the newly recevied keystrokes to our internal buffer */ +#warning "Missing logic" + } + + /* If USB debug is on, then provide some periodic indication that + * polling is still happening. + */ + +#ifdef CONFIG_DEBUG_USB + npolls++; + if (!(npolls & ~31) == 0) + { + udbg("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. + */ + + usleep(CONFIG_HIDKBD_POLLUSEC); + } + + /* 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_takesem(&priv->exclsem); + + /* Indicate that we are no longer running and decrement the reference + * count help 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. + */ + + udbg("Keyboard removed, polling halted\n"); + priv->polling = false; + if (--priv->crefs < 2) + { + /* Destroy the instance (while we hold the semaphore!) */ + + usbhost_destroy(priv); + } + else + { + /* No, we will destroy the driver instance when it is finally closed */ + + usbhost_givesem(&priv->exclsem); + } + return 0; +} + +/**************************************************************************** * Name: usbhost_cfgdesc * * Description: @@ -769,6 +889,7 @@ static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv, 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 */ @@ -787,63 +908,63 @@ static inline int usbhost_devinit(FAR struct usbhost_state_s *priv) priv->crefs++; DEBUGASSERT(priv->crefs == 2); - /* Setup a period worker thread event to poll the USB device. */ - - ret = work_queue(&priv->work, usbhost_kbdpoll, priv, CONFIG_HIDKBD_POLLTICKS); - - /* Register the driver */ - - if (ret == OK) - { - char devname[DEV_NAMELEN]; + /* Start a worker task to poll the USB device. It would be nice to used the + * 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). + */ - uvdbg("Register driver\n"); - usbhost_mkdevname(priv, devname); - (void)register_driver(devname, &usbhost_fops, 0666, NULL); - } + uvdbg("user_start: Start poll task\n"); - /* Check if we successfully initialized. We now have to be concerned - * about asynchronous modification of crefs because the driver has - * been registerd. + /* The inputs to a task started by task_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). */ - if (ret == OK) + usbhost_takesem(&g_exclsem); + g_priv = priv; + +#ifndef CONFIG_CUSTOM_STACK + priv->pollpid = task_create("usbhost", CONFIG_HIDKBD_DEFPRIO, + CONFIG_HIDKBD_STACKSIZE, + (main_t)usbhost_kbdpoll, (const char **)NULL); +#else + priv->pollpid = task_create("usbhost", CONFIG_HIDKBD_DEFPRIO, + (main_t)hidkbd_waiter, (const char **)NULL); +#endif + if (priv->pollpid == ERROR) { - usbhost_takesem(&priv->exclsem); - DEBUGASSERT(priv->crefs >= 2); + /* Failed to started the poll thread... probably due to memory resources */ - /* Handle a corner case where (1) open() has been called so the - * reference count is > 2, but the device has been disconnected. - * In this case, the class instance needs to persist until close() - * is called. - */ + usbhost_givesem(&g_exclsem); + ret = -ENOMEM; + goto errout; + } - if (priv->crefs <= 2 && priv->disconnected) - { - /* We don't have to give the semaphore because it will be - * destroyed when usb_destroy is called. - */ - - ret = -ENODEV; - } - else - { - /* Ready for normal operation as a character device driver */ + /* Now wait for the poll task to get properly initialized */ - uvdbg("Successfully initialized\n"); - priv->crefs--; - usbhost_givesem(&priv->exclsem); - } - } + usbhost_takesem(&g_syncsem); + usbhost_givesem(&g_exclsem); - /* Disconnect on any errors detected during volume initialization */ + /* Register the driver */ - if (ret != OK) - { - udbg("ERROR! Aborting: %d\n", ret); - usbhost_destroy(priv); - } + uvdbg("Register driver\n"); + usbhost_mkdevname(priv, devname); + ret = register_driver(devname, &usbhost_fops, 0666, NULL); + + /* We now have to be concerned about asynchronous modification of crefs + * because the driver has been registerd. + */ +errout: + usbhost_takesem(&priv->exclsem); + priv->crefs--; + usbhost_givesem(&priv->exclsem); return ret; } @@ -936,7 +1057,7 @@ static void usbhost_putle32(uint8_t *dest, uint32_t val) * Name: usbhost_tdalloc * * Description: - * Allocate transfer descriptor memory. + * Allocate transfer buffer memory. * * Input Parameters: * priv - A reference to the class instance. @@ -949,15 +1070,15 @@ static void usbhost_putle32(uint8_t *dest, uint32_t val) static inline int usbhost_tdalloc(FAR struct usbhost_state_s *priv) { - DEBUGASSERT(priv && priv->tdbuffer == NULL); - return DRVR_ALLOC(priv->drvr, &priv->tdbuffer, &priv->tdbuflen); + DEBUGASSERT(priv && priv->tbuffer == NULL); + return DRVR_ALLOC(priv->drvr, &priv->tbuffer, &priv->tbuflen); } /**************************************************************************** * Name: usbhost_tdfree * * Description: - * Free transfer descriptor memory. + * Free transfer buffer memory. * * Input Parameters: * priv - A reference to the class instance. @@ -973,12 +1094,12 @@ static inline int usbhost_tdfree(FAR struct usbhost_state_s *priv) int result = OK; DEBUGASSERT(priv); - if (priv->tdbuffer) + if (priv->tbuffer) { DEBUGASSERT(priv->drvr); - result = DRVR_FREE(priv->drvr, priv->tdbuffer); - priv->tdbuffer = NULL; - priv->tdbuflen = 0; + result = DRVR_FREE(priv->drvr, priv->tbuffer); + priv->tbuffer = NULL; + priv->tbuflen = 0; } return result; } @@ -1049,9 +1170,7 @@ static FAR struct usbhost_class_s *usbhost_create(FAR struct usbhost_driver_s *d priv->drvr = drvr; - /* NOTE: We do not yet know the geometry of the USB mass storage device */ - - /* Return the instance of the USB mass storage class */ + /* Return the instance of the USB keyboard class driver */ return &priv->class; } @@ -1123,6 +1242,34 @@ static int usbhost_connect(FAR struct usbhost_class_s *class, } } + /* Disconnect on any errors detected during initialization. */ + + if (ret != OK) + { + priv->disconnected = true; + + /* Is the polling task still running? If so, then ask it politely to + * stop and release its reference count. + */ + + while (priv->polling) + { + (void)kill(priv->pollpid, SIGALRM); + usleep(500*1000); + } + + /* The following operations when crefs == 1 are safe because we know + * that there is no outstanding open references to the driver. + */ + + if (priv->crefs <= 1) + { + /* Destroy the class instance */ + + usbhost_destroy(priv); + } + } + return ret; } @@ -1151,62 +1298,135 @@ static int usbhost_connect(FAR struct usbhost_class_s *class, static int usbhost_disconnected(struct usbhost_class_s *class) { FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)class; - irqstate_t flags; DEBUGASSERT(priv != NULL); - /* Set an indication to any users of the mass storage device that the device + /* Set an indication to any users of the keyboard device that the device * is no longer available. */ - flags = irqsave(); priv->disconnected = true; + ullvdbg("Disconnected\n"); - /* Now check the number of references on the class instance. If it is one, - * then we can free the class instance now. Otherwise, we will have to - * wait until the holders of the references free them by closing the - * driver. + /* Signal the keyboard polling task. When that task wakes up, it will + * decrement the reference count and, perhaps, destroy the class instance. */ - ullvdbg("crefs: %d\n", priv->crefs); - if (priv->crefs == 1) + (void)kill(priv->pollpid, SIGALRM); + return OK; +} + +/**************************************************************************** + * Character driver methods + ****************************************************************************/ +/**************************************************************************** + * 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; + + uvdbg("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->crefs > 0 && priv->crefs < USBHOST_MAX_CREFS); + usbhost_takesem(&priv->exclsem); + + /* Check if the keyboard device is still connected. We need to disable + * interrupts momentarily to assure that there are no asynchronous disconnect + * events. + */ + + flags = irqsave(); + if (priv->disconnected) { - /* Destroy the class instance. If we are executing from an interrupt - * handler, then defer the destruction to the worker thread. - * Otherwise, destroy the instance now. + /* 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. */ - if (up_interrupt_context()) - { - /* Destroy the instance on the worker thread. */ + ret = -ENODEV; + } + else + { + /* Otherwise, just increment the reference count on the driver */ + + priv->crefs++; + ret = OK; + } + irqrestore(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; - uvdbg("Queuing destruction: worker %p->%p\n", priv->work.worker, usbhost_destroy); + uvdbg("Entry\n"); + DEBUGASSERT(filep && filep->f_inode); + inode = filep->f_inode; + priv = inode->i_private; - /* Cancel the period polling thread */ + /* Decrement the reference count on the driver */ - (void)work_cancel(&priv->work); - DEBUGASSERT(priv->work.worker == NULL); + DEBUGASSERT(priv->crefs > 1); + usbhost_takesem(&priv->exclsem); + priv->crefs--; - /* Then schedule the destruction */ + /* Release the semaphore. The following operations when crefs == 1 are + * safe because we know that there is no outstanding open references to + * the driver. + */ - (void)work_queue(&priv->work, usbhost_destroy, priv, 0); - } - else - { - /* Do the work now */ + usbhost_givesem(&priv->exclsem); - usbhost_destroy(priv); - } + /* We need to disable interrupts momentarily to assure that there are + * no asynchronous disconnect events. + */ + + flags = irqsave(); + + /* Check if the USB keyboard device is still connected. If the device is + * not connected and the reference count just decremented to one, then + * unregister then free the driver class instance. + */ + + if (priv->crefs <= 1 && priv->disconnected) + { + /* Destroy the class instance */ + + usbhost_destroy(priv); } - irqrestore(flags); + irqrestore(flags); return OK; } /**************************************************************************** - * Character driver methods - ****************************************************************************/ -/**************************************************************************** * Name: usbhost_read * * Description: @@ -1214,8 +1434,45 @@ static int usbhost_disconnected(struct usbhost_class_s *class) * ****************************************************************************/ -static ssize_t usbhost_read(FAR struct file *filp, FAR char *buffer, size_t len) +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; + irqstate_t flags; + int ret; + + uvdbg("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); + usbhost_takesem(&priv->exclsem); + + /* Check if the keyboard is still connected. We need to disable interrupts + * momentarily to assure that there are no asynchronous disconnect events. + */ + + flags = irqsave(); + if (priv->disconnected) + { + /* No... the driver is no longer bound to the class. That means that + * the USB keybaord is no longer connected. Refuse any further attempts + * to access the driver. + */ + + ret = -ENODEV; + } + else + { + /* Read data from our internal buffer of received characters */ +#warning "Missing logic" + } + irqrestore(flags); + + usbhost_givesem(&priv->exclsem); return 0; /* Return EOF for now */ } @@ -1227,9 +1484,11 @@ static ssize_t usbhost_read(FAR struct file *filp, FAR char *buffer, size_t len) * ****************************************************************************/ -static ssize_t usbhost_write(FAR struct file *filp, FAR const char *buffer, size_t len) +static ssize_t usbhost_write(FAR struct file *filep, FAR const char *buffer, size_t len) { - return len; /* Say that everything was written for now */ + /* We won't try to write to the keyboard */ + + return -ENOSYS; } /**************************************************************************** @@ -1241,7 +1500,7 @@ static ssize_t usbhost_write(FAR struct file *filp, FAR const char *buffer, size ****************************************************************************/ #ifndef CONFIG_DISABLE_POLL -static int usbhost_poll(FAR struct file *filp, FAR struct pollfd *fds, +static int usbhost_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) { if (setup) @@ -1281,6 +1540,9 @@ int usbhost_kbdinit(void) { /* Perform any one-time initialization of the class implementation */ + sem_init(&g_exclsem, 0, 1); + sem_init(&g_syncsem, 0, 0); + /* Advertise our availability to support (certain) devices */ return usbhost_registerclass(&g_skeleton); |