patch-1.3.22 linux/drivers/char/scc.c

Next file: linux/drivers/char/scc_config.h
Previous file: linux/drivers/char/psaux.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.21/linux/drivers/char/scc.c linux/drivers/char/scc.c
@@ -1,54 +1,107 @@
 #include <linux/autoconf.h>	/* fastest method */
 #ifdef CONFIG_SCC
 
+#define RCS_ID "$Id: scc.c,v 1.25 1995/08/24 21:05:00 jreuter Exp jreuter $"
 
-#define BANNER "Z8530 SCC driver v1.8.17test.17.7.95 PE1AYX (c)\n"
+#define BANNER "Z8530 SCC driver v1.9.dl1bke (beta) by dl1bke\n"
 
-/* ******************************************************************** */
-/* *   SCC.C - Linux driver for Z8530 based HDLC cards for AX.25      *	*/ 
-/* ******************************************************************** */
+/*
 
-/* ********************************************************************
+   ********************************************************************
+   *   SCC.C - Linux driver for Z8530 based HDLC cards for AX.25      *
+   ********************************************************************
 
-	(c) Hans Alblas PE1AYX 1993,1994,1995
-	          Released for GNU
-	portions (c) 1995 by Joerg Reuter DL1BKE
-	         (c) 1993 Guido ten Dolle PE1NNZ
 
+   ********************************************************************
 
-   ********************************************************************	*/   
+	(c) 1993 - 1995 by Joerg Reuter DL1BKE
 
-/*
+	portions (c) 1994 Hans Alblas PE1AYX 
+	and      (c) 1993 Guido ten Dolle PE1NNZ
+
+   ********************************************************************
+   
+   The driver and the programs in this archive are UNDER CONSTRUCTION.
+   The code is likely to fail, and so your kernel could --- even 
+   a whole network. 
+
+   This driver is intended for Amateur Radio use. If you are running it
+   for commercial purposes, please drop me a note. I am nosy...
+
+   ...BUT:
+ 
+   ! You  m u s t  recognize the appropriate legislations of your country !
+   ! before you connect a radio to the SCC board and start to transmit or !
+   ! receive. The GPL allows you to use the  d r i v e r,  NOT the RADIO! !
+
+   For non-Amateur-Radio use please note that you might need a special
+   allowance/licence from the designer of the SCC Board and/or the
+   MODEM. 
+
+   This program is free software; you can redistribute it and/or modify 
+   it under the terms of the (modified) GNU General Public License 
+   delivered with the LinuX kernel source.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should find a copy of the GNU General Public License in 
+   /usr/src/linux/COPYING; 
+   
+   ******************************************************************** 
 
 
-   931100	- started with the projekt pe1ayx
-   9406??	- KISS-parameter setting and BayCom USCC support dl1bke
-   940613	- fixed memory leak ,main memory fragmentation problem,
-                  speeded up the interupt routines ,has now its own
-                  memory buffer pool and source cleanup. pe1ayx
-   940620	- bug fixed in line disipline change ,local ringbuf
-   		  reorganisation ,and some little bugfixes.
-   940715	- first release to the public (scc15b.tgz) pe1ayx		                
+   ...If you find any portions of the code that are copyrighted to you,
+   and you don't want to see in here, please send me a private (!) 
+   message to my internet site. I will change it as soon as possible. 
+   Please don't flame to the tcp-group or anywhere else. Thanks!
+
+   Joerg Reuter	ampr-net: dl1bke@db0pra.ampr.org
+		AX-25   : DL1BKE @ DB0ACH.#NRW.DEU.EU
+		Internet: jreuter@lykos.tng.oche.de
+		
+		
+   History of z8530drv:
+   --------------------
+
+   940913	- started to write the driver, rescued most of my own
+		  code (and Hans Alblas' memory buffer pool concept) from 
+		  an earlier project "sccdrv" which was initiated by 
+		  Guido ten Dolle. Not much of the old driver survived, 
+		  though. The first version I put my hands on was sccdrv1.3
+		  from August 1993. The memory buffer pool concept
+		  appeared in an unauthorized sccdrv version (1.5) from
+		  August 1994.
+
+   950131	- changed copyright notice to GPL without limitations.
+   
    950228	- (hopefully) fixed the reason for kernel panics in
-                  chk_rcv_queue() [stupid error] dl1bke
-   950229       - buffer timeout for vergotten buffer :-( pe1ayx                
-   950304       - fixed underrun/zcount handling dl1bke
-   950305	- the driver registers port addresses now dl1bke
-   950314	- fixed underrun interrupt handling again dl1bke
-   950514	- fixed slip tty wakeup (it wil now work again with kernel ax25)pe1ayx
-   950703	- rewrote the 8530 init/reset routines pe1ayx
-   950712	- rewrote the RXirq + buffering routines pe1ayx
-   950716	- It can now handle ax25 rx frame info > 256
-   O		- rewrite TX + buffering (there is a little mem leak ,but
-                  wil not be dangerous since my buffer timeout routine
-                  wil put back vergotten buffers in the queue pe1ayx
-   O		- change the tty i/o so that the tty wakeup is handled properly
-           	  pe1ayx
+                  chk_rcv_queue() [stupid error]
+                  
+   950304       - fixed underrun/zcount handling
+   
+   950305	- the driver registers port addresses now
+   
+   950314	- fixed underrun interrupt handling again
    
+   950512	- (hope to have) fixed hidden re-entrance problem
+   		  in scc_timer()
+   		  
+   950824	- received frames will be sent to the application
+   		  faster, clean-up of z8530_init()
+
    Thanks to:
-   DL1BKE Joerg - for some good ideas
+   ----------
+   
    PE1CHL Rob	- for a lot of good ideas from his SCC driver for DOS
    PE1NNZ Guido - for his port of the original driver to Linux
+   KA9Q   Phil  - from whom we stole the mbuf-structure
+   PA3AYX Hans  - who rewrote parts of the memory management and some 
+   		  minor, but nevertheless useful changes
+   DL8MBT Flori - for support
+   DG0FT  Rene  - for the BayCom USCC support
    PA3AOU Harry - for ESCC testing, information supply and support
    
    PE1KOX Rob, DG1RTF Thomas, ON5QK Roland, 
@@ -56,10 +109,18 @@
    and all who sent me bug reports and ideas... 
    
    
-      
+   NB -- if you find errors, change something, please let me know
+      	 first before you distribute it... And please don't touch
+   	 the version number. Just replace my callsign in
+   	 "v1.9.dl1bke" with your own. Just to avoid confusion...
+   	 
+   If you want to add your modification to the linux distribution
+   please (!) contact me first.
+   	  
+   Jörg Reuter DL1BKE
+  
 */
 
