1327 lines
30 KiB
C
1327 lines
30 KiB
C
/* q931.c
|
|
*
|
|
* Basic functions for all Q931 based protocols
|
|
*
|
|
* Author Karsten Keil <kkeil@novell.com>
|
|
*
|
|
* Copyright 2007 by Karsten Keil <kkeil@novell.com>
|
|
* Copyright 2010 by Karsten Keil <kkeil@linux-pingi.de>
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
|
|
* version 2.1 as published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU LESSER GENERAL PUBLIC LICENSE for more details.
|
|
*
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <mISDN/mbuffer.h>
|
|
#include <mISDN/q931.h>
|
|
#include <mISDN/suppserv.h>
|
|
#include "layer3.h"
|
|
#include "dss1.h"
|
|
|
|
static signed char __l3pos[128] = {
|
|
-1,-2,-2,-2, 0,-2,-2,-2, 1,-2,-2,-2,-2,-2,-2,-2,
|
|
2,-1,-1,-1, 3,-1,-1,-1, 4,-1,-1,-1, 5,-1, 6,-1,
|
|
7,-1,-1,-1,-1,-1,-1, 8, 9,10,-1,-1,11,-1,-1,-1,
|
|
-1,-1,-1,-1,12,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
13,-1,14,15,16,17,18,19,-1,-1,20,-1,21,22,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,23,24,-1,-1,
|
|
25,26,-1,-1,27,-1,28,-1,29,30,-1,-1,31,32,33,-1
|
|
};
|
|
|
|
static unsigned char __l3ie[IE_COUNT] = {
|
|
0x04, 0x08, 0x10, 0x14, 0x18, 0x1c, 0x1e, 0x20,
|
|
0x27, 0x28, 0x29, 0x2c, 0x34, 0x40, 0x42, 0x43,
|
|
0x44, 0x45, 0x46, 0x47, 0x4a, 0x4c, 0x4d, 0x6c,
|
|
0x6d, 0x70, 0x71, 0x74, 0x76, 0x78, 0x79, 0x7c,
|
|
0x7d, 0x7e
|
|
};
|
|
|
|
int
|
|
l3_ie2pos(u_char ie)
|
|
{
|
|
if (ie > 127) /* only variable IE */
|
|
return -3;
|
|
return __l3pos[ie];
|
|
}
|
|
|
|
unsigned char
|
|
l3_pos2ie(int pos)
|
|
{
|
|
if (pos < 0 || pos > IE_COUNT)
|
|
return 0;
|
|
return __l3ie[pos];
|
|
}
|
|
|
|
static int
|
|
__get_free_extra(struct l3_msg *m)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
if (!m->extra[i].val)
|
|
return i;
|
|
fprintf(stderr, "%s: internal overflow\n", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
parseQ931(struct mbuffer *mb) {
|
|
int codeset, maincodeset;
|
|
int iep, err = 0, eidx = -1;
|
|
unsigned char *p, ie, **v_ie = &mb->l3.bearer_capability;
|
|
|
|
__msg_pull(mb, 1);
|
|
mb->l3h.crlen = *__msg_pull(mb, 1);
|
|
if (mb->l3h.crlen > 2)
|
|
return Q931_ERROR_CREF;
|
|
if (mb->l3h.crlen)
|
|
mb->l3h.cr = *__msg_pull(mb, 1);
|
|
if (mb->l3h.crlen == 2) {
|
|
mb->l3h.cr <<= 8;
|
|
mb->l3h.cr |= *__msg_pull(mb, 1);
|
|
} else if (mb->l3h.crlen == 1)
|
|
if (mb->l3h.cr & 0x80) {
|
|
mb->l3h.cr |= MISDN_PID_CR_FLAG;
|
|
mb->l3h.cr &= 0x807F;
|
|
}
|
|
mb->l3.pid = mb->addr.channel << 16;
|
|
if (mb->l3h.crlen == 0)
|
|
mb->l3.pid |= MISDN_PID_DUMMY;
|
|
else if ((mb->l3h.cr & 0x7fff) == 0)
|
|
mb->l3.pid |= MISDN_PID_GLOBAL;
|
|
else
|
|
mb->l3.pid |= mb->l3h.cr;
|
|
if (mb->len < 1)
|
|
return Q931_ERROR_LEN;
|
|
|
|
mb->l3h.type = *__msg_pull(mb, 1);
|
|
mb->l3.type = mb->l3h.type;
|
|
codeset = maincodeset = 0;
|
|
while (mb->len) {
|
|
p = __msg_pull(mb, 1);
|
|
ie = *p;
|
|
if ((ie & 0xf0) == 0x90) {
|
|
codeset = ie & 0x07;
|
|
if (!(ie & 0x08))
|
|
maincodeset = codeset;
|
|
if (eidx >= 0) {
|
|
mb->l3.extra[eidx].len = mb->data - mb->l3.extra[eidx].val -1;
|
|
eidx = -1;
|
|
}
|
|
if (codeset != 0) {
|
|
eidx = __get_free_extra(&mb->l3);
|
|
if (eidx < 0)
|
|
return Q931_ERROR_OVERFLOW;
|
|
mb->l3.extra[eidx].ie = ie;
|
|
mb->l3.extra[eidx].codeset = codeset;
|
|
mb->l3.extra[eidx].val = mb->data;
|
|
}
|
|
continue;
|
|
}
|
|
if (codeset == 0) {
|
|
if (ie & 0x80) { /* single octett IE */
|
|
if (ie == IE_MORE_DATA)
|
|
mb->l3.more_data++;
|
|
else if (ie == IE_COMPLETE) {
|
|
mb->l3.sending_complete++;
|
|
}
|
|
else if ((ie & 0xf0) == IE_CONGESTION)
|
|
mb->l3.congestion_level = ie;
|
|
else {
|
|
err |= Q931_ERROR_UNKNOWN;
|
|
}
|
|
} else {
|
|
iep = __l3pos[ie];
|
|
if (mb->len < 1)
|
|
return Q931_ERROR_LEN;
|
|
p = __msg_pull(mb, 1);
|
|
if (mb->len < *p)
|
|
return Q931_ERROR_LEN;
|
|
if (iep >= 0) {
|
|
if (!v_ie[iep]) { /* IE not detected before */
|
|
v_ie[iep] = p;
|
|
} else { /* IE is repeated */
|
|
eidx = __get_free_extra(&mb->l3);
|
|
if (eidx < 0)
|
|
return Q931_ERROR_OVERFLOW;
|
|
mb->l3.extra[eidx].ie = ie;
|
|
mb->l3.extra[eidx].val = p;
|
|
eidx = -1;
|
|
}
|
|
} else {
|
|
if (iep == -2) {
|
|
err |= Q931_ERROR_COMPREH;
|
|
mb->l3.comprehension_req = ie;
|
|
}
|
|
err |= Q931_ERROR_UNKNOWN;
|
|
}
|
|
__msg_pull(mb, *p);
|
|
}
|
|
} else { /* codeset != 0 */
|
|
if (!(ie & 0x80)) { /* not single octett IE */
|
|
if (mb->len < 1)
|
|
return Q931_ERROR_LEN;
|
|
p = __msg_pull(mb, 1);
|
|
if (mb->len < *p)
|
|
return Q931_ERROR_LEN;
|
|
__msg_pull(mb, *p);
|
|
}
|
|
if (codeset != maincodeset) { /* not locked shift */
|
|
mb->l3.extra[eidx].len = mb->data - mb->l3.extra[eidx].val;
|
|
eidx = -1;
|
|
}
|
|
}
|
|
codeset = maincodeset;
|
|
}
|
|
if (eidx >= 0)
|
|
mb->l3.extra[eidx].len = mb->data - mb->l3.extra[eidx].val;
|
|
return(err);
|
|
}
|
|
|
|
static int
|
|
__get_next_extra(struct l3_msg *m, int i, unsigned char ie)
|
|
{
|
|
while(++i < 8) {
|
|
if (m->extra[i].codeset)
|
|
continue;
|
|
if (!m->extra[i].val)
|
|
break; /* assume that extra is filled in order without holes */
|
|
if (m->extra[i].ie == ie)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
assembleQ931(l3_process_t *pc, struct l3_msg *l3m)
|
|
{
|
|
struct mbuffer *mb = container_of(l3m, struct mbuffer, l3);
|
|
unsigned char ie, **v_ie = &l3m->bearer_capability;
|
|
int i, l, eidx = -1;
|
|
|
|
mb->data = mb->tail = mb->head;
|
|
mb->len = 0;
|
|
msg_reserve(mb, MISDN_HEADER_LEN);
|
|
if (pc->pid == MISDN_PID_DUMMY) {
|
|
mb->l3h.crlen = 0;
|
|
} else {
|
|
if (test_bit(FLG_BASICRATE, &pc->L3->ml3.options))
|
|
mb->l3h.crlen = 1;
|
|
else
|
|
mb->l3h.crlen = 2;
|
|
mb->l3h.cr = pc->pid & 0xffff;
|
|
}
|
|
mb->l3h.type = l3m->type & 0xff;
|
|
*msg_put(mb, 1) = Q931_PD; /* TODO: be flexible for national */
|
|
*msg_put(mb, 1) = mb->l3h.crlen;
|
|
if (mb->l3h.crlen == 2) {
|
|
*msg_put(mb, 1) = 0x80 ^ ((mb->l3h.cr >> 8) & 0xff);
|
|
*msg_put(mb, 1) = mb->l3h.cr & 0xff;
|
|
} else if (mb->l3h.crlen == 1) {
|
|
if (mb->l3h.cr & MISDN_PID_CR_FLAG)
|
|
*msg_put(mb, 1) = mb->l3h.cr & 0x7f;
|
|
else
|
|
*msg_put(mb, 1) = 0x80 | (mb->l3h.cr & 0x7f);
|
|
}
|
|
*msg_put(mb, 1) = mb->l3h.type;
|
|
|
|
/* single octett IE */
|
|
if (l3m->more_data)
|
|
*msg_put(mb, 1) = IE_MORE_DATA;
|
|
if (l3m->sending_complete)
|
|
*msg_put(mb, 1) = IE_COMPLETE;
|
|
if (l3m->congestion_level)
|
|
*msg_put(mb, 1) = l3m->congestion_level;
|
|
|
|
for (i=0; i< IE_COUNT; i++) {
|
|
if (v_ie[i]) {
|
|
ie = __l3ie[i];
|
|
*msg_put(mb, 1) = ie;
|
|
l = *v_ie[i] + 1;
|
|
memcpy(msg_put(mb, l), v_ie[i], l);
|
|
while (0 <= (eidx = __get_next_extra(l3m, eidx, ie))) {
|
|
*msg_put(mb, 1) = l3m->extra[eidx].ie;
|
|
l = *l3m->extra[eidx].val + 1;
|
|
memcpy(msg_put(mb, l), l3m->extra[eidx].val, l);
|
|
}
|
|
}
|
|
}
|
|
for (i=0; i<8; i++) {
|
|
/* handle other codeset elements */
|
|
if (l3m->extra[i].codeset) {
|
|
ie = 0x98 | l3m->extra[i].codeset;
|
|
*msg_put(mb, 1) = ie; /* shift codeset IE */
|
|
memcpy(msg_put(mb, l3m->extra[i].len), l3m->extra[i].val, l3m->extra[i].len);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
add_layer3_ie(struct l3_msg *l3m, unsigned char ie, int len, unsigned char *data)
|
|
{
|
|
struct mbuffer *mb = container_of(l3m, struct mbuffer, l3);
|
|
int i;
|
|
unsigned char **v_ie;
|
|
|
|
if (ie & 0x80) { /* single octett IE */
|
|
if (ie == IE_MORE_DATA)
|
|
l3m->more_data++;
|
|
else if (ie == IE_COMPLETE)
|
|
l3m->sending_complete++;
|
|
else if ((ie & 0xf0) == IE_CONGESTION)
|
|
l3m->congestion_level = ie;
|
|
else
|
|
return Q931_ERROR_UNKNOWN;
|
|
} else { /*variable lenght IE */
|
|
v_ie = &l3m->bearer_capability;
|
|
i = __l3pos[ie];
|
|
if (i < 0)
|
|
return Q931_ERROR_UNKNOWN;
|
|
if (len > 255)
|
|
return Q931_ERROR_IELEN;
|
|
if (len + 1 + mb->ctail > mb->cend)
|
|
return Q931_ERROR_OVERFLOW;
|
|
*mb->ctail = len & 0xFF;
|
|
memcpy(mb->ctail + 1, data, len);
|
|
if (!v_ie[i])
|
|
v_ie[i] = mb->ctail;
|
|
else {
|
|
i = __get_free_extra(l3m);
|
|
if (i < 0)
|
|
return Q931_ERROR_OVERFLOW;
|
|
l3m->extra[i].ie = ie;
|
|
l3m->extra[i].val = mb->ctail;
|
|
}
|
|
mb->ctail += len + 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* helper functions to encode common Q931 info elements */
|
|
|
|
int
|
|
mi_encode_bearer(struct l3_msg *l3m, unsigned int capability, unsigned int l1user, unsigned int mode, unsigned int rate)
|
|
{
|
|
unsigned char ie[8];
|
|
int l = 2, multi = -1, user;
|
|
|
|
user = l1user;
|
|
|
|
switch (capability) {
|
|
case Q931_CAP_UNRES_DIGITAL:
|
|
user = -1;
|
|
break;
|
|
case Q931_CAP_RES_DIGITAL:
|
|
user = -1;
|
|
break;
|
|
default:
|
|
rate = 0x10;
|
|
mode = 0;
|
|
break;
|
|
}
|
|
|
|
ie[0] = 0x80 | capability;
|
|
ie[1] = 0x80 | (mode << 5) | rate;
|
|
if (multi >= 0) {
|
|
ie[2] = 0x80 | multi;
|
|
l++;
|
|
}
|
|
if (user >= 0) {
|
|
ie[l] = 0xa0 | user;
|
|
l++;
|
|
}
|
|
|
|
return add_layer3_ie(l3m, IE_BEARER, l, ie);
|
|
}
|
|
|
|
int
|
|
mi_encode_hlc(struct l3_msg *l3m, int hlc, int ehlc)
|
|
{
|
|
unsigned char ie[3];
|
|
int l = 2;
|
|
|
|
if (hlc < 0) /* no hlc to include */
|
|
return 0;
|
|
|
|
ie[0] = 0x91;
|
|
ie[1] = hlc & 0x7f;
|
|
|
|
if (ehlc < 0)
|
|
ie[1] |= 0x80;
|
|
else {
|
|
l = 3;
|
|
ie[2] = 0x80 | (ehlc & 0x7f);
|
|
}
|
|
return add_layer3_ie(l3m, IE_HLC, l, ie);
|
|
}
|
|
|
|
int
|
|
mi_encode_channel_id(struct l3_msg *l3m, struct misdn_channel_info *ci)
|
|
{
|
|
unsigned char ie[3];
|
|
int ret, l, excl = 1;
|
|
|
|
if (ci->ctrl & MI_CHAN_CTRL_SENT) /* already sent */
|
|
return 0;
|
|
|
|
if (!(ci->ctrl & MI_CHAN_CTRL_NEEDSEND)) /* we do not need send the ie */
|
|
return 0;
|
|
|
|
if (ci->nr == MI_CHAN_ANY || ci->nr == MI_CHAN_NONE ||
|
|
(ci->flags & MI_CHAN_FLG_NONE) || (ci->flags & MI_CHAN_FLG_ANY) ||
|
|
(!(ci->flags & MI_CHAN_FLG_EXCLUSIVE)))
|
|
excl = 0;
|
|
|
|
if (ci->flags & MI_CHAN_FLG_OTHER_IF) { /* PRI */
|
|
if (ci->nr == MI_CHAN_NONE)
|
|
return 0; /* IE is not included */
|
|
else if (ci->nr == MI_CHAN_ANY || (ci->flags & MI_CHAN_FLG_ANY)) {
|
|
l = 1;
|
|
ie[0] = 0x80 | 0x20 | 0x03;
|
|
} else {
|
|
l = 3;
|
|
ie[0] = 0x80 | 0x20 | (excl << 3) | 1;
|
|
switch (ci->type) {
|
|
case MI_CHAN_TYP_B: /* most common */
|
|
ie[1] = 0x83;
|
|
break;
|
|
case MI_CHAN_TYP_H0:
|
|
ie[1] = 0x86;
|
|
break;
|
|
case MI_CHAN_TYP_H11:
|
|
ie[1] = 0x88;
|
|
break;
|
|
case MI_CHAN_TYP_H12:
|
|
ie[1] = 0x89;
|
|
break;
|
|
default: /* use B-channel */
|
|
ie[1] = 0x83;
|
|
break;
|
|
}
|
|
ie[2] = 0x80 | ci->nr;
|
|
}
|
|
} else { /* BRI */
|
|
l = 1;
|
|
if (ci->nr == MI_CHAN_DCHANNEL || ci->type == MI_CHAN_TYP_D || (ci->flags & MI_CHAN_FLG_DCHANNEL))
|
|
ie[0] = MI_CHAN_FLG_DCHANNEL;
|
|
else if (ci->nr == MI_CHAN_ANY || (ci->flags & MI_CHAN_FLG_ANY))
|
|
ie[0] = 3;
|
|
else if (ci->nr == MI_CHAN_NONE || (ci->flags & MI_CHAN_FLG_NONE))
|
|
ie[0] = 0;
|
|
else
|
|
ie[0] = ci->nr & 3;
|
|
ie[0] |= 0x80 | (excl << 3);
|
|
}
|
|
ret = add_layer3_ie(l3m, IE_CHANNEL_ID, l, ie);
|
|
if (!ret) {
|
|
ci->ctrl |= MI_CHAN_CTRL_SENT;
|
|
ci->ctrl &= ~MI_CHAN_CTRL_SENT;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
mi_encode_calling_nr(struct l3_msg *l3m, char *nr, int pres, unsigned int screen, unsigned int type, unsigned int plan)
|
|
{
|
|
unsigned char ie[32];
|
|
int l;
|
|
|
|
if (pres < 0 && (nr == NULL || *nr == 0)) /* defaults, no number provided */
|
|
return 0;
|
|
if (nr && strlen(nr) > 30)
|
|
return -EINVAL;
|
|
|
|
if (pres >= 0) {
|
|
l = 2;
|
|
ie[0] = (type << 4) | plan;
|
|
ie[1] = 0x80 | (pres << 5) | screen;
|
|
} else {
|
|
l = 1;
|
|
ie[0] = 0x80 | (type << 4) | plan;
|
|
}
|
|
if (nr && *nr) {
|
|
strncpy((char *)&ie[l], nr, 30);
|
|
l += strlen(nr);
|
|
}
|
|
return add_layer3_ie(l3m, IE_CALLING_PN, l, ie);
|
|
}
|
|
|
|
int
|
|
mi_encode_connected_nr(struct l3_msg *l3m, char *nr, int pres, unsigned int screen, unsigned int type, unsigned int plan)
|
|
{
|
|
unsigned char ie[32];
|
|
int l;
|
|
|
|
if (pres < 0 && (nr == NULL || *nr == 0)) /* defaults, no number provided */
|
|
return 0;
|
|
if (nr && strlen(nr) > 30)
|
|
return -EINVAL;
|
|
|
|
if (pres >= 0) {
|
|
l = 2;
|
|
ie[0] = (type << 4) | plan;
|
|
ie[1] = 0x80 | (pres << 5) | screen;
|
|
} else {
|
|
l = 1;
|
|
ie[0] = 0x80 | (type << 4) | plan;
|
|
}
|
|
if (nr && *nr) {
|
|
strncpy((char *)&ie[l], nr, 30);
|
|
l += strlen(nr);
|
|
}
|
|
return add_layer3_ie(l3m, IE_CONNECT_PN, l, ie);
|
|
}
|
|
|
|
int
|
|
mi_encode_called_nr(struct l3_msg *l3m, char *nr, unsigned int type, unsigned int plan)
|
|
{
|
|
unsigned char ie[32];
|
|
int l;
|
|
|
|
if (nr == NULL || *nr == 0) /* not provided */
|
|
return 0;
|
|
if (nr && strlen(nr) > 30)
|
|
return -EINVAL;
|
|
|
|
l = 1;
|
|
ie[0] = 0x80 | (type << 4) | plan;
|
|
if (nr && *nr) {
|
|
strncpy((char *)&ie[l], nr, 30);
|
|
l += strlen(nr);
|
|
}
|
|
return add_layer3_ie(l3m, IE_CALLED_PN, l, ie);
|
|
}
|
|
|
|
|
|
int
|
|
mi_encode_redirecting_nr(struct l3_msg *l3m, char *nr, int pres, unsigned int type, unsigned int plan, int reason)
|
|
{
|
|
unsigned char ie[32];
|
|
int l;
|
|
|
|
if (nr == NULL || *nr == 0) /* not provided */
|
|
return 0;
|
|
if (nr && strlen(nr) > 20)
|
|
return -EINVAL;
|
|
|
|
if (pres >= 0) {
|
|
l = 2;
|
|
ie[0] = (type << 4) | plan;
|
|
ie[1] = (pres << 5);
|
|
if (reason >= 0) {
|
|
l++;
|
|
ie[2] = 0x80 | reason;
|
|
} else
|
|
ie[1] |= 0x80;
|
|
} else {
|
|
l = 1;
|
|
ie[0] = 0x80 | (type << 4) | plan;
|
|
}
|
|
if (nr && *nr) {
|
|
strncpy((char *)&ie[l], nr, 30);
|
|
l += strlen(nr);
|
|
}
|
|
return add_layer3_ie(l3m, IE_REDIRECTING_NR, l, ie);
|
|
}
|
|
|
|
int
|
|
mi_encode_redirection_nr(struct l3_msg *l3m, char *nr, int pres, unsigned int type, unsigned int plan)
|
|
{
|
|
unsigned char ie[32];
|
|
int l;
|
|
|
|
if (nr == NULL || *nr == 0) /* not provided */
|
|
return 0;
|
|
if (nr && strlen(nr) > 20)
|
|
return -EINVAL;
|
|
|
|
if (pres >= 0) {
|
|
l = 2;
|
|
ie[0] = (type << 4) | plan;
|
|
ie[1] = 0x80 | (pres << 5);
|
|
} else {
|
|
l = 1;
|
|
ie[0] = 0x80 | (type << 4) | plan;
|
|
}
|
|
if (nr && *nr) {
|
|
strncpy((char *)&ie[l], nr, 30);
|
|
l += strlen(nr);
|
|
}
|
|
return add_layer3_ie(l3m, IE_REDIRECTION_NR, l, ie);
|
|
}
|
|
|
|
int
|
|
mi_encode_useruser(struct l3_msg *l3m, int protocol, int len, char *data)
|
|
{
|
|
unsigned char ie[256];
|
|
|
|
if (len <= 0) /* not included */
|
|
return 0;
|
|
if (len > 250 || !data)
|
|
return -EINVAL;
|
|
if (protocol<0 || protocol>127)
|
|
return -EINVAL;
|
|
ie[0] = protocol & 0xff;
|
|
memcpy(&ie[1], data, len);
|
|
return add_layer3_ie(l3m, IE_USER_USER, len + 1, ie);
|
|
}
|
|
|
|
int
|
|
mi_encode_cause(struct l3_msg *l3m, int cause, int loc, int dlen, unsigned char *diag)
|
|
{
|
|
unsigned char ie[32];
|
|
int l;
|
|
|
|
if (cause < 0 || cause == NO_CAUSE) /* not included */
|
|
return 0;
|
|
if (cause > 127)
|
|
return -EINVAL;
|
|
if (loc < 0 || loc > 7)
|
|
return -EINVAL;
|
|
if (dlen > 30)
|
|
return -EINVAL;
|
|
if (dlen && !diag)
|
|
return -EINVAL;
|
|
l = 2 + dlen;
|
|
ie[0] = 0x80 | loc;
|
|
ie[1] = 0x80 | cause;
|
|
if (dlen)
|
|
memcpy(&ie[2], diag, dlen);
|
|
return add_layer3_ie(l3m, IE_CAUSE, l, ie);
|
|
}
|
|
|
|
int
|
|
mi_encode_progress(struct l3_msg *l3m, struct misdn_progress_info *prg)
|
|
{
|
|
unsigned char ie[2];
|
|
int ret;
|
|
|
|
if (prg->ctrl & MI_PROG_CTRL_SENT)
|
|
return 0;
|
|
|
|
if (!(prg->ctrl & MI_PROG_CTRL_NEEDSEND))
|
|
return 0;
|
|
|
|
ie[0] = 0x80 | prg->loc;
|
|
ie[1] = 0x80 | prg->desc;
|
|
|
|
ret = add_layer3_ie(l3m, IE_PROGRESS, 2, ie);
|
|
if (!ret) {
|
|
prg->ctrl |= MI_PROG_CTRL_SENT;
|
|
prg->ctrl &= ~MI_PROG_CTRL_SENT;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
mi_encode_date(struct l3_msg *l3m, struct tm *tm)
|
|
{
|
|
unsigned char ie[5];
|
|
|
|
ie[0] = tm->tm_year % 100;
|
|
ie[1] = tm->tm_mon + 1;
|
|
ie[2] = tm->tm_mday;
|
|
ie[3] = tm->tm_hour;
|
|
ie[4] = tm->tm_min;
|
|
return add_layer3_ie(l3m, IE_DATE, 5, ie);
|
|
}
|
|
|
|
int
|
|
mi_encode_restart_ind(struct l3_msg *l3m, unsigned char _class)
|
|
{
|
|
switch(_class) {
|
|
case RESTART_CLASS_CHANNEL:
|
|
case RESTART_CLASS_SINGLE:
|
|
case RESTART_CLASS_ALL:
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
_class |= 0x80;
|
|
return add_layer3_ie(l3m, IE_RESTART_IND, 1, &_class);
|
|
}
|
|
|
|
int
|
|
mi_encode_facility(struct l3_msg *l3m, struct asn1_parm *fac)
|
|
{
|
|
struct mbuffer *mb = container_of(l3m, struct mbuffer, l3);
|
|
int i, len;
|
|
|
|
len = encodeFac(mb->ctail, fac);
|
|
if (len <= 0)
|
|
return -EINVAL;
|
|
if (mb->ctail + len >= mb->cend) {
|
|
eprint("Msg buffer overflow %d needed %d available\n", len + 1, (int)(mb->cend - mb->ctail));
|
|
return Q931_ERROR_OVERFLOW;
|
|
}
|
|
if (l3m->facility) {
|
|
i = __get_free_extra(l3m);
|
|
if (i < 0) {
|
|
eprint("To many Facility IEs\n");
|
|
return Q931_ERROR_OVERFLOW;
|
|
}
|
|
l3m->extra[i].ie = IE_FACILITY;
|
|
l3m->extra[i].val = mb->ctail + 1;
|
|
} else
|
|
l3m->facility = mb->ctail + 1;
|
|
mb->ctail += len + 1;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mi_encode_notification_ind(struct l3_msg *l3m, int notInd)
|
|
{
|
|
notInd |= 0x80;
|
|
|
|
return add_layer3_ie(l3m, IE_NOTIFY, 1, (unsigned char*) ¬Ind);
|
|
|
|
}
|
|
|
|
/* helper functions to decode common IE */
|
|
|
|
#define _ASSIGN_PVAL(p, v) if (p) *p = (v)
|
|
|
|
int
|
|
mi_decode_progress(struct l3_msg *l3m, struct misdn_progress_info *progress)
|
|
{
|
|
struct misdn_progress_info prg;
|
|
|
|
if (l3m == NULL || !l3m->progress)
|
|
return 0;
|
|
|
|
if (l3m->progress[0] < 2)
|
|
return -EINVAL;
|
|
prg.loc = l3m->progress[1] & 0x7f;
|
|
prg.desc = l3m->progress[2] & 0x7f;
|
|
prg.resv = 0;
|
|
prg.ctrl = MI_PROG_CTRL_UPDATED;
|
|
_ASSIGN_PVAL(progress, prg);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mi_decode_bearer_capability(struct l3_msg *l3m, int *coding, int *capability, int *mode, int *rate,
|
|
int *oct_4a, int *oct_4b, int *oct_5, int *oct_5a, int *oct_5b,
|
|
int *oct_5c, int *oct_5d, int *oct_6, int *oct_7)
|
|
{
|
|
int opt[9];
|
|
int i,j;
|
|
enum {
|
|
octet_4a = 0,
|
|
octet_4b = 1,
|
|
octet_5 = 2,
|
|
octet_5a = 3,
|
|
octet_5b = 4,
|
|
octet_5c = 5,
|
|
octet_5d = 6,
|
|
octet_6 = 7,
|
|
octet_7 = 8
|
|
};
|
|
|
|
if (!l3m->bearer_capability || *l3m->bearer_capability < 2)
|
|
return -EINVAL;
|
|
_ASSIGN_PVAL(coding, (l3m->bearer_capability[1] & 0x60) >> 5);
|
|
_ASSIGN_PVAL(capability, l3m->bearer_capability[1] & 0x1f);
|
|
_ASSIGN_PVAL(mode, (l3m->bearer_capability[2] & 0x60) >> 5);
|
|
_ASSIGN_PVAL(rate, l3m->bearer_capability[2] & 0x1f);
|
|
|
|
/* Now the optional octets */
|
|
for (j = 0; j < 9; j++)
|
|
opt[j] = -1;
|
|
i = 2;
|
|
if (*l3m->bearer_capability <= i)
|
|
goto done;
|
|
if (!(l3m->bearer_capability[i] & 0x80)) {
|
|
i++;
|
|
j = octet_4a;
|
|
opt[j] = l3m->bearer_capability[i];
|
|
if (*l3m->bearer_capability <= i)
|
|
goto done;
|
|
j++; /* octet4b */
|
|
if (!(l3m->bearer_capability[i] & 0x80)) {
|
|
i++;
|
|
opt[j] = l3m->bearer_capability[i];
|
|
}
|
|
}
|
|
i++;
|
|
if (*l3m->bearer_capability < i)
|
|
goto done;
|
|
opt[octet_5] = l3m->bearer_capability[i];
|
|
if (*l3m->bearer_capability <= i)
|
|
goto done;
|
|
j = octet_5a;
|
|
while (j <= octet_5d && *l3m->bearer_capability > i) {
|
|
if (l3m->bearer_capability[i] & 0x80)
|
|
break;
|
|
i++;
|
|
opt[j] = l3m->bearer_capability[i];
|
|
j++;
|
|
}
|
|
i++;
|
|
if (*l3m->bearer_capability < i)
|
|
goto done;
|
|
opt[octet_6] = l3m->bearer_capability[i];
|
|
i++;
|
|
if (*l3m->bearer_capability < i)
|
|
goto done;
|
|
opt[octet_7] = l3m->bearer_capability[i];
|
|
done:
|
|
_ASSIGN_PVAL(oct_4a, opt[octet_4a]);
|
|
_ASSIGN_PVAL(oct_4b, opt[octet_4b]);
|
|
_ASSIGN_PVAL(oct_5, opt[octet_5]);
|
|
_ASSIGN_PVAL(oct_5a, opt[octet_5a]);
|
|
_ASSIGN_PVAL(oct_5b, opt[octet_5b]);
|
|
_ASSIGN_PVAL(oct_5c, opt[octet_5c]);
|
|
_ASSIGN_PVAL(oct_5d, opt[octet_5d]);
|
|
_ASSIGN_PVAL(oct_6, opt[octet_6]);
|
|
_ASSIGN_PVAL(oct_7, opt[octet_7]);
|
|
return 0;
|
|
};
|
|
|
|
int
|
|
mi_decode_hlc(struct l3_msg *l3m, int *hlchar, int *exthcl)
|
|
{
|
|
int hlc = -1, exthlc = -1;
|
|
|
|
if (l3m == NULL || l3m->hlc == NULL)
|
|
goto done;
|
|
if (*l3m->hlc < 2)
|
|
return -EINVAL;
|
|
if (l3m->hlc[1] != 0x91)
|
|
return -EINVAL;
|
|
hlc = l3m->hlc[2] & 0x7f;
|
|
if (*l3m->hlc > 2 && !(l3m->hlc[2] & 0x80))
|
|
exthlc = l3m->hlc[2] & 0x7f;
|
|
done:
|
|
_ASSIGN_PVAL(hlchar, hlc);
|
|
_ASSIGN_PVAL(exthcl, exthlc);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mi_decode_cause(struct l3_msg *l3m, int *coding, int *loc, int *rec, int *val, int *dialen, unsigned char *dia)
|
|
{
|
|
int r = 0, l = 2, dl = 0;
|
|
|
|
if (l3m == NULL || !l3m->cause)
|
|
return 0;
|
|
if (*l3m->cause < 2)
|
|
return -EINVAL;
|
|
_ASSIGN_PVAL(coding, (l3m->cause[1] & 0x60) >> 5);
|
|
_ASSIGN_PVAL(loc, l3m->cause[1] & 0x0f);
|
|
if (!(l3m->cause[1] & 0x80)) {
|
|
r = l3m->cause[2] & 0x7f;
|
|
l++;
|
|
} else
|
|
r = 0;
|
|
_ASSIGN_PVAL(rec, r);
|
|
_ASSIGN_PVAL(val, l3m->cause[l] & 0x7f);
|
|
dl = *l3m->cause - l;
|
|
if (dl > 0 && dl < 30 && dia)
|
|
memcpy(dia, &l3m->cause[l + 1], dl);
|
|
else
|
|
dl = 0;
|
|
_ASSIGN_PVAL(dialen, dl);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mi_decode_channel_id(struct l3_msg *l3m, struct misdn_channel_info *cip)
|
|
{
|
|
struct misdn_channel_info c = {MI_CHAN_NONE, 0, 0, 0};
|
|
|
|
if (l3m == NULL || !l3m->channel_id)
|
|
return 0;
|
|
if (*l3m->channel_id == 0)
|
|
return 0;
|
|
if (!cip)
|
|
return 0;
|
|
c.ctrl = cip->ctrl & (MI_CHAN_CTRL_ALLOCATED | MI_CHAN_CTRL_NEEDSEND | MI_CHAN_CTRL_SENT);
|
|
/* We ignore explicit interface settings */
|
|
c.flags = l3m->channel_id[1] & 0x2c;
|
|
|
|
switch (l3m->channel_id[1] & 3) {
|
|
case 0:
|
|
c.nr = MI_CHAN_NONE;
|
|
c.flags |= MI_CHAN_FLG_NONE;
|
|
break;
|
|
case 3:
|
|
c.nr = MI_CHAN_ANY;
|
|
c.flags |= MI_CHAN_FLG_ANY;
|
|
break;
|
|
default:
|
|
c.nr = l3m->channel_id[1] & 3;
|
|
}
|
|
if ((c.flags & MI_CHAN_FLG_OTHER_IF) == 0) { /* BRI */
|
|
if (c.flags & MI_CHAN_FLG_DCHANNEL) {
|
|
c.nr = MI_CHAN_DCHANNEL;
|
|
c.flags &= 0xfc; /* clear NONE,ANY */
|
|
c.type = MI_CHAN_TYP_D;
|
|
} else
|
|
c.type = MI_CHAN_TYP_B;
|
|
} else { /* PRI */
|
|
if (*l3m->channel_id < 2)
|
|
return -EINVAL;
|
|
/* We ignore coding so CCITT/ETSI is assumed, no support for multiple channels or channelmaps */
|
|
switch (l3m->channel_id[2] & 0x7f) {
|
|
default:
|
|
case 3:
|
|
c.type = MI_CHAN_TYP_B;
|
|
break;
|
|
case 6:
|
|
c.type = MI_CHAN_TYP_H0;
|
|
break;
|
|
case 8:
|
|
c.type = MI_CHAN_TYP_H11;
|
|
break;
|
|
case 9:
|
|
c.type = MI_CHAN_TYP_H12;
|
|
break;
|
|
}
|
|
if (*l3m->channel_id > 2 && ((c.flags & (MI_CHAN_FLG_ANY | MI_CHAN_FLG_NONE)) == 0))
|
|
c.nr = l3m->channel_id[3] & 0x7f;
|
|
}
|
|
if (cip->nr != c.nr || cip->type != c.type || cip->flags != c.flags)
|
|
c.ctrl |= MI_CHAN_CTRL_UPDATED;
|
|
_ASSIGN_PVAL(cip, c);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mi_decode_calling_nr(struct l3_msg *l3m, int *type, int *plan, int *pres, int *screen, char *nr)
|
|
{
|
|
int _pres = 0, _screen = 0, i = 2, l;
|
|
|
|
if (l3m == NULL || !l3m->calling_nr)
|
|
return 0;
|
|
if (*l3m->calling_nr < 2)
|
|
return -EINVAL;
|
|
if (*l3m->calling_nr > 32)
|
|
return -EINVAL;
|
|
_ASSIGN_PVAL(type, (l3m->calling_nr[1] & 0x70) >> 4);
|
|
_ASSIGN_PVAL(plan, l3m->calling_nr[1] & 0x0f);
|
|
if ((l3m->calling_nr[1] & 0x80) == 0 && *l3m->calling_nr >= 2) {
|
|
_pres = (l3m->calling_nr[2] & 0x60) >> 5;
|
|
_screen = l3m->calling_nr[2] & 0x03;
|
|
i++;
|
|
}
|
|
l = *l3m->calling_nr + 1 - i;
|
|
if (nr) {
|
|
memcpy(nr, &l3m->calling_nr[i], l);
|
|
nr[l] = 0;
|
|
}
|
|
_ASSIGN_PVAL(pres, _pres);
|
|
_ASSIGN_PVAL(screen, _screen);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mi_decode_connected_nr(struct l3_msg *l3m, int *type, int *plan, int *pres, int *screen, char *nr)
|
|
{
|
|
int _pres = 0, _screen = 0, i = 2, l;
|
|
|
|
if (l3m == NULL || !l3m->connected_nr)
|
|
return 0;
|
|
if (*l3m->connected_nr < 2)
|
|
return -EINVAL;
|
|
if (*l3m->connected_nr > 32)
|
|
return -EINVAL;
|
|
_ASSIGN_PVAL(type, (l3m->connected_nr[1] & 0x70) >> 4);
|
|
_ASSIGN_PVAL(plan, l3m->connected_nr[1] & 0x0f);
|
|
if ((l3m->connected_nr[1] & 0x80) == 0 && *l3m->connected_nr >= 2) {
|
|
_pres = (l3m->connected_nr[2] & 0x60) >> 5;
|
|
_screen = l3m->connected_nr[2] & 0x03;
|
|
i++;
|
|
}
|
|
l = *l3m->connected_nr + 1 - i;
|
|
if (nr) {
|
|
memcpy(nr, &l3m->connected_nr[i], l);
|
|
nr[l] = 0;
|
|
}
|
|
_ASSIGN_PVAL(pres, _pres);
|
|
_ASSIGN_PVAL(screen, _screen);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mi_decode_called_nr(struct l3_msg *l3m, int *type, int *plan, char *nr)
|
|
{
|
|
int l;
|
|
if (l3m == NULL || !l3m->called_nr)
|
|
return 0;
|
|
if (*l3m->called_nr < 1)
|
|
return -EINVAL;
|
|
if (*l3m->called_nr > 32)
|
|
return -EINVAL;
|
|
_ASSIGN_PVAL(type, (l3m->called_nr[1] & 0x70) >> 4);
|
|
_ASSIGN_PVAL(plan, l3m->called_nr[1] & 0x0f);
|
|
l = *l3m->called_nr - 1;
|
|
if (nr) {
|
|
memcpy(nr, &l3m->called_nr[2], l);
|
|
nr[l] = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mi_decode_redirecting_nr(struct l3_msg *l3m, int *count, int *type, int *plan, int *pres, int *reason, char *nr)
|
|
{
|
|
int _pres = 0, _count = 0, _reason = 0, i, l;
|
|
unsigned char *rdnr;
|
|
|
|
_ASSIGN_PVAL(count, _count);
|
|
if (l3m == NULL || !l3m->redirecting_nr)
|
|
return 0;
|
|
rdnr = l3m->redirecting_nr;
|
|
_count++;
|
|
/* IE could be repeated, use the last one and calculate the number of redirects */
|
|
for (i = 0; i < 8; i++) {
|
|
if (!l3m->extra[i].val)
|
|
break;
|
|
if (l3m->extra[i].ie == IE_REDIRECTING_NR) {
|
|
_count++;
|
|
rdnr = l3m->extra[i].val;
|
|
}
|
|
}
|
|
_ASSIGN_PVAL(count, _count);
|
|
if (*rdnr < 2)
|
|
return -EINVAL;
|
|
if (*rdnr > 23)
|
|
return -EINVAL;
|
|
i = 2;
|
|
_ASSIGN_PVAL(type, (rdnr[1] & 0x70) >> 4);
|
|
_ASSIGN_PVAL(plan, rdnr[1] & 0x0f);
|
|
if ((rdnr[1] & 0x80) == 0 && *rdnr >= 2) {
|
|
_pres = (rdnr[2] & 0x60) >> 5;
|
|
i++;
|
|
if ((rdnr[2] & 0x80) == 0 && *rdnr >= 3) {
|
|
_reason = rdnr[3] & 0x0f;
|
|
i++;
|
|
}
|
|
}
|
|
l = *rdnr + 1 - i;
|
|
if (nr) {
|
|
memcpy(nr, &rdnr[i], l);
|
|
nr[l] = 0;
|
|
}
|
|
_ASSIGN_PVAL(pres, _pres);
|
|
_ASSIGN_PVAL(reason, _reason);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mi_decode_redirection_nr(struct l3_msg *l3m, int *type, int *plan, int *pres, char *nr)
|
|
{
|
|
int _pres = 0, i = 2, l;
|
|
|
|
if (l3m == NULL || !l3m->redirection_nr)
|
|
return 0;
|
|
if (*l3m->redirection_nr < 2)
|
|
return -EINVAL;
|
|
if (*l3m->redirection_nr > 23)
|
|
return -EINVAL;
|
|
_ASSIGN_PVAL(type, (l3m->redirection_nr[1] & 0x70) >> 4);
|
|
_ASSIGN_PVAL(plan, l3m->redirection_nr[1] & 0x0f);
|
|
if ((l3m->redirection_nr[1] & 0x80) == 0 && *l3m->redirection_nr >= 2) {
|
|
_pres = (l3m->redirection_nr[2] & 0x60) >> 5;
|
|
i++;
|
|
}
|
|
l = *l3m->redirection_nr + 1 - i;
|
|
if (nr) {
|
|
memcpy(nr, &l3m->redirection_nr[i], l);
|
|
nr[l] = 0;
|
|
}
|
|
_ASSIGN_PVAL(pres, _pres);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mi_decode_display(struct l3_msg *l3m, char *display, int maxlen)
|
|
{
|
|
if (l3m == NULL || !l3m->display)
|
|
return 0;
|
|
if (!display)
|
|
return 0;
|
|
maxlen--;
|
|
if (*l3m->display < maxlen)
|
|
maxlen = *l3m->display;
|
|
memcpy(display, &l3m->display[1], maxlen);
|
|
display[maxlen] = 0;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mi_decode_useruser(struct l3_msg *l3m, int *pd, int *uulen, char *uu, int maxlen)
|
|
{
|
|
int l;
|
|
if (l3m == NULL || !l3m->useruser)
|
|
return 0;
|
|
if (!uu)
|
|
return 0;
|
|
if (*l3m->useruser < 1)
|
|
return 0;
|
|
if (*l3m->useruser < maxlen)
|
|
l = *l3m->useruser - 1;
|
|
else
|
|
l = maxlen;
|
|
if (l > 0)
|
|
memcpy(uu, &l3m->useruser[2], l);
|
|
_ASSIGN_PVAL(uulen, l);
|
|
_ASSIGN_PVAL(pd, l3m->useruser[1]);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mi_decode_date(struct l3_msg *l3m, struct tm *dat)
|
|
{
|
|
struct tm tm;
|
|
|
|
if (!dat)
|
|
return 0;
|
|
|
|
if (l3m == NULL || !l3m->date)
|
|
return 0;
|
|
|
|
if (*l3m->date < 5)
|
|
return 0;
|
|
|
|
memset(&tm, 0, sizeof(tm));
|
|
tm.tm_year = l3m->date[1];
|
|
if (tm.tm_year < 70)
|
|
tm.tm_year += 100;
|
|
tm.tm_mon = l3m->date[2] - 1;
|
|
tm.tm_mday = l3m->date[3];
|
|
tm.tm_hour = l3m->date[4];
|
|
tm.tm_min = l3m->date[5];
|
|
_ASSIGN_PVAL(dat, tm);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mi_decode_restart_ind(struct l3_msg *l3m, unsigned char *_class)
|
|
{
|
|
if (l3m == NULL || !l3m->restart_ind)
|
|
return 0;
|
|
if (!_class)
|
|
return 0;
|
|
if (*l3m->restart_ind < 1)
|
|
return 0;
|
|
_ASSIGN_PVAL(_class, 0x7f & l3m->restart_ind[1]);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mi_decode_facility(struct l3_msg *l3m, struct asn1_parm *fac)
|
|
{
|
|
if (l3m == NULL || !l3m->facility)
|
|
return 0;
|
|
if (!fac)
|
|
return 0;
|
|
return decodeFac(l3m->facility, fac);
|
|
}
|
|
|
|
int
|
|
mi_decode_notification_ind(struct l3_msg *l3m, int *notInd)
|
|
{
|
|
if (l3m == NULL || !l3m->notify)
|
|
return 0;
|
|
if (!notInd)
|
|
return 0;
|
|
_ASSIGN_PVAL(notInd, l3m->notify[1] & 0x7f);
|
|
return 0;
|
|
}
|
|
|
|
const char *
|
|
mi_bearer2str(int cap)
|
|
{
|
|
static const char *bearers[] = {
|
|
"Speech",
|
|
"Audio 3.1 kHz",
|
|
"Audio 7 kHz",
|
|
"Unrestricted Digital",
|
|
"Restricted Digital",
|
|
"Video",
|
|
"Unknown Bearer"
|
|
};
|
|
|
|
switch (cap) {
|
|
case Q931_CAP_SPEECH:
|
|
return bearers[0];
|
|
break;
|
|
case Q931_CAP_3KHZ_AUDIO:
|
|
return bearers[1];
|
|
break;
|
|
case Q931_CAP_7KHZ_AUDIO:
|
|
return bearers[2];
|
|
break;
|
|
case Q931_CAP_UNRES_DIGITAL:
|
|
return bearers[3];
|
|
break;
|
|
case Q931_CAP_RES_DIGITAL:
|
|
return bearers[4];
|
|
break;
|
|
case Q931_CAP_VIDEO:
|
|
return bearers[5];
|
|
break;
|
|
default:
|
|
return bearers[6];
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const struct _cmdtab {
|
|
unsigned int cmd;
|
|
const char *name;
|
|
} cmdtab[] = {
|
|
{MT_ALERTING , "ALERTING"},
|
|
{MT_CALL_PROCEEDING , "CALL_PROCEEDING"},
|
|
{MT_CONNECT , "CONNECT"},
|
|
{MT_CONNECT_ACKNOWLEDGE , "CONNECT_ACKNOWLEDGE"},
|
|
{MT_PROGRESS , "PROGRESS"},
|
|
{MT_SETUP , "SETUP"},
|
|
{MT_SETUP_ACKNOWLEDGE , "SETUP_ACKNOWLEDGE"},
|
|
{MT_RESUME , "RESUME"},
|
|
{MT_RESUME_ACKNOWLEDGE , "RESUME_ACKNOWLEDGE"},
|
|
{MT_RESUME_REJECT , "RESUME_REJECT"},
|
|
{MT_SUSPEND , "SUSPEND"},
|
|
{MT_SUSPEND_ACKNOWLEDGE , "SUSPEND_ACKNOWLEDGE"},
|
|
{MT_SUSPEND_REJECT , "SUSPEND_REJECT"},
|
|
{MT_USER_INFORMATION , "USER_INFORMATION"},
|
|
{MT_DISCONNECT , "DISCONNECT"},
|
|
{MT_RELEASE , "RELEASE"},
|
|
{MT_RELEASE_COMPLETE , "RELEASE_COMPLETE"},
|
|
{MT_RESTART , "RESTART"},
|
|
{MT_RESTART_ACKNOWLEDGE , "RESTART_ACKNOWLEDGE"},
|
|
{MT_SEGMENT , "SEGMENT"},
|
|
{MT_CONGESTION_CONTROL , "CONGESTION_CONTROL"},
|
|
{MT_INFORMATION , "INFORMATION"},
|
|
{MT_FACILITY , "FACILITY"},
|
|
{MT_NOTIFY , "NOTIFY"},
|
|
{MT_STATUS , "STATUS"},
|
|
{MT_STATUS_ENQUIRY , "STATUS_ENQUIRY"},
|
|
{MT_HOLD , "HOLD"},
|
|
{MT_HOLD_ACKNOWLEDGE , "HOLD_ACKNOWLEDGE"},
|
|
{MT_HOLD_REJECT , "HOLD_REJECT"},
|
|
{MT_RETRIEVE , "RETRIEVE"},
|
|
{MT_RETRIEVE_ACKNOWLEDGE, "RETRIEVE_ACKNOWLEDGE"},
|
|
{MT_RETRIEVE_REJECT , "RETRIEVE_REJECT"},
|
|
{MT_REGISTER , "REGISTER"},
|
|
{MT_ASSIGN , "ASSIGN_PID"},
|
|
{MT_FREE , "FREE_PID"},
|
|
{MT_L2ESTABLISH , "L2ESTABLISH"},
|
|
{MT_L2RELEASE , "L2RELEASE"},
|
|
{MT_L2IDLE , "L2IDLE"},
|
|
{MT_ERROR , "ERROR"},
|
|
{MT_TIMEOUT , "TIMEOUT"},
|
|
{PH_ACTIVATE_REQ , "PH_ACTIVATE_REQ"},
|
|
{PH_DEACTIVATE_REQ , "PH_DEACTIVATE_REQ"},
|
|
{PH_DATA_REQ , "PH_DATA_REQ"},
|
|
{MPH_ACTIVATE_REQ , "MPH_ACTIVATE_REQ"},
|
|
{MPH_DEACTIVATE_REQ , "MPH_DEACTIVATE_REQ"},
|
|
{MPH_INFORMATION_REQ , "MPH_INFORMATION_REQ"},
|
|
{PH_CONTROL_REQ , "PH_CONTROL_REQ"},
|
|
/* layer 1 -> layer 2 */
|
|
{PH_ACTIVATE_IND , "PH_ACTIVATE_IND"},
|
|
{PH_ACTIVATE_CNF , "PH_ACTIVATE_CNF"},
|
|
{PH_DEACTIVATE_IND , "PH_DEACTIVATE_IND"},
|
|
{PH_DEACTIVATE_CNF , "PH_DEACTIVATE_CNF"},
|
|
{PH_DATA_IND , "PH_DATA_IND"},
|
|
{PH_DATA_E_IND , "PH_DATA_E_IND"},
|
|
{MPH_ACTIVATE_IND , "MPH_ACTIVATE_IND"},
|
|
{MPH_DEACTIVATE_IND , "MPH_DEACTIVATE_IND"},
|
|
{MPH_INFORMATION_IND , "MPH_INFORMATION_IND"},
|
|
{PH_DATA_CNF , "PH_DATA_CNF"},
|
|
{PH_CONTROL_IND , "PH_CONTROL_IND"},
|
|
{PH_CONTROL_CNF , "PH_CONTROL_CNF"},
|
|
|
|
/* layer 3 -> layer 2 */
|
|
{DL_ESTABLISH_REQ , "DL_ESTABLISH_REQ"},
|
|
{DL_RELEASE_REQ , "DL_RELEASE_REQ"},
|
|
{DL_DATA_REQ , "DL_DATA_REQ"},
|
|
{DL_UNITDATA_REQ , "DL_UNITDATA_REQ"},
|
|
{DL_INFORMATION_REQ , "DL_INFORMATION_REQ"},
|
|
|
|
/* layer 2 -> layer 3 */
|
|
{DL_ESTABLISH_IND , "DL_ESTABLISH_IND"},
|
|
{DL_ESTABLISH_CNF , "DL_ESTABLISH_CNF"},
|
|
{DL_RELEASE_IND , "DL_RELEASE_IND"},
|
|
{DL_RELEASE_CNF , "DL_RELEASE_CNF"},
|
|
{DL_DATA_IND , "DL_DATA_IND"},
|
|
{DL_UNITDATA_IND , "DL_UNITDATA_IND"},
|
|
{DL_INFORMATION_IND , "DL_INFORMATION_IND"},
|
|
|
|
/* intern layer 2 management */
|
|
{MDL_ASSIGN_REQ , "MDL_ASSIGN_REQ"},
|
|
{MDL_ASSIGN_IND , "MDL_ASSIGN_IND"},
|
|
{MDL_REMOVE_REQ , "MDL_REMOVE_REQ"},
|
|
{MDL_REMOVE_IND , "MDL_REMOVE_IND"},
|
|
{MDL_STATUS_UP_IND , "MDL_STATUS_UP_IND"},
|
|
{MDL_STATUS_DOWN_IND , "MDL_STATUS_DOWN_IND"},
|
|
{MDL_STATUS_UI_IND , "MDL_STATUS_UI_IND"},
|
|
{MDL_ERROR_IND , "MDL_ERROR_IND"},
|
|
{MDL_ERROR_RSP , "MDL_ERROR_RSP"},
|
|
|
|
/* intern layer 2 */
|
|
{DL_TIMER200_IND , "DL_TIMER200_IND"},
|
|
{DL_TIMER203_IND , "DL_TIMER203_IND"},
|
|
{DL_INTERN_MSG , "DL_INTERN_MSG"},
|
|
|
|
/* L3 timer */
|
|
{CC_T301 , "Timer 301"},
|
|
{CC_T302 , "Timer 302"},
|
|
{CC_T303 , "Timer 303"},
|
|
{CC_T304 , "Timer 304"},
|
|
{CC_T305 , "Timer 305"},
|
|
{CC_T308_1 , "Timer 308(1)"},
|
|
{CC_T308_2 , "Timer 308(2)"},
|
|
{CC_T309 , "Timer 309"},
|
|
{CC_T310 , "Timer 310"},
|
|
{CC_T312 , "Timer 312"},
|
|
{CC_T313 , "Timer 313"},
|
|
{CC_T318 , "Timer 318"},
|
|
{CC_T319 , "Timer 319"},
|
|
{CC_TCTRL , "Timer CTRL"},
|
|
{CC_THOLD , "Timer HOLD"},
|
|
{CC_TRETRIEVE , "Timer RETRIEVE"},
|
|
{0xFFFFFFFF , "UNKNOWN"}
|
|
};
|
|
|
|
|
|
const char *
|
|
mi_msg_type2str(unsigned int cmd)
|
|
{
|
|
const struct _cmdtab *ct = cmdtab;
|
|
|
|
while (ct->cmd != 0xFFFFFFFF) {
|
|
if (ct->cmd == cmd)
|
|
break;
|
|
ct++;
|
|
}
|
|
if (ct->cmd == 0xFFFFFFFF)
|
|
return NULL;
|
|
else
|
|
return ct->name;
|
|
}
|
|
|
|
static const char _unknown_mt[] = {"UNKNOWN"};
|
|
|
|
const char *
|
|
_mi_msg_type2str(unsigned int cmd)
|
|
{
|
|
const char *t = mi_msg_type2str(cmd);
|
|
|
|
if (!t)
|
|
t = _unknown_mt;
|
|
return t;
|
|
}
|