🚀 Platform设备驱动简介

很多设备虽然用途不同,但其接口定义都是一样的 ,因此很多时候我们不需要去为每个设备都写一个驱动 。

因此 Linux 2.6 及以后的内核中虚拟出来的一条总线 ,即 platform 总线。

平台总线模型将设备代码和驱动代码分离, 将和硬件设备相关的都放到 device.c 文件里面,驱动部分代码都放到 driver.c 文件里面。

这样做的好处是, 实现了此类设备和驱动的分离, 增强设备驱动的可移植性。

🚀 Platform 设备

✈ platform_device 结构体

在 platform 平台下用 platform_device 这个结构体表示 platform 设备。

如果内核支持设备树的话就不用使用 platform_device 来描述设备了, 因为改用设备树去描述了 platform_device。

具体定义在内核源码 /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; // platform 设备的名字, 用来和 platform 驱动相匹配。 名字相同才能匹配成功。
int id; // 用来区分名字相同的设备,一般为-1
bool id_auto;
struct device dev; // 内置的 device 结构体
u32 num_resources; // 资源结构体数量,可用 ARRAY_SIZE 函数来获取
struct resource *resource; // 指向一个资源结构体数组。 一般包含设备信息。

const struct platform_device_id *id_entry; // 用来进行与设备驱动匹配用的 id_table 表
char *driver_override; /* Driver name to force a match */

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

/* arch specific additions */
struct pdev_archdata archdata; // 添加自己的私有数据。
};

✈ resource 结构体

resource 结构体内容如下:

1
2
3
4
5
6
7
struct resource {
resource_size_t start; // 资源的起始信息
resource_size_t end; // 资源的终止信息
const char *name; // 资源名字
unsigned long flags; // 资源类型
struct resource *parent, *sibling, *child;
};

可选的资源类型都定义在了文件 include/linux/ioport.h 里面, 如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
#define IORESOURCE_BITS 0x000000ff 		/* Bus-specific bits */

#define IORESOURCE_TYPE_BITS 0x00001f00 /* Resource type */
#define IORESOURCE_IO 0x00000100 /* PCI/ISA I/O ports */
#define IORESOURCE_MEM 0x00000200
#define IORESOURCE_REG 0x00000300 /* Register offsets */
#define IORESOURCE_IRQ 0x00000400
#define IORESOURCE_DMA 0x00000800
#define IORESOURCE_BUS 0x00001000

/* PCI control bits. Shares IORESOURCE_BITS with above PCI ROM. */
#define IORESOURCE_PCI_FIXED (1<<4) /* Do not move resource */

✈ 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
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
#include <linux/init.h>             // 初始化头文件
#include <linux/module.h> // 最基本的文件, 支持动态添加和卸载模块。
#include <linux/platform_device.h> // 平台设备所需要的头文件


/******************************************************************************
* @功能: 释放 flatform 设备模块的时候此函数会执行
* @参数 dev: 要释放的设备
******************************************************************************/
void device_release(struct device *dev)
{
printk("device_release \n");
}

// 设备资源信息
struct resource device_res[] = {
[0] = {
.start = 0x020AC000,
.end = 0x020AC003,
.flags = IORESOURCE_MEM,
.name = "GPIO5_DR",
}
};

// platform 设备结构体
struct platform_device platform_device = {
.name = "platform_test",
.id = -1,
.resource = device_res,
.num_resources = ARRAY_SIZE(device_res),
.dev = {
.release = device_release
}
};


static int device_init(void)
{
// 设备信息注册到 Linux 内核
platform_device_register(&platform_device);
printk("platform_device_register ok \n");
return 0;
}

static void device_exit(void)
{
// 设备信息卸载
platform_device_unregister(&platform_device);
printk("gooodbye! \n");
}


module_init(device_init);
module_exit(device_exit);
MODULE_LICENSE("GPL");


🚀 Platform 驱动

✈ platform_driver 结构体

在 Linux 内核中, 用 platform_driver 结构体表示 platform 驱动 。

此结构体定义在文件 include/linux/platform_device.h 中, 内容如下:

1
2
3
4
5
6
7
8
9
10
struct platform_driver {
int (*probe)(struct platform_device *); // 当 driver 和 device 匹配成功的时候, 就会执行 probe 函数
int (*remove)(struct platform_device *); // 当 driver 和 device 任意一个 remove 的时候, 就会执行这个函数
void (*shutdown)(struct platform_device *); // 当设备收到 shutdown 命令的时候, 就会执行这个函数
int (*suspend)(struct platform_device *, pm_message_t state); //当设备收到 suspend 命令的时候, 就会执行这个函数
int (*resume)(struct platform_device *); // 当设备收到 resume 命令的时候, 就会执行这个函数
struct device_driver driver; // 内置的 device_driver 结构体
const struct platform_device_id *id_table; // 该设备驱动支持的设备的列表 他是通过这个指针去指向 platform_device_id 类型的数组
bool prevent_deferred_probe;
};

✈ device_driver 结构体

device_driver 结构体定义在 include/linux/device.h, device_driver 结构体内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct device_driver {
const char *name;
struct bus_type *bus;

struct module *owner;
const char *mod_name; /* used for built-in modules */

bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
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;
};

✈ platform_driver 程序框架

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
#include <linux/init.h> //初始化头文件
#include <linux/module.h> //最基本的文件, 支持动态添加和卸载模块。
#include <linux/platform_device.h> //平台设备所需要的头文件


// 驱动和设备匹配成功会进入此函数
int driver_probe(struct platform_device *pdev)
{
struct resource *res;

printk("driver_probe\n");

/*获取资源方法一: 不推荐*/
printk("beep_res is %s\n", pdev->resource[0].name);

/*获取资源方法二: 推荐*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL)
{
printk("platform_get_resource is error\n");
return -EBUSY;
}

//打印获取的寄存器地址和长度
printk("beep_res start is 0x%x \n", res->start);
printk("beep_res end is 0x%x \n", res->end);

return 0;
}

// 当 driver 和 device 任意一个 remove 的时候, 就会执行这个函数
int driver_remove(struct platform_device *pdev)
{
printk("driver_remove\n");
return 0;
}

// platform 驱动结构体
struct platform_driver platform_driver = {
.probe = driver_probe,
.remove = driver_remove,
.driver = {
.owner = THIS_MODULE,
.name = "platform_test"
}
};

static int platform_driver_init(void)
{
int ret = 0;
// platform 驱动注册到 Linux 内核
ret = platform_driver_register(&platform_driver); //注册平台驱动函数
if (ret < 0)
{
printk("platform_driver_register error \n");
}
printk("platform_driver_register ok \n");
return 0;
}

static void platform_driver_exit(void)
{
// platform 驱动卸载
platform_driver_unregister(&platform_driver);
printk("gooodbye! \n");
}

module_init(platform_driver_init);
module_exit(platform_driver_exit);
MODULE_LICENSE("GPL");