【Linux驱动】字符设备驱动模板(二)—— 分配 / 释放设备号

字符设备的注册分为两部分:

  • 注册设备号
  • 注册设备本身

注册 / 释放设备号将基于上一次的模板,主要修改的是入口函数和退出函数,在入口函数内注册设备号,在退出函数中释放设备号。


目录

一、认识设备号

二、分配 / 释放设备号 API

1、设备号操作(主次设备号获取、构建设备号)

2、静态分配设备号 —— register_chrdev_region

3、动态分配设备号 —— alloc_chrdev_region

4、释放设备号 —— unregister_chrdev_region

三、设备号注册测试

1、驱动模块基本思路

2、测试结果

3、驱动模板代码


一、认识设备号

 Linux中每一个设备都有一个设备号,使用 dev_t 类型表示,本质是一个 32 位的无符号整型。每个设备号包含主设备号次设备号两部分

  • 主设备号:高 12 位。表示某一个具体的驱动
  • 次设备号:低 20 位。表示某个驱动的各个设备

可以使用 cat /proc/devices 查看已经使用的主设备号。如果采用静态分配设备号,注意不要和已有主设备号重复。

二、分配 / 释放设备号 API

1、设备号操作(主次设备号获取、构建设备号)

 在内核源码的 include/linux/kdev.h 中提供了设备号的操作函数

2、静态分配设备号 —— register_chrdev_region

如果已经存在了主设备号(即之前已经注册过该类型的字符设备),只需要我们自己指定一个次设备号的话,我们推荐使用静态分配。

int register_chrdev_region(dev_t from, \
                           unsigned count, \
                           const char *name);

from:要申请的起始设备号(该函数支持一次注册多个设备)

count:要申请的设备号数量

name:设备名称

int major = 200;
int minor = 0;
dev_t devid = MKDEV(major, minor);        // 第一个参数是主设备号,第二个参数是次设备号
register_chrdev_region(devid, 1, "test");

3、动态分配设备号 —— alloc_chrdev_region

动态分配设备号时,系统会自动给你一个没有被使用的设备号,这样就避免了冲突,卸载驱动时释放掉这个设备号即可。函数声明如下:

int alloc_chrdev_region(dev_t *dev, \
                        unsigned baseminor, \
                        unsigned count, \
                        const char *name);

dev:输出型参数。保存申请到的设备号

baseminor:次设备号起始地址。alloc_chrdev_region 可以申请一段连续的多个设备号,这 些设备号的主设备号一样,但是次设备号不同,次设备号以 baseminor 为起始地址地址开始递 增。一般 baseminor 为 0,也就是说次设备号从 0 开始。

count:要申请的设备号数量

name:设备名字

int major;
int minor;
dev_t devid;
alloc_chrdev_region(&devid, 0, 1, "test"); 
major = MAJOR(devid);        // 获取主设备号
minor = MINOR(devid);        // 获取次设备号

4、释放设备号 —— unregister_chrdev_region

注销字符设备之后要释放掉设备号。无论是静态分配还是动态分配,都是使用该函数注销设备号。函数声明如下:

void unregister_chrdev_region(dev_t from, unsigned count);

from:要释放的设备号。(这里指的是主设备号,并非上面次设备号的起始地址)

count:表示从 from 开始,要释放的设备号数量。

三、设备号注册测试

1、驱动模块基本思路

为了方便后续使用,这里将主设备号、次设备号封装成一个结构体类型 chrdev_t,该类型的对象为chrdev。注册设备号的基本思路:

① 如果 chrdev.major 为 0

说明此前没有分配过主设备号,此时便动态分配一个设备号。(若没有分配过主设备号,int 类型的全局变量major默认初始化为 0)

② 如果 chrdev.major 不为 0

说明此前已经分配过主设备号,此时便只需静态分配一个次设备号。

2、测试结果

将驱动模块编译到内核以后,输入 cat /proc/devices 查看主设备号

insmod chrdevbase.ko    # 加载驱动模块
cat /proc/devices       # 查看主设备号

3、驱动模板代码

#include <linux/module.h>		// MODULE_LICENSE、MODULE_AUTHOR
#include <linux/init.h>			// module_init、module_exit
#include <linux/printk.h>		// printk
#include <linux/kdev_t.h>		// MKDEV
#include <linux/fs.h>			// register_chrdev_region

#define CHRDEVBASE_NAME "chrdevbase" 	/* 设备名 */

static struct chrdev_t{
	dev_t devid;			/* 设备号(由主设备号和次设备号组成) */
	int major;				/* 主设备号 */
	int minor;				/* 次设备号 */
} chrdev;

/*
 * @description	: 驱动入口函数 
 * @param 		: 无
 * @return 		: 0 成功;其他 失败
 */
static int __init chrdevbase_init(void)
{
	int ret = 0;
	printk("chrdevbase init!\n");
	/* 1、注册设备号 */
	if (!chrdev.major)    // 主设备号未曾分配过
	{
		ret = alloc_chrdev_region(&chrdev.devid, 0, 1, CHRDEVBASE_NAME);
		chrdev.major = MAJOR(chrdev.devid);
		chrdev.minor = MINOR(chrdev.devid);
	}
	else                // 已经分配过主设备号,只需手动分配次设备号
	{
		chrdev.devid = MKDEV(chrdev.major, 0);
		ret = register_chrdev_region(chrdev.devid, 1, CHRDEVBASE_NAME);
	}

	return 0;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit chrdevbase_exit(void)
{
	printk("chrdevbase exit!\n");
	/* 释放设备号 */
	unregister_chrdev_region(chrdev.devid, 1);
}

/* 
 * 将上面两个函数指定为驱动的入口和出口函数 
 */
module_init(chrdevbase_init);		// 注册 ko模块被加载到内核,系统会调用的函数
module_exit(chrdevbase_exit);		// 注册 ko模块从内核卸载,系统会调用的函数

/* 
 * LICENSE和作者信息
 */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("author_name");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值