简单入门linux设备驱动之第四部分:字符设备驱动major号 & minor号

声明:内容搬自阿三哥网站,只是翻译了一下。侵删。https://embetronicx.com/tutorials/linux/device-drivers/

正文如下:

这是“linux设备驱动系列”教程的续集,并且接着讨论字符驱动程序及其实现。本系列的目的是提供简单实用的示例,使每个人都能以简单的方式理解这些概念。现在让我们即将学习“linux设备驱动第四部分:字符设备驱动major号 & minor号”。

 在之前的教程中,我们讨论了如何在加载时向Linux设备驱动程序传递参数。所以现在让我们来学习有关major号和minor号的知识。

内容速览

·1 简介

·2 应用程序如何与硬件设备通信?

·3 字符设备驱动major号与minor号
·4 major号与minor号

  ·4.1 major号

  ·4.2 minor号
·5 分配major号与minor号

  ·5.1 静态分配

  ·5.2 动态分配
  ·5.3 静态和动态方法的区别
·6 注销major号与minor号
·7 静态分配major号程序示例
·8 动态分配minor号程序示例
 
 

·1 简介

我们已经知道了什么是驱动程序,以及为什么我们需要驱动程序。字符驱动程序有什么特殊的吗?如果我们编写面向字节操作的驱动程序,我们就将其视为字符驱动程序。由于大多数设备都是面向字节的,大多数设备驱动程序都是字符设备驱动程序。例如,串行驱动程序,音频驱动程序,视频驱动程序,摄像头驱动程序,以及基本I/O驱动程序。事实上,

设备驱动程序中,既不是存储设备驱动程序,也不是网络设备驱动程序的话,就属于字符驱动程序啦。

·2 应用程序如何与硬件设备通信?

下图展示了通信的完整路径。

简单入门linux设备驱动之第四部分:字符设备驱动major号 & minor号

 ·首先,应用程序会打开设备文件。这个设备文件是设备驱动程序创建的。

 ·然后这个设备文件会根据major号和minor号来找到对应的设备驱动程序。

 ·然后找到的这个设备驱动程序就可以跟硬件设备交流。

字符设备驱动major号与minor号

Linux内核的基本特性之一就是抽象对设备的处理。所有的硬件设备看起来都像是普通文件;可以使用相同的,标准的,用来操作文件的系统调用(system call)来打开(open),关闭(close),读(read),写(write)他们。对于Linux来说,一切皆文件。往硬盘写入数据时,其实是往文件写入。从键盘读取数据时,其实是向文件读取。保存磁带设备的备份时,其实是往文件里写入。甚至读取内存数据时也是从文件中读取。假如你想读或写的文件是一个普通文件,过程非常好理解:文件被打开,然后你读取或者写入数据。设备驱动程序与普通文件也差不多。驱动程序会为每个硬件设备创建一个特殊文件(与普通文件相对)。我们通过这些特殊文件(设备文件)与硬件沟通。

如果你想创建一个特殊文件,我们得了解设备驱动程序的major号与minor号相关的知识。在此节教程中我们会了解major和minor号。

·4 major号与minor号

 Linux内核用一对数字来表示字符和块设备 <major> : <minor>

·4.1 major号

 传统来说,major号用来标识与设备相关联的驱动程序。一个major可以被许多设备驱动程序共享。在 /proc/devices 中我们可以看到,在一个运行中的Linux实例是怎么分配major号的。

 简单入门linux设备驱动之第四部分:字符设备驱动major号 & minor号

 这些数字是major号。

·4.2 minor号

 major号用来标识相应的驱动程序。许多设备使用相同的major号。所以我们需要为使用相同major号的设备单独再分配一个数字。这就是minor号啦。换句话说,设备驱动程序使用minor号<minor>来辨别单个物理或逻辑设备。

·5 分配major号与minor号

我们有两种方法分配major号和minor号。

·静态分配

·动态分配

·5.1 静态分配

假如你想为你的驱动程序设置一个特别的major号,你可以使用这种方法。如果major号可用的话,这种方法会将major号分配给你。假如不可用,就分配失败。

  int register_chardev_refion(dev_t first, unsigned int count, char *name);

 

first  是你想要分配设备号范围的起始数字。 

count 是你请求的连续设备号的总数。请注意,如果count过大,你请求的范围会溢出至下一个major号;但是只要你请求的数字范围可用,一切都可以正常工作。

name 是与请求的设备号范围([first, first+count])相关联的设备名称。它将会在 /proc/devices sysfs 目录中出现。

 申请成功的话,函数 register_chrdev_region 的返回值为零。出错的话,会返回一个负的错误码,并且不能访问所申请的设备号范围。

dev_t 数据类型(在<linux/types.h>中定义)用来存储设备号的major和minor部分。dev_t 是32位的数据,12bits用于表示major号,20bits用来表示minor号。

假如你想创建dev_t结构体变量表示你的major号和minor号,请使用下面这个函数。

MKDEV(int major, int minor);

假如你想从dev_t变量中获取major号和minor号,请使用以下方法。

MAJOR(dev_t dev);

