Linux驱动之触摸屏(2)

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);

         }

}

         先分析到这里,分析的要点只要抓住两个中断产生的时间,否则很难分析。

你可能感兴趣的:(Linux驱动之触摸屏(2))