lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <20251205-v-spi-v1-1-89713cc6c0b4@nxp.com>
Date: Fri, 05 Dec 2025 21:40:26 +0800
From: "Peng Fan (OSS)" <peng.fan@....nxp.com>
To: Haixu Cui <quic_haixcui@...cinc.com>, Mark Brown <broonie@...nel.org>
Cc: virtualization@...ts.linux.dev, linux-spi@...r.kernel.org, 
 linux-kernel@...r.kernel.org, Peng Fan <peng.fan@....com>
Subject: [PATCH RFC] spi: virtio: Add [new,delete]_device sysfs interface

From: Peng Fan <peng.fan@....com>

spi-virtio registers a SPI controller, but we still need to register a
SPI device to initiate the SPI virtio transfer.

To Xen hypervisor, there might be no 'passthrough' node for DomU,
so add a sysfs interface to allow creating/deleting a SPI device.

Usage:
 echo "spidev" > /sys/class/spi_master/spi0/new_device
 echo "spi0.0" > /sys/class/spi_master/spi0/delete_device

Signed-off-by: Peng Fan <peng.fan@....com>
---
 drivers/spi/spi-virtio.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 171 insertions(+)

diff --git a/drivers/spi/spi-virtio.c b/drivers/spi/spi-virtio.c
index 2acb929b2c6907d6b073829c153d1e1d3bb729b5..9014d52da76b0f2e2c3f049605968fbbb790905e 100644
--- a/drivers/spi/spi-virtio.c
+++ b/drivers/spi/spi-virtio.c
@@ -5,12 +5,15 @@
  * Copyright (C) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
+#include <linux/cleanup.h>
 #include <linux/completion.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/kstrtox.h>
 #include <linux/module.h>
 #include <linux/spi/spi.h>
 #include <linux/stddef.h>
+#include <linux/string.h>
 #include <linux/virtio.h>
 #include <linux/virtio_ring.h>
 #include <linux/virtio_spi.h>
@@ -330,6 +333,170 @@ static void virtio_spi_del_vq(void *data)
 	vdev->config->del_vqs(vdev);
 }
 
