diff options
Diffstat (limited to 'kernel/xpp/card_fxo.c')
-rw-r--r-- | kernel/xpp/card_fxo.c | 569 |
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, ®_type, ®_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(®cmd, chipsel) = chipsel; - REG_FIELD(®cmd, 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(®cmd, regnum) = reg_num; - REG_FIELD(®cmd, 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(®cmd, data_low) = data_low; - REG_FIELD(®cmd, data_high) = 0; - REG_FIELD(®cmd, read_request) = writing; - xpd->requested_reply = regcmd; - if(print_dbg) - 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)); -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)); } |