* [PATCH v1 0/2] usb: typec: ucsi: sysfs mailbox for commands @ 2025-02-06 14:19 Heikki Krogerus 2025-02-06 14:19 ` [PATCH v1 1/2] usb: typec: ucsi: Command mailbox interface for the userspace Heikki Krogerus 2025-02-06 14:19 ` [PATCH v1 2/2] tools: usb: UCSI command testing tool Heikki Krogerus 0 siblings, 2 replies; 8+ messages in thread From: Heikki Krogerus @ 2025-02-06 14:19 UTC (permalink / raw) To: Łukasz Bartosik Cc: Abhishek Pandit-Subedi, Benson Leung, Pavan Holla, Dmitry Baryshkov, Christian A. Ehrhardt, Jameson Thies, Katiyar, Pooja, Pathak, Asutosh, Jayaraman, Venkat, Greg Kroah-Hartman, linux-usb, linux-kernel Hi, UCSI has commands that can be used to configure the platform policy manager (PPM, which is the EC in most cases) on top of individual connectors. That kind of commands are very UCSI specific, and because of that, don't fit very well into any of our existing device classes that are all designed to represent the connectors in generic fashion. Nevertheless, the user space needs some way to configure also the entire PPM with these commands. Exposing the UCSI data structure as a mailbox file to the user space felt to me as the simplest way to do that, so that's why this proposal. This mailbox is of course not limited to those commands only - any UCSI command can be send to the PPM with it. Łukasz, would this cover also your debugging and testing needs that you were planning the netlink for (although, for the ChromeOS UCSI driver only)? Br, Heikki Krogerus (2): usb: typec: ucsi: Command mailbox interface for the userspace tools: usb: UCSI command testing tool Documentation/ABI/testing/sysfs-driver-ucsi | 20 ++ drivers/usb/typec/ucsi/Makefile | 2 +- drivers/usb/typec/ucsi/sysfs.c | 127 ++++++++++ drivers/usb/typec/ucsi/ucsi.c | 31 ++- drivers/usb/typec/ucsi/ucsi.h | 7 + tools/usb/.gitignore | 1 + tools/usb/Build | 1 + tools/usb/Makefile | 8 +- tools/usb/ucsi.c | 250 ++++++++++++++++++++ 9 files changed, 432 insertions(+), 15 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-driver-ucsi create mode 100644 drivers/usb/typec/ucsi/sysfs.c create mode 100644 tools/usb/ucsi.c -- 2.47.2 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v1 1/2] usb: typec: ucsi: Command mailbox interface for the userspace 2025-02-06 14:19 [PATCH v1 0/2] usb: typec: ucsi: sysfs mailbox for commands Heikki Krogerus @ 2025-02-06 14:19 ` Heikki Krogerus 2025-02-06 14:51 ` Greg Kroah-Hartman 2025-02-06 14:19 ` [PATCH v1 2/2] tools: usb: UCSI command testing tool Heikki Krogerus 1 sibling, 1 reply; 8+ messages in thread From: Heikki Krogerus @ 2025-02-06 14:19 UTC (permalink / raw) To: Łukasz Bartosik Cc: Abhishek Pandit-Subedi, Benson Leung, Pavan Holla, Dmitry Baryshkov, Christian A. Ehrhardt, Jameson Thies, Katiyar, Pooja, Pathak, Asutosh, Jayaraman, Venkat, Greg Kroah-Hartman, linux-usb, linux-kernel Some of the UCSI commands can be used to configure the entire Platform Policy Manager (PPM) instead of just individual connectors. To allow the user space communicate those commands with the PPM, adding a mailbox interface. The interface is a single attribute file that represents the main "OPM to PPM" UCSI data structure. The mailbox allows any UCSI command to be sent to the PPM so it should be also useful for validation, testing and debugging purposes. Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> --- Documentation/ABI/testing/sysfs-driver-ucsi | 20 +++ drivers/usb/typec/ucsi/Makefile | 2 +- drivers/usb/typec/ucsi/sysfs.c | 127 ++++++++++++++++++++ drivers/usb/typec/ucsi/ucsi.c | 31 +++-- drivers/usb/typec/ucsi/ucsi.h | 7 ++ 5 files changed, 173 insertions(+), 14 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-driver-ucsi create mode 100644 drivers/usb/typec/ucsi/sysfs.c diff --git a/Documentation/ABI/testing/sysfs-driver-ucsi b/Documentation/ABI/testing/sysfs-driver-ucsi new file mode 100644 index 000000000000..9da15577f4ae --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-ucsi @@ -0,0 +1,20 @@ +What: /sys/class/typec/<port>/device/ucsi +Date: February 2025 +Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> +Description: + Command mailbox for UCSI (USB Type-C System Software Interface). + + The mailbox contains a copy of the main UCSI data structure. + Sending a command happens by writing the command specific data + structure to the CONTROL offset just like defined in the UCSI + specification. When a command is written to the mailbox, it is + automatically forwarded to the Platform Policy Manager (PPM) of + the UCSI instance. + + After writing the command to the mailbox, the result can be read + directly from the CCI and MESSAGE_IN offsets. The mailbox takes + care of command completion acknowledges automatically. + + Note. The mailbox is meant, and can only be used for, sending + commands. I.e. the mailbox is not updated when the UCSI receives + asynchronous events. diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile index be98a879104d..5ebc74e3055b 100644 --- a/drivers/usb/typec/ucsi/Makefile +++ b/drivers/usb/typec/ucsi/Makefile @@ -3,7 +3,7 @@ CFLAGS_trace.o := -I$(src) obj-$(CONFIG_TYPEC_UCSI) += typec_ucsi.o -typec_ucsi-y := ucsi.o +typec_ucsi-y := ucsi.o sysfs.o typec_ucsi-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/usb/typec/ucsi/sysfs.c b/drivers/usb/typec/ucsi/sysfs.c new file mode 100644 index 000000000000..06ea1b54aefa --- /dev/null +++ b/drivers/usb/typec/ucsi/sysfs.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * UCSI sysfs mailbox. + * + * Copyright (C) 2025, Intel Corporation + */ + +#include <linux/mutex.h> +#include <linux/overflow.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/unaligned.h> +#include "ucsi.h" + +#define UCSI_MAILBOX_SIZE(ucsi) ((ucsi)->version < UCSI_VERSION_2_0 ? 48 : 528) + +struct ucsi_sysfs { + struct bin_attribute bin_attr; + struct ucsi *ucsi; + struct mutex lock; /* mailbox lock */ + u8 mailbox[]; +}; + +static ssize_t ucsi_read(struct file *filp, struct kobject *kobj, + const struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct ucsi_sysfs *sysfs = attr->private; + + mutex_lock(&sysfs->lock); + memcpy(buf, sysfs->mailbox + off, count); + mutex_unlock(&sysfs->lock); + + return count; +} + +static ssize_t ucsi_write(struct file *filp, struct kobject *kobj, + const struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct ucsi_sysfs *sysfs = attr->private; + struct ucsi *ucsi = sysfs->ucsi; + int ret; + + u64 *control = (u64 *)&sysfs->mailbox[UCSI_CONTROL]; + u32 *cci = (u32 *)&sysfs->mailbox[UCSI_CCI]; + void *data = &sysfs->mailbox[UCSI_MESSAGE_IN]; + + /* TODO: MESSAGE_OUT. */ + if (off != UCSI_CONTROL || count != sizeof(*control)) + return -EFAULT; + + mutex_lock(&sysfs->lock); + + memset(data, 0, UCSI_MAX_DATA_LENGTH(ucsi)); + + /* PPM_RESET has to be handled separately. */ + *control = get_unaligned_le64(buf); + if (UCSI_COMMAND(*control) == UCSI_PPM_RESET) { + ret = ucsi_reset_ppm(ucsi, cci); + goto out_unlock_sysfs; + } + + mutex_lock(&ucsi->ppm_lock); + + ret = ucsi->ops->sync_control(ucsi, *control, cci, NULL, 0); + if (ret) + goto out_unlock_ppm; + + if (UCSI_CCI_LENGTH(*cci) && ucsi->ops->read_message_in(ucsi, data, UCSI_CCI_LENGTH(*cci))) + dev_err(ucsi->dev, "failed to read MESSAGE_IN\n"); + + ret = ucsi->ops->sync_control(ucsi, UCSI_ACK_CC_CI | UCSI_ACK_COMMAND_COMPLETE, + NULL, NULL, 0); +out_unlock_ppm: + mutex_unlock(&ucsi->ppm_lock); +out_unlock_sysfs: + mutex_unlock(&sysfs->lock); + + return ret ?: count; +} + +int ucsi_sysfs_register(struct ucsi *ucsi) +{ + struct ucsi_sysfs *sysfs; + int ret; + + sysfs = kzalloc(struct_size(sysfs, mailbox, UCSI_MAILBOX_SIZE(ucsi)), GFP_KERNEL); + if (!sysfs) + return -ENOMEM; + + sysfs->ucsi = ucsi; + mutex_init(&sysfs->lock); + memcpy(sysfs->mailbox, &ucsi->version, sizeof(ucsi->version)); + + sysfs_bin_attr_init(&sysfs->bin_attr); + + sysfs->bin_attr.attr.name = "ucsi"; + sysfs->bin_attr.attr.mode = 0644; + + sysfs->bin_attr.size = UCSI_MAILBOX_SIZE(ucsi); + sysfs->bin_attr.private = sysfs; + sysfs->bin_attr.read_new = ucsi_read; + sysfs->bin_attr.write_new = ucsi_write; + + ret = sysfs_create_bin_file(&ucsi->dev->kobj, &sysfs->bin_attr); + if (ret) + kfree(sysfs); + else + ucsi->sysfs = sysfs; + + return ret; +} + +void ucsi_sysfs_unregister(struct ucsi *ucsi) +{ + struct ucsi_sysfs *sysfs = ucsi->sysfs; + + if (!sysfs) + return; + + sysfs_remove_bin_file(&ucsi->dev->kobj, &sysfs->bin_attr); + ucsi->sysfs = NULL; + kfree(sysfs); +} diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 559390a07a4e..9dadfe879319 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1340,16 +1340,18 @@ static int ucsi_reset_connector(struct ucsi_connector *con, bool hard) return ucsi_send_command(con->ucsi, command, NULL, 0); } -static int ucsi_reset_ppm(struct ucsi *ucsi) +int ucsi_reset_ppm(struct ucsi *ucsi, u32 *cci) { u64 command; unsigned long tmo; - u32 cci; + u32 _cci; int ret; + cci = cci ?: &_cci; + mutex_lock(&ucsi->ppm_lock); - ret = ucsi->ops->read_cci(ucsi, &cci); + ret = ucsi->ops->read_cci(ucsi, cci); if (ret < 0) goto out; @@ -1359,7 +1361,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi) * UCSI_SET_NOTIFICATION_ENABLE command to achieve this. * Ignore a timeout and try the reset anyway if this fails. */ - if (cci & UCSI_CCI_RESET_COMPLETE) { + if (*cci & UCSI_CCI_RESET_COMPLETE) { command = UCSI_SET_NOTIFICATION_ENABLE; ret = ucsi->ops->async_control(ucsi, command); if (ret < 0) @@ -1367,17 +1369,17 @@ static int ucsi_reset_ppm(struct ucsi *ucsi) tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS); do { - ret = ucsi->ops->read_cci(ucsi, &cci); + ret = ucsi->ops->read_cci(ucsi, cci); if (ret < 0) goto out; - if (cci & UCSI_CCI_COMMAND_COMPLETE) + if (*cci & UCSI_CCI_COMMAND_COMPLETE) break; if (time_is_before_jiffies(tmo)) break; msleep(20); } while (1); - WARN_ON(cci & UCSI_CCI_RESET_COMPLETE); + WARN_ON(*cci & UCSI_CCI_RESET_COMPLETE); } command = UCSI_PPM_RESET; @@ -1396,18 +1398,18 @@ static int ucsi_reset_ppm(struct ucsi *ucsi) /* Give the PPM time to process a reset before reading CCI */ msleep(20); - ret = ucsi->ops->read_cci(ucsi, &cci); + ret = ucsi->ops->read_cci(ucsi, cci); if (ret) goto out; /* If the PPM is still doing something else, reset it again. */ - if (cci & ~UCSI_CCI_RESET_COMPLETE) { + if (*cci & ~UCSI_CCI_RESET_COMPLETE) { ret = ucsi->ops->async_control(ucsi, command); if (ret < 0) goto out; } - } while (!(cci & UCSI_CCI_RESET_COMPLETE)); + } while (!(*cci & UCSI_CCI_RESET_COMPLETE)); out: mutex_unlock(&ucsi->ppm_lock); @@ -1423,7 +1425,7 @@ static int ucsi_role_cmd(struct ucsi_connector *con, u64 command) u64 c; /* PPM most likely stopped responding. Resetting everything. */ - ucsi_reset_ppm(con->ucsi); + ucsi_reset_ppm(con->ucsi, NULL); c = UCSI_SET_NOTIFICATION_ENABLE | con->ucsi->ntfy; ucsi_send_command(con->ucsi, c, NULL, 0); @@ -1766,7 +1768,7 @@ static int ucsi_init(struct ucsi *ucsi) int i; /* Reset the PPM */ - ret = ucsi_reset_ppm(ucsi); + ret = ucsi_reset_ppm(ucsi, NULL); if (ret) { dev_err(ucsi->dev, "failed to reset PPM!\n"); goto err; @@ -1846,7 +1848,7 @@ static int ucsi_init(struct ucsi *ucsi) kfree(connector); err_reset: memset(&ucsi->cap, 0, sizeof(ucsi->cap)); - ucsi_reset_ppm(ucsi); + ucsi_reset_ppm(ucsi, NULL); err: return ret; } @@ -1958,6 +1960,7 @@ EXPORT_SYMBOL_GPL(ucsi_create); void ucsi_destroy(struct ucsi *ucsi) { ucsi_debugfs_unregister(ucsi); + ucsi_sysfs_unregister(ucsi); kfree(ucsi); } EXPORT_SYMBOL_GPL(ucsi_destroy); @@ -1989,6 +1992,8 @@ int ucsi_register(struct ucsi *ucsi) queue_delayed_work(system_long_wq, &ucsi->work, 0); ucsi_debugfs_register(ucsi); + ucsi_sysfs_register(ucsi); + return 0; } EXPORT_SYMBOL_GPL(ucsi_register); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index feb012db4c89..d5b90e57cf42 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -468,6 +468,8 @@ struct ucsi { unsigned long quirks; #define UCSI_NO_PARTNER_PDOS BIT(0) /* Don't read partner's PDOs */ #define UCSI_DELAY_DEVICE_PDOS BIT(1) /* Reading PDOs fails until the parter is in PD mode */ + + void *sysfs; }; #define UCSI_MAX_DATA_LENGTH(u) (((u)->version < UCSI_VERSION_2_0) ? 0x10 : 0xff) @@ -535,6 +537,8 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci); int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci, void *data, size_t size); +int ucsi_reset_ppm(struct ucsi *ucsi, u32 *cci); + #if IS_ENABLED(CONFIG_POWER_SUPPLY) int ucsi_register_port_psy(struct ucsi_connector *con); void ucsi_unregister_port_psy(struct ucsi_connector *con); @@ -578,6 +582,9 @@ static inline void ucsi_debugfs_register(struct ucsi *ucsi) { } static inline void ucsi_debugfs_unregister(struct ucsi *ucsi) { } #endif /* CONFIG_DEBUG_FS */ +int ucsi_sysfs_register(struct ucsi *ucsi); +void ucsi_sysfs_unregister(struct ucsi *ucsi); + /* * NVIDIA VirtualLink (svid 0x955) has two altmode. VirtualLink * DP mode with vdo=0x1 and NVIDIA test mode with vdo=0x3 -- 2.47.2 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v1 1/2] usb: typec: ucsi: Command mailbox interface for the userspace 2025-02-06 14:19 ` [PATCH v1 1/2] usb: typec: ucsi: Command mailbox interface for the userspace Heikki Krogerus @ 2025-02-06 14:51 ` Greg Kroah-Hartman 2025-02-07 13:04 ` Heikki Krogerus 0 siblings, 1 reply; 8+ messages in thread From: Greg Kroah-Hartman @ 2025-02-06 14:51 UTC (permalink / raw) To: Heikki Krogerus Cc: Łukasz Bartosik, Abhishek Pandit-Subedi, Benson Leung, Pavan Holla, Dmitry Baryshkov, Christian A. Ehrhardt, Jameson Thies, Katiyar, Pooja, Pathak, Asutosh, Jayaraman, Venkat, linux-usb, linux-kernel On Thu, Feb 06, 2025 at 04:19:31PM +0200, Heikki Krogerus wrote: > Some of the UCSI commands can be used to configure the > entire Platform Policy Manager (PPM) instead of just > individual connectors. To allow the user space communicate > those commands with the PPM, adding a mailbox interface. The > interface is a single attribute file that represents the > main "OPM to PPM" UCSI data structure. > > The mailbox allows any UCSI command to be sent to the PPM so > it should be also useful for validation, testing and > debugging purposes. As it's for this type of thing, why not put it in debugfs instead? > +static ssize_t ucsi_write(struct file *filp, struct kobject *kobj, > + const struct bin_attribute *attr, > + char *buf, loff_t off, size_t count) > +{ > + struct ucsi_sysfs *sysfs = attr->private; > + struct ucsi *ucsi = sysfs->ucsi; > + int ret; > + > + u64 *control = (u64 *)&sysfs->mailbox[UCSI_CONTROL]; > + u32 *cci = (u32 *)&sysfs->mailbox[UCSI_CCI]; > + void *data = &sysfs->mailbox[UCSI_MESSAGE_IN]; > + > + /* TODO: MESSAGE_OUT. */ > + if (off != UCSI_CONTROL || count != sizeof(*control)) > + return -EFAULT; > + > + mutex_lock(&sysfs->lock); > + > + memset(data, 0, UCSI_MAX_DATA_LENGTH(ucsi)); > + > + /* PPM_RESET has to be handled separately. */ > + *control = get_unaligned_le64(buf); > + if (UCSI_COMMAND(*control) == UCSI_PPM_RESET) { > + ret = ucsi_reset_ppm(ucsi, cci); > + goto out_unlock_sysfs; > + } > + > + mutex_lock(&ucsi->ppm_lock); > + > + ret = ucsi->ops->sync_control(ucsi, *control, cci, NULL, 0); > + if (ret) > + goto out_unlock_ppm; > + > + if (UCSI_CCI_LENGTH(*cci) && ucsi->ops->read_message_in(ucsi, data, UCSI_CCI_LENGTH(*cci))) > + dev_err(ucsi->dev, "failed to read MESSAGE_IN\n"); > + > + ret = ucsi->ops->sync_control(ucsi, UCSI_ACK_CC_CI | UCSI_ACK_COMMAND_COMPLETE, > + NULL, NULL, 0); > +out_unlock_ppm: > + mutex_unlock(&ucsi->ppm_lock); > +out_unlock_sysfs: > + mutex_unlock(&sysfs->lock); > + > + return ret ?: count; > +} This worries me, any userspace tool can now do this? What other "bad" things can it to the connection? > + > +int ucsi_sysfs_register(struct ucsi *ucsi) > +{ > + struct ucsi_sysfs *sysfs; > + int ret; > + > + sysfs = kzalloc(struct_size(sysfs, mailbox, UCSI_MAILBOX_SIZE(ucsi)), GFP_KERNEL); > + if (!sysfs) > + return -ENOMEM; > + > + sysfs->ucsi = ucsi; > + mutex_init(&sysfs->lock); > + memcpy(sysfs->mailbox, &ucsi->version, sizeof(ucsi->version)); > + > + sysfs_bin_attr_init(&sysfs->bin_attr); > + > + sysfs->bin_attr.attr.name = "ucsi"; > + sysfs->bin_attr.attr.mode = 0644; > + > + sysfs->bin_attr.size = UCSI_MAILBOX_SIZE(ucsi); > + sysfs->bin_attr.private = sysfs; > + sysfs->bin_attr.read_new = ucsi_read; > + sysfs->bin_attr.write_new = ucsi_write; > + > + ret = sysfs_create_bin_file(&ucsi->dev->kobj, &sysfs->bin_attr); You raced with userspace and lost, right? Why are you dynamically creating this attribute, can't you just use a static one? But again, why not debugfs? I'd feel a lot more comfortable with that instead of sysfs. thanks, greg k-h ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v1 1/2] usb: typec: ucsi: Command mailbox interface for the userspace 2025-02-06 14:51 ` Greg Kroah-Hartman @ 2025-02-07 13:04 ` Heikki Krogerus 2025-02-07 20:15 ` Dmitry Baryshkov 0 siblings, 1 reply; 8+ messages in thread From: Heikki Krogerus @ 2025-02-07 13:04 UTC (permalink / raw) To: Greg Kroah-Hartman Cc: Łukasz Bartosik, Abhishek Pandit-Subedi, Benson Leung, Pavan Holla, Dmitry Baryshkov, Christian A. Ehrhardt, Jameson Thies, Katiyar, Pooja, Pathak, Asutosh, Jayaraman, Venkat, linux-usb, linux-kernel On Thu, Feb 06, 2025 at 03:51:48PM +0100, Greg Kroah-Hartman wrote: > On Thu, Feb 06, 2025 at 04:19:31PM +0200, Heikki Krogerus wrote: > > Some of the UCSI commands can be used to configure the > > entire Platform Policy Manager (PPM) instead of just > > individual connectors. To allow the user space communicate > > those commands with the PPM, adding a mailbox interface. The > > interface is a single attribute file that represents the > > main "OPM to PPM" UCSI data structure. > > > > The mailbox allows any UCSI command to be sent to the PPM so > > it should be also useful for validation, testing and > > debugging purposes. > > As it's for this type of thing, why not put it in debugfs instead? > > > +static ssize_t ucsi_write(struct file *filp, struct kobject *kobj, > > + const struct bin_attribute *attr, > > + char *buf, loff_t off, size_t count) > > +{ > > + struct ucsi_sysfs *sysfs = attr->private; > > + struct ucsi *ucsi = sysfs->ucsi; > > + int ret; > > + > > + u64 *control = (u64 *)&sysfs->mailbox[UCSI_CONTROL]; > > + u32 *cci = (u32 *)&sysfs->mailbox[UCSI_CCI]; > > + void *data = &sysfs->mailbox[UCSI_MESSAGE_IN]; > > + > > + /* TODO: MESSAGE_OUT. */ > > + if (off != UCSI_CONTROL || count != sizeof(*control)) > > + return -EFAULT; > > + > > + mutex_lock(&sysfs->lock); > > + > > + memset(data, 0, UCSI_MAX_DATA_LENGTH(ucsi)); > > + > > + /* PPM_RESET has to be handled separately. */ > > + *control = get_unaligned_le64(buf); > > + if (UCSI_COMMAND(*control) == UCSI_PPM_RESET) { > > + ret = ucsi_reset_ppm(ucsi, cci); > > + goto out_unlock_sysfs; > > + } > > + > > + mutex_lock(&ucsi->ppm_lock); > > + > > + ret = ucsi->ops->sync_control(ucsi, *control, cci, NULL, 0); > > + if (ret) > > + goto out_unlock_ppm; > > + > > + if (UCSI_CCI_LENGTH(*cci) && ucsi->ops->read_message_in(ucsi, data, UCSI_CCI_LENGTH(*cci))) > > + dev_err(ucsi->dev, "failed to read MESSAGE_IN\n"); > > + > > + ret = ucsi->ops->sync_control(ucsi, UCSI_ACK_CC_CI | UCSI_ACK_COMMAND_COMPLETE, > > + NULL, NULL, 0); > > +out_unlock_ppm: > > + mutex_unlock(&ucsi->ppm_lock); > > +out_unlock_sysfs: > > + mutex_unlock(&sysfs->lock); > > + > > + return ret ?: count; > > +} > > This worries me, any userspace tool can now do this? What other "bad" > things can it to the connection? Although, there is actually only a limited number of things that you can do to the connection using UCSI, that is definitely a concern. The PPM (which is the EC firmware in most cases) is expected to prevent any harmful or "unauthorized" UCSI commands from being executed, but I'm not sure there is any guarantees for that at the moment. > > +int ucsi_sysfs_register(struct ucsi *ucsi) > > +{ > > + struct ucsi_sysfs *sysfs; > > + int ret; > > + > > + sysfs = kzalloc(struct_size(sysfs, mailbox, UCSI_MAILBOX_SIZE(ucsi)), GFP_KERNEL); > > + if (!sysfs) > > + return -ENOMEM; > > + > > + sysfs->ucsi = ucsi; > > + mutex_init(&sysfs->lock); > > + memcpy(sysfs->mailbox, &ucsi->version, sizeof(ucsi->version)); > > + > > + sysfs_bin_attr_init(&sysfs->bin_attr); > > + > > + sysfs->bin_attr.attr.name = "ucsi"; > > + sysfs->bin_attr.attr.mode = 0644; > > + > > + sysfs->bin_attr.size = UCSI_MAILBOX_SIZE(ucsi); > > + sysfs->bin_attr.private = sysfs; > > + sysfs->bin_attr.read_new = ucsi_read; > > + sysfs->bin_attr.write_new = ucsi_write; > > + > > + ret = sysfs_create_bin_file(&ucsi->dev->kobj, &sysfs->bin_attr); > > You raced with userspace and lost, right? Why are you dynamically > creating this attribute, can't you just use a static one? The size of the attribute depends on the UCSI version. > But again, why not debugfs? I'd feel a lot more comfortable with that > instead of sysfs. I would actually prefer debugfs for this, but this is in any case not primarily for debugging and validation. The initial goal was to supply the user space some way to control the EC's power related policies using UCSI commands such as SET_POWER_LEVEL and GET_POWER_LEVEL (guys, please correct me if I got that wrong). But I'm now again wondering could those power policy tasks be handled using the UCSI power supplies after all? Venkat, did you look into that? thanks, -- heikki ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v1 1/2] usb: typec: ucsi: Command mailbox interface for the userspace 2025-02-07 13:04 ` Heikki Krogerus @ 2025-02-07 20:15 ` Dmitry Baryshkov 2025-02-11 21:21 ` Pathak, Asutosh 0 siblings, 1 reply; 8+ messages in thread From: Dmitry Baryshkov @ 2025-02-07 20:15 UTC (permalink / raw) To: Heikki Krogerus Cc: Greg Kroah-Hartman, Łukasz Bartosik, Abhishek Pandit-Subedi, Benson Leung, Pavan Holla, Christian A. Ehrhardt, Jameson Thies, Katiyar, Pooja, Pathak, Asutosh, Jayaraman, Venkat, linux-usb, linux-kernel On Fri, Feb 07, 2025 at 03:04:34PM +0200, Heikki Krogerus wrote: > On Thu, Feb 06, 2025 at 03:51:48PM +0100, Greg Kroah-Hartman wrote: > > On Thu, Feb 06, 2025 at 04:19:31PM +0200, Heikki Krogerus wrote: > > > Some of the UCSI commands can be used to configure the > > > entire Platform Policy Manager (PPM) instead of just > > > individual connectors. To allow the user space communicate > > > those commands with the PPM, adding a mailbox interface. The > > > interface is a single attribute file that represents the > > > main "OPM to PPM" UCSI data structure. > > > > > > The mailbox allows any UCSI command to be sent to the PPM so > > > it should be also useful for validation, testing and > > > debugging purposes. > > > > As it's for this type of thing, why not put it in debugfs instead? > > > > > +static ssize_t ucsi_write(struct file *filp, struct kobject *kobj, > > > + const struct bin_attribute *attr, > > > + char *buf, loff_t off, size_t count) > > > +{ > > > + struct ucsi_sysfs *sysfs = attr->private; > > > + struct ucsi *ucsi = sysfs->ucsi; > > > + int ret; > > > + > > > + u64 *control = (u64 *)&sysfs->mailbox[UCSI_CONTROL]; > > > + u32 *cci = (u32 *)&sysfs->mailbox[UCSI_CCI]; > > > + void *data = &sysfs->mailbox[UCSI_MESSAGE_IN]; > > > + > > > + /* TODO: MESSAGE_OUT. */ > > > + if (off != UCSI_CONTROL || count != sizeof(*control)) > > > + return -EFAULT; > > > + > > > + mutex_lock(&sysfs->lock); > > > + > > > + memset(data, 0, UCSI_MAX_DATA_LENGTH(ucsi)); > > > + > > > + /* PPM_RESET has to be handled separately. */ > > > + *control = get_unaligned_le64(buf); > > > + if (UCSI_COMMAND(*control) == UCSI_PPM_RESET) { > > > + ret = ucsi_reset_ppm(ucsi, cci); > > > + goto out_unlock_sysfs; > > > + } > > > + > > > + mutex_lock(&ucsi->ppm_lock); > > > + > > > + ret = ucsi->ops->sync_control(ucsi, *control, cci, NULL, 0); > > > + if (ret) > > > + goto out_unlock_ppm; > > > + > > > + if (UCSI_CCI_LENGTH(*cci) && ucsi->ops->read_message_in(ucsi, data, UCSI_CCI_LENGTH(*cci))) > > > + dev_err(ucsi->dev, "failed to read MESSAGE_IN\n"); > > > + > > > + ret = ucsi->ops->sync_control(ucsi, UCSI_ACK_CC_CI | UCSI_ACK_COMMAND_COMPLETE, > > > + NULL, NULL, 0); > > > +out_unlock_ppm: > > > + mutex_unlock(&ucsi->ppm_lock); > > > +out_unlock_sysfs: > > > + mutex_unlock(&sysfs->lock); > > > + > > > + return ret ?: count; > > > +} > > > > This worries me, any userspace tool can now do this? What other "bad" > > things can it to the connection? > > Although, there is actually only a limited number of things that you > can do to the connection using UCSI, that is definitely a concern. > > The PPM (which is the EC firmware in most cases) is expected to prevent > any harmful or "unauthorized" UCSI commands from being executed, but > I'm not sure there is any guarantees for that at the moment. > > > > +int ucsi_sysfs_register(struct ucsi *ucsi) > > > +{ > > > + struct ucsi_sysfs *sysfs; > > > + int ret; > > > + > > > + sysfs = kzalloc(struct_size(sysfs, mailbox, UCSI_MAILBOX_SIZE(ucsi)), GFP_KERNEL); > > > + if (!sysfs) > > > + return -ENOMEM; > > > + > > > + sysfs->ucsi = ucsi; > > > + mutex_init(&sysfs->lock); > > > + memcpy(sysfs->mailbox, &ucsi->version, sizeof(ucsi->version)); > > > + > > > + sysfs_bin_attr_init(&sysfs->bin_attr); > > > + > > > + sysfs->bin_attr.attr.name = "ucsi"; > > > + sysfs->bin_attr.attr.mode = 0644; > > > + > > > + sysfs->bin_attr.size = UCSI_MAILBOX_SIZE(ucsi); > > > + sysfs->bin_attr.private = sysfs; > > > + sysfs->bin_attr.read_new = ucsi_read; > > > + sysfs->bin_attr.write_new = ucsi_write; > > > + > > > + ret = sysfs_create_bin_file(&ucsi->dev->kobj, &sysfs->bin_attr); > > > > You raced with userspace and lost, right? Why are you dynamically > > creating this attribute, can't you just use a static one? > > The size of the attribute depends on the UCSI version. > > > But again, why not debugfs? I'd feel a lot more comfortable with that > > instead of sysfs. > > I would actually prefer debugfs for this, but this is in any case > not primarily for debugging and validation. > > The initial goal was to supply the user space some way to control the > EC's power related policies using UCSI commands such as > SET_POWER_LEVEL and GET_POWER_LEVEL (guys, please correct me if I got > that wrong). It generally feels that exporting the whole unmoderated channel to the firmware just to set power level is wrong. It should be interfaced through the PSY driver. > > But I'm now again wondering could those power policy tasks be handled > using the UCSI power supplies after all? Venkat, did you look into > that? > > thanks, > > -- > heikki -- With best wishes Dmitry ^ permalink raw reply [flat|nested] 8+ messages in thread
* RE: [PATCH v1 1/2] usb: typec: ucsi: Command mailbox interface for the userspace 2025-02-07 20:15 ` Dmitry Baryshkov @ 2025-02-11 21:21 ` Pathak, Asutosh 2025-02-12 7:44 ` Greg Kroah-Hartman 0 siblings, 1 reply; 8+ messages in thread From: Pathak, Asutosh @ 2025-02-11 21:21 UTC (permalink / raw) To: Dmitry Baryshkov, Heikki Krogerus Cc: Greg Kroah-Hartman, Łukasz Bartosik, Abhishek Pandit-Subedi, Benson Leung, Pavan Holla, Christian A. Ehrhardt, Jameson Thies, Katiyar, Pooja, Jayaraman, Venkat, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org On Tue, Feb 11, 2025 at 01:21:28PM -0700, Pathak Asutosh wrote: > On Fri, Feb 07, 2025 at 03:04:34PM +0200, Heikki Krogerus wrote: > > On Thu, Feb 06, 2025 at 03:51:48PM +0100, Greg Kroah-Hartman wrote: > > > On Thu, Feb 06, 2025 at 04:19:31PM +0200, Heikki Krogerus wrote: > > > > Some of the UCSI commands can be used to configure the > > > > entire Platform Policy Manager (PPM) instead of just > > > > individual connectors. To allow the user space communicate > > > > those commands with the PPM, adding a mailbox interface. The > > > > interface is a single attribute file that represents the > > > > main "OPM to PPM" UCSI data structure. > > > > > > > > The mailbox allows any UCSI command to be sent to the PPM so > > > > it should be also useful for validation, testing and > > > > debugging purposes. > > > > > > As it's for this type of thing, why not put it in debugfs instead? The intend of this sysfs is not limited to validation, testing and debugging purposes but rather providing interface for major user space application developments. At present we are working on an application/ user space service which will be calling UCSI read/write power level commands. But in future there would be more such applications which may require additional UCSI commands to use. We wanted to have a common and generic solution - and hence thought of going with sysfs interface. Issue with debugfs is, it is default disabled in release kernels. User has to rebuild the kernel if the application is based on the debugfs interface. This will become a bottleneck for wider use of such appliances. > > > > > > > +static ssize_t ucsi_write(struct file *filp, struct kobject *kobj, > > > > + const struct bin_attribute *attr, > > > > + char *buf, loff_t off, size_t count) > > > > +{ > > > > + struct ucsi_sysfs *sysfs = attr->private; > > > > + struct ucsi *ucsi = sysfs->ucsi; > > > > + int ret; > > > > + > > > > + u64 *control = (u64 *)&sysfs->mailbox[UCSI_CONTROL]; > > > > + u32 *cci = (u32 *)&sysfs->mailbox[UCSI_CCI]; > > > > + void *data = &sysfs->mailbox[UCSI_MESSAGE_IN]; > > > > + > > > > + /* TODO: MESSAGE_OUT. */ > > > > + if (off != UCSI_CONTROL || count != sizeof(*control)) > > > > + return -EFAULT; > > > > + > > > > + mutex_lock(&sysfs->lock); > > > > + > > > > + memset(data, 0, UCSI_MAX_DATA_LENGTH(ucsi)); > > > > + > > > > + /* PPM_RESET has to be handled separately. */ > > > > + *control = get_unaligned_le64(buf); > > > > + if (UCSI_COMMAND(*control) == UCSI_PPM_RESET) { > > > > + ret = ucsi_reset_ppm(ucsi, cci); > > > > + goto out_unlock_sysfs; > > > > + } > > > > + > > > > + mutex_lock(&ucsi->ppm_lock); > > > > + > > > > + ret = ucsi->ops->sync_control(ucsi, *control, cci, NULL, 0); > > > > + if (ret) > > > > + goto out_unlock_ppm; > > > > + > > > > + if (UCSI_CCI_LENGTH(*cci) && ucsi->ops->read_message_in(ucsi, data, > UCSI_CCI_LENGTH(*cci))) > > > > + dev_err(ucsi->dev, "failed to read MESSAGE_IN\n"); > > > > + > > > > + ret = ucsi->ops->sync_control(ucsi, UCSI_ACK_CC_CI | > UCSI_ACK_COMMAND_COMPLETE, > > > > + NULL, NULL, 0); > > > > +out_unlock_ppm: > > > > + mutex_unlock(&ucsi->ppm_lock); > > > > +out_unlock_sysfs: > > > > + mutex_unlock(&sysfs->lock); > > > > + > > > > + return ret ?: count; > > > > +} > > > > > > This worries me, any userspace tool can now do this? What other "bad" > > > things can it to the connection? > > > > Although, there is actually only a limited number of things that you > > can do to the connection using UCSI, that is definitely a concern. > > > > The PPM (which is the EC firmware in most cases) is expected to prevent > > any harmful or "unauthorized" UCSI commands from being executed, but > > I'm not sure there is any guarantees for that at the moment. > > Critical power setting related features and options are tightly controlled by PPM/LPM. In such cases, those UCSI command request by user space will be blocked by PPM/LPM and will eventually end of into DoS. Moreover, to further mitigate the risk of any malicious attack our understanding is this sysfs interface will be accessible only with root or super user privilege. > > > > +int ucsi_sysfs_register(struct ucsi *ucsi) > > > > +{ > > > > + struct ucsi_sysfs *sysfs; > > > > + int ret; > > > > + > > > > + sysfs = kzalloc(struct_size(sysfs, mailbox, UCSI_MAILBOX_SIZE(ucsi)), > GFP_KERNEL); > > > > + if (!sysfs) > > > > + return -ENOMEM; > > > > + > > > > + sysfs->ucsi = ucsi; > > > > + mutex_init(&sysfs->lock); > > > > + memcpy(sysfs->mailbox, &ucsi->version, sizeof(ucsi->version)); > > > > + > > > > + sysfs_bin_attr_init(&sysfs->bin_attr); > > > > + > > > > + sysfs->bin_attr.attr.name = "ucsi"; > > > > + sysfs->bin_attr.attr.mode = 0644; > > > > + > > > > + sysfs->bin_attr.size = UCSI_MAILBOX_SIZE(ucsi); > > > > + sysfs->bin_attr.private = sysfs; > > > > + sysfs->bin_attr.read_new = ucsi_read; > > > > + sysfs->bin_attr.write_new = ucsi_write; > > > > + > > > > + ret = sysfs_create_bin_file(&ucsi->dev->kobj, &sysfs->bin_attr); > > > > > > You raced with userspace and lost, right? Why are you dynamically > > > creating this attribute, can't you just use a static one? > > > > The size of the attribute depends on the UCSI version. > > > > > But again, why not debugfs? I'd feel a lot more comfortable with that > > > instead of sysfs. > > > > I would actually prefer debugfs for this, but this is in any case > > not primarily for debugging and validation. > > > > The initial goal was to supply the user space some way to control the > > EC's power related policies using UCSI commands such as > > SET_POWER_LEVEL and GET_POWER_LEVEL (guys, please correct me if I got > > that wrong). > > It generally feels that exporting the whole unmoderated channel to the > firmware just to set power level is wrong. It should be interfaced > through the PSY driver. > > > > > But I'm now again wondering could those power policy tasks be handled > > using the UCSI power supplies after all? Venkat, did you look into > > that? > > We are looking into this to figure out if there is any best way to expose power level settings options using UCSI power supply class interface. But even so, I believe that also does not completely eliminate risk of any malicious use. At present we are working on an application/ user space service which will be calling UCSI read/write power level commands. But in future there would be more such applications which may require additional UCSI commands to use. We wanted to have a common and generic solution - and hence thought of going with sysfs interface. Issue with debugfs is, it is default disabled in release kernels. User has to rebuild the kernel if the application is based on the debugfs interface. This will become a bottleneck for wider use of such appliances. Can we still think of going ahead with sysfs interface and double make sure to make this accessible only with root/su privilege to minimize any potential risk of bad uses? > > thanks, > > > > -- > > heikki > > -- > With best wishes > Dmitry ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v1 1/2] usb: typec: ucsi: Command mailbox interface for the userspace 2025-02-11 21:21 ` Pathak, Asutosh @ 2025-02-12 7:44 ` Greg Kroah-Hartman 0 siblings, 0 replies; 8+ messages in thread From: Greg Kroah-Hartman @ 2025-02-12 7:44 UTC (permalink / raw) To: Pathak, Asutosh Cc: Dmitry Baryshkov, Heikki Krogerus, Łukasz Bartosik, Abhishek Pandit-Subedi, Benson Leung, Pavan Holla, Christian A. Ehrhardt, Jameson Thies, Katiyar, Pooja, Jayaraman, Venkat, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org On Tue, Feb 11, 2025 at 09:21:28PM +0000, Pathak, Asutosh wrote: > On Tue, Feb 11, 2025 at 01:21:28PM -0700, Pathak Asutosh wrote: > > On Fri, Feb 07, 2025 at 03:04:34PM +0200, Heikki Krogerus wrote: > > > On Thu, Feb 06, 2025 at 03:51:48PM +0100, Greg Kroah-Hartman wrote: > > > > On Thu, Feb 06, 2025 at 04:19:31PM +0200, Heikki Krogerus wrote: > > > > > Some of the UCSI commands can be used to configure the > > > > > entire Platform Policy Manager (PPM) instead of just > > > > > individual connectors. To allow the user space communicate > > > > > those commands with the PPM, adding a mailbox interface. The > > > > > interface is a single attribute file that represents the > > > > > main "OPM to PPM" UCSI data structure. > > > > > > > > > > The mailbox allows any UCSI command to be sent to the PPM so > > > > > it should be also useful for validation, testing and > > > > > debugging purposes. > > > > > > > > As it's for this type of thing, why not put it in debugfs instead? > > The intend of this sysfs is not limited to validation, testing and > debugging purposes but rather providing interface for major user space > application developments. But that's not what you are saying above. sysfs is for attributes of a device, NOT for full device control. Use a proper api for that that can be correctly mediated if needed. > At present we are working on an application/ user space service which > will be calling UCSI read/write power level commands. But in future > there would be more such applications which may require additional > UCSI commands to use. We wanted to have a common and > generic solution - and hence thought of going with sysfs interface. We can't take new user/kernel apis without a real user, so please hold off on this series until you have a real user. Otherwise it is guaranteed that you will have to change that api based on actually using it. > Issue with debugfs is, it is default disabled in release kernels. User has > to rebuild the kernel if the application is based on the debugfs interface. > This will become a bottleneck for wider use of such appliances. It is up to the distro to enable/disable debugfs, that's not our issue. debugfs is NOT for normal system operation, so if you want to make this a proper api for normal users, than no, don't use debugfs, make it a real api. Which is probably NOT going to be sysfs. > > > > > +static ssize_t ucsi_write(struct file *filp, struct kobject *kobj, > > > > > + const struct bin_attribute *attr, > > > > > + char *buf, loff_t off, size_t count) > > > > > +{ > > > > > + struct ucsi_sysfs *sysfs = attr->private; > > > > > + struct ucsi *ucsi = sysfs->ucsi; > > > > > + int ret; > > > > > + > > > > > + u64 *control = (u64 *)&sysfs->mailbox[UCSI_CONTROL]; > > > > > + u32 *cci = (u32 *)&sysfs->mailbox[UCSI_CCI]; > > > > > + void *data = &sysfs->mailbox[UCSI_MESSAGE_IN]; > > > > > + > > > > > + /* TODO: MESSAGE_OUT. */ > > > > > + if (off != UCSI_CONTROL || count != sizeof(*control)) > > > > > + return -EFAULT; > > > > > + > > > > > + mutex_lock(&sysfs->lock); > > > > > + > > > > > + memset(data, 0, UCSI_MAX_DATA_LENGTH(ucsi)); > > > > > + > > > > > + /* PPM_RESET has to be handled separately. */ > > > > > + *control = get_unaligned_le64(buf); > > > > > + if (UCSI_COMMAND(*control) == UCSI_PPM_RESET) { > > > > > + ret = ucsi_reset_ppm(ucsi, cci); > > > > > + goto out_unlock_sysfs; > > > > > + } > > > > > + > > > > > + mutex_lock(&ucsi->ppm_lock); > > > > > + > > > > > + ret = ucsi->ops->sync_control(ucsi, *control, cci, NULL, 0); > > > > > + if (ret) > > > > > + goto out_unlock_ppm; > > > > > + > > > > > + if (UCSI_CCI_LENGTH(*cci) && ucsi->ops->read_message_in(ucsi, data, > > UCSI_CCI_LENGTH(*cci))) > > > > > + dev_err(ucsi->dev, "failed to read MESSAGE_IN\n"); > > > > > + > > > > > + ret = ucsi->ops->sync_control(ucsi, UCSI_ACK_CC_CI | > > UCSI_ACK_COMMAND_COMPLETE, > > > > > + NULL, NULL, 0); > > > > > +out_unlock_ppm: > > > > > + mutex_unlock(&ucsi->ppm_lock); > > > > > +out_unlock_sysfs: > > > > > + mutex_unlock(&sysfs->lock); > > > > > + > > > > > + return ret ?: count; > > > > > +} > > > > > > > > This worries me, any userspace tool can now do this? What other "bad" > > > > things can it to the connection? > > > > > > Although, there is actually only a limited number of things that you > > > can do to the connection using UCSI, that is definitely a concern. > > > > > > The PPM (which is the EC firmware in most cases) is expected to prevent > > > any harmful or "unauthorized" UCSI commands from being executed, but > > > I'm not sure there is any guarantees for that at the moment. > > > > Critical power setting related features and options are tightly controlled > by PPM/LPM. In such cases, those UCSI command request by user space > will be blocked by PPM/LPM and will eventually end of into DoS. What is "PPM/LPM"? I don't see that here. > Moreover, to further mitigate the risk of any malicious attack our > understanding is this sysfs interface will be accessible only with root or > super user privilege. Is it? You really want normal users being forced to be root in order to talk to this device? Make this a real api please, don't try to just do "provide raw access to the hardware and we will hope any userspace program can get it right", that way lies madness :) > Can we still think of going ahead with sysfs interface and double make > sure to make this accessible only with root/su privilege to minimize > any potential risk of bad uses? Nope! Get it right please, once you add it, you can't remove it. thanks, greg k-h ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v1 2/2] tools: usb: UCSI command testing tool 2025-02-06 14:19 [PATCH v1 0/2] usb: typec: ucsi: sysfs mailbox for commands Heikki Krogerus 2025-02-06 14:19 ` [PATCH v1 1/2] usb: typec: ucsi: Command mailbox interface for the userspace Heikki Krogerus @ 2025-02-06 14:19 ` Heikki Krogerus 1 sibling, 0 replies; 8+ messages in thread From: Heikki Krogerus @ 2025-02-06 14:19 UTC (permalink / raw) To: Łukasz Bartosik Cc: Abhishek Pandit-Subedi, Benson Leung, Pavan Holla, Dmitry Baryshkov, Christian A. Ehrhardt, Jameson Thies, Katiyar, Pooja, Pathak, Asutosh, Jayaraman, Venkat, Greg Kroah-Hartman, linux-usb, linux-kernel A tool that can be used to send UCSI commands to UCSI mailboxes. The tool outputs the contents of the mailbox that result from the command execution. On most systems there will be only one UCSI interface, but if the system has for example discrete GPUs with USB Type-C connectors, each GPU card may expose its own UCSI. All detected UCSI interfaces can be listed with -l option. The UCSI interface that the command is meant for can be optionally specified with -d option. By default, when the interface is not specified, the first found will used. Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> --- tools/usb/.gitignore | 1 + tools/usb/Build | 1 + tools/usb/Makefile | 8 +- tools/usb/ucsi.c | 250 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 tools/usb/ucsi.c diff --git a/tools/usb/.gitignore b/tools/usb/.gitignore index fce1ef5a9267..4645c8a1ca15 100644 --- a/tools/usb/.gitignore +++ b/tools/usb/.gitignore @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only ffs-test testusb +ucsi diff --git a/tools/usb/Build b/tools/usb/Build index 2ad6f9745816..f77e2ed05a63 100644 --- a/tools/usb/Build +++ b/tools/usb/Build @@ -1,2 +1,3 @@ testusb-y += testusb.o ffs-test-y += ffs-test.o +ucsi-y += ucsi.o diff --git a/tools/usb/Makefile b/tools/usb/Makefile index c6235667dd46..1670892a6d5a 100644 --- a/tools/usb/Makefile +++ b/tools/usb/Makefile @@ -16,7 +16,7 @@ MAKEFLAGS += -r override CFLAGS += -O2 -Wall -Wextra -g -D_GNU_SOURCE -I$(OUTPUT)include -I$(srctree)/tools/include override LDFLAGS += -lpthread -ALL_TARGETS := testusb ffs-test +ALL_TARGETS := testusb ffs-test ucsi ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS)) all: $(ALL_PROGRAMS) @@ -36,6 +36,12 @@ $(FFS_TEST_IN): FORCE $(OUTPUT)ffs-test: $(FFS_TEST_IN) $(QUIET_LINK)$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) +UCSI_IN := $(OUTPUT)ucsi-in.o +$(UCSI_IN): FORCE + $(Q)$(MAKE) $(build)=ucsi +$(OUTPUT)ucsi: $(UCSI_IN) + $(QUIET_LINK)$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) + clean: rm -f $(ALL_PROGRAMS) find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete -o -name '\.*.o.cmd' -delete diff --git a/tools/usb/ucsi.c b/tools/usb/ucsi.c new file mode 100644 index 000000000000..4649f3a80d62 --- /dev/null +++ b/tools/usb/ucsi.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * UCSI command testing tool + * + * Copyright (C) 2025, Intel Corporation + */ + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <linux/types.h> + +/* UCSI data structure field offsets */ +#define UCSI_VERSION 0 +#define UCSI_CCI 4 +#define UCSI_CONTROL 8 +#define UCSI_MESSAGE_IN 16 + +#define CCI_DATA_LENGTH(cci) (((cci) >> 8) & 0xff) + +static const char *class = "/sys/class/typec"; +static char path[PATH_MAX]; + +#define MAX_INTERFACES 10 + +static const char *find_dev(const char *dev, char **arr, size_t arr_size) +{ + size_t i; + + for (i = 0; i < arr_size; i++) + if (!strcmp(dev, arr[i])) + return arr[i]; + return NULL; +} + +static int ucsi_match(const struct dirent *entry) +{ + snprintf(path, sizeof(path), "%s/%s/device/ucsi", class, entry->d_name); + + return !access(path, F_OK); +} + +static int find_devices(char **devs) +{ + struct dirent **dirs; + int ndevs = 0; + char *rpath; + int n; + + n = scandir(class, &dirs, ucsi_match, alphasort); + if (n <= 0) + return 0; + + while (n--) { + snprintf(path, sizeof(path), "%s/%s/device", + class, dirs[n]->d_name); + + rpath = realpath(path, NULL); + + if (find_dev(rpath, devs, ndevs)) { + free(rpath); + continue; + } + + devs[ndevs++] = rpath; + + if (ndevs == MAX_INTERFACES) { + fprintf(stderr, "maximum number of interfaces reached\n"); + break; + } + } + + free(dirs); + + return ndevs; +} + +static int run_command(const char *mb, __u64 command) +{ + __u8 data[256] = { }; + __u32 cci; + int ret; + int fd; + __u8 i; + + snprintf(path, sizeof(path), "%s/ucsi", mb); + + fd = open(path, O_RDWR); + if (fd < 0) { + fprintf(stderr, "failed to open \'%s\'\n", path); + return 1; + } + + ret = pwrite(fd, &command, sizeof(command), UCSI_CONTROL); + if (ret < 0) { + fprintf(stderr, "Failed to send command (%d)\n", ret); + goto err; + } + + ret = pread(fd, &cci, sizeof(cci), UCSI_CCI); + if (ret <= 0) { + fprintf(stderr, "failed to read CCI\n"); + goto err; + } + + if (CCI_DATA_LENGTH(cci)) { + ret = pread(fd, data, CCI_DATA_LENGTH(cci), UCSI_MESSAGE_IN); + if (ret <= 0) { + fprintf(stderr, "failed to read MESSAGE_IN (%d)\n", ret); + goto err; + } + } + + /* Print everything. */ + + printf("\nCONTROL:\t0x%016llx\n", command); + printf("CCI:\t\t0x%08x\n", cci); + + if (CCI_DATA_LENGTH(cci)) { + printf("MESSAGE_IN"); + for (i = 0; i < CCI_DATA_LENGTH(cci); i++) { + if (!(i % 8)) { + if (i % 16) + printf(" "); + else + printf("\n%08d\t", i ? 10 * 16 / i : 0); + } + printf("%02x ", data[i]); + } + printf("\n"); + } +err: + close(fd); + return ret < 0 ? ret : 0; +} + +static int print_version(const char *mb) +{ + __u16 version = 0; + int ret; + int fd; + + snprintf(path, sizeof(path), "%s/ucsi", mb); + + fd = open(path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "failed to open \'%s\'\n", path); + return fd; + } + ret = pread(fd, &version, sizeof(version), UCSI_VERSION); + close(fd); + + if (ret <= 0) { + fprintf(stderr, "\'%s\' -- failed to read version (%d)\n", mb, ret); + return ret; + } + + printf("%s - UCSI v%u.%u\n", mb, + version >> 8 & 0xff, /* Major */ + version >> 4 & 0xf); /* Minor */ + return 0; +} + +static void usage(const char *name) +{ + fprintf(stderr, + "Usage: %s -l | [-d INTERFACE] COMMAND\n" + "Execute UCSI commands\n\n" + " -l\tlist interfaces (devices)\n" + " -d\tselect interface\n" + " -h\tdisplay this help and exit\n" + "\nIf no interface is supplied, the first found is used.\n", + name); + fprintf(stderr, "Examples:\n" + " %s 0x6 (GET_CAPABILITY)\n" + " %s 0x10007 (GET_CONNECTOR_CAPABILITY)\n", + name, name); +} + +int main(int argc, char *argv[]) +{ + char *devs[MAX_INTERFACES]; + const char *mb = NULL; + __u64 command = 0; + int ret = -EINVAL; + int n = 0; + char *end; + int opt; + int i; + + n = find_devices(devs); + + while ((opt = getopt(argc, argv, "d:hl")) != -1) { + switch (opt) { + case 'h': + usage(argv[0]); + goto out_free; + case 'l': + for (i = 0; i < n; i++) { + print_version(devs[i]); + free(devs[i]); + } + return 0; + case 'd': + mb = optarg; + if (!find_dev(mb, devs, n)) { + fprintf(stderr, "\'%s\' is not UCSI\n", mb); + goto out_free; + } + break; + default: + usage(argv[0]); + goto out_free; + } + } + + if (!n) { + fprintf(stderr, "No UCSI found\n"); + return 1; + } + + if (optind >= argc) { + usage(argv[0]); + goto out_free; + } + + command = strtoll(argv[optind], &end, 16); + if (errno == ERANGE || end == argv[optind] || *end != '\0') { + fprintf(stderr, "invalid command -- \'%s\'\n", argv[optind]); + goto out_free; + } + + if (!mb) + mb = devs[0]; + + ret = print_version(mb); + if (ret) + goto out_free; + + ret = run_command(mb, command); +out_free: + for (i = 0; i < n; i++) + free(devs[i]); + + return !!ret; +} -- 2.47.2 ^ permalink raw reply related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-02-12 7:45 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-02-06 14:19 [PATCH v1 0/2] usb: typec: ucsi: sysfs mailbox for commands Heikki Krogerus 2025-02-06 14:19 ` [PATCH v1 1/2] usb: typec: ucsi: Command mailbox interface for the userspace Heikki Krogerus 2025-02-06 14:51 ` Greg Kroah-Hartman 2025-02-07 13:04 ` Heikki Krogerus 2025-02-07 20:15 ` Dmitry Baryshkov 2025-02-11 21:21 ` Pathak, Asutosh 2025-02-12 7:44 ` Greg Kroah-Hartman 2025-02-06 14:19 ` [PATCH v1 2/2] tools: usb: UCSI command testing tool Heikki Krogerus
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox