一、Rk3128 按键驱动
1、修改配置文件
vim arch/arm/boot/dts/rk3128-box.dts
新增gpio 管脚控制,描述如下,使用了 GPIO3_D2/GPIO3_D3/GPIO3_C1
rk3128_key{
compatible = "rk3128_mykey";
key_1 = <&gpio3 GPIO_D2 GPIO_ACTIVE_LOW>;
key_2 = <&gpio3 GPIO_D3 GPIO_ACTIVE_LOW>;
key_3 = <&gpio3 GPIO_C1 GPIO_ACTIVE_LOW>;
status = "okay";
};
2、编写驱动
中断方式
#define IRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001 上升沿触发
#define IRQF_TRIGGER_FALLING 0x00000002 下降沿触发
#define IRQF_TRIGGER_HIGH 0x00000004 高电平触发
#define IRQF_TRIGGER_LOW 0x00000008 低电平触发
设置中断触发方式
int irq_set_irq_type(unsigned int irq, unsigned int type);
申请中断
Int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev);
释放中断
void free_irq(unsigned int, void *);
3、按键功能定义
key_1 检测按下和抬起状态,按下状态提交BTN_0,抬起状态提交BTN_1值给安卓
key_2 检测按下状态 按下状态提交BTN_2值给安卓
key_3 检测按下状态 按下状态提交BTN_3值给安卓
#define CHECK_DOWN_UP 1 //检测按键按下和抬起状态
#define CHECK_DOWN 0 //检测按下状态
typedef struct{
unsigned int irqNum; //按键中断编号
unsigned int pin; //按键gpio
unsigned int reportValue; // report event value for android 提交按键值给应用
unsigned int irqmode; //按键中断触发方式
unsigned short old_down;//防抖,记录触发中断时按键值
unsigned short type; //按键检测类型
unsigned char keyState; //记录按键按下还是抬起状态,防止错误提交
char name[12]; //在dts 里面配置的按键名
}KeyButton;
4、完整驱动如下
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mm.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/time.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
#define DEBUG 1
#ifdef DEBUG
#define dbg(x...) printk(x)
#else
#define dbg(x...) (void)(0)
#endif
#define CHECK_DOWN_UP 1
#define CHECK_DOWN 0
#define KEY_DOWN 0
#define KEY_UP 1
#define BUTTION_SIZE (sizeof(keybutton)/sizeof(KeyButton))
typedef struct{
unsigned int irqNum;
unsigned int pin;
unsigned int reportValue; // report event value for android
unsigned int irqmode;
unsigned short old_down;
unsigned short type;
char name[12]; //在dts 里面配置的按键名
}KeyButton;
//定义按键值,和触发方式,默认设置按键为抬起状态
KeyButton keybutton[2]={
{0,0,BTN_0,IRQF_DISABLED|IRQF_TRIGGER_FALLING,0,CHECK_DOWN_UP,KEY_UP,"key_1"},
{0,0,BTN_2,IRQF_DISABLED|IRQF_TRIGGER_RISING,0,CHECK_DOWN,KEY_UP,"key_2"},
{0,0,BTN_3,IRQF_DISABLED|IRQF_TRIGGER_RISING,0,CHECK_DOWN,KEY_UP,"key_3"}
};
static struct platform_device *keypdev=NULL;
static struct input_dev *input_device;
struct timer_list my_timer;
static int old_down=0;
static int power_state=0;
// 提交按键值给安卓上传,因为是虚拟键值,需要上报1,再上报0
static void report_event(unsigned int event_value)
{
input_report_key(input_device,event_value, 1);
input_sync(input_device);
input_report_key(input_device,event_value, 0);
input_sync(input_device);
}
//定时器下半部,消抖检测
static void time_function(unsigned long data){
int i=0;
KeyButton *Key = (KeyButton *)data;
dbg("%s: interrupt gpio_get_value: %d old value : %d\n",Key->name,gpio_get_value(Key->pin),Key->old_down);
if(CHECK_DOWN_UP==Key->type)
{
if(gpio_get_value(Key->pin)==1)
{
if(Key->keyState==KEY_DOWN)
{
Key->keyState=KEY_UP;
report_event(Key->reportValue+1);
}
irq_set_irq_type(Key->irqNum, IRQF_DISABLED|IRQF_TRIGGER_FALLING);
dbg("input_report_key up \n");
}else{
if(Key->keyState==KEY_UP)
{
Key->keyState=KEY_DOWN;
report_event(Key->reportValue);
}
irq_set_irq_type(Key->irqNum, IRQF_DISABLED|IRQF_TRIGGER_RISING);
dbg("input_report_key down \n");
}
}else{
if(gpio_get_value(Key->pin)==0)
{
report_event(Key->reportValue);
}
}
}
//设置定时器回调函数
DEFINE_TIMER(my_timer,time_function,0,0);
//中断入口函数
static irqreturn_t key_irqhandler(int irq, void *dev_id)
{
//dbg("==========Interrupt============\n");
int i=0;
for(i=0;i<BUTTION_SIZE;i++)
{
if(irq==keybutton[i].irqNum)
{
keybutton[i].old_down=gpio_get_value(keybutton[i].pin);
//dbg("keybutton[%d].old_down =%d\n",i,keybutton[i].old_down);
my_timer.data=(unsigned long)&(keybutton[i]);
mod_timer(&my_timer,jiffies+HZ/10); //100ms
break;
}
}
return IRQ_HANDLED;
}
int wakeupKey_open(struct inode *inode, struct file *filp)
{
dbg("wakeupKey has been open!\n");
return -1;
}
int wakeupKey_release(struct inode *inode, struct file *filp)
{
dbg("wakeupKey has not been open yet!\n");
return -1;
}
ssize_t wakeupKey_read(struct file *filp, char *buf,
size_t count, loff_t fpos)
{
dbg("wakeupKey read!\n");
return 0;
}
ssize_t wakeupKey_write(struct file *filp, char *buf,
size_t count, loff_t fpos)
{
dbg("wakeupKey write!\n");
return 0;
}
int wakeupKey_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
dbg("ioctl is called!\n");
dbg("cmd:%d arg:%d\n", cmd, arg);
return 0;
}
struct file_operations fops =
{
.owner = THIS_MODULE,
.open = wakeupKey_open,
.release = wakeupKey_release,
.write = wakeupKey_write,
.read = wakeupKey_read,
.unlocked_ioctl = wakeupKey_ioctl
};
struct miscdevice dev =
{
.minor = MISC_DYNAMIC_MINOR,
.fops = &fops,
.name = "rk3128_key",
.nodename = "key_node"
};
static void gpioFree(unsigned int pin)
{
if (gpio_is_valid(pin)&&pin!=0){
gpio_free(pin);
}
}
static int Request_keyIrq(void)
{
int i,free;
int err = 0;
for(i=0;i<BUTTION_SIZE;i++)
{
keybutton[i].pin = of_get_named_gpio(keypdev->dev.of_node, keybutton[i].name, 0);
if (!gpio_is_valid(keybutton[i].pin))
{
dbg("invalid keybutton[i].pin: %d\n",keybutton[i].pin);
return -1;
}
if (devm_gpio_request(&keypdev->dev,keybutton[i].pin, keybutton[i].name))
{
dbg("gpio 0x%2x request failed name=%s ! ... \n",keybutton[i].pin,keybutton[i].name);
gpio_free(keybutton[i].pin);
keybutton[i].pin=0;
goto err0;
}
if(keybutton[i].pin==0)
{
continue;
}
gpio_direction_input(keybutton[i].pin);
keybutton[i].irqNum = gpio_to_irq(keybutton[i].pin);
/*注册中断函数*/
err = request_irq(keybutton[i].irqNum , key_irqhandler, keybutton[i].irqmode,keybutton[i].name, NULL);
if (err)
{
dbg("request_irq failed keybutton[%d].irqNum = %d \n",i,keybutton[i].irqNum);
goto err0;
}
dbg("gpio [%d] vaule = %d\n",i,gpio_get_value(keybutton[i].pin));
}
dbg("Request_keyIrq gpio request_irq ok .......\n");
return 0;
err0:
free=i;
dbg(" err2 : free = %d \n",free);
for(i=0;i<free;i++)
{
if(keybutton[i].irqNum)
free_irq(keybutton[i].irqNum,NULL);
gpio_free(keybutton[i].pin);
}
return -1;
}
static void free_keyIrq(void)
{
int i=0;
for(i=0;i<BUTTION_SIZE;i++)
{
if(keybutton[i].pin==0)
{
continue;
}
dbg("free_keyIrq: %d\n",keybutton[i].pin);
free_irq(keybutton[i].irqNum,NULL);
//gpio_free(keybutton[i].pin);
}
}
static int init_key(void)
{
int i=0,err=0;
Request_keyIrq();
input_device = input_allocate_device();
if (!input_device) {
goto err_free_irq;
}
set_bit(EV_KEY, input_device->evbit);
for(i=0;i<BUTTION_SIZE;i++)
{
set_bit(keybutton[i].reportValue, input_device->keybit);
if(keybutton[i].type==CHECK_DOWN_UP)
set_bit(keybutton[i].reportValue+1, input_device->keybit);
}
clear_bit(0, input_device->keybit);
err = input_register_device(input_device);
if (err) {
dbg(KERN_ERR "button.c: Failed to register device\n");
goto err_free_dev;
}
setup_timer(&my_timer,time_function,0);
return misc_register(&dev);
err_free_dev:
input_free_device(input_device);
err_free_irq:
free_keyIrq();
return -EBUSY;
}
static int event_key_probe(struct platform_device *pdev)
{
dbg("event_key_probe ok ............ \n");
//keyIrq = kmalloc(sizeof(KeyIrq),GFP_KERNEL);
keypdev = pdev;
init_key();
return 0;
}
static int event_key_remove(struct platform_device *pdev)
{
del_timer(&my_timer);
free_keyIrq();
input_free_device(input_device);
dbg("key remove ok \n");
return 0;
}
static const struct of_device_id of_event_key_match[] = {
{ .compatible = "rk3128_mykey", },
{},
};
static struct platform_driver event_key_driver = {
.probe = event_key_probe,
.remove = event_key_remove,
.driver = {
.name = "rk3128_key",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(of_event_key_match),
},
};
static int __init wakeupKey_init(void)
{
dbg("wakeupKey init ! ... \n");
platform_driver_register(&event_key_driver);
return 0;
}
static void __exit wakeupKey_exit(void)
{
platform_driver_unregister(&event_key_driver);
misc_deregister(&dev);
dbg("wakeupKey exit!\n");
}
module_init(wakeupKey_init);
module_exit(wakeupKey_exit);
MODULE_LICENSE("Dual BSD/GPL");
5、驱动测试
按下按键之后,事件会上报到安卓当中,测试阶段可以将提交的按键值为:
KEY_VOLUMEDOWN 音量减
KEY_VOLUMEUP 音量加
android 界面当中会弹出音量加减界面