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

usb: typec: Add a sysfs node to manage port type

User space applications in some cases have the need to enforce a
specific port type(DFP/UFP/DRP). This change allows userspace to
attempt setting the desired port type. Low level drivers can
however reject the request if the specific port type is not supported.

Signed-off-by: Badhri Jagan Sridharan <Badhri@google.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Badhri Jagan Sridharan and committed by
Greg Kroah-Hartman
bab35480 7ee4ce6e

+115 -10
+15
Documentation/ABI/testing/sysfs-class-typec
··· 30 30 31 31 Valid values: source, sink 32 32 33 + What: /sys/class/typec/<port>/port_type 34 + Date: May 2017 35 + Contact: Badhri Jagan Sridharan <Badhri@google.com> 36 + Description: 37 + Indicates the type of the port. This attribute can be used for 38 + requesting a change in the port type. Port type change is 39 + supported as a synchronous operation, so write(2) to the 40 + attribute will not return until the operation has finished. 41 + 42 + Valid values: 43 + - source (The port will behave as source only DFP port) 44 + - sink (The port will behave as sink only UFP port) 45 + - dual (The port will behave as dual-role-data and 46 + dual-role-power port) 47 + 33 48 What: /sys/class/typec/<port>/vconn_source 34 49 Date: April 2017 35 50 Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+96 -10
drivers/usb/typec/typec.c
··· 11 11 12 12 #include <linux/device.h> 13 13 #include <linux/module.h> 14 + #include <linux/mutex.h> 14 15 #include <linux/slab.h> 15 16 #include <linux/usb/typec.h> 16 17 ··· 70 69 enum typec_role pwr_role; 71 70 enum typec_role vconn_role; 72 71 enum typec_pwr_opmode pwr_opmode; 72 + enum typec_port_type port_type; 73 + struct mutex port_type_lock; 73 74 74 75 const struct typec_capability *cap; 75 76 }; ··· 793 790 [TYPEC_HOST] = "host", 794 791 }; 795 792 793 + static const char * const typec_port_types[] = { 794 + [TYPEC_PORT_DFP] = "source", 795 + [TYPEC_PORT_UFP] = "sink", 796 + [TYPEC_PORT_DRP] = "dual", 797 + }; 798 + 799 + static const char * const typec_port_types_drp[] = { 800 + [TYPEC_PORT_DFP] = "dual [source] sink", 801 + [TYPEC_PORT_UFP] = "dual source [sink]", 802 + [TYPEC_PORT_DRP] = "[dual] source sink", 803 + }; 804 + 796 805 static ssize_t 797 806 preferred_role_store(struct device *dev, struct device_attribute *attr, 798 807 const char *buf, size_t size) ··· 862 847 struct typec_port *port = to_typec_port(dev); 863 848 int ret; 864 849 865 - if (port->cap->type != TYPEC_PORT_DRP) { 866 - dev_dbg(dev, "data role swap only supported with DRP ports\n"); 867 - return -EOPNOTSUPP; 868 - } 869 - 870 850 if (!port->cap->dr_set) { 871 851 dev_dbg(dev, "data role swapping not supported\n"); 872 852 return -EOPNOTSUPP; ··· 871 861 if (ret < 0) 872 862 return ret; 873 863 864 + mutex_lock(&port->port_type_lock); 865 + if (port->port_type != TYPEC_PORT_DRP) { 866 + dev_dbg(dev, "port type fixed at \"%s\"", 867 + typec_port_types[port->port_type]); 868 + ret = -EOPNOTSUPP; 869 + goto unlock_and_ret; 870 + } 871 + 874 872 ret = port->cap->dr_set(port->cap, ret); 875 873 if (ret) 876 - return ret; 874 + goto unlock_and_ret; 877 875 878 - return size; 876 + ret = size; 877 + unlock_and_ret: 878 + mutex_unlock(&port->port_type_lock); 879 + return ret; 879 880 } 880 881 881 882 static ssize_t data_role_show(struct device *dev, ··· 907 886 const char *buf, size_t size) 908 887 { 909 888 struct typec_port *port = to_typec_port(dev); 910 - int ret = size; 889 + int ret; 911 890 912 891 if (!port->cap->pd_revision) { 913 892 dev_dbg(dev, "USB Power Delivery not supported\n"); ··· 928 907 if (ret < 0) 929 908 return ret; 930 909 910 + mutex_lock(&port->port_type_lock); 911 + if (port->port_type != TYPEC_PORT_DRP) { 912 + dev_dbg(dev, "port type fixed at \"%s\"", 913 + typec_port_types[port->port_type]); 914 + ret = -EOPNOTSUPP; 915 + goto unlock_and_ret; 916 + } 917 + 931 918 ret = port->cap->pr_set(port->cap, ret); 932 919 if (ret) 933 - return ret; 920 + goto unlock_and_ret; 934 921 935 - return size; 922 + ret = size; 923 + unlock_and_ret: 924 + mutex_unlock(&port->port_type_lock); 925 + return ret; 936 926 } 937 927 938 928 static ssize_t power_role_show(struct device *dev, ··· 958 926 return sprintf(buf, "[%s]\n", typec_roles[port->pwr_role]); 959 927 } 960 928 static DEVICE_ATTR_RW(power_role); 929 + 930 + static ssize_t 931 + port_type_store(struct device *dev, struct device_attribute *attr, 932 + const char *buf, size_t size) 933 + { 934 + struct typec_port *port = to_typec_port(dev); 935 + int ret; 936 + enum typec_port_type type; 937 + 938 + if (!port->cap->port_type_set || port->cap->type != TYPEC_PORT_DRP) { 939 + dev_dbg(dev, "changing port type not supported\n"); 940 + return -EOPNOTSUPP; 941 + } 942 + 943 + ret = sysfs_match_string(typec_port_types, buf); 944 + if (ret < 0) 945 + return ret; 946 + 947 + type = ret; 948 + mutex_lock(&port->port_type_lock); 949 + 950 + if (port->port_type == type) { 951 + ret = size; 952 + goto unlock_and_ret; 953 + } 954 + 955 + ret = port->cap->port_type_set(port->cap, type); 956 + if (ret) 957 + goto unlock_and_ret; 958 + 959 + port->port_type = type; 960 + ret = size; 961 + 962 + unlock_and_ret: 963 + mutex_unlock(&port->port_type_lock); 964 + return ret; 965 + } 966 + 967 + static ssize_t 968 + port_type_show(struct device *dev, struct device_attribute *attr, 969 + char *buf) 970 + { 971 + struct typec_port *port = to_typec_port(dev); 972 + 973 + if (port->cap->type == TYPEC_PORT_DRP) 974 + return sprintf(buf, "%s\n", 975 + typec_port_types_drp[port->port_type]); 976 + 977 + return sprintf(buf, "[%s]\n", typec_port_types[port->cap->type]); 978 + } 979 + static DEVICE_ATTR_RW(port_type); 961 980 962 981 static const char * const typec_pwr_opmodes[] = { 963 982 [TYPEC_PWR_MODE_USB] = "default", ··· 1119 1036 &dev_attr_usb_power_delivery_revision.attr, 1120 1037 &dev_attr_usb_typec_revision.attr, 1121 1038 &dev_attr_vconn_source.attr, 1039 + &dev_attr_port_type.attr, 1122 1040 NULL, 1123 1041 }; 1124 1042 ATTRIBUTE_GROUPS(typec); ··· 1315 1231 1316 1232 port->id = id; 1317 1233 port->cap = cap; 1234 + port->port_type = cap->type; 1235 + mutex_init(&port->port_type_lock); 1318 1236 port->prefer_role = cap->prefer_role; 1319 1237 1320 1238 port->dev.class = typec_class;
+4
include/linux/usb/typec.h
··· 190 190 * @pr_set: Set Power Role 191 191 * @vconn_set: Set VCONN Role 192 192 * @activate_mode: Enter/exit given Alternate Mode 193 + * @port_type_set: Set port type 193 194 * 194 195 * Static capabilities of a single USB Type-C port. 195 196 */ ··· 215 214 216 215 int (*activate_mode)(const struct typec_capability *, 217 216 int mode, int activate); 217 + int (*port_type_set)(const struct typec_capability *, 218 + enum typec_port_type); 219 + 218 220 }; 219 221 220 222 /* Specific to try_role(). Indicates the user want's to clear the preference. */