本文共 6147 字,大约阅读时间需要 20 分钟。
字符设备函数在文件“include/linux/fs.h”中
内核提供了三个函数来注册一组字符设备编号,这三个函数分别是 – register_chrdev_region() – alloc_chrdev_region() – register_chrdev()(1)register_chrdev_region
int register_chrdev_region(dev_t from, unsigned count, const char *name)
from :要分配的设备编号范围的初始值, 这组连续设备号的起始设备号, 相当于register_chrdev()中主设备号
Count:连续编号范围. 是这组设备号的大小(也是次设备号的个数) Name:编号相关联的设备名称. (/proc/devices); 本组设备的驱动名称(2)alloc_chrdev_region
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name){ struct char_device_struct *cd; cd = __register_chrdev_region(0, baseminor, count, name); if (IS_ERR(cd)) return PTR_ERR(cd); *dev = MKDEV(cd->major, cd->baseminor); return 0;}
dev :alloc_chrdev_region函数向内核申请下来的设备号
baseminor :次设备号的起始
count:申请次设备号的个数
name :执行 cat /proc/devices显示的名称
(3)register_chrdev(老版本的字符设备申请函数)
int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
major: 主设备号,当用户设置为0时,内核会动态分配一个设备号。
name:设备名称 fops:文件系统的接口指针头文件位置 “include/linux/kdev_t.h”。
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
宏定义:#define MKDEV(major,minor) (((major) << MINORBITS) | (minor))
major为主设备号 minor为次设备号
成功执行返回dev_t类型的设备编号,dev_t类型是unsigned int 类型,32位,用于在驱动程序中定义设备编号,高12位为主设备号,低20位为次设备号
可以通过MAJOR(dev)和MINOR(dev)来获得主设备号和次设备号。
原文链接:https://blog.csdn.net/u011328417/article/details/51968578
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类型是是字符设备描述的结构, 其中的设备号dev,必须用“dev_t”类型来描述,高12位为主设备号,低20位为次设备号。
equest_cdev_num.c
说明:register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NAME)中,DEVICE_MINOR_NUM代表次设备的个数。
#include/*包含初始化宏定义的头文件,代码中的module_init和module_exit在此文件中*/#include /*包含初始化加载模块的头文件,代码中的MODULE_LICENSE在此头文件中*//*定义module_param module_param_array的头文件*/#include /*定义module_param module_param_array中perm的头文件*/#include /*三个字符设备函数*/#include /*MKDEV转换设备号数据类型的宏定义*/#include /*定义字符设备的结构体*/#include #define DEVICE_NAME "scdev"#define DEVICE_MINOR_NUM 2#define DEV_MAJOR 0#define DEV_MINOR 0MODULE_LICENSE("Dual BSD/GPL");/*声明是开源的,没有内核版本限制*/MODULE_AUTHOR("iTOPEET_dz");/*声明作者*/int numdev_major = DEV_MAJOR;int numdev_minor = DEV_MINOR;/*输入主设备号*/module_param(numdev_major,int,S_IRUSR);/*输入次设备号*/module_param(numdev_minor,int,S_IRUSR);static int scdev_init(void){ int ret = 0; dev_t num_dev; printk(KERN_EMERG "numdev_major is %d!\n",numdev_major); printk(KERN_EMERG "numdev_minor is %d!\n",numdev_minor); if(numdev_major){ num_dev = MKDEV(numdev_major,numdev_minor); ret = register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NAME); } else{ printk(KERN_EMERG "numdev_major %d is failed!\n",numdev_major); } if(ret<0){ printk(KERN_EMERG "register_chrdev_region req %d is failed!\n",numdev_major); } printk(KERN_EMERG "scdev_init!\n"); /*打印信息,KERN_EMERG表示紧急信息*/ return 0;}static void scdev_exit(void){ printk(KERN_EMERG "scdev_exit!\n"); unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);}module_init(scdev_init);/*初始化函数*/module_exit(scdev_exit);/*卸载函数*/
Makefile
#!/bin/bash#通知编译器我们要编译模块的哪些源码#这里是编译itop4412_hello.c这个文件编译成中间文件mini_linux_module.oobj-m += request_cdev_num.o #源码目录变量,这里用户需要根据实际情况选择路径#作者是将Linux的源码拷贝到目录/home/topeet/android4.0下并解压的KDIR := /home/topeet/iTop4412_Kernel_3.0#当前目录变量PWD ?= $(shell pwd)#make命名默认寻找第一个目标#make -C就是指调用执行的路径#$(KDIR)Linux源码目录,作者这里指的是/home/topeet/android4.0/iTop4412_Kernel_3.0#$(PWD)当前目录变量#modules要执行的操作all: make -C $(KDIR) M=$(PWD) modules #make clean执行的操作是删除后缀为o的文件clean: rm -rf *.mod.c *.o *.order *.ko *.mod.o *.symvers
insmod request_cdev_num.ko numdev_major=9 numdev_minor=0
rmmod request_cdev_num.ko numdev_major=9 numdev_minor=0
request_acdev_num.c
#include/*包含初始化宏定义的头文件,代码中的module_init和module_exit在此文件中*/#include /*包含初始化加载模块的头文件,代码中的MODULE_LICENSE在此头文件中*//*定义module_param module_param_array的头文件*/#include /*定义module_param module_param_array中perm的头文件*/#include /*三个字符设备函数*/#include /*MKDEV转换设备号数据类型的宏定义*/#include /*定义字符设备的结构体*/#include #define DEVICE_NAME "acdev"#define DEVICE_MINOR_NUM 2#define DEV_MAJOR 0#define DEV_MINOR 0MODULE_LICENSE("Dual BSD/GPL");/*声明是开源的,没有内核版本限制*/MODULE_AUTHOR("iTOPEET_dz");/*声明作者*/int numdev_major = DEV_MAJOR;int numdev_minor = DEV_MINOR;/*输入主设备号*/module_param(numdev_major,int,S_IRUSR);/*输入次设备号*/module_param(numdev_minor,int,S_IRUSR);static int acdev_init(void){ int ret = 0; dev_t num_dev; printk(KERN_EMERG "numdev_major is %d!\n",numdev_major); printk(KERN_EMERG "numdev_minor is %d!\n",numdev_minor); if(numdev_major){ num_dev = MKDEV(numdev_major,numdev_minor); ret = register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NAME); } else{ /*动态注册设备号*/ ret = alloc_chrdev_region(&num_dev,numdev_minor,DEVICE_MINOR_NUM,DEVICE_NAME); /*获得主设备号*/ numdev_major = MAJOR(num_dev); printk(KERN_EMERG "adev_region req %d !\n",numdev_major); } if(ret<0){ printk(KERN_EMERG "register_chrdev_region req %d is failed!\n",numdev_major); } printk(KERN_EMERG "acdev_init!\n"); /*打印信息,KERN_EMERG表示紧急信息*/ return 0;}static void acdev_exit(void){ printk(KERN_EMERG "acdev_exit!\n"); unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);}module_init(acdev_init);/*初始化函数*/module_exit(acdev_exit);/*卸载函数*/
Makefile
#!/bin/bash#通知编译器我们要编译模块的哪些源码#这里是编译itop4412_hello.c这个文件编译成中间文件mini_linux_module.oobj-m += request_acdev_num.o #源码目录变量,这里用户需要根据实际情况选择路径#作者是将Linux的源码拷贝到目录/home/topeet/android4.0下并解压的KDIR := /home/topeet/iTop4412_Kernel_3.0#当前目录变量PWD ?= $(shell pwd)#make命名默认寻找第一个目标#make -C就是指调用执行的路径#$(KDIR)Linux源码目录,作者这里指的是/home/topeet/android4.0/iTop4412_Kernel_3.0#$(PWD)当前目录变量#modules要执行的操作all: make -C $(KDIR) M=$(PWD) modules #make clean执行的操作是删除后缀为o的文件clean: rm -rf *.mod.c *.o *.order *.ko *.mod.o *.symvers
[root@iTOP-4412]# insmod request_acdev_num.ko
[root@iTOP-4412]# rmmod request_acdev_num