-
 #include <linux/errno.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
@@ -87,8 +148,17 @@
 #include <asm/segment.h>
 #include <asm/bitops.h>
 
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <time.h>
 #include <linux/kernel.h>
 
+#ifndef Z8530_MAJOR
+#define Z8530_MAJOR 22
+#endif
+
+long scc_init(long kmem_start);
 
 int scc_open(struct tty_struct *tty, struct file *filp);
 static void scc_close(struct tty_struct *tty, struct file *filp);
@@ -108,6 +178,8 @@
 static void z8530_init(void);
 
 static void scc_change_speed(struct scc_channel *scc);
+static void kiss_encode(struct scc_channel *scc);
+
 static void init_channel(struct scc_channel *scc);
 static void scc_key_trx (struct scc_channel *scc, char tx);
 static void scc_txint(register struct scc_channel *scc);
@@ -117,23 +189,16 @@
 static void scc_isr(int irq, struct pt_regs *regs);
 static void scc_timer(void);
 static void scc_init_timer(struct scc_channel *scc);
+static void scc_rx_timer(void);
 
 /* from serial.c */
 
-#ifndef MIN
-#define MIN(a,b)	((a) < (b) ? (a) : (b))
-#endif
-
-static unsigned char *tmp_buf = 0;
-static struct semaphore tmp_buf_sem = MUTEX;
-static unsigned int start_controle;
-
 static int baud_table[] = {
 	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
 	9600, 19200, 38400, 57600, 115200, 0 };
 
 
-struct tty_driver scc_driver;		    /* new in 1.1.xx */
+struct tty_driver scc_driver;
 static int scc_refcount;
 static struct tty_struct *scc_table[2*MAXSCC];
 static struct termios scc_termios[2 * MAXSCC];
@@ -146,8 +211,7 @@
 static struct sccbuf *sccfreelist[MAX_IBUFS] = {0};
 static int allocated_ibufs = 0;
 
-#define SERIAL_TYPE_NORMAL	1
-
+static struct rx_timer_CB rx_timer_cb;
 
 /* ******************************************************************** */
 /* *			Port Access Functions			      * */
@@ -203,6 +267,10 @@
 /* * 			Memory Buffer Management			*/
 /* ******************************************************************** */
 
+/* mbuf concept lent from KA9Q. Tnx PE1AYX for the buffer pool concept	*/
+/* (sorry, do you have any better ideas?) */
+
+
 /* allocate memory for the interrupt buffer pool */
 
 void scc_alloc_buffer_pool(void)
@@ -211,8 +279,6 @@
 	struct sccbuf *sccb;
 	struct mbuf   *bp;
 	
-	start_controle = 0;
-	
    	for (i = 0 ; i < MAX_IBUFS ; i++)
    	{
    		sccb = (struct sccbuf *)kmalloc(sizeof(struct sccbuf), GFP_ATOMIC);
@@ -290,7 +356,6 @@
 			sccfreelist[i]->bp->refcnt = 1;
 			sccfreelist[i]->bp->cnt = 0;
 			sccfreelist[i]->bp->in_use = 0;
-			sccfreelist[i]->bp->time_out = CURRENT_TIME + 300;
 		
 			restore_flags(flags);
 			return sccfreelist[i]->bp;
@@ -319,33 +384,7 @@
 		
 	save_flags(flags); cli();
 	bpnext = bp->next;
-/*=========================================================================*/	
-/*==      THIS IS A LITTLE ROUTINE TO FIX THE TX MEM LEAK                ==*/
-/*==             UNTIL I HAVE REWRITE THE TX ROUTINES                    ==*/
-/*==                 PE1AYX@PI8HRL.AMPR.ORG                              ==*/
-/*=========================================================================*/
-
-	 start_controle++;
-	 if(start_controle > 100){
-	  if(bp->type == BT_TRANSMIT){
-	    start_controle = 0;
-	    for(i = 0 ; i < allocated_ibufs ; i++)
-	    {
-	       if(sccfreelist[i]->inuse == 1)
-	        if(sccfreelist[i]->bp->type == BT_TRANSMIT)
-	         if(sccfreelist[i]->bp->time_out < CURRENT_TIME)
-		    {
-			sccfreelist[i]->bp->cnt = 0;
-			sccfreelist[i]->bp->refcnt = 0;
-			sccfreelist[i]->inuse = 0;
-		    }
- 	     }
-           } 
-	 }
-/*=========================================================================*/
-/*==                END OF THAT SILLY STUPID ROUTINE                     ==*/
-/*=========================================================================*/	 
-
+	
 	if (bp->dup)
 	{
 		for(i = 0 ; i < allocated_ibufs ; i++)
@@ -412,7 +451,7 @@
 	abp = bp->anext;
 	while (bp) bp = scc_return_buffer(bp, type);
 		
-	restore_flags(flags); cli();
+	restore_flags(flags);
 	return abp;
 }
 
@@ -512,13 +551,13 @@
 	
 	if (Vector_Latch)
 	{
-	    	while(1)
+	    	while(1)			   /* forever...? */
     		{ 
 			Outb(Vector_Latch, 0);      /* Generate INTACK */
         
 			/* Read the vector */
-			if((vector=Inb(Vector_Latch)) >= 16 * Nchips)
-				break; 
+			if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; 
+						/* ...not forever! */
           
 			/* Extract channel number and status from vector. */
 			/* Isolate channel nummer */
@@ -650,20 +689,39 @@
 /* Throw away received mbuf(s) when an error occurred */
 
 static inline void
+scc_toss_buffer(register struct scc_channel *scc)
+{
+	register struct mbuf *bp;
+	
+	if((bp = scc->rbp) != NULLBUF)
+	{
+		scc_free_chain(bp->next, BT_RECEIVE);
+		bp->next = NULLBUF;
+		scc->rbp1 = bp;         /* Don't throw this one away */
+		bp->cnt = 0;            /* Simply rewind it */
+		bp->in_use = 0;
+	}
+}
+
+static inline void
 flush_FIFO(register struct scc_channel *scc)
 {
 	register int k;
 	
 	for (k=0; k<3; k++)
 		Inb(scc->data);
-
-	if(scc->rxbufcnt > 0)
+		
+	if(scc->rbp != NULLBUF)	/* did we receive something? */
 	{
-		scc->stat.rxerrs++;
-		scc->rxbufcnt = 0;         /* throw away frame */
+		if(scc->rbp->next != NULLBUF || scc->rbp->cnt > 0)
+			scc->stat.rxerrs++;  /* then count it as an error */
+			
+		scc_toss_buffer(scc);         /* throw away buffer */
 	}
 }
 
+
+
 /* External/Status interrupt handler */
 static void
 scc_exint(register struct scc_channel *scc)
@@ -741,7 +799,7 @@
 static void
 scc_rxint(register struct scc_channel *scc)
 {
-	unsigned char ch;
+	register struct mbuf *bp;
 
 	scc->stat.rxints++;
 
@@ -752,120 +810,98 @@
 		return;
 	}
 
-	if (scc->rxbufcnt > 2044)		/* no buffer available? */
-	{
-		Inb(scc->data);		/* discard character */
-		or(scc,R3,ENT_HM);      /* enter hunt mode */
-		scc->rxbufcnt = 0;	/* throw away frame */
-		scc->stat.nospace++;    /* and count this error */
-		return;
+	if ((bp = scc->rbp1) == NULLBUF || bp->cnt >= bp->size)	
+	{ 				/* no buffer available or buffer full */
+		if (scc->rbp == NULLBUF)
+		{
+			if ((bp = scc_get_buffer(BT_RECEIVE)) != NULLBUF)
+				scc->rbp = scc->rbp1 = bp;
+				
+		}
+		else if ((bp = scc_get_buffer(BT_RECEIVE)))
+		{
+			scc_append_to_chain(&scc->rbp, bp);
+			scc->rbp1 = bp;
+		}
+		
+		if (bp == NULLBUF)		/* no buffer available? */
+		{
+			Inb(scc->data);		/* discard character */
+			or(scc,R3,ENT_HM);      /* enter hunt mode */
+			scc_toss_buffer(scc);	/* throw away buffers */
+			scc->stat.nospace++;    /* and count this error */
+			return;
+		}
 	}
 
-	if(scc->rxbufcnt == 0) /* make begin of kissframe */
-	{
-		scc->rxbuf[scc->rxbufcnt++] = FEND;
-		if (scc->kiss.not_slip)
-			scc->rxbuf[scc->rxbufcnt++] = 0;
-	}
+	/* now, we have a buffer. read character and store it */
+	bp->data[bp->cnt++] = Inb(scc->data);
+}
+
+/* kick rx_timer (try to send received frame or part of it ASAP) */
+/* !experimental! */
+
+static inline void
+kick_rx_timer(register struct scc_channel *scc)
+{
+	register unsigned long expires;
 
-	switch( ch = Inb(scc->data) )
+	if (!rx_timer_cb.lock)
 	{
-		case FEND:
-			scc->rxbuf[scc->rxbufcnt++] = FESC;
-			scc->rxbuf[scc->rxbufcnt++] = TFEND;
-			break;
-		case FESC:
-			scc->rxbuf[scc->rxbufcnt++] = FESC;
-			scc->rxbuf[scc->rxbufcnt++] = TFESC;
-			break;
-		default:
-			scc->rxbuf[scc->rxbufcnt++] = ch;
-	}
+		expires = timer_table[SCC_TIMER].expires - jiffies;
 
+		rx_timer_cb.expires = (expires > 1)? expires:1;
+		rx_timer_cb.scc = scc;
+		rx_timer_cb.lock = 1;
+	
+		timer_table[SCC_TIMER].fn = scc_rx_timer;
+		timer_table[SCC_TIMER].expires = jiffies + 1;
+		timer_active |= 1 << SCC_TIMER;
+	}
 }
 
-
 /* Receive Special Condition interrupt handler */
 static void
 scc_spint(register struct scc_channel *scc)
 {
 	register unsigned char status;
-	int i;
-	unsigned char	*cp;
-	char		*fp;
-	int		count;
-
+	register struct mbuf *bp;
 
 	scc->stat.spints++;
 
-	status = InReg(scc->ctrl,R1);		/* read Special Receive Condition status */
+	status = InReg(scc->ctrl,R1);		/* read receiver status */
 	
-	Inb(scc->data);				/* read byte */
+	Inb(scc->data);				/* throw away Rx byte */
 
-	if(status & Rx_OVR)			/* RX_OVerRrun? */
+	if(status & Rx_OVR)			/* receiver overrun */
 	{
-		scc->stat.rx_over++;
-		or(scc,R3,ENT_HM);              /* enter hunt mode */
-		scc->rxbufcnt = 0;              /* rewind the buffer */
+		scc->stat.rx_over++;                /* count them */
+		or(scc,R3,ENT_HM);              /* enter hunt mode for next flag */
+		scc_toss_buffer(scc);                 /* rewind the buffer and toss */
 	}
 	
-	if(status & END_FR && scc->rxbufcnt != 0)	/* END of FRame */
+	if(status & END_FR && scc->rbp != NULLBUF)	/* end of frame */
 	{
-		if (!(status & CRC_ERR) && (status & 0xe) == RES8 && scc->rxbufcnt > 0)
+		/* CRC okay, frame ends on 8 bit boundary and received something ? */
+		
+		if (!(status & CRC_ERR) && (status & 0xe) == RES8 && scc->rbp->cnt)
 		{
-		   scc->rxbufcnt--;	/*strip the CRC */
-  		   scc->rxbuf[scc->rxbufcnt++] = FEND;
-
-		   for(i = 0 ; i < scc->rxbufcnt ; i++)
-			{
-			if((scc->tty->flip.count + 1) < TTY_FLIPBUF_SIZE)
-				tty_insert_flip_char(scc->tty, scc->rxbuf[i], 0);
-			else    {
-				if (scc->tty->flip.buf_num) {
-					cp = scc->tty->flip.char_buf + TTY_FLIPBUF_SIZE;
-					fp = scc->tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
-					scc->tty->flip.buf_num = 0;
-					scc->tty->flip.char_buf_ptr = scc->tty->flip.char_buf;
-					scc->tty->flip.flag_buf_ptr = scc->tty->flip.flag_buf;
-				} else {
-					cp = scc->tty->flip.char_buf;
-					fp = scc->tty->flip.flag_buf;
-					scc->tty->flip.buf_num = 1;
-					scc->tty->flip.char_buf_ptr = scc->tty->flip.char_buf + TTY_FLIPBUF_SIZE;
-					scc->tty->flip.flag_buf_ptr = scc->tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
-					}
-				count = scc->tty->flip.count;
-				scc->tty->flip.count = 0;
-				scc->tty->ldisc.receive_buf(scc->tty, cp, fp, count);
-				tty_insert_flip_char(scc->tty, scc->rxbuf[i], 0);
-				}
-			}
-
-		if (scc->tty->flip.buf_num) {
-			cp = scc->tty->flip.char_buf + TTY_FLIPBUF_SIZE;
-			fp = scc->tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
-			scc->tty->flip.buf_num = 0;
-			scc->tty->flip.char_buf_ptr = scc->tty->flip.char_buf;
-			scc->tty->flip.flag_buf_ptr = scc->tty->flip.flag_buf;
-		} else {
-			cp = scc->tty->flip.char_buf;
-			fp = scc->tty->flip.flag_buf;
-			scc->tty->flip.buf_num = 1;
-			scc->tty->flip.char_buf_ptr = scc->tty->flip.char_buf + TTY_FLIPBUF_SIZE;
-			scc->tty->flip.flag_buf_ptr = scc->tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
-			}
-		count = scc->tty->flip.count;
-		scc->tty->flip.count = 0;
-		scc->tty->ldisc.receive_buf(scc->tty, cp, fp, count);
-		scc->stat.rxframes++;
-
-		scc->rxbufcnt = 0;
+			/* ignore last received byte (first of the CRC bytes) */
 			
-		} else {
-			scc->rxbufcnt = 0;		/* frame is not good */
+			for (bp = scc->rbp; bp->next != NULLBUF; bp = bp->next) ;
+				bp->cnt--;              /* last byte is first CRC byte */
+				
+			scc_enqueue(&scc->rcvq,scc->rbp);
+			scc->rbp = scc->rbp1 = NULLBUF;
+			scc->stat.rxframes++;
+			scc->stat.rx_queued++;
+			kick_rx_timer(scc);
+		} else {				/* a bad frame */
+			scc_toss_buffer(scc);		/* throw away frame */
 			scc->stat.rxerrs++;
-	  		}		
+		}
 	}
+	
 	Outb(scc->ctrl,ERR_RES);
 }
 
@@ -919,7 +955,6 @@
 	wr(scc,R5,Tx8|DTR|TxCRC_ENAB);	/* TX 8 bits/char, disabled, DTR */
 	wr(scc,R6,0);			/* SDLC address zero (not used) */
 	wr(scc,R7,FLAG);		/* SDLC flag value */
-	wr(scc,R9,VIS);			/* vector includes status */
 	wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */
 	wr(scc,R14, 0);
 
@@ -990,7 +1025,7 @@
 	Outb(scc->ctrl,RES_EXT_INT);	/* must be done twice */
 	
 	scc->status = InReg(scc->ctrl,R0);	/* read initial status */
-	scc->rxbufcnt = 0;
+
 	or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */
 	or(scc,R9,MIE);			/* master interrupt enable */
 			
@@ -1198,22 +1233,57 @@
 	scc->t_tail = scc->kiss.tailtime;
 }
 
+static inline void check_rcv_queue(register struct scc_channel *scc)
+{
+	register struct mbuf *bp;
+	
+	if (scc->stat.rx_queued > QUEUE_THRES)
+	{
+		if (scc->rcvq == NULLBUF)
+		{
+			printk("z8530drv: Warning - scc->stat.rx_queued shows overflow"
+			       " (%d) but queue is empty\n", scc->stat.rx_queued);
+			       
+			scc->stat.rx_queued = 0;	/* correct it */
+			scc->stat.nospace = 12345;	/* draw attention to it */
+			return;
+		}
+			
+		bp = scc->rcvq->anext;	/* don't use the one we currently use */
+		
+		while (bp && (scc->stat.rx_queued > QUEUE_HYST))
+		{
+			bp = scc_free_chain(bp, BT_RECEIVE);
+			scc->stat.rx_queued--;
+			scc->stat.nospace++;
+		}
+		
+		scc->rcvq->anext = bp;
+	}
+}
+
 static void
 scc_timer(void)
 {
 	register struct scc_channel *scc;
 	register int chan;
 	unsigned long flags;
+		
 
-	save_flags(flags); cli();
-	
 	for (chan = 0; chan < (Nchips * 2); chan++)
 	{
 		scc = &SCC_Info[chan];
 		
 		if (scc->tty && scc->init)
 		{
+			kiss_encode(scc);
+			
+			save_flags(flags); cli();
+			
+			check_rcv_queue(scc);
+			
 			/* KISS-TNC emulation */
+			
 			if (Expired(t_dwait)) dw_slot_timeout(scc)	; else
 			if (Expired(t_slot))  dw_slot_timeout(scc)	; else
 			if (Expired(t_txdel)) txdel_timeout(scc)   	; else
@@ -1224,10 +1294,12 @@
 			if (Expired(t_mbusy)) busy_timeout(scc);
 			if (Expired(t_maxk))  maxk_idle_timeout(scc);
 			if (Expired(t_idle))  maxk_idle_timeout(scc);
-
+			
+			restore_flags(flags);
 		}
 	}
-
+	
+	save_flags(flags); cli();
 	
 	timer_table[SCC_TIMER].fn = scc_timer;
 	timer_table[SCC_TIMER].expires = jiffies + HZ/TPS;
@@ -1257,6 +1329,24 @@
 }
 
 
