patch-2.4.25 linux-2.4.25/drivers/net/sk98lin/skge.c

Next file: linux-2.4.25/drivers/net/sk98lin/skgehwt.c
Previous file: linux-2.4.25/drivers/net/sk98lin/skdim.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.24/drivers/net/sk98lin/skge.c linux-2.4.25/drivers/net/sk98lin/skge.c
@@ -1,35 +1,20 @@
 /******************************************************************************
  *
- * Name:    skge.c
+ * Name:	skge.c
  * Project:	GEnesis, PCI Gigabit Ethernet Adapter
- * Version:	$Revision: 1.11 $
- * Date:       	$Date: 2003/08/26 16:05:19 $
+ * Version:	$Revision: 1.43 $
+ * Date:       	$Date: 2004/01/29 15:47:07 $
  * Purpose:	The main driver source module
  *
  ******************************************************************************/
 
 /******************************************************************************
  *
- *	(C)Copyright 1998-2003 SysKonnect GmbH.
+ *	(C)Copyright 1998-2002 SysKonnect GmbH.
+ *	(C)Copyright 2002-2003 Marvell.
  *
- *	Driver for SysKonnect Gigabit Ethernet Server Adapters:
- *
- *	SK-9871 (single link 1000Base-ZX)
- *	SK-9872 (dual link   1000Base-ZX)
- *	SK-9861 (single link 1000Base-SX, VF45 Volition Plug)
- *	SK-9862 (dual link   1000Base-SX, VF45 Volition Plug)
- *	SK-9841 (single link 1000Base-LX)
- *	SK-9842 (dual link   1000Base-LX)
- *	SK-9843 (single link 1000Base-SX)
- *	SK-9844 (dual link   1000Base-SX)
- *	SK-9821 (single link 1000Base-T)
- *	SK-9822 (dual link   1000Base-T)
- *	SK-9881 (single link 1000Base-SX V2 LC)
- *	SK-9871 (single link 1000Base-ZX V2)
- *	SK-9861 (single link 1000Base-SX V2, VF45 Volition Plug)
- *	SK-9841 (single link 1000Base-LX V2)
- *	SK-9843 (single link 1000Base-SX V2)
- *	SK-9821 (single link 1000Base-T V2)
+ *	Driver for Marvell Yukon chipset and SysKonnect Gigabit Ethernet 
+ *      Server Adapters.
  *
  *	Created 10-Feb-1999, based on Linux' acenic.c, 3c59x.c and
  *	SysKonnects GEnesis Solaris driver
@@ -56,6 +41,90 @@
  * History:
  *
  *	$Log: skge.c,v $
+ *	Revision 1.43  2004/01/29 15:47:07  mlindner
+ *	Fix: Reset Xmac when stopping the port
+ *	
+ *	Revision 1.42  2003/12/12 10:05:43  mlindner
+ *	Fix: Format of error message corrected
+ *	
+ *	Revision 1.41  2003/12/11 16:03:57  mlindner
+ *	Fix: Create backup from pnmi data structure
+ *	
+ *	Revision 1.40  2003/12/11 12:14:48  mlindner
+ *	Fix: Initalize Board before network configuration
+ *	Fix: Change device names to driver name
+ *	
+ *	Revision 1.39  2003/12/10 08:57:38  rroesler
+ *	Fix: Modifications regarding try_module_get() and capable()
+ *	
+ *	Revision 1.38  2003/12/01 17:16:50  mlindner
+ *	Fix: Remove useless register_netdev
+ *	
+ *	Revision 1.37  2003/12/01 17:11:30  mlindner
+ *	Fix: Register net device before SkGeBoardInit
+ *	
+ *	Revision 1.36  2003/11/28 13:04:27  rroesler
+ *	Fix: do not print interface status in case DIAG is used
+ *	
+ *	Revision 1.35  2003/11/17 14:41:06  mlindner
+ *	Fix: Endif command
+ *	
+ *	Revision 1.34  2003/11/17 13:29:05  mlindner
+ *	Fix: Editorial changes
+ *	
+ *	Revision 1.33  2003/11/14 14:56:54  rroesler
+ *	Fix: corrected compilation warnings kernel 2.2
+ *	
+ *	Revision 1.32  2003/11/13 14:18:47  rroesler
+ *	Fix: added latest changes regarding the use of the proc system
+ *	
+ *	Revision 1.31  2003/11/13 09:28:35  rroesler
+ *	Fix: removed kernel warning 'driver changed get_stats after register'
+ *	
+ *	Revision 1.30  2003/11/11 13:15:27  rroesler
+ *	Fix: use suitables kernel usage count macros when using the diag
+ *	
+ *	Revision 1.29  2003/11/10 09:38:26  rroesler
+ *	Fix: restore PNMI structure backup for DIAG actions
+ *	
+ *	Revision 1.28  2003/11/07 17:28:45  rroesler
+ *	Fix: Additions for the LeaveDiagMode
+ *	
+ *	Revision 1.27  2003/11/03 13:21:14  mlindner
+ *	Add: SkGeBuffPad function for padding to ensure the trailing bytes exist
+ *	
+ *	Revision 1.26  2003/10/30 09:20:40  mlindner
+ *	Fix: Control bit check
+ *	
+ *	Revision 1.25  2003/10/29 07:43:37  rroesler
+ *	Fix: Implemented full None values handling for parameter Moderation
+ *	
+ *	Revision 1.24  2003/10/22 14:18:12  rroesler
+ *	Fix: DIAG handling for DualNet cards
+ *	
+ *	Revision 1.23  2003/10/17 10:05:13  mlindner
+ *	Add: New blinkmode for Morvell cards
+ *	
+ *	Revision 1.22  2003/10/15 12:31:25  rroesler
+ *	Fix: Corrected bugreport #10954 (Linux System crash when using vlans)
+ *	
+ *	Revision 1.21  2003/10/07 12:32:28  mlindner
+ *	Fix: Editorial changes
+ *	
+ *	Revision 1.20  2003/10/07 12:22:40  mlindner
+ *	Fix: Compiler warnings
+ *	
+ *	Revision 1.19  2003/10/07 09:33:40  mlindner
+ *	Fix: No warnings for illegal values of Mod and IntsPerSec
+ *	Fix: Speed 100 in Half Duplex not allowed for Yukon
+ *	Fix: PrefPort=B not allowed on single NICs
+ *	
+ *	Revision 1.18  2003/10/07 08:17:08  mlindner
+ *	Fix: Copyright changes
+ *	
+ *	Revision 1.17  2003/09/29 12:06:59  mlindner
+ *	*** empty log message ***
+ *	
  *	Revision 1.16  2003/09/23 11:07:35  mlindner
  *	Fix: IO-control return race condition
  *	Fix: Interrupt moderation value check
@@ -68,6 +137,12 @@
  *	Add: Yukon Plus changes (ChipID, PCI...)
  *	Fix: TCP and UDP Checksum calculation
  *	
+ *	Revision 1.13  2003/09/01 13:30:08  rroesler
+ *	Fix: Corrected missing defines
+ *	
+ *	Revision 1.12  2003/09/01 13:12:02  rroesler
+ *	Add: Code for improved DIAG Attach/Detach interface
+ *	
  *	Revision 1.11  2003/08/26 16:05:19  mlindner
  *	Fix: Compiler warnings (void *)
  *	
@@ -406,7 +481,6 @@
  *	<linux/module.h>
  *
  *	"h/skdrv1st.h"
- *		<linux/version.h>
  *		<linux/types.h>
  *		<linux/kernel.h>
  *		<linux/string.h>
@@ -568,6 +642,12 @@
 static void	StopDrvCleanupTimer(SK_AC *pAC);
 static int	XmitFrameSG(SK_AC*, TX_PORT*, struct sk_buff*);
 
+#ifdef SK_DIAG_SUPPORT
+static SK_U32   ParseDeviceNbrFromSlotName(const char *SlotName);
+static int      SkDrvInitAdapter(SK_AC *pAC, int devNbr);
+static int      SkDrvDeInitAdapter(SK_AC *pAC, int devNbr);
+#endif
+
 /*******************************************************************************
  *
  * Extern Function Prototypes
@@ -576,14 +656,13 @@
 
 #ifdef CONFIG_PROC_FS
 static const char 	SK_Root_Dir_entry[] = "sk98lin";
-static struct		proc_dir_entry *pSkRootDir;
-
-extern int 		sk_proc_read(	char   *buffer,
-					char	**buffer_location,
-					off_t	offset,
-					int	buffer_length,
-					int	*eof,
-					void	*data);
+static struct		proc_dir_entry *pSkRootDir = NULL;
+extern int 	sk_proc_read(	char   *buffer,
+				char	**buffer_location,
+				off_t	offset,
+				int	buffer_length,
+				int	*eof,
+				void	*data);
 #endif
 
 extern void SkDimEnableModerationIfNeeded(SK_AC *pAC);	
@@ -601,6 +680,7 @@
 static const char *BootString = BOOT_STRING;
 struct SK_NET_DEVICE *SkGeRootDev = NULL;
 static int probed __initdata = 0;
+static SK_BOOL DoPrintInterfaceChange = SK_TRUE;
 
 /* local variables **********************************************************/
 static uintptr_t TxQueueAddr[SK_MAX_MACS][2] = {{0x680, 0x600},{0x780, 0x700}};
@@ -672,17 +752,11 @@
 			break;
 		}
 
-		if (dev->priv == NULL) {
-			printk(KERN_ERR "Unable to allocate adapter "
-			       "structure!\n");
-			break;
-		}
-
 		pNet = dev->priv;
 		pNet->pAC = kmalloc(sizeof(SK_AC), GFP_KERNEL);
 		if (pNet->pAC == NULL){
-			dev->get_stats = NULL;
 			unregister_netdev(dev);
+			dev->get_stats = NULL;
 			kfree(dev->priv);
 			printk(KERN_ERR "Unable to allocate adapter "
 			       "structure!\n");
@@ -712,8 +786,8 @@
 		retval = SkGeInitPCI(pAC);
 		if (retval) {
 			printk("SKGE: PCI setup failed: %i\n", retval);
-			dev->get_stats = NULL;
 			unregister_netdev(dev);
+			dev->get_stats = NULL;
 			kfree(dev);
 			continue;
 		}
@@ -740,12 +814,30 @@
 #endif
 
 		pAC->Index = boards_found;
+
 		if (SkGeBoardInit(dev, pAC)) {
-			FreeResources(dev);
+			unregister_netdev(dev);
 			kfree(dev);
 			continue;
 		}
 
+
+		/* Print adapter specific string from vpd */
+		ProductStr(pAC);
+		printk("%s: %s\n", dev->name, pAC->DeviceStr);
+
+		/* Print configuration settings */
+		printk("      PrefPort:%c  RlmtMode:%s\n",
+			'A' + pAC->Rlmt.Net[0].Port[pAC->Rlmt.Net[0].PrefPort]->PortNumber,
+			(pAC->RlmtMode==0)  ? "Check Link State" :
+			((pAC->RlmtMode==1) ? "Check Link State" :
+			((pAC->RlmtMode==3) ? "Check Local Port" :
+			((pAC->RlmtMode==7) ? "Check Segmentation" :
+			((pAC->RlmtMode==17) ? "Dual Check Link State" :"Error")))));
+
+		SkGeYellowLED(pAC, pAC->IoBase, 1);
+
+
 		memcpy((caddr_t) &dev->dev_addr,
 			(caddr_t) &pAC->Addr.Net[0].CurrentMacAddress, 6);
 
@@ -770,27 +862,16 @@
 			S_IFREG | S_IXUSR | S_IWGRP | S_IROTH,
 			pSkRootDir);
 
-		
-		pProcFile->read_proc = sk_proc_read;
-		pProcFile->write_proc = NULL;
-		pProcFile->nlink = 1;
-		pProcFile->size = sizeof(dev->name + 1);
-		pProcFile->data = (void *)pProcFile;
-		pProcFile->owner = THIS_MODULE;
+		pProcFile->read_proc   = sk_proc_read;
+		pProcFile->write_proc  = NULL;
+		pProcFile->nlink       = 1;
+		pProcFile->size        = sizeof(dev->name + 1);
+		pProcFile->data        = (void *)pProcFile;
+		pProcFile->owner       = THIS_MODULE;
 #endif
 
 		pNet->PortNr = 0;
-		pNet->NetNr = 0;
-
-
-#ifdef SK_ZEROCOPY
-#ifdef USE_SK_TX_CHECKSUM
-			if (pAC->ChipsetType) {
-				/* SG and ZEROCOPY - fly baby... */
-				dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
-			}
-#endif
-#endif
+		pNet->NetNr  = 0;
 
 		boards_found++;
 
@@ -802,23 +883,23 @@
 				break;
 			}
 
-			pAC->dev[1] = dev;
-			pNet = dev->priv;
-			pNet->PortNr = 1;
-			pNet->NetNr = 1;
-			pNet->pAC = pAC;
-			pNet->Mtu = 1500;
-			pNet->Up = 0;
-
-			dev->open =		&SkGeOpen;
-			dev->stop =		&SkGeClose;
-			dev->hard_start_xmit =	&SkGeXmit;
-			dev->get_stats =	&SkGeStats;
+			pAC->dev[1]   = dev;
+			pNet          = dev->priv;
+			pNet->PortNr  = 1;
+			pNet->NetNr   = 1;
+			pNet->pAC     = pAC;
+			pNet->Mtu     = 1500;
+			pNet->Up      = 0;
+
+			dev->open               = &SkGeOpen;
+			dev->stop               = &SkGeClose;
+			dev->hard_start_xmit    = &SkGeXmit;
+			dev->get_stats          = &SkGeStats;
 			dev->set_multicast_list = &SkGeSetRxMode;
-			dev->set_mac_address =	&SkGeSetMacAddr;
-			dev->do_ioctl =		&SkGeIoctl;
-			dev->change_mtu =	&SkGeChangeMtu;
-			dev->flags &= 		~IFF_RUNNING;
+			dev->set_mac_address    = &SkGeSetMacAddr;
+			dev->do_ioctl           = &SkGeIoctl;
+			dev->change_mtu         = &SkGeChangeMtu;
+			dev->flags             &= ~IFF_RUNNING;
 
 #ifdef SK_ZEROCOPY
 #ifdef USE_SK_TX_CHECKSUM
@@ -833,14 +914,12 @@
 			pProcFile = create_proc_entry(dev->name,
 				S_IFREG | S_IXUSR | S_IWGRP | S_IROTH,
 				pSkRootDir);
-
-		
-			pProcFile->read_proc = sk_proc_read;
+			pProcFile->read_proc  = sk_proc_read;
 			pProcFile->write_proc = NULL;
-			pProcFile->nlink = 1;
-			pProcFile->size = sizeof(dev->name + 1);
-			pProcFile->data = (void *)pProcFile;
-			pProcFile->owner = THIS_MODULE;
+			pProcFile->nlink      = 1;
+			pProcFile->size       = sizeof(dev->name + 1);
+			pProcFile->data       = (void *)pProcFile;
+			pProcFile->owner      = THIS_MODULE;
 #endif
 
 			memcpy((caddr_t) &dev->dev_addr,
@@ -848,14 +927,20 @@
 	
 			printk("%s: %s\n", dev->name, pAC->DeviceStr);
 			printk("      PrefPort:B  RlmtMode:Dual Check Link State\n");
-
 		}
 
-
 		/* Save the hardware revision */
 		pAC->HWRevision = (((pAC->GIni.GIPciHwRev >> 4) & 0x0F)*10) +
 			(pAC->GIni.GIPciHwRev & 0x0F);
 
+		/* Set driver globals */
+		pAC->Pnmi.pDriverFileName    = DRIVER_FILE_NAME;
+		pAC->Pnmi.pDriverReleaseDate = DRIVER_REL_DATE;
+
+		SK_MEMSET(&(pAC->PnmiBackup), 0, sizeof(SK_PNMI_STRUCT_DATA));
+		SK_MEMCPY(&(pAC->PnmiBackup), &(pAC->PnmiStruct), 
+				sizeof(SK_PNMI_STRUCT_DATA));
+
 		/*
 		 * This is bollocks, but we need to tell the net-init
 		 * code that it shall go for the next device.
@@ -875,7 +960,6 @@
 } /* skge_probe */
 
 
-
 /*****************************************************************************
  *
  * 	SkGeInitPCI - Init the PCI resources
@@ -1249,7 +1333,7 @@
 	SkAddrInit( pAC, pAC->IoBase, SK_INIT_DATA);
 	SkRlmtInit( pAC, pAC->IoBase, SK_INIT_DATA);
 	SkTimerInit(pAC, pAC->IoBase, SK_INIT_DATA);
-	
+
 	pAC->BoardLevel = SK_INIT_DATA;
 	pAC->RxBufSize  = ETH_BUF_SIZE;
 
@@ -1261,7 +1345,7 @@
 	/* level 1 init common modules here (HW init) */
 	spin_lock_irqsave(&pAC->SlowPathLock, Flags);
 	if (SkGeInit(pAC, pAC->IoBase, SK_INIT_IO) != 0) {
-		printk("HWInit (1) failed.\n");
+		printk("sk98lin: HWInit (1) failed.\n");
 		spin_unlock_irqrestore(&pAC->SlowPathLock, Flags);
 		return(-EAGAIN);
 	}
@@ -1293,14 +1377,14 @@
 		Ret = request_irq(dev->irq, SkGeIsrOnePort, SA_SHIRQ,
 			pAC->Name, dev);
 	} else {
-		printk(KERN_WARNING "%s: Illegal number of ports: %d\n",
-		       dev->name, pAC->GIni.GIMacsFound);
+		printk(KERN_WARNING "sk98lin: Illegal number of ports: %d\n",
+		       pAC->GIni.GIMacsFound);
 		return -EAGAIN;
 	}
 
 	if (Ret) {
-		printk(KERN_WARNING "%s: Requested IRQ %d is busy.\n",
-		       dev->name, dev->irq);
+		printk(KERN_WARNING "sk98lin: Requested IRQ %d is busy.\n",
+		       dev->irq);
 		return -EAGAIN;
 	}
 	pAC->AllocFlag |= SK_ALLOC_IRQ;
@@ -1328,25 +1412,10 @@
 		pAC->ActivePort,
 		DualNet)) {
 		BoardFreeMem(pAC);
-		printk("SkGeInitAssignRamToQueues failed.\n");
+		printk("sk98lin: SkGeInitAssignRamToQueues failed.\n");
 		return(-EAGAIN);
 	}
 
-	/* Print adapter specific string from vpd */
-	ProductStr(pAC);
-	printk("%s: %s\n", dev->name, pAC->DeviceStr);
-
-	/* Print configuration settings */
-	printk("      PrefPort:%c  RlmtMode:%s\n",
-		'A' + pAC->Rlmt.Net[0].Port[pAC->Rlmt.Net[0].PrefPort]->PortNumber,
-		(pAC->RlmtMode==0)  ? "Check Link State" :
-		((pAC->RlmtMode==1) ? "Check Link State" :
-		((pAC->RlmtMode==3) ? "Check Local Port" :
-		((pAC->RlmtMode==7) ? "Check Segmentation" :
-		((pAC->RlmtMode==17) ? "Dual Check Link State" :"Error")))));
-
-	SkGeYellowLED(pAC, pAC->IoBase, 1);
-
 	/*
 	 * Register the device here
 	 */
@@ -1904,9 +1973,17 @@
 	SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY,
 		("SkGeOpen: pAC=0x%lX:\n", (unsigned long)pAC));
 
+#ifdef SK_DIAG_SUPPORT
+	if (pAC->DiagModeActive == DIAG_ACTIVE) {
+		if (pAC->Pnmi.DiagAttached == SK_DIAG_RUNNING) {
+			return (-1);   /* still in use by diag; deny actions */
+		} 
+	}
+#endif
+
 
 	/* Set blink mode */
-	if (pAC->PciDev->vendor == 0x1186)
+	if ((pAC->PciDev->vendor == 0x1186) || (pAC->PciDev->vendor == 0x11ab ))
 		pAC->GIni.GILedBlinkCtrl = OEM_CONFIG_VALUE;
 
 	if (pAC->BoardLevel == SK_INIT_DATA) {
@@ -2001,26 +2078,50 @@
 static int SkGeClose(
 struct SK_NET_DEVICE	*dev)
 {
-	DEV_NET			*pNet;
-	SK_AC			*pAC;
+	DEV_NET		*pNet;
+	DEV_NET		*newPtrNet;
+	SK_AC		*pAC;
 
 	unsigned long	Flags;		/* for spin lock */
-	int				i;
-	int				PortIdx;
-	SK_EVPARA		EvPara;
+	int		i;
+	int		PortIdx;
+	SK_EVPARA	EvPara;
+
+	SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY,
+		("SkGeClose: pAC=0x%lX ", (unsigned long)pAC));
 
-	netif_stop_queue(dev);
 	pNet = (DEV_NET*) dev->priv;
 	pAC = pNet->pAC;
 
+#ifdef SK_DIAG_SUPPORT
+	if (pAC->DiagModeActive == DIAG_ACTIVE) {
+		if (pAC->DiagFlowCtrl == SK_FALSE) {
+			MOD_DEC_USE_COUNT;
+			/* 
+			** notify that the interface which has been closed
+			** by operator interaction must not be started up 
+			** again when the DIAG has finished. 
+			*/
+			newPtrNet = (DEV_NET *) pAC->dev[0]->priv;
+			if (newPtrNet == pNet) {
+				pAC->WasIfUp[0] = SK_FALSE;
+			} else {
+				pAC->WasIfUp[1] = SK_FALSE;
+			}
+			return 0; /* return to system everything is fine... */
+		} else {
+			pAC->DiagFlowCtrl = SK_FALSE;
+		}
+	}
+#endif
+
+	netif_stop_queue(dev);
+
 	if (pAC->RlmtNets == 1)
 		PortIdx = pAC->ActivePort;
 	else
 		PortIdx = pNet->NetNr;
 
-	SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY,
-		("SkGeClose: pAC=0x%lX ", (unsigned long)pAC));
-
         StopDrvCleanupTimer(pAC);
 
 	/*
@@ -2049,6 +2150,7 @@
 		EvPara.Para32[0] = pNet->NetNr;
 		EvPara.Para32[1] = -1;
 		SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_STOP, EvPara);
+		SkPnmiEvent(pAC, pAC->IoBase, SK_PNMI_EVT_XMAC_RESET, EvPara);
 		SkEventDispatcher(pAC, pAC->IoBase);
 		spin_unlock_irqrestore(&pAC->SlowPathLock, Flags);
 		
@@ -2078,6 +2180,10 @@
 	SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY,
 		("SkGeClose: done "));
 
+	SK_MEMSET(&(pAC->PnmiBackup), 0, sizeof(SK_PNMI_STRUCT_DATA));
+	SK_MEMCPY(&(pAC->PnmiBackup), &(pAC->PnmiStruct), 
+			sizeof(SK_PNMI_STRUCT_DATA));
+
 	pAC->MaxPorts--;
 	pNet->Up = 0;
 
@@ -2224,9 +2330,10 @@
 	** This is to resolve faulty padding by the HW with 0xaa bytes.
 	*/
 	if (BytesSend < C_LEN_ETHERNET_MINSIZE) {
-	    skb_put(pMessage, (C_LEN_ETHERNET_MINSIZE-BytesSend));
-	    SK_MEMSET( ((char *)(pMessage->data))+BytesSend,
-	            0, C_LEN_ETHERNET_MINSIZE-BytesSend);
+		if ((pMessage = skb_padto(pMessage, C_LEN_ETHERNET_MINSIZE)) == NULL) {
+			return 0;
+		}
+		pMessage->len = C_LEN_ETHERNET_MINSIZE;
 	}
 
 	/* 
@@ -3343,6 +3450,16 @@
 		return -EINVAL;
 	}
 
+#ifdef SK_DIAG_SUPPORT
+	if (pAC->DiagModeActive == DIAG_ACTIVE) {
+		if (pAC->DiagFlowCtrl == SK_FALSE) {
+			return -1; /* still in use, deny any actions of MTU */
+		} else {
+			pAC->DiagFlowCtrl = SK_FALSE;
+		}
+	}
+#endif
+
 	pNet->Mtu = NewMtu;
 	pOtherNet = (DEV_NET*)pAC->dev[1 - pNet->NetNr]->priv;
 	if ((pOtherNet->Mtu>1500) && (NewMtu<=1500) && (pOtherNet->Up==1)) {
@@ -3562,11 +3679,20 @@
 	SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY,
 		("SkGeStats starts now...\n"));
 	pPnmiStruct = &pAC->PnmiStruct;
