diff options
Diffstat (limited to 'xpp/card_fxs.c')
-rw-r--r-- | xpp/card_fxs.c | 286 |
1 files changed, 220 insertions, 66 deletions
diff --git a/xpp/card_fxs.c b/xpp/card_fxs.c index af12cd2..fd50c3d 100644 --- a/xpp/card_fxs.c +++ b/xpp/card_fxs.c @@ -34,7 +34,8 @@ static const char rcsid[] = "$Id$"; DEF_PARM(int, print_dbg, 0, 0600, "Print DBG statements"); /* must be before zap_debug.h */ -DEF_PARM_BOOL(poll_digital_inputs, 1, 0600, "Poll Digital Inputs"); /* must be before zap_debug.h */ +DEF_PARM_BOOL(poll_digital_inputs, 1, 0600, "Poll Digital Inputs"); +DEF_PARM_BOOL(reversepolarity, 0, 0600, "Reverse Line Polarity"); /* Signaling is opposite (fxo signalling for fxs card) */ #if 1 @@ -69,6 +70,22 @@ static /* 0x0F */ DECLARE_CMD(FXS, REGISTER_REQUEST, byte chipsel, bool writing, #define VALID_CHIPSEL(x) (((chipsel) >= 0 && (chipsel) <= 7) || (chipsel) == ALL_CHANS) +/* Values of SLIC linefeed control register (0x40) */ +enum fxs_state { + FXS_LINE_OPEN = 0x00, /* Open */ + FXS_LINE_ACTIVE = 0x01, /* Forward active */ + FXS_LINE_OHTRANS = 0x02, /* Forward on-hook transmission */ + FXS_LINE_TIPOPEN = 0x03, /* TIP open */ + FXS_LINE_RING = 0x04, /* Ringing */ + FXS_LINE_REV_ACTIVE = 0x05, /* Reverse active */ + FXS_LINE_REV_OHTRANS = 0x06, /* Reverse on-hook transmission */ + FXS_LINE_RING_OPEN = 0x07 /* RING open */ +}; + +#define FXS_LINE_POL_ACTIVE ((reversepolarity) ? FXS_LINE_REV_ACTIVE : FXS_LINE_ACTIVE) +#define FXS_LINE_POL_OHTRANS ((reversepolarity) ? FXS_LINE_REV_OHTRANS : FXS_LINE_OHTRANS) + + /*---------------- FXS Protocol Commands ----------------------------------*/ static /* 0x0F */ DECLARE_CMD(FXS, XPD_STATE, bool on); @@ -86,13 +103,18 @@ static void start_stop_vm_led(xbus_t *xbus, xpd_t *xpd, lineno_t pos); #define PROC_FXS_INFO_FNAME "fxs_info" struct FXS_priv_data { - struct proc_dir_entry *regfile; - struct proc_dir_entry *fxs_info; - xpp_line_t ledstate[NUM_LEDS]; /* 0 - OFF, 1 - ON */ - xpp_line_t ledcontrol[NUM_LEDS]; /* 0 - OFF, 1 - ON */ - xpp_line_t found_fsk_pattern; - xpp_line_t msg_waiting; - int led_counter[NUM_LEDS][CHANNELS_PERXPD]; + struct proc_dir_entry *regfile; + struct proc_dir_entry *fxs_info; + xpp_line_t ledstate[NUM_LEDS]; /* 0 - OFF, 1 - ON */ + xpp_line_t ledcontrol[NUM_LEDS]; /* 0 - OFF, 1 - ON */ + xpp_line_t found_fsk_pattern; + xpp_line_t msg_waiting; + xpp_line_t update_offhook_state; + int led_counter[NUM_LEDS][CHANNELS_PERXPD]; + int ohttimer[CHANNELS_PERXPD]; +#define OHT_TIMER 6000 /* How long after RING to retain OHT */ + enum fxs_state idletxhookstate[CHANNELS_PERXPD]; /* IDLE changing hook state */ + enum fxs_state lasttxhook[CHANNELS_PERXPD]; }; /* @@ -108,6 +130,16 @@ struct FXS_priv_data { #define LED_BLINK_RING (1000/8) /* in ticks */ /*---------------- FXS: Static functions ----------------------------------*/ +static int linefeed_control(xbus_t *xbus, xpd_t *xpd, lineno_t chan, enum fxs_state value) +{ + struct FXS_priv_data *priv; + + priv = xpd->priv; + DBG("%s/%s/%d: value=0x%02X\n", xbus->busname, xpd->xpdname, chan, value); + priv->lasttxhook[chan] = value; + return SLIC_DIRECT_REQUEST(xbus, xpd, chan, SLIC_WRITE, 0x40, value); +} + static int do_chan_power(xbus_t *xbus, xpd_t *xpd, lineno_t chan, bool on) { int value = (on) ? 0x06 : 0x00; @@ -223,21 +255,20 @@ static void handle_fxs_leds(xpd_t *xpd) } } -static int do_callerid(xbus_t *xbus, xpd_t *xpd, lineno_t chan) +static void restore_leds(xpd_t *xpd) { - int ret = 0; - int i; + struct FXS_priv_data *priv; + int i; - BUG_ON(!xbus); - BUG_ON(!xpd); - DBG("%s/%s/%d:\n", xbus->busname, xpd->xpdname, chan); - ret = SLIC_DIRECT_REQUEST(xbus, xpd, chan, SLIC_WRITE, 0x40, FXS_LINE_CID); - for_each_line(xpd, i) - xpd->lasttxhook[i] = FXS_LINE_CID; - return ret; + priv = xpd->priv; + for_each_line(xpd, i) { + if(IS_SET(xpd->offhook, i)) + MARK_ON(priv, i, LED_GREEN); + else + MARK_OFF(priv, i, LED_GREEN); + } } - /*---------------- FXS: Methods -------------------------------------------*/ static xpd_t *FXS_card_new(xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, byte revision) @@ -310,6 +341,9 @@ static int FXS_card_init(xbus_t *xbus, xpd_t *xpd) priv->regfile->read_proc = proc_xpd_register_read; priv->regfile->data = xpd; #endif + for_each_line(xpd, i) { + priv->idletxhookstate[i] = FXS_LINE_POL_ACTIVE; + } ret = run_initialize_registers(xpd); if(ret < 0) goto err; @@ -333,6 +367,7 @@ static int FXS_card_init(xbus_t *xbus, xpd_t *xpd) do_led(xpd, i, LED_GREEN, 0); msleep(50); } + restore_leds(xpd); return 0; err: clean_proc(xbus, xpd); @@ -404,19 +439,26 @@ static int FXS_card_zaptel_postregistration(xpd_t *xpd, bool on) MARK_OFF(priv, i, LED_RED); msleep(2); } + restore_leds(xpd); return 0; } int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig) { - int ret = 0; + struct FXS_priv_data *priv; + int ret = 0; + struct zt_chan *chan = NULL; + enum fxs_state txhook; DBG("%s/%s/%d: %s\n", xbus->busname, xpd->xpdname, pos, txsig2str(txsig)); + priv = xpd->priv; BUG_ON(xpd->direction != TO_PHONE); if (IS_SET(xpd->digital_inputs, pos)) { DBG("Ignoring signal sent to digital input line\n"); return 0; } + if(SPAN_REGISTERED(xpd)) + chan = &xpd->span.chans[pos]; switch(txsig) { case ZT_TXSIG_ONHOOK: xpd->ringing[pos] = 0; @@ -426,41 +468,54 @@ int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig) ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 0); return ret; } + if (priv->lasttxhook[pos] == FXS_LINE_OPEN) { + /* + * Restore state after KEWL hangup. + */ + DBG("%s/%s/%d: KEWL STOP\n", + xbus->busname, xpd->xpdname, pos); + linefeed_control(xbus, xpd, pos, FXS_LINE_POL_ACTIVE); + if(IS_SET(xpd->offhook, pos)) + MARK_ON(priv, pos, LED_GREEN); + } ret = CALL_XMETHOD(RING, xbus, xpd, pos, 0); // RING off if (!IS_SET(xpd->offhook, pos)) start_stop_vm_led(xbus, xpd, pos); -#if 0 - switch(chan->sig) { - case ZT_SIG_EM: - case ZT_SIG_FXOKS: - case ZT_SIG_FXOLS: - xpd->lasttxhook[pos] = xpd->idletxhookstate[pos]; - break; - case ZT_SIG_FXOGS: - xpd->lasttxhook[pos] = FXS_LINE_TIPOPEN; - break; + txhook = priv->lasttxhook[pos]; + if(chan) { + switch(chan->sig) { + case ZT_SIG_EM: + case ZT_SIG_FXOKS: + case ZT_SIG_FXOLS: + txhook = priv->idletxhookstate[pos]; + break; + case ZT_SIG_FXOGS: + txhook = FXS_LINE_TIPOPEN; + break; + } } -#endif + ret = linefeed_control(xbus, xpd, pos, txhook); break; case ZT_TXSIG_OFFHOOK: + txhook = priv->lasttxhook[pos]; if(xpd->ringing[pos]) { BIT_SET(xpd->cid_on, pos); - ret = do_callerid(xpd->xbus, xpd, pos); // CALLER ID + txhook = FXS_LINE_OHTRANS; } xpd->ringing[pos] = 0; -#if 0 - switch(chan->sig) { - case ZT_SIG_EM: - xpd->lasttxhook[pos] = FXS_LINE_REV_ACTIVE; - break; - default: - xpd->lasttxhook[pos] = xpd->idletxhookstate[pos]; - break; + if(chan) { + switch(chan->sig) { + case ZT_SIG_EM: + txhook = FXS_LINE_POL_ACTIVE; + break; + default: + txhook = priv->idletxhookstate[pos]; + break; + } } -#endif + ret = linefeed_control(xbus, xpd, pos, txhook); break; case ZT_TXSIG_START: - xpd->lasttxhook[pos] = FXS_LINE_RING; xpd->ringing[pos] = 1; BIT_CLR(xpd->cid_on, pos); if(IS_SET(xpd->digital_outputs, pos)) { @@ -471,7 +526,9 @@ int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig) ret = CALL_XMETHOD(RING, xbus, xpd, pos, 1); // RING on break; case ZT_TXSIG_KEWL: - xpd->lasttxhook[pos] = FXS_LINE_DISABLED; + DBG("%s/%s/%d: KEWL START\n", xbus->busname, xpd->xpdname, pos); + linefeed_control(xbus, xpd, pos, FXS_LINE_OPEN); + MARK_OFF(priv, pos, LED_GREEN); break; default: NOTICE("%s: Can't set tx state to %s (%d)\n", __FUNCTION__, txsig2str(txsig), txsig); @@ -553,34 +610,65 @@ static int set_vm_led_mode(xbus_t *xbus, xpd_t *xpd, int pos, int on) static void start_stop_vm_led(xbus_t *xbus, xpd_t *xpd, lineno_t pos) { + struct FXS_priv_data *priv; bool on; + BUG_ON(!xpd); if (IS_SET(xpd->digital_outputs | xpd->digital_inputs, pos)) return; + priv = xpd->priv; on = IS_SET(((struct FXS_priv_data *)xpd->priv)->msg_waiting, pos); DBG("%s/%s/%d %s\n", xbus->busname, xpd->xpdname, pos, (on)?"ON":"OFF"); set_vm_led_mode(xbus, xpd, pos, on); do_chan_power(xbus, xpd, pos, on); - SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x40, (on) ? 0x04 : 0x01); + linefeed_control(xbus, xpd, pos, (on) ? FXS_LINE_RING : priv->idletxhookstate[pos]); } static int FXS_chan_onhooktransfer(xbus_t *xbus, xpd_t *xpd, lineno_t chan, int millies) { - int ret = 0; + struct FXS_priv_data *priv; + int ret = 0; BUG_ON(!xpd); + priv = xpd->priv; BUG_ON(chan == ALL_CHANS); DBG("%s/%s/%d: (%d millies)\n", xbus->busname, xpd->xpdname, chan, millies); - xpd->ohttimer[chan] = millies << 3; - xpd->idletxhookstate[chan] = FXS_LINE_CID; /* OHT mode when idle */ - if (xpd->lasttxhook[chan] == FXS_LINE_ENABLED) { - /* Apply the change if appropriate */ - ret = do_callerid(xpd->xbus, xpd, chan); // CALLER ID - } - start_stop_vm_led(xbus, xpd, chan); + if(!IS_SET(xpd->offhook, chan)) + start_stop_vm_led(xbus, xpd, chan); return ret; } +static int FXS_card_open(xpd_t *xpd, lineno_t chan) +{ + struct FXS_priv_data *priv; + bool is_offhook; + + BUG_ON(!xpd); + priv = xpd->priv; + is_offhook = IS_SET(xpd->offhook, chan); + DBG("%s/%s:%d (is %shook)\n", xpd->xbus->busname, xpd->xpdname, + chan, (is_offhook)?"off":"on"); + /* + * Delegate updating zaptel to FXS_card_tick(): + * The problem is that zt_hooksig() is spinlocking the channel and + * we are called by zaptel with the spinlock already held on the + * same channel. + */ + BIT_SET(priv->update_offhook_state, chan); + return 0; +} + +static int FXS_card_close(xpd_t *xpd, lineno_t chan) +{ + struct FXS_priv_data *priv; + + BUG_ON(!xpd); + DBG("%s/%s:%d\n", xpd->xbus->busname, xpd->xpdname, chan); + priv = xpd->priv; + priv->idletxhookstate[chan] = FXS_LINE_POL_ACTIVE; + return 0; +} + /* * INPUT polling is done via SLIC register 0x06 (same as LEDS): * 7 6 5 4 3 2 1 0 @@ -603,6 +691,36 @@ static void poll_inputs(xbus_t *xbus, xpd_t *xpd) } } +void handle_linefeed(xpd_t *xpd) +{ + struct FXS_priv_data *priv; + int i; + + BUG_ON(!xpd); + priv = xpd->priv; + BUG_ON(!priv); + for_each_line(xpd, i) { + if (priv->lasttxhook[i] == FXS_LINE_RING) { + /* RINGing, prepare for OHT */ + priv->ohttimer[i] = OHT_TIMER; + priv->idletxhookstate[i] = FXS_LINE_POL_OHTRANS; + } else { + if (priv->ohttimer[i]) { + priv->ohttimer[i]--; + if (!priv->ohttimer[i]) { + priv->idletxhookstate[i] = FXS_LINE_POL_ACTIVE; + if (priv->lasttxhook[i] == FXS_LINE_POL_OHTRANS) { + enum fxs_state txhook = FXS_LINE_POL_ACTIVE; + /* Apply the change if appropriate */ + BIT_CLR(xpd->cid_on, i); + linefeed_control(xpd->xbus, xpd, i, txhook); + } + } + } + } + } +} + #ifndef VMWI_IOCTL /* * Detect Voice Mail Waiting Indication @@ -678,6 +796,28 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) } #endif handle_fxs_leds(xpd); + handle_linefeed(xpd); + if(priv->update_offhook_state) { /* set in FXS_card_open() */ + int i; + + for_each_line(xpd, i) { + if(!IS_SET(priv->update_offhook_state, i)) + continue; + /* + * Update LEDs and zaptel with current state of line. + */ + if(IS_SET(xpd->offhook, i)) { + NOTICE("%s/%s/%d: Already offhook during open. OK.\n", + xbus->busname, xpd->xpdname, i); + MARK_ON(priv, i, LED_GREEN); + update_line_status(xpd, i, 1); + } else { + MARK_OFF(priv, i, LED_GREEN); + update_line_status(xpd, i, 0); + } + BIT_CLR(priv->update_offhook_state, i); + } + } #ifndef VMWI_IOCTL if(SPAN_REGISTERED(xpd)) detect_vmwi(xpd); @@ -721,9 +861,8 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) static /* 0x0F */ HOSTCMD(FXS, XPD_STATE, bool on) { - int ret = 0; int i; - enum fxs_state value = (on) ? 0x01 : 0x00; + enum fxs_state value = (on) ? FXS_LINE_POL_ACTIVE : FXS_LINE_OPEN; unsigned long flags; struct FXS_priv_data *priv; @@ -732,23 +871,22 @@ static /* 0x0F */ HOSTCMD(FXS, XPD_STATE, bool on) priv = xpd->priv; spin_lock_irqsave(&xpd->lock, flags); DBG("%s/%s: %s\n", xbus->busname, xpd->xpdname, (on) ? "on" : "off"); - ret = SLIC_DIRECT_REQUEST(xbus, xpd, ALL_CHANS, SLIC_WRITE, 0x40, value); for_each_line(xpd, i) - xpd->lasttxhook[i] = value; + linefeed_control(xbus, xpd, i, value); if(on) { MARK_ON(priv, ALL_CHANS, LED_GREEN); } else { MARK_OFF(priv, ALL_CHANS, LED_GREEN); } spin_unlock_irqrestore(&xpd->lock, flags); - return ret; + return 0; } static /* 0x0F */ HOSTCMD(FXS, RING, lineno_t chan, bool on) { int ret = 0; struct FXS_priv_data *priv; - enum fxs_state value = (on) ? 0x04 : 0x01; + enum fxs_state value = (on) ? FXS_LINE_RING : FXS_LINE_POL_ACTIVE; BUG_ON(!xbus); BUG_ON(!xpd); @@ -756,8 +894,7 @@ static /* 0x0F */ HOSTCMD(FXS, RING, lineno_t chan, bool on) priv = xpd->priv; set_vm_led_mode(xbus, xpd, chan, 0); do_chan_power(xbus, xpd, chan, on); // Power up (for ring) - ret = SLIC_DIRECT_REQUEST(xbus, xpd, chan, SLIC_WRITE, 0x40, value); - xpd->lasttxhook[chan] = value; + ret = linefeed_control(xbus, xpd, chan, value); if(on) { MARK_BLINK(priv, chan, LED_GREEN, LED_BLINK_RING); } else { @@ -819,7 +956,6 @@ HANDLER_DEF(FXS, SIG_CHANGED) DBG("%s/%s/%d: ONHOOK\n", xbus->busname, xpd->xpdname, i); MARK_OFF(priv, i, LED_GREEN); update_line_status(xpd, i, 0); - start_stop_vm_led(xbus, xpd, i); } } } @@ -910,6 +1046,8 @@ xproto_table_t PROTO_TABLE(FXS) = { .card_hooksig = FXS_card_hooksig, .card_tick = FXS_card_tick, .chan_onhooktransfer = FXS_chan_onhooktransfer, + .card_open = FXS_card_open, + .card_close = FXS_card_close, #ifdef VMWI_IOCTL .card_ioctl = FXS_card_ioctl, #endif @@ -917,8 +1055,6 @@ xproto_table_t PROTO_TABLE(FXS) = { .RING = XPROTO_CALLER(FXS, RING), .RELAY_OUT = XPROTO_CALLER(FXS, RELAY_OUT), .XPD_STATE = XPROTO_CALLER(FXS, XPD_STATE), - - .SYNC_SOURCE = XPROTO_CALLER(GLOBAL, SYNC_SOURCE), }, .packet_is_valid = fxs_packet_is_valid, .packet_dump = fxs_packet_dump, @@ -954,10 +1090,28 @@ static int proc_fxs_info_read(char *page, char **start, off_t off, int count, in spin_lock_irqsave(&xpd->lock, flags); priv = xpd->priv; BUG_ON(!priv); - len += sprintf(page + len, "\t%-17s: ", "Channel"); + len += sprintf(page + len, "%-8s %-10s %-10s %-10s\n", + "Channel", + "idletxhookstate", + "lasttxhook", + "ohttimer" + ); for_each_line(xpd, i) { - if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i)) - len += sprintf(page + len, "%d ", i % 10); + char pref; + + if(IS_SET(xpd->digital_outputs, i)) + pref = 'O'; + else if(IS_SET(xpd->digital_inputs, i)) + pref = 'I'; + else + pref = ' '; + len += sprintf(page + len, "%c%7d %10d %10d %10d\n", + pref, + i, + priv->idletxhookstate[i], + priv->lasttxhook[i], + priv->ohttimer[i] + ); } len += sprintf(page + len, "\n"); for(led = 0; led < NUM_LEDS; led++) { |