diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/dahdi/xpp/Kbuild | 2 | ||||
-rw-r--r-- | drivers/dahdi/xpp/card_bri.c | 30 | ||||
-rw-r--r-- | drivers/dahdi/xpp/card_echo.c | 387 | ||||
-rw-r--r-- | drivers/dahdi/xpp/card_echo.h | 31 | ||||
-rw-r--r-- | drivers/dahdi/xpp/card_fxo.c | 2 | ||||
-rw-r--r-- | drivers/dahdi/xpp/card_fxs.c | 2 | ||||
-rw-r--r-- | drivers/dahdi/xpp/card_pri.c | 35 | ||||
-rw-r--r-- | drivers/dahdi/xpp/xbus-core.c | 48 | ||||
-rw-r--r-- | drivers/dahdi/xpp/xbus-core.h | 24 | ||||
-rw-r--r-- | drivers/dahdi/xpp/xbus-pcm.c | 26 | ||||
-rw-r--r-- | drivers/dahdi/xpp/xbus-pcm.h | 2 | ||||
-rw-r--r-- | drivers/dahdi/xpp/xbus-sysfs.c | 97 | ||||
-rw-r--r-- | drivers/dahdi/xpp/xpd.h | 3 | ||||
-rw-r--r-- | drivers/dahdi/xpp/xpp_dahdi.c | 93 | ||||
-rw-r--r-- | drivers/dahdi/xpp/xpp_dahdi.h | 5 | ||||
-rwxr-xr-x | drivers/dahdi/xpp/xpp_debug | 2 | ||||
-rw-r--r-- | drivers/dahdi/xpp/xproto.c | 2 | ||||
-rw-r--r-- | drivers/dahdi/xpp/xproto.h | 4 |
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; |