/*
   MODULARIZED Linux driver for Xnet-1 board
   Copyright Kate Alhola 1995,  Matti Aarnio 1995
   kate@nic.funet.fi,  mea@nic.funet.fi

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 2, as
   published by the Free Software Foundation.

   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.

   This driver is based on Xnet-1 SVR4 driver by Kate Alhola
   It also contains fragments from :

   pi2.c: Driver for the Ottawa Amateur Radio Club PI and PI2 interface.
   Copyright (c) 1994 David Perry

   And 

   skeleton.c: A network driver outline for linux.
   Written 1993-94 by Donald Becker.

   Copyright 1993 United States Government as represented by the
   Director, National Security Agency.
 */


#ifdef MODULE
#include <linux/version.h>
#include <linux/module.h>
#endif


#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/malloc.h>
#include <linux/string.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <linux/errno.h>


/* On AXP this would be a real command, but now we just circumvent
   sometimes stupidly behaving compiler from optimizing our busy-
   loop-delays away .. (Should use microdelay()s..) */
#ifndef mb
#define mb() __asm__ __volatile__("": : :"memory")
#endif


#include <linux/netdevice.h>
/* #include <linux/etherdevice.h> */
#include <linux/skbuff.h>

/* ================================================================

   Card resource allocations -- stored on INTS so that INSMOD can
   alter them; for example:

   insmod -o xnet xnet.o xnet_base_addr=0x380 xnet_dma_out=2 xnet_dma_in=3 xnet_irq=7

   ================================================================ */

int xnet_major = 0;		/* 0: allocate dynamically */
int xnet_base_addr = 0x300;
int xnet_dma_out = 3;
int xnet_dma_in = 1;
int xnet_irq = 5;

#define CHRDEVNAME "xnet_char"

/*=========================================================================*/
/* Kludge warning, Unix outb is outb(port,val) Linux is outb(val,port)
   outb is replaced with outp(port,val) */

#define outp(port,val) outb(val,port)

/*=========================================================================*/


#define CI_OFF          1	/* when C or I is 1 they are off */
#define CI_ON           0	/* when C or I is 0 they are on */
#define DONT_CARE       2	/* when C or I is 0 they are on */

#define channelA        0
#define channelB        1

#define int_enable      0xDF	/* interrupt 5 enable */
#define int_disable     0x20	/* interrupt 5 disable */

#define int2            0xA	/* interrupt 2 vector */
#define int5            0xD	/* interrupt 5 vector */

#define eoi5            0x65	/* Specific End of interrupt. (level 5)
				 */

#define intcont         0x21	/* Address of 8259 interrupt controller
				   ICW2-3. */
#define intcon1         0x20	/* Address of 8259 ICW1. */

/* Addresses of 8-bit DMA channels */

#define DMAch0Base      0x0	/* CH 0 base and current address */
#define DMAch0WordCount 0x1	/* CH 0 base and word count */
#define DMAch1Base      0x2	/* CH 1 base and current address */
#define DMAch1WordCount 0x3	/* CH 1 base and word count */
#define DMAch2Base      0x4	/* CH 2 base and current address */
#define DMAch2WordCount 0x5	/* CH 2 base and word count */
#define DMAch3Base      0x6	/* CH 3 base and current address */
#define DMAch3WordCount 0x7	/* CH 3 base and word count */

#define DMAstatus       0x8	/* DMA status register */
#define DMAcommand      0x8	/* DMA command register */
#define DMAWreq         0x9	/* DMA write request register */
#define DMAsinglemask   0xA	/* DMA single mask register write */
#define DMAWmode        0xB	/* write mode register */
#define DMAClrBP        0xC	/* clear byte pointer flip-flop */
#define DMAtempReg      0xD	/* Read temporary register /write master
				   clear */
#define DMAclearmask    0xE	/* Clear mask register */
#define DMAwritemask    0xF	/* Write all mask register bits */

#define DMAch0page      0x87	/* Address of DMA Channel 0 page
				   register */
#define DMAch1page      0x83	/* Address of DMA Channel 1 page
				   register */
#define DMAch2page      0x81	/* Address of DMA Channel 2 page
				   register */
#define DMAch3page      0x82	/* Address of DMA Channel 3 page
				   register */

#define clearBytePointerFF outp(DMAClrBP,0x0)

#define RXDMAchannel    1	/* RX DMA uses channel 1 */
#define RXDMAaddr       DMAch1Base
#define RXDMAlength     DMAch1WordCount
#define RXDMApage       DMAch1page

#define TXDMAchannel    3	/* TX DMA uses channel 3 */
#define TXDMAaddr       DMAch3Base
#define TXDMAlength     DMAch3WordCount
#define TXDMApage       DMAch3page
#define TXDMATC         0x8	/* terminal count */

#define IOBASE          0x300	/* Card I/O base address */

/*      Serial communications controller I/O addresses          */

#define sccAData                IOBASE
#define sccACtrl                IOBASE + 1
#define sccBData                IOBASE + 2
#define sccBCtrl                IOBASE + 3

/*      Auxiliary data input address                            */

#define parallelIn      IOBASE + 8

/*                                      Data bit definitions:
   Bit 0 : B-channel DSR
   Bit 1 : B-channel RI
   Bit 2 : Reserved
   Bit 3 : Reserved
   Bit 4 : DMA int request
   Bit 5 : AMPSC int request
   Bit 6 : A-channel INDICATION
   Bit 7 : A-channel Rx -signal
 */

/*      Auxiliary control signals                                       */

#define interruptEnable IOBASE + 8	/* SCC interrupt enable */
#define DMAintEnable    IOBASE + 9	/* DMA interrupt enable */
#define RxDMAenable     IOBASE + 10	/* Receive DMA request enable */
#define TxDMAenable     IOBASE + 11	/* Transmit DMA request enable */
#define DMAintClear     IOBASE + 12	/* clear DMA interrupt */
#define x21Control      IOBASE + 15	/* x21 control signal */

/*                                      After power-up all control signals 
   are in inactive state. Signals can
   be set by writing 1 to I/O -port
   and reset by writing 0.
 */

#define sccEOI          0x38	/* end of interrupt */
#define resetES         0x10	/* reset E/S bit latch */
#define errorreset      0x30	/* reset error */


#define SCC_WR3_MASK       0x42	/* Rx 7 bits/char, sync character load inhibit,
				   receive disable */
