我的内核学习笔记2:platform设备模型

说实话,我做这个例子之前,我对linux的platform设备、platform驱动了解不多,只知道有这些东西,但没概念。上网搜索了一些资料,并下了点功夫跟踪了内核源代码,虽然不能说吃透了原理,但也不至于像以前那样一无所知。这篇文章主要是说一下一个简单的驱动例子。
为了明确层次,不把所有代码都放到一个文件中,这个例子中,分别有设备文件simplechar_dev.c,驱动文件simplechar_drv.c以及共用的头文件simplechar.h。为了避免例子十分的简单,额外添加了些结构体,也在代码 中对这些结构体做了测试。总之,可以认为这是一个platform的模板吧。

simplechar.h文件是自定义的结构体:

1
2
3
4
5
6
7
8
#ifndef SIMPLECHAR_H
#define SIMPLECHAR_H

struct simplechar_platdata {
char *name;
};

#endif

simplechar_dev.c文件的代码仅仅与“设备”有关,即定义这个设备有哪些特性(名称、私有数据,等):

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
/**
* @file simplechar_dev.c
* @author Late Lee <latelee@163.com>
* @date Tue Nov 12 21:07:03 2013
*
* @brief
*
*
*/

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>

#include "simplechar.h"

// device
static struct simplechar_platdata foo_pdata = {
.name = "gotohell",
};

// 避免删掉模块时出现警告
static void simplechar_dev_release(struct device* dev)
{
printk(KERN_NOTICE "do %s case of: Device xxx does not have a release() function, it is broken and must be fixed.\n", __func__);
return;
}

// 另一文件要使用到,此处不能为static
// 在/sys/devices/platform/生成目录:simplechar
struct platform_device simplechar_device = {
.name = "simplechar",
.id = -1, // 注:如何为1,则生成目录:simplechar.1
.dev = {
.platform_data = &foo_pdata,
.release = &simplechar_dev_release,
},
};

simplechar_drv.c文件主要实现了“驱动”,驱动的主要代码集中于此:

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
/**
* @file simplechar_drv.c
* @author Late Lee <latelee@163.com>
* @date Tue Nov 12 21:07:15 2013
*
* @brief platform模型示例
*
* @note 仅用于insmod和rmmod的测试,不具备字符设备特征
*/
/**
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>

#include "simplechar.h"

// device
extern struct platform_device simplechar_device;

// our own data
struct simplechar {
int id;
struct simplechar_platdata *pdata;
char buffer[16];
};


static inline struct simplechar *pdev_to_owndata(struct platform_device *dev)
{
return platform_get_drvdata(dev);
}

static int simplechar_remove(struct platform_device *dev)
{
struct simplechar *foo = pdev_to_owndata(dev);

// 释放自定义数据空间
kfree(foo);

return 0;
}

static int simplechar_test(struct platform_device *dev)
{
struct simplechar *foo = NULL;

foo = pdev_to_owndata(dev);

printk(KERN_NOTICE "%s: get id: %d data: %s\n", __func__, foo->id, foo->buffer);

return 0;
}

static int simplechar_probe(struct platform_device *dev)
{
struct simplechar_platdata *pdata = dev->dev.platform_data;
struct simplechar *foo = NULL;
int ret = 0;

printk(KERN_NOTICE "in %s our data name: %s\n", __func__, pdata->name);

// 申请自定义数据空间
foo = kzalloc(sizeof(struct simplechar), GFP_KERNEL);
if (foo == NULL) {
dev_err(&dev->dev, "No memory for device\n");
return -ENOMEM;
}
// 设置自定义结构体数据
platform_set_drvdata(dev, foo);

foo->id = 250;
strcpy(foo->buffer, "hello world");

// 简单测试
simplechar_test(dev);

return ret;
}

// driver
static struct platform_driver simplechar_driver = {
.probe = simplechar_probe,
.remove = simplechar_remove,
.driver = {
.name = "simplechar",
.owner = THIS_MODULE,
},
};

static int __init simplechar_drv_init(void)
{
int ret = 0;
printk(KERN_NOTICE "in %s\n", __func__);

// 先注册设备(适用于静态定义设备结构体)
ret = platform_device_register(&simplechar_device);
if (ret)
{
printk(KERN_NOTICE "platform_device_register failed!\n");
return ret;
}
// 再注册驱动
ret = platform_driver_register(&simplechar_driver);
if (ret)
{
printk(KERN_NOTICE "platform_driver_register failed!\n");
return ret;
}

printk("%s() ok\n", __func__);

return ret;
}

static void __exit simplechar_drv_exit(void)
{
printk(KERN_NOTICE "in %s\n", __func__);
// 先卸载驱动
platform_driver_unregister(&simplechar_driver);
// 再卸载设备
platform_device_unregister(&simplechar_device);
}

module_init(simplechar_drv_init);
module_exit(simplechar_drv_exit);

MODULE_AUTHOR("Late Lee");
MODULE_DESCRIPTION("Simple platform driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:simplechar");