diff options
Diffstat (limited to 'drivers/dahdi/dahdi_echocan_sec2.c')
-rw-r--r-- | drivers/dahdi/dahdi_echocan_sec2.c | 402 |
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); |