这一节,我们将从零开始写tiny4412的触摸屏驱动ft5x06,写这节博客之前,先了解下需要什么知识:
1、i2c驱动相关的知识
2、输入子系统
3、中断
4、工作队列
关于i2c驱动相关的知识,在后期的博文里会专门写几篇博文来进行总结,这里就不再说i2c相关的知识,我们先知道怎么用就行了。
首先,我在ts.h构造了一个ts_info_st结构体,用来存放触摸屏的中断线,x坐标,y坐标,压力值。
用ts_st构造了该触摸屏的设备结构体。
我们还是直接看点实际的东西,上代码:
ts.h
#ifndef __TS_H #define __TH_H struct ts_info_st { int irq ; int xres ; int yres ; int pressure ; }; //I2c_transfer 读写设备--->可能睡眠--->用工作队列 struct ts_st { int x ; //坐标 int y ; int irq ; //中断 int xres ; int yres ; int pressure ; //触摸力度 struct work_struct work ; //用于创建工作队列用 struct i2c_client *client ; //用于i2c struct input_dev *dev ; //用于输入子系统 }; #endifts_drv.c
#include <linux/module.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/workqueue.h> #include <linux/i2c.h> #include <linux/slab.h> #include "ts.h" struct ts_st *ts ; struct ts_info_st *pdata ; //一旦有中断,就会在串口终端打印中断号 irqreturn_t ts_handler(int irq , void *data) { printk("ts touch !\n"); printk("irq : %d\n",irq); return IRQ_HANDLED ; } //匹配成功就会调用probe函数 int ts_probe(struct i2c_client *client , const struct i2c_device_id *id) { int ret ; printk("probe\n"); //从i2c的client获取设备相关的数据保存到pdata里 pdata = client->dev.platform_data ; ts = kzalloc(sizeof(*ts),GFP_KERNEL); if(!ts){ return -ENOMEM ; } //给ts指针赋值 printk("malloc success!\n"); ts->client = client ; ts->irq = pdata->irq ; //获取中断号 ts->xres = pdata->xres ; //获取x坐标 ts->yres = pdata->yres ; //获取y坐标 ts->pressure = pdata->pressure ; //申请输入设备 ts->dev = input_allocate_device(); if(!ts->dev){ ret = -ENOMEM ; goto alloc_input_error ; } ts->dev->name = client->name ; //"myts"-->board_info_ ts->dev->phys = "xxxxx" ; ts->dev->uniq = "20170506" ; ts->dev->id.bustype = BUS_I2C ; ts->dev->id.vendor = 10010; ts->dev->id.product = 10111 ; ts->dev->id.version = 1 ; //以上赋值是表示注册设备以后的信息,可以通过cat /proc/bus/input查看 //注册input事件 ret = input_register_device(ts->dev); if(ret){ goto register_input_error ; } //注册中断-->中断下降沿 ret = request_irq(ts->irq , ts_handler , IRQF_TRIGGER_FALLING , client->name , ts) ; if(ret){ goto irq_erro ; } printk("register irq success!\n"); return ret ; register_input_error: input_free_device(ts->dev); alloc_input_error: kfree(ts); irq_erro : input_unregister_device(ts->dev); free_irq(ts->irq,&ts); kfree(ts); } //i2c的id_table struct i2c_device_id id_table[] = { {"myts",123}, {"herts","456"}, {}, }; //实现i2c驱动操作结构体 struct i2c_driver ts_drv = { .probe = ts_probe , .driver = { .name = "myts", }, .id_table = id_table , }; static int __init hello_init(void) { printk("Hello, kenerl installed for YYXXXXXXXXXX20170423!\n"); int ret ; ret = i2c_add_driver(&ts_drv); //添加一个i2c驱动 if(ret != 0){ printk("i2c add driver fair!\n"); i2c_del_driver(&ts_drv); //删除一个i2c驱动 return -1 ; } printk("i2c_add driver success!\n"); return 0; } static void __exit hello_cleanup(void) { printk("Good-bye, removed!\n"); i2c_del_driver(&ts_drv); } module_init(hello_init); module_exit(hello_cleanup); MODULE_LICENSE("GPL");
编写Makefile,这里我们在外面写一个用驱动就可以加载的模块:
obj-m += ts_drv.o ROOTFS = . KERNEL_SRC = /work/android-5.0.2/kernel all: make -C $(KERNEL_SRC) M=`pwd` modules clean: make -C $(KERNEL_SRC) M=`pwd` clean rm -rf app install: make -C $(KERNEL_SRC) M=`pwd` modules_install INSTALL_MOD_PATH=$(ROOTFS) app: arm-linux-gcc app.c -o app写完驱动后,在当前目录下执行make:
然后,我们通过adb push命令,将该ko放入android根文件系统的system目录下。
如果你的系统的分区没有设置成可读可写,还需要在当前目录下执行:adb remount就可以了。
接下来,执行adb push ts_drv.ko /system
然后切换到我的minicom终端,此时Android系统已经启动,我切换到system目录下,执行insmod ts_drv.ko后,然后手点击触摸屏,就会有驱动信息弹出来,做这个步骤之前,要把友善提供的内核里的写好的触摸屏驱动给卸载掉,否则不会成功,请注意。
我们看到,触摸屏的时候,中断服务程序就会被调用,打印出中断号。
下一节,我们将进一步完善这个触摸屏驱动程序。