# --------------------------------------------
# 02/09/29	jgarzik@mandrakesoft.com	1.702
# Use new MII lib helper generic_mii_ioctl in several net drivers:
# 8139too, epic100, fealnx, sundance and via-rhine.
# 
# In the process, several of these net drivers gained MII ioctl
# locking fixes simply by virtue of being brought in line with
# standardized code.
# --------------------------------------------
#
diff -Nru a/drivers/net/8139too.c b/drivers/net/8139too.c
--- a/drivers/net/8139too.c	Sun Sep 29 19:35:43 2002
+++ b/drivers/net/8139too.c	Sun Sep 29 19:35:43 2002
@@ -578,7 +578,6 @@
 	signed char phys[4];		/* MII device addresses. */
 	char twistie, twist_row, twist_col;	/* Twister tune state. */
 	unsigned int default_port:4;	/* Last dev->if_port value. */
-	unsigned int medialock:1;	/* Don't sense media type. */
 	spinlock_t lock;
 	chip_t chipset;
 	pid_t thr_pid;
@@ -986,6 +985,8 @@
 	tp->mii.dev = dev;
 	tp->mii.mdio_read = mdio_read;
 	tp->mii.mdio_write = mdio_write;
+	tp->mii.phy_id_mask = 0x3f;
+	tp->mii.reg_num_mask = 0x1f;
 
 	/* dev is fully set up and ready to use now */
 	DPRINTK("about to register device named %s (%p)...\n", dev->name, dev);
@@ -1041,7 +1042,7 @@
 		tp->mii.full_duplex = (option & 0x210) ? 1 : 0;
 		tp->default_port = option & 0xFF;
 		if (tp->default_port)
-			tp->medialock = 1;
+			tp->mii.force_media = 1;
 	}
 	if (board_idx < MAX_UNITS  &&  full_duplex[board_idx] > 0)
 		tp->mii.full_duplex = full_duplex[board_idx];
@@ -2438,61 +2439,25 @@
 }
 
 
-static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-	struct rtl8139_private *tp = dev->priv;
-	struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data;
-	int rc = 0;
-	int phy = tp->phys[0] & 0x3f;
+	struct rtl8139_private *np = dev->priv;
+	struct mii_ioctl_data *data = (struct mii_ioctl_data *) & rq->ifr_data;
+	int rc;
 
 	if (!netif_running(dev))
 		return -EINVAL;
 
-	if (cmd != SIOCETHTOOL) {
-		/* With SIOCETHTOOL, this would corrupt the pointer.  */
-		data->phy_id &= 0x3f;
-		data->reg_num &= 0x1f;
-	}
-
-	switch (cmd) {
-	case SIOCETHTOOL:
-		return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
-
-	case SIOCGMIIPHY:	/* Get the address of the PHY in use. */
-	case SIOCDEVPRIVATE:	/* binary compat, remove in 2.5 */
-		data->phy_id = phy;
-		/* Fall Through */
-
-	case SIOCGMIIREG:	/* Read the specified MII register. */
-	case SIOCDEVPRIVATE+1:	/* binary compat, remove in 2.5 */
-		data->val_out = mdio_read (dev, data->phy_id, data->reg_num);
-		break;
-
-	case SIOCSMIIREG:	/* Write the specified MII register */
-	case SIOCDEVPRIVATE+2:	/* binary compat, remove in 2.5 */
-		if (!capable (CAP_NET_ADMIN)) {
-			rc = -EPERM;
-			break;
-		}
+	if (cmd == SIOCETHTOOL)
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
 
-		if (data->phy_id == phy) {
-			u16 value = data->val_in;
-			switch (data->reg_num) {
-			case 0:
-				/* Check for autonegotiation on or reset. */
-				tp->medialock = (value & 0x9000) ? 0 : 1;
-				if (tp->medialock)
-					tp->mii.full_duplex = (value & 0x0100) ? 1 : 0;
-				break;
-			case 4: tp->mii.advertising = value; break;
-			}
-		}
-		mdio_write(dev, data->phy_id, data->reg_num, data->val_in);
-		break;
+	else {
+		spin_lock_irq(&np->lock);
+		rc = generic_mii_ioctl(dev, &np->mii, data, cmd);
+		spin_unlock_irq(&np->lock);
 
-	default:
-		rc = -EOPNOTSUPP;
-		break;
+		if (rc == 1)	/* don't care about duplex change, fix up rc */
+			rc = 0;
 	}
 
 	return rc;
diff -Nru a/drivers/net/epic100.c b/drivers/net/epic100.c
--- a/drivers/net/epic100.c	Sun Sep 29 19:35:43 2002
+++ b/drivers/net/epic100.c	Sun Sep 29 19:35:43 2002
@@ -430,6 +430,8 @@
 	ep->mii.dev = dev;
 	ep->mii.mdio_read = mdio_read;
 	ep->mii.mdio_write = mdio_write;
+	ep->mii.phy_id_mask = 0x1f;
+	ep->mii.reg_num_mask = 0x1f;
 
 	ring_space = pci_alloc_consistent(pdev, TX_TOTAL_SIZE, &ring_dma);
 	if (!ring_space)
@@ -1427,66 +1429,36 @@
 
 static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-	struct epic_private *ep = dev->priv;
+	struct epic_private *np = dev->priv;
 	long ioaddr = dev->base_addr;
 	struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data;
+	int rc;
 
-	switch(cmd) {
-	case SIOCETHTOOL:
-		return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
-
-	case SIOCGMIIPHY:		/* Get address of MII PHY in use. */
-	case SIOCDEVPRIVATE:		/* for binary compat, remove in 2.5 */
-		data->phy_id = ep->phys[0] & 0x1f;
-		/* Fall Through */
-
-	case SIOCGMIIREG:		/* Read MII PHY register. */
-	case SIOCDEVPRIVATE+1:		/* for binary compat, remove in 2.5 */
-		if (! netif_running(dev)) {
-			outl(0x0200, ioaddr + GENCTL);
-			outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
-		}
-		data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f);
-#if 0					/* Just leave on if the ioctl() is ever used. */
-		if (! netif_running(dev)) {
-			outl(0x0008, ioaddr + GENCTL);
-			outl((inl(ioaddr + NVCTL) & ~0x483C) | 0x0000, ioaddr + NVCTL);
-		}
-#endif
-		return 0;
+	/* power-up, if interface is down */
+	if (! netif_running(dev)) {
+		outl(0x0200, ioaddr + GENCTL);
+		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+	}
 
-	case SIOCSMIIREG:		/* Write MII PHY register. */
-	case SIOCDEVPRIVATE+2:		/* for binary compat, remove in 2.5 */
-		if (!capable(CAP_NET_ADMIN))
-			return -EPERM;
-		if (! netif_running(dev)) {
-			outl(0x0200, ioaddr + GENCTL);
-			outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
-		}
-		if (data->phy_id == ep->phys[0]) {
-			u16 value = data->val_in;
-			switch (data->reg_num) {
-			case 0:
-				/* Check for autonegotiation on or reset. */
-				ep->mii.force_media = (value & 0x9000) ? 0 : 1;
-				if (ep->mii.force_media)
-					ep->mii.full_duplex = (value & 0x0100) ? 1 : 0;
-				break;
-			case 4: ep->mii.advertising = value; break;
-			}
-			/* Perhaps check_duplex(dev), depending on chip semantics. */
-		}
-		mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in);
-#if 0					/* Leave on if the ioctl() is used. */
-		if (! netif_running(dev)) {
-			outl(0x0008, ioaddr + GENCTL);
-			outl((inl(ioaddr + NVCTL) & ~0x483C) | 0x0000, ioaddr + NVCTL);
-		}
-#endif
-		return 0;
-	default:
-		return -EOPNOTSUPP;
+	/* ethtool commands */
+	if (cmd == SIOCETHTOOL)
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
+	/* all other ioctls (the SIOC[GS]MIIxxx ioctls) */
+	else {
+		spin_lock_irq(&np->lock);
+		rc = generic_mii_ioctl(dev, &np->mii, data, cmd);
+		spin_unlock_irq(&np->lock);
+		if (rc == 1)	/* don't care about duplex change, fixup rc */
+			rc = 0;
+	}
+
+	/* power-down, if interface is down */
+	if (! netif_running(dev)) {
+		outl(0x0008, ioaddr + GENCTL);
+		outl((inl(ioaddr + NVCTL) & ~0x483C) | 0x0000, ioaddr + NVCTL);
 	}
