2. 触摸屏驱动分析
2.1overview
触摸屏驱动并没有阻性和容性之分,只有单点触摸和多点触摸之分。典型的触摸屏驱动都会基于输入子系统来做。这里先来分析一下s3c2410_ts.c(旧内核里是s3c_ts.c,相对来说变动很大,有兴趣可以去看看)这个驱动文件。此文件名字虽然是s3c2410,但是同样也支持s3c2440和s3c6410。
2.2 驱动分析(s3c2410_ts.c)
l 初始化模块
staticstruct platform_driver s3c_ts_driver = {
.driver = {
.name ="samsung-ts",
.owner = THIS_MODULE,
#ifdefCONFIG_PM
.pm = &s3c_ts_pmops,
#endif
},
.id_table = s3cts_driver_ids,
.probe =s3c2410ts_probe,
.remove =__devexit_p(s3c2410ts_remove),
};
staticint __init s3c2410ts_init(void)
{
returnplatform_driver_register(&s3c_ts_driver);
}
可以看出此驱动是挂接platform总线上,构造好一个platform_driver结构体,然后进行注册
l Probe函数,
static int__devinit s3c2410ts_probe(struct platform_device *pdev)
{
/* 获取时钟资源,用于ADC */
ts.clock = clk_get(dev,"adc");
/* 获取触摸屏中断资源 */
ts.irq_tc = ret =platform_get_irq(pdev, 0);
/* 获取IO内存资源 */
res = platform_get_resource(pdev,IORESOURCE_MEM, 0);
ts.io = ioremap(res->start,resource_size(res));
/*
*构造ADC客户,用来向ADC请求数据转换
* s3c24xx_ts_select:ADC选择或取消处理此client时调用
* s3c24xx_ts_conversion:ADC转换完成时调用
*/
ts.client = s3c_adc_register(pdev, s3c24xx_ts_select,
s3c24xx_ts_conversion, 1);
/* 分配一个输入设备结构体 */
input_dev = input_allocate_device();
ts.input = input_dev;
/* 设置输入设备支持EV_KEY和EV_ABS事件 */
ts.input->evbit[0] =BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
/* 设置支持按键BTN_TOUCH */
ts.input->keybit[BIT_WORD(BTN_TOUCH)]= BIT_MASK(BTN_TOUCH);
/* 设置触摸屏的范围(范围值参考数据手册),用于在处理事件时,把错误的事件过滤掉 */
input_set_abs_params(ts.input, ABS_X,0, 0x3FF, 0, 0);
input_set_abs_params(ts.input, ABS_Y,0, 0x3FF, 0, 0);
/* 设置一些属性 */
ts.input->name = "S3C24XXTouchScreen";
ts.input->id.bustype = BUS_HOST;
ts.input->id.vendor = 0xDEAD;
ts.input->id.product = 0xBEEF;
ts.input->id.version = 0x0102;
/* 设置缓冲区大小,如果oversampling_shift= 2,缓冲区的大小为1<<2 */
ts.shift = info->oversampling_shift;
ts.features =platform_get_device_id(pdev)->driver_data;
/* 申请触摸屏中断 */
ret = request_irq(ts.irq_tc, stylus_irq, 0,
"s3c2410_ts_pen", ts.input);
/* All went ok, so register to theinput system */
ret = input_register_device(ts.input);
return 0;
}
2.3 中断分析
l 触摸屏驱动的核心当属中断程序。当触摸笔点击和弹起时都会产生中断(INT_TC)
staticirqreturn_t stylus_irq(int irq, void *dev_id)
{
data0 = readl(ts.io + S3C2410_ADCDAT0); /* 获取X值 */
data1 = readl(ts.io + S3C2410_ADCDAT1); /* 获取Y值 */
down = get_down(data0, data1);
if (down) /* 判断是否按下,启动ADC转换 */
s3c_adc_start(ts.client, 0, 1 << ts.shift);
return IRQ_HANDLED;
}
l 启动ADC转换
ints3c_adc_start(struct s3c_adc_client *client,
unsigned int channel, unsigned intnr_samples)
{
spin_lock_irqsave(&adc->lock,flags);
client->channel = channel;
client->nr_samples = nr_samples;
if (client->is_ts)
adc->ts_pend = client; /* “阻塞”请求转换的client*/
else
list_add_tail(&client->pend,&adc_pending);
if (!adc->cur) /* 如果当前的请求完成,则ADC进行下一个转换请求 */
s3c_adc_try(adc); /* */
spin_unlock_irqrestore(&adc->lock,flags);
return 0;
}
因为对ADC请求转换的客户可能会很多,所以采用队列的机制来接受客户的转换请求。但是如果是触摸屏请求转换,会把请求”阻塞”在ts_pend。触摸屏请求转换的优先比其它的要高,我个人认为可能是实时性的原因
static voids3c_adc_try(struct adc_device *adc)
{
struct s3c_adc_client *next =adc->ts_pend;
if (!next &&!list_empty(&adc_pending)) {
next = list_first_entry(&adc_pending,
structs3c_adc_client, pend);
list_del(&next->pend);
} else
adc->ts_pend = NULL;
if (next) {
adc_dbg(adc, "new clientis %p\n", next);
adc->cur = next;
s3c_adc_select(adc, next); /* 选择请求转换的客户 */
s3c_adc_convert(adc); /* 开始转换 */
s3c_adc_dbgshow(adc);
}
}
l ADC转换完时会产生INT_ADC中断,s3c_adc_irq()->(client->select_cb)(client, 0),select_cb函数是触摸屏驱动中s3c_adc_register()注册的一个回调函数s3c24xx_ts_select()
static voids3c24xx_ts_select(struct s3c_adc_client *client, unsigned select)
{
if (select) { /* 选择ADC转换请求客户项 */
writel(S3C2410_ADCTSC_PULL_UP_DISABLE| AUTOPST,
ts.io + S3C2410_ADCTSC);
} else { /* */
mod_timer(&touch_timer, jiffies+1);
writel(WAIT4INT | INT_UP,ts.io + S3C2410_ADCTSC);
}
}
Touch_timer是一个定时器,超时时调用touch_timer_fire()函数来完成坐标值的上报任务,上报完事件之后都交由输入子系统来处理。
static voidtouch_timer_fire(unsigned long data)
{
data0 = readl(ts.io + S3C2410_ADCDAT0);
data1 = readl(ts.io + S3C2410_ADCDAT1);
down = get_down(data0, data1);
if (down) { /* 判断是按下还是弹起? */
if (ts.count == (1 <<ts.shift)) { /* 判断缓冲区是否填充满 */
ts.xp >>=ts.shift;
ts.yp >>=ts.shift;
/* 上报事件信息 */
input_report_abs(ts.input,ABS_X, ts.xp);
input_report_abs(ts.input,ABS_Y, ts.yp);
input_report_key(ts.input,BTN_TOUCH, 1);
input_sync(ts.input);
ts.xp = 0;
ts.yp = 0;
ts.count = 0;
}
s3c_adc_start(ts.client, 0, 1<< ts.shift);
} else {
ts.xp = 0;
ts.yp = 0;
ts.count = 0;
input_report_key(ts.input, BTN_TOUCH, 0);
input_sync(ts.input);
writel(WAIT4INT | INT_DOWN,ts.io + S3C2410_ADCTSC);
}
}
先分析到这里,分析的要点只要抓住两个中断产生的时间,否则很难分析。