vserver 1.9.3
[linux-2.6.git] / drivers / char / synclink.c
index 78d4efe..0a1cc1d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * linux/drivers/char/synclink.c
  *
- * $Id: synclink.c,v 4.24 2004/06/03 14:50:09 paulkf Exp $
+ * $Id: synclink.c,v 4.28 2004/08/11 19:30:01 paulkf Exp $
  *
  * Device driver for Microgate SyncLink ISA and PCI
  * high speed multiprotocol serial adapters.
 #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>
+#ifdef CONFIG_HDLC_MODULE
+#define CONFIG_HDLC 1
 #endif
 
 #define GET_USER(error,value,addr) error = get_user(value,addr)
@@ -187,7 +184,6 @@ struct tx_holding_buffer {
  */
  
 struct mgsl_struct {
-       void *if_ptr;   /* General purpose pointer (used by SPPP) */
        int                     magic;
        int                     flags;
        int                     count;          /* count of opens */
@@ -318,15 +314,13 @@ struct mgsl_struct {
        
        struct  _input_signal_events    input_signal_events;
 
-       /* SPPP/Cisco HDLC device parts */
+       /* generic HDLC device parts */
        int netcount;
        int dosyncppp;
        spinlock_t netlock;
-#ifdef CONFIG_SYNCLINK_SYNCPPP
-       struct ppp_device pppdev;
-       char netname[10];
+
+#ifdef CONFIG_HDLC
        struct net_device *netdev;
-       struct net_device_stats netstats;
 #endif
 };
 
@@ -734,18 +728,12 @@ int usc_loopmode_send_active( struct mgsl_struct * info );
 
 int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg);
 
-#ifdef CONFIG_SYNCLINK_SYNCPPP
-/* SPPP/HDLC stuff */
-static void mgsl_sppp_init(struct mgsl_struct *info);
-static void mgsl_sppp_delete(struct mgsl_struct *info);
-int mgsl_sppp_open(struct net_device *d);
-int mgsl_sppp_close(struct net_device *d);
-void mgsl_sppp_tx_timeout(struct net_device *d);
-int mgsl_sppp_tx(struct sk_buff *skb, struct net_device *d);
-void mgsl_sppp_rx_done(struct mgsl_struct *info, char *buf, int size);
-void mgsl_sppp_tx_done(struct mgsl_struct *info);
-int mgsl_sppp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
-struct net_device_stats *mgsl_net_stats(struct net_device *dev);
+#ifdef CONFIG_HDLC
+#define dev_to_port(D) (dev_to_hdlc(D)->priv)
+static void hdlcdev_tx_done(struct mgsl_struct *info);
+static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size);
+static int  hdlcdev_init(struct mgsl_struct *info);
+static void hdlcdev_exit(struct mgsl_struct *info);
 #endif
 
 /*
@@ -863,8 +851,6 @@ static int mgsl_rxenable(struct mgsl_struct * info, int enable);
 static int mgsl_wait_event(struct mgsl_struct * info, int __user *mask);
 static int mgsl_loopmode_send_done( struct mgsl_struct * info );
 
-#define jiffies_from_ms(a) ((((a) * HZ)/1000)+1)
-
 /* set non-zero on successful registration with PCI subsystem */
 static int pci_registered;
 
@@ -911,7 +897,7 @@ MODULE_PARM(txdmabufs,"1-" __MODULE_STRING(MAX_TOTAL_DEVICES) "i");
 MODULE_PARM(txholdbufs,"1-" __MODULE_STRING(MAX_TOTAL_DEVICES) "i");
 
 static char *driver_name = "SyncLink serial driver";
-static char *driver_version = "$Revision: 4.24 $";
+static char *driver_version = "$Revision: 4.28 $";
 
 static int synclink_init_one (struct pci_dev *dev,
                                     const struct pci_device_id *ent);
@@ -942,10 +928,6 @@ static struct tty_driver *serial_driver;
 static void mgsl_change_params(struct mgsl_struct *info);
 static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout);
 
-#ifndef MIN
-#define MIN(a,b)       ((a) < (b) ? (a) : (b))
-#endif
-
 /*
  * 1st function defined in .text section. Calling this function in
  * init_module() followed by a breakpoint allows a remote debugger
@@ -993,6 +975,29 @@ static inline int mgsl_paranoia_check(struct mgsl_struct *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);
+       }
+}
+
 /* mgsl_stop()         throttle (stop) transmitter
  *     
  * Arguments:          tty     pointer to tty info structure
@@ -1153,13 +1158,7 @@ void mgsl_bh_transmit(struct mgsl_struct *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):calling ldisc.write_wakeup on %s\n",
-                                       __FILE__,__LINE__,info->device_name);
-                       (tty->ldisc.write_wakeup)(tty);
-               }
+               tty_wakeup(tty);
                wake_up_interruptible(&tty->write_wait);
        }
 
@@ -1289,9 +1288,9 @@ void mgsl_isr_transmit_status( struct mgsl_struct *info )
                info->drop_rts_on_tx_done = 0;
        }
 
-#ifdef CONFIG_SYNCLINK_SYNCPPP 
+#ifdef CONFIG_HDLC
        if (info->netcount)
-               mgsl_sppp_tx_done(info);
+               hdlcdev_tx_done(info);
        else 
 #endif
        {
@@ -1352,12 +1351,12 @@ void mgsl_isr_io_pin( struct mgsl_struct *info )
                        icount->dcd++;
                        if (status & MISCSTATUS_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++;
+#ifdef CONFIG_HDLC
+                       if (info->netcount)
+                               hdlc_set_carrier(status & MISCSTATUS_DCD, info->netdev);
+#endif
                }
                if (status & MISCSTATUS_CTS_LATCHED)
                {
@@ -2258,8 +2257,8 @@ static int mgsl_write(struct tty_struct * tty, int from_user,
                if (from_user) {
                        down(&tmp_buf_sem);
                        while (1) {
-                               c = MIN(count,
-                                       MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                               c = min_t(int, count,
+                                       min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
                                            SERIAL_XMIT_SIZE - info->xmit_head));
                                if (c <= 0)
                                        break;
@@ -2272,7 +2271,7 @@ static int mgsl_write(struct tty_struct * tty, int from_user,
                                        break;
                                }
                                spin_lock_irqsave(&info->irq_spinlock,flags);
-                               c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                               c = min_t(int, c, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
                                               SERIAL_XMIT_SIZE - info->xmit_head));
                                memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
                                info->xmit_head = ((info->xmit_head + c) &
@@ -2287,8 +2286,8 @@ static int mgsl_write(struct tty_struct * tty, int from_user,
                } else {
                        while (1) {
                                spin_lock_irqsave(&info->irq_spinlock,flags);
-                               c = MIN(count,
-                                       MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                               c = min_t(int, count,
+                                       min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
                                            SERIAL_XMIT_SIZE - info->xmit_head));
                                if (c <= 0) {
                                        spin_unlock_irqrestore(&info->irq_spinlock,flags);
@@ -2415,11 +2414,8 @@ static void mgsl_flush_buffer(struct tty_struct *tty)
        spin_unlock_irqrestore(&info->irq_spinlock,flags);
        
        wake_up_interruptible(&tty->write_wait);
-       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-           tty->ldisc.write_wakeup)
-               (tty->ldisc.write_wakeup)(tty);
-               
-}      /* end of mgsl_flush_buffer() */
+       tty_wakeup(tty);
+}
 
 /* mgsl_send_xchar()
  *
@@ -3253,9 +3249,8 @@ static void mgsl_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);
        
@@ -3326,7 +3321,7 @@ static void mgsl_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 ||
                info->params.mode == MGSL_MODE_RAW ) {
@@ -3592,7 +3587,7 @@ static int mgsl_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--;
        }
@@ -4187,7 +4182,7 @@ int load_next_tx_holding_buffer(struct mgsl_struct *info)
                                info->get_tx_holding_index=0;
 
                        /* restart transmit timer */
-                       mod_timer(&info->tx_timer, jiffies + jiffies_from_ms(5000));
+                       mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(5000));
 
                        ret = 1;
                }
@@ -4415,12 +4410,10 @@ void mgsl_add_device( struct mgsl_struct *info )
                        info->max_frame_size );
        }
 
-#ifdef CONFIG_SYNCLINK_SYNCPPP
-#ifdef MODULE
-       if (info->dosyncppp)
-#endif
-               mgsl_sppp_init(info);
+#ifdef CONFIG_HDLC
+       hdlcdev_init(info);
 #endif