MINOR(dev_t dev);

如果你将dev_t变量传入MAJOR 或者MINOR函数,他会返回驱动程序的major号/minor号。

举个栗子,

dev_t dev = MKDEV(235, 0);

register_chrdev_region(dev, 1, "Embetronicx_Dev");

·5.2 动态分配

 假如我们不想要固定的major号和minor号请使用这种方法。这种方法会为驱动程序动态分配可用的major号。

  int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);

dev 是一个输出参数,成功执行时会返回分配范围的第一个值。

firstminor 应为请求使用的第一个minor号,通常为0。

count 为您请求的连续设备号总数。

name 时与分配的设备号相关联的设备名称。他会在 /proc/devices sysfs 中出现。

·5.3 静态和动态方法的区别

 静态方法只有在你提前知道你想从哪一个major号开始时真的有用。使用静态方法时,你告诉内核你想要的设备号(起始major/minor号以及count)然后内核分配给你或不分配(取决于这些设备号是否可用)。

使用动态方法时,你告诉内核你需要多少设备号(起始minor号以及count)然后他会为你找到一个起始major号,如果他是可用的。

有些人为了避免与其他设备驱动程序产生冲突,会优先选择使用动态方法函数,它会为你动态分配设备号。

 动态分配的缺点就是你不能提前创建设备节点,因为分配给你的major号可能会变化。对于驱动程序的正常使用来说,这不算是个问题。因为一旦分配了设备号,你可以从 /proc/devices 中读取。

·6 注销major号与minor号

不论你是怎样分配你的设备号的,当你不再使用他们的时候应该释放掉。设备号使用下面函数释放:

void unregister_chrdev_region(dev_t first, unsigned int count);

通常将unregister_chrdev_region放在模块的退出函数中。

 ·7 静态分配major号程序示例

GitHub中获取源代码

在此程序中,我指定235作为major号。

/***************************************************************************//**
*  file       driver.c
*
*  details    Simple linux driver (Statically allocating the Major and Minor number)
*
*  author     EmbeTronicX
*
* *******************************************************************************/
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
#include <linux/fs.h>

//creating the dev with our custom major and minor number
dev_t dev = MKDEV(235, 0);

/*
** Module Init function
*/
static int __init hello_world_init(void)
{
    register_chrdev_region(dev, 1, "Embetronicx_Dev");
    printk(KERN_INFO "Major = %d Minor = %d 
",MAJOR(dev), MINOR(dev));
    printk(KERN_INFO "Kernel Module Inserted Successfully...
");
    return 0;
}

/*
** Module exit function
*/
static void __exit hello_world_exit(void)
{
    unregister_chrdev_region(dev, 1);
    printk(KERN_INFO "Kernel Module Removed Successfully...
");
}
 
module_init(hello_world_init);
module_exit(hello_world_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
MODULE_DESCRIPTION("Simple linux driver (Statically allocating the Major and Minor number)");
MODULE_VERSION("1.0");


·使用makefile生成驱动程序(sudo make)

·使用 sudo insmode 加载驱动程序

·使用 cat /proc/devices 检查major号

linux@embetronicx-VirtualBox::/home/driver/driver$ cat /proc/devices | grep "Embetronicx_Dev"

235 Embetronicx_Dev
 

 ·8 动态分配major号程序示例

GitHub中获取源代码

此程序会动态分配一个major号。

/***************************************************************************//**
*  file       driver.c
*
*  details    Simple linux driver (Dynamically allocating the Major and Minor number)
*
*  author     EmbeTronicX
*
* *******************************************************************************/
#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;

/*
** Module Init function
*/
static int __init hello_world_init(void)
{
        /*Allocating Major number*/
        if((alloc_chrdev_region(&dev, 0, 1, "Embetronicx_Dev")) <0){
                printk(KERN_INFO "Cannot allocate major number for device 1
");
                return -1;
        }
        printk(KERN_INFO "Major = %d Minor = %d 
",MAJOR(dev), MINOR(dev));
        printk(KERN_INFO "Kernel Module Inserted Successfully...
");
        
        return 0;
}

/*
** Module exit function
*/
static void __exit hello_world_exit(void)
{
        unregister_chrdev_region(dev, 1);
        printk(KERN_INFO "Kernel Module Removed Successfully...
");
}
 
module_init(hello_world_init);
module_exit(hello_world_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
MODULE_DESCRIPTION("Simple linux driver (Dynamically allocating the Major and Minor number)");
MODULE_VERSION("1.1");

·使用makefile生成驱动程序(sudo make)

·使用 sudo insmode 加载驱动程序

·使用 cat /proc/devices 检查major号

linux@embetronicx-VirtualBox::/home/driver/driver$ cat /proc/devices | grep "Embetronicx_Dev"

243 Embetronicx_Dev

函数分配的major号243给这个驱动程序。

在卸载驱动程序之前使用 ls /dev/ 检查下/dev下的文件。你找不到我们的驱动程序文件。因为我们现在还没有创建它呢。在下一小节的教程里,我们会看到设备文件。

---------------------分割线-------------------