mii.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 351 行

C
351
字号
/*	mii.c: MII interface library	Maintained by Jeff Garzik <jgarzik@pobox.com>	Copyright 2001,2002 Jeff Garzik	Various code came from myson803.c and other files by	Donald Becker.  Copyright:		Written 1998-2002 by Donald Becker.		This software may be used and distributed according		to the terms of the GNU General Public License (GPL),		incorporated herein by reference.  Drivers based on		or derived from this code fall under the GPL and must		retain the authorship, copyright and license notice.		This file is not a complete program and may only be		used when the entire operating system is licensed		under the GPL.		The author may be reached as becker@scyld.com, or C/O		Scyld Computing Corporation		410 Severn Ave., Suite 210		Annapolis MD 21403 */#include <linux/kernel.h>#include <linux/module.h>#include <linux/netdevice.h>#include <linux/ethtool.h>#include <linux/mii.h>int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd){	struct net_device *dev = mii->dev;	u32 advert, bmcr, lpa, nego;	ecmd->supported =	    (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |	     SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |	     SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);	/* only supports twisted-pair */	ecmd->port = PORT_MII;	/* only supports internal transceiver */	ecmd->transceiver = XCVR_INTERNAL;	/* this isn't fully supported at higher layers */	ecmd->phy_address = mii->phy_id;	ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;	advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);	if (advert & ADVERTISE_10HALF)		ecmd->advertising |= ADVERTISED_10baseT_Half;	if (advert & ADVERTISE_10FULL)		ecmd->advertising |= ADVERTISED_10baseT_Full;	if (advert & ADVERTISE_100HALF)		ecmd->advertising |= ADVERTISED_100baseT_Half;	if (advert & ADVERTISE_100FULL)		ecmd->advertising |= ADVERTISED_100baseT_Full;	bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);	lpa = mii->mdio_read(dev, mii->phy_id, MII_LPA);	if (bmcr & BMCR_ANENABLE) {		ecmd->advertising |= ADVERTISED_Autoneg;		ecmd->autoneg = AUTONEG_ENABLE;				nego = mii_nway_result(advert & lpa);		if (nego == LPA_100FULL || nego == LPA_100HALF)			ecmd->speed = SPEED_100;		else			ecmd->speed = SPEED_10;		if (nego == LPA_100FULL || nego == LPA_10FULL) {			ecmd->duplex = DUPLEX_FULL;			mii->full_duplex = 1;		} else {			ecmd->duplex = DUPLEX_HALF;			mii->full_duplex = 0;		}	} else {		ecmd->autoneg = AUTONEG_DISABLE;		ecmd->speed = (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10;		ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;	}	/* ignore maxtxpkt, maxrxpkt for now */	return 0;}int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd){	struct net_device *dev = mii->dev;	if (ecmd->speed != SPEED_10 && ecmd->speed != SPEED_100)		return -EINVAL;	if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)		return -EINVAL;	if (ecmd->port != PORT_MII)		return -EINVAL;	if (ecmd->transceiver != XCVR_INTERNAL)		return -EINVAL;	if (ecmd->phy_address != mii->phy_id)		return -EINVAL;	if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)		return -EINVAL;				  	/* ignore supported, maxtxpkt, maxrxpkt */		if (ecmd->autoneg == AUTONEG_ENABLE) {		u32 bmcr, advert, tmp;		if ((ecmd->advertising & (ADVERTISED_10baseT_Half |					  ADVERTISED_10baseT_Full |					  ADVERTISED_100baseT_Half |					  ADVERTISED_100baseT_Full)) == 0)			return -EINVAL;		/* advertise only what has been requested */		advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);		tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);		if (ecmd->advertising & ADVERTISED_10baseT_Half)			tmp |= ADVERTISE_10HALF;		if (ecmd->advertising & ADVERTISED_10baseT_Full)			tmp |= ADVERTISE_10FULL;		if (ecmd->advertising & ADVERTISED_100baseT_Half)			tmp |= ADVERTISE_100HALF;		if (ecmd->advertising & ADVERTISED_100baseT_Full)			tmp |= ADVERTISE_100FULL;		if (advert != tmp) {			mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);			mii->advertising = tmp;		}				/* turn on autonegotiation, and force a renegotiate */		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);		bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);		mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);		mii->force_media = 0;	} else {		u32 bmcr, tmp;		/* turn off auto negotiation, set speed and duplexity */		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);		tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX);		if (ecmd->speed == SPEED_100)			tmp |= BMCR_SPEED100;		if (ecmd->duplex == DUPLEX_FULL) {			tmp |= BMCR_FULLDPLX;			mii->full_duplex = 1;		} else			mii->full_duplex = 0;		if (bmcr != tmp)			mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);		mii->force_media = 1;	}	return 0;}int mii_link_ok (struct mii_if_info *mii){	/* first, a dummy read, needed to latch some MII phys */	mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);	if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)		return 1;	return 0;}int mii_nway_restart (struct mii_if_info *mii){	int bmcr;	int r = -EINVAL;	/* if autoneg is off, it's an error */	bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);	if (bmcr & BMCR_ANENABLE) {		bmcr |= BMCR_ANRESTART;		mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);		r = 0;	}	return r;}void mii_check_link (struct mii_if_info *mii){	int cur_link = mii_link_ok(mii);	int prev_link = netif_carrier_ok(mii->dev);	if (cur_link && !prev_link)		netif_carrier_on(mii->dev);	else if (prev_link && !cur_link)		netif_carrier_off(mii->dev);}unsigned int mii_check_media (struct mii_if_info *mii,			      unsigned int ok_to_print,			      unsigned int init_media){	unsigned int old_carrier, new_carrier;	int advertise, lpa, media, duplex;	/* if forced media, go no further */	if (mii->force_media)		return 0; /* duplex did not change */	/* check current and old link status */	old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;	new_carrier = (unsigned int) mii_link_ok(mii);	/* if carrier state did not change, this is a "bounce",	 * just exit as everything is already set correctly	 */	if ((!init_media) && (old_carrier == new_carrier))		return 0; /* duplex did not change */	/* no carrier, nothing much to do */	if (!new_carrier) {		netif_carrier_off(mii->dev);		if (ok_to_print)			printk(KERN_INFO "%s: link down\n", mii->dev->name);		return 0; /* duplex did not change */	}	/*	 * we have carrier, see who's on the other end	 */	netif_carrier_on(mii->dev);	/* get MII advertise and LPA values */	if ((!init_media) && (mii->advertising))		advertise = mii->advertising;	else {		advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);		mii->advertising = advertise;	}	lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);	/* figure out media and duplex from advertise and LPA values */	media = mii_nway_result(lpa & advertise);	duplex = (media & ADVERTISE_FULL) ? 1 : 0;	if (ok_to_print)		printk(KERN_INFO "%s: link up, %sMbps, %s-duplex, lpa 0x%04X\n",		       mii->dev->name,		       media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?		       		"100" : "10",		       duplex ? "full" : "half",		       lpa);	if ((init_media) || (mii->full_duplex != duplex)) {		mii->full_duplex = duplex;		return 1; /* duplex changed */	}	return 0; /* duplex did not change */}int generic_mii_ioctl(struct mii_if_info *mii_if,		      struct mii_ioctl_data *mii_data, int cmd,		      unsigned int *duplex_chg_out){	int rc = 0;	unsigned int duplex_changed = 0;	if (duplex_chg_out)		*duplex_chg_out = 0;	mii_data->phy_id &= mii_if->phy_id_mask;	mii_data->reg_num &= mii_if->reg_num_mask;	switch(cmd) {	case SIOCGMIIPHY:		mii_data->phy_id = mii_if->phy_id;		/* fall through */	case SIOCGMIIREG:		mii_data->val_out =			mii_if->mdio_read(mii_if->dev, mii_data->phy_id,					  mii_data->reg_num);		break;	case SIOCSMIIREG: {		u16 val = mii_data->val_in;		if (!capable(CAP_NET_ADMIN))			return -EPERM;		if (mii_data->phy_id == mii_if->phy_id) {			switch(mii_data->reg_num) {			case MII_BMCR: {				unsigned int new_duplex = 0;				if (val & (BMCR_RESET|BMCR_ANENABLE))					mii_if->force_media = 0;				else					mii_if->force_media = 1;				if (mii_if->force_media &&				    (val & BMCR_FULLDPLX))					new_duplex = 1;				if (mii_if->full_duplex != new_duplex) {					duplex_changed = 1;					mii_if->full_duplex = new_duplex;				}				break;			}			case MII_ADVERTISE:				mii_if->advertising = val;				break;			default:				/* do nothing */				break;			}		}		mii_if->mdio_write(mii_if->dev, mii_data->phy_id,				   mii_data->reg_num, val);		break;	}	default:		rc = -EOPNOTSUPP;		break;	}	if ((rc == 0) && (duplex_chg_out) && (duplex_changed))		*duplex_chg_out = 1;	return rc;}MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");MODULE_DESCRIPTION ("MII hardware support library");MODULE_LICENSE("GPL");EXPORT_SYMBOL(mii_link_ok);EXPORT_SYMBOL(mii_nway_restart);EXPORT_SYMBOL(mii_ethtool_gset);EXPORT_SYMBOL(mii_ethtool_sset);EXPORT_SYMBOL(mii_check_link);EXPORT_SYMBOL(mii_check_media);EXPORT_SYMBOL(generic_mii_ioctl);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?