📄 l3ni1.c
字号:
/* $Id: l3ni1.c,v 2.8.2.3 2004/01/13 14:31:25 keil Exp $ * * NI1 D-channel protocol * * Author Matt Henderson & Guy Ellis * Copyright by Traverse Technologies Pty Ltd, www.travers.com.au * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * * 2000.6.6 Initial implementation of routines for US NI1 * Layer 3 protocol based on the EURO/DSS1 D-channel protocol * driver written by Karsten Keil et al. * NI-1 Hall of Fame - Thanks to.... * Ragnar Paulson - for some handy code fragments * Will Scales - beta tester extraordinaire * Brett Whittacre - beta tester and remote devel system in Vegas * */#include "hisax.h"#include "isdnl3.h"#include "l3ni1.h"#include <linux/ctype.h>extern char *HiSax_getrev(const char *revision);static const char *ni1_revision = "$Revision: 2.8.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.ni1.last_invoke_id + 1; /* try new id */ while ((i) && (p->prot.ni1.invoke_used[retval >> 3] == 0xFF)) { p->prot.ni1.last_invoke_id = (retval & 0xF8) + 8; i--; } if (i) { while (p->prot.ni1.invoke_used[retval >> 3] & (1 << (retval & 7))) retval++; } else retval = 0; p->prot.ni1.last_invoke_id = retval; p->prot.ni1.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.ni1.invoke_used[id >> 3] &= ~(1 << (id & 7));} /* free_invoke_id */ /**********************************************************//* create a new l3 process and fill in ni1 specific data *//**********************************************************/static struct l3_process*ni1_new_l3_process(struct PStack *st, int cr){ struct l3_process *proc; if (!(proc = new_l3_process(st, cr))) return(NULL); proc->prot.ni1.invoke_id = 0; proc->prot.ni1.remote_operation = 0; proc->prot.ni1.uus1_data[0] = '\0'; return(proc);} /* ni1_new_l3_process *//************************************************//* free a l3 process and all ni1 specific data *//************************************************/ static voidni1_release_l3_process(struct l3_process *p){ free_invoke_id(p->st,p->prot.ni1.invoke_id); release_l3_process(p);} /* ni1_release_l3_process */ /********************************************************//* search a process with invoke id id and dummy callref *//********************************************************/static struct l3_process *l3ni1_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.ni1.invoke_id == id)) return(pc); pc = pc->next; } return(NULL);} /* l3ni1_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 l3ni1_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 = l3ni1_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 = NI1_STAT_INVOKE_RES; ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id; ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id; ic.parm.ni1_io.proc = pc->prot.ni1.proc; ic.parm.ni1_io.timeout= 0; ic.parm.ni1_io.datalen = nlen; ic.parm.ni1_io.data = p; free_invoke_id(pc->st, pc->prot.ni1.invoke_id); pc->prot.ni1.invoke_id = 0; /* reset id */ cs->iif.statcallb(&ic); ni1_release_l3_process(pc); } else l3_debug(st, "dummy return result id=0x%x result len=%d",id,nlen);} /* l3ni1_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 l3ni1_dummy_error_return(struct PStack *st, int id, ulong error){ isdn_ctrl ic; struct IsdnCardState *cs; struct l3_process *pc = NULL; if ((pc = l3ni1_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 = NI1_STAT_INVOKE_ERR; ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id; ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id; ic.parm.ni1_io.proc = pc->prot.ni1.proc; ic.parm.ni1_io.timeout= error; ic.parm.ni1_io.datalen = 0; ic.parm.ni1_io.data = NULL; free_invoke_id(pc->st, pc->prot.ni1.invoke_id); pc->prot.ni1.invoke_id = 0; /* reset id */ cs->iif.statcallb(&ic); ni1_release_l3_process(pc); } else l3_debug(st, "dummy return error id=0x%x error=0x%lx",id,error);} /* l3ni1_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 l3ni1_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 = NI1_STAT_INVOKE_BRD; ic.parm.ni1_io.hl_id = id; ic.parm.ni1_io.ll_id = 0; ic.parm.ni1_io.proc = ident; ic.parm.ni1_io.timeout= 0; ic.parm.ni1_io.datalen = nlen; ic.parm.ni1_io.data = p; cs->iif.statcallb(&ic);} /* l3ni1_dummy_invoke */static voidl3ni1_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) { l3ni1_dummy_invoke(st, cr, id, ident, p, nlen); return; } l3_debug(st, "invoke break"); break; case 2: /* return result */ /* if no process available handle separately */ if (!pc) { if (cr == -1) l3ni1_dummy_return_result(st, id, p, nlen); return; } if ((pc->prot.ni1.invoke_id) && (pc->prot.ni1.invoke_id == id)) { /* Diversion successful */ free_invoke_id(st,pc->prot.ni1.invoke_id); pc->prot.ni1.remote_result = 0; /* success */ pc->prot.ni1.invoke_id = 0; pc->redir_result = pc->prot.ni1.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) l3ni1_dummy_error_return(st, id, err_ret); return; } if ((pc->prot.ni1.invoke_id) && (pc->prot.ni1.invoke_id == id)) { /* Deflection error */ free_invoke_id(st,pc->prot.ni1.invoke_id); pc->prot.ni1.remote_result = err_ret; /* result */ pc->prot.ni1.invoke_id = 0; pc->redir_result = pc->prot.ni1.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 voidl3ni1_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 voidl3ni1_message_plus_chid(struct l3_process *pc, u_char mt)/* sends an l3 messages plus channel id - added GE 05/09/00 */{ struct sk_buff *skb; u_char tmp[16]; u_char *p = tmp; u_char chid; chid = (u_char)(pc->para.bchannel & 0x03) | 0x88; MsgHead(p, pc->callref, mt); *p++ = IE_CHANNEL_ID; *p++ = 0x01; *p++ = chid; if (!(skb = l3_alloc_skb(7))) return; memcpy(skb_put(skb, 7), tmp, 7); l3_msg(pc->st, DL_DATA | REQUEST, skb);}static voidl3ni1_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 voidl3ni1_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 voidl3ni1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg){ /* This routine is called if here was no SETUP made (checks in ni1up and in * l3ni1_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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -