OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | From b12afa082622333816203f0fd2e93051de65de09 Mon Sep 17 00:00:00 2001 |
2 | From: Pantelis Antoniou <pantelis.antoniou@konsulko.com> |
||
3 | Date: Wed, 3 Dec 2014 13:23:28 +0200 |
||
4 | Subject: [PATCH 094/454] OF: DT-Overlay configfs interface |
||
5 | |||
6 | This is a port of Pantelis Antoniou's v3 port that makes use of the |
||
7 | new upstreamed configfs support for binary attributes. |
||
8 | |||
9 | Original commit message: |
||
10 | |||
11 | Add a runtime interface to using configfs for generic device tree overlay |
||
12 | usage. With it its possible to use device tree overlays without having |
||
13 | to use a per-platform overlay manager. |
||
14 | |||
15 | Please see Documentation/devicetree/configfs-overlays.txt for more info. |
||
16 | |||
17 | Changes since v2: |
||
18 | - Removed ifdef CONFIG_OF_OVERLAY (since for now it's required) |
||
19 | - Created a documentation entry |
||
20 | - Slight rewording in Kconfig |
||
21 | |||
22 | Changes since v1: |
||
23 | - of_resolve() -> of_resolve_phandles(). |
||
24 | |||
25 | Originally-signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com> |
||
26 | Signed-off-by: Phil Elwell <phil@raspberrypi.org> |
||
27 | |||
28 | DT configfs: Fix build errors on other platforms |
||
29 | |||
30 | Signed-off-by: Phil Elwell <phil@raspberrypi.org> |
||
31 | |||
32 | DT configfs: fix build error |
||
33 | |||
34 | There is an error when compiling rpi-4.6.y branch: |
||
35 | CC drivers/of/configfs.o |
||
36 | drivers/of/configfs.c:291:21: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types] |
||
37 | .default_groups = of_cfs_def_groups, |
||
38 | ^ |
||
39 | drivers/of/configfs.c:291:21: note: (near initialization for 'of_cfs_subsys.su_group.default_groups.next') |
||
40 | |||
41 | The .default_groups is linked list since commit |
||
42 | 1ae1602de028acaa42a0f6ff18d19756f8e825c6. |
||
43 | This commit uses configfs_add_default_group to fix this problem. |
||
44 | |||
45 | Signed-off-by: Slawomir Stepien <sst@poczta.fm> |
||
46 | --- |
||
47 | .../devicetree/configfs-overlays.txt | 31 ++ |
||
48 | drivers/of/Kconfig | 7 + |
||
49 | drivers/of/Makefile | 1 + |
||
50 | drivers/of/configfs.c | 311 ++++++++++++++++++ |
||
51 | 4 files changed, 350 insertions(+) |
||
52 | create mode 100644 Documentation/devicetree/configfs-overlays.txt |
||
53 | create mode 100644 drivers/of/configfs.c |
||
54 | |||
55 | --- /dev/null |
||
56 | +++ b/Documentation/devicetree/configfs-overlays.txt |
||
57 | @@ -0,0 +1,31 @@ |
||
58 | +Howto use the configfs overlay interface. |
||
59 | + |
||
60 | +A device-tree configfs entry is created in /config/device-tree/overlays |
||
61 | +and and it is manipulated using standard file system I/O. |
||
62 | +Note that this is a debug level interface, for use by developers and |
||
63 | +not necessarily something accessed by normal users due to the |
||
64 | +security implications of having direct access to the kernel's device tree. |
||
65 | + |
||
66 | +* To create an overlay you mkdir the directory: |
||
67 | + |
||
68 | + # mkdir /config/device-tree/overlays/foo |
||
69 | + |
||
70 | +* Either you echo the overlay firmware file to the path property file. |
||
71 | + |
||
72 | + # echo foo.dtbo >/config/device-tree/overlays/foo/path |
||
73 | + |
||
74 | +* Or you cat the contents of the overlay to the dtbo file |
||
75 | + |
||
76 | + # cat foo.dtbo >/config/device-tree/overlays/foo/dtbo |
||
77 | + |
||
78 | +The overlay file will be applied, and devices will be created/destroyed |
||
79 | +as required. |
||
80 | + |
||
81 | +To remove it simply rmdir the directory. |
||
82 | + |
||
83 | + # rmdir /config/device-tree/overlays/foo |
||
84 | + |
||
85 | +The rationalle of the dual interface (firmware & direct copy) is that each is |
||
86 | +better suited to different use patterns. The firmware interface is what's |
||
87 | +intended to be used by hardware managers in the kernel, while the copy interface |
||
88 | +make sense for developers (since it avoids problems with namespaces). |
||
89 | --- a/drivers/of/Kconfig |
||
90 | +++ b/drivers/of/Kconfig |
||
91 | @@ -112,4 +112,11 @@ config OF_OVERLAY |
||
92 | config OF_NUMA |
||
93 | bool |
||
94 | |||
95 | +config OF_CONFIGFS |
||
96 | + bool "Device Tree Overlay ConfigFS interface" |
||
97 | + select CONFIGFS_FS |
||
98 | + select OF_OVERLAY |
||
99 | + help |
||
100 | + Enable a simple user-space driven DT overlay interface. |
||
101 | + |
||
102 | endif # OF |
||
103 | --- a/drivers/of/Makefile |
||
104 | +++ b/drivers/of/Makefile |
||
105 | @@ -1,5 +1,6 @@ |
||
106 | # SPDX-License-Identifier: GPL-2.0 |
||
107 | obj-y = base.o device.o platform.o property.o |
||
108 | +obj-$(CONFIG_OF_CONFIGFS) += configfs.o |
||
109 | obj-$(CONFIG_OF_DYNAMIC) += dynamic.o |
||
110 | obj-$(CONFIG_OF_FLATTREE) += fdt.o |
||
111 | obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o |
||
112 | --- /dev/null |
||
113 | +++ b/drivers/of/configfs.c |
||
114 | @@ -0,0 +1,311 @@ |
||
115 | +/* |
||
116 | + * Configfs entries for device-tree |
||
117 | + * |
||
118 | + * Copyright (C) 2013 - Pantelis Antoniou <panto@antoniou-consulting.com> |
||
119 | + * |
||
120 | + * This program is free software; you can redistribute it and/or |
||
121 | + * modify it under the terms of the GNU General Public License |
||
122 | + * as published by the Free Software Foundation; either version |
||
123 | + * 2 of the License, or (at your option) any later version. |
||
124 | + */ |
||
125 | +#include <linux/ctype.h> |
||
126 | +#include <linux/cpu.h> |
||
127 | +#include <linux/module.h> |
||
128 | +#include <linux/of.h> |
||
129 | +#include <linux/of_fdt.h> |
||
130 | +#include <linux/spinlock.h> |
||
131 | +#include <linux/slab.h> |
||
132 | +#include <linux/proc_fs.h> |
||
133 | +#include <linux/configfs.h> |
||
134 | +#include <linux/types.h> |
||
135 | +#include <linux/stat.h> |
||
136 | +#include <linux/limits.h> |
||
137 | +#include <linux/file.h> |
||
138 | +#include <linux/vmalloc.h> |
||
139 | +#include <linux/firmware.h> |
||
140 | +#include <linux/sizes.h> |
||
141 | + |
||
142 | +#include "of_private.h" |
||
143 | + |
||
144 | +struct cfs_overlay_item { |
||
145 | + struct config_item item; |
||
146 | + |
||
147 | + char path[PATH_MAX]; |
||
148 | + |
||
149 | + const struct firmware *fw; |
||
150 | + struct device_node *overlay; |
||
151 | + int ov_id; |
||
152 | + |
||
153 | + void *dtbo; |
||
154 | + int dtbo_size; |
||
155 | +}; |
||
156 | + |
||
157 | +static int create_overlay(struct cfs_overlay_item *overlay, void *blob) |
||
158 | +{ |
||
159 | + int err; |
||
160 | + |
||
161 | + /* unflatten the tree */ |
||
162 | + of_fdt_unflatten_tree(blob, NULL, &overlay->overlay); |
||
163 | + if (overlay->overlay == NULL) { |
||
164 | + pr_err("%s: failed to unflatten tree\n", __func__); |
||
165 | + err = -EINVAL; |
||
166 | + goto out_err; |
||
167 | + } |
||
168 | + pr_debug("%s: unflattened OK\n", __func__); |
||
169 | + |
||
170 | + /* mark it as detached */ |
||
171 | + of_node_set_flag(overlay->overlay, OF_DETACHED); |
||
172 | + |
||
173 | + /* perform resolution */ |
||
174 | + err = of_resolve_phandles(overlay->overlay); |
||
175 | + if (err != 0) { |
||
176 | + pr_err("%s: Failed to resolve tree\n", __func__); |
||
177 | + goto out_err; |
||
178 | + } |
||
179 | + pr_debug("%s: resolved OK\n", __func__); |
||
180 | + |
||
181 | + err = of_overlay_create(overlay->overlay); |
||
182 | + if (err < 0) { |
||
183 | + pr_err("%s: Failed to create overlay (err=%d)\n", |
||
184 | + __func__, err); |
||
185 | + goto out_err; |
||
186 | + } |
||
187 | + overlay->ov_id = err; |
||
188 | + |
||
189 | +out_err: |
||
190 | + return err; |
||
191 | +} |
||
192 | + |
||
193 | +static inline struct cfs_overlay_item *to_cfs_overlay_item( |
||
194 | + struct config_item *item) |
||
195 | +{ |
||
196 | + return item ? container_of(item, struct cfs_overlay_item, item) : NULL; |
||
197 | +} |
||
198 | + |
||
199 | +static ssize_t cfs_overlay_item_path_show(struct config_item *item, |
||
200 | + char *page) |
||
201 | +{ |
||
202 | + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); |
||
203 | + return sprintf(page, "%s\n", overlay->path); |
||
204 | +} |
||
205 | + |
||
206 | +static ssize_t cfs_overlay_item_path_store(struct config_item *item, |
||
207 | + const char *page, size_t count) |
||
208 | +{ |
||
209 | + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); |
||
210 | + const char *p = page; |
||
211 | + char *s; |
||
212 | + int err; |
||
213 | + |
||
214 | + /* if it's set do not allow changes */ |
||
215 | + if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) |
||
216 | + return -EPERM; |
||
217 | + |
||
218 | + /* copy to path buffer (and make sure it's always zero terminated */ |
||
219 | + count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p); |
||
220 | + overlay->path[sizeof(overlay->path) - 1] = '\0'; |
||
221 | + |
||
222 | + /* strip trailing newlines */ |
||
223 | + s = overlay->path + strlen(overlay->path); |
||
224 | + while (s > overlay->path && *--s == '\n') |
||
225 | + *s = '\0'; |
||
226 | + |
||
227 | + pr_debug("%s: path is '%s'\n", __func__, overlay->path); |
||
228 | + |
||
229 | + err = request_firmware(&overlay->fw, overlay->path, NULL); |
||
230 | + if (err != 0) |
||
231 | + goto out_err; |
||
232 | + |
||
233 | + err = create_overlay(overlay, (void *)overlay->fw->data); |
||
234 | + if (err != 0) |
||
235 | + goto out_err; |
||
236 | + |
||
237 | + return count; |
||
238 | + |
||
239 | +out_err: |
||
240 | + |
||
241 | + release_firmware(overlay->fw); |
||
242 | + overlay->fw = NULL; |
||
243 | + |
||
244 | + overlay->path[0] = '\0'; |
||
245 | + return err; |
||
246 | +} |
||
247 | + |
||
248 | +static ssize_t cfs_overlay_item_status_show(struct config_item *item, |
||
249 | + char *page) |
||
250 | +{ |
||
251 | + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); |
||
252 | + |
||
253 | + return sprintf(page, "%s\n", |
||
254 | + overlay->ov_id >= 0 ? "applied" : "unapplied"); |
||
255 | +} |
||
256 | + |
||
257 | +CONFIGFS_ATTR(cfs_overlay_item_, path); |
||
258 | +CONFIGFS_ATTR_RO(cfs_overlay_item_, status); |
||
259 | + |
||
260 | +static struct configfs_attribute *cfs_overlay_attrs[] = { |
||
261 | + &cfs_overlay_item_attr_path, |
||
262 | + &cfs_overlay_item_attr_status, |
||
263 | + NULL, |
||
264 | +}; |
||
265 | + |
||
266 | +ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, |
||
267 | + void *buf, size_t max_count) |
||
268 | +{ |
||
269 | + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); |
||
270 | + |
||
271 | + pr_debug("%s: buf=%p max_count=%zu\n", __func__, |
||
272 | + buf, max_count); |
||
273 | + |
||
274 | + if (overlay->dtbo == NULL) |
||
275 | + return 0; |
||
276 | + |
||
277 | + /* copy if buffer provided */ |
||
278 | + if (buf != NULL) { |
||
279 | + /* the buffer must be large enough */ |
||
280 | + if (overlay->dtbo_size > max_count) |
||
281 | + return -ENOSPC; |
||
282 | + |
||
283 | + memcpy(buf, overlay->dtbo, overlay->dtbo_size); |
||
284 | + } |
||
285 | + |
||
286 | + return overlay->dtbo_size; |
||
287 | +} |
||
288 | + |
||
289 | +ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, |
||
290 | + const void *buf, size_t count) |
||
291 | +{ |
||
292 | + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); |
||
293 | + int err; |
||
294 | + |
||
295 | + /* if it's set do not allow changes */ |
||
296 | + if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) |
||
297 | + return -EPERM; |
||
298 | + |
||
299 | + /* copy the contents */ |
||
300 | + overlay->dtbo = kmemdup(buf, count, GFP_KERNEL); |
||
301 | + if (overlay->dtbo == NULL) |
||
302 | + return -ENOMEM; |
||
303 | + |
||
304 | + overlay->dtbo_size = count; |
||
305 | + |
||
306 | + err = create_overlay(overlay, overlay->dtbo); |
||
307 | + if (err != 0) |
||
308 | + goto out_err; |
||
309 | + |
||
310 | + return count; |
||
311 | + |
||
312 | +out_err: |
||
313 | + kfree(overlay->dtbo); |
||
314 | + overlay->dtbo = NULL; |
||
315 | + overlay->dtbo_size = 0; |
||
316 | + |
||
317 | + return err; |
||
318 | +} |
||
319 | + |
||
320 | +CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M); |
||
321 | + |
||
322 | +static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = { |
||
323 | + &cfs_overlay_item_attr_dtbo, |
||
324 | + NULL, |
||
325 | +}; |
||
326 | + |
||
327 | +static void cfs_overlay_release(struct config_item *item) |
||
328 | +{ |
||
329 | + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); |
||
330 | + |
||
331 | + if (overlay->ov_id >= 0) |
||
332 | + of_overlay_destroy(overlay->ov_id); |
||
333 | + if (overlay->fw) |
||
334 | + release_firmware(overlay->fw); |
||
335 | + /* kfree with NULL is safe */ |
||
336 | + kfree(overlay->dtbo); |
||
337 | + kfree(overlay); |
||
338 | +} |
||
339 | + |
||
340 | +static struct configfs_item_operations cfs_overlay_item_ops = { |
||
341 | + .release = cfs_overlay_release, |
||
342 | +}; |
||
343 | + |
||
344 | +static struct config_item_type cfs_overlay_type = { |
||
345 | + .ct_item_ops = &cfs_overlay_item_ops, |
||
346 | + .ct_attrs = cfs_overlay_attrs, |
||
347 | + .ct_bin_attrs = cfs_overlay_bin_attrs, |
||
348 | + .ct_owner = THIS_MODULE, |
||
349 | +}; |
||
350 | + |
||
351 | +static struct config_item *cfs_overlay_group_make_item( |
||
352 | + struct config_group *group, const char *name) |
||
353 | +{ |
||
354 | + struct cfs_overlay_item *overlay; |
||
355 | + |
||
356 | + overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); |
||
357 | + if (!overlay) |
||
358 | + return ERR_PTR(-ENOMEM); |
||
359 | + overlay->ov_id = -1; |
||
360 | + |
||
361 | + config_item_init_type_name(&overlay->item, name, &cfs_overlay_type); |
||
362 | + return &overlay->item; |
||
363 | +} |
||
364 | + |
||
365 | +static void cfs_overlay_group_drop_item(struct config_group *group, |
||
366 | + struct config_item *item) |
||
367 | +{ |
||
368 | + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); |
||
369 | + |
||
370 | + config_item_put(&overlay->item); |
||
371 | +} |
||
372 | + |
||
373 | +static struct configfs_group_operations overlays_ops = { |
||
374 | + .make_item = cfs_overlay_group_make_item, |
||
375 | + .drop_item = cfs_overlay_group_drop_item, |
||
376 | +}; |
||
377 | + |
||
378 | +static struct config_item_type overlays_type = { |
||
379 | + .ct_group_ops = &overlays_ops, |
||
380 | + .ct_owner = THIS_MODULE, |
||
381 | +}; |
||
382 | + |
||
383 | +static struct configfs_group_operations of_cfs_ops = { |
||
384 | + /* empty - we don't allow anything to be created */ |
||
385 | +}; |
||
386 | + |
||
387 | +static struct config_item_type of_cfs_type = { |
||
388 | + .ct_group_ops = &of_cfs_ops, |
||
389 | + .ct_owner = THIS_MODULE, |
||
390 | +}; |
||
391 | + |
||
392 | +struct config_group of_cfs_overlay_group; |
||
393 | + |
||
394 | +static struct configfs_subsystem of_cfs_subsys = { |
||
395 | + .su_group = { |
||
396 | + .cg_item = { |
||
397 | + .ci_namebuf = "device-tree", |
||
398 | + .ci_type = &of_cfs_type, |
||
399 | + }, |
||
400 | + }, |
||
401 | + .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex), |
||
402 | +}; |
||
403 | + |
||
404 | +static int __init of_cfs_init(void) |
||
405 | +{ |
||
406 | + int ret; |
||
407 | + |
||
408 | + pr_info("%s\n", __func__); |
||
409 | + |
||
410 | + config_group_init(&of_cfs_subsys.su_group); |
||
411 | + config_group_init_type_name(&of_cfs_overlay_group, "overlays", |
||
412 | + &overlays_type); |
||
413 | + configfs_add_default_group(&of_cfs_overlay_group, |
||
414 | + &of_cfs_subsys.su_group); |
||
415 | + |
||
416 | + ret = configfs_register_subsystem(&of_cfs_subsys); |
||
417 | + if (ret != 0) { |
||
418 | + pr_err("%s: failed to register subsys\n", __func__); |
||
419 | + goto out; |
||
420 | + } |
||
421 | + pr_info("%s: OK\n", __func__); |
||
422 | +out: |
||
423 | + return ret; |
||
424 | +} |
||
425 | +late_initcall(of_cfs_init); |