2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2011-2012 Stefan Bethke.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
31 #include <sys/param.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>
41 #include <net/if_media.h>
43 #include <dev/etherswitch/miiproxy.h>
44 #include <dev/mii/mii.h>
45 #include <dev/mii/miivar.h>
48 #include "miibus_if.h"
51 MALLOC_DECLARE(M_MIIPROXY);
52 MALLOC_DEFINE(M_MIIPROXY, "miiproxy", "miiproxy data structures");
54 driver_t miiproxy_driver;
55 driver_t mdioproxy_driver;
57 struct miiproxy_softc {
63 struct mdioproxy_softc {
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.
73 struct rendezvous_entry;
80 typedef int (*rendezvous_callback_t)(enum rendezvous_op,
81 struct rendezvous_entry *);
83 static SLIST_HEAD(rendezvoushead, rendezvous_entry) rendezvoushead =
84 SLIST_HEAD_INITIALIZER(rendezvoushead);
86 struct rendezvous_endpoint {
89 rendezvous_callback_t callback;
92 struct rendezvous_entry {
93 SLIST_ENTRY(rendezvous_entry) entries;
94 struct rendezvous_endpoint proxy;
95 struct rendezvous_endpoint target;
99 * Call the callback routines for both the proxy and the target. If either
100 * returns an error, undo the attachment.
103 rendezvous_attach(struct rendezvous_entry *e, struct rendezvous_endpoint *ep)
107 error = e->proxy.callback(RENDEZVOUS_ATTACH, e);
109 error = e->target.callback(RENDEZVOUS_ATTACH, e);
111 e->proxy.callback(RENDEZVOUS_DETACH, e);
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
128 rendezvous_register_proxy(device_t dev, const char *name,
129 rendezvous_callback_t callback)
131 struct rendezvous_entry *e;
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));
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);
153 * Create an entry in the rendezvous list for the target.
154 * Returns ENXIO if the proxy has not yet registered.
157 rendezvous_register_target(device_t dev, rendezvous_callback_t callback)
159 struct rendezvous_entry *e;
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));
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);
180 * Remove the registration for the proxy.
183 rendezvous_unregister_proxy(device_t dev)
185 struct rendezvous_entry *e;
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);
195 e->proxy.callback(RENDEZVOUS_DETACH, e);
196 e->target.callback(RENDEZVOUS_DETACH, e);
198 e->proxy.device = NULL;
199 e->proxy.callback = NULL;
207 * Remove the registration for the target.
210 rendezvous_unregister_target(device_t dev)
212 struct rendezvous_entry *e;
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);
222 e->proxy.callback(RENDEZVOUS_DETACH, e);
223 e->target.callback(RENDEZVOUS_DETACH, e);
225 e->target.device = NULL;
226 e->target.callback = NULL;
234 * Functions of the proxy that is interposed between the ethernet interface
235 * driver and the miibus device.
239 miiproxy_rendezvous_callback(enum rendezvous_op op, struct rendezvous_entry *rendezvous)
241 struct miiproxy_softc *sc = device_get_softc(rendezvous->proxy.device);
244 case RENDEZVOUS_ATTACH:
245 sc->mdio = device_get_parent(rendezvous->target.device);
247 case RENDEZVOUS_DETACH:
255 miiproxy_probe(device_t dev)
257 device_set_desc(dev, "MII/MDIO proxy, MII side");
259 return (BUS_PROBE_SPECIFIC);
263 miiproxy_attach(device_t dev)
267 * The ethernet interface needs to call mii_attach_proxy() to pass
268 * the relevant parameters for rendezvous with the MDIO target.
270 return (bus_generic_attach(dev));
274 miiproxy_detach(device_t dev)
277 rendezvous_unregister_proxy(dev);
278 bus_generic_detach(dev);
283 miiproxy_readreg(device_t dev, int phy, int reg)
285 struct miiproxy_softc *sc = device_get_softc(dev);
287 if (sc->mdio != NULL)
288 return (MDIO_READREG(sc->mdio, phy, reg));
293 miiproxy_writereg(device_t dev, int phy, int reg, int val)
295 struct miiproxy_softc *sc = device_get_softc(dev);
297 if (sc->mdio != NULL)
298 return (MDIO_WRITEREG(sc->mdio, phy, reg, val));
303 miiproxy_statchg(device_t dev)
306 MIIBUS_STATCHG(device_get_parent(dev));
310 miiproxy_linkchg(device_t dev)
313 MIIBUS_LINKCHG(device_get_parent(dev));
317 miiproxy_mediainit(device_t dev)
320 MIIBUS_MEDIAINIT(device_get_parent(dev));
324 * Functions for the MDIO target device driver.
327 mdioproxy_rendezvous_callback(enum rendezvous_op op, struct rendezvous_entry *rendezvous)
333 mdioproxy_identify(driver_t *driver, device_t parent)
337 if (device_find_child(parent, driver->name, -1) == NULL) {
338 child = BUS_ADD_CHILD(parent, 0, driver->name, -1);
343 mdioproxy_probe(device_t dev)
345 device_set_desc(dev, "MII/MDIO proxy, MDIO side");
347 return (BUS_PROBE_SPECIFIC);
351 mdioproxy_attach(device_t dev)
354 rendezvous_register_target(dev, mdioproxy_rendezvous_callback);
355 return (bus_generic_attach(dev));
359 mdioproxy_detach(device_t dev)
362 rendezvous_unregister_target(dev);
363 bus_generic_detach(dev);
368 * Attach this proxy in place of miibus. The target MDIO must be attached
369 * already. Returns NULL on error.
372 mii_attach_proxy(device_t dev)
374 struct miiproxy_softc *sc;
379 if (resource_string_value(device_get_name(dev),
380 device_get_unit(dev), "mdio", &name) != 0) {
382 printf("mii_attach_proxy: not attaching, no mdio"
383 " device hint for %s\n", device_get_nameunit(dev));
387 miiproxy = device_add_child(dev, miiproxy_driver.name, -1);
388 error = bus_generic_attach(dev);
390 device_printf(dev, "can't attach miiproxy\n");
393 sc = device_get_softc(miiproxy);
395 sc->proxy = miiproxy;
396 if (rendezvous_register_proxy(miiproxy, name, miiproxy_rendezvous_callback) != 0) {
397 device_printf(dev, "can't attach proxy\n");
400 device_printf(miiproxy, "attached to target %s\n", device_get_nameunit(sc->mdio));
404 static device_method_t miiproxy_methods[] = {
405 /* device interface */
406 DEVMETHOD(device_probe, miiproxy_probe),
407 DEVMETHOD(device_attach, miiproxy_attach),
408 DEVMETHOD(device_detach, miiproxy_detach),
409 DEVMETHOD(device_shutdown, bus_generic_shutdown),
412 DEVMETHOD(miibus_readreg, miiproxy_readreg),
413 DEVMETHOD(miibus_writereg, miiproxy_writereg),
414 DEVMETHOD(miibus_statchg, miiproxy_statchg),
415 DEVMETHOD(miibus_linkchg, miiproxy_linkchg),
416 DEVMETHOD(miibus_mediainit, miiproxy_mediainit),
421 static device_method_t mdioproxy_methods[] = {
422 /* device interface */
423 DEVMETHOD(device_identify, mdioproxy_identify),
424 DEVMETHOD(device_probe, mdioproxy_probe),
425 DEVMETHOD(device_attach, mdioproxy_attach),
426 DEVMETHOD(device_detach, mdioproxy_detach),
427 DEVMETHOD(device_shutdown, bus_generic_shutdown),
432 DEFINE_CLASS_0(miiproxy, miiproxy_driver, miiproxy_methods,
433 sizeof(struct miiproxy_softc));
434 DEFINE_CLASS_0(mdioproxy, mdioproxy_driver, mdioproxy_methods,
435 sizeof(struct mdioproxy_softc));
437 devclass_t miiproxy_devclass;
438 static devclass_t mdioproxy_devclass;
440 DRIVER_MODULE(mdioproxy, mdio, mdioproxy_driver, mdioproxy_devclass, 0, 0);
441 DRIVER_MODULE(miibus, miiproxy, miibus_driver, miibus_devclass, 0, 0);
442 MODULE_VERSION(miiproxy, 1);
443 MODULE_DEPEND(miiproxy, miibus, 1, 1, 1);