Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/lkm/net/ethfoo/ethfoo Add a sample utilization of Andrew...



details:   https://anonhg.NetBSD.org/src/rev/20f81f77bab4
branches:  trunk
changeset: 566549:20f81f77bab4
user:      cube <cube%NetBSD.org@localhost>
date:      Wed May 12 13:51:16 2004 +0000

description:
Add a sample utilization of Andrew Brown's sysctl framework.  See comments
in the code for more details.
Give copyright to TNF.

diffstat:

 sys/lkm/net/ethfoo/ethfoo/ethfoo_lkm.c |  287 ++++++++++++++++++++++++++++++++-
 1 files changed, 283 insertions(+), 4 deletions(-)

diffs (truncated from 387 to 300 lines):

diff -r da3c1098c1c5 -r 20f81f77bab4 sys/lkm/net/ethfoo/ethfoo/ethfoo_lkm.c
--- a/sys/lkm/net/ethfoo/ethfoo/ethfoo_lkm.c    Wed May 12 13:49:01 2004 +0000
+++ b/sys/lkm/net/ethfoo/ethfoo/ethfoo_lkm.c    Wed May 12 13:51:16 2004 +0000
@@ -1,7 +1,11 @@
-/*     $NetBSD: ethfoo_lkm.c,v 1.1 2003/11/24 21:58:45 cube Exp $      */
+/*     $NetBSD: ethfoo_lkm.c,v 1.2 2004/05/12 13:51:16 cube Exp $      */
 
 /*
- *  Copyright (c) 2003, Quentin Garnier.  All rights reserved.
+ *  Copyright (c) 2003, 2004 The NetBSD Foundation.
+ *  All rights reserved.
+ *
+ *  This code is derived from software contributed to the NetBSD Foundation
+ *   by Quentin Garnier.
  * 
  *  Redistribution and use in source and binary forms, with or without
  *  modification, are permitted provided that the following conditions
@@ -11,6 +15,13 @@
  *  2. Redistributions in binary form must reproduce the above copyright
  *     notice, this list of conditions and the following disclaimer in the
  *     documentation and/or other materials provided with the distribution.
+ *  3. All advertising materials mentioning features or use of this software
+ *     must display the following acknowledgement:
+ *         This product includes software developed by the NetBSD
+ *         Foundation, Inc. and its contributors.
+ *  4. Neither the name of The NetBSD Foundation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
  * 
  *  THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  *  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -33,6 +44,7 @@
  * Second, sample Ethernet driver.
  * Third, sample of use of autoconf stuff inside a LKM.
  * Fourth, sample clonable interface
+ * Fifth, sample sysctl interface use from a LKM.
  *
  * XXX Hacks
  * 1. NetBSD doesn't offer a way to change an Ethernet address, so I chose
@@ -51,6 +63,7 @@
 #endif
 #include <sys/lkm.h>
 #include <sys/sockio.h>
+#include <sys/sysctl.h>
 
 #include <net/if.h>
 #include <net/if_dl.h>
@@ -66,6 +79,25 @@
 };
 
 /*
+ * sysctl node management
+ *
+ * It's not really possible to use a SYSCTL_SETUP block with
+ * current LKM implementation, so it is easier to just define
+ * our own function.
+ *
+ * The handler function is a "helper" in Andrew Brown's sysctl
+ * framework terminology.  It is used as a gateway for sysctl
+ * requests over the nodes.
+ *
+ * ethfoo_log allows the module to log creations of nodes and
+ * destroy them all at once using sysctl_teardown.
+ */
+static struct sysctlnode *ethfoo_node;
+static struct sysctllog *ethfoo_log;
+static int     ethfoo_sysctl_setup();
+static int     ethfoo_sysctl_handler(SYSCTLFN_PROTO);
+
+/*
  * Since we're an Ethernet device, we need the 3 following
  * components: a leading struct device, a struct ethercom,
  * and also a struct ifmedia since we don't attach a PHY to
@@ -83,6 +115,7 @@
        struct ifmedia  sc_im;
        struct ethercom sc_ec;
        void            (*sc_bpf_mtap)(caddr_t, struct mbuf *);
+       int             sc_mibnum;
 };
 
 /* LKM management routines */
@@ -97,6 +130,11 @@
 static void    ethfoo_attach(struct device *, struct device *, void *);
 static int     ethfoo_detach(struct device*, int);
 
