Hi Heikki, On Wed, Nov 18, 2020 at 06:00:58PM +0300, Heikki Krogerus wrote: > USB Power Delivery Specification defines a set of product > types for partners and cables. The product type is defined > in the ID Header VDO, which is the first object in the > response to the Discover Identity command. > > This sysfs attribute file is only created for the partners > and cables if the product type is really known in the > driver. Some interfaces do not give access to the Discover > Identity response from the partner or cable, but they may > still supply the product type separately in some cases. > > When the product type of the partner or cable is detected, > uevent is also raised with PRODUCT_TYPE set to show the > actual product type (for example PRODUCT_TYPE=host). > > Signed-off-by: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> > --- > Documentation/ABI/testing/sysfs-class-typec | 55 ++++++++ > drivers/usb/typec/class.c | 132 ++++++++++++++++++-- > 2 files changed, 180 insertions(+), 7 deletions(-) > > diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec > index b7794e02ad205..4c09e327c62be 100644 > --- a/Documentation/ABI/testing/sysfs-class-typec > +++ b/Documentation/ABI/testing/sysfs-class-typec > @@ -139,6 +139,42 @@ Description: > Shows if the partner supports USB Power Delivery communication: > Valid values: yes, no > > +What: /sys/class/typec/<port>-partner/product_type > +Date: December 2020 > +Contact: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> > +Description: USB Power Delivery Specification defines a set of product types > + for the partner devices. This file will show the product type of > + the partner if it is known. Dual-role capable partners will have > + both UFP and DFP product types defined, but only one that > + matches the current role will be active at the time. If the > + product type of the partner is not visible to the device driver, > + this file will not exist. > + > + When the partner product type is detected, or changed with role > + swap, uvevent is also raised that contains PRODUCT_TYPE=<product > + type> (for example PRODUCT_TYPE=hub). > + > + Valid values: > + > + UFP / device role > + ======================== ========================== > + undefined - > + hub PDUSB Hub > + peripheral PDUSB Peripheral > + psd Power Bank > + ama Alternate Mode Adapter > + vpd VCONN Powered USB Device I have it on good authority that "vpd" is incorrectly categorized here, and for future proofing, we'd better not introduce vpd as a product type for UFP... A vpd is actually more closely related to a "cable" than it is a "UFP." A closer reading of the USB Type-C and USB PD specs will reveal that VPDs can only ever appear as SOP' and not as SOP, so having its type appear under UFP is a mistake. In other words, the USB PD V3.0 R2.0 spec is wrong. A change has been working its way through the spec committee to fix this, but it is not yet published. In order to reduce the amount of churn, I would recommend not including vpd as a possible type until a new version of the spec (or the ECN) is published. Thanks, Benson > + ======================== ========================== > + > + DFP / host role > + ======================== ========================== > + undefined - > + hub PDUSB Hub > + host PDUSB Host > + power_brick Power Brick > + amc Alternate Mode Controller > + ======================== ========================== > + > What: /sys/class/typec/<port>-partner>/identity/ > Date: April 2017 > Contact: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> > @@ -202,6 +238,25 @@ Description: > - type-c > - captive > > +What: /sys/class/typec/<port>-cable/product_type > +Date: December 2020 > +Contact: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> > +Description: USB Power Delivery Specification defines a set of product types > + for the cables. This file will show the product type of the > + cable if it is known. If the product type of the cable is not > + visible to the device driver, this file will not exist. > + > + When the cable product type is detected, uvevent is also raised > + with PRODUCT_TYPE showing the product type of the cable. > + > + Valid values: > + > + ======================== ========================== > + undefined - > + active Active Cable > + passive Passive Cable > + ======================== ========================== > + > What: /sys/class/typec/<port>-cable/identity/ > Date: April 2017 > Contact: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> > diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c > index 35eec707cb512..303f054181ff7 100644 > --- a/drivers/usb/typec/class.c > +++ b/drivers/usb/typec/class.c > @@ -11,6 +11,7 @@ > #include <linux/mutex.h> > #include <linux/property.h> > #include <linux/slab.h> > +#include <linux/usb/pd_vdo.h> > > #include "bus.h" > > @@ -81,6 +82,30 @@ static const char * const typec_accessory_modes[] = { > [TYPEC_ACCESSORY_DEBUG] = "debug", > }; > > +/* Product types defined in USB PD Specification R3.0 V2.0 */ > +static const char * const product_type_ufp[8] = { > + [IDH_PTYPE_UNDEF] = "undefined", > + [IDH_PTYPE_HUB] = "hub", > + [IDH_PTYPE_PERIPH] = "peripheral", > + [IDH_PTYPE_PSD] = "psd", > + [IDH_PTYPE_AMA] = "ama", > + [IDH_PTYPE_VPD] = "vpd", > +}; > + > +static const char * const product_type_dfp[8] = { > + [IDH_PTYPE_DFP_UNDEF] = "undefined", > + [IDH_PTYPE_DFP_HUB] = "hub", > + [IDH_PTYPE_DFP_HOST] = "host", > + [IDH_PTYPE_DFP_PB] = "power_brick", > + [IDH_PTYPE_DFP_AMC] = "amc", > +}; > + > +static const char * const product_type_cable[8] = { > + [IDH_PTYPE_UNDEF] = "undefined", > + [IDH_PTYPE_PCABLE] = "passive", > + [IDH_PTYPE_ACABLE] = "active", > +}; > + > static struct usb_pd_identity *get_pd_identity(struct device *dev) > { > if (is_typec_partner(dev)) { > @@ -95,6 +120,24 @@ static struct usb_pd_identity *get_pd_identity(struct device *dev) > return NULL; > } > > +static const char *get_pd_product_type(struct device *dev) > +{ > + struct typec_port *port = to_typec_port(dev->parent); > + struct usb_pd_identity *id = get_pd_identity(dev); > + const char *ptype = NULL; > + > + if (is_typec_partner(dev)) { > + if (port->data_role == TYPEC_HOST) > + ptype = product_type_ufp[PD_IDH_PTYPE(id->id_header)]; > + else > + ptype = product_type_dfp[PD_IDH_DFP_PTYPE(id->id_header)]; > + } else if (is_typec_cable(dev)) { > + ptype = product_type_cable[PD_IDH_PTYPE(id->id_header)]; > + } > + > + return ptype; > +} > + > static ssize_t id_header_show(struct device *dev, struct device_attribute *attr, > char *buf) > { > @@ -139,11 +182,55 @@ static const struct attribute_group *usb_pd_id_groups[] = { > NULL, > }; > > +static void typec_product_type_notify(struct device *dev) > +{ > + const char *ptype; > + char *envp[2]; > + > + ptype = get_pd_product_type(dev); > + if (!ptype) > + return; > + > + sysfs_notify(&dev->kobj, NULL, "product_type"); > + > + envp[0] = kasprintf(GFP_KERNEL, "PRODUCT_TYPE=%s", ptype); > + if (!envp[0]) > + return; > + > + envp[1] = NULL; > + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); > + kfree(envp[0]); > +} > + > static void typec_report_identity(struct device *dev) > { > sysfs_notify(&dev->kobj, "identity", "id_header"); > sysfs_notify(&dev->kobj, "identity", "cert_stat"); > sysfs_notify(&dev->kobj, "identity", "product"); > + typec_product_type_notify(dev); > +} > + > +static ssize_t > +product_type_show(struct device *dev, struct device_attribute *attr, char *buf) > +{ > + const char *ptype; > + > + ptype = get_pd_product_type(dev); > + if (!ptype) > + return 0; > + > + return sysfs_emit(buf, "%s\n", ptype); > +} > +static DEVICE_ATTR_RO(product_type); > + > +static umode_t typec_product_type_attr_is_visible(struct kobject *kobj, > + struct attribute *attr, int n) > +{ > + if (attr == &dev_attr_product_type.attr) > + if (!get_pd_identity(kobj_to_dev(kobj))) > + return 0; > + > + return attr->mode; > } > > /* ------------------------------------------------------------------------- */ > @@ -534,10 +621,20 @@ static DEVICE_ATTR_RO(supports_usb_power_delivery); > > static struct attribute *typec_partner_attrs[] = { > &dev_attr_accessory_mode.attr, > + &dev_attr_product_type.attr, > &dev_attr_supports_usb_power_delivery.attr, > NULL > }; > -ATTRIBUTE_GROUPS(typec_partner); > + > +static struct attribute_group typec_partner_group = { > + .is_visible = typec_product_type_attr_is_visible, > + .attrs = typec_partner_attrs, > +}; > + > +static const struct attribute_group *typec_partner_groups[] = { > + &typec_partner_group, > + NULL > +}; > > static void typec_partner_release(struct device *dev) > { > @@ -773,9 +870,19 @@ static DEVICE_ATTR_RO(plug_type); > static struct attribute *typec_cable_attrs[] = { > &dev_attr_type.attr, > &dev_attr_plug_type.attr, > + &dev_attr_product_type.attr, > + NULL > +}; > + > +static struct attribute_group typec_cable_group = { > + .is_visible = typec_product_type_attr_is_visible, > + .attrs = typec_cable_attrs, > +}; > + > +static const struct attribute_group *typec_cable_groups[] = { > + &typec_cable_group, > NULL > }; > -ATTRIBUTE_GROUPS(typec_cable); > > static void typec_cable_release(struct device *dev) > { > @@ -1352,6 +1459,11 @@ const struct device_type typec_port_dev_type = { > /* --------------------------------------- */ > /* Driver callbacks to report role updates */ > > +static int partner_match(struct device *dev, void *data) > +{ > + return is_typec_partner(dev); > +} > + > /** > * typec_set_data_role - Report data role change > * @port: The USB Type-C Port where the role was changed > @@ -1361,12 +1473,23 @@ const struct device_type typec_port_dev_type = { > */ > void typec_set_data_role(struct typec_port *port, enum typec_data_role role) > { > + struct device *partner_dev; > + > if (port->data_role == role) > return; > > port->data_role = role; > sysfs_notify(&port->dev.kobj, NULL, "data_role"); > kobject_uevent(&port->dev.kobj, KOBJ_CHANGE); > + > + partner_dev = device_find_child(&port->dev, NULL, partner_match); > + if (!partner_dev) > + return; > + > + if (to_typec_partner(partner_dev)->identity) > + typec_product_type_notify(partner_dev); > + > + put_device(partner_dev); > } > EXPORT_SYMBOL_GPL(typec_set_data_role); > > @@ -1407,11 +1530,6 @@ void typec_set_vconn_role(struct typec_port *port, enum typec_role role) > } > EXPORT_SYMBOL_GPL(typec_set_vconn_role); > > -static int partner_match(struct device *dev, void *data) > -{ > - return is_typec_partner(dev); > -} > - > /** > * typec_set_pwr_opmode - Report changed power operation mode > * @port: The USB Type-C Port where the mode was changed > -- > 2.29.2 > -- Benson Leung Staff Software Engineer Chrome OS Kernel Google Inc. bleung@xxxxxxxxxx Chromium OS Project bleung@xxxxxxxxxxxx
Attachment:
signature.asc
Description: PGP signature