Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: common: Small class for USB role switches

USB role switch is a device that can be used to choose the
data role for USB connector. With dual-role capable USB
controllers, the controller itself will be the switch, but
on some platforms the USB host and device controllers are
separate IPs and there is a mux between them and the
connector. On those platforms the mux driver will need to
register the switch.

With USB Type-C connectors, the host-to-device relationship
is negotiated over the Configuration Channel (CC). That
means the USB Type-C drivers need to be in control of the
role switch. The class provides a simple API for the USB
Type-C drivers for the control.

For other types of USB connectors (mainly microAB) the class
provides user space control via sysfs attribute file that
can be used to request role swapping from the switch.

Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Heikki Krogerus and committed by
Greg Kroah-Hartman
fde0aa6c bdecb33a

+383
+21
Documentation/ABI/testing/sysfs-class-usb_role
··· 1 + What: /sys/class/usb_role/ 2 + Date: Jan 2018 3 + Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> 4 + Description: 5 + Place in sysfs for USB Role Switches. USB Role Switch is a 6 + device that can select the data role (host or device) for USB 7 + port. 8 + 9 + What: /sys/class/usb_role/<switch>/role 10 + Date: Jan 2018 11 + Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> 12 + Description: 13 + The current role of the switch. This attribute can be used for 14 + requesting role swapping with non-USB Type-C ports. With USB 15 + Type-C ports, the ABI defined for USB Type-C connector class 16 + must be used. 17 + 18 + Valid values: 19 + - none 20 + - host 21 + - device
+3
drivers/usb/Kconfig
··· 203 203 To compile this driver as a module, choose M here: the module will 204 204 be called ulpi. 205 205 206 + config USB_ROLE_SWITCH 207 + tristate 208 + 206 209 endif # USB_SUPPORT
+1
drivers/usb/common/Makefile
··· 9 9 10 10 obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o 11 11 obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o 12 + obj-$(CONFIG_USB_ROLE_SWITCH) += roles.o
+305
drivers/usb/common/roles.c
··· 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 + 16 + static struct class *role_class; 17 + 18 + struct 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 + */ 41 + int 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 + } 58 + EXPORT_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 + */ 67 + enum 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 + } 85 + EXPORT_SYMBOL_GPL(usb_role_switch_get_role); 86 + 87 + static int __switch_match(struct device *dev, const void *name) 88 + { 89 + return !strcmp((const char *)name, dev_name(dev)); 90 + } 91 + 92 + static 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 + */ 110 + struct usb_role_switch *usb_role_switch_get(struct device *dev) 111 + { 112 + return device_connection_find_match(dev, "usb-role-switch", NULL, 113 + usb_role_switch_match); 114 + } 115 + EXPORT_SYMBOL_GPL(usb_role_switch_get); 116 + 117 + /** 118 + * usb_role_switch_put - Release handle to a switch 119 + * @sw: USB Role Switch 120 + * 121 + * Decrement reference count for @sw. 122 + */ 123 + void usb_role_switch_put(struct usb_role_switch *sw) 124 + { 125 + if (!IS_ERR_OR_NULL(sw)) 126 + put_device(&sw->dev); 127 + } 128 + EXPORT_SYMBOL_GPL(usb_role_switch_put); 129 + 130 + static umode_t 131 + usb_role_switch_is_visible(struct kobject *kobj, struct attribute *attr, int n) 132 + { 133 + struct device *dev = container_of(kobj, typeof(*dev), kobj); 134 + struct usb_role_switch *sw = to_role_switch(dev); 135 + 136 + if (sw->allow_userspace_control) 137 + return attr->mode; 138 + 139 + return 0; 140 + } 141 + 142 + static const char * const usb_roles[] = { 143 + [USB_ROLE_NONE] = "none", 144 + [USB_ROLE_HOST] = "host", 145 + [USB_ROLE_DEVICE] = "device", 146 + }; 147 + 148 + static ssize_t 149 + role_show(struct device *dev, struct device_attribute *attr, char *buf) 150 + { 151 + struct usb_role_switch *sw = to_role_switch(dev); 152 + enum usb_role role = usb_role_switch_get_role(sw); 153 + 154 + return sprintf(buf, "%s\n", usb_roles[role]); 155 + } 156 + 157 + static ssize_t role_store(struct device *dev, struct device_attribute *attr, 158 + const char *buf, size_t size) 159 + { 160 + struct usb_role_switch *sw = to_role_switch(dev); 161 + int ret; 162 + 163 + ret = sysfs_match_string(usb_roles, buf); 164 + if (ret < 0) { 165 + bool res; 166 + 167 + /* Extra check if the user wants to disable the switch */ 168 + ret = kstrtobool(buf, &res); 169 + if (ret || res) 170 + return -EINVAL; 171 + } 172 + 173 + ret = usb_role_switch_set_role(sw, ret); 174 + if (ret) 175 + return ret; 176 + 177 + return size; 178 + } 179 + static DEVICE_ATTR_RW(role); 180 + 181 + static struct attribute *usb_role_switch_attrs[] = { 182 + &dev_attr_role.attr, 183 + NULL, 184 + }; 185 + 186 + static const struct attribute_group usb_role_switch_group = { 187 + .is_visible = usb_role_switch_is_visible, 188 + .attrs = usb_role_switch_attrs, 189 + }; 190 + 191 + static const struct attribute_group *usb_role_switch_groups[] = { 192 + &usb_role_switch_group, 193 + NULL, 194 + }; 195 + 196 + static int 197 + usb_role_switch_uevent(struct device *dev, struct kobj_uevent_env *env) 198 + { 199 + int ret; 200 + 201 + ret = add_uevent_var(env, "USB_ROLE_SWITCH=%s", dev_name(dev)); 202 + if (ret) 203 + dev_err(dev, "failed to add uevent USB_ROLE_SWITCH\n"); 204 + 205 + return ret; 206 + } 207 + 208 + static void usb_role_switch_release(struct device *dev) 209 + { 210 + struct usb_role_switch *sw = to_role_switch(dev); 211 + 212 + kfree(sw); 213 + } 214 + 215 + static const struct device_type usb_role_dev_type = { 216 + .name = "usb_role_switch", 217 + .groups = usb_role_switch_groups, 218 + .uevent = usb_role_switch_uevent, 219 + .release = usb_role_switch_release, 220 + }; 221 + 222 + /** 223 + * usb_role_switch_register - Register USB Role Switch 224 + * @parent: Parent device for the switch 225 + * @desc: Description of the switch 226 + * 227 + * USB Role Switch is a device capable or choosing the role for USB connector. 228 + * On platforms where the USB controller is dual-role capable, the controller 229 + * driver will need to register the switch. On platforms where the USB host and 230 + * USB device controllers behind the connector are separate, there will be a 231 + * mux, and the driver for that mux will need to register the switch. 232 + * 233 + * Returns handle to a new role switch or ERR_PTR. The content of @desc is 234 + * copied. 235 + */ 236 + struct usb_role_switch * 237 + usb_role_switch_register(struct device *parent, 238 + const struct usb_role_switch_desc *desc) 239 + { 240 + struct usb_role_switch *sw; 241 + int ret; 242 + 243 + if (!desc || !desc->set) 244 + return ERR_PTR(-EINVAL); 245 + 246 + sw = kzalloc(sizeof(*sw), GFP_KERNEL); 247 + if (!sw) 248 + return ERR_PTR(-ENOMEM); 249 + 250 + mutex_init(&sw->lock); 251 + 252 + sw->allow_userspace_control = desc->allow_userspace_control; 253 + sw->usb2_port = desc->usb2_port; 254 + sw->usb3_port = desc->usb3_port; 255 + sw->udc = desc->udc; 256 + sw->set = desc->set; 257 + sw->get = desc->get; 258 + 259 + sw->dev.parent = parent; 260 + sw->dev.class = role_class; 261 + sw->dev.type = &usb_role_dev_type; 262 + dev_set_name(&sw->dev, "%s-role-switch", dev_name(parent)); 263 + 264 + ret = device_register(&sw->dev); 265 + if (ret) { 266 + put_device(&sw->dev); 267 + return ERR_PTR(ret); 268 + } 269 + 270 + /* TODO: Symlinks for the host port and the device controller. */ 271 + 272 + return sw; 273 + } 274 + EXPORT_SYMBOL_GPL(usb_role_switch_register); 275 + 276 + /** 277 + * usb_role_switch_unregister - Unregsiter USB Role Switch 278 + * @sw: USB Role Switch 279 + * 280 + * Unregister switch that was registered with usb_role_switch_register(). 281 + */ 282 + void usb_role_switch_unregister(struct usb_role_switch *sw) 283 + { 284 + if (!IS_ERR_OR_NULL(sw)) 285 + device_unregister(&sw->dev); 286 + } 287 + EXPORT_SYMBOL_GPL(usb_role_switch_unregister); 288 + 289 + static int __init usb_roles_init(void) 290 + { 291 + role_class = class_create(THIS_MODULE, "usb_role"); 292 + return PTR_ERR_OR_ZERO(role_class); 293 + } 294 + subsys_initcall(usb_roles_init); 295 + 296 + static void __exit usb_roles_exit(void) 297 + { 298 + class_destroy(role_class); 299 + } 300 + module_exit(usb_roles_exit); 301 + 302 + MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>"); 303 + MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 304 + MODULE_LICENSE("GPL v2"); 305 + MODULE_DESCRIPTION("USB Role Class");
+53
include/linux/usb/role.h
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #ifndef __LINUX_USB_ROLE_H 4 + #define __LINUX_USB_ROLE_H 5 + 6 + #include <linux/device.h> 7 + 8 + struct usb_role_switch; 9 + 10 + enum usb_role { 11 + USB_ROLE_NONE, 12 + USB_ROLE_HOST, 13 + USB_ROLE_DEVICE, 14 + }; 15 + 16 + typedef int (*usb_role_switch_set_t)(struct device *dev, enum usb_role role); 17 + typedef enum usb_role (*usb_role_switch_get_t)(struct device *dev); 18 + 19 + /** 20 + * struct usb_role_switch_desc - USB Role Switch Descriptor 21 + * @usb2_port: Optional reference to the host controller port device (USB2) 22 + * @usb3_port: Optional reference to the host controller port device (USB3) 23 + * @udc: Optional reference to the peripheral controller device 24 + * @set: Callback for setting the role 25 + * @get: Callback for getting the role (optional) 26 + * @allow_userspace_control: If true userspace may change the role through sysfs 27 + * 28 + * @usb2_port and @usb3_port will point to the USB host port and @udc to the USB 29 + * device controller behind the USB connector with the role switch. If 30 + * @usb2_port, @usb3_port and @udc are included in the description, the 31 + * reference count for them should be incremented by the caller of 32 + * usb_role_switch_register() before registering the switch. 33 + */ 34 + struct usb_role_switch_desc { 35 + struct device *usb2_port; 36 + struct device *usb3_port; 37 + struct device *udc; 38 + usb_role_switch_set_t set; 39 + usb_role_switch_get_t get; 40 + bool allow_userspace_control; 41 + }; 42 + 43 + int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role); 44 + enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw); 45 + struct usb_role_switch *usb_role_switch_get(struct device *dev); 46 + void usb_role_switch_put(struct usb_role_switch *sw); 47 + 48 + struct usb_role_switch * 49 + usb_role_switch_register(struct device *parent, 50 + const struct usb_role_switch_desc *desc); 51 + void usb_role_switch_unregister(struct usb_role_switch *sw); 52 + 53 + #endif /* __LINUX_USB_ROLE_H */