+/* Ethernet address helper functions */
+
+static char    *ethfoo_ether_sprintf(char *, const u_char *);
+static void    ethfoo_ether_aton(u_char *, char *);
+
 CFATTACH_DECL(ethfoo, sizeof(struct ethfoo_softc),
     ethfoo_match, ethfoo_attach, ethfoo_detach, NULL);
 
@@ -189,7 +227,7 @@
        }
 
        if_clone_attach(&ethfoo_cloners);
-
+       error = ethfoo_sysctl_setup();
 out:
        return error;
 }
@@ -219,6 +257,7 @@
 {
        int error, i;
 
+       sysctl_teardown(&ethfoo_log);
        if_clone_detach(&ethfoo_cloners);
 
        for (i = 0; i < ethfoo_cd.cd_ndevs; i++)
@@ -257,8 +296,11 @@
        struct ethfoo_softc *sc = (struct ethfoo_softc *)self;
        struct ifnet *ifp;
        u_int8_t enaddr[ETHER_ADDR_LEN] = { 0xf0, 0x0b, 0xa4, 0xff, 0xff, 0xff };
+       char enaddrstr[18];
        unsigned long u;
        uint32_t ui;
+       int error;
+       struct sysctlnode *node;
 
        aprint_normal("%s: faking Ethernet device\n",
            self->dv_xname);
@@ -272,7 +314,7 @@
        memcpy(enaddr+3, (u_int8_t *)&ui, 3);
 
        aprint_normal("%s: Ethernet address %s\n", sc->sc_dev.dv_xname,
-           ether_sprintf(enaddr));
+           ethfoo_ether_sprintf(enaddrstr, enaddr));
 
        /* ksyms interface is only available since mid-1.6R, so require
         * at least 1.6T
@@ -318,6 +360,28 @@
         * being common to all network interface drivers. */
        if_attach(ifp);
        ether_ifattach(ifp, enaddr);
+
+       /*
+        * Add a sysctl node for that interface.
+        *
+        * The pointer transmitted is not a string, but instead a pointer to the softc
+        * structure, which we can use to build the string value on the fly in the helper
+        * function of the node.  See the comments for ethfoo_sysctl_handler for details.
+        *
+        * As in ethfoo_sysctl_setup, we use ethfoo_log.  This allows the use of
+        * sysctl_teardown() in ethfoo_lkmunload, which undoes every creation we made in the
+        * module.
+        */
+       if ((error = sysctl_createv(&ethfoo_log, 0, &ethfoo_node,
+           &node, CTLFLAG_READWRITE,
+           CTLTYPE_STRING, sc->sc_dev.dv_xname, NULL,
+           ethfoo_sysctl_handler, 0, sc, 18,
+           CTL_CREATE, CTL_EOL)) != 0) {
+               sc->sc_mibnum = -1;
+               aprint_error("%s: sysctl_createv returned %d, ignoring\n",
+                   sc->sc_dev.dv_xname, error);
+       } else
+               sc->sc_mibnum = node->sysctl_num;
 }
 
 /*
@@ -329,7 +393,17 @@
 {
        struct ethfoo_softc *sc = (struct ethfoo_softc *)self;
        struct ifnet *ifp = &sc->sc_ec.ec_if;
+       int error;
 
+       /*
+        * Destroying a single leaf is a very straightforward operation using
+        * sysctl_destroyv.  One should be sure to always end the path with
+        * CTL_EOL.
+        */
+       if (sc->sc_mibnum != -1 && (error = sysctl_destroyv(ethfoo_node, sc->sc_mibnum,
+           CTL_EOL)) != 0)
+               aprint_error("%s: sysctl_destroyv returned %d, ignoring\n",
+                   sc->sc_dev.dv_xname, error);
        ether_ifdetach(ifp);
        if_detach(ifp);
        ifmedia_delete_instance(&sc->sc_im, IFM_INST_ANY);
@@ -516,3 +590,208 @@
                aprint_error("%s: unable to detach instance\n",
                    dev->dv_xname);
 }
