diff options
Diffstat (limited to 'drivers/input/misc')
-rw-r--r-- | drivers/input/misc/Kconfig | 19 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 2 | ||||
-rw-r--r-- | drivers/input/misc/ati_remote2.c | 16 | ||||
-rw-r--r-- | drivers/input/misc/dm355evm_keys.c | 329 | ||||
-rw-r--r-- | drivers/input/misc/rotary_encoder.c | 61 | ||||
-rw-r--r-- | drivers/input/misc/twl4030-pwrbutton.c | 145 | ||||
-rw-r--r-- | drivers/input/misc/uinput.c | 94 |
7 files changed, 615 insertions, 51 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 06f46fc..1acfa3a 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -193,6 +193,16 @@ config INPUT_CM109 To compile this driver as a module, choose M here: the module will be called cm109. +config INPUT_TWL4030_PWRBUTTON + tristate "TWL4030 Power button Driver" + depends on TWL4030_CORE + help + Say Y here if you want to enable power key reporting via the + TWL4030 family of chips. + + To compile this driver as a module, choose M here. The module will + be called twl4030_pwrbutton. + config INPUT_UINPUT tristate "User level driver support" help @@ -250,4 +260,13 @@ config INPUT_RB532_BUTTON To compile this driver as a module, choose M here: the module will be called rb532_button. +config INPUT_DM355EVM + tristate "TI DaVinci DM355 EVM Keypad and IR Remote" + depends on MFD_DM355EVM_MSP + help + Supports the pushbuttons and IR remote used with + the DM355 EVM board. + + To compile this driver as a module, choose M here: the + module will be called dm355evm_keys. endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index eb3f407..0d979fd 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o obj-$(CONFIG_INPUT_CM109) += cm109.o obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o +obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o @@ -21,6 +22,7 @@ obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o +obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_UINPUT) += uinput.o obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index 922c051..0501f0e 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -509,7 +509,7 @@ static int ati_remote2_setkeycode(struct input_dev *idev, int scancode, int keyc old_keycode = ar2->keycode[mode][index]; ar2->keycode[mode][index] = keycode; - set_bit(keycode, idev->keybit); + __set_bit(keycode, idev->keybit); for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) { for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) { @@ -518,7 +518,7 @@ static int ati_remote2_setkeycode(struct input_dev *idev, int scancode, int keyc } } - clear_bit(old_keycode, idev->keybit); + __clear_bit(old_keycode, idev->keybit); return 0; } @@ -543,7 +543,7 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2) for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) { for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) { ar2->keycode[mode][index] = ati_remote2_key_table[index].keycode; - set_bit(ar2->keycode[mode][index], idev->keybit); + __set_bit(ar2->keycode[mode][index], idev->keybit); } } @@ -554,11 +554,11 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2) ar2->keycode[ATI_REMOTE2_AUX3][index] = KEY_PROG3; ar2->keycode[ATI_REMOTE2_AUX4][index] = KEY_PROG4; ar2->keycode[ATI_REMOTE2_PC][index] = KEY_PC; - set_bit(KEY_PROG1, idev->keybit); - set_bit(KEY_PROG2, idev->keybit); - set_bit(KEY_PROG3, idev->keybit); - set_bit(KEY_PROG4, idev->keybit); - set_bit(KEY_PC, idev->keybit); + __set_bit(KEY_PROG1, idev->keybit); + __set_bit(KEY_PROG2, idev->keybit); + __set_bit(KEY_PROG3, idev->keybit); + __set_bit(KEY_PROG4, idev->keybit); + __set_bit(KEY_PC, idev->keybit); idev->rep[REP_DELAY] = 250; idev->rep[REP_PERIOD] = 33; diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c new file mode 100644 index 0000000..a63315c --- /dev/null +++ b/drivers/input/misc/dm355evm_keys.c @@ -0,0 +1,329 @@ +/* + * dm355evm_keys.c - support buttons and IR remote on DM355 EVM board + * + * Copyright (c) 2008 by David Brownell + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> + +#include <linux/i2c/dm355evm_msp.h> + + +/* + * The MSP430 firmware on the DM355 EVM monitors on-board pushbuttons + * and an IR receptor used for the remote control. When any key is + * pressed, or its autorepeat kicks in, an event is sent. This driver + * read those events from the small (32 event) queue and reports them. + * + * Because we communicate with the MSP430 using I2C, and all I2C calls + * in Linux sleep, we need to cons up a kind of threaded IRQ handler + * using a work_struct. The IRQ is active low, but we use it through + * the GPIO controller so we can trigger on falling edges. + * + * Note that physically there can only be one of these devices. + * + * This driver was tested with firmware revision A4. + */ +struct dm355evm_keys { + struct work_struct work; + struct input_dev *input; + struct device *dev; + int irq; +}; + +static irqreturn_t dm355evm_keys_irq(int irq, void *_keys) +{ + struct dm355evm_keys *keys = _keys; + + schedule_work(&keys->work); + return IRQ_HANDLED; +} + +/* These initial keycodes can be remapped by dm355evm_setkeycode(). */ +static struct { + u16 event; + u16 keycode; +} dm355evm_keys[] = { + + /* + * Pushbuttons on the EVM board ... note that the labels for these + * are SW10/SW11/etc on the PC board. The left/right orientation + * comes only from the firmware's documentation, and presumes the + * power connector is immediately in front of you and the IR sensor + * is to the right. (That is, rotate the board counter-clockwise + * by 90 degrees from the SW10/etc and "DM355 EVM" labels.) + */ + { 0x00d8, KEY_OK, }, /* SW12 */ + { 0x00b8, KEY_UP, }, /* SW13 */ + { 0x00e8, KEY_DOWN, }, /* SW11 */ + { 0x0078, KEY_LEFT, }, /* SW14 */ + { 0x00f0, KEY_RIGHT, }, /* SW10 */ + + /* + * IR buttons ... codes assigned to match the universal remote + * provided with the EVM (Philips PM4S) using DVD code 0020. + * + * These event codes match firmware documentation, but other + * remote controls could easily send more RC5-encoded events. + * The PM4S manual was used in several cases to help select + * a keycode reflecting the intended usage. + * + * RC5 codes are 14 bits, with two start bits (0x3 prefix) + * and a toggle bit (masked out below). + */ + { 0x300c, KEY_POWER, }, /* NOTE: docs omit this */ + { 0x3000, KEY_NUMERIC_0, }, + { 0x3001, KEY_NUMERIC_1, }, + { 0x3002, KEY_NUMERIC_2, }, + { 0x3003, KEY_NUMERIC_3, }, + { 0x3004, KEY_NUMERIC_4, }, + { 0x3005, KEY_NUMERIC_5, }, + { 0x3006, KEY_NUMERIC_6, }, + { 0x3007, KEY_NUMERIC_7, }, + { 0x3008, KEY_NUMERIC_8, }, + { 0x3009, KEY_NUMERIC_9, }, + { 0x3022, KEY_ENTER, }, + { 0x30ec, KEY_MODE, }, /* "tv/vcr/..." */ + { 0x300f, KEY_SELECT, }, /* "info" */ + { 0x3020, KEY_CHANNELUP, }, /* "up" */ + { 0x302e, KEY_MENU, }, /* "in/out" */ + { 0x3011, KEY_VOLUMEDOWN, }, /* "left" */ + { 0x300d, KEY_MUTE, }, /* "ok" */ + { 0x3010, KEY_VOLUMEUP, }, /* "right" */ + { 0x301e, KEY_SUBTITLE, }, /* "cc" */ + { 0x3021, KEY_CHANNELDOWN, }, /* "down" */ + { 0x3022, KEY_PREVIOUS, }, + { 0x3026, KEY_SLEEP, }, + { 0x3172, KEY_REWIND, }, /* NOTE: docs wrongly say 0x30ca */ + { 0x3175, KEY_PLAY, }, + { 0x3174, KEY_FASTFORWARD, }, + { 0x3177, KEY_RECORD, }, + { 0x3176, KEY_STOP, }, + { 0x3169, KEY_PAUSE, }, +}; + +static void dm355evm_keys_work(struct work_struct *work) +{ + struct dm355evm_keys *keys; + int status; + + keys = container_of(work, struct dm355evm_keys, work); + + /* For simplicity we ignore INPUT_COUNT and just read + * events until we get the "queue empty" indicator. + * Reading INPUT_LOW decrements the count. + */ + for (;;) { + static u16 last_event; + u16 event; + int keycode; + int i; + + status = dm355evm_msp_read(DM355EVM_MSP_INPUT_HIGH); + if (status < 0) { + dev_dbg(keys->dev, "input high err %d\n", + status); + break; + } + event = status << 8; + + status = dm355evm_msp_read(DM355EVM_MSP_INPUT_LOW); + if (status < 0) { + dev_dbg(keys->dev, "input low err %d\n", + status); + break; + } + event |= status; + if (event == 0xdead) + break; + + /* Press and release a button: two events, same code. + * Press and hold (autorepeat), then release: N events + * (N > 2), same code. For RC5 buttons the toggle bits + * distinguish (for example) "1-autorepeat" from "1 1"; + * but PCB buttons don't support that bit. + * + * So we must synthesize release events. We do that by + * mapping events to a press/release event pair; then + * to avoid adding extra events, skip the second event + * of each pair. + */ + if (event == last_event) { + last_event = 0; + continue; + } + last_event = event; + + /* ignore the RC5 toggle bit */ + event &= ~0x0800; + + /* find the key, or leave it as unknown */ + keycode = KEY_UNKNOWN; + for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) { + if (dm355evm_keys[i].event != event) + continue; + keycode = dm355evm_keys[i].keycode; + break; + } + dev_dbg(keys->dev, + "input event 0x%04x--> keycode %d\n", + event, keycode); + + /* report press + release */ + input_report_key(keys->input, keycode, 1); + input_sync(keys->input); + input_report_key(keys->input, keycode, 0); + input_sync(keys->input); + } +} + +static int dm355evm_setkeycode(struct input_dev *dev, int index, int keycode) +{ + u16 old_keycode; + unsigned i; + + if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys)) + return -EINVAL; + + old_keycode = dm355evm_keys[index].keycode; + dm355evm_keys[index].keycode = keycode; + set_bit(keycode, dev->keybit); + + for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) { + if (dm355evm_keys[index].keycode == old_keycode) + goto done; + } + clear_bit(old_keycode, dev->keybit); +done: + return 0; +} + +static int dm355evm_getkeycode(struct input_dev *dev, int index, int *keycode) +{ + if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys)) + return -EINVAL; + + return dm355evm_keys[index].keycode; +} + +/*----------------------------------------------------------------------*/ + +static int __devinit dm355evm_keys_probe(struct platform_device *pdev) +{ + struct dm355evm_keys *keys; + struct input_dev *input; + int status; + int i; + + /* allocate instance struct and input dev */ + keys = kzalloc(sizeof *keys, GFP_KERNEL); + input = input_allocate_device(); + if (!keys || !input) { + status = -ENOMEM; + goto fail1; + } + + keys->dev = &pdev->dev; + keys->input = input; + INIT_WORK(&keys->work, dm355evm_keys_work); + + /* set up "threaded IRQ handler" */ + status = platform_get_irq(pdev, 0); + if (status < 0) + goto fail1; + keys->irq = status; + + input_set_drvdata(input, keys); + + input->name = "DM355 EVM Controls"; + input->phys = "dm355evm/input0"; + input->dev.parent = &pdev->dev; + + input->id.bustype = BUS_I2C; + input->id.product = 0x0355; + input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV); + + input->evbit[0] = BIT(EV_KEY); + for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) + __set_bit(dm355evm_keys[i].keycode, input->keybit); + + input->setkeycode = dm355evm_setkeycode; + input->getkeycode = dm355evm_getkeycode; + + /* REVISIT: flush the event queue? */ + + status = request_irq(keys->irq, dm355evm_keys_irq, + IRQF_TRIGGER_FALLING, + dev_name(&pdev->dev), keys); + if (status < 0) + goto fail1; + + /* register */ + status = input_register_device(input); + if (status < 0) + goto fail2; + + platform_set_drvdata(pdev, keys); + + return 0; + +fail2: + free_irq(keys->irq, keys); +fail1: + input_free_device(input); + kfree(keys); + dev_err(&pdev->dev, "can't register, err %d\n", status); + + return status; +} + +static int __devexit dm355evm_keys_remove(struct platform_device *pdev) +{ + struct dm355evm_keys *keys = platform_get_drvdata(pdev); + + free_irq(keys->irq, keys); + input_unregister_device(keys->input); + kfree(keys); + + return 0; +} + +/* REVISIT: add suspend/resume when DaVinci supports it. The IRQ should + * be able to wake up the system. When device_may_wakeup(&pdev->dev), call + * enable_irq_wake() on suspend, and disable_irq_wake() on resume. + */ + +/* + * I2C is used to talk to the MSP430, but this platform device is + * exposed by an MFD driver that manages I2C communications. + */ +static struct platform_driver dm355evm_keys_driver = { + .probe = dm355evm_keys_probe, + .remove = __devexit_p(dm355evm_keys_remove), + .driver = { + .owner = THIS_MODULE, + .name = "dm355evm_keys", + }, +}; + +static int __init dm355evm_keys_init(void) +{ + return platform_driver_register(&dm355evm_keys_driver); +} +module_init(dm355evm_keys_init); + +static void __exit dm355evm_keys_exit(void) +{ + platform_driver_unregister(&dm355evm_keys_driver); +} +module_exit(dm355evm_keys_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 5bb3ab5..c806fbf 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -26,13 +26,17 @@ #define DRV_NAME "rotary-encoder" struct rotary_encoder { - unsigned int irq_a; - unsigned int irq_b; - unsigned int pos; - unsigned int armed; - unsigned int dir; struct input_dev *input; struct rotary_encoder_platform_data *pdata; + + unsigned int axis; + unsigned int pos; + + unsigned int irq_a; + unsigned int irq_b; + + bool armed; + unsigned char dir; /* 0 - clockwise, 1 - CCW */ }; static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) @@ -53,21 +57,32 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) if (!encoder->armed) break; - if (encoder->dir) { - /* turning counter-clockwise */ - encoder->pos += pdata->steps; - encoder->pos--; - encoder->pos %= pdata->steps; + if (pdata->relative_axis) { + input_report_rel(encoder->input, pdata->axis, + encoder->dir ? -1 : 1); } else { - /* turning clockwise */ - encoder->pos++; - encoder->pos %= pdata->steps; + unsigned int pos = encoder->pos; + + if (encoder->dir) { + /* turning counter-clockwise */ + if (pdata->rollover) + pos += pdata->steps; + if (pos) + pos--; + } else { + /* turning clockwise */ + if (pdata->rollover || pos < pdata->steps) + pos++; + } + if (pdata->rollover) + pos %= pdata->steps; + encoder->pos = pos; + input_report_abs(encoder->input, pdata->axis, + encoder->pos); } - - input_report_abs(encoder->input, pdata->axis, encoder->pos); input_sync(encoder->input); - encoder->armed = 0; + encoder->armed = false; break; case 0x1: @@ -77,7 +92,7 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) break; case 0x3: - encoder->armed = 1; + encoder->armed = true; break; } @@ -113,9 +128,15 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev) input->name = pdev->name; input->id.bustype = BUS_HOST; input->dev.parent = &pdev->dev; - input->evbit[0] = BIT_MASK(EV_ABS); - input_set_abs_params(encoder->input, - pdata->axis, 0, pdata->steps, 0, 1); + + if (pdata->relative_axis) { + input->evbit[0] = BIT_MASK(EV_REL); + input->relbit[0] = BIT_MASK(pdata->axis); + } else { + input->evbit[0] = BIT_MASK(EV_ABS); + input_set_abs_params(encoder->input, + pdata->axis, 0, pdata->steps, 0, 1); + } err = input_register_device(input); if (err) { diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c new file mode 100644 index 0000000..f5fc997 --- /dev/null +++ b/drivers/input/misc/twl4030-pwrbutton.c @@ -0,0 +1,145 @@ +/** + * twl4030-pwrbutton.c - TWL4030 Power Button Input Driver + * + * Copyright (C) 2008-2009 Nokia Corporation + * + * Written by Peter De Schrijver <peter.de-schrijver@nokia.com> + * Several fixes by Felipe Balbi <felipe.balbi@nokia.com> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/i2c/twl4030.h> + +#define PWR_PWRON_IRQ (1 << 0) + +#define STS_HW_CONDITIONS 0xf + +static irqreturn_t powerbutton_irq(int irq, void *_pwr) +{ + struct input_dev *pwr = _pwr; + int err; + u8 value; + +#ifdef CONFIG_LOCKDEP + /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which + * we don't want and can't tolerate since this is a threaded + * IRQ and can sleep due to the i2c reads it has to issue. + * Although it might be friendlier not to borrow this thread + * context... + */ + local_irq_enable(); +#endif + + err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &value, + STS_HW_CONDITIONS); + if (!err) { + input_report_key(pwr, KEY_POWER, value & PWR_PWRON_IRQ); + input_sync(pwr); + } else { + dev_err(pwr->dev.parent, "twl4030: i2c error %d while reading" + " TWL4030 PM_MASTER STS_HW_CONDITIONS register\n", err); + } + + return IRQ_HANDLED; +} + +static int __devinit twl4030_pwrbutton_probe(struct platform_device *pdev) +{ + struct input_dev *pwr; + int irq = platform_get_irq(pdev, 0); + int err; + + pwr = input_allocate_device(); + if (!pwr) { + dev_dbg(&pdev->dev, "Can't allocate power button\n"); + return -ENOMEM; + } + + pwr->evbit[0] = BIT_MASK(EV_KEY); + pwr->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); + pwr->name = "twl4030_pwrbutton"; + pwr->phys = "twl4030_pwrbutton/input0"; + pwr->dev.parent = &pdev->dev; + + err = request_irq(irq, powerbutton_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "twl4030_pwrbutton", pwr); + if (err < 0) { + dev_dbg(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err); + goto free_input_dev; + } + + err = input_register_device(pwr); + if (err) { + dev_dbg(&pdev->dev, "Can't register power button: %d\n", err); + goto free_irq; + } + + platform_set_drvdata(pdev, pwr); + + return 0; + +free_irq: + free_irq(irq, NULL); +free_input_dev: + input_free_device(pwr); + return err; +} + +static int __devexit twl4030_pwrbutton_remove(struct platform_device *pdev) +{ + struct input_dev *pwr = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + free_irq(irq, pwr); + input_unregister_device(pwr); + + return 0; +} + +struct platform_driver twl4030_pwrbutton_driver = { + .probe = twl4030_pwrbutton_probe, + .remove = __devexit_p(twl4030_pwrbutton_remove), + .driver = { + .name = "twl4030_pwrbutton", + .owner = THIS_MODULE, + }, +}; + +static int __init twl4030_pwrbutton_init(void) +{ + return platform_driver_register(&twl4030_pwrbutton_driver); +} +module_init(twl4030_pwrbutton_init); + +static void __exit twl4030_pwrbutton_exit(void) +{ + platform_driver_unregister(&twl4030_pwrbutton_driver); +} +module_exit(twl4030_pwrbutton_exit); + +MODULE_ALIAS("platform:twl4030_pwrbutton"); +MODULE_DESCRIPTION("Triton2 Power Button"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Peter De Schrijver <peter.de-schrijver@nokia.com>"); +MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>"); + diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 46b7cae..c5a49ab 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -54,27 +54,28 @@ static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned i return 0; } +/* Atomically allocate an ID for the given request. Returns 0 on success. */ static int uinput_request_alloc_id(struct uinput_device *udev, struct uinput_request *request) { - /* Atomically allocate an ID for the given request. Returns 0 on success. */ int id; int err = -1; spin_lock(&udev->requests_lock); - for (id = 0; id < UINPUT_NUM_REQUESTS; id++) + for (id = 0; id < UINPUT_NUM_REQUESTS; id++) { if (!udev->requests[id]) { request->id = id; udev->requests[id] = request; err = 0; break; } + } spin_unlock(&udev->requests_lock); return err; } -static struct uinput_request* uinput_request_find(struct uinput_device *udev, int id) +static struct uinput_request *uinput_request_find(struct uinput_device *udev, int id) { /* Find an input request, by ID. Returns NULL if the ID isn't valid. */ if (id >= UINPUT_NUM_REQUESTS || id < 0) @@ -99,14 +100,51 @@ static void uinput_request_done(struct uinput_device *udev, struct uinput_reques complete(&request->done); } -static int uinput_request_submit(struct input_dev *dev, struct uinput_request *request) +static int uinput_request_submit(struct uinput_device *udev, struct uinput_request *request) { + int retval; + + retval = uinput_request_reserve_slot(udev, request); + if (retval) + return retval; + + retval = mutex_lock_interruptible(&udev->mutex); + if (retval) + return retval; + + if (udev->state != UIST_CREATED) { + retval = -ENODEV; + goto out; + } + /* Tell our userspace app about this new request by queueing an input event */ - uinput_dev_event(dev, EV_UINPUT, request->code, request->id); + uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id); + + out: + mutex_unlock(&udev->mutex); + return retval; +} + +/* + * Fail all ouitstanding requests so handlers don't wait for the userspace + * to finish processing them. + */ +static void uinput_flush_requests(struct uinput_device *udev) +{ + struct uinput_request *request; + int i; + + spin_lock(&udev->requests_lock); + + for (i = 0; i < UINPUT_NUM_REQUESTS; i++) { + request = udev->requests[i]; + if (request) { + request->retval = -ENODEV; + uinput_request_done(udev, request); + } + } - /* Wait for the request to complete */ - wait_for_completion(&request->done); - return request->retval; + spin_unlock(&udev->requests_lock); } static void uinput_dev_set_gain(struct input_dev *dev, u16 gain) @@ -126,6 +164,7 @@ static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value) static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) { + struct uinput_device *udev = input_get_drvdata(dev); struct uinput_request request; int retval; @@ -146,15 +185,18 @@ static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *eff request.u.upload.effect = effect; request.u.upload.old = old; - retval = uinput_request_reserve_slot(input_get_drvdata(dev), &request); - if (!retval) - retval = uinput_request_submit(dev, &request); + retval = uinput_request_submit(udev, &request); + if (!retval) { + wait_for_completion(&request.done); + retval = request.retval; + } return retval; } static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) { + struct uinput_device *udev = input_get_drvdata(dev); struct uinput_request request; int retval; @@ -166,9 +208,11 @@ static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) request.code = UI_FF_ERASE; request.u.effect_id = effect_id; - retval = uinput_request_reserve_slot(input_get_drvdata(dev), &request); - if (!retval) - retval = uinput_request_submit(dev, &request); + retval = uinput_request_submit(udev, &request); + if (!retval) { + wait_for_completion(&request.done); + retval = request.retval; + } return retval; } @@ -176,20 +220,24 @@ static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) static void uinput_destroy_device(struct uinput_device *udev) { const char *name, *phys; + struct input_dev *dev = udev->dev; + enum uinput_state old_state = udev->state; - if (udev->dev) { - name = udev->dev->name; - phys = udev->dev->phys; - if (udev->state == UIST_CREATED) - input_unregister_device(udev->dev); - else - input_free_device(udev->dev); + udev->state = UIST_NEW_DEVICE; + + if (dev) { + name = dev->name; + phys = dev->phys; + if (old_state == UIST_CREATED) { + uinput_flush_requests(udev); + input_unregister_device(dev); + } else { + input_free_device(dev); + } kfree(name); kfree(phys); udev->dev = NULL; } - - udev->state = UIST_NEW_DEVICE; } static int uinput_create_device(struct uinput_device *udev) |