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 ef71625eded input: add keyboard matrix driver
ef71625eded is described below

commit ef71625eded3e5795733b32216188c0ffee28837
Author: Felipe Moura <[email protected]>
AuthorDate: Sun Feb 8 11:46:26 2026 -0300

    input: add keyboard matrix driver
    
    Add a generic kmatrix lower-half with polling/debounce, STM32 board 
adapters, Kconfig options, a public API header, and a test 
example/documentation.
    
    Signed-off-by: Felipe Moura <[email protected]>
    
    decouple kbd / keypad.
    
    Fix some comments
    
    add documentation
    
    fix rule issues
    
    Update CMakeLists.txt
    
    update documentation.
    
    improve documentation
---
 .../character/input/images/keypad-example.png      |   1 +
 .../components/drivers/character/input/index.rst   |   1 +
 .../input/{keypad.rst => keypad-keyboard.rst}      |   9 +-
 .../components/drivers/character/input/keypad.rst  | 217 +++++-------
 .../arm/stm32/common/include/stm32_kmatrix_gpio.h  |  87 +++++
 .../arm/stm32/common/include/stm32_kmatrix_i2c.h   |  87 +++++
 boards/arm/stm32/common/src/CMakeLists.txt         |   8 +
 boards/arm/stm32/common/src/Make.defs              |   8 +
 boards/arm/stm32/common/src/stm32_kmatrix_gpio.c   | 355 ++++++++++++++++++++
 boards/arm/stm32/common/src/stm32_kmatrix_i2c.c    | 210 ++++++++++++
 boards/arm/stm32/stm32f4discovery/Kconfig          |  19 ++
 boards/arm/stm32/stm32f4discovery/include/board.h  |  72 ++++
 .../arm/stm32/stm32f4discovery/src/stm32_bringup.c |  29 ++
 drivers/input/CMakeLists.txt                       |   8 +
 drivers/input/Kconfig                              |  68 +++-
 drivers/input/Make.defs                            |   8 +
 drivers/input/kmatrix.c                            | 362 +++++++++++++++++++++
 drivers/input/kmatrix_i2c.c                        | 262 +++++++++++++++
 include/nuttx/input/kmatrix.h                      | 160 +++++++++
 19 files changed, 1828 insertions(+), 143 deletions(-)

diff --git 
a/Documentation/components/drivers/character/input/images/keypad-example.png 
b/Documentation/components/drivers/character/input/images/keypad-example.png
new file mode 100644
index 00000000000..9fb4ea439c4
--- /dev/null
+++ b/Documentation/components/drivers/character/input/images/keypad-example.png
@@ -0,0 +1 @@
+<binary image placeholder>
\ No newline at end of file
diff --git a/Documentation/components/drivers/character/input/index.rst 
b/Documentation/components/drivers/character/input/index.rst
index c1317ebd493..3ef4746c003 100644
--- a/Documentation/components/drivers/character/input/index.rst
+++ b/Documentation/components/drivers/character/input/index.rst
@@ -5,6 +5,7 @@ Input Devices
 .. toctree::
   :caption: Supported Drivers
 
+  keypad-keyboard.rst
   keypad.rst
   sbutton.rst
 
diff --git a/Documentation/components/drivers/character/input/keypad.rst 
b/Documentation/components/drivers/character/input/keypad-keyboard.rst
similarity index 95%
copy from Documentation/components/drivers/character/input/keypad.rst
copy to Documentation/components/drivers/character/input/keypad-keyboard.rst
index 3724b8daa59..5e726b887dd 100644
--- a/Documentation/components/drivers/character/input/keypad.rst
+++ b/Documentation/components/drivers/character/input/keypad-keyboard.rst
@@ -2,9 +2,10 @@
 Keyboard/Keypad Drivers
 =======================
 
-**Keypads vs. Keyboards** Keyboards and keypads are really the
-same devices for NuttX. A keypad is thought of as simply a
-keyboard with fewer keys.
+
+**Keypads vs. Keyboards** Keyboards and keypads are really the same
+devices for NuttX. A keypad is thought of as simply a keyboard with
+fewer keys.
 
 **Special Commands**. In NuttX, a keyboard/keypad driver is simply
 a character driver that may have an (optional) encoding/decoding
@@ -30,7 +31,7 @@ can be used with the basic character driver to encode the 
keyboard
 events into the text data stream. The function interfaces that
 comprise that encoding/decoding layer are defined in the header
 file ``include/nuttx/input/kbd_code.h``. These functions provide
-an matched set of (a) driver encoding interfaces, and (b)
+a matched set of (a) driver encoding interfaces, and (b)
 application decoding interfaces.
 
 #. **Driver Encoding Interfaces**. These are interfaces used by
diff --git a/Documentation/components/drivers/character/input/keypad.rst 
b/Documentation/components/drivers/character/input/keypad.rst
index 3724b8daa59..93d357bc7ac 100644
--- a/Documentation/components/drivers/character/input/keypad.rst
+++ b/Documentation/components/drivers/character/input/keypad.rst
@@ -1,142 +1,83 @@
 =======================
-Keyboard/Keypad Drivers
+Matrix Keypad (KMATRIX)
 =======================
 
