patch-1.3.49 linux/drivers/char/scc.c
Next file: linux/drivers/char/scc_config.h
Previous file: linux/drivers/char/README.scc
Back to the patch index
Back to the overall index
- Lines: 2651
- Date:
Tue Dec 19 07:09:00 1995
- Orig file:
v1.3.48/linux/drivers/char/scc.c
- Orig date:
Wed Nov 8 07:11:31 1995
diff -u --recursive --new-file v1.3.48/linux/drivers/char/scc.c linux/drivers/char/scc.c
@@ -1,6 +1,6 @@
-#define RCS_ID "$Id: scc.c,v 1.26 1995/09/07 14:46:19 jreuter Exp jreuter $"
+#define RCS_ID "$Id: scc.c,v 1.41 1995/12/17 22:36:40 jreuter Exp jreuter $"
-#define BANNER "Z8530 SCC driver v1.9.dl1bke (beta) by dl1bke\n"
+#define BANNER "Z8530 SCC driver version 2.01.dl1bke (alpha) by DL1BKE\n"
/*
@@ -11,14 +11,13 @@
********************************************************************
- (c) 1993 - 1995 by Joerg Reuter DL1BKE
+ Copyright (c) 1993, 1995 Joerg Reuter DL1BKE
- portions (c) 1994 Hans Alblas PE1AYX
- and (c) 1993 Guido ten Dolle PE1NNZ
+ portions (c) 1993 Guido ten Dolle PE1NNZ
********************************************************************
- The driver and the programs in this archive are UNDER CONSTRUCTION.
+ The driver and the programs in the archive are UNDER CONSTRUCTION.
The code is likely to fail, and so your kernel could --- even
a whole network.
@@ -60,8 +59,8 @@
Internet: jreuter@lykos.tng.oche.de
- History of z8530drv:
- --------------------
+ Incomplete history of z8530drv:
+ -------------------------------
940913 - started to write the driver, rescued most of my own
code (and Hans Alblas' memory buffer pool concept) from
@@ -74,20 +73,47 @@
950131 - changed copyright notice to GPL without limitations.
- 950228 - (hopefully) fixed the reason for kernel panics in
- chk_rcv_queue() [stupid error]
-
- 950304 - fixed underrun/zcount handling
+ .
+ .
+ .
+
+ 950922 - using kernel timer chain
- 950305 - the driver registers port addresses now
-
- 950314 - fixed underrun interrupt handling again
+ 951002 - splitting timer routine, adding support for throttle/
+ unthrottle calls, removed typo in kiss decoder,
+ corrected termios settings.
+
+ 951011 - at last found one (the?) problem which caused the
+ driver to mess up buffers on heavy load pe1ayx was
+ complaining about. Quite simple to reproduce:
+ set a 9k6 port into audio loopback, add a route
+ to 44.99.99.99 on that route and do a
+ ping -f 44.99.99.99
+
+ 951013 - it still does not survive a flood ping...
- 950512 - (hope to have) fixed hidden re-entrance problem
- in scc_timer()
+ 951107 - axattach w/o "-s" option (or setting an invalid
+ baudrate) does not produce "division by zero" faults
+ anymore.
+
+ 951114 - rewrote memory management, took first steps to allow
+ compilation as a module. Well, it looks like a whole
+ new driver now. BTW: It d o e s survive the flood
+ ping at last...
+
+ 951116 - scc_config.h is gone -- the driver will be initialized
+ through ioctl-commands. Use sccinit.c for this purpose.
+
+ 951117 - Well --- what should I say: You can compile it as a
+ module now. And I solved the problem with slip.c...
+
+ 951120 - most ioctl() routines may be called w/o suser()
+ permissions, check if you've set the permissions of
+ /dev/scc* right! NOT 0666, you know it's evil ;-)
- 950824 - received frames will be sent to the application
- faster, clean-up of z8530_init()
+ 951217 - found silly bug in init_channel(), some bugfixes
+ for the "standalone" module
+
Thanks to:
----------
@@ -95,13 +121,13 @@
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
+ PA3AYX Hans - for some 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,
+ PE1KOX Rob, DG1RTF Thomas, ON5QK Roland, G4XYW Andy, Linus,
+ EI9GL Paul,
and all who sent me bug reports and ideas...
@@ -109,7 +135,7 @@
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...
+ "v2.01.dl1bke" with your own. Just to avoid confusion...
If you want to add your modification to the linux distribution
please (!) contact me first.
@@ -118,6 +144,25 @@
*/
+/* ----------------------------------------------------------------------- */
+
+#define DEBUG_BUFFERS /* keep defined unless it is really stable... */
+
+#undef SCC_DELAY /* perhaps a 486DX2 is a *bit* too fast */
+#undef SCC_LDELAY 5 /* slow it even a bit more down */
+#undef DONT_CHECK /* don't look if the SCCs you specified are available */
+
+#define MAXSCC 4 /* number of max. supported chips */
+#define RXBUFFERS 8 /* default number of RX buffers */
+#define TXBUFFERS 8 /* default number of TX buffers */
+#define BUFSIZE 384 /* must not exceed 4096-sizeof(mbuf) */
+#define TPS 25 /* scc_tx_timer(): Ticks Per Second */
+
+#define DEFAULT_CLOCK 4915200 /* default pclock if nothing is specified */
+
+/* ----------------------------------------------------------------------- */
+
+#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
@@ -135,9 +180,8 @@
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/malloc.h>
-#include <linux/scc.h>
#include <linux/delay.h>
-#include "scc_config.h"
+#include <linux/scc.h>
#include <asm/system.h>
#include <asm/io.h>
@@ -150,12 +194,23 @@
#include <time.h>
#include <linux/kernel.h>
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+#endif
+
#ifndef Z8530_MAJOR
#define Z8530_MAJOR 34
#endif
int scc_init(void);
+static struct mbuf * scc_enqueue_buffer(struct mbuf **queue, struct mbuf * buffer);
+static struct mbuf * scc_dequeue_buffer(struct mbuf **queue);
+static void alloc_buffer_pool(struct scc_channel *scc);
+static void free_buffer_pool(struct scc_channel *scc);
+static struct mbuf * scc_get_buffer(struct scc_channel *scc, char type);
+
int scc_open(struct tty_struct *tty, struct file *filp);
static void scc_close(struct tty_struct *tty, struct file *filp);
int scc_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count);
@@ -166,6 +221,7 @@
static void scc_flush_buffer(struct tty_struct *tty);
static int scc_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
static void scc_set_termios(struct tty_struct *tty, struct termios *old_termios);
+static void scc_set_ldisc(struct tty_struct *tty);
static void scc_throttle(struct tty_struct *tty);
static void scc_unthrottle(struct tty_struct *tty);
static void scc_start(struct tty_struct *tty);
@@ -183,9 +239,9 @@
static void scc_rxint(register struct scc_channel *scc);
static void scc_spint(register struct scc_channel *scc);
static void scc_isr(int irq, struct pt_regs *regs);
-static void scc_timer(void);
+static void scc_tx_timer(unsigned long);
+static void scc_rx_timer(unsigned long);
static void scc_init_timer(struct scc_channel *scc);
-static void scc_rx_timer(void);
/* from serial.c */
@@ -199,15 +255,21 @@
static struct tty_struct *scc_table[2*MAXSCC];
static struct termios scc_termios[2 * MAXSCC];
static struct termios scc_termios_locked[2 * MAXSCC];
+
+struct irqflags { unsigned char used : 1; } Ivec[16];
struct scc_channel SCC_Info[2 * MAXSCC]; /* information per channel */
+io_port SCC_ctrl[2 * MAXSCC]; /* Control ports */
unsigned char Random = 0; /* random number for p-persist */
unsigned char Driver_Initialized = 0;
-static struct sccbuf *sccfreelist[MAX_IBUFS] = {0};
-static int allocated_ibufs = 0;
+int Nchips = 0;
+io_port Vector_Latch = 0;
+
+struct semaphore scc_sem = MUTEX;
+unsigned char scc_wbuf[BUFSIZE];
-static struct rx_timer_CB rx_timer_cb;
+static struct termios scc_std_termios;
/* ******************************************************************** */
/* * Port Access Functions * */
@@ -219,9 +281,9 @@
#ifdef SCC_LDELAY
register unsigned char r;
Outb(port, reg);
- udelay(5);
+ udelay(SCC_LDELAY);
r=Inb(port);
- udelay(5);
+ udelay(SCC_LDELAY);
return r;
#else
Outb(port, reg);
@@ -233,8 +295,8 @@
OutReg(register io_port port, register unsigned char reg, register unsigned char val)
{
#ifdef SCC_LDELAY
- Outb(port, reg); udelay(5);
- Outb(port, val); udelay(5);
+ Outb(port, reg); udelay(SCC_LDELAY);
+ Outb(port, val); udelay(SCC_LDELAY);
#else
Outb(port, reg);
Outb(port, val);
@@ -263,243 +325,379 @@
/* * Memory Buffer Management */
/* ******************************************************************** */
-/* mbuf concept lent from KA9Q. Tnx PE1AYX for the buffer pool concept */
-/* (sorry, do you have any better ideas?) */
+/*
+ * The new buffer scheme uses a ring chain of buffers. This has the
+ * advantage to access both, first and last element of the list, very
+ * fast. It has the disadvantage to mess things up double if something
+ * is wrong.
+ *
+ * Every scc channel has its own buffer pool now with an adjustable
+ * number of buffers for transmit and receive buffers. The buffer
+ * size remains the same for each AX.25 frame, but is (as a semi-
+ * undocumented feature) adjustable within a range of 512 and 4096
+ * to benefit experiments with higher frame lengths. If you need
+ * larger frames... Well, you'd better choose a whole new concept
+ * for that, like a DMA capable I/O card and a block device driver...
+ *
+ */
+/* ------ Management of buffer queues ------ */
-/* allocate memory for the interrupt buffer pool */
-void scc_alloc_buffer_pool(void)
-{
- int i;
- struct sccbuf *sccb;
- struct mbuf *bp;
-
- for (i = 0 ; i < MAX_IBUFS ; i++)
- {
- sccb = (struct sccbuf *)kmalloc(sizeof(struct sccbuf), GFP_ATOMIC);
- bp = (struct mbuf *)kmalloc(sizeof(struct mbuf), GFP_ATOMIC);
-
- if ( !(sccb && bp) )
- {
- allocated_ibufs = --i;
-
- if (allocated_ibufs < 0)
- panic("scc_alloc_buffer_pool() - can't get buffer space");
- else
- printk("Warning: scc_alloc_buffer_pool() - allocated only %i buffers\n",i);
-
- return;
- }
-
- sccfreelist[i] = sccb;
- sccfreelist[i]->bp = bp;
- memset(sccfreelist[i]->bp ,0,sizeof(struct mbuf));
- sccfreelist[i]->inuse = 0;
- sccfreelist[i]->bp->type = 0;
- sccfreelist[i]->bp->refcnt = 0;
- sccfreelist[i]->bp->size = BUFSIZE;
- }
- allocated_ibufs = MAX_IBUFS;
-}
+/* Insert a buffer at the "end" of the chain */
-unsigned int scc_count_used_buffers(unsigned int * rx, unsigned int * tx)
+static struct mbuf *
+scc_enqueue_buffer(struct mbuf **queue, struct mbuf * buffer)
{
- unsigned int i, used = 0;
+ unsigned long flags;
+ struct mbuf * anchor;
- if (rx) *rx = 0;
- if (tx) *tx = 0;
+ save_flags(flags); cli(); /* do not disturb! */
+
+#ifdef DEBUG_BUFFERS
+ if (queue == NULLBUFP) /* called with illegal parameters, notify the user */
+
+ {
+ printk("z8530drv: Pointer to queue anchor is NULL pointer [enq]\n");
+ restore_flags(flags);
+ return NULLBUF;
+ }
- for (i = 0 ; i < allocated_ibufs ; i++)
+ if (buffer == NULLBUF)
{
- if (sccfreelist[i]->inuse)
- {
- switch (sccfreelist[i]->bp->type)
- {
- case BT_RECEIVE:
- if (rx) (*rx)++; break;
- case BT_TRANSMIT:
- if (tx) (*tx)++; break;
- }
-
- used++;
- }
+ printk("z8530drv: can't enqueue a NULL pointer\n");
+ restore_flags(flags);
+ return NULLBUF;
+ }
+#endif
+
+ anchor = *queue; /* the anchor is the "start" of the chain */
+
+ if (anchor == NULLBUF) /* found an empty list */
+ {
+ *queue = buffer; /* new anchor */
+ buffer->next = buffer->prev = NULLBUF;
+ } else
+ if (anchor->prev == NULLBUF) /* list has one member only */
+ {
+#ifdef DEBUG_BUFFERS
+ if (anchor->prev != NULLBUF) /* oops?! */
+ printk("weird --- anchor->prev is NULL but not anchor->next [enq]\n");
+#endif
+ anchor->next = anchor->prev = buffer;
+ buffer->next = buffer->prev = anchor;
+ } else
+#ifdef DEBUG_BUFFERS
+ if (anchor->next == NULLBUF) /* this has to be an error. Okay, make the best out of it */
+ {
+ printk("z8530drv: weird --- anchor->next is NULL but not anchor->prev [enq]\n");
+ anchor->next = anchor->prev = buffer;
+ buffer->next = buffer->prev = anchor;
+ } else
+#endif
+ { /* every other case */
+ buffer->prev = anchor->prev; /* self explaining, isn't it? */
+ buffer->next = anchor;
+ anchor->prev->next = buffer;
+ anchor->prev = buffer;
}
- return used;
+ restore_flags(flags);
+ return *queue; /* return "start" of chain */
}
-/* Allocate mbuf */
-struct mbuf *
-scc_get_buffer(char type)
+
+/* Remove a buffer from the "start" of the chain an return it */
+
+static struct mbuf *
+scc_dequeue_buffer(struct mbuf **queue)
{
- int i;
unsigned long flags;
+ struct mbuf *buffer;
+
+ save_flags(flags); cli();
+
+#ifdef DEBUG_BUFFERS
+ if (queue == NULLBUFP) /* called with illegal parameter */
- save_flags(flags); cli(); /* just to be sure */
+ {
+ printk("z8530drv: Pointer to queue anchor is NULL pointer [deq]\n");
+ restore_flags(flags);
+ return NULLBUF;
+ }
+#endif
- for (i = 0 ; i < allocated_ibufs ; i++)
+ buffer = *queue; /* head of the chain */
+
+ if (buffer != NULLBUF) /* not an empty list? */
{
- if(sccfreelist[i]->inuse == 0)
+ if (buffer->prev != NULLBUF) /* not last buffer? */
{
- sccfreelist[i]->inuse = 1;
- sccfreelist[i]->bp->type = type;
- sccfreelist[i]->bp->next = NULLBUF;
- sccfreelist[i]->bp->anext = NULLBUF;
- sccfreelist[i]->bp->dup = NULLBUF;
- sccfreelist[i]->bp->size = BUFSIZE;
- sccfreelist[i]->bp->refcnt = 1;
- sccfreelist[i]->bp->cnt = 0;
- sccfreelist[i]->bp->in_use = 0;
+#ifdef DEBUG_BUFFERS
+ if (buffer->next == NULLBUF)
+ { /* what?! */
+ printk("z8530drv: weird --- buffer->next is NULL but not buffer->prev [deq]\n");
+ } else
+#endif
+ if (buffer->prev->next == buffer->prev->prev)
+ { /* only one buffer remaining... */
+ buffer->next->prev = NULLBUF;
+ buffer->next->next = NULLBUF;
+ } else { /* the one remaining situation... */
+ buffer->next->prev = buffer->prev;
+ buffer->prev->next = buffer->next;
+ }
+ }
+#ifdef DEBUG_BUFFERS
+ else if (buffer->next != NULLBUF)
+ printk("z8530drv: weird --- buffer->prev is NULL but not buffer->next [deq]\n");
+#endif
+ *queue = buffer->next; /* new head of chain */
- restore_flags(flags);
- return sccfreelist[i]->bp;
- }
- }
+ buffer->next = NULLBUF; /* for securety only... */
+ buffer->prev = NULLBUF;
+ buffer->rw_ptr = buffer->data;
+ }
- printk("\nSCC scc_get_buffer(): buffer pool empty\n"); /* should never happen */
restore_flags(flags);
- return NULLBUF;
+ return buffer; /* give it away... */
}
+/* ------ buffer pool management ------ */
-/* Decrement the reference pointer in an mbuf. If it goes to zero,
- * free all resources associated with mbuf.
- * Return pointer to next mbuf in packet chain
- */
-struct mbuf *
-scc_return_buffer(register struct mbuf *bp, char type)
+
+/* allocate buffer pool for a channel */
+
+static void
+alloc_buffer_pool(struct scc_channel *scc)
{
- struct mbuf *bpnext;
- int i;
- unsigned long flags;
+ int k;
+ struct mbuf * bptr;
+ int buflen;
- if(!bp)
- return NULLBUF;
-
- save_flags(flags); cli();
- bpnext = bp->next;
+ buflen = sizeof(struct mbuf) + scc->stat.bufsize;
- if (bp->dup)
+ if (scc->stat.bufsize < 336) scc->stat.bufsize = 336;
+ if (buflen > 4096)
{
- for(i = 0 ; i < allocated_ibufs ; i++)
+ scc->stat.bufsize = 4096-sizeof(struct mbuf);
+ buflen = 4096;
+ }
+
+ if (scc->stat.rxbuffers < 4) scc->stat.rxbuffers = 4;
+ if (scc->stat.txbuffers < 4) scc->stat.txbuffers = 4;
+
+
+
+ /* allocate receive buffers for this channel */
+
+ for (k = 0; k < scc->stat.rxbuffers ; k++)
+ {
+ /* allocate memory for the struct and the buffer */
+
+ bptr = (struct mbuf *) kmalloc(buflen, GFP_ATOMIC);
+
+ /* should not happen, but who knows? */
+
+ if (bptr == NULLBUF)
{
- if(sccfreelist[i]->bp == bp->dup)
- {
- if (sccfreelist[i]->bp->type != type)
- {
- printk("scc_return_buffer(bp->dup, %i): wrong buffer type %i",
- type,sccfreelist[i]->bp->type);
- }
-
- sccfreelist[i]->bp->cnt = 0;
- sccfreelist[i]->bp->refcnt = 0;
- sccfreelist[i]->bp->in_use = 0;
- sccfreelist[i]->inuse = 0;
- bp->dup = NULLBUF;
- }
+ printk("z8530drv: %s: can't allocate memory for rx buffer pool", kdevname(scc->tty->device));
+ break;
}
+
+ /* clear memory */
+
+ memset(bptr, 0, buflen);
+
+ /* initialize structure */
+
+ bptr->rw_ptr = bptr->data;
+
+ /* and append the buffer to the pool */
+
+ scc_enqueue_buffer(&scc->rx_buffer_pool, bptr);
}
- /* Decrement reference count. If it has gone to zero, free it. */
- if(--bp->refcnt <= 0)
+ /* now do the same for the transmit buffers */
+
+ for (k = 0; k < scc->stat.txbuffers ; k++)
{
- for(i = 0 ; i < allocated_ibufs ; i++)
+ bptr = (struct mbuf *) kmalloc(buflen, GFP_ATOMIC);
+
+ if (bptr == NULLBUF)
{
- if(sccfreelist[i]->bp == bp)
- {
- if (sccfreelist[i]->bp->type != type)
- {
- printk("scc_return_buffer(bp, %i): wrong buffer type %i",
- type,sccfreelist[i]->bp->type);
- }
-
- sccfreelist[i]->bp->cnt = 0;
- sccfreelist[i]->bp->refcnt = 0;
- sccfreelist[i]->inuse = 0;
- restore_flags(flags);
- return bpnext;
- }
+ printk("z8530drv: %s: can't allocate memory for tx buffer pool", kdevname(scc->tty->device));
+ break;
}
- }
- printk("\nscc_return_buffer(): bogus pointer %p\n",bp);
- restore_flags(flags);
- return bpnext;
-}
-
-
-/* Free packet (a chain of mbufs). Return pointer to next packet on queue,
- * if any
- */
-struct mbuf *
-scc_free_chain(register struct mbuf *bp, char type)
-{
- register struct mbuf *abp;
- unsigned long flags;
-
- if(!bp)
- return NULLBUF;
+ memset(bptr, 0, buflen);
- save_flags(flags); cli();
-
- abp = bp->anext;
- while (bp) bp = scc_return_buffer(bp, type);
+ bptr->rw_ptr = bptr->data;
- restore_flags(flags);
- return abp;
+ scc_enqueue_buffer(&scc->tx_buffer_pool, bptr);
+ }
}
-/* Append mbuf to end of mbuf chain */
-void
-scc_append_to_chain(struct mbuf **bph,struct mbuf *bp)
+/* remove buffer pool */
+
+static void
+free_buffer_pool(struct scc_channel *scc)
{
- register struct mbuf *p;
+ struct mbuf * bptr;
unsigned long flags;
-
- if(bph == NULLBUFP || bp == NULLBUF)
- return;
+ int cnt;
+ /* this one is a bit tricky and probably dangerous. */
+
save_flags(flags); cli();
- if(*bph == NULLBUF)
+ /* esp. to free the buffers currently in use by ISR */
+
+ bptr = scc->rx_bp;
+ if (bptr != NULLBUF)
{
- /* First one on chain */
- *bph = bp;
- } else {
- for(p = *bph ; p->next != NULLBUF ; p = p->next)
- ;
- p->next = bp;
+ scc->rx_bp = NULLBUF;
+ scc_enqueue_buffer(&scc->rx_buffer_pool, bptr);
+ }
+
+ bptr = scc->tx_bp;
+ if (bptr != NULLBUF)
+ {
+ scc->tx_bp = NULLBUF;
+ scc_enqueue_buffer(&scc->tx_buffer_pool, bptr);
+ }
+
+ bptr = scc->kiss_decode_bp;
+ if (bptr != NULLBUF)
+ {
+ scc->kiss_decode_bp = NULLBUF;
+ scc_enqueue_buffer(&scc->tx_buffer_pool, bptr);
+ }
+
+ bptr = scc->kiss_encode_bp;
+ if (bptr != NULLBUF)
+ {
+ scc->kiss_encode_bp = NULLBUF;
+ scc_enqueue_buffer(&scc->rx_buffer_pool, bptr);
}
restore_flags(flags);
+
+
+ while (scc->rx_queue != NULLBUF)
+ {
+ bptr = scc_dequeue_buffer(&scc->rx_queue);
+ scc_enqueue_buffer(&scc->rx_buffer_pool, bptr);
+ }
+
+ while (scc->tx_queue != NULLBUF)
+ {
+ bptr = scc_dequeue_buffer(&scc->tx_queue);
+ scc_enqueue_buffer(&scc->tx_buffer_pool, bptr);
+ }
+
+ /* you want to know why we move every buffer back to the
+ buffer pool? Well, good question... ;-)
+ */
+
+ cnt = 0;
+
+ /* Probably because we just want a central position in the
+ code were we actually call free()?
+ */
+
+ while (scc->rx_buffer_pool != NULLBUF)
+ {
+ bptr = scc_dequeue_buffer(&scc->rx_buffer_pool);
+
+ if (bptr != NULLBUF)
+ {
+ cnt++;
+ kfree(bptr);
+ }
+ }
+
+ if (cnt < scc->stat.rxbuffers) /* hmm... hmm... :-( */
+ printk("z8530drv: oops, deallocated only %d of %d rx buffers\n", cnt, scc->stat.rxbuffers);
+ if (cnt > scc->stat.rxbuffers) /* WHAT?!! */
+ printk("z8530drv: oops, deallocated %d instead of %d rx buffers. Very strange.\n", cnt, scc->stat.rxbuffers);
+
+ cnt = 0;
+
+ while (scc->tx_buffer_pool != NULLBUF)
+ {
+ bptr = scc_dequeue_buffer(&scc->tx_buffer_pool);
+
+ if (bptr != NULLBUF)
+ {
+ cnt++;
+ kfree(bptr);
+ }
+ }
+
+ if (cnt < scc->stat.txbuffers)
+ printk("z8530drv: oops, deallocated only %d of %d tx buffers\n", cnt, scc->stat.txbuffers);
+ if (cnt > scc->stat.txbuffers)
+ printk("z8530drv: oops, deallocated %d instead of %d tx buffers. Very strange.\n", cnt, scc->stat.txbuffers);
}
+
+
+/* ------ rx/tx buffer management ------ */
+/*
+ get a fresh buffer from the pool if possible; if not: get one from
+ the queue. We will remove the oldest frame from the queue and hope
+ it was a good idea... ;-)
+
+ */
-/* Append packet (chain of mbufs) to end of packet queue */
-void
-scc_enqueue(struct mbuf **queue,struct mbuf *bp)
+static struct mbuf *
+scc_get_buffer(struct scc_channel *scc, char type)
{
- register struct mbuf *p;
- unsigned long flags;
-
- if(queue == NULLBUFP || bp == NULLBUF)
- return;
-
- save_flags(flags); cli();
+ struct mbuf * bptr;
- if(*queue == NULLBUF)
+ if (type == BT_TRANSMIT)
{
- /* List is empty, stick at front */
- *queue = bp;
+ bptr = scc_dequeue_buffer(&scc->tx_buffer_pool);
+
+ /* no free buffers in the pool anymore? */
+
+ if (bptr == NULLBUF)
+ {
+ printk("z8530drv: scc_get_buffer(%s): tx buffer pool empty\n", kdevname(scc->tty->device));
+
+ /* use the oldest from the queue instead */
+
+ bptr = scc_dequeue_buffer(&scc->tx_queue);
+
+ /* this should never, ever happen... */
+
+ if (bptr == NULLBUF)
+ printk("z8530drv: scc_get_buffer(): panic - even no buffer found in tx queue\n");
+ }
} else {
- for(p = *queue ; p->anext != NULLBUF ; p = p->anext)
- ;
- p->anext = bp;
+ bptr = scc_dequeue_buffer(&scc->rx_buffer_pool);
+
+ if (bptr == NULLBUF)
+ {
+ printk("z8530drv: scc_get_buffer(%s): rx buffer pool empty\n", kdevname(scc->tty->device));
+
+ bptr = scc_dequeue_buffer(&scc->rx_queue);
+
+ if (bptr == NULLBUF)
+ printk("z8530drv: scc_get_buffer(): panic - even no buffer found in rx queue\n");
+ }
}
- restore_flags(flags);
+
+ if (bptr != NULLBUF)
+ {
+ bptr->rw_ptr = bptr->data;
+ bptr->cnt = 0;
+ }
+
+ return bptr;
}
@@ -625,20 +823,6 @@
/* DCD/CTS and Rx/Tx errors */
-static inline void prepare_next_txframe(register struct scc_channel *scc)
-{
- if ((scc->tbp = scc->sndq))
- {
- scc->sndq = scc->sndq->anext;
- scc->stat.tx_state = TXS_NEWFRAME;
-
- } else {
- scc->stat.tx_state = TXS_BUSY;
- scc->t_tail = scc->kiss.tailtime;
- }
-}
-
-
/* Transmitter interrupt handler */
static void
scc_txint(register struct scc_channel *scc)
@@ -647,56 +831,60 @@
scc->stat.txints++;
- bp = scc->tbp;
-
- while (bp && !bp->cnt) /* find next buffer */
- bp = scc_return_buffer(bp, BT_TRANSMIT);
-
- if (bp == NULLBUF) /* no more buffers in this frame */
+ bp = scc->tx_bp;
+
+ if (bp == NULLBUF)
{
- if (--scc->stat.tx_queued < 0)
- scc->stat.tx_queued = 0;
+ do
+ {
+ if (bp != NULLBUF)
+ scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+
+ bp = scc_dequeue_buffer(&scc->tx_queue);
- Outb(scc->ctrl,RES_Tx_P); /* reset pending int */
- cl(scc,R10,ABUNDER); /* frame complete, allow CRC transmit */
- prepare_next_txframe(scc);
-
- } else { /* okay, send byte */
-
- if (scc->stat.tx_state == TXS_NEWFRAME)
- { /* first byte ? */
- Outb(scc->ctrl, RES_Tx_CRC); /* reset CRC generator */
- or(scc,R10,ABUNDER); /* re-install underrun protection */
- Outb(scc->data,bp->data[bp->in_use++]);
- /* send byte */
- if (!scc->enhanced) /* reset EOM latch */
- Outb(scc->ctrl, RES_EOM_L);
+ if (bp == NULLBUF)
+ {
+ scc->stat.tx_state = TXS_BUSY;
+ scc->t_tail = scc->kiss.tailtime;
- scc->stat.tx_state = TXS_ACTIVE;/* next byte... */
- } else {
- Outb(scc->data,bp->data[bp->in_use++]);
- }
+ Outb(scc->ctrl, RES_Tx_P); /* clear int */
+ return;
+ }
+
+ if ( scc->kiss.not_slip && (bp->cnt > 0) )
+ {
+ bp->rw_ptr++;
+ bp->cnt--;
+ }
+
+ } while (bp->cnt < 1);
- bp->cnt--; /* decrease byte count */
- scc->tbp=bp; /* store buffer address */
- }
-}
-
-/* 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)
+
+ Outb(scc->ctrl, RES_Tx_CRC); /* reset CRC generator */
+ or(scc,R10,ABUNDER); /* re-install underrun protection */
+ Outb(scc->data,*bp->rw_ptr); /* send byte */
+ if (!scc->enhanced) /* reset EOM latch */
+ Outb(scc->ctrl, RES_EOM_L);
+
+ scc->tx_bp = bp;
+ scc->stat.tx_state = TXS_ACTIVE; /* next byte... */
+ } else
+ if (bp->cnt <= 0)
{
- 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;
+ if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0;
+
+ Outb(scc->ctrl, RES_Tx_P); /* reset pending int */
+ cl(scc, R10, ABUNDER); /* send CRC */
+ scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+ scc->tx_bp = NULLBUF;
+ scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */
+ return;
+ } else {
+ Outb(scc->data,*bp->rw_ptr);
}
+
+ bp->rw_ptr++; /* increment pointer */
+ bp->cnt--; /* decrease byte count */
}
static inline void
@@ -707,17 +895,16 @@
for (k=0; k<3; k++)
Inb(scc->data);
- if(scc->rbp != NULLBUF) /* did we receive something? */
+ if(scc->rx_bp != NULLBUF) /* did we receive something? */
{
- 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 */
+ scc->stat.rxerrs++; /* then count it as an error */
+ scc_enqueue_buffer(&scc->rx_buffer_pool, scc->rx_bp);
+
+ scc->rx_bp = NULLBUF;
}
}
-
/* External/Status interrupt handler */
static void
scc_exint(register struct scc_channel *scc)
@@ -753,7 +940,7 @@
}
}
-
+#ifdef notdef
/* CTS: use external TxDelay (what's that good for?!) */
if (chg_and_stat & CTS) /* CTS is now ON */
@@ -762,14 +949,20 @@
scc->t_txdel = 0; /* kick it! */
}
+#endif
- if ((scc->stat.tx_state == TXS_ACTIVE) && (status & TxEOM))
+ if ( (status & TxEOM) && (scc->stat.tx_state == TXS_ACTIVE) )
{
scc->stat.tx_under++; /* oops, an underrun! count 'em */
Outb(scc->ctrl, RES_Tx_P);
Outb(scc->ctrl, RES_EXT_INT); /* reset ext/status interrupts */
scc->t_maxk = 1;
- scc->tbp = scc_free_chain(scc->tbp, BT_TRANSMIT);
+
+ if (scc->tx_bp != NULLBUF)
+ {
+ scc_enqueue_buffer(&scc->tx_buffer_pool, scc->tx_bp);
+ scc->tx_bp = NULLBUF;
+ }
if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0;
or(scc,R10,ABUNDER);
@@ -780,7 +973,13 @@
scc->stat.tx_under = 9999; /* errr... yes. */
Outb(scc->ctrl, RES_Tx_P); /* just to be sure */
scc->t_maxk = 1;
- scc->tbp = scc_free_chain(scc->tbp, BT_TRANSMIT);
+
+ if (scc->tx_bp != NULLBUF)
+ {
+ scc_enqueue_buffer(&scc->tx_buffer_pool, scc->tx_bp);
+ scc->tx_bp = NULLBUF;
+ }
+
if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0;
scc->kiss.tx_inhibit = 1; /* don't try it again! */
}
@@ -806,54 +1005,60 @@
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? */
+ bp = scc->rx_bp;
+
+ if (bp == NULLBUF)
+ {
+ bp = scc_get_buffer(scc, BT_RECEIVE);
+ if (bp == NULLBUF)
{
- 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 */
+ printk("scc_rxint(): panic --- cannot get a buffer\n");
+ Inb(scc->data);
+ or(scc, R3, ENT_HM);
+ scc->stat.nospace++;
return;
}
+
+ scc->rx_bp = bp;
}
-
+
+ if (bp->cnt > scc->stat.bufsize)
+ {
+#ifdef notdef
+ printk("scc_rxint(): oops, received huge frame...\n");
+#endif
+ scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
+ scc->rx_bp = NULLBUF;
+ Inb(scc->data);
+ or(scc, R3, ENT_HM);
+ return;
+ }
+
/* now, we have a buffer. read character and store it */
- bp->data[bp->cnt++] = Inb(scc->data);
+ *bp->rw_ptr = Inb(scc->data);
+ bp->rw_ptr++;
+ bp->cnt++;
}
/* kick rx_timer (try to send received frame or part of it ASAP) */
-/* !experimental! */
+
+/* of course we could define a "bottom half" routine to do the job,
+ but since its structures are saved in an array instead of a linked
+ list we would get in trouble if it clashes with another driver or
+ when we try to modularize the driver. IMHO we are fast enough
+ with a timer routine called on the next timer-INT... Your opinions?
+ */
static inline void
kick_rx_timer(register struct scc_channel *scc)
{
- register unsigned long expires;
-
- if (!rx_timer_cb.lock)
- {
- 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;
- }
+ if (scc->rx_t.next)
+ del_timer(&(scc->rx_t));
+
+ scc->rx_t.expires = jiffies + 1;
+ scc->rx_t.function = scc_rx_timer;
+ scc->rx_t.data = (unsigned long) scc;
+ add_timer(&scc->rx_t);
}
/* Receive Special Condition interrupt handler */
@@ -868,32 +1073,34 @@
status = InReg(scc->ctrl,R1); /* read receiver status */
Inb(scc->data); /* throw away Rx byte */
+ bp = scc->rx_bp;
if(status & Rx_OVR) /* receiver overrun */
{
- 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 */
+ scc->stat.rx_over++; /* count them */
+ or(scc,R3,ENT_HM); /* enter hunt mode for next flag */
+
+ if (bp) scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
+ scc->rx_bp = NULLBUF;
}
- if(status & END_FR && scc->rbp != NULLBUF) /* end of frame */
+ if(status & END_FR && bp != NULLBUF) /* end of frame */
{
/* CRC okay, frame ends on 8 bit boundary and received something ? */
- if (!(status & CRC_ERR) && (status & 0xe) == RES8 && scc->rbp->cnt)
+ if (!(status & CRC_ERR) && (status & 0xe) == RES8 && bp->cnt)
{
/* ignore last received byte (first of the CRC bytes) */
+ bp->cnt--;
- 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_enqueue_buffer(&scc->rx_queue, bp);
+ scc->rx_bp = NULLBUF;
scc->stat.rxframes++;
scc->stat.rx_queued++;
kick_rx_timer(scc);
} else { /* a bad frame */
- scc_toss_buffer(scc); /* throw away frame */
+ scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
+ scc->rx_bp = NULLBUF;
scc->stat.rxerrs++;
}
}
@@ -925,7 +1132,8 @@
static inline void set_speed(register struct scc_channel *scc)
{
- set_brg(scc, (unsigned) (Clock / (scc->modem.speed * 64)) - 2);
+ if (scc->modem.speed > 0) /* paranoia... */
+ set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2);
}
@@ -938,19 +1146,68 @@
OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */
}
+/*
+ * Initalization according to the Z8530 manual (SGS-Thomson's version):
+ *
+ * 1. Modes and constants
+ *
+ * WR9 11000000 chip reset
+ * WR4 XXXXXXXX Tx/Rx control, async or sync mode
+ * WR1 0XX00X00 select W/REQ (optional)
+ * WR2 XXXXXXXX program interrupt vector
+ * WR3 XXXXXXX0 select Rx control
+ * WR5 XXXX0XXX select Tx control
+ * WR6 XXXXXXXX sync character
+ * WR7 XXXXXXXX sync character
+ * WR9 000X0XXX select interrupt control
+ * WR10 XXXXXXXX miscellaneous control (optional)
+ * WR11 XXXXXXXX clock control
+ * WR12 XXXXXXXX time constant lower byte (optional)
+ * WR13 XXXXXXXX time constant upper byte (optional)
+ * WR14 XXXXXXX0 miscellaneous control
+ * WR14 XXXSSSSS commands (optional)
+ *
+ * 2. Enables
+ *
+ * WR14 000SSSS1 baud rate enable
+ * WR3 SSSSSSS1 Rx enable
+ * WR5 SSSS1SSS Tx enable
+ * WR0 10000000 reset Tx CRG (optional)
+ * WR1 XSS00S00 DMA enable (optional)
+ *
+ * 3. Interrupt status
+ *
+ * WR15 XXXXXXXX enable external/status
+ * WR0 00010000 reset external status
+ * WR0 00010000 reset external status twice
+ * WR1 SSSXXSXX enable Rx, Tx and Ext/status
+ * WR9 000SXSSS enable master interrupt enable
+ *
+ * 1 = set to one, 0 = reset to zero
+ * X = user defined, S = same as previous init
+ *
+ *
+ * Note that the implementation differs in some points from above scheme.
+ *
+ */
+
static void
init_channel(register struct scc_channel *scc)
{
unsigned long flags;
+
+ if (scc->rx_t.next) del_timer(&(scc->rx_t));
+ if (scc->tx_t.next) del_timer(&(scc->tx_t));
save_flags(flags); cli();
+ wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */
wr(scc,R1,0); /* no W/REQ operation */
wr(scc,R3,Rx8|RxCRC_ENAB); /* RX 8 bits/char, CRC, disabled */
- wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */
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);
@@ -989,20 +1246,19 @@
break;
case CLK_DIVIDER:
- wr(scc, R11, ((Board & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI);
+ wr(scc, R11, ((scc->brand & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI);
init_brg(scc);
break;
case CLK_EXTERNAL:
- wr(scc, R11, (Board & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP);
+ wr(scc, R11, (scc->brand & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP);
OutReg(scc->ctrl, R14, DISDPLL);
break;
}
- /* enable CTS (not for Baycom), ABORT & DCD interrupts */
- wr(scc,R15,((Board & BAYCOM) ? 0 : CTSIE)|BRKIE|DCDIE|TxUIE);
-
+ set_speed(scc); /* set baudrate */
+
if(scc->enhanced)
{
or(scc,R15,SHDLCE|FIFOE); /* enable FIFO, SDLC/HDLC Enhancements (From now R7 is R7') */
@@ -1017,17 +1273,21 @@
or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
}
+ /* enable CTS (not for Baycom), ABORT & DCD interrupts */
+ wr(scc,R15,((scc->brand & BAYCOM) ? 0 : CTSIE)|BRKIE|DCDIE|TxUIE);
+
Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */
-
- scc->status = InReg(scc->ctrl,R0); /* read initial status */
or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */
+
+ scc->status = InReg(scc->ctrl,R0); /* read initial status */
+
or(scc,R9,MIE); /* master interrupt enable */
+
+ scc_init_timer(scc);
restore_flags(flags);
-
- set_speed(scc);
}
@@ -1049,10 +1309,10 @@
if (scc->modem.speed < baud_table[1])
scc->modem.speed = 1200;
- if (Board & PRIMUS)
- Outb(scc->ctrl + 4, Option | (tx? 0x80 : 0));
+ if (scc->brand & PRIMUS)
+ Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0));
- time_const = (unsigned) (Clock / (scc->modem.speed * (tx? 2:64))) - 2;
+ time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2;
if (scc->modem.clocksrc == CLK_DPLL)
{ /* simplex operation */
@@ -1155,10 +1415,9 @@
scc->t_txdel = TIMER_STOPPED;
scc->t_maxk = TPS * scc->kiss.maxkeyup;
- prepare_next_txframe(scc);
- if (scc->stat.tx_state != TXS_BUSY)
- scc_txint(scc);
+ if (scc->tx_bp == NULLBUF)
+ scc_txint(scc);
}
@@ -1177,7 +1436,7 @@
if (scc->kiss.fulldup < 2)
{
- if (scc->sndq) /* we had a timeout? */
+ if (scc->tx_bp) /* we had a timeout? */
{
scc->stat.tx_state = TXS_BUSY;
scc->t_dwait = TPS * scc->kiss.mintime; /* try again */
@@ -1189,7 +1448,7 @@
return;
}
- if (scc->sndq) /* maxkeyup expired */ /* ?! */
+ if (scc->tx_bp) /* maxkeyup expired */ /* ?! */
{
scc->stat.tx_state = TXS_BUSY;
scc->t_txdel = TPS * scc->kiss.waittime;
@@ -1205,11 +1464,10 @@
#ifdef THROW_AWAY_AFTER_BUSY_TIMEOUT
register struct mbuf *bp; /* not tested */
- bp = scc->sndq;
-
- while (bp) bp = scc_free_chain(bp, BT_TRANSMIT);
-
- scc->sndq = NULLBUF;
+ while (bp = scc_dequeue_buffer(&scc->tx_queue))
+ scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+
+ scc->tx_queue = NULLBUF;
scc->stat.tx_state = TXS_IDLE;
#else
@@ -1229,81 +1487,66 @@
scc->t_tail = scc->kiss.tailtime;
}
-static inline void check_rcv_queue(register struct scc_channel *scc)
+static void
+scc_tx_timer(unsigned long channel)
{
- 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 */
+ register struct scc_channel *scc;
+ unsigned long flags;
- while (bp && (scc->stat.rx_queued > QUEUE_HYST))
- {
- bp = scc_free_chain(bp, BT_RECEIVE);
- scc->stat.rx_queued--;
- scc->stat.nospace++;
- }
+
+ scc = (struct scc_channel *) channel;
+
+ if (scc->tty && scc->init)
+ {
+ save_flags(flags); cli();
+
+ /* KISS-TNC emulation */
- scc->rcvq->anext = bp;
+ 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
+ if (Expired(t_tail)) tail_timeout(scc) ;
+
+ /* watchdogs */
+
+ 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);
}
+
+ scc->tx_t.expires = jiffies + HZ/TPS;
+ add_timer(&scc->tx_t);
}
+
static void
-scc_timer(void)
+scc_rx_timer(unsigned long channel)
{
register struct scc_channel *scc;
- register int chan;
- unsigned long flags;
-
-
- for (chan = 0; chan < (Nchips * 2); chan++)
+
+ scc = (struct scc_channel *) channel;
+
+ if (scc->rx_queue && scc->throttled)
{
- 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
- if (Expired(t_tail)) tail_timeout(scc) ;
-
- /* watchdogs */
-
- 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);
- }
+ scc->rx_t.expires = jiffies + HZ/TPS;
+ add_timer(&scc->rx_t);
+ return;
}
- save_flags(flags); cli();
-
- timer_table[SCC_TIMER].fn = scc_timer;
- timer_table[SCC_TIMER].expires = jiffies + HZ/TPS;
- timer_active |= 1 << SCC_TIMER;
+ kiss_encode(scc);
- restore_flags(flags);
+ if (scc->rx_queue && !scc->throttled)
+ {
+
+ printk("z8530drv: warning: %s should be throttled\n",
+ kdevname(scc->tty->device));
+
+ scc->rx_t.expires = jiffies + HZ/TPS;
+ add_timer(&scc->rx_t);
+ }
}
-
static void
scc_init_timer(struct scc_channel *scc)
@@ -1319,26 +1562,19 @@
Stop_Timer(t_mbusy);
Stop_Timer(t_maxk);
Stop_Timer(t_idle);
- scc->stat.tx_state = TXS_IDLE;
-
- restore_flags(flags);
-}
-
-
-static void
-scc_rx_timer(void)
-{
- unsigned long flags;
- kiss_encode(rx_timer_cb.scc);
+ scc->stat.tx_state = TXS_IDLE;
- save_flags(flags); cli();
+ if (scc->tx_t.next)
+ del_timer(&scc->tx_t);
- timer_table[SCC_TIMER].fn = scc_timer;
- timer_table[SCC_TIMER].expires = jiffies + rx_timer_cb.expires;
- timer_active |= 1 << SCC_TIMER;
+ scc->tx_t.data = (unsigned long) scc;
+ scc->tx_t.function = scc_tx_timer;
+ scc->tx_t.expires = jiffies + HZ/TPS;
+ add_timer(&scc->tx_t);
- rx_timer_cb.lock = 0;
+ scc->rx_t.data = (unsigned long) scc;
+ scc->rx_t.function = scc_rx_timer;
restore_flags(flags);
}
@@ -1401,15 +1637,23 @@
{
unsigned char kisscmd;
unsigned long flags;
+ struct mbuf *bp;
- if (scc->sndq1->cnt < 2)
+ bp = scc->kiss_decode_bp;
+ bp->rw_ptr = bp->data;
+
+#ifdef DEBUG_BUFFERS
+ if (bp == NULLBUF)
{
- if (scc->sndq1)
- scc_free_chain(scc->sndq1, BT_TRANSMIT);
- else
- scc->sndq1 = NULLBUF;
-
- scc->sndq2 = NULLBUF;
+ printk("kiss_interpret_frame(): weird --- nothing to do.\n");
+ return;
+ }
+#endif
+
+ if (bp->cnt < 2)
+ {
+ scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+ scc->kiss_decode_bp = NULLBUF;
return;
}
@@ -1417,20 +1661,20 @@
if (scc->kiss.not_slip)
{
- kisscmd = scc->sndq1->data[scc->sndq1->in_use++];
- scc->sndq1->cnt--;
+ kisscmd = *bp->rw_ptr;
+ bp->rw_ptr++;
} else {
kisscmd = 0;
}
if (kisscmd & 0xa0)
{
- if (scc->sndq1->cnt > 2)
- scc->sndq1->cnt -= 2;
+ if (bp->cnt > 3)
+ bp->cnt -= 2;
else
{
- scc_free_chain(scc->sndq1, BT_TRANSMIT);
- scc->sndq2 = NULLBUF;
+ scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+ scc->kiss_decode_bp = NULLBUF;
return;
}
}
@@ -1441,25 +1685,22 @@
if (kisscmd)
{
- kiss_set_param(scc, kisscmd, scc->sndq1->data[scc->sndq1->in_use]);
- scc->sndq1->cnt=0;
- scc->sndq1->in_use=0;
-
- scc_free_chain(scc->sndq1, BT_TRANSMIT);
- scc->sndq2 = NULLBUF;
+ kiss_set_param(scc, kisscmd, *bp->rw_ptr);
+ scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+ scc->kiss_decode_bp = NULLBUF;
return;
}
+
+ scc_enqueue_buffer(&scc->tx_queue, bp); /* enqueue frame */
- scc_enqueue(&scc->sndq,scc->sndq1); /* scc_enqueue packet */
scc->stat.txframes++;
scc->stat.tx_queued++;
- scc->sndq2 = NULLBUF; /* acquire a new buffer next time */
+ scc->kiss_decode_bp = NULLBUF;
save_flags(flags); cli();
if(scc->stat.tx_state == TXS_IDLE)
{ /* when transmitter is idle */
- scc_init_timer(scc);
scc->stat.tx_state = TXS_BUSY;
scc->t_dwait = (scc->kiss.fulldup? 0:scc->kiss.waittime);
}
@@ -1467,21 +1708,21 @@
restore_flags(flags);
}
-static void kiss_store_byte(struct scc_channel *scc, unsigned char ch)
+static inline void kiss_store_byte(struct scc_channel *scc, unsigned char ch)
{
- if (scc->sndq2 == NULLBUF) return;
+ register struct mbuf *bp = scc->kiss_decode_bp;
- if(scc->sndq2->cnt == scc->sndq2->size) /* buffer full? */
+ if (bp != NULLBUF)
{
- if((scc->sndq2 = scc_get_buffer(BT_TRANSMIT)) == NULLBUF)
+ if (bp->cnt > scc->stat.bufsize)
+ printk("kiss_decode(): frame too long\n");
+ else
{
- printk("\nsccdrv: running out of memory\n");
- return;
+ *bp->rw_ptr = ch;
+ bp->rw_ptr++;
+ bp->cnt++;
}
- scc_append_to_chain(&scc->sndq1,scc->sndq2); /* add buffer */
}
-
- scc->sndq2->data[scc->sndq2->cnt++] = ch;
}
static inline int kiss_decode(struct scc_channel *scc, unsigned char ch)
@@ -1491,9 +1732,10 @@
case KISS_IDLE:
if (ch == FEND)
{
- if (!(scc->sndq2 = scc->sndq1 = scc_get_buffer(BT_TRANSMIT)))
- return 0;
-
+ scc->kiss_decode_bp = scc_get_buffer(scc, BT_TRANSMIT);
+ if (scc->kiss_decode_bp == NULLBUF)
+ return 1;
+
scc->stat.tx_kiss_state = KISS_DATA;
} else scc->stat.txerrs++;
break;
@@ -1503,7 +1745,7 @@
scc->stat.tx_kiss_state = KISS_ESCAPE;
else if (ch == FEND)
{
- kiss_interpret_frame(scc);
+ kiss_interpret_frame(scc);
scc->stat.tx_kiss_state = KISS_IDLE;
}
else kiss_store_byte(scc, ch);
@@ -1522,8 +1764,8 @@
}
else
{
- scc_free_chain(scc->sndq1, BT_TRANSMIT);
- scc->sndq2 = NULLBUF;
+ scc_enqueue_buffer(&scc->tx_buffer_pool, scc->kiss_decode_bp);
+ scc->kiss_decode_bp = NULLBUF;
scc->stat.txerrs++;
scc->stat.tx_kiss_state = KISS_IDLE;
}
@@ -1536,91 +1778,78 @@
/* ----> 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 mbuf *bp;
struct tty_struct * tty = scc->tty;
- unsigned long flags;
unsigned char ch;
- if(!scc->rcvq)
- {
- scc->stat.rx_kiss_state = KISS_IDLE;
- return;
- }
+ bp = scc->kiss_encode_bp;
/* worst case: FEND 0 FESC TFEND -> 4 bytes */
- while(tty->flip.count < TTY_FLIPBUF_SIZE-3)
- {
- if (scc->rcvq->cnt)
+ while(tty->flip.count < TTY_FLIPBUF_SIZE-4)
+ {
+ if (bp == NULLBUF)
{
- bp = scc->rcvq;
+ bp = scc_dequeue_buffer(&scc->rx_queue);
+ scc->kiss_encode_bp = bp;
- if (scc->stat.rx_kiss_state == KISS_IDLE)
+ if (bp == NULLBUF)
{
- 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);
+ scc->stat.rx_kiss_state = KISS_IDLE;
+ break;
}
-
- 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;
+ }
+
+
+ if (bp->cnt <= 0)
+ {
+ if (--scc->stat.rx_queued < 0)
+ scc->stat.rx_queued = 0;
- } else scc->rcvq = bp; /* next buffer */
+ 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);
- }
+ scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
+
+ bp = scc->kiss_encode_bp = NULLBUF;
+ continue;
+ }
+
+ 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->rw_ptr)
+ {
+ 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->rw_ptr++;
+ bp->cnt--;
}
-
+
queue_task(&tty->flip.tqueue, &tq_timer); /* kick it... */
}
@@ -1634,27 +1863,42 @@
z8530_init(void)
{
struct scc_channel *scc;
- int chip;
+ int chip, k;
unsigned long flags;
+ char *flag;
+
+
+ printk("Init Z8530 driver: %u channels, IRQ", Nchips*2);
+
+ flag=" ";
+ for (k = 0; k < 16; k++)
+ if (Ivec[k].used)
+ {
+ printk("%s%d", flag, k);
+ flag=",";
+ }
+ printk("\n");
+
/* reset and pre-init all chips in the system */
for (chip = 0; chip < Nchips; chip++)
{
+ scc=&SCC_Info[2*chip];
+ if (!scc->ctrl) continue;
+
+ save_flags(flags); cli(); /* because of 2-step accesses */
+
/* Special SCC cards */
- if(Board & EAGLE) /* this is an EAGLE card */
- Outb(Special_Port,0x08); /* enable interrupt on the board */
+ if(scc->brand & EAGLE) /* this is an EAGLE card */
+ Outb(scc->special,0x08); /* enable interrupt on the board */
- if(Board & (PC100 | PRIMUS)) /* this is a PC100/EAGLE card */
- Outb(Special_Port,Option); /* set the MODEM mode (0x22) */
+ if(scc->brand & (PC100 | PRIMUS)) /* this is a PC100/EAGLE card */
+ Outb(scc->special,scc->option); /* set the MODEM mode (0x22) */
+
/* Init SCC */
-
- scc=&SCC_Info[2*chip];
- if (!scc->ctrl) continue;
-
- save_flags(flags); cli();
-
+
/* some general init we can do now */
Outb(scc->ctrl, 0);
@@ -1666,8 +1910,6 @@
restore_flags(flags);
}
- if (Ivec == 2) Ivec = 9; /* this f... IBM AT-design! */
- request_irq(Ivec, scc_isr, SA_INTERRUPT, "AX.25 SCC");
Driver_Initialized = 1;
}
@@ -1685,20 +1927,23 @@
const char *routine)
{
#ifdef SCC_PARANOIA_CHECK
- static const char *badmagic =
- "Warning: bad magic number for Z8530 SCC struct (%s) in %s\n";
- static const char *badinfo =
- "Warning: Z8530 not found for (%s) in %s\n";
+
+static const char *badmagic =
+ "Warning: bad magic number for Z8530 SCC struct (%s) in %s\n";
+static const char *badinfo =
+ "Warning: Z8530 not found for (%s) in %s\n";
if (!scc->init)
{
- printk(badinfo, kdevname(device), routine);
- return 1;
- }
- if (scc->magic != SCC_MAGIC) {
- printk(badmagic, kdevname(device), routine);
- return 1;
- }
+ printk(badinfo, kdevname(device), routine);
+ return 1;
+ }
+
+ if (scc->magic != SCC_MAGIC)
+ {
+ printk(badmagic, kdevname(device), routine);
+ return 1;
+ }
#endif
return 0;
@@ -1712,46 +1957,49 @@
struct scc_channel *scc;
int chan;
- chan = MINOR(tty->device) - tty->driver.minor_start;
- if ((chan < 0) || (chan >= (Nchips * 2)))
- return -ENODEV;
+ chan = MINOR(tty->device) - tty->driver.minor_start;
+
+ if (Driver_Initialized)
+ {
+ if ( (chan < 0) || (chan >= (Nchips * 2)) )
+ return -ENODEV;
+ } else {
+ tty->driver_data = &SCC_Info[0];
+ MOD_INC_USE_COUNT;
+ return 0;
+ }
scc = &SCC_Info[chan];
tty->driver_data = scc;
- tty->termios->c_cflag &= ~CBAUD;
+ tty->termios->c_cflag &= ~CBAUD;
- if (!Driver_Initialized)
- return 0;
-
if (scc->magic != SCC_MAGIC)
{
- printk("ERROR: scc_open(): bad magic number for device ("
- "%s)",
+ printk("ERROR: scc_open(): bad magic number for device (%s)",
kdevname(tty->device));
return -ENODEV;
}
+ MOD_INC_USE_COUNT;
+
if(scc->tty != NULL)
{
scc->tty_opened++;
return 0;
}
- if(!scc->init) return 0;
-
scc->tty = tty;
- init_channel(scc);
+ alloc_buffer_pool(scc);
+
+ if(!scc->init) return 0;
+
+ scc->throttled = 0;
scc->stat.tx_kiss_state = KISS_IDLE; /* don't change this... */
scc->stat.rx_kiss_state = KISS_IDLE; /* ...or this */
-
- scc_init_timer(scc);
-
- timer_table[SCC_TIMER].fn = scc_timer;
- timer_table[SCC_TIMER].expires = 0; /* now! */
- timer_active |= 1 << SCC_TIMER;
-
+
+ init_channel(scc);
return 0;
}
@@ -1766,6 +2014,8 @@
if (!scc || (scc->magic != SCC_MAGIC))
return;
+
+ MOD_DEC_USE_COUNT;
if(scc->tty_opened)
{
@@ -1775,6 +2025,9 @@
tty->driver_data = NULLBUF;
+ if (!Driver_Initialized)
+ return;
+
save_flags(flags); cli();
Outb(scc->ctrl,0); /* Make sure pointer is written */
@@ -1783,13 +2036,18 @@
scc->tty = NULL;
+ del_timer(&scc->tx_t);
+ del_timer(&scc->rx_t);
+
+ free_buffer_pool(scc);
+
restore_flags(flags);
- tty->stopped = 0;
+
+ scc->throttled = 0;
+ tty->stopped = 0;
}
-
-
/*
* change scc_speed
*/
@@ -1797,10 +2055,15 @@
static void
scc_change_speed(struct scc_channel * scc)
{
+ long speed;
+
if (scc->tty == NULL)
return;
- scc->modem.speed = baud_table[scc->tty->termios->c_cflag & CBAUD];
+
+ speed = baud_table[scc->tty->termios->c_cflag & CBAUD];
+
+ if (speed > 0) scc->modem.speed = speed;
if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */
set_speed(scc);
@@ -1833,11 +2096,13 @@
unsigned int result;
unsigned int value;
struct ioctl_command kiss_cmd;
- int error;
+ struct scc_mem_config memcfg;
+ struct scc_hw_config hwcfg;
+ int error, chan;
if (scc->magic != SCC_MAGIC)
{
- printk("ERROR: scc_ioctl(): bad magic number for device %s",
+ printk("ERROR: scc_ioctl(): bad magic number for device %s",
kdevname(tty->device));
return -ENODEV;
@@ -1847,13 +2112,106 @@
if (!Driver_Initialized)
{
+ if (cmd == TIOCSCCCFG)
+ {
+ int found = 1;
+
+ if (!suser()) return -EPERM;
+ if (!arg) return -EFAULT;
+
+ if (Nchips >= MAXSCC)
+ return -EINVAL;
+
+ memcpy_fromfs(&hwcfg, (void *) arg, sizeof(hwcfg));
+
+ if (hwcfg.irq == 2) hwcfg.irq = 9;
+
+ if (!Ivec[hwcfg.irq].used && hwcfg.irq)
+ {
+ if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC"))
+ printk("z8530drv: Warning --- could not get IRQ %d\n", hwcfg.irq);
+ else
+ Ivec[hwcfg.irq].used = 1;
+ }
+
+ if (hwcfg.vector_latch)
+ Vector_Latch = hwcfg.vector_latch;
+
+ if (hwcfg.clock == 0)
+ hwcfg.clock = DEFAULT_CLOCK;
+
+#ifndef DONT_CHECK
+ save_flags(flags); cli();
+
+ check_region(scc->ctrl, 1);
+ Outb(hwcfg.ctrl_a, 0);
+ udelay(5);
+ OutReg(hwcfg.ctrl_a,R13,0x55); /* is this chip really there? */
+ udelay(5);
+
+ if (InReg(hwcfg.ctrl_a,R13) != 0x55 )
+ found = 0;
+
+ restore_flags(flags);
+#endif
+
+ if (found)
+ {
+ SCC_Info[2*Nchips ].ctrl = hwcfg.ctrl_a;
+ SCC_Info[2*Nchips ].data = hwcfg.data_a;
+ SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b;
+ SCC_Info[2*Nchips+1].data = hwcfg.data_b;
+
+ SCC_ctrl[2*Nchips ] = hwcfg.ctrl_a;
+ SCC_ctrl[2*Nchips+1] = hwcfg.ctrl_b;
+ }
+
+ for (chan = 0; chan < 2; chan++)
+ {
+ SCC_Info[2*Nchips+chan].special = hwcfg.special;
+ SCC_Info[2*Nchips+chan].clock = hwcfg.clock;
+ SCC_Info[2*Nchips+chan].brand = hwcfg.brand;
+ SCC_Info[2*Nchips+chan].option = hwcfg.option;
+ SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc;
+
+#ifdef DONT_CHECK
+ printk("%s%i: data port = 0x%3.3x control port = 0x%3.3x\n",
+ scc_driver.name, 2*Nchips+chan,
+ SCC_Info[2*Nchips+chan].data,
+ SCC_Info[2*Nchips+chan].ctrl);
+
+#else
+ printk("%s%i: data port = 0x%3.3x control port = 0x%3.3x -- %s\n",
+ scc_driver.name, 2*Nchips+chan,
+ chan? hwcfg.data_b : hwcfg.data_a,
+ chan? hwcfg.ctrl_b : hwcfg.ctrl_a,
+ found? "found" : "missing");
+#endif
+
+ if (found)
+ {
+ request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl");
+ request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data");
+ }
+ }
+
+ if (found) Nchips++;
+
+ return 0;
+ }
+
if (cmd == TIOCSCCINI)
{
if (!suser())
return -EPERM;
+
+ if (Nchips == 0)
+ return -EINVAL;
- scc_alloc_buffer_pool();
z8530_init();
+
+ scc->tty=tty;
+ alloc_buffer_pool(scc);
return 0;
}
@@ -1862,7 +2220,6 @@
if (!scc->init)
{
-
if (cmd == TIOCCHANINI)
{
if (!arg)
@@ -1979,8 +2336,6 @@
case TCSETS:
case TCSETSF: /* should flush first, but... */
case TCSETSW: /* should wait 'till flush, but... */
- if (!suser())
- return -EPERM;
if (!arg)
return -EFAULT;
@@ -1988,6 +2343,23 @@
scc_change_speed(scc);
return 0;
+ case TIOCCHANMEM:
+ if (!arg)
+ return -EFAULT;
+
+ memcpy_fromfs(&memcfg, (void *) arg, sizeof(struct scc_mem_config));
+
+ save_flags(flags); cli();
+
+ free_buffer_pool(scc);
+ scc->stat.rxbuffers = memcfg.rxbuffers;
+ scc->stat.txbuffers = memcfg.txbuffers;
+ scc->stat.bufsize = memcfg.bufsize;
+ alloc_buffer_pool(scc);
+
+ restore_flags(flags);
+ return 0;
+
case TIOCSCCSTAT:
error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(struct scc_stat));
@@ -1997,9 +2369,6 @@
if (!arg)
return -EFAULT;
- scc->stat.used_buf = scc_count_used_buffers(&scc->stat.rx_alloc,
- &scc->stat.tx_alloc);
-
memcpy_tofs((void *) arg, &scc->stat, sizeof(struct scc_stat));
return 0;
@@ -2051,9 +2420,6 @@
if (!arg)
return -EFAULT;
- if (!suser())
- return -EPERM;
-
memcpy_fromfs(&kiss_cmd, (void *) arg, sizeof(struct ioctl_command));
switch (kiss_cmd.command)
@@ -2092,55 +2458,14 @@
}
-/* ----- TERMIOS function ----- */
-
-static void
-scc_set_termios(struct tty_struct * tty, struct termios * old_termios)
-{
- if (tty->termios->c_cflag == old_termios->c_cflag)
- return;
- scc_change_speed(tty->driver_data);
-}
-
-
-static inline void check_tx_queue(register struct scc_channel *scc)
-{
- register struct mbuf *bp;
-
- if (scc->stat.tx_queued > QUEUE_THRES)
- {
- if (scc->sndq1 == NULLBUF)
- {
- printk("z8530drv: Warning - scc->stat.tx_queued shows overflow"
- " (%d) but queue is empty\n", scc->stat.tx_queued);
-
- scc->stat.tx_queued = 0; /* correct it */
- scc->stat.nospace = 54321; /* draw attention to it */
- return;
- }
-
- bp = scc->sndq1->anext; /* don't use the one we currently use */
-
- while (bp && (scc->stat.tx_queued > QUEUE_HYST))
- {
- bp = scc_free_chain(bp, BT_TRANSMIT);
- scc->stat.tx_queued--;
- scc->stat.nospace++;
- }
-
- scc->sndq1->anext = bp;
- }
-}
-
-
-
/* ----> tx routine: decode KISS data and scc_enqueue it <---- */
/* 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)
{
struct scc_channel * scc = tty->driver_data;
- unsigned char tbuf[BUFSIZE], *p;
+ unsigned char *p;
+ unsigned long flags;
int cnt, cnt2;
if (!tty) return count;
@@ -2150,8 +2475,8 @@
if (scc->kiss.tx_inhibit) return count;
- check_tx_queue(scc);
-
+ save_flags(flags); cli();
+
cnt2 = count;
while (cnt2)
@@ -2160,23 +2485,40 @@
cnt2 -= cnt;
if (from_user)
- memcpy_fromfs(tbuf, buf, cnt);
+ {
+ down(&scc_sem);
+ memcpy_fromfs(scc_wbuf, buf, cnt);
+ up(&scc_sem);
+ }
else
- memcpy(tbuf, buf, cnt);
+ memcpy(scc_wbuf, buf, cnt);
- buf += cnt;
+ /* Strange thing. The timeout of the slip driver is */
+ /* very small, thus we'll wake him up now. */
+
+ if (cnt2 == 0)
+ {
+ wake_up_interruptible(&tty->write_wait);
+
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ } else
+ buf += cnt;
- p=tbuf;
+ p=scc_wbuf;
while(cnt--)
if (kiss_decode(scc, *p++))
{
scc->stat.nospace++;
+ restore_flags(flags);
return 0;
- }
-
+ }
} /* while cnt2 */
-
+
+ restore_flags(flags);
+
return count;
}
@@ -2204,7 +2546,6 @@
return; /* no flush needed */
}
-/* the kernel does NOT use this routine yet... */
static int scc_write_room(struct tty_struct *tty)
{
@@ -2213,12 +2554,6 @@
if (scc_paranoia_check(scc, tty->device, "scc_write_room"))
return 0;
- if (scc->stat.tx_alloc >= QUEUE_THRES)
- {
- printk("scc_write_room(): buffer full (ignore)\n");
- return 0;
- }
-
return BUFSIZE;
}
@@ -2226,8 +2561,8 @@
{
struct scc_channel *scc = tty->driver_data;
- if (scc && scc->sndq2)
- return scc->sndq2->cnt;
+ if (scc && scc->kiss_decode_bp)
+ return scc->kiss_decode_bp->cnt;
else
return 0;
}
@@ -2235,12 +2570,9 @@
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)
@@ -2253,9 +2585,15 @@
if (scc_paranoia_check(scc, tty->device, "scc_throttle"))
return;
-
-
- /* dummy */
+
+#ifdef DEBUG
+ printk("scc: scc_throttle() called for device %d\n", MINOR(tty->device));
+#endif
+ scc->throttled = 1;
+
+ del_timer(&(scc->rx_t));
+ scc->rx_t.expires = jiffies + HZ/TPS;
+ add_timer(&scc->rx_t);
}
static void scc_unthrottle(struct tty_struct *tty)
@@ -2265,9 +2603,16 @@
if (scc_paranoia_check(scc, tty->device, "scc_unthrottle"))
return;
- /* dummy */
+#ifdef DEBUG
+ printk("scc: scc_unthrottle() called for device %d\n", MINOR(tty->device));
+#endif
+ scc->throttled = 0;
+ del_timer(&(scc->rx_t));
+ scc_tx_timer(scc->rx_t.data);
}
+/* experimental, the easiest way to stop output is a fake scc_throttle */
+
static void scc_start(struct tty_struct *tty)
{
struct scc_channel *scc = tty->driver_data;
@@ -2275,9 +2620,8 @@
if (scc_paranoia_check(scc, tty->device, "scc_start"))
return;
- /* dummy */
+ scc_unthrottle(tty);
}
-
static void scc_stop(struct tty_struct *tty)
{
@@ -2286,7 +2630,32 @@
if (scc_paranoia_check(scc, tty->device, "scc_stop"))
return;
- /* dummy */
+ scc_throttle(tty);
+}
+
+static void
+scc_set_termios(struct tty_struct * tty, struct termios * old_termios)
+{
+ struct scc_channel *scc = tty->driver_data;
+
+ if (scc_paranoia_check(scc, tty->device, "scc_set_termios"))
+ return;
+
+ if (old_termios && (tty->termios->c_cflag == old_termios->c_cflag))
+ return;
+
+ scc_change_speed(scc);
+}
+
+static void
+scc_set_ldisc(struct tty_struct * tty)
+{
+ struct scc_channel *scc = tty->driver_data;
+
+ if (scc_paranoia_check(scc, tty->device, "scc_set_ldisc"))
+ return;
+
+ scc_change_speed(scc);
}
@@ -2294,25 +2663,24 @@
/* * Init SCC driver * */
/* ******************************************************************** */
-int scc_init (void)
+int scc_init (void)
{
- int chip, chan;
- register io_port ctrl;
- long flags;
-
+ int chip, chan, k;
+ memset(&scc_std_termios, 0, sizeof(struct termios));
memset(&scc_driver, 0, sizeof(struct tty_driver));
scc_driver.magic = TTY_DRIVER_MAGIC;
- scc_driver.name = "sc";
- scc_driver.major = Z8530_MAJOR;
+ scc_driver.name = "scc";
+ scc_driver.major = Z8530_MAJOR;
scc_driver.minor_start = 0;
- scc_driver.num = Nchips*2;
+ scc_driver.num = MAXSCC*2;
scc_driver.type = TTY_DRIVER_TYPE_SERIAL;
- scc_driver.subtype = 0; /* not needed */
- scc_driver.init_termios = tty_std_termios;
- scc_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ scc_driver.subtype = 1; /* not needed */
+ scc_driver.init_termios = scc_std_termios;
+ scc_driver.init_termios.c_cflag = B9600 | CREAD | CS8 | HUPCL | CLOCAL;
+ scc_driver.init_termios.c_iflag = IGNBRK | IGNPAR;
scc_driver.flags = TTY_DRIVER_REAL_RAW;
- scc_driver.refcount = &scc_refcount; /* not needed yet */
+ scc_driver.refcount = &scc_refcount;
scc_driver.table = scc_table;
scc_driver.termios = (struct termios **) scc_termios;
scc_driver.termios_locked = (struct termios **) scc_termios_locked;
@@ -2333,86 +2701,91 @@
scc_driver.ioctl = scc_ioctl;
scc_driver.set_termios = scc_set_termios;
+ scc_driver.set_ldisc = scc_set_ldisc;
+
+ printk(BANNER);
if (tty_register_driver(&scc_driver))
- panic("Couldn't register Z8530 SCC driver\n");
-
- printk (BANNER);
+ {
+ printk("Failed to register Z8530 SCC driver\n");
+ return -EIO;
+ }
- if (Nchips > MAXSCC) Nchips = MAXSCC; /* to avoid the "DAU" (duemmster anzunehmender User) */
-
- /* reset and pre-init all chips in the system */
+ /* pre-init channel information */
- for (chip = 0; chip < Nchips; chip++)
+ for (chip = 0; chip < MAXSCC; chip++)
{
memset((char *) &SCC_Info[2*chip ], 0, sizeof(struct scc_channel));
memset((char *) &SCC_Info[2*chip+1], 0, sizeof(struct scc_channel));
- ctrl = SCC_ctrl[chip * 2];
- if (!ctrl) continue;
+ for (chan = 0; chan < 2; chan++)
+ {
+ SCC_Info[2*chip+chan].magic = SCC_MAGIC;
+ SCC_Info[2*chip+chan].stat.rxbuffers = RXBUFFERS;
+ SCC_Info[2*chip+chan].stat.txbuffers = TXBUFFERS;
+ SCC_Info[2*chip+chan].stat.bufsize = BUFSIZE;
+ }
+ }
+
+ for (k = 0; k < 16; k++) Ivec[k].used = 0;
- save_flags(flags); cli(); /* because of 2-step accesses */
-
+ return 0;
+}
-/* Hmm... this may fail on fast systems with cards who don't delay the INTACK */
-/* If you are sure you specified the right port addresses and the driver doesn't */
-/* recognize the chips, define DONT_CHECK in scc_config.h */
+/* ******************************************************************** */
+/* * Module support * */
+/* ******************************************************************** */
-#ifndef DONT_CHECK
- check_region(ctrl, 1);
- Outb(ctrl, 0);
- OutReg(ctrl,R13,0x55); /* is this chip realy there? */
+#ifdef MODULE
+int init_module(void)
+{
+ int result = 0;
+
+ result = scc_init();
+
+ if (result == 0)
+ printk("Copyright 1993,1995 Joerg Reuter DL1BKE (jreuter@lykos.tng.oche.de)\n");
- if (InReg(ctrl,R13) != 0x55 )
+ return result;
+}
+
+void cleanup_module(void)
+{
+ long flags;
+ io_port ctrl;
+ int k, errno;
+ struct scc_channel *scc;
+
+ save_flags(flags); cli();
+ if ( (errno = tty_unregister_driver(&scc_driver)) )
+ {
+ printk("Failed to unregister Z8530 SCC driver (%d)", -errno);
+ restore_flags(flags);
+ return;
+ }
+
+ for (k = 0; k < Nchips; k++)
+ if ( (ctrl = SCC_ctrl[k*2]) )
{
- restore_flags(flags);
- continue;
+ Outb(ctrl, 0);
+ OutReg(ctrl,R9,FHWRES); /* force hardware reset */
+ udelay(50);
}
-#endif
- SCC_Info[2*chip ].magic = SCC_MAGIC;
- SCC_Info[2*chip ].ctrl = SCC_ctrl[2*chip];
- SCC_Info[2*chip ].data = SCC_data[2*chip];
- SCC_Info[2*chip ].enhanced = SCC_Enhanced[chip];
-
- SCC_Info[2*chip+1].magic = SCC_MAGIC;
- SCC_Info[2*chip+1].ctrl = SCC_ctrl[2*chip+1];
- SCC_Info[2*chip+1].data = SCC_data[2*chip+1];
- SCC_Info[2*chip+1].enhanced = SCC_Enhanced[chip];
-
-
- 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);
-
-
- for (chan = 0; chan < Nchips * 2 ; chan++)
+ for (k = 0; k < Nchips*2; k++)
{
- printk("/dev/%s%i: data port = 0x%3.3x control port = 0x%3.3x -- %s\n",
- scc_driver.name, chan, SCC_data[chan], SCC_ctrl[chan],
- SCC_Info[chan].ctrl? "found" : "missing");
-
- if (SCC_Info[chan].ctrl == 0)
+ scc = &SCC_Info[k];
+ if (scc)
{
- SCC_ctrl[chan] = 0;
- } else {
- request_region(SCC_ctrl[chan], 1, "scc ctrl");
- request_region(SCC_data[chan], 1, "scc data");
+ release_region(scc->ctrl, 1);
+ release_region(scc->data, 1);
}
}
-#else
- printk("Init Z8530 driver: %u channels\n",Nchips*2);
-#endif
-
- return 0;
+ for (k=0; k < 16 ; k++)
+ if (Ivec[k].used) free_irq(k);
+
+ restore_flags(flags);
}
+#endif
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