一、输入子系统情景回忆ING......

在Linux中,输入子系统是由输入子系统设备驱动层、输入子系统核心层(Input Core)和输入子系统事件处理层(Event Handler)组成。其中设备驱动层提供对硬件各寄存器的读写访问和将底层硬件对用户输入访问的响应转换为标准的输入事件,再通过核心层提交给事件处理层;而核心层对下提供了设备驱动层的编程接口,对上又提供了事件处理层的编程接口;而事件处理层就为我们用户空间的应用程序提供了统一访问设备的接口和驱动层提交来的事件处理。所以这使得我们输入设备的驱动部分不在用关心对设备文件的操作,而是要关心对各硬件寄存器的操作和提交的输入事件。下面用图形来描述一下这三者的关系吧!

二、S3C2440触摸屏接口预热ING......

S3C2440提供的触摸屏接口有4种处理模式,分别是:正常转换模式、单独的X/Y位置转换模式、自动X/Y位置转换模式

和等待中断模式,对于在每种模式下工作的要求,请详细查看数据手册的描述。本驱动实例将采用自动X/Y位置转换

模式和等待中断模式。

三、tq2440_ts.c源码分析

1.入口函数,为了方便分析,清晰架构,这里省去了返回值的判断,最详细的还请参考后面的源码。

[cpp] view
plain
?
  1. static int __init tq2440ts_init(void)
  2. {
  3. struct input_dev *input_dev;
  4. /* 获取ADC时钟,使能时钟(CLKCON[15])
  5. * ADC(&Touch Screen) [15], Control PCLK into ADC block.
  6. */
  7. adc_clock = clk_get(NULL, "adc");
  8. clk_enable(adc_clock);
  9. /* ADCCON寄存器地址:0x58000000 */
  10. base_addr=ioremap(S3C2410_PA_ADC,0x20);
  11. /* 使能预分频,预分频系数PRSCVL为0xff */
  12. iowrite32(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF),base_addr+S3C2410_ADCCON);
  13. /* ADCDLY = 0xffff */
  14. iowrite32(0xffff,  base_addr+S3C2410_ADCDLY);
  15. /* 进入等待按下中断模式 */
  16. iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
  17. /* 分配一个input_dev结构体 */
  18. input_dev = input_allocate_device();
  19. /* 初始化输入设备,即input_dev成员 */
  20. dev = input_dev;
  21. /* 支持同步事件、按键事件、绝对位移事件 */
  22. dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
  23. /* 支持按键类中的触摸屏点击 */
  24. dev->keybit[BITS_TO_LONGS(BTN_TOUCH)] = BIT(BTN_TOUCH);
  25. /* 设置触摸屏的X坐标、Y坐标、压力 */
  26. input_set_abs_params(dev, ABS_X, 0, 0x3FF, 0, 0);  /* Resolution: 10-bit,0x3ff即10位 */
  27. input_set_abs_params(dev, ABS_Y, 0, 0x3FF, 0, 0);
  28. input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0);
  29. /* 下面这些信息在驱动挂载后在/proc/bus/input/devices中看到 */
  30. dev->name = tq2440ts_name;
  31. dev->id.bustype = BUS_RS232;
  32. dev->id.vendor = 0xDEAD;
  33. dev->id.product = 0xBEEF;
  34. dev->id.version = S3C2410TSVERSION;
  35. /* 分别申请ADC、TC中断,在ADC中断里使用了IRQF_SHARED
  36. * 共享中断标志,因为在ADC驱动里也使用了ADC中断
  37. */
  38. request_irq(IRQ_ADC, stylus_action, IRQF_SHARED|IRQF_SAMPLE_RANDOM, tq2440ts_name, dev)
  39. request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM, tq2440ts_name, dev)
  40. printk(KERN_INFO "%s successfully loaded\n", tq2440ts_name);
  41. /* 将触摸屏输入设备注册到输入子系统 */
  42. input_register_device(dev);
  43. return 0;
  44. }

