From: Guenter Roeck <groeck@xxxxxxxxxxxx> New API functions (calls into class code) typec_set_usb_role() typec_set_pwr_role() typec_set_vconn_role() typec_set_pwr_opmode() Modified API functions (calls into class code): typec_register_port(dev, cap) -> typec_register_port(dev, cap, driver_data) Modified callback functions: dr_swap(port) -> dr_set(port, driver_data, role); pr_swap(port) -> pr_set(port, driver_data, role); vconn_swap(port) -> vconn_set(port, driver_data, role); fix_role(port) -> fix_role(port, driver_data, role); activate_mode(...) -> activate_mode(..., driver_data, ...); New sysfs attribute: current_vconn_role Other: - Extract role initialization to new function typec_init_roles() - Call driver code unconditionally on role changes - Add NULL check in typec_unregister_altmodes() - If an alternate mode description pointer is NULL, display an empty string. Signed-off-by: Guenter Roeck <groeck@xxxxxxxxxxxx> Signed-off-by: Guenter Roeck <linux@xxxxxxxxxxxx> --- This patch applies on top of '[RFC PATCHv2] usb: USB Type-C Connector Class' from Heikki Krogerus. It provided the changes I made to get the code operational. drivers/usb/type-c/typec.c | 134 ++++++++++++++++++++++++++++++++++++--------- include/linux/usb/typec.h | 26 ++++++--- 2 files changed, 125 insertions(+), 35 deletions(-) diff --git a/drivers/usb/type-c/typec.c b/drivers/usb/type-c/typec.c index 8028b7df0951..6836e972b681 100644 --- a/drivers/usb/type-c/typec.c +++ b/drivers/usb/type-c/typec.c @@ -27,6 +27,8 @@ struct typec_port { struct typec_partner *partner; struct typec_cable *cable; + void *driver_data; + unsigned int connected:1; int n_altmode; @@ -324,6 +326,20 @@ static void typec_remove_cable(struct typec_port *port) device_unregister(&port->cable->dev); } +static void typec_init_roles(struct typec_port *port) +{ + if (port->fixed_role == TYPEC_PORT_DFP) { + port->usb_role = TYPEC_HOST; + port->pwr_role = TYPEC_PWR_SOURCE; + port->vconn_role = TYPEC_PWR_SOURCE; + } else { + /* Device mode as default also with DRP ports */ + port->usb_role = TYPEC_DEVICE; + port->pwr_role = TYPEC_PWR_SINK; + port->vconn_role = TYPEC_PWR_SINK; + } +} + /* -------------------------------- */ int typec_connect(struct typec_port *port, struct typec_connection *con) @@ -378,16 +394,7 @@ void typec_disconnect(struct typec_port *port) port->pwr_opmode = TYPEC_PWR_MODE_USB; - if (port->fixed_role == TYPEC_PORT_DFP) { - port->usb_role = TYPEC_HOST; - port->pwr_role = TYPEC_PWR_SOURCE; - port->vconn_role = TYPEC_PWR_SOURCE; - } else { - /* Device mode as default also with DRP ports */ - port->usb_role = TYPEC_DEVICE; - port->pwr_role = TYPEC_PWR_SINK; - port->vconn_role = TYPEC_PWR_SINK; - } + typec_init_roles(port); kobject_uevent(&port->dev.kobj, KOBJ_CHANGE); } @@ -405,6 +412,34 @@ struct typec_port *typec_dev2port(struct device *dev) } EXPORT_SYMBOL_GPL(typec_dev2port); +/* --------------------------------------- */ +/* Driver callbacks to report role updates */ + +void typec_set_usb_role(struct typec_port *port, enum typec_usb_role role) +{ + port->usb_role = role; +} +EXPORT_SYMBOL(typec_set_usb_role); + +void typec_set_pwr_role(struct typec_port *port, enum typec_pwr_role role) +{ + port->pwr_role = role; +} +EXPORT_SYMBOL(typec_set_pwr_role); + +void typec_set_vconn_role(struct typec_port *port, enum typec_pwr_role role) +{ + port->vconn_role = role; +} +EXPORT_SYMBOL(typec_set_vconn_role); + +void typec_set_pwr_opmode(struct typec_port *port, + enum typec_pwr_opmode opmode) +{ + port->pwr_opmode = opmode; +} +EXPORT_SYMBOL(typec_set_pwr_opmode); + /* -------------------------------- */ /* Alternate Modes */ @@ -451,7 +486,7 @@ typec_altmode_desc_show(struct device *dev, struct device_attribute *attr, struct typec_mode *mode = container_of(attr, struct typec_mode, desc_attr); - return sprintf(buf, "%s\n", mode->desc); + return sprintf(buf, "%s\n", mode->desc ? mode->desc : ""); } static ssize_t @@ -561,6 +596,9 @@ void typec_unregister_altmodes(struct typec_altmode *alt_modes) { struct typec_altmode *alt; + if (!alt_modes) + return; + for (alt = alt_modes; alt->svid; alt++) device_unregister(&alt->dev); } @@ -581,7 +619,7 @@ current_usb_data_role_store(struct device *dev, struct device_attribute *attr, return -EOPNOTSUPP; } - if (!port->cap->dr_swap) { + if (!port->cap->dr_set) { dev_warn(dev, "data role swapping not supported\n"); return -EOPNOTSUPP; } @@ -593,10 +631,7 @@ current_usb_data_role_store(struct device *dev, struct device_attribute *attr, else return -EINVAL; - if (port->usb_role == role || !port->partner) - return size; - - ret = port->cap->dr_swap(port); + ret = port->cap->dr_set(port, port->driver_data, role); if (ret) return ret; @@ -655,10 +690,7 @@ current_data_role_store(struct device *dev, struct device_attribute *attr, else return -EINVAL; - if (port->fixed_role == role) - return size; - - ret = port->cap->fix_role(port, role); + ret = port->cap->fix_role(port, port->driver_data, role); if (ret) return ret; @@ -688,7 +720,7 @@ static ssize_t current_power_role_store(struct device *dev, return -EOPNOTSUPP; } - if (!port->cap->pr_swap) { + if (!port->cap->pr_set) { dev_warn(dev, "power role swapping not supported\n"); return -EOPNOTSUPP; } @@ -705,10 +737,7 @@ static ssize_t current_power_role_store(struct device *dev, else return -EINVAL; - if (port->pwr_role == role || !port->partner) - return size; - - ret = port->cap->pr_swap(port); + ret = port->cap->pr_set(port, port->driver_data, role); if (ret) return ret; @@ -762,6 +791,54 @@ static ssize_t power_operation_mode_show(struct device *dev, } static DEVICE_ATTR_RO(power_operation_mode); +static ssize_t current_vconn_role_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct typec_port *port = to_typec_port(dev); + enum typec_pwr_role role; + int ret; + + if (!port->cap->usb_pd) { + dev_dbg(dev, "vconn swap only supported with USB PD\n"); + return -EOPNOTSUPP; + } + + if (!port->cap->vconn_set) { + dev_warn(dev, "vconn swapping not supported\n"); + return -EOPNOTSUPP; + } + + if (!strncmp(buf, "source", 6)) + role = TYPEC_PWR_SOURCE; + else if (!strncmp(buf, "sink", 4)) + role = TYPEC_PWR_SINK; + else + return -EINVAL; + + ret = port->cap->vconn_set(port, port->driver_data, role); + if (ret) + return ret; + + return size; +} + +static ssize_t current_vconn_role_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct typec_port *port = to_typec_port(dev); + + switch (port->vconn_role) { + case TYPEC_PWR_SOURCE: + return sprintf(buf, "source\n"); + case TYPEC_PWR_SINK: + return sprintf(buf, "sink\n"); + default: + return sprintf(buf, "unknown\n"); + }; +} +static DEVICE_ATTR_RW(current_vconn_role); + static ssize_t supports_audio_accessory_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -795,6 +872,7 @@ static DEVICE_ATTR_RO(supports_usb_power_delivery); static struct attribute *typec_attrs[] = { &dev_attr_current_data_role.attr, &dev_attr_current_power_role.attr, + &dev_attr_current_vconn_role.attr, &dev_attr_current_usb_data_role.attr, &dev_attr_power_operation_mode.attr, &dev_attr_supported_data_roles.attr, @@ -862,7 +940,8 @@ static struct device_type typec_port_dev_type = { }; struct typec_port *typec_register_port(struct device *dev, - struct typec_capability *cap) + struct typec_capability *cap, + void *driver_data) { struct typec_port *port; int ret; @@ -880,6 +959,7 @@ struct typec_port *typec_register_port(struct device *dev, port->id = id; port->cap = cap; + port->driver_data = driver_data; port->dev.type = &typec_port_dev_type; port->dev.class = &typec_class; port->dev.parent = dev; @@ -888,6 +968,8 @@ struct typec_port *typec_register_port(struct device *dev, port->fixed_role = port->cap->role; + typec_init_roles(port); + ret = device_register(&port->dev); if (ret) { ida_simple_remove(&typec_index_ida, id); diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 86e5c867800b..d16a38de57ac 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -168,9 +168,9 @@ struct typec_partner { * @audio_accessory: Audio Accessory Adapter Mode support * @debug_accessory: Debug Accessory Mode support * @fix_role: Set a fixed data role for DRP port - * @dr_swap: Data Role Swap support - * @pr_swap: Power Role Swap support - * @vconn_swap: VCONN Swap support + * @dr_set: Set Data Role + * @pr_set: Set Power Role + * @vconn_set: Set VCONN Role * @activate_mode: Enter/exit given Alternate Mode * * Static capabilities of a single USB Type-C port. @@ -182,14 +182,14 @@ struct typec_capability { unsigned int audio_accessory:1; unsigned int debug_accessory:1; - int (*fix_role)(struct typec_port *, + int (*fix_role)(struct typec_port *, void *, enum typec_data_role); - int (*dr_swap)(struct typec_port *); - int (*pr_swap)(struct typec_port *); - int (*vconn_swap)(struct typec_port *); + int (*dr_set)(struct typec_port *, void *, enum typec_usb_role); + int (*pr_set)(struct typec_port *, void *, enum typec_pwr_role); + int (*vconn_set)(struct typec_port *, void *, enum typec_pwr_role); - int (*activate_mode)(struct typec_altmode *, + int (*activate_mode)(struct typec_altmode *, void *, int mode, int activate); }; @@ -217,7 +217,8 @@ struct typec_connection { }; struct typec_port *typec_register_port(struct device *dev, - struct typec_capability *cap); + struct typec_capability *cap, + void *driver_data); void typec_unregister_port(struct typec_port *port); int typec_connect(struct typec_port *port, struct typec_connection *con); @@ -227,4 +228,11 @@ void typec_disconnect(struct typec_port *port); struct device *typec_port2dev(struct typec_port *port); struct typec_port *typec_dev2port(struct device *dev); +/* Callbacks from driver */ + +void typec_set_usb_role(struct typec_port *, enum typec_usb_role); +void typec_set_pwr_role(struct typec_port *, enum typec_pwr_role); +void typec_set_vconn_role(struct typec_port *, enum typec_pwr_role); +void typec_set_pwr_opmode(struct typec_port *, enum typec_pwr_opmode); + #endif /* __LINUX_USB_TYPEC_H */ -- 2.5.0 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html