-        memset(pPnmiStruct, 0, sizeof(SK_PNMI_STRUCT_DATA));
+
+#ifdef SK_DIAG_SUPPORT
+        if ((pAC->DiagModeActive == DIAG_NOTACTIVE) &&
+                (pAC->BoardLevel == SK_INIT_RUN)) {
+#endif
+        SK_MEMSET(pPnmiStruct, 0, sizeof(SK_PNMI_STRUCT_DATA));
         spin_lock_irqsave(&pAC->SlowPathLock, Flags);
         Size = SK_PNMI_STRUCT_SIZE;
 		SkPnmiGetStruct(pAC, pAC->IoBase, pPnmiStruct, &Size, pNet->NetNr);
         spin_unlock_irqrestore(&pAC->SlowPathLock, Flags);
+#ifdef SK_DIAG_SUPPORT
+	}
+#endif
+
         pPnmiStat = &pPnmiStruct->Stat[0];
         pPnmiConf = &pPnmiStruct->Conf[0];
 
@@ -3629,7 +3755,7 @@
 DEV_NET		*pNet;
 SK_AC		*pAC;
 void		*pMemBuf;
-
+struct pci_dev  *pdev = NULL;
 SK_GE_IOCTL	Ioctl;
 unsigned int	Err = 0;
 int		Size = 0;
@@ -3696,6 +3822,45 @@
 fault_gen:
 		kfree(pMemBuf); /* cleanup everything */
 		break;
+#ifdef SK_DIAG_SUPPORT
+       case SK_IOCTL_DIAG:
+		if (!capable(CAP_NET_ADMIN)) return -EPERM;
+		if (Ioctl.Len < (sizeof(pAC->PnmiStruct) + HeaderLength)) {
+			Length = Ioctl.Len;
+		} else {
+			Length = sizeof(pAC->PnmiStruct) + HeaderLength;
+		}
+		if (NULL == (pMemBuf = kmalloc(Length, GFP_KERNEL))) {
+			return -ENOMEM;
+		}
+		if(copy_from_user(pMemBuf, Ioctl.pData, Length)) {
+			Err = -EFAULT;
+			goto fault_diag;
+		}
+		pdev = pAC->PciDev;
+		Length = 3 * sizeof(SK_U32);  /* Error, Bus and Device */
+		/* 
+		** While coding this new IOCTL interface, only a few lines of code
+		** are to to be added. Therefore no dedicated function has been 
+		** added. If more functionality is added, a separate function 
+		** should be used...
+		*/
+		* ((SK_U32 *)pMemBuf) = 0;
+		* ((SK_U32 *)pMemBuf + 1) = pdev->bus->number;
+		* ((SK_U32 *)pMemBuf + 2) = ParseDeviceNbrFromSlotName(pdev->slot_name);
+		if(copy_to_user(Ioctl.pData, pMemBuf, Length) ) {
+			Err = -EFAULT;
+			goto fault_diag;
+		}
+		Ioctl.Len = Length;
+		if(copy_to_user(rq->ifr_data, &Ioctl, sizeof(SK_GE_IOCTL))) {
+			Err = -EFAULT;
+			goto fault_diag;
+		}
+fault_diag:
+		kfree(pMemBuf); /* cleanup everything */
+		break;
+#endif
 	default:
 		Err = -EOPNOTSUPP;
 	}
@@ -3851,10 +4016,9 @@
 				(strcmp(ConType[pAC->Index],"Auto")!=0) &&
 				(strcmp(ConType[pAC->Index],"")!=0)) {
 				/* Set the speed parameter back */
-					printk("%s: Illegal value \"%s\" " 
+					printk("sk98lin: Illegal value \"%s\" " 
 							"for ConType."
 							" Using Auto.\n", 
-							pAC->dev[0]->name, 
 							ConType[pAC->Index]);
 
 					sprintf(ConType[pAC->Index], "Auto");	
@@ -3898,8 +4062,8 @@
 			M_CurrPort.PLinkSpeed    = SK_LSPEED_10MBPS;
 		    }
                 } else { 
-		    printk("%s: Illegal value \"%s\" for ConType\n", 
-			pAC->dev[0]->name, ConType[pAC->Index]);
+		    printk("sk98lin: Illegal value \"%s\" for ConType\n", 
+			ConType[pAC->Index]);
 		    IsConTypeDefined = SK_FALSE; /* Wrong ConType defined */
 		}
         } else {
@@ -3923,8 +4087,8 @@
 		} else if (strcmp(Speed_A[pAC->Index],"1000")==0) {
 		    LinkSpeed = SK_LSPEED_1000MBPS;
 		} else {
-		    printk("%s: Illegal value \"%s\" for Speed_A\n",
-			pAC->dev[0]->name, Speed_A[pAC->Index]);
+		    printk("sk98lin: Illegal value \"%s\" for Speed_A\n",
+			Speed_A[pAC->Index]);
 		    IsLinkSpeedDefined = SK_FALSE;
 		}
 	} else {
@@ -3938,9 +4102,9 @@
 	if (((!pAC->ChipsetType) || (pAC->GIni.GICopperType != SK_TRUE)) &&
 		((LinkSpeed != SK_LSPEED_AUTO) &&
 		(LinkSpeed != SK_LSPEED_1000MBPS))) {
-		printk("%s: Illegal value for Speed_A. "
+		printk("sk98lin: Illegal value for Speed_A. "
 			"Not a copper card or GE V2 card\n    Using "
-			"speed 1000\n", pAC->dev[0]->name);
+			"speed 1000\n");
 		LinkSpeed = SK_LSPEED_1000MBPS;
 	}
 	
@@ -3970,8 +4134,8 @@
 		} else if (strcmp(AutoNeg_A[pAC->Index],"Sense")==0) {
 		    AutoNeg = AN_SENS;
 		} else {
-		    printk("%s: Illegal value \"%s\" for AutoNeg_A\n",
-			pAC->dev[0]->name, AutoNeg_A[pAC->Index]);
+		    printk("sk98lin: Illegal value \"%s\" for AutoNeg_A\n",
+			AutoNeg_A[pAC->Index]);
 		}
 	}
 
@@ -3989,33 +4153,32 @@
 		} else if (strcmp(DupCap_A[pAC->Index],"Half")==0) {
 		    DuplexCap = DC_HALF;
 		} else {
-		    printk("%s: Illegal value \"%s\" for DupCap_A\n",
-			pAC->dev[0]->name, DupCap_A[pAC->Index]);
+		    printk("sk98lin: Illegal value \"%s\" for DupCap_A\n",
+			DupCap_A[pAC->Index]);
 		}
 	}
-	
+
 	/* 
 	** Check for illegal combinations 
 	*/
-	if ((LinkSpeed = SK_LSPEED_1000MBPS) &&
+	if ((LinkSpeed == SK_LSPEED_1000MBPS) &&
 		((DuplexCap == SK_LMODE_STAT_AUTOHALF) ||
 		(DuplexCap == SK_LMODE_STAT_HALF)) &&
 		(pAC->ChipsetType)) {
-		    printk("%s: Half Duplex not possible with Gigabit speed!\n"
-					"    Using Full Duplex.\n",
-				pAC->dev[0]->name);
+		    printk("sk98lin: Half Duplex not possible with Gigabit speed!\n"
+					"    Using Full Duplex.\n");
 				DuplexCap = DC_FULL;
 	}
 
 	if ( AutoSet && AutoNeg==AN_SENS && DupSet) {
-		printk("%s, Port A: DuplexCapabilities"
-			" ignored using Sense mode\n", pAC->dev[0]->name);
+		printk("sk98lin, Port A: DuplexCapabilities"
+			" ignored using Sense mode\n");
 	}
 
 	if (AutoSet && AutoNeg==AN_OFF && DupSet && DuplexCap==DC_BOTH){
-		printk("%s, Port A: Illegal combination"
+		printk("sk98lin: Port A: Illegal combination"
 			" of values AutoNeg. and DuplexCap.\n    Using "
-			"Full Duplex\n", pAC->dev[0]->name);
+			"Full Duplex\n");
 		DuplexCap = DC_FULL;
 	}
 
@@ -4024,10 +4187,9 @@
 	}
 	
 	if (!AutoSet && DupSet) {
-		printk("%s, Port A: Duplex setting not"
+		printk("sk98lin: Port A: Duplex setting not"
 			" possible in\n    default AutoNegotiation mode"
-			" (Sense).\n    Using AutoNegotiation On\n",
-			pAC->dev[0]->name);
+			" (Sense).\n    Using AutoNegotiation On\n");
 		AutoNeg = AN_ON;
 	}
 	
@@ -4054,8 +4216,8 @@
 		} else if (strcmp(FlowCtrl_A[pAC->Index],"None")==0) {
 		    FlowCtrl = SK_FLOW_MODE_NONE;
 		} else {
-		    printk("%s: Illegal value \"%s\" for FlowCtrl_A\n",
-                        pAC->dev[0]->name, FlowCtrl_A[pAC->Index]);
+		    printk("sk98lin: Illegal value \"%s\" for FlowCtrl_A\n",
+                        FlowCtrl_A[pAC->Index]);
 		    IsFlowCtrlDefined = SK_FALSE;
 		}
 	} else {
@@ -4064,9 +4226,9 @@
 
 	if (IsFlowCtrlDefined) {
 	    if ((AutoNeg == AN_OFF) && (FlowCtrl != SK_FLOW_MODE_NONE)) {
-		printk("%s, Port A: FlowControl"
+		printk("sk98lin: Port A: FlowControl"
 			" impossible without AutoNegotiation,"
-			" disabled\n", pAC->dev[0]->name);
+			" disabled\n");
 		FlowCtrl = SK_FLOW_MODE_NONE;
 	    }
 	    pAC->GIni.GP[0].PFlowCtrlMode = FlowCtrl;
@@ -4086,8 +4248,8 @@
 		} else if (strcmp(Role_A[pAC->Index],"Slave")==0) {
 		    MSMode = SK_MS_MODE_SLAVE;
 		} else {
-		    printk("%s: Illegal value \"%s\" for Role_A\n",
-			pAC->dev[0]->name, Role_A[pAC->Index]);
+		    printk("sk98lin: Illegal value \"%s\" for Role_A\n",
+			Role_A[pAC->Index]);
 		    IsRoleDefined = SK_FALSE;
 		}
 	} else {
@@ -4122,8 +4284,8 @@
 		} else if (strcmp(Speed_B[pAC->Index],"1000")==0) {
 		    LinkSpeed = SK_LSPEED_1000MBPS;
 		} else {
-		    printk("%s: Illegal value \"%s\" for Speed_B\n",
-			pAC->dev[1]->name, Speed_B[pAC->Index]);
+		    printk("sk98lin: Illegal value \"%s\" for Speed_B\n",
+			Speed_B[pAC->Index]);
 		    IsLinkSpeedDefined = SK_FALSE;
 		}
 	} else {
@@ -4137,9 +4299,9 @@
 	if (((!pAC->ChipsetType) || (pAC->GIni.GICopperType != SK_TRUE)) &&
 		((LinkSpeed != SK_LSPEED_AUTO) &&
 		(LinkSpeed != SK_LSPEED_1000MBPS))) {
-		printk("%s: Illegal value for Speed_B. "
+		printk("sk98lin: Illegal value for Speed_B. "
 			"Not a copper card or GE V2 card\n    Using "
-			"speed 1000\n", pAC->dev[1]->name);
+			"speed 1000\n");
 		LinkSpeed = SK_LSPEED_1000MBPS;
 	}
 
@@ -4169,8 +4331,8 @@
 		} else if (strcmp(AutoNeg_B[pAC->Index],"Sense")==0) {
 		    AutoNeg = AN_SENS;
 		} else {
-		    printk("%s: Illegal value \"%s\" for AutoNeg_B\n",
-			pAC->dev[0]->name, AutoNeg_B[pAC->Index]);
+		    printk("sk98lin: Illegal value \"%s\" for AutoNeg_B\n",
+			AutoNeg_B[pAC->Index]);
 		}
 	}
 
@@ -4188,8 +4350,8 @@
 		} else if (strcmp(DupCap_B[pAC->Index],"Half")==0) {
 		    DuplexCap = DC_HALF;
 		} else {
-		    printk("%s: Illegal value \"%s\" for DupCap_B\n",
-			pAC->dev[0]->name, DupCap_B[pAC->Index]);
+		    printk("sk98lin: Illegal value \"%s\" for DupCap_B\n",
+			DupCap_B[pAC->Index]);
 		}
 	}
 
@@ -4197,25 +4359,24 @@
 	/* 
 	** Check for illegal combinations 
 	*/
-	if ((LinkSpeed = SK_LSPEED_1000MBPS) &&
+	if ((LinkSpeed == SK_LSPEED_1000MBPS) &&
 		((DuplexCap == SK_LMODE_STAT_AUTOHALF) ||
 		(DuplexCap == SK_LMODE_STAT_HALF)) &&
 		(pAC->ChipsetType)) {
-		    printk("%s: Half Duplex not possible with Gigabit speed!\n"
-					"    Using Full Duplex.\n",
-				pAC->dev[1]->name);
+		    printk("sk98lin: Half Duplex not possible with Gigabit speed!\n"
+					"    Using Full Duplex.\n");
 				DuplexCap = DC_FULL;
 	}
 
 	if (AutoSet && AutoNeg==AN_SENS && DupSet) {
-		printk("%s, Port B: DuplexCapabilities"
-			" ignored using Sense mode\n", pAC->dev[1]->name);
+		printk("sk98lin, Port B: DuplexCapabilities"
+			" ignored using Sense mode\n");
 	}
 
 	if (AutoSet && AutoNeg==AN_OFF && DupSet && DuplexCap==DC_BOTH){
-		printk("%s, Port B: Illegal combination"
+		printk("sk98lin: Port B: Illegal combination"
 			" of values AutoNeg. and DuplexCap.\n    Using "
-			"Full Duplex\n", pAC->dev[1]->name);
+			"Full Duplex\n");
 		DuplexCap = DC_FULL;
 	}
 
@@ -4224,10 +4385,9 @@
 	}
 	
 	if (!AutoSet && DupSet) {
-		printk("%s, Port B: Duplex setting not"
+		printk("sk98lin: Port B: Duplex setting not"
 			" possible in\n    default AutoNegotiation mode"
-			" (Sense).\n    Using AutoNegotiation On\n",
-			pAC->dev[1]->name);
+			" (Sense).\n    Using AutoNegotiation On\n");
 		AutoNeg = AN_ON;
 	}
 
