• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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