patch-2.4.19 linux-2.4.19/drivers/net/wan/comx-hw-munich.c

Next file: linux-2.4.19/drivers/net/wan/comx.c
Previous file: linux-2.4.19/drivers/net/wan/comx-hw-locomx.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/drivers/net/wan/comx-hw-munich.c linux-2.4.19/drivers/net/wan/comx-hw-munich.c
@@ -0,0 +1,2860 @@
+/*
+ * 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/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/delay.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <linux/pci.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");
+
+/*
+ *	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 - kész */
+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 = dev->priv;
+    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 = dev->priv;
+    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 = dev->priv;
+    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);
+
+#if (LINUX_VERSION_CODE < 0x02030d)
+	bar1 = ioremap_nocache(pci->base_address[0], 0x100);
+	lbi = ioremap_nocache(pci->base_address[1], 0x100);
+#else
+	bar1 = ioremap_nocache(pci->resource[0].start, 0x100);
+	lbi = ioremap_nocache(pci->resource[1].start, 0x100);
+#endif
+
+	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;
+		SET_MODULE_OWNER(board);
+	    }
+	    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 = dev->priv;
+
+    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 = (struct comx_channel *)dev->priv;
+    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 void 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 = (struct comx_channel *)dev->priv;
+	    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 = (struct comx_channel *)dev->priv;
+		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                       */
+//              {
+
+udelay(10000);
+	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 = (struct comx_channel *)dev->priv;
+	    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 = (struct comx_channel *)dev->priv;
+		hw = (struct slicecom_privdata *)ch->HW_privdata;
+
+		/* We dont 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;
+}
+
+/* 
+ * 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 = dev->priv;
+    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)
+    {
+	board->modemline_timer.data = (unsigned int)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 = dev->priv;
+    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 = 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 = dev->priv;
+    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 += snprintf(page + len, PAGE_SIZE - len, "Controller status:\n");
+	if (frs0 == 0)
+	    len += snprintf(page + len, PAGE_SIZE - len, "\tNo alarms\n");
+	else
+	{
+	    if (frs0 & FRS0_LOS)
+	            len += snprintf(page + len, PAGE_SIZE - len, "\tLoss Of Signal\n");
+	    else
+	    {
+		if (frs0 & FRS0_AIS)
+		    len += snprintf(page + len, PAGE_SIZE - len,
+				 "\tAlarm Indication Signal\n");
+		else
+		{
+		    if (frs0 & FRS0_AUXP)
+			len += snprintf(page + len, PAGE_SIZE - len,
+				     "\tAuxiliary Pattern Indication\n");
+		    if (frs0 & FRS0_LFA)
+			len += snprintf(page + len, PAGE_SIZE - len,
+				     "\tLoss of Frame Alignment\n");
+		    else
+		    {
+			if (frs0 & FRS0_RRA)
+			    len += snprintf(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 += snprintf(page + len, PAGE_SIZE - len,
+					 "\tLoss of CRC4 Multiframe Alignment\n");
+
+			if (((fmr2 & 0xc0) == 0xc0) && (frs0 & FRS0_NMF))
+			    len += snprintf(page + len, PAGE_SIZE - len,
+				 "\tNo CRC4 Multiframe alignment Found after 400 msec\n");
+		    }
+		}
+	    }
+	}
+
+	frs1 = readb(lbi + FRS1);
+	if (FRS1_XLS & frs1)
+	    len += snprintf(page + len, PAGE_SIZE - len,
+		 "\tTransmit Line Short\n");
+
+	/* debug Rx ring: DEL: - vagy meghagyni, de akkor legyen kicsit altalanosabb */
+    }
+
+    len += snprintf(page + len, PAGE_SIZE - len, "Rx ring:\n");
+    len += snprintf(page + len, PAGE_SIZE - len, "\trafutott: %d\n", hw->rafutott);
+    len += snprintf(page + len, PAGE_SIZE - len,
+		 "\tlastcheck: %ld, jiffies: %ld\n", board->lastcheck, jiffies);
+    len += snprintf(page + len, PAGE_SIZE - len, "\tbase: %08x\n",
+	(u32) virt_to_phys(&hw->rx_desc[0]));
+    len += snprintf(page + len, PAGE_SIZE - len, "\trx_desc_ptr: %d\n",
+		 hw->rx_desc_ptr);
+    len += snprintf(page + len, PAGE_SIZE - len, "\trx_desc_ptr: %08x\n",
+	(u32) virt_to_phys(&hw->rx_desc[hw->rx_desc_ptr]));
+    len += snprintf(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 += snprintf(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 += snprintf(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 += snprintf(page + len, PAGE_SIZE - len, "\t%2d %s: ", i,
+			     devp->name);
+		for (j = 0; j < 32; j++)
+		    if ((1 << j) & timeslots)
+			len += snprintf(page + len, PAGE_SIZE - len, "%d ", j);
+		len += snprintf(page + len, PAGE_SIZE - len, "\n");
+	    }
+	}
+    }
+
+    len += snprintf(page + len, PAGE_SIZE - len, "Interrupt work histogram:\n");
+    for (i = 0; i < MAX_WORK; i++)
+	len += snprintf(page + len, PAGE_SIZE - len, "hist[%2d]: %8u%c", i,
+		     board->histogram[i], (i &&
+					   ((i + 1) % 4 == 0 ||
+					    i == MAX_WORK - 1)) ? '\n' : ' ');
+
+    len += snprintf(page + len, PAGE_SIZE - len, "Tx ring histogram:\n");
+    for (i = 0; i < TX_DESC_MAX; i++)
+	len += snprintf(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 += snprintf(page + len, PAGE_SIZE - len,
+		     "Data in current interval (%d seconds elapsed):\n",
+		     board->elapsed_seconds);
+	len += snprintf(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 += snprintf(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 += snprintf(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 += snprintf(page + len, PAGE_SIZE - len,
+		     "Data in Interval 1 (15 minutes):\n");
+	len += snprintf(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 += snprintf(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 += snprintf(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 += snprintf(page + len, PAGE_SIZE - len,
+		     "Data in last 4 intervals (1 hour):\n");
+	len += snprintf(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 += snprintf(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 += snprintf(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 += snprintf(page + len, PAGE_SIZE - len,
+		     "Data in last 96 intervals (24 hours):\n");
+	len += snprintf(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 += snprintf(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 += snprintf(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 +=snprintf( page + len, PAGE_SIZE - len, "Special events:\n" );
+//      len +=snprintf( page + len, PAGE_SIZE - len, "\tstat_pri/missed: %u / %u\n", board->stat_pri_races, board->stat_pri_races_missed );
+//      len +=snprintf( 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 = dev->priv;
+    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 += snprintf(page + len, PAGE_SIZE - len, "%d ", i);
+	len += snprintf(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 += snprintf(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 += snprintf(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 +=
+	    snprintf(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 += snprintf(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 += snprintf(page + len, PAGE_SIZE - len,
+		     "%s: " FILENAME_REG ": write-only file\n", dev->name);
+    }
+    else if (!strcmp(file->name, FILENAME_LBIREG))
+    {
+	len += snprintf(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 = dev->priv;
+    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 (file->f_dentry->d_inode->i_ino != entry->low_ino)
+    {
+	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 */
+    copy_from_user(page, buffer, count = min(count, PAGE_SIZE));
+    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 = (struct comx_channel *)dev->priv;
+    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 = (struct comx_channel *)dev->priv;
+    struct slicecom_privdata *hw = ch->HW_privdata;
+//    munich_board_t *board;
+
+    /* 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 */
+
+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);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)