@@ -4254,8 +4414,8 @@
 		} else if (strcmp(FlowCtrl_B[pAC->Index],"None")==0) {
 		    FlowCtrl = SK_FLOW_MODE_NONE;
 		} else {
-		    printk("%s: Illegal value \"%s\" for FlowCtrl_B\n",
-			pAC->dev[0]->name, FlowCtrl_B[pAC->Index]);
+		    printk("sk98lin: Illegal value \"%s\" for FlowCtrl_B\n",
+			FlowCtrl_B[pAC->Index]);
 		    IsFlowCtrlDefined = SK_FALSE;
 		}
 	} else {
@@ -4264,9 +4424,9 @@
 
 	if (IsFlowCtrlDefined) {
 	    if ((AutoNeg == AN_OFF) && (FlowCtrl != SK_FLOW_MODE_NONE)) {
-		printk("%s, Port B: FlowControl"
+		printk("sk98lin: Port B: FlowControl"
 			" impossible without AutoNegotiation,"
-			" disabled\n", pAC->dev[1]->name);
+			" disabled\n");
 		FlowCtrl = SK_FLOW_MODE_NONE;
 	    }
 	    pAC->GIni.GP[1].PFlowCtrlMode = FlowCtrl;
@@ -4286,8 +4446,8 @@
 		} else if (strcmp(Role_B[pAC->Index],"Slave")==0) {
 		    MSMode = SK_MS_MODE_SLAVE;
 		} else {
-		    printk("%s: Illegal value \"%s\" for Role_B\n",
-			pAC->dev[1]->name, Role_B[pAC->Index]);
+		    printk("sk98lin: Illegal value \"%s\" for Role_B\n",
+			Role_B[pAC->Index]);
 		    IsRoleDefined = SK_FALSE;
 		}
 	} else {
@@ -4305,28 +4465,37 @@
 	if (PrefPort != NULL && pAC->Index<SK_MAX_CARD_PARAM &&
 		PrefPort[pAC->Index] != NULL) {
 		if (strcmp(PrefPort[pAC->Index],"") == 0) { /* Auto */
-		    pAC->ActivePort             =  0;
-		    pAC->Rlmt.Net[0].Preference = -1; /* auto */
-		    pAC->Rlmt.Net[0].PrefPort   =  0;
+			pAC->ActivePort             =  0;
+			pAC->Rlmt.Net[0].Preference = -1; /* auto */
+			pAC->Rlmt.Net[0].PrefPort   =  0;
 		} else if (strcmp(PrefPort[pAC->Index],"A") == 0) {
-		    /*
-		    ** do not set ActivePort here, thus a port
-		    ** switch is issued after net up.
-		    */
-		    Port                        = 0;
-		    pAC->Rlmt.Net[0].Preference = Port;
-		    pAC->Rlmt.Net[0].PrefPort   = Port;
+			/*
+			** do not set ActivePort here, thus a port
+			** switch is issued after net up.
+			*/
+			Port                        = 0;
+			pAC->Rlmt.Net[0].Preference = Port;
+			pAC->Rlmt.Net[0].PrefPort   = Port;
 		} else if (strcmp(PrefPort[pAC->Index],"B") == 0) {
-		    /*
-		    ** do not set ActivePort here, thus a port
-		    ** switch is issued after net up.
-		    */
-		    Port                        = 1;
-		    pAC->Rlmt.Net[0].Preference = Port;
-		    pAC->Rlmt.Net[0].PrefPort   = Port;
+			/*
+			** do not set ActivePort here, thus a port
+			** switch is issued after net up.
+			*/
+			if (pAC->GIni.GIMacsFound == 1) {
+				printk("sk98lin: Illegal value \"B\" for PrefPort.\n"
+					"      Port B not available on single port adapters.\n");
+
+				pAC->ActivePort             =  0;
+				pAC->Rlmt.Net[0].Preference = -1; /* auto */
+				pAC->Rlmt.Net[0].PrefPort   =  0;
+			} else {
+				Port                        = 1;
+				pAC->Rlmt.Net[0].Preference = Port;
+				pAC->Rlmt.Net[0].PrefPort   = Port;
+			}
 		} else {
-		    printk("%s: Illegal value \"%s\" for PrefPort\n",
-			pAC->dev[0]->name, PrefPort[pAC->Index]);
+		    printk("sk98lin: Illegal value \"%s\" for PrefPort\n",
+			PrefPort[pAC->Index]);
 		}
 	}
 
@@ -4350,9 +4519,9 @@
 			pAC->RlmtMode = SK_RLMT_CHECK_LINK;
 			pAC->RlmtNets = 2;
 		} else {
-		    printk("%s: Illegal value \"%s\" for"
+		    printk("sk98lin: Illegal value \"%s\" for"
 			" RlmtMode, using default\n", 
-			pAC->dev[0]->name, RlmtMode[pAC->Index]);
+			RlmtMode[pAC->Index]);
 			pAC->RlmtMode = 0;
 		}
 	} else {
@@ -4363,97 +4532,111 @@
 	** Check the interrupt moderation parameters
 	*/
 	if (Moderation[pAC->Index] != NULL) {
-	    if (strcmp(Moderation[pAC->Index], "Static") == 0) {
-                pAC->DynIrqModInfo.IntModTypeSelect = C_INT_MOD_STATIC;
-	    } else if (strcmp(Moderation[pAC->Index], "Dynamic") == 0) {
-	        pAC->DynIrqModInfo.IntModTypeSelect = C_INT_MOD_DYNAMIC;
-	    } else {
-	        pAC->DynIrqModInfo.IntModTypeSelect = C_INT_MOD_NONE;
-	    }
+		if (strcmp(Moderation[pAC->Index], "") == 0) {
+			pAC->DynIrqModInfo.IntModTypeSelect = C_INT_MOD_NONE;
+		} else if (strcmp(Moderation[pAC->Index], "Static") == 0) {
+			pAC->DynIrqModInfo.IntModTypeSelect = C_INT_MOD_STATIC;
+		} else if (strcmp(Moderation[pAC->Index], "Dynamic") == 0) {
+			pAC->DynIrqModInfo.IntModTypeSelect = C_INT_MOD_DYNAMIC;
+		} else if (strcmp(Moderation[pAC->Index], "None") == 0) {
+			pAC->DynIrqModInfo.IntModTypeSelect = C_INT_MOD_NONE;
+		} else {
+	   		printk("sk98lin: Illegal value \"%s\" for Moderation.\n"
+				"      Disable interrupt moderation.\n",
+				Moderation[pAC->Index]);
+			pAC->DynIrqModInfo.IntModTypeSelect = C_INT_MOD_NONE;
+		}
 	} else {
-	    pAC->DynIrqModInfo.IntModTypeSelect = C_INT_MOD_NONE;
+		pAC->DynIrqModInfo.IntModTypeSelect = C_INT_MOD_NONE;
 	}
 
 	if (Stats[pAC->Index] != NULL) {
-	    if (strcmp(Stats[pAC->Index], "Yes") == 0) {
-	        pAC->DynIrqModInfo.DisplayStats = SK_TRUE;
-	    } else {
-		pAC->DynIrqModInfo.DisplayStats = SK_FALSE;
-	    }
+		if (strcmp(Stats[pAC->Index], "Yes") == 0) {
+			pAC->DynIrqModInfo.DisplayStats = SK_TRUE;
+		} else {
+			pAC->DynIrqModInfo.DisplayStats = SK_FALSE;
+		}
 	} else {
-	    pAC->DynIrqModInfo.DisplayStats = SK_FALSE;
+		pAC->DynIrqModInfo.DisplayStats = SK_FALSE;
 	}
 
-        if (ModerationMask[pAC->Index] != NULL) {
-           if (strcmp(ModerationMask[pAC->Index], "Rx") == 0) {
-               pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_RX_ONLY;
-           } else if (strcmp(ModerationMask[pAC->Index], "Tx") == 0) {
-               pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_TX_ONLY;
-           } else if (strcmp(ModerationMask[pAC->Index], "Sp") == 0) {
-               pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_SP_ONLY;
-           } else if (strcmp(ModerationMask[pAC->Index], "RxSp") == 0) {
-               pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_SP_RX;
-           } else if (strcmp(ModerationMask[pAC->Index], "SpRx") == 0) {
-               pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_SP_RX;
-           } else if (strcmp(ModerationMask[pAC->Index], "RxTx") == 0) {
-               pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_TX_RX;
-           } else if (strcmp(ModerationMask[pAC->Index], "TxRx") == 0) {
-               pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_TX_RX;
-           } else if (strcmp(ModerationMask[pAC->Index], "TxSp") == 0) {
-               pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_SP_TX;
-           } else if (strcmp(ModerationMask[pAC->Index], "SpTx") == 0) {
-               pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_SP_TX;
-           } else if (strcmp(ModerationMask[pAC->Index], "RxTxSp") == 0) {
-               pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_RX_TX_SP;
-           } else if (strcmp(ModerationMask[pAC->Index], "RxSpTx") == 0) {
-               pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_RX_TX_SP;
-           } else if (strcmp(ModerationMask[pAC->Index], "TxRxSp") == 0) {
-               pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_RX_TX_SP;
-           } else if (strcmp(ModerationMask[pAC->Index], "TxSpRx") == 0) {
-               pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_RX_TX_SP;
-           } else if (strcmp(ModerationMask[pAC->Index], "SpTxRx") == 0) {
-               pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_RX_TX_SP;
-           } else if (strcmp(ModerationMask[pAC->Index], "SpRxTx") == 0) {
-               pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_RX_TX_SP;
-           } else { /* some rubbish */
-               pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_RX_ONLY;
-           }
-        } else {  /* operator has stated nothing */
-           pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_TX_RX;
-        }
-
-        if (AutoSizing[pAC->Index] != NULL) {
-           if (strcmp(AutoSizing[pAC->Index], "On") == 0) {
-               pAC->DynIrqModInfo.AutoSizing = SK_FALSE;
-           } else {
-               pAC->DynIrqModInfo.AutoSizing = SK_FALSE;
-           }
-        } else {  /* operator has stated nothing */
-           pAC->DynIrqModInfo.AutoSizing = SK_FALSE;
-        }
+	if (ModerationMask[pAC->Index] != NULL) {
+		if (strcmp(ModerationMask[pAC->Index], "Rx") == 0) {
+			pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_RX_ONLY;
+		} else if (strcmp(ModerationMask[pAC->Index], "Tx") == 0) {
+			pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_TX_ONLY;
+		} else if (strcmp(ModerationMask[pAC->Index], "Sp") == 0) {
+			pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_SP_ONLY;
+		} else if (strcmp(ModerationMask[pAC->Index], "RxSp") == 0) {
+			pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_SP_RX;
+		} else if (strcmp(ModerationMask[pAC->Index], "SpRx") == 0) {
+			pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_SP_RX;
+		} else if (strcmp(ModerationMask[pAC->Index], "RxTx") == 0) {
+			pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_TX_RX;
+		} else if (strcmp(ModerationMask[pAC->Index], "TxRx") == 0) {
+			pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_TX_RX;
+		} else if (strcmp(ModerationMask[pAC->Index], "TxSp") == 0) {
+			pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_SP_TX;
+		} else if (strcmp(ModerationMask[pAC->Index], "SpTx") == 0) {
+			pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_SP_TX;
+		} else if (strcmp(ModerationMask[pAC->Index], "RxTxSp") == 0) {
+			pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_RX_TX_SP;
+		} else if (strcmp(ModerationMask[pAC->Index], "RxSpTx") == 0) {
+			pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_RX_TX_SP;
+		} else if (strcmp(ModerationMask[pAC->Index], "TxRxSp") == 0) {
+			pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_RX_TX_SP;
+		} else if (strcmp(ModerationMask[pAC->Index], "TxSpRx") == 0) {
+			pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_RX_TX_SP;
+		} else if (strcmp(ModerationMask[pAC->Index], "SpTxRx") == 0) {
+			pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_RX_TX_SP;
+		} else if (strcmp(ModerationMask[pAC->Index], "SpRxTx") == 0) {
+			pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_RX_TX_SP;
+		} else { /* some rubbish */
+			pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_RX_ONLY;
+		}
+	} else {  /* operator has stated nothing */
+		pAC->DynIrqModInfo.MaskIrqModeration = IRQ_MASK_TX_RX;
+	}
+
+	if (AutoSizing[pAC->Index] != NULL) {
+		if (strcmp(AutoSizing[pAC->Index], "On") == 0) {
+			pAC->DynIrqModInfo.AutoSizing = SK_FALSE;
+		} else {
+			pAC->DynIrqModInfo.AutoSizing = SK_FALSE;
+		}
+	} else {  /* operator has stated nothing */
+		pAC->DynIrqModInfo.AutoSizing = SK_FALSE;
+	}
 
-        if (IntsPerSec[pAC->Index] != 0) {
-           if ((IntsPerSec[pAC->Index]< 30) || (IntsPerSec[pAC->Index]> 40000)) {
-              pAC->DynIrqModInfo.MaxModIntsPerSec = C_INTS_PER_SEC_DEFAULT;
-           } else {
-              pAC->DynIrqModInfo.MaxModIntsPerSec = IntsPerSec[pAC->Index];
-           }
-        } else {
-           pAC->DynIrqModInfo.MaxModIntsPerSec = C_INTS_PER_SEC_DEFAULT;
-        }
+	if (IntsPerSec[pAC->Index] != 0) {
+		if ((IntsPerSec[pAC->Index]< C_INT_MOD_IPS_LOWER_RANGE) || 
+			(IntsPerSec[pAC->Index] > C_INT_MOD_IPS_UPPER_RANGE)) {
+	   		printk("sk98lin: Illegal value \"%d\" for IntsPerSec. (Range: %d - %d)\n"
+				"      Using default value of %i.\n", 
+				IntsPerSec[pAC->Index],
+				C_INT_MOD_IPS_LOWER_RANGE,
+				C_INT_MOD_IPS_UPPER_RANGE,
+				C_INTS_PER_SEC_DEFAULT);
+			pAC->DynIrqModInfo.MaxModIntsPerSec = C_INTS_PER_SEC_DEFAULT;
+		} else {
+			pAC->DynIrqModInfo.MaxModIntsPerSec = IntsPerSec[pAC->Index];
+		}
+	} else {
+		pAC->DynIrqModInfo.MaxModIntsPerSec = C_INTS_PER_SEC_DEFAULT;
+	}
 
-        /*
+	/*
 	** Evaluate upper and lower moderation threshold
 	*/
-        pAC->DynIrqModInfo.MaxModIntsPerSecUpperLimit =
-            pAC->DynIrqModInfo.MaxModIntsPerSec +
-            (pAC->DynIrqModInfo.MaxModIntsPerSec / 2);
-
-        pAC->DynIrqModInfo.MaxModIntsPerSecLowerLimit =
-            pAC->DynIrqModInfo.MaxModIntsPerSec -
-            (pAC->DynIrqModInfo.MaxModIntsPerSec / 2);
+	pAC->DynIrqModInfo.MaxModIntsPerSecUpperLimit =
+		pAC->DynIrqModInfo.MaxModIntsPerSec +
+		(pAC->DynIrqModInfo.MaxModIntsPerSec / 2);
+
+	pAC->DynIrqModInfo.MaxModIntsPerSecLowerLimit =
+		pAC->DynIrqModInfo.MaxModIntsPerSec -
+		(pAC->DynIrqModInfo.MaxModIntsPerSec / 2);
 
-        pAC->DynIrqModInfo.PrevTimeVal = jiffies;  /* initial value */
+	pAC->DynIrqModInfo.PrevTimeVal = jiffies;  /* initial value */
 
 
 } /* GetConfiguration */
