/* * Written by Oron Peled * Copyright (C) 2004-2006, Xorcom * * Parts derived from Cologne demo driver for the chip. * * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include "xpd.h" #include "xproto.h" #include "xpp_dahdi.h" #include "card_pri.h" #include "dahdi_debug.h" #include "xpd.h" #include "xbus-core.h" static const char rcsid[] = "$Id$"; static DEF_PARM(int, debug, 0, 0644, "Print DBG statements"); /* must be before dahdi_debug.h */ static DEF_PARM(uint, poll_interval, 500, 0644, "Poll channel state interval in milliseconds (0 - disable)"); #define PRI_LINES_BITMASK BITMASK(31) #define PRI_SIGCAP ( \ DAHDI_SIG_EM | \ DAHDI_SIG_CLEAR | \ DAHDI_SIG_FXSLS | \ DAHDI_SIG_FXSGS | \ DAHDI_SIG_FXSKS | \ DAHDI_SIG_HARDHDLC | \ DAHDI_SIG_MTP2 | \ DAHDI_SIG_FXOLS | \ DAHDI_SIG_FXOGS | \ DAHDI_SIG_FXOKS | \ DAHDI_SIG_CAS | \ DAHDI_SIG_EM_E1 | \ DAHDI_SIG_DACS_RBS \ ) static bool is_sigtype_dchan(int sigtype) { if((sigtype & DAHDI_SIG_HDLCRAW) == DAHDI_SIG_HDLCRAW) return 1; if((sigtype & DAHDI_SIG_HDLCFCS) == DAHDI_SIG_HDLCFCS) return 1; if((sigtype & DAHDI_SIG_HARDHDLC) == DAHDI_SIG_HARDHDLC) return 1; return 0; } #define MAX_SLAVES 4 /* we have MUX of 4 clocks */ #define PRI_PORT(xpd) ((xpd)->addr.subunit) #define CHAN_PER_REGS(p) (((p)->is_esf) ? 2 : 4) /*---------------- PRI Protocol Commands ----------------------------------*/ static bool pri_packet_is_valid(xpacket_t *pack); static void pri_packet_dump(const char *msg, xpacket_t *pack); #ifdef OLD_PROC static int proc_pri_info_read(char *page, char **start, off_t off, int count, int *eof, void *data); static int proc_pri_info_write(struct file *file, const char __user *buffer, unsigned long count, void *data); #endif static int pri_startup(struct dahdi_span *span); static int pri_shutdown(struct dahdi_span *span); static int pri_rbsbits(struct dahdi_chan *chan, int bits); static int pri_lineconfig(xpd_t *xpd, int lineconfig); static void send_idlebits(xpd_t *xpd, bool saveold); #define PROC_REGISTER_FNAME "slics" #ifdef OLD_PROC #define PROC_PRI_INFO_FNAME "pri_info" #endif enum pri_protocol { PRI_PROTO_0 = 0, PRI_PROTO_E1 = 1, PRI_PROTO_T1 = 2, PRI_PROTO_J1 = 3 }; static const char *pri_protocol_name(enum pri_protocol pri_protocol) { static const char *protocol_names[] = { [PRI_PROTO_0] = "??", /* unknown */ [PRI_PROTO_E1] = "E1", [PRI_PROTO_T1] = "T1", [PRI_PROTO_J1] = "J1" }; return protocol_names[pri_protocol]; } static int pri_num_channels(enum pri_protocol pri_protocol) { static int num_channels[] = { [PRI_PROTO_0] = 0, [PRI_PROTO_E1] = 31, [PRI_PROTO_T1] = 24, [PRI_PROTO_J1] = 0 }; return num_channels[pri_protocol]; } static const char *type_name(enum pri_protocol pri_protocol) { static const char *names[4] = { [PRI_PROTO_0] = "PRI-Unknown", [PRI_PROTO_E1] = "E1", [PRI_PROTO_T1] = "T1", [PRI_PROTO_J1] = "J1" }; return names[pri_protocol]; } static int pri_linecompat(enum pri_protocol pri_protocol) { static const int linecompat[] = { [PRI_PROTO_0] = 0, [PRI_PROTO_E1] = /* coding */ DAHDI_CONFIG_CCS | DAHDI_CONFIG_CRC4 | /* framing */ DAHDI_CONFIG_AMI | DAHDI_CONFIG_HDB3, [PRI_PROTO_T1] = /* coding */ DAHDI_CONFIG_D4 | DAHDI_CONFIG_ESF | /* framing */ DAHDI_CONFIG_AMI | DAHDI_CONFIG_B8ZS, [PRI_PROTO_J1] = 0 }; DBG(GENERAL, "pri_linecompat: pri_protocol=%d\n", pri_protocol); return linecompat[pri_protocol]; } #define PRI_DCHAN_IDX(priv) ((priv)->dchan_num - 1) enum pri_led_state { PRI_LED_OFF = 0x0, PRI_LED_ON = 0x1, /* * We blink by software from driver, so that * if the driver malfunction that blink would stop. */ // PRI_LED_BLINK_SLOW = 0x2, /* 1/2 a second blink cycle */ // PRI_LED_BLINK_FAST = 0x3 /* 1/4 a second blink cycle */ }; enum pri_led_selectors { BOTTOM_RED_LED = 0, BOTTOM_GREEN_LED = 1, TOP_RED_LED = 2, TOP_GREEN_LED = 3, }; #define NUM_LEDS 4 struct pri_leds { byte state:2; /* enum pri_led_state */ byte led_sel:2; /* enum pri_led_selectors */ byte reserved:4; }; #define REG_CCB1_T 0x2F /* Clear Channel Register 1 */ #define REG_FRS0 0x4C /* Framer Receive Status Register 0 */ #define REG_FRS0_T1_FSR BIT(0) /* T1 - Frame Search Restart Flag */ #define REG_FRS0_LMFA BIT(1) /* Loss of Multiframe Alignment */ #define REG_FRS0_E1_NMF BIT(2) /* E1 - No Multiframe Alignment Found */ #define REG_FRS0_RRA BIT(4) /* Receive Remote Alarm: T1-YELLOW-Alarm */ #define REG_FRS0_LFA BIT(5) /* Loss of Frame Alignment */ #define REG_FRS0_AIS BIT(6) /* Alarm Indication Signal: T1-BLUE-Alarm */ #define REG_FRS0_LOS BIT(7) /* Los Of Signal: T1-RED-Alarm */ #define REG_FRS1 0x4D /* Framer Receive Status Register 1 */ #define REG_LIM0 0x36 #define REG_LIM0_MAS BIT(0) /* Master Mode, DCO-R circuitry is frequency synchronized to the clock supplied by SYNC */ #define REG_LIM0_RTRS BIT(5) /* * Receive Termination Resistance Selection: * integrated resistor to create 75 Ohm termination (100 || 300 = 75) * 0 = 100 Ohm * 1 = 75 Ohm */ #define REG_LIM0_LL BIT(1) /* LL (Local Loopback) */ #define REG_FMR0 0x1C #define REG_FMR0_E_RC0 BIT(4) /* Receive Code - LSB */ #define REG_FMR0_E_RC1 BIT(5) /* Receive Code - MSB */ #define REG_FMR0_E_XC0 BIT(6) /* Transmit Code - LSB */ #define REG_FMR0_E_XC1 BIT(7) /* Transmit Code - MSB */ #define REG_FMR1 0x1D #define REG_FMR1_XAIS BIT(0) /* Transmit AIS toward transmit end */ #define REG_FMR1_SSD0 BIT(1) #define REG_FMR1_ECM BIT(2) #define REG_FMR1_T_CRC BIT(3) /* Enable CRC6 */ #define REG_FMR1_E_XFS BIT(3) /* Transmit Framing Select */ #define REG_FMR1_PMOD BIT(4) /* E1 = 0, T1/J1 = 1 */ #define REG_FMR1_EDL BIT(5) #define REG_FMR1_AFR BIT(6) #define REG_FMR2 0x1E #define REG_FMR2_E_ALMF BIT(0) /* Automatic Loss of Multiframe */ #define REG_FMR2_T_EXZE BIT(0) /* Excessive Zeros Detection Enable */ #define REG_FMR2_E_AXRA BIT(1) /* Automatic Transmit Remote Alarm */ #define REG_FMR2_T_AXRA BIT(1) /* Automatic Transmit Remote Alarm */ #define REG_FMR2_E_PLB BIT(2) /* Payload Loop-Back */ #define REG_FMR2_E_RFS0 BIT(6) /* Receive Framing Select - LSB */ #define REG_FMR2_E_RFS1 BIT(7) /* Receive Framing Select - MSB */ #define REG_FMR2_T_SSP BIT(5) /* Select Synchronization/Resynchronization Procedure */ #define REG_FMR2_T_MCSP BIT(6) /* Multiple Candidates Synchronization Procedure */ #define REG_FMR2_T_AFRS BIT(7) /* Automatic Force Resynchronization */ #define REG_FMR3 0x31 #define REG_FMR3_EXTIW BIT(0) /* Extended CRC4 to Non-CRC4 Interworking */ #define REG_FMR4 0x20 #define REG_FMR4_FM0 BIT(0) #define REG_FMR4_FM1 BIT(1) #define REG_FMR4_AUTO BIT(2) #define REG_FMR4_SSC0 BIT(3) #define REG_FMR4_SSC1 BIT(4) #define REG_FMR4_XRA BIT(5) /* Transmit Remote Alarm (Yellow Alarm) */ #define REG_FMR4_TM BIT(6) #define REG_FMR4_AIS3 BIT(7) #define REG_XSW_E 0x20 #define REG_XSW_E_XY4 BIT(0) #define REG_XSW_E_XY3 BIT(1) #define REG_XSW_E_XY2 BIT(2) #define REG_XSW_E_XY1 BIT(3) #define REG_XSW_E_XY0 BIT(4) #define REG_XSW_E_XRA BIT(5) /* Transmit Remote Alarm (Yellow Alarm) */ #define REG_XSW_E_XTM BIT(6) #define REG_XSW_E_XSIS BIT(7) #define REG_XSP_E 0x21 #define REG_FMR5_T 0x21 #define REG_XSP_E_XSIF BIT(2) /* Transmit Spare Bit For International Use (FAS Word) */ #define REG_FMR5_T_XTM BIT(2) /* Transmit Transparent Mode */ #define REG_XSP_E_AXS BIT(3) /* Automatic Transmission of Submultiframe Status */ #define REG_XSP_E_EBP BIT(4) /* E-Bit Polarity, Si-bit position of every outgoing CRC multiframe */ #define REG_XSP_E_CASEN BIT(6) /* CAS: Channel Associated Signaling Enable */ #define REG_FMR5_T_EIBR BIT(6) /* CAS: Enable Internal Bit Robbing Access */ #define REG_XC0_T 0x22 /* Transmit Control 0 */ #define REG_XC0_BRIF BIT(5) /* Bit Robbing Idle Function */ #define REG_CMDR_E 0x02 /* Command Register */ #define REG_CMDR_RRES BIT(6) /* Receiver reset */ #define REG_CMDR_XRES BIT(4) /* Transmitter reset */ #define REG_RC0 0x24 #define REG_RC0_SJR BIT(7) /* T1 = 0, J1 = 1 */ #define REG_CMR1 0x44 #define REG_CMR1_DRSS (BIT(7) | BIT(6)) #define REG_CMR1_RS (BIT(5) | BIT(4)) #define REG_CMR1_STF BIT(2) #define REG_RS1_E 0x70 /* Receive CAS Register 1 */ #define REG_RS2_E 0x71 /* Receive CAS Register 2 */ #define REG_RS3_E 0x72 /* Receive CAS Register 3 */ #define REG_RS4_E 0x73 /* Receive CAS Register 4 */ #define REG_RS5_E 0x74 /* Receive CAS Register 5 */ #define REG_RS6_E 0x75 /* Receive CAS Register 6 */ #define REG_RS7_E 0x76 /* Receive CAS Register 7 */ #define REG_RS8_E 0x77 /* Receive CAS Register 8 */ #define REG_RS9_E 0x78 /* Receive CAS Register 9 */ #define REG_RS10_E 0x79 /* Receive CAS Register 10 */ #define REG_RS11_E 0x7A /* Receive CAS Register 11 */ #define REG_RS12_E 0x7B /* Receive CAS Register 12 */ #define REG_RS13_E 0x7C /* Receive CAS Register 13 */ #define REG_RS14_E 0x7D /* Receive CAS Register 14 */ #define REG_RS15_E 0x7E /* Receive CAS Register 15 */ #define REG_RS16_E 0x7F /* Receive CAS Register 16 */ #define REG_PC2 0x81 /* Port Configuration 2 */ #define REG_PC3 0x82 /* Port Configuration 3 */ #define REG_PC4 0x83 /* Port Configuration 4 */ #define REG_XPM2 0x28 /* Transmit Pulse Mask 2 */ #define VAL_PC_SYPR 0x00 /* Synchronous Pulse Receive (Input, low active) */ #define VAL_PC_GPI 0x90 /* General purpose input */ #define VAL_PC_GPOH 0x0A /* General Purpose Output, high level */ #define VAL_PC_GPOL 0x0B /* General Purpose Output, low level */ #define NUM_CAS_RS_E (REG_RS16_E - REG_RS2_E + 1) /* and of those, the ones used in T1: */ #define NUM_CAS_RS_T (REG_RS12_E - REG_RS1_E + 1) struct PRI_priv_data { bool clock_source; #ifdef OLD_PROC struct proc_dir_entry *pri_info; #endif enum pri_protocol pri_protocol; xpp_line_t rbslines; int deflaw; unsigned int dchan_num; bool initialized; int is_cas; unsigned int chanconfig_dchan; #define NO_DCHAN (0) #define DCHAN(p) ((p)->chanconfig_dchan) #define VALID_DCHAN(p) (DCHAN(p) != NO_DCHAN) #define SET_DCHAN(p,d) do { DCHAN(p) = (d); } while(0); byte cas_rs_e[NUM_CAS_RS_E]; byte cas_ts_e[NUM_CAS_RS_E]; uint cas_replies; bool is_esf; bool local_loopback; uint poll_noreplies; uint layer1_replies; byte reg_frs0; byte reg_frs1; bool layer1_up; int alarms; byte dchan_tx_sample; byte dchan_rx_sample; uint dchan_tx_counter; uint dchan_rx_counter; bool dchan_alive; uint dchan_alive_ticks; enum pri_led_state ledstate[NUM_LEDS]; }; static xproto_table_t PROTO_TABLE(PRI); DEF_RPACKET_DATA(PRI, SET_LED, /* Set one of the LED's */ struct pri_leds pri_leds; ); static /* 0x33 */ DECLARE_CMD(PRI, SET_LED, enum pri_led_selectors led_sel, enum pri_led_state to_led_state); #define DO_LED(xpd, which, tostate) \ CALL_PROTO(PRI, SET_LED, (xpd)->xbus, (xpd), (which), (tostate)) /*---------------- PRI: Methods -------------------------------------------*/ static int query_subunit(xpd_t *xpd, byte regnum) { XPD_DBG(REGS, xpd, "(%d%d): REG=0x%02X\n", xpd->addr.unit, xpd->addr.subunit, regnum); return xpp_register_request( xpd->xbus, xpd, PRI_PORT(xpd), /* portno */ 0, /* writing */ regnum, 0, /* do_subreg */ 0, /* subreg */ 0, /* data_L */ 0, /* do_datah */ 0, /* data_H */ 0 /* should_reply */ ); } static int write_subunit(xpd_t *xpd, byte regnum, byte val) { XPD_DBG(REGS, xpd, "(%d%d): REG=0x%02X dataL=0x%02X\n", xpd->addr.unit, xpd->addr.subunit, regnum, val); return xpp_register_request( xpd->xbus, xpd, PRI_PORT(xpd), /* portno */ 1, /* writing */ regnum, 0, /* do_subreg */ 0, /* subreg */ val, /* data_L */ 0, /* do_datah */ 0, /* data_H */ 0 /* should_reply */ ); } static int pri_write_reg(xpd_t *xpd, int regnum, byte val) { XPD_DBG(REGS, xpd, "(%d%d): REG=0x%02X dataL=0x%02X\n", xpd->addr.unit, xpd->addr.subunit, regnum, val); return xpp_register_request( xpd->xbus, xpd, 0, /* portno=0 */ 1, /* writing */ regnum, 0, /* do_subreg */ 0, /* subreg */ val, /* data_L */ 0, /* do_datah */ 0, /* data_H */ 0 /* should_reply */ ); } static int cas_regbase(xpd_t *xpd) { struct PRI_priv_data *priv; priv = xpd->priv; switch (priv->pri_protocol) { case PRI_PROTO_E1: return REG_RS2_E; case PRI_PROTO_T1: /* fall-through */ case PRI_PROTO_J1: return REG_RS1_E; case PRI_PROTO_0: /* fall-through */ ; } BUG(); return 0; } static int cas_numregs(xpd_t *xpd) { struct PRI_priv_data *priv; priv = xpd->priv; switch (priv->pri_protocol) { case PRI_PROTO_E1: return NUM_CAS_RS_E; case PRI_PROTO_T1: /* fall-through */ case PRI_PROTO_J1: return NUM_CAS_RS_T; case PRI_PROTO_0: /* fall-through */ ; } BUG(); return 0; } static int write_cas_reg(xpd_t *xpd, int rsnum, byte val) { struct PRI_priv_data *priv; int regbase = cas_regbase(xpd); int num_cas_rs = cas_numregs(xpd); int regnum; bool is_d4 = 0; BUG_ON(!xpd); priv = xpd->priv; if ((priv->pri_protocol == PRI_PROTO_T1) && !priv->is_esf) { /* same data should be copied to RS7..12 in D4 only */ is_d4 = 1; } if (rsnum < 0 || rsnum >= num_cas_rs) { XPD_ERR(xpd, "RBS(TX): rsnum=%d\n", rsnum); BUG(); } regnum = regbase + rsnum; priv->cas_ts_e[rsnum] = val; XPD_DBG(SIGNAL, xpd, "RBS(TX): reg=0x%X val=0x%02X\n", regnum, val); write_subunit(xpd, regbase + rsnum, val); if (is_d4) { /* same data should be copied to RS7..12 in D4 only */ regnum = REG_RS7_E + rsnum; XPD_DBG(SIGNAL, xpd, "RBS(TX): reg=0x%X val=0x%02X\n", regnum, val); write_subunit(xpd, regnum, val); } return 0; } #ifdef OLD_PROC static void pri_proc_remove(xbus_t *xbus, xpd_t *xpd) { struct PRI_priv_data *priv; BUG_ON(!xpd); priv = xpd->priv; XPD_DBG(PROC, xpd, "\n"); #ifdef CONFIG_PROC_FS if(priv->pri_info) { XPD_DBG(PROC, xpd, "Removing xpd PRI_INFO file\n"); remove_proc_entry(PROC_PRI_INFO_FNAME, xpd->proc_xpd_dir); } #endif } #endif #ifdef OLD_PROC static int pri_proc_create(xbus_t *xbus, xpd_t *xpd) { struct PRI_priv_data *priv; BUG_ON(!xpd); priv = xpd->priv; XPD_DBG(PROC, xpd, "\n"); #ifdef CONFIG_PROC_FS XPD_DBG(PROC, xpd, "Creating PRI_INFO file\n"); priv->pri_info = create_proc_entry(PROC_PRI_INFO_FNAME, 0644, xpd->proc_xpd_dir); if(!priv->pri_info) { XPD_ERR(xpd, "Failed to create proc '%s'\n", PROC_PRI_INFO_FNAME); goto err; } SET_PROC_DIRENTRY_OWNER(priv->pri_info); priv->pri_info->write_proc = proc_pri_info_write; priv->pri_info->read_proc = proc_pri_info_read; priv->pri_info->data = xpd; #endif return 0; err: pri_proc_remove(xbus, xpd); return -EINVAL; } #endif static bool valid_pri_modes(const xpd_t *xpd) { struct PRI_priv_data *priv; BUG_ON(!xpd); priv = xpd->priv; if( priv->pri_protocol != PRI_PROTO_E1 && priv->pri_protocol != PRI_PROTO_T1 && priv->pri_protocol != PRI_PROTO_J1) return 0; return 1; } static void PRI_card_pcm_recompute(xbus_t *xbus, xpd_t *xpd, xpp_line_t pcm_mask) { struct PRI_priv_data *priv; int i; int line_count = 0; unsigned long flags; uint pcm_len; BUG_ON(!xpd); priv = xpd->priv; spin_lock_irqsave(&xpd->lock_recompute_pcm, flags); //XPD_DBG(SIGNAL, xpd, "pcm_mask=0x%X\n", pcm_mask); /* Add/remove all the trivial cases */ pcm_mask |= xpd->offhook_state; for_each_line(xpd, i) if (IS_SET(pcm_mask, i)) line_count++; if(priv->is_cas) { if(priv->pri_protocol == PRI_PROTO_E1) { /* CAS: Don't send PCM to D-Channel */ line_count--; pcm_mask &= ~BIT(PRI_DCHAN_IDX(priv)); } } /* * FIXME: Workaround a bug in sync code of the Astribank. * Send dummy PCM for sync. */ if (xpd->addr.unit == 0 && pcm_mask == 0) { pcm_mask = BIT(0); line_count = 1; } pcm_len = (line_count) ? RPACKET_HEADERSIZE + sizeof(xpp_line_t) + line_count * DAHDI_CHUNKSIZE : 0L; update_wanted_pcm_mask(xpd, pcm_mask, pcm_len); spin_unlock_irqrestore(&xpd->lock_recompute_pcm, flags); } /* * Set E1/T1/J1 * May only be called on unregistered xpd's * (the span and channel description are set according to this) */ static int set_pri_proto(xpd_t *xpd, enum pri_protocol set_proto) { struct PRI_priv_data *priv; int deflaw; unsigned int dchan_num; int default_lineconfig = 0; int ret; BUG_ON(!xpd); priv = xpd->priv; if(SPAN_REGISTERED(xpd)) { XPD_NOTICE(xpd, "Registered as span %d. Cannot do setup pri protocol (%s)\n", xpd->span.spanno, __FUNCTION__); return -EBUSY; } if(priv->pri_protocol != PRI_PROTO_0) { if(priv->pri_protocol == set_proto) { XPD_NOTICE(xpd, "Already in protocol %s. Ignored\n", pri_protocol_name(set_proto)); return 0; } else { XPD_INFO(xpd, "Switching from %s to %s\n", pri_protocol_name(priv->pri_protocol), pri_protocol_name(set_proto)); } } switch(set_proto) { case PRI_PROTO_E1: deflaw = DAHDI_LAW_ALAW; dchan_num = 16; default_lineconfig = DAHDI_CONFIG_CCS | DAHDI_CONFIG_CRC4 | DAHDI_CONFIG_HDB3; break; case PRI_PROTO_T1: deflaw = DAHDI_LAW_MULAW; dchan_num = 24; default_lineconfig = DAHDI_CONFIG_ESF | DAHDI_CONFIG_B8ZS; break; case PRI_PROTO_J1: /* * Check all assumptions */ deflaw = DAHDI_LAW_MULAW; dchan_num = 24; default_lineconfig = 0; /* FIXME: J1??? */ XPD_NOTICE(xpd, "J1 is not supported yet\n"); return -ENOSYS; default: XPD_ERR(xpd, "%s: Unknown pri protocol = %d\n", __FUNCTION__, set_proto); return -EINVAL; } priv->pri_protocol = set_proto; priv->is_cas = -1; xpd->channels = pri_num_channels(set_proto); xpd->offhook_state = BITMASK(xpd->channels); CALL_XMETHOD(card_pcm_recompute, xpd->xbus, xpd, 0); priv->deflaw = deflaw; priv->dchan_num = dchan_num; priv->local_loopback = 0; xpd->type_name = type_name(priv->pri_protocol); XPD_DBG(GENERAL, xpd, "%s, channels=%d, dchan_num=%d, deflaw=%d\n", pri_protocol_name(set_proto), xpd->channels, priv->dchan_num, priv->deflaw ); /* * Must set default now, so layer1 polling (Register REG_FRS0) would * give reliable results. */ ret = pri_lineconfig(xpd, default_lineconfig); if(ret) { XPD_NOTICE(xpd, "Failed setting PRI default line config\n"); return ret; } return 0; } static void dahdi_update_syncsrc(xpd_t *xpd) { struct PRI_priv_data *priv; xpd_t *subxpd; int best_spanno = 0; int i; if(!SPAN_REGISTERED(xpd)) return; for(i = 0; i < MAX_SLAVES; i++) { subxpd = xpd_byaddr(xpd->xbus, xpd->addr.unit, i); if(!subxpd) continue; priv = subxpd->priv; if(priv->clock_source && priv->alarms == 0) { if(best_spanno) XPD_ERR(xpd, "Duplicate XPD's with clock_source=1\n"); best_spanno = subxpd->span.spanno; } } for(i = 0; i < MAX_SLAVES; i++) { subxpd = xpd_byaddr(xpd->xbus, xpd->addr.unit, i); if(!subxpd) continue; if(subxpd->span.syncsrc == best_spanno) XPD_DBG(SYNC, xpd, "Setting SyncSource to span %d\n", best_spanno); else XPD_DBG(SYNC, xpd, "Slaving to span %d\n", best_spanno); subxpd->span.syncsrc = best_spanno; } } /* * Called from: * - set_master_mode() -- * As a result of dahdi_cfg * - layer1_state() -- * As a result of an alarm. */ static void set_clocking(xpd_t *xpd) { xbus_t *xbus; xpd_t *best_xpd = NULL; int best_subunit = -1; /* invalid */ unsigned int best_subunit_prio = INT_MAX; int i; xbus = xpd->xbus; /* Find subunit with best timing priority */ for(i = 0; i < MAX_SLAVES; i++) { struct PRI_priv_data *priv; xpd_t *subxpd; subxpd = xpd_byaddr(xbus, xpd->addr.unit, i); if(!subxpd) continue; priv = subxpd->priv; if(priv->alarms != 0) continue; if(subxpd->timing_priority > 0 && subxpd->timing_priority < best_subunit_prio) { best_xpd = subxpd; best_subunit = i; best_subunit_prio = subxpd->timing_priority; } } /* Now set it */ if(best_xpd && ((struct PRI_priv_data *)(best_xpd->priv))->clock_source == 0) { byte reg_pc_init[] = { VAL_PC_GPI, VAL_PC_GPI, VAL_PC_GPI }; for(i = 0; i < ARRAY_SIZE(reg_pc_init); i++) { byte reg_pc = reg_pc_init[i]; reg_pc |= (best_subunit & (1 << i)) ? VAL_PC_GPOH : VAL_PC_GPOL; XPD_DBG(SYNC, best_xpd, "ClockSource Set: PC%d=0x%02X\n", 2+i, reg_pc); pri_write_reg(xpd, REG_PC2 + i, reg_pc); } ((struct PRI_priv_data *)(best_xpd->priv))->clock_source = 1; } /* clear old clock sources */ for(i = 0; i < MAX_SLAVES; i++) { struct PRI_priv_data *priv; xpd_t *subxpd; subxpd = xpd_byaddr(xbus, xpd->addr.unit, i); if(subxpd && subxpd != best_xpd) { XPD_DBG(SYNC, subxpd, "Clearing clock source\n"); priv = subxpd->priv; priv->clock_source = 0; } } dahdi_update_syncsrc(xpd); } static void set_reg_lim0(const char *msg, xpd_t *xpd) { struct PRI_priv_data *priv; bool is_master_mode; bool localloop; byte lim0 = 0; BUG_ON(!xpd); priv = xpd->priv; BUG_ON(!priv); is_master_mode = xpd->timing_priority == 0; localloop = priv->local_loopback; lim0 |= (localloop) ? REG_LIM0_LL : 0; if(is_master_mode) lim0 |= REG_LIM0_MAS; else lim0 &= ~REG_LIM0_MAS; XPD_DBG(SIGNAL, xpd, "%s(%s): %s, %s\n", __FUNCTION__, msg, (is_master_mode) ? "MASTER" : "SLAVE", (localloop) ? "LOCALLOOP" : "NO_LOCALLOOP"); write_subunit(xpd, REG_LIM0 , lim0); } /* * Normally set by the timing parameter in /etc/dahdi/system.conf * If this is called by dahdi_cfg, than it's too late to change * dahdi sync priority (we are already registered) * * Also called from set_localloop() */ static int set_master_mode(const char *msg, xpd_t *xpd) { struct PRI_priv_data *priv; BUG_ON(!xpd); XPD_DBG(SIGNAL, xpd, "\n"); priv = xpd->priv; set_reg_lim0(__FUNCTION__, xpd); set_clocking(xpd); return 0; } static int set_localloop(xpd_t *xpd, bool localloop) { struct PRI_priv_data *priv; BUG_ON(!xpd); priv = xpd->priv; if(SPAN_REGISTERED(xpd)) { XPD_NOTICE(xpd, "Registered as span %d. Cannot do %s\n", xpd->span.spanno, __FUNCTION__); return -EBUSY; } priv->local_loopback = localloop; XPD_DBG(SIGNAL, xpd, "%s: %s\n", __FUNCTION__, (localloop) ? "LOCALLOOP" : "NO"); set_master_mode(__FUNCTION__, xpd); return 0; } #define VALID_CONFIG(bit,flg,str) [bit] = { .flags = flg, .name = str } static const struct { const char *name; const int flags; } valid_spanconfigs[sizeof(unsigned int)*8] = { /* These apply to T1 */ VALID_CONFIG(4, DAHDI_CONFIG_D4, "D4"), VALID_CONFIG(5, DAHDI_CONFIG_ESF, "ESF"), VALID_CONFIG(6, DAHDI_CONFIG_AMI, "AMI"), VALID_CONFIG(7, DAHDI_CONFIG_B8ZS, "B8ZS"), /* These apply to E1 */ VALID_CONFIG(8, DAHDI_CONFIG_CCS, "CCS"), VALID_CONFIG(9, DAHDI_CONFIG_HDB3, "HDB3"), VALID_CONFIG(10, DAHDI_CONFIG_CRC4, "CRC4"), }; /* * Mark the lines as CLEAR or RBS signalling. * With T1, we need to mark the CLEAR lines on the REG_CCB1_T registers * Should be called only when we are registered to DAHDI * The channo parameter: * channo == 0: set lines for the whole span * channo != 0: only set modified lines */ static void set_rbslines(xpd_t *xpd, int channo) { struct PRI_priv_data *priv; xpp_line_t new_rbslines = 0; xpp_line_t modified_lines; int i; priv = xpd->priv; for_each_line(xpd, i) { struct dahdi_chan *chan = XPD_CHAN(xpd, i); if(chan->flags & DAHDI_FLAG_CLEAR) BIT_CLR(new_rbslines, i); else BIT_SET(new_rbslines, i); } new_rbslines &= BITMASK(xpd->channels); modified_lines = priv->rbslines ^ new_rbslines; XPD_DBG(DEVICES, xpd, "RBSLINES-%d(%s): 0x%X\n", channo, pri_protocol_name(priv->pri_protocol), new_rbslines); if((priv->pri_protocol == PRI_PROTO_T1) || (priv->pri_protocol == PRI_PROTO_J1)) { byte clear_lines = 0; /* Mark clear lines */ bool reg_changed = 0; for_each_line(xpd, i) { int bytenum = i / 8; int bitnum = i % 8; if(!IS_SET(new_rbslines, i)) { BIT_SET(clear_lines, (7 - bitnum)); } if(IS_SET(modified_lines, i)) reg_changed = 1; if(bitnum == 7) { if(channo == 0 || reg_changed) { bytenum += REG_CCB1_T; XPD_DBG(DEVICES, xpd, "RBS(%s): modified=0x%X rbslines=0x%X reg=0x%X clear_lines=0x%X\n", pri_protocol_name(priv->pri_protocol), modified_lines, new_rbslines, bytenum, clear_lines); write_subunit(xpd, bytenum, clear_lines); } clear_lines = 0; reg_changed = 0; } } } priv->rbslines = new_rbslines; } static int set_mode_cas(xpd_t *xpd, bool want_cas) { struct PRI_priv_data *priv; priv = xpd->priv; XPD_INFO(xpd, "Setting TDM to %s\n", (want_cas) ? "CAS" : "PRI"); if(want_cas) { priv->is_cas = 1; priv->dchan_alive = 0; } else { priv->is_cas = 0; } return 0; } static int pri_lineconfig(xpd_t *xpd, int lineconfig) { struct PRI_priv_data *priv; const char *framingstr = ""; const char *codingstr = ""; const char *crcstr = ""; #ifdef JAPANEZE_SUPPORT byte rc0 = 0; /* FIXME: PCM offsets */ #endif byte fmr0 = 0; byte fmr1 = REG_FMR1_ECM; byte fmr2 = 0; byte fmr3 = 0; /* write only for CRC4 */ byte fmr4 = 0; byte cmdr = REG_CMDR_RRES | REG_CMDR_XRES; byte xsp = 0; unsigned int bad_bits; bool force_cas = 0; int i; BUG_ON(!xpd); priv = xpd->priv; /* * validate */ bad_bits = lineconfig & pri_linecompat(priv->pri_protocol); bad_bits = bad_bits ^ lineconfig; for(i = 0; i < ARRAY_SIZE(valid_spanconfigs); i++) { unsigned int flags = valid_spanconfigs[i].flags; if(bad_bits & BIT(i)) { if(flags) { XPD_ERR(xpd, "Bad config item '%s' for %s. Ignore\n", valid_spanconfigs[i].name, pri_protocol_name(priv->pri_protocol)); } else { /* we got real garbage */ XPD_ERR(xpd, "Unknown config item 0x%lX for %s. Ignore\n", BIT(i), pri_protocol_name(priv->pri_protocol)); } } if(flags && flags != BIT(i)) { ERR("%s: BUG: i=%d flags=0x%X\n", __FUNCTION__, i, flags); // BUG(); } } if(bad_bits) goto bad_lineconfig; if(priv->pri_protocol == PRI_PROTO_E1) { fmr1 |= REG_FMR1_AFR; fmr2 = REG_FMR2_E_AXRA | REG_FMR2_E_ALMF; /* 0x03 */ fmr4 = 0x9F; /* E1.XSW: All spare bits = 1*/ xsp |= REG_XSP_E_EBP | REG_XSP_E_AXS | REG_XSP_E_XSIF; } else if(priv->pri_protocol == PRI_PROTO_T1) { fmr1 |= REG_FMR1_PMOD | REG_FMR1_T_CRC; fmr2 = REG_FMR2_T_SSP | REG_FMR2_T_AXRA; /* 0x22 */ fmr4 = 0x0C; xsp &= ~REG_FMR5_T_XTM; force_cas = 1; /* T1 - Chip always in CAS mode */ } else if(priv->pri_protocol == PRI_PROTO_J1) { fmr1 |= REG_FMR1_PMOD; fmr4 = 0x1C; xsp &= ~REG_FMR5_T_XTM; force_cas = 1; /* T1 - Chip always in CAS mode */ XPD_ERR(xpd, "J1 unsupported yet\n"); return -ENOSYS; } if(priv->local_loopback) fmr2 |= REG_FMR2_E_PLB; /* framing first */ if (lineconfig & DAHDI_CONFIG_B8ZS) { framingstr = "B8ZS"; fmr0 = REG_FMR0_E_XC1 | REG_FMR0_E_XC0 | REG_FMR0_E_RC1 | REG_FMR0_E_RC0; } else if (lineconfig & DAHDI_CONFIG_AMI) { framingstr = "AMI"; fmr0 = REG_FMR0_E_XC1 | REG_FMR0_E_RC1; /* * From Infineon Errata Sheet: PEF 22554, Version 3.1 * Problem: Incorrect CAS Receiption when * using AMI receive line code * Workaround: For E1, * "...The receive line coding HDB3 is * recommended instead." * For T1, * "...in T1 mode it is recommended to * configure the Rx side to B8ZS coding" * For both cases this is the same bit in FMR0 */ if(priv->pri_protocol == PRI_PROTO_J1) XPD_NOTICE(xpd, "J1 is not supported yet\n"); else fmr0 |= REG_FMR0_E_RC0; } else if (lineconfig & DAHDI_CONFIG_HDB3) { framingstr = "HDB3"; fmr0 = REG_FMR0_E_XC1 | REG_FMR0_E_XC0 | REG_FMR0_E_RC1 | REG_FMR0_E_RC0; } else { XPD_NOTICE(xpd, "Bad lineconfig. Not (B8ZS|AMI|HDB3). Ignored.\n"); return -EINVAL; } /* then coding */ priv->is_esf = 0; if (lineconfig & DAHDI_CONFIG_ESF) { codingstr = "ESF"; fmr4 |= REG_FMR4_FM1; fmr2 |= REG_FMR2_T_AXRA | REG_FMR2_T_MCSP | REG_FMR2_T_SSP; priv->is_esf = 1; } else if (lineconfig & DAHDI_CONFIG_D4) { codingstr = "D4"; } else if (lineconfig & DAHDI_CONFIG_CCS) { codingstr = "CCS"; set_mode_cas(xpd, 0); /* In E1 we know right from the span statement. */ } else { codingstr = "CAS"; /* In E1 we know right from the span statement. */ force_cas = 1; set_mode_cas(xpd, 1); } CALL_XMETHOD(card_pcm_recompute, xpd->xbus, xpd, 0); /* * E1's can enable CRC checking * CRC4 is legal only for E1, and it is checked by pri_linecompat() * in the beginning of the function. */ if (lineconfig & DAHDI_CONFIG_CRC4) { crcstr = "CRC4"; fmr1 |= REG_FMR1_E_XFS; fmr2 |= REG_FMR2_E_RFS1; fmr3 |= REG_FMR3_EXTIW; } XPD_DBG(GENERAL, xpd, "[%s] lineconfig=%s/%s/%s %s (0x%X)\n", (priv->clock_source)?"MASTER":"SLAVE", framingstr, codingstr, crcstr, (lineconfig & DAHDI_CONFIG_NOTOPEN)?"YELLOW":"", lineconfig); set_reg_lim0(__FUNCTION__, xpd); XPD_DBG(GENERAL, xpd, "%s: fmr1(0x%02X) = 0x%02X\n", __FUNCTION__, REG_FMR1, fmr1); write_subunit(xpd, REG_FMR1, fmr1); XPD_DBG(GENERAL, xpd, "%s: fmr2(0x%02X) = 0x%02X\n", __FUNCTION__, REG_FMR2, fmr2); write_subunit(xpd, REG_FMR2, fmr2); XPD_DBG(GENERAL, xpd, "%s: fmr0(0x%02X) = 0x%02X\n", __FUNCTION__, REG_FMR0, fmr0); write_subunit(xpd, REG_FMR0, fmr0); XPD_DBG(GENERAL, xpd, "%s: fmr4(0x%02X) = 0x%02X\n", __FUNCTION__, REG_FMR4, fmr4); write_subunit(xpd, REG_FMR4, fmr4); if(fmr3) { XPD_DBG(GENERAL, xpd, "%s: fmr3(0x%02X) = 0x%02X\n", __FUNCTION__, REG_FMR3, fmr3); write_subunit(xpd, REG_FMR3, fmr3); } XPD_DBG(GENERAL, xpd, "%s: cmdr(0x%02X) = 0x%02X\n", __FUNCTION__, REG_CMDR_E, cmdr); write_subunit(xpd, REG_CMDR_E, cmdr); #ifdef JAPANEZE_SUPPORT if(rc0) { XPD_DBG(GENERAL, xpd, "%s: rc0(0x%02X) = 0x%02X\n", __FUNCTION__, REG_RC0, rc0); write_subunit(xpd, REG_RC0, rc0); } #endif if(force_cas) { xsp |= REG_XSP_E_CASEN; /* Same as REG_FMR5_T_EIBR for T1 */ } XPD_DBG(GENERAL, xpd, "%s: xsp(0x%02X) = 0x%02X\n", __FUNCTION__, REG_XSP_E, xsp); write_subunit(xpd, REG_XSP_E, xsp); return 0; bad_lineconfig: XPD_ERR(xpd, "Bad lineconfig. Abort\n"); return -EINVAL; } /* * Called only for 'span' keyword in /etc/dahdi/system.conf */ static int pri_spanconfig(struct dahdi_span *span, struct dahdi_lineconfig *lc) { xpd_t *xpd = container_of(span, struct xpd, span); struct PRI_priv_data *priv; int ret; BUG_ON(!xpd); priv = xpd->priv; if(lc->span != xpd->span.spanno) { XPD_ERR(xpd, "I am span %d but got spanconfig for span %d\n", xpd->span.spanno, lc->span); return -EINVAL; } /* * FIXME: lc->name is unused by dahdi_cfg and dahdi... * We currently ignore it also. */ XPD_DBG(GENERAL, xpd, "[%s] lbo=%d lineconfig=0x%X sync=%d\n", (priv->clock_source)?"MASTER":"SLAVE", lc->lbo, lc->lineconfig, lc->sync); ret = pri_lineconfig(xpd, lc->lineconfig); if(!ret) { span->lineconfig = lc->lineconfig; xpd->timing_priority = lc->sync; set_master_mode("spanconfig", xpd); elect_syncer("PRI-master_mode"); } return ret; } /* * Set signalling type (if appropriate) * Called from dahdi with spinlock held on chan. Must not call back * dahdi functions. */ static int pri_chanconfig(struct dahdi_chan *chan, int sigtype) { xpd_t *xpd = container_of(chan->span, struct xpd, span); struct PRI_priv_data *priv; BUG_ON(!xpd); priv = xpd->priv; DBG(GENERAL, "channel %d (%s) -> %s\n", chan->channo, chan->name, sig2str(sigtype)); /* * Some bookkeeping to check if we have DChan defined or not * FIXME: actually use this to prevent duplicate DChan definitions * and prevent DChan definitions with CAS. */ if(is_sigtype_dchan(sigtype)) { if(VALID_DCHAN(priv) && DCHAN(priv) != chan->channo) { ERR("channel %d (%s) marked DChan but also channel %d.\n", chan->channo, chan->name, DCHAN(priv)); return -EINVAL; } else { XPD_DBG(GENERAL, xpd, "channel %d (%s) marked as DChan\n", chan->channo, chan->name); SET_DCHAN(priv, chan->channo); /* In T1, we don't know before-hand */ if(priv->pri_protocol != PRI_PROTO_E1 && priv->is_cas != 0) set_mode_cas(xpd, 0); } } else { if(chan->channo == 1) { XPD_DBG(GENERAL, xpd, "channel %d (%s) marked a not DChan\n", chan->channo, chan->name); SET_DCHAN(priv, NO_DCHAN); } /* In T1, we don't know before-hand */ if(priv->pri_protocol != PRI_PROTO_E1 && priv->is_cas != 1) set_mode_cas(xpd, 1); } if(xpd->span.flags & DAHDI_FLAG_RUNNING) { XPD_DBG(DEVICES, xpd, "Span is RUNNING. Updating rbslines.\n"); set_rbslines(xpd, chan->channo); } // FIXME: sanity checks: // - should be supported (within the sigcap) // - should not replace fxs <->fxo ??? (covered by previous?) return 0; } static xpd_t *PRI_card_new(xbus_t *xbus, int unit, int subunit, const xproto_table_t *proto_table, byte subtype, int subunits, int subunit_ports, bool to_phone) { xpd_t *xpd = NULL; struct PRI_priv_data *priv; int channels = min(31, CHANNELS_PERXPD); /* worst case */ if(subunit_ports != 1) { XBUS_ERR(xbus, "Bad subunit_ports=%d\n", subunit_ports); return NULL; } XBUS_DBG(GENERAL, xbus, "\n"); xpd = xpd_alloc(xbus, unit, subunit, subtype, subunits, sizeof(struct PRI_priv_data), proto_table, channels); if(!xpd) return NULL; priv = xpd->priv; priv->pri_protocol = PRI_PROTO_0; /* Default, changes in set_pri_proto() */ priv->deflaw = DAHDI_LAW_DEFAULT; /* Default, changes in set_pri_proto() */ xpd->type_name = type_name(priv->pri_protocol); #ifdef OLD_PROC if(pri_proc_create(xbus, xpd) < 0) { xpd_free(xpd); return NULL; } #endif xbus->sync_mode_default = SYNC_MODE_AB; return xpd; } static int PRI_card_init(xbus_t *xbus, xpd_t *xpd) { struct PRI_priv_data *priv; int ret = 0; xproto_table_t *proto_table; BUG_ON(!xpd); XPD_DBG(GENERAL, xpd, "\n"); xpd->type = XPD_TYPE_PRI; proto_table = &PROTO_TABLE(PRI); priv = xpd->priv; if(priv->pri_protocol == PRI_PROTO_0) { /* * init_card_* script didn't set pri protocol * Let's have a default E1 */ ret = set_pri_proto(xpd, PRI_PROTO_E1); if(ret < 0) goto err; } SET_DCHAN(priv, NO_DCHAN); /* * initialization script should have set correct * operating modes. */ if(!valid_pri_modes(xpd)) { XPD_NOTICE(xpd, "PRI protocol not set\n"); goto err; } xpd->type_name = type_name(priv->pri_protocol); xpd->direction = TO_PSTN; XPD_DBG(DEVICES, xpd, "%s\n", xpd->type_name); xpd->timing_priority = 1; /* High priority SLAVE */ set_master_mode(__FUNCTION__, xpd); for(ret = 0; ret < NUM_LEDS; ret++) { DO_LED(xpd, ret, PRI_LED_ON); msleep(20); DO_LED(xpd, ret, PRI_LED_OFF); } priv->initialized = 1; return 0; err: #ifdef OLD_PROC pri_proc_remove(xbus, xpd); #endif XPD_ERR(xpd, "Failed initializing registers (%d)\n", ret); return ret; } static int PRI_card_remove(xbus_t *xbus, xpd_t *xpd) { struct PRI_priv_data *priv; BUG_ON(!xpd); priv = xpd->priv; XPD_DBG(GENERAL, xpd, "\n"); #ifdef OLD_PROC pri_proc_remove(xbus, xpd); #endif return 0; } #ifdef DAHDI_AUDIO_NOTIFY static int pri_audio_notify(struct dahdi_chan *chan, int on) { xpd_t *xpd = chan->pvt; int pos = chan->chanpos - 1; BUG_ON(!xpd); LINE_DBG(SIGNAL, xpd, pos, "PRI-AUDIO: %s\n", (on) ? "on" : "off"); mark_offhook(xpd, pos, on); return 0; } #endif static const struct dahdi_span_ops PRI_span_ops = { .owner = THIS_MODULE, .spanconfig = pri_spanconfig, .chanconfig = pri_chanconfig, .startup = pri_startup, .shutdown = pri_shutdown, .rbsbits = pri_rbsbits, .open = xpp_open, .close = xpp_close, .ioctl = xpp_ioctl, .maint = xpp_maint, #ifdef DAHDI_SYNC_TICK .sync_tick = dahdi_sync_tick, #endif #ifdef CONFIG_DAHDI_WATCHDOG .watchdog = xpp_watchdog, #endif #ifdef DAHDI_AUDIO_NOTIFY .audio_notify = pri_audio_notify, #endif }; static int PRI_card_dahdi_preregistration(xpd_t *xpd, bool on) { xbus_t *xbus; struct PRI_priv_data *priv; int i; BUG_ON(!xpd); xbus = xpd->xbus; priv = xpd->priv; BUG_ON(!xbus); XPD_DBG(GENERAL, xpd, "%s (proto=%s, channels=%d, deflaw=%d)\n", (on)?"on":"off", pri_protocol_name(priv->pri_protocol), xpd->channels, priv->deflaw); if(!on) { /* Nothing to do yet */ return 0; } xpd->span.spantype = pri_protocol_name(priv->pri_protocol); xpd->span.linecompat = pri_linecompat(priv->pri_protocol); xpd->span.deflaw = priv->deflaw; for_each_line(xpd, i) { struct dahdi_chan *cur_chan = XPD_CHAN(xpd, i); bool is_dchan = i == PRI_DCHAN_IDX(priv); XPD_DBG(GENERAL, xpd, "setting PRI channel %d (%s)\n", i, (is_dchan)?"DCHAN":"CLEAR"); snprintf(cur_chan->name, MAX_CHANNAME, "XPP_%s/%02d/%1d%1d/%d", xpd->type_name, xbus->num, xpd->addr.unit, xpd->addr.subunit, i); cur_chan->chanpos = i + 1; cur_chan->pvt = xpd; cur_chan->sigcap = PRI_SIGCAP; if(is_dchan && !priv->is_cas) { /* D-CHAN */ //FIXME: cur_chan->flags |= DAHDI_FLAG_PRIDCHAN; cur_chan->flags &= ~DAHDI_FLAG_HDLC; } } xpd->offhook_state = xpd->wanted_pcm_mask; xpd->span.ops = &PRI_span_ops; return 0; } static int PRI_card_dahdi_postregistration(xpd_t *xpd, bool on) { xbus_t *xbus; struct PRI_priv_data *priv; BUG_ON(!xpd); xbus = xpd->xbus; priv = xpd->priv; BUG_ON(!xbus); XPD_DBG(GENERAL, xpd, "%s\n", (on)?"on":"off"); dahdi_update_syncsrc(xpd); return(0); } static void dchan_state(xpd_t *xpd, bool up) { struct PRI_priv_data *priv; BUG_ON(!xpd); priv = xpd->priv; BUG_ON(!priv); if(priv->is_cas) { return; } if(priv->dchan_alive == up) return; if(!priv->layer1_up) /* No layer1, kill dchan */ up = 0; if(up) { XPD_DBG(SIGNAL, xpd, "STATE CHANGE: D-Channel RUNNING\n"); priv->dchan_alive = 1; } else { int d = PRI_DCHAN_IDX(priv); if(SPAN_REGISTERED(xpd) && d >= 0 && d < xpd->channels) { byte *pcm; pcm = (byte *)XPD_CHAN(xpd, d)->readchunk; pcm[0] = 0x00; pcm = (byte *)XPD_CHAN(xpd, d)->writechunk; pcm[0] = 0x00; } XPD_DBG(SIGNAL, xpd, "STATE CHANGE: D-Channel STOPPED\n"); priv->dchan_rx_counter = priv->dchan_tx_counter = 0; priv->dchan_alive = 0; priv->dchan_alive_ticks = 0; priv->dchan_rx_sample = priv->dchan_tx_sample = 0x00; } } /* * LED managment is done by the driver now: * - Turn constant ON RED/GREEN led to indicate MASTER/SLAVE port * - Very fast "Double Blink" to indicate Layer1 alive (without D-Channel) * - Constant blink (1/2 sec cycle) to indicate D-Channel alive. */ static void handle_leds(xbus_t *xbus, xpd_t *xpd) { struct PRI_priv_data *priv; unsigned int timer_count; int which_led; int other_led; enum pri_led_state ledstate; int mod; BUG_ON(!xpd); priv = xpd->priv; BUG_ON(!priv); if(xpd->timing_priority == 0) { which_led = TOP_RED_LED; other_led = BOTTOM_GREEN_LED; } else { which_led = BOTTOM_GREEN_LED; other_led = TOP_RED_LED; } ledstate = priv->ledstate[which_led]; timer_count = xpd->timer_count; if(xpd->blink_mode) { if((timer_count % DEFAULT_LED_PERIOD) == 0) { // led state is toggled if(ledstate == PRI_LED_OFF) { DO_LED(xpd, which_led, PRI_LED_ON); DO_LED(xpd, other_led, PRI_LED_ON); } else { DO_LED(xpd, which_led, PRI_LED_OFF); DO_LED(xpd, other_led, PRI_LED_OFF); } } return; } if(priv->ledstate[other_led] != PRI_LED_OFF) DO_LED(xpd, other_led, PRI_LED_OFF); if(priv->dchan_alive) { mod = timer_count % 1000; switch(mod) { case 0: DO_LED(xpd, which_led, PRI_LED_ON); break; case 500: DO_LED(xpd, which_led, PRI_LED_OFF); break; } } else if(priv->layer1_up) { mod = timer_count % 1000; switch(mod) { case 0: case 100: DO_LED(xpd, which_led, PRI_LED_ON); break; case 50: case 150: DO_LED(xpd, which_led, PRI_LED_OFF); break; } } else { if(priv->ledstate[which_led] != PRI_LED_ON) DO_LED(xpd, which_led, PRI_LED_ON); } } static int PRI_card_tick(xbus_t *xbus, xpd_t *xpd) { struct PRI_priv_data *priv; BUG_ON(!xpd); priv = xpd->priv; BUG_ON(!priv); if(!priv->initialized || !xbus->self_ticking) return 0; /* * Poll layer1 status (cascade subunits) */ if(poll_interval != 0 && ((xpd->timer_count % poll_interval) == 0)) { priv->poll_noreplies++; query_subunit(xpd, REG_FRS0); //query_subunit(xpd, REG_FRS1); } if(priv->dchan_tx_counter >= 1 && priv->dchan_rx_counter > 1) { dchan_state(xpd, 1); priv->dchan_alive_ticks++; } handle_leds(xbus, xpd); return 0; } static int PRI_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long arg) { struct dahdi_chan *chan; BUG_ON(!xpd); if(!XBUS_IS(xpd->xbus, READY)) return -ENODEV; chan = XPD_CHAN(xpd, pos); switch (cmd) { case DAHDI_TONEDETECT: /* * Asterisk call all span types with this (FXS specific) * call. Silently ignore it. */ LINE_DBG(SIGNAL, xpd, pos, "PRI: TONEDETECT (%s)\n", (chan->flags & DAHDI_FLAG_AUDIO) ? "AUDIO" : "SILENCE"); return -ENOTTY; case DAHDI_ONHOOKTRANSFER: LINE_DBG(SIGNAL, xpd, pos, "PRI: ONHOOKTRANSFER\n"); return -ENOTTY; case DAHDI_VMWI: LINE_DBG(SIGNAL, xpd, pos, "PRI: VMWI\n"); return -ENOTTY; case DAHDI_VMWI_CONFIG: LINE_DBG(SIGNAL, xpd, pos, "PRI: VMWI_CONFIG\n"); return -ENOTTY; default: report_bad_ioctl(THIS_MODULE->name, xpd, pos, cmd); return -ENOTTY; } return 0; } static int PRI_card_close(xpd_t *xpd, lineno_t pos) { //struct dahdi_chan *chan = XPD_CHAN(xpd, pos); dchan_state(xpd, 0); return 0; } /* * Called only for 'span' keyword in /etc/dahdi/system.conf */ static int pri_startup(struct dahdi_span *span) { xpd_t *xpd = container_of(span, struct xpd, span); struct PRI_priv_data *priv; BUG_ON(!xpd); priv = xpd->priv; BUG_ON(!priv); if(!XBUS_IS(xpd->xbus, READY)) { XPD_DBG(GENERAL, xpd, "Startup called by dahdi. No Hardware. Ignored\n"); return -ENODEV; } XPD_DBG(GENERAL, xpd, "STARTUP\n"); // Turn on all channels CALL_XMETHOD(XPD_STATE, xpd->xbus, xpd, 1); set_rbslines(xpd, 0); write_subunit(xpd, REG_XPM2, 0x00); return 0; } /* * Called only for 'span' keyword in /etc/dahdi/system.conf */ static int pri_shutdown(struct dahdi_span *span) { xpd_t *xpd = container_of(span, struct xpd, span); struct PRI_priv_data *priv; BUG_ON(!xpd); priv = xpd->priv; BUG_ON(!priv); if(!XBUS_IS(xpd->xbus, READY)) { XPD_DBG(GENERAL, xpd, "Shutdown called by dahdi. No Hardware. Ignored\n"); return -ENODEV; } XPD_DBG(GENERAL, xpd, "SHUTDOWN\n"); // Turn off all channels CALL_XMETHOD(XPD_STATE, xpd->xbus, xpd, 0); return 0; } static int encode_rbsbits_e1(xpd_t *xpd, int pos, int bits) { struct PRI_priv_data *priv; byte val; int rsnum; BUG_ON(!xpd); priv = xpd->priv; BUG_ON(!priv); BUG_ON(priv->pri_protocol != PRI_PROTO_E1); if(pos == 15) return 0; /* Don't write dchan in CAS */ if(pos < 0 || pos > 31) { XPD_NOTICE(xpd, "%s: pos=%d out of range. Ignore\n", __FUNCTION__, pos); return 0; } if(pos >= 16) { /* Low nibble */ rsnum = pos - 16; val = (priv->cas_ts_e[rsnum] & 0xF0) | (bits & 0x0F); } else { /* High nibble */ rsnum = pos; val = (priv->cas_ts_e[rsnum] & 0x0F) | ((bits << 4) & 0xF0); } LINE_DBG(SIGNAL, xpd, pos, "RBS: TX: bits=0x%X\n", bits); write_cas_reg(xpd, rsnum, val); return 0; } static int encode_rbsbits_t1(xpd_t *xpd, int pos, int bits) { struct PRI_priv_data *priv; int rsnum; int chan_per_reg; int offset; int width; uint tx_bits = bits; uint mask; byte val; BUG_ON(!xpd); priv = xpd->priv; BUG_ON(!priv); BUG_ON(priv->pri_protocol != PRI_PROTO_T1); if(pos < 0 || pos >= xpd->channels) { XPD_ERR(xpd, "%s: Bad pos=%d\n", __FUNCTION__, pos); return 0; } chan_per_reg = CHAN_PER_REGS(priv); width = 8 / chan_per_reg; rsnum = pos / chan_per_reg; offset = pos % chan_per_reg; mask = BITMASK(width) << (chan_per_reg - offset - 1) * width; if (!priv->is_esf) tx_bits >>= 2; tx_bits &= BITMASK(width); tx_bits <<= (chan_per_reg - offset - 1) * width; val = priv->cas_ts_e[rsnum]; val &= ~mask; val |= tx_bits; LINE_DBG(SIGNAL, xpd, pos, "bits=0x%02X RS%02d(%s) offset=%d tx_bits=0x%02X\n", bits, rsnum+1, (priv->is_esf) ? "esf" : "d4", offset, tx_bits); write_cas_reg(xpd, rsnum , val); priv->dchan_tx_counter++; return 0; } static void send_idlebits(xpd_t *xpd, bool saveold) { struct PRI_priv_data *priv; byte save_rs[NUM_CAS_RS_E]; int i; if (!SPAN_REGISTERED(xpd)) return; priv = xpd->priv; BUG_ON(!priv); XPD_DBG(SIGNAL, xpd, "saveold=%d\n", saveold); if (saveold) memcpy(save_rs, priv->cas_ts_e, sizeof(save_rs)); for_each_line(xpd, i) { struct dahdi_chan *chan = XPD_CHAN(xpd, i); pri_rbsbits(chan, chan->idlebits); } if (saveold) memcpy(priv->cas_ts_e, save_rs, sizeof(save_rs)); } static void send_oldbits(xpd_t *xpd) { struct PRI_priv_data *priv; int i; BUG_ON(!xpd); priv = xpd->priv; BUG_ON(!priv); XPD_DBG(SIGNAL, xpd, "\n"); for (i = 0; i < cas_numregs(xpd); i++) write_cas_reg(xpd, i , priv->cas_ts_e[i]); } static int pri_rbsbits(struct dahdi_chan *chan, int bits) { xpd_t *xpd; struct PRI_priv_data *priv; int pos; xpd = chan->pvt; BUG_ON(!xpd); pos = chan->chanpos - 1; priv = xpd->priv; BUG_ON(!priv); if(!priv->layer1_up) { XPD_DBG(SIGNAL, xpd, "RBS: TX: No layer1 yet. Keep going.\n"); } if(!priv->is_cas) { XPD_DBG(SIGNAL, xpd, "RBS: TX: not in CAS mode. Ignore.\n"); return 0; } if (chan->sig == DAHDI_SIG_NONE) { LINE_DBG(SIGNAL, xpd, pos, "RBS: TX: sigtyp=%s. , bits=0x%X. Ignore.\n", sig2str(chan->sig), bits); return 0; } if(priv->pri_protocol == PRI_PROTO_E1) { if(encode_rbsbits_e1(xpd, pos, bits) < 0) return -EINVAL; } else if(priv->pri_protocol == PRI_PROTO_T1) { if(encode_rbsbits_t1(xpd, pos, bits) < 0) return -EINVAL; } else { XPD_NOTICE(xpd, "%s: protocol %s is not supported yet with CAS\n", __FUNCTION__, pri_protocol_name(priv->pri_protocol)); return -EINVAL; } return 0; } /*! Copy PCM chunks from the buffers of the xpd to a new packet * \param xbus xbus of source xpd. * \param xpd source xpd. * \param lines a bitmask of the active channels that need to be copied. * \param pack packet to be filled. * * On PRI this function is should also shift the lines mask one bit, as * channel 0 on the wire is an internal chip control channel. We only * send 31 channels to the device, but they should be called 1-31 rather * than 0-30 . */ static void PRI_card_pcm_fromspan(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack) { struct PRI_priv_data *priv; byte *pcm; unsigned long flags; int i; xpp_line_t wanted_lines; int physical_chan; int physical_mask = 0; BUG_ON(!xbus); BUG_ON(!xpd); BUG_ON(!pack); priv = xpd->priv; BUG_ON(!priv); pcm = RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, pcm); spin_lock_irqsave(&xpd->lock, flags); wanted_lines = xpd->wanted_pcm_mask; physical_chan = 0; for_each_line(xpd, i) { struct dahdi_chan *chan = XPD_CHAN(xpd, i); if(priv->pri_protocol == PRI_PROTO_E1) { /* In E1 - Only 0'th channel is unused */ if(i == 0) { physical_chan++; } } else if(priv->pri_protocol == PRI_PROTO_T1) { /* In T1 - Every 4'th channel is unused */ if((i % 3) == 0) { physical_chan++; } } if(IS_SET(wanted_lines, i)) { physical_mask |= BIT(physical_chan); if(SPAN_REGISTERED(xpd)) { #ifdef DEBUG_PCMTX int channo = XPD_CHAN(xpd, i)->channo; if(pcmtx >= 0 && pcmtx_chan == channo) memset((u_char *)pcm, pcmtx, DAHDI_CHUNKSIZE); else #endif memcpy((u_char *)pcm, chan->writechunk, DAHDI_CHUNKSIZE); if(i == PRI_DCHAN_IDX(priv)) { if(priv->dchan_tx_sample != chan->writechunk[0]) { priv->dchan_tx_sample = chan->writechunk[0]; priv->dchan_tx_counter++; } else if(chan->writechunk[0] == 0xFF) dchan_state(xpd, 0); else chan->writechunk[0] = 0xFF; /* Clobber for next tick */ } } else memset((u_char *)pcm, DAHDI_XLAW(0, chan), DAHDI_CHUNKSIZE); pcm += DAHDI_CHUNKSIZE; } physical_chan++; } RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, lines) = physical_mask; XPD_COUNTER(xpd, PCM_WRITE)++; spin_unlock_irqrestore(&xpd->lock, flags); } /*! Copy PCM chunks from the packet we received to the xpd struct. * \param xbus xbus of target xpd. * \param xpd target xpd. * \param pack Source packet. * * On PRI this function is should also shift the lines back mask one bit, as * channel 0 on the wire is an internal chip control channel. * * \see PRI_card_pcm_fromspan */ static void PRI_card_pcm_tospan(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack) { struct PRI_priv_data *priv; byte *pcm; xpp_line_t physical_mask; unsigned long flags; int i; int logical_chan; if(!SPAN_REGISTERED(xpd)) return; priv = xpd->priv; BUG_ON(!priv); pcm = RPACKET_FIELD(pack, GLOBAL, PCM_READ, pcm); physical_mask = RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, lines); spin_lock_irqsave(&xpd->lock, flags); logical_chan = 0; for (i = 0; i < CHANNELS_PERXPD; i++) { volatile u_char *r; if(priv->pri_protocol == PRI_PROTO_E1) { /* In E1 - Only 0'th channel is unused */ if(i == 0) continue; } else if(priv->pri_protocol == PRI_PROTO_T1) { /* In T1 - Every 4'th channel is unused */ if((i % 4) == 0) continue; } if(logical_chan == PRI_DCHAN_IDX(priv) && !priv->is_cas) { if(priv->dchan_rx_sample != pcm[0]) { if(debug & DBG_PCM) { XPD_INFO(xpd, "RX-D-Chan: prev=0x%X now=0x%X\n", priv->dchan_rx_sample, pcm[0]); dump_packet("RX-D-Chan", pack, 1); } priv->dchan_rx_sample = pcm[0]; priv->dchan_rx_counter++; } else if(pcm[0] == 0xFF) dchan_state(xpd, 0); } if(IS_SET(physical_mask, i)) { r = XPD_CHAN(xpd, logical_chan)->readchunk; // memset((u_char *)r, 0x5A, DAHDI_CHUNKSIZE); // DEBUG memcpy((u_char *)r, pcm, DAHDI_CHUNKSIZE); pcm += DAHDI_CHUNKSIZE; } logical_chan++; } XPD_COUNTER(xpd, PCM_READ)++; spin_unlock_irqrestore(&xpd->lock, flags); } int PRI_timing_priority(xbus_t *xbus, xpd_t *xpd) { struct PRI_priv_data *priv; priv = xpd->priv; BUG_ON(!priv); if (priv->layer1_up) return xpd->timing_priority; XPD_DBG(SYNC, xpd, "No timing priority (no layer1)\n"); return -ENOENT; } /*---------------- PRI: HOST COMMANDS -------------------------------------*/ static /* 0x0F */ HOSTCMD(PRI, XPD_STATE, bool on) { BUG_ON(!xpd); XPD_DBG(GENERAL, xpd, "%s\n", (on)?"on":"off"); return 0; } static /* 0x33 */ HOSTCMD(PRI, SET_LED, enum pri_led_selectors led_sel, enum pri_led_state to_led_state) { int ret = 0; xframe_t *xframe; xpacket_t *pack; struct pri_leds *pri_leds; struct PRI_priv_data *priv; BUG_ON(!xbus); BUG_ON(!xpd); priv = xpd->priv; BUG_ON(!priv); XPD_DBG(LEDS, xpd, "led_sel=%d to_state=%d\n", led_sel, to_led_state); XFRAME_NEW_CMD(xframe, pack, xbus, PRI, SET_LED, xpd->xbus_idx); pri_leds = &RPACKET_FIELD(pack, PRI, SET_LED, pri_leds); pri_leds->state = to_led_state; pri_leds->led_sel = led_sel; pri_leds->reserved = 0; XPACKET_LEN(pack) = RPACKET_SIZE(PRI, SET_LED); ret = send_cmd_frame(xbus, xframe); priv->ledstate[led_sel] = to_led_state; return ret; } /*---------------- PRI: Astribank Reply Handlers --------------------------*/ static void layer1_state(xpd_t *xpd, byte data_low) { struct PRI_priv_data *priv; int alarms = DAHDI_ALARM_NONE; int layer1_up_prev; BUG_ON(!xpd); priv = xpd->priv; BUG_ON(!priv); priv->poll_noreplies = 0; if(data_low & REG_FRS0_LOS) alarms |= DAHDI_ALARM_RED; if(data_low & REG_FRS0_AIS) alarms |= DAHDI_ALARM_BLUE; if(data_low & REG_FRS0_RRA) alarms |= DAHDI_ALARM_YELLOW; layer1_up_prev = priv->layer1_up; priv->layer1_up = alarms == DAHDI_ALARM_NONE; #if 0 /* * Some bad bits (e.g: LMFA and NMF have no alarm "colors" * associated. However, layer1 is still not working if they are set. * FIXME: These behave differently in E1/T1, so ignore them for while. */ if(data_low & (REG_FRS0_LMFA | REG_FRS0_E1_NMF)) priv->layer1_up = 0; #endif priv->alarms = alarms; if(!priv->layer1_up) { dchan_state(xpd, 0); } else if (priv->is_cas && !layer1_up_prev) { int regbase = cas_regbase(xpd); int i; XPD_DBG(SIGNAL , xpd, "Returning From Alarm Refreshing Rx register data \n"); for (i = 0; i < cas_numregs(xpd); i++) query_subunit(xpd, regbase + i); } if(SPAN_REGISTERED(xpd) && xpd->span.alarms != alarms) { char str1[MAX_PROC_WRITE]; char str2[MAX_PROC_WRITE]; alarm2str(xpd->span.alarms, str1, sizeof(str1)); alarm2str(alarms, str2, sizeof(str2)); XPD_NOTICE(xpd, "Alarms: 0x%X (%s) => 0x%X (%s)\n", xpd->span.alarms, str1, alarms, str2); if (priv->is_cas) { if (alarms == DAHDI_ALARM_NONE) send_oldbits(xpd); else if (xpd->span.alarms == DAHDI_ALARM_NONE) send_idlebits(xpd, 1); } xpd->span.alarms = alarms; elect_syncer("LAYER1"); dahdi_alarm_notify(&xpd->span); set_clocking(xpd); } priv->reg_frs0 = data_low; priv->layer1_replies++; XPD_DBG(REGS, xpd, "subunit=%d data_low=0x%02X\n", xpd->addr.subunit, data_low); } static int decode_cas_e1(xpd_t *xpd, byte regnum, byte data_low) { struct PRI_priv_data *priv; uint pos = regnum - REG_RS2_E; int rsnum = pos + 2; int chan1 = pos; int chan2 = pos + 16; int val1 = (data_low >> 4) & 0xF; int val2 = data_low & 0xF; priv = xpd->priv; BUG_ON(!priv->is_cas); BUG_ON(priv->pri_protocol != PRI_PROTO_E1); XPD_DBG(SIGNAL, xpd, "RBS: RX: data_low=0x%02X\n", data_low); if(pos < 0 || pos >= NUM_CAS_RS_E) { XPD_ERR(xpd, "%s: got bad pos=%d [0-%d]\n", __FUNCTION__, pos, NUM_CAS_RS_E); return -EINVAL; } if(chan1 < 0 || chan1 > xpd->channels) { XPD_NOTICE(xpd, "%s: %s CAS: Bad chan1 number (%d)\n", __FUNCTION__, pri_protocol_name(priv->pri_protocol), chan1); return -EINVAL; } if(chan2 < 0 || chan2 > xpd->channels) { XPD_NOTICE(xpd, "%s: %s CAS: Bad chan2 number (%d)\n", __FUNCTION__, pri_protocol_name(priv->pri_protocol), chan2); return -EINVAL; } XPD_DBG(SIGNAL, xpd, "RBS: RX: RS%02d (channel %2d, channel %2d): 0x%02X -> 0x%02X\n", rsnum, chan1+1, chan2+1, priv->cas_rs_e[pos], data_low); if(SPAN_REGISTERED(xpd)) { dahdi_rbsbits(XPD_CHAN(xpd, chan1), val1); dahdi_rbsbits(XPD_CHAN(xpd, chan2), val2); } priv->dchan_rx_counter++; priv->cas_rs_e[pos] = data_low; return 0; } static int decode_cas_t1(xpd_t *xpd, byte regnum, byte data_low) { struct PRI_priv_data *priv; uint rsnum; uint chan_per_reg; uint width; int i; priv = xpd->priv; BUG_ON(!priv->is_cas); BUG_ON(priv->pri_protocol != PRI_PROTO_T1); rsnum = regnum - REG_RS1_E; if(rsnum < 0 || rsnum >= 12) { XPD_ERR(xpd, "Bad rsnum=%d\n", rsnum); return 0; } if(!priv->is_esf) rsnum = rsnum % 6; /* 2 identical banks of 6 registers */ chan_per_reg = CHAN_PER_REGS(priv); width = 8 / chan_per_reg; XPD_DBG(SIGNAL, xpd, "RBS: RX(%s,%d): RS%02d data_low=0x%02X\n", (priv->is_esf) ? "esf" : "d4", chan_per_reg, rsnum+1, data_low); for(i = 0; i < chan_per_reg; i++) { uint rxsig = (data_low >> (i * width)) & BITMASK(width); int pos; struct dahdi_chan *chan; if (!priv->is_esf) rxsig <<= 2; pos = rsnum * chan_per_reg + chan_per_reg - i - 1; if(pos < 0 || pos >= xpd->channels) { XPD_ERR(xpd, "%s: Bad pos=%d\n", __FUNCTION__, pos); continue; } chan = XPD_CHAN(xpd, pos); if(!chan) { XPD_ERR(xpd, "%s: Null channel in pos=%d\n", __FUNCTION__, pos); continue; } if(chan->rxsig != rxsig) { LINE_DBG(SIGNAL, xpd, pos, "i=%d rxsig=0x%02X\n", i, rxsig); dahdi_rbsbits(chan, rxsig); } } priv->cas_rs_e[rsnum] = data_low; return 0; } static void process_cas_dchan(xpd_t *xpd, byte regnum, byte data_low) { struct PRI_priv_data *priv; priv = xpd->priv; if(!priv->is_cas) { static int rate_limit; if((rate_limit++ % 10003) == 0) XPD_NOTICE(xpd, "RBS: RX: not in CAS mode. Ignore.\n"); return; } if(!priv->layer1_up) { static int rate_limit; if((rate_limit++ % 10003) == 0) XPD_DBG(SIGNAL, xpd, "RBS: RX: No layer1.\n"); } if(!SPAN_REGISTERED(xpd)) { static int rate_limit; if((rate_limit++ % 10003) == 0) XPD_DBG(SIGNAL, xpd, "RBS: RX: Span not registered. Ignore.\n"); return; } if(priv->pri_protocol == PRI_PROTO_E1) { if(regnum == REG_RS1_E) return; /* Time slot 0: Ignored for E1 */ if(regnum < REG_RS2_E) { /* Should not happen, but harmless. Ignore */ if (regnum == REG_RS1_E) return; XPD_NOTICE(xpd, "%s: received register 0x%X in protocol %s. Ignore\n", __FUNCTION__, regnum, pri_protocol_name(priv->pri_protocol)); return; } if(decode_cas_e1(xpd, regnum, data_low) < 0) return; } else if(priv->pri_protocol == PRI_PROTO_T1) { if(regnum > REG_RS12_E) { XPD_NOTICE(xpd, "%s: received register 0x%X in protocol %s. Ignore\n", __FUNCTION__, regnum, pri_protocol_name(priv->pri_protocol)); return; } if(decode_cas_t1(xpd, regnum, data_low) < 0) return; } else { XPD_NOTICE(xpd, "%s: protocol %s is not supported yet with CAS\n", __FUNCTION__, pri_protocol_name(priv->pri_protocol)); } priv->cas_replies++; } static int PRI_card_register_reply(xbus_t *xbus, xpd_t *xpd, reg_cmd_t *info) { unsigned long flags; struct PRI_priv_data *priv; struct xpd_addr addr; xpd_t *orig_xpd; byte regnum; byte data_low; /* Map UNIT + PORTNUM to XPD */ orig_xpd = xpd; addr.unit = orig_xpd->addr.unit; addr.subunit = info->portnum; regnum = REG_FIELD(info, regnum); data_low = REG_FIELD(info, data_low); xpd = xpd_byaddr(xbus, addr.unit, addr.subunit); if(!xpd) { static int rate_limit; if((rate_limit++ % 1003) < 5) notify_bad_xpd(__FUNCTION__, xbus, addr , orig_xpd->xpdname); return -EPROTO; } spin_lock_irqsave(&xpd->lock, flags); priv = xpd->priv; BUG_ON(!priv); if(info->is_multibyte) { XPD_NOTICE(xpd, "Got Multibyte: %d bytes, eoframe: %d\n", info->bytes, info->eoframe); goto end; } if(regnum == REG_FRS0 && !REG_FIELD(info, do_subreg)) layer1_state(xpd, data_low); else if(regnum == REG_FRS1 && !REG_FIELD(info, do_subreg)) priv->reg_frs1 = data_low; if(priv->is_cas && !REG_FIELD(info, do_subreg)) { if(regnum >= REG_RS1_E && regnum <= REG_RS16_E) { process_cas_dchan(xpd, regnum, data_low); } } /* Update /proc info only if reply relate to the last slic read request */ if( REG_FIELD(&xpd->requested_reply, regnum) == REG_FIELD(info, regnum) && REG_FIELD(&xpd->requested_reply, do_subreg) == REG_FIELD(info, do_subreg) && REG_FIELD(&xpd->requested_reply, subreg) == REG_FIELD(info, subreg)) { xpd->last_reply = *info; } end: spin_unlock_irqrestore(&xpd->lock, flags); return 0; } static xproto_table_t PROTO_TABLE(PRI) = { .owner = THIS_MODULE, .entries = { /* Table Card Opcode */ }, .name = "PRI", /* protocol name */ .ports_per_subunit = 1, .type = XPD_TYPE_PRI, .xops = { .card_new = PRI_card_new, .card_init = PRI_card_init, .card_remove = PRI_card_remove, .card_dahdi_preregistration = PRI_card_dahdi_preregistration, .card_dahdi_postregistration = PRI_card_dahdi_postregistration, .card_tick = PRI_card_tick, .card_pcm_recompute = PRI_card_pcm_recompute, .card_pcm_fromspan = PRI_card_pcm_fromspan, .card_pcm_tospan = PRI_card_pcm_tospan, .card_timing_priority = PRI_timing_priority, .card_ioctl = PRI_card_ioctl, .card_close = PRI_card_close, .card_register_reply = PRI_card_register_reply, .XPD_STATE = XPROTO_CALLER(PRI, XPD_STATE), }, .packet_is_valid = pri_packet_is_valid, .packet_dump = pri_packet_dump, }; static bool pri_packet_is_valid(xpacket_t *pack) { const xproto_entry_t *xe = NULL; // DBG(GENERAL, "\n"); xe = xproto_card_entry(&PROTO_TABLE(PRI), XPACKET_OP(pack)); return xe != NULL; } static void pri_packet_dump(const char *msg, xpacket_t *pack) { DBG(GENERAL, "%s\n", msg); } /*------------------------- REGISTER Handling --------------------------*/ #ifdef OLD_PROC static int proc_pri_info_write(struct file *file, const char __user *buffer, unsigned long count, void *data) { xpd_t *xpd = data; struct PRI_priv_data *priv; char buf[MAX_PROC_WRITE]; char *p; char *tok; int ret = 0; bool got_localloop = 0; bool got_nolocalloop = 0; bool got_e1 = 0; bool got_t1 = 0; bool got_j1 = 0; if(!xpd) return -ENODEV; XPD_NOTICE(xpd, "%s: DEPRECATED: %s[%d] write to /proc interface instead of /sys\n", __FUNCTION__, current->comm, current->tgid); priv = xpd->priv; if(count >= MAX_PROC_WRITE) { /* leave room for null */ XPD_ERR(xpd, "write too long (%ld)\n", count); return -E2BIG; } if(copy_from_user(buf, buffer, count)) { XPD_ERR(xpd, "Failed reading user data\n"); return -EFAULT; } buf[count] = '\0'; XPD_DBG(PROC, xpd, "PRI-SETUP: got %s\n", buf); /* * First parse. Act only of *everything* is good. */ p = buf; while((tok = strsep(&p, " \t\v\n")) != NULL) { if(*tok == '\0') continue; XPD_DBG(PROC, xpd, "Got token='%s'\n", tok); if(strnicmp(tok, "LOCALLOOP", 8) == 0) got_localloop = 1; else if(strnicmp(tok, "NOLOCALLOOP", 8) == 0) got_nolocalloop = 1; else if(strnicmp(tok, "E1", 2) == 0) got_e1 = 1; else if(strnicmp(tok, "T1", 2) == 0) got_t1 = 1; else if(strnicmp(tok, "J1", 2) == 0) { got_j1 = 1; } else { XPD_NOTICE(xpd, "PRI-SETUP: unknown keyword: '%s'\n", tok); return -EINVAL; } } if(got_e1) ret = set_pri_proto(xpd, PRI_PROTO_E1); else if(got_t1) ret = set_pri_proto(xpd, PRI_PROTO_T1); else if(got_j1) ret = set_pri_proto(xpd, PRI_PROTO_J1); if(priv->pri_protocol == PRI_PROTO_0) { XPD_ERR(xpd, "Must set PRI protocol (E1/T1/J1) before setting other parameters\n"); return -EINVAL; } if(got_localloop) ret = set_localloop(xpd, 1); if(got_nolocalloop) ret = set_localloop(xpd, 0); return (ret) ? ret : count; } static int proc_pri_info_read(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; unsigned long flags; xpd_t *xpd = data; struct PRI_priv_data *priv; int i; DBG(PROC, "\n"); if(!xpd) return -ENODEV; XPD_NOTICE(xpd, "%s: DEPRECATED: %s[%d] read from /proc interface instead of /sys\n", __FUNCTION__, current->comm, current->tgid); spin_lock_irqsave(&xpd->lock, flags); priv = xpd->priv; BUG_ON(!priv); len += sprintf(page + len, "PRI: %s %s%s (deflaw=%d, dchan=%d)\n", (priv->clock_source) ? "MASTER" : "SLAVE", pri_protocol_name(priv->pri_protocol), (priv->local_loopback) ? " LOCALLOOP" : "", priv->deflaw, priv->dchan_num); len += sprintf(page + len, "%05d Layer1: ", priv->layer1_replies); if(priv->poll_noreplies > 1) len += sprintf(page + len, "No Replies [%d]\n", priv->poll_noreplies); else { len += sprintf(page + len, "%s\n", ((priv->layer1_up) ? "UP" : "DOWN")); len += sprintf(page + len, "Framer Status: FRS0=0x%02X, FRS1=0x%02X ALARMS:", priv->reg_frs0, priv->reg_frs1); if(priv->reg_frs0 & REG_FRS0_LOS) len += sprintf(page + len, " RED"); if(priv->reg_frs0 & REG_FRS0_AIS) len += sprintf(page + len, " BLUE"); if(priv->reg_frs0 & REG_FRS0_RRA) len += sprintf(page + len, " YELLOW"); len += sprintf(page + len, "\n"); } if(priv->is_cas) { len += sprintf(page + len, "CAS: replies=%d\n", priv->cas_replies); len += sprintf(page + len, " CAS-TS: "); for(i = 0; i < NUM_CAS_RS_E; i++) { len += sprintf(page + len, " %02X", priv->cas_ts_e[i]); } len += sprintf(page + len, "\n"); len += sprintf(page + len, " CAS-RS: "); for(i = 0; i < NUM_CAS_RS_E; i++) { len += sprintf(page + len, " %02X", priv->cas_rs_e[i]); } len += sprintf(page + len, "\n"); } len += sprintf(page + len, "D-Channel: TX=[%5d] (0x%02X) RX=[%5d] (0x%02X) ", priv->dchan_tx_counter, priv->dchan_tx_sample, priv->dchan_rx_counter, priv->dchan_rx_sample); if(priv->dchan_alive) { len += sprintf(page + len, "(alive %d K-ticks)\n", priv->dchan_alive_ticks/1000); } else { len += sprintf(page + len, "(dead)\n"); } for(i = 0; i < NUM_LEDS; i++) len += sprintf(page + len, "LED #%d: %d\n", i, priv->ledstate[i]); spin_unlock_irqrestore(&xpd->lock, flags); if (len <= off+count) *eof = 1; *start = page + off; len -= off; if (len > count) len = count; if (len < 0) len = 0; return len; } #endif /*------------------------- sysfs stuff --------------------------------*/ static DEVICE_ATTR_READER(pri_protocol_show, dev, buf) { xpd_t *xpd; struct PRI_priv_data *priv; unsigned long flags; int len = 0; BUG_ON(!dev); xpd = dev_to_xpd(dev); if(!xpd) return -ENODEV; priv = xpd->priv; BUG_ON(!priv); spin_lock_irqsave(&xpd->lock, flags); len += sprintf(buf, "%s\n", pri_protocol_name(priv->pri_protocol)); spin_unlock_irqrestore(&xpd->lock, flags); return len; } static DEVICE_ATTR_WRITER(pri_protocol_store, dev, buf, count) { xpd_t *xpd; enum pri_protocol new_protocol = PRI_PROTO_0; int i; int ret; BUG_ON(!dev); xpd = dev_to_xpd(dev); XPD_DBG(GENERAL, xpd, "%s\n", buf); if(!xpd) return -ENODEV; if((i = strcspn(buf, " \r\n")) != 2) { XPD_NOTICE(xpd, "Protocol name '%s' has %d characters (should be 2). Ignored.\n", buf, i); return -EINVAL; } if(strnicmp(buf, "E1", 2) == 0) new_protocol = PRI_PROTO_E1; else if(strnicmp(buf, "T1", 2) == 0) new_protocol = PRI_PROTO_T1; else if(strnicmp(buf, "J1", 2) == 0) new_protocol = PRI_PROTO_J1; else { XPD_NOTICE(xpd, "Unknown PRI protocol '%s' (should be E1|T1|J1). Ignored.\n", buf); return -EINVAL; } ret = set_pri_proto(xpd, new_protocol); return (ret < 0) ? ret : count; } static DEVICE_ATTR(pri_protocol, S_IRUGO | S_IWUSR, pri_protocol_show, pri_protocol_store); static DEVICE_ATTR_READER(pri_localloop_show, dev, buf) { xpd_t *xpd; struct PRI_priv_data *priv; unsigned long flags; int len = 0; BUG_ON(!dev); xpd = dev_to_xpd(dev); if(!xpd) return -ENODEV; priv = xpd->priv; BUG_ON(!priv); spin_lock_irqsave(&xpd->lock, flags); len += sprintf(buf, "%c\n", (priv->local_loopback) ? 'Y' : 'N'); spin_unlock_irqrestore(&xpd->lock, flags); return len; } static DEVICE_ATTR_WRITER(pri_localloop_store, dev, buf, count) { xpd_t *xpd; bool ll = 0; int i; int ret; BUG_ON(!dev); xpd = dev_to_xpd(dev); XPD_DBG(GENERAL, xpd, "%s\n", buf); if(!xpd) return -ENODEV; if((i = strcspn(buf, " \r\n")) != 1) { XPD_NOTICE(xpd, "Value '%s' has %d characters (should be 1). Ignored.\n", buf, i); return -EINVAL; } if(strchr("1Yy", buf[0]) != NULL) ll = 1; else if(strchr("0Nn", buf[0]) != NULL) ll = 0; else { XPD_NOTICE(xpd, "Unknown value '%s' (should be [1Yy]|[0Nn]). Ignored.\n", buf); return -EINVAL; } ret = set_localloop(xpd, ll); return (ret < 0) ? ret : count; } static DEVICE_ATTR(pri_localloop, S_IRUGO | S_IWUSR, pri_localloop_show, pri_localloop_store); static DEVICE_ATTR_READER(pri_layer1_show, dev, buf) { xpd_t *xpd; struct PRI_priv_data *priv; unsigned long flags; int len = 0; BUG_ON(!dev); xpd = dev_to_xpd(dev); if(!xpd) return -ENODEV; priv = xpd->priv; BUG_ON(!priv); spin_lock_irqsave(&xpd->lock, flags); if(priv->poll_noreplies > 1) len += sprintf(buf + len, "Unknown[%d]", priv->poll_noreplies); else len += sprintf(buf + len, "%-10s", ((priv->layer1_up) ? "UP" : "DOWN")); len += sprintf(buf + len, "%d\n", priv->layer1_replies); spin_unlock_irqrestore(&xpd->lock, flags); return len; } static DEVICE_ATTR(pri_layer1, S_IRUGO, pri_layer1_show, NULL); static DEVICE_ATTR_READER(pri_alarms_show, dev, buf) { xpd_t *xpd; struct PRI_priv_data *priv; unsigned long flags; int len = 0; const static struct { byte bits; const char *name; } alarm_types[] = { { REG_FRS0_LOS, "RED" }, { REG_FRS0_AIS, "BLUE" }, { REG_FRS0_RRA, "YELLOW" }, }; BUG_ON(!dev); xpd = dev_to_xpd(dev); if(!xpd) return -ENODEV; priv = xpd->priv; BUG_ON(!priv); spin_lock_irqsave(&xpd->lock, flags); if(priv->poll_noreplies > 1) len += sprintf(buf + len, "Unknown[%d]", priv->poll_noreplies); else { int i; for(i = 0; i < ARRAY_SIZE(alarm_types); i++) { if(priv->reg_frs0 & alarm_types[i].bits) len += sprintf(buf + len, "%s ", alarm_types[i].name); } } len += sprintf(buf + len, "\n"); spin_unlock_irqrestore(&xpd->lock, flags); return len; } static DEVICE_ATTR(pri_alarms, S_IRUGO, pri_alarms_show, NULL); static DEVICE_ATTR_READER(pri_cas_show, dev, buf) { xpd_t *xpd; struct PRI_priv_data *priv; unsigned long flags; int len = 0; BUG_ON(!dev); xpd = dev_to_xpd(dev); if(!xpd) return -ENODEV; priv = xpd->priv; BUG_ON(!priv); spin_lock_irqsave(&xpd->lock, flags); if(priv->is_cas) { int i; len += sprintf(buf + len, "CAS: replies=%d\n", priv->cas_replies); len += sprintf(buf + len, " CAS-TS: "); for(i = 0; i < NUM_CAS_RS_E; i++) { len += sprintf(buf + len, " %02X", priv->cas_ts_e[i]); } len += sprintf(buf + len, "\n"); len += sprintf(buf + len, " CAS-RS: "); for(i = 0; i < NUM_CAS_RS_E; i++) { len += sprintf(buf + len, " %02X", priv->cas_rs_e[i]); } len += sprintf(buf + len, "\n"); } spin_unlock_irqrestore(&xpd->lock, flags); return len; } static DEVICE_ATTR(pri_cas, S_IRUGO, pri_cas_show, NULL); static DEVICE_ATTR_READER(pri_dchan_show, dev, buf) { xpd_t *xpd; struct PRI_priv_data *priv; unsigned long flags; int len = 0; BUG_ON(!dev); xpd = dev_to_xpd(dev); if(!xpd) return -ENODEV; priv = xpd->priv; BUG_ON(!priv); spin_lock_irqsave(&xpd->lock, flags); len += sprintf(buf + len, "D-Channel: TX=[%5d] (0x%02X) RX=[%5d] (0x%02X) ", priv->dchan_tx_counter, priv->dchan_tx_sample, priv->dchan_rx_counter, priv->dchan_rx_sample); if(priv->dchan_alive) { len += sprintf(buf + len, "(alive %d K-ticks)\n", priv->dchan_alive_ticks/1000); } else { len += sprintf(buf + len, "(dead)\n"); } spin_unlock_irqrestore(&xpd->lock, flags); return len; } static DEVICE_ATTR(pri_dchan, S_IRUGO, pri_dchan_show, NULL); static DEVICE_ATTR_READER(pri_clocking_show, dev, buf) { xpd_t *xpd; struct PRI_priv_data *priv; unsigned long flags; int len = 0; BUG_ON(!dev); xpd = dev_to_xpd(dev); if(!xpd) return -ENODEV; priv = xpd->priv; BUG_ON(!priv); spin_lock_irqsave(&xpd->lock, flags); len += sprintf(buf + len, "%s\n", (priv->clock_source) ? "MASTER" : "SLAVE"); spin_unlock_irqrestore(&xpd->lock, flags); return len; } static DEVICE_ATTR(pri_clocking, S_IRUGO, pri_clocking_show, NULL); static int pri_xpd_probe(struct device *dev) { xpd_t *xpd; int ret = 0; xpd = dev_to_xpd(dev); /* Is it our device? */ if(xpd->type != XPD_TYPE_PRI) { XPD_ERR(xpd, "drop suggestion for %s (%d)\n", dev_name(dev), xpd->type); return -EINVAL; } XPD_DBG(DEVICES, xpd, "SYSFS\n"); ret = device_create_file(dev, &dev_attr_pri_protocol); if(ret) { XPD_ERR(xpd, "%s: device_create_file(pri_protocol) failed: %d\n", __FUNCTION__, ret); goto fail_pri_protocol; } ret = device_create_file(dev, &dev_attr_pri_localloop); if(ret) { XPD_ERR(xpd, "%s: device_create_file(pri_localloop) failed: %d\n", __FUNCTION__, ret); goto fail_pri_localloop; } ret = device_create_file(dev, &dev_attr_pri_layer1); if(ret) { XPD_ERR(xpd, "%s: device_create_file(pri_layer1) failed: %d\n", __FUNCTION__, ret); goto fail_pri_layer1; } ret = device_create_file(dev, &dev_attr_pri_alarms); if(ret) { XPD_ERR(xpd, "%s: device_create_file(pri_alarms) failed: %d\n", __FUNCTION__, ret); goto fail_pri_alarms; } ret = device_create_file(dev, &dev_attr_pri_cas); if(ret) { XPD_ERR(xpd, "%s: device_create_file(pri_cas) failed: %d\n", __FUNCTION__, ret); goto fail_pri_cas; } ret = device_create_file(dev, &dev_attr_pri_dchan); if(ret) { XPD_ERR(xpd, "%s: device_create_file(pri_dchan) failed: %d\n", __FUNCTION__, ret); goto fail_pri_dchan; } ret = device_create_file(dev, &dev_attr_pri_clocking); if(ret) { XPD_ERR(xpd, "%s: device_create_file(pri_clocking) failed: %d\n", __FUNCTION__, ret); goto fail_pri_clocking; } return 0; fail_pri_clocking: device_remove_file(dev, &dev_attr_pri_dchan); fail_pri_dchan: device_remove_file(dev, &dev_attr_pri_cas); fail_pri_cas: device_remove_file(dev, &dev_attr_pri_alarms); fail_pri_alarms: device_remove_file(dev, &dev_attr_pri_layer1); fail_pri_layer1: device_remove_file(dev, &dev_attr_pri_localloop); fail_pri_localloop: device_remove_file(dev, &dev_attr_pri_protocol); fail_pri_protocol: return ret; } static int pri_xpd_remove(struct device *dev) { xpd_t *xpd; xpd = dev_to_xpd(dev); XPD_DBG(DEVICES, xpd, "SYSFS\n"); device_remove_file(dev, &dev_attr_pri_clocking); device_remove_file(dev, &dev_attr_pri_dchan); device_remove_file(dev, &dev_attr_pri_cas); device_remove_file(dev, &dev_attr_pri_alarms); device_remove_file(dev, &dev_attr_pri_layer1); device_remove_file(dev, &dev_attr_pri_localloop); device_remove_file(dev, &dev_attr_pri_protocol); return 0; } static struct xpd_driver pri_driver = { .type = XPD_TYPE_PRI, .driver = { .name = "pri", #ifndef OLD_HOTPLUG_SUPPORT .owner = THIS_MODULE, #endif .probe = pri_xpd_probe, .remove = pri_xpd_remove } }; static int __init card_pri_startup(void) { int ret; if((ret = xpd_driver_register(&pri_driver.driver)) < 0) return ret; INFO("revision %s\n", XPP_VERSION); #ifdef DAHDI_AUDIO_NOTIFY INFO("FEATURE: WITH DAHDI_AUDIO_NOTIFY\n"); #else INFO("FEATURE: WITHOUT DAHDI_AUDIO_NOTIFY\n"); #endif xproto_register(&PROTO_TABLE(PRI)); return 0; } static void __exit card_pri_cleanup(void) { DBG(GENERAL, "\n"); xproto_unregister(&PROTO_TABLE(PRI)); xpd_driver_unregister(&pri_driver.driver); } MODULE_DESCRIPTION("XPP PRI Card Driver"); MODULE_AUTHOR("Oron Peled "); MODULE_LICENSE("GPL"); MODULE_VERSION(XPP_VERSION); MODULE_ALIAS_XPD(XPD_TYPE_PRI); module_init(card_pri_startup); module_exit(card_pri_cleanup);