From 2b8fe6709be212c4454909b2b430cbb2de9d553d Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Fri, 28 Nov 2014 19:59:27 -0600 Subject: [PATCH] Add an analog joystick driver. Initial checkin is only a little more of a clone of the discrete joystick driver and is as-of-yet untested --- drivers/input/Kconfig | 18 + drivers/input/Make.defs | 4 + drivers/input/ajoystick.c | 879 ++++++++++++++++++++++++++++++++ drivers/input/djoystick.c | 14 +- include/nuttx/input/ajoystick.h | 37 +- 5 files changed, 940 insertions(+), 12 deletions(-) create mode 100644 drivers/input/ajoystick.c diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 06210eabf7..12717e2f65 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -340,3 +340,21 @@ config DJOYSTICK_NPOLLWAITERS depends on !DISABLE_POLL endif # DJOYSTICK + +config AJOYSTICK + bool "Analog Joystick" + default n + ---help--- + Enable standard analog joystick upper half driver. An analog + joystick refers to a joystick that provides position data as an + integer value that might have been obtained through DACs. + Discrete button inputs are also supported. + +if AJOYSTICK + +config AJOYSTICK_NPOLLWAITERS + int "Max Number of Poll Waiters" + default 2 + depends on !DISABLE_POLL + +endif # AJOYSTICK diff --git a/drivers/input/Make.defs b/drivers/input/Make.defs index b13cbf2600..dc01bda886 100644 --- a/drivers/input/Make.defs +++ b/drivers/input/Make.defs @@ -75,6 +75,10 @@ ifeq ($(CONFIG_DJOYSTICK),y) CSRCS += djoystick.c endif +ifeq ($(CONFIG_AJOYSTICK),y) + CSRCS += ajoystick.c +endif + # Include input device driver build support DEPPATH += --dep-path input diff --git a/drivers/input/ajoystick.c b/drivers/input/ajoystick.c new file mode 100644 index 0000000000..4ab7e6dd63 --- /dev/null +++ b/drivers/input/ajoystick.c @@ -0,0 +1,879 @@ +/**************************************************************************** + * drivers/ajoystick.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure provides the state of one discrete joystick driver */ + +struct ajoy_upperhalf_s +{ + /* Saved binding to the lower half discrete joystick driver */ + + FAR const struct ajoy_lowerhalf_s *au_lower; + + ajoy_buttonset_t au_enabled; /* Set of currently enabled button interrupts */ + ajoy_buttonset_t au_sample; /* Last sampled button states */ + sem_t au_exclsem; /* Supports exclusive access to the device */ + + /* The following is a singly linked list of open references to the + * joystick device. + */ + + FAR struct ajoy_open_s *au_open; +}; + +/* This structure describes the state of one open joystick driver instance */ + +struct ajoy_open_s +{ + /* Supports a singly linked list */ + + FAR struct ajoy_open_s *ao_flink; + + /* The following will be true if we are closing */ + + volatile bool ao_closing; + +#ifndef CONFIG_DISABLE_SIGNALS + /* Joystick event notification information */ + + pid_t ao_pid; + struct ajoy_notify_s ao_notify; +#endif + +#ifndef CONFIG_DISABLE_POLL + /* Poll event information */ + + struct ajoy_pollevents_s ao_pollevents; + + /* The following is a list if poll structures of threads waiting for + * driver events. + */ + + FAR struct pollfd *ao_fds[CONFIG_AJOYSTICK_NPOLLWAITERS]; +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Semaphore helpers */ + +static inline int ajoy_takesem(sem_t *sem); +#define ajoy_givesem(s) sem_post(s); + +/* Sampling and Interrupt handling */ + +#if !defined(CONFIG_DISABLE_POLL) || !defined(CONFIG_DISABLE_SIGNALS) +static void ajoy_enable(FAR struct ajoy_upperhalf_s *priv); +static void ajoy_interrupt(FAR const struct ajoy_lowerhalf_s *lower, + FAR void *arg); +#endif + +/* Sampling */ + +static void ajoy_sample(FAR struct ajoy_upperhalf_s *priv); + +/* Character driver methods */ + +static int ajoy_open(FAR struct file *filep); +static int ajoy_close(FAR struct file *filep); +static ssize_t ajoy_read(FAR struct file *, FAR char *, size_t); +static int ajoy_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); +#ifndef CONFIG_DISABLE_POLL +static int ajoy_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations ajoy_fops = +{ + ajoy_open, /* open */ + ajoy_close, /* close */ + ajoy_read, /* read */ + 0, /* write */ + 0, /* seek */ + ajoy_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , ajoy_poll /* poll */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ajoy_takesem + ****************************************************************************/ + +static inline int ajoy_takesem(sem_t *sem) +{ + /* Take a count from the semaphore, possibly waiting */ + + if (sem_wait(sem) < 0) + { + /* EINTR is the only error that we expect */ + + int errcode = get_errno(); + DEBUGASSERT(errcode == EINTR); + return -errcode; + } + + return OK; +} + +/**************************************************************************** + * Name: ajoy_enable + ****************************************************************************/ + +#if !defined(CONFIG_DISABLE_POLL) || !defined(CONFIG_DISABLE_SIGNALS) +static void ajoy_enable(FAR struct ajoy_upperhalf_s *priv) +{ + FAR const struct ajoy_lowerhalf_s *lower = priv->au_lower; + FAR struct ajoy_open_s *opriv; + ajoy_buttonset_t press; + ajoy_buttonset_t release; + irqstate_t flags; +#ifndef CONFIG_DISABLE_POLL + int i; +#endif + + DEBUGASSERT(priv && priv->au_lower); + lower = priv->au_lower; + + /* This routine is called both task level and interrupt level, so + * interrupts must be disabled. + */ + + flags = irqsave(); + + /* Visit each opened reference to the device */ + + press = 0; + release = 0; + + for (opriv = priv->au_open; opriv; opriv = opriv->ao_flink) + { +#ifndef CONFIG_DISABLE_POLL + /* Are there any poll waiters? */ + + for (i = 0; i < CONFIG_AJOYSTICK_NPOLLWAITERS; i++) + { + if (opriv->ao_fds[i]) + { + /* Yes.. OR in the poll event buttons */ + + press |= opriv->ao_pollevents.ap_press; + release |= opriv->ao_pollevents.ap_release; + break; + } + } +#endif + +#ifndef CONFIG_DISABLE_SIGNALS + /* OR in the signal events */ + + press |= opriv->ao_notify.an_press; + release |= opriv->ao_notify.an_release; +#endif + } + + /* Enable/disable button interrupts */ + + DEBUGASSERT(lower->al_enable); + if (press != 0 || release != 0) + { + /* Enable interrupts with the new button set */ + + lower->al_enable(lower, press, release, + (ajoy_handler_t)ajoy_interrupt, priv); + } + else + { + /* Disable further interrupts */ + + lower->al_enable(lower, 0, 0, NULL, NULL); + } + + irqrestore(flags); +} +#endif + +/**************************************************************************** + * Name: ajoy_interrupt + ****************************************************************************/ + +#if !defined(CONFIG_DISABLE_POLL) || !defined(CONFIG_DISABLE_SIGNALS) +static void ajoy_interrupt(FAR const struct ajoy_lowerhalf_s *lower, + FAR void *arg) +{ + FAR struct ajoy_upperhalf_s *priv = (FAR struct ajoy_upperhalf_s *)arg; + + DEBUGASSERT(priv); + + /* Process the next sample */ + + ajoy_sample(priv); +} +#endif + +/**************************************************************************** + * Name: ajoy_sample + ****************************************************************************/ + +static void ajoy_sample(FAR struct ajoy_upperhalf_s *priv) +{ + FAR const struct ajoy_lowerhalf_s *lower = priv->au_lower; + FAR struct ajoy_open_s *opriv; + ajoy_buttonset_t sample; +#if !defined(CONFIG_DISABLE_POLL) || !defined(CONFIG_DISABLE_SIGNALS) + ajoy_buttonset_t change; + ajoy_buttonset_t press; + ajoy_buttonset_t release; +#endif + irqstate_t flags; +#ifndef CONFIG_DISABLE_POLL + int i; +#endif + + DEBUGASSERT(priv && priv->au_lower); + lower = priv->au_lower; + + /* This routine is called both task level and interrupt level, so + * interrupts must be disabled. + */ + + flags = irqsave(); + + /* Sample the new button state */ + + DEBUGASSERT(lower->al_buttons); + sample = lower->al_buttons(lower); + +#if !defined(CONFIG_DISABLE_POLL) || !defined(CONFIG_DISABLE_SIGNALS) + /* Determine which buttons have been newly pressed and which have been + * newly released. + */ + + change = sample ^ priv->au_sample; + press = change & sample; + + DEBUGASSERT(lower->al_supported); + release = change & (lower->al_supported(lower) & ~sample); + + /* Visit each opened reference to the device */ + + for (opriv = priv->au_open; opriv; opriv = opriv->ao_flink) + { +#ifndef CONFIG_DISABLE_POLL + /* Have any poll events occurred? */ + + if ((press & opriv->ao_pollevents.ap_press) != 0 || + (release & opriv->ao_pollevents.ap_release) != 0) + { + /* Yes.. Notify all waiters */ + + for (i = 0; i < CONFIG_AJOYSTICK_NPOLLWAITERS; i++) + { + FAR struct pollfd *fds = opriv->ao_fds[i]; + if (fds) + { + fds->revents |= (fds->events & POLLIN); + if (fds->revents != 0) + { + ivdbg("Report events: %02x\n", fds->revents); + sem_post(fds->sem); + } + } + } + } +#endif + +#ifndef CONFIG_DISABLE_SIGNALS + /* Have any signal events occurred? */ + + if ((press & opriv->ao_notify.an_press) != 0 || + (release & opriv->ao_notify.an_release) != 0) + { + /* Yes.. Signal the waiter */ + +#ifdef CONFIG_CAN_PASS_STRUCTS + union sigval value; + value.sival_int = (int)sample; + (void)sigqueue(opriv->ao_pid, opriv->ao_notify.an_signo, value); +#else + (void)sigqueue(opriv->ao_pid, opriv->ao_notify.dn.signo, + (FAR void *)sample); +#endif + } +#endif + } + + /* Enable/disable interrupt handling */ + + ajoy_enable(priv); +#endif + + priv->au_sample = sample; + irqrestore(flags); +} + +/**************************************************************************** + * Name: ajoy_open + ****************************************************************************/ + +static int ajoy_open(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct ajoy_upperhalf_s *priv; + FAR const struct ajoy_lowerhalf_s *lower; + FAR struct ajoy_open_s *opriv; +#ifndef CONFIG_DISABLE_POLL + ajoy_buttonset_t supported; +#endif + int ret; + + DEBUGASSERT(filep && filep->f_inode); + inode = filep->f_inode; + DEBUGASSERT(inode->i_private); + priv = (FAR struct ajoy_upperhalf_s *)inode->i_private; + + /* Get exclusive access to the driver structure */ + + ret = ajoy_takesem(&priv->au_exclsem); + if (ret < 0) + { + ivdbg("ERROR: ajoy_takesem failed: %d\n", ret); + return ret; + } + + /* Allocate a new open structure */ + + opriv = (FAR struct ajoy_open_s *)kmm_zalloc(sizeof(struct ajoy_open_s)); + if (!opriv) + { + ivdbg("ERROR: Failled to allocate open structure\n"); + ret = -ENOMEM; + goto errout_with_sem; + } + + /* Initialize the open structure */ + +#ifndef CONFIG_DISABLE_POLL + lower = priv->au_lower; + DEBUGASSERT(lower && lower->al_supported); + supported = lower->al_supported(lower); + + opriv->ao_pollevents.ap_press = supported; + opriv->ao_pollevents.ap_release = supported; +#endif + + /* Attach the open structure to the device */ + + opriv->ao_flink = priv->au_open; + priv->au_open = opriv; + + /* Attach the open structure to the file structure */ + + filep->f_priv = (FAR void *)opriv; + ret = OK; + +errout_with_sem: + ajoy_givesem(&priv->au_exclsem); + return ret; +} + +/**************************************************************************** + * Name: ajoy_close + ****************************************************************************/ + +static int ajoy_close(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct ajoy_upperhalf_s *priv; + FAR struct ajoy_open_s *opriv; + FAR struct ajoy_open_s *curr; + FAR struct ajoy_open_s *prev; + irqstate_t flags; + bool closing; + int ret; + + DEBUGASSERT(filep && filep->f_priv && filep->f_inode); + opriv = filep->f_priv; + inode = filep->f_inode; + DEBUGASSERT(inode->i_private); + priv = (FAR struct ajoy_upperhalf_s *)inode->i_private; + + /* Handle an improbable race conditions with the following atomic test + * and set. + * + * This is actually a pretty feeble attempt to handle this. The + * improbable race condition occurs if two different threads try to + * close the joystick driver at the same time. The rule: don't do + * that! It is feeble because we do not really enforce stale pointer + * detection anyway. + */ + + flags = irqsave(); + closing = opriv->ao_closing; + opriv->ao_closing = true; + irqrestore(flags); + + if (closing) + { + /* Another thread is doing the close */ + + return OK; + } + + /* Get exclusive access to the driver structure */ + + ret = ajoy_takesem(&priv->au_exclsem); + if (ret < 0) + { + ivdbg("ERROR: ajoy_takesem failed: %d\n", ret); + return ret; + } + + /* Find the open structure in the list of open structures for the device */ + + for (prev = NULL, curr = priv->au_open; + curr && curr != opriv; + prev = curr, curr = curr->ao_flink); + + DEBUGASSERT(curr); + if (!curr) + { + ivdbg("ERROR: Failed to find open entry\n"); + ret = -ENOENT; + goto errout_with_exclsem; + } + + /* Remove the structure from the device */ + + if (prev) + { + prev->ao_flink = opriv->ao_flink; + } + else + { + priv->au_open = opriv->ao_flink; + } + + /* And free the open structure */ + + kmm_free(opriv); + + /* Enable/disable interrupt handling */ + + ajoy_enable(priv); + ret = OK; + +errout_with_exclsem: + ajoy_givesem(&priv->au_exclsem); + return ret; +} + +/**************************************************************************** + * Name: ajoy_read + ****************************************************************************/ + +static ssize_t ajoy_read(FAR struct file *filep, FAR char *buffer, + size_t len) +{ + FAR struct inode *inode; + FAR struct ajoy_upperhalf_s *priv; + FAR const struct ajoy_lowerhalf_s *lower; + int ret; + + DEBUGASSERT(filep && filep->f_inode); + inode = filep->f_inode; + DEBUGASSERT(inode->i_private); + priv = (FAR struct ajoy_upperhalf_s *)inode->i_private; + + /* Make sure that the buffer is sufficiently large to hold at least one + * complete sample. + * + * REVISIT: Should also check buffer alignment. + */ + + if (len < sizeof(struct ajoy_sample_s)) + { + ivdbg("ERROR: buffer too small: %lu\n", (unsigned long)len); + return -EINVAL; + } + + /* Get exclusive access to the driver structure */ + + ret = ajoy_takesem(&priv->au_exclsem); + if (ret < 0) + { + ivdbg("ERROR: ajoy_takesem failed: %d\n", ret); + return ret; + } + + /* Read and return the current state of the joystick buttons */ + + lower = priv->au_lower; + DEBUGASSERT(lower && lower->al_sample); + ret = lower->al_sample(lower, (FAR struct ajoy_sample_s *)buffer); + if (ret >= 0) + { + ret = sizeof(struct ajoy_sample_s); + } + + ajoy_givesem(&priv->au_exclsem); + return (ssize_t)ret; +} + +/**************************************************************************** + * Name: ajoy_ioctl + ****************************************************************************/ + +static int ajoy_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode; + FAR struct ajoy_upperhalf_s *priv; + FAR struct ajoy_open_s *opriv; + FAR const struct ajoy_lowerhalf_s *lower; + int ret; + + DEBUGASSERT(filep && filep->f_priv && filep->f_inode); + opriv = filep->f_priv; + inode = filep->f_inode; + DEBUGASSERT(inode->i_private); + priv = (FAR struct ajoy_upperhalf_s *)inode->i_private; + + /* Get exclusive access to the driver structure */ + + ret = ajoy_takesem(&priv->au_exclsem); + if (ret < 0) + { + ivdbg("ERROR: ajoy_takesem failed: %d\n", ret); + return ret; + } + + /* Handle the ioctl command */ + + ret = -EINVAL; + switch (cmd) + { + /* Command: AJOYIOC_SUPPORTED + * Description: Report the set of button events supported by the hardware; + * Argument: A pointer to writeable integer value in which to return the + * set of supported buttons. + * Return: Zero (OK) on success. Minus one will be returned on failure + * with the errno value set appropriately. + */ + + case AJOYIOC_SUPPORTED: + { + FAR int *supported = (FAR int *)((uintptr_t)arg); + + if (supported) + { + lower = priv->au_lower; + DEBUGASSERT(lower && lower->al_supported); + + *supported = (int)lower->al_supported(lower); + ret = OK; + } + } + break; + +#ifndef CONFIG_DISABLE_POLL + /* Command: AJOYIOC_POLLEVENTS + * Description: Specify the set of button events that can cause a poll() + * to awaken. The default is all button depressions and + * all button releases (all supported buttons); + * Argument: A read-only pointer to an instance of struct + * ajoy_pollevents_s + * Return: Zero (OK) on success. Minus one will be returned on + * failure with the errno value set appropriately. + */ + + case AJOYIOC_POLLEVENTS: + { + FAR struct ajoy_pollevents_s *pollevents = + (FAR struct ajoy_pollevents_s *)((uintptr_t)arg); + + if (pollevents) + { + /* Save the poll events */ + + opriv->ao_pollevents.ap_press = pollevents->ap_press; + opriv->ao_pollevents.ap_release = pollevents->ap_release; + + /* Enable/disable interrupt handling */ + + ajoy_enable(priv); + ret = OK; + } + } + break; +#endif + +#ifndef CONFIG_DISABLE_SIGNALS + /* Command: AJOYIOC_REGISTER + * Description: Register to receive a signal whenever there is a change + * in any of the joystick discrete inputs. This feature, + * of course, depends upon interrupt GPIO support from the + * platform. + * Argument: A read-only pointer to an instance of struct + * ajoy_notify_s + * Return: Zero (OK) on success. Minus one will be returned on + * failure with the errno value set appropriately. + */ + + case AJOYIOC_REGISTER: + { + FAR struct ajoy_notify_s *notify = + (FAR struct ajoy_notify_s *)((uintptr_t)arg); + + if (notify) + { + /* Save the notification events */ + + opriv->ao_notify.an_press = notify->an_press; + opriv->ao_notify.an_release = notify->an_release; + opriv->ao_notify.an_signo = notify->an_signo; + opriv->ao_pid = getpid(); + + /* Enable/disable interrupt handling */ + + ajoy_enable(priv); + ret = OK; + } + } + break; +#endif + + default: + ivdbg("ERROR: Unrecognized command: %ld\n", cmd); + ret = -ENOTTY; + break; + } + + ajoy_givesem(&priv->au_exclsem); + return ret; +} + +/**************************************************************************** + * Name: ajoy_poll + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +static int ajoy_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup) +{ + FAR struct inode *inode; + FAR struct ajoy_upperhalf_s *priv; + FAR struct ajoy_open_s *opriv; + int ret; + int i; + + DEBUGASSERT(filep && filep->f_priv && filep->f_inode); + opriv = filep->f_priv; + inode = filep->f_inode; + DEBUGASSERT(inode->i_private); + priv = (FAR struct ajoy_upperhalf_s *)inode->i_private; + + /* Get exclusive access to the driver structure */ + + ret = ajoy_takesem(&priv->au_exclsem); + if (ret < 0) + { + ivdbg("ERROR: ajoy_takesem failed: %d\n", ret); + return ret; + } + + /* Are we setting up the poll? Or tearing it down? */ + + if (setup) + { + /* This is a request to set up the poll. Find an available + * slot for the poll structure reference + */ + + for (i = 0; i < CONFIG_AJOYSTICK_NPOLLWAITERS; i++) + { + /* Find an available slot */ + + if (!opriv->ao_fds[i]) + { + /* Bind the poll structure and this slot */ + + opriv->ao_fds[i] = fds; + fds->priv = &opriv->ao_fds[i]; + break; + } + } + + if (i >= CONFIG_AJOYSTICK_NPOLLWAITERS) + { + ivdbg("ERROR: Too man poll waiters\n"); + fds->priv = NULL; + ret = -EBUSY; + goto errout_with_dusem; + } + } + else if (fds->priv) + { + /* This is a request to tear down the poll. */ + + FAR struct pollfd **slot = (FAR struct pollfd **)fds->priv; + +#ifdef CONFIG_DEBUG + if (!slot) + { + ivdbg("ERROR: Poll slot not found\n"); + ret = -EIO; + goto errout_with_dusem; + } +#endif + + /* Remove all memory of the poll setup */ + + *slot = NULL; + fds->priv = NULL; + } + +errout_with_dusem: + ajoy_givesem(&priv->au_exclsem); + return ret; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ajoy_register + * + * Description: + * Bind the lower half discrete joystick driver to an instance of the + * upper half discrete joystick driver and register the composite character + * driver as the specific device. + * + * Input Parameters: + * devname - The name of the discrete joystick device to be registers. + * This should be a string of the form "/priv/ajoyN" where N is the the + * minor device number. + * lower - An instance of the platform-specific discrete joystick lower + * half driver. + * + * Returned Values: + * Zero (OK) is returned on success. Otherwise a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +int ajoy_register(FAR const char *devname, + FAR const struct ajoy_lowerhalf_s *lower) + +{ + FAR struct ajoy_upperhalf_s *priv; + int ret; + + DEBUGASSERT(devname && lower); + + /* Allocate a new ajoystick driver instance */ + + priv = (FAR struct ajoy_upperhalf_s *) + kmm_zalloc(sizeof(struct ajoy_upperhalf_s)); + + if (!priv) + { + ivdbg("ERROR: Failed to allocate device structure\n"); + return -ENOMEM; + } + + /* Make sure that all ajoystick interrupts are disabled */ + + DEBUGASSERT(lower->al_enable); + lower->al_enable(lower, 0, 0, NULL, NULL); + + /* Initialize the new ajoystick driver instance */ + + priv->au_lower = lower; + sem_init(&priv->au_exclsem, 0, 1); + + DEBUGASSERT(lower->al_buttons); + priv->au_sample = lower->al_buttons(lower); + + /* And register the ajoystick driver */ + + ret = register_driver(devname, &ajoy_fops, 0666, priv); + if (ret < 0) + { + ivdbg("ERROR: register_driver failed: %d\n", ret); + goto errout_with_priv; + } + + return OK; + +errout_with_priv: + sem_destroy(&priv->au_exclsem); + kmm_free(priv); + return ret; +} diff --git a/drivers/input/djoystick.c b/drivers/input/djoystick.c index 49f88d234c..80e704851f 100644 --- a/drivers/input/djoystick.c +++ b/drivers/input/djoystick.c @@ -340,7 +340,7 @@ static void djoy_sample(FAR struct djoy_upperhalf_s *priv) fds->revents |= (fds->events & POLLIN); if (fds->revents != 0) { - ivdbg("Report events: %02x\n", fds->revents); + illvdbg("Report events: %02x\n", fds->revents); sem_post(fds->sem); } } @@ -550,6 +550,16 @@ static ssize_t djoy_read(FAR struct file *filep, FAR char *buffer, DEBUGASSERT(inode->i_private); priv = (FAR struct djoy_upperhalf_s *)inode->i_private; + /* Make sure that the buffer is sufficiently large to hold at least one + * complete sample. + */ + + if (len < sizeof(djoy_buttonset_t)) + { + ivdbg("ERROR: buffer too small: %lu\n", (unsigned long)len); + return -EINVAL; + } + /* Get exclusive access to the driver structure */ ret = djoy_takesem(&priv->du_exclsem); @@ -568,7 +578,7 @@ static ssize_t djoy_read(FAR struct file *filep, FAR char *buffer, ret = sizeof(djoy_buttonset_t); djoy_givesem(&priv->du_exclsem); - return ret; + return (ssize_t)ret; } /**************************************************************************** diff --git a/include/nuttx/input/ajoystick.h b/include/nuttx/input/ajoystick.h index 35ebd7210e..3537f15f1a 100644 --- a/include/nuttx/input/ajoystick.h +++ b/include/nuttx/input/ajoystick.h @@ -108,7 +108,7 @@ * seek() methods. The remaining driver methods behave as follows: * * 1) The read() method will always return a single value of size - * struct ajoy_sample_s represent the current joystick positiona and the + * struct ajoy_sample_s represent the current joystick positional and the * state of all joystick buttons. read() never blocks. * 2) The poll() method can be used to notify a client if there is a change * in any of the joystick button inputs. This feature, of course, @@ -185,6 +185,17 @@ struct ajoy_notify_s uint8_t an_signo; /* Signal number to use in the notification */ }; +/* This structure is returned by read() and provides the sample state of the + * analog joystick. + */ + +struct ajoy_sample_s +{ + int16_t as_x; /* X/horizontal position */ + int16_t as_y; /* Y/vertical position */ + ajoy_buttonset_t as_buttons; /* State of all buttons */ +}; + /* This is the type of the analog joystick interrupt handler used with * the struct ajoy_lowerhalf_s enable() method. */ @@ -198,7 +209,8 @@ typedef CODE void (*ajoy_handler_t) * 1) A common upper half driver that provides the common user interface to * the joystick, * 2) Platform-specific lower half drivers that provide the interface - * between the common upper half and the platform analog inputs. + * between the common upper half and the platform analog and discrete + * inputs. * * This structure defines the interface between an instance of the lower * half driver and the common upper half driver. Such an instance is @@ -210,17 +222,22 @@ struct ajoy_lowerhalf_s { /* Return the set of buttons supported on the analog joystick device */ - CODE ajoy_buttonset_t (*dl_supported)(FAR const struct ajoy_lowerhalf_s *lower); + CODE ajoy_buttonset_t (*al_supported)(FAR const struct ajoy_lowerhalf_s *lower); - /* Return the current state of all analog joystick buttons */ + /* Return the current state of all analog joystick position and button data */ - CODE ajoy_buttonset_t (*dl_sample)(FAR const struct ajoy_lowerhalf_s *lower); + CODE int (*al_sample)(FAR const struct ajoy_lowerhalf_s *lower, + FAR struct ajoy_sample_s *sample); - /* Enable interrupts on the selecte set of joystick buttons. And empty + /* Return the current state of button data (only) */ + + CODE ajoy_buttonset_t (*al_buttons)(FAR const struct ajoy_lowerhalf_s *lower); + + /* Enable interrupts on the selected set of joystick buttons. And empty * set will disable all interrupts. */ - CODE void (*dl_enable)(FAR const struct ajoy_lowerhalf_s *lower, + CODE void (*al_enable)(FAR const struct ajoy_lowerhalf_s *lower, ajoy_buttonset_t press, ajoy_buttonset_t release, ajoy_handler_t handler, FAR void *arg); }; @@ -245,9 +262,9 @@ extern "C" * Name: ajoy_register * * Description: - * Bind the lower half analog joystick driver to an instance of the - * upper half analog joystick driver and register the composite character - * driver as the specific device. + * Bind the lower half analog joystick driver to an instance of the upper + * half analog joystick driver and register the composite character + * driver as the specified device. * * Input Parameters: * devname - The name of the analog joystick device to be registers.