summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/dahdi/xpp/Kbuild2
-rw-r--r--drivers/dahdi/xpp/card_bri.c30
-rw-r--r--drivers/dahdi/xpp/card_echo.c387
-rw-r--r--drivers/dahdi/xpp/card_echo.h31
-rw-r--r--drivers/dahdi/xpp/card_fxo.c2
-rw-r--r--drivers/dahdi/xpp/card_fxs.c2
-rw-r--r--drivers/dahdi/xpp/card_pri.c35
-rw-r--r--drivers/dahdi/xpp/xbus-core.c48
-rw-r--r--drivers/dahdi/xpp/xbus-core.h24
-rw-r--r--drivers/dahdi/xpp/xbus-pcm.c26
-rw-r--r--drivers/dahdi/xpp/xbus-pcm.h2
-rw-r--r--drivers/dahdi/xpp/xbus-sysfs.c97
-rw-r--r--drivers/dahdi/xpp/xpd.h3
-rw-r--r--drivers/dahdi/xpp/xpp_dahdi.c93
-rw-r--r--drivers/dahdi/xpp/xpp_dahdi.h5
-rwxr-xr-xdrivers/dahdi/xpp/xpp_debug2
-rw-r--r--drivers/dahdi/xpp/xproto.c2
-rw-r--r--drivers/dahdi/xpp/xproto.h4
18 files changed, 794 insertions, 1 deletions
diff --git a/drivers/dahdi/xpp/Kbuild b/drivers/dahdi/xpp/Kbuild
index 13c23f5..8fa83ea 100644
--- a/drivers/dahdi/xpp/Kbuild
+++ b/drivers/dahdi/xpp/Kbuild
@@ -13,6 +13,7 @@ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_XPD_FXS) += xpd_fxs.o
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_XPD_FXO) += xpd_fxo.o
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_XPD_PRI) += xpd_pri.o
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_XPD_BRI) += xpd_bri.o
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_XPD_ECHO) += xpd_echo.o
# Build only supported modules
ifneq (,$(filter y m,$(CONFIG_USB)))
@@ -27,6 +28,7 @@ xpd_fxs-objs += card_fxs.o
xpd_fxo-objs += card_fxo.o
xpd_bri-objs += card_bri.o
xpd_pri-objs += card_pri.o
+xpd_echo-objs += card_echo.o
xpp_mmap-objs += mmapbus.o mmapdrv.o
ifeq (y,$(PARPORT_DEBUG))
diff --git a/drivers/dahdi/xpp/card_bri.c b/drivers/dahdi/xpp/card_bri.c
index f3b5565..86f0534 100644
--- a/drivers/dahdi/xpp/card_bri.c
+++ b/drivers/dahdi/xpp/card_bri.c
@@ -863,6 +863,8 @@ static const struct dahdi_span_ops BRI_span_ops = {
.hooksig = xpp_hooksig, /* Only with RBS bits */
.ioctl = xpp_ioctl,
.maint = xpp_maint,
+ .echocan_create = xpp_echocan_create,
+ .echocan_name = xpp_echocan_name,
#ifdef DAHDI_SYNC_TICK
.sync_tick = dahdi_sync_tick,
#endif
@@ -1428,7 +1430,33 @@ static void BRI_card_pcm_tospan(xpd_t *xpd, xpacket_t *pack)
}
}
+int BRI_echocancel_timeslot(xpd_t *xpd, int pos)
+{
+ return xpd->addr.subunit * 4 + pos;
+}
+
+static int BRI_echocancel_setmask(xpd_t *xpd, xpp_line_t ec_mask)
+{
+ struct BRI_priv_data *priv;
+ int i;
+
+ BUG_ON(!xpd);
+ priv = xpd->priv;
+ BUG_ON(!priv);
+ XPD_DBG(GENERAL, xpd, "0x%8X\n", ec_mask);
+ if (!ECHOOPS(xpd->xbus)) {
+ XPD_DBG(GENERAL, xpd,
+ "No echo canceller in XBUS: Doing nothing.\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < PHONEDEV(xpd).channels - 1; i++) {
+ int on = BIT(i) & ec_mask;
+ CALL_EC_METHOD(ec_set, xpd->xbus, xpd, i, on);
+ }
+ CALL_EC_METHOD(ec_update, xpd->xbus, xpd->xbus);
+ return 0;
+}
/*---------------- BRI: HOST COMMANDS -------------------------------------*/
@@ -1670,6 +1698,8 @@ static const struct phoneops bri_phoneops = {
.card_pcm_fromspan = BRI_card_pcm_fromspan,
.card_pcm_tospan = BRI_card_pcm_tospan,
.card_timing_priority = generic_timing_priority,
+ .echocancel_timeslot = BRI_echocancel_timeslot,
+ .echocancel_setmask = BRI_echocancel_setmask,
.card_ioctl = BRI_card_ioctl,
.card_open = BRI_card_open,
.card_close = BRI_card_close,
diff --git a/drivers/dahdi/xpp/card_echo.c b/drivers/dahdi/xpp/card_echo.c
new file mode 100644
index 0000000..cdd2ce2
--- /dev/null
+++ b/drivers/dahdi/xpp/card_echo.c
@@ -0,0 +1,387 @@
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * Copyright (C) 2011, 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 "card_echo.h"
+#include "xpp_dahdi.h"
+#include "dahdi_debug.h"
+#include "xpd.h"
+#include "xbus-core.h"
+
+static const char rcsid[] = "$Id$";
+
+/* must be before dahdi_debug.h: */
+static DEF_PARM(int, debug, 0, 0644, "Print DBG statements");
+
+/*---------------- ECHO Protocol Commands ----------------------------------*/
+
+static bool echo_packet_is_valid(xpacket_t *pack);
+static void echo_packet_dump(const char *msg, xpacket_t *pack);
+
+DEF_RPACKET_DATA(ECHO, SET,
+ byte timeslots[ECHO_TIMESLOTS];
+ );
+
+DEF_RPACKET_DATA(ECHO, SET_REPLY,
+ byte status;
+ byte reserved;
+ );
+
+struct ECHO_priv_data {
+};
+
+static xproto_table_t PROTO_TABLE(ECHO);
+
+/*---------------- ECHO: Methods -------------------------------------------*/
+
+static xpd_t *ECHO_card_new(xbus_t *xbus, int unit, int subunit,
+ const xproto_table_t *proto_table, byte subtype,
+ int subunits, int subunit_ports, bool to_phone)
+{
+ xpd_t *xpd = NULL;
+ struct ECHO_priv_data *priv;
+ int channels = 0;
+
+ if (subunit_ports != 1) {
+ XBUS_ERR(xbus, "Bad subunit_ports=%d\n", subunit_ports);
+ return NULL;
+ }
+ XBUS_DBG(GENERAL, xbus, "\n");
+ xpd = xpd_alloc(xbus, unit, subunit, subtype, subunits,
+ sizeof(struct ECHO_priv_data), proto_table, channels);
+ if (!xpd)
+ return NULL;
+ xpd->type_name = "ECHO";
+ priv = xpd->priv;
+ return xpd;
+}
+
+static int ECHO_card_init(xbus_t *xbus, xpd_t *xpd)
+{
+ struct ECHO_priv_data *priv;
+ xproto_table_t *proto_table;
+ int ret = 0;
+
+ BUG_ON(!xpd);
+ XPD_DBG(GENERAL, xpd, "\n");
+ xpd->type = XPD_TYPE_ECHO;
+ proto_table = &PROTO_TABLE(ECHO);
+ priv = xpd->priv;
+ XPD_DBG(DEVICES, xpd, "%s\n", xpd->type_name);
+ ret = CALL_EC_METHOD(ec_update, xbus, xbus);
+ return ret;
+}
+
+static int ECHO_card_remove(xbus_t *xbus, xpd_t *xpd)
+{
+ struct ECHO_priv_data *priv;
+
+ BUG_ON(!xpd);
+ priv = xpd->priv;
+ XPD_DBG(GENERAL, xpd, "\n");
+ return 0;
+}
+
+static int ECHO_card_tick(xbus_t *xbus, xpd_t *xpd)
+{
+ struct ECHO_priv_data *priv;
+
+ BUG_ON(!xpd);
+ priv = xpd->priv;
+ BUG_ON(!priv);
+ return 0;
+}
+
+static int ECHO_card_register_reply(xbus_t *xbus, xpd_t *xpd, reg_cmd_t *info)
+{
+ unsigned long flags;
+ struct xpd_addr addr;
+ xpd_t *orig_xpd;
+ byte regnum;
+ byte data_low;
+
+ /* Map UNIT + PORTNUM to XPD */
+ orig_xpd = xpd;
+ addr.unit = orig_xpd->addr.unit;
+ addr.subunit = info->portnum;
+ regnum = REG_FIELD(info, regnum);
+ data_low = REG_FIELD(info, data_low);
+ xpd = xpd_byaddr(xbus, addr.unit, addr.subunit);
+ if (!xpd) {
+ static int rate_limit;
+
+ if ((rate_limit++ % 1003) < 5)
+ notify_bad_xpd(__func__, xbus, addr,
+ orig_xpd->xpdname);
+ return -EPROTO;
+ }
+ spin_lock_irqsave(&xpd->lock, flags);
+ /* Update /proc info only if reply related to last reg 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;
+ }
+ spin_unlock_irqrestore(&xpd->lock, flags);
+ return 0;
+}
+
+/*---------------- ECHO: HOST COMMANDS -------------------------------------*/
+
+static /* 0x39 */ HOSTCMD(ECHO, SET)
+{
+ struct xbus_echo_state *es;
+ byte *ts;
+ xframe_t *xframe;
+ xpacket_t *pack;
+ int ret;
+ uint16_t frm_len;
+ int xpd_idx;
+
+ BUG_ON(!xbus);
+ /*
+ * Find echo canceller XPD address
+ */
+ es = &xbus->echo_state;
+ xpd_idx = es->xpd_idx;
+ XFRAME_NEW_CMD(xframe, pack, xbus, ECHO, SET, xpd_idx);
+ ts = RPACKET_FIELD(pack, ECHO, SET, timeslots);
+ memcpy(ts, es->timeslots, ECHO_TIMESLOTS);
+ frm_len = XFRAME_LEN(xframe);
+ XBUS_DBG(GENERAL, xbus, "ECHO SET: (len = %d)\n", frm_len);
+ ret = send_cmd_frame(xbus, xframe);
+ return ret;
+}
+
+static int ECHO_ec_set(xpd_t *xpd, int pos, bool on)
+{
+ int ts_number;
+ int ts_mask;
+ byte *ts;
+
+ ts = xpd->xbus->echo_state.timeslots;
+ /*
+ * ts_number = PCM time slot ("channel number" in the PCM XPP packet)
+ *
+ * Bit 0 is for UNIT=0
+ * PRI: ts_number * 4 + SUBUNIT
+ * BRI: ts_number
+ * FXS/FXO(all units): UNIT * 32 + ts_number
+ *
+ * Bit 1 is for UNIT=1-3: FXS/FXO
+ *
+ */
+ ts_mask = (xpd->addr.unit == 0) ? 0x1 : 0x2; /* Which bit? */
+ ts_number = CALL_PHONE_METHOD(echocancel_timeslot, xpd, pos);
+ if (ts_number >= ECHO_TIMESLOTS || ts_number < 0) {
+ XPD_ERR(xpd, "Bad ts_number=%d\n", ts_number);
+ return -EINVAL;
+ } else {
+ if (on)
+ ts[ts_number] |= ts_mask;
+ else
+ ts[ts_number] &= ~ts_mask;
+ }
+ LINE_DBG(GENERAL, xpd, pos, "%s = %d -- ts_number=%d ts_mask=0x%X\n",
+ __func__, on, ts_number, ts_mask);
+ return 0;
+}
+
+static int ECHO_ec_get(xpd_t *xpd, int pos)
+{
+ int ts_number;
+ int ts_mask;
+ int is_on;
+ byte *ts;
+
+ ts = xpd->xbus->echo_state.timeslots;
+ ts_mask = (xpd->addr.unit == 0) ? 0x1 : 0x2; /* Which bit? */
+ ts_number = CALL_PHONE_METHOD(echocancel_timeslot, xpd, pos);
+ if (ts_number >= ECHO_TIMESLOTS || ts_number < 0) {
+ XPD_ERR(xpd, "Bad ts_number=%d\n", ts_number);
+ return -EINVAL;
+ } else {
+ is_on = ts[ts_number] & ts_mask;
+ }
+#if 0
+ LINE_DBG(GENERAL, xpd, pos, "ec_get=%d -- ts_number=%d ts_mask=0x%X\n",
+ is_on, ts_number, ts_mask);
+#endif
+ return is_on;
+}
+
+static void ECHO_ec_dump(xbus_t *xbus)
+{
+ byte *ts;
+ int i;
+
+ ts = xbus->echo_state.timeslots;
+ for (i = 0; i + 15 < ECHO_TIMESLOTS; i += 16) {
+ XBUS_DBG(GENERAL, xbus,
+ "EC-DUMP[%03d]: "
+ "0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X "
+ "0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
+ i,
+ ts[i+0], ts[i+1], ts[i+2], ts[i+3], ts[i+4], ts[i+5],
+ ts[i+6], ts[i+7],
+ ts[i+8], ts[i+9], ts[i+10], ts[i+11], ts[i+12],
+ ts[i+13], ts[i+14], ts[i+15]
+ );
+ }
+}
+
+static int ECHO_ec_update(xbus_t *xbus)
+{
+ XBUS_DBG(GENERAL, xbus, "%s\n", __func__);
+ //ECHO_ec_dump(xbus);
+ return CALL_PROTO(ECHO, SET, xbus, NULL);
+}
+
+/*---------------- ECHO: Astribank Reply Handlers --------------------------*/
+HANDLER_DEF(ECHO, SET_REPLY)
+{
+ byte status;
+
+ BUG_ON(!xpd);
+ status = RPACKET_FIELD(pack, ECHO, SET_REPLY, status);
+ XPD_DBG(GENERAL, xpd, "status=0x%X\n", status);
+ return 0;
+}
+
+static const struct xops echo_xops = {
+ .card_new = ECHO_card_new,
+ .card_init = ECHO_card_init,
+ .card_remove = ECHO_card_remove,
+ .card_tick = ECHO_card_tick,
+ .card_register_reply = ECHO_card_register_reply,
+};
+
+static const struct echoops echoops = {
+ .ec_set = ECHO_ec_set,
+ .ec_get = ECHO_ec_get,
+ .ec_update = ECHO_ec_update,
+ .ec_dump = ECHO_ec_dump,
+};
+
+static xproto_table_t PROTO_TABLE(ECHO) = {
+ .owner = THIS_MODULE,
+ .entries = {
+ /* Table Card Opcode */
+ XENTRY(ECHO, ECHO, SET_REPLY),
+ },
+ .name = "ECHO",
+ .ports_per_subunit = 1,
+ .type = XPD_TYPE_ECHO,
+ .xops = &echo_xops,
+ .echoops = &echoops,
+ .packet_is_valid = echo_packet_is_valid,
+ .packet_dump = echo_packet_dump,
+};
+
+static bool echo_packet_is_valid(xpacket_t *pack)
+{
+ const xproto_entry_t *xe = NULL;
+ // DBG(GENERAL, "\n");
+ xe = xproto_card_entry(&PROTO_TABLE(ECHO), XPACKET_OP(pack));
+ return xe != NULL;
+}
+
+static void echo_packet_dump(const char *msg, xpacket_t *pack)
+{
+ DBG(GENERAL, "%s\n", msg);
+}
+
+/*------------------------- sysfs stuff --------------------------------*/
+static int echo_xpd_probe(struct device *dev)
+{
+ xpd_t *ec_xpd;
+ int ret = 0;
+
+ ec_xpd = dev_to_xpd(dev);
+ /* Is it our device? */
+ if (ec_xpd->type != XPD_TYPE_ECHO) {
+ XPD_ERR(ec_xpd, "drop suggestion for %s (%d)\n",
+ dev_name(dev), ec_xpd->type);
+ return -EINVAL;
+ }
+ XPD_DBG(DEVICES, ec_xpd, "SYSFS\n");
+ return ret;
+}
+
+static int echo_xpd_remove(struct device *dev)
+{
+ xpd_t *ec_xpd;
+
+ ec_xpd = dev_to_xpd(dev);
+ XPD_DBG(DEVICES, ec_xpd, "SYSFS\n");
+ return 0;
+}
+
+static struct xpd_driver echo_driver = {
+ .type = XPD_TYPE_ECHO,
+ .driver = {
+ .name = "echo",
+#ifndef OLD_HOTPLUG_SUPPORT
+ .owner = THIS_MODULE,
+#endif
+ .probe = echo_xpd_probe,
+ .remove = echo_xpd_remove
+ }
+};
+
+static int __init card_echo_startup(void)
+{
+ int ret;
+
+ ret = xpd_driver_register(&echo_driver.driver);
+ if (ret < 0)
+ return ret;
+ INFO("revision %s\n", XPP_VERSION);
+ INFO("FEATURE: WITH Octasic echo canceller\n");
+ xproto_register(&PROTO_TABLE(ECHO));
+ return 0;
+}
+
+static void __exit card_echo_cleanup(void)
+{
+ DBG(GENERAL, "\n");
+ xproto_unregister(&PROTO_TABLE(ECHO));
+ xpd_driver_unregister(&echo_driver.driver);
+}
+
+MODULE_DESCRIPTION("XPP ECHO Card Driver");
+MODULE_AUTHOR("Oron Peled <oron@actcom.co.il>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(XPP_VERSION);
+MODULE_ALIAS_XPD(XPD_TYPE_ECHO);
+
+module_init(card_echo_startup);
+module_exit(card_echo_cleanup);
diff --git a/drivers/dahdi/xpp/card_echo.h b/drivers/dahdi/xpp/card_echo.h
new file mode 100644
index 0000000..81fea05
--- /dev/null
+++ b/drivers/dahdi/xpp/card_echo.h
@@ -0,0 +1,31 @@
+#ifndef CARD_ECHO_H
+#define CARD_ECHO_H
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * Copyright (C) 2011, 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 "xpd.h"
+
+enum echo_opcodes {
+ XPROTO_NAME(ECHO, SET) = 0x39,
+ XPROTO_NAME(ECHO, SET_REPLY) = 0x3A,
+};
+
+#endif /* CARD_ECHO_H */
diff --git a/drivers/dahdi/xpp/card_fxo.c b/drivers/dahdi/xpp/card_fxo.c
index f533ac6..673e7d6 100644
--- a/drivers/dahdi/xpp/card_fxo.c
+++ b/drivers/dahdi/xpp/card_fxo.c
@@ -1118,6 +1118,8 @@ static const struct phoneops fxo_phoneops = {
.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,
diff --git a/drivers/dahdi/xpp/card_fxs.c b/drivers/dahdi/xpp/card_fxs.c
index 03e3d49..01d4ab7 100644
--- a/drivers/dahdi/xpp/card_fxs.c
+++ b/drivers/dahdi/xpp/card_fxs.c
@@ -1402,6 +1402,8 @@ static const struct phoneops fxs_phoneops = {
.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_open = FXS_card_open,
.card_close = FXS_card_close,
.card_ioctl = FXS_card_ioctl,
diff --git a/drivers/dahdi/xpp/card_pri.c b/drivers/dahdi/xpp/card_pri.c
index 8d7e76d..604163a 100644
--- a/drivers/dahdi/xpp/card_pri.c
+++ b/drivers/dahdi/xpp/card_pri.c
@@ -1213,6 +1213,8 @@ static const struct dahdi_span_ops PRI_span_ops = {
.close = xpp_close,
.ioctl = xpp_ioctl,
.maint = xpp_maint,
+ .echocan_create = xpp_echocan_create,
+ .echocan_name = xpp_echocan_name,
#ifdef DAHDI_SYNC_TICK
.sync_tick = dahdi_sync_tick,
#endif
@@ -1793,7 +1795,38 @@ int PRI_timing_priority(xpd_t *xpd)
return -ENOENT;
}
+static int PRI_echocancel_timeslot(xpd_t *xpd, int pos)
+{
+ /*
+ * Skip ts=0 (used for PRI sync)
+ */
+ return (1 + pos) * 4 + xpd->addr.subunit;
+}
+static int PRI_echocancel_setmask(xpd_t *xpd, xpp_line_t ec_mask)
+{
+ struct PRI_priv_data *priv;
+ int i;
+
+ BUG_ON(!xpd);
+ priv = xpd->priv;
+ BUG_ON(!priv);
+ XPD_DBG(GENERAL, xpd, "0x%8X\n", ec_mask);
+ if (!ECHOOPS(xpd->xbus)) {
+ XPD_DBG(GENERAL, xpd,
+ "No echo canceller in XBUS: Doing nothing.\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < PHONEDEV(xpd).channels; i++) {
+ int on = BIT(i) & ec_mask;
+
+ if (i == PRI_DCHAN_IDX(priv))
+ on = 0;
+ CALL_EC_METHOD(ec_set, xpd->xbus, xpd, i, on);
+ }
+ CALL_EC_METHOD(ec_update, xpd->xbus, xpd->xbus);
+ return 0;
+}
/*---------------- PRI: HOST COMMANDS -------------------------------------*/
@@ -2111,6 +2144,8 @@ static const struct phoneops pri_phoneops = {
.card_pcm_recompute = PRI_card_pcm_recompute,
.card_pcm_fromspan = PRI_card_pcm_fromspan,
.card_pcm_tospan = PRI_card_pcm_tospan,
+ .echocancel_timeslot = PRI_echocancel_timeslot,
+ .echocancel_setmask = PRI_echocancel_setmask,
.card_timing_priority = PRI_timing_priority,
.card_ioctl = PRI_card_ioctl,
.card_close = PRI_card_close,
diff --git a/drivers/dahdi/xpp/xbus-core.c b/drivers/dahdi/xpp/xbus-core.c
index d558a75..3d37f71 100644
--- a/drivers/dahdi/xpp/xbus-core.c
+++ b/drivers/dahdi/xpp/xbus-core.c
@@ -630,6 +630,7 @@ static int new_card(xbus_t *xbus,
int subunits;
int ret = 0;
int remaining_ports;
+ const struct echoops *echoops;
proto_table = xproto_get(type);
if(!proto_table) {
@@ -638,6 +639,18 @@ static int new_card(xbus_t *xbus,
unit, type);
return -EINVAL;
}
+ echoops = proto_table->echoops;
+ if (echoops) {
+ XBUS_INFO(xbus, "Detected ECHO Canceler (%d)\n", unit);
+ if (ECHOOPS(xbus)) {
+ XBUS_NOTICE(xbus,
+ "CARD %d: tryies to define echoops (type %d) but we already have one. Ignored.\n",
+ unit, type);
+ return -EINVAL;
+ }
+ xbus->echo_state.echoops = echoops;
+ xbus->echo_state.xpd_idx = XPD_IDX(unit, 0);
+ }
remaining_ports = ports;
subunits = (ports + proto_table->ports_per_subunit - 1) /
proto_table->ports_per_subunit;
@@ -748,6 +761,11 @@ static int xpd_initialize(xpd_t *xpd)
}
xpd->card_present = 1;
if (IS_PHONEDEV(xpd)) {
+ /*
+ * Set echo canceler channels (off)
+ * Asterisk will tell us when/if it's needed.
+ */
+ CALL_PHONE_METHOD(echocancel_setmask, xpd, 0);
CALL_PHONE_METHOD(card_state, xpd, 1); /* Turn on all channels */
}
if(!xpd_setstate(xpd, XPD_STATE_READY)) {
@@ -760,6 +778,34 @@ out:
return ret;
}
+static int xbus_echocancel(xbus_t *xbus, int on)
+{
+ int unit;
+ int subunit;
+ xpd_t *xpd;
+
+ if (!ECHOOPS(xbus))
+ return 0;
+ for (unit = 0; unit < MAX_UNIT; unit++) {
+ xpd = xpd_byaddr(xbus, unit, 0);
+ if (!xpd || !IS_PHONEDEV(xpd))
+ continue;
+ for (subunit = 0; subunit < MAX_SUBUNIT; subunit++) {
+ int ret;
+
+ xpd = xpd_byaddr(xbus, unit, subunit);
+ if (!xpd || !IS_PHONEDEV(xpd))
+ continue;
+ ret = echocancel_xpd(xpd, on);
+ if (ret < 0) {
+ XPD_ERR(xpd, "Fail in xbus_echocancel()\n");
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
static int xbus_initialize(xbus_t *xbus)
{
int unit;
@@ -807,6 +853,7 @@ static int xbus_initialize(xbus_t *xbus)
goto err;
}
}
+ xbus_echocancel(xbus, 1);
do_gettimeofday(&time_end);
timediff = usec_diff(&time_end, &time_start);
timediff /= 1000*100;
@@ -1154,6 +1201,7 @@ void xbus_deactivate(xbus_t *xbus)
return;
xbus_request_sync(xbus, SYNC_MODE_NONE); /* no more ticks */
elect_syncer("deactivate");
+ xbus_echocancel(xbus, 0);
xbus_request_removal(xbus);
XBUS_DBG(DEVICES, xbus, "[%s] Waiting for queues\n", xbus->label);
xbus_command_queue_clean(xbus);
diff --git a/drivers/dahdi/xpp/xbus-core.h b/drivers/dahdi/xpp/xbus-core.h
index 659d3d4..b781e94 100644
--- a/drivers/dahdi/xpp/xbus-core.h
+++ b/drivers/dahdi/xpp/xbus-core.h
@@ -156,6 +156,28 @@ void put_xbus(const char *msg, xbus_t *xbus);
int refcount_xbus(xbus_t *xbus);
/*
+ * Echo canceller related data
+ */
+#define ECHO_TIMESLOTS 128
+
+struct echoops {
+ int (*ec_set)(xpd_t *xpd, int pos, bool on);
+ int (*ec_get)(xpd_t *xpd, int pos);
+ int (*ec_update)(xbus_t *xbus);
+ void (*ec_dump)(xbus_t *xbus);
+};
+
+struct xbus_echo_state {
+ const struct echoops *echoops;
+ byte timeslots[ECHO_TIMESLOTS];
+ int xpd_idx;
+ struct device_attribute *da[MAX_XPDS];
+};
+#define ECHOOPS(xbus) ((xbus)->echo_state.echoops)
+#define EC_METHOD(name, xbus) (ECHOOPS(xbus)->name)
+#define CALL_EC_METHOD(name, xbus, ...) (EC_METHOD(name, (xbus))(__VA_ARGS__))
+
+/*
* An xbus is a transport layer for Xorcom Protocol commands
*/
struct xbus {
@@ -169,6 +191,7 @@ struct xbus {
int num;
struct xpd *xpds[MAX_XPDS];
+ struct xbus_echo_state echo_state;
int command_tick_counter;
int usec_nosend; /* Firmware flow control */
@@ -311,6 +334,7 @@ int xbus_xpd_unbind(xbus_t *xbus, xpd_t *xpd);
/* sysfs */
int xpd_device_register(xbus_t *xbus, xpd_t *xpd);
void xpd_device_unregister(xpd_t *xpd);
+int echocancel_xpd(xpd_t *xpd, int on);
int xpp_driver_init(void);
void xpp_driver_exit(void);
diff --git a/drivers/dahdi/xpp/xbus-pcm.c b/drivers/dahdi/xpp/xbus-pcm.c
index 448cf5a..a93de5e 100644
--- a/drivers/dahdi/xpp/xbus-pcm.c
+++ b/drivers/dahdi/xpp/xbus-pcm.c
@@ -935,6 +935,32 @@ out:
spin_unlock_irqrestore(&xpd->lock, flags);
}
+int generic_echocancel_timeslot(xpd_t *xpd, int pos)
+{
+ return xpd->addr.unit * 32 + pos;
+}
+EXPORT_SYMBOL(generic_echocancel_timeslot);
+
+int generic_echocancel_setmask(xpd_t *xpd, xpp_line_t ec_mask)
+{
+ int i;
+
+ BUG_ON(!xpd);
+ XPD_DBG(GENERAL, xpd, "0x%8X\n", ec_mask);
+ if (!ECHOOPS(xpd->xbus)) {
+ XPD_DBG(GENERAL, xpd,
+ "No echo canceller in XBUS: Doing nothing.\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < PHONEDEV(xpd).channels; i++) {
+ int on = BIT(i) & ec_mask;
+ CALL_EC_METHOD(ec_set, xpd->xbus, xpd, i, on);
+ }
+ CALL_EC_METHOD(ec_update, xpd->xbus, xpd->xbus);
+ return 0;
+}
+EXPORT_SYMBOL(generic_echocancel_setmask);
+
static int copy_pcm_tospan(xbus_t *xbus, xframe_t *xframe)
{
byte *xframe_end;
diff --git a/drivers/dahdi/xpp/xbus-pcm.h b/drivers/dahdi/xpp/xbus-pcm.h
index f050541..4e00b80 100644
--- a/drivers/dahdi/xpp/xbus-pcm.h
+++ b/drivers/dahdi/xpp/xbus-pcm.h
@@ -109,6 +109,8 @@ void generic_card_pcm_recompute(xpd_t *xpd, xpp_line_t pcm_mask);
void generic_card_pcm_fromspan(xpd_t *xpd, xpacket_t *pack);
void generic_card_pcm_tospan(xpd_t *xpd, xpacket_t *pack);
int generic_timing_priority(xpd_t *xpd);
+int generic_echocancel_timeslot(xpd_t *xpd, int pos);
+int generic_echocancel_setmask(xpd_t *xpd, xpp_line_t ec_mask);
void fill_beep(u_char *buf, int num, int duration);
const char *sync_mode_name(enum sync_mode mode);
void xbus_set_command_timer(xbus_t *xbus, bool on);
diff --git a/drivers/dahdi/xpp/xbus-sysfs.c b/drivers/dahdi/xpp/xbus-sysfs.c
index d983f17..daf16fc 100644
--- a/drivers/dahdi/xpp/xbus-sysfs.c
+++ b/drivers/dahdi/xpp/xbus-sysfs.c
@@ -807,6 +807,103 @@ void xpd_device_unregister(xpd_t *xpd)
dev_set_drvdata(dev, NULL);
}
+static DEVICE_ATTR_READER(echocancel_show, dev, buf)
+{
+ xpd_t *xpd;
+ unsigned long flags;
+ int len = 0;
+ xpp_line_t ec_mask = 0;
+ int i;
+ int ret;
+
+ BUG_ON(!dev);
+ xpd = dev_to_xpd(dev);
+ if (!xpd)
+ return -ENODEV;
+ if (!ECHOOPS(xpd->xbus))
+ return -ENODEV;
+ spin_lock_irqsave(&xpd->lock, flags);
+ for (i = 0; i < PHONEDEV(xpd).channels; i++) {
+ ret = CALL_EC_METHOD(ec_get, xpd->xbus, xpd, i);
+ if (ret < 0) {
+ LINE_ERR(xpd, i, "ec_get failed\n");
+ len = -ENODEV;
+ goto out;
+ }
+ if (ret)
+ ec_mask |= (1 << i);
+ }
+ len += sprintf(buf, "0x%08X\n", ec_mask);
+out:
+ spin_unlock_irqrestore(&xpd->lock, flags);
+ return len;
+}
+
+static DEVICE_ATTR_WRITER(echocancel_store, dev, buf, count)
+{
+ xpd_t *xpd;
+ char *endp;
+ unsigned long mask;
+ int channels;
+ int ret;
+
+ BUG_ON(!dev);
+ xpd = dev_to_xpd(dev);
+ XPD_DBG(GENERAL, xpd, "%s\n", buf);
+ if (!xpd)
+ return -ENODEV;
+ if (!ECHOOPS(xpd->xbus)) {
+ XPD_ERR(xpd, "No echo canceller in this XBUS\n");
+ return -ENODEV;
+ }
+ if (!IS_PHONEDEV(xpd)) {
+ XPD_ERR(xpd, "Not a phone device\n");
+ return -ENODEV;
+ }
+ channels = PHONEDEV(xpd).channels;
+ mask = simple_strtoul(buf, &endp, 0);
+ if (*endp != '\0' && *endp != '\n' && *endp != '\r') {
+ XPD_ERR(xpd, "Too many channels: %d\n", channels);
+ return -EINVAL;
+ }
+ if (mask != 0 && __ffs(mask) > channels) {
+ XPD_ERR(xpd,
+ "Channel mask (0x%lX) larger than available channels (%d)\n",
+ mask, channels);
+ return -EINVAL;
+ }
+ XPD_DBG(GENERAL, xpd, "ECHOCANCEL channels: 0x%lX\n", mask);
+ ret = CALL_PHONE_METHOD(echocancel_setmask, xpd, mask);
+ if (ret < 0) {
+ XPD_ERR(xpd, "echocancel_setmask failed\n");
+ return ret;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(echocancel, S_IRUGO | S_IWUSR, echocancel_show,
+ echocancel_store);
+
+int echocancel_xpd(xpd_t *xpd, int on)
+{
+ int ret;
+
+ XPD_DBG(GENERAL, xpd, "echocancel_xpd(%s)\n", (on) ? "on" : "off");
+ if (!on) {
+ device_remove_file(xpd->echocancel, &dev_attr_echocancel);
+ return 0;
+ }
+
+ ret = device_create_file(&xpd->xpd_dev, &dev_attr_echocancel);
+ if (ret)
+ XPD_ERR(xpd,
+ "%s: device_create_file(echocancel) failed: %d\n",
+ __func__, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL(echocancel_xpd);
+
/*--------- Sysfs Device handling ----*/
void xbus_sysfs_transport_remove(xbus_t *xbus)
diff --git a/drivers/dahdi/xpp/xpd.h b/drivers/dahdi/xpp/xpd.h
index dadbf55..8eaa8dd 100644
--- a/drivers/dahdi/xpp/xpd.h
+++ b/drivers/dahdi/xpp/xpd.h
@@ -149,6 +149,8 @@ struct phonedev {
struct dahdi_span span;
struct dahdi_chan *chans[32];
#define XPD_CHAN(xpd,chan) (PHONEDEV(xpd).chans[(chan)])
+ struct dahdi_echocan_state *ec[32];
+
int channels;
xpd_direction_t direction; /* TO_PHONE, TO_PSTN */
xpp_line_t no_pcm; /* Temporary: disable PCM (for USB-1) */
@@ -198,6 +200,7 @@ struct xpd {
#define kref_to_xpd(k) container_of(k, struct xpd, kref)
xbus_t *xbus; /* The XBUS we are connected to */
+ struct device *echocancel;
spinlock_t lock;
diff --git a/drivers/dahdi/xpp/xpp_dahdi.c b/drivers/dahdi/xpp/xpp_dahdi.c
index 89adf51..3bad387 100644
--- a/drivers/dahdi/xpp/xpp_dahdi.c
+++ b/drivers/dahdi/xpp/xpp_dahdi.c
@@ -463,6 +463,8 @@ static void phonedev_cleanup(xpd_t *xpd)
if (phonedev->chans[x]) {
KZFREE(phonedev->chans[x]);
}
+ if (phonedev->ec[x])
+ KZFREE(phonedev->ec[x]);
}
}
@@ -486,6 +488,13 @@ __must_check static int phonedev_init(xpd_t *xpd, const xproto_table_t *proto_ta
ERR("%s: Unable to allocate channel %d\n", __FUNCTION__, x);
goto err;
}
+ phonedev->ec[x] = KZALLOC(sizeof(*(phonedev->ec[x])),
+ GFP_KERNEL);
+ if (!phonedev->ec[x]) {
+ ERR("%s: Unable to allocate ec state %d\n", __func__,
+ x);
+ goto err;
+ }
}
return 0;
err:
@@ -887,6 +896,86 @@ static int xpp_watchdog(struct dahdi_span *span, int cause)
}
#endif
+/*
+ * Hardware Echo Canceller management
+ */
+static void echocan_free(struct dahdi_chan *chan,
+ struct dahdi_echocan_state *ec)
+{
+ xpd_t *xpd;
+ xbus_t *xbus;
+ int pos = chan->chanpos - 1;
+ const struct echoops *echoops;
+
+ xpd = chan->pvt;
+ xbus = xpd->xbus;
+ echoops = ECHOOPS(xbus);
+ if (!echoops)
+ return;
+ LINE_NOTICE(xpd, pos, "%s: mode=0x%X\n", __func__, ec->status.mode);
+ CALL_EC_METHOD(ec_set, xbus, xpd, pos, 0);
+ CALL_EC_METHOD(ec_update, xbus, xbus);
+}
+
+static const struct dahdi_echocan_features xpp_ec_features = {
+};
+
+static const struct dahdi_echocan_ops xpp_ec_ops = {
+ .echocan_free = echocan_free,
+};
+
+const char *xpp_echocan_name(const struct dahdi_chan *chan)
+{
+ xpd_t *xpd;
+ xbus_t *xbus;
+ int pos;
+
+ if (!chan) {
+ NOTICE("%s(NULL)\n", __func__);
+ return "XPP";
+ }
+ xpd = chan->pvt;
+ xbus = xpd->xbus;
+ pos = chan->chanpos - 1;
+ LINE_DBG(GENERAL, xpd, pos, "%s:\n", __func__);
+ if (!ECHOOPS(xbus))
+ return NULL;
+ return "XPP";
+}
+EXPORT_SYMBOL(xpp_echocan_name);
+
+int xpp_echocan_create(struct dahdi_chan *chan,
+ struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p,
+ struct dahdi_echocan_state **ec)
+{
+ xpd_t *xpd;
+ xbus_t *xbus;
+ int pos;
+ struct phonedev *phonedev;
+ const struct echoops *echoops;
+ int ret;
+
+ xpd = chan->pvt;
+ xbus = xpd->xbus;
+ pos = chan->chanpos - 1;
+ echoops = ECHOOPS(xbus);
+ if (!echoops)
+ return -ENODEV;
+ phonedev = &PHONEDEV(xpd);
+ *ec = phonedev->ec[pos];
+ (*ec)->ops = &xpp_ec_ops;
+ (*ec)->features = xpp_ec_features;
+ LINE_NOTICE(xpd, pos, "%s: (tap=%d, param_count=%d)\n",
+ __func__,
+ ecp->tap_length, ecp->param_count);
+ ret = CALL_EC_METHOD(ec_set, xbus, xpd, pos, 1);
+ CALL_EC_METHOD(ec_update, xbus, xbus);
+ return ret;
+}
+EXPORT_SYMBOL(xpp_echocan_create);
+
+
/**
* Unregister an xpd from dahdi and release related resources
* @xpd The xpd to be unregistered
@@ -940,6 +1029,8 @@ static const struct dahdi_span_ops xpp_span_ops = {
.close = xpp_close,
.ioctl = xpp_ioctl,
.maint = xpp_maint,
+ .echocan_create = xpp_echocan_create,
+ .echocan_name = xpp_echocan_name,
};
static const struct dahdi_span_ops xpp_rbs_span_ops = {
@@ -949,6 +1040,8 @@ static const struct dahdi_span_ops xpp_rbs_span_ops = {
.close = xpp_close,
.ioctl = xpp_ioctl,
.maint = xpp_maint,
+ .echocan_create = xpp_echocan_create,
+ .echocan_name = xpp_echocan_name,
};
int dahdi_register_xpd(xpd_t *xpd)
diff --git a/drivers/dahdi/xpp/xpp_dahdi.h b/drivers/dahdi/xpp/xpp_dahdi.h
index 3ab0299..01f4a24 100644
--- a/drivers/dahdi/xpp/xpp_dahdi.h
+++ b/drivers/dahdi/xpp/xpp_dahdi.h
@@ -35,6 +35,11 @@ xpd_t *xpd_alloc(xbus_t *xbus, int unit, int subunit, int subtype, int subunits,
void xpd_free(xpd_t *xpd);
void xpd_remove(xpd_t *xpd);
void update_xpd_status(xpd_t *xpd, int alarm_flag);
+const char *xpp_echocan_name(const struct dahdi_chan *chan);
+int xpp_echocan_create(struct dahdi_chan *chan,
+ struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p,
+ struct dahdi_echocan_state **ec);
void hookstate_changed(xpd_t *xpd, int pos, bool good);
int xpp_open(struct dahdi_chan *chan);
int xpp_close(struct dahdi_chan *chan);
diff --git a/drivers/dahdi/xpp/xpp_debug b/drivers/dahdi/xpp/xpp_debug
index e7fc0da..a89e0c4 100755
--- a/drivers/dahdi/xpp/xpp_debug
+++ b/drivers/dahdi/xpp/xpp_debug
@@ -3,7 +3,7 @@
# xpp_debug: Turn on/off debugging flags via /sys/module/*/parameters/debug
#
-modules="xpp xpp_usb xpd_fxs xpd_fxo xpd_bri xpd_pri"
+modules="xpp xpp_usb xpd_fxs xpd_fxo xpd_bri xpd_pri xpd_echo"
dbg_names="DEFAULT PCM LEDS SYNC SIGNAL PROC REGS DEVICES COMMANDS"
usage() {
diff --git a/drivers/dahdi/xpp/xproto.c b/drivers/dahdi/xpp/xproto.c
index 570fd85..c5c263a 100644
--- a/drivers/dahdi/xpp/xproto.c
+++ b/drivers/dahdi/xpp/xproto.c
@@ -455,6 +455,8 @@ int xproto_register(const xproto_table_t *proto_table)
CHECK_PHONEOP(phoneops, card_pcm_recompute);
CHECK_PHONEOP(phoneops, card_pcm_fromspan);
CHECK_PHONEOP(phoneops, card_pcm_tospan);
+ CHECK_PHONEOP(phoneops, echocancel_timeslot);
+ CHECK_PHONEOP(phoneops, echocancel_setmask);
CHECK_PHONEOP(phoneops, card_dahdi_preregistration);
CHECK_PHONEOP(phoneops, card_dahdi_postregistration);
/* optional method -- call after testing: */
diff --git a/drivers/dahdi/xpp/xproto.h b/drivers/dahdi/xpp/xproto.h
index 78ef980..2d5f88c 100644
--- a/drivers/dahdi/xpp/xproto.h
+++ b/drivers/dahdi/xpp/xproto.h
@@ -78,6 +78,7 @@ struct xpacket_header {
#define XPD_TYPE_FXO 2 // TO_PSTN
#define XPD_TYPE_BRI 3 // TO_PSTN/TO_PHONE (from hardware)
#define XPD_TYPE_PRI 4 // TO_PSTN/TO_PHONE (runtime)
+#define XPD_TYPE_ECHO 5 // Octasic echo canceller
#define XPD_TYPE_NOMODULE 7
typedef byte xpd_type_t;
@@ -233,6 +234,8 @@ struct phoneops {
void (*card_pcm_recompute)(xpd_t *xpd, xpp_line_t pcm_mask);
void (*card_pcm_fromspan)(xpd_t *xpd, xpacket_t *pack);
void (*card_pcm_tospan)(xpd_t *xpd, xpacket_t *pack);
+ int (*echocancel_timeslot)(xpd_t *xpd, int pos);
+ int (*echocancel_setmask)(xpd_t *xpd, xpp_line_t ec_mask);
int (*card_timing_priority)(xpd_t *xpd);
int (*card_dahdi_preregistration)(xpd_t *xpd, bool on);
int (*card_dahdi_postregistration)(xpd_t *xpd, bool on);
@@ -265,6 +268,7 @@ struct xproto_table {
xproto_entry_t entries[256]; /* Indexed by opcode */
const struct xops *xops; /* Card level operations */
const struct phoneops *phoneops; /* DAHDI operations */
+ const struct echoops *echoops; /* Echo Canceller operations */
xpd_type_t type;
byte ports_per_subunit;
const char *name;