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>
This commit is contained in:
Badhri Jagan Sridharan 2017-05-30 12:39:53 -07:00 committed by Greg Kroah-Hartman
parent 7ee4ce6e93
commit bab3548078
3 changed files with 115 additions and 10 deletions

View file

@ -30,6 +30,21 @@ Description:
Valid values: source, sink
What: /sys/class/typec/<port>/port_type
Date: May 2017
Contact: Badhri Jagan Sridharan <Badhri@google.com>
Description:
Indicates the type of the port. This attribute can be used for
requesting a change in the port type. Port type change is
supported as a synchronous operation, so write(2) to the
attribute will not return until the operation has finished.
Valid values:
- source (The port will behave as source only DFP port)
- sink (The port will behave as sink only UFP port)
- dual (The port will behave as dual-role-data and
dual-role-power port)
What: /sys/class/typec/<port>/vconn_source
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>

View file

@ -11,6 +11,7 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/usb/typec.h>
@ -69,6 +70,8 @@ struct typec_port {
enum typec_role pwr_role;
enum typec_role vconn_role;
enum typec_pwr_opmode pwr_opmode;
enum typec_port_type port_type;
struct mutex port_type_lock;
const struct typec_capability *cap;
};
@ -790,6 +793,18 @@ static const char * const typec_data_roles[] = {
[TYPEC_HOST] = "host",
};
static const char * const typec_port_types[] = {
[TYPEC_PORT_DFP] = "source",
[TYPEC_PORT_UFP] = "sink",
[TYPEC_PORT_DRP] = "dual",
};
static const char * const typec_port_types_drp[] = {
[TYPEC_PORT_DFP] = "dual [source] sink",
[TYPEC_PORT_UFP] = "dual source [sink]",
[TYPEC_PORT_DRP] = "[dual] source sink",
};
static ssize_t
preferred_role_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
@ -847,11 +862,6 @@ static ssize_t data_role_store(struct device *dev,
struct typec_port *port = to_typec_port(dev);
int ret;
if (port->cap->type != TYPEC_PORT_DRP) {
dev_dbg(dev, "data role swap only supported with DRP ports\n");
return -EOPNOTSUPP;
}
if (!port->cap->dr_set) {
dev_dbg(dev, "data role swapping not supported\n");
return -EOPNOTSUPP;
@ -861,11 +871,22 @@ static ssize_t data_role_store(struct device *dev,
if (ret < 0)
return ret;
mutex_lock(&port->port_type_lock);
if (port->port_type != TYPEC_PORT_DRP) {
dev_dbg(dev, "port type fixed at \"%s\"",
typec_port_types[port->port_type]);
ret = -EOPNOTSUPP;
goto unlock_and_ret;
}
ret = port->cap->dr_set(port->cap, ret);
if (ret)
return ret;
goto unlock_and_ret;
return size;
ret = size;
unlock_and_ret:
mutex_unlock(&port->port_type_lock);
return ret;
}
static ssize_t data_role_show(struct device *dev,
@ -886,7 +907,7 @@ static ssize_t power_role_store(struct device *dev,
const char *buf, size_t size)
{
struct typec_port *port = to_typec_port(dev);
int ret = size;
int ret;
if (!port->cap->pd_revision) {
dev_dbg(dev, "USB Power Delivery not supported\n");
@ -907,11 +928,22 @@ static ssize_t power_role_store(struct device *dev,
if (ret < 0)
return ret;
mutex_lock(&port->port_type_lock);
if (port->port_type != TYPEC_PORT_DRP) {
dev_dbg(dev, "port type fixed at \"%s\"",
typec_port_types[port->port_type]);
ret = -EOPNOTSUPP;
goto unlock_and_ret;
}
ret = port->cap->pr_set(port->cap, ret);
if (ret)
return ret;
goto unlock_and_ret;
return size;
ret = size;
unlock_and_ret:
mutex_unlock(&port->port_type_lock);
return ret;
}
static ssize_t power_role_show(struct device *dev,
@ -927,6 +959,57 @@ static ssize_t power_role_show(struct device *dev,
}
static DEVICE_ATTR_RW(power_role);
static ssize_t
port_type_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
struct typec_port *port = to_typec_port(dev);
int ret;
enum typec_port_type type;
if (!port->cap->port_type_set || port->cap->type != TYPEC_PORT_DRP) {
dev_dbg(dev, "changing port type not supported\n");
return -EOPNOTSUPP;
}
ret = sysfs_match_string(typec_port_types, buf);
if (ret < 0)
return ret;
type = ret;
mutex_lock(&port->port_type_lock);
if (port->port_type == type) {
ret = size;
goto unlock_and_ret;
}
ret = port->cap->port_type_set(port->cap, type);
if (ret)
goto unlock_and_ret;
port->port_type = type;
ret = size;
unlock_and_ret:
mutex_unlock(&port->port_type_lock);
return ret;
}
static ssize_t
port_type_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct typec_port *port = to_typec_port(dev);
if (port->cap->type == TYPEC_PORT_DRP)
return sprintf(buf, "%s\n",
typec_port_types_drp[port->port_type]);
return sprintf(buf, "[%s]\n", typec_port_types[port->cap->type]);
}
static DEVICE_ATTR_RW(port_type);
static const char * const typec_pwr_opmodes[] = {
[TYPEC_PWR_MODE_USB] = "default",
[TYPEC_PWR_MODE_1_5A] = "1.5A",
@ -1036,6 +1119,7 @@ static struct attribute *typec_attrs[] = {
&dev_attr_usb_power_delivery_revision.attr,
&dev_attr_usb_typec_revision.attr,
&dev_attr_vconn_source.attr,
&dev_attr_port_type.attr,
NULL,
};
ATTRIBUTE_GROUPS(typec);
@ -1231,6 +1315,8 @@ struct typec_port *typec_register_port(struct device *parent,
port->id = id;
port->cap = cap;
port->port_type = cap->type;
mutex_init(&port->port_type_lock);
port->prefer_role = cap->prefer_role;
port->dev.class = typec_class;

View file

@ -190,6 +190,7 @@ struct typec_partner_desc {
* @pr_set: Set Power Role
* @vconn_set: Set VCONN Role
* @activate_mode: Enter/exit given Alternate Mode
* @port_type_set: Set port type
*
* Static capabilities of a single USB Type-C port.
*/
@ -214,6 +215,9 @@ struct typec_capability {
int (*activate_mode)(const struct typec_capability *,
int mode, int activate);
int (*port_type_set)(const struct typec_capability *,
enum typec_port_type);
};
/* Specific to try_role(). Indicates the user want's to clear the preference. */