fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / char / synclinkmp.c
index 1361ea0..8f4d67a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: synclinkmp.c,v 4.22 2004/06/03 14:50:10 paulkf Exp $
+ * $Id: synclinkmp.c,v 4.38 2005/07/15 13:29:44 paulkf Exp $
  *
  * Device driver for Microgate SyncLink Multiport
  * high speed multiprotocol serial adapter.
@@ -34,7 +34,6 @@
 
 #define MAX_DEVICES 12
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/signal.h>
@@ -55,7 +54,6 @@
 #include <linux/netdevice.h>
 #include <linux/vmalloc.h>
 #include <linux/init.h>
-#include <asm/serial.h>
 #include <linux/delay.h>
 #include <linux/ioctl.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/dma.h>
-#include <asm/bitops.h>
+#include <linux/bitops.h>
 #include <asm/types.h>
 #include <linux/termios.h>
 #include <linux/workqueue.h>
+#include <linux/hdlc.h>
 
-#ifdef CONFIG_SYNCLINK_SYNCPPP_MODULE
-#define CONFIG_SYNCLINK_SYNCPPP 1
-#endif
-
-#ifdef CONFIG_SYNCLINK_SYNCPPP
-#include <net/syncppp.h>
+#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINKMP_MODULE))
+#define SYNCLINK_GENERIC_HDLC 1
+#else
+#define SYNCLINK_GENERIC_HDLC 0
 #endif
 
 #define GET_USER(error,value,addr) error = get_user(value,addr)
@@ -284,12 +281,11 @@ typedef struct _synclinkmp_info {
        int netcount;
        int dosyncppp;
        spinlock_t netlock;
-#ifdef CONFIG_SYNCLINK_SYNCPPP
-       struct ppp_device pppdev;
-       char netname[10];
+
+#if SYNCLINK_GENERIC_HDLC
        struct net_device *netdev;
-       struct net_device_stats netstats;
 #endif
+
 } SLMP_INFO;
 
 #define MGSL_MAGIC 0x5401
@@ -361,12 +357,7 @@ typedef struct _synclinkmp_info {
 #define TMCS   0x64
 #define TEPR   0x65
 
-/*
- *  FIXME: DAR here clashed with asm-ppc/reg.h and asm-sh/.../dma.h
- */
-#undef DAR
 /* DMA Controller Register macros */
-#define DAR    0x80
 #define DARL   0x80
 #define DARH   0x81
 #define DARB   0x82
@@ -462,8 +453,6 @@ typedef struct _synclinkmp_info {
 #define CRCE   BIT2
 
 
-#define jiffies_from_ms(a) ((((a) * HZ)/1000)+1)
-
 /*
  * Global linked list of SyncLink devices
  */
@@ -491,14 +480,14 @@ static int debug_level = 0;
 static int maxframe[MAX_DEVICES] = {0,};
 static int dosyncppp[MAX_DEVICES] = {0,};
 
-MODULE_PARM(break_on_load,"i");
-MODULE_PARM(ttymajor,"i");
-MODULE_PARM(debug_level,"i");
-MODULE_PARM(maxframe,"1-" __MODULE_STRING(MAX_DEVICES) "i");
-MODULE_PARM(dosyncppp,"1-" __MODULE_STRING(MAX_DEVICES) "i");
+module_param(break_on_load, bool, 0);
+module_param(ttymajor, int, 0);
+module_param(debug_level, int, 0);
+module_param_array(maxframe, int, NULL, 0);
+module_param_array(dosyncppp, int, NULL, 0);
 
 static char *driver_name = "SyncLink MultiPort driver";
-static char *driver_version = "$Revision: 4.22 $";
+static char *driver_version = "$Revision: 4.38 $";
 
 static int synclinkmp_init_one(struct pci_dev *dev,const struct pci_device_id *ent);
 static void synclinkmp_remove_one(struct pci_dev *dev);
@@ -524,19 +513,15 @@ static struct tty_driver *serial_driver;
 /* number of characters left in xmit buffer before we ask for more */
 #define WAKEUP_CHARS 256
 
-#ifndef MIN
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
-
 
 /* tty callbacks */
 
 static int  open(struct tty_struct *tty, struct file * filp);
 static void close(struct tty_struct *tty, struct file * filp);
 static void hangup(struct tty_struct *tty);
-static void set_termios(struct tty_struct *tty, struct termios *old_termios);
+static void set_termios(struct tty_struct *tty, struct ktermios *old_termios);
 
-static int  write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count);
+static int  write(struct tty_struct *tty, const unsigned char *buf, int count);
 static void put_char(struct tty_struct *tty, unsigned char ch);
 static void send_xchar(struct tty_struct *tty, char ch);
 static void wait_until_sent(struct tty_struct *tty, int timeout);
@@ -553,20 +538,12 @@ static void throttle(struct tty_struct * tty);
 static void unthrottle(struct tty_struct * tty);
 static void set_break(struct tty_struct *tty, int break_state);
 
-/* sppp support and callbacks */
-
-#ifdef CONFIG_SYNCLINK_SYNCPPP
-static void sppp_init(SLMP_INFO *info);
-static void sppp_delete(SLMP_INFO *info);
-static void sppp_rx_done(SLMP_INFO *info, char *buf, int size);
-static void sppp_tx_done(SLMP_INFO *info);
-
-static int  sppp_cb_open(struct net_device *d);
-static int  sppp_cb_close(struct net_device *d);
-static int  sppp_cb_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
-static int  sppp_cb_tx(struct sk_buff *skb, struct net_device *dev);
-static void sppp_cb_tx_timeout(struct net_device *dev);
-static struct net_device_stats *sppp_cb_net_stats(struct net_device *dev);
+#if SYNCLINK_GENERIC_HDLC
+#define dev_to_port(D) (dev_to_hdlc(D)->priv)
+static void hdlcdev_tx_done(SLMP_INFO *info);
+static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size);
+static int  hdlcdev_init(SLMP_INFO *info);
+static void hdlcdev_exit(SLMP_INFO *info);
 #endif
 
 /* ioctl handlers */
@@ -579,7 +556,6 @@ static int  set_txidle(SLMP_INFO *info, int idle_mode);
 static int  tx_enable(SLMP_INFO *info, int enable);
 static int  tx_abort(SLMP_INFO *info);
 static int  rx_enable(SLMP_INFO *info, int enable);
-static int  map_status(int signals);
 static int  modem_input_wait(SLMP_INFO *info,int arg);
 static int  wait_mgsl_event(SLMP_INFO *info, int __user *mask_ptr);
 static int  tiocmget(struct tty_struct *tty, struct file *file);
@@ -628,7 +604,7 @@ static void enable_loopback(SLMP_INFO *info, int enable);
 static void set_rate(SLMP_INFO *info, u32 data_rate);
 
 static int  bh_action(SLMP_INFO *info);
-static void bh_handler(void* Context);
+static void bh_handler(struct work_struct *work);
 static void bh_receive(SLMP_INFO *info);
 static void bh_transmit(SLMP_INFO *info);
 static void bh_status(SLMP_INFO *info);
@@ -668,7 +644,7 @@ static unsigned char tx_active_fifo_level = 16;     // tx request FIFO activation le
 static unsigned char tx_negate_fifo_level = 32;        // tx request FIFO negation level in bytes
 
 static u32 misc_ctrl_value = 0x007e4040;
-static u32 lcr1_brdr_value = 0x0080002d;
+static u32 lcr1_brdr_value = 0x00800028;
 
 static u32 read_ahead_count = 8;
 
@@ -722,6 +698,29 @@ static inline int sanity_check(SLMP_INFO *info,
        return 0;
 }
 
+/**
+ * line discipline callback wrappers
+ *
+ * The wrappers maintain line discipline references
+ * while calling into the line discipline.
+ *
+ * ldisc_receive_buf  - pass receive data to line discipline
+ */
+
+static void ldisc_receive_buf(struct tty_struct *tty,
+                             const __u8 *data, char *flags, int count)
+{
+       struct tty_ldisc *ld;
+       if (!tty)
+               return;
+       ld = tty_ldisc_ref(tty);
+       if (ld) {
+               if (ld->receive_buf)
+                       ld->receive_buf(tty, data, flags, count);
+               tty_ldisc_deref(ld);
+       }
+}
+
 /* tty callbacks */
 
 /* Called when a port is opened.  Init and enable port.
@@ -800,7 +799,7 @@ static int open(struct tty_struct *tty, struct file *filp)
 cleanup:
        if (retval) {
                if (tty->count == 1)
-                       info->tty = NULL;/* tty layer will release tty struct */
+                       info->tty = NULL; /* tty layer will release tty struct */
                if(info->count)
                        info->count--;
        }
@@ -869,8 +868,7 @@ static void close(struct tty_struct *tty, struct file *filp)
        if (tty->driver->flush_buffer)
                tty->driver->flush_buffer(tty);
 
-       if (tty->ldisc.flush_buffer)
-               tty->ldisc.flush_buffer(tty);
+       tty_ldisc_flush(tty);
 
        shutdown(info);
 
@@ -879,8 +877,7 @@ static void close(struct tty_struct *tty, struct file *filp)
 
        if (info->blocked_open) {
                if (info->close_delay) {
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       schedule_timeout(info->close_delay);
+                       msleep_interruptible(jiffies_to_msecs(info->close_delay));
                }
                wake_up_interruptible(&info->open_wait);
        }
@@ -921,7 +918,7 @@ static void hangup(struct tty_struct *tty)
 
 /* Set new termios settings
  */
-static void set_termios(struct tty_struct *tty, struct termios *old_termios)
+static void set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 {
        SLMP_INFO *info = (SLMP_INFO *)tty->driver_data;
        unsigned long flags;
@@ -973,16 +970,15 @@ static void set_termios(struct tty_struct *tty, struct termios *old_termios)
  * Arguments:
  *
  *     tty             pointer to tty information structure
- *     from_user       flag: 1 = from user process
  *     buf             pointer to buffer containing send data
  *     count           size of send data in bytes
  *
  * Return Value:       number of characters written
  */
-static int write(struct tty_struct *tty, int from_user,
+static int write(struct tty_struct *tty,
                 const unsigned char *buf, int count)
 {
-       int     c, ret = 0, err;
+       int     c, ret = 0;
        SLMP_INFO *info = (SLMP_INFO *)tty->driver_data;
        unsigned long flags;
 
@@ -993,7 +989,7 @@ static int write(struct tty_struct *tty, int from_user,
        if (sanity_check(info, tty->name, "write"))
                goto cleanup;
 
-       if (!tty || !info->tx_buf)
+       if (!info->tx_buf)
                goto cleanup;
 
        if (info->params.mode == MGSL_MODE_HDLC) {
@@ -1009,29 +1005,19 @@ static int write(struct tty_struct *tty, int from_user,
                        tx_load_dma_buffer(info, info->tx_buf, info->tx_count);
                        goto start;
                }
-               if (!from_user) {
-                       ret = info->tx_count = count;
-                       tx_load_dma_buffer(info, buf, count);
-                       goto start;
-               }
+               ret = info->tx_count = count;
+               tx_load_dma_buffer(info, buf, count);
+               goto start;
        }
 
        for (;;) {
-               c = MIN(count,
-                       MIN(info->max_frame_size - info->tx_count - 1,
+               c = min_t(int, count,
+                       min(info->max_frame_size - info->tx_count - 1,
                            info->max_frame_size - info->tx_put));
                if (c <= 0)
                        break;
                        
-               if (from_user) {
-                       COPY_FROM_USER(err, info->tx_buf + info->tx_put, buf, c);
-                       if (err) {
-                               if (!ret)
-                                       ret = -EFAULT;
-                               break;
-                       }
-               } else
-                       memcpy(info->tx_buf + info->tx_put, buf, c);
+               memcpy(info->tx_buf + info->tx_put, buf, c);
 
                spin_lock_irqsave(&info->lock,flags);
                info->tx_put += c;
@@ -1082,7 +1068,7 @@ static void put_char(struct tty_struct *tty, unsigned char ch)
        if (sanity_check(info, tty->name, "put_char"))
                return;
 
-       if (!tty || !info->tx_buf)
+       if (!info->tx_buf)
                return;
 
        spin_lock_irqsave(&info->lock,flags);
@@ -1161,12 +1147,11 @@ static void wait_until_sent(struct tty_struct *tty, int timeout)
                char_time = 1;
 
        if (timeout)
-               char_time = MIN(char_time, timeout);
+               char_time = min_t(unsigned long, char_time, timeout);
 
        if ( info->params.mode == MGSL_MODE_HDLC ) {
                while (info->tx_active) {
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       schedule_timeout(char_time);
+                       msleep_interruptible(jiffies_to_msecs(char_time));
                        if (signal_pending(current))
                                break;
                        if (timeout && time_after(jiffies, orig_jiffies + timeout))
@@ -1176,8 +1161,7 @@ static void wait_until_sent(struct tty_struct *tty, int timeout)
                //TODO: determine if there is something similar to USC16C32
                //      TXSTATUS_ALL_SENT status
                while ( info->tx_active && info->tx_enabled) {
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       schedule_timeout(char_time);
+                       msleep_interruptible(jiffies_to_msecs(char_time));
                        if (signal_pending(current))
                                break;
                        if (timeout && time_after(jiffies, orig_jiffies + timeout))
@@ -1275,9 +1259,7 @@ static void flush_buffer(struct tty_struct *tty)
        spin_unlock_irqrestore(&info->lock,flags);
 
        wake_up_interruptible(&tty->write_wait);
-       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-           tty->ldisc.write_wakeup)
-               (tty->ldisc.write_wakeup)(tty);
+       tty_wakeup(tty);
 }
 
 /* throttle (stop) transmitter
@@ -1627,79 +1609,125 @@ static void set_break(struct tty_struct *tty, int break_state)
        spin_unlock_irqrestore(&info->lock,flags);
 }
 
-#ifdef CONFIG_SYNCLINK_SYNCPPP
-
-/* syncppp support and callbacks */
-
-static void cb_setup(struct net_device *dev)
-{
-       dev->open = sppp_cb_open;
-       dev->stop = sppp_cb_close;
-       dev->hard_start_xmit = sppp_cb_tx;
-       dev->do_ioctl = sppp_cb_ioctl;
-       dev->get_stats = sppp_cb_net_stats;
-       dev->tx_timeout = sppp_cb_tx_timeout;
-       dev->watchdog_timeo = 10*HZ;
-}
+#if SYNCLINK_GENERIC_HDLC
 
-static void sppp_init(SLMP_INFO *info)
+/**
+ * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.)
+ * set encoding and frame check sequence (FCS) options
+ *
+ * dev       pointer to network device structure
+ * encoding  serial encoding setting
+ * parity    FCS setting
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
+                         unsigned short parity)
 {
-       struct net_device *d;
+       SLMP_INFO *info = dev_to_port(dev);
+       unsigned char  new_encoding;
+       unsigned short new_crctype;
 
-       sprintf(info->netname,"mgslm%dp%d",info->adapter_num,info->port_num);
+       /* return error if TTY interface open */
+       if (info->count)
+               return -EBUSY;
 
-       d = alloc_netdev(0, info->netname, cb_setup);
-       if (!d) {
-               printk(KERN_WARNING "%s: alloc_netdev failed.\n",
-                                               info->netname);
-               return;
+       switch (encoding)
+       {
+       case ENCODING_NRZ:        new_encoding = HDLC_ENCODING_NRZ; break;
+       case ENCODING_NRZI:       new_encoding = HDLC_ENCODING_NRZI_SPACE; break;
+       case ENCODING_FM_MARK:    new_encoding = HDLC_ENCODING_BIPHASE_MARK; break;
+       case ENCODING_FM_SPACE:   new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break;
+       case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break;
+       default: return -EINVAL;
        }
 
-       info->if_ptr = &info->pppdev;
-       info->netdev = info->pppdev.dev = d;
-
-       d->irq = info->irq_level;
-       d->priv = info;
+       switch (parity)
+       {
+       case PARITY_NONE:            new_crctype = HDLC_CRC_NONE; break;
+       case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break;
+       case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break;
+       default: return -EINVAL;
+       }
 
-       sppp_attach(&info->pppdev);
-       cb_setup(d);
+       info->params.encoding = new_encoding;
+       info->params.crc_type = new_crctype;
 
-       if (register_netdev(d)) {
-               printk(KERN_WARNING "%s: register_netdev failed.\n", d->name);
-               sppp_detach(info->netdev);
-               info->netdev = NULL;
-               info->pppdev.dev = NULL;
-               free_netdev(d);
-               return;
-       }
+       /* if network interface up, reprogram hardware */
+       if (info->netcount)
+               program_hw(info);
 
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("sppp_init(%s)\n",info->netname);
+       return 0;
 }
 
-static void sppp_delete(SLMP_INFO *info)
+/**
+ * called by generic HDLC layer to send frame
+ *
+ * skb  socket buffer containing HDLC frame
+ * dev  pointer to network device structure
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev)
 {
+       SLMP_INFO *info = dev_to_port(dev);
+       struct net_device_stats *stats = hdlc_stats(dev);
+       unsigned long flags;
+
        if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("sppp_delete(%s)\n",info->netname);
-       unregister_netdev(info->netdev);
-       sppp_detach(info->netdev);
-       free_netdev(info->netdev);
-       info->netdev = NULL;
-       info->pppdev.dev = NULL;
+               printk(KERN_INFO "%s:hdlc_xmit(%s)\n",__FILE__,dev->name);
+
+       /* stop sending until this frame completes */
+       netif_stop_queue(dev);
+
+       /* copy data to device buffers */
+       info->tx_count = skb->len;
+       tx_load_dma_buffer(info, skb->data, skb->len);
+
+       /* update network statistics */
+       stats->tx_packets++;
+       stats->tx_bytes += skb->len;
+
+       /* done with socket buffer, so free it */
+       dev_kfree_skb(skb);
+
+       /* save start time for transmit timeout detection */
+       dev->trans_start = jiffies;
+
+       /* start hardware transmitter if necessary */
+       spin_lock_irqsave(&info->lock,flags);
+       if (!info->tx_active)
+               tx_start(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       return 0;
 }
 
-static int sppp_cb_open(struct net_device *d)
+/**
+ * called by network layer when interface enabled
+ * claim resources and initialize hardware
+ *
+ * dev  pointer to network device structure
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_open(struct net_device *dev)
 {
-       SLMP_INFO *info = d->priv;
-       int err;
+       SLMP_INFO *info = dev_to_port(dev);
+       int rc;
        unsigned long flags;
 
        if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("sppp_cb_open(%s)\n",info->netname);
+               printk("%s:hdlcdev_open(%s)\n",__FILE__,dev->name);
+
+       /* generic HDLC layer open processing */
+       if ((rc = hdlc_open(dev)))
+               return rc;
 
+       /* arbitrate between network and tty opens */
        spin_lock_irqsave(&info->netlock, flags);
        if (info->count != 0 || info->netcount != 0) {
-               printk(KERN_WARNING "%s: sppp_cb_open returning busy\n", info->netname);
+               printk(KERN_WARNING "%s: hdlc_open returning busy\n", dev->name);
                spin_unlock_irqrestore(&info->netlock, flags);
                return -EBUSY;
        }
@@ -1707,141 +1735,300 @@ static int sppp_cb_open(struct net_device *d)
        spin_unlock_irqrestore(&info->netlock, flags);
 
        /* claim resources and init adapter */
-       if ((err = startup(info)) != 0)
-               goto open_fail;
-
-       /* allow syncppp module to do open processing */
-       if ((err = sppp_open(d)) != 0) {
-               shutdown(info);
-               goto open_fail;
+       if ((rc = startup(info)) != 0) {
+               spin_lock_irqsave(&info->netlock, flags);
+               info->netcount=0;
+               spin_unlock_irqrestore(&info->netlock, flags);
+               return rc;
        }
 
+       /* assert DTR and RTS, apply hardware settings */
        info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
        program_hw(info);
 
-       d->trans_start = jiffies;
-       netif_start_queue(d);
-       return 0;
+       /* enable network layer transmit */
+       dev->trans_start = jiffies;
+       netif_start_queue(dev);
 
-open_fail:
-       spin_lock_irqsave(&info->netlock, flags);
-       info->netcount=0;
-       spin_unlock_irqrestore(&info->netlock, flags);
-       return err;
+       /* inform generic HDLC layer of current DCD status */
+       spin_lock_irqsave(&info->lock, flags);
+       get_signals(info);
+       spin_unlock_irqrestore(&info->lock, flags);
+       if (info->serial_signals & SerialSignal_DCD)
+               netif_carrier_on(dev);
+       else
+               netif_carrier_off(dev);
+       return 0;
 }
 
-static void sppp_cb_tx_timeout(struct net_device *dev)
+/**
+ * called by network layer when interface is disabled
+ * shutdown hardware and release resources
+ *
+ * dev  pointer to network device structure
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_close(struct net_device *dev)
 {
-       SLMP_INFO *info = dev->priv;
+       SLMP_INFO *info = dev_to_port(dev);
        unsigned long flags;
 
        if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("sppp_tx_timeout(%s)\n",info->netname);
+               printk("%s:hdlcdev_close(%s)\n",__FILE__,dev->name);
 
-       info->netstats.tx_errors++;
-       info->netstats.tx_aborted_errors++;
+       netif_stop_queue(dev);
 
-       spin_lock_irqsave(&info->lock,flags);
-       tx_stop(info);
-       spin_unlock_irqrestore(&info->lock,flags);
+       /* shutdown adapter and release resources */
+       shutdown(info);
 
-       netif_wake_queue(dev);
+       hdlc_close(dev);
+
+       spin_lock_irqsave(&info->netlock, flags);
+       info->netcount=0;
+       spin_unlock_irqrestore(&info->netlock, flags);
+
+       return 0;
 }
 
-static int sppp_cb_tx(struct sk_buff *skb, struct net_device *dev)
+/**
+ * called by network layer to process IOCTL call to network device
+ *
+ * dev  pointer to network device structure
+ * ifr  pointer to network interface request structure
+ * cmd  IOCTL command code
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
-       SLMP_INFO *info = dev->priv;
-       unsigned long flags;
+       const size_t size = sizeof(sync_serial_settings);
+       sync_serial_settings new_line;
+       sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync;
+       SLMP_INFO *info = dev_to_port(dev);
+       unsigned int flags;
 
        if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("sppp_tx(%s)\n",info->netname);
+               printk("%s:hdlcdev_ioctl(%s)\n",__FILE__,dev->name);
 
-       netif_stop_queue(dev);
+       /* return error if TTY interface open */
+       if (info->count)
+               return -EBUSY;
 
-       info->tx_count = skb->len;
-       tx_load_dma_buffer(info, skb->data, skb->len);
-       info->netstats.tx_packets++;
-       info->netstats.tx_bytes += skb->len;
-       dev_kfree_skb(skb);
+       if (cmd != SIOCWANDEV)
+               return hdlc_ioctl(dev, ifr, cmd);
 
-       dev->trans_start = jiffies;
+       switch(ifr->ifr_settings.type) {
+       case IF_GET_IFACE: /* return current sync_serial_settings */
 
-       spin_lock_irqsave(&info->lock,flags);
-       if (!info->tx_active)
-               tx_start(info);
-       spin_unlock_irqrestore(&info->lock,flags);
+               ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
+               if (ifr->ifr_settings.size < size) {
+                       ifr->ifr_settings.size = size; /* data size wanted */
+                       return -ENOBUFS;
+               }
 
-       return 0;
+               flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
+                                             HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
+                                             HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
+                                             HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
+
+               switch (flags){
+               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break;
+               case (HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_INT; break;
+               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_TXINT; break;
+               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break;
+               default: new_line.clock_type = CLOCK_DEFAULT;
+               }
+
+               new_line.clock_rate = info->params.clock_speed;
+               new_line.loopback   = info->params.loopback ? 1:0;
+
+               if (copy_to_user(line, &new_line, size))
+                       return -EFAULT;
+               return 0;
+
+       case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */
+
+               if(!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+               if (copy_from_user(&new_line, line, size))
+                       return -EFAULT;
+
+               switch (new_line.clock_type)
+               {
+               case CLOCK_EXT:      flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break;
+               case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break;
+               case CLOCK_INT:      flags = HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG;    break;
+               case CLOCK_TXINT:    flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG;    break;
+               case CLOCK_DEFAULT:  flags = info->params.flags &
+                                            (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
+                                             HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
+                                             HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
+                                             HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN); break;
+               default: return -EINVAL;
+               }
+
+               if (new_line.loopback != 0 && new_line.loopback != 1)
+                       return -EINVAL;
+
+               info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
+                                       HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
+                                       HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
+                                       HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
+               info->params.flags |= flags;
+
+               info->params.loopback = new_line.loopback;
+
+               if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG))
+                       info->params.clock_speed = new_line.clock_rate;
+               else
+                       info->params.clock_speed = 0;
+
+               /* if network interface up, reprogram hardware */
+               if (info->netcount)
+                       program_hw(info);
+               return 0;
+
+       default:
+               return hdlc_ioctl(dev, ifr, cmd);
+       }
 }
 
-static int sppp_cb_close(struct net_device *d)
+/**
+ * called by network layer when transmit timeout is detected
+ *
+ * dev  pointer to network device structure
+ */
+static void hdlcdev_tx_timeout(struct net_device *dev)
 {
-       SLMP_INFO *info = d->priv;
+       SLMP_INFO *info = dev_to_port(dev);
+       struct net_device_stats *stats = hdlc_stats(dev);
        unsigned long flags;
 
        if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("sppp_cb_close(%s)\n",info->netname);
+               printk("hdlcdev_tx_timeout(%s)\n",dev->name);
 
-       /* shutdown adapter and release resources */
-       shutdown(info);
+       stats->tx_errors++;
+       stats->tx_aborted_errors++;
 
-       /* allow syncppp to do close processing */
-       sppp_close(d);
-       netif_stop_queue(d);
+       spin_lock_irqsave(&info->lock,flags);
+       tx_stop(info);
+       spin_unlock_irqrestore(&info->lock,flags);
 
-       spin_lock_irqsave(&info->netlock, flags);
-       info->netcount=0;
-       spin_unlock_irqrestore(&info->netlock, flags);
-       return 0;
+       netif_wake_queue(dev);
 }
 
-static void sppp_rx_done(SLMP_INFO *info, char *buf, int size)
+/**
+ * called by device driver when transmit completes
+ * reenable network layer transmit if stopped
+ *
+ * info  pointer to device instance information
+ */
+static void hdlcdev_tx_done(SLMP_INFO *info)
+{
+       if (netif_queue_stopped(info->netdev))
+               netif_wake_queue(info->netdev);
+}
+
+/**
+ * called by device driver when frame received
+ * pass frame to network layer
+ *
+ * info  pointer to device instance information
+ * buf   pointer to buffer contianing frame data
+ * size  count of data bytes in buf
+ */
+static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size)
 {
        struct sk_buff *skb = dev_alloc_skb(size);
+       struct net_device *dev = info->netdev;
+       struct net_device_stats *stats = hdlc_stats(dev);
+
        if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("sppp_rx_done(%s)\n",info->netname);
+               printk("hdlcdev_rx(%s)\n",dev->name);
+
        if (skb == NULL) {
-               printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n",
-                       info->netname);
-               info->netstats.rx_dropped++;
+               printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", dev->name);
+               stats->rx_dropped++;
                return;
        }
 
        memcpy(skb_put(skb, size),buf,size);
 
-       skb->protocol = htons(ETH_P_WAN_PPP);
-       skb->dev = info->netdev;
-       skb->mac.raw = skb->data;
-       info->netstats.rx_packets++;
-       info->netstats.rx_bytes += size;
+       skb->protocol = hdlc_type_trans(skb, info->netdev);
+
+       stats->rx_packets++;
+       stats->rx_bytes += size;
+
        netif_rx(skb);
-       info->netdev->trans_start = jiffies;
-}
 
-static void sppp_tx_done(SLMP_INFO *info)
-{
-       if (netif_queue_stopped(info->netdev))
-           netif_wake_queue(info->netdev);
+       info->netdev->last_rx = jiffies;
 }
 
-static struct net_device_stats *sppp_cb_net_stats(struct net_device *dev)
+/**
+ * called by device driver when adding device instance
+ * do generic HDLC initialization
+ *
+ * info  pointer to device instance information
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_init(SLMP_INFO *info)
 {
-       SLMP_INFO *info = dev->priv;
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("net_stats(%s)\n",info->netname);
-       return &info->netstats;
+       int rc;
+       struct net_device *dev;
+       hdlc_device *hdlc;
+
+       /* allocate and initialize network and HDLC layer objects */
+
+       if (!(dev = alloc_hdlcdev(info))) {
+               printk(KERN_ERR "%s:hdlc device allocation failure\n",__FILE__);
+               return -ENOMEM;
+       }
+
+       /* for network layer reporting purposes only */
+       dev->mem_start = info->phys_sca_base;
+       dev->mem_end   = info->phys_sca_base + SCA_BASE_SIZE - 1;
+       dev->irq       = info->irq_level;
+
+       /* network layer callbacks and settings */
+       dev->do_ioctl       = hdlcdev_ioctl;
+       dev->open           = hdlcdev_open;
+       dev->stop           = hdlcdev_close;
+       dev->tx_timeout     = hdlcdev_tx_timeout;
+       dev->watchdog_timeo = 10*HZ;
+       dev->tx_queue_len   = 50;
+
+       /* generic HDLC layer callbacks and settings */
+       hdlc         = dev_to_hdlc(dev);
+       hdlc->attach = hdlcdev_attach;
+       hdlc->xmit   = hdlcdev_xmit;
+
+       /* register objects with HDLC layer */
+       if ((rc = register_hdlc_device(dev))) {
+               printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__);
+               free_netdev(dev);
+               return rc;
+       }
+
+       info->netdev = dev;
+       return 0;
 }
 
-static int sppp_cb_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+/**
+ * called by device driver when removing device instance
+ * do generic HDLC cleanup
+ *
+ * info  pointer to device instance information
+ */
+static void hdlcdev_exit(SLMP_INFO *info)
 {
-       SLMP_INFO *info = dev->priv;
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):ioctl %s cmd=%08X\n", __FILE__,__LINE__,
-                       info->netname, cmd );
-       return sppp_do_ioctl(dev, ifr, cmd);
+       unregister_hdlc_device(info->netdev);
+       free_netdev(info->netdev);
+       info->netdev = NULL;
 }
 
-#endif /* ifdef CONFIG_SYNCLINK_SYNCPPP */
+#endif /* CONFIG_HDLC */
 
 
 /* Return next bottom half action to perform.
@@ -1878,9 +2065,9 @@ int bh_action(SLMP_INFO *info)
 
 /* Perform bottom half processing of work items queued by ISR.
  */
-void bh_handler(void* Context)
+void bh_handler(struct work_struct *work)
 {
-       SLMP_INFO *info = (SLMP_INFO*)Context;
+       SLMP_INFO *info = container_of(work, SLMP_INFO, task);
        int action;
 
        if (!info)
@@ -1941,13 +2128,7 @@ void bh_transmit(SLMP_INFO *info)
                        __FILE__,__LINE__,info->device_name);
 
        if (tty) {
-               if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-                   tty->ldisc.write_wakeup) {
-                       if ( debug_level >= DEBUG_LEVEL_BH )
-                               printk( "%s(%d):%s calling ldisc.write_wakeup\n",
-                                       __FILE__,__LINE__,info->device_name);
-                       (tty->ldisc.write_wakeup)(tty);
-               }
+               tty_wakeup(tty);
                wake_up_interruptible(&tty->write_wait);
        }
 }
@@ -1994,16 +2175,15 @@ void isr_rxint(SLMP_INFO * info)
 {
        struct tty_struct *tty = info->tty;
        struct  mgsl_icount *icount = &info->icount;
-       unsigned char status = read_reg(info, SR1);
-       unsigned char status2 = read_reg(info, SR2);
+       unsigned char status = read_reg(info, SR1) & info->ie1_value & (FLGD + IDLD + CDCD + BRKD);
+       unsigned char status2 = read_reg(info, SR2) & info->ie2_value & OVRN;
 
        /* clear status bits */
-       if ( status & (FLGD + IDLD + CDCD + BRKD) )
-               write_reg(info, SR1, 
-                               (unsigned char)(status & (FLGD + IDLD + CDCD + BRKD)));
+       if (status)
+               write_reg(info, SR1, status);
 
-       if ( status2 & OVRN )
-               write_reg(info, SR2, (unsigned char)(status2 & OVRN));
+       if (status2)
+               write_reg(info, SR2, status2);
        
        if ( debug_level >= DEBUG_LEVEL_ISR )
                printk("%s(%d):%s isr_rxint status=%02X %02x\n",
@@ -2019,7 +2199,7 @@ void isr_rxint(SLMP_INFO * info)
                        if ( tty ) {
                                if (!(status & info->ignore_status_mask1)) {
                                        if (info->read_status_mask1 & BRKD) {
-                                               *tty->flip.flag_buf_ptr = TTY_BREAK;
+                                               tty_insert_flip_char(tty, 0, TTY_BREAK);
                                                if (info->flags & ASYNC_SAK)
                                                        do_SAK(tty);
                                        }
@@ -2063,16 +2243,10 @@ void isr_rxrdy(SLMP_INFO * info)
 
        while((status = read_reg(info,CST0)) & BIT0)
        {
+               int flag = 0;
+               int over = 0;
                DataByte = read_reg(info,TRB);
 
-               if ( tty ) {
-                       if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-                               continue;
-
-                       *tty->flip.char_buf_ptr = DataByte;
-                       *tty->flip.flag_buf_ptr = 0;
-               }
-
                icount->rx++;
 
                if ( status & (PE + FRME + OVRN) ) {
@@ -2095,60 +2269,59 @@ void isr_rxrdy(SLMP_INFO * info)
 
                        if ( tty ) {
                                if (status & PE)
-                                       *tty->flip.flag_buf_ptr = TTY_PARITY;
+                                       flag = TTY_PARITY;
                                else if (status & FRME)
-                                       *tty->flip.flag_buf_ptr = TTY_FRAME;
+                                       flag = TTY_FRAME;
                                if (status & OVRN) {
                                        /* Overrun is special, since it's
                                         * reported immediately, and doesn't
                                         * affect the current character
                                         */
-                                       if (tty->flip.count < TTY_FLIPBUF_SIZE) {
-                                               tty->flip.count++;
-                                               tty->flip.flag_buf_ptr++;
-                                               tty->flip.char_buf_ptr++;
-                                               *tty->flip.flag_buf_ptr = TTY_OVERRUN;
-                                       }
+                                       over = 1;
                                }
                        }
                }       /* end of if (error) */
 
                if ( tty ) {
-                       tty->flip.flag_buf_ptr++;
-                       tty->flip.char_buf_ptr++;
-                       tty->flip.count++;
+                       tty_insert_flip_char(tty, DataByte, flag);
+                       if (over)
+                               tty_insert_flip_char(tty, 0, TTY_OVERRUN);
                }
        }
 
        if ( debug_level >= DEBUG_LEVEL_ISR ) {
-               printk("%s(%d):%s isr_rxrdy() flip count=%d\n",
-                       __FILE__,__LINE__,info->device_name,
-                       tty ? tty->flip.count : 0);
                printk("%s(%d):%s rx=%d brk=%d parity=%d frame=%d overrun=%d\n",
                        __FILE__,__LINE__,info->device_name,
                        icount->rx,icount->brk,icount->parity,
                        icount->frame,icount->overrun);
        }
 
-       if ( tty && tty->flip.count )
+       if ( tty )
                tty_flip_buffer_push(tty);
 }
 
-void isr_txeom(SLMP_INFO * info, unsigned char status)
+static void isr_txeom(SLMP_INFO * info, unsigned char status)
 {
        if ( debug_level >= DEBUG_LEVEL_ISR )
                printk("%s(%d):%s isr_txeom status=%02x\n",
                        __FILE__,__LINE__,info->device_name,status);
 
-       /* disable and clear MSCI interrupts */
-       info->ie1_value &= ~(IDLE + UDRN);
-       write_reg(info, IE1, info->ie1_value);
-       write_reg(info, SR1, (unsigned char)(UDRN + IDLE));
-
        write_reg(info, TXDMA + DIR, 0x00); /* disable Tx DMA IRQs */
        write_reg(info, TXDMA + DSR, 0xc0); /* clear IRQs and disable DMA */
        write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */
 
+       if (status & UDRN) {
+               write_reg(info, CMD, TXRESET);
+               write_reg(info, CMD, TXENABLE);
+       } else
+               write_reg(info, CMD, TXBUFCLR);
+
+       /* disable and clear tx interrupts */
+       info->ie0_value &= ~TXRDYE;
+       info->ie1_value &= ~(IDLE + UDRN);
+       write_reg16(info, IE0, (unsigned short)((info->ie1_value << 8) + info->ie0_value));
+       write_reg(info, SR1, (unsigned char)(UDRN + IDLE));
+
        if ( info->tx_active ) {
                if (info->params.mode != MGSL_MODE_ASYNC) {
                        if (status & UDRN)
@@ -2168,9 +2341,9 @@ void isr_txeom(SLMP_INFO * info, unsigned char status)
                        set_signals(info);
                }
 
-#ifdef CONFIG_SYNCLINK_SYNCPPP
+#if SYNCLINK_GENERIC_HDLC
                if (info->netcount)
-                       sppp_tx_done(info);
+                       hdlcdev_tx_done(info);
                else
 #endif
                {
@@ -2189,10 +2362,10 @@ void isr_txeom(SLMP_INFO * info, unsigned char status)
  */
 void isr_txint(SLMP_INFO * info)
 {
-       unsigned char status = read_reg(info, SR1);
+       unsigned char status = read_reg(info, SR1) & info->ie1_value & (UDRN + IDLE + CCTS);
 
        /* clear status bits */
-       write_reg(info, SR1, (unsigned char)(status & (UDRN + IDLE + CCTS)));
+       write_reg(info, SR1, status);
 
        if ( debug_level >= DEBUG_LEVEL_ISR )
                printk("%s(%d):%s isr_txint status=%02x\n",
@@ -2221,6 +2394,14 @@ void isr_txrdy(SLMP_INFO * info)
                printk("%s(%d):%s isr_txrdy() tx_count=%d\n",
                        __FILE__,__LINE__,info->device_name,info->tx_count);
 
+       if (info->params.mode != MGSL_MODE_ASYNC) {
+               /* disable TXRDY IRQ, enable IDLE IRQ */
+               info->ie0_value &= ~TXRDYE;
+               info->ie1_value |= IDLE;
+               write_reg16(info, IE0, (unsigned short)((info->ie1_value << 8) + info->ie0_value));
+               return;
+       }
+
        if (info->tty && (info->tty->stopped || info->tty->hw_stopped)) {
                tx_stop(info);
                return;
@@ -2275,13 +2456,6 @@ void isr_rxdmaerror(SLMP_INFO * info)
 
 void isr_txdmaok(SLMP_INFO * info)
 {
-       /* BIT7 = EOT (end of transfer, used for async mode)
-        * BIT6 = EOM (end of message/frame, used for sync mode)
-        *
-        * We don't look at DMA status because only EOT is enabled
-        * and we always clear and disable all tx DMA IRQs.
-        */
-//     unsigned char dma_status = read_reg(info,TXDMA + DSR) & 0xc0;
        unsigned char status_reg1 = read_reg(info, SR1);
 
        write_reg(info, TXDMA + DIR, 0x00);     /* disable Tx DMA IRQs */
@@ -2292,19 +2466,10 @@ void isr_txdmaok(SLMP_INFO * info)
                printk("%s(%d):%s isr_txdmaok(), status=%02x\n",
                        __FILE__,__LINE__,info->device_name,status_reg1);
 
-       /* If transmitter already idle, do end of frame processing,
-        * otherwise enable interrupt for tx IDLE.
-        */
-       if (status_reg1 & IDLE)
-               isr_txeom(info, IDLE);
-       else {
-               /* disable and clear underrun IRQ, enable IDLE interrupt */
-               info->ie1_value |= IDLE;
-               info->ie1_value &= ~UDRN;
-               write_reg(info, IE1, info->ie1_value);
-
-               write_reg(info, SR1, UDRN);
-       }
+       /* program TXRDY as FIFO empty flag, enable TXRDY IRQ */
+       write_reg16(info, TRC0, 0);
+       info->ie0_value |= TXRDYE;
+       write_reg(info, IE0, info->ie0_value);
 }
 
 void isr_txdmaerror(SLMP_INFO * info)
@@ -2358,12 +2523,16 @@ void isr_io_pin( SLMP_INFO *info, u16 status )
                        icount->dcd++;
                        if (status & SerialSignal_DCD) {
                                info->input_signal_events.dcd_up++;
-#ifdef CONFIG_SYNCLINK_SYNCPPP
-                               if (info->netcount)
-                                       sppp_reopen(info->netdev);
-#endif
                        } else
                                info->input_signal_events.dcd_down++;
+#if SYNCLINK_GENERIC_HDLC
+                       if (info->netcount) {
+                               if (status & SerialSignal_DCD)
+                                       netif_carrier_on(info->netdev);
+                               else
+                                       netif_carrier_off(info->netdev);
+                       }
+#endif
                }
                if (status & MISCSTATUS_CTS_LATCHED)
                {
@@ -2429,8 +2598,7 @@ void isr_io_pin( SLMP_INFO *info, u16 status )
  *     dev_id          device ID supplied during interrupt registration
  *     regs            interrupted processor context
  */
-static irqreturn_t synclinkmp_interrupt(int irq, void *dev_id,
-                                       struct pt_regs *regs)
+static irqreturn_t synclinkmp_interrupt(int irq, void *dev_id)
 {
        SLMP_INFO * info;
        unsigned char status, status0, status1=0;
@@ -2562,7 +2730,7 @@ static int startup(SLMP_INFO * info)
                return 0;
 
        if (!info->tx_buf) {
-               info->tx_buf = (unsigned char *)kmalloc(info->max_frame_size, GFP_KERNEL);
+               info->tx_buf = kmalloc(info->max_frame_size, GFP_KERNEL);
                if (!info->tx_buf) {
                        printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n",
                                __FILE__,__LINE__,info->device_name);
@@ -2572,12 +2740,14 @@ static int startup(SLMP_INFO * info)
 
        info->pending_bh = 0;
 
+       memset(&info->icount, 0, sizeof(info->icount));
+
        /* program hardware for current parameters */
        reset_port(info);
 
        change_params(info);
 
-       info->status_timer.expires = jiffies + jiffies_from_ms(10);
+       info->status_timer.expires = jiffies + msecs_to_jiffies(10);
        add_timer(&info->status_timer);
 
        if (info->tty)
@@ -2609,10 +2779,8 @@ static void shutdown(SLMP_INFO * info)
        del_timer(&info->tx_timer);
        del_timer(&info->status_timer);
 
-       if (info->tx_buf) {
-               kfree(info->tx_buf);
-               info->tx_buf = NULL;
-       }
+       kfree(info->tx_buf);
+       info->tx_buf = NULL;
 
        spin_lock_irqsave(&info->lock,flags);
 
@@ -2775,12 +2943,12 @@ static int get_stats(SLMP_INFO * info, struct mgsl_icount __user *user_icount)
                printk("%s(%d):%s get_params()\n",
                         __FILE__,__LINE__, info->device_name);
 
-       COPY_TO_USER(err,user_icount, &info->icount, sizeof(struct mgsl_icount));
-       if (err) {
-               if ( debug_level >= DEBUG_LEVEL_INFO )
-                       printk( "%s(%d):%s get_stats() user buffer copy failed\n",
-                               __FILE__,__LINE__,info->device_name);
-               return -EFAULT;
+       if (!user_icount) {
+               memset(&info->icount, 0, sizeof(info->icount));
+       } else {
+               COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount));
+               if (err)
+                       return -EFAULT;
        }
 
        return 0;
@@ -2931,16 +3099,6 @@ static int rx_enable(SLMP_INFO * info, int enable)
        return 0;
 }
 
-static int map_status(int signals)
-{
-       /* Map status bits to API event bits */
-
-       return ((signals & SerialSignal_DSR) ? MgslEvent_DsrActive : MgslEvent_DsrInactive) +
-              ((signals & SerialSignal_CTS) ? MgslEvent_CtsActive : MgslEvent_CtsInactive) +
-              ((signals & SerialSignal_DCD) ? MgslEvent_DcdActive : MgslEvent_DcdInactive) +
-              ((signals & SerialSignal_RI)  ? MgslEvent_RiActive : MgslEvent_RiInactive);
-}
-
 /* wait for specified event to occur
  */
 static int wait_mgsl_event(SLMP_INFO * info, int __user *mask_ptr)
@@ -2967,7 +3125,7 @@ static int wait_mgsl_event(SLMP_INFO * info, int __user *mask_ptr)
 
        /* return immediately if state matches requested events */
        get_signals(info);
-       s = map_status(info->serial_signals);
+       s = info->serial_signals;
 
        events = mask &
                ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) +
@@ -2988,7 +3146,7 @@ static int wait_mgsl_event(SLMP_INFO * info, int __user *mask_ptr)
                unsigned char oldval = info->ie1_value;
                unsigned char newval = oldval +
                         (mask & MgslEvent_ExitHuntMode ? FLGD:0) +
-                        (mask & MgslEvent_IdleReceived ? IDLE:0);
+                        (mask & MgslEvent_IdleReceived ? IDLD:0);
                if ( oldval != newval ) {
                        info->ie1_value = newval;
                        write_reg(info, IE1, info->ie1_value);
@@ -3055,7 +3213,7 @@ static int wait_mgsl_event(SLMP_INFO * info, int __user *mask_ptr)
                spin_lock_irqsave(&info->lock,flags);
                if (!waitqueue_active(&info->event_wait_q)) {
                        /* disable enable exit hunt mode/idle rcvd IRQs */
-                       info->ie1_value &= ~(FLGD|IDLE);
+                       info->ie1_value &= ~(FLGD|IDLD);
                        write_reg(info, IE1, info->ie1_value);
                }
                spin_unlock_irqrestore(&info->lock,flags);
@@ -3442,16 +3600,16 @@ int alloc_tmp_rx_buf(SLMP_INFO *info)
 
 void free_tmp_rx_buf(SLMP_INFO *info)
 {
-       if (info->tmp_rx_buf)
-               kfree(info->tmp_rx_buf);
+       kfree(info->tmp_rx_buf);
        info->tmp_rx_buf = NULL;
 }
 
 int claim_resources(SLMP_INFO *info)
 {
-       if (request_mem_region(info->phys_memory_base,0x40000,"synclinkmp") == NULL) {
+       if (request_mem_region(info->phys_memory_base,SCA_MEM_SIZE,"synclinkmp") == NULL) {
                printk( "%s(%d):%s mem addr conflict, Addr=%08X\n",
                        __FILE__,__LINE__,info->device_name, info->phys_memory_base);
+               info->init_error = DiagStatus_AddressConflict;
                goto errout;
        }
        else
@@ -3460,22 +3618,25 @@ int claim_resources(SLMP_INFO *info)
        if (request_mem_region(info->phys_lcr_base + info->lcr_offset,128,"synclinkmp") == NULL) {
                printk( "%s(%d):%s lcr mem addr conflict, Addr=%08X\n",
                        __FILE__,__LINE__,info->device_name, info->phys_lcr_base);
+               info->init_error = DiagStatus_AddressConflict;
                goto errout;
        }
        else
                info->lcr_mem_requested = 1;
 
-       if (request_mem_region(info->phys_sca_base + info->sca_offset,512,"synclinkmp") == NULL) {
+       if (request_mem_region(info->phys_sca_base + info->sca_offset,SCA_BASE_SIZE,"synclinkmp") == NULL) {
                printk( "%s(%d):%s sca mem addr conflict, Addr=%08X\n",
                        __FILE__,__LINE__,info->device_name, info->phys_sca_base);
+               info->init_error = DiagStatus_AddressConflict;
                goto errout;
        }
        else
                info->sca_base_requested = 1;
 
-       if (request_mem_region(info->phys_statctrl_base + info->statctrl_offset,16,"synclinkmp") == NULL) {
+       if (request_mem_region(info->phys_statctrl_base + info->statctrl_offset,SCA_REG_SIZE,"synclinkmp") == NULL) {
                printk( "%s(%d):%s stat/ctrl mem addr conflict, Addr=%08X\n",
                        __FILE__,__LINE__,info->device_name, info->phys_statctrl_base);
+               info->init_error = DiagStatus_AddressConflict;
                goto errout;
        }
        else
@@ -3485,33 +3646,41 @@ int claim_resources(SLMP_INFO *info)
        if (!info->memory_base) {
                printk( "%s(%d):%s Cant map shared memory, MemAddr=%08X\n",
                        __FILE__,__LINE__,info->device_name, info->phys_memory_base );
+               info->init_error = DiagStatus_CantAssignPciResources;
                goto errout;
        }
 
-       if ( !memory_test(info) ) {
-               printk( "%s(%d):Shared Memory Test failed for device %s MemAddr=%08X\n",
-                       __FILE__,__LINE__,info->device_name, info->phys_memory_base );
-               goto errout;
-       }
-
-       info->lcr_base = ioremap(info->phys_lcr_base,PAGE_SIZE) + info->lcr_offset;
+       info->lcr_base = ioremap(info->phys_lcr_base,PAGE_SIZE);
        if (!info->lcr_base) {
                printk( "%s(%d):%s Cant map LCR memory, MemAddr=%08X\n",
                        __FILE__,__LINE__,info->device_name, info->phys_lcr_base );
+               info->init_error = DiagStatus_CantAssignPciResources;
                goto errout;
        }
+       info->lcr_base += info->lcr_offset;
 
-       info->sca_base = ioremap(info->phys_sca_base,PAGE_SIZE) + info->sca_offset;
+       info->sca_base = ioremap(info->phys_sca_base,PAGE_SIZE);
        if (!info->sca_base) {
                printk( "%s(%d):%s Cant map SCA memory, MemAddr=%08X\n",
                        __FILE__,__LINE__,info->device_name, info->phys_sca_base );
+               info->init_error = DiagStatus_CantAssignPciResources;
                goto errout;
        }
+       info->sca_base += info->sca_offset;
 
-       info->statctrl_base = ioremap(info->phys_statctrl_base,PAGE_SIZE) + info->statctrl_offset;
+       info->statctrl_base = ioremap(info->phys_statctrl_base,PAGE_SIZE);
        if (!info->statctrl_base) {
                printk( "%s(%d):%s Cant map SCA Status/Control memory, MemAddr=%08X\n",
                        __FILE__,__LINE__,info->device_name, info->phys_statctrl_base );
+               info->init_error = DiagStatus_CantAssignPciResources;
+               goto errout;
+       }
+       info->statctrl_base += info->statctrl_offset;
+
+       if ( !memory_test(info) ) {
+               printk( "%s(%d):Shared Memory Test failed for device %s MemAddr=%08X\n",
+                       __FILE__,__LINE__,info->device_name, info->phys_memory_base );
+               info->init_error = DiagStatus_MemoryError;
                goto errout;
        }
 
@@ -3534,7 +3703,7 @@ void release_resources(SLMP_INFO *info)
        }
 
        if ( info->shared_mem_requested ) {
-               release_mem_region(info->phys_memory_base,0x40000);
+               release_mem_region(info->phys_memory_base,SCA_MEM_SIZE);
                info->shared_mem_requested = 0;
        }
        if ( info->lcr_mem_requested ) {
@@ -3542,11 +3711,11 @@ void release_resources(SLMP_INFO *info)
                info->lcr_mem_requested = 0;
        }
        if ( info->sca_base_requested ) {
-               release_mem_region(info->phys_sca_base + info->sca_offset,512);
+               release_mem_region(info->phys_sca_base + info->sca_offset,SCA_BASE_SIZE);
                info->sca_base_requested = 0;
        }
        if ( info->sca_statctrl_requested ) {
-               release_mem_region(info->phys_statctrl_base + info->statctrl_offset,16);
+               release_mem_region(info->phys_statctrl_base + info->statctrl_offset,SCA_REG_SIZE);
                info->sca_statctrl_requested = 0;
        }
 
@@ -3616,9 +3785,8 @@ void add_device(SLMP_INFO *info)
                info->irq_level,
                info->max_frame_size );
 
-#ifdef CONFIG_SYNCLINK_SYNCPPP
-       if (info->dosyncppp)
-               sppp_init(info);
+#if SYNCLINK_GENERIC_HDLC
+       hdlcdev_init(info);
 #endif
 }
 
@@ -3626,11 +3794,11 @@ void add_device(SLMP_INFO *info)
  *
  * Return Value:       pointer to SLMP_INFO if success, otherwise NULL
  */
-SLMP_INFO *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev)
+static SLMP_INFO *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev)
 {
        SLMP_INFO *info;
 
-       info = (SLMP_INFO *)kmalloc(sizeof(SLMP_INFO),
+       info = kmalloc(sizeof(SLMP_INFO),
                 GFP_KERNEL);
 
        if (!info) {
@@ -3639,7 +3807,7 @@ SLMP_INFO *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev)
        } else {
                memset(info, 0, sizeof(SLMP_INFO));
                info->magic = MGSL_MAGIC;
-               INIT_WORK(&info->task, bh_handler, info);
+               INIT_WORK(&info->task, bh_handler);
                info->max_frame_size = 4096;
                info->close_delay = 5*HZ/10;
                info->closing_wait = 30*HZ;
@@ -3674,7 +3842,7 @@ SLMP_INFO *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev)
                info->phys_statctrl_base &= ~(PAGE_SIZE-1);
 
                info->bus_type = MGSL_BUS_TYPE_PCI;
-               info->irq_flags = SA_SHIRQ;
+               info->irq_flags = IRQF_SHARED;
 
                init_timer(&info->tx_timer);
                info->tx_timer.data = (unsigned long)info;
@@ -3762,7 +3930,7 @@ void device_init(int adapter_num, struct pci_dev *pdev)
        }
 }
 
-static struct tty_operations ops = {
+static const struct tty_operations ops = {
        .open = open,
        .close = close,
        .write = write,
@@ -3788,7 +3956,6 @@ static struct tty_operations ops = {
 
 static void synclinkmp_cleanup(void)
 {
-       unsigned long flags;
        int rc;
        SLMP_INFO *info;
        SLMP_INFO *tmp;
@@ -3802,34 +3969,24 @@ static void synclinkmp_cleanup(void)
                put_tty_driver(serial_driver);
        }
 
+       /* reset devices */
        info = synclinkmp_device_list;
        while(info) {
-#ifdef CONFIG_SYNCLINK_SYNCPPP
-               if (info->dosyncppp)
-                       sppp_delete(info);
-#endif
                reset_port(info);
-               if ( info->port_num == 0 ) {
-                       if ( info->irq_requested ) {
-                               free_irq(info->irq_level, info);
-                               info->irq_requested = 0;
-                       }
-               }
                info = info->next_device;
        }
 
-       /* port 0 of each adapter originally claimed
-        * all resources, release those now
-        */
+       /* release devices */
        info = synclinkmp_device_list;
        while(info) {
+#if SYNCLINK_GENERIC_HDLC
+               hdlcdev_exit(info);
+#endif
                free_dma_bufs(info);
                free_tmp_rx_buf(info);
                if ( info->port_num == 0 ) {
-                       spin_lock_irqsave(&info->lock,flags);
-                       reset_adapter(info);
-                       write_reg(info, LPR, 1);                /* set low power mode */
-                       spin_unlock_irqrestore(&info->lock,flags);
+                       if (info->sca_base)
+                               write_reg(info, LPR, 1); /* set low power mode */
                        release_resources(info);
                }
                tmp = info;
@@ -3877,6 +4034,8 @@ static int __init synclinkmp_init(void)
        serial_driver->init_termios = tty_std_termios;
        serial_driver->init_termios.c_cflag =
                B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       serial_driver->init_termios.c_ispeed = 9600;
+       serial_driver->init_termios.c_ospeed = 9600;
        serial_driver->flags = TTY_DRIVER_REAL_RAW;
        tty_set_operations(serial_driver, &ops);
        if ((rc = tty_register_driver(serial_driver)) < 0) {
@@ -4112,6 +4271,9 @@ void tx_start(SLMP_INFO *info)
                                }
                        }
 
+                       write_reg16(info, TRC0,
+                               (unsigned short)(((tx_negate_fifo_level-1)<<8) + tx_active_fifo_level));
+
                        write_reg(info, TXDMA + DSR, 0);                /* disable DMA channel */
                        write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */
        
@@ -4123,17 +4285,16 @@ void tx_start(SLMP_INFO *info)
                        write_reg16(info, TXDMA + EDA,
                                info->tx_buf_list_ex[info->last_tx_buf].phys_entry);
        
-                       /* clear IDLE and UDRN status bit */
-                       info->ie1_value &= ~(IDLE + UDRN);
-                       if (info->params.mode != MGSL_MODE_ASYNC)
-                               info->ie1_value |= UDRN;                /* HDLC, IRQ on underrun */
-                       write_reg(info, IE1, info->ie1_value);  /* enable MSCI interrupts */
+                       /* enable underrun IRQ */
+                       info->ie1_value &= ~IDLE;
+                       info->ie1_value |= UDRN;
+                       write_reg(info, IE1, info->ie1_value);
                        write_reg(info, SR1, (unsigned char)(IDLE + UDRN));
        
                        write_reg(info, TXDMA + DIR, 0x40);             /* enable Tx DMA interrupts (EOM) */
                        write_reg(info, TXDMA + DSR, 0xf2);             /* clear Tx DMA IRQs, enable Tx DMA */
        
-                       info->tx_timer.expires = jiffies + jiffies_from_ms(5000);
+                       info->tx_timer.expires = jiffies + msecs_to_jiffies(5000);
                        add_timer(&info->tx_timer);
                }
                else {
@@ -4309,11 +4470,13 @@ void async_mode(SLMP_INFO *info)
        /* MD2, Mode Register 2
         *
         * 07..02  Reserved, must be 0
-        * 01..00  CNCT<1..0> Channel connection, 0=normal
+        * 01..00  CNCT<1..0> Channel connection, 00=normal 11=local loopback
         *
         * 0000 0000
         */
        RegValue = 0x00;
+       if (info->params.loopback)
+               RegValue |= (BIT1 + BIT0);
        write_reg(info, MD2, RegValue);
 
        /* RXS, Receive clock source
@@ -4348,7 +4511,7 @@ void async_mode(SLMP_INFO *info)
         * 07..05  Reserved, must be 0
         * 04..00  RRC<4..0> Rx FIFO trigger active 0x00 = 1 byte
         */
-       write_reg(info, TRC0, 0x00);
+       write_reg(info, RRC, 0x00);
 
        /* TRC0 Transmit Ready Control 0
         *
@@ -4394,9 +4557,6 @@ void async_mode(SLMP_INFO *info)
        write_reg(info, IE2, info->ie2_value);
 
        set_rate( info, info->params.data_rate * 16 );
-
-       if (info->params.loopback)
-               enable_loopback(info,1);
 }
 
 /* Program the SCA for HDLC communications.
@@ -4823,10 +4983,12 @@ CheckAgain:
                        info->icount.rxcrc++;
 
                framesize = 0;
-
-#ifdef CONFIG_SYNCLINK_SYNCPPP
-               info->netstats.rx_errors++;
-               info->netstats.rx_frame_errors++;
+#if SYNCLINK_GENERIC_HDLC
+               {
+                       struct net_device_stats *stats = hdlc_stats(info->netdev);
+                       stats->rx_errors++;
+                       stats->rx_frame_errors++;
+               }
 #endif
        }
 
@@ -4836,7 +4998,7 @@ CheckAgain:
 
        if ( debug_level >= DEBUG_LEVEL_DATA )
                trace_block(info,info->rx_buf_list_ex[StartIndex].virt_addr,
-                       MIN(framesize,SCABUFSIZE),0);
+                       min_t(int, framesize,SCABUFSIZE),0);
 
        if (framesize) {
                if (framesize > info->max_frame_size)
@@ -4851,7 +5013,7 @@ CheckAgain:
                        info->icount.rxok++;
 
                        while(copy_count) {
-                               int partial_count = MIN(copy_count,SCABUFSIZE);
+                               int partial_count = min(copy_count,SCABUFSIZE);
                                memcpy( ptmp,
                                        info->rx_buf_list_ex[index].virt_addr,
                                        partial_count );
@@ -4862,22 +5024,13 @@ CheckAgain:
                                        index = 0;
                        }
 
-#ifdef CONFIG_SYNCLINK_SYNCPPP
-                       if (info->netcount) {
-                               /* pass frame to syncppp device */
-                               sppp_rx_done(info,info->tmp_rx_buf,framesize);
-                       }
+#if SYNCLINK_GENERIC_HDLC
+                       if (info->netcount)
+                               hdlcdev_rx(info,info->tmp_rx_buf,framesize);
                        else
 #endif
-                       {
-                               if ( tty && tty->ldisc.receive_buf ) {
-                                       /* Call the line discipline receive callback directly. */
-                                       tty->ldisc.receive_buf(tty,
-                                               info->tmp_rx_buf,
-                                               info->flag_buf,
-                                               framesize);
-                               }
-                       }
+                               ldisc_receive_buf(tty,info->tmp_rx_buf,
+                                                 info->flag_buf, framesize);
                }
        }
        /* Free the buffers used by this frame. */
@@ -4910,14 +5063,14 @@ void tx_load_dma_buffer(SLMP_INFO *info, const char *buf, unsigned int count)
        SCADESC_EX *desc_ex;
 
        if ( debug_level >= DEBUG_LEVEL_DATA )
-               trace_block(info,buf, MIN(count,SCABUFSIZE), 1);
+               trace_block(info,buf, min_t(int, count,SCABUFSIZE), 1);
 
        /* Copy source buffer to one or more DMA buffers, starting with
         * the first transmit dma buffer.
         */
        for(i=0;;)
        {
-               copy_count = MIN(count,SCABUFSIZE);
+               copy_count = min_t(unsigned short,count,SCABUFSIZE);
 
                desc = &info->tx_buf_list[i];
                desc_ex = &info->tx_buf_list_ex[i];
@@ -4945,7 +5098,7 @@ void tx_load_dma_buffer(SLMP_INFO *info, const char *buf, unsigned int count)
 int register_test(SLMP_INFO *info)
 {
        static unsigned char testval[] = {0x00, 0xff, 0xaa, 0x55, 0x69, 0x96};
-       static unsigned int count = sizeof(testval)/sizeof(unsigned char);
+       static unsigned int count = ARRAY_SIZE(testval);
        unsigned int i;
        int rc = TRUE;
        unsigned long flags;
@@ -5020,8 +5173,7 @@ int irq_test(SLMP_INFO *info)
 
        timeout=100;
        while( timeout-- && !info->irq_occurred ) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(jiffies_from_ms(10));
+               msleep_interruptible(10);
        }
 
        spin_lock_irqsave(&info->lock,flags);
@@ -5033,7 +5185,7 @@ int irq_test(SLMP_INFO *info)
 
 /* initialize individual SCA device (2 ports)
  */
-int sca_init(SLMP_INFO *info)
+static int sca_init(SLMP_INFO *info)
 {
        /* set wait controller to single mem partition (low), no wait states */
        write_reg(info, PABR0, 0);      /* wait controller addr boundary 0 */
@@ -5171,8 +5323,7 @@ int loopback_test(SLMP_INFO *info)
        /* wait for receive complete */
        /* Set a timeout for waiting for interrupt. */
        for ( timeout = 100; timeout; --timeout ) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(jiffies_from_ms(10));
+               msleep_interruptible(10);
 
                if (rx_get_frame(info)) {
                        rc = TRUE;
@@ -5265,7 +5416,7 @@ int memory_test(SLMP_INFO *info)
 {
        static unsigned long testval[] = { 0x0, 0x55555555, 0xaaaaaaaa,
                0x66666666, 0x99999999, 0xffffffff, 0x12345678 };
-       unsigned long count = sizeof(testval)/sizeof(unsigned long);
+       unsigned long count = ARRAY_SIZE(testval);
        unsigned long i;
        unsigned long limit = SCA_MEM_SIZE/sizeof(unsigned long);
        unsigned long * addr = (unsigned long *)info->memory_base;
@@ -5384,9 +5535,9 @@ void tx_timeout(unsigned long context)
 
        spin_unlock_irqrestore(&info->lock,flags);
 
-#ifdef CONFIG_SYNCLINK_SYNCPPP
+#if SYNCLINK_GENERIC_HDLC
        if (info->netcount)
-               sppp_tx_done(info);
+               hdlcdev_tx_done(info);
        else
 #endif
                bh_transmit(info);
@@ -5428,7 +5579,7 @@ void status_timeout(unsigned long context)
 
        info->status_timer.data = (unsigned long)info;
        info->status_timer.function = status_timeout;
-       info->status_timer.expires = jiffies + jiffies_from_ms(10);
+       info->status_timer.expires = jiffies + msecs_to_jiffies(10);
        add_timer(&info->status_timer);
 }