diff options
Diffstat (limited to 'kernel/xpp/card_fxo.c')
-rw-r--r-- | kernel/xpp/card_fxo.c | 176 |
1 files changed, 124 insertions, 52 deletions
diff --git a/kernel/xpp/card_fxo.c b/kernel/xpp/card_fxo.c index 2090773..bddfa0c 100644 --- a/kernel/xpp/card_fxo.c +++ b/kernel/xpp/card_fxo.c @@ -40,6 +40,8 @@ static DEF_PARM(uint, poll_metering_interval, 500, 0644, "Poll metering interval #endif static DEF_PARM(int, ring_debounce, 50, 0644, "Number of ticks to debounce a false RING indication"); static DEF_PARM(int, caller_id_style, 0, 0444, "Caller-Id detection style: 0 - [BELL], 1 - [ETSI_FSK], 2 - [ETSI_DTMF]"); +static DEF_PARM(int, power_denial_safezone, 650, 0644, "msec after offhook to ignore power-denial ( (0 - disable power-denial)"); +static DEF_PARM(int, power_denial_minlen, 80, 0644, "Minimal detected power-denial length (msec) (0 - disable power-denial)"); /* Backward compatibility plug */ #ifndef ZT_GET_PARAMS_V1 @@ -77,8 +79,6 @@ enum fxo_leds { #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 */ @@ -144,10 +144,8 @@ struct FXO_priv_data { enum polarity_state polarity[CHANNELS_PERXPD]; ushort polarity_debounce[CHANNELS_PERXPD]; enum power_state power[CHANNELS_PERXPD]; - xpp_line_t maybe_power_denial; - ushort power_denial_debounce[CHANNELS_PERXPD]; ushort power_denial_delay[CHANNELS_PERXPD]; - ushort power_denial_minimum[CHANNELS_PERXPD]; + ushort power_denial_length[CHANNELS_PERXPD]; ushort power_denial_safezone[CHANNELS_PERXPD]; xpp_line_t cidfound; /* 0 - OFF, 1 - ON */ unsigned int cidtimer[CHANNELS_PERXPD]; @@ -201,9 +199,8 @@ 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); + power_change(xpd, pos, POWER_UNKNOWN); } static const int led_register_mask[] = { BIT(7), BIT(6), BIT(5) }; @@ -364,9 +361,12 @@ static int do_sethook(xpd_t *xpd, int pos, bool to_offhook) 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; - if(!to_offhook) - power_change(xpd, pos, POWER_UNKNOWN); + if(to_offhook) { + priv->power_denial_safezone[pos] = power_denial_safezone; + } else { + priv->power_denial_length[pos] = 0; + priv->power_denial_safezone[pos] = 0; + } priv->cidtimer[pos] = xpd->timer_count; spin_unlock_irqrestore(&xpd->lock, flags); return ret; @@ -443,13 +443,11 @@ static xpd_t *FXO_card_new(xbus_t *xbus, int unit, int subunit, const xproto_tab channels = min(2, CHANNELS_PERXPD); else channels = min(8, CHANNELS_PERXPD); - xpd = xpd_alloc(sizeof(struct FXO_priv_data), proto_table, channels); + xpd = xpd_alloc(xbus, unit, subunit, subtype, subunits, sizeof(struct FXO_priv_data), proto_table, channels); if(!xpd) return NULL; xpd->direction = TO_PSTN; xpd->type_name = "FXO"; - if(xpd_common_init(xbus, xpd, unit, subunit, subtype, subunits) < 0) - goto err; if(fxo_proc_create(xbus, xpd) < 0) goto err; return xpd; @@ -574,7 +572,6 @@ static int FXO_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig) /* XXX Enable hooksig for FXO XXX */ switch(txsig) { case ZT_TXSIG_START: - break; case ZT_TXSIG_OFFHOOK: ret = do_sethook(xpd, pos, 1); break; @@ -666,48 +663,51 @@ static void handle_fxo_power_denial(xpd_t *xpd) struct FXO_priv_data *priv; int i; + if(!power_denial_safezone) + return; /* Ignore power denials */ priv = xpd->priv; for_each_line(xpd, i) { - if(priv->power_denial_minimum[i] > 0) { - priv->power_denial_minimum[i]--; - if(priv->power_denial_minimum[i] <= 0) { - /* - * 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. - */ - LINE_DBG(SIGNAL, xpd, i, "Possible Power Denial Hangup\n"); - priv->power_denial_debounce[i] = 0; - BIT_SET(priv->maybe_power_denial, i); - } + if(xpd->ringing[i] || !IS_OFFHOOK(xpd, i)) { + priv->power_denial_delay[i] = 0; + continue; } if(priv->power_denial_safezone[i] > 0) { - if(--priv->power_denial_safezone[i]) { + if(--priv->power_denial_safezone[i] == 0) { /* * Poll current, previous answers are meaningless */ DAA_DIRECT_REQUEST(xpd->xbus, xpd, i, DAA_READ, DAA_REG_CURRENT, 0); } + continue; } - if(IS_SET(priv->maybe_power_denial, i) && !xpd->ringing[i] && IS_OFFHOOK(xpd, i)) { + if(priv->power_denial_length[i] > 0) { + priv->power_denial_length[i]--; + if(priv->power_denial_length[i] <= 0) { + /* + * 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. + */ + LINE_DBG(SIGNAL, xpd, i, "Possible Power Denial Hangup\n"); + priv->power_denial_delay[i] = POWER_DENIAL_DELAY; + } + continue; + } + if (priv->power_denial_delay[i] > 0) { /* * 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) { + priv->power_denial_delay[i]--; + if (priv->power_denial_delay[i] <= 0) { LINE_DBG(SIGNAL, xpd, i, "Power Denial Hangup\n"); priv->power_denial_delay[i] = 0; - BIT_CLR(priv->maybe_power_denial, i); /* * Let Asterisk decide what to do */ notify_rxsig(xpd, i, ZT_RXSIG_ONHOOK); } - } else { - priv->power_denial_delay[i] = 0; - BIT_CLR(priv->maybe_power_denial, i); } } } @@ -784,7 +784,6 @@ static int FXO_card_tick(xbus_t *xbus, xpd_t *xpd) return 0; } -/* FIXME: based on data from from wctdm.h */ #include <wctdm.h> /* * The first register is the ACIM, the other are coefficient registers. @@ -799,7 +798,7 @@ static int FXO_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a unsigned char echotune_data[ARRAY_SIZE(echotune_regs)]; BUG_ON(!xpd); - if(!TRANSPORT_RUNNING(xpd->xbus)) + if(!XBUS_IS(xpd->xbus, READY)) return -ENODEV; switch (cmd) { case WCTDM_SET_ECHOTUNE: @@ -921,8 +920,7 @@ static void update_battery_voltage(xpd_t *xpd, byte data_low, xportno_t portno) if(milliseconds > BAT_DEBOUNCE) { LINE_DBG(SIGNAL, xpd, portno, "BATTERY OFF voltage=%d\n", volts); priv->battery[portno] = BATTERY_OFF; - if(SPAN_REGISTERED(xpd)) - zap_report_battery(xpd, portno); + zap_report_battery(xpd, portno); /* What's the polarity ? */ priv->polarity[portno] = POL_UNKNOWN; priv->polarity_debounce[portno] = 0; @@ -940,8 +938,7 @@ static void update_battery_voltage(xpd_t *xpd, byte data_low, xportno_t portno) if(priv->battery[portno] != BATTERY_ON) { LINE_DBG(SIGNAL, xpd, portno, "BATTERY ON voltage=%d\n", volts); priv->battery[portno] = BATTERY_ON; - if(SPAN_REGISTERED(xpd)) - zap_report_battery(xpd, portno); + zap_report_battery(xpd, portno); } } #if 0 @@ -1039,20 +1036,19 @@ static void update_battery_current(xpd_t *xpd, byte data_low, xportno_t portno) if(data_low < POWER_DENIAL_CURRENT) { if(priv->power[portno] == POWER_ON) { power_change(xpd, portno, POWER_OFF); - priv->power_denial_minimum[portno] = POWER_DENIAL_TIME; + priv->power_denial_length[portno] = power_denial_minlen; } } else { if(priv->power[portno] != POWER_ON) { power_change(xpd, portno, POWER_ON); - priv->power_denial_minimum[portno] = 0; + priv->power_denial_length[portno] = 0; /* We are now OFFHOOK */ hookstate_changed(xpd, portno, 1); } } return; ignore_it: - BIT_CLR(priv->maybe_power_denial, portno); - priv->power_denial_debounce[portno] = 0; + priv->power_denial_delay[portno] = 0; } #ifdef WITH_METERING @@ -1256,14 +1252,6 @@ static int proc_fxo_info_read(char *page, char **start, off_t off, int count, in curr = "."; len += sprintf(page + len, "%4s ", curr); } - 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: ", "safezone"); for_each_line(xpd, i) { len += sprintf(page + len, "%4d ", priv->power_denial_safezone[i]); @@ -1327,12 +1315,95 @@ static int proc_xpd_metering_read(char *page, char **start, off_t off, int count } #endif +static DEVICE_ATTR_READER(fxo_battery_show, dev, buf) +{ + xpd_t *xpd; + struct FXO_priv_data *priv; + unsigned long flags; + int len = 0; + int i; + + BUG_ON(!dev); + xpd = dev_to_xpd(dev); + if(!xpd) + return -ENODEV; + priv = xpd->priv; + BUG_ON(!priv); + spin_lock_irqsave(&xpd->lock, flags); + 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(buf + len, "%c ", bat); + } + len += sprintf(buf + len, "\n"); + spin_unlock_irqrestore(&xpd->lock, flags); + return len; +} + +static DEVICE_ATTR(fxo_battery, S_IRUGO, fxo_battery_show, NULL); + + +static int fxo_xpd_probe(struct device *dev) +{ + xpd_t *xpd; + int ret; + + xpd = dev_to_xpd(dev); + /* Is it our device? */ + if(xpd->type != XPD_TYPE_FXO) { + XPD_ERR(xpd, "drop suggestion for %s (%d)\n", + dev->bus_id, xpd->type); + return -EINVAL; + } + XPD_DBG(DEVICES, xpd, "SYSFS\n"); + ret = device_create_file(dev, &dev_attr_fxo_battery); + if(ret) { + XPD_ERR(xpd, "%s: device_create_file(fxo_battery) failed: %d\n", __FUNCTION__, ret); + goto fail_fxo_battery; + } + return 0; +fail_fxo_battery: + return ret; +} + +static int fxo_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_fxo_battery); + return 0; +} + +static struct xpd_driver fxo_driver = { + .type = XPD_TYPE_FXO, + .driver = { + .name = "fxo", +#ifndef OLD_HOTPLUG_SUPPORT + .owner = THIS_MODULE, +#endif + .probe = fxo_xpd_probe, + .remove = fxo_xpd_remove + } +}; + static int __init card_fxo_startup(void) { + int ret; + if(ring_debounce <= 0) { ERR("ring_debounce=%d. Must be positive number of ticks\n", ring_debounce); return -EINVAL; } + if((ret = xpd_driver_register(&fxo_driver.driver)) < 0) + return ret; INFO("revision %s\n", XPP_VERSION); #ifdef WITH_METERING INFO("FEATURE: WITH METERING Detection\n"); @@ -1346,6 +1417,7 @@ static int __init card_fxo_startup(void) static void __exit card_fxo_cleanup(void) { xproto_unregister(&PROTO_TABLE(FXO)); + xpd_driver_unregister(&fxo_driver.driver); } MODULE_DESCRIPTION("XPP FXO Card Driver"); |