diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index abf5153e0d..6e6fe37912 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -35,6 +35,21 @@ config INPUT_TOUCHSCREEN select MM_CIRCBUF default n +config INPUT_KEYBOARD + bool + select MM_CIRCBUF + default n + +if INPUT_KEYBOARD + +config INPUT_KEYBOARD_BUFFSIZE + int "Number keyboard data buffer" + default 64 + ---help--- + Maximum number of keyboard data buffer + +endif # INPUT_KEYBOARD + config INPUT_UINPUT bool default n diff --git a/drivers/input/Make.defs b/drivers/input/Make.defs index 8595d28c16..5d77492155 100644 --- a/drivers/input/Make.defs +++ b/drivers/input/Make.defs @@ -80,6 +80,10 @@ endif endif +ifeq ($(CONFIG_INPUT_KEYBOARD),y) + CSRCS += keyboard_upper.c +endif + ifeq ($(CONFIG_INPUT_DJOYSTICK),y) CSRCS += djoystick.c endif diff --git a/drivers/input/keyboard_upper.c b/drivers/input/keyboard_upper.c new file mode 100644 index 0000000000..5b69ee08d6 --- /dev/null +++ b/drivers/input/keyboard_upper.c @@ -0,0 +1,444 @@ +/**************************************************************************** + * drivers/input/keyboard_upper.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct keyboard_opriv_s +{ + sem_t waitsem; + sem_t locksem; + struct circbuf_s circ; + struct list_node node; + FAR struct pollfd *fds; +}; + +/* This structure is for keyboard upper half driver */ + +struct keyboard_upperhalf_s +{ + sem_t exclsem; /* Manages exclusive access to this structure */ + struct list_node head; /* Head of list */ + FAR struct keyboard_lowerhalf_s + *lower; /* A pointer of lower half instance */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int keyboard_open(FAR struct file *filep); +static int keyboard_close(FAR struct file *filep); +static ssize_t keyboard_read(FAR struct file *filep, FAR char *buffer, + size_t len); +static ssize_t keyboard_write(FAR struct file *filep, FAR const char *buffer, + size_t len); +static int keyboard_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup); +static void keyboard_notify(FAR struct keyboard_opriv_s *buffer); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_keyboard_fops = +{ + keyboard_open, /* open */ + keyboard_close, /* close */ + keyboard_read, /* read */ + keyboard_write, /* write */ + NULL, /* seek */ + NULL, /* ioctl */ + keyboard_poll /* poll */ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , NULL /* unlink */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: keyboard_notify + ****************************************************************************/ + +static void keyboard_notify(FAR struct keyboard_opriv_s *opriv) +{ + FAR struct pollfd *fds = opriv->fds; + if (fds != NULL) + { + fds->revents |= (fds->events & POLLIN); + if (fds->revents != 0) + { + /* report event log */ + + int semcount; + nxsem_get_value(fds->sem, &semcount); + if (semcount < 1) + { + nxsem_post(fds->sem); + } + } + } +} + +/**************************************************************************** + * Name: keyboard_open + ****************************************************************************/ + +static int keyboard_open(FAR struct file *filep) +{ + FAR struct keyboard_opriv_s *opriv = NULL; + FAR struct inode *inode = filep->f_inode; + FAR struct keyboard_upperhalf_s *upper = inode->i_private; + int ret; + + ret = nxsem_wait(&upper->exclsem); + if (ret < 0) + { + return ret; + } + + opriv = kmm_zalloc(sizeof(FAR struct keyboard_opriv_s)); + if (opriv == NULL) + { + nxsem_post(&upper->exclsem); + return -ENOMEM; + } + + /* Initializes the buffer for each open file */ + + ret = circbuf_init(&opriv->circ, NULL, CONFIG_INPUT_KEYBOARD_BUFFSIZE); + if (ret < 0) + { + kmm_free(opriv); + nxsem_post(&upper->exclsem); + return ret; + } + + /* Perform the operations required by the lower level */ + + if (upper->lower->open) + { + ret = upper->lower->open(upper->lower); + if (ret < 0) + { + kmm_free(opriv); + nxsem_post(&upper->exclsem); + return ret; + } + } + + nxsem_init(&opriv->waitsem, 0, 0); + nxsem_init(&opriv->locksem, 0, 1); + nxsem_set_protocol(&opriv->waitsem, SEM_PRIO_NONE); + list_add_tail(&upper->head, &opriv->node); + filep->f_priv = opriv; + + nxsem_post(&upper->exclsem); + return ret; +} + +/**************************************************************************** + * Name: keyboard_close + ****************************************************************************/ + +static int keyboard_close(FAR struct file *filep) +{ + FAR struct keyboard_opriv_s *opriv = filep->f_priv; + FAR struct inode *inode = filep->f_inode; + FAR struct keyboard_upperhalf_s *upper = inode->i_private; + int ret; + + ret = nxsem_wait(&upper->exclsem); + if (ret < 0) + { + return ret; + } + + /* Perform the operations required by the lower level */ + + if (upper->lower->close) + { + ret = upper->lower->close(upper->lower); + if (ret < 0) + { + goto out; + } + } + + list_delete(&opriv->node); + circbuf_uninit(&opriv->circ); + nxsem_destroy(&opriv->waitsem); + nxsem_destroy(&opriv->locksem); + kmm_free(opriv); + +out: + nxsem_post(&upper->exclsem); + return ret; +} + +/**************************************************************************** + * Name: keyboard_read + ****************************************************************************/ + +static ssize_t keyboard_read(FAR struct file *filep, + FAR char *buff, size_t len) +{ + FAR struct keyboard_opriv_s *opriv = filep->f_priv; + int ret; + + /* Make sure that we have exclusive access to the private data structure */ + + ret = nxsem_wait(&opriv->locksem); + if (ret < 0) + { + return ret; + } + + /* Is there keyboard data now? */ + + while (circbuf_is_empty(&opriv->circ)) + { + if ((filep->f_oflags & O_NONBLOCK) != 0) + { + ret = -EAGAIN; + goto out; + } + else + { + nxsem_post(&opriv->locksem); + ret = nxsem_wait_uninterruptible(&opriv->waitsem); + if (ret < 0) + { + return ret; + } + + ret = nxsem_wait(&opriv->locksem); + if (ret < 0) + { + return ret; + } + } + } + + ret = circbuf_read(&opriv->circ, buff, len); + +out: + nxsem_post(&opriv->locksem); + return ret; +} + +/**************************************************************************** + * Name: keyboard_poll + ****************************************************************************/ + +static int keyboard_poll(FAR struct file *filep, + FAR struct pollfd *fds, bool setup) +{ + FAR struct keyboard_opriv_s *opriv = filep->f_priv; + int ret; + + ret = nxsem_wait(&opriv->locksem); + if (ret < 0) + { + return ret; + } + + if (setup) + { + if (opriv->fds == NULL) + { + opriv->fds = fds; + fds->priv = &opriv->fds; + } + else + { + ret = -EBUSY; + goto errout; + } + + if (!circbuf_is_empty(&opriv->circ)) + { + keyboard_notify(opriv); + } + } + else + { + opriv->fds = NULL; + fds->priv = NULL; + } + +errout: + nxsem_post(&opriv->locksem); + return ret; +} + +/**************************************************************************** + * Name: keyboard_write + ****************************************************************************/ + +static ssize_t keyboard_write(FAR struct file *filep, + FAR const char *buffer, size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct keyboard_upperhalf_s *upper = inode->i_private; + FAR struct keyboard_lowerhalf_s *lower = upper->lower; + + if (lower->write != NULL) + { + return lower->write(lower, buffer, buflen); + } + + return -ENOSYS; +} + +/**************************************************************************** + * Public Function + ****************************************************************************/ + +/**************************************************************************** + * Name: keyboard_register + ****************************************************************************/ + +int keyboard_register(FAR struct keyboard_lowerhalf_s *lower, + FAR const char *path) +{ + FAR struct keyboard_upperhalf_s *upper; + int ret; + + iinfo("Registering %s\n", path); + if (lower == NULL) + { + ierr("ERROR: invalid touchscreen device\n"); + return -EINVAL; + } + + upper = kmm_zalloc(sizeof(struct keyboard_upperhalf_s)); + if (upper == NULL) + { + ierr("ERROR: Failed to mem alloc!\n"); + return -ENOMEM; + } + + upper->lower = lower; + lower->priv = upper; + list_initialize(&upper->head); + nxsem_init(&upper->exclsem, 0, 1); + + ret = register_driver(path, &g_keyboard_fops, 0666, upper); + if (ret < 0) + { + nxsem_destroy(&upper->exclsem); + kmm_free(upper); + return ret; + } + + return ret; +} + +/**************************************************************************** + * Name: keyboard_unregister + ****************************************************************************/ + +int keyboard_unregister(FAR struct keyboard_lowerhalf_s *lower, + FAR const char *path) +{ + FAR struct keyboard_upperhalf_s *upper; + int ret; + + DEBUGASSERT(lower != NULL); + DEBUGASSERT(lower->priv != NULL); + + upper = lower->priv; + + iinfo("UnRegistering %s\n", path); + ret = unregister_driver(path); + if (ret < 0) + { + return ret; + } + + nxsem_destroy(&upper->exclsem); + kmm_free(upper); + return 0; +} + +/**************************************************************************** + * keyboard_event + ****************************************************************************/ + +void keyboard_event(FAR struct keyboard_lowerhalf_s *lower, uint32_t keycode, + uint32_t type) +{ + FAR struct keyboard_upperhalf_s *upper = lower->priv; + FAR struct keyboard_opriv_s *opriv; + struct keyboard_event_s key; + int semcount; + + if (nxsem_wait(&upper->exclsem) < 0) + { + return; + } + + key.code = keycode; + key.type = type; + list_for_every_entry(&upper->head, opriv, struct keyboard_opriv_s, node) + { + if (nxsem_wait(&opriv->locksem) == 0) + { + circbuf_overwrite(&opriv->circ, &key, + sizeof(struct keyboard_event_s)); + nxsem_get_value(&opriv->waitsem, &semcount); + if (semcount < 1) + { + nxsem_post(&opriv->waitsem); + } + + keyboard_notify(opriv); + nxsem_post(&opriv->locksem); + } + } + + nxsem_post(&upper->exclsem); +} diff --git a/include/nuttx/input/keyboard.h b/include/nuttx/input/keyboard.h new file mode 100644 index 0000000000..3b1c51ffec --- /dev/null +++ b/include/nuttx/input/keyboard.h @@ -0,0 +1,97 @@ +/**************************************************************************** + * include/nuttx/input/keyboard.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_INPUT_KEYBOARD_H +#define __INCLUDE_NUTTX_INPUT_KEYBOARD_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define KEYBOARD_PRESS 0 /* Key press event */ +#define KEYBOARD_RELEASE 1 /* Key release event */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct keyboard_event_s +{ + uint32_t type; + uint32_t code; +}; + +struct keyboard_lowerhalf_s +{ + FAR void *priv; + CODE int (*open)(FAR struct keyboard_lowerhalf_s *lower); + CODE int (*close)(FAR struct keyboard_lowerhalf_s *lower); + CODE ssize_t (*write)(FAR struct keyboard_lowerhalf_s *lower, + FAR const char *buffer, size_t buflen); +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: keyboard_event + ****************************************************************************/ + +void keyboard_event(FAR struct keyboard_lowerhalf_s *lower, uint32_t keycode, + uint32_t type); + +/**************************************************************************** + * Name: keyboard_register + ****************************************************************************/ + +int keyboard_register(FAR struct keyboard_lowerhalf_s *lower, + FAR const char *path); + +/**************************************************************************** + * Name: keyboard_register + ****************************************************************************/ + +int keyboard_unregister(FAR struct keyboard_lowerhalf_s *lower, + FAR const char *path); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_INPUT_KEYBOARD_H */