diff mbox

[4/6] Input: winbond-cir - switch to using new keycode interface

Message ID 20100908074200.32365.98120.stgit@hammer.corenet.prv (mailing list archive)
State New, archived
Headers show

Commit Message

Dmitry Torokhov Sept. 8, 2010, 7:42 a.m. UTC
None
diff mbox

Patch

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