#define SCC_WR3_RXEN       0x1	/* receive enable bit in WR3 */
#define SCC_WR3_HUNT       0x10	/* enter hunt mode bit in WR3 */
#define SCC_WR5_MASK       0x24	/* Tx 7 bits/char,
				   transmit disabled (send one's), crc-16 */
#define SCC_WR5_TXEN       0x8	/*transmit enable bit in wr5 */
#define SCC_WR5_BREAK      0x10	/*send break mask */
#define SCC_WR10_MASK      0x0	/*NRZ, 8 bit sync char */


/* Interrupt handler types */

#define none            0	/* No interrupt handler */
#define async           1	/* asynchronous interrupt handler */
#define sync            2	/* synchronous interrupt handler */
#define hdlc            3	/* HDLC interrupt handler (with DMA) */

/* Scc commands */
#define SccResTxInt     0x28
#define SccRxDrF        1
#define SccTxDrE        4


/*=========================================================================*/

unsigned char SccAsyncTable[] =

{
    28,				/* length of table */
    1, 0x12,			/* interrupt mode rx enabled and transmit */
    4, 0x44,			/* async protocol mode 16* clock, 1 stop, no parity */
    14, 0,			/* Disable BRG */
    12, 0x01,			/* baud rate setting for tx */
    26,				/* baud rate lsb (9600 bd * 16, */
    0,				/* baud rate msb */

    12, 0x02,			/* baud rate setting for tx */
    26, 0,			/* baud rate lsb (9600 bd * 16), baud rate msb */

    15, 0x52,			/* transmit/receive clock section */
    14, 0x7,			/* transmit/receive BRG enable */
    10, 0x0,			/* NRZ mode */
    3, 0xC1,			/* receive enable 8 bits/char */
    5, 0x68,			/* transmit enable 8 bits/char */

    11,
					      /*      0xa8,       *//* selection of E/S interrupt */
    0x0,			/* No e/s interrupts */

    0x11,			/* pointer 1, reset E/S latch */
    0x12			/* enable Rx interrupts and tx */

};


unsigned char SccSyncTable[] =

{
    22,				/* length of table */
    4, 0x1,			/* sync protocol mode  1* clock, sync, parity odd */
    10, 0x0,			/* NRZ mode, enable sync hunt */
    6, 0x16,			/* tx sync character */
    7, 0x16,			/* rx sync character */
    12, 0x0,			/* no internal BRG */
    15, 0x28,			/* transmit/receive clock section source is trxca */
    14, 0x64,			/* transmit/receive BRG disable disable PLL */
    3, 0x52,			/* receive, sync load inhibit 7 bits/char, rx disabled */
    5, 0x21,			/* transmit 7 bits/char  transmit disabled (send ones) */
    11, 0x48,			/* selection of E/S interrupt (Tx underrun, EOF) */
    0x0, 0x0			/* Dummy commands */

};

/* Externaly clocked HDLC */

unsigned char SccHdlcXTable[] =

{
    28,				/* length of table */

    4, 0x20,			/* hdlc protocol mode 1* clock, HDLC */
    10, 0x80,			/* NRZ mode, send flags CRC preset to 0xffff */
    6, 0xFF,			/* address = broadcast ?? */
    7, 0x7E,			/* HDLC flag */
    8, 0xFF,			/* Transmit data length low byte */
    9, 0xFF,			/* Transmit data length high byte */
    13, 0x0,			/* Transmit data length counter disable, no standby mode */
    12, 0x0,			/* no internal BRG */
    15, 0x28,			/* Tx/Rx clock section source is trxca */
    14, 0x07,			/* transmit/receive BRG enable,disable PLL */
    11, 0x04,			/* selection of E/S interrupt (all send ) */
    1, 0x0E,			/* Tx, Rx, E/S interrupt enable */
    3, 0xC0,			/* receive, addr search disable  8 bits/char, rx disabled */
    5, 0x61			/* transmit 8 bits/char transmit enable */
};


/* Internaly clocked HDLC */
unsigned char SccHdlcITable[] =

{
    34,				/* length of table */

    4, 0x20,			/* hdlc protocol mode 1* clock, HDLC */
    10, 0x0,			/* NRZ mode, send flags */
    6, 0xFF,			/* address = broadcast ?? */
    7, 0x7E,			/* HDLC flag */
    8, 0xFF,			/* Transmit data length low byte */
    9, 0xFF,			/* Transmit data length high byte */
    13, 0,			/* Transmit data length counter disable, no standby mode */
    12, 0x01,			/* baud rate setting for tx */
    26, 0,			/* baud rate lsb (9600 bd * 16, msb */
    12, 0x02,			/* baud rate setting for tx */
    26, 0,			/* baud rate lsb (9600 bd * 16) , msb */
    15, 0x55,			/* transmit/receive clock section */
    14, 0x07,			/* transmit/receive BRG enable disable PLL */
    11, 0x0c,			/* selection of E/S interrupt */
    1, 0x0E,			/* Tx, Rx, E/S interrupt enable */
    3, 0xC0,			/* Rx, addr search disable 8 bits/char, rx disabled */
    5, 0x60			/* Tx 8 bits/char transmit disabled (send ones) */


};
/* Internaly clocked with DPLL HDLC */
unsigned char SccHdlcDTable[] =

{
    39,				/* length of table */

    0x18,			/* Reset SCC */
    4, 0x20,			/* hdlc protocol mode  1* clock, HDLC */
    10, 0x40,			/* FM1 mode, send flags */
					      /*        10,0x00,  *//* NRZ mode, send flags */
    6, 0xFF,			/* address = broadcast ?? */
    7, 0x7E,			/* HDLC flag */
    8, 0xFF,			/* Transmit data length low byte */
    9, 0xFF,			/* Transmit data length high byte */
    13, 0x0,			/* Transmit data length counter disable, no standby mode */
    12, 0x82,			/* baud rate setting for rx */
    0x9e, 1,			/* baud rate lsb (9600 bd * 1, baud rate msb */
    11, 0x81,			/* baud rate setting for tx */
    13, 0,			/* baud rate lsb (9600 bd * 32), baud rate msb */
    15, 0x75,			/* transmit/receive clock from DPLL */
    14, 0x87,			/* transmit/receive BRG enable  DPLL clock from BRG */
    14, 0xc7,			/* FM mode */
    14, 0x27,			/* DPLL in search MODE */
    11, 0x0c,			/* selection of E/S interrupt */
    1, 0x13,			/* selection of  interrupt */
    3, 0xC9,			/* receive, addr search disable 8 bits/char, rx enable */
    5, 0x60			/* transmit 8 bits/char transmit disabled (send ones) */


};

/*=========================================================================*/


#define DMA_BUFF_SIZE 2048
/* Transmit timeout in jiffies unit */
#define TX_TIMEOUT 100
#define XNET_IOCTL_DEBUG 0x9901
#define XNET_IOCTL_MEMPRINT 0x9902

/* protocol numbers */
#define PROTO_IP       0x0021
#define PROTO_VJCOMP   0x002d
#define PROTO_VJUNCOMP 0x002f


/* use 0 for production, 1 for verification, >2 for debug */
#ifndef XNET_DEBUG
#define XNET_DEBUG 6
#endif
static unsigned int xnet_debug = XNET_DEBUG;

#define DMA_OUT 3
#define DMA_IN  1
#define MODE_ASYNC   0x100
#define MODE_HDLC    0x200
#define MODE_SLIP    0x400
#define MODE_PPP     0x800
#define MODE_DMA     0x1000
#define MODE_BISYNC  0x2000
#define MODE_LLC     0x4000
#define MODE_IDLEFLAG 0x8000


struct ppp_hdr {
    unsigned char addr, ctl;	/* 0xff 0x03  */
    unsigned short proto;	/* Protocol */
};

struct xnet_local {
    struct enet_statistics stats;
    char *rcvbuf;		/* Buffer for current rx packet */
    char *rxdmabuf1;		/* DMA rx buffer */
    char *rxdmabuf2;		/* DMA rx buffer */
    char *txdmabuf;		/* Transmit DMA buffer */

    struct sk_buff_head sndq;	/* Packets awaiting transmission */
    struct sk_buff_head crxq;	/* Packets awaiting for character if read */
    int sndcnt;			/* Number of packets on sndq */
    struct sk_buff *sndbuf;	/* Current buffer being transmitted */
    unsigned int txlen;
    int bufsiz;			/* Size of rcvbuf */
    char tstate;		/* Transmitter state */
#define CLOSED  0		/* Device is closed */
#define IDLE    1		/* Transmitter off, no data pending */
#define BUSY    2		/* Transmitter on, sending data */
    int txdma;			/* Tx DMA channel for this port */
    int rxdma;			/* Rx DMA channel for this port */
    int channel;		/* I/O Address of 72001 channel */
    int ioaddr;			/* Base address of the board */
    unsigned char cr3, cr5, cr15;	/* control register values */
    int mode;
    int dcd;
    int sbreak;			/* In idle send break ( all ones ) */
    int ifwait, ifdelay1;	/* Interframe vait */
    struct wait_queue *xnet_wait_queue;		/* Input wait queue */
    int chstate;		/* Chacacter Interface status */
};

/* Dev saved here for character device interface, where it is not 
   passed as a parameter */
static struct device *xnet_dev;
static struct xnet_local xnet_l;
static int dmabuf_set = 0;
static char *malloc_dmabuf = NULL;


/* Index to functions, as function prototypes. */

extern int xnet_init(struct device *dev);

static int xnet_open(struct device *dev);
static int xnet_send_packet(struct sk_buff *skb, struct device *dev);
static void xnet_interrupt(int irq, struct pt_regs *regs);
static int xnet_close(struct device *dev);
static struct enet_statistics *xnet_get_stats(struct device *dev);
#if LINUX_VERSION_CODE < (1*256*256+3*256)	/* Pre 1.3.0 */
static int xnet_header(unsigned char *, struct device *, unsigned short,
		       void *, void *, unsigned, struct sk_buff *);
#define NET02D 1
#else				/* Linux 1.3.0 (and latter ?) */
static int xnet_header(struct sk_buff *, struct device *, unsigned short,
		       void *, void *, unsigned);
#endif
static int xnet_rebuild_header(void *, struct device *, unsigned long,
			       struct sk_buff *);
#ifdef NET02D
static unsigned short xnet_type_trans(struct sk_buff *, struct device *);
#endif
static void xnet_rec_next_frame(register struct xnet_local *lp);
static void xnet_set_dma_mode(register struct xnet_local *lp);
static void xnet_setsccmode(register struct xnet_local *lp, int mode);
static void xnet_hexdump(unsigned char *p, int len);
static void hardware_send_packet(struct xnet_local *lp, struct sk_buff *skb);
unsigned long xnet_minit(unsigned long mem_start, unsigned long mem_end);
/*======================================================
!
! release a skbuf
!
\*======================================================*/
static void free_p(struct sk_buff *skb)
{
    if (xnet_debug > 5)
	printk("free_b skb=0x%08x free=%d lock=%d\n",
	       (long) skb, skb->free, skb->lock);
    if (skb->lock)
	dev_kfree_skb(skb, FREE_WRITE);
    else
	kfree_skb(skb, FREE_WRITE);
}

/*======================================================
!
! xnet as a character device file ops
!
\*======================================================*/
static int xnet_cread(struct inode *inode, struct file *filp,
		      char *buf, int count);
static int xnet_cwrite(struct inode *inode, struct file *filp,
		       char *buf, int count);
static int xnet_cioctl(struct inode *inode, struct file *filp,
		       unsigned int iocmd, unsigned long ioarg);
static int xnet_copen(struct inode *inode, struct file *filp);
static void xnet_crelease(struct inode *inode, struct file *filp);
static int xnet_select(struct inode *inode, struct file *file,
		       int sel_type, select_table * wait);

static struct file_operations xnet_char_fops =
{
    NULL,			/* lseek */
    xnet_cread,			/* read */
    xnet_cwrite,		/* write */
    NULL,			/* not allowed */
    xnet_select,		/* select  */
    xnet_cioctl,		/* ioctl */
    NULL,			/* mmap not allowed */
    xnet_copen,			/* open */
    xnet_crelease,		/* release */
    NULL,			/* fsync */
    NULL,			/* fasync */
    NULL,			/* check_media_change */
    NULL			/* revalidate */
};

static int xnet_cread(struct inode *inode, struct file *filp, char *buf, int count)
{
    int sksize;
    struct xnet_local *lp = (struct xnet_local *) xnet_dev->priv;
    struct sk_buff *sbuf;	/* Current buffer being handled */
    if (xnet_debug > 5)
	printk("xnet_cread n=%d\n", count);

    if ((sbuf = skb_dequeue(&lp->crxq)) == NULL) {
	/* Nothing to read, sleep a bit */
	interruptible_sleep_on(&lp->xnet_wait_queue);
	if ((sbuf = skb_dequeue(&lp->crxq)) == NULL) {
	    /* Woken up, but Nothing to read, just return */
	    return (0);
	}
    }
    sksize = sbuf->len > count ? count : sbuf->len;

    /* Copy data to user space */

    memcpy_tofs(buf, sbuf->data, sksize);
    free_p(sbuf);
    if (xnet_debug > 5)
	printk("xnet_cread return n=%d\n", sksize);
    return (sksize);
}

static int xnet_cwrite(struct inode *inode, struct file *filp, char *buf, int count)
{
    struct sk_buff *skb;
    int sksize;
    struct xnet_local *lp = (struct xnet_local *) xnet_dev->priv;

    sksize = count > DMA_BUFF_SIZE ? DMA_BUFF_SIZE : count;

    skb = alloc_skb(sksize, GFP_ATOMIC);
    if (skb == NULL) {
	printk("xnet: Memory squeeze, dropping packet.\n");
	return 0;
    }
    skb->len = (unsigned long) sksize;
    skb->dev = xnet_dev;
    skb->sk = NULL;
    skb->free = 1;

    /* 'skb->data' points to the start of sk_buff data area. */
    memcpy_fromfs(skb->data, buf, sksize);

    /* Send packet like any other net interface packet */

    hardware_send_packet(lp, skb);

    return (0);
}


static int xnet_cioctl(struct inode *inode, struct file *filp,
		       unsigned int iocmd, unsigned long ioarg)
{
    /* struct xnet_local *lp = (struct xnet_local *) xnet_dev->priv; */
    int retval = 0;

    switch (iocmd) {
    case XNET_IOCTL_DEBUG:
	printk("xnet ioctl debug=%d\n", (int) ioarg);
	xnet_debug = ioarg;
	break;
#ifndef MODULE
    case XNET_IOCTL_MEMPRINT:
	show_mem();		/* Print kernel memory statistics to console */
	break;
#endif
    default:
	retval = -EINVAL;
    }
    return retval;
}

/*==================================================
!
! Xnet open as a character device
!
\*====================================================*/

static int xnet_copen(struct inode *inode, struct file *filp)
{
    struct xnet_local *lp = (struct xnet_local *) xnet_dev->priv;
    lp->chstate += 1;		/* Character interface is open */
    return 0;
}

static void xnet_crelease(struct inode *inode, struct file *filp)
{
    struct xnet_local *lp = (struct xnet_local *) xnet_dev->priv;
    if (lp->chstate > 0)
	lp->chstate -= 1;	/* Character interface is closed */
}

/*==================================================
!
! Xnet character interface select()
!
\*====================================================*/

static int xnet_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
{
    struct xnet_local *lp = (struct xnet_local *) xnet_dev->priv;
    if (sel_type != SEL_IN)
	return 0;
    if (skb_peek(&lp->crxq) != NULL)
	return 1;
    select_wait(&lp->xnet_wait_queue, wait);
    return 0;
}


#ifndef MODULE
/*==============================================================*\
!
! Initialize Xnet board and related Linux structures
! Register IRQ and DMA Channels
!
\*==============================================================*/
unsigned long xnet_minit(unsigned long mem_start, unsigned long mem_end)
{
    unsigned long m;

    printk("xnet_minit start=%08x end=%08x\n", (int) mem_start, (int) mem_end);

    /* With granularity of 4kB, we collect some buffers... */
    m = (mem_start & 0xffff0000) + 0x1000;

    xnet_l.txdmabuf = (char *) m;
    printk("xnet_init: txdmabuf=%08x\n", (int) (xnet_l.txdmabuf));
    m += 0x1000;
    xnet_l.rxdmabuf1 = (char *) m;
    printk("xnet_init: rxdmabuf1=%08x\n", (int) (xnet_l.rxdmabuf1));
    m += 0x1000;
    xnet_l.rxdmabuf2 = (char *) m;
    printk("xnet_init: rxdmabuf2=%08x\n", (int) (xnet_l.rxdmabuf2));
    m += 0x1000;

    printk("xnet_minit m=%08x\n", (int) m);

    dmabuf_set = 1;

    return (m);
}

#endif				/* MODULE */

int xnet_init(struct device *dev)
{
/*  static unsigned version_printed = 0; */
    struct xnet_local *lp;
/*  char *bufp; */
    int i;

    /* Initialize the device structure. */
    dev->priv = &xnet_l;
    lp = &xnet_l;

    printk("Linux sync ppp driver, Copyright Kate Alhola 1995\n");
    printk("xnet_init: dev=%08x ioaddr=%04x priv=%08x\n", (int) dev,
	   (int) (dev->base_addr), (int) (dev->priv));
    /* Save static dev for char interface */
    xnet_dev = dev;

    if (!dmabuf_set) {
	char *dmabuf;

	malloc_dmabuf = kmalloc(sizeof(struct xnet_local) + DMA_BUFF_SIZE * 4,
				GFP_KERNEL | GFP_DMA);
	dmabuf = malloc_dmabuf;

	if (dmabuf == NULL) {
	    printk("XNET: Can't allocate DMA buffer area, aargh..\n");
	    return -ENOMEM;
	}
	/* Adjust into DMA_BUFF_SIZE boundary */
	dmabuf =
	    (char *) (((unsigned long) dmabuf) & ~(DMA_BUFF_SIZE - 1))
	    + DMA_BUFF_SIZE;
	lp->txdmabuf = dmabuf;
	dmabuf += DMA_BUFF_SIZE;
	lp->rxdmabuf1 = dmabuf;
	dmabuf += DMA_BUFF_SIZE;
	lp->rxdmabuf2 = dmabuf;

	dmabuf_set = 1;

	if (xnet_debug > 5) {
	    printk("xnet_init: lp->txdmabuf=%08x\n", (int) (lp->txdmabuf));
	    printk("xnet_init: lp->rxdmabuf1=%08x\n", (int) (lp->rxdmabuf1));
	    printk("xnet_init: lp->rxdmabuf2=%08x\n", (int) (lp->rxdmabuf2));
	}
    }
    dev->priv = &xnet_l;

    lp->ioaddr = dev->base_addr;
    lp->channel = dev->base_addr;	/* A-Channel as a default */

    lp->txdma = DMA_OUT;	/* Fixed at a moment */
    lp->rxdma = DMA_IN;		/* Fixed at a moment */
    lp->bufsiz = DMA_BUFF_SIZE;
    lp->tstate = CLOSED;
    lp->xnet_wait_queue = NULL;
    lp->chstate = CLOSED;	/* Character interface is closed */

    /* Grab the region */
    request_region(dev->base_addr & 0x3f0, 16, "xnet");


    /* Initialize the transmit and character rxq queue head structure */
    skb_queue_head_init(&lp->sndq);
    skb_queue_head_init(&lp->crxq);

    /* device INFO */
    dev->open = xnet_open;
    dev->stop = xnet_close;
    dev->mtu = 1500;
    dev->get_stats = xnet_get_stats;
    dev->hard_start_xmit = xnet_send_packet;
    dev->hard_header = xnet_header;
    dev->hard_header_len = 4;
#ifdef NET02D
    dev->type_trans = xnet_type_trans;
#endif
    dev->rebuild_header = xnet_rebuild_header;
    dev->addr_len = 0;
    dev->type = ARPHRD_PPP;

    /* New-style flags */
    dev->flags = IFF_POINTOPOINT;
    dev->family = AF_INET;
    dev->pa_addr = 0;
    dev->pa_brdaddr = 0;
    dev->pa_mask = 0;
    dev->pa_alen = 4;		/* IPv4 addresses are 4 bytes each.. */

    for (i = 0; i < DEV_NUMBUFFS; i++)
	skb_queue_head_init(&dev->buffs[i]);	/* = NULL if NET02D */

    /* This board has jumpered interrupts. Snarf the interrupt vector
       now.  There is no point in waiting since no other device can use
       the interrupt, and this marks the 'irqaction' as busy. */
    {
	int irqval = request_irq(dev->irq, &xnet_interrupt, 0, "xnet");
	if (irqval) {
	    printk("Xnet: unable to get IRQ %d (irqval=%d).\n",
		   dev->irq, irqval);
	    return EAGAIN;
	}
    }

    if ((xnet_major = register_chrdev(xnet_major /* Assign CMAJOR dynamically */ ,
				      "xnet_char", &xnet_char_fops)) < 0)
	printk("xnet : Unable to get chrdev major, all in use?\n");
    else
	printk("xnet : registred to chrdev major %d\n", xnet_major);

    return 0;
}

/*==================================================
!
!
! Open Xnet interface
!
!
\*=================================================*/


static int xnet_open(struct device *dev)
{
    unsigned long flags;
    int i;
    static first_time = 1;

    struct xnet_local *lp = (struct xnet_local *) dev->priv;

    if (xnet_debug > 5)
	printk("xnet_open: dev=%08x\n", (int) dev);
    if (first_time) {
	if (request_dma(lp->rxdma, "xnetrx")) {
	    free_irq(dev->irq);
	    return -EAGAIN;
	}
	if (request_dma(lp->txdma, "xnettx")) {
	    free_irq(dev->irq);
	    return -EAGAIN;
	}
	irq2dev_map[dev->irq] = dev;
    }
    lp->tstate = IDLE;

#ifdef MODULE
    MOD_INC_USE_COUNT;
#endif

/* Initialize scc here */
    save_flags(flags);
    cli();

    lp->rcvbuf = lp->rxdmabuf1;
    outp(lp->channel + 1, 0x18);	/* Reset A channel */
    for (i = 0; i < 1000; i++);
    xnet_setsccmode(lp, 3);	/* Set externally clocked hdlc mode */
    xnet_set_dma_mode(lp);
    outp(lp->channel + 8, 0x01);	/* Allow interupts */
    xnet_rec_next_frame(lp);	/* Start receiver */

    dev->tbusy = 0;
    dev->interrupt = 0;
    dev->start = 1;
    first_time = 0;

    restore_flags(flags);

    return 0;

}

/* The inverse routine to xnet_open(). */
static int xnet_close(struct device *dev)
{
    unsigned long flags;
    struct xnet_local *lp;
    struct sk_buff *ptr;

    if (xnet_debug > 5)
	printk("xnet_close: dev=%08x\n", (int) dev);

    save_flags(flags);
    cli();

    lp = (struct xnet_local *) dev->priv;
    ptr = NULL;

    /*    chipset_init(dev);                *//* reset the scc */
    disable_dma(lp->rxdma);
    disable_dma(lp->txdma);

/*    lp->open_time = 0; */

    dev->tbusy = 1;
    dev->start = 0;

    /* Free any buffers left in the hardware transmit queue */
    while ((ptr = skb_dequeue(&lp->sndq)) != NULL)
	free_p(ptr);

    restore_flags(flags);

#ifdef MODULE
    MOD_DEC_USE_COUNT;
#endif

    return 0;
}
/*====================================================
!
!
! Setup rx dma for receive
!
!
\*==================================================*/

static void setup_rx_dma(struct xnet_local *lp)
{
/*  unsigned long flags; */
    unsigned long dma_abs;
    unsigned dmachan;

#if 0				/* CALLED WITH IRQ DISABLED! */
    save_flags(flags);
    cli();
#endif

    dma_abs = (unsigned long) (lp->rcvbuf);
    dmachan = lp->rxdma;

    if (xnet_debug > 19)
	printk("xnet:setup_rx_dma lp=%08x dma_abs=%08x siz=%d\n",
	       (int) lp, (int) dma_abs, lp->bufsiz);

    disable_dma(dmachan);
    clear_dma_ff(dmachan);
    /*
     * Set DMA mode register to single transfers,
     * incrementing address, auto init, writes
     */
    set_dma_mode(dmachan, DMA_MODE_READ | 0x10);
    set_dma_addr(dmachan, dma_abs);
    set_dma_count(dmachan, lp->bufsiz);
    enable_dma(dmachan);
#if 0
    restore_flags(flags);
#endif
}

/*====================================================
!
!
! Setup tx dma for transmit
!
!
\*==================================================*/

static void setup_tx_dma(struct xnet_local *lp, int length)
{
    unsigned long dma_abs;
    unsigned long flags;
    unsigned long dmachan;

    save_flags(flags);
    cli();

    dmachan = lp->txdma;
    dma_abs = (unsigned long) (lp->txdmabuf);

    disable_dma(dmachan);
    /* Set DMA mode register to single transfers, incrementing address,
     *  no auto init, reads
     */
    set_dma_mode(dmachan, DMA_MODE_WRITE);
    clear_dma_ff(dmachan);
    set_dma_addr(dmachan, dma_abs);
    /* output byte count */
    set_dma_count(dmachan, length);

    restore_flags(flags);
}

static void xnet_dev_tinit(struct device *dev)
{
#if 0
    unsigned long flags;
    static first_time = 1;

    struct xnet_local *lp = (struct xnet_local *) dev->priv;
#endif
}

/*==========================================
!
!   send a next frame
!
\*=============================================*/


static void xnet_send_next_frame(struct xnet_local *lp)
{
    unsigned long flags;

    save_flags(flags);
    cli();

    if (xnet_debug > 19)
	printk("xnet_send_nex_frame: lp=%08x\n", (int) lp);

    /* Transmitter idle. Find a frame for transmission */
    if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
	/* Nothing to transmit, just return */
	lp->tstate = IDLE;
	xnet_dev->trans_start = 0;
	if (!(lp->mode & MODE_IDLEFLAG)) {
	    outp(lp->channel + 1, 5);	/*CR 5 Disable Transmit and sendbreak if needed */
	    outp(lp->channel + 1, lp->cr5 = (lp->cr5 & 0xe7) | lp->sbreak);
	}
	outb(lp->channel + 1, SccResTxInt);	/*Reset transmit interupt */
	if (xnet_debug > 19)
	    printk("xnet_send_nex_frame: nothing to transmit\n");
	restore_flags(flags);
	return;
    }
    /* If a buffer to send, we drop thru here */
    lp->tstate = BUSY;
    lp->txlen = lp->sndbuf->len;
    memcpy(lp->txdmabuf, lp->sndbuf->data, lp->txlen);
    if (xnet_debug > 19)
	xnet_hexdump(lp->txdmabuf, lp->txlen);

/*  dev_kfree_skb(lp->sndbuf, FREE_WRITE); */
    free_p(lp->sndbuf);
    lp->sndbuf = NULL;

    /* Setup DMA controller for tx */
    setup_tx_dma(lp, lp->txlen);

    outp(lp->channel + 1, 5);	/* CR 5 */
    outp(lp->channel + 1, lp->cr5 = (lp->cr5 & 0xf7));	/*Disable Transmit */
    /*      outp(lp->channel+11,0x0); *//* Disable TxDMA */
    outp(lp->channel + 1, 10);	/* CR 10 */
    outp(lp->channel + 1, 0x80);	/* Transmit flag */
    outp(lp->channel + 1, 13);	/* CR 13 */
    outp(lp->channel + 1, 0);	/* Disable Data count reg */
    outp(lp->channel + 1, 8);	/* CR 8 data count LSB */
    outp(lp->channel + 1, lp->txlen & 0xff);
    outp(lp->channel + 1, 9);	/* CR 9 data count MSB */
    outp(lp->channel + 1, lp->txlen >> 8);
    outp(lp->channel + 1, SccResTxInt);		/*Reset transmit interupt */
    outp(lp->channel + 1, 13);	/* CR 13 */
    outp(lp->channel + 1, 2);	/* Enable Data count reg */
    outp(lp->channel + 1, 5);	/* CR 5 */
    outp(lp->channel + 1, lp->cr5 = (lp->cr5 & 0xef) | 0x08);	/* Enable Transmit  and abort sending break */
    outp(lp->channel + 11, 0x1);	/* Enable TxDMA */

    if ((lp->mode & MODE_HDLC) && !(lp->mode & MODE_IDLEFLAG)) {
	outp(lp->channel + 1, 10);	/* CR 10 */
	outp(lp->channel + 1, 0x88);	/* Iddle :Transmit mark */
    }
    outp(lp->channel + 1, 0xc0);	/* Reset Tx underrun/eof */

    /* select transmit interrupts to enable */
    /* Allow DMA on chan */

    enable_dma(lp->txdma);
    /* packet going out now */
    if (xnet_debug > 19)
	printk("xnet_send_nex_frame: done\n");

    xnet_dev->trans_start = jiffies;

    restore_flags(flags);
    return;
}				/*xnet_send_next_frame_txint */

