当前位置: 首页 > news >正文

linux学习--总线设备驱动模型

跟着韦东山老师学习的linux,在到了驱动这里,算是有了一些疑惑,在这里记录下来。代码如下:
driver.c

#include <linux/module.h>
#include <linux/platform_device.h>static int my_driver_probe(struct platform_device *pdev)
{struct resource *res_mem, *res_irq;pr_info("Driver probe called for device: %s\n", pdev->name);// 在这里你就可以用 platform_get_resource() 拿硬件信息res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);if (res_mem)pr_info("MEM resource: start=0x%lx end=0x%lx name=%s\n",(unsigned long)res_mem->start, (unsigned long)res_mem->end, res_mem->name);if (res_irq)pr_info("IRQ resource: start=%lu name=%s\n",(unsigned long)res_irq->start, res_irq->name);return 0;
}static int my_driver_remove(struct platform_device *pdev)
{pr_info("Driver remove called for device: %s\n", pdev->name);return 0;
}static struct platform_driver my_driver = {.probe  = my_driver_probe,.remove = my_driver_remove,.driver = {.name = "my_demo_dev",   // 一定要匹配 device 的名字.owner = THIS_MODULE,},
};static int __init my_driver_init(void)
{pr_info("Registering platform driver: my_demo_dev\n");return platform_driver_register(&my_driver);
}static void __exit my_driver_exit(void)
{pr_info("Unregistering platform driver: my_demo_dev\n");platform_driver_unregister(&my_driver);
}module_init(my_driver_init);
module_exit(my_driver_exit);MODULE_LICENSE("GPL");

device.c

#include <linux/module.h>
#include <linux/platform_device.h>
static struct resource my_resources[] = {{.start = 0x10000000,.end   = 0x10000fff,.flags = IORESOURCE_MEM,.name  = "my_demo_mem",},{.start = 42,.end   = 42,.flags = IORESOURCE_IRQ,.name  = "my_demo_irq",}
};
static void dummy_dev_release(struct device *dev)
{pr_info("dummy_dev_release called\n");
}static struct platform_device my_device = {.name = "my_demo_dev",   // 用于匹配 driver 名字.id   = -1,.dev  = { .release = dummy_dev_release },.resource = my_resources,.num_resources = ARRAY_SIZE(my_resources),
};static int __init my_device_init(void)
{pr_info("Registering platform device: my_demo_dev\n");platform_device_register(&my_device)return 0;
}static void __exit my_device_exit(void)
{pr_info("Unregistering platform device: my_demo_dev\n");platform_device_unregister(&my_device);
}module_init(my_device_init);
module_exit(my_device_exit);MODULE_LICENSE("GPL");

Makefile

# 我们希望生成两个 .ko 模块
obj-m := device.o driver.o# 内核构建目录
KDIR := /lib/modules/$(shell uname -r)/build
PWD  := $(shell pwd)# 本地编译(不交叉编译),指定空的 CROSS_COMPILE
all:$(MAKE) ARCH=x86_64 CROSS_COMPILE="" -C $(KDIR) M=$(PWD) modulesclean:$(MAKE) -C $(KDIR) M=$(PWD) clean

make后,加载两个ko后,即可看到结果。
以下是我的一些疑惑:

为什么 driver 里用 platform_get_resource(pdev, IORESOURCE_MEM, 0),而不是直接访问 pdev->resource?

pdev->resource 是 platform_device 结构内部的资源数组指针,但这是设备核心结构的私有数据,driver 不建议直接访问成员变量。
通过 platform_get_resource() 接口访问资源是内核官方约定的安全用法:

struct resource *platform_get_resource(struct platform_device *pdev,unsigned int type, unsigned int idx);

参数说明:

pdev —— 指向当前 platform 设备结构
type —— 资源类型,比如 IORESOURCE_MEM (内存区域)、IORESOURCE_IRQ (中断线) 等
idx —— 该类型资源的第几个。例如 idx=0 是该类型的第一个资源,如果你设备有多个同类型资源,idx=1 是第2个,以此类推。

resource 结构体介绍

static struct resource my_resources[] = {{.start = 0x10000000,       // 资源起始地址(物理地址或者中断号等).end   = 0x10000fff,       // 资源结束地址(或同中断起始一致).flags = IORESOURCE_MEM,   // 资源类型标记:内存、IO、中断等.name  = "my_demo_mem",    // 资源名字,仅供分析调试},{.start = 42,               // IRQ 号 42.end   = 42,.flags = IORESOURCE_IRQ,.name  = "my_demo_irq",}
};

如果有多个同类型资源,如何获取?

举例:某设备有两个内存区域和一个中断号:

static struct resource my_resources[] = {{ .start=0x10000000, .end=0x10000fff, .flags=IORESOURCE_MEM, .name="mem0" },{ .start=0x10001000, .end=0x10001fff, .flags=IORESOURCE_MEM, .name="mem1" },{ .start=42,          .end=42,          .flags=IORESOURCE_IRQ, .name="irq" },
};

驱动中访问时,传递第三个参数 idx 来访问:

struct resource *mem0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); // 第一个mem资源
struct resource *mem1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); // 第二个mem资源
struct resource *irq  = platform_get_resource(pdev, IORESOURCE_IRQ, 0); // 第一个irq资源

