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

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

diff -u --recursive --new-file v2.4.4/linux/drivers/char/synclink.c linux/drivers/char/synclink.c
@@ -1,7 +1,7 @@
 /*
  * linux/drivers/char/synclink.c
  *
- * $Id: synclink.c,v 3.2 2000/11/06 22:34:38 paul Exp $
+ * $Id: synclink.c,v 3.8 2001/03/30 17:30:38 ez Exp $
  *
  * Device driver for Microgate SyncLink ISA and PCI
  * high speed multiprotocol serial adapters.
@@ -107,8 +107,12 @@
 #endif
 
 #ifdef CONFIG_SYNCLINK_SYNCPPP
+#if LINUX_VERSION_CODE < VERSION(2,4,3) 
+#include "../net/wan/syncppp.h"
+#else
 #include <net/syncppp.h>
 #endif
+#endif
 
 #include <asm/segment.h>
 #define GET_USER(error,value,addr) error = get_user(value,addr)
@@ -176,6 +180,14 @@
 	int	cts_down;
 };
 
+/* transmit holding buffer definitions*/
+#define MAX_TX_HOLDING_BUFFERS 5
+struct tx_holding_buffer {
+	int	buffer_size;
+	unsigned char *	buffer;
+};
+
+
 /*
  * Device instance data structure
  */
@@ -241,11 +253,21 @@
 	DMABUFFERENTRY *rx_buffer_list;	/* list of receive buffer entries */
 	unsigned int current_rx_buffer;
 
+	int num_tx_dma_buffers;		/* number of tx dma frames required */
+ 	int tx_dma_buffers_used;
 	unsigned int tx_buffer_count;	/* count of total allocated Tx buffers */
 	DMABUFFERENTRY *tx_buffer_list;	/* list of transmit buffer entries */
+	int start_tx_dma_buffer;	/* tx dma buffer to start tx dma operation */
+	int current_tx_buffer;          /* next tx dma buffer to be loaded */
 	
 	unsigned char *intermediate_rxbuffer;
 
+	int num_tx_holding_buffers;	/* number of tx holding buffer allocated */
+	int get_tx_holding_index;  	/* next tx holding buffer for adapter to load */
+	int put_tx_holding_index;  	/* next tx holding buffer to store user request */
+	int tx_holding_count;		/* number of tx holding buffers waiting */
+	struct tx_holding_buffer tx_holding_buffers[MAX_TX_HOLDING_BUFFERS];
+
 	int rx_enabled;
 	int rx_overflow;
 
@@ -685,6 +707,8 @@
 #define usc_TCmd(a,b) usc_OutReg((a), TCSR, (u16)((a)->tcsr_value + (b)))
 #define usc_RCmd(a,b) usc_OutReg((a), RCSR, (b))
 
+#define usc_SetTransmitSyncChars(a,s0,s1) usc_OutReg((a), TSR, (u16)(((u16)s0<<8)|(u16)s1))
+
 void usc_process_rxoverrun_sync( struct mgsl_struct *info );
 void usc_start_receiver( struct mgsl_struct *info );
 void usc_stop_receiver( struct mgsl_struct *info );
@@ -775,7 +799,10 @@
  */
 void mgsl_free_rx_frame_buffers( struct mgsl_struct *info, unsigned int StartIndex, unsigned int EndIndex );
 int  mgsl_get_rx_frame( struct mgsl_struct *info );
+int  mgsl_get_raw_rx_frame( struct mgsl_struct *info );
 void mgsl_reset_rx_dma_buffers( struct mgsl_struct *info );
+void mgsl_reset_tx_dma_buffers( struct mgsl_struct *info );
+int num_free_tx_dma_buffers(struct mgsl_struct *info);
 void mgsl_load_tx_dma_buffer( struct mgsl_struct *info, const char *Buffer, unsigned int BufferSize);
 void mgsl_load_pci_memory(char* TargetPtr, const char* SourcePtr, unsigned short count);
 
@@ -790,6 +817,10 @@
 void mgsl_free_buffer_list_memory(struct mgsl_struct *info);
 int mgsl_alloc_intermediate_rxbuffer_memory(struct mgsl_struct *info);
 void mgsl_free_intermediate_rxbuffer_memory(struct mgsl_struct *info);
+int mgsl_alloc_intermediate_txbuffer_memory(struct mgsl_struct *info);
+void mgsl_free_intermediate_txbuffer_memory(struct mgsl_struct *info);
+int load_next_tx_holding_buffer(struct mgsl_struct *info);
+int save_tx_buffer_request(struct mgsl_struct *info,const char *Buffer, unsigned int BufferSize);
 
 /*
  * Bottom half interrupt handlers
@@ -810,6 +841,7 @@
 void mgsl_isr_io_pin( struct mgsl_struct *info );
 void mgsl_isr_misc( struct mgsl_struct *info );
 void mgsl_isr_receive_dma( struct mgsl_struct *info );
+void mgsl_isr_transmit_dma( struct mgsl_struct *info );
 
 typedef void (*isr_dispatch_func)(struct mgsl_struct *);
 
@@ -874,6 +906,8 @@
 static int debug_level = 0;
 static int maxframe[MAX_TOTAL_DEVICES] = {0,};
 static int dosyncppp[MAX_TOTAL_DEVICES] = {0,};
+static int txdmabufs[MAX_TOTAL_DEVICES] = {0,};
+static int txholdbufs[MAX_TOTAL_DEVICES] = {0,};
 	
 MODULE_PARM(break_on_load,"i");
 MODULE_PARM(ttymajor,"i");
@@ -884,9 +918,11 @@
 MODULE_PARM(debug_level,"i");
 MODULE_PARM(maxframe,"1-" __MODULE_STRING(MAX_TOTAL_DEVICES) "i");
 MODULE_PARM(dosyncppp,"1-" __MODULE_STRING(MAX_TOTAL_DEVICES) "i");
+MODULE_PARM(txdmabufs,"1-" __MODULE_STRING(MAX_TOTAL_DEVICES) "i");
+MODULE_PARM(txholdbufs,"1-" __MODULE_STRING(MAX_TOTAL_DEVICES) "i");
 
 static char *driver_name = "SyncLink serial driver";
-static char *driver_version = "3.2";
+static char *driver_version = "3.8";
 
 static int __init synclink_init_one (struct pci_dev *dev,
 				     const struct pci_device_id *ent);
@@ -1096,11 +1132,14 @@
 
 void mgsl_bh_receive(struct mgsl_struct *info)
 {
+	int (*get_rx_frame)(struct mgsl_struct *info) =
+		(info->params.mode == MGSL_MODE_HDLC ? mgsl_get_rx_frame : mgsl_get_raw_rx_frame);
+
 	if ( debug_level >= DEBUG_LEVEL_BH )
 		printk( "%s(%d):mgsl_bh_receive(%s)\n",
 			__FILE__,__LINE__,info->device_name);
 	
-	while( mgsl_get_rx_frame(info) );
+	while( (get_rx_frame)(info) );
 }
 
 void mgsl_bh_transmit(struct mgsl_struct *info)
@@ -1318,11 +1357,6 @@
 #endif
 			} else
 				info->input_signal_events.dcd_down++;
-#ifdef CONFIG_HARD_PPS
-			if ((info->flags & ASYNC_HARDPPS_CD) &&
-			    (status & MISCSTATUS_DCD_LATCHED))
-				hardpps();
-#endif
 		}
 		if (status & MISCSTATUS_CTS_LATCHED)
 		{
@@ -1609,6 +1643,58 @@
 
 }	/* end of mgsl_isr_receive_dma() */
 
