linux驱动:(14)平台总线模型 (platform设备 一)

目录

作用

 优点

注册platform设备

平台总线注册一个 device.c

用蜂鸣器举一个例子device.c

注册platform驱动driver.c

注册一个driver.c驱动 

platform_driver 结构体

用蜂鸣器举一个例子driver.c

运行步骤

下面我们就开始讲解prove函数,请看下一章


平台总线模型也叫platform总线模型,是linux内核虚拟出来的一条总线,不是真实的导线

平台总线是以名字来匹配的,实际上就是字符串比较,

主要就是把原来的驱动.c文件分为两个c文件,一个驱动,c,一个设备,c

  • 把稳定不变的放到驱动,c
  • 需要变得放在设备.c
  • 一个是 device.c,一个是 driver.c,然后分别注册 device.c和 driver.c
    • device是设备,主要处理与具体设备相关的配置和操作。这个文件通常包含平台设备的定义、资源分配、设备初始化以及设备注册等操作
      • 定义设备的硬件资源,如 I/O 端口、内存区域、中断号等
      • 设备初始化和退出函数。这些函数负责在设备加载和卸载时,进行硬件初始化和资源释放
      • 通过 platform_device_register 函数将设备注册到内核中,使得设备可以被相应的驱动程序识别并操作
    • driver是驱动,处理驱动程序的实现,负责与设备进行交互,管理设备的生命周期(例如初始化和清理),并提供设备与操作系统之间的接口
      • 定义驱动程序的 proberemove 回调函数,这些函数在设备被检测到和移除时被调用
      • 处理设备的注册和注销。通过 platform_driver_registerplatform_driver_unregister 函数将驱动程序注册到内核中
      • 实现设备的操作逻辑,如打开、关闭、读写操作等 

作用

  • 可以提高代码的重用性
  • 减少重复性代码

 优点

  • 驱动和设备代码分开
  • 都挂在总线上,方便管理

注册platform设备

平台总线注册一个 device.c

device.c里面写的是硬件资源,这里的硬件资源是指寄存器的地址,中断号,时钟等硬件
资源。

在linux中是用一个结构体来描述硬件资源,定义在linux/include/platform_device.h中

struct platform_device{
    const char *name;
    int id;
    bool id_auto;
    struct device dev;
    u32 num_resources;
    struct resource *resource;
    const struct platform_device_id *id_entry;
    char *driver_override;
    struct mfd_cell *mfd_cell;
    struct pdev_archdata archdata;
};
  • 主要参数
    • name:平台总线进行匹配的时候用到的name,并且在sys/bus/platform.devices/目录下会生成一个name文件
    • id:设备id,一般为1,相同name的设备编号
    • dev:设备通用储存的部分,内嵌的device机构提
    • numresources:资源的个数
    • resources:硬件资源
      struct resource{
          resource_size_t start;
          resource_size_t end;
          const char *name;
          unsigned long flags;
          struct resource *parent,*sibling,*child;
      };
      • start:资源的起始
      • end:资源的结束
      • name:资源的名字
      • flags:资源的类型
        • IORESOURCE_IO IO的内存
        • IORESOURCE_MEM 物理内存
        • IORESOURCE_IRQ 中断

用蜂鸣器举一个例子device.c

目标

  • 加载时注册一个名为 beep_test 的平台设备,并在卸载时注销该设备

步骤

  • 新建一个device.c
  • 先定义一个平台设备 beep_device 结构体
    • 设备的名称 beep_test 
    • 设备 ID 1
    • 设备的资源 beep_res
    • 资源的数量 num_resources
    • 释放函数也在 dev 字段中指定为 beep_release
  • 定义一个设备资源 beep_res 结构体
    • beep_res 是一个资源数组,用于描述设备使用的硬件资源
    • 只定义了一个资源
    • start 起始地址为不同芯片的数据寄存器的起始地址
    • end 结束地址为不同芯片的数据寄存器的结束地址
    • flags 设置为 IORESOURCE_MEM,表示这是一个内存资源
  • 定义一个设备 beep_release 释放函数
    • beep_release 函数是平台设备 beep_device 的释放函数,在设备被卸载时调用
  • 模块 beep_init 初始化和 beep_exit 退出函数
    • platform_device_register 注册平台设备 beep_device
    • platform_device_unregister 注销平台设备 beep_device
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h> 
#include <linux/platform_device.h> 

void beep_release(struct device *dev)
{
    printk("beep_release\n");
};

struct resource beep_res[] =
{
    [0] = {
        .start = 0x0000000,
        .end = 0x0000000,
        .flags = IORESOURCE_MEM,
        .name = "GPIO5_DR"
    }
};

struct platform_device beep_device =
{
    .name = "beep_test",
    .id = 1,
    .resource = beep_res,
    .num_resources = ARRAY_SIZE(beep_res),
    .dev = {
        .release = beep_release
    }

};
 
