forked from Minki/linux
5a0e3ad6af
percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
3229 lines
81 KiB
C
3229 lines
81 KiB
C
/* $Id: l3dss1.c,v 2.32.2.3 2004/01/13 14:31:25 keil Exp $
|
|
*
|
|
* EURO/DSS1 D-channel protocol
|
|
*
|
|
* German 1TR6 D-channel protocol
|
|
*
|
|
* Author Karsten Keil
|
|
* based on the teles driver from Jan den Ouden
|
|
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
|
*
|
|
* This software may be used and distributed according to the terms
|
|
* of the GNU General Public License, incorporated herein by reference.
|
|
*
|
|
* For changes and modifications please read
|
|
* Documentation/isdn/HiSax.cert
|
|
*
|
|
* Thanks to Jan den Ouden
|
|
* Fritz Elfert
|
|
*
|
|
*/
|
|
|
|
#include "hisax.h"
|
|
#include "isdnl3.h"
|
|
#include "l3dss1.h"
|
|
#include <linux/ctype.h>
|
|
#include <linux/slab.h>
|
|
|
|
extern char *HiSax_getrev(const char *revision);
|
|
static const char *dss1_revision = "$Revision: 2.32.2.3 $";
|
|
|
|
#define EXT_BEARER_CAPS 1
|
|
|
|
#define MsgHead(ptr, cref, mty) \
|
|
*ptr++ = 0x8; \
|
|
if (cref == -1) { \
|
|
*ptr++ = 0x0; \
|
|
} else { \
|
|
*ptr++ = 0x1; \
|
|
*ptr++ = cref^0x80; \
|
|
} \
|
|
*ptr++ = mty
|
|
|
|
|
|
/**********************************************/
|
|
/* get a new invoke id for remote operations. */
|
|
/* Only a return value != 0 is valid */
|
|
/**********************************************/
|
|
static unsigned char new_invoke_id(struct PStack *p)
|
|
{
|
|
unsigned char retval;
|
|
int i;
|
|
|
|
i = 32; /* maximum search depth */
|
|
|
|
retval = p->prot.dss1.last_invoke_id + 1; /* try new id */
|
|
while ((i) && (p->prot.dss1.invoke_used[retval >> 3] == 0xFF)) {
|
|
p->prot.dss1.last_invoke_id = (retval & 0xF8) + 8;
|
|
i--;
|
|
}
|
|
if (i) {
|
|
while (p->prot.dss1.invoke_used[retval >> 3] & (1 << (retval & 7)))
|
|
retval++;
|
|
} else
|
|
retval = 0;
|
|
p->prot.dss1.last_invoke_id = retval;
|
|
p->prot.dss1.invoke_used[retval >> 3] |= (1 << (retval & 7));
|
|
return(retval);
|
|
} /* new_invoke_id */
|
|
|
|
/*************************/
|
|
/* free a used invoke id */
|
|
/*************************/
|
|
static void free_invoke_id(struct PStack *p, unsigned char id)
|
|
{
|
|
|
|
if (!id) return; /* 0 = invalid value */
|
|
|
|
p->prot.dss1.invoke_used[id >> 3] &= ~(1 << (id & 7));
|
|
} /* free_invoke_id */
|
|
|
|
|
|
/**********************************************************/
|
|
/* create a new l3 process and fill in dss1 specific data */
|
|
/**********************************************************/
|
|
static struct l3_process
|
|
*dss1_new_l3_process(struct PStack *st, int cr)
|
|
{ struct l3_process *proc;
|
|
|
|
if (!(proc = new_l3_process(st, cr)))
|
|
return(NULL);
|
|
|
|
proc->prot.dss1.invoke_id = 0;
|
|
proc->prot.dss1.remote_operation = 0;
|
|
proc->prot.dss1.uus1_data[0] = '\0';
|
|
|
|
return(proc);
|
|
} /* dss1_new_l3_process */
|
|
|
|
/************************************************/
|
|
/* free a l3 process and all dss1 specific data */
|
|
/************************************************/
|
|
static void
|
|
dss1_release_l3_process(struct l3_process *p)
|
|
{
|
|
free_invoke_id(p->st,p->prot.dss1.invoke_id);
|
|
release_l3_process(p);
|
|
} /* dss1_release_l3_process */
|
|
|
|
/********************************************************/
|
|
/* search a process with invoke id id and dummy callref */
|
|
/********************************************************/
|
|
static struct l3_process *
|
|
l3dss1_search_dummy_proc(struct PStack *st, int id)
|
|
{ struct l3_process *pc = st->l3.proc; /* start of processes */
|
|
|
|
if (!id) return(NULL);
|
|
|
|
while (pc)
|
|
{ if ((pc->callref == -1) && (pc->prot.dss1.invoke_id == id))
|
|
return(pc);
|
|
pc = pc->next;
|
|
}
|
|
return(NULL);
|
|
} /* l3dss1_search_dummy_proc */
|
|
|
|
/*******************************************************************/
|
|
/* called when a facility message with a dummy callref is received */
|
|
/* and a return result is delivered. id specifies the invoke id. */
|
|
/*******************************************************************/
|
|
static void
|
|
l3dss1_dummy_return_result(struct PStack *st, int id, u_char *p, u_char nlen)
|
|
{ isdn_ctrl ic;
|
|
struct IsdnCardState *cs;
|
|
struct l3_process *pc = NULL;
|
|
|
|
if ((pc = l3dss1_search_dummy_proc(st, id)))
|
|
{ L3DelTimer(&pc->timer); /* remove timer */
|
|
|
|
cs = pc->st->l1.hardware;
|
|
ic.driver = cs->myid;
|
|
ic.command = ISDN_STAT_PROT;
|
|
ic.arg = DSS1_STAT_INVOKE_RES;
|
|
ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id;
|
|
ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id;
|
|
ic.parm.dss1_io.proc = pc->prot.dss1.proc;
|
|
ic.parm.dss1_io.timeout= 0;
|
|
ic.parm.dss1_io.datalen = nlen;
|
|
ic.parm.dss1_io.data = p;
|
|
free_invoke_id(pc->st, pc->prot.dss1.invoke_id);
|
|
pc->prot.dss1.invoke_id = 0; /* reset id */
|
|
|
|
cs->iif.statcallb(&ic);
|
|
dss1_release_l3_process(pc);
|
|
}
|
|
else
|
|
l3_debug(st, "dummy return result id=0x%x result len=%d",id,nlen);
|
|
} /* l3dss1_dummy_return_result */
|
|
|
|
/*******************************************************************/
|
|
/* called when a facility message with a dummy callref is received */
|
|
/* and a return error is delivered. id specifies the invoke id. */
|
|
/*******************************************************************/
|
|
static void
|
|
l3dss1_dummy_error_return(struct PStack *st, int id, ulong error)
|
|
{ isdn_ctrl ic;
|
|
struct IsdnCardState *cs;
|
|
struct l3_process *pc = NULL;
|
|
|
|
if ((pc = l3dss1_search_dummy_proc(st, id)))
|
|
{ L3DelTimer(&pc->timer); /* remove timer */
|
|
|
|
cs = pc->st->l1.hardware;
|
|
ic.driver = cs->myid;
|
|
ic.command = ISDN_STAT_PROT;
|
|
ic.arg = DSS1_STAT_INVOKE_ERR;
|
|
ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id;
|
|
ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id;
|
|
ic.parm.dss1_io.proc = pc->prot.dss1.proc;
|
|
ic.parm.dss1_io.timeout= error;
|
|
ic.parm.dss1_io.datalen = 0;
|
|
ic.parm.dss1_io.data = NULL;
|
|
free_invoke_id(pc->st, pc->prot.dss1.invoke_id);
|
|
pc->prot.dss1.invoke_id = 0; /* reset id */
|
|
|
|
cs->iif.statcallb(&ic);
|
|
dss1_release_l3_process(pc);
|
|
}
|
|
else
|
|
l3_debug(st, "dummy return error id=0x%x error=0x%lx",id,error);
|
|
} /* l3dss1_error_return */
|
|
|
|
/*******************************************************************/
|
|
/* called when a facility message with a dummy callref is received */
|
|
/* and a invoke is delivered. id specifies the invoke id. */
|
|
/*******************************************************************/
|
|
static void
|
|
l3dss1_dummy_invoke(struct PStack *st, int cr, int id,
|
|
int ident, u_char *p, u_char nlen)
|
|
{ isdn_ctrl ic;
|
|
struct IsdnCardState *cs;
|
|
|
|
l3_debug(st, "dummy invoke %s id=0x%x ident=0x%x datalen=%d",
|
|
(cr == -1) ? "local" : "broadcast",id,ident,nlen);
|
|
if (cr >= -1) return; /* ignore local data */
|
|
|
|
cs = st->l1.hardware;
|
|
ic.driver = cs->myid;
|
|
ic.command = ISDN_STAT_PROT;
|
|
ic.arg = DSS1_STAT_INVOKE_BRD;
|
|
ic.parm.dss1_io.hl_id = id;
|
|
ic.parm.dss1_io.ll_id = 0;
|
|
ic.parm.dss1_io.proc = ident;
|
|
ic.parm.dss1_io.timeout= 0;
|
|
ic.parm.dss1_io.datalen = nlen;
|
|
ic.parm.dss1_io.data = p;
|
|
|
|
cs->iif.statcallb(&ic);
|
|
} /* l3dss1_dummy_invoke */
|
|
|
|
static void
|
|
l3dss1_parse_facility(struct PStack *st, struct l3_process *pc,
|
|
int cr, u_char * p)
|
|
{
|
|
int qd_len = 0;
|
|
unsigned char nlen = 0, ilen, cp_tag;
|
|
int ident, id;
|
|
ulong err_ret;
|
|
|
|
if (pc)
|
|
st = pc->st; /* valid Stack */
|
|
else
|
|
if ((!st) || (cr >= 0)) return; /* neither pc nor st specified */
|
|
|
|
p++;
|
|
qd_len = *p++;
|
|
if (qd_len == 0) {
|
|
l3_debug(st, "qd_len == 0");
|
|
return;
|
|
}
|
|
if ((*p & 0x1F) != 0x11) { /* Service discriminator, supplementary service */
|
|
l3_debug(st, "supplementary service != 0x11");
|
|
return;
|
|
}
|
|
while (qd_len > 0 && !(*p & 0x80)) { /* extension ? */
|
|
p++;
|
|
qd_len--;
|
|
}
|
|
if (qd_len < 2) {
|
|
l3_debug(st, "qd_len < 2");
|
|
return;
|
|
}
|
|
p++;
|
|
qd_len--;
|
|
if ((*p & 0xE0) != 0xA0) { /* class and form */
|
|
l3_debug(st, "class and form != 0xA0");
|
|
return;
|
|
}
|
|
|
|
cp_tag = *p & 0x1F; /* remember tag value */
|
|
|
|
p++;
|
|
qd_len--;
|
|
if (qd_len < 1)
|
|
{ l3_debug(st, "qd_len < 1");
|
|
return;
|
|
}
|
|
if (*p & 0x80)
|
|
{ /* length format indefinite or limited */
|
|
nlen = *p++ & 0x7F; /* number of len bytes or indefinite */
|
|
if ((qd_len-- < ((!nlen) ? 3 : (1 + nlen))) ||
|
|
(nlen > 1))
|
|
{ l3_debug(st, "length format error or not implemented");
|
|
return;
|
|
}
|
|
if (nlen == 1)
|
|
{ nlen = *p++; /* complete length */
|
|
qd_len--;
|
|
}
|
|
else
|
|
{ qd_len -= 2; /* trailing null bytes */
|
|
if ((*(p+qd_len)) || (*(p+qd_len+1)))
|
|
{ l3_debug(st,"length format indefinite error");
|
|
return;
|
|
}
|
|
nlen = qd_len;
|
|
}
|
|
}
|
|
else
|
|
{ nlen = *p++;
|
|
qd_len--;
|
|
}
|
|
if (qd_len < nlen)
|
|
{ l3_debug(st, "qd_len < nlen");
|
|
return;
|
|
}
|
|
qd_len -= nlen;
|
|
|
|
if (nlen < 2)
|
|
{ l3_debug(st, "nlen < 2");
|
|
return;
|
|
}
|
|
if (*p != 0x02)
|
|
{ /* invoke identifier tag */
|
|
l3_debug(st, "invoke identifier tag !=0x02");
|
|
return;
|
|
}
|
|
p++;
|
|
nlen--;
|
|
if (*p & 0x80)
|
|
{ /* length format */
|
|
l3_debug(st, "invoke id length format 2");
|
|
return;
|
|
}
|
|
ilen = *p++;
|
|
nlen--;
|
|
if (ilen > nlen || ilen == 0)
|
|
{ l3_debug(st, "ilen > nlen || ilen == 0");
|
|
return;
|
|
}
|
|
nlen -= ilen;
|
|
id = 0;
|
|
while (ilen > 0)
|
|
{ id = (id << 8) | (*p++ & 0xFF); /* invoke identifier */
|
|
ilen--;
|
|
}
|
|
|
|
switch (cp_tag) { /* component tag */
|
|
case 1: /* invoke */
|
|
if (nlen < 2) {
|
|
l3_debug(st, "nlen < 2 22");
|
|
return;
|
|
}
|
|
if (*p != 0x02) { /* operation value */
|
|
l3_debug(st, "operation value !=0x02");
|
|
return;
|
|
}
|
|
p++;
|
|
nlen--;
|
|
ilen = *p++;
|
|
nlen--;
|
|
if (ilen > nlen || ilen == 0) {
|
|
l3_debug(st, "ilen > nlen || ilen == 0 22");
|
|
return;
|
|
}
|
|
nlen -= ilen;
|
|
ident = 0;
|
|
while (ilen > 0) {
|
|
ident = (ident << 8) | (*p++ & 0xFF);
|
|
ilen--;
|
|
}
|
|
|
|
if (!pc)
|
|
{ l3dss1_dummy_invoke(st, cr, id, ident, p, nlen);
|
|
return;
|
|
}
|
|
#ifdef HISAX_DE_AOC
|
|
{
|
|
|
|
#define FOO1(s,a,b) \
|
|
while(nlen > 1) { \
|
|
int ilen = p[1]; \
|
|
if(nlen < ilen+2) { \
|
|
l3_debug(st, "FOO1 nlen < ilen+2"); \
|
|
return; \
|
|
} \
|
|
nlen -= ilen+2; \
|
|
if((*p & 0xFF) == (a)) { \
|
|
int nlen = ilen; \
|
|
p += 2; \
|
|
b; \
|
|
} else { \
|
|
p += ilen+2; \
|
|
} \
|
|
}
|
|
|
|
switch (ident) {
|
|
case 0x22: /* during */
|
|
FOO1("1A", 0x30, FOO1("1C", 0xA1, FOO1("1D", 0x30, FOO1("1E", 0x02, ( {
|
|
ident = 0;
|
|
nlen = (nlen)?nlen:0; /* Make gcc happy */
|
|
while (ilen > 0) {
|
|
ident = (ident << 8) | *p++;
|
|
ilen--;
|
|
}
|
|
if (ident > pc->para.chargeinfo) {
|
|
pc->para.chargeinfo = ident;
|
|
st->l3.l3l4(st, CC_CHARGE | INDICATION, pc);
|
|
}
|
|
if (st->l3.debug & L3_DEB_CHARGE) {
|
|
if (*(p + 2) == 0) {
|
|
l3_debug(st, "charging info during %d", pc->para.chargeinfo);
|
|
}
|
|
else {
|
|
l3_debug(st, "charging info final %d", pc->para.chargeinfo);
|
|
}
|
|
}
|
|
}
|
|
)))))
|
|
break;
|
|
case 0x24: /* final */
|
|
FOO1("2A", 0x30, FOO1("2B", 0x30, FOO1("2C", 0xA1, FOO1("2D", 0x30, FOO1("2E", 0x02, ( {
|
|
ident = 0;
|
|
nlen = (nlen)?nlen:0; /* Make gcc happy */
|
|
while (ilen > 0) {
|
|
ident = (ident << 8) | *p++;
|
|
ilen--;
|
|
}
|
|
if (ident > pc->para.chargeinfo) {
|
|
pc->para.chargeinfo = ident;
|
|
st->l3.l3l4(st, CC_CHARGE | INDICATION, pc);
|
|
}
|
|
if (st->l3.debug & L3_DEB_CHARGE) {
|
|
l3_debug(st, "charging info final %d", pc->para.chargeinfo);
|
|
}
|
|
}
|
|
))))))
|
|
break;
|
|
default:
|
|
l3_debug(st, "invoke break invalid ident %02x",ident);
|
|
break;
|
|
}
|
|
#undef FOO1
|
|
|
|
}
|
|
#else /* not HISAX_DE_AOC */
|
|
l3_debug(st, "invoke break");
|
|
#endif /* not HISAX_DE_AOC */
|
|
break;
|
|
case 2: /* return result */
|
|
/* if no process available handle separately */
|
|
if (!pc)
|
|
{ if (cr == -1)
|
|
l3dss1_dummy_return_result(st, id, p, nlen);
|
|
return;
|
|
}
|
|
if ((pc->prot.dss1.invoke_id) && (pc->prot.dss1.invoke_id == id))
|
|
{ /* Diversion successful */
|
|
free_invoke_id(st,pc->prot.dss1.invoke_id);
|
|
pc->prot.dss1.remote_result = 0; /* success */
|
|
pc->prot.dss1.invoke_id = 0;
|
|
pc->redir_result = pc->prot.dss1.remote_result;
|
|
st->l3.l3l4(st, CC_REDIR | INDICATION, pc); } /* Diversion successful */
|
|
else
|
|
l3_debug(st,"return error unknown identifier");
|
|
break;
|
|
case 3: /* return error */
|
|
err_ret = 0;
|
|
if (nlen < 2)
|
|
{ l3_debug(st, "return error nlen < 2");
|
|
return;
|
|
}
|
|
if (*p != 0x02)
|
|
{ /* result tag */
|
|
l3_debug(st, "invoke error tag !=0x02");
|
|
return;
|
|
}
|
|
p++;
|
|
nlen--;
|
|
if (*p > 4)
|
|
{ /* length format */
|
|
l3_debug(st, "invoke return errlen > 4 ");
|
|
return;
|
|
}
|
|
ilen = *p++;
|
|
nlen--;
|
|
if (ilen > nlen || ilen == 0)
|
|
{ l3_debug(st, "error return ilen > nlen || ilen == 0");
|
|
return;
|
|
}
|
|
nlen -= ilen;
|
|
while (ilen > 0)
|
|
{ err_ret = (err_ret << 8) | (*p++ & 0xFF); /* error value */
|
|
ilen--;
|
|
}
|
|
/* if no process available handle separately */
|
|
if (!pc)
|
|
{ if (cr == -1)
|
|
l3dss1_dummy_error_return(st, id, err_ret);
|
|
return;
|
|
}
|
|
if ((pc->prot.dss1.invoke_id) && (pc->prot.dss1.invoke_id == id))
|
|
{ /* Deflection error */
|
|
free_invoke_id(st,pc->prot.dss1.invoke_id);
|
|
pc->prot.dss1.remote_result = err_ret; /* result */
|
|
pc->prot.dss1.invoke_id = 0;
|
|
pc->redir_result = pc->prot.dss1.remote_result;
|
|
st->l3.l3l4(st, CC_REDIR | INDICATION, pc);
|
|
} /* Deflection error */
|
|
else
|
|
l3_debug(st,"return result unknown identifier");
|
|
break;
|
|
default:
|
|
l3_debug(st, "facility default break tag=0x%02x",cp_tag);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
l3dss1_message(struct l3_process *pc, u_char mt)
|
|
{
|
|
struct sk_buff *skb;
|
|
u_char *p;
|
|
|
|
if (!(skb = l3_alloc_skb(4)))
|
|
return;
|
|
p = skb_put(skb, 4);
|
|
MsgHead(p, pc->callref, mt);
|
|
l3_msg(pc->st, DL_DATA | REQUEST, skb);
|
|
}
|
|
|
|
static void
|
|
l3dss1_message_cause(struct l3_process *pc, u_char mt, u_char cause)
|
|
{
|
|
struct sk_buff *skb;
|
|
u_char tmp[16];
|
|
u_char *p = tmp;
|
|
int l;
|
|
|
|
MsgHead(p, pc->callref, mt);
|
|
*p++ = IE_CAUSE;
|
|
*p++ = 0x2;
|
|
*p++ = 0x80;
|
|
*p++ = cause | 0x80;
|
|
|
|
l = p - tmp;
|
|
if (!(skb = l3_alloc_skb(l)))
|
|
return;
|
|
memcpy(skb_put(skb, l), tmp, l);
|
|
l3_msg(pc->st, DL_DATA | REQUEST, skb);
|
|
}
|
|
|
|
static void
|
|
l3dss1_status_send(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
u_char tmp[16];
|
|
u_char *p = tmp;
|
|
int l;
|
|
struct sk_buff *skb;
|
|
|
|
MsgHead(p, pc->callref, MT_STATUS);
|
|
|
|
*p++ = IE_CAUSE;
|
|
*p++ = 0x2;
|
|
*p++ = 0x80;
|
|
*p++ = pc->para.cause | 0x80;
|
|
|
|
*p++ = IE_CALL_STATE;
|
|
*p++ = 0x1;
|
|
*p++ = pc->state & 0x3f;
|
|
|
|
l = p - tmp;
|
|
if (!(skb = l3_alloc_skb(l)))
|
|
return;
|
|
memcpy(skb_put(skb, l), tmp, l);
|
|
l3_msg(pc->st, DL_DATA | REQUEST, skb);
|
|
}
|
|
|
|
static void
|
|
l3dss1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
/* This routine is called if here was no SETUP made (checks in dss1up and in
|
|
* l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code
|
|
* MT_STATUS_ENQUIRE in the NULL state is handled too
|
|
*/
|
|
u_char tmp[16];
|
|
u_char *p = tmp;
|
|
int l;
|
|
struct sk_buff *skb;
|
|
|
|
switch (pc->para.cause) {
|
|
case 81: /* invalid callreference */
|
|
case 88: /* incomp destination */
|
|
case 96: /* mandory IE missing */
|
|
case 100: /* invalid IE contents */
|
|
case 101: /* incompatible Callstate */
|
|
MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
|
|
*p++ = IE_CAUSE;
|
|
*p++ = 0x2;
|
|
*p++ = 0x80;
|
|
*p++ = pc->para.cause | 0x80;
|
|
break;
|
|
default:
|
|
printk(KERN_ERR "HiSax l3dss1_msg_without_setup wrong cause %d\n",
|
|
pc->para.cause);
|
|
return;
|
|
}
|
|
l = p - tmp;
|
|
if (!(skb = l3_alloc_skb(l)))
|
|
return;
|
|
memcpy(skb_put(skb, l), tmp, l);
|
|
l3_msg(pc->st, DL_DATA | REQUEST, skb);
|
|
dss1_release_l3_process(pc);
|
|
}
|
|
|
|
static int ie_ALERTING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1,
|
|
IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_HLC,
|
|
IE_USER_USER, -1};
|
|
static int ie_CALL_PROCEEDING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1,
|
|
IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_HLC, -1};
|
|
static int ie_CONNECT[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1,
|
|
IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_DATE, IE_SIGNAL,
|
|
IE_CONNECT_PN, IE_CONNECT_SUB, IE_LLC, IE_HLC, IE_USER_USER, -1};
|
|
static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_SIGNAL, -1};
|
|
static int ie_DISCONNECT[] = {IE_CAUSE | IE_MANDATORY, IE_FACILITY,
|
|
IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1};
|
|
static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL,
|
|
IE_CALLED_PN, -1};
|
|
static int ie_NOTIFY[] = {IE_BEARER, IE_NOTIFY | IE_MANDATORY, IE_DISPLAY, -1};
|
|
static int ie_PROGRESS[] = {IE_BEARER, IE_CAUSE, IE_FACILITY, IE_PROGRESS |
|
|
IE_MANDATORY, IE_DISPLAY, IE_HLC, IE_USER_USER, -1};
|
|
static int ie_RELEASE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY, IE_DISPLAY,
|
|
IE_SIGNAL, IE_USER_USER, -1};
|
|
/* a RELEASE_COMPLETE with errors don't require special actions
|
|
static int ie_RELEASE_COMPLETE[] = {IE_CAUSE | IE_MANDATORY_1, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1};
|
|
*/
|
|
static int ie_RESUME_ACKNOWLEDGE[] = {IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY,
|
|
IE_DISPLAY, -1};
|
|
static int ie_RESUME_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
|
|
static int ie_SETUP[] = {IE_COMPLETE, IE_BEARER | IE_MANDATORY,
|
|
IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, IE_PROGRESS,
|
|
IE_NET_FAC, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL, IE_CALLING_PN,
|
|
IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_REDIR_NR,
|
|
IE_LLC, IE_HLC, IE_USER_USER, -1};
|
|
static int ie_SETUP_ACKNOWLEDGE[] = {IE_CHANNEL_ID | IE_MANDATORY, IE_FACILITY,
|
|
IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, -1};
|
|
static int ie_STATUS[] = {IE_CAUSE | IE_MANDATORY, IE_CALL_STATE |
|
|
IE_MANDATORY, IE_DISPLAY, -1};
|
|
static int ie_STATUS_ENQUIRY[] = {IE_DISPLAY, -1};
|
|
static int ie_SUSPEND_ACKNOWLEDGE[] = {IE_DISPLAY, IE_FACILITY, -1};
|
|
static int ie_SUSPEND_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
|
|
/* not used
|
|
* static int ie_CONGESTION_CONTROL[] = {IE_CONGESTION | IE_MANDATORY,
|
|
* IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
|
|
* static int ie_USER_INFORMATION[] = {IE_MORE_DATA, IE_USER_USER | IE_MANDATORY, -1};
|
|
* static int ie_RESTART[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_RESTART_IND |
|
|
* IE_MANDATORY, -1};
|
|
*/
|
|
static int ie_FACILITY[] = {IE_FACILITY | IE_MANDATORY, IE_DISPLAY, -1};
|
|
static int comp_required[] = {1,2,3,5,6,7,9,10,11,14,15,-1};
|
|
static int l3_valid_states[] = {0,1,2,3,4,6,7,8,9,10,11,12,15,17,19,25,-1};
|
|
|
|
struct ie_len {
|
|
int ie;
|
|
int len;
|
|
};
|
|
|
|
static
|
|
struct ie_len max_ie_len[] = {
|
|
{IE_SEGMENT, 4},
|
|
{IE_BEARER, 12},
|
|
{IE_CAUSE, 32},
|
|
{IE_CALL_ID, 10},
|
|
{IE_CALL_STATE, 3},
|
|
{IE_CHANNEL_ID, 34},
|
|
{IE_FACILITY, 255},
|
|
{IE_PROGRESS, 4},
|
|
{IE_NET_FAC, 255},
|
|
{IE_NOTIFY, 3},
|
|
{IE_DISPLAY, 82},
|
|
{IE_DATE, 8},
|
|
{IE_KEYPAD, 34},
|
|
{IE_SIGNAL, 3},
|
|
{IE_INFORATE, 6},
|
|
{IE_E2E_TDELAY, 11},
|
|
{IE_TDELAY_SEL, 5},
|
|
{IE_PACK_BINPARA, 3},
|
|
{IE_PACK_WINSIZE, 4},
|
|
{IE_PACK_SIZE, 4},
|
|
{IE_CUG, 7},
|
|
{IE_REV_CHARGE, 3},
|
|
{IE_CALLING_PN, 24},
|
|
{IE_CALLING_SUB, 23},
|
|
{IE_CALLED_PN, 24},
|
|
{IE_CALLED_SUB, 23},
|
|
{IE_REDIR_NR, 255},
|
|
{IE_TRANS_SEL, 255},
|
|
{IE_RESTART_IND, 3},
|
|
{IE_LLC, 18},
|
|
{IE_HLC, 5},
|
|
{IE_USER_USER, 131},
|
|
{-1,0},
|
|
};
|
|
|
|
static int
|
|
getmax_ie_len(u_char ie) {
|
|
int i = 0;
|
|
while (max_ie_len[i].ie != -1) {
|
|
if (max_ie_len[i].ie == ie)
|
|
return(max_ie_len[i].len);
|
|
i++;
|
|
}
|
|
return(255);
|
|
}
|
|
|
|
static int
|
|
ie_in_set(struct l3_process *pc, u_char ie, int *checklist) {
|
|
int ret = 1;
|
|
|
|
while (*checklist != -1) {
|
|
if ((*checklist & 0xff) == ie) {
|
|
if (ie & 0x80)
|
|
return(-ret);
|
|
else
|
|
return(ret);
|
|
}
|
|
ret++;
|
|
checklist++;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
check_infoelements(struct l3_process *pc, struct sk_buff *skb, int *checklist)
|
|
{
|
|
int *cl = checklist;
|
|
u_char mt;
|
|
u_char *p, ie;
|
|
int l, newpos, oldpos;
|
|
int err_seq = 0, err_len = 0, err_compr = 0, err_ureg = 0;
|
|
u_char codeset = 0;
|
|
u_char old_codeset = 0;
|
|
u_char codelock = 1;
|
|
|
|
p = skb->data;
|
|
/* skip cr */
|
|
p++;
|
|
l = (*p++) & 0xf;
|
|
p += l;
|
|
mt = *p++;
|
|
oldpos = 0;
|
|
while ((p - skb->data) < skb->len) {
|
|
if ((*p & 0xf0) == 0x90) { /* shift codeset */
|
|
old_codeset = codeset;
|
|
codeset = *p & 7;
|
|
if (*p & 0x08)
|
|
codelock = 0;
|
|
else
|
|
codelock = 1;
|
|
if (pc->debug & L3_DEB_CHECK)
|
|
l3_debug(pc->st, "check IE shift%scodeset %d->%d",
|
|
codelock ? " locking ": " ", old_codeset, codeset);
|
|
p++;
|
|
continue;
|
|
}
|
|
if (!codeset) { /* only codeset 0 */
|
|
if ((newpos = ie_in_set(pc, *p, cl))) {
|
|
if (newpos > 0) {
|
|
if (newpos < oldpos)
|
|
err_seq++;
|
|
else
|
|
oldpos = newpos;
|
|
}
|
|
} else {
|
|
if (ie_in_set(pc, *p, comp_required))
|
|
err_compr++;
|
|
else
|
|
err_ureg++;
|
|
}
|
|
}
|
|
ie = *p++;
|
|
if (ie & 0x80) {
|
|
l = 1;
|
|
} else {
|
|
l = *p++;
|
|
p += l;
|
|
l += 2;
|
|
}
|
|
if (!codeset && (l > getmax_ie_len(ie)))
|
|
err_len++;
|
|
if (!codelock) {
|
|
if (pc->debug & L3_DEB_CHECK)
|
|
l3_debug(pc->st, "check IE shift back codeset %d->%d",
|
|
codeset, old_codeset);
|
|
codeset = old_codeset;
|
|
codelock = 1;
|
|
}
|
|
}
|
|
if (err_compr | err_ureg | err_len | err_seq) {
|
|
if (pc->debug & L3_DEB_CHECK)
|
|
l3_debug(pc->st, "check IE MT(%x) %d/%d/%d/%d",
|
|
mt, err_compr, err_ureg, err_len, err_seq);
|
|
if (err_compr)
|
|
return(ERR_IE_COMPREHENSION);
|
|
if (err_ureg)
|
|
return(ERR_IE_UNRECOGNIZED);
|
|
if (err_len)
|
|
return(ERR_IE_LENGTH);
|
|
if (err_seq)
|
|
return(ERR_IE_SEQUENCE);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/* verify if a message type exists and contain no IE error */
|
|
static int
|
|
l3dss1_check_messagetype_validity(struct l3_process *pc, int mt, void *arg)
|
|
{
|
|
switch (mt) {
|
|
case MT_ALERTING:
|
|
case MT_CALL_PROCEEDING:
|
|
case MT_CONNECT:
|
|
case MT_CONNECT_ACKNOWLEDGE:
|
|
case MT_DISCONNECT:
|
|
case MT_INFORMATION:
|
|
case MT_FACILITY:
|
|
case MT_NOTIFY:
|
|
case MT_PROGRESS:
|
|
case MT_RELEASE:
|
|
case MT_RELEASE_COMPLETE:
|
|
case MT_SETUP:
|
|
case MT_SETUP_ACKNOWLEDGE:
|
|
case MT_RESUME_ACKNOWLEDGE:
|
|
case MT_RESUME_REJECT:
|
|
case MT_SUSPEND_ACKNOWLEDGE:
|
|
case MT_SUSPEND_REJECT:
|
|
case MT_USER_INFORMATION:
|
|
case MT_RESTART:
|
|
case MT_RESTART_ACKNOWLEDGE:
|
|
case MT_CONGESTION_CONTROL:
|
|
case MT_STATUS:
|
|
case MT_STATUS_ENQUIRY:
|
|
if (pc->debug & L3_DEB_CHECK)
|
|
l3_debug(pc->st, "l3dss1_check_messagetype_validity mt(%x) OK", mt);
|
|
break;
|
|
case MT_RESUME: /* RESUME only in user->net */
|
|
case MT_SUSPEND: /* SUSPEND only in user->net */
|
|
default:
|
|
if (pc->debug & (L3_DEB_CHECK | L3_DEB_WARN))
|
|
l3_debug(pc->st, "l3dss1_check_messagetype_validity mt(%x) fail", mt);
|
|
pc->para.cause = 97;
|
|
l3dss1_status_send(pc, 0, NULL);
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static void
|
|
l3dss1_std_ie_err(struct l3_process *pc, int ret) {
|
|
|
|
if (pc->debug & L3_DEB_CHECK)
|
|
l3_debug(pc->st, "check_infoelements ret %d", ret);
|
|
switch(ret) {
|
|
case 0:
|
|
break;
|
|
case ERR_IE_COMPREHENSION:
|
|
pc->para.cause = 96;
|
|
l3dss1_status_send(pc, 0, NULL);
|
|
break;
|
|
case ERR_IE_UNRECOGNIZED:
|
|
pc->para.cause = 99;
|
|
l3dss1_status_send(pc, 0, NULL);
|
|
break;
|
|
case ERR_IE_LENGTH:
|
|
pc->para.cause = 100;
|
|
l3dss1_status_send(pc, 0, NULL);
|
|
break;
|
|
case ERR_IE_SEQUENCE:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int
|
|
l3dss1_get_channel_id(struct l3_process *pc, struct sk_buff *skb) {
|
|
u_char *p;
|
|
|
|
p = skb->data;
|
|
if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
|
|
p++;
|
|
if (*p != 1) { /* len for BRI = 1 */
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "wrong chid len %d", *p);
|
|
return (-2);
|
|
}
|
|
p++;
|
|
if (*p & 0x60) { /* only base rate interface */
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "wrong chid %x", *p);
|
|
return (-3);
|
|
}
|
|
return(*p & 0x3);
|
|
} else
|
|
return(-1);
|
|
}
|
|
|
|
static int
|
|
l3dss1_get_cause(struct l3_process *pc, struct sk_buff *skb) {
|
|
u_char l, i=0;
|
|
u_char *p;
|
|
|
|
p = skb->data;
|
|
pc->para.cause = 31;
|
|
pc->para.loc = 0;
|
|
if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
|
|
p++;
|
|
l = *p++;
|
|
if (l>30)
|
|
return(1);
|
|
if (l) {
|
|
pc->para.loc = *p++;
|
|
l--;
|
|
} else {
|
|
return(2);
|
|
}
|
|
if (l && !(pc->para.loc & 0x80)) {
|
|
l--;
|
|
p++; /* skip recommendation */
|
|
}
|
|
if (l) {
|
|
pc->para.cause = *p++;
|
|
l--;
|
|
if (!(pc->para.cause & 0x80))
|
|
return(3);
|
|
} else
|
|
return(4);
|
|
while (l && (i<6)) {
|
|
pc->para.diag[i++] = *p++;
|
|
l--;
|
|
}
|
|
} else
|
|
return(-1);
|
|
return(0);
|
|
}
|
|
|
|
static void
|
|
l3dss1_msg_with_uus(struct l3_process *pc, u_char cmd)
|
|
{
|
|
struct sk_buff *skb;
|
|
u_char tmp[16+40];
|
|
u_char *p = tmp;
|
|
int l;
|
|
|
|
MsgHead(p, pc->callref, cmd);
|
|
|
|
if (pc->prot.dss1.uus1_data[0])
|
|
{ *p++ = IE_USER_USER; /* UUS info element */
|
|
*p++ = strlen(pc->prot.dss1.uus1_data) + 1;
|
|
*p++ = 0x04; /* IA5 chars */
|
|
strcpy(p,pc->prot.dss1.uus1_data);
|
|
p += strlen(pc->prot.dss1.uus1_data);
|
|
pc->prot.dss1.uus1_data[0] = '\0';
|
|
}
|
|
|
|
l = p - tmp;
|
|
if (!(skb = l3_alloc_skb(l)))
|
|
return;
|
|
memcpy(skb_put(skb, l), tmp, l);
|
|
l3_msg(pc->st, DL_DATA | REQUEST, skb);
|
|
} /* l3dss1_msg_with_uus */
|
|
|
|
static void
|
|
l3dss1_release_req(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
StopAllL3Timer(pc);
|
|
newl3state(pc, 19);
|
|
if (!pc->prot.dss1.uus1_data[0])
|
|
l3dss1_message(pc, MT_RELEASE);
|
|
else
|
|
l3dss1_msg_with_uus(pc, MT_RELEASE);
|
|
L3AddTimer(&pc->timer, T308, CC_T308_1);
|
|
}
|
|
|
|
static void
|
|
l3dss1_release_cmpl(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb = arg;
|
|
int ret;
|
|
|
|
if ((ret = l3dss1_get_cause(pc, skb))>0) {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "RELCMPL get_cause ret(%d)",ret);
|
|
} else if (ret < 0)
|
|
pc->para.cause = NO_CAUSE;
|
|
StopAllL3Timer(pc);
|
|
newl3state(pc, 0);
|
|
pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
|
|
dss1_release_l3_process(pc);
|
|
}
|
|
|
|
#ifdef EXT_BEARER_CAPS
|
|
|
|
static u_char *
|
|
EncodeASyncParams(u_char * p, u_char si2)
|
|
{ // 7c 06 88 90 21 42 00 bb
|
|
|
|
p[0] = 0;
|
|
p[1] = 0x40; // Intermediate rate: 16 kbit/s jj 2000.02.19
|
|
p[2] = 0x80;
|
|
if (si2 & 32) // 7 data bits
|
|
|
|
p[2] += 16;
|
|
else // 8 data bits
|
|
|
|
p[2] += 24;
|
|
|
|
if (si2 & 16) // 2 stop bits
|
|
|
|
p[2] += 96;
|
|
else // 1 stop bit
|
|
|
|
p[2] += 32;
|
|
|
|
if (si2 & 8) // even parity
|
|
|
|
p[2] += 2;
|
|
else // no parity
|
|
|
|
p[2] += 3;
|
|
|
|
switch (si2 & 0x07) {
|
|
case 0:
|
|
p[0] = 66; // 1200 bit/s
|
|
|
|
break;
|
|
case 1:
|
|
p[0] = 88; // 1200/75 bit/s
|
|
|
|
break;
|
|
case 2:
|
|
p[0] = 87; // 75/1200 bit/s
|
|
|
|
break;
|
|
case 3:
|
|
p[0] = 67; // 2400 bit/s
|
|
|
|
break;
|
|
case 4:
|
|
p[0] = 69; // 4800 bit/s
|
|
|
|
break;
|
|
case 5:
|
|
p[0] = 72; // 9600 bit/s
|
|
|
|
break;
|
|
case 6:
|
|
p[0] = 73; // 14400 bit/s
|
|
|
|
break;
|
|
case 7:
|
|
p[0] = 75; // 19200 bit/s
|
|
|
|
break;
|
|
}
|
|
return p + 3;
|
|
}
|
|
|
|
static u_char
|
|
EncodeSyncParams(u_char si2, u_char ai)
|
|
{
|
|
|
|
switch (si2) {
|
|
case 0:
|
|
return ai + 2; // 1200 bit/s
|
|
|
|
case 1:
|
|
return ai + 24; // 1200/75 bit/s
|
|
|
|
case 2:
|
|
return ai + 23; // 75/1200 bit/s
|
|
|
|
case 3:
|
|
return ai + 3; // 2400 bit/s
|
|
|
|
case 4:
|
|
return ai + 5; // 4800 bit/s
|
|
|
|
case 5:
|
|
return ai + 8; // 9600 bit/s
|
|
|
|
case 6:
|
|
return ai + 9; // 14400 bit/s
|
|
|
|
case 7:
|
|
return ai + 11; // 19200 bit/s
|
|
|
|
case 8:
|
|
return ai + 14; // 48000 bit/s
|
|
|
|
case 9:
|
|
return ai + 15; // 56000 bit/s
|
|
|
|
case 15:
|
|
return ai + 40; // negotiate bit/s
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return ai;
|
|
}
|
|
|
|
|
|
static u_char
|
|
DecodeASyncParams(u_char si2, u_char * p)
|
|
{
|
|
u_char info;
|
|
|
|
switch (p[5]) {
|
|
case 66: // 1200 bit/s
|
|
|
|
break; // si2 don't change
|
|
|
|
case 88: // 1200/75 bit/s
|
|
|
|
si2 += 1;
|
|
break;
|
|
case 87: // 75/1200 bit/s
|
|
|
|
si2 += 2;
|
|
break;
|
|
case 67: // 2400 bit/s
|
|
|
|
si2 += 3;
|
|
break;
|
|
case 69: // 4800 bit/s
|
|
|
|
si2 += 4;
|
|
break;
|
|
case 72: // 9600 bit/s
|
|
|
|
si2 += 5;
|
|
break;
|
|
case 73: // 14400 bit/s
|
|
|
|
si2 += 6;
|
|
break;
|
|
case 75: // 19200 bit/s
|
|
|
|
si2 += 7;
|
|
break;
|
|
}
|
|
|
|
info = p[7] & 0x7f;
|
|
if ((info & 16) && (!(info & 8))) // 7 data bits
|
|
|
|
si2 += 32; // else 8 data bits
|
|
|
|
if ((info & 96) == 96) // 2 stop bits
|
|
|
|
si2 += 16; // else 1 stop bit
|
|
|
|
if ((info & 2) && (!(info & 1))) // even parity
|
|
|
|
si2 += 8; // else no parity
|
|
|
|
return si2;
|
|
}
|
|
|
|
|
|
static u_char
|
|
DecodeSyncParams(u_char si2, u_char info)
|
|
{
|
|
info &= 0x7f;
|
|
switch (info) {
|
|
case 40: // bit/s negotiation failed ai := 165 not 175!
|
|
|
|
return si2 + 15;
|
|
case 15: // 56000 bit/s failed, ai := 0 not 169 !
|
|
|
|
return si2 + 9;
|
|
case 14: // 48000 bit/s
|
|
|
|
return si2 + 8;
|
|
case 11: // 19200 bit/s
|
|
|
|
return si2 + 7;
|
|
case 9: // 14400 bit/s
|
|
|
|
return si2 + 6;
|
|
case 8: // 9600 bit/s
|
|
|
|
return si2 + 5;
|
|
case 5: // 4800 bit/s
|
|
|
|
return si2 + 4;
|
|
case 3: // 2400 bit/s
|
|
|
|
return si2 + 3;
|
|
case 23: // 75/1200 bit/s
|
|
|
|
return si2 + 2;
|
|
case 24: // 1200/75 bit/s
|
|
|
|
return si2 + 1;
|
|
default: // 1200 bit/s
|
|
|
|
return si2;
|
|
}
|
|
}
|
|
|
|
static u_char
|
|
DecodeSI2(struct sk_buff *skb)
|
|
{
|
|
u_char *p; //, *pend=skb->data + skb->len;
|
|
|
|
if ((p = findie(skb->data, skb->len, 0x7c, 0))) {
|
|
switch (p[4] & 0x0f) {
|
|
case 0x01:
|
|
if (p[1] == 0x04) // sync. Bitratenadaption
|
|
|
|
return DecodeSyncParams(160, p[5]); // V.110/X.30
|
|
|
|
else if (p[1] == 0x06) // async. Bitratenadaption
|
|
|
|
return DecodeASyncParams(192, p); // V.110/X.30
|
|
|
|
break;
|
|
case 0x08: // if (p[5] == 0x02) // sync. Bitratenadaption
|
|
if (p[1] > 3)
|
|
return DecodeSyncParams(176, p[5]); // V.120
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
static void
|
|
l3dss1_setup_req(struct l3_process *pc, u_char pr,
|
|
void *arg)
|
|
{
|
|
struct sk_buff *skb;
|
|
u_char tmp[128];
|
|
u_char *p = tmp;
|
|
u_char channel = 0;
|
|
|
|
u_char send_keypad;
|
|
u_char screen = 0x80;
|
|
u_char *teln;
|
|
u_char *msn;
|
|
u_char *sub;
|
|
u_char *sp;
|
|
int l;
|
|
|
|
MsgHead(p, pc->callref, MT_SETUP);
|
|
|
|
teln = pc->para.setup.phone;
|
|
#ifndef CONFIG_HISAX_NO_KEYPAD
|
|
send_keypad = (strchr(teln,'*') || strchr(teln,'#')) ? 1 : 0;
|
|
#else
|
|
send_keypad = 0;
|
|
#endif
|
|
#ifndef CONFIG_HISAX_NO_SENDCOMPLETE
|
|
if (!send_keypad)
|
|
*p++ = 0xa1; /* complete indicator */
|
|
#endif
|
|
/*
|
|
* Set Bearer Capability, Map info from 1TR6-convention to EDSS1
|
|
*/
|
|
switch (pc->para.setup.si1) {
|
|
case 1: /* Telephony */
|
|
*p++ = IE_BEARER;
|
|
*p++ = 0x3; /* Length */
|
|
*p++ = 0x90; /* Coding Std. CCITT, 3.1 kHz audio */
|
|
*p++ = 0x90; /* Circuit-Mode 64kbps */
|
|
*p++ = 0xa3; /* A-Law Audio */
|
|
break;
|
|
case 5: /* Datatransmission 64k, BTX */
|
|
case 7: /* Datatransmission 64k */
|
|
default:
|
|
*p++ = IE_BEARER;
|
|
*p++ = 0x2; /* Length */
|
|
*p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inform. */
|
|
*p++ = 0x90; /* Circuit-Mode 64kbps */
|
|
break;
|
|
}
|
|
|
|
if (send_keypad) {
|
|
*p++ = IE_KEYPAD;
|
|
*p++ = strlen(teln);
|
|
while (*teln)
|
|
*p++ = (*teln++) & 0x7F;
|
|
}
|
|
|
|
/*
|
|
* What about info2? Mapping to High-Layer-Compatibility?
|
|
*/
|
|
if ((*teln) && (!send_keypad)) {
|
|
/* parse number for special things */
|
|
if (!isdigit(*teln)) {
|
|
switch (0x5f & *teln) {
|
|
case 'C':
|
|
channel = 0x08;
|
|
case 'P':
|
|
channel |= 0x80;
|
|
teln++;
|
|
if (*teln == '1')
|
|
channel |= 0x01;
|
|
else
|
|
channel |= 0x02;
|
|
break;
|
|
case 'R':
|
|
screen = 0xA0;
|
|
break;
|
|
case 'D':
|
|
screen = 0x80;
|
|
break;
|
|
|
|
default:
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "Wrong MSN Code");
|
|
break;
|
|
}
|
|
teln++;
|
|
}
|
|
}
|
|
if (channel) {
|
|
*p++ = IE_CHANNEL_ID;
|
|
*p++ = 1;
|
|
*p++ = channel;
|
|
}
|
|
msn = pc->para.setup.eazmsn;
|
|
sub = NULL;
|
|
sp = msn;
|
|
while (*sp) {
|
|
if ('.' == *sp) {
|
|
sub = sp;
|
|
*sp = 0;
|
|
} else
|
|
sp++;
|
|
}
|
|
if (*msn) {
|
|
*p++ = IE_CALLING_PN;
|
|
*p++ = strlen(msn) + (screen ? 2 : 1);
|
|
/* Classify as AnyPref. */
|
|
if (screen) {
|
|
*p++ = 0x01; /* Ext = '0'B, Type = '000'B, Plan = '0001'B. */
|
|
*p++ = screen;
|
|
} else
|
|
*p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
|
|
while (*msn)
|
|
*p++ = *msn++ & 0x7f;
|
|
}
|
|
if (sub) {
|
|
*sub++ = '.';
|
|
*p++ = IE_CALLING_SUB;
|
|
*p++ = strlen(sub) + 2;
|
|
*p++ = 0x80; /* NSAP coded */
|
|
*p++ = 0x50; /* local IDI format */
|
|
while (*sub)
|
|
*p++ = *sub++ & 0x7f;
|
|
}
|
|
sub = NULL;
|
|
sp = teln;
|
|
while (*sp) {
|
|
if ('.' == *sp) {
|
|
sub = sp;
|
|
*sp = 0;
|
|
} else
|
|
sp++;
|
|
}
|
|
|
|
if (!send_keypad) {
|
|
*p++ = IE_CALLED_PN;
|
|
*p++ = strlen(teln) + 1;
|
|
/* Classify as AnyPref. */
|
|
*p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
|
|
while (*teln)
|
|
*p++ = *teln++ & 0x7f;
|
|
|
|
if (sub) {
|
|
*sub++ = '.';
|
|
*p++ = IE_CALLED_SUB;
|
|
*p++ = strlen(sub) + 2;
|
|
*p++ = 0x80; /* NSAP coded */
|
|
*p++ = 0x50; /* local IDI format */
|
|
while (*sub)
|
|
*p++ = *sub++ & 0x7f;
|
|
}
|
|
}
|
|
#ifdef EXT_BEARER_CAPS
|
|
if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) { // sync. Bitratenadaption, V.110/X.30
|
|
|
|
*p++ = IE_LLC;
|
|
*p++ = 0x04;
|
|
*p++ = 0x88;
|
|
*p++ = 0x90;
|
|
*p++ = 0x21;
|
|
*p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80);
|
|
} else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) { // sync. Bitratenadaption, V.120
|
|
|
|
*p++ = IE_LLC;
|
|
*p++ = 0x05;
|
|
*p++ = 0x88;
|
|
*p++ = 0x90;
|
|
*p++ = 0x28;
|
|
*p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0);
|
|
*p++ = 0x82;
|
|
} else if (pc->para.setup.si2 >= 192) { // async. Bitratenadaption, V.110/X.30
|
|
|
|
*p++ = IE_LLC;
|
|
*p++ = 0x06;
|
|
*p++ = 0x88;
|
|
*p++ = 0x90;
|
|
*p++ = 0x21;
|
|
p = EncodeASyncParams(p, pc->para.setup.si2 - 192);
|
|
#ifndef CONFIG_HISAX_NO_LLC
|
|
} else {
|
|
switch (pc->para.setup.si1) {
|
|
case 1: /* Telephony */
|
|
*p++ = IE_LLC;
|
|
*p++ = 0x3; /* Length */
|
|
*p++ = 0x90; /* Coding Std. CCITT, 3.1 kHz audio */
|
|
*p++ = 0x90; /* Circuit-Mode 64kbps */
|
|
*p++ = 0xa3; /* A-Law Audio */
|
|
break;
|
|
case 5: /* Datatransmission 64k, BTX */
|
|
case 7: /* Datatransmission 64k */
|
|
default:
|
|
*p++ = IE_LLC;
|
|
*p++ = 0x2; /* Length */
|
|
*p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inform. */
|
|
*p++ = 0x90; /* Circuit-Mode 64kbps */
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
l = p - tmp;
|
|
if (!(skb = l3_alloc_skb(l)))
|
|
return;
|
|
memcpy(skb_put(skb, l), tmp, l);
|
|
L3DelTimer(&pc->timer);
|
|
L3AddTimer(&pc->timer, T303, CC_T303);
|
|
newl3state(pc, 1);
|
|
l3_msg(pc->st, DL_DATA | REQUEST, skb);
|
|
}
|
|
|
|
static void
|
|
l3dss1_call_proc(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb = arg;
|
|
int id, ret;
|
|
|
|
if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) {
|
|
if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "setup answer with wrong chid %x", id);
|
|
pc->para.cause = 100;
|
|
l3dss1_status_send(pc, pr, NULL);
|
|
return;
|
|
}
|
|
pc->para.bchannel = id;
|
|
} else if (1 == pc->state) {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "setup answer wrong chid (ret %d)", id);
|
|
if (id == -1)
|
|
pc->para.cause = 96;
|
|
else
|
|
pc->para.cause = 100;
|
|
l3dss1_status_send(pc, pr, NULL);
|
|
return;
|
|
}
|
|
/* Now we are on none mandatory IEs */
|
|
ret = check_infoelements(pc, skb, ie_CALL_PROCEEDING);
|
|
if (ERR_IE_COMPREHENSION == ret) {
|
|
l3dss1_std_ie_err(pc, ret);
|
|
return;
|
|
}
|
|
L3DelTimer(&pc->timer);
|
|
newl3state(pc, 3);
|
|
L3AddTimer(&pc->timer, T310, CC_T310);
|
|
if (ret) /* STATUS for none mandatory IE errors after actions are taken */
|
|
l3dss1_std_ie_err(pc, ret);
|
|
pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_setup_ack(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb = arg;
|
|
int id, ret;
|
|
|
|
if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) {
|
|
if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "setup answer with wrong chid %x", id);
|
|
pc->para.cause = 100;
|
|
l3dss1_status_send(pc, pr, NULL);
|
|
return;
|
|
}
|
|
pc->para.bchannel = id;
|
|
} else {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "setup answer wrong chid (ret %d)", id);
|
|
if (id == -1)
|
|
pc->para.cause = 96;
|
|
else
|
|
pc->para.cause = 100;
|
|
l3dss1_status_send(pc, pr, NULL);
|
|
return;
|
|
}
|
|
/* Now we are on none mandatory IEs */
|
|
ret = check_infoelements(pc, skb, ie_SETUP_ACKNOWLEDGE);
|
|
if (ERR_IE_COMPREHENSION == ret) {
|
|
l3dss1_std_ie_err(pc, ret);
|
|
return;
|
|
}
|
|
L3DelTimer(&pc->timer);
|
|
newl3state(pc, 2);
|
|
L3AddTimer(&pc->timer, T304, CC_T304);
|
|
if (ret) /* STATUS for none mandatory IE errors after actions are taken */
|
|
l3dss1_std_ie_err(pc, ret);
|
|
pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_disconnect(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb = arg;
|
|
u_char *p;
|
|
int ret;
|
|
u_char cause = 0;
|
|
|
|
StopAllL3Timer(pc);
|
|
if ((ret = l3dss1_get_cause(pc, skb))) {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "DISC get_cause ret(%d)", ret);
|
|
if (ret < 0)
|
|
cause = 96;
|
|
else if (ret > 0)
|
|
cause = 100;
|
|
}
|
|
if ((p = findie(skb->data, skb->len, IE_FACILITY, 0)))
|
|
l3dss1_parse_facility(pc->st, pc, pc->callref, p);
|
|
ret = check_infoelements(pc, skb, ie_DISCONNECT);
|
|
if (ERR_IE_COMPREHENSION == ret)
|
|
cause = 96;
|
|
else if ((!cause) && (ERR_IE_UNRECOGNIZED == ret))
|
|
cause = 99;
|
|
ret = pc->state;
|
|
newl3state(pc, 12);
|
|
if (cause)
|
|
newl3state(pc, 19);
|
|
if (11 != ret)
|
|
pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
|
|
else if (!cause)
|
|
l3dss1_release_req(pc, pr, NULL);
|
|
if (cause) {
|
|
l3dss1_message_cause(pc, MT_RELEASE, cause);
|
|
L3AddTimer(&pc->timer, T308, CC_T308_1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
l3dss1_connect(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb = arg;
|
|
int ret;
|
|
|
|
ret = check_infoelements(pc, skb, ie_CONNECT);
|
|
if (ERR_IE_COMPREHENSION == ret) {
|
|
l3dss1_std_ie_err(pc, ret);
|
|
return;
|
|
}
|
|
L3DelTimer(&pc->timer); /* T310 */
|
|
newl3state(pc, 10);
|
|
pc->para.chargeinfo = 0;
|
|
/* here should inserted COLP handling KKe */
|
|
if (ret)
|
|
l3dss1_std_ie_err(pc, ret);
|
|
pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_alerting(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb = arg;
|
|
int ret;
|
|
|
|
ret = check_infoelements(pc, skb, ie_ALERTING);
|
|
if (ERR_IE_COMPREHENSION == ret) {
|
|
l3dss1_std_ie_err(pc, ret);
|
|
return;
|
|
}
|
|
L3DelTimer(&pc->timer); /* T304 */
|
|
newl3state(pc, 4);
|
|
if (ret)
|
|
l3dss1_std_ie_err(pc, ret);
|
|
pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_setup(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
u_char *p;
|
|
int bcfound = 0;
|
|
char tmp[80];
|
|
struct sk_buff *skb = arg;
|
|
int id;
|
|
int err = 0;
|
|
|
|
/*
|
|
* Bearer Capabilities
|
|
*/
|
|
p = skb->data;
|
|
/* only the first occurence 'll be detected ! */
|
|
if ((p = findie(p, skb->len, 0x04, 0))) {
|
|
if ((p[1] < 2) || (p[1] > 11))
|
|
err = 1;
|
|
else {
|
|
pc->para.setup.si2 = 0;
|
|
switch (p[2] & 0x7f) {
|
|
case 0x00: /* Speech */
|
|
case 0x10: /* 3.1 Khz audio */
|
|
pc->para.setup.si1 = 1;
|
|
break;
|
|
case 0x08: /* Unrestricted digital information */
|
|
pc->para.setup.si1 = 7;
|
|
/* JIM, 05.11.97 I wanna set service indicator 2 */
|
|
#ifdef EXT_BEARER_CAPS
|
|
pc->para.setup.si2 = DecodeSI2(skb);
|
|
#endif
|
|
break;
|
|
case 0x09: /* Restricted digital information */
|
|
pc->para.setup.si1 = 2;
|
|
break;
|
|
case 0x11:
|
|
/* Unrestr. digital information with
|
|
* tones/announcements ( or 7 kHz audio
|
|
*/
|
|
pc->para.setup.si1 = 3;
|
|
break;
|
|
case 0x18: /* Video */
|
|
pc->para.setup.si1 = 4;
|
|
break;
|
|
default:
|
|
err = 2;
|
|
break;
|
|
}
|
|
switch (p[3] & 0x7f) {
|
|
case 0x40: /* packed mode */
|
|
pc->para.setup.si1 = 8;
|
|
break;
|
|
case 0x10: /* 64 kbit */
|
|
case 0x11: /* 2*64 kbit */
|
|
case 0x13: /* 384 kbit */
|
|
case 0x15: /* 1536 kbit */
|
|
case 0x17: /* 1920 kbit */
|
|
pc->para.moderate = p[3] & 0x7f;
|
|
break;
|
|
default:
|
|
err = 3;
|
|
break;
|
|
}
|
|
}
|
|
if (pc->debug & L3_DEB_SI)
|
|
l3_debug(pc->st, "SI=%d, AI=%d",
|
|
pc->para.setup.si1, pc->para.setup.si2);
|
|
if (err) {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "setup with wrong bearer(l=%d:%x,%x)",
|
|
p[1], p[2], p[3]);
|
|
pc->para.cause = 100;
|
|
l3dss1_msg_without_setup(pc, pr, NULL);
|
|
return;
|
|
}
|
|
} else {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "setup without bearer capabilities");
|
|
/* ETS 300-104 1.3.3 */
|
|
pc->para.cause = 96;
|
|
l3dss1_msg_without_setup(pc, pr, NULL);
|
|
return;
|
|
}
|
|
/*
|
|
* Channel Identification
|
|
*/
|
|
if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) {
|
|
if ((pc->para.bchannel = id)) {
|
|
if ((3 == id) && (0x10 == pc->para.moderate)) {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "setup with wrong chid %x",
|
|
id);
|
|
pc->para.cause = 100;
|
|
l3dss1_msg_without_setup(pc, pr, NULL);
|
|
return;
|
|
}
|
|
bcfound++;
|
|
} else
|
|
{ if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "setup without bchannel, call waiting");
|
|
bcfound++;
|
|
}
|
|
} else {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "setup with wrong chid ret %d", id);
|
|
if (id == -1)
|
|
pc->para.cause = 96;
|
|
else
|
|
pc->para.cause = 100;
|
|
l3dss1_msg_without_setup(pc, pr, NULL);
|
|
return;
|
|
}
|
|
/* Now we are on none mandatory IEs */
|
|
err = check_infoelements(pc, skb, ie_SETUP);
|
|
if (ERR_IE_COMPREHENSION == err) {
|
|
pc->para.cause = 96;
|
|
l3dss1_msg_without_setup(pc, pr, NULL);
|
|
return;
|
|
}
|
|
p = skb->data;
|
|
if ((p = findie(p, skb->len, 0x70, 0)))
|
|
iecpy(pc->para.setup.eazmsn, p, 1);
|
|
else
|
|
pc->para.setup.eazmsn[0] = 0;
|
|
|
|
p = skb->data;
|
|
if ((p = findie(p, skb->len, 0x71, 0))) {
|
|
/* Called party subaddress */
|
|
if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
|
|
tmp[0] = '.';
|
|
iecpy(&tmp[1], p, 2);
|
|
strcat(pc->para.setup.eazmsn, tmp);
|
|
} else if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "wrong called subaddress");
|
|
}
|
|
p = skb->data;
|
|
if ((p = findie(p, skb->len, 0x6c, 0))) {
|
|
pc->para.setup.plan = p[2];
|
|
if (p[2] & 0x80) {
|
|
iecpy(pc->para.setup.phone, p, 1);
|
|
pc->para.setup.screen = 0;
|
|
} else {
|
|
iecpy(pc->para.setup.phone, p, 2);
|
|
pc->para.setup.screen = p[3];
|
|
}
|
|
} else {
|
|
pc->para.setup.phone[0] = 0;
|
|
pc->para.setup.plan = 0;
|
|
pc->para.setup.screen = 0;
|
|
}
|
|
p = skb->data;
|
|
if ((p = findie(p, skb->len, 0x6d, 0))) {
|
|
/* Calling party subaddress */
|
|
if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
|
|
tmp[0] = '.';
|
|
iecpy(&tmp[1], p, 2);
|
|
strcat(pc->para.setup.phone, tmp);
|
|
} else if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "wrong calling subaddress");
|
|
}
|
|
newl3state(pc, 6);
|
|
if (err) /* STATUS for none mandatory IE errors after actions are taken */
|
|
l3dss1_std_ie_err(pc, err);
|
|
pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_reset(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
dss1_release_l3_process(pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_disconnect_req(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb;
|
|
u_char tmp[16+40];
|
|
u_char *p = tmp;
|
|
int l;
|
|
u_char cause = 16;
|
|
|
|
if (pc->para.cause != NO_CAUSE)
|
|
cause = pc->para.cause;
|
|
|
|
StopAllL3Timer(pc);
|
|
|
|
MsgHead(p, pc->callref, MT_DISCONNECT);
|
|
|
|
*p++ = IE_CAUSE;
|
|
*p++ = 0x2;
|
|
*p++ = 0x80;
|
|
*p++ = cause | 0x80;
|
|
|
|
if (pc->prot.dss1.uus1_data[0])
|
|
{ *p++ = IE_USER_USER; /* UUS info element */
|
|
*p++ = strlen(pc->prot.dss1.uus1_data) + 1;
|
|
*p++ = 0x04; /* IA5 chars */
|
|
strcpy(p,pc->prot.dss1.uus1_data);
|
|
p += strlen(pc->prot.dss1.uus1_data);
|
|
pc->prot.dss1.uus1_data[0] = '\0';
|
|
}
|
|
|
|
l = p - tmp;
|
|
if (!(skb = l3_alloc_skb(l)))
|
|
return;
|
|
memcpy(skb_put(skb, l), tmp, l);
|
|
newl3state(pc, 11);
|
|
l3_msg(pc->st, DL_DATA | REQUEST, skb);
|
|
L3AddTimer(&pc->timer, T305, CC_T305);
|
|
}
|
|
|
|
static void
|
|
l3dss1_setup_rsp(struct l3_process *pc, u_char pr,
|
|
void *arg)
|
|
{
|
|
if (!pc->para.bchannel)
|
|
{ if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "D-chan connect for waiting call");
|
|
l3dss1_disconnect_req(pc, pr, arg);
|
|
return;
|
|
}
|
|
newl3state(pc, 8);
|
|
l3dss1_message(pc, MT_CONNECT);
|
|
L3DelTimer(&pc->timer);
|
|
L3AddTimer(&pc->timer, T313, CC_T313);
|
|
}
|
|
|
|
static void
|
|
l3dss1_connect_ack(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb = arg;
|
|
int ret;
|
|
|
|
ret = check_infoelements(pc, skb, ie_CONNECT_ACKNOWLEDGE);
|
|
if (ERR_IE_COMPREHENSION == ret) {
|
|
l3dss1_std_ie_err(pc, ret);
|
|
return;
|
|
}
|
|
newl3state(pc, 10);
|
|
L3DelTimer(&pc->timer);
|
|
if (ret)
|
|
l3dss1_std_ie_err(pc, ret);
|
|
pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb;
|
|
u_char tmp[16];
|
|
u_char *p = tmp;
|
|
int l;
|
|
u_char cause = 21;
|
|
|
|
if (pc->para.cause != NO_CAUSE)
|
|
cause = pc->para.cause;
|
|
|
|
MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
|
|
|
|
*p++ = IE_CAUSE;
|
|
*p++ = 0x2;
|
|
*p++ = 0x80;
|
|
*p++ = cause | 0x80;
|
|
|
|
l = p - tmp;
|
|
if (!(skb = l3_alloc_skb(l)))
|
|
return;
|
|
memcpy(skb_put(skb, l), tmp, l);
|
|
l3_msg(pc->st, DL_DATA | REQUEST, skb);
|
|
pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
|
|
newl3state(pc, 0);
|
|
dss1_release_l3_process(pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_release(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb = arg;
|
|
u_char *p;
|
|
int ret, cause=0;
|
|
|
|
StopAllL3Timer(pc);
|
|
if ((ret = l3dss1_get_cause(pc, skb))>0) {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "REL get_cause ret(%d)", ret);
|
|
} else if (ret<0)
|
|
pc->para.cause = NO_CAUSE;
|
|
if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) {
|
|
l3dss1_parse_facility(pc->st, pc, pc->callref, p);
|
|
}
|
|
if ((ret<0) && (pc->state != 11))
|
|
cause = 96;
|
|
else if (ret>0)
|
|
cause = 100;
|
|
ret = check_infoelements(pc, skb, ie_RELEASE);
|
|
if (ERR_IE_COMPREHENSION == ret)
|
|
cause = 96;
|
|
else if ((ERR_IE_UNRECOGNIZED == ret) && (!cause))
|
|
cause = 99;
|
|
if (cause)
|
|
l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause);
|
|
else
|
|
l3dss1_message(pc, MT_RELEASE_COMPLETE);
|
|
pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
|
|
newl3state(pc, 0);
|
|
dss1_release_l3_process(pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_alert_req(struct l3_process *pc, u_char pr,
|
|
void *arg)
|
|
{
|
|
newl3state(pc, 7);
|
|
if (!pc->prot.dss1.uus1_data[0])
|
|
l3dss1_message(pc, MT_ALERTING);
|
|
else
|
|
l3dss1_msg_with_uus(pc, MT_ALERTING);
|
|
}
|
|
|
|
static void
|
|
l3dss1_proceed_req(struct l3_process *pc, u_char pr,
|
|
void *arg)
|
|
{
|
|
newl3state(pc, 9);
|
|
l3dss1_message(pc, MT_CALL_PROCEEDING);
|
|
pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_setup_ack_req(struct l3_process *pc, u_char pr,
|
|
void *arg)
|
|
{
|
|
newl3state(pc, 25);
|
|
L3DelTimer(&pc->timer);
|
|
L3AddTimer(&pc->timer, T302, CC_T302);
|
|
l3dss1_message(pc, MT_SETUP_ACKNOWLEDGE);
|
|
}
|
|
|
|
/********************************************/
|
|
/* deliver a incoming display message to HL */
|
|
/********************************************/
|
|
static void
|
|
l3dss1_deliver_display(struct l3_process *pc, int pr, u_char *infp)
|
|
{ u_char len;
|
|
isdn_ctrl ic;
|
|
struct IsdnCardState *cs;
|
|
char *p;
|
|
|
|
if (*infp++ != IE_DISPLAY) return;
|
|
if ((len = *infp++) > 80) return; /* total length <= 82 */
|
|
if (!pc->chan) return;
|
|
|
|
p = ic.parm.display;
|
|
while (len--)
|
|
*p++ = *infp++;
|
|
*p = '\0';
|
|
ic.command = ISDN_STAT_DISPLAY;
|
|
cs = pc->st->l1.hardware;
|
|
ic.driver = cs->myid;
|
|
ic.arg = pc->chan->chan;
|
|
cs->iif.statcallb(&ic);
|
|
} /* l3dss1_deliver_display */
|
|
|
|
|
|
static void
|
|
l3dss1_progress(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb = arg;
|
|
int err = 0;
|
|
u_char *p;
|
|
|
|
if ((p = findie(skb->data, skb->len, IE_PROGRESS, 0))) {
|
|
if (p[1] != 2) {
|
|
err = 1;
|
|
pc->para.cause = 100;
|
|
} else if (!(p[2] & 0x70)) {
|
|
switch (p[2]) {
|
|
case 0x80:
|
|
case 0x81:
|
|
case 0x82:
|
|
case 0x84:
|
|
case 0x85:
|
|
case 0x87:
|
|
case 0x8a:
|
|
switch (p[3]) {
|
|
case 0x81:
|
|
case 0x82:
|
|
case 0x83:
|
|
case 0x84:
|
|
case 0x88:
|
|
break;
|
|
default:
|
|
err = 2;
|
|
pc->para.cause = 100;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
err = 3;
|
|
pc->para.cause = 100;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
pc->para.cause = 96;
|
|
err = 4;
|
|
}
|
|
if (err) {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "progress error %d", err);
|
|
l3dss1_status_send(pc, pr, NULL);
|
|
return;
|
|
}
|
|
/* Now we are on none mandatory IEs */
|
|
err = check_infoelements(pc, skb, ie_PROGRESS);
|
|
if (err)
|
|
l3dss1_std_ie_err(pc, err);
|
|
if (ERR_IE_COMPREHENSION != err)
|
|
pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_notify(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb = arg;
|
|
int err = 0;
|
|
u_char *p;
|
|
|
|
if ((p = findie(skb->data, skb->len, IE_NOTIFY, 0))) {
|
|
if (p[1] != 1) {
|
|
err = 1;
|
|
pc->para.cause = 100;
|
|
} else {
|
|
switch (p[2]) {
|
|
case 0x80:
|
|
case 0x81:
|
|
case 0x82:
|
|
break;
|
|
default:
|
|
pc->para.cause = 100;
|
|
err = 2;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
pc->para.cause = 96;
|
|
err = 3;
|
|
}
|
|
if (err) {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "notify error %d", err);
|
|
l3dss1_status_send(pc, pr, NULL);
|
|
return;
|
|
}
|
|
/* Now we are on none mandatory IEs */
|
|
err = check_infoelements(pc, skb, ie_NOTIFY);
|
|
if (err)
|
|
l3dss1_std_ie_err(pc, err);
|
|
if (ERR_IE_COMPREHENSION != err)
|
|
pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_status_enq(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
int ret;
|
|
struct sk_buff *skb = arg;
|
|
|
|
ret = check_infoelements(pc, skb, ie_STATUS_ENQUIRY);
|
|
l3dss1_std_ie_err(pc, ret);
|
|
pc->para.cause = 30; /* response to STATUS_ENQUIRY */
|
|
l3dss1_status_send(pc, pr, NULL);
|
|
}
|
|
|
|
static void
|
|
l3dss1_information(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
int ret;
|
|
struct sk_buff *skb = arg;
|
|
u_char *p;
|
|
char tmp[32];
|
|
|
|
ret = check_infoelements(pc, skb, ie_INFORMATION);
|
|
if (ret)
|
|
l3dss1_std_ie_err(pc, ret);
|
|
if (pc->state == 25) { /* overlap receiving */
|
|
L3DelTimer(&pc->timer);
|
|
p = skb->data;
|
|
if ((p = findie(p, skb->len, 0x70, 0))) {
|
|
iecpy(tmp, p, 1);
|
|
strcat(pc->para.setup.eazmsn, tmp);
|
|
pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
|
|
}
|
|
L3AddTimer(&pc->timer, T302, CC_T302);
|
|
}
|
|
}
|
|
|
|
/******************************/
|
|
/* handle deflection requests */
|
|
/******************************/
|
|
static void l3dss1_redir_req(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb;
|
|
u_char tmp[128];
|
|
u_char *p = tmp;
|
|
u_char *subp;
|
|
u_char len_phone = 0;
|
|
u_char len_sub = 0;
|
|
int l;
|
|
|
|
|
|
strcpy(pc->prot.dss1.uus1_data,pc->chan->setup.eazmsn); /* copy uus element if available */
|
|
if (!pc->chan->setup.phone[0])
|
|
{ pc->para.cause = -1;
|
|
l3dss1_disconnect_req(pc,pr,arg); /* disconnect immediately */
|
|
return;
|
|
} /* only uus */
|
|
|
|
if (pc->prot.dss1.invoke_id)
|
|
free_invoke_id(pc->st,pc->prot.dss1.invoke_id);
|
|
|
|
if (!(pc->prot.dss1.invoke_id = new_invoke_id(pc->st)))
|
|
return;
|
|
|
|
MsgHead(p, pc->callref, MT_FACILITY);
|
|
|
|
for (subp = pc->chan->setup.phone; (*subp) && (*subp != '.'); subp++) len_phone++; /* len of phone number */
|
|
if (*subp++ == '.') len_sub = strlen(subp) + 2; /* length including info subaddress element */
|
|
|
|
*p++ = 0x1c; /* Facility info element */
|
|
*p++ = len_phone + len_sub + 2 + 2 + 8 + 3 + 3; /* length of element */
|
|
*p++ = 0x91; /* remote operations protocol */
|
|
*p++ = 0xa1; /* invoke component */
|
|
|
|
*p++ = len_phone + len_sub + 2 + 2 + 8 + 3; /* length of data */
|
|
*p++ = 0x02; /* invoke id tag, integer */
|
|
*p++ = 0x01; /* length */
|
|
*p++ = pc->prot.dss1.invoke_id; /* invoke id */
|
|
*p++ = 0x02; /* operation value tag, integer */
|
|
*p++ = 0x01; /* length */
|
|
*p++ = 0x0D; /* Call Deflect */
|
|
|
|
*p++ = 0x30; /* sequence phone number */
|
|
*p++ = len_phone + 2 + 2 + 3 + len_sub; /* length */
|
|
|
|
*p++ = 0x30; /* Deflected to UserNumber */
|
|
*p++ = len_phone+2+len_sub; /* length */
|
|
*p++ = 0x80; /* NumberDigits */
|
|
*p++ = len_phone; /* length */
|
|
for (l = 0; l < len_phone; l++)
|
|
*p++ = pc->chan->setup.phone[l];
|
|
|
|
if (len_sub)
|
|
{ *p++ = 0x04; /* called party subaddress */
|
|
*p++ = len_sub - 2;
|
|
while (*subp) *p++ = *subp++;
|
|
}
|
|
|
|
*p++ = 0x01; /* screening identifier */
|
|
*p++ = 0x01;
|
|
*p++ = pc->chan->setup.screen;
|
|
|
|
l = p - tmp;
|
|
if (!(skb = l3_alloc_skb(l))) return;
|
|
memcpy(skb_put(skb, l), tmp, l);
|
|
|
|
l3_msg(pc->st, DL_DATA | REQUEST, skb);
|
|
} /* l3dss1_redir_req */
|
|
|
|
/********************************************/
|
|
/* handle deflection request in early state */
|
|
/********************************************/
|
|
static void l3dss1_redir_req_early(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
l3dss1_proceed_req(pc,pr,arg);
|
|
l3dss1_redir_req(pc,pr,arg);
|
|
} /* l3dss1_redir_req_early */
|
|
|
|
/***********************************************/
|
|
/* handle special commands for this protocol. */
|
|
/* Examples are call independant services like */
|
|
/* remote operations with dummy callref. */
|
|
/***********************************************/
|
|
static int l3dss1_cmd_global(struct PStack *st, isdn_ctrl *ic)
|
|
{ u_char id;
|
|
u_char temp[265];
|
|
u_char *p = temp;
|
|
int i, l, proc_len;
|
|
struct sk_buff *skb;
|
|
struct l3_process *pc = NULL;
|
|
|
|
switch (ic->arg)
|
|
{ case DSS1_CMD_INVOKE:
|
|
if (ic->parm.dss1_io.datalen < 0) return(-2); /* invalid parameter */
|
|
|
|
for (proc_len = 1, i = ic->parm.dss1_io.proc >> 8; i; i++)
|
|
i = i >> 8; /* add one byte */
|
|
l = ic->parm.dss1_io.datalen + proc_len + 8; /* length excluding ie header */
|
|
if (l > 255)
|
|
return(-2); /* too long */
|
|
|
|
if (!(id = new_invoke_id(st)))
|
|
return(0); /* first get a invoke id -> return if no available */
|
|
|
|
i = -1;
|
|
MsgHead(p, i, MT_FACILITY); /* build message head */
|
|
*p++ = 0x1C; /* Facility IE */
|
|
*p++ = l; /* length of ie */
|
|
*p++ = 0x91; /* remote operations */
|
|
*p++ = 0xA1; /* invoke */
|
|
*p++ = l - 3; /* length of invoke */
|
|
*p++ = 0x02; /* invoke id tag */
|
|
*p++ = 0x01; /* length is 1 */
|
|
*p++ = id; /* invoke id */
|
|
*p++ = 0x02; /* operation */
|
|
*p++ = proc_len; /* length of operation */
|
|
|
|
for (i = proc_len; i; i--)
|
|
*p++ = (ic->parm.dss1_io.proc >> (i-1)) & 0xFF;
|
|
memcpy(p, ic->parm.dss1_io.data, ic->parm.dss1_io.datalen); /* copy data */
|
|
l = (p - temp) + ic->parm.dss1_io.datalen; /* total length */
|
|
|
|
if (ic->parm.dss1_io.timeout > 0)
|
|
if (!(pc = dss1_new_l3_process(st, -1)))
|
|
{ free_invoke_id(st, id);
|
|
return(-2);
|
|
}
|
|
pc->prot.dss1.ll_id = ic->parm.dss1_io.ll_id; /* remember id */
|
|
pc->prot.dss1.proc = ic->parm.dss1_io.proc; /* and procedure */
|
|
|
|
if (!(skb = l3_alloc_skb(l)))
|
|
{ free_invoke_id(st, id);
|
|
if (pc) dss1_release_l3_process(pc);
|
|
return(-2);
|
|
}
|
|
memcpy(skb_put(skb, l), temp, l);
|
|
|
|
if (pc)
|
|
{ pc->prot.dss1.invoke_id = id; /* remember id */
|
|
L3AddTimer(&pc->timer, ic->parm.dss1_io.timeout, CC_TDSS1_IO | REQUEST);
|
|
}
|
|
|
|
l3_msg(st, DL_DATA | REQUEST, skb);
|
|
ic->parm.dss1_io.hl_id = id; /* return id */
|
|
return(0);
|
|
|
|
case DSS1_CMD_INVOKE_ABORT:
|
|
if ((pc = l3dss1_search_dummy_proc(st, ic->parm.dss1_io.hl_id)))
|
|
{ L3DelTimer(&pc->timer); /* remove timer */
|
|
dss1_release_l3_process(pc);
|
|
return(0);
|
|
}
|
|
else
|
|
{ l3_debug(st, "l3dss1_cmd_global abort unknown id");
|
|
return(-2);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
l3_debug(st, "l3dss1_cmd_global unknown cmd 0x%lx", ic->arg);
|
|
return(-1);
|
|
} /* switch ic-> arg */
|
|
return(-1);
|
|
} /* l3dss1_cmd_global */
|
|
|
|
static void
|
|
l3dss1_io_timer(struct l3_process *pc)
|
|
{ isdn_ctrl ic;
|
|
struct IsdnCardState *cs = pc->st->l1.hardware;
|
|
|
|
L3DelTimer(&pc->timer); /* remove timer */
|
|
|
|
ic.driver = cs->myid;
|
|
ic.command = ISDN_STAT_PROT;
|
|
ic.arg = DSS1_STAT_INVOKE_ERR;
|
|
ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id;
|
|
ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id;
|
|
ic.parm.dss1_io.proc = pc->prot.dss1.proc;
|
|
ic.parm.dss1_io.timeout= -1;
|
|
ic.parm.dss1_io.datalen = 0;
|
|
ic.parm.dss1_io.data = NULL;
|
|
free_invoke_id(pc->st, pc->prot.dss1.invoke_id);
|
|
pc->prot.dss1.invoke_id = 0; /* reset id */
|
|
|
|
cs->iif.statcallb(&ic);
|
|
|
|
dss1_release_l3_process(pc);
|
|
} /* l3dss1_io_timer */
|
|
|
|
static void
|
|
l3dss1_release_ind(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
u_char *p;
|
|
struct sk_buff *skb = arg;
|
|
int callState = 0;
|
|
p = skb->data;
|
|
|
|
if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) {
|
|
p++;
|
|
if (1 == *p++)
|
|
callState = *p;
|
|
}
|
|
if (callState == 0) {
|
|
/* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1
|
|
* set down layer 3 without sending any message
|
|
*/
|
|
pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
|
|
newl3state(pc, 0);
|
|
dss1_release_l3_process(pc);
|
|
} else {
|
|
pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc);
|
|
}
|
|
}
|
|
|
|
static void
|
|
l3dss1_dummy(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
}
|
|
|
|
static void
|
|
l3dss1_t302(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
L3DelTimer(&pc->timer);
|
|
pc->para.loc = 0;
|
|
pc->para.cause = 28; /* invalid number */
|
|
l3dss1_disconnect_req(pc, pr, NULL);
|
|
pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_t303(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
if (pc->N303 > 0) {
|
|
pc->N303--;
|
|
L3DelTimer(&pc->timer);
|
|
l3dss1_setup_req(pc, pr, arg);
|
|
} else {
|
|
L3DelTimer(&pc->timer);
|
|
l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, 102);
|
|
pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc);
|
|
dss1_release_l3_process(pc);
|
|
}
|
|
}
|
|
|
|
static void
|
|
l3dss1_t304(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
L3DelTimer(&pc->timer);
|
|
pc->para.loc = 0;
|
|
pc->para.cause = 102;
|
|
l3dss1_disconnect_req(pc, pr, NULL);
|
|
pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
|
|
|
|
}
|
|
|
|
static void
|
|
l3dss1_t305(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
u_char tmp[16];
|
|
u_char *p = tmp;
|
|
int l;
|
|
struct sk_buff *skb;
|
|
u_char cause = 16;
|
|
|
|
L3DelTimer(&pc->timer);
|
|
if (pc->para.cause != NO_CAUSE)
|
|
cause = pc->para.cause;
|
|
|
|
MsgHead(p, pc->callref, MT_RELEASE);
|
|
|
|
*p++ = IE_CAUSE;
|
|
*p++ = 0x2;
|
|
*p++ = 0x80;
|
|
*p++ = cause | 0x80;
|
|
|
|
l = p - tmp;
|
|
if (!(skb = l3_alloc_skb(l)))
|
|
return;
|
|
memcpy(skb_put(skb, l), tmp, l);
|
|
newl3state(pc, 19);
|
|
l3_msg(pc->st, DL_DATA | REQUEST, skb);
|
|
L3AddTimer(&pc->timer, T308, CC_T308_1);
|
|
}
|
|
|
|
static void
|
|
l3dss1_t310(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
L3DelTimer(&pc->timer);
|
|
pc->para.loc = 0;
|
|
pc->para.cause = 102;
|
|
l3dss1_disconnect_req(pc, pr, NULL);
|
|
pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_t313(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
L3DelTimer(&pc->timer);
|
|
pc->para.loc = 0;
|
|
pc->para.cause = 102;
|
|
l3dss1_disconnect_req(pc, pr, NULL);
|
|
pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_t308_1(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
newl3state(pc, 19);
|
|
L3DelTimer(&pc->timer);
|
|
l3dss1_message(pc, MT_RELEASE);
|
|
L3AddTimer(&pc->timer, T308, CC_T308_2);
|
|
}
|
|
|
|
static void
|
|
l3dss1_t308_2(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
L3DelTimer(&pc->timer);
|
|
pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
|
|
dss1_release_l3_process(pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_t318(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
L3DelTimer(&pc->timer);
|
|
pc->para.cause = 102; /* Timer expiry */
|
|
pc->para.loc = 0; /* local */
|
|
pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
|
|
newl3state(pc, 19);
|
|
l3dss1_message(pc, MT_RELEASE);
|
|
L3AddTimer(&pc->timer, T308, CC_T308_1);
|
|
}
|
|
|
|
static void
|
|
l3dss1_t319(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
L3DelTimer(&pc->timer);
|
|
pc->para.cause = 102; /* Timer expiry */
|
|
pc->para.loc = 0; /* local */
|
|
pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
|
|
newl3state(pc, 10);
|
|
}
|
|
|
|
static void
|
|
l3dss1_restart(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
L3DelTimer(&pc->timer);
|
|
pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
|
|
dss1_release_l3_process(pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_status(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
u_char *p;
|
|
struct sk_buff *skb = arg;
|
|
int ret;
|
|
u_char cause = 0, callState = 0;
|
|
|
|
if ((ret = l3dss1_get_cause(pc, skb))) {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "STATUS get_cause ret(%d)",ret);
|
|
if (ret < 0)
|
|
cause = 96;
|
|
else if (ret > 0)
|
|
cause = 100;
|
|
}
|
|
if ((p = findie(skb->data, skb->len, IE_CALL_STATE, 0))) {
|
|
p++;
|
|
if (1 == *p++) {
|
|
callState = *p;
|
|
if (!ie_in_set(pc, *p, l3_valid_states))
|
|
cause = 100;
|
|
} else
|
|
cause = 100;
|
|
} else
|
|
cause = 96;
|
|
if (!cause) { /* no error before */
|
|
ret = check_infoelements(pc, skb, ie_STATUS);
|
|
if (ERR_IE_COMPREHENSION == ret)
|
|
cause = 96;
|
|
else if (ERR_IE_UNRECOGNIZED == ret)
|
|
cause = 99;
|
|
}
|
|
if (cause) {
|
|
u_char tmp;
|
|
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "STATUS error(%d/%d)",ret,cause);
|
|
tmp = pc->para.cause;
|
|
pc->para.cause = cause;
|
|
l3dss1_status_send(pc, 0, NULL);
|
|
if (cause == 99)
|
|
pc->para.cause = tmp;
|
|
else
|
|
return;
|
|
}
|
|
cause = pc->para.cause;
|
|
if (((cause & 0x7f) == 111) && (callState == 0)) {
|
|
/* ETS 300-104 7.6.1, 8.6.1, 10.6.1...
|
|
* if received MT_STATUS with cause == 111 and call
|
|
* state == 0, then we must set down layer 3
|
|
*/
|
|
pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
|
|
newl3state(pc, 0);
|
|
dss1_release_l3_process(pc);
|
|
}
|
|
}
|
|
|
|
static void
|
|
l3dss1_facility(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb = arg;
|
|
int ret;
|
|
|
|
ret = check_infoelements(pc, skb, ie_FACILITY);
|
|
l3dss1_std_ie_err(pc, ret);
|
|
{
|
|
u_char *p;
|
|
if ((p = findie(skb->data, skb->len, IE_FACILITY, 0)))
|
|
l3dss1_parse_facility(pc->st, pc, pc->callref, p);
|
|
}
|
|
}
|
|
|
|
static void
|
|
l3dss1_suspend_req(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb;
|
|
u_char tmp[32];
|
|
u_char *p = tmp;
|
|
u_char i, l;
|
|
u_char *msg = pc->chan->setup.phone;
|
|
|
|
MsgHead(p, pc->callref, MT_SUSPEND);
|
|
l = *msg++;
|
|
if (l && (l <= 10)) { /* Max length 10 octets */
|
|
*p++ = IE_CALL_ID;
|
|
*p++ = l;
|
|
for (i = 0; i < l; i++)
|
|
*p++ = *msg++;
|
|
} else if (l) {
|
|
l3_debug(pc->st, "SUS wrong CALL_ID len %d", l);
|
|
return;
|
|
}
|
|
l = p - tmp;
|
|
if (!(skb = l3_alloc_skb(l)))
|
|
return;
|
|
memcpy(skb_put(skb, l), tmp, l);
|
|
l3_msg(pc->st, DL_DATA | REQUEST, skb);
|
|
newl3state(pc, 15);
|
|
L3AddTimer(&pc->timer, T319, CC_T319);
|
|
}
|
|
|
|
static void
|
|
l3dss1_suspend_ack(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb = arg;
|
|
int ret;
|
|
|
|
L3DelTimer(&pc->timer);
|
|
newl3state(pc, 0);
|
|
pc->para.cause = NO_CAUSE;
|
|
pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc);
|
|
/* We don't handle suspend_ack for IE errors now */
|
|
if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE)))
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "SUSPACK check ie(%d)",ret);
|
|
dss1_release_l3_process(pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_suspend_rej(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb = arg;
|
|
int ret;
|
|
|
|
if ((ret = l3dss1_get_cause(pc, skb))) {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "SUSP_REJ get_cause ret(%d)",ret);
|
|
if (ret < 0)
|
|
pc->para.cause = 96;
|
|
else
|
|
pc->para.cause = 100;
|
|
l3dss1_status_send(pc, pr, NULL);
|
|
return;
|
|
}
|
|
ret = check_infoelements(pc, skb, ie_SUSPEND_REJECT);
|
|
if (ERR_IE_COMPREHENSION == ret) {
|
|
l3dss1_std_ie_err(pc, ret);
|
|
return;
|
|
}
|
|
L3DelTimer(&pc->timer);
|
|
pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
|
|
newl3state(pc, 10);
|
|
if (ret) /* STATUS for none mandatory IE errors after actions are taken */
|
|
l3dss1_std_ie_err(pc, ret);
|
|
}
|
|
|
|
static void
|
|
l3dss1_resume_req(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb;
|
|
u_char tmp[32];
|
|
u_char *p = tmp;
|
|
u_char i, l;
|
|
u_char *msg = pc->para.setup.phone;
|
|
|
|
MsgHead(p, pc->callref, MT_RESUME);
|
|
|
|
l = *msg++;
|
|
if (l && (l <= 10)) { /* Max length 10 octets */
|
|
*p++ = IE_CALL_ID;
|
|
*p++ = l;
|
|
for (i = 0; i < l; i++)
|
|
*p++ = *msg++;
|
|
} else if (l) {
|
|
l3_debug(pc->st, "RES wrong CALL_ID len %d", l);
|
|
return;
|
|
}
|
|
l = p - tmp;
|
|
if (!(skb = l3_alloc_skb(l)))
|
|
return;
|
|
memcpy(skb_put(skb, l), tmp, l);
|
|
l3_msg(pc->st, DL_DATA | REQUEST, skb);
|
|
newl3state(pc, 17);
|
|
L3AddTimer(&pc->timer, T318, CC_T318);
|
|
}
|
|
|
|
static void
|
|
l3dss1_resume_ack(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb = arg;
|
|
int id, ret;
|
|
|
|
if ((id = l3dss1_get_channel_id(pc, skb)) > 0) {
|
|
if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "resume ack with wrong chid %x", id);
|
|
pc->para.cause = 100;
|
|
l3dss1_status_send(pc, pr, NULL);
|
|
return;
|
|
}
|
|
pc->para.bchannel = id;
|
|
} else if (1 == pc->state) {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "resume ack without chid (ret %d)", id);
|
|
pc->para.cause = 96;
|
|
l3dss1_status_send(pc, pr, NULL);
|
|
return;
|
|
}
|
|
ret = check_infoelements(pc, skb, ie_RESUME_ACKNOWLEDGE);
|
|
if (ERR_IE_COMPREHENSION == ret) {
|
|
l3dss1_std_ie_err(pc, ret);
|
|
return;
|
|
}
|
|
L3DelTimer(&pc->timer);
|
|
pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc);
|
|
newl3state(pc, 10);
|
|
if (ret) /* STATUS for none mandatory IE errors after actions are taken */
|
|
l3dss1_std_ie_err(pc, ret);
|
|
}
|
|
|
|
static void
|
|
l3dss1_resume_rej(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
struct sk_buff *skb = arg;
|
|
int ret;
|
|
|
|
if ((ret = l3dss1_get_cause(pc, skb))) {
|
|
if (pc->debug & L3_DEB_WARN)
|
|
l3_debug(pc->st, "RES_REJ get_cause ret(%d)",ret);
|
|
if (ret < 0)
|
|
pc->para.cause = 96;
|
|
else
|
|
pc->para.cause = 100;
|
|
l3dss1_status_send(pc, pr, NULL);
|
|
return;
|
|
}
|
|
ret = check_infoelements(pc, skb, ie_RESUME_REJECT);
|
|
if (ERR_IE_COMPREHENSION == ret) {
|
|
l3dss1_std_ie_err(pc, ret);
|
|
return;
|
|
}
|
|
L3DelTimer(&pc->timer);
|
|
pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
|
|
newl3state(pc, 0);
|
|
if (ret) /* STATUS for none mandatory IE errors after actions are taken */
|
|
l3dss1_std_ie_err(pc, ret);
|
|
dss1_release_l3_process(pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
u_char tmp[32];
|
|
u_char *p;
|
|
u_char ri, ch = 0, chan = 0;
|
|
int l;
|
|
struct sk_buff *skb = arg;
|
|
struct l3_process *up;
|
|
|
|
newl3state(pc, 2);
|
|
L3DelTimer(&pc->timer);
|
|
p = skb->data;
|
|
if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) {
|
|
ri = p[2];
|
|
l3_debug(pc->st, "Restart %x", ri);
|
|
} else {
|
|
l3_debug(pc->st, "Restart without restart IE");
|
|
ri = 0x86;
|
|
}
|
|
p = skb->data;
|
|
if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
|
|
chan = p[2] & 3;
|
|
ch = p[2];
|
|
if (pc->st->l3.debug)
|
|
l3_debug(pc->st, "Restart for channel %d", chan);
|
|
}
|
|
newl3state(pc, 2);
|
|
up = pc->st->l3.proc;
|
|
while (up) {
|
|
if ((ri & 7) == 7)
|
|
up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
|
|
else if (up->para.bchannel == chan)
|
|
up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
|
|
up = up->next;
|
|
}
|
|
p = tmp;
|
|
MsgHead(p, pc->callref, MT_RESTART_ACKNOWLEDGE);
|
|
if (chan) {
|
|
*p++ = IE_CHANNEL_ID;
|
|
*p++ = 1;
|
|
*p++ = ch | 0x80;
|
|
}
|
|
*p++ = 0x79; /* RESTART Ind */
|
|
*p++ = 1;
|
|
*p++ = ri;
|
|
l = p - tmp;
|
|
if (!(skb = l3_alloc_skb(l)))
|
|
return;
|
|
memcpy(skb_put(skb, l), tmp, l);
|
|
newl3state(pc, 0);
|
|
l3_msg(pc->st, DL_DATA | REQUEST, skb);
|
|
}
|
|
|
|
static void
|
|
l3dss1_dl_reset(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
pc->para.cause = 0x29; /* Temporary failure */
|
|
pc->para.loc = 0;
|
|
l3dss1_disconnect_req(pc, pr, NULL);
|
|
pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_dl_release(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
newl3state(pc, 0);
|
|
pc->para.cause = 0x1b; /* Destination out of order */
|
|
pc->para.loc = 0;
|
|
pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
|
|
release_l3_process(pc);
|
|
}
|
|
|
|
static void
|
|
l3dss1_dl_reestablish(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
L3DelTimer(&pc->timer);
|
|
L3AddTimer(&pc->timer, T309, CC_T309);
|
|
l3_msg(pc->st, DL_ESTABLISH | REQUEST, NULL);
|
|
}
|
|
|
|
static void
|
|
l3dss1_dl_reest_status(struct l3_process *pc, u_char pr, void *arg)
|
|
{
|
|
L3DelTimer(&pc->timer);
|
|
|
|
pc->para.cause = 0x1F; /* normal, unspecified */
|
|
l3dss1_status_send(pc, 0, NULL);
|
|
}
|
|
|
|
/* *INDENT-OFF* */
|
|
static struct stateentry downstatelist[] =
|
|
{
|
|
{SBIT(0),
|
|
CC_SETUP | REQUEST, l3dss1_setup_req},
|
|
{SBIT(0),
|
|
CC_RESUME | REQUEST, l3dss1_resume_req},
|
|
{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(25),
|
|
CC_DISCONNECT | REQUEST, l3dss1_disconnect_req},
|
|
{SBIT(12),
|
|
CC_RELEASE | REQUEST, l3dss1_release_req},
|
|
{ALL_STATES,
|
|
CC_RESTART | REQUEST, l3dss1_restart},
|
|
{SBIT(6) | SBIT(25),
|
|
CC_IGNORE | REQUEST, l3dss1_reset},
|
|
{SBIT(6) | SBIT(25),
|
|
CC_REJECT | REQUEST, l3dss1_reject_req},
|
|
{SBIT(6) | SBIT(25),
|
|
CC_PROCEED_SEND | REQUEST, l3dss1_proceed_req},
|
|
{SBIT(6),
|
|
CC_MORE_INFO | REQUEST, l3dss1_setup_ack_req},
|
|
{SBIT(25),
|
|
CC_MORE_INFO | REQUEST, l3dss1_dummy},
|
|
{SBIT(6) | SBIT(9) | SBIT(25),
|
|
CC_ALERTING | REQUEST, l3dss1_alert_req},
|
|
{SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25),
|
|
CC_SETUP | RESPONSE, l3dss1_setup_rsp},
|
|
{SBIT(10),
|
|
CC_SUSPEND | REQUEST, l3dss1_suspend_req},
|
|
{SBIT(7) | SBIT(9) | SBIT(25),
|
|
CC_REDIR | REQUEST, l3dss1_redir_req},
|
|
{SBIT(6),
|
|
CC_REDIR | REQUEST, l3dss1_redir_req_early},
|
|
{SBIT(9) | SBIT(25),
|
|
CC_DISCONNECT | REQUEST, l3dss1_disconnect_req},
|
|
{SBIT(25),
|
|
CC_T302, l3dss1_t302},
|
|
{SBIT(1),
|
|
CC_T303, l3dss1_t303},
|
|
{SBIT(2),
|
|
CC_T304, l3dss1_t304},
|
|
{SBIT(3),
|
|
CC_T310, l3dss1_t310},
|
|
{SBIT(8),
|
|
CC_T313, l3dss1_t313},
|
|
{SBIT(11),
|
|
CC_T305, l3dss1_t305},
|
|
{SBIT(15),
|
|
CC_T319, l3dss1_t319},
|
|
{SBIT(17),
|
|
CC_T318, l3dss1_t318},
|
|
{SBIT(19),
|
|
CC_T308_1, l3dss1_t308_1},
|
|
{SBIT(19),
|
|
CC_T308_2, l3dss1_t308_2},
|
|
{SBIT(10),
|
|
CC_T309, l3dss1_dl_release},
|
|
};
|
|
|
|
static struct stateentry datastatelist[] =
|
|
{
|
|
{ALL_STATES,
|
|
MT_STATUS_ENQUIRY, l3dss1_status_enq},
|
|
{ALL_STATES,
|
|
MT_FACILITY, l3dss1_facility},
|
|
{SBIT(19),
|
|
MT_STATUS, l3dss1_release_ind},
|
|
{ALL_STATES,
|
|
MT_STATUS, l3dss1_status},
|
|
{SBIT(0),
|
|
MT_SETUP, l3dss1_setup},
|
|
{SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) |
|
|
SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25),
|
|
MT_SETUP, l3dss1_dummy},
|
|
{SBIT(1) | SBIT(2),
|
|
MT_CALL_PROCEEDING, l3dss1_call_proc},
|
|
{SBIT(1),
|
|
MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack},
|
|
{SBIT(2) | SBIT(3),
|
|
MT_ALERTING, l3dss1_alerting},
|
|
{SBIT(2) | SBIT(3),
|
|
MT_PROGRESS, l3dss1_progress},
|
|
{SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) |
|
|
SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25),
|
|
MT_INFORMATION, l3dss1_information},
|
|
{SBIT(10) | SBIT(11) | SBIT(15),
|
|
MT_NOTIFY, l3dss1_notify},
|
|
{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) |
|
|
SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25),
|
|
MT_RELEASE_COMPLETE, l3dss1_release_cmpl},
|
|
{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(25),
|
|
MT_RELEASE, l3dss1_release},
|
|
{SBIT(19), MT_RELEASE, l3dss1_release_ind},
|
|
{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(25),
|
|
MT_DISCONNECT, l3dss1_disconnect},
|
|
{SBIT(19),
|
|
MT_DISCONNECT, l3dss1_dummy},
|
|
{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4),
|
|
MT_CONNECT, l3dss1_connect},
|
|
{SBIT(8),
|
|
MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack},
|
|
{SBIT(15),
|
|
MT_SUSPEND_ACKNOWLEDGE, l3dss1_suspend_ack},
|
|
{SBIT(15),
|
|
MT_SUSPEND_REJECT, l3dss1_suspend_rej},
|
|
{SBIT(17),
|
|
MT_RESUME_ACKNOWLEDGE, l3dss1_resume_ack},
|
|
{SBIT(17),
|
|
MT_RESUME_REJECT, l3dss1_resume_rej},
|
|
};
|
|
|
|
static struct stateentry globalmes_list[] =
|
|
{
|
|
{ALL_STATES,
|
|
MT_STATUS, l3dss1_status},
|
|
{SBIT(0),
|
|
MT_RESTART, l3dss1_global_restart},
|
|
/* {SBIT(1),
|
|
MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack},
|
|
*/
|
|
};
|
|
|
|
static struct stateentry manstatelist[] =
|
|
{
|
|
{SBIT(2),
|
|
DL_ESTABLISH | INDICATION, l3dss1_dl_reset},
|
|
{SBIT(10),
|
|
DL_ESTABLISH | CONFIRM, l3dss1_dl_reest_status},
|
|
{SBIT(10),
|
|
DL_RELEASE | INDICATION, l3dss1_dl_reestablish},
|
|
{ALL_STATES,
|
|
DL_RELEASE | INDICATION, l3dss1_dl_release},
|
|
};
|
|
|
|
/* *INDENT-ON* */
|
|
|
|
|
|
static void
|
|
global_handler(struct PStack *st, int mt, struct sk_buff *skb)
|
|
{
|
|
u_char tmp[16];
|
|
u_char *p = tmp;
|
|
int l;
|
|
int i;
|
|
struct l3_process *proc = st->l3.global;
|
|
|
|
proc->callref = skb->data[2]; /* cr flag */
|
|
for (i = 0; i < ARRAY_SIZE(globalmes_list); i++)
|
|
if ((mt == globalmes_list[i].primitive) &&
|
|
((1 << proc->state) & globalmes_list[i].state))
|
|
break;
|
|
if (i == ARRAY_SIZE(globalmes_list)) {
|
|
if (st->l3.debug & L3_DEB_STATE) {
|
|
l3_debug(st, "dss1 global state %d mt %x unhandled",
|
|
proc->state, mt);
|
|
}
|
|
MsgHead(p, proc->callref, MT_STATUS);
|
|
*p++ = IE_CAUSE;
|
|
*p++ = 0x2;
|
|
*p++ = 0x80;
|
|
*p++ = 81 |0x80; /* invalid cr */
|
|
*p++ = 0x14; /* CallState */
|
|
*p++ = 0x1;
|
|
*p++ = proc->state & 0x3f;
|
|
l = p - tmp;
|
|
if (!(skb = l3_alloc_skb(l)))
|
|
return;
|
|
memcpy(skb_put(skb, l), tmp, l);
|
|
l3_msg(proc->st, DL_DATA | REQUEST, skb);
|
|
} else {
|
|
if (st->l3.debug & L3_DEB_STATE) {
|
|
l3_debug(st, "dss1 global %d mt %x",
|
|
proc->state, mt);
|
|
}
|
|
globalmes_list[i].rout(proc, mt, skb);
|
|
}
|
|
}
|
|
|
|
static void
|
|
dss1up(struct PStack *st, int pr, void *arg)
|
|
{
|
|
int i, mt, cr, cause, callState;
|
|
char *ptr;
|
|
u_char *p;
|
|
struct sk_buff *skb = arg;
|
|
struct l3_process *proc;
|
|
|
|
switch (pr) {
|
|
case (DL_DATA | INDICATION):
|
|
case (DL_UNIT_DATA | INDICATION):
|
|
break;
|
|
case (DL_ESTABLISH | CONFIRM):
|
|
case (DL_ESTABLISH | INDICATION):
|
|
case (DL_RELEASE | INDICATION):
|
|
case (DL_RELEASE | CONFIRM):
|
|
l3_msg(st, pr, arg);
|
|
return;
|
|
break;
|
|
default:
|
|
printk(KERN_ERR "HiSax dss1up unknown pr=%04x\n", pr);
|
|
return;
|
|
}
|
|
if (skb->len < 3) {
|
|
l3_debug(st, "dss1up frame too short(%d)", skb->len);
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
}
|
|
|
|
if (skb->data[0] != PROTO_DIS_EURO) {
|
|
if (st->l3.debug & L3_DEB_PROTERR) {
|
|
l3_debug(st, "dss1up%sunexpected discriminator %x message len %d",
|
|
(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
|
|
skb->data[0], skb->len);
|
|
}
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
}
|
|
cr = getcallref(skb->data);
|
|
if (skb->len < ((skb->data[1] & 0x0f) + 3)) {
|
|
l3_debug(st, "dss1up frame too short(%d)", skb->len);
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
}
|
|
mt = skb->data[skb->data[1] + 2];
|
|
if (st->l3.debug & L3_DEB_STATE)
|
|
l3_debug(st, "dss1up cr %d", cr);
|
|
if (cr == -2) { /* wrong Callref */
|
|
if (st->l3.debug & L3_DEB_WARN)
|
|
l3_debug(st, "dss1up wrong Callref");
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
} else if (cr == -1) { /* Dummy Callref */
|
|
if (mt == MT_FACILITY)
|
|
if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) {
|
|
l3dss1_parse_facility(st, NULL,
|
|
(pr == (DL_DATA | INDICATION)) ? -1 : -2, p);
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
}
|
|
if (st->l3.debug & L3_DEB_WARN)
|
|
l3_debug(st, "dss1up dummy Callref (no facility msg or ie)");
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
} else if ((((skb->data[1] & 0x0f) == 1) && (0==(cr & 0x7f))) ||
|
|
(((skb->data[1] & 0x0f) == 2) && (0==(cr & 0x7fff)))) { /* Global CallRef */
|
|
if (st->l3.debug & L3_DEB_STATE)
|
|
l3_debug(st, "dss1up Global CallRef");
|
|
global_handler(st, mt, skb);
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
} else if (!(proc = getl3proc(st, cr))) {
|
|
/* No transaction process exist, that means no call with
|
|
* this callreference is active
|
|
*/
|
|
if (mt == MT_SETUP) {
|
|
/* Setup creates a new transaction process */
|
|
if (skb->data[2] & 0x80) {
|
|
/* Setup with wrong CREF flag */
|
|
if (st->l3.debug & L3_DEB_STATE)
|
|
l3_debug(st, "dss1up wrong CRef flag");
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
}
|
|
if (!(proc = dss1_new_l3_process(st, cr))) {
|
|
/* May be to answer with RELEASE_COMPLETE and
|
|
* CAUSE 0x2f "Resource unavailable", but this
|
|
* need a new_l3_process too ... arghh
|
|
*/
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
}
|
|
} else if (mt == MT_STATUS) {
|
|
cause = 0;
|
|
if ((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) {
|
|
ptr++;
|
|
if (*ptr++ == 2)
|
|
ptr++;
|
|
cause = *ptr & 0x7f;
|
|
}
|
|
callState = 0;
|
|
if ((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) {
|
|
ptr++;
|
|
if (*ptr++ == 2)
|
|
ptr++;
|
|
callState = *ptr;
|
|
}
|
|
/* ETS 300-104 part 2.4.1
|
|
* if setup has not been made and a message type
|
|
* MT_STATUS is received with call state == 0,
|
|
* we must send nothing
|
|
*/
|
|
if (callState != 0) {
|
|
/* ETS 300-104 part 2.4.2
|
|
* if setup has not been made and a message type
|
|
* MT_STATUS is received with call state != 0,
|
|
* we must send MT_RELEASE_COMPLETE cause 101
|
|
*/
|
|
if ((proc = dss1_new_l3_process(st, cr))) {
|
|
proc->para.cause = 101;
|
|
l3dss1_msg_without_setup(proc, 0, NULL);
|
|
}
|
|
}
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
} else if (mt == MT_RELEASE_COMPLETE) {
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
} else {
|
|
/* ETS 300-104 part 2
|
|
* if setup has not been made and a message type
|
|
* (except MT_SETUP and RELEASE_COMPLETE) is received,
|
|
* we must send MT_RELEASE_COMPLETE cause 81 */
|
|
dev_kfree_skb(skb);
|
|
if ((proc = dss1_new_l3_process(st, cr))) {
|
|
proc->para.cause = 81;
|
|
l3dss1_msg_without_setup(proc, 0, NULL);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if (l3dss1_check_messagetype_validity(proc, mt, skb)) {
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
}
|
|
if ((p = findie(skb->data, skb->len, IE_DISPLAY, 0)) != NULL)
|
|
l3dss1_deliver_display(proc, pr, p); /* Display IE included */
|
|
for (i = 0; i < ARRAY_SIZE(datastatelist); i++)
|
|
if ((mt == datastatelist[i].primitive) &&
|
|
((1 << proc->state) & datastatelist[i].state))
|
|
break;
|
|
if (i == ARRAY_SIZE(datastatelist)) {
|
|
if (st->l3.debug & L3_DEB_STATE) {
|
|
l3_debug(st, "dss1up%sstate %d mt %#x unhandled",
|
|
(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
|
|
proc->state, mt);
|
|
}
|
|
if ((MT_RELEASE_COMPLETE != mt) && (MT_RELEASE != mt)) {
|
|
proc->para.cause = 101;
|
|
l3dss1_status_send(proc, pr, skb);
|
|
}
|
|
} else {
|
|
if (st->l3.debug & L3_DEB_STATE) {
|
|
l3_debug(st, "dss1up%sstate %d mt %x",
|
|
(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
|
|
proc->state, mt);
|
|
}
|
|
datastatelist[i].rout(proc, pr, skb);
|
|
}
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
dss1down(struct PStack *st, int pr, void *arg)
|
|
{
|
|
int i, cr;
|
|
struct l3_process *proc;
|
|
struct Channel *chan;
|
|
|
|
if ((DL_ESTABLISH | REQUEST) == pr) {
|
|
l3_msg(st, pr, NULL);
|
|
return;
|
|
} else if (((CC_SETUP | REQUEST) == pr) || ((CC_RESUME | REQUEST) == pr)) {
|
|
chan = arg;
|
|
cr = newcallref();
|
|
cr |= 0x80;
|
|
if ((proc = dss1_new_l3_process(st, cr))) {
|
|
proc->chan = chan;
|
|
chan->proc = proc;
|
|
memcpy(&proc->para.setup, &chan->setup, sizeof(setup_parm));
|
|
proc->callref = cr;
|
|
}
|
|
} else {
|
|
proc = arg;
|
|
}
|
|
if (!proc) {
|
|
printk(KERN_ERR "HiSax dss1down without proc pr=%04x\n", pr);
|
|
return;
|
|
}
|
|
|
|
if ( pr == (CC_TDSS1_IO | REQUEST)) {
|
|
l3dss1_io_timer(proc); /* timer expires */
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(downstatelist); i++)
|
|
if ((pr == downstatelist[i].primitive) &&
|
|
((1 << proc->state) & downstatelist[i].state))
|
|
break;
|
|
if (i == ARRAY_SIZE(downstatelist)) {
|
|
if (st->l3.debug & L3_DEB_STATE) {
|
|
l3_debug(st, "dss1down state %d prim %#x unhandled",
|
|
proc->state, pr);
|
|
}
|
|
} else {
|
|
if (st->l3.debug & L3_DEB_STATE) {
|
|
l3_debug(st, "dss1down state %d prim %#x",
|
|
proc->state, pr);
|
|
}
|
|
downstatelist[i].rout(proc, pr, arg);
|
|
}
|
|
}
|
|
|
|
static void
|
|
dss1man(struct PStack *st, int pr, void *arg)
|
|
{
|
|
int i;
|
|
struct l3_process *proc = arg;
|
|
|
|
if (!proc) {
|
|
printk(KERN_ERR "HiSax dss1man without proc pr=%04x\n", pr);
|
|
return;
|
|
}
|
|
for (i = 0; i < ARRAY_SIZE(manstatelist); i++)
|
|
if ((pr == manstatelist[i].primitive) &&
|
|
((1 << proc->state) & manstatelist[i].state))
|
|
break;
|
|
if (i == ARRAY_SIZE(manstatelist)) {
|
|
if (st->l3.debug & L3_DEB_STATE) {
|
|
l3_debug(st, "cr %d dss1man state %d prim %#x unhandled",
|
|
proc->callref & 0x7f, proc->state, pr);
|
|
}
|
|
} else {
|
|
if (st->l3.debug & L3_DEB_STATE) {
|
|
l3_debug(st, "cr %d dss1man state %d prim %#x",
|
|
proc->callref & 0x7f, proc->state, pr);
|
|
}
|
|
manstatelist[i].rout(proc, pr, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
setstack_dss1(struct PStack *st)
|
|
{
|
|
char tmp[64];
|
|
int i;
|
|
|
|
st->lli.l4l3 = dss1down;
|
|
st->lli.l4l3_proto = l3dss1_cmd_global;
|
|
st->l2.l2l3 = dss1up;
|
|
st->l3.l3ml3 = dss1man;
|
|
st->l3.N303 = 1;
|
|
st->prot.dss1.last_invoke_id = 0;
|
|
st->prot.dss1.invoke_used[0] = 1; /* Bit 0 must always be set to 1 */
|
|
i = 1;
|
|
while (i < 32)
|
|
st->prot.dss1.invoke_used[i++] = 0;
|
|
|
|
if (!(st->l3.global = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) {
|
|
printk(KERN_ERR "HiSax can't get memory for dss1 global CR\n");
|
|
} else {
|
|
st->l3.global->state = 0;
|
|
st->l3.global->callref = 0;
|
|
st->l3.global->next = NULL;
|
|
st->l3.global->debug = L3_DEB_WARN;
|
|
st->l3.global->st = st;
|
|
st->l3.global->N303 = 1;
|
|
st->l3.global->prot.dss1.invoke_id = 0;
|
|
|
|
L3InitTimer(st->l3.global, &st->l3.global->timer);
|
|
}
|
|
strcpy(tmp, dss1_revision);
|
|
printk(KERN_INFO "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp));
|
|
}
|