summaryrefslogtreecommitdiff
path: root/drivers/dahdi/dahdi_echocan_sec.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dahdi/dahdi_echocan_sec.c')
-rw-r--r--drivers/dahdi/dahdi_echocan_sec.c387
1 files changed, 203 insertions, 184 deletions
diff --git a/drivers/dahdi/dahdi_echocan_sec.c b/drivers/dahdi/dahdi_echocan_sec.c
index 14af244..2f6876e 100644
--- a/drivers/dahdi/dahdi_echocan_sec.c
+++ b/drivers/dahdi/dahdi_echocan_sec.c
@@ -67,36 +67,6 @@ static int debug;
#define NONUPDATE_DWELL_TIME 600 /* 600 samples, or 75ms */
-struct echo_can_state
-{
- int tx_power;
- int rx_power;
- int clean_rx_power;
-
- int rx_power_threshold;
- int nonupdate_dwell;
-
- int16_t *tx_history; /* Last N tx samples */
- int32_t *fir_taps; /* Echo FIR taps */
- int16_t *fir_taps_short; /* Echo FIR taps, shorts instead of ints */
-
- int curr_pos;
-
- int taps;
- int tap_mask;
- int use_nlp;
- int use_suppressor;
-
- int32_t supp_test1;
- int32_t supp_test2;
- int32_t supp1;
- int32_t supp2;
-
- int32_t latest_correction; /* Indication of the magnitude of the latest
- adaption, or a code to indicate why adaption
- was skipped, for test purposes */
-};
-
/* Original parameters :
#define MIN_TX_POWER_FOR_ADAPTION 256
#define MIN_RX_POWER_FOR_ADAPTION 128
@@ -110,221 +80,270 @@ struct echo_can_state
#define MIN_RX_POWER_FOR_ADAPTION 64
*/
-static int echo_can_create(struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p,
- struct echo_can_state **ec)
+static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec);
+static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size);
+static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val);
+static void echocan_NLP_toggle(struct dahdi_echocan_state *ec, unsigned int enable);
+
+static const struct dahdi_echocan_factory my_factory = {
+ .name = "SEC",
+ .owner = THIS_MODULE,
+ .echocan_create = echo_can_create,
+};
+
+static const struct dahdi_echocan_features my_features = {
+ .NLP_toggle = 1,
+};
+
+static const struct dahdi_echocan_ops my_ops = {
+ .name = "SEC",
+ .echocan_free = echo_can_free,
+ .echocan_process = echo_can_process,
+ .echocan_traintap = echo_can_traintap,
+ .echocan_NLP_toggle = echocan_NLP_toggle,
+};
+
+struct ec_pvt {
+ struct dahdi_echocan_state dahdi;
+ int tx_power;
+ int rx_power;
+ int clean_rx_power;
+
+ int rx_power_threshold;
+ int nonupdate_dwell;
+
+ int16_t *tx_history; /* Last N tx samples */
+ int32_t *fir_taps; /* Echo FIR taps */
+ int16_t *fir_taps_short; /* Echo FIR taps, shorts instead of ints */
+
+ int curr_pos;
+
+ int taps;
+ int tap_mask;
+ int use_nlp;
+ int use_suppressor;
+
+ int32_t supp_test1;
+ int32_t supp_test2;
+ int32_t supp1;
+ int32_t supp2;
+
+ int32_t latest_correction; /* Indication of the magnitude of the latest
+ adaption, or a code to indicate why adaption
+ was skipped, for test purposes */
+};
+
+#define dahdi_to_pvt(a) container_of(a, struct ec_pvt, dahdi)
+
+static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
{
+ struct ec_pvt *pvt;
size_t size;
-
+
if (ecp->param_count > 0) {
- printk(KERN_WARNING "SEC echo canceler does not support parameters; failing request\n");
+ printk(KERN_WARNING "SEC does not support parameters; failing request\n");
return -EINVAL;
}
+
+ size = sizeof(*pvt) + ecp->tap_length * sizeof(int32_t) + ecp->tap_length * 3 * sizeof(int16_t);
- size = sizeof(**ec) + ecp->tap_length * sizeof(int32_t) + ecp->tap_length * 3 * sizeof(int16_t);
-
- if (!(*ec = kmalloc(size, GFP_KERNEL)))
+ pvt = kzalloc(size, GFP_KERNEL);
+ if (!pvt)
return -ENOMEM;
-
- memset(*ec, 0, size);
-
- (*ec)->taps = ecp->tap_length;
- (*ec)->tap_mask = ecp->tap_length - 1;
- (*ec)->tx_history = (int16_t *) (*ec + sizeof(**ec));
- (*ec)->fir_taps = (int32_t *) (*ec + sizeof(**ec) +
- ecp->tap_length * 2 * sizeof(int16_t));
- (*ec)->fir_taps_short = (int16_t *) (*ec + sizeof(**ec) +
- ecp->tap_length * sizeof(int32_t) +
- ecp->tap_length * 2 * sizeof(int16_t));
- (*ec)->rx_power_threshold = 10000000;
- (*ec)->use_suppressor = FALSE;
+
+ pvt->dahdi.ops = &my_ops;
+ pvt->dahdi.features = my_features;
+
+ pvt->taps = ecp->tap_length;
+ pvt->tap_mask = ecp->tap_length - 1;
+ pvt->tx_history = (int16_t *) (pvt + sizeof(*pvt));
+ pvt->fir_taps = (int32_t *) (pvt + sizeof(*pvt) +
+ ecp->tap_length * 2 * sizeof(int16_t));
+ pvt->fir_taps_short = (int16_t *) (pvt + sizeof(*pvt) +
+ ecp->tap_length * sizeof(int32_t) +
+ ecp->tap_length * 2 * sizeof(int16_t));
+ pvt->rx_power_threshold = 10000000;
+ pvt->use_suppressor = FALSE;
/* Non-linear processor - a fancy way to say "zap small signals, to avoid
accumulating noise". */
- (*ec)->use_nlp = TRUE;
+ pvt->use_nlp = TRUE;
+ *ec = &pvt->dahdi;
return 0;
}
-/*- End of function --------------------------------------------------------*/
-static void echo_can_free(struct echo_can_state *ec)
+static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
{
- kfree(ec);
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
+ kfree(pvt);
}
-/*- End of function --------------------------------------------------------*/
-static inline int16_t sample_update(struct echo_can_state *ec, int16_t tx, int16_t rx)
+static inline int16_t sample_update(struct ec_pvt *pvt, int16_t tx, int16_t rx)
{
- int32_t echo_value;
- int clean_rx;
- int nsuppr;
-
- ec->tx_history[ec->curr_pos] = tx;
- ec->tx_history[ec->curr_pos + ec->taps] = tx;
-
- /* Evaluate the echo - i.e. apply the FIR filter */
- /* Assume the gain of the FIR does not exceed unity. Exceeding unity
- would seem like a rather poor thing for an echo cancellor to do :)
- This means we can compute the result with a total disregard for
- overflows. 16bits x 16bits -> 31bits, so no overflow can occur in
- any multiply. While accumulating we may overflow and underflow the
- 32 bit scale often. However, if the gain does not exceed unity,
- everything should work itself out, and the final result will be
- OK, without any saturation logic. */
- /* Overflow is very much possible here, and we do nothing about it because
- of the compute costs */
- /* 16 bit coeffs for the LMS give lousy results (maths good, actual sound
- bad!), but 32 bit coeffs require some shifting. On balance 32 bit seems
- best */
+ int32_t echo_value;
+ int clean_rx;
+ int nsuppr;
+
+ pvt->tx_history[pvt->curr_pos] = tx;
+ pvt->tx_history[pvt->curr_pos + pvt->taps] = tx;
+
+ /* Evaluate the echo - i.e. apply the FIR filter */
+ /* Assume the gain of the FIR does not exceed unity. Exceeding unity
+ would seem like a rather poor thing for an echo cancellor to do :)
+ This means we can compute the result with a total disregard for
+ overflows. 16bits x 16bits -> 31bits, so no overflow can occur in
+ any multiply. While accumulating we may overflow and underflow the
+ 32 bit scale often. However, if the gain does not exceed unity,
+ everything should work itself out, and the final result will be
+ OK, without any saturation logic. */
+ /* Overflow is very much possible here, and we do nothing about it because
+ of the compute costs */
+ /* 16 bit coeffs for the LMS give lousy results (maths good, actual sound
+ bad!), but 32 bit coeffs require some shifting. On balance 32 bit seems
+ best */
#ifdef USE_SHORTS
- echo_value = CONVOLVE2(ec->fir_taps_short, ec->tx_history + ec->curr_pos, ec->taps);
+ echo_value = CONVOLVE2(pvt->fir_taps_short, pvt->tx_history + pvt->curr_pos, pvt->taps);
#else
- echo_value = CONVOLVE(ec->fir_taps, ec->tx_history + ec->curr_pos, ec->taps);
+ echo_value = CONVOLVE(pvt->fir_taps, pvt->tx_history + pvt->curr_pos, pvt->taps);
#endif
- echo_value >>= 16;
-
- /* And the answer is..... */
- clean_rx = rx - echo_value;
-
- /* That was the easy part. Now we need to adapt! */
- if (ec->nonupdate_dwell > 0)
- ec->nonupdate_dwell--;
-
- /* If there is very little being transmitted, any attempt to train is
- futile. We would either be training on the far end's noise or signal,
- the channel's own noise, or our noise. Either way, this is hardly good
- training, so don't do it (avoid trouble). */
- /* If the received power is very low, either we are sending very little or
- we are already well adapted. There is little point in trying to improve
- the adaption under these circumstanceson, so don't do it (reduce the
- compute load). */
- if (ec->tx_power > MIN_TX_POWER_FOR_ADAPTION
- &&
- ec->rx_power > MIN_RX_POWER_FOR_ADAPTION)
- {
- /* This is a really crude piece of decision logic, but it does OK
- for now. */
- if (ec->tx_power > ec->rx_power << 1)
- {
- /* There is no far-end speech detected */
- if (ec->nonupdate_dwell == 0)
- {
- /* ... and we are not in the dwell time from previous speech. */
- //nsuppr = saturate((clean_rx << 16)/ec->tx_power);
- nsuppr = (clean_rx << 16) / ec->tx_power;
- nsuppr >>= 4;
- if (nsuppr > 512)
- nsuppr = 512;
- if (nsuppr < -512)
- nsuppr = -512;
-
- /* Update the FIR taps */
- ec->latest_correction = 0;
+ echo_value >>= 16;
+
+ /* And the answer is..... */
+ clean_rx = rx - echo_value;
+
+ /* That was the easy part. Now we need to adapt! */
+ if (pvt->nonupdate_dwell > 0)
+ pvt->nonupdate_dwell--;
+
+ /* If there is very little being transmitted, any attempt to train is
+ futile. We would either be training on the far end's noise or signal,
+ the channel's own noise, or our noise. Either way, this is hardly good
+ training, so don't do it (avoid trouble). */
+ /* If the received power is very low, either we are sending very little or
+ we are already well adapted. There is little point in trying to improve
+ the adaption under these circumstanceson, so don't do it (reduce the
+ compute load). */
+ if (pvt->tx_power > MIN_TX_POWER_FOR_ADAPTION && pvt->rx_power > MIN_RX_POWER_FOR_ADAPTION) {
+ /* This is a really crude piece of decision logic, but it does OK
+ for now. */
+ if (pvt->tx_power > pvt->rx_power << 1) {
+ /* There is no far-end speech detected */
+ if (pvt->nonupdate_dwell == 0) {
+ /* ... and we are not in the dwell time from previous speech. */
+ /* nsuppr = saturate((clean_rx << 16)/pvt->tx_power); */
+ nsuppr = (clean_rx << 16) / pvt->tx_power;
+ nsuppr >>= 4;
+ if (nsuppr > 512)
+ nsuppr = 512;
+ if (nsuppr < -512)
+ nsuppr = -512;
+
+ /* Update the FIR taps */
+ pvt->latest_correction = 0;
#ifdef USE_SHORTS
- UPDATE2(ec->fir_taps, ec->fir_taps_short, ec->tx_history + ec->curr_pos, nsuppr, ec->taps);
+ UPDATE2(pvt->fir_taps, pvt->fir_taps_short, pvt->tx_history + pvt->curr_pos, nsuppr, pvt->taps);
#else
- UPDATE(ec->fir_taps, ec->fir_taps_short, ec->tx_history + ec->curr_pos, nsuppr, ec->taps);
+ UPDATE(pvt->fir_taps, pvt->fir_taps_short, pvt->tx_history + pvt->curr_pos, nsuppr, pvt->taps);
#endif
- } else
- {
- ec->latest_correction = -3;
- }
+ } else {
+ pvt->latest_correction = -3;
+ }
+ } else {
+ pvt->nonupdate_dwell = NONUPDATE_DWELL_TIME;
+ pvt->latest_correction = -2;
+ }
+ } else {
+ pvt->nonupdate_dwell = 0;
+ pvt->latest_correction = -1;
}
- else
- {
- ec->nonupdate_dwell = NONUPDATE_DWELL_TIME;
- ec->latest_correction = -2;
- }
- }
- else
- {
- ec->nonupdate_dwell = 0;
- ec->latest_correction = -1;
- }
- /* Calculate short term power levels using very simple single pole IIRs */
- /* TODO: Is the nasty modulus approach the fastest, or would a real
- tx*tx power calculation actually be faster? */
- ec->tx_power += ((abs(tx) - ec->tx_power) >> 5);
- ec->rx_power += ((abs(rx) - ec->rx_power) >> 5);
- ec->clean_rx_power += ((abs(clean_rx) - ec->clean_rx_power) >> 5);
+ /* Calculate short term power levels using very simple single pole IIRs */
+ /* TODO: Is the nasty modulus approach the fastest, or would a real
+ tx*tx power calculation actually be faster? */
+ pvt->tx_power += ((abs(tx) - pvt->tx_power) >> 5);
+ pvt->rx_power += ((abs(rx) - pvt->rx_power) >> 5);
+ pvt->clean_rx_power += ((abs(clean_rx) - pvt->clean_rx_power) >> 5);
#if defined(XYZZY)
- if (ec->use_suppressor)
- {
- ec->supp_test1 += (ec->tx_history[ec->curr_pos] - ec->tx_history[(ec->curr_pos - 7) & ec->tap_mask]);
- ec->supp_test2 += (ec->tx_history[(ec->curr_pos - 24) & ec->tap_mask] - ec->tx_history[(ec->curr_pos - 31) & ec->tap_mask]);
- if (ec->supp_test1 > 42 && ec->supp_test2 > 42)
- supp_change = 25;
- else
- supp_change = 50;
- supp = supp_change + k1*ec->supp1 + k2*ec->supp2;
- ec->supp2 = ec->supp1;
- ec->supp1 = supp;
- clean_rx *= (1 - supp);
- }
+ if (pvt->use_suppressor) {
+ pvt->supp_test1 += (pvt->tx_history[pvt->curr_pos] - pvt->tx_history[(pvt->curr_pos - 7) & pvt->tap_mask]);
+ pvt->supp_test2 += (pvt->tx_history[(pvt->curr_pos - 24) & pvt->tap_mask] - pvt->tx_history[(pvt->curr_pos - 31) & pvt->tap_mask]);
+ if (pvt->supp_test1 > 42 && pvt->supp_test2 > 42)
+ supp_change = 25;
+ else
+ supp_change = 50;
+ supp = supp_change + k1*pvt->supp1 + k2*pvt->supp2;
+ pvt->supp2 = pvt->supp1;
+ pvt->supp1 = supp;
+ clean_rx *= (1 - supp);
+ }
#endif
- if (ec->use_nlp && ec->rx_power < 32)
- clean_rx = 0;
+ if (pvt->use_nlp && pvt->rx_power < 32)
+ clean_rx = 0;
- /* Roll around the rolling buffer */
- ec->curr_pos = (ec->curr_pos - 1) & ec->tap_mask;
+ /* Roll around the rolling buffer */
+ pvt->curr_pos = (pvt->curr_pos - 1) & pvt->tap_mask;
- return clean_rx;
+ return clean_rx;
}
-/*- End of function --------------------------------------------------------*/
-static void echo_can_update(struct echo_can_state *ec, short *isig, short *iref)
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size)
{
- unsigned int x;
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+ u32 x;
short result;
- for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
- result = sample_update(ec, *iref, *isig);
+ for (x = 0; x < size; x++) {
+ result = sample_update(pvt, *iref, *isig);
*isig++ = result;
++iref;
}
}
-/*- End of function --------------------------------------------------------*/
-static int echo_can_traintap(struct echo_can_state *ec, int pos, short val)
+static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val)
{
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
/* Reset hang counter to avoid adjustments after
initial forced training */
- ec->nonupdate_dwell = ec->taps << 1;
- if (pos >= ec->taps)
+ pvt->nonupdate_dwell = pvt->taps << 1;
+ if (pos >= pvt->taps)
return 1;
- ec->fir_taps[pos] = val << 17;
- ec->fir_taps_short[pos] = val << 1;
- if (++pos >= ec->taps)
+ pvt->fir_taps[pos] = val << 17;
+ pvt->fir_taps_short[pos] = val << 1;
+ if (++pos >= pvt->taps)
return 1;
- return 0;
+ else
+ return 0;
}
-/*- End of function --------------------------------------------------------*/
-static const struct dahdi_echocan me = {
- .name = "SEC",
- .owner = THIS_MODULE,
- .echo_can_create = echo_can_create,
- .echo_can_free = echo_can_free,
- .echo_can_array_update = echo_can_update,
- .echo_can_traintap = echo_can_traintap,
-};
+static void echocan_NLP_toggle(struct dahdi_echocan_state *ec, unsigned int enable)
+{
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
+ pvt->use_nlp = enable ? 1 : 0;
+}
static int __init mod_init(void)
{
- if (dahdi_register_echocan(&me)) {
+ if (dahdi_register_echocan_factory(&my_factory)) {
module_printk(KERN_ERR, "could not register with DAHDI core\n");
return -EPERM;
}
- module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", me.name);
+ module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", my_factory.name);
return 0;
}
static void __exit mod_exit(void)
{
- dahdi_unregister_echocan(&me);
+ dahdi_unregister_echocan_factory(&my_factory);
}
module_param(debug, int, S_IRUGO | S_IWUSR);