您的位置:首页 > 运维架构 > Linux

linux子系统分析及触摸屏驱动浅析

2011-07-11 16:11 411 查看
前段时间负责项目的触摸屏和光电鼠标,都是输入设备,看了会这方面的资料,结合项目代码,做点总结,基本上来自个人理解和互联网 在linux2.6以后,linux对输入设备进行了抽象,抽象出了输入子系统,该系统(Input子系统)是所有I/O设备驱动的中间层,为上层提供了一个统一的界面,将事件的上报和处理分离开,采用了分层模式,在我们的driver中,我们只需要关注事件的上报,其他的都由linux自己处理。在上层系统中,它不需要知道底层有多少键盘,鼠标,轨迹球,触摸屏等设备,只需要把上报上来的input事件做相应的处理就行了。

一.Input输入子系统的框架

Input子系统将整个将输入驱动分成三块: driver,input core和Event handler. 一个输入事件,如鼠标移动,键盘按键按下,joystick的移动,触摸屏的点击滑动等等通过 Driver -> InputCore -> Eventhandler -> userspace 的顺序到达用户空间传给应用程序


而在我们的平时的驱动开发中,往往需要做的只是input driver这一层,其他的基本上都由linux做好了,不需要任何改动,除非需要加入新的功能支持之类的(如想把多点触摸的支持添加进来等)

二.一个简单使用input子系统的例子

在内核自带的文档Documentation/input/input-programming.txt中。有一个使用input子系统的例子,并附带相应的说明。初学者可以以此例学习 view plain#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/irq.h>
#include <asm/io.h>
static struct input_dev *button_dev;
static irqreturn_t button_interrupt(int irq, void *dummy)
{
input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);
input_sync(button_dev);
return IRQ_HANDLED;
}
static int __init button_init(void)
{
int error;
if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {
printk(KERN_ERR "button.c: Can't allocate irq %d/n", button_irq);
return -EBUSY;
}
button_dev = input_allocate_device();
if (!button_dev) {
printk(KERN_ERR "button.c: Not enough memory/n");
error = -ENOMEM;
goto err_free_irq;
}
button_dev->evbit[0] = BIT_MASK(EV_KEY);
button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
error = input_register_device(button_dev);
if (error) {
printk(KERN_ERR "button.c: Failed to register device/n");
goto err_free_dev;
}
return 0;
err_free_dev:
input_free_device(button_dev);
err_free_irq:
free_irq(BUTTON_IRQ, button_interrupt);
return error;
}
static void __exit button_exit(void)
{
input_unregister_device(button_dev);
free_irq(BUTTON_IRQ, button_interrupt);
}
module_init(button_init);
module_exit(button_exit);

这个例子比较简单,没有具体的硬件设备,但程序里包含了最基本的input子系统驱动的注册流程。1)在初始化函数中申请了一个input device button_dev = input_allocate_device();2)注册 input_register_device(button_dev);3)还需要申请一个中断处理例程,在中断处理例程中将收到的按键信息上报给input子系统 request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)4)还有一个需要注意的就是上报数据的参数设置,告知input子系统它支持上报的事件 button_dev->evbit[0] = BIT_MASK(EV_KEY);button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); 分别用来设置设备所产生的事件以及上报的按键值。Struct iput_dev中有两个成员,一个是evbit.一个是keybit.分别用表示设备所支持的动作和按键类型。 5)input_report_key() 用于给上层上报一个按键动作 input_sync() 用来告诉上层,本次的上报事件已经完成了.

三.input core和Event handler

