summaryrefslogtreecommitdiff
path: root/xpp/card_fxs.c
diff options
context:
space:
mode:
authortzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2006-05-03 23:06:02 +0000
committertzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2006-05-03 23:06:02 +0000
commit2dd60aaf18e98b0e9d3c06bd9dce5f1128fa55ad (patch)
tree1a1cd28888f191e6ce83bcbbe539124e2529c90b /xpp/card_fxs.c
parent8c4db4e3acd9a7626e709af0494055487b589719 (diff)
xpp driver release 1.1.0 (first part of commit)
* FPGA firmware now loaded from PC (for newer models) * Driver for the FXO module * Moved most userspace files to the subdirectory utils (see also next commit) * Explicit license for firmware files * Optionally avoid auto-registration * Initializations parameters to chips given from userspace * And did I mention bugfixes? git-svn-id: http://svn.digium.com/svn/zaptel/trunk@1021 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'xpp/card_fxs.c')
-rw-r--r--xpp/card_fxs.c661
1 files changed, 502 insertions, 159 deletions
diff --git a/xpp/card_fxs.c b/xpp/card_fxs.c
index 37c0229..e4480a3 100644
--- a/xpp/card_fxs.c
+++ b/xpp/card_fxs.c
@@ -1,6 +1,6 @@
/*
* Written by Oron Peled <oron@actcom.co.il>
- * Copyright (C) 2004-2005, Xorcom
+ * Copyright (C) 2004-2006, Xorcom
*
* All rights reserved.
*
@@ -23,13 +23,15 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
+#include <linux/delay.h>
+#include <version.h> /* For zaptel version */
#include "xpd.h"
#include "xproto.h"
#include "xpp_zap.h"
-#include <linux/delay.h>
+#include "card_fxo.h"
+#include "zap_debug.h"
static const char rcsid[] = "$Id$";
-static const char revision[] = "$Revision$";
DEF_PARM(int, print_dbg, 0, "Print DBG statements"); /* must be before zap_debug.h */
@@ -42,20 +44,27 @@ DEF_PARM(int, print_dbg, 0, "Print DBG statements"); /* must be before zap_debug
#define MASK_DIGI_OUT (MASK_BITS(LINES_DIGI_OUT) << LINES_REGULAR)
#define MASK_DIGI_INP (MASK_BITS(LINES_DIGI_INP) << (LINES_REGULAR + LINES_DIGI_OUT))
+enum fxs_leds {
+ LED_GREEN,
+ LED_RED,
+ OUTPUT_RELAY,
+};
+
+#define NUM_LEDS 2
+
/*---------------- FXS Protocol Commands ----------------------------------*/
-/* 0x0F */ DECLARE_CMD(FXS, CHAN_ENABLE, xpp_line_t lines, bool on);
-/* 0x0F */ DECLARE_CMD(FXS, CHAN_POWER, xpp_line_t lines, bool on);
-/* 0x0F */ DECLARE_CMD(FXS, CHAN_CID, xpp_line_t lines);
-/* 0x0F */ DECLARE_CMD(FXS, RING, int pos, bool on);
-/* 0x0F */ DECLARE_CMD(FXS, SETHOOK, xpp_line_t hook_status);
-/* 0x0F */ DECLARE_CMD(FXS, LED, xpp_line_t lines, byte which, bool on);
-/* 0x0F */ DECLARE_CMD(FXS, RELAY_OUT, byte which, bool on);
-/* 0x0F */ DECLARE_CMD(FXS, SLIC_INIT);
-/* 0x0F */ DECLARE_CMD(FXS, SLIC_QUERY, int pos, byte reg_num);
+static /* 0x0F */ DECLARE_CMD(FXS, CHAN_ENABLE, xpp_line_t lines, bool on);
+static /* 0x0F */ DECLARE_CMD(FXS, CHAN_CID, xpp_line_t lines);
+static /* 0x0F */ DECLARE_CMD(FXS, RING, int pos, bool on);
+static /* 0x0F */ DECLARE_CMD(FXS, SETHOOK, int pos, bool offhook);
+static /* 0x0F */ DECLARE_CMD(FXS, RELAY_OUT, byte which, bool on);
+static /* 0x0F */ DECLARE_CMD(FXS, SLIC_INIT);
+static /* 0x0F */ DECLARE_CMD(FXS, SLIC_QUERY, int pos, byte reg_num);
static bool fxs_packet_is_valid(xpacket_t *pack);
static void fxs_packet_dump(xpacket_t *pack);
+static int proc_fxs_info_read(char *page, char **start, off_t off, int count, int *eof, void *data);
static int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data);
static int proc_xpd_slic_write(struct file *file, const char __user *buffer, unsigned long count, void *data);
@@ -71,17 +80,180 @@ struct slic_init_data {
xpp_line_t lines;
slic_data_t slic_data;
} slic_init_data[] = {
-#include "slic_init.inc"
+#ifdef OLD_CARD
+#include "init_data_3_19.inc"
+#else
+#include "init_data_3_20.inc"
+#endif
};
#undef S_
#define PROC_SLIC_FNAME "slics"
+#define PROC_FXS_INFO_FNAME "fxs_info"
struct FXS_priv_data {
- struct proc_dir_entry *xpd_slic;
- slic_reply_t last_reply;
+ struct proc_dir_entry *xpd_slic;
+ struct proc_dir_entry *fxs_info;
+ slic_reply_t requested_reply;
+ slic_reply_t last_reply;
+ xpp_line_t ledstate[NUM_LEDS]; /* 0 - OFF, 1 - ON */
+ xpp_line_t ledcontrol[NUM_LEDS]; /* 0 - OFF, 1 - ON */
+ int blinking[NUM_LEDS][CHANNELS_PERXPD];
};
+/*---------------- FXS: Static functions ----------------------------------*/
+static int do_chan_power(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, bool on)
+{
+ int ret = 0;
+ xpacket_t *pack;
+ slic_cmd_t *sc;
+ int len;
+
+ BUG_ON(!xbus);
+ BUG_ON(!xpd);
+ lines &= xpd->enabled_chans; // Ignore disabled channels
+ if(!lines) {
+ return 0;
+ }
+ DBG("%s/%s: 0x%04X %s\n", xbus->busname, xpd->xpdname, lines, (on) ? "up" : "down");
+ XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
+ sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
+ if(on) {
+ // Power up
+ len = slic_cmd_direct_write(sc, lines, 0x42, 0x06);
+ } else {
+ // Power down
+ len = slic_cmd_direct_write(sc, lines, 0x42, 0x00);
+ }
+ pack->datalen = len;
+
+ packet_send(xbus, pack);
+ return ret;
+}
+
+#define IS_BLINKING(priv,pos,color) ((priv)->blinking[color][pos] != 0)
+#define DO_BLINK(priv,pos,color,val) ((priv)->blinking[color][pos] = (val))
+#define DO_LED(priv,pos,color,val) ((val)?BIT_SET((priv)->ledcontrol[color],(pos)):BIT_CLR((priv)->ledcontrol[color],(pos)))
+
+/*
+ * LED and RELAY control is done via SLIC register 0x06:
+ * 7 6 5 4 3 2 1 0
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | M2 | M1 | M3 | C2 | O1 | O3 | C1 | C3 |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ *
+ * Cn - Control bit (control one digital line)
+ * On - Output bit (program a digital line for output)
+ * Mn - Mask bit (only the matching output control bit is affected)
+ *
+ * C3 - OUTPUT RELAY (0 - OFF, 1 - ON)
+ * C1 - GREEN LED (0 - OFF, 1 - ON)
+ * O3 - Output RELAY (this line is output)
+ * O1 - Output GREEN (this line is output)
+ * C2 - RED LED (0 - OFF, 1 - ON)
+ * M3 - Mask RELAY. (1 - C3 effect the OUTPUT RELAY)
+ * M2 - Mask RED. (1 - C2 effect the RED LED)
+ * M1 - Mask GREEN. (1 - C1 effect the GREEN LED)
+ *
+ * The OUTPUT RELAY (actually a relay out) is connected to line 0 and 4 only.
+ */
+
+// GREEN RED OUTPUT RELAY
+static const int led_register_mask[] = { BIT(7), BIT(6), BIT(5) };
+static const int led_register_vals[] = { BIT(4), BIT(1), BIT(0) };
+
+/*
+ * pos can be:
+ * - A line number
+ * - ALL_LINES
+ */
+static int do_led(xpd_t *xpd, lineno_t pos, byte which, bool on)
+{
+ unsigned long flags;
+ int ret = 0;
+ xpacket_t *pack;
+ slic_cmd_t *sc;
+ int len;
+ int value;
+ struct FXS_priv_data *priv;
+ xpp_line_t lines;
+ xbus_t *xbus;
+
+ BUG_ON(!xpd);
+ spin_lock_irqsave(&xpd->lock, flags);
+ xbus = xpd->xbus;
+ priv = xpd->priv;
+ which = which % NUM_LEDS;
+ if(IS_SET(xpd->digital_outputs, pos) || IS_SET(xpd->digital_inputs, pos))
+ goto out;
+ if(pos == ALL_LINES) {
+ lines = ~0;
+ priv->ledstate[which] = (on) ? ~0 : 0;
+ } else {
+ lines = BIT(pos);
+ if(on) {
+ BIT_SET(priv->ledstate[which], pos);
+ } else {
+ BIT_CLR(priv->ledstate[which], pos);
+ }
+ }
+ if(!(lines & xpd->enabled_chans)) // Ignore disabled channels
+ goto out;
+ DBG("%s/%s: LED: lines=0x%04X which=%d -- %s\n", xbus->busname, xpd->xpdname, lines, which, (on) ? "on" : "off");
+ value = BIT(2) | BIT(3);
+ value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_register_mask[which]);
+ if(on)
+ value |= led_register_vals[which];
+ XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
+ sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
+ len = slic_cmd_direct_write(sc, lines, 0x06, value);
+ pack->datalen = len;
+ packet_send(xbus, pack);
+
+out:
+ spin_unlock_irqrestore(&xpd->lock, flags);
+ return ret;
+}
+
+static void handle_fxs_leds(xpd_t *xpd)
+{
+ int i;
+ unsigned long flags;
+ const enum fxs_leds colors[] = { LED_GREEN, LED_RED };
+ int color;
+ unsigned int timer_count;
+ struct FXS_priv_data *priv;
+
+ BUG_ON(!xpd);
+ priv = xpd->priv;
+ spin_lock_irqsave(&xpd->lock, flags);
+ timer_count = xpd->timer_count;
+ for(color = 0; color < ARRAY_SIZE(colors); color++) {
+ for_each_enabled_line(xpd, i) {
+ if(IS_SET(xpd->digital_outputs, i) || IS_SET(xpd->digital_inputs, i))
+ continue;
+ if(IS_BLINKING(priv, i, color)) { // Blinking
+ // led state is toggled
+ if((timer_count % LED_BLINK_PERIOD) == 0) {
+ DBG("%s/%s/%d ledstate=%s\n", xpd->xbus->busname, xpd->xpdname, i,
+ (IS_SET(priv->ledstate[color], i))?"ON":"OFF");
+ if(!IS_SET(priv->ledstate[color], i)) {
+ 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);
+}
+
/*---------------- FXS: Methods -------------------------------------------*/
static xpd_t *FXS_card_new(xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, byte revision)
@@ -100,29 +272,67 @@ static xpd_t *FXS_card_new(xbus_t *xbus, int xpd_num, const xproto_table_t *prot
xpd->digital_inputs = MASK_DIGI_INP;
}
xpd->direction = TO_PHONE;
+ xpd->revision = revision;
return xpd;
}
+static void clean_proc(xbus_t *xbus, xpd_t *xpd)
+{
+ struct FXS_priv_data *priv;
+
+ BUG_ON(!xpd);
+ priv = xpd->priv;
+#ifdef CONFIG_PROC_FS
+ if(priv->xpd_slic) {
+ DBG("Removing xpd SLIC file %s/%s\n", xbus->busname, xpd->xpdname);
+ remove_proc_entry(PROC_SLIC_FNAME, xpd->proc_xpd_dir);
+ }
+ if(priv->fxs_info) {
+ DBG("Removing xpd FXS_INFO file %s/%s\n", xbus->busname, xpd->xpdname);
+ remove_proc_entry(PROC_FXS_INFO_FNAME, xpd->proc_xpd_dir);
+ }
+#endif
+}
+
static int FXS_card_init(xbus_t *xbus, xpd_t *xpd)
{
struct FXS_priv_data *priv;
+ int ret = 0;
BUG_ON(!xpd);
priv = xpd->priv;
- CALL_PROTO(FXS, SLIC_INIT, xbus, xpd);
#ifdef CONFIG_PROC_FS
+ DBG("Creating FXS_INFO file for %s/%s\n", xbus->busname, xpd->xpdname);
+ priv->fxs_info = create_proc_read_entry(PROC_FXS_INFO_FNAME, 0444, xpd->proc_xpd_dir, proc_fxs_info_read, xpd);
+ if(!priv->fxs_info) {
+ ERR("Failed to create proc '%s' for %s/%s\n", PROC_FXS_INFO_FNAME, xbus->busname, xpd->xpdname);
+ ret = -ENOENT;
+ goto out;
+ }
DBG("Creating SLICs file for %s/%s\n", xbus->busname, xpd->xpdname);
priv->xpd_slic = create_proc_entry(PROC_SLIC_FNAME, 0644, xpd->proc_xpd_dir);
if(!priv->xpd_slic) {
ERR("Failed to create proc file for SLICs of %s/%s\n", xbus->busname, xpd->xpdname);
+ ret = -ENOENT;
goto out;
}
priv->xpd_slic->write_proc = proc_xpd_slic_write;
priv->xpd_slic->read_proc = proc_xpd_slic_read;
priv->xpd_slic->data = xpd;
-out:
#endif
- return 0;
+#ifdef HARD_CODED_INIT
+ CALL_PROTO(FXS, SLIC_INIT, xbus, xpd);
+#else
+ ret = run_initialize_registers(xpd);
+#endif
+out:
+ if(ret < 0) {
+ clean_proc(xbus, xpd);
+ ERR("%s/%s: Failed initializing registers (%d)\n", xbus->busname, xpd->xpdname, ret);
+ } else {
+ DBG("done: %s/%s\n", xbus->busname, xpd->xpdname);
+ }
+ return ret;
}
static int FXS_card_remove(xbus_t *xbus, xpd_t *xpd)
@@ -132,15 +342,111 @@ static int FXS_card_remove(xbus_t *xbus, xpd_t *xpd)
BUG_ON(!xpd);
priv = xpd->priv;
DBG("%s/%s\n", xbus->busname, xpd->xpdname);
-#ifdef CONFIG_PROC_FS
- if(priv->xpd_slic) {
- DBG("Removing xpd SLIC file %s/%s\n", xbus->busname, xpd->xpdname);
- remove_proc_entry(PROC_SLIC_FNAME, xpd->proc_xpd_dir);
+ clean_proc(xbus, xpd);
+ return 0;
+}
+
+static int FXS_card_zaptel_registration(xpd_t *xpd, bool on)
+{
+ xbus_t *xbus;
+ struct FXS_priv_data *priv;
+ int i;
+
+ BUG_ON(!xpd);
+ xbus = xpd->xbus;
+ priv = xpd->priv;
+ BUG_ON(!xbus);
+ DBG("%s/%s: %s\n", xbus->busname, xpd->xpdname, (on)?"on":"off");
+ if(on) {
+ do_led(xpd, ALL_LINES, LED_GREEN, LED_OFF);
+ for_each_enabled_line(xpd, i) {
+ DO_LED(priv,i,LED_GREEN,LED_ON);
+ mdelay(50);
+ }
+ for_each_enabled_line(xpd, i) {
+ DO_LED(priv,i,LED_GREEN,LED_OFF);
+ mdelay(50);
+ }
+ } else {
+ do_led(xpd, ALL_LINES, LED_RED, LED_OFF);
+ for_each_enabled_line(xpd, i) {
+ DO_LED(priv,i,LED_RED,LED_ON);
+ mdelay(50);
+ }
+ for_each_enabled_line(xpd, i) {
+ DO_LED(priv,i,LED_RED,LED_OFF);
+ mdelay(50);
+ }
}
-#endif
return 0;
}
+int FXS_card_sethook(xbus_t *xbus, xpd_t *xpd, int pos, int hookstate)
+{
+ int ret = 0;
+
+ DBG("%s/%s/%d: %s\n", xbus->busname, xpd->xpdname, pos, hookstate2str(hookstate));
+ switch(hookstate) {
+ /* On-hook, off-hook: The PBX is playing a phone on an FXO line.
+ * Can be ignored for an FXS line
+ */
+ case ZT_ONHOOK:
+ if(IS_SET(xpd->digital_inputs, pos)) {
+ NOTICE("%s: Trying to ONHOOK a digital input channel %d. Ignoring\n", __FUNCTION__, pos);
+ ret = -EINVAL;
+ break;
+ }
+ if(IS_SET(xpd->digital_outputs, pos)) {
+ DBG("%s/%s/%d: digital output OFF\n", xbus->busname, xpd->xpdname, pos);
+ ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 0);
+ break;
+ }
+ xpd->ringing[pos] = 0;
+ DBG("%s/%s/%d: stop ringing\n", xbus->busname, xpd->xpdname, pos);
+#if 1 // FIXME: Not needed -- verify
+ ret = CALL_XMETHOD(RING, xbus, xpd, pos, 0); // RING off
+#endif
+ if(ret) {
+ DBG("ZT_ONHOOK(stop ring) Failed: ret=0x%02X\n", ret);
+ break;
+ }
+ break;
+ case ZT_START:
+ DBG("%s/%s/%d: fall through ZT_OFFHOOK\n", xbus->busname, xpd->xpdname, pos);
+ // Fall through
+ case ZT_OFFHOOK:
+ DBG("%s/%s/%d: ignoring (PHONE)\n", xbus->busname, xpd->xpdname, pos);
+ break;
+ case ZT_WINK:
+ WARN("No code yet\n");
+ break;
+ case ZT_FLASH:
+ WARN("No code yet\n");
+ break;
+ case ZT_RING:
+ DBG("%s/%s/%d: ringing[%d]=%d\n", xbus->busname, xpd->xpdname, pos, pos, xpd->ringing[pos]);
+ if(IS_SET(xpd->digital_inputs, pos)) {
+ NOTICE("%s: Trying to RING a digital input channel %d. Ignoring\n", __FUNCTION__, pos);
+ return -EINVAL;
+ }
+ if(IS_SET(xpd->digital_outputs, pos)) {
+ DBG("%s/%s/%d: digital output ON\n", xbus->busname, xpd->xpdname, pos);
+ ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 1);
+ return ret;
+ }
+ xpd->ringing[pos] = 1;
+ ret = CALL_XMETHOD(RING, xbus, xpd, pos, 1); // RING on
+ if(ret) {
+ DBG("ZT_RING Failed: ret=0x%02X\n", ret);
+ }
+ break;
+ case ZT_RINGOFF:
+ WARN("No code yet\n");
+ break;
+ }
+ return ret;
+}
+
/*
* INPUT polling is done via SLIC register 0x06 (same as LEDS):
* 7 6 5 4 3 2 1 0
@@ -164,22 +470,29 @@ static void poll_inputs(xbus_t *xbus, xpd_t *xpd)
static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd)
{
- static int rate_limit = 0;
+ static int rate_limit = 0;
+ struct FXS_priv_data *priv;
+ BUG_ON(!xpd);
+ priv = xpd->priv;
+ BUG_ON(!priv);
if((rate_limit++ % 1000) == 0) {
poll_inputs(xbus, xpd);
}
+ handle_fxs_leds(xpd);
return 0;
}
/*---------------- FXS: HOST COMMANDS -------------------------------------*/
-/* 0x0F */ HOSTCMD(FXS, CHAN_ENABLE, xpp_line_t lines, bool on)
+static /* 0x0F */ HOSTCMD(FXS, CHAN_ENABLE, xpp_line_t lines, bool on)
{
int ret = 0;
xpacket_t *pack;
slic_cmd_t *sc;
int len;
+ enum fxs_state value = (on) ? 0x01 : 0x00;
+ int i;
BUG_ON(!xbus);
BUG_ON(!xpd);
@@ -190,47 +503,26 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd)
DBG("Channel Activation: 0x%4X %s\n", lines, (on) ? "on" : "off");
XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
- len = slic_cmd_direct_write(sc, lines, 0x40, (on)?0x01:0x00);
+ len = slic_cmd_direct_write(sc, lines, 0x40, value);
pack->datalen = len;
+ for_each_line(xpd, i)
+ xpd->lasttxhook[i] = value;
packet_send(xbus, pack);
- return ret;
-}
-
-/* 0x0F */ HOSTCMD(FXS, CHAN_POWER, xpp_line_t lines, bool on)
-{
- int ret = 0;
- xpacket_t *pack;
- slic_cmd_t *sc;
- int len;
-
- BUG_ON(!xbus);
- BUG_ON(!xpd);
- lines &= xpd->enabled_chans; // Ignore disabled channels
- if(!lines) {
- return 0;
- }
- DBG("Channel Power: 0x%04X %s\n", lines, (on) ? "up" : "down");
- XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
- sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
if(on) {
- // Power up
- len = slic_cmd_direct_write(sc, lines, 0x42, 0x06);
+ do_led(xpd, ALL_LINES, LED_GREEN, LED_ON);
} else {
- // Power down
- len = slic_cmd_direct_write(sc, lines, 0x42, 0x00);
+ do_led(xpd, ALL_LINES, LED_GREEN, LED_OFF);
}
- pack->datalen = len;
-
- packet_send(xbus, pack);
return ret;
}
-/* 0x0F */ HOSTCMD(FXS, CHAN_CID, xpp_line_t lines)
+static /* 0x0F */ HOSTCMD(FXS, CHAN_CID, xpp_line_t lines)
{
int ret = 0;
xpacket_t *pack;
slic_cmd_t *sc;
+ int i;
BUG_ON(!xbus);
BUG_ON(!xpd);
@@ -241,100 +533,56 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd)
DBG("Channel CID: 0x%04X\n", lines);
XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
- pack->datalen = slic_cmd_direct_write(sc, lines, 0x40, 0x02);
+ pack->datalen = slic_cmd_direct_write(sc, lines, 0x40, FXS_LINE_CID);
packet_send(xbus, pack);
+ for_each_line(xpd, i)
+ xpd->lasttxhook[i] = FXS_LINE_CID;
return ret;
}
-/* 0x0F */ HOSTCMD(FXS, RING, int pos, bool on)
+static /* 0x0F */ HOSTCMD(FXS, RING, int pos, bool on)
{
int ret = 0;
+ struct FXS_priv_data *priv;
xpacket_t *pack;
slic_cmd_t *sc;
xpp_line_t mask = (1 << pos);
int len;
+ enum fxs_state value = (on) ? 0x04 : 0x01;
BUG_ON(!xbus);
BUG_ON(!xpd);
+ priv = xpd->priv;
mask &= xpd->enabled_chans; // Ignore disabled channels
if(!mask) {
return 0;
}
- DBG("%s pos=%d %s\n", xpd->xpdname, pos, (on) ? "on" : "off");
+ DBG("%s/%s/%d %s\n", xbus->busname, xpd->xpdname, pos, (on) ? "on" : "off");
+ do_chan_power(xbus, xpd, BIT(pos), on); // Power up (for ring)
XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
- len = slic_cmd_direct_write(sc, mask, 0x40, (on)?0x04:0x01);
+ len = slic_cmd_direct_write(sc, mask, 0x40, value);
+ xpd->lasttxhook[pos] = value;
pack->datalen = len;
packet_send(xbus, pack);
+ if(on) {
+ DO_BLINK(priv,pos,LED_GREEN,LED_BLINK);
+ } else {
+ if(IS_BLINKING(priv, pos, LED_GREEN))
+ DO_BLINK(priv,pos,LED_GREEN,0);
+ }
return ret;
}
-/* 0x0F */ HOSTCMD(FXS, SETHOOK, xpp_line_t hook_status)
+static /* 0x0F */ HOSTCMD(FXS, SETHOOK, int pos, bool offhook)
{
- DBG("\n");
+ BUG(); // Should never be called
return 0;
}
-/*
- * LED control is done via SLIC register 0x06:
- * 7 6 5 4 3 2 1 0
- * +-----+-----+-----+-----+-----+-----+-----+-----+
- * | MR | MG | MB | R | OG | OB | G | B |
- * +-----+-----+-----+-----+-----+-----+-----+-----+
- *
- * B - BLUE LED (0 - OFF, 1 - ON)
- * G - GREEN LED (0 - OFF, 1 - ON)
- * OB - Output BLUE (this line is output)
- * OG - Output GREEN (this line is output)
- * R - RED LED (0 - OFF, 1 - ON)
- * MB - Mask BLUE. (1 - B effect the BLUE LED)
- * MR - Mask RED. (1 - R effect the RED LED)
- * MG - Mask GREEN. (1 - G effect the GREEN LED)
- *
- * The BLUE LED (actually a relay out) is connected to line 0 and 4 only.
- */
-
-// GREEN RED BLUE
-static const int led_mask[NUM_LEDS] = { BIT(7), BIT(6), BIT(5) };
-static const int led_vals[NUM_LEDS] = { BIT(4), BIT(1), BIT(0) };
-
-/* 0x0F */ HOSTCMD(FXS, LED, xpp_line_t lines, byte which, bool on)
-{
- int ret = 0;
- xpacket_t *pack;
- slic_cmd_t *sc;
- int len;
- int value;
- int i;
-
- BUG_ON(!xbus);
- BUG_ON(!xpd);
- lines &= xpd->enabled_chans; // Ignore disabled channels
- if(!lines) {
- return 0;
- }
- DBG("LED: lines=0x%04X which=%d -- %s\n", lines, which, (on) ? "on" : "off");
- which = which % NUM_LEDS;
- value = BIT(2) | BIT(3);
- value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_mask[which]);
- if(on)
- value |= led_vals[which];
- for(i = 0; i < CHANNELS_PERXPD; i++) {
- if(!IS_SET(lines, i))
- continue;
- XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
- sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
- len = slic_cmd_direct_write(sc, lines, 0x06, value);
- DBG("LED pack: line=%d value=0x%04X\n", i, value);
- pack->datalen = len;
- packet_send(xbus, pack);
- }
- return ret;
-}
-
-/* 0x0F */ HOSTCMD(FXS, RELAY_OUT, byte which, bool on)
+static /* 0x0F */ HOSTCMD(FXS, RELAY_OUT, byte which, bool on)
{
int ret = 0;
xpacket_t *pack;
@@ -351,9 +599,9 @@ static const int led_vals[NUM_LEDS] = { BIT(4), BIT(1), BIT(0) };
which = which % ARRAY_SIZE(relay_channels);
lines = BIT(relay_channels[which]);
value = BIT(2) | BIT(3);
- value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_mask[LED_BLUE]);
+ value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_register_mask[OUTPUT_RELAY]);
if(on)
- value |= led_vals[LED_BLUE];
+ value |= led_register_vals[OUTPUT_RELAY];
XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
len = slic_cmd_direct_write(sc, lines, 0x06, value);
@@ -364,7 +612,7 @@ static const int led_vals[NUM_LEDS] = { BIT(4), BIT(1), BIT(0) };
return ret;
}
-/* 0x0F */ HOSTCMD(FXS, SLIC_INIT)
+static /* 0x0F */ HOSTCMD(FXS, SLIC_INIT)
{
int ret = 0;
xpacket_t *pack;
@@ -386,12 +634,12 @@ static const int led_vals[NUM_LEDS] = { BIT(4), BIT(1), BIT(0) };
pack->datalen = sizeof(xpp_line_t) + slic->len + 1;
// dump_packet("SLIC", pack, print_dbg);
packet_send(xbus, pack);
- mdelay(10); // FIXME: check with Dima
+ mdelay(1); // FIXME: check with Dima
}
return ret;
}
-/* 0x0F */ HOSTCMD(FXS, SLIC_QUERY, int pos, byte reg_num)
+static /* 0x0F */ HOSTCMD(FXS, SLIC_QUERY, int pos, byte reg_num)
{
int ret = 0;
xpacket_t *pack;
@@ -400,7 +648,7 @@ static const int led_vals[NUM_LEDS] = { BIT(4), BIT(1), BIT(0) };
BUG_ON(!xbus);
BUG_ON(!xpd);
- DBG("\n");
+ // DBG("\n");
XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
len = slic_cmd_direct_read(sc, BIT(pos), reg_num);
@@ -416,31 +664,45 @@ static const int led_vals[NUM_LEDS] = { BIT(4), BIT(1), BIT(0) };
HANDLER_DEF(FXS, SIG_CHANGED)
{
xpp_line_t sig_status = RPACKET_FIELD(pack, FXS, SIG_CHANGED, sig_status);
+ xpp_line_t sig_toggles = RPACKET_FIELD(pack, FXS, SIG_CHANGED, sig_toggles);
+ struct FXS_priv_data *priv;
+ int i;
- if(!xpd) {
- NOTICE("%s: received %s for non-existing xpd: %d\n",
- __FUNCTION__, cmd->name, XPD_NUM(pack->content.addr));
- return -EPROTO;
+ BUG_ON(!xpd);
+ BUG_ON(xpd->direction != TO_PHONE);
+ priv = xpd->priv;
+ DBG("%s/%s: (PHONE) sig_toggles=0x%04X sig_status=0x%04X\n", xbus->busname, xpd->xpdname, sig_toggles, sig_status);
+ if(!SPAN_REGISTERED(xpd)) {
+ NOTICE("%s: %s/%s is not registered. Skipping.\n", __FUNCTION__, xbus->busname, xpd->xpdname);
+ return -ENODEV;
}
- if(xpd->direction == TO_PHONE) { /* Hook state changes */
- DBG("%s (PHONE) sig_status=0x%04X\n", xpd->xpdname, sig_status);
- xpp_check_hookstate(xpd, sig_status);
- } else { /* TO_PSTN - line ring changes */
- unsigned long flags;
- int i;
-
- DBG("%s (PSTN) sig_status=0x%04X\n", xpd->xpdname, sig_status);
- spin_lock_irqsave(&xpd->lock, flags);
- for(i = 0; i < xpd->channels; i++) {
+#if 0
+ Is this needed?
+ for_each_enabled_line(xpd, i) {
+ if(IS_SET(sig_toggles, i))
+ do_chan_power(xpd->xbus, xpd, BIT(i), 0); // Power down (prevent overheating!!!)
+ }
+#endif
+ for_each_line(xpd, i) {
+ if(IS_SET(xpd->digital_outputs, i) || IS_SET(xpd->digital_inputs, i))
+ continue;
+ if(IS_SET(sig_toggles, i)) {
+ struct zt_chan *chan = &xpd->span.chans[i];
+
+ xpd->ringing[i] = 0; // No more ringing...
+ DO_BLINK(priv,i,LED_GREEN,0);
if(IS_SET(sig_status, i)) {
- xpd->ringing[i] = RINGS_NUM*2;
- zt_hooksig(&xpd->chans[i], ZT_RXSIG_OFFHOOK);
+ DBG("OFFHOOK: channo=%d\n", chan->channo);
+ DO_LED(priv,i,LED_GREEN,LED_ON);
+ BIT_SET(xpd->hookstate, i);
+ zt_hooksig(chan, ZT_RXSIG_OFFHOOK);
} else {
- zt_hooksig(&xpd->chans[i], ZT_RXSIG_ONHOOK);
- xpd->ringing[i] = 0;
+ DBG("ONHOOK channo=%d\n", chan->channo);
+ DO_LED(priv,i,LED_GREEN,LED_OFF);
+ BIT_CLR(xpd->hookstate, i);
+ zt_hooksig(chan, ZT_RXSIG_ONHOOK);
}
}
- spin_unlock_irqrestore(&xpd->lock, flags);
}
return 0;
}
@@ -460,52 +722,71 @@ HANDLER_DEF(FXS, SLIC_REPLY)
spin_lock_irqsave(&xpd->lock, flags);
priv = xpd->priv;
BUG_ON(!priv);
+#if 0
DBG("SLIC_REPLY: xpd #%d %s reg_num=0x%X, dataL=0x%X dataH=0x%X\n",
xpd->id, (info->indirect)?"I":"D",
info->reg_num, info->data_low, info->data_high);
- priv->last_reply = *info;
+#endif
if(xpd->id == 0 && info->indirect == 0 && info->reg_num == 0x06) { /* Digital Inputs Poll Result */
int i;
bool offhook = (info->data_low & 0x1) == 0;
/* Map SLIC number into line number */
for(i = 0; i < ARRAY_SIZE(input_channels); i++) {
- int channo = input_channels[i];
- int newchanno;
+ int channo = input_channels[i];
+ int newchanno;
+ struct zt_chan *chan;
if(IS_SET(lines, channo)) {
newchanno = LINES_REGULAR + LINES_DIGI_OUT + i;
BIT_CLR(lines, channo);
BIT_SET(lines, newchanno);
- phone_hook(xpd, newchanno, offhook);
+ chan = &xpd->span.chans[newchanno];
+ xpd->ringing[newchanno] = 0; // Stop ringing. No leds for digital inputs.
+ if(offhook && !IS_SET(xpd->hookstate, newchanno)) { // OFFHOOK
+ DBG("OFFHOOK: channo=%d\n", chan->channo);
+ BIT_SET(xpd->hookstate, newchanno);
+ zt_hooksig(chan, ZT_RXSIG_OFFHOOK);
+ } else if(!offhook && IS_SET(xpd->hookstate, newchanno)) { // ONHOOK
+ DBG("ONHOOK channo=%d\n", chan->channo);
+ BIT_CLR(xpd->hookstate, newchanno);
+ zt_hooksig(chan, ZT_RXSIG_ONHOOK);
+ }
}
}
}
+
+ /* Update /proc info only if reply relate to the last slic read request */
+ if(priv->requested_reply.indirect == info->indirect &&
+ priv->requested_reply.reg_num == info->reg_num) {
+ priv->last_reply = *info;
+ }
spin_unlock_irqrestore(&xpd->lock, flags);
return 0;
}
xproto_table_t PROTO_TABLE(FXS) = {
+ .owner = THIS_MODULE,
.entries = {
/* Card Opcode */
XENTRY( FXS, SIG_CHANGED ),
XENTRY( FXS, SLIC_REPLY ),
},
.name = "FXS",
- .type = XPD_TYPE(FXS),
+ .type = XPD_TYPE_FXS,
.xops = {
.card_new = FXS_card_new,
.card_init = FXS_card_init,
.card_remove = FXS_card_remove,
+ .card_zaptel_registration = FXS_card_zaptel_registration,
+ .card_sethook = FXS_card_sethook,
.card_tick = FXS_card_tick,
.RING = XPROTO_CALLER(FXS, RING),
.SETHOOK = XPROTO_CALLER(FXS, SETHOOK),
- .LED = XPROTO_CALLER(FXS, LED),
.RELAY_OUT = XPROTO_CALLER(FXS, RELAY_OUT),
.CHAN_ENABLE = XPROTO_CALLER(FXS, CHAN_ENABLE),
- .CHAN_POWER = XPROTO_CALLER(FXS, CHAN_POWER),
.CHAN_CID = XPROTO_CALLER(FXS, CHAN_CID),
.SYNC_SOURCE = XPROTO_CALLER(GLOBAL, SYNC_SOURCE),
@@ -519,7 +800,7 @@ static bool fxs_packet_is_valid(xpacket_t *pack)
{
const xproto_entry_t *xe;
- DBG("\n");
+ // DBG("\n");
xe = xproto_card_entry(&PROTO_TABLE(FXS), pack->content.opcode);
return xe != NULL;
}
@@ -531,6 +812,56 @@ static void fxs_packet_dump(xpacket_t *pack)
/*------------------------- SLIC Handling --------------------------*/
+static int proc_fxs_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 FXS_priv_data *priv;
+ int i;
+ int led;
+
+ BUG_ON(!xpd);
+ 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(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i))
+ len += sprintf(page + len, "%d ", i % 10);
+ }
+ len += sprintf(page + len, "\n");
+ for(led = 0; led < NUM_LEDS; led++) {
+ len += sprintf(page + len, "LED #%d", led);
+ len += sprintf(page + len, "\n\t%-17s: ", "ledstate");
+ for_each_line(xpd, i) {
+ if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i))
+ len += sprintf(page + len, "%d ", IS_SET(priv->ledstate[led], i));
+ }
+ len += sprintf(page + len, "\n\t%-17s: ", "ledcontrol");
+ for_each_line(xpd, i) {
+ if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i))
+ len += sprintf(page + len, "%d ", IS_SET(priv->ledcontrol[led], 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, "%d ", IS_BLINKING(priv,i,led));
+ }
+ 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;
+}
+
static int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len = 0;
@@ -570,7 +901,7 @@ static int proc_xpd_slic_read(char *page, char **start, off_t off, int count, in
* |
* SLIC #
*/
-static int parse_slic_cmd(const char *buf, slic_cmd_t *sc)
+static int parse_slic_cmd(const char *buf, slic_cmd_t *sc, slic_reply_t *requested_reply)
{
char op; /* [W]rite, [R]ead */
char reg_type; /* [D]irect, [I]ndirect */
@@ -588,9 +919,17 @@ static int parse_slic_cmd(const char *buf, slic_cmd_t *sc)
if(reg_type == 'D' && ret == 7) {
// DBG("0x%X 0x%X 0x%X 0x%X %c %x\n", s1, s2, s3, s4, reg_type, reg_num);
ret = slic_cmd_direct_read(sc, lines, reg_num);
+ if(requested_reply) {
+ requested_reply->indirect = 0;
+ requested_reply->reg_num = reg_num;
+ }
} else if(reg_type == 'I' && ret == 7) {
// DBG("0x%X 0x%X 0x%X 0x%X %c %x\n", s1, s2, s3, s4, reg_type, reg_num);
ret = slic_cmd_indirect_read(sc, lines, reg_num);
+ if(requested_reply) {
+ requested_reply->indirect = 1;
+ requested_reply->reg_num = reg_num;
+ }
} else {
NOTICE("%s: Bad read input: ret=%d buf='%s' reg_type=%c\n", __FUNCTION__, ret, buf, reg_type);
goto err;
@@ -619,14 +958,16 @@ err:
static int process_slic_cmdline(xpd_t *xpd, char *cmdline)
{
- xbus_t *xbus;
- slic_cmd_t sc;
- xpacket_t *pack;
- char *p;
- int len = strlen(cmdline);
+ xbus_t *xbus;
+ struct FXS_priv_data *priv;
+ slic_cmd_t sc;
+ xpacket_t *pack;
+ char *p;
+ int len = strlen(cmdline);
BUG_ON(!xpd);
xbus = xpd->xbus;
+ priv = xpd->priv;
if((p = strchr(cmdline, '#')) != NULL) /* Truncate comments */
*p = '\0';
if((p = strchr(cmdline, ';')) != NULL) /* Truncate comments */
@@ -635,7 +976,7 @@ static int process_slic_cmdline(xpd_t *xpd, char *cmdline)
;
if(*p == '\0')
return 0;
- len = parse_slic_cmd(p, &sc);
+ len = parse_slic_cmd(p, &sc, &priv->requested_reply);
if(len < 0)
return len;
sc.lines &= xpd->enabled_chans; // Ignore disabled channels
@@ -677,6 +1018,7 @@ static int proc_xpd_slic_write(struct file *file, const char __user *buffer, uns
ret = process_slic_cmdline(xpd, buf);
if(ret < 0)
return ret;
+ mdelay(1);
}
return count;
}
@@ -684,7 +1026,7 @@ static int proc_xpd_slic_write(struct file *file, const char __user *buffer, uns
int __init card_fxs_startup(void)
{
- INFO("%s revision %s\n", THIS_MODULE->name, revision);
+ INFO("%s revision %s\n", THIS_MODULE->name, ZAPTEL_VERSION);
xproto_register(&PROTO_TABLE(FXS));
return 0;
}
@@ -697,7 +1039,8 @@ void __exit card_fxs_cleanup(void)
MODULE_DESCRIPTION("XPP FXS Card Driver");
MODULE_AUTHOR("Oron Peled <oron@actcom.co.il>");
MODULE_LICENSE("GPL");
-MODULE_VERSION("$Id$");
+MODULE_VERSION(ZAPTEL_VERSION);
+MODULE_ALIAS_XPD(XPD_TYPE_FXS);
module_init(card_fxs_startup);
module_exit(card_fxs_cleanup);