作者:JCY
开始将触摸屏驱动吧!在Linux的内核中触摸屏驱动是基于输入子系统来设计的。现在先说一下输入子系统。原来编写的字符设备驱动模块中会定义一文件操作接口变量,通过该变量中的函数指针成员来操作相应的硬件。你也许会发现他们的程序框架是一样的。各种输入设备也能够使用字符设备驱动框架,来编写驱动,但是各种输入设备所输入的属性基本上一样的。例如键盘是一个输入设备,他只有一个按键事件和此按键事件的键值;还有触摸屏输入设备,它也只有两个操作属性,触摸屏按下事件和按下处的位置;鼠标也是一个输入设备,与上述操作属性差不多。你会发现输入设备的所处理的很多是一样的,因为各个输入设备的操作硬件部分差别很大,而操作属性确基本相同,那么就将操作硬件部分独立放在设备驱动层,对于事件核心层和万能事件处理层是有Linux系统统一提供,我们不需要对事件核心层和事件处理层做任何的修改,所以在编写基于基于输入子系统的输入设备驱动程序中我们只需要编写设备驱动层即可。
输入子系统分为三层:设备驱动层、事件核心层、事件处理层。
现在来说说这三个层主要做什么事情,具体怎么做稍后分析
第一、设备驱动层:主要完成输入设备初始化操作,例如寄存器的操作等等。将硬件对用户的输入访问转换为标准的输入事件。
第二、事件核心层:此部分是输入子系统的核心部分,负责初始化和管理整个输入子系统。它对输入设备提供输入设备的初始化和注册操作,也向事件处理层提供接口函数以使事件处理层到核心层注册。以及建立事件处理层和设备驱动层之间的联系。
第三、事件处理层:为用户空间的应用程序提供统一的接口和驱动层提交的事件处理,所以在编写驱动时不必再实现文件操作的函数。Linux内核对所有的基于输入子系统的设备提供了相似的统一接口,这为用户空间的操作提供了方便。
既然在编写输入子系统时我们只需要关心对输入设备的操作,那么我们只需要编写底层的设备驱动层了。好开始吧!
对于触摸屏的设备驱动层的实现文件是在s3c2410_ts.c源码文件中实现的。
首先当加入模块的时候第一个执行的程序就是模块初始化函数,那么我们就从模块初始化函数分析。进入初始化函数后执行顺序为
1. 获得ADC转换的时钟,然后使能ADC时钟。
2. 对寄存器物理地址重映射到虚拟地址。内核就是通过虚拟地址来访问物理地址的。
3. 初始化触摸屏的连接,其实就是初始化与触摸屏连接的那四个引脚为触摸屏操作
4. 然后再初始化ADC控制器相关的寄存器。
5. 建立一个输入设备,并通过把input_dev赋值给dev。然后通过dev来对刚建立的输入设备初始化。
6. 注册两个中断,是ADC中断和触摸屏中断。
7. 通过事件核心层提供的input_register_device函数来注册输入设备
到此就模块初始化函数就结束了。是不是很简单呀。具体代码复制如下:
static struct clk *adc_clock;
static int __init s3c2410ts_init(void)
{
struct input_dev *input_dev;
adc_clock = clk_get(NULL, "adc");
if (!adc_clock) {
printk(KERN_ERR "failed to get adc clock source\n");
return -ENOENT;
}
clk_enable(adc_clock);
base_addr=ioremap(S3C2410_PA_ADC,0x20);
if (base_addr == NULL) {
printk(KERN_ERR "Failed to remap register block\n");
return -ENOMEM;
}
/* Configure GPIOs */
s3c2410_ts_connect();
iowrite32(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF),\
base_addr+S3C2410_ADCCON);
iowrite32(0xffff, base_addr+S3C2410_ADCDLY);
iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
/* Initialise input stuff */
input_dev = input_allocate_device();
if (!input_dev) {
printk(KERN_ERR "Unable to allocate the input device !!\n");
return -ENOMEM;
}
dev = input_dev;
dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
dev->keybit[BITS_TO_LONGS(BTN_TOUCH)] = BIT(BTN_TOUCH);
input_set_abs_params(dev, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(dev, ABS_Y, 0, 0x3FF, 0, 0);
input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0);
dev->name = s3c2410ts_name;
dev->id.bustype = BUS_RS232;
dev->id.vendor = 0xDEAD;
dev->id.product = 0xBEEF;
dev->id.version = S3C2410TSVERSION;
/* Get irqs */
if (request_irq(IRQ_ADC, stylus_action, IRQF_SAMPLE_RANDOM,
"s3c2410_action", dev)) {
printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_ADC !\n");
iounmap(base_addr);
return -EIO;
}
if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,
"s3c2410_action", dev)) {
printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !\n");
iounmap(base_addr);
return -EIO;
}
printk(KERN_INFO "%s successfully loaded\n", s3c2410ts_name);
/* All went ok, so register to the input system */
input_register_device(dev);
return 0;
}
触摸屏的初始化就结束了
再来看一下笔针按下和抬起中断服务处理程序。在程序中会通过ADCDATA0或者ADCDATA1的高位相应为来判断是否有按键按下或者抬起。若是按下就睡执行touch_timer_fire函数在此函数中按下会启动ADC转换,这样在ADC采集四次后就会结束,延时一段时间,处于笔针抬起中断状态。若笔针抬起就会调用事件核心层提供的报告事件函数,向事件处理层报告按键事件和绝对位移