平台总线的结构及框架分析

  • 平台总线是linux系统虚拟出来的一种总线,是一个内核子系统,负责管理 platform_device(硬件描述)和 platform_driver(驱动代码),使它们先分离.后搭档

平台总线(Platform Bus)(总线控制器信息和控制器驱动之间)

  • 平台总线(Platform Bus)是内核的一条“虚拟”总线。它不像 PCI、USB 那样是物理上存在的总线,而是为了解决一类特殊设备的驱动问题而设计的 软件机制。这类设备通常是 SoC (System on Chip) 芯片内部集成的、不可被自动识别的外设,比如 I2C 控制器、SPI 控制器、GPIO 控制器、LCD 控制器等。
  • 原理:设备与驱动的分离与匹配 (Separation and Matching)
    • 问题: 对于 PCI 或 USB 设备,设备自身带有 ID 信息(Vendor ID, Product ID)。驱动可以根据这些 ID “认领” 设备。但 SoC 上的那些外设,它们的寄存器地址、中断号都是固定的,写死在芯片里了,没法自动发现。

    • 解决:设备信息驱动代码 分开!”

      1. 平台设备 (platform_device): 这是一块纯粹的“数据”,用来描述硬件资源。它告诉内核:“在物理地址 0x12345678 有个设备,它使用中断号 5,它的名字叫 my-i2c-controller”。这些信息通常写在 设备树 (Device Tree, .dts 文件) 中,或者早期的板级配置文件 (board-xxx.c)里。
      2. 平台驱动 (platform_driver): 真正的驱动代码。注册时告诉内核:是一个驱动,我能处理名字叫 my-i2c-controller 的设备。
      3. 匹配 (Match): 当一个 platform_device 和一个 platform_driver 被注册到内核时,平台总线核心会进行匹配。最常见的匹配方式就是看 名字 是否一样。
      4. 探测 (Probe): 一旦匹配成功,总线核心就会调用平台驱动的 .probe 函数。在这个函数里,驱动程序会通过相关的API函数从 platform_device 结构体中获取到设备的硬件资源(如内存地址、中断号),然后用这些信息去初始化硬件,完成驱动的加载。
    • 流程:

      • 系统启动,内核解析设备树。
      • 内核在设备树里读到一段描述 I2C 控制器硬件的节点(包含了寄存器地址、中断号,以及最重要的 compatible = “vendor,i2c-controller-v1”;)。
      • 内核根据这个节点,创建并注册一个 platform_device 到平台总线。
      • I2C 控制器驱动(platform_driver)在加载时,会告诉平台总线:“我能处理 compatible 是 “vendor,i2c-controller-v1” 的设备”。
      • 平台总线看到两者匹配,于是调用 I2C 控制器驱动的 .probe 函数。
        • 在 I2C 控制器驱动的 .probe 函数中,驱动程序会执行一系列初始化操作,其中最重要的一步是调用 i2c_add_adapter() 或 i2c_add_numbered_adapter()。这个函数调用,才是在内核中“建立”或“注册”了一条 I2C 总线(即一个 i2c_adapter)。这条逻辑上的总线就代表了那条物理的 I2C 总线。内核里的 i2c_adapter 就是物理 I2C 总线在软件层面的抽象。
      • 设备间交互
        • “其他设备驱动”(比如 I2C 温度传感器驱动)不直接调用 I2C 控制器驱动里的 ops。这是一个分层概念。
        • 正确的交互方式:
          • I2C 控制器驱动把它实现底层 I/O 操作的 ops(struct i2c_algorithm)注册给了 I2C 总线核心。
          • I2C 温度传感器驱动想通信时,它调用的是 I2C 总线核心提供的标准、统一的 API,如 i2c_master_send() 和 i2c_master_recv()。
          • I2C 总线核心在收到这些 API 调用后,会找到对应的 i2c_adapter,然后去调用这个 adapter 在注册时提供的 ops 里的具体函数,最终由 I2C 控制器驱动的代码来操作硬件。

platform bus 设备和驱动

1. platform_device结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct platform_device {
const char *name; // 显示在/sys/bus/platform/devices/name.id(.auto)
int id; // 用来区分不同设备:name.id, id = -1: 没有后缀
bool id_auto; // 自动设置id:name.id.auto
struct device dev; // 设备的通用属性部分
u64 platform_dma_mask;
struct device_dma_parameters dma_parms;
u32 num_resources; // 存储的资源的个数
struct resource *resource; // 存储资源

const struct platform_device_id *id_entry;
/*
* Driver name to force a match. Do not set directly, because core
* frees it. Use driver_set_override() to set or clear it.
*/
const char *driver_override;

/* MFD cell pointer */
struct mfd_cell *mfd_cell;

/* arch specific additions */
struct pdev_archdata archdata;
};

成员结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
struct device {
struct kobject kobj;
struct device *parent;

struct device_private *p;

const char *init_name; /* initial name of the device */
const struct device_type *type;

const struct bus_type *bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this
device */
void *platform_data; /* Platform specific data, device
core doesn't touch it */
void *driver_data; /* Driver data, set and get with
dev_set_drvdata/dev_get_drvdata */
struct mutex mutex; /* mutex to synchronize calls to
* its driver.
*/

struct dev_links_info links;
struct dev_pm_info power;
struct dev_pm_domain *pm_domain;

#ifdef CONFIG_ENERGY_MODEL
struct em_perf_domain *em_pd;
#endif

#ifdef CONFIG_PINCTRL
#endif
struct dev_pin_info *pins;
struct dev_msi_info msi;
#ifdef CONFIG_ARCH_HAS_DMA_OPS
const struct dma_map_ops *dma_ops;
#endif
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */
u64 bus_dma_limit; /* upstream dma constraint */
const struct bus_dma_region *dma_range_map;

struct device_dma_parameters *dma_parms;

struct list_head dma_pools; /* dma pools (if dma'ble) */

#ifdef CONFIG_DMA_DECLARE_COHERENT
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
#endif
#ifdef CONFIG_DMA_CMA
struct cma *cma_area; /* contiguous memory area for dma
allocations */
#endif
#ifdef CONFIG_SWIOTLB
struct io_tlb_mem *dma_io_tlb_mem;
#endif
#ifdef CONFIG_SWIOTLB_DYNAMIC
struct list_head dma_io_tlb_pools;
spinlock_t dma_io_tlb_lock;
bool dma_uses_io_tlb;
#endif
/* arch specific additions */
struct dev_archdata archdata;

struct device_node *of_node; /* associated device tree node */
struct fwnode_handle *fwnode; /* firmware device node */

#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
dev_t devt; /* dev_t, creates the sysfs "dev" */
u32 id; /* device instance */

spinlock_t devres_lock;
struct list_head devres_head;

const struct class *class;
const struct attribute_group **groups; /* optional groups */

void (*release)(struct device *dev); // 必须编写,不然驱动编译不过去
struct iommu_group *iommu_group;
struct dev_iommu *iommu;

struct device_physical_location *physical_location;

enum device_removable removable;

bool offline_disabled:1;
bool offline:1;
bool of_node_reused:1;
bool state_synced:1;
bool can_match:1;
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
bool dma_coherent:1;
#endif
#ifdef CONFIG_DMA_OPS_BYPASS
bool dma_ops_bypass : 1;
#endif
#ifdef CONFIG_DMA_NEED_SYNC
bool dma_skip_sync:1;
#endif
#ifdef CONFIG_IOMMU_DMA
bool dma_iommu:1;
#endif
};

/*
* Resources are tree-like, allowing
* nesting etc..
*/
struct resource {
resource_size_t start; // 资源的起始信息和终止信息
resource_size_t end; // etc:中断的起始地址和终止地址
const char *name; // 存储信息名称:etc:中断-irq
unsigned long flags; // 存储资源类型:etc: IORESOURCE_IO/MEM/REG/IRQ/DMA/BUS...
unsigned long desc; // 描述信息
struct resource *parent, *sibling, *child; // 节点相关
};

