diff options
author | tzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2008-03-07 00:45:53 +0000 |
---|---|---|
committer | tzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2008-03-07 00:45:53 +0000 |
commit | 1a571d19740d87e24b92ef49a2d38b26256112ae (patch) | |
tree | 8490182ea096b25f977d73c8c7a89b286014b94c /kernel/xpp/card_fxo.c | |
parent | 92d02c5fadec34a75c085e9f95eeab268c3683c8 (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.c | 396 |
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"); |