一、输入子系统情景回忆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. Angular表达式--插值字符串($interpolate)

    要在字符串模板中做插值操作,需要在你的对象中注入$interpolate服务.在下面的例子中,我们将会将它注入到一个控制器中: angular.module('myApp', []) .control ...

  2. 四十 Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)倒排索引

    倒排索引 倒排索引源于实际应用中需要根据属性的值来查找记录.这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址.由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引 ...

  3. day5-随机数相关:random模块&string模块

    一.概述 随机数在程序设计中的属于比较基础的内容,主要用于验证场景(如验证码,生成账号对应的密码等),今天结合random模块和string模块来谈谈python中随机数那些事儿. 二.随机数实现相关 ...

  4. Oracle归档模式与非归档模式设置

    (转自:http://www.cnblogs.com/spatial/archive/2009/08/01/1536429.html) Oracle的日志归档模式可以有效的防止instance和dis ...

  5. SQL Server中的联合主键、聚集索引、非聚集索引

    我们都知道在一个表中当需要2列以上才能确定记录的唯一性的时候,就需要用到联合主键,当建立联合主键以后,在查询数据的时候性能就会有很大的提升,不过并不是对联合主键的任何列单独查询的时候性能都会提升,但我 ...

  6. LeetCode OJ:Reverse Linked List (反转链表)

    Reverse a singly linked list. 做II之前应该先来做1的,这个倒是很简单,基本上不用考虑什么,简单的链表反转而已: /** * Definition for singly- ...

  7. android 删除SD卡或者手机的缓存图片和目录

    public static final String TEMP_PHOTO_FILE_NAME = "temp_photo.jpg"; private static String ...

  8. 类Flask实现前后端交互之代码聊天室

    前言 框架 项目目录及各自功能 流程图 后端 server backend exector 前端 ajax 页面更新 演示 简易应答模式 代理模式处理外部请求 后台日志 总结 前言 这两天老是做梦,全 ...

  9. Android快速开发-选项卡

    介绍 几行代码实现Android选项卡界面,支持标准底部Tab自定义视图选项卡,头部文字选项卡. 底部自定义视图选项卡 先来看看实现下图中的效果我们代码应该怎么写? 实现上图效果只需以下代码: pub ...

  10. VSCode安装jshint插件报错

    Mac电脑上使用VSCode安装jshint插件时提示如下错误: Failed to load jshint library. Please install jshint in your worksp ...