参考资料: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中做的事情都清理掉,需要注意清理的顺序。