@@ -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);