如果你写错了索引,比如访问 IORESOURCE_MEM, 2,返回 NULL,因为没有第 3 个内存资源。

多个设备如何区分处理?

假设你有多个设备:

static struct platform_device my_device1 = {.name = "my_demo_dev",.id   = 0,.resource = my_resources1,.num_resources = ARRAY_SIZE(my_resources1),// ...
};static struct platform_device my_device2 = {.name = "my_demo_dev",.id   = 1,.resource = my_resources2,.num_resources = ARRAY_SIZE(my_resources2),// ...
};

驱动 probe 里可以通过 pdev->id 或 pdev->name 来区分:

static int my_driver_probe(struct platform_device *pdev)
{pr_info("Probing device id=%d name=%s
", pdev->id, pdev->name);if (pdev->id == 0) {// 设备0特有初始化} else if (pdev->id == 1) {// 设备1特有初始化}// 资源获取等通用代码struct resource *res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);// ...return 0;
}

每一个设备注册后都会调用驱动的 probe函数

Linux设备模型(bus + device + driver)设计是:

  • 驱动程序注册后不会主动运行 probe,只有系统检测到 对应设备被注册 且匹配成功,才会调用驱动 probe。
  • 每个匹配的设备都会独立调用一次 probe 函数,并且在 probe 函数里,传入的是匹配的那个 platform_device *pdev 指针。
  • 所以如果你注册了两个 my_demo_dev 设备,probe 会调用两次,分别传入两台设备的指针。

flags 有哪些?含义和场景

struct resource 中的 flags 用来描述资源类型及属性,常见的宏定义在 <linux/ioport.h>。以下整理部分常用 flag:

宏名说明典型场景
IORESOURCE_MEM内存映射资源区通常是设备的寄存器物理地址区域
IORESOURCE_IOIO 端口资源(x86 用较多设备 IO 端口地址映射
IORESOURCE_IRQ中断资源设备使用的中断号
IORESOURCE_DMADMA 资源设备用的DMA通道
IORESOURCE_PREFETCH该内存区域可预取给 CPU 优化提示
IORESOURCE_CACHEABLE该内存区域可缓存同上
IORESOURCE_DISABLED该资源禁用设备掉电或者硬件不在线时使用
IORESOURCE_BUSY资源已被占用设备在使用该资源时状态标记
IORESOURCE_READONLY资源只读某些只读资源区
你也可以查看 kernel 源码 include/linux/ioport.h,里面定义了更多标志及宏。

如果两个设备的初始化函数不一样,怎么办?

平台驱动的 probe 函数是所有匹配设备的统一入口,如果多个设备的初始化逻辑差异较大,可以有下面做法:

在相同 probe 内区分设备(最常用)

利用 pdev->id 或者 pdev->name,根据ID或者名称区分后分别执行对应的初始化代码。
示例:

static int my_driver_probe(struct platform_device *pdev)
{pr_info("probe device: name=%s id=%d", pdev->name, pdev->id);if (pdev->id == 0) {pr_info("Init device 0 specific code");// 初始化逻辑1} else if (pdev->id == 1) {pr_info("Init device 1 specific code");// 初始化逻辑2} else {pr_warn("Unknown device id %d", pdev->id);}return 0;
}

方案B:使用设备树/ID表 区分多设备驱动(更高级)

设备树或者 Platform ID 表里定义多个设备描述,driver 用匹配表匹配不同设备,probe 中根据匹配表传入的上下文(id_entry->data)区别初始化。
如果有误、希望大家多多指导!

http://www.dtcms.com/a/542454.html

相关文章:

  • 佛山 网站建设培训班成品app直播源码
  • 开发网站和application2019做网站图片用什么格式
  • OpenHarmony内核中HDF内核态驱动khdf编译流程
  • 旅游商城网站订单处理网站建设所需美工
  • 深圳学校网站建设公司网站首页列表布局设计
  • 网站降权了怎么办虾皮购物网站怎么做
  • MC SDK V6.x 软件HSO功能ADC采样设计说明 LAT1560
  • 域名备案 网站备案淘宝网站推广方案
  • 华尔街之狼,与AI共舞
  • 图书馆管理网站建设logo菏泽炫佑网站建设
  • 网站营销方案设计公司怎样才能制做免费网站
  • Web身份认证 --- OAuth授权机制
  • 南京工程建设招聘信息网站网站设计师薪资参考
  • 国外做伞的品牌网站有偷菜餐厅城市建设的网站
  • 大流量网站解决访问量那些网站是asp做的
  • 班级网站建设步骤中国建设银行总行网站
  • 加法器进位的那些事
  • 江苏网站建设基本流程建站服务外贸
  • 免费网站推广产品百度网站怎么做
  • 网站开发建设需要多少钱建立网站的风险
  • ModbusRtc与ModbusTCP,esp32
  • 正规的网站制作服务电话社区论坛源码
  • 策划方案免费的网站oa系统办公软件怎么用
  • joomla 网站模板国内10大猎头公司排名
  • Python学习(12) ----- Python的异步操作
  • 什么腾讯网站做任务能刷q币百度搜索网站优化
  • 哈尔滨网站建设效果广州市建设集团网站首页
  • 平顶山网站开发wordpress程序 耗内存
  • 诸暨网站制作asia 域名 知名网站
  • python做直播网站网络推广公司招聘