驱动加载函数tq2440ts_init主要做了以下一些事情:获取ADC时钟,使能ADC时钟,映射ADC的IO地址,使能预分频、设ADCDLY寄存器,进入等待按下中断模式,分配一个input_dev结构体,初始化input_dev结构体成员,如:支持哪类事件、支持这类事件的哪些事件,申请ADC、TC中断,最后注册一个触摸屏输入设备。万事具备,只欠东风。加载函数准备好一切条件后,就当你触摸触摸屏了,当触摸屏被触摸后,即被按下后,会进入触摸屏中断处理函数stylus_updown

[cpp] view
plain
?
  1. /* 触摸屏中断服务程序,当触摸屏按下或抬起时触发执行 */
  2. static irqreturn_t stylus_updown(int irq, void *dev_id)
  3. {
  4. unsigned long data0;
  5. unsigned long data1;
  6. /* 用于判断触摸屏是按下还是抬起 */
  7. int updown;
  8. /* ADC资源可以获取,即上锁 */
  9. if (down_trylock(&ADC_LOCK) == 0)
  10. {
  11. /* 标识触摸屏资源可用 */
  12. OwnADC = 1;
  13. /* 读取AD转换后的值,注意这次重点是读出状态 */
  14. data0 = ioread32(base_addr+S3C2410_ADCDAT0);
  15. data1 = ioread32(base_addr+S3C2410_ADCDAT1);
  16. updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
  17. if (updown)
  18. {
  19. /*  ADCDAT0[15] = 0,则updown = 1,此时表示按下状态
  20. *  那么调用touch_timer_fire函数启动ADC转换
  21. */
  22. touch_timer_fire(0);
  23. }
  24. else
  25. {
  26. /* 如果是抬起状态,就结束这次的操作,并释放ADC资源的占用,即解锁 */
  27. OwnADC = 0;
  28. up(&ADC_LOCK);
  29. }
  30. }
  31. return IRQ_HANDLED;
  32. }

stylus_updown函数首先获得ADC资源,因为在ADC驱动里也有可能使用了ADC资源,然后获得触摸屏状态,判断触摸屏是被按下还是被抬起,如果是被按下,那么调用touch_timer_fire函数启动ADC转换;如果是抬起状态,就结束这次的操作,并释放ADC资源的占用。

touch_timer_fire函数分析

[cpp] view
plain
?
  1. static void touch_timer_fire(unsigned long data)
  2. {
  3. unsigned long data0;
  4. unsigned long data1;
  5. int updown;
  6. /* 读取AD转换后的值,注意这次重点是读出状态 */
  7. data0 = ioread32(base_addr+S3C2410_ADCDAT0);
  8. data1 = ioread32(base_addr+S3C2410_ADCDAT1);
  9. updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
  10. if (updown) {
  11. /* 如果是按下状态,并且ADC已经转换,就报告事件和数据 */
  12. if (count != 0)
  13. {
  14. long tmp;
  15. tmp = xp;
  16. xp = yp;
  17. yp = tmp;
  18. /* stylus_action函数是4次采样,所以要除以4 */
  19. xp >>= 2;
  20. yp >>= 2;
  21. /* 上报X/Y坐标数据 */
  22. input_report_abs(dev, ABS_X, xp);
  23. input_report_abs(dev, ABS_Y, yp);
  24. /* 报告按键事件,键值为1表示触摸点被按下 */
  25. input_report_key(dev, BTN_TOUCH, 1);
  26. /* 报告触摸屏状态,键值为1表示触摸屏被按下 */
  27. input_report_abs(dev, ABS_PRESSURE, 1);
  28. /* 报告完后,上报同步事件 */
  29. input_sync(dev);
  30. }
  31. /* 如果是按下状态,但ADC还没开始转换,就启动ADC转换 */
  32. xp = 0;
  33. yp = 0;
  34. count = 0;
  35. /* 设置触摸屏模式为自动X/Y位置转换模式 */
  36. iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
  37. /* 启动ADC转换,转换完成后进入stylus_action(ADC中断处理函数) */
  38. iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
  39. }
  40. else
  41. {
  42. /* 如果是抬起状态 */
  43. count = 0;
  44. /* 报告按键事件,键值为0表示触摸点被释放 */
  45. input_report_key(dev, BTN_TOUCH, 0);
  46. /* 报告触摸屏绝对位移事件,键值为0表示触摸屏没有被按下 */
  47. input_report_abs(dev, ABS_PRESSURE, 0);
  48. /* 报告完后,上报同步事件 */
  49. input_sync(dev);
  50. /* 将触摸屏重设置为等待按下中断模式 */
  51. iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
  52. /* 如果触摸屏抬起,意味着这一次的操作结束,所以应该释放ADC资源的占用 */
  53. if (OwnADC)
  54. {
  55. OwnADC = 0;
  56. up(&ADC_LOCK);
  57. }
  58. }
  59. }

