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
- Lines: 2861
- Date:
Fri Aug 2 17:39:44 2002
- Orig file:
linux-2.4.18/drivers/net/wan/comx-hw-munich.c
- Orig date:
Wed Dec 31 16:00:00 1969
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)