leds: rgb: wago-m4-led-wrapper: add sysfs cmd passthrough

Add a `wago_led_cmd` sysfs attribute to the leds-m4 platform device
that allows raw ASCII commands to be forwarded directly to the M4
co-processor via RPMsg. This is useful for testing and debugging
M4 LED firmware without going through the LED class abstraction.

The attribute strips trailing whitespace and re-appends a single
newline before forwarding, matching the protocol expected by the
Zephyr application. A read of the attribute reports the RPMsg
channel state (online/offline).

The sysfs group is created during probe and removed on teardown.

Signed-off-by: Heinrich Toews <ht@twx-software.de>
This commit is contained in:
Heinrich Toews
2026-04-02 15:44:28 +02:00
parent 924da5f867
commit b04c22d06c
+74 -6
View File
@@ -50,6 +50,10 @@
* echo "0 128 128" > /sys/class/leds/m4-led3/multi_intensity
* echo 255 > /sys/class/leds/m4-led3/brightness
*
* # Send raw commands directly to the M4 for testing:
* echo "CMD-IDL" > /sys/bus/platform/devices/leds-m4/wago_led_cmd
* echo "CMD-CYC-50-128" > /sys/bus/platform/devices/leds-m4/wago_led_cmd
*
* Author: WAGO GmbH & Co. KG
*/
@@ -202,16 +206,71 @@ static int wago_send_cmd(struct wago_m4_led_priv *priv, const char *cmd)
}
/* -------------------------------------------------------------------------
* LED multiclass brightness_set callback (non-blocking, fire-and-forget)
* Sysfs attribute: wago_led_cmd
*
* Called by the LED core whenever userspace writes to:
* /sys/class/leds/m4-led<N>/brightness (master)
* /sys/class/leds/m4-led<N>/multi_intensity (R G B)
* Allows sending raw ASCII commands to the M4 directly from the shell.
* The command string is forwarded as-is via RPMsg (fire-and-forget).
*
* Registered as brightness_set (not brightness_set_blocking) because we no
* longer wait for an ACK. The LED core may call this from any context.
* Usage:
* echo "CMD-IDL" > /sys/bus/platform/devices/leds-m4/wago_led_cmd
* echo "CMD-CYC-50-128" > /sys/bus/platform/devices/leds-m4/wago_led_cmd
* echo "CMD-BLK-0-R-200-255" > /sys/bus/platform/devices/leds-m4/wago_led_cmd
* echo "CMD-FAD-0-G-20-5" > /sys/bus/platform/devices/leds-m4/wago_led_cmd
* echo "CMD-WLED-3-B-128" > /sys/bus/platform/devices/leds-m4/wago_led_cmd
* ---------------------------------------------------------------------- */
static ssize_t wago_led_cmd_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct wago_m4_led_priv *priv = dev_get_drvdata(dev);
char cmd[WAGO_CMD_MAX_LEN];
size_t len;
int ret;
/* Strip trailing newline added by echo and copy into local buffer */
len = min(count, sizeof(cmd) - 2);
memcpy(cmd, buf, len);
/* Remove trailing whitespace / newline */
while (len > 0 && (cmd[len - 1] == '\n' ||
cmd[len - 1] == '\r' ||
cmd[len - 1] == ' '))
len--;
/* Re-add a single newline — the Zephyr app expects it */
cmd[len++] = '\n';
cmd[len] = '\0';
mutex_lock(&priv->send_lock);
ret = wago_send_cmd(priv, cmd);
mutex_unlock(&priv->send_lock);
return ret ? ret : count;
}
static ssize_t wago_led_cmd_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct wago_m4_led_priv *priv = dev_get_drvdata(dev);
return sysfs_emit(buf, "%s\n",
priv->rpdev ? "online" : "offline");
}
static DEVICE_ATTR_RW(wago_led_cmd);
static struct attribute *wago_led_attrs[] = {
&dev_attr_wago_led_cmd.attr,
NULL,
};
static const struct attribute_group wago_led_attr_group = {
.attrs = wago_led_attrs,
};
static void wago_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
@@ -549,6 +608,14 @@ static int wago_m4_led_probe(struct platform_device *pdev)
return ret;
}
ret = sysfs_create_group(&pdev->dev.kobj, &wago_led_attr_group);
if (ret) {
dev_err(&pdev->dev,
"Failed to create sysfs group: %d\n", ret);
unregister_rpmsg_driver(&wago_rpmsg_driver);
return ret;
}
/*
* Schedule the boot work. The first attempt fires after
* WAGO_BOOT_INITIAL_DELAY_MS, giving the rootfs a head start.
@@ -572,6 +639,7 @@ static int wago_m4_led_remove(struct platform_device *pdev)
/* Cancel any pending boot retry before tearing down */
cancel_delayed_work_sync(&priv->boot_work);
sysfs_remove_group(&pdev->dev.kobj, &wago_led_attr_group);
unregister_rpmsg_driver(&wago_rpmsg_driver);
wago_rproc_stop(priv);