+/* mgsl_isr_transmit_dma()
+ *
+ *	This function services a transmit DMA channel interrupt.
+ *
+ *	For this driver there is one source of transmit DMA interrupts
+ *	as identified in the Transmit DMA Mode Register (TDMR):
+ *
+ *     	BIT2  EOB       End of Buffer. This interrupt occurs when a
+ *     			transmit DMA buffer has been emptied.
+ *
+ *     	The driver maintains enough transmit DMA buffers to hold at least
+ *     	one max frame size transmit frame. When operating in a buffered
+ *     	transmit mode, there may be enough transmit DMA buffers to hold at
+ *     	least two or more max frame size frames. On an EOB condition,
+ *     	determine if there are any queued transmit buffers and copy into
+ *     	transmit DMA buffers if we have room.
+ *
+ * Arguments:		info		pointer to device instance data
+ * Return Value:	None
+ */
+void mgsl_isr_transmit_dma( struct mgsl_struct *info )
+{
+	u16 status;
+
+	/* clear interrupt pending and IUS bit for Tx DMA IRQ */
+	usc_OutDmaReg(info, CDIR, BIT8+BIT0 );
+
+	/* Read the transmit DMA status to identify interrupt type. */
+	/* This also clears the status bits. */
+
+	status = usc_InDmaReg( info, TDMR );
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )
+		printk("%s(%d):mgsl_isr_transmit_dma(%s) status=%04X\n",
+			__FILE__,__LINE__,info->device_name,status);
+
+	if ( status & BIT2 ) {
+		--info->tx_dma_buffers_used;
+
+		/* if there are transmit frames queued,
+		 *  try to load the next one
+		 */
+		if ( load_next_tx_holding_buffer(info) ) {
+			/* if call returns non-zero value, we have
+			 * at least one free tx holding buffer
+			 */
+			info->pending_bh |= BH_TRANSMIT;
+		}
+	}
+
+}	/* end of mgsl_isr_transmit_dma() */
+
 /* mgsl_interrupt()
  * 
  * 	Interrupt service routine entry point.
@@ -1652,6 +1738,8 @@
 		/* Dispatch interrupt vector */
 		if ( UscVector )
 			(*UscIsrTable[UscVector])(info);
+		else if ( (DmaVector&(BIT10|BIT9)) == BIT10)
+			mgsl_isr_transmit_dma(info);
 		else
 			mgsl_isr_receive_dma(info);
 
@@ -1818,7 +1906,9 @@
 	usc_stop_transmitter(info);
 	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
 	
-	if (info->params.mode == MGSL_MODE_HDLC || info->netcount)
+	if (info->params.mode == MGSL_MODE_HDLC ||
+	    info->params.mode == MGSL_MODE_RAW ||
+	    info->netcount)
 		usc_set_sync_mode(info);
 	else
 		usc_set_async_mode(info);
@@ -1970,8 +2060,7 @@
 
 	spin_lock_irqsave(&info->irq_spinlock,flags);
 	
-	if ( (info->params.mode != MGSL_MODE_HDLC) || 
-	     !info->tx_active ) {
+	if ( (info->params.mode == MGSL_MODE_ASYNC ) || !info->tx_active ) {
 	
 		if (info->xmit_cnt < SERIAL_XMIT_SIZE - 1) {
 			info->xmit_buf[info->xmit_head++] = ch;
@@ -2015,8 +2104,8 @@
 	spin_lock_irqsave(&info->irq_spinlock,flags);
 	
 	if (!info->tx_active) {
-		if ( (info->params.mode == MGSL_MODE_HDLC) &&
-			info->xmit_cnt ) {
+		if ( (info->params.mode == MGSL_MODE_HDLC ||
+			info->params.mode == MGSL_MODE_RAW) && info->xmit_cnt ) {
 			/* operating in synchronous (frame oriented) mode */
 			/* copy data from circular xmit_buf to */
 			/* transmit DMA buffer. */
@@ -2060,11 +2149,51 @@
 	if (!tty || !info->xmit_buf || !tmp_buf)
 		goto cleanup;
 
-	if ( info->params.mode == MGSL_MODE_HDLC ) {
+	if ( info->params.mode == MGSL_MODE_HDLC ||
+			info->params.mode == MGSL_MODE_RAW ) {
+		/* operating in synchronous (frame oriented) mode */
 		/* operating in synchronous (frame oriented) mode */
-
 		if (info->tx_active) {
-			ret = 0; goto cleanup; 
+
+			if ( info->params.mode == MGSL_MODE_HDLC ) {
+				ret = 0;
+				goto cleanup;
+			}
+			/* transmitter is actively sending data -
+			 * if we have multiple transmit dma and
+			 * holding buffers, attempt to queue this
+			 * frame for transmission at a later time.
+			 */
+			if (info->tx_holding_count >= info->num_tx_holding_buffers ) {
+				/* no tx holding buffers available */
+				ret = 0;
+				goto cleanup;
+			}
+
+			/* queue transmit frame request */
+			ret = count;
+			if (from_user) {
+				down(&tmp_buf_sem);
+				COPY_FROM_USER(err,tmp_buf, buf, count);
+				if (err) {
+					if ( debug_level >= DEBUG_LEVEL_INFO )
+						printk( "%s(%d):mgsl_write(%s) sync user buf copy failed\n",
+							__FILE__,__LINE__,info->device_name);
+					ret = -EFAULT;
+				} else
+					save_tx_buffer_request(info,tmp_buf,count);
+				up(&tmp_buf_sem);
+			}
+			else
+				save_tx_buffer_request(info,buf,count);
+
+			/* if we have sufficient tx dma buffers,
+			 * load the next buffered tx request
+			 */
+			spin_lock_irqsave(&info->irq_spinlock,flags);
+			load_next_tx_holding_buffer(info);
+			spin_unlock_irqrestore(&info->irq_spinlock,flags);
+			goto cleanup;
 		}
 	
 		/* if operating in HDLC LoopMode and the adapter  */
@@ -2200,7 +2329,8 @@
 		printk("%s(%d):mgsl_write_room(%s)=%d\n",
 			 __FILE__,__LINE__, info->device_name,ret );
 			 
-	if ( info->params.mode == MGSL_MODE_HDLC ) {
+	if ( info->params.mode == MGSL_MODE_HDLC ||
+		info->params.mode == MGSL_MODE_RAW ) {
 		/* operating in synchronous (frame oriented) mode */
 		if ( info->tx_active )
 			return 0;
@@ -2234,10 +2364,11 @@
 		printk("%s(%d):mgsl_chars_in_buffer(%s)=%d\n",
 			 __FILE__,__LINE__, info->device_name,info->xmit_cnt );
 			 
-	if ( info->params.mode == MGSL_MODE_HDLC ) {
+	if ( info->params.mode == MGSL_MODE_HDLC ||
+		info->params.mode == MGSL_MODE_RAW ) {
 		/* operating in synchronous (frame oriented) mode */
 		if ( info->tx_active )
-			return info->tx_buffer_list[0].rcc;
+			return info->max_frame_size;
 		else
 			return 0;
 	}
@@ -2627,11 +2758,11 @@
  	unsigned long flags;
 	int s;
 	int rc=0;
-	u16 regval;
 	struct mgsl_icount cprev, cnow;
-	int events = 0;
+	int events;
 	int mask;
-	struct	_input_signal_events signal_events_prev, signal_events_now;
+	struct	_input_signal_events oldsigs, newsigs;
+	DECLARE_WAITQUEUE(wait, current);
 
 	COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int));
 	if (rc) {
@@ -2644,114 +2775,99 @@
 
 	spin_lock_irqsave(&info->irq_spinlock,flags);
 
+	/* return immediately if state matches requested events */
 	usc_get_serial_signals(info);
 	s = info->serial_signals;
+	events = mask &
+		( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) +
+ 		  ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) +
+		  ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) +
+		  ((s & SerialSignal_RI)  ? MgslEvent_RiActive :MgslEvent_RiInactive) );
+	if (events) {
+		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+		goto exit;
+	}
 
-	/* note the counters on entry */
+	/* save current irq counts */
 	cprev = info->icount;
-	signal_events_prev = info->input_signal_events;
+	oldsigs = info->input_signal_events;
 	
-	if (mask & MgslEvent_ExitHuntMode) {
-		/* enable exit hunt mode IRQ */
-		regval = usc_InReg(info,RICR);
-		if (!(regval & RXSTATUS_EXITED_HUNT))
-			usc_OutReg(info, RICR, regval | RXSTATUS_EXITED_HUNT);
+	/* enable hunt and idle irqs if needed */
+	if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) {
+		u16 oldreg = usc_InReg(info,RICR);
+		u16 newreg = oldreg +
+			 (mask & MgslEvent_ExitHuntMode ? RXSTATUS_EXITED_HUNT:0) +
+			 (mask & MgslEvent_IdleReceived ? RXSTATUS_IDLE_RECEIVED:0);
+		if (oldreg != newreg)
+			usc_OutReg(info, RICR, newreg);
 	}
 	
-	if (mask & MgslEvent_IdleReceived) {
-		/* enable idle mode received IRQ */
-		regval = usc_InReg(info,RICR);
-		if (!(regval & RXSTATUS_IDLE_RECEIVED))
-			usc_OutReg(info, RICR, regval | RXSTATUS_IDLE_RECEIVED);
-	}
+	set_current_state(TASK_INTERRUPTIBLE);
+	add_wait_queue(&info->event_wait_q, &wait);
 	
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	
-	/* 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);
-		
-		/* see if a signal woke us */
+	for(;;) {
+		schedule();
 		if (signal_pending(current)) {
 			rc = -ERESTARTSYS;
 			break;
 		}
 			
+		/* get current irq counts */
 		spin_lock_irqsave(&info->irq_spinlock,flags);
-
-		/* get icount and serial signal states */
 		cnow = info->icount;
-		signal_events_now = info->input_signal_events;
+		newsigs = info->input_signal_events;
+		set_current_state(TASK_INTERRUPTIBLE);
 		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;
-		
-		if (cnow.exithunt != cprev.exithunt)
-			events |= (mask & MgslEvent_ExitHuntMode);
+		/* if no change, wait aborted for some reason */
+		if (newsigs.dsr_up   == oldsigs.dsr_up   &&
+		    newsigs.dsr_down == oldsigs.dsr_down &&
+		    newsigs.dcd_up   == oldsigs.dcd_up   &&
+		    newsigs.dcd_down == oldsigs.dcd_down &&
+		    newsigs.cts_up   == oldsigs.cts_up   &&
+		    newsigs.cts_down == oldsigs.cts_down &&
+		    newsigs.ri_up    == oldsigs.ri_up    &&
+		    newsigs.ri_down  == oldsigs.ri_down  &&
+		    cnow.exithunt    == cprev.exithunt   &&
+		    cnow.rxidle      == cprev.rxidle) {
+			rc = -EIO;
+			break;
+		}
 
-		if (cnow.rxidle != cprev.rxidle)
-			events |= (mask & MgslEvent_IdleReceived);
+		events = mask &
+			( (newsigs.dsr_up   != oldsigs.dsr_up   ? MgslEvent_DsrActive:0)   +
+			(newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) +
+			(newsigs.dcd_up   != oldsigs.dcd_up   ? MgslEvent_DcdActive:0)   +
+			(newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) +
+			(newsigs.cts_up   != oldsigs.cts_up   ? MgslEvent_CtsActive:0)   +
+			(newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) +
+			(newsigs.ri_up    != oldsigs.ri_up    ? MgslEvent_RiActive:0)    +
+			(newsigs.ri_down  != oldsigs.ri_down  ? MgslEvent_RiInactive:0)  +
+			(cnow.exithunt    != cprev.exithunt   ? MgslEvent_ExitHuntMode:0) +
+			  (cnow.rxidle      != cprev.rxidle     ? MgslEvent_IdleReceived:0) );
+		if (events)
+			break;
 		
 		cprev = cnow;
-		signal_events_prev = signal_events_now;
+		oldsigs = newsigs;
 	}
 	
+	remove_wait_queue(&info->event_wait_q, &wait);
+	set_current_state(TASK_RUNNING);
+
 	if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) {
 		spin_lock_irqsave(&info->irq_spinlock,flags);
 		if (!waitqueue_active(&info->event_wait_q)) {
 			/* disable enable exit hunt mode/idle rcvd IRQs */
-			regval = usc_InReg(info,RICR);
-			usc_OutReg(info, RICR, regval & 
+			usc_OutReg(info, RICR, usc_InReg(info,RICR) &
 				~(RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED));
 		}
 		spin_unlock_irqrestore(&info->irq_spinlock,flags);
 	}
-
+exit:
 	if ( rc == 0 )
 		PUT_USER(rc, events, mask_ptr);
 		
@@ -2759,6 +2875,56 @@
 	
 }	/* end of mgsl_wait_event() */
 
+static int modem_input_wait(struct mgsl_struct *info,int arg)
+{
+ 	unsigned long flags;
+	int rc;
+	struct mgsl_icount cprev, cnow;
+	DECLARE_WAITQUEUE(wait, current);
+
+	/* save current irq counts */
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	cprev = info->icount;
+	add_wait_queue(&info->status_event_wait_q, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+	for(;;) {
+		schedule();
+		if (signal_pending(current)) {
+			rc = -ERESTARTSYS;
+			break;
+		}
+
+		/* get new irq counts */
+		spin_lock_irqsave(&info->irq_spinlock,flags);
+		cnow = info->icount;
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+		/* if no change, wait aborted for some reason */
+		if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+		    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
+			rc = -EIO;
+			break;
+		}
+
+		/* check for change in caller specified modem input */
+		if ((arg & TIOCM_RNG && cnow.rng != cprev.rng) ||
+		    (arg & TIOCM_DSR && cnow.dsr != cprev.dsr) ||
+		    (arg & TIOCM_CD  && cnow.dcd != cprev.dcd) ||
+		    (arg & TIOCM_CTS && cnow.cts != cprev.cts)) {
+			rc = 0;
+			break;
+		}
+
+		cprev = cnow;
+	}
+	remove_wait_queue(&info->status_event_wait_q, &wait);
+	set_current_state(TASK_RUNNING);
+	return rc;
+}
+
 /* get_modem_info()
  * 
  * 	Read the state of the serial control and
@@ -2926,7 +3092,7 @@
 int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg)
 {
 	int error;
-	struct mgsl_icount cprev, cnow;	/* kernel counter temps */
+	struct mgsl_icount cnow;	/* kernel counter temps */
 	struct serial_icounter_struct *p_cuser;	/* user space */
 	unsigned long flags;
 	
@@ -2961,37 +3127,12 @@
 			while(MOD_IN_USE)
 				MOD_DEC_USE_COUNT;
 			return 0;
-		/*
-		 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
-		 * - mask passed in arg for lines of interest
- 		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
-		 * Caller should use TIOCGICOUNT to see which one it was
+
+		/* Wait for modem input (DCD,RI,DSR,CTS) change
+		 * as specified by mask in arg (TIOCM_RNG/DSR/CD/CTS)
 		 */
 		case TIOCMIWAIT:
-			spin_lock_irqsave(&info->irq_spinlock,flags);
-			/* note the counters on entry */
-			cprev = info->icount;
-			spin_unlock_irqrestore(&info->irq_spinlock,flags);
-			while (1) {
-				interruptible_sleep_on(&info->status_event_wait_q);
-				/* see if a signal did it */
-				if (signal_pending(current))
-					return -ERESTARTSYS;
-				save_flags(flags); cli();
-				cnow = info->icount; /* atomic copy */
-				restore_flags(flags);
-				if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 
-				    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
-					return -EIO; /* no change => error */
-				if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
-				     ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
-				     ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
-				     ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
-					return 0;
-				}
-				cprev = cnow;
-			}
-			/* NOTREACHED */
+			return modem_input_wait(info,(int)arg);
 
 		/* 
 		 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
@@ -3243,7 +3384,8 @@
 	if (timeout)
 		char_time = MIN(char_time, timeout);
 		
-	if ( info->params.mode == MGSL_MODE_HDLC ) {
+	if ( info->params.mode == MGSL_MODE_HDLC ||
+		info->params.mode == MGSL_MODE_RAW ) {
 		while (info->tx_active) {
 			set_current_state(TASK_INTERRUPTIBLE);
 			schedule_timeout(char_time);
@@ -3601,7 +3743,8 @@
 	if (info->serial_signals & SerialSignal_RI)
 		strcat(stat_buf, "|RI");
 
-	if (info->params.mode == MGSL_MODE_HDLC) {
+	if (info->params.mode == MGSL_MODE_HDLC ||
+	    info->params.mode == MGSL_MODE_RAW ) {
 		ret += sprintf(buf+ret, " HDLC txok:%d rxok:%d",
 			      info->icount.txok, info->icount.rxok);
 		if (info->icount.txunder)
@@ -3742,15 +3885,15 @@
 		 *
 		 * This leaves 62 4K pages.
 		 *
-		 * The next N pages are used for a transmit frame. We
-		 * reserve enough 4K page blocks to hold the configured
-		 * MaxFrameSize
+		 * The next N pages are used for transmit frame(s). We
+		 * reserve enough 4K page blocks to hold the required
+		 * number of transmit dma buffers (num_tx_dma_buffers),
+		 * each of MaxFrameSize size.
 		 *
 		 * Of the remaining pages (62-N), determine how many can
 		 * be used to receive full MaxFrameSize inbound frames
 		 */
-		
-		info->tx_buffer_count = BuffersPerFrame;
+		info->tx_buffer_count = info->num_tx_dma_buffers * BuffersPerFrame;
 		info->rx_buffer_count = 62 - info->tx_buffer_count;
 	} else {
 		/* Calculate the number of PAGE_SIZE buffers needed for */
@@ -3763,7 +3906,7 @@
 		/* End of List condition if all receive buffers are used when */
 		/* using linked list DMA buffers. */
 
-		info->tx_buffer_count = BuffersPerFrame;
+		info->tx_buffer_count = info->num_tx_dma_buffers * BuffersPerFrame;
 		info->rx_buffer_count = (BuffersPerFrame * MAXRXFRAMES) + 6;
 		
 		/* 
@@ -3783,12 +3926,14 @@
 	if ( mgsl_alloc_buffer_list_memory( info ) < 0 ||
 		  mgsl_alloc_frame_memory(info, info->rx_buffer_list, info->rx_buffer_count) < 0 || 
 		  mgsl_alloc_frame_memory(info, info->tx_buffer_list, info->tx_buffer_count) < 0 || 
-		  mgsl_alloc_intermediate_rxbuffer_memory(info) < 0 ) {
+		  mgsl_alloc_intermediate_rxbuffer_memory(info) < 0  ||
+		  mgsl_alloc_intermediate_txbuffer_memory(info) < 0 ) {
 		printk("%s(%d):Can't allocate DMA buffer memory\n",__FILE__,__LINE__);
 		return -ENOMEM;
 	}
 	
 	mgsl_reset_rx_dma_buffers( info );
+  	mgsl_reset_tx_dma_buffers( info );
 
 	return 0;
 
@@ -4044,6 +4189,149 @@
 
 }	/* end of mgsl_free_intermediate_rxbuffer_memory() */
 
+/*
+ * mgsl_alloc_intermediate_txbuffer_memory()
+ *
+ * 	Allocate intermdiate transmit buffer(s) large enough to hold max_frame_size.
+ * 	This buffer is used to load transmit frames into the adapter's dma transfer
+ * 	buffers when there is sufficient space.
+ *
+ * Arguments:
+ *
+ *	info		pointer to device instance data
+ *
+ * Return Value:	0 if success, otherwise -ENOMEM
+ */
+int mgsl_alloc_intermediate_txbuffer_memory(struct mgsl_struct *info)
+{
+	int i;
+
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk("%s %s(%d)  allocating %d tx holding buffers\n",
+				info->device_name, __FILE__,__LINE__,info->num_tx_holding_buffers);
+
+	memset(info->tx_holding_buffers,0,sizeof(info->tx_holding_buffers));
+
+	for ( i=0; i<info->num_tx_holding_buffers; ++i) {
+		info->tx_holding_buffers[i].buffer =
+			kmalloc(info->max_frame_size, GFP_KERNEL);
+		if ( info->tx_holding_buffers[i].buffer == NULL )
+			return -ENOMEM;
+	}
+
+	return 0;
+
+}	/* end of mgsl_alloc_intermediate_txbuffer_memory() */
+
+/*
+ * mgsl_free_intermediate_txbuffer_memory()
+ *
+ *
+ * Arguments:
+ *
+ *	info		pointer to device instance data
+ *
+ * Return Value:	None
+ */
+void mgsl_free_intermediate_txbuffer_memory(struct mgsl_struct *info)
+{
+	int i;
+
+	for ( i=0; i<info->num_tx_holding_buffers; ++i ) {
+		if ( info->tx_holding_buffers[i].buffer ) {
+				kfree(info->tx_holding_buffers[i].buffer);
+				info->tx_holding_buffers[i].buffer=NULL;
+		}
+	}
+
+	info->get_tx_holding_index = 0;
+	info->put_tx_holding_index = 0;
+	info->tx_holding_count = 0;
+
+}	/* end of mgsl_free_intermediate_txbuffer_memory() */
+
+
+/*
+ * load_next_tx_holding_buffer()
+ *
+ * attempts to load the next buffered tx request into the
+ * tx dma buffers
+ *
+ * Arguments:
+ *
+ *	info		pointer to device instance data
+ *
+ * Return Value:	1 if next buffered tx request loaded
+ * 			into adapter's tx dma buffer,
+ * 			0 otherwise
+ */
+int load_next_tx_holding_buffer(struct mgsl_struct *info)
+{
+	int ret = 0;
+
+	if ( info->tx_holding_count ) {
+		/* determine if we have enough tx dma buffers
+		 * to accomodate the next tx frame
+		 */
+		struct tx_holding_buffer *ptx =
+			&info->tx_holding_buffers[info->get_tx_holding_index];
+		int num_free = num_free_tx_dma_buffers(info);
+		int num_needed = ptx->buffer_size / DMABUFFERSIZE;
+		if ( ptx->buffer_size % DMABUFFERSIZE )
+			++num_needed;
+
+		if (num_needed <= num_free) {
+			info->xmit_cnt = ptx->buffer_size;
+			mgsl_load_tx_dma_buffer(info,ptx->buffer,ptx->buffer_size);
+
+			--info->tx_holding_count;
+			if ( ++info->get_tx_holding_index >= info->num_tx_holding_buffers)
+				info->get_tx_holding_index=0;
+
+			/* restart transmit timer */
+			del_timer(&info->tx_timer);
+			info->tx_timer.expires = jiffies + jiffies_from_ms(5000);
+			add_timer(&info->tx_timer);
+
+			ret = 1;
+		}
+	}
+
+	return ret;
+}
+
+/*
+ * save_tx_buffer_request()
+ *
+ * attempt to store transmit frame request for later transmission
+ *
+ * Arguments:
+ *
+ *	info		pointer to device instance data
+ * 	Buffer		pointer to buffer containing frame to load
+ * 	BufferSize	size in bytes of frame in Buffer
+ *
+ * Return Value:	1 if able to store, 0 otherwise
+ */
+int save_tx_buffer_request(struct mgsl_struct *info,const char *Buffer, unsigned int BufferSize)
+{
+	struct tx_holding_buffer *ptx;
+
+	if ( info->tx_holding_count >= info->num_tx_holding_buffers ) {
+		return 0;	        /* all buffers in use */
+	}
+
+	ptx = &info->tx_holding_buffers[info->put_tx_holding_index];
+	ptx->buffer_size = BufferSize;
+	memcpy( ptx->buffer, Buffer, BufferSize);
+
+	++info->tx_holding_count;
+	if ( ++info->put_tx_holding_index >= info->num_tx_holding_buffers)
+		info->put_tx_holding_index=0;
+
+	return 1;
+}
+
 int mgsl_claim_resources(struct mgsl_struct *info)
 {
 	if (request_region(info->io_base,info->io_addr_size,"synclink") == NULL) {
@@ -4120,7 +4408,7 @@
 	return 0;
 errout:
 	mgsl_release_resources(info);
-	return ENODEV;
+	return -ENODEV;
 
 }	/* end of mgsl_claim_resources() */
 
@@ -4141,6 +4429,7 @@
 	}
 	mgsl_free_dma_buffers(info);
 	mgsl_free_intermediate_rxbuffer_memory(info);
+     	mgsl_free_intermediate_txbuffer_memory(info);
 	
 	if ( info->io_addr_requested ) {
 		release_region(info->io_base,info->io_addr_size);
@@ -4187,6 +4476,20 @@
 		if (maxframe[info->line])
 			info->max_frame_size = maxframe[info->line];
 		info->dosyncppp = dosyncppp[info->line];
+
+		if (txdmabufs[info->line]) {
+			info->num_tx_dma_buffers = txdmabufs[info->line];
+			if (info->num_tx_dma_buffers < 1)
+				info->num_tx_dma_buffers = 1;
+		}
+
+		if (txholdbufs[info->line]) {
+			info->num_tx_holding_buffers = txholdbufs[info->line];
+			if (info->num_tx_holding_buffers < 1)
+				info->num_tx_holding_buffers = 1;
+			else if (info->num_tx_holding_buffers > MAX_TX_HOLDING_BUFFERS)
+				info->num_tx_holding_buffers = MAX_TX_HOLDING_BUFFERS;
+		}
 	}
 
 	mgsl_device_count++;
@@ -4255,6 +4558,8 @@
 		spin_lock_init(&info->netlock);
 		memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
 		info->idle_mode = HDLC_TXIDLE_FLAGS;		
+		info->num_tx_dma_buffers = 1;
+		info->num_tx_holding_buffers = 0;
 	}
 	
 	return info;
@@ -4432,6 +4737,7 @@
 	unsigned long flags;
 	int rc;
 	struct mgsl_struct *info;
+	struct mgsl_struct *tmp;
 
 	printk("Unloading %s: version %s\n", driver_name, driver_version);
 	save_flags(flags);
@@ -4451,7 +4757,9 @@
 			mgsl_sppp_delete(info);
 #endif
 		mgsl_release_resources(info);
+		tmp = info;
 		info = info->next_device;
+		kfree(tmp);
 	}
 	
 	if (tmp_buf) {
@@ -4691,6 +4999,27 @@
 		 *
 		 * 0000 0110 0000 0110 = 0x0606
 		 */
+		if (info->params.mode == MGSL_MODE_RAW) {
+			RegValue = 0x0001;		/* Set Receive mode = external sync */
+
+			usc_OutReg( info, IOCR,		/* Set IOCR DCD is RxSync Detect Input */
+				(unsigned short)((usc_InReg(info, IOCR) & ~(BIT13|BIT12)) | BIT12));
+
+			/*
+			 * TxSubMode:
+			 * 	CMR <15>		0	Don't send CRC on Tx Underrun
+			 * 	CMR <14>		x	undefined
+			 * 	CMR <13>		0	Send preamble before openning sync
+			 * 	CMR <12>		0	Send 8-bit syncs, 1=send Syncs per TxLength
+			 *
+			 * TxMode:
+			 * 	CMR <11-8)	0100	MonoSync
+			 *
+			 * 	0x00 0100 xxxx xxxx  04xx
+			 */
+			RegValue |= 0x0400;
+		}
+		else {
 
 		RegValue = 0x0606;
 
@@ -4700,12 +5029,14 @@
 			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.flags & HDLC_FLAG_SHARE_ZERO )
+	if ( info->params.mode == MGSL_MODE_HDLC &&
+		(info->params.flags & HDLC_FLAG_SHARE_ZERO) )
 		RegValue |= BIT12;
 
 	if ( info->params.addr_filter != 0xff )
@@ -4745,15 +5076,13 @@
 	case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break;
 	}
 
-	if ( info->params.crc_type == HDLC_CRC_16_CCITT )
+	if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_16_CCITT )
 		RegValue |= BIT9;
-	else if ( info->params.crc_type == HDLC_CRC_32_CCITT )
+	else if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_32_CCITT )
 		RegValue |= ( BIT12 | BIT10 | BIT9 );
 
 	usc_OutReg( info, RMR, RegValue );
 
-
- 
 	/* Set the Receive count Limit Register (RCLR) to 0xffff. */
 	/* When an opening flag of an SDLC frame is recognized the */
 	/* Receive Character count (RCC) is loaded with the value in */
@@ -4822,9 +5151,9 @@
 	case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break;
 	}
 