/*==============================================
!
!
!  Switch alternating receive buffers
!
!
\*================================================*/
static void switchbuffers(struct xnet_local *lp)
{
    if (lp->rcvbuf == lp->rxdmabuf1)
	lp->rcvbuf = lp->rxdmabuf2;
    else
	lp->rcvbuf = lp->rxdmabuf1;
}

/*==============================================
!
!
!  A packet has been received, handle it 
!
!
\*================================================*/

static void xnet_rec_frame(struct device *dev, struct xnet_local *lp)
{
    unsigned long flags;
    int bytecount, proto;
    struct sk_buff *skb;
    int pkt_len;
    char *cur_buf;
    struct ppp_hdr *hdr;
    unsigned long dma_abs;
    unsigned dmachan;

    save_flags(flags);
    cli();			/* disable interrupts */

    /* If end of frame */
    /* figure length of frame from 8237 */
    clear_dma_ff(lp->rxdma);
    bytecount = lp->bufsiz - get_dma_residue(lp->rxdma);

    /* Here we have a valid frame */
    /* Get buffer for next frame */
    cur_buf = lp->rcvbuf;

    if (lp->rcvbuf == lp->rxdmabuf1)
	lp->rcvbuf = lp->rxdmabuf2;
    else
	lp->rcvbuf = lp->rxdmabuf1;

    dma_abs = (unsigned long) (lp->rcvbuf);
    dmachan = lp->rxdma;

    set_dma_addr(dmachan, dma_abs);
    set_dma_count(dmachan, lp->bufsiz);
    outp(lp->channel + 1, 0x30);	/* Reset sr0 bits with error reset */


    hdr = (struct ppp_hdr *) cur_buf;
    proto = htons(hdr->proto);

    /* If improper data, quit right away! */
    if (hdr->addr != 0xFF || hdr->ctl != 0x03) {
	lp->stats.rx_errors++;
	restore_flags(flags);
	return;
    }
    if (xnet_debug > 10)
	printk("xnet_rec_frame count=%d proto=%04x\n", bytecount, proto);

    pkt_len = bytecount;

    if (xnet_debug > 12)
	xnet_hexdump(cur_buf, pkt_len - 2);

    /* Malloc up new buffer. */

    if ((pkt_len > 4) &&
	(((proto == PROTO_IP) && (pkt_len > 20)) ||
	 lp->chstate)) {	/* The packet is valid IP packet,
				   or there is someone in character
				   interface to get it */

	skb = alloc_skb(pkt_len, GFP_ATOMIC);
	if (xnet_debug > 10)
	    printk("xnet_rec_frame skb=%08x\n", (int) skb);
	if (skb == NULL) {
	    printk("xnet: %s: Memory squeeze, dropping packet.\n", dev->name);
	    lp->stats.rx_dropped++;
	    restore_flags(flags);
	    return;
	}
	skb->len = (unsigned long) pkt_len;
	skb->dev = dev;
	skb->sk = NULL;
	skb->free = 1;
	/* 'skb->data' points to the start of sk_buff data area. */

	memcpy(skb->data, cur_buf, pkt_len - 2);

	if ((proto == PROTO_IP) && (pkt_len > 20)) {
	    if (xnet_debug > 10)
		printk("xnet_rec_frame send to ip\n");
#ifndef NET02D
	    skb->protocol = htons(ETH_P_IP);
	    /* skb->mac.raw = skb->data *//* ??? */
#endif
	    netif_rx(skb);
	} else if (lp->chstate) {
	    /* It is for pppd */
	    if (xnet_debug > 10)
		printk("xnet_rec_frame put to crxq\n");
	    skb_queue_tail(&lp->crxq, skb);
	    if (lp->xnet_wait_queue)
		wake_up_interruptible(&lp->xnet_wait_queue);
	} else
	    /* It is for ip */
	    printk("xnet: Trying to pass illegal packet to IP \n");
	lp->stats.rx_packets++;

    } else if (xnet_debug > 10)
	printk("xnet_rec_frame no ones packet, droping it\n");

    restore_flags(flags);
}
/*==========================================
!
! Re-initialize receiving afret receive error
! like overrun or crc error
!
\*============================================*/
static void xnet_recfail(struct device *dev, struct xnet_local *lp)
{
    unsigned long dma_abs;
    unsigned dmachan;

    clear_dma_ff(lp->rxdma);

    dma_abs = (unsigned long) (lp->rcvbuf);
    dmachan = lp->rxdma;
    set_dma_addr(dmachan, dma_abs);
    set_dma_count(dmachan, lp->bufsiz);
    outp(lp->channel + 1, 0x30);	/* Reset sr0 bits with error reset */
    lp->stats.rx_errors++;
}