static int beep_init(void)
{
    printk("hello beep\n");
    return platform_device_register(&beep_device);
}
 
static void beep_exit(void)
{
    printk("beep goodbye");
    platform_device_unregister(&beep_device);
}
 
module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");

注册platform驱动driver.c

  • 首先定义一个 platform driver 结构体变量
  • 然后去实现结构体中的各个成员变量
  • 那么当我们的 driver 和 device 匹配成功的时候,就会执行 platform_driver 结构体中的 probe 函数,所以匹配成功以后的重点就在于 probe 函数的编写。

注册一个driver.c驱动 

platform_driver 结构体

struct platform_driver{
    int (*probe)(struct platform_device *);
    int (*remove)(struct platform device *);
    void (*shutdown)(struct platform_device *);
    int(*suspend)(struct platform_device *,pm_message_t state);
    int (*resume)(struct platform_device  *);
    struct device_driver driver;
    const struct platform_device_id *id_table;
    bool prevent_deferred_probe;
}
  • 最主要的就是probe函数,可以在driver与device匹配成功后,注册杂项设备或者点个灯都可以
  • remove:当driver与device其中一个remove时,就会调用
  • shutdown:当设备收到shutdown命令的时候,就会调用
  • suspend:当设备收到suspent(休眠)命令的时候,就会调用
  • resume:当设备收到resume(唤醒)命令的时候,就会调用
  • driver:
    struct device_driver 
    {
        const char*name;
        struct bus_fype *bus;
        struct module *owner;
        const char *mod_name;
        bool suppress_bind_attrs;
        const struct of_device_id *of_match_table;;
        const struct acpi_device_id *acpi_match_table;
        int(*probe)(struct device *dev);
        int(*remove)(struct device *dev);
        void(*shutdown)(struct device *dev);
        int(*suspend)(struct device *dev,pm_message_t state);
        int(*resume)(struct device *dev);
        const struct attribute_group **groups;
        const struct dev_pm_ops *pm;
        struct driver_private *p;
    };
    • 在这里我们只用到name和owner和of_match_table三个成员
    • name:匹配设备的名字,应该与上面的device.c里的platform_device结构体里的name相匹配,要不然driver与device不会互相匹配
    • owner:为THIS_MODULE
    • of_match_table:
  • id_table:platform_device_id 结构体里还有一个name成员,与driver的name意义一样,如果driver和id_table都有name,会优先匹配id_table的name
  • prevent_deferred_probe:

用蜂鸣器举一个例子driver.c

目标

  • 定义了一个平台驱动程序,在init中注册,用于管理一个名为 "beep_test" 的设备,有匹配到的话就会输出,

步骤

  • 新建一个driver.c
  • 定义一个平台驱动platform_driver结构体
  • 定义platform_driver结构体中的平台设备与平台驱动程序匹配函数
  • 定义platform_driver结构体中的平台设备被移除或驱动程序卸载函数
  • 定义platform_driver结构体中的driver成员
    • 赋值需要匹配的name,并与device中的name相同
    • 赋值owner为THIS_MODULE
  • 定义platform_driver结构体中的id_table成员,会优先匹配id_table里的name,如果查不到id_table里的name就不会有匹配的设备,就不会调用probe函数(可以自己定义来试一试)
  • 在intit函数中将平台驱动注册到内核中,内核会根据这个结构体中的信息来管理与其关联的平台设备
  • 在exit函数中进行注销,如果有匹配的设备,此时会调用 remove 函数
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h> 
#include <linux/platform_device.h> 

int beep_probe(struct platform_device *pdev)
{
    printk("beep_probe\n");
    return 0;
}

int beep_remove(struct platform_device *pedv)
{
    printk("beep_remove\n");
    return 0;
};
//const struct platform_device_id beep_id_table =
//{
//    .name = "123"
//};

struct platform_driver beep_device =
{
    .probe = beep_probe,
    .remove = beep_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "beep_test"
    },
    //.id_table = &beep_id_table
};

 
static int beep_driver_init(void)
{
    int ret = 0;
    ret = platform_driver_register(&beep_device);
    if (ret<0)
    {
        printk("platform_driver_register error\n");
        return ret;
    }
    
    printk("platform_driver_register ok\n");
    return 0;
}
 
static void beep_driver_exit(void)
{
    printk("beep goodbye");
    platform_driver_unregister(&beep_device);
}
 
module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");


运行步骤

  • 将上面的device.c和driver.c编译成模块
    • 需要注意的是device和driver中的name必须相同才能匹配
  • 加载这两个device和driver模块就可以看到匹配成功函数的输出 beep_probe
    • 需要注意的是无论先加载哪个都可以
  • 成功后就可以在probe函数中写需要完成的动作了,比如控制哪个io口,创建什么设备什么的

下面我们就开始讲解prove函数,请看下一章

  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码农小白

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

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

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

打赏作者

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

抵扣说明:

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

余额充值