@@ -25,12 +25,10 @@
#include <linux/poll.h>
#include "rc-core-priv.h"
-/* Sizes are in bytes, 256 bytes allows for 32 entries on x64 */
-#define IR_TAB_MIN_SIZE 256
-#define IR_TAB_MAX_SIZE 8192
+#define RC_TAB_MAX_SIZE 1024 /* entries */
-/* FIXME: IR_KEYPRESS_TIMEOUT should be protocol specific */
-#define IR_KEYPRESS_TIMEOUT 250
+/* FIXME: RC_KEYPRESS_TIMEOUT should be protocol specific */
+#define RC_KEYPRESS_TIMEOUT 250
/* Used to keep track of known keymaps */
static LIST_HEAD(rc_map_list);
@@ -99,237 +97,189 @@ void rc_map_unregister(struct rc_map_list *map)
EXPORT_SYMBOL_GPL(rc_map_unregister);
/**
- * ir_create_table() - initializes a scancode table
- * @rc_map: the rc_map to initialize
- * @name: name to assign to the table
- * @size: initial size of the table
- * @return: zero on success or a negative error code
- *
- * This routine will initialize the rc_map and will allocate
- * memory to hold at least the specified number of elements.
+ * rc_scan_size() - determine the necessary size for a rc_scan struct
+ * @len: the number of keytable entries the struct should hold
+ * @return: the size of the struct in bytes
*/
-static int ir_create_table(struct rc_map *rc_map,
- const char *name, size_t size)
+static inline size_t rc_scan_size(unsigned len)
{
- rc_map->name = name;
- rc_map->alloc = roundup_pow_of_two(size * sizeof(struct rc_map_table));
- rc_map->size = rc_map->alloc / sizeof(struct rc_map_table);
- rc_map->scan = kmalloc(rc_map->alloc, GFP_KERNEL);
- if (!rc_map->scan)
- return -ENOMEM;
-
- IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n",
- rc_map->size, rc_map->alloc);
- return 0;
+ return sizeof(struct rc_scan) + len * sizeof(struct rc_map_table);
}
/**
- * ir_free_table() - frees memory allocated by a scancode table
- * @rc_map: the table whose mappings need to be freed
+ * rc_keytable_update_entry() - update an existing entry in the keytable
+ * @kt: the keytable to update
+ * @i: the index of the entry to update
+ * @entry: the new values for the entry
+ * @return: the old rc_scan struct, NULL if memory could not be allocated
*
- * This routine will free memory alloctaed for key mappings used by given
- * scancode table.
+ * Updates a keytable by replacing an existing entry at the given index.
+ * The old rc_scan struct is returned so that it can be freed at a
+ * later stage.
*/
-void ir_free_table(struct rc_map *rc_map)
+static struct rc_scan *rc_keytable_update_entry(struct rc_keytable *kt,
+ unsigned i,
+ struct rc_map_table *entry)
{
- rc_map->size = 0;
- kfree(rc_map->scan);
- rc_map->scan = NULL;
-}
+ struct rc_scan *old_scan = kt->scan;
+ struct rc_scan *new_scan = kt->scan;
+ u32 old_keycode;
-/**
- * ir_resize_table() - resizes a scancode table if necessary
- * @rc_map: the rc_map to resize
- * @gfp_flags: gfp flags to use when allocating memory
- * @return: zero on success or a negative error code
- *
- * This routine will shrink the rc_map if it has lots of
- * unused entries and grow it if it is full.
- */
-static int ir_resize_table(struct rc_map *rc_map, gfp_t gfp_flags)
-{
- unsigned int oldalloc = rc_map->alloc;
- unsigned int newalloc = oldalloc;
- struct rc_map_table *oldscan = rc_map->scan;
- struct rc_map_table *newscan;
-
- if (rc_map->size == rc_map->len) {
- /* All entries in use -> grow keytable */
- if (rc_map->alloc >= IR_TAB_MAX_SIZE)
- return -ENOMEM;
-
- newalloc *= 2;
- IR_dprintk(1, "Growing table to %u bytes\n", newalloc);
- }
+ if (i >= old_scan->len)
+ return NULL;
- if ((rc_map->len * 3 < rc_map->size) && (oldalloc > IR_TAB_MIN_SIZE)) {
- /* Less than 1/3 of entries in use -> shrink keytable */
- newalloc /= 2;
- IR_dprintk(1, "Shrinking table to %u bytes\n", newalloc);
- }
+ new_scan = kmalloc(rc_scan_size(old_scan->len), GFP_KERNEL);
+ if (!new_scan)
+ return NULL;
+ memcpy(new_scan, old_scan, rc_scan_size(old_scan->len));
- if (newalloc == oldalloc)
- return 0;
+ IR_dprintk(1, "#%d: New keycode 0x%04x\n", i, entry->keycode);
+ new_scan->table[i].keycode = entry->keycode;
- newscan = kmalloc(newalloc, gfp_flags);
- if (!newscan) {
- IR_dprintk(1, "Failed to kmalloc %u bytes\n", newalloc);
- return -ENOMEM;
- }
+ /* Another scancode might use the old keycode... */
+ __set_bit(entry->keycode, kt->idev->keybit);
+ old_keycode = old_scan->table[i].keycode;
+ for (i = 0; i < new_scan->len; i++)
+ if (new_scan->table[i].keycode == old_keycode)
+ break;
- memcpy(newscan, rc_map->scan, rc_map->len * sizeof(struct rc_map_table));
- rc_map->scan = newscan;
- rc_map->alloc = newalloc;
- rc_map->size = rc_map->alloc / sizeof(struct rc_map_table);
- kfree(oldscan);
- return 0;
+ if (i >= new_scan->len)
+ /* ...nope */
+ __clear_bit(old_keycode, kt->idev->keybit);
+
+ rcu_assign_pointer(kt->scan, new_scan);
+ return old_scan;
}
/**
- * ir_update_mapping() - set a keycode in the scancode->keycode table
- * @kt: the struct rc_keytable
- * @rc_map: scancode table to be adjusted
- * @index: index of the mapping that needs to be updated
- * @keycode: the desired keycode
- * @return: previous keycode assigned to the mapping
+ * rc_keytable_remove_entry() - remove an existing entry in the keytable
+ * @kt: the keytable to update
+ * @i: the index of the entry to remove
+ * @return: the old rc_scan struct, NULL if memory could not be allocated
*
- * This routine is used to update scancode->keycode mapping at given
- * position.
+ * Updates a keytable by removing an existing entry at the given index.
+ * The old rc_scan struct is returned so that it can be freed at a
+ * later stage.
*/
-static unsigned int ir_update_mapping(struct rc_keytable *kt,
- struct rc_map *rc_map,
- unsigned int index,
- unsigned int new_keycode)
+static struct rc_scan *rc_keytable_remove_entry(struct rc_keytable *kt,
+ unsigned i)
{
- int old_keycode = rc_map->scan[index].keycode;
- int i;
-
- /* Did the user wish to remove the mapping? */
- if (new_keycode == KEY_RESERVED || new_keycode == KEY_UNKNOWN) {
- IR_dprintk(1, "#%d: Deleting proto 0x%04x, scan 0x%08llx\n",
- index, rc_map->scan[index].protocol,
- (unsigned long long)rc_map->scan[index].scancode);
- rc_map->len--;
- memmove(&rc_map->scan[index], &rc_map->scan[index+ 1],
- (rc_map->len - index) * sizeof(struct rc_map_table));
- } else {
- IR_dprintk(1, "#%d: %s proto 0x%04x, scan 0x%08llx "
- "with key 0x%04x\n",
- index,
- old_keycode == KEY_RESERVED ? "New" : "Replacing",
- rc_map->scan[index].protocol,
- (unsigned long long)rc_map->scan[index].scancode,
- new_keycode);
- rc_map->scan[index].keycode = new_keycode;
- __set_bit(new_keycode, kt->idev->keybit);
- }
+ struct rc_scan *old_scan = kt->scan;
+ struct rc_scan *new_scan = kt->scan;
+ u32 old_keycode;
- if (old_keycode != KEY_RESERVED) {
- /* A previous mapping was updated... */
- __clear_bit(old_keycode, kt->idev->keybit);
- /* ... but another scancode might use the same keycode */
- for (i = 0; i < rc_map->len; i++) {
- if (rc_map->scan[i].keycode == old_keycode) {
- __set_bit(old_keycode, kt->idev->keybit);
- break;
- }
- }
+ if (i >= old_scan->len)
+ return NULL;
- /* Possibly shrink the keytable, failure is not a problem */
- ir_resize_table(rc_map, GFP_ATOMIC);
- }
+ new_scan = kmalloc(rc_scan_size(old_scan->len - 1), GFP_ATOMIC);
+ if (!new_scan)
+ return NULL;
+ new_scan->len = old_scan->len - 1;
+ memcpy(&new_scan->table[0], &old_scan->table[0],
+ i * sizeof(struct rc_map_table));
+ memcpy(&new_scan->table[i], &old_scan->table[i + 1],
+ (new_scan->len - i) * sizeof(struct rc_map_table));
+ IR_dprintk(1, "#%d: Deleted\n", i);
+
+ /* Another scancode might use the removed keycode... */
+ old_keycode = old_scan->table[i].keycode;
+ for (i = 0; i < new_scan->len; i++)
+ if (new_scan->table[i].keycode == old_keycode)
+ break;
+
+ if (i >= new_scan->len)
+ /* ...nope */
+ __clear_bit(old_keycode, kt->idev->keybit);
- return old_keycode;
+ rcu_assign_pointer(kt->scan, new_scan);
+ return old_scan;
}
/**
- * ir_establish_scancode() - set a keycode in the scancode->keycode table
- * @kt: the struct rc_keytable descriptor
- * @rc_map: scancode table to be searched
- * @entry: the entry to be added to the table
- * @resize: controls whether we are allowed to resize the table to
- * accomodate not yet present scancodes
- * @return: index of the mapping containing scancode in question
- * or -1U in case of failure.
+ * rc_keytable_add_entry() - add an existing entry in the keytable
+ * @kt: the keytable to update
+ * @entry: the new entry to insert
+ * @init: whether the keytable is being initialized for the first time
+ * @return: the old rc_scan struct, NULL if memory could not be allocated
*
- * This routine is used to locate given scancode in rc_map.
- * If scancode is not yet present the routine will allocate a new slot
- * for it.
+ * Updates a keytable by inserting an entry at the proper index. Unless @init is
+ * %true, the old rc_scan struct is returned so that it can be freed at a
+ * later stage.
*/
-static unsigned int ir_establish_scancode(struct rc_keytable *kt,
- struct rc_map *rc_map,
- struct rc_map_table *entry,
- bool resize)
+static struct rc_scan *rc_keytable_add_entry(struct rc_keytable *kt,
+ struct rc_map_table *entry,
+ bool init)
{
- unsigned int i;
+ struct rc_scan *old_scan = kt->scan;
+ struct rc_scan *new_scan = kt->scan;
+ unsigned i;
- /*
- * Unfortunately, some hardware-based IR decoders don't provide
- * all bits for the complete IR code. In general, they provide only
- * the command part of the IR code. Yet, as it is possible to replace
- * the provided IR with another one, it is needed to allow loading
- * IR tables from other remotes. So, we support specifying a mask to
- * indicate the valid bits of the scancodes.
- */
- if (kt->dev->scancode_mask)
- entry->scancode &= kt->dev->scancode_mask;
+ if (old_scan->len >= RC_TAB_MAX_SIZE)
+ return NULL;
- /*
- * First check if we already have a mapping for this command.
- * Note that the keytable is sorted first on protocol and second
- * on scancode (lowest to highest).
- */
- for (i = 0; i < rc_map->len; i++) {
- if (rc_map->scan[i].protocol < entry->protocol)
+ /* Find the right index to insert the new entry at */
+ for (i = 0; i < old_scan->len; i++) {
+ if (old_scan->table[i].protocol < entry->protocol)
continue;
- if (rc_map->scan[i].protocol > entry->protocol)
+ if (old_scan->table[i].protocol > entry->protocol)
break;
- if (rc_map->scan[i].scancode < entry->scancode)
+ if (old_scan->table[i].scancode < entry->scancode)
continue;
- if (rc_map->scan[i].scancode > entry->scancode)
+ if (old_scan->table[i].scancode > entry->scancode)
break;
- return i;
+ /* BUG: We already have a matching entry */
+ return NULL;
}
- /* No previous mapping found, we might need to grow the table */
- if (rc_map->size == rc_map->len) {
- if (!resize || ir_resize_table(rc_map, GFP_ATOMIC))
- return -1U;
+ if (init) {
+ /* The init code already allocates a suitably sized table */
+ memmove(&new_scan->table[i + 1], &new_scan->table[i],
+ (new_scan->len - i) * sizeof(struct rc_map_table));
+ new_scan->len++;
+ } else {
+ new_scan = kmalloc(rc_scan_size(old_scan->len + 1), GFP_ATOMIC);
+ if (!new_scan)
+ return NULL;
+ new_scan->len = old_scan->len + 1;
+ memcpy(&new_scan->table[0], &old_scan->table[0],
+ i * sizeof(struct rc_map_table));
+ memcpy(&new_scan->table[i + 1], &old_scan->table[i],
+ (old_scan->len - i) * sizeof(struct rc_map_table));
}
- /* i is the proper index to insert our new keycode */
- if (i < rc_map->len)
- memmove(&rc_map->scan[i + 1], &rc_map->scan[i],
- (rc_map->len - i) * sizeof(struct rc_map_table));
- rc_map->scan[i].scancode = entry->scancode;
- rc_map->scan[i].protocol = entry->protocol;
- rc_map->scan[i].keycode = KEY_RESERVED;
- rc_map->len++;
+ new_scan->table[i].scancode = entry->scancode;
+ new_scan->table[i].protocol = entry->protocol;
+ new_scan->table[i].keycode = entry->keycode;
+ IR_dprintk(1, "#%d: New proto 0x%04x, scan 0x%08llx with key 0x%04x\n",
+ i, entry->protocol, (unsigned long long)entry->scancode,
+ entry->keycode);
+ __set_bit(entry->keycode, kt->idev->keybit);
- return i;
+ rcu_assign_pointer(kt->scan, new_scan);
+ return old_scan;
}
/**
* guess_protocol() - heuristics to guess the protocol for a scancode
* @rdev: the struct rc_dev device descriptor
- * @return: the guessed RC_TYPE_* protocol
+ * @scan: the struct rc_scan table to use
+ * @return: the guessed RC_TYPE_* protocol
*
* Internal routine to guess the current IR protocol for legacy ioctls.
*/
-static inline enum rc_type guess_protocol(struct rc_dev *rdev)
+static inline enum rc_type guess_protocol(struct rc_dev *rdev,
+ struct rc_scan *scan)
{
- struct rc_map *rc_map = &rdev->keytables[0]->rc_map;
-
if (hweight64(rdev->enabled_protocols) == 1)
return rc_bitmap_to_type(rdev->enabled_protocols);
else if (hweight64(rdev->allowed_protocols) == 1)
return rc_bitmap_to_type(rdev->allowed_protocols);
- else if (rc_map->len > 0)
- return rc_map->scan[0].protocol;
+ else if (scan->len > 0)
+ return scan->table[0].protocol;
else
return RC_TYPE_OTHER;
}
@@ -359,33 +309,71 @@ static u32 to_nec32(u32 orig)
}
/**
- * ir_setkeycode() - set a keycode in the scancode->keycode table
+ * rc_scancode_to_index() - locate keytable index by scancode
+ * @rc_scan: the struct rc_scan to search
+ * @protocol: protocol to look for in the table
+ * @scancode: scancode to look for in the table
+ * @return: index in the table, -1U if not found
+ *
+ * This routine performs a binary search in a keytable for a
+ * given scancode.
+ */
+static unsigned rc_scancode_to_index(struct rc_scan *scan,
+ u16 protocol, u64 scancode)
+{
+ int start = 0;
+ int end = scan->len - 1;
+ int mid;
+ struct rc_map_table *m;
+
+ while (start <= end) {
+ mid = (start + end) / 2;
+ m = &scan->table[mid];
+
+ if (m->protocol < protocol)
+ start = mid + 1;
+ else if (m->protocol > protocol)
+ end = mid - 1;
+ else if (m->scancode < scancode)
+ start = mid + 1;
+ else if (m->scancode > scancode)
+ end = mid - 1;
+ else
+ return mid;
+ }
+
+ return -1U;
+}
+
+/**
+ * rc_keytable_set() - add/update/remove an entry in the keytable
* @idev: the struct input_dev device descriptor
- * @scancode: the desired scancode
- * @keycode: result
- * @return: -EINVAL if the keycode could not be inserted, otherwise zero.
+ * @ke: the keymap entry to add/update/remove
+ * @old_keycode:used to return the previous keycode for this entry
+ * @return: zero on success or a negative error code
*
- * This routine is used to handle evdev EVIOCSKEY ioctl.
+ * This function handles the evdev EVIOCSKEYCODE(_V2) ioctls.
*/
-static int ir_setkeycode(struct input_dev *idev,
- const struct input_keymap_entry *ke,
- unsigned int *old_keycode)
+static int rc_keytable_set(struct input_dev *idev,
+ const struct input_keymap_entry *ke,
+ unsigned int *old_keycode)
{
struct rc_keytable *kt = input_get_drvdata(idev);
struct rc_dev *rdev = kt->dev;
- struct rc_map *rc_map = &kt->rc_map;
+ struct rc_scan *old_scan = NULL;
unsigned int index;
struct rc_map_table entry;
int retval = 0;
- unsigned long flags;
entry.keycode = ke->keycode;
- spin_lock_irqsave(&kt->lock, flags);
+ retval = mutex_lock_interruptible(&kt->scan_mutex);
+ if (retval)
+ return retval;
if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
index = ke->index;
- if (index >= rc_map->len) {
+ if (index >= kt->scan->len) {
retval = -EINVAL;
goto out;
}
@@ -397,15 +385,16 @@ static int ir_setkeycode(struct input_dev *idev,
goto out;
entry.scancode = scancode;
- entry.protocol = guess_protocol(rdev);
+ entry.protocol = guess_protocol(rdev, kt->scan);
if (entry.protocol == RC_TYPE_NEC)
entry.scancode = to_nec32(scancode);
- index = ir_establish_scancode(kt, rc_map, &entry, true);
- if (index >= rc_map->len) {
- retval = -ENOMEM;
- goto out;
- }
+ if (kt->dev->scancode_mask)
+ entry.scancode &= kt->dev->scancode_mask;
+
+ index = rc_scancode_to_index(kt->scan, entry.protocol,
+ entry.scancode);
+
} else if (ke->len == sizeof(struct rc_scancode)) {
/* New EVIOCSKEYCODE_V2 ioctl */
const struct rc_keymap_entry *rke = (struct rc_keymap_entry *)ke;
@@ -417,127 +406,63 @@ static int ir_setkeycode(struct input_dev *idev,
goto out;
}
- index = ir_establish_scancode(kt, rc_map, &entry, true);
- if (index >= rc_map->len) {
- retval = -ENOMEM;
- goto out;
- }
+ if (kt->dev->scancode_mask)
+ entry.scancode &= kt->dev->scancode_mask;
+
+ index = rc_scancode_to_index(kt->scan, entry.protocol,
+ entry.scancode);
+
} else {
retval = -EINVAL;
goto out;
}
- *old_keycode = ir_update_mapping(kt, rc_map, index, ke->keycode);
-
-out:
- spin_unlock_irqrestore(&kt->lock, flags);
- return retval;
-}
-
-/**
- * ir_setkeytable() - sets several entries in the scancode->keycode table
- * @kt: the struct rc_keytable descriptor
- * @to: the struct rc_map to copy entries to
- * @from: the struct rc_map to copy entries from
- * @return: -ENOMEM if all keycodes could not be inserted, otherwise zero.
- *
- * This routine is used to handle table initialization.
- */
-int rc_setkeytable(struct rc_keytable *kt, const struct rc_map *from)
-{
- struct rc_map *rc_map = &kt->rc_map;
- struct rc_map_table entry;
- unsigned int i, index;
- int rc;
-
- rc = ir_create_table(rc_map, from->name, from->size);
- if (rc)
- return rc;
-
- IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n",
- rc_map->size, rc_map->alloc);
-
- for (i = 0; i < from->size; i++) {
- if (from->rc_type == RC_TYPE_NEC)
- entry.scancode = to_nec32(from->scan[i].scancode);
+ if (index >= kt->scan->len) {
+ /* Old entry not found */
+ *old_keycode = KEY_RESERVED;
+ if (ke->keycode == KEY_RESERVED)
+ /* removing a non-existing entry eh? */
+ goto out;
+ old_scan = rc_keytable_add_entry(kt, &entry, false);
+ } else {
+ /* Previous entry found */
+ *old_keycode = kt->scan->table[index].keycode;
+ if (ke->keycode == KEY_RESERVED)
+ old_scan = rc_keytable_remove_entry(kt, index);
else
- entry.scancode = from->scan[i].scancode;
-
- entry.protocol = from->rc_type;
- index = ir_establish_scancode(kt, rc_map, &entry, false);
- if (index >= rc_map->len) {
- rc = -ENOMEM;
- break;
- }
-
- ir_update_mapping(kt, rc_map, index, from->scan[i].keycode);
+ old_scan = rc_keytable_update_entry(kt, index, &entry);
}
- if (rc)
- ir_free_table(rc_map);
-
- return rc;
-}
-
-/**
- * ir_lookup_by_scancode() - locate mapping by scancode
- * @rc_map: the struct rc_map to search
- * @protocol: protocol to look for in the table
- * @scancode: scancode to look for in the table
- * @return: index in the table, -1U if not found
- *
- * This routine performs binary search in RC keykeymap table for
- * given scancode.
- */
-static unsigned int ir_lookup_by_scancode(const struct rc_map *rc_map,
- u16 protocol, u64 scancode)
-{
- int start = 0;
- int end = rc_map->len - 1;
- int mid;
- struct rc_map_table *m;
-
- while (start <= end) {
- mid = (start + end) / 2;
- m = &rc_map->scan[mid];
-
- if (m->protocol < protocol)
- start = mid + 1;
- else if (m->protocol > protocol)
- end = mid - 1;
- else if (m->scancode < scancode)
- start = mid + 1;
- else if (m->scancode > scancode)
- end = mid - 1;
- else
- return mid;
+out:
+ mutex_unlock(&kt->scan_mutex);
+ if (old_scan) {
+ synchronize_rcu();
+ kfree(old_scan);
}
-
- return -1U;
+ return retval;
}
/**
- * ir_getkeycode() - get a keycode from the scancode->keycode table
+ * rc_keytable_get() - get an entry from the keytable
* @idev: the struct input_dev device descriptor
- * @scancode: the desired scancode
- * @keycode: used to return the keycode, if found, or KEY_RESERVED
- * @return: always returns zero.
+ * @ke: the requested entry which is filled in by this function
+ * @return: zero on success, or a negative error code
*
- * This routine is used to handle evdev EVIOCGKEY ioctl.
+ * This function handles the evdev EVIOCGKEYCODE(_V2) ioctls.
*/
-int ir_getkeycode(struct input_dev *idev,
- struct input_keymap_entry *ke)
+static int rc_keytable_get(struct input_dev *idev,
+ struct input_keymap_entry *ke)
{
struct rc_keymap_entry *rke = (struct rc_keymap_entry *)ke;
struct rc_keytable *kt = input_get_drvdata(idev);
struct rc_dev *rdev = kt->dev;
- struct rc_map *rc_map = &kt->rc_map;
+ struct rc_scan *scan;
struct rc_map_table *entry;
- unsigned long flags;
unsigned int index;
int retval;
- spin_lock_irqsave(&kt->lock, flags);
+ rcu_read_lock();
+ scan = rcu_dereference(kt->scan);
if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
index = ke->index;
@@ -550,11 +475,11 @@ int ir_getkeycode(struct input_dev *idev,
if (retval)
goto out;
- protocol = guess_protocol(rdev);
+ protocol = guess_protocol(rdev, scan);
if (protocol == RC_TYPE_NEC)
scancode = to_nec32(scancode);
- index = ir_lookup_by_scancode(rc_map, protocol, scancode);
+ index = rc_scancode_to_index(scan, protocol, scancode);
} else if (ke->len == sizeof(struct rc_scancode)) {
/* New EVIOCGKEYCODE_V2 ioctl */
@@ -563,16 +488,17 @@ int ir_getkeycode(struct input_dev *idev,
goto out;
}
- index = ir_lookup_by_scancode(rc_map,
- rke->rc.protocol, rke->rc.scancode);
+ index = rc_scancode_to_index(scan,
+ rke->rc.protocol,
+ rke->rc.scancode);
} else {
retval = -EINVAL;
goto out;
}
- if (index < rc_map->len) {
- entry = &rc_map->scan[index];
+ if (index < scan->len) {
+ entry = &scan->table[index];
ke->index = index;
ke->keycode = entry->keycode;
if (ke->len == sizeof(int)) {
@@ -600,29 +526,10 @@ int ir_getkeycode(struct input_dev *idev,
retval = 0;
out:
- spin_unlock_irqrestore(&kt->lock, flags);
+ rcu_read_unlock();
return retval;
}
-static u32 rc_get_keycode(struct rc_keytable *kt,
- enum rc_type protocol, u64 scancode)
-{
- struct rc_map *rc_map;
- unsigned int keycode = KEY_RESERVED;
- unsigned int index;
-
- rc_map = &kt->rc_map;
- if (!rc_map)
- return KEY_RESERVED;
-
- index = ir_lookup_by_scancode(rc_map, protocol, scancode);
- if (index < rc_map->len)
- keycode = rc_map->scan[index].keycode;
-
- return keycode;
-}
-
-
/**
* rc_g_keycode_from_table() - gets the keycode that corresponds to a scancode
* @dev: the struct rc_dev descriptor of the device
@@ -638,18 +545,20 @@ u32 rc_g_keycode_from_table(struct rc_dev *dev,
enum rc_type protocol, u64 scancode)
{
struct rc_keytable *kt;
- unsigned int keycode = KEY_RESERVED;
- unsigned long flags;
+ struct rc_scan *scan;
+ unsigned keycode = KEY_RESERVED;
+ unsigned index;
/* FIXME: This entire function is a hack. Remove it */
rcu_read_lock();
kt = rcu_dereference(dev->keytables[0]);
if (!kt)
goto out;
+ scan = rcu_dereference(kt->scan);
- spin_lock_irqsave(&kt->lock, flags);
- keycode = rc_get_keycode(kt, protocol, scancode);
- spin_unlock_irqrestore(&kt->lock, flags);
+ index = rc_scancode_to_index(scan, protocol, scancode);
+ if (index < scan->len)
+ keycode = scan->table[index].keycode;
out:
rcu_read_unlock();
@@ -663,11 +572,11 @@ EXPORT_SYMBOL_GPL(rc_g_keycode_from_table);
* @sync: whether or not to call input_sync
*
* This function is used internally to release a keypress, it must be
- * called with kt->lock held.
+ * called with kt->key_lock held.
*/
static void rc_do_keyup(struct rc_keytable *kt, bool sync)
{
- if (!kt->keypressed)
+ if (!kt->key_pressed)
return;
IR_dprintk(1, "keyup key 0x%04x\n", kt->last_keycode);
@@ -675,7 +584,7 @@ static void rc_do_keyup(struct rc_keytable *kt, bool sync)
led_trigger_event(led_feedback, LED_OFF);
if (sync)
input_sync(kt->idev);
- kt->keypressed = false;
+ kt->key_pressed = false;
}
/**
@@ -688,14 +597,14 @@ void rc_keytable_keyup(struct rc_keytable *kt)
{
unsigned long flags;
- spin_lock_irqsave(&kt->lock, flags);
+ spin_lock_irqsave(&kt->key_lock, flags);
rc_do_keyup(kt, true);
- spin_unlock_irqrestore(&kt->lock, flags);
+ spin_unlock_irqrestore(&kt->key_lock, flags);
}
/**
- * ir_timer_keyup() - generates a keyup event after a timeout
- * @cookie: a pointer to the struct rc_keytable
+ * rc_timer_keyup() - generates a keyup event after a timeout
+ * @cookie: a pointer to the struct rc_keytable descriptor of the keytable
*
* This routine will generate a keyup event some time after a keydown event
* is generated when no further activity has been detected.
@@ -715,37 +624,36 @@ static void rc_timer_keyup(unsigned long cookie)
* to allow the input subsystem to do its auto-repeat magic or
* a keyup event might follow immediately after the keydown.
*/
- spin_lock_irqsave(&kt->lock, flags);
+ spin_lock_irqsave(&kt->key_lock, flags);
if (time_is_before_eq_jiffies(kt->keyup_jiffies))
rc_do_keyup(kt, true);
- spin_unlock_irqrestore(&kt->lock, flags);
+ spin_unlock_irqrestore(&kt->key_lock, flags);
}
/**
* rc_keytable_repeat() - signals that a key is still pressed
* @kt: the keytable
*
- * This routine is used by IR decoders when a repeat message which does
- * not include the necessary bits to reproduce the scancode has been
- * received.
+ * This routine is used when a repeat message which does not include the
+ * necessary bits to reproduce the scancode has been received.
*/
void rc_keytable_repeat(struct rc_keytable *kt)
{
unsigned long flags;
- spin_lock_irqsave(&kt->lock, flags);
+ spin_lock_irqsave(&kt->key_lock, flags);
input_event(kt->idev, EV_MSC, MSC_SCAN, kt->last_scancode);
input_sync(kt->idev);
- if (!kt->keypressed)
+ if (!kt->key_pressed)
goto out;
- kt->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT);
+ kt->keyup_jiffies = jiffies + msecs_to_jiffies(RC_KEYPRESS_TIMEOUT);
mod_timer(&kt->timer_keyup, kt->keyup_jiffies);
out:
- spin_unlock_irqrestore(&kt->lock, flags);
+ spin_unlock_irqrestore(&kt->key_lock, flags);
}
/**
@@ -762,26 +670,33 @@ out:
void rc_keytable_keydown(struct rc_keytable *kt, enum rc_type protocol,
u32 scancode, u8 toggle, bool autoup)
{
+ struct rc_scan *scan;
+ unsigned index;
+ u32 keycode = KEY_RESERVED;
unsigned long flags;
- u32 keycode;
bool new_event;
- spin_lock_irqsave(&kt->lock, flags);
+ rcu_read_lock();
+ scan = rcu_dereference(kt->scan);
+ index = rc_scancode_to_index(scan, protocol, scancode);
+ if (index < scan->len)
+ keycode = scan->table[index].keycode;
+ rcu_read_unlock();
- keycode = rc_get_keycode(kt, protocol, scancode);
- new_event = (!kt->keypressed ||
+ spin_lock_irqsave(&kt->key_lock, flags);
+ new_event = (!kt->key_pressed ||
kt->last_protocol != protocol ||
kt->last_scancode != scancode ||
kt->last_toggle != toggle);
- if (new_event && kt->keypressed)
+ if (new_event)
rc_do_keyup(kt, false);
input_event(kt->idev, EV_MSC, MSC_SCAN, scancode);
if (new_event && keycode != KEY_RESERVED) {
/* Register a keypress */
- kt->keypressed = true;
+ kt->key_pressed = true;
kt->last_protocol = protocol;
kt->last_scancode = scancode;
kt->last_toggle = toggle;
@@ -795,11 +710,11 @@ void rc_keytable_keydown(struct rc_keytable *kt, enum rc_type protocol,
}
input_sync(kt->idev);
- if (autoup && kt->keypressed) {
- kt->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT);
+ if (autoup && kt->key_pressed) {
+ kt->keyup_jiffies = jiffies + msecs_to_jiffies(RC_KEYPRESS_TIMEOUT);
mod_timer(&kt->timer_keyup, kt->keyup_jiffies);
}
- spin_unlock_irqrestore(&kt->lock, flags);
+ spin_unlock_irqrestore(&kt->key_lock, flags);
}
static int rc_input_open(struct input_dev *idev)
@@ -817,6 +732,42 @@ static void rc_input_close(struct input_dev *idev)
}
/**
+ * rc_keytable_init() - performs initial setup of a keytable
+ * @dev: the struct rc_dev device descriptor
+ * @from: the struct rc_map to copy entries from
+ * @return: zero on success, or a negative error code
+ *
+ * This function is used to handle table initialization.
+ */
+static int rc_keytable_init(struct rc_keytable *kt,
+ const struct rc_map *from)
+{
+ unsigned size;
+ unsigned i;
+ struct rc_map_table entry;
+
+ size = from ? from->size : 0;
+ kt->scan = kmalloc(rc_scan_size(size), GFP_KERNEL);
+ if (!kt->scan)
+ return -ENOMEM;
+
+ kt->scan->len = 0;
+ for (i = 0; i < size; i++) {
+ entry.protocol = from->rc_type;
+ if (entry.protocol == RC_TYPE_NEC)
+ entry.scancode = to_nec32(from->scan[i].scancode);
+ else
+ entry.scancode = from->scan[i].scancode;
+ if (kt->dev->scancode_mask)
+ entry.scancode &= kt->dev->scancode_mask;
+ entry.keycode = from->scan[i].keycode;
+ rc_keytable_add_entry(kt, &entry, true);
+ }
+
+ return 0;
+}
+
+/**
* rc_keytable_create() - create a new keytable
* @dev: the struct rc_dev device this keytable should belong to
* @name: the userfriendly name of this keymap
@@ -848,10 +799,11 @@ struct rc_keytable *rc_keytable_create(struct rc_dev *dev, const char *name,
kt->idev = idev;
kt->dev = dev;
- spin_lock_init(&kt->lock);
+ spin_lock_init(&kt->key_lock);
+ mutex_init(&kt->scan_mutex);
snprintf(kt->name, sizeof(*kt->name), name ? name : "undefined");
- idev->getkeycode = ir_getkeycode;
- idev->setkeycode = ir_setkeycode;
+ idev->getkeycode = rc_keytable_get;
+ idev->setkeycode = rc_keytable_set;
idev->open = rc_input_open;
idev->close = rc_input_close;
set_bit(EV_KEY, idev->evbit);
@@ -861,7 +813,7 @@ struct rc_keytable *rc_keytable_create(struct rc_dev *dev, const char *name,
input_set_drvdata(idev, kt);
setup_timer(&kt->timer_keyup, rc_timer_keyup, (unsigned long)kt);
- err = rc_setkeytable(kt, rc_map);
+ err = rc_keytable_init(kt, rc_map);
if (err)
goto out;
@@ -892,8 +844,8 @@ struct rc_keytable *rc_keytable_create(struct rc_dev *dev, const char *name,
return kt;
out:
- ir_free_table(&kt->rc_map);
input_free_device(idev);
+ kfree(kt->scan);
kfree(kt);
return ERR_PTR(err);
}
@@ -910,8 +862,8 @@ void rc_keytable_destroy(struct rc_keytable *kt)
return;
del_timer_sync(&kt->timer_keyup);
- ir_free_table(&kt->rc_map);
input_unregister_device(kt->idev);
+ kfree(kt->scan);
kfree(kt);
}
@@ -386,35 +386,47 @@ struct rc_dev {
};
/**
+ * struct rc_scan - rcu-friendly scancode<->keycode table
+ * @len: number of elements in the table array
+ * @table: array of struct rc_map_table elements
+ */
+struct rc_scan {
+ unsigned len;
+ struct rc_map_table table[];
+};
+
+/**
* struct rc_keytable - represents one keytable for a rc_dev device
* @node: used to iterate over all keytables for a rc_dev device
* @dev: the rc_dev device this keytable belongs to
* @idev: the input_dev device which belongs to this keytable
* @name: the user-friendly name of this keytable
- * @rc_map: holds the scancode <-> keycode mappings
- * @keypressed: whether a key is currently pressed or not
- * @keyup_jiffies: when the key should be auto-released
- * @timer_keyup: responsible for the auto-release of keys
- * @lock: protects the key state
+ * @scan_mutex: protects @scan against concurrent writers
+ * @scan: the current scancode<->keycode table
+ * @key_lock: protects the key state
+ * @key_pressed: whether a key is currently pressed or not
* @last_keycode: keycode of the last keypress
* @last_protocol: protocol of the last keypress
* @last_scancode: scancode of the last keypress
* @last_toggle: toggle of the last keypress
+ * @timer_keyup: responsible for the auto-release of keys
+ * @keyup_jiffies: when the key should be auto-released
*/
struct rc_keytable {
struct list_head node;
struct rc_dev *dev;
struct input_dev *idev;
char name[RC_KEYTABLE_NAME_SIZE];
- struct rc_map rc_map;
- bool keypressed;
- unsigned long keyup_jiffies;
- struct timer_list timer_keyup;
- spinlock_t lock;
+ struct mutex scan_mutex;
+ struct rc_scan __rcu *scan;
+ spinlock_t key_lock;
+ bool key_pressed;
u32 last_keycode;
enum rc_type last_protocol;
u32 last_scancode;
u8 last_toggle;
+ struct timer_list timer_keyup;
+ unsigned long keyup_jiffies;
};
#define to_rc_dev(d) container_of(d, struct rc_dev, dev)
Change struct rc_keytable to be RCU-friendly by kmalloc():ing an entire new scancode,protocol <-> keycode table every time the table is changed (i.e. via EVIOCSKEYCODE(_V2)). The advantage is that the performance-critical keycode lookup path can be made entirely lock-free and that GFP_ATOMIC allocations can be avoided entirely at the cost of a couple of extra kmalloc() calls when changing a keytable (which is normally done once during boot). Signed-off-by: David Härdeman <david@hardeman.nu> --- drivers/media/rc/rc-keytable.c | 668 +++++++++++++++++++--------------------- include/media/rc-core.h | 32 +- 2 files changed, 332 insertions(+), 368 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html