17、设备树实验
一、设备树
C
/*添加led_test节点,*/
led_test{
#address-cells = <1>;
#size-cells = <1>;
compatible = "led_test";
ranges;
//例程是系统灯GPIO0_C7
led@0xfdd60004{
reg = <0xfdd60004 0x00000004 0xfdd6000C 0x00000004>; //GPIO0_H 的数据寄存器和数据方向寄存器
status = "okay";
};
};
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
二、驱动
C
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/platform_device.h>
/*------------------字符设备内容----------------------*/
#define DEV_NAME "led_test"
#define DEV_CNT (1)
/*定义 led 资源结构体,保存获取得到的节点信息以及转换后的虚拟寄存器地址*/
struct led_resource
{
struct device_node *device_node; //led的设备树节点
void __iomem *va_DR;
void __iomem *va_DDR;
};
static dev_t led_devno; //定义字符设备的设备号
static struct cdev led_chr_dev; //定义字符设备结构体chr_dev
struct class *class_led; //保存创建的类
struct device *device; // 保存创建的设备
struct device_node *led_test_device_node; //led_test的设备树节点结构体
/*定义led灯的led_resource 结构体,保存获取得到的节点信息*/
struct led_resource led_res;
/*字符设备操作函数集,open函数*/
static int led_chr_dev_open(struct inode *inode, struct file *filp)
{
printk("\n led_chr_dev_open \n");
return 0;
}
/*字符设备操作函数集,write函数*/
static ssize_t led_chr_dev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
unsigned int register_data = 0; //暂存读取得到的寄存器数据
unsigned char write_data; //用于保存接收到的数据
int error = copy_from_user(&write_data, buf, cnt);
if (error < 0)
{
return -1;
}
/*设置led引脚 输出电平*/
if (write_data)
{
register_data |= ((unsigned int)0x1 << (23));
register_data |= ((unsigned int)0x1 << (7));
writel(register_data, led_res.va_DR); // GPIO0_C7引脚输出高电平,红灯灭
}
else
{
register_data |= ((unsigned int)0x1 << (23));
register_data &= ~((unsigned int)0x1 << (7));
writel(register_data, led_res.va_DR); // GPIO0_C7引脚输出低电平,红灯亮
}
return 0;
}
/*字符设备操作函数集*/
static struct file_operations led_chr_dev_fops =
{
.owner = THIS_MODULE,
.open = led_chr_dev_open,
.write = led_chr_dev_write,
};
/*----------------平台驱动函数集-----------------*/
static int led_probe(struct platform_device *pdv)
{
int ret = -1; //保存错误状态码
unsigned int register_data = 0;
printk(KERN_EMERG "\t match successed \n");
/*获取led_test的设备树节点*/
led_test_device_node = of_find_node_by_path("/led_test");
if (led_test_device_node == NULL)
{
printk(KERN_ERR "\t get led_test failed! \n");
return -1;
}
/*获取led节点*/
led_res.device_node = of_find_node_by_name(led_test_device_node,"led");
if (led_res.device_node == NULL)
{
printk(KERN_ERR "\n get led_device_node failed ! \n");
return -1;
}
/*获取 reg 属性并转化为虚拟地址*/
led_res.va_DR = of_iomap(led_res.device_node, 0);
if(led_res.va_DR == NULL){
printk("of_iomap is error \n");
return -1;
}
led_res.va_DDR = of_iomap(led_res.device_node, 1);
if(led_res.va_DDR == NULL){
printk("of_iomap is error \n");
return -1;
}
// 设置模式寄存器:输出模式
register_data = readl(led_res.va_DDR);
register_data |= ((unsigned int)0x1 << (23));
register_data |= ((unsigned int)0x1 << (7));
writel(register_data,led_res.va_DDR);
// 设置置位寄存器:默认输出高电平
register_data = readl(led_res.va_DR);
register_data |= ((unsigned int)0x1 << (23));
register_data |= ((unsigned int)0x1 << (7));
writel(register_data, led_res.va_DR);
/*---------------------注册 字符设备部分-----------------*/
//第一步
//采用动态分配的方式,获取设备编号,次设备号为0,
//DEV_CNT为1,当前只申请一个设备编号
ret = alloc_chrdev_region(&led_devno, 0, DEV_CNT, DEV_NAME);
if (ret < 0)
{
printk("fail to alloc led_devno\n");
goto alloc_err;
}
//第二步
//关联字符设备结构体cdev与文件操作结构体file_operations
led_chr_dev.owner = THIS_MODULE;
cdev_init(&led_chr_dev, &led_chr_dev_fops);
//第三步
//添加设备至cdev_map散列表中
ret = cdev_add(&led_chr_dev, led_devno, DEV_CNT);
if (ret < 0)
{
printk("fail to add cdev\n");
goto add_err;
}
//第四步
/*创建类 */
class_led = class_create(THIS_MODULE, DEV_NAME);
/*创建设备*/
device = device_create(class_led, NULL, led_devno, NULL, DEV_NAME);
return 0;
add_err:
//添加设备失败时,需要注销设备号
unregister_chrdev_region(led_devno, DEV_CNT);
printk("\n error! \n");
alloc_err:
return -1;
}
static const struct of_device_id led_ids[] = {
{.compatible = "led_test"},
{/* sentinel */}
};
/*定义平台设备结构体*/
struct platform_driver led_platform_driver = {
.probe = led_probe,
.driver = {
.name = "leds-platform",
.owner = THIS_MODULE,
.of_match_table = led_ids,
}};
/*
*驱动初始化函数
*/
static int __init led_platform_driver_init(void)
{
int DriverState;
DriverState = platform_driver_register(&led_platform_driver);
printk(KERN_EMERG "\tDriverState is %d\n", DriverState);
return 0;
}
/*
*驱动注销函数
*/
static void __exit led_platform_driver_exit(void)
{
/*取消物理地址映射到虚拟地址*/
iounmap(led_res.va_DR);
iounmap(led_res.va_DDR);
/*删除设备*/
device_destroy(class_led, led_devno); //清除设备
class_destroy(class_led); //清除类
cdev_del(&led_chr_dev); //清除设备号
unregister_chrdev_region(led_devno, DEV_CNT); //取消注册字符设备
/*注销字符设备*/
platform_driver_unregister(&led_platform_driver);
printk(KERN_EMERG "dts test led exit!\n");
}
module_init(led_platform_driver_init);
module_exit(led_platform_driver_exit);
MODULE_LICENSE("GPL");
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
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
解析:
C
/**
* of_iomap - Maps the memory mapped IO for a given device_node
* @device: the device whose io range will be mapped
* @index: index of the io range
*
* Returns a pointer to the mapped memory
*/
void __iomem *of_iomap(struct device_node *np, int index)
{
struct resource res;
if (of_address_to_resource(np, index, &res))
return NULL;
return ioremap(res.start, resource_size(&res));
}
EXPORT_SYMBOL(of_iomap);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
C
int of_address_to_resource(struct device_node *dev, int index,
struct resource *r)
{
const __be32 *addrp;
u64 size;
unsigned int flags;
const char *name = NULL;
addrp = of_get_address(dev, index, &size, &flags);
if (addrp == NULL)
return -EINVAL;
/* Get optional "reg-names" property to add a name to a resource */
of_property_read_string_index(dev, "reg-names", index, &name);
return __of_address_to_resource(dev, addrp, size, flags, name, r);
}
EXPORT_SYMBOL_GPL(of_address_to_resource);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
of_get_address
__of_address_to_resource
三、APP
C
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])
{
printf("led test_app\n");
/*判断输入的命令是否合法*/
if(argc != 2)
{
printf(" command error ! \n");
printf(" usage : sudo test_app num [num can be 0 or 1]\n");
return -1;
}
/*打开文件*/
int fd = open("/dev/led_test", O_RDWR);
if(fd < 0)
{
printf("open file : %s failed !\n", argv[0]);
return -1;
}
unsigned char commend = atoi(argv[1]); //将接收值转化为数字;
/*写入命令*/
int error = write(fd,&commend,sizeof(commend));
if(error < 0)
{
printf("write file error! \n");
close(fd);
/*判断是否关闭成功*/
}
/*关闭文件*/
error = close(fd);
if(error < 0)
{
printf("close file error! \n");
}
return 0;
}
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
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