/*
 * $Id: convert.c,v 1.9 1999/12/06 17:08:30 calle Exp $
 *
 * $Log: convert.c,v $
 * Revision 1.9  1999/12/06 17:08:30  calle
 * - Splitted capi20.h into capi20.h and capiutils.h.
 *   - capi20.h: the functions from the CAPI-2.0 Spec
 *   - capiutils.h: the "CAPI-ADK" functions
 * - bug in 64Bit-Support fixed.
 *
 * Revision 1.8  1999/10/20 16:43:17  calle
 * - The CAPI20 library is now a shared library.
 * - Arguments of function capi20_put_message swapped, to match capi spec.
 * - All capi20 related subdirs converted to use automake.
 * - Removed dependency to CONFIG_KERNELDIR where not needed.
 *
 * Revision 1.7  1999/09/15 08:10:44  calle
 * Bugfix: error in 64Bit extention.
 *
 * Revision 1.6  1999/09/10 17:20:33  calle
 * Last changes for proposed standards (CAPI 2.0):
 * - AK1-148 "Linux Extention"
 * - AK1-155 "Support of 64-bit Applications"
 *
 * Revision 1.5  1999/09/06 17:40:07  calle
 * Changes for CAPI 2.0 Spec.
 *
 * Revision 1.4  1998/10/23 12:20:44  fritz
 * Added some missing functions.
 *
 * Revision 1.3  1998/08/30 09:57:21  calle
 * I hope it is know readable for everybody.
 *
 * Revision 1.1  1998/08/25 16:33:23  calle
 * Added CAPI2.0 library. First Version.
 *
 */
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <stddef.h>
#include <time.h>
#include <ctype.h>

#include "capi20.h"

typedef struct {
    int typ;
    unsigned off;
} _cdef;

#define _CBYTE	       1
#define _CWORD	       2
#define _CDWORD        3
#define _CQWORD        4
#define _CSTRUCT       5
#define _CMSTRUCT      6
#define _CEND	       7

