/* * 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> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. */ #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"); MODULE_VERSION("0.1"); 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 deeds, may be %NULL * * The function calculates size and allocates copy of the original * keymap after which sets up input device event bits appropriately. * Before destroying input device allocated keymap should be freed * with a call to sparse_keymap_free(). */ 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 = kcalloc(map_size, sizeof(struct key_entry), GFP_KERNEL); if (!map) return -ENOMEM; memcpy(map, keymap, map_size * sizeof(struct key_entry)); for (i = 0; i < map_size; i++) { entry = &map[i]; if (setup) { error = setup(dev, entry); if (error) goto err_out; } 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; err_out: kfree(map); return error; } EXPORT_SYMBOL(sparse_keymap_setup); /** * sparse_keymap_free - free memory allocated for sparse keymap * @dev: Input device using sparse keymap * * This function is used to free memory allocated by sparse keymap * in an input device that was set up by sparse_keymap_setup(). * NOTE: It is safe to cal this function while input device is * still registered (however the drivers should care not to try to * use freed keymap and thus have to shut off interrupts/polling * before freeing the keymap). */ void sparse_keymap_free(struct input_dev *dev) { unsigned long flags; /* * Take event lock to prevent racing with input_get_keycode() * and input_set_keycode() if we are called while input device * is still registered. */ spin_lock_irqsave(&dev->event_lock, flags); kfree(dev->keycode); dev->keycode = NULL; dev->keycodemax = 0; spin_unlock_irqrestore(&dev->event_lock, flags); } EXPORT_SYMBOL(sparse_keymap_free); /** * 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; /* fall through */ case KE_VSW: input_report_switch(dev, ke->sw.code, value); 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);