patch-2.3.7 linux/drivers/char/synclink.c

Next file: linux/drivers/char/tty_io.c
Previous file: linux/drivers/char/n_hdlc.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.6/linux/drivers/char/synclink.c linux/drivers/char/synclink.c
@@ -1,6 +1,8 @@
 /*
  * linux/drivers/char/synclink.c
  *
+ * ==FILEDATE 19990610==
+ *
  * Device driver for Microgate SyncLink ISA and PCI
  * high speed multiprotocol serial adapters.
  *
@@ -43,14 +45,15 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
- 
+
 #define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq))
 #define BREAKPOINT() asm("   int $3");
 
 #define MAX_ISA_DEVICES 10
 
-#include <linux/config.h>
+#include <linux/config.h>	
 #include <linux/module.h>
+#include <linux/version.h>
 #include <linux/errno.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
@@ -68,7 +71,7 @@
 #include <linux/mm.h>
 #include <linux/malloc.h>
 
-#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+#if LINUX_VERSION_CODE >= VERSION(2,1,0) 
 #include <linux/vmalloc.h>
 #include <linux/init.h>
 #include <asm/serial.h>
@@ -209,8 +212,21 @@
 } BH_EVENT, *BH_QUEUE;     /* Queue of BH actions to be done.  */
 
 #define MAX_BH_QUEUE_ENTRIES 200
+#define IO_PIN_SHUTDOWN_LIMIT (MAX_BH_QUEUE_ENTRIES/4)
 
 #define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+struct	_input_signal_events {
+	int	ri_up;	
+	int	ri_down;
+	int	dsr_up;
+	int	dsr_down;
+	int	dcd_up;
+	int	dcd_down;
+	int	cts_up;
+	int	cts_down;
+};
+
 /*
  * Device instance data structure
  */
@@ -266,6 +282,11 @@
 	int bh_running;		/* Protection from multiple */
 	int isr_overflow;
 	int bh_requested;
+	
+	int dcd_chkcount;		/* check counts to prevent */
+	int cts_chkcount;		/* too many IRQs if a signal */
+	int dsr_chkcount;		/* is floating */
+	int ri_chkcount;
 
 	char *buffer_list;		/* virtual address of Rx & Tx buffer lists */
 	unsigned long buffer_list_phys;
@@ -327,6 +348,11 @@
 	char flag_buf[HDLC_MAX_FRAME_SIZE];
 	char char_buf[HDLC_MAX_FRAME_SIZE];	
 	BOOLEAN drop_rts_on_tx_done;
+
+	BOOLEAN loopmode_insert_requested;
+	BOOLEAN	loopmode_send_done_requested;
+	
+	struct	_input_signal_events	input_signal_events;
 };
 
 #define MGSL_MAGIC 0x5401
@@ -712,6 +738,13 @@
 
 void mgsl_tx_timeout(unsigned long context);
 
+
+void usc_loopmode_cancel_transmit( struct mgsl_struct * info );
+void usc_loopmode_insert_request( struct mgsl_struct * info );
+int usc_loopmode_active( struct mgsl_struct * info);
+void usc_loopmode_send_done( struct mgsl_struct * info );
+int usc_loopmode_send_active( struct mgsl_struct * info );
+
 /*
  * Defines a BUS descriptor value for the PCI adapter
  * local bus address ranges.
@@ -820,7 +853,8 @@
 static int mgsl_txenable(struct mgsl_struct * info, int enable);
 static int mgsl_txabort(struct mgsl_struct * info);
 static int mgsl_rxenable(struct mgsl_struct * info, int enable);
-static int mgsl_wait_event(struct mgsl_struct * info, int mask);
+static int mgsl_wait_event(struct mgsl_struct * info, int * mask);
+static int mgsl_loopmode_send_done( struct mgsl_struct * info );
 
 #define jiffies_from_ms(a) ((((a) * HZ)/1000)+1)
 
@@ -865,7 +899,7 @@
 #endif
 
 static char *driver_name = "SyncLink serial driver";
-static char *driver_version = "1.00";
+static char *driver_version = "1.7";
 
 static struct tty_driver serial_driver, callout_driver;
 static int serial_refcount;
@@ -1001,6 +1035,7 @@
 
 	/* As a safety measure, mark the end of the chain with a NULL */
 	info->free_bh_queue_tail->link = NULL;
