From 733ff9059bb75f9a0916edd82fbfead6e2d50edb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20Tigeot?= <ftigeot@wolfpond.org>
Date: Mon, 9 Dec 2013 22:35:45 +0100
Subject: [PATCH 1/2] sysctl: Give each device its own context and subtree

* Add device_get_sysctl_ctx

* And device_get_sysctl_tree()

Obtained-from: FreeBSD
---
 sys/kern/subr_bus.c   | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++
 sys/sys/bus.h         |   2 +
 sys/sys/bus_private.h |   8 +++
 3 files changed, 165 insertions(+)

diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c
index c9c986b..3a64b1d 100644
--- a/sys/kern/subr_bus.c
+++ b/sys/kern/subr_bus.c
@@ -54,6 +54,7 @@
 #include <sys/mplock2.h>
 
 SYSCTL_NODE(_hw, OID_AUTO, bus, CTLFLAG_RW, NULL, NULL);
+SYSCTL_NODE(, OID_AUTO, dev, CTLFLAG_RW, NULL, NULL);
 
 MALLOC_DEFINE(M_BUS, "bus", "Bus data structures");
 
@@ -100,6 +101,144 @@ void		print_devclass_list(void);
 #define print_devclass_list()		/* nop */
 #endif
 
+/*
+ * dev sysctl tree
+ */
+
+enum {
+	DEVCLASS_SYSCTL_PARENT,
+};
+
+static int
+devclass_sysctl_handler(SYSCTL_HANDLER_ARGS)
+{
+	devclass_t dc = (devclass_t)arg1;
+	const char *value;
+
+	switch (arg2) {
+	case DEVCLASS_SYSCTL_PARENT:
+		value = dc->parent ? dc->parent->name : "";
+		break;
+	default:
+		return (EINVAL);
+	}
+	return (SYSCTL_OUT(req, value, strlen(value)));
+}
+
+static void
+devclass_sysctl_init(devclass_t dc)
+{
+
+	if (dc->sysctl_tree != NULL)
+		return;
+	sysctl_ctx_init(&dc->sysctl_ctx);
+	dc->sysctl_tree = SYSCTL_ADD_NODE(&dc->sysctl_ctx,
+	    SYSCTL_STATIC_CHILDREN(_dev), OID_AUTO, dc->name,
+	    CTLFLAG_RD, NULL, "");
+	SYSCTL_ADD_PROC(&dc->sysctl_ctx, SYSCTL_CHILDREN(dc->sysctl_tree),
+	    OID_AUTO, "%parent", CTLTYPE_STRING | CTLFLAG_RD,
+	    dc, DEVCLASS_SYSCTL_PARENT, devclass_sysctl_handler, "A",
+	    "parent class");
+}
+
+enum {
+	DEVICE_SYSCTL_DESC,
+	DEVICE_SYSCTL_DRIVER,
+	DEVICE_SYSCTL_LOCATION,
+	DEVICE_SYSCTL_PNPINFO,
+	DEVICE_SYSCTL_PARENT,
+};
+
+static int
+device_sysctl_handler(SYSCTL_HANDLER_ARGS)
+{
+	device_t dev = (device_t)arg1;
+	const char *value;
+	char *buf;
+	int error;
+
+	buf = NULL;
+	switch (arg2) {
+	case DEVICE_SYSCTL_DESC:
+		value = dev->desc ? dev->desc : "";
+		break;
+	case DEVICE_SYSCTL_DRIVER:
+		value = dev->driver ? dev->driver->name : "";
+		break;
+	case DEVICE_SYSCTL_LOCATION:
+		value = buf = kmalloc(1024, M_BUS, M_WAITOK | M_ZERO);
+		bus_child_location_str(dev, buf, 1024);
+		break;
+	case DEVICE_SYSCTL_PNPINFO:
+		value = buf = kmalloc(1024, M_BUS, M_WAITOK | M_ZERO);
+		bus_child_pnpinfo_str(dev, buf, 1024);
+		break;
+	case DEVICE_SYSCTL_PARENT:
+		value = dev->parent ? dev->parent->nameunit : "";
+		break;
+	default:
+		return (EINVAL);
+	}
+	error = SYSCTL_OUT(req, value, strlen(value));
+	if (buf != NULL)
+		kfree(buf, M_BUS);
+	return (error);
+}
+
+static void
+device_sysctl_init(device_t dev)
+{
+	devclass_t dc = dev->devclass;
+
+	if (dev->sysctl_tree != NULL)
+		return;
+	devclass_sysctl_init(dc);
+	sysctl_ctx_init(&dev->sysctl_ctx);
+	dev->sysctl_tree = SYSCTL_ADD_NODE(&dev->sysctl_ctx,
+	    SYSCTL_CHILDREN(dc->sysctl_tree), OID_AUTO,
+	    dev->nameunit + strlen(dc->name),
+	    CTLFLAG_RD, NULL, "");
+	SYSCTL_ADD_PROC(&dev->sysctl_ctx, SYSCTL_CHILDREN(dev->sysctl_tree),
+	    OID_AUTO, "%desc", CTLTYPE_STRING | CTLFLAG_RD,
+	    dev, DEVICE_SYSCTL_DESC, device_sysctl_handler, "A",
+	    "device description");
+	SYSCTL_ADD_PROC(&dev->sysctl_ctx, SYSCTL_CHILDREN(dev->sysctl_tree),
+	    OID_AUTO, "%driver", CTLTYPE_STRING | CTLFLAG_RD,
+	    dev, DEVICE_SYSCTL_DRIVER, device_sysctl_handler, "A",
+	    "device driver name");
+	SYSCTL_ADD_PROC(&dev->sysctl_ctx, SYSCTL_CHILDREN(dev->sysctl_tree),
+	    OID_AUTO, "%location", CTLTYPE_STRING | CTLFLAG_RD,
+	    dev, DEVICE_SYSCTL_LOCATION, device_sysctl_handler, "A",
+	    "device location relative to parent");
+	SYSCTL_ADD_PROC(&dev->sysctl_ctx, SYSCTL_CHILDREN(dev->sysctl_tree),
+	    OID_AUTO, "%pnpinfo", CTLTYPE_STRING | CTLFLAG_RD,
+	    dev, DEVICE_SYSCTL_PNPINFO, device_sysctl_handler, "A",
+	    "device identification");
+	SYSCTL_ADD_PROC(&dev->sysctl_ctx, SYSCTL_CHILDREN(dev->sysctl_tree),
+	    OID_AUTO, "%parent", CTLTYPE_STRING | CTLFLAG_RD,
+	    dev, DEVICE_SYSCTL_PARENT, device_sysctl_handler, "A",
+	    "parent device");
+}
+
+static void
+device_sysctl_update(device_t dev)
+{
+	devclass_t dc = dev->devclass;
+
+	if (dev->sysctl_tree == NULL)
+		return;
+	sysctl_rename_oid(dev->sysctl_tree, dev->nameunit + strlen(dc->name));
+}
+
+static void
+device_sysctl_fini(device_t dev)
+{
+	if (dev->sysctl_tree == NULL)
+		return;
+	sysctl_ctx_free(&dev->sysctl_ctx);
+	dev->sysctl_tree = NULL;
+}
+
 static void	device_attach_async(device_t dev);
 static void	device_attach_thread(void *arg);
 static int	device_doattach(device_t dev);
