The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/etherswitch/miiproxy.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2011-2012 Stefan Bethke.
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  *
   28  * $FreeBSD$
   29  */
   30 
   31 #include <sys/param.h>
   32 #include <sys/bus.h>
   33 #include <sys/kernel.h>
   34 #include <sys/malloc.h>
   35 #include <sys/module.h>
   36 #include <sys/socket.h>
   37 #include <sys/sockio.h>
   38 #include <sys/systm.h>
   39 
   40 #include <net/if.h>
   41 #include <net/if_media.h>
   42 
   43 #include <dev/etherswitch/miiproxy.h>
   44 #include <dev/mii/mii.h>
   45 #include <dev/mii/miivar.h>
   46 
   47 #include "mdio_if.h"
   48 #include "miibus_if.h"
   49 
   50 
   51 MALLOC_DECLARE(M_MIIPROXY);
   52 MALLOC_DEFINE(M_MIIPROXY, "miiproxy", "miiproxy data structures");
   53 
   54 driver_t miiproxy_driver;
   55 driver_t mdioproxy_driver;
   56 
   57 struct miiproxy_softc {
   58         device_t        parent;
   59         device_t        proxy;
   60         device_t        mdio;
   61 };
   62 
   63 struct mdioproxy_softc {
   64 };
   65 
   66 /*
   67  * The rendezvous data structures and functions allow two device endpoints to
   68  * match up, so that the proxy endpoint can be associated with a target
   69  * endpoint.  The proxy has to know the device name of the target that it
   70  * wants to associate with, for example through a hint.  The rendezvous code
   71  * makes no assumptions about the devices that want to meet.
   72  */
   73 struct rendezvous_entry;
   74 
   75 enum rendezvous_op {
   76         RENDEZVOUS_ATTACH,
   77         RENDEZVOUS_DETACH
   78 };
   79 
   80 typedef int (*rendezvous_callback_t)(enum rendezvous_op,
   81     struct rendezvous_entry *);
   82 
   83 static SLIST_HEAD(rendezvoushead, rendezvous_entry) rendezvoushead =
   84     SLIST_HEAD_INITIALIZER(rendezvoushead);
   85 
   86 struct rendezvous_endpoint {
   87         device_t                device;
   88         const char              *name;
   89         rendezvous_callback_t   callback;
   90 };
   91 
   92 struct rendezvous_entry {
   93         SLIST_ENTRY(rendezvous_entry)   entries;
   94         struct rendezvous_endpoint      proxy;
   95         struct rendezvous_endpoint      target;
   96 };
   97 
   98 /*
   99  * Call the callback routines for both the proxy and the target.  If either
  100  * returns an error, undo the attachment.
  101  */
  102 static int
  103 rendezvous_attach(struct rendezvous_entry *e, struct rendezvous_endpoint *ep)
  104 {
  105         int error;
  106 
  107         error = e->proxy.callback(RENDEZVOUS_ATTACH, e);
  108         if (error == 0) {
  109                 error = e->target.callback(RENDEZVOUS_ATTACH, e);
  110                 if (error != 0) {
  111                         e->proxy.callback(RENDEZVOUS_DETACH, e);
  112                         ep->device = NULL;
  113                         ep->callback = NULL;
  114                 }
  115         }
  116         return (error);
  117 }
  118 
  119 /*
  120  * Create an entry for the proxy in the rendezvous list.  The name parameter
  121  * indicates the name of the device that is the target endpoint for this
  122  * rendezvous.  The callback will be invoked as soon as the target is
  123  * registered: either immediately if the target registered itself earlier,
  124  * or once the target registers.  Returns ENXIO if the target has not yet
  125  * registered.
  126  */
  127 static int
  128 rendezvous_register_proxy(device_t dev, const char *name,
  129     rendezvous_callback_t callback)
  130 {
  131         struct rendezvous_entry *e;
  132 
  133         KASSERT(callback != NULL, ("callback must be set"));
  134         SLIST_FOREACH(e, &rendezvoushead, entries) {
  135                 if (strcmp(name, e->target.name) == 0) {
  136                         /* the target is already attached */
  137                         e->proxy.name = device_get_nameunit(dev);
  138                         e->proxy.device = dev;
  139                         e->proxy.callback = callback;
  140                         return (rendezvous_attach(e, &e->proxy));
  141                 }
  142         }
  143         e = malloc(sizeof(*e), M_MIIPROXY, M_WAITOK | M_ZERO);
  144         e->proxy.name = device_get_nameunit(dev);
  145         e->proxy.device = dev;
  146         e->proxy.callback = callback;
  147         e->target.name = name;
  148         SLIST_INSERT_HEAD(&rendezvoushead, e, entries);
  149         return (ENXIO);
  150 }
  151 
  152 /*
  153  * Create an entry in the rendezvous list for the target.
  154  * Returns ENXIO if the proxy has not yet registered.
  155  */
  156 static int
  157 rendezvous_register_target(device_t dev, rendezvous_callback_t callback)
  158 {
  159         struct rendezvous_entry *e;
  160         const char *name;
  161 
  162         KASSERT(callback != NULL, ("callback must be set"));
  163         name = device_get_nameunit(dev);
  164         SLIST_FOREACH(e, &rendezvoushead, entries) {
  165                 if (strcmp(name, e->target.name) == 0) {
  166                         e->target.device = dev;
  167                         e->target.callback = callback;
  168                         return (rendezvous_attach(e, &e->target));
  169                 }
  170         }
  171         e = malloc(sizeof(*e), M_MIIPROXY, M_WAITOK | M_ZERO);
  172         e->target.name = name;
  173         e->target.device = dev;
  174         e->target.callback = callback;
  175         SLIST_INSERT_HEAD(&rendezvoushead, e, entries);
  176         return (ENXIO);
  177 }
  178 
  179 /*
  180  * Remove the registration for the proxy.
  181  */
  182 static int
  183 rendezvous_unregister_proxy(device_t dev)
  184 {
  185         struct rendezvous_entry *e;
  186         int error = 0;
  187 
  188         SLIST_FOREACH(e, &rendezvoushead, entries) {
  189                 if (e->proxy.device == dev) {
  190                         if (e->target.device == NULL) {
  191                                 SLIST_REMOVE(&rendezvoushead, e, rendezvous_entry, entries);
  192                                 free(e, M_MIIPROXY);
  193                                 return (0);
  194                         } else {
  195                                 e->proxy.callback(RENDEZVOUS_DETACH, e);
  196                                 e->target.callback(RENDEZVOUS_DETACH, e);
  197                         }
  198                         e->proxy.device = NULL;
  199                         e->proxy.callback = NULL;
  200                         return (error);
  201                 }
  202         }
  203         return (ENOENT);
  204 }
  205 
  206 /*
  207  * Remove the registration for the target.
  208  */
  209 static int
  210 rendezvous_unregister_target(device_t dev)
  211 {
  212         struct rendezvous_entry *e;
  213         int error = 0;
  214 
  215         SLIST_FOREACH(e, &rendezvoushead, entries) {
  216                 if (e->target.device == dev) {
  217                         if (e->proxy.device == NULL) {
  218                                 SLIST_REMOVE(&rendezvoushead, e, rendezvous_entry, entries);
  219                                 free(e, M_MIIPROXY);
  220                                 return (0);
  221                         } else {
  222                                 e->proxy.callback(RENDEZVOUS_DETACH, e);
  223                                 e->target.callback(RENDEZVOUS_DETACH, e);
  224                         }
  225                         e->target.device = NULL;
  226                         e->target.callback = NULL;
  227                         return (error);
  228                 }
  229         }
  230         return (ENOENT);
  231 }
  232 
  233 /*
  234  * Functions of the proxy that is interposed between the ethernet interface
  235  * driver and the miibus device.
  236  */
  237 
  238 static int
  239 miiproxy_rendezvous_callback(enum rendezvous_op op, struct rendezvous_entry *rendezvous)
  240 {
  241         struct miiproxy_softc *sc = device_get_softc(rendezvous->proxy.device);
  242 
  243         switch (op) {
  244         case RENDEZVOUS_ATTACH:
  245                 sc->mdio = device_get_parent(rendezvous->target.device);
  246                 break;
  247         case RENDEZVOUS_DETACH:
  248                 sc->mdio = NULL;
  249                 break;
  250         }
  251         return (0);
  252 }
  253 
  254 static int
  255 miiproxy_probe(device_t dev)
  256 {
  257         device_set_desc(dev, "MII/MDIO proxy, MII side");
  258 
  259         return (BUS_PROBE_SPECIFIC);
  260 }
  261 
  262 static int
  263 miiproxy_attach(device_t dev)
  264 {
  265 
  266         /*
  267          * The ethernet interface needs to call mii_attach_proxy() to pass
  268          * the relevant parameters for rendezvous with the MDIO target.
  269          */
  270         return (bus_generic_attach(dev));
  271 }
  272 
  273 static int
  274 miiproxy_detach(device_t dev)
  275 {
  276 
  277         rendezvous_unregister_proxy(dev);
  278         bus_generic_detach(dev);
  279         return (0);
  280 }
  281 
  282 static int
  283 miiproxy_readreg(device_t dev, int phy, int reg)
  284 {
  285         struct miiproxy_softc *sc = device_get_softc(dev);
  286 
  287         if (sc->mdio != NULL)
  288                 return (MDIO_READREG(sc->mdio, phy, reg));
  289         return (-1);
  290 }
  291 
  292 static int
  293 miiproxy_writereg(device_t dev, int phy, int reg, int val)
  294 {
  295         struct miiproxy_softc *sc = device_get_softc(dev);
  296 
  297         if (sc->mdio != NULL)
  298                 return (MDIO_WRITEREG(sc->mdio, phy, reg, val));
  299         return (-1);
  300 }
  301 
  302 static void
  303 miiproxy_statchg(device_t dev)
  304 {
  305 
  306         MIIBUS_STATCHG(device_get_parent(dev));
  307 }
  308 
  309 static void
  310 miiproxy_linkchg(device_t dev)
  311 {
  312 
  313         MIIBUS_LINKCHG(device_get_parent(dev));
  314 }
  315 
  316 static void
  317 miiproxy_mediainit(device_t dev)
  318 {
  319 
  320         MIIBUS_MEDIAINIT(device_get_parent(dev));
  321 }
  322 
  323 /*
  324  * Functions for the MDIO target device driver.
  325  */
  326 static int
  327 mdioproxy_rendezvous_callback(enum rendezvous_op op, struct rendezvous_entry *rendezvous)
  328 {
  329         return (0);
  330 }
  331 
  332 static void
  333 mdioproxy_identify(driver_t *driver, device_t parent)
  334 {
  335         if (device_find_child(parent, driver->name, -1) == NULL) {
  336                 BUS_ADD_CHILD(parent, 0, driver->name, -1);
  337         }
  338 }
  339 
  340 static int
  341 mdioproxy_probe(device_t dev)
  342 {
  343         device_set_desc(dev, "MII/MDIO proxy, MDIO side");
  344 
  345         return (BUS_PROBE_SPECIFIC);
  346 }
  347 
  348 static int
  349 mdioproxy_attach(device_t dev)
  350 {
  351 
  352         rendezvous_register_target(dev, mdioproxy_rendezvous_callback);
  353         return (bus_generic_attach(dev));
  354 }
  355 
  356 static int
  357 mdioproxy_detach(device_t dev)
  358 {
  359 
  360         rendezvous_unregister_target(dev);
  361         bus_generic_detach(dev);
  362         return (0);
  363 }
  364 
  365 /*
  366  * Attach this proxy in place of miibus.  The target MDIO must be attached
  367  * already.  Returns NULL on error.
  368  */
  369 device_t
  370 mii_attach_proxy(device_t dev)
  371 {
  372         struct miiproxy_softc *sc;
  373         int             error;
  374         const char      *name;
  375         device_t        miiproxy;
  376 
  377         if (resource_string_value(device_get_name(dev),
  378             device_get_unit(dev), "mdio", &name) != 0) {
  379                 if (bootverbose)
  380                         printf("mii_attach_proxy: not attaching, no mdio"
  381                             " device hint for %s\n", device_get_nameunit(dev));
  382                 return (NULL);
  383         }
  384 
  385         miiproxy = device_add_child(dev, miiproxy_driver.name, -1);
  386         error = bus_generic_attach(dev);
  387         if (error != 0) {
  388                 device_printf(dev, "can't attach miiproxy\n");
  389                 return (NULL);
  390         }
  391         sc = device_get_softc(miiproxy);
  392         sc->parent = dev;
  393         sc->proxy = miiproxy;
  394         if (rendezvous_register_proxy(miiproxy, name, miiproxy_rendezvous_callback) != 0) {
  395                 device_printf(dev, "can't attach proxy\n");
  396                 return (NULL);
  397         }
  398         device_printf(miiproxy, "attached to target %s\n", device_get_nameunit(sc->mdio));
  399         return (miiproxy);
  400 }
  401 
  402 static device_method_t miiproxy_methods[] = {
  403         /* device interface */
  404         DEVMETHOD(device_probe,         miiproxy_probe),
  405         DEVMETHOD(device_attach,        miiproxy_attach),
  406         DEVMETHOD(device_detach,        miiproxy_detach),
  407         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
  408 
  409         /* MII interface */
  410         DEVMETHOD(miibus_readreg,       miiproxy_readreg),
  411         DEVMETHOD(miibus_writereg,      miiproxy_writereg),
  412         DEVMETHOD(miibus_statchg,       miiproxy_statchg),
  413         DEVMETHOD(miibus_linkchg,       miiproxy_linkchg),
  414         DEVMETHOD(miibus_mediainit,     miiproxy_mediainit),
  415 
  416         DEVMETHOD_END
  417 };
  418 
  419 static device_method_t mdioproxy_methods[] = {
  420         /* device interface */
  421         DEVMETHOD(device_identify,      mdioproxy_identify),
  422         DEVMETHOD(device_probe,         mdioproxy_probe),
  423         DEVMETHOD(device_attach,        mdioproxy_attach),
  424         DEVMETHOD(device_detach,        mdioproxy_detach),
  425         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
  426 
  427         DEVMETHOD_END
  428 };
  429 
  430 DEFINE_CLASS_0(miiproxy, miiproxy_driver, miiproxy_methods,
  431     sizeof(struct miiproxy_softc));
  432 DEFINE_CLASS_0(mdioproxy, mdioproxy_driver, mdioproxy_methods,
  433     sizeof(struct mdioproxy_softc));
  434 
  435 DRIVER_MODULE(mdioproxy, mdio, mdioproxy_driver, 0, 0);
  436 DRIVER_MODULE(miibus, miiproxy, miibus_driver, 0, 0);
  437 MODULE_VERSION(miiproxy, 1);
  438 MODULE_DEPEND(miiproxy, miibus, 1, 1, 1);

Cache object: 48808a0c23c4b9a12cfa44b29c953108


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.