嵌入式Linux(7):字符设备驱动--申请设备号

news/2024/10/17 22:30:09/

文章目录

  • 1、字符设备和杂项设备的区别
  • 2、注册字符类设备号的两个办法
    • 第一种:静态分配一个设备号
    • 第二种:动态分配
    • 注销设备号
  • 写代码
    • 不带参数测试(动态分配):
    • 带参数测试(静态设置):
  • 建议使用动态设备号的办法

1、字符设备和杂项设备的区别

  1. 杂项设备的主设备号是固定的,固定为10;字符类设备就需要自己或者系统来给我们分配主设备号。
  2. 杂项设备可以自动生成设备节点,字符设备需要我们自己生成设备节点。
  3. 代码量的角度来说,字符设备要比杂项设备代码量要多,因为多了分配设备号和自己生成设备节点这两步。

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

建议使用动态设备号的办法


http://www.ppmy.cn/news/47238.html

相关文章

C/C++编译器内存优化技术:内存优化关注程序对内存的访问和使用,以提高内存访问速度和减少内存占用。

目录标题 引言缓存优化数据局部性 数据对齐&#xff1a;优化数据结构的布局&#xff0c;以提高内存访问速度。内存池&#xff1a;为对象分配使用预先分配的内存池&#xff0c;以减少动态内存分配和释放的开销。垃圾收集优化&#xff1a;针对使用垃圾收集的语言&#xff0c;优化…

jsp有哪些内置对象?作用分别是什么?

JSP(Java Server Pages)中有以下九个内置对象&#xff1a; 1.request: 表示客户端的HTTP请求。可以使用它来获取客户端提交的表单数据、URL参数、HTTP头等信息。 <!DOCTYPE html> <html> <head><title>获取request对象信息</title> </head&…

运行时内存数据区之方法区(二)

方法区的演进细节 首先明确&#xff1a;只有HotSpot才有永久代。BEA JRockit、IBMJ9等来说&#xff0c;是不存在永久代的概念的。原则上如何实现方法区属于虚拟机实现细节&#xff0c;不受《]Va虚拟机规范》管束&#xff0c;并不要求统一。Hotspot中方法区的变化&#xff1a; …

学系统集成项目管理工程师(中项)系列08b_合同管理(下)

1. 项目变更约定 1.1. 合同生效后&#xff0c;当事人不得因姓名、名称的变更或者法定代表人、负责人、承办人的变动而不履行合同义务 2. 违约责任的承担方式 2.1. 继续履行 2.2. 采取补救措施 2.3. 赔偿损失 2.4. 支付约定违约金或定金 3. 注意事项 3.1. 当事人的法律资…

业余爱好者想入门编程,一定远离那些只会说No的家伙,尤其程序员

视频&#xff1a;https://haokan.baidu.com/v?pdwisenatural&vid3050207991292418741 自媒体上的程序员群体有一个非常有意思的特点&#xff0c;就是特别愿意否定别人&#xff0c;特别喜欢说no&#xff0c;还有一个特点&#xff0c;特别不爱分享一些有用的技术和知识&…

jdk8到jdk17新增新特性介绍

JDK 8: Lambda表达式和函数式接口 Lambda表达式是一个匿名方法&#xff0c;可以用于将行为作为参数传递给方法&#xff0c;或者在函数式接口中直接表示行为。Lambda表达式使用箭头 -> 将参数列表分隔开来&#xff0c;并且主体由花括号包含。以下是一个简单的Lambda表达式示…

【学习笔记】[JSOI2019]节日庆典

我觉得很厉害。因为这道字符串题目的分析非常自然而且并不复杂。 考虑枚举右端点到 r r r时&#xff0c;维护可能成为答案的位置的集合。显然对于相邻两个元素 i , j i,j i,j&#xff0c;我们有 lcp(S[i:],S[j:]) > r − j \text{lcp(S[i:],S[j:])}>r-j lcp(S[i:],S[j:]…

SSM整合的基本思路梳理

SSM整合的简单思路流程 基本思路 我在整合的时候一般习惯从MyBatis开始向上构建&#xff0c;也就是在开始一个项目的时候先将DAO层搭建起来&#xff0c;再向上整合Spring以及SpringMVC。按照这个流程&#xff0c;可以做出一个比较简单的大致流程作为参考&#xff0c;帮助我们…