Commit a7a8afcf authored by Adam Kropelin's avatar Adam Kropelin Committed by Jeff Garzik

The following patch adds support for ethtool to the ewrk3 driver. It is against

2.5-BK but should apply to any recent 2.5 and 2.4 as well. In addition to adding
ethtool support, it also removes the cli/sti fixup attribution from the
changelog since that didn't actually go in yet and fixes a small style issue I
introduced in the multi-card support patch.

Note that for ewrk3 still needs VDA's cli/sti removal patch, which I will
send along in a separate mail along with some other cleanups.

This has been tested on an SMP x86 box containing 3 DE205 NICs.
parent ecf2c214
......@@ -135,8 +135,8 @@
0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c
0.44 08-Nov-01 use library crc32 functions <Matt_Domsch@dell.com>
0.45 19-Jul-02 fix unaligned access on alpha <martin@bruli.net>
0.46 10-Oct-02 cli/sti removal <VDA@port.imtp.ilyichevsk.odessa.ua>
Multiple NIC support when module <akropel1@rochester.rr.com>
0.46 10-Oct-02 Multiple NIC support when module <akropel1@rochester.rr.com>
0.47 18-Oct-02 ethtool support <akropel1@rochester.rr.com>
=========================================================================
*/
......@@ -161,6 +161,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/ethtool.h>
#include <linux/time.h>
#include <linux/types.h>
......@@ -169,8 +170,11 @@
#include "ewrk3.h"
#define DRV_NAME "ewrk3"
#define DRV_VERSION "0.47"
static char version[] __initdata =
"ewrk3.c:v0.46 2002/10/09 davies@maniac.ultranet.com\n";
DRV_NAME ":v" DRV_VERSION " 2002/10/17 davies@maniac.ultranet.com\n";
#ifdef EWRK3_DEBUG
static int ewrk3_debug = EWRK3_DEBUG;
......@@ -278,6 +282,7 @@ struct ewrk3_private {
u_char hard_strapped; /* Don't allow a full open */
u_char txc; /* Transmit cut through */
u_char *mctbl; /* Pointer to the multicast table */
u_char led_mask; /* Used to reserve LED access for ethtool */
spinlock_t hw_lock;
};
......@@ -533,6 +538,7 @@ ewrk3_hw_init(struct net_device *dev, u_long iobase)
lp->shmem_length = shmem_length;
lp->lemac = lemac;
lp->hard_strapped = hard_strapped;
lp->led_mask = CR_LED;
spin_lock_init(&lp->hw_lock);
lp->mPage = 64;
......@@ -903,7 +909,7 @@ static void ewrk3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
DISABLE_IRQs;
cr = inb(EWRK3_CR);
cr |= CR_LED;
cr |= lp->led_mask;
outb(cr, EWRK3_CR);
if (csr & CSR_RNE) /* Rx interrupt (packet[s] arrived) */
......@@ -928,7 +934,7 @@ static void ewrk3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
/* Unmask the EWRK3 board interrupts and turn off the LED */
cr &= ~CR_LED;
cr &= ~(lp->led_mask);
outb(cr, EWRK3_CR);
ENABLE_IRQs;
spin_unlock(&lp->hw_lock);
......@@ -1624,6 +1630,195 @@ static int __init EISA_signature(char *name, s32 eisa_id)
return status; /* return the device name string */
}
static int ewrk3_ethtool_ioctl(struct net_device *dev, void *useraddr)
{
struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv;
u_long iobase = dev->base_addr;
u32 ethcmd;
if (get_user(ethcmd, (u32 *)useraddr))
return -EFAULT;
switch (ethcmd) {
/* Get driver info */
case ETHTOOL_GDRVINFO: {
struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
int fwrev = Read_EEPROM(dev->base_addr, EEPROM_REVLVL);
strcpy(info.driver, DRV_NAME);
strcpy(info.version, DRV_VERSION);
sprintf(info.fw_version, "%d", fwrev);
strcpy(info.bus_info, "N/A");
info.eedump_len = EEPROM_MAX;
if (copy_to_user(useraddr, &info, sizeof(info)))
return -EFAULT;
return 0;
}
/* Get settings */
case ETHTOOL_GSET: {
struct ethtool_cmd ecmd = { ETHTOOL_GSET };
u_char cr = inb(EWRK3_CR);
switch (lp->adapter_name[4]) {
case '3': /* DE203 */
ecmd.supported = SUPPORTED_BNC;
ecmd.port = PORT_BNC;
break;
case '4': /* DE204 */
ecmd.supported = SUPPORTED_TP;
ecmd.port = PORT_TP;
break;
case '5': /* DE205 */
ecmd.supported = SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_AUI;
ecmd.autoneg = !(cr & CR_APD);
/*
** Port is only valid if autoneg is disabled
** and even then we don't know if AUI is jumpered.
*/
if (!ecmd.autoneg)
ecmd.port = (cr & CR_PSEL) ? PORT_BNC : PORT_TP;
break;
}
ecmd.supported |= SUPPORTED_10baseT_Half;
ecmd.speed = SPEED_10;
ecmd.duplex = DUPLEX_HALF;
if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
return -EFAULT;
return 0;
}
/* Set settings */
case ETHTOOL_SSET: {
struct ethtool_cmd ecmd;
u_char cr;
u_long flags;
/* DE205 is the only card with anything to set */
if (lp->adapter_name[4] != '5')
return -EOPNOTSUPP;
if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
return -EFAULT;
/* Sanity-check parameters */
if (ecmd.speed != SPEED_10)
return -EINVAL;
if (ecmd.port != PORT_TP && ecmd.port != PORT_BNC)
return -EINVAL; /* AUI is not software-selectable */
if (ecmd.transceiver != XCVR_INTERNAL)
return -EINVAL;
if (ecmd.duplex != DUPLEX_HALF)
return -EINVAL;
if (ecmd.phy_address != 0)
return -EINVAL;
spin_lock_irqsave(&lp->hw_lock, flags);
cr = inb(EWRK3_CR);
/* If Autoneg is set, change to Auto Port mode */
/* Otherwise, disable Auto Port and set port explicitly */
if (ecmd.autoneg) {
cr &= ~CR_APD;
} else {
cr |= CR_APD;
if (ecmd.port == PORT_TP)
cr &= ~CR_PSEL; /* Force TP */
else
cr |= CR_PSEL; /* Force BNC */
}
/* Commit the changes */
outb(cr, EWRK3_CR);
spin_unlock_irqrestore(&lp->hw_lock, flags);
if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
return -EFAULT;
return 0;
}
/* Get link status */
case ETHTOOL_GLINK: {
struct ethtool_value edata = { ETHTOOL_GLINK };
u_char cmr = inb(EWRK3_CMR);
/* DE203 has BNC only and link status does not apply */
if (lp->adapter_name[4] == '3')
return -EOPNOTSUPP;
/* On DE204 this is always valid since TP is the only port. */
/* On DE205 this reflects TP status even if BNC or AUI is selected. */
edata.data = !(cmr & CMR_LINK);
if (copy_to_user(useraddr, &edata, sizeof(edata)))
return -EFAULT;
return 0;
}
/* Blink LED for identification */
case ETHTOOL_PHYS_ID: {
struct ethtool_value edata;
u_long flags;
long delay, ret;
u_char cr;
int count;
wait_queue_head_t wait;
init_waitqueue_head(&wait);
if (copy_from_user(&edata, useraddr, sizeof(edata)))
return -EFAULT;
/* Toggle LED 4x per second */
delay = HZ >> 2;
count = edata.data << 2;
spin_lock_irqsave(&lp->hw_lock, flags);
/* Bail if a PHYS_ID is already in progress */
if (lp->led_mask == 0) {
spin_unlock_irqrestore(&lp->hw_lock, flags);
return -EBUSY;
}
/* Prevent ISR from twiddling the LED */
lp->led_mask = 0;
while (count--) {
/* Toggle the LED */
cr = inb(EWRK3_CR);
outb(cr ^ CR_LED, EWRK3_CR);
/* Wait a little while */
spin_unlock_irqrestore(&lp->hw_lock, flags);
ret = delay;
__wait_event_interruptible_timeout(wait, 0, ret);
spin_lock_irqsave(&lp->hw_lock, flags);
/* Exit if we got a signal */
if (ret == -ERESTARTSYS)
goto out;
}
ret = 0;
out:
lp->led_mask = CR_LED;
cr = inb(EWRK3_CR);
outb(cr & ~CR_LED, EWRK3_CR);
spin_unlock_irqrestore(&lp->hw_lock, flags);
return ret;
}
}
return -EOPNOTSUPP;
}
/*
** Perform IOCTL call functions here. Some are privileged operations and the
** effective uid is checked in those cases.
......@@ -1642,6 +1837,10 @@ static int ewrk3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
union ewrk3_addr *tmp;
/* ethtool IOCTLs are handled elsewhere */
if (cmd == SIOCETHTOOL)
return ewrk3_ethtool_ioctl(dev, (void *)rq->ifr_data);
tmp = kmalloc(sizeof(union ewrk3_addr), GFP_KERNEL);
if(tmp==NULL)
return -ENOMEM;
......@@ -1895,8 +2094,7 @@ void cleanup_module(void)
{
int i;
for( i=0; i<ndevs; i++ )
{
for( i=0; i<ndevs; i++ ) {
unregister_netdev(ewrk3_devs[i]);
if (ewrk3_devs[i]->priv) {
kfree(ewrk3_devs[i]->priv);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment