at v5.0 314 lines 7.4 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * USB Role Switch Support 4 * 5 * Copyright (C) 2018 Intel Corporation 6 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> 7 * Hans de Goede <hdegoede@redhat.com> 8 */ 9 10#include <linux/usb/role.h> 11#include <linux/device.h> 12#include <linux/module.h> 13#include <linux/mutex.h> 14#include <linux/slab.h> 15 16static struct class *role_class; 17 18struct usb_role_switch { 19 struct device dev; 20 struct mutex lock; /* device lock*/ 21 enum usb_role role; 22 23 /* From descriptor */ 24 struct device *usb2_port; 25 struct device *usb3_port; 26 struct device *udc; 27 usb_role_switch_set_t set; 28 usb_role_switch_get_t get; 29 bool allow_userspace_control; 30}; 31 32#define to_role_switch(d) container_of(d, struct usb_role_switch, dev) 33 34/** 35 * usb_role_switch_set_role - Set USB role for a switch 36 * @sw: USB role switch 37 * @role: USB role to be switched to 38 * 39 * Set USB role @role for @sw. 40 */ 41int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role) 42{ 43 int ret; 44 45 if (IS_ERR_OR_NULL(sw)) 46 return 0; 47 48 mutex_lock(&sw->lock); 49 50 ret = sw->set(sw->dev.parent, role); 51 if (!ret) 52 sw->role = role; 53 54 mutex_unlock(&sw->lock); 55 56 return ret; 57} 58EXPORT_SYMBOL_GPL(usb_role_switch_set_role); 59 60/** 61 * usb_role_switch_get_role - Get the USB role for a switch 62 * @sw: USB role switch 63 * 64 * Depending on the role-switch-driver this function returns either a cached 65 * value of the last set role, or reads back the actual value from the hardware. 66 */ 67enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw) 68{ 69 enum usb_role role; 70 71 if (IS_ERR_OR_NULL(sw)) 72 return USB_ROLE_NONE; 73 74 mutex_lock(&sw->lock); 75 76 if (sw->get) 77 role = sw->get(sw->dev.parent); 78 else 79 role = sw->role; 80 81 mutex_unlock(&sw->lock); 82 83 return role; 84} 85EXPORT_SYMBOL_GPL(usb_role_switch_get_role); 86 87static int __switch_match(struct device *dev, const void *name) 88{ 89 return !strcmp((const char *)name, dev_name(dev)); 90} 91 92static void *usb_role_switch_match(struct device_connection *con, int ep, 93 void *data) 94{ 95 struct device *dev; 96 97 dev = class_find_device(role_class, NULL, con->endpoint[ep], 98 __switch_match); 99 100 return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER); 101} 102 103/** 104 * usb_role_switch_get - Find USB role switch linked with the caller 105 * @dev: The caller device 106 * 107 * Finds and returns role switch linked with @dev. The reference count for the 108 * found switch is incremented. 109 */ 110struct usb_role_switch *usb_role_switch_get(struct device *dev) 111{ 112 struct usb_role_switch *sw; 113 114 sw = device_connection_find_match(dev, "usb-role-switch", NULL, 115 usb_role_switch_match); 116 117 if (!IS_ERR_OR_NULL(sw)) 118 WARN_ON(!try_module_get(sw->dev.parent->driver->owner)); 119 120 return sw; 121} 122EXPORT_SYMBOL_GPL(usb_role_switch_get); 123 124/** 125 * usb_role_switch_put - Release handle to a switch 126 * @sw: USB Role Switch 127 * 128 * Decrement reference count for @sw. 129 */ 130void usb_role_switch_put(struct usb_role_switch *sw) 131{ 132 if (!IS_ERR_OR_NULL(sw)) { 133 put_device(&sw->dev); 134 module_put(sw->dev.parent->driver->owner); 135 } 136} 137EXPORT_SYMBOL_GPL(usb_role_switch_put); 138 139static umode_t 140usb_role_switch_is_visible(struct kobject *kobj, struct attribute *attr, int n) 141{ 142 struct device *dev = container_of(kobj, typeof(*dev), kobj); 143 struct usb_role_switch *sw = to_role_switch(dev); 144 145 if (sw->allow_userspace_control) 146 return attr->mode; 147 148 return 0; 149} 150 151static const char * const usb_roles[] = { 152 [USB_ROLE_NONE] = "none", 153 [USB_ROLE_HOST] = "host", 154 [USB_ROLE_DEVICE] = "device", 155}; 156 157static ssize_t 158role_show(struct device *dev, struct device_attribute *attr, char *buf) 159{ 160 struct usb_role_switch *sw = to_role_switch(dev); 161 enum usb_role role = usb_role_switch_get_role(sw); 162 163 return sprintf(buf, "%s\n", usb_roles[role]); 164} 165 166static ssize_t role_store(struct device *dev, struct device_attribute *attr, 167 const char *buf, size_t size) 168{ 169 struct usb_role_switch *sw = to_role_switch(dev); 170 int ret; 171 172 ret = sysfs_match_string(usb_roles, buf); 173 if (ret < 0) { 174 bool res; 175 176 /* Extra check if the user wants to disable the switch */ 177 ret = kstrtobool(buf, &res); 178 if (ret || res) 179 return -EINVAL; 180 } 181 182 ret = usb_role_switch_set_role(sw, ret); 183 if (ret) 184 return ret; 185 186 return size; 187} 188static DEVICE_ATTR_RW(role); 189 190static struct attribute *usb_role_switch_attrs[] = { 191 &dev_attr_role.attr, 192 NULL, 193}; 194 195static const struct attribute_group usb_role_switch_group = { 196 .is_visible = usb_role_switch_is_visible, 197 .attrs = usb_role_switch_attrs, 198}; 199 200static const struct attribute_group *usb_role_switch_groups[] = { 201 &usb_role_switch_group, 202 NULL, 203}; 204 205static int 206usb_role_switch_uevent(struct device *dev, struct kobj_uevent_env *env) 207{ 208 int ret; 209 210 ret = add_uevent_var(env, "USB_ROLE_SWITCH=%s", dev_name(dev)); 211 if (ret) 212 dev_err(dev, "failed to add uevent USB_ROLE_SWITCH\n"); 213 214 return ret; 215} 216 217static void usb_role_switch_release(struct device *dev) 218{ 219 struct usb_role_switch *sw = to_role_switch(dev); 220 221 kfree(sw); 222} 223 224static const struct device_type usb_role_dev_type = { 225 .name = "usb_role_switch", 226 .groups = usb_role_switch_groups, 227 .uevent = usb_role_switch_uevent, 228 .release = usb_role_switch_release, 229}; 230 231/** 232 * usb_role_switch_register - Register USB Role Switch 233 * @parent: Parent device for the switch 234 * @desc: Description of the switch 235 * 236 * USB Role Switch is a device capable or choosing the role for USB connector. 237 * On platforms where the USB controller is dual-role capable, the controller 238 * driver will need to register the switch. On platforms where the USB host and 239 * USB device controllers behind the connector are separate, there will be a 240 * mux, and the driver for that mux will need to register the switch. 241 * 242 * Returns handle to a new role switch or ERR_PTR. The content of @desc is 243 * copied. 244 */ 245struct usb_role_switch * 246usb_role_switch_register(struct device *parent, 247 const struct usb_role_switch_desc *desc) 248{ 249 struct usb_role_switch *sw; 250 int ret; 251 252 if (!desc || !desc->set) 253 return ERR_PTR(-EINVAL); 254 255 sw = kzalloc(sizeof(*sw), GFP_KERNEL); 256 if (!sw) 257 return ERR_PTR(-ENOMEM); 258 259 mutex_init(&sw->lock); 260 261 sw->allow_userspace_control = desc->allow_userspace_control; 262 sw->usb2_port = desc->usb2_port; 263 sw->usb3_port = desc->usb3_port; 264 sw->udc = desc->udc; 265 sw->set = desc->set; 266 sw->get = desc->get; 267 268 sw->dev.parent = parent; 269 sw->dev.class = role_class; 270 sw->dev.type = &usb_role_dev_type; 271 dev_set_name(&sw->dev, "%s-role-switch", dev_name(parent)); 272 273 ret = device_register(&sw->dev); 274 if (ret) { 275 put_device(&sw->dev); 276 return ERR_PTR(ret); 277 } 278 279 /* TODO: Symlinks for the host port and the device controller. */ 280 281 return sw; 282} 283EXPORT_SYMBOL_GPL(usb_role_switch_register); 284 285/** 286 * usb_role_switch_unregister - Unregsiter USB Role Switch 287 * @sw: USB Role Switch 288 * 289 * Unregister switch that was registered with usb_role_switch_register(). 290 */ 291void usb_role_switch_unregister(struct usb_role_switch *sw) 292{ 293 if (!IS_ERR_OR_NULL(sw)) 294 device_unregister(&sw->dev); 295} 296EXPORT_SYMBOL_GPL(usb_role_switch_unregister); 297 298static int __init usb_roles_init(void) 299{ 300 role_class = class_create(THIS_MODULE, "usb_role"); 301 return PTR_ERR_OR_ZERO(role_class); 302} 303subsys_initcall(usb_roles_init); 304 305static void __exit usb_roles_exit(void) 306{ 307 class_destroy(role_class); 308} 309module_exit(usb_roles_exit); 310 311MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>"); 312MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 313MODULE_LICENSE("GPL v2"); 314MODULE_DESCRIPTION("USB Role Class");