summaryrefslogtreecommitdiff
path: root/drivers/dahdi/dahdi_echocan_sec2.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dahdi/dahdi_echocan_sec2.c')
-rw-r--r--drivers/dahdi/dahdi_echocan_sec2.c402
1 files changed, 205 insertions, 197 deletions
diff --git a/drivers/dahdi/dahdi_echocan_sec2.c b/drivers/dahdi/dahdi_echocan_sec2.c
index 5133cbf..f578f39 100644
--- a/drivers/dahdi/dahdi_echocan_sec2.c
+++ b/drivers/dahdi/dahdi_echocan_sec2.c
@@ -64,36 +64,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;
-
- fir16_state_t fir_state;
- int16_t *fir_taps16; /* 16-bit version of FIR taps */
- int32_t *fir_taps32; /* 32-bit version of FIR taps */
-
- 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 */
-};
-
/*
* According to Jim...
*/
@@ -106,231 +76,269 @@ struct echo_can_state
/* #define MIN_TX_POWER_FOR_ADAPTION 4096
#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 const struct dahdi_echocan_factory my_factory = {
+ .name = "SEC2",
+ .owner = THIS_MODULE,
+ .echocan_create = echo_can_create,
+};
+
+static const struct dahdi_echocan_ops my_ops = {
+ .name = "SEC2",
+ .echocan_free = echo_can_free,
+ .echocan_process = echo_can_process,
+ .echocan_traintap = echo_can_traintap,
+};
+
+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;
+
+ fir16_state_t fir_state;
+ int16_t *fir_taps16; /* 16-bit version of FIR taps */
+ int32_t *fir_taps32; /* 32-bit version of FIR taps */
+
+ 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-2 echo canceler does not support parameters; failing request\n");
+ printk(KERN_WARNING "SEC2 does not support parameters; failing request\n");
return -EINVAL;
}
- size = sizeof(**ec) + ecp->tap_length * sizeof(int32_t) + ecp->tap_length * 3 * sizeof(int16_t);
+ size = sizeof(*pvt) + 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)->curr_pos = ecp->tap_length - 1;
- (*ec)->tap_mask = ecp->tap_length - 1;
- (*ec)->fir_taps32 = (int32_t *) (*ec + sizeof(**ec));
- (*ec)->fir_taps16 = (int16_t *) (*ec + sizeof(**ec) + ecp->tap_length * sizeof(int32_t));
+ pvt->dahdi.ops = &my_ops;
+
+ if (ecp->param_count > 0) {
+ printk(KERN_WARNING "SEC-2 echo canceler does not support parameters; failing request\n");
+ return -EINVAL;
+ }
+
+ pvt->taps = ecp->tap_length;
+ pvt->curr_pos = ecp->tap_length - 1;
+ pvt->tap_mask = ecp->tap_length - 1;
+ pvt->fir_taps32 = (int32_t *) (pvt + sizeof(*pvt));
+ pvt->fir_taps16 = (int16_t *) (pvt + sizeof(*pvt) + ecp->tap_length * sizeof(int32_t));
/* Create FIR filter */
- fir16_create(&(*ec)->fir_state, (*ec)->fir_taps16, (*ec)->taps);
- (*ec)->rx_power_threshold = 10000000;
- (*ec)->use_suppressor = FALSE;
+ fir16_create(&pvt->fir_state, pvt->fir_taps16, pvt->taps);
+ 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 = FALSE;
+ pvt->use_nlp = FALSE;
+ *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)
{
- fir16_free(&ec->fir_state);
- kfree(ec);
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
+ fir16_free(&pvt->fir_state);
+ 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)
{
- int offset1;
- int offset2;
- int32_t echo_value;
- int clean_rx;
- int nsuppr;
- int i;
- int correction;
-
- /* 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 */
- echo_value = fir16 (&ec->fir_state, tx);
-
- /* 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 > 2*ec->rx_power)
- {
- /* 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 >> 3;
-
- /* Update the FIR taps */
- offset2 = ec->curr_pos + 1;
- offset1 = ec->taps - offset2;
- ec->latest_correction = 0;
- for (i = ec->taps - 1; i >= offset1; i--)
- {
- correction = ec->fir_state.history[i - offset1]*nsuppr;
- /* Leak to avoid false training on signals with multiple
- strong correlations. */
- ec->fir_taps32[i] -= (ec->fir_taps32[i] >> 12);
- ec->fir_taps32[i] += correction;
- ec->fir_state.coeffs[i] = ec->fir_taps32[i] >> 15;
- ec->latest_correction += abs(correction);
- }
- for ( ; i >= 0; i--)
- {
- correction = ec->fir_state.history[i + offset2]*nsuppr;
- /* Leak to avoid false training on signals with multiple
- strong correlations. */
- ec->fir_taps32[i] -= (ec->fir_taps32[i] >> 12);
- ec->fir_taps32[i] += correction;
- ec->fir_state.coeffs[i] = ec->fir_taps32[i] >> 15;
- ec->latest_correction += abs(correction);
- }
- }
- else
- {
- ec->latest_correction = -1;
- }
+ int offset1;
+ int offset2;
+ int32_t echo_value;
+ int clean_rx;
+ int nsuppr;
+ int i;
+ int correction;
+
+ /* 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 */
+ echo_value = fir16 (&pvt->fir_state, tx);
+
+ /* 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 > 2*pvt->rx_power) {
+ /* 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 >> 3;
+
+ /* Update the FIR taps */
+ offset2 = pvt->curr_pos + 1;
+ offset1 = pvt->taps - offset2;
+ pvt->latest_correction = 0;
+ for (i = pvt->taps - 1; i >= offset1; i--) {
+ correction = pvt->fir_state.history[i - offset1]*nsuppr;
+ /* Leak to avoid false training on signals with multiple
+ strong correlations. */
+ pvt->fir_taps32[i] -= (pvt->fir_taps32[i] >> 12);
+ pvt->fir_taps32[i] += correction;
+ pvt->fir_state.coeffs[i] = pvt->fir_taps32[i] >> 15;
+ pvt->latest_correction += abs(correction);
+ }
+ for ( ; i >= 0; i--) {
+ correction = pvt->fir_state.history[i + offset2]*nsuppr;
+ /* Leak to avoid false training on signals with multiple
+ strong correlations. */
+ pvt->fir_taps32[i] -= (pvt->fir_taps32[i] >> 12);
+ pvt->fir_taps32[i] += correction;
+ pvt->fir_state.coeffs[i] = pvt->fir_taps32[i] >> 15;
+ pvt->latest_correction += abs(correction);
+ }
+ } else {
+ pvt->latest_correction = -1;
+ }
+ } else {
+ pvt->nonupdate_dwell = NONUPDATE_DWELL_TIME;
+ pvt->latest_correction = -2;
+ }
+ } else {
+ pvt->nonupdate_dwell = 0;
+ pvt->latest_correction = -3;
}
- else
- {
- ec->nonupdate_dwell = NONUPDATE_DWELL_TIME;
- ec->latest_correction = -2;
- }
- }
- else
- {
- ec->nonupdate_dwell = 0;
- ec->latest_correction = -3;
- }
- /* 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->fir_state.history[ec->curr_pos] - ec->fir_state.history[(ec->curr_pos - 7) & ec->tap_mask]);
- ec->supp_test2 += (ec->fir_state.history[(ec->curr_pos - 24) & ec->tap_mask] - ec->fir_state.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->fir_state.history[pvt->curr_pos] - pvt->fir_state.history[(pvt->curr_pos - 7) & pvt->tap_mask]);
+ pvt->supp_test2 += (pvt->fir_state.history[(pvt->curr_pos - 24) & pvt->tap_mask] - pvt->fir_state.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 */
- if (ec->curr_pos <= 0)
- ec->curr_pos = ec->taps;
- ec->curr_pos--;
+ /* Roll around the rolling buffer */
+ if (pvt->curr_pos <= 0)
+ pvt->curr_pos = pvt->taps;
+ pvt->curr_pos--;
- 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_taps32[pos] = val << 17;
- ec->fir_taps16[pos] = val << 1;
- if (++pos >= ec->taps)
+ pvt->fir_taps32[pos] = val << 17;
+ pvt->fir_taps16[pos] = val << 1;
+ if (++pos >= pvt->taps)
return 1;
- return 0;
+ else
+ return 0;
}
-static const struct dahdi_echocan me = {
- .name = "SEC2",
- .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 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);