2 * Copyright (c) 2011-2012 Stefan Bethke.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/param.h>
31 #include <sys/kernel.h>
32 #include <sys/module.h>
33 #include <sys/socket.h>
34 #include <sys/sockio.h>
35 #include <sys/systm.h>
38 #include <net/if_arp.h>
39 #include <net/ethernet.h>
40 #include <net/if_dl.h>
41 #include <net/if_media.h>
42 #include <net/if_types.h>
44 #include <dev/etherswitch/miiproxy.h>
45 #include <dev/mii/mii.h>
46 #include <dev/mii/miivar.h>
49 #include "miibus_if.h"
52 MALLOC_DECLARE(M_MIIPROXY);
53 MALLOC_DEFINE(M_MIIPROXY, "miiproxy", "miiproxy data structures");
55 driver_t miiproxy_driver;
56 driver_t mdioproxy_driver;
58 struct miiproxy_softc {
64 struct mdioproxy_softc {
68 * The rendevous data structures and functions allow two device endpoints to
69 * match up, so that the proxy endpoint can be associated with a target
70 * endpoint. The proxy has to know the device name of the target that it
71 * wants to associate with, for example through a hint. The rendevous code
72 * makes no assumptions about the devices that want to meet.
74 struct rendevous_entry;
81 typedef int (*rendevous_callback_t)(enum rendevous_op,
82 struct rendevous_entry *);
84 static SLIST_HEAD(rendevoushead, rendevous_entry) rendevoushead =
85 SLIST_HEAD_INITIALIZER(rendevoushead);
87 struct rendevous_endpoint {
90 rendevous_callback_t callback;
93 struct rendevous_entry {
94 SLIST_ENTRY(rendevous_entry) entries;
95 struct rendevous_endpoint proxy;
96 struct rendevous_endpoint target;
100 * Call the callback routines for both the proxy and the target. If either
101 * returns an error, undo the attachment.
104 rendevous_attach(struct rendevous_entry *e, struct rendevous_endpoint *ep)
108 error = e->proxy.callback(RENDEVOUS_ATTACH, e);
110 error = e->target.callback(RENDEVOUS_ATTACH, e);
112 e->proxy.callback(RENDEVOUS_DETACH, e);
121 * Create an entry for the proxy in the rendevous list. The name parameter
122 * indicates the name of the device that is the target endpoint for this
123 * rendevous. The callback will be invoked as soon as the target is
124 * registered: either immediately if the target registered itself earlier,
125 * or once the target registers. Returns ENXIO if the target has not yet
129 rendevous_register_proxy(device_t dev, const char *name,
130 rendevous_callback_t callback)
132 struct rendevous_entry *e;
134 KASSERT(callback != NULL, ("callback must be set"));
135 SLIST_FOREACH(e, &rendevoushead, entries) {
136 if (strcmp(name, e->target.name) == 0) {
137 /* the target is already attached */
138 e->proxy.name = device_get_nameunit(dev);
139 e->proxy.device = dev;
140 e->proxy.callback = callback;
141 return (rendevous_attach(e, &e->proxy));
144 e = malloc(sizeof(*e), M_MIIPROXY, M_WAITOK | M_ZERO);
145 e->proxy.name = device_get_nameunit(dev);
146 e->proxy.device = dev;
147 e->proxy.callback = callback;
148 e->target.name = name;
149 SLIST_INSERT_HEAD(&rendevoushead, e, entries);
154 * Create an entry in the rendevous list for the target.
155 * Returns ENXIO if the proxy has not yet registered.
158 rendevous_register_target(device_t dev, rendevous_callback_t callback)
160 struct rendevous_entry *e;
163 KASSERT(callback != NULL, ("callback must be set"));
164 name = device_get_nameunit(dev);
165 SLIST_FOREACH(e, &rendevoushead, entries) {
166 if (strcmp(name, e->target.name) == 0) {
167 e->target.device = dev;
168 e->target.callback = callback;
169 return (rendevous_attach(e, &e->target));
172 e = malloc(sizeof(*e), M_MIIPROXY, M_WAITOK | M_ZERO);
173 e->target.name = name;
174 e->target.device = dev;
175 e->target.callback = callback;
176 SLIST_INSERT_HEAD(&rendevoushead, e, entries);
181 * Remove the registration for the proxy.
184 rendevous_unregister_proxy(device_t dev)
186 struct rendevous_entry *e;
189 SLIST_FOREACH(e, &rendevoushead, entries) {
190 if (e->proxy.device == dev) {
191 if (e->target.device == NULL) {
192 SLIST_REMOVE(&rendevoushead, e, rendevous_entry, entries);
196 e->proxy.callback(RENDEVOUS_DETACH, e);
197 e->target.callback(RENDEVOUS_DETACH, e);
199 e->proxy.device = NULL;
200 e->proxy.callback = NULL;
208 * Remove the registration for the target.
211 rendevous_unregister_target(device_t dev)
213 struct rendevous_entry *e;
216 SLIST_FOREACH(e, &rendevoushead, entries) {
217 if (e->target.device == dev) {
218 if (e->proxy.device == NULL) {
219 SLIST_REMOVE(&rendevoushead, e, rendevous_entry, entries);
223 e->proxy.callback(RENDEVOUS_DETACH, e);
224 e->target.callback(RENDEVOUS_DETACH, e);
226 e->target.device = NULL;
227 e->target.callback = NULL;
235 * Functions of the proxy that is interposed between the ethernet interface
236 * driver and the miibus device.
240 miiproxy_rendevous_callback(enum rendevous_op op, struct rendevous_entry *rendevous)
242 struct miiproxy_softc *sc = device_get_softc(rendevous->proxy.device);
245 case RENDEVOUS_ATTACH:
246 sc->mdio = device_get_parent(rendevous->target.device);
248 case RENDEVOUS_DETACH:
256 miiproxy_probe(device_t dev)
258 device_set_desc(dev, "MII/MDIO proxy, MII side");
260 return (BUS_PROBE_SPECIFIC);
264 miiproxy_attach(device_t dev)
268 * The ethernet interface needs to call mii_attach_proxy() to pass
269 * the relevant parameters for rendevous with the MDIO target.
271 return (bus_generic_attach(dev));
275 miiproxy_detach(device_t dev)
278 rendevous_unregister_proxy(dev);
279 bus_generic_detach(dev);
284 miiproxy_readreg(device_t dev, int phy, int reg)
286 struct miiproxy_softc *sc = device_get_softc(dev);
288 if (sc->mdio != NULL)
289 return (MDIO_READREG(sc->mdio, phy, reg));
294 miiproxy_writereg(device_t dev, int phy, int reg, int val)
296 struct miiproxy_softc *sc = device_get_softc(dev);
298 if (sc->mdio != NULL)
299 return (MDIO_WRITEREG(sc->mdio, phy, reg, val));
304 miiproxy_statchg(device_t dev)
307 MIIBUS_STATCHG(device_get_parent(dev));
311 miiproxy_linkchg(device_t dev)
314 MIIBUS_LINKCHG(device_get_parent(dev));
318 miiproxy_mediainit(device_t dev)
321 MIIBUS_MEDIAINIT(device_get_parent(dev));
325 * Functions for the MDIO target device driver.
328 mdioproxy_rendevous_callback(enum rendevous_op op, struct rendevous_entry *rendevous)
334 mdioproxy_identify(driver_t *driver, device_t parent)
338 if (device_find_child(parent, driver->name, -1) == NULL) {
339 child = BUS_ADD_CHILD(parent, 0, driver->name, -1);
344 mdioproxy_probe(device_t dev)
346 device_set_desc(dev, "MII/MDIO proxy, MDIO side");
348 return (BUS_PROBE_SPECIFIC);
352 mdioproxy_attach(device_t dev)
355 rendevous_register_target(dev, mdioproxy_rendevous_callback);
356 return (bus_generic_attach(dev));
360 mdioproxy_detach(device_t dev)
363 rendevous_unregister_target(dev);
364 bus_generic_detach(dev);
369 * Attach this proxy in place of miibus. The target MDIO must be attached
370 * already. Returns NULL on error.
373 mii_attach_proxy(device_t dev)
375 struct miiproxy_softc *sc;
380 if (resource_string_value(device_get_name(dev),
381 device_get_unit(dev), "mdio", &name) != 0) {
383 printf("mii_attach_proxy: not attaching, no mdio"
384 " device hint for %s\n", device_get_nameunit(dev));
388 miiproxy = device_add_child(dev, miiproxy_driver.name, -1);
389 error = bus_generic_attach(dev);
391 device_printf(dev, "can't attach miiproxy\n");
394 sc = device_get_softc(miiproxy);
396 sc->proxy = miiproxy;
397 if (rendevous_register_proxy(miiproxy, name, miiproxy_rendevous_callback) != 0) {
398 device_printf(dev, "can't attach proxy\n");
401 device_printf(miiproxy, "attached to target %s\n", device_get_nameunit(sc->mdio));
405 static device_method_t miiproxy_methods[] = {
406 /* device interface */
407 DEVMETHOD(device_probe, miiproxy_probe),
408 DEVMETHOD(device_attach, miiproxy_attach),
409 DEVMETHOD(device_detach, miiproxy_detach),
410 DEVMETHOD(device_shutdown, bus_generic_shutdown),
413 DEVMETHOD(miibus_readreg, miiproxy_readreg),
414 DEVMETHOD(miibus_writereg, miiproxy_writereg),
415 DEVMETHOD(miibus_statchg, miiproxy_statchg),
416 DEVMETHOD(miibus_linkchg, miiproxy_linkchg),
417 DEVMETHOD(miibus_mediainit, miiproxy_mediainit),
422 static device_method_t mdioproxy_methods[] = {
423 /* device interface */
424 DEVMETHOD(device_identify, mdioproxy_identify),
425 DEVMETHOD(device_probe, mdioproxy_probe),
426 DEVMETHOD(device_attach, mdioproxy_attach),
427 DEVMETHOD(device_detach, mdioproxy_detach),
428 DEVMETHOD(device_shutdown, bus_generic_shutdown),
433 DEFINE_CLASS_0(miiproxy, miiproxy_driver, miiproxy_methods,
434 sizeof(struct miiproxy_softc));
435 DEFINE_CLASS_0(mdioproxy, mdioproxy_driver, mdioproxy_methods,
436 sizeof(struct mdioproxy_softc));
438 devclass_t miiproxy_devclass;
439 static devclass_t mdioproxy_devclass;
441 DRIVER_MODULE(mdioproxy, mdio, mdioproxy_driver, mdioproxy_devclass, 0, 0);
442 DRIVER_MODULE(miibus, miiproxy, miibus_driver, miibus_devclass, 0, 0);
443 MODULE_VERSION(miiproxy, 1);
444 MODULE_DEPEND(miiproxy, miibus, 1, 1, 1);