Yuhang Zheng

第十三节、平台总线模型——注册platform设备

N 人看过

本节用于介绍平台总线模型——注册platform设备

1、什么是平台总线模型?

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

平台总线模型就是把原来的驱动C文件给分成了俩个C文件,一个是device.c,一个是driver.c

把稳定不变的放在driver.c里面,需要变得就放在了device.c里面。

2、为什么会有平台总线模型?

  1. 可以提高代码的重用性
  2. 减少重复性代码。
设备 总线 驱动
device.c driver.c

3、平台总线模型的优点。

4、怎么编写以平台总线模型设计的驱动?

一个是device.c,一个是driver.c,然后分别注册 device.c和 driver.c

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


1、平台总线注册一个platform_device

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

在Linux内核里面,我们是用一个结构体来描述硬件资源的。

文件位置路径:include/linux/platform_device.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct platform_device {
const char *name; //常用,用于平台设备与平台驱动的匹配,会在/sys/bus/platform/devices/下生成name的节点
int id; //常用,设备id,一般为-1,为相同name的设备进行编号的
bool id_auto;
struct device dev; //常用,内嵌的device结构体,
u32 num_resources; //常用,资源的个数
struct resource *resource; //常用,device里面的硬件资源,可以看到这是一个指针,指向的是resource结构体数组

const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */

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

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

2、填写platform_device里面的resource硬件资源结构体数组

文件位置路径:include/linux/ioport.h

1
2
3
4
5
6
7
8
struct resource {
resource_size_t start; //资源的起始地址
resource_size_t end; //资源的结束地址
const char *name; //资源的名字
unsigned long flags; //资源的类型
unsigned long desc;
struct resource *parent, *sibling, *child;
};

这里面的资源的类型有哪些呢?下面是几个常用的类型

1
2
3
4
5
#define IORESOURCE_IO           //IO的内存
#define IORESOURCE_MEM //最常用,表示一段地址
#define IORESOURCE_IRQ //最常用,表示中断号
#define IORESOURCE_DMA //表示DMA的地址
#define IORESOURCE_BUS //表示总线号

注意:platform_device结构体内的内嵌的device结构体中如果void (*release)(struct device *dev);函数没有实现的话,可能会有内核报警。

1
[ 2901.445771] Device 'beep_test' does not have a release() function, it is broken and must be fixed.

3、填写platform_device里面的device结构体

文件位置路径:include/linux/device.h

1
2
3
4
5
struct device {
...
void (*release)(struct device *dev);
...
}

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void beep_release(struct device *dev)
{
printk("beep_release\n");
}

struct resource beep_res[] = { //resource是一个结构体数组。这个是结构体数组赋值的方式
[0] = {
.start = 0x02310008,
.end = 0x0231000b,
.name = "GPDAT_1",
.flags = IORESOURCE_MEM
}
};

struct platform_device beep_device = {
.name = "beep_test",
.id = -1,
.dev = {
.release = beep_release //函数没有实现的话,可能会有内核报警。这个是结构体内结构体赋值的方式
},
.num_resources = ARRAY_SIZE(beep_res), //资源的个数可以通过ARRAY_SIZE函数来计算长度
.resource = beep_res
};

4、注册和注销平台设备函数

当使用上节的函数创建完成一个platform_device后,使用platform_device_register函数进行平台设备的注册

文件位置路径:include/linux/platform_device.h

实际函数位置路径:drivers/base/platform.c

1
2
3
4
5
6
7
8
9
10
extern int platform_device_register(struct platform_device *);
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
arch_setup_pdev_archdata(pdev);
return platform_device_add(pdev);
}
EXPORT_SYMBOL_GPL(platform_device_register);
//参数
//pdev:要注册的平台设备
1
2
3
4
5
6
7
8
9
extern void platform_device_unregister(struct platform_device *);
void platform_device_unregister(struct platform_device *pdev)
{
platform_device_del(pdev);
platform_device_put(pdev);
}
EXPORT_SYMBOL_GPL(platform_device_unregister);
//参数
//pdev:要注销的平台设备