diff options
Diffstat (limited to 'private/ntos/ndis/pcimac/cm_q931.c')
-rw-r--r-- | private/ntos/ndis/pcimac/cm_q931.c | 950 |
1 files changed, 950 insertions, 0 deletions
diff --git a/private/ntos/ndis/pcimac/cm_q931.c b/private/ntos/ndis/pcimac/cm_q931.c new file mode 100644 index 000000000..4bd3ef014 --- /dev/null +++ b/private/ntos/ndis/pcimac/cm_q931.c @@ -0,0 +1,950 @@ +/* + * CM_Q931.C - q931 handling module. mainly outgoing side + */ + +#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> + +extern ULONG SwitchStyle; + +/* local assist, copy data into buffer & advance pointer */ +#define adv_ptr(_p, _buf, _len) \ + { \ + NdisMoveMemory((_p), _buf, _len); \ + (_p) += _len; \ + } + +/* format an establish request */ +INT +cm__est_rq(CM_CHAN *chan) +{ + IDD_MSG msg; + UCHAR *p; + + D_LOG(D_ENTRY, ("cm__est_rq: entry, chan: 0x%p", chan)); + + /* clear outgoing message */ + NdisZeroMemory(&msg, sizeof(msg)); + + /* allocate a local buffer */ + if ( !(msg.bufptr = p = ut_get_buf()) ) + goto give_up; + + // + // added to support the new switch styles + // TB 03/14 + // + if (SwitchStyle != CM_SWITCHSTYLE_1TR6) + { + /* build bearer capabilities element */ + switch ( chan->type ) + { + case CM_CT_VOICE : + adv_ptr(p, ((SwitchStyle == CM_SWITCHSTYLE_NET3) ? + "\x04\x03\x80\x90\xA3" : + "\x04\x03\x80\x90\xA2"), 5); + chan->speed = 56000; + break; + +// +// changed to make 56K Data calls in Europe get established with a bearer +// capability of 64K Data (8 bits) but we will actually do +// 56K Data (7 bits) on the line +// TB 09/20 +// +// case CM_CT_D56 : +// adv_ptr(p, "\x04\x04\x88\x90\x21\x8F", 6); +// chan->speed = 56000; +// break; +// + case CM_CT_D56 : + if (SwitchStyle == CM_SWITCHSTYLE_NET3) + { + adv_ptr(p, "\x04\x02\x88\x90", 4); + } + else + { + adv_ptr(p, "\x04\x04\x88\x90\x21\x8F", 6); + } + + chan->speed = 56000; + break; + + default : + case CM_CT_D64 : + adv_ptr(p, "\x04\x02\x88\x90", 4); + chan->speed = 64000; + break; + } + } + + /* channel id element */ + switch ( chan->bchan ) + { + case CM_BCHAN_B1 : adv_ptr(p, "\x18\x01\x89", 3); + break; + case CM_BCHAN_B2 : adv_ptr(p, "\x18\x01\x8A", 3); + break; + default : + case CM_BCHAN_ANY : adv_ptr(p, "\x18\x01\x83", 3); + break; + } + + // + // added to support the new switch styles + // TB 03/14 + // + if (SwitchStyle != CM_SWITCHSTYLE_1TR6 + && SwitchStyle != CM_SWITCHSTYLE_NET3) + { + /* called number/address */ + *p++ = 0x2C; + *p++ = strlen(chan->addr); + adv_ptr(p, chan->addr, p[-1]); + } + else + { + // + // added to support the new switch styles + // TB 03/14 + // + *p++ = 0x70; + *p++ = strlen(chan->addr) + 1; + *p++ = (SwitchStyle == CM_SWITCHSTYLE_1TR6) ? 0x81 : 0x80; + adv_ptr(p, chan->addr, strlen(chan->addr)); + } + + // + // added for net3 fix + // TB 04/13 + // + if (SwitchStyle == CM_SWITCHSTYLE_NET3) + *p++ = 0xA1; // Sending Complete + + // + // added to support the new switch styles + // TB 03/14 + // + if (SwitchStyle == CM_SWITCHSTYLE_1TR6) + { + *p++ = 0x96; + *p++ = 0x01; + switch (chan->type) + { + case CM_CT_VOICE: + adv_ptr(p, "\x02\x01\x01", 3); + break; + + case CM_CT_D56: + case CM_CT_D64: + default: + adv_ptr(p, "\x02\x07\x00", 3); + break; + } + } + + /* fillin message structure */ + msg.opcode = Q931_EST_RQ; + msg.buflen = p - msg.bufptr; + msg.bufid = MAKELONG(chan->cid, 0); + chan->cid = 0; + + /* send to idd */ + if ( idd_send_msg(chan->idd, &msg, (USHORT)CM_PORT(chan), + (VOID*)cm__q931_cmpl_handler, chan) != IDD_E_SUCC ) + { + /* failed, give up on channel */ + give_up: + chan->gave_up = 1; + ut_free_buf(msg.bufptr); + return(CM_E_IDD); + } + + D_LOG(D_EXIT, ("cm__est_rq: exit")); + return(CM_E_SUCC); +} + +/* format an establish response */ +INT +cm__est_rsp(CM_CHAN *chan) +{ + IDD_MSG msg; + + D_LOG(D_ENTRY, ("cm__est_rsp: entry, chan: 0x%p", chan)); + + /* clear outgoing message */ + NdisZeroMemory(&msg, sizeof(msg)); + + /* fillin message structure */ + msg.opcode = Q931_EST_RSP; + msg.bufid = MAKELONG(0, chan->cid); + msg.bufptr = ut_get_buf(); + msg.buflen = 0; + + + /* send to idd */ + if (idd_send_msg(chan->idd, &msg, (USHORT)CM_PORT(chan), (VOID*)cm__q931_cmpl_handler, NULL) != IDD_E_SUCC) + ut_free_buf(msg.bufptr); + + return(CM_E_SUCC); +} + +/* format a call ignore response */ +INT +cm__est_ignore( + PVOID idd, + USHORT cid, + USHORT lterm) +{ + IDD_MSG msg; + + D_LOG(D_ENTRY, ("cm__est_ignore: entry, idd: 0x%p, cid: 0x%x, lterm: 0x%x", idd, cid, lterm)); + + /* clear outgoing message */ + NdisZeroMemory(&msg, sizeof(msg)); + + /* fillin message structure */ + msg.opcode = Q931_EST_IGNORE; + msg.bufid = MAKELONG(0, cid); + msg.bufptr = ut_get_buf(); + msg.buflen = 0; + + + /* send to idd */ + if (idd_send_msg(idd, &msg, (USHORT)(lterm + IDD_PORT_CM0_TX), (VOID*)cm__q931_cmpl_handler, NULL) != IDD_E_SUCC) + ut_free_buf(msg.bufptr); + + return(CM_E_SUCC); +} + +/* format a disconenct request */ +INT +cm__disc_rq(CM_CHAN *chan) +{ + IDD_MSG msg; + + D_LOG(D_ENTRY, ("cm__disc_rq: entry, chan: 0x%p", chan)); + + /* clear outgoing message */ + NdisZeroMemory(&msg, sizeof(msg)); + + /* fillin message structure */ + msg.opcode = Q931_REL_RQ; + msg.bufid = MAKELONG(0, chan->cid); + msg.bufptr = ut_get_buf(); + msg.buflen = 0; + + /* send to idd */ + if (idd_send_msg(chan->idd, &msg, (USHORT)CM_PORT(chan), (VOID*)cm__q931_cmpl_handler, NULL) != IDD_E_SUCC) + ut_free_buf(msg.bufptr); + + + /* turn off channel */ + cm__bchan_ctrl(chan, 0); + return(CM_E_SUCC); +} + +/* control data transfer on a bchannel */ +INT +cm__bchan_ctrl(CM_CHAN *chan, BOOL turn_on) +{ + USHORT is_ans, subchan, op; + IDD_MSG msg; + CM *cm = chan->cm; + ULONG IddFramingType = IDD_FRAME_DETECT; + + D_LOG(D_ENTRY, ("cm__bchan_ctrl: entry, chan: 0x%p, state: %d", \ + chan, turn_on)); + + // + // check for a redundant operation + // + if ((!chan->active && !turn_on) || + (chan->active && turn_on)) + return(CM_E_SUCC); + + /* channel must be assigned */ + if ( !CM_BCHAN_ASSIGNED(chan->bchan) ) + return(CM_E_BADCHAN); + + /* find out if on answering side */ + if ( cm ) + is_ans = cm->was_listen ? 0x0100 : 0x0000; + else + is_ans = 0x0100; + + /* map channel type to operation */ + if ( !turn_on ) + op = CMD_BCHAN_OFF; + else + { + switch ( chan->type ) + { + case CM_CT_VOICE : op = CMD_BCHAN_VOICE; break; + case CM_CT_D56 : op = CMD_BCHAN_56; break; + default : + case CM_CT_D64 : op = CMD_BCHAN_HDLC; break; + } + + chan->speed = cm__type2speed(chan->type); + } + + /* build subchannel descriptor */ + subchan = 0x1717; + + /* build msg */ + NdisZeroMemory(&msg, sizeof(msg)); + msg.opcode = op; + msg.bufid = MAKELONG(is_ans | chan->bchan | (subchan << 1), 0); + + /* send it */ + idd_send_msg(chan->idd, &msg, IDD_PORT_CMD_TX, NULL, NULL); + + // + // Set rx framing mode + // + // if channel is being turned off + // + // channel off - rxmode = IDD_FRAME_DONTCARE + // + if (!turn_on) + IddFramingType = IDD_FRAME_DONTCARE; + + IddSetRxFraming(chan->idd, chan->bchan, IddFramingType); + + /* mark channel active state */ + chan->active = turn_on; + + return(CM_E_SUCC); +} + + +/* control data transfer on a bchannel */ +INT +cm__bchan_ctrl_comp(CM_CHAN *chan, ULONG CompressionFlag) +{ + IDD_MSG msg; + USHORT Enable = 0; + + D_LOG(D_ENTRY, ("cm__bchan_ctrl_comp: entry, chan: 0x%p, state: %d", \ + chan, CompressionFlag)); + + /* channel must be assigned */ + if ( !CM_BCHAN_ASSIGNED(chan->bchan) ) + return(CM_E_BADCHAN); + + /* map channel type to operation */ + if ( CompressionFlag ) + Enable = COMP_TX_ENA | COMP_RX_ENA; + + /* build msg */ + NdisZeroMemory(&msg, sizeof(msg)); + msg.opcode = CMD_COMPRESS; + msg.bufid = MAKELONG( chan->bchan | ( Enable << 8 ), 0); + + /* send it */ + idd_send_msg(chan->idd, &msg, IDD_PORT_CMD_TX, NULL, NULL); + + return(CM_E_SUCC); +} + + +/* issue an element request to idp cm port */ +INT +cm__elem_rq(VOID *idd, USHORT port, CHAR *elem_buf, USHORT elem_len) +{ + IDD_MSG msg; + CHAR *p; + + D_LOG(D_ENTRY, ("cm__elem_rq: entry, idd: 0x%p, port: 0x%d, elem_buf: 0x%p, elem_len: 0x%d", \ + idd, port, elem_buf, elem_len)); + + /* clear outgoing message */ + NdisZeroMemory(&msg, sizeof(msg)); + + /* allocate a local buffer */ + if ( !(msg.bufptr = p = ut_get_buf()) ) + return(CM_E_NOMEM); + + /* copy buffer */ + adv_ptr(p, elem_buf, (INT)elem_len); + + /* fillin message structure */ + msg.opcode = Q931_ELEM_RQ; + msg.buflen = p - msg.bufptr; + + /* send to idd */ + if ( idd_send_msg(idd, &msg, port, + (VOID*)cm__q931_cmpl_handler, NULL) != IDD_E_SUCC ) + { + ut_free_buf(msg.bufptr); + return(CM_E_IDD); + } + + return(CM_E_SUCC); +} + +/* completion handler for q931 command with attached local buffers */ +VOID +cm__q931_cmpl_handler(VOID *arg, USHORT port, IDD_MSG *msg) +{ + D_LOG(D_ENTRY, ("cm__q931_cmpl_handler: arg: 0x%p, port: 0x%d, msg: 0x%p", \ + arg, port, msg)); + + /* free attached buffer */ + ut_free_buf(msg->bufptr); +} + +/* handler for q931 events */ +VOID +cm__q931_handler(IDD *idd, USHORT port, ULONG Reserved, IDD_MSG *msg) +{ + USHORT lterm; + CM_CHAN *chan; + CM *cm; + extern BOOL cm_terminated; + + D_LOG(D_ENTRY, ("cm__q931_handler: entry, idd: 0x%p, port: %d, msg: 0x%p", \ + idd, port, msg)); + D_LOG(D_ALWAYS, ("cm_q931_handler: msg->opcode: 0x%x", msg->opcode)); + + /* ignore if already terminated */ + if ( cm_terminated ) + return; + + /* convert port to logical terminal */ + lterm = port - IDD_PORT_CM0_RX; + + /* try resolving idd/lterm/cid into a channel */ + if ( chan = cm__map_chan(idd, lterm, HIWORD(msg->bufid)) ) + cm = chan->cm; + else + cm = NULL; + + D_LOG(D_ALWAYS, ("cm_q931_handler: chan: 0x%p, cm: 0x%p", chan, cm)); + + // + // since q.931 stuff touches this so much it is easier to + // copy locally then to keep track of adapter memory access + // + NdisZeroMemory(idd->RxBuffer, sizeof(idd->RxBuffer)); + IddGetDataFromAdapter(idd, + (PUCHAR)idd->RxBuffer, + (PUCHAR)msg->bufptr, + (USHORT)MIN(IDP_MAX_RX_BUFFER, msg->buflen)); + + msg->bufptr = idd->RxBuffer; + + /* switch to message handler */ + switch ( msg->opcode ) + { + case Q931_EST_IND : + cm__ans_est_ind(chan, (IDD_MSG*)msg, idd, lterm); + break; + + case Q931_CID_IND : + cm__org_cid_ind(chan, (IDD_MSG*)msg); + break; + + case Q931_P_STATE_IND: + D_LOG(D_ALWAYS, ("cm_q931_handler: ProtocolStateInd")); + break; + + case Q931_STATE_IND : + if ( !chan || cm ) + cm__org_state_ind(chan, (IDD_MSG*)msg); + else if ( chan ) + cm__ans_state_ind(chan, (IDD_MSG*)msg); + break; + + case Q931_ELEM_IND : + if ( cm && !cm->was_listen ) + cm__org_elem_ind(chan, (IDD_MSG*)msg); + break; + + default : + break; + } +} + +VOID +cm__ppp_conn(VOID *idd, USHORT port) +{ + CM_CHAN *chan; + CM *cm; + ULONG n, CompressionFlag; + IDD_MSG msg1; + + /* try resolving idd/bchan into a channel */ + if ( !(chan = cm__map_bchan_chan(idd, port)) ) + return; + + // + // if this channel is already connected no need to do this stuff + // + if (chan->ustate == CM_US_CONN) + return; + + // + // 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); + + NdisZeroMemory(chan->DstAddr, sizeof(chan->DstAddr)); + + /* last channel, find matching connection/profile */ + if ( !(cm = cm__find_listen_conn("*", "*", "*", idd)) ) + { + /* none found, reject */ + D_LOG(D_ALWAYS, ("cm__ppp_con: no listener found")); + return; + } + + /* matching connection found!, fillin */ + D_LOG(D_ALWAYS, ("cm__ppp_conn: matching connection: cm: 0x%p", cm)); + + chan->remote_conn_index = 1; + + cm->state = CM_ST_IN_ANS; + cm->StateChangeFlag = TRUE; + cm->was_listen = TRUE; + cm->active_chan_num = 1; + cm->remote_conn_index = chan->remote_conn_index; + cm->ConnectionType = CM_PPP; + NdisMoveMemory(cm->DstAddr, chan->DstAddr, 6); + NdisZeroMemory(cm->remote_name, 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; + + /* 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 *chan1 = cm->dprof.chan_tbl + n; + + chan1->ustate = CM_US_CONN; + chan1->timeout = ut_time_now(); + chan1->num = (USHORT)n; + chan1->cm = cm; + chan1->active = TRUE; + } + + /* make connection active */ + cm->state = CM_ST_ACTIVE; + cm->StateChangeFlag = TRUE; + + // Set compression Flag + CompressionFlag = 0; + + cm__activate_conn(cm, CompressionFlag); + + return; +} + + +/* handler for bchannel data */ +VOID +cm__q931_bchan_handler( + IDD *idd, + USHORT port, + ULONG RxFrameType, + IDD_XMSG *msg + ) +{ + CM_CHAN *chan; + IDD_MSG msg1; + UCHAR DetectBytes[2]; + extern BOOL cm_terminated; + + D_LOG(D_ENTRY, ("cm__q931_bchan_handler: entry, idd: 0x%p, port: %d, msg: 0x%p", \ + idd, port, msg)); + + /* ignore if terminated */ + if ( cm_terminated ) + return; + + // + // check to see if this port is servicing DKF or PPP + // + if (RxFrameType != IDD_FRAME_DKF) + return; + + // + // see if this is really uus or dror data + // if not uus we don't want to do all of the copying that we + // would do for uus + // + IddGetDataFromAdapter(idd, + (PUCHAR)&DetectBytes, + (PUCHAR)msg->bufptr, + 2); + +// NdisMoveMemory((PUCHAR)&DetectBytes, (PUCHAR)msg->bufptr, 2 * sizeof(UCHAR)); + + if ( (msg->buflen < 4) || DetectBytes[0] != DKF_UUS_SIG || DetectBytes[1]) + return; + + // + // since uus stuff touches this so much it is easier to + // copy locally then to keep track of adapter memory access + // + NdisZeroMemory(idd->RxBuffer, sizeof(idd->RxBuffer)); + IddGetDataFromAdapter(idd, + (PUCHAR)idd->RxBuffer, + (PUCHAR)msg->bufptr, + (USHORT)MIN(IDP_MAX_RX_BUFFER, msg->buflen)); + + msg->bufptr = idd->RxBuffer; + + D_LOG(D_ALWAYS, ("cm__q931_bchan_handler: msg->buflen: 0x%x, DetectByte[0]: 0x%x", \ + msg->buflen, DetectBytes[0])); + + /* try resolving idd/bchan into a channel */ + if ( !(chan = cm__map_bchan_chan(idd, port)) ) + return; + + // + // make a copy of the message without the header or + // fragmentation flags + // + NdisMoveMemory(&msg1, msg, sizeof(IDD_MSG)); + msg1.bufptr += 4; + msg1.buflen -= 4; + + /* call handler */ + if ( chan->cm && !((CM*)(chan->cm))->was_listen ) + cm__org_data_ind(chan, &msg1); + else + cm__ans_data_ind(chan, &msg1); +} + +/* transmit a uus packet */ +INT +cm__tx_uus_pkt(CM_CHAN *chan, UCHAR opcode, UCHAR cause) +{ + CHAR *p; + IDD_MSG msg; + CM_UUS *uus; + CM *cm = chan->cm; + + /* must have a channel at this time */ + if ( !CM_BCHAN_ASSIGNED(chan->bchan) ) + return(CM_E_BADCHAN); + + /* allocate a buffer for uus */ + if ( !(p = ut_get_buf()) ) + return(CM_E_NOMEM); + + /* init messages structure */ + NdisZeroMemory(&msg, sizeof(msg)); + msg.bufptr = p; + msg.buflen = 4 + CM_UUS_SIZE; + + /* build frame header */ + *p++ = 0x50; + *p++ = 0x00; + *p++ = 0x00; + *p++ = 0x00; + uus = (CM_UUS*)p; + + /* init uus */ + NdisZeroMemory(uus, sizeof(*uus)); + NdisMoveMemory(uus->dst_addr, "\xff\xff\xff\xff\xff\xff", 6); + + if ( cm ) + NdisMoveMemory(uus->src_addr, cm->SrcAddr, sizeof(uus->src_addr)); + uus->pkt_type = CM_PKT_TYPE; + uus->prot_desc = CM_PROT_DESC; + uus->opcode = opcode; + uus->cause = cause; + uus->option_0 = 0; + + if (cm) + if (cm->dprof.HWCompression) + uus->option_0 = UUS_0_COMPRESSION; + + /* install connection fields */ + uus->conn = cm ? cm->local_conn_index : 0; + uus->channum = cm ? (UCHAR)cm->dprof.chan_num : 0; + uus->chan = (UCHAR)chan->num; + if ( cm ) + { + NdisMoveMemory(uus->lname, cm->dprof.name, sizeof(uus->lname)); + NdisMoveMemory(uus->rname, cm->dprof.remote_name, sizeof(uus->rname)); + cm->tx_last_frame_time = ut_time_now(); + } + + /* calc chksum */ + uus->chksum = 256 - cm__calc_chksum(uus, CM_UUS_SIZE); + + /* send message to idd */ + if ( idd_send_msg(chan->idd, &msg, chan->bchan, (VOID*)cm__q931_cmpl_handler, chan) + != IDD_E_SUCC ) + ut_free_buf(msg.bufptr); + + return(CM_E_SUCC); +} + +/* get channel identification out of q931 element buffer */ +INT +cm__get_bchan(IDD_MSG *msg, USHORT *bchan) +{ + UCHAR *elem; + + + /* locate channel id element */ + if ( !(elem = cm__q931_elem(msg->bufptr, msg->buflen, 0x18)) ) + return(CM_E_NOSUCH); + else + elem++; + + /* verify length */ + if ( *elem++ != 0x1 ) + return(CM_E_NOSUCH); + + /* extract b channel */ + if ( *elem == 0x89 ) + *bchan = CM_BCHAN_B1; + else if ( *elem == 0x8A ) + *bchan = CM_BCHAN_B2; + else + return(CM_E_NOSUCH); + + /* if here, succ */ + return(CM_E_SUCC); +} + +/* get channel type out of q931 element buffer */ +INT +cm__get_type(IDD_MSG *msg, USHORT *type) +{ + UCHAR *elem, elem_len; + + /* locate type element */ + if ( !(elem = cm__q931_elem(msg->bufptr, msg->buflen, 0x04)) ) + return(CM_E_NOSUCH); + else + elem++; + elem_len = *elem++; + + // + // added to support the new switch styles + // TB 03/14 + // + if (SwitchStyle == CM_SWITCHSTYLE_1TR6) + { + switch (*elem) + { + case 0x01: + *type = CM_CT_VOICE; + return(CM_E_SUCC); + + case 0x07: + *type = CM_CT_D64; + return(CM_E_SUCC); + + default: + return(CM_E_BADPARAM); + } + } + + /* if information transfer type is speech, -> voice */ + if ( (*elem++ & 0x1F) == 0 ) + { + *type = CM_CT_VOICE; + return(CM_E_SUCC); + } + + /* trasnfer mode & type must be 64 */ + if ( (*elem++ & 0x7F) != 0x10 ) + return(CM_E_BADPARAM); + + /* if end of element here, must be 64 */ + if ( elem_len == 2 ) + { + *type = CM_CT_D64; + return(CM_E_SUCC); + } + + /* check for 56 */ + if ( (elem_len >= 4) && + ((*elem++ & 0x7F) == 0x21) && + ((*elem++ & 0x7F) == 0x0F) ) + { + *type = CM_CT_D56; + return(CM_E_SUCC); + } + + /* if here, unknown */ + return(CM_E_BADPARAM); +} + +/* get caller address out of q931 element buffer */ +INT +cm__get_addr(IDD_MSG *msg, CHAR addr[32]) +{ + UCHAR *elem, elem_len; + + /* locate type element */ + if ( !(elem = cm__q931_elem(msg->bufptr, msg->buflen, 0x6C)) ) + return(CM_E_NOSUCH); + else + elem++; + +// Subtracting 1 looks like a mistake +// TB 11.09.93 +// if ( (elem_len = *elem++ - 1) > 32 ) +// elem_len = 31; + if ( (elem_len = *elem++) > 32 ) + elem_len = 31; + + if (elem_len < 2) + return(CM_E_NOSUCH); + + elem += 2; + elem_len -= 2; + + /* copy in & terminate */ + NdisMoveMemory (addr, elem, elem_len); + addr[elem_len] = '\0'; + + return(CM_E_SUCC); +} + +/* scan q931 element buffer for a specific element */ +UCHAR* +cm__q931_elem(VOID *ptr_1, INT len, UCHAR elem) +{ + UCHAR *ptr = (UCHAR*)ptr_1; + CHAR codeset = 0; /* starting with code set 0 */ + CHAR prev_codeset; /* saving area for prev. codeset */ + CHAR locked = 1; /* locked/nonlocked codeset shift */ + + /* loop while length left */ + while ( len > 0 ) + { + /* handle shifting codesets */ + if ( (*ptr & 0xF0) == 0x90 /*Q931_IE0_SHIFT*/ ) + { + prev_codeset = codeset; /* save current code set */ + codeset = *ptr & 0x07; /* extract new codeset */ + locked = !(*ptr & 0x08); /* ... and locking status */ + ptr++; /* move past shift element */ + len--; + continue; + } + + /* check for codeset 0 */ + if ( codeset != 0 ) /* non codeset0 elements, just skip */ + { + if ( *ptr & 0x80 ) + { + ptr++; + len--; + } + // + // added to support the new switch styles + // TB 03/14 + // + else if (SwitchStyle == CM_SWITCHSTYLE_1TR6 && + elem == 0x04 && + *ptr == 0x01) + { + return(ptr); + } + else + { + len -= (2 + ptr[1]); + ptr += (ptr[1] + 2); + } + + if ( !locked ) + { + codeset = prev_codeset; + locked = 1; + } + + continue; /* move to next element */ + } + + /* try to match elem from codeset 0 */ + if ( *ptr & 0x80 ) /* single octet elem? */ + { /* yes */ + if ( (((elem & 0xF0) == 0xA0) && (elem == (UCHAR)*ptr)) || + (((elem & 0x80) == 0x80) && (elem == (UCHAR)(*ptr & 0xF0))) ) + { /* element found */ + return(ptr); + } + else + { + ptr++; /* skip this elem */ + len--; + } + } + else + { + if ( *ptr == elem ) + { /* multi byte elem match */ + return(ptr); + } + else + { + len -= (2 + ptr[1]); + ptr += (ptr[1] + 2); + } + } + + /* resert codeset if not locked */ + if ( !locked ) + { + codeset = prev_codeset; + locked = 1; + } + } + + /* if here, not found */ + return(NULL); +} + +/* convert channel type to speed */ +ULONG +cm__type2speed(USHORT type) +{ + switch ( type ) + { + case CM_CT_VOICE : + case CM_CT_D56 : + return(56000); + + case CM_CT_D64 : + default : + return(64000); + } +} |