+static ssize_t new_device_store(struct device *dev, struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct spi_controller *ctlr = container_of(dev, struct spi_controller, dev);
+	char *cs_str = NULL, *max_hz_str = NULL, *mode_str = NULL;
+	u32 mode = SPI_MODE_0, max_hz = 500000, cs = 0; /* MODE_0, 500KHz */
+	char *tok, *save;
+	struct spi_board_info bi = { };
+	struct spi_device *spi;
+	char *modalias = NULL;
+	int err;
+
+	char *kbuf __free(kfree) = kstrdup(buf, GFP_KERNEL);
+	if (!kbuf)
+		return -ENOMEM;
+
+	/*
+	 * Parse: "<modalias> <cs> [max_hz] [mode]"
+	 * Example: "spidev 0 5000000 0"
+	 */
+	tok = strim(strsep(&kbuf, "\n"));
+	if (!tok || !*tok)
+		return -EINVAL;
+
+	save = tok;
+
+	/* Extract all tokens first */
+	modalias = strsep(&save, " \t");
+	if (save && *save)
+		cs_str = strsep(&save, " \t");
+	if (save && *save)
+		max_hz_str = strsep(&save, " \t");
+	if (save && *save)
+		mode_str = strsep(&save, " \t");
+
+	/* Validate required parameters */
+	if (!modalias || !*modalias) {
+		dev_err(dev, "Missing modalias\n");
+		return -EINVAL;
+	}
+
+	if (cs_str && *cs_str) {
+		/* Parse chip_select (optional) */
+		err = kstrtou32(cs_str, 0, &cs);
+		if (err) {
+			dev_err(dev, "Invalid chip_select: %s\n", cs_str);
+			return -EINVAL;
+		}
+
+		if (cs >= ctlr->num_chipselect) {
+			dev_err(dev, "chip_select %u out of range (max %u)\n",
+				cs, ctlr->num_chipselect - 1);
+			return -EINVAL;
+		}
+	}
+
+	/* Parse max_speed_hz (optional) */
+	if (max_hz_str && *max_hz_str) {
+		err = kstrtou32(max_hz_str, 0, &max_hz);
+		if (err) {
+			dev_err(dev, "Invalid max_speed_hz: %s\n", max_hz_str);
+			return -EINVAL;
+		}
+
+		if (max_hz == 0 || max_hz > ctlr->max_speed_hz) {
+			dev_err(dev, "max_speed_hz %u out of range (1-%u)\n",
+				max_hz, ctlr->max_speed_hz);
+			return -EINVAL;
+		}
+	}
+
+	/* Parse mode (optional) */
+	if (mode_str && *mode_str) {
+		err = kstrtouint(mode_str, 0, &mode);
+		if (err) {
+			dev_err(dev, "Invalid mode: %s\n", mode_str);
+			return -EINVAL;
+		}
+	}
+
+	/* Check for extra unexpected parameters */
+	if (save && *save)
+		dev_warn(dev, "Ignoring extra parameters: %s\n", save);
+
+	/* Populate board info */
+	strscpy(bi.modalias, modalias, sizeof(bi.modalias));
+	bi.chip_select	= cs;
+	bi.max_speed_hz	= max_hz;
+	bi.mode		= mode;
+
+	/* Serialize against controller add/remove paths */
+	mutex_lock(&ctlr->bus_lock_mutex);
+	spi = spi_new_device(ctlr, &bi);
+	mutex_unlock(&ctlr->bus_lock_mutex);
+
+	if (!spi) {
+		dev_err(dev, "Failed to create SPI device %s (cs=%u, mode=0x%x)\n",
+			modalias, cs, mode);
+		return -ENODEV;
+	}
+
+	dev_info(dev, "Created SPI device %s: cs=%u, max_hz=%u, mode=0x%x\n",
+		 modalias, cs, max_hz, mode);
+
+	/*
+	 * generic "spidev" may NOT auto-bind. force-bind from userspace via:
+	 * echo spidev > /sys/bus/spi/devices/spiB.C/driver_override
+	 * echo spiB.C > /sys/bus/spi/drivers/spidev/bind
+	 */
+
+	return count;
+}
+
+static ssize_t delete_device_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	struct spi_controller *ctlr = container_of(dev, struct spi_controller, dev);
+	struct spi_device *spi;
+	struct device *child;
+	char *device_name;
+
+	char *kbuf __free(kfree) = kstrdup(buf, GFP_KERNEL);
+	if (!kbuf)
+		return -ENOMEM;
+
+	device_name = strim(kbuf);
+	if (!device_name || !*device_name) {
+		dev_err(dev, "Empty device name\n");
+		return -EINVAL;
+	}
+
+	guard(mutex)(&ctlr->bus_lock_mutex);
+
+	/* Find and remove device with matching name */
+	child = device_find_child_by_name(&ctlr->dev, device_name);
+	if (!child) {
+		dev_err(dev, "SPI device '%s' not found\n", device_name);
+		return -ENODEV;
+	}
+
+	spi = to_spi_device(child);
+	dev_info(dev, "Removing SPI device %s (cs=%u, modalias=%s)\n",
+		 dev_name(&spi->dev), spi_get_chipselect(spi, 0),
+		 spi->modalias);
+
+	spi_unregister_device(spi);
+
+	return count;
+}
+
+static DEVICE_ATTR_WO(new_device);
+static DEVICE_ATTR_WO(delete_device);
+
+static struct attribute *virtio_spi_attrs[] = {
+	&dev_attr_new_device.attr,
+	&dev_attr_delete_device.attr,
+	NULL,
+};
+
+static const struct attribute_group virtio_spi_attr_group = {
+	.attrs = virtio_spi_attrs,
+};
+
 static int virtio_spi_probe(struct virtio_device *vdev)
 {
 	struct virtio_spi_priv *priv;
@@ -366,6 +533,10 @@ static int virtio_spi_probe(struct virtio_device *vdev)
 	if (ret)
 		return dev_err_probe(&vdev->dev, ret, "Cannot register controller\n");
 
+	ret = devm_device_add_group(&ctrl->dev, &virtio_spi_attr_group);
+	if (ret)
+		return dev_err_probe(&ctrl->dev, ret, "Cannot add virtio_spi_attr_group\n");
+
 	return 0;
 }
 

---
base-commit: bc04acf4aeca588496124a6cf54bfce3db327039
change-id: 20251205-v-spi-55a2a0ae8cc9

Best regards,
-- 
Peng Fan <peng.fan@....com>


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