summaryrefslogtreecommitdiff
path: root/drivers/dahdi/xpp/card_fxo.c
diff options
context:
space:
mode:
authorShaun Ruffell <sruffell@digium.com>2014-09-03 17:35:52 +0000
committerShaun Ruffell <sruffell@digium.com>2014-09-03 17:35:52 +0000
commit19e5621b53930e912b17d4a81e943ee6cf49f9ad (patch)
tree40a5a2294c4deb8baa63111402a3ac703a8b2602 /drivers/dahdi/xpp/card_fxo.c
parent9df2805ef9a14d731fcaa1325b6b1054c1f2c101 (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.c1523
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);