platform实例

1 基本模板

1.1 传统方法

在设备树出现之前, platform_device 也是需要手动去书写的.

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
58
59
60
61
62
63
64
65
66
#include <linux/module.h>
#include <linux/platform_device.h>

#define DEV0_BASE_ADDR 0x10000000
#define DEV1_BASE_ADDR 0x20000000
#define RES_SIZE 0x100

/* device 0 */
static struct resource dev0_res[] = {
[0] = {
.start = DEV0_BASE_ADDR,
.end = DEV0_BASE_ADDR + RES_SIZE - 1,
.flags = IORESOURCE_MEM,
},
};

static void release_dev(struct device *dev) {
pr_info(KERN_INFO "Legacy Devs: Device released\n");
}

static struct platform_device pdev0 = {
.name = "legacy-demo-device", // core: name
.id = 0, // ID = 0
.num_resources = ARRAY_SIZE(dev0_res),
.resource = dev0_res,
.dev.release = release_dev,
};

/* device 1 */
static struct resource dev1_res[] = {
[0] = {
.start = DEV1_BASE_ADDR,
.end = DEV1_BASE_ADDR + RES_SIZE - 1,
.flags = IORESOURCE_MEM,
},
};

static struct platform_device pdev1 = {
.name = "legacy-demo-device", // core: name (must same as device 0)
.id = 1, // ID = 1
.num_resources = ARRAY_SIZE(dev1_res),
.resource = dev1_res,
.dev.release = release_dev,
};

static int __init legacy_devs_init(void)
{
int ret;
pr_info(KERN_INFO "legacy devs: Registering devices...\n");

platform_device_register(&pdev0);
platform_device_register(&pdev1);

return 0;
}

static void __exit legacy_devs_exit(void)
{
platform_device_unregister(&pdev0);
platform_device_unregister(&pdev1);
pr_info(KERN_INFO "legacy devs: Unregistered devices.\n");
}

module_init(legacy_devs_init);
module_exit(legacy_devs_exit);
MODULE_LICENSE("GPL");

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
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/io.h>

struct my_driver_data {
int id;
phys_addr_t phys_base;
};

static int legacy_probe(struct platform_device *pdev)
{
struct my_driver_data *priv;
struct resource *res;

pr_info(KERN_INFO "legacy driver: Probing device ID = %d\n", pdev->id);

priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) return -ENOMEM;

/* 1. get resources */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "No memory resource found\n");
return -EINVAL;
}

priv->id = pdev->id;
priv->phys_base = res->start;

/* 3. mount private data to device */
platform_set_drvdata(pdev, priv);

pr_info(KERN_INFO "legacy driver: device %d initialized at 0x%llx\n",
priv->id, (unsigned long long)priv->phys_base);

return 0;
}

static void legacy_remove(struct platform_device *pdev)
{
struct my_driver_data *priv = platform_get_drvdata(pdev);

pr_info(KERN_INFO "legacy driver: removing device %d (base: 0x%llx)\n",
priv->id, (unsigned long long)priv->phys_base);

}

static struct platform_driver legacy_driver = {
.probe = legacy_probe,
.remove = legacy_remove,
.driver = {
.name = "legacy-demo-device", // core: must same as device
},
};

module_platform_driver(legacy_driver);
MODULE_LICENSE("GPL");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sh-5.2# insmod /lib/modules/6.18.5/updates/legacy_driver.ko 
[ 18.451395] legacy_driver: loading out-of-tree module taints kernel.
sh-5.2# insmod /lib/modules/6.18.5/updates/legacy_device.ko
[ 22.298768] legacy devs: Registering devices...
[ 22.299902] legacy driver: Probing device ID = 0
[ 22.300307] legacy driver: device 0 initialized at 0x10000000
[ 22.301217] legacy driver: Probing device ID = 1
[ 22.301420] legacy driver: device 1 initialized at 0x20000000
sh-5.2# rmmod /lib/modules/6.18.5/updates/legacy_device.ko
[ 37.067971] legacy driver: removing device 0 (base: 0x10000000)
[ 37.068557] Legacy Devs: Device released
[ 37.068812] legacy driver: removing device 1 (base: 0x20000000)
[ 37.069151] Legacy Devs: Device released
[ 37.069395] legacy devs: Unregistered devices.
sh-5.2# rmmod /lib/modules/6.18.5/updates/legacy_driver.ko

1.2 设备树方法

在设备树模式下,内核优先使用 of_match_table 来寻找硬件.

  • 传统方法: 内核拿着 .name 去和设备的 .name 逐字比较.

  • 设备树: 内核优先查看 of_match_table 里的 compatible 字符串去设备树里找对应的节点.

但是它对于 /sys 来说还是非常重要.

挂挂载之后我们可以看到该文件.

1
2
sh-5.2# ls /sys/bus/platform/drivers/dt-demo-driver/    
1000000000000100.demo 2000000000000100.demo bind module uevent unbind

内核程序.

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
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h> // core
#include <linux/slab.h>

struct my_dt_data {
phys_addr_t phys_base;
const char *label_name;
};

static int dt_probe(struct platform_device *pdev)
{
struct my_dt_data *priv;
struct resource *res;
const char *label;

dev_info(&pdev->dev, "DT Driver: Probing started...\n");

priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) return -ENOMEM;
/* Notice:
* IORESOURCE_IO: for IO, need special io ins to operate it.
* IORESOURCE_MEM: for mem space.
* IORESOURCE_IRQ: for interrupt, now we use `platform_get_irq` replace it.
* IORESOURCE_DMA: for DMA chanel, morden system use dmaengine replace it.
* ......
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res) {
priv->phys_base = res->start;
}

if (of_property_read_string(pdev->dev.of_node, "label", &label) == 0) {
priv->label_name = label;
} else {
priv->label_name = "Unknown";
}

platform_set_drvdata(pdev, priv);

dev_info(&pdev->dev, "DT Driver: Success! Addr=0x%llx, Label=%s\n",
(unsigned long long)priv->phys_base, priv->label_name);

return 0;
}

static void dt_remove(struct platform_device *pdev)
{
struct my_dt_data *priv = platform_get_drvdata(pdev);
dev_info(&pdev->dev, "DT Driver: Removing %s\n", priv->label_name);
}

/* core: device match table */
static const struct of_device_id my_of_match[] = {
{ .compatible = "example,my-demo-device" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_of_match);

static struct platform_driver dt_driver = {
.probe = dt_probe,
.remove = dt_remove,
.driver = {
.name = "dt-demo-driver",
.of_match_table = my_of_match,
},
};

module_platform_driver(dt_driver);
MODULE_LICENSE("GPL");

运行效果.

1
2
3
4
5
6
7
8
sh-5.2# insmod /lib/modules/6.18.5/updates/dt_driver.ko 
[ 347.523308] dt-demo-driver 1000000000000100.demo: DT Driver: Probing started...
[ 347.523795] dt-demo-driver 1000000000000100.demo: DT Driver: Success! Addr=0x0, Label=Instance-A
[ 347.525442] dt-demo-driver 2000000000000100.demo: DT Driver: Probing started...
[ 347.526605] dt-demo-driver 2000000000000100.demo: DT Driver: Success! Addr=0x0, Label=Instance-B
sh-5.2# rmmod /lib/modules/6.18.5/updates/dt_driver.ko
[ 356.249162] dt-demo-driver 2000000000000100.demo: DT Driver: Removing Instance-B
[ 356.249847] dt-demo-driver 1000000000000100.demo: DT Driver: Removing Instance-A