Switch the code to use new style of getkeycode and setkeycode methods to allow retrieving and setting keycodes not only by their scancodes but also by index. Signed-off-by: Dmitry Torokhov <dtor@xxxxxxx> --- drivers/input/misc/winbond-cir.c | 248 +++++++++++++++++++++++++------------- 1 files changed, 163 insertions(+), 85 deletions(-) diff --git a/drivers/input/misc/winbond-cir.c b/drivers/input/misc/winbond-cir.c index 64f1de7..6a69067 100644 --- a/drivers/input/misc/winbond-cir.c +++ b/drivers/input/misc/winbond-cir.c @@ -172,7 +172,6 @@ enum wbcir_protocol { #define WBCIR_MAX_IDLE_BYTES 10 static DEFINE_SPINLOCK(wbcir_lock); -static DEFINE_RWLOCK(keytable_lock); struct wbcir_key { u32 scancode; @@ -184,7 +183,7 @@ struct wbcir_keyentry { struct list_head list; }; -static struct wbcir_key rc6_def_keymap[] = { +static const struct wbcir_key rc6_def_keymap[] = { { 0x800F0400, KEY_NUMERIC_0 }, { 0x800F0401, KEY_NUMERIC_1 }, { 0x800F0402, KEY_NUMERIC_2 }, @@ -365,88 +364,152 @@ wbcir_to_rc6cells(u8 val) * *****************************************************************************/ -static unsigned int -wbcir_do_getkeycode(struct wbcir_data *data, u32 scancode) +static struct wbcir_keyentry * +wbcir_keyentry_by_scancode(struct wbcir_data *data, u32 scancode) { struct wbcir_keyentry *keyentry; - unsigned int keycode = KEY_RESERVED; - unsigned long flags; - read_lock_irqsave(&keytable_lock, flags); + list_for_each_entry(keyentry, &data->keytable, list) + if (keyentry->key.scancode == scancode) + return keyentry; + + return NULL; +} + +static struct wbcir_keyentry * +wbcir_keyentry_by_index(struct wbcir_data *data, unsigned int index) +{ + struct wbcir_keyentry *keyentry; + unsigned int cur_idx = 0; + + list_for_each_entry(keyentry, &data->keytable, list) + if (cur_idx++ == index) + return keyentry; + + return NULL; +} + +static struct wbcir_keyentry * +wbcir_lookup_keyentry(struct wbcir_data *data, + const struct input_keymap_entry *ke) +{ + struct wbcir_keyentry *keyentry; + unsigned int scancode; + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) + keyentry = wbcir_keyentry_by_index(data, ke->index); + else if (input_scancode_to_scalar(ke, &scancode) == 0) + keyentry = wbcir_keyentry_by_scancode(data, scancode); + else + keyentry = NULL; + + return keyentry; + +} + +static unsigned int +wbcir_keyentry_get_index(struct wbcir_data *data, + const struct wbcir_keyentry *keyentry) +{ + struct wbcir_keyentry *k; + int idx = 0; - list_for_each_entry(keyentry, &data->keytable, list) { - if (keyentry->key.scancode == scancode) { - keycode = keyentry->key.keycode; + list_for_each_entry(k, &data->keytable, list) { + if (k == keyentry) break; - } + idx++; } - read_unlock_irqrestore(&keytable_lock, flags); - return keycode; + return idx; } static int -wbcir_getkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) +wbcir_getkeycode(struct input_dev *dev, struct input_keymap_entry *ke) { struct wbcir_data *data = input_get_drvdata(dev); + const struct wbcir_keyentry *keyentry; + + keyentry = wbcir_lookup_keyentry(data, ke); + if (keyentry) { + ke->keycode = keyentry->key.keycode; + if (!(ke->flags & INPUT_KEYMAP_BY_INDEX)) + ke->index = wbcir_keyentry_get_index(data, keyentry); + ke->len = sizeof(keyentry->key.scancode); + memcpy(ke->scancode, &keyentry->key.scancode, + sizeof(keyentry->key.scancode)); + + return 0; + } - *keycode = wbcir_do_getkeycode(data, scancode); - return 0; + return -EINVAL; } static int wbcir_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) + const struct input_keymap_entry *ke, + unsigned int *old_keycode) { struct wbcir_data *data = input_get_drvdata(dev); struct wbcir_keyentry *keyentry; - struct wbcir_keyentry *new_keyentry; - unsigned long flags; - unsigned int old_keycode = KEY_RESERVED; - - new_keyentry = kmalloc(sizeof(*new_keyentry), GFP_KERNEL); - if (!new_keyentry) - return -ENOMEM; + unsigned int scancode; - write_lock_irqsave(&keytable_lock, flags); + *old_keycode = KEY_RESERVED; - list_for_each_entry(keyentry, &data->keytable, list) { - if (keyentry->key.scancode != scancode) - continue; + if (input_scancode_to_scalar(ke, &scancode)) + return -EINVAL; - old_keycode = keyentry->key.keycode; - keyentry->key.keycode = keycode; + keyentry = wbcir_lookup_keyentry(data, ke); + if (keyentry) { + *old_keycode = keyentry->key.keycode; + clear_bit(*old_keycode, dev->keybit); + } else { + if (ke->flags & INPUT_KEYMAP_BY_INDEX) + return -EINVAL; - if (keyentry->key.keycode == KEY_RESERVED) { - list_del(&keyentry->list); - kfree(keyentry); - } + keyentry = kmalloc(sizeof(*keyentry), GFP_ATOMIC); + if (!keyentry) + return -ENOMEM; - break; + list_add_tail(&keyentry->list, &data->keytable); } - set_bit(keycode, dev->keybit); + keyentry->key.keycode = ke->keycode; + keyentry->key.scancode = scancode; - if (old_keycode == KEY_RESERVED) { - new_keyentry->key.scancode = scancode; - new_keyentry->key.keycode = keycode; - list_add(&new_keyentry->list, &data->keytable); + if (keyentry->key.keycode == KEY_RESERVED) { + list_del(&keyentry->list); + kfree(keyentry); } else { - kfree(new_keyentry); - clear_bit(old_keycode, dev->keybit); + set_bit(ke->keycode, dev->keybit); list_for_each_entry(keyentry, &data->keytable, list) { - if (keyentry->key.keycode == old_keycode) { - set_bit(old_keycode, dev->keybit); + if (keyentry->key.keycode == *old_keycode) { + set_bit(*old_keycode, dev->keybit); break; } } } - write_unlock_irqrestore(&keytable_lock, flags); return 0; } +static unsigned int +wbcir_fetch_keycode(struct wbcir_data *data, u32 scancode) +{ + struct input_dev *input = data->input_dev; + const struct wbcir_keyentry *keyentry; + unsigned int keycode; + unsigned long flags; + + /* Take event lock to prevent race with setkeycode */ + spin_lock_irqsave(&input->event_lock, flags); + + keyentry = wbcir_keyentry_by_scancode(data, scancode); + keycode = keyentry ? keyentry->key.keycode : KEY_RESERVED; + + spin_unlock_irqrestore(&input->event_lock, flags); + return keycode; +} + /* * Timer function to report keyup event some time after keydown is * reported by the ISR. @@ -503,7 +566,7 @@ wbcir_keydown(struct wbcir_data *data, u32 scancode, u8 toggle) input_event(data->input_dev, EV_MSC, MSC_SCAN, (int)scancode); /* Do we know this scancode? */ - keycode = wbcir_do_getkeycode(data, scancode); + keycode = wbcir_fetch_keycode(data, scancode); if (keycode == KEY_RESERVED) goto set_timer; @@ -1247,7 +1310,7 @@ wbcir_init_hw(struct wbcir_data *data) /* * Clear IR LED, set SP3 clock to 24Mhz - * set SP3_IRRX_SW to binary 01, helpfully not documented + ;* set SP3_IRRX_SW to binary 01, helpfully not documented */ outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS); @@ -1339,6 +1402,41 @@ wbcir_resume(struct pnp_dev *device) return 0; } +static void +wbcir_free_keymap(struct wbcir_data *data) +{ + struct wbcir_keyentry *key, *next; + + list_for_each_entry_safe(key, next, &data->keytable, list) { + list_del(&key->list); + kfree(key); + } +} + +static int +wbcir_load_keymap(struct wbcir_data *data, + const struct wbcir_key *keymap, unsigned int keymap_size) +{ + struct wbcir_keyentry *keyentry; + int i; + + for (i = 0; i < keymap_size; i++) { + if (keymap[i].keycode != KEY_RESERVED) { + keyentry = kmalloc(sizeof(*keyentry), GFP_KERNEL); + if (!keyentry) { + wbcir_free_keymap(data); + return -ENOMEM; + } + + keyentry->key.keycode = keymap[i].keycode; + keyentry->key.scancode = keymap[i].scancode; + list_add_tail(&keyentry->list, &data->keytable); + } + } + + return 0; +} + static int __devinit wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) { @@ -1359,6 +1457,10 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) goto exit; } + data->last_scancode = INVALID_SCANCODE; + INIT_LIST_HEAD(&data->keytable); + setup_timer(&data->timer_keyup, wbcir_keyup, (unsigned long)data); + pnp_set_drvdata(device, data); data->ebase = pnp_port_start(device, 0); @@ -1439,50 +1541,31 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) data->input_dev->id.vendor = PCI_VENDOR_ID_WINBOND; data->input_dev->id.product = WBCIR_ID_FAMILY; data->input_dev->id.version = WBCIR_ID_CHIP; - data->input_dev->getkeycode = wbcir_getkeycode; - data->input_dev->setkeycode = wbcir_setkeycode; + data->input_dev->getkeycode_new = wbcir_getkeycode; + data->input_dev->setkeycode_new = wbcir_setkeycode; input_set_capability(data->input_dev, EV_MSC, MSC_SCAN); input_set_drvdata(data->input_dev, data); - err = input_register_device(data->input_dev); - if (err) - goto exit_free_input; - - data->last_scancode = INVALID_SCANCODE; - INIT_LIST_HEAD(&data->keytable); - setup_timer(&data->timer_keyup, wbcir_keyup, (unsigned long)data); - /* Load default keymaps */ if (protocol == IR_PROTOCOL_RC6) { - int i; - for (i = 0; i < ARRAY_SIZE(rc6_def_keymap); i++) { - err = wbcir_setkeycode(data->input_dev, - (int)rc6_def_keymap[i].scancode, - (int)rc6_def_keymap[i].keycode); - if (err) - goto exit_unregister_keys; - } + err = wbcir_load_keymap(data, rc6_def_keymap, + ARRAY_SIZE(rc6_def_keymap)); + if (err) + goto exit_free_input; } + err = input_register_device(data->input_dev); + if (err) + goto exit_free_keymap; + device_init_wakeup(&device->dev, 1); wbcir_init_hw(data); return 0; -exit_unregister_keys: - if (!list_empty(&data->keytable)) { - struct wbcir_keyentry *key; - struct wbcir_keyentry *keytmp; - - list_for_each_entry_safe(key, keytmp, &data->keytable, list) { - list_del(&key->list); - kfree(key); - } - } - input_unregister_device(data->input_dev); - /* Can't call input_free_device on an unregistered device */ - data->input_dev = NULL; +exit_free_keymap: + wbcir_free_keymap(data); exit_free_input: input_free_device(data->input_dev); exit_unregister_led: @@ -1510,8 +1593,6 @@ static void __devexit wbcir_remove(struct pnp_dev *device) { struct wbcir_data *data = pnp_get_drvdata(device); - struct wbcir_keyentry *key; - struct wbcir_keyentry *keytmp; /* Disable interrupts */ wbcir_select_bank(data, WBCIR_BANK_0); @@ -1544,10 +1625,7 @@ wbcir_remove(struct pnp_dev *device) release_region(data->ebase, EHFUNC_IOMEM_LEN); release_region(data->sbase, SP_IOMEM_LEN); - list_for_each_entry_safe(key, keytmp, &data->keytable, list) { - list_del(&key->list); - kfree(key); - } + wbcir_free_keymap(data); kfree(data); -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html