miiproxy.c revision 234861
1234861Sadrian/*- 2234861Sadrian * Copyright (c) 2011-2012 Stefan Bethke. 3234861Sadrian * All rights reserved. 4234861Sadrian * 5234861Sadrian * Redistribution and use in source and binary forms, with or without 6234861Sadrian * modification, are permitted provided that the following conditions 7234861Sadrian * are met: 8234861Sadrian * 1. Redistributions of source code must retain the above copyright 9234861Sadrian * notice, this list of conditions and the following disclaimer. 10234861Sadrian * 2. Redistributions in binary form must reproduce the above copyright 11234861Sadrian * notice, this list of conditions and the following disclaimer in the 12234861Sadrian * documentation and/or other materials provided with the distribution. 13234861Sadrian * 14234861Sadrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15234861Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16234861Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17234861Sadrian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18234861Sadrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19234861Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20234861Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21234861Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22234861Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23234861Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24234861Sadrian * SUCH DAMAGE. 25234861Sadrian * 26234861Sadrian * $FreeBSD: head/sys/dev/etherswitch/miiproxy.c 234861 2012-05-01 06:11:38Z adrian $ 27234861Sadrian */ 28234861Sadrian 29234861Sadrian#include <sys/param.h> 30234861Sadrian#include <sys/bus.h> 31234861Sadrian#include <sys/kernel.h> 32234861Sadrian#include <sys/module.h> 33234861Sadrian#include <sys/socket.h> 34234861Sadrian#include <sys/sockio.h> 35234861Sadrian#include <sys/systm.h> 36234861Sadrian 37234861Sadrian#include <net/if.h> 38234861Sadrian#include <net/if_arp.h> 39234861Sadrian#include <net/ethernet.h> 40234861Sadrian#include <net/if_dl.h> 41234861Sadrian#include <net/if_media.h> 42234861Sadrian#include <net/if_types.h> 43234861Sadrian 44234861Sadrian#include <dev/etherswitch/miiproxy.h> 45234861Sadrian#include <dev/mii/mii.h> 46234861Sadrian#include <dev/mii/miivar.h> 47234861Sadrian 48234861Sadrian#include "mdio_if.h" 49234861Sadrian#include "miibus_if.h" 50234861Sadrian 51234861Sadrian 52234861SadrianMALLOC_DECLARE(M_MIIPROXY); 53234861SadrianMALLOC_DEFINE(M_MIIPROXY, "miiproxy", "miiproxy data structures"); 54234861Sadrian 55234861Sadriandriver_t miiproxy_driver; 56234861Sadriandriver_t mdioproxy_driver; 57234861Sadrian 58234861Sadrianstruct miiproxy_softc { 59234861Sadrian device_t parent; 60234861Sadrian device_t proxy; 61234861Sadrian device_t mdio; 62234861Sadrian}; 63234861Sadrian 64234861Sadrianstruct mdioproxy_softc { 65234861Sadrian}; 66234861Sadrian 67234861Sadrian/* 68234861Sadrian * The rendevous data structures and functions allow two device endpoints to 69234861Sadrian * match up, so that the proxy endpoint can be associated with a target 70234861Sadrian * endpoint. The proxy has to know the device name of the target that it 71234861Sadrian * wants to associate with, for example through a hint. The rendevous code 72234861Sadrian * makes no assumptions about the devices that want to meet. 73234861Sadrian */ 74234861Sadrianstruct rendevous_entry; 75234861Sadrian 76234861Sadrianenum rendevous_op { 77234861Sadrian RENDEVOUS_ATTACH, 78234861Sadrian RENDEVOUS_DETACH 79234861Sadrian}; 80234861Sadrian 81234861Sadriantypedef int (*rendevous_callback_t)(enum rendevous_op, 82234861Sadrian struct rendevous_entry *); 83234861Sadrian 84234861Sadrianstatic SLIST_HEAD(rendevoushead, rendevous_entry) rendevoushead = 85234861Sadrian SLIST_HEAD_INITIALIZER(rendevoushead); 86234861Sadrian 87234861Sadrianstruct rendevous_endpoint { 88234861Sadrian device_t device; 89234861Sadrian const char *name; 90234861Sadrian rendevous_callback_t callback; 91234861Sadrian}; 92234861Sadrian 93234861Sadrianstruct rendevous_entry { 94234861Sadrian SLIST_ENTRY(rendevous_entry) entries; 95234861Sadrian struct rendevous_endpoint proxy; 96234861Sadrian struct rendevous_endpoint target; 97234861Sadrian}; 98234861Sadrian 99234861Sadrian/* 100234861Sadrian * Call the callback routines for both the proxy and the target. If either 101234861Sadrian * returns an error, undo the attachment. 102234861Sadrian */ 103234861Sadrianstatic int 104234861Sadrianrendevous_attach(struct rendevous_entry *e, struct rendevous_endpoint *ep) 105234861Sadrian{ 106234861Sadrian int error; 107234861Sadrian 108234861Sadrian error = e->proxy.callback(RENDEVOUS_ATTACH, e); 109234861Sadrian if (error == 0) { 110234861Sadrian error = e->target.callback(RENDEVOUS_ATTACH, e); 111234861Sadrian if (error != 0) { 112234861Sadrian e->proxy.callback(RENDEVOUS_DETACH, e); 113234861Sadrian ep->device = NULL; 114234861Sadrian ep->callback = NULL; 115234861Sadrian } 116234861Sadrian } 117234861Sadrian return (error); 118234861Sadrian} 119234861Sadrian 120234861Sadrian/* 121234861Sadrian * Create an entry for the proxy in the rendevous list. The name parameter 122234861Sadrian * indicates the name of the device that is the target endpoint for this 123234861Sadrian * rendevous. The callback will be invoked as soon as the target is 124234861Sadrian * registered: either immediately if the target registered itself earlier, 125234861Sadrian * or once the target registers. Returns ENXIO if the target has not yet 126234861Sadrian * registered. 127234861Sadrian */ 128234861Sadrianstatic int 129234861Sadrianrendevous_register_proxy(device_t dev, const char *name, 130234861Sadrian rendevous_callback_t callback) 131234861Sadrian{ 132234861Sadrian struct rendevous_entry *e; 133234861Sadrian 134234861Sadrian KASSERT(callback != NULL, ("callback must be set")); 135234861Sadrian SLIST_FOREACH(e, &rendevoushead, entries) { 136234861Sadrian if (strcmp(name, e->target.name) == 0) { 137234861Sadrian /* the target is already attached */ 138234861Sadrian e->proxy.name = device_get_nameunit(dev); 139234861Sadrian e->proxy.device = dev; 140234861Sadrian e->proxy.callback = callback; 141234861Sadrian return (rendevous_attach(e, &e->proxy)); 142234861Sadrian } 143234861Sadrian } 144234861Sadrian e = malloc(sizeof(*e), M_MIIPROXY, M_WAITOK | M_ZERO); 145234861Sadrian e->proxy.name = device_get_nameunit(dev); 146234861Sadrian e->proxy.device = dev; 147234861Sadrian e->proxy.callback = callback; 148234861Sadrian e->target.name = name; 149234861Sadrian SLIST_INSERT_HEAD(&rendevoushead, e, entries); 150234861Sadrian return (ENXIO); 151234861Sadrian} 152234861Sadrian 153234861Sadrian/* 154234861Sadrian * Create an entry in the rendevous list for the target. 155234861Sadrian * Returns ENXIO if the proxy has not yet registered. 156234861Sadrian */ 157234861Sadrianstatic int 158234861Sadrianrendevous_register_target(device_t dev, rendevous_callback_t callback) 159234861Sadrian{ 160234861Sadrian struct rendevous_entry *e; 161234861Sadrian const char *name; 162234861Sadrian 163234861Sadrian KASSERT(callback != NULL, ("callback must be set")); 164234861Sadrian name = device_get_nameunit(dev); 165234861Sadrian SLIST_FOREACH(e, &rendevoushead, entries) { 166234861Sadrian if (strcmp(name, e->target.name) == 0) { 167234861Sadrian e->target.device = dev; 168234861Sadrian e->target.callback = callback; 169234861Sadrian return (rendevous_attach(e, &e->target)); 170234861Sadrian } 171234861Sadrian } 172234861Sadrian e = malloc(sizeof(*e), M_MIIPROXY, M_WAITOK | M_ZERO); 173234861Sadrian e->target.name = name; 174234861Sadrian e->target.device = dev; 175234861Sadrian e->target.callback = callback; 176234861Sadrian SLIST_INSERT_HEAD(&rendevoushead, e, entries); 177234861Sadrian return (ENXIO); 178234861Sadrian} 179234861Sadrian 180234861Sadrian/* 181234861Sadrian * Remove the registration for the proxy. 182234861Sadrian */ 183234861Sadrianstatic int 184234861Sadrianrendevous_unregister_proxy(device_t dev) 185234861Sadrian{ 186234861Sadrian struct rendevous_entry *e; 187234861Sadrian int error = 0; 188234861Sadrian 189234861Sadrian SLIST_FOREACH(e, &rendevoushead, entries) { 190234861Sadrian if (e->proxy.device == dev) { 191234861Sadrian if (e->target.device == NULL) { 192234861Sadrian SLIST_REMOVE(&rendevoushead, e, rendevous_entry, entries); 193234861Sadrian free(e, M_MIIPROXY); 194234861Sadrian return (0); 195234861Sadrian } else { 196234861Sadrian e->proxy.callback(RENDEVOUS_DETACH, e); 197234861Sadrian e->target.callback(RENDEVOUS_DETACH, e); 198234861Sadrian } 199234861Sadrian e->proxy.device = NULL; 200234861Sadrian e->proxy.callback = NULL; 201234861Sadrian return (error); 202234861Sadrian } 203234861Sadrian } 204234861Sadrian return (ENOENT); 205234861Sadrian} 206234861Sadrian 207234861Sadrian/* 208234861Sadrian * Remove the registration for the target. 209234861Sadrian */ 210234861Sadrianstatic int 211234861Sadrianrendevous_unregister_target(device_t dev) 212234861Sadrian{ 213234861Sadrian struct rendevous_entry *e; 214234861Sadrian int error = 0; 215234861Sadrian 216234861Sadrian SLIST_FOREACH(e, &rendevoushead, entries) { 217234861Sadrian if (e->target.device == dev) { 218234861Sadrian if (e->proxy.device == NULL) { 219234861Sadrian SLIST_REMOVE(&rendevoushead, e, rendevous_entry, entries); 220234861Sadrian free(e, M_MIIPROXY); 221234861Sadrian return (0); 222234861Sadrian } else { 223234861Sadrian e->proxy.callback(RENDEVOUS_DETACH, e); 224234861Sadrian e->target.callback(RENDEVOUS_DETACH, e); 225234861Sadrian } 226234861Sadrian e->target.device = NULL; 227234861Sadrian e->target.callback = NULL; 228234861Sadrian return (error); 229234861Sadrian } 230234861Sadrian } 231234861Sadrian return (ENOENT); 232234861Sadrian} 233234861Sadrian 234234861Sadrian/* 235234861Sadrian * Functions of the proxy that is interposed between the ethernet interface 236234861Sadrian * driver and the miibus device. 237234861Sadrian */ 238234861Sadrian 239234861Sadrianstatic int 240234861Sadrianmiiproxy_rendevous_callback(enum rendevous_op op, struct rendevous_entry *rendevous) 241234861Sadrian{ 242234861Sadrian struct miiproxy_softc *sc = device_get_softc(rendevous->proxy.device); 243234861Sadrian 244234861Sadrian switch (op) { 245234861Sadrian case RENDEVOUS_ATTACH: 246234861Sadrian sc->mdio = device_get_parent(rendevous->target.device); 247234861Sadrian break; 248234861Sadrian case RENDEVOUS_DETACH: 249234861Sadrian sc->mdio = NULL; 250234861Sadrian break; 251234861Sadrian } 252234861Sadrian return (0); 253234861Sadrian} 254234861Sadrian 255234861Sadrianstatic int 256234861Sadrianmiiproxy_probe(device_t dev) 257234861Sadrian{ 258234861Sadrian device_set_desc(dev, "MII/MDIO proxy, MII side"); 259234861Sadrian 260234861Sadrian return (BUS_PROBE_SPECIFIC); 261234861Sadrian} 262234861Sadrian 263234861Sadrianstatic int 264234861Sadrianmiiproxy_attach(device_t dev) 265234861Sadrian{ 266234861Sadrian 267234861Sadrian /* 268234861Sadrian * The ethernet interface needs to call mii_attach_proxy() to pass 269234861Sadrian * the relevant parameters for rendevous with the MDIO target. 270234861Sadrian */ 271234861Sadrian return (bus_generic_attach(dev)); 272234861Sadrian} 273234861Sadrian 274234861Sadrianstatic int 275234861Sadrianmiiproxy_detach(device_t dev) 276234861Sadrian{ 277234861Sadrian 278234861Sadrian rendevous_unregister_proxy(dev); 279234861Sadrian bus_generic_detach(dev); 280234861Sadrian return (0); 281234861Sadrian} 282234861Sadrian 283234861Sadrianstatic int 284234861Sadrianmiiproxy_readreg(device_t dev, int phy, int reg) 285234861Sadrian{ 286234861Sadrian struct miiproxy_softc *sc = device_get_softc(dev); 287234861Sadrian 288234861Sadrian if (sc->mdio != NULL) 289234861Sadrian return (MDIO_READREG(sc->mdio, phy, reg)); 290234861Sadrian return (-1); 291234861Sadrian} 292234861Sadrian 293234861Sadrianstatic int 294234861Sadrianmiiproxy_writereg(device_t dev, int phy, int reg, int val) 295234861Sadrian{ 296234861Sadrian struct miiproxy_softc *sc = device_get_softc(dev); 297234861Sadrian 298234861Sadrian if (sc->mdio != NULL) 299234861Sadrian return (MDIO_WRITEREG(sc->mdio, phy, reg, val)); 300234861Sadrian return (-1); 301234861Sadrian} 302234861Sadrian 303234861Sadrianstatic void 304234861Sadrianmiiproxy_statchg(device_t dev) 305234861Sadrian{ 306234861Sadrian 307234861Sadrian MIIBUS_STATCHG(device_get_parent(dev)); 308234861Sadrian} 309234861Sadrian 310234861Sadrianstatic void 311234861Sadrianmiiproxy_linkchg(device_t dev) 312234861Sadrian{ 313234861Sadrian 314234861Sadrian MIIBUS_LINKCHG(device_get_parent(dev)); 315234861Sadrian} 316234861Sadrian 317234861Sadrianstatic void 318234861Sadrianmiiproxy_mediainit(device_t dev) 319234861Sadrian{ 320234861Sadrian 321234861Sadrian MIIBUS_MEDIAINIT(device_get_parent(dev)); 322234861Sadrian} 323234861Sadrian 324234861Sadrian/* 325234861Sadrian * Functions for the MDIO target device driver. 326234861Sadrian */ 327234861Sadrianstatic int 328234861Sadrianmdioproxy_rendevous_callback(enum rendevous_op op, struct rendevous_entry *rendevous) 329234861Sadrian{ 330234861Sadrian return (0); 331234861Sadrian} 332234861Sadrian 333234861Sadrianstatic void 334234861Sadrianmdioproxy_identify(driver_t *driver, device_t parent) 335234861Sadrian{ 336234861Sadrian device_t child; 337234861Sadrian 338234861Sadrian if (device_find_child(parent, driver->name, -1) == NULL) { 339234861Sadrian child = BUS_ADD_CHILD(parent, 0, driver->name, -1); 340234861Sadrian } 341234861Sadrian} 342234861Sadrian 343234861Sadrianstatic int 344234861Sadrianmdioproxy_probe(device_t dev) 345234861Sadrian{ 346234861Sadrian device_set_desc(dev, "MII/MDIO proxy, MDIO side"); 347234861Sadrian 348234861Sadrian return (BUS_PROBE_SPECIFIC); 349234861Sadrian} 350234861Sadrian 351234861Sadrianstatic int 352234861Sadrianmdioproxy_attach(device_t dev) 353234861Sadrian{ 354234861Sadrian 355234861Sadrian rendevous_register_target(dev, mdioproxy_rendevous_callback); 356234861Sadrian return (bus_generic_attach(dev)); 357234861Sadrian} 358234861Sadrian 359234861Sadrianstatic int 360234861Sadrianmdioproxy_detach(device_t dev) 361234861Sadrian{ 362234861Sadrian 363234861Sadrian rendevous_unregister_target(dev); 364234861Sadrian bus_generic_detach(dev); 365234861Sadrian return (0); 366234861Sadrian} 367234861Sadrian 368234861Sadrian/* 369234861Sadrian * Attach this proxy in place of miibus. The target MDIO must be attached 370234861Sadrian * already. Returns NULL on error. 371234861Sadrian */ 372234861Sadriandevice_t 373234861Sadrianmii_attach_proxy(device_t dev) 374234861Sadrian{ 375234861Sadrian struct miiproxy_softc *sc; 376234861Sadrian int error; 377234861Sadrian const char *name; 378234861Sadrian device_t miiproxy; 379234861Sadrian 380234861Sadrian if (resource_string_value(device_get_name(dev), 381234861Sadrian device_get_unit(dev), "mdio", &name) != 0) { 382234861Sadrian if (bootverbose) 383234861Sadrian printf("mii_attach_proxy: not attaching, no mdio" 384234861Sadrian " device hint for %s\n", device_get_nameunit(dev)); 385234861Sadrian return (NULL); 386234861Sadrian } 387234861Sadrian 388234861Sadrian miiproxy = device_add_child(dev, miiproxy_driver.name, -1); 389234861Sadrian error = bus_generic_attach(dev); 390234861Sadrian if (error != 0) { 391234861Sadrian device_printf(dev, "can't attach miiproxy\n"); 392234861Sadrian return (NULL); 393234861Sadrian } 394234861Sadrian sc = device_get_softc(miiproxy); 395234861Sadrian sc->parent = dev; 396234861Sadrian sc->proxy = miiproxy; 397234861Sadrian if (rendevous_register_proxy(miiproxy, name, miiproxy_rendevous_callback) != 0) { 398234861Sadrian device_printf(dev, "can't attach proxy\n"); 399234861Sadrian return (NULL); 400234861Sadrian } 401234861Sadrian device_printf(miiproxy, "attached to target %s\n", device_get_nameunit(sc->mdio)); 402234861Sadrian return (miiproxy); 403234861Sadrian} 404234861Sadrian 405234861Sadrianstatic device_method_t miiproxy_methods[] = { 406234861Sadrian /* device interface */ 407234861Sadrian DEVMETHOD(device_probe, miiproxy_probe), 408234861Sadrian DEVMETHOD(device_attach, miiproxy_attach), 409234861Sadrian DEVMETHOD(device_detach, miiproxy_detach), 410234861Sadrian DEVMETHOD(device_shutdown, bus_generic_shutdown), 411234861Sadrian 412234861Sadrian /* MII interface */ 413234861Sadrian DEVMETHOD(miibus_readreg, miiproxy_readreg), 414234861Sadrian DEVMETHOD(miibus_writereg, miiproxy_writereg), 415234861Sadrian DEVMETHOD(miibus_statchg, miiproxy_statchg), 416234861Sadrian DEVMETHOD(miibus_linkchg, miiproxy_linkchg), 417234861Sadrian DEVMETHOD(miibus_mediainit, miiproxy_mediainit), 418234861Sadrian 419234861Sadrian DEVMETHOD_END 420234861Sadrian}; 421234861Sadrian 422234861Sadrianstatic device_method_t mdioproxy_methods[] = { 423234861Sadrian /* device interface */ 424234861Sadrian DEVMETHOD(device_identify, mdioproxy_identify), 425234861Sadrian DEVMETHOD(device_probe, mdioproxy_probe), 426234861Sadrian DEVMETHOD(device_attach, mdioproxy_attach), 427234861Sadrian DEVMETHOD(device_detach, mdioproxy_detach), 428234861Sadrian DEVMETHOD(device_shutdown, bus_generic_shutdown), 429234861Sadrian 430234861Sadrian DEVMETHOD_END 431234861Sadrian}; 432234861Sadrian 433234861SadrianDEFINE_CLASS_0(miiproxy, miiproxy_driver, miiproxy_methods, 434234861Sadrian sizeof(struct miiproxy_softc)); 435234861SadrianDEFINE_CLASS_0(mdioproxy, mdioproxy_driver, mdioproxy_methods, 436234861Sadrian sizeof(struct mdioproxy_softc)); 437234861Sadrian 438234861Sadriandevclass_t miiproxy_devclass; 439234861Sadrianstatic devclass_t mdioproxy_devclass; 440234861Sadrian 441234861SadrianDRIVER_MODULE(mdioproxy, mdio, mdioproxy_driver, mdioproxy_devclass, 0, 0); 442234861SadrianDRIVER_MODULE(miibus, miiproxy, miibus_driver, miibus_devclass, 0, 0); 443234861SadrianMODULE_VERSION(miiproxy, 1); 444234861SadrianMODULE_DEPEND(miiproxy, miibus, 1, 1, 1); 445