现在的位置: 首页 > 综合 > 正文

linux input输入子系统分析《三》:S3C2440的触摸屏驱动实例

2018年04月16日 ⁄ 综合 ⁄ 共 14711字 ⁄ 字号 评论关闭

主要讲述本人在学习Linux内核input子系统的全部过程,如有分析不当,多谢指正。以下方式均可联系,文章欢迎转载,保留联系信息,以便交流。

邮箱:eabi010@gmail.com

主页:www.ielife.cn(爱嵌论坛——嵌入式技术学习交流)

博客:blog.csdn.net/ielife

1.1    本节阅读前提

本节的说明建立在前两节的基础之上,需要先阅读如下两篇章:

linux input输入子系统分析《一》:初识input输入子系统

linux input输入子系统分析《二》:s3c2440ADC简单驱动实例分析

1.2    触摸屏工作原理

S3C2440的触摸屏接口是4线电阻式触摸屏接口,可以控制x、y方向上的引脚(XP、XM、YP、YM)的变换,S3C2440触摸屏的硬件资源包括触摸屏引脚和ADC转换接口,可以使用寄存器组中的ADCTSC寄存器来操作触摸屏引脚资源,ADCCON寄存器来控制AD转换功能,由linux
input
输入子系统分析《二》:s3c2440ADC简单驱动实例分析
中介绍的触摸屏接口工作模式为4种,这里我们只是用x/y方向自动转换模式和等待中断模式。自动转换模式用于转换x方向和y方向的值到ADCDAT0和ADCDAT1中,等待中断模式用于检测触摸屏的按下和抬起,一般使用上升沿和下降沿触发获得触摸屏事件。

触摸屏使用引脚的4个引脚XP、XM、YP、YM分别对应S3C2440芯片的AIN7、AIN6、AIN5、AIN4模拟输入源,其中按照linux input输入子系统分析《二》:s3c2440ADC简单驱动实例分析中的图4和图5可以看出AIN7接XP,AIN5接YP,由此可得x和y坐标的模拟信号由AIN5和AIN7引脚通过ADC转换器产生,产生的数据保存在ADCDAT0和ADCDAT1中。

1.3    驱动程序组成结构

分析代码前,有必要了解驱动程序的组成结构。

s3c2440ts_init()完成的功能:

使能adc的PCLK时钟源

映射操作触摸屏寄存器的地址

初始化寄存器

初始化输入设备

填充输入子系统设备结构体input_dev

申请中断IRQ_TS和IRQ_ADC

注册输入设备到输入子系统中

s3c2440ts_exit()完成的功能:

注销使用的系统资源

stylus_updown()完成的功能:

中断处理程序,完成对触摸屏按下和释放的判断

启动ADC转换

stylus_action()完成的功能:

ADC转换程序

上报事件

touch_timer_fire()完成的功能:

ADC的子功能

1.4    代码分析