static _cdef cdef[] = {
    /*00*/{_CEND},
    /*01*/{_CEND},
    /*02*/{_CEND},
    /*03*/{_CDWORD,     offsetof(_cmsg,adr.adrController)	},
    /*04*/{_CMSTRUCT,   offsetof(_cmsg,AdditionalInfo)	},
    /*05*/{_CSTRUCT,    offsetof(_cmsg,B1configuration)	},
    /*06*/{_CWORD,      offsetof(_cmsg,B1protocol)	},
    /*07*/{_CSTRUCT,    offsetof(_cmsg,B2configuration)	},
    /*08*/{_CWORD,      offsetof(_cmsg,B2protocol)	},
    /*09*/{_CSTRUCT,    offsetof(_cmsg,B3configuration)	},
    /*0a*/{_CWORD,      offsetof(_cmsg,B3protocol)	},
    /*0b*/{_CSTRUCT,    offsetof(_cmsg,BC)	},
    /*0c*/{_CSTRUCT,    offsetof(_cmsg,BChannelinformation)	},
    /*0d*/{_CMSTRUCT,   offsetof(_cmsg,BProtocol)	},
    /*0e*/{_CSTRUCT,    offsetof(_cmsg,CalledPartyNumber)	},
    /*0f*/{_CSTRUCT,    offsetof(_cmsg,CalledPartySubaddress)	},
    /*10*/{_CSTRUCT,    offsetof(_cmsg,CallingPartyNumber)	},
    /*11*/{_CSTRUCT,    offsetof(_cmsg,CallingPartySubaddress)	},
    /*12*/{_CDWORD,     offsetof(_cmsg,CIPmask)	},
    /*13*/{_CDWORD,     offsetof(_cmsg,CIPmask2)	},
    /*14*/{_CWORD,      offsetof(_cmsg,CIPValue)	},
    /*15*/{_CDWORD,     offsetof(_cmsg,Class)	},
    /*16*/{_CSTRUCT,    offsetof(_cmsg,ConnectedNumber)	},
    /*17*/{_CSTRUCT,    offsetof(_cmsg,ConnectedSubaddress)	},
    /*18*/{_CDWORD,     offsetof(_cmsg,Data32)	},
    /*19*/{_CWORD,      offsetof(_cmsg,DataHandle)	},
    /*1a*/{_CWORD,      offsetof(_cmsg,DataLength)	},
    /*1b*/{_CSTRUCT,    offsetof(_cmsg,FacilityConfirmationParameter)	},
    /*1c*/{_CSTRUCT,    offsetof(_cmsg,Facilitydataarray)	},
    /*1d*/{_CSTRUCT,    offsetof(_cmsg,FacilityIndicationParameter)	},
    /*1e*/{_CSTRUCT,    offsetof(_cmsg,FacilityRequestParameter)	},
    /*1f*/{_CSTRUCT,    offsetof(_cmsg,FacilityResponseParameters)	},
    /*20*/{_CWORD,      offsetof(_cmsg,FacilitySelector)	},
    /*21*/{_CWORD,      offsetof(_cmsg,Flags)	},
    /*22*/{_CDWORD,     offsetof(_cmsg,Function)	},
    /*23*/{_CSTRUCT,    offsetof(_cmsg,HLC)	},
    /*24*/{_CWORD,      offsetof(_cmsg,Info)	},
    /*25*/{_CSTRUCT,    offsetof(_cmsg,InfoElement)	},
    /*26*/{_CDWORD,     offsetof(_cmsg,InfoMask)	},
    /*27*/{_CWORD,      offsetof(_cmsg,InfoNumber)	},
    /*28*/{_CSTRUCT,    offsetof(_cmsg,Keypadfacility)	},
    /*29*/{_CSTRUCT,    offsetof(_cmsg,LLC)	},
    /*2a*/{_CSTRUCT,    offsetof(_cmsg,ManuData)	},
    /*2b*/{_CDWORD,     offsetof(_cmsg,ManuID)	},
    /*2c*/{_CSTRUCT,    offsetof(_cmsg,NCPI)	},
    /*2d*/{_CWORD,      offsetof(_cmsg,Reason)	},
    /*2e*/{_CWORD,      offsetof(_cmsg,Reason_B3)	},
    /*2f*/{_CWORD,      offsetof(_cmsg,Reject)	},
    /*30*/{_CSTRUCT,    offsetof(_cmsg,Useruserdata)	},
    /*31*/{_CQWORD,     offsetof(_cmsg,Data64)	},
};

static unsigned char *cpars[] = {
    /*00*/ 0,
    /*01 ALERT_REQ*/
           (unsigned char*)"\x03\x04\x0c\x28\x30\x1c\x01\x01",
    /*02 CONNECT_REQ*/
          (unsigned char*)"\x03\x14\x0e\x10\x0f\x11\x0d\x06\x08\x0a\x05\x07\x09\x01\x0b\x29\x23\x04\x0c\x28\x30\x1c\x01\x01",
    /*03*/ 0,
    /*04 DISCONNECT_REQ*/
          (unsigned char*)"\x03\x04\x0c\x28\x30\x1c\x01\x01",
    /*05 LISTEN_REQ*/
          (unsigned char*)"\x03\x26\x12\x13\x10\x11\x01",
    /*06*/ 0,
    /*07*/ 0,
    /*08 INFO_REQ*/
          (unsigned char*)"\x03\x0e\x04\x0c\x28\x30\x1c\x01\x01",
    /*09 FACILITY_REQ*/
          (unsigned char*)"\x03\x20\x1e\x01",
    /*0a SELECT_B_PROTOCOL_REQ*/
          (unsigned char*)"\x03\x0d\x06\x08\x0a\x05\x07\x09\x01\x01",
    /*0b CONNECT_B3_REQ*/ 
          (unsigned char*)"\x03\x2c\x01",
    /*0c*/ 0,
    /*0d DISCONNECT_B3_REQ*/
          (unsigned char*)"\x03\x2c\x01",
    /*0e*/ 0,
    /*0f DATA_B3_REQ*/
          (unsigned char*)"\x03\x18\x1a\x19\x21\x31\x01",
    /*10 RESET_B3_REQ*/
          (unsigned char*)"\x03\x2c\x01",
    /*11*/ 0,
    /*12*/ 0,
    /*13 ALERT_CONF*/
          (unsigned char*)"\x03\x24\x01",
    /*14 CONNECT_CONF*/
          (unsigned char*)"\x03\x24\x01",
    /*15*/ 0,
    /*16 DISCONNECT_CONF*/
          (unsigned char*)"\x03\x24\x01",
    /*17 LISTEN_CONF*/
          (unsigned char*)"\x03\x24\x01",
    /*18 MANUFACTURER_REQ*/
          (unsigned char*)"\x03\x2b\x15\x22\x2a\x01",
    /*19*/ 0,
    /*1a INFO_CONF*/
          (unsigned char*)"\x03\x24\x01",
    /*1b FACILITY_CONF*/
          (unsigned char*)"\x03\x24\x20\x1b\x01",
    /*1c SELECT_B_PROTOCOL_CONF*/
          (unsigned char*)"\x03\x24\x01",
    /*1d CONNECT_B3_CONF*/
          (unsigned char*)"\x03\x24\x01",
    /*1e*/ 0,
    /*1f DISCONNECT_B3_CONF*/
          (unsigned char*)"\x03\x24\x01",
    /*20*/ 0,
    /*21 DATA_B3_CONF*/
          (unsigned char*)"\x03\x19\x24\x01",
    /*22 RESET_B3_CONF*/
          (unsigned char*)"\x03\x24\x01",
    /*23*/ 0,
    /*24*/ 0,
    /*25*/ 0,
    /*26 CONNECT_IND*/
          (unsigned char*)"\x03\x14\x0e\x10\x0f\x11\x0b\x29\x23\x04\x0c\x28\x30\x1c\x01\x01",
    /*27 CONNECT_ACTIVE_IND*/
          (unsigned char*)"\x03\x16\x17\x29\x01",
    /*28 DISCONNECT_IND*/
          (unsigned char*)"\x03\x2d\x01",
    /*29*/ 0,
    /*2a MANUFACTURER_CONF*/ 
          (unsigned char*)"\x03\x2b\x15\x22\x2a\x01",
    /*2b*/ 0,
    /*2c INFO_IND*/  
          (unsigned char*)"\x03\x27\x25\x01",
    /*2d FACILITY_IND*/ 
          (unsigned char*)"\x03\x20\x1d\x01",
    /*2e*/ 0,
    /*2f CONNECT_B3_IND*/
          (unsigned char*)"\x03\x2c\x01",
    /*30 CONNECT_B3_ACTIVE_IND*/
          (unsigned char*)"\x03\x2c\x01",
    /*31 DISCONNECT_B3_IND*/
          (unsigned char*)"\x03\x2e\x2c\x01",
    /*32*/ 0,
    /*33 DATA_B3_IND*/
          (unsigned char*)"\x03\x18\x1a\x19\x21\x31\x01",
    /*34 RESET_B3_IND*/ 
          (unsigned char*)"\x03\x2c\x01",
    /*35 CONNECT_B3_T90_ACTIVE_IND*/
          (unsigned char*)"\x03\x2c\x01",
    /*36*/ 0,
    /*37*/ 0,
    /*38 CONNECT_RESP*/
          (unsigned char*)"\x03\x2f\x0d\x06\x08\x0a\x05\x07\x09\x01\x16\x17\x29\x04\x0c\x28\x30\x1c\x01\x01",
    /*39 CONNECT_ACTIVE_RESP*/
          (unsigned char*)"\x03\x01",
    /*3a DISCONNECT_RESP*/
          (unsigned char*)"\x03\x01",
    /*3b*/ 0,
    /*3c MANUFACTURER_IND*/
          (unsigned char*)"\x03\x2b\x15\x22\x2a\x01",
    /*3d*/ 0,
    /*3e INFO_RESP*/ 
          (unsigned char*)"\x03\x01",
    /*3f FACILITY_RESP*/
          (unsigned char*)"\x03\x20\x1f\x01",
    /*40*/ 0,
    /*41 CONNECT_B3_RESP*/
          (unsigned char*)"\x03\x2f\x2c\x01",
    /*42 CONNECT_B3_ACTIVE_RESP*/
          (unsigned char*)"\x03\x01",
    /*43 DISCONNECT_B3_RESP*/
          (unsigned char*)"\x03\x01",
    /*44*/ 0,
    /*45 DATA_B3_RESP*/
          (unsigned char*)"\x03\x19\x01",
    /*46 RESET_B3_RESP*/
          (unsigned char*)"\x03\x01",
    /*47 CONNECT_B3_T90_ACTIVE_RESP*/
          (unsigned char*)"\x03\x01",
    /*48*/ 0,
    /*49*/ 0,
    /*4a*/ 0,
    /*4b*/ 0,
    /*4c*/ 0,
    /*4d*/ 0,
    /*4e MANUFACTURER_RESP*/
          (unsigned char*)"\x03\x2b\x15\x22\x2a\x01",
};