+
+/*
+ * sysctl management routines
+ * You can set the address of an interface through:
+ * net.link.ethfoo.ethfoo<number>
+ *
+ * Note the consistent use of ethfoo_log in order to use
+ * sysctl_teardown at unload time.
+ *
+ * In the kernel you will find a lot of SYSCTL_SETUP blocks.  Those
+ * blocks register a function in a special section of the kernel
+ * (called a link set) which is used at init_sysctl() time to cycle
+ * through all those functions to create the kernel's sysctl tree.
+ *
+ * It is not possible to use link sets in a LKM, so the easiest is
+ * to simply call our own setup routine at load time.
+ *
+ * In the SYSCTL_SETUP blocks you find in the kernel, nodes have the
+ * CTLFLAG_PERMANENT flag, meaning they cannot be removed.  Once the
+ * whole kernel sysctl tree is built, it is not possible to add any
+ * permanent node.  This is fine with us since ethfoo is meant to be
+ * unloaded without leaving anything behind.
+ */
+int
+ethfoo_sysctl_setup(void)
+{
+       int error = 0;
+
+       if ((error = sysctl_createv(&ethfoo_log, 0, NULL, NULL, 0,
+           CTLTYPE_NODE, "net", NULL,
+           NULL, 0, NULL, 0,
+           CTL_NET, CTL_EOL)) != 0)
+               return (error);
+
+       if ((error = sysctl_createv(&ethfoo_log, 0, NULL, NULL, 0,
+           CTLTYPE_NODE, "link", NULL,
+           NULL, 0, NULL, 0,
+           CTL_NET, PF_LINK, CTL_EOL)) != 0)
+               return (error);
+
+       /*
+        * The first four parameters of sysctl_createv are for management.
+        *
+        * The four that follows, here starting with a '0' for the flags,
+        * describe the node.
+        *
+        * The next series of four set its value, through various possible
+        * means.
+        *
+        * Last but not least, the path to the node is described.  That path
+        * is relative to the given root (third argument).  Here we're
+        * starting from the root.
+        */
+       error = sysctl_createv(&ethfoo_log, 0, NULL, &ethfoo_node, 0,
+           CTLTYPE_NODE, "ethfoo", NULL,
+           NULL, 0, NULL, 0,
+           CTL_NET, PF_LINK, CTL_CREATE, CTL_EOL);
+
+       return (error);
+}
+
+/*
+ * The helper functions make Andrew Brown's interface really
+ * shine.  It makes possible to create value on the fly whether
+ * the sysctl value is read or written.
+ *
+ * As shown as an example in the man page, the first step is to
+ * create a copy of the node to have sysctl_lookup work on it.
+ *
+ * Here, we have more work to do than just a copy, since we have
+ * to create the string.  The first step is to collect the actual
+ * value of the node, which is a convenient pointer to the softc
+ * of the interface.  From there we create the string and use it
+ * as the value, but only for the *copy* of the node.
+ *
+ * Then we let sysctl_lookup do the magic, which consists in
+ * setting oldp and newp as required by the operation.  When the
+ * value is read, that means that the string will be copied to
+ * the user, and when it is written, the new value will be copied
+ * over in the addr array.
+ *
+ * If newp is NULL, the user was reading the value, so we don't
+ * have anything else to do.  If a new value was written, we
+ * have to check it.
+ *
+ * If it is incorrect, we can return an error and leave 'node' as
+ * it is:  since it is a copy of the actual node, the change will
+ * be forgotten.
+ *
+ * Upon a correct input, we commit the change to the ifnet
+ * structure of our interface.
+ */
+static int
+ethfoo_sysctl_handler(SYSCTLFN_ARGS)
+{
+       struct sysctlnode node;
+       struct ethfoo_softc *sc;
+       struct ifnet *ifp;
+       int error, i;
+       char addr[18];
+
+       node = *rnode;
+       sc = node.sysctl_data;
+       ifp = &sc->sc_ec.ec_if;
+       (void)ethfoo_ether_sprintf(addr, LLADDR(ifp->if_sadl));
+       node.sysctl_data = addr;
+       error = sysctl_lookup(SYSCTLFN_CALL(&node));
+       if (error || newp == NULL)
+               return (error);
+
+       if (strlen(addr) != 17)
+               return (EINVAL);
+
+       for (i = 0; i < 17; i++)
+               if ((i % 3 == 2 && addr[i] != ':') ||
+                   (i % 3 != 2 && !isxdigit(addr[i])))
+                       return (EINVAL);
+



Home | Main Index | Thread Index | Old Index