代码如下:

  1. /* 
  2.  * s3c2440 触摸屏驱动程序 
  3.  * 
  4.  * Kevin Lee <www.ielife.cn> 
  5.  */  
  6.    
  7. #include<linux/kernel.h> /* 提供prink等内核特有属性 */  
  8. #include<linux/module.h> /* 提供模块及符号接口*/  
  9. #include<linux/init.h> /* 设置段,如_init、_exit,设置初始化优先级,如__initcall */  
  10. #include<linux/wait.h> /* 等待队列wait_queue */  
  11. #include<linux/interrupt.h> /* 中断方式,如IRQF_SHARED */  
  12. #include<linux/fs.h> /* file_operations操作接口等 */  
  13. #include<linux/clk.h> /* 时钟控制接口,如struct clk */  
  14. #include<linux/miscdevice.h> /* 杂项设备 */  
  15. #include<asm/io.h> /* 提供readl、writel */  
  16. #include<linux/irq.h> /* 提供中断相关宏 */  
  17. #include<asm/irq.h> /* 提供中断号,中断类型等,如IRQ_ADC中断号 */  
  18. #include<asm/arch/regs-adc.h> /* 提供控制器的寄存器操作,如S3C2410_ADCCON */  
  19. #include<asm/uaccess.h> /* 提供copy_to_user等存储接口 */  
  20. #include<linux/input.h> /* 内核输入子系统操作接口 */  
  21. #include<linux/slab.h> /* kzalloc内存分配函数 */  
  22. #include<linux/time.h> /* do_gettimeofday时间函数 */  
  23. #include<linux/timer.h> /* timer定时器 */  
  24.    
  25. /* 用于代码的调试 */  
  26. #define CONFIG_S3C2440_TOUCHSCREEN__DEBUG 1  
  27.    
  28. /* 用于处理位操作 */  
  29. #define BITS_PER_LONG           32  
  30. #define BIT_MASK(nr)         (1UL << ((nr) %BITS_PER_LONG))  
  31. #define BIT_WORD(nr)        ((nr) / BITS_PER_LONG)  
  32.    
  33. /* 定义一个宏WAIT4INT,用于对ADCTSC触摸屏控制寄存器进行操作, 
  34.  * S3C2410_ADCTSC_YM_SEN等在内核include/asm/arch/regs-adc.h中被定义 
  35.  */  
  36. #define WAIT4INT(x)    (((x)<<8) |S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | \  
  37.                     S3C2410_ADCTSC_XP_SEN |S3C2410_ADCTSC_XY_PST(3))  
  38.    
  39. /* 定义一个宏AUTOPST,用于设置ADCTSC触摸屏控制寄存器为自动转换模式 */  
  40. #define AUTOPST    (S3C2410_ADCTSC_YM_SEN |S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \  
  41.                 S3C2410_ADCTSC_AUTO_PST |S3C2410_ADCTSC_XY_PST(0))  
  42.    
  43. /* 触摸屏数据结构体 */  
  44. struct ts_event {  
  45.        short pressure;              /* 是否按下 */  
  46.        short xp;        /*触摸屏x坐标值 */  
  47.        short yp;        /*触摸屏y坐标值 */  
  48. };  
  49.    
  50. /* 触摸屏设备结构体 */  
  51. struct s3c2440_ts {  
  52.        struct input_dev *input;/* 输入子系统设备结构体 */  
  53.        struct timer_list timer;/* 定时器,用于ADC转换操作 */  
  54.        struct ts_event tc;  /* ADC转换的值的保存位置,也用于上报input子系统的数据 */  
  55.        int pendown;          /* 判断是否有按下 */  
  56.        int count;        /* 用于驱动去抖的计数 */  
  57.        int shift;         /* 用于驱动去抖的基数 */  
  58. };  
  59.    
  60. /* 定义触摸屏设备结构体 */  
  61. static struct s3c2440_ts *s3c2440_ts;  
  62.    
  63. /* 定义虚拟地址访问硬件寄存器,__iomem只是用于表示指针将指向I/O内存 */  
  64. static void __iomem *base_addr;  
  65.    
  66. /* 定义adc时钟,通过adc_clock接口获得adc输入时钟,adc转换器需要 */  
  67. static struct clk *adc_clock;  
  68.    
  69. /* 申明外部定义的信号量,adc.c中定义,处理IRQ_ADC共享中断引起的资源互斥 */  
  70. extern struct semaphore adc_lock;  
  71. //DECLARE_MUTEX(adc_lock);  
  72.    
  73. /* 处理触摸屏的数据及事件上报,属于中断调用的函数,不能睡眠 */  
  74. static void touch_timer_fire(unsigned long data)  
  75. {  
  76.     /* 保存ADCDAT0及ADCDAT1的x,y坐标值 */  
  77.     unsigned long data0;  
  78.     unsigned long data1;  
  79.      
  80.     /* 禁止中断,处理完数据再打开中断 */  
  81.     set_irq_type(IRQ_TC, IRQT_NOEDGE);  
  82.     set_irq_type(IRQ_ADC, IRQT_NOEDGE);  
  83.    
  84.     /* 读取ADCDAT0和ADCDAT1寄存器,提取ADC转换的x,y坐标值 */  
  85.     data0 = readl(base_addr + S3C2410_ADCDAT0);  
  86.     data1 = readl(base_addr + S3C2410_ADCDAT1);  
  87.    
  88.     /* 判断ADCDAT0和ADCDAT1中的[15]位,[15]位为等待中断模式下用于判断笔尖是否有抬起或落下,0=落下 */  
  89.     s3c2440_ts->pendown = (!(data0 &S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));  
  90.    
  91.     /* 当触摸屏处于被按下状态,执行下面代码 */  
  92.     if (s3c2440_ts->pendown) {  
  93.            /*count不为0,说明正在转换,xp和yp往右移动shift(2)位是为了去抖 
  94.             * 需要结合stylus_action中断函数来看,当count=4时,才能认为转换结束, 
  95.             * 即最后所得的xp和yp是被计算4次的,最终上报时需要除以4,因此这里预先 
  96.             * 右移2位,相当于乘以4 
  97.             */  
  98.            if(s3c2440_ts->count != 0) {  
  99.                   s3c2440_ts->tc.xp>>= s3c2440_ts->shift;  
  100.                   s3c2440_ts->tc.yp>>= s3c2440_ts->shift;  
  101.    
  102. /* 终端打印调试信息 */  
  103. #ifdef CONFIG_S3C2440_TOUCHSCREEN__DEBUG  
  104.             {  
  105.                    struct timeval tv;  
  106.                    do_gettimeofday(&tv);  
  107.                    printk(KERN_INFO"T: %06d, X: %03x, Y: %03x\n", (int)tv.tv_usec, s3c2440_ts->tc.xp,s3c2440_ts->tc.yp);  
  108.             }  
  109. #endif  
  110.               /*报告X、Y的绝对坐标值*/  
  111.               input_report_abs(s3c2440_ts->input,ABS_X, s3c2440_ts->tc.xp);  
  112.               input_report_abs(s3c2440_ts->input,ABS_Y, s3c2440_ts->tc.yp);  
  113.                
  114.               /*报告触摸屏的状态,1表明触摸屏被按下*/  
  115.               input_report_abs(s3c2440_ts->input,ABS_PRESSURE, s3c2440_ts->tc.pressure);  
  116.                
  117.               /*报告按键事件,键值为1(代表触摸屏对应的按键被按下)*/  
  118.               input_report_key(s3c2440_ts->input,BTN_TOUCH, s3c2440_ts->pendown);  
  119.                
  120.               /*等待接收方受到数据后回复确认,用于同步*/  
  121.               input_sync(s3c2440_ts->input);  
  122.        }  
  123.    
  124.         /* count=0执行这里面的代码,ADC还没有开始转换 */  
  125.          s3c2440_ts->tc.xp = 0;  
  126.          s3c2440_ts->tc.yp = 0;  
  127.          s3c2440_ts->count = 0;  
  128.          s3c2440_ts->tc.pressure = 0;  
  129.    
  130.         /* 因为触摸屏是按下状态,ADC还没有转换,需要启动ADC开始转换 
  131.          * 本句代码是设置触摸屏为自动转换模式 
  132.          */  
  133.          writel(S3C2410_ADCTSC_PULL_UP_DISABLE| AUTOPST, base_addr + S3C2410_ADCTSC);  
  134.    
  135.         /* 启动ADC转换 */  
  136.          writel(readl(base_addr +S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr + S3C2410_ADCCON);  
  137.      } else {  
  138.         /* 执行到这里说明按键没有被按下或按键抬起,count清0 */  
  139.         s3c2440_ts->count = 0;  
  140.    
  141.         /* 报告按键事件,给0值说明按键抬起 */  
  142.         input_report_key(s3c2440_ts->input,BTN_TOUCH, 0);  
  143.    
  144.         /* 报告按键事件,给0值说明按键抬起 */  
  145.         input_report_abs(s3c2440_ts->input,ABS_PRESSURE, 0);  
  146.    
  147.         /* 用于同步事件处理层同步上报的按键和触摸事件 */  
  148.         input_sync(s3c2440_ts->input);  
  149.    
  150.         /* 将触摸屏重新设置为等待中断状态,等待触摸屏被按下 */  
  151.         writel(WAIT4INT(0), base_addr +S3C2410_ADCTSC);  
  152.          
  153.         /* 触摸屏抬起了,可以释放信号量了 */  
  154.         up(&adc_lock);  
  155.      }  
  156.       
  157.      /* 使能中断触发条件,IRQT_BOTHEDGE为使能上升沿和下降沿触发 */  
  158.      set_irq_type(IRQ_TC, IRQT_BOTHEDGE);  
  159.      set_irq_type(IRQ_ADC, IRQT_BOTHEDGE);  
  160. }  
  161.    
  162. /* 触摸屏中断服务程序,触摸屏按下或抬起时触发执行 */  
  163. static irqreturn_t stylus_updown(int irq, void *dev_id)  
  164. {  
  165.     /* 用于记录ADC转换后的值 */  
  166.     unsigned long data0;  
  167.     unsigned long data1;  
  168.    
  169.     /* 由于是中断程序,所以不能使用down和down_interruptible,会导致睡眠 */  
  170.     if (down_trylock(&adc_lock) == 0)  
  171.     {  
  172.         /* 读取ADCDAT0和ADCDAT1,用于判断触摸屏是否被按下 */  
  173.         data0 = readl(base_addr +S3C2410_ADCDAT0);  
  174.         data1 = readl(base_addr +S3C2410_ADCDAT1);  
  175.    
  176.         /* 判断按键是否被按下 */  
  177.         s3c2440_ts->pendown = (!(data0 &S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));  
  178.    
  179.         /* 按键已经按下 */  
  180.         if (s3c2440_ts->pendown)  
  181.             /* 启动ADC开始转换数据 */  
  182.             touch_timer_fire(0);  
  183.     }  
  184.    
  185.     return IRQ_RETVAL(IRQ_HANDLED);  
  186. }  
  187.    
  188. /*ADC中断服务程序,ADC启动后被执行 */  
  189. static irqreturn_t stylus_action(int irq, void *dev_id)  
  190. {  
  191.     /* 同上函数 */  
  192.     unsigned long data0;  
  193.     unsigned long data1;  
  194.    
  195. #ifdef CONFIG_S3C2440_TOUCHSCREEN__DEBUG  
  196.        printk(KERN_ERR "%s() No.%dline:\n\r",__FUNCTION__,__LINE__);  
  197. #endif  
  198.    
  199.     /* 获取触摸屏的x,y坐标值 */  
  200.     data0 = readl(base_addr + S3C2410_ADCDAT0);  
  201.     data1 = readl(base_addr + S3C2410_ADCDAT1);  
  202.      
  203.     /* 既然按键按下了,执行到此ADC转换也开始了,应该取得x,y坐标值了 
  204.      * x=ADCDAT0[9:0],y=ADCDAT1[9:0],count++,presssure设置为1 
  205.      */  
  206.     s3c2440_ts->tc.xp += data0 &S3C2410_ADCDAT0_XPDATA_MASK;  
  207.     s3c2440_ts->tc.yp += data1 &S3C2410_ADCDAT1_YPDATA_MASK;  
  208.     s3c2440_ts->count++;  
  209.     s3c2440_ts->tc.pressure = 1;  
  210.      
  211.     /* 如果count小于4,需要重启设置自动转换模式,并进行ADC转换,用于去抖 */  
  212.     if (s3c2440_ts->count <(1<<s3c2440_ts->shift)) {  
  213.            writel(S3C2410_ADCTSC_PULL_UP_DISABLE| AUTOPST, base_addr + S3C2410_ADCTSC);  
  214.            writel(readl(base_addr+ S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr + S3C2410_ADCCON);  
  215.     } else {  
  216.            /*到这里说明count=4,启动定时器去执行touch_timer_fire函数上报按键和触摸事件*/  
  217.            mod_timer(&s3c2440_ts->timer,jiffies + HZ / 100);  
  218.            /*检测触摸屏抬起的中断信号*/  
  219.            writel(WAIT4INT(1),base_addr + S3C2410_ADCTSC);  
  220.     }  
  221.    
  222.     return IRQ_HANDLED;  
  223. }  
  224.    
  225. /* 初始化ADC控制寄存器和ADC触摸屏控制寄存器 */  
  226. static void adc_init(void)  
  227. {  
  228.     /* S3C2410_ADCCON_PRSCEN设置ADCCON的位[14]=1为使能A/D预分频器 
  229.      * S3C2410_ADCCON_PRSCVL设置ADCCON的位[13:6]=32表示设置的分频值, 
  230.      * ADC的转换频率需要在2.5MHZ以下,我们使用的ADC输入时钟为PCLK=50MHZ,50MHZ/(49+1)=1MHZ,满足条件 
  231.      */  
  232.     writel(S3C2410_ADCCON_PRSCEN |S3C2410_ADCCON_PRSCVL(49), base_addr + S3C2410_ADCCON);  
  233.    
  234.     /* 初始化ADC启动或延时寄存器,ADC转换启动延时值设置为0xffff */  
  235.     writel(0xffff, base_addr + S3C2410_ADCDLY);  
  236.    
  237.     /* 初始化ADC触摸屏控制寄存器ADCTSC, 
  238.      * WAIT4INT(0)在上面定义,引脚YM、YP、XM、XP的使能位位于ADCTSC的[7:4]位, 
  239.      * 设置触摸屏的工作状态在[1:0]位,WAIT4INT(0)=11010011, 
  240.      * 1101代表笔尖按下时发生触摸屏中断信号IRQ_TS给CPU,0011表示XP上拉使能, 
  241.      * 使用正常ADC转换,转换的方式为等待中断模式 
  242.      * 原理图见linux input输入子系统分析《二》:s3c2440的ADC简单驱动实例分析 
  243.      */  
  244.     writel(WAIT4INT(0), base_addr +S3C2410_ADCTSC);  
  245. }  
  246.    
  247. /*s3c2440触摸屏驱动模块加载程序,做了以下工作 
  248.  * 获得时钟源、设置访问触摸屏控制器的虚拟地址并初始化触摸屏控制器、初始化中断定时器、 
  249.  * 填充input_dev结构体(设备基本信息及事件信息)、注册中断、注册设备到input子系统 
  250.  */  
  251. static int __init s3c2440ts_init(void)  
  252. {  
  253.     struct input_dev *input_dev;  
  254.     int err = -ENOMEM;  
  255.      
  256.     s3c2440_ts = kzalloc(sizeof(structs3c2440_ts), GFP_KERNEL);  
  257.     /*给输入设备申请空间,input_allocate_device定义在input.h中*/  
  258.     input_dev = input_allocate_device();  
  259.     if (!s3c2440_ts || !input_dev)  
  260.            gotofail1;  
  261.    
  262.     /* 获得adc的时钟源,通过arch/arm/mach-s3c2410/clock.c获得提供的时钟源为PCLK */  
  263.     adc_clock = clk_get(NULL, "adc");  
  264.     if (!adc_clock)  
  265.     {  
  266.         printk(KERN_ERR "failed to get adcclock source\n");  
  267.         return -ENOENT;  
  268.     }  
  269.    
  270.     /* 在时钟控制器中给adc提供输入时钟,ADC转换需要输入时钟 */  
  271.     clk_enable(adc_clock);  
  272.    
  273.     /* 使用ioremap获得操作ADC控制器的虚拟地址 
  274.      * S3C2410_PA_ADC=ADCCON,是ADC控制器的基地址,寄存器组的长度=0x1c 
  275.      */  
  276.     base_addr = ioremap(S3C2410_PA_ADC, 0x1c);  
  277.     if (base_addr == NULL)  
  278.     {  
  279.         printk(KERN_ERR "Failed to remapregister block\n");  
  280.         return -ENOMEM;  
  281.         goto fail1;  
  282.     }  
  283.    
  284.     /*初始化ADC控制寄存器和ADC触摸屏控制寄存器*/  
  285.     adc_init();  
  286.      
  287.     /* 初始化定时器 */  
  288.     init_timer(&s3c2440_ts->timer);  
  289.     s3c2440_ts->timer.data = 1;  
  290.     s3c2440_ts->timer.function =touch_timer_fire;  
  291.         
  292.     /* 设置触摸屏输入设备的标志,注册输入设备成功进入根文件系统,可以cat /proc/bus/input/devices查看其内容*/  
  293.     input_dev->name          = "s3c2410Touchscreen";  /* 设备名称 */  
  294.     input_dev->phys         ="s3c2440ts/input0"/*  */  
  295.     input_dev->id.bustype    = BUS_HOST;                    /*总线类型 */     
  296.     input_dev->id.vendor     = 0x1;                /*经销商ID */  
  297.     input_dev->id.product    = 0x2;                /*产品ID */  
  298.     input_dev->id.version    = 0x0100;                   /*版本ID */  
  299.      
  300.     s3c2440_ts->shift = 2;  
  301.      
  302.     /*下面初始化输入设备,即给输入设备结构体input_dev的成员设置值。 
  303.     evbit字段用于描述支持的事件,这里支持同步事件、按键事件、绝对坐标事件, 
  304.     BIT宏实际就是对1进行位操作,定义在linux/bitops.h中*/  
  305.     input_dev->evbit[0] = BIT_MASK(EV_SYN) |BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);  
  306.      
  307.     /*keybit字段用于描述按键的类型,在input.h中定义了很多,这里用BTN_TOUCH类型来表示触摸屏的点击*/  
  308.     input_dev->keybit[BIT_WORD(BTN_TOUCH)] =BIT_MASK(BTN_TOUCH);  
  309.    
  310.     /*对于触摸屏来说,使用的是绝对坐标系统。这里设置该坐标系统中X和Y坐标的最小值和最大值(0-1023范围) 
  311.     ABS_X和ABS_Y就表示X坐标和Y坐标,ABS_PRESSURE就表示触摸屏是按下还是抬起状态*/  
  312.     input_set_abs_params(input_dev, ABS_X, 0,0x3FF, 0, 0);  
  313.     input_set_abs_params(input_dev, ABS_Y, 0,0x3FF, 0, 0);  
  314.     input_set_abs_params(input_dev,ABS_PRESSURE, 0, 1, 0, 0);  
  315.    
  316.     /* 申请ADC中断,AD转换完成后触发。这里使用共享中断IRQF_SHARED是因为该中断号在ADC驱动中也使用了, 
  317.     最后一个参数1是随便给的一个值,因为如果不给值设为NULL的话,中断就申请不成功*/  
  318.     if(request_irq(IRQ_ADC, stylus_action,IRQF_SHARED | IRQF_SAMPLE_RANDOM, input_dev->name, s3c2440_ts))  
  319.     {  
  320.         printk(KERN_ERR "s3c2440_ts.c:Could not allocate ts IRQ_ADC !\n");  
  321.        err = -EBUSY;  
  322.        goto fail2;  
  323.     }  
  324.    
  325.     /*申请触摸屏中断,对触摸屏按下或提笔时触发*/  
  326.     if(request_irq(IRQ_TC, stylus_updown,IRQF_SAMPLE_RANDOM, input_dev->name, s3c2440_ts))  
  327.     {  
  328.         printk(KERN_ERR "s3c2440_ts.c:Could not allocate ts IRQ_TC !\n");  
  329.        err = -EBUSY;  
  330.        goto fail2;  
  331.     }  
  332.    
  333.     /* 初始化完毕,注册输入子系统 */  
  334.     s3c2440_ts->input = input_dev;  
  335.     err =input_register_device(s3c2440_ts->input);  
  336.     if(err)  
  337.            gotofail3;  
  338.      
  339.     /* 设置中断触发条件,IRQT_BOTHEDGE为使能上升沿和下降沿触发 */  
  340.     set_irq_type(IRQ_TC, IRQT_BOTHEDGE);  
  341.     set_irq_type(IRQ_ADC, IRQT_BOTHEDGE);  
  342.      
  343.     return 0;  
  344.    
  345. fail3:  
  346.        free_irq(IRQ_TC, (void *)s3c2440_ts);  
  347.        free_irq(IRQ_ADC, (void *)s3c2440_ts);  
  348. fail2:  
  349.        iounmap(base_addr);  
  350. fail1:  
  351.        input_free_device(input_dev);  
  352.        kfree(s3c2440_ts);  
  353.    
  354.     return err;  
  355. }  
  356.    
  357. static void __exit s3c2440ts_exit(void)  
  358. {  
  359.     /* 屏蔽并释放中断 */  
  360.     disable_irq(IRQ_TC);  
  361.     disable_irq(IRQ_ADC);  
  362.     free_irq(IRQ_TC, (void *)s3c2440_ts);  
  363.     free_irq(IRQ_ADC, (void *)s3c2440_ts);  
  364.      
  365.     /* 注销定时器 */  
  366.     del_timer_sync(&s3c2440_ts->timer);  
  367.    
  368.     /* 屏蔽和禁止adc时钟 */  
  369.     if(adc_clock)  
  370.     {  
  371.         clk_disable(adc_clock);  
  372.         clk_put(adc_clock);  
  373.         adc_clock = NULL;  
  374.     }  
  375.    
  376.     /* 注销触摸屏输入子系统 */  
  377.    input_unregister_device(s3c2440_ts->input);  
  378.      
  379.     /* 释放虚拟地址 */  
  380.     iounmap(base_addr);  
  381.      
  382.     /* 释放触摸屏设备结构体 */  
  383.     kfree(s3c2440_ts);  
  384. }  
  385.    
  386. module_init(s3c2440ts_init);  
  387. module_exit(s3c2440ts_exit);  
  388.    
  389. MODULE_AUTHOR("KevinLee <www.ielife.cn>");  
  390. MODULE_DESCRIPTION("S3c2440TouchScreen Device Driver");  
  391. MODULE_VERSION("S3C2440TOUCHSCREEN 1.0");  
  392. MODULE_LICENSE("GPL");  

1.5    添加Makefile及编译模块

Mkaefile脚本如下:

  1. MODULENAME:= s3c2440_ts.o  
  2.    
  3. ifneq($(KERNELRELEASE),)  
  4. #call from kernel build system  
  5. obj-m      := $(MODULENAME)  
  6.    
  7. else  
  8. #KERNELDIR?= /lib/modules/$(shell uname -r)/build  
  9. KERNELDIR?= /work/system/linux-2.6.22.6  
  10. PWD       := $(shell pwd)  
  11. default:  
  12.        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules  
  13. endif  
  14.    
  15. clean:  
  16.        rm -rf *.o *~ core .depend .*.cmd *.ko*.mod.c .tmp_versions module* Module* $(APPNAME)  
  17.    
  18. depend.depend dep:  
  19.        $(CC) $(CFLAGS) -M *.c > .depend  
  20.    
  21. ifeq(.depend,$(wildcard .depend))  
  22. include.depend  
  23. endif  

直接执行make,获得s3c2440_ts.ko文件,insmod进入内核,点击触摸屏可以看到驱动中打印的信息。

insmod驱动模块s3c2440_ts.ko之后,还可以通过cat /proc/bus/input/devices来查看输入设备在输入子系统中的信息:

I:Bus=0019 Vendor=0001 Product=0002 Version=0100

N:Name="s3c2410 Touchscreen"

P:Phys=s3c2440ts/input0

S:Sysfs=/class/input/input0

U:Uniq=

H:Handlers=mouse0 event0 evbug

B:EV=b

B:KEY=400 0 0 0 0 0 0 0 0 0 0

B:ABS=1000003

抱歉!评论已关闭.