【Linux Device Driver】—(2)—Concurrency and Race Conditions——代码

本文介绍了一个使用信号量和异步通知的按键驱动实验。该实验利用Linux内核的异步通知机制,实现按键状态变化时通知用户空间程序。通过定义文件操作结构体和设备节点,使用户程序能够响应按键事件。

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

做了做这个信号量的实验,不过用到了异步通知,也就是通过内核来通知应用程序(具体就是按键按下后,内核通知应用程序可以进行读操作)!


在《Linux Device Driver》这本书里我查过了,在下章节会介绍到,但是也懒得改代码了,所以就这样贴出来吧,马上也就要写一下异步通知这个很实用的动动。。

这里我用的是互斥锁,还可以设置 O_NONBLOCK 位。

1、驱动程序

①、tiny6410_button_sem.c

 

#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/poll.h>
#include <linux/cdev.h>

#include <mach/regs-gpio.h>
#include <mach/gpio-bank-n.h>
#include <mach/gpio-bank-l.h>
#include <mach/map.h>

MODULE_LICENSE("GPL");

static int major;
static dev_t devno;
static struct cdev cdev;

static struct class *tiny6410_class;
static struct device *tiny6410_device;

static struct fasync_struct *tiny6410_button_fasync;

static volatile char key_value;

static DEFINE_SEMAPHORE(button_semlock);

typedef struct {
	int irq;			/* 中断号 */
	int num;			/* 对应的按键编码 */
	char *name;			/* 对应的按键名称 */
} button_irq;

button_irq button_irqs[] = {
			{IRQ_EINT(0), 0, "KEY1"},
			{IRQ_EINT(1), 1, "KEY2"},
			{IRQ_EINT(2), 2, "KEY3"},
			{IRQ_EINT(3), 3, "KEY4"},
			{IRQ_EINT(4), 4, "KEY5"},
			{IRQ_EINT(5), 5, "KEY6"},
			{IRQ_EINT(19), 6, "KEY7"},
			{IRQ_EINT(20), 7, "KEY8"},

};

static irqreturn_t button_interrupt(int irq,void *dev_id)
{
	button_irq *button_irqs_tmp = (struct button_irq *)dev_id;
	int down;
	int number;
	unsigned tmp;

	number = button_irqs_tmp->num;
	switch(number) {
	case 0: case 1: case 2: case 3: case 4: case 5:
		tmp = readl(S3C64XX_GPNDAT);
		down = !(tmp & (1<<number));
		break;
	case 6: case 7:
		tmp = readl(S3C64XX_GPLDAT);
		down = !(tmp & (1 << (number + 5)));
		break;
	default:
		down = 0;
	}

	if (down == !(key_value & (1<<number))) {
		key_value = down ? (key_value | (1<<number)) : (key_value & ~(1<<number));
	
		kill_fasync (&tiny6410_button_fasync, SIGIO, POLL_IN);
    	}

    return IRQ_RETVAL(IRQ_HANDLED);

}

static void button_request_irq(void)
{
	int i;
	for(i=0; i<8; i++) {
		request_irq(button_irqs[i].irq, button_interrupt,IRQ_TYPE_EDGE_BOTH, button_irqs[i].name, (void *)(&button_irqs[i]));
	}
}

static void button_free_irq(void)
{
	int i;
	for(i=0; i<8; i++) {
		free_irq(button_irqs[i].irq, (void *)(&button_irqs[i]));
	}
}

static int button_open(struct inode *inode, struct file *filp)
{
	if(filp->f_flags & O_NONBLOCK) {
		if(down_trylock(&button_semlock)) {
			return -EBUSY;
		}
	}
	else {
		down(&button_semlock);
	}

	button_request_irq();

	return 0;
}

static int button_release(struct inode *inode, struct file *filp)
{
	up(&button_semlock);
	
	button_free_irq();

	return 0;
}

static ssize_t button_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
	unsigned long err;

	err = copy_to_user(buf, &key_value, sizeof(key_value));
	
	return err ? -EFAULT : min(sizeof(key_value), size);
}

static int button_fasync(int fd, struct file *filp, int on)
{
	printk("Call button_fasync!\n");

	return fasync_helper(fd, filp, on, &tiny6410_button_fasync);

}

static const struct file_operations button_fops =
{
	.owner 		= THIS_MODULE,

	.open  		= button_open,
	.release 	= button_release,
	.read  		= button_read,
	.fasync 	= button_fasync,
};

static int __init button_sem_init(void)
{
	alloc_chrdev_region(&devno, 0, 1, "tiny6410_button_sem");
	major = MAJOR(devno);

	/* 初始化cdev */
	cdev_init(&cdev, &button_fops);
	cdev.owner = THIS_MODULE;

	/* 注册cdev */
	cdev_add(&cdev, MKDEV(major, 0), 1);

	tiny6410_class = class_create(THIS_MODULE, "tiny6410_sem_class");
	tiny6410_device = device_create(tiny6410_class, NULL, MKDEV(major, 0), NULL, "tiny6410_button_sem");
	
	return 0;
}

static void __exit button_sem_exit(void)
{
	device_destroy(tiny6410_class, devno);
	class_destroy(tiny6410_class);

	/* 注销cdev */
	cdev_del(&cdev);

	unregister_chrdev_region(MKDEV(major, 0), 1);
	;
}

module_init(button_sem_init);
moudle_exit(button_sem_exit);


 2、测试程序

①、tiny6410_button_sem_app.c

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


/* 
	tiny6410_button_sem_app.c
  */
  
int fd;

void button_fasync_signal(int signum)
{
	int i;
	int key_value, last_key_value;


	read(fd, &key_value, sizeof(key_value));

	for (i=0; i<8; i++) {
		if ((key_value & (1<<i)) != (last_key_value & (1<<i))) {
			printf("KEY%d: %s (key_value = 0x%x)\n",i+1, (key_value & (1<<i)) ? "DOWN" : "UP", key_value);
		}
	}

	last_key_value = key_value;
}

int main(int argc, char **argv)
{
	unsigned char key_val;
	int ret;
	int oflags;


	signal(SIGIO, button_fasync_signal);
	
	fd = open("/dev/tiny6410_button_sem", O_RDWR | O_NONBLOCK);
	if (fd < 0) {
		printf("can't open!\n");
	}

	fcntl(fd, F_SETOWN, getpid());			/* Save process ID in filp -> f_owner,告诉内核我要发给谁 */
	
	oflags = fcntl(fd, F_GETFL); 
	
	fcntl(fd, F_SETFL, (oflags | FASYNC));	/* 改变fasync 标志,最终会调用到驱动的fasync > fasync_helper;用来初始化/ 释放fasync_struct */


	while (1) {
		sleep(1000);
	}
	
	return 0;
}


3、测试结果

 

当时由于没有记录。。。。所以就这样吧。。呵呵。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值