1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Uclass for sandbox host interface, used to access files on the host which
4  * contain partitions and filesystem
5  *
6  * Copyright 2022 Google LLC
7  * Written by Simon Glass <sjg@chromium.org>
8  */
9 
10 #define LOG_CATEGORY UCLASS_HOST
11 
12 #include <blk.h>
13 #include <dm.h>
14 #include <malloc.h>
15 #include <part.h>
16 #include <sandbox_host.h>
17 #include <dm/device-internal.h>
18 #include <dm/lists.h>
19 #include <dm/uclass-internal.h>
20 
21 DECLARE_GLOBAL_DATA_PTR;
22 
23 /**
24  * struct host_priv - information kept by the host uclass
25  *
26  * @cur_dev: Currently selected host device, or NULL if none
27  */
28 struct host_priv {
29 	struct udevice *cur_dev;
30 };
31 
host_create_device(const char * label,bool removable,unsigned long blksz,struct udevice ** devp)32 int host_create_device(const char *label, bool removable, unsigned long blksz,
33 		       struct udevice **devp)
34 {
35 	char dev_name[30], *str, *label_new;
36 	struct host_sb_plat *plat;
37 	struct udevice *dev, *blk;
38 	int ret;
39 
40 	/* unbind any existing device with this label */
41 	dev = host_find_by_label(label);
42 	if (dev) {
43 		ret = host_detach_file(dev);
44 		if (ret)
45 			return log_msg_ret("det", ret);
46 
47 		ret = device_unbind(dev);
48 		if (ret)
49 			return log_msg_ret("unb", ret);
50 	}
51 
52 	snprintf(dev_name, sizeof(dev_name), "host-%s", label);
53 	str = strdup(dev_name);
54 	if (!str)
55 		return -ENOMEM;
56 
57 	label_new = strdup(label);
58 	if (!label_new) {
59 		ret = -ENOMEM;
60 		goto no_label;
61 	}
62 
63 	ret = device_bind_driver(gd->dm_root, "host_sb_drv", str, &dev);
64 	if (ret)
65 		goto no_dev;
66 	device_set_name_alloced(dev);
67 
68 	if (!blk_find_from_parent(dev, &blk)) {
69 		struct blk_desc *desc = dev_get_uclass_plat(blk);
70 
71 		desc->removable = removable;
72 
73 		/* update blk device's block size with the provided one */
74 		if (blksz != desc->blksz) {
75 			desc->blksz = blksz;
76 			desc->log2blksz = LOG2(desc->blksz);
77 		}
78 	}
79 
80 	plat = dev_get_plat(dev);
81 	plat->label = label_new;
82 	*devp = dev;
83 
84 	return 0;
85 
86 no_dev:
87 	free(label_new);
88 no_label:
89 	free(str);
90 
91 	return ret;
92 }
93 
host_attach_file(struct udevice * dev,const char * filename)94 int host_attach_file(struct udevice *dev, const char *filename)
95 {
96 	struct host_ops *ops = host_get_ops(dev);
97 
98 	if (!ops->attach_file)
99 		return -ENOSYS;
100 
101 	return ops->attach_file(dev, filename);
102 }
103 
host_create_attach_file(const char * label,const char * filename,bool removable,unsigned long blksz,struct udevice ** devp)104 int host_create_attach_file(const char *label, const char *filename,
105 			    bool removable, unsigned long blksz,
106 			    struct udevice **devp)
107 {
108 	struct udevice *dev;
109 	int ret;
110 
111 	ret = host_create_device(label, removable, blksz, &dev);
112 	if (ret)
113 		return log_msg_ret("cre", ret);
114 
115 	ret = host_attach_file(dev, filename);
116 	if (ret) {
117 		device_unbind(dev);
118 		return log_msg_ret("att", ret);
119 	}
120 	*devp = dev;
121 
122 	return 0;
123 }
124 
host_detach_file(struct udevice * dev)125 int host_detach_file(struct udevice *dev)
126 {
127 	struct host_ops *ops = host_get_ops(dev);
128 
129 	if (!ops->detach_file)
130 		return -ENOSYS;
131 
132 	if (dev == host_get_cur_dev())
133 		host_set_cur_dev(NULL);
134 
135 	return ops->detach_file(dev);
136 }
137 
host_find_by_label(const char * label)138 struct udevice *host_find_by_label(const char *label)
139 {
140 	struct udevice *dev;
141 	struct uclass *uc;
142 
143 	uclass_id_foreach_dev(UCLASS_HOST, dev, uc) {
144 		struct host_sb_plat *plat = dev_get_plat(dev);
145 
146 		if (plat->label && !strcmp(label, plat->label))
147 			return dev;
148 	}
149 
150 	return NULL;
151 }
152 
host_get_cur_dev(void)153 struct udevice *host_get_cur_dev(void)
154 {
155 	struct uclass *uc = uclass_find(UCLASS_HOST);
156 
157 	if (uc) {
158 		struct host_priv *priv = uclass_get_priv(uc);
159 
160 		return priv->cur_dev;
161 	}
162 
163 	return NULL;
164 }
165 
host_set_cur_dev(struct udevice * dev)166 void host_set_cur_dev(struct udevice *dev)
167 {
168 	struct uclass *uc = uclass_find(UCLASS_HOST);
169 
170 	if (uc) {
171 		struct host_priv *priv = uclass_get_priv(uc);
172 
173 		priv->cur_dev = dev;
174 	}
175 }
176 
177 UCLASS_DRIVER(host) = {
178 	.id		= UCLASS_HOST,
179 	.name		= "host",
180 #if CONFIG_IS_ENABLED(OF_REAL)
181 	.post_bind	= dm_scan_fdt_dev,
182 #endif
183 	.priv_auto	= sizeof(struct host_priv),
184 };
185