adc驱动实例(工作队列+等待队列+设备树+属性文件)

9 篇文章 0 订阅

吐舌头驱动:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/uaccess.h> 
#include <linux/kobject.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/interrupt.h>
#include <linux/highmem.h> 
#include <asm/kmap_types.h>
#include <linux/wait.h>

#define HIGH_RSL 4096 // 2^12
#define LOW_RSL  1024 // 2^10

/*DTS
	myadc {
		compatible = "xadc";
		reg = <0x10010118 0x1>, <0x126C0000 0x20>;
		interrupt-parent = <&combiner>;
		interrupts = <10 3> ;
	};
*/

MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("a simple adc_driver !");

typedef struct {
	unsigned int    ADCCON;  //0

	unsigned int    NOVAL;   //4

	unsigned int    ADCDLY;  //8

	unsigned int    ADCDAT;  //12

	unsigned int    NOVAL1;  //16
	unsigned int    NOVAL2;  //20

	unsigned int    CLRINTADC;  //24
	unsigned int    ADCMUX;  //28                              
                              
}adc_t;

struct ADC
{
	dev_t num;
	int val;	//readval
	int val_ok_flag;
	int available;	//adc aviliable  struct mutex lock;
	spinlock_t lock;
	int resolution;	//ADC resolution
	struct cdev xadc_cdev;  //for  containor_of()
	struct resource *res;	//create a platform driver
	adc_t  *adcaddr; 
	struct class *xadc_class;	//support hotplug function	
	struct device *xadc_device;
	struct work_struct *xadc_work; //irq_bottom_section
	wait_queue_head_t readqueue;	//wait IO
	struct kobject *xadc_kobj;	//support attr_file op

}adc;

struct attribute xadc_attrs[] = {

	[0] = {"xadc3", S_IRUGO |S_IWUSR},

	{}
};

ssize_t xadc_kobj_show(struct kobject *kobj, struct attribute *attr, char *buffer)
{
	if(0 == strcmp(attr->name, "xadc3"))
	{
		if(adc.resolution == HIGH_RSL)
			printk("adc_resolution: HIGH_RSL(2^12)\n");
		else
			printk("adc_resolution: LOW_RSL(2^10)\n");

		return 0;
	}
	else
		return -1;
}

ssize_t xadc_kobj_store(struct kobject *kobj, struct attribute *attr, const char *buffer, size_t size)
{
	iowrite32( ioread32( &(adc.adcaddr->ADCCON) ) & (~(1<<0)) , &(adc.adcaddr->ADCCON) );	///STOP转换	
	if(0 == strcmp(attr->name, "xadc3")) {
		if(0 == strncmp(buffer, "low",3)) {
			adc.resolution = LOW_RSL;
			iowrite32( ioread32( &(adc.adcaddr->ADCCON) ) & (~(1 << 16))  ,&(adc.adcaddr->ADCCON) );	//resolution  10bit
			printk("low resolution set ok!\n");

		}
		else if(0 == strncmp(buffer, "high",4)) {
			adc.resolution = HIGH_RSL;
			iowrite32( ioread32( &(adc.adcaddr->ADCCON) ) | (1 << 16) ,&(adc.adcaddr->ADCCON) );	//resolution  12bit
			printk("high resolution set ok!\n");
		}
	}

    return size;
}

void xadc_kobj_release(struct kobject * k)
{
	kfree(k);

	return ;
}

struct sysfs_ops xadc_sysfs_ops = {
	.show = xadc_kobj_show,
	.store = xadc_kobj_store
};

struct kobj_type xadc_ktype = {
	.release = xadc_kobj_release,
	.sysfs_ops = &xadc_sysfs_ops,
};


int xadc_open(struct inode *i, struct file *f)
{
	spin_lock(&adc.lock);  //lock

	if(adc.available == 1)  //userful
	{
		adc.available = 0;
		spin_unlock(&adc.lock);  //unlock
		
		printk("xadc open\n");
	
		return 0;
	}
		
	spin_unlock(&adc.lock);  //unlock
	
	return -EBUSY;
}

//read waitqueue
ssize_t xadc_read (struct file *f, char __user *p, size_t n, loff_t *off)
{

	iowrite32( ioread32( &(adc.adcaddr->ADCCON) ) | (1<<0) , &(adc.adcaddr->ADCCON) );	///开始转换	

	if(adc.val_ok_flag == 0)	
	{
		
		//let this procss go to sleep
		wait_event_interruptible(adc.readqueue, adc.val_ok_flag != 0);

	}

	if( copy_to_user(p, &(adc.val), sizeof(int)) )    //get val to user
		return -EFAULT;

	adc.val_ok_flag = 0;

	return  sizeof(int);
}

