patch-2.0.31 linux/drivers/char/scc.c
Next file: linux/drivers/char/serial.c
Previous file: linux/drivers/char/random.c
Back to the patch index
Back to the overall index
- Lines: 3981
- Date:
Tue Aug 12 14:43:42 1997
- Orig file:
v2.0.30/linux/drivers/char/scc.c
- Orig date:
Thu Apr 11 23:49:35 1996
diff -u --recursive --new-file v2.0.30/linux/drivers/char/scc.c linux/drivers/char/scc.c
@@ -1,6 +1,6 @@
-#define RCS_ID "$Id: scc.c,v 1.41 1995/12/17 22:36:40 jreuter Exp jreuter $"
+#define RCS_ID "$Id: scc.c,v 1.67 1997/04/10 15:24:41 jreuter Exp jreuter $"
-#define BANNER "Z8530 SCC driver version 2.01.dl1bke (alpha) by DL1BKE\n"
+#define BANNER "Z8530 SCC driver version 2.4c.dl1bke (experimental) by DL1BKE\n"
/*
@@ -11,7 +11,7 @@
********************************************************************
- Copyright (c) 1993, 1995 Joerg Reuter DL1BKE
+ Copyright (c) 1993, 1997 Joerg Reuter DL1BKE
portions (c) 1993 Guido ten Dolle PE1NNZ
@@ -48,16 +48,6 @@
********************************************************************
-
- ...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
-
Incomplete history of z8530drv:
-------------------------------
@@ -74,89 +64,105 @@
950131 - changed copyright notice to GPL without limitations.
.
- .
+ . <snip!>
.
- 950922 - using kernel timer chain
-
- 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
+ 951114 - rewrote memory management, took first steps to allow
+ compilation as a module. Well, it looks like a whole
+ new driver now.
+
+ 960413 - Heiko Eissfeld fixed missing verify_area() calls
+
+ 960507 - the driver may be used as a network driver now.
+ (for kernel AX.25).
+
+ 960512 - added scc_net_ioctl(), restructured ioctl routines
+ added /proc filesystem support
- 951013 - it still does not survive a flood ping...
+ 960517 - fixed fullduplex mode 2 operation and waittime bug
- 951107 - axattach w/o "-s" option (or setting an invalid
- baudrate) does not produce "division by zero" faults
- anymore.
+ 960518 - added fullduplex mode 3 to allow direct hardware
+ access via protocol layer. Removed kiss.not_slip,
+ I do not think someone really needed it.
- 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.
+ 960607 - switching off receiver while transmission in halfduplex
+ mode with external MODEM clock.
- 951117 - Well --- what should I say: You can compile it as a
- module now. And I solved the problem with slip.c...
+ 960715 - rewrote interrupt service routine for "polling" mode,
+ fixed bug in grouping algorithm.
- 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 ;-)
+ 960719 - New transmit timer routines (let's see what it breaks),
+ some improvements regarding DCD and SYNC interrupts,
+ clean-up of printk().
- 951217 - found silly bug in init_channel(), some bugfixes
- for the "standalone" module
+ 960725 - Fixed Maxkeyup problems. Will generate HDLC abort and
+ recover now.
+ 960808 - Maxkeyup will set dev->tbusy until all remaining frames
+ are sent.
+
+ 970115 - Fixed return values in scc_net_tx(), added missing
+ scc_enqueue_buffer()
+
+ 970410 - Fixed problem with new *_timer() code in sched.h for
+ kernel 2.0.30.
Thanks to:
----------
- 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 - for some useful changes
- DL8MBT Flori - for support
- DG0FT Rene - for the BayCom USCC support
- PA3AOU Harry - for ESCC testing, information supply and support
-
+ 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 - for some useful changes
+ DL8MBT Flori - for support
+ DG0FT Rene - for the BayCom USCC support
+ PA3AOU Harry - for ESCC testing, information supply and support
+ Heiko Eissfeld - verify_area() clean-up
+ DL3KHB Klaus - fullduplex mode 2 bug-hunter
+ PA3GCU Richard - for support and finding various bugs.
+
PE1KOX Rob, DG1RTF Thomas, ON5QK Roland, G4XYW Andy, Linus,
- EI9GL Paul,
-
+ GW4PTS Alan, EI9GL Paul, G0DZB Peter, G8IMB Martin
+
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
- "v2.01.dl1bke" with your own. Just to avoid confusion...
-
+ "v2.4c.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
-
+
+ New versions of the driver will be announced on the linux-hams
+ mailing list on vger.rutgers.edu. To subscribe send an e-mail
+ to majordomo@vger.rutgers.edu with the following line in
+ the body of the mail:
+
+ subscribe linux-hams
+
+ The content of the "Subject" field will be ignored.
+
+ vy 73,
+ Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org
+ AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU
+ Internet: jreuter@poboxes.com
*/
/* ----------------------------------------------------------------------- */
-#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 SCC_DELAY /* perhaps your ISA bus is a *bit* too fast? */
+#undef SCC_LDELAY 1 /* 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 */
+#undef DISABLE_ALL_INTS /* use cli()/sti() in ISR instead of */
+ /* enable_irq()/disable_irq() */
+#undef SCC_DEBUG
#define DEFAULT_CLOCK 4915200 /* default pclock if nothing is specified */
@@ -177,12 +183,26 @@
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/string.h>
+#include <linux/in.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/malloc.h>
#include <linux/delay.h>
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/socket.h>
+
+#ifdef CONFIG_SCC_STANDALONE
+#include "scc.h"
+#else
#include <linux/scc.h>
+#endif
+#include <net/ax25.h>
+#include <asm/irq.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
@@ -193,6 +213,7 @@
#include <ctype.h>
#include <time.h>
#include <linux/kernel.h>
+#include <linux/proc_fs.h>
#ifdef MODULE
int init_module(void);
@@ -203,6 +224,15 @@
#define Z8530_MAJOR 34
#endif
+#ifndef PROC_NET_Z8530
+#define PROC_NET_Z8530 PROC_NET_LAST+10 /* sorry... */
+#endif
+
+#if !defined(CONFIG_SCC_DEV) && !defined(CONFIG_SCC_TTY)
+#define CONFIG_SCC_DEV
+#define CONFIG_SCC_TTY
+#endif
+
int scc_init(void);
static struct mbuf * scc_enqueue_buffer(struct mbuf **queue, struct mbuf * buffer);
@@ -211,6 +241,7 @@
static void free_buffer_pool(struct scc_channel *scc);
static struct mbuf * scc_get_buffer(struct scc_channel *scc, char type);
+#ifdef CONFIG_SCC_TTY
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);
@@ -219,81 +250,112 @@
static int scc_write_room(struct tty_struct *tty);
static int scc_chars_in_buffer(struct tty_struct *tty);
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 int scc_tty_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);
static void scc_stop(struct tty_struct *tty);
+static void kiss_encode(struct scc_channel *scc);
+static void scc_rx_timer(unsigned long);
+static void scc_change_speed(struct scc_channel *scc);
+#endif
-static void z8530_init(void);
+static void t_dwait(unsigned long);
+static void t_txdelay(unsigned long);
+static void t_tail(unsigned long);
+static void t_busy(unsigned long);
+static void t_maxkeyup(unsigned long);
+static void t_idle(unsigned long);
+static void scc_tx_done(struct scc_channel *);
+static void scc_start_tx_timer(struct scc_channel *, void (*)(unsigned long), unsigned long);
+static void scc_start_maxkeyup(struct scc_channel *);
+static void scc_start_defer(struct scc_channel *);
-static void scc_change_speed(struct scc_channel *scc);
-static void kiss_encode(struct scc_channel *scc);
+static int scc_ioctl(struct scc_channel *scc, unsigned int cmd, void * arg);
+
+static void z8530_init(void);
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);
-static void scc_exint(register struct scc_channel *scc);
-static void scc_rxint(register struct scc_channel *scc);
-static void scc_spint(register struct scc_channel *scc);
static void scc_isr(int irq, void *dev_id, struct pt_regs *regs);
-static void scc_tx_timer(unsigned long);
-static void scc_rx_timer(unsigned long);
static void scc_init_timer(struct scc_channel *scc);
-/* from serial.c */
+#ifdef CONFIG_SCC_DEV
+static int scc_net_setup(struct scc_channel *scc, unsigned char *name);
+static void scc_net_rx(struct scc_channel *scc, struct mbuf *bp);
+#endif
+
+static unsigned char *SCC_DriverName = "scc";
+#ifdef CONFIG_SCC_TTY
static int baud_table[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600, 19200, 38400, 57600, 115200, 0 };
+struct semaphore scc_sem = MUTEX;
+static struct termios scc_std_termios;
+unsigned char scc_wbuf[BUFSIZE];
struct tty_driver scc_driver;
-static int scc_refcount;
static struct tty_struct *scc_table[2*MAXSCC];
static struct termios scc_termios[2 * MAXSCC];
static struct termios scc_termios_locked[2 * MAXSCC];
+static int scc_refcount;
+#endif
+
-struct irqflags { unsigned char used : 1; } Ivec[16];
+static 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 */
+static struct scc_channel SCC_Info[2 * MAXSCC]; /* information per channel */
-unsigned char Random = 0; /* random number for p-persist */
-unsigned char Driver_Initialized = 0;
-int Nchips = 0;
-io_port Vector_Latch = 0;
+static struct scc_ctrl {
+ io_port chan_A;
+ io_port chan_B;
+ int irq;
+} SCC_ctrl[MAXSCC+1];
-struct semaphore scc_sem = MUTEX;
-unsigned char scc_wbuf[BUFSIZE];
+static unsigned char Driver_Initialized = 0;
+static int Nchips = 0;
+static io_port Vector_Latch = 0;
-static struct termios scc_std_termios;
+#define SCC_DEVNAME (scc->stat.is_netdev? scc->dev->name:kdevname(scc->tty->device))
/* ******************************************************************** */
/* * Port Access Functions * */
/* ******************************************************************** */
-static inline unsigned char
-InReg(register io_port port, register unsigned char reg)
+/* These provide interrupt save 2-step access to the Z8530 registers */
+
+static __inline__ unsigned char
+InReg(io_port port, unsigned char reg)
{
+ unsigned long flags;
+ unsigned char r;
+
+ save_flags(flags);
+ cli();
#ifdef SCC_LDELAY
- register unsigned char r;
Outb(port, reg);
udelay(SCC_LDELAY);
r=Inb(port);
udelay(SCC_LDELAY);
- return r;
#else
Outb(port, reg);
- return Inb(port);
+ r=Inb(port);
#endif
+ restore_flags(flags);
+ return r;
}
-static inline void
-OutReg(register io_port port, register unsigned char reg, register unsigned char val)
+static __inline__ void
+OutReg(io_port port, unsigned char reg, unsigned char val)
{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
#ifdef SCC_LDELAY
Outb(port, reg); udelay(SCC_LDELAY);
Outb(port, val); udelay(SCC_LDELAY);
@@ -301,26 +363,40 @@
Outb(port, reg);
Outb(port, val);
#endif
+ restore_flags(flags);
}
-static inline void
-wr(register struct scc_channel *scc, register unsigned char reg, register unsigned char val)
+static __inline__ void
+wr(struct scc_channel *scc, unsigned char reg, unsigned char val)
{
OutReg(scc->ctrl, reg, (scc->wreg[reg] = val));
}
-static inline void
-or(register struct scc_channel *scc, register unsigned char reg, register unsigned char val)
+static __inline__ void
+or(struct scc_channel *scc, unsigned char reg, unsigned char val)
{
OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val));
}
-static inline void
-cl(register struct scc_channel *scc, register unsigned char reg, register unsigned char val)
+static __inline__ void
+cl(struct scc_channel *scc, unsigned char reg, unsigned char val)
{
OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val));
}
+#ifdef DISABLE_ALL_INTS
+static __inline__ void scc_cli(int irq)
+{ cli(); }
+static __inline__ void scc_sti(int irq)
+{ sti(); }
+#else
+static __inline__ void scc_cli(int irq)
+{ disable_irq(irq); }
+static __inline__ void scc_sti(int irq)
+{ enable_irq(irq); }
+#endif
+
+
/* ******************************************************************** */
/* * Memory Buffer Management */
/* ******************************************************************** */
@@ -331,6 +407,9 @@
* fast. It has the disadvantage to mess things up double if something
* is wrong.
*
+ * We could have used the socket buffer routines from skbuff.h instead,
+ * but our own routines keep the driver portable...
+ *
* 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-
@@ -354,23 +433,6 @@
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;
- }
-
- if (buffer == NULLBUF)
- {
- 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 */
@@ -380,22 +442,9 @@
} 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 */
+ } else { /* every other case */
buffer->prev = anchor->prev; /* self explaining, isn't it? */
buffer->next = anchor;
anchor->prev->next = buffer;
@@ -418,28 +467,12 @@
save_flags(flags); cli();
-#ifdef DEBUG_BUFFERS
- if (queue == NULLBUFP) /* called with illegal parameter */
-
- {
- printk("z8530drv: Pointer to queue anchor is NULL pointer [deq]\n");
- restore_flags(flags);
- return NULLBUF;
- }
-#endif
-
buffer = *queue; /* head of the chain */
if (buffer != NULLBUF) /* not an empty list? */
{
if (buffer->prev != NULLBUF) /* not last buffer? */
{
-#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;
@@ -449,10 +482,7 @@
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 */
buffer->next = NULLBUF; /* for security only... */
@@ -476,6 +506,12 @@
struct mbuf * bptr;
int buflen;
+ if (scc->mempool)
+ {
+ printk(KERN_DEBUG "z8530drv: alloc_buffer_pool() had buffers already allocated\n");
+ return;
+ }
+
buflen = sizeof(struct mbuf) + scc->stat.bufsize;
if (scc->stat.bufsize < 336) scc->stat.bufsize = 336;
@@ -487,7 +523,6 @@
if (scc->stat.rxbuffers < 4) scc->stat.rxbuffers = 4;
if (scc->stat.txbuffers < 4) scc->stat.txbuffers = 4;
-
/* allocate receive buffers for this channel */
@@ -502,7 +537,7 @@
if (bptr == NULLBUF)
{
- printk("z8530drv: %s: can't allocate memory for rx buffer pool", kdevname(scc->tty->device));
+ printk(KERN_WARNING "z8530drv: %s: can't allocate memory for rx buffer pool", SCC_DEVNAME);
break;
}
@@ -527,7 +562,7 @@
if (bptr == NULLBUF)
{
- printk("z8530drv: %s: can't allocate memory for tx buffer pool", kdevname(scc->tty->device));
+ printk(KERN_WARNING "z8530drv: %s: can't allocate memory for tx buffer pool", SCC_DEVNAME);
break;
}
@@ -537,6 +572,8 @@
scc_enqueue_buffer(&scc->tx_buffer_pool, bptr);
}
+
+ scc->mempool = 1;
}
@@ -545,102 +582,89 @@
static void
free_buffer_pool(struct scc_channel *scc)
{
- struct mbuf * bptr;
unsigned long flags;
- int cnt;
+ int rx_cnt, tx_cnt;
+
+ if (!scc->mempool)
+ return;
/* this one is a bit tricky and probably dangerous. */
+ rx_cnt = tx_cnt = 0;
save_flags(flags); cli();
/* esp. to free the buffers currently in use by ISR */
- bptr = scc->rx_bp;
- if (bptr != NULLBUF)
+ if (scc->rx_bp != NULLBUF)
{
+ kfree(scc->rx_bp);
scc->rx_bp = NULLBUF;
- scc_enqueue_buffer(&scc->rx_buffer_pool, bptr);
+ rx_cnt++;
}
- bptr = scc->tx_bp;
- if (bptr != NULLBUF)
+ if (scc->tx_bp != NULLBUF)
{
+ kfree(scc->tx_bp);
scc->tx_bp = NULLBUF;
- scc_enqueue_buffer(&scc->tx_buffer_pool, bptr);
+ tx_cnt++;
}
- bptr = scc->kiss_decode_bp;
- if (bptr != NULLBUF)
+ if (scc->kiss_decode_bp != NULLBUF)
{
+ kfree(scc->kiss_decode_bp);
scc->kiss_decode_bp = NULLBUF;
- scc_enqueue_buffer(&scc->tx_buffer_pool, bptr);
+ tx_cnt++;
}
-
- bptr = scc->kiss_encode_bp;
- if (bptr != NULLBUF)
+
+#ifdef CONFIG_SCC_TTY
+ if (scc->kiss_encode_bp != NULLBUF)
{
+ kfree(scc->kiss_encode_bp);
scc->kiss_encode_bp = NULLBUF;
- scc_enqueue_buffer(&scc->rx_buffer_pool, bptr);
+ rx_cnt++;
}
+#endif
restore_flags(flags);
while (scc->rx_queue != NULLBUF)
{
- bptr = scc_dequeue_buffer(&scc->rx_queue);
- scc_enqueue_buffer(&scc->rx_buffer_pool, bptr);
+ kfree(scc_dequeue_buffer(&scc->rx_queue));
+ rx_cnt++;
}
+ scc->stat.rx_queued = 0;
while (scc->tx_queue != NULLBUF)
{
- bptr = scc_dequeue_buffer(&scc->tx_queue);
- scc_enqueue_buffer(&scc->tx_buffer_pool, bptr);
+ kfree(scc_dequeue_buffer(&scc->tx_queue));
+ tx_cnt++;
}
-
- /* 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()?
- */
+ scc->stat.tx_queued = 0;
while (scc->rx_buffer_pool != NULLBUF)
{
- bptr = scc_dequeue_buffer(&scc->rx_buffer_pool);
-
- if (bptr != NULLBUF)
- {
- cnt++;
- kfree(bptr);
- }
+ kfree(scc_dequeue_buffer(&scc->rx_buffer_pool));
+ rx_cnt++;
}
-
- 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);
- }
+ kfree(scc_dequeue_buffer(&scc->tx_buffer_pool));
+ tx_cnt++;
}
-
- 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);
+
+ if (rx_cnt < scc->stat.rxbuffers) /* hmm... hmm... :-( */
+ printk(KERN_ERR "z8530drv: oops, deallocated only %d of %d rx buffers\n", rx_cnt, scc->stat.rxbuffers);
+ if (rx_cnt > scc->stat.rxbuffers) /* WHAT?!! */
+ printk(KERN_ERR "z8530drv: oops, deallocated %d instead of %d rx buffers. Very strange.\n", rx_cnt, scc->stat.rxbuffers);
+
+ if (tx_cnt < scc->stat.txbuffers)
+ printk(KERN_ERR "z8530drv: oops, deallocated only %d of %d tx buffers\n", tx_cnt, scc->stat.txbuffers);
+ if (tx_cnt > scc->stat.txbuffers)
+ printk(KERN_ERR "z8530drv: oops, deallocated %d instead of %d tx buffers. Very strange.\n", tx_cnt, scc->stat.txbuffers);
+
+ scc->mempool = 0;
}
@@ -666,28 +690,33 @@
if (bptr == NULLBUF)
{
- printk("z8530drv: scc_get_buffer(%s): tx buffer pool empty\n", kdevname(scc->tty->device));
-
+#if 0
+ printk(KERN_DEBUG "z8530drv: tx buffer pool for %s empty\n", SCC_DEVNAME);
+#endif
/* use the oldest from the queue instead */
bptr = scc_dequeue_buffer(&scc->tx_queue);
+ scc->stat.tx_queued--;
+ scc->stat.nospace++;
/* this should never, ever happen... */
if (bptr == NULLBUF)
- printk("z8530drv: scc_get_buffer(): panic - even no buffer found in tx queue\n");
+ printk(KERN_ERR "z8530drv: panic - all tx buffers for %s gone\n", SCC_DEVNAME);
}
} else {
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));
+ printk(KERN_DEBUG "z8530drv: rx buffer pool for %s empty\n", SCC_DEVNAME);
bptr = scc_dequeue_buffer(&scc->rx_queue);
+ scc->stat.rx_queued--;
+ scc->stat.nospace++;
if (bptr == NULLBUF)
- printk("z8530drv: scc_get_buffer(): panic - even no buffer found in rx queue\n");
+ printk(KERN_ERR "z8530drv: panic - all rx buffers for %s gone\n", SCC_DEVNAME);
}
}
@@ -705,153 +734,117 @@
/* * Interrupt Service Routines * */
/* ******************************************************************** */
-/* ----> interrupt service routine for the 8530 <---- */
-/* it's recommended to keep this function "inline" ;-) */
-
-static inline void
-scc_isr_dispatch(register struct scc_channel *scc, register int vector)
-{
- switch (vector & VECTOR_MASK)
- {
- case TXINT: scc_txint(scc); break;
- case EXINT: scc_exint(scc); break;
- case RXINT: scc_rxint(scc); break;
- case SPINT: scc_spint(scc); break;
- default : printk("scc_isr(): unknown interrupt status (addr %4.4x, state %2.2x)\n",scc->ctrl,vector);
- }
-}
+/* ----> subroutines for the interrupt handlers <---- */
+#ifdef CONFIG_SCC_TTY
+/* kick rx_timer (try to send received frame or a status message ASAP) */
-
-/* If the card has a latch for the interrupt vector (like the PA0HZP card)
- use it to get the number of the chip that generated the int.
- If not: poll all defined chips.
+/* 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.
+ IMHO we are fast enough with a timer routine called on the next
+ timer-INT... Your opinions?
*/
-static void
-scc_isr(int irq, void *dev_id, struct pt_regs *regs)
+static __inline__ void
+kick_rx_timer(struct scc_channel *scc)
{
- register unsigned char vector;
- register struct scc_channel *scc;
- register io_port q;
- register io_port *p;
- register int k;
-
+ 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);
+}
+#endif
- cli();
+static __inline__ void
+scc_notify(struct scc_channel *scc, int event)
+{
+ struct mbuf *bp;
- if (Vector_Latch)
- {
- while(1) /* forever...? */
- {
- Outb(Vector_Latch, 0); /* Generate INTACK */
-
- /* Read the vector */
- if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break;
- /* ...not forever! */
-
- /* Extract channel number and status from vector. */
- /* Isolate channel number */
- /* Call handler */
-
- if (vector & 0x01) break;
-
- scc=&SCC_Info[(((vector>>1)&0x7c)^0x04) >> 2];
-
- if (!scc->tty) break;
-
- scc_isr_dispatch(scc, vector);
-
- Outb(scc->ctrl,0x38); /* Reset Highest IUS" opcode to WR0 */
- }
-
- sti();
+ if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA)
return;
- }
-
- /* Find the SCC generating the interrupt by polling all attached SCCs
- * reading RR3A (the interrupt pending register)
- */
+ bp = scc_get_buffer(scc, BT_RECEIVE);
- k = 0;
- p = SCC_ctrl;
-
- while (k++ < Nchips)
- {
- if (!(q=*p++)) break;
-
- Outb(q,3);
+ if (bp == NULLBUF)
+ return;
- if (Inb(q))
- {
- if (!(q=*p++)) break;
-
- Outb(q,2);
- vector=Inb(q); /* Read the vector */
-
- /* Extract channel number and status from vector. */
- /* Isolate channel number */
- /* Call handler */
-
+ bp->data[0] = PARAM_HWEVENT;
+ bp->data[1] = event;
+ bp->cnt += 2;
- if (vector & 1) break;
-
- scc = &SCC_Info[(((vector >> 1) & 0x7c) ^ 0x04) >> 2];
-
- if (!scc->tty) break;
-
- /* Isolate status info from vector, call handler */
-
- scc_isr_dispatch(scc, vector);
-
- k = 0;
- p = SCC_ctrl;
+ if (scc->stat.is_netdev)
+ {
+#ifdef CONFIG_SCC_DEV
+ scc_net_rx(scc, bp);
+ scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
+#endif
+ } else {
+#ifdef CONFIG_SCC_TTY
+ scc_enqueue_buffer(&scc->rx_queue, bp);
+ scc->stat.rx_queued++;
+ kick_rx_timer(scc);
+#endif
+ }
+}
- } else p++;
- }
+static __inline__ void
+flush_rx_FIFO(struct scc_channel *scc)
+{
+ int k;
- sti();
+ for (k=0; k<3; k++)
+ Inb(scc->data);
+
+ if(scc->rx_bp != NULLBUF) /* did we receive something? */
+ {
+ scc->stat.rxerrs++; /* then count it as an error */
+ scc_enqueue_buffer(&scc->rx_buffer_pool, scc->rx_bp);
+
+ scc->rx_bp = NULLBUF;
+ }
}
-
/* ----> four different interrupt handlers for Tx, Rx, changing of */
/* DCD/CTS and Rx/Tx errors */
-
/* Transmitter interrupt handler */
-static void
-scc_txint(register struct scc_channel *scc)
+static __inline__ void
+scc_txint(struct scc_channel *scc)
{
- register struct mbuf *bp;
+ struct mbuf *bp;
scc->stat.txints++;
bp = scc->tx_bp;
+ /* send first octet */
+
if (bp == NULLBUF)
{
- do
+ do
{
if (bp != NULLBUF)
+ {
scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+ scc->stat.tx_queued--;
+ }
bp = scc_dequeue_buffer(&scc->tx_queue);
if (bp == NULLBUF)
{
- scc->stat.tx_state = TXS_BUSY;
- scc->t_tail = scc->kiss.tailtime;
-
+ scc_tx_done(scc);
Outb(scc->ctrl, RES_Tx_P); /* clear int */
return;
}
- if ( scc->kiss.not_slip && (bp->cnt > 0) )
+ if (bp->cnt > 0)
{
bp->rw_ptr++;
bp->cnt--;
@@ -859,57 +852,47 @@
} while (bp->cnt < 1);
-
- Outb(scc->ctrl, RES_Tx_CRC); /* reset CRC generator */
+ scc->tx_bp = bp;
+ scc->stat.tx_state = TXS_ACTIVE;
+
+ OutReg(scc->ctrl, R0, RES_Tx_CRC);
+ /* reset CRC generator */
or(scc,R10,ABUNDER); /* re-install underrun protection */
- Outb(scc->data,*bp->rw_ptr); /* send byte */
+ 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
+ Outb(scc->ctrl,RES_EOM_L);
+
+ bp->cnt--;
+ return;
+ }
+
+ /* End Of Frame... */
+
if (bp->cnt <= 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->stat.tx_queued--;
scc->tx_bp = NULLBUF;
scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */
return;
- } else {
- Outb(scc->data,*bp->rw_ptr);
- }
+ }
+ /* send octet */
+
+ Outb(scc->data,*bp->rw_ptr);
bp->rw_ptr++; /* increment pointer */
bp->cnt--; /* decrease byte count */
}
-static inline void
-flush_FIFO(register struct scc_channel *scc)
+
+/* External/Status interrupt handler */
+static __inline__ void
+scc_exint(struct scc_channel *scc)
{
- register int k;
-
- for (k=0; k<3; k++)
- Inb(scc->data);
-
- if(scc->rx_bp != NULLBUF) /* did we receive something? */
- {
- 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)
-{
- register unsigned char status,changes,chg_and_stat;
+ unsigned char status,changes,chg_and_stat;
scc->stat.exints++;
@@ -920,9 +903,9 @@
/* ABORT: generated whenever DCD drops while receiving */
if (chg_and_stat & BRK_ABRT) /* Received an ABORT */
- flush_FIFO(scc);
-
-
+ flush_rx_FIFO(scc);
+
+
/* DCD: on = start to receive packet, off = ABORT condition */
/* (a successfully received packet generates a special condition int) */
@@ -936,69 +919,67 @@
or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
} else { /* DCD is now OFF */
cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */
- flush_FIFO(scc);
+ flush_rx_FIFO(scc);
}
+
+ if (!scc->kiss.softdcd)
+ scc_notify(scc, (status & DCD)? HWEV_DCD_ON:HWEV_DCD_OFF);
+ }
+
+ /* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */
+
+ if (changes & SYNC_HUNT)
+ {
+ if (scc->kiss.softdcd)
+ scc_notify(scc, (status & SYNC_HUNT)? HWEV_DCD_OFF:HWEV_DCD_ON);
+ else
+ cl(scc,R15,SYNCIE); /* oops, we were too lazy to disable this? */
}
#ifdef notdef
- /* CTS: use external TxDelay (what's that good for?!) */
-
+ /* CTS: use external TxDelay (what's that good for?!)
+ * Anyway: If we _could_ use it (BayCom USCC uses CTS for
+ * own purposes) we _should_ use the "autoenable" feature
+ * of the Z8530 and not this interrupt...
+ */
+
if (chg_and_stat & CTS) /* CTS is now ON */
- {
- if (!Running(t_txdel) && scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */
- scc->t_txdel = 0; /* kick it! */
-
+ {
+ if (scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */
+ scc_start_tx_timer(scc, t_txdelay, 0);
}
#endif
- if ( (status & TxEOM) && (scc->stat.tx_state == TXS_ACTIVE) )
+ if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM))
{
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;
-
+
if (scc->tx_bp != NULLBUF)
{
scc_enqueue_buffer(&scc->tx_buffer_pool, scc->tx_bp);
+ scc->stat.tx_queued--;
scc->tx_bp = NULLBUF;
}
- if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0;
or(scc,R10,ABUNDER);
+ scc_start_tx_timer(scc, t_txdelay, 1); /* restart transmission */
}
- if (status & ZCOUNT) /* Oops? */
- {
- scc->stat.tx_under = 9999; /* errr... yes. */
- Outb(scc->ctrl, RES_Tx_P); /* just to be sure */
- scc->t_maxk = 1;
-
- 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! */
- }
-
-
scc->status = status;
Outb(scc->ctrl,RES_EXT_INT);
}
/* Receiver interrupt handler */
-static void
-scc_rxint(register struct scc_channel *scc)
+static __inline__ void
+scc_rxint(struct scc_channel *scc)
{
- register struct mbuf *bp;
+ struct mbuf *bp;
scc->stat.rxints++;
- if( Running(t_maxk) && !(scc->kiss.fulldup))
+ if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF)
{
Inb(scc->data); /* discard char */
or(scc,R3,ENT_HM); /* enter hunt mode for next flag */
@@ -1012,20 +993,22 @@
bp = scc_get_buffer(scc, BT_RECEIVE);
if (bp == NULLBUF)
{
- 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;
+
+ *bp->rw_ptr=0; /* KISS data */
+ bp->rw_ptr++;
+ bp->cnt++;
}
if (bp->cnt > scc->stat.bufsize)
{
#ifdef notdef
- printk("scc_rxint(): oops, received huge frame...\n");
+ printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n");
#endif
scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
scc->rx_bp = NULLBUF;
@@ -1040,33 +1023,13 @@
bp->cnt++;
}
-/* kick rx_timer (try to send received frame or part of it ASAP) */
-
-/* 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)
-{
- 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 */
-static void
-scc_spint(register struct scc_channel *scc)
+static __inline__ void
+scc_spint(struct scc_channel *scc)
{
- register unsigned char status;
- register struct mbuf *bp;
+ unsigned char status;
+ struct mbuf *bp;
scc->stat.spints++;
@@ -1083,7 +1046,7 @@
if (bp) scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
scc->rx_bp = NULLBUF;
}
-
+
if(status & END_FR && bp != NULLBUF) /* end of frame */
{
/* CRC okay, frame ends on 8 bit boundary and received something ? */
@@ -1092,23 +1055,141 @@
{
/* ignore last received byte (first of the CRC bytes) */
bp->cnt--;
-
- scc_enqueue_buffer(&scc->rx_queue, bp);
+
+ if (scc->stat.is_netdev)
+ {
+#ifdef CONFIG_SCC_DEV
+ scc_net_rx(scc, bp);
+ scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
+#endif
+ } else {
+#ifdef CONFIG_SCC_TTY
+ scc_enqueue_buffer(&scc->rx_queue, bp);
+ scc->stat.rx_queued++;
+ kick_rx_timer(scc);
+#endif
+ }
+
scc->rx_bp = NULLBUF;
scc->stat.rxframes++;
- scc->stat.rx_queued++;
- kick_rx_timer(scc);
} else { /* a bad frame */
scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
scc->rx_bp = NULLBUF;
scc->stat.rxerrs++;
}
+ }
+
+ Outb(scc->ctrl,ERR_RES);
+}
+
+
+/* ----> interrupt service routine for the Z8530 <---- */
+
+static void
+scc_isr_dispatch(struct scc_channel *scc, int vector)
+{
+ switch (vector & VECTOR_MASK)
+ {
+ case TXINT: scc_txint(scc); break;
+ case EXINT: scc_exint(scc); break;
+ case RXINT: scc_rxint(scc); break;
+ case SPINT: scc_spint(scc); break;
}
+}
+
+/* If the card has a latch for the interrupt vector (like the PA0HZP card)
+ use it to get the number of the chip that generated the int.
+ If not: poll all defined chips.
+ */
+
+#define SCC_IRQTIMEOUT 30000
+
+static void
+scc_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned char vector;
+ struct scc_channel *scc;
+ struct scc_ctrl *ctrl;
+ int k;
- Outb(scc->ctrl,ERR_RES);
+ scc_cli(irq);
+
+ if (Vector_Latch)
+ {
+ for(k=0; k < SCC_IRQTIMEOUT; k++)
+ {
+ Outb(Vector_Latch, 0); /* Generate INTACK */
+
+ /* Read the vector */
+ if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break;
+ if (vector & 0x01) break;
+
+ scc=&SCC_Info[vector >> 3 ^ 0x01];
+ if (!scc->tty && !scc->dev) break;
+
+ scc_isr_dispatch(scc, vector);
+
+ OutReg(scc->ctrl,R0,RES_H_IUS); /* Reset Highest IUS */
+ }
+ scc_sti(irq);
+
+ if (k == SCC_IRQTIMEOUT)
+ printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n");
+
+ return;
+ }
+
+ /* Find the SCC generating the interrupt by polling all attached SCCs
+ * reading RR3A (the interrupt pending register)
+ */
+
+ ctrl = SCC_ctrl;
+ while (ctrl->chan_A)
+ {
+ if (ctrl->irq != irq)
+ {
+ ctrl++;
+ continue;
+ }
+
+ scc = NULL;
+ for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++)
+ {
+ vector=InReg(ctrl->chan_B,R2); /* Read the vector */
+ if (vector & 0x01) break;
+
+ scc = &SCC_Info[vector >> 3 ^ 0x01];
+ if (!scc->tty && !scc->dev) break;
+
+ scc_isr_dispatch(scc, vector);
+ }
+
+ if (k == SCC_IRQTIMEOUT)
+ {
+ printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n");
+ break;
+ }
+
+ /* This looks wierd and it is. At least the BayCom USCC doesn't
+ * use the Interrupt Daisy Chain, thus we'll have to start
+ * all over again to be sure not to miss an interrupt from
+ * (any of) the other chip(s)...
+ * Honestly, the situation *is* braindamaged...
+ */
+
+ if (scc != NULL)
+ {
+ OutReg(scc->ctrl,R0,RES_H_IUS);
+ ctrl = SCC_ctrl;
+ } else
+ ctrl++;
+ }
+
+ scc_sti(irq);
}
+
/* ******************************************************************** */
/* * Init Channel */
/* ******************************************************************** */
@@ -1116,30 +1197,31 @@
/* ----> set SCC channel speed <---- */
-static inline void set_brg(register struct scc_channel *scc, unsigned int tc)
+static __inline__ void
+set_brg(struct scc_channel *scc, unsigned int tc)
{
- unsigned long flags;
-
- save_flags(flags); cli(); /* 2-step register accesses... */
-
cl(scc,R14,BRENABL); /* disable baudrate generator */
wr(scc,R12,tc & 255); /* brg rate LOW */
wr(scc,R13,tc >> 8); /* brg rate HIGH */
or(scc,R14,BRENABL); /* enable baudrate generator */
-
- restore_flags(flags);
}
-static inline void set_speed(register struct scc_channel *scc)
+static __inline__ void
+set_speed(struct scc_channel *scc)
{
+ disable_irq(scc->irq);
+
if (scc->modem.speed > 0) /* paranoia... */
set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2);
+
+ enable_irq(scc->irq);
}
/* ----> initialize a SCC channel <---- */
-static inline void init_brg(register struct scc_channel *scc)
+static __inline__ void
+init_brg(struct scc_channel *scc)
{
wr(scc, R14, BRSRC); /* BRG source = PCLK */
OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */
@@ -1192,14 +1274,17 @@
*/
static void
-init_channel(register struct scc_channel *scc)
+init_channel(struct scc_channel *scc)
{
- unsigned long flags;
+ disable_irq(scc->irq);
- if (scc->rx_t.next) del_timer(&(scc->rx_t));
- if (scc->tx_t.next) del_timer(&(scc->tx_t));
+#ifdef CONFIG_SCC_TTY
+ del_timer(&scc->rx_t);
+#endif
+ del_timer(&scc->tx_t);
+ del_timer(&scc->tx_wdog);
+
- save_flags(flags); cli();
wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */
wr(scc,R1,0); /* no W/REQ operation */
@@ -1273,9 +1358,12 @@
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);
-
+ /* enable ABORT, DCD & SYNC/HUNT interrupts */
+
+ wr(scc,R15, BRKIE|TxUIE|DCDIE);
+ if (scc->kiss.softdcd)
+ or(scc,R15, SYNCIE);
+
Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */
@@ -1287,7 +1375,7 @@
scc_init_timer(scc);
- restore_flags(flags);
+ enable_irq(scc->irq);
}
@@ -1305,21 +1393,29 @@
scc_key_trx(struct scc_channel *scc, char tx)
{
unsigned int time_const;
-
- if (scc->modem.speed < baud_table[1])
- scc->modem.speed = 1200;
if (scc->brand & PRIMUS)
Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0));
-
+
+ if (scc->modem.speed < 300)
+ scc->modem.speed = 1200;
+
time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2;
-
+
+ disable_irq(scc->irq);
+
+ if (tx)
+ {
+ or(scc, R1, TxINT_ENAB); /* t_maxkeyup may have reset these */
+ or(scc, R15, TxUIE);
+ }
+
if (scc->modem.clocksrc == CLK_DPLL)
- { /* simplex operation */
+ { /* force simplex operation */
if (tx)
{
- cl(scc,R3,RxENABLE|ENT_HM); /* then switch off receiver */
-
+ cl(scc, R3, RxENABLE|ENT_HM); /* switch off receiver */
+ cl(scc, R15, DCDIE); /* No DCD changes, please */
set_brg(scc, time_const); /* reprogram baudrate generator */
/* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */
@@ -1335,21 +1431,140 @@
wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
or(scc,R3,RxENABLE|ENT_HM);
+ or(scc,R15, DCDIE);
}
} else {
if (tx)
+ {
+ if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+ {
+ cl(scc, R3, RxENABLE);
+ cl(scc, R15, DCDIE);
+ }
+
+
or(scc,R5,RTS|TxENAB); /* enable tx */
- else
+ } else {
cl(scc,R5,RTS|TxENAB); /* disable tx */
+
+ if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+ {
+ or(scc, R3, RxENABLE|ENT_HM);
+ or(scc, R15, DCDIE);
+ }
+ }
+ }
+
+ enable_irq(scc->irq);
+}
+
+
+/* ----> SCC timer interrupt handler and friends. <---- */
+
+static void
+scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ del_timer(&scc->tx_t);
+
+ if (when == 0)
+ {
+ handler((unsigned long) scc);
+ } else
+ if (when != TIMER_OFF)
+ {
+ scc->tx_t.data = (unsigned long) scc;
+ scc->tx_t.function = handler;
+ scc->tx_t.expires = jiffies + (when*HZ)/100;
+ add_timer(&scc->tx_t);
+ }
+
+ restore_flags(flags);
+}
+
+static void
+scc_start_defer(struct scc_channel *scc)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ del_timer(&scc->tx_wdog);
+
+ if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF)
+ {
+ scc->tx_wdog.data = (unsigned long) scc;
+ scc->tx_wdog.function = t_busy;
+ scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer;
+ add_timer(&scc->tx_wdog);
+ }
+ restore_flags(flags);
+}
+
+static void
+scc_start_maxkeyup(struct scc_channel *scc)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ del_timer(&scc->tx_wdog);
+
+ if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF)
+ {
+ scc->tx_wdog.data = (unsigned long) scc;
+ scc->tx_wdog.function = t_maxkeyup;
+ scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup;
+ add_timer(&scc->tx_wdog);
}
+
+ restore_flags(flags);
}
+/*
+ * This is called from scc_txint() when there are no more frames to send.
+ * Not exactly a timer function, but it is a close friend of the family...
+ */
+
+static void
+scc_tx_done(struct scc_channel *scc)
+{
+ /*
+ * trx remains keyed in fulldup mode 2 until t_idle expires.
+ */
+
+ switch (scc->kiss.fulldup)
+ {
+ case KISS_DUPLEX_LINK:
+ scc->stat.tx_state = TXS_IDLE2;
+ if (scc->kiss.idletime != TIMER_OFF)
+ scc_start_tx_timer(scc, t_idle, scc->kiss.idletime*100);
+ break;
+ case KISS_DUPLEX_OPTIMA:
+ scc_notify(scc, HWEV_ALL_SENT);
+ break;
+ default:
+ scc->stat.tx_state = TXS_BUSY;
+ scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
+ }
+
+#ifdef CONFIG_SCC_DEV
+ if (scc->stat.is_netdev)
+ scc->dev->tbusy = 0;
+#endif
+}
-/* ----> SCC timer interrupt handler and friends. Will be called every 1/TPS s <---- */
static unsigned char Rand = 17;
-static inline int is_grouped(register struct scc_channel *scc)
+static __inline__ int
+is_grouped(struct scc_channel *scc)
{
int k;
struct scc_channel *scc2;
@@ -1362,8 +1577,8 @@
scc2 = &SCC_Info[k];
grp2 = scc2->kiss.group;
- if (scc2 == scc || !(scc2->tty && grp2))
- return 0;
+ if (scc2 == scc || !(scc2->tty && scc2->dev && grp2))
+ continue;
if ((grp1 & 0x3f) == (grp2 & 0x3f))
{
@@ -1376,205 +1591,244 @@
}
return 0;
}
-
-static inline void dw_slot_timeout(register struct scc_channel *scc)
+/* DWAIT and SLOTTIME expired
+ *
+ * fulldup == 0: DCD is active or Rand > P-persistence: start t_busy timer
+ * else key trx and start txdelay
+ * fulldup == 1: key trx and start txdelay
+ * fulldup == 2: mintime expired, reset status or key trx and start txdelay
+ */
+
+static void
+t_dwait(unsigned long channel)
{
- scc->t_dwait = TIMER_STOPPED;
- scc->t_slot = TIMER_STOPPED;
+ struct scc_channel *scc = (struct scc_channel *) channel;
- if (!scc->kiss.fulldup)
+ if (scc->stat.tx_state == TXS_WAIT)
+ {
+ if (scc->tx_queue == NULL)
+ {
+ scc->stat.tx_state = TXS_IDLE;
+ return;
+ }
+
+ scc->stat.tx_state = TXS_BUSY;
+ }
+
+ if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
{
Rand = Rand * 17 + 31;
if ( (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD)) || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) )
{
- if (scc->t_mbusy == TIMER_STOPPED)
- scc->t_mbusy = TPS * scc->kiss.maxdefer;
-
- scc->t_slot = scc->kiss.slottime;
+ scc_start_defer(scc);
+ scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime);
return ;
-
}
}
-
+
if ( !(scc->wreg[R5] & RTS) )
{
- scc->t_txdel = scc->kiss.txdelay;
scc_key_trx(scc, TX_ON);
+ scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
} else {
- scc->t_txdel = 0;
+ scc_start_tx_timer(scc, t_txdelay, 0);
}
}
+/* TXDELAY expired
+ *
+ * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog.
+ */
-
-static inline void txdel_timeout(register struct scc_channel *scc)
+static void
+t_txdelay(unsigned long channel)
{
- scc->t_txdel = TIMER_STOPPED;
-
- scc->t_maxk = TPS * scc->kiss.maxkeyup;
-
+ struct scc_channel *scc = (struct scc_channel *) channel;
+
+ scc_start_maxkeyup(scc);
+
if (scc->tx_bp == NULLBUF)
+ {
+ disable_irq(scc->irq);
scc_txint(scc);
+ enable_irq(scc->irq);
+ }
}
+/* TAILTIME expired
+ *
+ * switch off transmitter. If we were stopped by Maxkeyup restart
+ * transmission after 'mintime' seconds
+ */
-static inline void tail_timeout(register struct scc_channel *scc)
+static void
+t_tail(unsigned long channel)
{
- scc->t_tail = TIMER_STOPPED;
+ struct scc_channel *scc = (struct scc_channel *) channel;
- /* when fulldup is 0 or 1, switch off the transmitter.
- * when frames are still queued (because of transmit time limit),
- * restart the procedure to get the channel after MINTIME.
- * when fulldup is 2, the transmitter remains keyed and we
- * continue sending after waiting for waittime. IDLETIME is an
- * idle timeout in this case.
- */
-
- if (scc->kiss.fulldup < 2)
- {
- if (scc->tx_bp) /* we had a timeout? */
- {
- scc->stat.tx_state = TXS_BUSY;
- scc->t_dwait = TPS * scc->kiss.mintime; /* try again */
- }
-
- scc->stat.tx_state = TXS_IDLE;
- scc->t_maxk = TIMER_STOPPED;
- scc_key_trx(scc, TX_OFF);
- return;
- }
-
- if (scc->tx_bp) /* maxkeyup expired */ /* ?! */
- {
- scc->stat.tx_state = TXS_BUSY;
- scc->t_txdel = TPS * scc->kiss.waittime;
- }
- else
-
- scc->t_idle = TPS * scc->kiss.idletime;
+ del_timer(&scc->tx_wdog);
+ scc_key_trx(scc, TX_OFF);
+
+ if (scc->stat.tx_state == TXS_TIMEOUT) /* we had a timeout? */
+ {
+ scc->stat.tx_state = TXS_WAIT;
+
+ if (scc->tx_bp != NULLBUF)
+ scc_enqueue_buffer(&scc->tx_queue, scc->tx_bp);
+
+ scc->tx_bp = NULLBUF;
+
+ if (scc->kiss.mintime != TIMER_OFF) /* try it again */
+ scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
+ else
+ scc_start_tx_timer(scc, t_dwait, 0);
+ return;
+ }
+
+ scc->stat.tx_state = TXS_IDLE;
+
+#ifdef CONFIG_SCC_DEV
+ if (scc->stat.is_netdev)
+ scc->dev->tbusy = 0;
+#endif
}
-static inline void busy_timeout(register struct scc_channel *scc)
+/* BUSY timeout
+ *
+ * throw away send buffers if DCD remains active too long.
+ */
+
+static void
+t_busy(unsigned long channel)
{
-#ifdef THROW_AWAY_AFTER_BUSY_TIMEOUT
- register struct mbuf *bp; /* not tested */
+ struct scc_channel *scc = (struct scc_channel *) channel;
+ struct mbuf *bp;
+
+ del_timer(&scc->tx_t);
- while (bp = scc_dequeue_buffer(&scc->tx_queue))
+ while ((bp = scc_dequeue_buffer(&scc->tx_queue)) != NULLBUF)
scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+
+ scc->stat.tx_queued = 0;
+ scc->stat.txerrs++;
scc->tx_queue = NULLBUF;
scc->stat.tx_state = TXS_IDLE;
-
-#else
- scc->t_txdel = scc->kiss.txdelay; /* brute force ... */
+
+#ifdef CONFIG_SCC_DEV
+ if (scc->stat.is_netdev)
+ scc->dev->tbusy = 0;
#endif
- scc->t_mbusy = TIMER_STOPPED;
-
}
+/* MAXKEYUP timeout
+ *
+ * this is our watchdog.
+ */
-static inline void maxk_idle_timeout(register struct scc_channel *scc)
-{
- scc->t_maxk = TIMER_STOPPED;
- scc->t_idle = TIMER_STOPPED;
-
- scc->stat.tx_state = TXS_BUSY;
- scc->t_tail = scc->kiss.tailtime;
-}
-
-static void
-scc_tx_timer(unsigned long channel)
+static void
+t_maxkeyup(unsigned long channel)
{
- register struct scc_channel *scc;
+ struct scc_channel *scc = (struct scc_channel *) channel;
unsigned long flags;
-
- scc = (struct scc_channel *) channel;
-
- if (scc->tty && scc->init)
- {
- save_flags(flags); cli();
-
- /* 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);
+ save_flags(flags);
+ cli();
+
+ if (scc->stat.is_netdev)
+ {
+#ifdef CONFIG_SCC_DEV
+ /*
+ * let things settle down before we start to
+ * accept new data.
+ */
+ scc->dev->tbusy = 1;
+ scc->dev->trans_start = jiffies;
+#endif
}
+
+ del_timer(&scc->tx_t);
+
+ cl(scc, R1, TxINT_ENAB); /* force an ABORT, but don't */
+ cl(scc, R15, TxUIE); /* count it. */
+ OutReg(scc->ctrl, R0, RES_Tx_P);
+
+ restore_flags(flags);
+
+ scc->stat.txerrs++;
+ scc->stat.tx_state = TXS_TIMEOUT;
+ scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
+}
+
+/* IDLE timeout
+ *
+ * in fulldup mode 2 it keys down the transmitter after 'idle' seconds
+ * of inactivity. We will not restart transmission before 'mintime'
+ * expires.
+ */
+
+static void
+t_idle(unsigned long channel)
+{
+ struct scc_channel *scc = (struct scc_channel *) channel;
- scc->tx_t.expires = jiffies + HZ/TPS;
- add_timer(&scc->tx_t);
+ del_timer(&scc->tx_wdog);
+ scc_key_trx(scc, TX_OFF);
+
+ if (scc->kiss.mintime != TIMER_OFF)
+ scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
+ scc->stat.tx_state = TXS_WAIT;
}
-
+
+#ifdef CONFIG_SCC_TTY
static void
scc_rx_timer(unsigned long channel)
{
- register struct scc_channel *scc;
+ struct scc_channel *scc;
scc = (struct scc_channel *) channel;
if (scc->rx_queue && scc->throttled)
{
- scc->rx_t.expires = jiffies + HZ/TPS;
+ scc->rx_t.expires = jiffies + HZ/25; /* 40 msec */
add_timer(&scc->rx_t);
return;
}
kiss_encode(scc);
-
+
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;
+ printk(KERN_DEBUG "z8530drv: warning, %s should be throttled\n",
+ SCC_DEVNAME);
+
+ scc->rx_t.expires = jiffies + HZ/25; /* 40 msec */
add_timer(&scc->rx_t);
}
}
+#endif
static void
scc_init_timer(struct scc_channel *scc)
{
unsigned long flags;
- save_flags(flags); cli();
-
- Stop_Timer(t_dwait);
- Stop_Timer(t_slot);
- Stop_Timer(t_txdel);
- Stop_Timer(t_tail);
- Stop_Timer(t_mbusy);
- Stop_Timer(t_maxk);
- Stop_Timer(t_idle);
+ save_flags(flags);
+ cli();
scc->stat.tx_state = TXS_IDLE;
- if (scc->tx_t.next)
- del_timer(&scc->tx_t);
-
- 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);
-
+#ifdef CONFIG_SCC_TTY
scc->rx_t.data = (unsigned long) scc;
scc->rx_t.function = scc_rx_timer;
+#endif
restore_flags(flags);
}
@@ -1586,54 +1840,111 @@
/*
- * this will set the "kiss" parameters through kiss itself
+ * this will set the "kiss" parameters through KISS commands or ioctl()
*/
-
-static void
-kiss_set_param(struct scc_channel *scc,char cmd, unsigned int val)
+
+#define CAST(x) (unsigned long)(x)
+
+static unsigned int
+kiss_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg)
{
+ int dcd;
+
+ switch (cmd)
+ {
+ case PARAM_TXDELAY: scc->kiss.txdelay=arg; break;
+ case PARAM_PERSIST: scc->kiss.persist=arg; break;
+ case PARAM_SLOTTIME: scc->kiss.slottime=arg; break;
+ case PARAM_TXTAIL: scc->kiss.tailtime=arg; break;
+ case PARAM_FULLDUP: scc->kiss.fulldup=arg; break;
+ case PARAM_DTR: break; /* does someone need this? */
+ case PARAM_GROUP: scc->kiss.group=arg; break;
+ case PARAM_IDLE: scc->kiss.idletime=arg; break;
+ case PARAM_MIN: scc->kiss.mintime=arg; break;
+ case PARAM_MAXKEY: scc->kiss.maxkeyup=arg; break;
+ case PARAM_WAIT: scc->kiss.waittime=arg; break;
+ case PARAM_MAXDEFER: scc->kiss.maxdefer=arg; break;
+ case PARAM_TX: scc->kiss.tx_inhibit=arg; break;
+
+ case PARAM_SOFTDCD:
+ scc->kiss.softdcd=arg;
+ if (arg)
+ or(scc, R15, SYNCIE);
+ else
+ cl(scc, R15, SYNCIE);
+ break;
+
+ case PARAM_SPEED:
+ if (arg < 256)
+ scc->modem.speed=arg*100;
+ else
+ scc->modem.speed=arg;
+
+ if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */
+ set_speed(scc);
+ break;
+
+ case PARAM_RTS:
+ if ( !(scc->wreg[R5] & RTS) )
+ {
+ if (arg != TX_OFF)
+ scc_key_trx(scc, TX_ON);
+ scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
+ } else {
+ if (arg == TX_OFF)
+ {
+ scc->stat.tx_state = TXS_BUSY;
+ scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
+ }
+ }
+ break;
+
+ case PARAM_HWEVENT:
+ dcd = (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD));
+ scc_notify(scc, dcd? HWEV_DCD_ON:HWEV_DCD_OFF);
+ break;
+
+ default: return -EINVAL;
+ }
+
+ return 0;
+}
-#define VAL val=val*TPS/100
-#define SVAL val? val:TIMER_STOPPED
- switch(cmd){
- case PARAM_TXDELAY:
- scc->kiss.txdelay = VAL; break;
- case PARAM_PERSIST:
- scc->kiss.persist = val; break;
- case PARAM_SLOTTIME:
- scc->kiss.slottime = VAL; break;
- case PARAM_TXTAIL:
- scc->kiss.tailtime = VAL; break;
- case PARAM_FULLDUP:
- scc->kiss.fulldup = val; break;
- case PARAM_WAIT:
- scc->kiss.waittime = VAL; break;
- case PARAM_MAXKEY:
- scc->kiss.maxkeyup = SVAL; break;
- case PARAM_MIN:
- scc->kiss.mintime = SVAL; break;
- case PARAM_IDLE:
- scc->kiss.idletime = val; break;
- case PARAM_MAXDEFER:
- scc->kiss.maxdefer = SVAL; break;
- case PARAM_GROUP:
- scc->kiss.group = val; break;
- case PARAM_TX:
- scc->kiss.tx_inhibit = val;
- case PARAM_SOFTDCD:
- scc->kiss.softdcd = val;
+
+static unsigned long
+kiss_get_param(struct scc_channel *scc, unsigned int cmd)
+{
+ switch (cmd)
+ {
+ case PARAM_TXDELAY: return CAST(scc->kiss.txdelay);
+ case PARAM_PERSIST: return CAST(scc->kiss.persist);
+ case PARAM_SLOTTIME: return CAST(scc->kiss.slottime);
+ case PARAM_TXTAIL: return CAST(scc->kiss.tailtime);
+ case PARAM_FULLDUP: return CAST(scc->kiss.fulldup);
+ case PARAM_SOFTDCD: return CAST(scc->kiss.softdcd);
+ case PARAM_DTR: return CAST((scc->wreg[R5] & DTR)? 1:0);
+ case PARAM_RTS: return CAST((scc->wreg[R5] & RTS)? 1:0);
+ case PARAM_SPEED: return CAST(scc->modem.speed);
+ case PARAM_GROUP: return CAST(scc->kiss.group);
+ case PARAM_IDLE: return CAST(scc->kiss.idletime);
+ case PARAM_MIN: return CAST(scc->kiss.mintime);
+ case PARAM_MAXKEY: return CAST(scc->kiss.maxkeyup);
+ case PARAM_WAIT: return CAST(scc->kiss.waittime);
+ case PARAM_MAXDEFER: return CAST(scc->kiss.maxdefer);
+ case PARAM_TX: return CAST(scc->kiss.tx_inhibit);
+ default: return NO_SUCH_PARAM;
}
- return;
-#undef VAL
-#undef SVAL
}
+#undef CAST
+#undef SVAL
/* interpret frame: strip CRC and decode KISS */
-static void kiss_interpret_frame(struct scc_channel * scc)
+static void
+kiss_interpret_frame(struct scc_channel * scc)
{
unsigned char kisscmd;
unsigned long flags;
@@ -1642,14 +1953,6 @@
bp = scc->kiss_decode_bp;
bp->rw_ptr = bp->data;
-#ifdef DEBUG_BUFFERS
- if (bp == NULLBUF)
- {
- printk("kiss_interpret_frame(): weird --- nothing to do.\n");
- return;
- }
-#endif
-
if (bp->cnt < 2)
{
scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
@@ -1657,15 +1960,8 @@
return;
}
-
-
- if (scc->kiss.not_slip)
- {
- kisscmd = *bp->rw_ptr;
- bp->rw_ptr++;
- } else {
- kisscmd = 0;
- }
+ kisscmd = *bp->rw_ptr;
+ bp->rw_ptr++;
if (kisscmd & 0xa0)
{
@@ -1679,10 +1975,8 @@
}
}
-
kisscmd &= 0x1f;
-
-
+
if (kisscmd)
{
kiss_set_param(scc, kisscmd, *bp->rw_ptr);
@@ -1692,32 +1986,47 @@
}
scc_enqueue_buffer(&scc->tx_queue, bp); /* enqueue frame */
+ scc->kiss_decode_bp = NULLBUF;
scc->stat.txframes++;
scc->stat.tx_queued++;
- scc->kiss_decode_bp = NULLBUF;
- save_flags(flags); cli();
+ save_flags(flags);
+ cli();
+
+ /*
+ * start transmission if the trx state is idle or
+ * t_idle hasn't expired yet. Use dwait/persistance/slottime
+ * algorithm for normal halfduplex operation.
+ */
- if(scc->stat.tx_state == TXS_IDLE)
- { /* when transmitter is idle */
+ if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2)
+ {
scc->stat.tx_state = TXS_BUSY;
- scc->t_dwait = (scc->kiss.fulldup? 0:scc->kiss.waittime);
+ if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+ scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime);
+ else
+ scc_start_tx_timer(scc, t_dwait, 0);
}
-
+
restore_flags(flags);
}
-static inline void kiss_store_byte(struct scc_channel *scc, unsigned char ch)
+#ifdef CONFIG_SCC_TTY
+static inline void
+kiss_store_byte(struct scc_channel *scc, unsigned char ch)
{
- register struct mbuf *bp = scc->kiss_decode_bp;
+ struct mbuf *bp = scc->kiss_decode_bp;
+ static int k = 0;
if (bp != NULLBUF)
{
if (bp->cnt > scc->stat.bufsize)
- printk("kiss_decode(): frame too long\n");
- else
{
+ if (!k++)
+ printk(KERN_NOTICE "z8530drv: KISS frame too long\n");
+ } else {
+ k = 0;
*bp->rw_ptr = ch;
bp->rw_ptr++;
bp->cnt++;
@@ -1725,7 +2034,8 @@
}
}
-static inline int kiss_decode(struct scc_channel *scc, unsigned char ch)
+static inline int
+kiss_decode(struct scc_channel *scc, unsigned char ch)
{
switch (scc->stat.tx_kiss_state)
{
@@ -1779,7 +2089,7 @@
/* ----> Encode received data and write it to the flip-buffer <---- */
static void
-kiss_encode(register struct scc_channel *scc)
+kiss_encode(struct scc_channel *scc)
{
struct mbuf *bp;
struct tty_struct * tty = scc->tty;
@@ -1825,10 +2135,6 @@
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;
}
@@ -1852,6 +2158,7 @@
queue_task(&tty->flip.tqueue, &tq_timer); /* kick it... */
}
+#endif /* CONFIG_SCC_TTY */
/* ******************************************************************* */
@@ -1868,7 +2175,7 @@
char *flag;
- printk("Init Z8530 driver: %u channels, IRQ", Nchips*2);
+ printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2);
flag=" ";
for (k = 0; k < 16; k++)
@@ -1885,9 +2192,7 @@
{
scc=&SCC_Info[2*chip];
if (!scc->ctrl) continue;
-
- save_flags(flags); cli(); /* because of 2-step accesses */
-
+
/* Special SCC cards */
if(scc->brand & EAGLE) /* this is an EAGLE card */
@@ -1901,12 +2206,15 @@
/* some general init we can do now */
+ save_flags(flags);
+ cli();
+
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);
}
@@ -1920,18 +2228,17 @@
/* ******************************************************************** */
-
/* scc_paranoia_check(): warn user if something went wrong */
-static inline int scc_paranoia_check(struct scc_channel *scc, kdev_t device,
- const char *routine)
+static __inline__ int
+scc_paranoia_check(struct scc_channel *scc, kdev_t device, const char *routine)
{
#ifdef SCC_PARANOIA_CHECK
static const char *badmagic =
- "Warning: bad magic number for Z8530 SCC struct (%s) in %s\n";
+ KERN_ALERT "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";
+ KERN_CRIT "Warning: Z8530 not found for (%s) in %s (forgot to run sccinit?)\n";
if (!scc->init)
{
@@ -1949,10 +2256,11 @@
return 0;
}
-
+#ifdef CONFIG_SCC_TTY
/* ----> this one is called whenever you open the device <---- */
-int scc_open(struct tty_struct *tty, struct file * filp)
+int
+scc_open(struct tty_struct *tty, struct file * filp)
{
struct scc_channel *scc;
int chan;
@@ -1976,28 +2284,30 @@
if (scc->magic != SCC_MAGIC)
{
- printk("ERROR: scc_open(): bad magic number for device (%s)",
+ printk(KERN_ALERT "z8530drv: scc_open() found bad magic number for device (%s)",
kdevname(tty->device));
return -ENODEV;
- }
-
+ }
+
MOD_INC_USE_COUNT;
-
- if(scc->tty != NULL)
+
+ if(scc->tty != NULL || scc->stat.is_netdev)
{
scc->tty_opened++;
return 0;
}
+
+ scc->tty = tty;
- scc->tty = tty;
+ if(!scc->init)
+ return 0;
+
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->stat.tx_kiss_state = KISS_IDLE;
+ scc->stat.rx_kiss_state = KISS_IDLE;
init_channel(scc);
return 0;
@@ -2014,10 +2324,11 @@
if (!scc || (scc->magic != SCC_MAGIC))
return;
+
MOD_DEC_USE_COUNT;
- if(scc->tty_opened)
+ if(scc->tty_opened || scc->stat.is_netdev)
{
scc->tty_opened--;
return;
@@ -2028,21 +2339,26 @@
if (!Driver_Initialized)
return;
- save_flags(flags); cli();
+ save_flags(flags);
+ cli();
Outb(scc->ctrl,0); /* Make sure pointer is written */
wr(scc,R1,0); /* disable interrupts */
wr(scc,R3,0);
scc->tty = NULL;
-
+
del_timer(&scc->tx_t);
+ del_timer(&scc->tx_wdog);
del_timer(&scc->rx_t);
- free_buffer_pool(scc);
-
restore_flags(flags);
+ if (!scc->init)
+ return;
+
+ free_buffer_pool(scc);
+
scc->throttled = 0;
tty->stopped = 0;
}
@@ -2068,135 +2384,144 @@
if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */
set_speed(scc);
}
-
+#endif /* CONFIG_SCC_TTY */
/* ----> ioctl-routine of the driver <---- */
-/* perform ioctl on SCC (sdlc) channel
- * this is used for AX.25 mode, and will set the "kiss" parameters
- */
+
+/* sub routines to read/set KISS parameters */
-/* TIOCMGET - get modem status arg: (unsigned long *) arg
- * TIOCMBIS - set PTT arg: ---
- * TIOCMBIC - reset PTT arg: ---
- * TIOCMBIC - set PTT arg: ---
- * TIOCSCCINI - initialize driver arg: ---
- * TIOCCHANINI - initialize channel arg: (struct scc_modem *) arg
- * TIOCGKISS - get level 1 parameter arg: (struct ioctl_command *) arg
- * TIOCSKISS - set level 1 parameter arg: (struct ioctl_command *) arg
- * TIOCSCCSTAT - get driver status arg: (struct scc_stat *) arg
+/* generic ioctl() functions for both TTY or network device mode:
+ *
+ * TIOCSCCCFG - configure driver arg: (struct scc_hw_config *) arg
+ * TIOCSCCINI - initialize driver arg: ---
+ * TIOCSCCCHANINI - initialize channel arg: (struct scc_modem *) arg
+ * TIOCSCCGKISS - get level 1 parameter arg: (struct scc_kiss_cmd *) arg
+ * TIOCSCCSKISS - set level 1 parameter arg: (struct scc_kiss_cmd *) arg
+ * TIOCSCCSTAT - get driver status arg: (struct scc_stat *) arg
*/
-
static int
-scc_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
+scc_ioctl(struct scc_channel *scc, unsigned int cmd, void *arg)
{
- struct scc_channel * scc = tty->driver_data;
- unsigned long flags, r;
- unsigned int result;
- unsigned int value;
- struct ioctl_command kiss_cmd;
+ unsigned long flags;
+ struct scc_kiss_cmd kiss_cmd;
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",
- kdevname(tty->device));
-
- return -ENODEV;
- }
-
- r = NO_SUCH_PARAM;
+ unsigned char device_name[10];
if (!Driver_Initialized)
{
if (cmd == TIOCSCCCFG)
{
int found = 1;
-
+
if (!suser()) return -EPERM;
if (!arg) return -EFAULT;
-
+
+ error = verify_area(VERIFY_READ, arg, sizeof(struct scc_hw_config));
+ if (error) return error;
+
if (Nchips >= MAXSCC)
return -EINVAL;
-
- memcpy_fromfs(&hwcfg, (void *) arg, sizeof(hwcfg));
-
+
+ memcpy_fromfs(&hwcfg, 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", NULL))
- printk("z8530drv: Warning --- could not get IRQ %d\n", hwcfg.irq);
+ printk(KERN_WARNING "z8530drv: warning, cannot 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();
-
+ disable_irq(hwcfg.irq);
+
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 )
+
+ if (InReg(hwcfg.ctrl_a,R13) != 0x55)
found = 0;
-
- restore_flags(flags);
+
+ enable_irq(hwcfg.irq);
#endif
if (found)
{
SCC_Info[2*Nchips ].ctrl = hwcfg.ctrl_a;
SCC_Info[2*Nchips ].data = hwcfg.data_a;
+ SCC_Info[2*Nchips ].irq = hwcfg.irq;
SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b;
SCC_Info[2*Nchips+1].data = hwcfg.data_b;
+ SCC_Info[2*Nchips+1].irq = hwcfg.irq;
- SCC_ctrl[2*Nchips ] = hwcfg.ctrl_a;
- SCC_ctrl[2*Nchips+1] = hwcfg.ctrl_b;
+ SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a;
+ SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b;
+ SCC_ctrl[Nchips].irq = hwcfg.irq;
}
-
+
+
for (chan = 0; chan < 2; chan++)
{
+ sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+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,
+ printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x\n",
+ device_name,
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,
+ printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x -- %s\n",
+ device_name,
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");
+#ifdef CONFIG_SCC_DEV
+ if (Nchips+chan != 0)
+ scc_net_setup(&SCC_Info[2*Nchips+chan], device_name);
+#endif
}
}
if (found) Nchips++;
+#ifdef SCC_DEBUG
+ printk(KERN_INFO "Available modes:"
+#ifdef CONFIG_SCC_DEV
+ " dev"
+#endif
+#ifdef CONFIG_SCC_TTY
+ " tty"
+#endif
+ "\n");
+#endif
+
return 0;
}
@@ -2209,9 +2534,6 @@
return -EINVAL;
z8530_init();
-
- scc->tty=tty;
- alloc_buffer_pool(scc);
return 0;
}
@@ -2220,263 +2542,268 @@
if (!scc->init)
{
- if (cmd == TIOCCHANINI)
+ if (cmd == TIOCSCCCHANINI)
{
- if (!arg)
- return -EFAULT;
-
- if (!suser())
- return -EPERM;
-
- memcpy_fromfs(&scc->modem, (void *) arg, sizeof(struct scc_modem));
+ if (!suser()) return -EPERM;
+ if (!arg) return -EINVAL;
+ error = verify_area(VERIFY_READ, arg, sizeof(struct scc_modem));
+ if (error) return error;
+
+ scc->stat.rxbuffers = RXBUFFERS;
+ scc->stat.txbuffers = TXBUFFERS;
+ scc->stat.bufsize = BUFSIZE;
+
+ memcpy_fromfs(&scc->modem, arg, sizeof(struct scc_modem));
/* default KISS Params */
if (scc->modem.speed < 4800)
{
- scc->kiss.txdelay = 36*TPS/100; /* 360 ms */
- scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */
- scc->kiss.slottime = 16*TPS/100; /* 160 ms */
- scc->kiss.tailtime = 4; /* minimal reasonable value */
- scc->kiss.fulldup = 0; /* CSMA */
- scc->kiss.waittime = 50*TPS/100; /* 500 ms */
- scc->kiss.maxkeyup = 10; /* 10 s */
- scc->kiss.mintime = 3; /* 3 s */
- scc->kiss.idletime = 30; /* 30 s */
- scc->kiss.maxdefer = 120; /* 2 min */
- scc->kiss.not_slip = 1; /* KISS mode */
- scc->kiss.softdcd = 0; /* hardware dcd */
+ scc->kiss.txdelay = 36; /* 360 ms */
+ scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */
+ scc->kiss.slottime = 16; /* 160 ms */
+ scc->kiss.tailtime = 4; /* minimal reasonable value */
+ scc->kiss.fulldup = 0; /* CSMA */
+ scc->kiss.waittime = 50; /* 500 ms */
+ scc->kiss.maxkeyup = 10; /* 10 s */
+ scc->kiss.mintime = 3; /* 3 s */
+ scc->kiss.idletime = 30; /* 30 s */
+ scc->kiss.maxdefer = 120; /* 2 min */
+ scc->kiss.softdcd = 0; /* hardware dcd */
} else {
- scc->kiss.txdelay = 10*TPS/100; /* 100 ms */
- scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */
- scc->kiss.slottime = 8*TPS/100; /* 160 ms */
- scc->kiss.tailtime = 1; /* minimal reasonable value */
- scc->kiss.fulldup = 0; /* CSMA */
- scc->kiss.waittime = 50*TPS/100; /* 500 ms */
- scc->kiss.maxkeyup = 7; /* 7 s */
- scc->kiss.mintime = 3; /* 3 s */
- scc->kiss.idletime = 30; /* 30 s */
- scc->kiss.maxdefer = 120; /* 2 min */
- scc->kiss.not_slip = 1; /* KISS mode */
- scc->kiss.softdcd = 0; /* hardware dcd */
+ scc->kiss.txdelay = 10; /* 100 ms */
+ scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */
+ scc->kiss.slottime = 8; /* 160 ms */
+ scc->kiss.tailtime = 1; /* minimal reasonable value */
+ scc->kiss.fulldup = 0; /* CSMA */
+ scc->kiss.waittime = 50; /* 500 ms */
+ scc->kiss.maxkeyup = 7; /* 7 s */
+ scc->kiss.mintime = 3; /* 3 s */
+ scc->kiss.idletime = 30; /* 30 s */
+ scc->kiss.maxdefer = 120; /* 2 min */
+ scc->kiss.softdcd = 0; /* hardware dcd */
}
- scc->init = 1;
+ scc->init = 1;
return 0;
}
return -EINVAL;
}
-
- switch(cmd){
- case TCSBRK:
- return 0;
- case TIOCMGET:
- error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(unsigned int *));
- if (error)
- return error;
-
- save_flags(flags); cli();
-
- result = ((scc->wreg[R5] & RTS) ? TIOCM_RTS : 0)
- | ((scc->wreg[R5] & DTR) ? TIOCM_DTR : 0)
- | ((InReg(scc->ctrl,R0) & DCD) ? TIOCM_CAR : 0)
- | ((InReg(scc->ctrl,R0) & CTS) ? TIOCM_CTS : 0);
+
+ switch(cmd)
+ {
+ case TIOCSCCSMEM:
+ case TIOCCHANMEM_OLD:
+ if (!suser()) return -EPERM;
+ if (!arg) return -EINVAL;
+ error = verify_area(VERIFY_READ, arg, sizeof(struct scc_mem_config));
+ if (error) return error;
+
+ memcpy_fromfs(&memcfg, 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;
+ if (scc->tty || (scc->dev && (scc->dev->flags & IFF_UP)))
+ alloc_buffer_pool(scc);
- restore_flags(flags);
+ restore_flags(flags);
+ return 0;
+
+ case TIOCSCCGSTAT:
+ case TIOCSCCSTAT_OLD:
+ if (!arg) return -EINVAL;
+ error = verify_area(VERIFY_WRITE, arg, sizeof(struct scc_mem_config));
+ if (error) return error;
- put_user(result,(unsigned int *) arg);
- return 0;
- case TIOCMBIS:
- case TIOCMBIC:
- case TIOCMSET:
- switch (cmd) {
- case TIOCMBIS:
- scc->wreg[R5] |= DTR;
- scc->wreg[R5] |= RTS;
+ memcpy_tofs(arg, &scc->stat, sizeof(struct scc_stat));
+ return 0;
+
+ case TIOCSCCGKISS:
+ case TIOCGKISS_OLD:
+ if (!arg) return -EINVAL;
+ error = verify_area(VERIFY_WRITE, arg, sizeof(struct scc_mem_config));
+ if (error) return error;
+
+ memcpy_fromfs(&kiss_cmd, arg, sizeof(struct scc_kiss_cmd));
+ kiss_cmd.param = kiss_get_param(scc, kiss_cmd.command);
+ memcpy_tofs(arg, &kiss_cmd, sizeof(struct scc_kiss_cmd));
+ return 0;
break;
- case TIOCMBIC:
- scc->wreg[R5] &= ~DTR;
- scc->wreg[R5] &= ~RTS;
+
+ case TIOCSCCSKISS:
+ case TIOCSKISS_OLD:
+ if (!suser()) return -EPERM;
+ if (!arg) return -EINVAL;
+ error = verify_area(VERIFY_READ, arg, sizeof(struct scc_mem_config));
+ if (error) return error;
+
+ memcpy_fromfs(&kiss_cmd, arg, sizeof(struct scc_kiss_cmd));
+ return kiss_set_param(scc, kiss_cmd.command, kiss_cmd.param);
break;
- case TIOCMSET:
- value = get_user((unsigned int *) arg);
-
- if(value & TIOCM_DTR)
- scc->wreg[R5] |= DTR;
- else
- scc->wreg[R5] &= ~DTR;
- if(value & TIOCM_RTS)
- scc->wreg[R5] |= RTS;
- else
- scc->wreg[R5] &= ~RTS;
- break;
- }
-
- save_flags(flags); cli();
+
+ default:
+ return -ENOIOCTLCMD;
- if(scc->stat.tx_state == TXS_IDLE && !Running(t_idle))
- maxk_idle_timeout(scc);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_SCC_TTY
+/* addtional ioctl() for TTY driver mode:
+ *
+ * TIOCMGET - get modem status arg: (unsigned long *) arg
+ * TIOCMBIS - set PTT arg: ---
+ * TIOCMBIC - reset PTT arg: ---
+ * TIOCMBIC - set PTT arg: ---
+ */
+
+static int
+scc_tty_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
+{
+ struct scc_channel * scc = tty->driver_data;
+ unsigned long flags;
+ unsigned int result;
+ unsigned int value;
+ int error;
+
+ if (scc->magic != SCC_MAGIC)
+ {
+ printk(KERN_ALERT "z8530drv: scc_ioctl() found bad magic number for device %s",
+ SCC_DEVNAME);
- restore_flags(flags);
-
- return 0;
-
- case TCGETS:
- error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct termios));
- if (error)
- return error;
- if (!arg)
- return -EFAULT;
+ return -ENODEV;
+ }
+
+ if (!Driver_Initialized && cmd == TIOCSCCINI)
+ scc->tty=tty;
+
+ switch(cmd)
+ {
+ case TIOCSCCCFG:
+ case TIOCSCCINI:
+ case TIOCSCCCHANINI:
+ case TIOCSCCSMEM:
+ case TIOCSCCSKISS:
+ case TIOCSCCGKISS:
+
+ case TIOCSCCCFG_OLD:
+ case TIOCSCCINI_OLD:
+ case TIOCCHANINI_OLD:
+ case TIOCCHANMEM_OLD:
+ case TIOCSKISS_OLD:
+ case TIOCGKISS_OLD:
+ case TIOCSCCSTAT_OLD:
+ return scc_ioctl(scc, cmd, (void *) arg);
+
+ case TCSBRK:
+ return 0;
- memcpy_tofs((void *) arg, scc->tty->termios, sizeof(struct termios));
- return 0;
-
- case TCSETS:
- case TCSETSF: /* should flush first, but... */
- case TCSETSW: /* should wait 'till flush, but... */
- if (!arg)
- return -EFAULT;
-
- memcpy_fromfs(scc->tty->termios, (void *) arg, sizeof(struct termios));
- scc_change_speed(scc);
- return 0;
+ case TIOCMGET:
+ error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(unsigned int *));
+ if (error)
+ return error;
+
+ save_flags(flags);
+ cli();
+
+ result = ((scc->wreg[R5] & RTS) ? TIOCM_RTS : 0)
+ | ((scc->wreg[R5] & DTR) ? TIOCM_DTR : 0)
+ | ((InReg(scc->ctrl,R0) & DCD) ? TIOCM_CAR : 0)
+ | ((InReg(scc->ctrl,R0) & CTS) ? TIOCM_CTS : 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));
- if (error)
- return error;
-
- if (!arg)
- return -EFAULT;
+ restore_flags(flags);
- memcpy_tofs((void *) arg, &scc->stat, sizeof(struct scc_stat));
- return 0;
-
-#define TICKS (100/TPS)
-#define CAST(x) (unsigned long)(x)
-#define Val kiss_cmd.param
-#define VAL kiss_cmd.param*TPS/100
-#define SVAL kiss_cmd.param? kiss_cmd.param:TIMER_STOPPED
-
- case TIOCGKISS:
- error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(struct ioctl_command));
- if (error)
- return error;
-
- if (!arg)
- return -EFAULT;
-
- memcpy_fromfs(&kiss_cmd, (void *) arg, sizeof(struct ioctl_command));
-
- switch (kiss_cmd.command)
- {
- case PARAM_TXDELAY: r = CAST(scc->kiss.txdelay*TICKS); break;
- case PARAM_PERSIST: r = CAST(scc->kiss.persist); break;
- case PARAM_SLOTTIME: r = CAST(scc->kiss.slottime*TICKS); break;
- case PARAM_TXTAIL: r = CAST(scc->kiss.tailtime*TICKS); break;
- case PARAM_FULLDUP: r = CAST(scc->kiss.fulldup); break;
- case PARAM_SOFTDCD: r = CAST(scc->kiss.softdcd); break;
- case PARAM_DTR: r = CAST((scc->wreg[R5] & DTR)? 1:0); break;
- case PARAM_RTS: r = CAST((scc->wreg[R5] & RTS)? 1:0); break;
- case PARAM_SPEED: r = CAST(scc->modem.speed); break;
- case PARAM_GROUP: r = CAST(scc->kiss.group); break;
- case PARAM_IDLE: r = CAST(scc->kiss.idletime); break;
- case PARAM_MIN: r = CAST(scc->kiss.mintime); break;
- case PARAM_MAXKEY: r = CAST(scc->kiss.maxkeyup); break;
- case PARAM_WAIT: r = CAST(scc->kiss.waittime); break;
- case PARAM_MAXDEFER: r = CAST(scc->kiss.maxdefer); break;
- case PARAM_TX: r = CAST(scc->kiss.tx_inhibit); break;
- case PARAM_SLIP: r = CAST(!scc->kiss.not_slip); break;
- default: r = NO_SUCH_PARAM;
- }
-
- kiss_cmd.param = r;
+ put_user(result,(unsigned int *) arg);
+ return 0;
+
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ switch (cmd)
+ {
+ case TIOCMBIS:
+ scc->wreg[R5] |= DTR;
+ scc->wreg[R5] |= RTS;
+ break;
+ case TIOCMBIC:
+ scc->wreg[R5] &= ~DTR;
+ scc->wreg[R5] &= ~RTS;
+ break;
+ case TIOCMSET:
+ error = verify_area(VERIFY_READ, (void *) arg,sizeof(unsigned int *));
+ if (error)
+ return error;
+ value = get_user((unsigned int *) arg);
+
+ if(value & TIOCM_DTR)
+ scc->wreg[R5] |= DTR;
+ else
+ scc->wreg[R5] &= ~DTR;
+ if(value & TIOCM_RTS)
+ scc->wreg[R5] |= RTS;
+ else
+ scc->wreg[R5] &= ~RTS;
+ break;
+ }
+
+ OutReg(scc->ctrl, R5, scc->wreg[R5]);
+ return 0;
- memcpy_tofs((void *) arg, &kiss_cmd, sizeof(struct ioctl_command));
- return 0;
- break;
+ case TCGETS:
+ error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct termios));
+ if (error)
+ return error;
+ if (!arg)
+ return -EFAULT;
+
+ memcpy_tofs((void *) arg, scc->tty->termios, sizeof(struct termios));
+ return 0;
- case TIOCSKISS:
- if (!arg)
- return -EFAULT;
-
- memcpy_fromfs(&kiss_cmd, (void *) arg, sizeof(struct ioctl_command));
-
- switch (kiss_cmd.command)
- {
- case PARAM_TXDELAY: scc->kiss.txdelay=VAL; break;
- case PARAM_PERSIST: scc->kiss.persist=Val; break;
- case PARAM_SLOTTIME: scc->kiss.slottime=VAL; break;
- case PARAM_TXTAIL: scc->kiss.tailtime=VAL; break;
- case PARAM_FULLDUP: scc->kiss.fulldup=Val; break;
- case PARAM_SOFTDCD: scc->kiss.softdcd=Val; break;
- case PARAM_DTR: break; /* does someone need this? */
- case PARAM_RTS: break; /* or this? */
- case PARAM_SPEED: scc->modem.speed=Val; break;
- case PARAM_GROUP: scc->kiss.group=Val; break;
- case PARAM_IDLE: scc->kiss.idletime=Val; break;
- case PARAM_MIN: scc->kiss.mintime=SVAL; break;
- case PARAM_MAXKEY: scc->kiss.maxkeyup=SVAL; break;
- case PARAM_WAIT: scc->kiss.waittime=Val; break;
- case PARAM_MAXDEFER: scc->kiss.maxdefer=SVAL; break;
- case PARAM_TX: scc->kiss.tx_inhibit=Val; break;
- case PARAM_SLIP: scc->kiss.not_slip=!Val; break;
- default: return -ENOIOCTLCMD;
- }
+ case TCSETS:
+ case TCSETSF: /* should flush first, but... */
+ case TCSETSW: /* should wait 'till flush, but... */
+ if (!arg)
+ return -EFAULT;
- return 0;
- break;
-#undef TICKS
-#undef CAST
-#undef VAL
-#undef SVAL
-#undef Val
+ error = verify_area(VERIFY_READ, (void *) arg, sizeof(struct termios));
+ if (error)
+ return error;
+ memcpy_fromfs(scc->tty->termios, (void *) arg, sizeof(struct termios));
+ scc_change_speed(scc);
+ return 0;
- default:
- return -ENOIOCTLCMD;
- }
+ }
+
+ return -ENOIOCTLCMD;
}
/* ----> 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)
+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 *p;
- unsigned long flags;
int cnt, cnt2;
- if (!tty) return count;
+ if (!tty)
+ return -EINVAL;
if (scc_paranoia_check(scc, tty->device, "scc_write"))
- return 0;
+ return -EINVAL;
if (scc->kiss.tx_inhibit) return count;
-
- save_flags(flags); cli();
-
+
cnt2 = count;
while (cnt2)
@@ -2493,8 +2820,8 @@
else
memcpy(scc_wbuf, buf, cnt);
- /* Strange thing. The timeout of the slip driver is */
- /* very small, thus we'll wake him up now. */
+ /* The timeout of the slip driver is very small, */
+ /* thus we'll wake him up now. */
if (cnt2 == 0)
{
@@ -2510,34 +2837,28 @@
while(cnt--)
if (kiss_decode(scc, *p++))
- {
- scc->stat.nospace++;
- restore_flags(flags);
- return 0;
- }
+ return -ENOMEM;
} /* while cnt2 */
- restore_flags(flags);
-
return count;
}
/* put a single char into the buffer */
-static void scc_put_char(struct tty_struct * tty, unsigned char ch)
+static void
+scc_put_char(struct tty_struct * tty, unsigned char ch)
{
struct scc_channel *scc = tty->driver_data;
- unsigned char ch2;
if (scc_paranoia_check(scc, tty->device, "scc_put_char"))
return;
-
- ch2 = ch;
- scc_write(tty, 0, &ch2, 1); /* that's all */
+
+ kiss_decode(scc, ch);
}
-static void scc_flush_chars(struct tty_struct * tty)
+static void
+scc_flush_chars(struct tty_struct * tty)
{
struct scc_channel *scc = tty->driver_data;
@@ -2547,7 +2868,8 @@
}
-static int scc_write_room(struct tty_struct *tty)
+static int
+scc_write_room(struct tty_struct *tty)
{
struct scc_channel *scc = tty->driver_data;
@@ -2557,7 +2879,8 @@
return BUFSIZE;
}
-static int scc_chars_in_buffer(struct tty_struct *tty)
+static int
+scc_chars_in_buffer(struct tty_struct *tty)
{
struct scc_channel *scc = tty->driver_data;
@@ -2567,7 +2890,8 @@
return 0;
}
-static void scc_flush_buffer(struct tty_struct *tty)
+static void
+scc_flush_buffer(struct tty_struct *tty)
{
struct scc_channel *scc = tty->driver_data;
@@ -2579,41 +2903,43 @@
(tty->ldisc.write_wakeup)(tty);
}
-static void scc_throttle(struct tty_struct *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;
-#ifdef DEBUG
- printk("scc: scc_throttle() called for device %d\n", MINOR(tty->device));
+#ifdef SCC_DEBUG
+ printk(KERN_DEBUG "z8530drv: 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;
+ scc->rx_t.expires = jiffies + HZ/25;
add_timer(&scc->rx_t);
}
-static void scc_unthrottle(struct tty_struct *tty)
+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;
-#ifdef DEBUG
- printk("scc: scc_unthrottle() called for device %d\n", MINOR(tty->device));
+#ifdef SCC_DEBUG
+ printk(KERN_DEBUG "z8350drv: 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)
+static void
+scc_start(struct tty_struct *tty)
{
struct scc_channel *scc = tty->driver_data;
@@ -2623,7 +2949,8 @@
scc_unthrottle(tty);
}
-static void scc_stop(struct tty_struct *tty)
+static void
+scc_stop(struct tty_struct *tty)
{
struct scc_channel *scc = tty->driver_data;
@@ -2657,20 +2984,521 @@
scc_change_speed(scc);
}
+#endif /* CONFIG_SCC_TTY */
+
+#ifdef CONFIG_SCC_DEV
+/* ******************************************************************** */
+/* * Network driver routines * */
+/* ******************************************************************** */
+
+static unsigned char ax25_bcast[AX25_ADDR_LEN] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static unsigned char ax25_nocall[AX25_ADDR_LEN] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+static int
+scc_net_init(struct device *dev)
+{
+ return 0; /* dummy */
+}
+
+/* ----> open network device <---- */
+
+static int
+scc_net_open(struct device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+
+ if (scc == NULL || scc->magic != SCC_MAGIC)
+ return -ENODEV;
+
+ if (scc->tty != NULL)
+ return -EBUSY;
+
+ if (!scc->init)
+ return -EINVAL;
+
+ scc->stat.is_netdev = 1;
+
+ MOD_INC_USE_COUNT;
+
+ alloc_buffer_pool(scc);
+
+ scc->stat.tx_kiss_state = KISS_IDLE; /* unused */
+ scc->stat.rx_kiss_state = KISS_IDLE; /* unused */
+
+ init_channel(scc);
+
+ dev->tbusy = 0;
+ dev->start = 1;
+
+ return 0;
+}
+
+/* ----> close network device <---- */
+
+static int
+scc_net_close(struct device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ unsigned long flags;
+
+ if (scc == NULL || scc->magic != SCC_MAGIC)
+ return -ENODEV;
+
+ if (!scc->stat.is_netdev)
+ return -ENXIO;
+
+ MOD_DEC_USE_COUNT;
+
+ scc->stat.is_netdev = 0;
+
+ save_flags(flags);
+ cli();
+
+ Outb(scc->ctrl,0); /* Make sure pointer is written */
+ wr(scc,R1,0); /* disable interrupts */
+ wr(scc,R3,0);
+
+ del_timer(&scc->tx_t);
+ del_timer(&scc->tx_wdog);
+#ifdef CONFIG_SCC_TTY
+ del_timer(&scc->rx_t);
+#endif
+ restore_flags(flags);
+
+ free_buffer_pool(scc);
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ return 0;
+}
+
+/* ----> receive frame, called from scc_rxint() <---- */
+
+static void
+scc_net_rx(struct scc_channel *scc, struct mbuf *bp)
+{
+ struct sk_buff *skb;
+
+ if ( bp == NULL || bp->cnt == 0 ||
+ scc == NULL || scc->magic != SCC_MAGIC)
+ return;
+
+ skb = dev_alloc_skb(bp->cnt+1);
+
+ if (skb == NULL)
+ {
+ scc->dev_stat.rx_dropped++;
+ return;
+ }
+
+ scc->dev_stat.rx_packets++;
+
+ skb->dev = scc->dev;
+ skb->protocol = htons(ETH_P_AX25);
+
+ memcpy(skb_put(skb, bp->cnt), bp->data, bp->cnt);
+ skb->mac.raw = skb->data;
+ netif_rx(skb);
+ return;
+}
+
+/* ----> transmit frame <---- */
+
+static int
+scc_net_tx(struct sk_buff *skb, struct device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ struct mbuf *bp;
+ long ticks;
+
+ /*
+ * We have our own queues, dev->tbusy is set only by t_maxkeyup
+ * to avoid running the driver into maxkeup again and again.
+ *
+ * Hope I havn't outsmart me _again_ ;-)
+ */
+
+ if (dev->tbusy)
+ {
+ ticks = (signed) (jiffies - dev->trans_start);
+
+ if (ticks < scc->kiss.maxdefer*HZ || scc == NULL)
+ return 1;
+
+ /*
+ * Arrgh... Seems to be a _very_ busy channel.
+ * throw away transmission queue.
+ */
+
+ del_timer(&scc->tx_wdog);
+ t_busy((unsigned long) scc);
+
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+
+ if (skb == NULL)
+ {
+ dev_tint(dev);
+ return 0;
+ }
+
+ if (scc == NULL || scc->magic != SCC_MAGIC)
+ {
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
+ }
+
+ scc->dev_stat.tx_packets++;
+ bp = scc_get_buffer(scc, BT_TRANSMIT);
+
+ if (bp == NULLBUF || skb->len > scc->stat.bufsize || skb->len == 0)
+ {
+ scc->dev_stat.tx_dropped++;
+ if (bp) scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
+ }
+
+ memcpy(bp->data, skb->data, skb->len);
+ bp->cnt = skb->len;
+
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ scc->kiss_decode_bp = bp;
+ kiss_interpret_frame(scc);
+ dev->trans_start = jiffies;
+ return 0;
+}
+
+/* ----> set interface callsign <---- */
+
+static int
+scc_net_set_mac_address(struct device *dev, void *addr)
+{
+ struct sockaddr *sa = (struct sockaddr *) addr;
+ memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+ return 0;
+}
+
+/* ----> rebuild header <---- */
+
+static int
+scc_net_rebuild_header(void *buff, struct device *dev, unsigned long raddr, struct sk_buff *skb)
+{
+ return ax25_rebuild_header(buff, dev, raddr, skb);
+}
+
+/* ----> "hard" header <---- */
+
+static int
+scc_net_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+ void *daddr, void *saddr, unsigned len)
+{
+ return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
+}
+
+/* ----> get statistics <---- */
+
+static struct enet_statistics *
+scc_net_get_stats(struct device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+
+ if (scc == NULL || scc->magic != SCC_MAGIC)
+ return NULL;
+
+ scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over;
+ scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under;
+ scc->dev_stat.rx_fifo_errors = scc->stat.rx_over;
+ scc->dev_stat.tx_fifo_errors = scc->stat.tx_under;
+
+ return &scc->dev_stat;
+}
+
+/* ----> ioctl for network devices <---- */
+
+static int
+scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ unsigned int ncmd = 0;
+ int res;
+
+ if (scc == NULL || scc->magic != SCC_MAGIC)
+ return -EINVAL;
+
+ if (!Driver_Initialized && cmd == SIOCSCCINI)
+ {
+ scc->dev = dev;
+ scc->stat.is_netdev = 1;
+ }
+
+ switch (cmd)
+ {
+ case SIOCSCCRESERVED: return -EINVAL; /* unused */
+ case SIOCSCCCFG: ncmd = TIOCSCCCFG; break;
+ case SIOCSCCINI: ncmd = TIOCSCCINI; break;
+ case SIOCSCCCHANINI: ncmd = TIOCSCCCHANINI; break;
+ case SIOCSCCSMEM: ncmd = TIOCSCCSMEM; break;
+ case SIOCSCCGKISS: ncmd = TIOCSCCGKISS; break;
+ case SIOCSCCSKISS: ncmd = TIOCSCCSKISS; break;
+ case SIOCSCCGSTAT: ncmd = TIOCSCCGSTAT; break;
+ default: return -EINVAL;
+ }
+
+ res = scc_ioctl(scc, ncmd, (void *) ifr->ifr_data);
+
+#ifdef CONFIG_SCC_TTY
+ if (!(dev->flags & IFF_UP))
+ scc->stat.is_netdev = 0;
+#endif
+
+ return res;
+
+}
+
+
+/* ----> initialize interface <---- */
+
+static int
+scc_net_setup(struct scc_channel *scc, unsigned char *name)
+{
+ int k;
+ unsigned char *buf;
+ struct device *dev;
+
+ if (dev_get(name) != NULL)
+ {
+ printk(KERN_INFO "Z8530drv: device %s already exists.\n", name);
+ return -EEXIST;
+ }
+
+ if ((scc->dev = (struct device *) kmalloc(sizeof(struct device), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+#ifndef CONFIG_SCC_TTY
+ scc->stat.is_netdev = 1;
+#endif
+ dev = scc->dev;
+ memset(dev, 0, sizeof(struct device));
+
+ buf = (unsigned char *) kmalloc(10, GFP_KERNEL);
+ strcpy(buf, name);
+
+ dev->priv = (void *) scc;
+ dev->name = buf;
+ dev->init = scc_net_init;
+
+ if (register_netdev(dev) != 0)
+ {
+ kfree(dev);
+ return -EIO;
+ }
+
+ for (k=0; k < DEV_NUMBUFFS; k++)
+ skb_queue_head_init(&dev->buffs[k]);
+
+ dev->open = scc_net_open;
+ dev->stop = scc_net_close;
+
+ dev->hard_start_xmit = scc_net_tx;
+ dev->hard_header = scc_net_header;
+ dev->rebuild_header = scc_net_rebuild_header;
+ dev->set_mac_address = scc_net_set_mac_address;
+ dev->get_stats = scc_net_get_stats;
+ dev->do_ioctl = scc_net_ioctl;
+
+ memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+ memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN);
+
+ dev->flags = 0;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = 4;
+
+ dev->type = ARPHRD_AX25;
+ dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+ dev->mtu = AX25_DEF_PACLEN;
+ dev->addr_len = AX25_ADDR_LEN;
+
+ return 0;
+}
+#endif /* CONFIG_SCC_DEV */
+
+/* ******************************************************************** */
+/* * dump statistics to /proc/net/z8530drv * */
+/* ******************************************************************** */
+
+
+static int
+scc_net_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ struct scc_channel *scc;
+ char *txstate, *clksrc, *brand;
+ struct scc_kiss *kiss;
+ struct scc_stat *stat;
+ int len = 0;
+ off_t pos = 0;
+ off_t begin = 0;
+ int k;
+
+ if (!Driver_Initialized)
+ {
+ len += sprintf(buffer, "Driver not initialized.\n");
+ goto done;
+ }
+
+ if (!Nchips)
+ {
+ len += sprintf(buffer, "Z8530 chips not found\n");
+ goto done;
+ }
+
+ for (k = 0; k < Nchips*2; k++)
+ {
+ scc = &SCC_Info[k];
+ stat = &scc->stat;
+ kiss = &scc->kiss;
+
+ if (!scc->init)
+ continue;
+
+ switch(stat->tx_state)
+ {
+ case TXS_IDLE: txstate = "idle"; break;
+ case TXS_BUSY: txstate = "busy"; break;
+ case TXS_ACTIVE: txstate = "active"; break;
+ case TXS_NEWFRAME: txstate = "new"; break;
+ case TXS_IDLE2: txstate = "keyed"; break;
+ case TXS_WAIT: txstate = "wait"; break;
+ case TXS_TIMEOUT: txstate = "timeout"; break;
+ default: txstate = "???";
+ }
+ switch(scc->modem.clocksrc)
+ {
+ case CLK_DPLL: clksrc = "dpll"; break;
+ case CLK_EXTERNAL: clksrc = "ext"; break;
+ case CLK_DIVIDER: clksrc = "div"; break;
+ default: clksrc = "???";
+ }
+ switch(scc->brand)
+ {
+ case PA0HZP: brand="pa0hzp"; break;
+ case EAGLE: brand="eagle"; break;
+ case PC100: brand="pc1100"; break;
+ case PRIMUS: brand="primus"; break;
+ case DRSI: brand="drsi"; break;
+ case BAYCOM: brand="baycom"; break;
+ default: brand="???";
+ }
+
+ len += sprintf(buffer+len, "Device : %s%d\n", SCC_DriverName, k);
+ len += sprintf(buffer+len, "Hardware : %3.3x %3.3x %d %lu %s %s %3.3x %3.3x %d\n",
+ scc->data, scc->ctrl, scc->irq, scc->clock, brand,
+ scc->enhanced? "enh":"norm", Vector_Latch, scc->special,
+ scc->option);
+ len += sprintf(buffer+len, "Received : %lu %lu %d\n",
+ stat->rxframes, stat->rxerrs, stat->rx_over);
+ len += sprintf(buffer+len, "Transmitted: %lu %lu %d %s\n",
+ stat->txframes, stat->txerrs, stat->tx_under, txstate);
+ len += sprintf(buffer+len, "Interrupts : %lu %lu %lu %lu\n",
+ stat->rxints, stat->txints, stat->exints, stat->spints);
+ len += sprintf(buffer+len, "Buffers : %d %d/%d %d/%d %d\n",
+ stat->bufsize, stat->rx_queued, stat->rxbuffers,
+ stat->tx_queued, stat->txbuffers, stat->nospace);
+ len += sprintf(buffer+len, "MODEM : %lu %s %s %s\n",
+ scc->modem.speed, scc->modem.nrz? "nrz":"nrzi",
+ clksrc, kiss->softdcd? "soft":"hard");
+ len += sprintf(buffer+len, "Mode : %s\n",
+ stat->is_netdev? "dev":"tty");
+#define K(x) kiss->x
+ len += sprintf(buffer+len, "KISS params: %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ K(txdelay), K(persist), K(slottime), K(tailtime),
+ K(fulldup), K(waittime), K(mintime), K(maxkeyup),
+ K(idletime), K(maxdefer), K(tx_inhibit), K(group));
+#undef K
+#ifdef SCC_DEBUG
+ {
+ int reg;
+
+ len += sprintf(buffer+len, "Z8530 wregs: ");
+ for (reg = 0; reg < 16; reg++)
+ len += sprintf(buffer+len, "%2.2x ", scc->wreg[reg]);
+ len += sprintf(buffer+len, "\n");
+
+ len += sprintf(buffer+len, "Z8530 rregs: %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1));
+ for (reg = 3; reg < 8; reg++)
+ len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg));
+ len += sprintf(buffer+len, "XX ");
+ for (reg = 9; reg < 16; reg++)
+ len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg));
+ len += sprintf(buffer+len, "\n");
+ }
+#endif
+ len += sprintf(buffer+len, "\n");
+
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ if (pos > offset + length)
+ break;
+ }
+
+done:
+
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+
+ if (len > length) len = length;
+
+ return len;
+}
+
+#ifdef CONFIG_INET
+struct proc_dir_entry scc_proc_dir_entry =
+{
+ PROC_NET_Z8530, 8, "z8530drv", S_IFREG | S_IRUGO, 1, 0, 0, 0,
+ &proc_net_inode_operations, scc_net_get_info
+};
+
+#define scc_net_procfs_init() proc_net_register(&scc_proc_dir_entry);
+#define scc_net_procfs_remove() proc_net_unregister(PROC_NET_Z8530);
+#else
+#define scc_net_procfs_init()
+#define scc_net_procfs_remove()
+#endif
+
+
/* ******************************************************************** */
/* * Init SCC driver * */
/* ******************************************************************** */
-int scc_init (void)
+int scc_init (void)
{
int chip, chan, k;
+#ifdef CONFIG_SCC_DEV
+ char devname[10];
+#endif
+ printk(KERN_INFO BANNER);
+
+#ifdef CONFIG_SCC_TTY
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 = "scc";
+ scc_driver.name = SCC_DriverName;
scc_driver.major = Z8530_MAJOR;
scc_driver.minor_start = 0;
scc_driver.num = MAXSCC*2;
@@ -2699,17 +3527,19 @@
scc_driver.throttle = scc_throttle;
scc_driver.unthrottle = scc_unthrottle;
- scc_driver.ioctl = scc_ioctl;
+ scc_driver.ioctl = scc_tty_ioctl;
scc_driver.set_termios = scc_set_termios;
scc_driver.set_ldisc = scc_set_ldisc;
- printk(BANNER);
if (tty_register_driver(&scc_driver))
{
- printk("Failed to register Z8530 SCC driver\n");
+ printk(KERN_ERR "Failed to register Z8530 SCC driver\n");
return -EIO;
}
+#endif
+
+ memset(&SCC_ctrl, 0, sizeof(SCC_ctrl));
/* pre-init channel information */
@@ -2719,16 +3549,19 @@
memset((char *) &SCC_Info[2*chip+1], 0, sizeof(struct scc_channel));
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;
+#ifdef CONFIG_SCC_DEV
+ sprintf(devname,"%s0", SCC_DriverName);
+
+ if (scc_net_setup(SCC_Info, devname))
+ printk(KERN_WARNING "z8530drv: cannot setup network device\n");
+#endif
+ scc_net_procfs_init();
+
return 0;
}
@@ -2743,9 +3576,9 @@
int result = 0;
result = scc_init();
-
+
if (result == 0)
- printk("Copyright 1993,1995 Joerg Reuter DL1BKE (jreuter@lykos.tng.oche.de)\n");
+ printk(KERN_INFO "Copyright 1993,1996 Joerg Reuter DL1BKE (jreuter@lykos.tng.oche.de)\n");
return result;
}
@@ -2754,19 +3587,26 @@
{
long flags;
io_port ctrl;
- int k, errno;
+ int k;
struct scc_channel *scc;
- save_flags(flags); cli();
- if ( (errno = tty_unregister_driver(&scc_driver)) )
+ save_flags(flags);
+ cli();
+
+#ifdef CONFIG_SCC_TTY
+ if ( (k = tty_unregister_driver(&scc_driver)) )
{
- printk("Failed to unregister Z8530 SCC driver (%d)", -errno);
+ printk(KERN_ERR "z8530drv: failed to unregister tty driver: (%d)\n", -k);
restore_flags(flags);
return;
}
-
+#endif
+
+ if (Nchips == 0)
+ unregister_netdev(SCC_Info[0].dev);
+
for (k = 0; k < Nchips; k++)
- if ( (ctrl = SCC_ctrl[k*2]) )
+ if ( (ctrl = SCC_ctrl[k].chan_A) )
{
Outb(ctrl, 0);
OutReg(ctrl,R9,FHWRES); /* force hardware reset */
@@ -2780,6 +3620,11 @@
{
release_region(scc->ctrl, 1);
release_region(scc->data, 1);
+ if (scc->dev)
+ {
+ unregister_netdev(scc->dev);
+ kfree(scc->dev);
+ }
}
}
@@ -2787,5 +3632,7 @@
if (Ivec[k].used) free_irq(k, NULL);
restore_flags(flags);
+
+ scc_net_procfs_remove();
}
#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov