Index: channels/chan_zap.c =================================================================== --- channels/chan_zap.c (revision 47383) +++ channels/chan_zap.c (working copy) @@ -237,6 +237,8 @@ static ast_group_t cur_pickupgroup = 0; static int relaxdtmf = 0; +static int allow_aoc = 0; + static int immediate = 0; static int stripmsd = 0; @@ -508,6 +510,11 @@ static int dialplan = PRI_NATIONAL_ISDN + 1; static int localdialplan = PRI_NATIONAL_ISDN + 1; +struct aocd_data { + int units; + int units_type; +}; + #else /*! Shut up the compiler */ struct zt_pri; @@ -614,6 +621,7 @@ unsigned int hardwaredtmf:1; unsigned int hidecallerid; unsigned int ignoredtmf:1; + unsigned int allow_aoc:1; /*!< Handle and generate AOC messages? */ unsigned int immediate:1; /*!< Answer before getting digits? */ unsigned int inalarm:1; unsigned int mate:1; /*!< flag to say its in MATE mode */ @@ -5465,6 +5473,61 @@ } else res = 0; break; + case AST_CONTROL_AOCD: + /* generate a FACILITY message with AOC-D */ + if (!p->allow_aoc) { + if (option_verbose > 3) + ast_verbose( VERBOSE_PREFIX_4 "AOC deactivated, ignoring AOCD control frame for channel '%s'\n", chan->name); + break; + } + if (p->sig == SIG_PRI) { /* Only SIG_PRI supports AOC-D */ + if(!pri_grab(p, p->pri)) { + struct aocd_data *aocd; + aocd = (struct aocd_data *)data; + aoc_aocd_charging_unit_encode(p->pri->pri, p->call, aocd->units, aocd->units_type); + if (option_verbose > 2) { + if ( aocd->units == -2) + ast_verbose(VERBOSE_PREFIX_3 "AST_CONTROL_AOCD: generated freeOfCharge AOC-D on %s\n", chan->name); + else + ast_verbose(VERBOSE_PREFIX_3 "AST_CONTROL_AOCD: generated %i unit(s) %s AOC-D on %s\n", aocd->units, aocd->units_type==0?"SUBTOTAL":"TOTAL", chan->name); + } + pri_rel(p->pri); + } else { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "AST_CONTROL_AOCD: Unable to grab PRI for AOC-D on span %d\n", p->span); + } + res=0; + } else { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "AST_CONTROL_AOCD: can't passthrough to channel %s (wrong type)\n", chan->name); + res=0; + } + break; + case AST_CONTROL_AOCE: + /* handle the AOC-E units over to libpri to store inside the q931_call structure. this will be used by + libpri when hanging up this call. */ + if (!p->allow_aoc) { + if (option_verbose > 3) + ast_verbose( VERBOSE_PREFIX_4 "AOC deactivated, ignoring AOCE control frame for channel '%s'\n", chan->name); + break; + } + if (p->sig == SIG_PRI) { /* Only SIG_PRI supports AOC-E */ + int aoce_units; + aoce_units = *(int *)data; + aoc_aoce_add_units_to_call(p->call, aoce_units); + if (option_verbose > 2) { + if ( aoce_units == -2) + ast_verbose(VERBOSE_PREFIX_3 "AST_CONTROL_AOCE: marked channel %s with freeOfCharge AOC-E\n", chan->name); + else + ast_verbose(VERBOSE_PREFIX_3 "AST_CONTROL_AOCE: marked channel %s with %i unit(s) AOC-E\n", chan->name, aoce_units); + } + res=0; + } else { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "AST_CONTROL_AOCE: can't mark channel %s with AOC-E: wrong type\n", chan->name); + res=0; + } + break; case -1: res = tone_zone_play_tone(p->subs[index].zfd, -1); break; @@ -7718,6 +7781,7 @@ ast_log(LOG_WARNING, "Unable to check buffer policy on channel %d\n", channel); } #endif + tmp->allow_aoc = allow_aoc; tmp->immediate = immediate; tmp->transfertobusy = transfertobusy; tmp->sig = signalling; @@ -9847,6 +9911,40 @@ ast_log(LOG_WARNING, "Facility Name requested on channel %d/%d not in use on span %d\n", PRI_SPAN(e->facname.channel), PRI_CHANNEL(e->facname.channel), pri->span); } else { + + /* Q.956 AOC-D bridging */ + if ( (e->facname.aocd_units > -1) || (e->facname.aocd_units == -2) ) { /* an AOC-D was received ... */ + if (!pri->pvts[chanpos]->allow_aoc) { + if (option_verbose > 3) + ast_verbose( VERBOSE_PREFIX_4 "AOC deactivated, ignoring AOCD in FACILITY event on channel '%s'\n", pri->pvts[chanpos]->owner->name); + } else { + ast_verbose(VERBOSE_PREFIX_3 "PRI_EVENT_FACNAME: received AOC-D FACILITY on q931_call %p\n", pri->pvts[chanpos]->call); + ast_mutex_lock(&pri->pvts[chanpos]->lock); + if (pri->pvts[chanpos]->owner) { + /* store AOC-D data in call structure, can be used to fake AOC-E */ + pri->pvts[chanpos]->owner->aocd_units = e->facname.aocd_units; + pri->pvts[chanpos]->owner->aocd_units_type = e->facname.aocd_units_type; + struct ast_channel *otherchan; + otherchan = ast_bridged_channel(pri->pvts[chanpos]->owner); + if (otherchan && (!strcmp(otherchan->tech->type, "Zap"))) { /* Only for Zap channels */ + struct aocd_data aocd; + aocd.units = e->facname.aocd_units; + aocd.units_type = e->facname.aocd_units_type; + if (option_verbose > 2) { + if (e->facname.aocd_units == -2) + ast_verbose(VERBOSE_PREFIX_3 "AOC-D: passing freeOfCharge AOC-D from %s to %s\n", pri->pvts[chanpos]->owner->name, otherchan->name); + else + ast_verbose(VERBOSE_PREFIX_3 "AOC-D: passing %i unit(s) %s AOC-D from %s to %s\n", aocd.units, aocd.units_type==0?"SUBTOTAL":"TOTAL", pri->pvts[chanpos]->owner->name, otherchan->name); + } + ast_indicate_data(otherchan, AST_CONTROL_AOCD, &aocd, sizeof(struct aocd_data)); + } else { + ast_verbose(VERBOSE_PREFIX_3 "no bridged called found for passing the AOC-D\n"); + } + } + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } + } /* end AOC-D code */ + /* Re-use *69 field for PRI */ ast_mutex_lock(&pri->pvts[chanpos]->lock); ast_copy_string(pri->pvts[chanpos]->lastcid_num, e->facname.callingnum, sizeof(pri->pvts[chanpos]->lastcid_num)); @@ -9966,10 +10064,10 @@ pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos])); pri->pvts[chanpos]->resetting = 1; } - if (e->hangup.aoc_units > -1) + if (e->hangup.aoce_units > -1) if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E charging %d unit%s\n", - pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s"); + ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E on hangup charging %d unit%s\n", + pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoce_units, (e->hangup.aoce_units == 1) ? "" : "s"); #ifdef SUPPORT_USERUSER if (!ast_strlen_zero(e->hangup.useruserinfo)) { @@ -10017,10 +10115,66 @@ } if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d got hangup request\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - if (e->hangup.aoc_units > -1) + if ( (e->hangup.aoce_units > -1) || (e->hangup.aoce_units == -2) ) { + /* hangup-request contained AOC-E */ if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E charging %d unit%s\n", - pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s"); + ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E on hangup-req charging %d unit%s\n", + pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoce_units, (e->hangup.aoce_units == 1) ? "" : "s"); + if (!pri->pvts[chanpos]->allow_aoc) { + if (option_verbose > 3) + ast_verbose( VERBOSE_PREFIX_4 "PRI_EVENT_HANGUP_REQ: AOC deactivated, ignoring AOCE in hangup-req event on channel '%s'\n", pri->pvts[chanpos]->owner->name); + } else { + struct ast_channel *otherchan; + otherchan = ast_bridged_channel(pri->pvts[chanpos]->owner); + if (otherchan && (!strcmp(otherchan->tech->type, "Zap"))) { /* Only for Zap channels */ + if ( ((struct zt_pvt *)otherchan->tech_pvt)->sig == SIG_PRI ) { /* Only SIG_PRI supports AOC-D */ + ast_indicate_data(otherchan, AST_CONTROL_AOCE, &(e->hangup.aoce_units), sizeof(int)); + if (option_verbose > 2) { + if (e->hangup.aoce_units == -2) + ast_verbose(VERBOSE_PREFIX_3 "PRI_EVENT_HANGUP_REQ: passing freeOfCharge AOC-E from %s to %s\n", pri->pvts[chanpos]->owner->name, otherchan->name); + else + ast_verbose(VERBOSE_PREFIX_3 "PRI_EVENT_HANGUP_REQ: passing %i unit(s) AOC-E from %s to %s\n", e->hangup.aoce_units, pri->pvts[chanpos]->owner->name, otherchan->name); + } + } else { + ast_verbose(VERBOSE_PREFIX_3 "AOC-E: can't passthrough to channel %s (wrong type)\n", otherchan->name); + } + } else { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "PRI_EVENT_HANGUP_REQ: no bridged called found for passing the AOC-E\n"); + } + } + } else { + /* No AOC-E received, check if there is AOC-D on other call leg which + can be used to fake an AOC-E on this call leg */ + if (pri->pvts[chanpos]->allow_aoc) { + if (option_verbose > 3) + ast_verbose( VERBOSE_PREFIX_4 "PRI_EVENT_HANGUP_REQ: AOC activated, try to fake AOCE on channel '%s'\n", pri->pvts[chanpos]->owner->name); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received hangup-req without AOC-E, checking for AOC-D on bridged call leg\n", + pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span); + struct ast_channel *otherchan; + otherchan = ast_bridged_channel(pri->pvts[chanpos]->owner); + if (otherchan && (!strcmp(otherchan->tech->type, "Zap"))) { /* Only for Zap channels */ + if ( ((struct zt_pvt *)otherchan->tech_pvt)->sig == SIG_PRI) { /* Only SIG_PRI supports AOC */ + if (otherchan->aocd_units != -1 ) { + /* fetch aocd from bridged channel and send it currents channels as aoce */ + aoc_aoce_add_units_to_call(pri->pvts[chanpos]->call, otherchan->aocd_units); + if (option_verbose > 2) { + if ( otherchan->aocd_units == -2) + ast_verbose(VERBOSE_PREFIX_3 "PRI_EVENT_HANGUP_REQ: fake AOC-E: marked channel %s with freeOfCharge AOC-E\n", pri->pvts[chanpos]->owner->name); + else + ast_verbose(VERBOSE_PREFIX_3 "PRI_EVENT_HANGUP_REQ: fake AOC-E: marked channel %s with %i unit(s) AOC-E\n", pri->pvts[chanpos]->owner->name, otherchan->aocd_units); + } + } + } else { + ast_verbose(VERBOSE_PREFIX_3 "PRI_EVENT_HANGUP_REQ: can't fake AOC-E, wrong type on channel %s\n", otherchan->name); + } + } else { + ast_verbose(VERBOSE_PREFIX_3 "PRI_EVENT_HANGUP_REQ: no bridged ZAP call found for faking AOC-E\n"); + } + + } + } } else { pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause); pri->pvts[chanpos]->call = NULL; @@ -10172,6 +10326,44 @@ ast_mutex_unlock(&pri->pvts[chanpos]->lock); } break; + case PRI_EVENT_CONNECT_ACK: + chanpos = pri_find_principle(pri, e->connect_ack.channel); + if (chanpos < 0) { + ast_log(LOG_WARNING, "Received CONNECT_ACK on unconfigured channel %d/%d span %d\n", + PRI_SPAN(e->connect_ack.channel), PRI_CHANNEL(e->connect_ack.channel), pri->span); + } else { + chanpos = pri_fixup_principle(pri, chanpos, e->connect_ack.call); + if (chanpos > -1) { + if (pri->pvts[chanpos]->allow_aoc) { + if (option_verbose > 3) + ast_verbose( VERBOSE_PREFIX_4 "PRI_EVENT_CONNECT_ACK: AOC activated, try to relay AOCD on channel '%s'\n", pri->pvts[chanpos]->owner->name); + ast_mutex_lock(&pri->pvts[chanpos]->lock); + /* Send AOC-D if available, check if bridged call leg has received AOC-D yet */ + struct ast_channel *otherchan; + otherchan = ast_bridged_channel(pri->pvts[chanpos]->owner); + if (otherchan && (!strcmp(otherchan->tech->type, "Zap"))) { /* Only for Zap channels */ + if (otherchan->aocd_units != -1) { + if (option_verbose > 2) { + if (otherchan->aocd_units == -2) + ast_verbose(VERBOSE_PREFIX_3 "PRI_EVENT_CONNECT_ACK: passing freeOfCharge AOC-D from %s to %s\n", otherchan->name, pri->pvts[chanpos]->owner->name); + else + ast_verbose(VERBOSE_PREFIX_3 "PRI_EVENT_CONNECT_ACK: passing %i unit(s) %s AOC-D from %s to %s\n", otherchan->aocd_units, otherchan->aocd_units_type==0?"SUBTOTAL":"TOTAL", otherchan->name, pri->pvts[chanpos]->owner->name); + } + aoc_aocd_charging_unit_encode(pri->pri, pri->pvts[chanpos]->call, otherchan->aocd_units, otherchan->aocd_units_type); + } else { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "PRI_EVENT_CONNECT_ACK: bridged ZAP has no AOC-D to send\n"); + } + } else { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "PRI_EVENT_CONNECT_ACK: no bridged ZAP call found for retrieving AOC-D data\n"); + } + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } + } else + ast_log(LOG_WARNING, "Unable to move channel %d!\n", e->connect_ack.channel); + } + break; default: if (option_debug) ast_log(LOG_DEBUG, "Event: %d\n", e->e); @@ -10360,7 +10552,7 @@ if (pris[span-1].dchans[x]) pri_set_debug(pris[span-1].dchans[x], PRI_DEBUG_APDU | PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE | - PRI_DEBUG_Q921_STATE); + PRI_DEBUG_Q921_STATE | PRI_DEBUG_AOC ); } ast_cli(fd, "Enabled debugging on span %d\n", span); return RESULT_SUCCESS; @@ -10410,7 +10602,8 @@ if (pris[span-1].dchans[x]) pri_set_debug(pris[span-1].dchans[x], PRI_DEBUG_APDU | PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE | - PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_STATE); + PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_STATE | + PRI_DEBUG_AOC); } ast_cli(fd, "Enabled EXTENSIVE debugging on span %d\n", span); return RESULT_SUCCESS; @@ -11849,6 +12042,8 @@ cur_callergroup = ast_get_group(v->value); } else if (!strcasecmp(v->name, "pickupgroup")) { cur_pickupgroup = ast_get_group(v->value); + } else if (!strcasecmp(v->name, "allow_aoc")) { + allow_aoc = ast_true(v->value); } else if (!strcasecmp(v->name, "immediate")) { immediate = ast_true(v->value); } else if (!strcasecmp(v->name, "transfertobusy")) { Index: include/asterisk/channel.h =================================================================== --- include/asterisk/channel.h (revision 47383) +++ include/asterisk/channel.h (working copy) @@ -431,6 +431,10 @@ char emulate_dtmf_digit; /*!< Digit being emulated */ unsigned int emulate_dtmf_samples; /*!< Number of samples left to emulate DTMF for */ + /*! \brief Advice-of-Charge data */ + int aocd_units; /*!< The actual AOC-D units */ + int aocd_units_type; /*!< Type of the AOC-D units (total or subtotal) */ + /*! \brief Data stores on the channel */ AST_LIST_HEAD_NOLOCK(datastores, ast_datastore) datastores; }; Index: include/asterisk/frame.h =================================================================== --- include/asterisk/frame.h (revision 47383) +++ include/asterisk/frame.h (working copy) @@ -281,6 +281,8 @@ AST_CONTROL_HOLD = 16, /*!< Indicate call is placed on hold */ AST_CONTROL_UNHOLD = 17, /*!< Indicate call is left from hold */ AST_CONTROL_VIDUPDATE = 18, /*!< Indicate video frame update */ + AST_CONTROL_AOCD = 19, /*!< Indicate an AOC-D update */ + AST_CONTROL_AOCE = 20, /*!< Indicate an AOC-E update */ }; #define AST_SMOOTHER_FLAG_G729 (1 << 0) Index: main/channel.c =================================================================== --- main/channel.c (revision 47383) +++ main/channel.c (working copy) @@ -753,6 +753,9 @@ tmp->tech = &null_tech; + tmp->aocd_units=-1; + tmp->aocd_units_type=-1; + AST_LIST_LOCK(&channels); AST_LIST_INSERT_HEAD(&channels, tmp, chan_list); AST_LIST_UNLOCK(&channels); Index: main/cli.c =================================================================== --- main/cli.c (revision 47383) +++ main/cli.c (working copy) @@ -773,7 +773,10 @@ " Pickup Group: %llu\n" " Application: %s\n" " Data: %s\n" - " Blocking in: %s\n", + " Blocking in: %s\n" + " -- AOC --\n" + " AOCD units: %d\n" + " AOCD type: %s\n", c->name, c->tech->type, c->uniqueid, S_OR(c->cid.cid_num, "(N/A)"), S_OR(c->cid.cid_name, "(N/A)"), @@ -790,7 +793,10 @@ cdrtime, c->_bridge ? c->_bridge->name : "", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "", c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ), ( c-> data ? S_OR(c->data, "(Empty)") : "(None)"), - (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)")); + (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"), + c->aocd_units, + c->aocd_units_type==0?"SUBTOTAL":(c->aocd_units_type==1?"TOTAL":"not available") + ); if(pbx_builtin_serialize_variables(c,buf,sizeof(buf))) ast_cli(fd," Variables:\n%s\n",buf);