Linux设备驱动笔记:字符设备驱动程序


参考资料:LDD3,精通Linux设备驱动程序
关键字:编写Linux字符设备驱动程序
 

Linux提供了几种注册字符设备的方式。如下:

示例一:register_chrdev

#include <linux/init.h>
#include <linux/module.h>

#include <linux/cdev.h>

#include <linux/fs.h>


#define DEV_MAJOR	100
#define DEV_NAME	"test"

static int myopen(struct inode *inode, struct file *filp)
{
	printk("myopen!\n");	
	return 0;
}

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

static int myread(struct file *filp, const char __user *buf, size_t len, loff_t *loff)
{
	printk("myread\n");
	return 0;	
}

static int mywrite(struct file *filp, const char __user *buf, size_t len, loff_t *loff)
{
	printk("mywrite!\n");	
	return 0;
}

struct file_operations fops = {
	.owner = THIS_MODULE,
	.open = myopen,
	.release = myrelease,
	.read = myread,
	.write = mywrite,
};

static int __init test_init(void)
{
	int res;

	printk("hello kernel!\n");

	res = register_chrdev(DEV_MAJOR, DEV_NAME, &fops);

	return res;
}

static void __exit test_exit(void)
{
	unregister_chrdev(DEV_MAJOR, DEV_NAME);
	printk("exit kernel!\n");
}


module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("HGL");

  • static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)

这里, major 是感兴趣的主编号, name 是驱动的名子(出现在 /proc/devices), fops 是缺省的file_operations 结构. 一个对 register_chrdev 的调用为给定的主编号注册 0 - 255 的次编号, 并且为每一个建立一个缺省的 cdev 结构. 使用这个接口的驱动必须准备好处理对所有 256 个次编号的 open调用( 不管它们是否对应真实设备 ), 它们不能使用大于 255 的主或次编号.如果你使用 register_chrdev, 从系统中去除你的设备的正确的函数是:

  • int unregister_chrdev(unsigned int major, const char *name);

major 和 name 必须和传递给 register_chrdev 的相同, 否则调用会失败。

通过register_chrdev去注册设备,需要确保主设备号没有被占用,也不能自动在/dev下生产设备节点,需要通过mknod去创建设备文件。如:

$ mknod test c 100 0

 
 

示例二:device_create

#include <linux/init.h>
#include <linux/module.h>

#include <linux/cdev.h>

#include <linux/fs.h>

#include <linux/device.h>

dev_t dev_id;

static struct cdev cdev;
static struct class *cls = NULL;

static struct device *device;

#define DEV_MINOR	5

#define DEV_MAJOR	100
#define DEV_NAME	"test"

static int myopen(struct inode *inode, struct file *filp)
{
	printk("myopen!\n");	
	return 0;
}

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

static int myread(struct file *filp, const char __user *buf, size_t len, loff_t *loff)
{
	printk("myread\n");
	return 0;	
}

static int mywrite(struct file *filp, const char __user *buf, size_t len, loff_t *loff)
{
	printk("mywrite!\n");	
	return 0;
}

struct file_operations fops = {
	.owner = THIS_MODULE,
	.open = myopen,
	.release = myrelease,
	.read = myread,
	.write = mywrite,
};

static int __init test_init(void)
{
	int res;

	printk("hello kernel!\n");

	res = alloc_chrdev_region(&dev_id, DEV_MINOR, 3,  DEV_NAME);
	if (res < 0)
		return res;

	cdev_init(&cdev, &fops);	//将fops和设备关联
	res = cdev_add(&cdev, dev_id, 3);
	if (res < 0)
		return res;
	
	cls = class_create(THIS_MODULE, "test");

	device = device_create(cls, NULL, dev_id, NULL, "test%d", 1);


	return res;
}

static void __exit test_exit(void)
{
	printk("exit kernel!\n");

	device_destroy(cls, dev_id);

	class_destroy(cls);

	cdev_del(&cdev);
	unregister_chrdev_region(dev_id, 3);
}


module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("HGL");

cdev_add和register_chrdev的区别在于cdev_add可以通过alloc_chrdev_region去动态获取主设备号,但同样不能自动生成设备节点。这里使用device_create这个函数,可以帮我们自动创建设备节点。这个函数需要传入一个类class,可以通过class_create这个函数去获取,并通过device_create将设备归入该类。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

在init函数中至少需要完成如下工作:
1),申请设备号。
2),将入口函数(open,read等)与字符驱动程序的cdev抽象相关联。
3),将设备号与cdev相关联。
需要创建设备文件让应用程序去调用设备驱动,可以手动通过mknod去创建,也可以使用device_create自动生成。
在exit函数中需要将init中做的事情都清理掉,需要注意清理的顺序。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章