• 1. cdev 结构体、设备号相关知识解析


    https://blog.csdn.net/zqixiao_09/article/details/50839042

    1、字符设备基础知识

    1.1、设备驱动分类linux系统将设备分为3类:字符设备,块设备,网络设备。使用驱动程序:

    》什么是字符设备:
    》》是指只能一个字节一个字节读写的设备
    》》不能随机读取设备内存中的某一数据
    》》读取数据需要按照先后顺序。
    字符设备是面向流的设备,常见的字符设备有鼠标,键盘,串口,控制台,LED设备等。

    》什么是块设备
    》》是指可以从设备的任意位置读取一定长度数据的设备。
    》》块设备包括硬盘,磁盘,U盘和SD卡等。

    每一个字符设备或者块设备都在/dev目录下对应一个设备文件。linux用户程序通过通过设备文件(或称设备节点)来使用驱动程序操作字符设备和块设备。

    1.2、字符设备、字符设备驱动与用户空间访问该设备的程序三者之间的关系

    如图:在linux内核中:

    * 使用cdev结构体来描述字符设备
    * 通过其成员dev_t来定义设备号(分为主次设备号)以确定字符设备的唯一性
    * 通过其成员file_operations来定义字符设备驱动提供给VFS的接口方式,如常见的open()、read()、write()等;
    

    在linux字符设备驱动中

    * 模块加载函数通过 register_chrdev_region( ) 或 alloc_chrdev_region( )来静态或者动态获取设备号
    * 通过 cdev_init( ) 建立cdev与 file_operations之间的连接,通过 cdev_add( ) 向系统添加一个cdev以完成注册;
    * 模块卸载函数通过cdev_del( )来注销cdev,通过 unregister_chrdev_region( )来释放设备号
    

    用户空间访问该设备的程序:

    * 通过linux系统调用,如open,read,write来“调用”file_operations来定义字符设备驱动提供给VFS的接口函数。
    

    1.3、字符设备驱动模型

    2、cdev 结构体解析
    在Linux内核中,使用cdev结构体来描述一个字符设备,cdev结构体的定义如下:

    内核给出的操作struct cdev结构的接口主要有以下几个:
    a -- void cdev_init(struct cdev *, const struct file_operations *);
    其源代码如代码清单如下:

    该函数主要对struct cdev结构体做初始化,最重要的就是建立cdev和file_operations之间的连接。

    * 将整个结构体清零
    * 初始化list成员使其指向自身
    * 初始化kobj成员
    * 初始化ops成员
    

    b --struct cdev *cdev_alloc(void);
    该函数主要分配一个struct cdev结构,动态申请一个cdev内存, 并做了cdev_init中所做的前面3步初始化工作(第四步初始化工作需要在调用cdev_alloc后,显式的做初始化即:.ops=xxx_ops

    在上面的两个初始化的函数中,我们没有看到关于owner成员,dev成员,count成员的初始化;其实,owner成员的存在体现了驱动程序与内核模块之间的亲密关系,struct module是内核对一个模块的抽象,该成员在字符设备中可以体现该设备隶属于那个模块,在驱动模块中一般有用户显示的初始化 .owner = THIS_MODULE, 该成员可以防止设备的方法正在被使用时,设备所在模块被卸载。而dev成员和count成员则在cdev_add中才会赋上有效的值。

    c -- int cdev_add(struct cdev *p, dev_t dev, unsigned count);
    该函数向内核注册一个struct cdev结构,即正式通知内核由struct cdev *p代表的字符设备已经可以使用了。当然这里还需提供两个参数:
    *第一个设备号dev
    *和该设备关联的设备编号的参数
    这两个参数直接赋值给struct cdev的dev成员和count成员。

    d -- void cdev_del(struct cdev *p);
    该函数向内核注销一个struct cdev结构,即正式通知内核由struct cdev *p代表的字符设备已经不可以使用了。
    从上述的接口讨论中,我们发现对于struct cdev的初始化和注册的过程中,我们需要提供几个东西
    * struct file_operations结构指针;
    *dev设备号
    *count此设备号个数

    3、设备号相应操作
    3.1 -- 主设备号和次设备号(二者一起为设备号):
    一个字符设备或块设备都有一个主设备号和一个次设备号。
    * 主设备号用了标识与设备文件相连的驱动程序,用来反映设备类型
    * 次设备号被驱动程序用来辨别操作的是哪个设备,用来区分同类型的设备。
    linux内核中,设备号用dev_t来描述,2.6.18中定义如下:

    在32位机中是4个字节,高12位表示主设备号,低20位表示次设备号。

    内核也为我们提供了结构方便操作的宏实现dev_t:

    1. -- 从设备号中提取major和minor

      • MAJOR(dev_t dev);
      • MINOR(dev_t dev);
    2. -- 通过major和minor构建设备号

      • MKDEV(int major, int minor);

    注:这只是构建设备号。并未注册,需要调用register_chrdev_region静态申请

    3.2、分配设备号(两种方法):
    a -- 静态申请:
    int register_chrdev_region(dev_t from, unsigned count, const char *name);
    其源代码清单如下:

    b -- 动态分配:
    int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
    其源代码清单如下:

    可以看到二者都是调用了__register_chrdev_region 函数,其源代码如下:


    通过这个函数可以看出register_chrdev_region和alloc_chrdev_region的区别,register_chrdev_region直接将Major注册进入,而alloc_chrdev_region从Major=0开始, 逐个查找设备号,直到找到一个闲置的设备号,并将其注册进入。

    可以看到,除了前面两个函数,还加了一个register_chrdev 函数,可以发现这个函数的应用非常简单,只要一句就可以搞定前面函数所做之事;
    下面分析一下register_chrdev 函数,其源代码定义如下

    调用了 __register_chrdev(major, 0, 256, name, fops) 函数:

    可以看到这个函数不只帮我们注册了设备号,还帮我们做了cdev 的初始化以及cdev 的注册;

    3.3、注销设备号:
    void unregister_chrdev_region(dev_t from, unsigned count);

    3.4、创建设备文件:
    使用cat /proc/devices查看申请到的设备名,设备号

    1>使用mknod手动吵架呢mknod filename type major minor
    1)使用mknod手工创建:mknod filename type major minor
    2)自动创建设备节点:
    利用udev(mdev)来实现设备文件的自动创建,首先用保证支持udev(mdev),由busybox配置。在驱动初始化代码里调用class_create为该设备创建一个class,再为每个设备调用device_create创建对应的设备
    详细解析见:Linux 字符设备驱动开发 (二)—— 自动创建设备节点

    下面看一个实例,练习一下上面的操作:
    hello.c


    测试程序 test.c

    makefile:

    编译成功后,使用 insmod 命令加载:
    然后用cat /proc/devices 查看,会发现设备号已经申请成功;

  • 相关阅读:
    PAT2019顶级7-2:美丽的序列(线段树+DP)
    ZOJ2112 Dynamic Rank(可持久化线段树套树状数组)
    CF1353E K-periodic Garland(动态规划)
    CF1353D Constructing the array(优先队列)
    HDU2069 Coin Change(基础DP)
    surf(树状数组+DP)
    双倍快乐(回文树)
    ZOJ3591 Nim(博弈论)
    HDU6601 Keep On EveryThing But Triangle(可持久化线段树)
    HDU6599 I Love Palindrome String(回文树)
  • 原文地址:https://www.cnblogs.com/Ocean-Star/p/9245568.html
Copyright © 2020-2023  润新知