-**Keypads vs. Keyboards** Keyboards and keypads are really the
-same devices for NuttX. A keypad is thought of as simply a
-keyboard with fewer keys.
-
-**Special Commands**. In NuttX, a keyboard/keypad driver is simply
-a character driver that may have an (optional) encoding/decoding
-layer on the data returned by the character driver. A keyboard may
-return simple text data (alphabetic, numeric, and punctuation) or
-control characters (enter, control-C, etc.) when a key is pressed.
-We can think about this the "normal" keyboard data stream.
-However, in addition, most keyboards support actions that cannot
-be represented as text or control data. Such actions include
-things like cursor controls (home, up arrow, page down, etc.),
-editing functions (insert, delete, etc.), volume controls, (mute,
-volume up, etc.) and other special functions. In this case, some
-special encoding may be required to multiplex the normal text data
-and special command key press data streams.
-
-**Key Press and Release Events** Sometimes the time that a key is
-released is needed by applications as well. Thus, in addition to
-normal and special key press events, it may also be necessary to
-encode normal and special key release events.
-
-**Encoding/Decoding** Layer. An optional encoding/decoding layer
-can be used with the basic character driver to encode the keyboard
-events into the text data stream. The function interfaces that
-comprise that encoding/decoding layer are defined in the header
-file ``include/nuttx/input/kbd_code.h``. These functions provide
-an matched set of (a) driver encoding interfaces, and (b)
-application decoding interfaces.
-
-#. **Driver Encoding Interfaces**. These are interfaces used by
-   the keyboard/keypad driver to encode keyboard events and data.
-
-   -  ``kbd_press()``
-
-      **Function Prototype:**
-
-      **Description:**
-
-      **Input Parameters:**
-
-      -  ``ch``: The character to be added to the output stream.
-      -  ``stream``: An instance of ``lib_outstream_s`` to perform
-         the actual low-level put operation.
-
-      **Returned Value:**
-
-   -  ``kbd_release()``
-
-      **Function Prototype:**
-
-      **Description:**
-
-      **Input Parameters:**
-
-      -  ``ch``: The character associated with the key that was
-         released.
-      -  ``stream``: An instance of ``lib_outstream_s`` to perform
-         the actual low-level put operation.
-
-      **Returned Value:**
-
-   -  ``kbd_specpress()``
-
-      **Function Prototype:**
-
-      **Description:**
-
-      **Input Parameters:**
-
-      -  ``keycode``: The command to be added to the output
-         stream. The enumeration ``enum kbd_keycode_e keycode``
-         identifies all commands known to the system.
-      -  ``stream``: An instance of ``lib_outstream_s`` to perform
-         the actual low-level put operation.
-
-      **Returned Value:**
-
-   -  ``kbd_specrel()``
-
-      **Function Prototype:**
-
-      **Description:**
-
-      **Input Parameters:**
-
-      -  ``keycode``: The command to be added to the output
-         stream. The enumeration ``enum kbd_keycode_e keycode``
-         identifies all commands known to the system.
-      -  ``stream``: An instance of ``lib_outstream_s`` to perform
-         the actual low-level put operation.
-
-      **Returned Value:**
-
-#. **Application Decoding Interfaces**. These are user interfaces
-   to decode the values returned by the keyboard/keypad driver.
-
-   -  ``kbd_decode()``
-
-      **Function Prototype:**
-
-      **Description:**
-
-      **Input Parameters:**
-
-      -  ``stream``: An instance of ``lib_instream_s`` to perform
-         the actual low-level get operation.
-      -  ``pch``: The location to save the returned value. This
-         may be either a normal, character code or a special
-         command (i.e., a value from ``enum kbd_getstate_s``.
-      -  ``state``: A user provided buffer to support parsing.
-         This structure should be cleared the first time that
-         ``kbd_decode()`` is called.
-
-      **Returned Value:**
-
-      -  ``KBD_PRESS`` (0)**: Indicates the successful receipt
-         of normal, keyboard data. This corresponds to a keypress
-         event. The returned value in ``pch`` is a simple byte of
-         text or control data.
-      -  ``KBD_RELEASE`` (1)**: Indicates a key release event.
-         The returned value in ``pch`` is the byte of text or
-         control data corresponding to the released key.
-      -  ``KBD_SPECPRESS`` (2)**: Indicates the successful
-         receipt of a special keyboard command. The returned value
-         in ``pch`` is a value from ``enum kbd_getstate_s``.
-      -  ``KBD_SPECREL`` (3)**: Indicates a special command key
-         release event. The returned value in ``pch`` is a value
-         from ``enum kbd_getstate_s``.
-      -  ``KBD_ERROR`` (``EOF``)**: An error has getting the
-         next character (reported by the ``stream``). Normally
-         indicates the end of file.
-
-**I/O Streams**. Notice the use of the abstract I/O streams in
-these interfaces. These stream interfaces are defined in
-``include/nuttx/streams.h``.
+**What is a Keypad?**
+A keypad is a small keyboard with a limited set of keys, typically
+arranged in a matrix. It is commonly used for numeric input, access
+control, or simple user interfaces.
+
+For example, a typical 12-key numeric keypad looks like this:
+
+.. image:: images/keypad-example.png
+  :alt: Example of a 12-key matrix keypad
+  :align: center
+  :width: 200px
+
+**Purpose**. The KMATRIX driver provides a generic keypad
+implementation for boards that expose a switch matrix through GPIOs.
+It periodically scans rows and columns, detects state changes with a
+simple debounce, and emits keyboard events through the common keyboard
+upper-half. This makes the device available as a character driver
+(e.g., ``/dev/keypad0``) using the standard keyboard
+interfaces.
+
+**Why Polling**. This first version uses polling to be broadly usable
+on any board with available GPIOs, without requiring per-board IRQ
+wiring, pin interrupt capabilities, or expander-specific interrupt
+support. Polling also simplifies early bring-up and makes the driver
+predictable while the keymap and GPIO configuration are validated.
+Future iterations are expected to add interrupt-driven scanning and
+I2C expander variants; the GPIO polling path remains a good baseline
+and fallback.
+
+**Driver Overview**. The KMATRIX lower-half scans the matrix and calls
+``keyboard_event()`` when it detects a press or release. The keyboard
+upper-half registers the character device at the requested ``devpath``
+and stores events in a circular buffer. Applications read
+``struct keyboard_event_s`` from the device or use the optional
+kbd-codec layer.
+
+**Board Support**. To support KMATRIX, a board must provide:
+
+#. **GPIO Definitions**
+
+   - Define the row and column GPIOs (arrays of pins).
+   - Provide a keymap array indexed by ``row * ncols + col``.
+
+#. **Configuration Callbacks**
+
+   - ``config_row(pin)``: Configure a row GPIO as output.
+   - ``config_col(pin)``: Configure a column GPIO as input with pull-up
+     or pull-down consistent with the wiring.
+   - ``row_set(pin, active)``: Drive a row active/inactive. For the
+     STM32F4Discovery example, rows are driven low to activate.
+   - ``col_get(pin)``: Read a column and return ``true`` when pressed.
+
+#. **Registration Hook**
+
+   - Implement ``board_kmatrix_initialize(const char *devpath)`` to
+     call ``kmatrix_register(&config, devpath)``.
+   - Invoke the board hook during bring-up (for example,
+     ``board_kmatrix_initialize("/dev/keypad0")``).
+
+**Reference Implementation (STM32F4Discovery)**. The current reference
+is in ``boards/arm/stm32/common/src/stm32_kmatrix_gpio.c``:
+
+- Rows: ``BOARD_KMATRIX_ROW0..3`` (outputs)
+- Columns: ``BOARD_KMATRIX_COL0..2`` (inputs with pull-up)
+- Keymap: 4x3 phone keypad layout
+- Callbacks: ``km_stm32_config_row``, ``km_stm32_config_col``,
+  ``km_stm32_row_set``, ``km_stm32_col_get``
+- Registration: ``board_kmatrix_initialize()`` calls
+  ``kmatrix_register()``
+
+**Data Path Summary**.
+
+- Board calls ``board_kmatrix_initialize("/dev/keypad0")``
+- ``kmatrix_register()`` configures GPIOs and calls
+  ``keyboard_register(&lower, devpath, buflen)``
+- The upper-half registers the device node at ``devpath``
+- ``kmatrix_scan_worker()`` calls ``keyboard_event()`` on press/release
+- Applications read events from the device node
 
diff --git a/boards/arm/stm32/common/include/stm32_kmatrix_gpio.h 
b/boards/arm/stm32/common/include/stm32_kmatrix_gpio.h
new file mode 100644
index 00000000000..6615fcc9b36
--- /dev/null
+++ b/boards/arm/stm32/common/include/stm32_kmatrix_gpio.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+ * boards/arm/stm32/common/include/stm32_kmatrix_gpio.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_STM32_KMATRIX_GPIO_H
+#define __BOARDS_ARM_STM32_COMMON_INCLUDE_STM32_KMATRIX_GPIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Type Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Inline Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: board_kmatrix_initialize
+ *
+ * Description:
+ *   This function is called by application-specific setup logic to
+ *   configure the keyboard matrix device.
+ *
+ * Input Parameters:
+ *   devpath - The device path, typically "/dev/kbd0"
+ *
+ * Returned Value:
+ *   Zero is returned on success.  Otherwise, a negated errno value is
+ *   returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int board_kmatrix_initialize(const char *devpath);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BOARDS_ARM_STM32_COMMON_INCLUDE_STM32_KMATRIX_GPIO_H */
diff --git a/boards/arm/stm32/common/include/stm32_kmatrix_i2c.h 
b/boards/arm/stm32/common/include/stm32_kmatrix_i2c.h
new file mode 100644
index 00000000000..82669a943db
--- /dev/null
+++ b/boards/arm/stm32/common/include/stm32_kmatrix_i2c.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+ * boards/arm/stm32/common/include/stm32_kmatrix_i2c.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_STM32_KMATRIX_I2C_H
+#define __BOARDS_ARM_STM32_COMMON_INCLUDE_STM32_KMATRIX_I2C_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Type Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Inline Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: board_kmatrix_i2c_initialize
+ *
+ * Description:
+ *   This function is called by application-specific setup logic to
+ *   configure the keyboard matrix device using an I2C GPIO expander.
+ *
+ * Input Parameters:
+ *   devpath - The device path, typically "/dev/kbd0"
+ *
+ * Returned Value:
+ *   Zero is returned on success.  Otherwise, a negated errno value is
+ *   returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int board_kmatrix_i2c_initialize(const char *devpath);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BOARDS_ARM_STM32_COMMON_INCLUDE_STM32_KMATRIX_I2C_H */
diff --git a/boards/arm/stm32/common/src/CMakeLists.txt 
b/boards/arm/stm32/common/src/CMakeLists.txt
index d13a75b1a4d..acbd4200a7f 100644
--- a/boards/arm/stm32/common/src/CMakeLists.txt
+++ b/boards/arm/stm32/common/src/CMakeLists.txt
@@ -158,4 +158,12 @@ if(CONFIG_INPUT_SBUTTON)
   list(APPEND SRCS stm32_sbutton.c)
 endif()
 
+if(CONFIG_INPUT_KMATRIX)
+  list(APPEND SRCS stm32_kmatrix_gpio.c)
+endif()
+
+if(CONFIG_INPUT_KMATRIX_I2C)
+  list(APPEND SRCS stm32_kmatrix_i2c.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 1048df7d17a..a86d230436e 100644
--- a/boards/arm/stm32/common/src/Make.defs
+++ b/boards/arm/stm32/common/src/Make.defs
@@ -166,6 +166,14 @@ ifeq ($(CONFIG_INPUT_SBUTTON),y)
   CSRCS += stm32_sbutton.c
 endif
 
+ifeq ($(CONFIG_INPUT_KMATRIX),y)
+  CSRCS += stm32_kmatrix_gpio.c
+endif
+
+ifeq ($(CONFIG_INPUT_KMATRIX_I2C),y)
+  CSRCS += stm32_kmatrix_i2c.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_kmatrix_gpio.c 
b/boards/arm/stm32/common/src/stm32_kmatrix_gpio.c
new file mode 100644
index 00000000000..fd107c71687
--- /dev/null
+++ b/boards/arm/stm32/common/src/stm32_kmatrix_gpio.c
@@ -0,0 +1,355 @@
+/****************************************************************************
+ * boards/arm/stm32/common/src/stm32_kmatrix_gpio.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 <unistd.h>
+
+#include <nuttx/board.h>
+#include <arch/board/board.h>
+#include <nuttx/input/kmatrix.h>
+
+#include "stm32.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+typedef uint32_t kmatrix_pin_t;
+
+struct stm32_kmatrix_gpio_config_s
+{
+  /* Configuration structure as seen by the kmatrix driver */
+
+  struct kmatrix_config_s config;
+
+  /* Additional private definitions only known to this driver */
+
+  void *arg; /* Argument to pass if needed */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void km_stm32_config_row(kmatrix_pin_t pin);
+static void km_stm32_config_col(kmatrix_pin_t pin);
+static void km_stm32_row_set(kmatrix_pin_t pin, bool active);
+static bool km_stm32_col_get(kmatrix_pin_t pin);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Row and column GPIO pin definitions for 4x3 keypad matrix on
+ * STM32F4Discovery
+ * Rows: PB0-PB3 (outputs)
+ * Columns: PC0-PC2 (inputs with pull-up)
+ */
+
+static const kmatrix_pin_t g_km_rows[] =
+{
+  BOARD_KMATRIX_ROW0,
+  BOARD_KMATRIX_ROW1,
+  BOARD_KMATRIX_ROW2,
+  BOARD_KMATRIX_ROW3,
+};
+
+static const kmatrix_pin_t g_km_cols[] =
+{
+  BOARD_KMATRIX_COL0,
+  BOARD_KMATRIX_COL1,
+  BOARD_KMATRIX_COL2,
+};
+
+/* Keymap for 4x3 matrix - Standard phone keypad layout
+ * Rows: 0-3, Columns: 0-2
+ */
+
+static const uint32_t g_km_keymap[] =
+{
+  '1', '2', '3',  /* Row 0 */
+  '4', '5', '6',  /* Row 1 */
+  '7', '8', '9',  /* Row 2 */
+  '*', '0', '#',  /* Row 3 */
+};
+
+/* A reference to a structure of this type must be passed to the kmatrix
+ * driver.  This structure provides information about the configuration
+ * of the keypad matrix and provides GPIO callbacks.
+ *
+ * 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_kmatrix_gpio_config_s g_km_config =
+{
+  .config =
+    {
+      .nrows            = 4,
+      .ncols            = 3,
+      .rows             = g_km_rows,
+      .cols             = g_km_cols,
+      .keymap           = g_km_keymap,
+      .poll_interval_ms = CONFIG_INPUT_KMATRIX_POLL_MS,
+      .config_row       = km_stm32_config_row,
+      .config_col       = km_stm32_config_col,
+      .row_set          = km_stm32_row_set,
+      .col_get          = km_stm32_col_get,
+    },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: km_stm32_config_row
+ *
+ * Description:
+ *   Configure a row GPIO pin as an output
+ *
+ ****************************************************************************/
+
+static void km_stm32_config_row(kmatrix_pin_t pin)
+{
+  iinfo("Configuring row pin as output\n");
+  stm32_configgpio(pin);
+  stm32_gpiowrite(pin, true); /* Initialize to inactive (high) */
+}
+
+/****************************************************************************
+ * Name: km_stm32_config_col
+ *
+ * Description:
+ *   Configure a column GPIO pin as an input with pull-up
+ *
+ ****************************************************************************/
+
+static void km_stm32_config_col(kmatrix_pin_t pin)
+{
+  iinfo("Configuring column pin as input\n");
+  stm32_configgpio(pin);
+}
+
+/****************************************************************************
+ * Name: km_stm32_row_set
+ *
+ * Description:
+ *   Activate or deactivate a row (logic: active=true sets to 0/low to
+ *   activate the row, active=false sets to 1/high to deactivate)
+ *
+ ****************************************************************************/
+
+static void km_stm32_row_set(kmatrix_pin_t pin, bool active)
+{
+  /* With diodes, we drive rows low to activate.
+   * active=true  -> write 0 (low)
+   * active=false -> write 1 (high)
+   */
+
+  stm32_gpiowrite(pin, active ? 0 : 1);
+}
+
+/****************************************************************************
+ * Name: km_stm32_col_get
+ *
+ * Description:
+ *   Read the state of a column GPIO pin
+ *
+ ****************************************************************************/
+
+static bool km_stm32_col_get(kmatrix_pin_t pin)
+{
+  /* With pull-up resistors:
+   * Key pressed   -> column goes low (0) when row is driven low
+   * Key released  -> column stays high (1)
+   * Return true when pressed (low), false when released (high)
+   */
+
+  return !stm32_gpioread(pin);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: board_kmatrix_initialize
+ *
+ * Description:
+ *   This function is called by application-specific setup logic to
+ *   configure the keyboard matrix device.
+ *
+ * Input Parameters:
+ *   devpath - The device path, typically "/dev/keypad0"
+ *
+ * Returned Value:
+ *   Zero is returned on success.  Otherwise, a negated errno value is
+ *   returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int board_kmatrix_initialize(const char *devpath)
+{
+  iinfo("Initializing keyboard matrix at %s\n", devpath);
+
+  /* Register the keyboard matrix with the generic driver */
+
+  return kmatrix_register(&g_km_config.config, devpath);
+}
+
+int board_kmatrix_diag(int loops, int delay_ms)
+{
+  int iter = 0;
+  uint8_t last_bits[4] =
+  {
+    0xff, 0xff, 0xff, 0xff
+  };
+
+  const useconds_t pulse_us = 200000;
+
+  iinfo("KMATRIX diag: pin identify pulses (disconnect keypad)\n");
+  for (unsigned int r = 0; r < g_km_config.config.nrows; r++)
+    {
+      iinfo("Pulse ROW%u\n", r + 1);
+      stm32_configgpio(g_km_rows[r]);
+      stm32_gpiowrite(g_km_rows[r], true);
+      usleep(pulse_us);
+      stm32_gpiowrite(g_km_rows[r], false);
+      usleep(pulse_us);
+      stm32_gpiowrite(g_km_rows[r], true);
+      usleep(pulse_us);
+    }
+
+  iinfo("KMATRIX diag: column pulses require BOARD_KMATRIX_COLx_OUT\n");
+#ifdef BOARD_KMATRIX_COL0_OUT
+  iinfo("Pulse COL1 (output mode)\n");
+  stm32_configgpio(BOARD_KMATRIX_COL0_OUT);
+  stm32_gpiowrite(BOARD_KMATRIX_COL0_OUT, true);
+  usleep(pulse_us);
+  stm32_gpiowrite(BOARD_KMATRIX_COL0_OUT, false);
+  usleep(pulse_us);
+  stm32_gpiowrite(BOARD_KMATRIX_COL0_OUT, true);
+  usleep(pulse_us);
+#endif
+
+#ifdef BOARD_KMATRIX_COL1_OUT
+  iinfo("Pulse COL2 (output mode)\n");
+  stm32_configgpio(BOARD_KMATRIX_COL1_OUT);
+  stm32_gpiowrite(BOARD_KMATRIX_COL1_OUT, true);
+  usleep(pulse_us);
+  stm32_gpiowrite(BOARD_KMATRIX_COL1_OUT, false);
+  usleep(pulse_us);
+  stm32_gpiowrite(BOARD_KMATRIX_COL1_OUT, true);
+  usleep(pulse_us);
+#endif
+
+#ifdef BOARD_KMATRIX_COL2_OUT
+  iinfo("Pulse COL3 (output mode)\n");
+  stm32_configgpio(BOARD_KMATRIX_COL2_OUT);
+  stm32_gpiowrite(BOARD_KMATRIX_COL2_OUT, true);
+  usleep(pulse_us);
+  stm32_gpiowrite(BOARD_KMATRIX_COL2_OUT, false);
+  usleep(pulse_us);
+  stm32_gpiowrite(BOARD_KMATRIX_COL2_OUT, true);
+  usleep(pulse_us);
+#endif
+
+  for (unsigned int r = 0; r < g_km_config.config.nrows; r++)
+    {
+      km_stm32_config_row(g_km_rows[r]);
+      stm32_gpiowrite(g_km_rows[r], true);
+    }
+
+  for (unsigned int c = 0; c < g_km_config.config.ncols; c++)
+    {
+      km_stm32_config_col(g_km_cols[c]);
+    }
+
+  iinfo("KMATRIX diag: loops=%d delay_ms=%d\n", loops, delay_ms);
+
+  while (loops <= 0 || iter < loops)
+    {
+      for (unsigned int r = 0; r < g_km_config.config.nrows; r++)
+        {
+          for (unsigned int rr = 0; rr < g_km_config.config.nrows; rr++)
+            {
+              stm32_gpiowrite(g_km_rows[rr], true);
+            }
+
+          stm32_gpiowrite(g_km_rows[r], false);
+          usleep(1000);
+
+          if (g_km_config.config.ncols == 3)
+            {
+              bool b0 = stm32_gpioread(g_km_cols[0]);
+              bool b1 = stm32_gpioread(g_km_cols[1]);
+              bool b2 = stm32_gpioread(g_km_cols[2]);
+              uint8_t bits = (b0 ? 1 : 0) |
+                             (b1 ? 2 : 0) |
+                             (b2 ? 4 : 0);
+
+              if (bits != last_bits[r])
+                {
+                  iinfo("ROW=%u COLS(raw)=%d%d%d\n",
+                        r + 1, b0 ? 1 : 0, b1 ? 1 : 0, b2 ? 1 : 0);
+                  last_bits[r] = bits;
+                }
+            }
+
+          for (unsigned int c = 0; c < g_km_config.config.ncols; c++)
+            {
+              bool pressed = !stm32_gpioread(g_km_cols[c]);
+              if (pressed)
+                {
+                  iinfo("ROW=%u COL=%u\n", r + 1, c + 1);
+                  while (!stm32_gpioread(g_km_cols[c]))
+                    {
+                      usleep(1000);
+                    }
+                }
+            }
+        }
+
+      if (delay_ms > 0)
+        {
+          usleep(delay_ms * 1000);
+        }
+
+      iter++;
+    }
+
+  return OK;
+}
diff --git a/boards/arm/stm32/common/src/stm32_kmatrix_i2c.c 
b/boards/arm/stm32/common/src/stm32_kmatrix_i2c.c
new file mode 100644
index 00000000000..da8ab87a60e
--- /dev/null
+++ b/boards/arm/stm32/common/src/stm32_kmatrix_i2c.c
@@ -0,0 +1,210 @@
+/****************************************************************************
+ * boards/arm/stm32/common/src/stm32_kmatrix_i2c.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 <stdio.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/input/kmatrix.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/ioexpander/ioexpander.h>
+
+#include <arch/board/board.h>
+
+#include "stm32.h"
+#include "stm32_i2c.h"
+
+#ifdef CONFIG_IOEXPANDER_MCP23X08
+#  include <nuttx/ioexpander/mcp23x08.h>
+#endif
+
+#ifdef CONFIG_IOEXPANDER_PCA9538
+#  include <nuttx/ioexpander/pca9538.h>
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+typedef uint32_t kmatrix_pin_t;
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Row and column pin definitions for 4x3 keypad matrix via I2C expander
+ *
+ * For MCP23X08/PCA9538 I2C expanders, pins are numbered 0-7.
+ *
+ * Example mapping:
+ *   Rows (outputs):    Pins 0-3
+ *   Columns (inputs):  Pins 4-6 (with pull-ups)
+ */
+
+static const kmatrix_pin_t g_km_rows[] =
+{
+  0, 1, 2, 3,  /* Row 0-3: Output pins on expander */
+};
+
+static const kmatrix_pin_t g_km_cols[] =
+{
+  4, 5, 6,  /* Col 0-2: Input pins on expander (with pull-up) */
+};
+
+/* Keymap for 4x3 matrix - Standard phone keypad layout
+ * Rows: 0-3, Columns: 0-2
+ */
+
+static const uint32_t g_km_keymap[] =
+{
+  '1', '2', '3',  /* Row 0 */
+  '4', '5', '6',  /* Row 1 */
+  '7', '8', '9',  /* Row 2 */
+  '*', '0', '#',  /* Row 3 */
+};
+
+/* Get callbacks from I2C driver */
+
+extern FAR struct kmatrix_callbacks_s *kmatrix_i2c_get_callbacks(void);
+
+/* Keyboard matrix configuration structure
+ * Callbacks are set in board_kmatrix_i2c_initialize.
+ */
+
+static struct kmatrix_config_s g_km_i2c_config =
+{
+  .nrows              = 4,
+  .ncols              = 3,
+  .rows               = g_km_rows,
+  .cols               = g_km_cols,
+  .keymap             = g_km_keymap,
+  .poll_interval_ms   = CONFIG_INPUT_KMATRIX_POLL_MS,
+};
+
+/* IO expander configuration */
+
+#ifdef CONFIG_IOEXPANDER_MCP23X08
+static struct mcp23x08_config_s g_mcp23x08_config =
+{
+  .address   = CONFIG_STM32_KMATRIX_I2C_ADDR,
+  .frequency = CONFIG_STM32_KMATRIX_I2C_FREQ,
+};
+#endif
+
+#ifdef CONFIG_IOEXPANDER_PCA9538
+static struct pca9538_config_s g_pca9538_config =
+{
+  .address   = CONFIG_STM32_KMATRIX_I2C_ADDR,
+  .frequency = CONFIG_STM32_KMATRIX_I2C_FREQ,
+};
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/**
+ * Name: board_kmatrix_i2c_initialize
+ *
+ * Description:
+ *   Initialize keyboard matrix driver using I2C GPIO expander.
+ *   This function is called by stm32_bringup.c during initialization.
+ *
+ * Input Parameters:
+ *   devpath - Device path (e.g., "/dev/kbd0")
+ *
+ * Returned Value:
+ *   Zero on success; negated errno on failure.
+ */
+
+int board_kmatrix_i2c_initialize(const char *devpath)
+{
+  FAR struct i2c_master_s *i2c;
+  FAR struct ioexpander_dev_s *ioe;
+  FAR struct kmatrix_callbacks_s *callbacks;
+  int ret;
+
+  iinfo("Initializing keyboard matrix via I2C expander\n");
+
+  /* Initialize I2C bus */
+
+  i2c = stm32_i2cbus_initialize(CONFIG_STM32_KMATRIX_I2C_BUS);
+  if (i2c == NULL)
+    {
+      ierr("ERROR: Failed to initialize I2C bus %d\n",
+           CONFIG_STM32_KMATRIX_I2C_BUS);
+      return -ENODEV;
+    }
+
+  /* Initialize IO expander */
+
+#ifdef CONFIG_IOEXPANDER_MCP23X08
+  ioe = mcp23x08_initialize(i2c, &g_mcp23x08_config);
+  if (ioe == NULL)
+    {
+      ierr("ERROR: Failed to initialize MCP23X08\n");
+      stm32_i2cbus_uninitialize(i2c);
+      return -ENODEV;
+    }
+
+  iinfo("MCP23X08 initialized at 0x%02x\n", CONFIG_STM32_KMATRIX_I2C_ADDR);
+#elif defined(CONFIG_IOEXPANDER_PCA9538)
+  ioe = pca9538_initialize(i2c, &g_pca9538_config);
+  if (ioe == NULL)
+    {
+      ierr("ERROR: Failed to initialize PCA9538\n");
+      stm32_i2cbus_uninitialize(i2c);
+      return -ENODEV;
+    }
+
+  iinfo("PCA9538 initialized at 0x%02x\n", CONFIG_STM32_KMATRIX_I2C_ADDR);
+#else
+#  error "No IO expander configured"
+#endif
+
+  /* Get callbacks from I2C driver */
+
+  callbacks = kmatrix_i2c_get_callbacks();
+  g_km_i2c_config.config_row = callbacks->config_row;
+  g_km_i2c_config.config_col = callbacks->config_col;
+  g_km_i2c_config.row_set = callbacks->row_set;
+  g_km_i2c_config.col_get = callbacks->col_get;
+
+  /* Register keyboard matrix driver */
+
+  ret = kmatrix_i2c_register(ioe, &g_km_i2c_config, devpath);
+  if (ret < 0)
+    {
+      ierr("ERROR: Failed to register keyboard matrix: %d\n", ret);
+      stm32_i2cbus_uninitialize(i2c);
+      return ret;
+    }
+
+  iinfo("Keyboard matrix I2C driver registered at %s\n", devpath);
+  return OK;
+}
diff --git a/boards/arm/stm32/stm32f4discovery/Kconfig 
b/boards/arm/stm32/stm32f4discovery/Kconfig
index 7b86a5cafe3..084ab04bc12 100644
--- a/boards/arm/stm32/stm32f4discovery/Kconfig
+++ b/boards/arm/stm32/stm32f4discovery/Kconfig
@@ -120,4 +120,23 @@ config PM_SLEEP_WAKEUP_NSEC
                Number of additional nanoseconds to wait in PM_SLEEP before 
going to
                PM_STANDBY mode.
 
+if INPUT_KMATRIX_I2C
+
+config STM32_KMATRIX_I2C_BUS
+       int "I2C Bus Number"
+       default 1
+       ---help---
+               I2C bus number to use for the keyboard matrix GPIO expander.
+               Common values: 1 or 2 (depends on available I2C interfaces).
+
+config STM32_KMATRIX_I2C_ADDR
+       hex "I2C Slave Address of GPIO Expander"
+       default 0x20
+       ---help---
+               I2C slave address of the GPIO expander (PCF8574 or MCP23017).
+               PCF8574/MCP23017 default addresses (7-bit):
+                 0x20-0x27: varies with A0-A2 pins (default is 0x20)
+
+endif # INPUT_KMATRIX_I2C
+
 endif
diff --git a/boards/arm/stm32/stm32f4discovery/include/board.h 
b/boards/arm/stm32/stm32f4discovery/include/board.h
index ddc86ddc5fe..7392eff96cb 100644
--- a/boards/arm/stm32/stm32f4discovery/include/board.h
+++ b/boards/arm/stm32/stm32f4discovery/include/board.h
@@ -472,4 +472,76 @@
 
 #define BOARD_XEN1210_PWMTIMER   1
 
+/* Keyboard Matrix Configuration */
+
+/* Define keyboard matrix row pins (outputs) */
+
+#define GPIO_KMATRIX_ROW0  (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
+                            GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN7)
+#define GPIO_KMATRIX_ROW1  (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
+                            GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN8)
+#define GPIO_KMATRIX_ROW2  (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
+                            GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN9)
+#define GPIO_KMATRIX_ROW3  (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
+                            GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN10)
+
+/* Row pins as inputs with pull-up for early diagnostics */
+
+#define GPIO_KMATRIX_ROW0_IN (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|\
+                              GPIO_PORTE|GPIO_PIN7)
+#define GPIO_KMATRIX_ROW1_IN (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|\
+                              GPIO_PORTE|GPIO_PIN8)
+#define GPIO_KMATRIX_ROW2_IN (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|\
+                              GPIO_PORTE|GPIO_PIN9)
+#define GPIO_KMATRIX_ROW3_IN (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|\
+                              GPIO_PORTE|GPIO_PIN10)
+
+/* Define keyboard matrix column pins (inputs) */
+
+#define GPIO_KMATRIX_COL0  (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|\
+                            GPIO_PORTE|GPIO_PIN11)
+#define GPIO_KMATRIX_COL1  (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|\
+                            GPIO_PORTE|GPIO_PIN13)
+#define GPIO_KMATRIX_COL2  (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|\
+                            GPIO_PORTE|GPIO_PIN14)
+
+/* Column pins as outputs for diagnostics only */
+
+#define GPIO_KMATRIX_COL0_OUT (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
+                               GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN11)
+#define GPIO_KMATRIX_COL1_OUT (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
+                               GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN13)
+#define GPIO_KMATRIX_COL2_OUT (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
+                               GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN14)
+
+/* Board-level KMATRIX pin definitions */
+
+#define BOARD_KMATRIX_ROW0  GPIO_KMATRIX_ROW0
+#define BOARD_KMATRIX_ROW1  GPIO_KMATRIX_ROW1
+#define BOARD_KMATRIX_ROW2  GPIO_KMATRIX_ROW2
+#define BOARD_KMATRIX_ROW3  GPIO_KMATRIX_ROW3
+
+#define BOARD_KMATRIX_ROW0_IN  GPIO_KMATRIX_ROW0_IN
+#define BOARD_KMATRIX_ROW1_IN  GPIO_KMATRIX_ROW1_IN
+#define BOARD_KMATRIX_ROW2_IN  GPIO_KMATRIX_ROW2_IN
+#define BOARD_KMATRIX_ROW3_IN  GPIO_KMATRIX_ROW3_IN
+
+#define BOARD_KMATRIX_COL0  GPIO_KMATRIX_COL0
+#define BOARD_KMATRIX_COL1  GPIO_KMATRIX_COL1
+#define BOARD_KMATRIX_COL2  GPIO_KMATRIX_COL2
+
+#define BOARD_KMATRIX_COL0_OUT  GPIO_KMATRIX_COL0_OUT
+#define BOARD_KMATRIX_COL1_OUT  GPIO_KMATRIX_COL1_OUT
+#define BOARD_KMATRIX_COL2_OUT  GPIO_KMATRIX_COL2_OUT
+
+#ifdef CONFIG_INPUT_KMATRIX
+int board_kmatrix_diag(int loops, int delay_ms);
+#endif
+
+/* Keyboard Matrix I2C Configuration */
+
+#define CONFIG_STM32_KMATRIX_I2C_BUS   1         /* I2C1 */
+#define CONFIG_STM32_KMATRIX_I2C_ADDR  0x20      /* MCP23X08/PCA9538 address */
+#define CONFIG_STM32_KMATRIX_I2C_FREQ  400000    /* 400 kHz */
+
 #endif /* __BOARDS_ARM_STM32_STM32F4DISCOVERY_INCLUDE_BOARD_H */
diff --git a/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c 
b/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c
index 59742accb3e..058e5eead26 100644
--- a/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c
+++ b/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c
@@ -102,6 +102,14 @@
 #include "board_sbutton.h"
 #endif
 
+#ifdef CONFIG_INPUT_KMATRIX
+#include "stm32_kmatrix_gpio.h"
+#endif
+
+#ifdef CONFIG_INPUT_KMATRIX_I2C
+#include "stm32_kmatrix_i2c.h"
+#endif
+
 #ifdef CONFIG_SENSORS_ZEROCROSS
 #include "stm32_zerocross.h"
 #endif
@@ -414,6 +422,27 @@ int stm32_bringup(void)
     }
 #endif
 
+#ifdef CONFIG_INPUT_KMATRIX
+  /* Initialize and register the keyboard matrix driver */
+
+  ret = board_kmatrix_initialize(CONFIG_INPUT_KMATRIX_DEVPATH);
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: board_kmatrix_initialize() failed: %d\n", ret);
+    }
+#endif
+
+#ifdef CONFIG_INPUT_KMATRIX_I2C
+  /* Initialize and register the keyboard matrix driver via I2C expander */
+
+  ret = board_kmatrix_i2c_initialize("/dev/kbd0");
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: board_kmatrix_i2c_initialize() failed: %d\n",
+             ret);
+    }
+#endif
+
 #ifdef CONFIG_INPUT_NUNCHUCK
   /* Register the Nunchuck driver */
 
