驱动-兼容不同设备-container_of

驱动兼容不同类型设备
在 Linux 驱动开发中,container_of 宏常被用来实现一个驱动兼容多种不同设备的架构。这种设计模式在 Linux 内核中非常常见,特别
是在设备驱动模型中。linux内核的主要开发语言是C,但是现在内核的框架使用了非常多的面向对象的思想,这就面临了一个用C语言
来实现面向对象编程的问题


参考资料

在字符设备这块内容,所有知识点都是串联起来的,需要整体来理解,缺一不可,建议多了解一下基础知识
驱动-申请字符设备号
驱动-注册字符设备
驱动-创建设备节点
驱动-字符设备驱动框架
驱动-杂项设备
驱动-内核空间和用户空间数据交换
驱动-文件私有数据

典型应用场景

  • 同一厂商的不同型号设备
  • 功能相似但寄存器布局不同的设备
  • 需要维护设备特定数据的场合

原理:利用结构体中元素指针获取结构体指针

Kobject是linux设备驱动模型的基础,也是设备模型中抽象的一部分。

linux内核为了兼容各种形形色色的设备,就需要对各种设备的共性进行抽象,抽象出一个基类,其余的设备只需要继承此基类就可以了。

而此基类就是kobject(暂且把它看成是一个类),但是C语言没有面向对象语法。

在C++中这样的操作非常简单,继承基类就可以了,而在C语言中需要将基类的结构体指针嵌入到派生的类中,那么为什么将基类指针嵌入就可以得到派生类的指针呢?

这个实现是一个宏:container_of。

container_of 函数

container_of 在 Linux 内核中是一个常用的宏, 用于从包含在某个结构中的指针获得结构本
身的指针, 通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地
址。 那么可以使用这个函数获取不同设备的地址, 来对不同的设备进行操作, 从而一个驱动可
以兼容不同的设备。

container_of 

函数原型:
    container_of(ptr,type,member)
函数作用:
   通过结构体变量中某个成员的首地址获取到整个结构体变量的首地址。
参数含义:
  ptr 是结构体变量中某个成员的地址。
  type 是结构体的类型
  member 是该结构体变量的具体名字

container_of 宏的作用是通过结构体内某个成员变量的地址和该变量名, 以及结构体类型。
找到该结构体变量的地址

理解

C程序例子

 #include <stdio.h>
 
 struct Base{
   
                       //定义一个Base类
     int var;
     char *string;
 };
 
 struct Derived{
   
                    //定义一个派生类
     int var;
     struct Base base;           //派生类中包含了struct Base类
 };

 #define offsetof(TYPE, MEMBER)	((size_t)&((TYPE *)0)->MEMBER)       //offset_of宏
 #define container_of(ptr, type, member) ({
   
   				\            //阉割版container_of,省去了类型检查
     void *__mptr = (void *)(ptr);                     \
     ((type *)(__mptr - offsetof(type, member))); })
 
 struct Base *base_p;                  
 struct Derived test_derived;       
 
 
 struct Derived *get;
 int main()
 {
   
   
    base_p = &test_derived.base;                                     //赋值给指针
    printf("Derived addr = %x\n",(unsigned int)&test_derived);
    printf("=================================\n");
    get = container_of(base_p,struct Derived,base);                 //使用base_p指针获取派生类的首地址
    printf("get Derived addr = %x\n",(unsigned int)get);

    return 0;
 }

输出结果:


输出如下:
Derived addr = 601050
=================================
get Derived addr = 601050

源码分析:最重要的就是理解 container_of 函数

    get = container_of(base_p,struct Derived,base);                 //使用base_p指针获取派生类的首地址

  
  


  • base_p :ptr 是结构体变量中某个成员的地址。 这个base_p 是怎么定义的 struct Base *base_p; Base 又是在哪里定义的呢? 作为派生类Deviced 的成员变量 struct Base base

  • struct Derived: type 是结构体的类型,不就是派生类的结构体类型吗? 这个函数就是要返回这个结构体类型的指针

  • base: member 是该结构体变量的具体名字。 就是派生类里面 定义的成员变量名字。

我们这里讨论和接下来讨论的目的就是要理解这个函数container_of的作用,理解参数和返回类型 以及理解实际应用的价值。

实验

使用 container_of 函数编写一个驱动兼容不同设备

源码程序 file.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

struct device_test
{
   
   

    dev_t dev_num;         // 设备号
    int major;             // 主设备号
    int minor;             // 次设备号
    struct cdev cdev_test; // cdev
    struct class *class;   // 类
    struct device *device; // 设备
    char kbuf
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野火少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值