这一节,我们将从零开始写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 ; //用于输入子系统
};
#endif
ts_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后,然后手点击触摸屏,就会有驱动信息弹出来,做这个步骤之前,要把友善提供的内核里的写好的触摸屏驱动给卸载掉,否则不会成功,请注意。
我们看到,触摸屏的时候,中断服务程序就会被调用,打印出中断号。
下一节,我们将进一步完善这个触摸屏驱动程序。