zoukankan      html  css  js  c++  java
  • 驱动之路四------adc驱动(input设备)

    开发板: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_ADC	0x7e00b000
    #define S3C_SZ_ADC	SZ_4K
    
    #define ADCCON 		0x000
    #define ADCTSC 		0x004
    #define ADCDLY 		0x008
    #define ADCDAT0 	0x00C
    #define ADCDAT1 	0x010
    #define ADCUPDN 	0x014
    #define ADCCLRINT 	0x018
    #define ADCCLRINTPNDNUP	0x020 
    
    #define S3C_IRQ_ADC_S	IRQ_ADC	
    #define S3C_IRQ_ADC_E	IRQ_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
    ");
    }
    //资源也是两类,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.
    ");
    }
    
    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_info
    	adc = kzalloc(sizeof(struct adc_info), GFP_KERNEL);
    	if(!adc)
    		return -ENOMEM;
    
    	//3.ioremap
    	adc->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->timer
    	setup_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");
    


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

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


  • 相关阅读:
    SQLServer.Produce的研究
    petshop以异步方式插入订单的疑惑(后面理解了)
    SQLHelper.GetCachedParameters方法之缓存
    DALFactory抽象工厂理解
    petshop异时消息处理队列抽象工厂
    AJAX学习2(经典)
    对SQLServerDAL.order的研究(不错,有心得)
    Invertory类对商品库存的更新困惑解决了
    AJAX学习1
    用DataSet读取xml文件
  • 原文地址:https://www.cnblogs.com/riasky/p/3428985.html
Copyright © 2011-2022 走看看