+	info->isr_overflow=0;
 
 }	/* end of mgsl_format_bh_queue() */
 
@@ -1092,6 +1127,14 @@
 		spin_unlock_irqrestore(&info->irq_spinlock,flags);
 		return 1;
 	}
+	
+	if ( info->isr_overflow ) {
+		if (debug_level >= DEBUG_LEVEL_BH)
+			printk("ISR overflow cleared.\n");
+		info->isr_overflow=0;
+		usc_EnableMasterIrqBit(info);
+		usc_EnableDmaInterrupts(info,DICR_MASTER);
+	}
 
 	/* Mark BH routine as complete */
 	info->bh_running   = 0;
@@ -1155,10 +1198,6 @@
 		}
 	}
 
-	if ( info->isr_overflow ) {
-		printk("ISR overflow detected.\n");
-	}
-
 	if ( debug_level >= DEBUG_LEVEL_BH )
 		printk( "%s(%d):mgsl_bh_handler(%s) exit\n",
 			__FILE__,__LINE__,info->device_name);
@@ -1199,6 +1238,7 @@
 void mgsl_bh_transmit_data( struct mgsl_struct *info, unsigned short Datacount )
 {
 	struct tty_struct *tty = info->tty;
+	unsigned long flags;
 	
 	if ( debug_level >= DEBUG_LEVEL_BH )
 		printk( "%s(%d):mgsl_bh_transmit_data() entry on %s\n",
@@ -1215,7 +1255,15 @@
 		}
 		wake_up_interruptible(&tty->write_wait);
 	}
-	
+
+	/* if transmitter idle and loopmode_send_done_requested
+	 * then start echoing RxD to TxD
+	 */
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+ 	if ( !info->tx_active && info->loopmode_send_done_requested )
+ 		usc_loopmode_send_done( info );
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
 }	/* End Of mgsl_bh_transmit_data() */
 
 /* mgsl_bh_status_handler()
@@ -1240,6 +1288,23 @@
 		printk( "%s(%d):mgsl_bh_status_handler() entry on %s\n",
 			__FILE__,__LINE__,info->device_name);
 
+	if (status & MISCSTATUS_RI_LATCHED) {
+		if (info->ri_chkcount)
+			(info->ri_chkcount)--;
+	}
+	if (status & MISCSTATUS_DSR_LATCHED) {
+		if (info->dsr_chkcount)
+			(info->dsr_chkcount)--;
+	}
+	if (status & MISCSTATUS_DCD_LATCHED) {
+		if (info->dcd_chkcount)
+			(info->dcd_chkcount)--;
+	}
+	if (status & MISCSTATUS_CTS_LATCHED) {
+		if (info->cts_chkcount)
+			(info->cts_chkcount)--;
+	}
+	
 }	/* End Of mgsl_bh_status_handler() */
 
 /* mgsl_isr_receive_status()
@@ -1259,8 +1324,21 @@
 		printk("%s(%d):mgsl_isr_receive_status status=%04X\n",
 			__FILE__,__LINE__,status);
 			
-	usc_ClearIrqPendingBits( info, RECEIVE_STATUS );
-	usc_UnlatchRxstatusBits( info, status );
+ 	if ( (status & RXSTATUS_ABORT_RECEIVED) && 
+		info->loopmode_insert_requested &&
+ 		usc_loopmode_active(info) )
+ 	{
+		++info->icount.rxabort;
+	 	info->loopmode_insert_requested = FALSE;
+ 
+ 		/* clear CMR:13 to start echoing RxD to TxD */
+		info->cmr_value &= ~BIT13;
+ 		usc_OutReg(info, CMR, info->cmr_value);
+ 
+		/* disable received abort irq (no longer required) */
+	 	usc_OutReg(info, RICR,
+ 			(usc_InReg(info, RICR) & ~RXSTATUS_ABORT_RECEIVED));
+ 	}
 
 	if (status & (RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)) {
 		if (status & RXSTATUS_EXITED_HUNT)
@@ -1278,6 +1356,9 @@
 		usc_RTCmd( info, RTCmd_PurgeRxFifo );
 	}
 
