]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/etherswitch/miiproxy.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / etherswitch / miiproxy.c
1 /*-
2  * Copyright (c) 2011-2012 Stefan Bethke.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/param.h>
30 #include <sys/bus.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>
36
37 #include <net/if.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>
43
44 #include <dev/etherswitch/miiproxy.h>
45 #include <dev/mii/mii.h>
46 #include <dev/mii/miivar.h>
47
48 #include "mdio_if.h"
49 #include "miibus_if.h"
50
51
52 MALLOC_DECLARE(M_MIIPROXY);
53 MALLOC_DEFINE(M_MIIPROXY, "miiproxy", "miiproxy data structures");
54
55 driver_t miiproxy_driver;
56 driver_t mdioproxy_driver;
57
58 struct miiproxy_softc {
59         device_t        parent;
60         device_t        proxy;
61         device_t        mdio;
62 };
63
64 struct mdioproxy_softc {
65 };
66
67 /*
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.
73  */
74 struct rendevous_entry;
75
76 enum rendevous_op {
77         RENDEVOUS_ATTACH,
78         RENDEVOUS_DETACH
79 };
80
81 typedef int (*rendevous_callback_t)(enum rendevous_op,
82     struct rendevous_entry *);
83
84 static SLIST_HEAD(rendevoushead, rendevous_entry) rendevoushead =
85     SLIST_HEAD_INITIALIZER(rendevoushead);
86
87 struct rendevous_endpoint {
88         device_t                device;
89         const char              *name;
90         rendevous_callback_t    callback;
91 };
92
93 struct rendevous_entry {
94         SLIST_ENTRY(rendevous_entry)    entries;
95         struct rendevous_endpoint       proxy;
96         struct rendevous_endpoint       target;
97 };
98
99 /*
100  * Call the callback routines for both the proxy and the target.  If either
101  * returns an error, undo the attachment.
102  */
103 static int
104 rendevous_attach(struct rendevous_entry *e, struct rendevous_endpoint *ep)
105 {
106         int error;
107
108         error = e->proxy.callback(RENDEVOUS_ATTACH, e);
109         if (error == 0) {
110                 error = e->target.callback(RENDEVOUS_ATTACH, e);
111                 if (error != 0) {
112                         e->proxy.callback(RENDEVOUS_DETACH, e);
113                         ep->device = NULL;
114                         ep->callback = NULL;
115                 }
116         }
117         return (error);
118 }
119
120 /*
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
126  * registered.
127  */
128 static int
129 rendevous_register_proxy(device_t dev, const char *name,
130     rendevous_callback_t callback)
131 {
132         struct rendevous_entry *e;
133
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));
142                 }
143         }
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);
150         return (ENXIO);
151 }
152
153 /*
154  * Create an entry in the rendevous list for the target.
155  * Returns ENXIO if the proxy has not yet registered.
156  */
157 static int
158 rendevous_register_target(device_t dev, rendevous_callback_t callback)
159 {
160         struct rendevous_entry *e;
161         const char *name;
162         
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));
170                 }
171         }
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);
177         return (ENXIO);
178 }
179
180 /*
181  * Remove the registration for the proxy.
182  */
183 static int
184 rendevous_unregister_proxy(device_t dev)
185 {
186         struct rendevous_entry *e;
187         int error = 0;
188         
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);
193                                 free(e, M_MIIPROXY);
194                                 return (0);
195                         } else {
196                                 e->proxy.callback(RENDEVOUS_DETACH, e);
197                                 e->target.callback(RENDEVOUS_DETACH, e);
198                         }
199                         e->proxy.device = NULL;
200                         e->proxy.callback = NULL;
201                         return (error);
202                 }
203         }
204         return (ENOENT);
205 }
206
207 /*
208  * Remove the registration for the target.
209  */
210 static int
211 rendevous_unregister_target(device_t dev)
212 {
213         struct rendevous_entry *e;
214         int error = 0;
215         
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);
220                                 free(e, M_MIIPROXY);
221                                 return (0);
222                         } else {
223                                 e->proxy.callback(RENDEVOUS_DETACH, e);
224                                 e->target.callback(RENDEVOUS_DETACH, e);
225                         }
226                         e->target.device = NULL;
227                         e->target.callback = NULL;
228                         return (error);
229                 }
230         }
231         return (ENOENT);
232 }
233
234 /*
235  * Functions of the proxy that is interposed between the ethernet interface
236  * driver and the miibus device.
237  */
238
239 static int
240 miiproxy_rendevous_callback(enum rendevous_op op, struct rendevous_entry *rendevous)
241 {
242         struct miiproxy_softc *sc = device_get_softc(rendevous->proxy.device);
243
244         switch (op) {
245         case RENDEVOUS_ATTACH:
246                 sc->mdio = device_get_parent(rendevous->target.device);
247                 break;
248         case RENDEVOUS_DETACH:
249                 sc->mdio = NULL;
250                 break;
251         }
252         return (0);
253 }
254
255 static int
256 miiproxy_probe(device_t dev)
257 {
258         device_set_desc(dev, "MII/MDIO proxy, MII side");
259
260         return (BUS_PROBE_SPECIFIC);
261 }
262
263 static int
264 miiproxy_attach(device_t dev)
265 {
266
267         /*
268          * The ethernet interface needs to call mii_attach_proxy() to pass
269          * the relevant parameters for rendevous with the MDIO target.
270          */
271         return (bus_generic_attach(dev));
272 }
273
274 static int
275 miiproxy_detach(device_t dev)
276 {
277
278         rendevous_unregister_proxy(dev);
279         bus_generic_detach(dev);
280         return (0);
281 }
282
283 static int
284 miiproxy_readreg(device_t dev, int phy, int reg)
285 {
286         struct miiproxy_softc *sc = device_get_softc(dev);
287
288         if (sc->mdio != NULL)
289                 return (MDIO_READREG(sc->mdio, phy, reg));
290         return (-1);
291 }
292
293 static int
294 miiproxy_writereg(device_t dev, int phy, int reg, int val)
295 {
296         struct miiproxy_softc *sc = device_get_softc(dev);
297
298         if (sc->mdio != NULL)
299                 return (MDIO_WRITEREG(sc->mdio, phy, reg, val));
300         return (-1);
301 }
302
303 static void
304 miiproxy_statchg(device_t dev)
305 {
306
307         MIIBUS_STATCHG(device_get_parent(dev));
308 }
309
310 static void
311 miiproxy_linkchg(device_t dev)
312 {
313
314         MIIBUS_LINKCHG(device_get_parent(dev));
315 }
316
317 static void
318 miiproxy_mediainit(device_t dev)
319 {
320
321         MIIBUS_MEDIAINIT(device_get_parent(dev));
322 }
323
324 /*
325  * Functions for the MDIO target device driver.
326  */
327 static int
328 mdioproxy_rendevous_callback(enum rendevous_op op, struct rendevous_entry *rendevous)
329 {
330         return (0);
331 }
332
333 static void
334 mdioproxy_identify(driver_t *driver, device_t parent)
335 {
336         device_t child;
337
338         if (device_find_child(parent, driver->name, -1) == NULL) {
339                 child = BUS_ADD_CHILD(parent, 0, driver->name, -1);
340         }
341 }
342
343 static int
344 mdioproxy_probe(device_t dev)
345 {
346         device_set_desc(dev, "MII/MDIO proxy, MDIO side");
347
348         return (BUS_PROBE_SPECIFIC);
349 }
350
351 static int
352 mdioproxy_attach(device_t dev)
353 {
354
355         rendevous_register_target(dev, mdioproxy_rendevous_callback);
356         return (bus_generic_attach(dev));
357 }
358
359 static int
360 mdioproxy_detach(device_t dev)
361 {
362
363         rendevous_unregister_target(dev);
364         bus_generic_detach(dev);
365         return (0);
366 }
367
368 /*
369  * Attach this proxy in place of miibus.  The target MDIO must be attached
370  * already.  Returns NULL on error.
371  */
372 device_t
373 mii_attach_proxy(device_t dev)
374 {
375         struct miiproxy_softc *sc;
376         int             error;
377         const char      *name;
378         device_t        miiproxy;
379         
380         if (resource_string_value(device_get_name(dev),
381             device_get_unit(dev), "mdio", &name) != 0) {
382                 if (bootverbose)
383                         printf("mii_attach_proxy: not attaching, no mdio"
384                             " device hint for %s\n", device_get_nameunit(dev));
385                 return (NULL);
386         }
387
388         miiproxy = device_add_child(dev, miiproxy_driver.name, -1);
389         error = bus_generic_attach(dev);
390         if (error != 0) {
391                 device_printf(dev, "can't attach miiproxy\n");
392                 return (NULL);
393         }
394         sc = device_get_softc(miiproxy);
395         sc->parent = dev;
396         sc->proxy = miiproxy;
397         if (rendevous_register_proxy(miiproxy, name, miiproxy_rendevous_callback) != 0) {
398                 device_printf(dev, "can't attach proxy\n");
399                 return (NULL);
400         }
401         device_printf(miiproxy, "attached to target %s\n", device_get_nameunit(sc->mdio));
402         return (miiproxy);
403 }
404
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),
411
412         /* MII interface */
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),
418
419         DEVMETHOD_END
420 };
421
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),
429
430         DEVMETHOD_END
431 };
432
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));
437
438 devclass_t miiproxy_devclass;
439 static devclass_t mdioproxy_devclass;
440
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);