Commit 557c670b authored by Jeff Garzik's avatar Jeff Garzik

Merge redhat.com:/spare/repo/netdev-2.6/remove-comx

into redhat.com:/spare/repo/net-drivers-2.6
parents 1cda7f59 db4830e5
......@@ -57,119 +57,6 @@ config COSA
The driver will be compiled as a module: the
module will be called cosa.
#
# COMX drivers
#
# Not updated to 2.6.
config COMX
tristate "MultiGate (COMX) synchronous serial boards support"
depends on WAN && (ISA || PCI) && BROKEN
---help---
Drivers for the PC synchronous serial adapters by
ITConsult-Pro Co, Hungary.
Read <file:Documentation/networking/comx.txt> for help on configuring
and using COMX interfaces. Further info on these cards can be found
at <http://www.itc.hu/> or <info@itc.hu>.
Say Y if you want to use any board from the MultiGate (COMX)
family, you must also say Y to "/proc file system support"
(CONFIG_PROC_FS) in order to use these drivers.
To compile this driver as a module, choose M here: the
module will be called comx.
config COMX_HW_COMX
tristate "Support for COMX/CMX/HiCOMX boards"
depends on COMX
help
Driver for the 'CMX', 'COMX' and 'HiCOMX' boards.
You will need additional firmware to use these cards, which are
downloadable from <ftp://ftp.itc.hu/>.
Say Y if you have a board like this.
To compile this driver as a module, choose M here: the
module will be called comx-hw-comx.
config COMX_HW_LOCOMX
tristate "Support for LoCOMX board"
depends on COMX
help
Driver for the 'LoCOMX' board.
Say Y if you have a board like this.
To compile this driver as a module, choose M here: the
module will be called comx-hw-locomx.
config COMX_HW_MIXCOM
tristate "Support for MixCOM board"
depends on COMX
---help---
Driver for the 'MixCOM' board.
If you want to use the watchdog device on this card, you should
select it in the Watchdog Cards section of the Character Devices
configuration. The ISDN interface of this card is Teles 16.3
compatible, you should enable it in the ISDN configuration menu. The
driver for the flash ROM of this card is available separately on
<ftp://ftp.itc.hu/>.
Say Y if you have a board like this.
To compile this driver as a module, choose M here: the
module will be called comx-hw-mixcom.
config COMX_HW_MUNICH
tristate "Support for MUNICH based boards: SliceCOM, PCICOM (WelCOM)"
depends on COMX
---help---
Driver for the 'SliceCOM' (channelized E1) and 'PciCOM' (X21) boards.
Read <file:Documentation/networking/slicecom.txt> for help on
configuring and using SliceCOM interfaces. Further info on these
cards can be found at <http://www.itc.hu> or <info@itc.hu>.
Say Y if you have a board like this.
To compile this driver as a module, choose M here: the
module will be called comx-hw-munich.
config COMX_PROTO_PPP
tristate "Support for HDLC and syncPPP protocols on MultiGate boards"
depends on COMX
help
Cisco-HDLC and synchronous PPP protocol driver.
Say Y if you want to use either protocol.
To compile this as a module, choose M here: the
module will be called comx-proto-ppp.
config COMX_PROTO_LAPB
tristate "Support for LAPB protocol on MultiGate boards"
depends on WAN && (COMX!=n && LAPB=m && LAPB || LAPB=y && COMX)
help
LAPB protocol driver.
Say Y if you want to use this protocol.
To compile this as a module, choose M here: the
module will be called comx-proto-lapb.
config COMX_PROTO_FR
tristate "Support for Frame Relay on MultiGate boards"
depends on COMX
help
Frame Relay protocol driver.
Say Y if you want to use this protocol.
To compile this as a module, choose M here: the
module will be called comx-proto-fr.
config DSCC4
tristate "Etinc PCISYNC serial board support"
depends on WAN && PCI && m
......
......@@ -32,14 +32,6 @@ pc300-objs := $(pc300-y)
obj-$(CONFIG_HOSTESS_SV11) += z85230.o syncppp.o hostess_sv11.o
obj-$(CONFIG_SEALEVEL_4021) += z85230.o syncppp.o sealevel.o
obj-$(CONFIG_COMX) += comx.o
obj-$(CONFIG_COMX_HW_COMX) += comx-hw-comx.o
obj-$(CONFIG_COMX_HW_LOCOMX) += z85230.o syncppp.o comx-hw-locomx.o
obj-$(CONFIG_COMX_HW_MIXCOM) += comx-hw-mixcom.o
obj-$(CONFIG_COMX_HW_MUNICH) += comx-hw-munich.o
obj-$(CONFIG_COMX_PROTO_PPP) += syncppp.o comx-proto-ppp.o
obj-$(CONFIG_COMX_PROTO_LAPB) += comx-proto-lapb.o
obj-$(CONFIG_COMX_PROTO_FR) += comx-proto-fr.o
obj-$(CONFIG_COSA) += syncppp.o cosa.o
obj-$(CONFIG_FARSYNC) += syncppp.o farsync.o
obj-$(CONFIG_DSCC4) += dscc4.o
......
/*
* 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>
*
* Contributors:
* Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 0.86
* Daniele Bellucci <bellucda@tiscali.it> - 0.87
*
* 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
* Version 0.86 (00/08/15):
* - resource release on failure at COMX_init
*
* Version 0.87 (03/07/09)
* - audit copy_from_user in comxhw_write_proc
*/
#define VERSION "0.87"
#include <linux/module.h>
#include <linux/types.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/io.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");
MODULE_LICENSE("GPL");
#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 irqreturn_t 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 irqreturn_t 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 IRQ_NONE;
}
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);
return IRQ_HANDLED;
}
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 (!request_region(dev->base_addr, hw->io_extent, dev->name)) {
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);
release_region(dev->base_addr, hw->io_extent);
return -EAGAIN;
}
ch->init_status |= IRQ_ALLOCATED;
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 && time_before(jiffies, jiffs + HZ)) {
schedule_timeout(1);
}
if (time_after_eq(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 && time_before(jiffies, jiffs + HZ)) {
schedule_timeout(1);
}
if (time_after_eq(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(ch->init_status & HW_OPEN) {
return -EAGAIN;
}
if (strcmp(FILENAME_FIRMWARE, entry->name) != 0) {
if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
return -ENOMEM;
}
if(copy_from_user(page, buffer, count = (min_t(int, count, PAGE_SIZE))))
{
count = -EFAULT;
goto out;
}
if (page[count-1] == '\n')
page[count-1] = '\0';
else if (count < PAGE_SIZE)
page[count] = '\0';
else if (page[count]) {
count = -EINVAL;
goto out;
}
page[count]=0; /* Null terminate */
} 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);
}
if (copy_from_user(tmp + file->f_pos, buffer, count))
return -EFAULT;
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;
}
}
out:
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_t(int, FILE_PAGESIZE,
min_t(int, 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_t(int, 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) {
goto cleanup_HW_privdata;
}
new_file->data = (void *)new_file;
new_file->read_proc = &comxhw_read_proc;
new_file->write_proc = &comxhw_write_proc;
new_file->size = 6;
new_file->nlink = 1;
if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, ch->procdir))
== NULL) {
goto cleanup_filename_io;
}
new_file->data = (void *)new_file;
new_file->read_proc = &comxhw_read_proc;
new_file->write_proc = &comxhw_write_proc;
new_file->size = 5;
new_file->nlink = 1;
if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644,
ch->procdir)) == NULL) {
goto cleanup_filename_irq;
}
new_file->data = (void *)new_file;
new_file->read_proc = &comxhw_read_proc;
new_file->write_proc = &comxhw_write_proc;
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) {
goto cleanup_filename_channel;
}
new_file->data = (void *)new_file;
new_file->read_proc = &comxhw_read_proc;
new_file->write_proc = &comxhw_write_proc;
new_file->size = 9;
new_file->nlink = 1;
}
if ((new_file = create_proc_entry(FILENAME_MEMADDR, S_IFREG | 0644,
ch->procdir)) == NULL) {
goto cleanup_filename_clock;
}
new_file->data = (void *)new_file;
new_file->read_proc = &comxhw_read_proc;
new_file->write_proc = &comxhw_write_proc;
new_file->size = 8;
new_file->nlink = 1;
if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444,
ch->procdir)) == NULL) {
goto cleanup_filename_memaddr;
}
new_file->data = (void *)new_file;
new_file->read_proc = &comxhw_read_proc;
new_file->write_proc = NULL;
new_file->nlink = 1;
if ((new_file = create_proc_entry(FILENAME_FIRMWARE, S_IFREG | 0644,
ch->procdir)) == NULL) {
goto cleanup_filename_twin;
}
new_file->data = (void *)new_file;
new_file->read_proc = &comxhw_read_proc;
new_file->write_proc = &comxhw_write_proc;
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;
cleanup_filename_twin:
remove_proc_entry(FILENAME_TWIN, ch->procdir);
cleanup_filename_memaddr:
remove_proc_entry(FILENAME_MEMADDR, ch->procdir);
cleanup_filename_clock:
if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw)
remove_proc_entry(FILENAME_CLOCK, ch->procdir);
cleanup_filename_channel:
remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
cleanup_filename_irq:
remove_proc_entry(FILENAME_IRQ, ch->procdir);
cleanup_filename_io:
remove_proc_entry(FILENAME_IO, ch->procdir);
cleanup_HW_privdata:
kfree(ch->HW_privdata);
return -EIO;
}
/* 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
};
static int __init comx_hw_comx_init(void)
{
comx_register_hardware(&comx_hw);
comx_register_hardware(&cmx_hw);
comx_register_hardware(&hicomx_hw);
return 0;
}
static void __exit comx_hw_comx_exit(void)
{
comx_unregister_hardware("comx");
comx_unregister_hardware("cmx");
comx_unregister_hardware("hicomx");
}
module_init(comx_hw_comx_init);
module_exit(comx_hw_comx_exit);
/*
* 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
*
* Contributors:
* Arnaldo Carvalho de Melo <acme@conectiva.com.br> (0.14)
*
* 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
* Version 0.14 (00/08/15):
* - resource release on failure at LOCOMX_init
*/
#define VERSION "0.14"
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <asm/types.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/dma.h>
#include "comx.h"
#include "z85230.h"
MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
MODULE_DESCRIPTION("Hardware driver for the LoCOMX board");
MODULE_LICENSE("GPL");
#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 = netdev_priv(dev);
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 = netdev_priv(dev);
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 = netdev_priv(dev);
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 = netdev_priv(dev);
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 = netdev_priv(dev);
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 (!request_region(dev->base_addr, hw->io_extent, dev->name)) {
return -EAGAIN;
}
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 = netdev_priv(dev);
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_t(int, 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 (!(page = (char *)__get_free_page(GFP_KERNEL))) {
return -ENOMEM;
}
if (copy_from_user(page, buffer, count = min_t(unsigned long, count, PAGE_SIZE))) {
free_page((unsigned long)page);
return -EBADF;
}
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 = netdev_priv(dev);
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) {
goto cleanup_HW_privdata;
}
new_file->data = (void *)new_file;
new_file->read_proc = &locomx_read_proc;
new_file->write_proc = &locomx_write_proc;
new_file->nlink = 1;
if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644,
ch->procdir)) == NULL) {
goto cleanup_filename_io;
}
new_file->data = (void *)new_file;
new_file->read_proc = &locomx_read_proc;
new_file->write_proc = &locomx_write_proc;
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->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;
cleanup_filename_io:
remove_proc_entry(FILENAME_IO, ch->procdir);
cleanup_HW_privdata:
kfree(ch->HW_privdata);
return -EIO;
}
static int LOCOMX_exit(struct net_device *dev)
{
struct comx_channel *ch = netdev_priv(dev);
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
};
static int __init comx_hw_locomx_init(void)
{
comx_register_hardware(&locomx_hw);
return 0;
}
static void __exit comx_hw_locomx_exit(void)
{
comx_unregister_hardware("locomx");
}
module_init(comx_hw_locomx_init);
module_exit(comx_hw_locomx_exit);
/*
* 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>
*
* Contributors:
* Arnaldo Carvalho de Melo <acme@conectiva.com.br> (0.65)
*
* 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
*
* Version 0.65 (00/08/15)
* - resource release on failure at MIXCOM_init
*/
#define VERSION "0.65"
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <asm/types.h>
#include <asm/uaccess.h>
#include <asm/io.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");
MODULE_LICENSE("GPL");
#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;
long 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) &&
time_before(jiffies, jiffs + HZ)) {
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_t(unsigned int, 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 irqreturn_t 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 IRQ_NONE;
}
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 IRQ_HANDLED;
}
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;
int ret = -ENODEV;
if (!dev->base_addr || !dev->irq)
goto err_ret;
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);
ret = -EAGAIN;
goto err_ret;
}
}
/* 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 (!request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name)) {
ret = -EAGAIN;
goto err_ret;
}
if (mixcom_probe(dev)) {
ret = -ENODEV;
goto err_release_region;
}
}
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);
ret = -EAGAIN;
goto err_release_region;
}
}
save_flags(flags); cli();
if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) {
ch->init_status|=IRQ_ALLOCATED;
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;
err_release_region:
release_region(dev->base_addr, MIXCOM_IO_EXTENT);
err_ret:
return ret;
}
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_t(int, 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 (!(page = (char *)__get_free_page(GFP_KERNEL))) {
return -ENOMEM;
}
if (copy_from_user(page, buffer, count = min_t(unsigned long, count, PAGE_SIZE))) {
free_page((unsigned long)page);
return -EFAULT;
}
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) {
goto cleanup_HW_privdata;
}
new_file->data = (void *)new_file;
new_file->read_proc = &mixcom_read_proc;
new_file->write_proc = &mixcom_write_proc;
new_file->nlink = 1;
if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644,
ch->procdir)) == NULL) {
goto cleanup_filename_io;
}
new_file->data = (void *)new_file;
new_file->read_proc = &mixcom_read_proc;
new_file->write_proc = &mixcom_write_proc;
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->nlink = 1;
#endif
if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644,
ch->procdir)) == NULL) {
goto cleanup_filename_irq;
}
new_file->data = (void *)new_file;
new_file->read_proc = &mixcom_read_proc;
new_file->write_proc = &mixcom_write_proc;
new_file->nlink = 1;
if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444,
ch->procdir)) == NULL) {
goto cleanup_filename_channel;
}
new_file->data = (void *)new_file;
new_file->read_proc = &mixcom_read_proc;
new_file->write_proc = &mixcom_write_proc;
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;
cleanup_filename_channel:
remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
cleanup_filename_irq:
remove_proc_entry(FILENAME_IRQ, ch->procdir);
cleanup_filename_io:
remove_proc_entry(FILENAME_IO, ch->procdir);
cleanup_HW_privdata:
kfree(ch->HW_privdata);
return -EIO;
}
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
};
static int __init comx_hw_mixcom_init(void)
{
return comx_register_hardware(&mixcomhw);
}
static void __exit comx_hw_mixcom_exit(void)
{
comx_unregister_hardware("mixcom");
}
module_init(comx_hw_mixcom_init);
module_exit(comx_hw_mixcom_exit);
/*
* Hardware-level driver for the SliceCOM board for Linux kernels 2.4.X
*
* Current maintainer / latest changes: Pasztor Szilard <don@itc.hu>
*
* Original author: Bartok Istvan <bartoki@itc.hu>
* Based on skeleton by Tivadar Szemethy <tiv@itc.hu>
*
* 0.51:
* - port for 2.4.x
* - clean up some code, make it more portable
* - busted direct hardware access through mapped memory
* - fix a possible race
* - prevent procfs buffer overflow
*
* 0.50:
* - support for the pcicom board, lots of rearrangements
* - handle modem status lines
*
* 0.50a:
* - fix for falc version 1.0
*
* 0.50b: T&t
* - fix for bad localbus
*/
#define VERSION "0.51"
#define VERSIONSTR "SliceCOM v" VERSION ", 2002/01/07\n"
#include <linux/config.h>
#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <asm/delay.h>
#include <asm/types.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define COMX_NEW
#ifndef COMX_NEW
#include "../include/comx.h"
#include "../include/munich32x.h"
#include "../include/falc-lh.h"
#else
#include "comx.h"
#include "munich32x.h"
#include "falc-lh.h"
#endif
MODULE_AUTHOR("Bartok Istvan <bartoki@itc.hu>, Gergely Madarasz <gorgo@itc.hu>, Szilard Pasztor <don@itc.hu>");
MODULE_DESCRIPTION("Hardware-level driver for the SliceCOM and PciCOM (WelCOM) adapters");
MODULE_LICENSE("GPL");
/*
* TODO: az ilyenek a comxhw.h -ban szoktak lenni, idovel menjenek majd oda:
*/
#define FILENAME_BOARDNUM "boardnum" /* /proc/comx/comx0.1/boardnum */
#define FILENAME_TIMESLOTS "timeslots" /* /proc/comx/comx0.1/timeslots */
#define FILENAME_FRAMING "framing" /* /proc/comx/comx0.1/framing */
#define FILENAME_LINECODE "linecode" /* /proc/comx/comx0.1/linecode */
#define FILENAME_CLOCK_SOURCE "clock_source" /* /proc/comx/comx0.1/clock_source */
#define FILENAME_LOOPBACK "loopback" /* /proc/comx/comx0.1/loopback */
#define FILENAME_REG "reg" /* /proc/comx/comx0.1/reg */
#define FILENAME_LBIREG "lbireg" /* /proc/comx/comx0.1/lbireg */
#define SLICECOM_BOARDNUM_DEFAULT 0
#define SLICECOM_FRAMING_CRC4 1
#define SLICECOM_FRAMING_NO_CRC4 2
#define SLICECOM_FRAMING_DEFAULT SLICECOM_FRAMING_CRC4
#define SLICECOM_LINECODE_HDB3 1
#define SLICECOM_LINECODE_AMI 2
#define SLICECOM_LINECODE_DEFAULT SLICECOM_LINECODE_HDB3
#define SLICECOM_CLOCK_SOURCE_LINE 1
#define SLICECOM_CLOCK_SOURCE_INTERNAL 2
#define SLICECOM_CLOCK_SOURCE_DEFAULT SLICECOM_CLOCK_SOURCE_LINE
#define SLICECOM_LOOPBACK_NONE 1
#define SLICECOM_LOOPBACK_LOCAL 2
#define SLICECOM_LOOPBACK_REMOTE 3
#define SLICECOM_LOOPBACK_DEFAULT SLICECOM_LOOPBACK_NONE
#define MUNICH_VIRT(addr) (void *)(&bar1[addr])
struct slicecom_stringtable
{
char *name;
int value;
};
/* A convention: keep "default" the last not NULL when reading from /proc,
"error" is an indication that something went wrong, we have an undefined value */
struct slicecom_stringtable slicecom_framings[] =
{
{"crc4", SLICECOM_FRAMING_CRC4},
{"no-crc4", SLICECOM_FRAMING_NO_CRC4},
{"default", SLICECOM_FRAMING_DEFAULT},
{"error", 0}
};
struct slicecom_stringtable slicecom_linecodes[] =
{
{"hdb3", SLICECOM_LINECODE_HDB3},
{"ami", SLICECOM_LINECODE_AMI},
{"default", SLICECOM_LINECODE_DEFAULT},
{"error", 0}
};
struct slicecom_stringtable slicecom_clock_sources[] =
{
{"line", SLICECOM_CLOCK_SOURCE_LINE},
{"internal", SLICECOM_CLOCK_SOURCE_INTERNAL},
{"default", SLICECOM_CLOCK_SOURCE_DEFAULT},
{"error", 0}
};
struct slicecom_stringtable slicecom_loopbacks[] =
{
{"none", SLICECOM_LOOPBACK_NONE},
{"local", SLICECOM_LOOPBACK_LOCAL},
{"remote", SLICECOM_LOOPBACK_REMOTE},
{"default", SLICECOM_LOOPBACK_DEFAULT},
{"error", 0}
};
/*
* Some tunable values...
*
* Note: when tuning values which change the length of text in
* /proc/comx/comx[n]/status, keep in mind that it must be shorter then
* PAGESIZE !
*/
#define MAX_BOARDS 4 /* ezzel 4 kartya lehet a gepben: 0..3 */
#define RX_DESC_MAX 8 /* Rx ring size, must be >= 4 */
#define TX_DESC_MAX 4 /* Tx ring size, must be >= 2 */
/* a sokkal hosszabb Tx ring mar ronthatja a nem-FIFO packet */
/* schedulerek (fair queueing, stb.) hatekonysagat. */
#define MAX_WORK 10 /* TOD: update the info max. ennyi-1 esemenyt dolgoz fel egy interrupt hivasnal */
/*
* These are tunable too, but don't touch them without fully understanding what is happening
*/
#define UDELAY 20 /* We wait UDELAY usecs with disabled interrupts before and */
/* after each command to avoid writing into each other's */
/* ccb->action_spec. A _send_packet nem var, mert azt az */
/* _interrupt()-bol is meghivhatja a LINE_tx() */
/*
* Just to avoid warnings about implicit declarations:
*/
static int MUNICH_close(struct net_device *dev);
static struct comx_hardware slicecomhw;
static struct comx_hardware pcicomhw;
static unsigned long flags;
static spinlock_t mister_lock = SPIN_LOCK_UNLOCKED;
typedef volatile struct /* Time Slot Assignment */
{
u32 rxfillmask:8, // ----------------------------+------+
// | |
rxchannel:5, // ----------------------+---+ | |
rti:1, // ---------------------+| | | |
res2:2, // -------------------++|| | | |
// |||| | | |
txfillmask:8, // ----------+------+ |||| | | |
// | | |||| | | |
txchannel:5, // ----+---+ | | |||| | | |
tti:1, // ---+| | | | |||| | | |
res1:2; // -++|| | | | |||| | | |
// 3 2 1
// 10987654 32109876 54321098 76543210
} timeslot_spec_t;
typedef volatile struct /* Receive Descriptor */
{
u32 zero1:16, no:13, hi:1, hold:1, zero2:1;
u32 next;
u32 data;
u32 zero3:8, status:8, bno:13, zero4:1, c:1, fe:1;
} rx_desc_t;
typedef volatile struct /* Transmit Descriptor */
{
u32 fnum:11, csm:1, no13:1, zero1:2, v110:1, no:13, hi:1, hold:1, fe:1;
u32 next;
u32 data;
} tx_desc_t;
typedef volatile struct /* Channel Specification */
{
u32 iftf:1, mode:2, fa:1, trv:2, crc:1, inv:1, cs:1, tflag:7, ra:1, ro:1,
th:1, ta:1, to:1, ti:1, ri:1, nitbs:1, fit:1, fir:1, re:1, te:1, ch:1,
ifc:1, sfe:1, fe2:1;
u32 frda;
u32 ftda;
u32 itbs:6, zero1:26;
} channel_spec_t;
typedef volatile struct /* Configuration Control Block */
{
u32 action_spec;
u32 reserved1;
u32 reserved2;
timeslot_spec_t timeslot_spec[32];
channel_spec_t channel_spec[32];
u32 current_rx_desc[32];
u32 current_tx_desc[32];
u32 csa; /* Control Start Address. CSA = *CCBA; CCB = *CSA */
/* MUNICH does it like: CCB = *( *CCBA ) */
} munich_ccb_t;
typedef volatile struct /* Entry in the interrupt queue */
{
u32 all;
} munich_intq_t;
#define MUNICH_INTQLEN 63 /* Rx/Tx Interrupt Queue Length
(not the real len, but the TIQL/RIQL value) */
#define MUNICH_INTQMAX ( 16*(MUNICH_INTQLEN+1) ) /* Rx/Tx/Periph Interrupt Queue size in munich_intq_t's */
#define MUNICH_INTQSIZE ( 4*MUNICH_INTQMAX ) /* Rx/Tx/Periph Interrupt Queue size in bytes */
#define MUNICH_PIQLEN 4 /* Peripheral Interrupt Queue Length. Unlike the RIQL/TIQL, */
#define MUNICH_PIQMAX ( 4*MUNICH_PIQLEN ) /* PIQL register needs it like this */
#define MUNICH_PIQSIZE ( 4*MUNICH_PIQMAX )
typedef volatile u32 vol_u32; /* TOD: ezek megszunnek ha atirom readw()/writew()-re - ksz */
typedef volatile u8 vol_u8;
typedef volatile struct /* counters of E1-errors and errored seconds, see rfc2495 */
{
/* use here only unsigned ints, we depend on it when calculating the sum for the last N intervals */
unsigned line_code_violations, /* AMI: bipolar violations, HDB3: hdb3 violations */
path_code_violations, /* FAS errors and CRC4 errors */
e_bit_errors, /* E-Bit Errors (the remote side received from us with CRC4-error) */
slip_secs, /* number of seconds with (receive) Controlled Slip(s) */
fr_loss_secs, /* number of seconds an Out Of Frame defect was detected */
line_err_secs, /* number of seconds with one or more Line Code Violations */
degraded_mins, /* Degraded Minute - the estimated error rate is >1E-6, but <1E-3 */
errored_secs, /* Errored Second - at least one of these happened:
- Path Code Violation
- Out Of Frame defect
- Slip
- receiving AIS
- not incremented during an Unavailable Second */
bursty_err_secs, /* Bursty Errored Second: (rfc2495 says it does not apply to E1)
- Path Code Violations >1, but <320
- not a Severely Errored Second
- no AIS
- not incremented during an Unavailabla Second */
severely_err_secs, /* Severely Errored Second:
- CRC4: >=832 Path COde Violations || >0 Out Of Frame defects
- noCRC4: >=2048 Line Code Violations
- not incremented during an Unavailable Second */
unavail_secs; /* number of Unavailable Seconds. Unavailable state is said after:
- 10 contiguous Severely Errored Seconds
- or RAI || AIS || LOF || LOS
- (any) loopback has been set */
/*
* we do not strictly comply to the rfc: we do not retroactively reduce errored_secs,
* bursty_err_secs, severely_err_secs when 'unavailable state' is reached
*/
} e1_stats_t;
typedef volatile struct /* ezek board-adatok, nem lehetnek a slicecom_privdata -ban */
{
int use_count; /* num. of interfaces using the board */
int irq; /* a kartya irq-ja. belemasoljuk a dev->irq -kba is, de csak hogy */
/* szebb legyen az ifconfig outputja */
/* ha != 0, az azt jelenti hogy az az irq most nekunk sikeresen */
/* le van foglalva */
struct pci_dev *pci; /* a kartya PCI strukturaja. NULL, ha nincs kartya */
u32 *bar1; /* pci->base_address[0] ioremap()-ed by munich_probe(), */
/* on x86 can be used both as a bus or virtual address. */
/* These are the Munich's registers */
u8 *lbi; /* pci->base_address[1] ioremap()-ed by munich_probe(), */
/* this is a 256-byte range, the start of the LBI on the board */
munich_ccb_t *ccb; /* virtual address of CCB */
munich_intq_t *tiq; /* Tx Interrupt Queue */
munich_intq_t *riq; /* Rx Interrupt Queue */
munich_intq_t *piq; /* Peripheral Interrupt Queue (FALC interrupts arrive here) */
int tiq_ptr, /* A 'current' helyek a tiq/riq/piq -ban. */
riq_ptr, /* amikor feldolgoztam az interruptokat, a legelso ures */
piq_ptr; /* interrupt_information szora mutatnak. */
struct net_device *twins[32]; /* MUNICH channel -> network interface assignment */
unsigned long lastcheck; /* When were the Rx rings last checked. Time in jiffies */
struct timer_list modemline_timer;
char isx21;
char lineup;
char framing; /* a beallitasok tarolasa */
char linecode;
char clock_source;
char loopback;
char devname[30]; /* what to show in /proc/interrupts */
unsigned histogram[MAX_WORK]; /* number of processed events in the interrupt loop */
unsigned stat_pri_races; /* number of special events, we try to handle them */
unsigned stat_pti_races;
unsigned stat_pri_races_missed; /* when it can not be handled, because of MAX_WORK */
unsigned stat_pti_races_missed;
#define SLICECOM_BOARD_INTERVALS_SIZE 97
e1_stats_t intervals[SLICECOM_BOARD_INTERVALS_SIZE]; /* E1 line statistics */
unsigned current_interval; /* pointer to the current interval */
unsigned elapsed_seconds; /* elapsed seconds from the start of the current interval */
unsigned ses_seconds; /* counter of contiguous Severely Errored Seconds */
unsigned is_unavailable; /* set to 1 after 10 contiguous Severely Errored Seconds */
unsigned no_ses_seconds; /* contiguous Severely Error -free seconds in unavail state */
unsigned deg_elapsed_seconds; /* for counting the 'Degraded Mins' */
unsigned deg_cumulated_errors;
struct module *owner; /* pointer to our module to avoid module load races */
} munich_board_t;
struct slicecom_privdata
{
int busy; /* transmitter busy - number of packets in the Tx ring */
int channel; /* Munich logical channel ('channel-group' in Cisco) */
unsigned boardnum;
u32 timeslots; /* i-th bit means i-th timeslot is our */
int tx_ring_hist[TX_DESC_MAX]; /* histogram: number of packets in Tx ring when _send_packet is called */
tx_desc_t tx_desc[TX_DESC_MAX]; /* the ring of Tx descriptors */
u8 tx_data[TX_DESC_MAX][TXBUFFER_SIZE]; /* buffers for data to transmit */
int tx_desc_ptr; /* hanyadik descriptornal tartunk a beirassal */
/* ahol ez all, oda irtunk utoljara */
rx_desc_t rx_desc[RX_DESC_MAX]; /* the ring of Rx descriptors */
u8 rx_data[RX_DESC_MAX][RXBUFFER_SIZE]; /* buffers for received data */
int rx_desc_ptr; /* hanyadik descriptornal tartunk az olvasassal */
int rafutott;
};
static u32 reg, reg_ertek; /* why static: don't write stack trash into regs if strtoul() fails */
static u32 lbireg;
static u8 lbireg_ertek; /* why static: don't write stack trash into regs if strtoul() fails */
static munich_board_t slicecom_boards[MAX_BOARDS];
static munich_board_t pcicom_boards[MAX_BOARDS];
/*
* Reprogram Idle Channel Registers in the FALC - send special code in not used channels
* Should be called from the open and close, when the timeslot assignment changes
*/
void rework_idle_channels(struct net_device *dev)
{
struct comx_channel *ch = netdev_priv(dev);
struct slicecom_privdata *hw = ch->HW_privdata;
munich_board_t *board = slicecom_boards + hw->boardnum;
munich_ccb_t *ccb = board->ccb;
u8 *lbi = board->lbi;
int i, j, tmp;
spin_lock_irqsave(&mister_lock, flags);
for (i = 0; i < 4; i++)
{
tmp = 0xFF;
for (j = 0; j < 8; j++)
if (ccb->timeslot_spec[8 * i + j].tti == 0) tmp ^= (0x80 >> j);
writeb(tmp, lbi + 0x30 + i);
}
spin_unlock_irqrestore(&mister_lock, flags);
}
/*
* Set PCM framing - /proc/comx/comx0/framing
*/
void slicecom_set_framing(int boardnum, int value)
{
u8 *lbi = slicecom_boards[boardnum].lbi;
spin_lock_irqsave(&mister_lock, flags);
slicecom_boards[boardnum].framing = value;
switch (value)
{
case SLICECOM_FRAMING_CRC4:
writeb(readb(lbi + FMR1) | 8, lbi + FMR1);
writeb((readb(lbi + FMR2) & 0x3f) | 0x80, lbi + FMR2);
break;
case SLICECOM_FRAMING_NO_CRC4:
writeb(readb(lbi + FMR1) & 0xf7, lbi + FMR1);
writeb(readb(lbi + FMR2) & 0x3f, lbi + FMR2);
break;
default:
printk("slicecom: board %d: unhandled " FILENAME_FRAMING
" value %d\n", boardnum, value);
}
spin_unlock_irqrestore(&mister_lock, flags);
}
/*
* Set PCM linecode - /proc/comx/comx0/linecode
*/
void slicecom_set_linecode(int boardnum, int value)
{
u8 *lbi = slicecom_boards[boardnum].lbi;
spin_lock_irqsave(&mister_lock, flags);
slicecom_boards[boardnum].linecode = value;
switch (value)
{
case SLICECOM_LINECODE_HDB3:
writeb(readb(lbi + FMR0) | 0xf0, lbi + FMR0);
break;
case SLICECOM_LINECODE_AMI:
writeb((readb(lbi + FMR0) & 0x0f) | 0xa0, lbi + FMR0);
break;
default:
printk("slicecom: board %d: unhandled " FILENAME_LINECODE
" value %d\n", boardnum, value);
}
spin_unlock_irqrestore(&mister_lock, flags);
}
/*
* Set PCM clock source - /proc/comx/comx0/clock_source
*/
void slicecom_set_clock_source(int boardnum, int value)
{
u8 *lbi = slicecom_boards[boardnum].lbi;
spin_lock_irqsave(&mister_lock, flags);
slicecom_boards[boardnum].clock_source = value;
switch (value)
{
case SLICECOM_CLOCK_SOURCE_LINE:
writeb(readb(lbi + LIM0) & ~1, lbi + LIM0);
break;
case SLICECOM_CLOCK_SOURCE_INTERNAL:
writeb(readb(lbi + LIM0) | 1, lbi + LIM0);
break;
default:
printk("slicecom: board %d: unhandled " FILENAME_CLOCK_SOURCE
" value %d\n", boardnum, value);
}
spin_unlock_irqrestore(&mister_lock, flags);
}
/*
* Set loopbacks - /proc/comx/comx0/loopback
*/
void slicecom_set_loopback(int boardnum, int value)
{
u8 *lbi = slicecom_boards[boardnum].lbi;
spin_lock_irqsave(&mister_lock, flags);
slicecom_boards[boardnum].loopback = value;
switch (value)
{
case SLICECOM_LOOPBACK_NONE:
writeb(readb(lbi + LIM0) & ~2, lbi + LIM0); /* Local Loop OFF */
writeb(readb(lbi + LIM1) & ~2, lbi + LIM1); /* Remote Loop OFF */
break;
case SLICECOM_LOOPBACK_LOCAL:
writeb(readb(lbi + LIM1) & ~2, lbi + LIM1); /* Remote Loop OFF */
writeb(readb(lbi + LIM0) | 2, lbi + LIM0); /* Local Loop ON */
break;
case SLICECOM_LOOPBACK_REMOTE:
writeb(readb(lbi + LIM0) & ~2, lbi + LIM0); /* Local Loop OFF */
writeb(readb(lbi + LIM1) | 2, lbi + LIM1); /* Remote Loop ON */
break;
default:
printk("slicecom: board %d: unhandled " FILENAME_LOOPBACK
" value %d\n", boardnum, value);
}
spin_unlock_irqrestore(&mister_lock, flags);
}
/*
* Update E1 line status LEDs on the adapter
*/
void slicecom_update_leds(munich_board_t * board)
{
u32 *bar1 = board->bar1;
u8 *lbi = board->lbi;
u8 frs0;
u32 leds;
int i;
spin_lock_irqsave(&mister_lock, flags);
leds = 0;
frs0 = readb(lbi + FRS0); /* FRS0 bits described on page 137 */
if (!(frs0 & 0xa0))
{
leds |= 0x2000; /* Green LED: Input signal seems to be OK, no LOS, no LFA */
if (frs0 & 0x10)
leds |= 0x8000; /* Red LED: Receiving Remote Alarm */
}
writel(leds, MUNICH_VIRT(GPDATA));
if (leds == 0x2000 && !board->lineup)
{ /* line up */
board->lineup = 1;
for (i = 0; i < 32; i++)
{
if (board->twins[i] && (board->twins[i]->flags & IFF_RUNNING))
{
struct comx_channel *ch = board->twins[i]->priv;
if (!test_and_set_bit(0, &ch->lineup_pending))
{
ch->lineup_timer.function = comx_lineup_func;
ch->lineup_timer.data = (unsigned long)board->twins[i];
ch->lineup_timer.expires = jiffies + HZ * ch->lineup_delay;
add_timer(&ch->lineup_timer);
}
}
}
}
else if (leds != 0x2000 && board->lineup)
{ /* line down */
board->lineup = 0;
for (i = 0; i < 32; i++)
if (board->twins[i] && (board->twins[i]->flags & IFF_RUNNING))
{
struct comx_channel *ch = board->twins[i]->priv;
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(board->twins[i], ch->line_status);
}
}
}
spin_unlock_irqrestore(&mister_lock, flags);
}
/*
* This function gets called every second when the FALC issues the interrupt.
* Hardware counters contain error counts for last 1-second time interval.
* We add them to the global counters here.
* Read rfc2495 to understand this.
*/
void slicecom_update_line_counters(munich_board_t * board)
{
e1_stats_t *curr_int = &board->intervals[board->current_interval];
u8 *lbi = board->lbi;
unsigned framing_errors, code_violations, path_code_violations, crc4_errors,
e_bit_errors;
unsigned slip_detected, /* this one has logical value, not the number of slips! */
out_of_frame_defect, /* logical value */
ais_defect, /* logical value */
errored_sec, bursty_err_sec, severely_err_sec = 0, failure_sec;
u8 isr2, isr3, isr5, frs0;
spin_lock_irqsave(&mister_lock, flags);
isr2 = readb(lbi + ISR2); /* ISR0-5 described on page 156 */
isr3 = readb(lbi + ISR3);
isr5 = readb(lbi + ISR5);
frs0 = readb(lbi + FRS0); /* FRS0 described on page 137 */
/* Error Events: */
code_violations = readb(lbi + CVCL) + (readb(lbi + CVCH) << 8);
framing_errors = readb(lbi + FECL) + (readb(lbi + FECH) << 8);
crc4_errors = readb(lbi + CEC1L) + (readb(lbi + CEC1H) << 8);
e_bit_errors = readb(lbi + EBCL) + (readb(lbi + EBCH) << 8);
slip_detected = isr3 & (ISR3_RSN | ISR3_RSP);
path_code_violations = framing_errors + crc4_errors;
curr_int->line_code_violations += code_violations;
curr_int->path_code_violations += path_code_violations;
curr_int->e_bit_errors += e_bit_errors;
/* Performance Defects: */
/* there was an LFA in the last second, but maybe disappeared: */
out_of_frame_defect = (isr2 & ISR2_LFA) || (frs0 & FRS0_LFA);
/* there was an AIS in the last second, but maybe disappeared: */
ais_defect = (isr2 & ISR2_AIS) || (frs0 & FRS0_AIS);
/* Performance Parameters: */
if (out_of_frame_defect)
curr_int->fr_loss_secs++;
if (code_violations)
curr_int->line_err_secs++;
errored_sec = ((board->framing == SLICECOM_FRAMING_NO_CRC4) &&
(code_violations)) || path_code_violations ||
out_of_frame_defect || slip_detected || ais_defect;
bursty_err_sec = !out_of_frame_defect && !ais_defect &&
(path_code_violations > 1) && (path_code_violations < 320);
switch (board->framing)
{
case SLICECOM_FRAMING_CRC4:
severely_err_sec = out_of_frame_defect ||
(path_code_violations >= 832);
break;
case SLICECOM_FRAMING_NO_CRC4:
severely_err_sec = (code_violations >= 2048);
break;
}
/*
* failure_sec: true if there was a condition leading to a failure
* (and leading to unavailable state) in this second:
*/
failure_sec = (isr2 & ISR2_RA) || (frs0 & FRS0_RRA) /* Remote/Far End/Distant Alarm Failure */
|| ais_defect || out_of_frame_defect /* AIS or LOF Failure */
|| (isr2 & ISR2_LOS) || (frs0 & FRS0_LOS) /* Loss Of Signal Failure */
|| (board->loopback != SLICECOM_LOOPBACK_NONE); /* Loopback has been set */
if (board->is_unavailable)
{
if (severely_err_sec)
board->no_ses_seconds = 0;
else
board->no_ses_seconds++;
if ((board->no_ses_seconds >= 10) && !failure_sec)
{
board->is_unavailable = 0;
board->ses_seconds = 0;
board->no_ses_seconds = 0;
}
}
else
{
if (severely_err_sec)
board->ses_seconds++;
else
board->ses_seconds = 0;
if ((board->ses_seconds >= 10) || failure_sec)
{
board->is_unavailable = 1;
board->ses_seconds = 0;
board->no_ses_seconds = 0;
}
}
if (board->is_unavailable)
curr_int->unavail_secs++;
else
{
if (slip_detected)
curr_int->slip_secs++;
curr_int->errored_secs += errored_sec;
curr_int->bursty_err_secs += bursty_err_sec;
curr_int->severely_err_secs += severely_err_sec;
}
/* the RFC does not say clearly which errors to count here, we try to count bit errors */
if (!board->is_unavailable && !severely_err_sec)
{
board->deg_cumulated_errors += code_violations;
board->deg_elapsed_seconds++;
if (board->deg_elapsed_seconds >= 60)
{
if (board->deg_cumulated_errors >= 123)
curr_int->degraded_mins++;
board->deg_cumulated_errors = 0;
board->deg_elapsed_seconds = 0;
}
}
board->elapsed_seconds++;
if (board->elapsed_seconds >= 900)
{
board->current_interval =
(board->current_interval + 1) % SLICECOM_BOARD_INTERVALS_SIZE;
memset((void *)&board->intervals[board->current_interval], 0,
sizeof(e1_stats_t));
board->elapsed_seconds = 0;
}
spin_unlock_irqrestore(&mister_lock, flags);
}
static void pcicom_modemline(unsigned long b)
{
munich_board_t *board = (munich_board_t *) b;
struct net_device *dev = board->twins[0];
struct comx_channel *ch = netdev_priv(dev);
unsigned long regs;
regs = readl((void *)(&board->bar1[GPDATA]));
if ((ch->line_status & LINE_UP) && (regs & 0x0800))
{
ch->line_status &= ~LINE_UP;
board->lineup = 0;
if (ch->LINE_status)
{
ch->LINE_status(dev, ch->line_status);
}
}
if (!(ch->line_status & LINE_UP) && !(regs & 0x0800))
{
ch->line_status |= LINE_UP;
board->lineup = 1;
if (ch->LINE_status)
{
ch->LINE_status(dev, ch->line_status);
}
}
mod_timer((struct timer_list *)&board->modemline_timer, jiffies + HZ);
}
/*
* Is it possible to transmit ?
* Called (may be called) by the protocol layer
*/
static int MUNICH_txe(struct net_device *dev)
{
struct comx_channel *ch = netdev_priv(dev);
struct slicecom_privdata *hw = ch->HW_privdata;
return (hw->busy < TX_DESC_MAX - 1);
}
/*
* Hw probe function. Detects all the boards in the system,
* and fills up slicecom_boards[] and pcicom_boards[]
* Returns 0 on success.
* We do not disable interrupts!
*/
static int munich_probe(void)
{
struct pci_dev *pci;
int boardnum;
int slicecom_boardnum;
int pcicom_boardnum;
u32 *bar1;
u8 *lbi;
munich_board_t *board;
for (boardnum = 0; boardnum < MAX_BOARDS; boardnum++)
{
pcicom_boards[boardnum].pci = 0;
pcicom_boards[boardnum].bar1 = 0;
pcicom_boards[boardnum].lbi = 0;
slicecom_boards[boardnum].pci = 0;
slicecom_boards[boardnum].bar1 = 0;
slicecom_boards[boardnum].lbi = 0;
}
pci = NULL;
board = NULL;
slicecom_boardnum = 0;
pcicom_boardnum = 0;
for (boardnum = 0;
boardnum < MAX_BOARDS && (pci = pci_find_device(PCI_VENDOR_ID_SIEMENS,
PCI_DEVICE_ID_SIEMENS_MUNICH32X, pci)); boardnum++)
{
if (pci_enable_device(pci))
continue;
printk("munich_probe: munich chip found, IRQ %d\n", pci->irq);
bar1 = ioremap_nocache(pci->resource[0].start, 0x100);
lbi = ioremap_nocache(pci->resource[1].start, 0x100);
if (bar1 && lbi)
{
pci_write_config_dword(pci, MUNICH_PCI_PCIRES, 0xe0000);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
pci_write_config_dword(pci, MUNICH_PCI_PCIRES, 0);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
/* check the type of the card */
writel(LREG0_MAGIC, MUNICH_VIRT(LREG0));
writel(LREG1_MAGIC, MUNICH_VIRT(LREG1));
writel(LREG2_MAGIC, MUNICH_VIRT(LREG2));
writel(LREG3_MAGIC, MUNICH_VIRT(LREG3));
writel(LREG4_MAGIC, MUNICH_VIRT(LREG4));
writel(LREG5_MAGIC, MUNICH_VIRT(LREG5));
writel(LCONF_MAGIC2,MUNICH_VIRT(LCONF)); /* enable the DMSM */
if ((readb(lbi + VSTR) == 0x13) || (readb(lbi + VSTR) == 0x10))
{
board = slicecom_boards + slicecom_boardnum;
sprintf((char *)board->devname, "slicecom%d",
slicecom_boardnum);
board->isx21 = 0;
slicecom_boardnum++;
}
else if ((readb(lbi + VSTR) == 0x6) || (readb(lbi + GIS) == 0x6))
{
board = pcicom_boards + pcicom_boardnum;
sprintf((char *)board->devname, "pcicom%d", pcicom_boardnum);
board->isx21 = 1;
pcicom_boardnum++;
}
if (board)
{
printk("munich_probe: %s board found\n", board->devname);
writel(LCONF_MAGIC1, MUNICH_VIRT(LCONF)); /* reset the DMSM */
board->pci = pci;
board->bar1 = bar1;
board->lbi = lbi;
board->framing = SLICECOM_FRAMING_DEFAULT;
board->linecode = SLICECOM_LINECODE_DEFAULT;
board->clock_source = SLICECOM_CLOCK_SOURCE_DEFAULT;
board->loopback = SLICECOM_LOOPBACK_DEFAULT;
board->owner = THIS_MODULE;
}
else
{
printk("munich_probe: Board error, VSTR: %02X\n",
readb(lbi + VSTR));
iounmap((void *)bar1);
iounmap((void *)lbi);
}
}
else
{
printk("munich_probe: ioremap() failed, not enabling this board!\n");
/* .pci = NULL, so the MUNICH_open will not try to open it */
if (bar1) iounmap((void *)bar1);
if (lbi) iounmap((void *)lbi);
}
}
if (!pci && !boardnum)
{
printk("munich_probe: no PCI present!\n");
return -ENODEV;
}
if (pcicom_boardnum + slicecom_boardnum == 0)
{
printk
("munich_probe: Couldn't find any munich board: vendor:device %x:%x not found\n",
PCI_VENDOR_ID_SIEMENS, PCI_DEVICE_ID_SIEMENS_MUNICH32X);
return -ENODEV;
}
/* Found some */
if (pcicom_boardnum)
printk("%d pcicom board(s) found.\n", pcicom_boardnum);
if (slicecom_boardnum)
printk("%d slicecom board(s) found.\n", slicecom_boardnum);
return 0;
}
/*
* Reset the hardware. Get called only from within this module if needed.
*/
#if 0
static int slicecom_reset(struct net_device *dev)
{
struct comx_channel *ch = netdev_priv(dev);
printk("slicecom_reset: resetting the hardware\n");
/* Begin to reset the hardware */
if (ch->HW_set_clock)
ch->HW_set_clock(dev);
/* And finish it */
return 0;
}
#endif
/*
* Transmit a packet.
* Called by the protocol layer
* Return values:
* FRAME_ACCEPTED: frame is being transmited, transmitter is busy
* FRAME_QUEUED: frame is being transmitted, there's more room in
* the transmitter for additional packet(s)
* FRAME_ERROR:
* FRAME_DROPPED: there was some error
*/
static int MUNICH_send_packet(struct net_device *dev, struct sk_buff *skb)
{
struct comx_channel *ch = netdev_priv(dev);
struct slicecom_privdata *hw = ch->HW_privdata;
/* Send it to the debug facility too if needed: */
if (ch->debug_flags & DEBUG_HW_TX)
comx_debug_bytes(dev, skb->data, skb->len, "MUNICH_send_packet");
/* If the line is inactive, don't accept: */
/* TODO: atgondolni hogy mi is legyen itt */
/* if (!(ch->line_status & LINE_UP)) return FRAME_DROPPED; */
/* More check, to be sure: */
if (skb->len > TXBUFFER_SIZE)
{
ch->stats.tx_errors++;
kfree_skb(skb);
return FRAME_ERROR;
}
/* Maybe you have to disable irq's while programming the hw: */
spin_lock_irqsave(&mister_lock, flags);
/* And more check: */
if (hw->busy >= TX_DESC_MAX - 1)
{
printk(KERN_ERR
"%s: Transmitter called while busy... dropping frame, busy = %d\n",
dev->name, hw->busy);
spin_unlock_irqrestore(&mister_lock, flags);
kfree_skb(skb);
return FRAME_DROPPED;
}
if (hw->busy >= 0)
hw->tx_ring_hist[hw->busy]++;
/* DELL: */
else
printk("slicecom: %s: FATAL: busy = %d\n", dev->name, hw->busy);
// /* DEL: */
// printk("slicecom: %s: _send_packet called, busy = %d\n", dev->name, hw->busy );
/* Packet can go, update stats: */
ch->stats.tx_packets++;
ch->stats.tx_bytes += skb->len;
/* Pass the packet to the HW: */
/* Step forward with the transmit descriptors: */
hw->tx_desc_ptr = (hw->tx_desc_ptr + 1) % TX_DESC_MAX;
memcpy(&(hw->tx_data[hw->tx_desc_ptr][0]), skb->data, skb->len);
hw->tx_desc[hw->tx_desc_ptr].no = skb->len;
/* We don't issue any command, just step with the HOLD bit */
hw->tx_desc[hw->tx_desc_ptr].hold = 1;
hw->tx_desc[(hw->tx_desc_ptr + TX_DESC_MAX - 1) % TX_DESC_MAX].hold = 0;
#ifdef COMX_NEW
dev_kfree_skb(skb);
#endif
/* csomag kerult a Tx ringbe: */
hw->busy++;
/* Report it: */
if (ch->debug_flags & DEBUG_HW_TX)
comx_debug(dev, "%s: MUNICH_send_packet was successful\n\n", dev->name);
if (hw->busy >= TX_DESC_MAX - 1)
{
spin_unlock_irqrestore(&mister_lock, flags);
return FRAME_ACCEPTED;
}
spin_unlock_irqrestore(&mister_lock, flags);
/* All done */
return FRAME_QUEUED;
}
/*
* Interrupt handler routine.
* Called by the Linux kernel.
* BEWARE! The interrupts are enabled on the call!
*/
static irqreturn_t MUNICH_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct sk_buff *skb;
int length;
int rx_status;
int work; /* hany esemenyt kezeltem mar le */
u32 *bar1;
u8 *lbi;
u32 stat, /* az esemenyek, amiket a ebben a loop korben le kell meg kezelni */
race_stat = 0, /* race eseten ebben uzenek magamnak hogy mit kell meg lekezelni */
ack; /* ezt fogom a vegen a STAT-ba irni, kiveszek belole 1-1 bitet ha */
/* az adott dolgot nem kell ack-olni mert volt vele munkam, es */
/* legjobb ha visszaterek ide megegyszer */
munich_intq_t int_info;
struct net_device *dev;
struct comx_channel *ch;
struct slicecom_privdata *hw;
munich_board_t *board = (munich_board_t *) dev_id;
int channel;
// , boardnum = (int)dev_id;
// board = munich_boards + boardnum;
bar1 = board->bar1;
lbi = board->lbi;
// Do not uncomment this under heavy load! :->
// printk("MUNICH_interrupt: masked STAT=0x%08x, tiq=0x%08x, riq=0x%08x, piq=0x%08x\n", stat, board->tiq[0].all, board->riq[0].all, board->piq[0].all );
for (work = 0; (stat = (race_stat | (readl(MUNICH_VIRT(STAT)) & ~STAT_NOT_HANDLED_BY_INTERRUPT))) && (work < MAX_WORK - 1); work++)
{
ack = stat & (STAT_PRI | STAT_PTI | STAT_LBII);
/* Handle the interrupt information in the Rx queue. We don't really trust */
/* info from this queue, because it can be overflowed, so later check */
/* every Rx ring for received packets. But there are some errors which can't */
/* be counted from the Rx rings, so we parse it. */
int_info = board->riq[board->riq_ptr];
if (int_info.all & 0xF0000000) /* ha ez nem 0, akkor itt interrupt_info van */
{
ack &= ~STAT_PRI; /* don't ack the interrupt, we had some work to do */
channel = PCM_INT_CHANNEL(int_info.all);
dev = board->twins[channel];
if (dev == NULL)
{
printk
("MUNICH_interrupt: got an Rx interrupt info for NULL device "
"%s.twins[%d], int_info = 0x%08x\n", board->devname,
channel, int_info.all);
goto go_for_next_interrupt;
}
ch = netdev_priv(dev);
hw = (struct slicecom_privdata *)ch->HW_privdata;
// printk("Rx STAT=0x%08x int_info=0x%08x rx_desc_ptr=%d rx_desc.status=0x%01x\n",
// stat, int_info.all, hw->rx_desc_ptr, hw->rx_desc[ hw->rx_desc_ptr ].status );
if (int_info.all & PCM_INT_HI)
printk("SliceCOM: %s: Host Initiated interrupt\n", dev->name);
if (int_info.all & PCM_INT_IFC)
printk("SliceCOM: %s: Idle/Flag Change\n", dev->name);
/* TOD: jo ez az Idle/Flag Change valamire? - azonnal latszik belole hogy mikor ad a masik oldal */
/* TOD: ilyen IT most nem is jon, mert ki van maszkolva az interrupt, biztosan kell ez? */
if (int_info.all & PCM_INT_FO)
/* Internal buffer (RB) overrun */
ch->stats.rx_over_errors++; /* TOD: Ez azt jelenti hogy a belso RB nem volt hozzaferheto, es ezert kihagyott valamit. De nem csak csomag lehetett, hanem esemeny, stb. is. lasd page 247. Ezzel a 'cat status'-hoz igazodok, de a netdevice.h szerint nem egyertelmu hogy ide ez kellene. Nem lehet hogy rx_missed ? */
/* DE: nem gotozok sehova, elvileg jo igy */
/* kesobb meg visszaterek az FO-ra, ha packet-FO volt. Keresd a "packet-FO"-t. */
if (int_info.all & PCM_INT_FI) /* frame received, but we do not trust the int_info queue */
if (int_info.all & PCM_INT_SF)
{ /* Short Frame: rovidebb mint a CRC */
/* "rovidebb mint CRC+2byte" vizsgalat a "CRC+2"-nel */
ch->stats.rx_length_errors++; /* TOD: noveljem? ne noveljem? */
goto go_for_next_interrupt;
}
go_for_next_interrupt: /* One step in the interrupt queue */
board->riq[board->riq_ptr].all = 0; /* megjelolom hogy itt meg nem jart a hw */
board->riq_ptr = (board->riq_ptr + 1) % MUNICH_INTQMAX;
}
/* Check every Rx ring for incomed packets: */
for (channel = 0; channel < 32; channel++)
{
dev = board->twins[channel];
if (dev != NULL)
{
ch = netdev_priv(dev);
hw = (struct slicecom_privdata *)ch->HW_privdata;
rx_status = hw->rx_desc[hw->rx_desc_ptr].status;
if (!(rx_status & 0x80)) /* mar jart itt a hardver */
{
ack &= ~STAT_PRI; /* Don't ack, we had some work */
/* Ez most egy kicsit zuros, mert itt mar nem latom az int_infot */
if (rx_status & RX_STATUS_ROF)
ch->stats.rx_over_errors++; /* TOD: 'cat status'-hoz igazodok */
if (rx_status & RX_STATUS_RA)
/* Abort received or issued on channel */
ch->stats.rx_frame_errors++; /* or HOLD bit in the descriptor */
/* TOD: 'cat status'-hoz igazodok */
if (rx_status & RX_STATUS_LFD)
{ /* Long Frame (longer then MFL in the MODE1) */
ch->stats.rx_length_errors++;
goto go_for_next_frame;
}
if (rx_status & RX_STATUS_NOB)
{ /* Not n*8 bits long frame - frame alignment */
ch->stats.rx_frame_errors++; /* ez viszont nem igazodik a 'cat status'-hoz */
goto go_for_next_frame;
}
if (rx_status & RX_STATUS_CRCO)
{ /* CRC error */
ch->stats.rx_crc_errors++;
goto go_for_next_frame;
}
if (rx_status & RX_STATUS_SF)
{ /* Short Frame: rovidebb mint CRC+2byte */
ch->stats.rx_errors++; /* The HW does not set PCI_INT_ERR bit for this one, see page 246 */
ch->stats.rx_length_errors++;
goto go_for_next_frame;
}
if (rx_status != 0)
{
printk("SliceCOM: %s: unhandled rx_status: 0x%02x\n",
dev->name, rx_status);
goto go_for_next_frame;
}
/* frame received without errors: */
length = hw->rx_desc[hw->rx_desc_ptr].bno;
ch->stats.rx_packets++; /* Count only 'good' packets */
ch->stats.rx_bytes += length;
/* Allocate a larger skb and reserve the heading for efficiency: */
if ((skb = dev_alloc_skb(length + 16)) == NULL)
{
ch->stats.rx_dropped++;
goto go_for_next_frame;
}
/* Do bookkeeping: */
skb_reserve(skb, 16);
skb_put(skb, length);
skb->dev = dev;
/* Now copy the data into the buffer: */
memcpy(skb->data, &(hw->rx_data[hw->rx_desc_ptr][0]), length);
/* DEL: UGLY HACK!!!! */
if (*((int *)skb->data) == 0x02000000 &&
*(((int *)skb->data) + 1) == 0x3580008f)
{
printk("%s: swapping hack\n", dev->name);
*((int *)skb->data) = 0x3580008f;
*(((int *)skb->data) + 1) = 0x02000000;
}
if (ch->debug_flags & DEBUG_HW_RX)
comx_debug_skb(dev, skb, "MUNICH_interrupt receiving");
/* Pass it to the protocol entity: */
ch->LINE_rx(dev, skb);
go_for_next_frame:
/* DEL: rafutott-e a HOLD bitre -detektalas */
{
if( ((rx_desc_t*)phys_to_virt(board->ccb->current_rx_desc[channel]))->hold
&& ((rx_desc_t*)phys_to_virt(board->ccb->current_rx_desc[channel]))->status != 0xff)
hw->rafutott++; /* rafutott: hanyszor volt olyan hogy a current descriptoron HOLD bit volt, es a hw mar befejezte az irast (azaz a hw rafutott a HOLD bitre) */
}
// if( jiffies % 2 ) /* DELL: okozzunk egy kis Rx ring slipet :) */
// {
/* Step forward with the receive descriptors: */
/* if you change this, change the copy of it below too! Search for: "RxSlip" */
hw->rx_desc[(hw->rx_desc_ptr + RX_DESC_MAX - 1) % RX_DESC_MAX].hold = 1;
hw->rx_desc[hw->rx_desc_ptr].status = 0xFF; /* megjelolom hogy itt meg nem jart a hw */
hw->rx_desc[(hw->rx_desc_ptr + RX_DESC_MAX - 2) % RX_DESC_MAX].hold = 0;
hw->rx_desc_ptr = (hw->rx_desc_ptr + 1) % RX_DESC_MAX;
// }
}
}
}
stat &= ~STAT_PRI;
// }
// if( stat & STAT_PTI ) /* TOD: primko megvalositas: mindig csak egy esemenyt dolgozok fel, */
/* es nem torlom a STAT-ot, ezert ujra visszajon ide a rendszer. Amikor */
/* jon interrupt, de nincs mit feldolgozni, akkor torlom a STAT-ot. */
/* 'needs a rewrite', de elso megoldasnak jo lesz */
// {
int_info = board->tiq[board->tiq_ptr];
if (int_info.all & 0xF0000000) /* ha ez nem 0, akkor itt interrupt_info van */
{
ack &= ~STAT_PTI; /* don't ack the interrupt, we had some work to do */
channel = PCM_INT_CHANNEL(int_info.all);
dev = board->twins[channel];
if (dev == NULL)
{
printk("MUNICH_interrupt: got a Tx interrupt for NULL device "
"%s.twins[%d], int_info = 0x%08x\n",
board->isx21 ? "pcicom" : "slicecom", channel, int_info.all);
goto go_for_next_tx_interrupt;
}
ch = netdev_priv(dev);
hw = (struct slicecom_privdata *)ch->HW_privdata;
// printk("Tx STAT=0x%08x int_info=0x%08x tiq_ptr=%d\n", stat, int_info.all, board->tiq_ptr );
if (int_info.all & PCM_INT_FE2)
{ /* "Tx available" */
/* do nothing */
}
else if (int_info.all & PCM_INT_FO)
{ /* Internal buffer (RB) overrun */
ch->stats.rx_over_errors++;
}
else
{
printk("slicecom: %s: unhandled Tx int_info: 0x%08x\n",
dev->name, int_info.all);
}
go_for_next_tx_interrupt:
board->tiq[board->tiq_ptr].all = 0;
board->tiq_ptr = (board->tiq_ptr + 1) % MUNICH_INTQMAX;
}
/* Check every Tx ring for incoming packets: */
for (channel = 0; channel < 32; channel++)
{
dev = board->twins[channel];
if (dev != NULL)
{
int newbusy;
ch = netdev_priv(dev);
hw = (struct slicecom_privdata *)ch->HW_privdata;
/* We don't trust the "Tx available" info from the TIQ, but check */
/* every ring if there is some free room */
if (ch->init_status && netif_running(dev))
{
newbusy = ( TX_DESC_MAX + (& hw->tx_desc[ hw->tx_desc_ptr ]) -
(tx_desc_t*)phys_to_virt(board->ccb->current_tx_desc[ hw->channel ]) ) % TX_DESC_MAX;
if(newbusy < 0)
{
printk("slicecom: %s: FATAL: fresly computed busy = %d, HW: 0x%p, SW: 0x%p\n",
dev->name, newbusy,
phys_to_virt(board->ccb->current_tx_desc[hw->channel]),
& hw->tx_desc[hw->tx_desc_ptr]);
}
/* Fogyott valami a Tx ringbol? */
if (newbusy < hw->busy)
{
// ack &= ~STAT_PTI; /* Don't ack, we had some work */
hw->busy = newbusy;
if (ch->LINE_tx)
ch->LINE_tx(dev); /* Report it to protocol driver */
}
else if (newbusy > hw->busy)
printk("slicecom: %s: newbusy > hw->busy, this should not happen!\n", dev->name);
}
}
}
stat &= ~STAT_PTI;
int_info = board->piq[board->piq_ptr];
if (int_info.all & 0xF0000000) /* ha ez nem 0, akkor itt interrupt_info van */
{
ack &= ~STAT_LBII; /* don't ack the interrupt, we had some work to do */
/* We do not really use (yet) the interrupt info from this queue, */
// printk("slicecom: %s: LBI Interrupt event: %08x\n", board->devname, int_info.all);
if (!board->isx21)
{
slicecom_update_leds(board);
slicecom_update_line_counters(board);
}
goto go_for_next_lbi_interrupt; /* To avoid warning about unused label */
go_for_next_lbi_interrupt: /* One step in the interrupt queue */
board->piq[board->piq_ptr].all = 0; /* megjelolom hogy itt meg nem jart a hw */
board->piq_ptr = (board->piq_ptr + 1) % MUNICH_PIQMAX;
}
stat &= ~STAT_LBII;
writel(ack, MUNICH_VIRT(STAT));
if (stat & STAT_TSPA)
{
// printk("slicecom: %s: PCM TSP Asynchronous\n", board->devname);
writel(STAT_TSPA, MUNICH_VIRT(STAT));
stat &= ~STAT_TSPA;
}
if (stat & STAT_RSPA)
{
// printk("slicecom: %s: PCM RSP Asynchronous\n", board->devname);
writel(STAT_RSPA, MUNICH_VIRT(STAT));
stat &= ~STAT_RSPA;
}
if (stat)
{
printk("MUNICH_interrupt: unhandled interrupt, STAT=0x%08x\n",
stat);
writel(stat, MUNICH_VIRT(STAT)); /* ha valamit megsem kezeltunk le, azert ack-ot kuldunk neki */
}
}
board->histogram[work]++;
/* We can miss these if we reach the MAX_WORK */
/* Count it to see how often it happens */
if (race_stat & STAT_PRI)
board->stat_pri_races_missed++;
if (race_stat & STAT_PTI)
board->stat_pti_races_missed++;
return IRQ_HANDLED;
}
/*
* Hardware open routine.
* Called by comx (upper) layer when the user wants to bring up the interface
* with ifconfig.
* Initializes hardware, allocates resources etc.
* Returns 0 on OK, or standard error value on error.
*/
static int MUNICH_open(struct net_device *dev)
{
struct comx_channel *ch = netdev_priv(dev);
struct slicecom_privdata *hw = ch->HW_privdata;
struct proc_dir_entry *procfile = ch->procdir->subdir;
munich_board_t *board;
munich_ccb_t *ccb;
u32 *bar1;
u8 *lbi;
u32 stat;
unsigned long flags, jiffs;
int i, channel;
u32 timeslots = hw->timeslots;
board = hw->boardnum + (ch->hardware == &pcicomhw ? pcicom_boards : slicecom_boards);
bar1 = board->bar1;
lbi = board->lbi;
/* TODO: a timeslotok ellenorzese kell majd ide .. hat, biztos? mar a write_proc-ban is
ellenorzom valamennyire.
if (!dev->io || !dev->irq) return -ENODEV;
*/
if (!board->pci)
{
printk("MUNICH_open: no %s board with boardnum = %d\n",
ch->hardware->name, hw->boardnum);
return -ENODEV;
}
spin_lock_irqsave(&mister_lock, flags);
/* lock the section to avoid race with multiple opens and make sure
that no interrupts get called while this lock is active */
if (board->use_count == 0) /* bring up the board if it was unused */
/* if fails, frees allocated resources and returns. */
/* TOD: is it safe? nem kellene resetelni a kartyat? */
{
printk("MUNICH_open: %s: bringing up board\n", board->devname);
/* Clean up the board's static struct if messed: */
for (i = 0; i < 32; i++)
board->twins[i] = NULL;
for (i = 0; i < MAX_WORK; i++)
board->histogram[i] = 0;
board->lineup = 0;
/* Allocate CCB: */
board->ccb = kmalloc(sizeof(munich_ccb_t), GFP_KERNEL);
if (board->ccb == NULL)
{
spin_unlock_irqrestore(&mister_lock, flags);
return -ENOMEM;
}
memset((void *)board->ccb, 0, sizeof(munich_ccb_t));
board->ccb->csa = virt_to_phys(board->ccb);
ccb = board->ccb;
for (i = 0; i < 32; i++)
{
ccb->timeslot_spec[i].tti = 1;
ccb->timeslot_spec[i].rti = 1;
}
/* Interrupt queues: */
board->tiq = kmalloc(MUNICH_INTQSIZE, GFP_KERNEL);
if (board->tiq == NULL)
{
spin_unlock_irqrestore(&mister_lock, flags);
return -ENOMEM;
}
memset((void *)board->tiq, 0, MUNICH_INTQSIZE);
board->riq = kmalloc(MUNICH_INTQSIZE, GFP_KERNEL);
if (board->riq == NULL)
{
spin_unlock_irqrestore(&mister_lock, flags);
return -ENOMEM;
}
memset((void *)board->riq, 0, MUNICH_INTQSIZE);
board->piq = kmalloc(MUNICH_PIQSIZE, GFP_KERNEL);
if (board->piq == NULL)
{
spin_unlock_irqrestore(&mister_lock, flags);
return -ENOMEM;
}
memset((void *)board->piq, 0, MUNICH_PIQSIZE);
board->tiq_ptr = 0;
board->riq_ptr = 0;
board->piq_ptr = 0;
/* Request irq: */
board->irq = 0;
/* (char*) cast to avoid warning about discarding volatile: */
if (request_irq(board->pci->irq, MUNICH_interrupt, 0,
(char *)board->devname, (void *)board))
{
printk("MUNICH_open: %s: unable to obtain irq %d\n", board->devname,
board->pci->irq);
/* TOD: free other resources (a sok malloc feljebb) */
spin_unlock_irqrestore(&mister_lock, flags);
return -EAGAIN;
}
board->irq = board->pci->irq; /* csak akkor legyen != 0, ha tenyleg le van foglalva nekunk */
/* Programming device: */
/* Reset the board like a power-on: */
/* TOD:
- It is not a real power-on: if a DMA transaction fails with master abort, the board
stays in half-dead state.
- It doesn't reset the FALC line driver */
pci_write_config_dword(board->pci, MUNICH_PCI_PCIRES, 0xe0000);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
pci_write_config_dword(board->pci, MUNICH_PCI_PCIRES, 0);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
writel(virt_to_phys(&ccb->csa), MUNICH_VIRT(CCBA));
writel(virt_to_phys( board->tiq ), MUNICH_VIRT(TIQBA));
writel(MUNICH_INTQLEN, MUNICH_VIRT(TIQL));
writel(virt_to_phys( board->riq ), MUNICH_VIRT(RIQBA));
writel(MUNICH_INTQLEN, MUNICH_VIRT(RIQL));
writel(virt_to_phys( board->piq ), MUNICH_VIRT(PIQBA));
writel(MUNICH_PIQLEN, MUNICH_VIRT(PIQL));
/* Put the magic values into the registers: */
writel(MODE1_MAGIC, MUNICH_VIRT(MODE1));
writel(MODE2_MAGIC, MUNICH_VIRT(MODE2));
writel(LREG0_MAGIC, MUNICH_VIRT(LREG0));
writel(LREG1_MAGIC, MUNICH_VIRT(LREG1));
writel(LREG2_MAGIC, MUNICH_VIRT(LREG2));
writel(LREG3_MAGIC, MUNICH_VIRT(LREG3));
writel(LREG4_MAGIC, MUNICH_VIRT(LREG4));
writel(LREG5_MAGIC, MUNICH_VIRT(LREG5));
writel(LCONF_MAGIC1, MUNICH_VIRT(LCONF)); /* reset the DMSM */
writel(LCONF_MAGIC2, MUNICH_VIRT(LCONF)); /* enable the DMSM */
writel(~0, MUNICH_VIRT(TXPOLL));
writel(board->isx21 ? 0x1400 : 0xa000, MUNICH_VIRT(GPDIR));
if (readl(MUNICH_VIRT(STAT))) writel(readl(MUNICH_VIRT(STAT)), MUNICH_VIRT(STAT));
ccb->action_spec = CCB_ACTIONSPEC_RES | CCB_ACTIONSPEC_IA;
writel(CMD_ARPCM, MUNICH_VIRT(CMD)); /* Start the PCM core reset */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
stat = 0; /* Wait for the action to complete max. 1 second */
jiffs = jiffies;
while (!((stat = readl(MUNICH_VIRT(STAT))) & (STAT_PCMA | STAT_PCMF)) && time_before(jiffies, jiffs + HZ))
{
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
}
if (stat & STAT_PCMF)
{
printk(KERN_ERR
"MUNICH_open: %s: Initial ARPCM failed. STAT=0x%08x\n",
board->devname, stat);
writel(readl(MUNICH_VIRT(STAT)) & STAT_PCMF, MUNICH_VIRT(STAT));
free_irq(board->irq, (void *)board); /* TOD: free other resources too *//* maybe shut down hw? */
board->irq = 0;
spin_unlock_irqrestore(&mister_lock, flags);
return -EAGAIN;
}
else if (!(stat & STAT_PCMA))
{
printk(KERN_ERR
"MUNICH_open: %s: Initial ARPCM timeout. STAT=0x%08x\n",
board->devname, stat);
free_irq(board->irq, (void *)board); /* TOD: free other resources too *//* maybe shut off the hw? */
board->irq = 0;
spin_unlock_irqrestore(&mister_lock, flags);
return -EIO;
}
writel(readl(MUNICH_VIRT(STAT)) & STAT_PCMA, MUNICH_VIRT(STAT)); /* Acknowledge */
if (board->isx21) writel(0, MUNICH_VIRT(GPDATA));
printk("MUNICH_open: %s: succesful HW-open took %ld jiffies\n",
board->devname, jiffies - jiffs);
/* Set up the FALC hanging on the Local Bus: */
if (!board->isx21)
{
writeb(0x0e, lbi + FMR1);
writeb(0, lbi + LIM0);
writeb(0xb0, lbi + LIM1); /* TODO: input threshold */
writeb(0xf7, lbi + XPM0);
writeb(0x02, lbi + XPM1);
writeb(0x00, lbi + XPM2);
writeb(0xf0, lbi + FMR0);
writeb(0x80, lbi + PCD);
writeb(0x80, lbi + PCR);
writeb(0x00, lbi + LIM2);
writeb(0x07, lbi + XC0);
writeb(0x3d, lbi + XC1);
writeb(0x05, lbi + RC0);
writeb(0x00, lbi + RC1);
writeb(0x83, lbi + FMR2);
writeb(0x9f, lbi + XSW);
writeb(0x0f, lbi + XSP);
writeb(0x00, lbi + TSWM);
writeb(0xe0, lbi + MODE);
writeb(0xff, lbi + IDLE); /* Idle Code to send in unused timeslots */
writeb(0x83, lbi + IPC); /* interrupt query line mode: Push/pull output, active high */
writeb(0xbf, lbi + IMR3); /* send an interrupt every second */
slicecom_set_framing(hw->boardnum, board->framing);
slicecom_set_linecode(hw->boardnum, board->linecode);
slicecom_set_clock_source(hw->boardnum, board->clock_source);
slicecom_set_loopback(hw->boardnum, board->loopback);
memset((void *)board->intervals, 0, sizeof(board->intervals));
board->current_interval = 0;
board->elapsed_seconds = 0;
board->ses_seconds = 0;
board->is_unavailable = 0;
board->no_ses_seconds = 0;
board->deg_elapsed_seconds = 0;
board->deg_cumulated_errors = 0;
}
/* Enable the interrupts last */
/* These interrupts will be enabled. We do not need the others. */
writel(readl(MUNICH_VIRT(IMASK)) & ~(STAT_PTI | STAT_PRI | STAT_LBII | STAT_TSPA | STAT_RSPA), MUNICH_VIRT(IMASK));
}
spin_unlock_irqrestore(&mister_lock, flags);
dev->irq = board->irq; /* hogy szep legyen az ifconfig outputja */
ccb = board->ccb; /* TODO: ez igy csunya egy kicsit hogy benn is meg kinn is beletoltom :( */
spin_lock_irqsave(&mister_lock, flags);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
/* Check if the selected timeslots aren't used already */
for (i = 0; i < 32; i++)
if (((1 << i) & timeslots) && !ccb->timeslot_spec[i].tti)
{
printk("MUNICH_open: %s: timeslot %d already used by %s\n",
dev->name, i, board->twins[ccb->timeslot_spec[i].txchannel]->name);
spin_unlock_irqrestore(&mister_lock, flags);
return -EBUSY; /* TODO: lehet hogy valami mas errno kellene? */
}
/* find a free channel: */
/* TODO: ugly, rewrite it */
for (channel = 0; channel <= 32; channel++)
{
if (channel == 32)
{ /* not found a free one */
printk
("MUNICH_open: %s: FATAL: can not find a free channel - this should not happen!\n",
dev->name);
spin_unlock_irqrestore(&mister_lock, flags);
return -ENODEV;
}
if (board->twins[channel] == NULL)
break; /* found the first free one */
}
board->lastcheck = jiffies; /* avoid checking uninitialized hardware channel */
/* Open the channel. If fails, calls MUNICH_close() to properly free resources and stop the HW */
hw->channel = channel;
board->twins[channel] = dev;
board->use_count++; /* meg nem nyitottuk meg a csatornat, de a twins-ben
mar elfoglaltunk egyet, es ha a _close-t akarjuk hivni, akkor ez kell. */
for (i = 0; i < 32; i++)
if ((1 << i) & timeslots)
{
ccb->timeslot_spec[i].tti = 0;
ccb->timeslot_spec[i].txchannel = channel;
ccb->timeslot_spec[i].txfillmask = ~0;
ccb->timeslot_spec[i].rti = 0;
ccb->timeslot_spec[i].rxchannel = channel;
ccb->timeslot_spec[i].rxfillmask = ~0;
}
if (!board->isx21) rework_idle_channels(dev);
memset((void *)&(hw->tx_desc), 0, TX_DESC_MAX * sizeof(tx_desc_t));
memset((void *)&(hw->rx_desc), 0, RX_DESC_MAX * sizeof(rx_desc_t));
for (i = 0; i < TX_DESC_MAX; i++)
{
hw->tx_desc[i].fe = 1;
hw->tx_desc[i].fnum = 2;
hw->tx_desc[i].data = virt_to_phys( & (hw->tx_data[i][0]) );
hw->tx_desc[i].next = virt_to_phys( & (hw->tx_desc[ (i+1) % TX_DESC_MAX ]) );
}
hw->tx_desc_ptr = 0; /* we will send an initial packet so it is correct: "oda irtunk utoljara" */
hw->busy = 0;
hw->tx_desc[hw->tx_desc_ptr].hold = 1;
hw->tx_desc[hw->tx_desc_ptr].no = 1; /* TOD: inkabb csak 0 hosszut kuldjunk ki az initkor? */
for (i = 0; i < RX_DESC_MAX; i++)
{
hw->rx_desc[i].no = RXBUFFER_SIZE;
hw->rx_desc[i].data = virt_to_phys(&(hw->rx_data[i][0]));
hw->rx_desc[i].next = virt_to_phys(&(hw->rx_desc[(i+1) % RX_DESC_MAX]));
hw->rx_desc[i].status = 0xFF;
}
hw->rx_desc_ptr = 0;
hw->rx_desc[(hw->rx_desc_ptr + RX_DESC_MAX - 2) % RX_DESC_MAX].hold = 1;
memset((void *)&ccb->channel_spec[channel], 0, sizeof(channel_spec_t));
ccb->channel_spec[channel].ti = 0; /* Transmit off */
ccb->channel_spec[channel].to = 1;
ccb->channel_spec[channel].ta = 0;
ccb->channel_spec[channel].th = 1; /* Transmit hold */
ccb->channel_spec[channel].ri = 0; /* Receive off */
ccb->channel_spec[channel].ro = 1;
ccb->channel_spec[channel].ra = 0;
ccb->channel_spec[channel].mode = 3; /* HDLC */
ccb->action_spec = CCB_ACTIONSPEC_IN | (channel << 8);
writel(CMD_ARPCM, MUNICH_VIRT(CMD));
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
spin_unlock_irqrestore(&mister_lock, flags);
stat = 0;
jiffs = jiffies;
while (!((stat = readl(MUNICH_VIRT(STAT))) & (STAT_PCMA | STAT_PCMF)) && time_before(jiffies, jiffs + HZ))
{
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
}
if (stat & STAT_PCMF)
{
printk(KERN_ERR "MUNICH_open: %s: %s channel %d off failed\n",
dev->name, board->devname, channel);
writel(readl(MUNICH_VIRT(STAT)) & STAT_PCMF, MUNICH_VIRT(STAT));
MUNICH_close(dev);
return -EAGAIN;
}
else if (!(stat & STAT_PCMA))
{
printk(KERN_ERR "MUNICH_open: %s: %s channel %d off timeout\n",
dev->name, board->devname, channel);
MUNICH_close(dev);
return -EIO;
}
writel(readl(MUNICH_VIRT(STAT)) & STAT_PCMA, MUNICH_VIRT(STAT));
// printk("MUNICH_open: %s: succesful channel off took %ld jiffies\n", board->devname, jiffies-jiffs);
spin_lock_irqsave(&mister_lock, flags);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
ccb->channel_spec[channel].ifc = 1; /* 1 .. 'Idle/Flag change' interrupt letiltva */
ccb->channel_spec[channel].fit = 1;
ccb->channel_spec[channel].nitbs = 1;
ccb->channel_spec[channel].itbs = 2;
/* TODOO: lehet hogy jo lenne igy, de utana kellene nezni hogy nem okoz-e fragmentaciot */
// ccb->channel_spec[channel].itbs = 2 * number_of_timeslots;
// printk("open: %s: number_of_timeslots: %d\n", dev->name, number_of_timeslots);
ccb->channel_spec[channel].mode = 3; /* HDLC */
ccb->channel_spec[channel].ftda = virt_to_phys(&(hw->tx_desc));
ccb->channel_spec[channel].frda = virt_to_phys(&(hw->rx_desc[0]));
ccb->channel_spec[channel].ti = 1; /* Transmit init */
ccb->channel_spec[channel].to = 0;
ccb->channel_spec[channel].ta = 1;
ccb->channel_spec[channel].th = 0;
ccb->channel_spec[channel].ri = 1; /* Receive init */
ccb->channel_spec[channel].ro = 0;
ccb->channel_spec[channel].ra = 1;
ccb->action_spec = CCB_ACTIONSPEC_ICO | (channel << 8);
writel(CMD_ARPCM, MUNICH_VIRT(CMD)); /* Start the channel init */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
spin_unlock_irqrestore(&mister_lock, flags);
stat = 0; /* Wait for the action to complete max. 1 second */
jiffs = jiffies;
while (!((stat = readl(MUNICH_VIRT(STAT))) & (STAT_PCMA | STAT_PCMF)) && time_before(jiffies, jiffs + HZ))
{
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
}
if (stat & STAT_PCMF)
{
printk(KERN_ERR "MUNICH_open: %s: channel open ARPCM failed\n",
board->devname);
writel(readl(MUNICH_VIRT(STAT)) & STAT_PCMF, MUNICH_VIRT(STAT));
MUNICH_close(dev);
return -EAGAIN;
}
else if (!(stat & STAT_PCMA))
{
printk(KERN_ERR "MUNICH_open: %s: channel open ARPCM timeout\n",
board->devname);
MUNICH_close(dev);
return -EIO;
}
writel(readl(MUNICH_VIRT(STAT)) & STAT_PCMA, MUNICH_VIRT(STAT));
// printk("MUNICH_open: %s: succesful channel open took %ld jiffies\n", board->devname, jiffies-jiffs);
spin_lock_irqsave(&mister_lock, flags);
ccb->channel_spec[channel].nitbs = 0; /* once ITBS defined, these must be 0 */
ccb->channel_spec[channel].itbs = 0;
if (board->isx21)
{
init_timer(&board->modemline_timer);
board->modemline_timer.data = (unsigned long)board;
board->modemline_timer.function = pcicom_modemline;
board->modemline_timer.expires = jiffies + HZ;
add_timer((struct timer_list *)&board->modemline_timer);
}
/* It is done. Declare that we're open: */
hw->busy = 0; /* It may be 1 if the frame at Tx init already ended, but it is not */
/* a real problem: we compute hw->busy on every interrupt */
hw->rafutott = 0;
ch->init_status |= HW_OPEN;
/* Initialize line state: */
if (board->lineup)
ch->line_status |= LINE_UP;
else
ch->line_status &= ~LINE_UP;
/* Remove w attribute from /proc files associated to hw parameters:
no write when the device is open */
for (; procfile; procfile = procfile->next)
if (strcmp(procfile->name, FILENAME_BOARDNUM) == 0 ||
strcmp(procfile->name, FILENAME_TIMESLOTS) == 0)
procfile->mode = S_IFREG | 0444;
spin_unlock_irqrestore(&mister_lock, flags);
return 0;
}
/*
* Hardware close routine.
* Called by comx (upper) layer when the user wants to bring down the interface
* with ifconfig.
* We also call it from MUNICH_open, if the open fails.
* Brings down hardware, frees resources, stops receiver
* Returns 0 on OK, or standard error value on error.
*/
static int MUNICH_close(struct net_device *dev)
{
struct comx_channel *ch = netdev_priv(dev);
struct slicecom_privdata *hw = ch->HW_privdata;
struct proc_dir_entry *procfile = ch->procdir->subdir;
munich_board_t *board;
munich_ccb_t *ccb;
u32 *bar1;
u32 timeslots = hw->timeslots;
int stat, i, channel = hw->channel;
unsigned long jiffs;
board = hw->boardnum + (ch->hardware == &pcicomhw ? pcicom_boards : slicecom_boards);
ccb = board->ccb;
bar1 = board->bar1;
if (board->isx21)
del_timer((struct timer_list *)&board->modemline_timer);
spin_lock_irqsave(&mister_lock, flags);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
/* Disable receiver for the channel: */
for (i = 0; i < 32; i++)
if ((1 << i) & timeslots)
{
ccb->timeslot_spec[i].tti = 1;
ccb->timeslot_spec[i].txfillmask = 0; /* just to be double-sure :) */
ccb->timeslot_spec[i].rti = 1;
ccb->timeslot_spec[i].rxfillmask = 0;
}
if (!board->isx21) rework_idle_channels(dev);
ccb->channel_spec[channel].ti = 0; /* Receive off, Transmit off */
ccb->channel_spec[channel].to = 1;
ccb->channel_spec[channel].ta = 0;
ccb->channel_spec[channel].th = 1;
ccb->channel_spec[channel].ri = 0;
ccb->channel_spec[channel].ro = 1;
ccb->channel_spec[channel].ra = 0;
board->twins[channel] = NULL;
ccb->action_spec = CCB_ACTIONSPEC_IN | (channel << 8);
writel(CMD_ARPCM, MUNICH_VIRT(CMD));
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
spin_unlock_irqrestore(&mister_lock, flags);
stat = 0;
jiffs = jiffies;
while (!((stat = readl(MUNICH_VIRT(STAT))) & (STAT_PCMA | STAT_PCMF)) && time_before(jiffies, jiffs + HZ))
{
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
}
if (stat & STAT_PCMF)
{
printk(KERN_ERR
"MUNICH_close: %s: FATAL: channel off ARPCM failed, not closing!\n",
dev->name);
writel(readl(MUNICH_VIRT(STAT)) & STAT_PCMF, MUNICH_VIRT(STAT));
/* If we return success, the privdata (and the descriptor list) will be freed */
return -EIO;
}
else if (!(stat & STAT_PCMA))
printk(KERN_ERR "MUNICH_close: %s: channel off ARPCM timeout\n",
board->devname);
writel(readl(MUNICH_VIRT(STAT)) & STAT_PCMA, MUNICH_VIRT(STAT));
// printk("MUNICH_close: %s: channel off took %ld jiffies\n", board->devname, jiffies-jiffs);
spin_lock_irqsave(&mister_lock, flags);
if (board->use_count) board->use_count--;
if (!board->use_count) /* we were the last user of the board */
{
printk("MUNICH_close: bringing down board %s\n", board->devname);
/* program down the board: */
writel(0x0000FF7F, MUNICH_VIRT(IMASK)); /* do not send any interrupts */
writel(0, MUNICH_VIRT(CMD)); /* stop the timer if someone started it */
writel(~0U, MUNICH_VIRT(STAT)); /* if an interrupt came between the cli()-sti(), quiet it */
if (ch->hardware == &pcicomhw)
writel(0x1400, MUNICH_VIRT(GPDATA));
/* Put the board into 'reset' state: */
pci_write_config_dword(board->pci, MUNICH_PCI_PCIRES, 0xe0000);
/* Free irq and other resources: */
if (board->irq)
free_irq(board->irq, (void *)board); /* Ha nem inicializalta magat, akkor meg nincs irq */
board->irq = 0;
/* Free CCB and the interrupt queues */
if (board->ccb) kfree((void *)board->ccb);
if (board->tiq) kfree((void *)board->tiq);
if (board->riq) kfree((void *)board->riq);
if (board->piq) kfree((void *)board->piq);
board->ccb = NULL;
board->tiq = board->riq = board->piq = NULL;
}
/* Enable setting of hw parameters */
for (; procfile; procfile = procfile->next)
if (strcmp(procfile->name, FILENAME_BOARDNUM) == 0 ||
strcmp(procfile->name, FILENAME_TIMESLOTS) == 0)
procfile->mode = S_IFREG | 0644;
/* We're not open anymore */
ch->init_status &= ~HW_OPEN;
spin_unlock_irqrestore(&mister_lock, flags);
return 0;
}
/*
* Give (textual) status information.
* The text it returns will be a part of what appears when the user does a
* cat /proc/comx/comx[n]/status
* Don't write more than PAGESIZE.
* Return value: number of bytes written (length of the string, incl. 0)
*/
static int MUNICH_minden(struct net_device *dev, char *page)
{
struct comx_channel *ch = netdev_priv(dev);
struct slicecom_privdata *hw = ch->HW_privdata;
munich_board_t *board;
struct net_device *devp;
u8 *lbi;
e1_stats_t *curr_int, *prev_int;
e1_stats_t last4, last96; /* sum of last 4, resp. last 96 intervals */
unsigned *sump, /* running pointer for the sum data */
*p; /* running pointer for the interval data */
int len = 0;
u8 frs0, frs1;
u8 fmr2;
int i, j;
u32 timeslots;
board = hw->boardnum + (ch->hardware == &pcicomhw ? pcicom_boards : slicecom_boards);
lbi = board->lbi;
curr_int = &board->intervals[board->current_interval];
prev_int =
&board->
intervals[(board->current_interval + SLICECOM_BOARD_INTERVALS_SIZE -
1) % SLICECOM_BOARD_INTERVALS_SIZE];
if (!board->isx21)
{
frs0 = readb(lbi + FRS0);
fmr2 = readb(lbi + FMR2);
len += scnprintf(page + len, PAGE_SIZE - len, "Controller status:\n");
if (frs0 == 0)
len += scnprintf(page + len, PAGE_SIZE - len, "\tNo alarms\n");
else
{
if (frs0 & FRS0_LOS)
len += scnprintf(page + len, PAGE_SIZE - len, "\tLoss Of Signal\n");
else
{
if (frs0 & FRS0_AIS)
len += scnprintf(page + len, PAGE_SIZE - len,
"\tAlarm Indication Signal\n");
else
{
if (frs0 & FRS0_AUXP)
len += scnprintf(page + len, PAGE_SIZE - len,
"\tAuxiliary Pattern Indication\n");
if (frs0 & FRS0_LFA)
len += scnprintf(page + len, PAGE_SIZE - len,
"\tLoss of Frame Alignment\n");
else
{
if (frs0 & FRS0_RRA)
len += scnprintf(page + len, PAGE_SIZE - len,
"\tReceive Remote Alarm\n");
/* You can't set this framing with the /proc interface, but it */
/* may be good to have here this alarm if you set it by hand: */
if ((board->framing == SLICECOM_FRAMING_CRC4) &&
(frs0 & FRS0_LMFA))
len += scnprintf(page + len, PAGE_SIZE - len,
"\tLoss of CRC4 Multiframe Alignment\n");
if (((fmr2 & 0xc0) == 0xc0) && (frs0 & FRS0_NMF))
len += scnprintf(page + len, PAGE_SIZE - len,
"\tNo CRC4 Multiframe alignment Found after 400 msec\n");
}
}
}
}
frs1 = readb(lbi + FRS1);
if (FRS1_XLS & frs1)
len += scnprintf(page + len, PAGE_SIZE - len,
"\tTransmit Line Short\n");
/* debug Rx ring: DEL: - vagy meghagyni, de akkor legyen kicsit altalanosabb */
}
len += scnprintf(page + len, PAGE_SIZE - len, "Rx ring:\n");
len += scnprintf(page + len, PAGE_SIZE - len, "\trafutott: %d\n", hw->rafutott);
len += scnprintf(page + len, PAGE_SIZE - len,
"\tlastcheck: %ld, jiffies: %ld\n", board->lastcheck, jiffies);
len += scnprintf(page + len, PAGE_SIZE - len, "\tbase: %08x\n",
(u32) virt_to_phys(&hw->rx_desc[0]));
len += scnprintf(page + len, PAGE_SIZE - len, "\trx_desc_ptr: %d\n",
hw->rx_desc_ptr);
len += scnprintf(page + len, PAGE_SIZE - len, "\trx_desc_ptr: %08x\n",
(u32) virt_to_phys(&hw->rx_desc[hw->rx_desc_ptr]));
len += scnprintf(page + len, PAGE_SIZE - len, "\thw_curr_ptr: %08x\n",
board->ccb->current_rx_desc[hw->channel]);
for (i = 0; i < RX_DESC_MAX; i++)
len += scnprintf(page + len, PAGE_SIZE - len, "\t%08x %08x %08x %08x\n",
*((u32 *) & hw->rx_desc[i] + 0),
*((u32 *) & hw->rx_desc[i] + 1),
*((u32 *) & hw->rx_desc[i] + 2),
*((u32 *) & hw->rx_desc[i] + 3));
if (!board->isx21)
{
len += scnprintf(page + len, PAGE_SIZE - len,
"Interfaces using this board: (channel-group, interface, timeslots)\n");
for (i = 0; i < 32; i++)
{
devp = board->twins[i];
if (devp != NULL)
{
timeslots =
((struct slicecom_privdata *)((struct comx_channel *)devp->
priv)->HW_privdata)->
timeslots;
len += scnprintf(page + len, PAGE_SIZE - len, "\t%2d %s: ", i,
devp->name);
for (j = 0; j < 32; j++)
if ((1 << j) & timeslots)
len += scnprintf(page + len, PAGE_SIZE - len, "%d ", j);
len += scnprintf(page + len, PAGE_SIZE - len, "\n");
}
}
}
len += scnprintf(page + len, PAGE_SIZE - len, "Interrupt work histogram:\n");
for (i = 0; i < MAX_WORK; i++)
len += scnprintf(page + len, PAGE_SIZE - len, "hist[%2d]: %8u%c", i,
board->histogram[i], (i &&
((i + 1) % 4 == 0 ||
i == MAX_WORK - 1)) ? '\n' : ' ');
len += scnprintf(page + len, PAGE_SIZE - len, "Tx ring histogram:\n");
for (i = 0; i < TX_DESC_MAX; i++)
len += scnprintf(page + len, PAGE_SIZE - len, "hist[%2d]: %8u%c", i,
hw->tx_ring_hist[i], (i &&
((i + 1) % 4 == 0 ||
i ==
TX_DESC_MAX - 1)) ? '\n' : ' ');
if (!board->isx21)
{
memset((void *)&last4, 0, sizeof(last4));
memset((void *)&last96, 0, sizeof(last96));
/* Calculate the sum of last 4 intervals: */
for (i = 1; i <= 4; i++)
{
p = (unsigned *)&board->intervals[(board->current_interval +
SLICECOM_BOARD_INTERVALS_SIZE -
i) % SLICECOM_BOARD_INTERVALS_SIZE];
sump = (unsigned *)&last4;
for (j = 0; j < (sizeof(e1_stats_t) / sizeof(unsigned)); j++)
sump[j] += p[j];
}
/* Calculate the sum of last 96 intervals: */
for (i = 1; i <= 96; i++)
{
p = (unsigned *)&board->intervals[(board->current_interval +
SLICECOM_BOARD_INTERVALS_SIZE -
i) % SLICECOM_BOARD_INTERVALS_SIZE];
sump = (unsigned *)&last96;
for (j = 0; j < (sizeof(e1_stats_t) / sizeof(unsigned)); j++)
sump[j] += p[j];
}
len += scnprintf(page + len, PAGE_SIZE - len,
"Data in current interval (%d seconds elapsed):\n",
board->elapsed_seconds);
len += scnprintf(page + len, PAGE_SIZE - len,
" %d Line Code Violations, %d Path Code Violations, %d E-Bit Errors\n",
curr_int->line_code_violations,
curr_int->path_code_violations, curr_int->e_bit_errors);
len += scnprintf(page + len, PAGE_SIZE - len,
" %d Slip Secs, %d Fr Loss Secs, %d Line Err Secs, %d Degraded Mins\n",
curr_int->slip_secs, curr_int->fr_loss_secs,
curr_int->line_err_secs, curr_int->degraded_mins);
len += scnprintf(page + len, PAGE_SIZE - len,
" %d Errored Secs, %d Bursty Err Secs, %d Severely Err Secs, %d Unavail Secs\n",
curr_int->errored_secs, curr_int->bursty_err_secs,
curr_int->severely_err_secs, curr_int->unavail_secs);
len += scnprintf(page + len, PAGE_SIZE - len,
"Data in Interval 1 (15 minutes):\n");
len += scnprintf(page + len, PAGE_SIZE - len,
" %d Line Code Violations, %d Path Code Violations, %d E-Bit Errors\n",
prev_int->line_code_violations,
prev_int->path_code_violations, prev_int->e_bit_errors);
len += scnprintf(page + len, PAGE_SIZE - len,
" %d Slip Secs, %d Fr Loss Secs, %d Line Err Secs, %d Degraded Mins\n",
prev_int->slip_secs, prev_int->fr_loss_secs,
prev_int->line_err_secs, prev_int->degraded_mins);
len += scnprintf(page + len, PAGE_SIZE - len,
" %d Errored Secs, %d Bursty Err Secs, %d Severely Err Secs, %d Unavail Secs\n",
prev_int->errored_secs, prev_int->bursty_err_secs,
prev_int->severely_err_secs, prev_int->unavail_secs);
len += scnprintf(page + len, PAGE_SIZE - len,
"Data in last 4 intervals (1 hour):\n");
len += scnprintf(page + len, PAGE_SIZE - len,
" %d Line Code Violations, %d Path Code Violations, %d E-Bit Errors\n",
last4.line_code_violations, last4.path_code_violations,
last4.e_bit_errors);
len += scnprintf(page + len, PAGE_SIZE - len,
" %d Slip Secs, %d Fr Loss Secs, %d Line Err Secs, %d Degraded Mins\n",
last4.slip_secs, last4.fr_loss_secs, last4.line_err_secs,
last4.degraded_mins);
len += scnprintf(page + len, PAGE_SIZE - len,
" %d Errored Secs, %d Bursty Err Secs, %d Severely Err Secs, %d Unavail Secs\n",
last4.errored_secs, last4.bursty_err_secs,
last4.severely_err_secs, last4.unavail_secs);
len += scnprintf(page + len, PAGE_SIZE - len,
"Data in last 96 intervals (24 hours):\n");
len += scnprintf(page + len, PAGE_SIZE - len,
" %d Line Code Violations, %d Path Code Violations, %d E-Bit Errors\n",
last96.line_code_violations, last96.path_code_violations,
last96.e_bit_errors);
len += scnprintf(page + len, PAGE_SIZE - len,
" %d Slip Secs, %d Fr Loss Secs, %d Line Err Secs, %d Degraded Mins\n",
last96.slip_secs, last96.fr_loss_secs,
last96.line_err_secs, last96.degraded_mins);
len += scnprintf(page + len, PAGE_SIZE - len,
" %d Errored Secs, %d Bursty Err Secs, %d Severely Err Secs, %d Unavail Secs\n",
last96.errored_secs, last96.bursty_err_secs,
last96.severely_err_secs, last96.unavail_secs);
}
// len +=scnprintf( page + len, PAGE_SIZE - len, "Special events:\n" );
// len +=scnprintf( page + len, PAGE_SIZE - len, "\tstat_pri/missed: %u / %u\n", board->stat_pri_races, board->stat_pri_races_missed );
// len +=scnprintf( page + len, PAGE_SIZE - len, "\tstat_pti/missed: %u / %u\n", board->stat_pti_races, board->stat_pti_races_missed );
return len;
}
/*
* Memory dump function. Not used currently.
*/
static int BOARD_dump(struct net_device *dev)
{
printk
("BOARD_dump() requested. It is unimplemented, it should not be called\n");
return (-1);
}
/*
* /proc file read function for the files registered by this module.
* This function is called by the procfs implementation when a user
* wants to read from a file registered by this module.
* page is the workspace, start should point to the real start of data,
* off is the file offset, data points to the file's proc_dir_entry
* structure.
* Returns the number of bytes copied to the request buffer.
*/
static int munich_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 = netdev_priv(dev);
struct slicecom_privdata *hw = ch->HW_privdata;
munich_board_t *board;
int len = 0, i;
u32 timeslots = hw->timeslots;
board = hw->boardnum + (ch->hardware == &pcicomhw ? pcicom_boards : slicecom_boards);
if (!strcmp(file->name, FILENAME_BOARDNUM))
len = sprintf(page, "%d\n", hw->boardnum);
else if (!strcmp(file->name, FILENAME_TIMESLOTS))
{
for (i = 0; i < 32; i++)
if ((1 << i) & timeslots)
len += scnprintf(page + len, PAGE_SIZE - len, "%d ", i);
len += scnprintf(page + len, PAGE_SIZE - len, "\n");
}
else if (!strcmp(file->name, FILENAME_FRAMING))
{
i = 0;
while (slicecom_framings[i].value &&
slicecom_framings[i].value != board->framing)
i++;
len += scnprintf(page + len, PAGE_SIZE - len, "%s\n",
slicecom_framings[i].name);
}
else if (!strcmp(file->name, FILENAME_LINECODE))
{
i = 0;
while (slicecom_linecodes[i].value &&
slicecom_linecodes[i].value != board->linecode)
i++;
len += scnprintf(page + len, PAGE_SIZE - len, "%s\n",
slicecom_linecodes[i].name);
}
else if (!strcmp(file->name, FILENAME_CLOCK_SOURCE))
{
i = 0;
while (slicecom_clock_sources[i].value &&
slicecom_clock_sources[i].value != board->clock_source)
i++;
len +=
scnprintf(page + len, PAGE_SIZE - len, "%s\n",
slicecom_clock_sources[i].name);
}
else if (!strcmp(file->name, FILENAME_LOOPBACK))
{
i = 0;
while (slicecom_loopbacks[i].value &&
slicecom_loopbacks[i].value != board->loopback)
i++;
len += scnprintf(page + len, PAGE_SIZE - len, "%s\n",
slicecom_loopbacks[i].name);
}
/* We set permissions to write-only for REG and LBIREG, but root can read them anyway: */
else if (!strcmp(file->name, FILENAME_REG))
{
len += scnprintf(page + len, PAGE_SIZE - len,
"%s: " FILENAME_REG ": write-only file\n", dev->name);
}
else if (!strcmp(file->name, FILENAME_LBIREG))
{
len += scnprintf(page + len, PAGE_SIZE - len,
"%s: " FILENAME_LBIREG ": write-only file\n", dev->name);
}
else
{
printk("slicecom_read_proc: internal error, filename %s\n", file->name);
return -EBADF;
}
/* file handling administration: count eof status, offset, start address
and count: */
if (off >= len)
{
*eof = 1;
return 0;
}
*start = page + off;
if (count >= len - off)
*eof = 1;
return min((off_t) count, (off_t) len - off);
}
/*
* Write function for /proc files registered by us.
* See the comment on read function above.
* Beware! buffer is in userspace!!!
* Returns the number of bytes written
*/
static int munich_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 = netdev_priv(dev);
struct slicecom_privdata *hw = ch->HW_privdata;
munich_board_t *board;
unsigned long ts, tmp_boardnum;
u32 tmp_timeslots = 0;
char *page, *p;
int i;
board = hw->boardnum + (ch->hardware == &pcicomhw ? pcicom_boards : slicecom_boards);
/* Paranoia checking: */
if (PDE(file->f_dentry->d_inode) != entry)
{
printk(KERN_ERR "munich_write_proc: file <-> data internal error\n");
return -EIO;
}
/* Request tmp buffer */
if (!(page = (char *)__get_free_page(GFP_KERNEL)))
return -ENOMEM;
/* Copy user data and cut trailing \n */
if (copy_from_user(page, buffer, count = min(count, PAGE_SIZE))) {
free_page((unsigned long)page);
return -EFAULT;
}
if (*(page + count - 1) == '\n')
*(page + count - 1) = 0;
*(page + PAGE_SIZE - 1) = 0;
if (!strcmp(entry->name, FILENAME_BOARDNUM))
{
tmp_boardnum = simple_strtoul(page, NULL, 0);
if (0 <= tmp_boardnum && tmp_boardnum < MAX_BOARDS)
hw->boardnum = tmp_boardnum;
else
{
printk("%s: " FILENAME_BOARDNUM " range is 0...%d\n", dev->name,
MAX_BOARDS - 1);
free_page((unsigned long)page);
return -EINVAL;
}
}
else if (!strcmp(entry->name, FILENAME_TIMESLOTS))
{
p = page;
while (*p)
{
if (isspace(*p))
p++;
else
{
ts = simple_strtoul(p, &p, 10); /* base = 10: Don't read 09 as an octal number */
/* ts = 0 ha nem tudta beolvasni a stringet, erre egy kicsit epitek itt: */
if (0 <= ts && ts < 32)
{
tmp_timeslots |= (1 << ts);
}
else
{
printk("%s: " FILENAME_TIMESLOTS " range is 1...31\n",
dev->name);
free_page((unsigned long)page);
return -EINVAL;
}
}
}
hw->timeslots = tmp_timeslots;
}
else if (!strcmp(entry->name, FILENAME_FRAMING))
{
i = 0;
while (slicecom_framings[i].value &&
strncmp(slicecom_framings[i].name, page,
strlen(slicecom_framings[i].name)))
i++;
if (!slicecom_framings[i].value)
{
printk("slicecom: %s: Invalid " FILENAME_FRAMING " '%s'\n",
dev->name, page);
free_page((unsigned long)page);
return -EINVAL;
}
else
{ /*
* If somebody says:
* echo >boardnum 0
* echo >framing no-crc4
* echo >boardnum 1
* - when the framing was set, hw->boardnum was 0, so it would set the framing for board 0
* Workaround: allow to set it only if interface is administrative UP
*/
if (netif_running(dev))
slicecom_set_framing(hw->boardnum, slicecom_framings[i].value);
else
{
printk("%s: " FILENAME_FRAMING
" can not be set while the interface is DOWN\n",
dev->name);
free_page((unsigned long)page);
return -EINVAL;
}
}
}
else if (!strcmp(entry->name, FILENAME_LINECODE))
{
i = 0;
while (slicecom_linecodes[i].value &&
strncmp(slicecom_linecodes[i].name, page,
strlen(slicecom_linecodes[i].name)))
i++;
if (!slicecom_linecodes[i].value)
{
printk("slicecom: %s: Invalid " FILENAME_LINECODE " '%s'\n",
dev->name, page);
free_page((unsigned long)page);
return -EINVAL;
}
else
{ /*
* Allow to set it only if interface is administrative UP,
* for the same reason as FILENAME_FRAMING
*/
if (netif_running(dev))
slicecom_set_linecode(hw->boardnum,
slicecom_linecodes[i].value);
else
{
printk("%s: " FILENAME_LINECODE
" can not be set while the interface is DOWN\n",
dev->name);
free_page((unsigned long)page);
return -EINVAL;
}
}
}
else if (!strcmp(entry->name, FILENAME_CLOCK_SOURCE))
{
i = 0;
while (slicecom_clock_sources[i].value &&
strncmp(slicecom_clock_sources[i].name, page,
strlen(slicecom_clock_sources[i].name)))
i++;
if (!slicecom_clock_sources[i].value)
{
printk("%s: Invalid " FILENAME_CLOCK_SOURCE " '%s'\n", dev->name,
page);
free_page((unsigned long)page);
return -EINVAL;
}
else
{ /*
* Allow to set it only if interface is administrative UP,
* for the same reason as FILENAME_FRAMING
*/
if (netif_running(dev))
slicecom_set_clock_source(hw->boardnum,
slicecom_clock_sources[i].value);
else
{
printk("%s: " FILENAME_CLOCK_SOURCE
" can not be set while the interface is DOWN\n",
dev->name);
free_page((unsigned long)page);
return -EINVAL;
}
}
}
else if (!strcmp(entry->name, FILENAME_LOOPBACK))
{
i = 0;
while (slicecom_loopbacks[i].value &&
strncmp(slicecom_loopbacks[i].name, page,
strlen(slicecom_loopbacks[i].name)))
i++;
if (!slicecom_loopbacks[i].value)
{
printk("%s: Invalid " FILENAME_LOOPBACK " '%s'\n", dev->name, page);
free_page((unsigned long)page);
return -EINVAL;
}
else
{ /*
* Allow to set it only if interface is administrative UP,
* for the same reason as FILENAME_FRAMING
*/
if (netif_running(dev))
slicecom_set_loopback(hw->boardnum,
slicecom_loopbacks[i].value);
else
{
printk("%s: " FILENAME_LOOPBACK
" can not be set while the interface is DOWN\n",
dev->name);
free_page((unsigned long)page);
return -EINVAL;
}
}
}
else if (!strcmp(entry->name, FILENAME_REG))
{ /* DEL: 'reg' csak tmp */
char *p;
u32 *bar1 = board->bar1;
reg = simple_strtoul(page, &p, 0);
reg_ertek = simple_strtoul(p + 1, NULL, 0);
if (reg < 0x100)
{
printk("reg(0x%02x) := 0x%08x jiff: %lu\n", reg, reg_ertek, jiffies);
writel(reg_ertek, MUNICH_VIRT(reg >> 2));
}
else
{
printk("reg(0x%02x) is 0x%08x jiff: %lu\n", reg - 0x100,
readl(MUNICH_VIRT((reg - 0x100) >> 2)), jiffies);
}
}
else if (!strcmp(entry->name, FILENAME_LBIREG))
{ /* DEL: 'lbireg' csak tmp */
char *p;
u8 *lbi = board->lbi;
lbireg = simple_strtoul(page, &p, 0);
lbireg_ertek = simple_strtoul(p + 1, NULL, 0);
if (lbireg < 0x100)
{
printk("lbireg(0x%02x) := 0x%02x jiff: %lu\n", lbireg,
lbireg_ertek, jiffies);
writeb(lbireg_ertek, lbi + lbireg);
}
else
printk("lbireg(0x%02x) is 0x%02x jiff: %lu\n", lbireg - 0x100,
readb(lbi + lbireg - 0x100), jiffies);
}
else
{
printk(KERN_ERR "munich_write_proc: internal error, filename %s\n",
entry->name);
free_page((unsigned long)page);
return -EBADF;
}
/* Don't forget to free the workspace */
free_page((unsigned long)page);
return count;
}
/*
* Boardtype init function.
* Called by the comx (upper) layer, when you set boardtype.
* Allocates resources associated to using munich board for this device,
* initializes ch_struct pointers etc.
* Returns 0 on success and standard error codes on error.
*/
static int init_escape(struct comx_channel *ch)
{
kfree(ch->HW_privdata);
return -EIO;
}
static int BOARD_init(struct net_device *dev)
{
struct comx_channel *ch = netdev_priv(dev);
struct slicecom_privdata *hw;
struct proc_dir_entry *new_file;
/* Alloc data for private structure */
if ((ch->HW_privdata =
kmalloc(sizeof(struct slicecom_privdata), GFP_KERNEL)) == NULL)
return -ENOMEM;
memset(hw = ch->HW_privdata, 0, sizeof(struct slicecom_privdata));
/* Register /proc files */
if ((new_file = create_proc_entry(FILENAME_BOARDNUM, S_IFREG | 0644,
ch->procdir)) == NULL)
return init_escape(ch);
new_file->data = (void *)new_file;
new_file->read_proc = &munich_read_proc;
new_file->write_proc = &munich_write_proc;
// new_file->proc_iops = &comx_normal_inode_ops;
new_file->nlink = 1;
if (ch->hardware == &slicecomhw)
{
if ((new_file = create_proc_entry(FILENAME_TIMESLOTS, S_IFREG | 0644,
ch->procdir)) == NULL)
return init_escape(ch);
new_file->data = (void *)new_file;
new_file->read_proc = &munich_read_proc;
new_file->write_proc = &munich_write_proc;
// new_file->proc_iops = &comx_normal_inode_ops;
new_file->nlink = 1;
if ((new_file = create_proc_entry(FILENAME_FRAMING, S_IFREG | 0644,
ch->procdir)) == NULL)
return init_escape(ch);
new_file->data = (void *)new_file;
new_file->read_proc = &munich_read_proc;
new_file->write_proc = &munich_write_proc;
// new_file->proc_iops = &comx_normal_inode_ops;
new_file->nlink = 1;
if ((new_file = create_proc_entry(FILENAME_LINECODE, S_IFREG | 0644,
ch->procdir)) == NULL)
return init_escape(ch);
new_file->data = (void *)new_file;
new_file->read_proc = &munich_read_proc;
new_file->write_proc = &munich_write_proc;
// new_file->proc_iops = &comx_normal_inode_ops;
new_file->nlink = 1;
if ((new_file = create_proc_entry(FILENAME_CLOCK_SOURCE, S_IFREG | 0644,
ch->procdir)) == NULL)
return init_escape(ch);
new_file->data = (void *)new_file;
new_file->read_proc = &munich_read_proc;
new_file->write_proc = &munich_write_proc;
// new_file->proc_iops = &comx_normal_inode_ops;
new_file->nlink = 1;
if ((new_file = create_proc_entry(FILENAME_LOOPBACK, S_IFREG | 0644,
ch->procdir)) == NULL)
return init_escape(ch);
new_file->data = (void *)new_file;
new_file->read_proc = &munich_read_proc;
new_file->write_proc = &munich_write_proc;
// new_file->proc_iops = &comx_normal_inode_ops;
new_file->nlink = 1;
}
/* DEL: ez itt csak fejlesztesi celokra!! */
if ((new_file = create_proc_entry(FILENAME_REG, S_IFREG | 0200, ch->procdir)) == NULL)
return init_escape(ch);
new_file->data = (void *)new_file;
new_file->read_proc = &munich_read_proc;
new_file->write_proc = &munich_write_proc;
// new_file->proc_iops = &comx_normal_inode_ops;
new_file->nlink = 1;
/* DEL: ez itt csak fejlesztesi celokra!! */
if ((new_file = create_proc_entry(FILENAME_LBIREG, S_IFREG | 0200,
ch->procdir)) == NULL)
return init_escape(ch);
new_file->data = (void *)new_file;
new_file->read_proc = &munich_read_proc;
new_file->write_proc = &munich_write_proc;
// new_file->proc_iops = &comx_normal_inode_ops;
new_file->nlink = 1;
/* Fill in ch_struct hw specific pointers: */
ch->HW_txe = MUNICH_txe;
ch->HW_open = MUNICH_open;
ch->HW_close = MUNICH_close;
ch->HW_send_packet = MUNICH_send_packet;
#ifndef COMX_NEW
ch->HW_minden = MUNICH_minden;
#else
ch->HW_statistics = MUNICH_minden;
#endif
hw->boardnum = SLICECOM_BOARDNUM_DEFAULT;
hw->timeslots = ch->hardware == &pcicomhw ? 0xffffffff : 2;
/* O.K. Count one more user on this module */
MOD_INC_USE_COUNT;
return 0;
}
/*
* Boardtype exit function.
* Called by the comx (upper) layer, when you clear boardtype from munich.
* Frees resources associated to using munich board for this device,
* resets ch_struct pointers etc.
*/
static int BOARD_exit(struct net_device *dev)
{
struct comx_channel *ch = netdev_priv(dev);
/* Free private data area */
// board = hw->boardnum + (ch->hardware == &pcicomhw ? pcicom_boards : slicecom_boards);
kfree(ch->HW_privdata);
/* Remove /proc files */
remove_proc_entry(FILENAME_BOARDNUM, ch->procdir);
if (ch->hardware == &slicecomhw)
{
remove_proc_entry(FILENAME_TIMESLOTS, ch->procdir);
remove_proc_entry(FILENAME_FRAMING, ch->procdir);
remove_proc_entry(FILENAME_LINECODE, ch->procdir);
remove_proc_entry(FILENAME_CLOCK_SOURCE, ch->procdir);
remove_proc_entry(FILENAME_LOOPBACK, ch->procdir);
}
remove_proc_entry(FILENAME_REG, ch->procdir);
remove_proc_entry(FILENAME_LBIREG, ch->procdir);
/* Minus one user for the module accounting */
MOD_DEC_USE_COUNT;
return 0;
}
static struct comx_hardware slicecomhw =
{
"slicecom",
#ifdef COMX_NEW
VERSION,
#endif
BOARD_init,
BOARD_exit,
BOARD_dump,
NULL
};
static struct comx_hardware pcicomhw =
{
"pcicom",
#ifdef COMX_NEW
VERSION,
#endif
BOARD_init,
BOARD_exit,
BOARD_dump,
NULL
};
/* Module management */
static int __init init_mister(void)
{
printk(VERSIONSTR);
comx_register_hardware(&slicecomhw);
comx_register_hardware(&pcicomhw);
return munich_probe();
}
static void __exit cleanup_mister(void)
{
int i;
comx_unregister_hardware("slicecom");
comx_unregister_hardware("pcicom");
for (i = 0; i < MAX_BOARDS; i++)
{
if (slicecom_boards[i].bar1)
iounmap((void *)slicecom_boards[i].bar1);
if (slicecom_boards[i].lbi)
iounmap((void *)slicecom_boards[i].lbi);
if (pcicom_boards[i].bar1)
iounmap((void *)pcicom_boards[i].bar1);
if (pcicom_boards[i].lbi)
iounmap((void *)pcicom_boards[i].lbi);
}
}
module_init(init_mister);
module_exit(cleanup_mister);
/*
* 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>
*
* Contributors:
* Arnaldo Carvalho de Melo <acme@conectiva.com.br> (0.73)
*
* 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
*
* Version 0.73 (00/08/15)
* - resource release on failure at fr_master_init and
* fr_slave_init
*/
#define VERSION "0.73"
#include <linux/module.h>
#include <linux/types.h>
#include <linux/jiffies.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.4.X");
MODULE_LICENSE("GPL");
#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;
init_timer(&fr->keepa_timer);
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);
if (!newskb)
return -ENOMEM;
newskb->dev=fr->master;
dev_queue_xmit(newskb);
ch->stats.tx_bytes += skb->len;
ch->stats.tx_packets++;
dev_kfree_skb(skb);
} 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_t(int, 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 (!(page = (char *)__get_free_page(GFP_KERNEL))) {
return -ENOMEM;
}
if (copy_from_user(page, buffer, count)) {
free_page((unsigned long)page);
return -EFAULT;
}
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);
count = -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) {
goto cleanup_LINE_privdata;
}
new_file->data = (void *)new_file;
new_file->read_proc = &fr_read_proc;
new_file->write_proc = &fr_write_proc;
new_file->size = 5;
new_file->nlink = 1;
if ((new_file = create_proc_entry(FILENAME_KEEPALIVE, S_IFREG | 0644,
ch->procdir)) == NULL) {
goto cleanup_filename_dlci;
}
new_file->data = (void *)new_file;
new_file->read_proc = &fr_read_proc;
new_file->write_proc = &fr_write_proc;
new_file->size = 4;
new_file->nlink = 1;
fr_set_keepalive(dev, 0);
MOD_INC_USE_COUNT;
return 0;
cleanup_filename_dlci:
remove_proc_entry(FILENAME_DLCI, ch->procdir);
cleanup_LINE_privdata:
kfree(fr);
return -EIO;
}
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) {
goto cleanup_LINE_privdata;
}
new_file->data = (void *)new_file;
new_file->read_proc = &fr_read_proc;
new_file->write_proc = &fr_write_proc;
new_file->size = 5;
new_file->nlink = 1;
if ((new_file = create_proc_entry(FILENAME_MASTER, S_IFREG | 0644,
ch->procdir)) == NULL) {
goto cleanup_filename_dlci;
}
new_file->data = (void *)new_file;
new_file->read_proc = &fr_read_proc;
new_file->write_proc = &fr_write_proc;
new_file->size = 10;
new_file->nlink = 1;
MOD_INC_USE_COUNT;
return 0;
cleanup_filename_dlci:
remove_proc_entry(FILENAME_DLCI, ch->procdir);
cleanup_LINE_privdata:
kfree(fr);
return -EIO;
}
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 = {
.name = "frad",
.version = VERSION,
.encap_type = ARPHRD_FRAD,
.line_init = fr_master_init,
.line_exit = fr_exit,
};
static struct comx_protocol fr_slave_protocol = {
.name = "ietf-ip",
.version = VERSION,
.encap_type = ARPHRD_DLCI,
.line_init = fr_slave_init,
.line_exit = fr_exit,
};
static struct comx_hardware fr_dlci = {
.name = "dlci",
.version = VERSION,
.hw_init = dlci_init,
.hw_exit = dlci_exit,
.hw_dump = dlci_dump,
};
static 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);
}
static void __exit comx_proto_fr_exit(void)
{
comx_unregister_hardware(fr_dlci.name);
comx_unregister_protocol(fr_master_protocol.name);
comx_unregister_protocol(fr_slave_protocol.name);
}
module_init(comx_proto_fr_init);
module_exit(comx_proto_fr_exit);
/*
* 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
*
* Changed (00/10/29, Henner Eisen):
* - comx_rx() / comxlapb_data_indication() return status.
*
*/
#define VERSION "0.80"
#include <linux/module.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, 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(dev);
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(dev);
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(dev);
kfree_skb(skb);
return 0;
case 0x02:
lapb_disconnect_request(dev);
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(dev, 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, &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, &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_t(int, 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 (lapb_getparms(dev, &parms)) {
return -ENODEV;
}
if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
return -ENOMEM;
}
if (copy_from_user(page, buffer, count)) {
free_page((unsigned long)page);
return -EFAULT;
}
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, &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, &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, &parms);
}
} else if (strcmp(entry->name, FILENAME_WINDOW) == 0) {
parms.window = simple_strtoul(page, NULL, 10);
lapb_setparms(dev, &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, &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(struct net_device *dev, int reason)
{
struct comx_channel *ch = dev->priv;
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);
ch->dev->last_rx = jiffies;
}
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(struct net_device *dev, int reason)
{
struct comx_channel *ch = dev->priv;
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);
ch->dev->last_rx = jiffies;
}
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 int comxlapb_data_indication(struct net_device *dev, struct sk_buff *skb)
{
struct comx_channel *ch = dev->priv;
if (ch->dev->type == ARPHRD_X25) {
skb_push(skb, 1);
if (skb_cow(skb, 1))
return NET_RX_DROP;
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;
return comx_rx(ch->dev, skb);
}
static void comxlapb_data_transmit(struct net_device *dev, struct sk_buff *skb)
{
struct comx_channel *ch = dev->priv;
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);
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, &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->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
};
static int __init comx_proto_lapb_init(void)
{
int ret;
if ((ret = comx_register_protocol(&comxlapb_protocol)) != 0) {
return ret;
}
return comx_register_protocol(&comx25_protocol);
}
static void __exit comx_proto_lapb_exit(void)
{
comx_unregister_protocol(comxlapb_protocol.name);
comx_unregister_protocol(comx25_protocol.name);
}
module_init(comx_proto_lapb_init);
module_exit(comx_proto_lapb_exit);
MODULE_LICENSE("GPL");
/*
* 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/types.h>
#include <linux/jiffies.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 <net/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");
MODULE_LICENSE("GPL");
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);
if (!ch->LINE_privdata)
return -ENOMEM;
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
};
static int __init comx_proto_ppp_init(void)
{
int ret;
ret = comx_register_protocol(&hdlc_protocol);
if (!ret) {
ret = comx_register_protocol(&syncppp_protocol);
if (ret)
comx_unregister_protocol(hdlc_protocol.name);
}
return ret;
}
static void __exit comx_proto_ppp_exit(void)
{
comx_unregister_protocol(syncppp_protocol.name);
comx_unregister_protocol(hdlc_protocol.name);
}
module_init(comx_proto_ppp_init);
module_exit(comx_proto_ppp_exit);
/*
* Device driver framework for the COMX line of synchronous serial boards
*
* for Linux kernel 2.2.X / 2.4.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.
*
* Contributors:
* Arnaldo Carvalho de Melo <acme@conectiva.com.br> (0.85)
*
* 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())
*
* Version 0.85 (00/08/15):
* - resource release on failure in comx_mkdir
* - fix return value on failure at comx_write_proc
*
* Changed (00/10/29, Henner Eisen):
* - comx_rx() / comxlapb_data_indication() return status.
*/
#define VERSION "0.85"
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/jiffies.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/ctype.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#ifdef CONFIG_KMOD
#include <linux/kmod.h>
#endif
#ifndef CONFIG_PROC_FS
#error For now, COMX really needs the /proc filesystem
#endif
#include <net/syncppp.h>
#include "comx.h"
MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
MODULE_DESCRIPTION("Common code for the COMX synchronous serial adapters");
MODULE_LICENSE("GPL");
static struct comx_hardware *comx_channels = NULL;
static struct comx_protocol *comx_lines = NULL;
static int comx_mkdir(struct inode *, struct dentry *, int);
static int comx_rmdir(struct inode *, struct dentry *);
static struct dentry *comx_lookup(struct inode *, struct dentry *, struct nameidata *);
static struct inode_operations comx_root_inode_ops = {
.lookup = comx_lookup,
.mkdir = comx_mkdir,
.rmdir = comx_rmdir,
};
static int 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 struct dentry_operations comx_dentry_operations = {
.d_delete = comx_delete_dentry,
};
static struct proc_dir_entry * comx_root_dir;
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 }
};
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_t(int, free ? free : ch->debug_size,
min_t(int, 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);
dev->last_rx = jiffies;
}
return 0;
}
static struct net_device_stats *comx_stats(struct net_device *dev)
{
struct comx_channel *ch = 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", netif_running(dev));
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) {
kfree(ch);
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 = 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_t(int, 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_t(int, 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 = dev->priv;
char *page;
struct comx_hardware *hw = comx_channels;
struct comx_protocol *line = comx_lines;
int ret=0;
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;
if(copy_from_user(page, buffer, count))
{
count = -EFAULT;
goto out;
}
if (page[count-1] == '\n')
page[count-1] = '\0';
else if (count < PAGE_SIZE)
page[count] = '\0';
else if (page[count]) {
count = -EINVAL;
goto out;
}
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 ret ? ret : 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){
request_module("comx-hw-%s",page);
}
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) {
request_module("comx-proto-%s",page);
}
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");
}
}
}
out:
free_page((unsigned long)page);
return count;
}
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;
int ret = -EIO;
if ((dev = kmalloc(sizeof(struct net_device), GFP_KERNEL)) == NULL) {
return -ENOMEM;
}
memset(dev, 0, sizeof(struct net_device));
lock_kernel();
if ((new_dir = create_proc_entry(dentry->d_name.name, mode | S_IFDIR,
comx_root_dir)) == NULL) {
goto cleanup_dev;
}
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)) {
goto cleanup_new_dir;
}
if (!create_comx_proc_entry(FILENAME_PROTOCOL, 0644,
strlen(PROTONAME_NONE) + 1, new_dir)) {
goto cleanup_filename_hardware;
}
if (!create_comx_proc_entry(FILENAME_STATUS, 0444, 0, new_dir)) {
goto cleanup_filename_protocol;
}
if (!create_comx_proc_entry(FILENAME_LINEUPDELAY, 0644, 2, new_dir)) {
goto cleanup_filename_status;
}
if ((debug_file = create_proc_entry(FILENAME_DEBUG,
S_IFREG | 0644, new_dir)) == NULL) {
goto cleanup_filename_lineupdelay;
}
debug_file->data = (void *)debug_file;
debug_file->read_proc = NULL; // see below
debug_file->write_proc = &comx_write_proc;
debug_file->nlink = 1;
strcpy(dev->name, (char *)new_dir->name);
dev->init = comx_init_dev;
if (register_netdevice(dev)) {
goto cleanup_filename_debug;
}
ch = dev->priv;
if((ch->if_ptr = (void *)kmalloc(sizeof(struct ppp_device),
GFP_KERNEL)) == NULL) {
goto cleanup_register;
}
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) {
ret = -ENOMEM;
goto cleanup_if_ptr;
}
ch->lineup_delay = DEFAULT_LINEUP_DELAY;
MOD_INC_USE_COUNT;
unlock_kernel();
return 0;
cleanup_if_ptr:
kfree(ch->if_ptr);
cleanup_register:
unregister_netdevice(dev);
cleanup_filename_debug:
remove_proc_entry(FILENAME_DEBUG, new_dir);
cleanup_filename_lineupdelay:
remove_proc_entry(FILENAME_LINEUPDELAY, new_dir);
cleanup_filename_status:
remove_proc_entry(FILENAME_STATUS, new_dir);
cleanup_filename_protocol:
remove_proc_entry(FILENAME_PROTOCOL, new_dir);
cleanup_filename_hardware:
remove_proc_entry(FILENAME_HARDWARE, new_dir);
cleanup_new_dir:
remove_proc_entry(dentry->d_name.name, comx_root_dir);
cleanup_dev:
kfree(dev);
unlock_kernel();
return ret;
}
static int comx_rmdir(struct inode *dir, struct dentry *dentry)
{
struct proc_dir_entry *entry = PDE(dentry->d_inode);
struct net_device *dev;
struct comx_channel *ch;
int ret;
lock_kernel();
dev = entry->data;
ch = dev->priv;
if (dev->flags & IFF_UP) {
printk(KERN_ERR "%s: down interface before removing it\n", dev->name);
unlock_kernel();
return -EBUSY;
}
if (ch->protocol && ch->protocol->line_exit &&
(ret=ch->protocol->line_exit(dev))) {
unlock_kernel();
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);
}
unlock_kernel();
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);
}
free_netdev(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);
MOD_DEC_USE_COUNT;
unlock_kernel();
return 0;
}
static struct dentry *comx_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
struct proc_dir_entry *de;
struct inode *inode = NULL;
lock_kernel();
if ((de = PDE(dir)) != NULL) {
for (de = de->subdir ; de ; de = de->next) {
if ((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");
unlock_kernel();
return ERR_PTR(-EINVAL);
}
break;
}
}
}
unlock_kernel();
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 int comx_delete_dentry(struct dentry *dentry)
{
return 1;
}
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->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;
}
static int __init comx_init(void)
{
struct proc_dir_entry *new_file;
comx_root_dir = create_proc_entry("comx",
S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO, &proc_root);
if (!comx_root_dir)
return -ENOMEM;
comx_root_dir->proc_iops = &comx_root_inode_ops;
if ((new_file = create_proc_entry(FILENAME_HARDWARELIST,
S_IFREG | 0444, comx_root_dir)) == NULL) {
return -ENOMEM;
}
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->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);
return 0;
}
static void __exit comx_exit(void)
{
remove_proc_entry(FILENAME_HARDWARELIST, comx_root_dir);
remove_proc_entry(FILENAME_PROTOCOLLIST, comx_root_dir);
remove_proc_entry(comx_root_dir->name, &proc_root);
}
module_init(comx_init);
module_exit(comx_exit);
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_root_dir);
/*
* 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
long int lineup_pending;
unsigned char lineup_delay;
#if 0
struct timer_list reset_timer; // for board resetting
long 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
#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;
/*
* 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment