Linux旋钮驱动调试(安卓)
一、旋钮特性
旋钮转动时会有相位差,我们只需要根据这个特点去判断是正旋还是反旋做出相应的操作即可:
正旋输出两次时:
反旋输出两次时:
由上图可以发现,判断正反旋其实很简单。判断哪个引脚先出现电平变化即可。
二、驱动编写
#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/module.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/irq.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#define TAG "knod"
#define pr_info(fmt, arg...) printk(KERN_ERR fmt, ##arg)
#define DEBUG_ON 1
#define DEBUG(fmt, arg...) do {\
if (DEBUG_ON)\
pr_info("<<-DEBUG->> [%d]"fmt"\n", __LINE__, ##arg);\
} while (0)
#define PIN_MAX 4
int knod_pin[PIN_MAX] = {0};
int knod_start[PIN_MAX/2] = {-1};
int knod_key_events[PIN_MAX] = {KEY_VOLUMEDOWN,KEY_VOLUMEUP,KEY_VOLUMEDOWN,KEY_VOLUMEUP};
/*业务需求需要两个旋钮*/
struct input_dev *input_dev;
static int input_dev_init(struct platform_device *pdev)
{
int ret = 0;
int i = 0;
input_dev = input_allocate_device();
if (!input_dev)
{
input_free_device(input_dev);
return -ENOMEM;
}
input_dev->name = "knod";
input_dev->dev.parent = &pdev->dev;
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0x0001;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
set_bit(EV_KEY, input_dev->evbit);
for(i = 0; i < PIN_MAX; i ++)
__set_bit(knod_key_events[i] & KEY_MAX, input_dev->keybit);
input_set_drvdata(input_dev,pdev);
/*register device*/
ret = input_register_device(input_dev);
if (ret < 0)
{
input_free_device(input_dev);
DEBUG("##@@input register failed.ret=%d\n",ret);
return ret;
}
DEBUG("##@@input register ok.\n");
return 0;
}
irqreturn_t knod1_handler(int irq,void *date)
{
if (!(gpio_get_value(knod_pin[0])))
knod_start[0] = 1;
else
knod_start[0] = 0;
return IRQ_HANDLED;
}
irqreturn_t knod2_handler(int irq,void *date)
{
int state;
state = gpio_get_value(knod_pin[1]);
if ((knod_start[0]) == (state))
{
input_report_key(input_dev, knod_key_events[0], 1);
input_sync(input_dev);
input_report_key(input_dev, knod_key_events[0], 0);
input_sync(input_dev);
}
if ((knod_start[0]) == (!state))
{
input_report_key(input_dev, knod_key_events[1], 1);
input_sync(input_dev);
input_report_key(input_dev, knod_key_events[1], 0);
input_sync(input_dev);
}
return IRQ_HANDLED;
}
irqreturn_t knod3_handler(int irq,void *date)
{
if (!(gpio_get_value(knod_pin[2])))
knod_start[1] = 1;
else
knod_start[1] = 0;
return IRQ_HANDLED;
}
irqreturn_t knod4_handler(int irq,void *date)
{
int state;
state = gpio_get_value(knod_pin[3]);
if ((knod_start[1]) == (state))
{
input_report_key(input_dev, knod_key_events[2], 1);
input_sync(input_dev);
input_report_key(input_dev, knod_key_events[2], 0);
input_sync(input_dev);
}
if ((knod_start[1]) == (!state))
{
input_report_key(input_dev, knod_key_events[3], 1);
input_sync(input_dev);
input_report_key(input_dev, knod_key_events[3], 0);
input_sync(input_dev);
}
return IRQ_HANDLED;
}
static int irq_gpio_init(void)
{
int i = 0;
int ret = -1;
int irq[PIN_MAX ] = {-1};
char *knod_named[PIN_MAX] = {"knob_1_gpio1","knob_1_gpio2","knob_2_gpio1","knob_2_gpio2"};
char *request_knod_named[PIN_MAX] = {"1_knod_pin","2_knod_pin","3_knod_pin","4_knod_pin"};
char *knod_irq_name[PIN_MAX] = {"knod-1","knod-2","knod-3","knod-4"};
irqreturn_t (*knod_handler[PIN_MAX])(int irq,void *date);
static struct device_node *knod_node = NULL;
knod_node = of_find_compatible_node(NULL, NULL, "xxxx, knod");
if(knod_node == NULL)
{
DEBUG("@@of_find_compatible_node_fail !!!\n");
goto of_find_compatible_node_fail;
}
knod_handler[0] = knod1_handler;
knod_handler[1] = knod2_handler;
knod_handler[2] = knod3_handler;
knod_handler[3] = knod4_handler;
for(i = 0; i < PIN_MAX;i ++)
{
knod_pin[i] = of_get_named_gpio(knod_node,knod_named[i], 0);
if (!gpio_is_valid(knod_pin[i]))
{
DEBUG("@@of_get_named_gpio_fail !!!,knod_pin[%d] = %d\n",i,knod_pin[i]);
goto of_get_named_gpio_fail;
}
gpio_free(knod_pin[i]);
ret = gpio_request(knod_pin[i], request_knod_named[i]);
if (ret < 0)
{
DEBUG("@@gpio_request_fail !!!,button_pin[%d] = %d\n",i,knod_pin[i]);
goto gpio_request_fail;
}
gpio_direction_input(knod_pin[i]);
irq[i] = gpio_to_irq(knod_pin[i]);
if(irq[i] < 0)
{
DEBUG("@@request irq is failed !!!,irq[%d] = %d\n",i,irq[i]);
goto gpio_to_irq_fail;
}
ret = request_irq(irq[i],
knod_handler[i],
IRQ_TYPE_EDGE_BOTH,
knod_irq_name[i],
NULL);
if(ret != 0)
{
DEBUG("@@@request_irq_fail !!! ret=%d,irq[%d]=%d\n",ret,i,irq[i]);
goto request_irq_fail;
}
DEBUG("@@refunction irq ret = %d,request_irq suc = %s\n",ret,request_knod_named[i]);
}
return 0;
request_irq_fail:
gpio_to_irq_fail:
gpio_request_fail:
while(--i > -1)
gpio_free(knod_pin[i]);
of_get_named_gpio_fail:
of_find_compatible_node_fail:
return ret;
}
static int knod_probe(struct platform_device *pdev)
{
int ret = 0,i;
DEBUG("knod_probe\n");
ret = irq_gpio_init();
if(ret != 0)
{
DEBUG("irq_gpio_init error\n");
return ret;
}
ret = input_dev_init(pdev);
if(ret != 0)
{
DEBUG("input_dev_init error\n");
goto input_dev_init_error;
}
return 0;
input_dev_init_error:
for(i = 0; i < PIN_MAX; i++ )
gpio_free(knod_pin[i]);
return ret;
}
static int knod_remove(struct platform_device *pdev)
{
int i = 0;
input_free_device(input_dev);
for(i = 0; i < PIN_MAX; i++ )
gpio_free(knod_pin[i]);
return 0;
}
static struct platform_driver knod_driver = {
.driver = {
.name = "knod-dev",
.owner = THIS_MODULE,
},
.remove = knod_remove,
.probe = knod_probe,
};
static struct platform_device knod_device = {
.name = "knod-dev",
.id = -1,
};
static int __init knod_init(void)
{
int ret = -1;
DEBUG("knod_init\n");
ret = platform_device_register(&knod_device);
if (ret)
{
DEBUG("platform_device_register_failed ");
goto platform_device_register_failed;
}
ret = platform_driver_register(&knod_driver);
if (ret)
{
DEBUG("platform_driver_register_failed ");
goto platform_driver_register_failed;
}
return 0;
platform_driver_register_failed:
platform_device_unregister(&knod_device);
platform_device_register_failed:
return ret;
}
static void __exit knod_exit(void)
{
DEBUG("knod exit\r\n");
platform_driver_unregister(&knod_driver);
platform_device_unregister(&knod_device);
}
module_init(knod_init);
module_exit(knod_exit);
MODULE_LICENSE("GPL");
三、GPIO配置(设备树)
1、dtsi配置节点属性
knod: knod{
compatible = "xxxx, knod";/*与驱动相对应*/
};
2、dts配置gpio属性
&knod{
knob_1_gpio1 = <&pio0 77 0>;
knob_1_gpio2 = <&pio0 78 0>;
knob_2_gpio1 = <&pio0 73 0>;
knob_2_gpio2 = <&pio0 74 0>;
interrupt-parent = <&pio0>;/*对应的gpio0中断控制器*/
interrupts = <120 IRQ_TYPE_EDGE_BOTH>,<121 IRQ_TYPE_EDGE_BOTH>,<116 IRQ_TYPE_EDGE_BOTH>,<117 IRQ_TYPE_EDGE_BOTH>;/*对应的gpio中断号,中断触发方式(上下沿)*/
status = "okay";
};
四、input事件调试
检查是否上报用getevent查看就足够了,想要了解具体的上报流程可以参考以下网址:
Android input键值从底层到应用层映射流程与修改方法
五、驱动改进版
#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/module.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/irq.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#define TAG "knod"
#define pr_info(fmt, arg...) printk(KERN_ERR fmt, ##arg)
#define GPIO_DEBUG_ON 1
#define GPIO_DEBUG(fmt, arg...) do {\
if (GPIO_DEBUG_ON)\
pr_info("<<-DEBUG->> [%d]"fmt"\n", __LINE__, ##arg);\
} while (0)
#define PIN_MAX 4
#define KEY_AIR_CONDITION_UP 763
#define KEY_AIR_CONDITION_DOWN 764
#define KNOD1_RIGHT 1
#define KNOD1_LEFT 2
#define KNOD2_RIGHT 3
#define KNOD2_LEFT 4
#define KNOD1_ID 0
#define KNOD2_ID 1
#define KNOD_PIN1 0
#define KNOD_PIN2 1
#define KNOD_THREAD_STOP 0
#define KNOD_THREAD_RUN 1
#define KNOD_KEY_DOWN 1
#define KNOD_KEY_UP 0
#define KNOD_NAMED {\
"knob_1_gpio1","knob_1_gpio2",\
"knob_2_gpio1","knob_2_gpio2",\
}
#define REQUEST_KNOD_NAMED {\
"1_knod_pin","2_knod_pin",\
"3_knod_pin","4_knod_pin",\
}
#define KNOD_IRQ_NAME {\
"knod-1","knod-2",\
"knod-3","knod-4",\
}
#define KNOD_KEY_EVENTS {\
KEY_AIR_CONDITION_DOWN,KEY_AIR_CONDITION_UP,\
KEY_VOLUMEDOWN,KEY_VOLUMEUP,\
}
#define KNOD_SENSITIVITY 20
struct knod_info
{
u8 knod_start;
s32 irq[2];
s32 knod_right_count;
s32 knod_left_count;
u32 knod_pin[2];
u32 knod_key_events[2];
};
struct knod_struct
{
u8 knod_status;
u8 thread_flage;
spinlock_t irq_lock;
struct input_dev *input_dev;
struct task_struct *knod_input_task;
struct completion origin_completion;
struct knod_info knod[PIN_MAX/2];
};
struct knod_struct *knod;
void input_knod_event(u32 knod_id,u32 knod_pin)
{
#if 0
s32 count = 0;
if(knod_pin == KNOD_PIN2)
count = knod->knod[knod_id].knod_right_count;
else
count = knod->knod[knod_id].knod_left_count;
GPIO_DEBUG("@@count = %d\n",count);
while(count > 0)
{
if(1)
{
}
--count;
}
#endif
input_report_key(knod->input_dev, knod->knod[knod_id].knod_key_events[knod_pin], KNOD_KEY_DOWN);
input_sync(knod->input_dev);
input_report_key(knod->input_dev, knod->knod[knod_id].knod_key_events[knod_pin], KNOD_KEY_UP);
input_sync(knod->input_dev);
}
int knod_input_thread(void *data)
{
u32 i = 0;
while(1)
{
//GPIO_DEBUG("@@knod_input_thread knod->knod_status = %d,knod->thread_flage = %d\n",knod->knod_status,knod->thread_flage);
if(knod->knod_status == 0)
{
wait_for_completion(&knod->origin_completion);
}
msleep(10);//Filter the fast rotation within 10ms
switch(knod->knod_status)
{
case KNOD1_RIGHT:
input_knod_event(KNOD1_ID,KNOD_PIN2);break;
case KNOD1_LEFT:
input_knod_event(KNOD1_ID,KNOD_PIN1);break;
case KNOD2_RIGHT:
input_knod_event(KNOD2_ID,KNOD_PIN2);break;
case KNOD2_LEFT:
input_knod_event(KNOD2_ID,KNOD_PIN1);break;
default:break;
}
for(i = 0;i < (PIN_MAX/2);i ++)
{
knod->knod[i].knod_left_count = 0;
knod->knod[i].knod_right_count = 0;
}
knod->thread_flage = KNOD_THREAD_STOP;
wait_for_completion(&knod->origin_completion);
}
return 0;
}
static int input_dev_init(struct platform_device *pdev)
{
int ret = 0;
int i = 0,j = 0;
knod->input_dev = input_allocate_device();
if (!knod->input_dev)
{
input_free_device(knod->input_dev);
return -ENOMEM;
}
knod->input_dev->name = "knod";
knod->input_dev->dev.parent = &pdev->dev;
knod->input_dev->id.bustype = BUS_HOST;
knod->input_dev->id.vendor = 0x0001;
knod->input_dev->id.product = 0x0001;
knod->input_dev->id.version = 0x0100;
set_bit(EV_KEY, knod->input_dev->evbit);
for(i = 0; i < (PIN_MAX/2); i ++)
for(j = 0; j < 2;j ++)
__set_bit(knod->knod[i].knod_key_events[j] & KEY_MAX, knod->input_dev->keybit);
input_set_drvdata(knod->input_dev,pdev);
/*register device*/
ret = input_register_device(knod->input_dev);
if (ret < 0)
{
input_free_device(knod->input_dev);
GPIO_DEBUG("##@@input register failed.ret=%d\n",ret);
return ret;
}
GPIO_DEBUG("##@@input register ok.\n");
return 0;
}
void knod_count(u32 knod_id,u32 knod_pin)
{
u8 state;
if(knod_pin == KNOD_PIN1)
{
if (!(gpio_get_value(knod->knod[knod_id].knod_pin[knod_pin])))
knod->knod[knod_id].knod_start = 1;
else
knod->knod[knod_id].knod_start = 0;
}
else if(knod_pin == KNOD_PIN2)
{
state = gpio_get_value(knod->knod[knod_id].knod_pin[knod_pin]);
if ((knod->knod[knod_id].knod_start) == (state))
{
if(knod_id == KNOD1_ID)
knod->knod_status = KNOD1_LEFT;
else
knod->knod_status = KNOD2_LEFT;
++(knod->knod[knod_id].knod_left_count);
GPIO_DEBUG("knod->knod[%d].knod_left_count=%d!!!\n",knod_id,knod->knod[knod_id].knod_left_count);
}
if ((knod->knod[knod_id].knod_start) == (!state))
{
if(knod_id == KNOD1_ID)
knod->knod_status = KNOD1_RIGHT;
else
knod->knod_status = KNOD2_RIGHT;
++(knod->knod[knod_id].knod_right_count);
GPIO_DEBUG("knod->knod[%d].knod_right_count=%d!!!\n",knod_id,knod->knod[knod_id].knod_right_count);
}
if(knod->thread_flage == KNOD_THREAD_STOP)
{
complete(&knod->origin_completion);
knod->thread_flage = KNOD_THREAD_RUN;
}
}
else
{
GPIO_DEBUG("knod_pin error\n");
}
}
irqreturn_t knod1_handler(int irq,void *date)
{
unsigned long irqflags = 0;
spin_lock_irqsave(&knod->irq_lock, irqflags);//prevent irq from nesting
knod_count(KNOD1_ID,KNOD_PIN1);
spin_unlock_irqrestore(&knod->irq_lock, irqflags);
return IRQ_HANDLED;
}
irqreturn_t knod2_handler(int irq,void *date)
{
unsigned long irqflags = 0;
spin_lock_irqsave(&knod->irq_lock, irqflags);//prevent irq from nesting
knod_count(KNOD1_ID,KNOD_PIN2);
spin_unlock_irqrestore(&knod->irq_lock, irqflags);
return IRQ_HANDLED;
}
irqreturn_t knod3_handler(int irq,void *date)
{
unsigned long irqflags = 0;
spin_lock_irqsave(&knod->irq_lock, irqflags);//prevent irq from nesting
knod_count(KNOD2_ID,KNOD_PIN1);
spin_unlock_irqrestore(&knod->irq_lock, irqflags);
return IRQ_HANDLED;
}
irqreturn_t knod4_handler(int irq,void *date)
{
unsigned long irqflags = 0;
spin_lock_irqsave(&knod->irq_lock, irqflags);//prevent irq from nesting
knod_count(KNOD2_ID,KNOD_PIN2);
spin_unlock_irqrestore(&knod->irq_lock, irqflags);
return IRQ_HANDLED;
}
static int irq_gpio_init(void)
{
u32 i = 0,j = 0,count = 0;
u32 ret = -1;
u8 *knod_named[PIN_MAX] = KNOD_NAMED;
u8 *request_knod_named[PIN_MAX] = REQUEST_KNOD_NAMED;
u8 *knod_irq_name[PIN_MAX] = KNOD_IRQ_NAME;
u32 knod_key_events[PIN_MAX] = KNOD_KEY_EVENTS;
irqreturn_t (*knod_handler[PIN_MAX])(int irq,void *date);
static struct device_node *knod_node = NULL;
knod_node = of_find_compatible_node(NULL, NULL, "audio, knod");
if(knod_node == NULL)
{
GPIO_DEBUG("@@of_find_compatible_node_fail !!!\n");
goto of_find_compatible_node_fail;
}
knod = (struct knod_struct *)kmalloc( sizeof(struct knod_struct),GFP_KERNEL);
if (knod == NULL)
{
ret = -ENOMEM;
GPIO_DEBUG("[knob]:kmalloc err\n");
goto kmalloc_error;
}
memset(knod,0,sizeof(struct knod_struct));
knod_handler[0] = knod1_handler;
knod_handler[1] = knod2_handler;
knod_handler[2] = knod3_handler;
knod_handler[3] = knod4_handler;
for(i = 0,count = 0; i < (PIN_MAX/2) && count < PIN_MAX;i ++)
{
for(j = 0; j < 2;j ++,count++)
{
knod->knod[i].knod_pin[j] = of_get_named_gpio(knod_node,knod_named[count],0);
if (!gpio_is_valid(knod->knod[i].knod_pin[j]))
{
GPIO_DEBUG("@@of_get_named_gpio_fail !!!,knod->knod[%d]->knod_pin[%d] = %d\n",i,j,knod->knod[i].knod_pin[j]);
goto of_get_named_gpio_fail;
}
gpio_free(knod->knod[i].knod_pin[j]);
ret = gpio_request(knod->knod[i].knod_pin[j], request_knod_named[count]);
if (ret < 0)
{
GPIO_DEBUG("@@of_get_named_gpio_fail !!!,knod->knod[%d]->knod_pin[%d] = %d\n",i,j,knod->knod[i].knod_pin[j]);
goto gpio_request_fail;
}
knod->knod[i].irq[j] = gpio_to_irq(knod->knod[i].knod_pin[j]);
if(knod->knod[i].irq[j] < 0)
{
GPIO_DEBUG("@@request irq is failed !!!,knod->knod[%d]->irq[%d] = %d\n",i,j,knod->knod[i].irq[j]);
goto gpio_to_irq_fail;
}
ret = request_irq(knod->knod[i].irq[j],
knod_handler[count],
IRQ_TYPE_EDGE_BOTH,
knod_irq_name[count],
NULL);
if(ret != 0)
{
GPIO_DEBUG("@@request irq is failed !!!,ret =%d,knod->knod[%d]->irq[%d] = %d\n",ret,i,j,knod->knod[i].irq[j]);
goto request_irq_fail;
}
GPIO_DEBUG("@@refunction irq ret = %d,request_irq suc = %s\n",ret,request_knod_named[count]);
memcpy(&knod->knod[i].knod_key_events[j],&knod_key_events[count],sizeof(knod_key_events[count]));
}
}
return 0;
request_irq_fail:
gpio_to_irq_fail:
gpio_request_fail:
for(i = 0; i < (PIN_MAX/2);i ++)
for(j = 0; j < 2;j ++)
gpio_free(knod->knod[i].knod_pin[j]);
of_get_named_gpio_fail:
kmalloc_error:
of_find_compatible_node_fail:
return ret;
}
static int knod_probe(struct platform_device *pdev)
{
int ret = 0,i,j;
GPIO_DEBUG("knod_probe\n");
ret = irq_gpio_init();
if(ret != 0)
{
GPIO_DEBUG("irq_gpio_init error\n");
goto irq_gpio_init_error;
}
init_completion(&knod->origin_completion);
knod->knod_input_task = kthread_create(knod_input_thread, NULL, "knod_input_task");
if(IS_ERR(knod->knod_input_task))
{
printk("Unable to start kernel thread.\n");
ret = PTR_ERR(knod->knod_input_task);
knod->knod_input_task = NULL;
goto kthread_create_error;
}
wake_up_process(knod->knod_input_task);
ret = input_dev_init(pdev);
if(ret != 0)
{
GPIO_DEBUG("input_dev_init error\n");
goto input_dev_init_error;
}
return 0;
input_dev_init_error:
kthread_create_error:
for(i = 0; i < (PIN_MAX/2);i ++)
for(j = 0; j < 2;j ++)
gpio_free(knod->knod[i].knod_pin[j]);
irq_gpio_init_error:
return ret;
}
static int knod_remove(struct platform_device *pdev)
{
int i = 0,j = 0;
kthread_stop(knod->knod_input_task);
input_free_device(knod->input_dev);
for(i = 0; i < (PIN_MAX/2);i ++)
for(j = 0; j < 2;j ++)
gpio_free(knod->knod[i].knod_pin[j]);
return 0;
}
static struct platform_driver knod_driver = {
.driver = {
.name = "knod-dev",
.owner = THIS_MODULE,
},
.remove = knod_remove,
.probe = knod_probe,
};
static struct platform_device knod_device = {
.name = "knod-dev",
.id = -1,
};
static int __init knod_init(void)
{
int ret = -1;
GPIO_DEBUG("knod_init\n");
ret = platform_device_register(&knod_device);
if (ret)
{
GPIO_DEBUG("platform_device_register_failed ");
goto platform_device_register_failed;
}
ret = platform_driver_register(&knod_driver);
if (ret)
{
GPIO_DEBUG("platform_driver_register_failed ");
goto platform_driver_register_failed;
}
return 0;
platform_driver_register_failed:
platform_device_unregister(&knod_device);
platform_device_register_failed:
return ret;
}
static void __exit knod_exit(void)
{
GPIO_DEBUG("knod exit\r\n");
platform_driver_unregister(&knod_driver);
platform_device_unregister(&knod_device);
}
module_init(knod_init);
module_exit(knod_exit);
MODULE_LICENSE("GPL");