From c88eaa22e13bd4c092b367a28e57064659660466 Mon Sep 17 00:00:00 2001 From: tzafrir Date: Thu, 2 Aug 2007 12:21:11 +0000 Subject: Merge xpp r4372: * Update to zaptel-1.2.18 and zaptel-1.4.3 (r4308 onward) * Fix a critical race with zaptel synchronization (r4362) * Added a /proc/xpp/cmds for statistics about command timing (r4360) * Fix a digit mapping bug with hardware dtmf detection (r4357) * In xpp/utils/Makefile add perl syntax checks to our scripts (r4337) * Better USB data error checking (r4336) * udev rules (xpp.rules) avoid false calls from wrong nodes (r4331) * Improve hardware detection and reporting in lszaptel, zaptel_hardware. zapconf is basically functional. * Leds are blinked synchronously on all Astribanks now (r4262) * Fix a BRI bug if OPTIMIZE_CHANMUTE was compiled into zaptel (r4258) (This feature was not yet accepted into official zaptel) * Removed compile warning about HZ != 1000 (r4218) * Firmware updates. * fpga_load now supports USB pathes without zeros (r4211) * XPD numbers have changed to '' (r4196) * Proper support for ZT_VMWI ioctl, if used in zaptel (r4092) * Fix FXO power denial detection (r4054) * FXO could accidentally go off-hook with some compilers (r4048) (From branches/1.2 r2732, r2735 - branches/1.4 2736) git-svn-id: http://svn.digium.com/svn/zaptel/trunk@2813 5390a7c7-147a-4af0-8ec9-7488f05a26cb --- xpp/card_fxo.c | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 178 insertions(+), 13 deletions(-) (limited to 'xpp/card_fxo.c') 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", ®cmd); + dump_reg_cmd("FXO", ®cmd, 1); ret = DAA_DIRECT_REQUEST(xpd->xbus, xpd, REG_FIELD(®cmd, chipsel), writing, REG_FIELD(®cmd, regnum), REG_FIELD(®cmd, 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; } -- cgit v1.2.3