diff --git a/drivers/input/CMakeLists.txt b/drivers/input/CMakeLists.txt
index 3b897b1a733..1b088339188 100644
--- a/drivers/input/CMakeLists.txt
+++ b/drivers/input/CMakeLists.txt
@@ -106,6 +106,14 @@ if(CONFIG_INPUT)
     list(APPEND SRCS keyboard_upper.c)
   endif()
 
+  if(CONFIG_INPUT_KMATRIX)
+    list(APPEND SRCS kmatrix.c)
+  endif()
+
+  if(CONFIG_INPUT_KMATRIX_I2C)
+    list(APPEND SRCS kmatrix_i2c.c)
+  endif()
+
   if(CONFIG_INPUT_SBUTTON)
     list(APPEND SRCS sbutton.c)
   endif()
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 3917fe6538e..d1695ee6f26 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -723,7 +723,7 @@ config INPUT_SPQ10KBD
        select I2C
        ---help---
                Enable the Solder Party Q10 BlackBerry Keyboard support.  This
-               exposes itself as a standard keyboard at /dev/kbdN.
+               exposes itself as a standard keyboard at /dev/keypadN.
                This keyboard exists both as a standalone module and integrated
                into the Solder Party Keyboard FeatherWing.  Information on this
                can be found at https://www.solder.party/docs/keyboard-pmod/
