This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 54b2381c421 drivers/input: Create Single Button Multi Actions
54b2381c421 is described below

commit 54b2381c421cee8bb3b853fd1bc5ae1f0a0e862a
Author: Alan Carvalho de Assis <acas...@gmail.com>
AuthorDate: Fri Jul 11 10:37:07 2025 -0300

    drivers/input: Create Single Button Multi Actions
    
    This commit creates a sbutton device that uses a single button to
    create a keyboard driver that returns TAB or ENTER depending how
    long the user keeps the button pressed.
    
    Signed-off-by: Alan C. Assis <acas...@gmail.com>
---
 .../components/drivers/character/input/index.rst   |   1 +
 .../components/drivers/character/input/sbutton.rst |  41 +++
 .../arm/stm32f4/boards/stm32f4discovery/index.rst  |  21 ++
 boards/arm/stm32/common/include/board_sbutton.h    |  83 ++++++
 boards/arm/stm32/common/src/CMakeLists.txt         |   4 +
 boards/arm/stm32/common/src/Make.defs              |   4 +
 boards/arm/stm32/common/src/stm32_sbutton.c        | 182 ++++++++++++++
 .../stm32f4discovery/configs/sbutton/defconfig     |  56 +++++
 boards/arm/stm32/stm32f4discovery/include/board.h  |   3 +
 .../arm/stm32/stm32f4discovery/src/stm32_bringup.c |  14 ++
 drivers/input/CMakeLists.txt                       |   4 +
 drivers/input/Kconfig                              |  41 +++
 drivers/input/Make.defs                            |   4 +
 drivers/input/sbutton.c                            | 278 +++++++++++++++++++++
 include/nuttx/input/sbutton.h                      | 113 +++++++++
 15 files changed, 849 insertions(+)

diff --git a/Documentation/components/drivers/character/input/index.rst 
b/Documentation/components/drivers/character/input/index.rst
index ce0d896c629..c1317ebd493 100644
--- a/Documentation/components/drivers/character/input/index.rst
+++ b/Documentation/components/drivers/character/input/index.rst
@@ -6,5 +6,6 @@ Input Devices
   :caption: Supported Drivers
 
   keypad.rst
