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