ssize_t xadc_write (struct file *f, const char __user *p, size_t n, loff_t *off)
{
	//copy_from_user(buf, p, n);

	return n;
}

#define LOW   0
#define HIGH  1

long xadc_ioctl(struct file *f, unsigned int resolution, unsigned long arg)
{
	iowrite32( ioread32( &(adc.adcaddr->ADCCON) ) & (~(1<<0)) , &(adc.adcaddr->ADCCON) );	///STOP转换	

	if(resolution < LOW || resolution > HIGH)
	{
		printk("error type!\n");
		return -1;
	}

	if(resolution == LOW) 
	{
		adc.resolution = LOW_RSL;
		iowrite32( ioread32( &(adc.adcaddr->ADCCON) ) & (~(1 << 16))  ,&(adc.adcaddr->ADCCON) );	//resolution  10bit
		printk("low resolution set ok!\n");
	}
	else if(resolution == HIGH)
	{
		adc.resolution = HIGH_RSL;
		iowrite32( ioread32( &(adc.adcaddr->ADCCON) ) | (1 << 16) ,&(adc.adcaddr->ADCCON) );	//resolution  12bit
		printk("high resolution set ok!\n");
	}

	return 0;
}

int xadc_release(struct inode *inode, struct file *file)
{
	printk("xadc closed\n");
 
	iowrite32( ioread32( &(adc.adcaddr->ADCCON) ) & (~(1<<0)) , &(adc.adcaddr->ADCCON) );	///STOP转换	
	adc.available = 1;  //useful

	return 0;
}



struct file_operations xadc_ops = {

	.owner = THIS_MODULE,

	.open = xadc_open,
	.unlocked_ioctl  = xadc_ioctl,
	.read = xadc_read,
	.write = xadc_write ,
	.release = xadc_release
};

//
//irq_handler

//deal_thing
void adc_workfunc(struct work_struct *work)
{
	adc.val = ioread32( &(adc.adcaddr->ADCDAT) ) & 0xfff ;
	
	adc.val_ok_flag = 1;  //ok
	
	//woke up processes
	wake_up_interruptible(&adc.readqueue);	

	return;
}
//irq
irqreturn_t  xadc_handler(int n, void *dev)
{
	adc.xadc_work = kzalloc(GFP_ATOMIC, sizeof(struct work_struct));  //workstruct 将处理的事务交给内核线程中处理
	INIT_WORK(adc.xadc_work, adc_workfunc);
	schedule_work(adc.xadc_work);
	
	iowrite32( 0x12 , &(adc.adcaddr->CLRINTADC) ); //clear INT
	printk("IRQ getval , adc3_val can be read!\n");

	return IRQ_HANDLED;
}

//
//driver 
int xadc_probe(struct platform_device *pdev)
{
	int ret = 0;
	printk("probe the adc_device!\n");

	//get adc_addr
	adc.res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
	printk("adc_baseaddr: %#x\n", adc.res->start);
	adc.adcaddr = ioremap(adc.res->start, adc.res->end-adc.res->start);

	//get adc_irq
	adc.res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 
	printk("adc_irqnum:: %#x\n", adc.res->start);

	ret = request_irq(adc.res->start, xadc_handler, IRQF_DISABLED, "xadc_IRQ", NULL); //申请一个中断服务(需要获取中断号) 最后一个是中断函数的参数pdev

	printk("adc_device info get !\n");

	//request cdev num
	ret = alloc_chrdev_region(&adc.num, 0, 1, "xadc3");
	if(ret) {
		printk("devnum alloc fail!\n");
		return ret;
	}
	printk("devnum: %d  %d \n", MAJOR(adc.num) ,MINOR(adc.num));

	//init cdev fops
	cdev_init(&adc.xadc_cdev,  &xadc_ops);
	
	//register cdev into kernel
	ret = cdev_add(&adc.xadc_cdev, adc.num , 1);
	if(ret) {
		printk("add cdev fail!\n");
		goto cdev_add_out;
	}

	//support hotplug function	
	adc.xadc_class = class_create(THIS_MODULE, "xadc");  //sys/class/
	adc.xadc_device = device_create(adc.xadc_class , NULL, adc.num, NULL, "adc%d", 3);  //dev/

	//init device adc3
	iowrite32( ioread32( &(adc.adcaddr->ADCCON) ) & (~(1 << 16))  ,&(adc.adcaddr->ADCCON) );	//resolution
	iowrite32( ioread32( &(adc.adcaddr->ADCCON) ) | (1 << 14) , &(adc.adcaddr->ADCCON) );	//enable prescaler
	iowrite32( (ioread32( &(adc.adcaddr->ADCCON) ) & (~(0xFF << 6))) | (19<<6) , &(adc.adcaddr->ADCCON) );	//prescaler
	iowrite32( ioread32( &(adc.adcaddr->ADCCON) ) & (~(1 << 2)) , &(adc.adcaddr->ADCCON) );   //normal
	iowrite32( ioread32( &(adc.adcaddr->ADCMUX) ) | 0x3 , &(adc.adcaddr->ADCMUX) );   //adc 第三通道 
	adc.resolution = LOW_RSL;  //init resolution
	init_waitqueue_head(&adc.readqueue);  //init wait queue
	adc.available = 1;   //	spin_lock_init(&adc.lock);
	spin_lock_init(&adc.lock);	
	printk("adc.available!\n");

	//attr_file
	adc.xadc_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
	kobject_init(adc.xadc_kobj, &xadc_ktype);
	ret = kobject_add(adc.xadc_kobj, NULL, "xadc3");
	ret = sysfs_create_file(adc.xadc_kobj, &xadc_attrs[0]);

	//success
	return 0;

cdev_add_out:
	unregister_chrdev_region(adc.num, 1);  //release dev_num
	//error
	return ret;

}

int xadc_remove(struct platform_device *pdev)
{

	//release  remap
	iounmap(adc.adcaddr);
	///释放中断 
	free_irq(adc.res->start, NULL);  
	//unregister cdev from kernel
	cdev_del(&adc.xadc_cdev);
/*
	//release cdev object
	kfree(&beep.xadc_cdev);
*/
	//release dev num
	unregister_chrdev_region(adc.num, 1); 
	
	//support hotplug function
	//free device
	device_del(adc.xadc_device);
	//free class
	class_destroy(adc.xadc_class);

	//free attr_file
	sysfs_remove_file(adc.xadc_kobj, &xadc_attrs[0]);
	kobject_del(adc.xadc_kobj);
	kfree(adc.xadc_kobj);
	
	printk("adc_device remove !\n");

	return 0;
}


//multi_device table
struct platform_device_id xadc_ids[] = {	//support auto_install the driver

	[0] = {
		.name = "adc3"
	},
	[1] = {
		//null
	}

};

#ifdef CONFIG_OF
//设备树匹配方式 
struct of_device_id xadc_table[] = {

	{ .compatible = "xadc" },  //match  adc3

	{ } //NULL
};

#endif

struct platform_driver xadc_driver = {

	.probe = xadc_probe,
	.remove = xadc_remove,
	.id_table = xadc_ids,   //multi_device table
	.driver = {
		.name = "adc3",
		.of_match_table = of_match_ptr(xadc_table)     //设备树匹配选项 
	}	

};

MODULE_DEVICE_TABLE(platform, xadc_ids);  //support auto_install the driver

//driver install  and  release 
static int adcmodule_init(void)
{
	printk("xadc driver install\n");

	//add into platform bus
	platform_driver_register(&xadc_driver);

	return 0;
}

static void adcmodule_exit(void)
{
	printk("xad cdriver release\n");

	//del from platform bus
	platform_driver_unregister(&xadc_driver);

	return ;
}


module_init(adcmodule_init);
module_exit(adcmodule_exit);

吐舌头测试:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define LOW   0
#define HIGH  1

int main(int argc,char **argv)
{
	int chose;

	int fd;
	int val;
	int RES;

	while(1)
	{
		printf("1.open adc  2.close adc  3.set high_resolution 4.set low_resolution 5.read adc_val \n");
		scanf("%d",&chose);
		
		switch(chose)
		{
			case 1: printf("open\n"); 
				fd = open("/dev/adc3", O_RDWR);
				if(-1 == fd)
				{
					perror("open fail EBUSY\n");
					
					return -1;
				}

				break;

			case 2: printf("close\n"); 
				close(fd);
				break;

			case 3: printf("set high_resolution, ");
				
				ioctl(fd, HIGH);
				RES = HIGH;
				printf("set ok!\n");
				break;

			case 4: printf("set low_resolution, ");
				
				ioctl(fd, LOW);
				RES = LOW;
				printf("set ok!\n");
				break;	

			case 5: read(fd,&val,sizeof(int));
				if(RES == LOW)
					val = 1000 * (1.8/1024 * val);
				else
					val = 1000 * (1.8/4096 * val);

				printf("readval: %d mV \n",val);
				
				break;	
			default: printf("err type\n");			 

		}
	}

	close(fd);

	return 0;
}





  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值