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 static struct attribute_group system_group = {
112 .attrs = system_attrs,
113 };
114
115 static struct attribute *vendor_attrs[] = {
116 &vendor_compatible_attr.attr,
117 &vendor_dev_attr.attr,
118 &vendor_type_attr.attr,
119 &vendor_mnt_flags_attr.attr,
120 &vendor_fsmgr_flags_attr.attr,
121 NULL,
122 };
123 static struct attribute_group vendor_group = {
124 .attrs = vendor_attrs,
125 };
126
create_folder(struct kobject * parent,const char * name)127 static struct kobject *create_folder(struct kobject *parent, const char *name)
128 {
129 struct kobject *kobj;
130
131 kobj = kobject_create_and_add(name, parent);
132 if (!kobj) {
133 pr_err("%s: Failed to create %s/\n", __func__, name);
134 return NULL;
135 }
136 return kobj;
137 }
138
create_folder_with_file(struct kobject * parent,const char * name,struct kobj_attribute * attr)139 static struct kobject *create_folder_with_file(struct kobject *parent,
140 const char *name,
141 struct kobj_attribute *attr)
142 {
143 struct kobject *kobj;
144
145 kobj = create_folder(parent, name);
146 if (kobj) {
147 /* Note: Usually drivers should use device_create_file() rather
148 * than sysfs_create_file(), but the former does not support
149 * creating the file in a subfolder.
150 */
151 int ret;
152
153 ret = sysfs_create_file(kobj, &attr->attr);
154 if (ret) {
155 pr_err("%s: Failed to create %s/%s: ret=%d\n", __func__,
156 name, attr->attr.name, ret);
157 kobject_put(kobj);
158 return NULL;
159 }
160 }
161 return kobj;
162 }
163
remove_folder_with_file(struct kobject * kobj,struct kobj_attribute * attr)164 static void remove_folder_with_file(struct kobject *kobj,
165 struct kobj_attribute *attr)
166 {
167 sysfs_remove_file(kobj, &attr->attr);
168 kobject_put(kobj);
169 }
170
create_folder_with_files(struct kobject * parent,const char * name,struct attribute_group * group)171 static struct kobject *create_folder_with_files(struct kobject *parent,
172 const char *name,
173 struct attribute_group *group)
174 {
175 struct kobject *kobj;
176
177 kobj = create_folder(parent, name);
178 if (kobj) {
179 /* Note: Usually drivers should use device_add_groups() rather
180 * than sysfs_create_group(), but the former does not support
181 * creating the folder in a subfolder.
182 */
183 int ret;
184
185 ret = sysfs_create_group(kobj, group);
186 if (ret) {
187 pr_err("%s: Failed to create %s/*: ret=%d\n", __func__,
188 name, ret);
189 kobject_put(kobj);
190 return NULL;
191 }
192 }
193 return kobj;
194 }
195
remove_folder_with_files(struct kobject * kobj,struct attribute_group * group)196 static void remove_folder_with_files(struct kobject *kobj,
197 struct attribute_group *group)
198 {
199 sysfs_remove_group(kobj, group);
200 kobject_put(kobj);
201 }
202
clean_up(void)203 static void clean_up(void)
204 {
205 if (state.vendor_kobj) {
206 /* Delete <sysfs_device>/properties/android/fstab/vendor/ */
207 remove_folder_with_files(state.vendor_kobj, &vendor_group);
208 state.vendor_kobj = NULL;
209 }
210 if (state.system_kobj) {
211 /* Delete <sysfs_device>/properties/android/fstab/system/ */
212 remove_folder_with_files(state.system_kobj, &system_group);
213 state.system_kobj = NULL;
214 }
215 if (state.fstab_kobj) {
216 /* Delete <sysfs_device>/properties/android/fstab/ */
217 remove_folder_with_file(state.fstab_kobj,
218 &fstab_compatible_attr);
219 state.fstab_kobj = NULL;
220 }
221 if (state.android_kobj) {
222 /* Delete <sysfs_device>/properties/android/ */
223 remove_folder_with_file(state.android_kobj,
224 &android_compatible_attr);
225 state.android_kobj = NULL;
226 }
227 if (state.properties_kobj) {
228 /* Delete <sysfs_device>/properties/ */
229 kobject_put(state.properties_kobj);
230 state.properties_kobj = NULL;
231 }
232 }
233
goldfish_fwdata_probe(struct platform_device * pdev)234 static int goldfish_fwdata_probe(struct platform_device *pdev)
235 {
236 int ret = -EIO;
237
238 state.dev = &pdev->dev;
239 /* Create <sysfs_device>/properties/ */
240 state.properties_kobj = create_folder(&state.dev->kobj, "properties");
241 if (!state.properties_kobj)
242 goto out;
243
244 /* TODO: Iterate over all device properties in firmware, and dynamically
245 * create sysfs nodes under <sysfs_device>/properties/
246 */
247
248 /* Create <sysfs_device>/properties/android/compatible */
249 state.android_kobj = create_folder_with_file(state.properties_kobj,
250 "android",
251 &android_compatible_attr);
252 if (!state.android_kobj)
253 goto out;
254
255 /* Create <sysfs_device>/properties/android/fstab/compatible */
256 state.fstab_kobj = create_folder_with_file(state.android_kobj, "fstab",
257 &fstab_compatible_attr);
258 if (!state.fstab_kobj)
259 goto out;
260
261 if (device_property_present(state.dev, "android.fstab.system.dev")) {
262 /* Firmware contains fstab config for early mount of /system */
263 state.system_kobj = create_folder_with_files(state.fstab_kobj,
264 "system",
265 &system_group);
266 if (!state.system_kobj)
267 goto out;
268 }
269 if (device_property_present(state.dev, "android.fstab.vendor.dev")) {
270 /* Firmware contains fstab config for early mount of /vendor */
271 state.vendor_kobj = create_folder_with_files(state.fstab_kobj,
272 "vendor",
273 &vendor_group);
274 if (!state.vendor_kobj)
275 goto out;
276 }
277 return 0;
278
279 out:
280 clean_up();
281 return ret;
282 }
283
goldfish_fwdata_remove(struct platform_device * pdev)284 static int goldfish_fwdata_remove(struct platform_device *pdev)
285 {
286 clean_up();
287 return 0;
288 }
289
290 static const struct acpi_device_id goldfish_fwdata_acpi_match[] = {
291 { "ANDR0001", 0 },
292 { },
293 };
294 MODULE_DEVICE_TABLE(acpi, goldfish_fwdata_acpi_match);
295
296 static struct platform_driver goldfish_fwdata_driver = {
297 .probe = goldfish_fwdata_probe,
298 .remove = goldfish_fwdata_remove,
299 .driver = {
300 .name = "goldfish_fwdata",
301 .owner = THIS_MODULE,
302 .acpi_match_table = ACPI_PTR(goldfish_fwdata_acpi_match),
303 }
304 };
305
306 module_platform_driver(goldfish_fwdata_driver);
307