android 拨动按键驱动

云科  一如

 

新板上有两个按键,是拨动按键,驱动参照了gpio_矩阵的驱动。我把这两个按键注册为2个不同的input_dev,产生不同的中断,并用同一个timer和handler来接收。其中一个按键——手电,并不报值,而是在驱动直接设置闪光灯的模式和亮度。FM按键功能是打开FM - -!(好雷),在上报值方面,使用input的EV_KEY类型来保值,当拨动为开时,上报一个键的按下,等待10ms,再上报该键的弹起;拨动为关时,上报另一个键的按下,等待10ms,再上报这个键的弹起。

源文件:

/**********************************************************
* toggle-keyboard.c
*
* keyboard driver for toggle button
*
*
***********************************************************/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
#include <asm/irq.h>
#include "toggle_keys.h"
#include <linux/gpio.h>
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/wakelock.h>
#include <linux/earlysuspend.h>
#include <linux/leds.h>
#include <linux/ktime.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>

#define DEVICE_NAME "toggle_kb"

extern struct toggle_bottons botton_array[];

struct gpio_toggle {
    struct toggle_bottons *input_devs;
    const struct gpio_toggle_platform_data *info;
};

static unsigned int kp_toggle_gpios[] = {
    GPIO_DEBUG_SELECT_N,
    GPIO_SIM2_MSM_SCLK
};

static struct mutex toggle_mutex;
static struct wake_lock toggle_wake_lock;
static struct hrtimer toggle_timer;

#define IRQ_EINT0 gpio_to_irq(kp_toggle_gpios[0])//torch
#define IRQ_EINT1 gpio_to_irq(kp_toggle_gpios[1])//FM
#define GPIO_FLASH_MODE 19
#define LED_FLASH 96

static int setTorch(char brightness){  
//    printk("dyyr-setTorch, bright=%d\n", brightness);
    gpio_set_value(GPIO_FLASH_MODE, 0);//torch mode
    mdelay(1);
    gpio_set_value(LED_FLASH, brightness);
    return 0;
}

 
static enum hrtimer_restart toggle_timer_func(struct hrtimer *timer){
    int gpio;
    int err;
    
    mutex_lock(&toggle_mutex);
    mdelay(200);    //Prevent jitter
    //only report FM, control torch directly here
    if (botton_array[1].irq_state == 1){
        gpio=gpio_get_value(kp_toggle_gpios[1]);
        if (gpio){
            input_report_key(botton_array[1].dev, KEY_F4, 1);
            input_sync(botton_array[1].dev);
            mdelay(10);
            input_report_key(botton_array[1].dev, KEY_F4, 0);
            input_sync(botton_array[1].dev);
            printk("dyyr-report press FM\n");
        }
        else{
            input_report_key(botton_array[1].dev, KEY_F5, 1);
            input_sync(botton_array[1].dev);
            mdelay(10);
            input_report_key(botton_array[1].dev, KEY_F5, 0);
            input_sync(botton_array[1].dev);
            printk("dyyr-report unpress FM\n");
        }
        enable_irq(IRQ_EINT1);
        botton_array[1].irq_state = 0;
    }
    else if (botton_array[0].irq_state == 1){
//        printk("dyyr-torch\n");
        gpio=gpio_get_value(kp_toggle_gpios[0]);
        if(gpio){
            err=setTorch(1);
        }
        else {
            err=setTorch(0);
        }
        botton_array[0].irq_state = 0;
        enable_irq(IRQ_EINT0);
    }
    else
       printk("dyyr-error in handler,all irq_state=0. \n");
    wake_unlock(&toggle_wake_lock);
    mutex_unlock(&toggle_mutex);
    return HRTIMER_NORESTART;
}


static irqreturn_t handler(int irq, void *botton){
    struct toggle_bottons *tb;
//    int gpio;

//    gpio=gpio_get_value(kp_toggle_gpios[1]);
//    printk("dyyr-in handler, irq=%d, gpio_84=%d\n", irq, gpio);
    tb = botton;
    disable_irq_nosync(irq);
    wake_lock(&toggle_wake_lock);
    tb->irq_state = 1;
    hrtimer_start(&toggle_timer, ktime_set(0, 0), HRTIMER_MODE_REL);
    return IRQ_HANDLED;
}

static int requestIrq(struct gpio_toggle *ip){    
    int err;

//    printk("dyyr-requestIrq\n");
    //Torch
    err = request_irq(IRQ_EINT0, handler, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, DEVICE_NAME, &ip->input_devs[0]);
    if(err)
        goto eint0_failed;

    if (err) {
        printk("set_irq_wake failed");
    }
    
    //FM
    err = request_irq(IRQ_EINT1, handler, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, DEVICE_NAME, &ip->input_devs[1]);
    if(err)
        goto eint1_failed;

    if (err) {
        printk("set_irq_wake failed ");
    }
    
    return 0;

eint1_failed:
    ;
eint0_failed:

    printk(DEVICE_NAME ": IRQ Requeset Error\n");
    return err;
}