+	usc_ClearIrqPendingBits( info, RECEIVE_STATUS );
+	usc_UnlatchRxstatusBits( info, status );
+
 }	/* end of mgsl_isr_receive_status() */
 
 /* mgsl_isr_transmit_status()
@@ -1300,7 +1381,7 @@
 	
 	usc_ClearIrqPendingBits( info, TRANSMIT_STATUS );
 	usc_UnlatchTxstatusBits( info, status );
-
+ 
 	if ( status & TXSTATUS_EOF_SENT )
 		info->icount.txok++;
 	else if ( status & TXSTATUS_UNDERRUN )
@@ -1356,12 +1437,32 @@
 	              MISCSTATUS_DSR_LATCHED | MISCSTATUS_RI_LATCHED) ) {
 		icount = &info->icount;
 		/* update input line counters */
-		if (status & MISCSTATUS_RI_LATCHED)
+		if (status & MISCSTATUS_RI_LATCHED) {
+			if ((info->ri_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
+				usc_DisablestatusIrqs(info,SICR_RI);
 			icount->rng++;
-		if (status & MISCSTATUS_DSR_LATCHED)
+			if ( status & MISCSTATUS_RI )
+				info->input_signal_events.ri_up++;	
+			else
+				info->input_signal_events.ri_down++;	
+		}
+		if (status & MISCSTATUS_DSR_LATCHED) {
+			if ((info->dsr_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
+				usc_DisablestatusIrqs(info,SICR_DSR);
 			icount->dsr++;
+			if ( status & MISCSTATUS_DSR )
+				info->input_signal_events.dsr_up++;
+			else
+				info->input_signal_events.dsr_down++;
+		}
 		if (status & MISCSTATUS_DCD_LATCHED) {
+			if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
+				usc_DisablestatusIrqs(info,SICR_DCD);
 			icount->dcd++;
+			if ( status & MISCSTATUS_DCD )
+				info->input_signal_events.dcd_up++;
+			else
+				info->input_signal_events.dcd_down++;
 #ifdef CONFIG_HARD_PPS
 			if ((info->flags & ASYNC_HARDPPS_CD) &&
 			    (status & MISCSTATUS_DCD_LATCHED))
@@ -1369,7 +1470,15 @@
 #endif
 		}
 		if (status & MISCSTATUS_CTS_LATCHED)
+		{
+			if ((info->cts_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
+				usc_DisablestatusIrqs(info,SICR_CTS);
 			icount->cts++;
+			if ( status & MISCSTATUS_CTS )
+				info->input_signal_events.cts_up++;
+			else
+				info->input_signal_events.cts_down++;
+		}
 		wake_up_interruptible(&info->status_event_wait_q);
 		wake_up_interruptible(&info->event_wait_q);
 
@@ -1411,6 +1520,8 @@
 		}
 	}
 
+	mgsl_bh_queue_put(info, BH_TYPE_STATUS, status);
+	
 	/* for diagnostics set IRQ flag */
 	if ( status & MISCSTATUS_TXC_LATCHED ){
 		usc_OutReg( info, SICR,
@@ -1642,8 +1753,10 @@
 	/* Post a receive event for BH processing. */
 	mgsl_bh_queue_put( info, BH_TYPE_RECEIVE_DMA, status );
 	
-	if ( status & BIT3 )
+	if ( status & BIT3 ) {
 		info->rx_overflow = 1;
+		info->icount.buf_overrun++;
+	}
 
 }	/* end of mgsl_isr_receive_dma() */
 
@@ -1696,9 +1809,9 @@
 		if ( info->isr_overflow ) {
 			printk(KERN_ERR"%s(%d):%s isr overflow irq=%d\n",
 				__FILE__,__LINE__,info->device_name, irq);
-				/* Interrupt overflow. Reset adapter and exit. */
-//				UscReset(info);
-//				break;
+			usc_DisableMasterIrqBit(info);
+			usc_DisableDmaInterrupts(info,DICR_MASTER);
+			break;
 		}
 	}
 	
@@ -1980,6 +2093,11 @@
 		usc_set_async_mode(info);
 		
 	usc_set_serial_signals(info);
+	
+	info->dcd_chkcount = 0;
+	info->cts_chkcount = 0;
+	info->ri_chkcount = 0;
+	info->dsr_chkcount = 0;
 
 	/* enable modem signal IRQs and read initial signal states */
 	usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI);		
@@ -2112,16 +2230,27 @@
 
 	if ( info->params.mode == MGSL_MODE_HDLC ) {
 		/* operating in synchronous (frame oriented) mode */
-	
+
 		if (info->tx_active) {
 			ret = 0; goto cleanup; 
 		}
-		
+	
+		/* if operating in HDLC LoopMode and the adapter  */
+		/* has yet to be inserted into the loop, we can't */
+		/* transmit					  */
+
+		if ( (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) &&
+			!usc_loopmode_active(info) )
+		{
+			ret = 0;
+			goto cleanup;
+		}
+
 		if ( info->xmit_cnt ) {
 			/* Send accumulated from send_char() calls */
 			/* as frame and wait before accepting more data. */
 			ret = 0;
-				
+			
 			/* copy data from circular xmit_buf to */
 			/* transmit DMA buffer. */
 			mgsl_load_tx_dma_buffer(info,
@@ -2578,8 +2707,19 @@
 			
 	spin_lock_irqsave(&info->irq_spinlock,flags);
 	if ( enable ) {
-		if ( !info->tx_enabled )
+		if ( !info->tx_enabled ) {
+
 			usc_start_transmitter(info);
+			/*--------------------------------------------------
+			 * if HDLC/SDLC Loop mode, attempt to insert the
+			 * station in the 'loop' by setting CMR:13. Upon
+			 * receipt of the next GoAhead (RxAbort) sequence,
+			 * the OnLoop indicator (CCSR:7) should go active
+			 * to indicate that we are on the loop
+			 *--------------------------------------------------*/
+			if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE )
+				usc_loopmode_insert_request( info );
+		}
 	} else {
 		if ( info->tx_enabled )
 			usc_stop_transmitter(info);
@@ -2604,7 +2744,12 @@
 			
 	spin_lock_irqsave(&info->irq_spinlock,flags);
 	if ( info->tx_active && info->params.mode == MGSL_MODE_HDLC )
-		usc_TCmd(info,TCmd_SendAbort);
+	{
+		if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE )
+			usc_loopmode_cancel_transmit( info );
+		else
+			usc_TCmd(info,TCmd_SendAbort);
+	}
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	return 0;
 	
@@ -2640,25 +2785,39 @@
 /* mgsl_wait_event() 	wait for specified event to occur
  * 	
  * Arguments:	 	info	pointer to device instance data
- * 			mask	bitmask of events to wait for
- * Return Value:	bit mask of triggering event, otherwise error code
+ * 			mask	pointer to bitmask of events to wait for
+ * Return Value:	0 	if successful and bit mask updated with
+ *				of events triggerred,
+ * 			otherwise error code
  */
-static int mgsl_wait_event(struct mgsl_struct * info, int mask)
+static int mgsl_wait_event(struct mgsl_struct * info, int * mask_ptr)
 {
  	unsigned long flags;
 	int s;
 	int rc=0;
 	u16 regval;
 	struct mgsl_icount cprev, cnow;
+	int events = 0;
+	int mask;
+	struct	_input_signal_events signal_events_prev, signal_events_now;
+
+	COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int));
+	if (rc) {
+		return  -EFAULT;
+	}
 		 
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):mgsl_wait_event(%s,%d)\n", __FILE__,__LINE__,
 			info->device_name, mask);
-			
+
 	spin_lock_irqsave(&info->irq_spinlock,flags);
-	
+
+	usc_get_serial_signals(info);
+	s = info->serial_signals;
+
 	/* note the counters on entry */
 	cprev = info->icount;
+	signal_events_prev = info->input_signal_events;
 	
 	if (mask & MgslEvent_ExitHuntMode) {
 		/* enable exit hunt mode IRQ */
@@ -2676,7 +2835,22 @@
 	
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	
-	while(!rc) {
+	/* Determine if any user requested events for input signals is currently TRUE */
+	
+	events |= (mask & ((s & SerialSignal_DSR) ?
+			MgslEvent_DsrActive:MgslEvent_DsrInactive));
+
+	events |= (mask & ((s & SerialSignal_DCD) ?
+			MgslEvent_DcdActive:MgslEvent_DcdInactive));
+		
+	events |= (mask & ((s & SerialSignal_CTS) ?
+			MgslEvent_CtsActive:MgslEvent_CtsInactive));
+		
+	events |= (mask & ((s & SerialSignal_RI) ?
+			MgslEvent_RiActive:MgslEvent_RiInactive));
+	
+
+	while(!events) {
 		/* sleep until event occurs */
 		interruptible_sleep_on(&info->event_wait_q);
 		
@@ -2687,39 +2861,52 @@
 		}
 			
 		spin_lock_irqsave(&info->irq_spinlock,flags);
+
 		/* get icount and serial signal states */
 		cnow = info->icount;
-		s = info->serial_signals;
+		signal_events_now = info->input_signal_events;
 		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+		if (signal_events_now.dsr_up != signal_events_prev.dsr_up && 
+				mask & MgslEvent_DsrActive )
+			events |= MgslEvent_DsrActive;
+		
+		if (signal_events_now.dsr_down != signal_events_prev.dsr_down && 
+				mask & MgslEvent_DsrInactive )
+			events |= MgslEvent_DsrInactive;
+
+		if (signal_events_now.dcd_up != signal_events_prev.dcd_up &&
+				mask & MgslEvent_DcdActive )
+			events |= MgslEvent_DcdActive;
+		
+		if (signal_events_now.dcd_down != signal_events_prev.dcd_down &&
+				mask & MgslEvent_DcdInactive )
+			events |= MgslEvent_DcdInactive;
+		
+		if (signal_events_now.cts_up != signal_events_prev.cts_up &&
+				mask & MgslEvent_CtsActive )
+			events |= MgslEvent_CtsActive;
+		
+		if (signal_events_now.cts_down != signal_events_prev.cts_down &&
+				mask & MgslEvent_CtsInactive )
+			events |= MgslEvent_CtsInactive;
+		
+		if (signal_events_now.ri_up != signal_events_prev.ri_up &&
+				mask & MgslEvent_RiActive )
+			events |= MgslEvent_RiActive;
+		
+		if (signal_events_now.ri_down != signal_events_prev.ri_down &&
+				mask & MgslEvent_RiInactive )
+			events |= MgslEvent_RiInactive;
 		
-		rc = 0;		
-		
-		if (cnow.dsr != cprev.dsr)
-			rc |= (mask & ((s & SerialSignal_DSR) ?
-				MgslEvent_DsrActive:MgslEvent_DsrInactive));
-		
-		if (cnow.dcd != cprev.dcd)
-			rc |= (mask & ((s & SerialSignal_DCD) ?
-				MgslEvent_DcdActive:MgslEvent_DcdInactive));
-				
-		if (cnow.cts != cprev.cts)
-			rc |= (mask & ((s & SerialSignal_CTS) ?
-				MgslEvent_CtsActive:MgslEvent_CtsInactive));
-				
-		if (cnow.rng != cprev.rng)
-			rc |= (mask & ((s & SerialSignal_RI) ?
-				MgslEvent_RiActive:MgslEvent_RiInactive));
-				
 		if (cnow.exithunt != cprev.exithunt)
-			rc |= (mask & MgslEvent_ExitHuntMode);
-			
+			events |= (mask & MgslEvent_ExitHuntMode);
+
 		if (cnow.rxidle != cprev.rxidle)
-			rc |= (mask & MgslEvent_ExitHuntMode);
-				
-		if (!rc)
-			rc = -EIO; /* no change => error */
-			
+			events |= (mask & MgslEvent_IdleReceived);
+		
 		cprev = cnow;
+		signal_events_prev = signal_events_now;
 	}
 	
 	if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) {
@@ -2732,7 +2919,10 @@
 		}
 		spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	}
-	
+
+	if ( rc == 0 )
+		PUT_USER(rc, events, mask_ptr);
+		
 	return rc;
 	
 }	/* end of mgsl_wait_event() */
@@ -2772,7 +2962,7 @@
 
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):mgsl_get_modem_info %s value=%08X\n",
-			 __FILE__,__LINE__, info->device_name, *value );
+			 __FILE__,__LINE__, info->device_name, result );
 			
 	PUT_USER(err,result,value);
 	return err;
@@ -2928,7 +3118,9 @@
 		case MGSL_IOCGSTATS:
 			return mgsl_get_stats(info,(struct mgsl_icount*)arg);
 		case MGSL_IOCWAITEVENT:
-			return mgsl_wait_event(info,(int)arg);
+			return mgsl_wait_event(info,(int*)arg);
+		case MGSL_IOCLOOPTXDONE:
+			return mgsl_loopmode_send_done(info);
 		case MGSL_IOCCLRMODCOUNT:
 			while(MOD_IN_USE)
 				MOD_DEC_USE_COUNT;
@@ -3626,11 +3818,6 @@
 	}
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	
-#if 0 && LINUX_VERSION_CODE >= VERSION(2,1,0)
-	ret += sprintf(buf+ret, "irq_spinlock=%08X\n",
-	 		info->irq_spinlock.lock );
-#endif
-	
 	return ret;
 	
 }	/* end of line_info() */
@@ -4227,6 +4414,18 @@
 			if ( PCIBIOS_SUCCESSFUL == pcibios_find_device(
 				MICROGATE_VENDOR_ID, SYNCLINK_DEVICE_ID, i, &bus, &func) ) {
 				
+#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+				struct pci_dev *pdev = pci_find_slot(bus,func);
+				irq_line = pdev->irq;				
+#else												
+				if (pcibios_read_config_byte(bus,func,
+					PCI_INTERRUPT_LINE,&irq_line) ) {
+					printk( "%s(%d):USC I/O addr not set.\n",
+						__FILE__,__LINE__);
+					continue;
+				}
+#endif
+
 				if (pcibios_read_config_dword(bus,func,
 					PCI_BASE_ADDRESS_3,&shared_mem_base) ) {
 					printk( "%s(%d):Shared mem addr not set.\n",
@@ -4248,13 +4447,6 @@
 					continue;
 				}
 				
-				if (pcibios_read_config_byte(bus,func,
-					PCI_INTERRUPT_LINE,&irq_line) ) {
-					printk( "%s(%d):USC I/O addr not set.\n",
-						__FILE__,__LINE__);
-					continue;
-				}
-				
 				info = mgsl_allocate_device();
 				if ( !info ) {
 					/* error allocating device instance data */
@@ -4671,29 +4863,53 @@
 {
 	u16 RegValue;
 
-	/* Channel mode Register (CMR)
-	 *
-	 * <15..14>  00    Tx Sub modes, Underrun Action
-	 * <13>      0     1 = Send Preamble before opening flag
-	 * <12>      0     1 = Consecutive Idles share common 0
-	 * <11..8>   0110  Transmitter mode = HDLC/SDLC
-	 * <7..4>    0000  Rx Sub modes, addr/ctrl field handling
-	 * <3..0>    0110  Receiver mode = HDLC/SDLC
-	 *
-	 * 0000 0110 0000 0110 = 0x0606
-	 */
+ 	if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE )
+ 	{
+ 	   /*
+ 	   ** Channel Mode Register (CMR)
+ 	   **
+ 	   ** <15..14>    10    Tx Sub Modes, Send Flag on Underrun
+ 	   ** <13>        0     0 = Transmit Disabled (initially)
+ 	   ** <12>        0     1 = Consecutive Idles share common 0
+ 	   ** <11..8>     1110  Transmitter Mode = HDLC/SDLC Loop
+ 	   ** <7..4>      0000  Rx Sub Modes, addr/ctrl field handling
+ 	   ** <3..0>      0110  Receiver Mode = HDLC/SDLC
+ 	   **
+ 	   ** 1000 1110 0000 0110 = 0x8e06
+ 	   */
+ 	   RegValue = 0x8e06;
+ 
+ 	   /*--------------------------------------------------
+ 	    * ignore user options for UnderRun Actions and
+ 	    * preambles
+ 	    *--------------------------------------------------*/
+ 	}
+ 	else
+ 	{	
+		/* Channel mode Register (CMR)
+		 *
+		 * <15..14>  00    Tx Sub modes, Underrun Action
+		 * <13>      0     1 = Send Preamble before opening flag
+		 * <12>      0     1 = Consecutive Idles share common 0
+		 * <11..8>   0110  Transmitter mode = HDLC/SDLC
+		 * <7..4>    0000  Rx Sub modes, addr/ctrl field handling
+		 * <3..0>    0110  Receiver mode = HDLC/SDLC
+		 *
+		 * 0000 0110 0000 0110 = 0x0606
+		 */
 
-	RegValue = 0x0606;
+		RegValue = 0x0606;
 
-	if ( info->params.flags & HDLC_FLAG_UNDERRUN_ABORT15 )
-		RegValue |= BIT14;
-	else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG )
-		RegValue |= BIT15;
-	else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC )
-		RegValue |= BIT15 + BIT14;
+		if ( info->params.flags & HDLC_FLAG_UNDERRUN_ABORT15 )
+			RegValue |= BIT14;
+		else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG )
+			RegValue |= BIT15;
+		else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC )
+			RegValue |= BIT15 + BIT14;
 
