驱动之路四------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\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_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");


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

 

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


 

你可能感兴趣的:(input)