linux内核模块编程(六)----字符设备驱动&中断开发

先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题
在这里插入图片描述

一 why

字符设备驱动在我们之前的博文中都已经有过比较详细的介绍了,为什么需要带上一个中断开发呢?我们知道CPU获取外设的数据有两种方式,一种是CPU主动去轮询外设,另一种就是外设通过中断的方式异步地将自己的数据上报给CPU。轮询的方式会造成CPU占用率过高浪费太多的资源,同时实时性得不到保障,中断是一种比较好的解决轮询缺点的解决方案,今天我们就来看看字符设备驱动中的中断开发

二 what

a. 驱动如何获取中断号

//1. 获取到设备树中的节点
of_find_node_by_path()
//2. 通过节点去获取到中断号码
irq_of_parse_and_map()

b. 驱动如何申请中断

request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char * name, void * dev)
参数1: 设备对应的中断号
参数2: 中断的处理函数
	typedef irqreturn_t (*irq_handler_t)(int, void *);
参数3: 触发方式
	#define IRQF_TRIGGER_NONE	0x00000000
	#define IRQF_TRIGGER_RISING	0x00000001
	#define IRQF_TRIGGER_FALLING	0x00000002
	#define IRQF_TRIGGER_HIGH	0x00000004
	#define IRQF_TRIGGER_LOW	0x00000008
参数4: 中断的描述,自定义,主要是给用户查看的
	/proc/interrupts
参数5: 传递给参数2的参数
返回值: 正确为0,错误为非零

三 how

a. 获取中断号的函数如下

//获取中断号
static int get_irqno_from_node(void)
{
	struct device_node *np;
	int no;
	
	//获取到设备树中的节点
	np = of_find_node_by_path("/key_interrupt");
	if (np) {
		printk("find node ok\n");
	} else {
		printk("find node fail\n");
	}

	//通过节点去获取到中断号码
	no = irq_of_parse_and_map(np, 0);
	printk("irqno = %d\n", no);

	return no;
}

b. 获取完中断号就可以注册中断了

key_dev->irqno = get_irqno_from_node();
ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
		"key3_exit10", NULL);

c. 完整代码
这个程序只是一个字符设备驱动之中断开发的一个框架,暂时还没有实现任何实际的功能,当然这个驱动是可以正确编译并被加载到内核的

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>

typedef struct key_desc_s {
	unsigned int dev_major;
	struct class *chardev_class;
	struct device *chardrv_class_dev;
	int irqno;
} key_desc_t;

key_desc_t *key_dev;


static int kernel_value1 = 555;
static int kernel_value2;

irqreturn_t key_irq_handler(int irq_no, void *devid)
{
	printk("-----------------\n");

	return IRQ_HANDLED;
}

static int chardev_drv_open(struct inode *inode, struct file *file)
{
	printk(KERN_INFO "chardev_drv_open\n");
	return 0;
}

static ssize_t chardev_drv_read(struct file *filp, char __user *buf,
	size_t size, loff_t *ppos)
{
	int ret = 0;
	printk(KERN_INFO "chardev_drv_read\n");

	ret = copy_to_user(buf, &kernel_value1, size);
	if (ret > 0) {
		printk(KERN_ERR "copy_to_user error\n");
		return -EINVAL;
	}

	return 0;
}

static ssize_t chardev_drv_write(struct file *filp, const char __user *buf,
	size_t size, loff_t *ppos)
{
	int ret = 0;

	printk(KERN_INFO "chardev_drv_write\n");

	ret = copy_from_user(&kernel_value2, buf, size);
	if (ret > 0) {
		printk(KERN_ERR "copy_from_user error\n");
		return -EINVAL;
	}

	printk("kernel_value2 = %d", kernel_value2);

	return 0;
}

int chardev_drv_close(struct inode *inode, struct file *file)
{
	printk(KERN_INFO "chardev_drv_close\n");
	return 0;
}

static struct file_operations chardev_drv_fops = {
	.owner	 = THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
	.open	 = chardev_drv_open,
	.read	 = chardev_drv_read,
	.write	 = chardev_drv_write,
	.release = chardev_drv_close,
};

//获取中断号
static int get_irqno_from_node(void)
{
	struct device_node *np;
	int no;
	
	//获取到设备树中的节点
	np = of_find_node_by_path("/key_interrupt");
	if (np) {
		printk("find node ok\n");
	} else {
		printk("find node fail\n");
	}

	//通过节点去获取到中断号码
	no = irq_of_parse_and_map(np, 0);
	printk("irqno = %d\n", no);

	return no;
}

static int __init key_drv_init(void)
{
	int ret = 0;

	//1. 设定一个全局的设备对象
	key_dev = (key_desc_t *)kzalloc(sizeof(key_desc_t), GFP_KERNEL);
	if (key_dev == NULL) {
		printk("kzalloc failure\n");
		return -EINVAL;
	}

	//2. 申请主设备号
	key_dev->dev_major = register_chrdev(0, "chardev0", &chardev_drv_fops);

	//3. 创建设备节点文件
	key_dev->chardev_class = class_create(THIS_MODULE, "chardev0");
	key_dev->chardrv_class_dev = device_create(key_dev->chardev_class, NULL,
		MKDEV(key_dev->dev_major, 0), NULL, "chardev0"); /* /dev/xyz */

	//4. 注册中断
	key_dev->irqno = get_irqno_from_node();
	ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
		"key3_exit10", NULL);

	return 0;
}

static void __exit key_drv_exit(void)
{
	printk(KERN_INFO "new exit chardev0\n");
	//释放中断
	free_irq(key_dev->irqno, NULL);

	unregister_chrdev(key_dev->dev_major, "chardev0");
	device_unregister(key_dev->chardrv_class_dev);
	class_destroy(key_dev->chardev_class);

	kfree(key_dev);
}


module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值