From: Guenter Roeck <gro...@chromium.org>

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 <gro...@chromium.org>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
---
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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to