+	return rc;
 }
 
 
diff -Nru a/drivers/net/fealnx.c b/drivers/net/fealnx.c
--- a/drivers/net/fealnx.c	Sun Sep 29 19:35:43 2002
+++ b/drivers/net/fealnx.c	Sun Sep 29 19:35:43 2002
@@ -559,6 +559,8 @@
 	np->mii.dev = dev;
 	np->mii.mdio_read = mdio_read;
 	np->mii.mdio_write = mdio_write;
+	np->mii.phy_id_mask = 0x1f;
+	np->mii.reg_num_mask = 0x1f;
 
 	ring_space = pci_alloc_consistent(pdev, RX_TOTAL_SIZE, &ring_dma);
 	if (!ring_space) {
@@ -1833,31 +1835,25 @@
 
 static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
+	struct netdev_private *np = dev->priv;
 	struct mii_ioctl_data *data = (struct mii_ioctl_data *) & rq->ifr_data;
+	int rc;
 
-	switch (cmd) {
-	case SIOCETHTOOL:
-		return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
-
-	case SIOCGMIIPHY:		/* Get address of MII PHY in use. */
-	case SIOCDEVPRIVATE:		/* for binary compat, remove in 2.5 */
-		data->phy_id = ((struct netdev_private *) dev->priv)->phys[0] & 0x1f;
-		/* Fall Through */
-
-	case SIOCGMIIREG:		/* Read MII PHY register. */
-	case SIOCDEVPRIVATE+1:		/* for binary compat, remove in 2.5 */
-		data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f);
-		return 0;
+	if (!netif_running(dev))
+		return -EINVAL;
 
-	case SIOCSMIIREG:		/* Write MII PHY register. */
-	case SIOCDEVPRIVATE+2:		/* for binary compat, remove in 2.5 */
-		if (!capable(CAP_NET_ADMIN))
-			return -EPERM;
-		mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in);
-		return 0;
-	default:
-		return -EOPNOTSUPP;
+	if (cmd == SIOCETHTOOL)
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
+	else {
+		spin_lock_irq(&np->lock);
+		rc = generic_mii_ioctl(dev, &np->mii, data, cmd);
+		spin_unlock_irq(&np->lock);
+		if (rc == 1)	/* don't care about duplex change, fix up rc */
+			rc = 0;
 	}
+
+	return rc;
 }
 
 
diff -Nru a/drivers/net/sundance.c b/drivers/net/sundance.c
--- a/drivers/net/sundance.c	Sun Sep 29 19:35:43 2002
+++ b/drivers/net/sundance.c	Sun Sep 29 19:35:43 2002
@@ -573,6 +573,8 @@
 	np->mii_if.dev = dev;
 	np->mii_if.mdio_read = mdio_read;
 	np->mii_if.mdio_write = mdio_write;
+	np->mii_if.phy_id_mask = 0x1f;
+	np->mii_if.reg_num_mask = 0x1f;
 
 	/* The chip-specific entries in the device structure. */
 	dev->open = &netdev_open;
@@ -1550,27 +1552,25 @@
 
 static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-	struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data;
+	struct netdev_private *np = dev->priv;
+	struct mii_ioctl_data *data = (struct mii_ioctl_data *) & rq->ifr_data;
+	int rc;
 
-	switch(cmd) {
-	case SIOCETHTOOL:
-		return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
-	case SIOCGMIIPHY:		/* Get address of MII PHY in use. */
-		data->phy_id = ((struct netdev_private *)dev->priv)->phys[0] & 0x1f;
-		/* Fall Through */
-
-	case SIOCGMIIREG:		/* Read MII PHY register. */
-		data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f);
-		return 0;
-
-	case SIOCSMIIREG:		/* Write MII PHY register. */
-		if (!capable(CAP_NET_ADMIN))
-			return -EPERM;
-		mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in);
-		return 0;
-	default:
-		return -EOPNOTSUPP;
+	if (!netif_running(dev))
+		return -EINVAL;
+
+	if (cmd == SIOCETHTOOL)
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
+	else {
+		spin_lock_irq(&np->lock);
+		rc = generic_mii_ioctl(dev, &np->mii_if, data, cmd);
+		spin_unlock_irq(&np->lock);
+		if (rc == 1)	/* don't care about duplex change, fix up rc */
+			rc = 0;
 	}