-	if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE )
-		RegValue |= BIT13;
+		if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE )
+			RegValue |= BIT13;
+	}
 
 	if ( info->params.flags & HDLC_FLAG_SHARE_ZERO )
 		RegValue |= BIT12;
@@ -4862,6 +5078,8 @@
 		RegValue |= 0x0003;	/* RxCLK from DPLL */
 	else if ( info->params.flags & HDLC_FLAG_RXC_BRG )
 		RegValue |= 0x0004;	/* RxCLK from BRG0 */
+ 	else if ( info->params.flags & HDLC_FLAG_RXC_TXCPIN)
+ 		RegValue |= 0x0006;	/* RxCLK from TXC Input */
 	else
 		RegValue |= 0x0007;	/* RxCLK from Port1 */
 
@@ -4869,6 +5087,8 @@
 		RegValue |= 0x0018;	/* TxCLK from DPLL */
 	else if ( info->params.flags & HDLC_FLAG_TXC_BRG )
 		RegValue |= 0x0020;	/* TxCLK from BRG0 */
+ 	else if ( info->params.flags & HDLC_FLAG_TXC_RXCPIN)
+ 		RegValue |= 0x0038;	/* RxCLK from TXC Input */
 	else
 		RegValue |= 0x0030;	/* TxCLK from Port0 */
 