static int gpio_toggle_probe(struct platform_device *pdev)
{
    int err;    
    struct gpio_toggle *ip;
    int dev_count = 2;
    int i;
    int registered = 0;
    

//    printk("dyyr-probe\n");
    ip = kzalloc(sizeof(*ip) +
             sizeof(pdev) * dev_count, GFP_KERNEL);
    if (ip == NULL) {
        err = -ENOMEM;
        printk("gpio_toggle_probe: Failed to allocate private data\n");
        goto err_kp_alloc_failed;
    }
    ip->input_devs = botton_array;
    for (i = 0; i < dev_count; i++) {
        struct input_dev *input_dev = input_allocate_device();
        if (input_dev == NULL) {
            err = -ENOMEM;
            printk("gpio_toggle_probe: Failed to allocate input device\n");
            goto err_input_dev_alloc_failed;
        }
        input_set_drvdata(input_dev, ip);
         input_dev->name = i ? "FM_botton" : "Torch_botton";
        ip->input_devs[i].dev = input_dev;
        ip->input_devs[i].irq_state = 0;
        err = input_register_device(ip->input_devs[i].dev);
        if (err) {
            printk("gpio_toggle_probe: Unable to register ");
            goto err_input_register_device_failed;
        }
        registered++;
        err = gpio_request(kp_toggle_gpios[i], "gpio_toggle");
            if (err) {
                printk("gpiomatrix: gpio_request failed for output %d\n", kp_toggle_gpios[i]);
                goto err_request_output_gpio_failed;
        }
        err = gpio_direction_input(kp_toggle_gpios[i]);
        if (err) {
            printk("dyyr-: gpio_configure failed for input %d\n", kp_toggle_gpios[i]);
            goto err_output_gpio_configure_failed;
        }        
    }
    ip->input_devs[0].timer = ip->input_devs[1].timer = &toggle_timer;
    ip->input_devs[0].wake_lock = ip->input_devs[1].wake_lock = &toggle_wake_lock;
    ip->input_devs[0].mutex = ip->input_devs[1].mutex = &toggle_mutex;
    hrtimer_init(&toggle_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    toggle_timer.function = toggle_timer_func;
    wake_lock_init(&toggle_wake_lock, WAKE_LOCK_SUSPEND, "toggle_button");            

    input_set_capability(ip->input_devs[1].dev, EV_KEY, KEY_F4);
    input_set_capability(ip->input_devs[1].dev, EV_KEY, KEY_F5);
    err=requestIrq(ip);
//    printk("dyyr ,after requestIrq,err = %d\n", err);    
    mutex_init(&toggle_mutex);
    return 0;
    
    
    for (i = dev_count - 1; i >= 0; i--) {
err_output_gpio_configure_failed:
        gpio_free(kp_toggle_gpios[i]);
    }
err_request_output_gpio_failed:

err_input_register_device_failed:
    for (i = 0; i < registered; i++)
        input_unregister_device(ip->input_devs[i].dev);
    for (i = dev_count - 1; i >= registered; i--) {
        input_free_device(ip->input_devs[i].dev);
err_input_dev_alloc_failed:
        ;
    }
    kfree(ip);
err_kp_alloc_failed:
    return err;

}


/*----------------------------------------------------
* func: 注销设备,释放中断
* param:
*
*
* return:

*
------------------------------------------------------*/
static int gpio_toggle_remove(struct platform_device *pdev)
{
    input_unregister_device(botton_array[0].dev);
    input_unregister_device(botton_array[1].dev);
    return 0;
}

static struct platform_driver gpio_toggle_driver = {
    .probe        = gpio_toggle_probe,
    .remove        = gpio_toggle_remove,
    .driver        = {
        .name    = DEVICE_NAME,
    },
};

static int __devinit toggle_kb_init(void)
{
    return platform_driver_register(&gpio_toggle_driver);
}

static void __exit toggle_kb_exit(void)
{
    platform_driver_unregister(&gpio_toggle_driver);
}


MODULE_AUTHOR("Daiyyr_mail:[email protected]");
MODULE_LICENSE("GPL");
module_init(toggle_kb_init);
module_exit(toggle_kb_exit);

 



源文件:

/*
 * Copyright (C) 2007 Google, Inc.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * 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.
 *
 */
#include<linux/input.h>
#include<linux/wakelock.h>


struct toggle_bottons {
    char irq_state;
    struct mutex *mutex;
    struct wake_lock *wake_lock;
    struct hrtimer *timer;
    struct input_dev *dev;
};


struct gpio_toggle_input_devs {
    int count;
    struct input_dev *dev[];
};

struct gpio_toggle_info {
    int (*func)(struct gpio_toggle_input_devs *input_devs,
            struct gpio_toggle_info *info,
            void **data, int func);
    int (*event)(struct gpio_toggle_input_devs *input_devs,
             struct gpio_toggle_info *info,
             void **data, unsigned int dev, unsigned int type,
             unsigned int code, int value); /* out events */
    bool no_suspend;
};


struct gpio_toggle_platform_data {
    const char *name;
    struct gpio_toggle_info **info;
    size_t info_count;
    int (*power)(const struct gpio_toggle_platform_data *pdata, bool on);
    const char *names[]; /* If name is NULL, names contain a NULL */
                 /* terminated list of input devices to create */
};


源文件
(部分)


//daiyyr@20130315, add for toggle button

struct toggle_bottons botton_array[2];


static struct platform_device tg_pdev = {
    .name    = "toggle_kb",
    .id    = -1,
    .dev    = {
        .platform_data    = &botton_array,
    },
};

其它make、config等有关编译的文件修改请参照:将新的驱动源文件添加进android内核进行编译

你可能感兴趣的:(android)