diff --git a/src/libs/compat/freebsd_network/compat/dev/mii/mii.h b/src/libs/compat/freebsd_network/compat/dev/mii/mii.h index d9883b6c40..1d887d23ab 100644 --- a/src/libs/compat/freebsd_network/compat/dev/mii/mii.h +++ b/src/libs/compat/freebsd_network/compat/dev/mii/mii.h @@ -1,5 +1,5 @@ /* $NetBSD: mii.h,v 1.9 2001/05/31 03:07:14 thorpej Exp $ */ - + /*- * Copyright (c) 1997 Manuel Bouyer. All rights reserved. * @@ -14,11 +14,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Manuel Bouyer. - * 4. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES @@ -31,7 +26,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/mii/mii.h,v 1.5 2005/01/06 01:42:56 imp Exp $ + * $FreeBSD$ */ #ifndef _DEV_MII_MII_H_ @@ -52,7 +47,7 @@ #define MII_COMMAND_WRITE 0x01 #define MII_COMMAND_ACK 0x02 -#define MII_BMCR 0x00 /* Basic mode control register (rw) */ +#define MII_BMCR 0x00 /* Basic mode control register (rw) */ #define BMCR_RESET 0x8000 /* reset */ #define BMCR_LOOP 0x4000 /* loopback */ #define BMCR_SPEED0 0x2000 /* speed selection (LSB) */ @@ -87,6 +82,8 @@ #define BMSR_JABBER 0x0002 /* Jabber detected */ #define BMSR_EXTCAP 0x0001 /* Extended capability */ +#define BMSR_DEFCAPMASK 0xffffffff + /* * Note that the EXTSTAT bit indicates that there is extended status * info available in register 15, but 802.3 section 22.2.4.3 also @@ -131,6 +128,10 @@ #define ANAR_10_FD 0x0040 /* local device supports 10bT FD */ #define ANAR_10 0x0020 /* local device supports 10bT */ #define ANAR_CSMA 0x0001 /* protocol selector CSMA/CD */ +#define ANAR_PAUSE_NONE (0 << 10) +#define ANAR_PAUSE_SYM (1 << 10) +#define ANAR_PAUSE_ASYM (2 << 10) +#define ANAR_PAUSE_TOWARDS (3 << 10) #define ANAR_X_FD 0x0020 /* local device supports 1000BASE-X FD */ #define ANAR_X_HD 0x0040 /* local device supports 1000BASE-X HD */ @@ -151,6 +152,11 @@ #define ANLPAR_10_FD 0x0040 /* link partner supports 10bT FD */ #define ANLPAR_10 0x0020 /* link partner supports 10bT */ #define ANLPAR_CSMA 0x0001 /* protocol selector CSMA/CD */ +#define ANLPAR_PAUSE_MASK (3 << 10) +#define ANLPAR_PAUSE_NONE (0 << 10) +#define ANLPAR_PAUSE_SYM (1 << 10) +#define ANLPAR_PAUSE_ASYM (2 << 10) +#define ANLPAR_PAUSE_TOWARDS (3 << 10) #define ANLPAR_X_FD 0x0020 /* local device supports 1000BASE-X FD */ #define ANLPAR_X_HD 0x0040 /* local device supports 1000BASE-X HD */ diff --git a/src/libs/compat/freebsd_network/compat/dev/mii/miivar.h b/src/libs/compat/freebsd_network/compat/dev/mii/miivar.h index 9f716ef7d5..3fe6d678cb 100644 --- a/src/libs/compat/freebsd_network/compat/dev/mii/miivar.h +++ b/src/libs/compat/freebsd_network/compat/dev/mii/miivar.h @@ -16,13 +16,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -36,7 +29,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/mii/miivar.h,v 1.15.14.1 2006/06/24 06:14:24 oleg Exp $ + * $FreeBSD$ */ #ifndef _DEV_MII_MIIVAR_H_ @@ -109,7 +102,7 @@ typedef int (*mii_downcall_t)(struct mii_softc *, struct mii_data *, int); */ struct mii_softc { device_t mii_dev; /* generic device glue */ - + LIST_ENTRY(mii_softc) mii_list; /* entry on parent's PHY list */ int mii_phy; /* our MII address */ @@ -129,15 +122,25 @@ struct mii_softc { typedef struct mii_softc mii_softc_t; /* mii_flags */ -#define MIIF_INITDONE 0x0001 /* has been initialized (mii_data) */ -#define MIIF_NOISOLATE 0x0002 /* do not isolate the PHY */ -#define MIIF_NOLOOP 0x0004 /* no loopback capability */ -#define MIIF_AUTOTSLEEP 0x0010 /* use tsleep(), not callout() */ -#define MIIF_HAVEFIBER 0x0020 /* from parent: has fiber interface */ -#define MIIF_HAVE_GTCR 0x0040 /* has 100base-T2/1000base-T CR */ -#define MIIF_IS_1000X 0x0080 /* is a 1000BASE-X device */ -#define MIIF_DOPAUSE 0x0100 /* advertise PAUSE capability */ -#define MIIF_IS_HPNA 0x0200 /* is a HomePNA device */ +#define MIIF_INITDONE 0x00000001 /* has been initialized (mii_data) */ +#define MIIF_NOISOLATE 0x00000002 /* do not isolate the PHY */ +#define MIIF_NOLOOP 0x00000004 /* no loopback capability */ +#define MIIF_DOINGAUTO 0x00000008 /* doing autonegotiation (mii_softc) */ +#define MIIF_AUTOTSLEEP 0x00000010 /* use tsleep(), not callout() */ +#define MIIF_HAVEFIBER 0x00000020 /* from parent: has fiber interface */ +#define MIIF_HAVE_GTCR 0x00000040 /* has 100base-T2/1000base-T CR */ +#define MIIF_IS_1000X 0x00000080 /* is a 1000BASE-X device */ +#define MIIF_DOPAUSE 0x00000100 /* advertise PAUSE capability */ +#define MIIF_IS_HPNA 0x00000200 /* is a HomePNA device */ +#define MIIF_FORCEANEG 0x00000400 /* force auto-negotiation */ +#define MIIF_NOMANPAUSE 0x00100000 /* no manual PAUSE selection */ +#define MIIF_FORCEPAUSE 0x00200000 /* force PAUSE advertisment */ +#define MIIF_MACPRIV0 0x01000000 /* private to the MAC driver */ +#define MIIF_MACPRIV1 0x02000000 /* private to the MAC driver */ +#define MIIF_MACPRIV2 0x04000000 /* private to the MAC driver */ +#define MIIF_PHYPRIV0 0x10000000 /* private to the PHY driver */ +#define MIIF_PHYPRIV1 0x20000000 /* private to the PHY driver */ +#define MIIF_PHYPRIV2 0x40000000 /* private to the PHY driver */ /* Default mii_anegticks values */ #define MII_ANEGTICKS 5 @@ -145,6 +148,14 @@ typedef struct mii_softc mii_softc_t; #define MIIF_INHERIT_MASK (MIIF_NOISOLATE|MIIF_NOLOOP|MIIF_AUTOTSLEEP) +/* + * Special `locators' passed to mii_attach(). If one of these is not + * an `any' value, we look for *that* PHY and configure it. If both + * are not `any', that is an error, and mii_attach() will fail. + */ +#define MII_OFFSET_ANY -1 +#define MII_PHY_ANY -1 + /* * Used to attach a PHY to a parent. */ @@ -192,6 +203,7 @@ struct mii_media { #ifdef _KERNEL +#ifdef __HAIKU__ int __haiku_miibus_readreg(device_t dev, int phy, int reg); int __haiku_miibus_writereg(device_t dev, int phy, int reg, int data); void __haiku_miibus_statchg(device_t dev); @@ -213,12 +225,26 @@ void __haiku_miibus_mediainit(device_t dev); #define MIIBUS_MEDIAINIT(dev) \ __haiku_miibus_mediainit(dev) +#endif + #define PHY_READ(p, r) \ MIIBUS_READREG((p)->mii_dev, (p)->mii_phy, (r)) #define PHY_WRITE(p, r, v) \ MIIBUS_WRITEREG((p)->mii_dev, (p)->mii_phy, (r), (v)) +enum miibus_device_ivars { + MIIBUS_IVAR_FLAGS +}; + +/* + * Simplified accessors for miibus + */ +#define MIIBUS_ACCESSOR(var, ivar, type) \ + __BUS_ACCESSOR(miibus, var, MIIBUS, ivar, type) + +MIIBUS_ACCESSOR(flags, FLAGS, int) + extern devclass_t miibus_devclass; extern driver_t miibus_driver; @@ -226,6 +252,8 @@ int miibus_probe(device_t); int miibus_attach(device_t); int miibus_detach(device_t); +int mii_attach(device_t, device_t *, struct ifnet *, ifm_change_cb_t, + ifm_stat_cb_t, int, int, int, int); int mii_anar(int); void mii_down(struct mii_data *); int mii_mediachg(struct mii_data *); @@ -235,17 +263,23 @@ int mii_phy_probe(device_t, device_t *, ifm_change_cb_t, ifm_stat_cb_t); void mii_add_media(struct mii_softc *); void mii_phy_add_media(struct mii_softc *); -int mii_media_from_bmcr(int); +#ifdef __HAIKU__ +int mii_media_from_bmcr(int); +#endif int mii_phy_auto(struct mii_softc *); int mii_phy_detach(device_t dev); void mii_phy_down(struct mii_softc *); +u_int mii_phy_flowstatus(struct mii_softc *); void mii_phy_reset(struct mii_softc *); void mii_phy_setmedia(struct mii_softc *sc); void mii_phy_update(struct mii_softc *, int); int mii_phy_tick(struct mii_softc *); -const struct mii_phydesc * mii_phy_match(const struct mii_attach_args *ma, const struct mii_phydesc *mpd); +const struct mii_phydesc * mii_phy_match(const struct mii_attach_args *ma, + const struct mii_phydesc *mpd); +const struct mii_phydesc * mii_phy_match_gen(const struct mii_attach_args *ma, + const struct mii_phydesc *mpd, size_t endlen); int mii_phy_dev_probe(device_t dev, const struct mii_phydesc *mpd, int mrv); void ukphy_status(struct mii_softc *); diff --git a/src/libs/compat/freebsd_network/fbsd_mii.c b/src/libs/compat/freebsd_network/fbsd_mii.c index cca08c0fa0..a7e3389701 100644 --- a/src/libs/compat/freebsd_network/fbsd_mii.c +++ b/src/libs/compat/freebsd_network/fbsd_mii.c @@ -16,13 +16,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -36,10 +29,15 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ +#ifdef __HAIKU__ #include "device.h" +#define malloc(size, tag, flags) kernel_malloc(size, tag, flags) +#define free(pointer, tag) kernel_free(pointer, tag) +#endif + #include -__FBSDID("$FreeBSD: src/sys/dev/mii/mii.c,v 1.26.2.1 2006/03/17 20:17:43 glebius Exp $"); +__FBSDID("$FreeBSD$"); /* * MII bus layer, glues MII-capable network interface drivers to sharable @@ -65,6 +63,9 @@ MODULE_VERSION(miibus, 1); #include "miibus_if.h" +static int miibus_print_child(device_t dev, device_t child); +static int miibus_read_ivar(device_t dev, device_t child, int which, + uintptr_t *result); static int miibus_child_location_str(device_t bus, device_t child, char *buf, size_t buflen); static int miibus_child_pnpinfo_str(device_t bus, device_t child, char *buf, @@ -83,7 +84,8 @@ static device_method_t miibus_methods[] = { DEVMETHOD(device_shutdown, bus_generic_shutdown), /* bus interface */ - DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_print_child, miibus_print_child), + DEVMETHOD(bus_read_ivar, miibus_read_ivar), DEVMETHOD(bus_driver_added, bus_generic_driver_added), DEVMETHOD(bus_child_pnpinfo_str, miibus_child_pnpinfo_str), DEVMETHOD(bus_child_location_str, miibus_child_location_str), @@ -106,84 +108,53 @@ driver_t miibus_driver = { sizeof(struct mii_data) }; -/* - * Helper function used by network interface drivers, attaches PHYs - * to the network interface driver parent. - */ +struct miibus_ivars { + struct ifnet *ifp; + ifm_change_cb_t ifmedia_upd; + ifm_stat_cb_t ifmedia_sts; + int mii_flags; +}; + int miibus_probe(device_t dev) { - struct mii_attach_args ma, *args; - struct mii_data *mii; - device_t child = NULL, parent; - int bmsr, capmask = 0xFFFFFFFF; - - mii = device_get_softc(dev); - parent = device_get_parent(dev); - LIST_INIT(&mii->mii_phys); - - for (ma.mii_phyno = 0; ma.mii_phyno < MII_NPHY; ma.mii_phyno++) { - /* - * Check to see if there is a PHY at this address. Note, - * many braindead PHYs report 0/0 in their ID registers, - * so we test for media in the BMSR. - */ - bmsr = MIIBUS_READREG(parent, ma.mii_phyno, MII_BMSR); - if (bmsr == 0 || bmsr == 0xffff || - (bmsr & (BMSR_EXTSTAT|BMSR_MEDIAMASK)) == 0) { - /* Assume no PHY at this address. */ - continue; - } - - /* - * Extract the IDs. Braindead PHYs will be handled by - * the `ukphy' driver, as we have no ID information to - * match on. - */ - ma.mii_id1 = MIIBUS_READREG(parent, ma.mii_phyno, - MII_PHYIDR1); - ma.mii_id2 = MIIBUS_READREG(parent, ma.mii_phyno, - MII_PHYIDR2); - - ma.mii_data = mii; - ma.mii_capmask = capmask; - - args = kernel_malloc(sizeof(struct mii_attach_args), - M_DEVBUF, M_NOWAIT); - bcopy((char *)&ma, (char *)args, sizeof(ma)); - child = device_add_child(dev, NULL, -1); - device_set_ivars(child, args); - } - - if (child == NULL) - return(ENXIO); device_set_desc(dev, "MII bus"); - return(0); + return (BUS_PROBE_SPECIFIC); } int miibus_attach(device_t dev) { - void **v; - ifm_change_cb_t ifmedia_upd; - ifm_stat_cb_t ifmedia_sts; + struct miibus_ivars *ivars; + struct mii_attach_args *ma; struct mii_data *mii; + device_t *children; + int i, nchildren; mii = device_get_softc(dev); - /* - * Note that each NIC's softc must start with an ifnet pointer. - * XXX: EVIL HACK! - */ - mii->mii_ifp = *(struct ifnet**)device_get_softc(device_get_parent(dev)); - v = device_get_ivars(dev); - ifmedia_upd = v[0]; - ifmedia_sts = v[1]; - ifmedia_init(&mii->mii_media, IFM_IMASK, ifmedia_upd, ifmedia_sts); - bus_generic_attach(dev); + nchildren = 0; + if (device_get_children(dev, &children, &nchildren) == 0) { + for (i = 0; i < nchildren; i++) { + ma = device_get_ivars(children[i]); + ma->mii_data = mii; + } + free(children, M_TEMP); + } + if (nchildren == 0) { + device_printf(dev, "cannot get children\n"); + return (ENXIO); + } + ivars = device_get_ivars(dev); + ifmedia_init(&mii->mii_media, IFM_IMASK, ivars->ifmedia_upd, + ivars->ifmedia_sts); + mii->mii_ifp = ivars->ifp; + mii->mii_ifp->if_capabilities |= IFCAP_LINKSTATE; + mii->mii_ifp->if_capenable |= IFCAP_LINKSTATE; + LIST_INIT(&mii->mii_phys); - return(0); + return (bus_generic_attach(dev)); } int @@ -196,26 +167,65 @@ miibus_detach(device_t dev) ifmedia_removeall(&mii->mii_media); mii->mii_ifp = NULL; - return(0); -} - -static int -miibus_child_pnpinfo_str(device_t bus, device_t child, char *buf, - size_t buflen) -{ - struct mii_attach_args *maa = device_get_ivars(child); - snprintf(buf, buflen, "oui=0x%x model=0x%x rev=0x%x", - MII_OUI(maa->mii_id1, maa->mii_id2), - MII_MODEL(maa->mii_id2), MII_REV(maa->mii_id2)); return (0); } static int -miibus_child_location_str(device_t bus, device_t child, char *buf, +miibus_print_child(device_t dev, device_t child) +{ + struct mii_attach_args *ma; + int retval; + + ma = device_get_ivars(child); + retval = bus_print_child_header(dev, child); + retval += printf(" PHY %d", ma->mii_phyno); + retval += bus_print_child_footer(dev, child); + + return (retval); +} + +static int +miibus_read_ivar(device_t dev, device_t child __unused, int which, + uintptr_t *result) +{ + struct miibus_ivars *ivars; + + /* + * NB: this uses the instance variables of the miibus rather than + * its PHY children. + */ + ivars = device_get_ivars(dev); + switch (which) { + case MIIBUS_IVAR_FLAGS: + *result = ivars->mii_flags; + break; + default: + return (ENOENT); + } + return (0); +} + +static int +miibus_child_pnpinfo_str(device_t bus __unused, device_t child, char *buf, size_t buflen) { - struct mii_attach_args *maa = device_get_ivars(child); - snprintf(buf, buflen, "phyno=%d", maa->mii_phyno); + struct mii_attach_args *ma; + + ma = device_get_ivars(child); + snprintf(buf, buflen, "oui=0x%x model=0x%x rev=0x%x", + MII_OUI(ma->mii_id1, ma->mii_id2), + MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2)); + return (0); +} + +static int +miibus_child_location_str(device_t bus __unused, device_t child, char *buf, + size_t buflen) +{ + struct mii_attach_args *ma; + + ma = device_get_ivars(child); + snprintf(buf, buflen, "phyno=%d", ma->mii_phyno); return (0); } @@ -225,7 +235,7 @@ miibus_readreg(device_t dev, int phy, int reg) device_t parent; parent = device_get_parent(dev); - return(MIIBUS_READREG(parent, phy, reg)); + return (MIIBUS_READREG(parent, phy, reg)); } static int @@ -234,7 +244,7 @@ miibus_writereg(device_t dev, int phy, int reg, int data) device_t parent; parent = device_get_parent(dev); - return(MIIBUS_WRITEREG(parent, phy, reg, data)); + return (MIIBUS_WRITEREG(parent, phy, reg, data)); } static void @@ -255,7 +265,6 @@ miibus_statchg(device_t dev) */ ifp = *(struct ifnet **)device_get_softc(parent); ifp->if_baudrate = ifmedia_baudrate(mii->mii_media_active); - return; } static void @@ -277,7 +286,6 @@ miibus_linkchg(device_t dev) link_state = LINK_STATE_DOWN; } else link_state = LINK_STATE_UNKNOWN; - /* * Note that each NIC's softc must start with an ifnet pointer. * XXX: EVIL HACK! @@ -298,50 +306,187 @@ miibus_mediainit(device_t dev) mii = device_get_softc(dev); LIST_FOREACH(m, &mii->mii_media.ifm_list, ifm_list) { media = m->ifm_media; - if (media == (IFM_ETHER|IFM_AUTO)) + if (media == (IFM_ETHER | IFM_AUTO)) break; } ifmedia_set(&mii->mii_media, media); +} - return; +/* + * Helper function used by network interface drivers, attaches the miibus and + * the PHYs to the network interface driver parent. + */ +int +mii_attach(device_t dev, device_t *miibus, struct ifnet *ifp, + ifm_change_cb_t ifmedia_upd, ifm_stat_cb_t ifmedia_sts, int capmask, + int phyloc, int offloc, int flags) +{ + struct miibus_ivars *ivars; + struct mii_attach_args ma, *args; + device_t *children, phy; + int bmsr, first, i, nchildren, offset, phymax, phymin, rv; + + if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY) { + printf("%s: phyloc and offloc specified\n", __func__); + return (EINVAL); + } + + if (offloc != MII_OFFSET_ANY && (offloc < 0 || offloc >= MII_NPHY)) { + printf("%s: ivalid offloc %d\n", __func__, offloc); + return (EINVAL); + } + + if (phyloc == MII_PHY_ANY) { + phymin = 0; + phymax = MII_NPHY - 1; + } else { + if (phyloc < 0 || phyloc >= MII_NPHY) { + printf("%s: ivalid phyloc %d\n", __func__, phyloc); + return (EINVAL); + } + phymin = phymax = phyloc; + } + + first = 0; + if (*miibus == NULL) { + first = 1; + ivars = malloc(sizeof(*ivars), M_DEVBUF, M_NOWAIT); + if (ivars == NULL) + return (ENOMEM); + ivars->ifp = ifp; + ivars->ifmedia_upd = ifmedia_upd; + ivars->ifmedia_sts = ifmedia_sts; + ivars->mii_flags = flags; + *miibus = device_add_child(dev, "miibus", -1); + if (*miibus == NULL) { + rv = ENXIO; + goto fail; + } + device_set_ivars(*miibus, ivars); + } else { + ivars = device_get_ivars(*miibus); + if (ivars->ifp != ifp || ivars->ifmedia_upd != ifmedia_upd || + ivars->ifmedia_sts != ifmedia_sts || + ivars->mii_flags != flags) { + printf("%s: non-matching invariant\n", __func__); + return (EINVAL); + } + /* + * Assignment of the attach arguments mii_data for the first + * pass is done in miibus_attach(), i.e. once the miibus softc + * has been allocated. + */ + ma.mii_data = device_get_softc(*miibus); + } + + ma.mii_capmask = capmask; + + phy = NULL; + offset = 0; + for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) { + /* + * Make sure we haven't already configured a PHY at this + * address. This allows mii_attach() to be called + * multiple times. + */ + if (device_get_children(*miibus, &children, &nchildren) == 0) { + for (i = 0; i < nchildren; i++) { + args = device_get_ivars(children[i]); + if (args->mii_phyno == ma.mii_phyno) { + /* + * Yes, there is already something + * configured at this address. + */ + free(children, M_TEMP); + goto skip; + } + } + free(children, M_TEMP); + } + + /* + * Check to see if there is a PHY at this address. Note, + * many braindead PHYs report 0/0 in their ID registers, + * so we test for media in the BMSR. + */ + bmsr = MIIBUS_READREG(dev, ma.mii_phyno, MII_BMSR); + if (bmsr == 0 || bmsr == 0xffff || + (bmsr & (BMSR_EXTSTAT | BMSR_MEDIAMASK)) == 0) { + /* Assume no PHY at this address. */ + continue; + } + + /* + * There is a PHY at this address. If we were given an + * `offset' locator, skip this PHY if it doesn't match. + */ + if (offloc != MII_OFFSET_ANY && offloc != offset) + goto skip; + + /* + * Extract the IDs. Braindead PHYs will be handled by + * the `ukphy' driver, as we have no ID information to + * match on. + */ + ma.mii_id1 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR1); + ma.mii_id2 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR2); + + args = malloc(sizeof(struct mii_attach_args), M_DEVBUF, + M_NOWAIT); + if (args == NULL) + goto skip; + bcopy((char *)&ma, (char *)args, sizeof(ma)); + phy = device_add_child(*miibus, NULL, -1); + if (phy == NULL) { + free(args, M_DEVBUF); + goto skip; + } + device_set_ivars(phy, args); + skip: + offset++; + } + + if (first != 0) { + if (phy == NULL) { + rv = ENXIO; + goto fail; + } + rv = bus_generic_attach(dev); + if (rv != 0) + goto fail; + + /* Attaching of the PHY drivers is done in miibus_attach(). */ + return (0); + } + rv = bus_generic_attach(*miibus); + if (rv != 0) + goto fail; + + return (0); + + fail: + if (*miibus != NULL) + device_delete_child(dev, *miibus); + free(ivars, M_DEVBUF); + if (first != 0) + *miibus = NULL; + return (rv); } int mii_phy_probe(device_t dev, device_t *child, ifm_change_cb_t ifmedia_upd, ifm_stat_cb_t ifmedia_sts) { - void **v; - int bmsr, i; + struct ifnet *ifp; - v = kernel_malloc(sizeof(vm_offset_t) * 2, M_DEVBUF, M_NOWAIT); - if (v == 0) { - return (ENOMEM); - } - v[0] = ifmedia_upd; - v[1] = ifmedia_sts; - *child = device_add_child(dev, "miibus", -1); - device_set_ivars(*child, v); - - for (i = 0; i < MII_NPHY; i++) { - bmsr = MIIBUS_READREG(dev, i, MII_BMSR); - if (bmsr == 0 || bmsr == 0xffff || - (bmsr & (BMSR_EXTSTAT|BMSR_MEDIAMASK)) == 0) { - /* Assume no PHY at this address. */ - continue; - } else - break; - } - - if (i == MII_NPHY) { - device_delete_child(dev, *child); - *child = NULL; - return(ENXIO); - } - - bus_generic_attach(dev); - - return(0); + /* + * Note that each NIC's softc must start with an ifnet pointer. + * XXX: EVIL HACK! + */ + ifp = *(struct ifnet **)device_get_softc(dev); + return (mii_attach(dev, child, ifp, ifmedia_upd, ifmedia_sts, + BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0)); } /* @@ -351,12 +496,28 @@ int mii_mediachg(struct mii_data *mii) { struct mii_softc *child; + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; int rv; mii->mii_media_status = 0; mii->mii_media_active = IFM_NONE; LIST_FOREACH(child, &mii->mii_phys, mii_list) { + /* + * If the media indicates a different PHY instance, + * isolate this one. + */ + if (IFM_INST(ife->ifm_media) != child->mii_inst) { + if ((child->mii_flags & MIIF_NOISOLATE) != 0) { + device_printf(child->mii_dev, "%s: " + "can't handle non-zero PHY instance %d\n", + __func__, child->mii_inst); + continue; + } + PHY_WRITE(child, MII_BMCR, PHY_READ(child, MII_BMCR) | + BMCR_ISO); + continue; + } rv = (*child->mii_service)(child, mii, MII_MEDIACHG); if (rv) return (rv); @@ -371,9 +532,17 @@ void mii_tick(struct mii_data *mii) { struct mii_softc *child; + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; - LIST_FOREACH(child, &mii->mii_phys, mii_list) - (void) (*child->mii_service)(child, mii, MII_TICK); + LIST_FOREACH(child, &mii->mii_phys, mii_list) { + /* + * If this PHY instance isn't currently selected, just skip + * it. + */ + if (IFM_INST(ife->ifm_media) != child->mii_inst) + continue; + (void)(*child->mii_service)(child, mii, MII_TICK); + } } /* @@ -383,12 +552,19 @@ void mii_pollstat(struct mii_data *mii) { struct mii_softc *child; + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; mii->mii_media_status = 0; mii->mii_media_active = IFM_NONE; - LIST_FOREACH(child, &mii->mii_phys, mii_list) - (void) (*child->mii_service)(child, mii, MII_POLLSTAT); + LIST_FOREACH(child, &mii->mii_phys, mii_list) { + /* + * If we're not polling this PHY instance, just skip it. + */ + if (IFM_INST(ife->ifm_media) != child->mii_inst) + continue; + (void)(*child->mii_service)(child, mii, MII_POLLSTAT); + } } /*