touch_timer_fire函数首先读出触摸屏状态,如果是按下状态,并且ADC已经转换,就报告事件和数据;如果是按下状态,但ADC还没开始转换,就启动ADC转换;如果是抬起状态,报告事件后,将触摸屏重设置为等待按下中断模式;如果触摸屏抬起,意味着这一次的操作结束,所以应该释放ADC资源的占用。

当ADC转换完成后触发ADC中断,就会进入ADC中断处理函数stylus_action

[cpp] view
plain
?
  1. /* ADC中断处理程序,AD转换完成后触发执行 */
  2. static irqreturn_t stylus_action(int irq, void *dev_id)
  3. {
  4. unsigned long data0;
  5. unsigned long data1;
  6. /* 标识触摸屏资源可用 */
  7. if (OwnADC)
  8. {
  9. /* 读取AD转换后的值,注意这次重点读X/Y坐标数据 */
  10. data0 = ioread32(base_addr+S3C2410_ADCDAT0);
  11. data1 = ioread32(base_addr+S3C2410_ADCDAT1);
  12. /* 取出bit[9:0],即X/Y坐标数据 */
  13. xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
  14. yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
  15. /* 记录这一次AD转换次数 */
  16. count++;
  17. if (count < (1<<2))
  18. {
  19. /* 如果转换次数小于4,则重启动ADC转换 */
  20. iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
  21. iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
  22. }
  23. else
  24. {
  25. /* 否则,启动1个时间滴答的定时器,这就会去执行
  26. * touch_timer_fire定时器超时函数的上报事件和数据
  27. */
  28. mod_timer(&touch_timer, jiffies+1);
  29. /* 停止ADC转换,防止屏幕抖动 */
  30. iowrite32(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
  31. }
  32. }
  33. return IRQ_HANDLED;
  34. }

在stylus_action函数里,首先读取ADC转换后的数据,判断是否连续转换够4次,如果没有则重新启动ADC转换;否则,启动1个时间滴答的定时器,这就会去执行touch_timer_fire定时器超时函数的上报事件和数据。

在本驱动的定时器的定义和初始化与以往不一样,这里使用了简易的方法

[cpp] view
plain
?
  1. /* 定义并初始化了一个touch_timer定时器,超时处理函数为touch_timer_fire */
  2. static struct timer_list touch_timer =
  3. TIMER_INITIALIZER(touch_timer_fire, 0, 0);

在这里总结一下触摸的工作流程:

(1)如果触摸屏感觉到触摸,则触发触摸屏中断即进入stylus_updown,获取ADC_LOCK后判断触摸屏状态为按下,

则调用touch_timer_fire启动ADC转换;

(2)当ADC转换启动后,触发ADC中断即进入stylus_action,如果这一次转换的次数小于4,则重新启动ADC进行转换,

如果4次完毕后,启动1个时间滴答的定时器,停止ADC转换,也就是说在这个时间滴答内,ADC转换是停止的;

(3)这里为什么要在1个时间滴答到来之前停止ADC的转换呢?这是为了防止屏幕抖动。

(4)如果1个时间滴答到来则进入定时器服务程序touch_timer_fire,判断触摸屏仍然处于按下状态则上报事件和

转换的数据,并重启ADC转换,重复第(2)步;

(5)如果触摸抬起了,则上报释放事件,并将触摸屏重新设置为等待中断状态。

触摸屏驱动参考源码

[cpp] view
plain
?
  1. #include <linux/errno.h>
  2. #include <linux/kernel.h>
  3. #include <linux/module.h>
  4. #include <linux/slab.h>
  5. #include <linux/input.h>
  6. #include <linux/init.h>
  7. #include <linux/serio.h>
  8. #include <linux/delay.h>
  9. #include <linux/platform_device.h>
  10. #include <linux/clk.h>
  11. #include <asm/io.h>
  12. #include <asm/irq.h>
  13. #include <plat/regs-adc.h>
  14. #include <mach/regs-gpio.h>
  15. /* For ts.dev.id.version */
  16. #define S3C2410TSVERSION    0x0101
  17. /* x = 0:等待按下中断模式,即ADCTSC = 0xd3
  18. * x = 1:等待松开中断模式, 即ADCTSC = 0x1d3
  19. */
  20. #define WAIT4INT(x)  (((x)<<8) | \
  21. S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
  22. S3C2410_ADCTSC_XY_PST(3))
  23. /* 自动X/Y位置转换模式 */
  24. #define AUTOPST      (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
  25. S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
  26. static char *tq2440ts_name = "TQ2440 TouchScreen";
  27. static  struct input_dev *dev;
  28. static  long xp;
  29. static  long yp;
  30. static  int count;
  31. /* 外部信号量ADC_LOCK,在ADC驱动中有定义 */
  32. extern struct semaphore ADC_LOCK;
  33. static int OwnADC = 0;
  34. static void __iomem *base_addr;
  35. /*
  36. static inline void tq2440_ts_connect(void)
  37. {
  38. s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);
  39. s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);
  40. s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);
  41. s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);
  42. }
  43. */
  44. static void touch_timer_fire(unsigned long data)
  45. {
  46. unsigned long data0;
  47. unsigned long data1;
  48. int updown;
  49. /* 读取AD转换后的值,注意这次重点是读出状态 */
  50. data0 = ioread32(base_addr+S3C2410_ADCDAT0);
  51. data1 = ioread32(base_addr+S3C2410_ADCDAT1);
  52. updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
  53. if (updown) {
  54. /* 如果是按下状态,并且ADC已经转换,就报告事件和数据 */
  55. if (count != 0)
  56. {
  57. long tmp;
  58. tmp = xp;
  59. xp = yp;
  60. yp = tmp;
  61. /* stylus_action函数是4次采样,所以要除以4 */
  62. xp >>= 2;
  63. yp >>= 2;
  64. /* 上报X/Y坐标数据 */
  65. input_report_abs(dev, ABS_X, xp);
  66. input_report_abs(dev, ABS_Y, yp);
  67. /* 报告按键事件,键值为1表示触摸点被按下 */
  68. input_report_key(dev, BTN_TOUCH, 1);
  69. /* 报告触摸屏状态,键值为1表示触摸屏被按下 */
  70. input_report_abs(dev, ABS_PRESSURE, 1);
  71. /* 报告完后,上报同步事件 */
  72. input_sync(dev);
  73. }
  74. /* 如果是按下状态,但ADC还没开始转换,就启动ADC转换 */
  75. xp = 0;
  76. yp = 0;
  77. count = 0;
  78. /* 设置触摸屏模式为自动X/Y位置转换模式 */
  79. iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
  80. /* 启动ADC转换,转换完成后进入stylus_action(ADC中断处理函数) */
  81. iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
  82. }
  83. else
  84. {
  85. /* 如果是抬起状态 */
  86. count = 0;
  87. /* 报告按键事件,键值为0表示触摸点被释放 */
  88. input_report_key(dev, BTN_TOUCH, 0);
  89. /* 报告触摸屏绝对位移事件,键值为0表示触摸屏没有被按下 */
  90. input_report_abs(dev, ABS_PRESSURE, 0);
  91. /* 报告完后,上报同步事件 */
  92. input_sync(dev);
  93. /* 将触摸屏重设置为等待按下中断模式 */
  94. iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
  95. /* 如果触摸屏抬起,意味着这一次的操作结束,所以应该释放ADC资源的占用 */
  96. if (OwnADC)
  97. {
  98. OwnADC = 0;
  99. up(&ADC_LOCK);
  100. }
  101. }
  102. }
  103. /* 定义并初始化了一个touch_timer定时器,超时处理函数为touch_timer_fire */
  104. static struct timer_list touch_timer =
  105. TIMER_INITIALIZER(touch_timer_fire, 0, 0);
  106. /* 触摸屏中断服务程序,当触摸屏按下或抬起时触发执行 */
  107. static irqreturn_t stylus_updown(int irq, void *dev_id)
  108. {
  109. unsigned long data0;
  110. unsigned long data1;
  111. /* 用于判断触摸屏是按下还是抬起 */
  112. int updown;
  113. /* ADC资源可以获取,即上锁 */
  114. if (down_trylock(&ADC_LOCK) == 0)
  115. {
  116. /* 标识触摸屏资源可用 */
  117. OwnADC = 1;
  118. /* 读取AD转换后的值,注意这次重点是读出状态 */
  119. data0 = ioread32(base_addr+S3C2410_ADCDAT0);
  120. data1 = ioread32(base_addr+S3C2410_ADCDAT1);
  121. updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
  122. if (updown)
  123. {
  124. /*  ADCDAT0[15] = 0,则updown = 1,此时表示按下状态
  125. *  那么调用touch_timer_fire函数启动ADC转换
  126. */
  127. touch_timer_fire(0);
  128. }
  129. else
  130. {
  131. /* 如果是抬起状态,就结束这次的操作,并释放ADC资源的占用,即解锁 */
  132. OwnADC = 0;
  133. up(&ADC_LOCK);
  134. }
  135. }
  136. return IRQ_HANDLED;
  137. }
  138. /* ADC中断处理程序,AD转换完成后触发执行 */
  139. static irqreturn_t stylus_action(int irq, void *dev_id)
  140. {
  141. unsigned long data0;
  142. unsigned long data1;
  143. /* 标识触摸屏资源可用 */
  144. if (OwnADC)
  145. {
  146. /* 读取AD转换后的值,注意这次重点读X/Y坐标数据 */
  147. data0 = ioread32(base_addr+S3C2410_ADCDAT0);
  148. data1 = ioread32(base_addr+S3C2410_ADCDAT1);
  149. /* 取出bit[9:0],即X/Y坐标数据 */
  150. xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
  151. yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
  152. /* 记录这一次AD转换次数 */
  153. count++;
  154. if (count < (1<<2))
  155. {
  156. /* 如果转换次数小于4,则重启动ADC转换 */
  157. iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
  158. iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
  159. }
  160. else
  161. {
  162. /* 否则,启动1个时间滴答的定时器,这就会去执行
  163. * touch_timer_fire定时器超时函数的上报事件和数据
  164. */
  165. mod_timer(&touch_timer, jiffies+1);
  166. /* 停止ADC转换,防止屏幕抖动 */
  167. iowrite32(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
  168. }
  169. }
  170. return IRQ_HANDLED;
  171. }
  172. static struct clk   *adc_clock;
  173. static int __init tq2440ts_init(void)
  174. {
  175. struct input_dev *input_dev;
  176. /* 获取ADC时钟,使能时钟(CLKCON[15])
  177. * ADC(&Touch Screen) [15], Control PCLK into ADC block.
  178. */
  179. adc_clock = clk_get(NULL, "adc");
  180. if (!adc_clock)
  181. {
  182. printk(KERN_ERR "failed to get adc clock source\n");
  183. return -ENOENT;
  184. }
  185. clk_enable(adc_clock);
  186. /* ADCCON寄存器地址:0x58000000 */
  187. base_addr=ioremap(S3C2410_PA_ADC,0x20);
  188. if (base_addr == NULL)
  189. {
  190. printk(KERN_ERR "Failed to remap register block\n");
  191. return -ENOMEM;
  192. }
  193. /* Configure GPIOs */
  194. //  tq2440_ts_connect();
  195. /* 使能预分频,预分频系数PRSCVL为0xff */
  196. iowrite32(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF),base_addr+S3C2410_ADCCON);
  197. /* ADCDLY = 0xffff */
  198. iowrite32(0xffff,  base_addr+S3C2410_ADCDLY);
  199. /* 进入等待按下中断模式 */
  200. iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
  201. /* 分配一个input_dev结构体 */
  202. input_dev = input_allocate_device();
  203. if (!input_dev)
  204. {
  205. printk(KERN_ERR "Unable to allocate the input device !!\n");
  206. return -ENOMEM;
  207. }
  208. /* 初始化输入设备,即input_dev成员 */
  209. dev = input_dev;
  210. /* 支持同步事件、按键事件、绝对位移事件 */
  211. dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
  212. /* 支持按键类中的触摸屏点击 */
  213. dev->keybit[BITS_TO_LONGS(BTN_TOUCH)] = BIT(BTN_TOUCH);
  214. /* 设置触摸屏的X坐标、Y坐标、压力 */
  215. input_set_abs_params(dev, ABS_X, 0, 0x3FF, 0, 0);  /* Resolution: 10-bit,0x3ff即10位 */
  216. input_set_abs_params(dev, ABS_Y, 0, 0x3FF, 0, 0);
  217. input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0);
  218. /* 下面这些信息在驱动挂载后在/proc/bus/input/devices中看到 */
  219. dev->name = tq2440ts_name;
  220. dev->id.bustype = BUS_RS232;
  221. dev->id.vendor = 0xDEAD;
  222. dev->id.product = 0xBEEF;
  223. dev->id.version = S3C2410TSVERSION;
  224. /* 分别申请ADC、TC中断,在ADC中断里使用了IRQF_SHARED
  225. * 共享中断标志,因为在ADC驱动里也使用了ADC中断
  226. */
  227. if (request_irq(IRQ_ADC, stylus_action, IRQF_SHARED|IRQF_SAMPLE_RANDOM, tq2440ts_name, dev))
  228. {
  229. printk(KERN_ERR "tq2440_ts.c: Could not allocate ts IRQ_ADC !\n");
  230. iounmap(base_addr);
  231. return -EIO;
  232. }
  233. if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM, tq2440ts_name, dev))
  234. {
  235. printk(KERN_ERR "tq2440_ts.c: Could not allocate ts IRQ_ADC !\n");
  236. iounmap(base_addr);
  237. return -EIO;
  238. }
  239. printk(KERN_INFO "%s successfully loaded\n", tq2440ts_name);
  240. /* 将触摸屏输入设备注册到输入子系统 */
  241. input_register_device(dev);
  242. return 0;
  243. }
  244. static void __exit tq2440ts_exit(void)
  245. {
  246. disable_irq(IRQ_ADC);
  247. disable_irq(IRQ_TC);
  248. free_irq(IRQ_TC,dev);
  249. free_irq(IRQ_ADC,dev);
  250. if (adc_clock)
  251. {
  252. clk_disable(adc_clock);
  253. clk_put(adc_clock);
  254. adc_clock = NULL;
  255. }
  256. input_unregister_device(dev);
  257. iounmap(base_addr);
  258. }
  259. module_init(tq2440ts_init);
  260. module_exit(tq2440ts_exit);

测试,使用tslib库来测试,关于如何测试请参考韦东山视频的“第16课第3节 触摸屏驱动程序之使用TSLIB测试_P”,在这里再说显得有点多余。

2014-01-20补充

[cpp] view
plain
?
  1. [WJ2440]# cat proc/bus/input/devices
  2. I: Bus=0013 Vendor=dead Product=beef Version=0101
  3. N: Name="TQ2440 TouchScreen"
  4. P: Phys=
  5. S: Sysfs=/devices/virtual/input/input0
  6. U: Uniq=
  7. H: Handlers=event0
  8. B: EV=b
  9. B: KEY=0
  10. B: ABS=1000003

与源码里的信息是一致的

[cpp] view
plain
?
  1. dev->name = tq2440ts_name;
  2. dev->id.bustype = BUS_RS232;
  3. dev->id.vendor = 0xDEAD;
  4. dev->id.product = 0xBEEF;
  5. dev->id.version = S3C2410TSVERSION;

linux 输入子系统之电阻式触摸屏驱动的更多相关文章

  1. Linux输入子系统框架分析(1)

    在Linux下的输入设备键盘.触摸屏.鼠标等都能够用输入子系统来实现驱动.输入子系统分为三层,核心层和设备驱动层.事件层.核心层和事件层由Linux输入子系统本身实现,设备驱动层由我们实现.我们在设备 ...

  2. Linux输入子系统(转)

    Linux输入子系统(Input Subsystem) 1.1.input子系统概述 输入设备(如按键,键盘,触摸屏,鼠标等)是典型的字符设备,其一般的工作机制是低层在按键,触摸等动作发生时产生一个中 ...

  3. Linux输入子系统详解

    input输入子系统框架  linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(Input ...

  4. linux输入子系统简述【转】

    本文转载自:http://blog.csdn.net/xubin341719/article/details/7678035 1,linux输入子系统简述 其实驱动这部分大多还是转载别人的,linux ...

  5. 7.Linux 输入子系统分析

    为什么要引入输入子系统? 在前面我们写了一些简单的字符设备的驱动程序,我们是怎么样打开一个设备并操作的呢? 一般都是在执行应用程序时,open一个特定的设备文件,如:/dev/buttons .... ...

  6. linux输入子系统(input subsystem)之evdev.c事件处理过程

    1.代码 input_subsys.drv.c 在linux输入子系统(input subsystem)之按键输入和LED控制的基础上有小改动,input_subsys_test.c不变. input ...

  7. linux输入子系统

    linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(InputCore)和输入子系统设备驱 ...

  8. linux输入子系统概念介绍

    在此文章之前,我们讲解的都是简单的字符驱动,涉及的内容有字符驱动的框架.自动创建设备节点.linux中断.poll机制.异步通知.同步互斥.非阻塞.定时器去抖动. 上一节文章链接:http://blo ...

  9. Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值

    Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值 题外话:一个问题研究到最后,那边记录文档的前半部分基本上都是没用的,甚至是错误的. 重点在最后,前边不过一些假想猜測. ht ...

随机推荐

  1. Django Celery Redis 异步执行任务demo实例

    一.windows中安装redis 安装过程见 <在windows x64上部署使用Redis> 二.环境准备 requirements.txt Django==1.10.5 celery ...

  2. Win10/Server2016镜像集成离线补丁

    Win10镜像集成离线补丁 因为正常安装系统后再打补丁比较漫长,可以事先做好打过补丁的iso,备将来使用. 以管理员身份运行cmd,然后通过dism提取.挂载.集成补丁.保存install.wim镜像 ...

  3. Java实现数组去除重复数据的方法详解

    一.用List集合实现 int[] str = {5, 6, 6, 6, 8, 8, 7,4}; List<Integer> list = new ArrayList<Integer ...

  4. 转载 IOS开发之---static变量

    Objective-C 支持全局变量 主要有两种实现方式: (1)第一种和C/C++中的一样, 使用"extern"关键词: (2)另外一种就是使用单例实现. (比如我们经常会把一 ...

  5. day5-shelve模块

    一.概述 前面章节我们讲述了json和pickle模块的序列化和反序列化处理,他们有一个不足是在python 3中不能多次dump和load,shelve模块则可以规避这个问题.shelve模块是一个 ...

  6. hibernate13--缓存

    <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN&q ...

  7. New Concept English three (45)

    31w/m 65error In democratic countries any efforts to restrict the freedom of the press are rightly c ...

  8. android安装apk

     * 安装apk */ private void installApk() { // 获取当前sdcard存储路径 File apkfile = new File(Environment.getE ...

  9. C++使用初始化列表的方式来初始化字段

    几个月之前,接触Android recovery源代码的时候,看ScreenRecoveryUI类的时候,那时候C++基础还不是特别好,一直不明白以下的初始化方式: 下面这个是Recovery的一个构 ...

  10. 解决PKIX问题:unable to find valid certification path to requested target

    第一步:执行方式:java InstallCert hostname                       eg:java InstallCert www.cebbank.com 第二步:然后输 ...