diff options
Diffstat (limited to 'cpukit/libnetworking/rtems/rtems_mii_ioctl_kern.c')
-rw-r--r-- | cpukit/libnetworking/rtems/rtems_mii_ioctl_kern.c | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/cpukit/libnetworking/rtems/rtems_mii_ioctl_kern.c b/cpukit/libnetworking/rtems/rtems_mii_ioctl_kern.c new file mode 100644 index 0000000000..cd763917be --- /dev/null +++ b/cpukit/libnetworking/rtems/rtems_mii_ioctl_kern.c @@ -0,0 +1,193 @@ +/* $Id$ */ + +/* Simple (default) implementation for SIOCGIFMEDIA/SIOCSIFMEDIA + * to be used by ethernet drivers [from their ioctl]. + * + * KERNEL PART (support for drivers) + * + * NOTE: This much simpler than the BSD ifmedia API + */ + +/* Author: Till Straumann, <straumanatslacdotstandorddotedu>, 2005 */ + +/* include first to avoid 'malloc' clash with rtems_bsdnet_malloc() hack */ + +#include <rtems.h> +#include <rtems/rtems_bsdnet.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <net/ethernet.h> +#include <net/if.h> + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif + +#include <rtems/rtems_mii_ioctl.h> + +#include <sys/errno.h> + + +#define DEBUG + + +#ifndef MII_1000TCR +#define MII_1000TCR MII_100T2CR +#endif + +#ifndef MII_1000TSR +#define MII_1000TSR MII_100T2SR +#endif + +int +rtems_mii_ioctl (struct rtems_mdio_info *info, void *uarg, int cmd, + int *media) +{ + unsigned32 bmcr, bmsr, bmcr2 = 0, bmsr2 = 0, anar, lpar; + int phy = IFM_INST (*media); + unsigned tmp; + int subtype = 0, options = 0; + + switch (cmd) { + default: + return EINVAL; + +#ifdef DEBUG + case 0: +#endif + case SIOCGIFMEDIA: + if (info->mdio_r (phy, uarg, MII_BMCR, &bmcr)) + return EINVAL; + if (info->mdio_r (phy, uarg, MII_BMSR, &bmsr)) + return EINVAL; + if (info->has_gmii) { + if (info->mdio_r (phy, uarg, MII_1000TCR, &bmcr2)) + return EINVAL; + if (info->mdio_r (phy, uarg, MII_1000TSR, &bmsr2)) + return EINVAL; + } + + /* link status */ + if (BMSR_LINK & bmsr) + options |= IFM_LINK_OK; + + /* do we have autonegotiation disabled ? */ + if (!(BMCR_AUTOEN & bmcr)) { + options |= IFM_ANEG_DIS; + + /* duplex is enforced */ + options |= BMCR_FDX & bmcr ? IFM_FDX : IFM_HDX; + + /* determine speed */ + switch (BMCR_SPEED (bmcr)) { + case BMCR_S10: + subtype = IFM_10_T; + break; + case BMCR_S100: + subtype = IFM_100_TX; + break; + case BMCR_S1000: + subtype = IFM_1000_T; + break; + default: + return ENOTSUP; /* ?? */ + } + } else if (!(BMSR_LINK & bmsr) || !(BMSR_ACOMP & bmsr)) { + subtype = IFM_NONE; + } else { + /* everything ok */ + + tmp = ((bmcr2 >> 2) & bmsr2) & (GTSR_LP_1000THDX | GTSR_LP_1000TFDX); + if (tmp) { + if (GTSR_LP_1000TFDX & tmp) + options |= IFM_FDX; + subtype = IFM_1000_T; + } else { + if (info->mdio_r (phy, uarg, MII_ANAR, &anar)) + return EINVAL; + if (info->mdio_r (phy, uarg, MII_ANLPAR, &lpar)) + return EINVAL; + if (ANLPAR_ACK & lpar) { + /* this is a negotiated link; otherwise we merely detect the partner's ability */ + } + tmp = anar & lpar; + if (ANLPAR_TX_FD & tmp) { + options |= IFM_FDX; + subtype = IFM_100_TX; + } else if (ANLPAR_T4 & tmp) { + subtype = IFM_100_T4; + } else if (ANLPAR_TX & tmp) { + subtype = IFM_100_TX; + } else if (ANLPAR_10_FD & tmp) { + options |= IFM_FDX; + subtype = IFM_10_T; + } else { + subtype = IFM_10_T; + } + } + } + + *media = IFM_MAKEWORD (IFM_ETHER, subtype, options, phy); + + break; + +#ifdef DEBUG + case 1: +#endif + case SIOCSIFMEDIA: + if (IFM_ETHER != IFM_TYPE (*media)) + return EINVAL; + + if (info->mdio_r (phy, uarg, MII_BMSR, &bmsr)) + return EINVAL; + + tmp = (IFM_FDX & *media); + + switch (IFM_SUBTYPE (*media)) { + default: + return ENOTSUP; + + case IFM_AUTO: + bmcr = BMCR_AUTOEN | BMCR_STARTNEG; + tmp = 0; + break; + + case IFM_1000_T: + if (!info->has_gmii) + return ENOTSUP; + + if (info->mdio_r (phy, uarg, MII_EXTSR, &bmsr2)) + return EINVAL; + + if (!(bmsr2 & (tmp ? EXTSR_1000TFDX : EXTSR_1000THDX))) + return EOPNOTSUPP; + bmcr = BMCR_S1000; + break; + + case IFM_100_TX: + if (!(bmsr & (tmp ? BMSR_100TXFDX : BMSR_100TXHDX))) + return EOPNOTSUPP; + bmcr = BMCR_S100; + break; + + case IFM_10_T: + if (!(bmsr & (tmp ? BMSR_10TFDX : BMSR_10THDX))) + return EOPNOTSUPP; + bmcr = BMCR_S10; + break; + } + + if (tmp) + bmcr |= BMCR_FDX; + + if (info->mdio_w (phy, uarg, MII_BMCR, bmcr)) + return EINVAL; + + /* TODO: should we adapt advertised capabilites ? */ + + break; + } + + return 0; +} |