From: Guenter Roeck <linux@roeck-us.net>
To: Badhri Jagan Sridharan <badhri@google.com>
Cc: Heikki Krogerus <heikki.krogerus@linux.intel.com>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Oliver Neukum <oneukum@suse.com>,
linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCHv3] usb: typec: Add a sysfs node to manage port type
Date: Fri, 26 May 2017 11:00:05 -0700 [thread overview]
Message-ID: <20170526180005.GA29160@roeck-us.net> (raw)
In-Reply-To: <20170526174559.30057-1-Badhri@google.com>
On Fri, May 26, 2017 at 10:45:59AM -0700, Badhri Jagan Sridharan wrote:
> 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>
> Change-Id: Ia76877558c47271a34d912a54eea3459655dda3c
> ---
> Changelog since v1:
> - introduced a new variable port_type in struct typec_port to track
> the current port type instead of changing type member in
> typec_capability to address Heikki Krogerus comments.
> - changed the output format and strings that would be displayed as
> suggested by Heikki Krogerus.
>
> Changelog since v2:
> - introduced a new mutex lock to protect port_type for addressing
> the race conditions identified by Geunter Roeck
> - added typec_port_types_drp for printing port_type when cap->type
> is TYPE_PORT_DRP as suggested by Geunter Roeck
> - Power role swap and data role swaps would be rejected unless
> port port_type == TYPE_PORT_DRP
> - port_type_store would return immediately if the current port_type
> is same as the new port_type as suggested by Geunter Roeck
>
> drivers/usb/typec/typec.c | 127 ++++++++++++++++++++++++++++++++++++++++------
> include/linux/usb/typec.h | 4 ++
> 2 files changed, 116 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/usb/typec/typec.c b/drivers/usb/typec/typec.c
> index 89e540bb7ff3..a0d8887ad560 100644
> --- a/drivers/usb/typec/typec.c
> +++ b/drivers/usb/typec/typec.c
> @@ -11,9 +11,11 @@
>
> #include <linux/device.h>
> #include <linux/module.h>
> +#include <linux/mutex.h>
> #include <linux/slab.h>
> #include <linux/usb/typec.h>
>
> +
Single empty lines only, please.
> struct typec_mode {
> int index;
> u32 vdo;
> @@ -69,6 +71,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;
> };
> @@ -789,6 +793,19 @@ 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",
> +};
> +
> +
Single empty line.
> static ssize_t
> preferred_role_store(struct device *dev, struct device_attribute *attr,
> const char *buf, size_t size)
> @@ -846,25 +863,34 @@ 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;
> - }
> + mutex_lock(&port->port_type_lock);
>
> if (!port->cap->dr_set) {
> dev_dbg(dev, "data role swapping not supported\n");
> - return -EOPNOTSUPP;
> + ret = -EOPNOTSUPP;
> + goto unlock_and_ret;
> + }
I think you can check this outside the lock; capabilities won't change.
> +
> + 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 = sysfs_match_string(typec_data_roles, buf);
> if (ret < 0)
> - return ret;
> + goto unlock_and_ret;
Move outside the lock ?
>
> 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,
> @@ -885,32 +911,47 @@ 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;
>
> + mutex_lock(&port->port_type_lock);
> if (!port->cap->pd_revision) {
> dev_dbg(dev, "USB Power Delivery not supported\n");
> - return -EOPNOTSUPP;
> + ret = -EOPNOTSUPP;
> + goto unlock_and_ret;
> }
>
> if (!port->cap->pr_set) {
> dev_dbg(dev, "power role swapping not supported\n");
> - return -EOPNOTSUPP;
> + ret = -EOPNOTSUPP;
> + goto unlock_and_ret;
> + }
> +
Capabilities can be checked outside the 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;
> }
>
> if (port->pwr_opmode != TYPEC_PWR_MODE_PD) {
> dev_dbg(dev, "partner unable to swap power role\n");
> - return -EIO;
> + ret = -EIO;
> + goto unlock_and_ret;
> }
>
> ret = sysfs_match_string(typec_roles, buf);
> if (ret < 0)
> - return ret;
> + 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,
> @@ -926,6 +967,59 @@ 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;
> +
> + mutex_lock(&port->port_type_lock);
> +
> + if (!port->cap->port_type_set || port->cap->type != TYPEC_PORT_DRP) {
> + dev_dbg(dev, "changing port type not supported\n");
> + ret = -EOPNOTSUPP;
> + goto unlock_and_ret;
> + }
> +
> + ret = sysfs_match_string(typec_port_types, buf);
> + if (ret < 0)
> + goto unlock_and_ret;
> +
I would suggest to check as much as possible outside the lock.
> + type = ret;
> +
> + 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 size;
> +}
> +
> +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",
> @@ -1035,6 +1129,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);
> @@ -1211,6 +1306,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;
> diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
> index ec78204964ab..b174248af7c9 100644
> --- a/include/linux/usb/typec.h
> +++ b/include/linux/usb/typec.h
> @@ -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. */
> --
> 2.13.0.219.gdb65acc882-goog
>
next prev parent reply other threads:[~2017-05-26 18:00 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-05-26 17:45 [PATCHv3] usb: typec: Add a sysfs node to manage port type Badhri Jagan Sridharan
2017-05-26 18:00 ` Guenter Roeck [this message]
2017-05-26 20:37 ` Badhri Jagan Sridharan
2017-05-26 18:31 ` Greg Kroah-Hartman
2017-05-26 20:36 ` Badhri Jagan Sridharan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20170526180005.GA29160@roeck-us.net \
--to=linux@roeck-us.net \
--cc=badhri@google.com \
--cc=gregkh@linuxfoundation.org \
--cc=heikki.krogerus@linux.intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=oneukum@suse.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.