/*****************************************************************************

 @(#) $Id: sls_codec.c,v 0.7.2.5 2001/02/03 05:48:53 brian Test $

 -----------------------------------------------------------------------------

 Copyright (C) 1997-2001  Brian Bidulock <bidulock@dallas.net>

 All Rights Reserved.

 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free Software
 Foundation; either version 2 of the License, or (at your option) any later
 version.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 details.

 You should have received a copy of the GNU General Public License along with
 this program; if not, write to the Free Software Foundation, Inc., 675 Mass
 Ave, Cambridge, MA 02139, USA.

 -----------------------------------------------------------------------------

 Last Modified $Date: 2001/02/03 05:48:53 $ by $Author: brian $

 *****************************************************************************/

#include <ss7/slsi.h>
#include "sls_parse.h"
#include "sls_codec.h"

/*
 *  This is a decoder (parser) and encoder (builder) for SS7 MTP messages.  It
 *  is used to abstract the actual line coding of the SS7 message by placing
 *  it into a common internal representation in a message buffer.
 */

static int ls_itut_build_msu(mblk_t *mp)
{
    mblk_t *md = mp->b_cont;
    ls_prim_t *p = (ls_prim_t *)mp->b_rptr;
    mtp_msg_t *m = (mtp_msg_t *)md->b_rptr;
    m->itut.mh.ni     = p->sig.mh.ni;
    m->itut.mh.mp     = p->sig.mh.mp;
    m->itut.mh.si     = p->sig.mh.si;
    m->itut.mh.rl.dpc = p->sig.mh.rl.dpc;
    m->itut.mh.rl.opc = p->sig.mh.rl.opc;
    m->itut.mh.rl.sls = p->sig.mh.rl.sls;
    switch ( p->sig.signal ) {
        case LS_SIGNAL_COO:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x1;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x1;
            m->itut.msg.coo.fsnc = p->coo.fsnc;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.coo);
            break;
        case LS_SIGNAL_COA:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x1;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x2;
            m->itut.msg.coa.fsnc = p->coa.fsnc;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.coa);
            break;
        case LS_SIGNAL_CBD:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x1;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x5;
            m->itut.msg.cbd.cbc = p->cbd.cbc;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.cbd);
            break;
        case LS_SIGNAL_CBA:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x1;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x6;
            m->itut.msg.cba.cbc = p->cba.cbc;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.cba);
            break;
        case LS_SIGNAL_ECO:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x2;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x1;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.eco);
            break;
        case LS_SIGNAL_ECA:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x2;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x2;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.eca);
            break;
        case LS_SIGNAL_RCT:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x3;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x1;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.rct);
            break;
        case LS_SIGNAL_TFC:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x3;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x2;
            m->itut.msg.tfc.dest = p->tfc.dest;
            m->itut.msg.tfc.stat = p->tfc.stat;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.tfc);
            break;
        case LS_SIGNAL_TFP:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x4;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x1;
            m->itut.msg.tfp.dest = p->tfp.dest;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.tfp);
            break;
        case LS_SIGNAL_TFR:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x4;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x3;
            m->itut.msg.tfr.dest = p->tfr.dest;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.tfr);
            break;
        case LS_SIGNAL_TFA:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x4;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x5;
            m->itut.msg.tfa.dest = p->tfa.dest;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.tfa);
            break;
        case LS_SIGNAL_RSP:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x5;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x1;
            m->itut.msg.rsp.dest = p->rsp.dest;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.rsp);
            break;
        case LS_SIGNAL_RSR:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x5;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x2;
            m->itut.msg.rsr.dest = p->rsr.dest;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.rsr);
            break;
        case LS_SIGNAL_LIN:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x6;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x1;
            m->itut.mh.rl.sls = p->lin.slc;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.lin);
            break;
        case LS_SIGNAL_LUN:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x6;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x2;
            m->itut.mh.rl.sls = p->lun.slc;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.lun);
            break;
        case LS_SIGNAL_LIA:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x6;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x3;
            m->itut.mh.rl.sls = p->lia.slc;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.lia);
            break;
        case LS_SIGNAL_LUA:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x6;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x4;
            m->itut.mh.rl.sls = p->lua.slc;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.lua);
            break;
        case LS_SIGNAL_LID:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x6;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x5;
            m->itut.mh.rl.sls = p->lid.slc;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.lid);
            break;
        case LS_SIGNAL_LFU:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x6;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x6;
            m->itut.mh.rl.sls = p->lfu.slc;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.lfu);
            break;
        case LS_SIGNAL_LLI:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x6;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x7;
            m->itut.mh.rl.sls = p->lli.slc;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.lli);
            break;
        case LS_SIGNAL_LRI:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x6;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x8;
            m->itut.mh.rl.sls = p->lri.slc;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.lri);
            break;
        case LS_SIGNAL_TRA:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x7;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x1;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.tra);
            break;
        case LS_SIGNAL_DLC:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x8;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x1;
            m->itut.msg.dlc.sdli = p->dlc.sdli;
            m->itut.mh.rl.sls = p->css.slc;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.dlc);
            break;
        case LS_SIGNAL_CSS:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x8;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x2;
            m->itut.mh.rl.sls = p->css.slc;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.css);
            break;
        case LS_SIGNAL_CNS:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x8;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x3;
            m->itut.mh.rl.sls = p->cns.slc;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.cns);
            break;
        case LS_SIGNAL_CNP:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x8;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x4;
            m->itut.mh.rl.sls = p->cnp.slc;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.cnp);
            break;
        case LS_SIGNAL_UPU:
            m->itut.mh.si = p->sig.mh.si = 0x0;
            m->itut.mh.h0 = p->sig.mh.h0 = 0xa;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x1;
            m->itut.msg.upu.dest = p->upu.dest;
            m->itut.msg.upu.upi  = p->upu.upi;
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.upu);
            break;
        case LS_SIGNAL_SLTM:
            m->itut.mh.si = p->sig.mh.si = 0x1;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x1;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x1;
            m->itut.mh.rl.sls = p->sltm.slc;
            m->itut.msg.sltm.tli = p->sltm.tli;
            bcopy(p->sltm.tmsx, m->itut.msg.sltm.tmsx, p->sltm.tli & 0xf);
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.sltm) - 15 + (p->sltm.tli & 0xf);
            break;
        case LS_SIGNAL_SLTA:
            m->itut.mh.si = p->sig.mh.si = 0x1;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x1;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x2;
            m->itut.mh.rl.sls = p->slta.slc;
            m->itut.msg.slta.tli = p->slta.tli;
            bcopy(p->slta.tmsx, m->itut.msg.slta.tmsx, p->slta.tli & 0xf);
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.slta) - 15 + (p->slta.tli & 0xf);
            break;
        case LS_SIGNAL_SSLTM:
            m->itut.mh.si = p->sig.mh.si = 0x2;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x1;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x1;
            m->itut.mh.rl.sls = p->ssltm.slc;
            m->itut.msg.ssltm.tli = p->ssltm.tli;
            bcopy(p->ssltm.tmsx, m->itut.msg.ssltm.tmsx, p->ssltm.tli & 0xf);
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.ssltm) - 15 + (p->ssltm.tli & 0xf);
            break;
        case LS_SIGNAL_SSLTA:
            m->itut.mh.si = p->sig.mh.si = 0x2;
            m->itut.mh.h0 = p->sig.mh.h0 = 0x1;
            m->itut.mh.h1 = p->sig.mh.h1 = 0x2;
            m->itut.mh.rl.sls = p->sslta.slc;
            m->itut.msg.sslta.tli = p->sslta.tli;
            bcopy(p->sslta.tmsx, m->itut.msg.sslta.tmsx, p->sslta.tli & 0xf);
            md->b_wptr = md->b_rptr + sizeof(m->itut.msg.sslta) - 15 + (p->sslta.tli & 0xf);
            break;
        case LS_SIGNAL_USER:
            md->b_wptr = md->b_rptr + sizeof(m->itut.mh) - 1;
            return(0);
        default:
            return -EINVAL;
    }
    m->itut.mh.mp = p->sig.mh.mp = 0x3;
    mp->b_wptr = mp->b_rptr + sizeof(p->primitive);
    p->primitive = SL_PDU_REQ;
    return(0);
}

static int ls_ansi_build_msu(mblk_t *mp)
{
    mblk_t *md = mp->b_cont;
    ls_prim_t *p = (ls_prim_t *)mp->b_rptr;
    mtp_msg_t *m = (mtp_msg_t *)md->b_rptr;
    m->ansi.mh.ni     = p->sig.mh.ni;
    m->ansi.mh.mp     = p->sig.mh.mp;
    m->ansi.mh.si     = p->sig.mh.si;
    m->ansi.mh.rl.dpc = p->sig.mh.rl.dpc;
    m->ansi.mh.rl.opc = p->sig.mh.rl.opc;
    m->ansi.mh.rl.sls = p->sig.mh.rl.sls;
    switch ( p->sig.signal ) {
        case LS_SIGNAL_COO:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x1;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x1;
            m->ansi.msg.coo.slc  = p->coo.slc;
            m->ansi.msg.coo.fsnc = p->coo.fsnc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.coo);
            break;
        case LS_SIGNAL_COA:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x1;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x2;
            m->ansi.msg.coa.slc  = p->coa.slc;
            m->ansi.msg.coa.fsnc = p->coa.fsnc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.coa);
            break;
        case LS_SIGNAL_CBD:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x1;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x5;
            m->ansi.msg.cbd.slc = p->cbd.slc;
            m->ansi.msg.cbd.cbc = p->cbd.cbc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.cbd);
            break;
        case LS_SIGNAL_CBA:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x1;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x6;
            m->ansi.msg.cba.slc = p->cba.slc;
            m->ansi.msg.cba.cbc = p->cba.cbc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.cba);
            break;
        case LS_SIGNAL_ECO:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x2;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x1;
            m->ansi.msg.eco.slc = p->eco.slc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.eco);
            break;
        case LS_SIGNAL_ECA:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x2;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x2;
            m->ansi.msg.eca.slc = p->eca.slc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.eca);
            break;
        case LS_SIGNAL_RCT:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x3;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x1;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.rct);
            break;
        case LS_SIGNAL_TFC:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x3;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x2;
            m->ansi.msg.tfc.dest = p->tfc.dest;
            m->ansi.msg.tfc.stat = p->tfc.stat;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.tfc);
            break;
        case LS_SIGNAL_TFP:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x4;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x1;
            m->ansi.msg.tfp.dest = p->tfp.dest;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.tfp);
            break;
        case LS_SIGNAL_TFR:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x4;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x3;
            m->ansi.msg.tfr.dest = p->tfr.dest;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.tfr);
            break;
        case LS_SIGNAL_TFA:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x4;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x5;
            m->ansi.msg.tfa.dest = p->tfa.dest;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.tfa);
            break;
        case LS_SIGNAL_RSP:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x5;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x1;
            m->ansi.msg.rsp.dest = p->rsp.dest;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.rsp);
            break;
        case LS_SIGNAL_RSR:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x5;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x2;
            m->ansi.msg.rsr.dest = p->rsr.dest;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.rsr);
            break;
        case LS_SIGNAL_LIN:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x6;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x1;
            m->ansi.msg.lin.slc = p->lin.slc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.lin);
            break;
        case LS_SIGNAL_LUN:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x6;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x2;
            m->ansi.msg.lun.slc = p->lun.slc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.lun);
            break;
        case LS_SIGNAL_LIA:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x6;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x3;
            m->ansi.msg.lia.slc = p->lia.slc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.lia);
            break;
        case LS_SIGNAL_LUA:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x6;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x4;
            m->ansi.msg.lua.slc = p->lua.slc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.lua);
            break;
        case LS_SIGNAL_LID:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x6;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x5;
            m->ansi.msg.lid.slc = p->lid.slc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.lid);
            break;
        case LS_SIGNAL_LFU:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x6;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x6;
            m->ansi.msg.lfu.slc = p->lfu.slc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.lfu);
            break;
        case LS_SIGNAL_LLI:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x6;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x7;
            m->ansi.msg.lli.slc = p->lli.slc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.lli);
            break;
        case LS_SIGNAL_LRI:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x6;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x8;
            m->ansi.msg.lri.slc = p->lri.slc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.lri);
            break;
        case LS_SIGNAL_TRA:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x7;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x1;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.tra);
            break;
        case LS_SIGNAL_DLC:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x8;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x1;
            m->ansi.msg.dlc.sdli = p->dlc.sdli;
            m->ansi.msg.dlc.slc  = p->dlc.slc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.dlc);
            break;
        case LS_SIGNAL_CSS:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x8;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x2;
            m->ansi.msg.css.slc  = p->css.slc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.css);
            break;
        case LS_SIGNAL_CNS:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x8;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x3;
            m->ansi.msg.cns.slc  = p->cns.slc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.cns);
            break;
        case LS_SIGNAL_CNP:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x8;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x4;
            m->ansi.msg.cnp.slc  = p->cnp.slc;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.cnp);
            break;
        case LS_SIGNAL_UPU:
            m->ansi.mh.si = p->sig.mh.si = 0x0;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0xa;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x1;
            m->ansi.msg.upu.dest = p->upu.dest;
            m->ansi.msg.upu.upi  = p->upu.upi;
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.upu);
            break;
        case LS_SIGNAL_SLTM:
            m->ansi.mh.si = p->sig.mh.si = 0x1;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x1;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x1;
            m->ansi.msg.sltm.slc = p->sltm.slc;
            m->ansi.msg.sltm.tli = p->sltm.tli;
            bcopy(p->sltm.tmsx, m->ansi.msg.sltm.tmsx, p->sltm.tli & 0xf);
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.sltm) - 15 + (p->sltm.tli & 0xf);
            break;
        case LS_SIGNAL_SLTA:
            m->ansi.mh.si = p->sig.mh.si = 0x1;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x1;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x2;
            m->ansi.msg.slta.slc = p->slta.slc;
            m->ansi.msg.slta.tli = p->slta.tli;
            bcopy(p->slta.tmsx, m->ansi.msg.slta.tmsx, p->slta.tli & 0xf);
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.slta) - 15 + (p->slta.tli & 0xf);
            break;
        case LS_SIGNAL_SSLTM:
            m->ansi.mh.si = p->sig.mh.si = 0x2;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x1;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x1;
            m->ansi.msg.ssltm.slc = p->ssltm.slc;
            m->ansi.msg.ssltm.tli = p->ssltm.tli;
            bcopy(p->ssltm.tmsx, m->ansi.msg.ssltm.tmsx, p->ssltm.tli & 0xf);
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.ssltm) - 15 + (p->ssltm.tli & 0xf);
            break;
        case LS_SIGNAL_SSLTA:
            m->ansi.mh.si = p->sig.mh.si = 0x2;
            m->ansi.mh.h0 = p->sig.mh.h0 = 0x1;
            m->ansi.mh.h1 = p->sig.mh.h1 = 0x2;
            m->ansi.msg.sslta.slc = p->sslta.slc;
            m->ansi.msg.sslta.tli = p->sslta.tli;
            bcopy(p->sslta.tmsx, m->ansi.msg.sslta.tmsx, p->sslta.tli & 0xf);
            md->b_wptr = md->b_rptr + sizeof(m->ansi.msg.sslta) - 15 + (p->sslta.tli & 0xf);
            break;
        case LS_SIGNAL_USER:
            md->b_wptr = md->b_rptr + sizeof(m->ansi.mh) - 1;
            return(0);
        default:
            return -EINVAL;
    }
    m->ansi.mh.mp = p->sig.mh.mp = 0x3;
    mp->b_wptr = mp->b_rptr + sizeof(p->primitive);
    p->primitive = SL_PDU_REQ;
    return(0);
}

static int (*ls_pvar_build_msu[])(mblk_t *) =
{
    ls_itut_build_msu, /* SS7_PVAR_ITUT_88 ITU-T protocol variant */
    ls_itut_build_msu, /* SS7_PVAR_ITUT_93 ITU-T protocol variant */
    ls_itut_build_msu, /* SS7_PVAR_ITUT_96 ITU-T protocol variant */
    ls_itut_build_msu, /* SS7_PVAR_ITUT_00 ITU-T protocol variant */
    ls_ansi_build_msu, /* SS7_PVAR_ANSI_88 ANSI  protocol variant */
    ls_ansi_build_msu, /* SS7_PVAR_ANSI_92 ANSI  protocol variant */
    ls_ansi_build_msu, /* SS7_PVAR_ANSI_96 ANSI  protocol variant */
    ls_ansi_build_msu, /* SS7_PVAR_ANSI_00 ANSI  protocol variant */
    ls_itut_build_msu, /* SS7_PVAR_ETSI_88 ETSI  protocol variant */
    ls_itut_build_msu, /* SS7_PVAR_ETSI_93 ETSI  protocol variant */
    ls_itut_build_msu, /* SS7_PVAR_ETSI_96 ETSI  protocol variant */
    ls_itut_build_msu, /* SS7_PVAR_ETSI_00 ETSI  protocol variant */
    ls_ansi_build_msu  /* SS7_PVAR_JTTC_94 JTTC  protocol variant */
};

int ls_build_msu(ls_t *ls, mblk_t *mp)
{
    /*
     *  Takes the message (M_PROTO) as provided for in the message block in mp
     *  and encodes it into an MSU frame in attached (M_DATA).  Attached
     *  M_DATA must be large enough to hold the resulting frame.  The M_PROTO
     *  message block is converted to a generic MSU primitive block.
     *
     *  Get it right: send a M_PROTO block followed by an M_DATA block.  I do
     *  very little error checking here for speed.
     *
     *  The array of protocol variants (pvar[4]) is used to determine which
     *  protocol variant to use for which network indicators.
     */
    int var = ls->option.pvar;
    mblk_t *md = mp->b_cont;

    if ( !md )
        return -EINVAL;

    if ( 0 <= var && var <= SS7_PVAR_MAX )
        return ls_pvar_build_msu[var](mp);

    return -EINVAL;
}

static int ls_itut_parse_msu(mblk_t *mp)
{
    mblk_t *md = mp->b_cont;
    ls_prim_t *p = (ls_prim_t *)mp->b_rptr;
    mtp_msg_t *m = (mtp_msg_t *)md->b_rptr;
    p->sig.mh.ni     = m->itut.mh.ni;
    p->sig.mh.mp     = m->itut.mh.mp;
    p->sig.mh.si     = m->itut.mh.si;
    p->sig.mh.rl.dpc = m->itut.mh.rl.dpc;
    p->sig.mh.rl.opc = m->itut.mh.rl.opc;
    p->sig.mh.rl.sls = m->itut.mh.rl.sls;
    switch ( p->sig.mh.si ) {
        case 0x0:   /* snmm */
            p->sig.mh.h0 = m->itut.mh.h0;
            p->sig.mh.h1 = m->itut.mh.h1;
            switch ( p->sig.mh.h0 ) {
                case 0x1: /* chm */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* coo - changeover-order */
                            p->coo.slc  = m->itut.mh.rl.sls;
                            p->coo.fsnc = m->itut.msg.coo.fsnc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_COO_SIZE;
                            p->primitive = LS_SIGNAL_COO;
                            break;
                        case 0x2: /* coa - changeover-ack */
                            p->coa.slc  = m->itut.mh.rl.sls;
                            p->coa.fsnc = m->itut.msg.coa.fsnc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_COA_SIZE;
                            p->primitive = LS_SIGNAL_COA;
                            break;
                        case 0x5: /* cbd - changeback-declaration */
                            p->cbd.slc  = m->itut.mh.rl.sls;
                            p->cbd.cbc = m->itut.msg.cbd.cbc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_CBD_SIZE;
                            p->primitive = LS_SIGNAL_CBD;
                            break;
                        case 0x6: /* cba - changeback-ack */
                            p->cba.slc  = m->itut.mh.rl.sls;
                            p->cba.cbc = m->itut.msg.cba.cbc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_CBA_SIZE;
                            p->primitive = LS_SIGNAL_CBA;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                case 0x2: /* ecm */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* eco - emergency-changeover-order */
                            p->eco.slc  = m->itut.mh.rl.sls;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_ECO_SIZE;
                            p->primitive = LS_SIGNAL_ECO;
                            break;
                        case 0x2: /* eca - emergency-changeover-ack */
                            p->eca.slc  = m->itut.mh.rl.sls;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_ECA_SIZE;
                            p->primitive = LS_SIGNAL_ECA;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                case 0x3: /* fcm */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* rct - signalling-route-set-congestion-test */
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_RCT_SIZE;
                            p->primitive = LS_SIGNAL_RCT;
                            break;
                        case 0x2: /* tfc - transfer-controlled */
                            p->tfc.dest = m->itut.msg.tfc.dest;
                            p->tfc.stat = m->itut.msg.tfc.stat;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_TFC_SIZE;
                            p->primitive = LS_SIGNAL_TFC;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                case 0x4: /* tfm */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* tfp - transfer prohibited */
                            p->tfp.dest = m->itut.msg.tfp.dest;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_TFP_SIZE;
                            p->primitive = LS_SIGNAL_TFP;
                            break;
                        case 0x3: /* tfr - transfer-restricted */
                            p->tfr.dest = m->itut.msg.tfr.dest;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_TFR_SIZE;
                            p->primitive = LS_SIGNAL_TFR;
                            break;
                        case 0x5: /* tfa - transfer-allowed */
                            p->tfa.dest = m->itut.msg.tfa.dest;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_TFA_SIZE;
                            p->primitive = LS_SIGNAL_TFA;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                case 0x5: /* rsm */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* rsp - signalling-route-set-test-prohibited */
                            p->rsp.dest = m->itut.msg.rsp.dest;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_RSP_SIZE;
                            p->primitive = LS_SIGNAL_TFA;
                            break;
                        case 0x2: /* rsr - signalling-route-set-test-restricted */
                            p->rsr.dest = m->itut.msg.rsr.dest;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_RSR_SIZE;
                            p->primitive = LS_SIGNAL_RSR;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                case 0x6: /* mim */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* lin - link-inhibit */
                            p->lin.slc = m->itut.mh.rl.sls;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_LIN_SIZE;
                            p->primitive = LS_SIGNAL_LIN;
                            break;
                        case 0x2: /* lun - link-uninhibit */
                            p->lun.slc = m->itut.mh.rl.sls;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_LUN_SIZE;
                            p->primitive = LS_SIGNAL_LUN;
                            break;
                        case 0x3: /* lia - link-inhibit-ack */
                            p->lia.slc = m->itut.mh.rl.sls;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_LIA_SIZE;
                            p->primitive = LS_SIGNAL_LIA;
                            break;
                        case 0x4: /* lua - link-uninhibit-ack */
                            p->lua.slc = m->itut.mh.rl.sls;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_LUA_SIZE;
                            p->primitive = LS_SIGNAL_LUA;
                            break;
                        case 0x5: /* lid - link-inhibit-denied */
                            p->lid.slc = m->itut.mh.rl.sls;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_LID_SIZE;
                            p->primitive = LS_SIGNAL_LID;
                            break;
                        case 0x6: /* lfu - force-uninhibit */
                            p->lfu.slc = m->itut.mh.rl.sls;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_LFU_SIZE;
                            p->primitive = LS_SIGNAL_LFU;
                            break;
                        case 0x7: /* llt - local-inhibit-test */
                            p->lli.slc = m->itut.mh.rl.sls;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_LLI_SIZE;
                            p->primitive = LS_SIGNAL_LLI;
                            break;
                        case 0x8: /* lrt - remote-inhibit-test */
                            p->lri.slc = m->itut.mh.rl.sls;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_LRI_SIZE;
                            p->primitive = LS_SIGNAL_LRI;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                case 0x7: /* trm */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* tra - traffic-restart-allowed */
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_TRA_SIZE;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                case 0x8: /* dlm */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* dlc - signalling-data-link-connection-order */
                            p->dlc.sdli = m->itut.msg.dlc.sdli;
                            p->dlc.slc  = m->itut.mh.rl.sls;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_DLC_SIZE;
                            p->primitive = LS_SIGNAL_DLC;
                            break;
                        case 0x2: /* css - connection-successful */
                            p->css.slc  = m->itut.mh.rl.sls;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_CSS_SIZE;
                            p->primitive = LS_SIGNAL_CSS;
                            break;
                        case 0x3: /* cns - connection-not-successful */
                            p->cns.slc  = m->itut.mh.rl.sls;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_CNS_SIZE;
                            p->primitive = LS_SIGNAL_CNS;
                            break;
                        case 0x4: /* cnp - connection-not-possible */
                            p->cnp.slc  = m->itut.mh.rl.sls;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_CNP_SIZE;
                            p->primitive = LS_SIGNAL_CNP;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                case 0xa: /* ufc */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* upu - user part unavailable */
                            p->upu.dest = m->itut.msg.upu.dest;
                            p->upu.upi  = m->itut.msg.upu.upi;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_UPU_SIZE;
                            p->primitive = LS_SIGNAL_UPU;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                default:
                    return -EINVAL;
            }
            break;
        case 0x1:   /* sltc */
            p->sig.mh.h0 = m->itut.mh.h0;
            p->sig.mh.h1 = m->itut.mh.h1;
            switch ( p->sig.mh.h0 ) {
                case 0x1:
                    switch (p->sig.mh.h1) {
                        case 0x1: /* sltm */
                            p->sltm.slc  = m->itut.mh.rl.sls;
                            p->sltm.tli = m->itut.msg.sltm.tli;
                            bcopy(m->itut.msg.sltm.tmsx, p->sltm.tmsx, p->sltm.tli);
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_SLTM_SIZE - 15 + p->sltm.tli;
                            p->primitive = LS_SIGNAL_SLTM;
                            break;
                        case 0x2: /* slta */
                            p->slta.slc  = m->itut.mh.rl.sls;
                            p->slta.tli = m->itut.msg.slta.tli;
                            bcopy(m->itut.msg.slta.tmsx, p->slta.tmsx, p->slta.tli);
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_SLTM_SIZE - 15 + p->slta.tli;
                            p->primitive = LS_SIGNAL_SLTA;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                default:
                    return -EINVAL;
            }
            break;
        case 0x2:   /* ssltc */
            p->sig.mh.h0 = m->itut.mh.h0;
            p->sig.mh.h1 = m->itut.mh.h1;
            switch ( p->sig.mh.h0 ) {
                case 0x1:
                    switch (p->sig.mh.h1) {
                        case 0x1: /* ssltm */
                            p->ssltm.slc  = m->itut.mh.rl.sls;
                            p->ssltm.tli = m->itut.msg.ssltm.tli;
                            bcopy(m->itut.msg.ssltm.tmsx, p->ssltm.tmsx, p->ssltm.tli);
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_SLTM_SIZE - 15 + p->ssltm.tli;
                            p->primitive = LS_SIGNAL_SSLTM;
                            break;
                        case 0x2: /* sslta */
                            p->sslta.slc  = m->itut.mh.rl.sls;
                            p->sslta.tli = m->itut.msg.sslta.tli;
                            bcopy(m->itut.msg.sslta.tmsx, p->sslta.tmsx, p->sslta.tli);
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_SLTM_SIZE - 15 + p->sslta.tli;
                            p->primitive = LS_SIGNAL_SSLTA;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                default:
                    return -EINVAL;
            }
            break;
        default:
            mp->b_wptr = mp->b_rptr + LS_SIGNAL_USER_SIZE;
            md->b_rptr += sizeof(m->itut.mh) - 1; /* strip MTP header */
            p->primitive = LS_SIGNAL_USER;
            return(0);
    }
    mp->b_datap->db_type = M_PROTO;
    md = unlinkb(mp);
    freemsg(md);
    return(0);
}

static int ls_ansi_parse_msu(mblk_t *mp)
{
    mblk_t *md = mp->b_cont;
    ls_prim_t *p = (ls_prim_t *)mp->b_rptr;
    mtp_msg_t *m = (mtp_msg_t *)md->b_rptr;
    p->sig.mh.ni     = m->ansi.mh.ni;
    p->sig.mh.mp     = m->ansi.mh.mp;
    p->sig.mh.si     = m->ansi.mh.si;
    p->sig.mh.rl.dpc = m->ansi.mh.rl.dpc;
    p->sig.mh.rl.opc = m->ansi.mh.rl.opc;
    p->sig.mh.rl.sls = m->ansi.mh.rl.sls;
    switch ( p->sig.mh.si ) {
        case 0x0:   /* snmm */
            p->sig.mh.h0 = m->ansi.mh.h0;
            p->sig.mh.h1 = m->ansi.mh.h1;
            switch ( p->sig.mh.h0 ) {
                case 0x1: /* chm */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* coo - changeover-order */
                            p->coo.slc  = m->ansi.msg.coo.slc;
                            p->coo.fsnc = m->ansi.msg.coo.fsnc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_COO_SIZE;
                            p->primitive = LS_SIGNAL_COO;
                            break;
                        case 0x2: /* coa - changeover-ack */
                            p->coa.slc  = m->ansi.msg.coa.slc;
                            p->coa.fsnc = m->ansi.msg.coa.fsnc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_COA_SIZE;
                            p->primitive = LS_SIGNAL_COA;
                            break;
                        case 0x5: /* cbd - changeback-declaration */
                            p->cbd.slc = m->ansi.msg.cbd.slc;
                            p->cbd.cbc = m->ansi.msg.cbd.cbc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_CBD_SIZE;
                            p->primitive = LS_SIGNAL_CBD;
                            break;
                        case 0x6: /* cba - changeback-ack */
                            p->cba.slc = m->ansi.msg.cba.slc;
                            p->cba.cbc = m->ansi.msg.cba.cbc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_CBA_SIZE;
                            p->primitive = LS_SIGNAL_CBA;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                case 0x2: /* ecm */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* eco - emergency-changeover-order */
                            p->eco.slc = m->ansi.msg.eco.slc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_ECO_SIZE;
                            p->primitive = LS_SIGNAL_ECO;
                            break;
                        case 0x2: /* eca - emergency-changeover-ack */
                            p->eca.slc = m->ansi.msg.eca.slc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_ECA_SIZE;
                            p->primitive = LS_SIGNAL_ECA;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                case 0x3: /* fcm */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* rct - signalling-route-set-congestion-test */
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_RCT_SIZE;
                            p->primitive = LS_SIGNAL_RCT;
                            break;
                        case 0x2: /* tfc - transfer-controlled */
                            p->tfc.dest = m->ansi.msg.tfc.dest;
                            p->tfc.stat = m->ansi.msg.tfc.stat;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_TFC_SIZE;
                            p->primitive = LS_SIGNAL_TFC;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                case 0x4: /* tfm */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* tfp - transfer-prohibited */
                            p->tfp.dest = m->ansi.msg.tfp.dest;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_TFP_SIZE;
                            p->primitive = LS_SIGNAL_TFP;
                            break;
                        case 0x2: /* tcp - transfer-cluster-prohibited */
                            p->tcp.dest = m->ansi.msg.tcp.dest;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_TCP_SIZE;
                            p->primitive = LS_SIGNAL_TCP;
                            break;
                        case 0x3: /* tfr - transfer-restricted */
                            p->tfr.dest = m->ansi.msg.tfr.dest;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_TFR_SIZE;
                            p->primitive = LS_SIGNAL_TFR;
                            break;
                        case 0x4: /* tcr - transfer-cluster-restricted */
                            p->tcr.dest = m->ansi.msg.tcr.dest;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_TCR_SIZE;
                            p->primitive = LS_SIGNAL_TCR;
                            break;
                        case 0x5: /* tfa - transfer-allowed */
                            p->tfa.dest = m->ansi.msg.tfa.dest;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_TFA_SIZE;
                            p->primitive = LS_SIGNAL_TFA;
                            break;
                        case 0x6: /* tca - transfer-cluster-allowed */
                            p->tca.dest = m->ansi.msg.tca.dest;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_TCA_SIZE;
                            p->primitive = LS_SIGNAL_TCA;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                case 0x5: /* rsm */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* rsp - signalling-route-set-test-prohibited */
                            p->rsp.dest = m->ansi.msg.rsp.dest;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_RSP_SIZE;
                            p->primitive = LS_SIGNAL_RSP;
                            break;
                        case 0x2: /* rsr - signalling-route-set-test-restricted */
                            p->rsr.dest = m->ansi.msg.rsr.dest;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_RSR_SIZE;
                            p->primitive = LS_SIGNAL_RSR;
                            break;
                        case 0x3: /* rcp - signalling-route-set-test-cluster-prohibited */
                            p->rcp.dest = m->ansi.msg.rcp.dest;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_RCP_SIZE;
                            p->primitive = LS_SIGNAL_RCP;
                            break;
                        case 0x4: /* rcr - signalling-route-set-test-cluster-restricted */
                            p->rcr.dest = m->ansi.msg.rcr.dest;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_RCR_SIZE;
                            p->primitive = LS_SIGNAL_RCR;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                case 0x6: /* mim */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* lin - link-inhibit */
                            p->lin.slc = m->ansi.msg.lin.slc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_LIN_SIZE;
                            p->primitive = LS_SIGNAL_LIN;
                            break;
                        case 0x2: /* lun - link-uninhibit */
                            p->lun.slc = m->ansi.msg.lun.slc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_LUN_SIZE;
                            p->primitive = LS_SIGNAL_LUN;
                            break;
                        case 0x3: /* lia - link-inhibit-ack */
                            p->lia.slc = m->ansi.msg.lia.slc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_LIA_SIZE;
                            p->primitive = LS_SIGNAL_LIA;
                            break;
                        case 0x4: /* lua - link-uninhibit-ack */
                            p->lua.slc = m->ansi.msg.lua.slc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_LUA_SIZE;
                            p->primitive = LS_SIGNAL_LUA;
                            break;
                        case 0x5: /* lid - link-inhibit-denied */
                            p->lid.slc = m->ansi.msg.lid.slc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_LID_SIZE;
                            p->primitive = LS_SIGNAL_LID;
                            break;
                        case 0x6: /* lfu - force-uninhibit */
                            p->lfu.slc = m->ansi.msg.lfu.slc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_LFU_SIZE;
                            p->primitive = LS_SIGNAL_LFU;
                            break;
                        case 0x7: /* lli - local-inhibit-test */
                            p->lli.slc = m->ansi.msg.lli.slc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_LLI_SIZE;
                            p->primitive = LS_SIGNAL_LLI;
                            break;
                        case 0x8: /* lri - remote-inhibit-test */
                            p->lri.slc = m->ansi.msg.lri.slc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_LRI_SIZE;
                            p->primitive = LS_SIGNAL_LRI;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                case 0x7: /* trm */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* tra - traffic-restart-allowed */
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_TRA_SIZE;
                            break;
                        case 0x2: /* trw - traffic-restart-waiting */
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_TRW_SIZE;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                case 0x8: /* dlm */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* dlc - signalling-data-link-connection-order */
                            p->dlc.sdli = m->ansi.msg.dlc.sdli;
                            p->dlc.slc  = m->ansi.msg.dlc.slc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_DLC_SIZE;
                            p->primitive = LS_SIGNAL_DLC;
                            break;
                        case 0x2: /* css - connection-successful */
                            p->css.slc  = m->ansi.msg.css.slc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_CSS_SIZE;
                            p->primitive = LS_SIGNAL_CSS;
                            break;
                        case 0x3: /* cns - connection-not-successful */
                            p->cns.slc  = m->ansi.msg.cns.slc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_CNS_SIZE;
                            p->primitive = LS_SIGNAL_CNS;
                            break;
                        case 0x4: /* cnp - connection-not-possible */
                            p->cnp.slc  = m->ansi.msg.cnp.slc;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_CNP_SIZE;
                            p->primitive = LS_SIGNAL_CNP;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                case 0xa: /* ufc */
                    switch (p->sig.mh.h1) {
                        case 0x1: /* upu - user part unavailable */
                            p->upu.dest = m->ansi.msg.upu.dest;
                            p->upu.upi  = m->ansi.msg.upu.upi;
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_UPU_SIZE;
                            p->primitive = LS_SIGNAL_UPU;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                default:
                    return -EINVAL;
            }
            break;
        case 0x1:   /* sltc */
            p->sig.mh.h0 = m->ansi.mh.h0;
            p->sig.mh.h1 = m->ansi.mh.h1;
            switch ( p->sig.mh.h0 ) {
                case 0x1:
                    switch (p->sig.mh.h1) {
                        case 0x1: /* sltm */
                            p->sltm.slc = m->ansi.msg.sltm.slc;
                            p->sltm.tli = m->ansi.msg.sltm.tli;
                            bcopy(m->ansi.msg.sltm.tmsx, p->sltm.tmsx, p->sltm.tli);
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_SLTM_SIZE - 15 + p->sltm.tli;
                            p->primitive = LS_SIGNAL_SLTM;
                            break;
                        case 0x2: /* slta */
                            p->slta.slc = m->ansi.msg.slta.slc;
                            p->slta.tli = m->ansi.msg.slta.tli;
                            bcopy(m->ansi.msg.slta.tmsx, p->slta.tmsx, p->slta.tli);
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_SLTM_SIZE - 15 + p->slta.tli;
                            p->primitive = LS_SIGNAL_SLTA;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                default:
                    return -EINVAL;
            }
            break;
        case 0x2:   /* ssltc */
            p->sig.mh.h0 = m->ansi.mh.h0;
            p->sig.mh.h1 = m->ansi.mh.h1;
            switch ( p->sig.mh.h0 ) {
                case 0x1:
                    switch (p->sig.mh.h1) {
                        case 0x1: /* ssltm */
                            p->ssltm.slc = m->ansi.msg.ssltm.slc;
                            p->ssltm.tli = m->ansi.msg.ssltm.tli;
                            bcopy(m->ansi.msg.ssltm.tmsx, p->ssltm.tmsx, p->ssltm.tli);
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_SLTM_SIZE - 15 + p->ssltm.tli;
                            p->primitive = LS_SIGNAL_SSLTM;
                            break;
                        case 0x2: /* sslta */
                            p->sslta.slc = m->ansi.msg.sslta.slc;
                            p->sslta.tli = m->ansi.msg.sslta.tli;
                            bcopy(m->ansi.msg.sslta.tmsx, p->sslta.tmsx, p->sslta.tli);
                            mp->b_wptr = mp->b_rptr + LS_SIGNAL_SLTM_SIZE - 15 + p->sslta.tli;
                            p->primitive = LS_SIGNAL_SSLTA;
                            break;
                        default:
                            return -EINVAL;
                    }
                    break;
                default:
                    return -EINVAL;
            }
            break;
        default:
            mp->b_wptr = mp->b_rptr + LS_SIGNAL_USER_SIZE;
            md->b_rptr += sizeof(m->ansi.mh) - 1; /* strip MTP header */
            p->primitive = LS_SIGNAL_USER;
            return(0);
    }
    mp->b_datap->db_type = M_PROTO;
    md = unlinkb(mp);
    freemsg(md);
    return(0);
}

static int (*ls_pvar_parse_msu[])(mblk_t *) =
{
    ls_itut_parse_msu, /* SS7_PVAR_ITUT_88 ITU-T protocol variant */
    ls_itut_parse_msu, /* SS7_PVAR_ITUT_93 ITU-T protocol variant */
    ls_itut_parse_msu, /* SS7_PVAR_ITUT_96 ITU-T protocol variant */
    ls_itut_parse_msu, /* SS7_PVAR_ITUT_00 ITU-T protocol variant */
    ls_ansi_parse_msu, /* SS7_PVAR_ANSI_88 ANSI  protocol variant */
    ls_ansi_parse_msu, /* SS7_PVAR_ANSI_92 ANSI  protocol variant */
    ls_ansi_parse_msu, /* SS7_PVAR_ANSI_96 ANSI  protocol variant */
    ls_ansi_parse_msu, /* SS7_PVAR_ANSI_00 ANSI  protocol variant */
    ls_itut_parse_msu, /* SS7_PVAR_ETSI_88 ETSI  protocol variant */
    ls_itut_parse_msu, /* SS7_PVAR_ETSI_93 ETSI  protocol variant */
    ls_itut_parse_msu, /* SS7_PVAR_ETSI_96 ETSI  protocol variant */
    ls_itut_parse_msu, /* SS7_PVAR_ETSI_00 ETSI  protocol variant */
    ls_ansi_parse_msu  /* SS7_PVAR_JTTC_94 JTTC  protocol variant */
};

int ls_parse_msu(ls_t *ls, mblk_t *mp)
{
    /*
     *  Takes the MSU (M_DATA) as provided for in the message block (M_DATA)
     *  and decodes it into a message block in mp (M_PROTO).
     *
     *  Get it right: send a M_PROTO block followed by an M_DATA block.  I do
     *  very little error checking here for speed.
     *
     *  The array of protocol variants (pvar[4]) is used to determine which
     *  protocol variant to use for which network indicators.
     */
    int var = ls->option.pvar;
    mblk_t *md = mp->b_cont;

    if ( !md )
        return -EINVAL;

    if ( 0 <= var && var <= SS7_PVAR_MAX )
        return ls_pvar_parse_msu[var](mp);

    return -EINVAL;
}