另外在input子系统中还有很多关于input core和Event handler等方面的介绍,这些主要是linux内核的工作,在我们driver开发中用的不多,也不是我们所关注的重点,我在这里不做多的描述,如果感兴趣的话可以去网上去找,很很多这方面的资源四.G15项目中的触摸屏驱动 驱动程序流程和上述流程基本一致,但由于涉及到了具体的硬件设备,在一些细节处理方面会不一样,也会复杂一些 Host端通过IIC总线,从芯片读出需要的数据,一般为X,Y的绝对坐标,还有数据的标志位(单点,多点,未点击)等,这些就够了。有些芯片还有手势之类的寄存器值,但在linux/android系统中一般都没有使用,在上层处理都是用坐标值的变化来算出手势的。 初始化函数如下:view plainstatic int micco_ts_probe(struct platform_device *pdev)
{
int ret;
struct proc_dir_entry *micco_ts_proc_entry;
micco_td = kzalloc(sizeof(struct micco_ts_data), GFP_KERNEL);
if (!micco_td) {
ret = -ENOMEM;
goto micco_ts_out;
}
//micco_td->pen_state = TSI_PEN_UP;
pen_state = TSI_PEN_UP;
micco_td->suspended = 0;
micco_td->use_count = 0;
mutex_init(&mutex);
/* register input device */
micco_ts_input_dev = input_allocate_device(); //申请一个input设备
if (micco_ts_input_dev == NULL) {
printk(KERN_ERR "%s: failed to allocate input dev/n",__FUNCTION__);
return -ENOMEM;
}
//填充input 结构体,这些是对input设备的一些属性描述,名称,总线等
micco_ts_input_dev->name = "micco-ts";
micco_ts_input_dev->phys = "micco-ts/input0";
micco_ts_input_dev->dev.parent = &pdev->dev;
micco_ts_input_dev->open = new_ts_open;
micco_ts_input_dev->close = new_ts_close;
micco_ts_input_dev->id.bustype = BUS_I2C;
//所支持的事件
set_bit(EV_SYN, micco_ts_input_dev->evbit);
set_bit(EV_KEY, micco_ts_input_dev->evbit);
set_bit(BTN_TOUCH, micco_ts_input_dev->keybit);
set_bit(BTN_TOUCH2, micco_ts_input_dev->keybit);
set_bit(EV_ABS, micco_ts_input_dev->evbit);
input_set_abs_params(micco_ts_input_dev, ABS_MT_POSITION_X, 0, 0x31f, 0, 0);
input_set_abs_params(micco_ts_input_dev, ABS_MT_POSITION_Y, 0, 0x1dF, 0, 0);
input_set_abs_params(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0);
//初始化一个工作队列,用于调度中断处理例程的底半部
INIT_WORK(&work, new_ts_work);
//向系统注册input设备
ret = input_register_device(micco_ts_input_dev);
if (ret) {
printk(KERN_ERR
"%s: unabled to register input device, ret = %d/n",
__FUNCTION__, ret);
return ret;
}
init_ts(); //芯片的一些初始化工作
if (gpio_request(mfp_to_gpio(MFP_CFG_PIN(TP_INT)), "TP INT")) {
gpio_free(mfp_to_gpio(MFP_CFG_PIN(TP_INT)));
printk(KERN_ERR "Request GPIO failed,"
"gpio: %d/n", TP_INT);
return 0;
}
gpio_direction_input(mfp_to_gpio(MFP_CFG_PIN(TP_INT)));
//用TP_INT脚申请一个中断,当手指触摸到屏,将产生中断,进入中断处理程序,读取数据并上报
if (request_irq(TP_IRQ_NO, new_ts_isr,IRQF_TRIGGER_FALLING, "micco-ts", NULL)) {
printk(KERN_ERR "micco_touch.c: Can't allocate irq %d/n", TP_IRQ_NO);
return -EBUSY;
}
//屏蔽中断,在open时再打开
disable_irq_nosync(TP_IRQ_NO);
//printk("disable_irq_nosync(TP_IRQ_NO)mark2/n");
enable_irq_flag = 0;
if (ret < 0)
goto pmic_cb_out;
micco_ts_proc_entry = create_proc_entry("driver/micc_ts", 0, NULL);
if (micco_ts_proc_entry) {
micco_ts_proc_entry->write_proc = micco_ts_proc_write;
}
return 0;
pmic_cb_out:
input_unregister_device(micco_ts_input_dev);
micco_ts_out:
kfree(micco_td);
return ret;
}

