Index: pri_facility.c =================================================================== --- pri_facility.c (revision 382) +++ pri_facility.c (working copy) @@ -1242,7 +1242,7 @@ case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1): /* freeOfCharge (0x81) */ if (pri->debug & PRI_DEBUG_AOC) pri_message(pri, "Channel %d/%d, Call %d - received AOC-E free of charge\n", call->ds1no, call->channelno, call->cr); - chargingunits = 0; + chargingunits = -2; /* -2 means free of charge */ break; default: pri_message(pri, "!! Invalid AOC-E specificChargingUnits. Expected Sequence (0x30) or Object Identifier (0x81/0x01) but received 0x%02X\n", comp1->type); @@ -1271,13 +1271,39 @@ dump_apdu (pri, data, len); return -1; /* oops - aborted before */ } - call->aoc_units = chargingunits; + call->aoce_units = chargingunits; return 0; } -static int aoc_aoce_charging_unit_encode(struct pri *pri, q931_call *c, long chargedunits) +int aoc_aoce_charging_unit_send_facility(struct pri *pri, q931_call *c, long chargedunits) { + int res=0; +pri_message(pri, "entering aoc_aoce_charging_unit_send_facility\n"); + + res = aoc_aoce_charging_unit_encode_and_queue(pri, c, chargedunits, Q931_FACILITY); + if (res) { + pri_message(pri, "Could not queue APDU in FACILITY message\n"); + return(res); + } + + /* Remember that if we queue a facility IE for a facility message we + * have to explicitly send the facility message ourselves */ + res = q931_facility(c->pri, c); + if (res) { + pri_message(pri, "Could not schedule facility message for call %d\n", c->cr); + return -1; + } + + return 0; +} + + +int aoc_aoce_charging_unit_encode_and_queue(struct pri *pri, q931_call *c, long chargedunits, int message_type) +{ + +pri_message(pri, "entering aoc_aoce_charging_unit_encode_and_queue: chargedunits=%d\n", chargedunits); + /* sample data: [ 91 a1 12 02 02 3a 78 02 01 24 30 09 30 07 a1 05 30 03 02 01 01 ] */ int i = 0, res = 0, compsp = 0; unsigned char buffer[255] = ""; @@ -1330,6 +1356,148 @@ dump_apdu (pri, buffer, i); /* code below is untested */ + res = pri_call_apdu_queue(c, message_type, buffer, i, NULL, NULL); + if (res) { + pri_message(pri, "Could not queue APDU in message\n"); + return -1; + } + + return 0; +} + +static int aoc_aocd_charging_unit_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) +{ + long chargingunits = 0; + unsigned char *vdata = data; + struct rose_component *comp1 = NULL, *comp2 = NULL, *comp3 = NULL; + int pos1 = 0, pos2, pos3, sublen2, sublen3, temp; + + if (pri->debug & PRI_DEBUG_AOC) + dump_apdu(pri, data, len); + + do { + GET_COMPONENT(comp1, pos1, vdata, len); /* AOCDChargingUnitInfo */ + switch(comp1->type) { + case(ASN1_SEQUENCE | ASN1_CONSTRUCTOR): /* specificChargingUnits */ + sublen2 = comp1->len; + pos2 = pos1; + comp2 = comp1; + SUB_COMPONENT(comp2, pos2); + do { + GET_COMPONENT(comp2, pos2, vdata, len); + switch(comp2->type) { + case(ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1): /* recordedUnitsList */ + SUB_COMPONENT(comp2, pos2); + GET_COMPONENT(comp2, pos2, vdata, len); + CHECK_COMPONENT(comp2, ASN1_SEQUENCE, "!! Invalid AOC-D charging unit argument in recordedUnitsList. Expected sequence but received 0x%02X\n"); /* recordedUnits */ + sublen3 = pos2 + comp2->len; + pos3 = pos2; + comp3 = comp2; + SUB_COMPONENT(comp3, pos3); + do { + GET_COMPONENT(comp3, pos3, vdata, len); + switch(comp3->type) { + case ASN1_INTEGER: /* numberOfUnits */ + ASN1_GET_INTEGER(comp3, temp); + chargingunits += temp; + case ASN1_NULL: + break; + default: + pri_message(pri, "!! Don't know how to handle 0x%02X in AOC-D recordedUnits\n", comp3->type); + } + NEXT_COMPONENT(comp3, pos3); + } while (pos3 < sublen3); + if (pri->debug & PRI_DEBUG_AOC) + pri_message(pri, "Channel %d/%d, Call %d - received AOC-D charging: %i unit%s\n", + call->ds1no, call->channelno, call->cr, chargingunits, (chargingunits == 1) ? "" : "s"); + break; + case(ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2): /* typeOfChargingInfo */ + SUB_COMPONENT(comp2, pos2); + GET_COMPONENT(comp2, pos2, vdata, len); + ASN1_GET_INTEGER(comp3, temp); + if (temp == ROSE_AOC_AOCD_SUBTOTAL) { + pri->ev.facname.aocd_units_type = ROSE_AOC_AOCD_SUBTOTAL; + } else if (temp == ROSE_AOC_AOCD_TOTAL) { + pri->ev.facname.aocd_units_type = ROSE_AOC_AOCD_TOTAL; + } + break; + case(ASN1_CONTEXT_SPECIFIC | ASN1_TAG_3): /* aOCDBillingId */ + SUB_COMPONENT(comp2, pos2); + GET_COMPONENT(comp2, pos2, vdata, len); + /* FIXME: Handle oAOCBillingId here */ + break; + default: + pri_message(pri, "!! Don't know how to handle 0x%02X inc AOC-D recordedUnitsList\n", comp2->type); + } + NEXT_COMPONENT(comp2, pos2); + } while (pos2 < sublen2); + break; + case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1): /* freeOfCharge */ + if (pri->debug & PRI_DEBUG_AOC) + pri_message(pri, "Channel %d/%d, Call %d - received AOC-D free of Charge\n", call->ds1no, call->channelno, call->cr); + chargingunits = -2; /* -2 means free of charge */ + break; + default: + pri_message(pri, "!! Invalid AOC-D specificChargingUnits - expected Sequence, or object identifier, but got 0x%02X\n", comp1->type); + return -1; + } + NEXT_COMPONENT(comp1, pos1); + } while (pos1 < len); + pri->ev.facname.aocd_units = chargingunits; + return(0); +} +/* End AOC */ + +int aoc_aocd_charging_unit_encode(struct pri *pri, q931_call *c, long chargedunits, int aocd_units_type) { + /* sample including facility header: + * [1c 16 91 a1 13 02 02 02 b7 02 01 22 30 0a a1 05 30 03 02 01 0a 82 01 00] */ + int i = 0, compsp = 0, res = 0; + unsigned char buffer[255] = ""; + struct rose_component *comp = NULL, *compstk[10]; + /* ROSE (0x91) */ + buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_ROSE); + /* ROSE component (0xA1, len) */ + ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + /* ROSE invokeID component (0x02, len, id) */ + ASN1_ADD_WORDCOMP(comp, INVOKE_IDENTIFIER, buffer, i, ++pri->last_invoke); + /* ROSE operationID component (0x02, 0x01, 0x22) */ + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, ROSE_AOC_AOCD_CHARGING_UNIT); + + if (chargedunits == -2) { /* send "free" of charge AOC-D */ + /* freeOfCharge (0x81,0) */ + ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1), buffer, i); + } else if (chargedunits > -1) { + /* specificChargingUnitInfo */ + ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); + ASN1_PUSH(compstk, compsp, comp); + /* RecordedUnitsList (0xa1, len) */ + ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1), buffer, i); + ASN1_PUSH(compstk, compsp,comp); + /* RecordedUnits (0x02, len, charge) */ + ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); + ASN1_PUSH(compstk, compsp, comp); + /* NumberOfUnits */ + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, chargedunits); + + ASN1_FIXUP(compstk, compsp, buffer, i); + ASN1_FIXUP(compstk, compsp, buffer, i); + + /* typeOfChargingInfo */ + if (aocd_units_type == ROSE_AOC_AOCD_SUBTOTAL ) /* ( == 0) */ + ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i, ROSE_AOC_AOCD_SUBTOTAL); + else /* ( == 1) */ + ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i, ROSE_AOC_AOCD_TOTAL); + + ASN1_FIXUP(compstk, compsp, buffer, i); + } + + ASN1_FIXUP(compstk, compsp, buffer, i); + + if (pri->debug & PRI_DEBUG_AOC) + dump_apdu(pri, buffer, i); + + /* actually send AOC-D frame */ res = pri_call_apdu_queue(c, Q931_FACILITY, buffer, i, NULL, NULL); if (res) { pri_message(pri, "Could not queue APDU in facility message\n"); @@ -1346,8 +1514,8 @@ return 0; } -/* End AOC */ + int rose_reject_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) { int i = 0; @@ -1627,10 +1795,10 @@ return -1; case ROSE_AOC_AOCD_CHARGING_UNIT: if (pri->debug & PRI_DEBUG_APDU) { - pri_message(pri, "ROSE %i: AOC-D Charging Unit - not handled!", operation_tag); + pri_message(pri, "ROSE %i: handling AOC-D Charging Unit", operation_tag); dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); } - return -1; + return aoc_aocd_charging_unit_decode(pri, call, (u_int8_t *)comp, comp->len +2); case ROSE_AOC_AOCE_CURRENCY: if (pri->debug & PRI_DEBUG_APDU) { pri_message(pri, "ROSE %i: AOC-E Currency - not handled!", operation_tag); @@ -1640,7 +1808,8 @@ case ROSE_AOC_AOCE_CHARGING_UNIT: return aoc_aoce_charging_unit_decode(pri, call, (u_int8_t *)comp, comp->len + 2); if (0) { /* the following function is currently not used - just to make the compiler happy */ - aoc_aoce_charging_unit_encode(pri, call, call->aoc_units); /* use this function to forward the aoc-e on a bridged channel */ + aoc_aoce_charging_unit_send_facility(pri, call, call->aoce_units); /* use this function to send FACILITY with AOC-E */ + aoc_aocd_charging_unit_encode(pri, call, call->aoce_units, ROSE_AOC_AOCD_SUBTOTAL); /* use this function to send FACILITY with AOC-D */ return 0; } case ROSE_AOC_IDENTIFICATION_OF_CHARGE: @@ -1759,3 +1928,8 @@ return 0; } +void aoc_aoce_add_units_to_call(q931_call *call, int aoce_units) +{ + call->aoce_units_bridged_call = aoce_units; +} + Index: pri_facility.h =================================================================== --- pri_facility.h (revision 382) +++ pri_facility.h (working copy) @@ -45,7 +45,9 @@ #define ROSE_AOC_AOCD_CHARGING_UNIT 34 #define ROSE_AOC_AOCE_CURRENCY 35 #define ROSE_AOC_AOCE_CHARGING_UNIT 36 -#define ROSE_AOC_IDENTIFICATION_OF_CHARGE 37 +#define ROSE_AOC_IDENTIFICATION_OF_CHARGE 37 +#define ROSE_AOC_AOCD_SUBTOTAL 0 +#define ROSE_AOC_AOCD_TOTAL 1 /* Q.SIG operations */ #define SS_CNID_CALLINGNAME 0 #define SS_DIVERTING_LEG_INFORMATION2 21 Index: libpri.h =================================================================== --- libpri.h (revision 382) +++ libpri.h (working copy) @@ -76,6 +76,7 @@ #define PRI_EVENT_NOTIFY 16 /* Notification received */ #define PRI_EVENT_PROGRESS 17 /* When we get CALL_PROCEEDING or PROGRESS */ #define PRI_EVENT_KEYPAD_DIGIT 18 /* When we receive during ACTIVE state */ +#define PRI_EVENT_CONNECT_ACK 19 /* When we receive CONNET_ACK, will trigger AOC-D FACILITY if available */ /* Simple states */ #define PRI_STATE_DOWN 0 @@ -296,6 +297,8 @@ int channel; int cref; q931_call *call; + int aocd_units; /* deliver the AOC-D units with an event to chan_zap */ + int aocd_units_type; /* total or subtotal, usually this will be subtotal as total will be transmitted as AOC-E */ } pri_event_facname; #define PRI_CALLINGPLANANI @@ -338,7 +341,7 @@ int cause; int cref; q931_call *call; /* Opaque call pointer */ - long aoc_units; /* Advise of Charge number of charged units */ + int aoce_units; /* Advise of Charge number of charged units */ char useruserinfo[260]; /* User->User info */ } pri_event_hangup; @@ -377,6 +380,12 @@ char digits[64]; } pri_event_keypad_digit; +typedef struct pri_event_connect_ack { + int e; + int channel; + q931_call *call; +} pri_event_connect_ack; + typedef union { int e; pri_event_generic gen; /* Generic view */ @@ -392,6 +401,7 @@ pri_event_setup_ack setup_ack; /* SETUP_ACKNOWLEDGE structure */ pri_event_notify notify; /* Notification */ pri_event_keypad_digit digit; /* Digits that come during a call */ + pri_event_connect_ack connect_ack; /* CONNECT_ACK structure */ } pri_event; struct pri; @@ -595,6 +605,12 @@ extern int pri_get_timer(struct pri *pri, int timer); extern int pri_timer2idx(char *timer); +/* AOC sending */ +extern int aoc_aoce_charging_unit_send_facility(struct pri *pri, q931_call *c, long chargedunits); +extern int aoc_aoce_charging_unit_encode_and_queue(struct pri *pri, q931_call *c, long chargedunits, int message_type); +extern int aoc_aocd_charging_unit_encode(struct pri *pri, q931_call *c, long chargedunits, int aocd_units_type); +extern void aoc_aoce_add_units_to_call(q931_call *call, int aoce_units); + #define PRI_MAX_TIMERS 32 #define PRI_TIMER_N200 0 /* Maximum numer of q921 retransmissions */ Index: pri_internal.h =================================================================== --- pri_internal.h (revision 382) +++ pri_internal.h (working copy) @@ -240,7 +240,8 @@ char useruserinfo[256]; char callingsubaddr[256]; /* Calling parties sub address */ - long aoc_units; /* Advice of Charge Units */ + int aoce_units; /* Advice of Charge Units */ + int aoce_units_bridged_call; /* Advice of Charge-E Units, received on and copied from the already hang up bridged call leg*/ struct apdu_event *apdus; /* APDU queue for call */ Index: q931.c =================================================================== --- q931.c (revision 382) +++ q931.c (working copy) @@ -270,6 +270,8 @@ c->newcall = 1; c->ourcallstate = Q931_CALL_STATE_NULL; c->peercallstate = Q931_CALL_STATE_NULL; + c->aoce_units = -1; /* Advice of Charge Units */ + c->aoce_units_bridged_call = -1; /* Advice of Charge-E Units from the already hang up bridged call*/ } static char *binary(int b, int len) { @@ -2662,7 +2664,7 @@ pri->ev.hangup.cause = c->cause; pri->ev.hangup.cref = c->cr; pri->ev.hangup.call = c; - pri->ev.hangup.aoc_units = c->aoc_units; + pri->ev.hangup.aoce_units = c->aoce_units; libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); q931_hangup(pri, c, c->cause); } @@ -2708,7 +2710,8 @@ return send_message(pri, c, Q931_CONNECT, connect_ies); } -static int release_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, -1 }; +static int release_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, -1 }; +static int release_aoce_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, Q931_IE_FACILITY, -1 }; int q931_release(struct pri *pri, q931_call *c, int cause) { @@ -2727,7 +2730,12 @@ } else { c->retranstimer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T308], pri_release_finaltimeout, c); } - return send_message(pri, c, Q931_RELEASE, release_ies); + if (c->aoce_units_bridged_call != -1) { /* add faked AOC-E to RELEASE */ + aoc_aoce_charging_unit_encode_and_queue(pri, c, c->aoce_units_bridged_call, Q931_RELEASE); + return send_message(pri, c, Q931_RELEASE, release_aoce_ies); + } else { + return send_message(pri, c, Q931_RELEASE, release_ies); + } } else return send_message(pri, c, Q931_RELEASE_COMPLETE, release_ies); /* Yes, release_ies, not release_complete_ies */ } else @@ -2757,6 +2765,7 @@ } static int disconnect_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, -1 }; +static int disconnect_aoce_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, Q931_IE_FACILITY, -1 }; int q931_disconnect(struct pri *pri, q931_call *c, int cause) { @@ -2771,7 +2780,13 @@ if (c->retranstimer) pri_schedule_del(pri, c->retranstimer); c->retranstimer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T305], pri_disconnect_timeout, c); - return send_message(pri, c, Q931_DISCONNECT, disconnect_ies); + + if (c->aoce_units_bridged_call != -1) { + aoc_aoce_charging_unit_encode_and_queue(pri, c, c->aoce_units_bridged_call, Q931_DISCONNECT); + return send_message(pri, c, Q931_DISCONNECT, disconnect_aoce_ies); + } else { + return send_message(pri, c, Q931_DISCONNECT, disconnect_ies); + } } else return 0; } @@ -3061,6 +3076,7 @@ break; case Q931_FACILITY: c->callername[0] = '\0'; + pri->ev.facname.aocd_units = -1; break; case Q931_SETUP: if (pri->debug & PRI_DEBUG_Q931_STATE) @@ -3099,7 +3115,7 @@ c->useruserinfo[0] = '\0'; c->complete = 0; c->nonisdn = 0; - c->aoc_units = -1; + c->aoce_units = -1; /* Fall through */ case Q931_CONNECT: case Q931_ALERTING: @@ -3121,7 +3137,7 @@ c->cause = -1; c->causecode = -1; c->causeloc = -1; - c->aoc_units = -1; + c->aoce_units = -1; if (c->retranstimer) pri_schedule_del(pri, c->retranstimer); c->retranstimer = 0; @@ -3138,7 +3154,7 @@ c->causecode = -1; c->causeloc = -1; c->sugcallstate = -1; - c->aoc_units = -1; + c->aoce_units = -1; break; case Q931_RESTART_ACKNOWLEDGE: c->channelno = -1; @@ -3441,7 +3457,10 @@ } UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_ACTIVE); c->peercallstate = Q931_CALL_STATE_ACTIVE; - break; + pri->ev.e = PRI_EVENT_CONNECT_ACK; + pri->ev.connect_ack.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); + pri->ev.connect_ack.call = c; + return Q931_RES_HAVEEVENT; case Q931_STATUS: if (missingmand) { q931_status(pri, c, PRI_CAUSE_MANDATORY_IE_MISSING); @@ -3464,7 +3483,7 @@ pri->ev.hangup.cause = c->cause; pri->ev.hangup.cref = c->cr; pri->ev.hangup.call = c; - pri->ev.hangup.aoc_units = c->aoc_units; + pri->ev.hangup.aoce_units = c->aoce_units; libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); /* Free resources */ UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); @@ -3492,7 +3511,7 @@ pri->ev.hangup.cause = c->cause; pri->ev.hangup.cref = c->cr; pri->ev.hangup.call = c; - pri->ev.hangup.aoc_units = c->aoc_units; + pri->ev.hangup.aoce_units = c->aoce_units; libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; /* Free resources */ @@ -3527,7 +3546,7 @@ pri->ev.hangup.cause = c->cause; pri->ev.hangup.cref = c->cr; pri->ev.hangup.call = c; - pri->ev.hangup.aoc_units = c->aoc_units; + pri->ev.hangup.aoce_units = c->aoce_units; libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; /* Don't send release complete if they send us release @@ -3555,7 +3574,7 @@ pri->ev.hangup.cause = c->cause; pri->ev.hangup.cref = c->cr; pri->ev.hangup.call = c; - pri->ev.hangup.aoc_units = c->aoc_units; + pri->ev.hangup.aoce_units = c->aoce_units; libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; if (c->alive) @@ -3669,7 +3688,7 @@ c->causecode = -1; c->causeloc = -1; c->sugcallstate = -1; - c->aoc_units = -1; + c->aoce_units = -1; UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; @@ -3677,7 +3696,7 @@ pri->ev.hangup.cause = c->cause; pri->ev.hangup.cref = c->cr; pri->ev.hangup.call = c; - pri->ev.hangup.aoc_units = c->aoc_units; + pri->ev.hangup.aoce_units = c->aoce_units; libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); /* Free resources */