git: d0d0d14abcab - stable/13 - linuxkpi: Rework detach function

From: Emmanuel Vadot <manu_at_FreeBSD.org>
Date: Tue, 21 Jun 2022 15:23:13 UTC
The branch stable/13 has been updated by manu:

URL: https://cgit.FreeBSD.org/src/commit/?id=d0d0d14abcab6427ca747f198d2bcfecc0d142c2

commit d0d0d14abcab6427ca747f198d2bcfecc0d142c2
Author:     Emmanuel Vadot <manu@FreeBSD.org>
AuthorDate: 2022-05-13 11:56:36 +0000
Commit:     Emmanuel Vadot <manu@FreeBSD.org>
CommitDate: 2022-06-21 15:13:57 +0000

    linuxkpi: Rework detach function
    
    We need to detach the matching i2c adapter so look for the right one.
    While here add some locks to protect multiple add/del at the same time.
    
    Fixes:  1961a14a47 ("linuxkpi: Add i2c support")
    Sponsored by:   Beckhoff Automation GmbH & Co. KG
    
    (cherry picked from commit 25d21a845223ffad189fd2a7831ebf5e298b628a)
---
 sys/compat/linuxkpi/common/src/linux_i2c.c   | 70 ++++++++++++++++++++++++----
 sys/compat/linuxkpi/common/src/linux_i2cbb.c | 10 ++++
 sys/compat/linuxkpi/common/src/lkpi_iic_if.m |  4 ++
 3 files changed, 76 insertions(+), 8 deletions(-)

diff --git a/sys/compat/linuxkpi/common/src/linux_i2c.c b/sys/compat/linuxkpi/common/src/linux_i2c.c
index ef264abd3fd4..eedd877f0e0f 100644
--- a/sys/compat/linuxkpi/common/src/linux_i2c.c
+++ b/sys/compat/linuxkpi/common/src/linux_i2c.c
@@ -53,6 +53,27 @@ struct lkpi_iic_softc {
 	struct i2c_adapter	*adapter;
 };
 
+static struct sx lkpi_sx_i2c;
+
+static void
+lkpi_sysinit_i2c(void *arg __unused)
+{
+
+	sx_init(&lkpi_sx_i2c, "lkpi-i2c");
+}
+
+static void
+lkpi_sysuninit_i2c(void *arg __unused)
+{
+
+	sx_destroy(&lkpi_sx_i2c);
+}
+
+SYSINIT(lkpi_i2c, SI_SUB_DRIVERS, SI_ORDER_ANY,
+    lkpi_sysinit_i2c, NULL);
+SYSUNINIT(lkpi_i2c, SI_SUB_DRIVERS, SI_ORDER_ANY,
+    lkpi_sysuninit_i2c, NULL);
+
 static int
 lkpi_iic_probe(device_t dev)
 {
@@ -98,6 +119,15 @@ lkpi_iic_add_adapter(device_t dev, struct i2c_adapter *adapter)
 	return (0);
 }
 
+static struct i2c_adapter *
+lkpi_iic_get_adapter(device_t dev)
+{
+	struct lkpi_iic_softc *sc;
+
+	sc = device_get_softc(dev);
+	return (sc->adapter);
+}
+
 static device_method_t lkpi_iic_methods[] = {
 	/* device interface */
 	DEVMETHOD(device_probe,		lkpi_iic_probe),
@@ -113,6 +143,7 @@ static device_method_t lkpi_iic_methods[] = {
 
 	/* lkpi_iic interface */
 	DEVMETHOD(lkpi_iic_add_adapter,	lkpi_iic_add_adapter),
+	DEVMETHOD(lkpi_iic_get_adapter,	lkpi_iic_get_adapter),
 
 	DEVMETHOD_END
 };
@@ -179,12 +210,16 @@ lkpi_i2c_add_adapter(struct i2c_adapter *adapter)
 	device_t lkpi_iic;
 	int error;
 
+	if (adapter->name[0] == '\0')
+		return (-EINVAL);
 	if (bootverbose)
 		device_printf(adapter->dev.parent->bsddev,
 		    "Adding i2c adapter %s\n", adapter->name);
+	sx_xlock(&lkpi_sx_i2c);
 	lkpi_iic = device_add_child(adapter->dev.parent->bsddev, "lkpi_iic", -1);
 	if (lkpi_iic == NULL) {
 		device_printf(adapter->dev.parent->bsddev, "Couldn't add lkpi_iic\n");
+		sx_xunlock(&lkpi_sx_i2c);
 		return (ENXIO);
 	}
 
@@ -192,9 +227,11 @@ lkpi_i2c_add_adapter(struct i2c_adapter *adapter)
 	if (error) {
 		device_printf(adapter->dev.parent->bsddev,
 		  "failed to attach child: error %d\n", error);
+		sx_xunlock(&lkpi_sx_i2c);
 		return (ENXIO);
 	}
 	LKPI_IIC_ADD_ADAPTER(lkpi_iic, adapter);
+	sx_xunlock(&lkpi_sx_i2c);
 	return (0);
 }
 
