/* * Copyright (c) 2000 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * *--------------------------------------------------------------------------- * * i4b_ihfc_drv.c - ihfc ISA PnP-bus interface * ------------------------------------------- * * Everything which has got anything to do with the * HFC-1/S/SP chips has been put here. * * last edit-date: [Fri Jan 12 17:06:52 2001] * * $FreeBSD$ * *---------------------------------------------------------------------------*/ #include "ihfc.h" #if (NIHFC > 0) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*---------------------------------------------------------------------------* * Local prototypes *---------------------------------------------------------------------------*/ void ihfc_loadconfig (ihfc_sc_t *sc); static void ihfc_trans_Bread (ihfc_sc_t *sc, u_char chan); static void ihfc_trans_Bwrite (ihfc_sc_t *sc, u_char chan); static void ihfc_hdlc_Bread (ihfc_sc_t *sc, u_char chan); static void ihfc_hdlc_Bwrite (ihfc_sc_t *sc, u_char chan); static void ihfc_hdlc_Dread (ihfc_sc_t *sc, u_char chan); static void ihfc_hdlc_Dwrite (ihfc_sc_t *sc, u_char chan); static void ihfc_isac_Dread (ihfc_sc_t *sc, u_char chan); static void ihfc_isac_Dwrite (ihfc_sc_t *sc, u_char chan); void ihfc_cmdr_hdlr (ihfc_sc_t *sc, u_char cmdr); void ihfc_exir_hdlr (ihfc_sc_t *sc, u_char exir); void ihfc_sq (ihfc_sc_t *sc); static void ihfc_test_Bread (ihfc_sc_t *sc, u_char chan); static void ihfc_test_Bwrite (ihfc_sc_t *sc, u_char chan); u_short ihfc_Bsel_fifo (ihfc_sc_t *sc, u_char chan, u_char flag); u_int32_t ihfc_Dsel_fifo (ihfc_sc_t *sc, u_char chan, u_char flag); /*---------------------------------------------------------------------------* * Commonly used ISA bus commands *---------------------------------------------------------------------------*/ #define IHFC_DATA_OFFSET 0 #define IHFC_REG_OFFSET 1 #define BUS_VAR bus_space_handle_t h = rman_get_bushandle(S_IOBASE[0]); \ bus_space_tag_t t = rman_get_bustag (S_IOBASE[0]) #define SET_REG(reg) bus_space_write_1(t,h, IHFC_REG_OFFSET, reg) #define GET_STAT bus_space_read_1 (t,h, IHFC_REG_OFFSET) #define READ_DATA_1 bus_space_read_1 (t,h, IHFC_DATA_OFFSET) #define READ_BOTH_2 bus_space_read_2 (t,h, IHFC_DATA_OFFSET) #define WRITE_DATA_1(data) bus_space_write_1(t,h, IHFC_DATA_OFFSET, data) #define WRITE_BOTH_2(data) bus_space_write_2(t,h, IHFC_DATA_OFFSET, data) #define DISBUSY(okcmd, tocmd) \ { \ if (GET_STAT & 1) \ { \ register u_char a; \ register u_int to = IHFC_DISBUSYTO; \ \ while(((a = GET_STAT) & 1) && --to); \ \ if (!to) \ { \ NDBGL1(L1_ERROR, "DISBUSY-TIMEOUT! (a=%04x, " \ "unit=%d)", a, S_UNIT); \ tocmd; \ } \ else \ { \ okcmd; \ } \ } \ else \ { \ okcmd; \ } \ } #define WAITBUSY_2(okcmd, tocmd) \ { \ register u_short a; \ register u_int to = IHFC_NONBUSYTO; \ \ while((~(a = READ_BOTH_2) & 0x100) && --to); \ \ if (!to) \ { \ NDBGL1(L1_ERROR, "NONBUSY-TIMEOUT! (a=%04x, " \ "unit=%d)", a, S_UNIT); \ tocmd; \ } \ else \ { \ okcmd; \ } \ } /*---------------------------------------------------------------------------* * Control function (HFC-1/S/SP) * * Flag: * 1: reset and unlock chip (at boot only) * 2: prepare for shutdown (at shutdown only) * 3: reset and resume * 4: select TE-mode (boot default) * 5: select NT-mode (only HFC-S/SP/PCI) * * Returns != 0 on errornous chip *---------------------------------------------------------------------------*/ int ihfc_control(ihfc_sc_t *sc, int flag) { BUS_VAR; if (flag == 3) goto reset0; if (flag == 4) { S_NTMODE = 0; goto mode0; } if (flag == 5) { S_NTMODE = 1; goto mode0; } if (flag == 1) { WRITE_BOTH_2(0x5400 | S_IIO); /* enable IO (HFC-1/S) */ S_LAST_CHAN = -1; /* HFC-S/SP configuration */ S_CIRM = S_IIRQ|0x10; /* IRQ, 8K fifo mode */ S_CLKDEL = 0x00; /* 12.288mhz */ S_CTMT = 0x03; /* transperant mode */ S_CONNECT = 0x00; /* B channel data flow */ S_INT_M1 = 0x40; /* interrupt mask */ S_INT_M2 = 0x08; /* enable interrupt output */ S_MST_MODE = 0x01; /* master mode */ S_SCTRL = 0x50; /* S/Q on, non cap. line mode */ S_SCTRL_R = 0x00; /* B channel receive disable */ S_TEST = 0x00; /* no need for awake enable */ if (S_HFC & (HFC_1 | HFC_S)) /* configure timer (50ms) */ { S_CTMT |= 0x08; } else { S_CTMT |= 0x14; } /* HFC-1 ISAC configuration (IOM-2 mode) */ S_ADF1 = 0x00; /* */ S_ADF2 = 0x80; /* select mode IOM-2 */ S_SPCR = 0x00; /* B channel send disable (0x10 for test loop) */ S_MASK = 0xfb; /* enable CISQ */ S_MODE = 0xc9; /* receiver enabled */ S_SQXR = 0x0f; /* master, clock always active */ S_STCR = 0x70; /* TIC bus address = 7 */ S_STAR2 = 0x04; /* enable S/Q */ mode0: if (S_NTMODE) /* configure NT- or TE-mode */ { S_SCTRL |= 0x04; /* NT mode */ S_CLKDEL &= ~0x7f; /* clear delay */ S_CLKDEL |= 0x6c; /* set delay */ } else { S_SCTRL &= ~0x04; /* TE mode */ S_STDEL &= 0x7f; /* use mask! */ S_CLKDEL &= ~0x7f; /* clear delay */ S_CLKDEL |= S_STDEL; /* set delay */ } if (S_DLP) /* configure D-priority */ { S_SCTRL |= 0x08; /* (10/11) */ } else { S_SCTRL &= ~0x08; /* (8/9) */ } reset0: /* chip reset (HFC-1/S/SP) */ if (S_HFC & HFC_1) { SET_REG((S_CIRM | 0xc8) & 0xdf); DELAY(10); /* HFC-2B manual recommends a 4 * * clock cycle delay after CIRM * * write with reset=1. A 1us * * delay, should do for 7.68mhz,* * but just in case I make that * * 10us. */ SET_REG((S_CIRM | 0xc0) & 0xdf); DELAY(250); /* ISAC manual says reset pulse * * length is 125us. Accessing * * ISAC before those 125us, we * * may risk chip corruption and * * irq failure. The HFC-2B also * * needs some delay to recover, * * so we add some us. */ } else { SET_REG(0x18); WRITE_DATA_1(S_CIRM | 8); DELAY(10); /* HFC-2BDS0 manual recommends * * a 4 clock cycle delay after * * CIRM write with reset=1. * * A 1us delay, should do for * * 12.288mhz, but just in case * * I make that 10us. */ WRITE_DATA_1(S_CIRM); DELAY(25); /* HFC-2BDS0 needs some time to * * recover after CIRM write * * with reset=0. Experiments * * show this delay should be * * 8-9us. Just in case we make * * that 25us. */ } { /* HFC-1/S/SP chip test * * * * NOTE: after reset the HFC-1/S/SP should be * * in a mode where it is always non-busy/non- * * processing, and bit[0] of STATUS/DISBUSY * * register, should always return binary '0' * * until we configure the chips for normal * * operation. */ #ifdef IHFC_DEBUG printf("ihfc: GET_STAT value is: 0x%x\n", GET_STAT); #endif SET_REG(0x30); if ((GET_STAT & 1) || (READ_DATA_1 & 0xf)) goto f0; } ihfc_loadconfig(sc); if (S_HFC & HFC_1) ihfc_cmdr_hdlr(sc, 0x41); /* rres, xres */ S_PHSTATE = 0; HFC_FSM(sc, 0); } if (flag == 2) { if (S_HFC & HFC_1) S_CIRM &= ~0x03; /* disable interrupt */ S_SQXR |= 0x40; /* power down */ S_INT_M2 &= ~0x01; S_MASK |= 0x02; S_SPCR &= ~0x0f; /* send 1's only */ S_SCTRL &= ~0x83; /* send 1's only + enable oscillator */ ihfc_loadconfig(sc); } return(0); /* success */ f0: return(1); /* failure */ } /*---------------------------------------------------------------------------* * Softc initializer and hardware setup (HFC-1/S/SP) * * Returns: 0 on success * 1 on failure *---------------------------------------------------------------------------*/ int ihfc_init (ihfc_sc_t *sc, u_char chan, int prot, int activate) { if (chan > 5) goto f0; chan &= ~1; do { if (chan < 2) /* D-Channel */ { i4b_Dfreembuf(S_MBUF); if (!IF_QEMPTY(&S_IFQUEUE)) i4b_Dcleanifq(&S_IFQUEUE); RESET_SOFT_CHAN(sc, chan); S_IFQUEUE.ifq_maxlen = IFQ_MAXLEN; #if defined (__FreeBSD__) && __FreeBSD__ > 4 mtx_init(&S_IFQUEUE.ifq_mtx, "i4b_ihfc", MTX_DEF); #endif if (!activate) continue; if (S_HFC & HFC_1) { S_FILTER = (chan & 1) ? ihfc_isac_Dread : ihfc_isac_Dwrite; } else { S_FILTER = (chan & 1) ? ihfc_hdlc_Dread : ihfc_hdlc_Dwrite; } } else /* B-Channel */ { i4b_Bfreembuf(S_MBUF); if (!IF_QEMPTY(&S_IFQUEUE)) i4b_Bcleanifq(&S_IFQUEUE); RESET_SOFT_CHAN(sc, chan); S_IFQUEUE.ifq_maxlen = IFQ_MAXLEN; #if defined (__FreeBSD__) && __FreeBSD__ > 4 mtx_init(&S_IFQUEUE.ifq_mtx, "i4b_ihfc", MTX_DEF); #endif S_PROT = prot; if (!activate) continue; switch(prot) { case(BPROT_NONE): S_FILTER = (chan & 1) ? ihfc_trans_Bread : ihfc_trans_Bwrite; break; case(BPROT_RHDLC): S_FILTER = (chan & 1) ? ihfc_hdlc_Bread : ihfc_hdlc_Bwrite; break; case(5): S_FILTER = (chan & 1) ? ihfc_test_Bread : ihfc_test_Bwrite; break; } } } while (++chan & 1); S_MASK |= 0xfb; /* disable all, but CISQ interrupt (ISAC) */ S_INT_M1 &= 0x40; /* disable all, but TE/NT state machine (HFC) */ S_SCTRL &= ~0x03; /* B1/B2 send disable (HFC) */ S_SPCR &= ~0x0f; /* B1/B2 send disable (ISAC) */ S_SCTRL_R &= ~0x03; /* B1/B2 receive disable (HFC) */ chan = 0; if (S_FILTER) /* D-Channel active */ { S_MASK &= 0x2e; /* enable RME, RPF, XPR, EXI */ S_INT_M1 |= 0x24; /* enable D-receive, D-transmit */ } chan = 2; if (S_FILTER) /* B1-Channel active */ { S_SCTRL |= 1; /* send enable (HFC) */ S_SPCR |= 8; /* send enable (ISAC) */ S_SCTRL_R |= 1; /* receive enable (HFC) */ S_INT_M1 |= 0x80; /* timer enable (HFC) */ S_INT_M1 &= ~0x04; /* let D-channel use timer too */ } chan = 4; if (S_FILTER) /* B2-Channel active */ { S_SCTRL |= 2; /* send enable (HFC) */ S_SPCR |= 2; /* send enable (ISAC) */ S_SCTRL_R |= 2; /* receive enable (HFC) */ S_INT_M1 |= 0x80; /* timer enable (HFC) */ S_INT_M1 &= ~0x04; /* let D-channel use timer too */ } ihfc_loadconfig(sc); /* XXX reset timer? */ return 0; /* success */ f0: return 1; /* failure */ } /*---------------------------------------------------------------------------* * Load configuration data (HFC-1/S/SP) *---------------------------------------------------------------------------*/ void ihfc_loadconfig(ihfc_sc_t *sc) { BUS_VAR; if (S_HFC & HFC_1) { /* HFC-1 chips w/ISAC: */ const u_char *src = (void *)&S_ISAC_CONFIG; const u_char *dst = (void *)&isac_configtable; SET_REG((S_CIRM | 0xc0) & 0xdf); S_CTMT = (S_CTMT & ~0x14) | ((S_INT_M1 >> 5) & 0x4); SET_REG((S_CTMT | 0xe0) & 0xff); while(*dst) { SET_REG(*dst++); /* set register */ /* write configuration */ DISBUSY(WRITE_DATA_1(*src++), break); } } else { /* HFC-S/SP chips: */ const u_char *src = (void *)&S_HFC_CONFIG; const u_char *dst = (void *)&ihfc_configtable; while(*dst) { SET_REG(*dst++); /* set register */ WRITE_DATA_1(*src++); /* write configuration */ } } } /*---------------------------------------------------------------------------* * Function State Machine handler (PH layer) (HFC-1/S/SP) * * Flag: 0 = Refresh softc S_PHSTATE + take hints * 1 = Activate * 2 = Deactivate * * NOTE: HFC-1 only supports TE mode. *---------------------------------------------------------------------------*/ void ihfc_fsm(ihfc_sc_t *sc, int flag) { const struct ihfc_FSMtable *fsmtab; u_char ihfc_cmd = 0; u_char isac_cmd = 0; u_char tmp; BUS_VAR; /* get current state (rx/downstream) */ if (S_HFC & HFC_1) { SET_REG(0x31); DISBUSY(tmp = (READ_DATA_1 >> 2) & 0xf, return); fsmtab = (S_NTMODE) ? &ihfc_TEtable2[tmp]: &ihfc_TEtable2[tmp]; } else { SET_REG(0x30); tmp = READ_DATA_1 & 0xf; fsmtab = (S_NTMODE) ? &ihfc_NTtable[tmp]: &ihfc_TEtable[tmp]; } if (fsmtab->string) { NDBGL1(L1_I_CICO, "%s (ind=0x%x, flag=%d, unit=%d).", fsmtab->string, tmp, flag, S_UNIT); } else { NDBGL1(L1_I_ERR, "Illegal indicatation (ind=0x%x, " "flag=%d, unit=%d).", tmp, flag, S_UNIT); } /* indication machine / state change * * * * Whenever the state of the S0-line changes, we check to see in which * * direction the change went. Generally upwards means activate, and * * downwards means deactivate. * * The test signal is used to ensure proper syncronization. */ if (fsmtab->state == 0) /* deactivated indication */ { if (S_PHSTATE != 0) { isac_cmd = 0x3c; /* deactivate DUI */ i4b_l1_ph_deactivate_ind(S_I4BUNIT); } } if (fsmtab->state == 2) /* syncronized indication */ { if (S_PHSTATE != 2) { if (S_NTMODE) ihfc_cmd = 0x80; } } if (fsmtab->state == 3) /* activated indication */ { if (S_PHSTATE != 3) { isac_cmd = (S_DLP) ? 0x24 /* activate AR10 */ : 0x20; /* activate AR8 */ i4b_l1_ph_activate_ind(S_I4BUNIT); } } if (fsmtab->state == 4) /* error indication */ { if (S_PHSTATE < 4) { isac_cmd = 0x3c; /* deactivate DUI */ } } S_PHSTATE = fsmtab->state; if ((flag == 1) && (fsmtab->state != 3)) { isac_cmd = (S_DLP) ? 0x24 : 0x20; ihfc_cmd = 0x60; } if ((flag == 2) && (fsmtab->state != 0)) { isac_cmd = 0x3c; ihfc_cmd = 0x40; } /* set new state (tx / upstream) * * * * NOTE: HFC-S/SP and ISAC transmitters are always active when * * activated state is reached. The bytes sent to the S0-bus are all * * high impedance, so they do not disturb. * * The HFC-1 has a separate SIEMENS S0-device. */ if (S_HFC & HFC_1) { if (isac_cmd) { if (S_IOM2) isac_cmd |= 3; SET_REG(0x31); DISBUSY(WRITE_DATA_1(isac_cmd), ); NDBGL1(L1_I_CICO, "(isac_cmd=0x%x, unit=%d).", isac_cmd, S_UNIT); } } else { if (ihfc_cmd || (fsmtab->state == 5)) { SET_REG(0x30); WRITE_DATA_1(ihfc_cmd); NDBGL1(L1_I_CICO, "(ihfc_cmd=0x%x, unit=%d).", ihfc_cmd, S_UNIT); } } } /*---------------------------------------------------------------------------* * S/Q - channel handler (read) (HFC-S/SP) *---------------------------------------------------------------------------*/ void ihfc_sq (ihfc_sc_t *sc) { const struct ihfc_SQtable *SQtab; register u_char a = 0; BUS_VAR; if (S_HFC & HFC_1) { SET_REG(0x31); DISBUSY(a = READ_DATA_1, a = 0); if (a & 0x80) { SET_REG(0x3b); DISBUSY(a = READ_DATA_1, a = 0); } } else { SET_REG(0x34); a = READ_DATA_1; } SQtab = (S_NTMODE) ? &ihfc_Qtable[a & 7]: &ihfc_Stable[a & 7]; if (a & 0x10) { if (SQtab->string) { NDBGL1(L1_I_CICO, "%s (unit=%d, int=%x)", SQtab->string, S_UNIT, S_INT_S1); } else { NDBGL1(L1_ERROR, "Unknown indication = %x (unit=%d)", a & 7, S_UNIT); } } } /*---------------------------------------------------------------------------* * Interrupt handler (HFC-1) *---------------------------------------------------------------------------*/ void ihfc_intr1 (ihfc_sc_t *sc) { u_char chan; u_char tmp; BUS_VAR; HFC_VAR; HFC_BEG; SET_REG(0x20); tmp = GET_STAT; DISBUSY(S_ISTA |= READ_DATA_1, ); if (S_ISTA & 0x04) /* CIRQ */ { HFC_FSM(sc, 0); ihfc_sq(sc); } S_INTR_ACTIVE = 1; if (S_ISTA & 0xc0) /* RPF or RME */ { chan = 1; if (S_FILTER) S_FILTER(sc, chan); } if (S_ISTA & 0x10) /* XPR */ { chan = 0; if (S_FILTER) S_FILTER(sc, chan); } if (tmp & 0x04) /* Timer elapsed (50ms) */ { SET_REG((S_CTMT | 0xf0) & 0xff); chan = 6; while(chan--) { if (chan == 1) break; if (S_FILTER) S_FILTER(sc, chan); HFC_END; DELAY(10); HFC_BEG; } } S_INTR_ACTIVE = 0; if (S_ISTA & 0x01) /* EXIR */ { SET_REG(0x24); DISBUSY(ihfc_exir_hdlr(sc, READ_DATA_1), ); } S_ISTA &= ~(0x1 | 0x4); HFC_END; } /*---------------------------------------------------------------------------* * Interrupt handler (HFC-S/SP) *---------------------------------------------------------------------------*/ void ihfc_intr2 (ihfc_sc_t *sc) { u_char chan; BUS_VAR; HFC_VAR; HFC_BEG; SET_REG(0x1e); S_INT_S1 = READ_DATA_1; /* this will enable new interrupts! */ if (S_INT_S1 & 0x40) { HFC_FSM(sc, 0); /* statemachine changed */ ihfc_sq(sc); } S_INTR_ACTIVE = 1; if (S_INT_S1 & 0x20) /* D-Channel frame (rx) */ { chan = 1; if (S_FILTER) S_FILTER(sc, chan); } if (S_INT_S1 & 0x04) /* D-Channel frame (tx) */ { chan = 0; if (S_FILTER && (~S_INT_S1 & 0x80)) S_FILTER(sc, chan); } if (S_INT_S1 & 0x80) /* Timer elapsed (50ms) */ { chan = 6; while(chan--) { if (chan == 1) continue; if (S_FILTER) S_FILTER(sc, chan); HFC_END; DELAY(10); HFC_BEG; } } S_INTR_ACTIVE = 0; HFC_END; } /*---------------------------------------------------------------------------* * Select a Bfifo (HFC-1/S/SP) * and return bytes in FIFO * * (this code is optimized) *---------------------------------------------------------------------------*/ u_short ihfc_Bsel_fifo(ihfc_sc_t *sc, u_char chan, u_char flag) { register u_char reg = 0x7e + chan; register u_short tmp = 0x100; register u_short z1; register u_short z2; BUS_VAR; if (S_HFC & (HFC_1 | HFC_S)) { if (S_LAST_CHAN != chan) { SET_REG(reg); DISBUSY(WAITBUSY_2( , return 0), return 0); S_LAST_CHAN = chan; } } else { SET_REG(0x10); WRITE_DATA_1(chan - 2); DISBUSY( , return 0); } #define FAST_READ (u_char)(tmp = READ_BOTH_2) #define FAST_STAT if (tmp & 0x100) DISBUSY( , return 0); SET_REG(reg ); FAST_STAT; z1 = FAST_READ; SET_REG(reg += 4); FAST_STAT; z1 |= FAST_READ << 8; SET_REG(reg += 4); FAST_STAT; z2 = FAST_READ; SET_REG(reg += 4); FAST_STAT; z2 |= READ_DATA_1 << 8; #undef FAST_READ #undef FAST_STAT z1 &= 0x1fff; z2 &= 0x1fff; z1 = 0x5ff - (z2 = z1 - z2 + ((z2 <= z1) ? 0 : 0x600)); if (chan & 1) return(z2); /* receive channel */ else return(z1); /* transmit channel */ } /*---------------------------------------------------------------------------* * Select a Dfifo (HFC-S/SP) * and return bytes, and frames in FIFO * * Flag values: * 0x00: select new fifo + update counters * 0x10: increment f1 + update counters * 0x20: increment f2 + update counters * * NOTE: The upper 16bits holds the number of frames in the FIFO. * NOTE: FIFO has to be selected before you can use flags 0x10/0x20. *---------------------------------------------------------------------------*/ u_int32_t ihfc_Dsel_fifo(ihfc_sc_t *sc, u_char chan, u_char flag) { register u_char reg = 0x90 + chan; register u_short tmp = 0x100; register u_char f1; register u_char f2; register u_short z1; register u_short z2; BUS_VAR; if (S_HFC & (HFC_1 | HFC_S)) { switch(flag) { case(0x10): case(0x20): SET_REG(reg); if (~GET_STAT & 1) WAITBUSY_2( , return 0); SET_REG(0xa2 - (flag & 0x10) + chan); DISBUSY(READ_DATA_1, return 0); SET_REG(reg); if (~GET_STAT & 1) WAITBUSY_2( , return 0); break; default: if (S_LAST_CHAN != chan) { SET_REG(reg); DISBUSY(WAITBUSY_2( , return 0), return 0); S_LAST_CHAN = chan; } break; } } else { switch(flag) { case(0x10): case(0x20): SET_REG(0xb8 - (flag & 0x10) + chan); READ_DATA_1; DISBUSY( , return 0); if (chan & 1) { /* Before reading a FIFO a change * * FIFO operation must be done. * * (see HFC-SP manual p.38) */ SET_REG(0x10); WRITE_DATA_1(chan | 4); DISBUSY( , return 0); } break; default: SET_REG(0x10); WRITE_DATA_1(chan | 4); DISBUSY( , return 0); break; } } #define FAST_READ (u_char)(tmp = READ_BOTH_2) #define FAST_STAT if (tmp & 0x100) DISBUSY( , return 0); if (S_HFC & HFC_SP) reg = 0x80 + chan; SET_REG(reg ); FAST_STAT; z1 = FAST_READ; SET_REG(reg += 4); FAST_STAT; z1 |= FAST_READ << 8; SET_REG(reg += 4); FAST_STAT; z2 = FAST_READ; SET_REG(reg += 4); FAST_STAT; z2 |= FAST_READ << 8; if (S_HFC & HFC_SP) reg += 0x26; SET_REG(reg -= 2); FAST_STAT; f1 = FAST_READ; SET_REG(reg += 4); FAST_STAT; f2 = READ_DATA_1; #undef FAST_READ #undef FAST_STAT if (~chan & 1) { /* XXX was Z1 */ S_HDLC_DZ_TAB[f1 & 0xf] = z2; /* We keep track of the 'Z' * * values for D-channel (tx),* * so we may calculate the # * * of FIFO bytes free when * * f1 != f2. */ z2 = S_HDLC_DZ_TAB[f2 & 0xf]; } z1 = 0x1ff - (z2 = (z1 - z2) & 0x1ff); f1 = 0xf - (f2 = (f1 - f2) & 0xf); if (chan & 1) return(z2 | (f2 << 16)); /* receive channel */ else return(z1 | (f1 << 16)); /* transmit channel */ } /*---------------------------------------------------------------------------* * Data handler for D channel(write) - chan 0 (HFC-S/SP) *---------------------------------------------------------------------------*/ void ihfc_hdlc_Dwrite (ihfc_sc_t *sc, u_char chan) { register u_int32_t sendlen; register u_short len; register u_char * src; BUS_VAR; if (!S_MBUF && IF_QEMPTY(&S_IFQUEUE)) return; sendlen = ihfc_Dsel_fifo(sc, chan, 0); /* select new fifo * * NOTE: the 16 higher bits * * contain the # of frame- * * etries free in the FIFO */ while (sendlen & ~0xffff) { if (!S_MBUF) { if (!(S_MBUF = ihfc_getmbuf(sc, chan))) goto j1; } src = S_MBUFDATA; len = S_MBUFLEN; if (len >= 0x1ff) goto j0; /* frame is too big: skip! */ sendlen &= 0xffff; /* only keep # of * * bytes free */ SET_REG((S_HFC & HFC_SP) ? 0xac : 0x96); while (sendlen--) { if (!len--) break; DISBUSY(WRITE_DATA_1(*src++), sendlen = -1; len++; break); } if (!++sendlen) /* out of fifo: suspend */ { S_MBUFDATA = src; S_MBUFLEN = len; break; } sendlen = ihfc_Dsel_fifo(sc, chan, 0x10); /* inc F1 */ j0: i4b_Dfreembuf(S_MBUF); S_MBUF = NULL; } j1: } /*---------------------------------------------------------------------------* * Data handler for D channel(read) - chan 1 (HFC-S/SP) * * NOTE: Max framelength is (511 - 3) = 508 bytes, when only one frame * is received at a time. *---------------------------------------------------------------------------*/ void ihfc_hdlc_Dread (ihfc_sc_t *sc, u_char chan) { register u_char tmp = -1; register u_char to = 15; register u_int32_t reclen; register u_short crc; register u_short len; register u_char * dst; BUS_VAR; reclen = ihfc_Dsel_fifo(sc, chan, 0); /* select new fifo * * NOTE: the higher 16 bits * * contain the # of frames * * to receive. */ while ((reclen & ~0xffff) && to--) { reclen &= 0xffff; /* only keep # of * * bytes to receive */ if (!(S_MBUF = i4b_Dgetmbuf(DCH_MAX_LEN))) panic("ihfc_hdlc_Dread: No mbufs(unit=%d)!\n", S_UNIT); SET_REG((S_HFC & HFC_SP) ? 0xbd : 0xa7); if ((reclen > 2) && (reclen <= (DCH_MAX_LEN+2))) { dst = S_MBUFDATA; len = S_MBUFLEN = (reclen += 1) - 3; } else { len = 0; dst = NULL; } crc = -1; /* NOTE: after a "F1" or "Z1" hardware overflow * * it appears not to be necessary to reset the * * HFC-1/S or SP chips to continue proper * * operation, only and only, if we always read * * "Z1-Z2+1" bytes when F1!=F2 followed by a * * F2-counter increment. The bi-effect of doing * * this is the "STAT" field may say frame is ok * * when the frame is actually bad. * * The simple solution is to re-CRC the frame * * including "STAT" field to see if we get * * CRC == 0x3933. Then we're 99% sure all * * frames received are good. */ while(reclen--) { DISBUSY(tmp = READ_DATA_1, break); if (len) { len--; *dst++ = tmp; } crc = (HDLC_FCS_TAB[(u_char)(tmp ^ crc)] ^ (u_char)(crc >> 8)); } crc ^= 0x3933; if (!tmp && !crc) { ihfc_putmbuf(sc, chan, S_MBUF); S_MBUF = NULL; } else { NDBGL1(L1_ERROR, "Frame error (len=%d, stat=0x%x, " "crc=0x%x, unit=%d)", S_MBUFLEN, (u_char)tmp, crc, S_UNIT); i4b_Dfreembuf(S_MBUF); S_MBUF = NULL; } reclen = ihfc_Dsel_fifo(sc, chan, 0x20); } } /*---------------------------------------------------------------------------* * EXIR error handler - ISAC (D - channel) (HFC-1) *---------------------------------------------------------------------------*/ void ihfc_exir_hdlr (ihfc_sc_t *sc, u_char exir) { register u_char a; register u_char cmd; for (a = 0, cmd = 0; exir; a++, exir >>= 1) { if (exir & 1) { NDBGL1(L1_I_ERR, "%s. (unit=%d)", ihfc_EXIRtable[a].string, S_UNIT); cmd |= ihfc_EXIRtable[a].cmd; } } if (cmd) ihfc_cmdr_hdlr(sc, cmd); } /*---------------------------------------------------------------------------* * CMDR handler - ISAC (D - channel) (HFC-1) *---------------------------------------------------------------------------*/ void ihfc_cmdr_hdlr (ihfc_sc_t *sc, u_char cmdr) { BUS_VAR; SET_REG(0x21); DISBUSY(WRITE_DATA_1(cmdr); DELAY(30), ); } /*---------------------------------------------------------------------------* * Data handler for D channel(write) - chan 0 (HFC-1) *---------------------------------------------------------------------------*/ void ihfc_isac_Dwrite (ihfc_sc_t *sc, u_char chan) { register u_char sendlen = 32; register u_char cmd = 0; register u_short len; register u_char * src; BUS_VAR; if (~S_ISTA & 0x10) goto j0; if (!S_MBUF) if (!(S_MBUF = ihfc_getmbuf(sc, chan))) goto j0; len = S_MBUFLEN; src = S_MBUFDATA; SET_REG(0x00); while(sendlen--) /* write data */ { if (!len--) break; DISBUSY(WRITE_DATA_1(*src++), goto a0); } cmd |= 0x08; if (!++sendlen) /* suspend */ { S_MBUFLEN = len; S_MBUFDATA = src; } else { a0: i4b_Dfreembuf(S_MBUF); S_MBUF = NULL; cmd |= 0x02; } if (cmd) ihfc_cmdr_hdlr(sc, cmd); S_ISTA &= ~0x10; j0: } /*---------------------------------------------------------------------------* * Data handler for D channel(read) - chan 1 (HFC-1) *---------------------------------------------------------------------------*/ void ihfc_isac_Dread (ihfc_sc_t *sc, u_char chan) { register u_char cmd = 0; register u_char reclen; register u_short tmp; register u_short len; register u_char * dst; BUS_VAR; if (!(S_ISTA & 0xc0)) goto j1; /* only receive data * * on interrupt */ if (!S_MBUF) { if (!(S_MBUF = i4b_Dgetmbuf(DCH_MAX_LEN))) panic("ihfc%d: (D) Out of mbufs!\n", S_UNIT); } len = S_MBUFLEN; dst = S_MBUFDATA + (DCH_MAX_LEN - len); if (S_ISTA & 0x80) /* RME */ { SET_REG(0x27); DISBUSY(tmp = (READ_DATA_1 ^ 0x20), goto j0); if (tmp & 0x70) goto j0; /* error */ SET_REG(0x25); DISBUSY(tmp = (READ_DATA_1 & 0x1f), goto j0); reclen = (tmp) ? tmp : 32; } else /* RPF */ { reclen = 32; } if ((len -= reclen) <= DCH_MAX_LEN) /* get data */ { SET_REG(0x00); while(reclen--) { DISBUSY(*dst++ = READ_DATA_1, goto j0); } } else /* soft rdo or error */ { j0: i4b_Dfreembuf(S_MBUF); S_MBUF = NULL; cmd |= 0x40; NDBGL1(L1_I_ERR, "Frame error (unit=%d)", S_UNIT); } if (S_ISTA & 0x80) /* frame complete */ { if (S_MBUF) { S_MBUFLEN = (DCH_MAX_LEN - len); ihfc_putmbuf(sc, chan, S_MBUF); S_MBUF = NULL; } } if (S_MBUF) /* suspend */ { S_MBUFLEN = len; } ihfc_cmdr_hdlr(sc, cmd | 0x80); S_ISTA &= ~0xc0; j1: } /*---------------------------------------------------------------------------* * Data handler for B channel(write) - chan 2 and 4 (HFC-1/S/SP) * * NOTE: No XDU checking! *---------------------------------------------------------------------------*/ void ihfc_trans_Bwrite (ihfc_sc_t *sc, u_char chan) { register u_short sendlen; register u_short len; register u_char * src; BUS_VAR; if (!S_MBUF && IF_QEMPTY(&S_IFQUEUE)) return; sendlen = (u_short)ihfc_Bsel_fifo(sc, chan, 0); SET_REG(0xaa + chan); while (1) { if (!S_MBUF) { S_MBUF = ihfc_getmbuf(sc, chan); if (!S_MBUF) break; } src = S_MBUFDATA; len = S_MBUFLEN; while (sendlen--) { if (!len--) break; DISBUSY(WRITE_DATA_1(*src++), sendlen = -1; len++; break); } if (!++sendlen) /* out of fifo: Suspend */ { S_MBUFDATA = src; S_MBUFLEN = len; break; } i4b_Dfreembuf(S_MBUF); S_MBUF = NULL; } } /*---------------------------------------------------------------------------* * Data handler for B channel(read) - chan 3 and 5 (HFC-1/S/SP) * (this code is optimized) *---------------------------------------------------------------------------*/ void ihfc_trans_Bread (ihfc_sc_t *sc, u_char chan) { register u_short reclen; register u_short tmp; register u_short len; register u_char * dst; BUS_VAR; reclen = (u_short)ihfc_Bsel_fifo(sc, chan, 0); while (1) { SET_REG(0xba + chan); tmp = 0x100; if (!S_MBUF) if (!(S_MBUF = i4b_Bgetmbuf(BCH_MAX_DATALEN))) panic("ihfc%d: (B) Out of mbufs!\n", S_UNIT); len = S_MBUFLEN; dst = S_MBUFDATA + (BCH_MAX_DATALEN - len); while (reclen--) { if (!len--) break; if (tmp & 0x100) DISBUSY( , reclen = -1; len++; break); *dst++ = (u_char)(tmp = READ_BOTH_2); } if (~tmp & 0x100) { SET_REG(0x30); READ_DATA_1; /* a read to the data port * * will disable the internal * * disbusy signal for HFC-1/S * * chips. This is neccessary * * to avvoid data loss. */ } if (!++reclen) /* out of fifo: suspend */ { S_MBUFLEN = len; break; } S_MBUFLEN = (BCH_MAX_DATALEN - ++len); ihfc_putmbuf(sc, chan, S_MBUF); S_MBUF = NULL; } } /*---------------------------------------------------------------------------* * Data handler for B channel(write) - chan 2 and 4 (HFC-1/S/SP) * * NOTE: Software HDLC encoding! *---------------------------------------------------------------------------*/ void ihfc_hdlc_Bwrite (ihfc_sc_t *sc, u_char chan) { register u_short blevel = S_HDLC_BLEVEL; register u_char flag = S_HDLC_FLAG; register u_int tmp = S_HDLC_TMP; register u_short crc = S_HDLC_CRC; register u_short ib = S_HDLC_IB; register u_char * src = NULL; register u_short len = 0; register u_short sendlen; register u_short tmp2; BUS_VAR; if (!S_MBUF && IF_QEMPTY(&S_IFQUEUE) && (flag == 2)) return; sendlen = (u_short)ihfc_Bsel_fifo(sc, chan, 0); SET_REG(0xaa + chan); if (S_MBUF) { /* resume */ src = S_MBUFDATA; len = S_MBUFLEN; if (sendlen == 0x5ff) { /* XDU */ flag = -2; len = 0; NDBGL1(L1_S_ERR, "XDU (unit=%d)", S_UNIT); } } while (sendlen--) { HDLC_ENCODE(*src++, len, tmp, tmp2, blevel, ib, crc, flag, {/* gfr */ i4b_Bfreembuf(S_MBUF); S_MBUF = ihfc_getmbuf(sc, chan); if (S_MBUF) { src = S_MBUFDATA; len = S_MBUFLEN; } else { sendlen = 0; /* Exit after final FS, * * else the buffer will * * only be filled with * * "0x7e"-bytes! */ } }, {/* wrd */ DISBUSY(WRITE_DATA_1((u_char)tmp), sendlen = 0); }, d ); } if (S_MBUF) /* suspend */ { S_MBUFDATA = src; S_MBUFLEN = len; } S_HDLC_IB = ib; S_HDLC_BLEVEL = blevel; S_HDLC_TMP = tmp; S_HDLC_FLAG = flag; S_HDLC_CRC = crc; } /*---------------------------------------------------------------------------* * Data handler for B channel(read) - chan 3 and 5 (HFC-1/S/SP) * * NOTE: Software HDLC decoding! *---------------------------------------------------------------------------*/ void ihfc_hdlc_Bread (ihfc_sc_t *sc, u_char chan) { register u_char blevel = S_HDLC_BLEVEL; u_char flag = S_HDLC_FLAG; register u_short crc = S_HDLC_CRC; register u_int tmp = S_HDLC_TMP; register u_short ib = S_HDLC_IB; register u_char * dst = NULL; register u_short tmp2 = 0x100; register u_short len = 0; register u_short reclen; BUS_VAR; if (S_MBUF) { /* resume */ len = S_MBUFLEN; dst = S_MBUFDATA + (BCH_MAX_DATALEN - len); } reclen = (u_short)ihfc_Bsel_fifo(sc, chan, 0); SET_REG(0xba + chan); while (reclen--) { HDLC_DECODE(*dst++, len, tmp, tmp2, blevel, ib, crc, flag, {/* rdd */ /* if (tmp2 & 0x100) while (GET_STAT & 1); * tmp2 = READ_BOTH_2; */ DISBUSY(tmp2 = READ_DATA_1, reclen = 0; tmp2 = 0); }, {/* nfr */ if (!(S_MBUF = i4b_Bgetmbuf(BCH_MAX_DATALEN))) panic("ihfc:(B) Out of mbufs!\n"); dst = S_MBUFDATA; len = BCH_MAX_DATALEN; }, {/* cfr */ len = (BCH_MAX_DATALEN - len); if ((!len) || (len > BCH_MAX_DATALEN)) { /* NOTE: frames without any data, * * only crc field, should be silently discared. */ i4b_Bfreembuf(S_MBUF); NDBGL1(L1_S_MSG, "Bad frame (len=%d, unit=%d)", len, S_UNIT); goto s0; } if (crc) { i4b_Bfreembuf(S_MBUF); NDBGL1(L1_S_ERR, "CRC (crc=0x%04x, len=%d, unit=%d)", crc, len, S_UNIT); goto s0; } S_MBUFLEN = len; ihfc_putmbuf(sc, chan, S_MBUF); s0: S_MBUF = NULL; }, {/* rab */ i4b_Bfreembuf(S_MBUF); S_MBUF = NULL; NDBGL1(L1_S_MSG, "Read Abort (unit=%d)", S_UNIT); }, {/* rdo */ i4b_Bfreembuf(S_MBUF); S_MBUF = NULL; NDBGL1(L1_S_ERR, "RDO (unit=%d)", S_UNIT); }, continue, d); } /* SET_REG(0x30); * if (~tmp2 & 0x100) READ_DATA_1; kill disbusy signal */ if (S_MBUF) S_MBUFLEN = len; /* suspend */ S_HDLC_IB = ib; S_HDLC_CRC = crc; S_HDLC_TMP = tmp; S_HDLC_FLAG = flag; S_HDLC_BLEVEL = blevel; } /*---------------------------------------------------------------------------* * Data handler for B channel(write) - chan 2 and 4 (HFC-1/S/SP) * * This filter generates a pattern which is recognized * and examinated and verified by ihfc_test_Bread. * * NOTE: This filter is only for testing purpose. *---------------------------------------------------------------------------*/ void ihfc_test_Bwrite (ihfc_sc_t *sc, u_char chan) { struct mbuf *m; register u_char fb; register u_short sendlen, tlen; register u_short xlen = S_HDLC_IB; BUS_VAR; goto j0; while((m = ihfc_getmbuf(sc, chan))) /* internal loop */ { if (chan == 2) ihfc_putmbuf(sc, 5, m); else ihfc_putmbuf(sc, 3, m); } j0: sendlen = /* (u_short)ihfc_Bsel_fifo(sc, chan, 0); */ 0; if (sendlen == 0x5ff) printf("(send empty)"); SET_REG(0xaa + chan); S_BYTES += sendlen; tlen = S_HDLC_CRC; if (sendlen > 0x400) printf("(slow: %d)", sendlen); fb = 0x80; while (sendlen--) { if (!tlen--) fb |= 0x20; if (!xlen--) { while(GET_STAT & 1); WRITE_DATA_1(0x3e); xlen = 200; } else { while(GET_STAT & 1); WRITE_DATA_1((xlen + 1) & 0xef); } fb = 0; } S_HDLC_IB = xlen; } /*---------------------------------------------------------------------------* * Data handler for B channel(read) - chan 3 and 5 (HFC-1/S/SP) * * This filter examins and verifies the pattern * generated by ihfc_test_Bwrite. * * NOTE: This filter is only for testing purpose. *---------------------------------------------------------------------------*/ void ihfc_test_Bread (ihfc_sc_t *sc, u_char chan) { static u_short toterrors = 0; register u_short reclen, len, tlen; register u_char fb, tmp; register u_short xlen = S_HDLC_IB; register u_char *dst = NULL; register u_char error = S_HDLC_TMP; register u_char ecount = S_HDLC_FLAG; BUS_VAR; if (S_UNIT != 0) return; reclen = /* (u_short)ihfc_Bsel_fifo(sc, chan, 0); */ 0; S_BYTES += reclen; tlen = S_HDLC_CRC; fb = 0x40; if (S_MBUF) { len = S_MBUFLEN; dst = S_MBUFDATA + (BCH_MAX_DATALEN - len); } else { len = 0; } SET_REG(0xba + chan); while (reclen--) { /* if (tmp2 & 0x100) while(GET_STAT & 1); * tmp = (u_char)(tmp2 = READ_BOTH_2); */ if (GET_STAT & 1) { /* if (!(++busy % 4)) reclen++; */ while(GET_STAT & 1); } tmp = READ_DATA_1; if ((tmp & 0x3f) == 0x3e) { if ((BCH_MAX_DATALEN - len) != 201) error |= 4; if ((S_MBUF) && (error)) { if (len) { len--; *dst++ = error; } if (len) { len--; *dst++ = xlen+1; } if (len) { len--; *dst++ = ecount; } S_MBUFLEN = BCH_MAX_DATALEN - len; if (S_TRACE & TRACE_B_RX) ihfc_putmbuf(sc, chan, S_MBUF); else i4b_Bfreembuf(S_MBUF); S_MBUF = NULL; printf("(error%d, %d, %d)", S_UNIT, ecount, toterrors++); } i4b_Bfreembuf(S_MBUF); S_MBUF = i4b_Bgetmbuf(BCH_MAX_DATALEN); dst = S_MBUFDATA; len = BCH_MAX_DATALEN; xlen = 200; error = 0; ecount = 0; /* SET_REG(0xba + chan); */ } else { if (!xlen) error |= 2; if ((tmp ^ xlen--) & 0xef) { error |= 1; ecount++; } } if (!tlen--) fb |= 0x20; if (len--) { *dst++ = (tmp | fb); } else { len++; } fb = 0; } if (S_MBUF) { S_MBUFLEN = len; } S_HDLC_IB = xlen; S_HDLC_TMP = error; S_HDLC_FLAG = ecount; } #endif /* NIHFC > 0 */