-	if ( info->params.crc_type == HDLC_CRC_16_CCITT )
+	if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_16_CCITT )
 		RegValue |= BIT9 + BIT8;
-	else if ( info->params.crc_type == HDLC_CRC_32_CCITT )
+	else if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_32_CCITT )
 		RegValue |= ( BIT12 | BIT10 | BIT9 | BIT8);
 
 	usc_OutReg( info, TMR, RegValue );
@@ -5495,7 +5824,8 @@
 	usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) );
 	usc_RTCmd( info, RTCmd_PurgeRxFifo );
 
-	if ( info->params.mode == MGSL_MODE_HDLC ) {
+	if ( info->params.mode == MGSL_MODE_HDLC ||
+		info->params.mode == MGSL_MODE_RAW ) {
 		/* DMA mode Transfers */
 		/* Program the DMA controller. */
 		/* Enable the DMA controller end of buffer interrupt. */
@@ -5585,8 +5915,14 @@
 			/* Transmit DMA buffer is loaded, so program USC */
 			/* to send the frame contained in the buffers.	 */
 
+			FrameSize = info->tx_buffer_list[info->start_tx_dma_buffer].rcc;
 
-			FrameSize = info->tx_buffer_list[0].rcc;
+			/* if operating in Raw sync mode, reset the rcc component
+			 * of the tx dma buffer entry, otherwise, the serial controller
+			 * will send a closing sync char after this count.
+			 */
+	    		if ( info->params.mode == MGSL_MODE_RAW )
+				info->tx_buffer_list[info->start_tx_dma_buffer].rcc = 0;
 
 			/* Program the Transmit Character Length Register (TCLR) */
 			/* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */
@@ -5595,7 +5931,7 @@
 			usc_RTCmd( info, RTCmd_PurgeTxFifo );
 
 			/* Program the address of the 1st DMA Buffer Entry in linked list */
-			phys_addr = info->tx_buffer_list[0].phys_entry;
+			phys_addr = info->tx_buffer_list[info->start_tx_dma_buffer].phys_entry;
 			usc_OutDmaReg( info, NTARL, (u16)phys_addr );
 			usc_OutDmaReg( info, NTARU, (u16)(phys_addr >> 16) );
 
@@ -5603,6 +5939,19 @@
 			usc_ClearIrqPendingBits( info, TRANSMIT_STATUS );
 			usc_EnableInterrupts( info, TRANSMIT_STATUS );
 
+			if ( info->params.mode == MGSL_MODE_RAW &&
+					info->num_tx_dma_buffers > 1 ) {
+			   /* When running external sync mode, attempt to 'stream' transmit  */
+			   /* by filling tx dma buffers as they become available. To do this */
+			   /* we need to enable Tx DMA EOB Status interrupts :               */
+			   /*                                                                */
+			   /* 1. Arm End of Buffer (EOB) Transmit DMA Interrupt (BIT2 of TDIAR) */
+			   /* 2. Enable Transmit DMA Interrupts (BIT0 of DICR) */
+
+			   usc_OutDmaReg( info, TDIAR, BIT2|BIT3 );
+			   usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT0) );
+			}
+
 			/* Initialize Transmit DMA Channel */
 			usc_DmaCmd( info, DmaCmd_InitTxChannel );
 			
