文章目录
- 1、字符设备和杂项设备的区别
- 2、注册字符类设备号的两个办法
- 第一种:静态分配一个设备号
- 第二种:动态分配
- 注销设备号
- 写代码
- 不带参数测试(动态分配):
- 带参数测试(静态设置):
- 建议使用动态设备号的办法
1、字符设备和杂项设备的区别
- 杂项设备的主设备号是固定的,固定为10;字符类设备就需要自己或者系统来给我们分配主设备号。
- 杂项设备可以自动生成设备节点,字符设备需要我们自己生成设备节点。
- 代码量的角度来说,字符设备要比杂项设备代码量要多,因为多了分配设备号和自己生成设备节点这两步。
2、注册字符类设备号的两个办法
第一种:静态分配一个设备号
使用的函数:
register_chrdev_region(dev_t from, unsigned count, const char * name)
参数:
- 第一个:设备号的起始值。类型是dev_t类型。
- 第二个:次设备号的个数。
- 第三个:设备的名称。如:
1 mem
mem就是设备名称。
返回值:成功返回0,失败访问非0。
cat /proc/devices
1 mem
4 /dev/vc/0
4 tty
dev_t (linux/types.h)类型:是用来保存设备号的,是一个32位数。
高12位是用来保存主设备号,低20位是用来保存次设备号的。
typedef u32 __kernel_dev_t;
typedef __kernel_dev_t dev_t;
需要明确知道系统里面的哪些设备号没有被使用。
Linux提供了几个宏定义来操作设备号。
#define MINORBITS 20
次设备号的位数,一共是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))
将我们的主设备号和次设备号组成一个dev_t类型。第一个参数是主设备号,第二个参数是次设备号。
第二种:动态分配
使用的函数是:
alloc_chrdev_region(dev_t * dev, unsigned baseminor, unsigned count, const char * name)
参数:
- 第一个:保存生成的设备号。
- 第二个:请求的第一个次设备号,通常是0。
- 第三个:连续申请的设备号的个数。
- 第四个:设备名称。
返回值:成功返回0,失败访问非0
使用动态分配会优先使用255~234
注销设备号
使用的函数是:
unregister_chrdev_region(dev_t, unsigned);
参数:
- 第一个:申请的设备号的起始地址。
- 第二个:申请的连续的设备号的个数。
写代码
功能描述:
- 代码根据模块加载时是否带参数而确定是使用动态加载设备号还是静态加载设备号。模块加载时后面的参数是主设备号。
chrdev.c
#include <linux/init.h> // 包含宏定义
#include <linux/module.h> // 包含初始化、加载模块的头文件
#include <linux/fs.h>
#include <linux/kdev_t.h>#define DEVICE_NUMBER 1
#define DEVICE_SNAME "schrdev"
#define DEVICE_ANAME "achrdev"#define DEVICE_MINOR_NUMBER 0static int major_num, minor_num;module_param(major_num, int, S_IRUSR);
module_param(minor_num, int, S_IRUSR);static int hello_init(void)
{dev_t dev_num;int ret;if(major_num){printk("major_num: %d\n", major_num);printk("minor_num: %d\n", minor_num);dev_num = MKDEV(major_num, minor_num);ret = register_chrdev_region(dev_num, DEVICE_NUMBER, DEVICE_SNAME);if(ret < 0){printk("register_chrdev_region error\n");}elseprintk("register_chrdev_region ok\n");}else{ret = alloc_chrdev_region(&dev_num, DEVICE_MINOR_NUMBER, DEVICE_NUMBER, DEVICE_ANAME);if(ret <0){printk("alloc_chrdev_region error\n");}elseprintk("alloc_chrdev_region ok\n");major_num = MAJOR(dev_num);minor_num = MINOR(dev_num);printk("major_num: %d\n", major_num);printk("minor_num: %d\n", minor_num);}printk("major_num = %d, minor_num = %d\n",major_num, minor_num);return 0;
}
static void hello_exit(void)
{unregister_chrdev_region(MKDEV(major_num, minor_num), DEVICE_NUMBER);printk("Bye Bye\n");
}/* 模块的入口 */
module_init(hello_init);
/* 模块的出口 */
module_exit(hello_exit);/* 模块声明 */
MODULE_LICENSE("GPL");
Makefile
# 定义内核源码的目录
KERN_DIR ?= /home/liefyuan/Linux/rk356x_linux/kernel
# 定义当前目录
PWD := $(shell pwd)
# 要生成的内核模块
obj-m += chrdev.oall:make -C $(KERN_DIR) M=$(PWD) modulesclean:rm -rf *.order *o *.symvers *.mod.c *.mod *.ko
编译:
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make
运行测试:
不带参数测试(动态分配):
[root@RK356X:/opt]# insmod chrdev.ko
[26656.968890] alloc_chrdev_region ok
[26656.968998] major_num: 236
[26656.969006[root@RK356X:/opt]# ] minor_num: 0
[26656.969014] major_num = 236, minor_num = 0[root@RK356X:/opt]# rmmod chrdev
[26987.753632] Bye Bye
可以看出设备号被优先分配到255~234的范围内。
带参数测试(静态设置):
[root@RK356X:/opt]# insmod chrdev.ko major_num=235 minor_num=1
[27157.343387] major_num: 235
[27157.343491] minor_num: 1
[27157.343504[root@RK356X:/opt]# ] register_chrdev_region ok
[27157.343512] major_num = 235, minor_num = 1[root@RK356X:/opt]# rmmod chrdev.ko
[27170.387528] Bye Bye