2. 简单的platform_device

  • insmod注册成功:ls /sys/bus/platform/devices/mydevice
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>

static struct resource mydevice_resource[] = {
[0] = {
.start = 0xFDD60000,
.end = 0xFDD50004,
.flags = IORESOURCE_IO,
},
[1] = {
.start = 13,
.end = 13,
.flags = IORESOURCE_IRQ,
},
}

void mydevice_release(struct device *dev)
{
printk("This is mydevice_release\n");
}

static platform_device platform_device_test = {
.name = "mydevice",
.id = -1,
.resource = mydevice_resource,
.num_resources = ARRAY_SIZE(mydevice_resource),
.dev = {
.release = mydevice_release,
},
};

static int platform_device_init(void)
{
platform_device_register(&platform_device_test);

printk("platform_device init\n");
return 0;
}

static int platform_device_exit(void)
{
platform_device_unregister(&platform_device_test);

printk("platform_device exit\n");
return 0;
}

module init(platform_device_init);
module exit(platform_device_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("goko");
MODULE_VERSION("V1.0");

3. platform_driver结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct platform_driver {
int (*probe)(struct platform_device *); // 匹配成功之后执行
void (*remove)(struct platform_device *); // 设备移除时执行
void (*shutdown)(struct platform_device *); // 设备关闭时执行dy
int (*suspend)(struct platform_device *, pm_message_t state); // 设备挂起时执行dy
int (*resume)(struct platform_device *); // 设备恢复时执行dy
struct device_driver driver; // 设备公用的一些属性
const struct platform_device_id *id_table; // 设备id表
bool prevent_deferred_probe;
/*
* For most device drivers, no need to care about this flag as long as
* all DMAs are handled through the kernel DMA API. For some special
* ones, for example VFIO drivers, they know how to manage the DMA
* themselves and set this flag so that the IOMMU layer will allow them
* to setup and manage their own I/O address space.
*/
bool driver_managed_dma;
};

4. 简单的platform_driver

  • insmod注册成功:ls /sys/bus/platform/drivers/mydevice
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
#include <linux/module.h>      // 所有模块都需要
#include <linux/init.h> // __init 和 __exit 宏
#include <linux/fs.h> // file_operations 结构体和文件系统相关函数
#include <linux/cdev.h> // cdev 结构体和相关函数
#include <linux/uaccess.h> // copy_to_user, copy_from_user
#include <linux/device.h> // class_create, device_create
#include <linux/io.h> // ioremap, iounmap
#include <linux/platform_device.h> // platform_driver 和 platform_device
#include <linux/of.h> // of_match_ptr, 设备树相关(如果使用设备树匹配)
#include <linux/slab.h> // kzalloc, kfree

#define DRIVER_NAME "my_platform_device" // 定义驱动名称
#define DEVICE_COUNT 1 // 定义设备数量

// 驱动的私有数据结构体,用于存储设备相关的所有信息
// 这个结构体整合了你截图中`struct device_test`的所有成员
struct mydevice_dev {
dev_t dev_num; // 设备号 (主设备号 + 次设备号)
struct cdev cdev_test; // 字符设备结构体
struct class *class; // 设备类,用于在/sys/class/下创建条目
struct device *device; // 设备实例,用于在/dev/下创建设备文件
char kbuf[32]; // 内核缓冲区,用于与用户空间交换数据
void __iomem *vir_gpio_dr; // 经过ioremap映射后的虚拟地址
};

// 全局指针,指向我们的私有数据结构体
// 在probe中分配,在remove中释放
// 注意:更好的做法是通过 platform_set_drvdata/platform_get_drvdata 来管理,这里为了清晰展示,先用一个全局指针
// 稍后会展示更标准的做法
struct mydevice_dev *global_mydev;

// --- 文件操作函数集 (file_operations) ---

static int mydevice_open(struct inode *inode, struct file *file)
{
struct mydevice_dev *dev;

printk(KERN_INFO "mydevice: device opened\n");

// 通过 inode 中的 cdev 指针,找到包含它的父结构体 mydevice_dev
// 这是内核中非常常见和重要的技巧
dev = container_of(inode->i_cdev, struct mydevice_dev, cdev_test);

// 将设备私有结构体的指针存放在 file->private_data 中
// 这样,在后续的 read/write/release 操作中,就可以直接从 file 中获取,无需再次查找
file->private_data = dev;

return 0;
}

static int mydevice_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "mydevice: device closed\n");
// 这里不需要释放 file->private_data,因为它指向的是在 probe 中分配的内存
// 该内存的生命周期与驱动绑定,而不是与文件的打开/关闭绑定
return 0;
}

static ssize_t mydevice_read(struct file *file, char __user *buf, size_t size, loff_t *off)
{
// 从 file->private_data 中获取设备私有结构体指针
struct mydevice_dev *dev = file->private_data;
size_t len = strlen(dev->kbuf);
int ret;

printk(KERN_INFO "mydevice: reading data: %s\n", dev->kbuf);

if (size > len) {
size = len;
}

// 将内核空间的数据 (dev->kbuf) 拷贝到用户空间 (buf)
ret = copy_to_user(buf, dev->kbuf, size);
if (ret != 0) {
printk(KERN_ERR "mydevice: copy_to_user failed\n");
return -EFAULT; // 返回一个标准的错误码
}

// 在这里,一个简单的实现是每次读取后返回已读取的字节数
// 一个更完整的实现需要处理 *off,以支持多次读取文件的不同部分
return size;
}

static ssize_t mydevice_write(struct file *file, const char __user *buf, size_t size, loff_t *off)
{
// 从 file->private_data 中获取设备私有结构体指针
struct mydevice_dev *dev = file->private_data;
int ret;

if (size >= sizeof(dev->kbuf)) {
printk(KERN_WARNING "mydevice: write size is too large\n");
// 截断写入的数据,防止缓冲区溢出
size = sizeof(dev->kbuf) - 1;
}

// 将用户空间的数据 (buf) 拷贝到内核空间 (dev->kbuf)
ret = copy_from_user(dev->kbuf, buf, size);
if (ret != 0) {
printk(KERN_ERR "mydevice: copy_from_user failed\n");
return -EFAULT;
}

// 给内核缓冲区加上字符串结束符
dev->kbuf[size] = '\0';

printk(KERN_INFO "mydevice: written data: %s\n", dev->kbuf);

// 在一个真实的GPIO驱动中,这里会解析 kbuf 中的命令(如"on"或"off")
// 然后通过 dev->vir_gpio_dr 指针向硬件寄存器写入值
// 例如:iowrite32(1, dev->vir_gpio_dr);

return size; // 返回成功写入的字节数
}

// 定义 file_operations 结构体,并将我们的函数与之关联
static const struct file_operations mydevice_fops = {
.owner = THIS_MODULE,
.open = mydevice_open,
.release = mydevice_release,
.read = mydevice_read,
.write = mydevice_write,
};


// --- Platform 驱动核心函数 ---

// 当内核匹配到同名的 platform_device 时,会调用此 probe 函数
static int mydriver_probe(struct platform_device *pdev)
{
int ret;
struct resource *mem_res;
struct mydevice_dev *dev;

printk(KERN_INFO "mydriver_probe: device probed!\n");

// 1. 分配私有数据结构体内存
// 使用 devm_kzalloc, "devm_" 开头的函数是受设备管理的,当设备卸载时会自动释放资源,非常方便
dev = devm_kzalloc(&pdev->dev, sizeof(struct mydevice_dev), GFP_KERNEL);
if (!dev) {
return -ENOMEM;
}
global_mydev = dev; // 赋值给全局指针(仅为示例)

// 2. 从 platform_device 获取资源 (这里以内存资源为例)
// 参数: platform_device指针, 资源类型, 索引(第0个内存资源)
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem_res) {
printk(KERN_ERR "mydriver: failed to get memory resource\n");
return -EINVAL;
}
printk(KERN_INFO "mydriver: mem resource start: 0x%pa, size: %lld\n", &mem_res->start, resource_size(mem_res));

