驱动之路四------adc驱动(input设备)

来源:互联网 发布:java多线程框架有哪些 编辑:程序博客网 时间:2024/05/22 06:59

开发板:smdk6410

开发环境:Linux


突然想起一点,写这些驱动,内核需要配成支持抢占才行。

前面的博客已经将其它的基本知识都解释了,这里也就不过多的阐述了,咱就直接写代码吧

这次写的是adc驱动,将其做为输入设备进行使用,

先写头文件,s3c_adc.h

#ifndef __ADC_H#define __ADC_H#include <linux/device.h>#include <linux/interrupt.h>#include <linux/input.h>struct adc_info {char name[32];int user;int status;void __iomem *v;struct input_dev *dev;struct clk *clk;struct timer_list timer;int irq;irqreturn_t (*handle)(int no, void *data);};#define S3C_PA_ADC0x7e00b000#define S3C_SZ_ADCSZ_4K#define ADCCON 0x000#define ADCTSC 0x004#define ADCDLY 0x008#define ADCDAT0 0x00C#define ADCDAT1 0x010#define ADCUPDN 0x014#define ADCCLRINT 0x018#define ADCCLRINTPNDNUP0x020 #define S3C_IRQ_ADC_SIRQ_ADC#define S3C_IRQ_ADC_EIRQ_ADC#endif

头文件主要即使设备信息结构体和相关的宏定义,

现在写设备文件,

#include <linux/init.h>#include <linux/module.h>#include <linux/device.h>#include <linux/platform_device.h>#include "s3c_adc.h"void b_release(struct device *dev){printk("Device is released\n");}//资源也是两类,MEM一类,IRQ一类struct resource b_res[] = {[0] = {.start = S3C_PA_ADC,.end = S3C_PA_ADC + S3C_SZ_ADC - 1,.flags = IORESOURCE_MEM,},[1] = {.start = S3C_IRQ_ADC_S,.end = S3C_IRQ_ADC_E,.flags = IORESOURCE_IRQ,}};struct platform_device dev = {.name = "s3c-my-adc",.id = -1,.num_resources = ARRAY_SIZE(b_res),.resource = b_res,.dev = {.release = b_release,}};static __init int module_test_init(void){return platform_device_register(&dev);}static __exit void module_test_exit(void){platform_device_unregister(&dev);}module_init(module_test_init);module_exit(module_test_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Musesea");MODULE_VERSION("1.0");MODULE_DESCRIPTION("Test for module");

看过前几篇驱动的应该都直到,套路比较固定,只要将其中的很少的东西修改以下就行,就不详细说每一步的功能了,

下面写驱动文件,这才是大头,

#include <linux/init.h>#include <linux/module.h>#include <asm/uaccess.h>#include <asm/io.h>#include <linux/device.h>#include <linux/slab.h>#include <linux/platform_device.h>#include <linux/input.h>#include <linux/clk.h>#include <linux/clkdev.h>#include <linux/timer.h>#include "s3c_adc.h"void start_adc(struct adc_info *a){u32 tmp;tmp = readl(a->v + ADCCON);tmp |= 1;writel(tmp, a->v + ADCCON);}//这个函数运行在中断上下文,函数体内可不能含有可睡眠的函数void do_timer(unsigned long data){struct adc_info *a;a = (struct adc_info *)data;//中断上下文start_adc(a);mod_timer(&a->timer, jiffies + HZ / 2);}int get_adc(struct adc_info *a){u32 tmp;tmp = readl(a->v + ADCDAT0);tmp = tmp & 0xfff;return tmp;}irqreturn_t do_adc(int no, void *data){struct adc_info *a = data;int adc_val;//获取adc转换的结果adc_val = get_adc(a);input_report_abs(a->dev, ABS_X, adc_val);input_sync(a->dev);//清中断writel(1, a->v + ADCCLRINT);return IRQ_HANDLED;}void s3c_adc_exit(struct adc_info *a){printk("Driver is release.\n");}void s3c_adc_init(struct adc_info *a){u32 tmp;//tmp = readl(a->v + ADCCON);tmp = (1 << 16) | (1 << 14) | (21 << 6);writel(tmp, a->v + ADCCON);}int b_probe(struct platform_device *pdev){struct resource *a_res, *irq_res;struct adc_info *adc;int ret;//1.申请资源a_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);if(!a_res || !irq_res)return -EBUSY;//2.分配adc_infoadc = kzalloc(sizeof(struct adc_info), GFP_KERNEL);if(!adc)return -ENOMEM;//3.ioremapadc->v = ioremap(a_res->start, a_res->end - a_res->start + 1);if(!adc->v){ret = -ENOMEM;goto remap_error;}//申请设备结构体adc->dev = input_allocate_device();if(!adc->dev){ret = -ENOMEM;goto input_allocate_device_error;}adc->dev->name = pdev->name;adc->dev->uniq = "20131113";adc->dev->phys = "/dev/eventx";adc->dev->id.bustype = BUS_HOST;adc->dev->id.vendor = 110;adc->dev->id.product = 120;adc->dev->id.version = 119;//4.设置该设备要支持的事件类型set_bit(EV_SYN, adc->dev->evbit);set_bit(EV_ABS, adc->dev->evbit);//使该设备支持绝对的x事件        //写绝对事件时不要使用set_bit,使用内核给出的下列函数        input_set_abs_params(adc->dev, ABS_X, 0, 4095, 0, 0);//5.注册input设备ret = input_register_device(adc->dev);if(ret)goto input_register_device_error;//将adc保存到pdev中platform_set_drvdata(pdev, adc);//6.打开clock;注意在初始化adc之前一定要写打开时钟;只要使用时钟的设备,在初始化之前都要先打开时钟adc->clk = clk_get(NULL, "adc");clk_enable(adc->clk);//7.初始化adc//sprintf(adc->name, "adc");adc->user = 0;adc->irq = irq_res->start;adc->handle = do_adc;//涉及时钟的硬件在初始化之前一定要确认时钟打开s3c_adc_init(adc);//8.申请中断(adc)ret = request_irq(adc->irq, adc->handle, 0, pdev->name, adc);if(ret){goto request_irq_error;}//9.建立一个adc->timer_list,定时时间为0.5s,处理函数为do_adc->timersetup_timer(&adc->timer, do_timer, (unsigned long)adc);mod_timer(&adc->timer, jiffies + HZ / 2);//这里不要用0.5*HZ,内核是不支持浮点数的,也可以写成(HZ>>1)return 0;free_irq(adc->irq, adc);request_irq_error:input_unregister_device(adc->dev);input_register_device_error:input_free_device(adc->dev);input_allocate_device_error:iounmap(adc->v);remap_error:kfree(adc);return ret;}int b_remove(struct platform_device *pdev){struct adc_info *adc;adc = platform_get_drvdata(pdev);del_timer_sync(&adc->timer);free_irq(adc->irq, adc);s3c_adc_exit(adc);//clock的反操作clk_disable(adc->clk);clk_put(adc->clk);input_free_device(adc->dev);iounmap(adc->v);kfree(adc);return 0;}struct platform_driver drv = {.driver = {.name = "s3c-my-adc",},.probe = b_probe,.remove = b_remove,};static __init int module_test_init(void){return platform_driver_register(&drv);}static __exit void module_test_exit(void){platform_driver_unregister(&drv);}module_init(module_test_init);module_exit(module_test_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Musesea");MODULE_VERSION("1.0");MODULE_DESCRIPTION("Test for module");

应该注意的地方在代码中做了标注了,至此又一个驱动搞完了。

还是那句话,大家若是发现有什么问题,一定要告诉我,大家一起学习了。