diff options
Diffstat (limited to 'drivers/media/IR/ir-keytable.c')
-rw-r--r-- | drivers/media/IR/ir-keytable.c | 125 |
1 files changed, 101 insertions, 24 deletions
diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c index a0aa5c1b6b41..a89456932f7c 100644 --- a/drivers/media/IR/ir-keytable.c +++ b/drivers/media/IR/ir-keytable.c @@ -21,6 +21,9 @@ #define IR_TAB_MIN_SIZE 256 #define IR_TAB_MAX_SIZE 8192 +/* FIXME: IR_KEYPRESS_TIMEOUT should be protocol specific */ +#define IR_KEYPRESS_TIMEOUT 250 + /** * ir_resize_table() - resizes a scancode table if necessary * @rc_tab: the ir_scancode_table to resize @@ -263,56 +266,124 @@ EXPORT_SYMBOL_GPL(ir_g_keycode_from_table); /** * ir_keyup() - generates input event to cleanup a key press - * @input_dev: the struct input_dev descriptor of the device + * @ir: the struct ir_input_dev descriptor of the device * - * This routine is used by the input routines when a key is pressed at the - * IR. It reports a keyup input event via input_report_key(). + * This routine is used to signal that a key has been released on the + * remote control. It reports a keyup input event via input_report_key(). + */ +static void ir_keyup(struct ir_input_dev *ir) +{ + if (!ir->keypressed) + return; + + IR_dprintk(1, "keyup key 0x%04x\n", ir->last_keycode); + input_report_key(ir->input_dev, ir->last_keycode, 0); + input_sync(ir->input_dev); + ir->keypressed = false; +} + +/** + * ir_timer_keyup() - generates a keyup event after a timeout + * @cookie: a pointer to struct ir_input_dev passed to setup_timer() + * + * This routine will generate a keyup event some time after a keydown event + * is generated when no further activity has been detected. */ -void ir_keyup(struct input_dev *dev) +static void ir_timer_keyup(unsigned long cookie) { + struct ir_input_dev *ir = (struct ir_input_dev *)cookie; + unsigned long flags; + + /* + * ir->keyup_jiffies is used to prevent a race condition if a + * hardware interrupt occurs at this point and the keyup timer + * event is moved further into the future as a result. + * + * The timer will then be reactivated and this function called + * again in the future. We need to exit gracefully in that case + * to allow the input subsystem to do its auto-repeat magic or + * a keyup event might follow immediately after the keydown. + */ + spin_lock_irqsave(&ir->keylock, flags); + if (time_is_after_eq_jiffies(ir->keyup_jiffies)) + ir_keyup(ir); + spin_unlock_irqrestore(&ir->keylock, flags); +} + +/** + * ir_repeat() - notifies the IR core that a key is still pressed + * @dev: the struct input_dev descriptor of the device + * + * 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. + */ +void ir_repeat(struct input_dev *dev) +{ + unsigned long flags; struct ir_input_dev *ir = input_get_drvdata(dev); + spin_lock_irqsave(&ir->keylock, flags); + if (!ir->keypressed) - return; + goto out; - IR_dprintk(1, "keyup key 0x%04x\n", ir->keycode); - input_report_key(dev, ir->keycode, 0); - input_sync(dev); - ir->keypressed = 0; + ir->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT); + mod_timer(&ir->timer_keyup, ir->keyup_jiffies); + +out: + spin_unlock_irqrestore(&ir->keylock, flags); } -EXPORT_SYMBOL_GPL(ir_keyup); +EXPORT_SYMBOL_GPL(ir_repeat); /** * ir_keydown() - generates input event for a key press - * @input_dev: the struct input_dev descriptor of the device - * @scancode: the scancode that we're seeking + * @dev: the struct input_dev descriptor of the device + * @scancode: the scancode that we're seeking + * @toggle: the toggle value (protocol dependent, if the protocol doesn't + * support toggle values, this should be set to zero) * * This routine is used by the input routines when a key is pressed at the * IR. It gets the keycode for a scancode and reports an input event via * input_report_key(). */ -void ir_keydown(struct input_dev *dev, int scancode) +void ir_keydown(struct input_dev *dev, int scancode, u8 toggle) { + unsigned long flags; struct ir_input_dev *ir = input_get_drvdata(dev); u32 keycode = ir_g_keycode_from_table(dev, scancode); - /* If already sent a keydown, do a keyup */ - if (ir->keypressed) - ir_keyup(dev); + spin_lock_irqsave(&ir->keylock, flags); - if (KEY_RESERVED == keycode) - return; + /* Repeat event? */ + if (ir->keypressed && + ir->last_scancode == scancode && + ir->last_toggle == toggle) + goto set_timer; - ir->keycode = keycode; - ir->keypressed = 1; + /* Release old keypress */ + ir_keyup(ir); - IR_dprintk(1, "%s: key down event, key 0x%04x, scancode 0x%04x\n", - dev->name, keycode, scancode); + ir->last_scancode = scancode; + ir->last_toggle = toggle; + ir->last_keycode = keycode; + + if (keycode == KEY_RESERVED) + goto out; - input_report_key(dev, ir->keycode, 1); + /* Register a keypress */ + ir->keypressed = true; + IR_dprintk(1, "%s: key down event, key 0x%04x, scancode 0x%04x\n", + dev->name, keycode, scancode); + input_report_key(dev, ir->last_keycode, 1); input_sync(dev); +set_timer: + ir->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT); + mod_timer(&ir->timer_keyup, ir->keyup_jiffies); +out: + spin_unlock_irqrestore(&ir->keylock, flags); } EXPORT_SYMBOL_GPL(ir_keydown); @@ -365,8 +436,12 @@ int __ir_input_register(struct input_dev *input_dev, input_dev->getkeycode = ir_getkeycode; input_dev->setkeycode = ir_setkeycode; input_set_drvdata(input_dev, ir_dev); + ir_dev->input_dev = input_dev; spin_lock_init(&ir_dev->rc_tab.lock); + spin_lock_init(&ir_dev->keylock); + setup_timer(&ir_dev->timer_keyup, ir_timer_keyup, (unsigned long)ir_dev); + ir_dev->rc_tab.name = rc_tab->name; ir_dev->rc_tab.ir_type = rc_tab->ir_type; ir_dev->rc_tab.alloc = roundup_pow_of_two(rc_tab->size * @@ -383,6 +458,8 @@ int __ir_input_register(struct input_dev *input_dev, ir_dev->rc_tab.size, ir_dev->rc_tab.alloc); set_bit(EV_KEY, input_dev->evbit); + set_bit(EV_REP, input_dev->evbit); + if (ir_setkeytable(input_dev, &ir_dev->rc_tab, rc_tab)) { rc = -ENOMEM; goto out_table; @@ -428,7 +505,7 @@ void ir_input_unregister(struct input_dev *dev) return; IR_dprintk(1, "Freed keycode table\n"); - + del_timer_sync(&ir_dev->timer_keyup); rc_tab = &ir_dev->rc_tab; rc_tab->size = 0; kfree(rc_tab->scan); |