+  sbutton.rst
 
 See ``include/nuttx/input/*.h`` for registration information.
diff --git a/Documentation/components/drivers/character/input/sbutton.rst 
b/Documentation/components/drivers/character/input/sbutton.rst
new file mode 100644
index 00000000000..3281db7fd4a
--- /dev/null
+++ b/Documentation/components/drivers/character/input/sbutton.rst
@@ -0,0 +1,41 @@
+==================================
+Single Button Multi Actions Driver
+==================================
+
+**Single Button (aka SButton)** is an kind of keyboard that uses
+only a single physical button (Switch) in the board. This kind of
+button is used with simple interfaces like those used on 3D Printers
+or other devices where all the user needs is to move to the next
+option and confirm the selection.
+
+It could be done detecting if the button was pressed for a short
+period of time (i.e. less than 500ms) or long pressed. If it is a
+short press the driver will return **TAB** and if it is a long
+press the driver will return **ENTER**. Using it is possible to
+navigate on those kind of menu.
+
+**How does it work?**. The driver uses a simple config data (this
+config data is equivalent to the platform data on Linux kernel) to
+map the pin from MCU will be used to register and detect the
+interrupt from this pin physically connected to the button.
+
+It uses a kind of "polymorphism" in C to allow the driver to get
+access to the functions responsible to attach and enable the
+interrupt and to get the status of the pin.
+See ``include/nuttx/input/sbutton.h``
+and ``boards/arm/stm32/common/src/stm32_sbutton.c`` to understand
+better how it works. But basically the board file (config data)
+creates a struct when the first field (variable) is the config
+struct used the but SButton driver (``drivers/input/sbutton.c``).
+
+Every time the user presses or releases the key button an interrupt
+is generated. The ISR of this interrupt inside sbutton
+(``sbutton_interrupt()``) calls a workqueue to process it (because
+we cannot spend time inside the ISR processing data, it could
+degradate the performance of the RTOS). All that workqueue
+(``sbutton_worker()``) needs to do it measure the elapsed time
+(ticks) from the moment the key was pressed until the moment it
+was released to decide if it is a "KEY_1" (**TAB**) or a "KEY_2"
+(**ENTER**). Then is call the ``keyboard_event()`` from the
+keyboard upper to send this key stroke to the user application.
+
diff --git 
a/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/index.rst 
b/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/index.rst
index 5f76b5cac5e..f75ef95816e 100644
--- a/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/index.rst
+++ b/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/index.rst
@@ -1970,6 +1970,27 @@ the second dongle you will connect to UART3 (PB10 and 
PB11).
 In the main NSH console (in USART2) type: "pts_test &". It will create a
 new console in UART3. Just press ENTER and start typing commands on it.
 
+sbutton
+-------
+
+This is a configuration to test the Single Button Dual Action feature.
+To test it just compile and flash nuttx.bin in the board. Then run the
+``kbd`` command inside ``nsh>`` and short press and long press User
+Button (B1) on the board.
+
+You will see something like this::
+
+     NuttShell (NSH) NuttX-12.10.0
+     nsh> kbd
+     kbd_main: nsamples: 0
+     kbd_main: Opening /dev/kbd0
+     Sample  :
+        code : 65
+        type : 0
+     Sample  :
+        code : 66
+        type : 0
+
 sporadic
 --------
 
diff --git a/boards/arm/stm32/common/include/board_sbutton.h 
b/boards/arm/stm32/common/include/board_sbutton.h
new file mode 100644
index 00000000000..0dd67c11398
--- /dev/null
+++ b/boards/arm/stm32/common/include/board_sbutton.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+ * boards/arm/stm32/common/include/board_sbutton.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 __BOARDS_ARM_STM32_COMMON_INCLUDE_BOARD_SBUTTON_H
+#define __BOARDS_ARM_STM32_COMMON_INCLUDE_BOARD_SBUTTON_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Inline Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_sbutton_initialize
+ *
+ * Description:
+ *   This function is called by application-specific, setup logic to
+ *   configure the Single Button Dual Action.
+ *
+ * Input Parameters:
+ *   devno - The device number, used to build the device path as /dev/distN
+ *
+ * Returned Value:
+ *   Zero is returned on success.  Otherwise, a negated errno value is
+ *   returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int board_sbutton_initialize(int devno);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BOARDS_ARM_STM32_COMMON_INCLUDE_BOARD_SBUTTON_H */
diff --git a/boards/arm/stm32/common/src/CMakeLists.txt 
b/boards/arm/stm32/common/src/CMakeLists.txt
index 733194f4eb1..d13a75b1a4d 100644
--- a/boards/arm/stm32/common/src/CMakeLists.txt
+++ b/boards/arm/stm32/common/src/CMakeLists.txt
@@ -154,4 +154,8 @@ if(CONFIG_STEPPER_DRV8825)
   list(APPEND SRCS stm32_drv8825.c)
 endif()
 
+if(CONFIG_INPUT_SBUTTON)
+  list(APPEND SRCS stm32_sbutton.c)
+endif()
+
 target_sources(board PRIVATE ${SRCS})
diff --git a/boards/arm/stm32/common/src/Make.defs 
b/boards/arm/stm32/common/src/Make.defs
index e5ea54072a7..1048df7d17a 100644
--- a/boards/arm/stm32/common/src/Make.defs
+++ b/boards/arm/stm32/common/src/Make.defs
@@ -162,6 +162,10 @@ ifeq ($(CONFIG_STEPPER_DRV8825),y)
   CSRCS += stm32_drv8825.c
 endif
 
+ifeq ($(CONFIG_INPUT_SBUTTON),y)
+  CSRCS += stm32_sbutton.c
+endif
+
 DEPPATH += --dep-path src
 VPATH += :src
 CFLAGS += 