@@ -749,4 +749,70 @@ config SPQ10KBD_NPOLLWAITERS
 
 endif # INPUT_SPQ10KBD
 
+config INPUT_KMATRIX
+       bool "Keyboard Matrix Driver"
+       default n
+       select INPUT_KEYBOARD
+       ---help---
+               Enable support for keyboard matrix input devices with 
polling-based
+               scanning. This driver supports NxM key matrices with debounce 
and
+               anti-ghosting using diodes.
+
+if INPUT_KMATRIX
+
+config INPUT_KMATRIX_BUFSIZE
+       int "Keyboard matrix buffer size"
+       default 64
+       ---help---
+               Size of the keyboard event buffer for each open file descriptor.
+
+config INPUT_KMATRIX_POLL_MS
+       int "Polling interval (milliseconds)"
+       default 10
+       ---help---
+               Time interval in milliseconds between matrix scans. Smaller 
values
+               provide lower latency but consume more CPU. Default: 10ms.
+
+config INPUT_KMATRIX_DEBOUNCE
+       int "Debounce threshold (scan cycles)"
+       default 3
+       ---help---
+               Number of consecutive scan cycles a key must maintain the same 
state
+               before generating a press/release event. Higher values provide 
more
+               robust debounce but increase latency. Default: 3 cycles.
+
+config INPUT_KMATRIX_DEVPATH
+       string "Device path"
+       default "/dev/keypad0"
+       ---help---
+               Path where the keyboard matrix device will be registered. 
Default: /dev/keypad0
+
+config INPUT_KMATRIX_I2C
+       bool "Keyboard Matrix via I2C GPIO Expander"
+       depends on INPUT_KMATRIX && I2C
+       default n
+       ---help---
+               Enable keyboard matrix driver using I2C GPIO expander (PCF8574 
or MCP23017).
+               Requires I2C support to be enabled.
+
+if INPUT_KMATRIX_I2C
+
+config INPUT_KMATRIX_I2C_PCF8574
+       bool "Use PCF8574 I2C Expander"
+       default n
+       ---help---
+               Use PCF8574 I2C GPIO expander (8-bit, quasi-bidirectional I/O).
+               This is a simple, low-cost 8-bit I/O expander commonly used in 
hobbyist projects.
+
+config INPUT_KMATRIX_I2C_MCP23017
+       bool "Use MCP23017 I2C Expander"
+       default n
+       ---help---
+               Use MCP23017 I2C GPIO expander (16-bit, configurable I/O 
directions).
+               This offers more features and flexibility than PCF8574.
+
+endif # INPUT_KMATRIX_I2C
+
+endif # INPUT_KMATRIX
+
 endif # INPUT
