aboutsummaryrefslogtreecommitdiff
path: root/drivers/block/host-uclass.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/block/host-uclass.c')
-rw-r--r--drivers/block/host-uclass.c176
1 files changed, 176 insertions, 0 deletions
diff --git a/drivers/block/host-uclass.c b/drivers/block/host-uclass.c
new file mode 100644
index 0000000..6460d96
--- /dev/null
+++ b/drivers/block/host-uclass.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Uclass for sandbox host interface, used to access files on the host which
+ * contain partitions and filesystem
+ *
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#define LOG_CATEGORY UCLASS_HOST
+
+#include <common.h>
+#include <blk.h>
+#include <dm.h>
+#include <malloc.h>
+#include <sandbox_host.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/uclass-internal.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * struct host_priv - information kept by the host uclass
+ *
+ * @cur_dev: Currently selected host device, or NULL if none
+ */
+struct host_priv {
+ struct udevice *cur_dev;
+};
+
+int host_create_device(const char *label, bool removable, struct udevice **devp)
+{
+ char dev_name[30], *str, *label_new;
+ struct host_sb_plat *plat;
+ struct udevice *dev, *blk;
+ int ret;
+
+ /* unbind any existing device with this label */
+ dev = host_find_by_label(label);
+ if (dev) {
+ ret = host_detach_file(dev);
+ if (ret)
+ return log_msg_ret("det", ret);
+
+ ret = device_unbind(dev);
+ if (ret)
+ return log_msg_ret("unb", ret);
+ }
+
+ snprintf(dev_name, sizeof(dev_name), "host-%s", label);
+ str = strdup(dev_name);
+ if (!str)
+ return -ENOMEM;
+
+ label_new = strdup(label);
+ if (!label_new) {
+ ret = -ENOMEM;
+ goto no_label;
+ }
+
+ ret = device_bind_driver(gd->dm_root, "host_sb_drv", str, &dev);
+ if (ret)
+ goto no_dev;
+ device_set_name_alloced(dev);
+
+ if (!blk_find_from_parent(dev, &blk)) {
+ struct blk_desc *desc = dev_get_uclass_plat(blk);
+
+ desc->removable = removable;
+ }
+
+ plat = dev_get_plat(dev);
+ plat->label = label_new;
+ *devp = dev;
+
+ return 0;
+
+no_dev:
+ free(label_new);
+no_label:
+ free(str);
+
+ return ret;
+}
+
+int host_attach_file(struct udevice *dev, const char *filename)
+{
+ struct host_ops *ops = host_get_ops(dev);
+
+ if (!ops->attach_file)
+ return -ENOSYS;
+
+ return ops->attach_file(dev, filename);
+}
+
+int host_create_attach_file(const char *label, const char *filename,
+ bool removable, struct udevice **devp)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = host_create_device(label, removable, &dev);
+ if (ret)
+ return log_msg_ret("cre", ret);
+
+ ret = host_attach_file(dev, filename);
+ if (ret) {
+ device_unbind(dev);
+ return log_msg_ret("att", ret);
+ }
+ *devp = dev;
+
+ return 0;
+}
+
+int host_detach_file(struct udevice *dev)
+{
+ struct host_ops *ops = host_get_ops(dev);
+
+ if (!ops->detach_file)
+ return -ENOSYS;
+
+ if (dev == host_get_cur_dev())
+ host_set_cur_dev(NULL);
+
+ return ops->detach_file(dev);
+}
+
+struct udevice *host_find_by_label(const char *label)
+{
+ struct udevice *dev;
+ struct uclass *uc;
+
+ uclass_id_foreach_dev(UCLASS_HOST, dev, uc) {
+ struct host_sb_plat *plat = dev_get_plat(dev);
+
+ if (plat->label && !strcmp(label, plat->label))
+ return dev;
+ }
+
+ return NULL;
+}
+
+struct udevice *host_get_cur_dev(void)
+{
+ struct uclass *uc = uclass_find(UCLASS_HOST);
+
+ if (uc) {
+ struct host_priv *priv = uclass_get_priv(uc);
+
+ return priv->cur_dev;
+ }
+
+ return NULL;
+}
+
+void host_set_cur_dev(struct udevice *dev)
+{
+ struct uclass *uc = uclass_find(UCLASS_HOST);
+
+ if (uc) {
+ struct host_priv *priv = uclass_get_priv(uc);
+
+ priv->cur_dev = dev;
+ }
+}
+
+UCLASS_DRIVER(host) = {
+ .id = UCLASS_HOST,
+ .name = "host",
+#if CONFIG_IS_ENABLED(OF_REAL)
+ .post_bind = dm_scan_fdt_dev,
+#endif
+ .priv_auto = sizeof(struct host_priv),
+};