@@ -1387,6 +1526,18 @@ device_get_flags(device_t dev)
 	return(dev->devflags);
 }
 
+struct sysctl_ctx_list *
+device_get_sysctl_ctx(device_t dev)
+{
+	return (&dev->sysctl_ctx);
+}
+
+struct sysctl_oid *
+device_get_sysctl_tree(device_t dev)
+{
+	return (dev->sysctl_tree);
+}
+
 int
 device_print_prettyname(device_t dev)
 {
@@ -1721,11 +1872,13 @@ device_doattach(device_t dev)
 	int hasclass = (dev->devclass != NULL);
 	int error;
 
+	device_sysctl_init(dev);
 	error = DEVICE_ATTACH(dev);
 	if (error == 0) {
 		dev->state = DS_ATTACHED;
 		if (bootverbose && !device_is_quiet(dev))
 			device_print_child(bus, dev);
+		device_sysctl_update(dev);
 		devadded(dev);
 	} else {
 		kprintf("device_probe_and_attach: %s%d attach returned %d\n",
@@ -1735,6 +1888,7 @@ device_doattach(device_t dev)
 			device_set_devclass(dev, 0);
 		device_set_driver(dev, NULL);
 		dev->state = DS_NOTPRESENT;
+		device_sysctl_fini(dev);
 	}
 	return(error);
 }
@@ -1762,6 +1916,7 @@ device_detach(device_t dev)
 
 	dev->state = DS_NOTPRESENT;
 	device_set_driver(dev, NULL);
+	device_sysctl_fini(dev);
 
 	return(0);
 }
diff --git a/sys/sys/bus.h b/sys/sys/bus.h
index c730b26..1f2e94c 100644
--- a/sys/sys/bus.h
+++ b/sys/sys/bus.h
@@ -373,6 +373,8 @@ const	char *device_get_nameunit(device_t dev);
 void	*device_get_softc(device_t dev);
 device_state_t	device_get_state(device_t dev);
 int	device_get_unit(device_t dev);
+struct sysctl_ctx_list *device_get_sysctl_ctx(device_t dev);
+struct sysctl_oid *device_get_sysctl_tree(device_t dev);
 int	device_is_alive(device_t dev); /* did probe succeed? */
 int	device_is_attached(device_t dev);	/* did attach succeed? */
 int	device_is_enabled(device_t dev);
diff --git a/sys/sys/bus_private.h b/sys/sys/bus_private.h
index dd407ba..3176053 100644
--- a/sys/sys/bus_private.h
+++ b/sys/sys/bus_private.h
@@ -30,6 +30,8 @@
 #ifndef _SYS_BUS_PRIVATE_H_
 #define _SYS_BUS_PRIVATE_H_
 
+#include <sys/sysctl.h>
+
 #if !defined(_KERNEL) && !defined(_KERNEL_STRUCTURES)
 
 #error "This file should not be included by userland programs."
@@ -63,6 +65,9 @@ struct devclass {
 	char		*name;
 	device_t	*devices;	/* array of devices indexed by unit */
 	int		maxunit;	/* size of devices array */
+
+	struct sysctl_ctx_list sysctl_ctx;
+	struct sysctl_oid *sysctl_tree;
 };
 
 /*
@@ -131,6 +136,9 @@ struct device {
 	u_char		pad;
 	void		*ivars;
 	void		*softc;
+
+	struct sysctl_ctx_list sysctl_ctx; /**< state for sysctl variables  */
+	struct sysctl_oid *sysctl_tree;	/**< state for sysctl variables */
 };
 
 struct device_op_desc {
-- 
1.9.3

