[PATCH] usb: typec: hd3ss3220: Add sysfs attribute for USB role state
From: Venkata Swamy Kassa
Date: Fri Feb 27 2026 - 13:23:30 EST
The HD3SS3220 driver correctly detects USB Type-C cable attach/detach
events and propagates the role to the USB controller. However, there is
no way for userspace to query the current role state (device/host/none).
This becomes problematic when using udev rules to trigger scripts on
role changes. The driver generates kobject change events, but the event
itself doesn't contain the role information. Userspace needs to read
the current state to determine the appropriate action.
Add a 'usb_role' sysfs attribute that exposes the current USB role as
a string ("device", "host", or "none"). Also ensure sysfs_notify() and
kobject_uevent() are called when the role changes, enabling userspace
applications to poll() on the sysfs file or receive udev events.
This is useful for systems that need to:
- Start/stop USB gadget functions based on cable connection
- Switch between host and device modes dynamically
- Monitor USB Type-C port state from userspace
Signed-off-by: Venkata Swamy Kassa <venkata.swamy.kassa@xxxxxxxxxxx>
---
drivers/usb/typec/hd3ss3220.c | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c
index 14a25e4cb034..92c118609e20 100644
--- a/drivers/usb/typec/hd3ss3220.c
+++ b/drivers/usb/typec/hd3ss3220.c
@@ -56,6 +56,28 @@ struct hd3ss3220 {
bool poll;
};
+/*
+ * Sysfs attribute to show current USB role (device/host/none)
+ */
+static ssize_t usb_role_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hd3ss3220 *hd3ss3220 = dev_get_drvdata(dev);
+ const char *role_str = usb_role_string(hd3ss3220->role_state);
+
+ return sysfs_emit(buf, "%s\n", role_str);
+}
+static DEVICE_ATTR_RO(usb_role);
+
+static struct attribute *hd3ss3220_attrs[] = {
+ &dev_attr_usb_role.attr,
+ NULL
+};
+
+static const struct attribute_group hd3ss3220_attr_group = {
+ .attrs = hd3ss3220_attrs,
+};
+
static int hd3ss3220_set_power_opmode(struct hd3ss3220 *hd3ss3220, enum typec_pwr_opmode power_opmode)
{
int current_mode;
@@ -172,6 +194,10 @@ static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)
}
hd3ss3220->role_state = role_state;
+
+ /* Notify userspace of usb_role change */
+ sysfs_notify(&hd3ss3220->dev->kobj, NULL, "usb_role");
+ kobject_uevent(&hd3ss3220->dev->kobj, KOBJ_CHANGE);
}
static void output_poll_execute(struct work_struct *work)
@@ -310,6 +336,12 @@ static int hd3ss3220_probe(struct i2c_client *client)
if (ret < 0)
goto err_unreg_port;
+ ret = devm_device_add_group(&client->dev, &hd3ss3220_attr_group);
+ if (ret) {
+ dev_err(&client->dev, "Failed to create sysfs attributes: %d\n", ret);
+ goto err_unreg_port;
+ }
+
hd3ss3220_set_role(hd3ss3220);
ret = regmap_read(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL, &data);
if (ret < 0)
--
2.34.1