服装网站建设策划书论文,万网站长工具,技能培训学校,天津单位网站建设前言 这一篇博客来谈谈字符设备的注册、分配与释放。 嵌入式驱动学习专栏将详细记录博主学习驱动的详细过程#xff0c;未来预计四个月将高强度更新本专栏#xff0c;喜欢的可以关注本博主并订阅本专栏#xff0c;一起讨论一起学习。现在关注就是老粉啦#xff01; 目录 前…前言 这一篇博客来谈谈字符设备的注册、分配与释放。 嵌入式驱动学习专栏将详细记录博主学习驱动的详细过程未来预计四个月将高强度更新本专栏喜欢的可以关注本博主并订阅本专栏一起讨论一起学习。现在关注就是老粉啦 目录 前言1. 设备号1.1 设备号组成1.2 设备号的获取 2. 字符设备分配2.1 静态分配2.2 动态分配2.3 实际代码编写 3. 字符设备注册3.1 cdev结构体3.2 cdev_init()函数3.3 cdev_add()函数 4. 字符设备释放参考资料 1. 设备号
1.1 设备号组成 Linux中为了方便管理每个设备都有一个设备号由主设备号和次设备号两部分组成。主设备号表示某一个具体的驱动次设备号表示使用这个驱动的各个设备。 Linux下提供了dev_t数据类型表示设备号其是一个unsigned int 类型32位的数据其中高12位为主设备号低20位为次设备号因此主设备号的范围不要超过0~4095。
1.2 设备号的获取 在include/linux/kdev_t.h中提供了关于设备号的操作函数宏
#define MINORBITS 20
#define MINORMASK ((1U MINORBITS) - 1)#define MAJOR(dev) ((unsigned int) ((dev) MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) MINORMASK))
#define MKDEV(ma, mi) (((ma) MINORBITS) | (mi))主要是最后三个前面两个是为后三个服务的
宏 MAJOR 用于从 dev_t 中获取主设备号将 dev_t 右移 20 位即可宏 MINOR 用于从 dev_t 中获取次设备号取 dev_t 的低 20 位的值即可宏 MKDEV 用于将给定的主设备号和次设备号的值组合成 dev_t 类型的设备号
2. 字符设备分配 设备号的分配有两种一种是静态分配一种是动态分配
2.1 静态分配 所谓的静态分配就是直接在注册时指定设备号这个设备号可以是驱动开发者静态的指定一个设备号比如200。有一些常用的设备号已经被 Linux 内核开发者给分配掉了具体分配的内容可以查看文档 Documentation/devices.txt。并不是说内核开发者已经分配掉的主设备号我们就不能用了具体能不能用还得看我们的硬件平台运行过程中有没有使用这个主设备号。 使用cat /proc/devices可以查看当前系统中使用了的设备号字符设备、块设备、网络设备均可以看到。 可以使用函数register_chrdev_region来用给定的主设备号进行注册
/* * description: 使用静态分配注册设备号* param-from : 要分配的设备编号范围的初始值(次设备号常设为0)* param-count: 连续编号范围* param-name : 编号相关联的设备名称* return : 当返回值小于0,表示注册失败*/
int register_chrdev_region(dev_t from, unsigned count, const char *name)2.2 动态分配 静态分配还是不够灵活需要先查看已使用的设备号因此常用的还是动态分配等到运行时让系统自动分配一个没有使用的设备号这样就避免了冲突。
/** description : 向linux申请一个没有使用过的设备号* param-dev : 用于保存申请到的设备号* param-baseminor: 次设备号起始地址alloc_chrdev_region 可以申请一段连续的多个设备号这些设备号的主设备号一样但是次设备号不同次设备号以 baseminor 为起始地址地址开始递增。一般 baseminor 为 0也就是说次设备号从 0 开始* param-count : 要申请的设备号数量* param-name : 设备名字* return : 小于0则错误自动分配设备号错误。否则分配得到的设备号就被第一个参数带出来*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)用alloc_chrdev_region()函数得到设备号后需要利用MAJOR宏和MINOR宏获取一下主设备号和次设备号。例如下面 alloc_chrdev_region(chrdevTest.devid, 0, chrdevTest_CNT, chrdevTest_NAME); // 申请设备号chrdevTest.major MAJOR(chrdevTest.devid); // 获取主设备号chrdevTest.minor MINOR(chrdevTest.devid); // 获取次设备号最后卸载驱动的时候需要释放设备号
/** description: 释放设备号* param-from : 要释放的设备号* param-count: 从from开始要释放的设备号数量* return : 无*/
void unregister_chrdev_region(dev_t from, unsigned count)2.3 实际代码编写 在实际操作中我们可以通过判断是否已经给定主设备号来选择静态分配还是动态分配具体写法如下
if (chrdevTest.major) {chrdevTest.devid MKDEV(chrdevTest.major, 0);register_chrdev_region(chrdevTest.devid, chrdevTest_CNT, chrdevTest_NAME);
} else {alloc_chrdev_region(chrdevTest.devid, 0, chrdevTest_CNT, chrdevTest_NAME); // 申请设备号chrdevTest.major MAJOR(chrdevTest.devid); // 获取主设备号chrdevTest.minor MINOR(chrdevTest.devid); // 获取次设备号
}3. 字符设备注册
3.1 cdev结构体 在Linux中使用cdev结构体表示一个字符设备cdev在include/linux/cdev.h文件中的定义
struct cdev {struct kobject kobj;struct module *owner;const struct file_operations *ops;struct list_head list;dev_t dev;unsigned int count;
};在 cdev 中有两个重要的成员变量ops 和 dev这两个就是字符设备文件操作函数集合file_operations 以及设备号 dev_t。编写字符设备驱动之前需要定义一个 cdev 结构体变量一般在设备结构体中定义如下所示:
struct chrdevTest_dev {dev_t devid;struct cdev cdev;struct class *class;struct device *device;int major;int minor;struct device_node *nd;
};3.2 cdev_init()函数 使用cdev_init函数进行初始化
/** description: 初始化cdev结构体* param-cdev : 要初始化的cdev结构体变量* param-fops : 字符设备文件操作函数集合* return : 无*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops)3.3 cdev_add()函数 cdev_add用于向Linux系统添加字符设备(cdev结构体变量)
/** description: 向系统添加字符设备* param-p : 指向要添加的字符设备cdev结构体变量* param-dev : 设备所使用的设备号* param-count: 要添加的设备数量* return : 无*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count)4. 字符设备释放 卸载驱动时一定要用cdev_del函数从Linux内核中删除相应的字符设备即在出口函数中编写cdev_del。
/* * description: 从Linux内核中删除相应的字符设备* param-p : 要删除的字符设备* return : 无*/
void cdev_del(struct cdev *p)删除字符设备后再释放设备号因此完整的流程是 cdev_del(chrdevTest.cdev);unregister_chrdev_region(chrdevTest.devid, chrdevTest_CNT);参考资料
[1] 【正点原子】I.MX6U嵌入式Linux驱区动开发指南 第四十章、第四十二章 [2] linux驱动2设备号与字符设备注册与注销 [3] 字符设备驱动之register_chrdev_region()系列