@@ -4922,10 +5142,24 @@
 		/*  of rounding up and then subtracting 1 we just don't subtract */
 		/*  the one in this case. */
 
-		Tc = (u16)((XtalSpeed/DpllDivisor)/info->params.clock_speed);
-		if ( !((((XtalSpeed/DpllDivisor) % info->params.clock_speed) * 2)
-		       / info->params.clock_speed) )
-			Tc--;
+ 		/*--------------------------------------------------
+ 		 * ejz: for DPLL mode, application should use the
+ 		 * same clock speed as the partner system, even 
+ 		 * though clocking is derived from the input RxData.
+ 		 * In case the user uses a 0 for the clock speed,
+ 		 * default to 0xffffffff and don't try to divide by
+ 		 * zero
+ 		 *--------------------------------------------------*/
+ 		if ( info->params.clock_speed )
+ 		{
+			Tc = (u16)((XtalSpeed/DpllDivisor)/info->params.clock_speed);
+			if ( !((((XtalSpeed/DpllDivisor) % info->params.clock_speed) * 2)
+			       / info->params.clock_speed) )
+				Tc--;
+ 		}
+ 		else
+ 			Tc = -1;
+ 				  
 
 		/* Write 16-bit Time Constant for BRG1 */
 		usc_OutReg( info, TC1R, Tc );
