blob: 51fcb545a9d578589da728c488643d6887f75008 [file] [log] [blame]
Badhri Jagan Sridharan7dfee9c2015-08-31 21:36:07 -07001/*
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
29static ssize_t dual_role_store_property(struct device *dev,
30 struct device_attribute *attr,
31 const char *buf, size_t count);
32static 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
43static 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
51struct class *dual_role_class;
52EXPORT_SYMBOL_GPL(dual_role_class);
53
54static struct device_type dual_role_dev_type;
55
56static 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
73static 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
83void 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}
Amit Pundir9a9b1682015-09-02 16:38:31 +053089EXPORT_SYMBOL_GPL(dual_role_instance_changed);
Badhri Jagan Sridharan7dfee9c2015-08-31 21:36:07 -070090
91int 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}
97EXPORT_SYMBOL_GPL(dual_role_get_property);
98
99int 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}
108EXPORT_SYMBOL_GPL(dual_role_set_property);
109
110int 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}
118EXPORT_SYMBOL_GPL(dual_role_property_is_writeable);
119
120static 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
128static struct dual_role_phy_instance *__must_check
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
169device_add_failed:
170 device_init_wakeup(dev, false);
171wakeup_init_failed:
172dev_set_name_failed:
173 put_device(dev);
174 kfree(dual_role);
175
176 return ERR_PTR(rc);
177}
178
179static 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
187static 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
194struct dual_role_phy_instance *__must_check
195devm_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}
213EXPORT_SYMBOL_GPL(devm_dual_role_instance_register);
214
215static 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
225void 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}
235EXPORT_SYMBOL_GPL(devm_dual_role_instance_unregister);
236
237void *dual_role_get_drvdata(struct dual_role_phy_instance *dual_role)
238{
239 return dual_role->drv_data;
240}
241EXPORT_SYMBOL_GPL(dual_role_get_drvdata);
242
243/***************** Device attribute functions **************************/
244
245/* port type */
246static char *supported_modes_text[] = {
247 "ufp dfp", "dfp", "ufp"
248};
249
250/* current mode */
251static char *mode_text[] = {
252 "ufp", "dfp", "none"
253};
254
255/* Power role */
256static char *pr_text[] = {
257 "source", "sink", "none"
258};
259
260/* Data role */
261static char *dr_text[] = {
262 "host", "device", "none"
263};
264
265/* Vconn supply */
266static char *vconn_supply_text[] = {
267 "n", "y"
268};
269
270static 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
338static 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
386setprop:
387 ret = dual_role->desc->set_property(dual_role, off, &value);
388
389error:
390 kfree(dup_buf);
391
392 if (ret < 0)
393 return ret;
394
395 return count;
396}
397
398static 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
425static struct attribute *__dual_role_attrs[ARRAY_SIZE(dual_role_attrs) + 1];
426
427static struct attribute_group dual_role_attr_group = {
428 .attrs = __dual_role_attrs,
429 .is_visible = dual_role_attr_is_visible,
430};
431
432static const struct attribute_group *dual_role_attr_groups[] = {
433 &dual_role_attr_group,
434 NULL,
435};
436
437void 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
447int 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
502out:
503 free_page((unsigned long)prop_buf);
504
505 return ret;
506}
507
508/******************* Module Init ***********************************/
509
510static 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
523static void __exit dual_role_class_exit(void)
524{
525 class_destroy(dual_role_class);
526}
527
528subsys_initcall(dual_role_class_init);
529module_exit(dual_role_class_exit);