${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)$(CONFIG_ARCH)$(DELIM)src$(DELIM)board$(DELIM)src
diff --git a/boards/arm/stm32/common/src/stm32_sbutton.c 
b/boards/arm/stm32/common/src/stm32_sbutton.c
new file mode 100644
index 00000000000..096a9e9fc5e
--- /dev/null
+++ b/boards/arm/stm32/common/src/stm32_sbutton.c
@@ -0,0 +1,182 @@
+/****************************************************************************
+ * boards/arm/stm32/common/src/stm32_sbutton.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 <nuttx/config.h>
+#include <nuttx/arch.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/board.h>
+#include <arch/board/board.h>
+#include <nuttx/input/sbutton.h>
+
+#include "stm32.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32_sbtnconfig_s
+{
+  /* Configuration structure as seen by the HC-SR04 driver */
+
+  struct sbutton_config_s config;
+
+  /* Additional private definitions only known to this driver */
+
+  void *arg;    /* Argument to pass to the interrupt handler */
+  xcpt_t isr;   /* ISR Handler */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int  sbtn_irq_attach(const struct sbutton_config_s *config,
+                            xcpt_t isr, void *arg);
+static void sbtn_irq_enable(const struct sbutton_config_s *config,
+                            bool enable);
+static void sbtn_irq_clear(const struct sbutton_config_s *config);
+
+static bool sbtn_pin_status(void);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* A reference to a structure of this type must be passed to the SButton
+ * driver.  This structure provides information about the configuration
+ * of the SButton and provides some board-specific hooks.
+ *
+ * Memory for this structure is provided by the caller.  It is not copied
+ * by the driver and is presumed to persist while the driver is active.
+ */
+
+static struct stm32_sbtnconfig_s g_sbtnconfig =
+{
+  .config =
+  {
+    .attach     = sbtn_irq_attach,
+    .enable     = sbtn_irq_enable,
+    .clear      = sbtn_irq_clear,
+    .status     = sbtn_pin_status,
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/* Attach the SBUTTON interrupt handler to the GPIO interrupt */
+
+static int sbtn_irq_attach(const struct sbutton_config_s *state, xcpt_t isr,
+                           void *arg)
+{
+  struct stm32_sbtnconfig_s *priv = (struct stm32_sbtnconfig_s *)state;
+  irqstate_t flags;
+
+  sinfo("sbtn_irq_attach\n");
+
+  flags = enter_critical_section();
+
+  priv->isr     = isr;
+  priv->arg     = arg;
+
+  stm32_gpiosetevent(BOARD_SBUTTON_GPIO_INT, true, true,
+                     true, isr, arg);
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/* Enable or disable the GPIO interrupt */
+
+static void sbtn_irq_enable(const struct sbutton_config_s *state,
+                            bool enable)
+{
+  struct stm32_sbtnconfig_s *priv = (struct stm32_sbtnconfig_s *)state;
+
+  iinfo("%d\n", enable);
+
+  stm32_gpiosetevent(BOARD_SBUTTON_GPIO_INT, true, true,
+                     true, enable ? priv->isr : NULL, priv->arg);
+}
+
+/* Acknowledge/clear any pending GPIO interrupt */
+
+static void sbtn_irq_clear(const struct sbutton_config_s *state)
+{
+  /* FIXME: Nothing to do ? */
+}
+
+/* Read and return the status of button (PRESSED = true; RELEASED = false */
+
+static bool sbtn_pin_status(void)
+{
+  /* STM32F4Discovery button B1 is high level when press
+   * then just returned the status of the pin directly.
+   */
+
+  return stm32_gpioread(BOARD_SBUTTON_GPIO_INT);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: board_sbutton_initialize
+ *
+ * Description:
+ *   This function is called by application-specific, setup logic to
+ *   configure the Single Button Dual Action.
+ *
+ * Input Parameters:
+ *   devno - The device number, used to build the device path as /dev/kbdN
+ *
+ * Returned Value:
+ *   Zero is returned on success.  Otherwise, a negated errno value is
+ *   returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int board_sbutton_initialize(int devno)
+{
+  /* Configure the Single Button Dual Action interrupt pin */
+
+  stm32_configgpio(BOARD_SBUTTON_GPIO_INT);
+
+  /* Register the Single Button with overlay config pointer */
+
+  return sbutton_register(&g_sbtnconfig.config, devno);
+}
diff --git a/boards/arm/stm32/stm32f4discovery/configs/sbutton/defconfig 
b/boards/arm/stm32/stm32f4discovery/configs/sbutton/defconfig
new file mode 100644
index 00000000000..3f7bd12d6f7
--- /dev/null
+++ b/boards/arm/stm32/stm32f4discovery/configs/sbutton/defconfig
@@ -0,0 +1,56 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed 
.config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that 
includes your
+# modifications.
+#
+# CONFIG_ARCH_FPU is not set
+# CONFIG_ASSERTIONS_FILENAME is not set
+# CONFIG_NDEBUG is not set
+# CONFIG_NSH_ARGCAT is not set
+# CONFIG_NSH_CMDOPT_HEXDUMP is not set
+CONFIG_ARCH="arm"
+CONFIG_ARCH_BOARD="stm32f4discovery"
+CONFIG_ARCH_BOARD_COMMON=y
+CONFIG_ARCH_BOARD_STM32F4_DISCOVERY=y
+CONFIG_ARCH_CHIP="stm32"
+CONFIG_ARCH_CHIP_STM32=y
+CONFIG_ARCH_CHIP_STM32F407VG=y
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_BOARD_LATE_INITIALIZE=y
+CONFIG_BOARD_LOOPSPERMSEC=16717
+CONFIG_BUILTIN=y
+CONFIG_EXAMPLES_HELLO=y
+CONFIG_EXAMPLES_KEYBOARD=y
+CONFIG_FS_PROCFS=y
+CONFIG_HAVE_CXX=y
+CONFIG_HAVE_CXXINITIALIZE=y
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_INPUT=y
+CONFIG_INPUT_SBUTTON=y
+CONFIG_INTELHEX_BINARY=y
+CONFIG_LINE_MAX=64
+CONFIG_MM_REGIONS=2
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_FILEIOSIZE=512
+CONFIG_NSH_READLINE=y
+CONFIG_PREALLOC_TIMERS=4
+CONFIG_RAM_SIZE=114688
+CONFIG_RAM_START=0x20000000
+CONFIG_RAW_BINARY=y
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_HPWORK=y
+CONFIG_SCHED_WAITPID=y
+CONFIG_START_DAY=6
+CONFIG_START_MONTH=12
+CONFIG_START_YEAR=2011
+CONFIG_STM32_JTAG_SW_ENABLE=y
+CONFIG_STM32_PWR=y
+CONFIG_STM32_SPI1=y
+CONFIG_STM32_USART2=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_USART2_RXBUFSIZE=128
+CONFIG_USART2_SERIAL_CONSOLE=y
+CONFIG_USART2_TXBUFSIZE=128
+CONFIG_WQUEUE_NOTIFIER=y
diff --git a/boards/arm/stm32/stm32f4discovery/include/board.h 
b/boards/arm/stm32/stm32f4discovery/include/board.h
index 287d8e63933..4dce12316ab 100644
--- a/boards/arm/stm32/stm32f4discovery/include/board.h
+++ b/boards/arm/stm32/stm32f4discovery/include/board.h
@@ -465,6 +465,9 @@
 
 #define BOARD_XEN1210_GPIO_INT  GPIO_XEN1210_INT
 
+#define BOARD_SBUTTON_GPIO_INT (GPIO_INPUT|GPIO_FLOAT|GPIO_EXTI|\
+                                GPIO_OPENDRAIN|GPIO_PORTA|GPIO_PIN0)
+
 /* Define what timer to use as XEN1210 CLK (will use channel 1) */
 
 #define BOARD_XEN1210_PWMTIMER   1
diff --git a/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c 
b/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c
index ef55e3c4552..d34e6b38281 100644
--- a/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c
+++ b/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c
@@ -98,6 +98,10 @@
 #include "stm32_nunchuck.h"
 #endif
 
+#ifdef CONFIG_INPUT_SBUTTON
+#include "board_sbutton.h"
+#endif
+
 #ifdef CONFIG_SENSORS_ZEROCROSS
 #include "stm32_zerocross.h"
 #endif
@@ -452,6 +456,16 @@ int stm32_bringup(void)
     }
 #endif
 
+#ifdef CONFIG_INPUT_SBUTTON
+  /* Register the Single Button Dual Action driver */
+
+  ret = board_sbutton_initialize(0);
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: board_sbtn_initialize() failed: %d\n", ret);
+    }
+#endif
+
 #ifdef CONFIG_SENSORS_APDS9960
   /* Register the APDS-9960 gesture sensor */
 
diff --git a/drivers/input/CMakeLists.txt b/drivers/input/CMakeLists.txt
index a1c4eea640e..3b897b1a733 100644
--- a/drivers/input/CMakeLists.txt
+++ b/drivers/input/CMakeLists.txt
@@ -106,6 +106,10 @@ if(CONFIG_INPUT)
     list(APPEND SRCS keyboard_upper.c)
   endif()
 
+  if(CONFIG_INPUT_SBUTTON)
+    list(APPEND SRCS sbutton.c)
+  endif()
+
   if(CONFIG_INPUT_DJOYSTICK)
     list(APPEND SRCS djoystick.c)
   endif()
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 47b861b7398..a5cff99c51c 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -622,6 +622,47 @@ config INPUT_BUTTONS_NPOLLWAITERS
 
 endif # INPUT_BUTTONS
 
+config INPUT_SBUTTON
+       bool "Single Button Multi Actions"
+       select INPUT_KEYBOARD
+       default n
+       ---help---
+               Enable the Single Button Multi Actions upper half driver.
+               This driver allows using a single button to generate two
+               events (mapped to short press and long press). Using it
+               is possible navigate a menu interface when using with a
+               LCD/OLED display.
+
+if INPUT_SBUTTON
+
+config INPUT_SBUTTON_KEY1
+       int "ASCII code to generate for short press"
+       default 9
+       ---help---
+               Which key will be generated when user do a short press.
+               Default key is TAB (ASCII 9)
+
+config INPUT_SBUTTON_KEY2
+       int "ASCII code to generate for long press"
+       default 13
+       ---help---
+               Which key will be generated when user do a short press.
+               Default key is ENTER (ASCII 13)
+
+config INPUT_SBUTTON_BUFSIZE
+       int "Buffer size to store keys"
+       default 8
+
+config INPUT_SBUTTON_KEY_THRESH_MS
+       int "Threshold in milliseconds for short press"
+       default 500
+
+config INPUT_SBUTTON_KEY_DEBOUNCE_MS
+       int "Key Debounce value in milliseconds"
+       default 30
+
+endif # INPUT_SBUTTON
+
 config INPUT_DJOYSTICK
        bool "Discrete Joystick"
        default n
diff --git a/drivers/input/Make.defs b/drivers/input/Make.defs
index 7cfc0c28d7c..815a4ce847d 100644
--- a/drivers/input/Make.defs
+++ b/drivers/input/Make.defs
@@ -118,6 +118,10 @@ ifeq ($(CONFIG_INPUT_NUNCHUCK),y)
   CSRCS += nunchuck.c
 endif
 
+ifeq ($(CONFIG_INPUT_SBUTTON),y)
+  CSRCS += sbutton.c
+endif
+
 ifeq ($(CONFIG_INPUT_SPQ10KBD),y)
   CSRCS += spq10kbd.c
 endif
diff --git a/drivers/input/sbutton.c b/drivers/input/sbutton.c
new file mode 100644
index 00000000000..0c2e21e24d1
--- /dev/null
+++ b/drivers/input/sbutton.c
@@ -0,0 +1,278 @@
+/****************************************************************************
+ * drivers/input/sbutton.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 <nuttx/config.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <poll.h>
+#include <fcntl.h>
+
+#include <nuttx/input/sbutton.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/clock.h>
+#include <nuttx/ascii.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mutex.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/input/keyboard.h>
+#include <nuttx/input/kbd_codec.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* 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/kbd%d"
+#define DEV_NAMELEN     12
+
+#define KEY_PRESS       true
+#define KEY_RELEASE     false
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct sbutton_dev_s
+{
+  FAR const struct sbutton_config_s *config;  /* Board configuration data */
+
+  mutex_t lock;         /* Exclusive access to dev */
+  clock_t start;        /* Clock tick when the key was pressed */
+  clock_t end;          /* Clock tick when the key was released */
+  bool    pressed;      /* Keep previous status of button */
+  struct  work_s work;  /* Supports the interrupt handling "bottom half" */
+
+  /* Keyboard lowerhalf of the registered keyboard */
+
+  struct keyboard_lowerhalf_s lower;
+};
+
+/****************************************************************************
+ * Static Function Prototypes
+ ****************************************************************************/
+
+static int sbutton_interrupt(int irq, FAR void *context, FAR void *arg);
+static void sbutton_worker(FAR void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sbutton_worker
+ ****************************************************************************/
+
+static void sbutton_worker(FAR void *arg)
+{
+  FAR struct sbutton_dev_s *priv = (FAR struct sbutton_dev_s *)arg;
+  uint8_t                   state;
+  int                       ret;
+
+  ret = nxmutex_lock(&priv->lock);
+  if (ret < 0)
+    {
+      return;
+    }
+
+  /* Read the status of the button */
+
+  state = priv->config->status();
+
+  /* If the user just pressed the button, start measuring */
+
+  if (state == KEY_PRESS && !priv->pressed)
+    {
+      iinfo("Button pressed\n");
+
+      priv->pressed = true;
+      priv->start   = clock_systime_ticks();
+    }
+  else
+    {
+      if (state == KEY_RELEASE && priv->pressed)
+        {
+          uint32_t elapsed;
+
+          iinfo("Button released\n");
+
+          priv->pressed = false;
+
+          priv->end = clock_systime_ticks();
+          elapsed = priv->end - priv->start;
+
+          /* Debounce to avoid getting wrong press/release event */
+
+          if (elapsed < MSEC2TICK(CONFIG_INPUT_SBUTTON_KEY_DEBOUNCE_MS))
+            {
+              iwarn("Button event too short, ignoring it\n");
+            }
+          else
+            {
+              if (elapsed < MSEC2TICK(CONFIG_INPUT_SBUTTON_KEY_THRESH_MS))
+                {
+                  iinfo("KEY_1\n");
+                  keyboard_event(&priv->lower, CONFIG_INPUT_SBUTTON_KEY1,
+                                 KEYBOARD_PRESS);
+                }
+              else
+                {
+                  iinfo("KEY_2\n");
+                  keyboard_event(&priv->lower, CONFIG_INPUT_SBUTTON_KEY2,
+                                 KEYBOARD_PRESS);
+                }
+            }
+        }
+    }
+
+  /* Unlock and return */
+
+  nxmutex_unlock(&priv->lock);
+}
+
+/****************************************************************************
+ * Name: sbutton_interrupt
+ ****************************************************************************/
+
+static int sbutton_interrupt(int irq, FAR void *context, FAR void *arg)
+{
+  FAR struct sbutton_dev_s *priv = (FAR struct sbutton_dev_s *)arg;
+  int                        ret;
+
+  /* Let the event worker know that it has an interrupt event to handle
+   * It is possible that we will already have work scheduled from a
+   * previous interrupt event.  That is OK we will service all the events
+   * in the same work job.
+   */
+
+  if (work_available(&priv->work))
+    {
+      ret = work_queue(HPWORK, &priv->work, sbutton_worker, priv, 0);
+      if (ret != 0)
+        {
+          ierr("ERROR: Failed to queue work: %d\n", ret);
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sbutton_register
+ *
+ * Description:
+ *   Configure the Single Button Multi Key Keyboard to use the provided
+ *   instance.  This will register the driver as /dev/kbdN where N is the
+ *   minor device number.
+ *
+ * Input Parameters:
+ *   config      - Persistent board configuration data
+ *   kbdminor    - The keyboard input device minor number
+ *
+ * Returned Value:
+ *   Zero is returned on success.  Otherwise, a negated errno value is
+ *   returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int sbutton_register(FAR const struct sbutton_config_s *config,
+                     char kbdminor)
+{
+  FAR struct sbutton_dev_s *priv;
+  char                      kbddevname[DEV_NAMELEN];
+  int                       ret;
+
+  /* Debug Sanity Checks */
+
+  DEBUGASSERT(config != NULL);
+  DEBUGASSERT(config->attach != NULL);
+  DEBUGASSERT(config->enable != NULL);
+  DEBUGASSERT(config->clear  != NULL);
+
+  priv = kmm_zalloc(sizeof(struct sbutton_dev_s));
+  if (!priv)
+    {
+      ierr("ERROR: kmm_zalloc(%d) failed\n", sizeof(struct sbutton_dev_s));
+      return -ENOMEM;
+    }
+
+  /* Initialize the device driver instance */
+
+  priv->config    = config;  /* Save the board configuration */
+  priv->pressed   = false;
+
+  nxmutex_init(&priv->lock);   /* Initialize device mutex */
+
+  config->clear(config);
+  config->enable(config, false);
+
+  /* Attach the interrupt handler */
+
+  ret = config->attach(config, sbutton_interrupt, priv);
+  if (ret < 0)
+    {
+      ierr("ERROR: Failed to attach interrupt\n");
+      goto errout_with_priv;
+    }
+
+  /* Start servicing events */
+
+  priv->config->enable(priv->config, true);
+
+  snprintf(kbddevname, sizeof(kbddevname), DEV_FORMAT, kbdminor);
+
+  /* Register the device as a keyboard device */
+
+  ret = keyboard_register(&priv->lower, kbddevname,
+                          CONFIG_INPUT_SBUTTON_BUFSIZE);
+  if (ret < 0)
+    {
+      ierr("ERROR: keyboard_register() failed: %d\n", ret);
+      goto errout_with_priv;
+    }
+
+  return OK;
+
+errout_with_priv:
+  nxmutex_destroy(&priv->lock);
+  kmm_free(priv);
+  return ret;
+}
diff --git a/include/nuttx/input/sbutton.h b/include/nuttx/input/sbutton.h
new file mode 100644
index 00000000000..9e9cd6b3d4e
--- /dev/null
+++ b/include/nuttx/input/sbutton.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+ * include/nuttx/input/sbutton.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/* The single button driver exports a standard character driver interface. By
+ * convention, the driver is exposed as /dev/kbd[n] and works like a common
+ * keyboard device, but generates only INPUT_TAB and INPUT_ENTER events.
+ */
+
+#ifndef __INCLUDE_NUTTX_INPUT_SBUTTON_H
+#define __INCLUDE_NUTTX_INPUT_SBUTTON_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <stdbool.h>
+#include <nuttx/irq.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* A reference to a structure of this type must be passed to the
+ * single button keyboard driver.  This structure provides information
+ * about the configuration and provides some board-specific hooks.
+ */
+
+struct sbutton_config_s
+{
+  /* IRQ/GPIO access callbacks.  These operations all hidden behind
+   * callbacks to isolate the Q10 Keyboard driver from differences in GPIO
+   * interrupt handling by varying boards and MCUs.
+   *
+   * attach  - Attach the Q10 kbd interrupt handler to the GPIO interrupt
+   * enable  - Enable or disable the GPIO interrupt
+   * clear   - Acknowledge/clear any pending GPIO interrupt
+   */
+
+  int  (*attach)(FAR const struct sbutton_config_s *config, xcpt_t isr,
+                 FAR void *arg);
+  void (*enable)(FAR const struct sbutton_config_s *config, bool enable);
+  void (*clear)(FAR const struct sbutton_config_s *config);
+  bool (*status)(void);
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spq10kbd_register
+ *
+ * Description:
+ *   Configure the single button driver to use the provided instance.
+ *   This will register the driver as /dev/kbdN where N is the
+ *   minor device number.
+ *
+ * Input Parameters:
+ *   config      - Persistent board configuration data
+ *   kbdminor    - The keyboard input device minor number
+ *
+ * Returned Value:
+ *   Zero is returned on success.  Otherwise, a negated errno value is
+ *   returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int sbutton_register(FAR const struct sbutton_config_s *config,
+                     char kbdminor);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_NUTTX_INPUT_SBUTTON_H */


Reply via email to