+
 }      /* end of mgsl_add_device() */
 
 /* mgsl_allocate_device()
@@ -4575,9 +4568,8 @@ static void synclink_cleanup(void)
 
        info = mgsl_device_list;
        while(info) {
-#ifdef CONFIG_SYNCLINK_SYNCPPP
-               if (info->dosyncppp)
-                       mgsl_sppp_delete(info);
+#ifdef CONFIG_HDLC
+               hdlcdev_exit(info);
 #endif
                mgsl_release_resources(info);
                tmp = info;
@@ -5819,7 +5811,7 @@ void usc_start_transmitter( struct mgsl_struct *info )
                        
                        usc_TCmd( info, TCmd_SendFrame );
                        
-                       info->tx_timer.expires = jiffies + jiffies_from_ms(5000);
+                       info->tx_timer.expires = jiffies + msecs_to_jiffies(5000);
                        add_timer(&info->tx_timer);     
                }
                info->tx_active = 1;
@@ -6750,9 +6742,12 @@ int mgsl_get_rx_frame(struct mgsl_struct *info)
                                return_frame = 1;
                }
                framesize = 0;
-#ifdef CONFIG_SYNCLINK_SYNCPPP
-               info->netstats.rx_errors++;
-               info->netstats.rx_frame_errors++;
+#ifdef CONFIG_HDLC
+               {
+                       struct net_device_stats *stats = hdlc_stats(info->netdev);
+                       stats->rx_errors++;
+                       stats->rx_frame_errors++;
+               }
 #endif
        } else
                return_frame = 1;
@@ -6779,7 +6774,7 @@ int mgsl_get_rx_frame(struct mgsl_struct *info)
                        
        if ( debug_level >= DEBUG_LEVEL_DATA )
                mgsl_trace_block(info,info->rx_buffer_list[StartIndex].virt_addr,
-                       MIN(framesize,DMABUFFERSIZE),0);        
+                       min_t(int, framesize, DMABUFFERSIZE),0);
                
        if (framesize) {
                if ( ( (info->params.crc_type & HDLC_CRC_RETURN_EX) &&
@@ -6823,18 +6818,12 @@ int mgsl_get_rx_frame(struct mgsl_struct *info)
                                                *ptmp);
                        }
 
-#ifdef CONFIG_SYNCLINK_SYNCPPP
-                       if (info->netcount) {
-                               /* pass frame to syncppp device */
-                               mgsl_sppp_rx_done(info,info->intermediate_rxbuffer,framesize);
-                       } 
+#ifdef CONFIG_HDLC
+                       if (info->netcount)
+                               hdlcdev_rx(info,info->intermediate_rxbuffer,framesize);
                        else
 #endif
-                       {
-                               /* Call the line discipline receive callback directly. */
-                               if ( tty && tty->ldisc.receive_buf )
-                               tty->ldisc.receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
-                       }
+                               ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
                }
        }
        /* Free the buffers used by this frame. */
@@ -6996,7 +6985,7 @@ int mgsl_get_raw_rx_frame(struct mgsl_struct *info)
 
                if ( debug_level >= DEBUG_LEVEL_DATA )
                        mgsl_trace_block(info,info->rx_buffer_list[CurrentIndex].virt_addr,
-                               MIN(framesize,DMABUFFERSIZE),0);
+                               min_t(int, framesize, DMABUFFERSIZE),0);
 
                if (framesize) {
                        /* copy dma buffer(s) to contiguous intermediate buffer */
@@ -7006,9 +6995,7 @@ int mgsl_get_raw_rx_frame(struct mgsl_struct *info)
                        memcpy( info->intermediate_rxbuffer, pBufEntry->virt_addr, framesize);
                        info->icount.rxok++;
 
-                       /* Call the line discipline receive callback directly. */
-                       if ( tty && tty->ldisc.receive_buf )
-                               tty->ldisc.receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
+                       ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
                }
 
                /* Free the buffers used by this frame. */