+
+	return rc;
 }
 
 static int netdev_close(struct net_device *dev)
diff -Nru a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c
--- a/drivers/net/via-rhine.c	Sun Sep 29 19:35:43 2002
+++ b/drivers/net/via-rhine.c	Sun Sep 29 19:35:43 2002
@@ -528,7 +528,7 @@
 static void via_rhine_error(struct net_device *dev, int intr_status);
 static void via_rhine_set_rx_mode(struct net_device *dev);
 static struct net_device_stats *via_rhine_get_stats(struct net_device *dev);
-static int via_rhine_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static int  via_rhine_close(struct net_device *dev);
 static inline void clear_tally_counters(long ioaddr);
 static inline void via_restart_tx(struct net_device *dev);
@@ -733,6 +733,8 @@
 	np->mii_if.dev = dev;
 	np->mii_if.mdio_read = mdio_read;
 	np->mii_if.mdio_write = mdio_write;
+	np->mii_if.phy_id_mask = 0x1f;
+	np->mii_if.reg_num_mask = 0x1f;
 
 	if (dev->mem_start)
 		option = dev->mem_start;
@@ -758,7 +760,7 @@
 	dev->stop = via_rhine_close;
 	dev->get_stats = via_rhine_get_stats;
 	dev->set_multicast_list = via_rhine_set_rx_mode;
-	dev->do_ioctl = via_rhine_ioctl;
+	dev->do_ioctl = netdev_ioctl;
 	dev->tx_timeout = via_rhine_tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
 	if (np->drv_flags & ReqTxAlign)
@@ -1633,7 +1635,7 @@
 	writeb(np->rx_thresh | rx_mode, ioaddr + RxConfig);
 }
 
-static int via_rhine_ethtool_ioctl (struct net_device *dev, void *useraddr)
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
 {
 	struct netdev_private *np = dev->priv;
 	u32 ethcmd;
@@ -1716,44 +1718,28 @@
 
 	return -EOPNOTSUPP;
 }
-static int via_rhine_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
 	struct netdev_private *np = dev->priv;
-	struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data;
-	unsigned long flags;
-	int retval;
+	struct mii_ioctl_data *data = (struct mii_ioctl_data *) & rq->ifr_data;
+	int rc;
 
-	if (cmd == SIOCETHTOOL)
-		return via_rhine_ethtool_ioctl(dev, (void *) rq->ifr_data);
+	if (!netif_running(dev))
+		return -EINVAL;
 
-	spin_lock_irqsave(&np->lock, flags);
-	retval = 0;
-
-	switch(cmd) {
-	case SIOCGMIIPHY:		/* Get address of MII PHY in use. */
-	case SIOCDEVPRIVATE:		/* for binary compat, remove in 2.5 */
-		data->phy_id = np->phys[0] & 0x1f;
-		/* Fall Through */
-
-	case SIOCGMIIREG:		/* Read MII PHY register. */
-	case SIOCDEVPRIVATE+1:		/* for binary compat, remove in 2.5 */
-		data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f);
-		break;
+	if (cmd == SIOCETHTOOL)
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
 
-	case SIOCSMIIREG:		/* Write MII PHY register. */
-	case SIOCDEVPRIVATE+2:		/* for binary compat, remove in 2.5 */
-		if (!capable(CAP_NET_ADMIN)) {
-			retval = -EPERM;
-			break;
-		}
-		mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in);
-		break;
-	default:
-		retval = -EOPNOTSUPP;
+	else {
+		spin_lock_irq(&np->lock);
+		rc = generic_mii_ioctl(dev, &np->mii_if, data, cmd);
+		spin_unlock_irq(&np->lock);
+		if (rc == 1)	/* don't care about duplex change, fix up rc */
+			rc = 0;
 	}
 
-	spin_unlock_irqrestore(&np->lock, flags);
-	return retval;
+	return rc;
 }
 
 static int via_rhine_close(struct net_device *dev)