@@ -4816,6 +4999,7 @@
 		spin_lock_irqsave(
 			&pAC->TxPort[FromPort][TX_PRIO_LOW].TxDesRingLock,
 			Flags);
+
 		SkGeStopPort(pAC, IoC, FromPort, SK_STOP_ALL, SK_HARD_RST);
 		pAC->dev[Param.Para32[0]]->flags &= ~IFF_RUNNING;
 		spin_unlock_irqrestore(
@@ -4851,6 +5035,10 @@
 		FromPort = Param.Para32[0];
 		SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_EVENT,
 			("NET UP EVENT, Port: %d ", Param.Para32[0]));
+		/* Mac update */
+		SkAddrMcUpdate(pAC,IoC, FromPort);
+
+		if (DoPrintInterfaceChange) {
 		printk("%s: network connection up using"
 			" port %c\n", pAC->dev[Param.Para32[0]]->name, 'A'+Param.Para32[0]);
 
@@ -4866,8 +5054,6 @@
 			printk("    speed:           unknown\n");
 		}
 
-		/* Mac update */
-		SkAddrMcUpdate(pAC,IoC, FromPort);
 
 		Stat = pAC->GIni.GP[FromPort].PLinkModeStatus;
 		if (Stat == SK_LMODE_STAT_AUTOHALF ||
@@ -4945,6 +5131,9 @@
 			printk("    rx-checksum:     disabled\n");
 #endif
 
+		} else {
+                        DoPrintInterfaceChange = SK_TRUE;
+                }
 	
 		if ((Param.Para32[0] != pAC->ActivePort) &&
 			(pAC->RlmtNets == 1)) {
@@ -4962,7 +5151,12 @@
 		/* action list 7 */
 		SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_EVENT,
 			("NET DOWN EVENT "));
-		printk("%s: network connection down\n", pAC->dev[Param.Para32[1]]->name);
+		if (DoPrintInterfaceChange) {
+			printk("%s: network connection down\n", 
+				pAC->dev[Param.Para32[1]]->name);
+		} else {
+			DoPrintInterfaceChange = SK_TRUE;
+		}
 		pAC->dev[Param.Para32[1]]->flags &= ~IFF_RUNNING;
 		break;
 	case SK_DRV_SWITCH_HARD: /* SK_U32 FromPortIdx SK_U32 ToPortIdx */
