NT4/private/ntos/ndis/pcimac/cm_state.c
2020-09-30 17:12:29 +02:00

756 lines
20 KiB
C

/*
* CM_STATE.C - q931 state managment code
*/
#include <ndis.h>
//#include <ndismini.h>
#include <ndiswan.h>
#include <mytypes.h>
#include <mydefs.h>
#include <disp.h>
#include <util.h>
#include <opcodes.h>
#include <adapter.h>
#include <idd.h>
#include <mtl.h>
#include <cm.h>
/* (ans) process incoming connections */
INT
cm__ans_est_ind(CM_CHAN *chan, IDD_MSG *msg, VOID *idd, USHORT lterm)
{
USHORT bchan, type, cid;
INT RetCode;
D_LOG(D_ENTRY, ("cm__ans_est_ind: entry, chan: 0x%p, msg: 0x%p", \
chan, msg));
cid = HIWORD(msg->bufid);
/* must not have a channel at this time */
if ( chan )
{
D_LOG(D_ALWAYS, ("cm__ans_est_ind: on used channel, ignored!"));
RetCode = CM_E_BADPARAM;
//
// we need to let the adapter know that we are not processing
// this incoming call indication
//
ignored:
/* answer channel */
cm__est_ignore(idd, cid, lterm);
return(RetCode);
}
/* extract info out of message, must have bchan/type */
if ( (cm__get_bchan(msg, &bchan) != CM_E_SUCC) ||
(cm__get_type(msg, &type) != CM_E_SUCC) )
{
D_LOG(D_ALWAYS, ("cm__ans_est_ind: bchan or type missing, ignored!"));
RetCode = CM_E_BADPARAM;
goto ignored;
}
if ( !CM_BCHAN_ASSIGNED(bchan) )
{
D_LOG(D_ALWAYS, ("cm__ans_est_ind: bchan: %d, unassigned, ignored!",\
bchan));
RetCode = CM_E_BADPARAM;
goto ignored;
}
D_LOG(D_ALWAYS, ("cm__ans_est_ind: cid: 0x%x, bchan: %d, type: 0x%d",\
cid, bchan, type));
/* channel will be answered only if a listening profile exists */
if ( !cm__find_listen_conn("*", "*", "*", idd) )
{
D_LOG(D_ALWAYS, ("cm__ans_est_ind: not listening profile, ignored!"));
RetCode = CM_E_NOSUCH;
goto ignored;
}
/* allocate a channel out of incoming channel poll */
if ( !(chan = cm__chan_alloc()) )
{
D_LOG(D_ALWAYS, ("cm__ans_est_ind: no channel slot, ignored!"));
RetCode = CM_E_NOSLOT;
goto ignored;
}
/* fillup channel structure */
NdisZeroMemory(chan, sizeof(*chan));
chan->idd = idd;
chan->lterm = lterm;
chan->bchan = bchan;
chan->type = type;
chan->speed = cm__type2speed(type);
chan->ustate = CM_US_UNDEF;
chan->cid = cid;
chan->timeout = ut_time_now();
/* extract caller address, if present */
if ( cm__get_addr(msg, chan->addr) != CM_E_SUCC )
strcpy(chan->addr, "<unknown>");
D_LOG(D_ALWAYS, ("cm__ans_est_ind: caller address is: %s", \
chan->addr));
/* answer channel */
cm__est_rsp(chan);
/* return succ */
return(CM_E_SUCC);
}
/* (ans) process state indications */
INT
cm__ans_state_ind(CM_CHAN *chan, IDD_MSG *msg)
{
D_LOG(D_ENTRY, ("cm__ans_state_ind: entry, chan: 0x%p, msg: 0x%p", \
chan, msg));
/* log state change */
chan->ustate = LOWORD(msg->bufid);
D_LOG(D_ALWAYS, ("cm__ans_state_ind: ustate: %d", chan->ustate));
/* if changed to U0, has been released */
if ( !chan->ustate )
{
cm__bchan_ctrl(chan, 0);
cm__chan_free(chan);
return(CM_E_SUCC);
}
/* if changed to U10, just got connected, open data path */
if ( chan->ustate == 10 )
{
cm__bchan_ctrl(chan, 1);
chan->timeout = ut_time_now();
return(CM_E_SUCC);
}
/* else ignore */
return(CM_E_SUCC);
}
/* (ans) process data indications */
INT
cm__ans_data_ind(CM_CHAN *chan, IDD_MSG *msg)
{
CM_UUS *uus;
ULONG chan_num, n;
UCHAR cause;
CM *cm;
ULONG CompressionFlag = 0;
IDD_MSG msg1;
D_LOG(D_ENTRY, ("cm__ans_data_ind: entry, chan: 0x%p, msg: 0x%p", \
chan, msg));
/* assign UUS pointer & do some basic checks */
uus = (CM_UUS*)msg->bufptr;
if ( msg->buflen < CM_UUS_SIZE )
return(CM_E_BADUUS);
if ( (uus->pkt_type != CM_PKT_TYPE) ||
(uus->prot_desc != CM_PROT_DESC) ||
(cm__calc_chksum(uus, CM_UUS_SIZE) != 0) )
return(CM_E_BADUUS);
/* channel must be atleast connected */
if ( chan->ustate < 10 )
return(CM_E_BADSTATE);
/* if channel already accepted assoc, accept again (other side lost) */
if ( chan->ustate == CM_US_UUS_OKED )
{
D_LOG(D_ALWAYS, ("cm__ans_data_ind: chan_num->ustate: %d", chan->ustate));
accept:
chan->ustate = CM_US_UUS_OKED;
chan->timeout = ut_time_now();
cm__tx_uus_pkt(chan, CM_ASSOC_ACK, 0);
return(CM_E_SUCC);
}
/* record information from uus */
NdisMoveMemory (chan->DstAddr, uus->src_addr, 6);
chan->remote_conn_index = uus->conn;
//
// kill dead man timer for this channel
//
NdisZeroMemory(&msg1, sizeof(IDD_MSG));
msg1.opcode = Q931_CAN_TU10_RQ;
msg1.bufptr = ut_get_buf();
msg1.buflen = 0;
msg1.bufid = MAKELONG(0, chan->cid);
/* send to idd */
if (idd_send_msg(chan->idd, &msg1, (USHORT)CM_PORT(chan), (VOID*)cm__q931_cmpl_handler, NULL) != IDD_E_SUCC)
ut_free_buf(msg1.bufptr);
/* if not last channel, accept */
chan_num = 0;
cm__chan_foreach(cm__inc_chan_num, chan, &chan_num);
if ( (UCHAR)chan_num < uus->channum )
{
D_LOG(D_ALWAYS, ("cm__ans_data_ind: chan_num: %d, uus->channum: %d", chan_num, uus->channum));
goto accept;
}
/* last channel, find matching connection/profile */
if ( !(cm = cm__find_listen_conn(uus->lname, uus->rname, chan->addr, chan->idd)) )
{
/* none found, reject */
cause = CM_NO_PROF;
D_LOG(D_ALWAYS, ("cm__ans_data_ind: rejected, cause: %d", cause));
cm__tx_uus_pkt(chan, CM_ASSOC_NACK, cause);
return(CM_E_NOSUCH);
}
/* matching connection found!, fillin */
D_LOG(D_ALWAYS, ("cm__ans_data_ind: matching connection: cm: 0x%p", cm));
cm->state = CM_ST_IN_ANS;
cm->StateChangeFlag = TRUE;
cm->was_listen = TRUE;
cm->active_chan_num = chan_num;
cm->remote_conn_index = chan->remote_conn_index;
cm->ConnectionType = CM_DKF;
NdisMoveMemory(cm->DstAddr, chan->DstAddr, 6);
NdisMoveMemory (cm->remote_name, uus->rname, sizeof(cm->remote_name));
cm->timeout = cm->rx_last_frame_time = cm->tx_last_frame_time =
ut_time_now();
/* accept channel here */
chan->ustate = CM_US_UUS_OKED;
chan->timeout = ut_time_now();
chan->cm = cm;
cm__tx_uus_pkt(chan, CM_ASSOC_ACK, 0);
/* collect channels info local vector */
cm->dprof.chan_num = 0;
cm__chan_foreach(cm__add_chan, chan, cm);
/* init channel fields */
for ( n = 0 ; n < cm->dprof.chan_num ; n++)
{
CM_CHAN *chan = cm->dprof.chan_tbl + n;
chan->ustate = CM_US_CONN;
chan->timeout = ut_time_now();
chan->num = (USHORT)n;
chan->cm = cm;
chan->active = TRUE;
}
/* make connection active */
cm->state = CM_ST_ACTIVE;
cm->StateChangeFlag = TRUE;
// Set compression Flag
if (cm->dprof.HWCompression && (uus->option_0 & UUS_0_COMPRESSION))
CompressionFlag = 1;
else
CompressionFlag = 0;
D_LOG(D_ALWAYS, ("cm__ans_data_ind: Activating connection for cm: 0x%p", cm));
return(cm__activate_conn(cm, CompressionFlag));
}
/* (org) new cid indicated on outgoing channel */
INT
cm__org_cid_ind(CM_CHAN *chan, IDD_MSG *msg)
{
CM *cm;
USHORT conn_num, chan_num, cid;
D_LOG(D_ENTRY, ("cm__org_cid_ind: entry, chan: 0x%p, msg: 0x%p", \
chan, msg));
/* extract conn_num/chan_num out of param 3, get cid */
conn_num = HIBYTE(LOWORD(msg->bufid));
chan_num = LOBYTE(LOWORD(msg->bufid));
cid = HIWORD(msg->bufid);
D_LOG(D_ALWAYS, ("cm__org_cid_ind: conn_num: %d, chan_num: %d, cid: 0x%x", conn_num, chan_num, cid));
// DbgPrint("cid_ind: conn_num: %d, chan_num: %d, cid: 0x%x\n",
// conn_num, chan_num, cid);
/* get related connection */
if ( !(cm = cm__get_conn(conn_num)) )
{
// DbgPrint("cid_ind: cm__get_conn failed!\n");
return(CM_E_BADPARAM);
}
/* get related channel */
if ( chan_num >= cm->dprof.chan_num )
{
// DbgPrint("cid_ind: invalid chan_num!\n");
return(CM_E_BADPARAM);
}
else
chan = cm->dprof.chan_tbl + chan_num;
/* check channel ustate */
if ( chan->ustate != CM_US_WAIT_CID )
{
// DbgPrint("cid_ind: invalid ustate (%d)!\n", chan->ustate);
return(CM_E_BADSTATE);
}
/* cid == 0, no free slots at idp, simulate state change to 0 */
if ( !cid )
{
cm->NoActiveLine = 1;
return(cm__org_state_ind(chan, NULL));
}
/* assign params */
chan->ustate = CM_US_UNDEF;
chan->cid = cid;
// DbgPrint("cid_ind: ustate: %d, cid: 0x%x assigned\n", chan->ustate, chan->cid);
return(CM_E_SUCC);
}
/* (org) process state indications */
INT
cm__org_state_ind(CM_CHAN *chan, IDD_MSG *msg)
{
CM *cm;
ULONG n;
USHORT gave_up_num, chan_num;
ULONG CompressionFlag;
D_LOG(D_ENTRY, ("cm__org_state_ind: entry, chan: 0x%p, msg: 0x%p", \
chan, msg));
/* check for change of state at protocol level */
if ( !chan )
{
/* not used for now */
return(CM_E_NOTIMPL);
}
/* log change */
cm = chan->cm;
chan->ustate = msg ? LOWORD(msg->bufid) : 0;
D_LOG(D_ALWAYS,("cm__org_state_ind: cm 0x%p, ustate: 0x%x", \
cm, chan->ustate));
/* if changed to U0, has been released, may retry connection here */
if ( !chan->ustate )
{
/* turn off bchannel */
cm__bchan_ctrl(chan, 0);
/* if not in activation, this is a fatal error, disconnect */
if ( cm->state != CM_ST_IN_ACT )
{
disc_all:
for ( n = 0 ; n < cm->dprof.chan_num ; n++ )
{
CM_CHAN *chan1 = cm->dprof.chan_tbl + n;
if ( chan1->ustate > 0 )
cm__disc_rq(chan1);
}
/* deactivate connection */
cm__deactivate_conn(cm, 0);
return(CM_E_SUCC);
}
/* attampt to retry */
if ( !cm->dprof.fallback ||
(cm->CauseValue == 0x11 || cm->SignalValue == 0x04) ||
(cm__get_next_chan(chan) != CM_E_SUCC) )
{
chan->ustate = CM_US_GAVE_UP;
chan->gave_up = 1;
}
else
{
/* if here, retrying */
chan->cid = MAKEWORD(chan->num, cm->local_conn_index);
chan->ustate = CM_US_WAIT_CID;
chan->timeout = ut_time_now();
cm__est_rq(chan);
}
/* find out how many channels gave up (or connected) */
check_chan:
for ( n = gave_up_num = 0 ; n < cm->dprof.chan_num ; n++ )
if ( cm->dprof.chan_tbl[n].gave_up )
gave_up_num++;
else if ( cm->dprof.chan_tbl[n].ustate != CM_US_WAIT_CONN )
break;
/* if broke out of loop before hitting chan_num, some channels
are still in progress */
if ( n < cm->dprof.chan_num )
return(CM_E_SUCC);
/* if all gave up, give up conn */
if ( gave_up_num >= cm->dprof.chan_num )
{
cm__deactivate_conn(cm, 0);
return(CM_E_SUCC);
}
/* if here, some channels connected and some gave up, continue */
chan_num = cm->dprof.chan_num - gave_up_num;
/* if fallback set to no, must match */
if ( !cm->dprof.fallback && (chan_num != cm->dprof.chan_num) )
goto disc_all;
/* connection enters in_sync state */
cm->state = CM_ST_IN_SYNC;
cm->StateChangeFlag = TRUE;
cm->timeout = ut_time_now();
/* compact channel table & renumber */
for ( n = 0 ; n < cm->dprof.chan_num ; n++ )
{
CM_CHAN *chan1 = cm->dprof.chan_tbl + n;
if ( chan1->gave_up )
{
NdisMoveMemory(cm->dprof.chan_tbl + n,
cm->dprof.chan_tbl + n + 1,
sizeof(CM_CHAN) * (cm->dprof.chan_num - n - 1));
cm->dprof.chan_num--;
n--;
}
}
for ( n = 0 ; n < cm->dprof.chan_num ; n++ )
cm->dprof.chan_tbl[n].num = (USHORT)n;
if (cm->ConnectionType == CM_DKF)
{
//
// if this is a uus connnection tx uus frames
//
/* send initial uus_rq on all active channels */
for ( n = 0 ; n < cm->dprof.chan_num ; n++ )
{
CM_CHAN *chan1 = cm->dprof.chan_tbl + n;
if ( chan1->gave_up )
continue;
chan1->timeout = ut_time_now();
chan1->ustate = CM_US_UUS_SEND;
cm__tx_uus_pkt(chan1, CM_ASSOC_RQ, 0);
}
}
else
{
//
// if this is a ppp connection mark channels
// as being connected and activate the connection
//
for ( n = 0 ; n < cm->dprof.chan_num ; n++ )
{
CM_CHAN *chan1 = cm->dprof.chan_tbl + n;
if ( chan1->gave_up )
continue;
chan1->ustate = CM_US_CONN;
chan1->timeout = ut_time_now();
chan1->remote_conn_index = 1;
}
NdisZeroMemory(cm->DstAddr, sizeof(cm->DstAddr));
NdisZeroMemory(cm->remote_name, sizeof(cm->remote_name));
/* make connection active now */
cm->state = CM_ST_ACTIVE;
cm->StateChangeFlag = TRUE;
CompressionFlag = 0;
return (cm__activate_conn(cm, CompressionFlag));
}
return(CM_E_SUCC);
}
/* if change state to U10 just connected */
if ( chan->ustate == 10 )
{
/* start data transfer on channel */
cm__bchan_ctrl(chan, 1);
chan->ustate = CM_US_WAIT_CONN;
chan->timeout = ut_time_now();
/* see if all channels are connected, continue as a change to U0 */
goto check_chan;
}
}
/* (org) process data indications */
INT
cm__org_data_ind(CM_CHAN *chan, IDD_MSG *msg)
{
CM_UUS *uus;
CM *cm = chan->cm;
ULONG n, first;
ULONG CompressionFlag;
D_LOG(D_ENTRY, ("cm__org_data_ind: entry, chan: 0x%p, msg: 0x%p", \
chan, msg));
/* assign UUS pointer & do some basic checks */
uus = (CM_UUS*)msg->bufptr;
if ( msg->buflen < CM_UUS_SIZE )
return(CM_E_BADUUS);
if ( (uus->pkt_type != CM_PKT_TYPE) ||
(uus->prot_desc != CM_PROT_DESC) ||
(cm__calc_chksum(uus, CM_UUS_SIZE) != 0) )
return(CM_E_BADUUS);
/* channel must be atleast connected */
if ( chan->ustate < 10 )
return(CM_E_BADSTATE);
/* if is a request, channel is part of a listening conn */
if ( uus->opcode == CM_ASSOC_RQ )
{
cm__tx_uus_pkt(chan, CM_ASSOC_ACK, 0);
return(CM_E_SUCC);
}
/* if nack detected, connection is torn down */
if ( uus->opcode == CM_ASSOC_NACK )
{
// disc_all:
for ( n = 0 ; n < cm->dprof.chan_num ; n++ )
{
CM_CHAN *chan1 = cm->dprof.chan_tbl + n;
if ( chan1->ustate > 0 )
cm__disc_rq(chan1);
}
/* deactivate connection */
cm__deactivate_conn(cm, 0);
return(CM_E_SUCC);
}
/* if here must be an ack */
if ( uus->opcode != CM_ASSOC_ACK )
return(CM_E_BADUUS);
/* if channel already connected and uus ack'ed - ignore */
if ( chan->ustate > CM_US_UUS_OKED )
return(CM_E_SUCC);
//
// if this flag is set then we originally had a PPP connection
// this means that all of the connection stuff is taken care of and
// we just need to satisfy the remote ends uus requirements.
// there should be only one channel in this case!
//
if (cm->PPPToDKF)
{
chan->ustate = CM_US_CONN;
NdisMoveMemory (chan->DstAddr, uus->src_addr, 6);
NdisMoveMemory(cm->DstAddr, chan->DstAddr, 6);
cm->remote_conn_index = cm->dprof.chan_tbl[0].remote_conn_index;
NdisMoveMemory(cm->remote_name, uus->lname, sizeof(cm->remote_name));
cm->PPPToDKF = 0;
return(CM_E_SUCC);
}
/* if here, it is an ack */
chan->ustate = CM_US_UUS_OKED;
chan->timeout = ut_time_now();
NdisMoveMemory (chan->DstAddr, uus->src_addr, 6);
chan->remote_conn_index = uus->conn;
/* proceed only if all channels ok'ed */
for ( n = 0 ; n < cm->dprof.chan_num ; n++ )
if ( !cm->dprof.chan_tbl[n].gave_up &&
(cm->dprof.chan_tbl[n].ustate != CM_US_UUS_OKED) )
return(CM_E_SUCC);
/* verify all channel got connected to the same eaddr/conn */
for ( first = 0 ; first < cm->dprof.chan_num ; first++ )
if ( !cm->dprof.chan_tbl[n].gave_up )
break;
/* move all channels to connected state */
for ( n = 0 ; n < cm->dprof.chan_num ; n++ )
if ( !cm->dprof.chan_tbl[n].gave_up )
{
cm->dprof.chan_tbl[n].ustate = CM_US_CONN;
cm->dprof.chan_tbl[n].timeout = ut_time_now();
}
// Hack to get around no cm for all channels < the last channel received
NdisMoveMemory(cm->DstAddr, chan->DstAddr, 6);
/* store some values on a connection level */
cm->remote_conn_index = cm->dprof.chan_tbl[first].remote_conn_index;
NdisMoveMemory(cm->remote_name, uus->lname, sizeof(cm->remote_name));
/* make connection active now */
cm->state = CM_ST_ACTIVE;
cm->StateChangeFlag = TRUE;
// Set compression Flag
if (cm->dprof.HWCompression && (uus->option_0 & UUS_0_COMPRESSION))
CompressionFlag = 1;
else
CompressionFlag = 0;
return(cm__activate_conn(cm, CompressionFlag));
}
/* (org) process element indications */
INT
cm__org_elem_ind(CM_CHAN *chan, IDD_MSG *msg)
{
USHORT bchan;
int auto_disc = 0;
CHAR *elem;
D_LOG(D_ENTRY, ("cm__org_elem_ind: entry, chan: 0x%p, msg: 0x%p", \
chan, msg));
/* must have a valid channel to proceed */
if ( !chan )
return(CM_E_SUCC);
/* check if bchannel reported */
if ( cm__get_bchan(msg, &bchan) == CM_E_SUCC )
{
D_LOG(D_ALWAYS, ("cm__org_elem_ind: bchan: %d", bchan));
if ( !CM_BCHAN_ASSIGNED(chan->bchan) )
auto_disc |= 1;
else
chan->bchan = bchan;
}
/* scan for cause */
if ( (elem = cm__q931_elem(msg->bufptr, msg->buflen, 0x08)) &&
(elem[1] >= 2) && !(elem[2] & 0x78) )
{
static CHAR disc_vals[] = { 0x01, 0x11, 0x12 };
D_LOG(D_ALWAYS, ("cm__org_elem_ind: cause: 0x%x", elem[3] & 0x7F));
if ( memchr(disc_vals, elem[3] & 0x7F, sizeof(disc_vals)) )
{
CM *cm = (CM*)chan->cm;
cm->CauseValue = elem[3] & 0x7F;
auto_disc |= 2;
}
}
/* scan for signal */
if ( (elem = cm__q931_elem(msg->bufptr, msg->buflen, 0x34)) &&
(elem[1] == 1) )
{
// static CHAR signal_vals[] = { 0x00, 0x03, 0x04, 0x0C };
static CHAR signal_vals[] = { 0x03, 0x04, 0x0C };
D_LOG(D_ALWAYS, ("cm__org_elem_ind: signal: 0x%x", elem[2]));
if ( memchr(signal_vals, elem[2], sizeof(signal_vals)) )
{
CM *cm = (CM*)chan->cm;
cm->SignalValue = elem[2];
auto_disc |= 4;
}
}
/* check if need to disconnect */
if ( auto_disc )
{
D_LOG(D_ALWAYS, ("cm__org_elem_ind: auto_disc: 0x%x", auto_disc));
cm__disc_rq(chan);
}
return(CM_E_SUCC);
}
/* calc a checksum for a buffer */
UCHAR
cm__calc_chksum(VOID *buf_1, INT len)
{
UCHAR *buf = (UCHAR *)buf_1;
UCHAR sum;
for ( sum = 0 ; len ; len-- )
sum += *buf++;
return(sum);
}
/* increment a channel count */
BOOL
cm__inc_chan_num(CM_CHAN *chan, CM_CHAN *ref_chan, ULONG *chan_num)
{
/* find if this channel is part of same connection as ref_chan */
if ( memcmp(chan->DstAddr, ref_chan->DstAddr, 6) ||
(chan->remote_conn_index != ref_chan->remote_conn_index) )
return(TRUE);
/* inrement here */
*chan_num += 1;
return(TRUE);
}
/* add a channel to a connection */
BOOL
cm__add_chan(CM_CHAN *chan, CM_CHAN *ref_chan, CM *cm)
{
CM_CHAN *chan1;
/* if connection already full, stop here */
if ( cm->dprof.chan_num >= cm->active_chan_num )
return(FALSE);
/* find if this channel is part of same connection as ref_chan */
if ( memcmp(chan->DstAddr, ref_chan->DstAddr, 6) ||
(chan->remote_conn_index != ref_chan->remote_conn_index) )
return(TRUE);
/* add this channel */
chan1 = &cm->dprof.chan_tbl[cm->dprof.chan_num++];
*chan1 = *chan;
cm__chan_free(chan);
return(TRUE);
}