MTK开发 — touch key驱动

本文介绍了Linux驱动开发中关于设备树、平台设备驱动、GPIO、中断处理、input子系统及工作队列的流程。通过实例展示了如何解析设备树配置GPIO和中断,注册input设备,以及在中断下半部使用工作队列进行处理。详细代码注释帮助理解各个知识点的应用。
摘要由CSDN通过智能技术生成

本驱动的大概流程是:加载驱动module_init—》注册platform驱动—》解析设备树—》申请gpio与中断—》注册input设备—》创建工作队列用于中断下半部

其中涉及驱动相关的知识点有设备树、platform设备驱动总线、gpio/pinctrl子系统、input子系统、中断上/下半部、软中断与工作队列的使用

如果要将以上每点都讲清,实在是不容易的,每个知识点都可以长篇大论,本人更喜欢实操,直接上代码,代码中有比较详细的注释,看看应该都能懂!

设备树 

定义了一个touchsensor节点,上面两个节点是pinctrl的两个状态,都配置成输入,pull disable

touchsensor_pins_tp_int8: eint@8 {
	pins_cmd_dat {
		pins = <MT8163_PIN_30_EINT8__FUNC_GPIO30>;
		slew-rate = <0>;     //输入
		bias-disable;        //pull disable
	};
};

touchsensor_pins_tp_int9: eint@9 {
	pins_cmd_dat {
		pins = <MT8163_PIN_31_EINT9__FUNC_GPIO31>;
		slew-rate = <0>;
	    bias-disable;
	};
};

&touchsensor {
	
	interrupt-parent = <&pio>;
	interrupts = <8 IRQ_TYPE_EDGE_FALLING>, 
                 <9 IRQ_TYPE_EDGE_FALLING>;
							
	eint-debounce = <256>;	
	pinctrl-names = "tp_int8", "tp_int9";
	pinctrl-0 = <&touchsensor_pins_tp_int8>;
	pinctrl-1 = <&touchsensor_pins_tp_int9>;
	
	int8_gpio30 = <&pio 30 0>;
	int9_gpio31 = <&pio 31 0>;
	
	status = "okay";
};

驱动代码

#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/string.h>
#include <linux/gpio.h>
#include <linux/timer.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/fs.h>
#include <linux/input.h>
#include <linux/workqueue.h>
#include <linux/pinctrl/consumer.h>

#define	USE_DECLARE_WORK	1//是否使用静态工作队列

static struct input_dev *touch_input_dev;
void func_task_let(unsigned long data);
void func_work_queue(struct work_struct *work);

//方式1:使用静态方式创建工作队列与软中断
//静态(编译时)创建名为my_work的结构体变量并把函数入口地址和参数地址赋给它
DECLARE_WORK(mywork, func_work_queue);
//静态(编译时)创建名为mytasklet的结构体变量并把函数入口地址和参数地址赋给它
DECLARE_TASKLET(mytasklet, func_task_let, 0);
//也可以在程序运行时再用INIT_WORK()创建

//方式1:使用动态方式创建工作队列与软中断
//定义一个名为work的工作结构体变量,需使用INIT_WORK()初始化
static struct work_struct work;
//定义一个名为wq的工作队列结构体指针,需指向create_workqueue创建的工作队列
static struct workqueue_struct *wq;

static struct pinctrl *pin_ctrl;
static struct pinctrl_state *tp_int8, *tp_int9;

static unsigned int int8_gpio30,int9_gpio31;
static unsigned int irq_num8, irq_num9;

static unsigned char touch_event = 0;

static void touch_work_handle(struct work_struct *work)
{
	if(touch_event=='a') {
		printk("work queue handle: aaa event\n");
	}
 	else if(touch_event=='b') {
		printk("work queue handle: bbb event\n");
	}
	touch_event=0;
}

void func_task_let(unsigned long data)
{
	printk("key-tasklet-test: this is bottom half\n");
}

void func_work_queue(struct work_struct *work)
{
	printk("key-work-queue-test: this is workqueue bottom half\n");
}

static irqreturn_t tp_eint_interrupt_handler(int irq, void *dev_id)
{
	if(irq_num8 == irq) {
	    touch_event='a';
		printk("interrupt:---eint10---\n");
		#if(!USE_DECLARE_WORK)
		//判断中断下文处理函数是否还未完成
		if (!work_pending(&work)) {
			queue_work(wq, &work);//将工作添加到动态工作队列等待执行
		}
		#else
		//将任务提交到工作队列,系统会调度执行
		schedule_work(&mywork);
		//schedule_delayed_work(&my_work,tick);//延时tick个滴答后再提交工作
		#endif
	}
	else if(irq_num9 == irq) {
		touch_event='b';
		printk("interrupt:---eint11---\n");
		//将mytasklet代表的软中断添加到向量tasklet_vec的尾部,并触发一个软中断
		tasklet_schedule(&mytasklet);
	}
	printk("irq num:%d\n", irq);
	
	return IRQ_HANDLED;
}