+static void
+scc_rx_timer(void)
+{
+	unsigned long flags;
+	
+	kiss_encode(rx_timer_cb.scc);
+	
+	save_flags(flags); cli();
+	
+	timer_table[SCC_TIMER].fn = scc_timer;
+	timer_table[SCC_TIMER].expires = jiffies + rx_timer_cb.expires;
+	timer_active |= 1 << SCC_TIMER;
+	
+	rx_timer_cb.lock = 0;
+	
+	restore_flags(flags);
+}
+
 
 /* ******************************************************************** */
 /* *			KISS interpreter			      * */
@@ -1284,7 +1374,7 @@
 	case PARAM_TXTAIL:
 		scc->kiss.tailtime = VAL; break;
 	case PARAM_FULLDUP:
-		scc->kiss.fulldup = val; break; 
+		scc->kiss.fulldup = val; break;
 	case PARAM_WAIT:
 		scc->kiss.waittime = VAL; break;
 	case PARAM_MAXKEY:
@@ -1448,6 +1538,97 @@
 	
 }
 
+/* ----> Encode received data and write it to the flip-buffer  <---- */
+
+/* receive raw frame from SCC. used for AX.25 */
+static void
+kiss_encode(register struct scc_channel *scc)
+{
+	struct mbuf *bp,*bp2;
+	struct tty_struct * tty = scc->tty;
+	unsigned long flags; 
+	unsigned char ch;
+
+	if(!scc->rcvq)
+	{
+		scc->stat.rx_kiss_state = KISS_IDLE;
+		return;
+	}
+	
+	/* worst case: FEND 0 FESC TFEND -> 4 bytes */
+	
+	while(tty->flip.count < TTY_FLIPBUF_SIZE-3)
+	{ 
+		if (scc->rcvq->cnt)
+		{
+			bp = scc->rcvq;
+			
+			if (scc->stat.rx_kiss_state == KISS_IDLE)
+			{
+				tty_insert_flip_char(tty, FEND, 0);
+				
+				if (scc->kiss.not_slip)
+					tty_insert_flip_char(tty, 0, 0);
+					
+				scc->stat.rx_kiss_state = KISS_RXFRAME;
+			}
+				
+			switch(ch = bp->data[bp->in_use++])
+			{
+				case FEND:
+					tty_insert_flip_char(tty, FESC, 0);
+					tty_insert_flip_char(tty, TFEND, 0);
+					break;
+				case FESC:
+					tty_insert_flip_char(tty, FESC, 0);
+					tty_insert_flip_char(tty, TFESC, 0);
+					break;
+				default:
+					tty_insert_flip_char(tty, ch, 0);
+			}
+			
+			bp->cnt--;
+			
+ 		} else {
+			save_flags(flags); cli();
+			
+			while (!scc->rcvq->cnt)
+	 		{				 /* buffer empty? */
+				bp  = scc->rcvq->next;  /* next buffer */
+				bp2 = scc->rcvq->anext; /* next packet */
+				
+				
+				scc_return_buffer(scc->rcvq, BT_RECEIVE);
+				
+				if (!bp)	/* end of frame ? */
+				{
+					scc->rcvq = bp2;
+					
+					if (--scc->stat.rx_queued < 0)
+						scc->stat.rx_queued = 0;
+					
+					if (scc->stat.rx_kiss_state == KISS_RXFRAME)	/* new packet? */
+					{
+						tty_insert_flip_char(tty, FEND, 0); /* send FEND for old frame */
+						scc->stat.rx_kiss_state = KISS_IDLE; /* generate FEND for new frame */
+					}
+					
+					restore_flags(flags);
+					queue_task(&tty->flip.tqueue, &tq_timer);
+					return;
+					
+				} else scc->rcvq = bp; /* next buffer */
+			}
+			
+			restore_flags(flags);
+		}						
+		
+	}
+	
+ 	queue_task(&tty->flip.tqueue, &tq_timer); /* kick it... */
+}
+
+
 /* ******************************************************************* */
 /* *		Init channel structures, special HW, etc...	     * */
 /* ******************************************************************* */
@@ -1460,7 +1641,7 @@
 	int chip;
 	unsigned long flags;
 
-	/* reset all scc chips */
+	/* reset and pre-init all chips in the system */
 	for (chip = 0; chip < Nchips; chip++)
 	{
 		/* Special SCC cards */
@@ -1469,27 +1650,27 @@
 			Outb(Special_Port,0x08);	/* enable interrupt on the board */
 			
 		if(Board & (PC100 | PRIMUS))		/* this is a PC100/EAGLE card */
-			Outb(Special_Port,Option);	/* set the MODEM mode (22H normally) */
+			Outb(Special_Port,Option);	/* set the MODEM mode (0x22) */
 			
+		/* Init SCC */
+		
 		scc=&SCC_Info[2*chip];
 		if (!scc->ctrl) continue;
 		
 		save_flags(flags); cli();
 		
-		OutReg(scc->ctrl,R9,FHWRES);	/* force hardware reset */
-		OutReg(scc->ctrl,R9,0);		/* end hardware reset */
-		OutReg(scc->ctrl,R9,CHRA);	/* reset channel A */
-		OutReg(scc->ctrl,R9,CHRB);	/* reset channel B */
-		OutReg(scc->ctrl,R1, 0);	/* No Rx irq from channel A */
-		scc=&SCC_Info[2*chip+1];
-		OutReg(scc->ctrl,R1, 0);	/* No Rx irq from channel B */
-		scc=&SCC_Info[2*chip];
-		OutReg(scc->ctrl,R2, chip*16);	/* Set Interrupt vector */
- 
+		/* some general init we can do now */
+		
+		Outb(scc->ctrl, 0);
+		OutReg(scc->ctrl,R9,FHWRES);		/* force hardware reset */
+		udelay(100);				/* give it 'a bit' more time than required */
+		wr(scc, R2, chip*16);			/* interrupt vector */
+		wr(scc, R9, VIS);			/* vector includes status */
+
         	restore_flags(flags);
         }
 
-	if (Ivec == 2) Ivec = 9;
+	if (Ivec == 2) Ivec = 9;			/* this f... IBM AT-design! */
 	request_irq(Ivec, scc_isr,   SA_INTERRUPT, "AX.25 SCC");
  
 	Driver_Initialized = 1;
@@ -1541,9 +1722,6 @@
  	scc = &SCC_Info[chan];
  	
 	tty->driver_data = scc;
-        tty->termios->c_iflag = IGNBRK | IGNPAR;
-        tty->termios->c_cflag = B9600 | CS8 | CLOCAL;
-
  	tty->termios->c_cflag &= ~CBAUD; 
  	
 	if (!Driver_Initialized)
@@ -1574,28 +1752,7 @@
 	timer_table[SCC_TIMER].fn = scc_timer;
 	timer_table[SCC_TIMER].expires = 0;	/* now! */
 	timer_active |= 1 << SCC_TIMER;
-
-/*====================new pe1ayx====================
-planed for the new TX routines
-
-	if (!scc->xmit_buf) {
-		scc->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);
-		if (!scc->xmit_buf)
-			return -ENOMEM;
-	}
-
-	scc->xmit_cnt = scc->xmit_head = scc->xmit_tail = 0;
-
-
-====================new pe1ayx end================*/
-
-	if (!tmp_buf) {
-		tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL);
-		if (!tmp_buf)
-			return -ENOMEM;
-	}
-
-
+	
 	return 0;
 }
 
@@ -1748,7 +1905,7 @@
 				scc->kiss.softdcd = 0;		   /* hardware dcd */
 			}
 			
-			scc->init = 1;
+			scc->init = 1;			
 			
 			return 0;
 		}
@@ -1773,7 +1930,7 @@
 		
 		restore_flags(flags);
 			
-		put_fs_long(result,(unsigned long *) arg);
+		put_user_long(result,(unsigned int *) arg);
 		return 0;
 	case TIOCMBIS:
 	case TIOCMBIC:
@@ -1788,7 +1945,7 @@
 			scc->wreg[R5] &= ~RTS;
 			break;
 		case TIOCMSET:
-			value = get_fs_long((unsigned long *) arg);
+			value = get_user_long((unsigned int *) arg);
 			
 			if(value & TIOCM_DTR)
 				scc->wreg[R5] |= DTR;
@@ -1974,7 +2131,6 @@
 		
 		scc->sndq1->anext = bp;
 	}
-	
 }
 
 
@@ -1984,14 +2140,12 @@
 /* send raw frame to SCC. used for AX.25 */
 int scc_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
 {
-	unsigned long flags;
-	static unsigned char *p;
 	struct scc_channel * scc = tty->driver_data;
+	unsigned char tbuf[BUFSIZE], *p;
 	int cnt, cnt2;
-
-	if (!tty || !tmp_buf)
-		return 0;
-		
+	
+	if (!tty) return count;
+	
 	if (scc_paranoia_check(scc, tty->device, "scc_write"))
 		return 0;
 
@@ -1999,43 +2153,31 @@
 	
 	check_tx_queue(scc);
 
-	save_flags(flags);
-
 	cnt2 = count;
 	
 	while (cnt2)
 	{
-		cli();
-		cnt   = cnt2 > SERIAL_XMIT_SIZE? SERIAL_XMIT_SIZE:cnt2;
+		cnt   = cnt2 > BUFSIZE? BUFSIZE:cnt2;
 		cnt2 -= cnt;
 		
-		if (from_user){
-			down(&tmp_buf_sem);
-			memcpy_fromfs(tmp_buf, buf, cnt);
-			up(&tmp_buf_sem);
-			}
+		if (from_user)
+			memcpy_fromfs(tbuf, buf, cnt);
 		else
-			memcpy(tmp_buf, buf, cnt);
+			memcpy(tbuf, buf, cnt);
 		
 		buf += cnt;
 			
-		p=tmp_buf;
+		p=tbuf;
 		
 		while(cnt--) 
 		  if (kiss_decode(scc, *p++))
 		  {
 		  	scc->stat.nospace++;
-		  	restore_flags(flags);
 		  	return 0;
 		  }
 		  	
 	} /* while cnt2 */
-		
-	if ((scc->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) 
-	     && scc->tty->ldisc.write_wakeup)
-		(scc->tty->ldisc.write_wakeup)(scc->tty);
-
-	restore_flags(flags);
+	
 	return count;
 }
 				
@@ -2059,7 +2201,7 @@
 	struct scc_channel *scc = tty->driver_data;
 	
 	scc_paranoia_check(scc, tty->device, "scc_flush_chars"); /* just to annoy the user... */
-
+	
 	return;	/* no flush needed */
 }
 
@@ -2068,7 +2210,7 @@
 static int scc_write_room(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
-
+	
 	if (scc_paranoia_check(scc, tty->device, "scc_write_room"))
 		return 0;
 	
@@ -2084,7 +2226,7 @@
 static int scc_chars_in_buffer(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
-
+	
 	if (scc && scc->sndq2)
 		return scc->sndq2->cnt;
 	else
@@ -2094,35 +2236,37 @@
 static void scc_flush_buffer(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
-
+	
 	if (scc_paranoia_check(scc, tty->device, "scc_flush_buffer"))
 		return;
 		
 	scc->stat.tx_kiss_state = KISS_IDLE;
-
+	
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
 }
 
 static void scc_throttle(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
-
+	
 	if (scc_paranoia_check(scc, tty->device, "scc_throttle"))
 		return;
 		
 		
 	/* dummy */
-	
 }
 
 static void scc_unthrottle(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
-
+	
 	if (scc_paranoia_check(scc, tty->device, "scc_unthrottle"))
 		return;
 		
 	/* dummy */
-	
 }
 
 static void scc_start(struct tty_struct *tty)
@@ -2146,14 +2290,6 @@
 	/* dummy */
 }
 
-void scc_hangup(struct tty_struct *tty)
-{
-	struct scc_channel *scc = tty->driver_data;
-	
-	if (scc_paranoia_check(scc, tty->device, "scc_hangup"))
-		return;
-}
-
 
 /* ******************************************************************** */
 /* * 			Init SCC driver 			      * */
@@ -2161,11 +2297,7 @@
 
 long scc_init (long kmem_start)
 {
-
-	int chip;
-#ifdef VERBOSE_BOOTMSG
-	int chan;
-#endif	
+	int chip, chan;
 	register io_port ctrl;
 	long flags;
 	
@@ -2173,35 +2305,35 @@
         memset(&scc_driver, 0, sizeof(struct tty_driver));
         scc_driver.magic = TTY_DRIVER_MAGIC;
         scc_driver.name = "sc";
-        scc_driver.major = TTY_MAJOR;		
-        scc_driver.minor_start = 96;
+        scc_driver.major = Z8530_MAJOR;		
+        scc_driver.minor_start = 0;
         scc_driver.num = Nchips*2;
         scc_driver.type = TTY_DRIVER_TYPE_SERIAL;
-        scc_driver.subtype = SERIAL_TYPE_NORMAL;			/* not needed */
+        scc_driver.subtype = 0;			/* not needed */
         scc_driver.init_termios = tty_std_termios;
-        scc_driver.init_termios.c_iflag = IGNBRK | IGNPAR;
-        scc_driver.init_termios.c_cflag = B9600	| CS8 | CLOCAL;
+        scc_driver.init_termios.c_cflag = B9600	| CS8 | CREAD | HUPCL | CLOCAL;
         scc_driver.flags = TTY_DRIVER_REAL_RAW;
         scc_driver.refcount = &scc_refcount;	/* not needed yet */
         scc_driver.table = scc_table;
         scc_driver.termios = (struct termios **) scc_termios;
         scc_driver.termios_locked = (struct termios **) scc_termios_locked;
-
         scc_driver.open = scc_open;
         scc_driver.close = scc_close;
         scc_driver.write = scc_write;
+        scc_driver.start = scc_start;
+        scc_driver.stop = scc_stop;
+        
         scc_driver.put_char = scc_put_char;
         scc_driver.flush_chars = scc_flush_chars;        
 	scc_driver.write_room = scc_write_room;
 	scc_driver.chars_in_buffer = scc_chars_in_buffer;
 	scc_driver.flush_buffer = scc_flush_buffer;
-        scc_driver.ioctl = scc_ioctl;
+	
 	scc_driver.throttle = scc_throttle;
 	scc_driver.unthrottle = scc_unthrottle;
+        
+        scc_driver.ioctl = scc_ioctl;
         scc_driver.set_termios = scc_set_termios;
-        scc_driver.stop = scc_stop;
-        scc_driver.start = scc_start;
-        scc_driver.hangup = scc_hangup;
         
         if (tty_register_driver(&scc_driver))
            panic("Couldn't register Z8530 SCC driver\n");
@@ -2253,6 +2385,12 @@
  
         	restore_flags(flags);
         }
+
+#ifdef DO_FAST_RX
+        rx_timer_cb.lock = 0;
+#else
+	rx_timer_cb.lock = 1;
+#endif
         
 #ifdef VERBOSE_BOOTMSG
 	printk("Init Z8530 driver: %u channels, using irq %u\n",Nchips*2,Ivec);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this