summaryrefslogtreecommitdiff
path: root/kernel/xpp/card_fxo.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/xpp/card_fxo.c')
-rw-r--r--kernel/xpp/card_fxo.c569
1 files changed, 217 insertions, 352 deletions
diff --git a/kernel/xpp/card_fxo.c b/kernel/xpp/card_fxo.c
index 34fc41c..0bb30ff 100644
--- a/kernel/xpp/card_fxo.c
+++ b/kernel/xpp/card_fxo.c
@@ -33,14 +33,13 @@
static const char rcsid[] = "$Id$";
-DEF_PARM(int, print_dbg, 0, 0644, "Print DBG statements");
-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)");
+static DEF_PARM(int, debug, 0, 0644, "Print DBG statements");
+static DEF_PARM(uint, poll_battery_interval, 500, 0644, "Poll battery interval in milliseconds (0 - disable)");
#ifdef WITH_METERING
-DEF_PARM(uint, poll_metering_interval, 500, 0644, "Poll metering interval in milliseconds (0 - disable)");
+static 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]");
+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 - [BT], 2 - [PASS]");
enum cid_style {
CID_STYLE_BELL = 0, /* E.g: US (Bellcore) */
@@ -57,9 +56,10 @@ enum cid_style {
enum fxo_leds {
LED_GREEN,
+ LED_RED,
};
-#define NUM_LEDS 1
+#define NUM_LEDS 2
#define DELAY_UNTIL_DIALTONE 3000
/*
@@ -78,16 +78,12 @@ enum fxo_leds {
/* Shortcuts */
#define DAA_WRITE 1
#define DAA_READ 0
-#define DAA_DIRECT_REQUEST(xbus,xpd,chipsel,writing,reg,dL) \
- xpp_register_request((xbus), (xpd), (chipsel), (writing), 0, (reg), 0, (dL), 0)
-
-#define VALID_CHIPSEL(x) (((chipsel) >= 0 && (chipsel) <= 7) || (chipsel) == ALL_CHANS)
+#define DAA_DIRECT_REQUEST(xbus,xpd,port,writing,reg,dL) \
+ xpp_register_request((xbus), (xpd), (port), (writing), (reg), 0, 0, (dL), 0, 0, 0)
/*---------------- FXO Protocol Commands ----------------------------------*/
static /* 0x0F */ DECLARE_CMD(FXO, XPD_STATE, bool on);
-static /* 0x0F */ DECLARE_CMD(FXO, RING, lineno_t chan, bool on);
-static /* 0x0F */ DECLARE_CMD(FXO, RELAY_OUT, byte which, bool on);
static bool fxo_packet_is_valid(xpacket_t *pack);
static void fxo_packet_dump(const char *msg, xpacket_t *pack);
@@ -95,9 +91,6 @@ static int proc_fxo_info_read(char *page, char **start, off_t off, int count, in
#ifdef WITH_METERING
static int proc_xpd_metering_read(char *page, char **start, off_t off, int count, int *eof, void *data);
#endif
-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"
@@ -106,7 +99,10 @@ static void zap_report_battery(xpd_t *xpd, lineno_t chan);
#define PROC_METERING_FNAME "metering_read"
#endif
-#define DAA_REG_RING 0x05
+#define REG_DAA_CONTROL1 0x05 /* 5 - DAA Control 1 */
+#define REG_DAA_CONTROL1_OH BIT(0) /* Off-Hook. */
+#define REG_DAA_CONTROL1_ONHM BIT(3) /* On-Hook Line Monitor */
+
#define DAA_REG_METERING 0x11 /* 17 */
#define DAA_REG_CURRENT 0x1C /* 28 */
#define DAA_REG_VBAT 0x1D /* 29 */
@@ -123,8 +119,13 @@ enum polarity_state {
POL_NEGATIVE = -1
};
+enum power_state {
+ POWER_UNKNOWN = 0,
+ POWER_ON = 1,
+ POWER_OFF = -1
+};
+
struct FXO_priv_data {
- struct proc_dir_entry *regfile;
#ifdef WITH_METERING
struct proc_dir_entry *meteringfile;
#endif
@@ -136,9 +137,11 @@ struct FXO_priv_data {
ushort nobattery_debounce[CHANNELS_PERXPD];
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_safezone[CHANNELS_PERXPD];
xpp_line_t ledstate[NUM_LEDS]; /* 0 - OFF, 1 - ON */
xpp_line_t ledcontrol[NUM_LEDS]; /* 0 - OFF, 1 - ON */
@@ -174,6 +177,8 @@ static void reset_battery_readings(xpd_t *xpd, lineno_t pos)
BIT_CLR(priv->maybe_power_denial, pos);
}
+static const int led_register_mask[] = { BIT(7), BIT(6), BIT(5) };
+
/*
* LED control is done via DAA register 0x20
*/
@@ -182,6 +187,7 @@ static int do_led(xpd_t *xpd, lineno_t chan, byte which, bool on)
int ret = 0;
struct FXO_priv_data *priv;
xbus_t *xbus;
+ byte value;
BUG_ON(!xpd);
xbus = xpd->xbus;
@@ -189,7 +195,7 @@ static int do_led(xpd_t *xpd, lineno_t chan, byte which, bool on)
which = which % NUM_LEDS;
if(IS_SET(xpd->digital_outputs, chan) || IS_SET(xpd->digital_inputs, chan))
goto out;
- if(chan == ALL_CHANS) {
+ if(chan == PORT_BROADCAST) {
priv->ledstate[which] = (on) ? ~0 : 0;
} else {
if(on) {
@@ -198,8 +204,12 @@ static int do_led(xpd_t *xpd, lineno_t chan, byte which, bool on)
BIT_CLR(priv->ledstate[which], chan);
}
}
+ value = 0;
+ value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_register_mask[which]);
+ value |= (on) ? BIT(0) : 0;
+ value |= (on) ? BIT(1) : 0;
LINE_DBG(LEDS, xpd, chan, "LED: which=%d -- %s\n", which, (on) ? "on" : "off");
- ret = DAA_DIRECT_REQUEST(xbus, xpd, chan, DAA_WRITE, 0x20, on);
+ ret = DAA_DIRECT_REQUEST(xbus, xpd, chan, DAA_WRITE, 0x20, value);
out:
return ret;
}
@@ -208,7 +218,8 @@ static void handle_fxo_leds(xpd_t *xpd)
{
int i;
unsigned long flags;
- const enum fxo_leds color = LED_GREEN;
+ const enum fxo_leds colors[] = { LED_GREEN, LED_RED };
+ enum fxo_leds color;
unsigned int timer_count;
struct FXO_priv_data *priv;
@@ -216,27 +227,29 @@ static void handle_fxo_leds(xpd_t *xpd)
spin_lock_irqsave(&xpd->lock, flags);
priv = xpd->priv;
timer_count = xpd->timer_count;
- for_each_line(xpd, i) {
- if(IS_SET(xpd->digital_outputs, i) || IS_SET(xpd->digital_inputs, i))
- continue;
- if(xpd->blink_mode || IS_BLINKING(priv,i,color)) {
- int mod_value = LED_COUNTER(priv, i, color);
-
- if(!mod_value)
- mod_value = DEFAULT_LED_PERIOD; /* safety value */
- // led state is toggled
- if((timer_count % mod_value) == 0) {
- LINE_DBG(LEDS, xpd, i, "ledstate=%s\n", (IS_SET(priv->ledstate[color], i))?"ON":"OFF");
- if(!IS_SET(priv->ledstate[color], i)) {
- do_led(xpd, i, color, 1);
- } else {
- do_led(xpd, i, color, 0);
+ for(color = 0; color < ARRAY_SIZE(colors); color++) {
+ for_each_line(xpd, i) {
+ if(IS_SET(xpd->digital_outputs, i) || IS_SET(xpd->digital_inputs, i))
+ continue;
+ if((xpd->blink_mode & BIT(i)) || IS_BLINKING(priv, i, color)) { // Blinking
+ int mod_value = LED_COUNTER(priv, i, color);
+
+ if(!mod_value)
+ mod_value = DEFAULT_LED_PERIOD; /* safety value */
+ // led state is toggled
+ if((timer_count % mod_value) == 0) {
+ LINE_DBG(LEDS, xpd, i, "ledstate=%s\n", (IS_SET(priv->ledstate[color], i))?"ON":"OFF");
+ if(!IS_SET(priv->ledstate[color], i)) {
+ do_led(xpd, i, color, 1);
+ } else {
+ do_led(xpd, i, color, 0);
+ }
}
+ } else if(IS_SET(priv->ledcontrol[color], i) && !IS_SET(priv->ledstate[color], i)) {
+ do_led(xpd, i, color, 1);
+ } else if(!IS_SET(priv->ledcontrol[color], i) && IS_SET(priv->ledstate[color], i)) {
+ do_led(xpd, i, color, 0);
}
- } else if(IS_SET(priv->ledcontrol[color], i) && !IS_SET(priv->ledstate[color], i)) {
- do_led(xpd, i, color, 1);
- } else if(!IS_SET(priv->ledcontrol[color], i) && IS_SET(priv->ledstate[color], i)) {
- do_led(xpd, i, color, 0);
}
}
spin_unlock_irqrestore(&xpd->lock, flags);
@@ -317,13 +330,15 @@ static int do_sethook(xpd_t *xpd, int pos, bool to_offhook)
}
spin_lock_irqsave(&xpd->lock, flags);
mark_ring(xpd, pos, 0, 0); // No more rings
- value = (to_offhook) ? 0x09 : 0x08; /* Bit 3 is for CID */
+ value = REG_DAA_CONTROL1_ONHM; /* Bit 3 is for CID */
+ if(to_offhook)
+ value |= REG_DAA_CONTROL1_OH;
LINE_DBG(SIGNAL, xpd, pos, "SETHOOK: value=0x%02X %s\n", value, (to_offhook)?"OFFHOOK":"ONHOOK");
if(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_REG_RING, value);
+ ret = DAA_DIRECT_REQUEST(xbus, xpd, pos, DAA_WRITE, REG_DAA_CONTROL1, value);
if(to_offhook) {
BIT_SET(xpd->offhook, pos);
} else {
@@ -340,31 +355,15 @@ static int do_sethook(xpd_t *xpd, int pos, bool to_offhook)
#endif
reset_battery_readings(xpd, pos); /* unstable during hook changes */
priv->power_denial_safezone[pos] = (to_offhook) ? POWER_DENIAL_SAFEZONE : 0;
+ if(!to_offhook)
+ priv->power[pos] = POWER_UNKNOWN;
spin_unlock_irqrestore(&xpd->lock, flags);
return ret;
}
/*---------------- FXO: Methods -------------------------------------------*/
-static xpd_t *FXO_card_new(xbus_t *xbus, int unit, int subunit, const xproto_table_t *proto_table, byte subtype, byte revision)
-{
- xpd_t *xpd = NULL;
- int channels;
-
- if(subtype == 2)
- channels = min(2, CHANNELS_PERXPD);
- else
- channels = min(8, CHANNELS_PERXPD);
- xpd = xpd_alloc(sizeof(struct FXO_priv_data), proto_table, channels);
- if(!xpd)
- return NULL;
- xpd->direction = TO_PSTN;
- xpd->revision = revision;
- xpd->type_name = proto_table->name;
- return xpd;
-}
-
-static void clean_proc(xbus_t *xbus, xpd_t *xpd)
+static void fxo_proc_remove(xbus_t *xbus, xpd_t *xpd)
{
struct FXO_priv_data *priv;
@@ -372,11 +371,6 @@ static void clean_proc(xbus_t *xbus, xpd_t *xpd)
priv = xpd->priv;
XPD_DBG(PROC, xpd, "\n");
#ifdef CONFIG_PROC_FS
- if(priv->regfile) {
- XPD_DBG(PROC, xpd, "Removing xpd DAA file\n");
- remove_proc_entry(PROC_REGISTER_FNAME, xpd->proc_xpd_dir);
- priv->regfile->data = NULL;
- }
#ifdef WITH_METERING
if(priv->meteringfile) {
XPD_DBG(PROC, xpd, "Removing xpd metering tone file\n");
@@ -393,11 +387,9 @@ static void clean_proc(xbus_t *xbus, xpd_t *xpd)
#endif
}
-static int FXO_card_init(xbus_t *xbus, xpd_t *xpd)
+static int fxo_proc_create(xbus_t *xbus, xpd_t *xpd)
{
struct FXO_priv_data *priv;
- int ret = 0;
- int i;
BUG_ON(!xpd);
priv = xpd->priv;
@@ -406,7 +398,6 @@ static int FXO_card_init(xbus_t *xbus, xpd_t *xpd)
priv->fxo_info = create_proc_read_entry(PROC_FXO_INFO_FNAME, 0444, xpd->proc_xpd_dir, proc_fxo_info_read, xpd);
if(!priv->fxo_info) {
XPD_ERR(xpd, "Failed to create proc file '%s'\n", PROC_FXO_INFO_FNAME);
- ret = -ENOENT;
goto err;
}
priv->fxo_info->owner = THIS_MODULE;
@@ -416,31 +407,59 @@ static int FXO_card_init(xbus_t *xbus, xpd_t *xpd)
proc_xpd_metering_read, xpd);
if(!priv->meteringfile) {
XPD_ERR(xpd, "Failed to create proc file '%s'\n", PROC_METERING_FNAME);
- ret = -ENOENT;
goto err;
}
priv->meteringfile->owner = THIS_MODULE;
#endif
- XPD_DBG(PROC, xpd, "Creating DAAs file\n");
- priv->regfile = create_proc_entry(PROC_REGISTER_FNAME, 0644, xpd->proc_xpd_dir);
- if(!priv->regfile) {
- XPD_ERR(xpd, "Failed to create proc file '%s'\n", PROC_REGISTER_FNAME);
- ret = -ENOENT;
- goto err;
- }
- priv->regfile->owner = THIS_MODULE;
- priv->regfile->write_proc = proc_xpd_register_write;
- priv->regfile->read_proc = proc_xpd_register_read;
- priv->regfile->data = xpd;
#endif
- ret = run_initialize_registers(xpd);
- if(ret < 0)
+ return 0;
+err:
+ return -EINVAL;
+}
+
+static xpd_t *FXO_card_new(xbus_t *xbus, int unit, int subunit, const xproto_table_t *proto_table, byte subtype, int subunits, bool to_phone)
+{
+ xpd_t *xpd = NULL;
+ int channels;
+
+ if(to_phone) {
+ XBUS_NOTICE(xbus,
+ "XPD=%d%d: try to instanciate FXO with reverse direction\n",
+ unit, subunit);
+ return NULL;
+ }
+ if(subtype == 2)
+ channels = min(2, CHANNELS_PERXPD);
+ else
+ channels = min(8, CHANNELS_PERXPD);
+ xpd = xpd_alloc(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;
+err:
+ xpd_free(xpd);
+ return NULL;
+}
+
+static int FXO_card_init(xbus_t *xbus, xpd_t *xpd)
+{
+ struct FXO_priv_data *priv;
+ int i;
+
+ BUG_ON(!xpd);
+ priv = xpd->priv;
// 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 */
+ priv->power[i] = POWER_UNKNOWN; /* will be updated on next battery sample */
}
XPD_DBG(GENERAL, xpd, "done\n");
for_each_line(xpd, i) {
@@ -456,10 +475,6 @@ static int FXO_card_init(xbus_t *xbus, xpd_t *xpd)
}
pcm_recompute(xpd, 0);
return 0;
-err:
- clean_proc(xbus, xpd);
- XPD_ERR(xpd, "Failed initializing registers (%d)\n", ret);
- return ret;
}
static int FXO_card_remove(xbus_t *xbus, xpd_t *xpd)
@@ -469,7 +484,7 @@ static int FXO_card_remove(xbus_t *xbus, xpd_t *xpd)
BUG_ON(!xpd);
priv = xpd->priv;
XPD_DBG(GENERAL, xpd, "\n");
- clean_proc(xbus, xpd);
+ fxo_proc_remove(xbus, xpd);
return 0;
}
@@ -501,6 +516,7 @@ static int FXO_card_zaptel_preregistration(xpd_t *xpd, bool on)
for_each_line(xpd, i) {
MARK_ON(priv, i, LED_GREEN);
msleep(4);
+ MARK_ON(priv, i, LED_RED);
}
return 0;
}
@@ -521,7 +537,7 @@ static int FXO_card_zaptel_postregistration(xpd_t *xpd, bool on)
zap_report_battery(xpd, i);
MARK_OFF(priv, i, LED_GREEN);
msleep(2);
- // MARK_OFF(priv, i, LED_RED);
+ MARK_OFF(priv, i, LED_RED);
msleep(2);
}
return 0;
@@ -596,16 +612,6 @@ static void poll_battery(xbus_t *xbus, xpd_t *xpd)
}
}
-static void poll_current(xbus_t *xbus, xpd_t *xpd)
-{
- int i;
-
- for_each_line(xpd, i) {
- if (IS_SET(xpd->offhook, i))
- DAA_DIRECT_REQUEST(xbus, xpd, i, DAA_READ, DAA_REG_CURRENT, 0);
- }
-}
-
#ifdef WITH_METERING
static void poll_metering(xbus_t *xbus, xpd_t *xpd)
{
@@ -644,8 +650,27 @@ static void handle_fxo_power_denial(xpd_t *xpd)
priv = xpd->priv;
for_each_line(xpd, i) {
- if(priv->power_denial_safezone[i] > 0)
- priv->power_denial_safezone[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(priv->power_denial_safezone[i] > 0) {
+ if(--priv->power_denial_safezone[i]) {
+ /*
+ * Poll current, previous answers are meaningless
+ */
+ DAA_DIRECT_REQUEST(xpd->xbus, xpd, i, DAA_READ, DAA_REG_CURRENT, 0);
+ }
+ }
if(IS_SET(priv->maybe_power_denial, i) && !xpd->ringing[i] && IS_SET(xpd->offhook, i)) {
/*
* Ring detection by the firmware takes some time.
@@ -677,8 +702,6 @@ static int FXO_card_tick(xbus_t *xbus, xpd_t *xpd)
BUG_ON(!priv);
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);
@@ -744,7 +767,7 @@ static int FXO_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a
/*---------------- FXO: HOST COMMANDS -------------------------------------*/
-/* 0x0F */ HOSTCMD(FXO, XPD_STATE, bool on)
+static /* 0x0F */ HOSTCMD(FXO, XPD_STATE, bool on)
{
int ret = 0;
struct FXO_priv_data *priv;
@@ -757,19 +780,6 @@ static int FXO_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a
return ret;
}
-/* 0x0F */ HOSTCMD(FXO, RING, lineno_t chan, bool on)
-{
- BUG_ON(!xbus);
- BUG_ON(!xpd);
- LINE_DBG(SIGNAL, xpd, chan, "%s\n", (on) ? "on" : "off");
- return DAA_DIRECT_REQUEST(xbus, xpd, chan, DAA_WRITE, 0x40, (on)?0x04:0x01);
-}
-
-/* 0x0F */ HOSTCMD(FXO, RELAY_OUT, byte which, bool on)
-{
- return -ENOSYS;
-}
-
/*---------------- FXO: Astribank Reply Handlers --------------------------*/
HANDLER_DEF(FXO, SIG_CHANGED)
@@ -820,7 +830,7 @@ 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_voltage(xpd_t *xpd, byte data_low, lineno_t chipsel)
+static void update_battery_voltage(xpd_t *xpd, byte data_low, xportno_t portno)
{
struct FXO_priv_data *priv;
enum polarity_state pol;
@@ -828,24 +838,25 @@ static void update_battery_voltage(xpd_t *xpd, byte data_low, lineno_t chipsel)
priv = xpd->priv;
BUG_ON(!priv);
- priv->battery_voltage[chipsel] = data_low;
- if(xpd->ringing[chipsel])
+ priv->battery_voltage[portno] = data_low;
+ if(xpd->ringing[portno])
goto ignore_reading; /* ring voltage create false alarms */
if(abs((signed char)data_low) < BAT_THRESHOLD) {
/*
* Check for battery voltage fluctuations
*/
- if(priv->battery[chipsel] != BATTERY_OFF) {
+ if(priv->battery[portno] != BATTERY_OFF) {
int milliseconds;
- milliseconds = priv->nobattery_debounce[chipsel]++ *
+ milliseconds = priv->nobattery_debounce[portno]++ *
poll_battery_interval;
if(milliseconds > BAT_DEBOUNCE) {
- LINE_DBG(SIGNAL, xpd, chipsel, "BATTERY OFF voltage=%d\n", data_low);
- priv->battery[chipsel] = BATTERY_OFF;
+ LINE_DBG(SIGNAL, xpd, portno, "BATTERY OFF voltage=%d\n", data_low);
+ priv->battery[portno] = BATTERY_OFF;
if(SPAN_REGISTERED(xpd))
- zap_report_battery(xpd, chipsel);
- priv->polarity[chipsel] = POL_UNKNOWN; /* What's the polarity ? */
+ zap_report_battery(xpd, portno);
+ priv->polarity[portno] = POL_UNKNOWN; /* What's the polarity ? */
+ priv->power[portno] = POWER_UNKNOWN; /* What's the current ? */
/*
* Stop further processing for now
*/
@@ -854,16 +865,25 @@ static void update_battery_voltage(xpd_t *xpd, byte data_low, lineno_t chipsel)
}
} else {
- 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;
+ priv->nobattery_debounce[portno] = 0;
+ if(priv->battery[portno] != BATTERY_ON) {
+ LINE_DBG(SIGNAL, xpd, portno, "BATTERY ON voltage=%d\n", data_low);
+ priv->battery[portno] = BATTERY_ON;
if(SPAN_REGISTERED(xpd))
- zap_report_battery(xpd, chipsel);
+ zap_report_battery(xpd, portno);
}
}
- if(priv->battery[chipsel] != BATTERY_ON) {
- priv->polarity[chipsel] = POL_UNKNOWN; /* What's the polarity ? */
+#if 0
+ /*
+ * Mark FXO ports without battery!
+ */
+ if(priv->battery[portno] != BATTERY_ON)
+ MARK_ON(priv, portno, LED_RED);
+ else
+ MARK_OFF(priv, portno, LED_RED);
+#endif
+ if(priv->battery[portno] != BATTERY_ON) {
+ priv->polarity[portno] = POL_UNKNOWN; /* What's the polarity ? */
return;
}
/*
@@ -875,20 +895,20 @@ static void update_battery_voltage(xpd_t *xpd, byte data_low, lineno_t chipsel)
pol = POL_NEGATIVE;
else
pol = POL_POSITIVE;
- if(priv->polarity[chipsel] == pol) {
+ if(priv->polarity[portno] == pol) {
/*
* Same polarity, reset debounce counter
*/
- priv->polarity_debounce[chipsel] = 0;
+ priv->polarity_debounce[portno] = 0;
return;
}
/*
* Track polarity reversals and debounce spikes.
* Only reversals with long duration count.
*/
- msec = priv->polarity_debounce[chipsel]++ * poll_battery_interval;
+ msec = priv->polarity_debounce[portno]++ * poll_battery_interval;
if (msec >= POLREV_THRESHOLD) {
- priv->polarity_debounce[chipsel] = 0;
+ priv->polarity_debounce[portno] = 0;
if(pol != POL_UNKNOWN) {
char *polname = NULL;
@@ -898,7 +918,7 @@ static void update_battery_voltage(xpd_t *xpd, byte data_low, lineno_t chipsel)
polname = "Negative";
else
BUG();
- LINE_DBG(SIGNAL, xpd, chipsel,
+ LINE_DBG(SIGNAL, xpd, portno,
"Polarity changed to %s\n", polname);
/*
* Inform zaptel/Asterisk:
@@ -907,71 +927,68 @@ static void update_battery_voltage(xpd_t *xpd, byte data_low, lineno_t chipsel)
* but before first ring.
*/
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 */
+ LINE_DBG(SIGNAL, xpd, portno, "Caller-ID PCM: on\n");
+ BIT_SET(xpd->cid_on, portno); /* will be cleared on ring/offhook */
}
if(SPAN_REGISTERED(xpd)) {
- LINE_DBG(SIGNAL, xpd, chipsel,
+ LINE_DBG(SIGNAL, xpd, portno,
"Send ZT_EVENT_POLARITY: %s\n", polname);
- zt_qevent_lock(&xpd->chans[chipsel], ZT_EVENT_POLARITY);
+ zt_qevent_lock(&xpd->chans[portno], ZT_EVENT_POLARITY);
}
}
- priv->polarity[chipsel] = pol;
+ priv->polarity[portno] = pol;
}
return;
ignore_reading:
/*
* Reset debounce counters to prevent false alarms
*/
- reset_battery_readings(xpd, chipsel); /* unstable during hook changes */
+ reset_battery_readings(xpd, portno); /* unstable during hook changes */
}
-static void update_battery_current(xpd_t *xpd, byte data_low, lineno_t chipsel)
+static void update_battery_current(xpd_t *xpd, byte data_low, xportno_t portno)
{
struct FXO_priv_data *priv;
priv = xpd->priv;
BUG_ON(!priv);
- priv->battery_current[chipsel] = data_low;
+ priv->battery_current[portno] = 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))
+ if(xpd->ringing[portno] || !IS_SET(xpd->offhook, portno))
goto ignore_it;
/*
* Power denial with no battery voltage is meaningless
*/
- if(priv->battery[chipsel] != BATTERY_ON)
+ if(priv->battery[portno] != BATTERY_ON)
goto ignore_it;
/* Safe zone after offhook */
- if(priv->power_denial_safezone[chipsel] > 0)
+ if(priv->power_denial_safezone[portno] > 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);
+ if(priv->power[portno] == POWER_ON) {
+ LINE_DBG(SIGNAL, xpd, portno, "power: ON -> OFF\n");
+ priv->power[portno] = POWER_OFF;
+ priv->power_denial_minimum[portno] = POWER_DENIAL_TIME;
}
+ } else {
+ LINE_DBG(SIGNAL, xpd, portno, "power: ON\n");
+ priv->power[portno] = POWER_ON;
+ priv->power_denial_minimum[portno] = 0;
+ update_line_status(xpd, portno, 1);
}
return;
ignore_it:
- BIT_CLR(priv->maybe_power_denial, chipsel);
- priv->power_denial_debounce[chipsel] = 0;
+ BIT_CLR(priv->maybe_power_denial, portno);
+ priv->power_denial_debounce[portno] = 0;
}
#ifdef WITH_METERING
#define BTD_BIT BIT(0)
-static void update_metering_state(xpd_t *xpd, byte data_low, lineno_t chipsel)
+static void update_metering_state(xpd_t *xpd, byte data_low, lineno_t portno)
{
struct FXO_priv_data *priv;
bool metering_tone = data_low & BTD_BIT;
@@ -979,20 +996,20 @@ static void update_metering_state(xpd_t *xpd, byte data_low, lineno_t chipsel)
priv = xpd->priv;
BUG_ON(!priv);
- old_metering_tone = IS_SET(priv->metering_tone_state, chipsel);
- LINE_DBG(SIGNAL, xpd, chipsel, "METERING: %s [dL=0x%X] (%d)\n",
+ old_metering_tone = IS_SET(priv->metering_tone_state, portno);
+ LINE_DBG(SIGNAL, xpd, portno, "METERING: %s [dL=0x%X] (%d)\n",
(metering_tone) ? "ON" : "OFF",
- data_low, priv->metering_count[chipsel]);
+ data_low, priv->metering_count[portno]);
if(metering_tone && !old_metering_tone) {
/* Rising edge */
- priv->metering_count[chipsel]++;
- BIT_SET(priv->metering_tone_state, chipsel);
+ priv->metering_count[portno]++;
+ BIT_SET(priv->metering_tone_state, portno);
} else if(!metering_tone && old_metering_tone)
- BIT_CLR(priv->metering_tone_state, chipsel);
+ BIT_CLR(priv->metering_tone_state, portno);
if(metering_tone) {
/* Clear the BTD bit */
data_low &= ~BTD_BIT;
- DAA_DIRECT_REQUEST(xpd->xbus, xpd, chipsel, DAA_WRITE, DAA_REG_METERING, data_low);
+ DAA_DIRECT_REQUEST(xpd->xbus, xpd, portno, DAA_WRITE, DAA_REG_METERING, data_low);
}
}
#endif
@@ -1000,25 +1017,25 @@ static void update_metering_state(xpd_t *xpd, byte data_low, lineno_t chipsel)
static int FXO_card_register_reply(xbus_t *xbus, xpd_t *xpd, reg_cmd_t *info)
{
struct FXO_priv_data *priv;
- lineno_t chipsel;
+ lineno_t portno;
priv = xpd->priv;
BUG_ON(!priv);
- chipsel = REG_FIELD(info, chipsel);
+ portno = info->portnum;
switch(REG_FIELD(info, regnum)) {
case DAA_REG_VBAT:
- update_battery_voltage(xpd, REG_FIELD(info, data_low), chipsel);
+ update_battery_voltage(xpd, REG_FIELD(info, data_low), portno);
break;
case DAA_REG_CURRENT:
- update_battery_current(xpd, REG_FIELD(info, data_low), chipsel);
+ update_battery_current(xpd, REG_FIELD(info, data_low), portno);
break;
#ifdef WITH_METERING
case DAA_REG_METERING:
- update_metering_state(xpd, REG_FIELD(info, data_low), chipsel);
+ update_metering_state(xpd, REG_FIELD(info, data_low), portno);
break;
#endif
}
- LINE_DBG(REGS, xpd, chipsel, "%c reg_num=0x%X, dataL=0x%X dataH=0x%X\n",
+ LINE_DBG(REGS, xpd, portno, "%c reg_num=0x%X, dataL=0x%X dataH=0x%X\n",
((info->bytes == 3)?'I':'D'),
REG_FIELD(info, regnum),
REG_FIELD(info, data_low),
@@ -1040,7 +1057,8 @@ static xproto_table_t PROTO_TABLE(FXO) = {
/* Prototable Card Opcode */
XENTRY( FXO, FXO, SIG_CHANGED ),
},
- .name = "FXO",
+ .name = "FXO", /* protocol name */
+ .ports_per_subunit = 8,
.type = XPD_TYPE_FXO,
.xops = {
.card_new = FXO_card_new,
@@ -1056,8 +1074,6 @@ static xproto_table_t PROTO_TABLE(FXO) = {
.card_open = FXO_card_open,
.card_register_reply = FXO_card_register_reply,
- .RING = XPROTO_CALLER(FXO, RING),
- .RELAY_OUT = XPROTO_CALLER(FXO, RELAY_OUT),
.XPD_STATE = XPROTO_CALLER(FXO, XPD_STATE),
},
.packet_is_valid = fxo_packet_is_valid,
@@ -1102,12 +1118,16 @@ static int proc_fxo_info_read(char *page, char **start, off_t off, int count, in
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, "%4d ", IS_SET(priv->ledstate[LED_GREEN], i));
+ len += sprintf(page + len, " %d%d ",
+ IS_SET(priv->ledstate[LED_GREEN], i),
+ IS_SET(priv->ledstate[LED_RED], 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, "%4d ", IS_BLINKING(priv,i,LED_GREEN));
+ len += sprintf(page + len, " %d%d ",
+ IS_BLINKING(priv,i,LED_GREEN),
+ IS_BLINKING(priv,i,LED_RED));
}
len += sprintf(page + len, "\nBattery-Data:");
len += sprintf(page + len, "\n\t%-17s: ", "voltage");
@@ -1153,6 +1173,18 @@ static int proc_fxo_info_read(char *page, char **start, off_t off, int count, in
len += sprintf(page + len, "%4d ", priv->polarity_debounce[i]);
}
len += sprintf(page + len, "\nPower-Denial:");
+ len += sprintf(page + len, "\n\t%-17s: ", "power");
+ for_each_line(xpd, i) {
+ char *curr;
+
+ if(priv->power[i] == POWER_ON)
+ curr = "+";
+ else if(priv->power[i] == POWER_OFF)
+ curr = "-";
+ else
+ 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));
@@ -1189,173 +1221,6 @@ static int proc_fxo_info_read(char *page, char **start, off_t off, int count, in
return len;
}
-/*
- *
- * Direct/Indirect
- * |
- * | Reg#
- * | |
- * | | Data (only in Write)
- * | | |
- * | | +-+-+
- * v v v v
- * FF WD 06 01 05
- * ^ ^
- * | |
- * | Write/Read
- * |
- * Chan#
- *
- */
-static int handle_register_command(xpd_t *xpd, char *cmdline)
-{
- unsigned chipsel;
- unsigned data_low = 0;
- char op; /* [W]rite, [R]ead */
- char reg_type; /* [D]irect */
- int reg_num;
- int elements;
- bool writing;
- char *p;
- reg_cmd_t regcmd;
- xbus_t *xbus;
- int ret = -EINVAL;
-
- BUG_ON(!xpd);
- xbus = xpd->xbus;
- if((p = strchr(cmdline, '#')) != NULL) /* Truncate comments */
- *p = '\0';
- if((p = strchr(cmdline, ';')) != NULL) /* Truncate comments */
- *p = '\0';
- for(p = cmdline; *p && (*p == ' ' || *p == '\t'); p++) /* Trim leading whitespace */
- ;
- 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,
- &data_low);
- 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);
- goto out;
- }
- if(!VALID_CHIPSEL(chipsel)) {
- ERR("Bad chipsel number: %d\n", chipsel);
- goto out;
- }
- REG_FIELD(&regcmd, chipsel) = chipsel;
- REG_FIELD(&regcmd, do_subreg) = 0;
- switch(op) {
- case 'W':
- writing = 1;
- break;
- case 'R':
- writing = 0;
- break;
- default:
- ERR("Unkown operation type '%c'\n", op);
- goto out;
- }
- switch(reg_type) {
- case 'D':
- REG_FIELD(&regcmd, regnum) = reg_num;
- REG_FIELD(&regcmd, subreg) = 0;
- break;
- default:
- ERR("Unkown register type '%c'\n", reg_type);
- goto out;
- }
- if(
- (op == 'W' && reg_type == 'D' && elements != 5) ||
- (op == 'R' && reg_type == 'D' && elements != 4)
- ) {
- ERR("%s: '%s' (%d elements): %d %c%c %02X %02X\n", __FUNCTION__,
- cmdline, elements,
- chipsel, op, reg_type, reg_num, data_low);
- 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;
- 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));
-out:
- XBUS_PUT(xbus);
- return ret;
-}
-
-static int proc_xpd_register_write(struct file *file, const char __user *buffer, unsigned long count, void *data)
-{
- xpd_t *xpd = data;
- char buf[MAX_PROC_WRITE];
- char *p;
- int i;
- int ret;
-
- if(!xpd)
- return -ENODEV;
- for(i = 0; i < count; /* noop */) {
- for(p = buf; p < buf + MAX_PROC_WRITE; p++) { /* read a line */
- if(i >= count)
- break;
- if(get_user(*p, buffer + i))
- return -EFAULT;
- i++;
- if(*p == '\n' || *p == '\r') /* whatever */
- break;
- }
- if(p >= buf + MAX_PROC_WRITE)
- return -E2BIG;
- *p = '\0';
- ret = handle_register_command(xpd, buf);
- if(ret < 0)
- return ret;
- msleep(1);
- }
- return count;
-}
-
-static int proc_xpd_register_read(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
- int len = 0;
- unsigned long flags;
- xpd_t *xpd = data;
- reg_cmd_t *info;
- byte regnum;
-
- if(!xpd)
- return -ENODEV;
- spin_lock_irqsave(&xpd->lock, flags);
- info = &xpd->last_reply;
- regnum = REG_FIELD(info, regnum);
- len += sprintf(page + len, "# Writing bad data into this file may damage your hardware!\n");
- len += sprintf(page + len, "# Consult firmware docs first\n");
- len += sprintf(page + len, "#\n");
- len += sprintf(page + len, "#CH\tD/I\tReg.\tDL\n");
- len += sprintf(page + len, "%2d\tRD\t%02X\t%02X\n",
- REG_FIELD(info, chipsel),
- regnum, REG_FIELD(info, data_low));
- spin_unlock_irqrestore(&xpd->lock, flags);
- if (len <= off+count)
- *eof = 1;
- *start = page + off;
- len -= off;
- if (len > count)
- len = count;
- if (len < 0)
- len = 0;
- return len;
-}
-
#ifdef WITH_METERING
static int proc_xpd_metering_read(char *page, char **start, off_t off, int count, int *eof, void *data)
{
@@ -1391,7 +1256,7 @@ static int proc_xpd_metering_read(char *page, char **start, off_t off, int count
}
#endif
-int __init card_fxo_startup(void)
+static int __init card_fxo_startup(void)
{
if(ring_debounce <= 0) {
ERR("ring_debounce=%d. Must be positive number of ticks\n", ring_debounce);
@@ -1407,7 +1272,7 @@ int __init card_fxo_startup(void)
return 0;
}
-void __exit card_fxo_cleanup(void)
+static void __exit card_fxo_cleanup(void)
{
xproto_unregister(&PROTO_TABLE(FXO));
}