云科 一如
新板上有两个按键,是拨动按键,驱动参照了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内核进行编译