设备号
内核利用设备号来标识设备。换句话说,每个内核管理的设备都有唯一的设备号。
#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))
如果对MINORMASK不太理解,可以用下面的例子打印一下二进制表示
#include <iostream>
#include <bitset>
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)
int main() {
std::bitset<32> x(MINORMASK);
std::cout << x << '\n';
}
// output 00000000000011111111111111111111
可以看出:设备号被表示位一个32位无符号整数,前12位记录主设备号,用来区分设备的类型,后20位记录次设备号,用来区分同一个设备类型下的不同设备及设备分区。
设备号生成
有两种方法:
系统自动分配设备号: 需要传递进次设备号的起始数字,和总设备数。如果最终创建的设备数大约总设备数,虽然创建设备节点可能成功,但是读取数据会失败
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);
/**
* alloc_chrdev_region() - register a range of char device numbers
* @dev: output parameter for first assigned number
* @baseminor: first of the requested range of minor numbers
* @count: the number of minor numbers required
* @name: the name of the associated device or driver
*
* Allocates a range of char device numbers. The major number will be
* chosen dynamically, and returned (along with the first minor number)
* in @dev. Returns zero or a negative error code.
*/
用户指定设备号
int register_chrdev_region(dev_t from, unsigned count, const char *name);
/**
* register_chrdev_region() - register a range of device numbers
* @from: the first in the desired range of device numbers; must include
* the major number.
* @count: the number of consecutive device numbers required
* @name: the name of the device or driver.
*
* Return value is zero on success, a negative error code on failure.
*/
设备节点
设备节点的创建使用命令mknod, 删除设备节点使用rm命令。如下:
# 创建字符mknod
#用法:mknod [选项]... 名称 类型 [主设备号 次设备号]
# 创建主设备号位252,次设备号为0的,名字为demo的字符设备节点
mknod /dev/demo -c 252 0
#删除设备节点
rm /dev/demo
#注意: 内核驱动模块的卸载并不会删除设备节点,但读取设备节点会失败。
流程总结
以demo.ko为例,实现驱动正常工作的流程可能是这样子的:
1. 加载内核驱动,生成设备号
insmod demo.ko
// output Major number = 250, minor number = 0
2. 根据设备号,创建设备节点
mknod /dev/demo -c 250 0
3. 用户空间对设备节点进行读写操作
int fd = open("/dev/demo", O_RDONLY);
// 打开成功后可以进行读写