mISDNuser/i4lnet/tei.c

442 lines
9.0 KiB
C

/* $Id: tei.c,v 0.9 2003/08/27 07:33:03 kkeil Exp $
*
* Author Karsten Keil (keil@isdn4linux.de)
*
* This file is (c) under GNU PUBLIC LICENSE
* For changes and modifications please read
* ../../../Documentation/isdn/mISDN.cert
*
*/
#define __NO_VERSION__
#include <stdlib.h>
#include "net_l2.h"
// #include "helper.h"
// #include "debug.h"
// #include <linux/random.h>
const char *tei_revision = "$Revision: 0.9 $";
#define ID_REQUEST 1
#define ID_ASSIGNED 2
#define ID_DENIED 3
#define ID_CHK_REQ 4
#define ID_CHK_RES 5
#define ID_REMOVE 6
#define ID_VERIFY 7
#define TEI_ENTITY_ID 0xf
static
struct Fsm teifsm =
{NULL, 0, 0, NULL, NULL};
enum {
ST_TEI_NOP,
ST_TEI_REMOVE,
ST_TEI_IDVERIFY,
};
#define TEI_STATE_COUNT (ST_TEI_IDVERIFY+1)
static char *strTeiState[] =
{
"ST_TEI_NOP",
"ST_TEI_REMOVE",
"ST_TEI_IDVERIFY",
};
enum {
EV_IDREQ,
EV_ASSIGN,
EV_ASSIGN_REQ,
EV_CHECK_RES,
EV_CHECK_REQ,
EV_REMOVE,
EV_VERIFY,
EV_T201,
};
#define TEI_EVENT_COUNT (EV_T201+1)
static char *strTeiEvent[] =
{
"EV_IDREQ",
"EV_ASSIGN",
"EV_ASSIGN_REQ",
"EV_CHECK_RES",
"EV_CHECK_REQ",
"EV_REMOVE",
"EV_VERIFY",
"EV_T201",
};
static layer2_t
*new_tei_req(net_stack_t *nst)
{
layer2_t *l2;
int tei;
for (tei=64;tei<127;tei++) {
l2 = nst->layer2;
while(l2) {
if (l2->tei == tei)
break;
l2 = l2->next;
}
if (!l2)
break;
}
if (tei==127) /* all tei in use */
return(NULL);
l2 = new_dl2(nst, tei);
return(l2);
}
unsigned int
random_ri(void)
{
long int x;
x = random();
return (x & 0xffff);
}
static layer2_t *
find_tei(net_stack_t *nst, int tei)
{
layer2_t *l2;
l2 = nst->layer2;
while(l2) {
if (l2->tei == tei)
break;
l2 = l2->next;
}
return(l2);
}
static void
put_tei_msg(teimgr_t *tm, u_char m_id, unsigned int ri, u_char tei)
{
msg_t *msg;
u_char bp[8];
bp[0] = (TEI_SAPI << 2);
if (test_bit(FLG_LAPD_NET, &tm->l2->flag))
bp[0] |= 2; /* CR:=1 for net command */
bp[1] = (GROUP_TEI << 1) | 0x1;
bp[2] = UI;
bp[3] = TEI_ENTITY_ID;
bp[4] = ri >> 8;
bp[5] = ri & 0xff;
bp[6] = m_id;
bp[7] = (tei << 1) | 1;
msg = create_link_msg(MDL_UNITDATA | REQUEST, DINFO_SKB, 8, bp, 0);
if (!msg) {
dprint(DBGM_TEI, "mISDN: No msg for TEI manager\n");
return;
}
if (tei_l2(tm->l2, msg))
free_msg(msg);
}
static void
tei_assign_req(struct FsmInst *fi, int event, void *arg)
{
teimgr_t *tm = fi->userdata;
u_char *dp = arg;
if (tm->l2->tei == -1) {
tm->tei_m.printdebug(&tm->tei_m,
"net tei assign request without tei");
return;
}
tm->ri = ((unsigned int) *dp++ << 8);
tm->ri += *dp++;
if (tm->debug)
tm->tei_m.printdebug(&tm->tei_m,
"net assign request ri %d teim %d", tm->ri, *dp);
put_tei_msg(tm, ID_ASSIGNED, tm->ri, tm->l2->tei);
FsmChangeState(fi, ST_TEI_NOP);
}
static void
tei_id_chk_res(struct FsmInst *fi, int event, void *arg)
{
teimgr_t *tm = fi->userdata;
int *ri = arg;
if (tm->debug)
tm->tei_m.printdebug(fi, "identity %d check response ri %x/%x",
tm->l2->tei, *ri, tm->ri);
if (tm->ri != -1) {
FsmDelTimer(&tm->t201, 4);
tm->tei_m.printdebug(fi, "duplicat %d response", tm->l2->tei);
tm->val = tm->l2->tei;
put_tei_msg(tm, ID_REMOVE, 0, tm->val);
FsmAddTimer(&tm->t201, tm->T201, EV_T201, NULL, 2);
FsmChangeState(&tm->tei_m, ST_TEI_REMOVE);
} else
tm->ri = *ri;
}
static void
tei_id_remove(struct FsmInst *fi, int event, void *arg)
{
teimgr_t *tm = fi->userdata;
int *tei = arg;
if (tm->debug)
tm->tei_m.printdebug(fi, "identity remove tei %d/%d", *tei, tm->l2->tei);
tm->val = *tei;
put_tei_msg(tm, ID_REMOVE, 0, tm->val);
FsmAddTimer(&tm->t201, tm->T201, EV_T201, NULL, 2);
FsmChangeState(&tm->tei_m, ST_TEI_REMOVE);
}
static void
tei_id_verify(struct FsmInst *fi, int event, void *arg)
{
teimgr_t *tm = fi->userdata;
if (tm->debug)
tm->tei_m.printdebug(fi, "id verify request for tei %d",
tm->l2->tei);
tm->ri = -1;
put_tei_msg(tm, ID_CHK_REQ, 0, tm->l2->tei);
FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY);
test_and_set_bit(FLG_TEI_T201_1, &tm->l2->flag);
FsmAddTimer(&tm->t201, tm->T201, EV_T201, NULL, 2);
}
static void
tei_id_remove_tout(struct FsmInst *fi, int event, void *arg)
{
teimgr_t *tm = fi->userdata;
if (tm->debug)
tm->tei_m.printdebug(fi, "remove req(2) tei %d",
tm->l2->tei);
put_tei_msg(tm, ID_REMOVE, 0, tm->val);
FsmChangeState(fi, ST_TEI_NOP);
}
static void
tei_id_ver_tout(struct FsmInst *fi, int event, void *arg)
{
teimgr_t *tm = fi->userdata;
if (tm->debug)
tm->tei_m.printdebug(fi, "verify tout tei %d",
tm->l2->tei);
if (test_and_clear_bit(FLG_TEI_T201_1, &tm->l2->flag)) {
put_tei_msg(tm, ID_CHK_REQ, 0, tm->l2->tei);
tm->ri = -1;
FsmAddTimer(&tm->t201, tm->T201, EV_T201, NULL, 3);
} else {
FsmChangeState(fi, ST_TEI_NOP);
if (tm->ri == -1) {
tm->tei_m.printdebug(fi, "tei %d check no response",
tm->l2->tei);
// remove tei
} else
tm->tei_m.printdebug(fi, "tei %d check ok",
tm->l2->tei);
}
}
int
l2_tei(teimgr_t *tm, msg_t *msg)
{
mISDN_head_t *hh;
int ret = -EINVAL;
if (!tm || !msg)
return(ret);
hh = (mISDN_head_t *)msg->data;
dprint(DBGM_TEI, "%s: prim(%x)\n", __FUNCTION__, hh->prim);
if (msg->len < mISDN_FRAME_MIN)
return(ret);
switch(hh->prim) {
case (MDL_REMOVE | INDICATION):
FsmEvent(&tm->tei_m, EV_REMOVE, &hh->dinfo);
break;
case (MDL_ERROR | REQUEST):
if (!test_bit(FLG_FIXED_TEI, &tm->l2->flag))
FsmEvent(&tm->tei_m, EV_VERIFY, NULL);
break;
}
free_msg(msg);
return(0);
}
static void
tei_debug(struct FsmInst *fi, char *fmt, ...)
{
teimgr_t *tm = fi->userdata;
char tbuf[128];
va_list args;
va_start(args, fmt);
vsprintf(tbuf, fmt, args);
dprint(DBGM_L2, "tei%d %s\n", tm->l2->tei, tbuf);
va_end(args);
}
static struct FsmNode TeiFnList[] =
{
{ST_TEI_NOP, EV_ASSIGN_REQ, tei_assign_req},
{ST_TEI_NOP, EV_VERIFY, tei_id_verify},
{ST_TEI_NOP, EV_REMOVE, tei_id_remove},
{ST_TEI_REMOVE, EV_T201, tei_id_remove_tout},
{ST_TEI_IDVERIFY, EV_T201, tei_id_ver_tout},
{ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove},
{ST_TEI_IDVERIFY, EV_CHECK_RES, tei_id_chk_res},
};
#define TEI_FN_COUNT (sizeof(TeiFnList)/sizeof(struct FsmNode))
void
release_tei(teimgr_t *tm)
{
FsmDelTimer(&tm->t201, 1);
free(tm);
}
int
create_teimgr(layer2_t *l2) {
teimgr_t *ntei;
if (!l2) {
eprint("create_tei no layer2\n");
return(-EINVAL);
}
if (!(ntei = malloc(sizeof(teimgr_t)))) {
eprint("kmalloc teimgr failed\n");
return(-ENOMEM);
}
memset(ntei, 0, sizeof(teimgr_t));
ntei->l2 = l2;
ntei->T201 = 1000; /* T201 1000 milliseconds */
ntei->debug = l2->debug;
ntei->tei_m.nst = l2->nst;
ntei->tei_m.debug = l2->debug;
ntei->tei_m.userdata = ntei;
ntei->tei_m.printdebug = tei_debug;
ntei->tei_m.fsm = &teifsm;
ntei->tei_m.state = ST_TEI_NOP;
FsmInitTimer(&ntei->tei_m, &ntei->t201);
l2->tm = ntei;
return(0);
}
int
tei_mux(net_stack_t *nst, msg_t *msg)
{
mISDN_head_t *hh;
u_char *dp;
int mt;
layer2_t *l2;
unsigned int ri, ai;
hh = (mISDN_head_t *)msg->data;
dprint(DBGM_TEI, "%s: prim(%x) len(%d)\n", __FUNCTION__,
hh->prim, msg->len);
if (msg->len < mISDN_FRAME_MIN)
return(-EINVAL);
if (hh->prim != (MDL_UNITDATA | INDICATION)) {
wprint("%s: prim(%x) unhandled\n", __FUNCTION__,
hh->prim);
return(-EINVAL);
}
msg_pull(msg, mISDN_HEAD_SIZE);
if (msg->len < 8) {
wprint("short tei mgr frame %d/8\n", msg->len);
return(-EINVAL);
}
dp = msg->data + 2;
if ((*dp & 0xef) != UI) {
wprint("tei mgr frame is not ui %x\n", *dp);
return(-EINVAL);
}
dp++;
if (*dp++ != TEI_ENTITY_ID) {
/* wrong management entity identifier, ignore */
dp--;
wprint("tei handler wrong entity id %x\n", *dp);
return(-EINVAL);
} else {
mt = *(dp+2);
ri = ((unsigned int) *dp++ << 8);
ri += *dp++;
dp++;
ai = (unsigned int) *dp++;
ai >>= 1;
dprint(DBGM_TEI, "tei handler mt %x ri(%x) ai(%d)\n",
mt, ri, ai);
if (mt == ID_REQUEST) {
if (ai != 127) {
wprint("%s: ID_REQUEST ai(%d) not 127\n", __FUNCTION__,
ai);
return(-EINVAL);
}
l2 = new_tei_req(nst);
if (!l2) {
wprint("%s: no free tei\n", __FUNCTION__);
return(-EBUSY);
}
l2->tm->ri = ri;
put_tei_msg(l2->tm, ID_ASSIGNED, ri, l2->tei);
free_msg(msg);
return(0);
}
l2 = find_tei(nst, ai);
if (mt == ID_VERIFY) {
if (l2) {
FsmEvent(&l2->tm->tei_m, EV_VERIFY, &ai);
} else {
l2 = find_tei(nst, 127);
if (!l2) {
wprint("%s: no 127 manager\n", __FUNCTION__);
return(-EINVAL);
}
FsmEvent(&l2->tm->tei_m, EV_REMOVE, &ai);
}
} else if (mt == ID_CHK_RES) {
if (l2) {
FsmEvent(&l2->tm->tei_m, EV_CHECK_RES, &ri);
} else {
l2 = find_tei(nst, 127);
if (!l2) {
wprint("%s: no 127 manager\n", __FUNCTION__);
return(-EINVAL);
}
FsmEvent(&l2->tm->tei_m, EV_REMOVE, &ai);
}
} else {
wprint("%s: wrong mt %x", __FUNCTION__, mt);
return(-EINVAL);
}
}
free_msg(msg);
return(0);
}
int TEIInit(void)
{
teifsm.state_count = TEI_STATE_COUNT;
teifsm.event_count = TEI_EVENT_COUNT;
teifsm.strEvent = strTeiEvent;
teifsm.strState = strTeiState;
FsmNew(&teifsm, TeiFnList, TEI_FN_COUNT);
return(0);
}
void TEIFree(void)
{
FsmFree(&teifsm);
}