Merge branch 'for-jeff' of git://git.kernel.org/pub/scm/linux/kernel/git/chris/linux...
Jeff Garzik [Thu, 7 Aug 2008 08:05:46 +0000 (04:05 -0400)]
1  2 
drivers/char/pcmcia/synclink_cs.c
drivers/char/synclink.c
drivers/char/synclink_gt.c
drivers/char/synclinkmp.c
drivers/net/wan/Kconfig
drivers/net/wan/cosa.c

@@@ -232,7 -232,6 +232,6 @@@ typedef struct _mgslpc_info 
  
        /* SPPP/Cisco HDLC device parts */
        int netcount;
-       int dosyncppp;
        spinlock_t netlock;
  
  #if SYNCLINK_GENERIC_HDLC
@@@ -459,13 -458,11 +458,11 @@@ static int ttymajor=0
  
  static int debug_level = 0;
  static int maxframe[MAX_DEVICE_COUNT] = {0,};
- static int dosyncppp[MAX_DEVICE_COUNT] = {1,1,1,1};
  
  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);
  
  MODULE_LICENSE("GPL");
  
@@@ -2230,7 -2227,7 +2227,7 @@@ static int tiocmset(struct tty_struct *
   * Arguments:         tty             pointer to tty instance data
   *                    break_state     -1=set break condition, 0=clear
   */
 -static void mgslpc_break(struct tty_struct *tty, int break_state)
 +static int mgslpc_break(struct tty_struct *tty, int break_state)
  {
        MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data;
        unsigned long flags;
                         __FILE__,__LINE__, info->device_name, break_state);
  
        if (mgslpc_paranoia_check(info, tty->name, "mgslpc_break"))
 -              return;
 +              return -EINVAL;
  
        spin_lock_irqsave(&info->lock,flags);
        if (break_state == -1)
        else
                clear_reg_bits(info, CHA+DAFO, BIT6);
        spin_unlock_irqrestore(&info->lock,flags);
 +      return 0;
  }
  
  /* Service an IOCTL request
@@@ -2915,7 -2911,6 +2912,6 @@@ static void mgslpc_add_device(MGSLPC_IN
        if (info->line < MAX_DEVICE_COUNT) {
                if (maxframe[info->line])
                        info->max_frame_size = maxframe[info->line];
-               info->dosyncppp = dosyncppp[info->line];
        }
  
        mgslpc_device_count++;
diff --combined drivers/char/synclink.c
@@@ -304,7 -304,6 +304,6 @@@ struct mgsl_struct 
  
        /* generic HDLC device parts */
        int netcount;
-       int dosyncppp;
        spinlock_t netlock;
  
  #if SYNCLINK_GENERIC_HDLC
@@@ -868,7 -867,6 +867,6 @@@ static int irq[MAX_ISA_DEVICES]
  static int dma[MAX_ISA_DEVICES];
  static int debug_level;
  static int maxframe[MAX_TOTAL_DEVICES];
- static int dosyncppp[MAX_TOTAL_DEVICES];
  static int txdmabufs[MAX_TOTAL_DEVICES];
  static int txholdbufs[MAX_TOTAL_DEVICES];
        
@@@ -879,7 -877,6 +877,6 @@@ module_param_array(irq, int, NULL, 0)
  module_param_array(dma, int, NULL, 0);
  module_param(debug_level, int, 0);
  module_param_array(maxframe, int, NULL, 0);
- module_param_array(dosyncppp, int, NULL, 0);
  module_param_array(txdmabufs, int, NULL, 0);
  module_param_array(txholdbufs, int, NULL, 0);
  
@@@ -2897,9 -2894,9 +2894,9 @@@ static int tiocmset(struct tty_struct *
   *
   * Arguments:         tty             pointer to tty instance data
   *                    break_state     -1=set break condition, 0=clear
 - * Return Value:      None
 + * Return Value:      error code
   */
 -static void mgsl_break(struct tty_struct *tty, int break_state)
 +static int mgsl_break(struct tty_struct *tty, int break_state)
  {
        struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data;
        unsigned long flags;
                         __FILE__,__LINE__, info->device_name, break_state);
                         
        if (mgsl_paranoia_check(info, tty->name, "mgsl_break"))
 -              return;
 +              return -EINVAL;
  
        spin_lock_irqsave(&info->irq_spinlock,flags);
        if (break_state == -1)
        else 
                usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) & ~BIT7));
        spin_unlock_irqrestore(&info->irq_spinlock,flags);
 +      return 0;
        
  }     /* end of mgsl_break() */
  
@@@ -4258,7 -4254,6 +4255,6 @@@ static void mgsl_add_device( struct mgs
        if (info->line < MAX_TOTAL_DEVICES) {
                if (maxframe[info->line])
                        info->max_frame_size = maxframe[info->line];
-               info->dosyncppp = dosyncppp[info->line];
  
                if (txdmabufs[info->line]) {
                        info->num_tx_dma_buffers = txdmabufs[info->line];
@@@ -128,17 -128,14 +128,14 @@@ static int slgt_device_count
  static int ttymajor;
  static int debug_level;
  static int maxframe[MAX_DEVICES];
- static int dosyncppp[MAX_DEVICES];
  
  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);
  
  MODULE_PARM_DESC(ttymajor, "TTY major device number override: 0=auto assigned");
  MODULE_PARM_DESC(debug_level, "Debug syslog output: 0=disabled, 1 to 5=increasing detail");
  MODULE_PARM_DESC(maxframe, "Maximum frame size used by device (4096 to 65535)");
- MODULE_PARM_DESC(dosyncppp, "Enable synchronous net device, 0=disable 1=enable");
  
  /*
   * tty support and callbacks
@@@ -165,7 -162,7 +162,7 @@@ static int  read_proc(char *page, char 
  static int  chars_in_buffer(struct tty_struct *tty);
  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);
 +static int set_break(struct tty_struct *tty, int break_state);
  
  /*
   * generic HDLC support and callbacks
@@@ -214,7 -211,6 +211,7 @@@ struct slgt_des
        char *buf;          /* virtual  address of data buffer */
        unsigned int pdesc; /* physical address of this descriptor */
        dma_addr_t buf_dma_addr;
 +      unsigned short buf_count;
  };
  
  #define set_desc_buffer(a,b) (a).pbuf = cpu_to_le32((unsigned int)(b))
@@@ -303,7 -299,7 +300,7 @@@ struct slgt_info 
        u32 idle_mode;
        u32 max_frame_size;       /* as set by device config */
  
 -      unsigned int raw_rx_size;
 +      unsigned int rbuf_fill_level;
        unsigned int if_mode;
  
        /* device status */
        /* SPPP/Cisco HDLC device parts */
  
        int netcount;
-       int dosyncppp;
        spinlock_t netlock;
  #if SYNCLINK_GENERIC_HDLC
        struct net_device *netdev;
@@@ -467,7 -462,6 +463,7 @@@ static void tx_start(struct slgt_info *
  static void tx_stop(struct slgt_info *info);
  static void tx_set_idle(struct slgt_info *info);
  static unsigned int free_tbuf_count(struct slgt_info *info);
 +static unsigned int tbuf_bytes(struct slgt_info *info);
  static void reset_tbufs(struct slgt_info *info);
  static void tdma_reset(struct slgt_info *info);
  static void tdma_start(struct slgt_info *info);
@@@ -515,7 -509,7 +511,7 @@@ static int  wait_mgsl_event(struct slgt
  static int  tiocmget(struct tty_struct *tty, struct file *file);
  static int  tiocmset(struct tty_struct *tty, struct file *file,
                     unsigned int set, unsigned int clear);
 -static void set_break(struct tty_struct *tty, int break_state);
 +static int set_break(struct tty_struct *tty, int break_state);
  static int  get_interface(struct slgt_info *info, int __user *if_mode);
  static int  set_interface(struct slgt_info *info, int if_mode);
  static int  set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
@@@ -851,7 -845,6 +847,7 @@@ static int write(struct tty_struct *tty
        int ret = 0;
        struct slgt_info *info = tty->driver_data;
        unsigned long flags;
 +      unsigned int bufs_needed;
  
        if (sanity_check(info, tty->name, "write"))
                goto cleanup;
        if (!count)
                goto cleanup;
  
 -      if (info->params.mode == MGSL_MODE_RAW ||
 -          info->params.mode == MGSL_MODE_MONOSYNC ||
 -          info->params.mode == MGSL_MODE_BISYNC) {
 -              unsigned int bufs_needed = (count/DMABUFSIZE);
 -              unsigned int bufs_free = free_tbuf_count(info);
 -              if (count % DMABUFSIZE)
 -                      ++bufs_needed;
 -              if (bufs_needed > bufs_free)
 -                      goto cleanup;
 -      } else {
 -              if (info->tx_active)
 -                      goto cleanup;
 -              if (info->tx_count) {
 -                      /* send accumulated data from send_char() calls */
 -                      /* as frame and wait before accepting more data. */
 -                      tx_load(info, info->tx_buf, info->tx_count);
 -                      goto start;
 -              }
 +      if (!info->tx_active && info->tx_count) {
 +              /* send accumulated data from send_char() */
 +              tx_load(info, info->tx_buf, info->tx_count);
 +              goto start;
        }
 +      bufs_needed = (count/DMABUFSIZE);
 +      if (count % DMABUFSIZE)
 +              ++bufs_needed;
 +      if (bufs_needed > free_tbuf_count(info))
 +              goto cleanup;
  
        ret = info->tx_count = count;
        tx_load(info, buf, count);
@@@ -1390,12 -1392,10 +1386,12 @@@ done
  static int chars_in_buffer(struct tty_struct *tty)
  {
        struct slgt_info *info = tty->driver_data;
 +      int count;
        if (sanity_check(info, tty->name, "chars_in_buffer"))
                return 0;
 -      DBGINFO(("%s chars_in_buffer()=%d\n", info->device_name, info->tx_count));
 -      return info->tx_count;
 +      count = tbuf_bytes(info);
 +      DBGINFO(("%s chars_in_buffer()=%d\n", info->device_name, count));
 +      return count;
  }
  
  /*
@@@ -1448,14 -1448,14 +1444,14 @@@ static void unthrottle(struct tty_struc
   * set or clear transmit break condition
   * break_state        -1=set break condition, 0=clear
   */
 -static void set_break(struct tty_struct *tty, int break_state)
 +static int set_break(struct tty_struct *tty, int break_state)
  {
        struct slgt_info *info = tty->driver_data;
        unsigned short value;
        unsigned long flags;
  
        if (sanity_check(info, tty->name, "set_break"))
 -              return;
 +              return -EINVAL;
        DBGINFO(("%s set_break(%d)\n", info->device_name, break_state));
  
        spin_lock_irqsave(&info->lock,flags);
                value &= ~BIT6;
        wr_reg16(info, TCR, value);
        spin_unlock_irqrestore(&info->lock,flags);
 +      return 0;
  }
  
  #if SYNCLINK_GENERIC_HDLC
@@@ -2676,31 -2675,8 +2672,31 @@@ static int tx_abort(struct slgt_info *i
  static int rx_enable(struct slgt_info *info, int enable)
  {
        unsigned long flags;
 -      DBGINFO(("%s rx_enable(%d)\n", info->device_name, enable));
 +      unsigned int rbuf_fill_level;
 +      DBGINFO(("%s rx_enable(%08x)\n", info->device_name, enable));
        spin_lock_irqsave(&info->lock,flags);
 +      /*
 +       * enable[31..16] = receive DMA buffer fill level
 +       * 0 = noop (leave fill level unchanged)
 +       * fill level must be multiple of 4 and <= buffer size
 +       */
 +      rbuf_fill_level = ((unsigned int)enable) >> 16;
 +      if (rbuf_fill_level) {
 +              if ((rbuf_fill_level > DMABUFSIZE) || (rbuf_fill_level % 4)) {
 +                      spin_unlock_irqrestore(&info->lock, flags);
 +                      return -EINVAL;
 +              }
 +              info->rbuf_fill_level = rbuf_fill_level;
 +              rx_stop(info); /* restart receiver to use new fill level */
 +      }
 +
 +      /*
 +       * enable[1..0] = receiver enable command
 +       * 0 = disable
 +       * 1 = enable
 +       * 2 = enable or force hunt mode if already enabled
 +       */
 +      enable &= 3;
        if (enable) {
                if (!info->rx_enabled)
                        rx_start(info);
@@@ -3405,7 -3381,6 +3401,6 @@@ static void add_device(struct slgt_inf
        if (info->line < MAX_DEVICES) {
                if (maxframe[info->line])
                        info->max_frame_size = maxframe[info->line];
-               info->dosyncppp = dosyncppp[info->line];
        }
  
        slgt_device_count++;
@@@ -3467,7 -3442,7 +3462,7 @@@ static struct slgt_info *alloc_dev(int 
                info->magic = MGSL_MAGIC;
                INIT_WORK(&info->task, bh_handler);
                info->max_frame_size = 4096;
 -              info->raw_rx_size = DMABUFSIZE;
 +              info->rbuf_fill_level = DMABUFSIZE;
                info->port.close_delay = 5*HZ/10;
                info->port.closing_wait = 30*HZ;
                init_waitqueue_head(&info->status_event_wait_q);
@@@ -3954,7 -3929,15 +3949,7 @@@ static void tdma_start(struct slgt_inf
  
        /* set 1st descriptor address */
        wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
 -      switch(info->params.mode) {
 -      case MGSL_MODE_RAW:
 -      case MGSL_MODE_MONOSYNC:
 -      case MGSL_MODE_BISYNC:
 -              wr_reg32(info, TDCSR, BIT2 + BIT0); /* IRQ + DMA enable */
 -              break;
 -      default:
 -              wr_reg32(info, TDCSR, BIT0); /* DMA enable */
 -      }
 +      wr_reg32(info, TDCSR, BIT2 + BIT0); /* IRQ + DMA enable */
  }
  
  static void tx_stop(struct slgt_info *info)
@@@ -4157,7 -4140,7 +4152,7 @@@ static void sync_mode(struct slgt_info 
         * 01      enable
         * 00      auto-CTS enable
         */
 -      val = 0;
 +      val = BIT2;
  
        switch(info->params.mode) {
        case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break;
@@@ -4430,8 -4413,6 +4425,8 @@@ static void msc_set_vcr(struct slgt_inf
                break;
        }
  
 +      if (info->if_mode & MGSL_INTERFACE_MSB_FIRST)
 +              val |= BIT4;
        if (info->signals & SerialSignal_DTR)
                val |= BIT3;
        if (info->signals & SerialSignal_RTS)
@@@ -4470,7 -4451,16 +4465,7 @@@ static void free_rbufs(struct slgt_inf
        while(!done) {
                /* reset current buffer for reuse */
                info->rbufs[i].status = 0;
 -              switch(info->params.mode) {
 -              case MGSL_MODE_RAW:
 -              case MGSL_MODE_MONOSYNC:
 -              case MGSL_MODE_BISYNC:
 -                      set_desc_count(info->rbufs[i], info->raw_rx_size);
 -                      break;
 -              default:
 -                      set_desc_count(info->rbufs[i], DMABUFSIZE);
 -              }
 -
 +              set_desc_count(info->rbufs[i], info->rbuf_fill_level);
                if (i == last)
                        done = 1;
                if (++i == info->rbuf_count)
@@@ -4577,7 -4567,7 +4572,7 @@@ check_again
  
        DBGBH(("%s rx frame status=%04X size=%d\n",
                info->device_name, status, framesize));
 -      DBGDATA(info, info->rbufs[start].buf, min_t(int, framesize, DMABUFSIZE), "rx");
 +      DBGDATA(info, info->rbufs[start].buf, min_t(int, framesize, info->rbuf_fill_level), "rx");
  
        if (framesize) {
                if (!(info->params.crc_type & HDLC_CRC_RETURN_EX)) {
                        info->icount.rxok++;
  
                        while(copy_count) {
 -                              int partial_count = min(copy_count, DMABUFSIZE);
 +                              int partial_count = min_t(int, copy_count, info->rbuf_fill_level);
                                memcpy(p, info->rbufs[i].buf, partial_count);
                                p += partial_count;
                                copy_count -= partial_count;
@@@ -4689,56 -4679,6 +4684,56 @@@ static unsigned int free_tbuf_count(str
  }
  
  /*
 + * return number of bytes in unsent transmit DMA buffers
 + * and the serial controller tx FIFO
 + */
 +static unsigned int tbuf_bytes(struct slgt_info *info)
 +{
 +      unsigned int total_count = 0;
 +      unsigned int i = info->tbuf_current;
 +      unsigned int reg_value;
 +      unsigned int count;
 +      unsigned int active_buf_count = 0;
 +
 +      /*
 +       * Add descriptor counts for all tx DMA buffers.
 +       * If count is zero (cleared by DMA controller after read),
 +       * the buffer is complete or is actively being read from.
 +       *
 +       * Record buf_count of last buffer with zero count starting
 +       * from current ring position. buf_count is mirror
 +       * copy of count and is not cleared by serial controller.
 +       * If DMA controller is active, that buffer is actively
 +       * being read so add to total.
 +       */
 +      do {
 +              count = desc_count(info->tbufs[i]);
 +              if (count)
 +                      total_count += count;
 +              else if (!total_count)
 +                      active_buf_count = info->tbufs[i].buf_count;
 +              if (++i == info->tbuf_count)
 +                      i = 0;
 +      } while (i != info->tbuf_current);
 +
 +      /* read tx DMA status register */
 +      reg_value = rd_reg32(info, TDCSR);
 +
 +      /* if tx DMA active, last zero count buffer is in use */
 +      if (reg_value & BIT0)
 +              total_count += active_buf_count;
 +
 +      /* add tx FIFO count = reg_value[15..8] */
 +      total_count += (reg_value >> 8) & 0xff;
 +
 +      /* if transmitter active add one byte for shift register */
 +      if (info->tx_active)
 +              total_count++;
 +
 +      return total_count;
 +}
 +
 +/*
   * load transmit DMA buffer(s) with data
   */
  static void tx_load(struct slgt_info *info, const char *buf, unsigned int size)
                        set_desc_eof(*d, 0);
  
                set_desc_count(*d, count);
 +              d->buf_count = count;
        }
  
        info->tbuf_current = i;
@@@ -270,7 -270,6 +270,6 @@@ typedef struct _synclinkmp_info 
  
        /* SPPP/Cisco HDLC device parts */
        int netcount;
-       int dosyncppp;
        spinlock_t netlock;
  
  #if SYNCLINK_GENERIC_HDLC
@@@ -469,13 -468,11 +468,11 @@@ static int ttymajor = 0
   */
  static int debug_level = 0;
  static int maxframe[MAX_DEVICES] = {0,};
- static int dosyncppp[MAX_DEVICES] = {0,};
  
  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.38 $";
@@@ -527,7 -524,7 +524,7 @@@ static int  read_proc(char *page, char 
  static int  chars_in_buffer(struct tty_struct *tty);
  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);
 +static int set_break(struct tty_struct *tty, int break_state);
  
  #if SYNCLINK_GENERIC_HDLC
  #define dev_to_port(D) (dev_to_hdlc(D)->priv)
@@@ -552,7 -549,7 +549,7 @@@ static int  wait_mgsl_event(SLMP_INFO *
  static int  tiocmget(struct tty_struct *tty, struct file *file);
  static int  tiocmset(struct tty_struct *tty, struct file *file,
                     unsigned int set, unsigned int clear);
 -static void set_break(struct tty_struct *tty, int break_state);
 +static int  set_break(struct tty_struct *tty, int break_state);
  
  static void add_device(SLMP_INFO *info);
  static void device_init(int adapter_num, struct pci_dev *pdev);
@@@ -1587,7 -1584,7 +1584,7 @@@ static void unthrottle(struct tty_struc
  /* set or clear transmit break condition
   * break_state        -1=set break condition, 0=clear
   */
 -static void set_break(struct tty_struct *tty, int break_state)
 +static int set_break(struct tty_struct *tty, int break_state)
  {
        unsigned char RegValue;
        SLMP_INFO * info = (SLMP_INFO *)tty->driver_data;
                         __FILE__,__LINE__, info->device_name, break_state);
  
        if (sanity_check(info, tty->name, "set_break"))
 -              return;
 +              return -EINVAL;
  
        spin_lock_irqsave(&info->lock,flags);
        RegValue = read_reg(info, CTL);
                RegValue &= ~BIT3;
        write_reg(info, CTL, RegValue);
        spin_unlock_irqrestore(&info->lock,flags);
 +      return 0;
  }
  
  #if SYNCLINK_GENERIC_HDLC
@@@ -3752,7 -3748,6 +3749,6 @@@ static void add_device(SLMP_INFO *info
        if (info->line < MAX_DEVICES) {
                if (maxframe[info->line])
                        info->max_frame_size = maxframe[info->line];
-               info->dosyncppp = dosyncppp[info->line];
        }
  
        synclinkmp_device_count++;
diff --combined drivers/net/wan/Kconfig
@@@ -25,7 -25,7 +25,7 @@@ if WA
  # There is no way to detect a comtrol sv11 - force it modular for now.
  config HOSTESS_SV11
        tristate "Comtrol Hostess SV-11 support"
-       depends on ISA && m && ISA_DMA_API && INET
+       depends on ISA && m && ISA_DMA_API && INET && HDLC
        help
          Driver for Comtrol Hostess SV-11 network card which
          operates on low speed synchronous serial links at up to
@@@ -37,7 -37,7 +37,7 @@@
  # The COSA/SRP driver has not been tested as non-modular yet.
  config COSA
        tristate "COSA/SRP sync serial boards support"
-       depends on ISA && m && ISA_DMA_API
+       depends on ISA && m && ISA_DMA_API && HDLC
        ---help---
          Driver for COSA and SRP synchronous serial boards.
  
@@@ -61,7 -61,7 +61,7 @@@
  #
  config LANMEDIA
        tristate "LanMedia Corp. SSI/V.35, T1/E1, HSSI, T3 boards"
-       depends on PCI && VIRT_TO_BUS
+       depends on PCI && VIRT_TO_BUS && HDLC
        ---help---
          Driver for the following Lan Media family of serial boards:
  
@@@ -78,9 -78,8 +78,8 @@@
          - LMC 5245 board connects directly to a T3 circuit saving the
          additional external hardware.
  
-         To change setting such as syncPPP vs Cisco HDLC or clock source you
-         will need lmcctl.  It is available at <ftp://ftp.lanmedia.com/>
-         (broken link).
+         To change setting such as clock source you will need lmcctl.
+         It is available at <ftp://ftp.lanmedia.com/> (broken link).
  
          To compile this driver as a module, choose M here: the
          module will be called lmc.
@@@ -88,7 -87,7 +87,7 @@@
  # There is no way to detect a Sealevel board. Force it modular
  config SEALEVEL_4021
        tristate "Sealevel Systems 4021 support"
-       depends on ISA && m && ISA_DMA_API && INET
+       depends on ISA && m && ISA_DMA_API && INET && HDLC
        help
          This is a driver for the Sealevel Systems ACB 56 serial I/O adapter.
  
@@@ -154,6 -153,8 +153,6 @@@ config HDLC_PP
        help
          Generic HDLC driver supporting PPP over WAN connections.
  
 -        It will be replaced by new PPP implementation in Linux 2.6.26.
 -
          If unsure, say N.
  
  config HDLC_X25
diff --combined drivers/net/wan/cosa.c
@@@ -2,6 -2,7 +2,7 @@@
  
  /*
   *  Copyright (C) 1995-1997  Jan "Yenya" Kasprzak <kas@fi.muni.cz>
+  *  Generic HDLC port Copyright (C) 2008 Krzysztof Halasa <khc@pm.waw.pl>
   *
   *  This program is free software; you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
@@@ -54,7 -55,7 +55,7 @@@
   *
   * The Linux driver (unlike the present *BSD drivers :-) can work even
   * for the COSA and SRP in one computer and allows each channel to work
-  * in one of the three modes (character device, Cisco HDLC, Sync PPP).
+  * in one of the two modes (character or network device).
   *
   * AUTHOR
   *
   * The Comtrol Hostess SV11 driver by Alan Cox
   * The Sync PPP/Cisco HDLC layer (syncppp.c) ported to Linux by Alan Cox
   */
- /*
-  *     5/25/1999 : Marcelo Tosatti <marcelo@conectiva.com.br>
-  *             fixed a deadlock in cosa_sppp_open
-  */
\f
- /* ---------- Headers, macros, data structures ---------- */
  
  #include <linux/module.h>
  #include <linux/kernel.h>
@@@ -86,6 -81,7 +81,7 @@@
  #include <linux/fs.h>
  #include <linux/interrupt.h>
  #include <linux/delay.h>
+ #include <linux/hdlc.h>
  #include <linux/errno.h>
  #include <linux/ioport.h>
  #include <linux/netdevice.h>
  #include <linux/mutex.h>
  #include <linux/device.h>
  #include <linux/smp_lock.h>
- #undef COSA_SLOW_IO   /* for testing purposes only */
  #include <asm/io.h>
  #include <asm/dma.h>
  #include <asm/byteorder.h>
  
- #include <net/syncppp.h>
+ #undef COSA_SLOW_IO   /* for testing purposes only */
  #include "cosa.h"
  
  /* Maximum length of the identification string. */
  /* Per-channel data structure */
  
  struct channel_data {
-       void *if_ptr;   /* General purpose pointer (used by SPPP) */
        int usage;      /* Usage count; >0 for chrdev, -1 for netdev */
        int num;        /* Number of the channel */
        struct cosa_data *cosa; /* Pointer to the per-card structure */
        wait_queue_head_t txwaitq, rxwaitq;
        int tx_status, rx_status;
  
-       /* SPPP/HDLC device parts */
-       struct ppp_device pppdev;
+       /* generic HDLC device parts */
+       struct net_device *netdev;
        struct sk_buff *rx_skb, *tx_skb;
-       struct net_device_stats stats;
  };
  
  /* cosa->firmware_status bits */
@@@ -281,21 -273,19 +273,19 @@@ static int cosa_start_tx(struct channel
  static void cosa_kick(struct cosa_data *cosa);
  static int cosa_dma_able(struct channel_data *chan, char *buf, int data);
  
- /* SPPP/HDLC stuff */
- static void sppp_channel_init(struct channel_data *chan);
- static void sppp_channel_delete(struct channel_data *chan);
- static int cosa_sppp_open(struct net_device *d);
- static int cosa_sppp_close(struct net_device *d);
- static void cosa_sppp_timeout(struct net_device *d);
- static int cosa_sppp_tx(struct sk_buff *skb, struct net_device *d);
- static char *sppp_setup_rx(struct channel_data *channel, int size);
- static int sppp_rx_done(struct channel_data *channel);
- static int sppp_tx_done(struct channel_data *channel, int size);
- static int cosa_sppp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
- static struct net_device_stats *cosa_net_stats(struct net_device *dev);
+ /* Network device stuff */
+ static int cosa_net_attach(struct net_device *dev, unsigned short encoding,
+                          unsigned short parity);
+ static int cosa_net_open(struct net_device *d);
+ static int cosa_net_close(struct net_device *d);
+ static void cosa_net_timeout(struct net_device *d);
+ static int cosa_net_tx(struct sk_buff *skb, struct net_device *d);
+ static char *cosa_net_setup_rx(struct channel_data *channel, int size);
+ static int cosa_net_rx_done(struct channel_data *channel);
+ static int cosa_net_tx_done(struct channel_data *channel, int size);
+ static int cosa_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
  
  /* Character device */
- static void chardev_channel_init(struct channel_data *chan);
  static char *chrdev_setup_rx(struct channel_data *channel, int size);
  static int chrdev_rx_done(struct channel_data *channel);
  static int chrdev_tx_done(struct channel_data *channel, int size);
@@@ -357,17 -347,17 +347,17 @@@ static void debug_status_in(struct cosa
  static void debug_status_out(struct cosa_data *cosa, int status);
  #endif
  
\f
+ static inline struct channel_data* dev_to_chan(struct net_device *dev)
+ {
+       return (struct channel_data *)dev_to_hdlc(dev)->priv;
+ }
  /* ---------- Initialization stuff ---------- */
  
  static int __init cosa_init(void)
  {
        int i, err = 0;
  
-       printk(KERN_INFO "cosa v1.08 (c) 1997-2000 Jan Kasprzak <kas@fi.muni.cz>\n");
- #ifdef CONFIG_SMP
-       printk(KERN_INFO "cosa: SMP found. Please mail any success/failure reports to the author.\n");
- #endif
        if (cosa_major > 0) {
                if (register_chrdev(cosa_major, "cosa", &cosa_fops)) {
                        printk(KERN_WARNING "cosa: unable to get major %d\n",
                err = PTR_ERR(cosa_class);
                goto out_chrdev;
        }
 -      for (i=0; i<nr_cards; i++) {
 -              device_create(cosa_class, NULL, MKDEV(cosa_major, i), "cosa%d", i);
 -      }
 +      for (i = 0; i < nr_cards; i++)
 +              device_create_drvdata(cosa_class, NULL, MKDEV(cosa_major, i),
 +                                    NULL, "cosa%d", i);
        err = 0;
        goto out;
-       
  out_chrdev:
        unregister_chrdev(cosa_major, "cosa");
  out:
@@@ -414,43 -404,29 +404,29 @@@ static void __exit cosa_exit(void
  {
        struct cosa_data *cosa;
        int i;
-       printk(KERN_INFO "Unloading the cosa module\n");
  
-       for (i=0; i<nr_cards; i++)
+       for (i = 0; i < nr_cards; i++)
                device_destroy(cosa_class, MKDEV(cosa_major, i));
        class_destroy(cosa_class);
-       for (cosa=cosa_cards; nr_cards--; cosa++) {
+       for (cosa = cosa_cards; nr_cards--; cosa++) {
                /* Clean up the per-channel data */
-               for (i=0; i<cosa->nchannels; i++) {
+               for (i = 0; i < cosa->nchannels; i++) {
                        /* Chardev driver has no alloc'd per-channel data */
-                       sppp_channel_delete(cosa->chan+i);
+                       unregister_hdlc_device(cosa->chan[i].netdev);
+                       free_netdev(cosa->chan[i].netdev);
                }
                /* Clean up the per-card data */
                kfree(cosa->chan);
                kfree(cosa->bouncebuf);
                free_irq(cosa->irq, cosa);
                free_dma(cosa->dma);
-               release_region(cosa->datareg,is_8bit(cosa)?2:4);
+               release_region(cosa->datareg, is_8bit(cosa) ? 2 : 4);
        }
        unregister_chrdev(cosa_major, "cosa");
  }
  module_exit(cosa_exit);
  
- /*
-  * This function should register all the net devices needed for the
-  * single channel.
-  */
- static __inline__ void channel_init(struct channel_data *chan)
- {
-       sprintf(chan->name, "cosa%dc%d", chan->cosa->num, chan->num);
-       /* Initialize the chardev data structures */
-       chardev_channel_init(chan);
-       /* Register the sppp interface */
-       sppp_channel_init(chan);
- }
-       
  static int cosa_probe(int base, int irq, int dma)
  {
        struct cosa_data *cosa = cosa_cards+nr_cards;
        /* Initialize the per-channel data */
        cosa->chan = kcalloc(cosa->nchannels, sizeof(struct channel_data), GFP_KERNEL);
        if (!cosa->chan) {
-               err = -ENOMEM;
+               err = -ENOMEM;
                goto err_out3;
        }
-       for (i=0; i<cosa->nchannels; i++) {
-               cosa->chan[i].cosa = cosa;
-               cosa->chan[i].num = i;
-               channel_init(cosa->chan+i);
+       for (i = 0; i < cosa->nchannels; i++) {
+               struct channel_data *chan = &cosa->chan[i];
+               chan->cosa = cosa;
+               chan->num = i;
+               sprintf(chan->name, "cosa%dc%d", chan->cosa->num, i);
+               /* Initialize the chardev data structures */
+               mutex_init(&chan->rlock);
+               init_MUTEX(&chan->wsem);
+               /* Register the network interface */
+               if (!(chan->netdev = alloc_hdlcdev(chan))) {
+                       printk(KERN_WARNING "%s: alloc_hdlcdev failed.\n",
+                              chan->name);
+                       goto err_hdlcdev;
+               }
+               dev_to_hdlc(chan->netdev)->attach = cosa_net_attach;
+               dev_to_hdlc(chan->netdev)->xmit = cosa_net_tx;
+               chan->netdev->open = cosa_net_open;
+               chan->netdev->stop = cosa_net_close;
+               chan->netdev->do_ioctl = cosa_net_ioctl;
+               chan->netdev->tx_timeout = cosa_net_timeout;
+               chan->netdev->watchdog_timeo = TX_TIMEOUT;
+               chan->netdev->base_addr = chan->cosa->datareg;
+               chan->netdev->irq = chan->cosa->irq;
+               chan->netdev->dma = chan->cosa->dma;
+               if (register_hdlc_device(chan->netdev)) {
+                       printk(KERN_WARNING "%s: register_hdlc_device()"
+                              " failed.\n", chan->netdev->name);
+                       free_netdev(chan->netdev);
+                       goto err_hdlcdev;
+               }
        }
  
        printk (KERN_INFO "cosa%d: %s (%s at 0x%x irq %d dma %d), %d channels\n",
                cosa->datareg, cosa->irq, cosa->dma, cosa->nchannels);
  
        return nr_cards++;
+ err_hdlcdev:
+       while (i-- > 0) {
+               unregister_hdlc_device(cosa->chan[i].netdev);
+               free_netdev(cosa->chan[i].netdev);
+       }
+       kfree(cosa->chan);
  err_out3:
        kfree(cosa->bouncebuf);
  err_out2:
        free_dma(cosa->dma);
  err_out1:
        free_irq(cosa->irq, cosa);
- err_out:      
+ err_out:
        release_region(cosa->datareg,is_8bit(cosa)?2:4);
        printk(KERN_NOTICE "cosa%d: allocating resources failed\n",
               cosa->num);
  }
  
  \f
- /*---------- SPPP/HDLC netdevice ---------- */
- static void cosa_setup(struct net_device *d)
- {
-       d->open = cosa_sppp_open;
-       d->stop = cosa_sppp_close;
-       d->hard_start_xmit = cosa_sppp_tx;
-       d->do_ioctl = cosa_sppp_ioctl;
-       d->get_stats = cosa_net_stats;
-       d->tx_timeout = cosa_sppp_timeout;
-       d->watchdog_timeo = TX_TIMEOUT;
- }
- static void sppp_channel_init(struct channel_data *chan)
- {
-       struct net_device *d;
-       chan->if_ptr = &chan->pppdev;
-       d = alloc_netdev(0, chan->name, cosa_setup);
-       if (!d) {
-               printk(KERN_WARNING "%s: alloc_netdev failed.\n", chan->name);
-               return;
-       }
-       chan->pppdev.dev = d;
-       d->base_addr = chan->cosa->datareg;
-       d->irq = chan->cosa->irq;
-       d->dma = chan->cosa->dma;
-       d->ml_priv = chan;
-       sppp_attach(&chan->pppdev);
-       if (register_netdev(d)) {
-               printk(KERN_WARNING "%s: register_netdev failed.\n", d->name);
-               sppp_detach(d);
-               free_netdev(d);
-               chan->pppdev.dev = NULL;
-               return;
-       }
- }
+ /*---------- network device ---------- */
  
- static void sppp_channel_delete(struct channel_data *chan)
+ static int cosa_net_attach(struct net_device *dev, unsigned short encoding,
+                          unsigned short parity)
  {
-       unregister_netdev(chan->pppdev.dev);
-       sppp_detach(chan->pppdev.dev);
-       free_netdev(chan->pppdev.dev);
-       chan->pppdev.dev = NULL;
+       if (encoding == ENCODING_NRZ && parity == PARITY_CRC16_PR1_CCITT)
+               return 0;
+       return -EINVAL;
  }
  
- static int cosa_sppp_open(struct net_device *d)
+ static int cosa_net_open(struct net_device *dev)
  {
-       struct channel_data *chan = d->ml_priv;
+       struct channel_data *chan = dev_to_chan(dev);
        int err;
        unsigned long flags;
  
        }
        spin_lock_irqsave(&chan->cosa->lock, flags);
        if (chan->usage != 0) {
-               printk(KERN_WARNING "%s: sppp_open called with usage count %d\n",
-                       chan->name, chan->usage);
+               printk(KERN_WARNING "%s: cosa_net_open called with usage count"
+                      " %d\n", chan->name, chan->usage);
                spin_unlock_irqrestore(&chan->cosa->lock, flags);
                return -EBUSY;
        }
-       chan->setup_rx = sppp_setup_rx;
-       chan->tx_done = sppp_tx_done;
-       chan->rx_done = sppp_rx_done;
-       chan->usage=-1;
+       chan->setup_rx = cosa_net_setup_rx;
+       chan->tx_done = cosa_net_tx_done;
+       chan->rx_done = cosa_net_rx_done;
+       chan->usage = -1;
        chan->cosa->usage++;
        spin_unlock_irqrestore(&chan->cosa->lock, flags);
  
-       err = sppp_open(d);
+       err = hdlc_open(dev);
        if (err) {
                spin_lock_irqsave(&chan->cosa->lock, flags);
-               chan->usage=0;
+               chan->usage = 0;
                chan->cosa->usage--;
-               
                spin_unlock_irqrestore(&chan->cosa->lock, flags);
                return err;
        }
  
-       netif_start_queue(d);
+       netif_start_queue(dev);
        cosa_enable_rx(chan);
        return 0;
  }
  
- static int cosa_sppp_tx(struct sk_buff *skb, struct net_device *dev)
+ static int cosa_net_tx(struct sk_buff *skb, struct net_device *dev)
  {
-       struct channel_data *chan = dev->ml_priv;
+       struct channel_data *chan = dev_to_chan(dev);
  
        netif_stop_queue(dev);
  
        return 0;
  }
  
- static void cosa_sppp_timeout(struct net_device *dev)
+ static void cosa_net_timeout(struct net_device *dev)
  {
-       struct channel_data *chan = dev->ml_priv;
+       struct channel_data *chan = dev_to_chan(dev);
  
        if (test_bit(RXBIT, &chan->cosa->rxtx)) {
-               chan->stats.rx_errors++;
-               chan->stats.rx_missed_errors++;
+               chan->netdev->stats.rx_errors++;
+               chan->netdev->stats.rx_missed_errors++;
        } else {
-               chan->stats.tx_errors++;
-               chan->stats.tx_aborted_errors++;
+               chan->netdev->stats.tx_errors++;
+               chan->netdev->stats.tx_aborted_errors++;
        }
        cosa_kick(chan->cosa);
        if (chan->tx_skb) {
        netif_wake_queue(dev);
  }
  
- static int cosa_sppp_close(struct net_device *d)
+ static int cosa_net_close(struct net_device *dev)
  {
-       struct channel_data *chan = d->ml_priv;
+       struct channel_data *chan = dev_to_chan(dev);
        unsigned long flags;
  
-       netif_stop_queue(d);
-       sppp_close(d);
+       netif_stop_queue(dev);
+       hdlc_close(dev);
        cosa_disable_rx(chan);
        spin_lock_irqsave(&chan->cosa->lock, flags);
        if (chan->rx_skb) {
                kfree_skb(chan->tx_skb);
                chan->tx_skb = NULL;
        }
-       chan->usage=0;
+       chan->usage = 0;
        chan->cosa->usage--;
        spin_unlock_irqrestore(&chan->cosa->lock, flags);
        return 0;
  }
  
- static char *sppp_setup_rx(struct channel_data *chan, int size)
+ static char *cosa_net_setup_rx(struct channel_data *chan, int size)
  {
        /*
         * We can safely fall back to non-dma-able memory, because we have
        if (chan->rx_skb == NULL) {
                printk(KERN_NOTICE "%s: Memory squeeze, dropping packet\n",
                        chan->name);
-               chan->stats.rx_dropped++;
+               chan->netdev->stats.rx_dropped++;
                return NULL;
        }
-       chan->pppdev.dev->trans_start = jiffies;
+       chan->netdev->trans_start = jiffies;
        return skb_put(chan->rx_skb, size);
  }
  
- static int sppp_rx_done(struct channel_data *chan)
+ static int cosa_net_rx_done(struct channel_data *chan)
  {
        if (!chan->rx_skb) {
                printk(KERN_WARNING "%s: rx_done with empty skb!\n",
                        chan->name);
-               chan->stats.rx_errors++;
-               chan->stats.rx_frame_errors++;
+               chan->netdev->stats.rx_errors++;
+               chan->netdev->stats.rx_frame_errors++;
                return 0;
        }
-       chan->rx_skb->protocol = htons(ETH_P_WAN_PPP);
-       chan->rx_skb->dev = chan->pppdev.dev;
+       chan->rx_skb->protocol = hdlc_type_trans(chan->rx_skb, chan->netdev);
+       chan->rx_skb->dev = chan->netdev;
        skb_reset_mac_header(chan->rx_skb);
-       chan->stats.rx_packets++;
-       chan->stats.rx_bytes += chan->cosa->rxsize;
+       chan->netdev->stats.rx_packets++;
+       chan->netdev->stats.rx_bytes += chan->cosa->rxsize;
        netif_rx(chan->rx_skb);
        chan->rx_skb = NULL;
-       chan->pppdev.dev->last_rx = jiffies;
+       chan->netdev->last_rx = jiffies;
        return 0;
  }
  
  /* ARGSUSED */
- static int sppp_tx_done(struct channel_data *chan, int size)
+ static int cosa_net_tx_done(struct channel_data *chan, int size)
  {
        if (!chan->tx_skb) {
                printk(KERN_WARNING "%s: tx_done with empty skb!\n",
                        chan->name);
-               chan->stats.tx_errors++;
-               chan->stats.tx_aborted_errors++;
+               chan->netdev->stats.tx_errors++;
+               chan->netdev->stats.tx_aborted_errors++;
                return 1;
        }
        dev_kfree_skb_irq(chan->tx_skb);
        chan->tx_skb = NULL;
-       chan->stats.tx_packets++;
-       chan->stats.tx_bytes += size;
-       netif_wake_queue(chan->pppdev.dev);
+       chan->netdev->stats.tx_packets++;
+       chan->netdev->stats.tx_bytes += size;
+       netif_wake_queue(chan->netdev);
        return 1;
  }
  
- static struct net_device_stats *cosa_net_stats(struct net_device *dev)
- {
-       struct channel_data *chan = dev->ml_priv;
-       return &chan->stats;
- }
\f
  /*---------- Character device ---------- */
  
- static void chardev_channel_init(struct channel_data *chan)
- {
-       mutex_init(&chan->rlock);
-       init_MUTEX(&chan->wsem);
- }
  static ssize_t cosa_read(struct file *file,
        char __user *buf, size_t count, loff_t *ppos)
  {
@@@ -1223,16 -1187,15 +1187,15 @@@ static int cosa_ioctl_common(struct cos
        return -ENOIOCTLCMD;
  }
  
- static int cosa_sppp_ioctl(struct net_device *dev, struct ifreq *ifr,
-       int cmd)
+ static int cosa_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
  {
        int rv;
-       struct channel_data *chan = dev->ml_priv;
-       rv = cosa_ioctl_common(chan->cosa, chan, cmd, (unsigned long)ifr->ifr_data);
-       if (rv == -ENOIOCTLCMD) {
-               return sppp_do_ioctl(dev, ifr, cmd);
-       }
-       return rv;
+       struct channel_data *chan = dev_to_chan(dev);
+       rv = cosa_ioctl_common(chan->cosa, chan, cmd,
+                              (unsigned long)ifr->ifr_data);
+       if (rv != -ENOIOCTLCMD)
+               return rv;
+       return hdlc_ioctl(dev, ifr, cmd);
  }
  
  static int cosa_chardev_ioctl(struct inode *inode, struct file *file,