summaryrefslogtreecommitdiffstats
path: root/drivers/net/wan
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-03-19 01:28:40 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-03-19 01:28:40 +0000
commit8abb719409c9060a7c0676f76e9182c1e0b8ca46 (patch)
treeb88cc5a6cd513a04a512b7e6215c873c90a1c5dd /drivers/net/wan
parentf01bd7aeafd95a08aafc9e3636bb26974df69d82 (diff)
Merge with 2.3.99-pre1.
Diffstat (limited to 'drivers/net/wan')
-rw-r--r--drivers/net/wan/Config.in19
-rw-r--r--drivers/net/wan/Makefile56
-rw-r--r--drivers/net/wan/comx-hw-comx.c1426
-rw-r--r--drivers/net/wan/comx-hw-locomx.c496
-rw-r--r--drivers/net/wan/comx-hw-mixcom.c948
-rw-r--r--drivers/net/wan/comx-proto-fr.c1006
-rw-r--r--drivers/net/wan/comx-proto-lapb.c548
-rw-r--r--drivers/net/wan/comx-proto-ppp.c269
-rw-r--r--drivers/net/wan/comx.c1238
-rw-r--r--drivers/net/wan/comx.h240
-rw-r--r--drivers/net/wan/comxhw.h113
-rw-r--r--drivers/net/wan/cosa.c10
-rw-r--r--drivers/net/wan/hscx.h103
-rw-r--r--drivers/net/wan/mixcom.h35
-rw-r--r--drivers/net/wan/sbni.c6
-rw-r--r--drivers/net/wan/sdla.c2
-rw-r--r--drivers/net/wan/syncppp.c142
-rw-r--r--drivers/net/wan/syncppp.h4
-rw-r--r--drivers/net/wan/z85230.c94
19 files changed, 6681 insertions, 74 deletions
diff --git a/drivers/net/wan/Config.in b/drivers/net/wan/Config.in
index bc4bd5331..e0a6f5e8f 100644
--- a/drivers/net/wan/Config.in
+++ b/drivers/net/wan/Config.in
@@ -16,6 +16,25 @@ if [ "$CONFIG_WAN" = "y" ]; then
dep_tristate 'COSA/SRP sync serial boards support' CONFIG_COSA m
+ #
+ # COMX drivers
+ #
+
+ tristate 'MultiGate (COMX) synchronous serial boards support' CONFIG_COMX
+ if [ "$CONFIG_COMX" != "n" ]; then
+ dep_tristate ' Support for COMX/CMX/HiCOMX boards' CONFIG_COMX_HW_COMX $CONFIG_COMX
+ dep_tristate ' Support for LoCOMX board' CONFIG_COMX_HW_LOCOMX $CONFIG_COMX
+ dep_tristate ' Support for MixCOM board' CONFIG_COMX_HW_MIXCOM $CONFIG_COMX
+ dep_tristate ' Support for HDLC and syncPPP protocols on MultiGate boards' CONFIG_COMX_PROTO_PPP $CONFIG_COMX
+ if [ "$CONFIG_LAPB" = "y" ]; then
+ dep_tristate ' Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_COMX
+ fi
+ if [ "$CONFIG_LAPB" = "m" ]; then
+ dep_tristate ' Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_LAPB
+ fi
+ dep_tristate ' Support for Frame Relay on MultiGate boards' CONFIG_COMX_PROTO_FR $CONFIG_COMX
+ fi
+
# There is no way to detect a Sealevel board. Force it modular
dep_tristate 'Sealevel Systems 4021 support' CONFIG_SEALEVEL_4021 m
diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile
index 67f6bb59b..b2cd8aafb 100644
--- a/drivers/net/wan/Makefile
+++ b/drivers/net/wan/Makefile
@@ -49,6 +49,58 @@ else
endif
endif
+ifeq ($(CONFIG_COMX_HW_COMX),y)
+L_OBJS += comx-hw-comx.o
+else
+ ifeq ($(CONFIG_COMX_HW_COMX),m)
+ M_OBJS += comx-hw-comx.o
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_HW_LOCOMX),y)
+L_OBJS += comx-hw-locomx.o
+CONFIG_85230_BUILTIN=y
+else
+ ifeq ($(CONFIG_COMX_HW_LOCOMX),m)
+ M_OBJS += comx-hw-locomx.o
+ CONFIG_85230_MODULE=y
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_HW_MIXCOM),y)
+L_OBJS += comx-hw-mixcom.o
+else
+ ifeq ($(CONFIG_COMX_HW_MIXCOM),m)
+ M_OBJS += comx-hw-mixcom.o
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_PPP),y)
+L_OBJS += comx-proto-ppp.o
+CONFIG_SYNCPPP_BUILTIN = y
+else
+ ifeq ($(CONFIG_COMX_PROTO_PPP),m)
+ M_OBJS += comx-proto-ppp.o
+ CONFIG_SYNCPPP_MODULE = y
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_LAPB),y)
+L_OBJS += comx-proto-lapb.o
+else
+ ifeq ($(CONFIG_COMX_PROTO_LAPB),m)
+ M_OBJS += comx-proto-lapb.o
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_FR),y)
+L_OBJS += comx-proto-fr.o
+else
+ ifeq ($(CONFIG_COMX_PROTO_FR),m)
+ M_OBJS += comx-proto-fr.o
+ endif
+endif
+
ifeq ($(CONFIG_COSA),y)
L_OBJS += cosa.o
CONFIG_SYNCPPP_BUILTIN = y
@@ -180,8 +232,8 @@ clean:
rm -f core *.o *.a *.s
wanpipe.o: $(WANPIPE_OBJS)
- ld -r -o $@ $(WANPIPE_OBJS)
+ $(LD) -r -o $@ $(WANPIPE_OBJS)
cyclomx.o: $(CYCLOMX_OBJS)
- ld -r -o $@ $(CYCLOMX_OBJS)
+ $(LD) -r -o $@ $(CYCLOMX_OBJS)
diff --git a/drivers/net/wan/comx-hw-comx.c b/drivers/net/wan/comx-hw-comx.c
new file mode 100644
index 000000000..7381dc8a9
--- /dev/null
+++ b/drivers/net/wan/comx-hw-comx.c
@@ -0,0 +1,1426 @@
+/*
+ * Hardware-level driver for the COMX and HICOMX cards
+ * for Linux kernel 2.2.X
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Rewritten by: Tivadar Szemethy <tiv@itc.hu>
+ * Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-2000 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.80 (99/06/11):
+ * - port back to kernel, add support builtin driver
+ * - cleaned up the source code a bit
+ *
+ * Version 0.81 (99/06/22):
+ * - cleaned up the board load functions, no more long reset
+ * timeouts
+ * - lower modem lines on close
+ * - some interrupt handling fixes
+ *
+ * Version 0.82 (99/08/24):
+ * - fix multiple board support
+ *
+ * Version 0.83 (99/11/30):
+ * - interrupt handling and locking fixes during initalization
+ * - really fix multiple board support
+ *
+ * Version 0.84 (99/12/02):
+ * - some workarounds for problematic hardware/firmware
+ *
+ * Version 0.85 (00/01/14):
+ * - some additional workarounds :/
+ * - printk cleanups
+ */
+
+#define VERSION "0.85"
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>, Tivadar Szemethy <tiv@itc.hu>, Arpad Bakay");
+MODULE_DESCRIPTION("Hardware-level driver for the COMX and HICOMX adapters\n");
+
+#define COMX_readw(dev, offset) (readw(dev->mem_start + offset + \
+ (unsigned int)(((struct comx_privdata *)\
+ ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \
+ * COMX_CHANNEL_OFFSET))
+
+#define COMX_WRITE(dev, offset, value) (writew(value, dev->mem_start + offset \
+ + (unsigned int)(((struct comx_privdata *) \
+ ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \
+ * COMX_CHANNEL_OFFSET))
+
+#define COMX_CMD(dev, cmd) (COMX_WRITE(dev, OFF_A_L2_CMD, cmd))
+
+struct comx_firmware {
+ int len;
+ unsigned char *data;
+};
+
+struct comx_privdata {
+ struct comx_firmware *firmware;
+ u16 clock;
+ char channel; // channel no.
+ int memory_size;
+ short io_extent;
+ u_long histogram[5];
+};
+
+static struct net_device *memory_used[(COMX_MEM_MAX - COMX_MEM_MIN) / 0x10000];
+extern struct comx_hardware hicomx_hw;
+extern struct comx_hardware comx_hw;
+extern struct comx_hardware cmx_hw;
+
+static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static void COMX_board_on(struct net_device *dev)
+{
+ outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) |
+ COMX_ENABLE_BOARD_IT | COMX_ENABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void COMX_board_off(struct net_device *dev)
+{
+ outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) |
+ COMX_ENABLE_BOARD_IT), dev->base_addr);
+}
+
+static void HICOMX_board_on(struct net_device *dev)
+{
+ outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) |
+ HICOMX_ENABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void HICOMX_board_off(struct net_device *dev)
+{
+ outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) |
+ HICOMX_DISABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void COMX_set_clock(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+
+ COMX_WRITE(dev, OFF_A_L1_CLKINI, hw->clock);
+}
+
+static struct net_device *COMX_access_board(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct net_device *ret;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ unsigned long flags;
+
+
+ save_flags(flags); cli();
+
+ ret = memory_used[mempos];
+
+ if(ret == dev) {
+ goto out;
+ }
+
+ memory_used[mempos] = dev;
+
+ if (!ch->twin || ret != ch->twin) {
+ if (ret) ((struct comx_channel *)ret->priv)->HW_board_off(ret);
+ ch->HW_board_on(dev);
+ }
+out:
+ restore_flags(flags);
+ return ret;
+}
+
+static void COMX_release_board(struct net_device *dev, struct net_device *savep)
+{
+ unsigned long flags;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ struct comx_channel *ch = dev->priv;
+
+ save_flags(flags); cli();
+
+ if (memory_used[mempos] == savep) {
+ goto out;
+ }
+
+ memory_used[mempos] = savep;
+ if (!ch->twin || ch->twin != savep) {
+ ch->HW_board_off(dev);
+ if (savep) ((struct comx_channel*)savep->priv)->HW_board_on(savep);
+ }
+out:
+ restore_flags(flags);
+}
+
+static int COMX_txe(struct net_device *dev)
+{
+ struct net_device *savep;
+ struct comx_channel *ch = dev->priv;
+ int rc = 0;
+
+ savep = ch->HW_access_board(dev);
+ if (COMX_readw(dev,OFF_A_L2_LINKUP) == LINKUP_READY) {
+ rc = COMX_readw(dev,OFF_A_L2_TxEMPTY);
+ }
+ ch->HW_release_board(dev,savep);
+ if(rc==0xffff) {
+ printk(KERN_ERR "%s, OFF_A_L2_TxEMPTY is %d\n",dev->name, rc);
+ }
+ return rc;
+}
+
+static int COMX_send_packet(struct net_device *dev, struct sk_buff *skb)
+{
+ struct net_device *savep;
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ int ret = FRAME_DROPPED;
+ word tmp;
+
+ savep = ch->HW_access_board(dev);
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug_bytes(dev, skb->data, skb->len,"COMX_send packet");
+ }
+
+ if (skb->len > COMX_MAX_TX_SIZE) {
+ ret=FRAME_DROPPED;
+ goto out;
+ }
+
+ tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY);
+ if ((ch->line_status & LINE_UP) && tmp==1) {
+ int lensave = skb->len;
+ int dest = COMX_readw(dev, OFF_A_L2_TxBUFP);
+ word *data = (word *)skb->data;
+
+ if(dest==0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L2_TxBUFP is %d\n", dev->name, dest);
+ ret=FRAME_DROPPED;
+ goto out;
+ }
+
+ writew((unsigned short)skb->len, dev->mem_start + dest);
+ dest += 2;
+ while (skb->len > 1) {
+ writew(*data++, dev->mem_start + dest);
+ dest += 2; skb->len -= 2;
+ }
+ if (skb->len == 1) {
+ writew(*((byte *)data), dev->mem_start + dest);
+ }
+ writew(0, dev->mem_start + (int)hw->channel *
+ COMX_CHANNEL_OFFSET + OFF_A_L2_TxEMPTY);
+ ch->stats.tx_packets++;
+ ch->stats.tx_bytes += lensave;
+ ret = FRAME_ACCEPTED;
+ } else {
+ ch->stats.tx_dropped++;
+ printk(KERN_INFO "%s: frame dropped\n",dev->name);
+ if(tmp) {
+ printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n",dev->name,tmp);
+ }
+ }
+
+out:
+ ch->HW_release_board(dev, savep);
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+static inline int comx_read_buffer(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ word rbuf_offs;
+ struct sk_buff *skb;
+ word len;
+ int i=0;
+ word *writeptr;
+
+ i = 0;
+ rbuf_offs = COMX_readw(dev, OFF_A_L2_RxBUFP);
+ if(rbuf_offs == 0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L2_RxBUFP is %d\n",dev->name,rbuf_offs);
+ return 0;
+ }
+ len = readw(dev->mem_start + rbuf_offs);
+ if(len > COMX_MAX_RX_SIZE) {
+ printk(KERN_ERR "%s: packet length is %d\n",dev->name,len);
+ return 0;
+ }
+ if ((skb = dev_alloc_skb(len + 16)) == NULL) {
+ ch->stats.rx_dropped++;
+ COMX_WRITE(dev, OFF_A_L2_DAV, 0);
+ return 0;
+ }
+ rbuf_offs += 2;
+ skb_reserve(skb, 16);
+ skb_put(skb, len);
+ skb->dev = dev;
+ writeptr = (word *)skb->data;
+ while (i < len) {
+ *writeptr++ = readw(dev->mem_start + rbuf_offs);
+ rbuf_offs += 2;
+ i += 2;
+ }
+ COMX_WRITE(dev, OFF_A_L2_DAV, 0);
+ ch->stats.rx_packets++;
+ ch->stats.rx_bytes += len;
+ if (ch->debug_flags & DEBUG_HW_RX) {
+ comx_debug_skb(dev, skb, "COMX_interrupt receiving");
+ }
+ ch->LINE_rx(dev, skb);
+ return 1;
+}
+
+static inline char comx_line_change(struct net_device *dev, char linestat)
+{
+ struct comx_channel *ch=dev->priv;
+ char idle=1;
+
+
+ if (linestat & LINE_UP) { /* Vonal fol */
+ if (ch->lineup_delay) {
+ if (!test_and_set_bit(0, &ch->lineup_pending)) {
+ ch->lineup_timer.function = comx_lineup_func;
+ ch->lineup_timer.data = (unsigned long)dev;
+ ch->lineup_timer.expires = jiffies +
+ HZ*ch->lineup_delay;
+ add_timer(&ch->lineup_timer);
+ idle=0;
+ }
+ } else {
+ idle=0;
+ ch->LINE_status(dev, ch->line_status |= LINE_UP);
+ }
+ } else { /* Vonal le */
+ idle=0;
+ if (test_and_clear_bit(0, &ch->lineup_pending)) {
+ del_timer(&ch->lineup_timer);
+ } else {
+ ch->line_status &= ~LINE_UP;
+ if (ch->LINE_status) {
+ ch->LINE_status(dev, ch->line_status);
+ }
+ }
+ }
+ return idle;
+}
+
+
+
+static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = dev_id;
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct net_device *interrupted;
+ unsigned long jiffs;
+ char idle = 0;
+ int count = 0;
+ word tmp;
+
+ if (dev == NULL) {
+ printk(KERN_ERR "COMX_interrupt: irq %d for unknown device\n", irq);
+ return;
+ }
+
+ jiffs = jiffies;
+
+ interrupted = ch->HW_access_board(dev);
+
+ while (!idle && count < 5000) {
+ char channel = 0;
+ idle = 1;
+
+ while (channel < 2) {
+ char linestat = 0;
+ char buffers_emptied = 0;
+
+ if (channel == 1) {
+ if (ch->twin) {
+ dev = ch->twin;
+ ch = dev->priv;
+ hw = ch->HW_privdata;
+ } else {
+ break;
+ }
+ } else {
+ COMX_WRITE(dev, OFF_A_L1_REPENA,
+ COMX_readw(dev, OFF_A_L1_REPENA) & 0xFF00);
+ }
+ channel++;
+
+ if ((ch->init_status & (HW_OPEN | LINE_OPEN)) !=
+ (HW_OPEN | LINE_OPEN)) {
+ continue;
+ }
+
+ /* Collect stats */
+ tmp = COMX_readw(dev, OFF_A_L1_ABOREC);
+ COMX_WRITE(dev, OFF_A_L1_ABOREC, 0);
+ if(tmp==0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L1_ABOREC is %d\n",dev->name,tmp);
+ break;
+ } else {
+ ch->stats.rx_missed_errors += (tmp >> 8) & 0xff;
+ ch->stats.rx_over_errors += tmp & 0xff;
+ }
+ tmp = COMX_readw(dev, OFF_A_L1_CRCREC);
+ COMX_WRITE(dev, OFF_A_L1_CRCREC, 0);
+ if(tmp==0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L1_CRCREC is %d\n",dev->name,tmp);
+ break;
+ } else {
+ ch->stats.rx_crc_errors += (tmp >> 8) & 0xff;
+ ch->stats.rx_missed_errors += tmp & 0xff;
+ }
+
+ if ((ch->line_status & LINE_UP) && ch->LINE_rx) {
+ tmp=COMX_readw(dev, OFF_A_L2_DAV);
+ while (tmp==1) {
+ idle=0;
+ buffers_emptied+=comx_read_buffer(dev);
+ tmp=COMX_readw(dev, OFF_A_L2_DAV);
+ }
+ if(tmp) {
+ printk(KERN_ERR "%s: OFF_A_L2_DAV is %d\n", dev->name, tmp);
+ break;
+ }
+ }
+
+ tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY);
+ if (tmp==1 && ch->LINE_tx) {
+ ch->LINE_tx(dev);
+ }
+ if(tmp==0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n", dev->name, tmp);
+ break;
+ }
+
+ if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
+ linestat &= ~LINE_UP;
+ } else {
+ linestat |= LINE_UP;
+ }
+
+ if ((linestat & LINE_UP) != (ch->line_status & LINE_UP)) {
+ ch->stats.tx_carrier_errors++;
+ idle &= comx_line_change(dev,linestat);
+ }
+
+ hw->histogram[(int)buffers_emptied]++;
+ }
+ count++;
+ }
+
+ if(count==5000) {
+ printk(KERN_WARNING "%s: interrupt stuck\n",dev->name);
+ }
+
+ ch->HW_release_board(dev, interrupted);
+}
+
+static int COMX_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ unsigned long jiffs;
+ int twin_open=0;
+ int retval;
+ struct net_device *savep;
+
+ if (!dev->base_addr || !dev->irq || !dev->mem_start) {
+ return -ENODEV;
+ }
+
+ if (ch->twin && (((struct comx_channel *)(ch->twin->priv))->init_status & HW_OPEN)) {
+ twin_open=1;
+ }
+
+ if (!twin_open) {
+ if (check_region(dev->base_addr, hw->io_extent)) {
+ return -EAGAIN;
+ }
+ if (request_irq(dev->irq, COMX_interrupt, 0, dev->name,
+ (void *)dev)) {
+ printk(KERN_ERR "comx-hw-comx: unable to obtain irq %d\n", dev->irq);
+ return -EAGAIN;
+ }
+ ch->init_status |= IRQ_ALLOCATED;
+ request_region(dev->base_addr, hw->io_extent, dev->name);
+ if (!ch->HW_load_board || ch->HW_load_board(dev)) {
+ ch->init_status &= ~IRQ_ALLOCATED;
+ retval=-ENODEV;
+ goto error;
+ }
+ }
+
+ savep = ch->HW_access_board(dev);
+ COMX_WRITE(dev, OFF_A_L2_LINKUP, 0);
+
+ if (ch->HW_set_clock) {
+ ch->HW_set_clock(dev);
+ }
+
+ COMX_CMD(dev, COMX_CMD_INIT);
+ jiffs = jiffies;
+ while (COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiffs + HZ) {
+ schedule_timeout(1);
+ }
+
+ if (jiffies >= jiffs + HZ) {
+ printk(KERN_ERR "%s: board timeout on INIT command\n", dev->name);
+ ch->HW_release_board(dev, savep);
+ retval=-EIO;
+ goto error;
+ }
+ udelay(1000);
+
+ COMX_CMD(dev, COMX_CMD_OPEN);
+
+ jiffs = jiffies;
+ while (COMX_readw(dev, OFF_A_L2_LINKUP) != 3 && jiffies < jiffs + HZ) {
+ schedule_timeout(1);
+ }
+
+ if (jiffies >= jiffs + HZ) {
+ printk(KERN_ERR "%s: board timeout on OPEN command\n", dev->name);
+ ch->HW_release_board(dev, savep);
+ retval=-EIO;
+ goto error;
+ }
+
+ ch->init_status |= HW_OPEN;
+
+ /* Ez eleg ciki, de ilyen a rendszer */
+ if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
+ ch->line_status &= ~LINE_UP;
+ } else {
+ ch->line_status |= LINE_UP;
+ }
+
+ if (ch->LINE_status) {
+ ch->LINE_status(dev, ch->line_status);
+ }
+
+ ch->HW_release_board(dev, savep);
+
+ for ( ; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IRQ) == 0
+ || strcmp(procfile->name, FILENAME_IO) == 0
+ || strcmp(procfile->name, FILENAME_MEMADDR) == 0
+ || strcmp(procfile->name, FILENAME_CHANNEL) == 0
+ || strcmp(procfile->name, FILENAME_FIRMWARE) == 0
+ || strcmp(procfile->name, FILENAME_CLOCK) == 0) {
+ procfile->mode = S_IFREG | 0444;
+
+ }
+ }
+
+ return 0;
+
+error:
+ if(!twin_open) {
+ release_region(dev->base_addr, hw->io_extent);
+ free_irq(dev->irq, (void *)dev);
+ }
+ return retval;
+
+}
+
+static int COMX_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct comx_channel *twin_ch;
+ struct net_device *savep;
+
+ savep = ch->HW_access_board(dev);
+
+ COMX_CMD(dev, COMX_CMD_CLOSE);
+ udelay(1000);
+ COMX_CMD(dev, COMX_CMD_EXIT);
+
+ ch->HW_release_board(dev, savep);
+
+ if (ch->init_status & IRQ_ALLOCATED) {
+ free_irq(dev->irq, (void *)dev);
+ ch->init_status &= ~IRQ_ALLOCATED;
+ }
+ release_region(dev->base_addr, hw->io_extent);
+
+ if (ch->twin && (twin_ch = ch->twin->priv) &&
+ (twin_ch->init_status & HW_OPEN)) {
+ /* Pass the irq to the twin */
+ if (request_irq(dev->irq, COMX_interrupt, 0, ch->twin->name,
+ (void *)ch->twin) == 0) {
+ twin_ch->init_status |= IRQ_ALLOCATED;
+ }
+ }
+
+ for ( ; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IRQ) == 0
+ || strcmp(procfile->name, FILENAME_IO) == 0
+ || strcmp(procfile->name, FILENAME_MEMADDR) == 0
+ || strcmp(procfile->name, FILENAME_CHANNEL) == 0
+ || strcmp(procfile->name, FILENAME_FIRMWARE) == 0
+ || strcmp(procfile->name, FILENAME_CLOCK) == 0) {
+ procfile->mode = S_IFREG | 0644;
+ }
+ }
+
+ ch->init_status &= ~HW_OPEN;
+ return 0;
+}
+
+static int COMX_statistics(struct net_device *dev, char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct net_device *savep;
+ int len = 0;
+
+ savep = ch->HW_access_board(dev);
+
+ len += sprintf(page + len, "Board data: %s %s %s %s\nPBUFOVR: %02x, "
+ "MODSTAT: %02x, LINKUP: %02x, DAV: %02x\nRxBUFP: %02x, "
+ "TxEMPTY: %02x, TxBUFP: %02x\n",
+ (ch->init_status & HW_OPEN) ? "HW_OPEN" : "",
+ (ch->init_status & LINE_OPEN) ? "LINE_OPEN" : "",
+ (ch->init_status & FW_LOADED) ? "FW_LOADED" : "",
+ (ch->init_status & IRQ_ALLOCATED) ? "IRQ_ALLOCATED" : "",
+ COMX_readw(dev, OFF_A_L1_PBUFOVR) & 0xff,
+ (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) & 0xff,
+ COMX_readw(dev, OFF_A_L2_LINKUP) & 0xff,
+ COMX_readw(dev, OFF_A_L2_DAV) & 0xff,
+ COMX_readw(dev, OFF_A_L2_RxBUFP) & 0xff,
+ COMX_readw(dev, OFF_A_L2_TxEMPTY) & 0xff,
+ COMX_readw(dev, OFF_A_L2_TxBUFP) & 0xff);
+
+ len += sprintf(page + len, "hist[0]: %8lu hist[1]: %8lu hist[2]: %8lu\n"
+ "hist[3]: %8lu hist[4]: %8lu\n",hw->histogram[0],hw->histogram[1],
+ hw->histogram[2],hw->histogram[3],hw->histogram[4]);
+
+ ch->HW_release_board(dev, savep);
+
+ return len;
+}
+
+static int COMX_load_board(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct comx_firmware *fw = hw->firmware;
+ word board_segment = dev->mem_start >> 16;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ unsigned long flags;
+ unsigned char id1, id2;
+ struct net_device *saved;
+ int retval;
+ int loopcount;
+ int len;
+ byte *COMX_address;
+
+ if (!fw || !fw->len) {
+ struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+ struct comx_privdata *twin_hw;
+
+ if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+ return -EAGAIN;
+ }
+
+ if (!(fw = twin_hw->firmware) || !fw->len) {
+ return -EAGAIN;
+ }
+ }
+
+ id1 = fw->data[OFF_FW_L1_ID];
+ id2 = fw->data[OFF_FW_L1_ID + 1];
+
+ if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_COMX) {
+ printk(KERN_ERR "%s: incorrect firmware, load aborted\n",
+ dev->name);
+ return -EAGAIN;
+ }
+
+ printk(KERN_INFO "%s: Loading COMX Layer 1 firmware %s\n", dev->name,
+ (char *)(fw->data + OFF_FW_L1_ID + 2));
+
+ id1 = fw->data[OFF_FW_L2_ID];
+ id2 = fw->data[OFF_FW_L2_ID + 1];
+ if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) {
+ printk(KERN_INFO "with Layer 2 code %s\n",
+ (char *)(fw->data + OFF_FW_L2_ID + 2));
+ }
+
+ outb_p(board_segment | COMX_BOARD_RESET, dev->base_addr);
+ /* 10 usec should be enough here */
+ udelay(100);
+
+ save_flags(flags); cli();
+ saved=memory_used[mempos];
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_off(saved);
+ }
+ memory_used[mempos]=dev;
+
+ outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+ writeb(0, dev->mem_start + COMX_JAIL_OFFSET);
+
+ loopcount=0;
+ while(loopcount++ < 10000 &&
+ readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) {
+ udelay(100);
+ }
+
+ if (readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) {
+ printk(KERN_ERR "%s: Can't reset board, JAIL value is %02x\n",
+ dev->name, readb(dev->mem_start + COMX_JAIL_OFFSET));
+ retval=-ENODEV;
+ goto out;
+ }
+
+ writeb(0x55, dev->mem_start + 0x18ff);
+
+ loopcount=0;
+ while(loopcount++ < 10000 && readb(dev->mem_start + 0x18ff) != 0) {
+ udelay(100);
+ }
+
+ if(readb(dev->mem_start + 0x18ff) != 0) {
+ printk(KERN_ERR "%s: Can't reset board, reset timeout\n",
+ dev->name);
+ retval=-ENODEV;
+ goto out;
+ }
+
+ len = 0;
+ COMX_address = (byte *)dev->mem_start;
+ while (fw->len > len) {
+ writeb(fw->data[len++], COMX_address++);
+ }
+
+ len = 0;
+ COMX_address = (byte *)dev->mem_start;
+ while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
+ len++;
+ }
+
+ if (len != fw->len) {
+ printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+ "instead of 0x%02x\n", dev->name, len,
+ readb(COMX_address - 1), fw->data[len]);
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ writeb(0, dev->mem_start + COMX_JAIL_OFFSET);
+
+ loopcount = 0;
+ while ( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+ udelay(100);
+ }
+
+ if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
+ printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+ dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+ retval=-EAGAIN;
+ goto out;
+ }
+
+
+ ch->init_status |= FW_LOADED;
+ retval=0;
+
+out:
+ outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_on(saved);
+ }
+ memory_used[mempos]=saved;
+ restore_flags(flags);
+ return retval;
+}
+
+static int CMX_load_board(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct comx_firmware *fw = hw->firmware;
+ word board_segment = dev->mem_start >> 16;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ #if 0
+ unsigned char id1, id2;
+ #endif
+ struct net_device *saved;
+ unsigned long flags;
+ int retval;
+ int loopcount;
+ int len;
+ byte *COMX_address;
+
+ if (!fw || !fw->len) {
+ struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+ struct comx_privdata *twin_hw;
+
+ if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+ return -EAGAIN;
+ }
+
+ if (!(fw = twin_hw->firmware) || !fw->len) {
+ return -EAGAIN;
+ }
+ }
+
+ /* Ide kell olyat tenni, hogy ellenorizze az ID-t */
+
+ if (inb_p(dev->base_addr) != CMX_ID_BYTE) {
+ printk(KERN_ERR "%s: CMX id byte is invalid(%02x)\n", dev->name,
+ inb_p(dev->base_addr));
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "%s: Loading CMX Layer 1 firmware %s\n", dev->name,
+ (char *)(fw->data + OFF_FW_L1_ID + 2));
+
+ save_flags(flags); cli();
+ saved=memory_used[mempos];
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_off(saved);
+ }
+ memory_used[mempos]=dev;
+
+ outb_p(board_segment | COMX_ENABLE_BOARD_MEM | COMX_BOARD_RESET,
+ dev->base_addr);
+
+ len = 0;
+ COMX_address = (byte *)dev->mem_start;
+ while (fw->len > len) {
+ writeb(fw->data[len++], COMX_address++);
+ }
+
+ len = 0;
+ COMX_address = (byte *)dev->mem_start;
+ while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
+ len++;
+ }
+
+ outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+ if (len != fw->len) {
+ printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+ "instead of 0x%02x\n", dev->name, len,
+ readb(COMX_address - 1), fw->data[len]);
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ loopcount=0;
+ while( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+ udelay(100);
+ }
+
+ if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
+ printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+ dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ ch->init_status |= FW_LOADED;
+ retval=0;
+
+out:
+ outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_on(saved);
+ }
+ memory_used[mempos]=saved;
+ restore_flags(flags);
+ return retval;
+}
+
+static int HICOMX_load_board(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct comx_firmware *fw = hw->firmware;
+ word board_segment = dev->mem_start >> 12;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ struct net_device *saved;
+ unsigned char id1, id2;
+ unsigned long flags;
+ int retval;
+ int loopcount;
+ int len;
+ word *HICOMX_address;
+ char id = 1;
+
+ if (!fw || !fw->len) {
+ struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+ struct comx_privdata *twin_hw;
+
+ if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+ return -EAGAIN;
+ }
+
+ if (!(fw = twin_hw->firmware) || !fw->len) {
+ return -EAGAIN;
+ }
+ }
+
+ while (id != 4) {
+ if (inb_p(dev->base_addr + id++) != HICOMX_ID_BYTE) {
+ break;
+ }
+ }
+
+ if (id != 4) {
+ printk(KERN_ERR "%s: can't find HICOMX at 0x%04x, id[%d] = %02x\n",
+ dev->name, (unsigned int)dev->base_addr, id - 1,
+ inb_p(dev->base_addr + id - 1));
+ return -1;
+ }
+
+ id1 = fw->data[OFF_FW_L1_ID];
+ id2 = fw->data[OFF_FW_L1_ID + 1];
+ if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_HICOMX) {
+ printk(KERN_ERR "%s: incorrect firmware, load aborted\n", dev->name);
+ return -EAGAIN;
+ }
+
+ printk(KERN_INFO "%s: Loading HICOMX Layer 1 firmware %s\n", dev->name,
+ (char *)(fw->data + OFF_FW_L1_ID + 2));
+
+ id1 = fw->data[OFF_FW_L2_ID];
+ id2 = fw->data[OFF_FW_L2_ID + 1];
+ if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) {
+ printk(KERN_INFO "with Layer 2 code %s\n",
+ (char *)(fw->data + OFF_FW_L2_ID + 2));
+ }
+
+ outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
+ udelay(10);
+
+ save_flags(flags); cli();
+ saved=memory_used[mempos];
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_off(saved);
+ }
+ memory_used[mempos]=dev;
+
+ outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
+ outb_p(HICOMX_PRG_MEM, dev->base_addr + 1);
+
+ len = 0;
+ HICOMX_address = (word *)dev->mem_start;
+ while (fw->len > len) {
+ writeb(fw->data[len++], HICOMX_address++);
+ }
+
+ len = 0;
+ HICOMX_address = (word *)dev->mem_start;
+ while (len != fw->len && (readw(HICOMX_address++) & 0xff) == fw->data[len]) {
+ len++;
+ }
+
+ if (len != fw->len) {
+ printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+ "instead of 0x%02x\n", dev->name, len,
+ readw(HICOMX_address - 1) & 0xff, fw->data[len]);
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
+ outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
+
+ outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+ loopcount=0;
+ while(loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
+ udelay(100);
+ }
+
+ if ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+ printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+ dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ ch->init_status |= FW_LOADED;
+ retval=0;
+
+out:
+ outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr);
+ outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
+
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_on(saved);
+ }
+ memory_used[mempos]=saved;
+ restore_flags(flags);
+ return retval;
+}
+
+static struct net_device *comx_twin_check(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
+ struct comx_privdata *hw = ch->HW_privdata;
+
+ struct net_device *twin;
+ struct comx_channel *ch_twin;
+ struct comx_privdata *hw_twin;
+
+
+ for ( ; procfile ; procfile = procfile->next) {
+
+ if(!S_ISDIR(procfile->mode)) {
+ continue;
+ }
+
+ twin=procfile->data;
+ ch_twin=twin->priv;
+ hw_twin=ch_twin->HW_privdata;
+
+
+ if (twin != dev && dev->irq && dev->base_addr && dev->mem_start &&
+ dev->irq == twin->irq && dev->base_addr == twin->base_addr &&
+ dev->mem_start == twin->mem_start &&
+ hw->channel == (1 - hw_twin->channel) &&
+ ch->hardware == ch_twin->hardware) {
+ return twin;
+ }
+ }
+ return NULL;
+}
+
+static int comxhw_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = entry->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ char *page;
+
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comx_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if(ch->init_status & HW_OPEN) {
+ return -EAGAIN;
+ }
+
+ if (strcmp(FILENAME_FIRMWARE, entry->name) != 0) {
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+ copy_from_user(page, buffer, count = (min(count, PAGE_SIZE)));
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+ } else {
+ byte *tmp;
+
+ if (!hw->firmware) {
+ if ((hw->firmware = kmalloc(sizeof(struct comx_firmware),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ hw->firmware->len = 0;
+ hw->firmware->data = NULL;
+ }
+
+ if ((tmp = kmalloc(count + file->f_pos, GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Ha nem 0 a fpos, akkor meglevo file-t irunk. Gyenge trukk. */
+ if (hw->firmware && hw->firmware->len && file->f_pos
+ && hw->firmware->len < count + file->f_pos) {
+ memcpy(tmp, hw->firmware->data, hw->firmware->len);
+ }
+ if (hw->firmware->data) {
+ kfree(hw->firmware->data);
+ }
+ copy_from_user(tmp + file->f_pos, buffer, count);
+ hw->firmware->len = entry->size = file->f_pos + count;
+ hw->firmware->data = tmp;
+ file->f_pos += count;
+ return count;
+ }
+
+ if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
+ hw->channel = simple_strtoul(page, NULL, 0);
+ if (hw->channel >= MAX_CHANNELNO) {
+ printk(KERN_ERR "Invalid channel number\n");
+ hw->channel = 0;
+ }
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+ twin_ch->twin = dev;
+ }
+ } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+ dev->irq = simple_strtoul(page, NULL, 0);
+ if (dev->irq == 2) {
+ dev->irq = 9;
+ }
+ if (dev->irq < 3 || dev->irq > 15) {
+ printk(KERN_ERR "comxhw: Invalid irq number\n");
+ dev->irq = 0;
+ }
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+ twin_ch->twin = dev;
+ }
+ } else if (strcmp(entry->name, FILENAME_IO) == 0) {
+ dev->base_addr = simple_strtoul(page, NULL, 0);
+ if ((dev->base_addr & 3) != 0 || dev->base_addr < 0x300
+ || dev->base_addr > 0x3fc) {
+ printk(KERN_ERR "Invalid io value\n");
+ dev->base_addr = 0;
+ }
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+
+ twin_ch->twin = dev;
+ }
+ } else if (strcmp(entry->name, FILENAME_MEMADDR) == 0) {
+ dev->mem_start = simple_strtoul(page, NULL, 0);
+ if (dev->mem_start <= 0xf000 && dev->mem_start >= 0xa000) {
+ dev->mem_start *= 16;
+ }
+ if ((dev->mem_start & 0xfff) != 0 || dev->mem_start < COMX_MEM_MIN
+ || dev->mem_start + hw->memory_size > COMX_MEM_MAX) {
+ printk(KERN_ERR "Invalid memory page\n");
+ dev->mem_start = 0;
+ }
+ dev->mem_end = dev->mem_start + hw->memory_size;
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+
+ twin_ch->twin = dev;
+ }
+ } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
+ if (strncmp("ext", page, 3) == 0) {
+ hw->clock = 0;
+ } else {
+ int kbps;
+
+ kbps = simple_strtoul(page, NULL, 0);
+ hw->clock = kbps ? COMX_CLOCK_CONST/kbps : 0;
+ }
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static int comxhw_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ int len = 0;
+
+
+ if (strcmp(file->name, FILENAME_IO) == 0) {
+ len = sprintf(page, "0x%03x\n", (unsigned int)dev->base_addr);
+ } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+ len = sprintf(page, "0x%02x\n", dev->irq == 9 ? 2 : dev->irq);
+ } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
+ len = sprintf(page, "%01d\n", hw->channel);
+ } else if (strcmp(file->name, FILENAME_MEMADDR) == 0) {
+ len = sprintf(page, "0x%05x\n", (unsigned int)dev->mem_start);
+ } else if (strcmp(file->name, FILENAME_TWIN) == 0) {
+ len = sprintf(page, "%s\n", ch->twin ? ch->twin->name : "none");
+ } else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
+ if (hw->clock) {
+ len = sprintf(page, "%-8d\n", COMX_CLOCK_CONST/hw->clock);
+ } else {
+ len = sprintf(page, "external\n");
+ }
+ } else if (strcmp(file->name, FILENAME_FIRMWARE) == 0) {
+ len = min(FILE_PAGESIZE, min(count,
+ hw->firmware ? (hw->firmware->len - off) : 0));
+ if (len < 0) {
+ len = 0;
+ }
+ *start = hw->firmware ? (hw->firmware->data + off) : NULL;
+ if (off + len >= (hw->firmware ? hw->firmware->len : 0) || len == 0) {
+ *eof = 1;
+ }
+ return len;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return(min(count, len - off));
+}
+
+/* Called on echo comx >boardtype */
+static int COMX_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw;
+ struct proc_dir_entry *new_file;
+
+ if ((ch->HW_privdata = kmalloc(sizeof(struct comx_privdata),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(hw = ch->HW_privdata, 0, sizeof(struct comx_privdata));
+
+ if (ch->hardware == &comx_hw || ch->hardware == &cmx_hw) {
+ hw->memory_size = COMX_MEMORY_SIZE;
+ hw->io_extent = COMX_IO_EXTENT;
+ dev->base_addr = COMX_DEFAULT_IO;
+ dev->irq = COMX_DEFAULT_IRQ;
+ dev->mem_start = COMX_DEFAULT_MEMADDR;
+ dev->mem_end = COMX_DEFAULT_MEMADDR + COMX_MEMORY_SIZE;
+ } else if (ch->hardware == &hicomx_hw) {
+ hw->memory_size = HICOMX_MEMORY_SIZE;
+ hw->io_extent = HICOMX_IO_EXTENT;
+ dev->base_addr = HICOMX_DEFAULT_IO;
+ dev->irq = HICOMX_DEFAULT_IRQ;
+ dev->mem_start = HICOMX_DEFAULT_MEMADDR;
+ dev->mem_end = HICOMX_DEFAULT_MEMADDR + HICOMX_MEMORY_SIZE;
+ } else {
+ printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
+ }
+
+ if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, ch->procdir))
+ == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 6;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, ch->procdir))
+ == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 5;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 2; // Ezt tudjuk
+ new_file->nlink = 1;
+
+ if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) {
+ if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 9;
+ new_file->nlink = 1;
+ }
+
+ if ((new_file = create_proc_entry(FILENAME_MEMADDR, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 8;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = NULL;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_FIRMWARE, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if (ch->hardware == &comx_hw) {
+ ch->HW_board_on = COMX_board_on;
+ ch->HW_board_off = COMX_board_off;
+ ch->HW_load_board = COMX_load_board;
+ } else if (ch->hardware == &cmx_hw) {
+ ch->HW_board_on = COMX_board_on;
+ ch->HW_board_off = COMX_board_off;
+ ch->HW_load_board = CMX_load_board;
+ ch->HW_set_clock = COMX_set_clock;
+ } else if (ch->hardware == &hicomx_hw) {
+ ch->HW_board_on = HICOMX_board_on;
+ ch->HW_board_off = HICOMX_board_off;
+ ch->HW_load_board = HICOMX_load_board;
+ ch->HW_set_clock = COMX_set_clock;
+ } else {
+ printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
+ }
+
+ ch->HW_access_board = COMX_access_board;
+ ch->HW_release_board = COMX_release_board;
+ ch->HW_txe = COMX_txe;
+ ch->HW_open = COMX_open;
+ ch->HW_close = COMX_close;
+ ch->HW_send_packet = COMX_send_packet;
+ ch->HW_statistics = COMX_statistics;
+
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+
+ twin_ch->twin = dev;
+ }
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* Called on echo valami >boardtype */
+static int COMX_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+
+ if (hw->firmware) {
+ if (hw->firmware->data) kfree(hw->firmware->data);
+ kfree(hw->firmware);
+ } if (ch->twin) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+
+ twin_ch->twin = NULL;
+ }
+
+ kfree(ch->HW_privdata);
+ remove_proc_entry(FILENAME_IO, ch->procdir);
+ remove_proc_entry(FILENAME_IRQ, ch->procdir);
+ remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
+ remove_proc_entry(FILENAME_MEMADDR, ch->procdir);
+ remove_proc_entry(FILENAME_FIRMWARE, ch->procdir);
+ remove_proc_entry(FILENAME_TWIN, ch->procdir);
+ if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) {
+ remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+ }
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int COMX_dump(struct net_device *dev)
+{
+ printk(KERN_INFO "%s: COMX_dump called, why ?\n", dev->name);
+ return 0;
+}
+
+static struct comx_hardware comx_hw = {
+ "comx",
+ VERSION,
+ COMX_init,
+ COMX_exit,
+ COMX_dump,
+ NULL
+};
+
+static struct comx_hardware cmx_hw = {
+ "cmx",
+ VERSION,
+ COMX_init,
+ COMX_exit,
+ COMX_dump,
+ NULL
+};
+
+static struct comx_hardware hicomx_hw = {
+ "hicomx",
+ VERSION,
+ COMX_init,
+ COMX_exit,
+ COMX_dump,
+ NULL
+};
+
+#ifdef MODULE
+#define comx_hw_comx_init init_module
+#endif
+
+int __init comx_hw_comx_init(void)
+{
+ comx_register_hardware(&comx_hw);
+ comx_register_hardware(&cmx_hw);
+ comx_register_hardware(&hicomx_hw);
+ memset(memory_used, 0, sizeof(memory_used));
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_hardware("comx");
+ comx_unregister_hardware("cmx");
+ comx_unregister_hardware("hicomx");
+}
+#endif
diff --git a/drivers/net/wan/comx-hw-locomx.c b/drivers/net/wan/comx-hw-locomx.c
new file mode 100644
index 000000000..010f02373
--- /dev/null
+++ b/drivers/net/wan/comx-hw-locomx.c
@@ -0,0 +1,496 @@
+/*
+ * Hardware driver for the LoCOMX card, using the generic z85230
+ * functions
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Based on skeleton code and old LoCOMX driver by Tivadar Szemethy <tiv@itc.hu>
+ * and the hostess_sv11 driver
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.10 (99/06/17):
+ * - rewritten for the z85230 layer
+ *
+ * Version 0.11 (99/06/21):
+ * - some printk's fixed
+ * - get rid of a memory leak (it was impossible though :))
+ *
+ * Version 0.12 (99/07/07):
+ * - check CTS for modem lines, not DCD (which is always high
+ * in case of this board)
+ * Version 0.13 (99/07/08):
+ * - Fix the transmitter status check
+ * - Handle the net device statistics better
+ */
+
+#define VERSION "0.13"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "z85230.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Hardware driver for the LoCOMX board");
+
+#define RX_DMA 3
+#define TX_DMA 1
+#define LOCOMX_ID 0x33
+#define LOCOMX_IO_EXTENT 8
+#define LOCOMX_DEFAULT_IO 0x368
+#define LOCOMX_DEFAULT_IRQ 7
+
+u8 z8530_locomx[] = {
+ 11, TCRTxCP,
+ 14, DTRREQ,
+ 255
+};
+
+struct locomx_data {
+ int io_extent;
+ struct z8530_dev board;
+ struct timer_list status_timer;
+};
+
+static int LOCOMX_txe(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct locomx_data *hw = ch->HW_privdata;
+
+ return (!hw->board.chanA.tx_next_skb);
+}
+
+
+static void locomx_rx(struct z8530_channel *c, struct sk_buff *skb)
+{
+ struct net_device *dev=c->netdevice;
+ struct comx_channel *ch=dev->priv;
+
+ if (ch->debug_flags & DEBUG_HW_RX) {
+ comx_debug_skb(dev, skb, "locomx_rx receiving");
+ }
+ ch->LINE_rx(dev,skb);
+}
+
+static int LOCOMX_send_packet(struct net_device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+ struct locomx_data *hw = ch->HW_privdata;
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug_bytes(dev, skb->data, skb->len, "LOCOMX_send_packet");
+ }
+
+ if (!(ch->line_status & LINE_UP)) {
+ return FRAME_DROPPED;
+ }
+
+ if(z8530_queue_xmit(&hw->board.chanA,skb)) {
+ printk(KERN_WARNING "%s: FRAME_DROPPED\n",dev->name);
+ return FRAME_DROPPED;
+ }
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug(dev, "%s: LOCOMX_send_packet was successful\n\n", dev->name);
+ }
+
+ if(!hw->board.chanA.tx_next_skb) {
+ return FRAME_QUEUED;
+ } else {
+ return FRAME_ACCEPTED;
+ }
+}
+
+static void locomx_status_timerfun(unsigned long d)
+{
+ struct net_device *dev=(struct net_device *)d;
+ struct comx_channel *ch=dev->priv;
+ struct locomx_data *hw=ch->HW_privdata;
+
+ if(!(ch->line_status & LINE_UP) &&
+ (hw->board.chanA.status & CTS)) {
+ ch->LINE_status(dev, ch->line_status | LINE_UP);
+ }
+ if((ch->line_status & LINE_UP) &&
+ !(hw->board.chanA.status & CTS)) {
+ ch->LINE_status(dev, ch->line_status & ~LINE_UP);
+ }
+ mod_timer(&hw->status_timer,jiffies + ch->lineup_delay * HZ);
+}
+
+
+static int LOCOMX_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct locomx_data *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ unsigned long flags;
+ int ret;
+
+ if (!dev->base_addr || !dev->irq) {
+ return -ENODEV;
+ }
+
+ if (check_region(dev->base_addr, hw->io_extent)) {
+ return -EAGAIN;
+ }
+
+ request_region(dev->base_addr, hw->io_extent, dev->name);
+
+ hw->board.chanA.ctrlio=dev->base_addr + 5;
+ hw->board.chanA.dataio=dev->base_addr + 7;
+
+ hw->board.irq=dev->irq;
+ hw->board.chanA.netdevice=dev;
+ hw->board.chanA.dev=&hw->board;
+ hw->board.name=dev->name;
+ hw->board.chanA.txdma=TX_DMA;
+ hw->board.chanA.rxdma=RX_DMA;
+ hw->board.chanA.irqs=&z8530_nop;
+ hw->board.chanB.irqs=&z8530_nop;
+
+ if(request_irq(dev->irq, z8530_interrupt, SA_INTERRUPT,
+ dev->name, &hw->board)) {
+ printk(KERN_ERR "%s: unable to obtain irq %d\n", dev->name,
+ dev->irq);
+ ret=-EAGAIN;
+ goto irq_fail;
+ }
+ if(request_dma(TX_DMA,"LoCOMX (TX)")) {
+ printk(KERN_ERR "%s: unable to obtain TX DMA (DMA channel %d)\n",
+ dev->name, TX_DMA);
+ ret=-EAGAIN;
+ goto dma1_fail;
+ }
+
+ if(request_dma(RX_DMA,"LoCOMX (RX)")) {
+ printk(KERN_ERR "%s: unable to obtain RX DMA (DMA channel %d)\n",
+ dev->name, RX_DMA);
+ ret=-EAGAIN;
+ goto dma2_fail;
+ }
+
+ save_flags(flags);
+ cli();
+
+ if(z8530_init(&hw->board)!=0)
+ {
+ printk(KERN_ERR "%s: Z8530 device not found.\n",dev->name);
+ ret=-ENODEV;
+ goto z8530_fail;
+ }
+
+ hw->board.chanA.dcdcheck=CTS;
+
+ z8530_channel_load(&hw->board.chanA, z8530_hdlc_kilostream_85230);
+ z8530_channel_load(&hw->board.chanA, z8530_locomx);
+ z8530_channel_load(&hw->board.chanB, z8530_dead_port);
+
+ z8530_describe(&hw->board, "I/O", dev->base_addr);
+
+ if((ret=z8530_sync_dma_open(dev, &hw->board.chanA))!=0) {
+ goto z8530_fail;
+ }
+
+ restore_flags(flags);
+
+
+ hw->board.active=1;
+ hw->board.chanA.rx_function=locomx_rx;
+
+ ch->init_status |= HW_OPEN;
+ if (hw->board.chanA.status & DCD) {
+ ch->line_status |= LINE_UP;
+ } else {
+ ch->line_status &= ~LINE_UP;
+ }
+
+ comx_status(dev, ch->line_status);
+
+ init_timer(&hw->status_timer);
+ hw->status_timer.function=locomx_status_timerfun;
+ hw->status_timer.data=(unsigned long)dev;
+ hw->status_timer.expires=jiffies + ch->lineup_delay * HZ;
+ add_timer(&hw->status_timer);
+
+ for (; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0444;
+ }
+ }
+ return 0;
+
+z8530_fail:
+ restore_flags(flags);
+ free_dma(RX_DMA);
+dma2_fail:
+ free_dma(TX_DMA);
+dma1_fail:
+ free_irq(dev->irq, &hw->board);
+irq_fail:
+ release_region(dev->base_addr, hw->io_extent);
+ return ret;
+}
+
+static int LOCOMX_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct locomx_data *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+
+ hw->board.chanA.rx_function=z8530_null_rx;
+ netif_stop_queue(dev);
+ z8530_sync_dma_close(dev, &hw->board.chanA);
+
+ z8530_shutdown(&hw->board);
+
+ del_timer(&hw->status_timer);
+ free_dma(RX_DMA);
+ free_dma(TX_DMA);
+ free_irq(dev->irq,&hw->board);
+ release_region(dev->base_addr,8);
+
+ for (; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0644;
+ }
+ }
+
+ ch->init_status &= ~HW_OPEN;
+ return 0;
+}
+
+static int LOCOMX_statistics(struct net_device *dev,char *page)
+{
+ int len = 0;
+
+ len += sprintf(page + len, "Hello\n");
+
+ return len;
+}
+
+static int LOCOMX_dump(struct net_device *dev) {
+ printk(KERN_INFO "LOCOMX_dump called\n");
+ return(-1);
+}
+
+static int locomx_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_IO) == 0) {
+ len = sprintf(page, "0x%x\n", (unsigned int)dev->base_addr);
+ } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+ len = sprintf(page, "%d\n", (unsigned int)dev->irq);
+ } else {
+ printk(KERN_ERR "hw_read_proc: internal error, filename %s\n",
+ file->name);
+ return -EBADF;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return ( min(count, len - off) );
+}
+
+static int locomx_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = (struct net_device *)entry->parent->data;
+ int val;
+ char *page;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "hw_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ copy_from_user(page, buffer, count = min(count, PAGE_SIZE));
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+
+ if (strcmp(entry->name, FILENAME_IO) == 0) {
+ val = simple_strtoul(page, NULL, 0);
+ if (val != 0x360 && val != 0x368 && val != 0x370 &&
+ val != 0x378) {
+ printk(KERN_ERR "LoCOMX: incorrect io address!\n");
+ } else {
+ dev->base_addr = val;
+ }
+ } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+ val = simple_strtoul(page, NULL, 0);
+ if (val != 3 && val != 4 && val != 5 && val != 6 && val != 7) {
+ printk(KERN_ERR "LoCOMX: incorrect irq value!\n");
+ } else {
+ dev->irq = val;
+ }
+ } else {
+ printk(KERN_ERR "locomx_write_proc: internal error, filename %s\n",
+ entry->name);
+ free_page((unsigned long)page);
+ return -EBADF;
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+
+
+static int LOCOMX_init(struct net_device *dev)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+ struct locomx_data *hw;
+ struct proc_dir_entry *new_file;
+
+ /* Alloc data for private structure */
+ if ((ch->HW_privdata = kmalloc(sizeof(struct locomx_data),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ memset(hw = ch->HW_privdata, 0, sizeof(struct locomx_data));
+ hw->io_extent = LOCOMX_IO_EXTENT;
+
+ /* Register /proc files */
+ if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &locomx_read_proc;
+ new_file->write_proc = &locomx_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &locomx_read_proc;
+ new_file->write_proc = &locomx_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+/* No clock yet */
+/*
+ if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &locomx_read_proc;
+ new_file->write_proc = &locomx_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+*/
+
+ ch->HW_access_board = NULL;
+ ch->HW_release_board = NULL;
+ ch->HW_txe = LOCOMX_txe;
+ ch->HW_open = LOCOMX_open;
+ ch->HW_close = LOCOMX_close;
+ ch->HW_send_packet = LOCOMX_send_packet;
+ ch->HW_statistics = LOCOMX_statistics;
+ ch->HW_set_clock = NULL;
+
+ ch->current_stats = &hw->board.chanA.stats;
+ memcpy(ch->current_stats, &ch->stats, sizeof(struct net_device_stats));
+
+ dev->base_addr = LOCOMX_DEFAULT_IO;
+ dev->irq = LOCOMX_DEFAULT_IRQ;
+
+
+ /* O.K. Count one more user on this module */
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+
+static int LOCOMX_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+
+ ch->HW_access_board = NULL;
+ ch->HW_release_board = NULL;
+ ch->HW_txe = NULL;
+ ch->HW_open = NULL;
+ ch->HW_close = NULL;
+ ch->HW_send_packet = NULL;
+ ch->HW_statistics = NULL;
+ ch->HW_set_clock = NULL;
+ memcpy(&ch->stats, ch->current_stats, sizeof(struct net_device_stats));
+ ch->current_stats = &ch->stats;
+
+ kfree(ch->HW_privdata);
+
+ remove_proc_entry(FILENAME_IO, ch->procdir);
+ remove_proc_entry(FILENAME_IRQ, ch->procdir);
+// remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct comx_hardware locomx_hw = {
+ "locomx",
+ VERSION,
+ LOCOMX_init,
+ LOCOMX_exit,
+ LOCOMX_dump,
+ NULL
+};
+
+#ifdef MODULE
+#define comx_hw_locomx_init init_module
+#endif
+
+int __init comx_hw_locomx_init(void)
+{
+ comx_register_hardware(&locomx_hw);
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_hardware("locomx");
+ return;
+}
+#endif
diff --git a/drivers/net/wan/comx-hw-mixcom.c b/drivers/net/wan/comx-hw-mixcom.c
new file mode 100644
index 000000000..552443f88
--- /dev/null
+++ b/drivers/net/wan/comx-hw-mixcom.c
@@ -0,0 +1,948 @@
+/*
+ * Hardware driver for the MixCom synchronous serial board
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * based on skeleton driver code and a preliminary hscx driver by
+ * Tivadar Szemethy <tiv@itc.hu>
+ *
+ * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.60 (99/06/11):
+ * - ported to the kernel, now works as builtin code
+ *
+ * Version 0.61 (99/06/11):
+ * - recognize the one-channel MixCOM card (id byte = 0x13)
+ * - printk fixes
+ *
+ * Version 0.62 (99/07/15):
+ * - fixes according to the new hw docs
+ * - report line status when open
+ *
+ * Version 0.63 (99/09/21):
+ * - line status report fixes
+ *
+ * Version 0.64 (99/12/01):
+ * - some more cosmetical fixes
+ */
+
+#define VERSION "0.64"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "mixcom.h"
+#include "hscx.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Hardware-level driver for the serial port of the MixCom board");
+
+#define MIXCOM_DATA(d) ((struct mixcom_privdata *)(COMX_CHANNEL(d)-> \
+ HW_privdata))
+
+#define MIXCOM_BOARD_BASE(d) (d->base_addr - MIXCOM_SERIAL_OFFSET - \
+ (1 - MIXCOM_DATA(d)->channel) * MIXCOM_CHANNEL_OFFSET)
+
+#define MIXCOM_DEV_BASE(port,channel) (port + MIXCOM_SERIAL_OFFSET + \
+ (1 - channel) * MIXCOM_CHANNEL_OFFSET)
+
+/* Values used to set the IRQ line */
+static unsigned char mixcom_set_irq[]={0xFF, 0xFF, 0xFF, 0x0, 0xFF, 0x2, 0x4, 0x6, 0xFF, 0xFF, 0x8, 0xA, 0xC, 0xFF, 0xE, 0xFF};
+
+static unsigned char* hscx_versions[]={"A1", NULL, "A2", NULL, "A3", "2.1"};
+
+struct mixcom_privdata {
+ u16 clock;
+ char channel;
+ char txbusy;
+ struct sk_buff *sending;
+ unsigned tx_ptr;
+ struct sk_buff *recving;
+ unsigned rx_ptr;
+ unsigned char status;
+ char card_has_status;
+};
+
+static inline void wr_hscx(struct net_device *dev, int reg, unsigned char val)
+{
+ outb(val, dev->base_addr + reg);
+}
+
+static inline unsigned char rd_hscx(struct net_device *dev, int reg)
+{
+ return inb(dev->base_addr + reg);
+}
+
+static inline void hscx_cmd(struct net_device *dev, int cmd)
+{
+ unsigned long jiffs = jiffies;
+ unsigned char cec;
+ unsigned delay = 0;
+
+ while ((cec = (rd_hscx(dev, HSCX_STAR) & HSCX_CEC) != 0) &&
+ (jiffs + HZ > jiffies)) {
+ udelay(1);
+ if (++delay > (100000 / HZ)) break;
+ }
+ if (cec) {
+ printk(KERN_WARNING "%s: CEC stuck, probably no clock!\n",dev->name);
+ } else {
+ wr_hscx(dev, HSCX_CMDR, cmd);
+ }
+}
+
+static inline void hscx_fill_fifo(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ register word to_send = hw->sending->len - hw->tx_ptr;
+
+
+ outsb(dev->base_addr + HSCX_FIFO,
+ &(hw->sending->data[hw->tx_ptr]), min(to_send, 32));
+ if (to_send <= 32) {
+ hscx_cmd(dev, HSCX_XTF | HSCX_XME);
+ kfree_skb(hw->sending);
+ hw->sending = NULL;
+ hw->tx_ptr = 0;
+ } else {
+ hscx_cmd(dev, HSCX_XTF);
+ hw->tx_ptr += 32;
+ }
+}
+
+static inline void hscx_empty_fifo(struct net_device *dev, int cnt)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ if (hw->recving == NULL) {
+ if (!(hw->recving = dev_alloc_skb(HSCX_MTU + 16))) {
+ ch->stats.rx_dropped++;
+ hscx_cmd(dev, HSCX_RHR);
+ } else {
+ skb_reserve(hw->recving, 16);
+ skb_put(hw->recving, HSCX_MTU);
+ }
+ hw->rx_ptr = 0;
+ }
+ if (cnt > 32 || !cnt || hw->recving == NULL) {
+ printk(KERN_ERR "hscx_empty_fifo: cnt is %d, hw->recving %p\n",
+ cnt, (void *)hw->recving);
+ return;
+ }
+
+ insb(dev->base_addr + HSCX_FIFO, &(hw->recving->data[hw->rx_ptr]),cnt);
+ hw->rx_ptr += cnt;
+ hscx_cmd(dev, HSCX_RMC);
+}
+
+
+static int MIXCOM_txe(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ return !test_bit(0, &hw->txbusy);
+}
+
+static int mixcom_probe(struct net_device *dev)
+{
+ unsigned long flags;
+ int id, vstr, ret=0;
+
+ save_flags(flags); cli();
+
+ id=inb_p(MIXCOM_BOARD_BASE(dev) + MIXCOM_ID_OFFSET) & 0x7f;
+
+ if (id != MIXCOM_ID ) {
+ ret=-ENODEV;
+ printk(KERN_WARNING "%s: no MixCOM board found at 0x%04lx\n",dev->name, dev->base_addr);
+ goto out;
+ }
+
+ vstr=inb_p(dev->base_addr + HSCX_VSTR) & 0x0f;
+ if(vstr>=sizeof(hscx_versions)/sizeof(char*) ||
+ hscx_versions[vstr]==NULL) {
+ printk(KERN_WARNING "%s: board found but no HSCX chip detected at 0x%4lx (vstr = 0x%1x)\n",dev->name,dev->base_addr,vstr);
+ ret = -ENODEV;
+ } else {
+ printk(KERN_INFO "%s: HSCX chip version %s\n",dev->name,hscx_versions[vstr]);
+ ret = 0;
+ }
+
+out:
+
+ restore_flags(flags);
+ return ret;
+}
+
+#if 0
+static void MIXCOM_set_clock(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ if (hw->clock) {
+ ;
+ } else {
+ ;
+ }
+}
+#endif
+
+static void mixcom_board_on(struct net_device *dev)
+{
+ outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+ udelay(1000);
+ outb_p(mixcom_set_irq[dev->irq] | MIXCOM_ON,
+ MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+ udelay(1000);
+}
+
+static void mixcom_board_off(struct net_device *dev)
+{
+ outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+ udelay(1000);
+}
+
+static void mixcom_off(struct net_device *dev)
+{
+ wr_hscx(dev, HSCX_CCR1, 0x0);
+}
+
+static void mixcom_on(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ wr_hscx(dev, HSCX_CCR1, HSCX_PU | HSCX_ODS | HSCX_ITF); // power up, push-pull
+ wr_hscx(dev, HSCX_CCR2, HSCX_CIE /* | HSCX_RIE */ );
+ wr_hscx(dev, HSCX_MODE, HSCX_TRANS | HSCX_ADM8 | HSCX_RAC | HSCX_RTS );
+ wr_hscx(dev, HSCX_RLCR, HSCX_RC | 47); // 1504 bytes
+ wr_hscx(dev, HSCX_MASK, HSCX_RSC | HSCX_TIN );
+ hscx_cmd(dev, HSCX_XRES | HSCX_RHR);
+
+ if (ch->HW_set_clock) ch->HW_set_clock(dev);
+
+}
+
+static int MIXCOM_send_packet(struct net_device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ unsigned long flags;
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug_bytes(dev, skb->data, skb->len, "MIXCOM_send_packet");
+ }
+
+ if (!(ch->line_status & LINE_UP)) {
+ return FRAME_DROPPED;
+ }
+
+ if (skb->len > HSCX_MTU) {
+ ch->stats.tx_errors++;
+ return FRAME_ERROR;
+ }
+
+ save_flags(flags); cli();
+
+ if (test_and_set_bit(0, &hw->txbusy)) {
+ printk(KERN_ERR "%s: transmitter called while busy... dropping frame (length %d)\n", dev->name, skb->len);
+ restore_flags(flags);
+ return FRAME_DROPPED;
+ }
+
+
+ hw->sending = skb;
+ hw->tx_ptr = 0;
+ hw->txbusy = 1;
+// atomic_inc(&skb->users); // save it
+ hscx_fill_fifo(dev);
+ restore_flags(flags);
+
+ ch->stats.tx_packets++;
+ ch->stats.tx_bytes += skb->len;
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug(dev, "MIXCOM_send_packet was successful\n\n");
+ }
+
+ return FRAME_ACCEPTED;
+}
+
+static inline void mixcom_receive_frame(struct net_device *dev)
+{
+ struct comx_channel *ch=dev->priv;
+ struct mixcom_privdata *hw=ch->HW_privdata;
+ register byte rsta;
+ register word length;
+
+ rsta = rd_hscx(dev, HSCX_RSTA) & (HSCX_VFR | HSCX_RDO |
+ HSCX_CRC | HSCX_RAB);
+ length = ((rd_hscx(dev, HSCX_RBCH) & 0x0f) << 8) |
+ rd_hscx(dev, HSCX_RBCL);
+
+ if ( length > hw->rx_ptr ) {
+ hscx_empty_fifo(dev, length - hw->rx_ptr);
+ }
+
+ if (!(rsta & HSCX_VFR)) {
+ ch->stats.rx_length_errors++;
+ }
+ if (rsta & HSCX_RDO) {
+ ch->stats.rx_over_errors++;
+ }
+ if (!(rsta & HSCX_CRC)) {
+ ch->stats.rx_crc_errors++;
+ }
+ if (rsta & HSCX_RAB) {
+ ch->stats.rx_frame_errors++;
+ }
+ ch->stats.rx_packets++;
+ ch->stats.rx_bytes += length;
+
+ if (rsta == (HSCX_VFR | HSCX_CRC) && hw->recving) {
+ skb_trim(hw->recving, hw->rx_ptr - 1);
+ if (ch->debug_flags & DEBUG_HW_RX) {
+ comx_debug_skb(dev, hw->recving,
+ "MIXCOM_interrupt receiving");
+ }
+ hw->recving->dev = dev;
+ if (ch->LINE_rx) {
+ ch->LINE_rx(dev, hw->recving);
+ }
+ }
+ else if(hw->recving) {
+ kfree_skb(hw->recving);
+ }
+ hw->recving = NULL;
+ hw->rx_ptr = 0;
+}
+
+
+static inline void mixcom_extended_interrupt(struct net_device *dev)
+{
+ struct comx_channel *ch=dev->priv;
+ struct mixcom_privdata *hw=ch->HW_privdata;
+ register byte exir;
+
+ exir = rd_hscx(dev, HSCX_EXIR) & (HSCX_XDU | HSCX_RFO | HSCX_CSC );
+
+ if (exir & HSCX_RFO) {
+ ch->stats.rx_over_errors++;
+ if (hw->rx_ptr) {
+ kfree_skb(hw->recving);
+ hw->recving = NULL; hw->rx_ptr = 0;
+ }
+ printk(KERN_ERR "MIXCOM: rx overrun\n");
+ hscx_cmd(dev, HSCX_RHR);
+ }
+
+ if (exir & HSCX_XDU) { // xmit underrun
+ ch->stats.tx_errors++;
+ ch->stats.tx_aborted_errors++;
+ if (hw->tx_ptr) {
+ kfree_skb(hw->sending);
+ hw->sending = NULL;
+ hw->tx_ptr = 0;
+ }
+ hscx_cmd(dev, HSCX_XRES);
+ clear_bit(0, &hw->txbusy);
+ if (ch->LINE_tx) {
+ ch->LINE_tx(dev);
+ }
+ printk(KERN_ERR "MIXCOM: tx underrun\n");
+ }
+
+ if (exir & HSCX_CSC) {
+ ch->stats.tx_carrier_errors++;
+ if ((rd_hscx(dev, HSCX_STAR) & HSCX_CTS) == 0) { // Vonal le
+ if (test_and_clear_bit(0, &ch->lineup_pending)) {
+ del_timer(&ch->lineup_timer);
+ } else if (ch->line_status & LINE_UP) {
+ ch->line_status &= ~LINE_UP;
+ if (ch->LINE_status) {
+ ch->LINE_status(dev,ch->line_status);
+ }
+ }
+ }
+ if (!(ch->line_status & LINE_UP) && (rd_hscx(dev, HSCX_STAR) &
+ HSCX_CTS)) { // Vonal fol
+ if (!test_and_set_bit(0,&ch->lineup_pending)) {
+ ch->lineup_timer.function = comx_lineup_func;
+ ch->lineup_timer.data = (unsigned long)dev;
+ ch->lineup_timer.expires = jiffies + HZ *
+ ch->lineup_delay;
+ add_timer(&ch->lineup_timer);
+ hscx_cmd(dev, HSCX_XRES);
+ clear_bit(0, &hw->txbusy);
+ if (hw->sending) {
+ kfree_skb(hw->sending);
+ }
+ hw->sending=NULL;
+ hw->tx_ptr = 0;
+ }
+ }
+ }
+}
+
+
+static void MIXCOM_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct comx_channel *ch, *twin_ch;
+ struct mixcom_privdata *hw, *twin_hw;
+ register unsigned char ista;
+
+ if (dev==NULL) {
+ printk(KERN_ERR "comx_interrupt: irq %d for unknown device\n",irq);
+ return;
+ }
+
+ ch = dev->priv;
+ hw = ch->HW_privdata;
+
+ save_flags(flags); cli();
+
+ while((ista = (rd_hscx(dev, HSCX_ISTA) & (HSCX_RME | HSCX_RPF |
+ HSCX_XPR | HSCX_EXB | HSCX_EXA | HSCX_ICA)))) {
+ register byte ista2 = 0;
+
+ if (ista & HSCX_RME) {
+ mixcom_receive_frame(dev);
+ }
+ if (ista & HSCX_RPF) {
+ hscx_empty_fifo(dev, 32);
+ }
+ if (ista & HSCX_XPR) {
+ if (hw->tx_ptr) {
+ hscx_fill_fifo(dev);
+ } else {
+ clear_bit(0, &hw->txbusy);
+ ch->LINE_tx(dev);
+ }
+ }
+
+ if (ista & HSCX_EXB) {
+ mixcom_extended_interrupt(dev);
+ }
+
+ if ((ista & HSCX_EXA) && ch->twin) {
+ mixcom_extended_interrupt(ch->twin);
+ }
+
+ if ((ista & HSCX_ICA) && ch->twin &&
+ (ista2 = rd_hscx(ch->twin, HSCX_ISTA) &
+ (HSCX_RME | HSCX_RPF | HSCX_XPR ))) {
+ if (ista2 & HSCX_RME) {
+ mixcom_receive_frame(ch->twin);
+ }
+ if (ista2 & HSCX_RPF) {
+ hscx_empty_fifo(ch->twin, 32);
+ }
+ if (ista2 & HSCX_XPR) {
+ twin_ch=ch->twin->priv;
+ twin_hw=twin_ch->HW_privdata;
+ if (twin_hw->tx_ptr) {
+ hscx_fill_fifo(ch->twin);
+ } else {
+ clear_bit(0, &twin_hw->txbusy);
+ ch->LINE_tx(ch->twin);
+ }
+ }
+ }
+ }
+
+ restore_flags(flags);
+ return;
+}
+
+static int MIXCOM_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ unsigned long flags;
+
+ if (!dev->base_addr || !dev->irq) return -ENODEV;
+
+
+ if(hw->channel==1) {
+ if(!TWIN(dev) || !(COMX_CHANNEL(TWIN(dev))->init_status &
+ IRQ_ALLOCATED)) {
+ printk(KERN_ERR "%s: channel 0 not yet initialized\n",dev->name);
+ return -EAGAIN;
+ }
+ }
+
+
+ /* Is our hw present at all ? Not checking for channel 0 if it is already
+ open */
+ if(hw->channel!=0 || !(ch->init_status & IRQ_ALLOCATED)) {
+ if (check_region(dev->base_addr, MIXCOM_IO_EXTENT)) {
+ return -EAGAIN;
+ }
+ if (mixcom_probe(dev)) {
+ return -ENODEV;
+ }
+ }
+
+ save_flags(flags); cli();
+
+ if(hw->channel==1) {
+ request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name);
+ }
+
+ if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) {
+ if (request_irq(dev->irq, MIXCOM_interrupt, 0,
+ dev->name, (void *)dev)) {
+ printk(KERN_ERR "MIXCOM: unable to obtain irq %d\n", dev->irq);
+ return -EAGAIN;
+ }
+ ch->init_status|=IRQ_ALLOCATED;
+ request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name);
+ mixcom_board_on(dev);
+ }
+
+ mixcom_on(dev);
+
+
+ hw->status=inb(MIXCOM_BOARD_BASE(dev) + MIXCOM_STATUS_OFFSET);
+ if(hw->status != 0xff) {
+ printk(KERN_DEBUG "%s: board has status register, good\n", dev->name);
+ hw->card_has_status=1;
+ }
+
+ hw->txbusy = 0;
+ ch->init_status |= HW_OPEN;
+
+ if (rd_hscx(dev, HSCX_STAR) & HSCX_CTS) {
+ ch->line_status |= LINE_UP;
+ } else {
+ ch->line_status &= ~LINE_UP;
+ }
+
+ restore_flags(flags);
+
+ ch->LINE_status(dev, ch->line_status);
+
+ for (; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+ strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0444;
+ }
+ }
+
+ return 0;
+}
+
+static int MIXCOM_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ unsigned long flags;
+
+
+ save_flags(flags); cli();
+
+ mixcom_off(dev);
+
+ /* This is channel 0, twin is not open, we can safely turn off everything */
+ if(hw->channel==0 && (!(TWIN(dev)) ||
+ !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN))) {
+ mixcom_board_off(dev);
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, MIXCOM_IO_EXTENT);
+ ch->init_status &= ~IRQ_ALLOCATED;
+ }
+
+ /* This is channel 1, channel 0 has already been shutdown, we can release
+ this one too */
+ if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
+ if(COMX_CHANNEL(TWIN(dev))->init_status & IRQ_ALLOCATED) {
+ mixcom_board_off(TWIN(dev));
+ free_irq(TWIN(dev)->irq, TWIN(dev));
+ release_region(TWIN(dev)->base_addr, MIXCOM_IO_EXTENT);
+ COMX_CHANNEL(TWIN(dev))->init_status &= ~IRQ_ALLOCATED;
+ }
+ }
+
+ /* the ioports for channel 1 can be safely released */
+ if(hw->channel==1) {
+ release_region(dev->base_addr, MIXCOM_IO_EXTENT);
+ }
+
+ restore_flags(flags);
+
+ /* If we don't hold any hardware open */
+ if(!(ch->init_status & IRQ_ALLOCATED)) {
+ for (; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+ strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0644;
+ }
+ }
+ }
+
+ /* channel 0 was only waiting for us to close channel 1
+ close it completely */
+
+ if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
+ for (procfile=COMX_CHANNEL(TWIN(dev))->procdir->subdir;
+ procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+ strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0644;
+ }
+ }
+ }
+
+ ch->init_status &= ~HW_OPEN;
+ return 0;
+}
+
+static int MIXCOM_statistics(struct net_device *dev,char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ // struct mixcom_privdata *hw = ch->HW_privdata;
+ int len = 0;
+
+ if(ch->init_status && IRQ_ALLOCATED) {
+ len += sprintf(page + len, "Mixcom board: hardware open\n");
+ }
+
+ return len;
+}
+
+static int MIXCOM_dump(struct net_device *dev) {
+ return 0;
+}
+
+static int mixcom_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_IO) == 0) {
+ len = sprintf(page, "0x%x\n",
+ (unsigned int)MIXCOM_BOARD_BASE(dev));
+ } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+ len = sprintf(page, "%d\n", (unsigned int)dev->irq);
+ } else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
+ if (hw->clock) len = sprintf(page, "%d\n", hw->clock);
+ else len = sprintf(page, "external\n");
+ } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
+ len = sprintf(page, "%01d\n", hw->channel);
+ } else if (strcmp(file->name, FILENAME_TWIN) == 0) {
+ if (ch->twin) {
+ len = sprintf(page, "%s\n",ch->twin->name);
+ } else {
+ len = sprintf(page, "none\n");
+ }
+ } else {
+ printk(KERN_ERR "mixcom_read_proc: internal error, filename %s\n", file->name);
+ return -EBADF;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+ *start = page + off;
+ if (count >= len - off) *eof = 1;
+ return ( min(count, len - off) );
+}
+
+
+static struct net_device *mixcom_twin_check(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ struct net_device *twin;
+ struct comx_channel *ch_twin;
+ struct mixcom_privdata *hw_twin;
+
+
+ for ( ; procfile ; procfile = procfile->next) {
+ if(!S_ISDIR(procfile->mode)) continue;
+
+ twin = procfile->data;
+ ch_twin = twin->priv;
+ hw_twin = ch_twin->HW_privdata;
+
+
+ if (twin != dev && dev->irq && dev->base_addr &&
+ dev->irq == twin->irq &&
+ ch->hardware == ch_twin->hardware &&
+ dev->base_addr == twin->base_addr +
+ (1-2*hw->channel)*MIXCOM_CHANNEL_OFFSET &&
+ hw->channel == (1 - hw_twin->channel)) {
+ if (!TWIN(twin) || TWIN(twin)==dev) {
+ return twin;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+static void setup_twin(struct net_device* dev)
+{
+
+ if(TWIN(dev) && TWIN(TWIN(dev))) {
+ TWIN(TWIN(dev))=NULL;
+ }
+ if ((TWIN(dev) = mixcom_twin_check(dev)) != NULL) {
+ if (TWIN(TWIN(dev)) && TWIN(TWIN(dev)) != dev) {
+ TWIN(dev)=NULL;
+ } else {
+ TWIN(TWIN(dev))=dev;
+ }
+ }
+}
+
+static int mixcom_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = (struct net_device *)entry->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ char *page;
+ int value;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "mixcom_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ copy_from_user(page, buffer, count = min(count, PAGE_SIZE));
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+
+ if (strcmp(entry->name, FILENAME_IO) == 0) {
+ value = simple_strtoul(page, NULL, 0);
+ if (value != 0x180 && value != 0x280 && value != 0x380) {
+ printk(KERN_ERR "MIXCOM: incorrect io address!\n");
+ } else {
+ dev->base_addr = MIXCOM_DEV_BASE(value,hw->channel);
+ }
+ } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+ value = simple_strtoul(page, NULL, 0);
+ if (value < 0 || value > 15 || mixcom_set_irq[value]==0xFF) {
+ printk(KERN_ERR "MIXCOM: incorrect irq value!\n");
+ } else {
+ dev->irq = value;
+ }
+ } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
+ if (strncmp("ext", page, 3) == 0) {
+ hw->clock = 0;
+ } else {
+ int kbps;
+
+ kbps = simple_strtoul(page, NULL, 0);
+ if (!kbps) {
+ hw->clock = 0;
+ } else {
+ hw->clock = kbps;
+ }
+ if (hw->clock < 32 || hw->clock > 2000) {
+ hw->clock = 0;
+ printk(KERN_ERR "MIXCOM: invalid clock rate!\n");
+ }
+ }
+ if (ch->init_status & HW_OPEN && ch->HW_set_clock) {
+ ch->HW_set_clock(dev);
+ }
+ } else if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
+ value = simple_strtoul(page, NULL, 0);
+ if (value > 2) {
+ printk(KERN_ERR "Invalid channel number\n");
+ } else {
+ dev->base_addr+=(hw->channel - value) * MIXCOM_CHANNEL_OFFSET;
+ hw->channel = value;
+ }
+ } else {
+ printk(KERN_ERR "hw_read_proc: internal error, filename %s\n",
+ entry->name);
+ return -EBADF;
+ }
+
+ setup_twin(dev);
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static int MIXCOM_init(struct net_device *dev) {
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw;
+ struct proc_dir_entry *new_file;
+
+ if ((ch->HW_privdata = kmalloc(sizeof(struct mixcom_privdata),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ memset(hw = ch->HW_privdata, 0, sizeof(struct mixcom_privdata));
+
+ if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+#if 0
+ if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+#endif
+
+ if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ setup_twin(dev);
+
+ /* Fill in ch_struct hw specific pointers */
+ ch->HW_access_board = NULL;
+ ch->HW_release_board = NULL;
+ ch->HW_txe = MIXCOM_txe;
+ ch->HW_open = MIXCOM_open;
+ ch->HW_close = MIXCOM_close;
+ ch->HW_send_packet = MIXCOM_send_packet;
+ ch->HW_statistics = MIXCOM_statistics;
+ ch->HW_set_clock = NULL;
+
+ dev->base_addr = MIXCOM_DEV_BASE(MIXCOM_DEFAULT_IO,0);
+ dev->irq = MIXCOM_DEFAULT_IRQ;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int MIXCOM_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ if(hw->channel==0 && TWIN(dev)) {
+ return -EBUSY;
+ }
+
+ if(hw->channel==1 && TWIN(dev)) {
+ TWIN(TWIN(dev))=NULL;
+ }
+
+ kfree(ch->HW_privdata);
+ remove_proc_entry(FILENAME_IO, ch->procdir);
+ remove_proc_entry(FILENAME_IRQ, ch->procdir);
+#if 0
+ remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+#endif
+ remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
+ remove_proc_entry(FILENAME_TWIN, ch->procdir);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct comx_hardware mixcomhw = {
+ "mixcom",
+ VERSION,
+ MIXCOM_init,
+ MIXCOM_exit,
+ MIXCOM_dump,
+ NULL
+};
+
+/* Module management */
+
+#ifdef MODULE
+#define comx_hw_mixcom_init init_module
+#endif
+
+int __init comx_hw_mixcom_init(void)
+{
+ return(comx_register_hardware(&mixcomhw));
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+ comx_unregister_hardware("mixcom");
+}
+#endif
diff --git a/drivers/net/wan/comx-proto-fr.c b/drivers/net/wan/comx-proto-fr.c
new file mode 100644
index 000000000..ad62e310c
--- /dev/null
+++ b/drivers/net/wan/comx-proto-fr.c
@@ -0,0 +1,1006 @@
+/*
+ * Frame-relay protocol module for the COMX driver
+ * for Linux 2.2.X
+ *
+ * Original author: Tivadar Szemethy <tiv@itc.hu>
+ * Maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.70 (99/06/14):
+ * - cleaned up the source code a bit
+ * - ported back to kernel, now works as builtin code
+ *
+ * Version 0.71 (99/06/25):
+ * - use skb priorities and queues for sending keepalive
+ * - use device queues for slave->master data transmit
+ * - set IFF_RUNNING only line protocol up
+ * - fixes on slave device flags
+ *
+ * Version 0.72 (99/07/09):
+ * - handle slave tbusy with master tbusy (should be fixed)
+ * - fix the keepalive timer addition/deletion
+ */
+
+#define VERSION "0.72"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <linux/pkt_sched.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+MODULE_AUTHOR("Author: Tivadar Szemethy <tiv@itc.hu>");
+MODULE_DESCRIPTION("Frame Relay protocol implementation for the COMX drivers"
+ "for Linux kernel 2.2.X");
+
+#define FRAD_UI 0x03
+#define NLPID_IP 0xcc
+#define NLPID_Q933_LMI 0x08
+#define NLPID_CISCO_LMI 0x09
+#define Q933_ENQ 0x75
+#define Q933_LINESTAT 0x51
+#define Q933_COUNTERS 0x53
+
+#define MAXALIVECNT 3 /* No. of failures */
+
+struct fr_data {
+ u16 dlci;
+ struct net_device *master;
+ char keepa_pend;
+ char keepa_freq;
+ char keepalivecnt, keeploopcnt;
+ struct timer_list keepa_timer;
+ u8 local_cnt, remote_cnt;
+};
+
+static struct comx_protocol fr_master_protocol;
+static struct comx_protocol fr_slave_protocol;
+static struct comx_hardware fr_dlci;
+
+static void fr_keepalive_send(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct sk_buff *skb;
+ u8 *fr_packet;
+
+ skb=alloc_skb(dev->hard_header_len + 13, GFP_ATOMIC);
+
+ if(skb==NULL)
+ return;
+
+ skb_reserve(skb, dev->hard_header_len);
+
+ fr_packet=(u8*)skb_put(skb, 13);
+
+ fr_packet[0] = (fr->dlci & (1024 - 15)) >> 2;
+ fr_packet[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1
+ fr_packet[2] = FRAD_UI;
+ fr_packet[3] = NLPID_Q933_LMI;
+ fr_packet[4] = 0;
+ fr_packet[5] = Q933_ENQ;
+ fr_packet[6] = Q933_LINESTAT;
+ fr_packet[7] = 0x01;
+ fr_packet[8] = 0x01;
+ fr_packet[9] = Q933_COUNTERS;
+ fr_packet[10] = 0x02;
+ fr_packet[11] = ++fr->local_cnt;
+ fr_packet[12] = fr->remote_cnt;
+
+ skb->dev = dev;
+ skb->priority = TC_PRIO_CONTROL;
+ dev_queue_xmit(skb);
+}
+
+static void fr_keepalive_timerfun(unsigned long d)
+{
+ struct net_device *dev = (struct net_device *)d;
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ struct net_device *sdev;
+
+ if (ch->init_status & LINE_OPEN) {
+ if (fr->keepalivecnt == MAXALIVECNT) {
+ comx_status(dev, ch->line_status & ~PROTO_UP);
+ dev->flags &= ~IFF_RUNNING;
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata)
+ && (sfr->master == dev) &&
+ (sdev->flags & IFF_UP)) {
+ sdev->flags &= ~IFF_RUNNING;
+ comx_status(sdev,
+ sch->line_status & ~PROTO_UP);
+ }
+ }
+ }
+ if (fr->keepalivecnt <= MAXALIVECNT) {
+ ++fr->keepalivecnt;
+ }
+ fr_keepalive_send(dev);
+ }
+ mod_timer(&fr->keepa_timer, jiffies + HZ * fr->keepa_freq);
+}
+
+static void fr_rx_lmi(struct net_device *dev, struct sk_buff *skb,
+ u16 dlci, u8 nlpid)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ struct net_device *sdev;
+
+ if (dlci != fr->dlci || nlpid != NLPID_Q933_LMI || !fr->keepa_freq) {
+ return;
+ }
+
+ fr->remote_cnt = skb->data[7];
+ if (skb->data[8] == fr->local_cnt) { // keepalive UP!
+ fr->keepalivecnt = 0;
+ if ((ch->line_status & LINE_UP) &&
+ !(ch->line_status & PROTO_UP)) {
+ comx_status(dev, ch->line_status |= PROTO_UP);
+ dev->flags |= IFF_RUNNING;
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata)
+ && (sfr->master == dev) &&
+ (sdev->flags & IFF_UP)) {
+ sdev->flags |= IFF_RUNNING;
+ comx_status(sdev,
+ sch->line_status | PROTO_UP);
+ }
+ }
+ }
+ }
+}
+
+static void fr_set_keepalive(struct net_device *dev, int keepa)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+
+ if (!keepa && fr->keepa_freq) { // switch off
+ fr->keepa_freq = 0;
+ if (ch->line_status & LINE_UP) {
+ comx_status(dev, ch->line_status | PROTO_UP);
+ dev->flags |= IFF_RUNNING;
+ del_timer(&fr->keepa_timer);
+ }
+ return;
+ }
+
+ if (keepa) { // bekapcs
+ if(fr->keepa_freq && (ch->line_status & LINE_UP)) {
+ del_timer(&fr->keepa_timer);
+ }
+ fr->keepa_freq = keepa;
+ fr->local_cnt = fr->remote_cnt = 0;
+ fr->keepa_timer.expires = jiffies + HZ;
+ fr->keepa_timer.function = fr_keepalive_timerfun;
+ fr->keepa_timer.data = (unsigned long)dev;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+ dev->flags &= ~IFF_RUNNING;
+ comx_status(dev, ch->line_status);
+ if(ch->line_status & LINE_UP) {
+ add_timer(&fr->keepa_timer);
+ }
+ }
+}
+
+static void fr_rx(struct net_device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev = dev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ u16 dlci;
+ u8 nlpid;
+
+ if(skb->len <= 4 || skb->data[2] != FRAD_UI) {
+ kfree_skb(skb);
+ return;
+ }
+
+ /* Itt majd ki kell talalni, melyik slave kapja a csomagot */
+ dlci = ((skb->data[0] & 0xfc) << 2) | ((skb->data[1] & 0xf0) >> 4);
+ if ((nlpid = skb->data[3]) == 0) { // Optional padding
+ nlpid = skb->data[4];
+ skb_pull(skb, 1);
+ }
+ skb_pull(skb, 4); /* DLCI and header throw away */
+
+ if (ch->debug_flags & DEBUG_COMX_DLCI) {
+ comx_debug(dev, "Frame received, DLCI: %d, NLPID: 0x%02x\n",
+ dlci, nlpid);
+ comx_debug_skb(dev, skb, "Contents");
+ }
+
+ /* Megkeressuk, kihez tartozik */
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) && (sfr->dlci == dlci)) {
+ skb->dev = sdev;
+ if (ch->debug_flags & DEBUG_COMX_DLCI) {
+ comx_debug(dev, "Passing it to %s\n",sdev->name);
+ }
+ if (dev != sdev) {
+ sch->stats.rx_packets++;
+ sch->stats.rx_bytes += skb->len;
+ }
+ break;
+ }
+ }
+ switch(nlpid) {
+ case NLPID_IP:
+ skb->protocol = htons(ETH_P_IP);
+ skb->mac.raw = skb->data;
+ comx_rx(sdev, skb);
+ break;
+ case NLPID_Q933_LMI:
+ fr_rx_lmi(dev, skb, dlci, nlpid);
+ default:
+ kfree_skb(skb);
+ break;
+ }
+}
+
+static int fr_tx(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ int cnt = 1;
+
+ /* Ha minden igaz, 2 helyen fog allni a tbusy: a masternel,
+ es annal a slave-nel aki eppen kuldott.
+ Egy helyen akkor all, ha a master kuldott.
+ Ez megint jo lesz majd, ha utemezni akarunk */
+
+ /* This should be fixed, the slave tbusy should be set when
+ the masters queue is full and reset when not */
+
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) && (netif_queue_stopped(sdev))) {
+ netif_wake_queue(sdev);
+ cnt++;
+ }
+ }
+
+ netif_wake_queue(dev);
+ return 0;
+}
+
+static void fr_status(struct net_device *dev, unsigned short status)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+
+ if (status & LINE_UP) {
+ if (!fr->keepa_freq) {
+ status |= PROTO_UP;
+ }
+ } else {
+ status &= ~(PROTO_UP | PROTO_LOOP);
+ }
+
+ if (dev == fr->master && fr->keepa_freq) {
+ if (status & LINE_UP) {
+ fr->keepa_timer.expires = jiffies + HZ;
+ add_timer(&fr->keepa_timer);
+ fr->keepalivecnt = MAXALIVECNT + 1;
+ fr->keeploopcnt = 0;
+ } else {
+ del_timer(&fr->keepa_timer);
+ }
+ }
+
+ /* Itt a status valtozast vegig kell vinni az osszes slave-n */
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_FRAD || sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
+ if(status & LINE_UP) {
+ netif_wake_queue(sdev);
+ }
+ comx_status(sdev, status);
+ if(status & (PROTO_UP | PROTO_LOOP)) {
+ dev->flags |= IFF_RUNNING;
+ } else {
+ dev->flags &= ~IFF_RUNNING;
+ }
+ }
+ }
+}
+
+static int fr_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *comxdir = ch->procdir;
+ struct comx_channel *mch;
+
+ if (!(ch->init_status & HW_OPEN)) {
+ return -ENODEV;
+ }
+
+ if ((ch->hardware == &fr_dlci && ch->protocol != &fr_slave_protocol) ||
+ (ch->protocol == &fr_slave_protocol && ch->hardware != &fr_dlci)) {
+ printk(KERN_ERR "Trying to open an improperly set FR interface, giving up\n");
+ return -EINVAL;
+ }
+
+ if (!fr->master) {
+ return -ENODEV;
+ }
+ mch = fr->master->priv;
+ if (fr->master != dev && (!(mch->init_status & LINE_OPEN)
+ || (mch->protocol != &fr_master_protocol))) {
+ printk(KERN_ERR "Master %s is inactive, or incorrectly set up, "
+ "unable to open %s\n", fr->master->name, dev->name);
+ return -ENODEV;
+ }
+
+ ch->init_status |= LINE_OPEN;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+ dev->flags &= ~IFF_RUNNING;
+
+ if (fr->master == dev) {
+ if (fr->keepa_freq) {
+ fr->keepa_timer.function = fr_keepalive_timerfun;
+ fr->keepa_timer.data = (unsigned long)dev;
+ add_timer(&fr->keepa_timer);
+ } else {
+ if (ch->line_status & LINE_UP) {
+ ch->line_status |= PROTO_UP;
+ dev->flags |= IFF_RUNNING;
+ }
+ }
+ } else {
+ ch->line_status = mch->line_status;
+ if(fr->master->flags & IFF_RUNNING) {
+ dev->flags |= IFF_RUNNING;
+ }
+ }
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
+ strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
+ strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
+ comxdir->mode = S_IFREG | 0444;
+ }
+ }
+// comx_status(dev, ch->line_status);
+ return 0;
+}
+
+static int fr_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *comxdir = ch->procdir;
+
+ if (fr->master == dev) { // Ha master
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev = dev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+
+ if (!(ch->init_status & HW_OPEN)) {
+ return -ENODEV;
+ }
+
+ if (fr->keepa_freq) {
+ del_timer(&fr->keepa_timer);
+ }
+
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) &&
+ (sch->init_status & LINE_OPEN)) {
+ dev_close(sdev);
+ }
+ }
+ }
+
+ ch->init_status &= ~LINE_OPEN;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+ dev->flags &= ~IFF_RUNNING;
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
+ strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
+ strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
+ comxdir->mode = S_IFREG | 0444;
+ }
+ }
+
+ return 0;
+}
+
+static int fr_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_channel *sch, *mch;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct fr_data *sfr;
+ struct net_device *sdev;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+
+ if (!fr->master) {
+ printk(KERN_ERR "BUG: fr_xmit without a master!!! dev: %s\n", dev->name);
+ return 0;
+ }
+
+ mch = fr->master->priv;
+
+ /* Ennek majd a slave utemezeskor lesz igazan jelentosege */
+ if (ch->debug_flags & DEBUG_COMX_DLCI) {
+ comx_debug_skb(dev, skb, "Sending frame");
+ }
+
+ if (dev != fr->master) {
+ struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
+ newskb->dev=fr->master;
+ dev_queue_xmit(newskb);
+ dev_kfree_skb(skb);
+ ch->stats.tx_packets++;
+ ch->stats.tx_bytes += skb->len;
+ } else {
+ netif_stop_queue(dev);
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) && (netif_queue_stopped(sdev))) {
+ netif_stop_queue(sdev);
+ }
+ }
+
+ switch(mch->HW_send_packet(dev, skb)) {
+ case FRAME_QUEUED:
+ netif_wake_queue(dev);
+ break;
+ case FRAME_ACCEPTED:
+ case FRAME_DROPPED:
+ break;
+ case FRAME_ERROR:
+ printk(KERN_ERR "%s: Transmit frame error (len %d)\n",
+ dev->name, skb->len);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int fr_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+
+ skb_push(skb, dev->hard_header_len);
+ /* Put in DLCI */
+ skb->data[0] = (fr->dlci & (1024 - 15)) >> 2;
+ skb->data[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1
+ skb->data[2] = FRAD_UI;
+ skb->data[3] = NLPID_IP;
+
+ return dev->hard_header_len;
+}
+
+static int fr_statistics(struct net_device *dev, char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ int len = 0;
+
+ if (fr->master == dev) {
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ int slaves = 0;
+
+ len += sprintf(page + len,
+ "This is a Frame Relay master device\nSlaves: ");
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) && (sdev != dev)) {
+ slaves++;
+ len += sprintf(page + len, "%s ", sdev->name);
+ }
+ }
+ len += sprintf(page + len, "%s\n", slaves ? "" : "(none)");
+ if (fr->keepa_freq) {
+ len += sprintf(page + len, "Line keepalive (value %d) "
+ "status %s [%d]\n", fr->keepa_freq,
+ ch->line_status & PROTO_LOOP ? "LOOP" :
+ ch->line_status & PROTO_UP ? "UP" : "DOWN",
+ fr->keepalivecnt);
+ } else {
+ len += sprintf(page + len, "Line keepalive protocol "
+ "is not set\n");
+ }
+ } else { // if slave
+ len += sprintf(page + len,
+ "This is a Frame Relay slave device, master: %s\n",
+ fr->master ? fr->master->name : "(not set)");
+ }
+ return len;
+}
+
+static int fr_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = NULL;
+ int len = 0;
+
+ if (ch) {
+ fr = ch->LINE_privdata;
+ }
+
+ if (strcmp(file->name, FILENAME_DLCI) == 0) {
+ len = sprintf(page, "%04d\n", fr->dlci);
+ } else if (strcmp(file->name, FILENAME_MASTER) == 0) {
+ len = sprintf(page, "%-9s\n", fr->master ? fr->master->name :
+ "(none)");
+ } else if (strcmp(file->name, FILENAME_KEEPALIVE) == 0) {
+ len = fr->keepa_freq ? sprintf(page, "% 3d\n", fr->keepa_freq)
+ : sprintf(page, "off\n");
+ } else {
+ printk(KERN_ERR "comxfr: internal error, filename %s\n", file->name);
+ return -EBADF;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) *eof = 1;
+ return ( min(count, len - off) );
+}
+
+static int fr_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = entry->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = NULL;
+ char *page;
+
+ if (ch) {
+ fr = ch->LINE_privdata;
+ }
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comxfr_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ copy_from_user(page, buffer, count);
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+
+ if (strcmp(entry->name, FILENAME_DLCI) == 0) {
+ u16 dlci_new = simple_strtoul(page, NULL, 10);
+
+ if (dlci_new > 1023) {
+ printk(KERN_ERR "Invalid DLCI value\n");
+ }
+ else fr->dlci = dlci_new;
+ } else if (strcmp(entry->name, FILENAME_MASTER) == 0) {
+ struct net_device *new_master = dev_get_by_name(page);
+
+ if (new_master && new_master->type == ARPHRD_FRAD) {
+ struct comx_channel *sch = new_master->priv;
+ struct fr_data *sfr = sch->LINE_privdata;
+
+ if (sfr && sfr->master == new_master) {
+ if(fr->master)
+ dev_put(fr->master);
+ fr->master = new_master;
+ /* Megorokli a master statuszat */
+ ch->line_status = sch->line_status;
+ }
+ }
+ } else if (strcmp(entry->name, FILENAME_KEEPALIVE) == 0) {
+ int keepa_new = -1;
+
+ if (strcmp(page, KEEPALIVE_OFF) == 0) {
+ keepa_new = 0;
+ } else {
+ keepa_new = simple_strtoul(page, NULL, 10);
+ }
+
+ if (keepa_new < 0 || keepa_new > 100) {
+ printk(KERN_ERR "invalid keepalive\n");
+ } else {
+ if (fr->keepa_freq && keepa_new != fr->keepa_freq) {
+ fr_set_keepalive(dev, 0);
+ }
+ if (keepa_new) {
+ fr_set_keepalive(dev, keepa_new);
+ }
+ }
+ } else {
+ printk(KERN_ERR "comxfr_write_proc: internal error, filename %s\n",
+ entry->name);
+ return -EBADF;
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static int fr_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct net_device *sdev = dev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+
+ /* Ha lezarunk egy master-t, le kell kattintani a slave-eket is */
+ if (fr->master && fr->master == dev) {
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
+ dev_close(sdev);
+ sfr->master = NULL;
+ }
+ }
+ }
+ dev->flags = 0;
+ dev->type = 0;
+ dev->mtu = 0;
+ dev->hard_header_len = 0;
+
+ ch->LINE_rx = NULL;
+ ch->LINE_tx = NULL;
+ ch->LINE_status = NULL;
+ ch->LINE_open = NULL;
+ ch->LINE_close = NULL;
+ ch->LINE_xmit = NULL;
+ ch->LINE_header = NULL;
+ ch->LINE_rebuild_header = NULL;
+ ch->LINE_statistics = NULL;
+
+ ch->LINE_status = 0;
+
+ if (fr->master != dev) { // if not master, remove dlci
+ if(fr->master)
+ dev_put(fr->master);
+ remove_proc_entry(FILENAME_DLCI, ch->procdir);
+ remove_proc_entry(FILENAME_MASTER, ch->procdir);
+ } else {
+ if (fr->keepa_freq) {
+ fr_set_keepalive(dev, 0);
+ }
+ remove_proc_entry(FILENAME_KEEPALIVE, ch->procdir);
+ remove_proc_entry(FILENAME_DLCI, ch->procdir);
+ }
+
+ kfree(fr);
+ ch->LINE_privdata = NULL;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int fr_master_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr;
+ struct proc_dir_entry *new_file;
+
+ if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(fr, 0, sizeof(struct fr_data));
+ fr->master = dev; // this means master
+ fr->dlci = 0; // let's say default
+
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ dev->type = ARPHRD_FRAD;
+ dev->mtu = 1500;
+ dev->hard_header_len = 4;
+ dev->addr_len = 0;
+
+ ch->LINE_rx = fr_rx;
+ ch->LINE_tx = fr_tx;
+ ch->LINE_status = fr_status;
+ ch->LINE_open = fr_open;
+ ch->LINE_close = fr_close;
+ ch->LINE_xmit = fr_xmit;
+ ch->LINE_header = fr_header;
+ ch->LINE_rebuild_header = NULL;
+ ch->LINE_statistics = fr_statistics;
+
+ if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -ENOMEM;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &fr_read_proc;
+ new_file->write_proc = &fr_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 5;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_KEEPALIVE, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -ENOMEM;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &fr_read_proc;
+ new_file->write_proc = &fr_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 4;
+ new_file->nlink = 1;
+
+ fr_set_keepalive(dev, 0);
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int fr_slave_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr;
+ struct proc_dir_entry *new_file;
+
+ if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(fr, 0, sizeof(struct fr_data));
+
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ dev->type = ARPHRD_DLCI;
+ dev->mtu = 1500;
+ dev->hard_header_len = 4;
+ dev->addr_len = 0;
+
+ ch->LINE_rx = fr_rx;
+ ch->LINE_tx = fr_tx;
+ ch->LINE_status = fr_status;
+ ch->LINE_open = fr_open;
+ ch->LINE_close = fr_close;
+ ch->LINE_xmit = fr_xmit;
+ ch->LINE_header = fr_header;
+ ch->LINE_rebuild_header = NULL;
+ ch->LINE_statistics = fr_statistics;
+
+ if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -ENOMEM;
+ }
+
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &fr_read_proc;
+ new_file->write_proc = &fr_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 5;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_MASTER, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &fr_read_proc;
+ new_file->write_proc = &fr_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 10;
+ new_file->nlink = 1;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ ch->init_status |= HW_OPEN;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ ch->init_status &= ~HW_OPEN;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_txe(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+
+ if (!fr->master) {
+ return 0;
+ }
+
+ ch = fr->master->priv;
+ fr = ch->LINE_privdata;
+ return ch->HW_txe(fr->master);
+}
+
+static int dlci_statistics(struct net_device *dev, char *page)
+{
+ return 0;
+}
+
+static int dlci_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ ch->HW_open = dlci_open;
+ ch->HW_close = dlci_close;
+ ch->HW_txe = dlci_txe;
+ ch->HW_statistics = dlci_statistics;
+
+ /* Nincs egyeb hw info, mert ugyis a fr->master-bol fog minden kiderulni */
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ ch->HW_open = NULL;
+ ch->HW_close = NULL;
+ ch->HW_txe = NULL;
+ ch->HW_statistics = NULL;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_dump(struct net_device *dev)
+{
+ printk(KERN_INFO "dlci_dump %s, HOGY MI ???\n", dev->name);
+ return -1;
+}
+
+static struct comx_protocol fr_master_protocol = {
+ "frad",
+ VERSION,
+ ARPHRD_FRAD,
+ fr_master_init,
+ fr_exit,
+ NULL
+};
+
+static struct comx_protocol fr_slave_protocol = {
+ "ietf-ip",
+ VERSION,
+ ARPHRD_DLCI,
+ fr_slave_init,
+ fr_exit,
+ NULL
+};
+
+static struct comx_hardware fr_dlci = {
+ "dlci",
+ VERSION,
+ dlci_init,
+ dlci_exit,
+ dlci_dump,
+ NULL
+};
+
+#ifdef MODULE
+#define comx_proto_fr_init init_module
+#endif
+
+int __init comx_proto_fr_init(void)
+{
+ int ret;
+
+ if ((ret = comx_register_hardware(&fr_dlci))) {
+ return ret;
+ }
+ if ((ret = comx_register_protocol(&fr_master_protocol))) {
+ return ret;
+ }
+ return comx_register_protocol(&fr_slave_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_hardware(fr_dlci.name);
+ comx_unregister_protocol(fr_master_protocol.name);
+ comx_unregister_protocol(fr_slave_protocol.name);
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/wan/comx-proto-lapb.c b/drivers/net/wan/comx-proto-lapb.c
new file mode 100644
index 000000000..c0fc0c6bd
--- /dev/null
+++ b/drivers/net/wan/comx-proto-lapb.c
@@ -0,0 +1,548 @@
+/*
+ * LAPB protocol module for the COMX driver
+ * for Linux kernel 2.2.X
+ *
+ * Original author: Tivadar Szemethy <tiv@itc.hu>
+ * Maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1997-1999 (C) ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.80 (99/06/14):
+ * - cleaned up the source code a bit
+ * - ported back to kernel, now works as non-module
+ *
+ */
+
+#define VERSION "0.80"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <asm/uaccess.h>
+#include <linux/lapb.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir);
+
+static void comxlapb_rx(struct net_device *dev, struct sk_buff *skb)
+{
+ if (!dev || !dev->priv) {
+ dev_kfree_skb(skb);
+ } else {
+ lapb_data_received(dev->priv, skb);
+ }
+}
+
+static int comxlapb_tx(struct net_device *dev)
+{
+ netif_wake_queue(dev);
+ return 0;
+}
+
+static int comxlapb_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ return dev->hard_header_len;
+}
+
+static void comxlapb_status(struct net_device *dev, unsigned short status)
+{
+ struct comx_channel *ch;
+
+ if (!dev || !(ch = dev->priv)) {
+ return;
+ }
+ if (status & LINE_UP) {
+ netif_wake_queue(dev);
+ }
+ comx_status(dev, status);
+}
+
+static int comxlapb_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ int err = 0;
+
+ if (!(ch->init_status & HW_OPEN)) {
+ return -ENODEV;
+ }
+
+ err = lapb_connect_request(ch);
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(dev, "%s: lapb opened, error code: %d\n",
+ dev->name, err);
+ }
+
+ if (!err) {
+ ch->init_status |= LINE_OPEN;
+ MOD_INC_USE_COUNT;
+ }
+ return err;
+}
+
+static int comxlapb_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (!(ch->init_status & HW_OPEN)) {
+ return -ENODEV;
+ }
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(dev, "%s: lapb closed\n", dev->name);
+ }
+
+ lapb_disconnect_request(ch);
+
+ ch->init_status &= ~LINE_OPEN;
+ ch->line_status &= ~PROTO_UP;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int comxlapb_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct sk_buff *skb2;
+
+ if (!dev || !(ch = dev->priv) || !(dev->flags & (IFF_UP | IFF_RUNNING))) {
+ return -ENODEV;
+ }
+
+ if (dev->type == ARPHRD_X25) { // first byte tells what to do
+ switch(skb->data[0]) {
+ case 0x00:
+ break; // transmit
+ case 0x01:
+ lapb_connect_request(ch);
+ kfree_skb(skb);
+ return 0;
+ case 0x02:
+ lapb_disconnect_request(ch);
+ default:
+ kfree_skb(skb);
+ return 0;
+ }
+ skb_pull(skb,1);
+ }
+
+ netif_stop_queue(dev);
+
+ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+ lapb_data_request(ch, skb2);
+ }
+
+ return FRAME_ACCEPTED;
+}
+
+static int comxlapb_statistics(struct net_device *dev, char *page)
+{
+ struct lapb_parms_struct parms;
+ int len = 0;
+
+ len += sprintf(page + len, "Line status: ");
+ if (lapb_getparms(dev->priv, &parms) != LAPB_OK) {
+ len += sprintf(page + len, "not initialized\n");
+ return len;
+ }
+ len += sprintf(page + len, "%s (%s), T1: %d/%d, T2: %d/%d, N2: %d/%d, "
+ "window: %d\n", parms.mode & LAPB_DCE ? "DCE" : "DTE",
+ parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD",
+ parms.t1timer, parms.t1, parms.t2timer, parms.t2,
+ parms.n2count, parms.n2, parms.window);
+
+ return len;
+}
+
+static int comxlapb_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct lapb_parms_struct parms;
+ int len = 0;
+
+ if (lapb_getparms(dev->priv, &parms)) {
+ return -ENODEV;
+ }
+
+ if (strcmp(file->name, FILENAME_T1) == 0) {
+ len += sprintf(page + len, "%02u / %02u\n",
+ parms.t1timer, parms.t1);
+ } else if (strcmp(file->name, FILENAME_T2) == 0) {
+ len += sprintf(page + len, "%02u / %02u\n",
+ parms.t2timer, parms.t2);
+ } else if (strcmp(file->name, FILENAME_N2) == 0) {
+ len += sprintf(page + len, "%02u / %02u\n",
+ parms.n2count, parms.n2);
+ } else if (strcmp(file->name, FILENAME_WINDOW) == 0) {
+ len += sprintf(page + len, "%u\n", parms.window);
+ } else if (strcmp(file->name, FILENAME_MODE) == 0) {
+ len += sprintf(page + len, "%s, %s\n",
+ parms.mode & LAPB_DCE ? "DCE" : "DTE",
+ parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD");
+ } else {
+ printk(KERN_ERR "comxlapb: internal error, filename %s\n", file->name);
+ return -EBADF;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return ( min(count, len - off) );
+}
+
+static int comxlapb_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = entry->parent->data;
+ struct lapb_parms_struct parms;
+ unsigned long parm;
+ char *page;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comxlapb_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (lapb_getparms(dev->priv, &parms)) {
+ return -ENODEV;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ copy_from_user(page, buffer, count);
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+
+ if (strcmp(entry->name, FILENAME_T1) == 0) {
+ parm=simple_strtoul(page,NULL,10);
+ if (parm > 0 && parm < 100) {
+ parms.t1=parm;
+ lapb_setparms(dev->priv, &parms);
+ }
+ } else if (strcmp(entry->name, FILENAME_T2) == 0) {
+ parm=simple_strtoul(page, NULL, 10);
+ if (parm > 0 && parm < 100) {
+ parms.t2=parm;
+ lapb_setparms(dev->priv, &parms);
+ }
+ } else if (strcmp(entry->name, FILENAME_N2) == 0) {
+ parm=simple_strtoul(page, NULL, 10);
+ if (parm > 0 && parm < 100) {
+ parms.n2=parm;
+ lapb_setparms(dev->priv, &parms);
+ }
+ } else if (strcmp(entry->name, FILENAME_WINDOW) == 0) {
+ parms.window = simple_strtoul(page, NULL, 10);
+ lapb_setparms(dev->priv, &parms);
+ } else if (strcmp(entry->name, FILENAME_MODE) == 0) {
+ if (comx_strcasecmp(page, "dte") == 0) {
+ parms.mode &= ~(LAPB_DCE | LAPB_DTE);
+ parms.mode |= LAPB_DTE;
+ } else if (comx_strcasecmp(page, "dce") == 0) {
+ parms.mode &= ~(LAPB_DTE | LAPB_DCE);
+ parms.mode |= LAPB_DCE;
+ } else if (comx_strcasecmp(page, "std") == 0 ||
+ comx_strcasecmp(page, "standard") == 0) {
+ parms.mode &= ~LAPB_EXTENDED;
+ parms.mode |= LAPB_STANDARD;
+ } else if (comx_strcasecmp(page, "ext") == 0 ||
+ comx_strcasecmp(page, "extended") == 0) {
+ parms.mode &= ~LAPB_STANDARD;
+ parms.mode |= LAPB_EXTENDED;
+ }
+ lapb_setparms(dev->priv, &parms);
+ } else {
+ printk(KERN_ERR "comxlapb_write_proc: internal error, filename %s\n",
+ entry->name);
+ return -EBADF;
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static void comxlapb_connected(void *token, int reason)
+{
+ struct comx_channel *ch = token;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(ch->dev, "%s: lapb connected, reason: %d\n",
+ ch->dev->name, reason);
+ }
+
+ if (ch->dev->type == ARPHRD_X25) {
+ unsigned char *p;
+ struct sk_buff *skb;
+
+ if ((skb = dev_alloc_skb(1)) == NULL) {
+ printk(KERN_ERR "comxlapb: out of memory!\n");
+ return;
+ }
+ p = skb_put(skb,1);
+ *p = 0x01; // link established
+ skb->dev = ch->dev;
+ skb->protocol = htons(ETH_P_X25);
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
+
+ netif_rx(skb);
+ }
+
+ for (; comxdir; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_MODE) == 0) {
+ comxdir->mode = S_IFREG | 0444;
+ }
+ }
+
+
+ ch->line_status |= PROTO_UP;
+ comx_status(ch->dev, ch->line_status);
+}
+
+static void comxlapb_disconnected(void *token, int reason)
+{
+ struct comx_channel *ch = token;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(ch->dev, "%s: lapb disconnected, reason: %d\n",
+ ch->dev->name, reason);
+ }
+
+ if (ch->dev->type == ARPHRD_X25) {
+ unsigned char *p;
+ struct sk_buff *skb;
+
+ if ((skb = dev_alloc_skb(1)) == NULL) {
+ printk(KERN_ERR "comxlapb: out of memory!\n");
+ return;
+ }
+ p = skb_put(skb,1);
+ *p = 0x02; // link disconnected
+ skb->dev = ch->dev;
+ skb->protocol = htons(ETH_P_X25);
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
+
+ netif_rx(skb);
+ }
+
+ for (; comxdir; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_MODE) == 0) {
+ comxdir->mode = S_IFREG | 0644;
+ }
+ }
+
+ ch->line_status &= ~PROTO_UP;
+ comx_status(ch->dev, ch->line_status);
+}
+
+static void comxlapb_data_indication(void *token, struct sk_buff *skb)
+{
+ struct comx_channel *ch = token;
+
+ if (ch->dev->type == ARPHRD_X25) {
+ skb_push(skb, 1);
+ skb->data[0] = 0; // indicate data for X25
+ skb->protocol = htons(ETH_P_X25);
+ } else {
+ skb->protocol = htons(ETH_P_IP);
+ }
+
+ skb->dev = ch->dev;
+ skb->mac.raw = skb->data;
+ comx_rx(ch->dev, skb);
+}
+
+static void comxlapb_data_transmit(void *token, struct sk_buff *skb)
+{
+ struct comx_channel *ch = token;
+
+ if (ch->HW_send_packet) {
+ ch->HW_send_packet(ch->dev, skb);
+ }
+}
+
+static int comxlapb_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ dev->flags = 0;
+ dev->type = 0;
+ dev->mtu = 0;
+ dev->hard_header_len = 0;
+
+ ch->LINE_rx = NULL;
+ ch->LINE_tx = NULL;
+ ch->LINE_status = NULL;
+ ch->LINE_open = NULL;
+ ch->LINE_close = NULL;
+ ch->LINE_xmit = NULL;
+ ch->LINE_header = NULL;
+ ch->LINE_statistics = NULL;
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(dev, "%s: unregistering lapb\n", dev->name);
+ }
+ lapb_unregister(dev->priv);
+
+ remove_proc_entry(FILENAME_T1, ch->procdir);
+ remove_proc_entry(FILENAME_T2, ch->procdir);
+ remove_proc_entry(FILENAME_N2, ch->procdir);
+ remove_proc_entry(FILENAME_MODE, ch->procdir);
+ remove_proc_entry(FILENAME_WINDOW, ch->procdir);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int comxlapb_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct lapb_register_struct lapbreg;
+
+ dev->mtu = 1500;
+ dev->hard_header_len = 4;
+ dev->addr_len = 0;
+
+ ch->LINE_rx = comxlapb_rx;
+ ch->LINE_tx = comxlapb_tx;
+ ch->LINE_status = comxlapb_status;
+ ch->LINE_open = comxlapb_open;
+ ch->LINE_close = comxlapb_close;
+ ch->LINE_xmit = comxlapb_xmit;
+ ch->LINE_header = comxlapb_header;
+ ch->LINE_statistics = comxlapb_statistics;
+
+ lapbreg.connect_confirmation = comxlapb_connected;
+ lapbreg.connect_indication = comxlapb_connected;
+ lapbreg.disconnect_confirmation = comxlapb_disconnected;
+ lapbreg.disconnect_indication = comxlapb_disconnected;
+ lapbreg.data_indication = comxlapb_data_indication;
+ lapbreg.data_transmit = comxlapb_data_transmit;
+ if (lapb_register(dev->priv, &lapbreg)) {
+ return -ENOMEM;
+ }
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(dev, "%s: lapb registered\n", dev->name);
+ }
+
+ if (!create_comxlapb_proc_entry(FILENAME_T1, 0644, 8, ch->procdir)) {
+ return -ENOMEM;
+ }
+ if (!create_comxlapb_proc_entry(FILENAME_T2, 0644, 8, ch->procdir)) {
+ return -ENOMEM;
+ }
+ if (!create_comxlapb_proc_entry(FILENAME_N2, 0644, 8, ch->procdir)) {
+ return -ENOMEM;
+ }
+ if (!create_comxlapb_proc_entry(FILENAME_MODE, 0644, 14, ch->procdir)) {
+ return -ENOMEM;
+ }
+ if (!create_comxlapb_proc_entry(FILENAME_WINDOW, 0644, 0, ch->procdir)) {
+ return -ENOMEM;
+ }
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int comxlapb_init_lapb(struct net_device *dev)
+{
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ dev->type = ARPHRD_LAPB;
+
+ return(comxlapb_init(dev));
+}
+
+static int comxlapb_init_x25(struct net_device *dev)
+{
+ dev->flags = IFF_NOARP;
+ dev->type = ARPHRD_X25;
+
+ return(comxlapb_init(dev));
+}
+
+static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir)
+{
+ struct proc_dir_entry *new_file;
+
+ if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxlapb_read_proc;
+ new_file->write_proc = &comxlapb_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = size;
+ new_file->nlink = 1;
+ }
+ return(new_file);
+}
+
+static struct comx_protocol comxlapb_protocol = {
+ "lapb",
+ VERSION,
+ ARPHRD_LAPB,
+ comxlapb_init_lapb,
+ comxlapb_exit,
+ NULL
+};
+
+static struct comx_protocol comx25_protocol = {
+ "x25",
+ VERSION,
+ ARPHRD_X25,
+ comxlapb_init_x25,
+ comxlapb_exit,
+ NULL
+};
+
+#ifdef MODULE
+#define comx_proto_lapb_init init_module
+#endif
+
+__initfunc(int comx_proto_lapb_init(void))
+{
+ int ret;
+
+ if ((ret = comx_register_protocol(&comxlapb_protocol)) != 0) {
+ return ret;
+ }
+ return comx_register_protocol(&comx25_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_protocol(comxlapb_protocol.name);
+ comx_unregister_protocol(comx25_protocol.name);
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/wan/comx-proto-ppp.c b/drivers/net/wan/comx-proto-ppp.c
new file mode 100644
index 000000000..0b791685d
--- /dev/null
+++ b/drivers/net/wan/comx-proto-ppp.c
@@ -0,0 +1,269 @@
+/*
+ * Synchronous PPP / Cisco-HDLC driver for the COMX boards
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * based on skeleton code by Tivadar Szemethy <tiv@itc.hu>
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ *
+ * Version 0.10 (99/06/10):
+ * - written the first code :)
+ *
+ * Version 0.20 (99/06/16):
+ * - added hdlc protocol
+ * - protocol up is IFF_RUNNING
+ *
+ * Version 0.21 (99/07/15):
+ * - some small fixes with the line status
+ *
+ * Version 0.22 (99/08/05):
+ * - don't test IFF_RUNNING but the pp_link_state of the sppp
+ *
+ * Version 0.23 (99/12/02):
+ * - tbusy fixes
+ *
+ */
+
+#define VERSION "0.23"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <asm/uaccess.h>
+#include <linux/init.h>
+
+#include "syncppp.h"
+#include "comx.h"
+
+MODULE_AUTHOR("Author: Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Cisco-HDLC / Synchronous PPP driver for the COMX sync serial boards");
+
+static struct comx_protocol syncppp_protocol;
+static struct comx_protocol hdlc_protocol;
+
+struct syncppp_data {
+ struct timer_list status_timer;
+};
+
+static void syncppp_status_timerfun(unsigned long d) {
+ struct net_device *dev=(struct net_device *)d;
+ struct comx_channel *ch=dev->priv;
+ struct syncppp_data *spch=ch->LINE_privdata;
+ struct sppp *sp = (struct sppp *)sppp_of(dev);
+
+ if(!(ch->line_status & PROTO_UP) &&
+ (sp->pp_link_state==SPPP_LINK_UP)) {
+ comx_status(dev, ch->line_status | PROTO_UP);
+ }
+ if((ch->line_status & PROTO_UP) &&
+ (sp->pp_link_state==SPPP_LINK_DOWN)) {
+ comx_status(dev, ch->line_status & ~PROTO_UP);
+ }
+ mod_timer(&spch->status_timer,jiffies + HZ*3);
+}
+
+static int syncppp_tx(struct net_device *dev)
+{
+ struct comx_channel *ch=dev->priv;
+
+ if(ch->line_status & LINE_UP) {
+ netif_wake_queue(dev);
+ }
+ return 0;
+}
+
+static void syncppp_status(struct net_device *dev, unsigned short status)
+{
+ status &= ~(PROTO_UP | PROTO_LOOP);
+ if(status & LINE_UP) {
+ netif_wake_queue(dev);
+ sppp_open(dev);
+ } else {
+ /* Line went down */
+ netif_stop_queue(dev);
+ sppp_close(dev);
+ }
+ comx_status(dev, status);
+}
+
+static int syncppp_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct syncppp_data *spch = ch->LINE_privdata;
+
+ if (!(ch->init_status & HW_OPEN)) return -ENODEV;
+
+ ch->init_status |= LINE_OPEN;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+
+ if(ch->line_status & LINE_UP) {
+ sppp_open(dev);
+ }
+
+ init_timer(&spch->status_timer);
+ spch->status_timer.function=syncppp_status_timerfun;
+ spch->status_timer.data=(unsigned long)dev;
+ spch->status_timer.expires=jiffies + HZ*3;
+ add_timer(&spch->status_timer);
+
+ return 0;
+}
+
+static int syncppp_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct syncppp_data *spch = ch->LINE_privdata;
+
+ if (!(ch->init_status & HW_OPEN)) return -ENODEV;
+ del_timer(&spch->status_timer);
+
+ sppp_close(dev);
+
+ ch->init_status &= ~LINE_OPEN;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+
+ return 0;
+}
+
+static int syncppp_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ netif_stop_queue(dev);
+ switch(ch->HW_send_packet(dev, skb)) {
+ case FRAME_QUEUED:
+ netif_wake_queue(dev);
+ break;
+ case FRAME_ACCEPTED:
+ case FRAME_DROPPED:
+ break;
+ case FRAME_ERROR:
+ printk(KERN_ERR "%s: Transmit frame error (len %d)\n",
+ dev->name, skb->len);
+ break;
+ }
+ return 0;
+}
+
+
+static int syncppp_statistics(struct net_device *dev, char *page)
+{
+ int len = 0;
+
+ len += sprintf(page + len, " ");
+ return len;
+}
+
+
+static int syncppp_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ sppp_detach(dev);
+
+ dev->flags = 0;
+ dev->type = 0;
+ dev->mtu = 0;
+
+ ch->LINE_rx = NULL;
+ ch->LINE_tx = NULL;
+ ch->LINE_status = NULL;
+ ch->LINE_open = NULL;
+ ch->LINE_close = NULL;
+ ch->LINE_xmit = NULL;
+ ch->LINE_header = NULL;
+ ch->LINE_rebuild_header = NULL;
+ ch->LINE_statistics = NULL;
+
+ kfree(ch->LINE_privdata);
+ ch->LINE_privdata = NULL;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int syncppp_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct ppp_device *pppdev = (struct ppp_device *)ch->if_ptr;
+
+ ch->LINE_privdata = kmalloc(sizeof(struct syncppp_data), GFP_KERNEL);
+
+ pppdev->dev = dev;
+ sppp_attach(pppdev);
+
+ if(ch->protocol == &hdlc_protocol) {
+ pppdev->sppp.pp_flags |= PP_CISCO;
+ dev->type = ARPHRD_HDLC;
+ } else {
+ pppdev->sppp.pp_flags &= ~PP_CISCO;
+ dev->type = ARPHRD_PPP;
+ }
+
+ ch->LINE_rx = sppp_input;
+ ch->LINE_tx = syncppp_tx;
+ ch->LINE_status = syncppp_status;
+ ch->LINE_open = syncppp_open;
+ ch->LINE_close = syncppp_close;
+ ch->LINE_xmit = syncppp_xmit;
+ ch->LINE_header = NULL;
+ ch->LINE_statistics = syncppp_statistics;
+
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static struct comx_protocol syncppp_protocol = {
+ "ppp",
+ VERSION,
+ ARPHRD_PPP,
+ syncppp_init,
+ syncppp_exit,
+ NULL
+};
+
+static struct comx_protocol hdlc_protocol = {
+ "hdlc",
+ VERSION,
+ ARPHRD_PPP,
+ syncppp_init,
+ syncppp_exit,
+ NULL
+};
+
+
+#ifdef MODULE
+#define comx_proto_ppp_init init_module
+#endif
+
+int __init comx_proto_ppp_init(void)
+{
+ int ret;
+
+ if(0!=(ret=comx_register_protocol(&hdlc_protocol))) {
+ return ret;
+ }
+ return comx_register_protocol(&syncppp_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_protocol(syncppp_protocol.name);
+ comx_unregister_protocol(hdlc_protocol.name);
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/wan/comx.c b/drivers/net/wan/comx.c
new file mode 100644
index 000000000..d3ca69e86
--- /dev/null
+++ b/drivers/net/wan/comx.c
@@ -0,0 +1,1238 @@
+/*
+ * Device driver framework for the COMX line of synchronous serial boards
+ *
+ * for Linux kernel 2.2.X
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Current maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.80 (99/06/11):
+ * - clean up source code (playing a bit of indent)
+ * - port back to kernel, add support for non-module versions
+ * - add support for board resets when channel protocol is down
+ * - reset the device structure after protocol exit
+ * the syncppp driver needs it
+ * - add support for /proc/comx/protocols and
+ * /proc/comx/boardtypes
+ *
+ * Version 0.81 (99/06/21):
+ * - comment out the board reset support code, the locomx
+ * driver seems not buggy now
+ * - printk() levels fixed
+ *
+ * Version 0.82 (99/07/08):
+ * - Handle stats correctly if the lowlevel driver is
+ * is not a comx one (locomx - z85230)
+ *
+ * Version 0.83 (99/07/15):
+ * - reset line_status when interface is down
+ *
+ * Version 0.84 (99/12/01):
+ * - comx_status should not check for IFF_UP (to report
+ * line status from dev->open())
+ */
+
+#define VERSION "0.84"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#ifndef CONFIG_PROC_FS
+#error For now, COMX really needs the /proc filesystem
+#endif
+
+#include "comx.h"
+#include "syncppp.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Common code for the COMX synchronous serial adapters");
+
+extern int comx_hw_comx_init(void);
+extern int comx_hw_locomx_init(void);
+extern int comx_hw_mixcom_init(void);
+extern int comx_proto_hdlc_init(void);
+extern int comx_proto_ppp_init(void);
+extern int comx_proto_syncppp_init(void);
+extern int comx_proto_lapb_init(void);
+extern int comx_proto_fr_init(void);
+
+static struct comx_hardware *comx_channels = NULL;
+static struct comx_protocol *comx_lines = NULL;
+
+struct inode_operations comx_normal_inode_ops;
+static struct inode_operations comx_root_inode_ops; // for mkdir
+static struct inode_operations comx_debug_inode_ops; // mas a file_ops
+static struct file_operations comx_normal_file_ops; // with open/relase
+static struct file_operations comx_debug_file_ops; // with lseek+read
+
+static void comx_delete_dentry(struct dentry *dentry);
+static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir);
+
+static void comx_fill_inode(struct inode *inode, int fill);
+
+static struct dentry_operations comx_dentry_operations = {
+ NULL, /* revalidate */
+ NULL, /* d_hash */
+ NULL, /* d_compare */
+ &comx_delete_dentry /* d_delete */
+};
+
+
+struct proc_dir_entry comx_root_dir = {
+ 0, 4, "comx",
+ S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO, 2, 0, 0,
+ 0, &comx_root_inode_ops,
+ NULL, comx_fill_inode,
+ NULL, &proc_root, NULL
+};
+
+struct comx_debugflags_struct comx_debugflags[] = {
+ { "comx_rx", DEBUG_COMX_RX },
+ { "comx_tx", DEBUG_COMX_TX },
+ { "hw_tx", DEBUG_HW_TX },
+ { "hw_rx", DEBUG_HW_RX },
+ { "hdlc_keepalive", DEBUG_HDLC_KEEPALIVE },
+ { "comxppp", DEBUG_COMX_PPP },
+ { "comxlapb", DEBUG_COMX_LAPB },
+ { "dlci", DEBUG_COMX_DLCI },
+ { NULL, 0 }
+};
+
+static void comx_fill_inode(struct inode *inode, int fill)
+{
+ if (fill)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+}
+
+
+int comx_debug(struct net_device *dev, char *fmt, ...)
+{
+ struct comx_channel *ch = dev->priv;
+ char *page,*str;
+ va_list args;
+ int len;
+
+ if (!ch->debug_area) return 0;
+
+ if (!(page = (char *)__get_free_page(GFP_ATOMIC))) return -ENOMEM;
+
+ va_start(args, fmt);
+ len = vsprintf(str = page, fmt, args);
+ va_end(args);
+
+ if (len >= PAGE_SIZE) {
+ printk(KERN_ERR "comx_debug: PANIC! len = %d !!!\n", len);
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+
+ while (len) {
+ int to_copy;
+ int free = (ch->debug_start - ch->debug_end + ch->debug_size)
+ % ch->debug_size;
+
+ to_copy = min( free ? free : ch->debug_size,
+ min (ch->debug_size - ch->debug_end, len) );
+ memcpy(ch->debug_area + ch->debug_end, str, to_copy);
+ str += to_copy;
+ len -= to_copy;
+ ch->debug_end = (ch->debug_end + to_copy) % ch->debug_size;
+ if (ch->debug_start == ch->debug_end) // Full ? push start away
+ ch->debug_start = (ch->debug_start + len + 1) %
+ ch->debug_size;
+ ch->debug_file->size = (ch->debug_end - ch->debug_start +
+ ch->debug_size) % ch->debug_size;
+ }
+
+ free_page((unsigned long)page);
+ return 0;
+}
+
+int comx_debug_skb(struct net_device *dev, struct sk_buff *skb, char *msg)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (!ch->debug_area) return 0;
+ if (!skb) comx_debug(dev, "%s: %s NULL skb\n\n", dev->name, msg);
+ if (!skb->len) comx_debug(dev, "%s: %s empty skb\n\n", dev->name, msg);
+
+ return comx_debug_bytes(dev, skb->data, skb->len, msg);
+}
+
+int comx_debug_bytes(struct net_device *dev, unsigned char *bytes, int len,
+ char *msg)
+{
+ int pos = 0;
+ struct comx_channel *ch = dev->priv;
+
+ if (!ch->debug_area) return 0;
+
+ comx_debug(dev, "%s: %s len %d\n", dev->name, msg, len);
+
+ while (pos != len) {
+ char line[80];
+ int i = 0;
+
+ memset(line, 0, 80);
+ sprintf(line,"%04d ", pos);
+ do {
+ sprintf(line + 5 + (pos % 16) * 3, "%02x", bytes[pos]);
+ sprintf(line + 60 + (pos % 16), "%c",
+ isprint(bytes[pos]) ? bytes[pos] : '.');
+ pos++;
+ } while (pos != len && pos % 16);
+
+ while ( i++ != 78 ) if (line[i] == 0) line[i] = ' ';
+ line[77] = '\n';
+ line[78] = 0;
+
+ comx_debug(dev, "%s", line);
+ }
+ comx_debug(dev, "\n");
+ return 0;
+}
+
+static void comx_loadavg_timerfun(unsigned long d)
+{
+ struct net_device *dev = (struct net_device *)d;
+ struct comx_channel *ch = dev->priv;
+
+ ch->avg_bytes[ch->loadavg_counter] = ch->current_stats->rx_bytes;
+ ch->avg_bytes[ch->loadavg_counter + ch->loadavg_size] =
+ ch->current_stats->tx_bytes;
+
+ ch->loadavg_counter = (ch->loadavg_counter + 1) % ch->loadavg_size;
+
+ mod_timer(&ch->loadavg_timer,jiffies + HZ * ch->loadavg[0]);
+}
+
+#if 0
+static void comx_reset_timerfun(unsigned long d)
+{
+ struct net_device *dev = (struct net_device *)d;
+ struct comx_channel *ch = dev->priv;
+
+ if(!(ch->line_status & (PROTO_LOOP | PROTO_UP))) {
+ if(test_and_set_bit(0,&ch->reset_pending) && ch->HW_reset) {
+ ch->HW_reset(dev);
+ }
+ }
+
+ mod_timer(&ch->reset_timer, jiffies + HZ * ch->reset_timeout);
+}
+#endif
+
+static int comx_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+ int ret=0;
+
+ if (!ch->protocol || !ch->hardware) return -ENODEV;
+
+ if ((ret = ch->HW_open(dev))) return ret;
+ if ((ret = ch->LINE_open(dev))) {
+ ch->HW_close(dev);
+ return ret;
+ };
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
+ strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
+ comxdir->mode = S_IFREG | 0444;
+ }
+
+#if 0
+ ch->reset_pending = 1;
+ ch->reset_timeout = 30;
+ ch->reset_timer.function = comx_reset_timerfun;
+ ch->reset_timer.data = (unsigned long)dev;
+ ch->reset_timer.expires = jiffies + HZ * ch->reset_timeout;
+ add_timer(&ch->reset_timer);
+#endif
+
+ return 0;
+}
+
+static int comx_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+ int ret = -ENODEV;
+
+ if (test_and_clear_bit(0, &ch->lineup_pending)) {
+ del_timer(&ch->lineup_timer);
+ }
+
+#if 0
+ del_timer(&ch->reset_timer);
+#endif
+
+ if (ch->init_status & LINE_OPEN && ch->protocol && ch->LINE_close) {
+ ret = ch->LINE_close(dev);
+ }
+
+ if (ret) return ret;
+
+ if (ch->init_status & HW_OPEN && ch->hardware && ch->HW_close) {
+ ret = ch->HW_close(dev);
+ }
+
+ ch->line_status=0;
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
+ strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
+ comxdir->mode = S_IFREG | 0644;
+ }
+
+ return ret;
+}
+
+void comx_status(struct net_device *dev, int status)
+{
+ struct comx_channel *ch = dev->priv;
+
+#if 0
+ if(status & (PROTO_UP | PROTO_LOOP)) {
+ clear_bit(0,&ch->reset_pending);
+ }
+#endif
+
+ printk(KERN_NOTICE "Interface %s: modem status %s, line protocol %s\n",
+ dev->name, status & LINE_UP ? "UP" : "DOWN",
+ status & PROTO_LOOP ? "LOOP" : status & PROTO_UP ?
+ "UP" : "DOWN");
+
+ ch->line_status = status;
+}
+
+static int comx_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ int rc;
+
+ if (skb->len > dev->mtu + dev->hard_header_len) {
+ printk(KERN_ERR "comx_xmit: %s: skb->len %d > dev->mtu %d\n", dev->name,
+ (int)skb->len, dev->mtu);
+ }
+
+ if (ch->debug_flags & DEBUG_COMX_TX) {
+ comx_debug_skb(dev, skb, "comx_xmit skb");
+ }
+
+ rc=ch->LINE_xmit(skb, dev);
+// if (!rc) dev_kfree_skb(skb);
+
+ return rc;
+}
+
+static int comx_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->LINE_header) {
+ return (ch->LINE_header(skb, dev, type, daddr, saddr, len));
+ } else {
+ return 0;
+ }
+}
+
+static int comx_rebuild_header(struct sk_buff *skb)
+{
+ struct net_device *dev = skb->dev;
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->LINE_rebuild_header) {
+ return(ch->LINE_rebuild_header(skb));
+ } else {
+ return 0;
+ }
+}
+
+int comx_rx(struct net_device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->debug_flags & DEBUG_COMX_RX) {
+ comx_debug_skb(dev, skb, "comx_rx skb");
+ }
+ if (skb) {
+ netif_rx(skb);
+ }
+ return 0;
+}
+
+static struct net_device_stats *comx_stats(struct net_device *dev)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+
+ return ch->current_stats;
+}
+
+void comx_lineup_func(unsigned long d)
+{
+ struct net_device *dev = (struct net_device *)d;
+ struct comx_channel *ch = dev->priv;
+
+ del_timer(&ch->lineup_timer);
+ clear_bit(0, &ch->lineup_pending);
+
+ if (ch->LINE_status) {
+ ch->LINE_status(dev, ch->line_status |= LINE_UP);
+ }
+}
+
+#define LOADAVG(avg, off) (int) \
+ ((ch->avg_bytes[(ch->loadavg_counter - 1 + ch->loadavg_size * 2) \
+ % ch->loadavg_size + off] - ch->avg_bytes[(ch->loadavg_counter - 1 \
+ - ch->loadavg[avg] / ch->loadavg[0] + ch->loadavg_size * 2) \
+ % ch->loadavg_size + off]) / ch->loadavg[avg] * 8)
+
+static int comx_statistics(struct net_device *dev, char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ int len = 0;
+ int tmp;
+ int i = 0;
+ char tmpstr[20];
+ int tmpstrlen = 0;
+
+ len += sprintf(page + len, "Interface administrative status is %s, "
+ "modem status is %s, protocol is %s\n",
+ dev->flags & IFF_UP ? "UP" : "DOWN",
+ ch->line_status & LINE_UP ? "UP" : "DOWN",
+ ch->line_status & PROTO_LOOP ? "LOOP" :
+ ch->line_status & PROTO_UP ? "UP" : "DOWN");
+ len += sprintf(page + len, "Modem status changes: %lu, Transmitter status "
+ "is %s, tbusy: %d\n", ch->current_stats->tx_carrier_errors, ch->HW_txe ?
+ ch->HW_txe(dev) ? "IDLE" : "BUSY" : "NOT READY", (int)dev->tbusy);
+ len += sprintf(page + len, "Interface load (input): %d / %d / %d bits/s (",
+ LOADAVG(0,0), LOADAVG(1, 0), LOADAVG(2, 0));
+ tmpstr[0] = 0;
+ for (i=0; i != 3; i++) {
+ char tf;
+
+ tf = ch->loadavg[i] % 60 == 0 &&
+ ch->loadavg[i] / 60 > 0 ? 'm' : 's';
+ tmpstrlen += sprintf(tmpstr + tmpstrlen, "%d%c%s",
+ ch->loadavg[i] / (tf == 'm' ? 60 : 1), tf,
+ i == 2 ? ")\n" : "/");
+ }
+ len += sprintf(page + len,
+ "%s (output): %d / %d / %d bits/s (%s", tmpstr,
+ LOADAVG(0,ch->loadavg_size), LOADAVG(1, ch->loadavg_size),
+ LOADAVG(2, ch->loadavg_size), tmpstr);
+
+ len += sprintf(page + len, "Debug flags: ");
+ tmp = len; i = 0;
+ while (comx_debugflags[i].name) {
+ if (ch->debug_flags & comx_debugflags[i].value)
+ len += sprintf(page + len, "%s ",
+ comx_debugflags[i].name);
+ i++;
+ }
+ len += sprintf(page + len, "%s\n", tmp == len ? "none" : "");
+
+ len += sprintf(page + len, "RX errors: len: %lu, overrun: %lu, crc: %lu, "
+ "aborts: %lu\n buffer overrun: %lu, pbuffer overrun: %lu\n"
+ "TX errors: underrun: %lu\n",
+ ch->current_stats->rx_length_errors, ch->current_stats->rx_over_errors,
+ ch->current_stats->rx_crc_errors, ch->current_stats->rx_frame_errors,
+ ch->current_stats->rx_missed_errors, ch->current_stats->rx_fifo_errors,
+ ch->current_stats->tx_fifo_errors);
+
+ if (ch->LINE_statistics && (ch->init_status & LINE_OPEN)) {
+ len += ch->LINE_statistics(dev, page + len);
+ } else {
+ len += sprintf(page+len, "Line status: driver not initialized\n");
+ }
+ if (ch->HW_statistics && (ch->init_status & HW_OPEN)) {
+ len += ch->HW_statistics(dev, page + len);
+ } else {
+ len += sprintf(page+len, "Board status: driver not initialized\n");
+ }
+
+ return len;
+}
+
+static int comx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->LINE_ioctl) {
+ return(ch->LINE_ioctl(dev, ifr, cmd));
+ }
+ return -EINVAL;
+}
+
+static void comx_reset_dev(struct net_device *dev)
+{
+ dev->open = comx_open;
+ dev->stop = comx_close;
+ dev->hard_start_xmit = comx_xmit;
+ dev->hard_header = comx_header;
+ dev->rebuild_header = comx_rebuild_header;
+ dev->get_stats = comx_stats;
+ dev->do_ioctl = comx_ioctl;
+ dev->change_mtu = NULL;
+ dev->tx_queue_len = 20;
+ dev->flags = IFF_NOARP;
+}
+
+static int comx_init_dev(struct net_device *dev)
+{
+ struct comx_channel *ch;
+
+ if ((ch = kmalloc(sizeof(struct comx_channel), GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(ch, 0, sizeof(struct comx_channel));
+
+ ch->loadavg[0] = 5;
+ ch->loadavg[1] = 300;
+ ch->loadavg[2] = 900;
+ ch->loadavg_size = ch->loadavg[2] / ch->loadavg[0] + 1;
+ if ((ch->avg_bytes = kmalloc(ch->loadavg_size *
+ sizeof(unsigned long) * 2, GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ memset(ch->avg_bytes, 0, ch->loadavg_size * sizeof(unsigned long) * 2);
+ ch->loadavg_counter = 0;
+ ch->loadavg_timer.function = comx_loadavg_timerfun;
+ ch->loadavg_timer.data = (unsigned long)dev;
+ ch->loadavg_timer.expires = jiffies + HZ * ch->loadavg[0];
+ add_timer(&ch->loadavg_timer);
+
+ dev->priv = (void *)ch;
+ ch->dev = dev;
+ ch->line_status &= ~LINE_UP;
+
+ ch->current_stats = &ch->stats;
+
+ comx_reset_dev(dev);
+ return 0;
+}
+
+static int comx_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct comx_channel *ch=(struct comx_channel *)dev->priv;
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_STATUS) == 0) {
+ len = comx_statistics(dev, page);
+ } else if (strcmp(file->name, FILENAME_HARDWARE) == 0) {
+ len = sprintf(page, "%s\n", ch->hardware ?
+ ch->hardware->name : HWNAME_NONE);
+ } else if (strcmp(file->name, FILENAME_PROTOCOL) == 0) {
+ len = sprintf(page, "%s\n", ch->protocol ?
+ ch->protocol->name : PROTONAME_NONE);
+ } else if (strcmp(file->name, FILENAME_LINEUPDELAY) == 0) {
+ len = sprintf(page, "%01d\n", ch->lineup_delay);
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return( min(count, len - off) );
+}
+
+
+static int comx_root_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct comx_hardware *hw;
+ struct comx_protocol *line;
+
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_HARDWARELIST) == 0) {
+ for(hw=comx_channels;hw;hw=hw->next)
+ len+=sprintf(page+len, "%s\n", hw->name);
+ } else if (strcmp(file->name, FILENAME_PROTOCOLLIST) == 0) {
+ for(line=comx_lines;line;line=line->next)
+ len+=sprintf(page+len, "%s\n", line->name);
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return( min(count, len - off) );
+}
+
+
+
+static int comx_write_proc(struct file *file, const char *buffer, u_long count,
+ void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = (struct net_device *)entry->parent->data;
+ struct comx_channel *ch=(struct comx_channel *)dev->priv;
+ char *page;
+ struct comx_hardware *hw = comx_channels;
+ struct comx_protocol *line = comx_lines;
+ char str[30];
+ int ret=0;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comx_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (count > PAGE_SIZE) {
+ printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
+ return -ENOSPC;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
+
+ copy_from_user(page, buffer, count);
+
+ if (*(page + count - 1) == '\n') *(page + count - 1) = 0;
+
+ if (strcmp(entry->name, FILENAME_DEBUG) == 0) {
+ int i;
+ int ret = 0;
+
+ if ((i = simple_strtoul(page, NULL, 10)) != 0) {
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ if (ch->debug_area) kfree(ch->debug_area);
+ if ((ch->debug_area = kmalloc(ch->debug_size = i,
+ GFP_KERNEL)) == NULL) {
+ ret = -ENOMEM;
+ }
+ ch->debug_start = ch->debug_end = 0;
+ restore_flags(flags);
+ free_page((unsigned long)page);
+ return count;
+ }
+
+ if (*page != '+' && *page != '-') {
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ while (comx_debugflags[i].value &&
+ strncmp(comx_debugflags[i].name, page + 1,
+ strlen(comx_debugflags[i].name))) {
+ i++;
+ }
+
+ if (comx_debugflags[i].value == 0) {
+ printk(KERN_ERR "Invalid debug option\n");
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ if (*page == '+') {
+ ch->debug_flags |= comx_debugflags[i].value;
+ } else {
+ ch->debug_flags &= ~comx_debugflags[i].value;
+ }
+ } else if (strcmp(entry->name, FILENAME_HARDWARE) == 0) {
+ if(strlen(page)>10) {
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ while (hw) {
+ if (strcmp(hw->name, page) == 0) {
+ break;
+ } else {
+ hw = hw->next;
+ }
+ }
+#ifdef CONFIG_KMOD
+ if(!hw && comx_strcasecmp(HWNAME_NONE,page) != 0){
+ sprintf(str,"comx-hw-%s",page);
+ request_module(str);
+ }
+ hw=comx_channels;
+ while (hw) {
+ if (comx_strcasecmp(hw->name, page) == 0) {
+ break;
+ } else {
+ hw = hw->next;
+ }
+ }
+#endif
+
+ if (comx_strcasecmp(HWNAME_NONE, page) != 0 && !hw) {
+ free_page((unsigned long)page);
+ return -ENODEV;
+ }
+ if (ch->init_status & HW_OPEN) {
+ free_page((unsigned long)page);
+ return -EBUSY;
+ }
+ if (ch->hardware && ch->hardware->hw_exit &&
+ (ret=ch->hardware->hw_exit(dev))) {
+ free_page((unsigned long)page);
+ return ret;
+ }
+ ch->hardware = hw;
+ entry->size = strlen(page) + 1;
+ if (hw && hw->hw_init) hw->hw_init(dev);
+ } else if (strcmp(entry->name, FILENAME_PROTOCOL) == 0) {
+ if(strlen(page)>10) {
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ while (line) {
+ if (comx_strcasecmp(line->name, page) == 0) {
+ break;
+ } else {
+ line = line->next;
+ }
+ }
+#ifdef CONFIG_KMOD
+ if(!line && comx_strcasecmp(PROTONAME_NONE, page) != 0) {
+ sprintf(str,"comx-proto-%s",page);
+ request_module(str);
+ }
+ line=comx_lines;
+ while (line) {
+ if (comx_strcasecmp(line->name, page) == 0) {
+ break;
+ } else {
+ line = line->next;
+ }
+ }
+#endif
+
+ if (comx_strcasecmp(PROTONAME_NONE, page) != 0 && !line) {
+ free_page((unsigned long)page);
+ return -ENODEV;
+ }
+
+ if (ch->init_status & LINE_OPEN) {
+ free_page((unsigned long)page);
+ return -EBUSY;
+ }
+
+ if (ch->protocol && ch->protocol->line_exit &&
+ (ret=ch->protocol->line_exit(dev))) {
+ free_page((unsigned long)page);
+ return ret;
+ }
+ ch->protocol = line;
+ entry->size = strlen(page) + 1;
+ comx_reset_dev(dev);
+ if (line && line->line_init) line->line_init(dev);
+ } else if (strcmp(entry->name, FILENAME_LINEUPDELAY) == 0) {
+ int i;
+
+ if ((i = simple_strtoul(page, NULL, 10)) != 0) {
+ if (i >=0 && i < 10) {
+ ch->lineup_delay = i;
+ } else {
+ printk(KERN_ERR "comx: invalid lineup_delay value\n");
+ }
+ }
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static loff_t comx_debug_lseek(struct file *file, loff_t offset, int orig)
+{
+ switch(orig) {
+ case 0:
+ file->f_pos = max(0, min(offset,
+ file->f_dentry->d_inode->i_size));
+ return(file->f_pos);
+ case 1:
+ file->f_pos = max(0, min(offset + file->f_pos,
+ file->f_dentry->d_inode->i_size));
+ return(file->f_pos);
+ case 2:
+ file->f_pos = max(0,
+ min(offset + file->f_dentry->d_inode->i_size,
+ file->f_dentry->d_inode->i_size));
+ return(file->f_pos);
+ }
+ return(file->f_pos);
+}
+
+static int comx_file_open(struct inode *inode, struct file *file)
+{
+
+ if((file->f_mode & FMODE_WRITE) && !(inode->i_mode & 0200)) {
+ return -EACCES;
+ }
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int comx_file_release(struct inode *inode, struct file *file)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static ssize_t comx_debug_read(struct file *file, char *buffer, size_t count,
+ loff_t *ppos)
+{
+ struct proc_dir_entry *de = file->f_dentry->d_inode->u.generic_ip;
+ struct net_device *dev = de->parent->data;
+ struct comx_channel *ch = dev->priv;
+ loff_t copied = 0;
+ unsigned long flags;
+
+ save_flags(flags); cli(); // We may run into trouble when debug_area is filled
+ // from irq inside read. no problem if the buffer is
+ // large enough
+
+ while (count > 0 && ch->debug_start != ch->debug_end) {
+ int len;
+
+ len = min( (ch->debug_end - ch->debug_start + ch->debug_size)
+ %ch->debug_size, min (ch->debug_size -
+ ch->debug_start, count));
+
+ if (len) copy_to_user(buffer + copied,
+ ch->debug_area + ch->debug_start, len);
+ ch->debug_start = (ch->debug_start + len) % ch->debug_size;
+
+ de->size -= len;
+ count -= len;
+ copied += len;
+ }
+
+ restore_flags(flags);
+ return copied;
+}
+
+static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct proc_dir_entry *new_dir, *debug_file;
+ struct net_device *dev;
+ struct comx_channel *ch;
+
+ if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR;
+
+ if ((new_dir = create_proc_entry(dentry->d_name.name, mode | S_IFDIR,
+ &comx_root_dir)) == NULL) {
+ return -EIO;
+ }
+
+ new_dir->ops = &proc_dir_inode_operations; // ez egy normalis /proc konyvtar
+ new_dir->nlink = 2;
+ new_dir->data = NULL; // ide jon majd a struct dev
+
+ /* Ezek kellenek */
+ if (!create_comx_proc_entry(FILENAME_HARDWARE, 0644,
+ strlen(HWNAME_NONE) + 1, new_dir)) {
+ return -ENOMEM;
+ }
+ if (!create_comx_proc_entry(FILENAME_PROTOCOL, 0644,
+ strlen(PROTONAME_NONE) + 1, new_dir)) {
+ return -ENOMEM;
+ }
+ if (!create_comx_proc_entry(FILENAME_STATUS, 0444, 0, new_dir)) {
+ return -ENOMEM;
+ }
+ if (!create_comx_proc_entry(FILENAME_LINEUPDELAY, 0644, 2, new_dir)) {
+ return -ENOMEM;
+ }
+
+ if ((debug_file = create_proc_entry(FILENAME_DEBUG,
+ S_IFREG | 0644, new_dir)) == NULL) {
+ return -ENOMEM;
+ }
+ debug_file->ops = &comx_debug_inode_ops;
+ debug_file->data = (void *)debug_file;
+ debug_file->read_proc = NULL; // see below
+ debug_file->write_proc = &comx_write_proc;
+ debug_file->nlink = 1;
+
+ if ((dev = kmalloc(sizeof(struct net_device), GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(dev, 0, sizeof(struct net_device));
+ dev->name = (char *)new_dir->name;
+ dev->init = comx_init_dev;
+
+ if (register_netdevice(dev)) {
+ return -EIO;
+ }
+ ch=dev->priv;
+ if((ch->if_ptr = (void *)kmalloc(sizeof(struct ppp_device),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(ch->if_ptr, 0, sizeof(struct ppp_device));
+ ch->debug_file = debug_file;
+ ch->procdir = new_dir;
+ new_dir->data = dev;
+
+ ch->debug_start = ch->debug_end = 0;
+ if ((ch->debug_area = kmalloc(ch->debug_size = DEFAULT_DEBUG_SIZE,
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ ch->lineup_delay = DEFAULT_LINEUP_DELAY;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int comx_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct proc_dir_entry *entry = dentry->d_inode->u.generic_ip;
+ struct net_device *dev = entry->data;
+ struct comx_channel *ch = dev->priv;
+ int ret;
+
+ /* Egyelore miert ne ? */
+ if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR;
+
+ if (dev->flags & IFF_UP) {
+ printk(KERN_ERR "%s: down interface before removing it\n", dev->name);
+ return -EBUSY;
+ }
+
+ if (ch->protocol && ch->protocol->line_exit &&
+ (ret=ch->protocol->line_exit(dev))) {
+ return ret;
+ }
+ if (ch->hardware && ch->hardware->hw_exit &&
+ (ret=ch->hardware->hw_exit(dev))) {
+ if(ch->protocol && ch->protocol->line_init) {
+ ch->protocol->line_init(dev);
+ }
+ return ret;
+ }
+ ch->protocol = NULL;
+ ch->hardware = NULL;
+
+ del_timer(&ch->loadavg_timer);
+ kfree(ch->avg_bytes);
+
+ unregister_netdev(dev);
+ if (ch->debug_area) {
+ kfree(ch->debug_area);
+ }
+ if (dev->priv) {
+ kfree(dev->priv);
+ }
+ kfree(dev);
+
+ remove_proc_entry(FILENAME_DEBUG, entry);
+ remove_proc_entry(FILENAME_LINEUPDELAY, entry);
+ remove_proc_entry(FILENAME_STATUS, entry);
+ remove_proc_entry(FILENAME_HARDWARE, entry);
+ remove_proc_entry(FILENAME_PROTOCOL, entry);
+ remove_proc_entry(dentry->d_name.name, &comx_root_dir);
+// proc_unregister(&comx_root_dir, dentry->d_inode->i_ino);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct dentry *comx_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct proc_dir_entry *de;
+ struct inode *inode = NULL;
+
+ if (!dir || !S_ISDIR(dir->i_mode)) {
+ return ERR_PTR(-ENOTDIR);
+ }
+
+ if ((de = (struct proc_dir_entry *) dir->u.generic_ip) != NULL) {
+ for (de = de->subdir ; de ; de = de->next) {
+ if ((de && de->low_ino) &&
+ (de->namelen == dentry->d_name.len) &&
+ (memcmp(dentry->d_name.name, de->name,
+ de->namelen) == 0)) {
+ if ((inode = proc_get_inode(dir->i_sb,
+ de->low_ino, de)) == NULL) {
+ printk(KERN_ERR "COMX: lookup error\n");
+ return ERR_PTR(-EINVAL);
+ }
+ break;
+ }
+ }
+ }
+ dentry->d_op = &comx_dentry_operations;
+ d_add(dentry, inode);
+ return NULL;
+}
+
+int comx_strcasecmp(const char *cs, const char *ct)
+{
+ register signed char __res;
+
+ while (1) {
+ if ((__res = toupper(*cs) - toupper(*ct++)) != 0 || !*cs++) {
+ break;
+ }
+ }
+ return __res;
+}
+
+static void comx_delete_dentry(struct dentry *dentry)
+{
+ d_drop(dentry);
+}
+
+static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir)
+{
+ struct proc_dir_entry *new_file;
+
+ if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
+ new_file->ops = &comx_normal_inode_ops;
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comx_read_proc;
+ new_file->write_proc = &comx_write_proc;
+ new_file->size = size;
+ new_file->nlink = 1;
+ }
+ return(new_file);
+}
+
+int comx_register_hardware(struct comx_hardware *comx_hw)
+{
+ struct comx_hardware *hw = comx_channels;
+
+ if (!hw) {
+ comx_channels = comx_hw;
+ } else {
+ while (hw->next != NULL && strcmp(comx_hw->name, hw->name) != 0) {
+ hw = hw->next;
+ }
+ if (strcmp(comx_hw->name, hw->name) == 0) {
+ return -1;
+ }
+ hw->next = comx_hw;
+ }
+
+ printk(KERN_INFO "COMX: driver for hardware type %s, version %s\n", comx_hw->name, comx_hw->version);
+ return 0;
+}
+
+int comx_unregister_hardware(char *name)
+{
+ struct comx_hardware *hw = comx_channels;
+
+ if (!hw) {
+ return -1;
+ }
+
+ if (strcmp(hw->name, name) == 0) {
+ comx_channels = comx_channels->next;
+ return 0;
+ }
+
+ while (hw->next != NULL && strcmp(hw->next->name,name) != 0) {
+ hw = hw->next;
+ }
+
+ if (hw->next != NULL && strcmp(hw->next->name, name) == 0) {
+ hw->next = hw->next->next;
+ return 0;
+ }
+ return -1;
+}
+
+int comx_register_protocol(struct comx_protocol *comx_line)
+{
+ struct comx_protocol *pr = comx_lines;
+
+ if (!pr) {
+ comx_lines = comx_line;
+ } else {
+ while (pr->next != NULL && strcmp(comx_line->name, pr->name) !=0) {
+ pr = pr->next;
+ }
+ if (strcmp(comx_line->name, pr->name) == 0) {
+ return -1;
+ }
+ pr->next = comx_line;
+ }
+
+ printk(KERN_INFO "COMX: driver for protocol type %s, version %s\n", comx_line->name, comx_line->version);
+ return 0;
+}
+
+int comx_unregister_protocol(char *name)
+{
+ struct comx_protocol *pr = comx_lines;
+
+ if (!pr) {
+ return -1;
+ }
+
+ if (strcmp(pr->name, name) == 0) {
+ comx_lines = comx_lines->next;
+ return 0;
+ }
+
+ while (pr->next != NULL && strcmp(pr->next->name,name) != 0) {
+ pr = pr->next;
+ }
+
+ if (pr->next != NULL && strcmp(pr->next->name, name) == 0) {
+ pr->next = pr->next->next;
+ return 0;
+ }
+ return -1;
+}
+
+#ifdef MODULE
+#define comx_init init_module
+#endif
+
+__initfunc(int comx_init(void))
+{
+ struct proc_dir_entry *new_file;
+
+ memcpy(&comx_root_inode_ops, &proc_dir_inode_operations,
+ sizeof(struct inode_operations));
+ comx_root_inode_ops.lookup = &comx_lookup;
+ comx_root_inode_ops.mkdir = &comx_mkdir;
+ comx_root_inode_ops.rmdir = &comx_rmdir;
+
+ memcpy(&comx_normal_inode_ops, &proc_net_inode_operations,
+ sizeof(struct inode_operations));
+ comx_normal_inode_ops.default_file_ops = &comx_normal_file_ops;
+ comx_normal_inode_ops.lookup = &comx_lookup;
+
+ memcpy(&comx_debug_inode_ops, &comx_normal_inode_ops,
+ sizeof(struct inode_operations));
+ comx_debug_inode_ops.default_file_ops = &comx_debug_file_ops;
+
+ memcpy(&comx_normal_file_ops, proc_net_inode_operations.default_file_ops,
+ sizeof(struct file_operations));
+ comx_normal_file_ops.open = &comx_file_open;
+ comx_normal_file_ops.release = &comx_file_release;
+
+ memcpy(&comx_debug_file_ops, &comx_normal_file_ops,
+ sizeof(struct file_operations));
+ comx_debug_file_ops.llseek = &comx_debug_lseek;
+ comx_debug_file_ops.read = &comx_debug_read;
+
+ if (proc_register(&proc_root, &comx_root_dir) < 0) return -ENOMEM;
+
+
+ if ((new_file = create_proc_entry(FILENAME_HARDWARELIST,
+ S_IFREG | 0444, &comx_root_dir)) == NULL) {
+ return -ENOMEM;
+ }
+
+ new_file->ops = &comx_normal_inode_ops;
+ new_file->data = new_file;
+ new_file->read_proc = &comx_root_read_proc;
+ new_file->write_proc = NULL;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_PROTOCOLLIST,
+ S_IFREG | 0444, &comx_root_dir)) == NULL) {
+ return -ENOMEM;
+ }
+
+ new_file->ops = &comx_normal_inode_ops;
+ new_file->data = new_file;
+ new_file->read_proc = &comx_root_read_proc;
+ new_file->write_proc = NULL;
+ new_file->nlink = 1;
+
+
+ printk(KERN_INFO "COMX: driver version %s (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>\n",
+ VERSION);
+
+#ifndef MODULE
+#ifdef CONFIG_COMX_HW_COMX
+ comx_hw_comx_init();
+#endif
+#ifdef CONFIG_COMX_HW_LOCOMX
+ comx_hw_locomx_init();
+#endif
+#ifdef CONFIG_COMX_HW_MIXCOM
+ comx_hw_mixcom_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_HDLC
+ comx_proto_hdlc_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_PPP
+ comx_proto_ppp_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_LAPB
+ comx_proto_lapb_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_FR
+ comx_proto_fr_init();
+#endif
+#endif
+
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ remove_proc_entry(FILENAME_HARDWARELIST, &comx_root_dir);
+ remove_proc_entry(FILENAME_PROTOCOLLIST, &comx_root_dir);
+ proc_unregister(&proc_root, comx_root_dir.low_ino);
+}
+#endif
+
+EXPORT_SYMBOL(comx_register_hardware);
+EXPORT_SYMBOL(comx_unregister_hardware);
+EXPORT_SYMBOL(comx_register_protocol);
+EXPORT_SYMBOL(comx_unregister_protocol);
+EXPORT_SYMBOL(comx_debug_skb);
+EXPORT_SYMBOL(comx_debug_bytes);
+EXPORT_SYMBOL(comx_debug);
+EXPORT_SYMBOL(comx_lineup_func);
+EXPORT_SYMBOL(comx_status);
+EXPORT_SYMBOL(comx_rx);
+EXPORT_SYMBOL(comx_strcasecmp);
+EXPORT_SYMBOL(comx_normal_inode_ops);
+EXPORT_SYMBOL(comx_root_dir);
diff --git a/drivers/net/wan/comx.h b/drivers/net/wan/comx.h
new file mode 100644
index 000000000..e02849b90
--- /dev/null
+++ b/drivers/net/wan/comx.h
@@ -0,0 +1,240 @@
+/*
+ * General definitions for the COMX driver
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ *
+ * net_device_stats:
+ * rx_length_errors rec_len < 4 || rec_len > 2000
+ * rx_over_errors receive overrun (OVR)
+ * rx_crc_errors rx crc error
+ * rx_frame_errors aborts rec'd (ABO)
+ * rx_fifo_errors status fifo overrun (PBUFOVR)
+ * rx_missed_errors receive buffer overrun (BUFOVR)
+ * tx_aborted_errors ?
+ * tx_carrier_errors modem line status changes
+ * tx_fifo_errors tx underrun (locomx)
+ */
+#include <linux/config.h>
+
+struct comx_protocol {
+ char *name;
+ char *version;
+ unsigned short encap_type;
+ int (*line_init)(struct net_device *dev);
+ int (*line_exit)(struct net_device *dev);
+ struct comx_protocol *next;
+ };
+
+struct comx_hardware {
+ char *name;
+ char *version;
+ int (*hw_init)(struct net_device *dev);
+ int (*hw_exit)(struct net_device *dev);
+ int (*hw_dump)(struct net_device *dev);
+ struct comx_hardware *next;
+ };
+
+struct comx_channel {
+ void *if_ptr; // General purpose pointer
+ struct net_device *dev; // Where we belong to
+ struct net_device *twin; // On dual-port cards
+ struct proc_dir_entry *procdir; // the directory
+
+ unsigned char init_status;
+ unsigned char line_status;
+
+ struct timer_list lineup_timer; // against line jitter
+ int lineup_pending;
+ unsigned char lineup_delay;
+
+#if 0
+ struct timer_list reset_timer; // for board resetting
+ int reset_pending;
+ int reset_timeout;
+#endif
+
+ struct net_device_stats stats;
+ struct net_device_stats *current_stats;
+#if 0
+ unsigned long board_resets;
+#endif
+ unsigned long *avg_bytes;
+ int loadavg_counter, loadavg_size;
+ int loadavg[3];
+ struct timer_list loadavg_timer;
+ int debug_flags;
+ char *debug_area;
+ int debug_start, debug_end, debug_size;
+ struct proc_dir_entry *debug_file;
+#ifdef CONFIG_COMX_DEBUG_RAW
+ char *raw;
+ int raw_len;
+#endif
+ // LINE specific
+ struct comx_protocol *protocol;
+ void (*LINE_rx)(struct net_device *dev, struct sk_buff *skb);
+ int (*LINE_tx)(struct net_device *dev);
+ void (*LINE_status)(struct net_device *dev, u_short status);
+ int (*LINE_open)(struct net_device *dev);
+ int (*LINE_close)(struct net_device *dev);
+ int (*LINE_xmit)(struct sk_buff *skb, struct net_device *dev);
+ int (*LINE_header)(struct sk_buff *skb, struct net_device *dev,
+ u_short type,void *daddr, void *saddr,
+ unsigned len);
+ int (*LINE_rebuild_header)(struct sk_buff *skb);
+ int (*LINE_statistics)(struct net_device *dev, char *page);
+ int (*LINE_parameter_check)(struct net_device *dev);
+ int (*LINE_ioctl)(struct net_device *dev, struct ifreq *ifr,
+ int cmd);
+ void (*LINE_mod_use)(int);
+ void * LINE_privdata;
+
+ // HW specific
+
+ struct comx_hardware *hardware;
+ void (*HW_board_on)(struct net_device *dev);
+ void (*HW_board_off)(struct net_device *dev);
+ struct net_device *(*HW_access_board)(struct net_device *dev);
+ void (*HW_release_board)(struct net_device *dev, struct net_device *savep);
+ int (*HW_txe)(struct net_device *dev);
+ int (*HW_open)(struct net_device *dev);
+ int (*HW_close)(struct net_device *dev);
+ int (*HW_send_packet)(struct net_device *dev,struct sk_buff *skb);
+ int (*HW_statistics)(struct net_device *dev, char *page);
+#if 0
+ int (*HW_reset)(struct net_device *dev, char *page);
+#endif
+ int (*HW_load_board)(struct net_device *dev);
+ void (*HW_set_clock)(struct net_device *dev);
+ void *HW_privdata;
+ };
+
+struct comx_debugflags_struct {
+ char *name;
+ int value;
+ };
+
+#define COMX_ROOT_DIR_NAME "comx"
+
+#define FILENAME_HARDWARE "boardtype"
+#define FILENAME_HARDWARELIST "boardtypes"
+#define FILENAME_PROTOCOL "protocol"
+#define FILENAME_PROTOCOLLIST "protocols"
+#define FILENAME_DEBUG "debug"
+#define FILENAME_CLOCK "clock"
+#define FILENAME_STATUS "status"
+#define FILENAME_IO "io"
+#define FILENAME_IRQ "irq"
+#define FILENAME_KEEPALIVE "keepalive"
+#define FILENAME_LINEUPDELAY "lineup_delay"
+#define FILENAME_CHANNEL "channel"
+#define FILENAME_FIRMWARE "firmware"
+#define FILENAME_MEMADDR "memaddr"
+#define FILENAME_TWIN "twin"
+#define FILENAME_T1 "t1"
+#define FILENAME_T2 "t2"
+#define FILENAME_N2 "n2"
+#define FILENAME_WINDOW "window"
+#define FILENAME_MODE "mode"
+#define FILENAME_DLCI "dlci"
+#define FILENAME_MASTER "master"
+#ifdef CONFIG_COMX_DEBUG_RAW
+#define FILENAME_RAW "raw"
+#endif
+
+#define PROTONAME_NONE "none"
+#define HWNAME_NONE "none"
+#define KEEPALIVE_OFF "off"
+
+#define FRAME_ACCEPTED 0 /* sending and xmitter busy */
+#define FRAME_DROPPED 1
+#define FRAME_ERROR 2 /* xmitter error */
+#define FRAME_QUEUED 3 /* sending but more can come */
+
+#define LINE_UP 1 /* Modem UP */
+#define PROTO_UP 2
+#define PROTO_LOOP 4
+
+#define HW_OPEN 1
+#define LINE_OPEN 2
+#define FW_LOADED 4
+#define IRQ_ALLOCATED 8
+
+#define DEBUG_COMX_RX 2
+#define DEBUG_COMX_TX 4
+#define DEBUG_HW_TX 16
+#define DEBUG_HW_RX 32
+#define DEBUG_HDLC_KEEPALIVE 64
+#define DEBUG_COMX_PPP 128
+#define DEBUG_COMX_LAPB 256
+#define DEBUG_COMX_DLCI 512
+
+#define DEBUG_PAGESIZE 3072
+#define DEFAULT_DEBUG_SIZE 4096
+#define DEFAULT_LINEUP_DELAY 1
+#define FILE_PAGESIZE 3072
+
+#ifndef COMX_PPP_MAJOR
+#define COMX_PPP_MAJOR 88
+#endif
+
+
+#ifndef min
+#define min(a,b) ((a) > (b) ? (b) : (a))
+#endif
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+
+#define COMX_CHANNEL(dev) ((struct comx_channel*)dev->priv)
+
+#define TWIN(dev) (COMX_CHANNEL(dev)->twin)
+
+
+#ifndef byte
+typedef u8 byte;
+#endif
+#ifndef word
+typedef u16 word;
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+extern struct proc_dir_entry comx_root_dir;
+
+extern int comx_register_hardware(struct comx_hardware *comx_hw);
+extern int comx_unregister_hardware(char *name);
+extern int comx_register_protocol(struct comx_protocol *comx_line);
+extern int comx_unregister_protocol(char *name);
+
+extern int comx_rx(struct net_device *dev, struct sk_buff *skb);
+extern void comx_status(struct net_device *dev, int status);
+extern void comx_lineup_func(unsigned long d);
+
+extern int comx_debug(struct net_device *dev, char *fmt, ...);
+extern int comx_debug_skb(struct net_device *dev, struct sk_buff *skb, char *msg);
+extern int comx_debug_bytes(struct net_device *dev, unsigned char *bytes, int len,
+ char *msg);
+extern int comx_strcasecmp(const char *cs, const char *ct);
+
+extern struct inode_operations comx_normal_inode_ops;
diff --git a/drivers/net/wan/comxhw.h b/drivers/net/wan/comxhw.h
new file mode 100644
index 000000000..15230dc1f
--- /dev/null
+++ b/drivers/net/wan/comxhw.h
@@ -0,0 +1,113 @@
+/*
+ * Defines for comxhw.c
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Current maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#define LOCOMX_IO_EXTENT 8
+#define COMX_IO_EXTENT 4
+#define HICOMX_IO_EXTENT 16
+
+#define COMX_MAX_TX_SIZE 1600
+#define COMX_MAX_RX_SIZE 2048
+
+#define COMX_JAIL_OFFSET 0xffff
+#define COMX_JAIL_VALUE 0xfe
+#define COMX_MEMORY_SIZE 65536
+#define HICOMX_MEMORY_SIZE 16384
+#define COMX_MEM_MIN 0xa0000
+#define COMX_MEM_MAX 0xf0000
+
+#define COMX_DEFAULT_IO 0x360
+#define COMX_DEFAULT_IRQ 10
+#define COMX_DEFAULT_MEMADDR 0xd0000
+#define HICOMX_DEFAULT_IO 0x320
+#define HICOMX_DEFAULT_IRQ 10
+#define HICOMX_DEFAULT_MEMADDR 0xd0000
+#define LOCOMX_DEFAULT_IO 0x368
+#define LOCOMX_DEFAULT_IRQ 7
+
+#define MAX_CHANNELNO 2
+
+#define COMX_CHANNEL_OFFSET 0x2000
+
+#define COMX_ENABLE_BOARD_IT 0x40
+#define COMX_BOARD_RESET 0x20
+#define COMX_ENABLE_BOARD_MEM 0x10
+#define COMX_DISABLE_BOARD_MEM 0
+#define COMX_DISABLE_ALL 0x00
+
+#define HICOMX_DISABLE_ALL 0x00
+#define HICOMX_ENABLE_BOARD_MEM 0x02
+#define HICOMX_DISABLE_BOARD_MEM 0x0
+#define HICOMX_BOARD_RESET 0x01
+#define HICOMX_PRG_MEM 4
+#define HICOMX_DATA_MEM 0
+#define HICOMX_ID_BYTE 0x55
+
+#define CMX_ID_BYTE 0x31
+#define COMX_CLOCK_CONST 8000
+
+#define LINKUP_READY 3
+
+#define OFF_FW_L1_ID 0x01e /* ID bytes */
+#define OFF_FW_L2_ID 0x1006
+#define FW_L1_ID_1 0xab
+#define FW_L1_ID_2_COMX 0xc0
+#define FW_L1_ID_2_HICOMX 0xc1
+#define FW_L2_ID_1 0xab
+
+#define OFF_A_L2_CMD 0x130 /* command register for L2 */
+#define OFF_A_L2_CMDPAR 0x131 /* command parameter byte */
+#define OFF_A_L1_STATB 0x122 /* stat. block for L1 */
+#define OFF_A_L1_ABOREC 0x122 /* receive ABORT counter */
+#define OFF_A_L1_OVERRUN 0x123 /* receive overrun counter */
+#define OFF_A_L1_CRCREC 0x124 /* CRC error counter */
+#define OFF_A_L1_BUFFOVR 0x125 /* buffer overrun counter */
+#define OFF_A_L1_PBUFOVR 0x126 /* priority buffer overrun counter */
+#define OFF_A_L1_MODSTAT 0x127 /* current state of modem ctrl lines */
+#define OFF_A_L1_STATE 0x127 /* end of stat. block for L1 */
+#define OFF_A_L1_TXPC 0x128 /* Tx counter for the PC */
+#define OFF_A_L1_TXZ80 0x129 /* Tx counter for the Z80 */
+#define OFF_A_L1_RXPC 0x12a /* Rx counter for the PC */
+#define OFF_A_L1_RXZ80 0x12b /* Rx counter for the Z80 */
+#define OFF_A_L1_REPENA 0x12c /* IT rep disable */
+#define OFF_A_L1_CHNR 0x12d /* L1 channel logical number */
+#define OFF_A_L1_CLKINI 0x12e /* Timer Const */
+#define OFF_A_L2_LINKUP 0x132 /* Linkup byte */
+#define OFF_A_L2_DAV 0x134 /* Rx DAV */
+#define OFF_A_L2_RxBUFP 0x136 /* Rx buff relative to membase */
+#define OFF_A_L2_TxEMPTY 0x138 /* Tx Empty */
+#define OFF_A_L2_TxBUFP 0x13a /* Tx Buf */
+#define OFF_A_L2_NBUFFS 0x144 /* Number of buffers to fetch */
+
+#define OFF_A_L2_SABMREC 0x164 /* LAPB no. of SABMs received */
+#define OFF_A_L2_SABMSENT 0x165 /* LAPB no. of SABMs sent */
+#define OFF_A_L2_REJREC 0x166 /* LAPB no. of REJs received */
+#define OFF_A_L2_REJSENT 0x167 /* LAPB no. of REJs sent */
+#define OFF_A_L2_FRMRREC 0x168 /* LAPB no. of FRMRs received */
+#define OFF_A_L2_FRMRSENT 0x169 /* LAPB no. of FRMRs sent */
+#define OFF_A_L2_PROTERR 0x16A /* LAPB no. of protocol errors rec'd */
+#define OFF_A_L2_LONGREC 0x16B /* LAPB no. of long frames */
+#define OFF_A_L2_INVNR 0x16C /* LAPB no. of invalid N(R)s rec'd */
+#define OFF_A_L2_UNDEFFR 0x16D /* LAPB no. of invalid frames */
+
+#define OFF_A_L2_T1 0x174 /* T1 timer */
+#define OFF_A_L2_ADDR 0x176 /* DCE = 1, DTE = 3 */
+
+#define COMX_CMD_INIT 1
+#define COMX_CMD_EXIT 2
+#define COMX_CMD_OPEN 16
+#define COMX_CMD_CLOSE 17
+
diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c
index 633514166..544fcb8dd 100644
--- a/drivers/net/wan/cosa.c
+++ b/drivers/net/wan/cosa.c
@@ -1150,19 +1150,19 @@ static int cosa_ioctl_common(struct cosa_data *cosa,
{
switch(cmd) {
case COSAIORSET: /* Reset the device */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EACCES;
return cosa_reset(cosa);
case COSAIOSTRT: /* Start the firmware */
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
return cosa_start(cosa, arg);
case COSAIODOWNLD: /* Download the firmware */
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
return cosa_download(cosa, (struct cosa_download *)arg);
case COSAIORMEM:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
return cosa_readmem(cosa, (struct cosa_download *)arg);
case COSAIORTYPE:
@@ -1189,7 +1189,7 @@ static int cosa_ioctl_common(struct cosa_data *cosa,
case COSAIONRCHANS:
return cosa->nchannels;
case COSAIOBMSET:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
if (is_8bit(cosa))
return -EINVAL;
diff --git a/drivers/net/wan/hscx.h b/drivers/net/wan/hscx.h
new file mode 100644
index 000000000..675b7b1f1
--- /dev/null
+++ b/drivers/net/wan/hscx.h
@@ -0,0 +1,103 @@
+#define HSCX_MTU 1600
+
+#define HSCX_ISTA 0x00
+#define HSCX_MASK 0x00
+#define HSCX_STAR 0x01
+#define HSCX_CMDR 0x01
+#define HSCX_MODE 0x02
+#define HSCX_TIMR 0x03
+#define HSCX_EXIR 0x04
+#define HSCX_XAD1 0x04
+#define HSCX_RBCL 0x05
+#define HSCX_SAD2 0x05
+#define HSCX_RAH1 0x06
+#define HSCX_RSTA 0x07
+#define HSCX_RAH2 0x07
+#define HSCX_RAL1 0x08
+#define HSCX_RCHR 0x09
+#define HSCX_RAL2 0x09
+#define HSCX_XBCL 0x0a
+#define HSCX_BGR 0x0b
+#define HSCX_CCR2 0x0c
+#define HSCX_RBCH 0x0d
+#define HSCX_XBCH 0x0d
+#define HSCX_VSTR 0x0e
+#define HSCX_RLCR 0x0e
+#define HSCX_CCR1 0x0f
+#define HSCX_FIFO 0x1e
+
+#define HSCX_HSCX_CHOFFS 0x400
+#define HSCX_SEROFFS 0x1000
+
+#define HSCX_RME 0x80
+#define HSCX_RPF 0x40
+#define HSCX_RSC 0x20
+#define HSCX_XPR 0x10
+#define HSCX_TIN 0x08
+#define HSCX_ICA 0x04
+#define HSCX_EXA 0x02
+#define HSCX_EXB 0x01
+
+#define HSCX_XMR 0x80
+#define HSCX_XDU 0x40
+#define HSCX_EXE 0x40
+#define HSCX_PCE 0x20
+#define HSCX_RFO 0x10
+#define HSCX_CSC 0x08
+#define HSCX_RFS 0x04
+
+#define HSCX_XDOV 0x80
+#define HSCX_XFW 0x40
+#define HSCX_XRNR 0x20
+#define HSCX_RRNR 0x10
+#define HSCX_RLI 0x08
+#define HSCX_CEC 0x04
+#define HSCX_CTS 0x02
+#define HSCX_WFA 0x01
+
+#define HSCX_RMC 0x80
+#define HSCX_RHR 0x40
+#define HSCX_RNR 0x20
+#define HSCX_XREP 0x20
+#define HSCX_STI 0x10
+#define HSCX_XTF 0x08
+#define HSCX_XIF 0x04
+#define HSCX_XME 0x02
+#define HSCX_XRES 0x01
+
+#define HSCX_AUTO 0x00
+#define HSCX_NONAUTO 0x40
+#define HSCX_TRANS 0x80
+#define HSCX_XTRANS 0xc0
+#define HSCX_ADM16 0x20
+#define HSCX_ADM8 0x00
+#define HSCX_TMD_EXT 0x00
+#define HSCX_TMD_INT 0x10
+#define HSCX_RAC 0x08
+#define HSCX_RTS 0x04
+#define HSCX_TLP 0x01
+
+#define HSCX_VFR 0x80
+#define HSCX_RDO 0x40
+#define HSCX_CRC 0x20
+#define HSCX_RAB 0x10
+
+#define HSCX_CIE 0x04
+#define HSCX_RIE 0x02
+
+#define HSCX_DMA 0x80
+#define HSCX_NRM 0x40
+#define HSCX_CAS 0x20
+#define HSCX_XC 0x10
+
+#define HSCX_OV 0x10
+
+#define HSCX_CD 0x80
+
+#define HSCX_RC 0x80
+
+#define HSCX_PU 0x80
+#define HSCX_NRZ 0x00
+#define HSCX_NRZI 0x40
+#define HSCX_ODS 0x10
+#define HSCX_ITF 0x08
diff --git a/drivers/net/wan/mixcom.h b/drivers/net/wan/mixcom.h
new file mode 100644
index 000000000..1815eef75
--- /dev/null
+++ b/drivers/net/wan/mixcom.h
@@ -0,0 +1,35 @@
+/*
+ * Defines for the mixcom board
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#define MIXCOM_IO_EXTENT 0x20
+
+#define MIXCOM_DEFAULT_IO 0x180
+#define MIXCOM_DEFAULT_IRQ 5
+
+#define MIXCOM_ID 0x11
+#define MIXCOM_SERIAL_OFFSET 0x1000
+#define MIXCOM_CHANNEL_OFFSET 0x400
+#define MIXCOM_IT_OFFSET 0xc14
+#define MIXCOM_STATUS_OFFSET 0xc14
+#define MIXCOM_ID_OFFSET 0xc10
+#define MIXCOM_ON 0x1
+#define MIXCOM_OFF 0x0
+
+/* Status register bits */
+
+#define MIXCOM_CTSB 0x1
+#define MIXCOM_CTSA 0x2
+#define MIXCOM_CHANNELNO 0x20
+#define MIXCOM_POWERFAIL 0x40
+#define MIXCOM_BOOT 0x80
diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c
index b161fbabc..be665f0ae 100644
--- a/drivers/net/wan/sbni.c
+++ b/drivers/net/wan/sbni.c
@@ -101,7 +101,7 @@ static void sbni_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static int sbni_close(struct net_device *dev);
static void sbni_drop_tx_queue(struct net_device *dev);
static struct enet_statistics *sbni_get_stats(struct net_device *dev);
-void card_start(struct net_device *dev);
+static void card_start(struct net_device *dev);
static inline unsigned short sbni_recv(struct net_device *dev);
void change_level(struct net_device *dev);
static inline void sbni_xmit(struct net_device *dev);
@@ -647,7 +647,7 @@ static int sbni_start_xmit(struct sk_buff *skb, struct net_device *dev)
return 0;
}
-void card_start(struct net_device *dev)
+static void card_start(struct net_device *dev)
{
struct net_local *lp = (struct net_local*)dev->priv;
@@ -1200,6 +1200,8 @@ static int sbni_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
case SIOCDEVRESINSTATS:
{
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
DP( printk("%s: SIOCDEVRESINSTATS\n",dev->name); )
lp->in_stats.all_rx_number = 0;
lp->in_stats.bad_rx_number = 0;
diff --git a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c
index e7875649e..0454118a8 100644
--- a/drivers/net/wan/sdla.c
+++ b/drivers/net/wan/sdla.c
@@ -1247,7 +1247,7 @@ static int sdla_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct frad_local *flp;
- if(!suser())
+ if(!capable(CAP_NET_ADMIN))
return -EPERM;
flp = dev->priv;
diff --git a/drivers/net/wan/syncppp.c b/drivers/net/wan/syncppp.c
index e039bbc28..5b8504616 100644
--- a/drivers/net/wan/syncppp.c
+++ b/drivers/net/wan/syncppp.c
@@ -147,13 +147,17 @@ static void sppp_print_bytes (u8 *p, u16 len);
static int debug = 0;
+MODULE_PARM(debug,"1i");
+
/*
* Interface down stub
*/
static void if_down(struct net_device *dev)
{
- ;
+ struct sppp *sp = &((struct ppp_device *)dev)->sppp;
+
+ sp->pp_link_state=SPPP_LINK_DOWN;
}
/*
@@ -182,8 +186,19 @@ static void sppp_clear_timeout(struct sppp *p)
}
}
-/*
- * Process the received packet.
+/**
+ * sppp_input - receive and process a WAN PPP frame
+ * @skb: The buffer to process
+ * @dev: The device it arrived on
+ *
+ * This can be called directly by cards that do not have
+ * timing constraints but is normally called from the network layer
+ * after interrupt servicing to process frames queued via netif_rx.
+ *
+ * We process the options in the card. If the frame is destined for
+ * the protocol stacks then it requeues the frame for the upper level
+ * protocol. If it is a control from it is processed and discarded
+ * here.
*/
void sppp_input (struct net_device *dev, struct sk_buff *skb)
@@ -194,7 +209,7 @@ void sppp_input (struct net_device *dev, struct sk_buff *skb)
skb->dev=dev;
skb->mac.raw=skb->data;
- if (dev->flags & IFF_UP)
+ if (dev->flags & IFF_RUNNING)
{
/* Count received bytes, add FCS and one flag */
sp->ibytes+= skb->len + 3;
@@ -324,7 +339,7 @@ static int sppp_hard_header(struct sk_buff *skb, struct net_device *dev, __u16 t
h=(struct ppp_header *)skb->data;
if(sp->pp_flags&PP_CISCO)
{
- h->address = CISCO_MULTICAST;
+ h->address = CISCO_UNICAST;
h->control = 0;
}
else
@@ -370,7 +385,7 @@ static void sppp_keepalive (unsigned long dummy)
/* Keepalive mode disabled or channel down? */
if (! (sp->pp_flags & PP_KEEPALIVE) ||
- ! (dev->flags & IFF_RUNNING))
+ ! (dev->flags & IFF_UP))
continue;
/* No keepalive in PPP mode if LCP not opened yet. */
@@ -530,10 +545,10 @@ badreq:
if (h->ident != sp->lcp.confid)
break;
sppp_clear_timeout (sp);
- if (! (dev->flags & IFF_UP) &&
- (dev->flags & IFF_RUNNING)) {
+ if ((sp->pp_link_state != SPPP_LINK_UP) &&
+ (dev->flags & IFF_UP)) {
/* Coming out of loopback mode. */
- dev->flags |= IFF_UP;
+ sp->pp_link_state=SPPP_LINK_UP;
printk (KERN_INFO "%s: up\n", dev->name);
}
switch (sp->lcp.state) {
@@ -698,9 +713,9 @@ static void sppp_cisco_input (struct sppp *sp, struct sk_buff *skb)
break;
}
sp->pp_loopcnt = 0;
- if (! (dev->flags & IFF_UP) &&
- (dev->flags & IFF_RUNNING)) {
- dev->flags |= IFF_UP;
+ if (sp->pp_link_state==SPPP_LINK_DOWN &&
+ (dev->flags & IFF_UP)) {
+ sp->pp_link_state=SPPP_LINK_UP;
printk (KERN_INFO "%s: up\n", dev->name);
}
break;
@@ -825,11 +840,19 @@ static void sppp_cisco_send (struct sppp *sp, int type, long par1, long par2)
dev_queue_xmit(skb);
}
+/**
+ * sppp_close - close down a synchronous PPP or Cisco HDLC link
+ * @dev: The network device to drop the link of
+ *
+ * This drops the logical interface to the channel. It is not
+ * done politely as we assume we will also be dropping DTR. Any
+ * timeouts are killed.
+ */
int sppp_close (struct net_device *dev)
{
struct sppp *sp = (struct sppp *)sppp_of(dev);
- dev->flags &= ~IFF_RUNNING;
+ sp->pp_link_state = SPPP_LINK_DOWN;
sp->lcp.state = LCP_STATE_CLOSED;
sp->ipcp.state = IPCP_STATE_CLOSED;
sppp_clear_timeout (sp);
@@ -838,24 +861,49 @@ int sppp_close (struct net_device *dev)
EXPORT_SYMBOL(sppp_close);
+/**
+ * sppp_open - open a synchronous PPP or Cisco HDLC link
+ * @dev: Network device to activate
+ *
+ * Close down any existing synchronous session and commence
+ * from scratch. In the PPP case this means negotiating LCP/IPCP
+ * and friends, while for Cisco HDLC we simply need to staet sending
+ * keepalives
+ */
int sppp_open (struct net_device *dev)
{
struct sppp *sp = (struct sppp *)sppp_of(dev);
sppp_close(dev);
- dev->flags |= IFF_RUNNING;
- if (!(sp->pp_flags & PP_CISCO))
+ if (!(sp->pp_flags & PP_CISCO)) {
sppp_lcp_open (sp);
+ }
+ sp->pp_link_state = SPPP_LINK_DOWN;
return 0;
}
EXPORT_SYMBOL(sppp_open);
+/**
+ * sppp_reopen - notify of physical link loss
+ * @dev: Device that lost the link
+ *
+ * This function informs the synchronous protocol code that
+ * the underlying link died (for example a carrier drop on X.21)
+ *
+ * We increment the magic numbers to ensure that if the other end
+ * failed to notice we will correctly start a new session. It happens
+ * do to the nature of telco circuits is that you can lose carrier on
+ * one endonly.
+ *
+ * Having done this we go back to negotiating. This function may
+ * be called from an interrupt context.
+ */
+
int sppp_reopen (struct net_device *dev)
{
struct sppp *sp = (struct sppp *)sppp_of(dev);
sppp_close(dev);
- dev->flags |= IFF_RUNNING;
if (!(sp->pp_flags & PP_CISCO))
{
sp->lcp.magic = jiffies;
@@ -864,12 +912,23 @@ int sppp_reopen (struct net_device *dev)
sp->ipcp.state = IPCP_STATE_CLOSED;
/* Give it a moment for the line to settle then go */
sppp_set_timeout (sp, 1);
- }
+ }
+ sp->pp_link_state=SPPP_LINK_DOWN;
return 0;
}
EXPORT_SYMBOL(sppp_reopen);
+/**
+ * sppp_change_mtu - Change the link MTU
+ * @dev: Device to change MTU on
+ * @new_mtu: New MTU
+ *
+ * Change the MTU on the link. This can only be called with
+ * the link down. It returns an error if the link is up or
+ * the mtu is out of range.
+ */
+
int sppp_change_mtu(struct net_device *dev, int new_mtu)
{
if(new_mtu<128||new_mtu>PPP_MTU||(dev->flags&IFF_UP))
@@ -880,6 +939,18 @@ int sppp_change_mtu(struct net_device *dev, int new_mtu)
EXPORT_SYMBOL(sppp_change_mtu);
+/**
+ * sppp_do_ioctl - Ioctl handler for ppp/hdlc
+ * @dev: Device subject to ioctl
+ * @ifr: Interface request block from the user
+ * @cmd: Command that is being issued
+ *
+ * This function handles the ioctls that may be issued by the user
+ * to control the settings of a PPP/HDLC link. It does both busy
+ * and security checks. This function is intended to be wrapped by
+ * callers who wish to add additional ioctl calls of their own.
+ */
+
int sppp_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct sppp *sp = (struct sppp *)sppp_of(dev);
@@ -913,6 +984,16 @@ int sppp_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
EXPORT_SYMBOL(sppp_do_ioctl);
+/**
+ * sppp_attach - attach synchronous PPP/HDLC to a device
+ * @pd: PPP device to initialise
+ *
+ * This initialises the PPP/HDLC support on an interface. At the
+ * time of calling the dev element must point to the network device
+ * that this interface is attached to. The interface should not yet
+ * be registered.
+ */
+
void sppp_attach(struct ppp_device *pd)
{
struct net_device *dev = pd->dev;
@@ -973,6 +1054,15 @@ void sppp_attach(struct ppp_device *pd)
EXPORT_SYMBOL(sppp_attach);
+/**
+ * sppp_detach - release PPP resources from a device
+ * @dev: Network device to release
+ *
+ * Stop and free up any PPP/HDLC resources used by this
+ * interface. This must be called before the device is
+ * freed.
+ */
+
void sppp_detach (struct net_device *dev)
{
struct sppp **q, *p, *sp = (struct sppp *)sppp_of(dev);
@@ -1187,7 +1277,7 @@ static void sppp_cp_timeout (unsigned long arg)
cli();
sp->pp_flags &= ~PP_TIMO;
- if (! (sp->pp_if->flags & IFF_RUNNING) || (sp->pp_flags & PP_CISCO)) {
+ if (! (sp->pp_if->flags & IFF_UP) || (sp->pp_flags & PP_CISCO)) {
restore_flags(flags);
return;
}
@@ -1273,18 +1363,24 @@ static void sppp_print_bytes (u_char *p, u16 len)
printk ("-%x", *p++);
}
-/*
+/**
+ * sppp_rcv - receive and process a WAN PPP frame
+ * @skb: The buffer to process
+ * @dev: The device it arrived on
+ * @p: Unused
+ *
* Protocol glue. This drives the deferred processing mode the poorer
- * cards use.
+ * cards use. This can be called directly by cards that do not have
+ * timing constraints but is normally called from the network layer
+ * after interrupt servicing to process frames queued via netif_rx.
*/
-int sppp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p)
+static int sppp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p)
{
sppp_input(dev,skb);
return 0;
}
-EXPORT_SYMBOL(sppp_rcv);
struct packet_type sppp_packet_type=
{
@@ -1305,8 +1401,6 @@ void sync_ppp_init(void)
dev_add_pack(&sppp_packet_type);
}
-EXPORT_SYMBOL(sync_ppp_init);
-
#ifdef MODULE
int init_module(void)
diff --git a/drivers/net/wan/syncppp.h b/drivers/net/wan/syncppp.h
index 1e2056869..8f6ed5c1f 100644
--- a/drivers/net/wan/syncppp.h
+++ b/drivers/net/wan/syncppp.h
@@ -47,6 +47,7 @@ struct sppp
u32 ipkts,opkts; /* Packets in/out */
struct timer_list pp_timer;
struct net_device *pp_if;
+ char pp_link_state; /* Link status */
};
struct ppp_device
@@ -75,6 +76,9 @@ struct ppp_device
#define IPCP_STATE_ACK_SENT 2 /* IPCP state: conf-ack sent */
#define IPCP_STATE_OPENED 3 /* IPCP state: opened */
+#define SPPP_LINK_DOWN 0 /* link down - no keepalive */
+#define SPPP_LINK_UP 1 /* link is up - keepalive ok */
+
void sppp_attach (struct ppp_device *pd);
void sppp_detach (struct net_device *dev);
void sppp_input (struct net_device *dev, struct sk_buff *m);
diff --git a/drivers/net/wan/z85230.c b/drivers/net/wan/z85230.c
index 2ff818b04..f1c618a90 100644
--- a/drivers/net/wan/z85230.c
+++ b/drivers/net/wan/z85230.c
@@ -55,7 +55,7 @@
static spinlock_t z8530_buffer_lock = SPIN_LOCK_UNLOCKED;
/**
- * z8530_read_port:
+ * z8530_read_port - Architecture specific interface function
* @p: port to read
*
* Provided port access methods. The Comtrol SV11 requires no delays
@@ -79,7 +79,7 @@ extern __inline__ int z8530_read_port(unsigned long p)
}
/**
- * z8530_write_port:
+ * z8530_write_port - Architecture specific interface function
* @p: port to write
* @d: value to write
*
@@ -108,7 +108,7 @@ static void z8530_tx_done(struct z8530_channel *c);
/**
- * read_zsreg:
+ * read_zsreg - Read a register from a Z85230
* @c: Z8530 channel to read from (2 per chip)
* @reg: Register to read
* FIXME: Use a spinlock.
@@ -133,7 +133,7 @@ extern inline u8 read_zsreg(struct z8530_channel *c, u8 reg)
}
/**
- * read_zsdata:
+ * read_zsdata - Read the data port of a Z8530 channel
* @c: The Z8530 channel to read the data port from
*
* The data port provides fast access to some things. We still
@@ -148,7 +148,7 @@ extern inline u8 read_zsdata(struct z8530_channel *c)
}
/**
- * write_zsreg:
+ * write_zsreg - Write to a Z8530 channel register
* @c: The Z8530 channel
* @reg: Register number
* @val: Value to write
@@ -169,11 +169,28 @@ extern inline void write_zsreg(struct z8530_channel *c, u8 reg, u8 val)
restore_flags(flags);
}
+/**
+ * write_zsctrl - Write to a Z8530 control register
+ * @c: The Z8530 channel
+ * @val: Value to write
+ *
+ * Write directly to the control register on the Z8530
+ */
+
extern inline void write_zsctrl(struct z8530_channel *c, u8 val)
{
z8530_write_port(c->ctrlio, val);
}
+/**
+ * write_zsdata - Write to a Z8530 control register
+ * @c: The Z8530 channel
+ * @val: Value to write
+ *
+ * Write directly to the data register on the Z8530
+ */
+
+
extern inline void write_zsdata(struct z8530_channel *c, u8 val)
{
z8530_write_port(c->dataio, val);
@@ -249,7 +266,7 @@ u8 z8530_hdlc_kilostream_85230[]=
EXPORT_SYMBOL(z8530_hdlc_kilostream_85230);
/**
- * z8530_flush_fifo:
+ * z8530_flush_fifo - Flush on chip RX FIFO
* @c: Channel to flush
*
* Flush the receive FIFO. There is no specific option for this, we
@@ -276,8 +293,8 @@ static void z8530_flush_fifo(struct z8530_channel *c)
}
/**
- * z8530_rtsdtr:
- * @c: The Z8530 channel to contro;
+ * z8530_rtsdtr - Control the outgoing DTS/RTS line
+ * @c: The Z8530 channel to control;
* @set: 1 to set, 0 to clear
*
* Sets or clears DTR/RTS on the requested line. All locking is handled
@@ -296,7 +313,7 @@ static void z8530_rtsdtr(struct z8530_channel *c, int set)
}
/**
- * z8530_rx:
+ * z8530_rx - Handle a PIO receive event
* @c: Z8530 channel to process
*
* Receive handler for receiving in PIO mode. This is much like the
@@ -378,7 +395,7 @@ static void z8530_rx(struct z8530_channel *c)
/**
- * z8530_tx:
+ * z8530_tx - Handle a PIO transmit event
* @c: Z8530 channel to process
*
* Z8530 transmit interrupt handler for the PIO mode. The basic
@@ -421,7 +438,7 @@ static void z8530_tx(struct z8530_channel *c)
}
/**
- * z8530_status:
+ * z8530_status - Handle a PIO status exception
* @chan: Z8530 channel to process
*
* A status event occured in PIO synchronous mode. There are several
@@ -479,7 +496,7 @@ struct z8530_irqhandler z8530_sync=
EXPORT_SYMBOL(z8530_sync);
/**
- * z8530_dma_rx:
+ * z8530_dma_rx - Handle a DMA RX event
* @chan: Channel to handle
*
* Non bus mastering DMA interfaces for the Z8x30 devices. This
@@ -514,7 +531,7 @@ static void z8530_dma_rx(struct z8530_channel *chan)
}
/**
- * z8530_dma_tx:
+ * z8530_dma_tx - Handle a DMA TX event
* @chan: The Z8530 channel to handle
*
* We have received an interrupt while doing DMA transmissions. It
@@ -525,17 +542,17 @@ static void z8530_dma_tx(struct z8530_channel *chan)
{
if(!chan->dma_tx)
{
- printk("Hey who turned the DMA off?\n");
+ printk(KERN_WARNING "Hey who turned the DMA off?\n");
z8530_tx(chan);
return;
}
/* This shouldnt occur in DMA mode */
- printk(KERN_ERR "DMA tx ??\n");
+ printk(KERN_ERR "DMA tx - bogus event!\n");
z8530_tx(chan);
}
/**
- * z8530_dma_status:
+ * z8530_dma_status - Handle a DMA status exception
* @chan: Z8530 channel to process
*
* A status event occured on the Z8530. We receive these for two reasons
@@ -606,7 +623,7 @@ struct z8530_irqhandler z8530_txdma_sync=
EXPORT_SYMBOL(z8530_txdma_sync);
/**
- * z8530_rx_clear:
+ * z8530_rx_clear - Handle RX events from a stopped chip
* @c: Z8530 channel to shut up
*
* Receive interrupt vectors for a Z8530 that is in 'parked' mode.
@@ -635,7 +652,7 @@ static void z8530_rx_clear(struct z8530_channel *c)
}
/**
- * z8530_tx_clear:
+ * z8530_tx_clear - Handle TX events from a stopped chip
* @c: Z8530 channel to shut up
*
* Transmit interrupt vectors for a Z8530 that is in 'parked' mode.
@@ -650,7 +667,7 @@ static void z8530_tx_clear(struct z8530_channel *c)
}
/**
- * z8530_status_clear:
+ * z8530_status_clear - Handle status events from a stopped chip
* @chan: Z8530 channel to shut up
*
* Status interrupt vectors for a Z8530 that is in 'parked' mode.
@@ -678,7 +695,7 @@ struct z8530_irqhandler z8530_nop=
EXPORT_SYMBOL(z8530_nop);
/**
- * z8530_interrupt:
+ * z8530_interrupt - Handle an interrupt from a Z8530
* @irq: Interrupt number
* @dev_id: The Z8530 device that is interrupting.
* @regs: unused
@@ -758,7 +775,7 @@ static char reg_init[16]=
/**
- * z8530_sync_open:
+ * z8530_sync_open - Open a Z8530 channel for PIO
* @dev: The network interface we are using
* @c: The Z8530 channel to open in synchronous PIO mode
*
@@ -789,7 +806,7 @@ int z8530_sync_open(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_open);
/**
- * z8530_sync_close:
+ * z8530_sync_close - Close a PIO Z8530 channel
* @dev: Network device to close
* @c: Z8530 channel to disassociate and move to idle
*
@@ -814,7 +831,7 @@ int z8530_sync_close(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_close);
/**
- * z8530_sync_dma_open:
+ * z8530_sync_dma_open - Open a Z8530 for DMA I/O
* @dev: The network device to attach
* @c: The Z8530 channel to configure in sync DMA mode.
*
@@ -934,7 +951,7 @@ int z8530_sync_dma_open(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_dma_open);
/**
- * z8530_sync_dma_close:
+ * z8530_sync_dma_close - Close down DMA I/O
* @dev: Network device to detach
* @c: Z8530 channel to move into discard mode
*
@@ -999,7 +1016,7 @@ int z8530_sync_dma_close(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_dma_close);
/**
- * z8530_sync_txdma_open:
+ * z8530_sync_txdma_open - Open a Z8530 for TX driven DMA
* @dev: The network device to attach
* @c: The Z8530 channel to configure in sync DMA mode.
*
@@ -1099,7 +1116,7 @@ int z8530_sync_txdma_open(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_txdma_open);
/**
- * z8530_sync_txdma_close:
+ * z8530_sync_txdma_close - Close down a TX driven DMA channel
* @dev: Network device to detach
* @c: Z8530 channel to move into discard mode
*
@@ -1168,7 +1185,7 @@ static char *z8530_type_name[]={
};
/**
- * z8530_describe:
+ * z8530_describe - Uniformly describe a Z8530 port
* @dev: Z8530 device to describe
* @mapping: string holding mapping type (eg "I/O" or "Mem")
* @io: the port value in question
@@ -1191,7 +1208,7 @@ void z8530_describe(struct z8530_dev *dev, char *mapping, unsigned long io)
EXPORT_SYMBOL(z8530_describe);
/**
- * z8530_init:
+ * z8530_init - Initialise a Z8530 device
* @dev: Z8530 device to initialise.
*
* Configure up a Z8530/Z85C30 or Z85230 chip. We check the device
@@ -1273,7 +1290,7 @@ int z8530_init(struct z8530_dev *dev)
EXPORT_SYMBOL(z8530_init);
/**
- * z8530_shutdown:
+ * z8530_shutdown - Shutdown a Z8530 device
* @dev: The Z8530 chip to shutdown
*
* We set the interrupt handlers to silence any interrupts. We then
@@ -1294,7 +1311,7 @@ int z8530_shutdown(struct z8530_dev *dev)
EXPORT_SYMBOL(z8530_shutdown);
/**
- * z8530_channel_load:
+ * z8530_channel_load - Load channel data
* @c: Z8530 channel to configure
* @rtable: Table of register, value pairs
* FIXME: ioctl to allow user uploaded tables
@@ -1333,7 +1350,7 @@ EXPORT_SYMBOL(z8530_channel_load);
/**
- * z8530_tx_begin:
+ * z8530_tx_begin - Begin packet transmission
* @c: The Z8530 channel to kick
*
* This is the speed sensitive side of transmission. If we are called
@@ -1430,7 +1447,7 @@ static void z8530_tx_begin(struct z8530_channel *c)
}
/**
- * z8530_tx_done:
+ * z8530_tx_done - TX complete callback
* @c: The channel that completed a transmit.
*
* This is called when we complete a packet send. We wake the queue,
@@ -1461,7 +1478,7 @@ static void z8530_tx_done(struct z8530_channel *c)
}
/**
- * z8530_null_rx:
+ * z8530_null_rx - Discard a packet
* @c: The channel the packet arrived on
* @skb: The buffer
*
@@ -1477,7 +1494,7 @@ void z8530_null_rx(struct z8530_channel *c, struct sk_buff *skb)
EXPORT_SYMBOL(z8530_null_rx);
/**
- * z8530_rx_done:
+ * z8530_rx_done - Receive completion callback
* @c: The channel that completed a receive
*
* A new packet is complete. Our goal here is to get back into receive
@@ -1630,7 +1647,7 @@ static void z8530_rx_done(struct z8530_channel *c)
}
/**
- * spans_boundary:
+ * spans_boundary - Check a packet can be ISA DMA'd
* @skb: The buffer to check
*
* Returns true if the buffer cross a DMA boundary on a PC. The poor
@@ -1642,15 +1659,12 @@ extern inline int spans_boundary(struct sk_buff *skb)
unsigned long a=(unsigned long)skb->data;
a^=(a+skb->len);
if(a&0x00010000) /* If the 64K bit is different.. */
- {
- printk("spanner\n");
return 1;
- }
return 0;
}
/**
- * z8530_queue_xmit:
+ * z8530_queue_xmit - Queue a packet
* @c: The channel to use
* @skb: The packet to kick down the channel
*
@@ -1707,7 +1721,7 @@ int z8530_queue_xmit(struct z8530_channel *c, struct sk_buff *skb)
EXPORT_SYMBOL(z8530_queue_xmit);
/**
- * z8530_get_stats:
+ * z8530_get_stats - Get network statistics
* @c: The channel to use
*
* Get the statistics block. We keep the statistics in software as