diff -Naur libpri-1.4-2008-12-10/libpri.h libpri-1.4-2008-12-10.new/libpri.h --- libpri-1.4-2008-12-10/libpri.h 2008-12-11 16:06:35.000000000 +0100 +++ libpri-1.4-2008-12-10.new/libpri.h 2008-12-11 16:37:52.000000000 +0100 @@ -333,6 +333,11 @@ #define PRI_NSF_ATT_MULTIQUEST 0xF0 #define PRI_NSF_CALL_REDIRECTION_SERVICE 0xF7 +/* ECMA 186 */ +#define PRI_CC_CCNRREQUEST 27 +#define PRI_CC_CANCEL 28 +#define PRI_CC_CCBSREQUEST 40 + typedef struct q931_call q931_call; /* Connected line update source code */ @@ -369,9 +374,39 @@ int reason; /* Redirection reasons */ }; +/* Structures for qsig_cc_facilities */ +struct qsig_cc_extension { + int cc_extension_tag; + char extension[256]; +}; + +struct qsig_cc_optional_arg { + char number_A[256]; + char number_B[256]; + int service; + struct qsig_cc_extension cc_extension; +}; + +struct qsig_cc_request_res { + int no_path_reservation; + int retain_service; + struct qsig_cc_extension cc_extension; +}; + /* Command derived from Facility */ #define CMD_REDIRECTING 1 #define CMD_CONNECTEDLINE 2 +#define CMD_CC_CCBSREQUEST_RR 3 +#define CMD_CC_CCNRREQUEST_RR 4 +#define CMD_CC_CANCEL_INV 5 +#define CMD_CC_EXECPOSIBLE_INV 6 +#define CMD_CC_RINGOUT_INV 7 +#define CMD_CC_SUSPEND_INV 8 +#define CMD_CC_ERROR 9 + +#define CCERROR_UNSPECIFIED 1008 +#define CCERROR_REMOTE_USER_BUSY_AGAIN 1012 +#define CCERROR_FAILURE_TO_MATCH 1013 typedef struct cmd_connectedline { @@ -388,11 +423,46 @@ struct pri_party_redirecting redirecting; } cmd_redirecting; +typedef struct cmd_cc_ccbs_rr { + struct qsig_cc_request_res cc_request_res; +} cmd_cc_ccbs_rr; + +typedef struct cmd_cc_ccnr_rr { + struct qsig_cc_request_res cc_request_res; +} cmd_cc_ccnr_rr; + +typedef struct cmd_cc_cancel_inv { + struct qsig_cc_optional_arg cc_optional_arg; +} cmd_cc_cancel_inv; + +typedef struct cmd_cc_execposible_inv { + struct qsig_cc_optional_arg cc_optional_arg; +} cmd_cc_execposible_inv; + +typedef struct cmd_cc_suspend_inv { + struct qsig_cc_extension cc_extension; +} cmd_cc_suspend_inv; + +typedef struct cmd_cc_ringout_inv { + struct qsig_cc_extension cc_extension; +} cmd_cc_ringout_inv; + +typedef struct cmd_cc_error { + int error_value; +} cmd_cc_error; + typedef struct subcommand { int cmd; union { cmd_connectedline connectedline; cmd_redirecting redirecting; + cmd_cc_ccbs_rr cc_ccbs_rr; + cmd_cc_ccnr_rr cc_ccnr_rr; + cmd_cc_cancel_inv cc_cancel_inv; + cmd_cc_execposible_inv cc_execposible_inv; + cmd_cc_suspend_inv cc_suspend_inv; + cmd_cc_ringout_inv cc_ringout_inv; + cmd_cc_error cc_error; }; } subcommand; @@ -432,6 +502,7 @@ char callednum[256]; int calledpres; int calledplan; + struct subcommands subcmds; } pri_event_ringing; typedef struct pri_event_answer { @@ -447,6 +518,7 @@ char connectednum[256]; char connectedname[256]; int source; + struct subcommands subcmds; } pri_event_answer; typedef struct pri_event_facname { @@ -502,6 +574,7 @@ char origcallednum[256]; int callingplanorigcalled; /* Dialing plan of Originally Called Number */ int origredirectingreason; + struct subcommands subcmds; } pri_event_ring; typedef struct pri_event_hangup { @@ -512,6 +585,7 @@ q931_call *call; /* Opaque call pointer */ long aoc_units; /* Advise of Charge number of charged units */ char useruserinfo[260]; /* User->User info */ + struct subcommands subcmds; } pri_event_hangup; typedef struct pri_event_restart_ack { @@ -684,6 +758,11 @@ /* Create a new call */ q931_call *pri_new_call(struct pri *pri); +q931_call *pri_new_nochannel_call(struct pri *pri, int *cr); + +q931_call *pri_find_call(struct pri *pri, int cr); + +void pri_call_set_cc_operation(q931_call *call, int cc_operation); /* Retrieve CRV reference for GR-303 calls. Returns >0 on success. */ int pri_get_crv(struct pri *pri, q931_call *call, int *callmode); @@ -710,6 +789,10 @@ int pri_sr_set_called(struct pri_sr *sr, char *called, int calledplan, int complete); int pri_sr_set_caller(struct pri_sr *sr, char *caller, char *callername, int callerplan, int callerpres); int pri_sr_set_redirecting(struct pri_sr *sr, char *name, char *num, int plan, int pres, int reason); + +int pri_sr_set_ccringout(struct pri_sr *sr, int ccringout); +int pri_sr_set_ccbsnr(struct pri_sr *sr, int ccbsnr); + #define PRI_USER_USER_TX /* Set the user user field. Warning! don't send binary data accross this field */ void pri_sr_set_useruser(struct pri_sr *sr, const char *userchars); @@ -721,6 +804,9 @@ /* Set a call has a call indpendent signalling connection (i.e. no bchan) */ int pri_sr_set_connection_call_independent(struct pri_sr *req); +/* Set a no channel call (i.e. QSIG-CCBS/CCNR) */ +int pri_sr_set_no_channel_call(struct pri_sr *req); + /* Send an MWI indication to a remote location. If activate is non zero, activates, if zero, decativates */ int pri_mwi_activate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan); @@ -824,6 +910,7 @@ #define PRI_TIMER_TM20 28 /* maximum time avaiting XID response */ #define PRI_TIMER_NM20 29 /* number of XID retransmits */ +#define PRI_TIMER_CCBST2 30 /* maximum time on completion of CC Call */ /* Get PRI version */ const char *pri_get_version(void); diff -Naur libpri-1.4-2008-12-10/pri.c libpri-1.4-2008-12-10.new/pri.c --- libpri-1.4-2008-12-10/pri.c 2008-12-11 16:06:35.000000000 +0100 +++ libpri-1.4-2008-12-10.new/pri.c 2008-12-11 16:37:52.000000000 +0100 @@ -166,6 +166,8 @@ return PRI_TIMER_T321; else if (!strcasecmp(timer, "T322")) return PRI_TIMER_T322; + else if (!strcasecmp(timer, "CCT2")) + return PRI_TIMER_CCBST2; else return -1; } @@ -751,6 +753,17 @@ return q931_new_call(pri); } +q931_call *pri_new_nochannel_call(struct pri *pri, int *cr) +{ + q931_call *call; + if (!pri) + return NULL; + call = q931_new_call(pri); + if (cr) + *cr = call->cr; + return call; +} + void pri_dump_event(struct pri *pri, pri_event *e) { if (!pri || !e) @@ -793,6 +806,15 @@ return 0; } +int pri_sr_set_no_channel_call(struct pri_sr *req) +{ + if (!req) + return -1; + + req->nochannelsignalling = 1; + return 0; +} + /* Don't call any other pri functions on this */ int pri_mwi_activate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan) @@ -943,6 +965,10 @@ struct q921_frame *f; int q921outstanding = 0; #endif + q931_call *cur, *prev; + struct pri *master; + int counter = 0; + if (!pri) return NULL; @@ -976,10 +1002,57 @@ len += sprintf(buf + len, "T309 Timer: %d\n", pri->timers[PRI_TIMER_T309]); len += sprintf(buf + len, "T313 Timer: %d\n", pri->timers[PRI_TIMER_T313]); len += sprintf(buf + len, "N200 Counter: %d\n", pri->timers[PRI_TIMER_N200]); + len += sprintf(buf + len, "CCT2 Timer: %d\n", pri->timers[PRI_TIMER_CCBST2]); + /* Find the master - He has the call pool */ + if (pri->master) + master = pri->master; + else + master = pri; + + cur = *master->callpool; + prev = NULL; + while(cur) { + if (cur->cctimer2) { + struct timeval tv; + int time_ms_to_go, time_to_go_min, time_to_go_sec; + gettimeofday(&tv, NULL); + time_ms_to_go = (pri->pri_sched[cur->cctimer2].when.tv_sec - tv.tv_sec)*1000; + time_to_go_min = time_ms_to_go/1000/60; + time_to_go_sec = (time_ms_to_go-(time_to_go_min*60*1000))/1000; + + len += sprintf(buf + len, "%d. Active Q.931 Call: %x cr=%d: (%dmin %dsec)\n", + ++counter, (int)cur, cur->cr, + time_to_go_min, time_to_go_sec); + } else { + len += sprintf(buf + len, "%d. Active Q.931 Call: %x cr=%d\n", ++counter, (int)cur, cur->cr); + } + cur = cur->next; + } return strdup(buf); } +q931_call *pri_find_call(struct pri *pri, int cr) +{ + q931_call *cur; + struct pri *master; + + /* Find the master - He has the call pool */ + if (pri->master) + master = pri->master; + else + master = pri; + + cur = *master->callpool; + while(cur) { + if (cur->cr == cr) + return cur; + cur = cur->next; + } + + return NULL; +} + int pri_get_crv(struct pri *pri, q931_call *call, int *callmode) { return q931_call_getcrv(pri, call, callmode); @@ -1051,3 +1124,20 @@ sr->redirectingreason = reason; return 0; } + +int pri_sr_set_ccringout(struct pri_sr *sr, int ccringout) +{ + sr->ccringout = ccringout; + return 0; +} + +int pri_sr_set_ccbsnr(struct pri_sr *sr, int ccbsnr) +{ + sr->ccbsnr = ccbsnr; + return 0; +} + +void pri_call_set_cc_operation(q931_call *call, int cc_operation) +{ + call->ccoperation = cc_operation; +} diff -Naur libpri-1.4-2008-12-10/pri_facility.c libpri-1.4-2008-12-10.new/pri_facility.c --- libpri-1.4-2008-12-10/pri_facility.c 2008-12-11 16:06:35.000000000 +0100 +++ libpri-1.4-2008-12-10.new/pri_facility.c 2008-12-11 16:37:52.000000000 +0100 @@ -3589,6 +3589,289 @@ return 0; } +/* ===== Begin Call Completion Supplementary Service (ETS 300 366/ECMA 186) ===== */ +/* operationId e.g. QSIG_CCBSRINGOUT, QSIG_CC_CANCEL */ +int add_qsigCcInv_facility_ie (struct pri *pri, q931_call *c, int messagetype) +{ + int i = 0; + unsigned char buffer[256]; + struct rose_component *comp = NULL, *compstk[10]; + int compsp = 0; + u_int8_t operationId = c->ccoperation; + + /* 1 Byte 0x80 | 0x1F = 9F Protocol Profile */ + buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); + + /* Interpretation component */ + ASN1_ADD_SIMPLE(comp, COMP_TYPE_NFE, buffer, i); /* 2. Byte NEtwork Facility Extension 0xAA = ASN1_CONTEXT_SPECIFIC(0x80) | (ASN1_CONSTRUCTOR 0x20) 0x0A (Tag laut Standard) */ + ASN1_PUSH(compstk, compsp, comp); + ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), buffer, i, 0); /* (0x80, 0x01(len), 0x00) endPTNX */ + ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i, 0); /* (0x82, 0x01(len), 0x00) endPTNX */ + ASN1_FIXUP(compstk, compsp, buffer, i); + + ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0); /* 0x8B, 0x01(len), 0x00 discard */ + ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); /* 0xA1, 0xXX (len of Invoke Sequenz) invoke APDU */ + ASN1_PUSH(compstk, compsp, comp); + + /* Invoke ID */ + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); /* 0x02 0x01 0xXX */ + + /* Operation ID: QSIG_CCBSRINGOUT, QSIG_CC_CANCEL */ + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, operationId); /* 0x02 0x01 0x1f/0x1c */ + + /* CcExtension */ + ASN1_ADD_SIMPLE(comp, ASN1_NULL, buffer, i); /* 0x05 0x00 */ + + ASN1_FIXUP(compstk, compsp, buffer, i); + + if (pri_call_apdu_queue(c, messagetype, buffer, i, NULL, NULL)) + return -1; + + return 0; +} + +static int rose_cc_ringout_inv_decode(struct pri *pri, struct qsig_cc_extension *cc_extension, struct rose_component *choice, int len) { + int i = 0; + cc_extension->cc_extension_tag = 0; + + do { + switch(choice->type) { + case (ASN1_NULL): /* none NULL */ + cc_extension->cc_extension_tag = ASN1_NULL; + return 0; + + case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_14): /* single [14] IMPLICIT Extension */ + cc_extension->cc_extension_tag = ASN1_TAG_14; + return 0; + + + case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_15): /* multiple [15] IMPLICIT SEQUENCE OF Extension */ + cc_extension->cc_extension_tag = ASN1_TAG_15; + return 0; + + default: + if (choice->type == 0 && choice->len == 0) { + return 0; + } + pri_message(pri, "!! Invalid ss-cc-optional-Arg component received 0x%X\n", choice->type); + return -1; + } + + if (i < len) + pri_message(pri, " ss-cc-extension: !! not all information is handled !! i=%d / len=%d\n", i, len); + + return 0; + } + while (0); + + return -1; +} + +static int rose_cc_optional_arg_decode(struct pri *pri, q931_call *call, struct qsig_cc_optional_arg *cc_optional_arg , struct rose_component *choice, int len) { + int i = 0; + int res = 0; + struct rose_component *comp = NULL; + unsigned char *vdata = choice->data; + struct addressingdataelements_presentednumberunscreened numberA; + struct addressingdataelements_presentednumberunscreened numberB; + + cc_optional_arg->cc_extension.cc_extension_tag = 0; + cc_optional_arg->number_A[0] = '\0'; + cc_optional_arg->number_B[0] = '\0'; + + do { + switch(choice->type) { + case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_0): /* fullArg [0] IMPLICIT SEQUENCE */ + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, " ss-cc-optional-Arg: len=%d\n", len); + + numberA.partyaddress[0] = '\0'; + + /* numberA */ + GET_COMPONENT(comp, i, vdata, len); + res += rose_party_number_decode(pri, (u_int8_t *)comp, comp->len + 2, &numberA); + if (res < 0) + return -1; + comp->len = res; + if (res > 2) { + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, " ss-cc-optional-Arg: Received numberA=%s\n", numberA.partyaddress); + strncpy(cc_optional_arg->number_A, numberA.partyaddress, 20); + cc_optional_arg->number_A[20] = '\0'; + } + NEXT_COMPONENT(comp, i); + + numberB.partyaddress[0] = '\0'; + + /* numberB */ + GET_COMPONENT(comp, i, vdata, len); + res = rose_party_number_decode(pri, (u_int8_t *)comp, comp->len + 2, &numberB); + if (res < 0) + return -1; + comp->len = res; + if (res > 2) { + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, " ss-cc-optional-Arg: Received numberB=%s\n", numberB.partyaddress); + strncpy(cc_optional_arg->number_B, numberB.partyaddress, 20); + cc_optional_arg->number_B[20] = '\0'; + } + NEXT_COMPONENT(comp, i); + + /* service */ /* PSS1InformationElement */ + GET_COMPONENT(comp, i, vdata, len); + NEXT_COMPONENT(comp, i); + + /* optional */ + for (; i < len; NEXT_COMPONENT(comp, i)) { + GET_COMPONENT(comp, i, vdata, len); + switch(comp->type) { + case (ASN1_NULL): /* */ + cc_optional_arg->cc_extension.cc_extension_tag = ASN1_NULL; + NEXT_COMPONENT(comp, i); + break; + case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_14): /* */ + NEXT_COMPONENT(comp, i); + break; + case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_15): /* */ + NEXT_COMPONENT(comp, i); + break; + default: + if (comp->type == 0 && comp->len == 0) { + return 0; + break; /* Found termination characters */ + } + pri_message(pri, "!! Invalid ss-cc-optional-Arg component received 0x%X\n", comp->type); + return -1; + } + } + + if (i < len) + pri_message(pri, " ss-cc-optional-Arg: !! not all information is handled !! i=%d / len=%d\n", i, len); + + return 0; + + /* extArg CcExtension */ + case (ASN1_NULL): /* none NULL */ + cc_optional_arg->cc_extension.cc_extension_tag = ASN1_NULL; + return 0; + + case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_14): /* single [14] IMPLICIT Extension */ + cc_optional_arg->cc_extension.cc_extension_tag = ASN1_TAG_14; + return 0; + + + case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_15): /* multiple [15] IMPLICIT SEQUENCE OF Extension */ + cc_optional_arg->cc_extension.cc_extension_tag = ASN1_TAG_15; + return 0; + + default: + if (choice->type == 0 && choice->len == 0) { + return 0; + } + pri_message(pri, "!! Invalid ss-cc-optional-Arg component received 0x%X\n", choice->type); + return -1; + } + + if (i < len) + pri_message(pri, " ss-cc-optional-Arg: !! not all information is handled !! i=%d / len=%d\n", i, len); + + return 0; + } + while (0); + + return -1; +} + +static int rose_cc_request_result_decode(struct pri *pri, struct qsig_cc_request_res *cc_request_res , struct rose_component *sequence, int len) +{ + int i = 0; + struct rose_component *comp = NULL; + unsigned char *vdata = sequence->data; + + cc_request_res->no_path_reservation = 0; /* Default FALSE */ + cc_request_res->retain_service = 0; /* Default FALSE */ + cc_request_res->cc_extension.cc_extension_tag = 0; + + /* Data checks */ + if (sequence->type != (ASN1_CONSTRUCTOR | ASN1_SEQUENCE)) { /* Constructed Sequence */ + pri_message(pri, "Invalid cc request result argument. (Not a sequence)\n"); + return -1; + } + + if (sequence->len == ASN1_LEN_INDEF) { + len -= 4; /* For the 2 extra characters at the end + * and two characters of header */ + } else + len -= 2; + + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, " CC-request-Return-Result: len=%d\n", len); + + do { + /* defaults and optional */ + for (; i < len; NEXT_COMPONENT(comp, i)) { + GET_COMPONENT(comp, i, vdata, len); + switch(comp->type) { + case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0): + /* no-path-reservation */ + ASN1_GET_INTEGER(comp, cc_request_res->no_path_reservation); + NEXT_COMPONENT(comp, i); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, " cc request result: Received noPathReservation=%d\n", cc_request_res->no_path_reservation); + break; + + case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1): + /* retain_service */ + ASN1_GET_INTEGER(comp, cc_request_res->retain_service); + NEXT_COMPONENT(comp, i); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, " cc request result: Received retainService=%d\n", cc_request_res->retain_service); + break; + + case (ASN1_NULL): /* */ + cc_request_res->cc_extension.cc_extension_tag = ASN1_NULL; + NEXT_COMPONENT(comp, i); + break; + + case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_14): + cc_request_res->cc_extension.cc_extension_tag = ASN1_TAG_14; + NEXT_COMPONENT(comp, i); + break; + + case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_15): + cc_request_res->cc_extension.cc_extension_tag = ASN1_TAG_15; + NEXT_COMPONENT(comp, i); + break; + + default: + if (comp->type == 0 && comp->len == 0) { + break; /* Found termination characters */ + } + pri_message(pri, "!! Invalid ss-cc-optional-Arg component received 0x%X\n", comp->type); + return -1; + } + } + + if (i < len) + pri_message(pri, " ss-cc-optional-Arg: !! not all information is handled !! i=%d / len=%d\n", i, len); + return 0; + } + while (0); + + return -1; +} + +static int rose_ccbs_request_result_decode(struct pri *pri, struct qsig_cc_request_res *cc_request_res , struct rose_component *sequence, int len) +{ + return rose_cc_request_result_decode(pri, cc_request_res , sequence, len); +} + +static int rose_ccnr_request_result_decode(struct pri *pri, struct qsig_cc_request_res *cc_request_res , struct rose_component *sequence, int len) +{ + return rose_cc_request_result_decode(pri, cc_request_res , sequence, len); +} +/* ===== End Call Completion Supplementary Service (ETS 300 366/ECMA 186) ===== */ + int rose_reject_decode(struct pri *pri, q931_call *call, q931_ie *ie, unsigned char *data, int len) { @@ -3660,6 +3943,18 @@ } +static subcommand *get_ptr_subcommand(subcommands *sub) +{ + if (sub->counter_subcmd < MAX_SUBCOMMANDS) { + int count = sub->counter_subcmd; + sub->counter_subcmd++; + return &sub->subcmd[count]; + } + + return NULL; +} + + int rose_return_error_decode(struct pri *pri, q931_call *call, q931_ie *ie, unsigned char *data, int len) { int i = 0; @@ -3668,6 +3963,7 @@ unsigned char *vdata = data; struct rose_component *comp = NULL; char *invokeidstr, *errorstr; + struct subcommand *c_subcmd; do { /* Invoke ID stuff */ @@ -3714,9 +4010,39 @@ pri_error(pri, "\tERROR: %s\n", errorstr); return 0; + } else if (pri->switchtype == PRI_SWITCH_QSIG) { + switch (errorvalue) { + case 1008: + errorstr = "Unspecified"; + break; + case 1012: + errorstr = "Remote user busy again"; + break; + case 1013: + errorstr = "Failure to match"; + break; + default: + errorstr = "Unknown"; + } + + c_subcmd = get_ptr_subcommand(&call->subcmds); + if (!c_subcmd) { + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "ROSE RETURN ERROR %i - more than %d facilities !\n", errorvalue, MAX_SUBCOMMANDS); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + return -1; + } + + if (pri->debug & PRI_DEBUG_APDU) + { + pri_message(pri, "ROSE RETURN RESULT %i: %s\n", errorvalue, errorstr); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + } + c_subcmd->cmd = CMD_CC_ERROR; + c_subcmd->cc_error.error_value = errorvalue; + return 0; } else { - pri_message(pri, "Unable to handle return result on switchtype %d!\n", pri->switchtype); - return -1; + pri_message(pri, "Unable to handle return error on switchtype %d!\n", pri->switchtype); } } while(0); @@ -3731,6 +4057,8 @@ int invokeidvalue = -1; unsigned char *vdata = data; struct rose_component *comp = NULL; + int res; + struct subcommand *c_subcmd; do { /* Invoke ID stuff */ @@ -3777,10 +4105,89 @@ return -1; } } else if (pri->switchtype == PRI_SWITCH_QSIG) { - switch (invokeidvalue) { + int operation_tag; + + /* sequence is optional */ + if (i >= len) + return 0; + + /* Data checks, sequence is optional */ + GET_COMPONENT(comp, i, vdata, len); + if (comp->type != (ASN1_CONSTRUCTOR | ASN1_SEQUENCE)) { /* Constructed Sequence */ + pri_message(pri, "No arguments on cc-return result\n"); + return 0; + } + + if (comp->len == ASN1_LEN_INDEF) { + len -= 2; /* For the 2 extra characters at the end*/ + } + + /* Traverse the contents of this sequence */ + SUB_COMPONENT(comp, i); + + /* Operation Tag */ + GET_COMPONENT(comp, i, vdata, len); + CHECK_COMPONENT(comp, ASN1_INTEGER, "Don't know what to do if second ROSE component is of type 0x%x\n"); + ASN1_GET_INTEGER(comp, operation_tag); + NEXT_COMPONENT(comp, i); + + /* No argument - return with error */ + if (i >= len) + return -1; + + /* Arguement Tag */ + GET_COMPONENT(comp, i, vdata, len); + if (!comp->type) + return -1; + + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, " [ Handling operation %d ]\n", operation_tag); + switch (operation_tag) { case QSIG_CF_CALLREROUTING: - if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, "Successfully completed QSIG CF callRerouting!\n"); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "Successfully completed QSIG CF callRerouting!\n"); return 0; + + case QSIG_CC_CCBSREQUEST: + c_subcmd = get_ptr_subcommand(&call->subcmds); + if (!c_subcmd) { + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "ROSE %i: return_result CcCcbsRequest - more than %d facilities !\n", operation_tag, MAX_SUBCOMMANDS); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + return -1; + } + if (pri->debug & PRI_DEBUG_APDU) + { + pri_message(pri, "ROSE %i: Handle CcCcbsRequest\n", operation_tag); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + } + c_subcmd->cmd = CMD_CC_CCBSREQUEST_RR; + res = rose_ccbs_request_result_decode(pri, &c_subcmd->cc_ccbs_rr.cc_request_res, comp, len-i); + return res; + + case QSIG_CC_CCNRREQUEST: + c_subcmd = get_ptr_subcommand(&call->subcmds); + if (!c_subcmd) { + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "ROSE %i: return_result CcCcnrRequest - more than %d facilities !\n", operation_tag, MAX_SUBCOMMANDS); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + return -1; + } + if (pri->debug & PRI_DEBUG_APDU) + { + pri_message(pri, "ROSE %i: Handle CcCcnrRequest\n", operation_tag); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + } + c_subcmd->cmd = CMD_CC_CCNRREQUEST_RR; + res = rose_ccnr_request_result_decode(pri, &c_subcmd->cc_ccnr_rr.cc_request_res, comp, len-i); + return res; + + default: + if (pri->debug & PRI_DEBUG_APDU) { + pri_message(pri, "!! Unable to handle ROSE operation %d", operation_tag); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + } + return -1; } } else { pri_message(pri, "Unable to handle return result on switchtype %d!\n", pri->switchtype); @@ -3799,6 +4206,7 @@ int operation_tag; unsigned char *vdata = data; struct rose_component *comp = NULL, *invokeid = NULL, *operationid = NULL; + struct subcommand *c_subcmd; do { /* Invoke ID stuff */ @@ -3914,6 +4322,81 @@ } anfpr_pathreplacement_respond(pri, call, ie); break; + case QSIG_CC_CCBSREQUEST: + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "ROSE %i: invoke CcbsRequest - not handled!\n", operation_tag); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + return -1; + case QSIG_CC_CCNRREQUEST: + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "ROSE %i: invoke CcnrRequest - not handled!\n", operation_tag); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + return -1; + case QSIG_CC_CANCEL: + c_subcmd = get_ptr_subcommand(&call->subcmds); + if (!c_subcmd) { + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "ROSE %i: invoke CcCancel - more than %d facilities !\n", operation_tag, MAX_SUBCOMMANDS); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + return -1; + } + if (pri->debug & PRI_DEBUG_APDU) + { + pri_message(pri, "ROSE %i: Handle CcCancel\n", operation_tag); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + } + c_subcmd->cmd = CMD_CC_CANCEL_INV; + res = rose_cc_optional_arg_decode(pri, call, &c_subcmd->cc_cancel_inv.cc_optional_arg, comp, len-i); + return res; + case QSIG_CC_EXECPOSIBLE: + c_subcmd = get_ptr_subcommand(&call->subcmds); + if (!c_subcmd) { + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "ROSE %i: invoke CcExecposible - more than %d facilities !\n", operation_tag, MAX_SUBCOMMANDS); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + return -1; + } + if (pri->debug & PRI_DEBUG_APDU) + { + pri_message(pri, "ROSE %i: Handle CcExecposible\n", operation_tag); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + } + c_subcmd->cmd = CMD_CC_EXECPOSIBLE_INV; + res = rose_cc_optional_arg_decode(pri, call, &c_subcmd->cc_execposible_inv.cc_optional_arg, comp, len-i); + return res; + case QSIG_CC_PATHRESERVE: + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "ROSE %i: invoke CcPathreserve - not handled!\n", operation_tag); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + return -1; + case QSIG_CC_RINGOUT: + c_subcmd = get_ptr_subcommand(&call->subcmds); + if (!c_subcmd) { + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "ROSE %i: invoke CcRingout - more than %d facilities !\n", operation_tag, MAX_SUBCOMMANDS); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + return -1; + } + if (pri->debug & PRI_DEBUG_APDU) + { + pri_message(pri, "ROSE %i: Handle CcRingout\n", operation_tag); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + } + c_subcmd->cmd = CMD_CC_RINGOUT_INV; + res = rose_cc_ringout_inv_decode(pri, &c_subcmd->cc_ringout_inv.cc_extension, comp, len-i); + return res; + case QSIG_CC_SUSPEND: + if (pri->debug & PRI_DEBUG_APDU) + { + pri_message(pri, "ROSE %i: Handle CcSuspend\n", operation_tag); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + } + return 0; + case QSIG_CC_RESUME: + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "ROSE %i: invoke CcResume - not handled!\n", operation_tag); + dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + return -1; default: if (pri->debug & PRI_DEBUG_APDU) { pri_message(pri, "!! Unable to handle ROSE operation %d", operation_tag); @@ -4097,6 +4580,95 @@ return 0; } +/* ===== Begin Call Completion Supplementary Service (ETS 300 366/ECMA 186) ===== */ +/* operationId e.g. QSIG_CC_CCBS_REQUEST and QSIG_CC_CCNR_REQUEST */ +static int add_qsigCcRequestArg_facility_ie (struct pri *pri, q931_call *c) +{ + int size = 0; + int i = 0; + unsigned char buffer[256]; + struct rose_component *comp = NULL, *compstk[10]; + int compsp = 0; + u_int8_t operationId = c->ccoperation; + char *numberA = c->callernum; + char *numberB = c->callednum; + + /* 1 Byte 0x80 | 0x1F = 9F Protocol Profile (0x93 wäre altes QSIG oder DDS1) */ + buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); + + /* Interpretation component */ + ASN1_ADD_SIMPLE(comp, COMP_TYPE_NFE, buffer, i); /* 2. Byte NEtwork Facility Extension 0xAA = ASN1_CONTEXT_SPECIFIC(0x80) | (ASN1_CONSTRUCTOR 0x20) 0x0A (Tag laut Standard) */ + ASN1_PUSH(compstk, compsp, comp); + + ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), buffer, i, 0); /* (0x80, 0x01(len), 0x00) endPTNX */ + ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i, 0); /* (0x82, 0x01(len), 0x00) endPTNX */ + + ASN1_FIXUP(compstk, compsp, buffer, i); + +#if 0 + ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0); /* 0x8B, 0x01(len), 0x00 discard */ +#endif + ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); /* 0xA1, 0xXX (len of Invoke Sequenz) invoke APDU */ + ASN1_PUSH(compstk, compsp, comp); + + /* Invoke ID */ + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); /* InvokeID 0x02 0x01 0xXX */ + + /*CcbsRequest ::= 40 or CcnrRequest ::= 27 */ + /* Operation ID: CCBS/CCNR */ + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, operationId); /* 0x02 0x01 0x28/0x1b */ + + /* ccbs/ccnr request argument */ + /* PresentedNumberUnscreened */ + ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); /*0x30 0xXX (len)*/ + ASN1_PUSH(compstk, compsp, comp); + /* (0xA0, 0x01(len)) presentationAlloweAddress [0] PartyNumber */ + /* (0xA1, 0xXX (len) publicPartyNumber [1] IMPLICIT PublicPartyNumber */ + /* (0x0A, 0x01, 0x00 ) type of public party number = subscriber number */ + /* (0x12, 0xXX (len), 0xXX .. 0xXX) numeric string */ + size = rose_presented_number_unscreened_encode(pri, &buffer[i], PRES_ALLOWED, Q932_TON_UNKNOWN, numberA); + if (size < 0) + return -1; + i += size; + + /* (0xA1, 0xXX (len) [1] IMPLICIT PublicPartyNumber */ + ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1), buffer, i); + ASN1_PUSH(compstk, compsp, comp); + /* (0x0A, 0x01, 0x00 ) type of public party number = subscriber number */ + /* (0x12, 0xXX (len), 0xXX .. 0xXX) numeric string */ + size = rose_public_party_number_encode(pri, comp->data, 1, Q932_TON_UNKNOWN, numberB); + if (size < 0) + return -1; + i += size; + ASN1_FIXUP(compstk, compsp, buffer, i); + + /* (0x40, 0xXX (len), 0xXX .. 0xXX) pSS1InfoElement */ + ASN1_ADD_SIMPLE(comp, (ASN1_APPLICATION | ASN1_TAG_0 ), buffer, i); + ASN1_PUSH(compstk, compsp, comp); + buffer[i++] = (0x04); /* add Bearer Capability IE */ + buffer[i++] = (0x03); /* len*/ + buffer[i++] = (0x80); /* ETSI Standard, Speech */ + buffer[i++] = (0x90); /* circuit mode, 64kbit/s */ + buffer[i++] = (0xa3); /* level1 protocol, a-law */ + ASN1_FIXUP(compstk, compsp, buffer, i); +#if 0 + /* can-retain-service [12] IMPLICIT BOOLEAN DEFAULT FALSE,*/ + ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_12), buffer, i, 0); /* 0x1C, 0x01(len), 0x00 false */ +#endif + /* retain-sig-connection [13] IMPLICIT BOOLEAN OPTIONAL, --TRUE: sign. connection to be retained */ + ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_13), buffer, i, 1); /* 0x1D, 0x01(len), 0x01 true */ + + ASN1_FIXUP(compstk, compsp, buffer, i); + + ASN1_FIXUP(compstk, compsp, buffer, i); + + if (pri_call_apdu_queue(c, Q931_SETUP, buffer, i, NULL, NULL)) + return -1; + + return 0; +} +/* ===== End Call Completion Supplementary Service (ETS 300 366/ECMA 186) ===== */ + int pri_call_add_standard_apdus(struct pri *pri, q931_call *call) { if (!pri->sendfacility) @@ -4106,6 +4678,21 @@ if (call->redirectingnum[0]) rose_diverting_leg_information2_encode(pri, call); add_callername_facility_ies(pri, call, 1); + if (call->ccoperation) { + switch(call->ccoperation) { + case 0: + break; + case QSIG_CC_CCBSREQUEST: + case QSIG_CC_CCNRREQUEST: + add_qsigCcRequestArg_facility_ie(pri, call); + break; + case QSIG_CC_RINGOUT: + add_qsigCcInv_facility_ie(pri, call, Q931_SETUP); + break; + default: + break; + } + } return 0; } diff -Naur libpri-1.4-2008-12-10/pri_facility.h libpri-1.4-2008-12-10.new/pri_facility.h --- libpri-1.4-2008-12-10/pri_facility.h 2008-12-11 16:06:35.000000000 +0100 +++ libpri-1.4-2008-12-10.new/pri_facility.h 2008-12-11 16:37:52.000000000 +0100 @@ -81,6 +81,20 @@ #define SS_MWI_DEACTIVATE 81 #define SS_MWI_INTERROGATE 82 +/* ECMA 186 */ +#define QSIG_CC_CCNRREQUEST PRI_CC_CCNRREQUEST /* 27 */ +#define QSIG_CC_CANCEL PRI_CC_CANCEL /* 28 */ +#define QSIG_CC_EXECPOSIBLE 29 +#define QSIG_CC_PATHRESERVE 30 +#define QSIG_CC_RINGOUT 31 +#define QSIG_CC_SUSPEND 32 +#define QSIG_CC_RESUME 33 +#define QSIG_CC_CCBSREQUEST PRI_CC_CCBSREQUEST /* 40 */ + +#define QSIG_CC_UNSPECIFIED 1008 +#define QSIG_CC_REMOTE_USER_BUSY_AGAIN 1012 +#define QSIG_CC_FAILURE_TO_MATCH 1013 + /* ROSE definitions and data structures */ #define INVOKE_IDENTIFIER 0x02 #define INVOKE_LINKED_IDENTIFIER 0x80 @@ -126,6 +140,8 @@ #define ASN1_TAG_11 0x0b #define ASN1_TAG_12 0x0c #define ASN1_TAG_13 0x0d +#define ASN1_TAG_14 0x0e +#define ASN1_TAG_15 0x0f /* ASN.1 Identifier Octet - Primitive/Constructor Bit */ #define ASN1_PC_MASK 0x20 @@ -349,6 +365,8 @@ /* Adds the "standard" APDUs to a call */ int pri_call_add_standard_apdus(struct pri *pri, q931_call *call); +int add_qsigCcInv_facility_ie (struct pri *pri, q931_call *c, int messagetype); + int asn1_dump(struct pri *pri, void *comp, int len); #endif /* _PRI_FACILITY_H */ diff -Naur libpri-1.4-2008-12-10/pri_internal.h libpri-1.4-2008-12-10.new/pri_internal.h --- libpri-1.4-2008-12-10/pri_internal.h 2008-12-11 16:06:35.000000000 +0100 +++ libpri-1.4-2008-12-10.new/pri_internal.h 2008-12-11 16:37:52.000000000 +0100 @@ -53,7 +53,7 @@ enum q931_mode; /* No more than 128 scheduled events */ -#define MAX_SCHED 128 +#define MAX_SCHED 128+256 /* 256 ccT2 timer events*/ #define MAX_TIMERS 32 @@ -154,8 +154,11 @@ int redirectingpres; int redirectingreason; int justsignalling; + int nochannelsignalling; + int ccbsnr; const char *useruserinfo; int transferable; + int ccringout; }; /* Internal switch types */ @@ -209,6 +212,7 @@ int sentchannel; int justsignalling; /* for a signalling-only connection */ + int nochannelsignalling; int progcode; /* Progress coding */ int progloc; /* Progress Location */ @@ -232,6 +236,12 @@ char callernum[256]; char callername[256]; + int ccoperation; /* QSIG_CCBSREQUEST/QSIG_CCNRREQUEST */ + int ccrequestresult; + int cctimer2; /* Timer for QSIG-timer2 */ + /* QSIG cc infos (receive) */ + struct subcommands subcmds; + char keypad_digits[64]; /* Buffer for digits that come in KEYPAD_FACILITY */ int ani2; /* ANI II */ diff -Naur libpri-1.4-2008-12-10/pri_timers.h libpri-1.4-2008-12-10.new/pri_timers.h --- libpri-1.4-2008-12-10/pri_timers.h 2008-12-10 10:39:38.000000000 +0100 +++ libpri-1.4-2008-12-10.new/pri_timers.h 2008-12-11 16:37:52.000000000 +0100 @@ -62,6 +62,7 @@ -1, /* T322 */ \ 2500, /* TM20 - Q.921 Appendix IV */ \ 3, /* NM20 - Q.921 Appendix IV */ \ + 45*60*1000 /* CC-Timer2 45 min */\ } /* XXX Only our default timers are setup now XXX */ diff -Naur libpri-1.4-2008-12-10/q931.c libpri-1.4-2008-12-10.new/q931.c --- libpri-1.4-2008-12-10/q931.c 2008-12-11 16:06:35.000000000 +0100 +++ libpri-1.4-2008-12-10.new/q931.c 2008-12-11 16:37:52.000000000 +0100 @@ -362,6 +362,11 @@ /* We are ready to transmit single IE only */ if (order > 1) return 0; + + if (call->nochannelsignalling) { + ie->data[pos++] = 0xac; + return pos + 2; + } if (call->justsignalling) { ie->data[pos++] = 0xac; /* Read the standards docs to figure this out @@ -804,7 +809,13 @@ ie->data[1] = 0x90; return 4; } - + + if (call->nochannelsignalling) { + ie->data[0] = 0xa8; + ie->data[1] = 0x80; + return 4; + } + if (call->justsignalling) { ie->data[0] = 0xa8; ie->data[1] = 0x80; @@ -2958,6 +2969,18 @@ q931_release(pri, c, PRI_CAUSE_NORMAL_CLEARING); } +static void pri_cctimer2_timeout(void *data) +{ + int cause = 16; + struct q931_call *c = data; + struct pri *pri = c->pri; + if (pri->debug & PRI_DEBUG_Q931_STATE) + pri_message(pri, "Timed out no-channel call\n"); + c->ccoperation = QSIG_CC_CANCEL; + /* normal clear cause */ + q931_hangup(pri, c, cause); +} + int q931_connect(struct pri *pri, q931_call *c, int channel, int nonisdn) { if (channel) { @@ -2989,7 +3012,7 @@ 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, Q931_IE_FACILITY, -1 }; int q931_release(struct pri *pri, q931_call *c, int cause) { @@ -3037,7 +3060,7 @@ return send_message(pri, c, Q931_RESTART, restart_ies); } -static int disconnect_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, -1 }; +static int disconnect_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, Q931_IE_FACILITY, -1 }; int q931_disconnect(struct pri *pri, q931_call *c, int cause) { @@ -3065,6 +3088,8 @@ static int cis_setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_CALLED_PARTY_NUMBER, -1 }; +static int nochannel_setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_CALLING_PARTY_NUMBER, Q931_CALLED_PARTY_NUMBER, Q931_SENDING_COMPLETE, -1 }; + int q931_setup(struct pri *pri, q931_call *c, struct pri_sr *req) { int res; @@ -3090,6 +3115,7 @@ c->nonisdn = req->nonisdn; c->newcall = 0; c->justsignalling = req->justsignalling; + c->nochannelsignalling = req->nochannelsignalling; c->complete = req->numcomplete; if (req->exclusive) c->chanflags = FLAG_EXCLUSIVE; @@ -3150,12 +3176,19 @@ else c->progressmask = 0; + if (req->ccringout) + c->ccoperation = QSIG_CC_RINGOUT; + if (req->ccbsnr) + c->ccoperation = req->ccbsnr; + pri_call_add_standard_apdus(pri, c); if (pri->subchannel && !pri->bri) res = send_message(pri, c, Q931_SETUP, gr303_setup_ies); else if (c->justsignalling) res = send_message(pri, c, Q931_SETUP, cis_setup_ies); + else if (c->nochannelsignalling) + res = send_message(pri, c, Q931_SETUP, nochannel_setup_ies); else res = send_message(pri, c, Q931_SETUP, setup_ies); if (!res) { @@ -3169,7 +3202,7 @@ } -static int release_complete_ies[] = { Q931_IE_USER_USER, -1 }; +static int release_complete_ies[] = { Q931_IE_USER_USER, Q931_IE_FACILITY, -1 }; static int q931_release_complete(struct pri *pri, q931_call *c, int cause) { @@ -3224,6 +3257,18 @@ /* We'll send RELEASE with these causes */ disconnect = 0; } + if (c->nochannelsignalling) { + if (c->ccoperation == QSIG_CC_CANCEL) { + add_qsigCcInv_facility_ie(pri, c, Q931_RELEASE); + } + if (c->cctimer2) { + pri_schedule_del(pri, c->cctimer2); + c->cctimer2 = 0; + pri_message(pri, "NEW_HANGUP DEBUG: stop CC-Timer2\n"); + } + disconnect = 0; + } + /* All other causes we send with DISCONNECT */ switch(c->ourcallstate) { case Q931_CALL_STATE_NULL: @@ -3264,6 +3309,10 @@ break; case Q931_CALL_STATE_ACTIVE: /* received CONNECT */ + if (c->nochannelsignalling) { + q931_release(pri,c,cause); + break; + } q931_disconnect(pri,c,cause); break; case Q931_CALL_STATE_DISCONNECT_REQUEST: @@ -3313,6 +3362,116 @@ return NULL; } +static subcommand *get_ptr_q931_subcommand_by_index(subcommands *sub, int index) +{ + if (index < MAX_SUBCOMMANDS) { + sub->counter_subcmd--; + return &sub->subcmd[index]; + } + + return NULL; +} + +static int q931_facilities2eventfacilities(struct pri *pri, q931_call *c, subcommands *subcmds) +{ + int facilitypos; + int facility_number; + struct subcommand *c_subcmd; + struct subcommand *e_subcmd; + + if (c->subcmds.counter_subcmd) { + facility_number = c->subcmds.counter_subcmd; + + for (facilitypos = 0; facilitypos < facility_number; facilitypos++) { + c->subcmds.counter_subcmd--; + c_subcmd = get_ptr_q931_subcommand_by_index(&c->subcmds, facilitypos); + e_subcmd = get_ptr_subcommand(subcmds); + if (c_subcmd && e_subcmd) { + switch (c_subcmd->cmd) { + case CMD_CC_CCBSREQUEST_RR: + e_subcmd->cmd = c_subcmd->cmd; + memcpy(&c_subcmd->cc_ccbs_rr, &e_subcmd->cc_ccbs_rr, sizeof(c_subcmd->cc_ccbs_rr)); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "facility(QSIG_CC_CCBSREQUEST) (%d/%d)\n", + e_subcmd->cc_ccbs_rr.cc_request_res.no_path_reservation, + e_subcmd->cc_ccbs_rr.cc_request_res.retain_service); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "counter_subcmd(%d)\n", subcmds->counter_subcmd); + c->ccrequestresult = 1; + break; + case CMD_CC_CCNRREQUEST_RR: + e_subcmd->cmd = c_subcmd->cmd; + memcpy(&c_subcmd->cc_ccnr_rr, &e_subcmd->cc_ccnr_rr, sizeof(c_subcmd->cc_ccnr_rr)); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "facility(QSIG_CC_CCNRREQUEST) (%d/%d)\n", + e_subcmd->cc_ccnr_rr.cc_request_res.no_path_reservation, + e_subcmd->cc_ccnr_rr.cc_request_res.retain_service); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "counter_subcmd(%d)\n", subcmds->counter_subcmd); + c->ccrequestresult = 1; + break; + case CMD_CC_CANCEL_INV: + e_subcmd->cmd = c_subcmd->cmd; + memcpy(&c_subcmd->cc_cancel_inv, &e_subcmd->cc_cancel_inv, sizeof(c_subcmd->cc_cancel_inv)); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "facility(QSIG_CC_CANCEL) (%s/%s)\n", + e_subcmd->cc_cancel_inv.cc_optional_arg.number_A, + e_subcmd->cc_cancel_inv.cc_optional_arg.number_B); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "counter_subcmd(%d)\n", subcmds->counter_subcmd); + break; + case CMD_CC_EXECPOSIBLE_INV: + e_subcmd->cmd = c_subcmd->cmd; + memcpy(&c_subcmd->cc_execposible_inv, &e_subcmd->cc_execposible_inv, sizeof(c_subcmd->cc_execposible_inv)); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "facility(QSIG_CC_EXECPOSIBLE) (%s/%s)\n", + e_subcmd->cc_execposible_inv.cc_optional_arg.number_A, + e_subcmd->cc_execposible_inv.cc_optional_arg.number_B); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "counter_subcmd(%d)\n", subcmds->counter_subcmd); + break; + case CMD_CC_RINGOUT_INV: + e_subcmd->cmd = c_subcmd->cmd; + memcpy(&c_subcmd->cc_ringout_inv, &e_subcmd->cc_ringout_inv, sizeof(c_subcmd->cc_ringout_inv)); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "facility(QSIG_CC_RINGOUT) (%d)\n", + e_subcmd->cc_ringout_inv.cc_extension.cc_extension_tag); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "counter_subcmd(%d)\n", subcmds->counter_subcmd); + break; + case CMD_CC_SUSPEND_INV: + e_subcmd->cmd = c_subcmd->cmd; + memcpy(&c_subcmd->cc_suspend_inv, &e_subcmd->cc_suspend_inv, sizeof(c_subcmd->cc_suspend_inv)); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "facility(QSIG_CC_SUSPEND) (%d)\n", + e_subcmd->cc_suspend_inv.cc_extension.cc_extension_tag); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "counter_subcmd(%d)\n", subcmds->counter_subcmd); + break; + case CMD_CC_ERROR: + e_subcmd->cmd = c_subcmd->cmd; + memcpy(&c_subcmd->cc_error, &e_subcmd->cc_error, sizeof(c_subcmd->cc_error)); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "facility(QSIG_CC_ERROR) (%d)\n", + e_subcmd->cc_error.error_value); + if (pri->debug & PRI_DEBUG_APDU) + pri_message(pri, "counter_subcmd(%d)\n", subcmds->counter_subcmd); + break; + default: + pri_error(pri, "Don't know how to handle Facility subcmd %d\n", c_subcmd->cmd); + break; + } + } + } +#if 0 + } else { + pri_message(pri, "No facilities specified\n"); +#endif + } + return 0; +} + + int q931_receive(struct pri *pri, q931_h *h, int len) { q931_mh *mh; @@ -3365,6 +3524,7 @@ break; case Q931_FACILITY: c->callername[0] = '\0'; + c->subcmds.counter_subcmd = 0; break; case Q931_SETUP: if (pri->debug & PRI_DEBUG_Q931_STATE) @@ -3430,7 +3590,9 @@ c->aoc_units = -1; /* Fall through */ case Q931_CONNECT: + c->ccrequestresult = 0; case Q931_ALERTING: + c->subcmds.counter_subcmd = 0; case Q931_PROGRESS: c->useruserinfo[0] = '\0'; c->cause = -1; @@ -3446,6 +3608,7 @@ break; case Q931_RELEASE: case Q931_DISCONNECT: + c->subcmds.counter_subcmd = 0; c->cause = -1; c->causecode = -1; c->causeloc = -1; @@ -3460,6 +3623,7 @@ pri_schedule_del(pri, c->retranstimer); c->retranstimer = 0; c->useruserinfo[0] = '\0'; + c->subcmds.counter_subcmd = 0; /* Fall through */ case Q931_STATUS: c->cause = -1; @@ -3616,6 +3780,9 @@ if (!c->newcall) { break; } + + clr_subcommands(&pri->ev.ring.subcmds); + if (c->progressmask & PRI_PROG_CALLER_NOT_ISDN) c->nonisdn = 1; c->newcall = 0; @@ -3659,12 +3826,19 @@ pri->ev.ring.ctype = c->transcapability; pri->ev.ring.progress = c->progress; pri->ev.ring.progressmask = c->progressmask; + if (pri->debug & PRI_DEBUG_APDU) { + pri_message(pri, "Sending ring event (%d) nochannelsignalling (%d) facility_number (%d)\n", + pri->ev.ring.cref, c->nochannelsignalling, pri->ev.ring.subcmds.counter_subcmd); + } + q931_facilities2eventfacilities(pri, c, &pri->ev.ring.subcmds); return Q931_RES_HAVEEVENT; case Q931_ALERTING: if (c->newcall) { q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } + clr_subcommands(&pri->ev.ringing.subcmds); + UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_CALL_DELIVERED); c->peercallstate = Q931_CALL_STATE_CALL_RECEIVED; pri->ev.e = PRI_EVENT_RINGING; @@ -3688,6 +3862,11 @@ } libpri_copy_string(pri->ev.ringing.useruserinfo, c->useruserinfo, sizeof(pri->ev.ringing.useruserinfo)); c->useruserinfo[0] = '\0'; + if (pri->debug & PRI_DEBUG_APDU) { + pri_message(pri, "Sending ringing event (%d) nochannelsignalling (%d) facility_number (%d)\n", + pri->ev.ringing.cref, c->nochannelsignalling, pri->ev.ringing.subcmds.counter_subcmd); + } + q931_facilities2eventfacilities(pri, c, &pri->ev.ringing.subcmds); cur = c->apdus; while (cur) { @@ -3708,6 +3887,8 @@ q931_status(pri, c, PRI_CAUSE_WRONG_MESSAGE); break; } + clr_subcommands(&pri->ev.answer.subcmds); + UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_ACTIVE); c->peercallstate = Q931_CALL_STATE_CONNECT_REQUEST; pri->ev.e = PRI_EVENT_ANSWER; @@ -3723,7 +3904,19 @@ pri->ev.answer.source = PRI_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; libpri_copy_string(pri->ev.answer.useruserinfo, c->useruserinfo, sizeof(pri->ev.answer.useruserinfo)); c->useruserinfo[0] = '\0'; + if (pri->debug & PRI_DEBUG_APDU) { + pri_message(pri, "Sending answer event (%d) nochannelsignalling (%d) facility_number (%d)\n", + pri->ev.answer.cref, c->nochannelsignalling, pri->ev.answer.subcmds.counter_subcmd); + } + q931_facilities2eventfacilities(pri, c, &pri->ev.answer.subcmds); q931_connect_acknowledge(pri, c); + if (c->nochannelsignalling) { + if (c->ccrequestresult) { + pri_message(pri, "Q931_CONNECT: start CC-Timer2\n"); + c->cctimer2 = pri_schedule_event(pri, pri->timers[PRI_TIMER_CCBST2], pri_cctimer2_timeout, c); + } + return Q931_RES_HAVEEVENT; + } if (c->justsignalling) { /* Make sure WE release when we initiatie a signalling only connection */ q931_release(pri, c, PRI_CAUSE_NORMAL_CLEARING); break; @@ -3738,6 +3931,19 @@ q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } + if (c->subcmds.counter_subcmd) { + pri->ev.e = PRI_EVENT_FACILITY; + pri->ev.facility.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); + pri->ev.facility.cref = c->cr; + pri->ev.facility.call = c; + + if (pri->debug & PRI_DEBUG_APDU) { + pri_message(pri, "Sending facility event (%d) nochannelsignalling (%d) facility_number (%d)\n", + pri->ev.facility.cref, c->nochannelsignalling, pri->ev.facility.subcmds.counter_subcmd); + } + q931_facilities2eventfacilities(pri, c, &pri->ev.facility.subcmds); + haveevent = 1; + } if (c->ctcompleteflag) { c->ctcompleteflag = 0; @@ -3952,6 +4158,8 @@ } break; case Q931_RELEASE_COMPLETE: + clr_subcommands(&pri->ev.hangup.subcmds); + UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; pri->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); @@ -3961,6 +4169,11 @@ pri->ev.hangup.aoc_units = c->aoc_units; libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; + if (pri->debug & PRI_DEBUG_APDU) { + pri_message(pri, "Sending hangup event (%d) nochannelsignalling (%d) facility_number (%d)\n", + pri->ev.hangup.cref, c->nochannelsignalling, pri->ev.hangup.subcmds.counter_subcmd); + } + q931_facilities2eventfacilities(pri, c, &pri->ev.hangup.subcmds); /* Free resources */ if (c->alive) { pri->ev.e = PRI_EVENT_HANGUP; @@ -3978,6 +4191,8 @@ q931_hangup(pri,c,c->cause); break; case Q931_RELEASE: + clr_subcommands(&pri->ev.hangup.subcmds); + if (missingmand) { /* Force cause to be mandatory IE missing */ c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; @@ -3996,6 +4211,11 @@ pri->ev.hangup.aoc_units = c->aoc_units; libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; + if (pri->debug & PRI_DEBUG_APDU) { + pri_message(pri, "Sending hangup event (%d) nochannelsignalling (%d) facility_number (%d)\n", + pri->ev.hangup.cref, c->nochannelsignalling, pri->ev.hangup.subcmds.counter_subcmd); + } + q931_facilities2eventfacilities(pri, c, &pri->ev.hangup.subcmds); /* Don't send release complete if they send us release while we sent it, assume a NULL state */ if (c->newcall) @@ -4004,6 +4224,8 @@ return Q931_RES_HAVEEVENT; break; case Q931_DISCONNECT: + clr_subcommands(&pri->ev.hangup.subcmds); + if (missingmand) { /* Still let user call release */ c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; @@ -4030,6 +4252,11 @@ pri->ev.hangup.aoc_units = c->aoc_units; libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; + if (pri->debug & PRI_DEBUG_APDU) { + pri_message(pri, "Sending hangup event (%d) nochannelsignalling (%d) facility_number (%d)\n", + pri->ev.hangup.cref, c->nochannelsignalling, pri->ev.hangup.subcmds.counter_subcmd); + } + q931_facilities2eventfacilities(pri, c, &pri->ev.hangup.subcmds); if (c->alive) return Q931_RES_HAVEEVENT; else