1 /*
2 * Copyright (C) 2017 Intel, Inc.
3 *
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15 #include <linux/acpi.h>
16 #include <linux/module.h>
17 #include <linux/platform_device.h>
18 #include <linux/property.h>
19 #include <linux/sysfs.h>
20
21 struct goldfish_fwdata_state {
22 struct device *dev;
23 struct kobject *properties_kobj;
24 struct kobject *android_kobj;
25 struct kobject *fstab_kobj;
26 struct kobject *system_kobj;
27 struct kobject *vendor_kobj;
28 };
29
30 static struct goldfish_fwdata_state state;
31
32 /* Called when <sysfs_device>/properties/<subpath>/<attr> is read. */
property_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)33 static ssize_t property_show(struct kobject *kobj, struct kobj_attribute *attr,
34 char *buf)
35 {
36 const char *prefix = NULL;
37 char key[128];
38 const char *value = NULL;
39 int ret;
40
41 /* It would be much more convenient if show() gave us the relative path
42 * to the file being read, e.g. properties/android/fstab/system/dev,
43 * which could be easily converted to a property key.
44 * TODO: Infer the relative path from kobj and remove all hard-coded
45 * property keys.
46 */
47 if (kobj == state.android_kobj) {
48 prefix = "android";
49 } else if (kobj == state.fstab_kobj) {
50 prefix = "android.fstab";
51 } else if (kobj == state.system_kobj) {
52 prefix = "android.fstab.system";
53 } else if (kobj == state.vendor_kobj) {
54 prefix = "android.fstab.vendor";
55 } else {
56 pr_err("%s: Unexpected folder\n", __func__);
57 return -EINVAL;
58 }
59 /* We don't put any file in properties/ directly, so prefix can't be
60 * empty.
61 */
62 snprintf(key, sizeof(key), "%s.%s", prefix, attr->attr.name);
63
64 ret = device_property_read_string(state.dev, key, &value);
65 if (ret) {
66 pr_err("%s: Failed to read property '%s', ret=%d\n", __func__,
67 key, ret);
68 return ret;
69 }
70 return scnprintf(buf, PAGE_SIZE, "%s\n", value);
71 }
72
73 #define DT_COMPATIBLE_ATTR(_folder) \
74 struct kobj_attribute _folder##_compatible_attr = { \
75 .attr = { .name = "compatible", .mode = S_IRUGO, }, \
76 .show = property_show, \
77 }
78
79 static DT_COMPATIBLE_ATTR(android);
80 static DT_COMPATIBLE_ATTR(fstab);
81 static DT_COMPATIBLE_ATTR(system);
82 static DT_COMPATIBLE_ATTR(vendor);
83
84 #define FSTAB_PROPERTY_ATTR(_partition, _name) \
85 struct kobj_attribute _partition##_##_name##_attr = { \
86 .attr = { \
87 .name = __stringify(_name), \
88 .mode = S_IRUGO, \
89 }, \
90 .show = property_show, \
91 }
92
93 static FSTAB_PROPERTY_ATTR(system, dev);
94 static FSTAB_PROPERTY_ATTR(system, type);
95 static FSTAB_PROPERTY_ATTR(system, mnt_flags);
96 static FSTAB_PROPERTY_ATTR(system, fsmgr_flags);
97
98 static FSTAB_PROPERTY_ATTR(vendor, dev);
99 static FSTAB_PROPERTY_ATTR(vendor, type);
100 static FSTAB_PROPERTY_ATTR(vendor, mnt_flags);
101 static FSTAB_PROPERTY_ATTR(vendor, fsmgr_flags);
102
103 static struct attribute *system_attrs[] = {
104 &system_compatible_attr.attr,
105 &system_dev_attr.attr,
106 &system_type_attr.attr,
107 &system_mnt_flags_attr.attr,
108 &system_fsmgr_flags_attr.attr,
109 NULL,
110 };
111 ATTRIBUTE_GROUPS(system);
112
113 static struct attribute *vendor_attrs[] = {
114 &vendor_compatible_attr.attr,
115 &vendor_dev_attr.attr,
116 &vendor_type_attr.attr,
117 &vendor_mnt_flags_attr.attr,
118 &vendor_fsmgr_flags_attr.attr,
119 NULL,
120 };
121 static struct attribute_group vendor_group = {
122 .attrs = vendor_attrs,
123 };
124
create_folder(struct kobject * parent,const char * name)125 static struct kobject *create_folder(struct kobject *parent, const char *name)
126 {
127 struct kobject *kobj;
128
129 kobj = kobject_create_and_add(name, parent);
130 if (!kobj) {
131 pr_err("%s: Failed to create %s/\n", __func__, name);
132 return NULL;
133 }
134 return kobj;
135 }
136
create_folder_with_file(struct kobject * parent,const char * name,struct kobj_attribute * attr)137 static struct kobject *create_folder_with_file(struct kobject *parent,
138 const char *name,
139 struct kobj_attribute *attr)
140 {
141 struct kobject *kobj;
142
143 kobj = create_folder(parent, name);
144 if (kobj) {
145 /* Note: Usually drivers should use device_create_file() rather
146 * than sysfs_create_file(), but the former does not support
147 * creating the file in a subfolder.
148 */
149 int ret;
150
151 ret = sysfs_create_file(kobj, &attr->attr);
152 if (ret) {
153 pr_err("%s: Failed to create %s/%s: ret=%d\n", __func__,
154 name, attr->attr.name, ret);
155 kobject_put(kobj);
156 return NULL;
157 }
158 }
159 return kobj;
160 }
161
remove_folder_with_file(struct kobject * kobj,struct kobj_attribute * attr)162 static void remove_folder_with_file(struct kobject *kobj,
163 struct kobj_attribute *attr)
164 {
165 sysfs_remove_file(kobj, &attr->attr);
166 kobject_put(kobj);
167 }
168
create_folder_with_files(struct kobject * parent,const char * name,const struct attribute_group * group)169 static struct kobject *create_folder_with_files(
170 struct kobject *parent,
171 const char *name,
172 const struct attribute_group *group)
173 {
174 struct kobject *kobj;
175
176 kobj = create_folder(parent, name);
177 if (kobj) {
178 /* Note: Usually drivers should use device_add_groups() rather
179 * than sysfs_create_group(), but the former does not support
180 * creating the folder in a subfolder.
181 */
182 int ret;
183
184 ret = sysfs_create_group(kobj, group);
185 if (ret) {
186 pr_err("%s: Failed to create %s/*: ret=%d\n", __func__,
187 name, ret);
188 kobject_put(kobj);
189 return NULL;
190 }
191 }
192 return kobj;
193 }
194
remove_folder_with_files(struct kobject * kobj,const struct attribute_group * group)195 static void remove_folder_with_files(struct kobject *kobj,
196 const struct attribute_group *group)
197 {
198 sysfs_remove_group(kobj, group);
199 kobject_put(kobj);
200 }
201
clean_up(void)202 static void clean_up(void)
203 {
204 if (state.vendor_kobj) {
205 /* Delete <sysfs_device>/properties/android/fstab/vendor/ */
206 remove_folder_with_files(state.vendor_kobj, &vendor_group);
207 state.vendor_kobj = NULL;
208 }
209 if (state.system_kobj) {
210 /* Delete <sysfs_device>/properties/android/fstab/system/ */
211 remove_folder_with_files(state.system_kobj, &system_group);
212 state.system_kobj = NULL;
213 }
214 if (state.fstab_kobj) {
215 /* Delete <sysfs_device>/properties/android/fstab/ */
216 remove_folder_with_file(state.fstab_kobj,
217 &fstab_compatible_attr);
218 state.fstab_kobj = NULL;
219 }
220 if (state.android_kobj) {
221 /* Delete <sysfs_device>/properties/android/ */
222 remove_folder_with_file(state.android_kobj,
223 &android_compatible_attr);
224 state.android_kobj = NULL;
225 }
226 if (state.properties_kobj) {
227 /* Delete <sysfs_device>/properties/ */
228 kobject_put(state.properties_kobj);
229 state.properties_kobj = NULL;
230 }
231 }
232
goldfish_fwdata_probe(struct platform_device * pdev)233 static int goldfish_fwdata_probe(struct platform_device *pdev)
234 {
235 int ret = -EIO;
236
237 state.dev = &pdev->dev;
238 /* Create <sysfs_device>/properties/ */
239 state.properties_kobj = create_folder(&state.dev->kobj, "properties");
240 if (!state.properties_kobj)
241 goto out;
242
243 /* TODO: Iterate over all device properties in firmware, and dynamically
244 * create sysfs nodes under <sysfs_device>/properties/
245 */
246
247 /* Create <sysfs_device>/properties/android/compatible */
248 state.android_kobj = create_folder_with_file(state.properties_kobj,
249 "android",
250 &android_compatible_attr);
251 if (!state.android_kobj)
252 goto out;
253
254 /* Create <sysfs_device>/properties/android/fstab/compatible */
255 state.fstab_kobj = create_folder_with_file(state.android_kobj, "fstab",
256 &fstab_compatible_attr);
257 if (!state.fstab_kobj)
258 goto out;
259
260 if (device_property_present(state.dev, "android.fstab.system.dev")) {
261 /* Firmware contains fstab config for early mount of /system */
262 state.system_kobj = create_folder_with_files(state.fstab_kobj,
263 "system",
264 &system_group);
265 if (!state.system_kobj)
266 goto out;
267 }
268 if (device_property_present(state.dev, "android.fstab.vendor.dev")) {
269 /* Firmware contains fstab config for early mount of /vendor */
270 state.vendor_kobj = create_folder_with_files(state.fstab_kobj,
271 "vendor",
272 &vendor_group);
273 if (!state.vendor_kobj)
274 goto out;
275 }
276 return 0;
277
278 out:
279 clean_up();
280 return ret;
281 }
282
goldfish_fwdata_remove(struct platform_device * pdev)283 static int goldfish_fwdata_remove(struct platform_device *pdev)
284 {
285 clean_up();
286 return 0;
287 }
288
289 static const struct acpi_device_id goldfish_fwdata_acpi_match[] = {
290 { "ANDR0001", 0 },
291 { },
292 };
293 MODULE_DEVICE_TABLE(acpi, goldfish_fwdata_acpi_match);
294
295 static struct platform_driver goldfish_fwdata_driver = {
296 .probe = goldfish_fwdata_probe,
297 .remove = goldfish_fwdata_remove,
298 .driver = {
299 .name = "goldfish_fwdata",
300 .acpi_match_table = ACPI_PTR(goldfish_fwdata_acpi_match),
301 }
302 };
303
304 module_platform_driver(goldfish_fwdata_driver);
305