@@ -202,18 +239,35 @@ int
 lkpi_i2c_del_adapter(struct i2c_adapter *adapter)
 {
 	device_t child;
+	int unit, rv;
 
+	if (adapter == NULL)
+		return (-EINVAL);
 	if (bootverbose)
 		device_printf(adapter->dev.parent->bsddev,
 		    "Removing i2c adapter %s\n", adapter->name);
+	sx_xlock(&lkpi_sx_i2c);
+	unit = 0;
+	while ((child = device_find_child(adapter->dev.parent->bsddev, "lkpi_iic", unit++)) != NULL) {
+
+		if (adapter == LKPI_IIC_GET_ADAPTER(child)) {
+			device_delete_child(adapter->dev.parent->bsddev, child);
+			rv = 0;
+			goto out;
+		}
+	}
 
-	child = device_find_child(adapter->dev.parent->bsddev, "lkpi_iic", -1);
-	if (child != NULL)
-		device_delete_child(adapter->dev.parent->bsddev, child);
-
-	child = device_find_child(adapter->dev.parent->bsddev, "lkpi_iicbb", -1);
-	if (child != NULL)
-		device_delete_child(adapter->dev.parent->bsddev, child);
+	unit = 0;
+	while ((child = device_find_child(adapter->dev.parent->bsddev, "lkpi_iicbb", unit++)) != NULL) {
 
-	return (0);
+		if (adapter == LKPI_IIC_GET_ADAPTER(child)) {
+			device_delete_child(adapter->dev.parent->bsddev, child);
+			rv = 0;
+			goto out;
+		}
+	}
+	rv = -EINVAL;
+out:
+	sx_xunlock(&lkpi_sx_i2c);
+	return (rv);
 }
diff --git a/sys/compat/linuxkpi/common/src/linux_i2cbb.c b/sys/compat/linuxkpi/common/src/linux_i2cbb.c
index e6f851bee103..f7321f060fa0 100644
--- a/sys/compat/linuxkpi/common/src/linux_i2cbb.c
+++ b/sys/compat/linuxkpi/common/src/linux_i2cbb.c
@@ -100,6 +100,15 @@ lkpi_iicbb_add_adapter(device_t dev, struct i2c_adapter *adapter)
 	return (0);
 }
 
+static struct i2c_adapter *
+lkpi_iicbb_get_adapter(device_t dev)
+{
+	struct lkpi_iicbb_softc *sc;
+
+	sc = device_get_softc(dev);
+	return (sc->adapter);
+}
+
 static device_method_t lkpi_iicbb_methods[] = {
 	/* device interface */
 	DEVMETHOD(device_probe,		lkpi_iicbb_probe),
@@ -117,6 +126,7 @@ static device_method_t lkpi_iicbb_methods[] = {
 
 	/* lkpi_iicbb interface */
 	DEVMETHOD(lkpi_iic_add_adapter,	lkpi_iicbb_add_adapter),
+	DEVMETHOD(lkpi_iic_get_adapter,	lkpi_iicbb_get_adapter),
 
 	DEVMETHOD_END
 };
diff --git a/sys/compat/linuxkpi/common/src/lkpi_iic_if.m b/sys/compat/linuxkpi/common/src/lkpi_iic_if.m
index 2379182c409b..c1b4abd79084 100644
--- a/sys/compat/linuxkpi/common/src/lkpi_iic_if.m
+++ b/sys/compat/linuxkpi/common/src/lkpi_iic_if.m
@@ -35,3 +35,7 @@ METHOD int add_adapter {
 	device_t dev;
 	struct i2c_adapter *adapter;
 };
+
+METHOD struct i2c_adapter * get_adapter {
+	device_t dev;
+};