Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c
new file mode 100644
index 0000000..067b353
--- /dev/null
+++ b/drivers/net/hamradio/6pack.c
@@ -0,0 +1,1051 @@
+/*
+ * 6pack.c	This module implements the 6pack protocol for kernel-based
+ *		devices like TTY. It interfaces between a raw TTY and the
+ *		kernel's AX.25 protocol layers.
+ *
+ * Authors:	Andreas Könsgen <ajk@iehk.rwth-aachen.de>
+ *              Ralf Baechle DL5RB <ralf@linux-mips.org>
+ *
+ * Quite a lot of stuff "stolen" by Joerg Reuter from slip.c, written by
+ *
+ *		Laurence Culhane, <loz@holmes.demon.co.uk>
+ *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/bitops.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/timer.h>
+#include <net/ax25.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/spinlock.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <asm/semaphore.h>
+#include <asm/atomic.h>
+
+#define SIXPACK_VERSION    "Revision: 0.3.0"
+
+/* sixpack priority commands */
+#define SIXP_SEOF		0x40	/* start and end of a 6pack frame */
+#define SIXP_TX_URUN		0x48	/* transmit overrun */
+#define SIXP_RX_ORUN		0x50	/* receive overrun */
+#define SIXP_RX_BUF_OVL		0x58	/* receive buffer overflow */
+
+#define SIXP_CHKSUM		0xFF	/* valid checksum of a 6pack frame */
+
+/* masks to get certain bits out of the status bytes sent by the TNC */
+
+#define SIXP_CMD_MASK		0xC0
+#define SIXP_CHN_MASK		0x07
+#define SIXP_PRIO_CMD_MASK	0x80
+#define SIXP_STD_CMD_MASK	0x40
+#define SIXP_PRIO_DATA_MASK	0x38
+#define SIXP_TX_MASK		0x20
+#define SIXP_RX_MASK		0x10
+#define SIXP_RX_DCD_MASK	0x18
+#define SIXP_LEDS_ON		0x78
+#define SIXP_LEDS_OFF		0x60
+#define SIXP_CON		0x08
+#define SIXP_STA		0x10
+
+#define SIXP_FOUND_TNC		0xe9
+#define SIXP_CON_ON		0x68
+#define SIXP_DCD_MASK		0x08
+#define SIXP_DAMA_OFF		0
+
+/* default level 2 parameters */
+#define SIXP_TXDELAY			(HZ/4)	/* in 1 s */
+#define SIXP_PERSIST			50	/* in 256ths */
+#define SIXP_SLOTTIME			(HZ/10)	/* in 1 s */
+#define SIXP_INIT_RESYNC_TIMEOUT	(3*HZ/2) /* in 1 s */
+#define SIXP_RESYNC_TIMEOUT		5*HZ	/* in 1 s */
+
+/* 6pack configuration. */
+#define SIXP_NRUNIT			31      /* MAX number of 6pack channels */
+#define SIXP_MTU			256	/* Default MTU */
+
+enum sixpack_flags {
+	SIXPF_ERROR,	/* Parity, etc. error	*/
+};
+
+struct sixpack {
+	/* Various fields. */
+	struct tty_struct	*tty;		/* ptr to TTY structure	*/
+	struct net_device	*dev;		/* easy for intr handling  */
+
+	/* These are pointers to the malloc()ed frame buffers. */
+	unsigned char		*rbuff;		/* receiver buffer	*/
+	int			rcount;         /* received chars counter  */
+	unsigned char		*xbuff;		/* transmitter buffer	*/
+	unsigned char		*xhead;         /* next byte to XMIT */
+	int			xleft;          /* bytes left in XMIT queue  */
+
+	unsigned char		raw_buf[4];
+	unsigned char		cooked_buf[400];
+
+	unsigned int		rx_count;
+	unsigned int		rx_count_cooked;
+
+	/* 6pack interface statistics. */
+	struct net_device_stats stats;
+
+	int			mtu;		/* Our mtu (to spot changes!) */
+	int			buffsize;       /* Max buffers sizes */
+
+	unsigned long		flags;		/* Flag values/ mode etc */
+	unsigned char		mode;		/* 6pack mode */
+
+	/* 6pack stuff */
+	unsigned char		tx_delay;
+	unsigned char		persistence;
+	unsigned char		slottime;
+	unsigned char		duplex;
+	unsigned char		led_state;
+	unsigned char		status;
+	unsigned char		status1;
+	unsigned char		status2;
+	unsigned char		tx_enable;
+	unsigned char		tnc_state;
+
+	struct timer_list	tx_t;
+	struct timer_list	resync_t;
+	atomic_t		refcnt;
+	struct semaphore	dead_sem;
+	spinlock_t		lock;
+};
+
+#define AX25_6PACK_HEADER_LEN 0
+
+static void sp_start_tx_timer(struct sixpack *);
+static void sixpack_decode(struct sixpack *, unsigned char[], int);
+static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned char);
+
+/*
+ * perform the persistence/slottime algorithm for CSMA access. If the
+ * persistence check was successful, write the data to the serial driver.
+ * Note that in case of DAMA operation, the data is not sent here.
+ */
+
+static void sp_xmit_on_air(unsigned long channel)
+{
+	struct sixpack *sp = (struct sixpack *) channel;
+	int actual;
+	static unsigned char random;
+
+	random = random * 17 + 41;
+
+	if (((sp->status1 & SIXP_DCD_MASK) == 0) && (random < sp->persistence)) {
+		sp->led_state = 0x70;
+		sp->tty->driver->write(sp->tty, &sp->led_state, 1);
+		sp->tx_enable = 1;
+		actual = sp->tty->driver->write(sp->tty, sp->xbuff, sp->status2);
+		sp->xleft -= actual;
+		sp->xhead += actual;
+		sp->led_state = 0x60;
+		sp->tty->driver->write(sp->tty, &sp->led_state, 1);
+		sp->status2 = 0;
+	} else
+		sp_start_tx_timer(sp);
+}
+
+/* ----> 6pack timer interrupt handler and friends. <---- */
+static void sp_start_tx_timer(struct sixpack *sp)
+{
+	int when = sp->slottime;
+
+	del_timer(&sp->tx_t);
+	sp->tx_t.data = (unsigned long) sp;
+	sp->tx_t.function = sp_xmit_on_air;
+	sp->tx_t.expires = jiffies + ((when + 1) * HZ) / 100;
+	add_timer(&sp->tx_t);
+}
+
+/* Encapsulate one AX.25 frame and stuff into a TTY queue. */
+static void sp_encaps(struct sixpack *sp, unsigned char *icp, int len)
+{
+	unsigned char *msg, *p = icp;
+	int actual, count;
+
+	if (len > sp->mtu) {	/* sp->mtu = AX25_MTU = max. PACLEN = 256 */
+		msg = "oversized transmit packet!";
+		goto out_drop;
+	}
+
+	if (len > sp->mtu) {	/* sp->mtu = AX25_MTU = max. PACLEN = 256 */
+		msg = "oversized transmit packet!";
+		goto out_drop;
+	}
+
+	if (p[0] > 5) {
+		msg = "invalid KISS command";
+		goto out_drop;
+	}
+
+	if ((p[0] != 0) && (len > 2)) {
+		msg = "KISS control packet too long";
+		goto out_drop;
+	}
+
+	if ((p[0] == 0) && (len < 15)) {
+		msg = "bad AX.25 packet to transmit";
+		goto out_drop;
+	}
+
+	count = encode_sixpack(p, sp->xbuff, len, sp->tx_delay);
+	set_bit(TTY_DO_WRITE_WAKEUP, &sp->tty->flags);
+
+	switch (p[0]) {
+	case 1:	sp->tx_delay = p[1];
+		return;
+	case 2:	sp->persistence = p[1];
+		return;
+	case 3:	sp->slottime = p[1];
+		return;
+	case 4:	/* ignored */
+		return;
+	case 5:	sp->duplex = p[1];
+		return;
+	}
+
+	if (p[0] != 0)
+		return;
+
+	/*
+	 * In case of fullduplex or DAMA operation, we don't take care about the
+	 * state of the DCD or of any timers, as the determination of the
+	 * correct time to send is the job of the AX.25 layer. We send
+	 * immediately after data has arrived.
+	 */
+	if (sp->duplex == 1) {
+		sp->led_state = 0x70;
+		sp->tty->driver->write(sp->tty, &sp->led_state, 1);
+		sp->tx_enable = 1;
+		actual = sp->tty->driver->write(sp->tty, sp->xbuff, count);
+		sp->xleft = count - actual;
+		sp->xhead = sp->xbuff + actual;
+		sp->led_state = 0x60;
+		sp->tty->driver->write(sp->tty, &sp->led_state, 1);
+	} else {
+		sp->xleft = count;
+		sp->xhead = sp->xbuff;
+		sp->status2 = count;
+		if (sp->duplex == 0)
+			sp_start_tx_timer(sp);
+	}
+
+	return;
+
+out_drop:
+	sp->stats.tx_dropped++;
+	netif_start_queue(sp->dev);
+	if (net_ratelimit())
+		printk(KERN_DEBUG "%s: %s - dropped.\n", sp->dev->name, msg);
+}
+
+/* Encapsulate an IP datagram and kick it into a TTY queue. */
+
+static int sp_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct sixpack *sp = netdev_priv(dev);
+
+	spin_lock_bh(&sp->lock);
+	/* We were not busy, so we are now... :-) */
+	netif_stop_queue(dev);
+	sp->stats.tx_bytes += skb->len;
+	sp_encaps(sp, skb->data, skb->len);
+	spin_unlock_bh(&sp->lock);
+
+	dev_kfree_skb(skb);
+
+	return 0;
+}
+
+static int sp_open_dev(struct net_device *dev)
+{
+	struct sixpack *sp = netdev_priv(dev);
+
+	if (sp->tty == NULL)
+		return -ENODEV;
+	return 0;
+}
+
+/* Close the low-level part of the 6pack channel. */
+static int sp_close(struct net_device *dev)
+{
+	struct sixpack *sp = netdev_priv(dev);
+
+	spin_lock_bh(&sp->lock);
+	if (sp->tty) {
+		/* TTY discipline is running. */
+		clear_bit(TTY_DO_WRITE_WAKEUP, &sp->tty->flags);
+	}
+	netif_stop_queue(dev);
+	spin_unlock_bh(&sp->lock);
+
+	return 0;
+}
+
+/* Return the frame type ID */
+static int sp_header(struct sk_buff *skb, struct net_device *dev,
+	unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+#ifdef CONFIG_INET
+	if (type != htons(ETH_P_AX25))
+		return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
+#endif
+	return 0;
+}
+
+static struct net_device_stats *sp_get_stats(struct net_device *dev)
+{
+	struct sixpack *sp = netdev_priv(dev);
+	return &sp->stats;
+}
+
+static int sp_set_mac_address(struct net_device *dev, void *addr)
+{
+	struct sockaddr_ax25 *sa = addr;
+
+	if (sa->sax25_family != AF_AX25)
+		return -EINVAL;
+
+	if (!sa->sax25_ndigis)
+		return -EINVAL;
+
+	spin_lock_irq(&dev->xmit_lock);
+	memcpy(dev->dev_addr, &sa->sax25_call, AX25_ADDR_LEN);
+	spin_unlock_irq(&dev->xmit_lock);
+
+	return 0;
+}
+
+static int sp_rebuild_header(struct sk_buff *skb)
+{
+#ifdef CONFIG_INET
+	return ax25_rebuild_header(skb);
+#else
+	return 0;
+#endif
+}
+
+static void sp_setup(struct net_device *dev)
+{
+	static char ax25_bcast[AX25_ADDR_LEN] =
+		{'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1};
+	static char ax25_test[AX25_ADDR_LEN] =
+		{'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1};
+
+	/* Finish setting up the DEVICE info. */
+	dev->mtu		= SIXP_MTU;
+	dev->hard_start_xmit	= sp_xmit;
+	dev->open		= sp_open_dev;
+	dev->destructor		= free_netdev;
+	dev->stop		= sp_close;
+	dev->hard_header	= sp_header;
+	dev->get_stats	        = sp_get_stats;
+	dev->set_mac_address    = sp_set_mac_address;
+	dev->hard_header_len	= AX25_MAX_HEADER_LEN;
+	dev->addr_len		= AX25_ADDR_LEN;
+	dev->type		= ARPHRD_AX25;
+	dev->tx_queue_len	= 10;
+	dev->rebuild_header	= sp_rebuild_header;
+	dev->tx_timeout		= NULL;
+
+	/* Only activated in AX.25 mode */
+	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+	memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN);
+
+	SET_MODULE_OWNER(dev);
+
+	dev->flags		= 0;
+}
+
+/* Send one completely decapsulated IP datagram to the IP layer. */
+
+/*
+ * This is the routine that sends the received data to the kernel AX.25.
+ * 'cmd' is the KISS command. For AX.25 data, it is zero.
+ */
+
+static void sp_bump(struct sixpack *sp, char cmd)
+{
+	struct sk_buff *skb;
+	int count;
+	unsigned char *ptr;
+
+	count = sp->rcount + 1;
+
+	sp->stats.rx_bytes += count;
+
+	if ((skb = dev_alloc_skb(count)) == NULL)
+		goto out_mem;
+
+	skb->dev = sp->dev;
+	ptr = skb_put(skb, count);
+	*ptr++ = cmd;	/* KISS command */
+
+	memcpy(ptr, sp->cooked_buf + 1, count);
+	skb->mac.raw = skb->data;
+	skb->protocol = htons(ETH_P_AX25);
+	netif_rx(skb);
+	sp->dev->last_rx = jiffies;
+	sp->stats.rx_packets++;
+
+	return;
+
+out_mem:
+	sp->stats.rx_dropped++;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * We have a potential race on dereferencing tty->disc_data, because the tty
+ * layer provides no locking at all - thus one cpu could be running
+ * sixpack_receive_buf while another calls sixpack_close, which zeroes
+ * tty->disc_data and frees the memory that sixpack_receive_buf is using.  The
+ * best way to fix this is to use a rwlock in the tty struct, but for now we
+ * use a single global rwlock for all ttys in ppp line discipline.
+ */
+static DEFINE_RWLOCK(disc_data_lock);
+                                                                                
+static struct sixpack *sp_get(struct tty_struct *tty)
+{
+	struct sixpack *sp;
+
+	read_lock(&disc_data_lock);
+	sp = tty->disc_data;
+	if (sp)
+		atomic_inc(&sp->refcnt);
+	read_unlock(&disc_data_lock);
+
+	return sp;
+}
+
+static void sp_put(struct sixpack *sp)
+{
+	if (atomic_dec_and_test(&sp->refcnt))
+		up(&sp->dead_sem);
+}
+
+/*
+ * Called by the TTY driver when there's room for more data.  If we have
+ * more packets to send, we send them here.
+ */
+static void sixpack_write_wakeup(struct tty_struct *tty)
+{
+	struct sixpack *sp = sp_get(tty);
+	int actual;
+
+	if (!sp)
+		return;
+	if (sp->xleft <= 0)  {
+		/* Now serial buffer is almost free & we can start
+		 * transmission of another packet */
+		sp->stats.tx_packets++;
+		clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+		sp->tx_enable = 0;
+		netif_wake_queue(sp->dev);
+		goto out;
+	}
+
+	if (sp->tx_enable) {
+		actual = tty->driver->write(tty, sp->xhead, sp->xleft);
+		sp->xleft -= actual;
+		sp->xhead += actual;
+	}
+
+out:
+	sp_put(sp);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int sixpack_receive_room(struct tty_struct *tty)
+{
+	return 65536;  /* We can handle an infinite amount of data. :-) */
+}
+
+/*
+ * Handle the 'receiver data ready' interrupt.
+ * This function is called by the 'tty_io' module in the kernel when
+ * a block of 6pack data has been received, which can now be decapsulated
+ * and sent on to some IP layer for further processing.
+ */
+static void sixpack_receive_buf(struct tty_struct *tty,
+	const unsigned char *cp, char *fp, int count)
+{
+	struct sixpack *sp;
+	unsigned char buf[512];
+	int count1;
+
+	if (!count)
+		return;
+
+	sp = sp_get(tty);
+	if (!sp)
+		return;
+
+	memcpy(buf, cp, count < sizeof(buf) ? count : sizeof(buf));
+
+	/* Read the characters out of the buffer */
+
+	count1 = count;
+	while (count) {
+		count--;
+		if (fp && *fp++) {
+			if (!test_and_set_bit(SIXPF_ERROR, &sp->flags))
+				sp->stats.rx_errors++;
+			continue;
+		}
+	}
+	sixpack_decode(sp, buf, count1);
+
+	sp_put(sp);
+	if (test_and_clear_bit(TTY_THROTTLED, &tty->flags)
+	    && tty->driver->unthrottle)
+		tty->driver->unthrottle(tty);
+}
+
+/*
+ * Try to resync the TNC. Called by the resync timer defined in
+ * decode_prio_command
+ */
+
+#define TNC_UNINITIALIZED	0
+#define TNC_UNSYNC_STARTUP	1
+#define TNC_UNSYNCED		2
+#define TNC_IN_SYNC		3
+
+static void __tnc_set_sync_state(struct sixpack *sp, int new_tnc_state)
+{
+	char *msg;
+
+	switch (new_tnc_state) {
+	default:			/* gcc oh piece-o-crap ... */
+	case TNC_UNSYNC_STARTUP:
+		msg = "Synchronizing with TNC";
+		break;
+	case TNC_UNSYNCED:
+		msg = "Lost synchronization with TNC\n";
+		break;
+	case TNC_IN_SYNC:
+		msg = "Found TNC";
+		break;
+	}
+
+	sp->tnc_state = new_tnc_state;
+	printk(KERN_INFO "%s: %s\n", sp->dev->name, msg);
+}
+
+static inline void tnc_set_sync_state(struct sixpack *sp, int new_tnc_state)
+{
+	int old_tnc_state = sp->tnc_state;
+
+	if (old_tnc_state != new_tnc_state)
+		__tnc_set_sync_state(sp, new_tnc_state);
+}
+
+static void resync_tnc(unsigned long channel)
+{
+	struct sixpack *sp = (struct sixpack *) channel;
+	static char resync_cmd = 0xe8;
+
+	/* clear any data that might have been received */
+
+	sp->rx_count = 0;
+	sp->rx_count_cooked = 0;
+
+	/* reset state machine */
+
+	sp->status = 1;
+	sp->status1 = 1;
+	sp->status2 = 0;
+
+	/* resync the TNC */
+
+	sp->led_state = 0x60;
+	sp->tty->driver->write(sp->tty, &sp->led_state, 1);
+	sp->tty->driver->write(sp->tty, &resync_cmd, 1);
+
+
+	/* Start resync timer again -- the TNC might be still absent */
+
+	del_timer(&sp->resync_t);
+	sp->resync_t.data	= (unsigned long) sp;
+	sp->resync_t.function	= resync_tnc;
+	sp->resync_t.expires	= jiffies + SIXP_RESYNC_TIMEOUT;
+	add_timer(&sp->resync_t);
+}
+
+static inline int tnc_init(struct sixpack *sp)
+{
+	unsigned char inbyte = 0xe8;
+
+	tnc_set_sync_state(sp, TNC_UNSYNC_STARTUP);
+
+	sp->tty->driver->write(sp->tty, &inbyte, 1);
+
+	del_timer(&sp->resync_t);
+	sp->resync_t.data = (unsigned long) sp;
+	sp->resync_t.function = resync_tnc;
+	sp->resync_t.expires = jiffies + SIXP_RESYNC_TIMEOUT;
+	add_timer(&sp->resync_t);
+
+	return 0;
+}
+
+/*
+ * Open the high-level part of the 6pack channel.
+ * This function is called by the TTY module when the
+ * 6pack line discipline is called for.  Because we are
+ * sure the tty line exists, we only have to link it to
+ * a free 6pcack channel...
+ */
+static int sixpack_open(struct tty_struct *tty)
+{
+	char *rbuff = NULL, *xbuff = NULL;
+	struct net_device *dev;
+	struct sixpack *sp;
+	unsigned long len;
+	int err = 0;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	dev = alloc_netdev(sizeof(struct sixpack), "sp%d", sp_setup);
+	if (!dev) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	sp = netdev_priv(dev);
+	sp->dev = dev;
+
+	spin_lock_init(&sp->lock);
+	atomic_set(&sp->refcnt, 1);
+	init_MUTEX_LOCKED(&sp->dead_sem);
+
+	/* !!! length of the buffers. MTU is IP MTU, not PACLEN!  */
+
+	len = dev->mtu * 2;
+
+	rbuff = kmalloc(len + 4, GFP_KERNEL);
+	xbuff = kmalloc(len + 4, GFP_KERNEL);
+
+	if (rbuff == NULL || xbuff == NULL) {
+		err = -ENOBUFS;
+		goto out_free;
+	}
+
+	spin_lock_bh(&sp->lock);
+
+	sp->tty = tty;
+
+	sp->rbuff	= rbuff;
+	sp->xbuff	= xbuff;
+
+	sp->mtu		= AX25_MTU + 73;
+	sp->buffsize	= len;
+	sp->rcount	= 0;
+	sp->rx_count	= 0;
+	sp->rx_count_cooked = 0;
+	sp->xleft	= 0;
+
+	sp->flags	= 0;		/* Clear ESCAPE & ERROR flags */
+
+	sp->duplex	= 0;
+	sp->tx_delay    = SIXP_TXDELAY;
+	sp->persistence = SIXP_PERSIST;
+	sp->slottime    = SIXP_SLOTTIME;
+	sp->led_state   = 0x60;
+	sp->status      = 1;
+	sp->status1     = 1;
+	sp->status2     = 0;
+	sp->tx_enable   = 0;
+
+	netif_start_queue(dev);
+
+	init_timer(&sp->tx_t);
+	init_timer(&sp->resync_t);
+
+	spin_unlock_bh(&sp->lock);
+
+	/* Done.  We have linked the TTY line to a channel. */
+	tty->disc_data = sp;
+
+	/* Now we're ready to register. */
+	if (register_netdev(dev))
+		goto out_free;
+
+	tnc_init(sp);
+
+	return 0;
+
+out_free:
+	kfree(xbuff);
+	kfree(rbuff);
+
+	if (dev)
+		free_netdev(dev);
+
+out:
+	return err;
+}
+
+
+/*
+ * Close down a 6pack channel.
+ * This means flushing out any pending queues, and then restoring the
+ * TTY line discipline to what it was before it got hooked to 6pack
+ * (which usually is TTY again).
+ */
+static void sixpack_close(struct tty_struct *tty)
+{
+	struct sixpack *sp;
+
+	write_lock(&disc_data_lock);
+	sp = tty->disc_data;
+	tty->disc_data = NULL;
+	write_unlock(&disc_data_lock);
+	if (sp == 0)
+		return;
+
+	/*
+	 * We have now ensured that nobody can start using ap from now on, but
+	 * we have to wait for all existing users to finish.
+	 */
+	if (!atomic_dec_and_test(&sp->refcnt))
+		down(&sp->dead_sem);
+
+	unregister_netdev(sp->dev);
+
+	del_timer(&sp->tx_t);
+	del_timer(&sp->resync_t);
+
+	/* Free all 6pack frame buffers. */
+	kfree(sp->rbuff);
+	kfree(sp->xbuff);
+}
+
+/* Perform I/O control on an active 6pack channel. */
+static int sixpack_ioctl(struct tty_struct *tty, struct file *file,
+	unsigned int cmd, unsigned long arg)
+{
+	struct sixpack *sp = sp_get(tty);
+	struct net_device *dev = sp->dev;
+	unsigned int tmp, err;
+
+	if (!sp)
+		return -ENXIO;
+
+	switch(cmd) {
+	case SIOCGIFNAME:
+		err = copy_to_user((void __user *) arg, dev->name,
+		                   strlen(dev->name) + 1) ? -EFAULT : 0;
+		break;
+
+	case SIOCGIFENCAP:
+		err = put_user(0, (int __user *) arg);
+		break;
+
+	case SIOCSIFENCAP:
+		if (get_user(tmp, (int __user *) arg)) {
+			err = -EFAULT;
+			break;
+		}
+
+		sp->mode = tmp;
+		dev->addr_len        = AX25_ADDR_LEN;
+		dev->hard_header_len = AX25_KISS_HEADER_LEN +
+		                       AX25_MAX_HEADER_LEN + 3;
+		dev->type            = ARPHRD_AX25;
+
+		err = 0;
+		break;
+
+	 case SIOCSIFHWADDR: {
+		char addr[AX25_ADDR_LEN];
+
+		if (copy_from_user(&addr,
+		                   (void __user *) arg, AX25_ADDR_LEN)) {
+			err = -EFAULT;
+			break;
+		}
+
+		spin_lock_irq(&dev->xmit_lock);
+		memcpy(dev->dev_addr, &addr, AX25_ADDR_LEN);
+		spin_unlock_irq(&dev->xmit_lock);
+
+		err = 0;
+		break;
+	}
+
+	/* Allow stty to read, but not set, the serial port */
+	case TCGETS:
+	case TCGETA:
+		err = n_tty_ioctl(tty, (struct file *) file, cmd, arg);
+		break;
+
+	default:
+		err = -ENOIOCTLCMD;
+	}
+
+	sp_put(sp);
+
+	return err;
+}
+
+static struct tty_ldisc sp_ldisc = {
+	.owner		= THIS_MODULE,
+	.magic		= TTY_LDISC_MAGIC,
+	.name		= "6pack",
+	.open		= sixpack_open,
+	.close		= sixpack_close,
+	.ioctl		= sixpack_ioctl,
+	.receive_buf	= sixpack_receive_buf,
+	.receive_room	= sixpack_receive_room,
+	.write_wakeup	= sixpack_write_wakeup,
+};
+
+/* Initialize 6pack control device -- register 6pack line discipline */
+
+static char msg_banner[]  __initdata = KERN_INFO \
+	"AX.25: 6pack driver, " SIXPACK_VERSION "\n";
+static char msg_regfail[] __initdata = KERN_ERR  \
+	"6pack: can't register line discipline (err = %d)\n";
+
+static int __init sixpack_init_driver(void)
+{
+	int status;
+
+	printk(msg_banner);
+
+	/* Register the provided line protocol discipline */
+	if ((status = tty_register_ldisc(N_6PACK, &sp_ldisc)) != 0)
+		printk(msg_regfail, status);
+
+	return status;
+}
+
+static const char msg_unregfail[] __exitdata = KERN_ERR \
+	"6pack: can't unregister line discipline (err = %d)\n";
+
+static void __exit sixpack_exit_driver(void)
+{
+	int ret;
+
+	if ((ret = tty_register_ldisc(N_6PACK, NULL)))
+		printk(msg_unregfail, ret);
+}
+
+/* encode an AX.25 packet into 6pack */
+
+static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw,
+	int length, unsigned char tx_delay)
+{
+	int count = 0;
+	unsigned char checksum = 0, buf[400];
+	int raw_count = 0;
+
+	tx_buf_raw[raw_count++] = SIXP_PRIO_CMD_MASK | SIXP_TX_MASK;
+	tx_buf_raw[raw_count++] = SIXP_SEOF;
+
+	buf[0] = tx_delay;
+	for (count = 1; count < length; count++)
+		buf[count] = tx_buf[count];
+
+	for (count = 0; count < length; count++)
+		checksum += buf[count];
+	buf[length] = (unsigned char) 0xff - checksum;
+
+	for (count = 0; count <= length; count++) {
+		if ((count % 3) == 0) {
+			tx_buf_raw[raw_count++] = (buf[count] & 0x3f);
+			tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x30);
+		} else if ((count % 3) == 1) {
+			tx_buf_raw[raw_count++] |= (buf[count] & 0x0f);
+			tx_buf_raw[raw_count] =	((buf[count] >> 2) & 0x3c);
+		} else {
+			tx_buf_raw[raw_count++] |= (buf[count] & 0x03);
+			tx_buf_raw[raw_count++] = (buf[count] >> 2);
+		}
+	}
+	if ((length % 3) != 2)
+		raw_count++;
+	tx_buf_raw[raw_count++] = SIXP_SEOF;
+	return raw_count;
+}
+
+/* decode 4 sixpack-encoded bytes into 3 data bytes */
+
+static void decode_data(struct sixpack *sp, unsigned char inbyte)
+{
+	unsigned char *buf;
+
+	if (sp->rx_count != 3) {
+		sp->raw_buf[sp->rx_count++] = inbyte;
+
+		return;
+	}
+
+	buf = sp->raw_buf;
+	sp->cooked_buf[sp->rx_count_cooked++] =
+		buf[0] | ((buf[1] << 2) & 0xc0);
+	sp->cooked_buf[sp->rx_count_cooked++] =
+		(buf[1] & 0x0f) | ((buf[2] << 2) & 0xf0);
+	sp->cooked_buf[sp->rx_count_cooked++] =
+		(buf[2] & 0x03) | (inbyte << 2);
+	sp->rx_count = 0;
+}
+
+/* identify and execute a 6pack priority command byte */
+
+static void decode_prio_command(struct sixpack *sp, unsigned char cmd)
+{
+	unsigned char channel;
+	int actual;
+
+	channel = cmd & SIXP_CHN_MASK;
+	if ((cmd & SIXP_PRIO_DATA_MASK) != 0) {     /* idle ? */
+
+	/* RX and DCD flags can only be set in the same prio command,
+	   if the DCD flag has been set without the RX flag in the previous
+	   prio command. If DCD has not been set before, something in the
+	   transmission has gone wrong. In this case, RX and DCD are
+	   cleared in order to prevent the decode_data routine from
+	   reading further data that might be corrupt. */
+
+		if (((sp->status & SIXP_DCD_MASK) == 0) &&
+			((cmd & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)) {
+				if (sp->status != 1)
+					printk(KERN_DEBUG "6pack: protocol violation\n");
+				else
+					sp->status = 0;
+				cmd &= !SIXP_RX_DCD_MASK;
+		}
+		sp->status = cmd & SIXP_PRIO_DATA_MASK;
+	} else { /* output watchdog char if idle */
+		if ((sp->status2 != 0) && (sp->duplex == 1)) {
+			sp->led_state = 0x70;
+			sp->tty->driver->write(sp->tty, &sp->led_state, 1);
+			sp->tx_enable = 1;
+			actual = sp->tty->driver->write(sp->tty, sp->xbuff, sp->status2);
+			sp->xleft -= actual;
+			sp->xhead += actual;
+			sp->led_state = 0x60;
+			sp->status2 = 0;
+
+		}
+	}
+
+	/* needed to trigger the TNC watchdog */
+	sp->tty->driver->write(sp->tty, &sp->led_state, 1);
+
+        /* if the state byte has been received, the TNC is present,
+           so the resync timer can be reset. */
+
+	if (sp->tnc_state == TNC_IN_SYNC) {
+		del_timer(&sp->resync_t);
+		sp->resync_t.data	= (unsigned long) sp;
+		sp->resync_t.function	= resync_tnc;
+		sp->resync_t.expires	= jiffies + SIXP_INIT_RESYNC_TIMEOUT;
+		add_timer(&sp->resync_t);
+	}
+
+	sp->status1 = cmd & SIXP_PRIO_DATA_MASK;
+}
+
+/* identify and execute a standard 6pack command byte */
+
+static void decode_std_command(struct sixpack *sp, unsigned char cmd)
+{
+	unsigned char checksum = 0, rest = 0, channel;
+	short i;
+
+	channel = cmd & SIXP_CHN_MASK;
+	switch (cmd & SIXP_CMD_MASK) {     /* normal command */
+	case SIXP_SEOF:
+		if ((sp->rx_count == 0) && (sp->rx_count_cooked == 0)) {
+			if ((sp->status & SIXP_RX_DCD_MASK) ==
+				SIXP_RX_DCD_MASK) {
+				sp->led_state = 0x68;
+				sp->tty->driver->write(sp->tty, &sp->led_state, 1);
+			}
+		} else {
+			sp->led_state = 0x60;
+			/* fill trailing bytes with zeroes */
+			sp->tty->driver->write(sp->tty, &sp->led_state, 1);
+			rest = sp->rx_count;
+			if (rest != 0)
+				 for (i = rest; i <= 3; i++)
+					decode_data(sp, 0);
+			if (rest == 2)
+				sp->rx_count_cooked -= 2;
+			else if (rest == 3)
+				sp->rx_count_cooked -= 1;
+			for (i = 0; i < sp->rx_count_cooked; i++)
+				checksum += sp->cooked_buf[i];
+			if (checksum != SIXP_CHKSUM) {
+				printk(KERN_DEBUG "6pack: bad checksum %2.2x\n", checksum);
+			} else {
+				sp->rcount = sp->rx_count_cooked-2;
+				sp_bump(sp, 0);
+			}
+			sp->rx_count_cooked = 0;
+		}
+		break;
+	case SIXP_TX_URUN: printk(KERN_DEBUG "6pack: TX underrun\n");
+		break;
+	case SIXP_RX_ORUN: printk(KERN_DEBUG "6pack: RX overrun\n");
+		break;
+	case SIXP_RX_BUF_OVL:
+		printk(KERN_DEBUG "6pack: RX buffer overflow\n");
+	}
+}
+
+/* decode a 6pack packet */
+
+static void
+sixpack_decode(struct sixpack *sp, unsigned char *pre_rbuff, int count)
+{
+	unsigned char inbyte;
+	int count1;
+
+	for (count1 = 0; count1 < count; count1++) {
+		inbyte = pre_rbuff[count1];
+		if (inbyte == SIXP_FOUND_TNC) {
+			tnc_set_sync_state(sp, TNC_IN_SYNC);
+			del_timer(&sp->resync_t);
+		}
+		if ((inbyte & SIXP_PRIO_CMD_MASK) != 0)
+			decode_prio_command(sp, inbyte);
+		else if ((inbyte & SIXP_STD_CMD_MASK) != 0)
+			decode_std_command(sp, inbyte);
+		else if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)
+			decode_data(sp, inbyte);
+	}
+}
+
+MODULE_AUTHOR("Ralf Baechle DO1GRB <ralf@linux-mips.org>");
+MODULE_DESCRIPTION("6pack driver for AX.25");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_LDISC(N_6PACK);
+
+module_init(sixpack_init_driver);
+module_exit(sixpack_exit_driver);
diff --git a/drivers/net/hamradio/Kconfig b/drivers/net/hamradio/Kconfig
new file mode 100644
index 0000000..34068f8
--- /dev/null
+++ b/drivers/net/hamradio/Kconfig
@@ -0,0 +1,191 @@
+config MKISS
+	tristate "Serial port KISS driver"
+	depends on AX25 && BROKEN_ON_SMP
+	---help---
+	  KISS is a protocol used for the exchange of data between a computer
+	  and a Terminal Node Controller (a small embedded system commonly
+	  used for networking over AX.25 amateur radio connections; it
+	  connects the computer's serial port with the radio's microphone
+	  input and speaker output).
+
+	  Although KISS is less advanced than the 6pack protocol, it has
+	  the advantage that it is already supported by most modern TNCs
+	  without the need for a firmware upgrade.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called mkiss.
+
+config 6PACK
+	tristate "Serial port 6PACK driver"
+	depends on AX25 && BROKEN_ON_SMP
+	---help---
+	  6pack is a transmission protocol for the data exchange between your
+	  PC and your TNC (the Terminal Node Controller acts as a kind of
+	  modem connecting your computer's serial port to your radio's
+	  microphone input and speaker output). This protocol can be used as
+	  an alternative to KISS for networking over AX.25 amateur radio
+	  connections, but it has some extended functionality.
+
+	  Note that this driver is still experimental and might cause
+	  problems. For details about the features and the usage of the
+	  driver, read <file:Documentation/networking/6pack.txt>.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called 6pack.
+
+config BPQETHER
+	tristate "BPQ Ethernet driver"
+	depends on AX25
+	help
+	  AX.25 is the protocol used for computer communication over amateur
+	  radio. If you say Y here, you will be able to send and receive AX.25
+	  traffic over Ethernet (also called "BPQ AX.25"), which could be
+	  useful if some other computer on your local network has a direct
+	  amateur radio connection.
+
+config DMASCC
+	tristate "High-speed (DMA) SCC driver for AX.25"
+	depends on ISA && AX25 && BROKEN_ON_SMP
+	---help---
+	  This is a driver for high-speed SCC boards, i.e. those supporting
+	  DMA on one port. You usually use those boards to connect your
+	  computer to an amateur radio modem (such as the WA4DSY 56kbps
+	  modem), in order to send and receive AX.25 packet radio network
+	  traffic.
+
+	  Currently, this driver supports Ottawa PI/PI2, Paccomm/Gracilis
+	  PackeTwin, and S5SCC/DMA boards. They are detected automatically.
+	  If you have one of these cards, say Y here and read the AX25-HOWTO,
+	  available from <http://www.tldp.org/docs.html#howto>.
+
+	  This driver can operate multiple boards simultaneously. If you
+	  compile it as a module (by saying M instead of Y), it will be called
+	  dmascc. If you don't pass any parameter to the driver, all
+	  possible I/O addresses are probed. This could irritate other devices
+	  that are currently not in use. You may specify the list of addresses
+	  to be probed by "dmascc=addr1,addr2,..." (when compiled into the
+	  kernel image) or "io=addr1,addr2,..." (when loaded as a module). The
+	  network interfaces will be called dmascc0 and dmascc1 for the board
+	  detected first, dmascc2 and dmascc3 for the second one, and so on.
+
+	  Before you configure each interface with ifconfig, you MUST set
+	  certain parameters, such as channel access timing, clock mode, and
+	  DMA channel. This is accomplished with a small utility program,
+	  dmascc_cfg, available at
+	  <http://cacofonix.nt.tuwien.ac.at/~oe1kib/Linux/>. Please be sure to
+	  get at least version 1.27 of dmascc_cfg, as older versions will not
+	  work with the current driver.
+
+config SCC
+	tristate "Z8530 SCC driver"
+	depends on ISA && AX25
+	---help---
+	  These cards are used to connect your Linux box to an amateur radio
+	  in order to communicate with other computers. If you want to use
+	  this, read <file:Documentation/networking/z8530drv.txt> and the
+	  AX25-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>. Also make sure to say Y
+	  to "Amateur Radio AX.25 Level 2" support.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called scc.
+
+config SCC_DELAY
+	bool "additional delay for PA0HZP OptoSCC compatible boards"
+	depends on SCC
+	help
+	  Say Y here if you experience problems with the SCC driver not
+	  working properly; please read
+	  <file:Documentation/networking/z8530drv.txt> for details.
+
+	  If unsure, say N.
+
+config SCC_TRXECHO
+	bool "support for TRX that feedback the tx signal to rx"
+	depends on SCC
+	help
+	  Some transmitters feed the transmitted signal back to the receive
+	  line.  Say Y here to foil this by explicitly disabling the receiver
+	  during data transmission.
+
+	  If in doubt, say Y.
+
+config BAYCOM_SER_FDX
+	tristate "BAYCOM ser12 fullduplex driver for AX.25"
+	depends on AX25
+	select CRC_CCITT
+	---help---
+	  This is one of two drivers for Baycom style simple amateur radio
+	  modems that connect to a serial interface. The driver supports the
+	  ser12 design in full-duplex mode. In addition, it allows the
+	  baudrate to be set between 300 and 4800 baud (however not all modems
+	  support all baudrates). This is the preferred driver. The next
+	  driver, "BAYCOM ser12 half-duplex driver for AX.25" is the old
+	  driver and still provided in case this driver does not work with
+	  your serial interface chip. To configure the driver, use the sethdlc
+	  utility available in the standard ax25 utilities package. For
+	  information on the modems, see <http://www.baycom.de/> and
+	  <file:Documentation/networking/baycom.txt>.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called baycom_ser_fdx.  This is recommended.
+
+config BAYCOM_SER_HDX
+	tristate "BAYCOM ser12 halfduplex driver for AX.25"
+	depends on AX25
+	select CRC_CCITT
+	---help---
+	  This is one of two drivers for Baycom style simple amateur radio
+	  modems that connect to a serial interface. The driver supports the
+	  ser12 design in full-duplex mode. This is the old driver.  It is
+	  still provided in case your serial interface chip does not work with
+	  the full-duplex driver. This driver is depreciated.  To configure
+	  the driver, use the sethdlc utility available in the standard ax25
+	  utilities package. For information on the modems, see
+	  <http://www.baycom.de/> and
+	  <file:Documentation/networking/baycom.txt>.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called baycom_ser_hdx.  This is recommended.
+
+config BAYCOM_PAR
+	tristate "BAYCOM picpar and par96 driver for AX.25"
+	depends on PARPORT && AX25
+	select CRC_CCITT
+	---help---
+	  This is a driver for Baycom style simple amateur radio modems that
+	  connect to a parallel interface. The driver supports the picpar and
+	  par96 designs. To configure the driver, use the sethdlc utility
+	  available in the standard ax25 utilities package. For information on
+	  the modems, see <http://www.baycom.de/> and the file
+	  <file:Documentation/networking/baycom.txt>.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called baycom_par.  This is recommended.
+
+config BAYCOM_EPP
+	tristate "BAYCOM epp driver for AX.25"
+	depends on PARPORT && AX25 && !64BIT
+	select CRC_CCITT
+	---help---
+	  This is a driver for Baycom style simple amateur radio modems that
+	  connect to a parallel interface. The driver supports the EPP
+	  designs. To configure the driver, use the sethdlc utility available
+	  in the standard ax25 utilities package. For information on the
+	  modems, see <http://www.baycom.de/> and the file
+	  <file:Documentation/networking/baycom.txt>.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called baycom_epp.  This is recommended.
+
+config YAM
+	tristate "YAM driver for AX.25"
+	depends on AX25
+	help
+	  The YAM is a modem for packet radio which connects to the serial
+	  port and includes some of the functions of a Terminal Node
+	  Controller. If you have one of those, say Y here.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called yam.
+
diff --git a/drivers/net/hamradio/Makefile b/drivers/net/hamradio/Makefile
new file mode 100644
index 0000000..9def867
--- /dev/null
+++ b/drivers/net/hamradio/Makefile
@@ -0,0 +1,22 @@
+#
+# Makefile for the Linux AX.25 and HFMODEM device drivers.
+#
+#
+# 19971130 	Moved the amateur radio related network drivers from 
+#		drivers/net/ to drivers/hamradio for easier maintainance.
+#               Joerg Reuter DL1BKE <jreuter@yaina.de>
+#
+# 20000806	Rewritten to use lists instead of if-statements.
+#		Christoph Hellwig <hch@infradead.org>
+#
+
+obj-$(CONFIG_DMASCC)		+= dmascc.o
+obj-$(CONFIG_SCC)		+= scc.o
+obj-$(CONFIG_MKISS)		+= mkiss.o
+obj-$(CONFIG_6PACK)		+= 6pack.o
+obj-$(CONFIG_YAM)		+= yam.o
+obj-$(CONFIG_BPQETHER)		+= bpqether.o
+obj-$(CONFIG_BAYCOM_SER_FDX)	+= baycom_ser_fdx.o	hdlcdrv.o
+obj-$(CONFIG_BAYCOM_SER_HDX)	+= baycom_ser_hdx.o	hdlcdrv.o
+obj-$(CONFIG_BAYCOM_PAR)	+= baycom_par.o		hdlcdrv.o
+obj-$(CONFIG_BAYCOM_EPP)	+= baycom_epp.o		hdlcdrv.o
diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c
new file mode 100644
index 0000000..e8cb87d
--- /dev/null
+++ b/drivers/net/hamradio/baycom_epp.c
@@ -0,0 +1,1382 @@
+/*****************************************************************************/
+
+/*
+ *	baycom_epp.c  -- baycom epp radio modem driver.
+ *
+ *	Copyright (C) 1998-2000
+ *          Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  History:
+ *   0.1  xx.xx.1998  Initial version by Matthias Welwarsky (dg2fef)
+ *   0.2  21.04.1998  Massive rework by Thomas Sailer
+ *                    Integrated FPGA EPP modem configuration routines
+ *   0.3  11.05.1998  Took FPGA config out and moved it into a separate program
+ *   0.4  26.07.1999  Adapted to new lowlevel parport driver interface
+ *   0.5  03.08.1999  adapt to Linus' new __setup/__initcall
+ *                    removed some pre-2.2 kernel compatibility cruft
+ *   0.6  10.08.1999  Check if parport can do SPP and is safe to access during interrupt contexts
+ *   0.7  12.02.2000  adapted to softnet driver interface
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/workqueue.h>
+#include <linux/fs.h>
+#include <linux/parport.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+#include <linux/if_arp.h>
+#include <linux/kmod.h>
+#include <linux/hdlcdrv.h>
+#include <linux/baycom.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+/* prototypes for ax25_encapsulate and ax25_rebuild_header */
+#include <net/ax25.h> 
+#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+#include <linux/crc-ccitt.h>
+
+/* --------------------------------------------------------------------- */
+
+#define BAYCOM_DEBUG
+#define BAYCOM_MAGIC 19730510
+
+/* --------------------------------------------------------------------- */
+
+static const char paranoia_str[] = KERN_ERR 
+	"baycom_epp: bad magic number for hdlcdrv_state struct in routine %s\n";
+
+static const char bc_drvname[] = "baycom_epp";
+static const char bc_drvinfo[] = KERN_INFO "baycom_epp: (C) 1998-2000 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "baycom_epp: version 0.7 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+static struct net_device *baycom_device[NR_PORTS];
+
+/* --------------------------------------------------------------------- */
+
+/* EPP status register */
+#define EPP_DCDBIT      0x80
+#define EPP_PTTBIT      0x08
+#define EPP_NREF        0x01
+#define EPP_NRAEF       0x02
+#define EPP_NRHF        0x04
+#define EPP_NTHF        0x20
+#define EPP_NTAEF       0x10
+#define EPP_NTEF        EPP_PTTBIT
+
+/* EPP control register */
+#define EPP_TX_FIFO_ENABLE 0x10
+#define EPP_RX_FIFO_ENABLE 0x08
+#define EPP_MODEM_ENABLE   0x20
+#define EPP_LEDS           0xC0
+#define EPP_IRQ_ENABLE     0x10
+
+/* LPT registers */
+#define LPTREG_ECONTROL       0x402
+#define LPTREG_CONFIGB        0x401
+#define LPTREG_CONFIGA        0x400
+#define LPTREG_EPPDATA        0x004
+#define LPTREG_EPPADDR        0x003
+#define LPTREG_CONTROL        0x002
+#define LPTREG_STATUS         0x001
+#define LPTREG_DATA           0x000
+
+/* LPT control register */
+#define LPTCTRL_PROGRAM       0x04   /* 0 to reprogram */
+#define LPTCTRL_WRITE         0x01
+#define LPTCTRL_ADDRSTB       0x08
+#define LPTCTRL_DATASTB       0x02
+#define LPTCTRL_INTEN         0x10
+
+/* LPT status register */
+#define LPTSTAT_SHIFT_NINTR   6
+#define LPTSTAT_WAIT          0x80
+#define LPTSTAT_NINTR         (1<<LPTSTAT_SHIFT_NINTR)
+#define LPTSTAT_PE            0x20
+#define LPTSTAT_DONE          0x10
+#define LPTSTAT_NERROR        0x08
+#define LPTSTAT_EPPTIMEOUT    0x01
+
+/* LPT data register */
+#define LPTDATA_SHIFT_TDI     0
+#define LPTDATA_SHIFT_TMS     2
+#define LPTDATA_TDI           (1<<LPTDATA_SHIFT_TDI)
+#define LPTDATA_TCK           0x02
+#define LPTDATA_TMS           (1<<LPTDATA_SHIFT_TMS)
+#define LPTDATA_INITBIAS      0x80
+
+
+/* EPP modem config/status bits */
+#define EPP_DCDBIT            0x80
+#define EPP_PTTBIT            0x08
+#define EPP_RXEBIT            0x01
+#define EPP_RXAEBIT           0x02
+#define EPP_RXHFULL           0x04
+
+#define EPP_NTHF              0x20
+#define EPP_NTAEF             0x10
+#define EPP_NTEF              EPP_PTTBIT
+
+#define EPP_TX_FIFO_ENABLE    0x10
+#define EPP_RX_FIFO_ENABLE    0x08
+#define EPP_MODEM_ENABLE      0x20
+#define EPP_LEDS              0xC0
+#define EPP_IRQ_ENABLE        0x10
+
+/* Xilinx 4k JTAG instructions */
+#define XC4K_IRLENGTH   3
+#define XC4K_EXTEST     0
+#define XC4K_PRELOAD    1
+#define XC4K_CONFIGURE  5
+#define XC4K_BYPASS     7
+
+#define EPP_CONVENTIONAL  0
+#define EPP_FPGA          1
+#define EPP_FPGAEXTSTATUS 2
+
+#define TXBUFFER_SIZE     ((HDLCDRV_MAXFLEN*6/5)+8)
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct baycom_state {
+	int magic;
+
+        struct pardevice *pdev;
+	unsigned int work_running;
+	struct work_struct run_work;
+	unsigned int modem;
+	unsigned int bitrate;
+	unsigned char stat;
+
+	struct {
+		unsigned int intclk;
+		unsigned int fclk;
+		unsigned int bps;
+		unsigned int extmodem;
+		unsigned int loopback;
+	} cfg;
+
+        struct hdlcdrv_channel_params ch_params;
+
+        struct {
+		unsigned int bitbuf, bitstream, numbits, state;
+		unsigned char *bufptr;
+		int bufcnt;
+		unsigned char buf[TXBUFFER_SIZE];
+        } hdlcrx;
+
+        struct {
+		int calibrate;
+                int slotcnt;
+		int flags;
+		enum { tx_idle = 0, tx_keyup, tx_data, tx_tail } state;
+		unsigned char *bufptr;
+		int bufcnt;
+		unsigned char buf[TXBUFFER_SIZE];
+        } hdlctx;
+
+        struct net_device_stats stats;
+	unsigned int ptt_keyed;
+	struct sk_buff *skb;  /* next transmit packet  */
+
+#ifdef BAYCOM_DEBUG
+	struct debug_vals {
+		unsigned long last_jiffies;
+		unsigned cur_intcnt;
+		unsigned last_intcnt;
+		int cur_pllcorr;
+		int last_pllcorr;
+		unsigned int mod_cycles;
+		unsigned int demod_cycles;
+	} debug_vals;
+#endif /* BAYCOM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+#define KISS_VERBOSE
+
+/* --------------------------------------------------------------------- */
+
+#define PARAM_TXDELAY   1
+#define PARAM_PERSIST   2
+#define PARAM_SLOTTIME  3
+#define PARAM_TXTAIL    4
+#define PARAM_FULLDUP   5
+#define PARAM_HARDWARE  6
+#define PARAM_RETURN    255
+
+/* --------------------------------------------------------------------- */
+/*
+ * the CRC routines are stolen from WAMPES
+ * by Dieter Deyke
+ */
+
+
+/*---------------------------------------------------------------------------*/
+
+#if 0
+static inline void append_crc_ccitt(unsigned char *buffer, int len)
+{
+ 	unsigned int crc = 0xffff;
+
+	for (;len>0;len--)
+		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff];
+	crc ^= 0xffff;
+	*buffer++ = crc;
+	*buffer++ = crc >> 8;
+}
+#endif
+
+/*---------------------------------------------------------------------------*/
+
+static inline int check_crc_ccitt(const unsigned char *buf, int cnt)
+{
+	return (crc_ccitt(0xffff, buf, cnt) & 0xffff) == 0xf0b8;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static inline int calc_crc_ccitt(const unsigned char *buf, int cnt)
+{
+	return (crc_ccitt(0xffff, buf, cnt) ^ 0xffff) & 0xffff;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#define tenms_to_flags(bc,tenms) ((tenms * bc->bitrate) / 800)
+
+/* --------------------------------------------------------------------- */
+
+static inline void baycom_int_freq(struct baycom_state *bc)
+{
+#ifdef BAYCOM_DEBUG
+	unsigned long cur_jiffies = jiffies;
+	/*
+	 * measure the interrupt frequency
+	 */
+	bc->debug_vals.cur_intcnt++;
+	if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
+		bc->debug_vals.last_jiffies = cur_jiffies;
+		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
+		bc->debug_vals.cur_intcnt = 0;
+		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
+		bc->debug_vals.cur_pllcorr = 0;
+	}
+#endif /* BAYCOM_DEBUG */
+}
+
+/* ---------------------------------------------------------------------- */
+/*
+ *    eppconfig_path should be setable  via /proc/sys.
+ */
+
+static char eppconfig_path[256] = "/usr/sbin/eppfpga";
+
+static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL };
+
+/* eppconfig: called during ifconfig up to configure the modem */
+static int eppconfig(struct baycom_state *bc)
+{
+	char modearg[256];
+	char portarg[16];
+        char *argv[] = { eppconfig_path, "-s", "-p", portarg, "-m", modearg,
+			 NULL };
+
+	/* set up arguments */
+	sprintf(modearg, "%sclk,%smodem,fclk=%d,bps=%d,divider=%d%s,extstat",
+		bc->cfg.intclk ? "int" : "ext",
+		bc->cfg.extmodem ? "ext" : "int", bc->cfg.fclk, bc->cfg.bps,
+		(bc->cfg.fclk + 8 * bc->cfg.bps) / (16 * bc->cfg.bps),
+		bc->cfg.loopback ? ",loopback" : "");
+	sprintf(portarg, "%ld", bc->pdev->port->base);
+	printk(KERN_DEBUG "%s: %s -s -p %s -m %s\n", bc_drvname, eppconfig_path, portarg, modearg);
+
+	return call_usermodehelper(eppconfig_path, argv, envp, 1);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void epp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+}
+
+/* ---------------------------------------------------------------------- */
+
+static inline void do_kiss_params(struct baycom_state *bc,
+				  unsigned char *data, unsigned long len)
+{
+
+#ifdef KISS_VERBOSE
+#define PKP(a,b) printk(KERN_INFO "baycomm_epp: channel params: " a "\n", b)
+#else /* KISS_VERBOSE */	      
+#define PKP(a,b) 
+#endif /* KISS_VERBOSE */	      
+
+	if (len < 2)
+		return;
+	switch(data[0]) {
+	case PARAM_TXDELAY:
+		bc->ch_params.tx_delay = data[1];
+		PKP("TX delay = %ums", 10 * bc->ch_params.tx_delay);
+		break;
+	case PARAM_PERSIST:   
+		bc->ch_params.ppersist = data[1];
+		PKP("p persistence = %u", bc->ch_params.ppersist);
+		break;
+	case PARAM_SLOTTIME:  
+		bc->ch_params.slottime = data[1];
+		PKP("slot time = %ums", bc->ch_params.slottime);
+		break;
+	case PARAM_TXTAIL:    
+		bc->ch_params.tx_tail = data[1];
+		PKP("TX tail = %ums", bc->ch_params.tx_tail);
+		break;
+	case PARAM_FULLDUP:   
+		bc->ch_params.fulldup = !!data[1];
+		PKP("%s duplex", bc->ch_params.fulldup ? "full" : "half");
+		break;
+	default:
+		break;
+	}
+#undef PKP
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * high performance HDLC encoder
+ * yes, it's ugly, but generates pretty good code
+ */
+
+#define ENCODEITERA(j)                         \
+({                                             \
+        if (!(notbitstream & (0x1f0 << j)))    \
+                goto stuff##j;                 \
+  encodeend##j:    	;                      \
+})
+
+#define ENCODEITERB(j)                                          \
+({                                                              \
+  stuff##j:                                                     \
+        bitstream &= ~(0x100 << j);                             \
+        bitbuf = (bitbuf & (((2 << j) << numbit) - 1)) |        \
+                ((bitbuf & ~(((2 << j) << numbit) - 1)) << 1);  \
+        numbit++;                                               \
+        notbitstream = ~bitstream;                              \
+        goto encodeend##j;                                      \
+})
+
+
+static void encode_hdlc(struct baycom_state *bc)
+{
+	struct sk_buff *skb;
+	unsigned char *wp, *bp;
+	int pkt_len;
+        unsigned bitstream, notbitstream, bitbuf, numbit, crc;
+	unsigned char crcarr[2];
+	
+	if (bc->hdlctx.bufcnt > 0)
+		return;
+	skb = bc->skb;
+	if (!skb)
+		return;
+	bc->skb = NULL;
+	pkt_len = skb->len-1; /* strip KISS byte */
+	wp = bc->hdlctx.buf;
+	bp = skb->data+1;
+	crc = calc_crc_ccitt(bp, pkt_len);
+	crcarr[0] = crc;
+	crcarr[1] = crc >> 8;
+	*wp++ = 0x7e;
+	bitstream = bitbuf = numbit = 0;
+	while (pkt_len > -2) {
+		bitstream >>= 8;
+		bitstream |= ((unsigned int)*bp) << 8;
+		bitbuf |= ((unsigned int)*bp) << numbit;
+		notbitstream = ~bitstream;
+		bp++;
+		pkt_len--;
+		if (!pkt_len)
+			bp = crcarr;
+		ENCODEITERA(0);
+		ENCODEITERA(1);
+		ENCODEITERA(2);
+		ENCODEITERA(3);
+		ENCODEITERA(4);
+		ENCODEITERA(5);
+		ENCODEITERA(6);
+		ENCODEITERA(7);
+		goto enditer;
+		ENCODEITERB(0);
+		ENCODEITERB(1);
+		ENCODEITERB(2);
+		ENCODEITERB(3);
+		ENCODEITERB(4);
+		ENCODEITERB(5);
+		ENCODEITERB(6);
+		ENCODEITERB(7);
+	enditer:
+		numbit += 8;
+		while (numbit >= 8) {
+			*wp++ = bitbuf;
+			bitbuf >>= 8;
+			numbit -= 8;
+		}
+	}
+	bitbuf |= 0x7e7e << numbit;
+	numbit += 16;
+	while (numbit >= 8) {
+		*wp++ = bitbuf;
+		bitbuf >>= 8;
+		numbit -= 8;
+	}
+	bc->hdlctx.bufptr = bc->hdlctx.buf;
+	bc->hdlctx.bufcnt = wp - bc->hdlctx.buf;
+	dev_kfree_skb(skb);
+	bc->stats.tx_packets++;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static unsigned short random_seed;
+
+static inline unsigned short random_num(void)
+{
+	random_seed = 28629 * random_seed + 157;
+	return random_seed;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int transmit(struct baycom_state *bc, int cnt, unsigned char stat)
+{
+	struct parport *pp = bc->pdev->port;
+	unsigned char tmp[128];
+	int i, j;
+
+	if (bc->hdlctx.state == tx_tail && !(stat & EPP_PTTBIT))
+		bc->hdlctx.state = tx_idle;
+	if (bc->hdlctx.state == tx_idle && bc->hdlctx.calibrate <= 0) {
+		if (bc->hdlctx.bufcnt <= 0)
+			encode_hdlc(bc);
+		if (bc->hdlctx.bufcnt <= 0)
+			return 0;
+		if (!bc->ch_params.fulldup) {
+			if (!(stat & EPP_DCDBIT)) {
+				bc->hdlctx.slotcnt = bc->ch_params.slottime;
+				return 0;
+			}
+			if ((--bc->hdlctx.slotcnt) > 0)
+				return 0;
+			bc->hdlctx.slotcnt = bc->ch_params.slottime;
+			if ((random_num() % 256) > bc->ch_params.ppersist)
+				return 0;
+		}
+	}
+	if (bc->hdlctx.state == tx_idle && bc->hdlctx.bufcnt > 0) {
+		bc->hdlctx.state = tx_keyup;
+		bc->hdlctx.flags = tenms_to_flags(bc, bc->ch_params.tx_delay);
+		bc->ptt_keyed++;
+	}
+	while (cnt > 0) {
+		switch (bc->hdlctx.state) {
+		case tx_keyup:
+			i = min_t(int, cnt, bc->hdlctx.flags);
+			cnt -= i;
+			bc->hdlctx.flags -= i;
+			if (bc->hdlctx.flags <= 0)
+				bc->hdlctx.state = tx_data;
+			memset(tmp, 0x7e, sizeof(tmp));
+			while (i > 0) {
+				j = (i > sizeof(tmp)) ? sizeof(tmp) : i;
+				if (j != pp->ops->epp_write_data(pp, tmp, j, 0))
+					return -1;
+				i -= j;
+			}
+			break;
+
+		case tx_data:
+			if (bc->hdlctx.bufcnt <= 0) {
+				encode_hdlc(bc);
+				if (bc->hdlctx.bufcnt <= 0) {
+					bc->hdlctx.state = tx_tail;
+					bc->hdlctx.flags = tenms_to_flags(bc, bc->ch_params.tx_tail);
+					break;
+				}
+			}
+			i = min_t(int, cnt, bc->hdlctx.bufcnt);
+			bc->hdlctx.bufcnt -= i;
+			cnt -= i;
+			if (i != pp->ops->epp_write_data(pp, bc->hdlctx.bufptr, i, 0))
+					return -1;
+			bc->hdlctx.bufptr += i;
+			break;
+			
+		case tx_tail:
+			encode_hdlc(bc);
+			if (bc->hdlctx.bufcnt > 0) {
+				bc->hdlctx.state = tx_data;
+				break;
+			}
+			i = min_t(int, cnt, bc->hdlctx.flags);
+			if (i) {
+				cnt -= i;
+				bc->hdlctx.flags -= i;
+				memset(tmp, 0x7e, sizeof(tmp));
+				while (i > 0) {
+					j = (i > sizeof(tmp)) ? sizeof(tmp) : i;
+					if (j != pp->ops->epp_write_data(pp, tmp, j, 0))
+						return -1;
+					i -= j;
+				}
+				break;
+			}
+
+		default:  /* fall through */
+			if (bc->hdlctx.calibrate <= 0)
+				return 0;
+			i = min_t(int, cnt, bc->hdlctx.calibrate);
+			cnt -= i;
+			bc->hdlctx.calibrate -= i;
+			memset(tmp, 0, sizeof(tmp));
+			while (i > 0) {
+				j = (i > sizeof(tmp)) ? sizeof(tmp) : i;
+				if (j != pp->ops->epp_write_data(pp, tmp, j, 0))
+					return -1;
+				i -= j;
+			}
+			break;
+		}
+	}
+	return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void do_rxpacket(struct net_device *dev)
+{
+	struct baycom_state *bc = netdev_priv(dev);
+	struct sk_buff *skb;
+	unsigned char *cp;
+	unsigned pktlen;
+
+	if (bc->hdlcrx.bufcnt < 4) 
+		return;
+	if (!check_crc_ccitt(bc->hdlcrx.buf, bc->hdlcrx.bufcnt)) 
+		return;
+	pktlen = bc->hdlcrx.bufcnt-2+1; /* KISS kludge */
+	if (!(skb = dev_alloc_skb(pktlen))) {
+		printk("%s: memory squeeze, dropping packet\n", dev->name);
+		bc->stats.rx_dropped++;
+		return;
+	}
+	skb->dev = dev;
+	cp = skb_put(skb, pktlen);
+	*cp++ = 0; /* KISS kludge */
+	memcpy(cp, bc->hdlcrx.buf, pktlen - 1);
+	skb->protocol = htons(ETH_P_AX25);
+	skb->mac.raw = skb->data;
+	netif_rx(skb);
+	dev->last_rx = jiffies;
+	bc->stats.rx_packets++;
+}
+
+#define DECODEITERA(j)                                                        \
+({                                                                            \
+        if (!(notbitstream & (0x0fc << j)))              /* flag or abort */  \
+                goto flgabrt##j;                                              \
+        if ((bitstream & (0x1f8 << j)) == (0xf8 << j))   /* stuffed bit */    \
+                goto stuff##j;                                                \
+  enditer##j:      ;                                                           \
+})
+
+#define DECODEITERB(j)                                                                 \
+({                                                                                     \
+  flgabrt##j:                                                                          \
+        if (!(notbitstream & (0x1fc << j))) {              /* abort received */        \
+                state = 0;                                                             \
+                goto enditer##j;                                                       \
+        }                                                                              \
+        if ((bitstream & (0x1fe << j)) != (0x0fc << j))   /* flag received */          \
+                goto enditer##j;                                                       \
+        if (state)                                                                     \
+                do_rxpacket(dev);                                                      \
+        bc->hdlcrx.bufcnt = 0;                                                         \
+        bc->hdlcrx.bufptr = bc->hdlcrx.buf;                                            \
+        state = 1;                                                                     \
+        numbits = 7-j;                                                                 \
+        goto enditer##j;                                                               \
+  stuff##j:                                                                            \
+        numbits--;                                                                     \
+        bitbuf = (bitbuf & ((~0xff) << j)) | ((bitbuf & ~((~0xff) << j)) << 1);        \
+        goto enditer##j;                                                               \
+})
+        
+static int receive(struct net_device *dev, int cnt)
+{
+	struct baycom_state *bc = netdev_priv(dev);
+	struct parport *pp = bc->pdev->port;
+        unsigned int bitbuf, notbitstream, bitstream, numbits, state;
+	unsigned char tmp[128];
+        unsigned char *cp;
+	int cnt2, ret = 0;
+        
+        numbits = bc->hdlcrx.numbits;
+	state = bc->hdlcrx.state;
+	bitstream = bc->hdlcrx.bitstream;
+	bitbuf = bc->hdlcrx.bitbuf;
+	while (cnt > 0) {
+		cnt2 = (cnt > sizeof(tmp)) ? sizeof(tmp) : cnt;
+		cnt -= cnt2;
+		if (cnt2 != pp->ops->epp_read_data(pp, tmp, cnt2, 0)) {
+			ret = -1;
+			break;
+		}
+		cp = tmp;
+		for (; cnt2 > 0; cnt2--, cp++) {
+			bitstream >>= 8;
+			bitstream |= (*cp) << 8;
+			bitbuf >>= 8;
+			bitbuf |= (*cp) << 8;
+			numbits += 8;
+			notbitstream = ~bitstream;
+			DECODEITERA(0);
+			DECODEITERA(1);
+			DECODEITERA(2);
+			DECODEITERA(3);
+			DECODEITERA(4);
+			DECODEITERA(5);
+			DECODEITERA(6);
+			DECODEITERA(7);
+			goto enddec;
+			DECODEITERB(0);
+			DECODEITERB(1);
+			DECODEITERB(2);
+			DECODEITERB(3);
+			DECODEITERB(4);
+			DECODEITERB(5);
+			DECODEITERB(6);
+			DECODEITERB(7);
+		enddec:
+			while (state && numbits >= 8) {
+				if (bc->hdlcrx.bufcnt >= TXBUFFER_SIZE) {
+					state = 0;
+				} else {
+					*(bc->hdlcrx.bufptr)++ = bitbuf >> (16-numbits);
+					bc->hdlcrx.bufcnt++;
+					numbits -= 8;
+				}
+			}
+		}
+	}
+        bc->hdlcrx.numbits = numbits;
+	bc->hdlcrx.state = state;
+	bc->hdlcrx.bitstream = bitstream;
+	bc->hdlcrx.bitbuf = bitbuf;
+	return ret;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef __i386__
+#include <asm/msr.h>
+#define GETTICK(x)                                                \
+({                                                                \
+	if (cpu_has_tsc)                                          \
+		rdtscl(x);                                        \
+})
+#else /* __i386__ */
+#define GETTICK(x)
+#endif /* __i386__ */
+
+static void epp_bh(struct net_device *dev)
+{
+	struct baycom_state *bc;
+	struct parport *pp;
+	unsigned char stat;
+	unsigned char tmp[2];
+	unsigned int time1 = 0, time2 = 0, time3 = 0;
+	int cnt, cnt2;
+	
+	bc = netdev_priv(dev);
+	if (!bc->work_running)
+		return;
+	baycom_int_freq(bc);
+	pp = bc->pdev->port;
+	/* update status */
+	if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1)
+		goto epptimeout;
+	bc->stat = stat;
+	bc->debug_vals.last_pllcorr = stat;
+	GETTICK(time1);
+	if (bc->modem == EPP_FPGAEXTSTATUS) {
+		/* get input count */
+		tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|1;
+		if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1)
+			goto epptimeout;
+		if (pp->ops->epp_read_addr(pp, tmp, 2, 0) != 2)
+			goto epptimeout;
+		cnt = tmp[0] | (tmp[1] << 8);
+		cnt &= 0x7fff;
+		/* get output count */
+		tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|2;
+		if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1)
+			goto epptimeout;
+		if (pp->ops->epp_read_addr(pp, tmp, 2, 0) != 2)
+			goto epptimeout;
+		cnt2 = tmp[0] | (tmp[1] << 8);
+		cnt2 = 16384 - (cnt2 & 0x7fff);
+		/* return to normal */
+		tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE;
+		if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1)
+			goto epptimeout;
+		if (transmit(bc, cnt2, stat))
+			goto epptimeout;
+		GETTICK(time2);
+		if (receive(dev, cnt))
+			goto epptimeout;
+		if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1)
+			goto epptimeout;
+		bc->stat = stat;
+	} else {
+		/* try to tx */
+		switch (stat & (EPP_NTAEF|EPP_NTHF)) {
+		case EPP_NTHF:
+			cnt = 2048 - 256;
+			break;
+		
+		case EPP_NTAEF:
+			cnt = 2048 - 1793;
+			break;
+		
+		case 0:
+			cnt = 0;
+			break;
+		
+		default:
+			cnt = 2048 - 1025;
+			break;
+		}
+		if (transmit(bc, cnt, stat))
+			goto epptimeout;
+		GETTICK(time2);
+		/* do receiver */
+		while ((stat & (EPP_NRAEF|EPP_NRHF)) != EPP_NRHF) {
+			switch (stat & (EPP_NRAEF|EPP_NRHF)) {
+			case EPP_NRAEF:
+				cnt = 1025;
+				break;
+
+			case 0:
+				cnt = 1793;
+				break;
+
+			default:
+				cnt = 256;
+				break;
+			}
+			if (receive(dev, cnt))
+				goto epptimeout;
+			if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1)
+				goto epptimeout;
+		}
+		cnt = 0;
+		if (bc->bitrate < 50000)
+			cnt = 256;
+		else if (bc->bitrate < 100000)
+			cnt = 128;
+		while (cnt > 0 && stat & EPP_NREF) {
+			if (receive(dev, 1))
+				goto epptimeout;
+			cnt--;
+			if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1)
+				goto epptimeout;
+		}
+	}
+	GETTICK(time3);
+#ifdef BAYCOM_DEBUG
+	bc->debug_vals.mod_cycles = time2 - time1;
+	bc->debug_vals.demod_cycles = time3 - time2;
+#endif /* BAYCOM_DEBUG */
+	schedule_delayed_work(&bc->run_work, 1);
+	if (!bc->skb)
+		netif_wake_queue(dev);
+	return;
+ epptimeout:
+	printk(KERN_ERR "%s: EPP timeout!\n", bc_drvname);
+}
+
+/* ---------------------------------------------------------------------- */
+/*
+ * ===================== network driver interface =========================
+ */
+
+static int baycom_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+	struct baycom_state *bc = netdev_priv(dev);
+
+	if (skb->data[0] != 0) {
+		do_kiss_params(bc, skb->data, skb->len);
+		dev_kfree_skb(skb);
+		return 0;
+	}
+	if (bc->skb)
+		return -1;
+	/* strip KISS byte */
+	if (skb->len >= HDLCDRV_MAXFLEN+1 || skb->len < 3) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+	netif_stop_queue(dev);
+	bc->skb = skb;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_set_mac_address(struct net_device *dev, void *addr)
+{
+	struct sockaddr *sa = (struct sockaddr *)addr;
+
+	/* addr is an AX.25 shifted ASCII mac address */
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); 
+	return 0;                                         
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct net_device_stats *baycom_get_stats(struct net_device *dev)
+{
+	struct baycom_state *bc = netdev_priv(dev);
+
+	/* 
+	 * Get the current statistics.  This may be called with the
+	 * card open or closed. 
+	 */
+	return &bc->stats;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void epp_wakeup(void *handle)
+{
+        struct net_device *dev = (struct net_device *)handle;
+        struct baycom_state *bc = netdev_priv(dev);
+
+        printk(KERN_DEBUG "baycom_epp: %s: why am I being woken up?\n", dev->name);
+        if (!parport_claim(bc->pdev))
+                printk(KERN_DEBUG "baycom_epp: %s: I'm broken.\n", dev->name);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+
+static int epp_open(struct net_device *dev)
+{
+	struct baycom_state *bc = netdev_priv(dev);
+        struct parport *pp = parport_find_base(dev->base_addr);
+	unsigned int i, j;
+	unsigned char tmp[128];
+	unsigned char stat;
+	unsigned long tstart;
+	
+        if (!pp) {
+                printk(KERN_ERR "%s: parport at 0x%lx unknown\n", bc_drvname, dev->base_addr);
+                return -ENXIO;
+        }
+#if 0
+        if (pp->irq < 0) {
+                printk(KERN_ERR "%s: parport at 0x%lx has no irq\n", bc_drvname, pp->base);
+		parport_put_port(pp);
+                return -ENXIO;
+        }
+#endif
+	if ((~pp->modes) & (PARPORT_MODE_TRISTATE | PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT)) {
+                printk(KERN_ERR "%s: parport at 0x%lx cannot be used\n",
+		       bc_drvname, pp->base);
+		parport_put_port(pp);
+                return -EIO;
+	}
+	memset(&bc->modem, 0, sizeof(bc->modem));
+        bc->pdev = parport_register_device(pp, dev->name, NULL, epp_wakeup, 
+					epp_interrupt, PARPORT_DEV_EXCL, dev);
+	parport_put_port(pp);
+        if (!bc->pdev) {
+                printk(KERN_ERR "%s: cannot register parport at 0x%lx\n", bc_drvname, pp->base);
+                return -ENXIO;
+        }
+        if (parport_claim(bc->pdev)) {
+                printk(KERN_ERR "%s: parport at 0x%lx busy\n", bc_drvname, pp->base);
+                parport_unregister_device(bc->pdev);
+                return -EBUSY;
+        }
+        dev->irq = /*pp->irq*/ 0;
+	INIT_WORK(&bc->run_work, (void *)(void *)epp_bh, dev);
+	bc->work_running = 1;
+	bc->modem = EPP_CONVENTIONAL;
+	if (eppconfig(bc))
+		printk(KERN_INFO "%s: no FPGA detected, assuming conventional EPP modem\n", bc_drvname);
+	else
+		bc->modem = /*EPP_FPGA*/ EPP_FPGAEXTSTATUS;
+	parport_write_control(pp, LPTCTRL_PROGRAM); /* prepare EPP mode; we aren't using interrupts */
+	/* reset the modem */
+	tmp[0] = 0;
+	tmp[1] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE;
+	if (pp->ops->epp_write_addr(pp, tmp, 2, 0) != 2)
+		goto epptimeout;
+	/* autoprobe baud rate */
+	tstart = jiffies;
+	i = 0;
+	while ((signed)(jiffies-tstart-HZ/3) < 0) {
+		if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1)
+			goto epptimeout;
+		if ((stat & (EPP_NRAEF|EPP_NRHF)) == EPP_NRHF) {
+			schedule();
+			continue;
+		}
+		if (pp->ops->epp_read_data(pp, tmp, 128, 0) != 128)
+			goto epptimeout;
+		if (pp->ops->epp_read_data(pp, tmp, 128, 0) != 128)
+			goto epptimeout;
+		i += 256;
+	}
+	for (j = 0; j < 256; j++) {
+		if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1)
+			goto epptimeout;
+		if (!(stat & EPP_NREF))
+			break;
+		if (pp->ops->epp_read_data(pp, tmp, 1, 0) != 1)
+			goto epptimeout;
+		i++;
+	}
+	tstart = jiffies - tstart;
+	bc->bitrate = i * (8 * HZ) / tstart;
+	j = 1;
+	i = bc->bitrate >> 3;
+	while (j < 7 && i > 150) {
+		j++;
+		i >>= 1;
+	}
+	printk(KERN_INFO "%s: autoprobed bitrate: %d  int divider: %d  int rate: %d\n", 
+	       bc_drvname, bc->bitrate, j, bc->bitrate >> (j+2));
+	tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE/*|j*/;
+	if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1)
+		goto epptimeout;
+	/*
+	 * initialise hdlc variables
+	 */
+	bc->hdlcrx.state = 0;
+	bc->hdlcrx.numbits = 0;
+	bc->hdlctx.state = tx_idle;
+	bc->hdlctx.bufcnt = 0;
+	bc->hdlctx.slotcnt = bc->ch_params.slottime;
+	bc->hdlctx.calibrate = 0;
+	/* start the bottom half stuff */
+	schedule_delayed_work(&bc->run_work, 1);
+	netif_start_queue(dev);
+	return 0;
+
+ epptimeout:
+	printk(KERN_ERR "%s: epp timeout during bitrate probe\n", bc_drvname);
+	parport_write_control(pp, 0); /* reset the adapter */
+        parport_release(bc->pdev);
+        parport_unregister_device(bc->pdev);
+	return -EIO;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int epp_close(struct net_device *dev)
+{
+	struct baycom_state *bc = netdev_priv(dev);
+	struct parport *pp = bc->pdev->port;
+	unsigned char tmp[1];
+
+	bc->work_running = 0;
+	flush_scheduled_work();
+	bc->stat = EPP_DCDBIT;
+	tmp[0] = 0;
+	pp->ops->epp_write_addr(pp, tmp, 1, 0);
+	parport_write_control(pp, 0); /* reset the adapter */
+        parport_release(bc->pdev);
+        parport_unregister_device(bc->pdev);
+	if (bc->skb)
+		dev_kfree_skb(bc->skb);
+	bc->skb = NULL;
+	printk(KERN_INFO "%s: close epp at iobase 0x%lx irq %u\n",
+	       bc_drvname, dev->base_addr, dev->irq);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_setmode(struct baycom_state *bc, const char *modestr)
+{
+	const char *cp;
+
+	if (strstr(modestr,"intclk"))
+		bc->cfg.intclk = 1;
+	if (strstr(modestr,"extclk"))
+		bc->cfg.intclk = 0;
+	if (strstr(modestr,"intmodem"))
+		bc->cfg.extmodem = 0;
+	if (strstr(modestr,"extmodem"))
+		bc->cfg.extmodem = 1;
+	if (strstr(modestr,"noloopback"))
+		bc->cfg.loopback = 0;
+	if (strstr(modestr,"loopback"))
+		bc->cfg.loopback = 1;
+	if ((cp = strstr(modestr,"fclk="))) {
+		bc->cfg.fclk = simple_strtoul(cp+5, NULL, 0);
+		if (bc->cfg.fclk < 1000000)
+			bc->cfg.fclk = 1000000;
+		if (bc->cfg.fclk > 25000000)
+			bc->cfg.fclk = 25000000;
+	}
+	if ((cp = strstr(modestr,"bps="))) {
+		bc->cfg.bps = simple_strtoul(cp+4, NULL, 0);
+		if (bc->cfg.bps < 1000)
+			bc->cfg.bps = 1000;
+		if (bc->cfg.bps > 1500000)
+			bc->cfg.bps = 1500000;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct baycom_state *bc = netdev_priv(dev);
+	struct hdlcdrv_ioctl hi;
+
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+
+	if (copy_from_user(&hi, ifr->ifr_data, sizeof(hi)))
+		return -EFAULT;
+	switch (hi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+	case HDLCDRVCTL_GETCHANNELPAR:
+		hi.data.cp.tx_delay = bc->ch_params.tx_delay;
+		hi.data.cp.tx_tail = bc->ch_params.tx_tail;
+		hi.data.cp.slottime = bc->ch_params.slottime;
+		hi.data.cp.ppersist = bc->ch_params.ppersist;
+		hi.data.cp.fulldup = bc->ch_params.fulldup;
+		break;
+
+	case HDLCDRVCTL_SETCHANNELPAR:
+		if (!capable(CAP_NET_ADMIN))
+			return -EACCES;
+		bc->ch_params.tx_delay = hi.data.cp.tx_delay;
+		bc->ch_params.tx_tail = hi.data.cp.tx_tail;
+		bc->ch_params.slottime = hi.data.cp.slottime;
+		bc->ch_params.ppersist = hi.data.cp.ppersist;
+		bc->ch_params.fulldup = hi.data.cp.fulldup;
+		bc->hdlctx.slotcnt = 1;
+		return 0;
+		
+	case HDLCDRVCTL_GETMODEMPAR:
+		hi.data.mp.iobase = dev->base_addr;
+		hi.data.mp.irq = dev->irq;
+		hi.data.mp.dma = dev->dma;
+		hi.data.mp.dma2 = 0;
+		hi.data.mp.seriobase = 0;
+		hi.data.mp.pariobase = 0;
+		hi.data.mp.midiiobase = 0;
+		break;
+
+	case HDLCDRVCTL_SETMODEMPAR:
+		if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev))
+			return -EACCES;
+		dev->base_addr = hi.data.mp.iobase;
+		dev->irq = /*hi.data.mp.irq*/0;
+		dev->dma = /*hi.data.mp.dma*/0;
+		return 0;	
+		
+	case HDLCDRVCTL_GETSTAT:
+		hi.data.cs.ptt = !!(bc->stat & EPP_PTTBIT);
+		hi.data.cs.dcd = !(bc->stat & EPP_DCDBIT);
+		hi.data.cs.ptt_keyed = bc->ptt_keyed;
+		hi.data.cs.tx_packets = bc->stats.tx_packets;
+		hi.data.cs.tx_errors = bc->stats.tx_errors;
+		hi.data.cs.rx_packets = bc->stats.rx_packets;
+		hi.data.cs.rx_errors = bc->stats.rx_errors;
+		break;		
+
+	case HDLCDRVCTL_OLDGETSTAT:
+		hi.data.ocs.ptt = !!(bc->stat & EPP_PTTBIT);
+		hi.data.ocs.dcd = !(bc->stat & EPP_DCDBIT);
+		hi.data.ocs.ptt_keyed = bc->ptt_keyed;
+		break;		
+
+	case HDLCDRVCTL_CALIBRATE:
+		if (!capable(CAP_SYS_RAWIO))
+			return -EACCES;
+		bc->hdlctx.calibrate = hi.data.calibrate * bc->bitrate / 8;
+		return 0;
+
+	case HDLCDRVCTL_DRIVERNAME:
+		strncpy(hi.data.drivername, "baycom_epp", sizeof(hi.data.drivername));
+		break;
+		
+	case HDLCDRVCTL_GETMODE:
+		sprintf(hi.data.modename, "%sclk,%smodem,fclk=%d,bps=%d%s", 
+			bc->cfg.intclk ? "int" : "ext",
+			bc->cfg.extmodem ? "ext" : "int", bc->cfg.fclk, bc->cfg.bps,
+			bc->cfg.loopback ? ",loopback" : "");
+		break;
+
+	case HDLCDRVCTL_SETMODE:
+		if (!capable(CAP_NET_ADMIN) || netif_running(dev))
+			return -EACCES;
+		hi.data.modename[sizeof(hi.data.modename)-1] = '\0';
+		return baycom_setmode(bc, hi.data.modename);
+
+	case HDLCDRVCTL_MODELIST:
+		strncpy(hi.data.modename, "intclk,extclk,intmodem,extmodem,divider=x",
+			sizeof(hi.data.modename));
+		break;
+
+	case HDLCDRVCTL_MODEMPARMASK:
+		return HDLCDRV_PARMASK_IOBASE;
+
+	}
+	if (copy_to_user(ifr->ifr_data, &hi, sizeof(hi)))
+		return -EFAULT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Check for a network adaptor of this type, and return '0' if one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ */
+static void baycom_probe(struct net_device *dev)
+{
+	static char ax25_bcast[AX25_ADDR_LEN] = {
+		'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1
+	};
+	static char ax25_nocall[AX25_ADDR_LEN] = {
+		'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1
+	};
+	const struct hdlcdrv_channel_params dflt_ch_params = { 
+		20, 2, 10, 40, 0 
+	};
+	struct baycom_state *bc;
+
+	/*
+	 * not a real probe! only initialize data structures
+	 */
+	bc = netdev_priv(dev);
+	/*
+	 * initialize the baycom_state struct
+	 */
+	bc->ch_params = dflt_ch_params;
+	bc->ptt_keyed = 0;
+
+	/*
+	 * initialize the device struct
+	 */
+	dev->open = epp_open;
+	dev->stop = epp_close;
+	dev->do_ioctl = baycom_ioctl;
+	dev->hard_start_xmit = baycom_send_packet;
+	dev->get_stats = baycom_get_stats;
+
+	/* Fill in the fields of the device structure */
+	bc->skb = NULL;
+	
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+	dev->hard_header = ax25_encapsulate;
+	dev->rebuild_header = ax25_rebuild_header;
+#else /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+	dev->hard_header = NULL;
+	dev->rebuild_header = NULL;
+#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+	dev->set_mac_address = baycom_set_mac_address;
+	
+	dev->type = ARPHRD_AX25;           /* AF_AX25 device */
+	dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+	dev->mtu = AX25_DEF_PACLEN;        /* eth_mtu is the default */
+	dev->addr_len = AX25_ADDR_LEN;     /* sizeof an ax.25 address */
+	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+	memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN);
+	dev->tx_queue_len = 16;
+
+	/* New style flags */
+	dev->flags = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * command line settable parameters
+ */
+static const char *mode[NR_PORTS] = { "", };
+static int iobase[NR_PORTS] = { 0x378, };
+
+module_param_array(mode, charp, NULL, 0);
+MODULE_PARM_DESC(mode, "baycom operating mode");
+module_param_array(iobase, int, NULL, 0);
+MODULE_PARM_DESC(iobase, "baycom io base address");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Baycom epp amateur radio modem driver");
+MODULE_LICENSE("GPL");
+
+/* --------------------------------------------------------------------- */
+
+static void __init baycom_epp_dev_setup(struct net_device *dev)
+{
+	struct baycom_state *bc = netdev_priv(dev);
+
+	/*
+	 * initialize part of the baycom_state struct
+	 */
+	bc->magic = BAYCOM_MAGIC;
+	bc->cfg.fclk = 19666600;
+	bc->cfg.bps = 9600;
+	/*
+	 * initialize part of the device struct
+	 */
+	baycom_probe(dev);
+}
+
+static int __init init_baycomepp(void)
+{
+	int i, found = 0;
+	char set_hw = 1;
+
+	printk(bc_drvinfo);
+	/*
+	 * register net devices
+	 */
+	for (i = 0; i < NR_PORTS; i++) {
+		struct net_device *dev;
+		
+		dev = alloc_netdev(sizeof(struct baycom_state), "bce%d",
+				   baycom_epp_dev_setup);
+
+		if (!dev) {
+			printk(KERN_WARNING "bce%d : out of memory\n", i);
+			return found ? 0 : -ENOMEM;
+		}
+			
+		sprintf(dev->name, "bce%d", i);
+		dev->base_addr = iobase[i];
+
+		if (!mode[i])
+			set_hw = 0;
+		if (!set_hw)
+			iobase[i] = 0;
+
+		if (register_netdev(dev)) {
+			printk(KERN_WARNING "%s: cannot register net device %s\n", bc_drvname, dev->name);
+			free_netdev(dev);
+			break;
+		}
+		if (set_hw && baycom_setmode(netdev_priv(dev), mode[i]))
+			set_hw = 0;
+		baycom_device[i] = dev;
+		found++;
+	}
+
+	return found ? 0 : -ENXIO;
+}
+
+static void __exit cleanup_baycomepp(void)
+{
+	int i;
+
+	for(i = 0; i < NR_PORTS; i++) {
+		struct net_device *dev = baycom_device[i];
+
+		if (dev) {
+			struct baycom_state *bc = netdev_priv(dev);
+			if (bc->magic == BAYCOM_MAGIC) {
+				unregister_netdev(dev);
+				free_netdev(dev);
+			} else
+				printk(paranoia_str, "cleanup_module");
+		}
+	}
+}
+
+module_init(init_baycomepp);
+module_exit(cleanup_baycomepp);
+
+/* --------------------------------------------------------------------- */
+
+#ifndef MODULE
+
+/*
+ * format: baycom_epp=io,mode
+ * mode: fpga config options
+ */
+
+static int __init baycom_epp_setup(char *str)
+{
+        static unsigned __initdata nr_dev = 0;
+	int ints[2];
+
+        if (nr_dev >= NR_PORTS)
+                return 0;
+	str = get_options(str, 2, ints);
+	if (ints[0] < 1)
+		return 0;
+	mode[nr_dev] = str;
+	iobase[nr_dev] = ints[1];
+	nr_dev++;
+	return 1;
+}
+
+__setup("baycom_epp=", baycom_epp_setup);
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c
new file mode 100644
index 0000000..612ad45
--- /dev/null
+++ b/drivers/net/hamradio/baycom_par.c
@@ -0,0 +1,576 @@
+/*****************************************************************************/
+
+/*
+ *	baycom_par.c  -- baycom par96 and picpar radio modem driver.
+ *
+ *	Copyright (C) 1996-2000  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  Supported modems
+ *
+ *  par96:  This is a modem for 9600 baud FSK compatible to the G3RUH standard.
+ *          The modem does all the filtering and regenerates the receiver clock.
+ *          Data is transferred from and to the PC via a shift register.
+ *          The shift register is filled with 16 bits and an interrupt is
+ *          signalled. The PC then empties the shift register in a burst. This
+ *          modem connects to the parallel port, hence the name. The modem
+ *          leaves the implementation of the HDLC protocol and the scrambler
+ *          polynomial to the PC. This modem is no longer available (at least
+ *          from Baycom) and has been replaced by the PICPAR modem (see below).
+ *          You may however still build one from the schematics published in
+ *          cq-DL :-).
+ *
+ *  picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The
+ *          modem is protocol compatible to par96, but uses only three low
+ *          power ICs and can therefore be fed from the parallel port and
+ *          does not require an additional power supply. It features
+ *          built in DCD circuitry. The driver should therefore be configured
+ *          for hardware DCD.
+ *
+ *
+ *  Command line options (insmod command line)
+ *
+ *  mode     driver mode string. Valid choices are par96 and picpar.
+ *  iobase   base address of the port; common values are 0x378, 0x278, 0x3bc
+ *
+ *
+ *  History:
+ *   0.1  26.06.1996  Adapted from baycom.c and made network driver interface
+ *        18.10.1996  Changed to new user space access routines (copy_{to,from}_user)
+ *   0.3  26.04.1997  init code/data tagged
+ *   0.4  08.07.1997  alternative ser12 decoding algorithm (uses delta CTS ints)
+ *   0.5  11.11.1997  split into separate files for ser12/par96
+ *   0.6  03.08.1999  adapt to Linus' new __setup/__initcall
+ *                    removed some pre-2.2 kernel compatibility cruft
+ *   0.7  10.08.1999  Check if parport can do SPP and is safe to access during interrupt contexts
+ *   0.8  12.02.2000  adapted to softnet driver interface
+ *                    removed direct parport access, uses parport driver methods
+ *   0.9  03.07.2000  fix interface name handling
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/hdlcdrv.h>
+#include <linux/baycom.h>
+#include <linux/parport.h>
+#include <linux/bitops.h>
+
+#include <asm/bug.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+/* --------------------------------------------------------------------- */
+
+#define BAYCOM_DEBUG
+
+/*
+ * modem options; bit mask
+ */
+#define BAYCOM_OPTIONS_SOFTDCD  1
+
+/* --------------------------------------------------------------------- */
+
+static const char bc_drvname[] = "baycom_par";
+static const char bc_drvinfo[] = KERN_INFO "baycom_par: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "baycom_par: version 0.9 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+static struct net_device *baycom_device[NR_PORTS];
+
+/* --------------------------------------------------------------------- */
+
+#define PAR96_BURSTBITS 16
+#define PAR96_BURST     4
+#define PAR96_PTT       2
+#define PAR96_TXBIT     1
+#define PAR96_ACK       0x40
+#define PAR96_RXBIT     0x20
+#define PAR96_DCD       0x10
+#define PAR97_POWER     0xf8
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct baycom_state {
+	struct hdlcdrv_state hdrv;
+
+	struct pardevice *pdev;
+	unsigned int options;
+
+	struct modem_state {
+		short arb_divider;
+		unsigned char flags;
+		unsigned int shreg;
+		struct modem_state_par96 {
+			int dcd_count;
+			unsigned int dcd_shreg;
+			unsigned long descram;
+			unsigned long scram;
+		} par96;
+	} modem;
+
+#ifdef BAYCOM_DEBUG
+	struct debug_vals {
+		unsigned long last_jiffies;
+		unsigned cur_intcnt;
+		unsigned last_intcnt;
+		int cur_pllcorr;
+		int last_pllcorr;
+	} debug_vals;
+#endif /* BAYCOM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+static void __inline__ baycom_int_freq(struct baycom_state *bc)
+{
+#ifdef BAYCOM_DEBUG
+	unsigned long cur_jiffies = jiffies;
+	/*
+	 * measure the interrupt frequency
+	 */
+	bc->debug_vals.cur_intcnt++;
+	if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
+		bc->debug_vals.last_jiffies = cur_jiffies;
+		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
+		bc->debug_vals.cur_intcnt = 0;
+		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
+		bc->debug_vals.cur_pllcorr = 0;
+	}
+#endif /* BAYCOM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== PAR96 specific routines =========================
+ */
+
+#define PAR96_DESCRAM_TAP1 0x20000
+#define PAR96_DESCRAM_TAP2 0x01000
+#define PAR96_DESCRAM_TAP3 0x00001
+
+#define PAR96_DESCRAM_TAPSH1 17
+#define PAR96_DESCRAM_TAPSH2 12
+#define PAR96_DESCRAM_TAPSH3 0
+
+#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */
+#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ void par96_tx(struct net_device *dev, struct baycom_state *bc)
+{
+	int i;
+	unsigned int data = hdlcdrv_getbits(&bc->hdrv);
+	struct parport *pp = bc->pdev->port;
+
+	for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) {
+		unsigned char val = PAR97_POWER;
+		bc->modem.par96.scram = ((bc->modem.par96.scram << 1) |
+					 (bc->modem.par96.scram & 1));
+		if (!(data & 1))
+			bc->modem.par96.scram ^= 1;
+		if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1))
+			bc->modem.par96.scram ^=
+				(PAR96_SCRAM_TAPN << 1);
+		if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2))
+			val |= PAR96_TXBIT;
+		pp->ops->write_data(pp, val);
+		pp->ops->write_data(pp, val | PAR96_BURST);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ void par96_rx(struct net_device *dev, struct baycom_state *bc)
+{
+	int i;
+	unsigned int data, mask, mask2, descx;
+	struct parport *pp = bc->pdev->port;
+
+	/*
+	 * do receiver; differential decode and descramble on the fly
+	 */
+	for(data = i = 0; i < PAR96_BURSTBITS; i++) {
+		bc->modem.par96.descram = (bc->modem.par96.descram << 1);
+		if (pp->ops->read_status(pp) & PAR96_RXBIT)
+			bc->modem.par96.descram |= 1;
+		descx = bc->modem.par96.descram ^
+			(bc->modem.par96.descram >> 1);
+		/* now the diff decoded data is inverted in descram */
+		pp->ops->write_data(pp, PAR97_POWER | PAR96_PTT);
+		descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^
+			  (descx >> PAR96_DESCRAM_TAPSH2));
+		data >>= 1;
+		if (!(descx & 1))
+			data |= 0x8000;
+		pp->ops->write_data(pp, PAR97_POWER | PAR96_PTT | PAR96_BURST);
+	}
+	hdlcdrv_putbits(&bc->hdrv, data);
+	/*
+	 * do DCD algorithm
+	 */
+	if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
+		bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16)
+			| (data << 16);
+		/* search for flags and set the dcd counter appropriately */
+		for(mask = 0x1fe00, mask2 = 0xfc00, i = 0;
+		    i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
+			if ((bc->modem.par96.dcd_shreg & mask) == mask2)
+				bc->modem.par96.dcd_count = HDLCDRV_MAXFLEN+4;
+		/* check for abort/noise sequences */
+		for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0;
+		    i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
+			if (((bc->modem.par96.dcd_shreg & mask) == mask2) &&
+			    (bc->modem.par96.dcd_count >= 0))
+				bc->modem.par96.dcd_count -= HDLCDRV_MAXFLEN-10;
+		/* decrement and set the dcd variable */
+		if (bc->modem.par96.dcd_count >= 0)
+			bc->modem.par96.dcd_count -= 2;
+		hdlcdrv_setdcd(&bc->hdrv, bc->modem.par96.dcd_count > 0);
+	} else {
+		hdlcdrv_setdcd(&bc->hdrv, !!(pp->ops->read_status(pp) & PAR96_DCD));
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void par96_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *)dev_id;
+	struct baycom_state *bc = netdev_priv(dev);
+
+	baycom_int_freq(bc);
+	/*
+	 * check if transmitter active
+	 */
+	if (hdlcdrv_ptt(&bc->hdrv))
+		par96_tx(dev, bc);
+	else {
+		par96_rx(dev, bc);
+		if (--bc->modem.arb_divider <= 0) {
+			bc->modem.arb_divider = 6;
+			local_irq_enable();
+			hdlcdrv_arbitrate(dev, &bc->hdrv);
+		}
+	}
+	local_irq_enable();
+	hdlcdrv_transmitter(dev, &bc->hdrv);
+	hdlcdrv_receiver(dev, &bc->hdrv);
+        local_irq_disable();
+}
+
+/* --------------------------------------------------------------------- */
+
+static void par96_wakeup(void *handle)
+{
+        struct net_device *dev = (struct net_device *)handle;
+	struct baycom_state *bc = netdev_priv(dev);
+
+	printk(KERN_DEBUG "baycom_par: %s: why am I being woken up?\n", dev->name);
+	if (!parport_claim(bc->pdev))
+		printk(KERN_DEBUG "baycom_par: %s: I'm broken.\n", dev->name);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_open(struct net_device *dev)
+{
+	struct baycom_state *bc = netdev_priv(dev);
+	struct parport *pp;
+
+	if (!dev || !bc)
+		return -ENXIO;
+	pp = parport_find_base(dev->base_addr);
+	if (!pp) {
+		printk(KERN_ERR "baycom_par: parport at 0x%lx unknown\n", dev->base_addr);
+		return -ENXIO;
+	}
+	if (pp->irq < 0) {
+		printk(KERN_ERR "baycom_par: parport at 0x%lx has no irq\n", pp->base);
+		parport_put_port(pp);
+		return -ENXIO;
+	}
+	if ((~pp->modes) & (PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT)) {
+		printk(KERN_ERR "baycom_par: parport at 0x%lx cannot be used\n", pp->base);
+		parport_put_port(pp);
+		return -ENXIO;
+	}
+	memset(&bc->modem, 0, sizeof(bc->modem));
+	bc->hdrv.par.bitrate = 9600;
+	bc->pdev = parport_register_device(pp, dev->name, NULL, par96_wakeup, 
+				 par96_interrupt, PARPORT_DEV_EXCL, dev);
+	parport_put_port(pp);
+	if (!bc->pdev) {
+		printk(KERN_ERR "baycom_par: cannot register parport at 0x%lx\n", dev->base_addr);
+		return -ENXIO;
+	}
+	if (parport_claim(bc->pdev)) {
+		printk(KERN_ERR "baycom_par: parport at 0x%lx busy\n", pp->base);
+		parport_unregister_device(bc->pdev);
+		return -EBUSY;
+	}
+	pp = bc->pdev->port;
+	dev->irq = pp->irq;
+	pp->ops->data_forward(pp);
+        bc->hdrv.par.bitrate = 9600;
+	pp->ops->write_data(pp, PAR96_PTT | PAR97_POWER); /* switch off PTT */
+	pp->ops->enable_irq(pp);
+	printk(KERN_INFO "%s: par96 at iobase 0x%lx irq %u options 0x%x\n",
+	       bc_drvname, dev->base_addr, dev->irq, bc->options);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_close(struct net_device *dev)
+{
+	struct baycom_state *bc = netdev_priv(dev);
+	struct parport *pp;
+
+	if (!dev || !bc)
+		return -EINVAL;
+	pp = bc->pdev->port;
+	/* disable interrupt */
+	pp->ops->disable_irq(pp);
+	/* switch off PTT */
+	pp->ops->write_data(pp, PAR96_PTT | PAR97_POWER);
+	parport_release(bc->pdev);
+	parport_unregister_device(bc->pdev);
+	printk(KERN_INFO "%s: close par96 at iobase 0x%lx irq %u\n",
+	       bc_drvname, dev->base_addr, dev->irq);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== hdlcdrv driver interface =========================
+ */
+
+static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
+			struct hdlcdrv_ioctl *hi, int cmd);
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops par96_ops = {
+	.drvname = bc_drvname,
+	.drvinfo = bc_drvinfo,
+	.open    = par96_open,
+	.close   = par96_close,
+	.ioctl   = baycom_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_setmode(struct baycom_state *bc, const char *modestr)
+{
+	if (!strncmp(modestr, "picpar", 6))
+		bc->options = 0;
+	else if (!strncmp(modestr, "par96", 5))
+		bc->options = BAYCOM_OPTIONS_SOFTDCD;
+	else
+		bc->options = !!strchr(modestr, '*');
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
+			struct hdlcdrv_ioctl *hi, int cmd)
+{
+	struct baycom_state *bc;
+	struct baycom_ioctl bi;
+
+	if (!dev)
+		return -EINVAL;
+
+	bc = netdev_priv(dev);
+	BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC);
+
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+	switch (hi->cmd) {
+	default:
+		break;
+
+	case HDLCDRVCTL_GETMODE:
+		strcpy(hi->data.modename, bc->options ? "par96" : "picpar");
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_SETMODE:
+		if (netif_running(dev) || !capable(CAP_NET_ADMIN))
+			return -EACCES;
+		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
+		return baycom_setmode(bc, hi->data.modename);
+
+	case HDLCDRVCTL_MODELIST:
+		strcpy(hi->data.modename, "par96,picpar");
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_MODEMPARMASK:
+		return HDLCDRV_PARMASK_IOBASE;
+
+	}
+
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+	switch (bi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+#ifdef BAYCOM_DEBUG
+	case BAYCOMCTL_GETDEBUG:
+		bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
+		bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
+		bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
+		break;
+#endif /* BAYCOM_DEBUG */
+
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * command line settable parameters
+ */
+static const char *mode[NR_PORTS] = { "picpar", };
+static int iobase[NR_PORTS] = { 0x378, };
+
+module_param_array(mode, charp, NULL, 0);
+MODULE_PARM_DESC(mode, "baycom operating mode; eg. par96 or picpar");
+module_param_array(iobase, int, NULL, 0);
+MODULE_PARM_DESC(iobase, "baycom io base address");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Baycom par96 and picpar amateur radio modem driver");
+MODULE_LICENSE("GPL");
+
+/* --------------------------------------------------------------------- */
+
+static int __init init_baycompar(void)
+{
+	int i, found = 0;
+	char set_hw = 1;
+
+	printk(bc_drvinfo);
+	/*
+	 * register net devices
+	 */
+	for (i = 0; i < NR_PORTS; i++) {
+		struct net_device *dev;
+		struct baycom_state *bc;
+		char ifname[IFNAMSIZ];
+
+		sprintf(ifname, "bcp%d", i);
+
+		if (!mode[i])
+			set_hw = 0;
+		if (!set_hw)
+			iobase[i] = 0;
+
+		dev = hdlcdrv_register(&par96_ops,
+				       sizeof(struct baycom_state),
+				       ifname, iobase[i], 0, 0);
+		if (IS_ERR(dev)) 
+			break;
+
+		bc = netdev_priv(dev);
+		if (set_hw && baycom_setmode(bc, mode[i]))
+			set_hw = 0;
+		found++;
+		baycom_device[i] = dev;
+	}
+
+	if (!found)
+		return -ENXIO;
+	return 0;
+}
+
+static void __exit cleanup_baycompar(void)
+{
+	int i;
+
+	for(i = 0; i < NR_PORTS; i++) {
+		struct net_device *dev = baycom_device[i];
+
+		if (dev)
+			hdlcdrv_unregister(dev);
+	}
+}
+
+module_init(init_baycompar);
+module_exit(cleanup_baycompar);
+
+/* --------------------------------------------------------------------- */
+
+#ifndef MODULE
+
+/*
+ * format: baycom_par=io,mode
+ * mode: par96,picpar
+ */
+
+static int __init baycom_par_setup(char *str)
+{
+        static unsigned nr_dev;
+	int ints[2];
+
+        if (nr_dev >= NR_PORTS)
+                return 0;
+        str = get_options(str, 2, ints);
+        if (ints[0] < 1)
+                return 0;
+        mode[nr_dev] = str;
+        iobase[nr_dev] = ints[1];
+	nr_dev++;
+	return 1;
+}
+
+__setup("baycom_par=", baycom_par_setup);
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c
new file mode 100644
index 0000000..25f270b
--- /dev/null
+++ b/drivers/net/hamradio/baycom_ser_fdx.c
@@ -0,0 +1,704 @@
+/*****************************************************************************/
+
+/*
+ *	baycom_ser_fdx.c  -- baycom ser12 fullduplex radio modem driver.
+ *
+ *	Copyright (C) 1996-2000  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  Supported modems
+ *
+ *  ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
+ *          of a modulator/demodulator chip, usually a TI TCM3105. The computer
+ *          is responsible for regenerating the receiver bit clock, as well as
+ *          for handling the HDLC protocol. The modem connects to a serial port,
+ *          hence the name. Since the serial port is not used as an async serial
+ *          port, the kernel driver for serial ports cannot be used, and this
+ *          driver only supports standard serial hardware (8250, 16450, 16550A)
+ *
+ *          This modem usually draws its supply current out of the otherwise unused
+ *          TXD pin of the serial port. Thus a contignuous stream of 0x00-bytes
+ *          is transmitted to achieve a positive supply voltage.
+ *
+ *  hsk:    This is a 4800 baud FSK modem, designed for TNC use. It works fine
+ *          in 'baycom-mode' :-)  In contrast to the TCM3105 modem, power is
+ *          externally supplied. So there's no need to provide the 0x00-byte-stream
+ *          when receiving or idle, which drastically reduces interrupt load.
+ *
+ *  Command line options (insmod command line)
+ *
+ *  mode     ser#    hardware DCD
+ *           ser#*   software DCD
+ *           ser#+   hardware DCD, inverted signal at DCD pin
+ *           '#' denotes the baud rate / 100, eg. ser12* is '1200 baud, soft DCD'
+ *  iobase   base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8
+ *  baud     baud rate (between 300 and 4800)
+ *  irq      interrupt line of the port; common values are 4,3
+ *
+ *
+ *  History:
+ *   0.1  26.06.1996  Adapted from baycom.c and made network driver interface
+ *        18.10.1996  Changed to new user space access routines (copy_{to,from}_user)
+ *   0.3  26.04.1997  init code/data tagged
+ *   0.4  08.07.1997  alternative ser12 decoding algorithm (uses delta CTS ints)
+ *   0.5  11.11.1997  ser12/par96 split into separate files
+ *   0.6  24.01.1998  Thorsten Kranzkowski, dl8bcu and Thomas Sailer:
+ *                    reduced interrupt load in transmit case
+ *                    reworked receiver
+ *   0.7  03.08.1999  adapt to Linus' new __setup/__initcall
+ *   0.8  10.08.1999  use module_init/module_exit
+ *   0.9  12.02.2000  adapted to softnet driver interface
+ *   0.10 03.07.2000  fix interface name handling
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/hdlcdrv.h>
+#include <linux/baycom.h>
+
+/* --------------------------------------------------------------------- */
+
+#define BAYCOM_DEBUG
+
+/* --------------------------------------------------------------------- */
+
+static const char bc_drvname[] = "baycom_ser_fdx";
+static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "baycom_ser_fdx: version 0.10 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+static struct net_device *baycom_device[NR_PORTS];
+
+/* --------------------------------------------------------------------- */
+
+#define RBR(iobase) (iobase+0)
+#define THR(iobase) (iobase+0)
+#define IER(iobase) (iobase+1)
+#define IIR(iobase) (iobase+2)
+#define FCR(iobase) (iobase+2)
+#define LCR(iobase) (iobase+3)
+#define MCR(iobase) (iobase+4)
+#define LSR(iobase) (iobase+5)
+#define MSR(iobase) (iobase+6)
+#define SCR(iobase) (iobase+7)
+#define DLL(iobase) (iobase+0)
+#define DLM(iobase) (iobase+1)
+
+#define SER12_EXTENT 8
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct baycom_state {
+	struct hdlcdrv_state hdrv;
+
+	unsigned int baud, baud_us, baud_arbdiv, baud_uartdiv, baud_dcdtimeout;
+	int opt_dcd;
+
+	struct modem_state {
+		unsigned char flags;
+		unsigned char ptt;
+		unsigned int shreg;
+		struct modem_state_ser12 {
+			unsigned char tx_bit;
+			unsigned char last_rxbit;
+			int dcd_sum0, dcd_sum1, dcd_sum2;
+			int dcd_time;
+			unsigned int pll_time;
+			unsigned int txshreg;
+		} ser12;
+	} modem;
+
+#ifdef BAYCOM_DEBUG
+	struct debug_vals {
+		unsigned long last_jiffies;
+		unsigned cur_intcnt;
+		unsigned last_intcnt;
+		int cur_pllcorr;
+		int last_pllcorr;
+	} debug_vals;
+#endif /* BAYCOM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+static inline void baycom_int_freq(struct baycom_state *bc)
+{
+#ifdef BAYCOM_DEBUG
+	unsigned long cur_jiffies = jiffies;
+	/*
+	 * measure the interrupt frequency
+	 */
+	bc->debug_vals.cur_intcnt++;
+	if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
+		bc->debug_vals.last_jiffies = cur_jiffies;
+		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
+		bc->debug_vals.cur_intcnt = 0;
+		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
+		bc->debug_vals.cur_pllcorr = 0;
+	}
+#endif /* BAYCOM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== SER12 specific routines =========================
+ */
+
+/* --------------------------------------------------------------------- */
+
+static inline void ser12_set_divisor(struct net_device *dev,
+                                     unsigned int divisor)
+{
+        outb(0x81, LCR(dev->base_addr));        /* DLAB = 1 */
+        outb(divisor, DLL(dev->base_addr));
+        outb(divisor >> 8, DLM(dev->base_addr));
+        outb(0x01, LCR(dev->base_addr));        /* word length = 6 */
+        /*
+         * make sure the next interrupt is generated;
+         * 0 must be used to power the modem; the modem draws its
+         * power from the TxD line
+         */
+        outb(0x00, THR(dev->base_addr));
+        /*
+         * it is important not to set the divider while transmitting;
+         * this reportedly makes some UARTs generating interrupts
+         * in the hundredthousands per second region
+         * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno)
+         */
+}
+
+/* --------------------------------------------------------------------- */
+
+#if 0
+static inline unsigned int hweight16(unsigned int w)
+        __attribute__ ((unused));
+static inline unsigned int hweight8(unsigned int w)
+        __attribute__ ((unused));
+
+static inline unsigned int hweight16(unsigned int w)
+{
+        unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
+        res = (res & 0x3333) + ((res >> 2) & 0x3333);
+        res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
+        return (res & 0x00FF) + ((res >> 8) & 0x00FF);
+}
+
+static inline unsigned int hweight8(unsigned int w)
+{
+        unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
+        res = (res & 0x33) + ((res >> 2) & 0x33);
+        return (res & 0x0F) + ((res >> 4) & 0x0F);
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ void ser12_rx(struct net_device *dev, struct baycom_state *bc, struct timeval *tv, unsigned char curs)
+{
+	int timediff;
+	int bdus8 = bc->baud_us >> 3;
+	int bdus4 = bc->baud_us >> 2;
+	int bdus2 = bc->baud_us >> 1;
+
+	timediff = 1000000 + tv->tv_usec - bc->modem.ser12.pll_time;
+	while (timediff >= 500000)
+		timediff -= 1000000;
+	while (timediff >= bdus2) {
+		timediff -= bc->baud_us;
+		bc->modem.ser12.pll_time += bc->baud_us;
+		bc->modem.ser12.dcd_time--;
+		/* first check if there is room to add a bit */
+		if (bc->modem.shreg & 1) {
+			hdlcdrv_putbits(&bc->hdrv, (bc->modem.shreg >> 1) ^ 0xffff);
+			bc->modem.shreg = 0x10000;
+		}
+		/* add a one bit */
+		bc->modem.shreg >>= 1;
+	}
+	if (bc->modem.ser12.dcd_time <= 0) {
+		if (!bc->opt_dcd)
+			hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + 
+						   bc->modem.ser12.dcd_sum1 + 
+						   bc->modem.ser12.dcd_sum2) < 0);
+		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+		bc->modem.ser12.dcd_sum0 = 2; /* slight bias */
+		bc->modem.ser12.dcd_time += 120;
+	}
+	if (bc->modem.ser12.last_rxbit != curs) {
+		bc->modem.ser12.last_rxbit = curs;
+		bc->modem.shreg |= 0x10000;
+		/* adjust the PLL */
+		if (timediff > 0)
+			bc->modem.ser12.pll_time += bdus8;
+		else
+			bc->modem.ser12.pll_time += 1000000 - bdus8;
+		/* update DCD */
+		if (abs(timediff) > bdus4)
+			bc->modem.ser12.dcd_sum0 += 4;
+		else
+			bc->modem.ser12.dcd_sum0--;
+#ifdef BAYCOM_DEBUG
+		bc->debug_vals.cur_pllcorr = timediff;
+#endif /* BAYCOM_DEBUG */
+	}
+	while (bc->modem.ser12.pll_time >= 1000000)
+		bc->modem.ser12.pll_time -= 1000000;
+}
+
+/* --------------------------------------------------------------------- */
+
+static irqreturn_t ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *)dev_id;
+	struct baycom_state *bc = netdev_priv(dev);
+	struct timeval tv;
+	unsigned char iir, msr;
+	unsigned int txcount = 0;
+
+	if (!bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+		return IRQ_NONE;
+	/* fast way out for shared irq */
+	if ((iir = inb(IIR(dev->base_addr))) & 1) 	
+		return IRQ_NONE;
+	/* get current time */
+	do_gettimeofday(&tv);
+	msr = inb(MSR(dev->base_addr));
+	/* delta DCD */
+	if ((msr & 8) && bc->opt_dcd)
+		hdlcdrv_setdcd(&bc->hdrv, !((msr ^ bc->opt_dcd) & 0x80));
+	do {
+		switch (iir & 6) {
+		case 6:
+			inb(LSR(dev->base_addr));
+			break;
+			
+		case 4:
+			inb(RBR(dev->base_addr));
+			break;
+			
+		case 2:
+			/*
+			 * make sure the next interrupt is generated;
+			 * 0 must be used to power the modem; the modem draws its
+			 * power from the TxD line
+			 */
+			outb(0x00, THR(dev->base_addr));
+			baycom_int_freq(bc);
+			txcount++;
+			/*
+			 * first output the last bit (!) then call HDLC transmitter,
+			 * since this may take quite long
+			 */
+			if (bc->modem.ptt)
+				outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
+			else
+				outb(0x0d, MCR(dev->base_addr));       /* transmitter off */
+			break;
+			
+		default:
+			msr = inb(MSR(dev->base_addr));
+			/* delta DCD */
+			if ((msr & 8) && bc->opt_dcd) 
+				hdlcdrv_setdcd(&bc->hdrv, !((msr ^ bc->opt_dcd) & 0x80));
+			break;
+		}
+		iir = inb(IIR(dev->base_addr));
+	} while (!(iir & 1));
+	ser12_rx(dev, bc, &tv, msr & 0x10); /* CTS */
+	if (bc->modem.ptt && txcount) {
+		if (bc->modem.ser12.txshreg <= 1) {
+			bc->modem.ser12.txshreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
+			if (!hdlcdrv_ptt(&bc->hdrv)) {
+				ser12_set_divisor(dev, 115200/100/8);
+				bc->modem.ptt = 0;
+				goto end_transmit;
+			}
+		}
+		bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ (bc->modem.ser12.txshreg & 1));
+		bc->modem.ser12.txshreg >>= 1;
+	}
+ end_transmit:
+	local_irq_enable();
+	if (!bc->modem.ptt && txcount) {
+		hdlcdrv_arbitrate(dev, &bc->hdrv);
+		if (hdlcdrv_ptt(&bc->hdrv)) {
+			ser12_set_divisor(dev, bc->baud_uartdiv);
+			bc->modem.ser12.txshreg = 1;
+			bc->modem.ptt = 1;
+		}
+	}
+	hdlcdrv_transmitter(dev, &bc->hdrv);
+	hdlcdrv_receiver(dev, &bc->hdrv);
+	local_irq_disable();
+	return IRQ_HANDLED;
+}
+
+/* --------------------------------------------------------------------- */
+
+enum uart { c_uart_unknown, c_uart_8250,
+	    c_uart_16450, c_uart_16550, c_uart_16550A};
+static const char *uart_str[] = { 
+	"unknown", "8250", "16450", "16550", "16550A" 
+};
+
+static enum uart ser12_check_uart(unsigned int iobase)
+{
+	unsigned char b1,b2,b3;
+	enum uart u;
+	enum uart uart_tab[] =
+		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
+
+	b1 = inb(MCR(iobase));
+	outb(b1 | 0x10, MCR(iobase));	/* loopback mode */
+	b2 = inb(MSR(iobase));
+	outb(0x1a, MCR(iobase));
+	b3 = inb(MSR(iobase)) & 0xf0;
+	outb(b1, MCR(iobase));			/* restore old values */
+	outb(b2, MSR(iobase));
+	if (b3 != 0x90)
+		return c_uart_unknown;
+	inb(RBR(iobase));
+	inb(RBR(iobase));
+	outb(0x01, FCR(iobase));		/* enable FIFOs */
+	u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
+	if (u == c_uart_16450) {
+		outb(0x5a, SCR(iobase));
+		b1 = inb(SCR(iobase));
+		outb(0xa5, SCR(iobase));
+		b2 = inb(SCR(iobase));
+		if ((b1 != 0x5a) || (b2 != 0xa5))
+			u = c_uart_8250;
+	}
+	return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_open(struct net_device *dev)
+{
+	struct baycom_state *bc = netdev_priv(dev);
+	enum uart u;
+
+	if (!dev || !bc)
+		return -ENXIO;
+	if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT ||
+	    dev->irq < 2 || dev->irq > 15)
+		return -ENXIO;
+	if (bc->baud < 300 || bc->baud > 4800)
+		return -EINVAL;
+	if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser_fdx")) {
+		printk(KERN_WARNING "BAYCOM_SER_FSX: I/O port 0x%04lx busy \n", 
+		       dev->base_addr);
+		return -EACCES;
+	}
+	memset(&bc->modem, 0, sizeof(bc->modem));
+	bc->hdrv.par.bitrate = bc->baud;
+	bc->baud_us = 1000000/bc->baud;
+	bc->baud_uartdiv = (115200/8)/bc->baud;
+	if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown){
+		release_region(dev->base_addr, SER12_EXTENT);
+		return -EIO;
+	}
+	outb(0, FCR(dev->base_addr));  /* disable FIFOs */
+	outb(0x0d, MCR(dev->base_addr));
+	outb(0, IER(dev->base_addr));
+	if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT | SA_SHIRQ,
+			"baycom_ser_fdx", dev)) {
+		release_region(dev->base_addr, SER12_EXTENT);
+		return -EBUSY;
+	}
+	/*
+	 * set the SIO to 6 Bits/character; during receive,
+	 * the baud rate is set to produce 100 ints/sec
+	 * to feed the channel arbitration process,
+	 * during transmit to baud ints/sec to run
+	 * the transmitter
+	 */
+	ser12_set_divisor(dev, 115200/100/8);
+	/*
+	 * enable transmitter empty interrupt and modem status interrupt
+	 */
+	outb(0x0a, IER(dev->base_addr));
+	/*
+	 * make sure the next interrupt is generated;
+	 * 0 must be used to power the modem; the modem draws its
+	 * power from the TxD line
+	 */
+	outb(0x00, THR(dev->base_addr));
+	hdlcdrv_setdcd(&bc->hdrv, 0);
+	printk(KERN_INFO "%s: ser_fdx at iobase 0x%lx irq %u baud %u uart %s\n",
+	       bc_drvname, dev->base_addr, dev->irq, bc->baud, uart_str[u]);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_close(struct net_device *dev)
+{
+	struct baycom_state *bc = netdev_priv(dev);
+
+	if (!dev || !bc)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	outb(0, IER(dev->base_addr));
+	outb(1, MCR(dev->base_addr));
+	free_irq(dev->irq, dev);
+	release_region(dev->base_addr, SER12_EXTENT);
+	printk(KERN_INFO "%s: close ser_fdx at iobase 0x%lx irq %u\n",
+	       bc_drvname, dev->base_addr, dev->irq);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== hdlcdrv driver interface =========================
+ */
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
+			struct hdlcdrv_ioctl *hi, int cmd);
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops ser12_ops = {
+	.drvname = bc_drvname,
+	.drvinfo = bc_drvinfo,
+	.open    = ser12_open,
+	.close   = ser12_close,
+	.ioctl   = baycom_ioctl,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_setmode(struct baycom_state *bc, const char *modestr)
+{
+	unsigned int baud;
+
+	if (!strncmp(modestr, "ser", 3)) {
+		baud = simple_strtoul(modestr+3, NULL, 10);
+		if (baud >= 3 && baud <= 48)
+			bc->baud = baud*100;
+	}
+	if (strchr(modestr, '*'))
+		bc->opt_dcd = 0;
+	else if (strchr(modestr, '+'))
+		bc->opt_dcd = -1;
+	else
+		bc->opt_dcd = 1;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
+			struct hdlcdrv_ioctl *hi, int cmd)
+{
+	struct baycom_state *bc;
+	struct baycom_ioctl bi;
+
+	if (!dev)
+		return -EINVAL;
+
+	bc = netdev_priv(dev);
+	BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC);
+
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+	switch (hi->cmd) {
+	default:
+		break;
+
+	case HDLCDRVCTL_GETMODE:
+		sprintf(hi->data.modename, "ser%u", bc->baud / 100);
+		if (bc->opt_dcd <= 0)
+			strcat(hi->data.modename, (!bc->opt_dcd) ? "*" : "+");
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_SETMODE:
+		if (netif_running(dev) || !capable(CAP_NET_ADMIN))
+			return -EACCES;
+		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
+		return baycom_setmode(bc, hi->data.modename);
+
+	case HDLCDRVCTL_MODELIST:
+		strcpy(hi->data.modename, "ser12,ser3,ser24");
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_MODEMPARMASK:
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ;
+
+	}
+
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+	switch (bi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+#ifdef BAYCOM_DEBUG
+	case BAYCOMCTL_GETDEBUG:
+		bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
+		bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
+		bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
+		break;
+#endif /* BAYCOM_DEBUG */
+
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * command line settable parameters
+ */
+static char *mode[NR_PORTS] = { "ser12*", };
+static int iobase[NR_PORTS] = { 0x3f8, };
+static int irq[NR_PORTS] = { 4, };
+static int baud[NR_PORTS] = { [0 ... NR_PORTS-1] = 1200 };
+
+module_param_array(mode, charp, NULL, 0);
+MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD");
+module_param_array(iobase, int, NULL, 0);
+MODULE_PARM_DESC(iobase, "baycom io base address");
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(irq, "baycom irq number");
+module_param_array(baud, int, NULL, 0);
+MODULE_PARM_DESC(baud, "baycom baud rate (300 to 4800)");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Baycom ser12 full duplex amateur radio modem driver");
+MODULE_LICENSE("GPL");
+
+/* --------------------------------------------------------------------- */
+
+static int __init init_baycomserfdx(void)
+{
+	int i, found = 0;
+	char set_hw = 1;
+
+	printk(bc_drvinfo);
+	/*
+	 * register net devices
+	 */
+	for (i = 0; i < NR_PORTS; i++) {
+		struct net_device *dev;
+		struct baycom_state *bc;
+		char ifname[IFNAMSIZ];
+
+		sprintf(ifname, "bcsf%d", i);
+
+		if (!mode[i])
+			set_hw = 0;
+		if (!set_hw)
+			iobase[i] = irq[i] = 0;
+
+		dev = hdlcdrv_register(&ser12_ops, 
+				       sizeof(struct baycom_state),
+				       ifname, iobase[i], irq[i], 0);
+		if (IS_ERR(dev)) 
+			break;
+
+		bc = netdev_priv(dev);
+		if (set_hw && baycom_setmode(bc, mode[i]))
+			set_hw = 0;
+		bc->baud = baud[i];
+		found++;
+		baycom_device[i] = dev;
+	}
+
+	if (!found)
+		return -ENXIO;
+	return 0;
+}
+
+static void __exit cleanup_baycomserfdx(void)
+{
+	int i;
+
+	for(i = 0; i < NR_PORTS; i++) {
+		struct net_device *dev = baycom_device[i];
+		if (dev) 
+			hdlcdrv_unregister(dev);
+	}
+}
+
+module_init(init_baycomserfdx);
+module_exit(cleanup_baycomserfdx);
+
+/* --------------------------------------------------------------------- */
+
+#ifndef MODULE
+
+/*
+ * format: baycom_ser_fdx=io,irq,mode
+ * mode: ser#    hardware DCD
+ *       ser#*   software DCD
+ *       ser#+   hardware DCD, inverted signal at DCD pin
+ * '#' denotes the baud rate / 100, eg. ser12* is '1200 baud, soft DCD'
+ */
+
+static int __init baycom_ser_fdx_setup(char *str)
+{
+        static unsigned nr_dev;
+        int ints[4];
+
+        if (nr_dev >= NR_PORTS)
+                return 0;
+        str = get_options(str, 4, ints);
+        if (ints[0] < 2)
+                return 0;
+        mode[nr_dev] = str;
+        iobase[nr_dev] = ints[1];
+        irq[nr_dev] = ints[2];
+	if (ints[0] >= 3)
+		baud[nr_dev] = ints[3];
+	nr_dev++;
+	return 1;
+}
+
+__setup("baycom_ser_fdx=", baycom_ser_fdx_setup);
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c
new file mode 100644
index 0000000..eead85d
--- /dev/null
+++ b/drivers/net/hamradio/baycom_ser_hdx.c
@@ -0,0 +1,740 @@
+/*****************************************************************************/
+
+/*
+ *	baycom_ser_hdx.c  -- baycom ser12 halfduplex radio modem driver.
+ *
+ *	Copyright (C) 1996-2000  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  Supported modems
+ *
+ *  ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
+ *          of a modulator/demodulator chip, usually a TI TCM3105. The computer
+ *          is responsible for regenerating the receiver bit clock, as well as
+ *          for handling the HDLC protocol. The modem connects to a serial port,
+ *          hence the name. Since the serial port is not used as an async serial
+ *          port, the kernel driver for serial ports cannot be used, and this
+ *          driver only supports standard serial hardware (8250, 16450, 16550A)
+ *
+ *
+ *  Command line options (insmod command line)
+ *
+ *  mode     ser12    hardware DCD
+ *           ser12*   software DCD
+ *           ser12@   hardware/software DCD, i.e. no explicit DCD signal but hardware
+ *                    mutes audio input to the modem
+ *           ser12+   hardware DCD, inverted signal at DCD pin
+ *  iobase   base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8
+ *  irq      interrupt line of the port; common values are 4,3
+ *
+ *
+ *  History:
+ *   0.1  26.06.1996  Adapted from baycom.c and made network driver interface
+ *        18.10.1996  Changed to new user space access routines (copy_{to,from}_user)
+ *   0.3  26.04.1997  init code/data tagged
+ *   0.4  08.07.1997  alternative ser12 decoding algorithm (uses delta CTS ints)
+ *   0.5  11.11.1997  ser12/par96 split into separate files
+ *   0.6  14.04.1998  cleanups
+ *   0.7  03.08.1999  adapt to Linus' new __setup/__initcall
+ *   0.8  10.08.1999  use module_init/module_exit
+ *   0.9  12.02.2000  adapted to softnet driver interface
+ *   0.10 03.07.2000  fix interface name handling
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/hdlcdrv.h>
+#include <linux/baycom.h>
+
+/* --------------------------------------------------------------------- */
+
+#define BAYCOM_DEBUG
+
+/* --------------------------------------------------------------------- */
+
+static const char bc_drvname[] = "baycom_ser_hdx";
+static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "baycom_ser_hdx: version 0.10 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+static struct net_device *baycom_device[NR_PORTS];
+
+/* --------------------------------------------------------------------- */
+
+#define RBR(iobase) (iobase+0)
+#define THR(iobase) (iobase+0)
+#define IER(iobase) (iobase+1)
+#define IIR(iobase) (iobase+2)
+#define FCR(iobase) (iobase+2)
+#define LCR(iobase) (iobase+3)
+#define MCR(iobase) (iobase+4)
+#define LSR(iobase) (iobase+5)
+#define MSR(iobase) (iobase+6)
+#define SCR(iobase) (iobase+7)
+#define DLL(iobase) (iobase+0)
+#define DLM(iobase) (iobase+1)
+
+#define SER12_EXTENT 8
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct baycom_state {
+	struct hdlcdrv_state hdrv;
+
+	int opt_dcd;
+
+	struct modem_state {
+		short arb_divider;
+		unsigned char flags;
+		unsigned int shreg;
+		struct modem_state_ser12 {
+			unsigned char tx_bit;
+			int dcd_sum0, dcd_sum1, dcd_sum2;
+			unsigned char last_sample;
+			unsigned char last_rxbit;
+			unsigned int dcd_shreg;
+			unsigned int dcd_time;
+			unsigned int bit_pll;
+			unsigned char interm_sample;
+		} ser12;
+	} modem;
+
+#ifdef BAYCOM_DEBUG
+	struct debug_vals {
+		unsigned long last_jiffies;
+		unsigned cur_intcnt;
+		unsigned last_intcnt;
+		int cur_pllcorr;
+		int last_pllcorr;
+	} debug_vals;
+#endif /* BAYCOM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+static inline void baycom_int_freq(struct baycom_state *bc)
+{
+#ifdef BAYCOM_DEBUG
+	unsigned long cur_jiffies = jiffies;
+	/*
+	 * measure the interrupt frequency
+	 */
+	bc->debug_vals.cur_intcnt++;
+	if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
+		bc->debug_vals.last_jiffies = cur_jiffies;
+		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
+		bc->debug_vals.cur_intcnt = 0;
+		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
+		bc->debug_vals.cur_pllcorr = 0;
+	}
+#endif /* BAYCOM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== SER12 specific routines =========================
+ */
+
+static inline void ser12_set_divisor(struct net_device *dev,
+				     unsigned char divisor)
+{
+	outb(0x81, LCR(dev->base_addr));	/* DLAB = 1 */
+	outb(divisor, DLL(dev->base_addr));
+	outb(0, DLM(dev->base_addr));
+	outb(0x01, LCR(dev->base_addr));	/* word length = 6 */
+	/*
+	 * make sure the next interrupt is generated;
+	 * 0 must be used to power the modem; the modem draws its
+	 * power from the TxD line
+	 */
+	outb(0x00, THR(dev->base_addr));
+	/*
+	 * it is important not to set the divider while transmitting;
+	 * this reportedly makes some UARTs generating interrupts
+	 * in the hundredthousands per second region
+	 * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno)
+	 */
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * must call the TX arbitrator every 10ms
+ */
+#define SER12_ARB_DIVIDER(bc)  (bc->opt_dcd ? 24 : 36)
+			       
+#define SER12_DCD_INTERVAL(bc) (bc->opt_dcd ? 12 : 240)
+
+static inline void ser12_tx(struct net_device *dev, struct baycom_state *bc)
+{
+	/* one interrupt per channel bit */
+	ser12_set_divisor(dev, 12);
+	/*
+	 * first output the last bit (!) then call HDLC transmitter,
+	 * since this may take quite long
+	 */
+	outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
+	if (bc->modem.shreg <= 1)
+		bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
+	bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^
+				   (bc->modem.shreg & 1));
+	bc->modem.shreg >>= 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void ser12_rx(struct net_device *dev, struct baycom_state *bc)
+{
+	unsigned char cur_s;
+	/*
+	 * do demodulator
+	 */
+	cur_s = inb(MSR(dev->base_addr)) & 0x10;	/* the CTS line */
+	hdlcdrv_channelbit(&bc->hdrv, cur_s);
+	bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) |
+		(cur_s != bc->modem.ser12.last_sample);
+	bc->modem.ser12.last_sample = cur_s;
+	if(bc->modem.ser12.dcd_shreg & 1) {
+		if (!bc->opt_dcd) {
+			unsigned int dcdspos, dcdsneg;
+
+			dcdspos = dcdsneg = 0;
+			dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
+			if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
+				dcdspos += 2;
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
+
+			bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
+		} else
+			bc->modem.ser12.dcd_sum0--;
+	}
+	if(!bc->modem.ser12.dcd_time) {
+		hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
+					   bc->modem.ser12.dcd_sum1 +
+					   bc->modem.ser12.dcd_sum2) < 0);
+		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+		/* offset to ensure DCD off on silent input */
+		bc->modem.ser12.dcd_sum0 = 2;
+		bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
+	}
+	bc->modem.ser12.dcd_time--;
+	if (!bc->opt_dcd) {
+		/*
+		 * PLL code for the improved software DCD algorithm
+		 */
+		if (bc->modem.ser12.interm_sample) {
+			/*
+			 * intermediate sample; set timing correction to normal
+			 */
+			ser12_set_divisor(dev, 4);
+		} else {
+			/*
+			 * do PLL correction and call HDLC receiver
+			 */
+			switch (bc->modem.ser12.dcd_shreg & 7) {
+			case 1: /* transition too late */
+				ser12_set_divisor(dev, 5);
+#ifdef BAYCOM_DEBUG
+				bc->debug_vals.cur_pllcorr++;
+#endif /* BAYCOM_DEBUG */
+				break;
+			case 4:	/* transition too early */
+				ser12_set_divisor(dev, 3);
+#ifdef BAYCOM_DEBUG
+				bc->debug_vals.cur_pllcorr--;
+#endif /* BAYCOM_DEBUG */
+				break;
+			default:
+				ser12_set_divisor(dev, 4);
+				break;
+			}
+			bc->modem.shreg >>= 1;
+			if (bc->modem.ser12.last_sample ==
+			    bc->modem.ser12.last_rxbit)
+				bc->modem.shreg |= 0x10000;
+			bc->modem.ser12.last_rxbit =
+				bc->modem.ser12.last_sample;
+		}
+		if (++bc->modem.ser12.interm_sample >= 3)
+			bc->modem.ser12.interm_sample = 0;
+		/*
+		 * DCD stuff
+		 */
+		if (bc->modem.ser12.dcd_shreg & 1) {
+			unsigned int dcdspos, dcdsneg;
+
+			dcdspos = dcdsneg = 0;
+			dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
+			dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
+				<< 1;
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
+
+			bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
+		}
+	} else {
+		/*
+		 * PLL algorithm for the hardware squelch DCD algorithm
+		 */
+		if (bc->modem.ser12.interm_sample) {
+			/*
+			 * intermediate sample; set timing correction to normal
+			 */
+			ser12_set_divisor(dev, 6);
+		} else {
+			/*
+			 * do PLL correction and call HDLC receiver
+			 */
+			switch (bc->modem.ser12.dcd_shreg & 3) {
+			case 1: /* transition too late */
+				ser12_set_divisor(dev, 7);
+#ifdef BAYCOM_DEBUG
+				bc->debug_vals.cur_pllcorr++;
+#endif /* BAYCOM_DEBUG */
+				break;
+			case 2:	/* transition too early */
+				ser12_set_divisor(dev, 5);
+#ifdef BAYCOM_DEBUG
+				bc->debug_vals.cur_pllcorr--;
+#endif /* BAYCOM_DEBUG */
+				break;
+			default:
+				ser12_set_divisor(dev, 6);
+				break;
+			}
+			bc->modem.shreg >>= 1;
+			if (bc->modem.ser12.last_sample ==
+			    bc->modem.ser12.last_rxbit)
+				bc->modem.shreg |= 0x10000;
+			bc->modem.ser12.last_rxbit =
+				bc->modem.ser12.last_sample;
+		}
+		bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
+		/*
+		 * DCD stuff
+		 */
+		bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1);
+	}
+	outb(0x0d, MCR(dev->base_addr));		/* transmitter off */
+	if (bc->modem.shreg & 1) {
+		hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
+		bc->modem.shreg = 0x10000;
+	}
+	if(!bc->modem.ser12.dcd_time) {
+		if (bc->opt_dcd & 1) 
+			hdlcdrv_setdcd(&bc->hdrv, !((inb(MSR(dev->base_addr)) ^ bc->opt_dcd) & 0x80));
+		else
+			hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
+						   bc->modem.ser12.dcd_sum1 +
+						   bc->modem.ser12.dcd_sum2) < 0);
+		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+		/* offset to ensure DCD off on silent input */
+		bc->modem.ser12.dcd_sum0 = 2;
+		bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
+	}
+	bc->modem.ser12.dcd_time--;
+}
+
+/* --------------------------------------------------------------------- */
+
+static irqreturn_t ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *)dev_id;
+	struct baycom_state *bc = netdev_priv(dev);
+	unsigned char iir;
+
+	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+		return IRQ_NONE;
+	/* fast way out */
+	if ((iir = inb(IIR(dev->base_addr))) & 1)
+		return IRQ_NONE;
+	baycom_int_freq(bc);
+	do {
+		switch (iir & 6) {
+		case 6:
+			inb(LSR(dev->base_addr));
+			break;
+			
+		case 4:
+			inb(RBR(dev->base_addr));
+			break;
+			
+		case 2:
+			/*
+			 * check if transmitter active
+			 */
+			if (hdlcdrv_ptt(&bc->hdrv))
+				ser12_tx(dev, bc);
+			else {
+				ser12_rx(dev, bc);
+				bc->modem.arb_divider--;
+			}
+			outb(0x00, THR(dev->base_addr));
+			break;
+			
+		default:
+			inb(MSR(dev->base_addr));
+			break;
+		}
+		iir = inb(IIR(dev->base_addr));
+	} while (!(iir & 1));
+	if (bc->modem.arb_divider <= 0) {
+		bc->modem.arb_divider = SER12_ARB_DIVIDER(bc);
+		local_irq_enable();
+		hdlcdrv_arbitrate(dev, &bc->hdrv);
+	}
+	local_irq_enable();
+	hdlcdrv_transmitter(dev, &bc->hdrv);
+	hdlcdrv_receiver(dev, &bc->hdrv);
+	local_irq_disable();
+	return IRQ_HANDLED;
+}
+
+/* --------------------------------------------------------------------- */
+
+enum uart { c_uart_unknown, c_uart_8250,
+	    c_uart_16450, c_uart_16550, c_uart_16550A};
+static const char *uart_str[] = { 
+	"unknown", "8250", "16450", "16550", "16550A" 
+};
+
+static enum uart ser12_check_uart(unsigned int iobase)
+{
+	unsigned char b1,b2,b3;
+	enum uart u;
+	enum uart uart_tab[] =
+		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
+
+	b1 = inb(MCR(iobase));
+	outb(b1 | 0x10, MCR(iobase));	/* loopback mode */
+	b2 = inb(MSR(iobase));
+	outb(0x1a, MCR(iobase));
+	b3 = inb(MSR(iobase)) & 0xf0;
+	outb(b1, MCR(iobase));			/* restore old values */
+	outb(b2, MSR(iobase));
+	if (b3 != 0x90)
+		return c_uart_unknown;
+	inb(RBR(iobase));
+	inb(RBR(iobase));
+	outb(0x01, FCR(iobase));		/* enable FIFOs */
+	u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
+	if (u == c_uart_16450) {
+		outb(0x5a, SCR(iobase));
+		b1 = inb(SCR(iobase));
+		outb(0xa5, SCR(iobase));
+		b2 = inb(SCR(iobase));
+		if ((b1 != 0x5a) || (b2 != 0xa5))
+			u = c_uart_8250;
+	}
+	return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_open(struct net_device *dev)
+{
+	struct baycom_state *bc = netdev_priv(dev);
+	enum uart u;
+
+	if (!dev || !bc)
+		return -ENXIO;
+	if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT ||
+	    dev->irq < 2 || dev->irq > 15)
+		return -ENXIO;
+	if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12"))
+		return -EACCES;
+	memset(&bc->modem, 0, sizeof(bc->modem));
+	bc->hdrv.par.bitrate = 1200;
+	if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown) {
+		release_region(dev->base_addr, SER12_EXTENT);       
+		return -EIO;
+	}
+	outb(0, FCR(dev->base_addr));  /* disable FIFOs */
+	outb(0x0d, MCR(dev->base_addr));
+	outb(0, IER(dev->base_addr));
+	if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT | SA_SHIRQ,
+			"baycom_ser12", dev)) {
+		release_region(dev->base_addr, SER12_EXTENT);       
+		return -EBUSY;
+	}
+	/*
+	 * enable transmitter empty interrupt
+	 */
+	outb(2, IER(dev->base_addr));
+	/*
+	 * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that
+	 * we get exactly (hopefully) 2 or 3 interrupts per radio symbol,
+	 * depending on the usage of the software DCD routine
+	 */
+	ser12_set_divisor(dev, bc->opt_dcd ? 6 : 4);
+	printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u uart %s\n", 
+	       bc_drvname, dev->base_addr, dev->irq, uart_str[u]);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_close(struct net_device *dev)
+{
+	struct baycom_state *bc = netdev_priv(dev);
+
+	if (!dev || !bc)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	outb(0, IER(dev->base_addr));
+	outb(1, MCR(dev->base_addr));
+	free_irq(dev->irq, dev);
+	release_region(dev->base_addr, SER12_EXTENT);
+	printk(KERN_INFO "%s: close ser12 at iobase 0x%lx irq %u\n",
+	       bc_drvname, dev->base_addr, dev->irq);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== hdlcdrv driver interface =========================
+ */
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
+			struct hdlcdrv_ioctl *hi, int cmd);
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops ser12_ops = {
+	.drvname = bc_drvname,
+	.drvinfo = bc_drvinfo,
+	.open    = ser12_open,
+	.close   = ser12_close,
+	.ioctl   = baycom_ioctl,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_setmode(struct baycom_state *bc, const char *modestr)
+{
+	if (strchr(modestr, '*'))
+		bc->opt_dcd = 0;
+	else if (strchr(modestr, '+'))
+		bc->opt_dcd = -1;
+	else if (strchr(modestr, '@'))
+		bc->opt_dcd = -2;
+	else
+		bc->opt_dcd = 1;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
+			struct hdlcdrv_ioctl *hi, int cmd)
+{
+	struct baycom_state *bc;
+	struct baycom_ioctl bi;
+
+	if (!dev)
+		return -EINVAL;
+
+	bc = netdev_priv(dev);
+	BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC);
+
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+	switch (hi->cmd) {
+	default:
+		break;
+
+	case HDLCDRVCTL_GETMODE:
+		strcpy(hi->data.modename, "ser12");
+		if (bc->opt_dcd <= 0)
+			strcat(hi->data.modename, (!bc->opt_dcd) ? "*" : (bc->opt_dcd == -2) ? "@" : "+");
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_SETMODE:
+		if (netif_running(dev) || !capable(CAP_NET_ADMIN))
+			return -EACCES;
+		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
+		return baycom_setmode(bc, hi->data.modename);
+
+	case HDLCDRVCTL_MODELIST:
+		strcpy(hi->data.modename, "ser12");
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_MODEMPARMASK:
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ;
+
+	}
+
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+	switch (bi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+#ifdef BAYCOM_DEBUG
+	case BAYCOMCTL_GETDEBUG:
+		bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
+		bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
+		bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
+		break;
+#endif /* BAYCOM_DEBUG */
+
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * command line settable parameters
+ */
+static char *mode[NR_PORTS] = { "ser12*", };
+static int iobase[NR_PORTS] = { 0x3f8, };
+static int irq[NR_PORTS] = { 4, };
+
+module_param_array(mode, charp, NULL, 0);
+MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD");
+module_param_array(iobase, int, NULL, 0);
+MODULE_PARM_DESC(iobase, "baycom io base address");
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(irq, "baycom irq number");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Baycom ser12 half duplex amateur radio modem driver");
+MODULE_LICENSE("GPL");
+
+/* --------------------------------------------------------------------- */
+
+static int __init init_baycomserhdx(void)
+{
+	int i, found = 0;
+	char set_hw = 1;
+
+	printk(bc_drvinfo);
+	/*
+	 * register net devices
+	 */
+	for (i = 0; i < NR_PORTS; i++) {
+		struct net_device *dev;
+		struct baycom_state *bc;
+		char ifname[IFNAMSIZ];
+
+		sprintf(ifname, "bcsh%d", i);
+
+		if (!mode[i])
+			set_hw = 0;
+		if (!set_hw)
+			iobase[i] = irq[i] = 0;
+
+		dev = hdlcdrv_register(&ser12_ops, 
+				       sizeof(struct baycom_state),
+				       ifname, iobase[i], irq[i], 0);
+		if (IS_ERR(dev)) 
+			break;
+
+		bc = netdev_priv(dev);
+		if (set_hw && baycom_setmode(bc, mode[i]))
+			set_hw = 0;
+		found++;
+		baycom_device[i] = dev;
+	}
+
+	if (!found)
+		return -ENXIO;
+	return 0;
+}
+
+static void __exit cleanup_baycomserhdx(void)
+{
+	int i;
+
+	for(i = 0; i < NR_PORTS; i++) {
+		struct net_device *dev = baycom_device[i];
+
+		if (dev)
+			hdlcdrv_unregister(dev);
+	}
+}
+
+module_init(init_baycomserhdx);
+module_exit(cleanup_baycomserhdx);
+
+/* --------------------------------------------------------------------- */
+
+#ifndef MODULE
+
+/*
+ * format: baycom_ser_hdx=io,irq,mode
+ * mode: ser12    hardware DCD
+ *       ser12*   software DCD
+ *       ser12@   hardware/software DCD, i.e. no explicit DCD signal but hardware
+ *                mutes audio input to the modem
+ *       ser12+   hardware DCD, inverted signal at DCD pin
+ */
+
+static int __init baycom_ser_hdx_setup(char *str)
+{
+        static unsigned nr_dev;
+	int ints[3];
+
+        if (nr_dev >= NR_PORTS)
+                return 0;
+	str = get_options(str, 3, ints);
+	if (ints[0] < 2)
+		return 0;
+	mode[nr_dev] = str;
+	iobase[nr_dev] = ints[1];
+	irq[nr_dev] = ints[2];
+	nr_dev++;
+	return 1;
+}
+
+__setup("baycom_ser_hdx=", baycom_ser_hdx_setup);
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c
new file mode 100644
index 0000000..ef1a359
--- /dev/null
+++ b/drivers/net/hamradio/bpqether.c
@@ -0,0 +1,643 @@
+/*
+ *	G8BPQ compatible "AX.25 via ethernet" driver release 004
+ *
+ *	This code REQUIRES 2.0.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	This is a "pseudo" network driver to allow AX.25 over Ethernet
+ *	using G8BPQ encapsulation. It has been extracted from the protocol
+ *	implementation because
+ *
+ *		- things got unreadable within the protocol stack
+ *		- to cure the protocol stack from "feature-ism"
+ *		- a protocol implementation shouldn't need to know on
+ *		  which hardware it is running
+ *		- user-level programs like the AX.25 utilities shouldn't
+ *		  need to know about the hardware.
+ *		- IP over ethernet encapsulated AX.25 was impossible
+ *		- rxecho.c did not work
+ *		- to have room for extensions
+ *		- it just deserves to "live" as an own driver
+ *
+ *	This driver can use any ethernet destination address, and can be
+ *	limited to accept frames from one dedicated ethernet card only.
+ *
+ *	Note that the driver sets up the BPQ devices automagically on
+ *	startup or (if started before the "insmod" of an ethernet device)
+ *	on "ifconfig up". It hopefully will remove the BPQ on "rmmod"ing
+ *	the ethernet device (in fact: as soon as another ethernet or bpq
+ *	device gets "ifconfig"ured).
+ *
+ *	I have heard that several people are thinking of experiments
+ *	with highspeed packet radio using existing ethernet cards.
+ *	Well, this driver is prepared for this purpose, just add
+ *	your tx key control and a txdelay / tailtime algorithm,
+ *	probably some buffering, and /voila/...
+ *
+ *	History
+ *	BPQ   001	Joerg(DL1BKE)		Extracted BPQ code from AX.25
+ *						protocol stack and added my own
+ *						yet existing patches
+ *	BPQ   002	Joerg(DL1BKE)		Scan network device list on
+ *						startup.
+ *	BPQ   003	Joerg(DL1BKE)		Ethernet destination address
+ *						and accepted source address
+ *						can be configured by an ioctl()
+ *						call.
+ *						Fixed to match Linux networking
+ *						changes - 2.1.15.
+ *	BPQ   004	Joerg(DL1BKE)		Fixed to not lock up on ifconfig.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/stat.h>
+#include <linux/netfilter.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/rtnetlink.h>
+
+#include <net/ip.h>
+#include <net/arp.h>
+
+#include <linux/bpqether.h>
+
+static char banner[] __initdata = KERN_INFO "AX.25: bpqether driver version 004\n";
+
+static unsigned char ax25_bcast[AX25_ADDR_LEN] =
+	{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static unsigned char ax25_defaddr[AX25_ADDR_LEN] =
+	{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
+
+static char bpq_eth_addr[6];
+
+static int bpq_rcv(struct sk_buff *, struct net_device *, struct packet_type *);
+static int bpq_device_event(struct notifier_block *, unsigned long, void *);
+static const char *bpq_print_ethaddr(const unsigned char *);
+
+static struct packet_type bpq_packet_type = {
+	.type	= __constant_htons(ETH_P_BPQ),
+	.func	= bpq_rcv,
+};
+
+static struct notifier_block bpq_dev_notifier = {
+	.notifier_call =bpq_device_event,
+};
+
+
+struct bpqdev {
+	struct list_head bpq_list;	/* list of bpq devices chain */
+	struct net_device *ethdev;	/* link to ethernet device */
+	struct net_device *axdev;	/* bpq device (bpq#) */
+	struct net_device_stats stats;	/* some statistics */
+	char   dest_addr[6];		/* ether destination address */
+	char   acpt_addr[6];		/* accept ether frames from this address only */
+};
+
+static LIST_HEAD(bpq_devices);
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Get the ethernet device for a BPQ device
+ */
+static inline struct net_device *bpq_get_ether_dev(struct net_device *dev)
+{
+	struct bpqdev *bpq = netdev_priv(dev);
+
+	return bpq ? bpq->ethdev : NULL;
+}
+
+/*
+ *	Get the BPQ device for the ethernet device
+ */
+static inline struct net_device *bpq_get_ax25_dev(struct net_device *dev)
+{
+	struct bpqdev *bpq;
+
+	list_for_each_entry(bpq, &bpq_devices, bpq_list) {
+		if (bpq->ethdev == dev)
+			return bpq->axdev;
+	}
+	return NULL;
+}
+
+static inline int dev_is_ethdev(struct net_device *dev)
+{
+	return (
+			dev->type == ARPHRD_ETHER
+			&& strncmp(dev->name, "dummy", 5)
+	);
+}
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Receive an AX.25 frame via an ethernet interface.
+ */
+static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype)
+{
+	int len;
+	char * ptr;
+	struct ethhdr *eth;
+	struct bpqdev *bpq;
+
+	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
+		return NET_RX_DROP;
+
+	if (!pskb_may_pull(skb, sizeof(struct ethhdr)))
+		goto drop;
+
+	rcu_read_lock();
+	dev = bpq_get_ax25_dev(dev);
+
+	if (dev == NULL || !netif_running(dev)) 
+		goto drop_unlock;
+
+	/*
+	 * if we want to accept frames from just one ethernet device
+	 * we check the source address of the sender.
+	 */
+
+	bpq = netdev_priv(dev);
+
+	eth = eth_hdr(skb);
+
+	if (!(bpq->acpt_addr[0] & 0x01) &&
+	    memcmp(eth->h_source, bpq->acpt_addr, ETH_ALEN))
+		goto drop_unlock;
+
+	if (skb_cow(skb, sizeof(struct ethhdr)))
+		goto drop_unlock;
+
+	len = skb->data[0] + skb->data[1] * 256 - 5;
+
+	skb_pull(skb, 2);	/* Remove the length bytes */
+	skb_trim(skb, len);	/* Set the length of the data */
+
+	bpq->stats.rx_packets++;
+	bpq->stats.rx_bytes += len;
+
+	ptr = skb_push(skb, 1);
+	*ptr = 0;
+
+	skb->dev = dev;
+	skb->protocol = htons(ETH_P_AX25);
+	skb->mac.raw = skb->data;
+	skb->pkt_type = PACKET_HOST;
+
+	netif_rx(skb);
+	dev->last_rx = jiffies;
+unlock:
+
+	rcu_read_unlock();
+
+	return 0;
+drop_unlock:
+	kfree_skb(skb);
+	goto unlock;
+
+drop:
+	kfree_skb(skb);
+	return 0;
+}
+
+/*
+ * 	Send an AX.25 frame via an ethernet interface
+ */
+static int bpq_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct sk_buff *newskb;
+	unsigned char *ptr;
+	struct bpqdev *bpq;
+	int size;
+
+	/*
+	 * Just to be *really* sure not to send anything if the interface
+	 * is down, the ethernet device may have gone.
+	 */
+	if (!netif_running(dev)) {
+		kfree_skb(skb);
+		return -ENODEV;
+	}
+
+	skb_pull(skb, 1);
+	size = skb->len;
+
+	/*
+	 * The AX.25 code leaves enough room for the ethernet header, but
+	 * sendto() does not.
+	 */
+	if (skb_headroom(skb) < AX25_BPQ_HEADER_LEN) {	/* Ough! */
+		if ((newskb = skb_realloc_headroom(skb, AX25_BPQ_HEADER_LEN)) == NULL) {
+			printk(KERN_WARNING "bpqether: out of memory\n");
+			kfree_skb(skb);
+			return -ENOMEM;
+		}
+
+		if (skb->sk != NULL)
+			skb_set_owner_w(newskb, skb->sk);
+
+		kfree_skb(skb);
+		skb = newskb;
+	}
+
+	skb->protocol = htons(ETH_P_AX25);
+
+	ptr = skb_push(skb, 2);
+
+	*ptr++ = (size + 5) % 256;
+	*ptr++ = (size + 5) / 256;
+
+	bpq = netdev_priv(dev);
+
+	if ((dev = bpq_get_ether_dev(dev)) == NULL) {
+		bpq->stats.tx_dropped++;
+		kfree_skb(skb);
+		return -ENODEV;
+	}
+
+	skb->dev = dev;
+	skb->nh.raw = skb->data;
+	dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0);
+	bpq->stats.tx_packets++;
+	bpq->stats.tx_bytes+=skb->len;
+  
+	dev_queue_xmit(skb);
+	netif_wake_queue(dev);
+	return 0;
+}
+
+/*
+ *	Statistics
+ */
+static struct net_device_stats *bpq_get_stats(struct net_device *dev)
+{
+	struct bpqdev *bpq = netdev_priv(dev);
+
+	return &bpq->stats;
+}
+
+/*
+ *	Set AX.25 callsign
+ */
+static int bpq_set_mac_address(struct net_device *dev, void *addr)
+{
+    struct sockaddr *sa = (struct sockaddr *)addr;
+
+    memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+
+    return 0;
+}
+
+/*	Ioctl commands
+ *
+ *		SIOCSBPQETHOPT		reserved for enhancements
+ *		SIOCSBPQETHADDR		set the destination and accepted
+ *					source ethernet address (broadcast
+ *					or multicast: accept all)
+ */
+static int bpq_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct bpq_ethaddr __user *ethaddr = ifr->ifr_data;
+	struct bpqdev *bpq = netdev_priv(dev);
+	struct bpq_req req;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	switch (cmd) {
+		case SIOCSBPQETHOPT:
+			if (copy_from_user(&req, ifr->ifr_data, sizeof(struct bpq_req)))
+				return -EFAULT;
+			switch (req.cmd) {
+				case SIOCGBPQETHPARAM:
+				case SIOCSBPQETHPARAM:
+				default:
+					return -EINVAL;
+			}
+
+			break;
+
+		case SIOCSBPQETHADDR:
+			if (copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN))
+				return -EFAULT;
+			if (copy_from_user(bpq->acpt_addr, ethaddr->accept, ETH_ALEN))
+				return -EFAULT;
+			break;
+
+		default:
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * open/close a device
+ */
+static int bpq_open(struct net_device *dev)
+{
+	netif_start_queue(dev);
+	return 0;
+}
+
+static int bpq_close(struct net_device *dev)
+{
+	netif_stop_queue(dev);
+	return 0;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Proc filesystem
+ */
+static const char * bpq_print_ethaddr(const unsigned char *e)
+{
+	static char buf[18];
+
+	sprintf(buf, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+		e[0], e[1], e[2], e[3], e[4], e[5]);
+
+	return buf;
+}
+
+static void *bpq_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	int i = 1;
+	struct bpqdev *bpqdev;
+
+	rcu_read_lock();
+
+	if (*pos == 0)
+		return SEQ_START_TOKEN;
+	
+	list_for_each_entry(bpqdev, &bpq_devices, bpq_list) {
+		if (i == *pos)
+			return bpqdev;
+	}
+	return NULL;
+}
+
+static void *bpq_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct list_head *p;
+
+	++*pos;
+
+	if (v == SEQ_START_TOKEN)
+		p = bpq_devices.next;
+	else
+		p = ((struct bpqdev *)v)->bpq_list.next;
+
+	return (p == &bpq_devices) ? NULL 
+		: list_entry(p, struct bpqdev, bpq_list);
+}
+
+static void bpq_seq_stop(struct seq_file *seq, void *v)
+{
+	rcu_read_unlock();
+}
+
+
+static int bpq_seq_show(struct seq_file *seq, void *v)
+{
+	if (v == SEQ_START_TOKEN)
+		seq_puts(seq, 
+			 "dev   ether      destination        accept from\n");
+	else {
+		const struct bpqdev *bpqdev = v;
+
+		seq_printf(seq, "%-5s %-10s %s  ",
+			bpqdev->axdev->name, bpqdev->ethdev->name,
+			bpq_print_ethaddr(bpqdev->dest_addr));
+
+		seq_printf(seq, "%s\n",
+			(bpqdev->acpt_addr[0] & 0x01) ? "*" 
+			   : bpq_print_ethaddr(bpqdev->acpt_addr));
+
+	}
+	return 0;
+}
+
+static struct seq_operations bpq_seqops = {
+	.start = bpq_seq_start,
+	.next = bpq_seq_next,
+	.stop = bpq_seq_stop,
+	.show = bpq_seq_show,
+};
+
+static int bpq_info_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &bpq_seqops);
+}
+
+static struct file_operations bpq_info_fops = {
+	.owner = THIS_MODULE,
+	.open = bpq_info_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release,
+};
+
+
+/* ------------------------------------------------------------------------ */
+
+
+static void bpq_setup(struct net_device *dev)
+{
+
+	dev->hard_start_xmit = bpq_xmit;
+	dev->open	     = bpq_open;
+	dev->stop	     = bpq_close;
+	dev->set_mac_address = bpq_set_mac_address;
+	dev->get_stats	     = bpq_get_stats;
+	dev->do_ioctl	     = bpq_ioctl;
+	dev->destructor	     = free_netdev;
+
+	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+	memcpy(dev->dev_addr,  ax25_defaddr, AX25_ADDR_LEN);
+
+	dev->flags      = 0;
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+	dev->hard_header     = ax25_encapsulate;
+	dev->rebuild_header  = ax25_rebuild_header;
+#endif
+
+	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;
+
+}
+
+/*
+ *	Setup a new device.
+ */
+static int bpq_new_device(struct net_device *edev)
+{
+	int err;
+	struct net_device *ndev;
+	struct bpqdev *bpq;
+
+	ndev = alloc_netdev(sizeof(struct bpqdev), "bpq%d",
+			   bpq_setup);
+	if (!ndev)
+		return -ENOMEM;
+
+		
+	bpq = netdev_priv(ndev);
+	dev_hold(edev);
+	bpq->ethdev = edev;
+	bpq->axdev = ndev;
+
+	memcpy(bpq->dest_addr, bcast_addr, sizeof(bpq_eth_addr));
+	memcpy(bpq->acpt_addr, bcast_addr, sizeof(bpq_eth_addr));
+
+	err = dev_alloc_name(ndev, ndev->name);
+	if (err < 0) 
+		goto error;
+
+	err = register_netdevice(ndev);
+	if (err)
+		goto error;
+
+	/* List protected by RTNL */
+	list_add_rcu(&bpq->bpq_list, &bpq_devices);
+	return 0;
+
+ error:
+	dev_put(edev);
+	free_netdev(ndev);
+	return err;
+	
+}
+
+static void bpq_free_device(struct net_device *ndev)
+{
+	struct bpqdev *bpq = netdev_priv(ndev);
+
+	dev_put(bpq->ethdev);
+	list_del_rcu(&bpq->bpq_list);
+
+	unregister_netdevice(ndev);
+}
+
+/*
+ *	Handle device status changes.
+ */
+static int bpq_device_event(struct notifier_block *this,unsigned long event, void *ptr)
+{
+	struct net_device *dev = (struct net_device *)ptr;
+
+	if (!dev_is_ethdev(dev))
+		return NOTIFY_DONE;
+
+	rcu_read_lock();
+
+	switch (event) {
+	case NETDEV_UP:		/* new ethernet device -> new BPQ interface */
+		if (bpq_get_ax25_dev(dev) == NULL)
+			bpq_new_device(dev);
+		break;
+
+	case NETDEV_DOWN:	/* ethernet device closed -> close BPQ interface */
+		if ((dev = bpq_get_ax25_dev(dev)) != NULL)
+			dev_close(dev);
+		break;
+
+	case NETDEV_UNREGISTER:	/* ethernet device removed -> free BPQ interface */
+		if ((dev = bpq_get_ax25_dev(dev)) != NULL)
+			bpq_free_device(dev);
+		break;
+	default:
+		break;
+	}
+	rcu_read_unlock();
+
+	return NOTIFY_DONE;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Initialize driver. To be called from af_ax25 if not compiled as a
+ * module
+ */
+static int __init bpq_init_driver(void)
+{
+#ifdef CONFIG_PROC_FS
+	if (!proc_net_fops_create("bpqether", S_IRUGO, &bpq_info_fops)) {
+		printk(KERN_ERR
+			"bpq: cannot create /proc/net/bpqether entry.\n");
+		return -ENOENT;
+	}
+#endif  /* CONFIG_PROC_FS */
+
+	dev_add_pack(&bpq_packet_type);
+
+	register_netdevice_notifier(&bpq_dev_notifier);
+
+	printk(banner);
+
+	return 0;
+}
+
+static void __exit bpq_cleanup_driver(void)
+{
+	struct bpqdev *bpq;
+
+	dev_remove_pack(&bpq_packet_type);
+
+	unregister_netdevice_notifier(&bpq_dev_notifier);
+
+	proc_net_remove("bpqether");
+
+	rtnl_lock();
+	while (!list_empty(&bpq_devices)) {
+		bpq = list_entry(bpq_devices.next, struct bpqdev, bpq_list);
+		bpq_free_device(bpq->axdev);
+	}
+	rtnl_unlock();
+}
+
+MODULE_AUTHOR("Joerg Reuter DL1BKE <jreuter@yaina.de>");
+MODULE_DESCRIPTION("Transmit and receive AX.25 packets over Ethernet");
+MODULE_LICENSE("GPL");
+module_init(bpq_init_driver);
+module_exit(bpq_cleanup_driver);
diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c
new file mode 100644
index 0000000..f3269b7
--- /dev/null
+++ b/drivers/net/hamradio/dmascc.c
@@ -0,0 +1,1493 @@
+/*
+ * Driver for high-speed SCC boards (those with DMA support)
+ * Copyright (C) 1997-2000 Klaus Kudielka
+ *
+ * S5SCC/DMA support by Janko Koleznik S52HI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/sockios.h>
+#include <linux/workqueue.h>
+#include <asm/atomic.h>
+#include <asm/bitops.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <net/ax25.h>
+#include "z8530.h"
+
+
+/* Number of buffers per channel */
+
+#define NUM_TX_BUF      2	/* NUM_TX_BUF >= 1 (min. 2 recommended) */
+#define NUM_RX_BUF      6	/* NUM_RX_BUF >= 1 (min. 2 recommended) */
+#define BUF_SIZE        1576	/* BUF_SIZE >= mtu + hard_header_len */
+
+
+/* Cards supported */
+
+#define HW_PI           { "Ottawa PI", 0x300, 0x20, 0x10, 8, \
+                            0, 8, 1843200, 3686400 }
+#define HW_PI2          { "Ottawa PI2", 0x300, 0x20, 0x10, 8, \
+			    0, 8, 3686400, 7372800 }
+#define HW_TWIN         { "Gracilis PackeTwin", 0x200, 0x10, 0x10, 32, \
+			    0, 4, 6144000, 6144000 }
+#define HW_S5           { "S5SCC/DMA", 0x200, 0x10, 0x10, 32, \
+                          0, 8, 4915200, 9830400 }
+
+#define HARDWARE        { HW_PI, HW_PI2, HW_TWIN, HW_S5 }
+
+#define TMR_0_HZ        25600	/* Frequency of timer 0 */
+
+#define TYPE_PI         0
+#define TYPE_PI2        1
+#define TYPE_TWIN       2
+#define TYPE_S5         3
+#define NUM_TYPES       4
+
+#define MAX_NUM_DEVS    32
+
+
+/* SCC chips supported */
+
+#define Z8530           0
+#define Z85C30          1
+#define Z85230          2
+
+#define CHIPNAMES       { "Z8530", "Z85C30", "Z85230" }
+
+
+/* I/O registers */
+
+/* 8530 registers relative to card base */
+#define SCCB_CMD        0x00
+#define SCCB_DATA       0x01
+#define SCCA_CMD        0x02
+#define SCCA_DATA       0x03
+
+/* 8253/8254 registers relative to card base */
+#define TMR_CNT0        0x00
+#define TMR_CNT1        0x01
+#define TMR_CNT2        0x02
+#define TMR_CTRL        0x03
+
+/* Additional PI/PI2 registers relative to card base */
+#define PI_DREQ_MASK    0x04
+
+/* Additional PackeTwin registers relative to card base */
+#define TWIN_INT_REG    0x08
+#define TWIN_CLR_TMR1   0x09
+#define TWIN_CLR_TMR2   0x0a
+#define TWIN_SPARE_1    0x0b
+#define TWIN_DMA_CFG    0x08
+#define TWIN_SERIAL_CFG 0x09
+#define TWIN_DMA_CLR_FF 0x0a
+#define TWIN_SPARE_2    0x0b
+
+
+/* PackeTwin I/O register values */
+
+/* INT_REG */
+#define TWIN_SCC_MSK       0x01
+#define TWIN_TMR1_MSK      0x02
+#define TWIN_TMR2_MSK      0x04
+#define TWIN_INT_MSK       0x07
+
+/* SERIAL_CFG */
+#define TWIN_DTRA_ON       0x01
+#define TWIN_DTRB_ON       0x02
+#define TWIN_EXTCLKA       0x04
+#define TWIN_EXTCLKB       0x08
+#define TWIN_LOOPA_ON      0x10
+#define TWIN_LOOPB_ON      0x20
+#define TWIN_EI            0x80
+
+/* DMA_CFG */
+#define TWIN_DMA_HDX_T1    0x08
+#define TWIN_DMA_HDX_R1    0x0a
+#define TWIN_DMA_HDX_T3    0x14
+#define TWIN_DMA_HDX_R3    0x16
+#define TWIN_DMA_FDX_T3R1  0x1b
+#define TWIN_DMA_FDX_T1R3  0x1d
+
+
+/* Status values */
+
+#define IDLE      0
+#define TX_HEAD   1
+#define TX_DATA   2
+#define TX_PAUSE  3
+#define TX_TAIL   4
+#define RTS_OFF   5
+#define WAIT      6
+#define DCD_ON    7
+#define RX_ON     8
+#define DCD_OFF   9
+
+
+/* Ioctls */
+
+#define SIOCGSCCPARAM SIOCDEVPRIVATE
+#define SIOCSSCCPARAM (SIOCDEVPRIVATE+1)
+
+
+/* Data types */
+
+struct scc_param {
+	int pclk_hz;		/* frequency of BRG input (don't change) */
+	int brg_tc;		/* BRG terminal count; BRG disabled if < 0 */
+	int nrzi;		/* 0 (nrz), 1 (nrzi) */
+	int clocks;		/* see dmascc_cfg documentation */
+	int txdelay;		/* [1/TMR_0_HZ] */
+	int txtimeout;		/* [1/HZ] */
+	int txtail;		/* [1/TMR_0_HZ] */
+	int waittime;		/* [1/TMR_0_HZ] */
+	int slottime;		/* [1/TMR_0_HZ] */
+	int persist;		/* 1 ... 256 */
+	int dma;		/* -1 (disable), 0, 1, 3 */
+	int txpause;		/* [1/TMR_0_HZ] */
+	int rtsoff;		/* [1/TMR_0_HZ] */
+	int dcdon;		/* [1/TMR_0_HZ] */
+	int dcdoff;		/* [1/TMR_0_HZ] */
+};
+
+struct scc_hardware {
+	char *name;
+	int io_region;
+	int io_delta;
+	int io_size;
+	int num_devs;
+	int scc_offset;
+	int tmr_offset;
+	int tmr_hz;
+	int pclk_hz;
+};
+
+struct scc_priv {
+	int type;
+	int chip;
+	struct net_device *dev;
+	struct scc_info *info;
+	struct net_device_stats stats;
+	int channel;
+	int card_base, scc_cmd, scc_data;
+	int tmr_cnt, tmr_ctrl, tmr_mode;
+	struct scc_param param;
+	char rx_buf[NUM_RX_BUF][BUF_SIZE];
+	int rx_len[NUM_RX_BUF];
+	int rx_ptr;
+	struct work_struct rx_work;
+	int rx_head, rx_tail, rx_count;
+	int rx_over;
+	char tx_buf[NUM_TX_BUF][BUF_SIZE];
+	int tx_len[NUM_TX_BUF];
+	int tx_ptr;
+	int tx_head, tx_tail, tx_count;
+	int state;
+	unsigned long tx_start;
+	int rr0;
+	spinlock_t *register_lock;	/* Per scc_info */
+	spinlock_t ring_lock;
+};
+
+struct scc_info {
+	int irq_used;
+	int twin_serial_cfg;
+	struct net_device *dev[2];
+	struct scc_priv priv[2];
+	struct scc_info *next;
+	spinlock_t register_lock;	/* Per device register lock */
+};
+
+
+/* Function declarations */
+static int setup_adapter(int card_base, int type, int n) __init;
+
+static void write_scc(struct scc_priv *priv, int reg, int val);
+static void write_scc_data(struct scc_priv *priv, int val, int fast);
+static int read_scc(struct scc_priv *priv, int reg);
+static int read_scc_data(struct scc_priv *priv);
+
+static int scc_open(struct net_device *dev);
+static int scc_close(struct net_device *dev);
+static int scc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+static int scc_send_packet(struct sk_buff *skb, struct net_device *dev);
+static struct net_device_stats *scc_get_stats(struct net_device *dev);
+static int scc_set_mac_address(struct net_device *dev, void *sa);
+
+static inline void tx_on(struct scc_priv *priv);
+static inline void rx_on(struct scc_priv *priv);
+static inline void rx_off(struct scc_priv *priv);
+static void start_timer(struct scc_priv *priv, int t, int r15);
+static inline unsigned char random(void);
+
+static inline void z8530_isr(struct scc_info *info);
+static irqreturn_t scc_isr(int irq, void *dev_id, struct pt_regs *regs);
+static void rx_isr(struct scc_priv *priv);
+static void special_condition(struct scc_priv *priv, int rc);
+static void rx_bh(void *arg);
+static void tx_isr(struct scc_priv *priv);
+static void es_isr(struct scc_priv *priv);
+static void tm_isr(struct scc_priv *priv);
+
+
+/* Initialization variables */
+
+static int io[MAX_NUM_DEVS] __initdata = { 0, };
+
+/* Beware! hw[] is also used in cleanup_module(). */
+static struct scc_hardware hw[NUM_TYPES] __initdata_or_module = HARDWARE;
+static char ax25_broadcast[7] __initdata =
+    { 'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1,
+'0' << 1 };
+static char ax25_test[7] __initdata =
+    { 'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1,
+'1' << 1 };
+
+
+/* Global variables */
+
+static struct scc_info *first;
+static unsigned long rand;
+
+
+MODULE_AUTHOR("Klaus Kudielka");
+MODULE_DESCRIPTION("Driver for high-speed SCC boards");
+MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NUM_DEVS) "i");
+MODULE_LICENSE("GPL");
+
+static void __exit dmascc_exit(void)
+{
+	int i;
+	struct scc_info *info;
+
+	while (first) {
+		info = first;
+
+		/* Unregister devices */
+		for (i = 0; i < 2; i++)
+			unregister_netdev(info->dev[i]);
+
+		/* Reset board */
+		if (info->priv[0].type == TYPE_TWIN)
+			outb(0, info->dev[0]->base_addr + TWIN_SERIAL_CFG);
+		write_scc(&info->priv[0], R9, FHWRES);
+		release_region(info->dev[0]->base_addr,
+			       hw[info->priv[0].type].io_size);
+
+		for (i = 0; i < 2; i++)
+			free_netdev(info->dev[i]);
+
+		/* Free memory */
+		first = info->next;
+		kfree(info);
+	}
+}
+
+#ifndef MODULE
+void __init dmascc_setup(char *str, int *ints)
+{
+	int i;
+
+	for (i = 0; i < MAX_NUM_DEVS && i < ints[0]; i++)
+		io[i] = ints[i + 1];
+}
+#endif
+
+static int __init dmascc_init(void)
+{
+	int h, i, j, n;
+	int base[MAX_NUM_DEVS], tcmd[MAX_NUM_DEVS], t0[MAX_NUM_DEVS],
+	    t1[MAX_NUM_DEVS];
+	unsigned t_val;
+	unsigned long time, start[MAX_NUM_DEVS], delay[MAX_NUM_DEVS],
+	    counting[MAX_NUM_DEVS];
+
+	/* Initialize random number generator */
+	rand = jiffies;
+	/* Cards found = 0 */
+	n = 0;
+	/* Warning message */
+	if (!io[0])
+		printk(KERN_INFO "dmascc: autoprobing (dangerous)\n");
+
+	/* Run autodetection for each card type */
+	for (h = 0; h < NUM_TYPES; h++) {
+
+		if (io[0]) {
+			/* User-specified I/O address regions */
+			for (i = 0; i < hw[h].num_devs; i++)
+				base[i] = 0;
+			for (i = 0; i < MAX_NUM_DEVS && io[i]; i++) {
+				j = (io[i] -
+				     hw[h].io_region) / hw[h].io_delta;
+				if (j >= 0 && j < hw[h].num_devs
+				    && hw[h].io_region +
+				    j * hw[h].io_delta == io[i]) {
+					base[j] = io[i];
+				}
+			}
+		} else {
+			/* Default I/O address regions */
+			for (i = 0; i < hw[h].num_devs; i++) {
+				base[i] =
+				    hw[h].io_region + i * hw[h].io_delta;
+			}
+		}
+
+		/* Check valid I/O address regions */
+		for (i = 0; i < hw[h].num_devs; i++)
+			if (base[i]) {
+				if (!request_region
+				    (base[i], hw[h].io_size, "dmascc"))
+					base[i] = 0;
+				else {
+					tcmd[i] =
+					    base[i] + hw[h].tmr_offset +
+					    TMR_CTRL;
+					t0[i] =
+					    base[i] + hw[h].tmr_offset +
+					    TMR_CNT0;
+					t1[i] =
+					    base[i] + hw[h].tmr_offset +
+					    TMR_CNT1;
+				}
+			}
+
+		/* Start timers */
+		for (i = 0; i < hw[h].num_devs; i++)
+			if (base[i]) {
+				/* Timer 0: LSB+MSB, Mode 3, TMR_0_HZ */
+				outb(0x36, tcmd[i]);
+				outb((hw[h].tmr_hz / TMR_0_HZ) & 0xFF,
+				     t0[i]);
+				outb((hw[h].tmr_hz / TMR_0_HZ) >> 8,
+				     t0[i]);
+				/* Timer 1: LSB+MSB, Mode 0, HZ/10 */
+				outb(0x70, tcmd[i]);
+				outb((TMR_0_HZ / HZ * 10) & 0xFF, t1[i]);
+				outb((TMR_0_HZ / HZ * 10) >> 8, t1[i]);
+				start[i] = jiffies;
+				delay[i] = 0;
+				counting[i] = 1;
+				/* Timer 2: LSB+MSB, Mode 0 */
+				outb(0xb0, tcmd[i]);
+			}
+		time = jiffies;
+		/* Wait until counter registers are loaded */
+		udelay(2000000 / TMR_0_HZ);
+
+		/* Timing loop */
+		while (jiffies - time < 13) {
+			for (i = 0; i < hw[h].num_devs; i++)
+				if (base[i] && counting[i]) {
+					/* Read back Timer 1: latch; read LSB; read MSB */
+					outb(0x40, tcmd[i]);
+					t_val =
+					    inb(t1[i]) + (inb(t1[i]) << 8);
+					/* Also check whether counter did wrap */
+					if (t_val == 0
+					    || t_val > TMR_0_HZ / HZ * 10)
+						counting[i] = 0;
+					delay[i] = jiffies - start[i];
+				}
+		}
+
+		/* Evaluate measurements */
+		for (i = 0; i < hw[h].num_devs; i++)
+			if (base[i]) {
+				if ((delay[i] >= 9 && delay[i] <= 11) &&
+				    /* Ok, we have found an adapter */
+				    (setup_adapter(base[i], h, n) == 0))
+					n++;
+				else
+					release_region(base[i],
+						       hw[h].io_size);
+			}
+
+	}			/* NUM_TYPES */
+
+	/* If any adapter was successfully initialized, return ok */
+	if (n)
+		return 0;
+
+	/* If no adapter found, return error */
+	printk(KERN_INFO "dmascc: no adapters found\n");
+	return -EIO;
+}
+
+module_init(dmascc_init);
+module_exit(dmascc_exit);
+
+static void dev_setup(struct net_device *dev)
+{
+	dev->type = ARPHRD_AX25;
+	dev->hard_header_len = 73;
+	dev->mtu = 1500;
+	dev->addr_len = 7;
+	dev->tx_queue_len = 64;
+	memcpy(dev->broadcast, ax25_broadcast, 7);
+	memcpy(dev->dev_addr, ax25_test, 7);
+}
+
+static int __init setup_adapter(int card_base, int type, int n)
+{
+	int i, irq, chip;
+	struct scc_info *info;
+	struct net_device *dev;
+	struct scc_priv *priv;
+	unsigned long time;
+	unsigned int irqs;
+	int tmr_base = card_base + hw[type].tmr_offset;
+	int scc_base = card_base + hw[type].scc_offset;
+	char *chipnames[] = CHIPNAMES;
+
+	/* Allocate memory */
+	info = kmalloc(sizeof(struct scc_info), GFP_KERNEL | GFP_DMA);
+	if (!info) {
+		printk(KERN_ERR "dmascc: "
+		       "could not allocate memory for %s at %#3x\n",
+		       hw[type].name, card_base);
+		goto out;
+	}
+
+	/* Initialize what is necessary for write_scc and write_scc_data */
+	memset(info, 0, sizeof(struct scc_info));
+
+	info->dev[0] = alloc_netdev(0, "", dev_setup);
+	if (!info->dev[0]) {
+		printk(KERN_ERR "dmascc: "
+		       "could not allocate memory for %s at %#3x\n",
+		       hw[type].name, card_base);
+		goto out1;
+	}
+
+	info->dev[1] = alloc_netdev(0, "", dev_setup);
+	if (!info->dev[1]) {
+		printk(KERN_ERR "dmascc: "
+		       "could not allocate memory for %s at %#3x\n",
+		       hw[type].name, card_base);
+		goto out2;
+	}
+	spin_lock_init(&info->register_lock);
+
+	priv = &info->priv[0];
+	priv->type = type;
+	priv->card_base = card_base;
+	priv->scc_cmd = scc_base + SCCA_CMD;
+	priv->scc_data = scc_base + SCCA_DATA;
+	priv->register_lock = &info->register_lock;
+
+	/* Reset SCC */
+	write_scc(priv, R9, FHWRES | MIE | NV);
+
+	/* Determine type of chip by enabling SDLC/HDLC enhancements */
+	write_scc(priv, R15, SHDLCE);
+	if (!read_scc(priv, R15)) {
+		/* WR7' not present. This is an ordinary Z8530 SCC. */
+		chip = Z8530;
+	} else {
+		/* Put one character in TX FIFO */
+		write_scc_data(priv, 0, 0);
+		if (read_scc(priv, R0) & Tx_BUF_EMP) {
+			/* TX FIFO not full. This is a Z85230 ESCC with a 4-byte FIFO. */
+			chip = Z85230;
+		} else {
+			/* TX FIFO full. This is a Z85C30 SCC with a 1-byte FIFO. */
+			chip = Z85C30;
+		}
+	}
+	write_scc(priv, R15, 0);
+
+	/* Start IRQ auto-detection */
+	irqs = probe_irq_on();
+
+	/* Enable interrupts */
+	if (type == TYPE_TWIN) {
+		outb(0, card_base + TWIN_DMA_CFG);
+		inb(card_base + TWIN_CLR_TMR1);
+		inb(card_base + TWIN_CLR_TMR2);
+		info->twin_serial_cfg = TWIN_EI;
+		outb(info->twin_serial_cfg, card_base + TWIN_SERIAL_CFG);
+	} else {
+		write_scc(priv, R15, CTSIE);
+		write_scc(priv, R0, RES_EXT_INT);
+		write_scc(priv, R1, EXT_INT_ENAB);
+	}
+
+	/* Start timer */
+	outb(1, tmr_base + TMR_CNT1);
+	outb(0, tmr_base + TMR_CNT1);
+
+	/* Wait and detect IRQ */
+	time = jiffies;
+	while (jiffies - time < 2 + HZ / TMR_0_HZ);
+	irq = probe_irq_off(irqs);
+
+	/* Clear pending interrupt, disable interrupts */
+	if (type == TYPE_TWIN) {
+		inb(card_base + TWIN_CLR_TMR1);
+	} else {
+		write_scc(priv, R1, 0);
+		write_scc(priv, R15, 0);
+		write_scc(priv, R0, RES_EXT_INT);
+	}
+
+	if (irq <= 0) {
+		printk(KERN_ERR
+		       "dmascc: could not find irq of %s at %#3x (irq=%d)\n",
+		       hw[type].name, card_base, irq);
+		goto out3;
+	}
+
+	/* Set up data structures */
+	for (i = 0; i < 2; i++) {
+		dev = info->dev[i];
+		priv = &info->priv[i];
+		priv->type = type;
+		priv->chip = chip;
+		priv->dev = dev;
+		priv->info = info;
+		priv->channel = i;
+		spin_lock_init(&priv->ring_lock);
+		priv->register_lock = &info->register_lock;
+		priv->card_base = card_base;
+		priv->scc_cmd = scc_base + (i ? SCCB_CMD : SCCA_CMD);
+		priv->scc_data = scc_base + (i ? SCCB_DATA : SCCA_DATA);
+		priv->tmr_cnt = tmr_base + (i ? TMR_CNT2 : TMR_CNT1);
+		priv->tmr_ctrl = tmr_base + TMR_CTRL;
+		priv->tmr_mode = i ? 0xb0 : 0x70;
+		priv->param.pclk_hz = hw[type].pclk_hz;
+		priv->param.brg_tc = -1;
+		priv->param.clocks = TCTRxCP | RCRTxCP;
+		priv->param.persist = 256;
+		priv->param.dma = -1;
+		INIT_WORK(&priv->rx_work, rx_bh, priv);
+		dev->priv = priv;
+		sprintf(dev->name, "dmascc%i", 2 * n + i);
+		SET_MODULE_OWNER(dev);
+		dev->base_addr = card_base;
+		dev->irq = irq;
+		dev->open = scc_open;
+		dev->stop = scc_close;
+		dev->do_ioctl = scc_ioctl;
+		dev->hard_start_xmit = scc_send_packet;
+		dev->get_stats = scc_get_stats;
+		dev->hard_header = ax25_encapsulate;
+		dev->rebuild_header = ax25_rebuild_header;
+		dev->set_mac_address = scc_set_mac_address;
+	}
+	if (register_netdev(info->dev[0])) {
+		printk(KERN_ERR "dmascc: could not register %s\n",
+		       info->dev[0]->name);
+		goto out3;
+	}
+	if (register_netdev(info->dev[1])) {
+		printk(KERN_ERR "dmascc: could not register %s\n",
+		       info->dev[1]->name);
+		goto out4;
+	}
+
+
+	info->next = first;
+	first = info;
+	printk(KERN_INFO "dmascc: found %s (%s) at %#3x, irq %d\n",
+	       hw[type].name, chipnames[chip], card_base, irq);
+	return 0;
+
+      out4:
+	unregister_netdev(info->dev[0]);
+      out3:
+	if (info->priv[0].type == TYPE_TWIN)
+		outb(0, info->dev[0]->base_addr + TWIN_SERIAL_CFG);
+	write_scc(&info->priv[0], R9, FHWRES);
+	free_netdev(info->dev[1]);
+      out2:
+	free_netdev(info->dev[0]);
+      out1:
+	kfree(info);
+      out:
+	return -1;
+}
+
+
+/* Driver functions */
+
+static void write_scc(struct scc_priv *priv, int reg, int val)
+{
+	unsigned long flags;
+	switch (priv->type) {
+	case TYPE_S5:
+		if (reg)
+			outb(reg, priv->scc_cmd);
+		outb(val, priv->scc_cmd);
+		return;
+	case TYPE_TWIN:
+		if (reg)
+			outb_p(reg, priv->scc_cmd);
+		outb_p(val, priv->scc_cmd);
+		return;
+	default:
+		spin_lock_irqsave(priv->register_lock, flags);
+		outb_p(0, priv->card_base + PI_DREQ_MASK);
+		if (reg)
+			outb_p(reg, priv->scc_cmd);
+		outb_p(val, priv->scc_cmd);
+		outb(1, priv->card_base + PI_DREQ_MASK);
+		spin_unlock_irqrestore(priv->register_lock, flags);
+		return;
+	}
+}
+
+
+static void write_scc_data(struct scc_priv *priv, int val, int fast)
+{
+	unsigned long flags;
+	switch (priv->type) {
+	case TYPE_S5:
+		outb(val, priv->scc_data);
+		return;
+	case TYPE_TWIN:
+		outb_p(val, priv->scc_data);
+		return;
+	default:
+		if (fast)
+			outb_p(val, priv->scc_data);
+		else {
+			spin_lock_irqsave(priv->register_lock, flags);
+			outb_p(0, priv->card_base + PI_DREQ_MASK);
+			outb_p(val, priv->scc_data);
+			outb(1, priv->card_base + PI_DREQ_MASK);
+			spin_unlock_irqrestore(priv->register_lock, flags);
+		}
+		return;
+	}
+}
+
+
+static int read_scc(struct scc_priv *priv, int reg)
+{
+	int rc;
+	unsigned long flags;
+	switch (priv->type) {
+	case TYPE_S5:
+		if (reg)
+			outb(reg, priv->scc_cmd);
+		return inb(priv->scc_cmd);
+	case TYPE_TWIN:
+		if (reg)
+			outb_p(reg, priv->scc_cmd);
+		return inb_p(priv->scc_cmd);
+	default:
+		spin_lock_irqsave(priv->register_lock, flags);
+		outb_p(0, priv->card_base + PI_DREQ_MASK);
+		if (reg)
+			outb_p(reg, priv->scc_cmd);
+		rc = inb_p(priv->scc_cmd);
+		outb(1, priv->card_base + PI_DREQ_MASK);
+		spin_unlock_irqrestore(priv->register_lock, flags);
+		return rc;
+	}
+}
+
+
+static int read_scc_data(struct scc_priv *priv)
+{
+	int rc;
+	unsigned long flags;
+	switch (priv->type) {
+	case TYPE_S5:
+		return inb(priv->scc_data);
+	case TYPE_TWIN:
+		return inb_p(priv->scc_data);
+	default:
+		spin_lock_irqsave(priv->register_lock, flags);
+		outb_p(0, priv->card_base + PI_DREQ_MASK);
+		rc = inb_p(priv->scc_data);
+		outb(1, priv->card_base + PI_DREQ_MASK);
+		spin_unlock_irqrestore(priv->register_lock, flags);
+		return rc;
+	}
+}
+
+
+static int scc_open(struct net_device *dev)
+{
+	struct scc_priv *priv = dev->priv;
+	struct scc_info *info = priv->info;
+	int card_base = priv->card_base;
+
+	/* Request IRQ if not already used by other channel */
+	if (!info->irq_used) {
+		if (request_irq(dev->irq, scc_isr, 0, "dmascc", info)) {
+			return -EAGAIN;
+		}
+	}
+	info->irq_used++;
+
+	/* Request DMA if required */
+	if (priv->param.dma >= 0) {
+		if (request_dma(priv->param.dma, "dmascc")) {
+			if (--info->irq_used == 0)
+				free_irq(dev->irq, info);
+			return -EAGAIN;
+		} else {
+			unsigned long flags = claim_dma_lock();
+			clear_dma_ff(priv->param.dma);
+			release_dma_lock(flags);
+		}
+	}
+
+	/* Initialize local variables */
+	priv->rx_ptr = 0;
+	priv->rx_over = 0;
+	priv->rx_head = priv->rx_tail = priv->rx_count = 0;
+	priv->state = IDLE;
+	priv->tx_head = priv->tx_tail = priv->tx_count = 0;
+	priv->tx_ptr = 0;
+
+	/* Reset channel */
+	write_scc(priv, R9, (priv->channel ? CHRB : CHRA) | MIE | NV);
+	/* X1 clock, SDLC mode */
+	write_scc(priv, R4, SDLC | X1CLK);
+	/* DMA */
+	write_scc(priv, R1, EXT_INT_ENAB | WT_FN_RDYFN);
+	/* 8 bit RX char, RX disable */
+	write_scc(priv, R3, Rx8);
+	/* 8 bit TX char, TX disable */
+	write_scc(priv, R5, Tx8);
+	/* SDLC address field */
+	write_scc(priv, R6, 0);
+	/* SDLC flag */
+	write_scc(priv, R7, FLAG);
+	switch (priv->chip) {
+	case Z85C30:
+		/* Select WR7' */
+		write_scc(priv, R15, SHDLCE);
+		/* Auto EOM reset */
+		write_scc(priv, R7, AUTOEOM);
+		write_scc(priv, R15, 0);
+		break;
+	case Z85230:
+		/* Select WR7' */
+		write_scc(priv, R15, SHDLCE);
+		/* The following bits are set (see 2.5.2.1):
+		   - Automatic EOM reset
+		   - Interrupt request if RX FIFO is half full
+		   This bit should be ignored in DMA mode (according to the
+		   documentation), but actually isn't. The receiver doesn't work if
+		   it is set. Thus, we have to clear it in DMA mode.
+		   - Interrupt/DMA request if TX FIFO is completely empty
+		   a) If set, the ESCC behaves as if it had no TX FIFO (Z85C30
+		   compatibility).
+		   b) If cleared, DMA requests may follow each other very quickly,
+		   filling up the TX FIFO.
+		   Advantage: TX works even in case of high bus latency.
+		   Disadvantage: Edge-triggered DMA request circuitry may miss
+		   a request. No more data is delivered, resulting
+		   in a TX FIFO underrun.
+		   Both PI2 and S5SCC/DMA seem to work fine with TXFIFOE cleared.
+		   The PackeTwin doesn't. I don't know about the PI, but let's
+		   assume it behaves like the PI2.
+		 */
+		if (priv->param.dma >= 0) {
+			if (priv->type == TYPE_TWIN)
+				write_scc(priv, R7, AUTOEOM | TXFIFOE);
+			else
+				write_scc(priv, R7, AUTOEOM);
+		} else {
+			write_scc(priv, R7, AUTOEOM | RXFIFOH);
+		}
+		write_scc(priv, R15, 0);
+		break;
+	}
+	/* Preset CRC, NRZ(I) encoding */
+	write_scc(priv, R10, CRCPS | (priv->param.nrzi ? NRZI : NRZ));
+
+	/* Configure baud rate generator */
+	if (priv->param.brg_tc >= 0) {
+		/* Program BR generator */
+		write_scc(priv, R12, priv->param.brg_tc & 0xFF);
+		write_scc(priv, R13, (priv->param.brg_tc >> 8) & 0xFF);
+		/* BRG source = SYS CLK; enable BRG; DTR REQ function (required by
+		   PackeTwin, not connected on the PI2); set DPLL source to BRG */
+		write_scc(priv, R14, SSBR | DTRREQ | BRSRC | BRENABL);
+		/* Enable DPLL */
+		write_scc(priv, R14, SEARCH | DTRREQ | BRSRC | BRENABL);
+	} else {
+		/* Disable BR generator */
+		write_scc(priv, R14, DTRREQ | BRSRC);
+	}
+
+	/* Configure clocks */
+	if (priv->type == TYPE_TWIN) {
+		/* Disable external TX clock receiver */
+		outb((info->twin_serial_cfg &=
+		      ~(priv->channel ? TWIN_EXTCLKB : TWIN_EXTCLKA)),
+		     card_base + TWIN_SERIAL_CFG);
+	}
+	write_scc(priv, R11, priv->param.clocks);
+	if ((priv->type == TYPE_TWIN) && !(priv->param.clocks & TRxCOI)) {
+		/* Enable external TX clock receiver */
+		outb((info->twin_serial_cfg |=
+		      (priv->channel ? TWIN_EXTCLKB : TWIN_EXTCLKA)),
+		     card_base + TWIN_SERIAL_CFG);
+	}
+
+	/* Configure PackeTwin */
+	if (priv->type == TYPE_TWIN) {
+		/* Assert DTR, enable interrupts */
+		outb((info->twin_serial_cfg |= TWIN_EI |
+		      (priv->channel ? TWIN_DTRB_ON : TWIN_DTRA_ON)),
+		     card_base + TWIN_SERIAL_CFG);
+	}
+
+	/* Read current status */
+	priv->rr0 = read_scc(priv, R0);
+	/* Enable DCD interrupt */
+	write_scc(priv, R15, DCDIE);
+
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+
+static int scc_close(struct net_device *dev)
+{
+	struct scc_priv *priv = dev->priv;
+	struct scc_info *info = priv->info;
+	int card_base = priv->card_base;
+
+	netif_stop_queue(dev);
+
+	if (priv->type == TYPE_TWIN) {
+		/* Drop DTR */
+		outb((info->twin_serial_cfg &=
+		      (priv->channel ? ~TWIN_DTRB_ON : ~TWIN_DTRA_ON)),
+		     card_base + TWIN_SERIAL_CFG);
+	}
+
+	/* Reset channel, free DMA and IRQ */
+	write_scc(priv, R9, (priv->channel ? CHRB : CHRA) | MIE | NV);
+	if (priv->param.dma >= 0) {
+		if (priv->type == TYPE_TWIN)
+			outb(0, card_base + TWIN_DMA_CFG);
+		free_dma(priv->param.dma);
+	}
+	if (--info->irq_used == 0)
+		free_irq(dev->irq, info);
+
+	return 0;
+}
+
+
+static int scc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct scc_priv *priv = dev->priv;
+
+	switch (cmd) {
+	case SIOCGSCCPARAM:
+		if (copy_to_user
+		    (ifr->ifr_data, &priv->param,
+		     sizeof(struct scc_param)))
+			return -EFAULT;
+		return 0;
+	case SIOCSSCCPARAM:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		if (netif_running(dev))
+			return -EAGAIN;
+		if (copy_from_user
+		    (&priv->param, ifr->ifr_data,
+		     sizeof(struct scc_param)))
+			return -EFAULT;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+
+static int scc_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+	struct scc_priv *priv = dev->priv;
+	unsigned long flags;
+	int i;
+
+	/* Temporarily stop the scheduler feeding us packets */
+	netif_stop_queue(dev);
+
+	/* Transfer data to DMA buffer */
+	i = priv->tx_head;
+	memcpy(priv->tx_buf[i], skb->data + 1, skb->len - 1);
+	priv->tx_len[i] = skb->len - 1;
+
+	/* Clear interrupts while we touch our circular buffers */
+
+	spin_lock_irqsave(&priv->ring_lock, flags);
+	/* Move the ring buffer's head */
+	priv->tx_head = (i + 1) % NUM_TX_BUF;
+	priv->tx_count++;
+
+	/* If we just filled up the last buffer, leave queue stopped.
+	   The higher layers must wait until we have a DMA buffer
+	   to accept the data. */
+	if (priv->tx_count < NUM_TX_BUF)
+		netif_wake_queue(dev);
+
+	/* Set new TX state */
+	if (priv->state == IDLE) {
+		/* Assert RTS, start timer */
+		priv->state = TX_HEAD;
+		priv->tx_start = jiffies;
+		write_scc(priv, R5, TxCRC_ENAB | RTS | TxENAB | Tx8);
+		write_scc(priv, R15, 0);
+		start_timer(priv, priv->param.txdelay, 0);
+	}
+
+	/* Turn interrupts back on and free buffer */
+	spin_unlock_irqrestore(&priv->ring_lock, flags);
+	dev_kfree_skb(skb);
+
+	return 0;
+}
+
+
+static struct net_device_stats *scc_get_stats(struct net_device *dev)
+{
+	struct scc_priv *priv = dev->priv;
+
+	return &priv->stats;
+}
+
+
+static int scc_set_mac_address(struct net_device *dev, void *sa)
+{
+	memcpy(dev->dev_addr, ((struct sockaddr *) sa)->sa_data,
+	       dev->addr_len);
+	return 0;
+}
+
+
+static inline void tx_on(struct scc_priv *priv)
+{
+	int i, n;
+	unsigned long flags;
+
+	if (priv->param.dma >= 0) {
+		n = (priv->chip == Z85230) ? 3 : 1;
+		/* Program DMA controller */
+		flags = claim_dma_lock();
+		set_dma_mode(priv->param.dma, DMA_MODE_WRITE);
+		set_dma_addr(priv->param.dma,
+			     (int) priv->tx_buf[priv->tx_tail] + n);
+		set_dma_count(priv->param.dma,
+			      priv->tx_len[priv->tx_tail] - n);
+		release_dma_lock(flags);
+		/* Enable TX underrun interrupt */
+		write_scc(priv, R15, TxUIE);
+		/* Configure DREQ */
+		if (priv->type == TYPE_TWIN)
+			outb((priv->param.dma ==
+			      1) ? TWIN_DMA_HDX_T1 : TWIN_DMA_HDX_T3,
+			     priv->card_base + TWIN_DMA_CFG);
+		else
+			write_scc(priv, R1,
+				  EXT_INT_ENAB | WT_FN_RDYFN |
+				  WT_RDY_ENAB);
+		/* Write first byte(s) */
+		spin_lock_irqsave(priv->register_lock, flags);
+		for (i = 0; i < n; i++)
+			write_scc_data(priv,
+				       priv->tx_buf[priv->tx_tail][i], 1);
+		enable_dma(priv->param.dma);
+		spin_unlock_irqrestore(priv->register_lock, flags);
+	} else {
+		write_scc(priv, R15, TxUIE);
+		write_scc(priv, R1,
+			  EXT_INT_ENAB | WT_FN_RDYFN | TxINT_ENAB);
+		tx_isr(priv);
+	}
+	/* Reset EOM latch if we do not have the AUTOEOM feature */
+	if (priv->chip == Z8530)
+		write_scc(priv, R0, RES_EOM_L);
+}
+
+
+static inline void rx_on(struct scc_priv *priv)
+{
+	unsigned long flags;
+
+	/* Clear RX FIFO */
+	while (read_scc(priv, R0) & Rx_CH_AV)
+		read_scc_data(priv);
+	priv->rx_over = 0;
+	if (priv->param.dma >= 0) {
+		/* Program DMA controller */
+		flags = claim_dma_lock();
+		set_dma_mode(priv->param.dma, DMA_MODE_READ);
+		set_dma_addr(priv->param.dma,
+			     (int) priv->rx_buf[priv->rx_head]);
+		set_dma_count(priv->param.dma, BUF_SIZE);
+		release_dma_lock(flags);
+		enable_dma(priv->param.dma);
+		/* Configure PackeTwin DMA */
+		if (priv->type == TYPE_TWIN) {
+			outb((priv->param.dma ==
+			      1) ? TWIN_DMA_HDX_R1 : TWIN_DMA_HDX_R3,
+			     priv->card_base + TWIN_DMA_CFG);
+		}
+		/* Sp. cond. intr. only, ext int enable, RX DMA enable */
+		write_scc(priv, R1, EXT_INT_ENAB | INT_ERR_Rx |
+			  WT_RDY_RT | WT_FN_RDYFN | WT_RDY_ENAB);
+	} else {
+		/* Reset current frame */
+		priv->rx_ptr = 0;
+		/* Intr. on all Rx characters and Sp. cond., ext int enable */
+		write_scc(priv, R1, EXT_INT_ENAB | INT_ALL_Rx | WT_RDY_RT |
+			  WT_FN_RDYFN);
+	}
+	write_scc(priv, R0, ERR_RES);
+	write_scc(priv, R3, RxENABLE | Rx8 | RxCRC_ENAB);
+}
+
+
+static inline void rx_off(struct scc_priv *priv)
+{
+	/* Disable receiver */
+	write_scc(priv, R3, Rx8);
+	/* Disable DREQ / RX interrupt */
+	if (priv->param.dma >= 0 && priv->type == TYPE_TWIN)
+		outb(0, priv->card_base + TWIN_DMA_CFG);
+	else
+		write_scc(priv, R1, EXT_INT_ENAB | WT_FN_RDYFN);
+	/* Disable DMA */
+	if (priv->param.dma >= 0)
+		disable_dma(priv->param.dma);
+}
+
+
+static void start_timer(struct scc_priv *priv, int t, int r15)
+{
+	unsigned long flags;
+
+	outb(priv->tmr_mode, priv->tmr_ctrl);
+	if (t == 0) {
+		tm_isr(priv);
+	} else if (t > 0) {
+		save_flags(flags);
+		cli();
+		outb(t & 0xFF, priv->tmr_cnt);
+		outb((t >> 8) & 0xFF, priv->tmr_cnt);
+		if (priv->type != TYPE_TWIN) {
+			write_scc(priv, R15, r15 | CTSIE);
+			priv->rr0 |= CTS;
+		}
+		restore_flags(flags);
+	}
+}
+
+
+static inline unsigned char random(void)
+{
+	/* See "Numerical Recipes in C", second edition, p. 284 */
+	rand = rand * 1664525L + 1013904223L;
+	return (unsigned char) (rand >> 24);
+}
+
+static inline void z8530_isr(struct scc_info *info)
+{
+	int is, i = 100;
+
+	while ((is = read_scc(&info->priv[0], R3)) && i--) {
+		if (is & CHARxIP) {
+			rx_isr(&info->priv[0]);
+		} else if (is & CHATxIP) {
+			tx_isr(&info->priv[0]);
+		} else if (is & CHAEXT) {
+			es_isr(&info->priv[0]);
+		} else if (is & CHBRxIP) {
+			rx_isr(&info->priv[1]);
+		} else if (is & CHBTxIP) {
+			tx_isr(&info->priv[1]);
+		} else {
+			es_isr(&info->priv[1]);
+		}
+		write_scc(&info->priv[0], R0, RES_H_IUS);
+		i++;
+	}
+	if (i < 0) {
+		printk(KERN_ERR "dmascc: stuck in ISR with RR3=0x%02x.\n",
+		       is);
+	}
+	/* Ok, no interrupts pending from this 8530. The INT line should
+	   be inactive now. */
+}
+
+
+static irqreturn_t scc_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct scc_info *info = dev_id;
+
+	spin_lock(info->priv[0].register_lock);
+	/* At this point interrupts are enabled, and the interrupt under service
+	   is already acknowledged, but masked off.
+
+	   Interrupt processing: We loop until we know that the IRQ line is
+	   low. If another positive edge occurs afterwards during the ISR,
+	   another interrupt will be triggered by the interrupt controller
+	   as soon as the IRQ level is enabled again (see asm/irq.h).
+
+	   Bottom-half handlers will be processed after scc_isr(). This is
+	   important, since we only have small ringbuffers and want new data
+	   to be fetched/delivered immediately. */
+
+	if (info->priv[0].type == TYPE_TWIN) {
+		int is, card_base = info->priv[0].card_base;
+		while ((is = ~inb(card_base + TWIN_INT_REG)) &
+		       TWIN_INT_MSK) {
+			if (is & TWIN_SCC_MSK) {
+				z8530_isr(info);
+			} else if (is & TWIN_TMR1_MSK) {
+				inb(card_base + TWIN_CLR_TMR1);
+				tm_isr(&info->priv[0]);
+			} else {
+				inb(card_base + TWIN_CLR_TMR2);
+				tm_isr(&info->priv[1]);
+			}
+		}
+	} else
+		z8530_isr(info);
+	spin_unlock(info->priv[0].register_lock);
+	return IRQ_HANDLED;
+}
+
+
+static void rx_isr(struct scc_priv *priv)
+{
+	if (priv->param.dma >= 0) {
+		/* Check special condition and perform error reset. See 2.4.7.5. */
+		special_condition(priv, read_scc(priv, R1));
+		write_scc(priv, R0, ERR_RES);
+	} else {
+		/* Check special condition for each character. Error reset not necessary.
+		   Same algorithm for SCC and ESCC. See 2.4.7.1 and 2.4.7.4. */
+		int rc;
+		while (read_scc(priv, R0) & Rx_CH_AV) {
+			rc = read_scc(priv, R1);
+			if (priv->rx_ptr < BUF_SIZE)
+				priv->rx_buf[priv->rx_head][priv->
+							    rx_ptr++] =
+				    read_scc_data(priv);
+			else {
+				priv->rx_over = 2;
+				read_scc_data(priv);
+			}
+			special_condition(priv, rc);
+		}
+	}
+}
+
+
+static void special_condition(struct scc_priv *priv, int rc)
+{
+	int cb;
+	unsigned long flags;
+
+	/* See Figure 2-15. Only overrun and EOF need to be checked. */
+
+	if (rc & Rx_OVR) {
+		/* Receiver overrun */
+		priv->rx_over = 1;
+		if (priv->param.dma < 0)
+			write_scc(priv, R0, ERR_RES);
+	} else if (rc & END_FR) {
+		/* End of frame. Get byte count */
+		if (priv->param.dma >= 0) {
+			flags = claim_dma_lock();
+			cb = BUF_SIZE - get_dma_residue(priv->param.dma) -
+			    2;
+			release_dma_lock(flags);
+		} else {
+			cb = priv->rx_ptr - 2;
+		}
+		if (priv->rx_over) {
+			/* We had an overrun */
+			priv->stats.rx_errors++;
+			if (priv->rx_over == 2)
+				priv->stats.rx_length_errors++;
+			else
+				priv->stats.rx_fifo_errors++;
+			priv->rx_over = 0;
+		} else if (rc & CRC_ERR) {
+			/* Count invalid CRC only if packet length >= minimum */
+			if (cb >= 15) {
+				priv->stats.rx_errors++;
+				priv->stats.rx_crc_errors++;
+			}
+		} else {
+			if (cb >= 15) {
+				if (priv->rx_count < NUM_RX_BUF - 1) {
+					/* Put good frame in FIFO */
+					priv->rx_len[priv->rx_head] = cb;
+					priv->rx_head =
+					    (priv->rx_head +
+					     1) % NUM_RX_BUF;
+					priv->rx_count++;
+					schedule_work(&priv->rx_work);
+				} else {
+					priv->stats.rx_errors++;
+					priv->stats.rx_over_errors++;
+				}
+			}
+		}
+		/* Get ready for new frame */
+		if (priv->param.dma >= 0) {
+			flags = claim_dma_lock();
+			set_dma_addr(priv->param.dma,
+				     (int) priv->rx_buf[priv->rx_head]);
+			set_dma_count(priv->param.dma, BUF_SIZE);
+			release_dma_lock(flags);
+		} else {
+			priv->rx_ptr = 0;
+		}
+	}
+}
+
+
+static void rx_bh(void *arg)
+{
+	struct scc_priv *priv = arg;
+	int i = priv->rx_tail;
+	int cb;
+	unsigned long flags;
+	struct sk_buff *skb;
+	unsigned char *data;
+
+	spin_lock_irqsave(&priv->ring_lock, flags);
+	while (priv->rx_count) {
+		spin_unlock_irqrestore(&priv->ring_lock, flags);
+		cb = priv->rx_len[i];
+		/* Allocate buffer */
+		skb = dev_alloc_skb(cb + 1);
+		if (skb == NULL) {
+			/* Drop packet */
+			priv->stats.rx_dropped++;
+		} else {
+			/* Fill buffer */
+			data = skb_put(skb, cb + 1);
+			data[0] = 0;
+			memcpy(&data[1], priv->rx_buf[i], cb);
+			skb->dev = priv->dev;
+			skb->protocol = ntohs(ETH_P_AX25);
+			skb->mac.raw = skb->data;
+			netif_rx(skb);
+			priv->dev->last_rx = jiffies;
+			priv->stats.rx_packets++;
+			priv->stats.rx_bytes += cb;
+		}
+		spin_lock_irqsave(&priv->ring_lock, flags);
+		/* Move tail */
+		priv->rx_tail = i = (i + 1) % NUM_RX_BUF;
+		priv->rx_count--;
+	}
+	spin_unlock_irqrestore(&priv->ring_lock, flags);
+}
+
+
+static void tx_isr(struct scc_priv *priv)
+{
+	int i = priv->tx_tail, p = priv->tx_ptr;
+
+	/* Suspend TX interrupts if we don't want to send anything.
+	   See Figure 2-22. */
+	if (p == priv->tx_len[i]) {
+		write_scc(priv, R0, RES_Tx_P);
+		return;
+	}
+
+	/* Write characters */
+	while ((read_scc(priv, R0) & Tx_BUF_EMP) && p < priv->tx_len[i]) {
+		write_scc_data(priv, priv->tx_buf[i][p++], 0);
+	}
+
+	/* Reset EOM latch of Z8530 */
+	if (!priv->tx_ptr && p && priv->chip == Z8530)
+		write_scc(priv, R0, RES_EOM_L);
+
+	priv->tx_ptr = p;
+}
+
+
+static void es_isr(struct scc_priv *priv)
+{
+	int i, rr0, drr0, res;
+	unsigned long flags;
+
+	/* Read status, reset interrupt bit (open latches) */
+	rr0 = read_scc(priv, R0);
+	write_scc(priv, R0, RES_EXT_INT);
+	drr0 = priv->rr0 ^ rr0;
+	priv->rr0 = rr0;
+
+	/* Transmit underrun (2.4.9.6). We can't check the TxEOM flag, since
+	   it might have already been cleared again by AUTOEOM. */
+	if (priv->state == TX_DATA) {
+		/* Get remaining bytes */
+		i = priv->tx_tail;
+		if (priv->param.dma >= 0) {
+			disable_dma(priv->param.dma);
+			flags = claim_dma_lock();
+			res = get_dma_residue(priv->param.dma);
+			release_dma_lock(flags);
+		} else {
+			res = priv->tx_len[i] - priv->tx_ptr;
+			priv->tx_ptr = 0;
+		}
+		/* Disable DREQ / TX interrupt */
+		if (priv->param.dma >= 0 && priv->type == TYPE_TWIN)
+			outb(0, priv->card_base + TWIN_DMA_CFG);
+		else
+			write_scc(priv, R1, EXT_INT_ENAB | WT_FN_RDYFN);
+		if (res) {
+			/* Update packet statistics */
+			priv->stats.tx_errors++;
+			priv->stats.tx_fifo_errors++;
+			/* Other underrun interrupts may already be waiting */
+			write_scc(priv, R0, RES_EXT_INT);
+			write_scc(priv, R0, RES_EXT_INT);
+		} else {
+			/* Update packet statistics */
+			priv->stats.tx_packets++;
+			priv->stats.tx_bytes += priv->tx_len[i];
+			/* Remove frame from FIFO */
+			priv->tx_tail = (i + 1) % NUM_TX_BUF;
+			priv->tx_count--;
+			/* Inform upper layers */
+			netif_wake_queue(priv->dev);
+		}
+		/* Switch state */
+		write_scc(priv, R15, 0);
+		if (priv->tx_count &&
+		    (jiffies - priv->tx_start) < priv->param.txtimeout) {
+			priv->state = TX_PAUSE;
+			start_timer(priv, priv->param.txpause, 0);
+		} else {
+			priv->state = TX_TAIL;
+			start_timer(priv, priv->param.txtail, 0);
+		}
+	}
+
+	/* DCD transition */
+	if (drr0 & DCD) {
+		if (rr0 & DCD) {
+			switch (priv->state) {
+			case IDLE:
+			case WAIT:
+				priv->state = DCD_ON;
+				write_scc(priv, R15, 0);
+				start_timer(priv, priv->param.dcdon, 0);
+			}
+		} else {
+			switch (priv->state) {
+			case RX_ON:
+				rx_off(priv);
+				priv->state = DCD_OFF;
+				write_scc(priv, R15, 0);
+				start_timer(priv, priv->param.dcdoff, 0);
+			}
+		}
+	}
+
+	/* CTS transition */
+	if ((drr0 & CTS) && (~rr0 & CTS) && priv->type != TYPE_TWIN)
+		tm_isr(priv);
+
+}
+
+
+static void tm_isr(struct scc_priv *priv)
+{
+	switch (priv->state) {
+	case TX_HEAD:
+	case TX_PAUSE:
+		tx_on(priv);
+		priv->state = TX_DATA;
+		break;
+	case TX_TAIL:
+		write_scc(priv, R5, TxCRC_ENAB | Tx8);
+		priv->state = RTS_OFF;
+		if (priv->type != TYPE_TWIN)
+			write_scc(priv, R15, 0);
+		start_timer(priv, priv->param.rtsoff, 0);
+		break;
+	case RTS_OFF:
+		write_scc(priv, R15, DCDIE);
+		priv->rr0 = read_scc(priv, R0);
+		if (priv->rr0 & DCD) {
+			priv->stats.collisions++;
+			rx_on(priv);
+			priv->state = RX_ON;
+		} else {
+			priv->state = WAIT;
+			start_timer(priv, priv->param.waittime, DCDIE);
+		}
+		break;
+	case WAIT:
+		if (priv->tx_count) {
+			priv->state = TX_HEAD;
+			priv->tx_start = jiffies;
+			write_scc(priv, R5,
+				  TxCRC_ENAB | RTS | TxENAB | Tx8);
+			write_scc(priv, R15, 0);
+			start_timer(priv, priv->param.txdelay, 0);
+		} else {
+			priv->state = IDLE;
+			if (priv->type != TYPE_TWIN)
+				write_scc(priv, R15, DCDIE);
+		}
+		break;
+	case DCD_ON:
+	case DCD_OFF:
+		write_scc(priv, R15, DCDIE);
+		priv->rr0 = read_scc(priv, R0);
+		if (priv->rr0 & DCD) {
+			rx_on(priv);
+			priv->state = RX_ON;
+		} else {
+			priv->state = WAIT;
+			start_timer(priv,
+				    random() / priv->param.persist *
+				    priv->param.slottime, DCDIE);
+		}
+		break;
+	}
+}
diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c
new file mode 100644
index 0000000..b89959a
--- /dev/null
+++ b/drivers/net/hamradio/hdlcdrv.c
@@ -0,0 +1,817 @@
+/*****************************************************************************/
+
+/*
+ *	hdlcdrv.c  -- HDLC packet radio network driver.
+ *
+ *	Copyright (C) 1996-2000  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *  The driver was derived from Donald Beckers skeleton.c
+ *	Written 1993-94 by Donald Becker.
+ *
+ *  History:
+ *   0.1  21.09.1996  Started
+ *        18.10.1996  Changed to new user space access routines 
+ *                    (copy_{to,from}_user)
+ *   0.2  21.11.1996  various small changes
+ *   0.3  03.03.1997  fixed (hopefully) IP not working with ax.25 as a module
+ *   0.4  16.04.1997  init code/data tagged
+ *   0.5  30.07.1997  made HDLC buffers bigger (solves a problem with the
+ *                    soundmodem driver)
+ *   0.6  05.04.1998  add spinlocks
+ *   0.7  03.08.1999  removed some old compatibility cruft
+ *   0.8  12.02.2000  adapted to softnet driver interface
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <asm/uaccess.h>
+
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/hdlcdrv.h>
+/* prototypes for ax25_encapsulate and ax25_rebuild_header */
+#include <net/ax25.h> 
+
+/* make genksyms happy */
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include <linux/crc-ccitt.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * The name of the card. Is used for messages and in the requests for
+ * io regions, irqs and dma channels
+ */
+
+static char ax25_bcast[AX25_ADDR_LEN] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static char ax25_nocall[AX25_ADDR_LEN] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+/* --------------------------------------------------------------------- */
+
+#define KISS_VERBOSE
+
+/* --------------------------------------------------------------------- */
+
+#define PARAM_TXDELAY   1
+#define PARAM_PERSIST   2
+#define PARAM_SLOTTIME  3
+#define PARAM_TXTAIL    4
+#define PARAM_FULLDUP   5
+#define PARAM_HARDWARE  6
+#define PARAM_RETURN    255
+
+/* --------------------------------------------------------------------- */
+/*
+ * the CRC routines are stolen from WAMPES
+ * by Dieter Deyke
+ */
+
+
+/*---------------------------------------------------------------------------*/
+
+static inline void append_crc_ccitt(unsigned char *buffer, int len)
+{
+ 	unsigned int crc = crc_ccitt(0xffff, buffer, len) ^ 0xffff;
+	*buffer++ = crc;
+	*buffer++ = crc >> 8;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static inline int check_crc_ccitt(const unsigned char *buf, int cnt)
+{
+	return (crc_ccitt(0xffff, buf, cnt) & 0xffff) == 0xf0b8;
+}
+
+/*---------------------------------------------------------------------------*/
+
+#if 0
+static int calc_crc_ccitt(const unsigned char *buf, int cnt)
+{
+	unsigned int crc = 0xffff;
+
+	for (; cnt > 0; cnt--)
+		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
+	crc ^= 0xffff;
+	return (crc & 0xffff);
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#define tenms_to_2flags(s,tenms) ((tenms * s->par.bitrate) / 100 / 16)
+
+/* ---------------------------------------------------------------------- */
+/*
+ * The HDLC routines
+ */
+
+static int hdlc_rx_add_bytes(struct hdlcdrv_state *s, unsigned int bits, 
+			     int num)
+{
+	int added = 0;
+	
+	while (s->hdlcrx.rx_state && num >= 8) {
+		if (s->hdlcrx.len >= sizeof(s->hdlcrx.buffer)) {
+			s->hdlcrx.rx_state = 0;
+			return 0;
+		}
+		*s->hdlcrx.bp++ = bits >> (32-num);
+		s->hdlcrx.len++;
+		num -= 8;
+		added += 8;
+	}
+	return added;
+}
+
+static void hdlc_rx_flag(struct net_device *dev, struct hdlcdrv_state *s)
+{
+	struct sk_buff *skb;
+	int pkt_len;
+	unsigned char *cp;
+
+	if (s->hdlcrx.len < 4) 
+		return;
+	if (!check_crc_ccitt(s->hdlcrx.buffer, s->hdlcrx.len)) 
+		return;
+	pkt_len = s->hdlcrx.len - 2 + 1; /* KISS kludge */
+	if (!(skb = dev_alloc_skb(pkt_len))) {
+		printk("%s: memory squeeze, dropping packet\n", dev->name);
+		s->stats.rx_dropped++;
+		return;
+	}
+	skb->dev = dev;
+	cp = skb_put(skb, pkt_len);
+	*cp++ = 0; /* KISS kludge */
+	memcpy(cp, s->hdlcrx.buffer, pkt_len - 1);
+	skb->protocol = htons(ETH_P_AX25);
+	skb->mac.raw = skb->data;
+	netif_rx(skb);
+	dev->last_rx = jiffies;
+	s->stats.rx_packets++;
+}
+
+void hdlcdrv_receiver(struct net_device *dev, struct hdlcdrv_state *s)
+{
+	int i;
+	unsigned int mask1, mask2, mask3, mask4, mask5, mask6, word;
+	
+	if (!s || s->magic != HDLCDRV_MAGIC) 
+		return;
+	if (test_and_set_bit(0, &s->hdlcrx.in_hdlc_rx))
+		return;
+
+	while (!hdlcdrv_hbuf_empty(&s->hdlcrx.hbuf)) {
+		word = hdlcdrv_hbuf_get(&s->hdlcrx.hbuf);	
+
+#ifdef HDLCDRV_DEBUG
+		hdlcdrv_add_bitbuffer_word(&s->bitbuf_hdlc, word);
+#endif /* HDLCDRV_DEBUG */
+	       	s->hdlcrx.bitstream >>= 16;
+		s->hdlcrx.bitstream |= word << 16;
+		s->hdlcrx.bitbuf >>= 16;
+		s->hdlcrx.bitbuf |= word << 16;
+		s->hdlcrx.numbits += 16;
+		for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00,
+		    mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff; 
+		    i >= 0; 
+		    i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1, 
+		    mask5 <<= 1, mask6 = (mask6 << 1) | 1) {
+			if ((s->hdlcrx.bitstream & mask1) == mask1)
+				s->hdlcrx.rx_state = 0; /* abort received */
+			else if ((s->hdlcrx.bitstream & mask2) == mask3) {
+				/* flag received */
+				if (s->hdlcrx.rx_state) {
+					hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf 
+							  << (8+i),
+							  s->hdlcrx.numbits
+							  -8-i);
+					hdlc_rx_flag(dev, s);
+				}
+				s->hdlcrx.len = 0;
+				s->hdlcrx.bp = s->hdlcrx.buffer;
+				s->hdlcrx.rx_state = 1;
+				s->hdlcrx.numbits = i;
+			} else if ((s->hdlcrx.bitstream & mask4) == mask5) {
+				/* stuffed bit */
+				s->hdlcrx.numbits--;
+				s->hdlcrx.bitbuf = (s->hdlcrx.bitbuf & (~mask6)) |
+					((s->hdlcrx.bitbuf & mask6) << 1);
+			}
+		}
+		s->hdlcrx.numbits -= hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf,
+						       s->hdlcrx.numbits);
+	}
+	clear_bit(0, &s->hdlcrx.in_hdlc_rx);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static inline void do_kiss_params(struct hdlcdrv_state *s,
+				  unsigned char *data, unsigned long len)
+{
+
+#ifdef KISS_VERBOSE
+#define PKP(a,b) printk(KERN_INFO "hdlcdrv.c: channel params: " a "\n", b)
+#else /* KISS_VERBOSE */	      
+#define PKP(a,b) 
+#endif /* KISS_VERBOSE */	      
+
+	if (len < 2)
+		return;
+	switch(data[0]) {
+	case PARAM_TXDELAY:
+		s->ch_params.tx_delay = data[1];
+		PKP("TX delay = %ums", 10 * s->ch_params.tx_delay);
+		break;
+	case PARAM_PERSIST:   
+		s->ch_params.ppersist = data[1];
+		PKP("p persistence = %u", s->ch_params.ppersist);
+		break;
+	case PARAM_SLOTTIME:  
+		s->ch_params.slottime = data[1];
+		PKP("slot time = %ums", s->ch_params.slottime);
+		break;
+	case PARAM_TXTAIL:    
+		s->ch_params.tx_tail = data[1];
+		PKP("TX tail = %ums", s->ch_params.tx_tail);
+		break;
+	case PARAM_FULLDUP:   
+		s->ch_params.fulldup = !!data[1];
+		PKP("%s duplex", s->ch_params.fulldup ? "full" : "half");
+		break;
+	default:
+		break;
+	}
+#undef PKP
+}
+
+/* ---------------------------------------------------------------------- */
+
+void hdlcdrv_transmitter(struct net_device *dev, struct hdlcdrv_state *s)
+{
+	unsigned int mask1, mask2, mask3;
+	int i;
+	struct sk_buff *skb;
+	int pkt_len;
+
+	if (!s || s->magic != HDLCDRV_MAGIC) 
+		return;
+	if (test_and_set_bit(0, &s->hdlctx.in_hdlc_tx))
+		return;
+	for (;;) {
+		if (s->hdlctx.numbits >= 16) {
+			if (hdlcdrv_hbuf_full(&s->hdlctx.hbuf)) {
+				clear_bit(0, &s->hdlctx.in_hdlc_tx);
+				return;
+			}
+			hdlcdrv_hbuf_put(&s->hdlctx.hbuf, s->hdlctx.bitbuf);
+			s->hdlctx.bitbuf >>= 16;
+			s->hdlctx.numbits -= 16;
+		}
+		switch (s->hdlctx.tx_state) {
+		default:
+			clear_bit(0, &s->hdlctx.in_hdlc_tx);
+			return;
+		case 0:
+		case 1:
+			if (s->hdlctx.numflags) {
+				s->hdlctx.numflags--;
+				s->hdlctx.bitbuf |= 
+					0x7e7e << s->hdlctx.numbits;
+				s->hdlctx.numbits += 16;
+				break;
+			}
+			if (s->hdlctx.tx_state == 1) {
+				clear_bit(0, &s->hdlctx.in_hdlc_tx);
+				return;
+			}
+			if (!(skb = s->skb)) {
+				int flgs = tenms_to_2flags(s, s->ch_params.tx_tail);
+				if (flgs < 2)
+					flgs = 2;
+				s->hdlctx.tx_state = 1;
+				s->hdlctx.numflags = flgs;
+				break;
+			}
+			s->skb = NULL;
+			netif_wake_queue(dev);
+			pkt_len = skb->len-1; /* strip KISS byte */
+			if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) {
+				s->hdlctx.tx_state = 0;
+				s->hdlctx.numflags = 1;
+				dev_kfree_skb_irq(skb);
+				break;
+			}
+			memcpy(s->hdlctx.buffer, skb->data+1, pkt_len);
+			dev_kfree_skb_irq(skb);
+			s->hdlctx.bp = s->hdlctx.buffer;
+			append_crc_ccitt(s->hdlctx.buffer, pkt_len);
+			s->hdlctx.len = pkt_len+2; /* the appended CRC */
+			s->hdlctx.tx_state = 2;
+			s->hdlctx.bitstream = 0;
+			s->stats.tx_packets++;
+			break;
+		case 2:
+			if (!s->hdlctx.len) {
+				s->hdlctx.tx_state = 0;
+				s->hdlctx.numflags = 1;
+				break;
+			}
+			s->hdlctx.len--;
+			s->hdlctx.bitbuf |= *s->hdlctx.bp <<
+				s->hdlctx.numbits;
+			s->hdlctx.bitstream >>= 8;
+			s->hdlctx.bitstream |= (*s->hdlctx.bp++) << 16;
+			mask1 = 0x1f000;
+			mask2 = 0x10000;
+			mask3 = 0xffffffff >> (31-s->hdlctx.numbits);
+			s->hdlctx.numbits += 8;
+			for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1, 
+			    mask3 = (mask3 << 1) | 1) {
+				if ((s->hdlctx.bitstream & mask1) != mask1) 
+					continue;
+				s->hdlctx.bitstream &= ~mask2;
+				s->hdlctx.bitbuf = 
+					(s->hdlctx.bitbuf & mask3) |
+						((s->hdlctx.bitbuf & 
+						 (~mask3)) << 1);
+				s->hdlctx.numbits++;
+				mask3 = (mask3 << 1) | 1;
+			}
+			break;
+		}
+	}
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void start_tx(struct net_device *dev, struct hdlcdrv_state *s)
+{
+	s->hdlctx.tx_state = 0;
+	s->hdlctx.numflags = tenms_to_2flags(s, s->ch_params.tx_delay);
+	s->hdlctx.bitbuf = s->hdlctx.bitstream = s->hdlctx.numbits = 0;
+	hdlcdrv_transmitter(dev, s);
+	s->hdlctx.ptt = 1;
+	s->ptt_keyed++;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static unsigned short random_seed;
+
+static inline unsigned short random_num(void)
+{
+	random_seed = 28629 * random_seed + 157;
+	return random_seed;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void hdlcdrv_arbitrate(struct net_device *dev, struct hdlcdrv_state *s)
+{
+	if (!s || s->magic != HDLCDRV_MAGIC || s->hdlctx.ptt || !s->skb) 
+		return;
+	if (s->ch_params.fulldup) {
+		start_tx(dev, s);
+		return;
+	}
+	if (s->hdlcrx.dcd) {
+		s->hdlctx.slotcnt = s->ch_params.slottime;
+		return;
+	}
+	if ((--s->hdlctx.slotcnt) > 0)
+		return;
+	s->hdlctx.slotcnt = s->ch_params.slottime;
+	if ((random_num() % 256) > s->ch_params.ppersist)
+		return;
+	start_tx(dev, s);
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== network driver interface =========================
+ */
+
+static int hdlcdrv_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+	struct hdlcdrv_state *sm = netdev_priv(dev);
+
+	if (skb->data[0] != 0) {
+		do_kiss_params(sm, skb->data, skb->len);
+		dev_kfree_skb(skb);
+		return 0;
+	}
+	if (sm->skb)
+		return -1;
+	netif_stop_queue(dev);
+	sm->skb = skb;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_set_mac_address(struct net_device *dev, void *addr)
+{
+	struct sockaddr *sa = (struct sockaddr *)addr;
+
+	/* addr is an AX.25 shifted ASCII mac address */
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); 
+	return 0;                                         
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct net_device_stats *hdlcdrv_get_stats(struct net_device *dev)
+{
+	struct hdlcdrv_state *sm = netdev_priv(dev);
+
+	/* 
+	 * Get the current statistics.  This may be called with the
+	 * card open or closed. 
+	 */
+	return &sm->stats;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+
+static int hdlcdrv_open(struct net_device *dev)
+{
+	struct hdlcdrv_state *s = netdev_priv(dev);
+	int i;
+
+	if (!s->ops || !s->ops->open)
+		return -ENODEV;
+
+	/*
+	 * initialise some variables
+	 */
+	s->opened = 1;
+	s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
+	s->hdlcrx.in_hdlc_rx = 0;
+	s->hdlcrx.rx_state = 0;
+	
+	s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
+	s->hdlctx.in_hdlc_tx = 0;
+	s->hdlctx.tx_state = 1;
+	s->hdlctx.numflags = 0;
+	s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0;
+	s->hdlctx.ptt = 0;
+	s->hdlctx.slotcnt = s->ch_params.slottime;
+	s->hdlctx.calibrate = 0;
+
+	i = s->ops->open(dev);
+	if (i)
+		return i;
+	netif_start_queue(dev);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/* 
+ * The inverse routine to hdlcdrv_open(). 
+ */
+
+static int hdlcdrv_close(struct net_device *dev)
+{
+	struct hdlcdrv_state *s = netdev_priv(dev);
+	int i = 0;
+
+	netif_stop_queue(dev);
+
+	if (s->ops && s->ops->close)
+		i = s->ops->close(dev);
+	if (s->skb)
+		dev_kfree_skb(s->skb);
+	s->skb = NULL;
+	s->opened = 0;
+	return i;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct hdlcdrv_state *s = netdev_priv(dev);
+	struct hdlcdrv_ioctl bi;
+
+	if (cmd != SIOCDEVPRIVATE) {
+		if (s->ops && s->ops->ioctl)
+			return s->ops->ioctl(dev, ifr, &bi, cmd);
+		return -ENOIOCTLCMD;
+	}
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+
+	switch (bi.cmd) {
+	default:
+		if (s->ops && s->ops->ioctl)
+			return s->ops->ioctl(dev, ifr, &bi, cmd);
+		return -ENOIOCTLCMD;
+
+	case HDLCDRVCTL_GETCHANNELPAR:
+		bi.data.cp.tx_delay = s->ch_params.tx_delay;
+		bi.data.cp.tx_tail = s->ch_params.tx_tail;
+		bi.data.cp.slottime = s->ch_params.slottime;
+		bi.data.cp.ppersist = s->ch_params.ppersist;
+		bi.data.cp.fulldup = s->ch_params.fulldup;
+		break;
+
+	case HDLCDRVCTL_SETCHANNELPAR:
+		if (!capable(CAP_NET_ADMIN))
+			return -EACCES;
+		s->ch_params.tx_delay = bi.data.cp.tx_delay;
+		s->ch_params.tx_tail = bi.data.cp.tx_tail;
+		s->ch_params.slottime = bi.data.cp.slottime;
+		s->ch_params.ppersist = bi.data.cp.ppersist;
+		s->ch_params.fulldup = bi.data.cp.fulldup;
+		s->hdlctx.slotcnt = 1;
+		return 0;
+		
+	case HDLCDRVCTL_GETMODEMPAR:
+		bi.data.mp.iobase = dev->base_addr;
+		bi.data.mp.irq = dev->irq;
+		bi.data.mp.dma = dev->dma;
+		bi.data.mp.dma2 = s->ptt_out.dma2;
+		bi.data.mp.seriobase = s->ptt_out.seriobase;
+		bi.data.mp.pariobase = s->ptt_out.pariobase;
+		bi.data.mp.midiiobase = s->ptt_out.midiiobase;
+		break;
+
+	case HDLCDRVCTL_SETMODEMPAR:
+		if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev))
+			return -EACCES;
+		dev->base_addr = bi.data.mp.iobase;
+		dev->irq = bi.data.mp.irq;
+		dev->dma = bi.data.mp.dma;
+		s->ptt_out.dma2 = bi.data.mp.dma2;
+		s->ptt_out.seriobase = bi.data.mp.seriobase;
+		s->ptt_out.pariobase = bi.data.mp.pariobase;
+		s->ptt_out.midiiobase = bi.data.mp.midiiobase;
+		return 0;	
+	
+	case HDLCDRVCTL_GETSTAT:
+		bi.data.cs.ptt = hdlcdrv_ptt(s);
+		bi.data.cs.dcd = s->hdlcrx.dcd;
+		bi.data.cs.ptt_keyed = s->ptt_keyed;
+		bi.data.cs.tx_packets = s->stats.tx_packets;
+		bi.data.cs.tx_errors = s->stats.tx_errors;
+		bi.data.cs.rx_packets = s->stats.rx_packets;
+		bi.data.cs.rx_errors = s->stats.rx_errors;
+		break;		
+
+	case HDLCDRVCTL_OLDGETSTAT:
+		bi.data.ocs.ptt = hdlcdrv_ptt(s);
+		bi.data.ocs.dcd = s->hdlcrx.dcd;
+		bi.data.ocs.ptt_keyed = s->ptt_keyed;
+		break;		
+
+	case HDLCDRVCTL_CALIBRATE:
+		if(!capable(CAP_SYS_RAWIO))
+			return -EPERM;
+		s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16;
+		return 0;
+
+	case HDLCDRVCTL_GETSAMPLES:
+#ifndef HDLCDRV_DEBUG
+		return -EPERM;
+#else /* HDLCDRV_DEBUG */
+		if (s->bitbuf_channel.rd == s->bitbuf_channel.wr) 
+			return -EAGAIN;
+		bi.data.bits = 
+			s->bitbuf_channel.buffer[s->bitbuf_channel.rd];
+		s->bitbuf_channel.rd = (s->bitbuf_channel.rd+1) %
+			sizeof(s->bitbuf_channel.buffer);
+		break;
+#endif /* HDLCDRV_DEBUG */
+				
+	case HDLCDRVCTL_GETBITS:
+#ifndef HDLCDRV_DEBUG
+		return -EPERM;
+#else /* HDLCDRV_DEBUG */
+		if (s->bitbuf_hdlc.rd == s->bitbuf_hdlc.wr) 
+			return -EAGAIN;
+		bi.data.bits = 
+			s->bitbuf_hdlc.buffer[s->bitbuf_hdlc.rd];
+		s->bitbuf_hdlc.rd = (s->bitbuf_hdlc.rd+1) %
+			sizeof(s->bitbuf_hdlc.buffer);
+		break;		
+#endif /* HDLCDRV_DEBUG */
+
+	case HDLCDRVCTL_DRIVERNAME:
+		if (s->ops && s->ops->drvname) {
+			strncpy(bi.data.drivername, s->ops->drvname, 
+				sizeof(bi.data.drivername));
+			break;
+		}
+		bi.data.drivername[0] = '\0';
+		break;
+		
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Initialize fields in hdlcdrv
+ */
+static void hdlcdrv_setup(struct net_device *dev)
+{
+	static const struct hdlcdrv_channel_params dflt_ch_params = { 
+		20, 2, 10, 40, 0 
+	};
+	struct hdlcdrv_state *s = netdev_priv(dev);
+
+	/*
+	 * initialize the hdlcdrv_state struct
+	 */
+	s->ch_params = dflt_ch_params;
+	s->ptt_keyed = 0;
+
+	spin_lock_init(&s->hdlcrx.hbuf.lock);
+	s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
+	s->hdlcrx.in_hdlc_rx = 0;
+	s->hdlcrx.rx_state = 0;
+	
+	spin_lock_init(&s->hdlctx.hbuf.lock);
+	s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
+	s->hdlctx.in_hdlc_tx = 0;
+	s->hdlctx.tx_state = 1;
+	s->hdlctx.numflags = 0;
+	s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0;
+	s->hdlctx.ptt = 0;
+	s->hdlctx.slotcnt = s->ch_params.slottime;
+	s->hdlctx.calibrate = 0;
+
+#ifdef HDLCDRV_DEBUG
+	s->bitbuf_channel.rd = s->bitbuf_channel.wr = 0;
+	s->bitbuf_channel.shreg = 0x80;
+
+	s->bitbuf_hdlc.rd = s->bitbuf_hdlc.wr = 0;
+	s->bitbuf_hdlc.shreg = 0x80;
+#endif /* HDLCDRV_DEBUG */
+
+	/*
+	 * initialize the device struct
+	 */
+	dev->open = hdlcdrv_open;
+	dev->stop = hdlcdrv_close;
+	dev->do_ioctl = hdlcdrv_ioctl;
+	dev->hard_start_xmit = hdlcdrv_send_packet;
+	dev->get_stats = hdlcdrv_get_stats;
+
+	/* Fill in the fields of the device structure */
+
+	s->skb = NULL;
+	
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+	dev->hard_header = ax25_encapsulate;
+	dev->rebuild_header = ax25_rebuild_header;
+#else /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+	dev->hard_header = NULL;
+	dev->rebuild_header = NULL;
+#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+	dev->set_mac_address = hdlcdrv_set_mac_address;
+	
+	dev->type = ARPHRD_AX25;           /* AF_AX25 device */
+	dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+	dev->mtu = AX25_DEF_PACLEN;        /* eth_mtu is the default */
+	dev->addr_len = AX25_ADDR_LEN;     /* sizeof an ax.25 address */
+	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+	memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN);
+	dev->tx_queue_len = 16;
+}
+
+/* --------------------------------------------------------------------- */
+struct net_device *hdlcdrv_register(const struct hdlcdrv_ops *ops,
+				    unsigned int privsize, const char *ifname,
+				    unsigned int baseaddr, unsigned int irq, 
+				    unsigned int dma) 
+{
+	struct net_device *dev;
+	struct hdlcdrv_state *s;
+	int err;
+
+	BUG_ON(ops == NULL);
+
+	if (privsize < sizeof(struct hdlcdrv_state))
+		privsize = sizeof(struct hdlcdrv_state);
+
+	dev = alloc_netdev(privsize, ifname, hdlcdrv_setup);
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	/*
+	 * initialize part of the hdlcdrv_state struct
+	 */
+	s = netdev_priv(dev);
+	s->magic = HDLCDRV_MAGIC;
+	s->ops = ops;
+	dev->base_addr = baseaddr;
+	dev->irq = irq;
+	dev->dma = dma;
+
+	err = register_netdev(dev);
+	if (err < 0) {
+		printk(KERN_WARNING "hdlcdrv: cannot register net "
+		       "device %s\n", dev->name);
+		free_netdev(dev);
+		dev = ERR_PTR(err);
+	}
+	return dev;
+}
+
+/* --------------------------------------------------------------------- */
+
+void hdlcdrv_unregister(struct net_device *dev) 
+{
+	struct hdlcdrv_state *s = netdev_priv(dev);
+
+	BUG_ON(s->magic != HDLCDRV_MAGIC);
+
+	if (s->opened && s->ops->close)
+		s->ops->close(dev);
+	unregister_netdev(dev);
+	
+	free_netdev(dev);
+}
+
+/* --------------------------------------------------------------------- */
+
+EXPORT_SYMBOL(hdlcdrv_receiver);
+EXPORT_SYMBOL(hdlcdrv_transmitter);
+EXPORT_SYMBOL(hdlcdrv_arbitrate);
+EXPORT_SYMBOL(hdlcdrv_register);
+EXPORT_SYMBOL(hdlcdrv_unregister);
+
+/* --------------------------------------------------------------------- */
+
+static int __init hdlcdrv_init_driver(void)
+{
+	printk(KERN_INFO "hdlcdrv: (C) 1996-2000 Thomas Sailer HB9JNX/AE4WA\n");
+	printk(KERN_INFO "hdlcdrv: version 0.8 compiled " __TIME__ " " __DATE__ "\n");
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void __exit hdlcdrv_cleanup_driver(void)
+{
+	printk(KERN_INFO "hdlcdrv: cleanup\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder");
+MODULE_LICENSE("GPL");
+module_init(hdlcdrv_init_driver);
+module_exit(hdlcdrv_cleanup_driver);
+
+/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c
new file mode 100644
index 0000000..d9ea080
--- /dev/null
+++ b/drivers/net/hamradio/mkiss.c
@@ -0,0 +1,951 @@
+/*
+ *	MKISS Driver
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * 		This module implements the AX.25 protocol for kernel-based
+ *		devices like TTYs. It interfaces between a raw TTY, and the
+ *		kernel's AX.25 protocol layers, just like slip.c.
+ *		AX.25 needs to be separated from slip.c while slip.c is no
+ *		longer a static kernel device since it is a module.
+ *		This method clears the way to implement other kiss protocols
+ *		like mkiss smack g8bpq ..... so far only mkiss is implemented.
+ *
+ * Hans Alblas <hans@esrac.ele.tue.nl>
+ *
+ *	History
+ *	Jonathan (G4KLX)	Fixed to match Linux networking changes - 2.1.15.
+ *	Matthias (DG2FEF)       Added support for FlexNet CRC (on special request)
+ *                              Fixed bug in ax25_close(): dev_lock_wait() was
+ *                              called twice, causing a deadlock.
+ *	Jeroen (PE1RXQ)		Removed old MKISS_MAGIC stuff and calls to
+ *				MOD_*_USE_COUNT
+ *				Remove cli() and fix rtnl lock usage.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/system.h>
+#include <linux/bitops.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/major.h>
+#include <linux/init.h>
+#include <linux/rtnetlink.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+
+#include <net/ax25.h>
+
+#include "mkiss.h"
+
+#ifdef CONFIG_INET
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#endif
+
+static char banner[] __initdata = KERN_INFO "mkiss: AX.25 Multikiss, Hans Albas PE1AYX\n";
+
+typedef struct ax25_ctrl {
+	struct ax_disp ctrl;	/* 				*/
+	struct net_device  dev;	/* the device			*/
+} ax25_ctrl_t;
+
+static ax25_ctrl_t **ax25_ctrls;
+
+int ax25_maxdev = AX25_MAXDEV;		/* Can be overridden with insmod! */
+
+static struct tty_ldisc	ax_ldisc;
+
+static int ax25_init(struct net_device *);
+static int kiss_esc(unsigned char *, unsigned char *, int);
+static int kiss_esc_crc(unsigned char *, unsigned char *, unsigned short, int);
+static void kiss_unesc(struct ax_disp *, unsigned char);
+
+/*---------------------------------------------------------------------------*/
+
+static const unsigned short Crc_flex_table[] = {
+  0x0f87, 0x1e0e, 0x2c95, 0x3d1c, 0x49a3, 0x582a, 0x6ab1, 0x7b38,
+  0x83cf, 0x9246, 0xa0dd, 0xb154, 0xc5eb, 0xd462, 0xe6f9, 0xf770,
+  0x1f06, 0x0e8f, 0x3c14, 0x2d9d, 0x5922, 0x48ab, 0x7a30, 0x6bb9,
+  0x934e, 0x82c7, 0xb05c, 0xa1d5, 0xd56a, 0xc4e3, 0xf678, 0xe7f1,
+  0x2e85, 0x3f0c, 0x0d97, 0x1c1e, 0x68a1, 0x7928, 0x4bb3, 0x5a3a,
+  0xa2cd, 0xb344, 0x81df, 0x9056, 0xe4e9, 0xf560, 0xc7fb, 0xd672,
+  0x3e04, 0x2f8d, 0x1d16, 0x0c9f, 0x7820, 0x69a9, 0x5b32, 0x4abb,
+  0xb24c, 0xa3c5, 0x915e, 0x80d7, 0xf468, 0xe5e1, 0xd77a, 0xc6f3,
+  0x4d83, 0x5c0a, 0x6e91, 0x7f18, 0x0ba7, 0x1a2e, 0x28b5, 0x393c,
+  0xc1cb, 0xd042, 0xe2d9, 0xf350, 0x87ef, 0x9666, 0xa4fd, 0xb574,
+  0x5d02, 0x4c8b, 0x7e10, 0x6f99, 0x1b26, 0x0aaf, 0x3834, 0x29bd,
+  0xd14a, 0xc0c3, 0xf258, 0xe3d1, 0x976e, 0x86e7, 0xb47c, 0xa5f5,
+  0x6c81, 0x7d08, 0x4f93, 0x5e1a, 0x2aa5, 0x3b2c, 0x09b7, 0x183e,
+  0xe0c9, 0xf140, 0xc3db, 0xd252, 0xa6ed, 0xb764, 0x85ff, 0x9476,
+  0x7c00, 0x6d89, 0x5f12, 0x4e9b, 0x3a24, 0x2bad, 0x1936, 0x08bf,
+  0xf048, 0xe1c1, 0xd35a, 0xc2d3, 0xb66c, 0xa7e5, 0x957e, 0x84f7,
+  0x8b8f, 0x9a06, 0xa89d, 0xb914, 0xcdab, 0xdc22, 0xeeb9, 0xff30,
+  0x07c7, 0x164e, 0x24d5, 0x355c, 0x41e3, 0x506a, 0x62f1, 0x7378,
+  0x9b0e, 0x8a87, 0xb81c, 0xa995, 0xdd2a, 0xcca3, 0xfe38, 0xefb1,
+  0x1746, 0x06cf, 0x3454, 0x25dd, 0x5162, 0x40eb, 0x7270, 0x63f9,
+  0xaa8d, 0xbb04, 0x899f, 0x9816, 0xeca9, 0xfd20, 0xcfbb, 0xde32,
+  0x26c5, 0x374c, 0x05d7, 0x145e, 0x60e1, 0x7168, 0x43f3, 0x527a,
+  0xba0c, 0xab85, 0x991e, 0x8897, 0xfc28, 0xeda1, 0xdf3a, 0xceb3,
+  0x3644, 0x27cd, 0x1556, 0x04df, 0x7060, 0x61e9, 0x5372, 0x42fb,
+  0xc98b, 0xd802, 0xea99, 0xfb10, 0x8faf, 0x9e26, 0xacbd, 0xbd34,
+  0x45c3, 0x544a, 0x66d1, 0x7758, 0x03e7, 0x126e, 0x20f5, 0x317c,
+  0xd90a, 0xc883, 0xfa18, 0xeb91, 0x9f2e, 0x8ea7, 0xbc3c, 0xadb5,
+  0x5542, 0x44cb, 0x7650, 0x67d9, 0x1366, 0x02ef, 0x3074, 0x21fd,
+  0xe889, 0xf900, 0xcb9b, 0xda12, 0xaead, 0xbf24, 0x8dbf, 0x9c36,
+  0x64c1, 0x7548, 0x47d3, 0x565a, 0x22e5, 0x336c, 0x01f7, 0x107e,
+  0xf808, 0xe981, 0xdb1a, 0xca93, 0xbe2c, 0xafa5, 0x9d3e, 0x8cb7,
+  0x7440, 0x65c9, 0x5752, 0x46db, 0x3264, 0x23ed, 0x1176, 0x00ff
+};
+
+/*---------------------------------------------------------------------------*/
+
+static unsigned short calc_crc_flex(unsigned char *cp, int size)
+{
+    unsigned short crc = 0xffff;
+    
+    while (size--)
+	crc = (crc << 8) ^ Crc_flex_table[((crc >> 8) ^ *cp++) & 0xff];
+
+    return crc;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static int check_crc_flex(unsigned char *cp, int size)
+{
+  unsigned short crc = 0xffff;
+
+  if (size < 3)
+      return -1;
+
+  while (size--)
+      crc = (crc << 8) ^ Crc_flex_table[((crc >> 8) ^ *cp++) & 0xff];
+
+  if ((crc & 0xffff) != 0x7070) 
+      return -1;
+
+  return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/* Find a free channel, and link in this `tty' line. */
+static inline struct ax_disp *ax_alloc(void)
+{
+	ax25_ctrl_t *axp=NULL;
+	int i;
+
+	for (i = 0; i < ax25_maxdev; i++) {
+		axp = ax25_ctrls[i];
+
+		/* Not allocated ? */
+		if (axp == NULL)
+			break;
+
+		/* Not in use ? */
+		if (!test_and_set_bit(AXF_INUSE, &axp->ctrl.flags))
+			break;
+	}
+
+	/* Sorry, too many, all slots in use */
+	if (i >= ax25_maxdev)
+		return NULL;
+
+	/* If no channels are available, allocate one */
+	if (axp == NULL && (ax25_ctrls[i] = kmalloc(sizeof(ax25_ctrl_t), GFP_KERNEL)) != NULL) {
+		axp = ax25_ctrls[i];
+	}
+	memset(axp, 0, sizeof(ax25_ctrl_t));
+
+	/* Initialize channel control data */
+	set_bit(AXF_INUSE, &axp->ctrl.flags);
+	sprintf(axp->dev.name, "ax%d", i++);
+	axp->ctrl.tty      = NULL;
+	axp->dev.base_addr = i;
+	axp->dev.priv      = (void *)&axp->ctrl;
+	axp->dev.next      = NULL;
+	axp->dev.init      = ax25_init;
+
+	if (axp != NULL) {
+		/*
+		 * register device so that it can be ifconfig'ed
+		 * ax25_init() will be called as a side-effect
+		 * SIDE-EFFECT WARNING: ax25_init() CLEARS axp->ctrl !
+		 */
+		if (register_netdev(&axp->dev) == 0) {
+			/* (Re-)Set the INUSE bit.   Very Important! */
+			set_bit(AXF_INUSE, &axp->ctrl.flags);
+			axp->ctrl.dev = &axp->dev;
+			axp->dev.priv = (void *) &axp->ctrl;
+
+			return &axp->ctrl;
+		} else {
+			clear_bit(AXF_INUSE,&axp->ctrl.flags);
+			printk(KERN_ERR "mkiss: ax_alloc() - register_netdev() failure.\n");
+		}
+	}
+
+	return NULL;
+}
+
+/* Free an AX25 channel. */
+static inline void ax_free(struct ax_disp *ax)
+{
+	/* Free all AX25 frame buffers. */
+	if (ax->rbuff)
+		kfree(ax->rbuff);
+	ax->rbuff = NULL;
+	if (ax->xbuff)
+		kfree(ax->xbuff);
+	ax->xbuff = NULL;
+	if (!test_and_clear_bit(AXF_INUSE, &ax->flags))
+		printk(KERN_ERR "mkiss: %s: ax_free for already free unit.\n", ax->dev->name);
+}
+
+static void ax_changedmtu(struct ax_disp *ax)
+{
+	struct net_device *dev = ax->dev;
+	unsigned char *xbuff, *rbuff, *oxbuff, *orbuff;
+	int len;
+
+	len = dev->mtu * 2;
+
+	/*
+	 * allow for arrival of larger UDP packets, even if we say not to
+	 * also fixes a bug in which SunOS sends 512-byte packets even with
+	 * an MSS of 128
+	 */
+	if (len < 576 * 2)
+		len = 576 * 2;
+
+	xbuff = kmalloc(len + 4, GFP_ATOMIC);
+	rbuff = kmalloc(len + 4, GFP_ATOMIC);
+
+	if (xbuff == NULL || rbuff == NULL)  {
+		printk(KERN_ERR "mkiss: %s: unable to grow ax25 buffers, MTU change cancelled.\n",
+		       ax->dev->name);
+		dev->mtu = ax->mtu;
+		if (xbuff != NULL)
+			kfree(xbuff);
+		if (rbuff != NULL)
+			kfree(rbuff);
+		return;
+	}
+
+	spin_lock_bh(&ax->buflock);
+
+	oxbuff    = ax->xbuff;
+	ax->xbuff = xbuff;
+	orbuff    = ax->rbuff;
+	ax->rbuff = rbuff;
+
+	if (ax->xleft) {
+		if (ax->xleft <= len) {
+			memcpy(ax->xbuff, ax->xhead, ax->xleft);
+		} else  {
+			ax->xleft = 0;
+			ax->tx_dropped++;
+		}
+	}
+
+	ax->xhead = ax->xbuff;
+
+	if (ax->rcount) {
+		if (ax->rcount <= len) {
+			memcpy(ax->rbuff, orbuff, ax->rcount);
+		} else  {
+			ax->rcount = 0;
+			ax->rx_over_errors++;
+			set_bit(AXF_ERROR, &ax->flags);
+		}
+	}
+
+	ax->mtu      = dev->mtu + 73;
+	ax->buffsize = len;
+
+	spin_unlock_bh(&ax->buflock);
+
+	if (oxbuff != NULL)
+		kfree(oxbuff);
+	if (orbuff != NULL)
+		kfree(orbuff);
+}
+
+
+/* Set the "sending" flag.  This must be atomic. */
+static inline void ax_lock(struct ax_disp *ax)
+{
+	netif_stop_queue(ax->dev);
+}
+
+
+/* Clear the "sending" flag.  This must be atomic. */
+static inline void ax_unlock(struct ax_disp *ax)
+{
+	netif_start_queue(ax->dev);
+}
+
+/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */
+static void ax_bump(struct ax_disp *ax)
+{
+	struct sk_buff *skb;
+	int count;
+
+	spin_lock_bh(&ax->buflock);
+	if (ax->rbuff[0] > 0x0f) {
+		if (ax->rbuff[0] & 0x20) {
+		        ax->crcmode = CRC_MODE_FLEX;
+			if (check_crc_flex(ax->rbuff, ax->rcount) < 0) {
+			        ax->rx_errors++;
+				return;
+			}
+			ax->rcount -= 2;
+                        /* dl9sau bugfix: the trailling two bytes flexnet crc
+                         * will not be passed to the kernel. thus we have
+                         * to correct the kissparm signature, because it
+                         * indicates a crc but there's none
+			 */
+                        *ax->rbuff &= ~0x20;
+		}
+ 	}
+	spin_unlock_bh(&ax->buflock);
+
+	count = ax->rcount;
+
+	if ((skb = dev_alloc_skb(count)) == NULL) {
+		printk(KERN_ERR "mkiss: %s: memory squeeze, dropping packet.\n", ax->dev->name);
+		ax->rx_dropped++;
+		return;
+	}
+
+	skb->dev      = ax->dev;
+	spin_lock_bh(&ax->buflock);
+	memcpy(skb_put(skb,count), ax->rbuff, count);
+	spin_unlock_bh(&ax->buflock);
+	skb->mac.raw  = skb->data;
+	skb->protocol = htons(ETH_P_AX25);
+	netif_rx(skb);
+	ax->dev->last_rx = jiffies;
+	ax->rx_packets++;
+	ax->rx_bytes+=count;
+}
+
+/* Encapsulate one AX.25 packet and stuff into a TTY queue. */
+static void ax_encaps(struct ax_disp *ax, unsigned char *icp, int len)
+{
+	unsigned char *p;
+	int actual, count;
+
+	if (ax->mtu != ax->dev->mtu + 73)	/* Someone has been ifconfigging */
+		ax_changedmtu(ax);
+
+	if (len > ax->mtu) {		/* Sigh, shouldn't occur BUT ... */
+		len = ax->mtu;
+		printk(KERN_ERR "mkiss: %s: truncating oversized transmit packet!\n", ax->dev->name);
+		ax->tx_dropped++;
+		ax_unlock(ax);
+		return;
+	}
+
+	p = icp;
+
+	spin_lock_bh(&ax->buflock);
+        switch (ax->crcmode) {
+	         unsigned short crc;
+
+	case CRC_MODE_FLEX:
+	         *p |= 0x20;
+	         crc = calc_crc_flex(p, len);
+		 count = kiss_esc_crc(p, (unsigned char *)ax->xbuff, crc, len+2);
+		 break;
+
+	default:
+	         count = kiss_esc(p, (unsigned char *)ax->xbuff, len);
+		 break;
+	}
+	
+	ax->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+	actual = ax->tty->driver->write(ax->tty, ax->xbuff, count);
+	ax->tx_packets++;
+	ax->tx_bytes+=actual;
+	ax->dev->trans_start = jiffies;
+	ax->xleft = count - actual;
+	ax->xhead = ax->xbuff + actual;
+
+	spin_unlock_bh(&ax->buflock);
+}
+
+/*
+ * Called by the driver when there's room for more data.  If we have
+ * more packets to send, we send them here.
+ */
+static void ax25_write_wakeup(struct tty_struct *tty)
+{
+	int actual;
+	struct ax_disp *ax = (struct ax_disp *) tty->disc_data;
+
+	/* First make sure we're connected. */
+	if (ax == NULL || ax->magic != AX25_MAGIC || !netif_running(ax->dev))
+		return;
+	if (ax->xleft <= 0)  {
+		/* Now serial buffer is almost free & we can start
+		 * transmission of another packet
+		 */
+		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+
+		netif_wake_queue(ax->dev);
+		return;
+	}
+
+	actual = tty->driver->write(tty, ax->xhead, ax->xleft);
+	ax->xleft -= actual;
+	ax->xhead += actual;
+}
+
+/* Encapsulate an AX.25 packet and kick it into a TTY queue. */
+static int ax_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ax_disp *ax = netdev_priv(dev);
+
+	if (!netif_running(dev))  {
+		printk(KERN_ERR "mkiss: %s: xmit call when iface is down\n", dev->name);
+		return 1;
+	}
+
+	if (netif_queue_stopped(dev)) {
+		/*
+		 * May be we must check transmitter timeout here ?
+		 *      14 Oct 1994 Dmitry Gorodchanin.
+		 */
+		if (jiffies - dev->trans_start  < 20 * HZ) {
+			/* 20 sec timeout not reached */
+			return 1;
+		}
+
+		printk(KERN_ERR "mkiss: %s: transmit timed out, %s?\n", dev->name,
+		       (ax->tty->driver->chars_in_buffer(ax->tty) || ax->xleft) ?
+		       "bad line quality" : "driver error");
+
+		ax->xleft = 0;
+		ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+		ax_unlock(ax);
+	}
+
+	/* We were not busy, so we are now... :-) */
+	if (skb != NULL) {
+		ax_lock(ax);
+		ax_encaps(ax, skb->data, skb->len);
+		kfree_skb(skb);
+	}
+
+	return 0;
+}
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+
+/* Return the frame type ID */
+static int ax_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
+	  void *daddr, void *saddr, unsigned len)
+{
+#ifdef CONFIG_INET
+	if (type != htons(ETH_P_AX25))
+		return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
+#endif
+	return 0;
+}
+
+
+static int ax_rebuild_header(struct sk_buff *skb)
+{
+#ifdef CONFIG_INET
+	return ax25_rebuild_header(skb);
+#else
+	return 0;
+#endif
+}
+
+#endif	/* CONFIG_{AX25,AX25_MODULE} */
+
+/* Open the low-level part of the AX25 channel. Easy! */
+static int ax_open(struct net_device *dev)
+{
+	struct ax_disp *ax = netdev_priv(dev);
+	unsigned long len;
+
+	if (ax->tty == NULL)
+		return -ENODEV;
+
+	/*
+	 * Allocate the frame buffers:
+	 *
+	 * rbuff	Receive buffer.
+	 * xbuff	Transmit buffer.
+	 */
+	len = dev->mtu * 2;
+
+	/*
+	 * allow for arrival of larger UDP packets, even if we say not to
+	 * also fixes a bug in which SunOS sends 512-byte packets even with
+	 * an MSS of 128
+	 */
+	if (len < 576 * 2)
+		len = 576 * 2;
+
+	if ((ax->rbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL)
+		goto norbuff;
+
+	if ((ax->xbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL)
+		goto noxbuff;
+
+	ax->mtu	     = dev->mtu + 73;
+	ax->buffsize = len;
+	ax->rcount   = 0;
+	ax->xleft    = 0;
+
+	ax->flags   &= (1 << AXF_INUSE);      /* Clear ESCAPE & ERROR flags */
+
+	spin_lock_init(&ax->buflock);
+
+	netif_start_queue(dev);
+	return 0;
+
+noxbuff:
+	kfree(ax->rbuff);
+
+norbuff:
+	return -ENOMEM;
+}
+
+
+/* Close the low-level part of the AX25 channel. Easy! */
+static int ax_close(struct net_device *dev)
+{
+	struct ax_disp *ax = netdev_priv(dev);
+
+	if (ax->tty == NULL)
+		return -EBUSY;
+
+	ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+
+	netif_stop_queue(dev);
+
+	return 0;
+}
+
+static int ax25_receive_room(struct tty_struct *tty)
+{
+	return 65536;  /* We can handle an infinite amount of data. :-) */
+}
+
+/*
+ * Handle the 'receiver data ready' interrupt.
+ * This function is called by the 'tty_io' module in the kernel when
+ * a block of data has been received, which can now be decapsulated
+ * and sent on to the AX.25 layer for further processing.
+ */
+static void ax25_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
+{
+	struct ax_disp *ax = (struct ax_disp *) tty->disc_data;
+
+	if (ax == NULL || ax->magic != AX25_MAGIC || !netif_running(ax->dev))
+		return;
+
+	/*
+	 * Argh! mtu change time! - costs us the packet part received
+	 * at the change
+	 */
+	if (ax->mtu != ax->dev->mtu + 73)
+		ax_changedmtu(ax);
+
+	/* Read the characters out of the buffer */
+	while (count--) {
+		if (fp != NULL && *fp++) {
+			if (!test_and_set_bit(AXF_ERROR, &ax->flags))
+				ax->rx_errors++;
+			cp++;
+			continue;
+		}
+
+		kiss_unesc(ax, *cp++);
+	}
+}
+
+static int ax25_open(struct tty_struct *tty)
+{
+	struct ax_disp *ax = (struct ax_disp *) tty->disc_data;
+	int err;
+
+	/* First make sure we're not already connected. */
+	if (ax && ax->magic == AX25_MAGIC)
+		return -EEXIST;
+
+	/* OK.  Find a free AX25 channel to use. */
+	if ((ax = ax_alloc()) == NULL)
+		return -ENFILE;
+
+	ax->tty = tty;
+	tty->disc_data = ax;
+
+	if (tty->driver->flush_buffer)
+		tty->driver->flush_buffer(tty);
+
+	/* Restore default settings */
+	ax->dev->type = ARPHRD_AX25;
+
+	/* Perform the low-level AX25 initialization. */
+	if ((err = ax_open(ax->dev)))
+		return err;
+
+	/* Done.  We have linked the TTY line to a channel. */
+	return ax->dev->base_addr;
+}
+
+static void ax25_close(struct tty_struct *tty)
+{
+	struct ax_disp *ax = (struct ax_disp *) tty->disc_data;
+
+	/* First make sure we're connected. */
+	if (ax == NULL || ax->magic != AX25_MAGIC)
+		return;
+
+	unregister_netdev(ax->dev);
+
+	tty->disc_data = NULL;
+	ax->tty        = NULL;
+
+	ax_free(ax);
+}
+
+
+static struct net_device_stats *ax_get_stats(struct net_device *dev)
+{
+	static struct net_device_stats stats;
+	struct ax_disp *ax = netdev_priv(dev);
+
+	memset(&stats, 0, sizeof(struct net_device_stats));
+
+	stats.rx_packets     = ax->rx_packets;
+	stats.tx_packets     = ax->tx_packets;
+	stats.rx_bytes	     = ax->rx_bytes;
+	stats.tx_bytes       = ax->tx_bytes;
+	stats.rx_dropped     = ax->rx_dropped;
+	stats.tx_dropped     = ax->tx_dropped;
+	stats.tx_errors      = ax->tx_errors;
+	stats.rx_errors      = ax->rx_errors;
+	stats.rx_over_errors = ax->rx_over_errors;
+
+	return &stats;
+}
+
+
+/************************************************************************
+ *			   STANDARD ENCAPSULATION	        	 *
+ ************************************************************************/
+
+static int kiss_esc(unsigned char *s, unsigned char *d, int len)
+{
+	unsigned char *ptr = d;
+	unsigned char c;
+
+	/*
+	 * Send an initial END character to flush out any
+	 * data that may have accumulated in the receiver
+	 * due to line noise.
+	 */
+
+	*ptr++ = END;
+
+	while (len-- > 0) {
+		switch (c = *s++) {
+			case END:
+				*ptr++ = ESC;
+				*ptr++ = ESC_END;
+				break;
+			case ESC:
+				*ptr++ = ESC;
+				*ptr++ = ESC_ESC;
+				break;
+			default:
+				*ptr++ = c;
+				break;
+		}
+	}
+
+	*ptr++ = END;
+
+	return ptr - d;
+}
+
+/*
+ * MW:
+ * OK its ugly, but tell me a better solution without copying the
+ * packet to a temporary buffer :-)
+ */
+static int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc, int len)
+{
+	unsigned char *ptr = d;
+	unsigned char c=0;
+
+	*ptr++ = END;
+	while (len > 0) {
+		if (len > 2) 
+			c = *s++;
+		else if (len > 1)
+			c = crc >> 8;
+		else if (len > 0)
+			c = crc & 0xff;
+
+		len--;
+
+		switch (c) {
+                        case END:
+                                *ptr++ = ESC;
+                                *ptr++ = ESC_END;
+                                break;
+                        case ESC:
+                                *ptr++ = ESC;
+                                *ptr++ = ESC_ESC;
+                                break;
+                        default:
+                                *ptr++ = c;
+                                break;
+		}
+	}
+	*ptr++ = END;
+	return ptr - d;		
+}
+
+static void kiss_unesc(struct ax_disp *ax, unsigned char s)
+{
+	switch (s) {
+		case END:
+			/* drop keeptest bit = VSV */
+			if (test_bit(AXF_KEEPTEST, &ax->flags))
+				clear_bit(AXF_KEEPTEST, &ax->flags);
+
+			if (!test_and_clear_bit(AXF_ERROR, &ax->flags) && (ax->rcount > 2))
+				ax_bump(ax);
+
+			clear_bit(AXF_ESCAPE, &ax->flags);
+			ax->rcount = 0;
+			return;
+
+		case ESC:
+			set_bit(AXF_ESCAPE, &ax->flags);
+			return;
+		case ESC_ESC:
+			if (test_and_clear_bit(AXF_ESCAPE, &ax->flags))
+				s = ESC;
+			break;
+		case ESC_END:
+			if (test_and_clear_bit(AXF_ESCAPE, &ax->flags))
+				s = END;
+			break;
+	}
+
+	spin_lock_bh(&ax->buflock);
+	if (!test_bit(AXF_ERROR, &ax->flags)) {
+		if (ax->rcount < ax->buffsize) {
+			ax->rbuff[ax->rcount++] = s;
+			spin_unlock_bh(&ax->buflock);
+			return;
+		}
+
+		ax->rx_over_errors++;
+		set_bit(AXF_ERROR, &ax->flags);
+	}
+	spin_unlock_bh(&ax->buflock);
+}
+
+
+static int ax_set_mac_address(struct net_device *dev, void __user *addr)
+{
+	if (copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN))
+		return -EFAULT;
+	return 0;
+}
+
+static int ax_set_dev_mac_address(struct net_device *dev, void *addr)
+{
+	struct sockaddr *sa = addr;
+
+	memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN);
+
+	return 0;
+}
+
+
+/* Perform I/O control on an active ax25 channel. */
+static int ax25_disp_ioctl(struct tty_struct *tty, void *file, int cmd, void __user *arg)
+{
+	struct ax_disp *ax = (struct ax_disp *) tty->disc_data;
+	unsigned int tmp;
+
+	/* First make sure we're connected. */
+	if (ax == NULL || ax->magic != AX25_MAGIC)
+		return -EINVAL;
+
+	switch (cmd) {
+	 	case SIOCGIFNAME:
+			if (copy_to_user(arg, ax->dev->name, strlen(ax->dev->name) + 1))
+				return -EFAULT;
+			return 0;
+
+		case SIOCGIFENCAP:
+			return put_user(4, (int __user *)arg);
+
+		case SIOCSIFENCAP:
+			if (get_user(tmp, (int __user *)arg))
+				return -EFAULT;
+			ax->mode = tmp;
+			ax->dev->addr_len        = AX25_ADDR_LEN;	  /* sizeof an AX.25 addr */
+			ax->dev->hard_header_len = AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3;
+			ax->dev->type            = ARPHRD_AX25;
+			return 0;
+
+		 case SIOCSIFHWADDR:
+			return ax_set_mac_address(ax->dev, arg);
+
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int ax_open_dev(struct net_device *dev)
+{
+	struct ax_disp *ax = netdev_priv(dev);
+
+	if (ax->tty == NULL)
+		return -ENODEV;
+
+	return 0;
+}
+
+
+/* Initialize the driver.  Called by network startup. */
+static int ax25_init(struct net_device *dev)
+{
+	struct ax_disp *ax = netdev_priv(dev);
+
+	static char ax25_bcast[AX25_ADDR_LEN] =
+		{'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1};
+	static char ax25_test[AX25_ADDR_LEN] =
+		{'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1};
+
+	if (ax == NULL)		/* Allocation failed ?? */
+		return -ENODEV;
+
+	/* Set up the "AX25 Control Block". (And clear statistics) */
+	memset(ax, 0, sizeof (struct ax_disp));
+	ax->magic  = AX25_MAGIC;
+	ax->dev	   = dev;
+
+	/* Finish setting up the DEVICE info. */
+	dev->mtu             = AX_MTU;
+	dev->hard_start_xmit = ax_xmit;
+	dev->open            = ax_open_dev;
+	dev->stop            = ax_close;
+	dev->get_stats	     = ax_get_stats;
+	dev->set_mac_address = ax_set_dev_mac_address;
+	dev->hard_header_len = 0;
+	dev->addr_len        = 0;
+	dev->type            = ARPHRD_AX25;
+	dev->tx_queue_len    = 10;
+	dev->hard_header     = ax_header;
+	dev->rebuild_header  = ax_rebuild_header;
+
+	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+	memcpy(dev->dev_addr,  ax25_test,  AX25_ADDR_LEN);
+
+	/* New-style flags. */
+	dev->flags      = IFF_BROADCAST | IFF_MULTICAST;
+
+	return 0;
+}
+
+
+/* ******************************************************************** */
+/* *			Init MKISS driver			      * */
+/* ******************************************************************** */
+
+static int __init mkiss_init_driver(void)
+{
+	int status;
+
+	printk(banner);
+
+	if (ax25_maxdev < 4)
+	  ax25_maxdev = 4; /* Sanity */
+
+	if ((ax25_ctrls = kmalloc(sizeof(void *) * ax25_maxdev, GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "mkiss: Can't allocate ax25_ctrls[] array!\n");
+		return -ENOMEM;
+	}
+
+	/* Clear the pointer array, we allocate devices when we need them */
+	memset(ax25_ctrls, 0, sizeof(void*) * ax25_maxdev); /* Pointers */
+
+	/* Fill in our line protocol discipline, and register it */
+	ax_ldisc.magic		= TTY_LDISC_MAGIC;
+	ax_ldisc.name		= "mkiss";
+	ax_ldisc.open		= ax25_open;
+	ax_ldisc.close		= ax25_close;
+	ax_ldisc.ioctl		= (int (*)(struct tty_struct *, struct file *,
+					unsigned int, unsigned long))ax25_disp_ioctl;
+	ax_ldisc.receive_buf	= ax25_receive_buf;
+	ax_ldisc.receive_room	= ax25_receive_room;
+	ax_ldisc.write_wakeup	= ax25_write_wakeup;
+
+	if ((status = tty_register_ldisc(N_AX25, &ax_ldisc)) != 0) {
+		printk(KERN_ERR "mkiss: can't register line discipline (err = %d)\n", status);
+		kfree(ax25_ctrls);
+	}
+	return status;
+}
+
+static void __exit mkiss_exit_driver(void)
+{
+	int i;
+
+	for (i = 0; i < ax25_maxdev; i++) {
+		if (ax25_ctrls[i]) {
+			/*
+			* VSV = if dev->start==0, then device
+			* unregistered while close proc.
+			*/
+			if (netif_running(&ax25_ctrls[i]->dev))
+				unregister_netdev(&ax25_ctrls[i]->dev);
+			kfree(ax25_ctrls[i]);
+		}
+	}
+
+	kfree(ax25_ctrls);
+	ax25_ctrls = NULL;
+
+	if ((i = tty_register_ldisc(N_AX25, NULL)))
+		printk(KERN_ERR "mkiss: can't unregister line discipline (err = %d)\n", i);
+}
+
+MODULE_AUTHOR("Hans Albas PE1AYX <hans@esrac.ele.tue.nl>");
+MODULE_DESCRIPTION("KISS driver for AX.25 over TTYs");
+MODULE_PARM(ax25_maxdev, "i");
+MODULE_PARM_DESC(ax25_maxdev, "number of MKISS devices");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_LDISC(N_AX25);
+module_init(mkiss_init_driver);
+module_exit(mkiss_exit_driver);
+
diff --git a/drivers/net/hamradio/mkiss.h b/drivers/net/hamradio/mkiss.h
new file mode 100644
index 0000000..4ab7004
--- /dev/null
+++ b/drivers/net/hamradio/mkiss.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+ *	Defines for the Multi-KISS driver.
+ ****************************************************************************/
+
+#define AX25_MAXDEV	16		/* MAX number of AX25 channels;
+					   This can be overridden with
+					   insmod -oax25_maxdev=nnn	*/
+#define AX_MTU		236	
+
+/* SLIP/KISS protocol characters. */
+#define END             0300		/* indicates end of frame	*/
+#define ESC             0333		/* indicates byte stuffing	*/
+#define ESC_END         0334		/* ESC ESC_END means END 'data'	*/
+#define ESC_ESC         0335		/* ESC ESC_ESC means ESC 'data'	*/
+
+struct ax_disp {
+	int                magic;
+
+	/* Various fields. */
+	struct tty_struct  *tty;		/* ptr to TTY structure		*/
+	struct net_device      *dev;		/* easy for intr handling	*/
+
+	/* These are pointers to the malloc()ed frame buffers. */
+	unsigned char      *rbuff;		/* receiver buffer		*/
+	int                rcount;		/* received chars counter       */
+	unsigned char      *xbuff;		/* transmitter buffer		*/
+	unsigned char      *xhead;		/* pointer to next byte to XMIT */
+	int                xleft;		/* bytes left in XMIT queue     */
+
+	/* SLIP interface statistics. */
+	unsigned long      rx_packets;		/* inbound frames counter	*/
+	unsigned long      tx_packets;		/* outbound frames counter      */
+	unsigned long      rx_bytes;		/* inbound bytes counter        */
+	unsigned long      tx_bytes;		/* outbound bytes counter       */
+	unsigned long      rx_errors;		/* Parity, etc. errors          */
+	unsigned long      tx_errors;		/* Planned stuff                */
+	unsigned long      rx_dropped;		/* No memory for skb            */
+	unsigned long      tx_dropped;		/* When MTU change              */
+	unsigned long      rx_over_errors;	/* Frame bigger then SLIP buf.  */
+
+	/* Detailed SLIP statistics. */
+	int                 mtu;		/* Our mtu (to spot changes!)   */
+	int                 buffsize;		/* Max buffers sizes            */
+
+
+	unsigned long   flags;		/* Flag values/ mode etc	*/
+					/* long req'd: used by set_bit --RR */
+#define AXF_INUSE	0		/* Channel in use               */
+#define AXF_ESCAPE	1               /* ESC received                 */
+#define AXF_ERROR	2               /* Parity, etc. error           */
+#define AXF_KEEPTEST	3		/* Keepalive test flag		*/
+#define AXF_OUTWAIT	4		/* is outpacket was flag	*/
+
+	int                 mode;
+        int                 crcmode;    /* MW: for FlexNet, SMACK etc.  */ 
+#define CRC_MODE_NONE   0
+#define CRC_MODE_FLEX   1
+#define CRC_MODE_SMACK  2
+	spinlock_t          buflock;	/* lock for rbuf and xbuf */
+};
+
+#define AX25_MAGIC		0x5316
diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c
new file mode 100644
index 0000000..ce9e7af
--- /dev/null
+++ b/drivers/net/hamradio/scc.c
@@ -0,0 +1,2191 @@
+#define RCS_ID "$Id: scc.c,v 1.75 1998/11/04 15:15:01 jreuter Exp jreuter $"
+
+#define VERSION "3.0"
+
+/*
+ * Please use z8530drv-utils-3.0 with this version.
+ *            ------------------
+ *
+ * You can find a subset of the documentation in 
+ * Documentation/networking/z8530drv.txt.
+ */
+
+/*
+   ********************************************************************
+   *   SCC.C - Linux driver for Z8530 based HDLC cards for AX.25      *
+   ********************************************************************
+
+
+   ********************************************************************
+
+	Copyright (c) 1993, 2000 Joerg Reuter DL1BKE
+
+	portions (c) 1993 Guido ten Dolle PE1NNZ
+
+   ********************************************************************
+   
+   The driver and the programs in the archive are UNDER CONSTRUCTION.
+   The code is likely to fail, and so your kernel could --- even 
+   a whole network. 
+
+   This driver is intended for Amateur Radio use. If you are running it
+   for commercial purposes, please drop me a note. I am nosy...
+
+   ...BUT:
+ 
+   ! You  m u s t  recognize the appropriate legislations of your country !
+   ! before you connect a radio to the SCC board and start to transmit or !
+   ! receive. The GPL allows you to use the  d r i v e r,  NOT the RADIO! !
+
+   For non-Amateur-Radio use please note that you might need a special
+   allowance/licence from the designer of the SCC Board and/or the
+   MODEM. 
+
+   This program is free software; you can redistribute it and/or modify 
+   it under the terms of the (modified) GNU General Public License 
+   delivered with the Linux kernel source.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should find a copy of the GNU General Public License in 
+   /usr/src/linux/COPYING; 
+   
+   ******************************************************************** 
+
+		
+   Incomplete history of z8530drv:
+   -------------------------------
+
+   1994-09-13	started to write the driver, rescued most of my own
+		code (and Hans Alblas' memory buffer pool concept) from 
+		an earlier project "sccdrv" which was initiated by 
+		Guido ten Dolle. Not much of the old driver survived, 
+		though. The first version I put my hands on was sccdrv1.3
+		from August 1993. The memory buffer pool concept
+		appeared in an unauthorized sccdrv version (1.5) from
+		August 1994.
+
+   1995-01-31	changed copyright notice to GPL without limitations.
+   
+     .
+     .	<SNIP>
+     .
+   		  
+   1996-10-05	New semester, new driver... 
+
+   		  * KISS TNC emulator removed (TTY driver)
+   		  * Source moved to drivers/net/
+   		  * Includes Z8530 defines from drivers/net/z8530.h
+   		  * Uses sk_buffer memory management
+   		  * Reduced overhead of /proc/net/z8530drv output
+   		  * Streamlined quite a lot things
+   		  * Invents brand new bugs... ;-)
+
+   		  The move to version number 3.0 reflects theses changes.
+   		  You can use 'kissbridge' if you need a KISS TNC emulator.
+
+   1996-12-13	Fixed for Linux networking changes. (G4KLX)
+   1997-01-08	Fixed the remaining problems.
+   1997-04-02	Hopefully fixed the problems with the new *_timer()
+   		routines, added calibration code.
+   1997-10-12	Made SCC_DELAY a CONFIG option, added CONFIG_SCC_TRXECHO
+   1998-01-29	Small fix to avoid lock-up on initialization
+   1998-09-29	Fixed the "grouping" bugs, tx_inhibit works again,
+   		using dev->tx_queue_len now instead of MAXQUEUE now.
+   1998-10-21	Postponed the spinlock changes, would need a lot of
+   		testing I currently don't have the time to. Softdcd doesn't
+   		work.
+   1998-11-04	Softdcd does not work correctly in DPLL mode, in fact it 
+   		never did. The DPLL locks on noise, the SYNC unit sees
+   		flags that aren't... Restarting the DPLL does not help
+   		either, it resynchronizes too slow and the first received
+   		frame gets lost.
+   2000-02-13	Fixed for new network driver interface changes, still
+   		does TX timeouts itself since it uses its own queue
+   		scheme.
+
+   Thanks to all who contributed to this driver with ideas and bug
+   reports!
+   
+   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
+   	 "v3.0.dl1bke" with your own. Just to avoid confusion...
+
+   If you want to add your modification to the linux distribution
+   please (!) contact me first.
+   
+   New versions of the driver will be announced on the linux-hams
+   mailing list on vger.kernel.org. To subscribe send an e-mail
+   to majordomo@vger.kernel.org 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 @ DB0ABH.#BAY.DEU.EU
+		Internet: jreuter@yaina.de
+		www     : http://yaina.de/jreuter
+*/
+
+/* ----------------------------------------------------------------------- */
+
+#undef  SCC_LDELAY		/* slow it even a bit more down */
+#undef  SCC_DONT_CHECK		/* don't look if the SCCs you specified are available */
+
+#define SCC_MAXCHIPS	4       /* number of max. supported chips */
+#define SCC_BUFSIZE	384     /* must not exceed 4096 */
+#undef	SCC_DEBUG
+
+#define SCC_DEFAULT_CLOCK	4915200 
+				/* default pclock if nothing is specified */
+
+/* ----------------------------------------------------------------------- */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/timer.h>
+#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/slab.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/socket.h>
+#include <linux/init.h>
+#include <linux/scc.h>
+#include <linux/ctype.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/bitops.h>
+
+#include <net/ax25.h>
+
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include "z8530.h"
+
+static char banner[] __initdata = KERN_INFO "AX.25: Z8530 SCC driver version "VERSION".dl1bke\n";
+
+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 z8530_init(void);
+
+static void init_channel(struct scc_channel *scc);
+static void scc_key_trx (struct scc_channel *scc, char tx);
+static irqreturn_t scc_isr(int irq, void *dev_id, struct pt_regs *regs);
+static void scc_init_timer(struct scc_channel *scc);
+
+static int scc_net_alloc(const char *name, struct scc_channel *scc);
+static void scc_net_setup(struct net_device *dev);
+static int scc_net_open(struct net_device *dev);
+static int scc_net_close(struct net_device *dev);
+static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb);
+static int scc_net_tx(struct sk_buff *skb, struct net_device *dev);
+static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+static int scc_net_set_mac_address(struct net_device *dev, void *addr);
+static struct net_device_stats * scc_net_get_stats(struct net_device *dev);
+
+static unsigned char SCC_DriverName[] = "scc";
+
+static struct irqflags { unsigned char used : 1; } Ivec[NR_IRQS];
+	
+static struct scc_channel SCC_Info[2 * SCC_MAXCHIPS];	/* information per channel */
+
+static struct scc_ctrl {
+	io_port chan_A;
+	io_port chan_B;
+	int irq;
+} SCC_ctrl[SCC_MAXCHIPS+1];
+
+static unsigned char Driver_Initialized;
+static int Nchips;
+static io_port Vector_Latch;
+
+
+/* ******************************************************************** */
+/* *			Port Access Functions			      * */
+/* ******************************************************************** */
+
+/* These provide interrupt save 2-step access to the Z8530 registers */
+
+static DEFINE_SPINLOCK(iolock);	/* Guards paired accesses */
+
+static inline unsigned char InReg(io_port port, unsigned char reg)
+{
+	unsigned long flags;
+	unsigned char r;
+
+	spin_lock_irqsave(&iolock, flags);	
+#ifdef SCC_LDELAY
+	Outb(port, reg);
+	udelay(SCC_LDELAY);
+	r=Inb(port);
+	udelay(SCC_LDELAY);
+#else
+	Outb(port, reg);
+	r=Inb(port);
+#endif
+	spin_unlock_irqrestore(&iolock, flags);
+	return r;
+}
+
+static inline void OutReg(io_port port, unsigned char reg, unsigned char val)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&iolock, flags);
+#ifdef SCC_LDELAY
+	Outb(port, reg); udelay(SCC_LDELAY);
+	Outb(port, val); udelay(SCC_LDELAY);
+#else
+	Outb(port, reg);
+	Outb(port, val);
+#endif
+	spin_unlock_irqrestore(&iolock, flags);
+}
+
+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(struct scc_channel *scc, unsigned char reg, unsigned char val)
+{
+	OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val));
+}
+
+static inline void cl(struct scc_channel *scc, unsigned char reg, unsigned char val)
+{
+	OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val));
+}
+
+/* ******************************************************************** */
+/* *			Some useful macros			      * */
+/* ******************************************************************** */
+
+static inline void scc_discard_buffers(struct scc_channel *scc)
+{
+	unsigned long flags;
+	
+	spin_lock_irqsave(&scc->lock, flags);	
+	if (scc->tx_buff != NULL)
+	{
+		dev_kfree_skb(scc->tx_buff);
+		scc->tx_buff = NULL;
+	}
+	
+	while (skb_queue_len(&scc->tx_queue))
+		dev_kfree_skb(skb_dequeue(&scc->tx_queue));
+
+	spin_unlock_irqrestore(&scc->lock, flags);
+}
+
+
+
+/* ******************************************************************** */
+/* *			Interrupt Service Routines		      * */
+/* ******************************************************************** */
+
+
+/* ----> subroutines for the interrupt handlers <---- */
+
+static inline void scc_notify(struct scc_channel *scc, int event)
+{
+	struct sk_buff *skb;
+	char *bp;
+	
+        if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA)
+		return;
+
+	skb = dev_alloc_skb(2);
+	if (skb != NULL)
+	{
+		bp = skb_put(skb, 2);
+		*bp++ = PARAM_HWEVENT;
+		*bp++ = event;
+		scc_net_rx(scc, skb);
+	} else
+		scc->stat.nospace++;
+}
+
+static inline void flush_rx_FIFO(struct scc_channel *scc)
+{
+	int k;
+	
+	for (k=0; k<3; k++)
+		Inb(scc->data);
+		
+	if(scc->rx_buff != NULL)		/* did we receive something? */
+	{
+		scc->stat.rxerrs++;  /* then count it as an error */
+		dev_kfree_skb_irq(scc->rx_buff);
+		scc->rx_buff = NULL;
+	}
+}
+
+static void start_hunt(struct scc_channel *scc)
+{
+	if ((scc->modem.clocksrc != CLK_EXTERNAL))
+		OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
+	or(scc,R3,ENT_HM|RxENABLE);  /* enable the receiver, hunt mode */
+}
+
+/* ----> four different interrupt handlers for Tx, Rx, changing of	*/
+/*       DCD/CTS and Rx/Tx errors					*/
+
+/* Transmitter interrupt handler */
+static inline void scc_txint(struct scc_channel *scc)
+{
+	struct sk_buff *skb;
+
+	scc->stat.txints++;
+	skb = scc->tx_buff;
+	
+	/* send first octet */
+	
+	if (skb == NULL)
+	{
+		skb = skb_dequeue(&scc->tx_queue);
+		scc->tx_buff = skb;
+		netif_wake_queue(scc->dev);
+
+		if (skb == NULL)
+		{
+			scc_tx_done(scc);
+			Outb(scc->ctrl, RES_Tx_P);
+			return;
+		}
+		
+		if (skb->len == 0)		/* Paranoia... */
+		{
+			dev_kfree_skb_irq(skb);
+			scc->tx_buff = NULL;
+			scc_tx_done(scc);
+			Outb(scc->ctrl, RES_Tx_P);
+			return;
+		}
+
+		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,*skb->data);	/* send byte */
+		skb_pull(skb, 1);
+
+		if (!scc->enhanced)		/* reset EOM latch */
+			Outb(scc->ctrl,RES_EOM_L);
+		return;
+	}
+	
+	/* End Of Frame... */
+	
+	if (skb->len == 0)
+	{
+		Outb(scc->ctrl, RES_Tx_P);	/* reset pending int */
+		cl(scc, R10, ABUNDER);		/* send CRC */
+		dev_kfree_skb_irq(skb);
+		scc->tx_buff = NULL;
+		scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */
+		return;
+	} 
+	
+	/* send octet */
+	
+	Outb(scc->data,*skb->data);		
+	skb_pull(skb, 1);
+}
+
+
+/* External/Status interrupt handler */
+static inline void scc_exint(struct scc_channel *scc)
+{
+	unsigned char status,changes,chg_and_stat;
+
+	scc->stat.exints++;
+
+	status = InReg(scc->ctrl,R0);
+	changes = status ^ scc->status;
+	chg_and_stat = changes & status;
+	
+	/* ABORT: generated whenever DCD drops while receiving */
+
+	if (chg_and_stat & BRK_ABRT)		/* Received an ABORT */
+		flush_rx_FIFO(scc);
+
+	/* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */
+
+	if ((changes & SYNC_HUNT) && scc->kiss.softdcd)
+	{
+		if (status & SYNC_HUNT)
+		{
+			scc->dcd = 0;
+			flush_rx_FIFO(scc);
+			if ((scc->modem.clocksrc != CLK_EXTERNAL))
+				OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
+		} else {
+			scc->dcd = 1;
+		}
+
+		scc_notify(scc, scc->dcd? HWEV_DCD_OFF:HWEV_DCD_ON);
+	}
+
+	/* DCD: on = start to receive packet, off = ABORT condition */
+	/* (a successfully received packet generates a special condition int) */
+	
+	if((changes & DCD) && !scc->kiss.softdcd) /* DCD input changed state */
+	{
+		if(status & DCD)                /* DCD is now ON */
+		{
+			start_hunt(scc);
+			scc->dcd = 1;
+		} else {                        /* DCD is now OFF */
+			cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */
+			flush_rx_FIFO(scc);
+			scc->dcd = 0;
+		}
+		
+		scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF);
+	}
+
+#ifdef notdef
+	/* 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 (scc->kiss.txdelay == 0)	/* zero TXDELAY = wait for CTS */
+			scc_start_tx_timer(scc, t_txdelay, 0);
+	}
+#endif
+	
+	if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM))
+	{
+		scc->stat.tx_under++;	  /* oops, an underrun! count 'em */
+		Outb(scc->ctrl, RES_EXT_INT);	/* reset ext/status interrupts */
+
+		if (scc->tx_buff != NULL)
+		{
+			dev_kfree_skb_irq(scc->tx_buff);
+			scc->tx_buff = NULL;
+		}
+		
+		or(scc,R10,ABUNDER);
+		scc_start_tx_timer(scc, t_txdelay, 0);	/* restart transmission */
+	}
+		
+	scc->status = status;
+	Outb(scc->ctrl,RES_EXT_INT);
+}
+
+
+/* Receiver interrupt handler */
+static inline void scc_rxint(struct scc_channel *scc)
+{
+	struct sk_buff *skb;
+
+	scc->stat.rxints++;
+
+	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 */
+		return;
+	}
+
+	skb = scc->rx_buff;
+	
+	if (skb == NULL)
+	{
+		skb = dev_alloc_skb(scc->stat.bufsize);
+		if (skb == NULL)
+		{
+			scc->dev_stat.rx_dropped++;
+			scc->stat.nospace++;
+			Inb(scc->data);
+			or(scc, R3, ENT_HM);
+			return;
+		}
+		
+		scc->rx_buff = skb;
+		*(skb_put(skb, 1)) = 0;	/* KISS data */
+	}
+	
+	if (skb->len >= scc->stat.bufsize)
+	{
+#ifdef notdef
+		printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n");
+#endif
+		dev_kfree_skb_irq(skb);
+		scc->rx_buff = NULL;
+		Inb(scc->data);
+		or(scc, R3, ENT_HM);
+		return;
+	}
+
+	*(skb_put(skb, 1)) = Inb(scc->data);
+}
+
+
+/* Receive Special Condition interrupt handler */
+static inline void scc_spint(struct scc_channel *scc)
+{
+	unsigned char status;
+	struct sk_buff *skb;
+
+	scc->stat.spints++;
+
+	status = InReg(scc->ctrl,R1);		/* read receiver status */
+	
+	Inb(scc->data);				/* throw away Rx byte */
+	skb = scc->rx_buff;
+
+	if(status & Rx_OVR)			/* receiver overrun */
+	{
+		scc->stat.rx_over++;             /* count them */
+		or(scc,R3,ENT_HM);               /* enter hunt mode for next flag */
+		
+		if (skb != NULL) 
+			dev_kfree_skb_irq(skb);
+		scc->rx_buff = skb = NULL;
+	}
+
+	if(status & END_FR && skb != NULL)	/* end of frame */
+	{
+		/* CRC okay, frame ends on 8 bit boundary and received something ? */
+		
+		if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0)
+		{
+			/* ignore last received byte (first of the CRC bytes) */
+			skb_trim(skb, skb->len-1);
+			scc_net_rx(scc, skb);
+			scc->rx_buff = NULL;
+			scc->stat.rxframes++;
+		} else {				/* a bad frame */
+			dev_kfree_skb_irq(skb);
+			scc->rx_buff = NULL;
+			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)
+{
+	spin_lock(&scc->lock);
+	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;
+	}
+	spin_unlock(&scc->lock);
+}
+
+/* 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 irqreturn_t scc_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned char vector;	
+	struct scc_channel *scc;
+	struct scc_ctrl *ctrl;
+	int k;
+	
+	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->dev) break;
+
+			scc_isr_dispatch(scc, vector);
+
+			OutReg(scc->ctrl,R0,RES_H_IUS);              /* Reset Highest IUS */
+		}  
+
+		if (k == SCC_IRQTIMEOUT)
+			printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n");
+
+		return IRQ_HANDLED;
+	}
+
+	/* 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->dev) break;
+
+			scc_isr_dispatch(scc, vector);
+		}
+
+		if (k == SCC_IRQTIMEOUT)
+		{
+			printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n");
+			break;
+		}
+
+		/* This looks weird 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++;
+	}
+	return IRQ_HANDLED;
+}
+
+
+
+/* ******************************************************************** */
+/* *			Init Channel					*/
+/* ******************************************************************** */
+
+
+/* ----> set SCC channel speed <---- */
+
+static inline void set_brg(struct scc_channel *scc, unsigned int tc)
+{
+	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 */
+}
+
+static inline void set_speed(struct scc_channel *scc)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&scc->lock, flags);
+
+	if (scc->modem.speed > 0)	/* paranoia... */
+		set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2);
+		
+	spin_unlock_irqrestore(&scc->lock, flags);
+}
+
+
+/* ----> initialize a SCC channel <---- */
+
+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 */
+	OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]);	/* DPLL NRZI mode */
+}
+
+/*
+ * Initialization according to the Z8530 manual (SGS-Thomson's version):
+ *
+ * 1. Modes and constants
+ *
+ * WR9	11000000	chip reset
+ * WR4	XXXXXXXX	Tx/Rx control, async or sync mode
+ * WR1	0XX00X00	select W/REQ (optional)
+ * WR2	XXXXXXXX	program interrupt vector
+ * WR3	XXXXXXX0	select Rx control
+ * WR5	XXXX0XXX	select Tx control
+ * WR6	XXXXXXXX	sync character
+ * WR7	XXXXXXXX	sync character
+ * WR9	000X0XXX	select interrupt control
+ * WR10	XXXXXXXX	miscellaneous control (optional)
+ * WR11	XXXXXXXX	clock control
+ * WR12	XXXXXXXX	time constant lower byte (optional)
+ * WR13	XXXXXXXX	time constant upper byte (optional)
+ * WR14	XXXXXXX0	miscellaneous control
+ * WR14	XXXSSSSS	commands (optional)
+ *
+ * 2. Enables
+ *
+ * WR14	000SSSS1	baud rate enable
+ * WR3	SSSSSSS1	Rx enable
+ * WR5	SSSS1SSS	Tx enable
+ * WR0	10000000	reset Tx CRG (optional)
+ * WR1	XSS00S00	DMA enable (optional)
+ *
+ * 3. Interrupt status
+ *
+ * WR15	XXXXXXXX	enable external/status
+ * WR0	00010000	reset external status
+ * WR0	00010000	reset external status twice
+ * WR1	SSSXXSXX	enable Rx, Tx and Ext/status
+ * WR9	000SXSSS	enable master interrupt enable
+ *
+ * 1 = set to one, 0 = reset to zero
+ * X = user defined, S = same as previous init
+ *
+ *
+ * Note that the implementation differs in some points from above scheme.
+ *
+ */
+ 
+static void init_channel(struct scc_channel *scc)
+{
+	del_timer(&scc->tx_t);
+	del_timer(&scc->tx_wdog);
+
+	disable_irq(scc->irq);
+
+	wr(scc,R4,X1CLK|SDLC);		/* *1 clock, SDLC mode */
+	wr(scc,R1,0);			/* no W/REQ operation */
+	wr(scc,R3,Rx8|RxCRC_ENAB);	/* RX 8 bits/char, CRC, disabled */	
+	wr(scc,R5,Tx8|DTR|TxCRC_ENAB);	/* TX 8 bits/char, disabled, DTR */
+	wr(scc,R6,0);			/* SDLC address zero (not used) */
+	wr(scc,R7,FLAG);		/* SDLC flag value */
+	wr(scc,R9,VIS);			/* vector includes status */
+	wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */
+	wr(scc,R14, 0);
+
+
+/* set clock sources:
+
+   CLK_DPLL: normal halfduplex operation
+   
+		RxClk: use DPLL
+		TxClk: use DPLL
+		TRxC mode DPLL output
+		
+   CLK_EXTERNAL: external clocking (G3RUH or DF9IC modem)
+   
+  	        BayCom: 		others:
+  	        
+  	        TxClk = pin RTxC	TxClk = pin TRxC
+  	        RxClk = pin TRxC 	RxClk = pin RTxC
+  	     
+
+   CLK_DIVIDER:
+   		RxClk = use DPLL
+   		TxClk = pin RTxC
+   		
+   		BayCom:			others:
+   		pin TRxC = DPLL		pin TRxC = BRG
+   		(RxClk * 1)		(RxClk * 32)
+*/  
+
+   		
+	switch(scc->modem.clocksrc)
+	{
+		case CLK_DPLL:
+			wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
+			init_brg(scc);
+			break;
+
+		case CLK_DIVIDER:
+			wr(scc, R11, ((scc->brand & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI);
+			init_brg(scc);
+			break;
+
+		case CLK_EXTERNAL:
+			wr(scc, R11, (scc->brand & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP);
+			OutReg(scc->ctrl, R14, DISDPLL);
+			break;
+
+	}
+	
+	set_speed(scc);			/* set baudrate */
+	
+	if(scc->enhanced)
+	{
+		or(scc,R15,SHDLCE|FIFOE);	/* enable FIFO, SDLC/HDLC Enhancements (From now R7 is R7') */
+		wr(scc,R7,AUTOEOM);
+	}
+
+	if(scc->kiss.softdcd || (InReg(scc->ctrl,R0) & DCD))
+						/* DCD is now ON */
+	{
+		start_hunt(scc);
+	}
+	
+	/* enable ABORT, DCD & SYNC/HUNT interrupts */
+
+	wr(scc,R15, BRKIE|TxUIE|(scc->kiss.softdcd? SYNCIE:DCDIE));
+
+	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
+	Outb(scc->ctrl,RES_EXT_INT);	/* must be done twice */
+
+	or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */
+	
+	scc->status = InReg(scc->ctrl,R0);	/* read initial status */
+	
+	or(scc,R9,MIE);			/* master interrupt enable */
+	
+	scc_init_timer(scc);
+			
+	enable_irq(scc->irq);
+}
+
+
+
+
+/* ******************************************************************** */
+/* *			SCC timer functions			      * */
+/* ******************************************************************** */
+
+
+/* ----> scc_key_trx sets the time constant for the baudrate 
+         generator and keys the transmitter		     <---- */
+
+static void scc_key_trx(struct scc_channel *scc, char tx)
+{
+	unsigned int time_const;
+		
+	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)
+	{				/* force simplex operation */
+		if (tx)
+		{
+#ifdef CONFIG_SCC_TRXECHO
+			cl(scc, R3, RxENABLE|ENT_HM);	/* switch off receiver */
+			cl(scc, R15, DCDIE|SYNCIE);	/* No DCD changes, please */
+#endif
+			set_brg(scc, time_const);	/* reprogram baudrate generator */
+
+			/* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */
+			wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR);
+			
+			/* By popular demand: tx_inhibit */
+			if (scc->kiss.tx_inhibit)
+			{
+				or(scc,R5, TxENAB);
+				scc->wreg[R5] |= RTS;
+			} else {
+				or(scc,R5,RTS|TxENAB);	/* set the RTS line and enable TX */
+			}
+		} else {
+			cl(scc,R5,RTS|TxENAB);
+			
+			set_brg(scc, time_const);	/* reprogram baudrate generator */
+			
+			/* DPLL -> Rx clk, DPLL -> Tx CLK, TRxC mode output, TRxC = DPLL */
+			wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
+
+#ifndef CONFIG_SCC_TRXECHO
+			if (scc->kiss.softdcd)
+#endif
+			{
+				or(scc,R15, scc->kiss.softdcd? SYNCIE:DCDIE);
+				start_hunt(scc);
+			}
+		}
+	} else {
+		if (tx)
+		{
+#ifdef CONFIG_SCC_TRXECHO
+			if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+			{
+				cl(scc, R3, RxENABLE);
+				cl(scc, R15, DCDIE|SYNCIE);
+			}
+#endif
+				
+			if (scc->kiss.tx_inhibit)
+			{
+				or(scc,R5, TxENAB);
+				scc->wreg[R5] |= RTS;
+			} else {	
+				or(scc,R5,RTS|TxENAB);	/* enable tx */
+			}
+		} else {
+			cl(scc,R5,RTS|TxENAB);		/* disable tx */
+
+			if ((scc->kiss.fulldup == KISS_DUPLEX_HALF) &&
+#ifndef CONFIG_SCC_TRXECHO
+			    scc->kiss.softdcd)
+#else
+			    1)
+#endif
+			{
+				or(scc, R15, scc->kiss.softdcd? SYNCIE:DCDIE);
+				start_hunt(scc);
+			}
+		}
+	}
+
+	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)
+{
+	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);
+	}
+}
+
+static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when)
+{
+	unsigned long flags;
+	
+	spin_lock_irqsave(&scc->lock, flags);
+	__scc_start_tx_timer(scc, handler, when);
+	spin_unlock_irqrestore(&scc->lock, flags);
+}
+
+static void scc_start_defer(struct scc_channel *scc)
+{
+	unsigned long flags;
+	
+	spin_lock_irqsave(&scc->lock, flags);
+	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);
+	}
+	spin_unlock_irqrestore(&scc->lock, flags);
+}
+
+static void scc_start_maxkeyup(struct scc_channel *scc)
+{
+	unsigned long flags;
+	
+	spin_lock_irqsave(&scc->lock, flags);
+	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);
+	}
+	spin_unlock_irqrestore(&scc->lock, 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);
+	}
+
+	netif_wake_queue(scc->dev);
+}
+
+
+static unsigned char Rand = 17;
+
+static inline int is_grouped(struct scc_channel *scc)
+{
+	int k;
+	struct scc_channel *scc2;
+	unsigned char grp1, grp2;
+
+	grp1 = scc->kiss.group;
+	
+	for (k = 0; k < (Nchips * 2); k++)
+	{
+		scc2 = &SCC_Info[k];
+		grp2 = scc2->kiss.group;
+		
+		if (scc2 == scc || !(scc2->dev && grp2))
+			continue;
+		
+		if ((grp1 & 0x3f) == (grp2 & 0x3f))
+		{
+			if ( (grp1 & TXGROUP) && (scc2->wreg[R5] & RTS) )
+				return 1;
+			
+			if ( (grp1 & RXGROUP) && scc2->dcd )
+				return 1;
+		}
+	}
+	return 0;
+}
+
+/* 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)
+{
+	struct scc_channel *scc = (struct scc_channel *) channel;
+	
+	if (scc->stat.tx_state == TXS_WAIT)	/* maxkeyup or idle timeout */
+	{
+		if (skb_queue_len(&scc->tx_queue) == 0)	/* nothing to send */
+		{
+			scc->stat.tx_state = TXS_IDLE;
+			netif_wake_queue(scc->dev);	/* t_maxkeyup locked it. */
+			return;
+		}
+
+		scc->stat.tx_state = TXS_BUSY;
+	}
+
+	if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+	{
+		Rand = Rand * 17 + 31;
+		
+		if (scc->dcd || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) )
+		{
+			scc_start_defer(scc);
+			scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime);
+			return ;
+		}
+	}
+
+	if ( !(scc->wreg[R5] & RTS) )
+	{
+		scc_key_trx(scc, TX_ON);
+		scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
+	} else {
+		scc_start_tx_timer(scc, t_txdelay, 0);
+	}
+}
+
+
+/* TXDELAY expired
+ *
+ * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog.
+ */
+
+static void t_txdelay(unsigned long channel)
+{
+	struct scc_channel *scc = (struct scc_channel *) channel;
+
+	scc_start_maxkeyup(scc);
+
+	if (scc->tx_buff == NULL)
+	{
+		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 void t_tail(unsigned long channel)
+{
+	struct scc_channel *scc = (struct scc_channel *) channel;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&scc->lock, flags); 
+ 	del_timer(&scc->tx_wdog);	
+ 	scc_key_trx(scc, TX_OFF);
+	spin_unlock_irqrestore(&scc->lock, flags);
+
+ 	if (scc->stat.tx_state == TXS_TIMEOUT)		/* we had a timeout? */
+ 	{
+ 		scc->stat.tx_state = TXS_WAIT;
+		scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
+ 		return;
+ 	}
+ 	
+ 	scc->stat.tx_state = TXS_IDLE;
+	netif_wake_queue(scc->dev);
+}
+
+
+/* BUSY timeout
+ *
+ * throw away send buffers if DCD remains active too long.
+ */
+
+static void t_busy(unsigned long channel)
+{
+	struct scc_channel *scc = (struct scc_channel *) channel;
+
+	del_timer(&scc->tx_t);
+	netif_stop_queue(scc->dev);	/* don't pile on the wabbit! */
+
+	scc_discard_buffers(scc);
+	scc->stat.txerrs++;
+	scc->stat.tx_state = TXS_IDLE;
+
+	netif_wake_queue(scc->dev);	
+}
+
+/* MAXKEYUP timeout
+ *
+ * this is our watchdog.
+ */
+
+static void t_maxkeyup(unsigned long channel)
+{
+	struct scc_channel *scc = (struct scc_channel *) channel;
+	unsigned long flags;
+
+	spin_lock_irqsave(&scc->lock, flags);
+	/* 
+	 * let things settle down before we start to
+	 * accept new data.
+	 */
+
+	netif_stop_queue(scc->dev);
+	scc_discard_buffers(scc);
+
+	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);
+
+	spin_unlock_irqrestore(&scc->lock, 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;
+	
+	del_timer(&scc->tx_wdog);
+
+	scc_key_trx(scc, TX_OFF);
+	if(scc->kiss.mintime)
+		scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
+	scc->stat.tx_state = TXS_WAIT;
+}
+
+static void scc_init_timer(struct scc_channel *scc)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&scc->lock, flags);	
+	scc->stat.tx_state = TXS_IDLE;
+	spin_unlock_irqrestore(&scc->lock, flags);
+}
+
+
+/* ******************************************************************** */
+/* *			Set/get L1 parameters			      * */
+/* ******************************************************************** */
+
+
+/*
+ * this will set the "hardware" parameters through KISS commands or ioctl()
+ */
+
+#define CAST(x) (unsigned long)(x)
+
+static unsigned int scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg)
+{
+	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);
+				cl(scc, R15, DCDIE);
+				start_hunt(scc);
+			} else {
+				or(scc, R15, DCDIE);
+				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:
+			scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF);
+			break;
+
+		default:		return -EINVAL;
+	}
+	
+	return 0;
+}
+
+
+ 
+static unsigned long scc_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;
+	}
+
+}
+
+#undef CAST
+
+/* ******************************************************************* */
+/* *			Send calibration pattern		     * */
+/* ******************************************************************* */
+
+static void scc_stop_calibrate(unsigned long channel)
+{
+	struct scc_channel *scc = (struct scc_channel *) channel;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&scc->lock, flags);
+	del_timer(&scc->tx_wdog);
+	scc_key_trx(scc, TX_OFF);
+	wr(scc, R6, 0);
+	wr(scc, R7, FLAG);
+	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
+	Outb(scc->ctrl,RES_EXT_INT);
+
+	netif_wake_queue(scc->dev);
+	spin_unlock_irqrestore(&scc->lock, flags);
+}
+
+
+static void
+scc_start_calibrate(struct scc_channel *scc, int duration, unsigned char pattern)
+{
+	unsigned long flags;
+	
+	spin_lock_irqsave(&scc->lock, flags);
+	netif_stop_queue(scc->dev);
+	scc_discard_buffers(scc);
+
+	del_timer(&scc->tx_wdog);
+
+	scc->tx_wdog.data = (unsigned long) scc;
+	scc->tx_wdog.function = scc_stop_calibrate;
+	scc->tx_wdog.expires = jiffies + HZ*duration;
+	add_timer(&scc->tx_wdog);
+
+	/* This doesn't seem to work. Why not? */	
+	wr(scc, R6, 0);
+	wr(scc, R7, pattern);
+
+	/* 
+	 * Don't know if this works. 
+	 * Damn, where is my Z8530 programming manual...? 
+	 */
+
+	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
+	Outb(scc->ctrl,RES_EXT_INT);
+
+	scc_key_trx(scc, TX_ON);
+	spin_unlock_irqrestore(&scc->lock, flags);
+}
+
+/* ******************************************************************* */
+/* *		Init channel structures, special HW, etc...	     * */
+/* ******************************************************************* */
+
+/*
+ * Reset the Z8530s and setup special hardware
+ */
+
+static void z8530_init(void)
+{
+	struct scc_channel *scc;
+	int chip, k;
+	unsigned long flags;
+	char *flag;
+
+
+	printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2);
+	
+	flag=" ";
+	for (k = 0; k < NR_IRQS; k++)
+		if (Ivec[k].used) 
+		{
+			printk("%s%d", flag, k);
+			flag=",";
+		}
+	printk("\n");
+
+
+	/* reset and pre-init all chips in the system */
+	for (chip = 0; chip < Nchips; chip++)
+	{
+		scc=&SCC_Info[2*chip];
+		if (!scc->ctrl) continue;
+
+		/* Special SCC cards */
+
+		if(scc->brand & EAGLE)			/* this is an EAGLE card */
+			Outb(scc->special,0x08);	/* enable interrupt on the board */
+			
+		if(scc->brand & (PC100 | PRIMUS))	/* this is a PC100/PRIMUS card */
+			Outb(scc->special,scc->option);	/* set the MODEM mode (0x22) */
+
+			
+		/* Reset and pre-init Z8530 */
+
+		spin_lock_irqsave(&scc->lock, flags);
+				
+		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 */
+		spin_unlock_irqrestore(&scc->lock, flags);		
+        }
+
+ 
+	Driver_Initialized = 1;
+}
+
+/*
+ * Allocate device structure, err, instance, and register driver
+ */
+
+static int scc_net_alloc(const char *name, struct scc_channel *scc)
+{
+	int err;
+	struct net_device *dev;
+
+	dev = alloc_netdev(0, name, scc_net_setup);
+	if (!dev) 
+		return -ENOMEM;
+
+	dev->priv = scc;
+	scc->dev = dev;
+	spin_lock_init(&scc->lock);
+	init_timer(&scc->tx_t);
+	init_timer(&scc->tx_wdog);
+
+	err = register_netdevice(dev);
+	if (err) {
+		printk(KERN_ERR "%s: can't register network device (%d)\n", 
+		       name, err);
+		free_netdev(dev);
+		scc->dev = NULL;
+		return err;
+	}
+
+	return 0;
+}
+
+
+
+/* ******************************************************************** */
+/* *			    Network driver methods		      * */
+/* ******************************************************************** */
+
+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};
+
+/* ----> Initialize device <----- */
+
+static void scc_net_setup(struct net_device *dev)
+{
+	SET_MODULE_OWNER(dev);
+	dev->tx_queue_len    = 16;	/* should be enough... */
+
+	dev->open            = scc_net_open;
+	dev->stop	     = scc_net_close;
+
+	dev->hard_start_xmit = scc_net_tx;
+	dev->hard_header     = ax25_encapsulate;
+	dev->rebuild_header  = ax25_rebuild_header;
+	dev->set_mac_address = scc_net_set_mac_address;
+	dev->get_stats       = scc_net_get_stats;
+	dev->do_ioctl        = scc_net_ioctl;
+	dev->tx_timeout      = NULL;
+
+	memcpy(dev->broadcast, ax25_bcast,  AX25_ADDR_LEN);
+	memcpy(dev->dev_addr,  ax25_nocall, AX25_ADDR_LEN);
+ 
+	dev->flags      = 0;
+
+	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;
+
+}
+
+/* ----> open network device <---- */
+
+static int scc_net_open(struct net_device *dev)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+
+ 	if (!scc->init)
+		return -EINVAL;
+
+	scc->tx_buff = NULL;
+	skb_queue_head_init(&scc->tx_queue);
+ 
+	init_channel(scc);
+
+	netif_start_queue(dev);
+	return 0;
+}
+
+/* ----> close network device <---- */
+
+static int scc_net_close(struct net_device *dev)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+	unsigned long flags;
+
+	netif_stop_queue(dev);
+
+	spin_lock_irqsave(&scc->lock, flags);	
+	Outb(scc->ctrl,0);		/* Make sure pointer is written */
+	wr(scc,R1,0);			/* disable interrupts */
+	wr(scc,R3,0);
+	spin_unlock_irqrestore(&scc->lock, flags);
+
+	del_timer_sync(&scc->tx_t);
+	del_timer_sync(&scc->tx_wdog);
+	
+	scc_discard_buffers(scc);
+
+	return 0;
+}
+
+/* ----> receive frame, called from scc_rxint() <---- */
+
+static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb)
+{
+	if (skb->len == 0) {
+		dev_kfree_skb_irq(skb);
+		return;
+	}
+		
+	scc->dev_stat.rx_packets++;
+	scc->dev_stat.rx_bytes += skb->len;
+
+	skb->dev      = scc->dev;
+	skb->protocol = htons(ETH_P_AX25);
+	skb->mac.raw  = skb->data;
+	skb->pkt_type = PACKET_HOST;
+	
+	netif_rx(skb);
+	scc->dev->last_rx = jiffies;
+	return;
+}
+
+/* ----> transmit frame <---- */
+
+static int scc_net_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+	unsigned long flags;
+	char kisscmd;
+
+	if (skb->len > scc->stat.bufsize || skb->len < 2) {
+		scc->dev_stat.tx_dropped++;	/* bogus frame */
+		dev_kfree_skb(skb);
+		return 0;
+	}
+	
+	scc->dev_stat.tx_packets++;
+	scc->dev_stat.tx_bytes += skb->len;
+	scc->stat.txframes++;
+	
+	kisscmd = *skb->data & 0x1f;
+	skb_pull(skb, 1);
+
+	if (kisscmd) {
+		scc_set_param(scc, kisscmd, *skb->data);
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	spin_lock_irqsave(&scc->lock, flags);
+		
+	if (skb_queue_len(&scc->tx_queue) > scc->dev->tx_queue_len) {
+		struct sk_buff *skb_del;
+		skb_del = skb_dequeue(&scc->tx_queue);
+		dev_kfree_skb(skb_del);
+	}
+	skb_queue_tail(&scc->tx_queue, skb);
+	dev->trans_start = jiffies;
+	
+
+	/*
+	 * Start transmission if the trx state is idle or
+	 * t_idle hasn't expired yet. Use dwait/persistence/slottime
+	 * algorithm for normal halfduplex operation.
+	 */
+
+	if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2) {
+		scc->stat.tx_state = TXS_BUSY;
+		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);
+	}
+	spin_unlock_irqrestore(&scc->lock, flags);
+	return 0;
+}
+
+/* ----> ioctl functions <---- */
+
+/*
+ * SIOCSCCCFG		- configure driver	arg: (struct scc_hw_config *) arg
+ * SIOCSCCINI		- initialize driver	arg: ---
+ * SIOCSCCCHANINI	- initialize channel	arg: (struct scc_modem *) arg
+ * SIOCSCCSMEM		- set memory		arg: (struct scc_mem_config *) arg
+ * SIOCSCCGKISS		- get level 1 parameter	arg: (struct scc_kiss_cmd *) arg
+ * SIOCSCCSKISS		- set level 1 parameter arg: (struct scc_kiss_cmd *) arg
+ * SIOCSCCGSTAT		- get driver status	arg: (struct scc_stat *) arg
+ * SIOCSCCCAL		- send calib. pattern	arg: (struct scc_calibrate *) arg
+ */
+
+static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct scc_kiss_cmd kiss_cmd;
+	struct scc_mem_config memcfg;
+	struct scc_hw_config hwcfg;
+	struct scc_calibrate cal;
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+	int chan;
+	unsigned char device_name[IFNAMSIZ];
+	void __user *arg = ifr->ifr_data;
+	
+	
+	if (!Driver_Initialized)
+	{
+		if (cmd == SIOCSCCCFG)
+		{
+			int found = 1;
+
+			if (!capable(CAP_SYS_RAWIO)) return -EPERM;
+			if (!arg) return -EFAULT;
+
+			if (Nchips >= SCC_MAXCHIPS) 
+				return -EINVAL;
+
+			if (copy_from_user(&hwcfg, arg, sizeof(hwcfg)))
+				return -EFAULT;
+
+			if (hwcfg.irq == 2) hwcfg.irq = 9;
+
+			if (hwcfg.irq < 0 || hwcfg.irq >= NR_IRQS)
+				return -EINVAL;
+				
+			if (!Ivec[hwcfg.irq].used && hwcfg.irq)
+			{
+				if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL))
+					printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq);
+				else
+					Ivec[hwcfg.irq].used = 1;
+			}
+
+			if (hwcfg.vector_latch && !Vector_Latch) {
+				if (!request_region(hwcfg.vector_latch, 1, "scc vector latch"))
+					printk(KERN_WARNING "z8530drv: warning, cannot reserve vector latch port 0x%lx\n, disabled.", hwcfg.vector_latch);
+				else
+					Vector_Latch = hwcfg.vector_latch;
+			}
+
+			if (hwcfg.clock == 0)
+				hwcfg.clock = SCC_DEFAULT_CLOCK;
+
+#ifndef SCC_DONT_CHECK
+
+			if(request_region(hwcfg.ctrl_a, 1, "scc-probe"))
+			{
+				disable_irq(hwcfg.irq);
+				Outb(hwcfg.ctrl_a, 0);
+				OutReg(hwcfg.ctrl_a, R9, FHWRES);
+				udelay(100);
+				OutReg(hwcfg.ctrl_a,R13,0x55);		/* is this chip really there? */
+				udelay(5);
+
+				if (InReg(hwcfg.ctrl_a,R13) != 0x55)
+					found = 0;
+				enable_irq(hwcfg.irq);
+				release_region(hwcfg.ctrl_a, 1);
+			}
+			else
+				found = 0;
+#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[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 SCC_DONT_CHECK
+				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(KERN_INFO "%s: data port = 0x%3.3lx  control port = 0x%3.3lx -- %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");
+					if (Nchips+chan != 0 &&
+					    scc_net_alloc(device_name, 
+							  &SCC_Info[2*Nchips+chan]))
+					    return -EINVAL;
+				}
+			}
+			
+			if (found) Nchips++;
+			
+			return 0;
+		}
+		
+		if (cmd == SIOCSCCINI)
+		{
+			if (!capable(CAP_SYS_RAWIO))
+				return -EPERM;
+				
+			if (Nchips == 0)
+				return -EINVAL;
+
+			z8530_init();
+			return 0;
+		}
+		
+		return -EINVAL;	/* confuse the user */
+	}
+	
+	if (!scc->init)
+	{
+		if (cmd == SIOCSCCCHANINI)
+		{
+			if (!capable(CAP_NET_ADMIN)) return -EPERM;
+			if (!arg) return -EINVAL;
+			
+			scc->stat.bufsize   = SCC_BUFSIZE;
+
+			if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem)))
+				return -EINVAL;
+			
+			/* default KISS Params */
+		
+			if (scc->modem.speed < 4800)
+			{
+				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;		/* 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->tx_buff = NULL;
+			skb_queue_head_init(&scc->tx_queue);
+			scc->init = 1;
+			
+			return 0;
+		}
+		
+		return -EINVAL;
+	}
+	
+	switch(cmd)
+	{
+		case SIOCSCCRESERVED:
+			return -ENOIOCTLCMD;
+
+		case SIOCSCCSMEM:
+			if (!capable(CAP_SYS_RAWIO)) return -EPERM;
+			if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg)))
+				return -EINVAL;
+			scc->stat.bufsize   = memcfg.bufsize;
+			return 0;
+		
+		case SIOCSCCGSTAT:
+			if (!arg || copy_to_user(arg, &scc->stat, sizeof(scc->stat)))
+				return -EINVAL;
+			return 0;
+		
+		case SIOCSCCGKISS:
+			if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
+				return -EINVAL;
+			kiss_cmd.param = scc_get_param(scc, kiss_cmd.command);
+			if (copy_to_user(arg, &kiss_cmd, sizeof(kiss_cmd)))
+				return -EINVAL;
+			return 0;
+		
+		case SIOCSCCSKISS:
+			if (!capable(CAP_NET_ADMIN)) return -EPERM;
+			if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
+				return -EINVAL;
+			return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param);
+		
+		case SIOCSCCCAL:
+			if (!capable(CAP_SYS_RAWIO)) return -EPERM;
+			if (!arg || copy_from_user(&cal, arg, sizeof(cal)) || cal.time == 0)
+				return -EINVAL;
+
+			scc_start_calibrate(scc, cal.time, cal.pattern);
+			return 0;
+
+		default:
+			return -ENOIOCTLCMD;
+		
+	}
+	
+	return -EINVAL;
+}
+
+/* ----> set interface callsign <---- */
+
+static int scc_net_set_mac_address(struct net_device *dev, void *addr)
+{
+	struct sockaddr *sa = (struct sockaddr *) addr;
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+	return 0;
+}
+
+/* ----> get statistics <---- */
+
+static struct net_device_stats *scc_net_get_stats(struct net_device *dev)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+	
+	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;
+}
+
+/* ******************************************************************** */
+/* *		dump statistics to /proc/net/z8530drv		      * */
+/* ******************************************************************** */
+
+#ifdef CONFIG_PROC_FS
+
+static inline struct scc_channel *scc_net_seq_idx(loff_t pos)
+{
+	int k;
+
+	for (k = 0; k < Nchips*2; ++k) {
+		if (!SCC_Info[k].init) 
+			continue;
+		if (pos-- == 0)
+			return &SCC_Info[k];
+	}
+	return NULL;
+}
+
+static void *scc_net_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	return *pos ? scc_net_seq_idx(*pos - 1) : SEQ_START_TOKEN;
+	
+}
+
+static void *scc_net_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	unsigned k;
+	struct scc_channel *scc = v;
+	++*pos;
+	
+	for (k = (v == SEQ_START_TOKEN) ? 0 : (scc - SCC_Info)+1;
+	     k < Nchips*2; ++k) {
+		if (SCC_Info[k].init) 
+			return &SCC_Info[k];
+	}
+	return NULL;
+}
+
+static void scc_net_seq_stop(struct seq_file *seq, void *v)
+{
+}
+
+static int scc_net_seq_show(struct seq_file *seq, void *v)
+{
+	if (v == SEQ_START_TOKEN) {
+		seq_puts(seq, "z8530drv-"VERSION"\n");
+	} else if (!Driver_Initialized) {
+		seq_puts(seq, "not initialized\n");
+	} else if (!Nchips) {
+		seq_puts(seq, "chips missing\n");
+	} else {
+		const struct scc_channel *scc = v;
+		const struct scc_stat *stat = &scc->stat;
+		const struct scc_kiss *kiss = &scc->kiss;
+
+
+		/* dev	data ctrl irq clock brand enh vector special option 
+		 *	baud nrz clocksrc softdcd bufsize
+		 *	rxints txints exints spints
+		 *	rcvd rxerrs over / xmit txerrs under / nospace bufsize
+		 *	txd pers slot tail ful wait min maxk idl defr txof grp
+		 *	W ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
+		 *	R ## ## XX ## ## ## ## ## XX ## ## ## ## ## ## ##
+		 */
+
+		seq_printf(seq, "%s\t%3.3lx %3.3lx %d %lu %2.2x %d %3.3lx %3.3lx %d\n",
+				scc->dev->name,
+				scc->data, scc->ctrl, scc->irq, scc->clock, scc->brand,
+				scc->enhanced, Vector_Latch, scc->special,
+				scc->option);
+		seq_printf(seq, "\t%lu %d %d %d %d\n",
+				scc->modem.speed, scc->modem.nrz,
+				scc->modem.clocksrc, kiss->softdcd,
+				stat->bufsize);
+		seq_printf(seq, "\t%lu %lu %lu %lu\n",
+				stat->rxints, stat->txints, stat->exints, stat->spints);
+		seq_printf(seq, "\t%lu %lu %d / %lu %lu %d / %d %d\n",
+				stat->rxframes, stat->rxerrs, stat->rx_over,
+				stat->txframes, stat->txerrs, stat->tx_under,
+				stat->nospace,  stat->tx_state);
+
+#define K(x) kiss->x
+		seq_printf(seq, "\t%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;
+
+		seq_printf(seq, "\tW ");
+			for (reg = 0; reg < 16; reg++)
+				seq_printf(seq, "%2.2x ", scc->wreg[reg]);
+			seq_printf(seq, "\n");
+			
+		seq_printf(seq, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1));
+			for (reg = 3; reg < 8; reg++)
+				seq_printf(seq, "%2.2x ", InReg(scc->ctrl, reg));
+			seq_printf(seq, "XX ");
+			for (reg = 9; reg < 16; reg++)
+				seq_printf(seq, "%2.2x ", InReg(scc->ctrl, reg));
+			seq_printf(seq, "\n");
+		}
+#endif
+		seq_putc(seq, '\n');
+	}
+
+        return 0;
+}
+
+static struct seq_operations scc_net_seq_ops = {
+	.start  = scc_net_seq_start,
+	.next   = scc_net_seq_next,
+	.stop   = scc_net_seq_stop,
+	.show   = scc_net_seq_show,
+};
+
+
+static int scc_net_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &scc_net_seq_ops);
+}
+
+static struct file_operations scc_net_seq_fops = {
+	.owner	 = THIS_MODULE,
+	.open	 = scc_net_seq_open,
+	.read	 = seq_read,
+	.llseek	 = seq_lseek,
+	.release = seq_release_private,
+};
+
+#endif /* CONFIG_PROC_FS */
+
+ 
+/* ******************************************************************** */
+/* * 			Init SCC driver 			      * */
+/* ******************************************************************** */
+
+static int __init scc_init_driver (void)
+{
+	char devname[IFNAMSIZ];
+	
+	printk(banner);
+	
+	sprintf(devname,"%s0", SCC_DriverName);
+	
+	rtnl_lock();
+	if (scc_net_alloc(devname, SCC_Info)) {
+		rtnl_unlock();
+		printk(KERN_ERR "z8530drv: cannot initialize module\n");
+		return -EIO;
+	}
+	rtnl_unlock();
+
+	proc_net_fops_create("z8530drv", 0, &scc_net_seq_fops);
+
+	return 0;
+}
+
+static void __exit scc_cleanup_driver(void)
+{
+	io_port ctrl;
+	int k;
+	struct scc_channel *scc;
+	struct net_device *dev;
+	
+	if (Nchips == 0 && (dev = SCC_Info[0].dev)) 
+	{
+		unregister_netdev(dev);
+		free_netdev(dev);
+	}
+
+	/* Guard against chip prattle */
+	local_irq_disable();
+	
+	for (k = 0; k < Nchips; k++)
+		if ( (ctrl = SCC_ctrl[k].chan_A) )
+		{
+			Outb(ctrl, 0);
+			OutReg(ctrl,R9,FHWRES);	/* force hardware reset */
+			udelay(50);
+		}
+		
+	/* To unload the port must be closed so no real IRQ pending */
+	for (k=0; k < NR_IRQS ; k++)
+		if (Ivec[k].used) free_irq(k, NULL);
+		
+	local_irq_enable();
+		
+	/* Now clean up */
+	for (k = 0; k < Nchips*2; k++)
+	{
+		scc = &SCC_Info[k];
+		if (scc->ctrl)
+		{
+			release_region(scc->ctrl, 1);
+			release_region(scc->data, 1);
+		}
+		if (scc->dev)
+		{
+			unregister_netdev(scc->dev);
+			free_netdev(scc->dev);
+		}
+	}
+	
+		
+	if (Vector_Latch)
+		release_region(Vector_Latch, 1);
+
+	proc_net_remove("z8530drv");
+}
+
+MODULE_AUTHOR("Joerg Reuter <jreuter@yaina.de>");
+MODULE_DESCRIPTION("AX.25 Device Driver for Z8530 based HDLC cards");
+MODULE_SUPPORTED_DEVICE("Z8530 based SCC cards for Amateur Radio");
+MODULE_LICENSE("GPL");
+module_init(scc_init_driver);
+module_exit(scc_cleanup_driver);
diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c
new file mode 100644
index 0000000..fd7b00f
--- /dev/null
+++ b/drivers/net/hamradio/yam.c
@@ -0,0 +1,1218 @@
+/*****************************************************************************/
+
+/*
+ *    yam.c  -- YAM radio modem driver.
+ *
+ *      Copyright (C) 1998 Frederic Rible F1OAT (frible@teaser.fr)
+ *      Adapted from baycom.c driver written by Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  History:
+ *   0.0 F1OAT 06.06.98  Begin of work with baycom.c source code V 0.3
+ *   0.1 F1OAT 07.06.98  Add timer polling routine for channel arbitration
+ *   0.2 F6FBB 08.06.98  Added delay after FPGA programming
+ *   0.3 F6FBB 29.07.98  Delayed PTT implementation for dupmode=2
+ *   0.4 F6FBB 30.07.98  Added TxTail, Slottime and Persistance
+ *   0.5 F6FBB 01.08.98  Shared IRQs, /proc/net and network statistics
+ *   0.6 F6FBB 25.08.98  Added 1200Bds format
+ *   0.7 F6FBB 12.09.98  Added to the kernel configuration
+ *   0.8 F6FBB 14.10.98  Fixed slottime/persistence timing bug
+ *       OK1ZIA 2.09.01  Fixed "kfree_skb on hard IRQ" 
+ *                       using dev_kfree_skb_any(). (important in 2.4 kernel)
+ *   
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+/* prototypes for ax25_encapsulate and ax25_rebuild_header */
+#include <net/ax25.h>
+#endif	/* CONFIG_AX25 || CONFIG_AX25_MODULE */
+
+/* make genksyms happy */
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include <asm/uaccess.h>
+#include <linux/init.h>
+
+#include <linux/yam.h>
+#include "yam9600.h"
+#include "yam1200.h"
+
+/* --------------------------------------------------------------------- */
+
+static const char yam_drvname[] = "yam";
+static char yam_drvinfo[] __initdata = KERN_INFO "YAM driver version 0.8 by F1OAT/F6FBB\n";
+
+/* --------------------------------------------------------------------- */
+
+#define YAM_9600	1
+#define YAM_1200	2
+
+#define NR_PORTS	4
+#define YAM_MAGIC	0xF10A7654
+
+/* Transmitter states */
+
+#define TX_OFF		0
+#define TX_HEAD		1
+#define TX_DATA		2
+#define TX_CRC1		3
+#define TX_CRC2		4
+#define TX_TAIL		5
+
+#define YAM_MAX_FRAME	1024
+
+#define DEFAULT_BITRATE	9600			/* bps */
+#define DEFAULT_HOLDD	10			/* sec */
+#define DEFAULT_TXD	300			/* ms */
+#define DEFAULT_TXTAIL	10			/* ms */
+#define DEFAULT_SLOT	100			/* ms */
+#define DEFAULT_PERS	64			/* 0->255 */
+
+struct yam_port {
+	int magic;
+	int bitrate;
+	int baudrate;
+	int iobase;
+	int irq;
+	int dupmode;
+
+	struct net_device *dev;
+
+	/* Stats section */
+
+	struct net_device_stats stats;
+
+	int nb_rxint;
+	int nb_mdint;
+
+	/* Parameters section */
+
+	int txd;				/* tx delay */
+	int holdd;				/* duplex ptt delay */
+	int txtail;				/* txtail delay */
+	int slot;				/* slottime */
+	int pers;				/* persistence */
+
+	/* Tx section */
+
+	int tx_state;
+	int tx_count;
+	int slotcnt;
+	unsigned char tx_buf[YAM_MAX_FRAME];
+	int tx_len;
+	int tx_crcl, tx_crch;
+	struct sk_buff_head send_queue;		/* Packets awaiting transmission */
+
+	/* Rx section */
+
+	int dcd;
+	unsigned char rx_buf[YAM_MAX_FRAME];
+	int rx_len;
+	int rx_crcl, rx_crch;
+};
+
+struct yam_mcs {
+	unsigned char bits[YAM_FPGA_SIZE];
+	int bitrate;
+	struct yam_mcs *next;
+};
+
+static struct net_device *yam_devs[NR_PORTS];
+
+static struct yam_mcs *yam_data;
+
+static char ax25_bcast[7] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static char ax25_test[7] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+static struct timer_list yam_timer = TIMER_INITIALIZER(NULL, 0, 0);
+
+/* --------------------------------------------------------------------- */
+
+#define RBR(iobase)	(iobase+0)
+#define THR(iobase)	(iobase+0)
+#define IER(iobase)	(iobase+1)
+#define IIR(iobase)	(iobase+2)
+#define FCR(iobase)	(iobase+2)
+#define LCR(iobase)	(iobase+3)
+#define MCR(iobase)	(iobase+4)
+#define LSR(iobase)	(iobase+5)
+#define MSR(iobase)	(iobase+6)
+#define SCR(iobase)	(iobase+7)
+#define DLL(iobase)	(iobase+0)
+#define DLM(iobase)	(iobase+1)
+
+#define YAM_EXTENT	8
+
+/* Interrupt Identification Register Bit Masks */
+#define IIR_NOPEND	1
+#define IIR_MSR		0
+#define IIR_TX		2
+#define IIR_RX		4
+#define IIR_LSR		6
+#define IIR_TIMEOUT	12			/* Fifo mode only */
+
+#define IIR_MASK	0x0F
+
+/* Interrupt Enable Register Bit Masks */
+#define IER_RX		1			/* enable rx interrupt */
+#define IER_TX		2			/* enable tx interrupt */
+#define IER_LSR		4			/* enable line status interrupts */
+#define IER_MSR		8			/* enable modem status interrupts */
+
+/* Modem Control Register Bit Masks */
+#define MCR_DTR		0x01			/* DTR output */
+#define MCR_RTS		0x02			/* RTS output */
+#define MCR_OUT1	0x04			/* OUT1 output (not accessible in RS232) */
+#define MCR_OUT2	0x08			/* Master Interrupt enable (must be set on PCs) */
+#define MCR_LOOP	0x10			/* Loopback enable */
+
+/* Modem Status Register Bit Masks */
+#define MSR_DCTS	0x01			/* Delta CTS input */
+#define MSR_DDSR	0x02			/* Delta DSR */
+#define MSR_DRIN	0x04			/* Delta RI */
+#define MSR_DDCD	0x08			/* Delta DCD */
+#define MSR_CTS		0x10			/* CTS input */
+#define MSR_DSR		0x20			/* DSR input */
+#define MSR_RING	0x40			/* RI  input */
+#define MSR_DCD		0x80			/* DCD input */
+
+/* line status register bit mask */
+#define LSR_RXC		0x01
+#define LSR_OE		0x02
+#define LSR_PE		0x04
+#define LSR_FE		0x08
+#define LSR_BREAK	0x10
+#define LSR_THRE	0x20
+#define LSR_TSRE	0x40
+
+/* Line Control Register Bit Masks */
+#define LCR_DLAB	0x80
+#define LCR_BREAK	0x40
+#define LCR_PZERO	0x28
+#define LCR_PEVEN	0x18
+#define LCR_PODD	0x08
+#define LCR_STOP1	0x00
+#define LCR_STOP2	0x04
+#define LCR_BIT5	0x00
+#define LCR_BIT6	0x02
+#define LCR_BIT7	0x01
+#define LCR_BIT8	0x03
+
+/* YAM Modem <-> UART Port mapping */
+
+#define TX_RDY		MSR_DCTS		/* transmitter ready to send */
+#define RX_DCD		MSR_DCD			/* carrier detect */
+#define RX_FLAG		MSR_RING		/* hdlc flag received */
+#define FPGA_DONE	MSR_DSR			/* FPGA is configured */
+#define PTT_ON		(MCR_RTS|MCR_OUT2)	/* activate PTT */
+#define PTT_OFF		(MCR_DTR|MCR_OUT2)	/* release PTT */
+
+#define ENABLE_RXINT	IER_RX			/* enable uart rx interrupt during rx */
+#define ENABLE_TXINT	IER_MSR			/* enable uart ms interrupt during tx */
+#define ENABLE_RTXINT	(IER_RX|IER_MSR)	/* full duplex operations */
+
+
+/*************************************************************************
+* CRC Tables
+************************************************************************/
+
+static const unsigned char chktabl[256] =
+{0x00, 0x89, 0x12, 0x9b, 0x24, 0xad, 0x36, 0xbf, 0x48, 0xc1, 0x5a, 0xd3, 0x6c, 0xe5, 0x7e,
+ 0xf7, 0x81, 0x08, 0x93, 0x1a, 0xa5, 0x2c, 0xb7, 0x3e, 0xc9, 0x40, 0xdb, 0x52, 0xed, 0x64,
+ 0xff, 0x76, 0x02, 0x8b, 0x10, 0x99, 0x26, 0xaf, 0x34, 0xbd, 0x4a, 0xc3, 0x58, 0xd1, 0x6e,
+ 0xe7, 0x7c, 0xf5, 0x83, 0x0a, 0x91, 0x18, 0xa7, 0x2e, 0xb5, 0x3c, 0xcb, 0x42, 0xd9, 0x50,
+ 0xef, 0x66, 0xfd, 0x74, 0x04, 0x8d, 0x16, 0x9f, 0x20, 0xa9, 0x32, 0xbb, 0x4c, 0xc5, 0x5e,
+ 0xd7, 0x68, 0xe1, 0x7a, 0xf3, 0x85, 0x0c, 0x97, 0x1e, 0xa1, 0x28, 0xb3, 0x3a, 0xcd, 0x44,
+ 0xdf, 0x56, 0xe9, 0x60, 0xfb, 0x72, 0x06, 0x8f, 0x14, 0x9d, 0x22, 0xab, 0x30, 0xb9, 0x4e,
+ 0xc7, 0x5c, 0xd5, 0x6a, 0xe3, 0x78, 0xf1, 0x87, 0x0e, 0x95, 0x1c, 0xa3, 0x2a, 0xb1, 0x38,
+ 0xcf, 0x46, 0xdd, 0x54, 0xeb, 0x62, 0xf9, 0x70, 0x08, 0x81, 0x1a, 0x93, 0x2c, 0xa5, 0x3e,
+ 0xb7, 0x40, 0xc9, 0x52, 0xdb, 0x64, 0xed, 0x76, 0xff, 0x89, 0x00, 0x9b, 0x12, 0xad, 0x24,
+ 0xbf, 0x36, 0xc1, 0x48, 0xd3, 0x5a, 0xe5, 0x6c, 0xf7, 0x7e, 0x0a, 0x83, 0x18, 0x91, 0x2e,
+ 0xa7, 0x3c, 0xb5, 0x42, 0xcb, 0x50, 0xd9, 0x66, 0xef, 0x74, 0xfd, 0x8b, 0x02, 0x99, 0x10,
+ 0xaf, 0x26, 0xbd, 0x34, 0xc3, 0x4a, 0xd1, 0x58, 0xe7, 0x6e, 0xf5, 0x7c, 0x0c, 0x85, 0x1e,
+ 0x97, 0x28, 0xa1, 0x3a, 0xb3, 0x44, 0xcd, 0x56, 0xdf, 0x60, 0xe9, 0x72, 0xfb, 0x8d, 0x04,
+ 0x9f, 0x16, 0xa9, 0x20, 0xbb, 0x32, 0xc5, 0x4c, 0xd7, 0x5e, 0xe1, 0x68, 0xf3, 0x7a, 0x0e,
+ 0x87, 0x1c, 0x95, 0x2a, 0xa3, 0x38, 0xb1, 0x46, 0xcf, 0x54, 0xdd, 0x62, 0xeb, 0x70, 0xf9,
+ 0x8f, 0x06, 0x9d, 0x14, 0xab, 0x22, 0xb9, 0x30, 0xc7, 0x4e, 0xd5, 0x5c, 0xe3, 0x6a, 0xf1,
+ 0x78};
+static const unsigned char chktabh[256] =
+{0x00, 0x11, 0x23, 0x32, 0x46, 0x57, 0x65, 0x74, 0x8c, 0x9d, 0xaf, 0xbe, 0xca, 0xdb, 0xe9,
+ 0xf8, 0x10, 0x01, 0x33, 0x22, 0x56, 0x47, 0x75, 0x64, 0x9c, 0x8d, 0xbf, 0xae, 0xda, 0xcb,
+ 0xf9, 0xe8, 0x21, 0x30, 0x02, 0x13, 0x67, 0x76, 0x44, 0x55, 0xad, 0xbc, 0x8e, 0x9f, 0xeb,
+ 0xfa, 0xc8, 0xd9, 0x31, 0x20, 0x12, 0x03, 0x77, 0x66, 0x54, 0x45, 0xbd, 0xac, 0x9e, 0x8f,
+ 0xfb, 0xea, 0xd8, 0xc9, 0x42, 0x53, 0x61, 0x70, 0x04, 0x15, 0x27, 0x36, 0xce, 0xdf, 0xed,
+ 0xfc, 0x88, 0x99, 0xab, 0xba, 0x52, 0x43, 0x71, 0x60, 0x14, 0x05, 0x37, 0x26, 0xde, 0xcf,
+ 0xfd, 0xec, 0x98, 0x89, 0xbb, 0xaa, 0x63, 0x72, 0x40, 0x51, 0x25, 0x34, 0x06, 0x17, 0xef,
+ 0xfe, 0xcc, 0xdd, 0xa9, 0xb8, 0x8a, 0x9b, 0x73, 0x62, 0x50, 0x41, 0x35, 0x24, 0x16, 0x07,
+ 0xff, 0xee, 0xdc, 0xcd, 0xb9, 0xa8, 0x9a, 0x8b, 0x84, 0x95, 0xa7, 0xb6, 0xc2, 0xd3, 0xe1,
+ 0xf0, 0x08, 0x19, 0x2b, 0x3a, 0x4e, 0x5f, 0x6d, 0x7c, 0x94, 0x85, 0xb7, 0xa6, 0xd2, 0xc3,
+ 0xf1, 0xe0, 0x18, 0x09, 0x3b, 0x2a, 0x5e, 0x4f, 0x7d, 0x6c, 0xa5, 0xb4, 0x86, 0x97, 0xe3,
+ 0xf2, 0xc0, 0xd1, 0x29, 0x38, 0x0a, 0x1b, 0x6f, 0x7e, 0x4c, 0x5d, 0xb5, 0xa4, 0x96, 0x87,
+ 0xf3, 0xe2, 0xd0, 0xc1, 0x39, 0x28, 0x1a, 0x0b, 0x7f, 0x6e, 0x5c, 0x4d, 0xc6, 0xd7, 0xe5,
+ 0xf4, 0x80, 0x91, 0xa3, 0xb2, 0x4a, 0x5b, 0x69, 0x78, 0x0c, 0x1d, 0x2f, 0x3e, 0xd6, 0xc7,
+ 0xf5, 0xe4, 0x90, 0x81, 0xb3, 0xa2, 0x5a, 0x4b, 0x79, 0x68, 0x1c, 0x0d, 0x3f, 0x2e, 0xe7,
+ 0xf6, 0xc4, 0xd5, 0xa1, 0xb0, 0x82, 0x93, 0x6b, 0x7a, 0x48, 0x59, 0x2d, 0x3c, 0x0e, 0x1f,
+ 0xf7, 0xe6, 0xd4, 0xc5, 0xb1, 0xa0, 0x92, 0x83, 0x7b, 0x6a, 0x58, 0x49, 0x3d, 0x2c, 0x1e,
+ 0x0f};
+
+/*************************************************************************
+* FPGA functions
+************************************************************************/
+
+static void delay(int ms)
+{
+	unsigned long timeout = jiffies + ((ms * HZ) / 1000);
+	while (time_before(jiffies, timeout))
+		cpu_relax();
+}
+
+/*
+ * reset FPGA
+ */
+
+static void fpga_reset(int iobase)
+{
+	outb(0, IER(iobase));
+	outb(LCR_DLAB | LCR_BIT5, LCR(iobase));
+	outb(1, DLL(iobase));
+	outb(0, DLM(iobase));
+
+	outb(LCR_BIT5, LCR(iobase));
+	inb(LSR(iobase));
+	inb(MSR(iobase));
+	/* turn off FPGA supply voltage */
+	outb(MCR_OUT1 | MCR_OUT2, MCR(iobase));
+	delay(100);
+	/* turn on FPGA supply voltage again */
+	outb(MCR_DTR | MCR_RTS | MCR_OUT1 | MCR_OUT2, MCR(iobase));
+	delay(100);
+}
+
+/*
+ * send one byte to FPGA
+ */
+
+static int fpga_write(int iobase, unsigned char wrd)
+{
+	unsigned char bit;
+	int k;
+	unsigned long timeout = jiffies + HZ / 10;
+
+	for (k = 0; k < 8; k++) {
+		bit = (wrd & 0x80) ? (MCR_RTS | MCR_DTR) : MCR_DTR;
+		outb(bit | MCR_OUT1 | MCR_OUT2, MCR(iobase));
+		wrd <<= 1;
+		outb(0xfc, THR(iobase));
+		while ((inb(LSR(iobase)) & LSR_TSRE) == 0)
+			if (time_after(jiffies, timeout))
+				return -1;
+	}
+
+	return 0;
+}
+
+static unsigned char *add_mcs(unsigned char *bits, int bitrate)
+{
+	struct yam_mcs *p;
+
+	/* If it already exists, replace the bit data */
+	p = yam_data;
+	while (p) {
+		if (p->bitrate == bitrate) {
+			memcpy(p->bits, bits, YAM_FPGA_SIZE);
+			return p->bits;
+		}
+		p = p->next;
+	}
+
+	/* Allocate a new mcs */
+	if ((p = kmalloc(sizeof(struct yam_mcs), GFP_KERNEL)) == NULL) {
+		printk(KERN_WARNING "YAM: no memory to allocate mcs\n");
+		return NULL;
+	}
+	memcpy(p->bits, bits, YAM_FPGA_SIZE);
+	p->bitrate = bitrate;
+	p->next = yam_data;
+	yam_data = p;
+
+	return p->bits;
+}
+
+static unsigned char *get_mcs(int bitrate)
+{
+	struct yam_mcs *p;
+
+	p = yam_data;
+	while (p) {
+		if (p->bitrate == bitrate)
+			return p->bits;
+		p = p->next;
+	}
+
+	/* Load predefined mcs data */
+	switch (bitrate) {
+	case 1200:
+		return add_mcs(bits_1200, bitrate);
+	default:
+		return add_mcs(bits_9600, bitrate);
+	}
+}
+
+/*
+ * download bitstream to FPGA
+ * data is contained in bits[] array in yam1200.h resp. yam9600.h
+ */
+
+static int fpga_download(int iobase, int bitrate)
+{
+	int i, rc;
+	unsigned char *pbits;
+
+	pbits = get_mcs(bitrate);
+	if (pbits == NULL)
+		return -1;
+
+	fpga_reset(iobase);
+	for (i = 0; i < YAM_FPGA_SIZE; i++) {
+		if (fpga_write(iobase, pbits[i])) {
+			printk(KERN_ERR "yam: error in write cycle\n");
+			return -1;			/* write... */
+		}
+	}
+
+	fpga_write(iobase, 0xFF);
+	rc = inb(MSR(iobase));		/* check DONE signal */
+
+	/* Needed for some hardwares */
+	delay(50);
+
+	return (rc & MSR_DSR) ? 0 : -1;
+}
+
+
+/************************************************************************
+* Serial port init 
+************************************************************************/
+
+static void yam_set_uart(struct net_device *dev)
+{
+	struct yam_port *yp = netdev_priv(dev);
+	int divisor = 115200 / yp->baudrate;
+
+	outb(0, IER(dev->base_addr));
+	outb(LCR_DLAB | LCR_BIT8, LCR(dev->base_addr));
+	outb(divisor, DLL(dev->base_addr));
+	outb(0, DLM(dev->base_addr));
+	outb(LCR_BIT8, LCR(dev->base_addr));
+	outb(PTT_OFF, MCR(dev->base_addr));
+	outb(0x00, FCR(dev->base_addr));
+
+	/* Flush pending irq */
+
+	inb(RBR(dev->base_addr));
+	inb(MSR(dev->base_addr));
+
+	/* Enable rx irq */
+
+	outb(ENABLE_RTXINT, IER(dev->base_addr));
+}
+
+
+/* --------------------------------------------------------------------- */
+
+enum uart {
+	c_uart_unknown, c_uart_8250,
+	c_uart_16450, c_uart_16550, c_uart_16550A
+};
+
+static const char *uart_str[] =
+{"unknown", "8250", "16450", "16550", "16550A"};
+
+static enum uart yam_check_uart(unsigned int iobase)
+{
+	unsigned char b1, b2, b3;
+	enum uart u;
+	enum uart uart_tab[] =
+	{c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A};
+
+	b1 = inb(MCR(iobase));
+	outb(b1 | 0x10, MCR(iobase));	/* loopback mode */
+	b2 = inb(MSR(iobase));
+	outb(0x1a, MCR(iobase));
+	b3 = inb(MSR(iobase)) & 0xf0;
+	outb(b1, MCR(iobase));		/* restore old values */
+	outb(b2, MSR(iobase));
+	if (b3 != 0x90)
+		return c_uart_unknown;
+	inb(RBR(iobase));
+	inb(RBR(iobase));
+	outb(0x01, FCR(iobase));	/* enable FIFOs */
+	u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
+	if (u == c_uart_16450) {
+		outb(0x5a, SCR(iobase));
+		b1 = inb(SCR(iobase));
+		outb(0xa5, SCR(iobase));
+		b2 = inb(SCR(iobase));
+		if ((b1 != 0x5a) || (b2 != 0xa5))
+			u = c_uart_8250;
+	}
+	return u;
+}
+
+/******************************************************************************
+* Rx Section
+******************************************************************************/
+static inline void yam_rx_flag(struct net_device *dev, struct yam_port *yp)
+{
+	if (yp->dcd && yp->rx_len >= 3 && yp->rx_len < YAM_MAX_FRAME) {
+		int pkt_len = yp->rx_len - 2 + 1;	/* -CRC + kiss */
+		struct sk_buff *skb;
+
+		if ((yp->rx_crch & yp->rx_crcl) != 0xFF) {
+			/* Bad crc */
+		} else {
+			if (!(skb = dev_alloc_skb(pkt_len))) {
+				printk(KERN_WARNING "%s: memory squeeze, dropping packet\n", dev->name);
+				++yp->stats.rx_dropped;
+			} else {
+				unsigned char *cp;
+				skb->dev = dev;
+				cp = skb_put(skb, pkt_len);
+				*cp++ = 0;		/* KISS kludge */
+				memcpy(cp, yp->rx_buf, pkt_len - 1);
+				skb->protocol = htons(ETH_P_AX25);
+				skb->mac.raw = skb->data;
+				netif_rx(skb);
+				dev->last_rx = jiffies;
+				++yp->stats.rx_packets;
+			}
+		}
+	}
+	yp->rx_len = 0;
+	yp->rx_crcl = 0x21;
+	yp->rx_crch = 0xf3;
+}
+
+static inline void yam_rx_byte(struct net_device *dev, struct yam_port *yp, unsigned char rxb)
+{
+	if (yp->rx_len < YAM_MAX_FRAME) {
+		unsigned char c = yp->rx_crcl;
+		yp->rx_crcl = (chktabl[c] ^ yp->rx_crch);
+		yp->rx_crch = (chktabh[c] ^ rxb);
+		yp->rx_buf[yp->rx_len++] = rxb;
+	}
+}
+
+/********************************************************************************
+* TX Section
+********************************************************************************/
+
+static void ptt_on(struct net_device *dev)
+{
+	outb(PTT_ON, MCR(dev->base_addr));
+}
+
+static void ptt_off(struct net_device *dev)
+{
+	outb(PTT_OFF, MCR(dev->base_addr));
+}
+
+static int yam_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+	struct yam_port *yp = netdev_priv(dev);
+
+	skb_queue_tail(&yp->send_queue, skb);
+	dev->trans_start = jiffies;
+	return 0;
+}
+
+static void yam_start_tx(struct net_device *dev, struct yam_port *yp)
+{
+	if ((yp->tx_state == TX_TAIL) || (yp->txd == 0))
+		yp->tx_count = 1;
+	else
+		yp->tx_count = (yp->bitrate * yp->txd) / 8000;
+	yp->tx_state = TX_HEAD;
+	ptt_on(dev);
+}
+
+static unsigned short random_seed;
+
+static inline unsigned short random_num(void)
+{
+	random_seed = 28629 * random_seed + 157;
+	return random_seed;
+}
+
+static void yam_arbitrate(struct net_device *dev)
+{
+	struct yam_port *yp = netdev_priv(dev);
+
+	if (yp->magic != YAM_MAGIC || yp->tx_state != TX_OFF ||
+	    skb_queue_empty(&yp->send_queue))
+		return;
+	/* tx_state is TX_OFF and there is data to send */
+
+	if (yp->dupmode) {
+		/* Full duplex mode, don't wait */
+		yam_start_tx(dev, yp);
+		return;
+	}
+	if (yp->dcd) {
+		/* DCD on, wait slotime ... */
+		yp->slotcnt = yp->slot / 10;
+		return;
+	}
+	/* Is slottime passed ? */
+	if ((--yp->slotcnt) > 0)
+		return;
+
+	yp->slotcnt = yp->slot / 10;
+
+	/* is random > persist ? */
+	if ((random_num() % 256) > yp->pers)
+		return;
+
+	yam_start_tx(dev, yp);
+}
+
+static void yam_dotimer(unsigned long dummy)
+{
+	int i;
+
+	for (i = 0; i < NR_PORTS; i++) {
+		struct net_device *dev = yam_devs[i];
+		if (dev && netif_running(dev))
+			yam_arbitrate(dev);
+	}
+	yam_timer.expires = jiffies + HZ / 100;
+	add_timer(&yam_timer);
+}
+
+static void yam_tx_byte(struct net_device *dev, struct yam_port *yp)
+{
+	struct sk_buff *skb;
+	unsigned char b, temp;
+
+	switch (yp->tx_state) {
+	case TX_OFF:
+		break;
+	case TX_HEAD:
+		if (--yp->tx_count <= 0) {
+			if (!(skb = skb_dequeue(&yp->send_queue))) {
+				ptt_off(dev);
+				yp->tx_state = TX_OFF;
+				break;
+			}
+			yp->tx_state = TX_DATA;
+			if (skb->data[0] != 0) {
+/*                              do_kiss_params(s, skb->data, skb->len); */
+				dev_kfree_skb_any(skb);
+				break;
+			}
+			yp->tx_len = skb->len - 1;	/* strip KISS byte */
+			if (yp->tx_len >= YAM_MAX_FRAME || yp->tx_len < 2) {
+        			dev_kfree_skb_any(skb);
+				break;
+			}
+			memcpy(yp->tx_buf, skb->data + 1, yp->tx_len);
+			dev_kfree_skb_any(skb);
+			yp->tx_count = 0;
+			yp->tx_crcl = 0x21;
+			yp->tx_crch = 0xf3;
+			yp->tx_state = TX_DATA;
+		}
+		break;
+	case TX_DATA:
+		b = yp->tx_buf[yp->tx_count++];
+		outb(b, THR(dev->base_addr));
+		temp = yp->tx_crcl;
+		yp->tx_crcl = chktabl[temp] ^ yp->tx_crch;
+		yp->tx_crch = chktabh[temp] ^ b;
+		if (yp->tx_count >= yp->tx_len) {
+			yp->tx_state = TX_CRC1;
+		}
+		break;
+	case TX_CRC1:
+		yp->tx_crch = chktabl[yp->tx_crcl] ^ yp->tx_crch;
+		yp->tx_crcl = chktabh[yp->tx_crcl] ^ chktabl[yp->tx_crch] ^ 0xff;
+		outb(yp->tx_crcl, THR(dev->base_addr));
+		yp->tx_state = TX_CRC2;
+		break;
+	case TX_CRC2:
+		outb(chktabh[yp->tx_crch] ^ 0xFF, THR(dev->base_addr));
+		if (skb_queue_empty(&yp->send_queue)) {
+			yp->tx_count = (yp->bitrate * yp->txtail) / 8000;
+			if (yp->dupmode == 2)
+				yp->tx_count += (yp->bitrate * yp->holdd) / 8;
+			if (yp->tx_count == 0)
+				yp->tx_count = 1;
+			yp->tx_state = TX_TAIL;
+		} else {
+			yp->tx_count = 1;
+			yp->tx_state = TX_HEAD;
+		}
+		++yp->stats.tx_packets;
+		break;
+	case TX_TAIL:
+		if (--yp->tx_count <= 0) {
+			yp->tx_state = TX_OFF;
+			ptt_off(dev);
+		}
+		break;
+	}
+}
+
+/***********************************************************************************
+* ISR routine
+************************************************************************************/
+
+static irqreturn_t yam_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev;
+	struct yam_port *yp;
+	unsigned char iir;
+	int counter = 100;
+	int i;
+	int handled = 0;
+
+	for (i = 0; i < NR_PORTS; i++) {
+		dev = yam_devs[i];
+		yp = netdev_priv(dev);
+
+		if (!netif_running(dev))
+			continue;
+
+		while ((iir = IIR_MASK & inb(IIR(dev->base_addr))) != IIR_NOPEND) {
+			unsigned char msr = inb(MSR(dev->base_addr));
+			unsigned char lsr = inb(LSR(dev->base_addr));
+			unsigned char rxb;
+
+			handled = 1;
+
+			if (lsr & LSR_OE)
+				++yp->stats.rx_fifo_errors;
+
+			yp->dcd = (msr & RX_DCD) ? 1 : 0;
+
+			if (--counter <= 0) {
+				printk(KERN_ERR "%s: too many irq iir=%d\n",
+						dev->name, iir);
+				goto out;
+			}
+			if (msr & TX_RDY) {
+				++yp->nb_mdint;
+				yam_tx_byte(dev, yp);
+			}
+			if (lsr & LSR_RXC) {
+				++yp->nb_rxint;
+				rxb = inb(RBR(dev->base_addr));
+				if (msr & RX_FLAG)
+					yam_rx_flag(dev, yp);
+				else
+					yam_rx_byte(dev, yp, rxb);
+			}
+		}
+	}
+out:
+	return IRQ_RETVAL(handled);
+}
+
+#ifdef CONFIG_PROC_FS
+
+static void *yam_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	return (*pos < NR_PORTS) ? yam_devs[*pos] : NULL;
+}
+
+static void *yam_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	++*pos;
+	return (*pos < NR_PORTS) ? yam_devs[*pos] : NULL;
+}
+
+static void yam_seq_stop(struct seq_file *seq, void *v)
+{
+}
+
+static int yam_seq_show(struct seq_file *seq, void *v)
+{
+	struct net_device *dev = v;
+	const struct yam_port *yp = netdev_priv(dev);
+
+	seq_printf(seq, "Device %s\n", dev->name);
+	seq_printf(seq, "  Up       %d\n", netif_running(dev));
+	seq_printf(seq, "  Speed    %u\n", yp->bitrate);
+	seq_printf(seq, "  IoBase   0x%x\n", yp->iobase);
+	seq_printf(seq, "  BaudRate %u\n", yp->baudrate);
+	seq_printf(seq, "  IRQ      %u\n", yp->irq);
+	seq_printf(seq, "  TxState  %u\n", yp->tx_state);
+	seq_printf(seq, "  Duplex   %u\n", yp->dupmode);
+	seq_printf(seq, "  HoldDly  %u\n", yp->holdd);
+	seq_printf(seq, "  TxDelay  %u\n", yp->txd);
+	seq_printf(seq, "  TxTail   %u\n", yp->txtail);
+	seq_printf(seq, "  SlotTime %u\n", yp->slot);
+	seq_printf(seq, "  Persist  %u\n", yp->pers);
+	seq_printf(seq, "  TxFrames %lu\n", yp->stats.tx_packets);
+	seq_printf(seq, "  RxFrames %lu\n", yp->stats.rx_packets);
+	seq_printf(seq, "  TxInt    %u\n", yp->nb_mdint);
+	seq_printf(seq, "  RxInt    %u\n", yp->nb_rxint);
+	seq_printf(seq, "  RxOver   %lu\n", yp->stats.rx_fifo_errors);
+	seq_printf(seq, "\n");
+	return 0;
+}
+
+static struct seq_operations yam_seqops = {
+	.start = yam_seq_start,
+	.next = yam_seq_next,
+	.stop = yam_seq_stop,
+	.show = yam_seq_show,
+};
+
+static int yam_info_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &yam_seqops);
+}
+
+static struct file_operations yam_info_fops = {
+	.owner = THIS_MODULE,
+	.open = yam_info_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release,
+};
+
+#endif
+
+
+/* --------------------------------------------------------------------- */
+
+static struct net_device_stats *yam_get_stats(struct net_device *dev)
+{
+	struct yam_port *yp;
+
+	if (!dev)
+		return NULL;
+
+	yp = netdev_priv(dev);
+	if (yp->magic != YAM_MAGIC)
+		return NULL;
+
+	/* 
+	 * Get the current statistics.  This may be called with the
+	 * card open or closed. 
+	 */
+	return &yp->stats;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int yam_open(struct net_device *dev)
+{
+	struct yam_port *yp = netdev_priv(dev);
+	enum uart u;
+	int i;
+	int ret=0;
+
+	printk(KERN_INFO "Trying %s at iobase 0x%lx irq %u\n", dev->name, dev->base_addr, dev->irq);
+
+	if (!dev || !yp->bitrate)
+		return -ENXIO;
+	if (!dev->base_addr || dev->base_addr > 0x1000 - YAM_EXTENT ||
+		dev->irq < 2 || dev->irq > 15) {
+		return -ENXIO;
+	}
+	if (!request_region(dev->base_addr, YAM_EXTENT, dev->name))
+	{
+		printk(KERN_ERR "%s: cannot 0x%lx busy\n", dev->name, dev->base_addr);
+		return -EACCES;
+	}
+	if ((u = yam_check_uart(dev->base_addr)) == c_uart_unknown) {
+		printk(KERN_ERR "%s: cannot find uart type\n", dev->name);
+		ret = -EIO;
+		goto out_release_base;
+	}
+	if (fpga_download(dev->base_addr, yp->bitrate)) {
+		printk(KERN_ERR "%s: cannot init FPGA\n", dev->name);
+		ret = -EIO;
+		goto out_release_base;
+	}
+	outb(0, IER(dev->base_addr));
+	if (request_irq(dev->irq, yam_interrupt, SA_INTERRUPT | SA_SHIRQ, dev->name, dev)) {
+		printk(KERN_ERR "%s: irq %d busy\n", dev->name, dev->irq);
+		ret = -EBUSY;
+		goto out_release_base;
+	}
+
+	yam_set_uart(dev);
+
+	netif_start_queue(dev);
+	
+	yp->slotcnt = yp->slot / 10;
+
+	/* Reset overruns for all ports - FPGA programming makes overruns */
+	for (i = 0; i < NR_PORTS; i++) {
+		struct net_device *dev = yam_devs[i];
+		struct yam_port *yp = netdev_priv(dev);
+		inb(LSR(dev->base_addr));
+		yp->stats.rx_fifo_errors = 0;
+	}
+
+	printk(KERN_INFO "%s at iobase 0x%lx irq %u uart %s\n", dev->name, dev->base_addr, dev->irq,
+		   uart_str[u]);
+	return 0;
+
+out_release_base:
+	release_region(dev->base_addr, YAM_EXTENT);
+	return ret;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int yam_close(struct net_device *dev)
+{
+	struct sk_buff *skb;
+	struct yam_port *yp = netdev_priv(dev);
+
+	if (!dev)
+		return -EINVAL;
+
+	/*
+	 * disable interrupts
+	 */
+	outb(0, IER(dev->base_addr));
+	outb(1, MCR(dev->base_addr));
+	/* Remove IRQ handler if last */
+	free_irq(dev->irq,dev);
+	release_region(dev->base_addr, YAM_EXTENT);
+	netif_stop_queue(dev);
+	while ((skb = skb_dequeue(&yp->send_queue)))
+		dev_kfree_skb(skb);
+
+	printk(KERN_INFO "%s: close yam at iobase 0x%lx irq %u\n",
+		   yam_drvname, dev->base_addr, dev->irq);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct yam_port *yp = netdev_priv(dev);
+	struct yamdrv_ioctl_cfg yi;
+	struct yamdrv_ioctl_mcs *ym;
+	int ioctl_cmd;
+
+	if (copy_from_user(&ioctl_cmd, ifr->ifr_data, sizeof(int)))
+		 return -EFAULT;
+
+	if (yp->magic != YAM_MAGIC)
+		return -EINVAL;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	if (cmd != SIOCDEVPRIVATE)
+		return -EINVAL;
+
+	switch (ioctl_cmd) {
+
+	case SIOCYAMRESERVED:
+		return -EINVAL;			/* unused */
+
+	case SIOCYAMSMCS:
+		if (netif_running(dev))
+			return -EINVAL;		/* Cannot change this parameter when up */
+		if ((ym = kmalloc(sizeof(struct yamdrv_ioctl_mcs), GFP_KERNEL)) == NULL)
+			return -ENOBUFS;
+		ym->bitrate = 9600;
+		if (copy_from_user(ym, ifr->ifr_data, sizeof(struct yamdrv_ioctl_mcs))) {
+			kfree(ym);
+			return -EFAULT;
+		}
+		if (ym->bitrate > YAM_MAXBITRATE) {
+			kfree(ym);
+			return -EINVAL;
+		}
+		add_mcs(ym->bits, ym->bitrate);
+		kfree(ym);
+		break;
+
+	case SIOCYAMSCFG:
+		if (!capable(CAP_SYS_RAWIO))
+			return -EPERM;
+		if (copy_from_user(&yi, ifr->ifr_data, sizeof(struct yamdrv_ioctl_cfg)))
+			 return -EFAULT;
+
+		if ((yi.cfg.mask & YAM_IOBASE) && netif_running(dev))
+			return -EINVAL;		/* Cannot change this parameter when up */
+		if ((yi.cfg.mask & YAM_IRQ) && netif_running(dev))
+			return -EINVAL;		/* Cannot change this parameter when up */
+		if ((yi.cfg.mask & YAM_BITRATE) && netif_running(dev))
+			return -EINVAL;		/* Cannot change this parameter when up */
+		if ((yi.cfg.mask & YAM_BAUDRATE) && netif_running(dev))
+			return -EINVAL;		/* Cannot change this parameter when up */
+
+		if (yi.cfg.mask & YAM_IOBASE) {
+			yp->iobase = yi.cfg.iobase;
+			dev->base_addr = yi.cfg.iobase;
+		}
+		if (yi.cfg.mask & YAM_IRQ) {
+			if (yi.cfg.irq > 15)
+				return -EINVAL;
+			yp->irq = yi.cfg.irq;
+			dev->irq = yi.cfg.irq;
+		}
+		if (yi.cfg.mask & YAM_BITRATE) {
+			if (yi.cfg.bitrate > YAM_MAXBITRATE)
+				return -EINVAL;
+			yp->bitrate = yi.cfg.bitrate;
+		}
+		if (yi.cfg.mask & YAM_BAUDRATE) {
+			if (yi.cfg.baudrate > YAM_MAXBAUDRATE)
+				return -EINVAL;
+			yp->baudrate = yi.cfg.baudrate;
+		}
+		if (yi.cfg.mask & YAM_MODE) {
+			if (yi.cfg.mode > YAM_MAXMODE)
+				return -EINVAL;
+			yp->dupmode = yi.cfg.mode;
+		}
+		if (yi.cfg.mask & YAM_HOLDDLY) {
+			if (yi.cfg.holddly > YAM_MAXHOLDDLY)
+				return -EINVAL;
+			yp->holdd = yi.cfg.holddly;
+		}
+		if (yi.cfg.mask & YAM_TXDELAY) {
+			if (yi.cfg.txdelay > YAM_MAXTXDELAY)
+				return -EINVAL;
+			yp->txd = yi.cfg.txdelay;
+		}
+		if (yi.cfg.mask & YAM_TXTAIL) {
+			if (yi.cfg.txtail > YAM_MAXTXTAIL)
+				return -EINVAL;
+			yp->txtail = yi.cfg.txtail;
+		}
+		if (yi.cfg.mask & YAM_PERSIST) {
+			if (yi.cfg.persist > YAM_MAXPERSIST)
+				return -EINVAL;
+			yp->pers = yi.cfg.persist;
+		}
+		if (yi.cfg.mask & YAM_SLOTTIME) {
+			if (yi.cfg.slottime > YAM_MAXSLOTTIME)
+				return -EINVAL;
+			yp->slot = yi.cfg.slottime;
+			yp->slotcnt = yp->slot / 10;
+		}
+		break;
+
+	case SIOCYAMGCFG:
+		yi.cfg.mask = 0xffffffff;
+		yi.cfg.iobase = yp->iobase;
+		yi.cfg.irq = yp->irq;
+		yi.cfg.bitrate = yp->bitrate;
+		yi.cfg.baudrate = yp->baudrate;
+		yi.cfg.mode = yp->dupmode;
+		yi.cfg.txdelay = yp->txd;
+		yi.cfg.holddly = yp->holdd;
+		yi.cfg.txtail = yp->txtail;
+		yi.cfg.persist = yp->pers;
+		yi.cfg.slottime = yp->slot;
+		if (copy_to_user(ifr->ifr_data, &yi, sizeof(struct yamdrv_ioctl_cfg)))
+			 return -EFAULT;
+		break;
+
+	default:
+		return -EINVAL;
+
+	}
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int yam_set_mac_address(struct net_device *dev, void *addr)
+{
+	struct sockaddr *sa = (struct sockaddr *) addr;
+
+	/* addr is an AX.25 shifted ASCII mac address */
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void yam_setup(struct net_device *dev)
+{
+	struct yam_port *yp = netdev_priv(dev);
+
+	yp->magic = YAM_MAGIC;
+	yp->bitrate = DEFAULT_BITRATE;
+	yp->baudrate = DEFAULT_BITRATE * 2;
+	yp->iobase = 0;
+	yp->irq = 0;
+	yp->dupmode = 0;
+	yp->holdd = DEFAULT_HOLDD;
+	yp->txd = DEFAULT_TXD;
+	yp->txtail = DEFAULT_TXTAIL;
+	yp->slot = DEFAULT_SLOT;
+	yp->pers = DEFAULT_PERS;
+	yp->dev = dev;
+
+	dev->base_addr = yp->iobase;
+	dev->irq = yp->irq;
+	SET_MODULE_OWNER(dev);
+
+	dev->open = yam_open;
+	dev->stop = yam_close;
+	dev->do_ioctl = yam_ioctl;
+	dev->hard_start_xmit = yam_send_packet;
+	dev->get_stats = yam_get_stats;
+
+	skb_queue_head_init(&yp->send_queue);
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+	dev->hard_header = ax25_encapsulate;
+	dev->rebuild_header = ax25_rebuild_header;
+#else							/* CONFIG_AX25 || CONFIG_AX25_MODULE */
+	dev->hard_header = NULL;
+	dev->rebuild_header = NULL;
+#endif							/* CONFIG_AX25 || CONFIG_AX25_MODULE */
+
+	dev->set_mac_address = yam_set_mac_address;
+
+	dev->type = ARPHRD_AX25;	/* AF_AX25 device */
+	dev->hard_header_len = 73;	/* We do digipeaters now */
+	dev->mtu = 256;				/* AX25 is the default */
+	dev->addr_len = 7;			/* sizeof an ax.25 address */
+	memcpy(dev->broadcast, ax25_bcast, 7);
+	memcpy(dev->dev_addr, ax25_test, 7);
+
+}
+
+static int __init yam_init_driver(void)
+{
+	struct net_device *dev;
+	int i, err;
+	char name[IFNAMSIZ];
+
+	printk(yam_drvinfo);
+
+	for (i = 0; i < NR_PORTS; i++) {
+		sprintf(name, "yam%d", i);
+		
+		dev = alloc_netdev(sizeof(struct yam_port), name,
+				   yam_setup);
+		if (!dev) {
+			printk(KERN_ERR "yam: cannot allocate net device %s\n",
+			       dev->name);
+			err = -ENOMEM;
+			goto error;
+		}
+		
+		err = register_netdev(dev);
+		if (err) {
+			printk(KERN_WARNING "yam: cannot register net device %s\n", dev->name);
+			goto error;
+		}
+		yam_devs[i] = dev;
+
+	}
+
+	yam_timer.function = yam_dotimer;
+	yam_timer.expires = jiffies + HZ / 100;
+	add_timer(&yam_timer);
+
+	proc_net_fops_create("yam", S_IRUGO, &yam_info_fops);
+	return 0;
+ error:
+	while (--i >= 0) {
+		unregister_netdev(yam_devs[i]);
+		free_netdev(yam_devs[i]);
+	}
+	return err;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void __exit yam_cleanup_driver(void)
+{
+	struct yam_mcs *p;
+	int i;
+
+	del_timer(&yam_timer);
+	for (i = 0; i < NR_PORTS; i++) {
+		struct net_device *dev = yam_devs[i];
+		if (dev) {
+			unregister_netdev(dev);
+			free_netdev(dev);
+		}
+	}
+
+	while (yam_data) {
+		p = yam_data;
+		yam_data = yam_data->next;
+		kfree(p);
+	}
+
+	proc_net_remove("yam");
+}
+
+/* --------------------------------------------------------------------- */
+
+MODULE_AUTHOR("Frederic Rible F1OAT frible@teaser.fr");
+MODULE_DESCRIPTION("Yam amateur radio modem driver");
+MODULE_LICENSE("GPL");
+
+module_init(yam_init_driver);
+module_exit(yam_cleanup_driver);
+
+/* --------------------------------------------------------------------- */
+
diff --git a/drivers/net/hamradio/yam1200.h b/drivers/net/hamradio/yam1200.h
new file mode 100644
index 0000000..53ca8a3
--- /dev/null
+++ b/drivers/net/hamradio/yam1200.h
@@ -0,0 +1,343 @@
+/*
+ *
+ * File yam1k2b5.mcs converted to h format by mcs2h
+ *
+ * (C) F6FBB 1998
+ *
+ * Tue Aug 25 20:24:08 1998
+ *
+ */
+
+static unsigned char bits_1200[]= {
+0xff,0xf2,0x00,0xa5,0xad,0xff,0xfe,0x9f,0xff,0xef,0xf3,0xcb,0xff,0xdb,0xfc,0xf2,
+0xff,0xf6,0xff,0x3c,0xbf,0xfd,0xbf,0xdf,0x6e,0x3f,0x6f,0xf1,0x7d,0xb4,0xfd,0xbf,
+0xdf,0x6f,0x3f,0x6f,0xf7,0x0b,0xff,0xdb,0xfd,0xf2,0xff,0xf6,0xff,0xff,0xff,0xff,
+0xf0,0xcf,0xff,0xff,0xff,0xfe,0xff,0xff,0xdf,0xff,0xff,0xff,0xef,0xff,0xff,0xff,
+0xfd,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,0xbf,
+0xff,0xff,0xf7,0xff,0xff,0xfb,0xff,0xff,0xff,0xfc,0xff,0xfe,0xff,0xff,0xff,0xf0,
+0x5f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xf7,0xff,0xff,0xff,0xf1,0xff,0xff,0xfe,0x7f,0xbf,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf7,0xff,0xfb,0xff,0xff,0xff,0xf0,0x9f,
+0xff,0xff,0xff,0xfe,0xff,0xfd,0xff,0xff,0xff,0xff,0xdf,0xff,0xff,0xff,0xf7,0xff,
+0xff,0xff,0xfb,0xff,0xfb,0xff,0xff,0xff,0xf0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xf7,0xff,0xff,0xfb,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xef,0xff,0xf0,0x5f,0xff,
+0xff,0xff,0xfe,0xff,0xff,0xef,0xff,0xff,0xfb,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xbf,0xff,0xff,0xdf,0xf7,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xfb,0xfe,0xff,0xff,0xff,0xff,0xff,0xf0,0xff,0xff,0xff,
+0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xeb,
+0xff,0xff,0xff,0xfd,0xff,0xbf,0xf1,0xff,0xff,0xff,0xff,0xdf,0xff,0xff,0xff,0xfb,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x6f,0xff,0xff,0xff,
+0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xf7,0xff,0xff,0xf1,0xff,0xff,0xf7,0xbf,0xe7,0xff,0xff,0xff,0xff,0xfb,
+0xff,0xff,0xff,0xff,0xff,0xff,0x77,0xff,0xff,0xff,0xf0,0xff,0xff,0xff,0xff,0xfe,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x1f,0xff,0xff,0xff,0xfe,0xdb,
+0xff,0xff,0xf5,0xa5,0xfd,0x4b,0x6e,0xef,0x33,0x32,0xdd,0xd3,0x4a,0xd6,0x92,0xfe,
+0xb3,0x3f,0xbd,0xf1,0xfa,0xdb,0xfe,0xf7,0xf6,0x96,0xbd,0xbd,0xff,0xbd,0xff,0xed,
+0x7f,0x6b,0x7f,0xfb,0xdf,0xfe,0xfb,0xfe,0x90,0xcf,0xff,0xff,0xff,0xfe,0xbe,0xef,
+0xff,0xff,0xdb,0x5f,0xf6,0xff,0xf6,0x8f,0xfd,0xa5,0xdd,0xff,0xff,0xff,0xff,0x6f,
+0x7f,0xdb,0xf1,0xfc,0xbf,0xff,0x6f,0xff,0xef,0xfc,0x5b,0x5d,0xda,0xdf,0xf4,0xff,
+0xf2,0xff,0xfd,0xbf,0xff,0xff,0xff,0xd0,0x1f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,
+0xff,0xfb,0xef,0xb7,0xfc,0x33,0xff,0xfb,0xff,0x04,0x6a,0xf3,0x3c,0x36,0xff,0xf0,
+0x0f,0xf1,0x0f,0xff,0xff,0xff,0xf3,0x15,0x72,0x0f,0xf1,0x6f,0xff,0xfe,0x94,0x3f,
+0xff,0xff,0xff,0x7b,0xff,0xff,0xf0,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xf0,
+0xf7,0xef,0xb7,0xfc,0x33,0xff,0xff,0xff,0x04,0x6a,0xf3,0x3c,0x36,0xff,0xf0,0x0f,
+0xf1,0x0f,0xff,0xff,0xff,0xf3,0x15,0x73,0x8f,0xf2,0x6f,0xff,0xfe,0x94,0x3f,0xff,
+0xff,0xff,0x7d,0x9f,0xff,0xf0,0x0f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0x9e,
+0xff,0xfc,0xef,0xd3,0xfb,0xff,0x7f,0xf5,0x5f,0xfe,0x59,0xff,0xff,0xff,0xfc,0xf1,
+0xfe,0x7f,0xff,0xff,0xfa,0x17,0xff,0xe7,0xef,0xef,0xff,0xff,0x3f,0xf1,0xff,0xff,
+0xff,0xff,0xff,0xff,0xf0,0xff,0xff,0xff,0xff,0xfe,0xf5,0xff,0xbf,0xff,0xfc,0xea,
+0xff,0xf0,0xff,0xff,0xbf,0xf9,0x3f,0xb1,0xef,0xff,0xd7,0xff,0xfb,0xff,0xf0,0xff,
+0xff,0xf3,0xff,0xdf,0xff,0x7b,0xff,0xfd,0xff,0xf6,0xff,0xbf,0xff,0xff,0xbf,0xff,
+0xff,0xff,0xda,0xf0,0xff,0xff,0xff,0xff,0xfe,0xf2,0xc0,0x01,0x00,0x00,0x02,0x02,
+0x02,0x02,0x00,0x40,0x40,0x40,0x10,0x00,0x00,0x00,0x20,0x00,0x00,0x01,0x00,0x00,
+0x00,0x00,0x00,0x00,0x19,0x00,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,
+0x00,0x3c,0xf0,0xaf,0xff,0xff,0xff,0xfe,0xfd,0xbf,0xff,0xff,0xfb,0xff,0xfd,0xff,
+0xff,0x7f,0xff,0xff,0xbf,0xff,0xef,0xff,0xff,0xfd,0xff,0xff,0xf1,0xff,0xdf,0xff,
+0xff,0xff,0xff,0xff,0xff,0xbf,0xfe,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xdf,
+0xdb,0xf0,0x6f,0xff,0xff,0xff,0xfe,0xf0,0xbf,0xdf,0xff,0x7f,0xff,0xff,0xff,0xff,
+0xdf,0xdf,0xff,0xef,0xff,0x9e,0xef,0xff,0xff,0x7f,0xff,0xf1,0xef,0xff,0xff,0xff,
+0xf7,0xfa,0xbf,0xff,0xff,0xfe,0x47,0xef,0xff,0xbd,0xf6,0xff,0xff,0xdf,0xf5,0xf0,
+0xf0,0xef,0xff,0xff,0xff,0xfe,0xf8,0x30,0x00,0x00,0x00,0x04,0x00,0x01,0x02,0x08,
+0x16,0x00,0x00,0x00,0x80,0x00,0x01,0x02,0x00,0x80,0x01,0x0c,0x02,0x00,0x00,0x01,
+0x00,0x00,0x20,0x00,0x00,0x06,0x00,0x20,0x00,0x10,0x00,0x14,0x00,0x04,0xc1,0xf0,
+0x2f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0x7f,
+0xec,0xff,0xff,0xfa,0xff,0xbf,0xff,0x6f,0xff,0xe1,0xff,0xff,0xff,0xff,0xbd,0xfe,
+0x46,0xff,0xef,0x7f,0xcd,0xdf,0xff,0xff,0xfd,0xff,0xbd,0xff,0x7f,0x7f,0xf0,0x4f,
+0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x0f,0xff,
+0xff,0xff,0xfe,0xff,0xff,0xff,0xfd,0xa4,0xbc,0xcd,0x6d,0x6b,0x6f,0x5b,0xdc,0x33,
+0x5a,0xf6,0xf7,0xf6,0xb3,0x3f,0xbd,0xc1,0xfa,0x5a,0xf6,0xf6,0xb6,0xf7,0xff,0xbd,
+0xbb,0x3c,0xce,0xcf,0x34,0xef,0x33,0xbb,0xcc,0xff,0xff,0xff,0xf0,0x4f,0xff,0xff,
+0xff,0xfe,0xbf,0xff,0xff,0xff,0xdb,0xff,0xf6,0xd6,0xff,0xfd,0xfd,0xbf,0xff,0xad,
+0xbf,0xf9,0x7f,0x6f,0xfc,0xdb,0xf1,0xfd,0xbf,0xff,0x6f,0xff,0xff,0xda,0xdb,0xfc,
+0xdb,0xff,0x76,0x8f,0xf6,0xff,0xcd,0xab,0xfe,0xfb,0xff,0xd0,0xff,0xff,0xff,0xff,
+0xfe,0xff,0x9f,0xff,0xf4,0x20,0xaf,0x6d,0x0b,0xc1,0x7b,0xff,0xff,0xff,0xcb,0xff,
+0x3f,0xf0,0xef,0x7f,0x0f,0xf1,0xc3,0x3c,0xff,0xff,0xff,0xff,0xff,0xff,0xf8,0x0b,
+0x1d,0x6a,0x64,0x05,0x6b,0x99,0x01,0xff,0xfd,0xef,0xf0,0x2f,0xff,0xff,0xff,0xfe,
+0xff,0xff,0xff,0xf4,0x00,0x2f,0xcc,0x0b,0xc3,0x7f,0xff,0xff,0xff,0x0a,0xdf,0xbf,
+0xfd,0x7f,0xff,0xff,0xf1,0xc3,0xbf,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x4a,0x0e,
+0x96,0x64,0x02,0x97,0x99,0x10,0xff,0xff,0xff,0xf0,0xdf,0xff,0xff,0xff,0xfe,0xff,
+0xff,0xff,0xfe,0x84,0xf9,0xd5,0x27,0xf1,0x7f,0xff,0xf8,0xeb,0xdf,0xf3,0xcf,0x3f,
+0x1f,0xff,0xf7,0x11,0xff,0xcf,0xff,0xfe,0x67,0xff,0xff,0xff,0xff,0xc4,0xff,0xff,
+0xb3,0xa1,0xff,0xf9,0xe0,0xff,0xff,0xff,0xf0,0xef,0xff,0xff,0xff,0xfe,0xf5,0xff,
+0xff,0xfb,0x7f,0xe0,0xff,0xc7,0xfe,0x7f,0x3f,0xff,0xfd,0x77,0x8d,0x7f,0x0f,0xff,
+0xc3,0xff,0xf1,0xbf,0x8f,0xcf,0xff,0xff,0xdd,0x7b,0xff,0xf6,0xfa,0xf7,0xff,0x40,
+0x9f,0xf9,0x7f,0xd8,0xff,0xff,0xfa,0xf0,0x1f,0xff,0xff,0xff,0xfe,0xf1,0xc0,0x00,
+0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x10,0x00,0x00,0x10,
+0x00,0x01,0x00,0x10,0x20,0x20,0x00,0x00,0x10,0x00,0x04,0x01,0x05,0x00,0x00,0x00,
+0x00,0x40,0x40,0x00,0x00,0x3c,0xf0,0x1f,0xff,0xff,0xff,0xfe,0xfd,0xbf,0xff,0xff,
+0xff,0xff,0xfe,0x7f,0x7f,0xff,0xef,0xff,0xff,0xdf,0xff,0xff,0xdf,0xff,0xef,0xf7,
+0xf1,0xff,0xff,0xff,0xff,0xdf,0xff,0xff,0xf7,0xff,0xff,0xff,0xfc,0xfd,0xff,0x7f,
+0x7e,0xff,0xff,0xff,0xdb,0xf0,0x6f,0xff,0xff,0xff,0xfe,0xf0,0xbb,0xff,0xff,0xff,
+0xff,0xff,0xfe,0xeb,0xfd,0x6f,0xff,0xf7,0xfe,0xf5,0x7f,0xff,0xff,0x7f,0xbf,0xb1,
+0xff,0xff,0x9f,0xbf,0xfb,0xff,0xfe,0xff,0xfe,0xff,0xf7,0xeb,0xdf,0xbf,0x5f,0xdd,
+0xff,0xdb,0xfd,0xd0,0xf0,0x6f,0xff,0xff,0xff,0xfe,0xf8,0x30,0x20,0x00,0x42,0x00,
+0x00,0x00,0x30,0x18,0x04,0x08,0x09,0x21,0x82,0x80,0x02,0x00,0x08,0x00,0x01,0x00,
+0x00,0x00,0x0c,0x20,0x10,0x00,0x11,0x00,0x44,0x84,0x00,0x20,0x20,0x84,0x80,0x00,
+0x00,0x00,0xc1,0xf0,0xdf,0xff,0xff,0xff,0xfe,0xff,0xf7,0xff,0xfb,0xdd,0xf9,0xff,
+0xda,0xff,0xdc,0xdd,0xfc,0xfb,0xff,0xbf,0xfb,0x3e,0xd7,0x96,0xfe,0x61,0xf7,0xff,
+0x7f,0xff,0x3f,0xfd,0xff,0xdf,0xcf,0xf7,0xdf,0xf7,0xbf,0xfd,0xff,0xfe,0xef,0xef,
+0xfe,0xff,0xf0,0x7f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xf0,0x2f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xf3,0xbd,0xfd,0x4b,0x74,0xcf,
+0x73,0x5b,0xcb,0x3b,0xdf,0xfe,0xf7,0xfe,0xd3,0x75,0xac,0xa1,0xfb,0xdf,0xfe,0xf7,
+0x76,0x96,0xb5,0x24,0xbd,0xa5,0xad,0x49,0x2f,0x69,0x2b,0x52,0x5b,0xbd,0xff,0xff,
+0xf0,0xcf,0xff,0xff,0xff,0xfe,0xbf,0xff,0xff,0xff,0xdb,0xff,0xf6,0xfe,0xff,0xcc,
+0xa7,0xfb,0xad,0xff,0x7f,0x6f,0xff,0x6d,0x7f,0xdb,0xf1,0xfd,0xbf,0xff,0x6f,0xff,
+0x6f,0xff,0xdb,0xff,0xdb,0xff,0xf6,0x97,0xf6,0xff,0xb5,0xb5,0xff,0xff,0xff,0xd0,
+0xef,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfd,0xa5,0xbc,0x43,0xfc,0x7c,0x03,0xe7,
+0xff,0xff,0x20,0xff,0xff,0xff,0xcc,0xfd,0x7d,0xf1,0xff,0xff,0xff,0xff,0xd5,0x59,
+0xba,0x56,0x66,0x6a,0xad,0x9a,0xa9,0x9a,0x97,0xa5,0xaa,0xbb,0xff,0xff,0xf0,0x0f,
+0xff,0xff,0xff,0xfe,0xfe,0xfb,0xff,0xfd,0xf7,0xfd,0x43,0xff,0xfd,0x6b,0xe7,0xff,
+0xff,0xdf,0xff,0xff,0xff,0xff,0xff,0x3f,0xf1,0xff,0xff,0xff,0xff,0xd5,0x59,0xb5,
+0xa6,0x66,0x6a,0xad,0x9a,0xa9,0x99,0x6b,0x5a,0xaa,0xff,0xff,0xb7,0xf0,0x3f,0xff,
+0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0x9c,0xf7,0xfd,0xd2,0x41,0xff,0xff,0xf2,0x7f,
+0x8f,0xff,0xff,0x3d,0xf3,0xff,0x17,0xf1,0xff,0xff,0xff,0xff,0xff,0x7f,0xdf,0xfc,
+0x8f,0x38,0xff,0xef,0x23,0xff,0xfb,0xf7,0xc8,0xff,0xff,0xff,0xf0,0x9f,0xff,0xff,
+0xff,0xfe,0xf5,0x7f,0xff,0xfd,0xff,0xe4,0xff,0xeb,0xff,0xcf,0xbf,0xfa,0xff,0xab,
+0xef,0xff,0xfb,0xff,0xf3,0xfd,0x61,0xff,0xff,0xff,0xff,0xfa,0xff,0xfb,0xfd,0x0d,
+0xff,0xfe,0xff,0x43,0x7f,0xfe,0xbf,0xd0,0xfd,0xff,0xfa,0xf0,0x3f,0xff,0xff,0xff,
+0xfe,0xf3,0xc0,0x00,0x00,0x00,0x02,0x00,0x02,0x01,0x00,0x60,0xc0,0x40,0x00,0x00,
+0x00,0x00,0x34,0x04,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x88,0x00,
+0x00,0x03,0x00,0x00,0x40,0x00,0x40,0x00,0x00,0x3c,0xf0,0x3f,0xff,0xff,0xff,0xfe,
+0xfd,0x3f,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,0x7f,0xbf,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xf7,0xf1,0xff,0xff,0xff,0xff,0xff,0xf7,0xff,0xff,0xff,0xfd,0xff,
+0xff,0xff,0xff,0xfe,0xfe,0x5f,0xff,0xff,0xcb,0xf0,0xdf,0xff,0xff,0xff,0xfe,0xf0,
+0xff,0xff,0xfd,0xff,0xef,0xe3,0xde,0xee,0xd9,0xc5,0x93,0xff,0xff,0xfe,0xfe,0xff,
+0xfb,0xee,0xfe,0xf1,0xff,0xff,0xff,0xff,0xff,0xfd,0xff,0xbf,0xf7,0xff,0xff,0x7f,
+0xaf,0xbd,0xdf,0xdf,0xfb,0xf3,0xf3,0xf0,0xf0,0xaf,0xff,0xff,0xff,0xfe,0xf8,0x34,
+0x00,0x06,0x61,0x00,0x18,0x01,0xa0,0x05,0x17,0x00,0x20,0x05,0x28,0x20,0x00,0x00,
+0x05,0x00,0x41,0x00,0x00,0x40,0x00,0x09,0x00,0x01,0x20,0x86,0x82,0x08,0x40,0x03,
+0x80,0x30,0x70,0x08,0x14,0x02,0xc1,0xf0,0xcf,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,
+0xff,0xff,0xbd,0xef,0xfb,0xff,0xff,0xfb,0x9c,0x7f,0xef,0xdf,0xff,0xbf,0xeb,0xde,
+0xff,0xc1,0x7f,0xff,0xfb,0x7f,0xff,0xff,0xff,0x5f,0xff,0xff,0xff,0xdf,0xbf,0xef,
+0x3f,0xf7,0x8f,0xef,0x7f,0xff,0xf0,0x7f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xf0,0x3f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xbd,
+0xdf,0xef,0x7d,0x6d,0x2b,0x5a,0x5d,0xd2,0xdf,0xf6,0x92,0xb6,0xb2,0xb3,0xac,0xa1,
+0xfb,0xdf,0xfe,0xf1,0xee,0xf5,0xf6,0xbc,0x6b,0xbd,0x7d,0xaf,0x1a,0xef,0x5f,0x6b,
+0xc6,0xff,0xff,0xff,0xf0,0x5f,0xff,0xff,0xff,0xfe,0xbf,0xff,0xff,0xff,0xdb,0xff,
+0xf6,0xff,0xf6,0xb7,0xfd,0xad,0xfd,0xbf,0xf3,0x6f,0xff,0x6f,0xff,0xdb,0xd1,0xfd,
+0xbf,0xff,0x6f,0xf5,0x6b,0xbc,0x5b,0x3c,0xda,0xef,0x16,0xaf,0x16,0xff,0xcd,0xab,
+0xff,0x6f,0xff,0xd0,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfc,0xbf,0xff,0xff,
+0xff,0x6c,0x03,0x10,0xc1,0xf3,0xff,0xf3,0x3a,0xf3,0xca,0xff,0xaf,0xf1,0xff,0xff,
+0xff,0xff,0xd9,0x96,0xa6,0x65,0xa6,0x66,0x6a,0x95,0x69,0x69,0x6a,0x5a,0x5a,0xff,
+0xff,0x5f,0xf0,0x1f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xbf,0xff,0xff,0xff,
+0xea,0x0f,0x50,0xc3,0xf3,0x7f,0xff,0xf3,0xf3,0xc3,0xff,0xaf,0xf1,0xff,0xff,0xff,
+0xff,0xd9,0x96,0xa6,0x65,0xa6,0x66,0x6a,0x95,0x69,0x69,0x6a,0x5a,0x5a,0xff,0xff,
+0xff,0xf0,0x3f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xd7,0xff,0xff,0x5f,0xc1,
+0x3f,0xf7,0x5e,0xf5,0xce,0x9e,0x5f,0x3f,0x17,0xff,0xf3,0xe1,0xff,0xff,0xff,0xff,
+0xd8,0xff,0xfa,0xfe,0x67,0xff,0xfe,0xbf,0x5a,0xff,0xff,0xaf,0xf5,0xff,0xff,0xff,
+0xf0,0x2f,0xff,0xff,0xff,0xfe,0xf5,0xff,0xff,0xfd,0xff,0xf7,0xff,0xfd,0x4e,0x3d,
+0x3f,0xe7,0x0b,0xbf,0x8f,0xf9,0xff,0xeb,0xe3,0xff,0xe1,0xff,0xff,0xfc,0xff,0xc7,
+0x9f,0xff,0x3e,0x39,0xe5,0xff,0xcf,0x9b,0xf9,0xff,0xff,0xc5,0xff,0xff,0xfa,0xf0,
+0x5f,0xff,0xff,0xff,0xfe,0xf3,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,
+0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x20,
+0x00,0x01,0x10,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3c,0xf0,0x4f,
+0xff,0xff,0xff,0xfe,0xfd,0xbf,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xbf,
+0x3f,0xff,0xff,0xbf,0xff,0xff,0xff,0xfb,0xf1,0xff,0xff,0xff,0xff,0xf7,0xff,0xf7,
+0xff,0xed,0xff,0xfb,0xfe,0xff,0x7f,0xff,0x7f,0xdf,0xff,0xff,0xdd,0xf0,0x3f,0xff,
+0xff,0xff,0xfe,0xf0,0xff,0xff,0xf3,0xff,0xf7,0xff,0xfe,0x5f,0xff,0xf7,0xff,0xff,
+0xdf,0xff,0xff,0xff,0xf7,0xfe,0x7b,0xf1,0xff,0xfd,0xfd,0xff,0xdf,0xdf,0xff,0x7d,
+0x73,0xf9,0xff,0xc3,0x7e,0xfe,0xff,0xef,0xd7,0xff,0xcf,0xd0,0xf0,0x6f,0xff,0xff,
+0xff,0xfe,0xf8,0x30,0x00,0x00,0x40,0x04,0x00,0x01,0x41,0x20,0x00,0x04,0x00,0x02,
+0xd5,0x09,0x00,0x02,0x80,0x02,0x01,0x00,0x00,0x00,0x0a,0x04,0x00,0x07,0x00,0x01,
+0x50,0x01,0x80,0x02,0x61,0x40,0x41,0x0c,0x14,0x08,0xc1,0xf0,0x9f,0xff,0xff,0xff,
+0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xdf,0xcb,0x5f,0xfe,0xef,0xff,0xfe,
+0xff,0x3f,0xff,0x7f,0xfd,0xc1,0xff,0xff,0x7f,0xff,0xdf,0xfd,0xfc,0xfd,0xf7,0xee,
+0xff,0xff,0x4e,0xff,0xdf,0xcf,0xdb,0xeb,0xff,0xff,0xf0,0x1f,0xff,0xff,0xff,0xfe,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x2f,0xff,0xff,0xff,0xfe,0x7f,
+0xff,0xff,0xff,0xfd,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0xff,0xff,0xff,
+0xf7,0xfb,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0x7f,0xff,0xff,0xff,0x7f,0xff,0xf0,0x1f,0xff,0xff,0xff,0xfe,0xdd,0xff,
+0xff,0xff,0xa5,0xff,0x6f,0x6b,0xe9,0x6f,0xda,0xca,0xfb,0xdd,0xee,0xf7,0xf6,0xb2,
+0xb3,0xa4,0xa1,0x5b,0x5b,0xf6,0xd7,0xf4,0xf7,0x7b,0xbd,0xbd,0xad,0xcf,0xef,0x7f,
+0x6b,0x7f,0x3b,0xdf,0xdb,0xff,0xff,0x30,0xcf,0xff,0xff,0xff,0xfe,0xbf,0xff,0xff,
+0xff,0xff,0xff,0xf6,0xfe,0x96,0xff,0xfd,0xb5,0xfd,0xbf,0xad,0x7f,0xff,0x6f,0xff,
+0xde,0xd1,0xad,0xad,0xe9,0xff,0xf1,0xec,0xef,0xde,0x3f,0xcb,0xff,0xf6,0xff,0x32,
+0xff,0xc5,0xbd,0xff,0xff,0xff,0xd0,0xbf,0xff,0xff,0xff,0xfe,0xfe,0xfb,0xff,0xf4,
+0x28,0xbf,0xff,0xfd,0xfb,0xd3,0xff,0xff,0x42,0xff,0xff,0xff,0xea,0xb3,0xfc,0xc3,
+0xc1,0xff,0x33,0xff,0xc0,0x15,0x6b,0x70,0xff,0xf0,0xf2,0x4f,0xff,0xfc,0x3e,0x97,
+0x3c,0xff,0xff,0xfd,0xef,0xf0,0xbf,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0x78,
+0xbf,0xff,0xfd,0xf3,0xef,0x55,0xff,0x7e,0xff,0xff,0xff,0xea,0xb3,0xfc,0xc3,0xc1,
+0xff,0x33,0xff,0xc0,0x15,0x6f,0xff,0x0f,0xf0,0xf0,0x0f,0xff,0xfc,0x3d,0x6b,0xc3,
+0xff,0xff,0xfe,0xf7,0xf0,0xcf,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xfc,0xff,
+0xff,0x23,0xf8,0x7f,0xff,0x4e,0xff,0xff,0xff,0xfb,0xf9,0x17,0xff,0xf6,0xf1,0xff,
+0xcf,0xef,0xff,0xff,0x13,0xdf,0xe6,0x2f,0xc7,0xff,0xff,0xe7,0xc1,0xfd,0xff,0xfe,
+0xff,0xff,0xff,0xf0,0x4f,0xff,0xff,0xff,0xfe,0xf5,0xff,0xff,0xff,0xfe,0xae,0xff,
+0xff,0x7f,0x3b,0x3f,0xfc,0x7f,0xfc,0xef,0xff,0xfc,0xe2,0x7b,0xff,0xf1,0xfd,0xed,
+0xef,0xff,0xff,0x35,0x73,0xff,0xff,0xfe,0xfa,0xff,0xff,0xff,0xfe,0xbf,0xff,0xff,
+0xff,0xfa,0xf0,0x8f,0xff,0xff,0xff,0xfe,0xf1,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x00,0x0c,0x04,0x01,0x40,0x40,0x00,
+0x00,0x30,0x28,0x04,0x00,0x08,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,
+0x38,0xf0,0x0f,0xff,0xff,0xff,0xfe,0xfd,0xbf,0xff,0xff,0xff,0xff,0xfb,0xff,0x7f,
+0xff,0xff,0x9f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf1,0xff,0xdf,0xdf,0xff,
+0xff,0xff,0xff,0xed,0xff,0xfd,0xff,0xff,0xff,0xff,0xff,0xbf,0xbf,0xff,0xff,0xc3,
+0xf0,0x3f,0xff,0xff,0xff,0xfe,0xf0,0xbf,0xfd,0xff,0xbf,0xff,0xff,0xfd,0xff,0xff,
+0xff,0xff,0xff,0xfd,0x7b,0xff,0x7f,0xff,0xbd,0xff,0xf1,0xef,0xff,0xff,0xfd,0xdf,
+0xfd,0xfb,0xff,0xff,0xbf,0xbe,0xff,0xcd,0x7f,0xfc,0xf7,0xf7,0x6f,0xbf,0xd8,0xf0,
+0xef,0xff,0xff,0xff,0xfe,0xf8,0x30,0x00,0x00,0x00,0x04,0x00,0x00,0xa0,0x00,0x00,
+0xc0,0x00,0x00,0x20,0x34,0x00,0x00,0x00,0x0c,0x81,0x00,0x20,0xa4,0x20,0x00,0x10,
+0x08,0x04,0x48,0x08,0x00,0x40,0x93,0x00,0x10,0x00,0x38,0x18,0x20,0xc1,0xf0,0x3f,
+0xff,0xff,0xff,0xfe,0xff,0xfb,0xff,0xff,0xb9,0xdf,0xfe,0xb3,0xff,0xff,0xe7,0xfd,
+0xff,0xff,0x3b,0xff,0x7f,0xff,0xbf,0xff,0xc1,0xff,0xfc,0xff,0xff,0x3f,0x77,0xfe,
+0xfe,0xcf,0xff,0xbf,0xfd,0xbf,0xff,0xfe,0xed,0xf2,0xfd,0xf7,0xff,0xf0,0x2f,0xff,
+0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0xbf,0xff,0xff,
+0xff,0xfe,0xff,0xff,0xff,0xf3,0xad,0xcf,0xef,0x70,0xc9,0x73,0x3b,0xdf,0x5b,0x4a,
+0xf6,0xb7,0xfe,0xd7,0xf5,0xbc,0xc1,0x33,0xca,0xd6,0xb7,0x6e,0xf7,0xfb,0xbd,0xc5,
+0x24,0xcf,0x6f,0x2f,0x4d,0x2b,0xba,0x5a,0xff,0xff,0xff,0xf0,0xaf,0xff,0xff,0xff,
+0xfe,0xbf,0xff,0xff,0xff,0xff,0xf6,0xf6,0xd7,0xff,0xff,0xad,0xbd,0xff,0xff,0xff,
+0xef,0xf7,0x7f,0xfc,0x5b,0xb1,0xfd,0xbd,0x75,0x6f,0xef,0x6a,0xfd,0x5b,0xfb,0xdb,
+0x3a,0xbf,0x8e,0x9f,0xff,0xbf,0xfd,0xff,0x6f,0xff,0xd0,0x6f,0xff,0xff,0xff,0xfe,
+0xff,0xbb,0xff,0xf0,0x3f,0xff,0xff,0xfd,0xfb,0x7f,0xde,0xff,0xff,0x5a,0xd6,0xbf,
+0xd8,0x2a,0xbf,0xbf,0xf1,0xe5,0xff,0xcc,0xc0,0xa9,0x70,0xff,0xf3,0x3c,0x3c,0xfd,
+0x57,0xfd,0x98,0x03,0x00,0xc3,0xff,0xff,0xff,0xf0,0xaf,0xff,0xff,0xff,0xfe,0xff,
+0xff,0xff,0xff,0x3d,0xbf,0xff,0xfd,0xfb,0xff,0xdb,0xff,0xff,0x0f,0xfc,0x3f,0xd8,
+0x2a,0xbf,0xbf,0xf1,0xef,0xff,0xcc,0xc0,0x96,0xbe,0xff,0xf3,0x3f,0xff,0xfd,0x57,
+0xfd,0x99,0x0f,0xff,0xc3,0xff,0xff,0xff,0xf0,0x4f,0xff,0xff,0xff,0xfe,0xff,0xff,
+0xff,0xf1,0xe7,0xff,0xff,0xf3,0x8e,0x7b,0xff,0xa8,0xff,0xdf,0x7f,0x8e,0x78,0x73,
+0xff,0xf1,0x51,0x62,0xff,0xfc,0x4b,0xff,0xf3,0xff,0x7e,0xcf,0xf9,0xff,0xfd,0xff,
+0xff,0x7f,0xff,0xe0,0xff,0xff,0xff,0xf0,0x4f,0xff,0xff,0xff,0xfe,0xf5,0xff,0xff,
+0xfb,0xfd,0xae,0xff,0xfc,0xfe,0x6f,0x3f,0xf8,0xfd,0x77,0xaf,0xfe,0x37,0xfe,0x7b,
+0xff,0xb1,0x8c,0xff,0xef,0xfd,0xf8,0xe7,0xbf,0xff,0xf1,0xfe,0x3e,0xf7,0xfe,0x95,
+0x3e,0xbf,0xff,0xff,0xff,0xfa,0xf0,0xbf,0xff,0xff,0xff,0xfe,0xf1,0xc0,0x00,0x00,
+0x01,0x04,0x00,0x00,0x00,0x00,0x80,0x02,0x00,0x00,0x10,0x00,0x10,0x00,0x10,0x08,
+0x41,0x80,0x10,0x00,0x00,0x08,0x10,0x84,0x00,0x0c,0x04,0x02,0x61,0x00,0x00,0x81,
+0x00,0x00,0x00,0x00,0x3d,0xf0,0x7f,0xff,0xff,0xff,0xfe,0xfd,0xbf,0xff,0xff,0xff,
+0xff,0xff,0x7f,0xff,0xfe,0xfd,0xbf,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf1,
+0x7f,0xbf,0xf7,0x7f,0xef,0xff,0xef,0xff,0xf7,0xfd,0xff,0xff,0xfd,0x7f,0xff,0xbe,
+0xdf,0xff,0xff,0xd9,0xf0,0xbf,0xff,0xff,0xff,0xfe,0xf0,0xbb,0xff,0x7f,0xfb,0xff,
+0xfb,0xff,0xbf,0xff,0xf3,0x7f,0xfb,0xfd,0xeb,0x7f,0xdf,0xfa,0xff,0xde,0xf0,0xed,
+0xff,0xb1,0xf7,0xf9,0x1f,0xb5,0x5b,0xfe,0x7e,0xf7,0xbe,0xfd,0x7f,0x5f,0xb5,0xf7,
+0xff,0xff,0xd0,0xf0,0x4f,0xff,0xff,0xff,0xfe,0xf8,0x30,0x01,0x00,0x07,0x42,0x01,
+0x00,0x6a,0x18,0x50,0x80,0x00,0x00,0x02,0x40,0x01,0x01,0x20,0x01,0x01,0x24,0x14,
+0x21,0x10,0x02,0x08,0x07,0x08,0x00,0x40,0x10,0x80,0x58,0x00,0x84,0x80,0x18,0x10,
+0x40,0xc1,0xf0,0xbf,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xf7,0xff,0xdb,0xb7,0xf3,
+0xdf,0x7c,0xf8,0x74,0xff,0xff,0x6f,0x7d,0x3f,0x7e,0xec,0x7f,0xc1,0xf5,0xff,0xcf,
+0x6f,0x9f,0xf9,0xdf,0xbe,0xe5,0xe7,0xff,0xd7,0xf3,0xdd,0xfb,0xff,0xfc,0xff,0xbf,
+0xff,0xf0,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xf0,0x2f,0xff,0xff,0xff,0xfe,0xd7,0xff,0xff,0xff,0xb4,0xcf,0xef,0x77,0x6f,0x73,
+0x3a,0x4a,0x3a,0xcb,0xd4,0xf7,0x2e,0xd6,0xbd,0xbd,0xa1,0x3b,0xdf,0xd6,0xf7,0xee,
+0xd3,0x35,0xbd,0xfb,0xbd,0xce,0xeb,0x2b,0x4d,0x2f,0xbb,0xda,0xff,0xff,0xfe,0xb0,
+0x5f,0xff,0xff,0xff,0xfe,0xbf,0xff,0xff,0xff,0xdf,0x5f,0x36,0xaf,0x3f,0xed,0xb7,
+0xf5,0xfd,0xf3,0x2b,0xef,0x77,0xff,0xfb,0xda,0xb1,0xbd,0xa3,0x77,0x69,0x7f,0x4f,
+0xff,0xdb,0xfa,0x5b,0xff,0xf2,0xfe,0xff,0x96,0xff,0xff,0xfe,0xdf,0xff,0xd0,0xaf,
+0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfd,0x8f,0xfd,0x40,0x6f,0x9e,0x83,0x5a,0x0f,
+0xfa,0xc3,0xff,0xff,0xfc,0xe9,0x7f,0xf3,0x01,0xd0,0x00,0xfe,0xbf,0xcd,0x3f,0xf0,
+0xef,0xfc,0xc5,0x0c,0x3f,0xfd,0x68,0x0b,0xff,0xff,0xff,0xfe,0xdf,0xf0,0xff,0xff,
+0xff,0xff,0xfe,0xff,0xbb,0xff,0xfd,0x85,0xff,0xd4,0x6f,0x9f,0xc3,0x5a,0x0f,0xff,
+0xff,0xff,0xff,0xfc,0xe9,0x7f,0xf3,0x01,0xf0,0xfb,0xc2,0xbf,0xfc,0x00,0x37,0xef,
+0xfc,0xcd,0xbc,0x3f,0xff,0x0c,0xbf,0xff,0xff,0xff,0xff,0xff,0xf0,0x5f,0xff,0xff,
+0xff,0xfe,0xff,0xff,0xff,0xff,0xd9,0xf7,0xd1,0xb7,0x7e,0x7f,0xf1,0xe4,0xfd,0xff,
+0xfb,0xfb,0xff,0x5f,0xff,0x7f,0xb1,0xbc,0x0f,0x67,0xeb,0xb8,0x3f,0xff,0xe2,0xff,
+0xe9,0xff,0xfd,0xe3,0xff,0x3f,0x9f,0xc2,0xff,0xff,0xff,0xf0,0x9f,0xff,0xff,0xff,
+0xfe,0xf5,0x7f,0xff,0xf0,0x3f,0xbc,0xff,0xd5,0xf5,0xce,0x3f,0xfe,0xff,0xfe,0x6d,
+0xff,0xf1,0xbf,0x7b,0xff,0xf1,0xfd,0xff,0x4f,0xff,0x87,0xff,0xae,0xff,0xb1,0xf8,
+0xfe,0xff,0xff,0x78,0x01,0xb9,0xff,0xff,0xff,0xfa,0xf0,0x2f,0xff,0xff,0xff,0xfe,
+0xf3,0xc0,0x00,0x00,0x00,0x04,0x02,0x13,0x02,0x00,0x80,0x40,0x00,0x90,0x10,0x00,
+0x10,0x00,0x02,0x00,0x01,0x20,0x80,0x12,0x10,0x00,0x40,0x08,0x00,0x04,0x00,0x00,
+0x02,0x00,0x01,0x40,0x00,0x80,0x00,0x00,0x3c,0xf0,0xef,0xff,0xff,0xff,0xfe,0xfd,
+0x1f,0xff,0xff,0xff,0x7f,0xff,0xff,0xff,0xff,0x7f,0xff,0x7f,0xf7,0xdf,0xf7,0xff,
+0xf7,0xfb,0xeb,0xd1,0xff,0xff,0xff,0xff,0xef,0xf7,0xff,0xff,0xfb,0xff,0xfe,0xff,
+0xff,0x7e,0xff,0xfb,0xff,0xff,0xff,0xdb,0xf0,0xff,0xff,0xff,0xff,0xfe,0xf0,0xff,
+0xff,0xb7,0xeb,0xf7,0xdf,0xff,0xfe,0xf5,0x6b,0xe7,0xed,0xf7,0x3e,0xec,0xff,0x54,
+0xef,0x6f,0xf1,0xf5,0xaf,0x6f,0xf6,0xfd,0xff,0xdd,0x7b,0xff,0xef,0xbf,0x7f,0xff,
+0xff,0xf7,0xff,0xf3,0x5f,0xf7,0xd0,0xf0,0xcf,0xff,0xff,0xff,0xfe,0xf8,0x30,0x00,
+0x80,0x40,0x04,0x00,0x81,0x2c,0x04,0x24,0x00,0x02,0x01,0xc8,0x02,0x00,0x02,0x24,
+0x00,0x01,0xb4,0x42,0xdc,0x44,0x02,0x15,0x90,0x02,0x03,0x48,0x39,0x10,0x02,0x24,
+0xa0,0xba,0x00,0x00,0x40,0xc1,0xf0,0xbf,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,
+0xfe,0xfc,0xf7,0xf0,0xee,0xb6,0x5d,0xfd,0xf5,0xff,0xdb,0xf7,0x7f,0x7f,0xbe,0xff,
+0xc1,0xfe,0xbf,0xfa,0xfa,0x5f,0xff,0xad,0xff,0xef,0xff,0x7f,0xdf,0x7f,0xfe,0xbf,
+0xb7,0x94,0xbf,0xff,0xff,0xf0,0x9f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf1,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xf0,0x8f,0xff,0xff,0xff,0xfe,0xd7,0xff,0xff,0xfb,0xb5,0xff,
+0xef,0x7c,0xeb,0x2b,0x52,0x5b,0x3b,0xda,0xd4,0xf3,0x36,0x96,0xb5,0xbd,0xf1,0xfb,
+0xda,0xee,0xf6,0xfe,0xd3,0x35,0xbd,0xdf,0xad,0xcf,0xef,0x7e,0xcd,0x6b,0xbb,0xdf,
+0xff,0xff,0xfd,0xb0,0xef,0xff,0xff,0xff,0xfe,0xbf,0xff,0xff,0xff,0xd3,0x5f,0xf6,
+0xff,0xf6,0xff,0xfd,0xad,0xfd,0xff,0x7f,0xef,0xff,0x6f,0x7f,0xdb,0xf1,0xa5,0xa3,
+0x7f,0x6f,0x6b,0x4f,0xff,0xdb,0xfb,0xcb,0xff,0xf6,0xff,0xf4,0xd7,0xfd,0xbf,0xfe,
+0xdf,0xff,0xd0,0xcf,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xf7,0xdf,0xff,0xff,0xff,
+0x3f,0x7f,0xfc,0xe5,0xff,0x20,0xfe,0xff,0xff,0xdf,0x7f,0xff,0xf1,0x7f,0xff,0xfe,
+0xff,0xf0,0x7c,0x3d,0x4f,0xf3,0xc3,0x3f,0xff,0xff,0x6f,0xc3,0xff,0x0f,0xff,0xff,
+0xaf,0xf0,0x2f,0xff,0xff,0xff,0xfe,0xff,0xff,0xfb,0xb7,0xe0,0x0f,0xff,0xff,0x2b,
+0xff,0x7d,0xbf,0xff,0xdf,0xff,0xff,0xf8,0x9f,0x7f,0xff,0xf1,0x55,0xff,0xff,0xff,
+0xfd,0x7c,0x3c,0xff,0xf3,0xc3,0x3f,0xff,0xff,0xef,0xc3,0xff,0xdf,0xff,0xff,0xff,
+0xf0,0x9f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xef,0xff,0xff,0x9f,0xbf,0x7f,
+0xf9,0x19,0x47,0x8e,0xe7,0x9f,0x3f,0x17,0xff,0xfc,0x81,0xc1,0x7e,0xf3,0xd9,0xf9,
+0x73,0xdf,0xf4,0x7f,0xfa,0xff,0xff,0xff,0xfb,0x7f,0x77,0xc7,0xff,0xff,0xff,0xf0,
+0x2f,0xff,0xff,0xff,0xfe,0xf5,0xf7,0xff,0xfb,0xff,0xf7,0x3f,0xfc,0xbf,0x3e,0x3f,
+0xec,0xff,0x81,0xaf,0xfe,0x4f,0xf3,0xbb,0xff,0xf0,0x7e,0xff,0x6f,0xff,0x87,0xff,
+0xbb,0xff,0xd5,0xfc,0xff,0x7f,0xfc,0x6f,0xff,0xef,0xe7,0xff,0xff,0xfa,0xf0,0x3f,
+0xff,0xff,0xff,0xfe,0xf3,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
+0x00,0x30,0x10,0x60,0x20,0x00,0x08,0x00,0x01,0x20,0x80,0x00,0x10,0x00,0x04,0x00,
+0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x80,0x40,0x00,0x08,0x20,0x3c,0xf0,0x6f,0xff,
+0xff,0xff,0xfe,0xf5,0xbf,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0x7f,0xfe,0x3f,0xff,
+0xff,0xff,0xff,0xff,0xef,0xff,0xff,0xf1,0xdf,0xdf,0xff,0xff,0xff,0x7f,0xdf,0xff,
+0xfd,0xbd,0xff,0xff,0xff,0xfb,0xdf,0xff,0xff,0xff,0xff,0x5b,0xf0,0xff,0xff,0xff,
+0xff,0xfe,0xf0,0xbf,0xbf,0xbf,0xff,0xf7,0xfb,0xff,0xfe,0xee,0xfa,0xff,0xff,0xff,
+0x3d,0x3b,0xff,0xff,0xfe,0xfb,0xf1,0xff,0xbf,0x7b,0xff,0xff,0xef,0xff,0xbf,0xff,
+0xff,0xff,0xff,0xff,0xfe,0xff,0xf7,0xef,0xff,0xfb,0xd0,0xf0,0xdf,0xff,0xff,0xff,
+0xfe,0xf8,0x30,0x00,0x00,0x00,0x00,0x00,0x0b,0x10,0x05,0x01,0x00,0x08,0x00,0x02,
+0x01,0x01,0x00,0x00,0x10,0x01,0xc8,0x08,0x00,0x00,0x00,0x00,0x42,0x02,0x00,0x00,
+0x00,0x80,0x02,0x00,0x00,0x40,0x24,0x80,0x00,0xc1,0xf0,0x3f,0xff,0xff,0xff,0xfe,
+0xff,0xff,0xff,0xff,0xf7,0xfd,0xf7,0xfa,0xef,0xee,0xf9,0xfd,0xff,0xf7,0xfe,0xbf,
+0x1f,0xfd,0x9e,0xfd,0xd1,0xef,0xff,0xf7,0x7f,0x9f,0xff,0xef,0xff,0xf6,0xff,0xfe,
+0xfe,0x7b,0xff,0xbd,0xff,0x7e,0xff,0xff,0xff,0xf0,0x3f,0xff,0xff,0xff,0xfe,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0xaf,0xff,0xff,0xff,0xfe,0xff,0xff,
+0xff,0xf7,0xff,0xff,0xff,0xff,0x7f,0xff,0xff,0xff,0xdf,0xfd,0xff,0xff,0xdf,0xff,
+0xff,0x5f,0xf1,0xbf,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0xdf,0xff,0xff,0xff,0xfe,0xff,0xef,0xff,
+0xf7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f,0xfb,0xff,0xff,0xef,0xfb,0xfd,
+0xff,0xf1,0xff,0xff,0xfb,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x2f,0xff,0xff,0xff,0xfe,0xf7,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xfd,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,0xff,0xff,0xe7,0xff,
+0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xf0,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xcf,0xff,0xfb,0xff,0xfb,0xf1,
+0xff,0xff,0xfb,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xf0,0x2f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7b,0xff,0xff,0xff,0x7f,0xff,0xf1,0xff,
+0xff,0xff,0xdf,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xf0,0x7f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xef,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0x57,0xff,0xfe,0xbf,0xfb,0xf1,0xff,0xff,
+0xfd,0xf7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xd7,0xff,0xf0,0x7f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xf7,0xdb,0xff,0xdb,0xfd,
+0xf6,0xff,0xf6,0xff,0x3c,0xbc,0xbc,0xbf,0xdf,0x6f,0xef,0x2f,0xf1,0x3c,0xbf,0xbc,
+0xbf,0xdf,0x6f,0xff,0x6f,0xf7,0xdb,0xff,0xdb,0xfd,0xf6,0xff,0xf6,0xff,0xff,0xff,
+0x01,0xe2,0xef,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff };
diff --git a/drivers/net/hamradio/yam9600.h b/drivers/net/hamradio/yam9600.h
new file mode 100644
index 0000000..5ed1fe6
--- /dev/null
+++ b/drivers/net/hamradio/yam9600.h
@@ -0,0 +1,343 @@
+/*
+ *
+ * File yam111.mcs converted to h format by mcs2h
+ *
+ * (C) F6FBB 1998
+ *
+ * Tue Aug 25 20:23:03 1998
+ *
+ */
+
+static unsigned char bits_9600[]= {
+0xff,0xf2,0x00,0xa5,0xad,0xff,0xfe,0x9f,0xff,0xef,0xfb,0xcb,0xff,0xdb,0xfe,0xf2,
+0xff,0xf6,0xff,0x9c,0xbf,0xfd,0xbf,0xef,0x2e,0x3f,0x6f,0xf1,0xfd,0xb4,0xfd,0xbf,
+0xff,0x6f,0xff,0x6f,0xff,0x0b,0xff,0xdb,0xff,0xf2,0xff,0xf6,0xff,0xff,0xff,0xff,
+0xf0,0x6f,0xff,0xff,0xff,0xfe,0xff,0xfd,0xdf,0xff,0xff,0xff,0xf7,0xff,0xff,0xff,
+0xfb,0xff,0xff,0xf7,0xff,0xff,0xff,0xfe,0xff,0x7f,0xf1,0xff,0xfe,0xff,0xbf,0xbf,
+0xff,0xff,0xff,0xff,0xff,0xf7,0xff,0xff,0xff,0xfe,0xff,0xfe,0xff,0xff,0xff,0xf0,
+0xef,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xbf,0xff,0xff,0xff,0xf7,
+0xff,0xff,0xf7,0xef,0xff,0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0x7e,0xff,0xff,
+0xff,0xff,0xff,0xff,0xdf,0xff,0xff,0xff,0xff,0xff,0xfd,0xff,0xff,0xff,0xf0,0xdf,
+0xff,0xff,0xff,0xfe,0xff,0xff,0xdf,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xef,0xff,0xf3,0xfb,0xfe,0xff,0xf1,0xff,0xfd,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xdf,0xff,0xf0,0x7f,0xff,
+0xff,0xff,0xfe,0xff,0xff,0xef,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xdf,0xff,0xff,0xff,0xf7,0xf1,0xff,0xff,0xff,0xdf,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xf0,0x0f,0xff,0xff,
+0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf5,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x2f,0xff,0xff,0xff,
+0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xef,0xff,0x7f,0xff,0xef,
+0xff,0xef,0xff,0x7f,0xef,0xf1,0xff,0xef,0xff,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xf0,0x9f,0xff,0xff,0xff,0xfe,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0xbf,0xff,0xff,0xff,0xfe,0xff,
+0xff,0xff,0xff,0xbd,0xff,0xef,0x7f,0xef,0x7f,0xfb,0xdf,0xd3,0x5a,0xfe,0xd7,0xd6,
+0xf7,0x7f,0xbd,0xf1,0xbb,0x5d,0xd6,0xf7,0xfe,0x96,0xff,0xbd,0xaf,0xad,0xbf,0xef,
+0x7f,0x6b,0x7f,0xfb,0xd6,0xfe,0xf7,0xff,0x10,0xef,0xff,0xff,0xff,0xfe,0xbe,0xef,
+0xff,0xff,0xdb,0xff,0xf6,0xff,0xf6,0xff,0xfd,0xbf,0xfd,0xbf,0xff,0x7f,0xff,0x7f,
+0xdf,0xdb,0xf1,0xfd,0x35,0xff,0x6f,0xff,0x6f,0xff,0xdb,0xff,0xcb,0xff,0xf6,0xff,
+0xf2,0xfd,0xfd,0xbf,0xff,0xff,0xff,0xd0,0xef,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x55,0xff,0xcc,0xc0,0x3f,0xff,
+0xff,0xf1,0x24,0xf0,0xff,0xff,0xcf,0xef,0x3f,0xff,0xf0,0xff,0xff,0xff,0xfc,0x3f,
+0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0xcf,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x55,0xff,0xcc,0xc0,0x3f,0xff,0xff,
+0xf1,0x00,0xf0,0xff,0xff,0xcf,0xdf,0xff,0xff,0xf0,0xff,0xff,0xff,0xfc,0x3f,0xff,
+0xff,0xff,0x7d,0xff,0xff,0xf0,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0xfe,0x7f,0xdf,0xff,0xff,0xff,0xf1,
+0xff,0xcf,0xff,0xf3,0xff,0x97,0xff,0xff,0x8f,0xe7,0xff,0xff,0xfc,0x71,0xff,0xff,
+0xff,0xff,0xff,0xff,0xf0,0xef,0xff,0xff,0xff,0xfe,0xf5,0xff,0xbf,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xe3,0xf7,0xef,0xff,0xff,0xfc,0x7b,0xff,0xf1,0x3f,
+0xff,0xef,0xff,0xcf,0xe3,0xe3,0xff,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xbf,0xff,
+0xbf,0xff,0xda,0xf0,0x7f,0xff,0xff,0xff,0xfe,0xf2,0xc0,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,
+0x01,0x3c,0xf0,0xaf,0xff,0xff,0xff,0xfe,0xfd,0xbf,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xdb,0xff,0xff,0xff,0xff,0xff,0xff,0xf1,0xff,0x9f,0xff,
+0xff,0xff,0xf7,0xff,0xef,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xdb,0xf0,0x7f,0xff,0xff,0xff,0xfe,0xf0,0xbb,0xdf,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xfb,0xdf,0xbf,0xf1,0xfe,0xfd,0xf7,0xff,
+0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x77,0xfd,0xf2,
+0xf0,0x1f,0xff,0xff,0xff,0xfe,0xf8,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,
+0x00,0x00,0x00,0x02,0x00,0x90,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x04,0x24,0x00,
+0x40,0x01,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x01,0xc0,0xf0,
+0x4f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xbf,0xff,0xff,0x6f,0xff,0xdf,0xff,0xd1,0xff,0xfe,0xff,0xff,0xff,0xff,
+0xff,0xff,0xdf,0xff,0xfb,0xff,0xfb,0xef,0xff,0xff,0xee,0xff,0xff,0x7f,0xf0,0xdf,
+0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x8f,0xff,
+0xff,0xff,0xfe,0xff,0xff,0xff,0xf5,0xad,0xff,0x69,0x2a,0xed,0x6b,0xfb,0xdf,0x3a,
+0xdc,0xf4,0x96,0xee,0xb3,0x3d,0x35,0xc1,0xbb,0xdd,0xfe,0xf6,0xfe,0xd6,0xb5,0xad,
+0xbf,0xa5,0xad,0x49,0x2f,0x4f,0x2b,0xda,0x5f,0xff,0xff,0xff,0xf0,0x2f,0xff,0xff,
+0xff,0xfe,0xbf,0xff,0xff,0xfb,0x5b,0xf7,0xf6,0xff,0xf6,0xff,0xfd,0xbf,0xfd,0xa5,
+0xf3,0x6f,0xf3,0x6e,0xfa,0x7b,0xd1,0xfd,0xb5,0x77,0x6f,0xe9,0x6f,0xff,0xdb,0xfb,
+0xdb,0xdf,0xf6,0xff,0xf6,0xff,0xfd,0x3f,0xfe,0xf7,0xff,0xd0,0x4f,0xff,0xff,0xff,
+0xfe,0xff,0x9f,0xff,0xff,0x0f,0xff,0xc0,0x3f,0x9c,0x03,0xff,0xff,0x8b,0xa5,0xfe,
+0x80,0x3e,0xc2,0xbf,0xac,0xb1,0x24,0xff,0xff,0xff,0xff,0xff,0xff,0x0f,0xff,0xa3,
+0xff,0xfd,0x6b,0xff,0xff,0xf0,0xa5,0xff,0xff,0xff,0xf0,0xaf,0xff,0xff,0xff,0xfe,
+0xff,0xff,0xff,0xff,0x0f,0xff,0xc0,0x3f,0xd4,0x6b,0xff,0xff,0xdb,0xff,0xfe,0x86,
+0xbf,0xc2,0xbf,0x30,0xa1,0x24,0xff,0xff,0xff,0xff,0xcc,0xff,0x0f,0xff,0xa3,0xff,
+0x05,0x6b,0xff,0xff,0xf0,0xa5,0xff,0xff,0xff,0xf0,0x7f,0xff,0xff,0xff,0xfe,0xff,
+0xff,0xff,0xfb,0xc7,0xff,0xc4,0xff,0xff,0x7f,0xff,0xec,0xfe,0x7f,0xdf,0xd8,0xb9,
+0x47,0xfc,0x36,0xc1,0xdf,0xff,0xff,0xf9,0xff,0xf3,0xff,0xf7,0xff,0xfc,0xff,0xfd,
+0x3f,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xf0,0x7f,0xff,0xff,0xff,0xfe,0xf5,0xff,
+0xff,0xff,0xff,0xfe,0xff,0xff,0x7e,0xbd,0x3f,0xff,0x2b,0xfe,0x2f,0xf5,0xa3,0xfc,
+0x5b,0xfe,0x61,0x9f,0x7f,0xef,0xff,0xff,0xa7,0xfb,0xff,0xff,0xfa,0xfe,0xff,0x33,
+0xf1,0xff,0xbf,0xff,0xff,0xff,0xfa,0xf0,0x7f,0xff,0xff,0xff,0xfe,0xf1,0xc0,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x30,0x24,0x04,
+0x00,0x01,0x00,0x80,0x40,0x00,0x08,0x00,0x00,0x00,0x02,0x01,0x01,0x00,0x02,0x00,
+0x00,0x00,0x00,0x00,0x01,0x3d,0xf0,0x2f,0xff,0xff,0xff,0xfe,0xfd,0xbd,0xff,0xfd,
+0xff,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0x7f,0xf6,0xef,0xbf,0xf7,0xff,0x73,0xeb,
+0xf1,0xff,0xff,0xff,0xdf,0xff,0xff,0xff,0xff,0xff,0xf9,0xff,0xfd,0xfe,0xff,0xff,
+0xff,0xff,0xff,0xff,0xd9,0xf0,0xdf,0xff,0xff,0xff,0xfe,0xf0,0xbf,0x7f,0xff,0xff,
+0xff,0x7f,0xff,0xff,0xde,0xff,0xff,0xef,0xdd,0xde,0x77,0xf2,0xfb,0xed,0xe7,0xf1,
+0x73,0xfd,0xfd,0xdf,0xff,0x7d,0xbe,0xdf,0xff,0xfb,0xff,0xef,0xff,0xef,0xff,0xff,
+0xff,0xff,0xff,0xd0,0xf0,0xbf,0xff,0xff,0xff,0xfe,0xf8,0x30,0x20,0x02,0x00,0x22,
+0x40,0xc0,0x00,0x00,0x00,0x08,0x00,0x02,0x41,0x02,0x12,0x00,0x21,0x87,0x81,0x00,
+0x00,0x80,0x04,0x0b,0x28,0x01,0xb0,0x00,0x82,0x00,0x40,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0xc1,0xf0,0xdf,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xfd,0xff,
+0xf7,0xff,0xfe,0x7f,0xed,0x79,0xff,0xde,0xeb,0x7f,0x74,0xf7,0xf7,0xe1,0xf9,0xff,
+0xf6,0x5f,0x7f,0xff,0xff,0xff,0xd7,0xdb,0xef,0xff,0xbb,0xff,0xff,0xff,0xcc,0xff,
+0xff,0xff,0xf0,0xcf,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xf0,0x0f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfd,0x3d,0xcd,0x49,0x7f,0x6f,
+0x2b,0xba,0x5c,0xd2,0xda,0xf6,0xf3,0x3e,0xf7,0xff,0xbd,0xf1,0xfa,0xdf,0xfe,0xf7,
+0xcc,0xf6,0xbb,0xa5,0xb3,0xad,0xbf,0x6f,0x7d,0x6f,0x6b,0xdb,0xdf,0xbd,0xff,0xfe,
+0xb0,0x5f,0xff,0xff,0xff,0xfe,0xbf,0xff,0xff,0xfb,0xdb,0x57,0xf6,0xfe,0x9f,0xd5,
+0xb7,0xff,0xaf,0xe5,0x3f,0xff,0xff,0x6f,0xff,0xdb,0xf1,0xfd,0xbf,0xff,0x6f,0x69,
+0x6c,0xdf,0xda,0xdf,0xcb,0xff,0xf6,0xff,0x76,0xfd,0xfd,0xbf,0xff,0xff,0xff,0xd0,
+0x3f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xfd,0xbd,0x08,0x03,0x89,0x4f,0x5a,
+0x0f,0xf0,0xff,0xf8,0xbf,0xff,0xff,0xff,0xff,0xf1,0x5a,0xff,0xff,0xff,0xff,0xf3,
+0xfa,0xa0,0xf0,0xf2,0xbf,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0xff,
+0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xfc,0xfd,0x00,0x6b,0xff,0xff,0x5a,0x0f,
+0xf0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf1,0x5a,0xff,0xff,0xff,0xff,0xb3,0xf5,
+0x50,0xf0,0xf0,0xff,0xff,0xff,0xd7,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x7f,0xff,
+0xff,0xff,0xfe,0xff,0xff,0xff,0xfd,0xbc,0xff,0xe4,0xe7,0x71,0xff,0xf9,0xc4,0xf4,
+0x7f,0x7f,0xcf,0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xfb,0xf7,0x73,0xbf,0x14,
+0xff,0xe6,0xff,0xff,0xe1,0x7d,0xff,0xff,0xe7,0xff,0xff,0xff,0xf0,0x3f,0xff,0xff,
+0xff,0xfe,0xf5,0xff,0xff,0xfe,0xd2,0xfa,0xff,0xc4,0xf4,0x5c,0xbf,0xfa,0xff,0xff,
+0xec,0x7e,0xbf,0xff,0xff,0xff,0xf1,0xff,0xff,0xef,0xff,0xff,0x6b,0xdb,0xff,0xdf,
+0xf9,0xfb,0xbf,0xff,0xf1,0xff,0xbf,0xff,0xff,0xff,0xfb,0xf0,0xbf,0xff,0xff,0xff,
+0xfe,0xf3,0xc0,0x00,0x02,0x00,0x00,0x00,0x00,0x82,0x00,0x00,0x00,0x00,0x80,0x00,
+0x00,0x00,0x00,0x40,0x00,0x01,0x00,0x00,0x00,0x01,0x08,0x20,0x00,0x00,0x00,0x00,
+0x01,0x00,0x01,0x00,0x00,0x80,0x02,0x00,0x01,0x3c,0xf0,0x5f,0xff,0xff,0xff,0xfe,
+0xfd,0xbf,0xff,0xff,0xff,0xdf,0xff,0xff,0xff,0xff,0x7f,0xff,0xdf,0xff,0xef,0xff,
+0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xf7,0xff,0xfb,0xff,0xfd,0xff,
+0xff,0xff,0xff,0xff,0xff,0xfd,0xff,0xff,0xc3,0xf0,0xaf,0xff,0xff,0xff,0xfe,0xf0,
+0xff,0xdf,0xff,0xff,0xf7,0x23,0xff,0xff,0xfd,0xff,0xef,0xff,0xfe,0x7f,0x7d,0xf7,
+0xfe,0xff,0x7f,0x71,0xff,0xfb,0x7f,0xff,0xff,0xff,0x6e,0xfd,0xf7,0xfd,0xff,0xbf,
+0xff,0xbf,0xf9,0xfd,0xff,0xdf,0xef,0xf0,0xf0,0xaf,0xff,0xff,0xff,0xfe,0xf8,0x30,
+0x40,0x01,0x00,0x83,0x00,0x00,0x00,0x0c,0x06,0x08,0x04,0x26,0x26,0x00,0x00,0x06,
+0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x04,0x00,0x70,0x08,0x80,0x00,0x20,0x01,0x20,
+0x00,0x02,0x00,0x30,0x00,0x00,0xc1,0xf0,0x5f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,
+0xff,0xff,0x7b,0x3f,0xf7,0xff,0xd7,0xfe,0xfe,0xfb,0xfe,0x3b,0xfe,0xbd,0xff,0x2f,
+0xff,0x71,0xff,0xfb,0x7f,0xe7,0xff,0xf9,0xef,0xff,0xd7,0xfa,0xff,0xb7,0xbb,0xfe,
+0xff,0xff,0x74,0xff,0xf7,0xff,0xf0,0xcf,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xf0,0x8f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xb5,
+0xbd,0x6f,0x7c,0xeb,0x7f,0xfb,0xdb,0xd3,0x4b,0xee,0xd6,0xf6,0xb7,0xfd,0xac,0xa1,
+0xfb,0xdf,0xfe,0xf7,0xf4,0x96,0xbd,0xb4,0xc5,0xa5,0xaf,0x6f,0x69,0x4f,0x7f,0xba,
+0xdb,0xff,0xff,0xff,0xf0,0x3f,0xff,0xff,0xff,0xfe,0xbf,0xff,0xff,0xff,0xdb,0xff,
+0xf6,0xff,0xf6,0xff,0xbd,0xbf,0xa5,0xbf,0xff,0x7d,0x7f,0xef,0xff,0xfb,0xf1,0xfd,
+0xbf,0xff,0x6f,0xff,0x6b,0x7a,0xdb,0xff,0xdb,0xdf,0xf6,0xfe,0xb6,0xfd,0xfd,0xbf,
+0xfe,0xf7,0xff,0xd0,0xef,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xf4,0x2f,0xff,
+0xfc,0x43,0x6b,0xff,0xff,0xff,0x0d,0xff,0xfc,0x33,0x3f,0xf0,0x5f,0xf1,0xff,0xff,
+0xff,0xff,0xf9,0xde,0xf0,0x4c,0xfe,0x77,0xaf,0xff,0xff,0xef,0xff,0xf0,0xff,0xdb,
+0xff,0x5f,0xf0,0xef,0xff,0xff,0xff,0xfe,0xff,0xfe,0xf7,0xff,0xf0,0x2f,0xff,0xfd,
+0x43,0x7f,0xff,0xff,0xf1,0x0f,0xff,0xfc,0x33,0x3f,0xff,0xaf,0xf1,0xff,0xff,0xff,
+0xff,0xf6,0xd7,0xff,0xbc,0xfd,0xbd,0xff,0xff,0xff,0xff,0xff,0xf0,0xff,0xff,0xff,
+0xff,0xf0,0xef,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xfc,0xff,0xff,0xfb,0xf1,
+0xbf,0xff,0xf9,0xfd,0xcf,0xf2,0x70,0xff,0x1f,0x9f,0xf3,0xf1,0xff,0xff,0xff,0xff,
+0xfc,0xf7,0xff,0x13,0x9f,0xfc,0xff,0xff,0x84,0xf7,0xff,0xff,0x47,0xff,0xff,0xff,
+0xf0,0xbf,0xff,0xff,0xff,0xfe,0xf5,0xff,0xff,0xff,0xf1,0xfc,0xff,0xfe,0xfe,0x79,
+0x3f,0xff,0x1d,0x46,0xcf,0xff,0xcf,0xfc,0x7b,0xff,0xf1,0xff,0xff,0xff,0xff,0xed,
+0xf3,0xab,0xff,0xcb,0xff,0xf8,0xff,0xfc,0xf5,0xff,0xbf,0xff,0xff,0xff,0xfa,0xf0,
+0x8f,0xff,0xff,0xff,0xfe,0xf3,0xc2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,
+0x00,0x00,0x20,0x00,0x20,0x00,0x00,0x04,0x08,0x01,0x00,0x00,0x00,0x00,0x00,0x20,
+0x0c,0x00,0x00,0x04,0x01,0x00,0x01,0x00,0x00,0x80,0x00,0x00,0x01,0x3c,0xf0,0x7f,
+0xff,0xff,0xff,0xfe,0xfd,0xbf,0xff,0xff,0xfd,0xfe,0xff,0xff,0xff,0xff,0xfe,0xff,
+0xdf,0xff,0xff,0xf7,0xff,0xff,0xff,0xef,0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xeb,
+0xff,0xdf,0xff,0xff,0xfb,0xf7,0x7f,0xff,0xfe,0xff,0xff,0xbf,0xdb,0xf0,0xff,0xff,
+0xff,0xff,0xfe,0xf0,0xff,0xff,0xff,0xff,0xff,0xdf,0xff,0xff,0xff,0x7f,0xf7,0xff,
+0xbf,0xbf,0xcf,0xff,0xff,0xff,0x3e,0xf1,0x7f,0xff,0xff,0xef,0xff,0xff,0xff,0xfe,
+0xff,0xfd,0xff,0xbf,0xbd,0xfe,0xff,0xfb,0xf7,0xdf,0xfb,0xd0,0xf0,0x9f,0xff,0xff,
+0xff,0xfe,0xf8,0x30,0x20,0x00,0x40,0x01,0x80,0xc0,0x30,0x00,0x00,0x20,0x00,0x10,
+0x50,0x88,0x20,0x00,0x00,0x13,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,
+0x00,0x00,0x01,0x80,0x08,0x00,0x00,0xa0,0x00,0x10,0xc1,0xf0,0xef,0xff,0xff,0xff,
+0xfe,0xfd,0xef,0x7f,0xff,0xff,0xbf,0xff,0xf7,0xff,0xef,0xfb,0xfd,0x77,0xef,0xbf,
+0xf7,0x7f,0xff,0xff,0xbf,0xd1,0x7f,0xff,0xff,0xf7,0xff,0xff,0xff,0xff,0xaf,0xff,
+0xdf,0xf7,0xfb,0xff,0xfd,0xff,0xfc,0xff,0xfd,0xff,0xf0,0xff,0xff,0xff,0xff,0xfe,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x5f,0xff,0xff,0xff,0xfe,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0xff,0xff,0xff,
+0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe0,0x3f,0xff,0xff,0xff,0xfe,0xdd,0xff,
+0xff,0xff,0xa5,0xfd,0x6f,0x7d,0x6d,0x7f,0x52,0xdf,0x5a,0x4b,0xee,0xb6,0xee,0xf2,
+0xbb,0xac,0xa1,0x5b,0x4d,0xd6,0xf7,0xfe,0xb2,0xbd,0x35,0xb5,0xb5,0xdd,0x6f,0x7f,
+0xe9,0x5f,0x52,0xdf,0xbd,0xff,0xff,0xf0,0xdf,0xff,0xff,0xff,0xfe,0xbf,0xff,0xff,
+0xff,0xdb,0xfe,0xf6,0xff,0xf6,0xff,0xfd,0xbf,0xfd,0xb5,0xbf,0xf9,0x7f,0x6f,0xff,
+0xdb,0xf1,0xfd,0xbf,0xff,0x6f,0xff,0x69,0x7f,0xdb,0xff,0xd3,0xff,0xf6,0xfe,0xf2,
+0xff,0xad,0xbf,0xff,0xff,0xff,0xd0,0xdf,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xf5,
+0x30,0x0f,0xff,0xff,0xfd,0x6b,0xca,0xff,0xf0,0x0f,0xd6,0xbf,0xcf,0x3f,0xff,0xff,
+0xf1,0xff,0xff,0xff,0xca,0xfe,0xbf,0xff,0xf0,0x05,0xaf,0x0f,0xff,0xfc,0xf0,0xcf,
+0xf0,0xff,0xff,0xff,0xff,0xf0,0xef,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xf5,0x30,
+0x0f,0xff,0xff,0xfc,0x3f,0xca,0xff,0x0f,0x0f,0xd6,0xbf,0xff,0xff,0xf5,0x5f,0xf1,
+0xff,0x8b,0xff,0xc3,0xff,0xff,0xff,0xff,0xff,0xff,0x0f,0xff,0xfc,0xf0,0xcf,0xf0,
+0xff,0xff,0xff,0xff,0xf0,0x3f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xcf,0xff,
+0xff,0xbf,0x9f,0x3f,0xfe,0xfc,0xff,0x4f,0xff,0xff,0xff,0xff,0xff,0xf7,0xf1,0xff,
+0xdf,0xfe,0x7e,0x3f,0x9f,0xf4,0xfc,0x7f,0xfc,0xff,0xff,0x3f,0xff,0x3f,0xfe,0x3f,
+0xff,0xff,0xff,0xf0,0x4f,0xff,0xff,0xff,0xfe,0xf5,0xff,0xff,0xfb,0xff,0xfe,0xff,
+0xff,0xff,0xff,0xbf,0xfb,0xff,0xf8,0xed,0xff,0x8f,0xff,0xbb,0xff,0xb1,0xf3,0xef,
+0x8f,0xf7,0xff,0xff,0xdb,0xff,0xff,0xff,0xef,0xbf,0xfd,0x79,0xbf,0xbf,0xff,0xff,
+0xff,0xfb,0xf0,0xdf,0xff,0xff,0xff,0xfe,0xf3,0xc0,0x00,0x00,0x00,0x04,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x04,0x08,0x08,0x01,0x01,0x00,0x90,
+0x00,0x00,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x00,0x01,
+0x3c,0xf0,0xdf,0xff,0xff,0xff,0xfe,0xfd,0xbf,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0x9f,0xff,0xaf,0xdf,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,
+0xbf,0xef,0xff,0xff,0xff,0xed,0xff,0xff,0xff,0xef,0xff,0xbf,0xff,0xff,0xff,0xc3,
+0xf0,0x3f,0xff,0xff,0xff,0xfe,0xf0,0xff,0xfd,0xff,0xff,0xff,0xfb,0xff,0xbb,0xff,
+0xff,0xff,0x7f,0xf6,0xff,0x7f,0xfb,0xfd,0xed,0xff,0xf1,0xff,0xfe,0x7f,0xff,0xff,
+0xff,0x5f,0xff,0xf7,0xff,0x7e,0xff,0xfd,0xff,0xef,0xff,0xff,0xff,0xef,0xf0,0xf0,
+0x8f,0xff,0xff,0xff,0xfe,0xf8,0x30,0x80,0x00,0x04,0x00,0x00,0x40,0x02,0x00,0x03,
+0x00,0x05,0x04,0x20,0x00,0x00,0x01,0xd0,0x00,0x81,0x00,0x20,0x04,0x04,0x00,0x00,
+0x81,0x04,0x08,0x80,0x10,0x00,0xc0,0x00,0x00,0x00,0x20,0x00,0x08,0xc1,0xf0,0x6f,
+0xff,0xff,0xff,0xfe,0xff,0xff,0x7f,0xff,0xff,0xff,0xff,0xf3,0xfd,0xff,0xed,0xfc,
+0xff,0xff,0x9f,0xfb,0xfd,0xff,0xff,0xff,0xf1,0xff,0xff,0x7f,0xfb,0x3e,0xff,0x9f,
+0xff,0xff,0xff,0xff,0xfd,0xf9,0xff,0xff,0xff,0xfd,0xff,0xff,0xff,0xf0,0x6f,0xff,
+0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0xcf,0xff,0xff,
+0xff,0xfe,0xff,0xff,0xff,0xfd,0xbd,0xff,0xef,0x7c,0xeb,0x7f,0xfb,0xdb,0xfa,0xdc,
+0xee,0xf7,0xf6,0xd7,0xf5,0x2d,0xa1,0xbb,0xdd,0xee,0xf7,0x54,0xf7,0xfb,0x2c,0xb5,
+0xb4,0xbd,0x6b,0x6f,0xef,0x6f,0xbb,0xdf,0xff,0xff,0xff,0xf0,0x1f,0xff,0xff,0xff,
+0xfe,0xbf,0xff,0xff,0xff,0xfb,0xff,0xf6,0xff,0xf6,0xff,0xfd,0xbf,0xff,0xbf,0xef,
+0x6f,0xff,0x6f,0xfa,0xdb,0xf1,0xc5,0xbd,0xf5,0x6f,0xff,0x6f,0xca,0xdb,0xff,0xdb,
+0xfb,0xf6,0x97,0xf6,0xff,0xfd,0xbf,0xfe,0xf7,0xff,0xd0,0x9f,0xff,0xff,0xff,0xfe,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x8b,0x7f,0xff,0xff,0xe7,0x63,0xff,0xff,
+0xff,0xfc,0x77,0xdf,0xf1,0xdb,0xff,0xd6,0xa8,0x3f,0xff,0xff,0x08,0x2f,0xf0,0xff,
+0xc3,0xff,0xeb,0xff,0xff,0xff,0xff,0xff,0x5f,0xf0,0xef,0xff,0xff,0xff,0xfe,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x8b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xfc,0xff,0xcf,0xf1,0xdb,0xff,0xd6,0xa8,0x3f,0xff,0xff,0x08,0x2f,0xf0,0xff,0xc3,
+0xff,0xeb,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x5f,0xff,0xff,0xff,0xfe,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xf5,0xbf,0xff,0xca,0xff,0x9f,0xff,0xfa,0xb9,0xe7,
+0x9f,0xf3,0x81,0xff,0xff,0xfc,0x73,0xd7,0xff,0xff,0x77,0xff,0xfd,0xff,0xfc,0xff,
+0xff,0xff,0xff,0xcf,0xff,0xff,0xff,0xf0,0x1f,0xff,0xff,0xff,0xfe,0xf5,0xff,0xff,
+0xff,0xf7,0xde,0xff,0xfe,0x7e,0xff,0xbf,0xff,0xbf,0xf1,0xb3,0xff,0xff,0xe3,0xfb,
+0xff,0xe1,0x1f,0x7f,0xff,0xf8,0x78,0xff,0xfb,0x1e,0xff,0xf7,0xfe,0xe7,0xff,0xff,
+0xff,0xbf,0xff,0xff,0xff,0xfa,0xf0,0x4f,0xff,0xff,0xff,0xfe,0xf3,0xc0,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x04,0x00,
+0x01,0x80,0x40,0x40,0x20,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,
+0x80,0x00,0x00,0x01,0x3c,0xf0,0xaf,0xff,0xff,0xff,0xfe,0xfd,0xbf,0xff,0xfb,0xff,
+0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xf7,0xf1,
+0xfd,0xff,0xff,0xff,0xdf,0xff,0xef,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,0xff,
+0xff,0xff,0xff,0xdb,0xf0,0x8f,0xff,0xff,0xff,0xfe,0xf0,0xff,0xdf,0xff,0xff,0x7f,
+0xff,0xff,0xff,0xbe,0xd7,0xff,0xed,0xbd,0x7e,0xbf,0xfe,0xf6,0x7f,0xbf,0x71,0xff,
+0xff,0xda,0xff,0xf9,0xff,0xbf,0x7f,0xfe,0xff,0x6f,0x7f,0xff,0xff,0xff,0xff,0xff,
+0x7f,0xff,0xd0,0xf0,0xcf,0xff,0xff,0xff,0xfe,0xf8,0x30,0x42,0x00,0x00,0x00,0x00,
+0x80,0xc1,0x00,0x00,0x90,0x00,0xc4,0x00,0x00,0x12,0x20,0x43,0x22,0x81,0x84,0x00,
+0x00,0x14,0x00,0x01,0x00,0x08,0x80,0x00,0x02,0x00,0x02,0x00,0x04,0x02,0x00,0x00,
+0x10,0xc1,0xf0,0x1f,0xff,0xff,0xff,0xfe,0xff,0xff,0xfd,0xff,0xff,0xdd,0xfe,0xff,
+0xb6,0x76,0xe5,0xbc,0xf9,0xf7,0xaf,0x5f,0xbf,0xfc,0xdf,0xcf,0xf1,0xff,0xef,0x79,
+0xff,0xbd,0xff,0xef,0xff,0xff,0xf7,0x6f,0x5f,0xff,0xff,0xfd,0xef,0xef,0xbf,0xff,
+0xff,0xf0,0x9f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xf0,0xff,0xff,0xff,0xff,0xfe,0xdb,0xff,0xff,0xfd,0x2d,0xff,0x69,0x2a,0xef,0x77,
+0xbb,0xdd,0x5a,0xdf,0xf6,0xf6,0xd6,0xf7,0x7d,0xbd,0xd1,0xb2,0x4a,0xd6,0xb2,0xbe,
+0x97,0xf5,0xbd,0xb3,0xad,0xff,0xef,0x7f,0x69,0x6b,0xfb,0xdf,0xff,0xff,0xff,0xf0,
+0x2f,0xff,0xff,0xff,0xfe,0xbf,0xff,0xff,0xff,0xdb,0xff,0xf6,0xfe,0x9f,0xd4,0xbf,
+0xed,0xaf,0xff,0x6b,0x6f,0xf7,0xff,0xdd,0xdb,0x31,0xfd,0xbf,0xff,0x6f,0x7f,0xff,
+0xff,0xdb,0xff,0xcb,0xdf,0xf6,0xff,0xf6,0xff,0xfd,0xbf,0xfe,0xf7,0xff,0xd0,0x8f,
+0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfd,0x1f,0xff,0x46,0x2f,0x9f,0xff,0xff,0xff,
+0xa5,0xff,0xff,0xff,0xdf,0xb7,0xff,0xff,0xf1,0xff,0xff,0xff,0xf7,0xe9,0x6a,0xbf,
+0xff,0xff,0xfd,0xff,0xff,0xfd,0x55,0x57,0xff,0xff,0xff,0xff,0xaf,0xf0,0x4f,0xff,
+0xff,0xff,0xfe,0xfe,0xdf,0xff,0xfd,0x1f,0xff,0x46,0x2f,0x9f,0xff,0xff,0xff,0xa5,
+0xff,0xff,0xff,0xc0,0x37,0xff,0xff,0xf1,0x99,0x8e,0xdc,0x7f,0xe9,0x6a,0xbf,0xff,
+0xf0,0x0f,0xff,0xff,0xfd,0x55,0x57,0xff,0xff,0xff,0xff,0xff,0xf0,0x0f,0xff,0xff,
+0xff,0xfe,0xff,0xff,0xff,0xff,0x07,0xff,0xc0,0xbe,0xff,0xff,0xcf,0xef,0x9f,0xff,
+0xff,0xfb,0xff,0xe7,0xff,0xff,0xa1,0xe3,0xce,0x3c,0x58,0x3f,0xf3,0xff,0xfd,0xef,
+0xf9,0xff,0xff,0xf7,0xf1,0x7f,0xff,0xcb,0xff,0xff,0xff,0xf0,0x2f,0xff,0xff,0xff,
+0xfe,0xf5,0x7f,0xff,0xf0,0xff,0xfe,0xff,0xc4,0x75,0xe7,0xb9,0xff,0xff,0xff,0xef,
+0xff,0xc7,0x37,0x3b,0xff,0xf0,0x13,0x9e,0x0f,0xf4,0xff,0xfe,0xfb,0xff,0xff,0xf9,
+0xfc,0xff,0xff,0xff,0xff,0xbf,0xff,0xff,0xff,0xfa,0xf0,0xef,0xff,0xff,0xff,0xfe,
+0xf3,0xc0,0x01,0x00,0x00,0x02,0x00,0x02,0x22,0x00,0x00,0xc0,0x40,0x00,0x40,0x00,
+0x04,0x08,0x04,0x0a,0x01,0x01,0x10,0x20,0x20,0x00,0x00,0x04,0x08,0x08,0x04,0x00,
+0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x01,0x3c,0xf0,0xcf,0xff,0xff,0xff,0xfe,0xfd,
+0x3f,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,0xff,0x7f,0xff,0x7f,0xff,0xcf,0x9d,0xff,
+0xff,0xf7,0xfd,0xf1,0xff,0xff,0xff,0xee,0xbf,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xf0,0x6f,0xff,0xff,0xff,0xfe,0xf0,0xff,
+0xff,0xff,0xf7,0xf7,0xff,0xff,0xfe,0xbf,0xf7,0xff,0xff,0x5b,0xff,0xbf,0xf7,0xff,
+0xfd,0x7f,0x71,0xfd,0xff,0xed,0xf7,0xfe,0xef,0xff,0xff,0x7f,0xff,0xff,0xff,0xff,
+0xff,0xff,0xef,0xff,0x7f,0xff,0xd0,0xf0,0xff,0xff,0xff,0xff,0xfe,0xf8,0x30,0x11,
+0x00,0x48,0x60,0x40,0x82,0x60,0x24,0x60,0x00,0xcc,0x00,0x80,0x04,0x01,0x00,0x00,
+0x14,0x01,0x0c,0x04,0x00,0x30,0x00,0x00,0x00,0x08,0x08,0x00,0x01,0x00,0xc2,0x00,
+0x00,0x02,0x00,0x80,0x00,0xc1,0xf0,0x5f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,
+0xf7,0x7b,0xff,0xf3,0xeb,0xbf,0xff,0xf7,0xff,0xff,0xff,0xe7,0x5d,0x3f,0xff,0xf6,
+0xd1,0xfd,0xff,0xeb,0xf7,0x3d,0xff,0xff,0xff,0x5f,0xff,0x7f,0x7f,0xf3,0xff,0xff,
+0xef,0xfd,0xbf,0xff,0xff,0xf0,0x5f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf1,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xf0,0xdf,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xf5,0xb5,0xdf,
+0x6f,0x7d,0x69,0x7f,0xfb,0xdf,0x52,0x5f,0xf6,0xf7,0xfe,0xf6,0xf3,0xbd,0xb1,0xda,
+0xcd,0xfe,0xf6,0xee,0xd2,0xbd,0xa5,0xaf,0xbd,0xff,0x6f,0x7c,0xeb,0x2b,0xfa,0xda,
+0xff,0xfe,0xdf,0xf0,0x4f,0xff,0xff,0xff,0xfe,0xbf,0xff,0xff,0xff,0xdb,0xff,0xf6,
+0xff,0xf6,0xff,0xbd,0xbf,0xcd,0xbf,0xeb,0x6f,0xf7,0x6f,0xdf,0xdb,0x51,0xfd,0xbd,
+0xff,0x6f,0xff,0x6f,0xfb,0x5b,0xff,0xdb,0xff,0xf6,0xfe,0xf6,0xfd,0xfd,0xbf,0xfe,
+0xf7,0xff,0xd0,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfa,0x50,0xff,0xff,0xff,
+0xf0,0x6f,0xff,0xff,0xf0,0x96,0xff,0xff,0xc6,0x2b,0xff,0xff,0xf1,0xfc,0xff,0xff,
+0xf7,0xdb,0xc3,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xc1,0x4f,0xc3,0xff,0xff,0xff,
+0xaf,0xf0,0x9f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xf5,0xa0,0xff,0xff,0xff,0xf0,
+0x6f,0xff,0xff,0xf0,0x96,0xff,0xff,0xc6,0x2b,0xff,0xff,0xf1,0x5a,0xff,0xff,0xff,
+0xf3,0xc3,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xc1,0x4f,0xc3,0xff,0xff,0xff,0xff,
+0xf0,0xcf,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xfc,0xff,0xff,0x9f,0xf0,0x7f,
+0xff,0xf9,0xfc,0x4f,0xf3,0xff,0x27,0xeb,0xff,0xfc,0x81,0xfc,0x7f,0xfe,0x7b,0xff,
+0xf7,0xff,0x12,0x7f,0xff,0xff,0xff,0xff,0x18,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,
+0x7f,0xff,0xff,0xff,0xfe,0xf5,0xff,0xff,0xff,0xdf,0xfe,0xff,0xfc,0x7e,0x7f,0xbf,
+0xff,0xff,0xaf,0xef,0xff,0xdf,0xdf,0xfb,0xff,0xf1,0xc3,0xfe,0x6f,0xf1,0xcf,0x3f,
+0xfb,0xff,0xff,0xcf,0xfe,0xff,0xff,0xfe,0x7f,0xbf,0xff,0xff,0xbf,0xfa,0xf0,0xdf,
+0xff,0xff,0xff,0xfe,0xf3,0xc0,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x01,0x00,0x00,
+0x20,0x00,0x01,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x02,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x02,0x00,0x00,0x80,0x00,0x02,0x80,0x00,0x02,0x3c,0xf0,0x2f,0xff,
+0xff,0xff,0xfe,0xfd,0xbf,0xff,0xfb,0xfd,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xf5,0xf1,0xff,0x7f,0xff,0xff,0xff,0xff,0xef,0xff,
+0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xf0,0x2f,0xff,0xff,
+0xff,0xfe,0xf0,0xff,0xff,0xff,0xfb,0xff,0xbf,0xff,0xff,0xff,0xff,0xf7,0xbf,0xfb,
+0xff,0xff,0xff,0xdf,0xf7,0xff,0xf1,0xf7,0xbf,0xfb,0xff,0xff,0xff,0x7f,0xde,0xff,
+0xff,0xff,0xff,0xff,0xff,0xed,0xf7,0xff,0xff,0x7f,0xd0,0xf0,0x3f,0xff,0xff,0xff,
+0xfe,0xf8,0x30,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0xe0,0x00,0x00,0x80,
+0x20,0x01,0x01,0x92,0x00,0x01,0x01,0x00,0xe0,0x1c,0x60,0x20,0x30,0x08,0x08,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0xc1,0xf0,0x6f,0xff,0xff,0xff,0xfe,
+0xff,0xff,0xff,0xff,0xff,0xdb,0xfe,0xff,0xff,0xdf,0xff,0xfc,0x7f,0xfb,0xbf,0xff,
+0xff,0xff,0xff,0xff,0xf1,0xf6,0xff,0xf7,0x7e,0x3f,0xff,0x7f,0xff,0xff,0xff,0xf7,
+0xff,0xff,0xff,0xed,0xff,0xdf,0xff,0xb7,0xff,0xf0,0x3f,0xff,0xff,0xff,0xfe,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,
+0xff,0xff,0xff,0xff,0xdf,0xff,0xff,0xff,0xdf,0xff,0xff,0xff,0xff,0xbf,0xff,0xdf,
+0x57,0xef,0xf1,0xfd,0xfe,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0xfb,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x7f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,
+0xff,0xff,0xff,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfb,0xff,0xdf,0xff,
+0xff,0xf1,0xfd,0xff,0x7f,0xbf,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xfe,0xff,0xff,0xff,0xff,0xf0,0x9f,0xff,0xff,0xff,0xfe,0xf7,0xfd,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xbf,0xff,0xff,0xff,0xff,0xff,
+0xf1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xf0,0x6f,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf1,
+0xff,0xff,0xfd,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xf0,0xcf,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xfb,0xff,0xff,0xff,0xfe,0xff,0xff,0xfb,0x6f,0xff,0xfe,0xbf,0xff,0xf1,0xff,
+0xf7,0xff,0xff,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfd,
+0xff,0xff,0xff,0xf0,0xef,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xfb,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0x57,0xff,0xfd,0xbf,0xff,0xf1,0xff,0xef,
+0xfe,0xff,0xbf,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,
+0xde,0xff,0xf0,0xcf,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xf7,0xdb,0xff,0xdb,0xfd,
+0xf6,0xff,0xf6,0xff,0x3c,0xbc,0xbc,0xbf,0xdf,0x6f,0xe7,0x2f,0xf1,0x3c,0xbf,0xfd,
+0xbf,0xdf,0x6f,0xff,0x6f,0xf7,0xdb,0xff,0xdb,0xfd,0xf6,0xff,0xf6,0xff,0xff,0xff,
+0x02,0x01,0xdf,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff };
diff --git a/drivers/net/hamradio/z8530.h b/drivers/net/hamradio/z8530.h
new file mode 100644
index 0000000..8bef548
--- /dev/null
+++ b/drivers/net/hamradio/z8530.h
@@ -0,0 +1,245 @@
+
+/* 8530 Serial Communications Controller Register definitions */
+#define	FLAG	0x7e
+
+/* Write Register 0 */
+#define	R0	0		/* Register selects */
+#define	R1	1
+#define	R2	2
+#define	R3	3
+#define	R4	4
+#define	R5	5
+#define	R6	6
+#define	R7	7
+#define	R8	8
+#define	R9	9
+#define	R10	10
+#define	R11	11
+#define	R12	12
+#define	R13	13
+#define	R14	14
+#define	R15	15
+
+#define	NULLCODE	0	/* Null Code */
+#define	POINT_HIGH	0x8	/* Select upper half of registers */
+#define	RES_EXT_INT	0x10	/* Reset Ext. Status Interrupts */
+#define	SEND_ABORT	0x18	/* HDLC Abort */
+#define	RES_RxINT_FC	0x20	/* Reset RxINT on First Character */
+#define	RES_Tx_P	0x28	/* Reset TxINT Pending */
+#define	ERR_RES		0x30	/* Error Reset */
+#define	RES_H_IUS	0x38	/* Reset highest IUS */
+
+#define	RES_Rx_CRC	0x40	/* Reset Rx CRC Checker */
+#define	RES_Tx_CRC	0x80	/* Reset Tx CRC Checker */
+#define	RES_EOM_L	0xC0	/* Reset EOM latch */
+
+/* Write Register 1 */
+
+#define	EXT_INT_ENAB	0x1	/* Ext Int Enable */
+#define	TxINT_ENAB	0x2	/* Tx Int Enable */
+#define	PAR_SPEC	0x4	/* Parity is special condition */
+
+#define	RxINT_DISAB	0	/* Rx Int Disable */
+#define	RxINT_FCERR	0x8	/* Rx Int on First Character Only or Error */
+#define	INT_ALL_Rx	0x10	/* Int on all Rx Characters or error */
+#define	INT_ERR_Rx	0x18	/* Int on error only */
+
+#define	WT_RDY_RT	0x20	/* Wait/Ready on R/T */
+#define	WT_FN_RDYFN	0x40	/* Wait/FN/Ready FN */
+#define	WT_RDY_ENAB	0x80	/* Wait/Ready Enable */
+
+/* Write Register #2 (Interrupt Vector) */
+
+/* Write Register 3 */
+
+#define	RxENABLE	0x1	/* Rx Enable */
+#define	SYNC_L_INH	0x2	/* Sync Character Load Inhibit */
+#define	ADD_SM		0x4	/* Address Search Mode (SDLC) */
+#define	RxCRC_ENAB	0x8	/* Rx CRC Enable */
+#define	ENT_HM		0x10	/* Enter Hunt Mode */
+#define	AUTO_ENAB	0x20	/* Auto Enables */
+#define	Rx5		0x0	/* Rx 5 Bits/Character */
+#define	Rx7		0x40	/* Rx 7 Bits/Character */
+#define	Rx6		0x80	/* Rx 6 Bits/Character */
+#define	Rx8		0xc0	/* Rx 8 Bits/Character */
+
+/* Write Register 4 */
+
+#define	PAR_ENA		0x1	/* Parity Enable */
+#define	PAR_EVEN	0x2	/* Parity Even/Odd* */
+
+#define	SYNC_ENAB	0	/* Sync Modes Enable */
+#define	SB1		0x4	/* 1 stop bit/char */
+#define	SB15		0x8	/* 1.5 stop bits/char */
+#define	SB2		0xc	/* 2 stop bits/char */
+
+#define	MONSYNC		0	/* 8 Bit Sync character */
+#define	BISYNC		0x10	/* 16 bit sync character */
+#define	SDLC		0x20	/* SDLC Mode (01111110 Sync Flag) */
+#define	EXTSYNC		0x30	/* External Sync Mode */
+
+#define	X1CLK		0x0	/* x1 clock mode */
+#define	X16CLK		0x40	/* x16 clock mode */
+#define	X32CLK		0x80	/* x32 clock mode */
+#define	X64CLK		0xC0	/* x64 clock mode */
+
+/* Write Register 5 */
+
+#define	TxCRC_ENAB	0x1	/* Tx CRC Enable */
+#define	RTS		0x2	/* RTS */
+#define	SDLC_CRC	0x4	/* SDLC/CRC-16 */
+#define	TxENAB		0x8	/* Tx Enable */
+#define	SND_BRK		0x10	/* Send Break */
+#define	Tx5		0x0	/* Tx 5 bits (or less)/character */
+#define	Tx7		0x20	/* Tx 7 bits/character */
+#define	Tx6		0x40	/* Tx 6 bits/character */
+#define	Tx8		0x60	/* Tx 8 bits/character */
+#define	DTR		0x80	/* DTR */
+
+/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
+
+/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+
+/* Write Register 8 (transmit buffer) */
+
+/* Write Register 9 (Master interrupt control) */
+#define	VIS	1	/* Vector Includes Status */
+#define	NV	2	/* No Vector */
+#define	DLC	4	/* Disable Lower Chain */
+#define	MIE	8	/* Master Interrupt Enable */
+#define	STATHI	0x10	/* Status high */
+#define	NORESET	0	/* No reset on write to R9 */
+#define	CHRB	0x40	/* Reset channel B */
+#define	CHRA	0x80	/* Reset channel A */
+#define	FHWRES	0xc0	/* Force hardware reset */
+
+/* Write Register 10 (misc control bits) */
+#define	BIT6	1	/* 6 bit/8bit sync */
+#define	LOOPMODE 2	/* SDLC Loop mode */
+#define	ABUNDER	4	/* Abort/flag on SDLC xmit underrun */
+#define	MARKIDLE 8	/* Mark/flag on idle */
+#define	GAOP	0x10	/* Go active on poll */
+#define	NRZ	0	/* NRZ mode */
+#define	NRZI	0x20	/* NRZI mode */
+#define	FM1	0x40	/* FM1 (transition = 1) */
+#define	FM0	0x60	/* FM0 (transition = 0) */
+#define	CRCPS	0x80	/* CRC Preset I/O */
+
+/* Write Register 11 (Clock Mode control) */
+#define	TRxCXT	0	/* TRxC = Xtal output */
+#define	TRxCTC	1	/* TRxC = Transmit clock */
+#define	TRxCBR	2	/* TRxC = BR Generator Output */
+#define	TRxCDP	3	/* TRxC = DPLL output */
+#define	TRxCOI	4	/* TRxC O/I */
+#define	TCRTxCP	0	/* Transmit clock = RTxC pin */
+#define	TCTRxCP	8	/* Transmit clock = TRxC pin */
+#define	TCBR	0x10	/* Transmit clock = BR Generator output */
+#define	TCDPLL	0x18	/* Transmit clock = DPLL output */
+#define	RCRTxCP	0	/* Receive clock = RTxC pin */
+#define	RCTRxCP	0x20	/* Receive clock = TRxC pin */
+#define	RCBR	0x40	/* Receive clock = BR Generator output */
+#define	RCDPLL	0x60	/* Receive clock = DPLL output */
+#define	RTxCX	0x80	/* RTxC Xtal/No Xtal */
+
+/* Write Register 12 (lower byte of baud rate generator time constant) */
+
+/* Write Register 13 (upper byte of baud rate generator time constant) */
+
+/* Write Register 14 (Misc control bits) */
+#define	BRENABL	1	/* Baud rate generator enable */
+#define	BRSRC	2	/* Baud rate generator source */
+#define	DTRREQ	4	/* DTR/Request function */
+#define	AUTOECHO 8	/* Auto Echo */
+#define	LOOPBAK	0x10	/* Local loopback */
+#define	SEARCH	0x20	/* Enter search mode */
+#define	RMC	0x40	/* Reset missing clock */
+#define	DISDPLL	0x60	/* Disable DPLL */
+#define	SSBR	0x80	/* Set DPLL source = BR generator */
+#define	SSRTxC	0xa0	/* Set DPLL source = RTxC */
+#define	SFMM	0xc0	/* Set FM mode */
+#define	SNRZI	0xe0	/* Set NRZI mode */
+
+/* Write Register 15 (external/status interrupt control) */
+#define	ZCIE	2	/* Zero count IE */
+#define	DCDIE	8	/* DCD IE */
+#define	SYNCIE	0x10	/* Sync/hunt IE */
+#define	CTSIE	0x20	/* CTS IE */
+#define	TxUIE	0x40	/* Tx Underrun/EOM IE */
+#define	BRKIE	0x80	/* Break/Abort IE */
+
+
+/* Read Register 0 */
+#define	Rx_CH_AV	0x1	/* Rx Character Available */
+#define	ZCOUNT		0x2	/* Zero count */
+#define	Tx_BUF_EMP	0x4	/* Tx Buffer empty */
+#define	DCD		0x8	/* DCD */
+#define	SYNC_HUNT	0x10	/* Sync/hunt */
+#define	CTS		0x20	/* CTS */
+#define	TxEOM		0x40	/* Tx underrun */
+#define	BRK_ABRT	0x80	/* Break/Abort */
+
+/* Read Register 1 */
+#define	ALL_SNT		0x1	/* All sent */
+/* Residue Data for 8 Rx bits/char programmed */
+#define	RES3		0x8	/* 0/3 */
+#define	RES4		0x4	/* 0/4 */
+#define	RES5		0xc	/* 0/5 */
+#define	RES6		0x2	/* 0/6 */
+#define	RES7		0xa	/* 0/7 */
+#define	RES8		0x6	/* 0/8 */
+#define	RES18		0xe	/* 1/8 */
+#define	RES28		0x0	/* 2/8 */
+/* Special Rx Condition Interrupts */
+#define	PAR_ERR		0x10	/* Parity error */
+#define	Rx_OVR		0x20	/* Rx Overrun Error */
+#define	CRC_ERR		0x40	/* CRC/Framing Error */
+#define	END_FR		0x80	/* End of Frame (SDLC) */
+
+/* Read Register 2 (channel b only) - Interrupt vector */
+
+/* Read Register 3 (interrupt pending register) ch a only */
+#define	CHBEXT	0x1		/* Channel B Ext/Stat IP */
+#define	CHBTxIP	0x2		/* Channel B Tx IP */
+#define	CHBRxIP	0x4		/* Channel B Rx IP */
+#define	CHAEXT	0x8		/* Channel A Ext/Stat IP */
+#define	CHATxIP	0x10		/* Channel A Tx IP */
+#define	CHARxIP	0x20		/* Channel A Rx IP */
+
+/* Read Register 8 (receive data register) */
+
+/* Read Register 10  (misc status bits) */
+#define	ONLOOP	2		/* On loop */
+#define	LOOPSEND 0x10		/* Loop sending */
+#define	CLK2MIS	0x40		/* Two clocks missing */
+#define	CLK1MIS	0x80		/* One clock missing */
+
+/* Read Register 12 (lower byte of baud rate generator constant) */
+
+/* Read Register 13 (upper byte of baud rate generator constant) */
+
+/* Read Register 15 (value of WR 15) */
+
+/* Z85C30/Z85230 Enhanced SCC register definitions */
+
+/* Write Register 7' (SDLC/HDLC Programmable Enhancements) */
+#define AUTOTXF	0x01		/* Auto Tx Flag */
+#define AUTOEOM 0x02		/* Auto EOM Latch Reset */
+#define AUTORTS	0x04		/* Auto RTS */
+#define TXDNRZI 0x08		/* TxD Pulled High in SDLC NRZI mode */
+#define RXFIFOH 0x08		/* Z85230: Int on RX FIFO half full */
+#define FASTDTR 0x10		/* Fast DTR/REQ Mode */
+#define CRCCBCR	0x20		/* CRC Check Bytes Completely Received */
+#define TXFIFOE 0x20		/* Z85230: Int on TX FIFO completely empty */
+#define EXTRDEN	0x40		/* Extended Read Enabled */
+
+/* Write Register 15 (external/status interrupt control) */
+#define SHDLCE	1		/* SDLC/HDLC Enhancements Enable */
+#define FIFOE	4		/* FIFO Enable */
+
+/* Read Register 6 (frame status FIFO) */
+#define BCLSB	0xff		/* LSB of 14 bits count */
+
+/* Read Register 7 (frame status FIFO) */
+#define BCMSB	0x3f		/* MSB of 14 bits count */
+#define FDA	0x40		/* FIFO Data Available Status */
+#define FOS	0x80		/* FIFO Overflow Status */