diff -urN linux-2.4.28/Documentation/computone.txt linux-2.4.28ct/Documentation/computone.txt
--- linux-2.4.28/Documentation/computone.txt	2001-11-02 20:26:17.000000000 -0500
+++ linux-2.4.28ct/Documentation/computone.txt	2004-12-22 22:00:39.000000000 -0500
@@ -6,11 +6,11 @@
 These notes are for the drivers which have already been integrated into the
 kernel and have been tested on Linux kernels 2.0, 2.2, 2.3, and 2.4.
 
-Version: 1.2.14
-Date: 11/01/2001
+Version: 1.2.18
+Date: 12/22/2004
 Historical Author: Andrew Manison <amanison@america.net>
 Primary Author: Doug McNash
-Support: support@computone.com
+Support: mhw@wittsend.com
 Fixes and Updates: Mike Warfield <mhw@wittsend.com>
 
 This file assumes that you are using the Computone drivers which are
@@ -26,7 +26,7 @@
 products previous to the Intelliport II.
 
 This driver was developed on the v2.0.x Linux tree and has been tested up
-to v2.4.14; it will probably not work with earlier v1.X kernels,.
+to v2.4.28; it will probably not work with earlier v1.X kernels,.
 
 
 2. QUICK INSTALLATION
diff -urN linux-2.4.28/drivers/char/ip2/i2ellis.c linux-2.4.28ct/drivers/char/ip2/i2ellis.c
--- linux-2.4.28/drivers/char/ip2/i2ellis.c	2001-10-24 15:05:18.000000000 -0400
+++ linux-2.4.28ct/drivers/char/ip2/i2ellis.c	2004-12-22 21:39:13.000000000 -0500
@@ -605,7 +605,7 @@
 
 	set_current_state( TASK_INTERRUPTIBLE );
 
-	pDelayTimer->expires  = jiffies + ( mseconds + 9 ) / 10;
+	pDelayTimer->expires  = jiffies + ((HZ * mseconds + 999) / 1000);
 	pDelayTimer->function = ii2DelayWakeup;
 	pDelayTimer->data     = 0;
 
diff -urN linux-2.4.28/drivers/char/ip2/i2ellis.h linux-2.4.28ct/drivers/char/ip2/i2ellis.h
--- linux-2.4.28/drivers/char/ip2/i2ellis.h	2001-10-24 15:05:18.000000000 -0400
+++ linux-2.4.28ct/drivers/char/ip2/i2ellis.h	2004-12-22 22:34:38.000000000 -0500
@@ -403,7 +403,6 @@
 //	For queuing interupt bottom half handlers.	/\/\|=mhw=|\/\/
 	struct tq_struct	tqueue_interrupt;
 
-	struct timer_list  SendPendingTimer;   // Used by iiSendPending
 	unsigned int	SendPendingRetry;
 
 #ifdef	CONFIG_DEVFS_FS
diff -urN linux-2.4.28/drivers/char/ip2/i2lib.c linux-2.4.28ct/drivers/char/ip2/i2lib.c
--- linux-2.4.28/drivers/char/ip2/i2lib.c	2002-08-02 20:39:43.000000000 -0400
+++ linux-2.4.28ct/drivers/char/ip2/i2lib.c	2004-12-22 22:35:26.000000000 -0500
@@ -174,8 +174,12 @@
 			pB->i2eWaitingForEmptyFifo |=
 				(pB->i2eOutMailWaiting & MB_OUT_STUFFED);
 			pB->i2eOutMailWaiting = 0;
+			if( pB->SendPendingRetry ) {
+				printk( KERN_DEBUG "IP2: iiSendPendingMail: busy board pickup on %d\n", pB->SendPendingRetry );
+			}
 			pB->SendPendingRetry = 0;
 		} else {
+#ifdef	IP2_USE_TIMER_WAIT
 /*		The only time we hit this area is when "iiTrySendMail" has
 		failed.  That only occurs when the outbound mailbox is
 		still busy with the last message.  We take a short breather
@@ -193,6 +197,11 @@
 			} else {
 				printk( KERN_ERR "IP2: iiSendPendingMail unable to queue outbound mail\n" );
 			}
+#endif
+			pB->SendPendingRetry++;
+			if( 0 == ( pB->SendPendingRetry % 8 ) ) {
+				printk( KERN_ERR "IP2: iiSendPendingMail: board busy, retry %d\n", pB->SendPendingRetry );
+			}
 		}
 	}
 }
@@ -1329,8 +1338,9 @@
 	remove_wait_queue(&(pCh->pBookmarkWait), &wait);
 
 	// if expires == 0 then timer poped, then do not need to del_timer
+	// jiffy wrap per Tim Schmielau (tim@physik3.uni-rostock.de)
 	if ((timeout > 0) && pCh->BookmarkTimer.expires && 
-	                     time_before(jiffies, pCh->BookmarkTimer.expires)) {
+			time_before(jiffies, pCh->BookmarkTimer.expires)) {
 		del_timer( &(pCh->BookmarkTimer) );
 		pCh->BookmarkTimer.expires = 0;
 
diff -urN linux-2.4.28/drivers/char/ip2/ip2types.h linux-2.4.28ct/drivers/char/ip2/ip2types.h
--- linux-2.4.28/drivers/char/ip2/ip2types.h	1999-08-23 13:23:23.000000000 -0400
+++ linux-2.4.28ct/drivers/char/ip2/ip2types.h	2004-12-22 12:05:27.000000000 -0500
@@ -49,6 +49,10 @@
 	short irq[IP2_MAX_BOARDS]; 
 	unsigned short addr[IP2_MAX_BOARDS];
 	int type[IP2_MAX_BOARDS];
+#ifdef CONFIG_PCI
+	struct pci_dev *pci_dev[IP2_MAX_BOARDS];
+#endif
+
 } ip2config_t;
 
 #endif
diff -urN linux-2.4.28/drivers/char/ip2main.c linux-2.4.28ct/drivers/char/ip2main.c
--- linux-2.4.28/drivers/char/ip2main.c	2004-11-17 06:54:21.000000000 -0500
+++ linux-2.4.28ct/drivers/char/ip2main.c	2004-12-22 22:36:08.000000000 -0500
@@ -19,6 +19,26 @@
 //
 // Done:
 //
+// 1.2.18	/\/\|=mhw=|\/\/
+// Fixed jiffies math in ii2DelayTimer...
+//	How long has THIS been broken?  Delay was basically a noop...
+// Dumped some groady tracing code that hasn't been used (or tested) in
+//	years and was only a source of warning messages.
+//
+// 1.2.17	/\/\|=mhw=|\/\/
+// PCI fixes submitted by Bjorn Helgaas
+//	Added calls to Add pci_enable_device()/pci_disable_device() per
+//	submitted patches.  My thanks to Bjorn Helgaas <bjorn.helgaas@hp.com>.
+//
+// 1.2.16	/\/\|=mhw=|\/\/
+// Yet another shot at the busy board timing window (use the poll timer).
+// 	Because of this, the poll timer is always enabled...
+// Cleaned up some comments on immediate interrupt mode (ppp code elsewhere
+// 	has been fixed and the new busy board logic won't throw a hairball).
+//
+// 1.2.15	dmc
+// Fixed jiffy wrap, PCI card may now be set to poll.
+//
 // 1.2.14	/\/\|=mhw=|\/\/
 // Added bounds checking to ip2_ipl_ioctl to avoid potential terroristic acts.
 // Changed the definition of ip2trace to be more consistant with kernel style
@@ -227,7 +247,7 @@
 
 /* String constants to identify ourselves */
 static char *pcName    = "Computone IntelliPort Plus multiport driver";
-static char *pcVersion = "1.2.14";
+static char *pcVersion = "1.2.18";
 
 /* String constants for port names */
 static char *pcDriver_name   = "ip2";
@@ -437,7 +457,7 @@
 	}
 	return 0;
 }
-#endif
+#endif /* MODULE */
 
 static int __init
 have_requested_irq( char irq )
@@ -545,6 +565,12 @@
 	// free memory
 	for (i = 0; i < IP2_MAX_BOARDS; i++) {
 		void *pB;
+#ifdef CONFIG_PCI
+		if (ip2config.type[i] == PCI && ip2config.pci_dev[i]) {
+			pci_disable_device(ip2config.pci_dev[i]);
+			ip2config.pci_dev[i] = NULL;
+		}
+#endif
 		if ((pB = i2BoardPtrTable[i]) != 0 ) {
 			kfree ( pB );
 			i2BoardPtrTable[i] = NULL;
@@ -604,17 +630,12 @@
 		if (iop) {
 			ip2config.addr[i] = iop[i];
 			if (irqp) {
-				if( irqp[i] >= 0 ) {
-					ip2config.irq[i] = irqp[i];
-				} else {
-					ip2config.irq[i] = 0;
-				}
+				ip2config.irq[i] = irqp[i];
 	// This is a little bit of a hack.  If poll_only=1 on command
 	// line back in ip2.c OR all IRQs on all specified boards are
 	// explicitly set to 0, then drop to poll only mode and override
 	// PCI or EISA interrupts.  This superceeds the old hack of
 	// triggering if all interrupts were zero (like da default).
-	// Still a hack but less prone to random acts of terrorism.
 	//
 	// What we really should do, now that the IRQ default is set
 	// to -1, is to use 0 as a hard coded, do not probe.
@@ -700,17 +721,12 @@
 					} else {
 						printk( KERN_ERR "IP2: PCI I/O address error\n");
 					}
-					pcibios_read_config_byte(pci_bus, pci_devfn,
+					if (ip2config.irq[i] != 0) {
+						pcibios_read_config_byte(pci_bus, pci_devfn,
 								  PCI_INTERRUPT_LINE, &pci_irq);
 
-//		If the PCI BIOS assigned it, lets try and use it.  If we
-//		can't acquire it or it screws up, deal with it then.
-
-//					if (!is_valid_irq(pci_irq)) {
-//						printk( KERN_ERR "IP2: Bad PCI BIOS IRQ(%d)\n",pci_irq);
-//						pci_irq = 0;
-//					}
-					ip2config.irq[i] = pci_irq;
+						ip2config.irq[i] = pci_irq;
+					}
 				} else {	// ann error
 					ip2config.addr[i] = 0;
 					if (status == PCIBIOS_DEVICE_NOT_FOUND) {
@@ -727,9 +743,15 @@
 							  PCI_DEVICE_ID_COMPUTONE_IP2EX, pci_dev_i);
 				if (pci_dev_i != NULL) {
 					unsigned int addr;
-					unsigned char pci_irq;
+
+					if (pci_enable_device(pci_dev_i)) {
+						printk( KERN_ERR "IP2: can't enable PCI device at %s\n",
+						pci_name(pci_dev_i));
+						break;
+					}
 
 					ip2config.type[i] = PCI;
+					ip2config.pci_dev[i] = pci_dev_i;
 					status =
 					pci_read_config_dword(pci_dev_i, PCI_BASE_ADDRESS_1, &addr);
 					if ( addr & 1 ) {
@@ -737,18 +759,8 @@
 					} else {
 						printk( KERN_ERR "IP2: PCI I/O address error\n");
 					}
-					status =
-					pci_read_config_byte(pci_dev_i, PCI_INTERRUPT_LINE, &pci_irq);
-
-//		If the PCI BIOS assigned it, lets try and use it.  If we
-//		can't acquire it or it screws up, deal with it then.
-
-//					if (!is_valid_irq(pci_irq)) {
-//						printk( KERN_ERR "IP2: Bad PCI BIOS IRQ(%d)\n",pci_irq);
-//						pci_irq = 0;
-//					}
-					ip2config.irq[i] = pci_irq;
-				} else {	// ann error
+					ip2config.irq[i] = pci_dev_i->irq;
+				} else {	// an error
 					ip2config.addr[i] = 0;
 					if (status == PCIBIOS_DEVICE_NOT_FOUND) {
 						printk( KERN_ERR "IP2: PCI board %d not found\n", i );
@@ -964,6 +976,14 @@
 				/* Initialise the interrupt handler bottom half (aka slih). */
 			}
 		}
+
+		if (!TimerOn) {
+//			Kick the timer on for stuck board safeties...
+			PollTimer.expires = POLL_TIMEOUT;
+			add_timer ( &PollTimer );
+			TimerOn = 1;
+		}
+
 		for( i = 0; i < IP2_MAX_BOARDS; ++i ) {
 			if ( i2BoardPtrTable[i] ) {
 				set_irq( i, ip2config.irq[i] ); /* set and enable board interrupt */
@@ -1375,8 +1395,10 @@
 
 //		Only process those boards which match our IRQ.
 //			IRQ = 0 for polled boards, we won't poll "IRQ" boards
+//		Add an additional "check" if the irq is zero for boards
+//		which are "stuck"
 
-		if ( pB && (pB->i2eUsingIrq == irq) ) {
+		if ( pB && ( pB->i2eUsingIrq == irq || ( 0 == irq && 0 != pB->SendPendingRetry ) ) ) {
 #ifdef USE_IQI
 
 		    if (NO_MAIL_HERE != ( pB->i2eStartMail = iiGetMail(pB))) {
@@ -1391,9 +1413,8 @@
 			mark_bh(IMMEDIATE_BH);
 		    }
 #else
-//		We are using immediate servicing here.  This sucks and can
-//		cause all sorts of havoc with ppp and others.  The failsafe
-//		check on iiSendPendingMail could also throw a hairball.
+//		We are using immediate servicing here.  Suboptimal
+//			to say the least...
 			i2ServiceBoard( pB );
 #endif /* USE_IQI */
 		}
@@ -1417,6 +1438,9 @@
 static void
 ip2_poll(unsigned long arg)
 {
+	int i;
+	i2eBordStrPtr  pB;
+
 	ip2trace (ITRC_NO_PORT, ITRC_INTR, 100, 0 );
 
 	TimerOn = 0; // it's the truth but not checked in service
@@ -1424,7 +1448,15 @@
 	// Just polled boards, IRQ = 0 will hit all non-interrupt boards.
 	// It will NOT poll boards handled by hard interrupts.
 	// The issue of queued BH interrups is handled in ip2_interrupt().
-	ip2_interrupt(0, NULL, NULL);
+
+	for( i = 0; i < i2nBoards; ++i ) {
+		pB = i2BoardPtrTable[i];
+	// Service the board if the irq indicates a polled board
+	// OR if the SendPendingRetry indicates a retry state in the board
+		if ( pB && ( 0 == pB->i2eUsingIrq || 0 != pB->SendPendingRetry ) ) {
+			i2ServiceBoard( pB );
+		}
+	}
 
 	PollTimer.expires = POLL_TIMEOUT;
 	add_timer( &PollTimer );
@@ -3198,7 +3230,16 @@
 	case 2:	    // Ping device
 		rc = -EINVAL;
 		break;
+
+#ifdef IP2_TRACE_DEVICE
 	case 3:	    // Trace device
+/*
+	This may have been useful at one time during early development
+	but has not been used for years and is just a source of excess
+	baggage and unused code and warnings.  The client programs that
+	understood this returned "blob" don't even currently compile...
+	Disabled until needed in the future.  /\/\|=mhw=|\/\/  12/22/2004
+*/
 		if ( cmd == 1 ) {
 			PUT_USER(rc, iiSendPendingMail, pIndex++ );
 			PUT_USER(rc, i2InitChannels, pIndex++ );
@@ -3259,6 +3300,7 @@
 		}
 
 		break;
+#endif
 
 	default:
 		rc = -ENODEV;
