写点什么

Linux 设备驱动系列(四)——设备号

  • 2024-04-25
    浙江
  • 本文字数:1943 字

    阅读完需:约 6 分钟

关注微信公众号:Linux 内核拾遗

文章来源:https://mp.weixin.qq.com/s/aXehGqP3FBN0B9u-3my10g

1 应用程序与硬件设备交互

应用程序与硬件设备通信和交互的完整流程如下:


  1. 首先,应用程序需要打开设备文件,该文件由设备驱动创建;

  2. 然后,根据主设备号和次设备号找到对应的设备驱动程序;

  3. 随后,应用程序便能通过设备驱动程序与硬件设备进行通信。

2 主/次设备号

Linux 内核的一个重要特性是将设备抽象为文件,使得硬件设备可以像普通文件一样进行打开、关闭、读取和写入操作,统一使用标准系统调用。因此,在 Linux 中,一切皆为文件,无论是向硬盘写入数据、从键盘读取数据,还是备份数据到磁带设备,甚至是从内存读取数据,都通过文件操作完成。设备驱动程序会为每个硬件设备创建特殊文件,我们可以通过这些文件与硬件进行通信。


要创建特殊文件,我们需要了解设备驱动程序中的主设备号和次设备号(Major && Minor Number)。


Linux 内核按照"<major>:<minor>"方式来表示一个字符设备或者块设备。

2.1 主设备号

通常主设备号用于标识与硬件设备关联的驱动程序,一个主设备号也可以被多个设备驱动程序共享。可以查看 /proc/devices 来了解系统中的主设备号分配:


2.2 次设备号

多个设备可能使用相同的主设备号,因此需要为使用相同主设备号的每个设备分配编号,即次设备号。换言之,设备驱动程序使用次设备号来区分单个的物理或逻辑设备。

2.3 设备号分配

主/次设备号支持静态分配和动态分配两种方式。

2.3.1 静态分配

用于需要为设备指定特定设备号的场景。


int register_chrdev_region(dev_t first, unsigned int count, char *name);
复制代码


其中:


  1. first:设备号范围的起始编号;

  2. count:请求分配的连续设备号的个数,如果取值过大,分配的设备号可能溢出到下一个主设备号;

  3. name:关联到分配的设备号的设备名称,它会显示到/proc/devices 和 sysfs 中。


dev_t 类型定义在"linux/types.h"头文件中,长度为 32-bit,用于保存设备号(12-bit 主设备号 + 20bit 次设备号)。


可以通过下面的宏来定义一个 dev_t 变量,或者从 dev_t 变量中获取主设备号或者次设备号:


dev_t dev = MKDEV(int major, int minor);unsigned major = MAJOR(dev_t dev);unsigned minor = MINOR(dev_t dev);
复制代码

2.3.2 动态分配

如果不需要分配固定的设备号,可以使用动态分配的方式,它能够避免与其他设备驱动产生设备号冲突:


int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,                                                unsigned int count, char *name);
复制代码


其中,调用完成后 dev 指向的是分配的设备号范围的第一个设备号。

2.4 设备号注销

设备号不再使用的时候,应当将其释放(通常是在设备驱动程序的退出函数中):


void unregister_chrdev_region(dev_t first, unsigned int count);
复制代码

3 示例

3.1 静态分配

下面代码静态分配了主设备号 168:


statically_allocate_devno.c


#include<linux/kernel.h>#include<linux/init.h>#include<linux/module.h>#include <linux/fs.h>
dev_t dev = MKDEV(168, 0);
static int __init my_init(void){ register_chrdev_region(dev, 1, "my_dev_1"); pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev)); pr_info("Kernel Module Inserted!\n"); return 0;}
static void __exit my_exit(void){ unregister_chrdev_region(dev, 1); pr_info("Kernel Module Removed!\n");} module_init(my_init);module_exit(my_exit); MODULE_LICENSE("GPL");MODULE_AUTHOR("feifei <feifei@gmail.com>");MODULE_DESCRIPTION("A simple linux device driver");
复制代码


编译运行:


3.2 动态分配

下面代码演示的是设备号动态分配:


dynamically_allocate_devno.c


#include<linux/kernel.h>#include<linux/init.h>#include<linux/module.h>#include<linux/kdev_t.h>#include<linux/fs.h> dev_t dev = 0;
static int __init my_init(void){ if((alloc_chrdev_region(&dev, 0, 1, "my_dev_2")) <0){ pr_err("Failed to allocate major number for device 1\n"); return -1; } pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev)); pr_info("Kernel Module Inserted!\n");
return 0;}/*** Module exit function*/static void __exit my_exit(void){ unregister_chrdev_region(dev, 1); printk(KERN_INFO "Kernel Module Removed Successfully...\n");} module_init(my_init);module_exit(my_exit); MODULE_LICENSE("GPL");MODULE_AUTHOR("feifei <feifei@gmail.com>");MODULE_DESCRIPTION("A simple linux device driver");
复制代码


编译运行:



关注微信公众号:Linux 内核拾遗

文章来源:https://mp.weixin.qq.com/s/aXehGqP3FBN0B9u-3my10g


发布于: 刚刚阅读数: 5
用户头像

聚沙成塔 2023-01-12 加入

分享Linux内核开发相关的编程语言、开发调试工具链、计算机组成及操作系统内核知识、Linux社区最新资讯等

评论

发布
暂无评论
Linux设备驱动系列(四)——设备号_linux开发_Linux内核拾遗_InfoQ写作社区