diff --git a/drivers/dahdi/wct4xxp/base.c b/drivers/dahdi/wct4xxp/base.c index 2b7c4a1..1ed10b1 100644 --- a/drivers/dahdi/wct4xxp/base.c +++ b/drivers/dahdi/wct4xxp/base.c @@ -390,7 +390,7 @@ static inline bool is_pcie(const struct t4 *wc) static inline bool has_e1_span(const struct t4 *wc) { - return (wc->t1e1) != 0; + return (wc->t1e1 > 0); } static inline bool is_octal(const struct t4 *wc) @@ -1121,6 +1121,7 @@ static int t4_echocan_create(struct dahdi_chan *chan, int channel; const struct dahdi_echocan_ops *ops; const struct dahdi_echocan_features *features; + const bool alaw = (chan->span->deflaw == 2); if (!vpmsupport || !wc->vpm) return -ENODEV; @@ -1141,19 +1142,19 @@ static int t4_echocan_create(struct dahdi_chan *chan, channel = has_e1_span(wc) ? chan->chanpos : chan->chanpos + 4; - if (wc->vpm) { - if (is_octal(wc)) - channel = channel << 3; - else - channel = channel << 2; - channel |= chan->span->offset; - if (debug & DEBUG_ECHOCAN) - dev_notice(&wc->dev->dev, "echocan: Card is %d, " - "Channel is %d, Span is %d, offset is %d " - "length %d\n", wc->num, chan->chanpos, - chan->span->offset, channel, ecp->tap_length); - vpm450m_setec(wc->vpm, channel, ecp->tap_length); + if (is_octal(wc)) + channel = channel << 3; + else + channel = channel << 2; + channel |= chan->span->offset; + if (debug & DEBUG_ECHOCAN) { + dev_notice(&wc->dev->dev, + "echocan: Card is %d, Channel is %d, Span is %d, offset is %d length %d\n", + wc->num, chan->chanpos, chan->span->offset, + channel, ecp->tap_length); } + vpm450m_set_alaw_companding(wc->vpm, channel, alaw); + vpm450m_setec(wc->vpm, channel, ecp->tap_length); return 0; } @@ -1162,23 +1163,23 @@ static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec struct t4 *wc = chan->pvt; int channel; - memset(ec, 0, sizeof(*ec)); + if (!wc->vpm) + return; + memset(ec, 0, sizeof(*ec)); channel = has_e1_span(wc) ? chan->chanpos : chan->chanpos + 4; - if (wc->vpm) { - if (is_octal(wc)) - channel = channel << 3; - else - channel = channel << 2; - channel |= chan->span->offset; - if (debug & DEBUG_ECHOCAN) - dev_notice(&wc->dev->dev, "echocan: Card is %d, " - "Channel is %d, Span is %d, offset is %d " - "length 0\n", wc->num, chan->chanpos, - chan->span->offset, channel); - vpm450m_setec(wc->vpm, channel, 0); + if (is_octal(wc)) + channel = channel << 3; + else + channel = channel << 2; + channel |= chan->span->offset; + if (debug & DEBUG_ECHOCAN) { + dev_notice(&wc->dev->dev, + "echocan: Card is %d, Channel is %d, Span is %d, offset is %d length 0\n", + wc->num, chan->chanpos, chan->span->offset, channel); } + vpm450m_setec(wc->vpm, channel, 0); } #endif @@ -2225,6 +2226,7 @@ static int t4_set_linemode(struct dahdi_span *span, enum spantypes linemode) int res = 0; enum linemode mode; const char *old_name; + static DEFINE_MUTEX(linemode_lock); dev_dbg(&wc->dev->dev, "Setting '%s' to '%s'\n", span->name, dahdi_spantype2str(linemode)); @@ -2232,22 +2234,27 @@ static int t4_set_linemode(struct dahdi_span *span, enum spantypes linemode) if (span->spantype == linemode) return 0; + /* Do not allow the t1e1 member to be changed by multiple threads. */ + mutex_lock(&linemode_lock); old_name = dahdi_spantype2str(span->spantype); switch (linemode) { case SPANTYPE_DIGITAL_T1: dev_info(&wc->dev->dev, "Changing from %s to T1 line mode.\n", old_name); mode = T1; + wc->t1e1 &= ~(1 << span->offset); break; case SPANTYPE_DIGITAL_E1: dev_info(&wc->dev->dev, "Changing from %s to E1 line mode.\n", old_name); mode = E1; + wc->t1e1 |= (1 << span->offset); break; case SPANTYPE_DIGITAL_J1: dev_info(&wc->dev->dev, "Changing from %s to J1 line mode.\n", old_name); mode = J1; + wc->t1e1 &= ~(1 << span->offset); break; default: dev_err(&wc->dev->dev, @@ -2261,6 +2268,7 @@ static int t4_set_linemode(struct dahdi_span *span, enum spantypes linemode) dahdi_init_span(span); } + mutex_unlock(&linemode_lock); return res; } diff --git a/drivers/dahdi/wct4xxp/vpm450m.c b/drivers/dahdi/wct4xxp/vpm450m.c index f332f01..45d84fb 100644 --- a/drivers/dahdi/wct4xxp/vpm450m.c +++ b/drivers/dahdi/wct4xxp/vpm450m.c @@ -177,6 +177,7 @@ struct vpm450m { #define FLAG_DTMF (1 << 0) #define FLAG_MUTE (1 << 1) #define FLAG_ECHO (1 << 2) +#define FLAG_ALAW (1 << 3) static unsigned int tones[] = { SOUT_DTMF_1, @@ -216,6 +217,50 @@ static unsigned int tones[] = { ROUT_G168_1100GB_ON, }; +void vpm450m_set_alaw_companding(struct vpm450m *vpm450m, int channel, + bool alaw) +{ + tOCT6100_CHANNEL_MODIFY *modify; + UINT32 ulResult; + UINT32 law_to_use = (alaw) ? cOCT6100_PCM_A_LAW : + cOCT6100_PCM_U_LAW; + + if (channel >= ARRAY_SIZE(vpm450m->chanflags)) { + pr_err("Channel out of bounds in %s\n", __func__); + return; + } + /* If we're already in this companding mode, no need to do anything. */ + if (alaw == ((vpm450m->chanflags[channel] & FLAG_ALAW) > 0)) + return; + + modify = kzalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_ATOMIC); + if (!modify) { + pr_notice("Unable to allocate memory for setec!\n"); + return; + } + + Oct6100ChannelModifyDef(modify); + modify->ulChannelHndl = vpm450m->aulEchoChanHndl[channel]; + modify->fTdmConfigModified = TRUE; + modify->TdmConfig.ulSinPcmLaw = law_to_use; + modify->TdmConfig.ulRinPcmLaw = law_to_use; + modify->TdmConfig.ulSoutPcmLaw = law_to_use; + modify->TdmConfig.ulRoutPcmLaw = law_to_use; + ulResult = Oct6100ChannelModify(vpm450m->pApiInstance, modify); + if (ulResult != GENERIC_OK) { + pr_notice("Failed to apply echo can changes on channel %d %d %08x!\n", + vpm450m->aulEchoChanHndl[channel], channel, ulResult); + } else { + pr_info("Changed companding on channel %d to %s.\n", channel, + (alaw) ? "alaw" : "ulaw"); + if (alaw) + vpm450m->chanflags[channel] |= FLAG_ALAW; + else + vpm450m->chanflags[channel] &= ~(FLAG_ALAW); + } + kfree(modify); +} + static void vpm450m_setecmode(struct vpm450m *vpm450m, int channel, int mode) { tOCT6100_CHANNEL_MODIFY *modify; @@ -248,6 +293,11 @@ void vpm450m_setdtmf(struct vpm450m *vpm450m, int channel, int detect, int mute) tOCT6100_CHANNEL_MODIFY *modify; UINT32 ulResult; + if (channel >= ARRAY_SIZE(vpm450m->chanflags)) { + pr_err("Channel out of bounds in %s\n", __func__); + return; + } + modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_KERNEL); if (!modify) { printk(KERN_NOTICE "wct4xxp: Unable to allocate memory for setdtmf!\n"); @@ -286,6 +336,11 @@ void vpm450m_setdtmf(struct vpm450m *vpm450m, int channel, int detect, int mute) void vpm450m_setec(struct vpm450m *vpm450m, int channel, int eclen) { + if (channel >= ARRAY_SIZE(vpm450m->chanflags)) { + pr_err("Channel out of bounds in %s\n", __func__); + return; + } + if (eclen) { vpm450m->chanflags[channel] |= FLAG_ECHO; vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); @@ -524,10 +579,13 @@ struct vpm450m *init_vpm450m(void *wc, int *isalaw, int numspans, const struct f * therefore, the lower 2 bits tell us which span this * timeslot/channel */ - if (isalaw[x & mask]) + if (isalaw[x & mask]) { law = cOCT6100_PCM_A_LAW; - else + vpm450m->chanflags[x] |= FLAG_ALAW; + } else { law = cOCT6100_PCM_U_LAW; + vpm450m->chanflags[x] &= ~(FLAG_ALAW); + } Oct6100ChannelOpenDef(ChannelOpen); ChannelOpen->pulChannelHndl = &vpm450m->aulEchoChanHndl[x]; ChannelOpen->ulUserChanId = x; diff --git a/drivers/dahdi/wct4xxp/vpm450m.h b/drivers/dahdi/wct4xxp/vpm450m.h index 735a2cc..adf4150 100644 --- a/drivers/dahdi/wct4xxp/vpm450m.h +++ b/drivers/dahdi/wct4xxp/vpm450m.h @@ -39,5 +39,7 @@ void vpm450m_setdtmf(struct vpm450m *instance, int channel, int dtmfdetect, int int vpm450m_checkirq(struct vpm450m *vpm450m); int vpm450m_getdtmf(struct vpm450m *vpm450m, int *channel, int *tone, int *start); void release_vpm450m(struct vpm450m *instance); +void vpm450m_set_alaw_companding(struct vpm450m *vpm450m, + int channel, bool alaw); #endif