@@ -6026,6 +6375,9 @@
 void usc_loopback_frame( struct mgsl_struct *info )
 {
 	int i;
+	unsigned long oldmode = info->params.mode;
+
+	info->params.mode = MGSL_MODE_HDLC;
 	
 	usc_DisableMasterIrqBit( info );
 
@@ -6079,6 +6431,8 @@
 
 	usc_EnableMasterIrqBit(info);
 
+	info->params.mode = oldmode;
+
 }	/* end of usc_loopback_frame() */
 
 /* usc_set_sync_mode()	Programs the USC for SDLC communications.
@@ -6130,6 +6484,38 @@
 	info->tcsr_value += usc_idle_mode;
 	usc_OutReg(info, TCSR, info->tcsr_value);
 
+	/*
+	 * if SyncLink WAN adapter is running in external sync mode, the
+	 * transmitter has been set to Monosync in order to try to mimic
+	 * a true raw outbound bit stream. Monosync still sends an open/close
+	 * sync char at the start/end of a frame. Try to match those sync
+	 * patterns to the idle mode set here
+	 */
+	if ( info->params.mode == MGSL_MODE_RAW ) {
+		unsigned char syncpat = 0;
+		switch( info->idle_mode ) {
+		case HDLC_TXIDLE_FLAGS:
+			syncpat = 0x7e;
+			break;
+		case HDLC_TXIDLE_ALT_ZEROS_ONES:
+			syncpat = 0x55;
+			break;
+		case HDLC_TXIDLE_ZEROS:
+		case HDLC_TXIDLE_SPACE:
+			syncpat = 0x00;
+			break;
+		case HDLC_TXIDLE_ONES:
+		case HDLC_TXIDLE_MARK:
+			syncpat = 0xff;
+			break;
+		case HDLC_TXIDLE_ALT_MARK_SPACE:
+			syncpat = 0xaa;
+			break;
+		}
+
+		usc_SetTransmitSyncChars(info,syncpat,syncpat);
+	}
+
 }	/* end of usc_set_txidle() */
 
 /* usc_get_serial_signals()
@@ -6307,6 +6693,48 @@
  */
 
 /*
+ * mgsl_reset_tx_dma_buffers()
+ *
+ * 	Set the count for all transmit buffers to 0 to indicate the
+ * 	buffer is available for use and set the current buffer to the
+ * 	first buffer. This effectively makes all buffers free and
+ * 	discards any data in buffers.
+ *
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+void mgsl_reset_tx_dma_buffers( struct mgsl_struct *info )
+{
+	unsigned int i;
+
+	for ( i = 0; i < info->tx_buffer_count; i++ ) {
+		*((unsigned long *)&(info->tx_buffer_list[i].count)) = 0;
+	}
+
+	info->current_tx_buffer = 0;
+	info->start_tx_dma_buffer = 0;
+	info->tx_dma_buffers_used = 0;
+
+	info->get_tx_holding_index = 0;
+	info->put_tx_holding_index = 0;
+	info->tx_holding_count = 0;
+
+}	/* end of mgsl_reset_tx_dma_buffers() */
+
+/*
+ * num_free_tx_dma_buffers()
+ *
+ * 	returns the number of free tx dma buffers available
+ *
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	number of free tx dma buffers
+ */
+int num_free_tx_dma_buffers(struct mgsl_struct *info)
+{
+	return info->tx_buffer_count - info->tx_dma_buffers_used;
+}
+
+/*
  * mgsl_reset_rx_dma_buffers()
  * 
  * 	Set the count for all receive buffers to DMABUFFERSIZE
@@ -6392,10 +6820,11 @@
 	unsigned int StartIndex, EndIndex;	/* index of 1st and last buffers of Rx frame */
 	unsigned short status;
 	DMABUFFERENTRY *pBufEntry;
-	unsigned int framesize;
+	unsigned int framesize = 0;
 	int ReturnCode = 0;
 	unsigned long flags;
 	struct tty_struct *tty = info->tty;
+	int return_frame = 0;
 	
 	/*
 	 * current_rx_buffer points to the 1st buffer of the next available
@@ -6451,14 +6880,20 @@
 			info->icount.rxabort++;
 		else if ( status & RXSTATUS_OVERRUN )
 			info->icount.rxover++;
-		else 
+		else {
 			info->icount.rxcrc++;
+			if ( info->params.crc_type & HDLC_CRC_RETURN_EX )
+				return_frame = 1;
+		}
 		framesize = 0;
 #ifdef CONFIG_SYNCLINK_SYNCPPP
 		info->netstats.rx_errors++;
 		info->netstats.rx_frame_errors++;
 #endif
-	} else {
+	} else
+		return_frame = 1;
+
+	if ( return_frame ) {
 		/* receive frame has no errors, get frame size.
 		 * The frame size is the starting value of the RCC (which was
 		 * set to 0xffff) minus the ending value of the RCC (decremented
@@ -6483,7 +6918,9 @@
 			MIN(framesize,DMABUFFERSIZE),0);	
 		
 	if (framesize) {
-		if (framesize > info->max_frame_size)
+		if ( ( (info->params.crc_type & HDLC_CRC_RETURN_EX) &&
+				((framesize+1) > info->max_frame_size) ) ||
+			(framesize > info->max_frame_size) )
 			info->icount.rxlong++;
 		else {
 			/* copy dma buffer(s) to contiguous intermediate buffer */
