Add service maintenance message support

This adds support for two new message types: Service and Service Acknowledge.
When a channel receives a service message it will either take the channel in
or out of service and then send a service acknowledgment. Although not
enforced here (enforced in chan_dahdi), the service messages are only supported
with switch types 4ess/5ess. The required Asterisk changes will be coming next.

(issue #3450)
Reported by: cmaj


git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@732 2fbb986a-6c06-0410-b554-c9c1f0a7f128
This commit is contained in:
Jeff Peeler
2009-04-14 15:05:21 +00:00
parent f9d5aa3c3a
commit 18fa4716a5
7 changed files with 301 additions and 52 deletions

View File

@@ -81,6 +81,8 @@
#define PRI_EVENT_NOTIFY 16 /* Notification received (NOTIFY) */
#define PRI_EVENT_PROGRESS 17 /* When we get PROGRESS */
#define PRI_EVENT_KEYPAD_DIGIT 18 /* When we receive during ACTIVE state (INFORMATION) */
#define PRI_EVENT_SERVICE 19 /* SERVICE maintenance message */
#define PRI_EVENT_SERVICE_ACK 20 /* SERVICE maintenance acknowledgement message */
/* Simple states */
#define PRI_STATE_DOWN 0
@@ -430,6 +432,18 @@ typedef struct pri_event_keypad_digit {
char digits[64];
} pri_event_keypad_digit;
typedef struct pri_event_service {
int e;
int channel;
int changestatus;
} pri_event_service;
typedef struct pri_event_service_ack {
int e;
int channel;
int changestatus;
} pri_event_service_ack;
typedef union {
int e;
pri_event_generic gen; /* Generic view */
@@ -445,6 +459,8 @@ typedef union {
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_service service; /* service message */
pri_event_service_ack service_ack; /* service acknowledgement message */
} pri_event;
struct pri;
@@ -556,6 +572,9 @@ int pri_restart(struct pri *pri);
int pri_reset(struct pri *pri, int channel);
/* handle b-channel maintenance messages */
extern int pri_maintenance_service(struct pri *pri, int span, int channel, int changestatus);
/* Create a new call */
q931_call *pri_new_call(struct pri *pri);
@@ -601,6 +620,9 @@ int pri_mwi_activate(struct pri *pri, q931_call *c, char *caller, int callerplan
/* Send an MWI deactivate request to a remote location */
int pri_mwi_deactivate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan);
/* Set service message support flag */
int pri_set_service_message_support(struct pri *pri, int supportflag);
#define PRI_2BCT
/* Attempt to pass the channels back to the NET side if compatable and
* suscribed. Sometimes called 2 bchannel transfer (2BCT) */

17
pri.c
View File

@@ -110,6 +110,15 @@ int pri_get_timer(struct pri *pri, int timer)
return pri->timers[timer];
}
int pri_set_service_message_support(struct pri *pri, int supportflag)
{
if (!pri) {
return -1;
}
pri->service_message_support = supportflag;
return 0;
}
int pri_timer2idx(char *timer)
{
if (!strcasecmp(timer, "N200"))
@@ -630,6 +639,14 @@ int pri_reset(struct pri *pri, int channel)
return q931_restart(pri, channel);
}
int pri_maintenance_service(struct pri *pri, int span, int channel, int changestatus)
{
if (!pri) {
return -1;
}
return maintenance_service(pri, span, channel, changestatus);
}
q931_call *pri_new_call(struct pri *pri)
{
if (!pri)

View File

@@ -114,6 +114,9 @@ struct pri {
/* do we do overlap dialing */
int overlapdial;
/* do we support SERVICE messages */
int service_message_support;
/* do not skip channel 16 */
int chan_mapping_logical;
@@ -264,6 +267,8 @@ struct q931_call {
/* Bridged call info */
q931_call *bridged_call; /* Pointer to other leg of bridged call */
int changestatus; /* SERVICE message changestatus */
};
extern int pri_schedule_event(struct pri *pri, int ms, void (*function)(void *data), void *data);

View File

@@ -192,4 +192,8 @@ extern pri_event *q921_receive(struct pri *pri, q921_h *h, int len);
extern int q921_transmit_iframe(struct pri *pri, void *buf, int len, int cr);
extern pri_event *q921_dchannel_up(struct pri *pri);
extern pri_event *q921_dchannel_down(struct pri *pri);
#endif

View File

@@ -113,6 +113,8 @@ typedef struct q931_ie {
#define Q931_PROTOCOL_DISCRIMINATOR 0x08
#define GR303_PROTOCOL_DISCRIMINATOR 0x4f
#define MAINTENANCE_PROTOCOL_DISCRIMINATOR_1 0x03
#define MAINTENANCE_PROTOCOL_DISCRIMINATOR_2 0x43
/* Q.931 / National ISDN Message Types */
@@ -160,6 +162,12 @@ typedef struct q931_ie {
#define NATIONAL_SERVICE 0x0f
#define NATIONAL_SERVICE_ACKNOWLEDGE 0x07
#define SERVICE_CHANGE_STATUS_INSERVICE 0
#define SERVICE_CHANGE_STATUS_LOOPBACK 1 /* not supported */
#define SERVICE_CHANGE_STATUS_OUTOFSERVICE 2
#define SERVICE_CHANGE_STATUS_REQCONTINUITYCHECK 3 /* not supported */
#define SERVICE_CHANGE_STATUS_SHUTDOWN 4 /* not supported */
/* Special codeset 0 IE */
#define NATIONAL_CHANGE_STATUS 0x1
@@ -248,6 +256,10 @@ typedef struct q931_ie {
/* EuroISDN */
#define Q931_SENDING_COMPLETE 0xa1
extern int maintenance_service(struct pri *pri, int span, int channel, int changestatus);
extern int maintenance_service_ack(struct pri *pri, q931_call *call);
/* Q.SIG specific */
#define QSIG_IE_TRANSIT_COUNT 0x31

5
q921.c
View File

@@ -248,7 +248,6 @@ static int q921_ack_packet(struct pri *pri, int num)
static void t203_expire(void *);
static void t200_expire(void *);
static pri_event *q921_dchannel_down(struct pri *pri);
static void reschedule_t200(struct pri *pri)
{
@@ -785,7 +784,7 @@ void q921_dump(struct pri *pri, q921_h *h, int len, int showraw, int txrx)
}
}
static pri_event *q921_dchannel_up(struct pri *pri)
pri_event *q921_dchannel_up(struct pri *pri)
{
/* Stop any SABME retransmissions */
if (pri->sabme_timer) {
@@ -814,7 +813,7 @@ static pri_event *q921_dchannel_up(struct pri *pri)
return &pri->ev;
}
static pri_event *q921_dchannel_down(struct pri *pri)
pri_event *q921_dchannel_down(struct pri *pri)
{
/* Reset counters, reset sabme timer etc */
q921_reset(pri);

288
q931.c
View File

@@ -89,11 +89,14 @@ static struct msgtype msgs[] = {
{ Q931_SUSPEND, "SUSPEND" },
{ Q931_SUSPEND_ACKNOWLEDGE, "SUSPEND ACKNOWLEDGE" },
{ Q931_SUSPEND_REJECT, "SUSPEND REJECT" },
/* Maintenance */
{ NATIONAL_SERVICE, "SERVICE" },
{ NATIONAL_SERVICE_ACKNOWLEDGE, "SERVICE ACKNOWLEDGE" },
};
static int post_handle_q931_message(struct pri *pri, struct q931_mh *mh, struct q931_call *c, int missingmand);
struct msgtype maintenance_msgs[] = {
{ NATIONAL_SERVICE, "SERVICE", { Q931_CHANNEL_IDENT } },
{ NATIONAL_SERVICE_ACKNOWLEDGE, "SERVICE ACKNOWLEDGE", { Q931_CHANNEL_IDENT } },
};
static int post_handle_maintenance_message(struct pri *pri, struct q931_mh *mh, struct q931_call *c);
static struct msgtype causes[] = {
{ PRI_CAUSE_UNALLOCATED, "Unallocated (unassigned) number" },
@@ -258,6 +261,20 @@ static char *code2str(int code, struct msgtype *codes, int max)
return "Unknown";
}
static char *pritype(int type)
{
switch (type) {
case PRI_CPE:
return "CPE";
break;
case PRI_NETWORK:
return "NET";
break;
default:
return "UNKNOWN";
}
}
static void call_init(struct q931_call *c)
{
c->forceinvert = -1;
@@ -458,8 +475,8 @@ static FUNC_DUMP(dump_channel_id)
if (!(ie->data[pos] & 0x10)) {
/* Number specified */
pos++;
pri_message(pri, "%c Ext: %d Channel: %d ]\n", prefix, (ie->data[pos] & 0x80) >> 7,
(ie->data[pos]) & 0x7f);
pri_message(pri, "%c Ext: %d Channel: %d Type: %s]\n", prefix, (ie->data[pos] & 0x80) >> 7,
(ie->data[pos]) & 0x7f, pritype(pri->localtype));
} else {
pos++;
/* Map specified */
@@ -1180,6 +1197,29 @@ static FUNC_SEND(transmit_user_user)
return 0;
}
static FUNC_DUMP(dump_change_status)
{
int x;
pri_message(pri, "%c Change Status Information (len=%2d) [", prefix, len);
for (x=0; x<ie->len; x++) {
pri_message(pri, " %02x", ie->data[x] & 0x7f);
}
pri_message(pri, " ]\n");
}
static FUNC_RECV(receive_change_status)
{
call->changestatus = ie->data[0] & 0x0f;
return 0;
}
static FUNC_SEND(transmit_change_status)
{
ie->data[0] = 0xc0 | call->changestatus;
return 3;
}
static char *prog2str(int prog)
{
static struct msgtype progs[] = {
@@ -2143,7 +2183,7 @@ static FUNC_DUMP(dump_transit_count)
static struct ie ies[] = {
/* Codeset 0 - Common */
{ 1, NATIONAL_CHANGE_STATUS, "Change Status" },
{ 1, NATIONAL_CHANGE_STATUS, "Change Status", dump_change_status, receive_change_status, transmit_change_status },
{ 0, Q931_LOCKING_SHIFT, "Locking Shift", dump_shift },
{ 0, Q931_BEARER_CAPABILITY, "Bearer Capability", dump_bearer_capability, receive_bearer_capability, transmit_bearer_capability },
{ 0, Q931_CAUSE, "Cause", dump_cause, receive_cause, transmit_cause },
@@ -2187,7 +2227,7 @@ static struct ie ies[] = {
{ 1, Q931_IE_USER_USER, "User-User", dump_user_user, receive_user_user, transmit_user_user },
{ 1, Q931_IE_ESCAPE_FOR_EXT, "Escape for Extension" },
{ 1, Q931_IE_CALL_STATUS, "Call Status" },
{ 1, Q931_IE_CHANGE_STATUS, "Change Status" },
{ 1, Q931_IE_CHANGE_STATUS, "Change Status", dump_change_status, receive_change_status, transmit_change_status },
{ 1, Q931_IE_CONNECTED_ADDR, "Connected Number", dump_connected_number },
{ 1, Q931_IE_CONNECTED_NUM, "Connected Number", dump_connected_number },
{ 1, Q931_IE_ORIGINAL_CALLED_NUMBER, "Original Called Number", dump_redirecting_number, receive_redirecting_number, transmit_redirecting_number },
@@ -2273,6 +2313,16 @@ static char *msg2str(int msg)
return "Unknown Message Type";
}
static char *maintenance_msg2str(int msg)
{
unsigned int x;
for (x=0; x<sizeof(maintenance_msgs)/sizeof(maintenance_msgs[0]); x++) {
if (maintenance_msgs[x].msgnum == msg)
return maintenance_msgs[x].name;
}
return "Unknown Message Type";
}
static inline int q931_cr(q931_h *h)
{
int cr = 0;
@@ -2525,7 +2575,11 @@ void q931_dump(struct pri *pri, q931_h *h, int len, int txrx)
pri_message(pri, "%c Call Ref: len=%2d (reference %d/0x%X) (%s)\n", c, h->crlen, q931_cr(h) & 0x7FFF, q931_cr(h) & 0x7FFF, (h->crv[0] & 0x80) ? "Terminator" : "Originator");
/* Message header begins at the end of the call reference number */
mh = (q931_mh *)(h->contents + h->crlen);
pri_message(pri, "%c Message type: %s (%d)\n", c, msg2str(mh->msg), mh->msg);
if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) {
pri_message(pri, "%c Message Type: %s (%d)\n", c, maintenance_msg2str(mh->msg), mh->msg);
} else {
pri_message(pri, "%c Message Type: %s (%d)\n", c, msg2str(mh->msg), mh->msg);
}
/* Drop length of header, including call reference */
len -= (h->crlen + 3);
codeset = cur_codeset = 0;
@@ -2571,12 +2625,16 @@ static int q931_handle_ie(int codeset, struct pri *pri, q931_call *c, int msg, q
return -1;
}
static void init_header(struct pri *pri, q931_call *call, unsigned char *buf, q931_h **hb, q931_mh **mhb, int *len)
static void init_header(struct pri *pri, q931_call *call, unsigned char *buf, q931_h **hb, q931_mh **mhb, int *len, int protodisc)
{
/* Returns header and message header and modifies length in place */
q931_h *h = (q931_h *)buf;
q931_mh * mh;
h->pd = pri->protodisc;
if (protodisc) {
h->pd = protodisc;
} else {
h->pd = pri->protodisc;
}
h->x0 = 0; /* Reserved 0 */
if (!pri->bri) {
h->crlen = 2; /* Two bytes of Call Reference. Invert the top bit to make it from our sense */
@@ -2639,8 +2697,8 @@ static int send_message(struct pri *pri, q931_call *c, int msgtype, int ies[])
memset(buf, 0, sizeof(buf));
len = sizeof(buf);
init_header(pri, c, buf, &h, &mh, &len);
mh->msg = msgtype;
init_header(pri, c, buf, &h, &mh, &len, (msgtype >> 8));
mh->msg = msgtype & 0x00ff;
x=0;
codeset = 0;
while(ies[x] > -1) {
@@ -2662,6 +2720,30 @@ static int send_message(struct pri *pri, q931_call *c, int msgtype, int ies[])
return 0;
}
static int maintenance_service_ies[] = { Q931_IE_CHANGE_STATUS, Q931_CHANNEL_IDENT, -1 };
int maintenance_service_ack(struct pri *pri, q931_call *c)
{
return send_message(pri, c, (MAINTENANCE_PROTOCOL_DISCRIMINATOR_1 << 8) | NATIONAL_SERVICE_ACKNOWLEDGE, maintenance_service_ies);
}
int maintenance_service(struct pri *pri, int span, int channel, int changestatus)
{
struct q931_call *c;
c = q931_getcall(pri, 0x8000, 0);
if (!c) {
return -1;
}
if (channel > -1) {
channel &= 0xff;
}
c->ds1no = span;
c->channelno = channel;
c->chanflags |= FLAG_EXCLUSIVE;
c->changestatus = changestatus;
return send_message(pri, c, (MAINTENANCE_PROTOCOL_DISCRIMINATOR_1 << 8) | NATIONAL_SERVICE, maintenance_service_ies);
}
static int status_ies[] = { Q931_CAUSE, Q931_CALL_STATE, -1 };
static int q931_status(struct pri *pri, q931_call *c, int cause)
@@ -3266,45 +3348,36 @@ int q931_hangup(struct pri *pri, q931_call *c, int cause)
return 0;
}
int q931_receive(struct pri *pri, q931_h *h, int len)
static int prepare_to_handle_maintenance_message(struct pri *pri, q931_mh *mh, q931_call *c)
{
q931_mh *mh;
q931_call *c;
q931_ie *ie;
unsigned int x;
int y;
int res;
int r;
int mandies[MAX_MAND_IES];
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(pri, h, len, 0);
#ifdef LIBPRI_COUNTERS
pri->q931_rxcount++;
#endif
mh = (q931_mh *)(h->contents + h->crlen);
if ((h->pd == 0x3) || (h->pd == 0x43)) {
/* This is the weird maintenance stuff. We majorly
KLUDGE this by changing byte 4 from a 0xf (SERVICE)
to a 0x7 (SERVICE ACKNOWLEDGE) */
h->raw[h->crlen + 2] -= 0x8;
q931_xmit(pri, h, len, 1);
return 0;
} else if (h->pd != pri->protodisc) {
pri_error(pri, "Warning: unknown/inappropriate protocol discriminator received (%02x/%d)\n", h->pd, h->pd);
return 0;
}
c = q931_getcall(pri, q931_cr(h), 0);
if (!c) {
pri_error(pri, "Unable to locate call %d\n", q931_cr(h));
if ((!pri) || (!mh) || (!c)) {
return -1;
}
/* Preliminary handling */
/* SERVICE messages are a superset of messages that can take b-channels
* or entire d-channels in and out of service */
switch(mh->msg) {
case NATIONAL_SERVICE:
case NATIONAL_SERVICE_ACKNOWLEDGE:
c->channelno = -1;
c->slotmap = -1;
c->chanflags = 0;
c->ds1no = 0;
c->ri = -1;
c->changestatus = -1;
break;
default:
pri_error(pri, "!! Don't know how to pre-handle maintenance message type '%s' (%d)\n", maintenance_msg2str(mh->msg), mh->msg);
return -1;
}
return 0;
}
static int prepare_to_handle_q931_message(struct pri *pri, q931_mh *mh, q931_call *c)
{
if ((!pri) || (!mh) || (!c)) {
return -1;
}
switch(mh->msg) {
case Q931_RESTART:
if (pri->debug & PRI_DEBUG_Q931_STATE)
@@ -3433,6 +3506,57 @@ int q931_receive(struct pri *pri, q931_h *h, int len)
q931_destroycall(pri,c->cr);
return -1;
}
return 0;
}
int q931_receive(struct pri *pri, q931_h *h, int len)
{
q931_mh *mh;
q931_call *c;
q931_ie *ie;
unsigned int x;
int y;
int res;
int r;
int mandies[MAX_MAND_IES];
int missingmand;
int codeset, cur_codeset;
int last_ie[8];
memset(last_ie, 0, sizeof(last_ie));
if (pri->debug & PRI_DEBUG_Q931_DUMP)
q931_dump(pri, h, len, 0);
#ifdef LIBPRI_COUNTERS
pri->q931_rxcount++;
#endif
mh = (q931_mh *)(h->contents + h->crlen);
if ((h->pd != pri->protodisc) && (h->pd != MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) && (h->pd != MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) {
pri_error(pri, "Warning: unknown/inappropriate protocol discriminator received (%02x/%d)\n", h->pd, h->pd);
return 0;
}
if (((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) && (!pri->service_message_support)) {
/* Real service message support has not been enabled (and is OFF in libpri by default),
* so we have to revert to the 'traditional' KLUDGE of changing byte 4 from a 0xf (SERVICE)
* to a 0x7 (SERVICE ACKNOWLEDGE) */
/* This is the weird maintenance stuff. We majorly
KLUDGE this by changing byte 4 from a 0xf (SERVICE)
to a 0x7 (SERVICE ACKNOWLEDGE) */
h->raw[h->crlen + 2] -= 0x8;
q931_xmit(pri, h, len, 1);
return 0;
}
c = q931_getcall(pri, q931_cr(h), 0);
if (!c) {
pri_error(pri, "Unable to locate call %d\n", q931_cr(h));
return -1;
}
/* Preliminary handling */
if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) {
prepare_to_handle_maintenance_message(pri, mh, c);
} else {
prepare_to_handle_q931_message(pri, mh, c);
}
/* Handle IEs */
memset(mandies, 0, sizeof(mandies));
missingmand = 0;
@@ -3521,6 +3645,72 @@ int q931_receive(struct pri *pri, q931_h *h, int len)
}
/* Post handling */
if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) {
res = post_handle_maintenance_message(pri, mh, c);
} else {
res = post_handle_q931_message(pri, mh, c, missingmand);
}
return res;
}
static int post_handle_maintenance_message(struct pri *pri, struct q931_mh *mh, struct q931_call *c)
{
/* Do some maintenance stuff */
switch (mh->msg) {
case NATIONAL_SERVICE:
if (c->channelno > 0) {
pri->ev.e = PRI_EVENT_SERVICE;
pri->ev.service.channel = c->channelno | (c->ds1no << 8);
pri->ev.service.changestatus = 0x0f & c->changestatus;
} else {
switch (0x0f & c->changestatus) {
case SERVICE_CHANGE_STATUS_INSERVICE:
pri->ev.e = PRI_EVENT_DCHAN_UP;
q921_dchannel_up(pri);
break;
case SERVICE_CHANGE_STATUS_OUTOFSERVICE:
pri->ev.e = PRI_EVENT_DCHAN_DOWN;
q921_dchannel_down(pri);
break;
default:
pri_error(pri, "!! Don't know how to handle span service change status '%d'\n", (0x0f & c->changestatus));
return -1;
}
}
maintenance_service_ack(pri, c);
return Q931_RES_HAVEEVENT;
case NATIONAL_SERVICE_ACKNOWLEDGE:
if (c->channelno > 0) {
pri->ev.e = PRI_EVENT_SERVICE_ACK;
pri->ev.service_ack.channel = c->channelno | (c->ds1no << 8);
pri->ev.service_ack.changestatus = 0x0f & c->changestatus;
} else {
switch (0x0f & c->changestatus) {
case SERVICE_CHANGE_STATUS_INSERVICE:
pri->ev.e = PRI_EVENT_DCHAN_UP;
q921_dchannel_up(pri);
break;
case SERVICE_CHANGE_STATUS_OUTOFSERVICE:
pri->ev.e = PRI_EVENT_DCHAN_DOWN;
q921_dchannel_down(pri);
break;
default:
pri_error(pri, "!! Don't know how to handle span service change status '%d'\n", (0x0f & c->changestatus));
return -1;
}
}
return Q931_RES_HAVEEVENT;
default:
pri_error(pri, "!! Don't know how to post-handle maintenance message type %s (%d)\n", maintenance_msg2str(mh->msg), mh->msg);
}
return -1;
}
static int post_handle_q931_message(struct pri *pri, struct q931_mh *mh, struct q931_call *c, int missingmand)
{
int res;
struct apdu_event *cur = NULL;
switch(mh->msg) {
case Q931_RESTART:
if (missingmand) {