summaryrefslogtreecommitdiff
path: root/xpp/card_fxo.c
diff options
context:
space:
mode:
Diffstat (limited to 'xpp/card_fxo.c')
-rw-r--r--xpp/card_fxo.c119
1 files changed, 74 insertions, 45 deletions
diff --git a/xpp/card_fxo.c b/xpp/card_fxo.c
index 404407f..513cd5b 100644
--- a/xpp/card_fxo.c
+++ b/xpp/card_fxo.c
@@ -34,7 +34,8 @@
static const char rcsid[] = "$Id$";
DEF_PARM(int, print_dbg, 0, 0644, "Print DBG statements");
-DEF_PARM(uint, poll_battery_interval, 40, 0644, "Poll battery interval in milliseconds (0 - disable)");
+DEF_PARM(uint, poll_battery_interval, 500, 0644, "Poll battery interval in milliseconds (0 - disable)");
+DEF_PARM(uint, poll_power_denial_interval, 40, 0644, "Power denial detection poll interval in milliseconds (0 - disable)");
#ifdef WITH_METERING
DEF_PARM(uint, poll_metering_interval, 500, 0644, "Poll metering interval in milliseconds (0 - disable)");
#endif
@@ -54,8 +55,10 @@ enum fxo_leds {
#define NUM_LEDS 1
#define DELAY_UNTIL_DIALTONE 3000
+#define POLREV_START 3 /* time after offhook to ignore polarity reversals (in ticks) */
+#define POLREV_THRESHOLD 1000 /* minimum duration for polarity reversal detection (in ticks) */
#define BAT_THRESHOLD 3
-#define BAT_DEBOUNCE 3 /* compensate for battery voltage fluctuation (in poll_battery_interval's) */
+#define BAT_DEBOUNCE 1000 /* compensate for battery voltage fluctuation (in ticks) */
/* Shortcuts */
#define DAA_WRITE 1
@@ -87,9 +90,10 @@ static int handle_register_command(xpd_t *xpd, char *cmdline);
#define PROC_METERING_FNAME "metering_read"
#endif
-#define DAA_RING_REGISTER 0x05
-#define DAA_METERING_REGISTER 0x11 /* 17 */
-#define DAA_CURRENT_REGISTER 0x1C /* 28 */
+#define DAA_REG_RING 0x05
+#define DAA_REG_METERING 0x11 /* 17 */
+#define DAA_REG_CURRENT 0x1C /* 28 */
+#define DAA_REG_VBAT 0x1D /* 29 */
#define POWER_DENIAL_CURRENT 3
#define POWER_DENIAL_TIME 80 /* ticks */
@@ -211,7 +215,7 @@ static void update_zap_ring(xpd_t *xpd, int pos, bool on)
BIT_SET(xpd->cid_on, pos);
rxsig = ZT_RXSIG_OFFHOOK;
}
- pcm_recompute(xpd, xpd->offhook | xpd->cid_on);
+ pcm_recompute(xpd, 0);
/*
* We should not spinlock before calling zt_hooksig() as
* it may call back into our xpp_hooksig() and cause
@@ -228,6 +232,11 @@ static void mark_ring(xpd_t *xpd, lineno_t pos, bool on, bool update_zap)
priv = xpd->priv;
BUG_ON(!priv);
atomic_set(&priv->ring_debounce[pos], 0); /* Stop debouncing */
+ /*
+ * We don't want to check battery during ringing
+ * due to voltage fluctuations.
+ */
+ priv->battery_debounce[pos] = 0;
if(on && !xpd->ringing[pos]) {
LINE_DBG(SIGNAL, xpd, pos, "START\n");
xpd->ringing[pos] = 1;
@@ -268,7 +277,7 @@ static int do_sethook(xpd_t *xpd, int pos, bool to_offhook)
MARK_ON(priv, pos, LED_GREEN);
else
MARK_OFF(priv, pos, LED_GREEN);
- ret = DAA_DIRECT_REQUEST(xbus, xpd, pos, DAA_WRITE, DAA_RING_REGISTER, value);
+ ret = DAA_DIRECT_REQUEST(xbus, xpd, pos, DAA_WRITE, DAA_REG_RING, value);
if(to_offhook) {
BIT_SET(xpd->offhook, pos);
priv->offhook_timestamp[pos] = priv->poll_counter;
@@ -279,7 +288,7 @@ static int do_sethook(xpd_t *xpd, int pos, bool to_offhook)
#ifdef WITH_METERING
priv->metering_count[pos] = 0;
priv->metering_tone_state = 0L;
- DAA_DIRECT_REQUEST(xbus, xpd, pos, DAA_WRITE, DAA_METERING_REGISTER, 0x2D);
+ DAA_DIRECT_REQUEST(xbus, xpd, pos, DAA_WRITE, DAA_REG_METERING, 0x2D);
#endif
spin_unlock_irqrestore(&xpd->lock, flags);
return ret;
@@ -393,7 +402,7 @@ static int FXO_card_init(xbus_t *xbus, xpd_t *xpd)
do_led(xpd, i, LED_GREEN, 0);
msleep(50);
}
- pcm_recompute(xpd, xpd->offhook | xpd->cid_on);
+ pcm_recompute(xpd, 0);
return 0;
err:
clean_proc(xbus, xpd);
@@ -485,7 +494,7 @@ static int FXO_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig)
txsig2str(txsig), txsig);
return -EINVAL;
}
- pcm_recompute(xpd, xpd->offhook | xpd->cid_on);
+ pcm_recompute(xpd, 0);
return 0;
}
@@ -511,7 +520,7 @@ static void poll_battery(xbus_t *xbus, xpd_t *xpd)
int i;
for_each_line(xpd, i) {
- DAA_DIRECT_REQUEST(xbus, xpd, i, DAA_READ, DAA_VBAT_REGISTER, 0);
+ DAA_DIRECT_REQUEST(xbus, xpd, i, DAA_READ, DAA_REG_VBAT, 0);
}
}
@@ -521,7 +530,7 @@ static void poll_current(xbus_t *xbus, xpd_t *xpd)
for_each_line(xpd, i) {
if (IS_SET(xpd->offhook, i))
- DAA_DIRECT_REQUEST(xbus, xpd, i, DAA_READ, DAA_CURRENT_REGISTER, 0);
+ DAA_DIRECT_REQUEST(xbus, xpd, i, DAA_READ, DAA_REG_CURRENT, 0);
}
}
@@ -532,7 +541,7 @@ static void poll_metering(xbus_t *xbus, xpd_t *xpd)
for_each_line(xpd, i) {
if (IS_SET(xpd->offhook, i))
- DAA_DIRECT_REQUEST(xbus, xpd, i, DAA_READ, DAA_METERING_REGISTER, 0);
+ DAA_DIRECT_REQUEST(xbus, xpd, i, DAA_READ, DAA_REG_METERING, 0);
}
}
#endif
@@ -563,10 +572,10 @@ static int FXO_card_tick(xbus_t *xbus, xpd_t *xpd)
BUG_ON(!xpd);
priv = xpd->priv;
BUG_ON(!priv);
- if(poll_battery_interval != 0 && (priv->poll_counter % poll_battery_interval) == 0) {
+ if(poll_battery_interval != 0 && (priv->poll_counter % poll_battery_interval) == 0)
poll_battery(xbus, xpd);
+ if(poll_power_denial_interval != 0 && (priv->poll_counter % poll_power_denial_interval) == 0)
poll_current(xbus, xpd);
- }
#ifdef WITH_METERING
if(poll_metering_interval != 0 && (priv->poll_counter % poll_metering_interval) == 0)
poll_metering(xbus, xpd);
@@ -592,7 +601,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(!xpd->xbus->hardware_exists)
+ if(!TRANSPORT_RUNNING(xpd->xbus))
return -ENODEV;
switch (cmd) {
case WCTDM_SET_ECHOTUNE:
@@ -614,8 +623,16 @@ static int FXO_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a
XPD_DBG(GENERAL, xpd, "-- Set echo registers successfully\n");
break;
+ case ZT_TONEDETECT:
+ /*
+ * Asterisk call all span types with this (FXS specific)
+ * call. Silently ignore it.
+ */
+ LINE_DBG(GENERAL, xpd, pos,
+ "ZT_TONEDETECT (FXO: NOTIMPLEMENTED)\n");
+ return -ENOTTY;
default:
- LINE_DBG(GENERAL, xpd, pos, "Unknown command 0x%X.\n", cmd);
+ report_bad_ioctl(THIS_MODULE->name, xpd, pos, cmd);
return -ENOTTY;
}
return 0;
@@ -704,11 +721,17 @@ static void update_battery_status(xpd_t *xpd, byte data_low, lineno_t chipsel)
/*
* Check for battery voltage fluctuations
*/
- if(IS_SET(priv->battery, chipsel) && priv->battery_debounce[chipsel]++ > BAT_DEBOUNCE) {
- LINE_DBG(SIGNAL, xpd, chipsel, "BATTERY OFF voltage=%d\n", bat);
- BIT_CLR(priv->battery, chipsel);
- if(SPAN_REGISTERED(xpd))
- zt_qevent_lock(&xpd->chans[chipsel], ZT_EVENT_ALARM);
+ if(IS_SET(priv->battery, chipsel)) {
+ int milliseconds;
+
+ milliseconds = priv->battery_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);
+ if(SPAN_REGISTERED(xpd))
+ zt_qevent_lock(&xpd->chans[chipsel], ZT_EVENT_ALARM);
+ }
}
} else {
@@ -724,23 +747,24 @@ static void update_battery_status(xpd_t *xpd, byte data_low, lineno_t chipsel)
* Handle reverse polarity
*/
if (IS_SET(xpd->offhook, chipsel)) { /* Learn the current polarity */
- if (priv->poll_counter - priv->offhook_timestamp[chipsel] < 3) {
+ if (priv->poll_counter - priv->offhook_timestamp[chipsel] < POLREV_START) {
priv->polarity_counter[chipsel] = 0;
if (pol)
BIT_SET(priv->polarity, chipsel);
else
BIT_CLR(priv->polarity, chipsel);
- }
- else if (IS_SET(priv->polarity, chipsel) != pol) { /* Polarity has reversed */
- priv->polarity_counter[chipsel]++;
- if (priv->polarity_counter[chipsel] >= 2) {
+ } else if (IS_SET(priv->polarity, chipsel) != pol) { /* Polarity has reversed */
+ int milliseconds;
+
+ milliseconds = priv->polarity_counter[chipsel]++ * poll_battery_interval;
+ if (milliseconds >= POLREV_THRESHOLD) {
if (pol)
BIT_SET(priv->polarity, chipsel);
else
BIT_CLR(priv->polarity, chipsel);
priv->polarity_counter[chipsel] = 0;
/* Inform Zaptel */
- LINE_DBG(GENERAL, xpd, chipsel, "Send ZT_EVENT_POLARITY\n");
+ LINE_DBG(SIGNAL, xpd, chipsel, "Send ZT_EVENT_POLARITY\n");
zt_qevent_lock(&xpd->chans[chipsel], ZT_EVENT_POLARITY);
#if 0
/*
@@ -752,7 +776,7 @@ static void update_battery_status(xpd_t *xpd, byte data_low, lineno_t chipsel)
*/
do_sethook(xpd, chipsel, 0);
update_line_status(xpd, chipsel, 0);
- pcm_recompute(xpd, xpd->offhook | xpd->cid_on);
+ pcm_recompute(xpd, 0);
#endif
}
}
@@ -773,7 +797,7 @@ static void update_power_denial(xpd_t *xpd, byte data_low, lineno_t chipsel)
priv->current_counter[chipsel] = 0;
do_sethook(xpd, chipsel, 0);
update_line_status(xpd, chipsel, 0);
- pcm_recompute(xpd, xpd->offhook | xpd->cid_on);
+ pcm_recompute(xpd, 0);
}
} else
priv->current_counter[chipsel] = 0;
@@ -803,7 +827,7 @@ static void update_metering_state(xpd_t *xpd, byte data_low, lineno_t chipsel)
if(metering_tone) {
/* Clear the BTD bit */
data_low &= ~BTD_BIT;
- DAA_DIRECT_REQUEST(xpd->xbus, xpd, chipsel, DAA_WRITE, DAA_METERING_REGISTER, data_low);
+ DAA_DIRECT_REQUEST(xpd->xbus, xpd, chipsel, DAA_WRITE, DAA_REG_METERING, data_low);
}
}
#endif
@@ -817,14 +841,14 @@ static int FXO_card_register_reply(xbus_t *xbus, xpd_t *xpd, reg_cmd_t *info)
BUG_ON(!priv);
chipsel = REG_FIELD(info, chipsel);
switch(REG_FIELD(info, regnum)) {
- case DAA_VBAT_REGISTER:
+ case DAA_REG_VBAT:
update_battery_status(xpd, REG_FIELD(info, data_low), chipsel);
break;
- case DAA_CURRENT_REGISTER:
+ case DAA_REG_CURRENT:
update_power_denial(xpd, REG_FIELD(info, data_low), chipsel);
break;
#ifdef WITH_METERING
- case DAA_METERING_REGISTER:
+ case DAA_REG_METERING:
update_metering_state(xpd, REG_FIELD(info, data_low), chipsel);
break;
#endif
@@ -927,6 +951,10 @@ static int proc_fxo_info_read(char *page, char **start, off_t off, int count, in
for_each_line(xpd, i) {
len += sprintf(page + len, "%2d ", IS_SET(priv->polarity, i));
}
+ len += sprintf(page + len, "\n\t%-17s: ", "polarity_counter");
+ for_each_line(xpd, i) {
+ len += sprintf(page + len, "%2d ", priv->polarity_counter[i]);
+ }
#ifdef WITH_METERING
len += sprintf(page + len, "\n\t%-17s: ", "metering");
for_each_line(xpd, i) {
@@ -976,7 +1004,7 @@ static int handle_register_command(xpd_t *xpd, char *cmdline)
char *p;
reg_cmd_t regcmd;
xbus_t *xbus;
- int ret;
+ int ret = -EINVAL;
BUG_ON(!xpd);
xbus = xpd->xbus;
@@ -989,6 +1017,10 @@ static int handle_register_command(xpd_t *xpd, char *cmdline)
if(*p == '\0')
return 0;
+ if(!XBUS_GET(xbus)) {
+ XBUS_DBG(GENERAL, xbus, "Dropped packet. Is shutting down.\n");
+ return -EBUSY;
+ }
elements = sscanf(cmdline, "%d %c%c %x %x",
&chipsel,
&op, &reg_type, &reg_num,
@@ -996,11 +1028,11 @@ static int handle_register_command(xpd_t *xpd, char *cmdline)
XPD_DBG(PROC, xpd, "'%s': %d %c%c %02X %02X\n", cmdline, chipsel, op, reg_type, reg_num, data_low);
if(elements < 4) { // At least: chipsel, op, reg_type, reg_num
ERR("Not enough arguments: (%d args) '%s'\n", elements, cmdline);
- return -EINVAL;
+ goto out;
}
if(!VALID_CHIPSEL(chipsel)) {
ERR("Bad chipsel number: %d\n", chipsel);
- return -EINVAL;
+ goto out;
}
REG_FIELD(&regcmd, chipsel) = chipsel;
REG_FIELD(&regcmd, do_subreg) = 0;
@@ -1013,7 +1045,7 @@ static int handle_register_command(xpd_t *xpd, char *cmdline)
break;
default:
ERR("Unkown operation type '%c'\n", op);
- return -EINVAL;
+ goto out;
}
switch(reg_type) {
case 'D':
@@ -1022,7 +1054,7 @@ static int handle_register_command(xpd_t *xpd, char *cmdline)
break;
default:
ERR("Unkown register type '%c'\n", reg_type);
- return -EINVAL;
+ goto out;
}
if(
(op == 'W' && reg_type == 'D' && elements != 5) ||
@@ -1031,21 +1063,18 @@ static int handle_register_command(xpd_t *xpd, char *cmdline)
ERR("%s: '%s' (%d elements): %d %c%c %02X %02X\n", __FUNCTION__,
cmdline, elements,
chipsel, op, reg_type, reg_num, data_low);
- return -EINVAL;
+ goto out;
}
regcmd.bytes = sizeof(regcmd) - 1;
REG_FIELD(&regcmd, data_low) = data_low;
REG_FIELD(&regcmd, data_high) = 0;
REG_FIELD(&regcmd, read_request) = writing;
- if(!down_read_trylock(&xbus->in_use)) {
- XBUS_DBG(GENERAL, xbus, "Dropped packet. Is in_use\n");
- return -EBUSY;
- }
xpd->requested_reply = regcmd;
if(print_dbg)
dump_reg_cmd("FXO", &regcmd, 1);
ret = DAA_DIRECT_REQUEST(xpd->xbus, xpd, REG_FIELD(&regcmd, chipsel), writing, REG_FIELD(&regcmd, regnum), REG_FIELD(&regcmd, data_low));
- up_read(&xbus->in_use);
+out:
+ XBUS_PUT(xbus);
return ret;
}