Back to home page

OSCL-LXR

 
 

    


0001 /*
0002 
0003     mii.c: MII interface library
0004 
0005     Maintained by Jeff Garzik <jgarzik@pobox.com>
0006     Copyright 2001,2002 Jeff Garzik
0007 
0008     Various code came from myson803.c and other files by
0009     Donald Becker.  Copyright:
0010 
0011         Written 1998-2002 by Donald Becker.
0012 
0013         This software may be used and distributed according
0014         to the terms of the GNU General Public License (GPL),
0015         incorporated herein by reference.  Drivers based on
0016         or derived from this code fall under the GPL and must
0017         retain the authorship, copyright and license notice.
0018         This file is not a complete program and may only be
0019         used when the entire operating system is licensed
0020         under the GPL.
0021 
0022         The author may be reached as becker@scyld.com, or C/O
0023         Scyld Computing Corporation
0024         410 Severn Ave., Suite 210
0025         Annapolis MD 21403
0026 
0027 
0028  */
0029 
0030 #include <linux/kernel.h>
0031 #include <linux/module.h>
0032 #include <linux/netdevice.h>
0033 #include <linux/ethtool.h>
0034 #include <linux/mii.h>
0035 
0036 static u32 mii_get_an(struct mii_if_info *mii, u16 addr)
0037 {
0038     int advert;
0039 
0040     advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
0041 
0042     return mii_lpa_to_ethtool_lpa_t(advert);
0043 }
0044 
0045 /**
0046  * mii_ethtool_gset - get settings that are specified in @ecmd
0047  * @mii: MII interface
0048  * @ecmd: requested ethtool_cmd
0049  *
0050  * The @ecmd parameter is expected to have been cleared before calling
0051  * mii_ethtool_gset().
0052  */
0053 void mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
0054 {
0055     struct net_device *dev = mii->dev;
0056     u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
0057     u32 nego;
0058 
0059     ecmd->supported =
0060         (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
0061          SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
0062          SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
0063     if (mii->supports_gmii)
0064         ecmd->supported |= SUPPORTED_1000baseT_Half |
0065             SUPPORTED_1000baseT_Full;
0066 
0067     /* only supports twisted-pair */
0068     ecmd->port = PORT_MII;
0069 
0070     /* only supports internal transceiver */
0071     ecmd->transceiver = XCVR_INTERNAL;
0072 
0073     /* this isn't fully supported at higher layers */
0074     ecmd->phy_address = mii->phy_id;
0075     ecmd->mdio_support = ETH_MDIO_SUPPORTS_C22;
0076 
0077     ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
0078 
0079     bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
0080     bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
0081     if (mii->supports_gmii) {
0082         ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
0083         stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
0084     }
0085 
0086     ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE);
0087     if (mii->supports_gmii)
0088         ecmd->advertising |=
0089             mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
0090 
0091     if (bmcr & BMCR_ANENABLE) {
0092         ecmd->advertising |= ADVERTISED_Autoneg;
0093         ecmd->autoneg = AUTONEG_ENABLE;
0094 
0095         if (bmsr & BMSR_ANEGCOMPLETE) {
0096             ecmd->lp_advertising = mii_get_an(mii, MII_LPA);
0097             ecmd->lp_advertising |=
0098                     mii_stat1000_to_ethtool_lpa_t(stat1000);
0099         } else {
0100             ecmd->lp_advertising = 0;
0101         }
0102 
0103         nego = ecmd->advertising & ecmd->lp_advertising;
0104 
0105         if (nego & (ADVERTISED_1000baseT_Full |
0106                 ADVERTISED_1000baseT_Half)) {
0107             ethtool_cmd_speed_set(ecmd, SPEED_1000);
0108             ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full);
0109         } else if (nego & (ADVERTISED_100baseT_Full |
0110                    ADVERTISED_100baseT_Half)) {
0111             ethtool_cmd_speed_set(ecmd, SPEED_100);
0112             ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
0113         } else {
0114             ethtool_cmd_speed_set(ecmd, SPEED_10);
0115             ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full);
0116         }
0117     } else {
0118         ecmd->autoneg = AUTONEG_DISABLE;
0119 
0120         ethtool_cmd_speed_set(ecmd,
0121                       ((bmcr & BMCR_SPEED1000 &&
0122                     (bmcr & BMCR_SPEED100) == 0) ?
0123                        SPEED_1000 :
0124                        ((bmcr & BMCR_SPEED100) ?
0125                     SPEED_100 : SPEED_10)));
0126         ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
0127     }
0128 
0129     mii->full_duplex = ecmd->duplex;
0130 
0131     /* ignore maxtxpkt, maxrxpkt for now */
0132 }
0133 
0134 /**
0135  * mii_ethtool_get_link_ksettings - get settings that are specified in @cmd
0136  * @mii: MII interface
0137  * @cmd: requested ethtool_link_ksettings
0138  *
0139  * The @cmd parameter is expected to have been cleared before calling
0140  * mii_ethtool_get_link_ksettings().
0141  */
0142 void mii_ethtool_get_link_ksettings(struct mii_if_info *mii,
0143                     struct ethtool_link_ksettings *cmd)
0144 {
0145     struct net_device *dev = mii->dev;
0146     u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
0147     u32 nego, supported, advertising, lp_advertising;
0148 
0149     supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
0150              SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
0151              SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
0152     if (mii->supports_gmii)
0153         supported |= SUPPORTED_1000baseT_Half |
0154             SUPPORTED_1000baseT_Full;
0155 
0156     /* only supports twisted-pair */
0157     cmd->base.port = PORT_MII;
0158 
0159     /* this isn't fully supported at higher layers */
0160     cmd->base.phy_address = mii->phy_id;
0161     cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22;
0162 
0163     advertising = ADVERTISED_TP | ADVERTISED_MII;
0164 
0165     bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
0166     bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
0167     if (mii->supports_gmii) {
0168         ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
0169         stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
0170     }
0171 
0172     advertising |= mii_get_an(mii, MII_ADVERTISE);
0173     if (mii->supports_gmii)
0174         advertising |= mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
0175 
0176     if (bmcr & BMCR_ANENABLE) {
0177         advertising |= ADVERTISED_Autoneg;
0178         cmd->base.autoneg = AUTONEG_ENABLE;
0179 
0180         if (bmsr & BMSR_ANEGCOMPLETE) {
0181             lp_advertising = mii_get_an(mii, MII_LPA);
0182             lp_advertising |=
0183                     mii_stat1000_to_ethtool_lpa_t(stat1000);
0184         } else {
0185             lp_advertising = 0;
0186         }
0187 
0188         nego = advertising & lp_advertising;
0189 
0190         if (nego & (ADVERTISED_1000baseT_Full |
0191                 ADVERTISED_1000baseT_Half)) {
0192             cmd->base.speed = SPEED_1000;
0193             cmd->base.duplex = !!(nego & ADVERTISED_1000baseT_Full);
0194         } else if (nego & (ADVERTISED_100baseT_Full |
0195                    ADVERTISED_100baseT_Half)) {
0196             cmd->base.speed = SPEED_100;
0197             cmd->base.duplex = !!(nego & ADVERTISED_100baseT_Full);
0198         } else {
0199             cmd->base.speed = SPEED_10;
0200             cmd->base.duplex = !!(nego & ADVERTISED_10baseT_Full);
0201         }
0202     } else {
0203         cmd->base.autoneg = AUTONEG_DISABLE;
0204 
0205         cmd->base.speed = ((bmcr & BMCR_SPEED1000 &&
0206                     (bmcr & BMCR_SPEED100) == 0) ?
0207                    SPEED_1000 :
0208                    ((bmcr & BMCR_SPEED100) ?
0209                     SPEED_100 : SPEED_10));
0210         cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ?
0211             DUPLEX_FULL : DUPLEX_HALF;
0212 
0213         lp_advertising = 0;
0214     }
0215 
0216     mii->full_duplex = cmd->base.duplex;
0217 
0218     ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
0219                         supported);
0220     ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
0221                         advertising);
0222     ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
0223                         lp_advertising);
0224 
0225     /* ignore maxtxpkt, maxrxpkt for now */
0226 }
0227 
0228 /**
0229  * mii_ethtool_sset - set settings that are specified in @ecmd
0230  * @mii: MII interface
0231  * @ecmd: requested ethtool_cmd
0232  *
0233  * Returns 0 for success, negative on error.
0234  */
0235 int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
0236 {
0237     struct net_device *dev = mii->dev;
0238     u32 speed = ethtool_cmd_speed(ecmd);
0239 
0240     if (speed != SPEED_10 &&
0241         speed != SPEED_100 &&
0242         speed != SPEED_1000)
0243         return -EINVAL;
0244     if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
0245         return -EINVAL;
0246     if (ecmd->port != PORT_MII)
0247         return -EINVAL;
0248     if (ecmd->transceiver != XCVR_INTERNAL)
0249         return -EINVAL;
0250     if (ecmd->phy_address != mii->phy_id)
0251         return -EINVAL;
0252     if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
0253         return -EINVAL;
0254     if ((speed == SPEED_1000) && (!mii->supports_gmii))
0255         return -EINVAL;
0256 
0257     /* ignore supported, maxtxpkt, maxrxpkt */
0258 
0259     if (ecmd->autoneg == AUTONEG_ENABLE) {
0260         u32 bmcr, advert, tmp;
0261         u32 advert2 = 0, tmp2 = 0;
0262 
0263         if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
0264                       ADVERTISED_10baseT_Full |
0265                       ADVERTISED_100baseT_Half |
0266                       ADVERTISED_100baseT_Full |
0267                       ADVERTISED_1000baseT_Half |
0268                       ADVERTISED_1000baseT_Full)) == 0)
0269             return -EINVAL;
0270 
0271         /* advertise only what has been requested */
0272         advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
0273         tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
0274         if (mii->supports_gmii) {
0275             advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
0276             tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
0277         }
0278         tmp |= ethtool_adv_to_mii_adv_t(ecmd->advertising);
0279 
0280         if (mii->supports_gmii)
0281             tmp2 |=
0282                   ethtool_adv_to_mii_ctrl1000_t(ecmd->advertising);
0283         if (advert != tmp) {
0284             mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
0285             mii->advertising = tmp;
0286         }
0287         if ((mii->supports_gmii) && (advert2 != tmp2))
0288             mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
0289 
0290         /* turn on autonegotiation, and force a renegotiate */
0291         bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
0292         bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
0293         mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
0294 
0295         mii->force_media = 0;
0296     } else {
0297         u32 bmcr, tmp;
0298 
0299         /* turn off auto negotiation, set speed and duplexity */
0300         bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
0301         tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
0302                    BMCR_SPEED1000 | BMCR_FULLDPLX);
0303         if (speed == SPEED_1000)
0304             tmp |= BMCR_SPEED1000;
0305         else if (speed == SPEED_100)
0306             tmp |= BMCR_SPEED100;
0307         if (ecmd->duplex == DUPLEX_FULL) {
0308             tmp |= BMCR_FULLDPLX;
0309             mii->full_duplex = 1;
0310         } else
0311             mii->full_duplex = 0;
0312         if (bmcr != tmp)
0313             mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
0314 
0315         mii->force_media = 1;
0316     }
0317     return 0;
0318 }
0319 
0320 /**
0321  * mii_ethtool_set_link_ksettings - set settings that are specified in @cmd
0322  * @mii: MII interfaces
0323  * @cmd: requested ethtool_link_ksettings
0324  *
0325  * Returns 0 for success, negative on error.
0326  */
0327 int mii_ethtool_set_link_ksettings(struct mii_if_info *mii,
0328                    const struct ethtool_link_ksettings *cmd)
0329 {
0330     struct net_device *dev = mii->dev;
0331     u32 speed = cmd->base.speed;
0332 
0333     if (speed != SPEED_10 &&
0334         speed != SPEED_100 &&
0335         speed != SPEED_1000)
0336         return -EINVAL;
0337     if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL)
0338         return -EINVAL;
0339     if (cmd->base.port != PORT_MII)
0340         return -EINVAL;
0341     if (cmd->base.phy_address != mii->phy_id)
0342         return -EINVAL;
0343     if (cmd->base.autoneg != AUTONEG_DISABLE &&
0344         cmd->base.autoneg != AUTONEG_ENABLE)
0345         return -EINVAL;
0346     if ((speed == SPEED_1000) && (!mii->supports_gmii))
0347         return -EINVAL;
0348 
0349     /* ignore supported, maxtxpkt, maxrxpkt */
0350 
0351     if (cmd->base.autoneg == AUTONEG_ENABLE) {
0352         u32 bmcr, advert, tmp;
0353         u32 advert2 = 0, tmp2 = 0;
0354         u32 advertising;
0355 
0356         ethtool_convert_link_mode_to_legacy_u32(
0357             &advertising, cmd->link_modes.advertising);
0358 
0359         if ((advertising & (ADVERTISED_10baseT_Half |
0360                     ADVERTISED_10baseT_Full |
0361                     ADVERTISED_100baseT_Half |
0362                     ADVERTISED_100baseT_Full |
0363                     ADVERTISED_1000baseT_Half |
0364                     ADVERTISED_1000baseT_Full)) == 0)
0365             return -EINVAL;
0366 
0367         /* advertise only what has been requested */
0368         advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
0369         tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
0370         if (mii->supports_gmii) {
0371             advert2 = mii->mdio_read(dev, mii->phy_id,
0372                          MII_CTRL1000);
0373             tmp2 = advert2 &
0374                 ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
0375         }
0376         tmp |= ethtool_adv_to_mii_adv_t(advertising);
0377 
0378         if (mii->supports_gmii)
0379             tmp2 |= ethtool_adv_to_mii_ctrl1000_t(advertising);
0380         if (advert != tmp) {
0381             mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
0382             mii->advertising = tmp;
0383         }
0384         if ((mii->supports_gmii) && (advert2 != tmp2))
0385             mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
0386 
0387         /* turn on autonegotiation, and force a renegotiate */
0388         bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
0389         bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
0390         mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
0391 
0392         mii->force_media = 0;
0393     } else {
0394         u32 bmcr, tmp;
0395 
0396         /* turn off auto negotiation, set speed and duplexity */
0397         bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
0398         tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
0399                    BMCR_SPEED1000 | BMCR_FULLDPLX);
0400         if (speed == SPEED_1000)
0401             tmp |= BMCR_SPEED1000;
0402         else if (speed == SPEED_100)
0403             tmp |= BMCR_SPEED100;
0404         if (cmd->base.duplex == DUPLEX_FULL) {
0405             tmp |= BMCR_FULLDPLX;
0406             mii->full_duplex = 1;
0407         } else {
0408             mii->full_duplex = 0;
0409         }
0410         if (bmcr != tmp)
0411             mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
0412 
0413         mii->force_media = 1;
0414     }
0415     return 0;
0416 }
0417 
0418 /**
0419  * mii_check_gmii_support - check if the MII supports Gb interfaces
0420  * @mii: the MII interface
0421  */
0422 int mii_check_gmii_support(struct mii_if_info *mii)
0423 {
0424     int reg;
0425 
0426     reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
0427     if (reg & BMSR_ESTATEN) {
0428         reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
0429         if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
0430             return 1;
0431     }
0432 
0433     return 0;
0434 }
0435 
0436 /**
0437  * mii_link_ok - is link status up/ok
0438  * @mii: the MII interface
0439  *
0440  * Returns 1 if the MII reports link status up/ok, 0 otherwise.
0441  */
0442 int mii_link_ok (struct mii_if_info *mii)
0443 {
0444     /* first, a dummy read, needed to latch some MII phys */
0445     mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
0446     if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
0447         return 1;
0448     return 0;
0449 }
0450 
0451 /**
0452  * mii_nway_restart - restart NWay (autonegotiation) for this interface
0453  * @mii: the MII interface
0454  *
0455  * Returns 0 on success, negative on error.
0456  */
0457 int mii_nway_restart (struct mii_if_info *mii)
0458 {
0459     int bmcr;
0460     int r = -EINVAL;
0461 
0462     /* if autoneg is off, it's an error */
0463     bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
0464 
0465     if (bmcr & BMCR_ANENABLE) {
0466         bmcr |= BMCR_ANRESTART;
0467         mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
0468         r = 0;
0469     }
0470 
0471     return r;
0472 }
0473 
0474 /**
0475  * mii_check_link - check MII link status
0476  * @mii: MII interface
0477  *
0478  * If the link status changed (previous != current), call
0479  * netif_carrier_on() if current link status is Up or call
0480  * netif_carrier_off() if current link status is Down.
0481  */
0482 void mii_check_link (struct mii_if_info *mii)
0483 {
0484     int cur_link = mii_link_ok(mii);
0485     int prev_link = netif_carrier_ok(mii->dev);
0486 
0487     if (cur_link && !prev_link)
0488         netif_carrier_on(mii->dev);
0489     else if (prev_link && !cur_link)
0490         netif_carrier_off(mii->dev);
0491 }
0492 
0493 /**
0494  * mii_check_media - check the MII interface for a carrier/speed/duplex change
0495  * @mii: the MII interface
0496  * @ok_to_print: OK to print link up/down messages
0497  * @init_media: OK to save duplex mode in @mii
0498  *
0499  * Returns 1 if the duplex mode changed, 0 if not.
0500  * If the media type is forced, always returns 0.
0501  */
0502 unsigned int mii_check_media (struct mii_if_info *mii,
0503                   unsigned int ok_to_print,
0504                   unsigned int init_media)
0505 {
0506     unsigned int old_carrier, new_carrier;
0507     int advertise, lpa, media, duplex;
0508     int lpa2 = 0;
0509 
0510     /* check current and old link status */
0511     old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
0512     new_carrier = (unsigned int) mii_link_ok(mii);
0513 
0514     /* if carrier state did not change, this is a "bounce",
0515      * just exit as everything is already set correctly
0516      */
0517     if ((!init_media) && (old_carrier == new_carrier))
0518         return 0; /* duplex did not change */
0519 
0520     /* no carrier, nothing much to do */
0521     if (!new_carrier) {
0522         netif_carrier_off(mii->dev);
0523         if (ok_to_print)
0524             netdev_info(mii->dev, "link down\n");
0525         return 0; /* duplex did not change */
0526     }
0527 
0528     /*
0529      * we have carrier, see who's on the other end
0530      */
0531     netif_carrier_on(mii->dev);
0532 
0533     if (mii->force_media) {
0534         if (ok_to_print)
0535             netdev_info(mii->dev, "link up\n");
0536         return 0; /* duplex did not change */
0537     }
0538 
0539     /* get MII advertise and LPA values */
0540     if ((!init_media) && (mii->advertising))
0541         advertise = mii->advertising;
0542     else {
0543         advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
0544         mii->advertising = advertise;
0545     }
0546     lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
0547     if (mii->supports_gmii)
0548         lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
0549 
0550     /* figure out media and duplex from advertise and LPA values */
0551     media = mii_nway_result(lpa & advertise);
0552     duplex = (media & ADVERTISE_FULL) ? 1 : 0;
0553     if (lpa2 & LPA_1000FULL)
0554         duplex = 1;
0555 
0556     if (ok_to_print)
0557         netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
0558                 lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
0559                 media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
0560                 100 : 10,
0561                 duplex ? "full" : "half",
0562                 lpa);
0563 
0564     if ((init_media) || (mii->full_duplex != duplex)) {
0565         mii->full_duplex = duplex;
0566         return 1; /* duplex changed */
0567     }
0568 
0569     return 0; /* duplex did not change */
0570 }
0571 
0572 /**
0573  * generic_mii_ioctl - main MII ioctl interface
0574  * @mii_if: the MII interface
0575  * @mii_data: MII ioctl data structure
0576  * @cmd: MII ioctl command
0577  * @duplex_chg_out: pointer to @duplex_changed status if there was no
0578  *  ioctl error
0579  *
0580  * Returns 0 on success, negative on error.
0581  */
0582 int generic_mii_ioctl(struct mii_if_info *mii_if,
0583               struct mii_ioctl_data *mii_data, int cmd,
0584               unsigned int *duplex_chg_out)
0585 {
0586     int rc = 0;
0587     unsigned int duplex_changed = 0;
0588 
0589     if (duplex_chg_out)
0590         *duplex_chg_out = 0;
0591 
0592     mii_data->phy_id &= mii_if->phy_id_mask;
0593     mii_data->reg_num &= mii_if->reg_num_mask;
0594 
0595     switch(cmd) {
0596     case SIOCGMIIPHY:
0597         mii_data->phy_id = mii_if->phy_id;
0598         fallthrough;
0599 
0600     case SIOCGMIIREG:
0601         mii_data->val_out =
0602             mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
0603                       mii_data->reg_num);
0604         break;
0605 
0606     case SIOCSMIIREG: {
0607         u16 val = mii_data->val_in;
0608 
0609         if (mii_data->phy_id == mii_if->phy_id) {
0610             switch(mii_data->reg_num) {
0611             case MII_BMCR: {
0612                 unsigned int new_duplex = 0;
0613                 if (val & (BMCR_RESET|BMCR_ANENABLE))
0614                     mii_if->force_media = 0;
0615                 else
0616                     mii_if->force_media = 1;
0617                 if (mii_if->force_media &&
0618                     (val & BMCR_FULLDPLX))
0619                     new_duplex = 1;
0620                 if (mii_if->full_duplex != new_duplex) {
0621                     duplex_changed = 1;
0622                     mii_if->full_duplex = new_duplex;
0623                 }
0624                 break;
0625             }
0626             case MII_ADVERTISE:
0627                 mii_if->advertising = val;
0628                 break;
0629             default:
0630                 /* do nothing */
0631                 break;
0632             }
0633         }
0634 
0635         mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
0636                    mii_data->reg_num, val);
0637         break;
0638     }
0639 
0640     default:
0641         rc = -EOPNOTSUPP;
0642         break;
0643     }
0644 
0645     if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
0646         *duplex_chg_out = 1;
0647 
0648     return rc;
0649 }
0650 
0651 MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
0652 MODULE_DESCRIPTION ("MII hardware support library");
0653 MODULE_LICENSE("GPL");
0654 
0655 EXPORT_SYMBOL(mii_link_ok);
0656 EXPORT_SYMBOL(mii_nway_restart);
0657 EXPORT_SYMBOL(mii_ethtool_gset);
0658 EXPORT_SYMBOL(mii_ethtool_get_link_ksettings);
0659 EXPORT_SYMBOL(mii_ethtool_sset);
0660 EXPORT_SYMBOL(mii_ethtool_set_link_ksettings);
0661 EXPORT_SYMBOL(mii_check_link);
0662 EXPORT_SYMBOL(mii_check_media);
0663 EXPORT_SYMBOL(mii_check_gmii_support);
0664 EXPORT_SYMBOL(generic_mii_ioctl);
0665