summaryrefslogtreecommitdiffstats
path: root/drivers/input/sparse-keymap.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-19 10:09:22 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-19 10:09:22 -0700
commitf4cdf7ca9a1fdcca413157df19753f388a5a224e (patch)
tree60a31895b2446cd7fa27514e8c2843534a0de0da /drivers/input/sparse-keymap.c
downloadlinux-stable-f4cdf7ca9a1fdcca413157df19753f388a5a224e.tar.gz
linux-stable-f4cdf7ca9a1fdcca413157df19753f388a5a224e.zip
Merge tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-mediagrafted
Pull media updates from Mauro Carvalho Chehab: - v4l2-core: added ISP statistics support and per-block validation - v4l2-core: Allow unknown HDR10 white point and luminance - New camera sensors: Sony IMX678 and IMX471m, Himax HM1092 IR sensor - New codec: Milos: VPU v2.0 codec support - isp driver: gained support for Dreamchip RPPX1 ISP framework - vsp1 driver: gained support for RZ/T2H and RZ/N2H - Novalake driver: gained CVS support for new NVL hardware - dvb-core: fix feed leak on failed DMX_ADD_PID - several driver fixes, cleanups and minor improvements * tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (308 commits) media: ipu-bridge: check all DMI entries when overriding sensor rotation media: v4l2-async: avoid deleting unlinked ASC entry on link error media: rzg2l-cru: Align bytesperline to hardware DMA stride requirement media: intel/ipu6: fix async notifier cleanup leak on parse error media: staging/ipu7: fix async notifier UAF on probe error path media: amd: isp4: fix self-deadlock in isp4sd_pwron_and_init() error path media: amd: isp4: release partial allocations in isp4if_alloc_fw_gpumem() media: rcar-isp: Fix VSPX reference leaks media: rcar-isp: Release ISPCORE resources media: i2c: imx415: Release runtime PM reference on VBLANK error media: i2c: imx415: Return test pattern write errors media: renesas: vsp1: Declare index variables in for loop statement media: renesas: vsp1: Make reset control optional to support platforms without a reset line media: dt-bindings: media: renesas,vsp1: Document RZ/T2H and RZ/N2H SoCs media: dt-bindings: media: renesas,fcp: Document RZ/T2H and RZ/N2H SoCs media: nxp: imx8-isi: Add additional 32-bit RGB format support media: nxp: imx8-isi: Add 16-bit raw Bayer format support media: nxp: imx8-isi: Implement per-stream reference counting for multiplexed streams media: nxp: imx8-isi: Use BIT_ULL() for 64-bit stream masks media: nxp: imx8-isi: Correct color map between V4L2 and ISI ...
Diffstat (limited to 'drivers/input/sparse-keymap.c')
-rw-r--r--drivers/input/sparse-keymap.c294
1 files changed, 294 insertions, 0 deletions
diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c
new file mode 100644
index 000000000..164f8fcfd
--- /dev/null
+++ b/drivers/input/sparse-keymap.c
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Generic support for sparse keymaps
+ *
+ * Copyright (c) 2009 Dmitry Torokhov
+ *
+ * Derived from wistron button driver:
+ * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
+ * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
+ * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
+ */
+
+#include <linux/export.h>
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
+MODULE_DESCRIPTION("Generic support for sparse keymaps");
+MODULE_LICENSE("GPL v2");
+
+static unsigned int sparse_keymap_get_key_index(struct input_dev *dev,
+ const struct key_entry *k)
+{
+ struct key_entry *key;
+ unsigned int idx = 0;
+
+ for (key = dev->keycode; key->type != KE_END; key++) {
+ if (key->type == KE_KEY) {
+ if (key == k)
+ break;
+ idx++;
+ }
+ }
+
+ return idx;
+}
+
+static struct key_entry *sparse_keymap_entry_by_index(struct input_dev *dev,
+ unsigned int index)
+{
+ struct key_entry *key;
+ unsigned int key_cnt = 0;
+
+ for (key = dev->keycode; key->type != KE_END; key++)
+ if (key->type == KE_KEY)
+ if (key_cnt++ == index)
+ return key;
+
+ return NULL;
+}
+
+/**
+ * sparse_keymap_entry_from_scancode - perform sparse keymap lookup
+ * @dev: Input device using sparse keymap
+ * @code: Scan code
+ *
+ * This function is used to perform &struct key_entry lookup in an
+ * input device using sparse keymap.
+ */
+struct key_entry *sparse_keymap_entry_from_scancode(struct input_dev *dev,
+ unsigned int code)
+{
+ struct key_entry *key;
+
+ for (key = dev->keycode; key->type != KE_END; key++)
+ if (code == key->code)
+ return key;
+
+ return NULL;
+}
+EXPORT_SYMBOL(sparse_keymap_entry_from_scancode);
+
+/**
+ * sparse_keymap_entry_from_keycode - perform sparse keymap lookup
+ * @dev: Input device using sparse keymap
+ * @keycode: Key code
+ *
+ * This function is used to perform &struct key_entry lookup in an
+ * input device using sparse keymap.
+ */
+struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev,
+ unsigned int keycode)
+{
+ struct key_entry *key;
+
+ for (key = dev->keycode; key->type != KE_END; key++)
+ if (key->type == KE_KEY && keycode == key->keycode)
+ return key;
+
+ return NULL;
+}
+EXPORT_SYMBOL(sparse_keymap_entry_from_keycode);
+
+static struct key_entry *sparse_keymap_locate(struct input_dev *dev,
+ const struct input_keymap_entry *ke)
+{
+ struct key_entry *key;
+ unsigned int scancode;
+
+ if (ke->flags & INPUT_KEYMAP_BY_INDEX)
+ key = sparse_keymap_entry_by_index(dev, ke->index);
+ else if (input_scancode_to_scalar(ke, &scancode) == 0)
+ key = sparse_keymap_entry_from_scancode(dev, scancode);
+ else
+ key = NULL;
+
+ return key;
+}
+
+static int sparse_keymap_getkeycode(struct input_dev *dev,
+ struct input_keymap_entry *ke)
+{
+ const struct key_entry *key;
+
+ if (dev->keycode) {
+ key = sparse_keymap_locate(dev, ke);
+ if (key && key->type == KE_KEY) {
+ ke->keycode = key->keycode;
+ if (!(ke->flags & INPUT_KEYMAP_BY_INDEX))
+ ke->index =
+ sparse_keymap_get_key_index(dev, key);
+ ke->len = sizeof(key->code);
+ memcpy(ke->scancode, &key->code, sizeof(key->code));
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sparse_keymap_setkeycode(struct input_dev *dev,
+ const struct input_keymap_entry *ke,
+ unsigned int *old_keycode)
+{
+ struct key_entry *key;
+
+ if (dev->keycode) {
+ key = sparse_keymap_locate(dev, ke);
+ if (key && key->type == KE_KEY) {
+ *old_keycode = key->keycode;
+ key->keycode = ke->keycode;
+ set_bit(ke->keycode, dev->keybit);
+ if (!sparse_keymap_entry_from_keycode(dev, *old_keycode))
+ clear_bit(*old_keycode, dev->keybit);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * sparse_keymap_setup - set up sparse keymap for an input device
+ * @dev: Input device
+ * @keymap: Keymap in form of array of &key_entry structures ending
+ * with %KE_END type entry
+ * @setup: Function that can be used to adjust keymap entries
+ * depending on device's needs, may be %NULL
+ *
+ * The function calculates size and allocates copy of the original
+ * keymap after which sets up input device event bits appropriately.
+ * The allocated copy of the keymap is automatically freed when it
+ * is no longer needed.
+ */
+int sparse_keymap_setup(struct input_dev *dev,
+ const struct key_entry *keymap,
+ int (*setup)(struct input_dev *, struct key_entry *))
+{
+ size_t map_size = 1; /* to account for the last KE_END entry */
+ const struct key_entry *e;
+ struct key_entry *map, *entry;
+ int i;
+ int error;
+
+ for (e = keymap; e->type != KE_END; e++)
+ map_size++;
+
+ map = devm_kmemdup_array(&dev->dev, keymap, map_size, sizeof(*keymap), GFP_KERNEL);
+ if (!map)
+ return -ENOMEM;
+
+ for (i = 0; i < map_size; i++) {
+ entry = &map[i];
+
+ if (setup) {
+ error = setup(dev, entry);
+ if (error)
+ return error;
+ }
+
+ switch (entry->type) {
+ case KE_KEY:
+ __set_bit(EV_KEY, dev->evbit);
+ __set_bit(entry->keycode, dev->keybit);
+ break;
+
+ case KE_SW:
+ case KE_VSW:
+ __set_bit(EV_SW, dev->evbit);
+ __set_bit(entry->sw.code, dev->swbit);
+ break;
+ }
+ }
+
+ if (test_bit(EV_KEY, dev->evbit)) {
+ __set_bit(KEY_UNKNOWN, dev->keybit);
+ __set_bit(EV_MSC, dev->evbit);
+ __set_bit(MSC_SCAN, dev->mscbit);
+ }
+
+ dev->keycode = map;
+ dev->keycodemax = map_size;
+ dev->getkeycode = sparse_keymap_getkeycode;
+ dev->setkeycode = sparse_keymap_setkeycode;
+
+ return 0;
+}
+EXPORT_SYMBOL(sparse_keymap_setup);
+
+/**
+ * sparse_keymap_report_entry - report event corresponding to given key entry
+ * @dev: Input device for which event should be reported
+ * @ke: key entry describing event
+ * @value: Value that should be reported (ignored by %KE_SW entries)
+ * @autorelease: Signals whether release event should be emitted for %KE_KEY
+ * entries right after reporting press event, ignored by all other
+ * entries
+ *
+ * This function is used to report input event described by given
+ * &struct key_entry.
+ */
+void sparse_keymap_report_entry(struct input_dev *dev, const struct key_entry *ke,
+ unsigned int value, bool autorelease)
+{
+ switch (ke->type) {
+ case KE_KEY:
+ input_event(dev, EV_MSC, MSC_SCAN, ke->code);
+ input_report_key(dev, ke->keycode, value);
+ input_sync(dev);
+ if (value && autorelease) {
+ input_report_key(dev, ke->keycode, 0);
+ input_sync(dev);
+ }
+ break;
+
+ case KE_SW:
+ value = ke->sw.value;
+ fallthrough;
+
+ case KE_VSW:
+ input_report_switch(dev, ke->sw.code, value);
+ input_sync(dev);
+ break;
+ }
+}
+EXPORT_SYMBOL(sparse_keymap_report_entry);
+
+/**
+ * sparse_keymap_report_event - report event corresponding to given scancode
+ * @dev: Input device using sparse keymap
+ * @code: Scan code
+ * @value: Value that should be reported (ignored by %KE_SW entries)
+ * @autorelease: Signals whether release event should be emitted for %KE_KEY
+ * entries right after reporting press event, ignored by all other
+ * entries
+ *
+ * This function is used to perform lookup in an input device using sparse
+ * keymap and report corresponding event. Returns %true if lookup was
+ * successful and %false otherwise.
+ */
+bool sparse_keymap_report_event(struct input_dev *dev, unsigned int code,
+ unsigned int value, bool autorelease)
+{
+ const struct key_entry *ke =
+ sparse_keymap_entry_from_scancode(dev, code);
+ struct key_entry unknown_ke;
+
+ if (ke) {
+ sparse_keymap_report_entry(dev, ke, value, autorelease);
+ return true;
+ }
+
+ /* Report an unknown key event as a debugging aid */
+ unknown_ke.type = KE_KEY;
+ unknown_ke.code = code;
+ unknown_ke.keycode = KEY_UNKNOWN;
+ sparse_keymap_report_entry(dev, &unknown_ke, value, true);
+
+ return false;
+}
+EXPORT_SYMBOL(sparse_keymap_report_event);
+