From 4e7c3d14620d341672cf6418a29ee4cef5513e9c Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Fri, 16 Dec 2011 19:26:56 +0000 Subject: [PATCH] Implement handling a multi-channel RESTART request. The channel id ie can supply a slotmap or list of channels. For a RESTART message, this can be handy to indicate multiple channels that need to be restarted at the same time. An incoming RESTART request will now generate a PRI_EVENT_RESTART to the upper layer for each channel indicated in the request. If the event is successfully generated for all indicated channels then a RESTART_ACKNOWLEDGE is sent back to the peer indicating all channels restarted. * Add the ability to process a channel id ie channel list with a RESTART request. * Add the ability to process slotmaps with a RESTART request. (closes issue PRI-93) Reported by: Marcin Kowalczyk Patches: jira_pri_93.patch (license #5621) patch uploaded by rmudgett Tested by: zvision, rmudgett (closes issue PRI-71) Reported by: Torrey Searle Tested by: rmudgett git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@2277 2fbb986a-6c06-0410-b554-c9c1f0a7f128 --- pri_internal.h | 13 +++++ q931.c | 156 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 150 insertions(+), 19 deletions(-) diff --git a/pri_internal.h b/pri_internal.h index 293295a..bba94b1 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -647,6 +647,19 @@ struct q931_call { /* AOC charge requesting on Setup */ int aoc_charging_request; + + unsigned int slotmap_size:1;/* TRUE if the slotmap is E1 (32 bits). */ + + struct { + /*! Timer ID of RESTART notification events to upper layer. */ + int timer; + /*! Current RESTART notification index. */ + int idx; + /*! Number of channels in the channel ID list. */ + int count; + /*! Channel ID list */ + char chan_no[32]; + } restart; }; enum CC_STATES { diff --git a/q931.c b/q931.c index bc5b5f1..ba793f3 100644 --- a/q931.c +++ b/q931.c @@ -1156,6 +1156,8 @@ static int receive_channel_id(int full_ie, struct pri *ctrl, q931_call *call, in int pos = 0; int need_extended_channel_octets;/*!< TRUE if octets 3.2 and 3.3 need to be present. */ + call->restart.count = 0; + if (ie->data[0] & 0x08) { call->chanflags = FLAG_EXCLUSIVE; } else { @@ -1224,32 +1226,62 @@ static int receive_channel_id(int full_ie, struct pri *ctrl, q931_call *call, in /* More coming */ if ((ie->data[pos] & 0x0f) != 3) { /* Channel type/mapping is not for B channel units. */ - pri_error(ctrl, "!! Unexpected Channel Type %d\n", ie->data[1] & 0x0f); + pri_error(ctrl, "!! Unexpected Channel Type %d\n", ie->data[pos] & 0x0f); return -1; } if ((ie->data[pos] & 0x60) != 0) { - pri_error(ctrl, "!! Invalid CCITT coding %d\n", (ie->data[1] & 0x60) >> 5); + pri_error(ctrl, "!! Invalid CCITT coding %d\n", (ie->data[pos] & 0x60) >> 5); return -1; } if (ie->data[pos] & 0x10) { - /* - * Expect Slot Map - * Note that we are assuming only T1's use slot maps which is wrong - * but oh well... We would need to know what type of line we are - * connected with (T1 or E1) to interpret the map correctly anyway. - */ + /* Expect Slot Map */ call->slotmap = 0; pos++; - for (x=0;x<3;x++) { + call->slotmap_size = (ie->len - pos > 3) ? 1 : 0; + for (x = 0; x < (call->slotmap_size ? 4 : 3); ++x) { call->slotmap <<= 8; call->slotmap |= ie->data[x + pos]; } + + if (msgtype == Q931_RESTART) { + int bit; + + /* Convert the slotmap to a channel list for RESTART support. */ + for (bit = 0; bit < ARRAY_LEN(call->restart.chan_no); ++bit) { + if (call->slotmap & (1UL << bit)) { + call->restart.chan_no[call->restart.count++] = bit + + (call->slotmap_size ? 0 : 1); + } + } + } } else { pos++; /* Only expect a particular channel */ call->channelno = ie->data[pos] & 0x7f; - if (ctrl->chan_mapping_logical && call->channelno > 15) + if (ctrl->chan_mapping_logical && call->channelno > 15) { call->channelno++; + } + + if (msgtype == Q931_RESTART) { + /* Read in channel list for RESTART support. */ + while (call->restart.count < ARRAY_LEN(call->restart.chan_no)) { + int chan_no; + + chan_no = ie->data[pos] & 0x7f; + if (ctrl->chan_mapping_logical && chan_no > 15) { + ++chan_no; + } + call->restart.chan_no[call->restart.count++] = chan_no; + if (ie->data[pos++] & 0x80) { + /* Channel list finished. */ + break; + } + if (ie->len <= pos) { + /* No more ie contents. */ + break; + } + } + } } } return 0; @@ -1322,17 +1354,35 @@ static int transmit_channel_id(int full_ie, struct pri *ctrl, q931_call *call, i if (0 < call->channelno && call->channelno != 0xff) { /* Channel number specified and preferred over slot map if we have one. */ ++pos; - if (ctrl->chan_mapping_logical && call->channelno > 16) { - ie->data[pos++] = 0x80 | (call->channelno - 1); + if (msgtype == Q931_RESTART_ACKNOWLEDGE && call->restart.count) { + int chan_no; + int idx; + + /* Build RESTART_ACKNOWLEDGE channel list */ + for (idx = 0; idx < call->restart.count; ++idx) { + chan_no = call->restart.chan_no[idx]; + if (ctrl->chan_mapping_logical && chan_no > 16) { + --chan_no; + } + if (call->restart.count <= idx + 1) { + /* Last channel list channel. */ + chan_no |= 0x80; + } + ie->data[pos++] = chan_no; + } } else { - ie->data[pos++] = 0x80 | call->channelno; + if (ctrl->chan_mapping_logical && call->channelno > 16) { + ie->data[pos++] = 0x80 | (call->channelno - 1); + } else { + ie->data[pos++] = 0x80 | call->channelno; + } } } else if (call->slotmap != -1) { int octet; /* We have to send a slot map */ ie->data[pos++] |= 0x10; - for (octet = 3; octet--;) { + for (octet = call->slotmap_size ? 4 : 3; octet--;) { ie->data[pos++] = (call->slotmap >> (8 * octet)) & 0xff; } } else { @@ -4240,6 +4290,7 @@ static void cleanup_and_free_call(struct q931_call *cur) struct pri *ctrl; ctrl = cur->pri; + pri_schedule_del(ctrl, cur->restart.timer); pri_schedule_del(ctrl, cur->retranstimer); pri_schedule_del(ctrl, cur->hold_timer); pri_schedule_del(ctrl, cur->fake_clearing_timer); @@ -8192,6 +8243,60 @@ static int newcall_rel_comp_cause(struct q931_call *call) return cause; } +/*! + * \internal + * \brief Restart channel notify event for upper layer notify chain timeout. + * + * \param data Callback data pointer. + * + * \return Nothing + */ +static void q931_restart_notify_timeout(void *data) +{ + struct q931_call *call = data; + struct pri *ctrl = call->pri; + + /* Create channel restart event to upper layer. */ + call->channelno = call->restart.chan_no[call->restart.idx++]; + ctrl->ev.e = PRI_EVENT_RESTART; + ctrl->ev.restart.channel = q931_encode_channel(call); + ctrl->schedev = 1; + + /* Reschedule for next channel restart event needed. */ + if (call->restart.idx < call->restart.count) { + call->restart.timer = pri_schedule_event(ctrl, 0, q931_restart_notify_timeout, + call); + } else { + /* No more restart events needed. */ + call->restart.timer = 0; + + /* Send back the Restart Acknowledge. All channels are now restarted. */ + if (call->slotmap != -1) { + /* Send slotmap format. */ + call->channelno = -1; + } + restart_ack(ctrl, call); + } +} + +/*! + * \internal + * \brief Setup restart channel notify events for upper layer. + * + * \param call Q.931 call leg. + * + * \return Nothing + */ +static void q931_restart_notify(struct q931_call *call) +{ + struct pri *ctrl = call->pri; + + /* Start notify chain. */ + pri_schedule_del(ctrl, call->restart.timer); + call->restart.idx = 0; + q931_restart_notify_timeout(call); +} + /*! * \internal * \brief Process the decoded information in the Q.931 message. @@ -8227,11 +8332,24 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct } UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_RESTART); c->peercallstate = Q931_CALL_STATE_RESTART_REQUEST; - /* Send back the Restart Acknowledge */ - restart_ack(ctrl, c); - /* Notify user of restart event */ - ctrl->ev.e = PRI_EVENT_RESTART; - ctrl->ev.restart.channel = q931_encode_channel(c); + + /* Notify upper layer of restart event */ + if ((c->channelno == -1 && c->slotmap == -1) || !c->restart.count) { + /* + * Whole link restart or channel not identified by Channel ID ie + * 3.3 octets. + * + * Send back the Restart Acknowledge + */ + restart_ack(ctrl, c); + + /* Create channel restart event to upper layer. */ + ctrl->ev.e = PRI_EVENT_RESTART; + ctrl->ev.restart.channel = q931_encode_channel(c); + } else { + /* Start notify chain. */ + q931_restart_notify(c); + } return Q931_RES_HAVEEVENT; case Q931_REGISTER: q931_display_subcmd(ctrl, c);