/* * Copyright 2009 Logitech. All Rights Reserved. */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #define DEBOUNCE_TIME (50 * HZ/1000) #define NUM_BUTTONS 3 unsigned int button_codes[] = { KEY_MUTE, KEY_RIGHT, KEY_POWER, }; enum button_state { BUTTON_IDLE, BUTTON_PRESSED, BUTTON_HOLD, BUTTON_RELEASED, }; struct baby_button { int bnum; struct input_dev *inputdev; struct timer_list timer; unsigned int gpio; enum button_state bstate; unsigned int code; }; struct baby_buttons { struct baby_button buttons[NUM_BUTTONS]; }; static void button_timer_callback(unsigned long data) { struct baby_button *state = (struct baby_button *) data; unsigned int gpio = state->gpio; int pressed = !mxc_get_gpio_datain(gpio); switch (state->bstate) { case BUTTON_IDLE: printk(KERN_ERR "button_timer_callback: should not be in timer callback when state is idle.\n"); break; case BUTTON_PRESSED: state->bstate = BUTTON_HOLD; break; case BUTTON_HOLD: if (!pressed) { state->bstate = BUTTON_RELEASED; input_report_key(state->inputdev, state->code, 0); } break; case BUTTON_RELEASED: state->bstate = BUTTON_IDLE; break; } if (state->bstate != BUTTON_IDLE) { mod_timer(&state->timer, jiffies + DEBOUNCE_TIME); } } static irqreturn_t button_irq(int irq, void *data) { struct baby_button *state = (struct baby_button *) data; int bnum; for (bnum=0; bnumbstate == BUTTON_IDLE) { /* Only start state machine if we're in IDLE. Otherwise ignore any state transitions */ mod_timer(&state->timer, jiffies + DEBOUNCE_TIME); state->bstate = BUTTON_PRESSED; input_report_key(state->inputdev, state->code, 1); } } } return IRQ_HANDLED; } static int baby_buttons_probe(struct platform_device *pdev) { struct baby_button *state; int bnum, ret; struct input_dev *inputdev; state = kmalloc(sizeof(struct baby_button[NUM_BUTTONS]), GFP_KERNEL); if (!state) { goto err0; } inputdev = input_allocate_device(); if (!inputdev) { goto err1; } inputdev->name = "front panel"; inputdev->evbit[0] = BIT(EV_KEY); inputdev->id.bustype = BUS_HOST; for (bnum = 0; bnum < NUM_BUTTONS; bnum++) { unsigned int gpio; state[bnum].bnum = bnum; state[bnum].code = button_codes[bnum]; init_timer(&state[bnum].timer); state[bnum].timer.data = (unsigned long)(&(state[bnum])); state[bnum].timer.function = button_timer_callback; state[bnum].inputdev = inputdev; gpio = platform_get_resource(pdev, IORESOURCE_MEM, bnum)->start; __set_bit(state[bnum].code, state->inputdev->keybit); state[bnum].gpio = gpio; set_irq_type(IOMUX_TO_IRQ(gpio), IRQ_TYPE_EDGE_FALLING); if (mxc_get_gpio_datain(gpio)) { state[bnum].bstate = BUTTON_IDLE; } else { /* Go ahead and start the button state machine */ state[bnum].bstate = BUTTON_HOLD; mod_timer(&state[bnum].timer, jiffies + DEBOUNCE_TIME); } ret = request_irq(IOMUX_TO_IRQ(gpio), button_irq, IRQF_DISABLED | IRQF_SAMPLE_RANDOM, pdev->name, &state[bnum]); if (ret) { printk(KERN_ERR "Can't allocate irq\n"); continue; } } ret = input_register_device(inputdev); if (ret < 0) { goto err2; } return 0; err2: input_free_device(inputdev); err1: kfree(state); err0: return 0; } static int baby_buttons_remove(struct platform_device *pdev) { return 0; } static struct platform_driver baby_buttons_driver = { .probe = baby_buttons_probe, .remove = baby_buttons_remove, .driver = { .name = "baby_buttons", }, }; static int __init baby_buttons_init(void) { return platform_driver_register(&baby_buttons_driver); } static void __exit baby_buttons_exit(void) { platform_driver_unregister(&baby_buttons_driver); } MODULE_AUTHOR ("Richard Titmuss "); MODULE_DESCRIPTION("Baby button driver"); MODULE_LICENSE("GPL"); module_init(baby_buttons_init) module_exit(baby_buttons_exit)