summaryrefslogtreecommitdiff
path: root/kernel/xpp/card_fxo.c
diff options
context:
space:
mode:
authortzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2008-03-07 00:45:53 +0000
committertzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2008-03-07 00:45:53 +0000
commit1a571d19740d87e24b92ef49a2d38b26256112ae (patch)
tree8490182ea096b25f977d73c8c7a89b286014b94c /kernel/xpp/card_fxo.c
parent92d02c5fadec34a75c085e9f95eeab268c3683c8 (diff)
xpp.r5512:
* Build: - Zaptel >= 1.4.9 is migrating to storing kernel stuff in zaptel/kernel/* - We conditionally use old/new directory scheme: In xpp/Kbuild and xpp/utils/Makefile use ZAP_KERNEL variable, so it's not confused with ZAPTEL_DIR (which appears in zaptel/Makefile as well). - Fix compile warnings on 64 bit systems. - Compile fixes for kernel-2.6.24 * UDEV: - /etc/udev/rules.d/xpp.rules now uses XPP_INIT_DIR to find astribank_hook. - astribank_hook: Modify to do nothing. Add some documentation. * Autoconfiguration -- zapconf: - Don't fail zapconf et.al. if no config file was found. - Skip the 'IRQ Missing:' line in /proc/zaptel/nnn for wcte1xp(?). - Add some newer Digium cards to our hardware inventory. - Partially handle cases where the /proc/zaptel strings does not contain info about E1/T1/J1 or NT/TE. * Better SYNC: - Finer tuning of PLL (New firmware). - Change calculation algorithm of sync offset. It now copes better with the variance in USB frame reception timing. - Statistics: . The view of results was moved from /proc/xpp/XBUS-*/summary to a new /sys/bus/astribanks/devices/xbus-*/timing and enhanced. . A new xpp_timing script shows all astribanks. . A new write only /sys/bus/astribanks/devices/xbus-*/cls is used to clear statistics. Eventually, clearing of XBUS related statistics should be done here. One that was migrated is the clearing of 'PCM [TR]X:' numbers currently appearing in /proc/xpp/XBUS-*/summary (they should be moved too later). - Shorten the strings representation sync_mode ("SYNC_MODE_AB" -> "AB") adapted their use in printk and /proc so the text is clear. - Added a command line parameter xpp.disable_pll_sync to stop all adjustments command to AB (calculations still continue as usual). * PRI: - 4 port support - set clocking master span via ztcfg, like other zaptel devices. * FXO: - Fix false hangups in some countries (voltage fluctuations). - Some countries send caller-id before first ring. Added code to handle caller-id PCM pass through according to a new command line parameter (xpd_fxo.caller_id_style). - No longer sends an event on zt_open. See #12160 . * Misc: - Adapt to zaptel-1.4.8 and above ztscan: added fields returend by new ZT_SPANSTAT_V2 ioctl() - Document sysfs and waitfor_xpds. - Miscelaneous optimizations and bugfixes. - Remove deprecated pcm_tasklet parameter. The rx_tasklet parameter has replaced it a long time ago. - Add RX_CMD counter to /proc/xpp/XBUS-*/summary - Unclutter some of the usb disconnect messages. - xpp_usb: minor preformance improvements in receive. Expose the number of pending receive URB's in /proc/xpp/XBUS-*/xpp_usb Merged revisions 3952 via svnmerge from http://svn.digium.com/svn/zaptel/branches/1.2 git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.4@3957 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'kernel/xpp/card_fxo.c')
-rw-r--r--kernel/xpp/card_fxo.c396
1 files changed, 300 insertions, 96 deletions
diff --git a/kernel/xpp/card_fxo.c b/kernel/xpp/card_fxo.c
index cbe953b..34fc41c 100644
--- a/kernel/xpp/card_fxo.c
+++ b/kernel/xpp/card_fxo.c
@@ -40,6 +40,13 @@ DEF_PARM(uint, poll_power_denial_interval, 40, 0644, "Power denial detection pol
DEF_PARM(uint, poll_metering_interval, 500, 0644, "Poll metering interval in milliseconds (0 - disable)");
#endif
DEF_PARM(int, ring_debounce, 50, 0644, "Number of ticks to debounce a false RING indication");
+DEF_PARM(int, caller_id_style, 0, 0444, "Caller-Id detection style: 0 - [BELL], 1 - [BT], 2 - [PASS]");
+
+enum cid_style {
+ CID_STYLE_BELL = 0, /* E.g: US (Bellcore) */
+ CID_STYLE_ETSI_POLREV = 1, /* E.g: UK (British Telecom) */
+ CID_STYLE_PASS_ALWAYS = 2, /* E.g: DK */
+};
/* Signaling is opposite (fxs signalling for fxo card) */
#if 1
@@ -55,9 +62,18 @@ enum fxo_leds {
#define NUM_LEDS 1
#define DELAY_UNTIL_DIALTONE 3000
-#define POLREV_THRESHOLD 1000 /* minimum duration for polarity reversal detection (in ticks) */
+/*
+ * Minimum duration for polarity reversal detection (in ticks)
+ * Should be longer than the time to detect a ring, so voltage
+ * fluctuation during ring won't trigger false detection.
+ */
+#define POLREV_THRESHOLD 200
#define BAT_THRESHOLD 3
#define BAT_DEBOUNCE 1000 /* compensate for battery voltage fluctuation (in ticks) */
+#define POWER_DENIAL_CURRENT 3
+#define POWER_DENIAL_TIME 80 /* ticks */
+#define POWER_DENIAL_SAFEZONE 100 /* ticks */
+#define POWER_DENIAL_DELAY 2500 /* ticks */
/* Shortcuts */
#define DAA_WRITE 1
@@ -82,6 +98,7 @@ static int proc_xpd_metering_read(char *page, char **start, off_t off, int count
static int proc_xpd_register_read(char *page, char **start, off_t off, int count, int *eof, void *data);
static int proc_xpd_register_write(struct file *file, const char __user *buffer, unsigned long count, void *data);
static int handle_register_command(xpd_t *xpd, char *cmdline);
+static void zap_report_battery(xpd_t *xpd, lineno_t chan);
#define PROC_REGISTER_FNAME "slics"
#define PROC_FXO_INFO_FNAME "fxo_info"
@@ -94,8 +111,17 @@ static int handle_register_command(xpd_t *xpd, char *cmdline);
#define DAA_REG_CURRENT 0x1C /* 28 */
#define DAA_REG_VBAT 0x1D /* 29 */
-#define POWER_DENIAL_CURRENT 3
-#define POWER_DENIAL_TIME 80 /* ticks */
+enum battery_state {
+ BATTERY_UNKNOWN = 0,
+ BATTERY_ON = 1,
+ BATTERY_OFF = -1
+};
+
+enum polarity_state {
+ POL_UNKNOWN = 0,
+ POL_POSITIVE = 1,
+ POL_NEGATIVE = -1
+};
struct FXO_priv_data {
struct proc_dir_entry *regfile;
@@ -105,11 +131,15 @@ struct FXO_priv_data {
struct proc_dir_entry *fxo_info;
uint poll_counter;
signed char battery_voltage[CHANNELS_PERXPD];
- xpp_line_t battery;
- ushort battery_debounce[CHANNELS_PERXPD];
- xpp_line_t polarity;
- ushort polarity_counter[CHANNELS_PERXPD];
- ushort current_counter[CHANNELS_PERXPD];
+ signed char battery_current[CHANNELS_PERXPD];
+ enum battery_state battery[CHANNELS_PERXPD];
+ ushort nobattery_debounce[CHANNELS_PERXPD];
+ enum polarity_state polarity[CHANNELS_PERXPD];
+ ushort polarity_debounce[CHANNELS_PERXPD];
+ xpp_line_t maybe_power_denial;
+ ushort power_denial_debounce[CHANNELS_PERXPD];
+ ushort power_denial_delay[CHANNELS_PERXPD];
+ ushort power_denial_safezone[CHANNELS_PERXPD];
xpp_line_t ledstate[NUM_LEDS]; /* 0 - OFF, 1 - ON */
xpp_line_t ledcontrol[NUM_LEDS]; /* 0 - OFF, 1 - ON */
int led_counter[NUM_LEDS][CHANNELS_PERXPD];
@@ -134,6 +164,16 @@ struct FXO_priv_data {
/*---------------- FXO: Static functions ----------------------------------*/
+static void reset_battery_readings(xpd_t *xpd, lineno_t pos)
+{
+ struct FXO_priv_data *priv = xpd->priv;
+
+ priv->nobattery_debounce[pos] = 0;
+ priv->power_denial_debounce[pos] = 0;
+ priv->power_denial_delay[pos] = 0;
+ BIT_CLR(priv->maybe_power_denial, pos);
+}
+
/*
* LED control is done via DAA register 0x20
*/
@@ -208,10 +248,16 @@ static void update_zap_ring(xpd_t *xpd, int pos, bool on)
BUG_ON(!xpd);
if(on) {
- BIT_CLR(xpd->cid_on, pos);
+ if(caller_id_style == CID_STYLE_BELL) {
+ LINE_DBG(SIGNAL, xpd, pos, "Caller-ID PCM: off\n");
+ BIT_CLR(xpd->cid_on, pos);
+ }
rxsig = ZT_RXSIG_RING;
} else {
- BIT_SET(xpd->cid_on, pos);
+ if(caller_id_style == CID_STYLE_BELL) {
+ LINE_DBG(SIGNAL, xpd, pos, "Caller-ID PCM: on\n");
+ BIT_SET(xpd->cid_on, pos);
+ }
rxsig = ZT_RXSIG_OFFHOOK;
}
pcm_recompute(xpd, 0);
@@ -235,7 +281,7 @@ static void mark_ring(xpd_t *xpd, lineno_t pos, bool on, bool update_zap)
* We don't want to check battery during ringing
* due to voltage fluctuations.
*/
- priv->battery_debounce[pos] = 0;
+ reset_battery_readings(xpd, pos);
if(on && !xpd->ringing[pos]) {
LINE_DBG(SIGNAL, xpd, pos, "START\n");
xpd->ringing[pos] = 1;
@@ -265,8 +311,9 @@ static int do_sethook(xpd_t *xpd, int pos, bool to_offhook)
xbus = xpd->xbus;
priv = xpd->priv;
BUG_ON(!priv);
- if(!IS_SET(priv->battery, pos)) {
- LINE_DBG(SIGNAL, xpd, pos, "WARNING: called while battery is off\n");
+ if(priv->battery[pos] != BATTERY_ON && to_offhook) {
+ LINE_NOTICE(xpd, pos, "Cannot take offhook while battery is off!\n");
+ return -EINVAL;
}
spin_lock_irqsave(&xpd->lock, flags);
mark_ring(xpd, pos, 0, 0); // No more rings
@@ -281,6 +328,9 @@ static int do_sethook(xpd_t *xpd, int pos, bool to_offhook)
BIT_SET(xpd->offhook, pos);
} else {
BIT_CLR(xpd->offhook, pos);
+ }
+ if(caller_id_style != CID_STYLE_PASS_ALWAYS) {
+ LINE_DBG(SIGNAL, xpd, pos, "Caller-ID PCM: off\n");
BIT_CLR(xpd->cid_on, pos);
}
#ifdef WITH_METERING
@@ -288,6 +338,8 @@ static int do_sethook(xpd_t *xpd, int pos, bool to_offhook)
priv->metering_tone_state = 0L;
DAA_DIRECT_REQUEST(xbus, xpd, pos, DAA_WRITE, DAA_REG_METERING, 0x2D);
#endif
+ reset_battery_readings(xpd, pos); /* unstable during hook changes */
+ priv->power_denial_safezone[pos] = (to_offhook) ? POWER_DENIAL_SAFEZONE : 0;
spin_unlock_irqrestore(&xpd->lock, flags);
return ret;
}
@@ -387,6 +439,8 @@ static int FXO_card_init(xbus_t *xbus, xpd_t *xpd)
// Hanghup all lines
for_each_line(xpd, i) {
do_sethook(xpd, i, 0);
+ priv->polarity[i] = POL_UNKNOWN; /* will be updated on next battery sample */
+ priv->battery[i] = BATTERY_UNKNOWN; /* will be updated on next battery sample */
}
XPD_DBG(GENERAL, xpd, "done\n");
for_each_line(xpd, i) {
@@ -431,6 +485,9 @@ static int FXO_card_zaptel_preregistration(xpd_t *xpd, bool on)
priv = xpd->priv;
BUG_ON(!priv);
XPD_DBG(GENERAL, xpd, "%s\n", (on)?"ON":"OFF");
+#ifdef ZT_SPANSTAT_V2
+ xpd->span.spantype = "FXO";
+#endif
for_each_line(xpd, i) {
struct zt_chan *cur_chan = &xpd->chans[i];
@@ -461,6 +518,7 @@ static int FXO_card_zaptel_postregistration(xpd_t *xpd, bool on)
BUG_ON(!priv);
XPD_DBG(GENERAL, xpd, "%s\n", (on)?"ON":"OFF");
for_each_line(xpd, i) {
+ zap_report_battery(xpd, i);
MARK_OFF(priv, i, LED_GREEN);
msleep(2);
// MARK_OFF(priv, i, LED_RED);
@@ -472,6 +530,7 @@ static int FXO_card_zaptel_postregistration(xpd_t *xpd, bool on)
static int FXO_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig)
{
struct FXO_priv_data *priv;
+ int ret = 0;
priv = xpd->priv;
BUG_ON(!priv);
@@ -482,10 +541,10 @@ static int FXO_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig)
case ZT_TXSIG_START:
break;
case ZT_TXSIG_OFFHOOK:
- do_sethook(xpd, pos, 1);
+ ret = do_sethook(xpd, pos, 1);
break;
case ZT_TXSIG_ONHOOK:
- do_sethook(xpd, pos, 0);
+ ret = do_sethook(xpd, pos, 0);
break;
default:
XPD_NOTICE(xpd, "Can't set tx state to %s (%d)\n",
@@ -493,7 +552,30 @@ static int FXO_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig)
return -EINVAL;
}
pcm_recompute(xpd, 0);
- return 0;
+ return ret;
+}
+
+static void zap_report_battery(xpd_t *xpd, lineno_t chan)
+{
+ struct FXO_priv_data *priv;
+
+ BUG_ON(!xpd);
+ priv = xpd->priv;
+ if(SPAN_REGISTERED(xpd)) {
+ switch(priv->battery[chan]) {
+ case BATTERY_UNKNOWN:
+ /* no-op */
+ break;
+ case BATTERY_OFF:
+ LINE_DBG(SIGNAL, xpd, chan, "Send ZT_ALARM_RED\n");
+ zt_alarm_channel(&xpd->chans[chan], ZT_ALARM_RED);
+ break;
+ case BATTERY_ON:
+ LINE_DBG(SIGNAL, xpd, chan, "Send ZT_ALARM_NONE\n");
+ zt_alarm_channel(&xpd->chans[chan], ZT_ALARM_NONE);
+ break;
+ }
+ }
}
static int FXO_card_open(xpd_t *xpd, lineno_t chan)
@@ -502,14 +584,6 @@ static int FXO_card_open(xpd_t *xpd, lineno_t chan)
BUG_ON(!xpd);
priv = xpd->priv;
- /*
- * We pretend to have battery. If this is really the case
- * than next calls to update_battery_status() won't change it.
- * If we don't have battery, than on the next calls to
- * update_battery_status() a battery_debounce[] cycle would start.
- * Than, if no-battery is persistent, asterisk would be notified.
- */
- BIT_SET(priv->battery, chan);
return 0;
}
@@ -563,6 +637,37 @@ static void handle_fxo_ring(xpd_t *xpd)
}
}
+static void handle_fxo_power_denial(xpd_t *xpd)
+{
+ struct FXO_priv_data *priv;
+ int i;
+
+ priv = xpd->priv;
+ for_each_line(xpd, i) {
+ if(priv->power_denial_safezone[i] > 0)
+ priv->power_denial_safezone[i]--;
+ if(IS_SET(priv->maybe_power_denial, i) && !xpd->ringing[i] && IS_SET(xpd->offhook, i)) {
+ /*
+ * Ring detection by the firmware takes some time.
+ * Therefore we delay our decision until we are
+ * sure that no ring has started during this time.
+ */
+ priv->power_denial_delay[i]++;
+ if (priv->power_denial_delay[i] >= POWER_DENIAL_DELAY) {
+ LINE_DBG(SIGNAL, xpd, i, "Power Denial Hangup\n");
+ priv->power_denial_delay[i] = 0;
+ BIT_CLR(priv->maybe_power_denial, i);
+ do_sethook(xpd, i, 0);
+ update_line_status(xpd, i, 0);
+ pcm_recompute(xpd, 0);
+ }
+ } else {
+ priv->power_denial_delay[i] = 0;
+ BIT_CLR(priv->maybe_power_denial, i);
+ }
+ }
+}
+
static int FXO_card_tick(xbus_t *xbus, xpd_t *xpd)
{
struct FXO_priv_data *priv;
@@ -580,6 +685,7 @@ static int FXO_card_tick(xbus_t *xbus, xpd_t *xpd)
#endif
handle_fxo_leds(xpd);
handle_fxo_ring(xpd);
+ handle_fxo_power_denial(xpd);
priv->poll_counter++;
return 0;
}
@@ -686,10 +792,13 @@ HANDLER_DEF(FXO, SIG_CHANGED)
int debounce;
if(IS_SET(sig_toggles, i)) {
- if(!IS_SET(priv->battery, i)) {
- LINE_DBG(SIGNAL, xpd, i, "SIG_CHANGED while battery is off.\n");
- // FIXME: allow dialing without battery polling...
- // continue;
+ if(priv->battery[i] == BATTERY_OFF) {
+ /*
+ * With poll_battery_interval==0 we cannot have BATTERY_OFF
+ * so we won't get here
+ */
+ LINE_NOTICE(xpd, i, "SIG_CHANGED while battery is off. Ignored.\n");
+ continue;
}
/* First report false ring alarms */
debounce = atomic_read(&priv->ring_debounce[i]);
@@ -711,103 +820,152 @@ HANDLER_DEF(FXO, SIG_CHANGED)
#define zt_alarm_channel(a,b) zt_qevent_lock(a,( (b)==ZT_ALARM_NONE )? \
ZT_EVENT_NOALARM : ZT_EVENT_ALARM)
#endif
-static void update_battery_status(xpd_t *xpd, byte data_low, lineno_t chipsel)
+static void update_battery_voltage(xpd_t *xpd, byte data_low, lineno_t chipsel)
{
struct FXO_priv_data *priv;
- byte bat = abs((signed char)data_low);
- byte pol = IS_SET(data_low, 7);
+ enum polarity_state pol;
int msec;
-
priv = xpd->priv;
BUG_ON(!priv);
priv->battery_voltage[chipsel] = data_low;
- if(bat < BAT_THRESHOLD) {
+ if(xpd->ringing[chipsel])
+ goto ignore_reading; /* ring voltage create false alarms */
+ if(abs((signed char)data_low) < BAT_THRESHOLD) {
/*
* Check for battery voltage fluctuations
*/
- if(IS_SET(priv->battery, chipsel)) {
+ if(priv->battery[chipsel] != BATTERY_OFF) {
int milliseconds;
- milliseconds = priv->battery_debounce[chipsel]++ *
+ milliseconds = priv->nobattery_debounce[chipsel]++ *
poll_battery_interval;
if(milliseconds > BAT_DEBOUNCE) {
- LINE_DBG(SIGNAL, xpd, chipsel, "BATTERY OFF voltage=%d\n", bat);
- BIT_CLR(priv->battery, chipsel);
+ LINE_DBG(SIGNAL, xpd, chipsel, "BATTERY OFF voltage=%d\n", data_low);
+ priv->battery[chipsel] = BATTERY_OFF;
if(SPAN_REGISTERED(xpd))
- zt_alarm_channel(&xpd->chans[chipsel], ZT_ALARM_RED);
+ zap_report_battery(xpd, chipsel);
+ priv->polarity[chipsel] = POL_UNKNOWN; /* What's the polarity ? */
+ /*
+ * Stop further processing for now
+ */
+ goto ignore_reading;
}
}
} else {
- priv->battery_debounce[chipsel] = 0;
- if(!IS_SET(priv->battery, chipsel)) {
- LINE_DBG(SIGNAL, xpd, chipsel, "BATTERY ON voltage=%d\n", bat);
- BIT_SET(priv->battery, chipsel);
+ priv->nobattery_debounce[chipsel] = 0;
+ if(priv->battery[chipsel] != BATTERY_ON) {
+ LINE_DBG(SIGNAL, xpd, chipsel, "BATTERY ON voltage=%d\n", data_low);
+ priv->battery[chipsel] = BATTERY_ON;
if(SPAN_REGISTERED(xpd))
- zt_alarm_channel(&xpd->chans[chipsel], ZT_ALARM_NONE);
+ zap_report_battery(xpd, chipsel);
}
}
+ if(priv->battery[chipsel] != BATTERY_ON) {
+ priv->polarity[chipsel] = POL_UNKNOWN; /* What's the polarity ? */
+ return;
+ }
/*
* Handle reverse polarity
*/
- if(IS_SET(priv->polarity, chipsel) == pol) {
- /* same, same, nothing to see here, move on */
- priv->polarity_counter[chipsel] = 0;
+ if(data_low == 0)
+ pol = POL_UNKNOWN;
+ else if(IS_SET(data_low, 7))
+ pol = POL_NEGATIVE;
+ else
+ pol = POL_POSITIVE;
+ if(priv->polarity[chipsel] == pol) {
+ /*
+ * Same polarity, reset debounce counter
+ */
+ priv->polarity_debounce[chipsel] = 0;
return;
}
/*
* Track polarity reversals and debounce spikes.
* Only reversals with long duration count.
*/
- msec = priv->polarity_counter[chipsel]++ * poll_battery_interval;
+ msec = priv->polarity_debounce[chipsel]++ * poll_battery_interval;
if (msec >= POLREV_THRESHOLD) {
- LINE_DBG(SIGNAL, xpd, chipsel, "Polarity is %s\n",
- (pol)?"Positive":"Negative");
- priv->polarity_counter[chipsel] = 0;
- if (pol)
- BIT_SET(priv->polarity, chipsel);
- else
- BIT_CLR(priv->polarity, chipsel);
- /* polarity reversal during offhook should be reported to zaptel */
- if(IS_SET(xpd->offhook, chipsel)) {
- /* Inform Zaptel */
- LINE_DBG(SIGNAL, xpd, chipsel, "Send ZT_EVENT_POLARITY\n");
- zt_qevent_lock(&xpd->chans[chipsel], ZT_EVENT_POLARITY);
-#if 0
+ priv->polarity_debounce[chipsel] = 0;
+ if(pol != POL_UNKNOWN) {
+ char *polname = NULL;
+
+ if(pol == POL_POSITIVE)
+ polname = "Positive";
+ else if(pol == POL_NEGATIVE)
+ polname = "Negative";
+ else
+ BUG();
+ LINE_DBG(SIGNAL, xpd, chipsel,
+ "Polarity changed to %s\n", polname);
/*
- * These two lines hangup the channel (by sending a message to
- * the firmware), and inform Zaptel that the line has been hung-up.
- * They are not needed if Asterisk does the hangup after receiving
- * a notification from Zaptel (which is sent by the above zt_qevent_lock().
- * Asterisk does that if it has "hanguponpolarityswitch=1" in zapata.conf.
+ * Inform zaptel/Asterisk:
+ * 1. Maybe used for hangup detection during offhook
+ * 2. In some countries used to report caller-id during onhook
+ * but before first ring.
*/
- do_sethook(xpd, chipsel, 0);
- update_line_status(xpd, chipsel, 0);
- pcm_recompute(xpd, 0);
-#endif
+ if(caller_id_style == CID_STYLE_ETSI_POLREV) {
+ LINE_DBG(SIGNAL, xpd, chipsel, "Caller-ID PCM: on\n");
+ BIT_SET(xpd->cid_on, chipsel); /* will be cleared on ring/offhook */
+ }
+ if(SPAN_REGISTERED(xpd)) {
+ LINE_DBG(SIGNAL, xpd, chipsel,
+ "Send ZT_EVENT_POLARITY: %s\n", polname);
+ zt_qevent_lock(&xpd->chans[chipsel], ZT_EVENT_POLARITY);
+ }
}
+ priv->polarity[chipsel] = pol;
}
+ return;
+ignore_reading:
+ /*
+ * Reset debounce counters to prevent false alarms
+ */
+ reset_battery_readings(xpd, chipsel); /* unstable during hook changes */
}
-static void update_power_denial(xpd_t *xpd, byte data_low, lineno_t chipsel)
+static void update_battery_current(xpd_t *xpd, byte data_low, lineno_t chipsel)
{
struct FXO_priv_data *priv;
priv = xpd->priv;
BUG_ON(!priv);
- if (IS_SET(xpd->offhook, chipsel) && data_low < POWER_DENIAL_CURRENT) {
- /* Current dropped */
- priv->current_counter[chipsel]++;
- if (priv->current_counter[chipsel] * poll_battery_interval >= POWER_DENIAL_TIME) {
- LINE_DBG(SIGNAL, xpd, chipsel, "Power Denial Hangup\n");
- priv->current_counter[chipsel] = 0;
- do_sethook(xpd, chipsel, 0);
- update_line_status(xpd, chipsel, 0);
- pcm_recompute(xpd, 0);
+ priv->battery_current[chipsel] = data_low;
+ /*
+ * During ringing, current is not stable.
+ * During onhook there should not be current anyway.
+ */
+ if(xpd->ringing[chipsel] || !IS_SET(xpd->offhook, chipsel))
+ goto ignore_it;
+ /*
+ * Power denial with no battery voltage is meaningless
+ */
+ if(priv->battery[chipsel] != BATTERY_ON)
+ goto ignore_it;
+ /* Safe zone after offhook */
+ if(priv->power_denial_safezone[chipsel] > 0)
+ goto ignore_it;
+ if(data_low < POWER_DENIAL_CURRENT) {
+ /* Current dropped -- is it long enough (minimum ~80msec) */
+ priv->power_denial_debounce[chipsel]++;
+ if (priv->power_denial_debounce[chipsel] * poll_battery_interval >= POWER_DENIAL_TIME) {
+ /*
+ * But maybe the FXS started to ring (and the firmware haven't
+ * detected it yet). This would cause false power denials.
+ * So we just flag it and schedule more ticks to wait.
+ * These ticks would elapse in handle_fxo_power_denial()
+ */
+ LINE_DBG(SIGNAL, xpd, chipsel, "Possible Power Denial Hangup\n");
+ priv->power_denial_debounce[chipsel] = 0;
+ BIT_SET(priv->maybe_power_denial, chipsel);
}
- } else
- priv->current_counter[chipsel] = 0;
+ }
+ return;
+ignore_it:
+ BIT_CLR(priv->maybe_power_denial, chipsel);
+ priv->power_denial_debounce[chipsel] = 0;
}
#ifdef WITH_METERING
@@ -849,10 +1007,10 @@ static int FXO_card_register_reply(xbus_t *xbus, xpd_t *xpd, reg_cmd_t *info)
chipsel = REG_FIELD(info, chipsel);
switch(REG_FIELD(info, regnum)) {
case DAA_REG_VBAT:
- update_battery_status(xpd, REG_FIELD(info, data_low), chipsel);
+ update_battery_voltage(xpd, REG_FIELD(info, data_low), chipsel);
break;
case DAA_REG_CURRENT:
- update_power_denial(xpd, REG_FIELD(info, data_low), chipsel);
+ update_battery_current(xpd, REG_FIELD(info, data_low), chipsel);
break;
#ifdef WITH_METERING
case DAA_REG_METERING:
@@ -938,38 +1096,84 @@ static int proc_fxo_info_read(char *page, char **start, off_t off, int count, in
len += sprintf(page + len, "\t%-17s: ", "Channel");
for_each_line(xpd, i) {
if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i))
- len += sprintf(page + len, "%3d ", i % 10);
+ len += sprintf(page + len, "%4d ", i % 10);
}
- len += sprintf(page + len, "\n\t%-17s: ", "ledstate");
+ len += sprintf(page + len, "\nLeds:");
+ len += sprintf(page + len, "\n\t%-17s: ", "state");
for_each_line(xpd, i) {
if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i))
- len += sprintf(page + len, "%3d ", IS_SET(priv->ledstate[LED_GREEN], i));
+ len += sprintf(page + len, "%4d ", IS_SET(priv->ledstate[LED_GREEN], i));
}
len += sprintf(page + len, "\n\t%-17s: ", "blinking");
for_each_line(xpd, i) {
if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i))
- len += sprintf(page + len, "%3d ", IS_BLINKING(priv,i,LED_GREEN));
+ len += sprintf(page + len, "%4d ", IS_BLINKING(priv,i,LED_GREEN));
}
- len += sprintf(page + len, "\n\t%-17s: ", "battery");
+ len += sprintf(page + len, "\nBattery-Data:");
+ len += sprintf(page + len, "\n\t%-17s: ", "voltage");
for_each_line(xpd, i) {
- len += sprintf(page + len, "%3d ", IS_SET(priv->battery, i));
+ len += sprintf(page + len, "%4d ", priv->battery_voltage[i]);
+ }
+ len += sprintf(page + len, "\n\t%-17s: ", "current");
+ for_each_line(xpd, i) {
+ len += sprintf(page + len, "%4d ", priv->battery_current[i]);
+ }
+ len += sprintf(page + len, "\nBattery:");
+ len += sprintf(page + len, "\n\t%-17s: ", "on");
+ for_each_line(xpd, i) {
+ char *bat;
+
+ if(priv->battery[i] == BATTERY_ON)
+ bat = "+";
+ else if(priv->battery[i] == BATTERY_OFF)
+ bat = "-";
+ else
+ bat = ".";
+ len += sprintf(page + len, "%4s ", bat);
}
+ len += sprintf(page + len, "\n\t%-17s: ", "debounce");
+ for_each_line(xpd, i) {
+ len += sprintf(page + len, "%4d ", priv->nobattery_debounce[i]);
+ }
+ len += sprintf(page + len, "\nPolarity-Reverse:");
len += sprintf(page + len, "\n\t%-17s: ", "polarity");
for_each_line(xpd, i) {
- len += sprintf(page + len, "%3d ", IS_SET(priv->polarity, i));
+ char *polname;
+
+ if(priv->polarity[i] == POL_POSITIVE)
+ polname = "+";
+ else if(priv->polarity[i] == POL_NEGATIVE)
+ polname = "-";
+ else
+ polname = ".";
+ len += sprintf(page + len, "%4s ", polname);
+ }
+ len += sprintf(page + len, "\n\t%-17s: ", "debounce");
+ for_each_line(xpd, i) {
+ len += sprintf(page + len, "%4d ", priv->polarity_debounce[i]);
+ }
+ len += sprintf(page + len, "\nPower-Denial:");
+ len += sprintf(page + len, "\n\t%-17s: ", "maybe");
+ for_each_line(xpd, i) {
+ len += sprintf(page + len, "%4d ", IS_SET(priv->maybe_power_denial, i));
+ }
+ len += sprintf(page + len, "\n\t%-17s: ", "debounce");
+ for_each_line(xpd, i) {
+ len += sprintf(page + len, "%4d ", priv->power_denial_debounce[i]);
}
- len += sprintf(page + len, "\n\t%-17s: ", "polarity_counter");
+ len += sprintf(page + len, "\n\t%-17s: ", "safezone");
for_each_line(xpd, i) {
- len += sprintf(page + len, "%3d ", priv->polarity_counter[i]);
+ len += sprintf(page + len, "%4d ", priv->power_denial_safezone[i]);
}
- len += sprintf(page + len, "\n\t%-17s: ", "battery_voltage");
+ len += sprintf(page + len, "\n\t%-17s: ", "delay");
for_each_line(xpd, i) {
- len += sprintf(page + len, "%3d ", priv->battery_voltage[i]);
+ len += sprintf(page + len, "%4d ", priv->power_denial_delay[i]);
}
#ifdef WITH_METERING
- len += sprintf(page + len, "\n\t%-17s: ", "metering");
+ len += sprintf(page + len, "\nMetering:");
+ len += sprintf(page + len, "\n\t%-17s: ", "count");
for_each_line(xpd, i) {
- len += sprintf(page + len, "%3d ", priv->metering_count[i]);
+ len += sprintf(page + len, "%4d ", priv->metering_count[i]);
}
#endif
len += sprintf(page + len, "\n");