Linux中触摸屏驱动的达成:基于S3C6410处理器
发布时间:2021-11-23 15:50:37 所属栏目:教程 来源:互联网
导读:这几篇文章主要是关于linux中触摸屏驱动的,基于s3c6410处理器进行分析。这一篇主要是关于触摸屏设备作为平台设备的实现,还有对应的probe函数和remove函数的源码分析。 1、触摸屏模块的加载和卸载函数 static char banner[] __initdata = KERN_INFO S3C Tou
这几篇文章主要是关于linux中触摸屏驱动的,基于s3c6410处理器进行分析。这一篇主要是关于触摸屏设备作为平台设备的实现,还有对应的probe函数和remove函数的源码分析。 1、触摸屏模块的加载和卸载函数 static char banner[] __initdata = KERN_INFO "S3C Touchscreen driver, (c) 2008 Samsung Electronicsn"; static int __init s3c_ts_init(void) { printk(banner); return platform_driver_register(&s3c_ts_driver); } static void __exit s3c_ts_exit(void) { platform_driver_unregister(&s3c_ts_driver); } module_init(s3c_ts_init); module_exit(s3c_ts_exit); 万变不离其宗,还是熟悉的那个他,只不过每一次都是一番新的历程。 对应的平台设备资源:在Dev-ts.c (linux2.6.28archarmplat-s3c)文件中 /* Touch srcreen */ static struct resource s3c_ts_resource[] = { [0] = { .start = S3C_PA_ADC, I/O端口 .end = S3C_PA_ADC + SZ_4K - 1, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_PENDN, 中断 .end = IRQ_PENDN, .flags = IORESOURCE_IRQ, }, [2] = { .start = IRQ_ADC, 中断 .end = IRQ_ADC, .flags = IORESOURCE_IRQ, } }; struct platform_device s3c_device_ts = { .name = "s3c-ts", .id = -1, .num_resources = ARRAY_SIZE(s3c_ts_resource), .resource = s3c_ts_resource, }; 对应的platform_driver结构体的定义如下: static struct platform_driver s3c_ts_driver = { .probe = s3c_ts_probe, .remove = s3c_ts_remove, .suspend = s3c_ts_suspend, .resume = s3c_ts_resume, .driver = { .owner = THIS_MODULE, .name = "s3c-ts", }, }; 2、我想应该知道要做什么了,接着来看probe函数,源码如下: /* * The functions for inserting/removing us as a module. */ static int __init s3c_ts_probe(struct platform_device *pdev) { struct resource *res; struct device *dev; struct input_dev *input_dev; struct s3c_ts_mach_info * s3c_ts_cfg; int ret, size; dev = &pdev->dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(dev,"no memory resource specifiedn"); return -ENOENT; } 得到寄存器操作地址 size = (res->end - res->start) + 1; ts_mem = request_mem_region(res->start, size, pdev->name); 申请这片内存区域 注:static struct resource*ts_mem; if (ts_mem == NULL) { dev_err(dev, "failed to get memory regionn"); ret = -ENOENT; goto err_req; } ts_base = ioremap(res->start, size); 进行映射 if (ts_base == NULL) { dev_err(dev, "failed to ioremap() regionn"); ret = -EINVAL; goto err_map; } ts_clock = clk_get(&pdev->dev, "adc"); 得到时钟 if (IS_ERR(ts_clock)) { dev_err(dev, "failed to find watchdog clock sourcen"); ret = PTR_ERR(ts_clock); goto err_clk; } clk_enable(ts_clock); 使能时钟 s3c_ts_cfg = s3c_ts_get_platdata(&pdev->dev); static struct s3c_ts_mach_info *s3c_ts_get_platdata (struct device *dev) { if (dev->platform_data != NULL) return (struct s3c_ts_mach_info *)dev->platform_data; return &s3c_ts_default_cfg; } 默认值: /* Touchscreen default configuration */ struct s3c_ts_mach_info s3c_ts_default_cfg __initdata = { .delay = 5000,//10000, .presc = 49, .oversampling_shift = 4,//2, .resol_bit = 10 }; 这里涉及到一个结构体s3c_ts_mach_info struct s3c_ts_mach_info { int delay; 延时时间 int presc; 预分频值 int oversampling_shift; 转化次数 int resol_bit; 分频率 enum s3c_adc_types3c_adc_con;看下面: }; 其中有 enum s3c_adc_type { ADC_TYPE_0, ADC_TYPE_1, /* S3C2416, S3C2450 */ ADC_TYPE_2,/* S3C64XX, S5PC1XX */ }; if ((s3c_ts_cfg->presc&0xff) > 0) 设置预分频值 writel(S3C_ADCCON_PRSCEN | S3C_ADCCON_PRSCVL(s3c_ts_cfg->presc&0xFF), ts_base+S3C_ADCCON); else writel(0, ts_base+S3C_ADCCON);没有定义的话,写0,其实也就是禁止预分频 这里主要和ADCCON寄存器的设置有关,而且有如下定义: #define S3C_ADCCON_PRSCEN(1<<14) #define S3C_ADCCON_PRSCVL(x)(((x)&0xFF)<<6) 看下图: /* Initialise registers */ if ((s3c_ts_cfg->delay&0xffff) > 0) writel(s3c_ts_cfg->delay & 0xffff, ts_base+S3C_ADCDLY); 和上面差不多,主要和ADCDLY寄存器有关。直接看图:注:在两种模式下有不同的含义 if (s3c_ts_cfg->resol_bit==12) { 分频率 switch(s3c_ts_cfg->s3c_adc_con) { case ADC_TYPE_2: writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT, ts_base+S3C_ADCCON); break; #define S3C_ADCCON_RESSEL_12BIT(0x1<<16) case ADC_TYPE_1: writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT_1, ts_base+S3C_ADCCON); break; default: dev_err(dev, "Touchscreen over this type of AP isn't supported !n"); break; } } writel(WAIT4INT(0), ts_base+S3C_ADCTSC);主要是对ADCTSC寄存器进行操作,使触摸屏处于等待中断模式 ts = kzalloc(sizeof(struct s3c_ts_info), GFP_KERNEL); 注:static struct s3c_ts_info*ts; input_dev = input_allocate_device();申请并初始化一个输入设备。通过输入设备,驱动程序才能和用户交互。 注:struct input_dev *input_dev; if (!input_dev) { ret = -ENOMEM; goto err_alloc; } ts->dev = input_dev; ts->dev->evbit[0] = ts->dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); ts->dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); if (s3c_ts_cfg->resol_bit==12) { input_set_abs_params(ts->dev, ABS_X, 0, 0xFFF, 0, 0); input_set_abs_params(ts->dev, ABS_Y, 0, 0xFFF, 0, 0); } else { input_set_abs_params(ts->dev, ABS_X, 0, 0x3FF, 0, 0); input_set_abs_params(ts->dev, ABS_Y, 0, 0x3FF, 0, 0); } input_set_abs_params(ts->dev, ABS_PRESSURE, 0, 1, 0, 0); sprintf(ts->phys, "input(ts)"); ts->dev->name = s3c_ts_name; ts->dev->phys = ts->phys; ts->dev->id.bustype = BUS_RS232; ts->dev->id.vendor = 0xDEAD; ts->dev->id.product = 0xBEEF; ts->dev->id.version = S3C_TSVERSION; ts->shift = s3c_ts_cfg->oversampling_shift; ts->resol_bit = s3c_ts_cfg->resol_bit; ts->s3c_adc_con = s3c_ts_cfg->s3c_adc_con; 上面这一段代码都是初始化触摸屏设备的全局量ts,对应的结构体原型是: struct s3c_ts_info { struct input_dev *dev; long xp; long yp; int count; int shift; char phys[32]; int resol_bit; enum s3c_adc_types3c_adc_con; }; /* For IRQ_PENDUP */ ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 得到触摸屏中断IRQ_PENDUP if (ts_irq == NULL) { dev_err(dev, "no irq resource specifiedn"); ret = -ENOENT; goto err_irq; } ret = request_irq(ts_irq->start, stylus_updown, IRQF_SAMPLE_RANDOM, "s3c_updown", ts);申请触摸屏中断,对应的中断处理函数是stylus_updown if (ret != 0) { dev_err(dev,"s3c_ts.c: Could not allocate ts IRQ_PENDN !n"); ret = -EIO; goto err_irq; } /* For IRQ_ADC */ ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1); 得到ADC中断 if (ts_irq == NULL) { dev_err(dev, "no irq resource specifiedn"); ret = -ENOENT; goto err_irq; } ret = request_irq(ts_irq->start, stylus_action, IRQF_SAMPLE_RANDOM, "s3c_action", ts);申请ADC中断,对应的中断函数是stylus_action if (ret != 0) { dev_err(dev, "s3c_ts.c: Could not allocate ts IRQ_ADC !n"); ret = -EIO; goto err_irq; } printk(KERN_INFO "%s got loaded successfully : %d bitsn", s3c_ts_name, s3c_ts_cfg->resol_bit); /* All went ok, so register to the input system */ 将触摸屏设备注册到输入子系统中 ret = input_register_device(ts->dev); if(ret) { dev_err(dev, "s3c_ts.c: Could not register input device(touchscreen)!n"); ret = -EIO; goto fail; } return 0; 下面这些是错误处理代码 fail: free_irq(ts_irq->start, ts->dev); free_irq(ts_irq->end, ts->dev); err_irq: input_free_device(input_dev); kfree(ts); err_alloc: clk_disable(ts_clock); clk_put(ts_clock); err_clk: iounmap(ts_base); err_map: release_resource(ts_mem); kfree(ts_mem); err_req: return ret; } 到这里,触摸屏设备驱动的probe函数就讲述完了。 3、当然,probe函数中几个重要的函数都没讲,就是关于输入子系统的,那不是我们现在关注的重点。接着看对应的remove函数,源码如下: static int s3c_ts_remove(struct platform_device *dev) { printk(KERN_INFO "s3c_ts_remove() of TS called !n"); disable_irq(IRQ_ADC); disable_irq(IRQ_PENDN); free_irq(IRQ_PENDN, ts->dev); free_irq(IRQ_ADC, ts->dev); if (ts_clock) { clk_disable(ts_clock); clk_put(ts_clock); ts_clock = NULL; } input_unregister_device(ts->dev); iounmap(ts_base); return 0; } 其实看懂了probe函数,remove函数就完全不用看了。 (编辑:东莞站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |