linux char驱动总结

本文介绍了一种基于Linux的按键驱动设计方法,包括轮询、中断、信号等多种模式的应用及实现细节,如等待队列、定时器等关键技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/poll.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>
#include <linux/delay.h>
static struct class *sevendrv_class;
static struct class_device *sevendrv_class_devs;
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
volatile int ev_press = 0;


/********************************************************************************************
应用层调用read函数,会相应的调用file_operation中的read函数。其中有如下几种模式
一.轮训模式,会耗用大量cpu资源
	1.自己写调用函数。需要知道GPIO的虚拟地址,此时应用vir_add=ioremap(phy_address,len)和iounmap(vir_addr)。
	2.应用程序 while(1){read()};
二.中断模式
利用中断和系统自带函数。request_irq(irq,call_back,module,name,value);
1.阻塞模式。
	a.定义等待队列button_waitq,
	static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
	b. 内核read函数中如果ev_press条件不满足且没有信号到来进入睡眠阻塞状态。
	wait_event_interruptible(button_waitq, ev_press);
	ev_press = 0;
	c.有数据过来唤醒等待队列。
	wake_up_interruptible(&button_waitq);
	ev_press = 1;
2.poll机制。和阻塞模式类似都是靠信号唤醒,多了个定时功能。
	a.定义等待队列button_waitq,
	static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
	b.注册poll函数
		struct file_operations  {.poll = seven_drv_poll,}
	{
		unsigned int mask = 0;
		poll_wait(file,&button_waitq,wait);//不会立即睡眠,sys_poll会调用此函数进入计时和睡眠
		if(ev_press)
		mask |= POLLIN | POLLRDNORM;//sys_poll 会应用这个mask,ev_press还是有用的
	}
	c.应用层调用poll
	struct pollfd fds[1];	
	fds[0].fd     = fd;
	fds[0].events = POLLIN;read模式
	ret = poll(fds, 1, 5000);//ms
	
3.信号机制。应用层注册信号,中断过来,内核向app发送信号。
	kernel做以下工作:
	a.注册
	static struct fasync_struct *button_async;//声明变量
	struct file_operations  {.fasync = seven_drv_fasync,}
	fasync_helper(fd,filp,on, &button_async);
	b.中断函数中执行
	kill_fasync(&button_async, SIGIO, POLL_IN);
	用户程序:
	a.将进程id信息告诉内核,并置FASYNC位
	fcntl(fd,F_SETOWN,getpid());
	oflags = fcntl(fd,F_GETFL);
	fcntl(fd,F_SETFL,oflags|FASYNC);
	b.注册信号函数,my_signal 为callback函数
	signal(SIGIO,my_signal);

********************************************************************************************/
	//互斥锁
static atomic_t canopen= ATOMIC_INIT(1);//原子操作
static DECLARE_MUTEX(button_lock);
/**********************************************************************************************
防止多个用户程序读取按键值操作
1.原子操作。都是volatile定义,直接从内存读,不经过寄存器
	a.static atomic_t canopen= ATOMIC_INIT(1);定义canpen为1
	b.open 中,判断如果
		if(!atomic_dec_and_test(&canopen)) //canopen减1是否为0,为0则真
		atomic_inc(&canopen);//加回去,返回
		return -EBUSY;
	c.close后 atomic_inc(&canopen);
2.互斥锁。
	static DECLARE_MUTEX(button_lock);1
	down(&button_lock)//取用,得不到就会阻塞其实就是-1
	up(&button_lock);//释放+1
	down_trylock(&button_lock)//不会和down一样阻塞。
***********************************************************************************************/

struct timer_list button_timer;

/**********************************************************************************************/
//1.定义timer list,初始化 init_timer(&button_timer);HZ=100,jiffers=10ms
//2.增加定器,add_timer(&button_timer);写调用时间,第一次时间到达执行callback函数并且只执行一次。
//3.修改定时器时间。mod_timer(&button_timer, jiffies + 2*HZ);每过jiffies + 2*HZ,就会调用一次callback
/**********************************************************************************************/


