--- wctdm.c.orig 2009-01-09 20:10:39.000000000 +1300 +++ wctdm.c 2009-01-14 23:48:53.000000000 +1300 @@ -55,6 +55,7 @@ 0x07 : 41mA */ static int loopcurrent = 20; +#define POLARITY_XOR(card) ((reversepolarity!=0) ^ (wc->mod[(card)].fxs.reversepolarity!=0) ^ (wc->mod[(card)].fxs.vmwi_lrev!=0)) static int reversepolarity = 0; @@ -236,6 +237,13 @@ struct wctdm { int idletxhookstate; /* IDLE changing hook state */ int lasttxhook; int palarms; + int reversepolarity; /* Reverse Line */ + int mwisendtype; + int vmwimessages; /* 0=none 1-255=number of messages */ + int vmwi_lrev:1; /* MWI Line Reversal*/ + int vmwi_hvdc:1; /* MWI High Voltage DC Idle line */ + int vmwi_hvac:1; /* MWI Neon High Voltage AC Idle line */ + int neonringing:1; /* Ring Generator is set for NEON */ struct calregs calregs; } fxs; } mod[NUM_CARDS]; @@ -293,6 +301,7 @@ static int fxstxgain = 0; static int fxsrxgain = 0; static int wctdm_init_proslic(struct wctdm *wc, int card, int fast , int manual, int sane); +static int wctdm_set_vmwi_neon_mode(struct wctdm *wc, int card, int state); static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char ints) { @@ -733,8 +742,9 @@ static inline void wctdm_proslic_recheck wc->mod[card].fxs.lasttxhook = 1; wctdm_setreg(wc, card, 64, wc->mod[card].fxs.lasttxhook); } else { - if (wc->mod[card].fxs.palarms == MAX_ALARMS) + if (wc->mod[card].fxs.palarms == MAX_ALARMS){ printk(KERN_NOTICE "Too many power alarms on card %d, NOT resetting!\n", card + 1); + } } } } @@ -1037,27 +1047,24 @@ DAHDI_IRQ_HANDLER(wctdm_interrupt) for (x=0;x<4;x++) { if (wc->cardflag & (1 << x) && (wc->modtype[x] == MOD_TYPE_FXS)) { - if (wc->mod[x].fxs.lasttxhook == 0x4) { + if ( (wc->mod[x].fxs.lasttxhook == 0x4) && !wc->mod[x].fxs.neonringing) { /* RINGing, prepare for OHT */ wc->mod[x].fxs.ohttimer = OHT_TIMER << 3; - if (reversepolarity) - wc->mod[x].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ - else - wc->mod[x].fxs.idletxhookstate = 0x2; + + /* logical XOR 3 variables + module parameter 'reversepolarity', global reverse all FXS lines. + ioctl channel variable fxs 'reversepolarity', Line Reversal Alert Signal if required. + ioctl channel variable fxs 'vmwi_lrev', VMWI pending. + */ + wc->mod[x].fxs.idletxhookstate = POLARITY_XOR(x) ? 0x6 : 0x2;/* OHT mode when idle */ } else { if (wc->mod[x].fxs.ohttimer) { wc->mod[x].fxs.ohttimer-= DAHDI_CHUNKSIZE; if (!wc->mod[x].fxs.ohttimer) { - if (reversepolarity) - wc->mod[x].fxs.idletxhookstate = 0x5; /* Switch to active */ - else - wc->mod[x].fxs.idletxhookstate = 0x1; + wc->mod[x].fxs.idletxhookstate = POLARITY_XOR(x) ? 0x5 : 0x1; /* Switch to Active : Reverse Forward */ if ((wc->mod[x].fxs.lasttxhook == 0x2) || (wc->mod[x].fxs.lasttxhook == 0x6)) { /* Apply the change if appropriate */ - if (reversepolarity) - wc->mod[x].fxs.lasttxhook = 0x5; - else - wc->mod[x].fxs.lasttxhook = 0x1; + wc->mod[x].fxs.lasttxhook = wc->mod[x].fxs.idletxhookstate; wctdm_setreg(wc, x, 64, wc->mod[x].fxs.lasttxhook); } } @@ -1095,7 +1102,7 @@ DAHDI_IRQ_HANDLER(wctdm_interrupt) wctdm_proslic_check_hook(wc, x); if (!(wc->intcount & 0xf0)) wctdm_proslic_recheck_sanity(wc, x); - } else if (wc->modtype[x] == MOD_TYPE_FXO) { + } else if (wc->modtype[x] == MOD_TYPE_FXO) { wctdm_voicedaa_check_hook(wc, x); } break; @@ -1314,45 +1321,45 @@ static int wctdm_proslic_manual_calibrat // Delay 10ms origjiffies=jiffies; while((jiffies-origjiffies)<1); - wctdm_proslic_setreg_indirect(wc, card, 88,0); - wctdm_proslic_setreg_indirect(wc,card,89,0); - wctdm_proslic_setreg_indirect(wc,card,90,0); - wctdm_proslic_setreg_indirect(wc,card,91,0); - wctdm_proslic_setreg_indirect(wc,card,92,0); - wctdm_proslic_setreg_indirect(wc,card,93,0); + wctdm_proslic_setreg_indirect(wc, card, 88, 0); + wctdm_proslic_setreg_indirect(wc, card, 89, 0); + wctdm_proslic_setreg_indirect(wc, card, 90, 0); + wctdm_proslic_setreg_indirect(wc, card, 91, 0); + wctdm_proslic_setreg_indirect(wc, card, 92, 0); + wctdm_proslic_setreg_indirect(wc, card, 93, 0); - wctdm_setreg(wc, card, 98,0x10); // This is necessary if the calibration occurs other than at reset time - wctdm_setreg(wc, card, 99,0x10); + wctdm_setreg(wc, card, 98, 0x10); // This is necessary if the calibration occurs other than at reset time + wctdm_setreg(wc, card, 99, 0x10); for ( i=0x1f; i>0; i--) { - wctdm_setreg(wc, card, 98,i); + wctdm_setreg(wc, card, 98, i); origjiffies=jiffies; while((jiffies-origjiffies)<4); - if((wctdm_getreg(wc,card,88)) == 0) + if((wctdm_getreg(wc, card, 88)) == 0) break; } // for for ( i=0x1f; i>0; i--) { - wctdm_setreg(wc, card, 99,i); + wctdm_setreg(wc, card, 99, i); origjiffies=jiffies; while((jiffies-origjiffies)<4); - if((wctdm_getreg(wc,card,89)) == 0) + if((wctdm_getreg(wc, card, 89)) == 0) break; }//for /*******************************The preceding is the manual gain mismatch calibration****************************/ /**********************************The following is the longitudinal Balance Cal***********************************/ - wctdm_setreg(wc,card,64,1); + wctdm_setreg(wc,card, 64, 1); while((jiffies-origjiffies)<10); // Sleep 100? wctdm_setreg(wc, card, 64, 0); wctdm_setreg(wc, card, 23, 0x4); // enable interrupt for the balance Cal wctdm_setreg(wc, card, 97, 0x1); // this is a singular calibration bit for longitudinal calibration - wctdm_setreg(wc, card, 96,0x40); + wctdm_setreg(wc, card, 96, 0x40); - wctdm_getreg(wc,card,96); /* Read Reg 96 just cause */ + wctdm_getreg(wc, card, 96); /* Read Reg 96 just cause */ wctdm_setreg(wc, card, 21, 0xFF); wctdm_setreg(wc, card, 22, 0xFF); @@ -1462,10 +1469,11 @@ static int wctdm_init_voicedaa(struct wc wait_just_a_bit(HZ/10); /* Enable PCM, ulaw */ - if (alawoverride) + if (alawoverride){ wctdm_setreg(wc, card, 33, 0x20); - else + } else { wctdm_setreg(wc, card, 33, 0x28); + } /* Set On-hook speed, Ringer impedence, and ringer threshold */ reg16 |= (fxo_modes[_opermode].ohs << 6); @@ -1560,9 +1568,16 @@ static int wctdm_init_proslic(struct wct /* Sanity check the ProSLIC */ if (!sane && wctdm_proslic_insane(wc, card)) return -2; + + /* default messages to none and method to FSK */ + wc->mod[card].fxs.mwisendtype=DAHDI_VMWI_FSK; + wc->mod[card].fxs.vmwimessages=0; + wc->mod[card].fxs.vmwi_lrev=0; + wc->mod[card].fxs.vmwi_hvdc=0; + wc->mod[card].fxs.vmwi_hvac=0; /* By default, don't send on hook */ - if (reversepolarity) + if (!reversepolarity != !wc->mod[card].fxs.reversepolarity) wc->mod[card].fxs.idletxhookstate = 5; else wc->mod[card].fxs.idletxhookstate = 1; @@ -1778,8 +1793,8 @@ static int wctdm_init_proslic(struct wct wctdm_setreg(wc,card,9,r9); } - if(debug) - printk(KERN_DEBUG "DEBUG: fxstxgain:%s fxsrxgain:%s\n",((wctdm_getreg(wc, card, 9)/8) == 1)?"3.5":(((wctdm_getreg(wc,card,9)/4) == 1)?"-3.5":"0.0"),((wctdm_getreg(wc, card, 9)/2) == 1)?"3.5":((wctdm_getreg(wc,card,9)%2)?"-3.5":"0.0")); + if (debug) + printk(KERN_DEBUG "DEBUG: fxstxgain:%s fxsrxgain:%s\n",((wctdm_getreg(wc, card, 9)/8) == 1)?"3.5":(((wctdm_getreg(wc,card,9)/4) == 1)?"-3.5":"0.0"),((wctdm_getreg(wc, card, 9)/2) == 1)?"3.5":((wctdm_getreg(wc,card,9)%2)?"-3.5":"0.0")); wctdm_setreg(wc, card, 64, 0x01); return 0; @@ -1802,17 +1817,11 @@ static int wctdm_ioctl(struct dahdi_chan if (get_user(x, (__user int *) data)) return -EFAULT; wc->mod[chan->chanpos - 1].fxs.ohttimer = x << 3; - if (reversepolarity) - wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ - else - wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 0x2; + wc->mod[chan->chanpos - 1].fxs.idletxhookstate = POLARITY_XOR(chan->chanpos - 1) ? 0x6 : 0x2; /* OHT mode when idle */ if (wc->mod[chan->chanpos - 1].fxs.lasttxhook == 0x1 || wc->mod[chan->chanpos - 1].fxs.lasttxhook == 0x5) { - /* Apply the change if appropriate */ - if (reversepolarity) - wc->mod[chan->chanpos - 1].fxs.lasttxhook = 0x6; - else - wc->mod[chan->chanpos - 1].fxs.lasttxhook = 0x2; - wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook); + /* Apply the change if appropriate */ + wc->mod[chan->chanpos - 1].fxs.lasttxhook = POLARITY_XOR(chan->chanpos - 1) ? 0x6 : 0x2; + wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook); } break; case DAHDI_SETPOLARITY: @@ -1824,11 +1833,74 @@ static int wctdm_ioctl(struct dahdi_chan if ((wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x04) || (wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x00)) return -EINVAL; - - if ((x && !reversepolarity) || (!x && reversepolarity)) + + wc->mod[chan->chanpos - 1].fxs.reversepolarity = x; + if ( POLARITY_XOR(chan->chanpos - 1) ) wc->mod[chan->chanpos - 1].fxs.lasttxhook |= 0x04; else wc->mod[chan->chanpos - 1].fxs.lasttxhook &= ~0x04; + + if (debug) { + printk(KERN_DEBUG "Setting %s Polarity on channel %d\n", + (wc->mod[chan->chanpos - 1].fxs.lasttxhook | 0x04) ? "REVERSE" : "FORWARD", + chan->chanpos-1 + ); + } + wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook); + break; + case DAHDI_VMWI: + /* value: bits 15-8 VMWI TYPE */ + /* bits 7-0 VMWI number of messages */ + if (get_user(x, (__user int *) data)) + return -EFAULT; + if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) + return -EINVAL; + + wc->mod[chan->chanpos - 1].fxs.vmwimessages = (x & 255); + wc->mod[chan->chanpos - 1].fxs.mwisendtype = (x & ~255); + if (wc->mod[chan->chanpos - 1].fxs.vmwimessages){ + x = wc->mod[chan->chanpos - 1].fxs.mwisendtype; + wc->mod[chan->chanpos - 1].fxs.vmwi_lrev = (x & DAHDI_VMWI_LREV)?1:0; + wc->mod[chan->chanpos - 1].fxs.vmwi_hvdc = (x & DAHDI_VMWI_HVDC)?1:0; + wc->mod[chan->chanpos - 1].fxs.vmwi_hvac = (x & DAHDI_VMWI_HVAC)?1:0; + } else { + wc->mod[chan->chanpos - 1].fxs.vmwi_lrev = 0; + wc->mod[chan->chanpos - 1].fxs.vmwi_hvdc = 0; + wc->mod[chan->chanpos - 1].fxs.vmwi_hvac = 0; + } + + if (debug) { + printk(KERN_DEBUG "Setting VMWI on channel %d, type=0x%X, messages=%d, lrev=%d, hvdc=%d, hvac=%d\n", + chan->chanpos-1, + wc->mod[chan->chanpos - 1].fxs.mwisendtype, + wc->mod[chan->chanpos - 1].fxs.vmwimessages, + wc->mod[chan->chanpos - 1].fxs.vmwi_lrev, + wc->mod[chan->chanpos - 1].fxs.vmwi_hvdc, + wc->mod[chan->chanpos - 1].fxs.vmwi_hvac + ); + } + + if (wc->mod[chan->chanpos-1].fxs.vmwi_hvac){ + wctdm_set_vmwi_neon_mode(wc, chan->chanpos-1, 1); /* Set ring generator for neon */ + wc->mod[chan->chanpos - 1].fxs.lasttxhook = 4; + }else{ + if (wc->mod[chan->chanpos -1 ].fxs.neonringing){ + wctdm_set_vmwi_neon_mode(wc, chan->chanpos-1, 0); /* Set ring generator for normal ringer */ + } else { + /* Can't change polarity while ringing or when open */ + if ((wc->mod[chan->chanpos -1].fxs.lasttxhook == 0x04) || (wc->mod[chan->chanpos -1].fxs.lasttxhook == 0x00)){ + printk(KERN_DEBUG "Unable to change polarity on channel %d, lasttxhook=0x%X\n", + chan->chanpos-1, + wc->mod[chan->chanpos - 1].fxs.lasttxhook + ); + return -EINVAL; + } + } + if ( POLARITY_XOR(chan->chanpos - 1) ) + wc->mod[chan->chanpos - 1].fxs.lasttxhook |= 0x04; + else + wc->mod[chan->chanpos - 1].fxs.lasttxhook &= ~0x04; + } wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook); break; case WCTDM_GET_STATS: @@ -1942,10 +2014,7 @@ static int wctdm_close(struct dahdi_chan wc->usecount--; module_put(THIS_MODULE); if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { - if (reversepolarity) - wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 5; - else - wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 1; + wc->mod[chan->chanpos - 1].fxs.idletxhookstate = POLARITY_XOR(chan->chanpos - 1) ? 0x5 : 0x1; } /* If we're dead, release us now */ if (!wc->usecount && wc->dead) @@ -1953,10 +2022,49 @@ static int wctdm_close(struct dahdi_chan return 0; } +static int wctdm_set_vmwi_neon_mode(struct wctdm *wc, int card, int state) +{ + wc->mod[card].fxs.neonringing = state; /* track ring generator mode */ + + if (state) { /* ON */ + if (debug) + printk(KERN_DEBUG "NEON ring on channel %d, lasttxhook was 0x%x\n", card, wc->mod[card].fxs.lasttxhook); + wc->mod[card].fxs.lasttxhook = 0x1; /* Must be in FORWARD ACTIVE before setting ringer */ + wctdm_setreg(wc, card, 64, wc->mod[card].fxs.lasttxhook); + + wctdm_proslic_setreg_indirect(wc, card, 22, 0x03e8); /* RNGY (4 HZ) */ + wctdm_proslic_setreg_indirect(wc, card, 21, 0x7bef); /* RNGX (91.5Vpk) */ + wctdm_proslic_setreg_indirect(wc, card, 20, 0x009f); /* RCO (RNGX, t rise) */ + + wctdm_setreg(wc, card, 34, 0x19); /* Ringing Osc. Control */ + wctdm_setreg(wc, card, 74, 0x3f); /* VBATH 94.5V */ + wctdm_setreg(wc, card, 48, 0xe0); /* Active Timer low byte */ + wctdm_setreg(wc, card, 49, 0x01); /* Active Timer high byte */ + wctdm_setreg(wc, card, 50, 0xF0); /* Inactive Timer low byte */ + wctdm_setreg(wc, card, 51, 0x05); /* Inactive Timer high byte */ + wctdm_proslic_setreg_indirect(wc, card, 29, 0x4600); /* RPTP */ + /* A write to register 64 will now turn on/off the VM led */ + } else { + if (debug) + printk(KERN_DEBUG "NORMAL ring on channel %d\n", card); + wctdm_proslic_setreg_indirect(wc, card, 22, 0x0000); /* RNGY */ + wctdm_proslic_setreg_indirect(wc, card, 21, 0x0160); /* RNGX (91.5Vpk) */ + wctdm_proslic_setreg_indirect(wc, card, 20, 0x7ef0); /* RCO (RNGX, t rise) */ + wctdm_setreg(wc, card, 34, 0x00); /* Ringing Osc. Control */ + wctdm_setreg(wc, card, 74, 0x3f); /* VBATH 94.5V */ + wctdm_setreg(wc, card, 48, 0x00); /* Active Timer low byte */ + wctdm_setreg(wc, card, 49, 0x00); /* Active Timer high byte */ + wctdm_setreg(wc, card, 50, 0x00); /* Inactive Timer low byte */ + wctdm_setreg(wc, card, 51, 0x00); /* Inactive Timer high byte */ + wctdm_proslic_setreg_indirect(wc, card, 29, 0x3600); /* RPTP */ + /* A write to register 64 will now turn on/off the ringer */ + } + return 0; +} + static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) { struct wctdm *wc = chan->pvt; - int reg=0; if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { /* XXX Enable hooksig for FXO XXX */ switch(txsig) { @@ -1976,9 +2084,12 @@ static int wctdm_hooksig(struct dahdi_ch switch(txsig) { case DAHDI_TXSIG_ONHOOK: switch(chan->sig) { - case DAHDI_SIG_EM: case DAHDI_SIG_FXOKS: case DAHDI_SIG_FXOLS: + wctdm_set_vmwi_neon_mode(wc, chan->chanpos-1, wc->mod[chan->chanpos-1].fxs.vmwi_hvac); + wc->mod[chan->chanpos-1].fxs.lasttxhook = (wc->mod[chan->chanpos-1].fxs.vmwi_hvac ? 4 : wc->mod[chan->chanpos-1].fxs.idletxhookstate); + break; + case DAHDI_SIG_EM: wc->mod[chan->chanpos-1].fxs.lasttxhook = wc->mod[chan->chanpos-1].fxs.idletxhookstate; break; case DAHDI_SIG_FXOGS: @@ -1997,6 +2108,7 @@ static int wctdm_hooksig(struct dahdi_ch } break; case DAHDI_TXSIG_START: + wctdm_set_vmwi_neon_mode(wc, chan->chanpos-1, 0); /* Set ringer mode */ wc->mod[chan->chanpos-1].fxs.lasttxhook = 4; break; case DAHDI_TXSIG_KEWL: @@ -2006,7 +2118,7 @@ static int wctdm_hooksig(struct dahdi_ch printk(KERN_NOTICE "wctdm: Can't set tx state to %d\n", txsig); } if (debug) - printk(KERN_DEBUG "Setting FXS hook state to %d (%02x)\n", txsig, reg); + printk(KERN_DEBUG "Setting FXS hook state to %d (%02x)\n", txsig, wc->mod[chan->chanpos-1].fxs.lasttxhook); #if 1 wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos-1].fxs.lasttxhook); @@ -2029,8 +2141,9 @@ static int wctdm_initialize(struct wctdm if (alawoverride) { printk(KERN_INFO "ALAW override parameter detected. Device will be operating in ALAW\n"); wc->span.deflaw = DAHDI_LAW_ALAW; - } else + } else { wc->span.deflaw = DAHDI_LAW_MULAW; + } for (x = 0; x < NUM_CARDS; x++) { sprintf(wc->chans[x]->name, "WCTDM/%d/%d", wc->pos, x); wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR;