• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * class-dual-role.c
3  *
4  * Copyright (C) 2015 Google, Inc.
5  *
6  * This software is licensed under the terms of the GNU General Public
7  * License version 2, as published by the Free Software Foundation, and
8  * may be copied, distributed, and modified under those terms.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16 
17 #include <linux/ctype.h>
18 #include <linux/device.h>
19 #include <linux/usb/class-dual-role.h>
20 #include <linux/err.h>
21 #include <linux/init.h>
22 #include <linux/module.h>
23 #include <linux/slab.h>
24 #include <linux/stat.h>
25 #include <linux/types.h>
26 
27 #define DUAL_ROLE_NOTIFICATION_TIMEOUT 2000
28 
29 static ssize_t dual_role_store_property(struct device *dev,
30 					struct device_attribute *attr,
31 					const char *buf, size_t count);
32 static ssize_t dual_role_show_property(struct device *dev,
33 				       struct device_attribute *attr,
34 				       char *buf);
35 
36 #define DUAL_ROLE_ATTR(_name)				\
37 {							\
38 	.attr = { .name = #_name },			\
39 	.show = dual_role_show_property,		\
40 	.store = dual_role_store_property,		\
41 }
42 
43 static struct device_attribute dual_role_attrs[] = {
44 	DUAL_ROLE_ATTR(supported_modes),
45 	DUAL_ROLE_ATTR(mode),
46 	DUAL_ROLE_ATTR(power_role),
47 	DUAL_ROLE_ATTR(data_role),
48 	DUAL_ROLE_ATTR(powers_vconn),
49 };
50 
51 struct class *dual_role_class;
52 EXPORT_SYMBOL_GPL(dual_role_class);
53 
54 static struct device_type dual_role_dev_type;
55 
kstrdupcase(const char * str,gfp_t gfp,bool to_upper)56 static char *kstrdupcase(const char *str, gfp_t gfp, bool to_upper)
57 {
58 	char *ret, *ustr;
59 
60 	ustr = ret = kmalloc(strlen(str) + 1, gfp);
61 
62 	if (!ret)
63 		return NULL;
64 
65 	while (*str)
66 		*ustr++ = to_upper ? toupper(*str++) : tolower(*str++);
67 
68 	*ustr = 0;
69 
70 	return ret;
71 }
72 
dual_role_changed_work(struct work_struct * work)73 static void dual_role_changed_work(struct work_struct *work)
74 {
75 	struct dual_role_phy_instance *dual_role =
76 	    container_of(work, struct dual_role_phy_instance,
77 			 changed_work);
78 
79 	dev_dbg(&dual_role->dev, "%s\n", __func__);
80 	kobject_uevent(&dual_role->dev.kobj, KOBJ_CHANGE);
81 }
82 
dual_role_instance_changed(struct dual_role_phy_instance * dual_role)83 void dual_role_instance_changed(struct dual_role_phy_instance *dual_role)
84 {
85 	dev_dbg(&dual_role->dev, "%s\n", __func__);
86 	pm_wakeup_event(&dual_role->dev, DUAL_ROLE_NOTIFICATION_TIMEOUT);
87 	schedule_work(&dual_role->changed_work);
88 }
89 EXPORT_SYMBOL_GPL(dual_role_instance_changed);
90 
dual_role_get_property(struct dual_role_phy_instance * dual_role,enum dual_role_property prop,unsigned int * val)91 int dual_role_get_property(struct dual_role_phy_instance *dual_role,
92 			   enum dual_role_property prop,
93 			   unsigned int *val)
94 {
95 	return dual_role->desc->get_property(dual_role, prop, val);
96 }
97 EXPORT_SYMBOL_GPL(dual_role_get_property);
98 
dual_role_set_property(struct dual_role_phy_instance * dual_role,enum dual_role_property prop,const unsigned int * val)99 int dual_role_set_property(struct dual_role_phy_instance *dual_role,
100 			   enum dual_role_property prop,
101 			   const unsigned int *val)
102 {
103 	if (!dual_role->desc->set_property)
104 		return -ENODEV;
105 
106 	return dual_role->desc->set_property(dual_role, prop, val);
107 }
108 EXPORT_SYMBOL_GPL(dual_role_set_property);
109 
dual_role_property_is_writeable(struct dual_role_phy_instance * dual_role,enum dual_role_property prop)110 int dual_role_property_is_writeable(struct dual_role_phy_instance *dual_role,
111 				    enum dual_role_property prop)
112 {
113 	if (!dual_role->desc->property_is_writeable)
114 		return -ENODEV;
115 
116 	return dual_role->desc->property_is_writeable(dual_role, prop);
117 }
118 EXPORT_SYMBOL_GPL(dual_role_property_is_writeable);
119 
dual_role_dev_release(struct device * dev)120 static void dual_role_dev_release(struct device *dev)
121 {
122 	struct dual_role_phy_instance *dual_role =
123 	    container_of(dev, struct dual_role_phy_instance, dev);
124 	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
125 	kfree(dual_role);
126 }
127 
128 static struct dual_role_phy_instance *__must_check
__dual_role_register(struct device * parent,const struct dual_role_phy_desc * desc)129 __dual_role_register(struct device *parent,
130 		     const struct dual_role_phy_desc *desc)
131 {
132 	struct device *dev;
133 	struct dual_role_phy_instance *dual_role;
134 	int rc;
135 
136 	dual_role = kzalloc(sizeof(*dual_role), GFP_KERNEL);
137 	if (!dual_role)
138 		return ERR_PTR(-ENOMEM);
139 
140 	dev = &dual_role->dev;
141 
142 	device_initialize(dev);
143 
144 	dev->class = dual_role_class;
145 	dev->type = &dual_role_dev_type;
146 	dev->parent = parent;
147 	dev->release = dual_role_dev_release;
148 	dev_set_drvdata(dev, dual_role);
149 	dual_role->desc = desc;
150 
151 	rc = dev_set_name(dev, "%s", desc->name);
152 	if (rc)
153 		goto dev_set_name_failed;
154 
155 	INIT_WORK(&dual_role->changed_work, dual_role_changed_work);
156 
157 	rc = device_init_wakeup(dev, true);
158 	if (rc)
159 		goto wakeup_init_failed;
160 
161 	rc = device_add(dev);
162 	if (rc)
163 		goto device_add_failed;
164 
165 	dual_role_instance_changed(dual_role);
166 
167 	return dual_role;
168 
169 device_add_failed:
170 	device_init_wakeup(dev, false);
171 wakeup_init_failed:
172 dev_set_name_failed:
173 	put_device(dev);
174 	kfree(dual_role);
175 
176 	return ERR_PTR(rc);
177 }
178 
dual_role_instance_unregister(struct dual_role_phy_instance * dual_role)179 static void dual_role_instance_unregister(struct dual_role_phy_instance
180 					  *dual_role)
181 {
182 	cancel_work_sync(&dual_role->changed_work);
183 	device_init_wakeup(&dual_role->dev, false);
184 	device_unregister(&dual_role->dev);
185 }
186 
devm_dual_role_release(struct device * dev,void * res)187 static void devm_dual_role_release(struct device *dev, void *res)
188 {
189 	struct dual_role_phy_instance **dual_role = res;
190 
191 	dual_role_instance_unregister(*dual_role);
192 }
193 
194 struct dual_role_phy_instance *__must_check
devm_dual_role_instance_register(struct device * parent,const struct dual_role_phy_desc * desc)195 devm_dual_role_instance_register(struct device *parent,
196 				 const struct dual_role_phy_desc *desc)
197 {
198 	struct dual_role_phy_instance **ptr, *dual_role;
199 
200 	ptr = devres_alloc(devm_dual_role_release, sizeof(*ptr), GFP_KERNEL);
201 
202 	if (!ptr)
203 		return ERR_PTR(-ENOMEM);
204 	dual_role = __dual_role_register(parent, desc);
205 	if (IS_ERR(dual_role)) {
206 		devres_free(ptr);
207 	} else {
208 		*ptr = dual_role;
209 		devres_add(parent, ptr);
210 	}
211 	return dual_role;
212 }
213 EXPORT_SYMBOL_GPL(devm_dual_role_instance_register);
214 
devm_dual_role_match(struct device * dev,void * res,void * data)215 static int devm_dual_role_match(struct device *dev, void *res, void *data)
216 {
217 	struct dual_role_phy_instance **r = res;
218 
219 	if (WARN_ON(!r || !*r))
220 		return 0;
221 
222 	return *r == data;
223 }
224 
devm_dual_role_instance_unregister(struct device * dev,struct dual_role_phy_instance * dual_role)225 void devm_dual_role_instance_unregister(struct device *dev,
226 					struct dual_role_phy_instance
227 					*dual_role)
228 {
229 	int rc;
230 
231 	rc = devres_release(dev, devm_dual_role_release,
232 			    devm_dual_role_match, dual_role);
233 	WARN_ON(rc);
234 }
235 EXPORT_SYMBOL_GPL(devm_dual_role_instance_unregister);
236 
dual_role_get_drvdata(struct dual_role_phy_instance * dual_role)237 void *dual_role_get_drvdata(struct dual_role_phy_instance *dual_role)
238 {
239 	return dual_role->drv_data;
240 }
241 EXPORT_SYMBOL_GPL(dual_role_get_drvdata);
242 
243 /***************** Device attribute functions **************************/
244 
245 /* port type */
246 static char *supported_modes_text[] = {
247 	"ufp dfp", "dfp", "ufp"
248 };
249 
250 /* current mode */
251 static char *mode_text[] = {
252 	"ufp", "dfp", "none"
253 };
254 
255 /* Power role */
256 static char *pr_text[] = {
257 	"source", "sink", "none"
258 };
259 
260 /* Data role */
261 static char *dr_text[] = {
262 	"host", "device", "none"
263 };
264 
265 /* Vconn supply */
266 static char *vconn_supply_text[] = {
267 	"n", "y"
268 };
269 
dual_role_show_property(struct device * dev,struct device_attribute * attr,char * buf)270 static ssize_t dual_role_show_property(struct device *dev,
271 				       struct device_attribute *attr, char *buf)
272 {
273 	ssize_t ret = 0;
274 	struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev);
275 	const ptrdiff_t off = attr - dual_role_attrs;
276 	unsigned int value;
277 
278 	if (off == DUAL_ROLE_PROP_SUPPORTED_MODES) {
279 		value = dual_role->desc->supported_modes;
280 	} else {
281 		ret = dual_role_get_property(dual_role, off, &value);
282 
283 		if (ret < 0) {
284 			if (ret == -ENODATA)
285 				dev_dbg(dev,
286 					"driver has no data for `%s' property\n",
287 					attr->attr.name);
288 			else if (ret != -ENODEV)
289 				dev_err(dev,
290 					"driver failed to report `%s' property: %zd\n",
291 					attr->attr.name, ret);
292 			return ret;
293 		}
294 	}
295 
296 	if (off == DUAL_ROLE_PROP_SUPPORTED_MODES) {
297 		BUILD_BUG_ON(DUAL_ROLE_PROP_SUPPORTED_MODES_TOTAL !=
298 			ARRAY_SIZE(supported_modes_text));
299 		if (value < DUAL_ROLE_PROP_SUPPORTED_MODES_TOTAL)
300 			return snprintf(buf, PAGE_SIZE, "%s\n",
301 					supported_modes_text[value]);
302 		else
303 			return -EIO;
304 	} else if (off == DUAL_ROLE_PROP_MODE) {
305 		BUILD_BUG_ON(DUAL_ROLE_PROP_MODE_TOTAL !=
306 			ARRAY_SIZE(mode_text));
307 		if (value < DUAL_ROLE_PROP_MODE_TOTAL)
308 			return snprintf(buf, PAGE_SIZE, "%s\n",
309 					mode_text[value]);
310 		else
311 			return -EIO;
312 	} else if (off == DUAL_ROLE_PROP_PR) {
313 		BUILD_BUG_ON(DUAL_ROLE_PROP_PR_TOTAL != ARRAY_SIZE(pr_text));
314 		if (value < DUAL_ROLE_PROP_PR_TOTAL)
315 			return snprintf(buf, PAGE_SIZE, "%s\n",
316 					pr_text[value]);
317 		else
318 			return -EIO;
319 	} else if (off == DUAL_ROLE_PROP_DR) {
320 		BUILD_BUG_ON(DUAL_ROLE_PROP_DR_TOTAL != ARRAY_SIZE(dr_text));
321 		if (value < DUAL_ROLE_PROP_DR_TOTAL)
322 			return snprintf(buf, PAGE_SIZE, "%s\n",
323 					dr_text[value]);
324 		else
325 			return -EIO;
326 	} else if (off == DUAL_ROLE_PROP_VCONN_SUPPLY) {
327 		BUILD_BUG_ON(DUAL_ROLE_PROP_VCONN_SUPPLY_TOTAL !=
328 				ARRAY_SIZE(vconn_supply_text));
329 		if (value < DUAL_ROLE_PROP_VCONN_SUPPLY_TOTAL)
330 			return snprintf(buf, PAGE_SIZE, "%s\n",
331 					vconn_supply_text[value]);
332 		else
333 			return -EIO;
334 	} else
335 		return -EIO;
336 }
337 
dual_role_store_property(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)338 static ssize_t dual_role_store_property(struct device *dev,
339 					struct device_attribute *attr,
340 					const char *buf, size_t count)
341 {
342 	ssize_t ret;
343 	struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev);
344 	const ptrdiff_t off = attr - dual_role_attrs;
345 	unsigned int value;
346 	int total, i;
347 	char *dup_buf, **text_array;
348 	bool result = false;
349 
350 	dup_buf = kstrdupcase(buf, GFP_KERNEL, false);
351 	switch (off) {
352 	case DUAL_ROLE_PROP_MODE:
353 		total = DUAL_ROLE_PROP_MODE_TOTAL;
354 		text_array = mode_text;
355 		break;
356 	case DUAL_ROLE_PROP_PR:
357 		total = DUAL_ROLE_PROP_PR_TOTAL;
358 		text_array = pr_text;
359 		break;
360 	case DUAL_ROLE_PROP_DR:
361 		total = DUAL_ROLE_PROP_DR_TOTAL;
362 		text_array = dr_text;
363 		break;
364 	case DUAL_ROLE_PROP_VCONN_SUPPLY:
365 		ret = strtobool(dup_buf, &result);
366 		value = result;
367 		if (!ret)
368 			goto setprop;
369 	default:
370 		ret = -EINVAL;
371 		goto error;
372 	}
373 
374 	for (i = 0; i <= total; i++) {
375 		if (i == total) {
376 			ret = -ENOTSUPP;
377 			goto error;
378 		}
379 		if (!strncmp(*(text_array + i), dup_buf,
380 			     strlen(*(text_array + i)))) {
381 			value = i;
382 			break;
383 		}
384 	}
385 
386 setprop:
387 	ret = dual_role->desc->set_property(dual_role, off, &value);
388 
389 error:
390 	kfree(dup_buf);
391 
392 	if (ret < 0)
393 		return ret;
394 
395 	return count;
396 }
397 
dual_role_attr_is_visible(struct kobject * kobj,struct attribute * attr,int attrno)398 static umode_t dual_role_attr_is_visible(struct kobject *kobj,
399 					 struct attribute *attr, int attrno)
400 {
401 	struct device *dev = container_of(kobj, struct device, kobj);
402 	struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev);
403 	umode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
404 	int i;
405 
406 	if (attrno == DUAL_ROLE_PROP_SUPPORTED_MODES)
407 		return mode;
408 
409 	for (i = 0; i < dual_role->desc->num_properties; i++) {
410 		int property = dual_role->desc->properties[i];
411 
412 		if (property == attrno) {
413 			if (dual_role->desc->property_is_writeable &&
414 			    dual_role_property_is_writeable(dual_role, property)
415 			    > 0)
416 				mode |= S_IWUSR;
417 
418 			return mode;
419 		}
420 	}
421 
422 	return 0;
423 }
424 
425 static struct attribute *__dual_role_attrs[ARRAY_SIZE(dual_role_attrs) + 1];
426 
427 static struct attribute_group dual_role_attr_group = {
428 	.attrs = __dual_role_attrs,
429 	.is_visible = dual_role_attr_is_visible,
430 };
431 
432 static const struct attribute_group *dual_role_attr_groups[] = {
433 	&dual_role_attr_group,
434 	NULL,
435 };
436 
dual_role_init_attrs(struct device_type * dev_type)437 void dual_role_init_attrs(struct device_type *dev_type)
438 {
439 	int i;
440 
441 	dev_type->groups = dual_role_attr_groups;
442 
443 	for (i = 0; i < ARRAY_SIZE(dual_role_attrs); i++)
444 		__dual_role_attrs[i] = &dual_role_attrs[i].attr;
445 }
446 
dual_role_uevent(struct device * dev,struct kobj_uevent_env * env)447 int dual_role_uevent(struct device *dev, struct kobj_uevent_env *env)
448 {
449 	struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev);
450 	int ret = 0, j;
451 	char *prop_buf;
452 	char *attrname;
453 
454 	dev_dbg(dev, "uevent\n");
455 
456 	if (!dual_role || !dual_role->desc) {
457 		dev_dbg(dev, "No dual_role phy yet\n");
458 		return ret;
459 	}
460 
461 	dev_dbg(dev, "DUAL_ROLE_NAME=%s\n", dual_role->desc->name);
462 
463 	ret = add_uevent_var(env, "DUAL_ROLE_NAME=%s", dual_role->desc->name);
464 	if (ret)
465 		return ret;
466 
467 	prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
468 	if (!prop_buf)
469 		return -ENOMEM;
470 
471 	for (j = 0; j < dual_role->desc->num_properties; j++) {
472 		struct device_attribute *attr;
473 		char *line;
474 
475 		attr = &dual_role_attrs[dual_role->desc->properties[j]];
476 
477 		ret = dual_role_show_property(dev, attr, prop_buf);
478 		if (ret == -ENODEV || ret == -ENODATA) {
479 			ret = 0;
480 			continue;
481 		}
482 
483 		if (ret < 0)
484 			goto out;
485 		line = strnchr(prop_buf, PAGE_SIZE, '\n');
486 		if (line)
487 			*line = 0;
488 
489 		attrname = kstrdupcase(attr->attr.name, GFP_KERNEL, true);
490 		if (!attrname)
491 			ret = -ENOMEM;
492 
493 		dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf);
494 
495 		ret = add_uevent_var(env, "DUAL_ROLE_%s=%s", attrname,
496 				     prop_buf);
497 		kfree(attrname);
498 		if (ret)
499 			goto out;
500 	}
501 
502 out:
503 	free_page((unsigned long)prop_buf);
504 
505 	return ret;
506 }
507 
508 /******************* Module Init ***********************************/
509 
dual_role_class_init(void)510 static int __init dual_role_class_init(void)
511 {
512 	dual_role_class = class_create(THIS_MODULE, "dual_role_usb");
513 
514 	if (IS_ERR(dual_role_class))
515 		return PTR_ERR(dual_role_class);
516 
517 	dual_role_class->dev_uevent = dual_role_uevent;
518 	dual_role_init_attrs(&dual_role_dev_type);
519 
520 	return 0;
521 }
522 
dual_role_class_exit(void)523 static void __exit dual_role_class_exit(void)
524 {
525 	class_destroy(dual_role_class);
526 }
527 
528 subsys_initcall(dual_role_class_init);
529 module_exit(dual_role_class_exit);
530