diff --git a/drivers/input/Make.defs b/drivers/input/Make.defs
index 815a4ce847d..66862bbbde3 100644
--- a/drivers/input/Make.defs
+++ b/drivers/input/Make.defs
@@ -126,6 +126,14 @@ ifeq ($(CONFIG_INPUT_SPQ10KBD),y)
   CSRCS += spq10kbd.c
 endif
 
+ifeq ($(CONFIG_INPUT_KMATRIX),y)
+  CSRCS += kmatrix.c
+endif
+
+ifeq ($(CONFIG_INPUT_KMATRIX_I2C),y)
+  CSRCS += kmatrix_i2c.c
+endif
+
 ifeq ($(CONFIG_INPUT_GOLDFISH_EVENTS),y)
   CSRCS += goldfish_events.c
 endif
diff --git a/drivers/input/kmatrix.c b/drivers/input/kmatrix.c
new file mode 100644
index 00000000000..23375b7c980
--- /dev/null
+++ b/drivers/input/kmatrix.c
@@ -0,0 +1,362 @@
+/****************************************************************************
+ * drivers/input/kmatrix.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/kmatrix.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/clock.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
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct kmatrix_dev_s
+{
+  FAR const struct kmatrix_config_s *config;  /* Board configuration data */
+
+  mutex_t lock;           /* Exclusive access to device */
+  struct work_s work;     /* Work queue for polling */
+  uint16_t poll_interval; /* Polling interval in milliseconds */
+
+  /* Current and previous state of the matrix (bitfield) */
+
+  FAR uint8_t *state;     /* Current state bitmap */
+  FAR uint8_t *debounce;  /* Debounce counter */
+
+  /* Keyboard lower-half registration */
+
+  struct keyboard_lowerhalf_s lower;
+};
+
+/****************************************************************************
+ * Static Function Prototypes
+ ****************************************************************************/
+
+static void kmatrix_scan_worker(FAR void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: kmatrix_get_state
+ *
+ * Description:
+ *   Get the current state of a key at position (row, col)
+ *
+ ****************************************************************************/
+
+static bool kmatrix_get_state(FAR struct kmatrix_dev_s *priv,
+                               uint8_t row, uint8_t col)
+{
+  uint16_t idx = row * priv->config->ncols + col;
+  uint16_t byte_idx = idx / 8;
+  uint8_t bit_idx = idx % 8;
+
+  return (priv->state[byte_idx] >> bit_idx) & 1;
+}
+
+/****************************************************************************
+ * Name: kmatrix_set_state
+ *
+ * Description:
+ *   Set the current state of a key at position (row, col)
+ *
+ ****************************************************************************/
+
+static void kmatrix_set_state(FAR struct kmatrix_dev_s *priv,
+                               uint8_t row, uint8_t col, bool pressed)
+{
+  uint16_t idx = row * priv->config->ncols + col;
+  uint16_t byte_idx = idx / 8;
+  uint8_t bit_idx = idx % 8;
+
+  if (pressed)
+    {
+      priv->state[byte_idx] |= (1 << bit_idx);
+    }
+  else
+    {
+      priv->state[byte_idx] &= ~(1 << bit_idx);
+    }
+}
+
+/****************************************************************************
+ * Name: kmatrix_inc_debounce
+ *
+ * Description:
+ *   Increment debounce counter for a key
+ *
+ ****************************************************************************/
+
+static void kmatrix_inc_debounce(FAR struct kmatrix_dev_s *priv,
+                                 uint8_t row, uint8_t col)
+{
+  uint16_t idx = row * priv->config->ncols + col;
+
+  if (priv->debounce[idx] < CONFIG_INPUT_KMATRIX_DEBOUNCE)
+    {
+      priv->debounce[idx]++;
+    }
+}
+
+/****************************************************************************
+ * Name: kmatrix_reset_debounce
+ *
+ * Description:
+ *   Reset debounce counter for a key
+ *
+ ****************************************************************************/
+
+static void kmatrix_reset_debounce(FAR struct kmatrix_dev_s *priv,
+                                   uint8_t row, uint8_t col)
+{
+  uint16_t idx = row * priv->config->ncols + col;
+
+  priv->debounce[idx] = 0;
+}
+
+/****************************************************************************
+ * Name: kmatrix_scan_worker
+ *
+ * Description:
+ *   Periodic worker that scans the keyboard matrix and detects key presses
+ *   and releases.
+ *
+ ****************************************************************************/
+
+static void kmatrix_scan_worker(FAR void *arg)
+{
+  FAR struct kmatrix_dev_s *priv = (FAR struct kmatrix_dev_s *)arg;
+  uint8_t row;
+  uint8_t col;
+  bool pressed;
+  bool old_state;
+  uint32_t keycode;
+  int ret;
+
+  ret = nxmutex_lock(&priv->lock);
+  if (ret < 0)
+    {
+      return;
+    }
+
+  /* Scan each row */
+
+  for (row = 0; row < priv->config->nrows; row++)
+    {
+      /* Activate this row */
+
+      priv->config->row_set(priv->config->rows[row], true);
+
+      /* Read each column */
+
+      for (col = 0; col < priv->config->ncols; col++)
+        {
+          pressed = priv->config->col_get(priv->config->cols[col]);
+          old_state = kmatrix_get_state(priv, row, col);
+
+          /* Check if state changed */
+
+          if (pressed != old_state)
+            {
+              kmatrix_inc_debounce(priv, row, col);
+
+              /* After debounce threshold is reached, update state */
+
+              if (priv->debounce[row * priv->config->ncols + col] >=
+                  CONFIG_INPUT_KMATRIX_DEBOUNCE)
+                {
+                  kmatrix_set_state(priv, row, col, pressed);
+                  kmatrix_reset_debounce(priv, row, col);
+
+                  /* Generate keyboard event */
+
+                  keycode = priv->config->keymap[
+                    row * priv->config->ncols + col];
+                  keyboard_event(&priv->lower, (uint16_t)keycode,
+                                 pressed ? KEYBOARD_PRESS :
+                                           KEYBOARD_RELEASE);
+
+                  iinfo("Key [%d,%d]: %s (code %lu)\n", row, col,
+                        pressed ? "PRESS" : "RELEASE",
+                        (unsigned long)keycode);
+                }
+            }
+          else
+            {
+              kmatrix_reset_debounce(priv, row, col);
+            }
+        }
+
+      /* Deactivate this row */
+
+      priv->config->row_set(priv->config->rows[row], false);
+    }
+
+  nxmutex_unlock(&priv->lock);
+
+  /* Reschedule the worker */
+
+  work_queue(LPWORK, &priv->work, kmatrix_scan_worker, priv,
+             MSEC2TICK(priv->poll_interval));
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: kmatrix_register
+ *
+ * Description:
+ *   Configure and register a keyboard matrix device.
+ *
+ ****************************************************************************/
+
+int kmatrix_register(FAR const struct kmatrix_config_s *config,
+                     FAR const char *devpath)
+{
+  FAR struct kmatrix_dev_s *priv;
+  int ret;
+  uint16_t state_size;
+  uint16_t debounce_size;
+  uint16_t keys;
+
+  iinfo("Registering keypad matrix: %dx%d at %s\n", config->nrows,
+        config->ncols, devpath);
+
+  /* Validate configuration */
+
+  DEBUGASSERT(config != NULL);
+  DEBUGASSERT(devpath != NULL);
+  DEBUGASSERT(config->rows != NULL);
+  DEBUGASSERT(config->cols != NULL);
+  DEBUGASSERT(config->keymap != NULL);
+  DEBUGASSERT(config->config_row != NULL);
+  DEBUGASSERT(config->config_col != NULL);
+  DEBUGASSERT(config->row_set != NULL);
+  DEBUGASSERT(config->col_get != NULL);
+
+  /* Allocate driver instance */
+
+  priv = kmm_zalloc(sizeof(struct kmatrix_dev_s));
+  if (!priv)
+    {
+      ierr("ERROR: kmm_zalloc(%zu) failed\n", sizeof(struct kmatrix_dev_s));
+      return -ENOMEM;
+    }
+
+  /* Calculate bitmap sizes */
+
+  keys = config->nrows * config->ncols;
+  state_size = (keys + 7) / 8;
+  debounce_size = keys;
+
+  /* Allocate state and debounce bitmaps */
+
+  priv->state = kmm_zalloc(state_size);
+  if (!priv->state)
+    {
+      ierr("ERROR: Failed to allocate state bitmap\n");
+      kmm_free(priv);
+      return -ENOMEM;
+    }
+
+  priv->debounce = kmm_zalloc(debounce_size);
+  if (!priv->debounce)
+    {
+      ierr("ERROR: Failed to allocate debounce bitmap\n");
+      kmm_free(priv->state);
+      kmm_free(priv);
+      return -ENOMEM;
+    }
+
+  /* Initialize device structure */
+
+  priv->config         = config;
+  priv->poll_interval  = config->poll_interval_ms > 0 ?
+                         config->poll_interval_ms :
+                         CONFIG_INPUT_KMATRIX_POLL_MS;
+
+  nxmutex_init(&priv->lock);
+
+  /* Configure all GPIO pins */
+
+  for (int i = 0; i < config->nrows; i++)
+    {
+      config->config_row(config->rows[i]);
+    }
+
+  for (int i = 0; i < config->ncols; i++)
+    {
+      config->config_col(config->cols[i]);
+    }
+
+  /* Register as keyboard device */
+
+  ret = keyboard_register(&priv->lower, devpath,
+                          CONFIG_INPUT_KMATRIX_BUFSIZE);
+  if (ret < 0)
+    {
+      ierr("ERROR: keyboard_register() failed: %d\n", ret);
+      goto errout_with_priv;
+    }
+
+  /* Start the scanning worker */
+
+  work_queue(LPWORK, &priv->work, kmatrix_scan_worker, priv,
+             MSEC2TICK(priv->poll_interval));
+
+  iinfo("Keypad matrix registered as %s\n", devpath);
+  return OK;
+
+errout_with_priv:
+  nxmutex_destroy(&priv->lock);
+  kmm_free(priv->debounce);
+  kmm_free(priv->state);
+  kmm_free(priv);
+  return ret;
+}
diff --git a/drivers/input/kmatrix_i2c.c b/drivers/input/kmatrix_i2c.c
new file mode 100644
index 00000000000..1b366f9c8bd
--- /dev/null
+++ b/drivers/input/kmatrix_i2c.c
@@ -0,0 +1,262 @@
+/****************************************************************************
+ * drivers/input/kmatrix_i2c.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 <debug.h>
+#include <errno.h>
+
+#include <nuttx/input/kmatrix.h>
+#include <nuttx/ioexpander/ioexpander.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Verify IO expander support is enabled */
+
+#if !defined(CONFIG_IOEXPANDER_PCA9538) && !defined(CONFIG_IOEXPANDER_MCP23X08)
+#  error "Either CONFIG_IOEXPANDER_PCA9538 or " \
+         "CONFIG_IOEXPANDER_MCP23X08 must be enabled"
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+typedef uint32_t kmatrix_pin_t;
+
+struct kmatrix_i2c_dev_s
+{
+  FAR struct ioexpander_dev_s *ioe;   /* IO expander device */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void km_i2c_config_row(kmatrix_pin_t pin);
+static void km_i2c_config_col(kmatrix_pin_t pin);
+static void km_i2c_row_set(kmatrix_pin_t pin, bool active);
+static bool km_i2c_col_get(kmatrix_pin_t pin);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Global I2C device instance (simplified - one per board) */
+
+static struct kmatrix_i2c_dev_s g_km_i2c_dev;
+
+/****************************************************************************
+ * I2C Keyboard Matrix Callbacks
+ ****************************************************************************/
+
+/**
+ * Name: km_i2c_config_row
+ *
+ * Description:
+ *   Configure row pins as outputs using IO expander API.
+ */
+
+static void km_i2c_config_row(kmatrix_pin_t pin)
+{
+  int ret;
+
+  iinfo("I2C: Configuring pin %lu as output (row)\n", (unsigned long)pin);
+
+  ret = IOEXP_SETDIRECTION(g_km_i2c_dev.ioe, (uint8_t)pin,
+                           IOEXPANDER_DIRECTION_OUT);
+  if (ret < 0)
+    {
+      ierr("ERROR: Failed to configure row pin %lu: %d\n",
+           (unsigned long)pin, ret);
+    }
+}
+
+/**
+ * Name: km_i2c_config_col
+ *
+ * Description:
+ *   Configure column pins as inputs with pull-up using IO expander API.
+ */
+
+static void km_i2c_config_col(kmatrix_pin_t pin)
+{
+  int ret;
+
+  iinfo("I2C: Configuring pin %lu as input (column)\n", (unsigned long)pin);
+
+  ret = IOEXP_SETDIRECTION(g_km_i2c_dev.ioe, (uint8_t)pin,
+                           IOEXPANDER_DIRECTION_IN_PULLUP);
+  if (ret < 0)
+    {
+      /* PCA9538 does not support IN_PULLUP; fall back to plain input. */
+
+      iinfo("I2C: IN_PULLUP not supported for pin %lu, falling back to IN\n",
+            (unsigned long)pin);
+
+      ret = IOEXP_SETDIRECTION(g_km_i2c_dev.ioe, (uint8_t)pin,
+                               IOEXPANDER_DIRECTION_IN);
+      if (ret < 0)
+        {
+          ierr("ERROR: Failed to configure col pin %lu: %d\n",
+               (unsigned long)pin, ret);
+        }
+    }
+}
+
+/**
+ * Name: km_i2c_row_set
+ *
+ * Description:
+ *   Control row output (active-low for matrix with diodes).
+ */
+
+static void km_i2c_row_set(kmatrix_pin_t pin, bool active)
+{
+  int ret;
+
+  /* For active-low: active=true means write LOW (false) */
+
+  ret = IOEXP_WRITEPIN(g_km_i2c_dev.ioe, (uint8_t)pin, !active);
+  if (ret < 0)
+    {
+      ierr("ERROR: Failed to set row pin %lu: %d\n",
+           (unsigned long)pin, ret);
+    }
+
+  iinfo("I2C: Row set pin %lu to %d\n", (unsigned long)pin, active ? 0 : 1);
+}
+
+/**
+ * Name: km_i2c_col_get
+ *
+ * Description:
+ *   Read column input (active-low with pull-up).
+ */
+
+static bool km_i2c_col_get(kmatrix_pin_t pin)
+{
+  bool value;
+  int ret;
+
+  ret = IOEXP_READPIN(g_km_i2c_dev.ioe, (uint8_t)pin, &value);
+  if (ret < 0)
+    {
+      ierr("ERROR: Failed to read col pin %lu: %d\n",
+           (unsigned long)pin, ret);
+      return false;
+    }
+
+  /* Return inverted: true = active (low), false = inactive (high) */
+
+  bool result = !value;
+
+  iinfo("I2C: Col get pin %lu = %d\n", (unsigned long)pin, result);
+
+  return result;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/**
+ * Name: kmatrix_i2c_get_callbacks
+ *
+ * Description:
+ *   Get the I2C callback functions to use in keyboard matrix config.
+ *   This is called by board adapters to populate the callbacks.
+ *
+ * Returned Value:
+ *   Structure with the callback function pointers.
+ */
+
+static struct kmatrix_callbacks_s g_km_i2c_callbacks =
+{
+  .config_row = km_i2c_config_row,
+  .config_col = km_i2c_config_col,
+  .row_set    = km_i2c_row_set,
+  .col_get    = km_i2c_col_get,
+};
+
+FAR struct kmatrix_callbacks_s *kmatrix_i2c_get_callbacks(void)
+{
+  return &g_km_i2c_callbacks;
+}
+
+/**
+ * Name: kmatrix_i2c_register
+ *
+ * Description:
+ *   Register keyboard matrix driver using I2C GPIO expander.
+ *   The IO expander device must already be initialized.
+ *
+ * Input Parameters:
+ *   ioe_dev  - IO expander device (from mcp23x08_initialize or
+ *              pca9538_initialize)
+ *   config   - Keyboard matrix configuration (with callbacks set)
+ *   devpath  - Device path (e.g., "/dev/kbd0")
+ *
+ * Returned Value:
+ *   Zero on success; negated errno on failure.
+ */
+
+int kmatrix_i2c_register(FAR struct ioexpander_dev_s *ioe_dev,
+                         FAR const struct kmatrix_config_s *config,
+                         FAR const char *devpath)
+{
+  int ret;
+
+  if (ioe_dev == NULL)
+    {
+      ierr("ERROR: IO expander device is NULL\n");
+      return -EINVAL;
+    }
+
+  iinfo("Initializing keyboard matrix via I2C IO expander\n");
+
+  /* Store IO expander device in global for callbacks */
+
+  g_km_i2c_dev.ioe = ioe_dev;
+
+  /* Register the keyboard matrix driver with provided config
+   * (which must have callbacks already set by the board adapter)
+   */
+
+  ret = kmatrix_register(config, devpath);
+  if (ret < 0)
+    {
+      ierr("ERROR: kmatrix_register failed: %d\n", ret);
+      return ret;
+    }
+
+  iinfo("Keyboard matrix I2C driver registered successfully\n");
+  return OK;
+}
diff --git a/include/nuttx/input/kmatrix.h b/include/nuttx/input/kmatrix.h
new file mode 100644
index 00000000000..cec15acfddd
--- /dev/null
+++ b/include/nuttx/input/kmatrix.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+ * include/nuttx/input/kmatrix.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 __INCLUDE_NUTTX_INPUT_KMATRIX_H
+#define __INCLUDE_NUTTX_INPUT_KMATRIX_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/input/keyboard.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+typedef uint32_t kmatrix_pin_t;
+
+/* Keyboard matrix configuration structure passed to kmatrix_register() */
+
+struct kmatrix_config_s
+{
+  uint8_t nrows;                          /* Number of rows */
+  uint8_t ncols;                          /* Number of columns */
+  FAR const kmatrix_pin_t *rows;          /* Array of row GPIO pins */
+  FAR const kmatrix_pin_t *cols;          /* Array of column GPIO pins */
+
+  /* Keymap: keycode[row * cols + col] */
+
+  FAR const uint32_t *keymap;
+  uint16_t poll_interval_ms;              /* Polling interval in milliseconds 
*/
+
+  /* GPIO callback functions specific to the SoC/board */
+
+  void (*config_row)(kmatrix_pin_t pin);
+  void (*config_col)(kmatrix_pin_t pin);
+  void (*row_set)(kmatrix_pin_t pin, bool active);
+  bool (*col_get)(kmatrix_pin_t pin);
+};
+
+#ifdef CONFIG_INPUT_KMATRIX_I2C
+
+/* Keyboard matrix callback structure for I2C expanders */
+
+struct kmatrix_callbacks_s
+{
+  void (*config_row)(kmatrix_pin_t pin);
+  void (*config_col)(kmatrix_pin_t pin);
+  void (*row_set)(kmatrix_pin_t pin, bool active);
+  bool (*col_get)(kmatrix_pin_t pin);
+};
+
+#endif /* CONFIG_INPUT_KMATRIX_I2C */
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/****************************************************************************
+ * Name: kmatrix_register
+ *
+ * Description:
+ *   Configure and register a keyboard matrix device.  This will create the
+ *   /dev/keypadN device node and enable keyboard scanning.
+ *
+ * Input Parameters:
+ *   config - The keyboard matrix configuration.  This structure is not
+ *            copied; it must persist for the lifetime of the driver.
+ *   devpath - The device path for the /dev/keypadN device.
+ *
+ * Returned Value:
+ *   Zero is returned on success.  Otherwise, a negated errno value is
+ *   returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int kmatrix_register(FAR const struct kmatrix_config_s *config,
+                     FAR const char *devpath);
+
+#ifdef CONFIG_INPUT_KMATRIX_I2C
+
+/* Forward declaration */
+
+struct ioexpander_dev_s;
+
+/****************************************************************************
+ * Name: kmatrix_i2c_register
+ *
+ * Description:
+ *   Register keyboard matrix driver using I2C GPIO expander.
+ *   The IO expander device must already be initialized using
+ *   mcp23x08_initialize() or pca9538_initialize().
+ *
+ * Input Parameters:
+ *   ioe_dev  - IO expander device (from mcp23x08_initialize or
+ *              pca9538_initialize)
+ *   config   - The keyboard matrix configuration (with callbacks set)
+ *   devpath  - The device path for the /dev/keypadN device
+ *
+ * Returned Value:
+ *   Zero is returned on success.  Otherwise, a negated errno value is
+ *   returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int kmatrix_i2c_register(FAR struct ioexpander_dev_s *ioe_dev,
+                         FAR const struct kmatrix_config_s *config,
+                         FAR const char *devpath);
+
+/****************************************************************************
+ * Name: kmatrix_i2c_get_callbacks
+ *
+ * Description:
+ *   Get the I2C callback functions to use in keyboard matrix config.
+ *   This is called by board adapters to populate the callbacks.
+ *
+ * Returned Value:
+ *   Structure with the callback function pointers.
+ *
+ ****************************************************************************/
+
+FAR struct kmatrix_callbacks_s *kmatrix_i2c_get_callbacks(void);
+
+#endif /* CONFIG_INPUT_KMATRIX_I2C */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_NUTTX_INPUT_KMATRIX_H */

Reply via email to