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