正点原子RK3568学习日志11-申请字符设备号
1.申请字符设备号
驱动编译成驱动模块ko 运行驱动编译成模块insmod
字符设备是指在I/O传输过程中以字符为单位进行传输的设备,可以使用与普通文件相同的文件操作命令(打开、关闭、读、写等)对字符设备进行操作,是Linux驱动中最基本的一类设备驱动,例如最常见的LED、按键、IIC、SPI,LCD等都属于字符设备的范畴。
要想对字符设备进行操作,需要通过设备号来对相应的设备进行查找
主设备号用来表示一个特定的驱动,次设备号用来管理下面的设备
/include/linux/fs.h”文件
register_chrdev_region(dev_t from, unsigned count, const char *name)静态
alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)动态
/include/linux/types.h” 文件——申请的设备号类型为dev_t
#define MINORBITS 20 /*次设备号位数*/ #define MINORMASK ((1U << MINORBITS) - 1) /*次设备号掩码*/ #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))/*dev右移20位得到主设备号*/ #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) /*与次设备掩码与,得到次设备号*/ #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))/*MKDEV宏将主设备号(ma)左移20位,然后与次设备号(mi)相与,得到设备号*/
dev_t是一个无符号的32位整形类型。其中高12位表示主设备号,低20位表示次设备号
静态申请设备号时需要将指定的主设备号和从设备号通过MKDEV(ma,mi)宏进行设备号的转换
动态申请设备号时可以用MAJOR(dev) 和MINOR(dev)宏将动态申请的设备号转化为主设备号和从设备号
内核源码/include/uapi/asm-generic/int-ll64.h”文件——u32类型
静态申请设备号:
extern int register_chrdev_region(dev_t, unsigned, const char *);
自定义的dev_t类型设备号,申请设备的数量,申请的设备名称
申请成功返回0,申请失败返回负数
动态申请设备号
extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
会将申请完成的设备号保存在dev变量中, 次设备号可申请的最小值, 申请设备的数量,
申请的设备名称
申请成功返回0,申请失败返回负数
static int major = 0; //静态主设备号major static int minor = 0; //静态次设备号minormodule_param(major, int, S_IRUGO); //驱动模块传参,主设备号major 全部只读 module_param(minor, int, S_IRUGO); //驱动模块传参,次设备号minor 全部只读
static dev_t dev_num; //定义32位的变量dev_num,用于存放设备号
dev_num = MKDEV(major, minor); //组成生成设备号
major = MAJOR(dev_num); //获取主设备号 minor = MINOR(dev_num); //获取次设备号
unregister_chrdev_region(dev_num, 1); //释放设备号
2.实验:04_dev_t 申请字符设备号
dev_t.c Makefile
dev_t.c
#include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/fs.h> #include <linux/kdev_t.h>static int major = 0; //静态主设备号major static int minor = 0; //静态次设备号minormodule_param(major, int, S_IRUGO); //驱动模块传参,主设备号major 全部只读 module_param(minor, int, S_IRUGO); //驱动模块传参,次设备号minor 全部只读static dev_t dev_num; //定义32位的变量dev_num,用于存放设备号static int __init dev_t_init(void) {int ret; //判断函数返回值if(major) //传递成功{dev_num = MKDEV(major, minor); //组成生成设备号printk("major = %d\n", major); //打印设备号printk("minor = %d\n", minor); //静态申请设备号ret = register_chrdev_region(dev_num, 1, "chrdev_name");if(ret < 0) //申请失败{printk("register_chrdev_region is error\n");}printk("register_chrdev_region is ok\n"); }else{//动态申请设备号ret = alloc_chrdev_region(&dev_num, 0, 1, "chrdev_name");if(ret < 0) //申请失败{printk("alloc_chrdev_region is error\n");}printk("alloc_chrdev_region is ok\n"); major = MAJOR(dev_num); //获取主设备号minor = MINOR(dev_num); //获取次设备号printk("major = %d\n", major); //打印设备号printk("minor = %d\n", minor); }return 0; } static void __exit dev_t_exit(void) {unregister_chrdev_region(dev_num, 1); //释放设备号printk("module exit. \n");} module_init(dev_t_init); module_exit(dev_t_exit); MODULE_LICENSE("GPL v2"); //声明模块许可证 MODULE_AUTHOR("AFANFAN"); //声明模块作者
Makefile
export ARCH=arm64#设置平台架构 export CROSS_COMPILE=/opt/atk-dlrk3568-5_10_sdk-toolchain/bin/aarch64-buildroot-linux-gnu-#交叉编译器前缀 obj-m += dev_t.o #此处要和你的驱动源文件同名 KDIR :=/home/alientek/rk3568_linux5.10_sdk/kernel #这里是你的内核目录 PWD ?= $(shell pwd) all:make -C $(KDIR) M=$(PWD) modules #make操作 clean:make -C $(KDIR) M=$(PWD) clean #make clean操作
驱动编译成驱动模块ko
使用命令“make”进行驱动的编译,编译完生成dev_t.ko目标文件
运行驱动编译成模块insmod
开发板启动之后,
如果在进行驱动模块加载时传入了major主设备号,则通过静态的方式进行设备号的申请,注意冲突
#静态设备号申请 insmod dev_t.ko major=200 minor=0
如果不传入任何参数进行驱动模块加载,则通过动态的方式进行设备号申请
#动态设备号申请 insmod dev_t.ko
注册设备号的查看 cat /proc/devices
对驱动进行卸载 rmmod cdev.ko
3.问题
1.vscode :
添加头文件
"includePath": [
"${workspaceFolder}/**",
"/home/alientek/rk3568_linux5.10_sdk/kernel/include",
"/home/alientek/rk3568_linux5.10_sdk/kernel/arch/arm64/include",
"/home/alientek/rk3568_linux5.10_sdk/kernel/arch/arm64/include/generated"
],修改键盘快捷方式
Preferences: Open KeyPreferences: Open Keyboard Shortcuts 更改vscode键盘快捷键
2.