中断处理顶半部static irqreturn_t new_ts_isr(int irq, void *dev_data){schedule_work(&work);return IRQ_HANDLED;}只是简单的调度了工作队列,以下才是真正的中断处理(底半部)view plainstatic void new_ts_work(struct work_struct *work)
{
u16 tem_x1, tem_y1;
u16 tem_x2, tem_y2;
u8 pen_down;
int pen_up_already_reported = 0;
mutex_lock(&mutex);
pen_down=tsi2c_byte_read(SLAVE_ADDR,CAP_TP_MODE);
//printk("new_ts_work pen_down is %x/n",pen_down);
if(129==pen_down)
{
pen_state = TSI_PEN_DOWN;
cap_tp_read(&tem_x1, &tem_y1,1);
// TOUCHSCREEN_CONSOLE_PRINT_XY1;
input_report_abs(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 1);
input_report_abs(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR,1);
input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_X, tem_x1&0x3ff);
input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_Y, tem_y1&0x3ff);
input_mt_sync(micco_ts_input_dev);
input_sync(micco_ts_input_dev);
pen_up_already_reported = 0;
}
else if(130==pen_down)
{
pen_state = TSI_PEN_DOWN;
cap_tp_read(&tem_x1, &tem_y1,1);
cap_tp_read(&tem_x2, &tem_y2,2);
//TOUCHSCREEN_CONSOLE_PRINT_XY1;
//TOUCHSCREEN_CONSOLE_PRINT_XY2;
input_report_abs(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 1);
input_report_abs(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR,1);
input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_X, tem_x1&0x3ff);
input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_Y, tem_y1&0x3ff);
input_mt_sync(micco_ts_input_dev);
input_report_abs(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 100);
input_report_abs(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR,1);
input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_X, tem_x2&0x3ff);
input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_Y, tem_y2&0x3ff);
input_mt_sync(micco_ts_input_dev);
input_sync(micco_ts_input_dev);
pen_up_already_reported = 0;
}
else
{
if((pen_state != TSI_PEN_UP) && !pen_up_already_reported) {
input_report_abs(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 0);
input_report_abs(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR, 0);
input_mt_sync(micco_ts_input_dev);
input_sync(micco_ts_input_dev);
pen_up_already_reported = 1;
}
pen_state = TSI_PEN_UP;
}
mutex_unlock(&mutex);
}

五.单点触摸&多点触摸

如上面的例子所示,已经包含了多点触摸的功能,现在简单介绍一下单点触摸和多点触摸在驱动实现时的区别 要实现多点触摸,必须要有内核的支持,也就是说input子系统中,input core和Event handler层要支持多点,在linux官方2.6.30中才加入了多点支持,但现在很多2.6.29的BSP中也给多点触摸打了补丁,也是可以用的,G15项目中的就是这样 首先,在probe函数的设备初始化阶段input_set_abs_params()函数,设置方式不同单点:input_set_abs_params(micco_ts_input_dev, ABS_X, 0, 0x31f, 0, 0);input_set_abs_params(micco_ts_input_dev, ABS_Y, 0, 0x1df, 0, 0);input_set_abs_params(micco_ts_input_dev, ABS_PRESSURE, 0, 255, 0, 0);input_set_abs_params(micco_ts_input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);多点:input_set_abs_params(micco_ts_input_dev, ABS_MT_POSITION_X, 0, 0x31f, 0, 0);input_set_abs_params(micco_ts_input_dev, ABS_MT_POSITION_Y, 0, 0x1dF, 0, 0);input_set_abs_params(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); //相当于单点屏的 ABX_PRESSUREinput_set_abs_params(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0); //相当于单点屏的 ABS_TOOL_WIDTH其次,就是上报数据的方式单点上报:input_report_abs(micco_ts_input_dev,ABS_X,tem_x&0x3ff);input_report_abs(micco_ts_input_dev,ABS_Y,tem_y&0x3ff);input_report_key(micco_ts_input_dev, BTN_TOUCH, 1);input_sync(micco_ts_input_dev);即ABS_XABS_YSYN_REPORT 多点上报: input_report_abs(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 100); input_report_abs(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR,1); input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_X, tem_x1&0x3ff); input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_Y, tem_y1&0x3ff); input_mt_sync(micco_ts_input_dev); input_report_abs(micco_ts_input_dev, ABS_MT_TOUCH_MAJOR, 100); input_report_abs(micco_ts_input_dev, ABS_MT_WIDTH_MAJOR,1); input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_X, tem_x2&0x3ff); input_report_abs(micco_ts_input_dev, ABS_MT_POSITION_Y, tem_y2&0x3ff); input_mt_sync(micco_ts_input_dev); input_sync(micco_ts_input_dev); 即 for(int i;i<n;i++) { ABS_MT_POSITION_X ABS_MT_POSITION_Y SYN_MT_REPORT } SYN_REPORT 以上只是单点和多点的在驱动开发时的主要区别,当然还有其他很多方面的差异,如硬件支持等,如果对多点触摸感兴趣的话可以去仔细阅读一下linux的内核文档Documentation / input / multi-touch-protocol.txt
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: