/* * Hardware-level driver for the SliceCOM board for Linux kernels 2.4.X * * Current maintainer / latest changes: Pasztor Szilard * * Original author: Bartok Istvan * Based on skeleton by Tivadar Szemethy * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 , Gergely Madarasz , Szilard Pasztor "); MODULE_DESCRIPTION("Hardware-level driver for the SliceCOM and PciCOM (WelCOM) adapters"); MODULE_LICENSE("GPL"); /* * TODO: az ilyenek a comxhw.h -ban szoktak lenni, idovel menjenek majd oda: */ #define FILENAME_BOARDNUM "boardnum" /* /proc/comx/comx0.1/boardnum */ #define FILENAME_TIMESLOTS "timeslots" /* /proc/comx/comx0.1/timeslots */ #define FILENAME_FRAMING "framing" /* /proc/comx/comx0.1/framing */ #define FILENAME_LINECODE "linecode" /* /proc/comx/comx0.1/linecode */ #define FILENAME_CLOCK_SOURCE "clock_source" /* /proc/comx/comx0.1/clock_source */ #define FILENAME_LOOPBACK "loopback" /* /proc/comx/comx0.1/loopback */ #define FILENAME_REG "reg" /* /proc/comx/comx0.1/reg */ #define FILENAME_LBIREG "lbireg" /* /proc/comx/comx0.1/lbireg */ #define SLICECOM_BOARDNUM_DEFAULT 0 #define SLICECOM_FRAMING_CRC4 1 #define SLICECOM_FRAMING_NO_CRC4 2 #define SLICECOM_FRAMING_DEFAULT SLICECOM_FRAMING_CRC4 #define SLICECOM_LINECODE_HDB3 1 #define SLICECOM_LINECODE_AMI 2 #define SLICECOM_LINECODE_DEFAULT SLICECOM_LINECODE_HDB3 #define SLICECOM_CLOCK_SOURCE_LINE 1 #define SLICECOM_CLOCK_SOURCE_INTERNAL 2 #define SLICECOM_CLOCK_SOURCE_DEFAULT SLICECOM_CLOCK_SOURCE_LINE #define SLICECOM_LOOPBACK_NONE 1 #define SLICECOM_LOOPBACK_LOCAL 2 #define SLICECOM_LOOPBACK_REMOTE 3 #define SLICECOM_LOOPBACK_DEFAULT SLICECOM_LOOPBACK_NONE #define MUNICH_VIRT(addr) (void *)(&bar1[addr]) struct slicecom_stringtable { char *name; int value; }; /* A convention: keep "default" the last not NULL when reading from /proc, "error" is an indication that something went wrong, we have an undefined value */ struct slicecom_stringtable slicecom_framings[] = { {"crc4", SLICECOM_FRAMING_CRC4}, {"no-crc4", SLICECOM_FRAMING_NO_CRC4}, {"default", SLICECOM_FRAMING_DEFAULT}, {"error", 0} }; struct slicecom_stringtable slicecom_linecodes[] = { {"hdb3", SLICECOM_LINECODE_HDB3}, {"ami", SLICECOM_LINECODE_AMI}, {"default", SLICECOM_LINECODE_DEFAULT}, {"error", 0} }; struct slicecom_stringtable slicecom_clock_sources[] = { {"line", SLICECOM_CLOCK_SOURCE_LINE}, {"internal", SLICECOM_CLOCK_SOURCE_INTERNAL}, {"default", SLICECOM_CLOCK_SOURCE_DEFAULT}, {"error", 0} }; struct slicecom_stringtable slicecom_loopbacks[] = { {"none", SLICECOM_LOOPBACK_NONE}, {"local", SLICECOM_LOOPBACK_LOCAL}, {"remote", SLICECOM_LOOPBACK_REMOTE}, {"default", SLICECOM_LOOPBACK_DEFAULT}, {"error", 0} }; /* * Some tunable values... * * Note: when tuning values which change the length of text in * /proc/comx/comx[n]/status, keep in mind that it must be shorter then * PAGESIZE ! */ #define MAX_BOARDS 4 /* ezzel 4 kartya lehet a gepben: 0..3 */ #define RX_DESC_MAX 8 /* Rx ring size, must be >= 4 */ #define TX_DESC_MAX 4 /* Tx ring size, must be >= 2 */ /* a sokkal hosszabb Tx ring mar ronthatja a nem-FIFO packet */ /* schedulerek (fair queueing, stb.) hatekonysagat. */ #define MAX_WORK 10 /* TOD: update the info max. ennyi-1 esemenyt dolgoz fel egy interrupt hivasnal */ /* * These are tunable too, but don't touch them without fully understanding what is happening */ #define UDELAY 20 /* We wait UDELAY usecs with disabled interrupts before and */ /* after each command to avoid writing into each other's */ /* ccb->action_spec. A _send_packet nem var, mert azt az */ /* _interrupt()-bol is meghivhatja a LINE_tx() */ /* * Just to avoid warnings about implicit declarations: */ static int MUNICH_close(struct net_device *dev); static struct comx_hardware slicecomhw; static struct comx_hardware pcicomhw; static unsigned long flags; static spinlock_t mister_lock = SPIN_LOCK_UNLOCKED; typedef volatile struct /* Time Slot Assignment */ { u32 rxfillmask:8, // ----------------------------+------+ // | | rxchannel:5, // ----------------------+---+ | | rti:1, // ---------------------+| | | | res2:2, // -------------------++|| | | | // |||| | | | txfillmask:8, // ----------+------+ |||| | | | // | | |||| | | | txchannel:5, // ----+---+ | | |||| | | | tti:1, // ---+| | | | |||| | | | res1:2; // -++|| | | | |||| | | | // 3 2 1 // 10987654 32109876 54321098 76543210 } timeslot_spec_t; typedef volatile struct /* Receive Descriptor */ { u32 zero1:16, no:13, hi:1, hold:1, zero2:1; u32 next; u32 data; u32 zero3:8, status:8, bno:13, zero4:1, c:1, fe:1; } rx_desc_t; typedef volatile struct /* Transmit Descriptor */ { u32 fnum:11, csm:1, no13:1, zero1:2, v110:1, no:13, hi:1, hold:1, fe:1; u32 next; u32 data; } tx_desc_t; typedef volatile struct /* Channel Specification */ { u32 iftf:1, mode:2, fa:1, trv:2, crc:1, inv:1, cs:1, tflag:7, ra:1, ro:1, th:1, ta:1, to:1, ti:1, ri:1, nitbs:1, fit:1, fir:1, re:1, te:1, ch:1, ifc:1, sfe:1, fe2:1; u32 frda; u32 ftda; u32 itbs:6, zero1:26; } channel_spec_t; typedef volatile struct /* Configuration Control Block */ { u32 action_spec; u32 reserved1; u32 reserved2; timeslot_spec_t timeslot_spec[32]; channel_spec_t channel_spec[32]; u32 current_rx_desc[32]; u32 current_tx_desc[32]; u32 csa; /* Control Start Address. CSA = *CCBA; CCB = *CSA */ /* MUNICH does it like: CCB = *( *CCBA ) */ } munich_ccb_t; typedef volatile struct /* Entry in the interrupt queue */ { u32 all; } munich_intq_t; #define MUNICH_INTQLEN 63 /* Rx/Tx Interrupt Queue Length (not the real len, but the TIQL/RIQL value) */ #define MUNICH_INTQMAX ( 16*(MUNICH_INTQLEN+1) ) /* Rx/Tx/Periph Interrupt Queue size in munich_intq_t's */ #define MUNICH_INTQSIZE ( 4*MUNICH_INTQMAX ) /* Rx/Tx/Periph Interrupt Queue size in bytes */ #define MUNICH_PIQLEN 4 /* Peripheral Interrupt Queue Length. Unlike the RIQL/TIQL, */ #define MUNICH_PIQMAX ( 4*MUNICH_PIQLEN ) /* PIQL register needs it like this */ #define MUNICH_PIQSIZE ( 4*MUNICH_PIQMAX ) typedef volatile u32 vol_u32; /* TOD: ezek megszunnek ha atirom readw()/writew()-re - 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 Unavailable 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 */ // { 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 long)board; board->modemline_timer.function = pcicom_modemline; board->modemline_timer.expires = jiffies + HZ; add_timer((struct timer_list *)&board->modemline_timer); } /* It is done. Declare that we're open: */ hw->busy = 0; /* It may be 1 if the frame at Tx init already ended, but it is not */ /* a real problem: we compute hw->busy on every interrupt */ hw->rafutott = 0; ch->init_status |= HW_OPEN; /* Initialize line state: */ if (board->lineup) ch->line_status |= LINE_UP; else ch->line_status &= ~LINE_UP; /* Remove w attribute from /proc files associated to hw parameters: no write when the device is open */ for (; procfile; procfile = procfile->next) if (strcmp(procfile->name, FILENAME_BOARDNUM) == 0 || strcmp(procfile->name, FILENAME_TIMESLOTS) == 0) procfile->mode = S_IFREG | 0444; spin_unlock_irqrestore(&mister_lock, flags); return 0; } /* * Hardware close routine. * Called by comx (upper) layer when the user wants to bring down the interface * with ifconfig. * We also call it from MUNICH_open, if the open fails. * Brings down hardware, frees resources, stops receiver * Returns 0 on OK, or standard error value on error. */ static int MUNICH_close(struct net_device *dev) { struct comx_channel *ch = 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 = NULL; board->tiq = board->riq = board->piq = NULL; } /* Enable setting of hw parameters */ for (; procfile; procfile = procfile->next) if (strcmp(procfile->name, FILENAME_BOARDNUM) == 0 || strcmp(procfile->name, FILENAME_TIMESLOTS) == 0) procfile->mode = S_IFREG | 0644; /* We're not open anymore */ ch->init_status &= ~HW_OPEN; spin_unlock_irqrestore(&mister_lock, flags); return 0; } /* * Give (textual) status information. * The text it returns will be a part of what appears when the user does a * cat /proc/comx/comx[n]/status * Don't write more than PAGESIZE. * Return value: number of bytes written (length of the string, incl. 0) */ static int MUNICH_minden(struct net_device *dev, char *page) { struct comx_channel *ch = 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; /* 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);