427 lines
13 KiB
Diff
427 lines
13 KiB
Diff
From f875ad99fe6d44c7683954b3023209d3d84dbf2d Mon Sep 17 00:00:00 2001
|
|
From: andy_chi <andy_chi@asus.com>
|
|
Date: Wed, 21 Jun 2017 15:52:19 +0800
|
|
Subject: [PATCH] Enable a simple user-space driven DT overlay interface
|
|
|
|
Change-Id: I6e9f71d8b46df65abd698378b8994988c7fa5651
|
|
---
|
|
drivers/of/Kconfig | 7 +
|
|
drivers/of/Makefile | 1 +
|
|
drivers/of/dtbocfg.c | 381 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
3 files changed, 389 insertions(+)
|
|
create mode 100644 drivers/of/dtbocfg.c
|
|
|
|
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
|
|
index e2a48415d969..7e5e6c4e77e0 100644
|
|
--- a/drivers/of/Kconfig
|
|
+++ b/drivers/of/Kconfig
|
|
@@ -112,4 +112,11 @@ config OF_OVERLAY
|
|
While this option is selected automatically when needed, you can
|
|
enable it manually to improve device tree unit test coverage.
|
|
|
|
+config OF_CONFIGFS
|
|
+ bool "Device Tree Overlay ConfigFS interface"
|
|
+ select CONFIGFS_FS
|
|
+ select OF_OVERLAY
|
|
+ help
|
|
+ Enable a simple user-space driven DT overlay interface.
|
|
+
|
|
endif # OF
|
|
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
|
|
index 156c072b3117..413b0fe675c4 100644
|
|
--- a/drivers/of/Makefile
|
|
+++ b/drivers/of/Makefile
|
|
@@ -1,4 +1,5 @@
|
|
obj-y = base.o device.o platform.o property.o
|
|
+obj-$(CONFIG_OF_CONFIGFS) += dtbocfg.o
|
|
obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
|
|
obj-$(CONFIG_OF_FLATTREE) += fdt.o
|
|
obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
|
|
diff --git a/drivers/of/dtbocfg.c b/drivers/of/dtbocfg.c
|
|
new file mode 100644
|
|
index 000000000000..1f715ea3895c
|
|
--- /dev/null
|
|
+++ b/drivers/of/dtbocfg.c
|
|
@@ -0,0 +1,381 @@
|
|
+/*********************************************************************************
|
|
+ *
|
|
+ * Copyright (C) 2016-2017 Ichiro Kawazome
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
+ * modification, are permitted provided that the following conditions
|
|
+ * are met:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer in
|
|
+ * the documentation and/or other materials provided with the
|
|
+ * distribution.
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
+ *
|
|
+ ********************************************************************************/
|
|
+#include <linux/slab.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_fdt.h>
|
|
+#include <linux/configfs.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/stat.h>
|
|
+#include <linux/limits.h>
|
|
+#include <linux/file.h>
|
|
+#include <linux/version.h>
|
|
+
|
|
+/**
|
|
+ * Device Tree Overlay Item Structure
|
|
+ */
|
|
+struct dtbocfg_overlay_item {
|
|
+ struct config_item item;
|
|
+ struct device_node* node;
|
|
+ int id;
|
|
+ void* dtbo;
|
|
+ int dtbo_size;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * dtbocfg_overlay_create() - Create Device Tree Overlay
|
|
+ * @overlay: Pointer to Device Tree Overlay Item
|
|
+ * return Success(0) or Error Status.
|
|
+ */
|
|
+static int dtbocfg_overlay_item_create(struct dtbocfg_overlay_item *overlay)
|
|
+{
|
|
+ int ret_val;
|
|
+
|
|
+#if (LINUX_VERSION_CODE >= 0x040700)
|
|
+ of_fdt_unflatten_tree(overlay->dtbo, NULL, &overlay->node);
|
|
+#else
|
|
+ of_fdt_unflatten_tree(overlay->dtbo, &overlay->node);
|
|
+#endif
|
|
+ if (overlay->node == NULL) {
|
|
+ pr_err("%s: failed to unflatten tree\n", __func__);
|
|
+ ret_val = -EINVAL;
|
|
+ goto failed;
|
|
+ }
|
|
+ pr_debug("%s: unflattened OK\n", __func__);
|
|
+
|
|
+ of_node_set_flag(overlay->node, OF_DETACHED);
|
|
+
|
|
+ ret_val = of_resolve_phandles(overlay->node);
|
|
+ if (ret_val != 0) {
|
|
+ pr_err("%s: Failed to resolve tree\n", __func__);
|
|
+ goto failed;
|
|
+ }
|
|
+ pr_debug("%s: resolved OK\n", __func__);
|
|
+
|
|
+ ret_val = of_overlay_create(overlay->node);
|
|
+ if (ret_val < 0) {
|
|
+ pr_err("%s: Failed to create overlay (ret_val=%d)\n", __func__, ret_val);
|
|
+ goto failed;
|
|
+ }
|
|
+ overlay->id = ret_val;
|
|
+ pr_debug("%s: create OK\n", __func__);
|
|
+ return 0;
|
|
+
|
|
+ failed:
|
|
+ return ret_val;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * dtbocfg_overlay_item_release() - Relase Device Tree Overlay
|
|
+ * @overlay: Pointer to Device Tree Overlay Item
|
|
+ * return none
|
|
+ */
|
|
+static void dtbocfg_overlay_item_release(struct dtbocfg_overlay_item *overlay)
|
|
+{
|
|
+ if (overlay->id >= 0) {
|
|
+ of_overlay_destroy(overlay->id);
|
|
+ overlay->id = -1;
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * container_of_dtbocfg_overlay_item() - Get Device Tree Overlay Item Pointer from Configuration Item
|
|
+ * @item: Pointer to Configuration Item
|
|
+ * return Pointer to Device Tree Overlay Item
|
|
+ */
|
|
+static inline struct dtbocfg_overlay_item* container_of_dtbocfg_overlay_item(struct config_item *item)
|
|
+{
|
|
+ return item ? container_of(item, struct dtbocfg_overlay_item, item) : NULL;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * dtbocfg_overlay_item_status_store() - Set Status Attibute
|
|
+ * @item: Pointer to Configuration Item
|
|
+ * @page: Pointer to Value Buffer
|
|
+ * @count: Size of Value Buffer Size
|
|
+ * return Stored Size or Error Status.
|
|
+ */
|
|
+static ssize_t dtbocfg_overlay_item_status_store(struct config_item *item, const char *buf, size_t count)
|
|
+{
|
|
+ struct dtbocfg_overlay_item *overlay = container_of_dtbocfg_overlay_item(item);
|
|
+ ssize_t status;
|
|
+ unsigned long value;
|
|
+ if (0 != (status = kstrtoul(buf, 10, &value))) {
|
|
+ goto failed;
|
|
+ }
|
|
+ if (value == 0) {
|
|
+ if (overlay->id >= 0) {
|
|
+ dtbocfg_overlay_item_release(overlay);
|
|
+ }
|
|
+ } else {
|
|
+ if (overlay->id < 0) {
|
|
+ dtbocfg_overlay_item_create(overlay);
|
|
+ }
|
|
+ }
|
|
+ return count;
|
|
+ failed:
|
|
+ return -EPERM;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * dtbocfg_overlay_item_status_show() - Show Status Attibute
|
|
+ * @item : Pointer to Configuration Item
|
|
+ * @page : Pointer to Value for Store
|
|
+ * return String Size or Error Status.
|
|
+ */
|
|
+static ssize_t dtbocfg_overlay_item_status_show(struct config_item *item, char *page)
|
|
+{
|
|
+ struct dtbocfg_overlay_item *overlay = container_of_dtbocfg_overlay_item(item);
|
|
+ return sprintf(page, "%d\n", overlay->id >= 0 ? 1 : 0);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * dtbocfg_overlay_item_dtbo_store() - Store Device Tree Blob to Configuration Item
|
|
+ * @item : Pointer to Configuration Item
|
|
+ * @page : Pointer to Value Buffer
|
|
+ * @count: Size of Value Buffer
|
|
+ * return Stored Size or Error Status.
|
|
+ */
|
|
+static ssize_t dtbocfg_overlay_item_dtbo_store(struct config_item *item, const char *buf, size_t count)
|
|
+{
|
|
+ struct dtbocfg_overlay_item *overlay = container_of_dtbocfg_overlay_item(item);
|
|
+
|
|
+ if (overlay->dtbo_size > 0) {
|
|
+ if (overlay->id >= 0) {
|
|
+ return -EPERM;
|
|
+ }
|
|
+ kfree(overlay->dtbo);
|
|
+ overlay->dtbo = NULL;
|
|
+ overlay->dtbo_size = 0;
|
|
+ }
|
|
+
|
|
+ overlay->dtbo = kmemdup(buf, count, GFP_KERNEL);
|
|
+ if (overlay->dtbo == NULL) {
|
|
+ overlay->dtbo_size = 0;
|
|
+ return -ENOMEM;
|
|
+ } else {
|
|
+ overlay->dtbo_size = count;
|
|
+ return count;
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * dtbocfg_overlay_item_dtbo_show() - Read Device Tree Blob from Configuration Item
|
|
+ * @item : Pointer to Configuration Item
|
|
+ * @page : Pointer to Value for Store
|
|
+ * return Read Size
|
|
+ */
|
|
+static ssize_t dtbocfg_overlay_item_dtbo_show(struct config_item *item, char *buf)
|
|
+{
|
|
+ struct dtbocfg_overlay_item *overlay = container_of_dtbocfg_overlay_item(item);
|
|
+
|
|
+ if (overlay->dtbo == NULL)
|
|
+ return 0;
|
|
+
|
|
+ if (overlay->dtbo_size > PAGE_SIZE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (buf != NULL)
|
|
+ memcpy(buf, overlay->dtbo, overlay->dtbo_size);
|
|
+
|
|
+ return overlay->dtbo_size;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Device Tree Blob Overlay Attribute Structure
|
|
+ */
|
|
+CONFIGFS_ATTR(dtbocfg_overlay_item_, dtbo );
|
|
+CONFIGFS_ATTR(dtbocfg_overlay_item_, status);
|
|
+
|
|
+static struct configfs_attribute *dtbocfg_overlay_attrs[] = {
|
|
+ &dtbocfg_overlay_item_attr_status,
|
|
+ &dtbocfg_overlay_item_attr_dtbo,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/**
|
|
+ * dtbocfg_overlay_release() - Release Device Tree Overlay Item
|
|
+ * @item : Pointer to Configuration Item
|
|
+ * Return None
|
|
+ */
|
|
+static void dtbocfg_overlay_release(struct config_item *item)
|
|
+{
|
|
+ struct dtbocfg_overlay_item *overlay = container_of_dtbocfg_overlay_item(item);
|
|
+
|
|
+ pr_debug("%s\n", __func__);
|
|
+
|
|
+ dtbocfg_overlay_item_release(overlay);
|
|
+
|
|
+ if (overlay->dtbo) {
|
|
+ kfree(overlay->dtbo);
|
|
+ overlay->dtbo = NULL;
|
|
+ overlay->dtbo_size = 0;
|
|
+ }
|
|
+
|
|
+ kfree(overlay);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Device Tree Blob Overlay Item Structure
|
|
+ */
|
|
+static struct configfs_item_operations dtbocfg_overlay_item_ops = {
|
|
+ .release = dtbocfg_overlay_release,
|
|
+};
|
|
+
|
|
+static struct config_item_type dtbocfg_overlay_item_type = {
|
|
+ .ct_item_ops = &dtbocfg_overlay_item_ops,
|
|
+ .ct_attrs = dtbocfg_overlay_attrs,
|
|
+ .ct_owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+/**
|
|
+ * dtbocfg_overlay_group_make_item() - Make Device Tree Overlay Group Item
|
|
+ * @group: Pointer to Configuration Group
|
|
+ * @name : Pointer to Group Name
|
|
+ * Return Pointer to Device Tree Overlay Group Item
|
|
+ */
|
|
+static struct config_item *dtbocfg_overlay_group_make_item(struct config_group *group, const char *name)
|
|
+{
|
|
+ struct dtbocfg_overlay_item *overlay;
|
|
+
|
|
+ pr_debug("%s\n", __func__);
|
|
+
|
|
+ overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
|
|
+
|
|
+ if (!overlay)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+ overlay->id = -1;
|
|
+ overlay->dtbo = NULL;
|
|
+ overlay->dtbo_size = 0;
|
|
+
|
|
+ config_item_init_type_name(&overlay->item, name, &dtbocfg_overlay_item_type);
|
|
+ return &overlay->item;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * dtbocfg_overlay_group_drop_item() - Drop Device Tree Overlay Group Item
|
|
+ * @group: Pointer to Configuration Group
|
|
+ * @item : Pointer to Device Tree Overlay Group Item
|
|
+ */
|
|
+static void dtbocfg_overlay_group_drop_item(struct config_group *group, struct config_item *item)
|
|
+{
|
|
+ struct dtbocfg_overlay_item *overlay = container_of_dtbocfg_overlay_item(item);
|
|
+
|
|
+ pr_debug("%s\n", __func__);
|
|
+
|
|
+ config_item_put(&overlay->item);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Device Tree Blob Overlay Sub Group Structures
|
|
+ */
|
|
+static struct configfs_group_operations dtbocfg_overlays_ops = {
|
|
+ .make_item = dtbocfg_overlay_group_make_item,
|
|
+ .drop_item = dtbocfg_overlay_group_drop_item,
|
|
+};
|
|
+
|
|
+static struct config_item_type dtbocfg_overlays_type = {
|
|
+ .ct_group_ops = &dtbocfg_overlays_ops,
|
|
+ .ct_owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+static struct config_group dtbocfg_overlay_group;
|
|
+
|
|
+/**
|
|
+ * Device Tree Blob Overlay Root Sub System Structures
|
|
+ */
|
|
+static struct configfs_group_operations dtbocfg_root_ops = {
|
|
+ /* empty - we don't allow anything to be created */
|
|
+};
|
|
+
|
|
+static struct config_item_type dtbocfg_root_type = {
|
|
+ .ct_group_ops = &dtbocfg_root_ops,
|
|
+ .ct_owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+static struct configfs_subsystem dtbocfg_root_subsys = {
|
|
+ .su_group = {
|
|
+ .cg_item = {
|
|
+ .ci_namebuf = "device-tree",
|
|
+ .ci_type = &dtbocfg_root_type,
|
|
+ },
|
|
+ },
|
|
+ .su_mutex = __MUTEX_INITIALIZER(dtbocfg_root_subsys.su_mutex),
|
|
+};
|
|
+
|
|
+/**
|
|
+ * dtbocfg_module_init()
|
|
+ */
|
|
+static int __init dtbocfg_module_init(void)
|
|
+{
|
|
+ int retval = 0;
|
|
+
|
|
+ pr_info("%s\n", __func__);
|
|
+
|
|
+ config_group_init(&dtbocfg_root_subsys.su_group);
|
|
+ config_group_init_type_name(&dtbocfg_overlay_group, "overlays", &dtbocfg_overlays_type);
|
|
+
|
|
+ retval = configfs_register_subsystem(&dtbocfg_root_subsys);
|
|
+ if (retval != 0) {
|
|
+ pr_err( "%s: couldn't register subsys\n", __func__);
|
|
+ goto register_subsystem_failed;
|
|
+ }
|
|
+
|
|
+ retval = configfs_register_group(&dtbocfg_root_subsys.su_group, &dtbocfg_overlay_group);
|
|
+ if (retval != 0) {
|
|
+ pr_err( "%s: couldn't register group\n", __func__);
|
|
+ goto register_group_failed;
|
|
+ }
|
|
+
|
|
+ pr_info("%s: OK\n", __func__);
|
|
+ return 0;
|
|
+
|
|
+ register_group_failed:
|
|
+ configfs_unregister_subsystem(&dtbocfg_root_subsys);
|
|
+ register_subsystem_failed:
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * dtbocfg_module_exit()
|
|
+ */
|
|
+static void __exit dtbocfg_module_exit(void)
|
|
+{
|
|
+ configfs_unregister_group(&dtbocfg_overlay_group);
|
|
+ configfs_unregister_subsystem(&dtbocfg_root_subsys);
|
|
+}
|
|
+
|
|
+module_init(dtbocfg_module_init);
|
|
+module_exit(dtbocfg_module_exit);
|
|
+
|
|
+MODULE_AUTHOR("ikwzm");
|
|
+MODULE_DESCRIPTION("Device Tree Overlay Configuration File System");
|
|
+MODULE_LICENSE("Dual BSD/GPL");
|