2. 字符设备驱动
一、设备号
1.1. 什么是设备号
设备号是用来标记一类设备以及区分这类设备中具体个体的一组号码。
设备号由主设备号和次设备号组成。主设备号的作用为标记一类设备、用于标识设备驱动程序,而次设备号的作用是为了区分这类设备中的具体个体设备及用于标识同一驱动程序下的具体设备;通过该区分,系统可以准确的识别和操作每一个设备和对设备的有效管理,提高设备的利用率和可扩展性。
在Linux中设备号的定义通常使用dev_t
,该类型需要包含头文件include/linux/types.h
,查看其定义我们可以发现它其实就是一个32位的无符号整型变量;其中主设备号为高12位(同类型驱动个数为0~212−10~2^{12}-10~212−1),次设备号为低20位(挂载设备为0~220−10~2^{20}-10~220−1)。
Linux中为我们提供了相关宏用于处理设备号,这些宏位于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))
1.2. 设备号的获取、释放和查看
1.2.1. 获取设备号
获取设备号的方式有两种:静态分配和动态分配,都是Linux内核提供的接口,该接口位于linux/fs.h
中。
- 静态分配
register_chrdev_region
函数原型:
/*** 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.
*/
int register_chrdev_region(dev_t from, unsigned count, const char *name);
- 动态分配
alloc_chrdev_region
函数原型:
/*** 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 alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
1.2.2. 释放设备号
- 释放设备号
函数原型:
/*** unregister_chrdev_region() - return a range of device numbers* @from: the first in the range of numbers to unregister* @count: the number of device numbers to unregister** This function will unregister a range of @count device numbers,* starting with @from. The caller should normally be the one who* allocated those numbers in the first place...*/
void unregister_chrdev_region(dev_t from, unsigned count);
1.2.3. 查看设备号
- 指令
lsblk
待补充 - 查看文件
/proc/devices
通过cat /proc/devices
将所有的设备号全部打印出来,如下:
Character devices: # 字符设备1 mem4 /dev/vc/04 tty4 ttyS
Block devices: # 块设备7 loop8 sd9 md
254 mdp
259 blkext
1.3. 栗子
驱动代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/kdev_t.h>int Isalloc = 0; /* 是否动态申请设备号:0-静态申请、非0-动态申请 */
int Major = 0;
int Minor = 0;module_param(Isalloc, int, S_IRUGO);
MODULE_PARM_DESC(Isalloc, "e.g.是否动态申请: 0-静态申请(需输入设备号),非0-动态申请(输入设备号无效).");
module_param(Major, int, S_IRUGO);
MODULE_PARM_DESC(Major, "e.g.主设备号");
module_param(Minor, int, S_IRUGO);
MODULE_PARM_DESC(Minor, "e.g.次设备号");dev_t chrdev_region = 0;static int chrdev_region_init(void)
{if (0 == Isalloc){/* 静态申请设备号 */chrdev_region = MKDEV(Major, Minor);/* zhf是驱动程序名称 */int i32Ret = register_chrdev_region(chrdev_region, 1, "zhf");if (0 > i32Ret){printk("register_chrdev_region err %d \n", i32Ret);}}else{/* 动态申请设备号 zhf是驱动程序名称 */int i32Ret = alloc_chrdev_region(&chrdev_region, 0, 1, "zhf");if (0 > i32Ret){printk("alloc_chrdev_region err %d \n", i32Ret);}}/* 设备号打印 */printk("chrdev region ok major:%d minor:%d\n", MAJOR(chrdev_region), MINOR(chrdev_region));return 0;
}static void chrdev_region_exit(void)
{/* 设备号销毁 */unregister_chrdev_region(&chrdev_region, 1);printk("unregister_chrdev_region ok \n");
}module_init(chrdev_region_init);
module_exit(chrdev_region_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhf");
二、字符设备驱动
2.1. 什么是字符设备
字符设备是一种用于传输字符或字节流的设备。其实就是传"acnah"
这样的ASSIC
码。常见的字符设备包括键盘、鼠标、串口等设备。字符设备以字节为单位进行读取和写入。
字符设备的交互方式:字符设备在系统中会被表示为设备文件,用户可通过打开设备文件与字符设备进行交互。
2.2. 字符设备的操作
2.2.1. 注册字符设备
定义字符类设备需要用到cd