• 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 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