static void hardware_send_packet(struct xnet_local *lp, struct sk_buff *skb)
{
    char kickflag;
    unsigned long flags;

    if (xnet_debug > 19)
	printk("hardware_send_packet: lp=%08x skb=%08x\n", (int) lp, (int) skb);

    lp->stats.tx_packets++;

    save_flags(flags);
    cli();
    if (((xnet_dev->trans_start + TX_TIMEOUT) < jiffies) &&
	(lp->tstate == BUSY)) {
	lp->tstate = IDLE;
	printk("xnet:hardware_send_packet: transmit timed out %d %d\n",
	       xnet_dev->trans_start, jiffies);

    }
/*    kickflag = (skb_peek(&lp->sndq) == NULL) && (lp->sndbuf == NULL); */
    kickflag = (lp->tstate == IDLE);
    restore_flags(flags);

    skb_queue_tail(&lp->sndq, skb);
    if (kickflag) {
	xnet_send_next_frame(lp);	/* process interrupt */
    }
}
/*==================================================== 
!
! send packet routine, called by Linux
! 
!
\*==================================================*/

static int xnet_send_packet(struct sk_buff *skb, struct device *dev)
{
    struct xnet_local *lp = (struct xnet_local *) dev->priv;

    if (xnet_debug > 19)
	printk("xnet_send_packet: dev=%08x skb=%08x\n", (int) dev, (int) skb);

    /* If some higher layer thinks we've missed an tx-done interrupt
       we are passed NULL. Caution: dev_tint() handles the cli()/sti()
       itself. */
    if (skb == NULL) {
	xnet_dev_tinit(dev);
	return 0;
    }
    hardware_send_packet(lp, skb);
    dev->trans_start = jiffies;

    return 0;

}
/*========================================================*\
!
!
! A-Channel receive interupt handler
! Enters here from xnet_interupt() main interupt handler
!
!
\*========================================================*/

static void xnet_a_rxs(struct device *dev, register struct xnet_local *lp, int sra0)
{
    if (xnet_debug > 19)
	printk("xnet_a_rxs sra=%02x\n", sra0);
    if (sra0 & 0x80) {		/* End of frame ?? */
	if (sra0 & 0x40) {
	    if (xnet_debug > 1)
		printk("%ld xnet: rec CRC error\n", jiffies);
	    xnet_recfail(dev, lp);
	    lp->stats.rx_crc_errors += 1;
	} else
	    xnet_rec_frame(dev, lp);
    }
    if (sra0 & 0x20) {		/* Read data Overrun ?? */
	if (xnet_debug > 1)
	    printk("%ld slpp1: rec overrun\n", jiffies);
	xnet_recfail(dev, lp);
	lp->stats.rx_over_errors += 1;
    }
    if (sra0 & 0x02) {		/* Sending Abort ?? */
	outp(lp->channel + 1, 0x30);	/*Reset sr0 bits with error reset */
    }
}
/*======================================================
!
!
! SCC ES (Extended status interrupt handler
! Handles here frame send complete, DCD changed and timer interrupts
! Enters here from xnet_interupt() main interupt handler
!
\*====================================================*/


static void xnet_a_es(struct device *dev, register struct xnet_local *lp, int sra0)
{
    int es, dlc, resid, z;

    outp(lp->ioaddr + 1, 1);	/* Status reg 1 */
    es = inb(lp->ioaddr + 1);
    outp(lp->ioaddr + 1, 8);	/* DLC L */
    dlc = 0xff & inb(lp->ioaddr + 1);
    outp(lp->ioaddr + 1, 9);	/* DLC H */
    dlc = dlc + ((0xff & inb(lp->ioaddr + 1)) << 8);

    if (es & 0x04) {		/* Frame send */

	resid = get_dma_residue(lp->txdma);
	if (xnet_debug > 6)
	    printk("%d slppintr1 all sent: %x %d %d\n",
		   (int) jiffies, es + (sra0 << 8), dlc, lp->txlen);
	if ((xnet_debug > 0) && (dlc != lp->txlen)) {
	    printk("%d xnet sent wrong size: es=%x dlc=%d txlen=%d dma=%d\n",
		   (int) jiffies, es, dlc, lp->txlen, resid);
	    /*      slpp_slpp[1].stati.slpps_fifounder+=1; *//* netstat shows this one */
	}
	xnet_send_next_frame(lp);
    }
    if (es & 0x40) {
	outp(lp->channel + 1, 0xc0);	/* Reset E/S TxUnd/Eom bit */
    }
    if ((es & 0x1) && lp->ifdelay1) {	/* BRG 0 */
	if (lp->ifwait <= 1) {
	    outp(lp->channel + 1, 11);
	    z = inb(lp->channel + 1);
	    z &= 0xfe;		/* Disable brg IE */
	    outp(lp->channel + 1, 11);
	    outp(lp->channel + 1, z);
	}
	if (lp->ifwait == 1) {
	    xnet_send_next_frame(lp);
	}
	if (lp->ifwait)
	    lp->ifwait -= 1;
    }
    if (es & 0x2) {
	if (xnet_debug > 24)
	    printk("%ld slpp1: idle detect es=%x\n",
		   jiffies, es);
    }
    outp(lp->ioaddr + 1, 0x10);	/* Reset E/S interupt reg 1 */
}

/*==================================================================
!
!
! Interupt entry point
! Comes here from hardware IRQ
!
\*==================================================================*/

static void xnet_interrupt(int irq, struct pt_regs *regs)
{

    struct device *dev = (struct device *) (irq2dev_map[irq]);
    struct xnet_local *lp;
    int ioaddr;
    unsigned int sra0, vec;

    if (xnet_debug > 19)
	printk("xnet_interrupt(): irq %d\n", irq);

    if (dev == NULL) {
	printk("xnet_interrupt(): irq %d for unknown device.\n", irq);
	return;
    }
    dev->interrupt = 1;

    ioaddr = dev->base_addr;
    lp = (struct xnet_local *) dev->priv;

    sra0 = inb(lp->ioaddr + 1);

    outp(lp->ioaddr + 3, 0x2);	/* SR 2B */
    vec = inb(lp->ioaddr + 3);
    if (xnet_debug > 19)
	printk("xnet: irq %x\n", (sra0 << 8) + vec);
    switch (vec & 7) {
    case 0:			/* Tx B */
    case 1:			/* E/S B */
    case 2:			/* Rx B */
    case 3:			/* Rx special B */
    case 4:			/* Tx A */
    case 5:
	xnet_a_es(dev, lp, sra0);	/* E/S A */
	break;
    case 6:			/* Rx A */
	break;
    case 7:
	xnet_a_rxs(dev, lp, sra0);	/* Rx Special A */
	break;
    }
    outp(lp->ioaddr + 1, 0x38);	/* End of Interupt */
    dev->interrupt = 0;
    return;
}