@@ -6491,6 +6928,7 @@
 			int index = StartIndex;
 			unsigned char *ptmp = info->intermediate_rxbuffer;
 
+			if ( !(status & RXSTATUS_CRC_ERROR))
 			info->icount.rxok++;
 			
 			while(copy_count) {
@@ -6509,6 +6947,18 @@
 					index = 0;
 			}
 
+			if ( info->params.crc_type & HDLC_CRC_RETURN_EX ) {
+				++framesize;
+				*ptmp = (status & RXSTATUS_CRC_ERROR ?
+						RX_CRC_ERROR :
+						RX_OK);
+
+				if ( debug_level >= DEBUG_LEVEL_DATA )
+					printk("%s(%d):mgsl_get_rx_frame(%s) rx frame status=%d\n",
+						__FILE__,__LINE__,info->device_name,
+						*ptmp);
+			}
+
 #ifdef CONFIG_SYNCLINK_SYNCPPP
 			if (info->netcount) {
 				/* pass frame to syncppp device */
@@ -6518,6 +6968,7 @@
 #endif
 			{
 				/* Call the line discipline receive callback directly. */
+				if ( tty && tty->ldisc.receive_buf )
 				tty->ldisc.receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
 			}
 		}
@@ -6547,6 +6998,180 @@
 
 }	/* end of mgsl_get_rx_frame() */
 