@@ -7056,7 +7043,7 @@ void mgsl_load_tx_dma_buffer(struct mgsl_struct *info, const char *Buffer,
        DMABUFFERENTRY *pBufEntry;
        
        if ( debug_level >= DEBUG_LEVEL_DATA )
-               mgsl_trace_block(info,Buffer, MIN(BufferSize,DMABUFFERSIZE), 1);        
+               mgsl_trace_block(info,Buffer, min_t(int, BufferSize, DMABUFFERSIZE), 1);
 
        if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) {
                /* set CMR:13 to start transmit when
@@ -7214,7 +7201,7 @@ BOOLEAN mgsl_irq_test( struct mgsl_struct *info )
        EndTime=100;
        while( EndTime-- && !info->irq_occurred ) {
                set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(jiffies_from_ms(10));
+               schedule_timeout(msecs_to_jiffies(10));
        }
        
        spin_lock_irqsave(&info->irq_spinlock,flags);
@@ -7353,7 +7340,7 @@ BOOLEAN mgsl_dma_test( struct mgsl_struct *info )
        /*************************************************************/
 
        /* Wait 100ms for interrupt. */
-       EndTime = jiffies + jiffies_from_ms(100);
+       EndTime = jiffies + msecs_to_jiffies(100);
 
        for(;;) {
                if (time_after(jiffies, EndTime)) {
@@ -7409,7 +7396,7 @@ BOOLEAN mgsl_dma_test( struct mgsl_struct *info )
        /**********************************/
        
        /* Wait 100ms */
-       EndTime = jiffies + jiffies_from_ms(100);
+       EndTime = jiffies + msecs_to_jiffies(100);
 
        for(;;) {
                if (time_after(jiffies, EndTime)) {
@@ -7451,7 +7438,7 @@ BOOLEAN mgsl_dma_test( struct mgsl_struct *info )
                /******************************/
 
                /* Wait 100ms */
-               EndTime = jiffies + jiffies_from_ms(100);
+               EndTime = jiffies + msecs_to_jiffies(100);
 
                /* While timer not expired wait for transmit complete */
 
@@ -7482,7 +7469,7 @@ BOOLEAN mgsl_dma_test( struct mgsl_struct *info )
                /* WAIT FOR RECEIVE COMPLETE */
 
                /* Wait 100ms */
-               EndTime = jiffies + jiffies_from_ms(100);
+               EndTime = jiffies + msecs_to_jiffies(100);
 
                /* Wait for 16C32 to write receive status to buffer entry. */
                status=info->rx_buffer_list[0].status;
@@ -7736,9 +7723,9 @@ void mgsl_tx_timeout(unsigned long context)
 
        spin_unlock_irqrestore(&info->irq_spinlock,flags);
        
-#ifdef CONFIG_SYNCLINK_SYNCPPP
+#ifdef CONFIG_HDLC
        if (info->netcount)
-               mgsl_sppp_tx_done(info);
+               hdlcdev_tx_done(info);
        else
 #endif
                mgsl_bh_transmit(info);
@@ -7819,79 +7806,125 @@ int usc_loopmode_send_active( struct mgsl_struct * info )
        return usc_InReg( info, CCSR ) & BIT6 ? 1 : 0 ;
 }                        
 
-#ifdef CONFIG_SYNCLINK_SYNCPPP
-/* syncppp net device routines
- */
-static void mgsl_setup(struct net_device *dev)
-{
-       dev->open = mgsl_sppp_open;
-       dev->stop = mgsl_sppp_close;
-       dev->hard_start_xmit = mgsl_sppp_tx;
-       dev->do_ioctl = mgsl_sppp_ioctl;
-       dev->get_stats = mgsl_net_stats;
-       dev->tx_timeout = mgsl_sppp_tx_timeout;
-       dev->watchdog_timeo = 10*HZ;
-}
+#ifdef CONFIG_HDLC
 
-static void mgsl_sppp_init(struct mgsl_struct *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;
+       struct mgsl_struct *info = dev_to_port(dev);
+       unsigned char  new_encoding;
+       unsigned short new_crctype;
 
-       sprintf(info->netname,"mgsl%d",info->line);
+       /* return error if TTY interface open */
+       if (info->count)
+               return -EBUSY;
 
-       d = alloc_netdev(0, info->netname, mgsl_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->base_addr = info->io_base;
-       d->irq = info->irq_level;
-       d->dma = info->dma_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);
-       mgsl_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;
-               free_netdev(d);
-               return;
-       }
+       /* if network interface up, reprogram hardware */
+       if (info->netcount)
+               mgsl_program_hw(info);
 
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("mgsl_sppp_init()\n");   
+       return 0;
 }
 
-void mgsl_sppp_delete(struct mgsl_struct *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)
 {
+       struct mgsl_struct *info = dev_to_port(dev);
+       struct net_device_stats *stats = hdlc_stats(dev);
+       unsigned long flags;
+
        if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("mgsl_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->xmit_cnt = skb->len;
+       mgsl_load_tx_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->irq_spinlock,flags);
+       if (!info->tx_active)
+               usc_start_transmitter(info);
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+       return 0;
 }
 
-int mgsl_sppp_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)
 {
-       struct mgsl_struct *info = d->priv;
-       int err;
+       struct mgsl_struct *info = dev_to_port(dev);
+       int rc;
        unsigned long flags;
 
        if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("mgsl_sppp_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_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;
        }
@@ -7899,141 +7932,301 @@ int mgsl_sppp_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;
        mgsl_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->irq_spinlock, flags);
+       usc_get_serial_signals(info);
+       spin_unlock_irqrestore(&info->irq_spinlock, flags);
+       hdlc_set_carrier(info->serial_signals & SerialSignal_DCD, dev);
+
+       return 0;
 }
 
-void mgsl_sppp_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)
 {
-       struct mgsl_struct *info = dev->priv;
+       struct mgsl_struct *info = dev_to_port(dev);
        unsigned long flags;
 
        if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("mgsl_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->irq_spinlock,flags);
-       usc_stop_transmitter(info);
-       spin_unlock_irqrestore(&info->irq_spinlock,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;
 }
 
-int mgsl_sppp_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)
 {
-       struct mgsl_struct *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;
+       struct mgsl_struct *info = dev_to_port(dev);
+       unsigned int flags;
 
        if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("mgsl_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->xmit_cnt = skb->len;
-       mgsl_load_tx_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->irq_spinlock,flags);
-       if (!info->tx_active)
-               usc_start_transmitter(info);
-       spin_unlock_irqrestore(&info->irq_spinlock,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)
+                       mgsl_program_hw(info);
+               return 0;
+
+       default:
+               return hdlc_ioctl(dev, ifr, cmd);
+       }
 }
 
-int mgsl_sppp_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)
 {
-       struct mgsl_struct *info = d->priv;
+       struct mgsl_struct *info = dev_to_port(dev);
+       struct net_device_stats *stats = hdlc_stats(dev);
        unsigned long flags;
 
        if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("mgsl_sppp_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->irq_spinlock,flags);
+       usc_stop_transmitter(info);
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
 
-       spin_lock_irqsave(&info->netlock, flags);
-       info->netcount=0;
-       spin_unlock_irqrestore(&info->netlock, flags);
-       return 0;
+       netif_wake_queue(dev);
+}
+
+/**
+ * called by device driver when transmit completes
+ * reenable network layer transmit if stopped
+ *
+ * info  pointer to device instance information
+ */
+static void hdlcdev_tx_done(struct mgsl_struct *info)
+{
+       if (netif_queue_stopped(info->netdev))
+               netif_wake_queue(info->netdev);
 }
 
-void mgsl_sppp_rx_done(struct mgsl_struct *info, char *buf, int size)
+/**
+ * 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(struct mgsl_struct *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("mgsl_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->dev      = info->netdev;
+       skb->mac.raw  = skb->data;
+       skb->protocol = hdlc_type_trans(skb, skb->dev);
+
+       stats->rx_packets++;
+       stats->rx_bytes += size;
+
        netif_rx(skb);
-       info->netdev->trans_start = jiffies;
-}
 
-void mgsl_sppp_tx_done(struct mgsl_struct *info)
-{
-       if (netif_queue_stopped(info->netdev))
-           netif_wake_queue(info->netdev);
+       info->netdev->last_rx = jiffies;
 }
 
-struct net_device_stats *mgsl_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(struct mgsl_struct *info)
 {
-       struct mgsl_struct *info = dev->priv;
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("mgsl_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->base_addr = info->io_base;
+       dev->irq       = info->irq_level;
+       dev->dma       = info->dma_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;
 }
 
-int mgsl_sppp_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(struct mgsl_struct *info)
 {
-       struct mgsl_struct *info = dev->priv;
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_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 */
+
 
 static int __devinit synclink_init_one (struct pci_dev *dev,
                                        const struct pci_device_id *ent)