zoukankan      html  css  js  c++  java
  • 简单入门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 应用程序如何与硬件设备通信?

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

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

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

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

    ·3 字符设备驱动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号的。

     

     这些数字是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下的文件。你找不到我们的驱动程序文件。因为我们现在还没有创建它呢。在下一小节的教程里,我们会看到设备文件。

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

    -------------BE SEXY AND BRAINY---------
  • 相关阅读:
    初级Linux学习指南
    2016/09/29 SQL中的join
    2016/09/27 Hadoop Yarn
    2016/06/27 HDFS概述
    2016/09/26 电能和电功率
    【转】2016/09/22 MapReduce初级案例
    2016/09/22 mapreduce
    2016/09/21 java关键字static
    2016/09/21 Java关键字final
    2016/09/21 java split用法
  • 原文地址:https://www.cnblogs.com/yuxiaolan/p/15055628.html
Copyright © 2011-2022 走看看