xpd_echo: XPP Octasic echo canceler module

* xpd_echo (card_echo.c) - a module to handle an Astribank hardware echo
  canceller module.
* All other XPDs are now of type 'telephony_device'. Only a telephony device
  XPD provides a span to register.
* The EC module will typically show up as XPD-40 and will always show up as
  Unregistered in 'dahdi_hardware -v'

Signed-off-by: Oron Peled <oron.peled@xorcom.com>
Signed-off-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com>


git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@9993 a0bf4364-ded3-4de4-8d8a-66a801d63aff
This commit is contained in:
Oron Peled
2011-06-28 18:23:00 +00:00
committed by Tzafrir Cohen
parent a36bd8863e
commit 15c12a08f8
18 changed files with 794 additions and 1 deletions

View File

@@ -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))

View File

@@ -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,

View File

@@ -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);

View File

@@ -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 */

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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);

View File

@@ -155,6 +155,28 @@ xbus_t *get_xbus(const char *msg, uint num);
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
*/
@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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)

View File

@@ -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;

View File

@@ -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)

View File

@@ -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);

View File

@@ -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() {

View File

@@ -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: */

View File

@@ -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;