From b7d92655621e5d2d3542fa945adf6c75946522e5 Mon Sep 17 00:00:00 2001 From: Stefan Knoblich Date: Mon, 8 Nov 2010 00:43:09 +0100 Subject: [PATCH] ftmod_libpri: First attempt at getting AOC Facility messages going (and minor cleanups) Requires libpri-1.4.12_beta1 or newer! Completely untested, AOC-S/D/E ChargingRequest operations are sent to NT (DTAG BRI PTMP), but rejected with "Unrecognized Operation". NOTE: FreeTDM core needs support for this to be useful! Only AOC-E events are decoded and sent to the log. To enable: Set (or add) in your libpri_spans span config. Signed-off-by: Stefan Knoblich --- libs/freetdm/configure.ac | 14 +- .../src/ftmod/ftmod_libpri/ftmod_libpri.c | 199 +++++++++++++++++- .../src/ftmod/ftmod_libpri/ftmod_libpri.h | 5 +- .../src/ftmod/ftmod_libpri/lpwrap_pri.c | 59 +++--- .../src/ftmod/ftmod_libpri/lpwrap_pri.h | 6 +- 5 files changed, 245 insertions(+), 38 deletions(-) diff --git a/libs/freetdm/configure.ac b/libs/freetdm/configure.ac index 7589957999..2b5ded8e7d 100644 --- a/libs/freetdm/configure.ac +++ b/libs/freetdm/configure.ac @@ -213,11 +213,23 @@ then [extern void pri_new_bri_cb(void);], [pri_new_bri_cb();] )], - [AC_DEFINE([HAVE_LIBPRI_BRI], [1], [libpri has bri support]) + [AC_DEFINE([HAVE_LIBPRI_BRI], [1], [libpri has BRI support]) AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_MSG_NOTICE([You will need libpri-1.4.12_beta1 or newer for BRI support])] ) + + AC_MSG_CHECKING([whether libpri has AOC event support]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [extern void pri_aoc_events_enable(void);], + [pri_aoc_events_enable();] + )], + [AC_DEFINE([HAVE_LIBPRI_AOC], [1], [libpri has AOC event support]) + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_MSG_NOTICE([You will need libpri-1.4.12_beta1 or newer for AOC event support])] + ) LIBS="${save_LIBS}" fi HAVE_LIBPRI="${enable_libpri}" diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c index ccb2342b02..65148f393e 100644 --- a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c +++ b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c @@ -30,7 +30,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "private/ftdm_core.h" #include "ftmod_libpri.h" @@ -578,7 +577,7 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan) sr = pri_sr_new(); assert(sr); pri_sr_set_channel(sr, ftdmchan->chan_id, 0, 0); - pri_sr_set_bearer(sr, 0, isdn_data->l1); + pri_sr_set_bearer(sr, PRI_TRANS_CAP_SPEECH, isdn_data->l1); pri_sr_set_called(sr, ftdmchan->caller_data.dnis.digits, dp, 1); pri_sr_set_caller(sr, ftdmchan->caller_data.cid_num.digits, (isdn_data->opts & FTMOD_LIBPRI_OPT_OMIT_DISPLAY_IE ? NULL : ftdmchan->caller_data.cid_name), @@ -589,7 +588,13 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan) pri_sr_set_redirecting(sr, ftdmchan->caller_data.cid_num.digits, dp, PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, PRI_REDIR_UNCONDITIONAL); } - +#ifdef HAVE_LIBPRI_AOC + if (isdn_data->opts & FTMOD_LIBPRI_OPT_FACILITY_AOC) { + /* request AOC on call */ + pri_sr_set_aoc_charging_request(sr, (PRI_AOC_REQUEST_S | PRI_AOC_REQUEST_E | PRI_AOC_REQUEST_D)); + ftdm_log(FTDM_LOG_DEBUG, "Requesting AOC-S/D/E on call\n"); + } +#endif if (pri_setup(isdn_data->spri.pri, call, sr)) { ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_DESTINATION_OUT_OF_ORDER; ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); @@ -978,6 +983,182 @@ static int on_restart(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_eve return 0; } +/* + * FACILITY Advice-On-Charge handler + */ +#ifdef HAVE_LIBPRI_AOC +static const char *aoc_billing_id(const int id) +{ + switch (id) { + case PRI_AOC_E_BILLING_ID_NOT_AVAILABLE: + return "not available"; + case PRI_AOC_E_BILLING_ID_NORMAL: + return "normal"; + case PRI_AOC_E_BILLING_ID_REVERSE: + return "reverse"; + case PRI_AOC_E_BILLING_ID_CREDIT_CARD: + return "credit card"; + case PRI_AOC_E_BILLING_ID_CALL_FORWARDING_UNCONDITIONAL: + return "call forwarding unconditional"; + case PRI_AOC_E_BILLING_ID_CALL_FORWARDING_BUSY: + return "call forwarding busy"; + case PRI_AOC_E_BILLING_ID_CALL_FORWARDING_NO_REPLY: + return "call forwarding no reply"; + case PRI_AOC_E_BILLING_ID_CALL_DEFLECTION: + return "call deflection"; + case PRI_AOC_E_BILLING_ID_CALL_TRANSFER: + return "call transfer"; + default: + return "unknown\n"; + } +} + +static float aoc_money_amount(const struct pri_aoc_amount *amount) +{ + switch (amount->multiplier) { + case PRI_AOC_MULTIPLIER_THOUSANDTH: + return amount->cost * 0.001f; + case PRI_AOC_MULTIPLIER_HUNDREDTH: + return amount->cost * 0.01f; + case PRI_AOC_MULTIPLIER_TENTH: + return amount->cost * 0.1f; + case PRI_AOC_MULTIPLIER_TEN: + return amount->cost * 10.0f; + case PRI_AOC_MULTIPLIER_HUNDRED: + return amount->cost * 100.0f; + case PRI_AOC_MULTIPLIER_THOUSAND: + return amount->cost * 1000.0f; + default: + return amount->cost; + } +} + +static int handle_facility_aoc_s(const struct pri_subcmd_aoc_s *aoc_s) +{ + /* Left as an excercise to the reader */ + return 0; +} + +static int handle_facility_aoc_d(const struct pri_subcmd_aoc_d *aoc_d) +{ + /* Left as an excercise to the reader */ + return 0; +} + +static int handle_facility_aoc_e(const struct pri_subcmd_aoc_e *aoc_e) +{ + char tmp[1024] = { 0 }; + int x = 0, offset = 0; + + switch (aoc_e->charge) { + case PRI_AOC_DE_CHARGE_FREE: + strcat(tmp, "\tcharge-type: none\n"); + offset = strlen(tmp); + break; + + case PRI_AOC_DE_CHARGE_CURRENCY: + sprintf(tmp, "\tcharge-type: money\n\tcharge-amount: %.2f\n\tcharge-currency: %s\n", + aoc_money_amount(&aoc_e->recorded.money.amount), + aoc_e->recorded.money.currency); + offset = strlen(tmp); + break; + + case PRI_AOC_DE_CHARGE_UNITS: + strcat(tmp, "\tcharge-type: units\n"); + offset = strlen(tmp); + + for (x = 0; x < aoc_e->recorded.unit.num_items; x++) { + sprintf(&tmp[offset], "\tcharge-amount: %ld (type: %d)\n", + aoc_e->recorded.unit.item[x].number, + aoc_e->recorded.unit.item[x].type); + offset += strlen(&tmp[offset]); + } + break; + + default: + strcat(tmp, "\tcharge-type: not available\n"); + offset = strlen(tmp); + } + + sprintf(&tmp[offset], "\tbilling-id: %s\n", aoc_billing_id(aoc_e->billing_id)); + offset += strlen(&tmp[offset]); + + strcat(&tmp[offset], "\tassociation-type: "); + offset += strlen(&tmp[offset]); + + switch (aoc_e->associated.charging_type) { + case PRI_AOC_E_CHARGING_ASSOCIATION_NOT_AVAILABLE: + strcat(&tmp[offset], "not available\n"); + break; + case PRI_AOC_E_CHARGING_ASSOCIATION_NUMBER: + sprintf(&tmp[offset], "number\n\tassociation-number: %s\n", aoc_e->associated.charge.number.str); + break; + case PRI_AOC_E_CHARGING_ASSOCIATION_ID: + sprintf(&tmp[offset], "id\n\tassociation-id: %d\n", aoc_e->associated.charge.id); + break; + default: + strcat(&tmp[offset], "unknown\n"); + } + + ftdm_log(FTDM_LOG_INFO, "AOC-E:\n%s", tmp); + return 0; +} +#endif + +/** + * \brief Handler for libpri facility events + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_facility(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + struct pri_event_facility *pfac = (struct pri_event_facility *)pevent; + int i = 0; + + if (!pevent) + return 0; + + if (!pfac->subcmds || pfac->subcmds->counter_subcmd <= 0) + return 0; + + for (i = 0; i < pfac->subcmds->counter_subcmd; i++) { + struct pri_subcommand *sub = &pfac->subcmds->subcmd[i]; + int res = -1; + + switch (sub->cmd) { +#ifdef HAVE_LIBPRI_AOC + case PRI_SUBCMD_AOC_S: /* AOC-S: Start of call */ + res = handle_facility_aoc_s(&sub->u.aoc_s); + break; + case PRI_SUBCMD_AOC_D: /* AOC-D: During call */ + res = handle_facility_aoc_d(&sub->u.aoc_d); + break; + case PRI_SUBCMD_AOC_E: /* AOC-E: End of call */ + res = handle_facility_aoc_e(&sub->u.aoc_e); + break; + case PRI_SUBCMD_AOC_CHARGING_REQ: + ftdm_log(FTDM_LOG_NOTICE, "AOC Charging Request received\n"); + break; + case PRI_SUBCMD_AOC_CHARGING_REQ_RSP: + ftdm_log(FTDM_LOG_NOTICE, "AOC Charging Request Response received [aoc_s data: %s, req: %x, resp: %x]\n", + sub->u.aoc_request_response.valid_aoc_s ? "yes" : "no", + sub->u.aoc_request_response.charging_request, + sub->u.aoc_request_response.charging_response); + break; +#endif + default: + ftdm_log(FTDM_LOG_DEBUG, "FACILITY subcommand %d is not implemented, ignoring\n", sub->cmd); + } + + ftdm_log(FTDM_LOG_DEBUG, "FACILITY subcommand %d handler returned %d\n", sub->cmd, res); + } + + ftdm_log(FTDM_LOG_DEBUG, "Caught Event on span %d %u (%s)\n", spri->span->span_id, event_type, lpwrap_pri_event_str(event_type)); + return 0; +} + /** * \brief Handler for libpri dchan up event * \param spri Pri wrapper structure (libpri, span, dchan) @@ -1145,6 +1326,13 @@ static void *ftdm_libpri_run(ftdm_thread_t *me, void *obj) goto out; } +#ifdef HAVE_LIBPRI_AOC + /* enable FACILITY on trunk, if needed */ + if (isdn_data->opts & FTMOD_LIBPRI_OPT_FACILITY_AOC) { + pri_facility_enable(isdn_data->spri.pri); + } +#endif + if (res == 0) { LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_ANY, on_anything); LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_RING, on_ring); @@ -1159,6 +1347,7 @@ static void *ftdm_libpri_run(ftdm_thread_t *me, void *obj) LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_INFO_RECEIVED, on_info); LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_RESTART, on_restart); LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_IO_FAIL, on_io_fail); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_FACILITY, on_facility); if (down) { ftdm_log(FTDM_LOG_INFO, "PRI back up on span %d\n", isdn_data->spri.span->span_id); @@ -1359,7 +1548,9 @@ static uint32_t parse_opts(const char *in) if (strstr(in, "omit_redirecting_number")) { flags |= FTMOD_LIBPRI_OPT_OMIT_REDIRECTING_NUMBER_IE; } - + if (strstr(in, "aoc")) { + flags |= FTMOD_LIBPRI_OPT_FACILITY_AOC; + } return flags; } diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.h b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.h index 0bf8c46817..b11baec6c5 100644 --- a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.h +++ b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.h @@ -42,8 +42,9 @@ typedef enum { FTMOD_LIBPRI_OPT_SUGGEST_CHANNEL = (1 << 0), FTMOD_LIBPRI_OPT_OMIT_DISPLAY_IE = (1 << 1), FTMOD_LIBPRI_OPT_OMIT_REDIRECTING_NUMBER_IE = (1 << 2), - - FTMOD_LIBPRI_OPT_MAX = (1 << 3) + FTMOD_LIBPRI_OPT_FACILITY_AOC = (1 << 3), + + FTMOD_LIBPRI_OPT_MAX = (1 << 4) } ftdm_isdn_opts_t; typedef enum { diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.c b/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.c index 5b04abec1b..317c9a5428 100644 --- a/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.c +++ b/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.c @@ -92,7 +92,7 @@ static struct lpwrap_pri_event_list LPWRAP_PRI_EVENT_LIST[] = { {8, LPWRAP_PRI_EVENT_ANSWER, "ANSWER"}, {9, LPWRAP_PRI_EVENT_HANGUP_ACK, "HANGUP_ACK"}, {10, LPWRAP_PRI_EVENT_RESTART_ACK, "RESTART_ACK"}, - {11, LPWRAP_PRI_EVENT_FACNAME, "FACNAME"}, + {11, LPWRAP_PRI_EVENT_FACILITY, "FACILITY"}, {12, LPWRAP_PRI_EVENT_INFO_RECEIVED, "INFO_RECEIVED"}, {13, LPWRAP_PRI_EVENT_PROCEEDING, "PROCEEDING"}, {14, LPWRAP_PRI_EVENT_SETUP_ACK, "SETUP_ACK"}, @@ -107,6 +107,9 @@ static struct lpwrap_pri_event_list LPWRAP_PRI_EVENT_LIST[] = { const char *lpwrap_pri_event_str(lpwrap_pri_event_t event_id) { + if (event_id < 0 || event_id >= LPWRAP_PRI_EVENT_MAX) + return ""; + return LPWRAP_PRI_EVENT_LIST[event_id].name; } @@ -114,8 +117,8 @@ static int __pri_lpwrap_read(struct pri *pri, void *buf, int buflen) { struct lpwrap_pri *spri = (struct lpwrap_pri *) pri_get_userdata(pri); ftdm_size_t len = buflen; - int res; ftdm_status_t zst; + int res; if ((zst = ftdm_channel_read(spri->dchan, buf, &len)) != FTDM_SUCCESS) { if (zst == FTDM_FAIL) { @@ -130,25 +133,25 @@ static int __pri_lpwrap_read(struct pri *pri, void *buf, int buflen) } spri->errs = 0; res = (int)len; - memset(&((unsigned char*)buf)[res],0,2); - res+=2; + + memset(&((unsigned char*)buf)[res], 0, 2); + res += 2; #ifdef IODEBUG { char bb[2048] = { 0 }; print_hex_bytes(buf, res - 2, bb, sizeof(bb)); - ftdm_log(FTDM_LOG_DEBUG, "READ %d\n", res-2); + ftdm_log(FTDM_LOG_DEBUG, "READ %d\n", res - 2); } #endif - return res; } static int __pri_lpwrap_write(struct pri *pri, void *buf, int buflen) { struct lpwrap_pri *spri = (struct lpwrap_pri *) pri_get_userdata(pri); - ftdm_size_t len = buflen -2; + ftdm_size_t len = buflen - 2; if (ftdm_channel_write(spri->dchan, buf, buflen, &len) != FTDM_SUCCESS) { ftdm_log(FTDM_LOG_CRIT, "span %d D-WRITE FAIL! [%s]\n", spri->span->span_id, spri->dchan->last_error); @@ -161,11 +164,10 @@ static int __pri_lpwrap_write(struct pri *pri, void *buf, int buflen) char bb[2048] = { 0 }; print_hex_bytes(buf, buflen - 2, bb, sizeof(bb)); - ftdm_log(FTDM_LOG_DEBUG, "WRITE %d\n", (int)buflen-2); + ftdm_log(FTDM_LOG_DEBUG, "WRITE %d\n", (int)buflen - 2); } #endif - - return (int) buflen; + return (int)buflen; } int lpwrap_init_pri(struct lpwrap_pri *spri, ftdm_span_t *span, ftdm_channel_t *dchan, int swtype, int node, int debug) @@ -186,6 +188,9 @@ int lpwrap_init_pri(struct lpwrap_pri *spri, ftdm_span_t *span, ftdm_channel_t * size_t buflen = sizeof(buf), len = 0; pri_set_debug(spri->pri, debug); +#ifdef HAVE_LIBPRI_AOC + pri_aoc_events_enable(spri->pri, 1); +#endif ftdm_channel_write(spri->dchan, buf, buflen, &len); ret = 0; @@ -214,6 +219,9 @@ int lpwrap_init_bri(struct lpwrap_pri *spri, ftdm_span_t *span, ftdm_channel_t * size_t buflen = sizeof(buf), len = 0; pri_set_debug(spri->pri, debug); +#ifdef HAVE_LIBPRI_AOC + pri_aoc_events_enable(spri->pri, 1); +#endif ftdm_channel_write(spri->dchan, buf, buflen, &len); ret = 0; @@ -267,9 +275,6 @@ int lpwrap_one_loop(struct lpwrap_pri *spri) now.tv_usec = 100000; sel = select(pri_fd(spri->pri) + 1, &rfds, NULL, &efds, &now); - - event = NULL; - if (!sel) { if ((next = pri_schedule_next(spri->pri))) { gettimeofday(&now, NULL); @@ -284,33 +289,29 @@ int lpwrap_one_loop(struct lpwrap_pri *spri) if (event) { /* 0 is catchall event handler */ - if ((handler = spri->eventmap[event->e] ? spri->eventmap[event->e] : spri->eventmap[0] ? spri->eventmap[0] : NULL)) { + if (event->e < 0 || event->e >= LPWRAP_PRI_EVENT_MAX) { + handler = spri->eventmap[0]; + } else if (spri->eventmap[event->e]) { + handler = spri->eventmap[event->e]; + } else { + handler = spri->eventmap[0]; + } + + if (handler) { handler(spri, event->e, event); } else { ftdm_log(FTDM_LOG_CRIT, "No event handler found for event %d.\n", event->e); } } - - return sel; - - - if ((handler = spri->eventmap[LPWRAP_PRI_EVENT_IO_FAIL] ? spri->eventmap[LPWRAP_PRI_EVENT_IO_FAIL] : spri->eventmap[0] ? spri->eventmap[0] : NULL)) { - handler(spri, LPWRAP_PRI_EVENT_IO_FAIL, NULL); - } - - return -1; } int lpwrap_run_pri(struct lpwrap_pri *spri) { int ret = 0; - for (;;){ - ret = lpwrap_one_loop(spri); - - if (ret < 0) { - + for (;;) { + if ((ret = lpwrap_one_loop(spri)) < 0) { #ifndef WIN32 //This needs to be adressed fror WIN32 still if (errno == EINTR){ /* Igonore an interrupted system call */ @@ -321,9 +322,7 @@ int lpwrap_run_pri(struct lpwrap_pri *spri) break; } } - return ret; - } /* For Emacs: diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.h b/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.h index 4701e27e18..b62e5fb176 100644 --- a/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.h +++ b/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.h @@ -51,7 +51,11 @@ typedef enum { LPWRAP_PRI_EVENT_ANSWER = PRI_EVENT_ANSWER, LPWRAP_PRI_EVENT_HANGUP_ACK = PRI_EVENT_HANGUP_ACK, LPWRAP_PRI_EVENT_RESTART_ACK = PRI_EVENT_RESTART_ACK, - LPWRAP_PRI_EVENT_FACNAME = PRI_EVENT_FACNAME, +#ifdef PRI_EVENT_FACILITY + LPWRAP_PRI_EVENT_FACILITY = PRI_EVENT_FACILITY, +#else + LPWRAP_PRI_EVENT_FACILITY = PRI_EVENT_FACNAME, +#endif LPWRAP_PRI_EVENT_INFO_RECEIVED = PRI_EVENT_INFO_RECEIVED, LPWRAP_PRI_EVENT_PROCEEDING = PRI_EVENT_PROCEEDING, LPWRAP_PRI_EVENT_SETUP_ACK = PRI_EVENT_SETUP_ACK,