@@ -6328,6 +6562,13 @@
 	if ( debug_level >= DEBUG_LEVEL_DATA )
 		mgsl_trace_block(info,Buffer,BufferSize,1);	
 
+	if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) {
+		/* set CMR:13 to start transmit when
+		 * next GoAhead (abort) is received
+		 */
+	 	info->cmr_value |= BIT13;			  
+	}
+		
 	/* Setup the status and RCC (Frame Size) fields of the 1st */
 	/* buffer entry in the transmit DMA buffer list. */
 
@@ -6381,7 +6622,7 @@
 	unsigned int i;
 	BOOLEAN rc = TRUE;
 	unsigned long flags;
-	
+
 	spin_lock_irqsave(&info->irq_spinlock,flags);
 	usc_reset(info);
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
@@ -6471,7 +6712,7 @@
 	usc_reset(info);
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	
-	if ( !info->irq_occurred )
+	if ( !info->irq_occurred ) 
 		return FALSE;
 	else
 		return TRUE;
@@ -6499,7 +6740,7 @@
 	volatile unsigned long EndTime;
 	unsigned long flags;
 	MGSL_PARAMS tmp_params;
-	
+
 	/* save current port options */
 	memcpy(&tmp_params,&info->params,sizeof(MGSL_PARAMS));
 	/* load default port options */
