diff options
author | Shaun Ruffell <sruffell@digium.com> | 2014-09-03 17:35:52 +0000 |
---|---|---|
committer | Shaun Ruffell <sruffell@digium.com> | 2014-09-03 17:35:52 +0000 |
commit | 19e5621b53930e912b17d4a81e943ee6cf49f9ad (patch) | |
tree | 40a5a2294c4deb8baa63111402a3ac703a8b2602 /drivers/dahdi/xpp/card_fxo.c | |
parent | 9df2805ef9a14d731fcaa1325b6b1054c1f2c101 (diff) |
Point people to where active development is taking placesvn_trunk
git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@10748 a0bf4364-ded3-4de4-8d8a-66a801d63aff
Diffstat (limited to 'drivers/dahdi/xpp/card_fxo.c')
-rw-r--r-- | drivers/dahdi/xpp/card_fxo.c | 1523 |
1 files changed, 0 insertions, 1523 deletions
diff --git a/drivers/dahdi/xpp/card_fxo.c b/drivers/dahdi/xpp/card_fxo.c deleted file mode 100644 index b578ad3..0000000 --- a/drivers/dahdi/xpp/card_fxo.c +++ /dev/null @@ -1,1523 +0,0 @@ -/* - * Written by Oron Peled <oron@actcom.co.il> - * Copyright (C) 2004-2006, Xorcom - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/fs.h> -#include <linux/delay.h> -#include "xpd.h" -#include "xproto.h" -#include "xpp_dahdi.h" -#include "card_fxo.h" -#include "dahdi_debug.h" -#include "xbus-core.h" - -static const char rcsid[] = "$Id$"; - -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 -static DEF_PARM(uint, poll_metering_interval, 500, 0644, - "Poll metering interval in milliseconds (0 - disable)"); -#endif -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 - [ETSI_FSK], 2 - [ETSI_DTMF]"); -static DEF_PARM(int, power_denial_safezone, 650, 0644, - "msec after offhook to ignore power-denial ( (0 - disable power-denial)"); -static DEF_PARM(int, power_denial_minlen, 80, 0644, - "Minimal detected power-denial length (msec) (0 - disable power-denial)"); -static DEF_PARM(uint, battery_threshold, 3, 0644, - "Minimum voltage that shows there is battery"); -static DEF_PARM(uint, battery_debounce, 1000, 0644, - "Minimum interval (msec) for detection of battery off"); - -enum cid_style { - CID_STYLE_BELL = 0, /* E.g: US (Bellcore) */ - CID_STYLE_ETSI_FSK = 1, /* E.g: UK (British Telecom) */ - CID_STYLE_ETSI_DTMF = 2, /* E.g: DK, Russia */ -}; - -/* Signaling is opposite (fxs signalling for fxo card) */ -#if 1 -#define FXO_DEFAULT_SIGCAP (DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS) -#else -#define FXO_DEFAULT_SIGCAP (DAHDI_SIG_SF) -#endif - -enum fxo_leds { - LED_GREEN, - LED_RED, -}; - -#define NUM_LEDS 2 -#define DELAY_UNTIL_DIALTONE 3000 - -/* - * Minimum duration for polarity reversal detection (in ticks) - * Should be longer than the time to detect a ring, so voltage - * fluctuation during ring won't trigger false detection. - */ -#define POLREV_THRESHOLD 200 -#define POWER_DENIAL_CURRENT 3 -#define POWER_DENIAL_DELAY 2500 /* ticks */ - -/* Shortcuts */ -#define DAA_WRITE 1 -#define DAA_READ 0 -#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 bool fxo_packet_is_valid(xpacket_t *pack); -static void fxo_packet_dump(const char *msg, xpacket_t *pack); -#ifdef CONFIG_PROC_FS -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 -#endif -static void dahdi_report_battery(xpd_t *xpd, lineno_t chan); - -#define PROC_REGISTER_FNAME "slics" -#define PROC_FXO_INFO_FNAME "fxo_info" -#ifdef WITH_METERING -#define PROC_METERING_FNAME "metering_read" -#endif - -#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 */ - -enum battery_state { - BATTERY_UNKNOWN = 0, - BATTERY_ON = 1, - BATTERY_OFF = -1 -}; - -enum polarity_state { - POL_UNKNOWN = 0, - POL_POSITIVE = 1, - POL_NEGATIVE = -1 -}; - -enum power_state { - POWER_UNKNOWN = 0, - POWER_ON = 1, - POWER_OFF = -1 -}; - -struct FXO_priv_data { -#ifdef WITH_METERING - struct proc_dir_entry *meteringfile; -#endif - struct proc_dir_entry *fxo_info; - uint poll_counter; - signed char battery_voltage[CHANNELS_PERXPD]; - signed char battery_current[CHANNELS_PERXPD]; - enum battery_state battery[CHANNELS_PERXPD]; - ushort nobattery_debounce[CHANNELS_PERXPD]; - enum polarity_state polarity[CHANNELS_PERXPD]; - ushort polarity_debounce[CHANNELS_PERXPD]; - enum power_state power[CHANNELS_PERXPD]; - ushort power_denial_delay[CHANNELS_PERXPD]; - ushort power_denial_length[CHANNELS_PERXPD]; - ushort power_denial_safezone[CHANNELS_PERXPD]; - xpp_line_t cidfound; /* 0 - OFF, 1 - ON */ - unsigned int cidtimer[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]; -#ifdef WITH_METERING - uint metering_count[CHANNELS_PERXPD]; - xpp_line_t metering_tone_state; -#endif -}; - -/* - * 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) - -#define LED_BLINK_RING (1000/8) /* in ticks */ - -/*---------------- FXO: Static functions ----------------------------------*/ - -static const char *power2str(enum power_state pw) -{ - switch (pw) { - case POWER_UNKNOWN: - return "UNKNOWN"; - case POWER_OFF: - return "OFF"; - case POWER_ON: - return "ON"; - } - return NULL; -} - -static void power_change(xpd_t *xpd, int portno, enum power_state pw) -{ - struct FXO_priv_data *priv; - - priv = xpd->priv; - LINE_DBG(SIGNAL, xpd, portno, "power: %s -> %s\n", - power2str(priv->power[portno]), power2str(pw)); - priv->power[portno] = pw; -} - -static void reset_battery_readings(xpd_t *xpd, lineno_t pos) -{ - struct FXO_priv_data *priv = xpd->priv; - - priv->nobattery_debounce[pos] = 0; - priv->power_denial_delay[pos] = 0; - power_change(xpd, pos, POWER_UNKNOWN); -} - -static const int led_register_mask[] = { BIT(7), BIT(6), BIT(5) }; - -/* - * LED control is done via DAA register 0x20 - */ -static int do_led(xpd_t *xpd, lineno_t chan, __u8 which, bool on) -{ - int ret = 0; - struct FXO_priv_data *priv; - xbus_t *xbus; - __u8 value; - - BUG_ON(!xpd); - xbus = xpd->xbus; - priv = xpd->priv; - which = which % NUM_LEDS; - if (IS_SET(PHONEDEV(xpd).digital_outputs, chan) - || IS_SET(PHONEDEV(xpd).digital_inputs, chan)) - goto out; - if (chan == PORT_BROADCAST) { - priv->ledstate[which] = (on) ? ~0 : 0; - } else { - if (on) - BIT_SET(priv->ledstate[which], chan); - else - 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, value); -out: - return ret; -} - -static void handle_fxo_leds(xpd_t *xpd) -{ - int i; - unsigned long flags; - const enum fxo_leds colors[] = { LED_GREEN, LED_RED }; - enum fxo_leds color; - unsigned int timer_count; - struct FXO_priv_data *priv; - - BUG_ON(!xpd); - spin_lock_irqsave(&xpd->lock, flags); - priv = xpd->priv; - timer_count = xpd->timer_count; - for (color = 0; color < ARRAY_SIZE(colors); color++) { - for_each_line(xpd, i) { - if (IS_SET(PHONEDEV(xpd).digital_outputs, i) - || IS_SET(PHONEDEV(xpd).digital_inputs, i)) - continue; - /* Blinking? */ - if ((xpd->blink_mode & BIT(i)) || IS_BLINKING(priv, i, color)) { - int mod_value = LED_COUNTER(priv, i, color); - - if (!mod_value) - /* safety value */ - mod_value = DEFAULT_LED_PERIOD; - // 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); - } - } - } - spin_unlock_irqrestore(&xpd->lock, flags); -} - -static void update_dahdi_ring(xpd_t *xpd, int pos, bool on) -{ - BUG_ON(!xpd); - if (caller_id_style == CID_STYLE_BELL) - oht_pcm(xpd, pos, !on); - /* - * We should not spinlock before calling dahdi_hooksig() as - * it may call back into our xpp_hooksig() and cause - * a nested spinlock scenario - */ - notify_rxsig(xpd, pos, (on) ? DAHDI_RXSIG_RING : DAHDI_RXSIG_OFFHOOK); -} - -static void mark_ring(xpd_t *xpd, lineno_t pos, bool on, bool update_dahdi) -{ - struct FXO_priv_data *priv; - - priv = xpd->priv; - BUG_ON(!priv); - atomic_set(&priv->ring_debounce[pos], 0); /* Stop debouncing */ - /* - * We don't want to check battery during ringing - * due to voltage fluctuations. - */ - reset_battery_readings(xpd, pos); - if (on && !PHONEDEV(xpd).ringing[pos]) { - LINE_DBG(SIGNAL, xpd, pos, "START\n"); - PHONEDEV(xpd).ringing[pos] = 1; - priv->cidtimer[pos] = xpd->timer_count; - MARK_BLINK(priv, pos, LED_GREEN, LED_BLINK_RING); - if (update_dahdi) - update_dahdi_ring(xpd, pos, on); - } else if (!on && PHONEDEV(xpd).ringing[pos]) { - LINE_DBG(SIGNAL, xpd, pos, "STOP\n"); - PHONEDEV(xpd).ringing[pos] = 0; - priv->cidtimer[pos] = xpd->timer_count; - if (IS_BLINKING(priv, pos, LED_GREEN)) - MARK_BLINK(priv, pos, LED_GREEN, 0); - if (update_dahdi) - update_dahdi_ring(xpd, pos, on); - } -} - -static int do_sethook(xpd_t *xpd, int pos, bool to_offhook) -{ - unsigned long flags; - xbus_t *xbus; - struct FXO_priv_data *priv; - int ret = 0; - __u8 value; - - BUG_ON(!xpd); - /* We can SETHOOK state only on PSTN */ - BUG_ON(PHONEDEV(xpd).direction == TO_PHONE); - xbus = xpd->xbus; - priv = xpd->priv; - BUG_ON(!priv); - if (priv->battery[pos] != BATTERY_ON && to_offhook) { - LINE_NOTICE(xpd, pos, - "Cannot take offhook while battery is off!\n"); - return -EINVAL; - } - spin_lock_irqsave(&xpd->lock, flags); - mark_ring(xpd, pos, 0, 0); // No more rings - 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, REG_DAA_CONTROL1, - value); - mark_offhook(xpd, pos, to_offhook); - if (caller_id_style != CID_STYLE_ETSI_DTMF) - oht_pcm(xpd, pos, 0); -#ifdef WITH_METERING - priv->metering_count[pos] = 0; - priv->metering_tone_state = 0L; - DAA_DIRECT_REQUEST(xbus, xpd, pos, DAA_WRITE, DAA_REG_METERING, 0x2D); -#endif - /* unstable during hook changes */ - reset_battery_readings(xpd, pos); - if (to_offhook) { - priv->power_denial_safezone[pos] = power_denial_safezone; - } else { - priv->power_denial_length[pos] = 0; - priv->power_denial_safezone[pos] = 0; - } - priv->cidtimer[pos] = xpd->timer_count; - spin_unlock_irqrestore(&xpd->lock, flags); - return ret; -} - -/*---------------- FXO: Methods -------------------------------------------*/ - -static void fxo_proc_remove(xbus_t *xbus, xpd_t *xpd) -{ - struct FXO_priv_data *priv; - - BUG_ON(!xpd); - priv = xpd->priv; - XPD_DBG(PROC, xpd, "\n"); -#ifdef CONFIG_PROC_FS -#ifdef WITH_METERING - if (priv->meteringfile) { - XPD_DBG(PROC, xpd, "Removing xpd metering tone file\n"); - priv->meteringfile->data = NULL; - remove_proc_entry(PROC_METERING_FNAME, xpd->proc_xpd_dir); - priv->meteringfile = NULL; - } -#endif - if (priv->fxo_info) { - XPD_DBG(PROC, xpd, "Removing xpd FXO_INFO file\n"); - remove_proc_entry(PROC_FXO_INFO_FNAME, xpd->proc_xpd_dir); - priv->fxo_info = NULL; - } -#endif -} - -static int fxo_proc_create(xbus_t *xbus, xpd_t *xpd) -{ - struct FXO_priv_data *priv; - - BUG_ON(!xpd); - priv = xpd->priv; -#ifdef CONFIG_PROC_FS - XPD_DBG(PROC, xpd, "Creating FXO_INFO file\n"); - 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); - fxo_proc_remove(xbus, xpd); - return -EINVAL; - } - SET_PROC_DIRENTRY_OWNER(priv->fxo_info); -#ifdef WITH_METERING - XPD_DBG(PROC, xpd, "Creating Metering tone file\n"); - priv->meteringfile = - create_proc_read_entry(PROC_METERING_FNAME, 0444, xpd->proc_xpd_dir, - proc_xpd_metering_read, xpd); - if (!priv->meteringfile) { - XPD_ERR(xpd, "Failed to create proc file '%s'\n", - PROC_METERING_FNAME); - fxo_proc_remove(xbus, xpd); - return -EINVAL; - } - SET_PROC_DIRENTRY_OWNER(priv->meteringfile); -#endif -#endif - return 0; -} - -static xpd_t *FXO_card_new(xbus_t *xbus, int unit, int subunit, - const xproto_table_t *proto_table, __u8 subtype, - int subunits, int subunit_ports, 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, subunit_ports); - else - channels = min(8, subunit_ports); - xpd = - xpd_alloc(xbus, unit, subunit, subtype, subunits, - sizeof(struct FXO_priv_data), proto_table, channels); - if (!xpd) - return NULL; - PHONEDEV(xpd).direction = TO_PSTN; - xpd->type_name = "FXO"; - 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); - /* will be updated on next battery sample */ - priv->polarity[i] = POL_UNKNOWN; - priv->polarity_debounce[i] = 0; - /* will be updated on next battery sample */ - priv->battery[i] = BATTERY_UNKNOWN; - /* will be updated on next battery sample */ - priv->power[i] = POWER_UNKNOWN; - if (caller_id_style == CID_STYLE_ETSI_DTMF) - oht_pcm(xpd, i, 1); - } - XPD_DBG(GENERAL, xpd, "done\n"); - 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); - } - CALL_PHONE_METHOD(card_pcm_recompute, xpd, 0); - return 0; -} - -static int FXO_card_remove(xbus_t *xbus, xpd_t *xpd) -{ - BUG_ON(!xpd); - XPD_DBG(GENERAL, xpd, "\n"); - fxo_proc_remove(xbus, xpd); - return 0; -} - -static int FXO_card_dahdi_preregistration(xpd_t *xpd, bool on) -{ - xbus_t *xbus; - struct FXO_priv_data *priv; - int i; - unsigned int timer_count; - - BUG_ON(!xpd); - xbus = xpd->xbus; - BUG_ON(!xbus); - priv = xpd->priv; - BUG_ON(!priv); - timer_count = xpd->timer_count; - XPD_DBG(GENERAL, xpd, "%s\n", (on) ? "ON" : "OFF"); - PHONEDEV(xpd).span.spantype = SPANTYPE_ANALOG_FXO; - for_each_line(xpd, i) { - struct dahdi_chan *cur_chan = XPD_CHAN(xpd, i); - - XPD_DBG(GENERAL, xpd, "setting FXO channel %d\n", 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; - } - for_each_line(xpd, i) { - MARK_ON(priv, i, LED_GREEN); - msleep(4); - MARK_ON(priv, i, LED_RED); - } - for_each_line(xpd, i) { - priv->cidtimer[i] = timer_count; - } - return 0; -} - -static int FXO_card_dahdi_postregistration(xpd_t *xpd, bool on) -{ - xbus_t *xbus; - struct FXO_priv_data *priv; - int i; - - BUG_ON(!xpd); - xbus = xpd->xbus; - BUG_ON(!xbus); - priv = xpd->priv; - BUG_ON(!priv); - XPD_DBG(GENERAL, xpd, "%s\n", (on) ? "ON" : "OFF"); - for_each_line(xpd, i) { - dahdi_report_battery(xpd, i); - MARK_OFF(priv, i, LED_GREEN); - msleep(2); - MARK_OFF(priv, i, LED_RED); - msleep(2); - } - return 0; -} - -static int FXO_card_hooksig(xpd_t *xpd, int pos, enum dahdi_txsig txsig) -{ - struct FXO_priv_data *priv; - int ret = 0; - - priv = xpd->priv; - BUG_ON(!priv); - LINE_DBG(SIGNAL, xpd, pos, "%s\n", txsig2str(txsig)); - BUG_ON(PHONEDEV(xpd).direction != TO_PSTN); - /* XXX Enable hooksig for FXO XXX */ - switch (txsig) { - case DAHDI_TXSIG_START: - case DAHDI_TXSIG_OFFHOOK: - ret = do_sethook(xpd, pos, 1); - break; - case DAHDI_TXSIG_ONHOOK: - ret = do_sethook(xpd, pos, 0); - break; - default: - XPD_NOTICE(xpd, "Can't set tx state to %s (%d)\n", - txsig2str(txsig), txsig); - return -EINVAL; - } - return ret; -} - -static void dahdi_report_battery(xpd_t *xpd, lineno_t chan) -{ - struct FXO_priv_data *priv; - - BUG_ON(!xpd); - priv = xpd->priv; - if (SPAN_REGISTERED(xpd)) { - switch (priv->battery[chan]) { - case BATTERY_UNKNOWN: - /* no-op */ - break; - case BATTERY_OFF: - LINE_DBG(SIGNAL, xpd, chan, "Send DAHDI_ALARM_RED\n"); - dahdi_alarm_channel(XPD_CHAN(xpd, chan), - DAHDI_ALARM_RED); - break; - case BATTERY_ON: - LINE_DBG(SIGNAL, xpd, chan, "Send DAHDI_ALARM_NONE\n"); - dahdi_alarm_channel(XPD_CHAN(xpd, chan), - DAHDI_ALARM_NONE); - break; - } - } -} - -static int FXO_card_open(xpd_t *xpd, lineno_t chan) -{ - BUG_ON(!xpd); - return 0; -} - -static void poll_battery(xbus_t *xbus, xpd_t *xpd) -{ - int i; - - for_each_line(xpd, i) { - DAA_DIRECT_REQUEST(xbus, xpd, i, DAA_READ, DAA_REG_VBAT, 0); - } -} - -#ifdef WITH_METERING -static void poll_metering(xbus_t *xbus, xpd_t *xpd) -{ - int i; - - for_each_line(xpd, i) { - if (IS_OFFHOOK(xpd, i)) - DAA_DIRECT_REQUEST(xbus, xpd, i, DAA_READ, - DAA_REG_METERING, 0); - } -} -#endif - -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 void handle_fxo_power_denial(xpd_t *xpd) -{ - struct FXO_priv_data *priv; - int i; - - if (!power_denial_safezone) - return; /* Ignore power denials */ - priv = xpd->priv; - for_each_line(xpd, i) { - if (PHONEDEV(xpd).ringing[i] || !IS_OFFHOOK(xpd, i)) { - priv->power_denial_delay[i] = 0; - continue; - } - if (priv->power_denial_safezone[i] > 0) { - if (--priv->power_denial_safezone[i] == 0) { - /* - * Poll current, prev answers are meaningless - */ - DAA_DIRECT_REQUEST(xpd->xbus, xpd, i, DAA_READ, - DAA_REG_CURRENT, 0); - } - continue; - } - if (priv->power_denial_length[i] > 0) { - priv->power_denial_length[i]--; - if (priv->power_denial_length[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_delay[i] = - POWER_DENIAL_DELAY; - } - continue; - } - if (priv->power_denial_delay[i] > 0) { - /* - * Ring detection by the firmware takes some time. - * Therefore we delay our decision until we are - * sure that no ring has started during this time. - */ - priv->power_denial_delay[i]--; - if (priv->power_denial_delay[i] <= 0) { - LINE_DBG(SIGNAL, xpd, i, - "Power Denial Hangup\n"); - priv->power_denial_delay[i] = 0; - /* - * Let Asterisk decide what to do - */ - notify_rxsig(xpd, i, DAHDI_RXSIG_ONHOOK); - } - } - } -} - -/* - * For caller-id CID_STYLE_ETSI_DTMF: - * - No indication is passed before the CID - * - We try to detect it and send "fake" polarity reversal. - * - The chan_dahdi.conf should have cidstart=polarity - * - Based on an idea in http://bugs.digium.com/view.php?id=9096 - */ -static void check_etsi_dtmf(xpd_t *xpd) -{ - struct FXO_priv_data *priv; - int portno; - unsigned int timer_count; - - if (!SPAN_REGISTERED(xpd)) - return; - priv = xpd->priv; - BUG_ON(!priv); - timer_count = xpd->timer_count; - for_each_line(xpd, portno) { - /* Skip offhook and ringing ports */ - if (IS_OFFHOOK(xpd, portno) || PHONEDEV(xpd).ringing[portno]) - continue; - if (IS_SET(priv->cidfound, portno)) { - if (timer_count > priv->cidtimer[portno] + 4000) { - /* reset flags if it's been a while */ - priv->cidtimer[portno] = timer_count; - BIT_CLR(priv->cidfound, portno); - LINE_DBG(SIGNAL, xpd, portno, - "Reset CID flag\n"); - } - continue; - } - if (timer_count > priv->cidtimer[portno] + 400) { - struct dahdi_chan *chan = XPD_CHAN(xpd, portno); - int sample; - int i; - - for (i = 0; i < DAHDI_CHUNKSIZE; i++) { - sample = DAHDI_XLAW(chan->readchunk[i], chan); - if (sample > 16000 || sample < -16000) { - priv->cidtimer[portno] = timer_count; - BIT_SET(priv->cidfound, portno); - LINE_DBG(SIGNAL, xpd, portno, - "Found DTMF CLIP (%d)\n", i); - dahdi_qevent_lock(chan, - DAHDI_EVENT_POLARITY); - break; - } - } - } - } -} - -static int FXO_card_tick(xbus_t *xbus, xpd_t *xpd) -{ - struct FXO_priv_data *priv; - - BUG_ON(!xpd); - priv = xpd->priv; - BUG_ON(!priv); - if (poll_battery_interval != 0 - && (priv->poll_counter % poll_battery_interval) == 0) - poll_battery(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); - handle_fxo_power_denial(xpd); - if (caller_id_style == CID_STYLE_ETSI_DTMF && likely(xpd->card_present)) - check_etsi_dtmf(xpd); - priv->poll_counter++; - return 0; -} - -#include <dahdi/wctdm_user.h> -/* - * The first register is the ACIM, the other are coefficient registers. - * We define the array size explicitly to track possible inconsistencies - * if the struct is modified. - */ -static const char echotune_regs[sizeof(struct wctdm_echo_coefs)] = - { 30, 45, 46, 47, 48, 49, 50, 51, 52 }; - -static int FXO_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, - unsigned long arg) -{ - int i, ret; - unsigned char echotune_data[ARRAY_SIZE(echotune_regs)]; - - BUG_ON(!xpd); - if (!XBUS_IS(xpd->xbus, READY)) - return -ENODEV; - switch (cmd) { - case WCTDM_SET_ECHOTUNE: - XPD_DBG(GENERAL, xpd, "-- Setting echo registers: \n"); - /* first off: check if this span is fxs. If not: -EINVALID */ - if (copy_from_user - (&echotune_data, (void __user *)arg, sizeof(echotune_data))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(echotune_regs); i++) { - XPD_DBG(REGS, xpd, "Reg=0x%02X, data=0x%02X\n", - echotune_regs[i], echotune_data[i]); - ret = - DAA_DIRECT_REQUEST(xpd->xbus, xpd, pos, DAA_WRITE, - echotune_regs[i], - echotune_data[i]); - if (ret < 0) { - LINE_NOTICE(xpd, pos, - "Couldn't write %0x02X to " - "register %0x02X\n", - echotune_data[i], echotune_regs[i]); - return ret; - } - msleep(1); - } - - XPD_DBG(GENERAL, xpd, "-- Set echo registers successfully\n"); - break; - case DAHDI_TONEDETECT: - /* - * Asterisk call all span types with this (FXS specific) - * call. Silently ignore it. - */ - LINE_DBG(GENERAL, xpd, pos, - "DAHDI_TONEDETECT (FXO: NOTIMPLEMENTED)\n"); - return -ENOTTY; - default: - report_bad_ioctl(THIS_MODULE->name, xpd, pos, cmd); - return -ENOTTY; - } - return 0; -} - -/*---------------- FXO: HOST COMMANDS -------------------------------------*/ - -/*---------------- FXO: Astribank Reply Handlers --------------------------*/ - -HANDLER_DEF(FXO, SIG_CHANGED) -{ - xpp_line_t sig_status = - RPACKET_FIELD(pack, FXO, SIG_CHANGED, sig_status); - xpp_line_t sig_toggles = - RPACKET_FIELD(pack, FXO, SIG_CHANGED, sig_toggles); - unsigned long flags; - int i; - struct FXO_priv_data *priv; - - if (!xpd) { - notify_bad_xpd(__func__, xbus, XPACKET_ADDR(pack), cmd->name); - return -EPROTO; - } - priv = xpd->priv; - BUG_ON(!priv); - XPD_DBG(SIGNAL, xpd, "(PSTN) sig_toggles=0x%04X sig_status=0x%04X\n", - sig_toggles, sig_status); - spin_lock_irqsave(&xpd->lock, flags); - for_each_line(xpd, i) { - int debounce; - - if (IS_SET(sig_toggles, i)) { - if (priv->battery[i] == BATTERY_OFF) { - /* - * With poll_battery_interval==0 we cannot - * have BATTERY_OFF so we won't get here - */ - LINE_NOTICE(xpd, i, - "SIG_CHANGED while battery is off. " - "Ignored.\n"); - continue; - } - /* First report false ring alarms */ - debounce = atomic_read(&priv->ring_debounce[i]); - if (debounce) - LINE_NOTICE(xpd, i, - "Ignored a false short ring " - "(lasted only %dms)\n", - ring_debounce - 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_voltage(xpd_t *xpd, __u8 data_low, - xportno_t portno) -{ - struct FXO_priv_data *priv; - enum polarity_state pol; - int msec; - signed char volts = (signed char)data_low; - - priv = xpd->priv; - BUG_ON(!priv); - priv->battery_voltage[portno] = volts; - if (PHONEDEV(xpd).ringing[portno]) - goto ignore_reading; /* ring voltage create false alarms */ - if (abs(volts) < battery_threshold) { - /* - * Check for battery voltage fluctuations - */ - if (priv->battery[portno] != BATTERY_OFF) { - int milliseconds; - - milliseconds = - priv->nobattery_debounce[portno]++ * - poll_battery_interval; - if (milliseconds > battery_debounce) { - LINE_DBG(SIGNAL, xpd, portno, - "BATTERY OFF voltage=%d\n", volts); - priv->battery[portno] = BATTERY_OFF; - dahdi_report_battery(xpd, portno); - /* What's the polarity ? */ - priv->polarity[portno] = POL_UNKNOWN; - priv->polarity_debounce[portno] = 0; - /* What's the current ? */ - power_change(xpd, portno, POWER_UNKNOWN); - /* - * Stop further processing for now - */ - goto ignore_reading; - } - - } - } else { - priv->nobattery_debounce[portno] = 0; - if (priv->battery[portno] != BATTERY_ON) { - LINE_DBG(SIGNAL, xpd, portno, "BATTERY ON voltage=%d\n", - volts); - priv->battery[portno] = BATTERY_ON; - dahdi_report_battery(xpd, portno); - } - } -#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) { - /* What's the polarity ? */ - priv->polarity[portno] = POL_UNKNOWN; - return; - } - /* - * Handle reverse polarity - */ - if (volts == 0) - pol = POL_UNKNOWN; - else if (volts < 0) - pol = POL_NEGATIVE; - else - pol = POL_POSITIVE; - if (priv->polarity[portno] == pol) { - /* - * Same polarity, reset debounce counter - */ - priv->polarity_debounce[portno] = 0; - return; - } - /* - * Track polarity reversals and debounce spikes. - * Only reversals with long duration count. - */ - msec = priv->polarity_debounce[portno]++ * poll_battery_interval; - if (msec >= POLREV_THRESHOLD) { - priv->polarity_debounce[portno] = 0; - if (pol != POL_UNKNOWN && priv->polarity[portno] != POL_UNKNOWN) { - char *polname = NULL; - - if (pol == POL_POSITIVE) - polname = "Positive"; - else if (pol == POL_NEGATIVE) - polname = "Negative"; - else - BUG(); - LINE_DBG(SIGNAL, xpd, portno, - "Polarity changed to %s\n", polname); - /* - * Inform dahdi/Asterisk: - * 1. Maybe used for hangup detection during offhook - * 2. In some countries used to report caller-id - * during onhook but before first ring. - */ - if (caller_id_style == CID_STYLE_ETSI_FSK) - /* will be cleared on ring/offhook */ - oht_pcm(xpd, portno, 1); - if (SPAN_REGISTERED(xpd)) { - LINE_DBG(SIGNAL, xpd, portno, - "Send DAHDI_EVENT_POLARITY: %s\n", - polname); - dahdi_qevent_lock(XPD_CHAN(xpd, portno), - DAHDI_EVENT_POLARITY); - } - } - priv->polarity[portno] = pol; - } - return; -ignore_reading: - /* - * Reset debounce counters to prevent false alarms - */ - /* unstable during hook changes */ - reset_battery_readings(xpd, portno); -} - -static void update_battery_current(xpd_t *xpd, __u8 data_low, - xportno_t portno) -{ - struct FXO_priv_data *priv; - - priv = xpd->priv; - BUG_ON(!priv); - priv->battery_current[portno] = data_low; - /* - * During ringing, current is not stable. - * During onhook there should not be current anyway. - */ - if (PHONEDEV(xpd).ringing[portno] || !IS_OFFHOOK(xpd, portno)) - goto ignore_it; - /* - * Power denial with no battery voltage is meaningless - */ - if (priv->battery[portno] != BATTERY_ON) - goto ignore_it; - /* Safe zone after offhook */ - if (priv->power_denial_safezone[portno] > 0) - goto ignore_it; - if (data_low < POWER_DENIAL_CURRENT) { - if (priv->power[portno] == POWER_ON) { - power_change(xpd, portno, POWER_OFF); - priv->power_denial_length[portno] = power_denial_minlen; - } - } else { - if (priv->power[portno] != POWER_ON) { - power_change(xpd, portno, POWER_ON); - priv->power_denial_length[portno] = 0; - /* We are now OFFHOOK */ - hookstate_changed(xpd, portno, 1); - } - } - return; -ignore_it: - priv->power_denial_delay[portno] = 0; -} - -#ifdef WITH_METERING -#define BTD_BIT BIT(0) - -static void update_metering_state(xpd_t *xpd, __u8 data_low, lineno_t portno) -{ - 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, portno); - LINE_DBG(SIGNAL, xpd, portno, "METERING: %s [dL=0x%X] (%d)\n", - (metering_tone) ? "ON" : "OFF", data_low, - priv->metering_count[portno]); - if (metering_tone && !old_metering_tone) { - /* Rising edge */ - priv->metering_count[portno]++; - BIT_SET(priv->metering_tone_state, portno); - } else if (!metering_tone && old_metering_tone) - BIT_CLR(priv->metering_tone_state, portno); - if (metering_tone) { - /* Clear the BTD bit */ - data_low &= ~BTD_BIT; - DAA_DIRECT_REQUEST(xpd->xbus, xpd, portno, DAA_WRITE, - DAA_REG_METERING, data_low); - } -} -#endif - -static int FXO_card_register_reply(xbus_t *xbus, xpd_t *xpd, reg_cmd_t *info) -{ - struct FXO_priv_data *priv; - lineno_t portno; - - priv = xpd->priv; - BUG_ON(!priv); - portno = info->portnum; - switch (REG_FIELD(info, regnum)) { - case DAA_REG_VBAT: - update_battery_voltage(xpd, REG_FIELD(info, data_low), portno); - break; - case DAA_REG_CURRENT: - 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), portno); - break; -#endif - } - 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), REG_FIELD(info, data_high)); - /* Update /proc info only if reply relate to the last slic read request */ - if (REG_FIELD(&xpd->requested_reply, regnum) == - REG_FIELD(info, regnum) - && REG_FIELD(&xpd->requested_reply, do_subreg) == - REG_FIELD(info, do_subreg) - && REG_FIELD(&xpd->requested_reply, subreg) == - REG_FIELD(info, subreg)) { - xpd->last_reply = *info; - } - return 0; -} - -static int FXO_card_state(xpd_t *xpd, bool on) -{ - int ret = 0; - struct FXO_priv_data *priv; - - BUG_ON(!xpd); - priv = xpd->priv; - BUG_ON(!priv); - XPD_DBG(GENERAL, xpd, "%s\n", (on) ? "on" : "off"); - return ret; -} - -static const struct xops fxo_xops = { - .card_new = FXO_card_new, - .card_init = FXO_card_init, - .card_remove = FXO_card_remove, - .card_tick = FXO_card_tick, - .card_register_reply = FXO_card_register_reply, -}; - -static const struct phoneops fxo_phoneops = { - .card_dahdi_preregistration = FXO_card_dahdi_preregistration, - .card_dahdi_postregistration = FXO_card_dahdi_postregistration, - .card_hooksig = FXO_card_hooksig, - .card_pcm_recompute = generic_card_pcm_recompute, - .card_pcm_fromspan = generic_card_pcm_fromspan, - .card_pcm_tospan = generic_card_pcm_tospan, - .card_timing_priority = generic_timing_priority, - .echocancel_timeslot = generic_echocancel_timeslot, - .echocancel_setmask = generic_echocancel_setmask, - .card_ioctl = FXO_card_ioctl, - .card_open = FXO_card_open, - .card_state = FXO_card_state, -}; - -static xproto_table_t PROTO_TABLE(FXO) = { - .owner = THIS_MODULE, - .entries = { - /* Prototable Card Opcode */ - XENTRY( FXO, FXO, SIG_CHANGED ), - }, - .name = "FXO", /* protocol name */ - .ports_per_subunit = 8, - .type = XPD_TYPE_FXO, - .xops = &fxo_xops, - .phoneops = &fxo_phoneops, - .packet_is_valid = fxo_packet_is_valid, - .packet_dump = fxo_packet_dump, -}; - -static bool fxo_packet_is_valid(xpacket_t *pack) -{ - const xproto_entry_t *xe; - - //DBG(GENERAL, "\n"); - xe = xproto_card_entry(&PROTO_TABLE(FXO), XPACKET_OP(pack)); - return xe != NULL; -} - -static void fxo_packet_dump(const char *msg, xpacket_t *pack) -{ - DBG(GENERAL, "%s\n", msg); -} - -/*------------------------- DAA Handling --------------------------*/ - -#ifdef CONFIG_PROC_FS -static int proc_fxo_info_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; - spin_lock_irqsave(&xpd->lock, flags); - priv = xpd->priv; - BUG_ON(!priv); - len += sprintf(page + len, "\t%-17s: ", "Channel"); - for_each_line(xpd, i) { - if (!IS_SET(PHONEDEV(xpd).digital_outputs, i) - && !IS_SET(PHONEDEV(xpd).digital_inputs, i)) - len += sprintf(page + len, "%4d ", i % 10); - } - len += sprintf(page + len, "\nLeds:"); - len += sprintf(page + len, "\n\t%-17s: ", "state"); - for_each_line(xpd, i) { - if (!IS_SET(PHONEDEV(xpd).digital_outputs, i) - && !IS_SET(PHONEDEV(xpd).digital_inputs, 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(PHONEDEV(xpd).digital_outputs, i) - && !IS_SET(PHONEDEV(xpd).digital_inputs, i)) - 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"); - for_each_line(xpd, i) { - len += sprintf(page + len, "%4d ", priv->battery_voltage[i]); - } - len += sprintf(page + len, "\n\t%-17s: ", "current"); - for_each_line(xpd, i) { - len += sprintf(page + len, "%4d ", priv->battery_current[i]); - } - len += sprintf(page + len, "\nBattery:"); - len += sprintf(page + len, "\n\t%-17s: ", "on"); - for_each_line(xpd, i) { - char *bat; - - if (priv->battery[i] == BATTERY_ON) - bat = "+"; - else if (priv->battery[i] == BATTERY_OFF) - bat = "-"; - else - bat = "."; - len += sprintf(page + len, "%4s ", bat); - } - len += sprintf(page + len, "\n\t%-17s: ", "debounce"); - for_each_line(xpd, i) { - len += sprintf(page + len, "%4d ", priv->nobattery_debounce[i]); - } - len += sprintf(page + len, "\nPolarity-Reverse:"); - len += sprintf(page + len, "\n\t%-17s: ", "polarity"); - for_each_line(xpd, i) { - char *polname; - - if (priv->polarity[i] == POL_POSITIVE) - polname = "+"; - else if (priv->polarity[i] == POL_NEGATIVE) - polname = "-"; - else - polname = "."; - len += sprintf(page + len, "%4s ", polname); - } - len += sprintf(page + len, "\n\t%-17s: ", "debounce"); - for_each_line(xpd, i) { - 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: ", "safezone"); - for_each_line(xpd, i) { - len += - sprintf(page + len, "%4d ", priv->power_denial_safezone[i]); - } - len += sprintf(page + len, "\n\t%-17s: ", "delay"); - for_each_line(xpd, i) { - len += sprintf(page + len, "%4d ", priv->power_denial_delay[i]); - } -#ifdef WITH_METERING - len += sprintf(page + len, "\nMetering:"); - len += sprintf(page + len, "\n\t%-17s: ", "count"); - for_each_line(xpd, i) { - len += sprintf(page + len, "%4d ", priv->metering_count[i]); - } -#endif - len += sprintf(page + len, "\n"); - 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; -} -#endif - -#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 - -static DEVICE_ATTR_READER(fxo_battery_show, dev, buf) -{ - xpd_t *xpd; - struct FXO_priv_data *priv; - unsigned long flags; - int len = 0; - int i; - - BUG_ON(!dev); - xpd = dev_to_xpd(dev); - if (!xpd) - return -ENODEV; - priv = xpd->priv; - BUG_ON(!priv); - spin_lock_irqsave(&xpd->lock, flags); - for_each_line(xpd, i) { - char bat; - - if (priv->battery[i] == BATTERY_ON) - bat = '+'; - else if (priv->battery[i] == BATTERY_OFF) - bat = '-'; - else - bat = '.'; - len += sprintf(buf + len, "%c ", bat); - } - len += sprintf(buf + len, "\n"); - spin_unlock_irqrestore(&xpd->lock, flags); - return len; -} - -static DEVICE_ATTR(fxo_battery, S_IRUGO, fxo_battery_show, NULL); - -static int fxo_xpd_probe(struct device *dev) -{ - xpd_t *xpd; - int ret; - - xpd = dev_to_xpd(dev); - /* Is it our device? */ - if (xpd->type != XPD_TYPE_FXO) { - XPD_ERR(xpd, "drop suggestion for %s (%d)\n", dev_name(dev), - xpd->type); - return -EINVAL; - } - XPD_DBG(DEVICES, xpd, "SYSFS\n"); - ret = device_create_file(dev, &dev_attr_fxo_battery); - if (ret) { - XPD_ERR(xpd, "%s: device_create_file(fxo_battery) failed: %d\n", - __func__, ret); - goto fail_fxo_battery; - } - return 0; -fail_fxo_battery: - return ret; -} - -static int fxo_xpd_remove(struct device *dev) -{ - xpd_t *xpd; - - xpd = dev_to_xpd(dev); - XPD_DBG(DEVICES, xpd, "SYSFS\n"); - device_remove_file(dev, &dev_attr_fxo_battery); - return 0; -} - -static struct xpd_driver fxo_driver = { - .type = XPD_TYPE_FXO, - .driver = { - .name = "fxo", - .owner = THIS_MODULE, - .probe = fxo_xpd_probe, - .remove = fxo_xpd_remove} -}; - -static int __init card_fxo_startup(void) -{ - int ret; - - if (ring_debounce <= 0) { - ERR("ring_debounce=%d. Must be positive number of ticks\n", - ring_debounce); - return -EINVAL; - } - if ((ret = xpd_driver_register(&fxo_driver.driver)) < 0) - return ret; - INFO("revision %s\n", XPP_VERSION); -#ifdef WITH_METERING - INFO("FEATURE: WITH METERING Detection\n"); -#else - INFO("FEATURE: NO METERING Detection\n"); -#endif - xproto_register(&PROTO_TABLE(FXO)); - return 0; -} - -static void __exit card_fxo_cleanup(void) -{ - xproto_unregister(&PROTO_TABLE(FXO)); - xpd_driver_unregister(&fxo_driver.driver); -} - -MODULE_DESCRIPTION("XPP FXO Card Driver"); -MODULE_AUTHOR("Oron Peled <oron@actcom.co.il>"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(XPP_VERSION); -MODULE_ALIAS_XPD(XPD_TYPE_FXO); - -module_init(card_fxo_startup); -module_exit(card_fxo_cleanup); |