xref: /linux-6.15/drivers/input/sparse-keymap.c (revision cdcc09a4)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
236203c4fSDmitry Torokhov /*
336203c4fSDmitry Torokhov  * Generic support for sparse keymaps
436203c4fSDmitry Torokhov  *
536203c4fSDmitry Torokhov  * Copyright (c) 2009 Dmitry Torokhov
636203c4fSDmitry Torokhov  *
736203c4fSDmitry Torokhov  * Derived from wistron button driver:
836203c4fSDmitry Torokhov  * Copyright (C) 2005 Miloslav Trmac <[email protected]>
936203c4fSDmitry Torokhov  * Copyright (C) 2005 Bernhard Rosenkraenzer <[email protected]>
1036203c4fSDmitry Torokhov  * Copyright (C) 2005 Dmitry Torokhov <[email protected]>
1136203c4fSDmitry Torokhov  */
1236203c4fSDmitry Torokhov 
1336203c4fSDmitry Torokhov #include <linux/input.h>
1436203c4fSDmitry Torokhov #include <linux/input/sparse-keymap.h>
15d2d8442dSPaul Gortmaker #include <linux/module.h>
165a0e3ad6STejun Heo #include <linux/slab.h>
1736203c4fSDmitry Torokhov 
1836203c4fSDmitry Torokhov MODULE_AUTHOR("Dmitry Torokhov <[email protected]>");
1936203c4fSDmitry Torokhov MODULE_DESCRIPTION("Generic support for sparse keymaps");
2036203c4fSDmitry Torokhov MODULE_LICENSE("GPL v2");
2136203c4fSDmitry Torokhov 
sparse_keymap_get_key_index(struct input_dev * dev,const struct key_entry * k)2267127f30SDmitry Torokhov static unsigned int sparse_keymap_get_key_index(struct input_dev *dev,
2367127f30SDmitry Torokhov 						const struct key_entry *k)
2467127f30SDmitry Torokhov {
2567127f30SDmitry Torokhov 	struct key_entry *key;
2667127f30SDmitry Torokhov 	unsigned int idx = 0;
2767127f30SDmitry Torokhov 
2867127f30SDmitry Torokhov 	for (key = dev->keycode; key->type != KE_END; key++) {
2967127f30SDmitry Torokhov 		if (key->type == KE_KEY) {
3067127f30SDmitry Torokhov 			if (key == k)
3167127f30SDmitry Torokhov 				break;
3267127f30SDmitry Torokhov 			idx++;
3367127f30SDmitry Torokhov 		}
3467127f30SDmitry Torokhov 	}
3567127f30SDmitry Torokhov 
3667127f30SDmitry Torokhov 	return idx;
3767127f30SDmitry Torokhov }
3867127f30SDmitry Torokhov 
sparse_keymap_entry_by_index(struct input_dev * dev,unsigned int index)3967127f30SDmitry Torokhov static struct key_entry *sparse_keymap_entry_by_index(struct input_dev *dev,
4067127f30SDmitry Torokhov 						      unsigned int index)
4167127f30SDmitry Torokhov {
4267127f30SDmitry Torokhov 	struct key_entry *key;
4367127f30SDmitry Torokhov 	unsigned int key_cnt = 0;
4467127f30SDmitry Torokhov 
4567127f30SDmitry Torokhov 	for (key = dev->keycode; key->type != KE_END; key++)
4667127f30SDmitry Torokhov 		if (key->type == KE_KEY)
4767127f30SDmitry Torokhov 			if (key_cnt++ == index)
4867127f30SDmitry Torokhov 				return key;
4967127f30SDmitry Torokhov 
5067127f30SDmitry Torokhov 	return NULL;
5167127f30SDmitry Torokhov }
5267127f30SDmitry Torokhov 
5336203c4fSDmitry Torokhov /**
5436203c4fSDmitry Torokhov  * sparse_keymap_entry_from_scancode - perform sparse keymap lookup
5536203c4fSDmitry Torokhov  * @dev: Input device using sparse keymap
5636203c4fSDmitry Torokhov  * @code: Scan code
5736203c4fSDmitry Torokhov  *
5836203c4fSDmitry Torokhov  * This function is used to perform &struct key_entry lookup in an
5936203c4fSDmitry Torokhov  * input device using sparse keymap.
6036203c4fSDmitry Torokhov  */
sparse_keymap_entry_from_scancode(struct input_dev * dev,unsigned int code)6136203c4fSDmitry Torokhov struct key_entry *sparse_keymap_entry_from_scancode(struct input_dev *dev,
6236203c4fSDmitry Torokhov 						    unsigned int code)
6336203c4fSDmitry Torokhov {
6436203c4fSDmitry Torokhov 	struct key_entry *key;
6536203c4fSDmitry Torokhov 
6636203c4fSDmitry Torokhov 	for (key = dev->keycode; key->type != KE_END; key++)
6736203c4fSDmitry Torokhov 		if (code == key->code)
6836203c4fSDmitry Torokhov 			return key;
6936203c4fSDmitry Torokhov 
7036203c4fSDmitry Torokhov 	return NULL;
7136203c4fSDmitry Torokhov }
7236203c4fSDmitry Torokhov EXPORT_SYMBOL(sparse_keymap_entry_from_scancode);
7336203c4fSDmitry Torokhov 
7436203c4fSDmitry Torokhov /**
7536203c4fSDmitry Torokhov  * sparse_keymap_entry_from_keycode - perform sparse keymap lookup
7636203c4fSDmitry Torokhov  * @dev: Input device using sparse keymap
7736203c4fSDmitry Torokhov  * @keycode: Key code
7836203c4fSDmitry Torokhov  *
7936203c4fSDmitry Torokhov  * This function is used to perform &struct key_entry lookup in an
8036203c4fSDmitry Torokhov  * input device using sparse keymap.
8136203c4fSDmitry Torokhov  */
sparse_keymap_entry_from_keycode(struct input_dev * dev,unsigned int keycode)8236203c4fSDmitry Torokhov struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev,
8336203c4fSDmitry Torokhov 						   unsigned int keycode)
8436203c4fSDmitry Torokhov {
8536203c4fSDmitry Torokhov 	struct key_entry *key;
8636203c4fSDmitry Torokhov 
8736203c4fSDmitry Torokhov 	for (key = dev->keycode; key->type != KE_END; key++)
8836203c4fSDmitry Torokhov 		if (key->type == KE_KEY && keycode == key->keycode)
8936203c4fSDmitry Torokhov 			return key;
9036203c4fSDmitry Torokhov 
9136203c4fSDmitry Torokhov 	return NULL;
9236203c4fSDmitry Torokhov }
9336203c4fSDmitry Torokhov EXPORT_SYMBOL(sparse_keymap_entry_from_keycode);
9436203c4fSDmitry Torokhov 
sparse_keymap_locate(struct input_dev * dev,const struct input_keymap_entry * ke)9567127f30SDmitry Torokhov static struct key_entry *sparse_keymap_locate(struct input_dev *dev,
9667127f30SDmitry Torokhov 					const struct input_keymap_entry *ke)
9767127f30SDmitry Torokhov {
9867127f30SDmitry Torokhov 	struct key_entry *key;
9967127f30SDmitry Torokhov 	unsigned int scancode;
10067127f30SDmitry Torokhov 
10167127f30SDmitry Torokhov 	if (ke->flags & INPUT_KEYMAP_BY_INDEX)
10267127f30SDmitry Torokhov 		key = sparse_keymap_entry_by_index(dev, ke->index);
10367127f30SDmitry Torokhov 	else if (input_scancode_to_scalar(ke, &scancode) == 0)
10467127f30SDmitry Torokhov 		key = sparse_keymap_entry_from_scancode(dev, scancode);
10567127f30SDmitry Torokhov 	else
10667127f30SDmitry Torokhov 		key = NULL;
10767127f30SDmitry Torokhov 
10867127f30SDmitry Torokhov 	return key;
10967127f30SDmitry Torokhov }
11067127f30SDmitry Torokhov 
sparse_keymap_getkeycode(struct input_dev * dev,struct input_keymap_entry * ke)11136203c4fSDmitry Torokhov static int sparse_keymap_getkeycode(struct input_dev *dev,
11267127f30SDmitry Torokhov 				    struct input_keymap_entry *ke)
11336203c4fSDmitry Torokhov {
1142e2e3b96SDmitry Torokhov 	const struct key_entry *key;
11536203c4fSDmitry Torokhov 
1162e2e3b96SDmitry Torokhov 	if (dev->keycode) {
11767127f30SDmitry Torokhov 		key = sparse_keymap_locate(dev, ke);
11836203c4fSDmitry Torokhov 		if (key && key->type == KE_KEY) {
11967127f30SDmitry Torokhov 			ke->keycode = key->keycode;
12067127f30SDmitry Torokhov 			if (!(ke->flags & INPUT_KEYMAP_BY_INDEX))
12167127f30SDmitry Torokhov 				ke->index =
12267127f30SDmitry Torokhov 					sparse_keymap_get_key_index(dev, key);
12367127f30SDmitry Torokhov 			ke->len = sizeof(key->code);
12467127f30SDmitry Torokhov 			memcpy(ke->scancode, &key->code, sizeof(key->code));
12536203c4fSDmitry Torokhov 			return 0;
12636203c4fSDmitry Torokhov 		}
1272e2e3b96SDmitry Torokhov 	}
12836203c4fSDmitry Torokhov 
12936203c4fSDmitry Torokhov 	return -EINVAL;
13036203c4fSDmitry Torokhov }
13136203c4fSDmitry Torokhov 
sparse_keymap_setkeycode(struct input_dev * dev,const struct input_keymap_entry * ke,unsigned int * old_keycode)13236203c4fSDmitry Torokhov static int sparse_keymap_setkeycode(struct input_dev *dev,
13367127f30SDmitry Torokhov 				    const struct input_keymap_entry *ke,
13467127f30SDmitry Torokhov 				    unsigned int *old_keycode)
13536203c4fSDmitry Torokhov {
13636203c4fSDmitry Torokhov 	struct key_entry *key;
13736203c4fSDmitry Torokhov 
1382e2e3b96SDmitry Torokhov 	if (dev->keycode) {
13967127f30SDmitry Torokhov 		key = sparse_keymap_locate(dev, ke);
14036203c4fSDmitry Torokhov 		if (key && key->type == KE_KEY) {
14167127f30SDmitry Torokhov 			*old_keycode = key->keycode;
14267127f30SDmitry Torokhov 			key->keycode = ke->keycode;
14367127f30SDmitry Torokhov 			set_bit(ke->keycode, dev->keybit);
14467127f30SDmitry Torokhov 			if (!sparse_keymap_entry_from_keycode(dev, *old_keycode))
14567127f30SDmitry Torokhov 				clear_bit(*old_keycode, dev->keybit);
14636203c4fSDmitry Torokhov 			return 0;
14736203c4fSDmitry Torokhov 		}
1482e2e3b96SDmitry Torokhov 	}
14936203c4fSDmitry Torokhov 
15036203c4fSDmitry Torokhov 	return -EINVAL;
15136203c4fSDmitry Torokhov }
15236203c4fSDmitry Torokhov 
15336203c4fSDmitry Torokhov /**
15436203c4fSDmitry Torokhov  * sparse_keymap_setup - set up sparse keymap for an input device
15536203c4fSDmitry Torokhov  * @dev: Input device
15636203c4fSDmitry Torokhov  * @keymap: Keymap in form of array of &key_entry structures ending
15736203c4fSDmitry Torokhov  *	with %KE_END type entry
15836203c4fSDmitry Torokhov  * @setup: Function that can be used to adjust keymap entries
159fabeb165SMichał Kępień  *	depending on device's needs, may be %NULL
16036203c4fSDmitry Torokhov  *
16136203c4fSDmitry Torokhov  * The function calculates size and allocates copy of the original
16236203c4fSDmitry Torokhov  * keymap after which sets up input device event bits appropriately.
163fabeb165SMichał Kępień  * The allocated copy of the keymap is automatically freed when it
164fabeb165SMichał Kępień  * is no longer needed.
16536203c4fSDmitry Torokhov  */
sparse_keymap_setup(struct input_dev * dev,const struct key_entry * keymap,int (* setup)(struct input_dev *,struct key_entry *))16636203c4fSDmitry Torokhov int sparse_keymap_setup(struct input_dev *dev,
16736203c4fSDmitry Torokhov 			const struct key_entry *keymap,
16836203c4fSDmitry Torokhov 			int (*setup)(struct input_dev *, struct key_entry *))
16936203c4fSDmitry Torokhov {
17036203c4fSDmitry Torokhov 	size_t map_size = 1; /* to account for the last KE_END entry */
17136203c4fSDmitry Torokhov 	const struct key_entry *e;
17236203c4fSDmitry Torokhov 	struct key_entry *map, *entry;
17336203c4fSDmitry Torokhov 	int i;
17436203c4fSDmitry Torokhov 	int error;
17536203c4fSDmitry Torokhov 
17636203c4fSDmitry Torokhov 	for (e = keymap; e->type != KE_END; e++)
17736203c4fSDmitry Torokhov 		map_size++;
17836203c4fSDmitry Torokhov 
179*cdcc09a4SRaag Jadav 	map = devm_kmemdup_array(&dev->dev, keymap, map_size, sizeof(*keymap), GFP_KERNEL);
18036203c4fSDmitry Torokhov 	if (!map)
18136203c4fSDmitry Torokhov 		return -ENOMEM;
18236203c4fSDmitry Torokhov 
18336203c4fSDmitry Torokhov 	for (i = 0; i < map_size; i++) {
18436203c4fSDmitry Torokhov 		entry = &map[i];
18536203c4fSDmitry Torokhov 
18636203c4fSDmitry Torokhov 		if (setup) {
18736203c4fSDmitry Torokhov 			error = setup(dev, entry);
18836203c4fSDmitry Torokhov 			if (error)
189fabeb165SMichał Kępień 				return error;
19036203c4fSDmitry Torokhov 		}
19136203c4fSDmitry Torokhov 
19236203c4fSDmitry Torokhov 		switch (entry->type) {
19336203c4fSDmitry Torokhov 		case KE_KEY:
19436203c4fSDmitry Torokhov 			__set_bit(EV_KEY, dev->evbit);
19536203c4fSDmitry Torokhov 			__set_bit(entry->keycode, dev->keybit);
19636203c4fSDmitry Torokhov 			break;
19736203c4fSDmitry Torokhov 
19836203c4fSDmitry Torokhov 		case KE_SW:
199cb1b1459SDmitry Torokhov 		case KE_VSW:
20036203c4fSDmitry Torokhov 			__set_bit(EV_SW, dev->evbit);
20136203c4fSDmitry Torokhov 			__set_bit(entry->sw.code, dev->swbit);
20236203c4fSDmitry Torokhov 			break;
20336203c4fSDmitry Torokhov 		}
20436203c4fSDmitry Torokhov 	}
20536203c4fSDmitry Torokhov 
206f3cf5c4fSSeth Forshee 	if (test_bit(EV_KEY, dev->evbit)) {
207170531baSSeth Forshee 		__set_bit(KEY_UNKNOWN, dev->keybit);
208f3cf5c4fSSeth Forshee 		__set_bit(EV_MSC, dev->evbit);
209f3cf5c4fSSeth Forshee 		__set_bit(MSC_SCAN, dev->mscbit);
210f3cf5c4fSSeth Forshee 	}
211f3cf5c4fSSeth Forshee 
21236203c4fSDmitry Torokhov 	dev->keycode = map;
21336203c4fSDmitry Torokhov 	dev->keycodemax = map_size;
214aebd636bSDmitry Torokhov 	dev->getkeycode = sparse_keymap_getkeycode;
215aebd636bSDmitry Torokhov 	dev->setkeycode = sparse_keymap_setkeycode;
21636203c4fSDmitry Torokhov 
21736203c4fSDmitry Torokhov 	return 0;
21836203c4fSDmitry Torokhov }
21936203c4fSDmitry Torokhov EXPORT_SYMBOL(sparse_keymap_setup);
22036203c4fSDmitry Torokhov 
22136203c4fSDmitry Torokhov /**
22236203c4fSDmitry Torokhov  * sparse_keymap_report_entry - report event corresponding to given key entry
22336203c4fSDmitry Torokhov  * @dev: Input device for which event should be reported
22436203c4fSDmitry Torokhov  * @ke: key entry describing event
22536203c4fSDmitry Torokhov  * @value: Value that should be reported (ignored by %KE_SW entries)
22636203c4fSDmitry Torokhov  * @autorelease: Signals whether release event should be emitted for %KE_KEY
22736203c4fSDmitry Torokhov  *	entries right after reporting press event, ignored by all other
22836203c4fSDmitry Torokhov  *	entries
22936203c4fSDmitry Torokhov  *
23036203c4fSDmitry Torokhov  * This function is used to report input event described by given
23136203c4fSDmitry Torokhov  * &struct key_entry.
23236203c4fSDmitry Torokhov  */
sparse_keymap_report_entry(struct input_dev * dev,const struct key_entry * ke,unsigned int value,bool autorelease)23336203c4fSDmitry Torokhov void sparse_keymap_report_entry(struct input_dev *dev, const struct key_entry *ke,
23436203c4fSDmitry Torokhov 				unsigned int value, bool autorelease)
23536203c4fSDmitry Torokhov {
23636203c4fSDmitry Torokhov 	switch (ke->type) {
23736203c4fSDmitry Torokhov 	case KE_KEY:
238f3cf5c4fSSeth Forshee 		input_event(dev, EV_MSC, MSC_SCAN, ke->code);
23936203c4fSDmitry Torokhov 		input_report_key(dev, ke->keycode, value);
24036203c4fSDmitry Torokhov 		input_sync(dev);
24136203c4fSDmitry Torokhov 		if (value && autorelease) {
24236203c4fSDmitry Torokhov 			input_report_key(dev, ke->keycode, 0);
24336203c4fSDmitry Torokhov 			input_sync(dev);
24436203c4fSDmitry Torokhov 		}
24536203c4fSDmitry Torokhov 		break;
24636203c4fSDmitry Torokhov 
24736203c4fSDmitry Torokhov 	case KE_SW:
24836203c4fSDmitry Torokhov 		value = ke->sw.value;
2496f49c4f5SGustavo A. R. Silva 		fallthrough;
25036203c4fSDmitry Torokhov 
25136203c4fSDmitry Torokhov 	case KE_VSW:
25236203c4fSDmitry Torokhov 		input_report_switch(dev, ke->sw.code, value);
2536f29c244SStefan Brüns 		input_sync(dev);
25436203c4fSDmitry Torokhov 		break;
25536203c4fSDmitry Torokhov 	}
25636203c4fSDmitry Torokhov }
25736203c4fSDmitry Torokhov EXPORT_SYMBOL(sparse_keymap_report_entry);
25836203c4fSDmitry Torokhov 
25936203c4fSDmitry Torokhov /**
26036203c4fSDmitry Torokhov  * sparse_keymap_report_event - report event corresponding to given scancode
26136203c4fSDmitry Torokhov  * @dev: Input device using sparse keymap
26236203c4fSDmitry Torokhov  * @code: Scan code
26336203c4fSDmitry Torokhov  * @value: Value that should be reported (ignored by %KE_SW entries)
26436203c4fSDmitry Torokhov  * @autorelease: Signals whether release event should be emitted for %KE_KEY
26536203c4fSDmitry Torokhov  *	entries right after reporting press event, ignored by all other
26636203c4fSDmitry Torokhov  *	entries
26736203c4fSDmitry Torokhov  *
26836203c4fSDmitry Torokhov  * This function is used to perform lookup in an input device using sparse
26936203c4fSDmitry Torokhov  * keymap and report corresponding event. Returns %true if lookup was
27036203c4fSDmitry Torokhov  * successful and %false otherwise.
27136203c4fSDmitry Torokhov  */
sparse_keymap_report_event(struct input_dev * dev,unsigned int code,unsigned int value,bool autorelease)27236203c4fSDmitry Torokhov bool sparse_keymap_report_event(struct input_dev *dev, unsigned int code,
27336203c4fSDmitry Torokhov 				unsigned int value, bool autorelease)
27436203c4fSDmitry Torokhov {
27536203c4fSDmitry Torokhov 	const struct key_entry *ke =
27636203c4fSDmitry Torokhov 		sparse_keymap_entry_from_scancode(dev, code);
277170531baSSeth Forshee 	struct key_entry unknown_ke;
27836203c4fSDmitry Torokhov 
27936203c4fSDmitry Torokhov 	if (ke) {
28036203c4fSDmitry Torokhov 		sparse_keymap_report_entry(dev, ke, value, autorelease);
28136203c4fSDmitry Torokhov 		return true;
28236203c4fSDmitry Torokhov 	}
28336203c4fSDmitry Torokhov 
284170531baSSeth Forshee 	/* Report an unknown key event as a debugging aid */
285170531baSSeth Forshee 	unknown_ke.type = KE_KEY;
286170531baSSeth Forshee 	unknown_ke.code = code;
287170531baSSeth Forshee 	unknown_ke.keycode = KEY_UNKNOWN;
288170531baSSeth Forshee 	sparse_keymap_report_entry(dev, &unknown_ke, value, true);
289170531baSSeth Forshee 
29036203c4fSDmitry Torokhov 	return false;
29136203c4fSDmitry Torokhov }
29236203c4fSDmitry Torokhov EXPORT_SYMBOL(sparse_keymap_report_event);
29336203c4fSDmitry Torokhov 
294