@@ -6657,7 +6898,7 @@
 	/**********************************/
 	/* WAIT FOR TRANSMIT FIFO TO FILL */
 	/**********************************/
-															 
+	
 	/* Wait 100ms */
 	EndTime = jiffies + jiffies_from_ms(100);
 
@@ -6724,7 +6965,7 @@
 
 	if ( rc == TRUE ){
 		/* CHECK FOR TRANSMIT ERRORS */
-		if ( status & (BIT5 + BIT1) )
+		if ( status & (BIT5 + BIT1) ) 
 			rc = FALSE;
 	}
 
@@ -6981,13 +7222,90 @@
 	if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) {
 		info->icount.txtimeout++;
 	}
-	
 	spin_lock_irqsave(&info->irq_spinlock,flags);
 	info->tx_active = 0;
 	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+	if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE )
+		usc_loopmode_cancel_transmit( info );
+
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	
 	mgsl_bh_transmit_data(info,0);
 	
 }	/* end of mgsl_tx_timeout() */
+
+/* signal that there are no more frames to send, so that
+ * line is 'released' by echoing RxD to TxD when current
+ * transmission is complete (or immediately if no tx in progress).
+ */
+static int mgsl_loopmode_send_done( struct mgsl_struct * info )
+{
+	unsigned long flags;
+	
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) {
+		if (info->tx_active)
+			info->loopmode_send_done_requested = TRUE;
+		else
+			usc_loopmode_send_done(info);
+	}
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+	return 0;
+}
+
+/* release the line by echoing RxD to TxD
+ * upon completion of a transmit frame
+ */
+void usc_loopmode_send_done( struct mgsl_struct * info )
+{
+ 	info->loopmode_send_done_requested = FALSE;
+ 	/* clear CMR:13 to 0 to start echoing RxData to TxData */
+ 	info->cmr_value &= ~BIT13;			  
+ 	usc_OutReg(info, CMR, info->cmr_value);
+}
+
+/* abort a transmit in progress while in HDLC LoopMode
+ */
+void usc_loopmode_cancel_transmit( struct mgsl_struct * info )
+{
+ 	/* reset tx dma channel and purge TxFifo */
+ 	usc_RTCmd( info, RTCmd_PurgeTxFifo );
+ 	usc_DmaCmd( info, DmaCmd_ResetTxChannel );
+  	usc_loopmode_send_done( info );
+}
+
+/* for HDLC/SDLC LoopMode, setting CMR:13 after the transmitter is enabled
+ * is an Insert Into Loop action. Upon receipt of a GoAhead sequence (RxAbort)
+ * we must clear CMR:13 to begin repeating TxData to RxData
+ */
+void usc_loopmode_insert_request( struct mgsl_struct * info )
+{
+ 	info->loopmode_insert_requested = TRUE;
+ 
+ 	/* enable RxAbort irq. On next RxAbort, clear CMR:13 to
+ 	 * begin repeating TxData on RxData (complete insertion)
+	 */
+ 	usc_OutReg( info, RICR, 
+		(usc_InReg( info, RICR ) | RXSTATUS_ABORT_RECEIVED ) );
+		
+	/* set CMR:13 to insert into loop on next GoAhead (RxAbort) */
+	info->cmr_value |= BIT13;
+ 	usc_OutReg(info, CMR, info->cmr_value);
+}
+
+/* return 1 if station is inserted into the loop, otherwise 0
+ */
+int usc_loopmode_active( struct mgsl_struct * info)
+{
+ 	return usc_InReg( info, CCSR ) & BIT7 ? 1 : 0 ;
+}
+
+/* return 1 if USC is in loop send mode, otherwise 0
+ */
+int usc_loopmode_send_active( struct mgsl_struct * info )
+{
+	return usc_InReg( info, CCSR ) & BIT6 ? 1 : 0 ;
+}			  
 

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