// 3. 将物理地址映射到内核虚拟地址空间
// devm_ioremap_resource 会自动处理 ioremap 和 iounmap,非常推荐使用
dev->vir_gpio_dr = devm_ioremap_resource(&pdev->dev, mem_res);
if (IS_ERR(dev->vir_gpio_dr)) {
printk(KERN_ERR "mydriver: failed to ioremap memory resource\n");
return PTR_ERR(dev->vir_gpio_dr);
}

// ======== 以下是字符设备创建的标准流程 (来自你的截图逻辑) ========

// 4. 动态申请设备号
ret = alloc_chrdev_region(&dev->dev_num, 0, DEVICE_COUNT, DRIVER_NAME);
if (ret < 0) {
printk(KERN_ERR "mydriver: failed to allocate chrdev region\n");
return ret;
}
printk(KERN_INFO "mydriver: allocated major=%d, minor=%d\n", MAJOR(dev->dev_num), MINOR(dev->dev_num));

// 5. 初始化 cdev 结构体,并绑定 file_operations
cdev_init(&dev->cdev_test, &mydevice_fops);
dev->cdev_test.owner = THIS_MODULE;

// 6. 将 cdev 添加到内核中
ret = cdev_add(&dev->cdev_test, dev->dev_num, DEVICE_COUNT);
if (ret < 0) {
printk(KERN_ERR "mydriver: failed to add cdev\n");
goto err_unregister_chrdev;
}

// 7. 创建设备类 /sys/class/my_platform_device
dev->class = class_create(THIS_MODULE, DRIVER_NAME);
if (IS_ERR(dev->class)) {
ret = PTR_ERR(dev->class);
printk(KERN_ERR "mydriver: failed to create class\n");
goto err_cdev_del;
}

// 8. 创建设备文件 /dev/my_platform_device
dev->device = device_create(dev->class, NULL, dev->dev_num, NULL, DRIVER_NAME);
if (IS_ERR(dev->device)) {
ret = PTR_ERR(dev->device);
printk(KERN_ERR "mydriver: failed to create device\n");
goto err_class_destroy;
}

// 9. 将私有数据结构体指针与 platform_device 关联
// 这样在 remove 函数中就可以通过 platform_get_drvdata 获取它
platform_set_drvdata(pdev, dev);

// 初始化内核缓冲区
strcpy(dev->kbuf, "Hello from kernel!");

printk(KERN_INFO "mydriver: probe successful, device created at /dev/%s\n", DRIVER_NAME);
return 0;

// 错误处理:按相反的顺序释放已申请的资源
err_class_destroy:
class_destroy(dev->class);
err_cdev_del:
cdev_del(&dev->cdev_test);
err_unregister_chrdev:
unregister_chrdev_region(dev->dev_num, DEVICE_COUNT);
// devm_kzalloc 和 devm_ioremap_resource 分配的资源会自动释放,无需手动处理
return ret;
}

// 当驱动被卸载或设备被移除时,调用此 remove 函数
static int mydriver_remove(struct platform_device *pdev)
{
// 通过 platform_get_drvdata 获取在 probe 中设置的私有数据
struct mydevice_dev *dev = platform_get_drvdata(pdev);

printk(KERN_INFO "mydriver_remove: removing device\n");

// 按照与 probe 相反的顺序销毁和释放资源
// 注意:devm_ 家族函数管理的资源(内存、ioremap)不需要在这里手动释放!
// 驱动核心会在这个函数返回后自动清理它们。

// 销毁设备文件 /dev/my_platform_device
device_destroy(dev->class, dev->dev_num);
// 销毁设备类 /sys/class/my_platform_device
class_destroy(dev->class);
// 从内核中删除 cdev
cdev_del(&dev->cdev_test);
// 注销设备号
unregister_chrdev_region(dev->dev_num, DEVICE_COUNT);

printk(KERN_INFO "mydriver_remove: remove successful\n");
return 0;
}

