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