前面字符设备用register_chrdev注册设备,用unregister_chrdev注销设备。新的字符设备驱动使用linux推荐的新API。此外,前面测试的时候要自己建立设备节点,本节学习如何在加载驱动的时候自动新建节点。
旧方法缺陷:
register_chrdev注册只需要给一个主设备号,但是这样就导致该主设备号下的次设备号全都归属该设备,比如led,太浪费资源。
解决方案:
<未给定设备号,向内核申请并注册设备号 PARA:设备号指针,起始设备号,申请个数,设备名>
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
<给定主设备号,注册设备号 通常用这个 para:起始设备号(上一行给定的),申请个数,设备名>
int register_chrdev_region(dev_t from, unsigned count, const char *name)
<注销设备号>
void unregister_chrdev_region(dev_t from, unsigned count)
/---------------------------------------------------------------/
int major; <主设备号>
int minor; <次设备号>
dev_t devid; <设备号>if (major) { <定义了主设备号>devid = MKDEV(major, 0); <大部分驱动次设备号都选择 零>register_chrdev_region(devid, 1, "test");
} else { <没有定义设备号>alloc_chrdev_region(&devid, 0, 1, "test"); <申请设备号>major = MAJOR(devid); <获取分配号的主设备号>minor = MINOR(devid); <获取分配号的次设备号>
}unregister_chrdev_region(devid, 1); <注销设备号>
使用cdev结构体表示一个字符设备。
struct cdev {struct kobject kobj;struct module *owner;const struct file_operations *ops; <操作函数集合>struct list_head list;dev_t dev; <设备号>unsigned int count;
};struct cdev test_cdev <定义了一个字符设备test_dev>
用于初始化字符设备。
<函数原型>
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
--------------------------------------------------------------------struct cdev testcdev;
<设备操作函数>
static struct file_operations test_fops = {.owner = THIS_MODULE,/* 其他具体的初始项 */
};testcdev.owner = THIS_MODULE;
cdev_init(&testcdev, &test_fops); <初始化cdev结构体变量>
向linux系统添加字符设备。
<函数原型 Para:字符设备,设备号,添加个数>
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
-------------------------------------------------------cdev_add(&testcdev, devid, 1);
卸载驱动时删除字符设备。
结合unregister_chrdev_region相当于unregister_chrdev。
<函数原型>
void cdev_del(struct cdev *p)
---------------------------------cdev_del(&testcdev);
前面使用modprobe加载模块之后会自动在/dev下创建设备节点文件,rmmod后就会删除文件。这其实是mdev用户程序完成的,该程序可以根据系统中硬件设备状态来创建或者删除设备文件。mdev是BusyBox根据Linux下的udev构建的简化版,用于嵌入式Linux。热插拔事件也是mdev管理的。
下面学习如何用mdev实现自动管理节点。
1)创建和删除类
位置:驱动入口函数,cdev_add之后。
<创建:>
struct class *class_create (struct module *owner, const char *name)<删除:>
void class_destroy(struct class *cls);
2)创建设备
位置:驱动出口函数。
<创建:>
struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
<删除:>
void device_destroy(struct class *class, dev_t devt)
3)设置文件私有数据
其实就是写成结构体封装一下,再open函数中作为私有数据添加到设备文件:
<设备结构体>
struct test_dev{dev_t devid; <设备号>struct cdev cdev; struct class *class; <类>struct device *device; <设备>int major; <主设备号>int minor; <次设备号>
};static int test_open(struct inode *inode, struct file *filp)
{filp->private_data = &testdev; /* 设置私有数据 */return 0;
}