during Power Delivery discovery. This file remains hidden until a value
greater than or equal to 0 is set by Type C port driver.
+What: /sys/class/typec/<port>-partner/type
+Date: December 2020
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+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
+ ====================== ==========================
+
+ 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@linux.intel.com>
+Description:
+ This directory appears only if the port device driver is capable
+ of showing the result of Discover Identity USB power delivery
+ command. That will not always be possible even when USB power
+ delivery is supported, for example when USB power delivery
+ communication for the port is mostly handled in firmware. If the
+ directory exists, it will have an attribute file for every VDO
+ in Discover Identity command result.
USB Type-C cable devices (eg. /sys/class/typec/port0-cable/)
What: /sys/class/typec/<port>-cable/type
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
-Description:
- Shows if the cable is active.
- Valid values: active, passive
+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/plug_type
Date: April 2017
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/slab.h>
+#include <linux/usb/pd_vdo.h>
#include "bus.h"
[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",
+};
+
+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)) {
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 (!id)
+ return NULL;
+
+ 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)) {
+ if (id)
+ ptype = product_type_cable[PD_IDH_PTYPE(id->id_header)];
+ else
+ ptype = to_typec_cable(dev)->active ?
+ product_type_cable[IDH_PTYPE_ACABLE] :
+ product_type_cable[IDH_PTYPE_PCABLE];
+ }
+
+ return ptype;
+}
+
static ssize_t id_header_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
NULL,
};
+static void typec_product_type_notify(struct device *dev)
+{
+ char *envp[2] = { };
+ const char *ptype;
+
+ ptype = get_pd_product_type(dev);
+ if (!ptype)
+ return;
+
+ sysfs_notify(&dev->kobj, NULL, "type");
+
+ envp[0] = kasprintf(GFP_KERNEL, "PRODUCT_TYPE=%s", ptype);
+ if (!envp[0])
+ return;
+
+ 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", "product_type_vdo1");
sysfs_notify(&dev->kobj, "identity", "product_type_vdo2");
sysfs_notify(&dev->kobj, "identity", "product_type_vdo3");
+ typec_product_type_notify(dev);
+}
+
+static ssize_t
+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(type);
/* ------------------------------------------------------------------------- */
/* Alternate Modes */
&dev_attr_accessory_mode.attr,
&dev_attr_supports_usb_power_delivery.attr,
&dev_attr_number_of_alternate_modes.attr,
+ &dev_attr_type.attr,
NULL
};
return 0;
}
+ if (attr == &dev_attr_type.attr)
+ if (!get_pd_product_type(kobj_to_dev(kobj)))
+ return 0;
+
return attr->mode;
}
/* Type-C Cables */
-static ssize_t
-type_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct typec_cable *cable = to_typec_cable(dev);
-
- return sprintf(buf, "%s\n", cable->active ? "active" : "passive");
-}
-static DEVICE_ATTR_RO(type);
-
static const char * const typec_plug_types[] = {
[USB_PLUG_NONE] = "unknown",
[USB_PLUG_TYPE_A] = "type-a",
/* --------------------------------------- */
/* 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
*/
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);
}
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