diff options
author | tzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2007-02-07 20:58:46 +0000 |
---|---|---|
committer | tzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2007-02-07 20:58:46 +0000 |
commit | 8953e09799d8995e3667e5766a362d282700af20 (patch) | |
tree | 8ce49c141cdccd5b1398979e809a3bdeeff0e9d8 /xpp/card_fxo.c | |
parent | 237daca586a95a8f46c5e3326acf9015a004bf1b (diff) |
xpp driver rev. 3332:
* Reverse polarity and power denial detection.
* A short led flash at registration time.
* Add a real version of the xpp modules to them (independent of the Zaptel
version).
* Update our line status even when not registered.
* Fixed a false SIG_CHANGED when inserting or removing cable to FXO.
* Fixed compilation fixes for 2.6.20 (Bug #8982)
* A cleaner fix for the bool changes of 2.6.19 .
* Automatically detect echo_can_state_t at debug time.
* Automaitcally set XPP_DEBUGFS (depending on debugfs) at compile time.
* Bug-fixes to zaptel-helper.
git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.2@2113 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'xpp/card_fxo.c')
-rw-r--r-- | xpp/card_fxo.c | 265 |
1 files changed, 194 insertions, 71 deletions
diff --git a/xpp/card_fxo.c b/xpp/card_fxo.c index 5d5bdc5..70d6018 100644 --- a/xpp/card_fxo.c +++ b/xpp/card_fxo.c @@ -33,9 +33,9 @@ static const char rcsid[] = "$Id$"; -DEF_PARM(int, print_dbg, 0, "Print DBG statements"); -DEF_PARM(uint, poll_battery_interval, 100, "Poll battery interval in milliseconds (0 - disable)"); -DEF_PARM(bool, report_battery, 0, "Report battery status to zaptel"); +DEF_PARM(int, print_dbg, 0, 0600, "Print DBG statements"); +DEF_PARM(uint, poll_battery_interval, 100, 0600, "Poll battery interval in milliseconds (0 - disable)"); +DEF_PARM(int, ring_debounce, 50, 0600, "Number of ticks to debounce a false RING indication"); /* Signaling is opposite (fxs signalling for fxo card) */ #if 1 @@ -79,33 +79,38 @@ static int handle_register_command(xpd_t *xpd, char *cmdline); #define PROC_REGISTER_FNAME "slics" #define PROC_FXO_INFO_FNAME "fxo_info" +#define DAA_CURRENT_REGISTER 0x1C #define DAA_RING_REGISTER 0x05 struct FXO_priv_data { - struct proc_dir_entry *regfile; - struct proc_dir_entry *fxo_info; - uint poll_counter; - xpp_line_t battery; - ushort battery_debounce[CHANNELS_PERXPD]; - xpp_line_t ledstate[NUM_LEDS]; /* 0 - OFF, 1 - ON */ - xpp_line_t ledcontrol[NUM_LEDS]; /* 0 - OFF, 1 - ON */ - int blinking[NUM_LEDS][CHANNELS_PERXPD]; + struct proc_dir_entry *regfile; + struct proc_dir_entry *fxo_info; + uint poll_counter; + xpp_line_t battery; + ushort battery_debounce[CHANNELS_PERXPD]; + xpp_line_t polarity; + ushort polarity_counter[CHANNELS_PERXPD]; + uint offhook_timestamp[CHANNELS_PERXPD]; + ushort current_counter[CHANNELS_PERXPD]; + xpp_line_t ledstate[NUM_LEDS]; /* 0 - OFF, 1 - ON */ + xpp_line_t ledcontrol[NUM_LEDS]; /* 0 - OFF, 1 - ON */ + int led_counter[NUM_LEDS][CHANNELS_PERXPD]; + atomic_t ring_debounce[CHANNELS_PERXPD]; }; -/*---------------- FXO: Static functions ----------------------------------*/ - -#define IS_BLINKING(priv,pos,color) ((priv)->blinking[color][pos] != 0) -#define MARK_BLINK(priv,pos,color,val) ((priv)->blinking[color][pos] = (val)) +/* + * LED counter values: + * n>1 : BLINK every n'th tick + */ +#define LED_COUNTER(priv,pos,color) ((priv)->led_counter[color][pos]) +#define IS_BLINKING(priv,pos,color) (LED_COUNTER(priv,pos,color) > 0) +#define MARK_BLINK(priv,pos,color,t) ((priv)->led_counter[color][pos] = (t)) +#define MARK_OFF(priv,pos,color) do { BIT_CLR((priv)->ledcontrol[color],(pos)); MARK_BLINK((priv),(pos),(color),0); } while(0) +#define MARK_ON(priv,pos,color) do { BIT_SET((priv)->ledcontrol[color],(pos)); MARK_BLINK((priv),(pos),(color),0); } while(0) -void MARK_LED(xpd_t *xpd, lineno_t pos, byte color, bool on) -{ - struct FXO_priv_data *priv = xpd->priv; +#define LED_BLINK_RING (1000/8) /* in ticks */ - if(on) - BIT_SET(priv->ledcontrol[color], pos); - else - BIT_CLR(priv->ledcontrol[color], pos); -} +/*---------------- FXO: Static functions ----------------------------------*/ /* * LED control is done via DAA register 0x20 @@ -153,8 +158,12 @@ static void handle_fxo_leds(xpd_t *xpd) 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 % LED_BLINK_PERIOD) == 0) { + if((timer_count % mod_value) == 0) { DBG("%s/%s/%d: ledstate=%s\n", xpd->xbus->busname, xpd->xpdname, i, (IS_SET(priv->ledstate[color], i))?"ON":"OFF"); if(!IS_SET(priv->ledstate[color], i)) { @@ -178,10 +187,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 */ if(on && !xpd->ringing[pos]) { DBG("%s/%s/%d: START\n", xpd->xbus->busname, xpd->xpdname, pos); xpd->ringing[pos] = 1; - MARK_BLINK(priv, pos, LED_GREEN, LED_BLINK); + MARK_BLINK(priv, pos, LED_GREEN, LED_BLINK_RING); if(update_zap) update_zap_ring(xpd, pos, on); } else if(!on && xpd->ringing[pos]) { @@ -214,18 +224,19 @@ static int do_sethook(xpd_t *xpd, int pos, bool to_offhook) mark_ring(xpd, pos, 0, 0); // No more rings value = (to_offhook) ? 0x09 : 0x08; /* Bit 3 is for CID */ DBG("%s/%s/%d: SETHOOK: value=0x%02X %s\n", xbus->busname, xpd->xpdname, pos, value, (to_offhook)?"OFFHOOK":"ONHOOK"); - MARK_LED(xpd, pos, LED_GREEN, (to_offhook)?LED_ON:LED_OFF); + 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_RING_REGISTER, value); if(to_offhook) { BIT_SET(xpd->offhook, pos); + priv->offhook_timestamp[pos] = priv->poll_counter; } else { BIT_CLR(xpd->offhook, pos); BIT_CLR(xpd->cid_on, pos); - xpd->delay_until_dialtone[pos] = 0; } spin_unlock_irqrestore(&xpd->lock, flags); - if(to_offhook) - wake_up_interruptible(&xpd->txstateq[pos]); return ret; } @@ -299,10 +310,20 @@ static int FXO_card_init(xbus_t *xbus, xpd_t *xpd) goto err; // Hanghup all lines for_each_line(xpd, i) { - init_waitqueue_head(&xpd->txstateq[i]); do_sethook(xpd, i, 0); } DBG("done: %s/%s\n", xbus->busname, xpd->xpdname); + for_each_line(xpd, i) { + do_led(xpd, i, LED_GREEN, 0); + } + for_each_line(xpd, i) { + do_led(xpd, i, LED_GREEN, 1); + msleep(50); + } + for_each_line(xpd, i) { + do_led(xpd, i, LED_GREEN, 0); + msleep(50); + } return 0; err: clean_proc(xbus, xpd); @@ -343,10 +364,9 @@ static int FXO_card_zaptel_preregistration(xpd_t *xpd, bool on) cur_chan->pvt = xpd; cur_chan->sigcap = FXO_DEFAULT_SIGCAP; } - MARK_LED(xpd, ALL_LINES, LED_GREEN, LED_OFF); for_each_line(xpd, i) { - MARK_LED(xpd, i, LED_GREEN, LED_ON); - msleep(50); + MARK_ON(priv, i, LED_GREEN); + msleep(4); } return 0; } @@ -364,8 +384,10 @@ static int FXO_card_zaptel_postregistration(xpd_t *xpd, bool on) BUG_ON(!priv); DBG("%s/%s (%d)\n", xbus->busname, xpd->xpdname, on); for_each_line(xpd, i) { - MARK_LED(xpd, i, LED_GREEN, LED_OFF); - msleep(50); + MARK_OFF(priv, i, LED_GREEN); + msleep(2); + // MARK_OFF(priv, i, LED_RED); + msleep(2); } return 0; } @@ -404,6 +426,35 @@ 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_CURRENT_REGISTER, 0); + } +} + +static void handle_fxo_ring(xpd_t *xpd) +{ + struct FXO_priv_data *priv; + int i; + + priv = xpd->priv; + for_each_line(xpd, i) { + if(atomic_read(&priv->ring_debounce[i]) > 0) { + /* Maybe start ring */ + if(atomic_dec_and_test(&priv->ring_debounce[i])) + mark_ring(xpd, i, 1, 1); + } else if (atomic_read(&priv->ring_debounce[i]) < 0) { + /* Maybe stop ring */ + if(atomic_inc_and_test(&priv->ring_debounce[i])) + mark_ring(xpd, i, 0, 1); + } + } +} + static int FXO_card_tick(xbus_t *xbus, xpd_t *xpd) { struct FXO_priv_data *priv; @@ -413,8 +464,10 @@ 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); + poll_current(xbus, xpd); } handle_fxo_leds(xpd); + handle_fxo_ring(xpd); priv->poll_counter++; return 0; } @@ -497,22 +550,14 @@ static int FXO_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a static /* 0x0F */ HOSTCMD(FXO, XPD_STATE, bool on) { - int ret = 0; - int i; + int ret = 0; + struct FXO_priv_data *priv; BUG_ON(!xbus); BUG_ON(!xpd); + priv = xpd->priv; + BUG_ON(!priv); DBG("%s/%s: %s\n", xbus->busname, xpd->xpdname, (on) ? "on" : "off"); - if(on) { - for_each_line(xpd, i) { - MARK_LED(xpd, i, LED_GREEN, LED_ON); - msleep(20); - } - for_each_line(xpd, i) { - MARK_LED(xpd, i, LED_GREEN, LED_OFF); - msleep(20); - } - } return ret; } @@ -549,6 +594,8 @@ HANDLER_DEF(FXO, SIG_CHANGED) DBG("%s/%s: (PSTN) sig_toggles=0x%04X sig_status=0x%04X\n", xpd->xbus->busname, xpd->xpdname, sig_toggles, sig_status); spin_lock_irqsave(&xpd->lock, flags); for_each_line(xpd, i) { + int debounce; + if(IS_SET(sig_toggles, i)) { if(!IS_SET(priv->battery, i)) { DBG("%s/%s/%d: SIG_CHANGED while battery is off.\n", @@ -556,13 +603,100 @@ HANDLER_DEF(FXO, SIG_CHANGED) // FIXME: allow dialing without battery polling... // continue; } - mark_ring(xpd, i, IS_SET(sig_status, i), 1); + /* First report false ring alarms */ + debounce = atomic_read(&priv->ring_debounce[i]); + if(debounce) + NOTICE("%s/%s/%d: debounced %d ticks\n", xbus->busname, xpd->xpdname, i, debounce); + /* + * Now set a new ring alarm. + * It will be checked in handle_fxo_ring() + */ + debounce = (IS_SET(sig_status, i)) ? ring_debounce : -ring_debounce; + atomic_set(&priv->ring_debounce[i], debounce); } } spin_unlock_irqrestore(&xpd->lock, flags); return 0; } +static void update_battery_status(xpd_t *xpd, byte data_low, lineno_t chipsel) +{ + struct FXO_priv_data *priv; + byte bat = abs((signed char)data_low); + byte pol = IS_SET(data_low, 7); + + priv = xpd->priv; + BUG_ON(!priv); + if(bat < BAT_THRESHOLD) { + /* + * Check for battery voltage fluctuations + */ + if(IS_SET(priv->battery, chipsel) && priv->battery_debounce[chipsel]++ > BAT_DEBOUNCE) { + DBG("%s/%s/%d: BATTERY OFF voltage=%d\n", xpd->xbus->busname, xpd->xpdname, chipsel, bat); + BIT_CLR(priv->battery, chipsel); + update_line_status(xpd, chipsel, 0); + } + } else { + priv->battery_debounce[chipsel] = 0; + if(!IS_SET(priv->battery, chipsel)) { + DBG("%s/%s/%d: BATTERY ON voltage=%d\n", xpd->xbus->busname, xpd->xpdname, chipsel, bat); + BIT_SET(priv->battery, chipsel); + } + } + /* + * Handle reverse polarity + */ + if (IS_SET(xpd->offhook, chipsel)) { /* Learn the current polarity */ + if (priv->poll_counter - priv->offhook_timestamp[chipsel] < 3) { + 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) { + if (pol) + BIT_SET(priv->polarity, chipsel); + else + BIT_CLR(priv->polarity, chipsel); + priv->polarity_counter[chipsel] = 0; + /* Inform Zaptel */ + zt_qevent_lock(&xpd->chans[chipsel], ZT_EVENT_POLARITY); +#if 0 + /* + * These two lines hangup the channel (by sending a message to + * the firmware), and inform Zaptel that the line has been hung-up. + * They are not needed if Asterisk does the hangup after receiving + * a notification from Zaptel (which is sent by the above zt_qevent_lock(). + * Asterisk does that if it has "hanguponpolarityswitch=1" in zapata.conf. + */ + do_sethook(xpd, chipsel, 0); + update_line_status(xpd, chipsel, 0); +#endif + } + } + } +} + +static void update_power_denial(xpd_t *xpd, byte data_low, lineno_t chipsel) +{ + struct FXO_priv_data *priv; + + priv = xpd->priv; + BUG_ON(!priv); + if (IS_SET(xpd->offhook, chipsel) && data_low < 3) { + priv->current_counter[chipsel]++; + if (priv->current_counter[chipsel] >= 10) { + priv->current_counter[chipsel] = 0; + do_sethook(xpd, chipsel, 0); + update_line_status(xpd, chipsel, 0); + } + } else + priv->current_counter[chipsel] = 0; +} + HANDLER_DEF(FXO, DAA_REPLY) { reg_cmd_t *info = &RPACKET_FIELD(pack, FXO, DAA_REPLY, regcmd); @@ -579,29 +713,13 @@ HANDLER_DEF(FXO, DAA_REPLY) priv = xpd->priv; BUG_ON(!priv); chipsel = REG_FIELD(info, chipsel); - - /* - * Update battery status - */ - if(REG_FIELD(info, regnum) == DAA_VBAT_REGISTER) { - byte bat = abs((signed char)REG_FIELD(info, data_low)); - - if(bat < BAT_THRESHOLD) { - /* - * Check for battery voltage fluctuations - */ - if(IS_SET(priv->battery, chipsel) && priv->battery_debounce[chipsel]++ > BAT_DEBOUNCE) { - DBG("%s/%s/%d: BATTERY OFF voltage=%d\n", xpd->xbus->busname, xpd->xpdname, chipsel, bat); - BIT_CLR(priv->battery, chipsel); - update_line_status(xpd, chipsel, 0); - } - } else { - priv->battery_debounce[chipsel] = 0; - if(!IS_SET(priv->battery, chipsel)) { - DBG("%s/%s/%d: BATTERY ON voltage=%d\n", xpd->xbus->busname, xpd->xpdname, chipsel, bat); - BIT_SET(priv->battery, chipsel); - } - } + switch(REG_FIELD(info, regnum)) { + case DAA_VBAT_REGISTER: + update_battery_status(xpd, REG_FIELD(info, data_low), chipsel); + break; + case DAA_CURRENT_REGISTER: + update_power_denial(xpd, REG_FIELD(info, data_low), chipsel); + break; } #if 0 DBG("DAA_REPLY: xpd #%d %s reg_num=0x%X, dataL=0x%X dataH=0x%X\n", @@ -879,6 +997,11 @@ static int proc_xpd_register_read(char *page, char **start, off_t off, int count int __init card_fxo_startup(void) { + if(ring_debounce <= 0) { + ERR("%s: ring_debounce=%d. Must be positive number of ticks\n", + THIS_MODULE->name, ring_debounce); + return -EINVAL; + } INFO("%s revision %s\n", THIS_MODULE->name, XPP_VERSION); xproto_register(&PROTO_TABLE(FXO)); return 0; |