patch-2.3.99-pre1 linux/drivers/net/wan/comx-hw-comx.c
Next file: linux/drivers/net/wan/comx-hw-locomx.c
Previous file: linux/drivers/net/wan/Makefile
Back to the patch index
Back to the overall index
- Lines: 1427
- Date:
Mon Mar 13 13:55:09 2000
- Orig file:
v2.3.51/linux/drivers/net/wan/comx-hw-comx.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/comx-hw-comx.c linux/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
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)