| // 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 <blk.h> | |
| #include <dm.h> | |
| #include <malloc.h> | |
| #include <part.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, unsigned long blksz, | |
| 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; | |
| /* update blk device's block size with the provided one */ | |
| if (blksz != desc->blksz) { | |
| desc->blksz = blksz; | |
| desc->log2blksz = LOG2(desc->blksz); | |
| } | |
| } | |
| 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, unsigned long blksz, | |
| struct udevice **devp) | |
| { | |
| struct udevice *dev; | |
| int ret; | |
| ret = host_create_device(label, removable, blksz, &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), | |
| }; |