@@ -5147,6 +5341,229 @@
 
 } /* SkErrorLog */
 
+#ifdef SK_DIAG_SUPPORT
+
+/*****************************************************************************
+ *
+ *	SkDrvEnterDiagMode - handles DIAG attach request
+ *
+ * Description:
+ *	Notify the kernel to NOT access the card any longer due to DIAG
+ *	Deinitialize the Card
+ *
+ * Returns:
+ *	int
+ */
+int SkDrvEnterDiagMode(
+SK_AC   *pAc)   /* pointer to adapter context */
+{
+	SK_AC   *pAC  = NULL;
+	DEV_NET *pNet = NULL;
+
+	pNet = (DEV_NET *) pAc->dev[0]->priv;
+	pAC = pNet->pAC;
+
+	SK_MEMCPY(&(pAc->PnmiBackup), &(pAc->PnmiStruct), 
+			sizeof(SK_PNMI_STRUCT_DATA));
+
+	pAC->DiagModeActive = DIAG_ACTIVE;
+	if (pAC->BoardLevel > SK_INIT_DATA) {
+		if (pNet->Up) {
+			pAC->WasIfUp[0] = SK_TRUE;
+			pAC->DiagFlowCtrl = SK_TRUE; /* for SkGeClose      */
+			DoPrintInterfaceChange = SK_FALSE;
+			SkDrvDeInitAdapter(pAC, 0);  /* performs SkGeClose */
+		} else {
+			pAC->WasIfUp[0] = SK_FALSE;
+		}
+		if (pNet != (DEV_NET *) pAc->dev[1]->priv) {
+			pNet = (DEV_NET *) pAc->dev[1]->priv;
+			if (pNet->Up) {
+				pAC->WasIfUp[1] = SK_TRUE;
+				pAC->DiagFlowCtrl = SK_TRUE; /* for SkGeClose */
+				DoPrintInterfaceChange = SK_FALSE;
+				SkDrvDeInitAdapter(pAC, 1);  /* do SkGeClose  */
+			} else {
+				pAC->WasIfUp[1] = SK_FALSE;
+			}
+		}
+		pAC->BoardLevel = SK_INIT_DATA;
+	}
+	return(0);
+}
+
+/*****************************************************************************
+ *
+ *	SkDrvLeaveDiagMode - handles DIAG detach request
+ *
+ * Description:
+ *	Notify the kernel to may access the card again after use by DIAG
+ *	Initialize the Card
+ *
+ * Returns:
+ * 	int
+ */
+int SkDrvLeaveDiagMode(
+SK_AC   *pAc)   /* pointer to adapter control context */
+{ 
+	SK_MEMCPY(&(pAc->PnmiStruct), &(pAc->PnmiBackup), 
+			sizeof(SK_PNMI_STRUCT_DATA));
+	pAc->DiagModeActive    = DIAG_NOTACTIVE;
+	pAc->Pnmi.DiagAttached = SK_DIAG_IDLE;
+        if (pAc->WasIfUp[0] == SK_TRUE) {
+                pAc->DiagFlowCtrl = SK_TRUE; /* for SkGeClose */
+		DoPrintInterfaceChange = SK_FALSE;
+                SkDrvInitAdapter(pAc, 0);    /* first device  */
+        }
+        if (pAc->WasIfUp[1] == SK_TRUE) {
+                pAc->DiagFlowCtrl = SK_TRUE; /* for SkGeClose */
+		DoPrintInterfaceChange = SK_FALSE;
+                SkDrvInitAdapter(pAc, 1);    /* second device */
+        }
+	return(0);
+}
+
+/*****************************************************************************
+ *
+ *	ParseDeviceNbrFromSlotName - Evaluate PCI device number
+ *
+ * Description:
+ * 	This function parses the PCI slot name information string and will
+ *	retrieve the devcie number out of it. The slot_name maintianed by
+ *	linux is in the form of '02:0a.0', whereas the first two characters 
+ *	represent the bus number in hex (in the sample above this is 
+ *	pci bus 0x02) and the next two characters the device number (0x0a).
+ *
+ * Returns:
+ *	SK_U32: The device number from the PCI slot name
+ */ 
+
+static SK_U32 ParseDeviceNbrFromSlotName(
+const char *SlotName)   /* pointer to pci slot name eg. '02:0a.0' */
+{
+	char	*CurrCharPos	= (char *) SlotName;
+	int	FirstNibble	= -1;
+	int	SecondNibble	= -1;
+	SK_U32	Result		=  0;
+
+	while (*CurrCharPos != '\0') {
+		if (*CurrCharPos == ':') { 
+			while (*CurrCharPos != '.') {
+				CurrCharPos++;  
+				if (	(*CurrCharPos >= '0') && 
+					(*CurrCharPos <= '9')) {
+					if (FirstNibble == -1) {
+						/* dec. value for '0' */
+						FirstNibble = *CurrCharPos - 48;
+					} else {
+						SecondNibble = *CurrCharPos - 48;
+					}  
+				} else if (	(*CurrCharPos >= 'a') && 
+						(*CurrCharPos <= 'f')  ) {
+					if (FirstNibble == -1) {
+						FirstNibble = *CurrCharPos - 87; 
+					} else {
+						SecondNibble = *CurrCharPos - 87; 
+					}
+				} else {
+					Result = 0;
+				}
+			}
+
+			Result = FirstNibble;
+			Result = Result << 4; /* first nibble is higher one */
+			Result = Result | SecondNibble;
+		}
+		CurrCharPos++;   /* next character */
+	}
+	return (Result);
+}
+
+/****************************************************************************
+ *
+ *	SkDrvDeInitAdapter - deinitialize adapter (this function is only 
+ *				called if Diag attaches to that card)
+ *
+ * Description:
+ *	Close initialized adapter.
+ *
+ * Returns:
+ *	0 - on success
+ *	error code - on error
+ */
+static int SkDrvDeInitAdapter(
+SK_AC   *pAC,		/* pointer to adapter context   */
+int      devNbr)	/* what device is to be handled */
+{
+	struct SK_NET_DEVICE *dev;
+
+	dev = pAC->dev[devNbr];
+
+	/*
+	** Function SkGeClose() uses MOD_DEC_USE_COUNT (2.2/2.4)
+	** or module_put() (2.6) to decrease the number of users for
+	** a device, but if a device is to be put under control of 
+	** the DIAG, that count is OK already and does not need to 
+	** be adapted! Hence the opposite MOD_INC_USE_COUNT or 
+	** try_module_get() needs to be used again to correct that.
+	*/
+	MOD_INC_USE_COUNT;
+
+	if (SkGeClose(dev) != 0) {
+		MOD_DEC_USE_COUNT;
+		return (-1);
+	}
+	return (0);
+
+} /* SkDrvDeInitAdapter() */
+
+/****************************************************************************
+ *
+ *	SkDrvInitAdapter - Initialize adapter (this function is only 
+ *				called if Diag deattaches from that card)
+ *
+ * Description:
+ *	Close initialized adapter.
+ *
+ * Returns:
+ *	0 - on success
+ *	error code - on error
+ */
+static int SkDrvInitAdapter(
+SK_AC   *pAC,		/* pointer to adapter context   */
+int      devNbr)	/* what device is to be handled */
+{
+	struct SK_NET_DEVICE *dev;
+
+	dev = pAC->dev[devNbr];
+
+	if (SkGeOpen(dev) != 0) {
+		return (-1);
+	} else {
+		/*
+		** Function SkGeOpen() uses MOD_INC_USE_COUNT (2.2/2.4) 
+		** or try_module_get() (2.6) to increase the number of 
+		** users for a device, but if a device was just under 
+		** control of the DIAG, that count is OK already and 
+		** does not need to be adapted! Hence the opposite 
+		** MOD_DEC_USE_COUNT or module_put() needs to be used 
+		** again to correct that.
+		*/
+		MOD_DEC_USE_COUNT;
+	}
+
+	/*
+	** Use correct MTU size and indicate to kernel TX queue can be started
+	*/ 
+	if (SkGeChangeMtu(dev, dev->mtu) != 0) {
+		return (-1);
+	} 
+	return (0);
+
+} /* SkDrvInitAdapter */
+
+#endif
+
 #ifdef DEBUG
 /****************************************************************************/
 /* "debug only" section *****************************************************/

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)