diff --git a/libpri.h b/libpri.h index 2e68ba4..b17f6b4 100755 --- a/libpri.h +++ b/libpri.h @@ -46,11 +46,11 @@ #define PRI_SWITCH_DMS100 2 /* DMS 100 */ #define PRI_SWITCH_LUCENT5E 3 /* Lucent 5E */ #define PRI_SWITCH_ATT4ESS 4 /* AT&T 4ESS */ -#define PRI_SWITCH_EUROISDN_E1 5 /* Standard EuroISDN (CTR4, ETSI 300-102) */ -#define PRI_SWITCH_EUROISDN_T1 6 /* T1 EuroISDN variant (ETSI 300-102) */ +#define PRI_SWITCH_EUROISDN_E1 5 /* Standard EuroISDN (CTR4, ETSI 300-102) */ +#define PRI_SWITCH_EUROISDN_T1 6 /* T1 EuroISDN variant (ETSI 300-102) */ #define PRI_SWITCH_NI1 7 /* National ISDN 1 */ -#define PRI_SWITCH_GR303_EOC 8 /* GR-303 Embedded Operations Channel */ -#define PRI_SWITCH_GR303_TMC 9 /* GR-303 Timeslot Management Channel */ +#define PRI_SWITCH_GR303_EOC 8 /* GR-303 Embedded Operations Channel */ +#define PRI_SWITCH_GR303_TMC 9 /* GR-303 Timeslot Management Channel */ #define PRI_SWITCH_QSIG 10 /* QSIG Switch */ /* Switchtypes 10 - 20 are reserved for internal use */ @@ -73,6 +73,7 @@ #define PRI_EVENT_HANGUP_REQ 15 /* Requesting the higher layer to hangup */ #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 */ /* Simple states */ #define PRI_STATE_DOWN 0 @@ -347,6 +348,13 @@ typedef struct pri_event_notify { int info; } pri_event_notify; +typedef struct pri_event_keypad_digit { + int e; + int channel; + q931_call *call; + char digits[64]; +} pri_event_keypad_digit; + typedef union { int e; pri_event_generic gen; /* Generic view */ @@ -361,6 +369,7 @@ typedef union { pri_event_proceeding proceeding; /* Call proceeding & Progress */ 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; struct pri; @@ -477,7 +486,21 @@ extern int pri_sr_set_caller(struct pri_sr *sr, char *caller, char *callername, extern int pri_sr_set_redirecting(struct pri_sr *sr, char *num, int plan, int pres, int reason); extern int pri_setup(struct pri *pri, q931_call *call, struct pri_sr *req); - + +/* Set a call has a call indpendent signalling connection (i.e. no bchan) */ +extern int pri_sr_set_connection_call_independent(struct pri_sr *req); + +/* Send an MWI indication to a remote location. If activate is non zero, activates, if zero, decativates */ +extern int pri_mwi_activate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan); + +/* Send an MWI deactivate request to a remote location */ +extern int pri_mwi_deactivate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan); + +#define PRI_2BCT +/* Attempt to pass the channels back to the NET side if compatable and + * suscribed. Sometimes called 2 bchannel transfer (2BCT) */ +int pri_channel_bridge(q931_call *call1, q931_call *call2); + /* Override message and error stuff */ extern void pri_set_message(void (*__pri_error)(char *)); extern void pri_set_error(void (*__pri_error)(char *)); diff --git a/pri.c b/pri.c index 0af67dd..71774d5 100755 --- a/pri.c +++ b/pri.c @@ -22,6 +22,7 @@ #include "compat.h" #include "libpri.h" #include "pri_internal.h" +#include "pri_facility.h" #include "pri_q921.h" #include "pri_q931.h" #include "pri_timers.h" @@ -231,6 +232,30 @@ char *pri_event2str(int id) return "Restart channel"; case PRI_EVENT_RING: return "Ring"; + case PRI_EVENT_HANGUP: + return "Hangup"; + case PRI_EVENT_RINGING: + return "Ringing"; + case PRI_EVENT_ANSWER: + return "Answer"; + case PRI_EVENT_HANGUP_ACK: + return "Hangup ACK"; + case PRI_EVENT_RESTART_ACK: + return "Restart ACK"; + case PRI_EVENT_FACNAME: + return "FacName"; + case PRI_EVENT_INFO_RECEIVED: + return "Info Received"; + case PRI_EVENT_PROCEEDING: + return "Proceeding"; + case PRI_EVENT_SETUP_ACK: + return "Setup ACK"; + case PRI_EVENT_HANGUP_REQ: + return "Hangup Req"; + case PRI_EVENT_NOTIFY: + return "Notify"; + case PRI_EVENT_PROGRESS: + return "Progress"; case PRI_EVENT_CONFIG_ERR: return "Configuration Error"; default: @@ -399,6 +424,28 @@ int pri_disconnect(struct pri *pri, q931_call *call, int cause) } #endif +int pri_channel_bridge(q931_call *call1, q931_call *call2) +{ + if (!call1 || !call2) + return -1; + + /* Check switchtype compatibility */ + if (call1->pri->switchtype != PRI_SWITCH_LUCENT5E || + call2->pri->switchtype != PRI_SWITCH_LUCENT5E) + return -1; + + /* Check to see if calls are on the same PRI dchannel + * Currently only support calls on the same dchannel + */ + if (call1->pri != call2->pri) + return -1; + + if (eect_initiate_transfer(call1->pri, call1, call2)) + return -1; + + return 0; +} + int pri_hangup(struct pri *pri, q931_call *call, int cause) { if (!pri || !call) @@ -456,10 +503,71 @@ static void pri_sr_init(struct pri_sr *req) } +int pri_sr_set_connection_call_independent(struct pri_sr *req) +{ + if (!req) + return -1; + + req->justsignalling = 1; /* have to set justsignalling for all those pesky IEs we need to setup */ + 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) +{ + struct pri_sr req; + if (!pri || !c) + return -1; + + pri_sr_init(&req); + pri_sr_set_connection_call_independent(&req); + + req.caller = caller; + req.callerplan = callerplan; + req.callername = callername; + req.callerpres = callerpres; + req.called = called; + req.calledplan = calledplan; + + if (mwi_message_send(pri, c, &req, 1) < 0) { + pri_message("Unable to send MWI activate message\n"); + return -1; + } + /* Do more stuff when we figure out that the CISC stuff works */ + return q931_setup(pri, c, &req); +} + +int pri_mwi_deactivate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, + int calledplan) +{ + struct pri_sr req; + if (!pri || !c) + return -1; + + pri_sr_init(&req); + pri_sr_set_connection_call_independent(&req); + + req.caller = caller; + req.callerplan = callerplan; + req.callername = callername; + req.callerpres = callerpres; + req.called = called; + req.calledplan = calledplan; + + if(mwi_message_send(pri, c, &req, 0) < 0) { + pri_message("Unable to send MWI deactivate message\n"); + return -1; + } + + return q931_setup(pri, c, &req); +} + int pri_setup(struct pri *pri, q931_call *c, struct pri_sr *req) { if (!pri || !c) return -1; + return q931_setup(pri, c, req); } diff --git a/pri_facility.c b/pri_facility.c index ecefb58..c75c27c 100755 --- a/pri_facility.c +++ b/pri_facility.c @@ -1,9 +1,12 @@ /* + This file and it's contents are licensed under the terms and conditions + of the GNU Public License. See http://www.gnu.org for details. + Routines for dealing with facility messages and their respective components (ROSE) by Matthew Fredrickson - Copyright (C) 2004 Digium, Inc + Copyright (C) 2004-2005 Digium, Inc */ #include "compat.h" @@ -18,9 +21,14 @@ #undef DEBUG +static unsigned char get_invokeid(struct pri *pri) +{ + return ++pri->last_invoke; +} + struct addressingdataelements_presentednumberunscreened { - char partyAddress[21]; - char partySubaddress[21]; + char partyaddress[21]; + char partysubaddress[21]; int npi; int ton; int pres; @@ -182,8 +190,8 @@ static int rose_number_digits_decode(struct pri *pri, q931_call *call, unsigned pri_message("!! Oversized NumberDigits component (%d)\n", comp->len); return -1; } - memcpy(value->partyAddress, comp->data, comp->len); - value->partyAddress[comp->len] = '\0'; + memcpy(value->partyaddress, comp->data, comp->len); + value->partyaddress[comp->len] = '\0'; return 0; } @@ -331,8 +339,8 @@ static int rose_diverting_leg_information2_decode(struct pri *pri, q931_call *ca int i = 0; int diversion_counter; int diversion_reason; - struct addressingdataelements_presentednumberunscreened divertingNr; - struct addressingdataelements_presentednumberunscreened originalCalledNr; + struct addressingdataelements_presentednumberunscreened divertingnr; + struct addressingdataelements_presentednumberunscreened originalcallednr; struct rose_component *comp = NULL; unsigned char *vdata = data; @@ -359,23 +367,23 @@ static int rose_diverting_leg_information2_decode(struct pri *pri, q931_call *ca for(; i < len; NEXT_COMPONENT(comp, i)) { GET_COMPONENT(comp, i, vdata, len); switch(comp->type) { - case 0xA1: /* divertingNr: PresentedNumberUnscreened */ - if(rose_presented_number_unscreened_decode(pri, call, comp->data, comp->len, &divertingNr) != 0) + case 0xA1: /* divertingnr: presentednumberunscreened */ + if(rose_presented_number_unscreened_decode(pri, call, comp->data, comp->len, &divertingnr) != 0) return -1; #ifdef DEBUG if (pri->debug) { - pri_message(" Received divertingNr '%s'\n", divertingNr.partyAddress); - pri_message(" ton = %d, pres = %d, npi = %d\n", divertingNr.ton, divertingNr.pres, divertingNr.npi); + pri_message(" Received divertingNr '%s'\n", divertingnr.partyaddress); + pri_message(" ton = %d, pres = %d, npi = %d\n", divertingnr.ton, divertingnr.pres, divertingnr.npi); } #endif break; case 0xA2: /* originalCalledNr: PresentedNumberUnscreened */ - if(rose_presented_number_unscreened_decode(pri, call, comp->data, comp->len, &originalCalledNr) != 0) + if(rose_presented_number_unscreened_decode(pri, call, comp->data, comp->len, &originalcallednr) != 0) return -1; #ifdef DEBUG if (pri->debug) { - pri_message(" Received originalCalledNr '%s'\n", originalCalledNr.partyAddress); - pri_message(" ton = %d, pres = %d, npi = %d\n", originalCalledNr.ton, originalCalledNr.pres, originalCalledNr.npi); + pri_message(" Received originalcallednr '%s'\n", originalcallednr.partyaddress); + pri_message(" ton = %d, pres = %d, npi = %d\n", originalcallednr.ton, originalcallednr.pres, originalcallednr.npi); } #endif break; @@ -387,18 +395,18 @@ static int rose_diverting_leg_information2_decode(struct pri *pri, q931_call *ca if (i < len) return -1; /* Aborted before */ - if (divertingNr.pres >= 0) { - call->redirectingplan = divertingNr.npi; - call->redirectingpres = divertingNr.pres; + if (divertingnr.pres >= 0) { + call->redirectingplan = divertingnr.npi; + call->redirectingpres = divertingnr.pres; call->redirectingreason = diversion_reason; - strncpy(call->redirectingnum, divertingNr.partyAddress, sizeof(call->redirectingnum)-1); + strncpy(call->redirectingnum, divertingnr.partyaddress, sizeof(call->redirectingnum)-1); call->redirectingnum[sizeof(call->redirectingnum)-1] = '\0'; } - else if (originalCalledNr.pres >= 0) { - call->redirectingplan = originalCalledNr.npi; - call->redirectingpres = originalCalledNr.pres; + else if (originalcallednr.pres >= 0) { + call->redirectingplan = originalcallednr.npi; + call->redirectingpres = originalcallednr.pres; call->redirectingreason = diversion_reason; - strncpy(call->redirectingnum, originalCalledNr.partyAddress, sizeof(call->redirectingnum)-1); + strncpy(call->redirectingnum, originalcallednr.partyaddress, sizeof(call->redirectingnum)-1); call->redirectingnum[sizeof(call->redirectingnum)-1] = '\0'; } return 0; @@ -408,7 +416,364 @@ static int rose_diverting_leg_information2_decode(struct pri *pri, q931_call *ca return -1; } -int rose_invoke_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) +static int rose_diverting_leg_information2_encode(struct pri *pri, q931_call *call) +{ + int i = 0, j, compsp = 0; + struct rose_component *comp, *compstk[10]; + unsigned char buffer[256]; + int len = 253; + + if (!strlen(call->callername)) { + return -1; + } + + buffer[i] = 0x80 | Q932_PROTOCOL_EXTENSIONS; + i++; + /* Interpretation component */ + ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0x00 /* Discard unrecognized invokes */); + + ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); + + ASN1_PUSH(compstk, compsp, comp); + /* Invoke component contents */ + /* Invoke ID */ + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); + /* Operation Tag */ + + /* ROSE operationId component */ + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, ROSE_DIVERTING_LEG_INFORMATION2); + + /* ROSE ARGUMENT component */ + ASN1_ADD_SIMPLE(comp, 0x30, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + /* ROSE DivertingLegInformation2.diversionCounter component */ + /* Always is 1 because other isn't available in the current design */ + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, 1); + + /* ROSE DivertingLegInformation2.diversionReason component */ + ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, redirectingreason_from_q931(pri, call->redirectingreason)); + + /* ROSE DivertingLegInformation2.divertingNr component */ + ASN1_ADD_SIMPLE(comp, 0xA1, buffer, i); + + ASN1_PUSH(compstk, compsp, comp); + /* Redirecting information always not screened */ + + switch(call->redirectingpres) { + case PRES_ALLOWED_USER_NUMBER_NOT_SCREENED: + case PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN: + if (call->redirectingnum && strlen(call->redirectingnum)) { + ASN1_ADD_SIMPLE(comp, 0xA0, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + /* NPI of redirected number is not supported in the current design */ + ASN1_ADD_SIMPLE(comp, 0xA1, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, typeofnumber_from_q931(pri, call->redirectingplan >> 4)); + j = asn1_string_encode(ASN1_NUMERICSTRING, &buffer[i], len - i, 20, call->redirectingnum, strlen(call->redirectingnum)); + if (j < 0) + return -1; + + i += j; + ASN1_FIXUP(compstk, compsp, buffer, i); + ASN1_FIXUP(compstk, compsp, buffer, i); + break; + } + /* fall through */ + case PRES_PROHIB_USER_NUMBER_PASSED_SCREEN: + case PRES_PROHIB_USER_NUMBER_NOT_SCREENED: + ASN1_ADD_SIMPLE(comp, 0x81, buffer, i); + break; + /* Don't know how to handle this */ + case PRES_ALLOWED_NETWORK_NUMBER: + case PRES_PROHIB_NETWORK_NUMBER: + case PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN: + case PRES_PROHIB_USER_NUMBER_FAILED_SCREEN: + ASN1_ADD_SIMPLE(comp, 0x81, buffer, i); + break; + default: + pri_message("!! Undefined presentation value for redirecting number: %d\n", call->redirectingpres); + case PRES_NUMBER_NOT_AVAILABLE: + ASN1_ADD_SIMPLE(comp, 0x82, buffer, i); + break; + } + ASN1_FIXUP(compstk, compsp, buffer, i); + + /* ROSE DivertingLegInformation2.originalCalledNr component */ + /* This information isn't supported by current design - duplicate divertingNr */ + ASN1_ADD_SIMPLE(comp, 0xA2, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + /* Redirecting information always not screened */ + switch(call->redirectingpres) { + case PRES_ALLOWED_USER_NUMBER_NOT_SCREENED: + case PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN: + if (call->redirectingnum && strlen(call->redirectingnum)) { + ASN1_ADD_SIMPLE(comp, 0xA0, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + ASN1_ADD_SIMPLE(comp, 0xA1, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, typeofnumber_from_q931(pri, call->redirectingplan >> 4)); + + j = asn1_string_encode(ASN1_NUMERICSTRING, &buffer[i], len - i, 20, call->redirectingnum, strlen(call->redirectingnum)); + if (j < 0) + return -1; + + i += j; + ASN1_FIXUP(compstk, compsp, buffer, i); + ASN1_FIXUP(compstk, compsp, buffer, i); + break; + } + /* fall through */ + case PRES_PROHIB_USER_NUMBER_PASSED_SCREEN: + case PRES_PROHIB_USER_NUMBER_NOT_SCREENED: + ASN1_ADD_SIMPLE(comp, 0x81, buffer, i); + break; + /* Don't know how to handle this */ + case PRES_ALLOWED_NETWORK_NUMBER: + case PRES_PROHIB_NETWORK_NUMBER: + case PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN: + case PRES_PROHIB_USER_NUMBER_FAILED_SCREEN: + ASN1_ADD_SIMPLE(comp, 0x81, buffer, i); + break; + default: + pri_message("!! Undefined presentation value for redirecting number: %d\n", call->redirectingpres); + case PRES_NUMBER_NOT_AVAILABLE: + ASN1_ADD_SIMPLE(comp, 0x82, buffer, i); + break; + } + ASN1_FIXUP(compstk, compsp, buffer, i); + + /* Fix length of stacked components */ + while(compsp > 0) { + ASN1_FIXUP(compstk, compsp, buffer, i); + } + + if (pri_call_apdu_queue(call, Q931_SETUP, buffer, i, NULL, NULL)) + return -1; + + return 0; +} + +/* Sending callername information functions */ +static int add_callername_facility_ies(struct pri *pri, q931_call *c) +{ + int res = 0; + int i = 0; + unsigned char buffer[256]; + unsigned char namelen = 0; + struct rose_component *comp = NULL, *compstk[10]; + int compsp = 0; + static unsigned char op_tag[] = { + 0x2a, /* informationFollowing 42 */ + 0x86, + 0x48, + 0xce, + 0x15, + 0x00, + 0x04 + }; + + if (!strlen(c->callername)) { + return -1; + } + + buffer[i++] = 0x80 | Q932_PROTOCOL_EXTENSIONS; + /* Interpretation component */ + + ASN1_ADD_SIMPLE(comp, COMP_TYPE_NFE, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + ASN1_ADD_BYTECOMP(comp, 0x80, buffer, i, 0); + ASN1_ADD_BYTECOMP(comp, 0x82, buffer, i, 0); + ASN1_FIXUP(compstk, compsp, buffer, i); + + ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0); + + ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + /* Invoke ID */ + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); + + /* Operation Tag */ + res = asn1_string_encode(ASN1_OBJECTIDENTIFIER, &buffer[i], sizeof(buffer)-i, sizeof(op_tag), op_tag, sizeof(op_tag)); + if (res < 0) + return -1; + i += res; + + ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, 0); + ASN1_FIXUP(compstk, compsp, buffer, i); + + if (pri_call_apdu_queue(c, Q931_SETUP, buffer, i, NULL, NULL)) + return -1; + + + /* Now the ADPu that contains the information that needs sent. + * We can reuse the buffer since the queue function doesn't + * need it. */ + + i = 0; + namelen = strlen(c->callername); + if (namelen > 50) { + namelen = 50; /* truncate the name */ + } + + buffer[i++] = 0x80 | Q932_PROTOCOL_EXTENSIONS; + /* Interpretation component */ + + ASN1_ADD_SIMPLE(comp, COMP_TYPE_NFE, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + ASN1_ADD_BYTECOMP(comp, 0x80, buffer, i, 0); + ASN1_ADD_BYTECOMP(comp, 0x82, buffer, i, 0); + ASN1_FIXUP(compstk, compsp, buffer, i); + + ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0); + + ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + + /* Invoke ID */ + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); + + /* Operation ID: Calling name */ + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, SS_CNID_CALLINGNAME); + + res = asn1_string_encode(0x80, &buffer[i], sizeof(buffer)-i, 50, c->callername, namelen); + if (res < 0) + return -1; + i += res; + ASN1_FIXUP(compstk, compsp, buffer, i); + + if (pri_call_apdu_queue(c, Q931_FACILITY, buffer, i, NULL, NULL)) + return -1; + + return 0; +} + +/* End Callername */ + +/* MWI related encode and decode functions */ +static void mwi_activate_encode_cb(void *data) +{ + return; +} + +extern int mwi_message_send(struct pri* pri, q931_call *call, struct pri_sr *req, int activate) +{ + int i = 0; + unsigned char buffer[255] = ""; + int destlen = strlen(req->called); + struct rose_component *comp = NULL, *compstk[10]; + int compsp = 0; + int res; + + if (destlen <= 0) { + return -1; + } else if (destlen > 20) + destlen = 20; /* Destination number cannot be greater then 20 digits */ + + buffer[i++] = 0x80 | Q932_PROTOCOL_EXTENSIONS; + /* Interpretation component */ + + ASN1_ADD_SIMPLE(comp, COMP_TYPE_NFE, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + ASN1_ADD_BYTECOMP(comp, 0x80, buffer, i, 0); + ASN1_ADD_BYTECOMP(comp, 0x82, buffer, i, 0); + ASN1_FIXUP(compstk, compsp, buffer, i); + + ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0); + + ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); + + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, (activate) ? SS_MWI_ACTIVATE : SS_MWI_DEACTIVATE); + ASN1_ADD_SIMPLE(comp, 0x30 /* Sequence */, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + /* PartyNumber */ + res = asn1_string_encode(0x80, &buffer[i], sizeof(buffer)-i, destlen, req->called, destlen); + + if (res < 0) + return -1; + i += res; + + /* Enumeration: basicService */ + ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, 1 /* contents: Voice */); + ASN1_FIXUP(compstk, compsp, buffer, i); + ASN1_FIXUP(compstk, compsp, buffer, i); + + return pri_call_apdu_queue(call, Q931_SETUP, buffer, i, mwi_activate_encode_cb, NULL); +} +/* End MWI */ + +/* EECT functions */ +extern int eect_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2) +{ + /* Did all the tests to see if we're on the same PRI and + * are on a compatible switchtype */ + /* TODO */ + int i = 0; + int res = 0; + unsigned char buffer[255] = ""; + unsigned short call_reference = c2->cr; + struct rose_component *comp = NULL, *compstk[10]; + int compsp = 0; + static unsigned char op_tag[] = { + 0x2A, + 0x86, + 0x48, + 0xCE, + 0x15, + 0x00, + 0x08, + }; + + buffer[i++] = 0x80 | Q932_PROTOCOL_EXTENSIONS; + /* Interpretation component */ + + ASN1_ADD_SIMPLE(comp, COMP_TYPE_NFE, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + ASN1_ADD_BYTECOMP(comp, 0x80, buffer, i, 0); + ASN1_ADD_BYTECOMP(comp, 0x82, buffer, i, 0); + ASN1_FIXUP(compstk, compsp, buffer, i); + + ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0); + + ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); + + res = asn1_string_encode(ASN1_OBJECTIDENTIFIER, &buffer[i], sizeof(buffer)-i, sizeof(op_tag), op_tag, sizeof(op_tag)); + if (res < 0) + return -1; + i += res; + + ASN1_ADD_SIMPLE(comp, ASN1_SEQUENCE | 0x20, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + ASN1_ADD_WORDCOMP(comp, 0x02, buffer, i, call_reference); + ASN1_FIXUP(compstk, compsp, buffer, i); + ASN1_FIXUP(compstk, compsp, buffer, i); + + res = pri_call_apdu_queue(c1, Q931_FACILITY, buffer, i, NULL, NULL); + if (res) { + pri_message("Could not queue ADPU in facility message\n"); + return -1; + } + + /* Remember that if we queue a facility IE for a facility message we + * have to explicitly send the facility message ourselves */ + + res = q931_facility(c1->pri, c1); + if (res) { + pri_message("Could not schedule facility message for call %d\n", c1->cr); + return -1; + } + + return 0; +} +/* End EECT */ + + +extern int rose_invoke_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) { int i = 0; int operation_tag; @@ -480,3 +845,76 @@ int rose_invoke_decode(struct pri *pri, q931_call *call, unsigned char *data, in return -1; } +extern int pri_call_apdu_queue(q931_call *call, int messagetype, void *apdu, int apdu_len, void (*function)(void *data), void *data) +{ + struct apdu_event *cur = NULL; + struct apdu_event *new_event = NULL; + + if (!call || !messagetype || !apdu || (apdu_len < 1) || (apdu_len > 255)) + return -1; + + new_event = malloc(sizeof(struct apdu_event)); + memset(new_event, 0, sizeof(struct apdu_event)); + + if (new_event) { + new_event->message = messagetype; + new_event->callback = function; + new_event->data = data; + memcpy(new_event->apdu, apdu, apdu_len); + new_event->apdu_len = apdu_len; + } else { + pri_error("malloc failed\n"); + return -1; + } + + if (call->apdus) { + cur = call->apdus; + while (cur->next) { + cur = cur->next; + } + cur->next = new_event; + } else + call->apdus = new_event; + + return 0; +} + +extern int pri_call_apdu_queue_cleanup(q931_call *call) +{ + struct apdu_event *cur_event = NULL, *free_event = NULL; + + if (call && call->apdus) { + cur_event = call->apdus; + while (cur_event) { + /* TODO: callbacks, some way of giving return res on status of apdu */ + free_event = cur_event; + free(free_event); + cur_event = cur_event->next; + } + call->apdus = NULL; + } + + return 0; +} + +extern int pri_call_add_standard_apdus(struct pri *pri, q931_call *call) +{ + if (pri->switchtype == PRI_SWITCH_QSIG) { /* For Q.SIG it does network and cpe operations */ + rose_diverting_leg_information2_encode(pri, call); + } + +#if 0 + if (pri->localtype == PRI_NETWORK) { +#endif + if (1) { + switch (pri->switchtype) { + case PRI_SWITCH_NI2: + add_callername_facility_ies(pri, call); + break; + default: + break; + } + } + return 0; +} + diff --git a/pri_facility.h b/pri_facility.h index 8f92754..039ad46 100755 --- a/pri_facility.h +++ b/pri_facility.h @@ -4,7 +4,7 @@ within those messages. by Matthew Fredrickson - Copyright (C) Digium, Inc. 2004 + Copyright (C) Digium, Inc. 2004-2005 */ #ifndef _PRI_FACILITY_H @@ -134,38 +134,80 @@ struct rose_component { } #define ASN1_GET_INTEGER(component, variable) \ - { \ + do { \ int comp_idx; \ (variable) = 0; \ for (comp_idx = 0; comp_idx < (component)->len; ++comp_idx) \ (variable) = ((variable) << 8) | (component)->data[comp_idx]; \ - } + } while (0) #define ASN1_ADD_SIMPLE(component, comptype, ptr, idx) \ - (component) = (struct rose_component *)&((ptr)[(idx)]); \ - (component)->type = (comptype); \ - (component)->len = 0; \ - (idx) += 2; + do { \ + (component) = (struct rose_component *)&((ptr)[(idx)]); \ + (component)->type = (comptype); \ + (component)->len = 0; \ + (idx) += 2; \ + } while (0) #define ASN1_ADD_BYTECOMP(component, comptype, ptr, idx, value) \ - (component) = (struct rose_component *)&((ptr)[(idx)]); \ - (component)->type = (comptype); \ - (component)->len = 1; \ - (component)->data[0] = (value); \ - (idx) += 3; + do { \ + (component) = (struct rose_component *)&((ptr)[(idx)]); \ + (component)->type = (comptype); \ + (component)->len = 1; \ + (component)->data[0] = (value); \ + (idx) += 3; \ + } while (0) + +#define ASN1_ADD_WORDCOMP(component, comptype, ptr, idx, value) \ + do { \ + int __val = (value); \ + int __i = 0; \ + (component) = (struct rose_component *)&((ptr)[(idx)]); \ + (component)->type = (comptype); \ + if ((__val >> 24)) \ + (component)->data[__i++] = (__val >> 24) & 0xff; \ + if ((__val >> 16)) \ + (component)->data[__i++] = (__val >> 16) & 0xff; \ + if ((__val >> 8)) \ + (component)->data[__i++] = (__val >> 8) & 0xff; \ + (component)->data[__i++] = __val & 0xff; \ + (component)->len = __i; \ + (idx) += 2 + __i; \ + } while (0) #define ASN1_PUSH(stack, stackpointer, component) \ - (stack)[(stackpointer)++] = (component); + (stack)[(stackpointer)++] = (component) #define ASN1_FIXUP(stack, stackpointer, data, idx) \ - --(stackpointer); \ - (stack)[(stackpointer)]->len = (unsigned char *)&((data)[(idx)]) - (unsigned char *)(stack)[(stackpointer)] - 2; + do { \ + --(stackpointer); \ + (stack)[(stackpointer)]->len = (unsigned char *)&((data)[(idx)]) - (unsigned char *)(stack)[(stackpointer)] - 2; \ + } while (0) -/* Decoder fo the invoke part of a ROSE request - It currently only support calling name decode */ +/* Decoder for the invoke part of a ROSE request */ extern int rose_invoke_decode(struct pri *pri, struct q931_call *call, unsigned char *data, int len); + extern int asn1_string_encode(unsigned char asn1_type, void *data, int len, int max_len, void *src, int src_len); -int typeofnumber_from_q931(struct pri *pri, int ton); -int redirectingreason_from_q931(struct pri *pri, int redirectingreason); + +extern int typeofnumber_from_q931(struct pri *pri, int ton); + +extern int redirectingreason_from_q931(struct pri *pri, int redirectingreason); + +/* Queues an MWI apdu on a the given call */ +extern int mwi_message_send(struct pri *pri, q931_call *call, struct pri_sr *req, int activate); + +/* starts a 2BCT */ +extern int eect_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2); + +/* Use this function to queue a facility-IE born ADPU onto a call + * call is the call to use, messagetype is any one of the Q931 messages, + * apdu is the apdu data, apdu_len is the length of the apdu data */ +extern int pri_call_apdu_queue(q931_call *call, int messagetype, void *apdu, int apdu_len, void (*function)(void *data), void *data); + +/* Used by q931.c to cleanup the apdu queue upon destruction of a call */ +extern int pri_call_apdu_queue_cleanup(q931_call *call); + +/* Adds the "standard" ADPUs to a call */ +extern int pri_call_add_standard_apdus(struct pri *pri, q931_call *call); #endif /* _PRI_FACILITY_H */ diff --git a/pri_internal.h b/pri_internal.h index 2645daa..18c2b08 100755 --- a/pri_internal.h +++ b/pri_internal.h @@ -124,11 +124,22 @@ struct pri_sr { int redirectingplan; int redirectingpres; int redirectingreason; + int justsignalling; }; /* Internal switch types */ -#define PRI_SWITCH_GR303_EOC_PATH 10 -#define PRI_SWITCH_GR303_TMC_SWITCHING 11 +#define PRI_SWITCH_GR303_EOC_PATH 19 +#define PRI_SWITCH_GR303_TMC_SWITCHING 20 + +struct apdu_event { + int message; /* What message to send the ADPU in */ + void (*callback)(void *data); /* Callback function for when response is received */ + void *data; /* Data to callback */ + unsigned char apdu[255]; /* ADPU to send */ + int apdu_len; /* Length of ADPU */ + int sent; /* Have we been sent already? */ + struct apdu_event *next; /* Linked list pointer */ +}; /* q931_call datastructure */ @@ -163,6 +174,7 @@ struct q931_call { int rateadaption; int sentchannel; + int justsignalling; /* for a signalling-only connection */ int progcode; /* Progress coding */ int progloc; /* Progress Location */ @@ -184,6 +196,8 @@ struct q931_call { char callernum[256]; /* Caller */ char callername[256]; + char digitbuf[64]; /* Buffer for digits that come in KEYPAD_FACILITY */ + int ani2; /* ANI II */ int calledplan; @@ -202,6 +216,8 @@ struct q931_call { int useruserprotocoldisc; char useruserinfo[256]; char callingsubaddr[256]; /* Calling parties sub address */ + + struct apdu_event *apdus; /* APDU queue for call */ }; extern int pri_schedule_event(struct pri *pri, int ms, void (*function)(void *data), void *data); diff --git a/pri_q931.h b/pri_q931.h index 7a2a47a..ac77ac7 100755 --- a/pri_q931.h +++ b/pri_q931.h @@ -267,6 +267,8 @@ extern int q931_hangup(struct pri *pri, q931_call *call, int cause); extern int q931_restart(struct pri *pri, int channel); +extern int q931_facility(struct pri *pri, q931_call *call); + extern int q931_call_getcrv(struct pri *pri, q931_call *call, int *callmode); extern int q931_call_setcrv(struct pri *pri, q931_call *call, int crv, int callmode); diff --git a/q931.c b/q931.c index 31024ad..5bd4303 100755 --- a/q931.c +++ b/q931.c @@ -277,9 +277,15 @@ static FUNC_RECV(receive_channel_id) } #endif #ifndef NOAUTO_CHANNEL_SELECTION_SUPPORT - if ((ie->data[0] & 3) != 1) { - pri_error("!! Unexpected Channel selection %d\n", ie->data[0] & 3); - return -1; + switch (ie->data[0] & 3) { + case 0: + call->justsignalling = 1; + break; + case 1: + break; + default: + pri_error("!! Unexpected Channel selection %d\n", ie->data[0] & 3); + return -1; } #endif if (ie->data[0] & 0x08) @@ -326,10 +332,17 @@ static FUNC_SEND(transmit_channel_id) { int pos=0; + /* We are ready to transmit single IE only */ if (order > 1) return 0; - + + if (call->justsignalling) { + ie->data[pos++] = 0xac; /* Read the standards docs to figure this out + ECMA-165 section 7.3 */ + return pos + 2; + } + /* Start with standard stuff */ if (pri->switchtype == PRI_SWITCH_GR303_TMC) ie->data[pos] = 0x69; @@ -615,6 +628,13 @@ static FUNC_SEND(transmit_bearer_capability) ie->data[1] = 0x90; return 4; } + + if (call->justsignalling) { + ie->data[0] = 0xa8; + ie->data[1] = 0x80; + return 4; + } + if (pri->switchtype == PRI_SWITCH_ATT4ESS) { /* 4ESS uses a different trans capability for 3.1khz audio */ if (tc == PRI_TRANS_CAP_3_1K_AUDIO) @@ -1047,6 +1067,33 @@ static FUNC_RECV(receive_progress_indicator) return 0; } +static FUNC_SEND(transmit_facility) +{ + struct apdu_event *tmp; + int i = 0; + + for (tmp = call->apdus; tmp; tmp = tmp->next) { + if (tmp->message == msgtype) + break; + } + + if (!tmp) /* No APDU found */ + return 0; + + if (tmp->apdu_len > 235) { /* TODO: find out how much sapce we can use */ + pri_message("Requested ADPU (%d bytes) is too long\n", tmp->apdu_len); + + + return 0; + } + + memcpy(ie->data, tmp->apdu, tmp->apdu_len); + i += tmp->apdu_len; + + return i + 2; +} + +#if 0 static FUNC_SEND(transmit_facility) { int i = 0, j, first_i, compsp = 0; @@ -1220,6 +1267,7 @@ static FUNC_SEND(transmit_facility) finish2: return (i ? i+2 : 0); } +#endif static FUNC_RECV(receive_facility) { @@ -1447,6 +1495,37 @@ static FUNC_DUMP(dump_time_date) pri_message(" ]\n"); } +static FUNC_DUMP(dump_keypad_facility) +{ + char tmp[64] = ""; + + if (ie->len == 0 || ie->len > sizeof(tmp)) + return; + + strncpy(tmp, ie->data, sizeof(tmp)); + pri_message("%c Keypad Facility (len=%2d) [ %s ]\n", prefix, ie->len, tmp ); +} + +static FUNC_RECV(receive_keypad_facility) +{ + int mylen = 0; + + if (ie->len == 0) + return -1; + + if (ie->len > sizeof(call->digitbuf)) + mylen = sizeof(call->digitbuf) - 1; + else + mylen = ie->len; + + strncpy(call->digitbuf, ie->data, mylen); + + /* I must be really neurotic */ + call->digitbuf[sizeof(call->digitbuf)-1] = '\0'; + + return 0; +} + static FUNC_DUMP(dump_display) { int x, y; @@ -2000,7 +2079,7 @@ struct ie ies[] = { { 1, Q931_IE_NOTIFY_IND, "Notification Indicator", dump_notify, receive_notify, transmit_notify }, { 1, Q931_DISPLAY, "Display", dump_display, receive_display, transmit_display }, { 1, Q931_IE_TIME_DATE, "Date/Time", dump_time_date }, - { 1, Q931_IE_KEYPAD_FACILITY, "Keypad Facility" }, + { 1, Q931_IE_KEYPAD_FACILITY, "Keypad Facility", dump_keypad_facility, receive_keypad_facility }, { 0, Q931_IE_SIGNAL, "Signal", dump_signal }, { 1, Q931_IE_SWITCHHOOK, "Switch-hook" }, { 1, Q931_IE_USER_USER, "User-User", dump_user_user, receive_user_user }, @@ -2212,6 +2291,7 @@ static void q931_destroy(struct pri *pri, int cr, q931_call *c) pri_message("NEW_HANGUP DEBUG: Destroying the call, ourstate %s, peerstate %s\n",callstate2str(cur->ourcallstate),callstate2str(cur->peercallstate)); if (cur->retranstimer) pri_schedule_del(pri, cur->retranstimer); + pri_call_apdu_queue_cleanup(cur); free(cur); return; } @@ -2410,6 +2490,8 @@ static int send_message(struct pri *pri, q931_call *c, int msgtype, int ies[]) int offset=0; int x; int codeset; + struct apdu_event *facevent = c->apdus; + memset(buf, 0, sizeof(buf)); len = sizeof(buf); init_header(pri, c, buf, &h, &mh, &len); @@ -2417,11 +2499,30 @@ static int send_message(struct pri *pri, q931_call *c, int msgtype, int ies[]) x=0; codeset = 0; while(ies[x] > -1) { - res = add_ie(pri, c, mh->msg, ies[x], (q931_ie *)(mh->data + offset), len, &codeset); + if (ies[x] == Q931_IE_FACILITY) { + res = 0; + while (facevent) { + if (!facevent->sent && (facevent->message == msgtype)) { + int tmpres; + tmpres = add_ie(pri, c, mh->msg, ies[x], (q931_ie *)(mh->data + offset), len, &codeset); + if (tmpres < 0) { + pri_error("!! Unable to add IE '%s'\n", ie2str(ies[x])); + return -1; + } + res += tmpres; + facevent->sent = 1; + } + facevent = facevent->next; + } + } else { + res = add_ie(pri, c, mh->msg, ies[x], (q931_ie *)(mh->data + offset), len, &codeset); + } + if (res < 0) { pri_error("!! Unable to add IE '%s'\n", ie2str(ies[x])); return -1; } + offset += res; len -= res; x++; @@ -2479,6 +2580,13 @@ static int restart_ack(struct pri *pri, q931_call *c) return send_message(pri, c, Q931_RESTART_ACKNOWLEDGE, restart_ack_ies); } +static int facility_ies[] = { Q931_IE_FACILITY, -1 }; + +int q931_facility(struct pri*pri, q931_call *c) +{ + return send_message(pri, c, Q931_FACILITY, facility_ies); +} + static int notify_ies[] = { Q931_IE_NOTIFY_IND, -1 }; int q931_notify(struct pri *pri, q931_call *c, int channel, int info) @@ -2737,6 +2845,8 @@ static int setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, Q931_IE_F static int gr303_setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, -1 }; +static int cis_setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_CALLED_PARTY_NUMBER, -1 }; + int q931_setup(struct pri *pri, q931_call *c, struct pri_sr *req) { int res; @@ -2757,7 +2867,8 @@ int q931_setup(struct pri *pri, q931_call *c, struct pri_sr *req) c->channelno = req->channel; c->slotmap = -1; c->nonisdn = req->nonisdn; - c->newcall = 0; + c->newcall = 0; + c->justsignalling = req->justsignalling; c->complete = req->numcomplete; if (req->exclusive) c->chanflags = FLAG_EXCLUSIVE; @@ -2810,8 +2921,13 @@ int q931_setup(struct pri *pri, q931_call *c, struct pri_sr *req) c->progressmask = PRI_PROG_CALLER_NOT_ISDN; else c->progressmask = 0; + + pri_call_add_standard_apdus(pri, c); + if (pri->subchannel) 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 res = send_message(pri, c, Q931_SETUP, setup_ies); if (!res) { @@ -2961,6 +3077,8 @@ int q931_receive(struct pri *pri, q931_h *h, int len) int missingmand; int codeset, cur_codeset; int last_ie[8]; + struct apdu_event *cur = NULL; + memset(last_ie, 0, sizeof(last_ie)); if (pri->debug & PRI_DEBUG_Q931_DUMP) q931_dump(h, len, 0); @@ -3455,8 +3573,13 @@ int q931_receive(struct pri *pri, q931_h *h, int len) q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } - if (c->ourcallstate!=Q931_CALL_STATE_OVERLAP_RECEIVING) - break; + if (c->ourcallstate != Q931_CALL_STATE_OVERLAP_RECEIVING) { + pri->ev.e = PRI_EVENT_KEYPAD_DIGIT; + pri->ev.digit.call = c; + pri->ev.digit.channel = c->channelno | (c->ds1no << 8); + strncpy(pri->ev.digit.digits, c->digitbuf, sizeof(pri->ev.digit.digits)); + return Q931_RES_HAVEEVENT; + } pri->ev.e = PRI_EVENT_INFO_RECEIVED; pri->ev.ring.call = c; pri->ev.ring.channel = c->channelno | (c->ds1no << 8); @@ -3464,7 +3587,6 @@ int q931_receive(struct pri *pri, q931_h *h, int len) strncpy(pri->ev.ring.callingsubaddr, c->callingsubaddr, sizeof(pri->ev.ring.callingsubaddr) - 1); pri->ev.ring.complete = c->complete; /* this covers IE 33 (Sending Complete) */ return Q931_RES_HAVEEVENT; - break; case Q931_STATUS_ENQUIRY: if (c->newcall) { q931_release_complete(pri, c, PRI_CAUSE_INVALID_CALL_REFERENCE); @@ -3480,6 +3602,16 @@ int q931_receive(struct pri *pri, q931_h *h, int len) c->peercallstate = Q931_CALL_STATE_OVERLAP_RECEIVING; pri->ev.e = PRI_EVENT_SETUP_ACK; pri->ev.setup_ack.channel = c->channelno; + + cur = c->apdus; + while (cur) { + if (!cur->sent && cur->message == Q931_FACILITY) { + q931_facility(pri, c); + break; + } + cur = cur->next; + } + return Q931_RES_HAVEEVENT; case Q931_NOTIFY: pri->ev.e = PRI_EVENT_NOTIFY; diff --git a/testprilib.c b/testprilib.c index 79373f9..f93811a 100755 --- a/testprilib.c +++ b/testprilib.c @@ -53,6 +53,7 @@ #include #include #include "libpri.h" +#include "pri_q931.h" #ifndef AF_LOCAL #define AF_LOCAL AF_UNIX @@ -89,7 +90,7 @@ static void event1(struct pri *pri, pri_event *e) } #if 0 sr = pri_sr_new(); - pri_sr_set_channel(sr, x, 0, 0); + pri_sr_set_channel(sr, x+1, 0, 0); pri_sr_set_bearer(sr, 0, PRI_LAYER_1_ULAW); pri_sr_set_called(sr, dest, PRI_NATIONAL_ISDN, 1); pri_sr_set_caller(sr, num, name, PRI_NATIONAL_ISDN, PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN); @@ -107,6 +108,11 @@ static void event1(struct pri *pri, pri_event *e) } printf("Setup %d calls!\n", TEST_CALLS); break; + case PRI_EVENT_RINGING: + printf("PRI 1: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); + q931_facility(pri, e->ringing.call); + pri_answer(pri, e->ringing.call, e->ringing.channel, 0); + break; default: printf("PRI 1: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); } @@ -116,6 +122,11 @@ static void event2(struct pri *pri, pri_event *e) { /* CPE */ switch(e->gen.e) { + case PRI_EVENT_RING: + printf("PRI 2: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); + pri_proceeding(pri, e->ring.call, e->ring.channel, 0); + pri_acknowledge(pri, e->ring.call, e->ring.channel, 0); + break; case PRI_EVENT_DCHAN_UP: default: printf("PRI 2: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e);