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:
committed by
Tzafrir Cohen
parent
a36bd8863e
commit
15c12a08f8
@@ -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))
|
||||
|
||||
@@ -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,
|
||||
|
||||
387
drivers/dahdi/xpp/card_echo.c
Normal file
387
drivers/dahdi/xpp/card_echo.c
Normal 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);
|
||||
31
drivers/dahdi/xpp/card_echo.h
Normal file
31
drivers/dahdi/xpp/card_echo.h
Normal 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 */
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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: */
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user