+/* mgsl_get_raw_rx_frame()
+ *
+ *     	This function attempts to return a received frame from the
+ *	receive DMA buffers when running in external loop mode. In this mode,
+ *	we will return at most one DMABUFFERSIZE frame to the application.
+ *	The USC receiver is triggering off of DCD going active to start a new
+ *	frame, and DCD going inactive to terminate the frame (similar to
+ *	processing a closing flag character).
+ *
+ *	In this routine, we will return DMABUFFERSIZE "chunks" at a time.
+ *	If DCD goes inactive, the last Rx DMA Buffer will have a non-zero
+ * 	status field and the RCC field will indicate the length of the
+ *	entire received frame. We take this RCC field and get the modulus
+ *	of RCC and DMABUFFERSIZE to determine if number of bytes in the
+ *	last Rx DMA buffer and return that last portion of the frame.
+ *
+ * Arguments:	 	info	pointer to device extension
+ * Return Value:	1 if frame returned, otherwise 0
+ */
+int mgsl_get_raw_rx_frame(struct mgsl_struct *info)
+{
+	unsigned int CurrentIndex, NextIndex;
+	unsigned short status;
+	DMABUFFERENTRY *pBufEntry;
+	unsigned int framesize = 0;
+	int ReturnCode = 0;
+	unsigned long flags;
+	struct tty_struct *tty = info->tty;
+
+	/*
+ 	 * current_rx_buffer points to the 1st buffer of the next available
+	 * receive frame. The status field is set by the 16C32 after
+	 * completing a receive frame. If the status field of this buffer
+	 * is zero, either the USC is still filling this buffer or this
+	 * is one of a series of buffers making up a received frame.
+	 *
+	 * If the count field of this buffer is zero, the USC is either
+	 * using this buffer or has used this buffer. Look at the count
+	 * field of the next buffer. If that next buffer's count is
+	 * non-zero, the USC is still actively using the current buffer.
+	 * Otherwise, if the next buffer's count field is zero, the
+	 * current buffer is complete and the USC is using the next
+	 * buffer.
+	 */
+	CurrentIndex = NextIndex = info->current_rx_buffer;
+	++NextIndex;
+	if ( NextIndex == info->rx_buffer_count )
+		NextIndex = 0;
+
+	if ( info->rx_buffer_list[CurrentIndex].status != 0 ||
+		(info->rx_buffer_list[CurrentIndex].count == 0 &&
+			info->rx_buffer_list[NextIndex].count == 0)) {
+		/*
+	 	 * Either the status field of this dma buffer is non-zero
+		 * (indicating the last buffer of a receive frame) or the next
+	 	 * buffer is marked as in use -- implying this buffer is complete
+		 * and an intermediate buffer for this received frame.
+	 	 */
+
+		status = info->rx_buffer_list[CurrentIndex].status;
+
+		if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN +
+				RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) {
+			if ( status & RXSTATUS_SHORT_FRAME )
+				info->icount.rxshort++;
+			else if ( status & RXSTATUS_ABORT )
+				info->icount.rxabort++;
+			else if ( status & RXSTATUS_OVERRUN )
+				info->icount.rxover++;
+			else
+				info->icount.rxcrc++;
+			framesize = 0;
+		} else {
+			/*
+			 * A receive frame is available, get frame size and status.
+			 *
+			 * The frame size is the starting value of the RCC (which was
+			 * set to 0xffff) minus the ending value of the RCC (decremented
+			 * once for each receive character) minus 2 or 4 for the 16-bit
+			 * or 32-bit CRC.
+			 *
+			 * If the status field is zero, this is an intermediate buffer.
+			 * It's size is 4K.
+			 *
+			 * If the DMA Buffer Entry's Status field is non-zero, the
+			 * receive operation completed normally (ie: DCD dropped). The
+			 * RCC field is valid and holds the received frame size.
+			 * It is possible that the RCC field will be zero on a DMA buffer
+			 * entry with a non-zero status. This can occur if the total
+			 * frame size (number of bytes between the time DCD goes active
+			 * to the time DCD goes inactive) exceeds 65535 bytes. In this
+			 * case the 16C32 has underrun on the RCC count and appears to
+			 * stop updating this counter to let us know the actual received
+			 * frame size. If this happens (non-zero status and zero RCC),
+			 * simply return the entire RxDMA Buffer
+			 */
+			if ( status ) {
+				/*
+				 * In the event that the final RxDMA Buffer is
+				 * terminated with a non-zero status and the RCC
+				 * field is zero, we interpret this as the RCC
+				 * having underflowed (received frame > 65535 bytes).
+				 *
+				 * Signal the event to the user by passing back
+				 * a status of RxStatus_CrcError returning the full
+				 * buffer and let the app figure out what data is
+				 * actually valid
+				 */
+				if ( info->rx_buffer_list[CurrentIndex].rcc )
+					framesize = RCLRVALUE - info->rx_buffer_list[CurrentIndex].rcc;
+				else
+					framesize = DMABUFFERSIZE;
+			}
+			else
+				framesize = DMABUFFERSIZE;
+		}
+
+		if ( framesize > DMABUFFERSIZE ) {
+			/*
+			 * if running in raw sync mode, ISR handler for
+			 * End Of Buffer events terminates all buffers at 4K.
+			 * If this frame size is said to be >4K, get the
+			 * actual number of bytes of the frame in this buffer.
+			 */
+			framesize = framesize % DMABUFFERSIZE;
+		}
+
+
+		if ( debug_level >= DEBUG_LEVEL_BH )
+			printk("%s(%d):mgsl_get_raw_rx_frame(%s) status=%04X size=%d\n",
+				__FILE__,__LINE__,info->device_name,status,framesize);
+
+		if ( debug_level >= DEBUG_LEVEL_DATA )
+			mgsl_trace_block(info,info->rx_buffer_list[CurrentIndex].virt_addr,
+				MIN(framesize,DMABUFFERSIZE),0);
+
+		if (framesize) {
+			/* copy dma buffer(s) to contiguous intermediate buffer */
+			/* NOTE: we never copy more than DMABUFFERSIZE bytes	*/
+
+			pBufEntry = &(info->rx_buffer_list[CurrentIndex]);
+			memcpy( info->intermediate_rxbuffer, pBufEntry->virt_addr, framesize);
+			info->icount.rxok++;
+
+			/* Call the line discipline receive callback directly. */
+			if ( tty && tty->ldisc.receive_buf )
+				tty->ldisc.receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
+		}
+
+		/* Free the buffers used by this frame. */
+		mgsl_free_rx_frame_buffers( info, CurrentIndex, CurrentIndex );
+
+		ReturnCode = 1;
+	}
+
+
+	if ( info->rx_enabled && info->rx_overflow ) {
+		/* The receiver needs to restarted because of
+		 * a receive overflow (buffer or FIFO). If the
+		 * receive buffers are now empty, then restart receiver.
+		 */
+
+		if ( !info->rx_buffer_list[CurrentIndex].status &&
+			info->rx_buffer_list[CurrentIndex].count ) {
+			spin_lock_irqsave(&info->irq_spinlock,flags);
+			usc_start_receiver(info);
+			spin_unlock_irqrestore(&info->irq_spinlock,flags);
+		}
+	}
+
+	return ReturnCode;
+
+}	/* end of mgsl_get_raw_rx_frame() */
+
 /* mgsl_load_tx_dma_buffer()
  * 
  * 	Load the transmit DMA buffer with the specified data.
@@ -6576,12 +7201,19 @@
 	 	info->cmr_value |= BIT13;			  
 	}
 		
+	/* begin loading the frame in the next available tx dma
+	 * buffer, remember it's starting location for setting
+	 * up tx dma operation
+	 */
+	i = info->current_tx_buffer;
+	info->start_tx_dma_buffer = i;
+
 	/* Setup the status and RCC (Frame Size) fields of the 1st */
 	/* buffer entry in the transmit DMA buffer list. */
 
-	info->tx_buffer_list[0].status = info->cmr_value & 0xf000;
-	info->tx_buffer_list[0].rcc    = BufferSize;
-	info->tx_buffer_list[0].count  = BufferSize;
+	info->tx_buffer_list[i].status = info->cmr_value & 0xf000;
+	info->tx_buffer_list[i].rcc    = BufferSize;
+	info->tx_buffer_list[i].count  = BufferSize;
 
 	/* Copy frame data from 1st source buffer to the DMA buffers. */
 	/* The frame data may span multiple DMA buffers. */
@@ -6590,6 +7222,9 @@
 		/* Get a pointer to next DMA buffer entry. */
 		pBufEntry = &info->tx_buffer_list[i++];
 			
+		if ( i == info->tx_buffer_count )
+			i=0;
+
 		/* Calculate the number of bytes that can be copied from */
 		/* the source buffer to this DMA buffer. */
 		if ( BufferSize > DMABUFFERSIZE )
@@ -6609,8 +7244,13 @@
 		/* Advance source pointer and reduce remaining data count. */
 		Buffer += Copycount;
 		BufferSize -= Copycount;
+
+		++info->tx_dma_buffers_used;
 	}
 
+	/* remember next available tx dma buffer */
+	info->current_tx_buffer = i;
+
 }	/* end of mgsl_load_tx_dma_buffer() */
 
 /*
@@ -6741,7 +7381,7 @@
 	unsigned int i;
 	char *TmpPtr;
 	BOOLEAN rc = TRUE;
-	unsigned short status;
+	unsigned short status=0;
 	unsigned long EndTime;
 	unsigned long flags;
 	MGSL_PARAMS tmp_params;
@@ -6998,7 +7638,7 @@
 		status = info->rx_buffer_list[0].status;
 
 		if ( status & (BIT8 + BIT3 + BIT1) ) {
-			/* receive error has occurred */
+			/* receive error has occured */
 			rc = FALSE;
 		} else {
 			if ( memcmp( info->tx_buffer_list[0].virt_addr ,
@@ -7219,7 +7859,9 @@
 	if ( debug_level >= DEBUG_LEVEL_INFO )
 		printk( "%s(%d):mgsl_tx_timeout(%s)\n",
 			__FILE__,__LINE__,info->device_name);
-	if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) {
+	if(info->tx_active &&
+	   (info->params.mode == MGSL_MODE_HDLC ||
+	    info->params.mode == MGSL_MODE_RAW) ) {
 		info->icount.txtimeout++;
 	}
 	spin_lock_irqsave(&info->irq_spinlock,flags);

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