static int tp_get_gpio_info(struct platform_device *pdev)
{
	int ret;
	struct device_node *node = NULL;
	
	/*如果设备树节点中把pinctrl-names = "default",则以下代码可以不写*/
	/*获取设备对应的pin control state holder*/
	pin_ctrl = devm_pinctrl_get(&pdev->dev);
	if (IS_ERR(pin_ctrl)) {
		ret = PTR_ERR(pin_ctrl);
		dev_err(&pdev->dev, "tp Cannot find touch pin_ctrl!\n");
		return ret;
	}
	/*查找pin control state*/
	tp_int8 = pinctrl_lookup_state(pin_ctrl, "tp_int8");
	if (IS_ERR(tp_int8)) {
		ret = PTR_ERR(tp_int8);
		dev_err(&pdev->dev, "tp Cannot find touch pinctrl default %d!\n", ret);
	}
	tp_int9 = pinctrl_lookup_state(pin_ctrl, "tp_int9");
	if (IS_ERR(tp_int9)) {
		ret = PTR_ERR(tp_int9);
		dev_err(&pdev->dev, "tp Cannot find touch pinctrl default %d!\n", ret);
	}
	/*设置gpio功能, 将获取到的信息直接设置到硬件*/
	pinctrl_select_state(pin_ctrl, tp_int8);
	pinctrl_select_state(pin_ctrl, tp_int9);

	//从设备树中根据compatible从树根开始寻找节点
	node = of_find_compatible_node(NULL, NULL, "mediatek,touchsensor");
	if (node) {
		//此函数从设备树中根据名称提取gpio口编号
		//会将设备树中类似<&pio 32 0>的属性信息转换为对应的GPIO编号
		int8_gpio30= of_get_named_gpio(node, "int8_gpio30", 0);
		int9_gpio31= of_get_named_gpio(node, "int9_gpio31", 0);
		
		//从设备树interupts属性中提取到对应的中断号
		irq_num8 = irq_of_parse_and_map(node, 0);
		printk("tp_probe_irq = %d\n", irq_num8);
		irq_num9 = irq_of_parse_and_map(node, 1);
		printk("tp_probe_irq = %d\n", irq_num9);		
	}

	printk("tp_get_gpio_info-----\n");
	
	return ret;
}

static int touch_probe(struct platform_device *dev)
{
	int ret, err;
	int debounce = 17;//消抖时间设置,单位(ms),也可从设备树获取

	tp_get_gpio_info(dev);//得到gpio信息

	if ((int8_gpio30 != 0)&&(int9_gpio31 != 0)) {
		//标记已有驱动使用了该gpio,此处就没判断返回结果了
		gpio_request(int8_gpio30, "int8_gpio30");
		gpio_request(int9_gpio31, "int9_gpio31");
		
		//设置gpio消抖时间
		gpio_set_debounce(int8_gpio30, debounce);
		gpio_set_debounce(int9_gpio31, debounce);
		//request_irq用于申请激活中断,设置以后可以在/proc/interrupts中看到对应的中断名字
		ret = request_irq(irq_num8, (irq_handler_t) tp_eint_interrupt_handler, \
							 IRQF_TRIGGER_FALLING, "touchsensor-eint", NULL);

		ret = request_irq(irq_num9, (irq_handler_t) tp_eint_interrupt_handler, \
							IRQF_TRIGGER_FALLING, "touchsensor-eint", NULL);
		if (ret > 0) {
			ret = -1;
			printk("tp_probe request_irq IRQ LINE NOT AVAILABLE!ret = %d.",ret);
		}
	}
	/**********************input设备注册**********************/
	touch_input_dev = input_allocate_device();	//分配设备,注册设备支持event类型
	__set_bit(EV_SYN, touch_input_dev->evbit);  //设置产生同步事件
	__set_bit(EV_KEY, touch_input_dev->evbit);	//设置产生按键事件
	__set_bit(KEY_VOLUMEUP, touch_input_dev->keybit);//设置产生哪些按键值(音量+-)
	__set_bit(KEY_VOLUMEDOWN, touch_input_dev->keybit);
	touch_input_dev->name ="TouchSensor";//input设备的名字,也就是input目录下文件名
	err = input_register_device(touch_input_dev);//向input子系统注册input_dev设备
	if (err) {
		printk( "touch register input device failed (%d)\n", err);
		input_free_device(touch_input_dev);
		return err;
	}
	/*********************************************************/
	//create_workqueue创建一个workqueue队列,为系统中的每个CPU都创建一个内核线程
	//创建一个名为kworkqueue_iqs的工作队列并把工作队列的入口地址赋给声明的指针
	wq = create_singlethread_workqueue("kworkqueue_iqs");//只创建一个内核线程
	flush_workqueue(wq);//清理指定工作队列中的所有任务
	//创建一个工作结构体变量并初始化
	//work表示要初始化的工作,touch_mcu_worker是工作对应的处理函数
	INIT_WORK(&work, touch_work_handle);

	printk("touch_probe====test\n");
	
	return 0;
}

#ifdef CONFIG_OF//有设备树的情况下就用这个匹配
static const struct of_device_id TP_of_match[] = {
	{.compatible = "mediatek,touchsensor"},
	{},
};
#endif

static struct platform_driver tp_platform_driver = {
	.probe = touch_probe,
	.driver = {
		   .name = "touchsensor",
		   .owner = THIS_MODULE,
#ifdef CONFIG_OF
		   .of_match_table = TP_of_match,
#endif
	},
};

//lkk触摸按钮:EINT_8 ~ EINT_11四个
static int __init touchsensor_init(void)
{	
	platform_driver_register(&tp_platform_driver);

	printk("touchsensor test init finish-------------\n");
	return  0;
}

static void __exit touchsensor_exit(void)
{
	free_irq(irq_num8, NULL);//释放irq_num10中断
	free_irq(irq_num9, NULL);//释放irq_num11中断
	input_unregister_device(touch_input_dev);//注销touch_input_dev
	input_free_device(touch_input_dev); 	 //删除touch_input_dev
}

module_init(touchsensor_init);
module_exit(touchsensor_exit);

MODULE_AUTHOR("mstar");
MODULE_DESCRIPTION("touchsensor");
MODULE_LICENSE("GPL");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东皇※太一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值