// ID 表,用于匹配 platform_device
// 当一个 platform_device 的 .name 字段与这里的 .name 匹配时,probe 就会被调用
static const struct platform_device_id mydriver_id_table[] = {
{ .name = "my-platform-device-example" }, // 这个名字需要与 platform_device 注册时使用的名字完全一致
{ /* sentinel */ }, // 结尾的空条目,表示列表结束
};
MODULE_DEVICE_TABLE(platform, mydriver_id_table); // 将id_table导出,让内核和用户空间知道

// 定义 platform_driver 结构体
static struct platform_driver my_platform_driver = {
.probe = mydriver_probe,
.remove = mydriver_remove,
.driver = {
.name = "my-platform-device-example", // 驱动的名字
.owner = THIS_MODULE,
},
.id_table = mydriver_id_table, // 关联ID匹配表
};

// 模块加载函数
static int __init my_driver_init(void)
{
printk(KERN_INFO "my_driver_init: Registering platform driver\n");
// 注册 platform_driver 到内核
return platform_driver_register(&my_platform_driver);
}

// 模块卸载函数
static void __exit my_driver_exit(void)
{
printk(KERN_INFO "my_driver_exit: Unregistering platform driver\n");
// 从内核中注销 platform_driver
platform_driver_unregister(&my_platform_driver);
}

// 注册模块加载和卸载函数
module_init(my_driver_init);
module_exit(my_driver_exit);

// 模块许可和信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A complete platform device driver example");
MODULE_VERSION("1.0");

平台总线和设备驱动

  1. 启动与准备 (第 0 步):

    • 内核启动,解析设备树。
    • 它看到一个描述 I2C 控制器的节点,于是创建了一个 platform_device
    • 它看到 I2C 控制器节点下还有一个描述温度传感器的子节点,于是为它创建了一个 i2c_client 的描述信息(但此时还未注册,因为 I2C 总线还不存在)。
  2. 第一层匹配 (平台总线):

    • 你加载了 I2C 控制器驱动 (platform_driver)。
    • 平台总线发现这个驱动和之前创建的 platform_device 匹配。
    • 平台总线调用 I2C 控制器驱动.probe() 函数。
  3. 桥梁搭建 (控制器驱动的工作):

    • I2C 控制器驱动.probe() 函数中,驱动初始化了硬件,然后调用 i2c_add_adapter()
    • 这个调用是关键! 它在内核里创建并注册了一条功能完备的 I2C 总线
  4. 第二层匹配 (I2C 总线):

    • 新的 I2C 总线被注册后,内核的 I2C 核心会把之前为温度传感器准备的 i2c_client 描述信息,正式注册到这条新的 I2C 总线上。
    • 现在,你加载了温度传感器驱动 (i2c_driver)。
    • I2C 总线发现这个驱动和刚刚注册的 i2c_client 匹配。
    • I2C 总线调用温度传感器驱动.probe() 函数。
  5. 最终通信 (设备驱动的工作):

    • 温度传感器驱动.probe() 或其他函数里,它想读取温度。
    • 它调用一个标准的、与硬件无关的函数 i2c_master_recv()
    • I2C 核心收到调用,查找该设备挂在哪条 I2C 总线上。
    • 它找到了由I2C 控制器驱动注册的那条总线,然后调用了该控制器驱动提供的底层传输函数。
    • I2C 控制器驱动的代码开始执行,通过操作寄存器来命令物理 I2C 控制器去和物理温度传感器通信,并取回数据。

平台总线的结构及框架分析

https://goko-son626.github.io/post/Platform-bus.html

作者

GoKo Mell

发布于

2024-06-07

更新于

2025-09-11

许可协议

评论

:D 一言句子获取中...