u-boot/drivers/block/host-uclass.c
Simon Glass 9bd1aa8af2 dm: sandbox: Create a new HOST uclass
Sandbox supports block devices which can access files on the host machine.
At present there is no uclass for this. The devices are attached to the
root devic. The block-device type is therefore set to UCLASS_ROOT which
is confusing.

Block devices should be attached to a 'media' device instead, something
which handles access to the actual media and provides the block driver
for the block device.

Create a new uclass to handle this. It supports two operations, to attach
and detach a file on the host machine.

For now this is not fully plumbed in.

Signed-off-by: Simon Glass <sjg@chromium.org>
2022-11-07 16:24:30 -07:00

177 lines
3.3 KiB
C

// 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),
};