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.c191
1 files changed, 178 insertions, 13 deletions
diff --git a/xpp/card_fxo.c b/xpp/card_fxo.c
index 4c2ac87..bc67950 100644
--- a/xpp/card_fxo.c
+++ b/xpp/card_fxo.c
@@ -35,6 +35,9 @@ 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)");
+#ifdef WITH_METERING
+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");
/* Signaling is opposite (fxs signalling for fxo card) */
@@ -72,21 +75,31 @@ 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);
static int proc_fxo_info_read(char *page, char **start, off_t off, int count, int *eof, void *data);
+#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);
#define PROC_REGISTER_FNAME "slics"
#define PROC_FXO_INFO_FNAME "fxo_info"
+#ifdef WITH_METERING
+#define PROC_METERING_FNAME "metering_read"
+#endif
-#define DAA_CURRENT_REGISTER 0x1C
#define DAA_RING_REGISTER 0x05
+#define DAA_METERING_REGISTER 0x11 /* 17 */
+#define DAA_CURRENT_REGISTER 0x1C /* 28 */
#define POWER_DENIAL_CURRENT 3
#define POWER_DENIAL_TIME 1000 /* ticks */
struct FXO_priv_data {
struct proc_dir_entry *regfile;
+#ifdef WITH_METERING
+ struct proc_dir_entry *meteringfile;
+#endif
struct proc_dir_entry *fxo_info;
uint poll_counter;
xpp_line_t battery;
@@ -99,6 +112,10 @@ struct FXO_priv_data {
xpp_line_t ledcontrol[NUM_LEDS]; /* 0 - OFF, 1 - ON */
int led_counter[NUM_LEDS][CHANNELS_PERXPD];
atomic_t ring_debounce[CHANNELS_PERXPD];
+#ifdef WITH_METERING
+ uint metering_count[CHANNELS_PERXPD];
+ xpp_line_t metering_tone_state;
+#endif
};
/*
@@ -184,6 +201,28 @@ static void handle_fxo_leds(xpd_t *xpd)
spin_unlock_irqrestore(&xpd->lock, flags);
}
+void update_zap_ring(xpd_t *xpd, int pos, bool on)
+{
+ zt_rxsig_t rxsig;
+
+ BUG_ON(!xpd);
+ if(on) {
+ BIT_CLR(xpd->cid_on, pos);
+ rxsig = ZT_RXSIG_RING;
+ } else {
+ BIT_SET(xpd->cid_on, pos);
+ rxsig = ZT_RXSIG_OFFHOOK;
+ }
+ pcm_recompute(xpd);
+ /*
+ * We should not spinlock before calling zt_hooksig() as
+ * it may call back into our xpp_hooksig() and cause
+ * a nested spinlock scenario
+ */
+ if(SPAN_REGISTERED(xpd))
+ zt_hooksig(&xpd->chans[pos], rxsig);
+}
+
static void mark_ring(xpd_t *xpd, lineno_t pos, bool on, bool update_zap)
{
struct FXO_priv_data *priv;
@@ -239,22 +278,28 @@ static int do_sethook(xpd_t *xpd, int pos, bool to_offhook)
BIT_CLR(xpd->offhook, pos);
BIT_CLR(xpd->cid_on, pos);
}
+#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);
+#endif
spin_unlock_irqrestore(&xpd->lock, flags);
return ret;
}
/*---------------- FXO: Methods -------------------------------------------*/
-static xpd_t *FXO_card_new(xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, byte revision)
+static xpd_t *FXO_card_new(xbus_t *xbus, int unit, int subunit, const xproto_table_t *proto_table, byte revision)
{
xpd_t *xpd = NULL;
int channels = min(8, CHANNELS_PERXPD);
- xpd = xpd_alloc(sizeof(struct FXO_priv_data), xbus, xpd_num, proto_table, channels, revision);
+ 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;
}
@@ -271,6 +316,14 @@ static void clean_proc(xbus_t *xbus, xpd_t *xpd)
remove_proc_entry(PROC_REGISTER_FNAME, xpd->proc_xpd_dir);
priv->regfile->data = NULL;
}
+#ifdef WITH_METERING
+ if(priv->meteringfile) {
+ DBG("Removing xpd metering tone file %s/%s\n", xbus->busname, xpd->xpdname);
+ priv->meteringfile->data = NULL;
+ remove_proc_entry(PROC_METERING_FNAME, xpd->proc_xpd_dir);
+ priv->meteringfile = NULL;
+ }
+#endif
if(priv->fxo_info) {
DBG("Removing xpd FXO_INFO file %s/%s\n", xbus->busname, xpd->xpdname);
remove_proc_entry(PROC_FXO_INFO_FNAME, xpd->proc_xpd_dir);
@@ -296,6 +349,17 @@ static int FXO_card_init(xbus_t *xbus, xpd_t *xpd)
goto err;
}
priv->fxo_info->owner = THIS_MODULE;
+#ifdef WITH_METERING
+ DBG("Creating Metering tone file for %s/%s\n", xbus->busname, xpd->xpdname);
+ priv->meteringfile = create_proc_read_entry(PROC_METERING_FNAME, 0444, xpd->proc_xpd_dir,
+ proc_xpd_metering_read, xpd);
+ if(!priv->meteringfile) {
+ ERR("%s/%s: Failed to create proc file for metering tone\n", xbus->busname, xpd->xpdname);
+ ret = -ENOENT;
+ goto err;
+ }
+ priv->meteringfile->owner = THIS_MODULE;
+#endif
DBG("Creating DAAs file for %s/%s\n", xbus->busname, xpd->xpdname);
priv->regfile = create_proc_entry(PROC_REGISTER_FNAME, 0644, xpd->proc_xpd_dir);
if(!priv->regfile) {
@@ -327,6 +391,7 @@ static int FXO_card_init(xbus_t *xbus, xpd_t *xpd)
do_led(xpd, i, LED_GREEN, 0);
msleep(50);
}
+ pcm_recompute(xpd);
return 0;
err:
clean_proc(xbus, xpd);
@@ -357,12 +422,12 @@ static int FXO_card_zaptel_preregistration(xpd_t *xpd, bool on)
priv = xpd->priv;
BUG_ON(!priv);
DBG("%s/%s (%d)\n", xbus->busname, xpd->xpdname, on);
- snprintf(xpd->span.desc, MAX_SPANDESC, "Xorcom XPD #%d/%d: FXO", xbus->num, xpd->id);
for_each_line(xpd, i) {
struct zt_chan *cur_chan = &xpd->chans[i];
DBG("setting FXO channel %d\n", i);
- snprintf(cur_chan->name, MAX_CHANNAME, "XPP_FXO/%d/%d/%d", xbus->num, xpd->id, i);
+ snprintf(cur_chan->name, MAX_CHANNAME, "XPP_FXO/%02d/%1d%1d/%d",
+ xbus->num, xpd->addr.unit, xpd->addr.subunit, i);
cur_chan->chanpos = i + 1;
cur_chan->pvt = xpd;
cur_chan->sigcap = FXO_DEFAULT_SIGCAP;
@@ -417,6 +482,7 @@ int FXO_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig)
NOTICE("Can't set tx state to %s (%d)\n", txsig2str(txsig), txsig);
return -EINVAL;
}
+ pcm_recompute(xpd);
return 0;
}
@@ -456,6 +522,18 @@ static void poll_current(xbus_t *xbus, xpd_t *xpd)
}
}
+#ifdef WITH_METERING
+static void poll_metering(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_METERING_REGISTER, 0);
+ }
+}
+#endif
+
static void handle_fxo_ring(xpd_t *xpd)
{
struct FXO_priv_data *priv;
@@ -486,6 +564,10 @@ static int FXO_card_tick(xbus_t *xbus, xpd_t *xpd)
poll_battery(xbus, xpd);
poll_current(xbus, xpd);
}
+#ifdef WITH_METERING
+ if(poll_metering_interval != 0 && (priv->poll_counter % poll_metering_interval) == 0)
+ poll_metering(xbus, xpd);
+#endif
handle_fxo_leds(xpd);
handle_fxo_ring(xpd);
priv->poll_counter++;
@@ -547,7 +629,7 @@ static int FXO_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a
DBG("NO XBUS\n");
return -EINVAL;
}
- XFRAME_NEW(xframe, pack, xbus, GLOBAL, REGISTER_REQUEST, xpd->id);
+ XFRAME_NEW(xframe, pack, xbus, GLOBAL, REGISTER_REQUEST, xpd->xbus_idx);
#if 0
DBG("%s/%s/%d: %c%c R%02X S%02X %02X %02X\n",
xbus->busname, xpd->xpdname, chipsel,
@@ -564,7 +646,7 @@ static int FXO_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a
REG_FIELD(reg_cmd, subreg) = subreg;
REG_FIELD(reg_cmd, data_low) = data_low;
REG_FIELD(reg_cmd, data_high) = data_high;
- ret = xframe_send(xbus, xframe);
+ ret = send_cmd_frame(xbus, xframe);
return ret;
}
@@ -605,8 +687,7 @@ HANDLER_DEF(FXO, SIG_CHANGED)
struct FXO_priv_data *priv;
if(!xpd) {
- NOTICE("%s: received %s for non-existing xpd: %d\n",
- __FUNCTION__, cmd->name, XPD_NUM(pack->addr));
+ notify_bad_xpd(__FUNCTION__, xbus, pack->addr, cmd->name);
return -EPROTO;
}
priv = xpd->priv;
@@ -699,6 +780,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);
#endif
}
}
@@ -719,11 +801,42 @@ 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);
}
} else
priv->current_counter[chipsel] = 0;
}
+#ifdef WITH_METERING
+#define BTD_BIT BIT(0)
+
+static void update_metering_state(xpd_t *xpd, byte data_low, lineno_t chipsel)
+{
+ struct FXO_priv_data *priv;
+ bool metering_tone = data_low & BTD_BIT;
+ bool old_metering_tone;
+
+ priv = xpd->priv;
+ BUG_ON(!priv);
+ old_metering_tone = IS_SET(priv->metering_tone_state, chipsel);
+ DBG("%s/%s/%d: METERING: %s [dL=0x%X] (%d)\n",
+ xpd->xbus->busname, xpd->xpdname, chipsel,
+ (metering_tone) ? "ON" : "OFF",
+ data_low, priv->metering_count[chipsel]);
+ if(metering_tone && !old_metering_tone) {
+ /* Rising edge */
+ priv->metering_count[chipsel]++;
+ BIT_SET(priv->metering_tone_state, chipsel);
+ } else if(!metering_tone && old_metering_tone)
+ BIT_CLR(priv->metering_tone_state, 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);
+ }
+}
+#endif
+
HANDLER_DEF(FXO, DAA_REPLY)
{
reg_cmd_t *info = &RPACKET_FIELD(pack, FXO, DAA_REPLY, regcmd);
@@ -731,8 +844,7 @@ HANDLER_DEF(FXO, DAA_REPLY)
lineno_t chipsel;
if(!xpd) {
- NOTICE("%s: received %s for non-existing xpd: %d\n",
- __FUNCTION__, cmd->name, XPD_NUM(pack->addr));
+ notify_bad_xpd(__FUNCTION__, xbus, pack->addr, cmd->name);
return -EPROTO;
}
priv = xpd->priv;
@@ -745,10 +857,15 @@ HANDLER_DEF(FXO, DAA_REPLY)
case DAA_CURRENT_REGISTER:
update_power_denial(xpd, REG_FIELD(info, data_low), chipsel);
break;
+#ifdef WITH_METERING
+ case DAA_METERING_REGISTER:
+ update_metering_state(xpd, REG_FIELD(info, data_low), chipsel);
+ break;
+#endif
}
#if 0
DBG("DAA_REPLY: xpd #%d %s reg_num=0x%X, dataL=0x%X dataH=0x%X\n",
- xpd->id, (info->size == 3)?"I":"D",
+ xpd->xbus_idx, (info->size == 3)?"I":"D",
info->reg_num, info->data_low, info->data_high);
#endif
@@ -780,6 +897,8 @@ xproto_table_t PROTO_TABLE(FXO) = {
.card_zaptel_postregistration = FXO_card_zaptel_postregistration,
.card_hooksig = FXO_card_hooksig,
.card_tick = FXO_card_tick,
+ .card_pcm_fromspan = generic_card_pcm_fromspan,
+ .card_pcm_tospan = generic_card_pcm_tospan,
.card_ioctl = FXO_card_ioctl,
.card_open = FXO_card_open,
@@ -843,6 +962,12 @@ 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));
}
+#ifdef WITH_METERING
+ len += sprintf(page + len, "\n\t%-17s: ", "metering");
+ for_each_line(xpd, i) {
+ len += sprintf(page + len, "%2d ", priv->metering_count[i]);
+ }
+#endif
len += sprintf(page + len, "\n");
spin_unlock_irqrestore(&xpd->lock, flags);
if (len <= off+count)
@@ -953,7 +1078,7 @@ static int handle_register_command(xpd_t *xpd, char *cmdline)
}
xpd->requested_reply = regcmd;
if(print_dbg)
- dump_reg_cmd("FXO", &regcmd);
+ 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);
return ret;
@@ -1022,6 +1147,41 @@ static int proc_xpd_register_read(char *page, char **start, off_t off, int count
return len;
}
+#ifdef WITH_METERING
+static int proc_xpd_metering_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ int len = 0;
+ unsigned long flags;
+ xpd_t *xpd = data;
+ struct FXO_priv_data *priv;
+ int i;
+
+ if(!xpd)
+ return -ENODEV;
+ priv = xpd->priv;
+ BUG_ON(!priv);
+ spin_lock_irqsave(&xpd->lock, flags);
+ len += sprintf(page + len, "# Chan\tMeter (since last read)\n");
+ for_each_line(xpd, i) {
+ len += sprintf(page + len, "%d\t%d\n",
+ i, priv->metering_count[i]);
+ }
+ 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;
+ /* Zero meters */
+ for_each_line(xpd, i)
+ priv->metering_count[i] = 0;
+ return len;
+}
+#endif
+
int __init card_fxo_startup(void)
{
if(ring_debounce <= 0) {
@@ -1030,6 +1190,11 @@ int __init card_fxo_startup(void)
return -EINVAL;
}
INFO("%s revision %s\n", THIS_MODULE->name, XPP_VERSION);
+#ifdef WITH_METERING
+ INFO("FEATURE: %s WITH METERING Detection\n", THIS_MODULE->name);
+#else
+ INFO("FEATURE: %s NO METERING Detection\n", THIS_MODULE->name);
+#endif
xproto_register(&PROTO_TABLE(FXO));
return 0;
}