/*============================================================
!
! Prepare SCC and DMAC for receiving next frame 
!
\*============================================================*/

static void xnet_rec_next_frame(register struct xnet_local *lp)
{
    unsigned long flags;

    save_flags(flags);
    cli();

    if (xnet_debug > 19)
	printk("xnet_rec_next_frame\n");

    outp(lp->channel + 1, 3);	/* CR 3 */
    outp(lp->channel + 1, lp->cr3 = lp->cr3 | 0x01);	/* Enable Receive  */
    outp(lp->channel + 1, 0x40);	/* Reset RX CRC counter */

    setup_rx_dma(lp);

    restore_flags(flags);
}

/*=======================================================
!
! Set scc a-channel to DMA mode
!
\*=======================================================*/
static void xnet_set_dma_mode(register struct xnet_local *lp)
{
    outp(lp->channel + 1, 2);
    outp(lp->channel + 1, 0x59);	/* A channel DMA, B IRQ */
    outp(lp->channel + 1, 1);	/* CTL reg 1 */
    outp(lp->channel + 1, 0x4f);	/* Tx and RX DMA enable */
}

static void xnet_setsccclk(register struct xnet_local *lp, int mode)
{
    int j;
    unsigned long flags;
    save_flags(flags);
    cli();
    if (xnet_debug > 4)
	printk("%d scc_setsccclk %x %d\n", (int) jiffies, lp->channel, mode);
    lp->cr15 = mode;
    outp(lp->channel + 1, 15);
    for (j = 0; j < 10; j++)
	mb();
    outp(lp->channel + 1, mode);	/* Cr 15 rx/tx clock selection */
    restore_flags(flags);
}


/*=============================================*\
!
! Initialize SCC with initialization table
!
\*=============================================*/
static void xnet_initScc(int channel, char *table)
{
    int i, j;
    unsigned long flags;

    printk("xnet_initScc %04x %02x ", channel, table[0]);

    save_flags(flags);
    cli();
    for (i = 0; i < table[0]; i++) {
	for (j = 0; j < 10; j++)
	    mb();		/* Just a delay loop */
	outp(channel + 1, table[i + 1]);
	printk("%02x ", (unsigned char) table[i + 1]);
    }
    printk("\n");
    restore_flags(flags);
}

/*===========================================================
!
! Set scc communication mode (async,hdlc, internally clocked hdlc 
!
\*=========================================================*/
static void xnet_setsccmode(register struct xnet_local *lp, int mode)
{
    int i;
    if (xnet_debug > 4)
	printk("%d xnet_setsccmode %x %d\n", (int) jiffies, lp->channel, mode);

    switch (mode) {
    case 1:
	xnet_initScc(lp->channel, SccAsyncTable);
	lp->cr5 = 0x68;
	lp->cr3 = 0xc1;
	break;
    case 2:
	outp(lp->channel, 0x18);	/* Reset channel */
	for (i = 0; i < 1000; i++)
	    mb();
	xnet_initScc(lp->channel, SccSyncTable);
	lp->cr5 = 0x21;
	lp->cr3 = 0x52;
	break;
    case 3:
	xnet_initScc(lp->channel, SccHdlcXTable);
	lp->cr5 = 0x61;
	lp->cr3 = 0xc0;
	break;
    case 4:
	xnet_initScc(lp->channel, SccHdlcITable);
	lp->cr5 = 0x61;
	lp->cr3 = 0xc0;
	break;
    case 5:
	xnet_initScc(lp->channel, SccHdlcDTable);
	lp->cr5 = 0x61;
	lp->cr3 = 0xc0;
	break;
    }
}
/*====================================================
!
! Dump region of memory to console screen in hex format
!
\*==================================================*/

static void xnet_hexdump(unsigned char *p, int len)
{
    int i;
    unsigned char c;

    for (i = 0; i < len; i++) {
	c = *p++;
	printk(" %02x", c);
	if ((i & 15) == 15)
	    printk("\n");
    }
    printk("\n");
}

/*
   !
   !
   ! Some Null support routines for Linux
   !
   |
 */


#ifdef NET02D			/* Pre 1.3.0 */
static int xnet_header(unsigned char *buff, struct device *dev,
		       unsigned short type, void *daddr, void *saddr,
		       unsigned len, struct sk_buff *skb)
{
    struct ppp_hdr *hdr;

    hdr = (struct ppp_hdr *) buff;

    if (xnet_debug > 19)
	printk("xnet_header: dev=%08x\n", (int) dev);
    hdr->addr = 0xff;
    hdr->ctl = 3;
    hdr->proto = htons(PROTO_IP);
    return (dev->hard_header_len);
}
#else				/* 1.3.0 (and latter) */
static int xnet_header(struct sk_buff *skb, struct device *dev,
		       unsigned short type, void *daddr, void *saddr,
		       unsigned len)
{
    struct ppp_hdr *hdr = (struct ppp_hdr *) skb_push(skb, 4);

    if (xnet_debug > 19)
	printk("xnet_header: dev=%08x\n", (int) dev);
    hdr->addr = 0xff;
    hdr->ctl = 3;
    hdr->proto = htons(PROTO_IP);
    return (dev->hard_header_len);
}
#endif

static int xnet_rebuild_header(void *buff, struct device *dev,
			       unsigned long raddr, struct sk_buff *skb)
{
    if (xnet_debug > 19)
	printk("xnet_rebuild_header: dev=%08x\n", (int) dev);
    return (0);
}

#ifdef NET02D			/* Pre 1.3.0 */
static unsigned short xnet_type_trans(struct sk_buff *skb, struct device *dev)
{
    if (xnet_debug > 19)
	printk("xnet_type_trans: dev=%08x\n", (int) dev);
    return (htons(ETH_P_IP));
}
#endif

/* Get the current statistics.
   This can be called with the card open, or closed. */
static struct enet_statistics *xnet_get_stats(struct device *dev)
{
    struct xnet_local *lp = (struct xnet_local *) dev->priv;

    return &lp->stats;
}

#ifdef MODULE
char kernel_version[] = UTS_RELEASE;

static struct device xnet0_dev =
{
    "xnet0",			/* Xnet board                           */
    0x0,			/* recv memory end                      */
    0x0,			/* recv memory start                    */
    0x0,			/* memory end                           */
    0x0,			/* memory start                         */
    0x300,			/* base I/O address                     */
    5,				/* IRQ                                  */
    0, 0, 0,			/* flags                                */
    NULL,			/* next device                          */
    xnet_init			/* xnet_init should set up the rest     */
};


int init_module(void)
{
    int err = 0;
    switch (xnet_base_addr) {
    case 0x100:
    case 0x180:
    case 0x200:
    case 0x280:
    case 0x300:
    case 0x380:
	break;
    default:
	printk("XNET: Bad   xnet_base_addr  defined: 0x%x\n", xnet_base_addr);
	err = -EIO;
	break;
    }
    if ((xnet_irq < 2 || xnet_irq > 7) && xnet_irq != 9) {
	printk("XNET: Bad  xnet_irq  defined: %d\n", xnet_irq);
	err = -EIO;
    }
    if (xnet_dma_in < 1 || xnet_dma_in > 3) {
	printk("XNET: Bad  xnet_dma_in  defined: %d\n", xnet_dma_in);
	err = -EIO;
    }
    if (xnet_dma_out < 1 || xnet_dma_out > 3) {
	printk("XNET: Bad  xnet_dma_out  defined: %d\n", xnet_dma_out);
	err = -EIO;
    }
    if (err)
	return err;

    xnet0_dev.base_addr = xnet_base_addr;
    xnet0_dev.irq = xnet_irq;

    if (register_netdev(&xnet0_dev) != 0)
	return -EIO;
    return 0;
}

void cleanup_module(void)
{
    printk("XNET: cleanup_module(), xnet_major = %d\n", xnet_major);
    if (MOD_IN_USE) {
	printk("XNET: device busy, remove delayed\n");
	return;
    }
    free_irq(xnet0_dev.irq);

    if (malloc_dmabuf != NULL)
	kfree(malloc_dmabuf);

    release_region(xnet_base_addr, 16);
    unregister_chrdev(xnet_major, CHRDEVNAME);
    unregister_netdev(&xnet0_dev);
}
#endif				/* MODULE */



/*
 * Local variables:
 *  compile-command: "gcc -DMODULE -DCONFIG_MODVERSIONS -D__KERNEL__ -I/usr/src/linux/net/inet  -Wstrict-prototypes -Wall -O6 -m486 -c xnet.c"
 *  version-control: t
 *  kept-new-versions: 10
 *  tab-width: 8
 * End:
 */