/*-------------------------------------------------------*/

#define byteTLcpy(x,y)        *(_cbyte *)(x)=*(_cbyte *)(y);
#define wordTLcpy(x,y)        *(_cword *)(x)=*(_cword *)(y);
#define dwordTLcpy(x,y)       memcpy(x,y,4);
#define qwordTLcpy(x,y)       memcpy(x,y,8);
#define structTLcpy(x,y,l)    memcpy (x,y,l)
#define structTLcpyovl(x,y,l) memmove (x,y,l)

#define byteTRcpy(x,y)        *(_cbyte *)(y)=*(_cbyte *)(x);
#define wordTRcpy(x,y)        *(_cword *)(y)=*(_cword *)(x);
#define dwordTRcpy(x,y)       memcpy(y,x,4);
#define qwordTRcpy(x,y)       memcpy(y,x,8);
#define structTRcpy(x,y,l)    memcpy (y,x,l)
#define structTRcpyovl(x,y,l) memmove (y,x,l)

/*-------------------------------------------------------*/
static unsigned command_2_index (unsigned c, unsigned sc) {
    if (c & 0x80) c = 0x9+(c&0x0f);
    else if (c<=0x0f) ;
    else if (c==0x41) c = 0x9+0x1;
    else if (c==0xff) c = 0x00;
    return (sc&3)*(0x9+0x9)+c;
}

/*-------------------------------------------------------*/
#define TYP (cdef[cmsg->par[cmsg->p]].typ)
#define OFF (((char *)cmsg)+cdef[cmsg->par[cmsg->p]].off)

static void jumpcstruct (_cmsg *cmsg) {
    unsigned layer;
    for (cmsg->p++,layer=1; layer;) {
	assert (cmsg->p);
	cmsg->p++;
	switch (TYP) {
	    case _CMSTRUCT:
		layer++;
		break;
	    case _CEND:
		layer--;
		break;
	}
    }
}
/*-------------------------------------------------------*/
static void PARS_2_MESSAGE (_cmsg *cmsg) {

    for (;TYP != _CEND; cmsg->p++) {
	switch (TYP) {
	    case _CBYTE:
		byteTLcpy (cmsg->m+cmsg->l, OFF);
		cmsg->l++;
		break;
	    case _CWORD:
		wordTLcpy (cmsg->m+cmsg->l, OFF);
		cmsg->l+=2;
		break;
	    case _CDWORD:
		dwordTLcpy (cmsg->m+cmsg->l, OFF);
		cmsg->l+=4;
		break;
	    case _CQWORD:
		qwordTLcpy (cmsg->m+cmsg->l, OFF);
		cmsg->l+=8;
		break;
	    case _CSTRUCT:
		if (*(CAPI_MESSAGE *) OFF == 0) {
		    *(cmsg->m+cmsg->l)='\0';
		    cmsg->l++;
		}
		else if(**(_cstruct*)OFF != 0xff) {
		    structTLcpy (cmsg->m+cmsg->l, *(_cstruct*)OFF, 1+**(_cstruct*)OFF);
		    cmsg->l+=1+**(_cstruct*)OFF;
		}
		else {
		    _cstruct s = *(_cstruct*)OFF;
		    structTLcpy (cmsg->m+cmsg->l, s, 3+*(_cword*)(s+1));
		    cmsg->l+= 3+*(_cword*)(s+1);
		}
		break;
	    case _CMSTRUCT:
		/*----- Metastruktur 0 -----*/
		if (*(_cmstruct*)OFF == CAPI_DEFAULT) {
		    *(cmsg->m+cmsg->l)='\0';
		    cmsg->l++;
		    jumpcstruct (cmsg);
		}
		/*----- Metastruktur wird composed -----*/
		else {
		    unsigned _l = cmsg->l;
		    unsigned _ls;
		    cmsg->l++;
		    cmsg->p++;
		    PARS_2_MESSAGE (cmsg);
		    _ls = cmsg->l-_l-1;
		    if (_ls < 255)
			(cmsg->m+_l)[0] = (_cbyte)_ls;
		    else {
			structTLcpyovl (cmsg->m+_l+3, cmsg->m+_l+1, _ls);
			(cmsg->m+_l)[0] = 0xff;
			wordTLcpy (cmsg->m+_l+1, &_ls);
                        cmsg->l+=2;
		    }
		}
		break;
	}
    }
}

/*-------------------------------------------------------*/
unsigned capi_cmsg2message (_cmsg *cmsg, CAPI_MESSAGE msg)
{
    cmsg->m = msg;
    cmsg->l = 8;
    cmsg->p = 0;
    cmsg->par = cpars [command_2_index (cmsg->Command,cmsg->Subcommand)];

    if (   cmsg->Command == CAPI_DATA_B3
	&& (cmsg->Subcommand == CAPI_REQ || cmsg->Subcommand == CAPI_IND)) {
       if (sizeof(void *) == 4) {
	  cmsg->Data32 = (_cdword)cmsg->Data;
	  cmsg->Data64 = 0;
       } else {
	  cmsg->Data32 = 0;
	  cmsg->Data64 = (_cqword)cmsg->Data;
       }
    }

    PARS_2_MESSAGE (cmsg);

    wordTLcpy (msg+0, &cmsg->l);
    byteTLcpy (cmsg->m+4, &cmsg->Command);
    byteTLcpy (cmsg->m+5, &cmsg->Subcommand);
    wordTLcpy (cmsg->m+2, &cmsg->ApplId);
    wordTLcpy (cmsg->m+6, &cmsg->Messagenumber);

    return 0;
}

/*-------------------------------------------------------*/
static void MESSAGE_2_PARS (_cmsg *cmsg) {
    for (;TYP != _CEND; cmsg->p++) {

	switch (TYP) {
	    case _CBYTE:
		byteTRcpy (cmsg->m+cmsg->l, OFF);
		cmsg->l++;
		break;
	    case _CWORD:
		wordTRcpy (cmsg->m+cmsg->l, OFF);
		cmsg->l+=2;
		break;
	    case _CDWORD:
		dwordTRcpy (cmsg->m+cmsg->l, OFF);
		cmsg->l+=4;
		break;
	    case _CQWORD:
		qwordTRcpy (cmsg->m+cmsg->l, OFF);
		cmsg->l+=8;
		break;
	    case _CSTRUCT:
		*(CAPI_MESSAGE *)OFF = cmsg->m+cmsg->l;

		if (cmsg->m[cmsg->l] != 0xff)
		    cmsg->l+= 1+ cmsg->m[cmsg->l];
		else
		    cmsg->l+= 3+ *(_cword *)(cmsg->m+cmsg->l+1);
		break;
	    case _CMSTRUCT:
		/*----- Metastruktur 0 -----*/
		if (cmsg->m[cmsg->l] == '\0') {
		    *(_cmstruct*)OFF = CAPI_DEFAULT;
		    cmsg->l++;
		    jumpcstruct (cmsg);
		}
		else {
		    unsigned _l = cmsg->l;
		    *(_cmstruct*)OFF = CAPI_COMPOSE;
		    cmsg->l = (cmsg->m+_l)[0] == 255 ? cmsg->l+3 : cmsg->l+1;
		    cmsg->p++;
		    MESSAGE_2_PARS (cmsg);
		}
		break;
	}
    }
}

/*-------------------------------------------------------*/
unsigned capi_message2cmsg (_cmsg *cmsg, CAPI_MESSAGE msg)
{
    memset (cmsg, 0, sizeof(_cmsg));
    cmsg->m = msg;
    cmsg->l = 8;
    cmsg->p = 0;
    byteTRcpy (cmsg->m+4, &cmsg->Command);
    byteTRcpy (cmsg->m+5, &cmsg->Subcommand);
    cmsg->par = cpars [command_2_index (cmsg->Command,cmsg->Subcommand)];

    MESSAGE_2_PARS (cmsg);

    if (   cmsg->Command == CAPI_DATA_B3
	&& (cmsg->Subcommand == CAPI_REQ || cmsg->Subcommand == CAPI_IND)) {
       if (sizeof(void *) == 4) {
	  cmsg->Data = (void *)cmsg->Data32;
       } else {
	  cmsg->Data = (void *)cmsg->Data64;
       }
    }

    wordTRcpy (msg+0, &cmsg->l);
    wordTRcpy (cmsg->m+2, &cmsg->ApplId);
    wordTRcpy (cmsg->m+6, &cmsg->Messagenumber);


    return 0;
}

/*-------------------------------------------------------*/
unsigned capi_cmsg_answer (_cmsg *cmsg)
{
    cmsg->Subcommand |= 0x01;
    return 0;
}

/*-------------------------------------------------------*/
unsigned capi20_cmsg_header (_cmsg *cmsg, unsigned _ApplId,
				 _cbyte _Command, _cbyte _Subcommand,
				 _cword _Messagenumber, _cdword _Controller) {
    memset (cmsg, 0, sizeof(_cmsg));
    cmsg->ApplId = _ApplId ;
    cmsg->Command = _Command ;
    cmsg->Subcommand = _Subcommand ;
    cmsg->Messagenumber = _Messagenumber;
    cmsg->adr.adrController = _Controller ;
    return 0;
}

/*-------------------------------------------------------*/
unsigned capi20_put_cmsg (_cmsg *cmsg)
{
    static unsigned char msg[2048];

    capi_cmsg2message(cmsg, (CAPI_MESSAGE)msg);
    return capi20_put_message(cmsg->ApplId, (CAPI_MESSAGE)msg);
}

/*-------------------------------------------------------*/
unsigned capi20_get_cmsg (_cmsg *cmsg, unsigned applid)
{
    MESSAGE_EXCHANGE_ERROR rtn;
    CAPI_MESSAGE msg;

    rtn = capi20_get_message(applid, &msg);
    if (rtn == 0x0000)
        capi_message2cmsg(cmsg, msg);
    return rtn;
}