static int major;
volatile unsigned int *GPFCON = NULL;
volatile unsigned int *GPFDAT = NULL;
volatile unsigned int *GPGCON = NULL;
volatile unsigned int *GPGDAT = NULL;
unsigned int key_values;
static struct fasync_struct *button_async;
struct pin_desc{
	unsigned int pin;
	unsigned int key_val;
};
struct pin_desc *dev_id = NULL;
struct pin_desc pins_desc[4] = {
		{S3C2410_GPF0,0X01},
		{S3C2410_GPF2,0X02},
		{S3C2410_GPG3,0X03},
		{S3C2410_GPG11,0X04},
};
static void button_irq_timer_expired(unsigned long p)
{
	struct pin_desc *tmp = (struct pin_desc*)dev_id;
	#if 1
	if(!tmp)
	{
		printk("null\n");//定时器第一次会调用这个函数
		return;
	}
	#endif
	unsigned int pinval;
	pinval=s3c2410_gpio_getpin(tmp->pin);
	if(pinval){ 
		key_values = 0x80|tmp->key_val;
		//printk("high,0x%x,%x\n",tmp->pin,pinval);
	}else {
		key_values = tmp->key_val;
	}
	ev_press = 1;
	wake_up_interruptible(&button_waitq);
	kill_fasync(&button_async, SIGIO, POLL_IN);
}
irqreturn_t button_irq(int irq, void *dev_id_tmp)
{
	dev_id = (struct pin_desc*)dev_id_tmp;
	mod_timer(&button_timer, jiffies + 2*HZ);
	return IRQ_HANDLED;
}
static int seven_drv_open(struct inode *inode, struct file *file)
{
	#if 0
	if(!atomic_dec_and_test(&canopen)) {
		atomic_inc(&canopen);
		return -EBUSY;
	}
	#endif
	if(file->f_flags & O_NONBLOCK){
		if(down_trylock(&button_lock))
		return -EBUSY;
	}else {
		down(&button_lock);
	}
	
	request_irq(IRQ_EINT0,button_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
	request_irq(IRQ_EINT2,button_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
	request_irq(IRQ_EINT11,button_irq, IRQT_BOTHEDGE, "S4",&pins_desc[2]);
	request_irq(IRQ_EINT19,button_irq, IRQT_BOTHEDGE, "S5",&pins_desc[3]);
	return 0;
}

static int seven_drv_write(struct file * file, const char __user * buf,size_t count, loff_t * off)
{
	return 0;
}
static int seven_drv_read(struct file * file, const char __user * buf,size_t count, loff_t * off)
{
	//wait_event_interruptible(button_waitq, ev_press);
	//ev_press = 0;
	copy_to_user(buf,&key_values,sizeof(key_values));
	return sizeof(key_values);
}
int seven_drv_close(struct inode * p, struct file *p2)
{
	//atomic_inc(&canopen);
	up(&button_lock);
	free_irq(IRQ_EINT0,&pins_desc[0]);
	free_irq(IRQ_EINT2,&pins_desc[1]);
	free_irq(IRQ_EINT11,&pins_desc[2]);
	free_irq(IRQ_EINT19,&pins_desc[3]);
}
unsigned int seven_drv_poll(struct file *file, poll_table *wait)
{
	unsigned int mask = 0;
	//poll_wait(file,&button_waitq,wait);
	if(ev_press)
		mask |= POLLIN | POLLRDNORM;
	return mask;
}
static int seven_drv_fasync(int fd, struct file *filp, int on)
{
	return fasync_helper(fd,filp,on, &button_async);
}
struct file_operations seven_drv_fops = {
	.owner = THIS_MODULE,
	.open = seven_drv_open,
	.write = seven_drv_write,
	.read = seven_drv_read,
	.release = seven_drv_close,
	.poll = seven_drv_poll,
	.fasync = seven_drv_fasync,
};
int seven_drv_init(void) //入口函数
{
	int minor = 0;

	init_timer(&button_timer);
	//button_timer.data     = (unsigned long) SCpnt;
	button_timer.expires  = jiffies + 5*HZ;   /* 5s 后时间到,调用 button_irq_timer_expired*/
	button_timer.function = (void (*)(unsigned long)) button_irq_timer_expired;
	add_timer(&button_timer);

	GPFCON =(unsigned int*)ioremap(0x56000050, 16);
	GPFDAT = GPFCON + 1;
	GPGCON =(unsigned int*)ioremap(0x56000060, 16);
	GPGDAT = GPGCON + 1;
	major = register_chrdev(0,"seven_drv",&seven_drv_fops);//注册,告诉内核,0表示内核分配
	sevendrv_class = class_create(THIS_MODULE,"sevendrv");
	sevendrv_class_devs = class_device_create(sevendrv_class,NULL,MKDEV(major,0),NULL,"button");// 创建/dev/button结点

	#if 0
	sevendrv_class_devs[1] = class_device_create(sevendrv_class,NULL,MKDEV(major,1),NULL,"led1");
	sevendrv_class_devs[2] = class_device_create(sevendrv_class,NULL,MKDEV(major,2),NULL,"led2");
	sevendrv_class_devs[3] = class_device_create(sevendrv_class,NULL,MKDEV(major,3),NULL,"led3");
	#endif
	return 0;
}

int seven_drv_exit(void) //入口函数
{
	int minor;
	iounmap(GPFCON);
	iounmap(GPGCON);
	unregister_chrdev(major,"seven_drv");//卸载驱动
	#if 0
	for (minor = 0; minor < 4; minor++)
	{
		class_device_unregister(sevendrv_class_devs[minor]);
	}
	#endif
	class_device_unregister(sevendrv_class_devs);
	class_destroy(sevendrv_class);
}
module_init(seven_drv_init);
module_exit(seven_drv_exit);
MODULE_LICENSE("GPL");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值