swh:1:snp:49cd9498d6cccc5e78252c27dcb645bcf7bf0c91
Raw File
Tip revision: eaa27f34e91a14cdceed26ed6c6793ec1d186115 authored by Linus Torvalds on 11 January 2015, 20:44:53 UTC
linux 3.19-rc4
Tip revision: eaa27f3
message.c
/*
 *
 Copyright (c) Eicon Networks, 2002.
 *
 This source file is supplied for the use with
 Eicon Networks range of DIVA Server Adapters.
 *
 Eicon File Revision :    2.1
 *
 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, or (at your option)
 any later version.
 *
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
 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.
 *
 */





#include "platform.h"
#include "di_defs.h"
#include "pc.h"
#include "capi20.h"
#include "divacapi.h"
#include "mdm_msg.h"
#include "divasync.h"



#define FILE_ "MESSAGE.C"
#define dprintf









/*------------------------------------------------------------------*/
/* This is options supported for all adapters that are server by    */
/* XDI driver. Allo it is not necessary to ask it from every adapter*/
/* and it is not necessary to save it separate for every adapter    */
/* Macrose defined here have only local meaning                     */
/*------------------------------------------------------------------*/
static dword diva_xdi_extended_features = 0;

#define DIVA_CAPI_USE_CMA                 0x00000001
#define DIVA_CAPI_XDI_PROVIDES_SDRAM_BAR  0x00000002
#define DIVA_CAPI_XDI_PROVIDES_NO_CANCEL  0x00000004
#define DIVA_CAPI_XDI_PROVIDES_RX_DMA     0x00000008

/*
  CAPI can request to process all return codes self only if:
  protocol code supports this && xdi supports this
*/
#define DIVA_CAPI_SUPPORTS_NO_CANCEL(__a__)   (((__a__)->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL) && ((__a__)->manufacturer_features & MANUFACTURER_FEATURE_OK_FC_LABEL) && (diva_xdi_extended_features & DIVA_CAPI_XDI_PROVIDES_NO_CANCEL))

/*------------------------------------------------------------------*/
/* local function prototypes                                        */
/*------------------------------------------------------------------*/

static void group_optimization(DIVA_CAPI_ADAPTER *a, PLCI *plci);
static void set_group_ind_mask(PLCI *plci);
static void clear_group_ind_mask_bit(PLCI *plci, word b);
static byte test_group_ind_mask_bit(PLCI *plci, word b);
void AutomaticLaw(DIVA_CAPI_ADAPTER *);
word CapiRelease(word);
word CapiRegister(word);
word api_put(APPL *, CAPI_MSG *);
static word api_parse(byte *, word, byte *, API_PARSE *);
static void api_save_msg(API_PARSE *in, byte *format, API_SAVE *out);
static void api_load_msg(API_SAVE *in, API_PARSE *out);

word api_remove_start(void);
void api_remove_complete(void);

static void plci_remove(PLCI *);
static void diva_get_extended_adapter_features(DIVA_CAPI_ADAPTER *a);
static void diva_ask_for_xdi_sdram_bar(DIVA_CAPI_ADAPTER *, IDI_SYNC_REQ *);

void callback(ENTITY *);

static void control_rc(PLCI *, byte, byte, byte, byte, byte);
static void data_rc(PLCI *, byte);
static void data_ack(PLCI *, byte);
static void sig_ind(PLCI *);
static void SendInfo(PLCI *, dword, byte **, byte);
static void SendSetupInfo(APPL *, PLCI *, dword, byte **, byte);
static void SendSSExtInd(APPL *, PLCI *plci, dword Id, byte **parms);

static void VSwitchReqInd(PLCI *plci, dword Id, byte **parms);

static void nl_ind(PLCI *);

static byte connect_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte connect_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte connect_a_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte disconnect_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte disconnect_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte listen_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte info_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte info_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte alert_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte facility_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte facility_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte connect_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte connect_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte connect_b3_a_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte disconnect_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte disconnect_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte data_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte data_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte reset_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte reset_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte connect_b3_t90_a_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte select_b_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte manufacturer_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
static byte manufacturer_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);

static word get_plci(DIVA_CAPI_ADAPTER *);
static void add_p(PLCI *, byte, byte *);
static void add_s(PLCI *plci, byte code, API_PARSE *p);
static void add_ss(PLCI *plci, byte code, API_PARSE *p);
static void add_ie(PLCI *plci, byte code, byte *p, word p_length);
static void add_d(PLCI *, word, byte *);
static void add_ai(PLCI *, API_PARSE *);
static word add_b1(PLCI *, API_PARSE *, word, word);
static word add_b23(PLCI *, API_PARSE *);
static word add_modem_b23(PLCI *plci, API_PARSE *bp_parms);
static void sig_req(PLCI *, byte, byte);
static void nl_req_ncci(PLCI *, byte, byte);
static void send_req(PLCI *);
static void send_data(PLCI *);
static word plci_remove_check(PLCI *);
static void listen_check(DIVA_CAPI_ADAPTER *);
static byte AddInfo(byte **, byte **, byte *, byte *);
static byte getChannel(API_PARSE *);
static void IndParse(PLCI *, word *, byte **, byte);
static byte ie_compare(byte *, byte *);
static word find_cip(DIVA_CAPI_ADAPTER *, byte *, byte *);
static word CPN_filter_ok(byte *cpn, DIVA_CAPI_ADAPTER *, word);

/*
  XON protocol helpers
*/
static void channel_flow_control_remove(PLCI *plci);
static void channel_x_off(PLCI *plci, byte ch, byte flag);
static void channel_x_on(PLCI *plci, byte ch);
static void channel_request_xon(PLCI *plci, byte ch);
static void channel_xmit_xon(PLCI *plci);
static int channel_can_xon(PLCI *plci, byte ch);
static void channel_xmit_extended_xon(PLCI *plci);

static byte SendMultiIE(PLCI *plci, dword Id, byte **parms, byte ie_type, dword info_mask, byte setupParse);
static word AdvCodecSupport(DIVA_CAPI_ADAPTER *, PLCI *, APPL *, byte);
static void CodecIdCheck(DIVA_CAPI_ADAPTER *, PLCI *);
static void SetVoiceChannel(PLCI *, byte *, DIVA_CAPI_ADAPTER *);
static void VoiceChannelOff(PLCI *plci);
static void adv_voice_write_coefs(PLCI *plci, word write_command);
static void adv_voice_clear_config(PLCI *plci);

static word get_b1_facilities(PLCI *plci, byte b1_resource);
static byte add_b1_facilities(PLCI *plci, byte b1_resource, word b1_facilities);
static void adjust_b1_facilities(PLCI *plci, byte new_b1_resource, word new_b1_facilities);
static word adjust_b_process(dword Id, PLCI *plci, byte Rc);
static void adjust_b1_resource(dword Id, PLCI *plci, API_SAVE *bp_msg, word b1_facilities, word internal_command);
static void adjust_b_restore(dword Id, PLCI *plci, byte Rc);
static void reset_b3_command(dword Id, PLCI *plci, byte Rc);
static void select_b_command(dword Id, PLCI *plci, byte Rc);
static void fax_connect_ack_command(dword Id, PLCI *plci, byte Rc);
static void fax_edata_ack_command(dword Id, PLCI *plci, byte Rc);
static void fax_connect_info_command(dword Id, PLCI *plci, byte Rc);
static void fax_adjust_b23_command(dword Id, PLCI *plci, byte Rc);
static void fax_disconnect_command(dword Id, PLCI *plci, byte Rc);
static void hold_save_command(dword Id, PLCI *plci, byte Rc);
static void retrieve_restore_command(dword Id, PLCI *plci, byte Rc);
static void init_b1_config(PLCI *plci);
static void clear_b1_config(PLCI *plci);

static void dtmf_command(dword Id, PLCI *plci, byte Rc);
static byte dtmf_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg);
static void dtmf_confirmation(dword Id, PLCI *plci);
static void dtmf_indication(dword Id, PLCI *plci, byte *msg, word length);
static void dtmf_parameter_write(PLCI *plci);


static void mixer_set_bchannel_id_esc(PLCI *plci, byte bchannel_id);
static void mixer_set_bchannel_id(PLCI *plci, byte *chi);
static void mixer_clear_config(PLCI *plci);
static void mixer_notify_update(PLCI *plci, byte others);
static void mixer_command(dword Id, PLCI *plci, byte Rc);
static byte mixer_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg);
static void mixer_indication_coefs_set(dword Id, PLCI *plci);
static void mixer_indication_xconnect_from(dword Id, PLCI *plci, byte *msg, word length);
static void mixer_indication_xconnect_to(dword Id, PLCI *plci, byte *msg, word length);
static void mixer_remove(PLCI *plci);


static void ec_command(dword Id, PLCI *plci, byte Rc);
static byte ec_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg);
static void ec_indication(dword Id, PLCI *plci, byte *msg, word length);


static void rtp_connect_b3_req_command(dword Id, PLCI *plci, byte Rc);
static void rtp_connect_b3_res_command(dword Id, PLCI *plci, byte Rc);


static int diva_get_dma_descriptor(PLCI *plci, dword *dma_magic);
static void diva_free_dma_descriptor(PLCI *plci, int nr);

/*------------------------------------------------------------------*/
/* external function prototypes                                     */
/*------------------------------------------------------------------*/

extern byte MapController(byte);
extern byte UnMapController(byte);
#define MapId(Id)(((Id) & 0xffffff00L) | MapController((byte)(Id)))
#define UnMapId(Id)(((Id) & 0xffffff00L) | UnMapController((byte)(Id)))

void sendf(APPL *, word, dword, word, byte *, ...);
void *TransmitBufferSet(APPL *appl, dword ref);
void *TransmitBufferGet(APPL *appl, void *p);
void TransmitBufferFree(APPL *appl, void *p);
void *ReceiveBufferGet(APPL *appl, int Num);

int fax_head_line_time(char *buffer);


/*------------------------------------------------------------------*/
/* Global data definitions                                          */
/*------------------------------------------------------------------*/
extern byte max_adapter;
extern byte max_appl;
extern DIVA_CAPI_ADAPTER *adapter;
extern APPL *application;







static byte remove_started = false;
static PLCI dummy_plci;


static struct _ftable {
	word command;
	byte *format;
	byte (*function)(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
} ftable[] = {
	{_DATA_B3_R,                          "dwww",         data_b3_req},
	{_DATA_B3_I | RESPONSE,               "w",            data_b3_res},
	{_INFO_R,                             "ss",           info_req},
	{_INFO_I | RESPONSE,                  "",             info_res},
	{_CONNECT_R,                          "wsssssssss",   connect_req},
	{_CONNECT_I | RESPONSE,               "wsssss",       connect_res},
	{_CONNECT_ACTIVE_I | RESPONSE,        "",             connect_a_res},
	{_DISCONNECT_R,                       "s",            disconnect_req},
	{_DISCONNECT_I | RESPONSE,            "",             disconnect_res},
	{_LISTEN_R,                           "dddss",        listen_req},
	{_ALERT_R,                            "s",            alert_req},
	{_FACILITY_R,                         "ws",           facility_req},
	{_FACILITY_I | RESPONSE,              "ws",           facility_res},
	{_CONNECT_B3_R,                       "s",            connect_b3_req},
	{_CONNECT_B3_I | RESPONSE,            "ws",           connect_b3_res},
	{_CONNECT_B3_ACTIVE_I | RESPONSE,     "",             connect_b3_a_res},
	{_DISCONNECT_B3_R,                    "s",            disconnect_b3_req},
	{_DISCONNECT_B3_I | RESPONSE,         "",             disconnect_b3_res},
	{_RESET_B3_R,                         "s",            reset_b3_req},
	{_RESET_B3_I | RESPONSE,              "",             reset_b3_res},
	{_CONNECT_B3_T90_ACTIVE_I | RESPONSE, "ws",           connect_b3_t90_a_res},
	{_CONNECT_B3_T90_ACTIVE_I | RESPONSE, "",             connect_b3_t90_a_res},
	{_SELECT_B_REQ,                       "s",            select_b_req},
	{_MANUFACTURER_R,                     "dws",          manufacturer_req},
	{_MANUFACTURER_I | RESPONSE,          "dws",          manufacturer_res},
	{_MANUFACTURER_I | RESPONSE,          "",             manufacturer_res}
};

static byte *cip_bc[29][2] = {
	{ "",                     ""                     }, /* 0 */
	{ "\x03\x80\x90\xa3",     "\x03\x80\x90\xa2"     }, /* 1 */
	{ "\x02\x88\x90",         "\x02\x88\x90"         }, /* 2 */
	{ "\x02\x89\x90",         "\x02\x89\x90"         }, /* 3 */
	{ "\x03\x90\x90\xa3",     "\x03\x90\x90\xa2"     }, /* 4 */
	{ "\x03\x91\x90\xa5",     "\x03\x91\x90\xa5"     }, /* 5 */
	{ "\x02\x98\x90",         "\x02\x98\x90"         }, /* 6 */
	{ "\x04\x88\xc0\xc6\xe6", "\x04\x88\xc0\xc6\xe6" }, /* 7 */
	{ "\x04\x88\x90\x21\x8f", "\x04\x88\x90\x21\x8f" }, /* 8 */
	{ "\x03\x91\x90\xa5",     "\x03\x91\x90\xa5"     }, /* 9 */
	{ "",                     ""                     }, /* 10 */
	{ "",                     ""                     }, /* 11 */
	{ "",                     ""                     }, /* 12 */
	{ "",                     ""                     }, /* 13 */
	{ "",                     ""                     }, /* 14 */
	{ "",                     ""                     }, /* 15 */

	{ "\x03\x80\x90\xa3",     "\x03\x80\x90\xa2"     }, /* 16 */
	{ "\x03\x90\x90\xa3",     "\x03\x90\x90\xa2"     }, /* 17 */
	{ "\x02\x88\x90",         "\x02\x88\x90"         }, /* 18 */
	{ "\x02\x88\x90",         "\x02\x88\x90"         }, /* 19 */
	{ "\x02\x88\x90",         "\x02\x88\x90"         }, /* 20 */
	{ "\x02\x88\x90",         "\x02\x88\x90"         }, /* 21 */
	{ "\x02\x88\x90",         "\x02\x88\x90"         }, /* 22 */
	{ "\x02\x88\x90",         "\x02\x88\x90"         }, /* 23 */
	{ "\x02\x88\x90",         "\x02\x88\x90"         }, /* 24 */
	{ "\x02\x88\x90",         "\x02\x88\x90"         }, /* 25 */
	{ "\x03\x91\x90\xa5",     "\x03\x91\x90\xa5"     }, /* 26 */
	{ "\x03\x91\x90\xa5",     "\x03\x91\x90\xa5"     }, /* 27 */
	{ "\x02\x88\x90",         "\x02\x88\x90"         }  /* 28 */
};

static byte *cip_hlc[29] = {
	"",                           /* 0 */
	"",                           /* 1 */
	"",                           /* 2 */
	"",                           /* 3 */
	"",                           /* 4 */
	"",                           /* 5 */
	"",                           /* 6 */
	"",                           /* 7 */
	"",                           /* 8 */
	"",                           /* 9 */
	"",                           /* 10 */
	"",                           /* 11 */
	"",                           /* 12 */
	"",                           /* 13 */
	"",                           /* 14 */
	"",                           /* 15 */

	"\x02\x91\x81",               /* 16 */
	"\x02\x91\x84",               /* 17 */
	"\x02\x91\xa1",               /* 18 */
	"\x02\x91\xa4",               /* 19 */
	"\x02\x91\xa8",               /* 20 */
	"\x02\x91\xb1",               /* 21 */
	"\x02\x91\xb2",               /* 22 */
	"\x02\x91\xb5",               /* 23 */
	"\x02\x91\xb8",               /* 24 */
	"\x02\x91\xc1",               /* 25 */
	"\x02\x91\x81",               /* 26 */
	"\x03\x91\xe0\x01",           /* 27 */
	"\x03\x91\xe0\x02"            /* 28 */
};

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

#define V120_HEADER_LENGTH 1
#define V120_HEADER_EXTEND_BIT  0x80
#define V120_HEADER_BREAK_BIT   0x40
#define V120_HEADER_C1_BIT      0x04
#define V120_HEADER_C2_BIT      0x08
#define V120_HEADER_FLUSH_COND  (V120_HEADER_BREAK_BIT | V120_HEADER_C1_BIT | V120_HEADER_C2_BIT)

static byte v120_default_header[] =
{

	0x83                          /*  Ext, BR , res, res, C2 , C1 , B  , F   */

};

static byte v120_break_header[] =
{

	0xc3 | V120_HEADER_BREAK_BIT  /*  Ext, BR , res, res, C2 , C1 , B  , F   */

};


/*------------------------------------------------------------------*/
/* API_PUT function                                                 */
/*------------------------------------------------------------------*/

word api_put(APPL *appl, CAPI_MSG *msg)
{
	word i, j, k, l, n;
	word ret;
	byte c;
	byte controller;
	DIVA_CAPI_ADAPTER *a;
	PLCI *plci;
	NCCI *ncci_ptr;
	word ncci;
	CAPI_MSG *m;
	API_PARSE msg_parms[MAX_MSG_PARMS + 1];

	if (msg->header.length < sizeof(msg->header) ||
	    msg->header.length > MAX_MSG_SIZE) {
		dbug(1, dprintf("bad len"));
		return _BAD_MSG;
	}

	controller = (byte)((msg->header.controller & 0x7f) - 1);

	/* controller starts with 0 up to (max_adapter - 1) */
	if (controller >= max_adapter)
	{
		dbug(1, dprintf("invalid ctrl"));
		return _BAD_MSG;
	}

	a = &adapter[controller];
	plci = NULL;
	if ((msg->header.plci != 0) && (msg->header.plci <= a->max_plci) && !a->adapter_disabled)
	{
		dbug(1, dprintf("plci=%x", msg->header.plci));
		plci = &a->plci[msg->header.plci - 1];
		ncci = GET_WORD(&msg->header.ncci);
		if (plci->Id
		    && (plci->appl
			|| (plci->State == INC_CON_PENDING)
			|| (plci->State == INC_CON_ALERT)
			|| (msg->header.command == (_DISCONNECT_I | RESPONSE)))
		    && ((ncci == 0)
			|| (msg->header.command == (_DISCONNECT_B3_I | RESPONSE))
			|| ((ncci < MAX_NCCI + 1) && (a->ncci_plci[ncci] == plci->Id))))
		{
			i = plci->msg_in_read_pos;
			j = plci->msg_in_write_pos;
			if (j >= i)
			{
				if (j + msg->header.length + MSG_IN_OVERHEAD <= MSG_IN_QUEUE_SIZE)
					i += MSG_IN_QUEUE_SIZE - j;
				else
					j = 0;
			}
			else
			{

				n = (((CAPI_MSG *)(plci->msg_in_queue))->header.length + MSG_IN_OVERHEAD + 3) & 0xfffc;

				if (i > MSG_IN_QUEUE_SIZE - n)
					i = MSG_IN_QUEUE_SIZE - n + 1;
				i -= j;
			}

			if (i <= ((msg->header.length + MSG_IN_OVERHEAD + 3) & 0xfffc))

			{
				dbug(0, dprintf("Q-FULL1(msg) - len=%d write=%d read=%d wrap=%d free=%d",
						msg->header.length, plci->msg_in_write_pos,
						plci->msg_in_read_pos, plci->msg_in_wrap_pos, i));

				return _QUEUE_FULL;
			}
			c = false;
			if ((((byte *) msg) < ((byte *)(plci->msg_in_queue)))
			    || (((byte *) msg) >= ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue)))
			{
				if (plci->msg_in_write_pos != plci->msg_in_read_pos)
					c = true;
			}
			if (msg->header.command == _DATA_B3_R)
			{
				if (msg->header.length < 20)
				{
					dbug(1, dprintf("DATA_B3 REQ wrong length %d", msg->header.length));
					return _BAD_MSG;
				}
				ncci_ptr = &(a->ncci[ncci]);
				n = ncci_ptr->data_pending;
				l = ncci_ptr->data_ack_pending;
				k = plci->msg_in_read_pos;
				while (k != plci->msg_in_write_pos)
				{
					if (k == plci->msg_in_wrap_pos)
						k = 0;
					if ((((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->header.command == _DATA_B3_R)
					    && (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->header.ncci == ncci))
					{
						n++;
						if (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->info.data_b3_req.Flags & 0x0004)
							l++;
					}

					k += (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->header.length +
					      MSG_IN_OVERHEAD + 3) & 0xfffc;

				}
				if ((n >= MAX_DATA_B3) || (l >= MAX_DATA_ACK))
				{
					dbug(0, dprintf("Q-FULL2(data) - pending=%d/%d ack_pending=%d/%d",
							ncci_ptr->data_pending, n, ncci_ptr->data_ack_pending, l));

					return _QUEUE_FULL;
				}
				if (plci->req_in || plci->internal_command)
				{
					if ((((byte *) msg) >= ((byte *)(plci->msg_in_queue)))
					    && (((byte *) msg) < ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue)))
					{
						dbug(0, dprintf("Q-FULL3(requeue)"));

						return _QUEUE_FULL;
					}
					c = true;
				}
			}
			else
			{
				if (plci->req_in || plci->internal_command)
					c = true;
				else
				{
					plci->command = msg->header.command;
					plci->number = msg->header.number;
				}
			}
			if (c)
			{
				dbug(1, dprintf("enqueue msg(0x%04x,0x%x,0x%x) - len=%d write=%d read=%d wrap=%d free=%d",
						msg->header.command, plci->req_in, plci->internal_command,
						msg->header.length, plci->msg_in_write_pos,
						plci->msg_in_read_pos, plci->msg_in_wrap_pos, i));
				if (j == 0)
					plci->msg_in_wrap_pos = plci->msg_in_write_pos;
				m = (CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[j]);
				for (i = 0; i < msg->header.length; i++)
					((byte *)(plci->msg_in_queue))[j++] = ((byte *) msg)[i];
				if (m->header.command == _DATA_B3_R)
				{

					m->info.data_b3_req.Data = (dword)(long)(TransmitBufferSet(appl, m->info.data_b3_req.Data));

				}

				j = (j + 3) & 0xfffc;

				*((APPL **)(&((byte *)(plci->msg_in_queue))[j])) = appl;
				plci->msg_in_write_pos = j + MSG_IN_OVERHEAD;
				return 0;
			}
		}
		else
		{
			plci = NULL;
		}
	}
	dbug(1, dprintf("com=%x", msg->header.command));

	for (j = 0; j < MAX_MSG_PARMS + 1; j++) msg_parms[j].length = 0;
	for (i = 0, ret = _BAD_MSG; i < ARRAY_SIZE(ftable); i++) {

		if (ftable[i].command == msg->header.command) {
			/* break loop if the message is correct, otherwise continue scan  */
			/* (for example: CONNECT_B3_T90_ACT_RES has two specifications)   */
			if (!api_parse(msg->info.b, (word)(msg->header.length - 12), ftable[i].format, msg_parms)) {
				ret = 0;
				break;
			}
			for (j = 0; j < MAX_MSG_PARMS + 1; j++) msg_parms[j].length = 0;
		}
	}
	if (ret) {
		dbug(1, dprintf("BAD_MSG"));
		if (plci) plci->command = 0;
		return ret;
	}


	c = ftable[i].function(GET_DWORD(&msg->header.controller),
			       msg->header.number,
			       a,
			       plci,
			       appl,
			       msg_parms);

	channel_xmit_extended_xon(plci);

	if (c == 1) send_req(plci);
	if (c == 2 && plci) plci->req_in = plci->req_in_start = plci->req_out = 0;
	if (plci && !plci->req_in) plci->command = 0;
	return 0;
}


/*------------------------------------------------------------------*/
/* api_parse function, check the format of api messages             */
/*------------------------------------------------------------------*/

static word api_parse(byte *msg, word length, byte *format, API_PARSE *parms)
{
	word i;
	word p;

	for (i = 0, p = 0; format[i]; i++) {
		if (parms)
		{
			parms[i].info = &msg[p];
		}
		switch (format[i]) {
		case 'b':
			p += 1;
			break;
		case 'w':
			p += 2;
			break;
		case 'd':
			p += 4;
			break;
		case 's':
			if (msg[p] == 0xff) {
				parms[i].info += 2;
				parms[i].length = msg[p + 1] + (msg[p + 2] << 8);
				p += (parms[i].length + 3);
			}
			else {
				parms[i].length = msg[p];
				p += (parms[i].length + 1);
			}
			break;
		}

		if (p > length) return true;
	}
	if (parms) parms[i].info = NULL;
	return false;
}

static void api_save_msg(API_PARSE *in, byte *format, API_SAVE *out)
{
	word i, j, n = 0;
	byte *p;

	p = out->info;
	for (i = 0; format[i] != '\0'; i++)
	{
		out->parms[i].info = p;
		out->parms[i].length = in[i].length;
		switch (format[i])
		{
		case 'b':
			n = 1;
			break;
		case 'w':
			n = 2;
			break;
		case 'd':
			n = 4;
			break;
		case 's':
			n = in[i].length + 1;
			break;
		}
		for (j = 0; j < n; j++)
			*(p++) = in[i].info[j];
	}
	out->parms[i].info = NULL;
	out->parms[i].length = 0;
}

static void api_load_msg(API_SAVE *in, API_PARSE *out)
{
	word i;

	i = 0;
	do
	{
		out[i].info = in->parms[i].info;
		out[i].length = in->parms[i].length;
	} while (in->parms[i++].info);
}


/*------------------------------------------------------------------*/
/* CAPI remove function                                             */
/*------------------------------------------------------------------*/

word api_remove_start(void)
{
	word i;
	word j;

	if (!remove_started) {
		remove_started = true;
		for (i = 0; i < max_adapter; i++) {
			if (adapter[i].request) {
				for (j = 0; j < adapter[i].max_plci; j++) {
					if (adapter[i].plci[j].Sig.Id) plci_remove(&adapter[i].plci[j]);
				}
			}
		}
		return 1;
	}
	else {
		for (i = 0; i < max_adapter; i++) {
			if (adapter[i].request) {
				for (j = 0; j < adapter[i].max_plci; j++) {
					if (adapter[i].plci[j].Sig.Id) return 1;
				}
			}
		}
	}
	api_remove_complete();
	return 0;
}


/*------------------------------------------------------------------*/
/* internal command queue                                           */
/*------------------------------------------------------------------*/

static void init_internal_command_queue(PLCI *plci)
{
	word i;

	dbug(1, dprintf("%s,%d: init_internal_command_queue",
			(char *)(FILE_), __LINE__));

	plci->internal_command = 0;
	for (i = 0; i < MAX_INTERNAL_COMMAND_LEVELS; i++)
		plci->internal_command_queue[i] = NULL;
}


static void start_internal_command(dword Id, PLCI *plci, t_std_internal_command command_function)
{
	word i;

	dbug(1, dprintf("[%06lx] %s,%d: start_internal_command",
			UnMapId(Id), (char *)(FILE_), __LINE__));

	if (plci->internal_command == 0)
	{
		plci->internal_command_queue[0] = command_function;
		(*command_function)(Id, plci, OK);
	}
	else
	{
		i = 1;
		while (plci->internal_command_queue[i] != NULL)
			i++;
		plci->internal_command_queue[i] = command_function;
	}
}


static void next_internal_command(dword Id, PLCI *plci)
{
	word i;

	dbug(1, dprintf("[%06lx] %s,%d: next_internal_command",
			UnMapId(Id), (char *)(FILE_), __LINE__));

	plci->internal_command = 0;
	plci->internal_command_queue[0] = NULL;
	while (plci->internal_command_queue[1] != NULL)
	{
		for (i = 0; i < MAX_INTERNAL_COMMAND_LEVELS - 1; i++)
			plci->internal_command_queue[i] = plci->internal_command_queue[i + 1];
		plci->internal_command_queue[MAX_INTERNAL_COMMAND_LEVELS - 1] = NULL;
		(*(plci->internal_command_queue[0]))(Id, plci, OK);
		if (plci->internal_command != 0)
			return;
		plci->internal_command_queue[0] = NULL;
	}
}


/*------------------------------------------------------------------*/
/* NCCI allocate/remove function                                    */
/*------------------------------------------------------------------*/

static dword ncci_mapping_bug = 0;

static word get_ncci(PLCI *plci, byte ch, word force_ncci)
{
	DIVA_CAPI_ADAPTER *a;
	word ncci, i, j, k;

	a = plci->adapter;
	if (!ch || a->ch_ncci[ch])
	{
		ncci_mapping_bug++;
		dbug(1, dprintf("NCCI mapping exists %ld %02x %02x %02x-%02x",
				ncci_mapping_bug, ch, force_ncci, a->ncci_ch[a->ch_ncci[ch]], a->ch_ncci[ch]));
		ncci = ch;
	}
	else
	{
		if (force_ncci)
			ncci = force_ncci;
		else
		{
			if ((ch < MAX_NCCI + 1) && !a->ncci_ch[ch])
				ncci = ch;
			else
			{
				ncci = 1;
				while ((ncci < MAX_NCCI + 1) && a->ncci_ch[ncci])
					ncci++;
				if (ncci == MAX_NCCI + 1)
				{
					ncci_mapping_bug++;
					i = 1;
					do
					{
						j = 1;
						while ((j < MAX_NCCI + 1) && (a->ncci_ch[j] != i))
							j++;
						k = j;
						if (j < MAX_NCCI + 1)
						{
							do
							{
								j++;
							} while ((j < MAX_NCCI + 1) && (a->ncci_ch[j] != i));
						}
					} while ((i < MAX_NL_CHANNEL + 1) && (j < MAX_NCCI + 1));
					if (i < MAX_NL_CHANNEL + 1)
					{
						dbug(1, dprintf("NCCI mapping overflow %ld %02x %02x %02x-%02x-%02x",
								ncci_mapping_bug, ch, force_ncci, i, k, j));
					}
					else
					{
						dbug(1, dprintf("NCCI mapping overflow %ld %02x %02x",
								ncci_mapping_bug, ch, force_ncci));
					}
					ncci = ch;
				}
			}
			a->ncci_plci[ncci] = plci->Id;
			a->ncci_state[ncci] = IDLE;
			if (!plci->ncci_ring_list)
				plci->ncci_ring_list = ncci;
			else
				a->ncci_next[ncci] = a->ncci_next[plci->ncci_ring_list];
			a->ncci_next[plci->ncci_ring_list] = (byte) ncci;
		}
		a->ncci_ch[ncci] = ch;
		a->ch_ncci[ch] = (byte) ncci;
		dbug(1, dprintf("NCCI mapping established %ld %02x %02x %02x-%02x",
				ncci_mapping_bug, ch, force_ncci, ch, ncci));
	}
	return (ncci);
}


static void ncci_free_receive_buffers(PLCI *plci, word ncci)
{
	DIVA_CAPI_ADAPTER *a;
	APPL *appl;
	word i, ncci_code;
	dword Id;

	a = plci->adapter;
	Id = (((dword) ncci) << 16) | (((word)(plci->Id)) << 8) | a->Id;
	if (ncci)
	{
		if (a->ncci_plci[ncci] == plci->Id)
		{
			if (!plci->appl)
			{
				ncci_mapping_bug++;
				dbug(1, dprintf("NCCI mapping appl expected %ld %08lx",
						ncci_mapping_bug, Id));
			}
			else
			{
				appl = plci->appl;
				ncci_code = ncci | (((word) a->Id) << 8);
				for (i = 0; i < appl->MaxBuffer; i++)
				{
					if ((appl->DataNCCI[i] == ncci_code)
					    && (((byte)(appl->DataFlags[i] >> 8)) == plci->Id))
					{
						appl->DataNCCI[i] = 0;
					}
				}
			}
		}
	}
	else
	{
		for (ncci = 1; ncci < MAX_NCCI + 1; ncci++)
		{
			if (a->ncci_plci[ncci] == plci->Id)
			{
				if (!plci->appl)
				{
					ncci_mapping_bug++;
					dbug(1, dprintf("NCCI mapping no appl %ld %08lx",
							ncci_mapping_bug, Id));
				}
				else
				{
					appl = plci->appl;
					ncci_code = ncci | (((word) a->Id) << 8);
					for (i = 0; i < appl->MaxBuffer; i++)
					{
						if ((appl->DataNCCI[i] == ncci_code)
						    && (((byte)(appl->DataFlags[i] >> 8)) == plci->Id))
						{
							appl->DataNCCI[i] = 0;
						}
					}
				}
			}
		}
	}
}


static void cleanup_ncci_data(PLCI *plci, word ncci)
{
	NCCI *ncci_ptr;

	if (ncci && (plci->adapter->ncci_plci[ncci] == plci->Id))
	{
		ncci_ptr = &(plci->adapter->ncci[ncci]);
		if (plci->appl)
		{
			while (ncci_ptr->data_pending != 0)
			{
				if (!plci->data_sent || (ncci_ptr->DBuffer[ncci_ptr->data_out].P != plci->data_sent_ptr))
					TransmitBufferFree(plci->appl, ncci_ptr->DBuffer[ncci_ptr->data_out].P);
				(ncci_ptr->data_out)++;
				if (ncci_ptr->data_out == MAX_DATA_B3)
					ncci_ptr->data_out = 0;
				(ncci_ptr->data_pending)--;
			}
		}
		ncci_ptr->data_out = 0;
		ncci_ptr->data_pending = 0;
		ncci_ptr->data_ack_out = 0;
		ncci_ptr->data_ack_pending = 0;
	}
}


static void ncci_remove(PLCI *plci, word ncci, byte preserve_ncci)
{
	DIVA_CAPI_ADAPTER *a;
	dword Id;
	word i;

	a = plci->adapter;
	Id = (((dword) ncci) << 16) | (((word)(plci->Id)) << 8) | a->Id;
	if (!preserve_ncci)
		ncci_free_receive_buffers(plci, ncci);
	if (ncci)
	{
		if (a->ncci_plci[ncci] != plci->Id)
		{
			ncci_mapping_bug++;
			dbug(1, dprintf("NCCI mapping doesn't exist %ld %08lx %02x",
					ncci_mapping_bug, Id, preserve_ncci));
		}
		else
		{
			cleanup_ncci_data(plci, ncci);
			dbug(1, dprintf("NCCI mapping released %ld %08lx %02x %02x-%02x",
					ncci_mapping_bug, Id, preserve_ncci, a->ncci_ch[ncci], ncci));
			a->ch_ncci[a->ncci_ch[ncci]] = 0;
			if (!preserve_ncci)
			{
				a->ncci_ch[ncci] = 0;
				a->ncci_plci[ncci] = 0;
				a->ncci_state[ncci] = IDLE;
				i = plci->ncci_ring_list;
				while ((i != 0) && (a->ncci_next[i] != plci->ncci_ring_list) && (a->ncci_next[i] != ncci))
					i = a->ncci_next[i];
				if ((i != 0) && (a->ncci_next[i] == ncci))
				{
					if (i == ncci)
						plci->ncci_ring_list = 0;
					else if (plci->ncci_ring_list == ncci)
						plci->ncci_ring_list = i;
					a->ncci_next[i] = a->ncci_next[ncci];
				}
				a->ncci_next[ncci] = 0;
			}
		}
	}
	else
	{
		for (ncci = 1; ncci < MAX_NCCI + 1; ncci++)
		{
			if (a->ncci_plci[ncci] == plci->Id)
			{
				cleanup_ncci_data(plci, ncci);
				dbug(1, dprintf("NCCI mapping released %ld %08lx %02x %02x-%02x",
						ncci_mapping_bug, Id, preserve_ncci, a->ncci_ch[ncci], ncci));
				a->ch_ncci[a->ncci_ch[ncci]] = 0;
				if (!preserve_ncci)
				{
					a->ncci_ch[ncci] = 0;
					a->ncci_plci[ncci] = 0;
					a->ncci_state[ncci] = IDLE;
					a->ncci_next[ncci] = 0;
				}
			}
		}
		if (!preserve_ncci)
			plci->ncci_ring_list = 0;
	}
}


/*------------------------------------------------------------------*/
/* PLCI remove function                                             */
/*------------------------------------------------------------------*/

static void plci_free_msg_in_queue(PLCI *plci)
{
	word i;

	if (plci->appl)
	{
		i = plci->msg_in_read_pos;
		while (i != plci->msg_in_write_pos)
		{
			if (i == plci->msg_in_wrap_pos)
				i = 0;
			if (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[i]))->header.command == _DATA_B3_R)
			{

				TransmitBufferFree(plci->appl,
						   (byte *)(long)(((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[i]))->info.data_b3_req.Data));

			}

			i += (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[i]))->header.length +
			      MSG_IN_OVERHEAD + 3) & 0xfffc;

		}
	}
	plci->msg_in_write_pos = MSG_IN_QUEUE_SIZE;
	plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE;
	plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE;
}


static void plci_remove(PLCI *plci)
{

	if (!plci) {
		dbug(1, dprintf("plci_remove(no plci)"));
		return;
	}
	init_internal_command_queue(plci);
	dbug(1, dprintf("plci_remove(%x,tel=%x)", plci->Id, plci->tel));
	if (plci_remove_check(plci))
	{
		return;
	}
	if (plci->Sig.Id == 0xff)
	{
		dbug(1, dprintf("D-channel X.25 plci->NL.Id:%0x", plci->NL.Id));
		if (plci->NL.Id && !plci->nl_remove_id)
		{
			nl_req_ncci(plci, REMOVE, 0);
			send_req(plci);
		}
	}
	else
	{
		if (!plci->sig_remove_id
		    && (plci->Sig.Id
			|| (plci->req_in != plci->req_out)
			|| (plci->nl_req || plci->sig_req)))
		{
			sig_req(plci, HANGUP, 0);
			send_req(plci);
		}
	}
	ncci_remove(plci, 0, false);
	plci_free_msg_in_queue(plci);

	plci->channels = 0;
	plci->appl = NULL;
	if ((plci->State == INC_CON_PENDING) || (plci->State == INC_CON_ALERT))
		plci->State = OUTG_DIS_PENDING;
}

/*------------------------------------------------------------------*/
/* Application Group function helpers                               */
/*------------------------------------------------------------------*/

static void set_group_ind_mask(PLCI *plci)
{
	word i;

	for (i = 0; i < C_IND_MASK_DWORDS; i++)
		plci->group_optimization_mask_table[i] = 0xffffffffL;
}

static void clear_group_ind_mask_bit(PLCI *plci, word b)
{
	plci->group_optimization_mask_table[b >> 5] &= ~(1L << (b & 0x1f));
}

static byte test_group_ind_mask_bit(PLCI *plci, word b)
{
	return ((plci->group_optimization_mask_table[b >> 5] & (1L << (b & 0x1f))) != 0);
}

/*------------------------------------------------------------------*/
/* c_ind_mask operations for arbitrary MAX_APPL                     */
/*------------------------------------------------------------------*/

static void clear_c_ind_mask(PLCI *plci)
{
	word i;

	for (i = 0; i < C_IND_MASK_DWORDS; i++)
		plci->c_ind_mask_table[i] = 0;
}

static byte c_ind_mask_empty(PLCI *plci)
{
	word i;

	i = 0;
	while ((i < C_IND_MASK_DWORDS) && (plci->c_ind_mask_table[i] == 0))
		i++;
	return (i == C_IND_MASK_DWORDS);
}

static void set_c_ind_mask_bit(PLCI *plci, word b)
{
	plci->c_ind_mask_table[b >> 5] |= (1L << (b & 0x1f));
}

static void clear_c_ind_mask_bit(PLCI *plci, word b)
{
	plci->c_ind_mask_table[b >> 5] &= ~(1L << (b & 0x1f));
}

static byte test_c_ind_mask_bit(PLCI *plci, word b)
{
	return ((plci->c_ind_mask_table[b >> 5] & (1L << (b & 0x1f))) != 0);
}

static void dump_c_ind_mask(PLCI *plci)
{
	static char hex_digit_table[0x10] =
		{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
	word i, j, k;
	dword d;
	char *p;
	char buf[40];

	for (i = 0; i < C_IND_MASK_DWORDS; i += 4)
	{
		p = buf + 36;
		*p = '\0';
		for (j = 0; j < 4; j++)
		{
			if (i + j < C_IND_MASK_DWORDS)
			{
				d = plci->c_ind_mask_table[i + j];
				for (k = 0; k < 8; k++)
				{
					*(--p) = hex_digit_table[d & 0xf];
					d >>= 4;
				}
			}
			else if (i != 0)
			{
				for (k = 0; k < 8; k++)
					*(--p) = ' ';
			}
			*(--p) = ' ';
		}
		dbug(1, dprintf("c_ind_mask =%s", (char *) p));
	}
}





#define dump_plcis(a)



/*------------------------------------------------------------------*/
/* translation function for each message                            */
/*------------------------------------------------------------------*/

static byte connect_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			PLCI *plci, APPL *appl, API_PARSE *parms)
{
	word ch;
	word i;
	word Info;
	byte LinkLayer;
	API_PARSE *ai;
	API_PARSE *bp;
	API_PARSE ai_parms[5];
	word channel = 0;
	dword ch_mask;
	byte m;
	static byte esc_chi[35] = {0x02, 0x18, 0x01};
	static byte lli[2] = {0x01, 0x00};
	byte noCh = 0;
	word dir = 0;
	byte *p_chi = "";

	for (i = 0; i < 5; i++) ai_parms[i].length = 0;

	dbug(1, dprintf("connect_req(%d)", parms->length));
	Info = _WRONG_IDENTIFIER;
	if (a)
	{
		if (a->adapter_disabled)
		{
			dbug(1, dprintf("adapter disabled"));
			Id = ((word)1 << 8) | a->Id;
			sendf(appl, _CONNECT_R | CONFIRM, Id, Number, "w", 0);
			sendf(appl, _DISCONNECT_I, Id, 0, "w", _L1_ERROR);
			return false;
		}
		Info = _OUT_OF_PLCI;
		if ((i = get_plci(a)))
		{
			Info = 0;
			plci = &a->plci[i - 1];
			plci->appl = appl;
			plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
			/* check 'external controller' bit for codec support */
			if (Id & EXT_CONTROLLER)
			{
				if (AdvCodecSupport(a, plci, appl, 0))
				{
					plci->Id = 0;
					sendf(appl, _CONNECT_R | CONFIRM, Id, Number, "w", _WRONG_IDENTIFIER);
					return 2;
				}
			}
			ai = &parms[9];
			bp = &parms[5];
			ch = 0;
			if (bp->length)LinkLayer = bp->info[3];
			else LinkLayer = 0;
			if (ai->length)
			{
				ch = 0xffff;
				if (!api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms))
				{
					ch = 0;
					if (ai_parms[0].length)
					{
						ch = GET_WORD(ai_parms[0].info + 1);
						if (ch > 4) ch = 0; /* safety -> ignore ChannelID */
						if (ch == 4) /* explizit CHI in message */
						{
							/* check length of B-CH struct */
							if ((ai_parms[0].info)[3] >= 1)
							{
								if ((ai_parms[0].info)[4] == CHI)
								{
									p_chi = &((ai_parms[0].info)[5]);
								}
								else
								{
									p_chi = &((ai_parms[0].info)[3]);
								}
								if (p_chi[0] > 35) /* check length of channel ID */
								{
									Info = _WRONG_MESSAGE_FORMAT;
								}
							}
							else Info = _WRONG_MESSAGE_FORMAT;
						}

						if (ch == 3 && ai_parms[0].length >= 7 && ai_parms[0].length <= 36)
						{
							dir = GET_WORD(ai_parms[0].info + 3);
							ch_mask = 0;
							m = 0x3f;
							for (i = 0; i + 5 <= ai_parms[0].length; i++)
							{
								if (ai_parms[0].info[i + 5] != 0)
								{
									if ((ai_parms[0].info[i + 5] | m) != 0xff)
										Info = _WRONG_MESSAGE_FORMAT;
									else
									{
										if (ch_mask == 0)
											channel = i;
										ch_mask |= 1L << i;
									}
								}
								m = 0;
							}
							if (ch_mask == 0)
								Info = _WRONG_MESSAGE_FORMAT;
							if (!Info)
							{
								if ((ai_parms[0].length == 36) || (ch_mask != ((dword)(1L << channel))))
								{
									esc_chi[0] = (byte)(ai_parms[0].length - 2);
									for (i = 0; i + 5 <= ai_parms[0].length; i++)
										esc_chi[i + 3] = ai_parms[0].info[i + 5];
								}
								else
									esc_chi[0] = 2;
								esc_chi[2] = (byte)channel;
								plci->b_channel = (byte)channel; /* not correct for ETSI ch 17..31 */
								add_p(plci, LLI, lli);
								add_p(plci, ESC, esc_chi);
								plci->State = LOCAL_CONNECT;
								if (!dir) plci->call_dir |= CALL_DIR_FORCE_OUTG_NL;     /* dir 0=DTE, 1=DCE */
							}
						}
					}
				}
				else  Info = _WRONG_MESSAGE_FORMAT;
			}

			dbug(1, dprintf("ch=%x,dir=%x,p_ch=%d", ch, dir, channel));
			plci->command = _CONNECT_R;
			plci->number = Number;
			/* x.31 or D-ch free SAPI in LinkLayer? */
			if (ch == 1 && LinkLayer != 3 && LinkLayer != 12) noCh = true;
			if ((ch == 0 || ch == 2 || noCh || ch == 3 || ch == 4) && !Info)
			{
				/* B-channel used for B3 connections (ch==0), or no B channel    */
				/* is used (ch==2) or perm. connection (3) is used  do a CALL    */
				if (noCh) Info = add_b1(plci, &parms[5], 2, 0);    /* no resource    */
				else     Info = add_b1(plci, &parms[5], ch, 0);
				add_s(plci, OAD, &parms[2]);
				add_s(plci, OSA, &parms[4]);
				add_s(plci, BC, &parms[6]);
				add_s(plci, LLC, &parms[7]);
				add_s(plci, HLC, &parms[8]);
				if (a->Info_Mask[appl->Id - 1] & 0x200)
				{
					/* early B3 connect (CIP mask bit 9) no release after a disc */
					add_p(plci, LLI, "\x01\x01");
				}
				if (GET_WORD(parms[0].info) < 29) {
					add_p(plci, BC, cip_bc[GET_WORD(parms[0].info)][a->u_law]);
					add_p(plci, HLC, cip_hlc[GET_WORD(parms[0].info)]);
				}
				add_p(plci, UID, "\x06\x43\x61\x70\x69\x32\x30");
				sig_req(plci, ASSIGN, DSIG_ID);
			}
			else if (ch == 1) {

				/* D-Channel used for B3 connections */
				plci->Sig.Id = 0xff;
				Info = 0;
			}

			if (!Info && ch != 2 && !noCh) {
				Info = add_b23(plci, &parms[5]);
				if (!Info) {
					if (!(plci->tel && !plci->adv_nl))nl_req_ncci(plci, ASSIGN, 0);
				}
			}

			if (!Info)
			{
				if (ch == 0 || ch == 2 || ch == 3 || noCh || ch == 4)
				{
					if (plci->spoofed_msg == SPOOFING_REQUIRED)
					{
						api_save_msg(parms, "wsssssssss", &plci->saved_msg);
						plci->spoofed_msg = CALL_REQ;
						plci->internal_command = BLOCK_PLCI;
						plci->command = 0;
						dbug(1, dprintf("Spoof"));
						send_req(plci);
						return false;
					}
					if (ch == 4)add_p(plci, CHI, p_chi);
					add_s(plci, CPN, &parms[1]);
					add_s(plci, DSA, &parms[3]);
					if (noCh) add_p(plci, ESC, "\x02\x18\xfd");  /* D-channel, no B-L3 */
					add_ai(plci, &parms[9]);
					if (!dir)sig_req(plci, CALL_REQ, 0);
					else
					{
						plci->command = PERM_LIST_REQ;
						plci->appl = appl;
						sig_req(plci, LISTEN_REQ, 0);
						send_req(plci);
						return false;
					}
				}
				send_req(plci);
				return false;
			}
			plci->Id = 0;
		}
	}
	sendf(appl,
	      _CONNECT_R | CONFIRM,
	      Id,
	      Number,
	      "w", Info);
	return 2;
}

static byte connect_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			PLCI *plci, APPL *appl, API_PARSE *parms)
{
	word i, Info;
	word Reject;
	static byte cau_t[] = {0, 0, 0x90, 0x91, 0xac, 0x9d, 0x86, 0xd8, 0x9b};
	static byte esc_t[] = {0x03, 0x08, 0x00, 0x00};
	API_PARSE *ai;
	API_PARSE ai_parms[5];
	word ch = 0;

	if (!plci) {
		dbug(1, dprintf("connect_res(no plci)"));
		return 0;  /* no plci, no send */
	}

	dbug(1, dprintf("connect_res(State=0x%x)", plci->State));
	for (i = 0; i < 5; i++) ai_parms[i].length = 0;
	ai = &parms[5];
	dbug(1, dprintf("ai->length=%d", ai->length));

	if (ai->length)
	{
		if (!api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms))
		{
			dbug(1, dprintf("ai_parms[0].length=%d/0x%x", ai_parms[0].length, GET_WORD(ai_parms[0].info + 1)));
			ch = 0;
			if (ai_parms[0].length)
			{
				ch = GET_WORD(ai_parms[0].info + 1);
				dbug(1, dprintf("BCH-I=0x%x", ch));
			}
		}
	}

	if (plci->State == INC_CON_CONNECTED_ALERT)
	{
		dbug(1, dprintf("Connected Alert Call_Res"));
		if (a->Info_Mask[appl->Id - 1] & 0x200)
		{
			/* early B3 connect (CIP mask bit 9) no release after a disc */
			add_p(plci, LLI, "\x01\x01");
		}
		add_s(plci, CONN_NR, &parms[2]);
		add_s(plci, LLC, &parms[4]);
		add_ai(plci, &parms[5]);
		plci->State = INC_CON_ACCEPT;
		sig_req(plci, CALL_RES, 0);
		return 1;
	}
	else if (plci->State == INC_CON_PENDING || plci->State == INC_CON_ALERT) {
		clear_c_ind_mask_bit(plci, (word)(appl->Id - 1));
		dump_c_ind_mask(plci);
		Reject = GET_WORD(parms[0].info);
		dbug(1, dprintf("Reject=0x%x", Reject));
		if (Reject)
		{
			if (c_ind_mask_empty(plci))
			{
				if ((Reject & 0xff00) == 0x3400)
				{
					esc_t[2] = ((byte)(Reject & 0x00ff)) | 0x80;
					add_p(plci, ESC, esc_t);
					add_ai(plci, &parms[5]);
					sig_req(plci, REJECT, 0);
				}
				else if (Reject == 1 || Reject > 9)
				{
					add_ai(plci, &parms[5]);
					sig_req(plci, HANGUP, 0);
				}
				else
				{
					esc_t[2] = cau_t[(Reject&0x000f)];
					add_p(plci, ESC, esc_t);
					add_ai(plci, &parms[5]);
					sig_req(plci, REJECT, 0);
				}
				plci->appl = appl;
			}
			else
			{
				sendf(appl, _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED);
			}
		}
		else {
			plci->appl = appl;
			if (Id & EXT_CONTROLLER) {
				if (AdvCodecSupport(a, plci, appl, 0)) {
					dbug(1, dprintf("connect_res(error from AdvCodecSupport)"));
					sig_req(plci, HANGUP, 0);
					return 1;
				}
				if (plci->tel == ADV_VOICE && a->AdvCodecPLCI)
				{
					Info = add_b23(plci, &parms[1]);
					if (Info)
					{
						dbug(1, dprintf("connect_res(error from add_b23)"));
						sig_req(plci, HANGUP, 0);
						return 1;
					}
					if (plci->adv_nl)
					{
						nl_req_ncci(plci, ASSIGN, 0);
					}
				}
			}
			else
			{
				plci->tel = 0;
				if (ch != 2)
				{
					Info = add_b23(plci, &parms[1]);
					if (Info)
					{
						dbug(1, dprintf("connect_res(error from add_b23 2)"));
						sig_req(plci, HANGUP, 0);
						return 1;
					}
				}
				nl_req_ncci(plci, ASSIGN, 0);
			}

			if (plci->spoofed_msg == SPOOFING_REQUIRED)
			{
				api_save_msg(parms, "wsssss", &plci->saved_msg);
				plci->spoofed_msg = CALL_RES;
				plci->internal_command = BLOCK_PLCI;
				plci->command = 0;
				dbug(1, dprintf("Spoof"));
			}
			else
			{
				add_b1(plci, &parms[1], ch, plci->B1_facilities);
				if (a->Info_Mask[appl->Id - 1] & 0x200)
				{
					/* early B3 connect (CIP mask bit 9) no release after a disc */
					add_p(plci, LLI, "\x01\x01");
				}
				add_s(plci, CONN_NR, &parms[2]);
				add_s(plci, LLC, &parms[4]);
				add_ai(plci, &parms[5]);
				plci->State = INC_CON_ACCEPT;
				sig_req(plci, CALL_RES, 0);
			}

			for (i = 0; i < max_appl; i++) {
				if (test_c_ind_mask_bit(plci, i)) {
					sendf(&application[i], _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED);
				}
			}
		}
	}
	return 1;
}

static byte connect_a_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			  PLCI *plci, APPL *appl, API_PARSE *msg)
{
	dbug(1, dprintf("connect_a_res"));
	return false;
}

static byte disconnect_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			   PLCI *plci, APPL *appl, API_PARSE *msg)
{
	word Info;
	word i;

	dbug(1, dprintf("disconnect_req"));

	Info = _WRONG_IDENTIFIER;

	if (plci)
	{
		if (plci->State == INC_CON_PENDING || plci->State == INC_CON_ALERT)
		{
			clear_c_ind_mask_bit(plci, (word)(appl->Id - 1));
			plci->appl = appl;
			for (i = 0; i < max_appl; i++)
			{
				if (test_c_ind_mask_bit(plci, i))
					sendf(&application[i], _DISCONNECT_I, Id, 0, "w", 0);
			}
			plci->State = OUTG_DIS_PENDING;
		}
		if (plci->Sig.Id && plci->appl)
		{
			Info = 0;
			if (plci->Sig.Id != 0xff)
			{
				if (plci->State != INC_DIS_PENDING)
				{
					add_ai(plci, &msg[0]);
					sig_req(plci, HANGUP, 0);
					plci->State = OUTG_DIS_PENDING;
					return 1;
				}
			}
			else
			{
				if (plci->NL.Id && !plci->nl_remove_id)
				{
					mixer_remove(plci);
					nl_req_ncci(plci, REMOVE, 0);
					sendf(appl, _DISCONNECT_R | CONFIRM, Id, Number, "w", 0);
					sendf(appl, _DISCONNECT_I, Id, 0, "w", 0);
					plci->State = INC_DIS_PENDING;
				}
				return 1;
			}
		}
	}

	if (!appl)  return false;
	sendf(appl, _DISCONNECT_R | CONFIRM, Id, Number, "w", Info);
	return false;
}

static byte disconnect_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			   PLCI *plci, APPL *appl, API_PARSE *msg)
{
	dbug(1, dprintf("disconnect_res"));
	if (plci)
	{
		/* clear ind mask bit, just in case of collsion of          */
		/* DISCONNECT_IND and CONNECT_RES                           */
		clear_c_ind_mask_bit(plci, (word)(appl->Id - 1));
		ncci_free_receive_buffers(plci, 0);
		if (plci_remove_check(plci))
		{
			return 0;
		}
		if (plci->State == INC_DIS_PENDING
		    || plci->State == SUSPENDING) {
			if (c_ind_mask_empty(plci)) {
				if (plci->State != SUSPENDING) plci->State = IDLE;
				dbug(1, dprintf("chs=%d", plci->channels));
				if (!plci->channels) {
					plci_remove(plci);
				}
			}
		}
	}
	return 0;
}

static byte listen_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
		       PLCI *plci, APPL *appl, API_PARSE *parms)
{
	word Info;
	byte i;

	dbug(1, dprintf("listen_req(Appl=0x%x)", appl->Id));

	Info = _WRONG_IDENTIFIER;
	if (a) {
		Info = 0;
		a->Info_Mask[appl->Id - 1] = GET_DWORD(parms[0].info);
		a->CIP_Mask[appl->Id - 1] = GET_DWORD(parms[1].info);
		dbug(1, dprintf("CIP_MASK=0x%lx", GET_DWORD(parms[1].info)));
		if (a->Info_Mask[appl->Id - 1] & 0x200) { /* early B3 connect provides */
			a->Info_Mask[appl->Id - 1] |=  0x10;   /* call progression infos    */
		}

		/* check if external controller listen and switch listen on or off*/
		if (Id&EXT_CONTROLLER && GET_DWORD(parms[1].info)) {
			if (a->profile.Global_Options & ON_BOARD_CODEC) {
				dummy_plci.State = IDLE;
				a->codec_listen[appl->Id - 1] = &dummy_plci;
				a->TelOAD[0] = (byte)(parms[3].length);
				for (i = 1; parms[3].length >= i && i < 22; i++) {
					a->TelOAD[i] = parms[3].info[i];
				}
				a->TelOAD[i] = 0;
				a->TelOSA[0] = (byte)(parms[4].length);
				for (i = 1; parms[4].length >= i && i < 22; i++) {
					a->TelOSA[i] = parms[4].info[i];
				}
				a->TelOSA[i] = 0;
			}
			else Info = 0x2002; /* wrong controller, codec not supported */
		}
		else{               /* clear listen */
			a->codec_listen[appl->Id - 1] = (PLCI *)0;
		}
	}
	sendf(appl,
	      _LISTEN_R | CONFIRM,
	      Id,
	      Number,
	      "w", Info);

	if (a) listen_check(a);
	return false;
}

static byte info_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
		     PLCI *plci, APPL *appl, API_PARSE *msg)
{
	word i;
	API_PARSE *ai;
	PLCI *rc_plci = NULL;
	API_PARSE ai_parms[5];
	word Info = 0;

	dbug(1, dprintf("info_req"));
	for (i = 0; i < 5; i++) ai_parms[i].length = 0;

	ai = &msg[1];

	if (ai->length)
	{
		if (api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms))
		{
			dbug(1, dprintf("AddInfo wrong"));
			Info = _WRONG_MESSAGE_FORMAT;
		}
	}
	if (!a) Info = _WRONG_STATE;

	if (!Info && plci)
	{                /* no fac, with CPN, or KEY */
		rc_plci = plci;
		if (!ai_parms[3].length && plci->State && (msg[0].length || ai_parms[1].length))
		{
			/* overlap sending option */
			dbug(1, dprintf("OvlSnd"));
			add_s(plci, CPN, &msg[0]);
			add_s(plci, KEY, &ai_parms[1]);
			sig_req(plci, INFO_REQ, 0);
			send_req(plci);
			return false;
		}

		if (plci->State && ai_parms[2].length)
		{
			/* User_Info option */
			dbug(1, dprintf("UUI"));
			add_s(plci, UUI, &ai_parms[2]);
			sig_req(plci, USER_DATA, 0);
		}
		else if (plci->State && ai_parms[3].length)
		{
			/* Facility option */
			dbug(1, dprintf("FAC"));
			add_s(plci, CPN, &msg[0]);
			add_ai(plci, &msg[1]);
			sig_req(plci, FACILITY_REQ, 0);
		}
		else
		{
			Info = _WRONG_STATE;
		}
	}
	else if ((ai_parms[1].length || ai_parms[2].length || ai_parms[3].length) && !Info)
	{
		/* NCR_Facility option -> send UUI and Keypad too */
		dbug(1, dprintf("NCR_FAC"));
		if ((i = get_plci(a)))
		{
			rc_plci = &a->plci[i - 1];
			appl->NullCREnable = true;
			rc_plci->internal_command = C_NCR_FAC_REQ;
			rc_plci->appl = appl;
			add_p(rc_plci, CAI, "\x01\x80");
			add_p(rc_plci, UID, "\x06\x43\x61\x70\x69\x32\x30");
			sig_req(rc_plci, ASSIGN, DSIG_ID);
			send_req(rc_plci);
		}
		else
		{
			Info = _OUT_OF_PLCI;
		}

		if (!Info)
		{
			add_s(rc_plci, CPN, &msg[0]);
			add_ai(rc_plci, &msg[1]);
			sig_req(rc_plci, NCR_FACILITY, 0);
			send_req(rc_plci);
			return false;
			/* for application controlled supplementary services    */
		}
	}

	if (!rc_plci)
	{
		Info = _WRONG_MESSAGE_FORMAT;
	}

	if (!Info)
	{
		send_req(rc_plci);
	}
	else
	{  /* appl is not assigned to a PLCI or error condition */
		dbug(1, dprintf("localInfoCon"));
		sendf(appl,
		      _INFO_R | CONFIRM,
		      Id,
		      Number,
		      "w", Info);
	}
	return false;
}

static byte info_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
		     PLCI *plci, APPL *appl, API_PARSE *msg)
{
	dbug(1, dprintf("info_res"));
	return false;
}

static byte alert_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
		      PLCI *plci, APPL *appl, API_PARSE *msg)
{
	word Info;
	byte ret;

	dbug(1, dprintf("alert_req"));

	Info = _WRONG_IDENTIFIER;
	ret = false;
	if (plci) {
		Info = _ALERT_IGNORED;
		if (plci->State != INC_CON_ALERT) {
			Info = _WRONG_STATE;
			if (plci->State == INC_CON_PENDING) {
				Info = 0;
				plci->State = INC_CON_ALERT;
				add_ai(plci, &msg[0]);
				sig_req(plci, CALL_ALERT, 0);
				ret = 1;
			}
		}
	}
	sendf(appl,
	      _ALERT_R | CONFIRM,
	      Id,
	      Number,
	      "w", Info);
	return ret;
}

static byte facility_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			 PLCI *plci, APPL *appl, API_PARSE *msg)
{
	word Info = 0;
	word i    = 0;

	word selector;
	word SSreq;
	long relatedPLCIvalue;
	DIVA_CAPI_ADAPTER *relatedadapter;
	byte *SSparms  = "";
	byte RCparms[]  = "\x05\x00\x00\x02\x00\x00";
	byte SSstruct[] = "\x09\x00\x00\x06\x00\x00\x00\x00\x00\x00";
	API_PARSE *parms;
	API_PARSE ss_parms[11];
	PLCI *rplci;
	byte cai[15];
	dword d;
	API_PARSE dummy;

	dbug(1, dprintf("facility_req"));
	for (i = 0; i < 9; i++) ss_parms[i].length = 0;

	parms = &msg[1];

	if (!a)
	{
		dbug(1, dprintf("wrong Ctrl"));
		Info = _WRONG_IDENTIFIER;
	}

	selector = GET_WORD(msg[0].info);

	if (!Info)
	{
		switch (selector)
		{
		case SELECTOR_HANDSET:
			Info = AdvCodecSupport(a, plci, appl, HOOK_SUPPORT);
			break;

		case SELECTOR_SU_SERV:
			if (!msg[1].length)
			{
				Info = _WRONG_MESSAGE_FORMAT;
				break;
			}
			SSreq = GET_WORD(&(msg[1].info[1]));
			PUT_WORD(&RCparms[1], SSreq);
			SSparms = RCparms;
			switch (SSreq)
			{
			case S_GET_SUPPORTED_SERVICES:
				if ((i = get_plci(a)))
				{
					rplci = &a->plci[i - 1];
					rplci->appl = appl;
					add_p(rplci, CAI, "\x01\x80");
					add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30");
					sig_req(rplci, ASSIGN, DSIG_ID);
					send_req(rplci);
				}
				else
				{
					PUT_DWORD(&SSstruct[6], MASK_TERMINAL_PORTABILITY);
					SSparms = (byte *)SSstruct;
					break;
				}
				rplci->internal_command = GETSERV_REQ_PEND;
				rplci->number = Number;
				rplci->appl = appl;
				sig_req(rplci, S_SUPPORTED, 0);
				send_req(rplci);
				return false;
				break;

			case S_LISTEN:
				if (parms->length == 7)
				{
					if (api_parse(&parms->info[1], (word)parms->length, "wbd", ss_parms))
					{
						dbug(1, dprintf("format wrong"));
						Info = _WRONG_MESSAGE_FORMAT;
						break;
					}
				}
				else
				{
					Info = _WRONG_MESSAGE_FORMAT;
					break;
				}
				a->Notification_Mask[appl->Id - 1] = GET_DWORD(ss_parms[2].info);
				if (a->Notification_Mask[appl->Id - 1] & SMASK_MWI) /* MWI active? */
				{
					if ((i = get_plci(a)))
					{
						rplci = &a->plci[i - 1];
						rplci->appl = appl;
						add_p(rplci, CAI, "\x01\x80");
						add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30");
						sig_req(rplci, ASSIGN, DSIG_ID);
						send_req(rplci);
					}
					else
					{
						break;
					}
					rplci->internal_command = GET_MWI_STATE;
					rplci->number = Number;
					sig_req(rplci, MWI_POLL, 0);
					send_req(rplci);
				}
				break;

			case S_HOLD:
				api_parse(&parms->info[1], (word)parms->length, "ws", ss_parms);
				if (plci && plci->State && plci->SuppState == IDLE)
				{
					plci->SuppState = HOLD_REQUEST;
					plci->command = C_HOLD_REQ;
					add_s(plci, CAI, &ss_parms[1]);
					sig_req(plci, CALL_HOLD, 0);
					send_req(plci);
					return false;
				}
				else Info = 0x3010;                    /* wrong state           */
				break;
			case S_RETRIEVE:
				if (plci && plci->State && plci->SuppState == CALL_HELD)
				{
					if (Id & EXT_CONTROLLER)
					{
						if (AdvCodecSupport(a, plci, appl, 0))
						{
							Info = 0x3010;                    /* wrong state           */
							break;
						}
					}
					else plci->tel = 0;

					plci->SuppState = RETRIEVE_REQUEST;
					plci->command = C_RETRIEVE_REQ;
					if (plci->spoofed_msg == SPOOFING_REQUIRED)
					{
						plci->spoofed_msg = CALL_RETRIEVE;
						plci->internal_command = BLOCK_PLCI;
						plci->command = 0;
						dbug(1, dprintf("Spoof"));
						return false;
					}
					else
					{
						sig_req(plci, CALL_RETRIEVE, 0);
						send_req(plci);
						return false;
					}
				}
				else Info = 0x3010;                    /* wrong state           */
				break;
			case S_SUSPEND:
				if (parms->length)
				{
					if (api_parse(&parms->info[1], (word)parms->length, "wbs", ss_parms))
					{
						dbug(1, dprintf("format wrong"));
						Info = _WRONG_MESSAGE_FORMAT;
						break;
					}
				}
				if (plci && plci->State)
				{
					add_s(plci, CAI, &ss_parms[2]);
					plci->command = SUSPEND_REQ;
					sig_req(plci, SUSPEND, 0);
					plci->State = SUSPENDING;
					send_req(plci);
				}
				else Info = 0x3010;                    /* wrong state           */
				break;

			case S_RESUME:
				if (!(i = get_plci(a)))
				{
					Info = _OUT_OF_PLCI;
					break;
				}
				rplci = &a->plci[i - 1];
				rplci->appl = appl;
				rplci->number = Number;
				rplci->tel = 0;
				rplci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
				/* check 'external controller' bit for codec support */
				if (Id & EXT_CONTROLLER)
				{
					if (AdvCodecSupport(a, rplci, appl, 0))
					{
						rplci->Id = 0;
						Info = 0x300A;
						break;
					}
				}
				if (parms->length)
				{
					if (api_parse(&parms->info[1], (word)parms->length, "wbs", ss_parms))
					{
						dbug(1, dprintf("format wrong"));
						rplci->Id = 0;
						Info = _WRONG_MESSAGE_FORMAT;
						break;
					}
				}
				dummy.length = 0;
				dummy.info = "\x00";
				add_b1(rplci, &dummy, 0, 0);
				if (a->Info_Mask[appl->Id - 1] & 0x200)
				{
					/* early B3 connect (CIP mask bit 9) no release after a disc */
					add_p(rplci, LLI, "\x01\x01");
				}
				add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30");
				sig_req(rplci, ASSIGN, DSIG_ID);
				send_req(rplci);
				add_s(rplci, CAI, &ss_parms[2]);
				rplci->command = RESUME_REQ;
				sig_req(rplci, RESUME, 0);
				rplci->State = RESUMING;
				send_req(rplci);
				break;

			case S_CONF_BEGIN: /* Request */
			case S_CONF_DROP:
			case S_CONF_ISOLATE:
			case S_CONF_REATTACH:
				if (api_parse(&parms->info[1], (word)parms->length, "wbd", ss_parms))
				{
					dbug(1, dprintf("format wrong"));
					Info = _WRONG_MESSAGE_FORMAT;
					break;
				}
				if (plci && plci->State && ((plci->SuppState == IDLE) || (plci->SuppState == CALL_HELD)))
				{
					d = GET_DWORD(ss_parms[2].info);
					if (d >= 0x80)
					{
						dbug(1, dprintf("format wrong"));
						Info = _WRONG_MESSAGE_FORMAT;
						break;
					}
					plci->ptyState = (byte)SSreq;
					plci->command = 0;
					cai[0] = 2;
					switch (SSreq)
					{
					case S_CONF_BEGIN:
						cai[1] = CONF_BEGIN;
						plci->internal_command = CONF_BEGIN_REQ_PEND;
						break;
					case S_CONF_DROP:
						cai[1] = CONF_DROP;
						plci->internal_command = CONF_DROP_REQ_PEND;
						break;
					case S_CONF_ISOLATE:
						cai[1] = CONF_ISOLATE;
						plci->internal_command = CONF_ISOLATE_REQ_PEND;
						break;
					case S_CONF_REATTACH:
						cai[1] = CONF_REATTACH;
						plci->internal_command = CONF_REATTACH_REQ_PEND;
						break;
					}
					cai[2] = (byte)d; /* Conference Size resp. PartyId */
					add_p(plci, CAI, cai);
					sig_req(plci, S_SERVICE, 0);
					send_req(plci);
					return false;
				}
				else Info = 0x3010;                    /* wrong state           */
				break;

			case S_ECT:
			case S_3PTY_BEGIN:
			case S_3PTY_END:
			case S_CONF_ADD:
				if (parms->length == 7)
				{
					if (api_parse(&parms->info[1], (word)parms->length, "wbd", ss_parms))
					{
						dbug(1, dprintf("format wrong"));
						Info = _WRONG_MESSAGE_FORMAT;
						break;
					}
				}
				else if (parms->length == 8) /* workaround for the T-View-S */
				{
					if (api_parse(&parms->info[1], (word)parms->length, "wbdb", ss_parms))
					{
						dbug(1, dprintf("format wrong"));
						Info = _WRONG_MESSAGE_FORMAT;
						break;
					}
				}
				else
				{
					Info = _WRONG_MESSAGE_FORMAT;
					break;
				}
				if (!msg[1].length)
				{
					Info = _WRONG_MESSAGE_FORMAT;
					break;
				}
				if (!plci)
				{
					Info = _WRONG_IDENTIFIER;
					break;
				}
				relatedPLCIvalue = GET_DWORD(ss_parms[2].info);
				relatedPLCIvalue &= 0x0000FFFF;
				dbug(1, dprintf("PTY/ECT/addCONF,relPLCI=%lx", relatedPLCIvalue));
				/* controller starts with 0 up to (max_adapter - 1) */
				if (((relatedPLCIvalue & 0x7f) == 0)
				    || (MapController((byte)(relatedPLCIvalue & 0x7f)) == 0)
				    || (MapController((byte)(relatedPLCIvalue & 0x7f)) > max_adapter))
				{
					if (SSreq == S_3PTY_END)
					{
						dbug(1, dprintf("wrong Controller use 2nd PLCI=PLCI"));
						rplci = plci;
					}
					else
					{
						Info = 0x3010;                    /* wrong state           */
						break;
					}
				}
				else
				{
					relatedadapter = &adapter[MapController((byte)(relatedPLCIvalue & 0x7f)) - 1];
					relatedPLCIvalue >>= 8;
					/* find PLCI PTR*/
					for (i = 0, rplci = NULL; i < relatedadapter->max_plci; i++)
					{
						if (relatedadapter->plci[i].Id == (byte)relatedPLCIvalue)
						{
							rplci = &relatedadapter->plci[i];
						}
					}
					if (!rplci || !relatedPLCIvalue)
					{
						if (SSreq == S_3PTY_END)
						{
							dbug(1, dprintf("use 2nd PLCI=PLCI"));
							rplci = plci;
						}
						else
						{
							Info = 0x3010;                    /* wrong state           */
							break;
						}
					}
				}
/*
  dbug(1, dprintf("rplci:%x", rplci));
  dbug(1, dprintf("plci:%x", plci));
  dbug(1, dprintf("rplci->ptyState:%x", rplci->ptyState));
  dbug(1, dprintf("plci->ptyState:%x", plci->ptyState));
  dbug(1, dprintf("SSreq:%x", SSreq));
  dbug(1, dprintf("rplci->internal_command:%x", rplci->internal_command));
  dbug(1, dprintf("rplci->appl:%x", rplci->appl));
  dbug(1, dprintf("rplci->Id:%x", rplci->Id));
*/
				/* send PTY/ECT req, cannot check all states because of US stuff */
				if (!rplci->internal_command && rplci->appl)
				{
					plci->command = 0;
					rplci->relatedPTYPLCI = plci;
					plci->relatedPTYPLCI = rplci;
					rplci->ptyState = (byte)SSreq;
					if (SSreq == S_ECT)
					{
						rplci->internal_command = ECT_REQ_PEND;
						cai[1] = ECT_EXECUTE;

						rplci->vswitchstate = 0;
						rplci->vsprot = 0;
						rplci->vsprotdialect = 0;
						plci->vswitchstate = 0;
						plci->vsprot = 0;
						plci->vsprotdialect = 0;

					}
					else if (SSreq == S_CONF_ADD)
					{
						rplci->internal_command = CONF_ADD_REQ_PEND;
						cai[1] = CONF_ADD;
					}
					else
					{
						rplci->internal_command = PTY_REQ_PEND;
						cai[1] = (byte)(SSreq - 3);
					}
					rplci->number = Number;
					if (plci != rplci) /* explicit invocation */
					{
						cai[0] = 2;
						cai[2] = plci->Sig.Id;
						dbug(1, dprintf("explicit invocation"));
					}
					else
					{
						dbug(1, dprintf("implicit invocation"));
						cai[0] = 1;
					}
					add_p(rplci, CAI, cai);
					sig_req(rplci, S_SERVICE, 0);
					send_req(rplci);
					return false;
				}
				else
				{
					dbug(0, dprintf("Wrong line"));
					Info = 0x3010;                    /* wrong state           */
					break;
				}
				break;

			case S_CALL_DEFLECTION:
				if (api_parse(&parms->info[1], (word)parms->length, "wbwss", ss_parms))
				{
					dbug(1, dprintf("format wrong"));
					Info = _WRONG_MESSAGE_FORMAT;
					break;
				}
				if (!plci)
				{
					Info = _WRONG_IDENTIFIER;
					break;
				}
				/* reuse unused screening indicator */
				ss_parms[3].info[3] = (byte)GET_WORD(&(ss_parms[2].info[0]));
				plci->command = 0;
				plci->internal_command = CD_REQ_PEND;
				appl->CDEnable = true;
				cai[0] = 1;
				cai[1] = CALL_DEFLECTION;
				add_p(plci, CAI, cai);
				add_p(plci, CPN, ss_parms[3].info);
				sig_req(plci, S_SERVICE, 0);
				send_req(plci);
				return false;
				break;

			case S_CALL_FORWARDING_START:
				if (api_parse(&parms->info[1], (word)parms->length, "wbdwwsss", ss_parms))
				{
					dbug(1, dprintf("format wrong"));
					Info = _WRONG_MESSAGE_FORMAT;
					break;
				}

				if ((i = get_plci(a)))
				{
					rplci = &a->plci[i - 1];
					rplci->appl = appl;
					add_p(rplci, CAI, "\x01\x80");
					add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30");
					sig_req(rplci, ASSIGN, DSIG_ID);
					send_req(rplci);
				}
				else
				{
					Info = _OUT_OF_PLCI;
					break;
				}

				/* reuse unused screening indicator */
				rplci->internal_command = CF_START_PEND;
				rplci->appl = appl;
				rplci->number = Number;
				appl->S_Handle = GET_DWORD(&(ss_parms[2].info[0]));
				cai[0] = 2;
				cai[1] = 0x70 | (byte)GET_WORD(&(ss_parms[3].info[0])); /* Function */
				cai[2] = (byte)GET_WORD(&(ss_parms[4].info[0])); /* Basic Service */
				add_p(rplci, CAI, cai);
				add_p(rplci, OAD, ss_parms[5].info);
				add_p(rplci, CPN, ss_parms[6].info);
				sig_req(rplci, S_SERVICE, 0);
				send_req(rplci);
				return false;
				break;

			case S_INTERROGATE_DIVERSION:
			case S_INTERROGATE_NUMBERS:
			case S_CALL_FORWARDING_STOP:
			case S_CCBS_REQUEST:
			case S_CCBS_DEACTIVATE:
			case S_CCBS_INTERROGATE:
				switch (SSreq)
				{
				case S_INTERROGATE_NUMBERS:
					if (api_parse(&parms->info[1], (word)parms->length, "wbd", ss_parms))
					{
						dbug(0, dprintf("format wrong"));
						Info = _WRONG_MESSAGE_FORMAT;
					}
					break;
				case S_CCBS_REQUEST:
				case S_CCBS_DEACTIVATE:
					if (api_parse(&parms->info[1], (word)parms->length, "wbdw", ss_parms))
					{
						dbug(0, dprintf("format wrong"));
						Info = _WRONG_MESSAGE_FORMAT;
					}
					break;
				case S_CCBS_INTERROGATE:
					if (api_parse(&parms->info[1], (word)parms->length, "wbdws", ss_parms))
					{
						dbug(0, dprintf("format wrong"));
						Info = _WRONG_MESSAGE_FORMAT;
					}
					break;
				default:
					if (api_parse(&parms->info[1], (word)parms->length, "wbdwws", ss_parms))
					{
						dbug(0, dprintf("format wrong"));
						Info = _WRONG_MESSAGE_FORMAT;
						break;
					}
					break;
				}

				if (Info) break;
				if ((i = get_plci(a)))
				{
					rplci = &a->plci[i - 1];
					switch (SSreq)
					{
					case S_INTERROGATE_DIVERSION: /* use cai with S_SERVICE below */
						cai[1] = 0x60 | (byte)GET_WORD(&(ss_parms[3].info[0])); /* Function */
						rplci->internal_command = INTERR_DIVERSION_REQ_PEND; /* move to rplci if assigned */
						break;
					case S_INTERROGATE_NUMBERS: /* use cai with S_SERVICE below */
						cai[1] = DIVERSION_INTERROGATE_NUM; /* Function */
						rplci->internal_command = INTERR_NUMBERS_REQ_PEND; /* move to rplci if assigned */
						break;
					case S_CALL_FORWARDING_STOP:
						rplci->internal_command = CF_STOP_PEND;
						cai[1] = 0x80 | (byte)GET_WORD(&(ss_parms[3].info[0])); /* Function */
						break;
					case S_CCBS_REQUEST:
						cai[1] = CCBS_REQUEST;
						rplci->internal_command = CCBS_REQUEST_REQ_PEND;
						break;
					case S_CCBS_DEACTIVATE:
						cai[1] = CCBS_DEACTIVATE;
						rplci->internal_command = CCBS_DEACTIVATE_REQ_PEND;
						break;
					case S_CCBS_INTERROGATE:
						cai[1] = CCBS_INTERROGATE;
						rplci->internal_command = CCBS_INTERROGATE_REQ_PEND;
						break;
					default:
						cai[1] = 0;
						break;
					}
					rplci->appl = appl;
					rplci->number = Number;
					add_p(rplci, CAI, "\x01\x80");
					add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30");
					sig_req(rplci, ASSIGN, DSIG_ID);
					send_req(rplci);
				}
				else
				{
					Info = _OUT_OF_PLCI;
					break;
				}

				appl->S_Handle = GET_DWORD(&(ss_parms[2].info[0]));
				switch (SSreq)
				{
				case S_INTERROGATE_NUMBERS:
					cai[0] = 1;
					add_p(rplci, CAI, cai);
					break;
				case S_CCBS_REQUEST:
				case S_CCBS_DEACTIVATE:
					cai[0] = 3;
					PUT_WORD(&cai[2], GET_WORD(&(ss_parms[3].info[0])));
					add_p(rplci, CAI, cai);
					break;
				case S_CCBS_INTERROGATE:
					cai[0] = 3;
					PUT_WORD(&cai[2], GET_WORD(&(ss_parms[3].info[0])));
					add_p(rplci, CAI, cai);
					add_p(rplci, OAD, ss_parms[4].info);
					break;
				default:
					cai[0] = 2;
					cai[2] = (byte)GET_WORD(&(ss_parms[4].info[0])); /* Basic Service */
					add_p(rplci, CAI, cai);
					add_p(rplci, OAD, ss_parms[5].info);
					break;
				}

				sig_req(rplci, S_SERVICE, 0);
				send_req(rplci);
				return false;
				break;

			case S_MWI_ACTIVATE:
				if (api_parse(&parms->info[1], (word)parms->length, "wbwdwwwssss", ss_parms))
				{
					dbug(1, dprintf("format wrong"));
					Info = _WRONG_MESSAGE_FORMAT;
					break;
				}
				if (!plci)
				{
					if ((i = get_plci(a)))
					{
						rplci = &a->plci[i - 1];
						rplci->appl = appl;
						rplci->cr_enquiry = true;
						add_p(rplci, CAI, "\x01\x80");
						add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30");
						sig_req(rplci, ASSIGN, DSIG_ID);
						send_req(rplci);
					}
					else
					{
						Info = _OUT_OF_PLCI;
						break;
					}
				}
				else
				{
					rplci = plci;
					rplci->cr_enquiry = false;
				}

				rplci->command = 0;
				rplci->internal_command = MWI_ACTIVATE_REQ_PEND;
				rplci->appl = appl;
				rplci->number = Number;

				cai[0] = 13;
				cai[1] = ACTIVATION_MWI; /* Function */
				PUT_WORD(&cai[2], GET_WORD(&(ss_parms[2].info[0]))); /* Basic Service */
				PUT_DWORD(&cai[4], GET_DWORD(&(ss_parms[3].info[0]))); /* Number of Messages */
				PUT_WORD(&cai[8], GET_WORD(&(ss_parms[4].info[0]))); /* Message Status */
				PUT_WORD(&cai[10], GET_WORD(&(ss_parms[5].info[0]))); /* Message Reference */
				PUT_WORD(&cai[12], GET_WORD(&(ss_parms[6].info[0]))); /* Invocation Mode */
				add_p(rplci, CAI, cai);
				add_p(rplci, CPN, ss_parms[7].info); /* Receiving User Number */
				add_p(rplci, OAD, ss_parms[8].info); /* Controlling User Number */
				add_p(rplci, OSA, ss_parms[9].info); /* Controlling User Provided Number */
				add_p(rplci, UID, ss_parms[10].info); /* Time */
				sig_req(rplci, S_SERVICE, 0);
				send_req(rplci);
				return false;

			case S_MWI_DEACTIVATE:
				if (api_parse(&parms->info[1], (word)parms->length, "wbwwss", ss_parms))
				{
					dbug(1, dprintf("format wrong"));
					Info = _WRONG_MESSAGE_FORMAT;
					break;
				}
				if (!plci)
				{
					if ((i = get_plci(a)))
					{
						rplci = &a->plci[i - 1];
						rplci->appl = appl;
						rplci->cr_enquiry = true;
						add_p(rplci, CAI, "\x01\x80");
						add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30");
						sig_req(rplci, ASSIGN, DSIG_ID);
						send_req(rplci);
					}
					else
					{
						Info = _OUT_OF_PLCI;
						break;
					}
				}
				else
				{
					rplci = plci;
					rplci->cr_enquiry = false;
				}

				rplci->command = 0;
				rplci->internal_command = MWI_DEACTIVATE_REQ_PEND;
				rplci->appl = appl;
				rplci->number = Number;

				cai[0] = 5;
				cai[1] = DEACTIVATION_MWI; /* Function */
				PUT_WORD(&cai[2], GET_WORD(&(ss_parms[2].info[0]))); /* Basic Service */
				PUT_WORD(&cai[4], GET_WORD(&(ss_parms[3].info[0]))); /* Invocation Mode */
				add_p(rplci, CAI, cai);
				add_p(rplci, CPN, ss_parms[4].info); /* Receiving User Number */
				add_p(rplci, OAD, ss_parms[5].info); /* Controlling User Number */
				sig_req(rplci, S_SERVICE, 0);
				send_req(rplci);
				return false;

			default:
				Info = 0x300E;  /* not supported */
				break;
			}
			break; /* case SELECTOR_SU_SERV: end */


		case SELECTOR_DTMF:
			return (dtmf_request(Id, Number, a, plci, appl, msg));



		case SELECTOR_LINE_INTERCONNECT:
			return (mixer_request(Id, Number, a, plci, appl, msg));



		case PRIV_SELECTOR_ECHO_CANCELLER:
			appl->appl_flags |= APPL_FLAG_PRIV_EC_SPEC;
			return (ec_request(Id, Number, a, plci, appl, msg));

		case SELECTOR_ECHO_CANCELLER:
			appl->appl_flags &= ~APPL_FLAG_PRIV_EC_SPEC;
			return (ec_request(Id, Number, a, plci, appl, msg));


		case SELECTOR_V42BIS:
		default:
			Info = _FACILITY_NOT_SUPPORTED;
			break;
		} /* end of switch (selector) */
	}

	dbug(1, dprintf("SendFacRc"));
	sendf(appl,
	      _FACILITY_R | CONFIRM,
	      Id,
	      Number,
	      "wws", Info, selector, SSparms);
	return false;
}

static byte facility_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			 PLCI *plci, APPL *appl, API_PARSE *msg)
{
	dbug(1, dprintf("facility_res"));
	return false;
}

static byte connect_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			   PLCI *plci, APPL *appl, API_PARSE *parms)
{
	word Info = 0;
	byte req;
	byte len;
	word w;
	word fax_control_bits, fax_feature_bits, fax_info_change;
	API_PARSE *ncpi;
	byte pvc[2];

	API_PARSE fax_parms[9];
	word i;


	dbug(1, dprintf("connect_b3_req"));
	if (plci)
	{
		if ((plci->State == IDLE) || (plci->State == OUTG_DIS_PENDING)
		    || (plci->State == INC_DIS_PENDING) || (plci->SuppState != IDLE))
		{
			Info = _WRONG_STATE;
		}
		else
		{
			/* local reply if assign unsuccessful
			   or B3 protocol allows only one layer 3 connection
			   and already connected
			   or B2 protocol not any LAPD
			   and connect_b3_req contradicts originate/answer direction */
			if (!plci->NL.Id
			    || (((plci->B3_prot != B3_T90NL) && (plci->B3_prot != B3_ISO8208) && (plci->B3_prot != B3_X25_DCE))
				&& ((plci->channels != 0)
				    || (((plci->B2_prot != B2_SDLC) && (plci->B2_prot != B2_LAPD) && (plci->B2_prot != B2_LAPD_FREE_SAPI_SEL))
					&& ((plci->call_dir & CALL_DIR_ANSWER) && !(plci->call_dir & CALL_DIR_FORCE_OUTG_NL))))))
			{
				dbug(1, dprintf("B3 already connected=%d or no NL.Id=0x%x, dir=%d sstate=0x%x",
						plci->channels, plci->NL.Id, plci->call_dir, plci->SuppState));
				Info = _WRONG_STATE;
				sendf(appl,
				      _CONNECT_B3_R | CONFIRM,
				      Id,
				      Number,
				      "w", Info);
				return false;
			}
			plci->requested_options_conn = 0;

			req = N_CONNECT;
			ncpi = &parms[0];
			if (plci->B3_prot == 2 || plci->B3_prot == 3)
			{
				if (ncpi->length > 2)
				{
					/* check for PVC */
					if (ncpi->info[2] || ncpi->info[3])
					{
						pvc[0] = ncpi->info[3];
						pvc[1] = ncpi->info[2];
						add_d(plci, 2, pvc);
						req = N_RESET;
					}
					else
					{
						if (ncpi->info[1] & 1) req = N_CONNECT | N_D_BIT;
						add_d(plci, (word)(ncpi->length - 3), &ncpi->info[4]);
					}
				}
			}
			else if (plci->B3_prot == 5)
			{
				if (plci->NL.Id && !plci->nl_remove_id)
				{
					fax_control_bits = GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low);
					fax_feature_bits = GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->feature_bits_low);
					if (!(fax_control_bits & T30_CONTROL_BIT_MORE_DOCUMENTS)
					    || (fax_feature_bits & T30_FEATURE_BIT_MORE_DOCUMENTS))
					{
						len = offsetof(T30_INFO, universal_6);
						fax_info_change = false;
						if (ncpi->length >= 4)
						{
							w = GET_WORD(&ncpi->info[3]);
							if ((w & 0x0001) != ((word)(((T30_INFO *)(plci->fax_connect_info_buffer))->resolution & 0x0001)))
							{
								((T30_INFO *)(plci->fax_connect_info_buffer))->resolution =
									(byte)((((T30_INFO *)(plci->fax_connect_info_buffer))->resolution & ~T30_RESOLUTION_R8_0770_OR_200) |
									       ((w & 0x0001) ? T30_RESOLUTION_R8_0770_OR_200 : 0));
								fax_info_change = true;
							}
							fax_control_bits &= ~(T30_CONTROL_BIT_REQUEST_POLLING | T30_CONTROL_BIT_MORE_DOCUMENTS);
							if (w & 0x0002)  /* Fax-polling request */
								fax_control_bits |= T30_CONTROL_BIT_REQUEST_POLLING;
							if ((w & 0x0004) /* Request to send / poll another document */
							    && (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_MORE_DOCUMENTS))
							{
								fax_control_bits |= T30_CONTROL_BIT_MORE_DOCUMENTS;
							}
							if (ncpi->length >= 6)
							{
								w = GET_WORD(&ncpi->info[5]);
								if (((byte) w) != ((T30_INFO *)(plci->fax_connect_info_buffer))->data_format)
								{
									((T30_INFO *)(plci->fax_connect_info_buffer))->data_format = (byte) w;
									fax_info_change = true;
								}

								if ((a->man_profile.private_options & (1L << PRIVATE_FAX_SUB_SEP_PWD))
								    && (GET_WORD(&ncpi->info[5]) & 0x8000)) /* Private SEP/SUB/PWD enable */
								{
									plci->requested_options_conn |= (1L << PRIVATE_FAX_SUB_SEP_PWD);
								}
								if ((a->man_profile.private_options & (1L << PRIVATE_FAX_NONSTANDARD))
								    && (GET_WORD(&ncpi->info[5]) & 0x4000)) /* Private non-standard facilities enable */
								{
									plci->requested_options_conn |= (1L << PRIVATE_FAX_NONSTANDARD);
								}
								fax_control_bits &= ~(T30_CONTROL_BIT_ACCEPT_SUBADDRESS | T30_CONTROL_BIT_ACCEPT_SEL_POLLING |
										      T30_CONTROL_BIT_ACCEPT_PASSWORD);
								if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[appl->Id - 1])
								    & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD)))
								{
									if (api_parse(&ncpi->info[1], ncpi->length, "wwwwsss", fax_parms))
										Info = _WRONG_MESSAGE_FORMAT;
									else
									{
										if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[appl->Id - 1])
										    & (1L << PRIVATE_FAX_SUB_SEP_PWD))
										{
											fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SUBADDRESS | T30_CONTROL_BIT_ACCEPT_PASSWORD;
											if (fax_control_bits & T30_CONTROL_BIT_ACCEPT_POLLING)
												fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SEL_POLLING;
										}
										w = fax_parms[4].length;
										if (w > 20)
											w = 20;
										((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = (byte) w;
										for (i = 0; i < w; i++)
											((T30_INFO *)(plci->fax_connect_info_buffer))->station_id[i] = fax_parms[4].info[1 + i];
										((T30_INFO *)(plci->fax_connect_info_buffer))->head_line_len = 0;
										len = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH;
										w = fax_parms[5].length;
										if (w > 20)
											w = 20;
										plci->fax_connect_info_buffer[len++] = (byte) w;
										for (i = 0; i < w; i++)
											plci->fax_connect_info_buffer[len++] = fax_parms[5].info[1 + i];
										w = fax_parms[6].length;
										if (w > 20)
											w = 20;
										plci->fax_connect_info_buffer[len++] = (byte) w;
										for (i = 0; i < w; i++)
											plci->fax_connect_info_buffer[len++] = fax_parms[6].info[1 + i];
										if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[appl->Id - 1])
										    & (1L << PRIVATE_FAX_NONSTANDARD))
										{
											if (api_parse(&ncpi->info[1], ncpi->length, "wwwwssss", fax_parms))
											{
												dbug(1, dprintf("non-standard facilities info missing or wrong format"));
												plci->fax_connect_info_buffer[len++] = 0;
											}
											else
											{
												if ((fax_parms[7].length >= 3) && (fax_parms[7].info[1] >= 2))
													plci->nsf_control_bits = GET_WORD(&fax_parms[7].info[2]);
												plci->fax_connect_info_buffer[len++] = (byte)(fax_parms[7].length);
												for (i = 0; i < fax_parms[7].length; i++)
													plci->fax_connect_info_buffer[len++] = fax_parms[7].info[1 + i];
											}
										}
									}
								}
								else
								{
									len = offsetof(T30_INFO, universal_6);
								}
								fax_info_change = true;

							}
							if (fax_control_bits != GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low))
							{
								PUT_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low, fax_control_bits);
								fax_info_change = true;
							}
						}
						if (Info == GOOD)
						{
							plci->fax_connect_info_length = len;
							if (fax_info_change)
							{
								if (fax_feature_bits & T30_FEATURE_BIT_MORE_DOCUMENTS)
								{
									start_internal_command(Id, plci, fax_connect_info_command);
									return false;
								}
								else
								{
									start_internal_command(Id, plci, fax_adjust_b23_command);
									return false;
								}
							}
						}
					}
					else  Info = _WRONG_STATE;
				}
				else  Info = _WRONG_STATE;
			}

			else if (plci->B3_prot == B3_RTP)
			{
				plci->internal_req_buffer[0] = ncpi->length + 1;
				plci->internal_req_buffer[1] = UDATA_REQUEST_RTP_RECONFIGURE;
				for (w = 0; w < ncpi->length; w++)
					plci->internal_req_buffer[2 + w] = ncpi->info[1 + w];
				start_internal_command(Id, plci, rtp_connect_b3_req_command);
				return false;
			}

			if (!Info)
			{
				nl_req_ncci(plci, req, 0);
				return 1;
			}
		}
	}
	else Info = _WRONG_IDENTIFIER;

	sendf(appl,
	      _CONNECT_B3_R | CONFIRM,
	      Id,
	      Number,
	      "w", Info);
	return false;
}

static byte connect_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			   PLCI *plci, APPL *appl, API_PARSE *parms)
{
	word ncci;
	API_PARSE *ncpi;
	byte req;

	word w;


	API_PARSE fax_parms[9];
	word i;
	byte len;


	dbug(1, dprintf("connect_b3_res"));

	ncci = (word)(Id >> 16);
	if (plci && ncci) {
		if (a->ncci_state[ncci] == INC_CON_PENDING) {
			if (GET_WORD(&parms[0].info[0]) != 0)
			{
				a->ncci_state[ncci] = OUTG_REJ_PENDING;
				channel_request_xon(plci, a->ncci_ch[ncci]);
				channel_xmit_xon(plci);
				cleanup_ncci_data(plci, ncci);
				nl_req_ncci(plci, N_DISC, (byte)ncci);
				return 1;
			}
			a->ncci_state[ncci] = INC_ACT_PENDING;

			req = N_CONNECT_ACK;
			ncpi = &parms[1];
			if ((plci->B3_prot == 4) || (plci->B3_prot == 5) || (plci->B3_prot == 7))
			{

				if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id - 1])
				    & (1L << PRIVATE_FAX_NONSTANDARD))
				{
					if (((plci->B3_prot == 4) || (plci->B3_prot == 5))
					    && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF)
					    && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP))
					{
						len = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH;
						if (plci->fax_connect_info_length < len)
						{
							((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = 0;
							((T30_INFO *)(plci->fax_connect_info_buffer))->head_line_len = 0;
						}
						if (api_parse(&ncpi->info[1], ncpi->length, "wwwwssss", fax_parms))
						{
							dbug(1, dprintf("non-standard facilities info missing or wrong format"));
						}
						else
						{
							if (plci->fax_connect_info_length <= len)
								plci->fax_connect_info_buffer[len] = 0;
							len += 1 + plci->fax_connect_info_buffer[len];
							if (plci->fax_connect_info_length <= len)
								plci->fax_connect_info_buffer[len] = 0;
							len += 1 + plci->fax_connect_info_buffer[len];
							if ((fax_parms[7].length >= 3) && (fax_parms[7].info[1] >= 2))
								plci->nsf_control_bits = GET_WORD(&fax_parms[7].info[2]);
							plci->fax_connect_info_buffer[len++] = (byte)(fax_parms[7].length);
							for (i = 0; i < fax_parms[7].length; i++)
								plci->fax_connect_info_buffer[len++] = fax_parms[7].info[1 + i];
						}
						plci->fax_connect_info_length = len;
						((T30_INFO *)(plci->fax_connect_info_buffer))->code = 0;
						start_internal_command(Id, plci, fax_connect_ack_command);
						return false;
					}
				}

				nl_req_ncci(plci, req, (byte)ncci);
				if ((plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
				    && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
				{
					if (plci->B3_prot == 4)
						sendf(appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
					else
						sendf(appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer);
					plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
				}
			}

			else if (plci->B3_prot == B3_RTP)
			{
				plci->internal_req_buffer[0] = ncpi->length + 1;
				plci->internal_req_buffer[1] = UDATA_REQUEST_RTP_RECONFIGURE;
				for (w = 0; w < ncpi->length; w++)
					plci->internal_req_buffer[2 + w] = ncpi->info[1+w];
				start_internal_command(Id, plci, rtp_connect_b3_res_command);
				return false;
			}

			else
			{
				if (ncpi->length > 2) {
					if (ncpi->info[1] & 1) req = N_CONNECT_ACK | N_D_BIT;
					add_d(plci, (word)(ncpi->length - 3), &ncpi->info[4]);
				}
				nl_req_ncci(plci, req, (byte)ncci);
				sendf(appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
				if (plci->adjust_b_restore)
				{
					plci->adjust_b_restore = false;
					start_internal_command(Id, plci, adjust_b_restore);
				}
			}
			return 1;
		}
	}
	return false;
}

static byte connect_b3_a_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			     PLCI *plci, APPL *appl, API_PARSE *parms)
{
	word ncci;

	ncci = (word)(Id >> 16);
	dbug(1, dprintf("connect_b3_a_res(ncci=0x%x)", ncci));

	if (plci && ncci && (plci->State != IDLE) && (plci->State != INC_DIS_PENDING)
	    && (plci->State != OUTG_DIS_PENDING))
	{
		if (a->ncci_state[ncci] == INC_ACT_PENDING) {
			a->ncci_state[ncci] = CONNECTED;
			if (plci->State != INC_CON_CONNECTED_ALERT) plci->State = CONNECTED;
			channel_request_xon(plci, a->ncci_ch[ncci]);
			channel_xmit_xon(plci);
		}
	}
	return false;
}

static byte disconnect_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			      PLCI *plci, APPL *appl, API_PARSE *parms)
{
	word Info;
	word ncci;
	API_PARSE *ncpi;

	dbug(1, dprintf("disconnect_b3_req"));

	Info = _WRONG_IDENTIFIER;
	ncci = (word)(Id >> 16);
	if (plci && ncci)
	{
		Info = _WRONG_STATE;
		if ((a->ncci_state[ncci] == CONNECTED)
		    || (a->ncci_state[ncci] == OUTG_CON_PENDING)
		    || (a->ncci_state[ncci] == INC_CON_PENDING)
		    || (a->ncci_state[ncci] == INC_ACT_PENDING))
		{
			a->ncci_state[ncci] = OUTG_DIS_PENDING;
			channel_request_xon(plci, a->ncci_ch[ncci]);
			channel_xmit_xon(plci);

			if (a->ncci[ncci].data_pending
			    && ((plci->B3_prot == B3_TRANSPARENT)
				|| (plci->B3_prot == B3_T30)
				|| (plci->B3_prot == B3_T30_WITH_EXTENSIONS)))
			{
				plci->send_disc = (byte)ncci;
				plci->command = 0;
				return false;
			}
			else
			{
				cleanup_ncci_data(plci, ncci);

				if (plci->B3_prot == 2 || plci->B3_prot == 3)
				{
					ncpi = &parms[0];
					if (ncpi->length > 3)
					{
						add_d(plci, (word)(ncpi->length - 3), (byte *)&(ncpi->info[4]));
					}
				}
				nl_req_ncci(plci, N_DISC, (byte)ncci);
			}
			return 1;
		}
	}
	sendf(appl,
	      _DISCONNECT_B3_R | CONFIRM,
	      Id,
	      Number,
	      "w", Info);
	return false;
}

static byte disconnect_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			      PLCI *plci, APPL *appl, API_PARSE *parms)
{
	word ncci;
	word i;

	ncci = (word)(Id >> 16);
	dbug(1, dprintf("disconnect_b3_res(ncci=0x%x", ncci));
	if (plci && ncci) {
		plci->requested_options_conn = 0;
		plci->fax_connect_info_length = 0;
		plci->ncpi_state = 0x00;
		if (((plci->B3_prot != B3_T90NL) && (plci->B3_prot != B3_ISO8208) && (plci->B3_prot != B3_X25_DCE))
		    && ((plci->B2_prot != B2_LAPD) && (plci->B2_prot != B2_LAPD_FREE_SAPI_SEL)))
		{
			plci->call_dir |= CALL_DIR_FORCE_OUTG_NL;
		}
		for (i = 0; i < MAX_CHANNELS_PER_PLCI && plci->inc_dis_ncci_table[i] != (byte)ncci; i++);
		if (i < MAX_CHANNELS_PER_PLCI) {
			if (plci->channels)plci->channels--;
			for (; i < MAX_CHANNELS_PER_PLCI - 1; i++) plci->inc_dis_ncci_table[i] = plci->inc_dis_ncci_table[i + 1];
			plci->inc_dis_ncci_table[MAX_CHANNELS_PER_PLCI - 1] = 0;

			ncci_free_receive_buffers(plci, ncci);

			if ((plci->State == IDLE || plci->State == SUSPENDING) && !plci->channels) {
				if (plci->State == SUSPENDING) {
					sendf(plci->appl,
					      _FACILITY_I,
					      Id & 0xffffL,
					      0,
					      "ws", (word)3, "\x03\x04\x00\x00");
					sendf(plci->appl, _DISCONNECT_I, Id & 0xffffL, 0, "w", 0);
				}
				plci_remove(plci);
				plci->State = IDLE;
			}
		}
		else
		{
			if ((a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
			    && ((plci->B3_prot == 4) || (plci->B3_prot == 5))
			    && (a->ncci_state[ncci] == INC_DIS_PENDING))
			{
				ncci_free_receive_buffers(plci, ncci);

				nl_req_ncci(plci, N_EDATA, (byte)ncci);

				plci->adapter->ncci_state[ncci] = IDLE;
				start_internal_command(Id, plci, fax_disconnect_command);
				return 1;
			}
		}
	}
	return false;
}

static byte data_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			PLCI *plci, APPL *appl, API_PARSE *parms)
{
	NCCI *ncci_ptr;
	DATA_B3_DESC *data;
	word Info;
	word ncci;
	word i;

	dbug(1, dprintf("data_b3_req"));

	Info = _WRONG_IDENTIFIER;
	ncci = (word)(Id >> 16);
	dbug(1, dprintf("ncci=0x%x, plci=0x%x", ncci, plci));

	if (plci && ncci)
	{
		Info = _WRONG_STATE;
		if ((a->ncci_state[ncci] == CONNECTED)
		    || (a->ncci_state[ncci] == INC_ACT_PENDING))
		{
			/* queue data */
			ncci_ptr = &(a->ncci[ncci]);
			i = ncci_ptr->data_out + ncci_ptr->data_pending;
			if (i >= MAX_DATA_B3)
				i -= MAX_DATA_B3;
			data = &(ncci_ptr->DBuffer[i]);
			data->Number = Number;
			if ((((byte *)(parms[0].info)) >= ((byte *)(plci->msg_in_queue)))
			    && (((byte *)(parms[0].info)) < ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue)))
			{

				data->P = (byte *)(long)(*((dword *)(parms[0].info)));

			}
			else
				data->P = TransmitBufferSet(appl, *(dword *)parms[0].info);
			data->Length = GET_WORD(parms[1].info);
			data->Handle = GET_WORD(parms[2].info);
			data->Flags = GET_WORD(parms[3].info);
			(ncci_ptr->data_pending)++;

			/* check for delivery confirmation */
			if (data->Flags & 0x0004)
			{
				i = ncci_ptr->data_ack_out + ncci_ptr->data_ack_pending;
				if (i >= MAX_DATA_ACK)
					i -= MAX_DATA_ACK;
				ncci_ptr->DataAck[i].Number = data->Number;
				ncci_ptr->DataAck[i].Handle = data->Handle;
				(ncci_ptr->data_ack_pending)++;
			}

			send_data(plci);
			return false;
		}
	}
	if (appl)
	{
		if (plci)
		{
			if ((((byte *)(parms[0].info)) >= ((byte *)(plci->msg_in_queue)))
			    && (((byte *)(parms[0].info)) < ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue)))
			{

				TransmitBufferFree(appl, (byte *)(long)(*((dword *)(parms[0].info))));

			}
		}
		sendf(appl,
		      _DATA_B3_R | CONFIRM,
		      Id,
		      Number,
		      "ww", GET_WORD(parms[2].info), Info);
	}
	return false;
}

static byte data_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			PLCI *plci, APPL *appl, API_PARSE *parms)
{
	word n;
	word ncci;
	word NCCIcode;

	dbug(1, dprintf("data_b3_res"));

	ncci = (word)(Id >> 16);
	if (plci && ncci) {
		n = GET_WORD(parms[0].info);
		dbug(1, dprintf("free(%d)", n));
		NCCIcode = ncci | (((word) a->Id) << 8);
		if (n < appl->MaxBuffer &&
		    appl->DataNCCI[n] == NCCIcode &&
		    (byte)(appl->DataFlags[n] >> 8) == plci->Id) {
			dbug(1, dprintf("found"));
			appl->DataNCCI[n] = 0;

			if (channel_can_xon(plci, a->ncci_ch[ncci])) {
				channel_request_xon(plci, a->ncci_ch[ncci]);
			}
			channel_xmit_xon(plci);

			if (appl->DataFlags[n] & 4) {
				nl_req_ncci(plci, N_DATA_ACK, (byte)ncci);
				return 1;
			}
		}
	}
	return false;
}

static byte reset_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			 PLCI *plci, APPL *appl, API_PARSE *parms)
{
	word Info;
	word ncci;

	dbug(1, dprintf("reset_b3_req"));

	Info = _WRONG_IDENTIFIER;
	ncci = (word)(Id >> 16);
	if (plci && ncci)
	{
		Info = _WRONG_STATE;
		switch (plci->B3_prot)
		{
		case B3_ISO8208:
		case B3_X25_DCE:
			if (a->ncci_state[ncci] == CONNECTED)
			{
				nl_req_ncci(plci, N_RESET, (byte)ncci);
				send_req(plci);
				Info = GOOD;
			}
			break;
		case B3_TRANSPARENT:
			if (a->ncci_state[ncci] == CONNECTED)
			{
				start_internal_command(Id, plci, reset_b3_command);
				Info = GOOD;
			}
			break;
		}
	}
	/* reset_b3 must result in a reset_b3_con & reset_b3_Ind */
	sendf(appl,
	      _RESET_B3_R | CONFIRM,
	      Id,
	      Number,
	      "w", Info);
	return false;
}

static byte reset_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			 PLCI *plci, APPL *appl, API_PARSE *parms)
{
	word ncci;

	dbug(1, dprintf("reset_b3_res"));

	ncci = (word)(Id >> 16);
	if (plci && ncci) {
		switch (plci->B3_prot)
		{
		case B3_ISO8208:
		case B3_X25_DCE:
			if (a->ncci_state[ncci] == INC_RES_PENDING)
			{
				a->ncci_state[ncci] = CONNECTED;
				nl_req_ncci(plci, N_RESET_ACK, (byte)ncci);
				return true;
			}
			break;
		}
	}
	return false;
}

static byte connect_b3_t90_a_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
				 PLCI *plci, APPL *appl, API_PARSE *parms)
{
	word ncci;
	API_PARSE *ncpi;
	byte req;

	dbug(1, dprintf("connect_b3_t90_a_res"));

	ncci = (word)(Id >> 16);
	if (plci && ncci) {
		if (a->ncci_state[ncci] == INC_ACT_PENDING) {
			a->ncci_state[ncci] = CONNECTED;
		}
		else if (a->ncci_state[ncci] == INC_CON_PENDING) {
			a->ncci_state[ncci] = CONNECTED;

			req = N_CONNECT_ACK;

			/* parms[0]==0 for CAPI original message definition! */
			if (parms[0].info) {
				ncpi = &parms[1];
				if (ncpi->length > 2) {
					if (ncpi->info[1] & 1) req = N_CONNECT_ACK | N_D_BIT;
					add_d(plci, (word)(ncpi->length - 3), &ncpi->info[4]);
				}
			}
			nl_req_ncci(plci, req, (byte)ncci);
			return 1;
		}
	}
	return false;
}


static byte select_b_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			 PLCI *plci, APPL *appl, API_PARSE *msg)
{
	word Info = 0;
	word i;
	byte tel;
	API_PARSE bp_parms[7];

	if (!plci || !msg)
	{
		Info = _WRONG_IDENTIFIER;
	}
	else
	{
		dbug(1, dprintf("select_b_req[%d],PLCI=0x%x,Tel=0x%x,NL=0x%x,appl=0x%x,sstate=0x%x",
				msg->length, plci->Id, plci->tel, plci->NL.Id, plci->appl, plci->SuppState));
		dbug(1, dprintf("PlciState=0x%x", plci->State));
		for (i = 0; i < 7; i++) bp_parms[i].length = 0;

		/* check if no channel is open, no B3 connected only */
		if ((plci->State == IDLE) || (plci->State == OUTG_DIS_PENDING) || (plci->State == INC_DIS_PENDING)
		    || (plci->SuppState != IDLE) || plci->channels || plci->nl_remove_id)
		{
			Info = _WRONG_STATE;
		}
		/* check message format and fill bp_parms pointer */
		else if (msg->length && api_parse(&msg->info[1], (word)msg->length, "wwwsss", bp_parms))
		{
			Info = _WRONG_MESSAGE_FORMAT;
		}
		else
		{
			if ((plci->State == INC_CON_PENDING) || (plci->State == INC_CON_ALERT)) /* send alert tone inband to the network, */
			{                                                                  /* e.g. Qsig or RBS or Cornet-N or xess PRI */
				if (Id & EXT_CONTROLLER)
				{
					sendf(appl, _SELECT_B_REQ | CONFIRM, Id, Number, "w", 0x2002); /* wrong controller */
					return 0;
				}
				plci->State = INC_CON_CONNECTED_ALERT;
				plci->appl = appl;
				clear_c_ind_mask_bit(plci, (word)(appl->Id - 1));
				dump_c_ind_mask(plci);
				for (i = 0; i < max_appl; i++) /* disconnect the other appls */
				{                         /* its quasi a connect        */
					if (test_c_ind_mask_bit(plci, i))
						sendf(&application[i], _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED);
				}
			}

			api_save_msg(msg, "s", &plci->saved_msg);
			tel = plci->tel;
			if (Id & EXT_CONTROLLER)
			{
				if (tel) /* external controller in use by this PLCI */
				{
					if (a->AdvSignalAppl && a->AdvSignalAppl != appl)
					{
						dbug(1, dprintf("Ext_Ctrl in use 1"));
						Info = _WRONG_STATE;
					}
				}
				else  /* external controller NOT in use by this PLCI ? */
				{
					if (a->AdvSignalPLCI)
					{
						dbug(1, dprintf("Ext_Ctrl in use 2"));
						Info = _WRONG_STATE;
					}
					else /* activate the codec */
					{
						dbug(1, dprintf("Ext_Ctrl start"));
						if (AdvCodecSupport(a, plci, appl, 0))
						{
							dbug(1, dprintf("Error in codec procedures"));
							Info = _WRONG_STATE;
						}
						else if (plci->spoofed_msg == SPOOFING_REQUIRED) /* wait until codec is active */
						{
							plci->spoofed_msg = AWAITING_SELECT_B;
							plci->internal_command = BLOCK_PLCI; /* lock other commands */
							plci->command = 0;
							dbug(1, dprintf("continue if codec loaded"));
							return false;
						}
					}
				}
			}
			else /* external controller bit is OFF */
			{
				if (tel) /* external controller in use, need to switch off */
				{
					if (a->AdvSignalAppl == appl)
					{
						CodecIdCheck(a, plci);
						plci->tel = 0;
						plci->adv_nl = 0;
						dbug(1, dprintf("Ext_Ctrl disable"));
					}
					else
					{
						dbug(1, dprintf("Ext_Ctrl not requested"));
					}
				}
			}
			if (!Info)
			{
				if (plci->call_dir & CALL_DIR_OUT)
					plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
				else if (plci->call_dir & CALL_DIR_IN)
					plci->call_dir = CALL_DIR_IN | CALL_DIR_ANSWER;
				start_internal_command(Id, plci, select_b_command);
				return false;
			}
		}
	}
	sendf(appl, _SELECT_B_REQ | CONFIRM, Id, Number, "w", Info);
	return false;
}

static byte manufacturer_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			     PLCI *plci, APPL *appl, API_PARSE *parms)
{
	word command;
	word i;
	word ncci;
	API_PARSE *m;
	API_PARSE m_parms[5];
	word codec;
	byte req;
	byte ch;
	byte dir;
	static byte chi[2] = {0x01, 0x00};
	static byte lli[2] = {0x01, 0x00};
	static byte codec_cai[2] = {0x01, 0x01};
	static byte null_msg = {0};
	static API_PARSE null_parms = { 0, &null_msg };
	PLCI *v_plci;
	word Info = 0;

	dbug(1, dprintf("manufacturer_req"));
	for (i = 0; i < 5; i++) m_parms[i].length = 0;

	if (GET_DWORD(parms[0].info) != _DI_MANU_ID) {
		Info = _WRONG_MESSAGE_FORMAT;
	}
	command = GET_WORD(parms[1].info);
	m = &parms[2];
	if (!Info)
	{
		switch (command) {
		case _DI_ASSIGN_PLCI:
			if (api_parse(&m->info[1], (word)m->length, "wbbs", m_parms)) {
				Info = _WRONG_MESSAGE_FORMAT;
				break;
			}
			codec = GET_WORD(m_parms[0].info);
			ch = m_parms[1].info[0];
			dir = m_parms[2].info[0];
			if ((i = get_plci(a))) {
				plci = &a->plci[i - 1];
				plci->appl = appl;
				plci->command = _MANUFACTURER_R;
				plci->m_command = command;
				plci->number = Number;
				plci->State = LOCAL_CONNECT;
				Id = (((word)plci->Id << 8) | plci->adapter->Id | 0x80);
				dbug(1, dprintf("ManCMD,plci=0x%x", Id));

				if ((ch == 1 || ch == 2) && (dir <= 2)) {
					chi[1] = (byte)(0x80 | ch);
					lli[1] = 0;
					plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
					switch (codec)
					{
					case 0:
						Info = add_b1(plci, &m_parms[3], 0, 0);
						break;
					case 1:
						add_p(plci, CAI, codec_cai);
						break;
						/* manual 'swich on' to the codec support without signalling */
						/* first 'assign plci' with this function, then use */
					case 2:
						if (AdvCodecSupport(a, plci, appl, 0)) {
							Info = _RESOURCE_ERROR;
						}
						else {
							Info = add_b1(plci, &null_parms, 0, B1_FACILITY_LOCAL);
							lli[1] = 0x10; /* local call codec stream */
						}
						break;
					}

					plci->State = LOCAL_CONNECT;
					plci->manufacturer = true;
					plci->command = _MANUFACTURER_R;
					plci->m_command = command;
					plci->number = Number;

					if (!Info)
					{
						add_p(plci, LLI, lli);
						add_p(plci, CHI, chi);
						add_p(plci, UID, "\x06\x43\x61\x70\x69\x32\x30");
						sig_req(plci, ASSIGN, DSIG_ID);

						if (!codec)
						{
							Info = add_b23(plci, &m_parms[3]);
							if (!Info)
							{
								nl_req_ncci(plci, ASSIGN, 0);
								send_req(plci);
							}
						}
						if (!Info)
						{
							dbug(1, dprintf("dir=0x%x,spoof=0x%x", dir, plci->spoofed_msg));
							if (plci->spoofed_msg == SPOOFING_REQUIRED)
							{
								api_save_msg(m_parms, "wbbs", &plci->saved_msg);
								plci->spoofed_msg = AWAITING_MANUF_CON;
								plci->internal_command = BLOCK_PLCI; /* reject other req meanwhile */
								plci->command = 0;
								send_req(plci);
								return false;
							}
							if (dir == 1) {
								sig_req(plci, CALL_REQ, 0);
							}
							else if (!dir) {
								sig_req(plci, LISTEN_REQ, 0);
							}
							send_req(plci);
						}
						else
						{
							sendf(appl,
							      _MANUFACTURER_R | CONFIRM,
							      Id,
							      Number,
							      "dww", _DI_MANU_ID, command, Info);
							return 2;
						}
					}
				}
			}
			else  Info = _OUT_OF_PLCI;
			break;

		case _DI_IDI_CTRL:
			if (!plci)
			{
				Info = _WRONG_IDENTIFIER;
				break;
			}
			if (api_parse(&m->info[1], (word)m->length, "bs", m_parms)) {
				Info = _WRONG_MESSAGE_FORMAT;
				break;
			}
			req = m_parms[0].info[0];
			plci->command = _MANUFACTURER_R;
			plci->m_command = command;
			plci->number = Number;
			if (req == CALL_REQ)
			{
				plci->b_channel = getChannel(&m_parms[1]);
				mixer_set_bchannel_id_esc(plci, plci->b_channel);
				if (plci->spoofed_msg == SPOOFING_REQUIRED)
				{
					plci->spoofed_msg = CALL_REQ | AWAITING_MANUF_CON;
					plci->internal_command = BLOCK_PLCI; /* reject other req meanwhile */
					plci->command = 0;
					break;
				}
			}
			else if (req == LAW_REQ)
			{
				plci->cr_enquiry = true;
			}
			add_ss(plci, FTY, &m_parms[1]);
			sig_req(plci, req, 0);
			send_req(plci);
			if (req == HANGUP)
			{
				if (plci->NL.Id && !plci->nl_remove_id)
				{
					if (plci->channels)
					{
						for (ncci = 1; ncci < MAX_NCCI + 1; ncci++)
						{
							if ((a->ncci_plci[ncci] == plci->Id) && (a->ncci_state[ncci] == CONNECTED))
							{
								a->ncci_state[ncci] = OUTG_DIS_PENDING;
								cleanup_ncci_data(plci, ncci);
								nl_req_ncci(plci, N_DISC, (byte)ncci);
							}
						}
					}
					mixer_remove(plci);
					nl_req_ncci(plci, REMOVE, 0);
					send_req(plci);
				}
			}
			break;

		case _DI_SIG_CTRL:
			/* signalling control for loop activation B-channel */
			if (!plci)
			{
				Info = _WRONG_IDENTIFIER;
				break;
			}
			if (m->length) {
				plci->command = _MANUFACTURER_R;
				plci->number = Number;
				add_ss(plci, FTY, m);
				sig_req(plci, SIG_CTRL, 0);
				send_req(plci);
			}
			else Info = _WRONG_MESSAGE_FORMAT;
			break;

		case _DI_RXT_CTRL:
			/* activation control for receiver/transmitter B-channel */
			if (!plci)
			{
				Info = _WRONG_IDENTIFIER;
				break;
			}
			if (m->length) {
				plci->command = _MANUFACTURER_R;
				plci->number = Number;
				add_ss(plci, FTY, m);
				sig_req(plci, DSP_CTRL, 0);
				send_req(plci);
			}
			else Info = _WRONG_MESSAGE_FORMAT;
			break;

		case _DI_ADV_CODEC:
		case _DI_DSP_CTRL:
			/* TEL_CTRL commands to support non standard adjustments: */
			/* Ring on/off, Handset micro volume, external micro vol. */
			/* handset+external speaker volume, receiver+transm. gain,*/
			/* handsfree on (hookinfo off), set mixer command         */

			if (command == _DI_ADV_CODEC)
			{
				if (!a->AdvCodecPLCI) {
					Info = _WRONG_STATE;
					break;
				}
				v_plci = a->AdvCodecPLCI;
			}
			else
			{
				if (plci
				    && (m->length >= 3)
				    && (m->info[1] == 0x1c)
				    && (m->info[2] >= 1))
				{
					if (m->info[3] == DSP_CTRL_OLD_SET_MIXER_COEFFICIENTS)
					{
						if ((plci->tel != ADV_VOICE) || (plci != a->AdvSignalPLCI))
						{
							Info = _WRONG_STATE;
							break;
						}
						a->adv_voice_coef_length = m->info[2] - 1;
						if (a->adv_voice_coef_length > m->length - 3)
							a->adv_voice_coef_length = (byte)(m->length - 3);
						if (a->adv_voice_coef_length > ADV_VOICE_COEF_BUFFER_SIZE)
							a->adv_voice_coef_length = ADV_VOICE_COEF_BUFFER_SIZE;
						for (i = 0; i < a->adv_voice_coef_length; i++)
							a->adv_voice_coef_buffer[i] = m->info[4 + i];
						if (plci->B1_facilities & B1_FACILITY_VOICE)
							adv_voice_write_coefs(plci, ADV_VOICE_WRITE_UPDATE);
						break;
					}
					else if (m->info[3] == DSP_CTRL_SET_DTMF_PARAMETERS)
					{
						if (!(a->manufacturer_features & MANUFACTURER_FEATURE_DTMF_PARAMETERS))
						{
							Info = _FACILITY_NOT_SUPPORTED;
							break;
						}

						plci->dtmf_parameter_length = m->info[2] - 1;
						if (plci->dtmf_parameter_length > m->length - 3)
							plci->dtmf_parameter_length = (byte)(m->length - 3);
						if (plci->dtmf_parameter_length > DTMF_PARAMETER_BUFFER_SIZE)
							plci->dtmf_parameter_length = DTMF_PARAMETER_BUFFER_SIZE;
						for (i = 0; i < plci->dtmf_parameter_length; i++)
							plci->dtmf_parameter_buffer[i] = m->info[4 + i];
						if (plci->B1_facilities & B1_FACILITY_DTMFR)
							dtmf_parameter_write(plci);
						break;

					}
				}
				v_plci = plci;
			}

			if (!v_plci)
			{
				Info = _WRONG_IDENTIFIER;
				break;
			}
			if (m->length) {
				add_ss(v_plci, FTY, m);
				sig_req(v_plci, TEL_CTRL, 0);
				send_req(v_plci);
			}
			else Info = _WRONG_MESSAGE_FORMAT;

			break;

		case _DI_OPTIONS_REQUEST:
			if (api_parse(&m->info[1], (word)m->length, "d", m_parms)) {
				Info = _WRONG_MESSAGE_FORMAT;
				break;
			}
			if (GET_DWORD(m_parms[0].info) & ~a->man_profile.private_options)
			{
				Info = _FACILITY_NOT_SUPPORTED;
				break;
			}
			a->requested_options_table[appl->Id - 1] = GET_DWORD(m_parms[0].info);
			break;



		default:
			Info = _WRONG_MESSAGE_FORMAT;
			break;
		}
	}

	sendf(appl,
	      _MANUFACTURER_R | CONFIRM,
	      Id,
	      Number,
	      "dww", _DI_MANU_ID, command, Info);
	return false;
}


static byte manufacturer_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
			     PLCI *plci, APPL *appl, API_PARSE *msg)
{
	word indication;

	API_PARSE m_parms[3];
	API_PARSE *ncpi;
	API_PARSE fax_parms[9];
	word i;
	byte len;


	dbug(1, dprintf("manufacturer_res"));

	if ((msg[0].length == 0)
	    || (msg[1].length == 0)
	    || (GET_DWORD(msg[0].info) != _DI_MANU_ID))
	{
		return false;
	}
	indication = GET_WORD(msg[1].info);
	switch (indication)
	{

	case _DI_NEGOTIATE_B3:
		if (!plci)
			break;
		if (((plci->B3_prot != 4) && (plci->B3_prot != 5))
		    || !(plci->ncpi_state & NCPI_NEGOTIATE_B3_SENT))
		{
			dbug(1, dprintf("wrong state for NEGOTIATE_B3 parameters"));
			break;
		}
		if (api_parse(&msg[2].info[1], msg[2].length, "ws", m_parms))
		{
			dbug(1, dprintf("wrong format in NEGOTIATE_B3 parameters"));
			break;
		}
		ncpi = &m_parms[1];
		len = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH;
		if (plci->fax_connect_info_length < len)
		{
			((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = 0;
			((T30_INFO *)(plci->fax_connect_info_buffer))->head_line_len = 0;
		}
		if (api_parse(&ncpi->info[1], ncpi->length, "wwwwssss", fax_parms))
		{
			dbug(1, dprintf("non-standard facilities info missing or wrong format"));
		}
		else
		{
			if (plci->fax_connect_info_length <= len)
				plci->fax_connect_info_buffer[len] = 0;
			len += 1 + plci->fax_connect_info_buffer[len];
			if (plci->fax_connect_info_length <= len)
				plci->fax_connect_info_buffer[len] = 0;
			len += 1 + plci->fax_connect_info_buffer[len];
			if ((fax_parms[7].length >= 3) && (fax_parms[7].info[1] >= 2))
				plci->nsf_control_bits = GET_WORD(&fax_parms[7].info[2]);
			plci->fax_connect_info_buffer[len++] = (byte)(fax_parms[7].length);
			for (i = 0; i < fax_parms[7].length; i++)
				plci->fax_connect_info_buffer[len++] = fax_parms[7].info[1 + i];
		}
		plci->fax_connect_info_length = len;
		plci->fax_edata_ack_length = plci->fax_connect_info_length;
		start_internal_command(Id, plci, fax_edata_ack_command);
		break;

	}
	return false;
}

/*------------------------------------------------------------------*/
/* IDI callback function                                            */
/*------------------------------------------------------------------*/

void callback(ENTITY *e)
{
	DIVA_CAPI_ADAPTER *a;
	APPL *appl;
	PLCI *plci;
	CAPI_MSG *m;
	word i, j;
	byte rc;
	byte ch;
	byte req;
	byte global_req;
	int no_cancel_rc;

	dbug(1, dprintf("%x:CB(%x:Req=%x,Rc=%x,Ind=%x)",
			(e->user[0] + 1) & 0x7fff, e->Id, e->Req, e->Rc, e->Ind));

	a = &(adapter[(byte)e->user[0]]);
	plci = &(a->plci[e->user[1]]);
	no_cancel_rc = DIVA_CAPI_SUPPORTS_NO_CANCEL(a);

	/*
	  If new protocol code and new XDI is used then CAPI should work
	  fully in accordance with IDI cpec an look on callback field instead
	  of Rc field for return codes.
	*/
	if (((e->complete == 0xff) && no_cancel_rc) ||
	    (e->Rc && !no_cancel_rc)) {
		rc = e->Rc;
		ch = e->RcCh;
		req = e->Req;
		e->Rc = 0;

		if (e->user[0] & 0x8000)
		{
			/*
			  If REMOVE request was sent then we have to wait until
			  return code with Id set to zero arrives.
			  All other return codes should be ignored.
			*/
			if (req == REMOVE)
			{
				if (e->Id)
				{
					dbug(1, dprintf("cancel RC in REMOVE state"));
					return;
				}
				channel_flow_control_remove(plci);
				for (i = 0; i < 256; i++)
				{
					if (a->FlowControlIdTable[i] == plci->nl_remove_id)
						a->FlowControlIdTable[i] = 0;
				}
				plci->nl_remove_id = 0;
				if (plci->rx_dma_descriptor > 0) {
					diva_free_dma_descriptor(plci, plci->rx_dma_descriptor - 1);
					plci->rx_dma_descriptor = 0;
				}
			}
			if (rc == OK_FC)
			{
				a->FlowControlIdTable[ch] = e->Id;
				a->FlowControlSkipTable[ch] = 0;

				a->ch_flow_control[ch] |= N_OK_FC_PENDING;
				a->ch_flow_plci[ch] = plci->Id;
				plci->nl_req = 0;
			}
			else
			{
				/*
				  Cancel return codes self, if feature was requested
				*/
				if (no_cancel_rc && (a->FlowControlIdTable[ch] == e->Id) && e->Id) {
					a->FlowControlIdTable[ch] = 0;
					if ((rc == OK) && a->FlowControlSkipTable[ch]) {
						dbug(3, dprintf("XDI CAPI: RC cancelled Id:0x02, Ch:%02x", e->Id, ch));
						return;
					}
				}

				if (a->ch_flow_control[ch] & N_OK_FC_PENDING)
				{
					a->ch_flow_control[ch] &= ~N_OK_FC_PENDING;
					if (ch == e->ReqCh)
						plci->nl_req = 0;
				}
				else
					plci->nl_req = 0;
			}
			if (plci->nl_req)
				control_rc(plci, 0, rc, ch, 0, true);
			else
			{
				if (req == N_XON)
				{
					channel_x_on(plci, ch);
					if (plci->internal_command)
						control_rc(plci, req, rc, ch, 0, true);
				}
				else
				{
					if (plci->nl_global_req)
					{
						global_req = plci->nl_global_req;
						plci->nl_global_req = 0;
						if (rc != ASSIGN_OK) {
							e->Id = 0;
							if (plci->rx_dma_descriptor > 0) {
								diva_free_dma_descriptor(plci, plci->rx_dma_descriptor - 1);
								plci->rx_dma_descriptor = 0;
							}
						}
						channel_xmit_xon(plci);
						control_rc(plci, 0, rc, ch, global_req, true);
					}
					else if (plci->data_sent)
					{
						channel_xmit_xon(plci);
						plci->data_sent = false;
						plci->NL.XNum = 1;
						data_rc(plci, ch);
						if (plci->internal_command)
							control_rc(plci, req, rc, ch, 0, true);
					}
					else
					{
						channel_xmit_xon(plci);
						control_rc(plci, req, rc, ch, 0, true);
					}
				}
			}
		}
		else
		{
			/*
			  If REMOVE request was sent then we have to wait until
			  return code with Id set to zero arrives.
			  All other return codes should be ignored.
			*/
			if (req == REMOVE)
			{
				if (e->Id)
				{
					dbug(1, dprintf("cancel RC in REMOVE state"));
					return;
				}
				plci->sig_remove_id = 0;
			}
			plci->sig_req = 0;
			if (plci->sig_global_req)
			{
				global_req = plci->sig_global_req;
				plci->sig_global_req = 0;
				if (rc != ASSIGN_OK)
					e->Id = 0;
				channel_xmit_xon(plci);
				control_rc(plci, 0, rc, ch, global_req, false);
			}
			else
			{
				channel_xmit_xon(plci);
				control_rc(plci, req, rc, ch, 0, false);
			}
		}
		/*
		  Again: in accordance with IDI spec Rc and Ind can't be delivered in the
		  same callback. Also if new XDI and protocol code used then jump
		  direct to finish.
		*/
		if (no_cancel_rc) {
			channel_xmit_xon(plci);
			goto capi_callback_suffix;
		}
	}

	channel_xmit_xon(plci);

	if (e->Ind) {
		if (e->user[0] & 0x8000) {
			byte Ind = e->Ind & 0x0f;
			byte Ch = e->IndCh;
			if (((Ind == N_DISC) || (Ind == N_DISC_ACK)) &&
			    (a->ch_flow_plci[Ch] == plci->Id)) {
				if (a->ch_flow_control[Ch] & N_RX_FLOW_CONTROL_MASK) {
					dbug(3, dprintf("XDI CAPI: I: pending N-XON Ch:%02x", Ch));
				}
				a->ch_flow_control[Ch] &= ~N_RX_FLOW_CONTROL_MASK;
			}
			nl_ind(plci);
			if ((e->RNR != 1) &&
			    (a->ch_flow_plci[Ch] == plci->Id) &&
			    (a->ch_flow_control[Ch] & N_RX_FLOW_CONTROL_MASK)) {
				a->ch_flow_control[Ch] &= ~N_RX_FLOW_CONTROL_MASK;
				dbug(3, dprintf("XDI CAPI: I: remove faked N-XON Ch:%02x", Ch));
			}
		} else {
			sig_ind(plci);
		}
		e->Ind = 0;
	}

capi_callback_suffix:

	while (!plci->req_in
	       && !plci->internal_command
	       && (plci->msg_in_write_pos != plci->msg_in_read_pos))
	{
		j = (plci->msg_in_read_pos == plci->msg_in_wrap_pos) ? 0 : plci->msg_in_read_pos;

		i = (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[j]))->header.length + 3) & 0xfffc;

		m = (CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[j]);
		appl = *((APPL **)(&((byte *)(plci->msg_in_queue))[j + i]));
		dbug(1, dprintf("dequeue msg(0x%04x) - write=%d read=%d wrap=%d",
				m->header.command, plci->msg_in_write_pos, plci->msg_in_read_pos, plci->msg_in_wrap_pos));
		if (plci->msg_in_read_pos == plci->msg_in_wrap_pos)
		{
			plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE;
			plci->msg_in_read_pos = i + MSG_IN_OVERHEAD;
		}
		else
		{
			plci->msg_in_read_pos = j + i + MSG_IN_OVERHEAD;
		}
		if (plci->msg_in_read_pos == plci->msg_in_write_pos)
		{
			plci->msg_in_write_pos = MSG_IN_QUEUE_SIZE;
			plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE;
		}
		else if (plci->msg_in_read_pos == plci->msg_in_wrap_pos)
		{
			plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE;
			plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE;
		}
		i = api_put(appl, m);
		if (i != 0)
		{
			if (m->header.command == _DATA_B3_R)

				TransmitBufferFree(appl, (byte *)(long)(m->info.data_b3_req.Data));

			dbug(1, dprintf("Error 0x%04x from msg(0x%04x)", i, m->header.command));
			break;
		}

		if (plci->li_notify_update)
		{
			plci->li_notify_update = false;
			mixer_notify_update(plci, false);
		}

	}
	send_data(plci);
	send_req(plci);
}


static void control_rc(PLCI *plci, byte req, byte rc, byte ch, byte global_req,
		       byte nl_rc)
{
	dword Id;
	dword rId;
	word Number;
	word Info = 0;
	word i;
	word ncci;
	DIVA_CAPI_ADAPTER *a;
	APPL *appl;
	PLCI *rplci;
	byte SSparms[] = "\x05\x00\x00\x02\x00\x00";
	byte SSstruct[] = "\x09\x00\x00\x06\x00\x00\x00\x00\x00\x00";

	if (!plci) {
		dbug(0, dprintf("A: control_rc, no plci %02x:%02x:%02x:%02x:%02x", req, rc, ch, global_req, nl_rc));
		return;
	}
	dbug(1, dprintf("req0_in/out=%d/%d", plci->req_in, plci->req_out));
	if (plci->req_in != plci->req_out)
	{
		if (nl_rc || (global_req != ASSIGN) || (rc == ASSIGN_OK))
		{
			dbug(1, dprintf("req_1return"));
			return;
		}
		/* cancel outstanding request on the PLCI after SIG ASSIGN failure */
	}
	plci->req_in = plci->req_in_start = plci->req_out = 0;
	dbug(1, dprintf("control_rc"));

	appl = plci->appl;
	a = plci->adapter;
	ncci = a->ch_ncci[ch];
	if (appl)
	{
		Id = (((dword)(ncci ? ncci : ch)) << 16) | ((word)plci->Id << 8) | a->Id;
		if (plci->tel && plci->SuppState != CALL_HELD) Id |= EXT_CONTROLLER;
		Number = plci->number;
		dbug(1, dprintf("Contr_RC-Id=%08lx,plci=%x,tel=%x, entity=0x%x, command=0x%x, int_command=0x%x", Id, plci->Id, plci->tel, plci->Sig.Id, plci->command, plci->internal_command));
		dbug(1, dprintf("channels=0x%x", plci->channels));
		if (plci_remove_check(plci))
			return;
		if (req == REMOVE && rc == ASSIGN_OK)
		{
			sig_req(plci, HANGUP, 0);
			sig_req(plci, REMOVE, 0);
			send_req(plci);
		}
		if (plci->command)
		{
			switch (plci->command)
			{
			case C_HOLD_REQ:
				dbug(1, dprintf("HoldRC=0x%x", rc));
				SSparms[1] = (byte)S_HOLD;
				if (rc != OK)
				{
					plci->SuppState = IDLE;
					Info = 0x2001;
				}
				sendf(appl, _FACILITY_R | CONFIRM, Id, Number, "wws", Info, 3, SSparms);
				break;

			case C_RETRIEVE_REQ:
				dbug(1, dprintf("RetrieveRC=0x%x", rc));
				SSparms[1] = (byte)S_RETRIEVE;
				if (rc != OK)
				{
					plci->SuppState = CALL_HELD;
					Info = 0x2001;
				}
				sendf(appl, _FACILITY_R | CONFIRM, Id, Number, "wws", Info, 3, SSparms);
				break;

			case _INFO_R:
				dbug(1, dprintf("InfoRC=0x%x", rc));
				if (rc != OK) Info = _WRONG_STATE;
				sendf(appl, _INFO_R | CONFIRM, Id, Number, "w", Info);
				break;

			case _CONNECT_R:
				dbug(1, dprintf("Connect_R=0x%x/0x%x/0x%x/0x%x", req, rc, global_req, nl_rc));
				if (plci->State == INC_DIS_PENDING)
					break;
				if (plci->Sig.Id != 0xff)
				{
					if (((global_req == ASSIGN) && (rc != ASSIGN_OK))
					    || (!nl_rc && (req == CALL_REQ) && (rc != OK)))
					{
						dbug(1, dprintf("No more IDs/Call_Req failed"));
						sendf(appl, _CONNECT_R | CONFIRM, Id & 0xffL, Number, "w", _OUT_OF_PLCI);
						plci_remove(plci);
						plci->State = IDLE;
						break;
					}
					if (plci->State != LOCAL_CONNECT) plci->State = OUTG_CON_PENDING;
					sendf(appl, _CONNECT_R | CONFIRM, Id, Number, "w", 0);
				}
				else /* D-ch activation */
				{
					if (rc != ASSIGN_OK)
					{
						dbug(1, dprintf("No more IDs/X.25 Call_Req failed"));
						sendf(appl, _CONNECT_R | CONFIRM, Id & 0xffL, Number, "w", _OUT_OF_PLCI);
						plci_remove(plci);
						plci->State = IDLE;
						break;
					}
					sendf(appl, _CONNECT_R | CONFIRM, Id, Number, "w", 0);
					sendf(plci->appl, _CONNECT_ACTIVE_I, Id, 0, "sss", "", "", "");
					plci->State = INC_ACT_PENDING;
				}
				break;

			case _CONNECT_I | RESPONSE:
				if (plci->State != INC_DIS_PENDING)
					plci->State = INC_CON_ACCEPT;
				break;

			case _DISCONNECT_R:
				if (plci->State == INC_DIS_PENDING)
					break;
				if (plci->Sig.Id != 0xff)
				{
					plci->State = OUTG_DIS_PENDING;
					sendf(appl, _DISCONNECT_R | CONFIRM, Id, Number, "w", 0);
				}
				break;

			case SUSPEND_REQ:
				break;

			case RESUME_REQ:
				break;

			case _CONNECT_B3_R:
				if (rc != OK)
				{
					sendf(appl, _CONNECT_B3_R | CONFIRM, Id, Number, "w", _WRONG_IDENTIFIER);
					break;
				}
				ncci = get_ncci(plci, ch, 0);
				Id = (Id & 0xffff) | (((dword) ncci) << 16);
				plci->channels++;
				if (req == N_RESET)
				{
					a->ncci_state[ncci] = INC_ACT_PENDING;
					sendf(appl, _CONNECT_B3_R | CONFIRM, Id, Number, "w", 0);
					sendf(appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
				}
				else
				{
					a->ncci_state[ncci] = OUTG_CON_PENDING;
					sendf(appl, _CONNECT_B3_R | CONFIRM, Id, Number, "w", 0);
				}
				break;

			case _CONNECT_B3_I | RESPONSE:
				break;

			case _RESET_B3_R:
/*        sendf(appl, _RESET_B3_R | CONFIRM, Id, Number, "w", 0);*/
				break;

			case _DISCONNECT_B3_R:
				sendf(appl, _DISCONNECT_B3_R | CONFIRM, Id, Number, "w", 0);
				break;

			case _MANUFACTURER_R:
				break;

			case PERM_LIST_REQ:
				if (rc != OK)
				{
					Info = _WRONG_IDENTIFIER;
					sendf(plci->appl, _CONNECT_R | CONFIRM, Id, Number, "w", Info);
					plci_remove(plci);
				}
				else
					sendf(plci->appl, _CONNECT_R | CONFIRM, Id, Number, "w", Info);
				break;

			default:
				break;
			}
			plci->command = 0;
		}
		else if (plci->internal_command)
		{
			switch (plci->internal_command)
			{
			case BLOCK_PLCI:
				return;

			case GET_MWI_STATE:
				if (rc == OK) /* command supported, wait for indication */
				{
					return;
				}
				plci_remove(plci);
				break;

				/* Get Supported Services */
			case GETSERV_REQ_PEND:
				if (rc == OK) /* command supported, wait for indication */
				{
					break;
				}
				PUT_DWORD(&SSstruct[6], MASK_TERMINAL_PORTABILITY);
				sendf(appl, _FACILITY_R | CONFIRM, Id, Number, "wws", 0, 3, SSstruct);
				plci_remove(plci);
				break;

			case INTERR_DIVERSION_REQ_PEND:      /* Interrogate Parameters        */
			case INTERR_NUMBERS_REQ_PEND:
			case CF_START_PEND:                  /* Call Forwarding Start pending */
			case CF_STOP_PEND:                   /* Call Forwarding Stop pending  */
			case CCBS_REQUEST_REQ_PEND:
			case CCBS_DEACTIVATE_REQ_PEND:
			case CCBS_INTERROGATE_REQ_PEND:
				switch (plci->internal_command)
				{
				case INTERR_DIVERSION_REQ_PEND:
					SSparms[1] = S_INTERROGATE_DIVERSION;
					break;
				case INTERR_NUMBERS_REQ_PEND:
					SSparms[1] = S_INTERROGATE_NUMBERS;
					break;
				case CF_START_PEND:
					SSparms[1] = S_CALL_FORWARDING_START;
					break;
				case CF_STOP_PEND:
					SSparms[1] = S_CALL_FORWARDING_STOP;
					break;
				case CCBS_REQUEST_REQ_PEND:
					SSparms[1] = S_CCBS_REQUEST;
					break;
				case CCBS_DEACTIVATE_REQ_PEND:
					SSparms[1] = S_CCBS_DEACTIVATE;
					break;
				case CCBS_INTERROGATE_REQ_PEND:
					SSparms[1] = S_CCBS_INTERROGATE;
					break;
				}
				if (global_req == ASSIGN)
				{
					dbug(1, dprintf("AssignDiversion_RC=0x%x/0x%x", req, rc));
					return;
				}
				if (!plci->appl) break;
				if (rc == ISDN_GUARD_REJ)
				{
					Info = _CAPI_GUARD_ERROR;
				}
				else if (rc != OK)
				{
					Info = _SUPPLEMENTARY_SERVICE_NOT_SUPPORTED;
				}
				sendf(plci->appl, _FACILITY_R | CONFIRM, Id & 0x7,
				      plci->number, "wws", Info, (word)3, SSparms);
				if (Info) plci_remove(plci);
				break;

				/* 3pty conference pending */
			case PTY_REQ_PEND:
				if (!plci->relatedPTYPLCI) break;
				rplci = plci->relatedPTYPLCI;
				SSparms[1] = plci->ptyState;
				rId = ((word)rplci->Id << 8) | rplci->adapter->Id;
				if (rplci->tel) rId |= EXT_CONTROLLER;
				if (rc != OK)
				{
					Info = 0x300E; /* not supported */
					plci->relatedPTYPLCI = NULL;
					plci->ptyState = 0;
				}
				sendf(rplci->appl,
				      _FACILITY_R | CONFIRM,
				      rId,
				      plci->number,
				      "wws", Info, (word)3, SSparms);
				break;

				/* Explicit Call Transfer pending */
			case ECT_REQ_PEND:
				dbug(1, dprintf("ECT_RC=0x%x/0x%x", req, rc));
				if (!plci->relatedPTYPLCI) break;
				rplci = plci->relatedPTYPLCI;
				SSparms[1] = S_ECT;
				rId = ((word)rplci->Id << 8) | rplci->adapter->Id;
				if (rplci->tel) rId |= EXT_CONTROLLER;
				if (rc != OK)
				{
					Info = 0x300E; /* not supported */
					plci->relatedPTYPLCI = NULL;
					plci->ptyState = 0;
				}
				sendf(rplci->appl,
				      _FACILITY_R | CONFIRM,
				      rId,
				      plci->number,
				      "wws", Info, (word)3, SSparms);
				break;

			case _MANUFACTURER_R:
				dbug(1, dprintf("_Manufacturer_R=0x%x/0x%x", req, rc));
				if ((global_req == ASSIGN) && (rc != ASSIGN_OK))
				{
					dbug(1, dprintf("No more IDs"));
					sendf(appl, _MANUFACTURER_R | CONFIRM, Id, Number, "dww", _DI_MANU_ID, _MANUFACTURER_R, _OUT_OF_PLCI);
					plci_remove(plci);  /* after codec init, internal codec commands pending */
				}
				break;

			case _CONNECT_R:
				dbug(1, dprintf("_Connect_R=0x%x/0x%x", req, rc));
				if ((global_req == ASSIGN) && (rc != ASSIGN_OK))
				{
					dbug(1, dprintf("No more IDs"));
					sendf(appl, _CONNECT_R | CONFIRM, Id & 0xffL, Number, "w", _OUT_OF_PLCI);
					plci_remove(plci);  /* after codec init, internal codec commands pending */
				}
				break;

			case PERM_COD_HOOK:                     /* finished with Hook_Ind */
				return;

			case PERM_COD_CALL:
				dbug(1, dprintf("***Codec Connect_Pending A, Rc = 0x%x", rc));
				plci->internal_command = PERM_COD_CONN_PEND;
				return;

			case PERM_COD_ASSIGN:
				dbug(1, dprintf("***Codec Assign A, Rc = 0x%x", rc));
				if (rc != ASSIGN_OK) break;
				sig_req(plci, CALL_REQ, 0);
				send_req(plci);
				plci->internal_command = PERM_COD_CALL;
				return;

				/* Null Call Reference Request pending */
			case C_NCR_FAC_REQ:
				dbug(1, dprintf("NCR_FAC=0x%x/0x%x", req, rc));
				if (global_req == ASSIGN)
				{
					if (rc == ASSIGN_OK)
					{
						return;
					}
					else
					{
						sendf(appl, _INFO_R | CONFIRM, Id & 0xf, Number, "w", _WRONG_STATE);
						appl->NullCREnable = false;
						plci_remove(plci);
					}
				}
				else if (req == NCR_FACILITY)
				{
					if (rc == OK)
					{
						sendf(appl, _INFO_R | CONFIRM, Id & 0xf, Number, "w", 0);
					}
					else
					{
						sendf(appl, _INFO_R | CONFIRM, Id & 0xf, Number, "w", _WRONG_STATE);
						appl->NullCREnable = false;
					}
					plci_remove(plci);
				}
				break;

			case HOOK_ON_REQ:
				if (plci->channels)
				{
					if (a->ncci_state[ncci] == CONNECTED)
					{
						a->ncci_state[ncci] = OUTG_DIS_PENDING;
						cleanup_ncci_data(plci, ncci);
						nl_req_ncci(plci, N_DISC, (byte)ncci);
					}
					break;
				}
				break;

			case HOOK_OFF_REQ:
				if (plci->State == INC_DIS_PENDING)
					break;
				sig_req(plci, CALL_REQ, 0);
				send_req(plci);
				plci->State = OUTG_CON_PENDING;
				break;


			case MWI_ACTIVATE_REQ_PEND:
			case MWI_DEACTIVATE_REQ_PEND:
				if (global_req == ASSIGN && rc == ASSIGN_OK)
				{
					dbug(1, dprintf("MWI_REQ assigned"));
					return;
				}
				else if (rc != OK)
				{
					if (rc == WRONG_IE)
					{
						Info = 0x2007; /* Illegal message parameter coding */
						dbug(1, dprintf("MWI_REQ invalid parameter"));
					}
					else
					{
						Info = 0x300B; /* not supported */
						dbug(1, dprintf("MWI_REQ not supported"));
					}
					/* 0x3010: Request not allowed in this state */
					PUT_WORD(&SSparms[4], 0x300E); /* SS not supported */

				}
				if (plci->internal_command == MWI_ACTIVATE_REQ_PEND)
				{
					PUT_WORD(&SSparms[1], S_MWI_ACTIVATE);
				}
				else PUT_WORD(&SSparms[1], S_MWI_DEACTIVATE);

				if (plci->cr_enquiry)
				{
					sendf(plci->appl,
					      _FACILITY_R | CONFIRM,
					      Id & 0xf,
					      plci->number,
					      "wws", Info, (word)3, SSparms);
					if (rc != OK) plci_remove(plci);
				}
				else
				{
					sendf(plci->appl,
					      _FACILITY_R | CONFIRM,
					      Id,
					      plci->number,
					      "wws", Info, (word)3, SSparms);
				}
				break;

			case CONF_BEGIN_REQ_PEND:
			case CONF_ADD_REQ_PEND:
			case CONF_SPLIT_REQ_PEND:
			case CONF_DROP_REQ_PEND:
			case CONF_ISOLATE_REQ_PEND:
			case CONF_REATTACH_REQ_PEND:
				dbug(1, dprintf("CONF_RC=0x%x/0x%x", req, rc));
				if ((plci->internal_command == CONF_ADD_REQ_PEND) && (!plci->relatedPTYPLCI)) break;
				rplci = plci;
				rId = Id;
				switch (plci->internal_command)
				{
				case CONF_BEGIN_REQ_PEND:
					SSparms[1] = S_CONF_BEGIN;
					break;
				case CONF_ADD_REQ_PEND:
					SSparms[1] = S_CONF_ADD;
					rplci = plci->relatedPTYPLCI;
					rId = ((word)rplci->Id << 8) | rplci->adapter->Id;
					break;
				case CONF_SPLIT_REQ_PEND:
					SSparms[1] = S_CONF_SPLIT;
					break;
				case CONF_DROP_REQ_PEND:
					SSparms[1] = S_CONF_DROP;
					break;
				case CONF_ISOLATE_REQ_PEND:
					SSparms[1] = S_CONF_ISOLATE;
					break;
				case CONF_REATTACH_REQ_PEND:
					SSparms[1] = S_CONF_REATTACH;
					break;
				}

				if (rc != OK)
				{
					Info = 0x300E; /* not supported */
					plci->relatedPTYPLCI = NULL;
					plci->ptyState = 0;
				}
				sendf(rplci->appl,
				      _FACILITY_R | CONFIRM,
				      rId,
				      plci->number,
				      "wws", Info, (word)3, SSparms);
				break;

			case VSWITCH_REQ_PEND:
				if (rc != OK)
				{
					if (plci->relatedPTYPLCI)
					{
						plci->relatedPTYPLCI->vswitchstate = 0;
						plci->relatedPTYPLCI->vsprot = 0;
						plci->relatedPTYPLCI->vsprotdialect = 0;
					}
					plci->vswitchstate = 0;
					plci->vsprot = 0;
					plci->vsprotdialect = 0;
				}
				else
				{
					if (plci->relatedPTYPLCI &&
					    plci->vswitchstate == 1 &&
					    plci->relatedPTYPLCI->vswitchstate == 3) /* join complete */
						plci->vswitchstate = 3;
				}
				break;

				/* Call Deflection Request pending (SSCT) */
			case CD_REQ_PEND:
				SSparms[1] = S_CALL_DEFLECTION;
				if (rc != OK)
				{
					Info = 0x300E; /* not supported */
					plci->appl->CDEnable = 0;
				}
				sendf(plci->appl, _FACILITY_R | CONFIRM, Id,
				      plci->number, "wws", Info, (word)3, SSparms);
				break;

			case RTP_CONNECT_B3_REQ_COMMAND_2:
				if (rc == OK)
				{
					ncci = get_ncci(plci, ch, 0);
					Id = (Id & 0xffff) | (((dword) ncci) << 16);
					plci->channels++;
					a->ncci_state[ncci] = OUTG_CON_PENDING;
				}

			default:
				if (plci->internal_command_queue[0])
				{
					(*(plci->internal_command_queue[0]))(Id, plci, rc);
					if (plci->internal_command)
						return;
				}
				break;
			}
			next_internal_command(Id, plci);
		}
	}
	else /* appl==0 */
	{
		Id = ((word)plci->Id << 8) | plci->adapter->Id;
		if (plci->tel) Id |= EXT_CONTROLLER;

		switch (plci->internal_command)
		{
		case BLOCK_PLCI:
			return;

		case START_L1_SIG_ASSIGN_PEND:
		case REM_L1_SIG_ASSIGN_PEND:
			if (global_req == ASSIGN)
			{
				break;
			}
			else
			{
				dbug(1, dprintf("***L1 Req rem PLCI"));
				plci->internal_command = 0;
				sig_req(plci, REMOVE, 0);
				send_req(plci);
			}
			break;

			/* Call Deflection Request pending, just no appl ptr assigned */
		case CD_REQ_PEND:
			SSparms[1] = S_CALL_DEFLECTION;
			if (rc != OK)
			{
				Info = 0x300E; /* not supported */
			}
			for (i = 0; i < max_appl; i++)
			{
				if (application[i].CDEnable)
				{
					if (!application[i].Id) application[i].CDEnable = 0;
					else
					{
						sendf(&application[i], _FACILITY_R | CONFIRM, Id,
						      plci->number, "wws", Info, (word)3, SSparms);
						if (Info) application[i].CDEnable = 0;
					}
				}
			}
			plci->internal_command = 0;
			break;

		case PERM_COD_HOOK:                   /* finished with Hook_Ind */
			return;

		case PERM_COD_CALL:
			plci->internal_command = PERM_COD_CONN_PEND;
			dbug(1, dprintf("***Codec Connect_Pending, Rc = 0x%x", rc));
			return;

		case PERM_COD_ASSIGN:
			dbug(1, dprintf("***Codec Assign, Rc = 0x%x", rc));
			plci->internal_command = 0;
			if (rc != ASSIGN_OK) break;
			plci->internal_command = PERM_COD_CALL;
			sig_req(plci, CALL_REQ, 0);
			send_req(plci);
			return;

		case LISTEN_SIG_ASSIGN_PEND:
			if (rc == ASSIGN_OK)
			{
				plci->internal_command = 0;
				dbug(1, dprintf("ListenCheck, new SIG_ID = 0x%x", plci->Sig.Id));
				add_p(plci, ESC, "\x02\x18\x00");             /* support call waiting */
				sig_req(plci, INDICATE_REQ, 0);
				send_req(plci);
			}
			else
			{
				dbug(1, dprintf("ListenCheck failed (assignRc=0x%x)", rc));
				a->listen_active--;
				plci_remove(plci);
				plci->State = IDLE;
			}
			break;

		case USELAW_REQ:
			if (global_req == ASSIGN)
			{
				if (rc == ASSIGN_OK)
				{
					sig_req(plci, LAW_REQ, 0);
					send_req(plci);
					dbug(1, dprintf("Auto-Law assigned"));
				}
				else
				{
					dbug(1, dprintf("Auto-Law assign failed"));
					a->automatic_law = 3;
					plci->internal_command = 0;
					a->automatic_lawPLCI = NULL;
				}
				break;
			}
			else if (req == LAW_REQ && rc == OK)
			{
				dbug(1, dprintf("Auto-Law initiated"));
				a->automatic_law = 2;
				plci->internal_command = 0;
			}
			else
			{
				dbug(1, dprintf("Auto-Law not supported"));
				a->automatic_law = 3;
				plci->internal_command = 0;
				sig_req(plci, REMOVE, 0);
				send_req(plci);
				a->automatic_lawPLCI = NULL;
			}
			break;
		}
		plci_remove_check(plci);
	}
}

static void data_rc(PLCI *plci, byte ch)
{
	dword Id;
	DIVA_CAPI_ADAPTER *a;
	NCCI *ncci_ptr;
	DATA_B3_DESC *data;
	word ncci;

	if (plci->appl)
	{
		TransmitBufferFree(plci->appl, plci->data_sent_ptr);
		a = plci->adapter;
		ncci = a->ch_ncci[ch];
		if (ncci && (a->ncci_plci[ncci] == plci->Id))
		{
			ncci_ptr = &(a->ncci[ncci]);
			dbug(1, dprintf("data_out=%d, data_pending=%d", ncci_ptr->data_out, ncci_ptr->data_pending));
			if (ncci_ptr->data_pending)
			{
				data = &(ncci_ptr->DBuffer[ncci_ptr->data_out]);
				if (!(data->Flags & 4) && a->ncci_state[ncci])
				{
					Id = (((dword)ncci) << 16) | ((word)plci->Id << 8) | a->Id;
					if (plci->tel) Id |= EXT_CONTROLLER;
					sendf(plci->appl, _DATA_B3_R | CONFIRM, Id, data->Number,
					      "ww", data->Handle, 0);
				}
				(ncci_ptr->data_out)++;
				if (ncci_ptr->data_out == MAX_DATA_B3)
					ncci_ptr->data_out = 0;
				(ncci_ptr->data_pending)--;
			}
		}
	}
}

static void data_ack(PLCI *plci, byte ch)
{
	dword Id;
	DIVA_CAPI_ADAPTER *a;
	NCCI *ncci_ptr;
	word ncci;

	a = plci->adapter;
	ncci = a->ch_ncci[ch];
	ncci_ptr = &(a->ncci[ncci]);
	if (ncci_ptr->data_ack_pending)
	{
		if (a->ncci_state[ncci] && (a->ncci_plci[ncci] == plci->Id))
		{
			Id = (((dword)ncci) << 16) | ((word)plci->Id << 8) | a->Id;
			if (plci->tel) Id |= EXT_CONTROLLER;
			sendf(plci->appl, _DATA_B3_R | CONFIRM, Id, ncci_ptr->DataAck[ncci_ptr->data_ack_out].Number,
			      "ww", ncci_ptr->DataAck[ncci_ptr->data_ack_out].Handle, 0);
		}
		(ncci_ptr->data_ack_out)++;
		if (ncci_ptr->data_ack_out == MAX_DATA_ACK)
			ncci_ptr->data_ack_out = 0;
		(ncci_ptr->data_ack_pending)--;
	}
}

static void sig_ind(PLCI *plci)
{
	dword x_Id;
	dword Id;
	dword rId;
	word i;
	word cip;
	dword cip_mask;
	byte *ie;
	DIVA_CAPI_ADAPTER *a;
	API_PARSE saved_parms[MAX_MSG_PARMS + 1];
#define MAXPARMSIDS 31
	byte *parms[MAXPARMSIDS];
	byte *add_i[4];
	byte *multi_fac_parms[MAX_MULTI_IE];
	byte *multi_pi_parms[MAX_MULTI_IE];
	byte *multi_ssext_parms[MAX_MULTI_IE];
	byte *multi_CiPN_parms[MAX_MULTI_IE];

	byte *multi_vswitch_parms[MAX_MULTI_IE];

	byte ai_len;
	byte *esc_chi = "";
	byte *esc_law = "";
	byte *pty_cai = "";
	byte *esc_cr  = "";
	byte *esc_profile = "";

	byte facility[256];
	PLCI *tplci = NULL;
	byte chi[] = "\x02\x18\x01";
	byte voice_cai[]  = "\x06\x14\x00\x00\x00\x00\x08";
	byte resume_cau[] = "\x05\x05\x00\x02\x00\x00";
	/* ESC_MSGTYPE must be the last but one message, a new IE has to be */
	/* included before the ESC_MSGTYPE and MAXPARMSIDS has to be incremented */
	/* SMSG is situated at the end because its 0 (for compatibility reasons */
	/* (see Info_Mask Bit 4, first IE. then the message type)           */
	word parms_id[] =
		{MAXPARMSIDS, CPN, 0xff, DSA, OSA, BC, LLC, HLC, ESC_CAUSE, DSP, DT, CHA,
		 UUI, CONG_RR, CONG_RNR, ESC_CHI, KEY, CHI, CAU, ESC_LAW,
		 RDN, RDX, CONN_NR, RIN, NI, CAI, ESC_CR,
		 CST, ESC_PROFILE, 0xff, ESC_MSGTYPE, SMSG};
	/* 14 FTY repl by ESC_CHI */
	/* 18 PI  repl by ESC_LAW */
	/* removed OAD changed to 0xff for future use, OAD is multiIE now */
	word multi_fac_id[] = {1, FTY};
	word multi_pi_id[]  = {1, PI};
	word multi_CiPN_id[]  = {1, OAD};
	word multi_ssext_id[]  = {1, ESC_SSEXT};

	word multi_vswitch_id[]  = {1, ESC_VSWITCH};

	byte *cau;
	word ncci;
	byte SS_Ind[] = "\x05\x02\x00\x02\x00\x00"; /* Hold_Ind struct*/
	byte CF_Ind[] = "\x09\x02\x00\x06\x00\x00\x00\x00\x00\x00";
	byte Interr_Err_Ind[] = "\x0a\x02\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
	byte CONF_Ind[] = "\x09\x16\x00\x06\x00\x00\0x00\0x00\0x00\0x00";
	byte force_mt_info = false;
	byte dir;
	dword d;
	word w;

	a = plci->adapter;
	Id = ((word)plci->Id << 8) | a->Id;
	PUT_WORD(&SS_Ind[4], 0x0000);

	if (plci->sig_remove_id)
	{
		plci->Sig.RNR = 2; /* discard */
		dbug(1, dprintf("SIG discard while remove pending"));
		return;
	}
	if (plci->tel && plci->SuppState != CALL_HELD) Id |= EXT_CONTROLLER;
	dbug(1, dprintf("SigInd-Id=%08lx,plci=%x,tel=%x,state=0x%x,channels=%d,Discflowcl=%d",
			Id, plci->Id, plci->tel, plci->State, plci->channels, plci->hangup_flow_ctrl_timer));
	if (plci->Sig.Ind == CALL_HOLD_ACK && plci->channels)
	{
		plci->Sig.RNR = 1;
		return;
	}
	if (plci->Sig.Ind == HANGUP && plci->channels)
	{
		plci->Sig.RNR = 1;
		plci->hangup_flow_ctrl_timer++;
		/* recover the network layer after timeout */
		if (plci->hangup_flow_ctrl_timer == 100)
		{
			dbug(1, dprintf("Exceptional disc"));
			plci->Sig.RNR = 0;
			plci->hangup_flow_ctrl_timer = 0;
			for (ncci = 1; ncci < MAX_NCCI + 1; ncci++)
			{
				if (a->ncci_plci[ncci] == plci->Id)
				{
					cleanup_ncci_data(plci, ncci);
					if (plci->channels)plci->channels--;
					if (plci->appl)
						sendf(plci->appl, _DISCONNECT_B3_I, (((dword) ncci) << 16) | Id, 0, "ws", 0, "");
				}
			}
			if (plci->appl)
				sendf(plci->appl, _DISCONNECT_I, Id, 0, "w", 0);
			plci_remove(plci);
			plci->State = IDLE;
		}
		return;
	}

	/* do first parse the info with no OAD in, because OAD will be converted */
	/* first the multiple facility IE, then mult. progress ind.              */
	/* then the parameters for the info_ind + conn_ind                       */
	IndParse(plci, multi_fac_id, multi_fac_parms, MAX_MULTI_IE);
	IndParse(plci, multi_pi_id, multi_pi_parms, MAX_MULTI_IE);
	IndParse(plci, multi_ssext_id, multi_ssext_parms, MAX_MULTI_IE);

	IndParse(plci, multi_vswitch_id, multi_vswitch_parms, MAX_MULTI_IE);

	IndParse(plci, parms_id, parms, 0);
	IndParse(plci, multi_CiPN_id, multi_CiPN_parms, MAX_MULTI_IE);
	esc_chi  = parms[14];
	esc_law  = parms[18];
	pty_cai  = parms[24];
	esc_cr   = parms[25];
	esc_profile = parms[27];
	if (esc_cr[0] && plci)
	{
		if (plci->cr_enquiry && plci->appl)
		{
			plci->cr_enquiry = false;
			/* d = MANU_ID            */
			/* w = m_command          */
			/* b = total length       */
			/* b = indication type    */
			/* b = length of all IEs  */
			/* b = IE1                */
			/* S = IE1 length + cont. */
			/* b = IE2                */
			/* S = IE2 length + cont. */
			sendf(plci->appl,
			      _MANUFACTURER_I,
			      Id,
			      0,
			      "dwbbbbSbS", _DI_MANU_ID, plci->m_command,
			      2 + 1 + 1 + esc_cr[0] + 1 + 1 + esc_law[0], plci->Sig.Ind, 1 + 1 + esc_cr[0] + 1 + 1 + esc_law[0], ESC, esc_cr, ESC, esc_law);
		}
	}
	/* create the additional info structure                                  */
	add_i[1] = parms[15]; /* KEY of additional info */
	add_i[2] = parms[11]; /* UUI of additional info */
	ai_len = AddInfo(add_i, multi_fac_parms, esc_chi, facility);

	/* the ESC_LAW indicates if u-Law or a-Law is actually used by the card  */
	/* indication returns by the card if requested by the function           */
	/* AutomaticLaw() after driver init                                      */
	if (a->automatic_law < 4)
	{
		if (esc_law[0]) {
			if (esc_law[2]) {
				dbug(0, dprintf("u-Law selected"));
				a->u_law = 1;
			}
			else {
				dbug(0, dprintf("a-Law selected"));
				a->u_law = 0;
			}
			a->automatic_law = 4;
			if (plci == a->automatic_lawPLCI) {
				plci->internal_command = 0;
				sig_req(plci, REMOVE, 0);
				send_req(plci);
				a->automatic_lawPLCI = NULL;
			}
		}
		if (esc_profile[0])
		{
			dbug(1, dprintf("[%06x] CardProfile: %lx %lx %lx %lx %lx",
					UnMapController(a->Id), GET_DWORD(&esc_profile[6]),
					GET_DWORD(&esc_profile[10]), GET_DWORD(&esc_profile[14]),
					GET_DWORD(&esc_profile[18]), GET_DWORD(&esc_profile[46])));

			a->profile.Global_Options &= 0x000000ffL;
			a->profile.B1_Protocols &= 0x000003ffL;
			a->profile.B2_Protocols &= 0x00001fdfL;
			a->profile.B3_Protocols &= 0x000000b7L;

			a->profile.Global_Options &= GET_DWORD(&esc_profile[6]) |
				GL_BCHANNEL_OPERATION_SUPPORTED;
			a->profile.B1_Protocols &= GET_DWORD(&esc_profile[10]);
			a->profile.B2_Protocols &= GET_DWORD(&esc_profile[14]);
			a->profile.B3_Protocols &= GET_DWORD(&esc_profile[18]);
			a->manufacturer_features = GET_DWORD(&esc_profile[46]);
			a->man_profile.private_options = 0;

			if (a->manufacturer_features & MANUFACTURER_FEATURE_ECHO_CANCELLER)
			{
				a->man_profile.private_options |= 1L << PRIVATE_ECHO_CANCELLER;
				a->profile.Global_Options |= GL_ECHO_CANCELLER_SUPPORTED;
			}


			if (a->manufacturer_features & MANUFACTURER_FEATURE_RTP)
				a->man_profile.private_options |= 1L << PRIVATE_RTP;
			a->man_profile.rtp_primary_payloads = GET_DWORD(&esc_profile[50]);
			a->man_profile.rtp_additional_payloads = GET_DWORD(&esc_profile[54]);


			if (a->manufacturer_features & MANUFACTURER_FEATURE_T38)
				a->man_profile.private_options |= 1L << PRIVATE_T38;


			if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_SUB_SEP_PWD)
				a->man_profile.private_options |= 1L << PRIVATE_FAX_SUB_SEP_PWD;


			if (a->manufacturer_features & MANUFACTURER_FEATURE_V18)
				a->man_profile.private_options |= 1L << PRIVATE_V18;


			if (a->manufacturer_features & MANUFACTURER_FEATURE_DTMF_TONE)
				a->man_profile.private_options |= 1L << PRIVATE_DTMF_TONE;


			if (a->manufacturer_features & MANUFACTURER_FEATURE_PIAFS)
				a->man_profile.private_options |= 1L << PRIVATE_PIAFS;


			if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
				a->man_profile.private_options |= 1L << PRIVATE_FAX_PAPER_FORMATS;


			if (a->manufacturer_features & MANUFACTURER_FEATURE_VOWN)
				a->man_profile.private_options |= 1L << PRIVATE_VOWN;


			if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_NONSTANDARD)
				a->man_profile.private_options |= 1L << PRIVATE_FAX_NONSTANDARD;

		}
		else
		{
			a->profile.Global_Options &= 0x0000007fL;
			a->profile.B1_Protocols &= 0x000003dfL;
			a->profile.B2_Protocols &= 0x00001adfL;
			a->profile.B3_Protocols &= 0x000000b7L;
			a->manufacturer_features &= MANUFACTURER_FEATURE_HARDDTMF;
		}
		if (a->manufacturer_features & (MANUFACTURER_FEATURE_HARDDTMF |
						MANUFACTURER_FEATURE_SOFTDTMF_SEND | MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE))
		{
			a->profile.Global_Options |= GL_DTMF_SUPPORTED;
		}
		a->manufacturer_features &= ~MANUFACTURER_FEATURE_OOB_CHANNEL;
		dbug(1, dprintf("[%06x] Profile: %lx %lx %lx %lx %lx",
				UnMapController(a->Id), a->profile.Global_Options,
				a->profile.B1_Protocols, a->profile.B2_Protocols,
				a->profile.B3_Protocols, a->manufacturer_features));
	}
	/* codec plci for the handset/hook state support is just an internal id  */
	if (plci != a->AdvCodecPLCI)
	{
		force_mt_info = SendMultiIE(plci, Id, multi_fac_parms, FTY, 0x20, 0);
		force_mt_info |= SendMultiIE(plci, Id, multi_pi_parms, PI, 0x210, 0);
		SendSSExtInd(NULL, plci, Id, multi_ssext_parms);
		SendInfo(plci, Id, parms, force_mt_info);

		VSwitchReqInd(plci, Id, multi_vswitch_parms);

	}

	/* switch the codec to the b-channel                                     */
	if (esc_chi[0] && plci && !plci->SuppState) {
		plci->b_channel = esc_chi[esc_chi[0]]&0x1f;
		mixer_set_bchannel_id_esc(plci, plci->b_channel);
		dbug(1, dprintf("storeChannel=0x%x", plci->b_channel));
		if (plci->tel == ADV_VOICE && plci->appl) {
			SetVoiceChannel(a->AdvCodecPLCI, esc_chi, a);
		}
	}

	if (plci->appl) plci->appl->Number++;

	switch (plci->Sig.Ind) {
		/* Response to Get_Supported_Services request */
	case S_SUPPORTED:
		dbug(1, dprintf("S_Supported"));
		if (!plci->appl) break;
		if (pty_cai[0] == 4)
		{
			PUT_DWORD(&CF_Ind[6], GET_DWORD(&pty_cai[1]));
		}
		else
		{
			PUT_DWORD(&CF_Ind[6], MASK_TERMINAL_PORTABILITY | MASK_HOLD_RETRIEVE);
		}
		PUT_WORD(&CF_Ind[1], 0);
		PUT_WORD(&CF_Ind[4], 0);
		sendf(plci->appl, _FACILITY_R | CONFIRM, Id & 0x7, plci->number, "wws", 0, 3, CF_Ind);
		plci_remove(plci);
		break;

		/* Supplementary Service rejected */
	case S_SERVICE_REJ:
		dbug(1, dprintf("S_Reject=0x%x", pty_cai[5]));
		if (!pty_cai[0]) break;
		switch (pty_cai[5])
		{
		case ECT_EXECUTE:
		case THREE_PTY_END:
		case THREE_PTY_BEGIN:
			if (!plci->relatedPTYPLCI) break;
			tplci = plci->relatedPTYPLCI;
			rId = ((word)tplci->Id << 8) | tplci->adapter->Id;
			if (tplci->tel) rId |= EXT_CONTROLLER;
			if (pty_cai[5] == ECT_EXECUTE)
			{
				PUT_WORD(&SS_Ind[1], S_ECT);

				plci->vswitchstate = 0;
				plci->relatedPTYPLCI->vswitchstate = 0;

			}
			else
			{
				PUT_WORD(&SS_Ind[1], pty_cai[5] + 3);
			}
			if (pty_cai[2] != 0xff)
			{
				PUT_WORD(&SS_Ind[4], 0x3600 | (word)pty_cai[2]);
			}
			else
			{
				PUT_WORD(&SS_Ind[4], 0x300E);
			}
			plci->relatedPTYPLCI = NULL;
			plci->ptyState = 0;
			sendf(tplci->appl, _FACILITY_I, rId, 0, "ws", 3, SS_Ind);
			break;

		case CALL_DEFLECTION:
			if (pty_cai[2] != 0xff)
			{
				PUT_WORD(&SS_Ind[4], 0x3600 | (word)pty_cai[2]);
			}
			else
			{
				PUT_WORD(&SS_Ind[4], 0x300E);
			}
			PUT_WORD(&SS_Ind[1], pty_cai[5]);
			for (i = 0; i < max_appl; i++)
			{
				if (application[i].CDEnable)
				{
					if (application[i].Id) sendf(&application[i], _FACILITY_I, Id, 0, "ws", 3, SS_Ind);
					application[i].CDEnable = false;
				}
			}
			break;

		case DEACTIVATION_DIVERSION:
		case ACTIVATION_DIVERSION:
		case DIVERSION_INTERROGATE_CFU:
		case DIVERSION_INTERROGATE_CFB:
		case DIVERSION_INTERROGATE_CFNR:
		case DIVERSION_INTERROGATE_NUM:
		case CCBS_REQUEST:
		case CCBS_DEACTIVATE:
		case CCBS_INTERROGATE:
			if (!plci->appl) break;
			if (pty_cai[2] != 0xff)
			{
				PUT_WORD(&Interr_Err_Ind[4], 0x3600 | (word)pty_cai[2]);
			}
			else
			{
				PUT_WORD(&Interr_Err_Ind[4], 0x300E);
			}
			switch (pty_cai[5])
			{
			case DEACTIVATION_DIVERSION:
				dbug(1, dprintf("Deact_Div"));
				Interr_Err_Ind[0] = 0x9;
				Interr_Err_Ind[3] = 0x6;
				PUT_WORD(&Interr_Err_Ind[1], S_CALL_FORWARDING_STOP);
				break;
			case ACTIVATION_DIVERSION:
				dbug(1, dprintf("Act_Div"));
				Interr_Err_Ind[0] = 0x9;
				Interr_Err_Ind[3] = 0x6;
				PUT_WORD(&Interr_Err_Ind[1], S_CALL_FORWARDING_START);
				break;
			case DIVERSION_INTERROGATE_CFU:
			case DIVERSION_INTERROGATE_CFB:
			case DIVERSION_INTERROGATE_CFNR:
				dbug(1, dprintf("Interr_Div"));
				Interr_Err_Ind[0] = 0xa;
				Interr_Err_Ind[3] = 0x7;
				PUT_WORD(&Interr_Err_Ind[1], S_INTERROGATE_DIVERSION);
				break;
			case DIVERSION_INTERROGATE_NUM:
				dbug(1, dprintf("Interr_Num"));
				Interr_Err_Ind[0] = 0xa;
				Interr_Err_Ind[3] = 0x7;
				PUT_WORD(&Interr_Err_Ind[1], S_INTERROGATE_NUMBERS);
				break;
			case CCBS_REQUEST:
				dbug(1, dprintf("CCBS Request"));
				Interr_Err_Ind[0] = 0xd;
				Interr_Err_Ind[3] = 0xa;
				PUT_WORD(&Interr_Err_Ind[1], S_CCBS_REQUEST);
				break;
			case CCBS_DEACTIVATE:
				dbug(1, dprintf("CCBS Deactivate"));
				Interr_Err_Ind[0] = 0x9;
				Interr_Err_Ind[3] = 0x6;
				PUT_WORD(&Interr_Err_Ind[1], S_CCBS_DEACTIVATE);
				break;
			case CCBS_INTERROGATE:
				dbug(1, dprintf("CCBS Interrogate"));
				Interr_Err_Ind[0] = 0xb;
				Interr_Err_Ind[3] = 0x8;
				PUT_WORD(&Interr_Err_Ind[1], S_CCBS_INTERROGATE);
				break;
			}
			PUT_DWORD(&Interr_Err_Ind[6], plci->appl->S_Handle);
			sendf(plci->appl, _FACILITY_I, Id & 0x7, 0, "ws", 3, Interr_Err_Ind);
			plci_remove(plci);
			break;
		case ACTIVATION_MWI:
		case DEACTIVATION_MWI:
			if (pty_cai[5] == ACTIVATION_MWI)
			{
				PUT_WORD(&SS_Ind[1], S_MWI_ACTIVATE);
			}
			else PUT_WORD(&SS_Ind[1], S_MWI_DEACTIVATE);

			if (pty_cai[2] != 0xff)
			{
				PUT_WORD(&SS_Ind[4], 0x3600 | (word)pty_cai[2]);
			}
			else
			{
				PUT_WORD(&SS_Ind[4], 0x300E);
			}

			if (plci->cr_enquiry)
			{
				sendf(plci->appl, _FACILITY_I, Id & 0xf, 0, "ws", 3, SS_Ind);
				plci_remove(plci);
			}
			else
			{
				sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, SS_Ind);
			}
			break;
		case CONF_ADD: /* ERROR */
		case CONF_BEGIN:
		case CONF_DROP:
		case CONF_ISOLATE:
		case CONF_REATTACH:
			CONF_Ind[0] = 9;
			CONF_Ind[3] = 6;
			switch (pty_cai[5])
			{
			case CONF_BEGIN:
				PUT_WORD(&CONF_Ind[1], S_CONF_BEGIN);
				plci->ptyState = 0;
				break;
			case CONF_DROP:
				CONF_Ind[0] = 5;
				CONF_Ind[3] = 2;
				PUT_WORD(&CONF_Ind[1], S_CONF_DROP);
				plci->ptyState = CONNECTED;
				break;
			case CONF_ISOLATE:
				CONF_Ind[0] = 5;
				CONF_Ind[3] = 2;
				PUT_WORD(&CONF_Ind[1], S_CONF_ISOLATE);
				plci->ptyState = CONNECTED;
				break;
			case CONF_REATTACH:
				CONF_Ind[0] = 5;
				CONF_Ind[3] = 2;
				PUT_WORD(&CONF_Ind[1], S_CONF_REATTACH);
				plci->ptyState = CONNECTED;
				break;
			case CONF_ADD:
				PUT_WORD(&CONF_Ind[1], S_CONF_ADD);
				plci->relatedPTYPLCI = NULL;
				tplci = plci->relatedPTYPLCI;
				if (tplci) tplci->ptyState = CONNECTED;
				plci->ptyState = CONNECTED;
				break;
			}

			if (pty_cai[2] != 0xff)
			{
				PUT_WORD(&CONF_Ind[4], 0x3600 | (word)pty_cai[2]);
			}
			else
			{
				PUT_WORD(&CONF_Ind[4], 0x3303); /* Time-out: network did not respond
								  within the required time */
			}

			PUT_DWORD(&CONF_Ind[6], 0x0);
			sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, CONF_Ind);
			break;
		}
		break;

		/* Supplementary Service indicates success */
	case S_SERVICE:
		dbug(1, dprintf("Service_Ind"));
		PUT_WORD(&CF_Ind[4], 0);
		switch (pty_cai[5])
		{
		case THREE_PTY_END:
		case THREE_PTY_BEGIN:
		case ECT_EXECUTE:
			if (!plci->relatedPTYPLCI) break;
			tplci = plci->relatedPTYPLCI;
			rId = ((word)tplci->Id << 8) | tplci->adapter->Id;
			if (tplci->tel) rId |= EXT_CONTROLLER;
			if (pty_cai[5] == ECT_EXECUTE)
			{
				PUT_WORD(&SS_Ind[1], S_ECT);

				if (plci->vswitchstate != 3)
				{

					plci->ptyState = IDLE;
					plci->relatedPTYPLCI = NULL;
					plci->ptyState = 0;

				}

				dbug(1, dprintf("ECT OK"));
				sendf(tplci->appl, _FACILITY_I, rId, 0, "ws", 3, SS_Ind);



			}
			else
			{
				switch (plci->ptyState)
				{
				case S_3PTY_BEGIN:
					plci->ptyState = CONNECTED;
					dbug(1, dprintf("3PTY ON"));
					break;

				case S_3PTY_END:
					plci->ptyState = IDLE;
					plci->relatedPTYPLCI = NULL;
					plci->ptyState = 0;
					dbug(1, dprintf("3PTY OFF"));
					break;
				}
				PUT_WORD(&SS_Ind[1], pty_cai[5] + 3);
				sendf(tplci->appl, _FACILITY_I, rId, 0, "ws", 3, SS_Ind);
			}
			break;

		case CALL_DEFLECTION:
			PUT_WORD(&SS_Ind[1], pty_cai[5]);
			for (i = 0; i < max_appl; i++)
			{
				if (application[i].CDEnable)
				{
					if (application[i].Id) sendf(&application[i], _FACILITY_I, Id, 0, "ws", 3, SS_Ind);
					application[i].CDEnable = false;
				}
			}
			break;

		case DEACTIVATION_DIVERSION:
		case ACTIVATION_DIVERSION:
			if (!plci->appl) break;
			PUT_WORD(&CF_Ind[1], pty_cai[5] + 2);
			PUT_DWORD(&CF_Ind[6], plci->appl->S_Handle);
			sendf(plci->appl, _FACILITY_I, Id & 0x7, 0, "ws", 3, CF_Ind);
			plci_remove(plci);
			break;

		case DIVERSION_INTERROGATE_CFU:
		case DIVERSION_INTERROGATE_CFB:
		case DIVERSION_INTERROGATE_CFNR:
		case DIVERSION_INTERROGATE_NUM:
		case CCBS_REQUEST:
		case CCBS_DEACTIVATE:
		case CCBS_INTERROGATE:
			if (!plci->appl) break;
			switch (pty_cai[5])
			{
			case DIVERSION_INTERROGATE_CFU:
			case DIVERSION_INTERROGATE_CFB:
			case DIVERSION_INTERROGATE_CFNR:
				dbug(1, dprintf("Interr_Div"));
				PUT_WORD(&pty_cai[1], S_INTERROGATE_DIVERSION);
				pty_cai[3] = pty_cai[0] - 3; /* Supplementary Service-specific parameter len */
				break;
			case DIVERSION_INTERROGATE_NUM:
				dbug(1, dprintf("Interr_Num"));
				PUT_WORD(&pty_cai[1], S_INTERROGATE_NUMBERS);
				pty_cai[3] = pty_cai[0] - 3; /* Supplementary Service-specific parameter len */
				break;
			case CCBS_REQUEST:
				dbug(1, dprintf("CCBS Request"));
				PUT_WORD(&pty_cai[1], S_CCBS_REQUEST);
				pty_cai[3] = pty_cai[0] - 3; /* Supplementary Service-specific parameter len */
				break;
			case CCBS_DEACTIVATE:
				dbug(1, dprintf("CCBS Deactivate"));
				PUT_WORD(&pty_cai[1], S_CCBS_DEACTIVATE);
				pty_cai[3] = pty_cai[0] - 3; /* Supplementary Service-specific parameter len */
				break;
			case CCBS_INTERROGATE:
				dbug(1, dprintf("CCBS Interrogate"));
				PUT_WORD(&pty_cai[1], S_CCBS_INTERROGATE);
				pty_cai[3] = pty_cai[0] - 3; /* Supplementary Service-specific parameter len */
				break;
			}
			PUT_WORD(&pty_cai[4], 0); /* Supplementary Service Reason */
			PUT_DWORD(&pty_cai[6], plci->appl->S_Handle);
			sendf(plci->appl, _FACILITY_I, Id & 0x7, 0, "wS", 3, pty_cai);
			plci_remove(plci);
			break;

		case ACTIVATION_MWI:
		case DEACTIVATION_MWI:
			if (pty_cai[5] == ACTIVATION_MWI)
			{
				PUT_WORD(&SS_Ind[1], S_MWI_ACTIVATE);
			}
			else PUT_WORD(&SS_Ind[1], S_MWI_DEACTIVATE);
			if (plci->cr_enquiry)
			{
				sendf(plci->appl, _FACILITY_I, Id & 0xf, 0, "ws", 3, SS_Ind);
				plci_remove(plci);
			}
			else
			{
				sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, SS_Ind);
			}
			break;
		case MWI_INDICATION:
			if (pty_cai[0] >= 0x12)
			{
				PUT_WORD(&pty_cai[3], S_MWI_INDICATE);
				pty_cai[2] = pty_cai[0] - 2; /* len Parameter */
				pty_cai[5] = pty_cai[0] - 5; /* Supplementary Service-specific parameter len */
				if (plci->appl && (a->Notification_Mask[plci->appl->Id - 1] & SMASK_MWI))
				{
					if (plci->internal_command == GET_MWI_STATE) /* result on Message Waiting Listen */
					{
						sendf(plci->appl, _FACILITY_I, Id & 0xf, 0, "wS", 3, &pty_cai[2]);
						plci_remove(plci);
						return;
					}
					else sendf(plci->appl, _FACILITY_I, Id, 0, "wS", 3, &pty_cai[2]);
					pty_cai[0] = 0;
				}
				else
				{
					for (i = 0; i < max_appl; i++)
					{
						if (a->Notification_Mask[i]&SMASK_MWI)
						{
							sendf(&application[i], _FACILITY_I, Id & 0x7, 0, "wS", 3, &pty_cai[2]);
							pty_cai[0] = 0;
						}
					}
				}

				if (!pty_cai[0])
				{ /* acknowledge */
					facility[2] = 0; /* returncode */
				}
				else facility[2] = 0xff;
			}
			else
			{
				/* reject */
				facility[2] = 0xff; /* returncode */
			}
			facility[0] = 2;
			facility[1] = MWI_RESPONSE; /* Function */
			add_p(plci, CAI, facility);
			add_p(plci, ESC, multi_ssext_parms[0]); /* remembered parameter -> only one possible */
			sig_req(plci, S_SERVICE, 0);
			send_req(plci);
			plci->command = 0;
			next_internal_command(Id, plci);
			break;
		case CONF_ADD: /* OK */
		case CONF_BEGIN:
		case CONF_DROP:
		case CONF_ISOLATE:
		case CONF_REATTACH:
		case CONF_PARTYDISC:
			CONF_Ind[0] = 9;
			CONF_Ind[3] = 6;
			switch (pty_cai[5])
			{
			case CONF_BEGIN:
				PUT_WORD(&CONF_Ind[1], S_CONF_BEGIN);
				if (pty_cai[0] == 6)
				{
					d = pty_cai[6];
					PUT_DWORD(&CONF_Ind[6], d); /* PartyID */
				}
				else
				{
					PUT_DWORD(&CONF_Ind[6], 0x0);
				}
				break;
			case CONF_ISOLATE:
				PUT_WORD(&CONF_Ind[1], S_CONF_ISOLATE);
				CONF_Ind[0] = 5;
				CONF_Ind[3] = 2;
				break;
			case CONF_REATTACH:
				PUT_WORD(&CONF_Ind[1], S_CONF_REATTACH);
				CONF_Ind[0] = 5;
				CONF_Ind[3] = 2;
				break;
			case CONF_DROP:
				PUT_WORD(&CONF_Ind[1], S_CONF_DROP);
				CONF_Ind[0] = 5;
				CONF_Ind[3] = 2;
				break;
			case CONF_ADD:
				PUT_WORD(&CONF_Ind[1], S_CONF_ADD);
				d = pty_cai[6];
				PUT_DWORD(&CONF_Ind[6], d); /* PartyID */
				tplci = plci->relatedPTYPLCI;
				if (tplci) tplci->ptyState = CONNECTED;
				break;
			case CONF_PARTYDISC:
				CONF_Ind[0] = 7;
				CONF_Ind[3] = 4;
				PUT_WORD(&CONF_Ind[1], S_CONF_PARTYDISC);
				d = pty_cai[6];
				PUT_DWORD(&CONF_Ind[4], d); /* PartyID */
				break;
			}
			plci->ptyState = CONNECTED;
			sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, CONF_Ind);
			break;
		case CCBS_INFO_RETAIN:
		case CCBS_ERASECALLLINKAGEID:
		case CCBS_STOP_ALERTING:
			CONF_Ind[0] = 5;
			CONF_Ind[3] = 2;
			switch (pty_cai[5])
			{
			case CCBS_INFO_RETAIN:
				PUT_WORD(&CONF_Ind[1], S_CCBS_INFO_RETAIN);
				break;
			case CCBS_STOP_ALERTING:
				PUT_WORD(&CONF_Ind[1], S_CCBS_STOP_ALERTING);
				break;
			case CCBS_ERASECALLLINKAGEID:
				PUT_WORD(&CONF_Ind[1], S_CCBS_ERASECALLLINKAGEID);
				CONF_Ind[0] = 7;
				CONF_Ind[3] = 4;
				CONF_Ind[6] = 0;
				CONF_Ind[7] = 0;
				break;
			}
			w = pty_cai[6];
			PUT_WORD(&CONF_Ind[4], w); /* PartyID */

			if (plci->appl && (a->Notification_Mask[plci->appl->Id - 1] & SMASK_CCBS))
			{
				sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, CONF_Ind);
			}
			else
			{
				for (i = 0; i < max_appl; i++)
					if (a->Notification_Mask[i] & SMASK_CCBS)
						sendf(&application[i], _FACILITY_I, Id & 0x7, 0, "ws", 3, CONF_Ind);
			}
			break;
		}
		break;
	case CALL_HOLD_REJ:
		cau = parms[7];
		if (cau)
		{
			i = _L3_CAUSE | cau[2];
			if (cau[2] == 0) i = 0x3603;
		}
		else
		{
			i = 0x3603;
		}
		PUT_WORD(&SS_Ind[1], S_HOLD);
		PUT_WORD(&SS_Ind[4], i);
		if (plci->SuppState == HOLD_REQUEST)
		{
			plci->SuppState = IDLE;
			sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, SS_Ind);
		}
		break;

	case CALL_HOLD_ACK:
		if (plci->SuppState == HOLD_REQUEST)
		{
			plci->SuppState = CALL_HELD;
			CodecIdCheck(a, plci);
			start_internal_command(Id, plci, hold_save_command);
		}
		break;

	case CALL_RETRIEVE_REJ:
		cau = parms[7];
		if (cau)
		{
			i = _L3_CAUSE | cau[2];
			if (cau[2] == 0) i = 0x3603;
		}
		else
		{
			i = 0x3603;
		}
		PUT_WORD(&SS_Ind[1], S_RETRIEVE);
		PUT_WORD(&SS_Ind[4], i);
		if (plci->SuppState == RETRIEVE_REQUEST)
		{
			plci->SuppState = CALL_HELD;
			CodecIdCheck(a, plci);
			sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, SS_Ind);
		}
		break;

	case CALL_RETRIEVE_ACK:
		PUT_WORD(&SS_Ind[1], S_RETRIEVE);
		if (plci->SuppState == RETRIEVE_REQUEST)
		{
			plci->SuppState = IDLE;
			plci->call_dir |= CALL_DIR_FORCE_OUTG_NL;
			plci->b_channel = esc_chi[esc_chi[0]]&0x1f;
			if (plci->tel)
			{
				mixer_set_bchannel_id_esc(plci, plci->b_channel);
				dbug(1, dprintf("RetrChannel=0x%x", plci->b_channel));
				SetVoiceChannel(a->AdvCodecPLCI, esc_chi, a);
				if (plci->B2_prot == B2_TRANSPARENT && plci->B3_prot == B3_TRANSPARENT)
				{
					dbug(1, dprintf("Get B-ch"));
					start_internal_command(Id, plci, retrieve_restore_command);
				}
				else
					sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, SS_Ind);
			}
			else
				start_internal_command(Id, plci, retrieve_restore_command);
		}
		break;

	case INDICATE_IND:
		if (plci->State != LISTENING) {
			sig_req(plci, HANGUP, 0);
			send_req(plci);
			break;
		}
		cip = find_cip(a, parms[4], parms[6]);
		cip_mask = 1L << cip;
		dbug(1, dprintf("cip=%d,cip_mask=%lx", cip, cip_mask));
		clear_c_ind_mask(plci);
		if (!remove_started && !a->adapter_disabled)
		{
			set_c_ind_mask_bit(plci, MAX_APPL);
			group_optimization(a, plci);
			for (i = 0; i < max_appl; i++) {
				if (application[i].Id
				    && (a->CIP_Mask[i] & 1 || a->CIP_Mask[i] & cip_mask)
				    && CPN_filter_ok(parms[0], a, i)
				    && test_group_ind_mask_bit(plci, i)) {
					dbug(1, dprintf("storedcip_mask[%d]=0x%lx", i, a->CIP_Mask[i]));
					set_c_ind_mask_bit(plci, i);
					dump_c_ind_mask(plci);
					plci->State = INC_CON_PENDING;
					plci->call_dir = (plci->call_dir & ~(CALL_DIR_OUT | CALL_DIR_ORIGINATE)) |
						CALL_DIR_IN | CALL_DIR_ANSWER;
					if (esc_chi[0]) {
						plci->b_channel = esc_chi[esc_chi[0]] & 0x1f;
						mixer_set_bchannel_id_esc(plci, plci->b_channel);
					}
					/* if a listen on the ext controller is done, check if hook states */
					/* are supported or if just a on board codec must be activated     */
					if (a->codec_listen[i] && !a->AdvSignalPLCI) {
						if (a->profile.Global_Options & HANDSET)
							plci->tel = ADV_VOICE;
						else if (a->profile.Global_Options & ON_BOARD_CODEC)
							plci->tel = CODEC;
						if (plci->tel) Id |= EXT_CONTROLLER;
						a->codec_listen[i] = plci;
					}

					sendf(&application[i], _CONNECT_I, Id, 0,
					      "wSSSSSSSbSSSSS", cip,    /* CIP                 */
					      parms[0],    /* CalledPartyNumber   */
					      multi_CiPN_parms[0],    /* CallingPartyNumber  */
					      parms[2],    /* CalledPartySubad    */
					      parms[3],    /* CallingPartySubad   */
					      parms[4],    /* BearerCapability    */
					      parms[5],    /* LowLC               */
					      parms[6],    /* HighLC              */
					      ai_len,      /* nested struct add_i */
					      add_i[0],    /* B channel info    */
					      add_i[1],    /* keypad facility   */
					      add_i[2],    /* user user data    */
					      add_i[3],    /* nested facility   */
					      multi_CiPN_parms[1]    /* second CiPN(SCR)   */
						);
					SendSSExtInd(&application[i],
						     plci,
						     Id,
						     multi_ssext_parms);
					SendSetupInfo(&application[i],
						      plci,
						      Id,
						      parms,
						      SendMultiIE(plci, Id, multi_pi_parms, PI, 0x210, true));
				}
			}
			clear_c_ind_mask_bit(plci, MAX_APPL);
			dump_c_ind_mask(plci);
		}
		if (c_ind_mask_empty(plci)) {
			sig_req(plci, HANGUP, 0);
			send_req(plci);
			plci->State = IDLE;
		}
		plci->notifiedcall = 0;
		a->listen_active--;
		listen_check(a);
		break;

	case CALL_PEND_NOTIFY:
		plci->notifiedcall = 1;
		listen_check(a);
		break;

	case CALL_IND:
	case CALL_CON:
		if (plci->State == ADVANCED_VOICE_SIG || plci->State == ADVANCED_VOICE_NOSIG)
		{
			if (plci->internal_command == PERM_COD_CONN_PEND)
			{
				if (plci->State == ADVANCED_VOICE_NOSIG)
				{
					dbug(1, dprintf("***Codec OK"));
					if (a->AdvSignalPLCI)
					{
						tplci = a->AdvSignalPLCI;
						if (tplci->spoofed_msg)
						{
							dbug(1, dprintf("***Spoofed Msg(0x%x)", tplci->spoofed_msg));
							tplci->command = 0;
							tplci->internal_command = 0;
							x_Id = ((word)tplci->Id << 8) | tplci->adapter->Id | 0x80;
							switch (tplci->spoofed_msg)
							{
							case CALL_RES:
								tplci->command = _CONNECT_I | RESPONSE;
								api_load_msg(&tplci->saved_msg, saved_parms);
								add_b1(tplci, &saved_parms[1], 0, tplci->B1_facilities);
								if (tplci->adapter->Info_Mask[tplci->appl->Id - 1] & 0x200)
								{
									/* early B3 connect (CIP mask bit 9) no release after a disc */
									add_p(tplci, LLI, "\x01\x01");
								}
								add_s(tplci, CONN_NR, &saved_parms[2]);
								add_s(tplci, LLC, &saved_parms[4]);
								add_ai(tplci, &saved_parms[5]);
								tplci->State = INC_CON_ACCEPT;
								sig_req(tplci, CALL_RES, 0);
								send_req(tplci);
								break;

							case AWAITING_SELECT_B:
								dbug(1, dprintf("Select_B continue"));
								start_internal_command(x_Id, tplci, select_b_command);
								break;

							case AWAITING_MANUF_CON: /* Get_Plci per Manufacturer_Req to ext controller */
								if (!tplci->Sig.Id)
								{
									dbug(1, dprintf("No SigID!"));
									sendf(tplci->appl, _MANUFACTURER_R | CONFIRM, x_Id, tplci->number, "dww", _DI_MANU_ID, _MANUFACTURER_R, _OUT_OF_PLCI);
									plci_remove(tplci);
									break;
								}
								tplci->command = _MANUFACTURER_R;
								api_load_msg(&tplci->saved_msg, saved_parms);
								dir = saved_parms[2].info[0];
								if (dir == 1) {
									sig_req(tplci, CALL_REQ, 0);
								}
								else if (!dir) {
									sig_req(tplci, LISTEN_REQ, 0);
								}
								send_req(tplci);
								sendf(tplci->appl, _MANUFACTURER_R | CONFIRM, x_Id, tplci->number, "dww", _DI_MANU_ID, _MANUFACTURER_R, 0);
								break;

							case (CALL_REQ | AWAITING_MANUF_CON):
								sig_req(tplci, CALL_REQ, 0);
								send_req(tplci);
								break;

							case CALL_REQ:
								if (!tplci->Sig.Id)
								{
									dbug(1, dprintf("No SigID!"));
									sendf(tplci->appl, _CONNECT_R | CONFIRM, tplci->adapter->Id, 0, "w", _OUT_OF_PLCI);
									plci_remove(tplci);
									break;
								}
								tplci->command = _CONNECT_R;
								api_load_msg(&tplci->saved_msg, saved_parms);
								add_s(tplci, CPN, &saved_parms[1]);
								add_s(tplci, DSA, &saved_parms[3]);
								add_ai(tplci, &saved_parms[9]);
								sig_req(tplci, CALL_REQ, 0);
								send_req(tplci);
								break;

							case CALL_RETRIEVE:
								tplci->command = C_RETRIEVE_REQ;
								sig_req(tplci, CALL_RETRIEVE, 0);
								send_req(tplci);
								break;
							}
							tplci->spoofed_msg = 0;
							if (tplci->internal_command == 0)
								next_internal_command(x_Id, tplci);
						}
					}
					next_internal_command(Id, plci);
					break;
				}
				dbug(1, dprintf("***Codec Hook Init Req"));
				plci->internal_command = PERM_COD_HOOK;
				add_p(plci, FTY, "\x01\x09");             /* Get Hook State*/
				sig_req(plci, TEL_CTRL, 0);
				send_req(plci);
			}
		}
		else if (plci->command != _MANUFACTURER_R  /* old style permanent connect */
			 && plci->State != INC_ACT_PENDING)
		{
			mixer_set_bchannel_id_esc(plci, plci->b_channel);
			if (plci->tel == ADV_VOICE && plci->SuppState == IDLE) /* with permanent codec switch on immediately */
			{
				chi[2] = plci->b_channel;
				SetVoiceChannel(a->AdvCodecPLCI, chi, a);
			}
			sendf(plci->appl, _CONNECT_ACTIVE_I, Id, 0, "Sss", parms[21], "", "");
			plci->State = INC_ACT_PENDING;
		}
		break;

	case TEL_CTRL:
		ie = multi_fac_parms[0]; /* inspect the facility hook indications */
		if (plci->State == ADVANCED_VOICE_SIG && ie[0]) {
			switch (ie[1] & 0x91) {
			case 0x80:   /* hook off */
			case 0x81:
				if (plci->internal_command == PERM_COD_HOOK)
				{
					dbug(1, dprintf("init:hook_off"));
					plci->hook_state = ie[1];
					next_internal_command(Id, plci);
					break;
				}
				else /* ignore doubled hook indications */
				{
					if (((plci->hook_state) & 0xf0) == 0x80)
					{
						dbug(1, dprintf("ignore hook"));
						break;
					}
					plci->hook_state = ie[1]&0x91;
				}
				/* check for incoming call pending */
				/* and signal '+'.Appl must decide */
				/* with connect_res if call must   */
				/* accepted or not                 */
				for (i = 0, tplci = NULL; i < max_appl; i++) {
					if (a->codec_listen[i]
					    && (a->codec_listen[i]->State == INC_CON_PENDING
						|| a->codec_listen[i]->State == INC_CON_ALERT)) {
						tplci = a->codec_listen[i];
						tplci->appl = &application[i];
					}
				}
				/* no incoming call, do outgoing call */
				/* and signal '+' if outg. setup   */
				if (!a->AdvSignalPLCI && !tplci) {
					if ((i = get_plci(a))) {
						a->AdvSignalPLCI = &a->plci[i - 1];
						tplci = a->AdvSignalPLCI;
						tplci->tel  = ADV_VOICE;
						PUT_WORD(&voice_cai[5], a->AdvSignalAppl->MaxDataLength);
						if (a->Info_Mask[a->AdvSignalAppl->Id - 1] & 0x200) {
							/* early B3 connect (CIP mask bit 9) no release after a disc */
							add_p(tplci, LLI, "\x01\x01");
						}
						add_p(tplci, CAI, voice_cai);
						add_p(tplci, OAD, a->TelOAD);
						add_p(tplci, OSA, a->TelOSA);
						add_p(tplci, SHIFT | 6, NULL);
						add_p(tplci, SIN, "\x02\x01\x00");
						add_p(tplci, UID, "\x06\x43\x61\x70\x69\x32\x30");
						sig_req(tplci, ASSIGN, DSIG_ID);
						a->AdvSignalPLCI->internal_command = HOOK_OFF_REQ;
						a->AdvSignalPLCI->command = 0;
						tplci->appl = a->AdvSignalAppl;
						tplci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
						send_req(tplci);
					}

				}

				if (!tplci) break;
				Id = ((word)tplci->Id << 8) | a->Id;
				Id |= EXT_CONTROLLER;
				sendf(tplci->appl,
				      _FACILITY_I,
				      Id,
				      0,
				      "ws", (word)0, "\x01+");
				break;

			case 0x90:   /* hook on  */
			case 0x91:
				if (plci->internal_command == PERM_COD_HOOK)
				{
					dbug(1, dprintf("init:hook_on"));
					plci->hook_state = ie[1] & 0x91;
					next_internal_command(Id, plci);
					break;
				}
				else /* ignore doubled hook indications */
				{
					if (((plci->hook_state) & 0xf0) == 0x90) break;
					plci->hook_state = ie[1] & 0x91;
				}
				/* hangup the adv. voice call and signal '-' to the appl */
				if (a->AdvSignalPLCI) {
					Id = ((word)a->AdvSignalPLCI->Id << 8) | a->Id;
					if (plci->tel) Id |= EXT_CONTROLLER;
					sendf(a->AdvSignalAppl,
					      _FACILITY_I,
					      Id,
					      0,
					      "ws", (word)0, "\x01-");
					a->AdvSignalPLCI->internal_command = HOOK_ON_REQ;
					a->AdvSignalPLCI->command = 0;
					sig_req(a->AdvSignalPLCI, HANGUP, 0);
					send_req(a->AdvSignalPLCI);
				}
				break;
			}
		}
		break;

	case RESUME:
		clear_c_ind_mask_bit(plci, (word)(plci->appl->Id - 1));
		PUT_WORD(&resume_cau[4], GOOD);
		sendf(plci->appl, _FACILITY_I, Id, 0, "ws", (word)3, resume_cau);
		break;

	case SUSPEND:
		clear_c_ind_mask(plci);

		if (plci->NL.Id && !plci->nl_remove_id) {
			mixer_remove(plci);
			nl_req_ncci(plci, REMOVE, 0);
		}
		if (!plci->sig_remove_id) {
			plci->internal_command = 0;
			sig_req(plci, REMOVE, 0);
		}
		send_req(plci);
		if (!plci->channels) {
			sendf(plci->appl, _FACILITY_I, Id, 0, "ws", (word)3, "\x05\x04\x00\x02\x00\x00");
			sendf(plci->appl, _DISCONNECT_I, Id, 0, "w", 0);
		}
		break;

	case SUSPEND_REJ:
		break;

	case HANGUP:
		plci->hangup_flow_ctrl_timer = 0;
		if (plci->manufacturer && plci->State == LOCAL_CONNECT) break;
		cau = parms[7];
		if (cau) {
			i = _L3_CAUSE | cau[2];
			if (cau[2] == 0) i = 0;
			else if (cau[2] == 8) i = _L1_ERROR;
			else if (cau[2] == 9 || cau[2] == 10) i = _L2_ERROR;
			else if (cau[2] == 5) i = _CAPI_GUARD_ERROR;
		}
		else {
			i = _L3_ERROR;
		}

		if (plci->State == INC_CON_PENDING || plci->State == INC_CON_ALERT)
		{
			for (i = 0; i < max_appl; i++)
			{
				if (test_c_ind_mask_bit(plci, i))
					sendf(&application[i], _DISCONNECT_I, Id, 0, "w", 0);
			}
		}
		else
		{
			clear_c_ind_mask(plci);
		}
		if (!plci->appl)
		{
			if (plci->State == LISTENING)
			{
				plci->notifiedcall = 0;
				a->listen_active--;
			}
			plci->State = INC_DIS_PENDING;
			if (c_ind_mask_empty(plci))
			{
				plci->State = IDLE;
				if (plci->NL.Id && !plci->nl_remove_id)
				{
					mixer_remove(plci);
					nl_req_ncci(plci, REMOVE, 0);
				}
				if (!plci->sig_remove_id)
				{
					plci->internal_command = 0;
					sig_req(plci, REMOVE, 0);
				}
				send_req(plci);
			}
		}
		else
		{
			/* collision of DISCONNECT or CONNECT_RES with HANGUP can   */
			/* result in a second HANGUP! Don't generate another        */
			/* DISCONNECT                                               */
			if (plci->State != IDLE && plci->State != INC_DIS_PENDING)
			{
				if (plci->State == RESUMING)
				{
					PUT_WORD(&resume_cau[4], i);
					sendf(plci->appl, _FACILITY_I, Id, 0, "ws", (word)3, resume_cau);
				}
				plci->State = INC_DIS_PENDING;
				sendf(plci->appl, _DISCONNECT_I, Id, 0, "w", i);
			}
		}
		break;

	case SSEXT_IND:
		SendSSExtInd(NULL, plci, Id, multi_ssext_parms);
		break;

	case VSWITCH_REQ:
		VSwitchReqInd(plci, Id, multi_vswitch_parms);
		break;
	case VSWITCH_IND:
		if (plci->relatedPTYPLCI &&
		    plci->vswitchstate == 3 &&
		    plci->relatedPTYPLCI->vswitchstate == 3 &&
		    parms[MAXPARMSIDS - 1][0])
		{
			add_p(plci->relatedPTYPLCI, SMSG, parms[MAXPARMSIDS - 1]);
			sig_req(plci->relatedPTYPLCI, VSWITCH_REQ, 0);
			send_req(plci->relatedPTYPLCI);
		}
		else VSwitchReqInd(plci, Id, multi_vswitch_parms);
		break;

	}
}


static void SendSetupInfo(APPL *appl, PLCI *plci, dword Id, byte **parms, byte Info_Sent_Flag)
{
	word i;
	byte *ie;
	word Info_Number;
	byte *Info_Element;
	word Info_Mask = 0;

	dbug(1, dprintf("SetupInfo"));

	for (i = 0; i < MAXPARMSIDS; i++) {
		ie = parms[i];
		Info_Number = 0;
		Info_Element = ie;
		if (ie[0]) {
			switch (i) {
			case 0:
				dbug(1, dprintf("CPN "));
				Info_Number = 0x0070;
				Info_Mask = 0x80;
				Info_Sent_Flag = true;
				break;
			case 8:  /* display      */
				dbug(1, dprintf("display(%d)", i));
				Info_Number = 0x0028;
				Info_Mask = 0x04;
				Info_Sent_Flag = true;
				break;
			case 16: /* Channel Id */
				dbug(1, dprintf("CHI"));
				Info_Number = 0x0018;
				Info_Mask = 0x100;
				Info_Sent_Flag = true;
				mixer_set_bchannel_id(plci, Info_Element);
				break;
			case 19: /* Redirected Number */
				dbug(1, dprintf("RDN"));
				Info_Number = 0x0074;
				Info_Mask = 0x400;
				Info_Sent_Flag = true;
				break;
			case 20: /* Redirected Number extended */
				dbug(1, dprintf("RDX"));
				Info_Number = 0x0073;
				Info_Mask = 0x400;
				Info_Sent_Flag = true;
				break;
			case 22: /* Redirecing Number  */
				dbug(1, dprintf("RIN"));
				Info_Number = 0x0076;
				Info_Mask = 0x400;
				Info_Sent_Flag = true;
				break;
			default:
				Info_Number = 0;
				break;
			}
		}

		if (i == MAXPARMSIDS - 2) { /* to indicate the message type "Setup" */
			Info_Number = 0x8000 | 5;
			Info_Mask = 0x10;
			Info_Element = "";
		}

		if (Info_Sent_Flag && Info_Number) {
			if (plci->adapter->Info_Mask[appl->Id - 1] & Info_Mask) {
				sendf(appl, _INFO_I, Id, 0, "wS", Info_Number, Info_Element);
			}
		}
	}
}


static void SendInfo(PLCI *plci, dword Id, byte **parms, byte iesent)
{
	word i;
	word j;
	word k;
	byte *ie;
	word Info_Number;
	byte *Info_Element;
	word Info_Mask = 0;
	static byte charges[5] = {4, 0, 0, 0, 0};
	static byte cause[] = {0x02, 0x80, 0x00};
	APPL *appl;

	dbug(1, dprintf("InfoParse "));

	if (
		!plci->appl
		&& !plci->State
		&& plci->Sig.Ind != NCR_FACILITY
		)
	{
		dbug(1, dprintf("NoParse "));
		return;
	}
	cause[2] = 0;
	for (i = 0; i < MAXPARMSIDS; i++) {
		ie = parms[i];
		Info_Number = 0;
		Info_Element = ie;
		if (ie[0]) {
			switch (i) {
			case 0:
				dbug(1, dprintf("CPN "));
				Info_Number = 0x0070;
				Info_Mask   = 0x80;
				break;
			case 7: /* ESC_CAU */
				dbug(1, dprintf("cau(0x%x)", ie[2]));
				Info_Number = 0x0008;
				Info_Mask = 0x00;
				cause[2] = ie[2];
				Info_Element = NULL;
				break;
			case 8:  /* display      */
				dbug(1, dprintf("display(%d)", i));
				Info_Number = 0x0028;
				Info_Mask = 0x04;
				break;
			case 9:  /* Date display */
				dbug(1, dprintf("date(%d)", i));
				Info_Number = 0x0029;
				Info_Mask = 0x02;
				break;
			case 10: /* charges */
				for (j = 0; j < 4; j++) charges[1 + j] = 0;
				for (j = 0; j < ie[0] && !(ie[1 + j] & 0x80); j++);
				for (k = 1, j++; j < ie[0] && k <= 4; j++, k++) charges[k] = ie[1 + j];
				Info_Number = 0x4000;
				Info_Mask = 0x40;
				Info_Element = charges;
				break;
			case 11: /* user user info */
				dbug(1, dprintf("uui"));
				Info_Number = 0x007E;
				Info_Mask = 0x08;
				break;
			case 12: /* congestion receiver ready */
				dbug(1, dprintf("clRDY"));
				Info_Number = 0x00B0;
				Info_Mask = 0x08;
				Info_Element = "";
				break;
			case 13: /* congestion receiver not ready */
				dbug(1, dprintf("clNRDY"));
				Info_Number = 0x00BF;
				Info_Mask = 0x08;
				Info_Element = "";
				break;
			case 15: /* Keypad Facility */
				dbug(1, dprintf("KEY"));
				Info_Number = 0x002C;
				Info_Mask = 0x20;
				break;
			case 16: /* Channel Id */
				dbug(1, dprintf("CHI"));
				Info_Number = 0x0018;
				Info_Mask = 0x100;
				mixer_set_bchannel_id(plci, Info_Element);
				break;
			case 17: /* if no 1tr6 cause, send full cause, else esc_cause */
				dbug(1, dprintf("q9cau(0x%x)", ie[2]));
				if (!cause[2] || cause[2] < 0x80) break;  /* eg. layer 1 error */
				Info_Number = 0x0008;
				Info_Mask = 0x01;
				if (cause[2] != ie[2]) Info_Element = cause;
				break;
			case 19: /* Redirected Number */
				dbug(1, dprintf("RDN"));
				Info_Number = 0x0074;
				Info_Mask = 0x400;
				break;
			case 22: /* Redirecing Number  */
				dbug(1, dprintf("RIN"));
				Info_Number = 0x0076;
				Info_Mask = 0x400;
				break;
			case 23: /* Notification Indicator  */
				dbug(1, dprintf("NI"));
				Info_Number = (word)NI;
				Info_Mask = 0x210;
				break;
			case 26: /* Call State  */
				dbug(1, dprintf("CST"));
				Info_Number = (word)CST;
				Info_Mask = 0x01; /* do with cause i.e. for now */
				break;
			case MAXPARMSIDS - 2:  /* Escape Message Type, must be the last indication */
				dbug(1, dprintf("ESC/MT[0x%x]", ie[3]));
				Info_Number = 0x8000 | ie[3];
				if (iesent) Info_Mask = 0xffff;
				else  Info_Mask = 0x10;
				Info_Element = "";
				break;
			default:
				Info_Number  = 0;
				Info_Mask    = 0;
				Info_Element = "";
				break;
			}
		}

		if (plci->Sig.Ind == NCR_FACILITY)           /* check controller broadcast */
		{
			for (j = 0; j < max_appl; j++)
			{
				appl = &application[j];
				if (Info_Number
				    && appl->Id
				    && plci->adapter->Info_Mask[appl->Id - 1] & Info_Mask)
				{
					dbug(1, dprintf("NCR_Ind"));
					iesent = true;
					sendf(&application[j], _INFO_I, Id & 0x0f, 0, "wS", Info_Number, Info_Element);
				}
			}
		}
		else if (!plci->appl)
		{ /* overlap receiving broadcast */
			if (Info_Number == CPN
			    || Info_Number == KEY
			    || Info_Number == NI
			    || Info_Number == DSP
			    || Info_Number == UUI)
			{
				for (j = 0; j < max_appl; j++)
				{
					if (test_c_ind_mask_bit(plci, j))
					{
						dbug(1, dprintf("Ovl_Ind"));
						iesent = true;
						sendf(&application[j], _INFO_I, Id, 0, "wS", Info_Number, Info_Element);
					}
				}
			}
		}               /* all other signalling states */
		else if (Info_Number
			 && plci->adapter->Info_Mask[plci->appl->Id - 1] & Info_Mask)
		{
			dbug(1, dprintf("Std_Ind"));
			iesent = true;
			sendf(plci->appl, _INFO_I, Id, 0, "wS", Info_Number, Info_Element);
		}
	}
}


static byte SendMultiIE(PLCI *plci, dword Id, byte **parms, byte ie_type,
			dword info_mask, byte setupParse)
{
	word i;
	word j;
	byte *ie;
	word Info_Number;
	byte *Info_Element;
	APPL *appl;
	word Info_Mask = 0;
	byte iesent = 0;

	if (
		!plci->appl
		&& !plci->State
		&& plci->Sig.Ind != NCR_FACILITY
		&& !setupParse
		)
	{
		dbug(1, dprintf("NoM-IEParse "));
		return 0;
	}
	dbug(1, dprintf("M-IEParse "));

	for (i = 0; i < MAX_MULTI_IE; i++)
	{
		ie = parms[i];
		Info_Number = 0;
		Info_Element = ie;
		if (ie[0])
		{
			dbug(1, dprintf("[Ind0x%x]:IE=0x%x", plci->Sig.Ind, ie_type));
			Info_Number = (word)ie_type;
			Info_Mask = (word)info_mask;
		}

		if (plci->Sig.Ind == NCR_FACILITY)           /* check controller broadcast */
		{
			for (j = 0; j < max_appl; j++)
			{
				appl = &application[j];
				if (Info_Number
				    && appl->Id
				    && plci->adapter->Info_Mask[appl->Id - 1] & Info_Mask)
				{
					iesent = true;
					dbug(1, dprintf("Mlt_NCR_Ind"));
					sendf(&application[j], _INFO_I, Id & 0x0f, 0, "wS", Info_Number, Info_Element);
				}
			}
		}
		else if (!plci->appl && Info_Number)
		{                                        /* overlap receiving broadcast */
			for (j = 0; j < max_appl; j++)
			{
				if (test_c_ind_mask_bit(plci, j))
				{
					iesent = true;
					dbug(1, dprintf("Mlt_Ovl_Ind"));
					sendf(&application[j] , _INFO_I, Id, 0, "wS", Info_Number, Info_Element);
				}
			}
		}                                        /* all other signalling states */
		else if (Info_Number
			 && plci->adapter->Info_Mask[plci->appl->Id - 1] & Info_Mask)
		{
			iesent = true;
			dbug(1, dprintf("Mlt_Std_Ind"));
			sendf(plci->appl, _INFO_I, Id, 0, "wS", Info_Number, Info_Element);
		}
	}
	return iesent;
}

static void SendSSExtInd(APPL *appl, PLCI *plci, dword Id, byte **parms)
{
	word i;
	/* Format of multi_ssext_parms[i][]:
	   0 byte length
	   1 byte SSEXTIE
	   2 byte SSEXT_REQ/SSEXT_IND
	   3 byte length
	   4 word SSExtCommand
	   6... Params
	*/
	if (
		plci
		&& plci->State
		&& plci->Sig.Ind != NCR_FACILITY
		)
		for (i = 0; i < MAX_MULTI_IE; i++)
		{
			if (parms[i][0] < 6) continue;
			if (parms[i][2] == SSEXT_REQ) continue;

			if (appl)
			{
				parms[i][0] = 0; /* kill it */
				sendf(appl, _MANUFACTURER_I,
				      Id,
				      0,
				      "dwS",
				      _DI_MANU_ID,
				      _DI_SSEXT_CTRL,
				      &parms[i][3]);
			}
			else if (plci->appl)
			{
				parms[i][0] = 0; /* kill it */
				sendf(plci->appl, _MANUFACTURER_I,
				      Id,
				      0,
				      "dwS",
				      _DI_MANU_ID,
				      _DI_SSEXT_CTRL,
				      &parms[i][3]);
			}
		}
};

static void nl_ind(PLCI *plci)
{
	byte ch;
	word ncci;
	dword Id;
	DIVA_CAPI_ADAPTER *a;
	word NCCIcode;
	APPL *APPLptr;
	word count;
	word Num;
	word i, ncpi_state;
	byte len, ncci_state;
	word msg;
	word info = 0;
	word fax_feature_bits;
	byte fax_send_edata_ack;
	static byte v120_header_buffer[2 + 3];
	static word fax_info[] = {
		0,                     /* T30_SUCCESS                        */
		_FAX_NO_CONNECTION,    /* T30_ERR_NO_DIS_RECEIVED            */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_TIMEOUT_NO_RESPONSE        */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_RESPONSE          */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_TOO_MANY_REPEATS           */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_UNEXPECTED_MESSAGE         */
		_FAX_REMOTE_ABORT,     /* T30_ERR_UNEXPECTED_DCN             */
		_FAX_LOCAL_ABORT,      /* T30_ERR_DTC_UNSUPPORTED            */
		_FAX_TRAINING_ERROR,   /* T30_ERR_ALL_RATES_FAILED           */
		_FAX_TRAINING_ERROR,   /* T30_ERR_TOO_MANY_TRAINS            */
		_FAX_PARAMETER_ERROR,  /* T30_ERR_RECEIVE_CORRUPTED          */
		_FAX_REMOTE_ABORT,     /* T30_ERR_UNEXPECTED_DISC            */
		_FAX_LOCAL_ABORT,      /* T30_ERR_APPLICATION_DISC           */
		_FAX_REMOTE_REJECT,    /* T30_ERR_INCOMPATIBLE_DIS           */
		_FAX_LOCAL_ABORT,      /* T30_ERR_INCOMPATIBLE_DCS           */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_TIMEOUT_NO_COMMAND         */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_COMMAND           */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_TIMEOUT_COMMAND_TOO_LONG   */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_TIMEOUT_RESPONSE_TOO_LONG  */
		_FAX_NO_CONNECTION,    /* T30_ERR_NOT_IDENTIFIED             */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_SUPERVISORY_TIMEOUT        */
		_FAX_PARAMETER_ERROR,  /* T30_ERR_TOO_LONG_SCAN_LINE         */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_PAGE_AFTER_MPS    */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_PAGE_AFTER_CFR    */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_DCS_AFTER_FTT     */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_DCS_AFTER_EOM     */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_DCS_AFTER_MPS     */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_DCN_AFTER_MCF     */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_DCN_AFTER_RTN     */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_CFR               */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_MCF_AFTER_EOP     */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_MCF_AFTER_EOM     */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_MCF_AFTER_MPS     */
		0x331d,                /* T30_ERR_SUB_SEP_UNSUPPORTED        */
		0x331e,                /* T30_ERR_PWD_UNSUPPORTED            */
		0x331f,                /* T30_ERR_SUB_SEP_PWD_UNSUPPORTED    */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_INVALID_COMMAND_FRAME      */
		_FAX_PARAMETER_ERROR,  /* T30_ERR_UNSUPPORTED_PAGE_CODING    */
		_FAX_PARAMETER_ERROR,  /* T30_ERR_INVALID_PAGE_CODING        */
		_FAX_REMOTE_REJECT,    /* T30_ERR_INCOMPATIBLE_PAGE_CONFIG   */
		_FAX_LOCAL_ABORT,      /* T30_ERR_TIMEOUT_FROM_APPLICATION   */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_V34FAX_NO_REACTION_ON_MARK */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_V34FAX_TRAINING_TIMEOUT    */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_V34FAX_UNEXPECTED_V21      */
		_FAX_PROTOCOL_ERROR,   /* T30_ERR_V34FAX_PRIMARY_CTS_ON      */
		_FAX_LOCAL_ABORT,      /* T30_ERR_V34FAX_TURNAROUND_POLLING  */
		_FAX_LOCAL_ABORT       /* T30_ERR_V34FAX_V8_INCOMPATIBILITY  */
	};

	byte dtmf_code_buffer[CAPIDTMF_RECV_DIGIT_BUFFER_SIZE + 1];


	static word rtp_info[] = {
		GOOD,                  /* RTP_SUCCESS                       */
		0x3600                 /* RTP_ERR_SSRC_OR_PAYLOAD_CHANGE    */
	};

	static dword udata_forwarding_table[0x100 / sizeof(dword)] =
		{
			0x0020301e, 0x00000000, 0x00000000, 0x00000000,
			0x00000000, 0x00000000, 0x00000000, 0x00000000
		};

	ch = plci->NL.IndCh;
	a = plci->adapter;
	ncci = a->ch_ncci[ch];
	Id = (((dword)(ncci ? ncci : ch)) << 16) | (((word) plci->Id) << 8) | a->Id;
	if (plci->tel) Id |= EXT_CONTROLLER;
	APPLptr = plci->appl;
	dbug(1, dprintf("NL_IND-Id(NL:0x%x)=0x%08lx,plci=%x,tel=%x,state=0x%x,ch=0x%x,chs=%d,Ind=%x",
			plci->NL.Id, Id, plci->Id, plci->tel, plci->State, ch, plci->channels, plci->NL.Ind & 0x0f));

	/* in the case if no connect_active_Ind was sent to the appl we wait for */

	if (plci->nl_remove_id)
	{
		plci->NL.RNR = 2; /* discard */
		dbug(1, dprintf("NL discard while remove pending"));
		return;
	}
	if ((plci->NL.Ind & 0x0f) == N_CONNECT)
	{
		if (plci->State == INC_DIS_PENDING
		    || plci->State == OUTG_DIS_PENDING
		    || plci->State == IDLE)
		{
			plci->NL.RNR = 2; /* discard */
			dbug(1, dprintf("discard n_connect"));
			return;
		}
		if (plci->State < INC_ACT_PENDING)
		{
			plci->NL.RNR = 1; /* flow control */
			channel_x_off(plci, ch, N_XON_CONNECT_IND);
			return;
		}
	}

	if (!APPLptr)                         /* no application or invalid data */
	{                                    /* while reloading the DSP        */
		dbug(1, dprintf("discard1"));
		plci->NL.RNR = 2;
		return;
	}

	if (((plci->NL.Ind & 0x0f) == N_UDATA)
	    && (((plci->B2_prot != B2_SDLC) && ((plci->B1_resource == 17) || (plci->B1_resource == 18)))
		|| (plci->B2_prot == 7)
		|| (plci->B3_prot == 7)))
	{
		plci->ncpi_buffer[0] = 0;

		ncpi_state = plci->ncpi_state;
		if (plci->NL.complete == 1)
		{
			byte *data = &plci->NL.RBuffer->P[0];

			if ((plci->NL.RBuffer->length >= 12)
			    && ((*data == DSP_UDATA_INDICATION_DCD_ON)
				|| (*data == DSP_UDATA_INDICATION_CTS_ON)))
			{
				word conn_opt, ncpi_opt = 0x00;
/*      HexDump ("MDM N_UDATA:", plci->NL.RBuffer->length, data); */

				if (*data == DSP_UDATA_INDICATION_DCD_ON)
					plci->ncpi_state |= NCPI_MDM_DCD_ON_RECEIVED;
				if (*data == DSP_UDATA_INDICATION_CTS_ON)
					plci->ncpi_state |= NCPI_MDM_CTS_ON_RECEIVED;

				data++;    /* indication code */
				data += 2; /* timestamp */
				if ((*data == DSP_CONNECTED_NORM_V18) || (*data == DSP_CONNECTED_NORM_VOWN))
					ncpi_state &= ~(NCPI_MDM_DCD_ON_RECEIVED | NCPI_MDM_CTS_ON_RECEIVED);
				data++;    /* connected norm */
				conn_opt = GET_WORD(data);
				data += 2; /* connected options */

				PUT_WORD(&(plci->ncpi_buffer[1]), (word)(GET_DWORD(data) & 0x0000FFFF));

				if (conn_opt & DSP_CONNECTED_OPTION_MASK_V42)
				{
					ncpi_opt |= MDM_NCPI_ECM_V42;
				}
				else if (conn_opt & DSP_CONNECTED_OPTION_MASK_MNP)
				{
					ncpi_opt |= MDM_NCPI_ECM_MNP;
				}
				else
				{
					ncpi_opt |= MDM_NCPI_TRANSPARENT;
				}
				if (conn_opt & DSP_CONNECTED_OPTION_MASK_COMPRESSION)
				{
					ncpi_opt |= MDM_NCPI_COMPRESSED;
				}
				PUT_WORD(&(plci->ncpi_buffer[3]), ncpi_opt);
				plci->ncpi_buffer[0] = 4;

				plci->ncpi_state |= NCPI_VALID_CONNECT_B3_IND | NCPI_VALID_CONNECT_B3_ACT | NCPI_VALID_DISC_B3_IND;
			}
		}
		if (plci->B3_prot == 7)
		{
			if (((a->ncci_state[ncci] == INC_ACT_PENDING) || (a->ncci_state[ncci] == OUTG_CON_PENDING))
			    && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
			    && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
			{
				a->ncci_state[ncci] = INC_ACT_PENDING;
				sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer);
				plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
			}
		}

		if (!((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1])
		      & ((1L << PRIVATE_V18) | (1L << PRIVATE_VOWN)))
		    || !(ncpi_state & NCPI_MDM_DCD_ON_RECEIVED)
		    || !(ncpi_state & NCPI_MDM_CTS_ON_RECEIVED))

		{
			plci->NL.RNR = 2;
			return;
		}
	}

	if (plci->NL.complete == 2)
	{
		if (((plci->NL.Ind & 0x0f) == N_UDATA)
		    && !(udata_forwarding_table[plci->RData[0].P[0] >> 5] & (1L << (plci->RData[0].P[0] & 0x1f))))
		{
			switch (plci->RData[0].P[0])
			{

			case DTMF_UDATA_INDICATION_FAX_CALLING_TONE:
				if (plci->dtmf_rec_active & DTMF_LISTEN_ACTIVE_FLAG)
					sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", SELECTOR_DTMF, "\x01X");
				break;
			case DTMF_UDATA_INDICATION_ANSWER_TONE:
				if (plci->dtmf_rec_active & DTMF_LISTEN_ACTIVE_FLAG)
					sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", SELECTOR_DTMF, "\x01Y");
				break;
			case DTMF_UDATA_INDICATION_DIGITS_RECEIVED:
				dtmf_indication(Id, plci, plci->RData[0].P, plci->RData[0].PLength);
				break;
			case DTMF_UDATA_INDICATION_DIGITS_SENT:
				dtmf_confirmation(Id, plci);
				break;


			case UDATA_INDICATION_MIXER_TAP_DATA:
				capidtmf_recv_process_block(&(plci->capidtmf_state), plci->RData[0].P + 1, (word)(plci->RData[0].PLength - 1));
				i = capidtmf_indication(&(plci->capidtmf_state), dtmf_code_buffer + 1);
				if (i != 0)
				{
					dtmf_code_buffer[0] = DTMF_UDATA_INDICATION_DIGITS_RECEIVED;
					dtmf_indication(Id, plci, dtmf_code_buffer, (word)(i + 1));
				}
				break;


			case UDATA_INDICATION_MIXER_COEFS_SET:
				mixer_indication_coefs_set(Id, plci);
				break;
			case UDATA_INDICATION_XCONNECT_FROM:
				mixer_indication_xconnect_from(Id, plci, plci->RData[0].P, plci->RData[0].PLength);
				break;
			case UDATA_INDICATION_XCONNECT_TO:
				mixer_indication_xconnect_to(Id, plci, plci->RData[0].P, plci->RData[0].PLength);
				break;


			case LEC_UDATA_INDICATION_DISABLE_DETECT:
				ec_indication(Id, plci, plci->RData[0].P, plci->RData[0].PLength);
				break;



			default:
				break;
			}
		}
		else
		{
			if ((plci->RData[0].PLength != 0)
			    && ((plci->B2_prot == B2_V120_ASYNC)
				|| (plci->B2_prot == B2_V120_ASYNC_V42BIS)
				|| (plci->B2_prot == B2_V120_BIT_TRANSPARENT)))
			{

				sendf(plci->appl, _DATA_B3_I, Id, 0,
				      "dwww",
				      plci->RData[1].P,
				      (plci->NL.RNum < 2) ? 0 : plci->RData[1].PLength,
				      plci->RNum,
				      plci->RFlags);

			}
			else
			{

				sendf(plci->appl, _DATA_B3_I, Id, 0,
				      "dwww",
				      plci->RData[0].P,
				      plci->RData[0].PLength,
				      plci->RNum,
				      plci->RFlags);

			}
		}
		return;
	}

	fax_feature_bits = 0;
	if ((plci->NL.Ind & 0x0f) == N_CONNECT ||
	    (plci->NL.Ind & 0x0f) == N_CONNECT_ACK ||
	    (plci->NL.Ind & 0x0f) == N_DISC ||
	    (plci->NL.Ind & 0x0f) == N_EDATA ||
	    (plci->NL.Ind & 0x0f) == N_DISC_ACK)
	{
		info = 0;
		plci->ncpi_buffer[0] = 0;
		switch (plci->B3_prot) {
		case  0: /*XPARENT*/
		case  1: /*T.90 NL*/
			break;    /* no network control protocol info - jfr */
		case  2: /*ISO8202*/
		case  3: /*X25 DCE*/
			for (i = 0; i < plci->NL.RLength; i++) plci->ncpi_buffer[4 + i] = plci->NL.RBuffer->P[i];
			plci->ncpi_buffer[0] = (byte)(i + 3);
			plci->ncpi_buffer[1] = (byte)(plci->NL.Ind & N_D_BIT ? 1 : 0);
			plci->ncpi_buffer[2] = 0;
			plci->ncpi_buffer[3] = 0;
			break;
		case  4: /*T.30 - FAX*/
		case  5: /*T.30 - FAX*/
			if (plci->NL.RLength >= sizeof(T30_INFO))
			{
				dbug(1, dprintf("FaxStatus %04x", ((T30_INFO *)plci->NL.RBuffer->P)->code));
				len = 9;
				PUT_WORD(&(plci->ncpi_buffer[1]), ((T30_INFO *)plci->NL.RBuffer->P)->rate_div_2400 * 2400);
				fax_feature_bits = GET_WORD(&((T30_INFO *)plci->NL.RBuffer->P)->feature_bits_low);
				i = (((T30_INFO *)plci->NL.RBuffer->P)->resolution & T30_RESOLUTION_R8_0770_OR_200) ? 0x0001 : 0x0000;
				if (plci->B3_prot == 5)
				{
					if (!(fax_feature_bits & T30_FEATURE_BIT_ECM))
						i |= 0x8000; /* This is not an ECM connection */
					if (fax_feature_bits & T30_FEATURE_BIT_T6_CODING)
						i |= 0x4000; /* This is a connection with MMR compression */
					if (fax_feature_bits & T30_FEATURE_BIT_2D_CODING)
						i |= 0x2000; /* This is a connection with MR compression */
					if (fax_feature_bits & T30_FEATURE_BIT_MORE_DOCUMENTS)
						i |= 0x0004; /* More documents */
					if (fax_feature_bits & T30_FEATURE_BIT_POLLING)
						i |= 0x0002; /* Fax-polling indication */
				}
				dbug(1, dprintf("FAX Options %04x %04x", fax_feature_bits, i));
				PUT_WORD(&(plci->ncpi_buffer[3]), i);
				PUT_WORD(&(plci->ncpi_buffer[5]), ((T30_INFO *)plci->NL.RBuffer->P)->data_format);
				plci->ncpi_buffer[7] = ((T30_INFO *)plci->NL.RBuffer->P)->pages_low;
				plci->ncpi_buffer[8] = ((T30_INFO *)plci->NL.RBuffer->P)->pages_high;
				plci->ncpi_buffer[len] = 0;
				if (((T30_INFO *)plci->NL.RBuffer->P)->station_id_len)
				{
					plci->ncpi_buffer[len] = 20;
					for (i = 0; i < T30_MAX_STATION_ID_LENGTH; i++)
						plci->ncpi_buffer[++len] = ((T30_INFO *)plci->NL.RBuffer->P)->station_id[i];
				}
				if (((plci->NL.Ind & 0x0f) == N_DISC) || ((plci->NL.Ind & 0x0f) == N_DISC_ACK))
				{
					if (((T30_INFO *)plci->NL.RBuffer->P)->code < ARRAY_SIZE(fax_info))
						info = fax_info[((T30_INFO *)plci->NL.RBuffer->P)->code];
					else
						info = _FAX_PROTOCOL_ERROR;
				}

				if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id - 1])
				    & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD)))
				{
					i = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + ((T30_INFO *)plci->NL.RBuffer->P)->head_line_len;
					while (i < plci->NL.RBuffer->length)
						plci->ncpi_buffer[++len] = plci->NL.RBuffer->P[i++];
				}

				plci->ncpi_buffer[0] = len;
				fax_feature_bits = GET_WORD(&((T30_INFO *)plci->NL.RBuffer->P)->feature_bits_low);
				PUT_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->feature_bits_low, fax_feature_bits);

				plci->ncpi_state |= NCPI_VALID_CONNECT_B3_IND;
				if (((plci->NL.Ind & 0x0f) == N_CONNECT_ACK)
				    || (((plci->NL.Ind & 0x0f) == N_CONNECT)
					&& (fax_feature_bits & T30_FEATURE_BIT_POLLING))
				    || (((plci->NL.Ind & 0x0f) == N_EDATA)
					&& ((((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_TRAIN_OK)
					    || (((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_DIS)
					    || (((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_DTC))))
				{
					plci->ncpi_state |= NCPI_VALID_CONNECT_B3_ACT;
				}
				if (((plci->NL.Ind & 0x0f) == N_DISC)
				    || ((plci->NL.Ind & 0x0f) == N_DISC_ACK)
				    || (((plci->NL.Ind & 0x0f) == N_EDATA)
					&& (((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_EOP_CAPI)))
				{
					plci->ncpi_state |= NCPI_VALID_CONNECT_B3_ACT | NCPI_VALID_DISC_B3_IND;
				}
			}
			break;

		case B3_RTP:
			if (((plci->NL.Ind & 0x0f) == N_DISC) || ((plci->NL.Ind & 0x0f) == N_DISC_ACK))
			{
				if (plci->NL.RLength != 0)
				{
					info = rtp_info[plci->NL.RBuffer->P[0]];
					plci->ncpi_buffer[0] = plci->NL.RLength - 1;
					for (i = 1; i < plci->NL.RLength; i++)
						plci->ncpi_buffer[i] = plci->NL.RBuffer->P[i];
				}
			}
			break;

		}
		plci->NL.RNR = 2;
	}
	switch (plci->NL.Ind & 0x0f) {
	case N_EDATA:
		if ((plci->B3_prot == 4) || (plci->B3_prot == 5))
		{
			dbug(1, dprintf("EDATA ncci=0x%x state=%d code=%02x", ncci, a->ncci_state[ncci],
					((T30_INFO *)plci->NL.RBuffer->P)->code));
			fax_send_edata_ack = (((T30_INFO *)(plci->fax_connect_info_buffer))->operating_mode == T30_OPERATING_MODE_CAPI_NEG);

			if ((plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF)
			    && (plci->nsf_control_bits & (T30_NSF_CONTROL_BIT_NEGOTIATE_IND | T30_NSF_CONTROL_BIT_NEGOTIATE_RESP))
			    && (((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_DIS)
			    && (a->ncci_state[ncci] == OUTG_CON_PENDING)
			    && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
			    && !(plci->ncpi_state & NCPI_NEGOTIATE_B3_SENT))
			{
				((T30_INFO *)(plci->fax_connect_info_buffer))->code = ((T30_INFO *)plci->NL.RBuffer->P)->code;
				sendf(plci->appl, _MANUFACTURER_I, Id, 0, "dwbS", _DI_MANU_ID, _DI_NEGOTIATE_B3,
				      (byte)(plci->ncpi_buffer[0] + 1), plci->ncpi_buffer);
				plci->ncpi_state |= NCPI_NEGOTIATE_B3_SENT;
				if (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP)
					fax_send_edata_ack = false;
			}

			if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
			{
				switch (((T30_INFO *)plci->NL.RBuffer->P)->code)
				{
				case EDATA_T30_DIS:
					if ((a->ncci_state[ncci] == OUTG_CON_PENDING)
					    && !(GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low) & T30_CONTROL_BIT_REQUEST_POLLING)
					    && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
					    && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
					{
						a->ncci_state[ncci] = INC_ACT_PENDING;
						if (plci->B3_prot == 4)
							sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
						else
							sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer);
						plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
					}
					break;

				case EDATA_T30_TRAIN_OK:
					if ((a->ncci_state[ncci] == INC_ACT_PENDING)
					    && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
					    && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
					{
						if (plci->B3_prot == 4)
							sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
						else
							sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer);
						plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
					}
					break;

				case EDATA_T30_EOP_CAPI:
					if (a->ncci_state[ncci] == CONNECTED)
					{
						sendf(plci->appl, _DISCONNECT_B3_I, Id, 0, "wS", GOOD, plci->ncpi_buffer);
						a->ncci_state[ncci] = INC_DIS_PENDING;
						plci->ncpi_state = 0;
						fax_send_edata_ack = false;
					}
					break;
				}
			}
			else
			{
				switch (((T30_INFO *)plci->NL.RBuffer->P)->code)
				{
				case EDATA_T30_TRAIN_OK:
					if ((a->ncci_state[ncci] == INC_ACT_PENDING)
					    && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
					    && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
					{
						if (plci->B3_prot == 4)
							sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
						else
							sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer);
						plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
					}
					break;
				}
			}
			if (fax_send_edata_ack)
			{
				((T30_INFO *)(plci->fax_connect_info_buffer))->code = ((T30_INFO *)plci->NL.RBuffer->P)->code;
				plci->fax_edata_ack_length = 1;
				start_internal_command(Id, plci, fax_edata_ack_command);
			}
		}
		else
		{
			dbug(1, dprintf("EDATA ncci=0x%x state=%d", ncci, a->ncci_state[ncci]));
		}
		break;
	case N_CONNECT:
		if (!a->ch_ncci[ch])
		{
			ncci = get_ncci(plci, ch, 0);
			Id = (Id & 0xffff) | (((dword) ncci) << 16);
		}
		dbug(1, dprintf("N_CONNECT: ch=%d state=%d plci=%lx plci_Id=%lx plci_State=%d",
				ch, a->ncci_state[ncci], a->ncci_plci[ncci], plci->Id, plci->State));

		msg = _CONNECT_B3_I;
		if (a->ncci_state[ncci] == IDLE)
			plci->channels++;
		else if (plci->B3_prot == 1)
			msg = _CONNECT_B3_T90_ACTIVE_I;

		a->ncci_state[ncci] = INC_CON_PENDING;
		if (plci->B3_prot == 4)
			sendf(plci->appl, msg, Id, 0, "s", "");
		else
			sendf(plci->appl, msg, Id, 0, "S", plci->ncpi_buffer);
		break;
	case N_CONNECT_ACK:
		dbug(1, dprintf("N_connect_Ack"));
		if (plci->internal_command_queue[0]
		    && ((plci->adjust_b_state == ADJUST_B_CONNECT_2)
			|| (plci->adjust_b_state == ADJUST_B_CONNECT_3)
			|| (plci->adjust_b_state == ADJUST_B_CONNECT_4)))
		{
			(*(plci->internal_command_queue[0]))(Id, plci, 0);
			if (!plci->internal_command)
				next_internal_command(Id, plci);
			break;
		}
		msg = _CONNECT_B3_ACTIVE_I;
		if (plci->B3_prot == 1)
		{
			if (a->ncci_state[ncci] != OUTG_CON_PENDING)
				msg = _CONNECT_B3_T90_ACTIVE_I;
			a->ncci_state[ncci] = INC_ACT_PENDING;
			sendf(plci->appl, msg, Id, 0, "S", plci->ncpi_buffer);
		}
		else if ((plci->B3_prot == 4) || (plci->B3_prot == 5) || (plci->B3_prot == 7))
		{
			if ((a->ncci_state[ncci] == OUTG_CON_PENDING)
			    && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
			    && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
			{
				a->ncci_state[ncci] = INC_ACT_PENDING;
				if (plci->B3_prot == 4)
					sendf(plci->appl, msg, Id, 0, "s", "");
				else
					sendf(plci->appl, msg, Id, 0, "S", plci->ncpi_buffer);
				plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
			}
		}
		else
		{
			a->ncci_state[ncci] = INC_ACT_PENDING;
			sendf(plci->appl, msg, Id, 0, "S", plci->ncpi_buffer);
		}
		if (plci->adjust_b_restore)
		{
			plci->adjust_b_restore = false;
			start_internal_command(Id, plci, adjust_b_restore);
		}
		break;
	case N_DISC:
	case N_DISC_ACK:
		if (plci->internal_command_queue[0]
		    && ((plci->internal_command == FAX_DISCONNECT_COMMAND_1)
			|| (plci->internal_command == FAX_DISCONNECT_COMMAND_2)
			|| (plci->internal_command == FAX_DISCONNECT_COMMAND_3)))
		{
			(*(plci->internal_command_queue[0]))(Id, plci, 0);
			if (!plci->internal_command)
				next_internal_command(Id, plci);
		}
		ncci_state = a->ncci_state[ncci];
		ncci_remove(plci, ncci, false);

		/* with N_DISC or N_DISC_ACK the IDI frees the respective   */
		/* channel, so we cannot store the state in ncci_state! The */
		/* information which channel we received a N_DISC is thus   */
		/* stored in the inc_dis_ncci_table buffer.                 */
		for (i = 0; plci->inc_dis_ncci_table[i]; i++);
		plci->inc_dis_ncci_table[i] = (byte) ncci;

		/* need a connect_b3_ind before a disconnect_b3_ind with FAX */
		if (!plci->channels
		    && (plci->B1_resource == 16)
		    && (plci->State <= CONNECTED))
		{
			len = 9;
			i = ((T30_INFO *)plci->fax_connect_info_buffer)->rate_div_2400 * 2400;
			PUT_WORD(&plci->ncpi_buffer[1], i);
			PUT_WORD(&plci->ncpi_buffer[3], 0);
			i = ((T30_INFO *)plci->fax_connect_info_buffer)->data_format;
			PUT_WORD(&plci->ncpi_buffer[5], i);
			PUT_WORD(&plci->ncpi_buffer[7], 0);
			plci->ncpi_buffer[len] = 0;
			plci->ncpi_buffer[0] = len;
			if (plci->B3_prot == 4)
				sendf(plci->appl, _CONNECT_B3_I, Id, 0, "s", "");
			else
			{

				if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id - 1])
				    & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD)))
				{
					plci->ncpi_buffer[++len] = 0;
					plci->ncpi_buffer[++len] = 0;
					plci->ncpi_buffer[++len] = 0;
					plci->ncpi_buffer[0] = len;
				}

				sendf(plci->appl, _CONNECT_B3_I, Id, 0, "S", plci->ncpi_buffer);
			}
			sendf(plci->appl, _DISCONNECT_B3_I, Id, 0, "wS", info, plci->ncpi_buffer);
			plci->ncpi_state = 0;
			sig_req(plci, HANGUP, 0);
			send_req(plci);
			plci->State = OUTG_DIS_PENDING;
			/* disc here */
		}
		else if ((a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
			 && ((plci->B3_prot == 4) || (plci->B3_prot == 5))
			 && ((ncci_state == INC_DIS_PENDING) || (ncci_state == IDLE)))
		{
			if (ncci_state == IDLE)
			{
				if (plci->channels)
					plci->channels--;
				if ((plci->State == IDLE || plci->State == SUSPENDING) && !plci->channels) {
					if (plci->State == SUSPENDING) {
						sendf(plci->appl,
						      _FACILITY_I,
						      Id & 0xffffL,
						      0,
						      "ws", (word)3, "\x03\x04\x00\x00");
						sendf(plci->appl, _DISCONNECT_I, Id & 0xffffL, 0, "w", 0);
					}
					plci_remove(plci);
					plci->State = IDLE;
				}
			}
		}
		else if (plci->channels)
		{
			sendf(plci->appl, _DISCONNECT_B3_I, Id, 0, "wS", info, plci->ncpi_buffer);
			plci->ncpi_state = 0;
			if ((ncci_state == OUTG_REJ_PENDING)
			    && ((plci->B3_prot != B3_T90NL) && (plci->B3_prot != B3_ISO8208) && (plci->B3_prot != B3_X25_DCE)))
			{
				sig_req(plci, HANGUP, 0);
				send_req(plci);
				plci->State = OUTG_DIS_PENDING;
			}
		}
		break;
	case N_RESET:
		a->ncci_state[ncci] = INC_RES_PENDING;
		sendf(plci->appl, _RESET_B3_I, Id, 0, "S", plci->ncpi_buffer);
		break;
	case N_RESET_ACK:
		a->ncci_state[ncci] = CONNECTED;
		sendf(plci->appl, _RESET_B3_I, Id, 0, "S", plci->ncpi_buffer);
		break;

	case N_UDATA:
		if (!(udata_forwarding_table[plci->NL.RBuffer->P[0] >> 5] & (1L << (plci->NL.RBuffer->P[0] & 0x1f))))
		{
			plci->RData[0].P = plci->internal_ind_buffer + (-((int)(long)(plci->internal_ind_buffer)) & 3);
			plci->RData[0].PLength = INTERNAL_IND_BUFFER_SIZE;
			plci->NL.R = plci->RData;
			plci->NL.RNum = 1;
			return;
		}
	case N_BDATA:
	case N_DATA:
		if (((a->ncci_state[ncci] != CONNECTED) && (plci->B2_prot == 1)) /* transparent */
		    || (a->ncci_state[ncci] == IDLE)
		    || (a->ncci_state[ncci] == INC_DIS_PENDING))
		{
			plci->NL.RNR = 2;
			break;
		}
		if ((a->ncci_state[ncci] != CONNECTED)
		    && (a->ncci_state[ncci] != OUTG_DIS_PENDING)
		    && (a->ncci_state[ncci] != OUTG_REJ_PENDING))
		{
			dbug(1, dprintf("flow control"));
			plci->NL.RNR = 1; /* flow control  */
			channel_x_off(plci, ch, 0);
			break;
		}

		NCCIcode = ncci | (((word)a->Id) << 8);

		/* count all buffers within the Application pool    */
		/* belonging to the same NCCI. If this is below the */
		/* number of buffers available per NCCI we accept   */
		/* this packet, otherwise we reject it              */
		count = 0;
		Num = 0xffff;
		for (i = 0; i < APPLptr->MaxBuffer; i++) {
			if (NCCIcode == APPLptr->DataNCCI[i]) count++;
			if (!APPLptr->DataNCCI[i] && Num == 0xffff) Num = i;
		}

		if (count >= APPLptr->MaxNCCIData || Num == 0xffff)
		{
			dbug(3, dprintf("Flow-Control"));
			plci->NL.RNR = 1;
			if (++(APPLptr->NCCIDataFlowCtrlTimer) >=
			    (word)((a->manufacturer_features & MANUFACTURER_FEATURE_OOB_CHANNEL) ? 40 : 2000))
			{
				plci->NL.RNR = 2;
				dbug(3, dprintf("DiscardData"));
			} else {
				channel_x_off(plci, ch, 0);
			}
			break;
		}
		else
		{
			APPLptr->NCCIDataFlowCtrlTimer = 0;
		}

		plci->RData[0].P = ReceiveBufferGet(APPLptr, Num);
		if (!plci->RData[0].P) {
			plci->NL.RNR = 1;
			channel_x_off(plci, ch, 0);
			break;
		}

		APPLptr->DataNCCI[Num] = NCCIcode;
		APPLptr->DataFlags[Num] = (plci->Id << 8) | (plci->NL.Ind >> 4);
		dbug(3, dprintf("Buffer(%d), Max = %d", Num, APPLptr->MaxBuffer));

		plci->RNum = Num;
		plci->RFlags = plci->NL.Ind >> 4;
		plci->RData[0].PLength = APPLptr->MaxDataLength;
		plci->NL.R = plci->RData;
		if ((plci->NL.RLength != 0)
		    && ((plci->B2_prot == B2_V120_ASYNC)
			|| (plci->B2_prot == B2_V120_ASYNC_V42BIS)
			|| (plci->B2_prot == B2_V120_BIT_TRANSPARENT)))
		{
			plci->RData[1].P = plci->RData[0].P;
			plci->RData[1].PLength = plci->RData[0].PLength;
			plci->RData[0].P = v120_header_buffer + (-((unsigned long)v120_header_buffer) & 3);
			if ((plci->NL.RBuffer->P[0] & V120_HEADER_EXTEND_BIT) || (plci->NL.RLength == 1))
				plci->RData[0].PLength = 1;
			else
				plci->RData[0].PLength = 2;
			if (plci->NL.RBuffer->P[0] & V120_HEADER_BREAK_BIT)
				plci->RFlags |= 0x0010;
			if (plci->NL.RBuffer->P[0] & (V120_HEADER_C1_BIT | V120_HEADER_C2_BIT))
				plci->RFlags |= 0x8000;
			plci->NL.RNum = 2;
		}
		else
		{
			if ((plci->NL.Ind & 0x0f) == N_UDATA)
				plci->RFlags |= 0x0010;

			else if ((plci->B3_prot == B3_RTP) && ((plci->NL.Ind & 0x0f) == N_BDATA))
				plci->RFlags |= 0x0001;

			plci->NL.RNum = 1;
		}
		break;
	case N_DATA_ACK:
		data_ack(plci, ch);
		break;
	default:
		plci->NL.RNR = 2;
		break;
	}
}

/*------------------------------------------------------------------*/
/* find a free PLCI */
/*------------------------------------------------------------------*/

static word get_plci(DIVA_CAPI_ADAPTER *a)
{
	word i, j;
	PLCI *plci;

	dump_plcis(a);
	for (i = 0; i < a->max_plci && a->plci[i].Id; i++);
	if (i == a->max_plci) {
		dbug(1, dprintf("get_plci: out of PLCIs"));
		return 0;
	}
	plci = &a->plci[i];
	plci->Id = (byte)(i + 1);

	plci->Sig.Id = 0;
	plci->NL.Id = 0;
	plci->sig_req = 0;
	plci->nl_req = 0;

	plci->appl = NULL;
	plci->relatedPTYPLCI = NULL;
	plci->State = IDLE;
	plci->SuppState = IDLE;
	plci->channels = 0;
	plci->tel = 0;
	plci->B1_resource = 0;
	plci->B2_prot = 0;
	plci->B3_prot = 0;

	plci->command = 0;
	plci->m_command = 0;
	init_internal_command_queue(plci);
	plci->number = 0;
	plci->req_in_start = 0;
	plci->req_in = 0;
	plci->req_out = 0;
	plci->msg_in_write_pos = MSG_IN_QUEUE_SIZE;
	plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE;
	plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE;

	plci->data_sent = false;
	plci->send_disc = 0;
	plci->sig_global_req = 0;
	plci->sig_remove_id = 0;
	plci->nl_global_req = 0;
	plci->nl_remove_id = 0;
	plci->adv_nl = 0;
	plci->manufacturer = false;
	plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
	plci->spoofed_msg = 0;
	plci->ptyState = 0;
	plci->cr_enquiry = false;
	plci->hangup_flow_ctrl_timer = 0;

	plci->ncci_ring_list = 0;
	for (j = 0; j < MAX_CHANNELS_PER_PLCI; j++) plci->inc_dis_ncci_table[j] = 0;
	clear_c_ind_mask(plci);
	set_group_ind_mask(plci);
	plci->fax_connect_info_length = 0;
	plci->nsf_control_bits = 0;
	plci->ncpi_state = 0x00;
	plci->ncpi_buffer[0] = 0;

	plci->requested_options_conn = 0;
	plci->requested_options = 0;
	plci->notifiedcall = 0;
	plci->vswitchstate = 0;
	plci->vsprot = 0;
	plci->vsprotdialect = 0;
	init_b1_config(plci);
	dbug(1, dprintf("get_plci(%x)", plci->Id));
	return i + 1;
}

/*------------------------------------------------------------------*/
/* put a parameter in the parameter buffer                          */
/*------------------------------------------------------------------*/

static void add_p(PLCI *plci, byte code, byte *p)
{
	word p_length;

	p_length = 0;
	if (p) p_length = p[0];
	add_ie(plci, code, p, p_length);
}

/*------------------------------------------------------------------*/
/* put a structure in the parameter buffer                          */
/*------------------------------------------------------------------*/
static void add_s(PLCI *plci, byte code, API_PARSE *p)
{
	if (p) add_ie(plci, code, p->info, (word)p->length);
}

/*------------------------------------------------------------------*/
/* put multiple structures in the parameter buffer                  */
/*------------------------------------------------------------------*/
static void add_ss(PLCI *plci, byte code, API_PARSE *p)
{
	byte i;

	if (p) {
		dbug(1, dprintf("add_ss(%x,len=%d)", code, p->length));
		for (i = 2; i < (byte)p->length; i += p->info[i] + 2) {
			dbug(1, dprintf("add_ss_ie(%x,len=%d)", p->info[i - 1], p->info[i]));
			add_ie(plci, p->info[i - 1], (byte *)&(p->info[i]), (word)p->info[i]);
		}
	}
}

/*------------------------------------------------------------------*/
/* return the channel number sent by the application in a esc_chi   */
/*------------------------------------------------------------------*/
static byte getChannel(API_PARSE *p)
{
	byte i;

	if (p) {
		for (i = 2; i < (byte)p->length; i += p->info[i] + 2) {
			if (p->info[i] == 2) {
				if (p->info[i - 1] == ESC && p->info[i + 1] == CHI) return (p->info[i + 2]);
			}
		}
	}
	return 0;
}


/*------------------------------------------------------------------*/
/* put an information element in the parameter buffer               */
/*------------------------------------------------------------------*/

static void add_ie(PLCI *plci, byte code, byte *p, word p_length)
{
	word i;

	if (!(code & 0x80) && !p_length) return;

	if (plci->req_in == plci->req_in_start) {
		plci->req_in += 2;
	}
	else {
		plci->req_in--;
	}
	plci->RBuffer[plci->req_in++] = code;

	if (p) {
		plci->RBuffer[plci->req_in++] = (byte)p_length;
		for (i = 0; i < p_length; i++) plci->RBuffer[plci->req_in++] = p[1 + i];
	}

	plci->RBuffer[plci->req_in++] = 0;
}

/*------------------------------------------------------------------*/
/* put a unstructured data into the buffer                          */
/*------------------------------------------------------------------*/

static void add_d(PLCI *plci, word length, byte *p)
{
	word i;

	if (plci->req_in == plci->req_in_start) {
		plci->req_in += 2;
	}
	else {
		plci->req_in--;
	}
	for (i = 0; i < length; i++) plci->RBuffer[plci->req_in++] = p[i];
}

/*------------------------------------------------------------------*/
/* put parameters from the Additional Info parameter in the         */
/* parameter buffer                                                 */
/*------------------------------------------------------------------*/

static void add_ai(PLCI *plci, API_PARSE *ai)
{
	word i;
	API_PARSE ai_parms[5];

	for (i = 0; i < 5; i++) ai_parms[i].length = 0;

	if (!ai->length)
		return;
	if (api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms))
		return;

	add_s(plci, KEY, &ai_parms[1]);
	add_s(plci, UUI, &ai_parms[2]);
	add_ss(plci, FTY, &ai_parms[3]);
}

/*------------------------------------------------------------------*/
/* put parameter for b1 protocol in the parameter buffer            */
/*------------------------------------------------------------------*/

static word add_b1(PLCI *plci, API_PARSE *bp, word b_channel_info,
		   word b1_facilities)
{
	API_PARSE bp_parms[8];
	API_PARSE mdm_cfg[9];
	API_PARSE global_config[2];
	byte cai[256];
	byte resource[] = {5, 9, 13, 12, 16, 39, 9, 17, 17, 18};
	byte voice_cai[] = "\x06\x14\x00\x00\x00\x00\x08";
	word i;

	API_PARSE mdm_cfg_v18[4];
	word j, n, w;
	dword d;


	for (i = 0; i < 8; i++) bp_parms[i].length = 0;
	for (i = 0; i < 2; i++) global_config[i].length = 0;

	dbug(1, dprintf("add_b1"));
	api_save_msg(bp, "s", &plci->B_protocol);

	if (b_channel_info == 2) {
		plci->B1_resource = 0;
		adjust_b1_facilities(plci, plci->B1_resource, b1_facilities);
		add_p(plci, CAI, "\x01\x00");
		dbug(1, dprintf("Cai=1,0 (no resource)"));
		return 0;
	}

	if (plci->tel == CODEC_PERMANENT) return 0;
	else if (plci->tel == CODEC) {
		plci->B1_resource = 1;
		adjust_b1_facilities(plci, plci->B1_resource, b1_facilities);
		add_p(plci, CAI, "\x01\x01");
		dbug(1, dprintf("Cai=1,1 (Codec)"));
		return 0;
	}
	else if (plci->tel == ADV_VOICE) {
		plci->B1_resource = add_b1_facilities(plci, 9, (word)(b1_facilities | B1_FACILITY_VOICE));
		adjust_b1_facilities(plci, plci->B1_resource, (word)(b1_facilities | B1_FACILITY_VOICE));
		voice_cai[1] = plci->B1_resource;
		PUT_WORD(&voice_cai[5], plci->appl->MaxDataLength);
		add_p(plci, CAI, voice_cai);
		dbug(1, dprintf("Cai=1,0x%x (AdvVoice)", voice_cai[1]));
		return 0;
	}
	plci->call_dir &= ~(CALL_DIR_ORIGINATE | CALL_DIR_ANSWER);
	if (plci->call_dir & CALL_DIR_OUT)
		plci->call_dir |= CALL_DIR_ORIGINATE;
	else if (plci->call_dir & CALL_DIR_IN)
		plci->call_dir |= CALL_DIR_ANSWER;

	if (!bp->length) {
		plci->B1_resource = 0x5;
		adjust_b1_facilities(plci, plci->B1_resource, b1_facilities);
		add_p(plci, CAI, "\x01\x05");
		return 0;
	}

	dbug(1, dprintf("b_prot_len=%d", (word)bp->length));
	if (bp->length > 256) return _WRONG_MESSAGE_FORMAT;
	if (api_parse(&bp->info[1], (word)bp->length, "wwwsssb", bp_parms))
	{
		bp_parms[6].length = 0;
		if (api_parse(&bp->info[1], (word)bp->length, "wwwsss", bp_parms))
		{
			dbug(1, dprintf("b-form.!"));
			return _WRONG_MESSAGE_FORMAT;
		}
	}
	else if (api_parse(&bp->info[1], (word)bp->length, "wwwssss", bp_parms))
	{
		dbug(1, dprintf("b-form.!"));
		return _WRONG_MESSAGE_FORMAT;
	}

	if (bp_parms[6].length)
	{
		if (api_parse(&bp_parms[6].info[1], (word)bp_parms[6].length, "w", global_config))
		{
			return _WRONG_MESSAGE_FORMAT;
		}
		switch (GET_WORD(global_config[0].info))
		{
		case 1:
			plci->call_dir = (plci->call_dir & ~CALL_DIR_ANSWER) | CALL_DIR_ORIGINATE;
			break;
		case 2:
			plci->call_dir = (plci->call_dir & ~CALL_DIR_ORIGINATE) | CALL_DIR_ANSWER;
			break;
		}
	}
	dbug(1, dprintf("call_dir=%04x", plci->call_dir));


	if ((GET_WORD(bp_parms[0].info) == B1_RTP)
	    && (plci->adapter->man_profile.private_options & (1L << PRIVATE_RTP)))
	{
		plci->B1_resource = add_b1_facilities(plci, 31, (word)(b1_facilities & ~B1_FACILITY_VOICE));
		adjust_b1_facilities(plci, plci->B1_resource, (word)(b1_facilities & ~B1_FACILITY_VOICE));
		cai[1] = plci->B1_resource;
		cai[2] = 0;
		cai[3] = 0;
		cai[4] = 0;
		PUT_WORD(&cai[5], plci->appl->MaxDataLength);
		for (i = 0; i < bp_parms[3].length; i++)
			cai[7 + i] = bp_parms[3].info[1 + i];
		cai[0] = 6 + bp_parms[3].length;
		add_p(plci, CAI, cai);
		return 0;
	}


	if ((GET_WORD(bp_parms[0].info) == B1_PIAFS)
	    && (plci->adapter->man_profile.private_options & (1L << PRIVATE_PIAFS)))
	{
		plci->B1_resource = add_b1_facilities(plci, 35/* PIAFS HARDWARE FACILITY */, (word)(b1_facilities & ~B1_FACILITY_VOICE));
		adjust_b1_facilities(plci, plci->B1_resource, (word)(b1_facilities & ~B1_FACILITY_VOICE));
		cai[1] = plci->B1_resource;
		cai[2] = 0;
		cai[3] = 0;
		cai[4] = 0;
		PUT_WORD(&cai[5], plci->appl->MaxDataLength);
		cai[0] = 6;
		add_p(plci, CAI, cai);
		return 0;
	}


	if ((GET_WORD(bp_parms[0].info) >= 32)
	    || (!((1L << GET_WORD(bp_parms[0].info)) & plci->adapter->profile.B1_Protocols)
		&& ((GET_WORD(bp_parms[0].info) != 3)
		    || !((1L << B1_HDLC) & plci->adapter->profile.B1_Protocols)
		    || ((bp_parms[3].length != 0) && (GET_WORD(&bp_parms[3].info[1]) != 0) && (GET_WORD(&bp_parms[3].info[1]) != 56000)))))
	{
		return _B1_NOT_SUPPORTED;
	}
	plci->B1_resource = add_b1_facilities(plci, resource[GET_WORD(bp_parms[0].info)],
					      (word)(b1_facilities & ~B1_FACILITY_VOICE));
	adjust_b1_facilities(plci, plci->B1_resource, (word)(b1_facilities & ~B1_FACILITY_VOICE));
	cai[0] = 6;
	cai[1] = plci->B1_resource;
	for (i = 2; i < sizeof(cai); i++) cai[i] = 0;

	if ((GET_WORD(bp_parms[0].info) == B1_MODEM_ALL_NEGOTIATE)
	    || (GET_WORD(bp_parms[0].info) == B1_MODEM_ASYNC)
	    || (GET_WORD(bp_parms[0].info) == B1_MODEM_SYNC_HDLC))
	{ /* B1 - modem */
		for (i = 0; i < 7; i++) mdm_cfg[i].length = 0;

		if (bp_parms[3].length)
		{
			if (api_parse(&bp_parms[3].info[1], (word)bp_parms[3].length, "wwwwww", mdm_cfg))
			{
				return (_WRONG_MESSAGE_FORMAT);
			}

			cai[2] = 0; /* Bit rate for adaptation */

			dbug(1, dprintf("MDM Max Bit Rate:<%d>", GET_WORD(mdm_cfg[0].info)));

			PUT_WORD(&cai[13], 0);                          /* Min Tx speed */
			PUT_WORD(&cai[15], GET_WORD(mdm_cfg[0].info)); /* Max Tx speed */
			PUT_WORD(&cai[17], 0);                          /* Min Rx speed */
			PUT_WORD(&cai[19], GET_WORD(mdm_cfg[0].info)); /* Max Rx speed */

			cai[3] = 0; /* Async framing parameters */
			switch (GET_WORD(mdm_cfg[2].info))
			{       /* Parity     */
			case 1: /* odd parity */
				cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_ODD);
				dbug(1, dprintf("MDM: odd parity"));
				break;

			case 2: /* even parity */
				cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_EVEN);
				dbug(1, dprintf("MDM: even parity"));
				break;

			default:
				dbug(1, dprintf("MDM: no parity"));
				break;
			}

			switch (GET_WORD(mdm_cfg[3].info))
			{       /* stop bits   */
			case 1: /* 2 stop bits */
				cai[3] |= DSP_CAI_ASYNC_TWO_STOP_BITS;
				dbug(1, dprintf("MDM: 2 stop bits"));
				break;

			default:
				dbug(1, dprintf("MDM: 1 stop bit"));
				break;
			}

			switch (GET_WORD(mdm_cfg[1].info))
			{     /* char length */
			case 5:
				cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_5;
				dbug(1, dprintf("MDM: 5 bits"));
				break;

			case 6:
				cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_6;
				dbug(1, dprintf("MDM: 6 bits"));
				break;

			case 7:
				cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_7;
				dbug(1, dprintf("MDM: 7 bits"));
				break;

			default:
				dbug(1, dprintf("MDM: 8 bits"));
				break;
			}

			cai[7] = 0; /* Line taking options */
			cai[8] = 0; /* Modulation negotiation options */
			cai[9] = 0; /* Modulation options */

			if (((plci->call_dir & CALL_DIR_ORIGINATE) != 0) ^ ((plci->call_dir & CALL_DIR_OUT) != 0))
			{
				cai[9] |= DSP_CAI_MODEM_REVERSE_DIRECTION;
				dbug(1, dprintf("MDM: Reverse direction"));
			}

			if (GET_WORD(mdm_cfg[4].info) & MDM_CAPI_DISABLE_RETRAIN)
			{
				cai[9] |= DSP_CAI_MODEM_DISABLE_RETRAIN;
				dbug(1, dprintf("MDM: Disable retrain"));
			}

			if (GET_WORD(mdm_cfg[4].info) & MDM_CAPI_DISABLE_RING_TONE)
			{
				cai[7] |= DSP_CAI_MODEM_DISABLE_CALLING_TONE | DSP_CAI_MODEM_DISABLE_ANSWER_TONE;
				dbug(1, dprintf("MDM: Disable ring tone"));
			}

			if (GET_WORD(mdm_cfg[4].info) & MDM_CAPI_GUARD_1800)
			{
				cai[8] |= DSP_CAI_MODEM_GUARD_TONE_1800HZ;
				dbug(1, dprintf("MDM: 1800 guard tone"));
			}
			else if (GET_WORD(mdm_cfg[4].info) & MDM_CAPI_GUARD_550)
			{
				cai[8] |= DSP_CAI_MODEM_GUARD_TONE_550HZ;
				dbug(1, dprintf("MDM: 550 guard tone"));
			}

			if ((GET_WORD(mdm_cfg[5].info) & 0x00ff) == MDM_CAPI_NEG_V100)
			{
				cai[8] |= DSP_CAI_MODEM_NEGOTIATE_V100;
				dbug(1, dprintf("MDM: V100"));
			}
			else if ((GET_WORD(mdm_cfg[5].info) & 0x00ff) == MDM_CAPI_NEG_MOD_CLASS)
			{
				cai[8] |= DSP_CAI_MODEM_NEGOTIATE_IN_CLASS;
				dbug(1, dprintf("MDM: IN CLASS"));
			}
			else if ((GET_WORD(mdm_cfg[5].info) & 0x00ff) == MDM_CAPI_NEG_DISABLED)
			{
				cai[8] |= DSP_CAI_MODEM_NEGOTIATE_DISABLED;
				dbug(1, dprintf("MDM: DISABLED"));
			}
			cai[0] = 20;

			if ((plci->adapter->man_profile.private_options & (1L << PRIVATE_V18))
			    && (GET_WORD(mdm_cfg[5].info) & 0x8000)) /* Private V.18 enable */
			{
				plci->requested_options |= 1L << PRIVATE_V18;
			}
			if (GET_WORD(mdm_cfg[5].info) & 0x4000) /* Private VOWN enable */
				plci->requested_options |= 1L << PRIVATE_VOWN;

			if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1])
			    & ((1L << PRIVATE_V18) | (1L << PRIVATE_VOWN)))
			{
				if (!api_parse(&bp_parms[3].info[1], (word)bp_parms[3].length, "wwwwwws", mdm_cfg))
				{
					i = 27;
					if (mdm_cfg[6].length >= 4)
					{
						d = GET_DWORD(&mdm_cfg[6].info[1]);
						cai[7] |= (byte) d;          /* line taking options */
						cai[9] |= (byte)(d >> 8);    /* modulation options */
						cai[++i] = (byte)(d >> 16);  /* vown modulation options */
						cai[++i] = (byte)(d >> 24);
						if (mdm_cfg[6].length >= 8)
						{
							d = GET_DWORD(&mdm_cfg[6].info[5]);
							cai[10] |= (byte) d;        /* disabled modulations mask */
							cai[11] |= (byte)(d >> 8);
							if (mdm_cfg[6].length >= 12)
							{
								d = GET_DWORD(&mdm_cfg[6].info[9]);
								cai[12] = (byte) d;          /* enabled modulations mask */
								cai[++i] = (byte)(d >> 8);   /* vown enabled modulations */
								cai[++i] = (byte)(d >> 16);
								cai[++i] = (byte)(d >> 24);
								cai[++i] = 0;
								if (mdm_cfg[6].length >= 14)
								{
									w = GET_WORD(&mdm_cfg[6].info[13]);
									if (w != 0)
										PUT_WORD(&cai[13], w);  /* min tx speed */
									if (mdm_cfg[6].length >= 16)
									{
										w = GET_WORD(&mdm_cfg[6].info[15]);
										if (w != 0)
											PUT_WORD(&cai[15], w);  /* max tx speed */
										if (mdm_cfg[6].length >= 18)
										{
											w = GET_WORD(&mdm_cfg[6].info[17]);
											if (w != 0)
												PUT_WORD(&cai[17], w);  /* min rx speed */
											if (mdm_cfg[6].length >= 20)
											{
												w = GET_WORD(&mdm_cfg[6].info[19]);
												if (w != 0)
													PUT_WORD(&cai[19], w);  /* max rx speed */
												if (mdm_cfg[6].length >= 22)
												{
													w = GET_WORD(&mdm_cfg[6].info[21]);
													cai[23] = (byte)(-((short) w));  /* transmit level */
													if (mdm_cfg[6].length >= 24)
													{
														w = GET_WORD(&mdm_cfg[6].info[23]);
														cai[22] |= (byte) w;        /* info options mask */
														cai[21] |= (byte)(w >> 8);  /* disabled symbol rates */
													}
												}
											}
										}
									}
								}
							}
						}
					}
					cai[27] = i - 27;
					i++;
					if (!api_parse(&bp_parms[3].info[1], (word)bp_parms[3].length, "wwwwwwss", mdm_cfg))
					{
						if (!api_parse(&mdm_cfg[7].info[1], (word)mdm_cfg[7].length, "sss", mdm_cfg_v18))
						{
							for (n = 0; n < 3; n++)
							{
								cai[i] = (byte)(mdm_cfg_v18[n].length);
								for (j = 1; j < ((word)(cai[i] + 1)); j++)
									cai[i + j] = mdm_cfg_v18[n].info[j];
								i += cai[i] + 1;
							}
						}
					}
					cai[0] = (byte)(i - 1);
				}
			}

		}
	}
	if (GET_WORD(bp_parms[0].info) == 2 ||                         /* V.110 async */
	    GET_WORD(bp_parms[0].info) == 3)                           /* V.110 sync */
	{
		if (bp_parms[3].length) {
			dbug(1, dprintf("V.110,%d", GET_WORD(&bp_parms[3].info[1])));
			switch (GET_WORD(&bp_parms[3].info[1])) {                 /* Rate */
			case 0:
			case 56000:
				if (GET_WORD(bp_parms[0].info) == 3) {                  /* V.110 sync 56k */
					dbug(1, dprintf("56k sync HSCX"));
					cai[1] = 8;
					cai[2] = 0;
					cai[3] = 0;
				}
				else if (GET_WORD(bp_parms[0].info) == 2) {
					dbug(1, dprintf("56k async DSP"));
					cai[2] = 9;
				}
				break;
			case 50:     cai[2] = 1;  break;
			case 75:     cai[2] = 1;  break;
			case 110:    cai[2] = 1;  break;
			case 150:    cai[2] = 1;  break;
			case 200:    cai[2] = 1;  break;
			case 300:    cai[2] = 1;  break;
			case 600:    cai[2] = 1;  break;
			case 1200:   cai[2] = 2;  break;
			case 2400:   cai[2] = 3;  break;
			case 4800:   cai[2] = 4;  break;
			case 7200:   cai[2] = 10; break;
			case 9600:   cai[2] = 5;  break;
			case 12000:  cai[2] = 13; break;
			case 24000:  cai[2] = 0;  break;
			case 14400:  cai[2] = 11; break;
			case 19200:  cai[2] = 6;  break;
			case 28800:  cai[2] = 12; break;
			case 38400:  cai[2] = 7;  break;
			case 48000:  cai[2] = 8;  break;
			case 76:     cai[2] = 15; break;  /* 75/1200     */
			case 1201:   cai[2] = 14; break;  /* 1200/75     */
			case 56001:  cai[2] = 9;  break;  /* V.110 56000 */

			default:
				return _B1_PARM_NOT_SUPPORTED;
			}
			cai[3] = 0;
			if (cai[1] == 13)                                        /* v.110 async */
			{
				if (bp_parms[3].length >= 8)
				{
					switch (GET_WORD(&bp_parms[3].info[3]))
					{       /* char length */
					case 5:
						cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_5;
						break;
					case 6:
						cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_6;
						break;
					case 7:
						cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_7;
						break;
					}
					switch (GET_WORD(&bp_parms[3].info[5]))
					{       /* Parity     */
					case 1: /* odd parity */
						cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_ODD);
						break;
					case 2: /* even parity */
						cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_EVEN);
						break;
					}
					switch (GET_WORD(&bp_parms[3].info[7]))
					{       /* stop bits   */
					case 1: /* 2 stop bits */
						cai[3] |= DSP_CAI_ASYNC_TWO_STOP_BITS;
						break;
					}
				}
			}
		}
		else if (cai[1] == 8 || GET_WORD(bp_parms[0].info) == 3) {
			dbug(1, dprintf("V.110 default 56k sync"));
			cai[1] = 8;
			cai[2] = 0;
			cai[3] = 0;
		}
		else {
			dbug(1, dprintf("V.110 default 9600 async"));
			cai[2] = 5;
		}
	}
	PUT_WORD(&cai[5], plci->appl->MaxDataLength);
	dbug(1, dprintf("CAI[%d]=%x,%x,%x,%x,%x,%x", cai[0], cai[1], cai[2], cai[3], cai[4], cai[5], cai[6]));
/* HexDump ("CAI", sizeof(cai), &cai[0]); */

	add_p(plci, CAI, cai);
	return 0;
}

/*------------------------------------------------------------------*/
/* put parameter for b2 and B3  protocol in the parameter buffer    */
/*------------------------------------------------------------------*/

static word add_b23(PLCI *plci, API_PARSE *bp)
{
	word i, fax_control_bits;
	byte pos, len;
	byte SAPI = 0x40;  /* default SAPI 16 for x.31 */
	API_PARSE bp_parms[8];
	API_PARSE *b1_config;
	API_PARSE *b2_config;
	API_PARSE b2_config_parms[8];
	API_PARSE *b3_config;
	API_PARSE b3_config_parms[6];
	API_PARSE global_config[2];

	static byte llc[3] = {2,0,0};
	static byte dlc[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	static byte nlc[256];
	static byte lli[12] = {1,1};

	const byte llc2_out[] = {1,2,4,6,2,0,0,0, X75_V42BIS,V120_L2,V120_V42BIS,V120_L2,6};
	const byte llc2_in[]  = {1,3,4,6,3,0,0,0, X75_V42BIS,V120_L2,V120_V42BIS,V120_L2,6};

	const byte llc3[] = {4,3,2,2,6,6,0};
	const byte header[] = {0,2,3,3,0,0,0};

	for (i = 0; i < 8; i++) bp_parms[i].length = 0;
	for (i = 0; i < 6; i++) b2_config_parms[i].length = 0;
	for (i = 0; i < 5; i++) b3_config_parms[i].length = 0;

	lli[0] = 1;
	lli[1] = 1;
	if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL)
		lli[1] |= 2;
	if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_OOB_CHANNEL)
		lli[1] |= 4;

	if ((lli[1] & 0x02) && (diva_xdi_extended_features & DIVA_CAPI_USE_CMA)) {
		lli[1] |= 0x10;
		if (plci->rx_dma_descriptor <= 0) {
			plci->rx_dma_descriptor = diva_get_dma_descriptor(plci, &plci->rx_dma_magic);
			if (plci->rx_dma_descriptor >= 0)
				plci->rx_dma_descriptor++;
		}
		if (plci->rx_dma_descriptor > 0) {
			lli[0] = 6;
			lli[1] |= 0x40;
			lli[2] = (byte)(plci->rx_dma_descriptor - 1);
			lli[3] = (byte)plci->rx_dma_magic;
			lli[4] = (byte)(plci->rx_dma_magic >>  8);
			lli[5] = (byte)(plci->rx_dma_magic >> 16);
			lli[6] = (byte)(plci->rx_dma_magic >> 24);
		}
	}

	if (DIVA_CAPI_SUPPORTS_NO_CANCEL(plci->adapter)) {
		lli[1] |= 0x20;
	}

	dbug(1, dprintf("add_b23"));
	api_save_msg(bp, "s", &plci->B_protocol);

	if (!bp->length && plci->tel)
	{
		plci->adv_nl = true;
		dbug(1, dprintf("Default adv.Nl"));
		add_p(plci, LLI, lli);
		plci->B2_prot = 1 /*XPARENT*/;
		plci->B3_prot = 0 /*XPARENT*/;
		llc[1] = 2;
		llc[2] = 4;
		add_p(plci, LLC, llc);
		dlc[0] = 2;
		PUT_WORD(&dlc[1], plci->appl->MaxDataLength);
		add_p(plci, DLC, dlc);
		return 0;
	}

	if (!bp->length) /*default*/
	{
		dbug(1, dprintf("ret default"));
		add_p(plci, LLI, lli);
		plci->B2_prot = 0 /*X.75   */;
		plci->B3_prot = 0 /*XPARENT*/;
		llc[1] = 1;
		llc[2] = 4;
		add_p(plci, LLC, llc);
		dlc[0] = 2;
		PUT_WORD(&dlc[1], plci->appl->MaxDataLength);
		add_p(plci, DLC, dlc);
		return 0;
	}
	dbug(1, dprintf("b_prot_len=%d", (word)bp->length));
	if ((word)bp->length > 256)    return _WRONG_MESSAGE_FORMAT;

	if (api_parse(&bp->info[1], (word)bp->length, "wwwsssb", bp_parms))
	{
		bp_parms[6].length = 0;
		if (api_parse(&bp->info[1], (word)bp->length, "wwwsss", bp_parms))
		{
			dbug(1, dprintf("b-form.!"));
			return _WRONG_MESSAGE_FORMAT;
		}
	}
	else if (api_parse(&bp->info[1], (word)bp->length, "wwwssss", bp_parms))
	{
		dbug(1, dprintf("b-form.!"));
		return _WRONG_MESSAGE_FORMAT;
	}

	if (plci->tel == ADV_VOICE) /* transparent B on advanced voice */
	{
		if (GET_WORD(bp_parms[1].info) != 1
		    || GET_WORD(bp_parms[2].info) != 0) return _B2_NOT_SUPPORTED;
		plci->adv_nl = true;
	}
	else if (plci->tel) return _B2_NOT_SUPPORTED;


	if ((GET_WORD(bp_parms[1].info) == B2_RTP)
	    && (GET_WORD(bp_parms[2].info) == B3_RTP)
	    && (plci->adapter->man_profile.private_options & (1L << PRIVATE_RTP)))
	{
		add_p(plci, LLI, lli);
		plci->B2_prot = (byte) GET_WORD(bp_parms[1].info);
		plci->B3_prot = (byte) GET_WORD(bp_parms[2].info);
		llc[1] = (plci->call_dir & (CALL_DIR_ORIGINATE | CALL_DIR_FORCE_OUTG_NL)) ? 14 : 13;
		llc[2] = 4;
		add_p(plci, LLC, llc);
		dlc[0] = 2;
		PUT_WORD(&dlc[1], plci->appl->MaxDataLength);
		dlc[3] = 3; /* Addr A */
		dlc[4] = 1; /* Addr B */
		dlc[5] = 7; /* modulo mode */
		dlc[6] = 7; /* window size */
		dlc[7] = 0; /* XID len Lo  */
		dlc[8] = 0; /* XID len Hi  */
		for (i = 0; i < bp_parms[4].length; i++)
			dlc[9 + i] = bp_parms[4].info[1 + i];
		dlc[0] = (byte)(8 + bp_parms[4].length);
		add_p(plci, DLC, dlc);
		for (i = 0; i < bp_parms[5].length; i++)
			nlc[1 + i] = bp_parms[5].info[1 + i];
		nlc[0] = (byte)(bp_parms[5].length);
		add_p(plci, NLC, nlc);
		return 0;
	}



	if ((GET_WORD(bp_parms[1].info) >= 32)
	    || (!((1L << GET_WORD(bp_parms[1].info)) & plci->adapter->profile.B2_Protocols)
		&& ((GET_WORD(bp_parms[1].info) != B2_PIAFS)
		    || !(plci->adapter->man_profile.private_options & (1L << PRIVATE_PIAFS)))))

	{
		return _B2_NOT_SUPPORTED;
	}
	if ((GET_WORD(bp_parms[2].info) >= 32)
	    || !((1L << GET_WORD(bp_parms[2].info)) & plci->adapter->profile.B3_Protocols))
	{
		return _B3_NOT_SUPPORTED;
	}
	if ((GET_WORD(bp_parms[1].info) != B2_SDLC)
	    && ((GET_WORD(bp_parms[0].info) == B1_MODEM_ALL_NEGOTIATE)
		|| (GET_WORD(bp_parms[0].info) == B1_MODEM_ASYNC)
		|| (GET_WORD(bp_parms[0].info) == B1_MODEM_SYNC_HDLC)))
	{
		return (add_modem_b23(plci, bp_parms));
	}

	add_p(plci, LLI, lli);

	plci->B2_prot = (byte)GET_WORD(bp_parms[1].info);
	plci->B3_prot = (byte)GET_WORD(bp_parms[2].info);
	if (plci->B2_prot == 12) SAPI = 0; /* default SAPI D-channel */

	if (bp_parms[6].length)
	{
		if (api_parse(&bp_parms[6].info[1], (word)bp_parms[6].length, "w", global_config))
		{
			return _WRONG_MESSAGE_FORMAT;
		}
		switch (GET_WORD(global_config[0].info))
		{
		case 1:
			plci->call_dir = (plci->call_dir & ~CALL_DIR_ANSWER) | CALL_DIR_ORIGINATE;
			break;
		case 2:
			plci->call_dir = (plci->call_dir & ~CALL_DIR_ORIGINATE) | CALL_DIR_ANSWER;
			break;
		}
	}
	dbug(1, dprintf("call_dir=%04x", plci->call_dir));


	if (plci->B2_prot == B2_PIAFS)
		llc[1] = PIAFS_CRC;
	else
/* IMPLEMENT_PIAFS */
	{
		llc[1] = (plci->call_dir & (CALL_DIR_ORIGINATE | CALL_DIR_FORCE_OUTG_NL)) ?
			llc2_out[GET_WORD(bp_parms[1].info)] : llc2_in[GET_WORD(bp_parms[1].info)];
	}
	llc[2] = llc3[GET_WORD(bp_parms[2].info)];

	add_p(plci, LLC, llc);

	dlc[0] = 2;
	PUT_WORD(&dlc[1], plci->appl->MaxDataLength +
		 header[GET_WORD(bp_parms[2].info)]);

	b1_config = &bp_parms[3];
	nlc[0] = 0;
	if (plci->B3_prot == 4
	    || plci->B3_prot == 5)
	{
		for (i = 0; i < sizeof(T30_INFO); i++) nlc[i] = 0;
		nlc[0] = sizeof(T30_INFO);
		if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
			((T30_INFO *)&nlc[1])->operating_mode = T30_OPERATING_MODE_CAPI;
		((T30_INFO *)&nlc[1])->rate_div_2400 = 0xff;
		if (b1_config->length >= 2)
		{
			((T30_INFO *)&nlc[1])->rate_div_2400 = (byte)(GET_WORD(&b1_config->info[1]) / 2400);
		}
	}
	b2_config = &bp_parms[4];


	if (llc[1] == PIAFS_CRC)
	{
		if (plci->B3_prot != B3_TRANSPARENT)
		{
			return _B_STACK_NOT_SUPPORTED;
		}
		if (b2_config->length && api_parse(&b2_config->info[1], (word)b2_config->length, "bwww", b2_config_parms)) {
			return _WRONG_MESSAGE_FORMAT;
		}
		PUT_WORD(&dlc[1], plci->appl->MaxDataLength);
		dlc[3] = 0; /* Addr A */
		dlc[4] = 0; /* Addr B */
		dlc[5] = 0; /* modulo mode */
		dlc[6] = 0; /* window size */
		if (b2_config->length >= 7) {
			dlc[7] = 7;
			dlc[8] = 0;
			dlc[9] = b2_config_parms[0].info[0]; /* PIAFS protocol Speed configuration */
			dlc[10] = b2_config_parms[1].info[0]; /* V.42bis P0 */
			dlc[11] = b2_config_parms[1].info[1]; /* V.42bis P0 */
			dlc[12] = b2_config_parms[2].info[0]; /* V.42bis P1 */
			dlc[13] = b2_config_parms[2].info[1]; /* V.42bis P1 */
			dlc[14] = b2_config_parms[3].info[0]; /* V.42bis P2 */
			dlc[15] = b2_config_parms[3].info[1]; /* V.42bis P2 */
			dlc[0] = 15;
			if (b2_config->length >= 8) { /* PIAFS control abilities */
				dlc[7] = 10;
				dlc[16] = 2; /* Length of PIAFS extension */
				dlc[17] = PIAFS_UDATA_ABILITIES; /* control (UDATA) ability */
				dlc[18] = b2_config_parms[4].info[0]; /* value */
				dlc[0] = 18;
			}
		}
		else /* default values, 64K, variable, no compression */
		{
			dlc[7] = 7;
			dlc[8] = 0;
			dlc[9] = 0x03; /* PIAFS protocol Speed configuration */
			dlc[10] = 0x03; /* V.42bis P0 */
			dlc[11] = 0;    /* V.42bis P0 */
			dlc[12] = 0;    /* V.42bis P1 */
			dlc[13] = 0;    /* V.42bis P1 */
			dlc[14] = 0;    /* V.42bis P2 */
			dlc[15] = 0;    /* V.42bis P2 */
			dlc[0] = 15;
		}
		add_p(plci, DLC, dlc);
	}
	else

		if ((llc[1] == V120_L2) || (llc[1] == V120_V42BIS))
		{
			if (plci->B3_prot != B3_TRANSPARENT)
				return _B_STACK_NOT_SUPPORTED;

			dlc[0] = 6;
			PUT_WORD(&dlc[1], GET_WORD(&dlc[1]) + 2);
			dlc[3] = 0x08;
			dlc[4] = 0x01;
			dlc[5] = 127;
			dlc[6] = 7;
			if (b2_config->length != 0)
			{
				if ((llc[1] == V120_V42BIS) && api_parse(&b2_config->info[1], (word)b2_config->length, "bbbbwww", b2_config_parms)) {
					return _WRONG_MESSAGE_FORMAT;
				}
				dlc[3] = (byte)((b2_config->info[2] << 3) | ((b2_config->info[1] >> 5) & 0x04));
				dlc[4] = (byte)((b2_config->info[1] << 1) | 0x01);
				if (b2_config->info[3] != 128)
				{
					dbug(1, dprintf("1D-dlc= %x %x %x %x %x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4]));
					return _B2_PARM_NOT_SUPPORTED;
				}
				dlc[5] = (byte)(b2_config->info[3] - 1);
				dlc[6] = b2_config->info[4];
				if (llc[1] == V120_V42BIS) {
					if (b2_config->length >= 10) {
						dlc[7] = 6;
						dlc[8] = 0;
						dlc[9] = b2_config_parms[4].info[0];
						dlc[10] = b2_config_parms[4].info[1];
						dlc[11] = b2_config_parms[5].info[0];
						dlc[12] = b2_config_parms[5].info[1];
						dlc[13] = b2_config_parms[6].info[0];
						dlc[14] = b2_config_parms[6].info[1];
						dlc[0] = 14;
						dbug(1, dprintf("b2_config_parms[4].info[0] [1]:  %x %x", b2_config_parms[4].info[0], b2_config_parms[4].info[1]));
						dbug(1, dprintf("b2_config_parms[5].info[0] [1]:  %x %x", b2_config_parms[5].info[0], b2_config_parms[5].info[1]));
						dbug(1, dprintf("b2_config_parms[6].info[0] [1]:  %x %x", b2_config_parms[6].info[0], b2_config_parms[6].info[1]));
					}
					else {
						dlc[6] = 14;
					}
				}
			}
		}
		else
		{
			if (b2_config->length)
			{
				dbug(1, dprintf("B2-Config"));
				if (llc[1] == X75_V42BIS) {
					if (api_parse(&b2_config->info[1], (word)b2_config->length, "bbbbwww", b2_config_parms))
					{
						return _WRONG_MESSAGE_FORMAT;
					}
				}
				else {
					if (api_parse(&b2_config->info[1], (word)b2_config->length, "bbbbs", b2_config_parms))
					{
						return _WRONG_MESSAGE_FORMAT;
					}
				}
				/* if B2 Protocol is LAPD, b2_config structure is different */
				if (llc[1] == 6)
				{
					dlc[0] = 4;
					if (b2_config->length >= 1) dlc[2] = b2_config->info[1];      /* TEI */
					else dlc[2] = 0x01;
					if ((b2_config->length >= 2) && (plci->B2_prot == 12))
					{
						SAPI = b2_config->info[2];    /* SAPI */
					}
					dlc[1] = SAPI;
					if ((b2_config->length >= 3) && (b2_config->info[3] == 128))
					{
						dlc[3] = 127;      /* Mode */
					}
					else
					{
						dlc[3] = 7;        /* Mode */
					}

					if (b2_config->length >= 4) dlc[4] = b2_config->info[4];      /* Window */
					else dlc[4] = 1;
					dbug(1, dprintf("D-dlc[%d]=%x,%x,%x,%x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4]));
					if (b2_config->length > 5) return _B2_PARM_NOT_SUPPORTED;
				}
				else
				{
					dlc[0] = (byte)(b2_config_parms[4].length + 6);
					dlc[3] = b2_config->info[1];
					dlc[4] = b2_config->info[2];
					if (b2_config->info[3] != 8 && b2_config->info[3] != 128) {
						dbug(1, dprintf("1D-dlc= %x %x %x %x %x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4]));
						return _B2_PARM_NOT_SUPPORTED;
					}

					dlc[5] = (byte)(b2_config->info[3] - 1);
					dlc[6] = b2_config->info[4];
					if (dlc[6] > dlc[5]) {
						dbug(1, dprintf("2D-dlc= %x %x %x %x %x %x %x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4], dlc[5], dlc[6]));
						return _B2_PARM_NOT_SUPPORTED;
					}

					if (llc[1] == X75_V42BIS) {
						if (b2_config->length >= 10) {
							dlc[7] = 6;
							dlc[8] = 0;
							dlc[9] = b2_config_parms[4].info[0];
							dlc[10] = b2_config_parms[4].info[1];
							dlc[11] = b2_config_parms[5].info[0];
							dlc[12] = b2_config_parms[5].info[1];
							dlc[13] = b2_config_parms[6].info[0];
							dlc[14] = b2_config_parms[6].info[1];
							dlc[0] = 14;
							dbug(1, dprintf("b2_config_parms[4].info[0] [1]:  %x %x", b2_config_parms[4].info[0], b2_config_parms[4].info[1]));
							dbug(1, dprintf("b2_config_parms[5].info[0] [1]:  %x %x", b2_config_parms[5].info[0], b2_config_parms[5].info[1]));
							dbug(1, dprintf("b2_config_parms[6].info[0] [1]:  %x %x", b2_config_parms[6].info[0], b2_config_parms[6].info[1]));
						}
						else {
							dlc[6] = 14;
						}

					}
					else {
						PUT_WORD(&dlc[7], (word)b2_config_parms[4].length);
						for (i = 0; i < b2_config_parms[4].length; i++)
							dlc[11 + i] = b2_config_parms[4].info[1 + i];
					}
				}
			}
		}
	add_p(plci, DLC, dlc);

	b3_config = &bp_parms[5];
	if (b3_config->length)
	{
		if (plci->B3_prot == 4
		    || plci->B3_prot == 5)
		{
			if (api_parse(&b3_config->info[1], (word)b3_config->length, "wwss", b3_config_parms))
			{
				return _WRONG_MESSAGE_FORMAT;
			}
			i = GET_WORD((byte *)(b3_config_parms[0].info));
			((T30_INFO *)&nlc[1])->resolution = (byte)(((i & 0x0001) ||
								    ((plci->B3_prot == 4) && (((byte)(GET_WORD((byte *)b3_config_parms[1].info))) != 5))) ? T30_RESOLUTION_R8_0770_OR_200 : 0);
			((T30_INFO *)&nlc[1])->data_format = (byte)(GET_WORD((byte *)b3_config_parms[1].info));
			fax_control_bits = T30_CONTROL_BIT_ALL_FEATURES;
			if ((((T30_INFO *)&nlc[1])->rate_div_2400 != 0) && (((T30_INFO *)&nlc[1])->rate_div_2400 <= 6))
				fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_V34FAX;
			if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
			{

				if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1])
				    & (1L << PRIVATE_FAX_PAPER_FORMATS))
				{
					((T30_INFO *)&nlc[1])->resolution |= T30_RESOLUTION_R8_1540 |
						T30_RESOLUTION_R16_1540_OR_400 | T30_RESOLUTION_300_300 |
						T30_RESOLUTION_INCH_BASED | T30_RESOLUTION_METRIC_BASED;
				}

				((T30_INFO *)&nlc[1])->recording_properties =
					T30_RECORDING_WIDTH_ISO_A3 |
					(T30_RECORDING_LENGTH_UNLIMITED << 2) |
					(T30_MIN_SCANLINE_TIME_00_00_00 << 4);
			}
			if (plci->B3_prot == 5)
			{
				if (i & 0x0002) /* Accept incoming fax-polling requests */
					fax_control_bits |= T30_CONTROL_BIT_ACCEPT_POLLING;
				if (i & 0x2000) /* Do not use MR compression */
					fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_2D_CODING;
				if (i & 0x4000) /* Do not use MMR compression */
					fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_T6_CODING;
				if (i & 0x8000) /* Do not use ECM */
					fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_ECM;
				if (plci->fax_connect_info_length != 0)
				{
					((T30_INFO *)&nlc[1])->resolution = ((T30_INFO *)plci->fax_connect_info_buffer)->resolution;
					((T30_INFO *)&nlc[1])->data_format = ((T30_INFO *)plci->fax_connect_info_buffer)->data_format;
					((T30_INFO *)&nlc[1])->recording_properties = ((T30_INFO *)plci->fax_connect_info_buffer)->recording_properties;
					fax_control_bits |= GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low) &
						(T30_CONTROL_BIT_REQUEST_POLLING | T30_CONTROL_BIT_MORE_DOCUMENTS);
				}
			}
			/* copy station id to NLC */
			for (i = 0; i < T30_MAX_STATION_ID_LENGTH; i++)
			{
				if (i < b3_config_parms[2].length)
				{
					((T30_INFO *)&nlc[1])->station_id[i] = ((byte *)b3_config_parms[2].info)[1 + i];
				}
				else
				{
					((T30_INFO *)&nlc[1])->station_id[i] = ' ';
				}
			}
			((T30_INFO *)&nlc[1])->station_id_len = T30_MAX_STATION_ID_LENGTH;
			/* copy head line to NLC */
			if (b3_config_parms[3].length)
			{

				pos = (byte)(fax_head_line_time(&(((T30_INFO *)&nlc[1])->station_id[T30_MAX_STATION_ID_LENGTH])));
				if (pos != 0)
				{
					if (CAPI_MAX_DATE_TIME_LENGTH + 2 + b3_config_parms[3].length > CAPI_MAX_HEAD_LINE_SPACE)
						pos = 0;
					else
					{
						nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ' ';
						nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ' ';
						len = (byte)b3_config_parms[2].length;
						if (len > 20)
							len = 20;
						if (CAPI_MAX_DATE_TIME_LENGTH + 2 + len + 2 + b3_config_parms[3].length <= CAPI_MAX_HEAD_LINE_SPACE)
						{
							for (i = 0; i < len; i++)
								nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ((byte *)b3_config_parms[2].info)[1 + i];
							nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ' ';
							nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ' ';
						}
					}
				}

				len = (byte)b3_config_parms[3].length;
				if (len > CAPI_MAX_HEAD_LINE_SPACE - pos)
					len = (byte)(CAPI_MAX_HEAD_LINE_SPACE - pos);
				((T30_INFO *)&nlc[1])->head_line_len = (byte)(pos + len);
				nlc[0] += (byte)(pos + len);
				for (i = 0; i < len; i++)
					nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] =  ((byte *)b3_config_parms[3].info)[1 + i];
			} else
				((T30_INFO *)&nlc[1])->head_line_len = 0;

			plci->nsf_control_bits = 0;
			if (plci->B3_prot == 5)
			{
				if ((plci->adapter->man_profile.private_options & (1L << PRIVATE_FAX_SUB_SEP_PWD))
				    && (GET_WORD((byte *)b3_config_parms[1].info) & 0x8000)) /* Private SUB/SEP/PWD enable */
				{
					plci->requested_options |= 1L << PRIVATE_FAX_SUB_SEP_PWD;
				}
				if ((plci->adapter->man_profile.private_options & (1L << PRIVATE_FAX_NONSTANDARD))
				    && (GET_WORD((byte *)b3_config_parms[1].info) & 0x4000)) /* Private non-standard facilities enable */
				{
					plci->requested_options |= 1L << PRIVATE_FAX_NONSTANDARD;
				}
				if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1])
				    & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD)))
				{
					if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1])
					    & (1L << PRIVATE_FAX_SUB_SEP_PWD))
					{
						fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SUBADDRESS | T30_CONTROL_BIT_ACCEPT_PASSWORD;
						if (fax_control_bits & T30_CONTROL_BIT_ACCEPT_POLLING)
							fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SEL_POLLING;
					}
					len = nlc[0];
					pos = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH;
					if (pos < plci->fax_connect_info_length)
					{
						for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--)
							nlc[++len] = plci->fax_connect_info_buffer[pos++];
					}
					else
						nlc[++len] = 0;
					if (pos < plci->fax_connect_info_length)
					{
						for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--)
							nlc[++len] = plci->fax_connect_info_buffer[pos++];
					}
					else
						nlc[++len] = 0;
					if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1])
					    & (1L << PRIVATE_FAX_NONSTANDARD))
					{
						if ((pos < plci->fax_connect_info_length) && (plci->fax_connect_info_buffer[pos] != 0))
						{
							if ((plci->fax_connect_info_buffer[pos] >= 3) && (plci->fax_connect_info_buffer[pos + 1] >= 2))
								plci->nsf_control_bits = GET_WORD(&plci->fax_connect_info_buffer[pos + 2]);
							for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--)
								nlc[++len] = plci->fax_connect_info_buffer[pos++];
						}
						else
						{
							if (api_parse(&b3_config->info[1], (word)b3_config->length, "wwsss", b3_config_parms))
							{
								dbug(1, dprintf("non-standard facilities info missing or wrong format"));
								nlc[++len] = 0;
							}
							else
							{
								if ((b3_config_parms[4].length >= 3) && (b3_config_parms[4].info[1] >= 2))
									plci->nsf_control_bits = GET_WORD(&b3_config_parms[4].info[2]);
								nlc[++len] = (byte)(b3_config_parms[4].length);
								for (i = 0; i < b3_config_parms[4].length; i++)
									nlc[++len] = b3_config_parms[4].info[1 + i];
							}
						}
					}
					nlc[0] = len;
					if ((plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF)
					    && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP))
					{
						((T30_INFO *)&nlc[1])->operating_mode = T30_OPERATING_MODE_CAPI_NEG;
					}
				}
			}

			PUT_WORD(&(((T30_INFO *)&nlc[1])->control_bits_low), fax_control_bits);
			len = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH;
			for (i = 0; i < len; i++)
				plci->fax_connect_info_buffer[i] = nlc[1 + i];
			((T30_INFO *) plci->fax_connect_info_buffer)->head_line_len = 0;
			i += ((T30_INFO *)&nlc[1])->head_line_len;
			while (i < nlc[0])
				plci->fax_connect_info_buffer[len++] = nlc[++i];
			plci->fax_connect_info_length = len;
		}
		else
		{
			nlc[0] = 14;
			if (b3_config->length != 16)
				return _B3_PARM_NOT_SUPPORTED;
			for (i = 0; i < 12; i++) nlc[1 + i] = b3_config->info[1 + i];
			if (GET_WORD(&b3_config->info[13]) != 8 && GET_WORD(&b3_config->info[13]) != 128)
				return _B3_PARM_NOT_SUPPORTED;
			nlc[13] = b3_config->info[13];
			if (GET_WORD(&b3_config->info[15]) >= nlc[13])
				return _B3_PARM_NOT_SUPPORTED;
			nlc[14] = b3_config->info[15];
		}
	}
	else
	{
		if (plci->B3_prot == 4
		    || plci->B3_prot == 5 /*T.30 - FAX*/) return _B3_PARM_NOT_SUPPORTED;
	}
	add_p(plci, NLC, nlc);
	return 0;
}

/*----------------------------------------------------------------*/
/*      make the same as add_b23, but only for the modem related  */
/*      L2 and L3 B-Chan protocol.                                */
/*                                                                */
/*      Enabled L2 and L3 Configurations:                         */
/*        If L1 == Modem all negotiation                          */
/*          only L2 == Modem with full negotiation is allowed     */
/*        If L1 == Modem async or sync                            */
/*          only L2 == Transparent is allowed                     */
/*        L3 == Modem or L3 == Transparent are allowed            */
/*      B2 Configuration for modem:                               */
/*          word : enable/disable compression, bitoptions         */
/*      B3 Configuration for modem:                               */
/*          empty                                                 */
/*----------------------------------------------------------------*/
static word add_modem_b23(PLCI *plci, API_PARSE *bp_parms)
{
	static byte lli[12] = {1,1};
	static byte llc[3] = {2,0,0};
	static byte dlc[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	API_PARSE mdm_config[2];
	word i;
	word b2_config = 0;

	for (i = 0; i < 2; i++) mdm_config[i].length = 0;
	for (i = 0; i < sizeof(dlc); i++) dlc[i] = 0;

	if (((GET_WORD(bp_parms[0].info) == B1_MODEM_ALL_NEGOTIATE)
	     && (GET_WORD(bp_parms[1].info) != B2_MODEM_EC_COMPRESSION))
	    || ((GET_WORD(bp_parms[0].info) != B1_MODEM_ALL_NEGOTIATE)
		&& (GET_WORD(bp_parms[1].info) != B2_TRANSPARENT)))
	{
		return (_B_STACK_NOT_SUPPORTED);
	}
	if ((GET_WORD(bp_parms[2].info) != B3_MODEM)
	    && (GET_WORD(bp_parms[2].info) != B3_TRANSPARENT))
	{
		return (_B_STACK_NOT_SUPPORTED);
	}

	plci->B2_prot = (byte) GET_WORD(bp_parms[1].info);
	plci->B3_prot = (byte) GET_WORD(bp_parms[2].info);

	if ((GET_WORD(bp_parms[1].info) == B2_MODEM_EC_COMPRESSION) && bp_parms[4].length)
	{
		if (api_parse(&bp_parms[4].info[1],
			      (word)bp_parms[4].length, "w",
			      mdm_config))
		{
			return (_WRONG_MESSAGE_FORMAT);
		}
		b2_config = GET_WORD(mdm_config[0].info);
	}

	/* OK, L2 is modem */

	lli[0] = 1;
	lli[1] = 1;
	if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL)
		lli[1] |= 2;
	if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_OOB_CHANNEL)
		lli[1] |= 4;

	if ((lli[1] & 0x02) && (diva_xdi_extended_features & DIVA_CAPI_USE_CMA)) {
		lli[1] |= 0x10;
		if (plci->rx_dma_descriptor <= 0) {
			plci->rx_dma_descriptor = diva_get_dma_descriptor(plci, &plci->rx_dma_magic);
			if (plci->rx_dma_descriptor >= 0)
				plci->rx_dma_descriptor++;
		}
		if (plci->rx_dma_descriptor > 0) {
			lli[1] |= 0x40;
			lli[0] = 6;
			lli[2] = (byte)(plci->rx_dma_descriptor - 1);
			lli[3] = (byte)plci->rx_dma_magic;
			lli[4] = (byte)(plci->rx_dma_magic >>  8);
			lli[5] = (byte)(plci->rx_dma_magic >> 16);
			lli[6] = (byte)(plci->rx_dma_magic >> 24);
		}
	}

	if (DIVA_CAPI_SUPPORTS_NO_CANCEL(plci->adapter)) {
		lli[1] |= 0x20;
	}

	llc[1] = (plci->call_dir & (CALL_DIR_ORIGINATE | CALL_DIR_FORCE_OUTG_NL)) ?
		/*V42*/ 10 : /*V42_IN*/ 9;
	llc[2] = 4;                      /* pass L3 always transparent */
	add_p(plci, LLI, lli);
	add_p(plci, LLC, llc);
	i =  1;
	PUT_WORD(&dlc[i], plci->appl->MaxDataLength);
	i += 2;
	if (GET_WORD(bp_parms[1].info) == B2_MODEM_EC_COMPRESSION)
	{
		if (bp_parms[4].length)
		{
			dbug(1, dprintf("MDM b2_config=%02x", b2_config));
			dlc[i++] = 3; /* Addr A */
			dlc[i++] = 1; /* Addr B */
			dlc[i++] = 7; /* modulo mode */
			dlc[i++] = 7; /* window size */
			dlc[i++] = 0; /* XID len Lo  */
			dlc[i++] = 0; /* XID len Hi  */

			if (b2_config & MDM_B2_DISABLE_V42bis)
			{
				dlc[i] |= DLC_MODEMPROT_DISABLE_V42_V42BIS;
			}
			if (b2_config & MDM_B2_DISABLE_MNP)
			{
				dlc[i] |= DLC_MODEMPROT_DISABLE_MNP_MNP5;
			}
			if (b2_config & MDM_B2_DISABLE_TRANS)
			{
				dlc[i] |= DLC_MODEMPROT_REQUIRE_PROTOCOL;
			}
			if (b2_config & MDM_B2_DISABLE_V42)
			{
				dlc[i] |= DLC_MODEMPROT_DISABLE_V42_DETECT;
			}
			if (b2_config & MDM_B2_DISABLE_COMP)
			{
				dlc[i] |= DLC_MODEMPROT_DISABLE_COMPRESSION;
			}
			i++;
		}
	}
	else
	{
		dlc[i++] = 3; /* Addr A */
		dlc[i++] = 1; /* Addr B */
		dlc[i++] = 7; /* modulo mode */
		dlc[i++] = 7; /* window size */
		dlc[i++] = 0; /* XID len Lo  */
		dlc[i++] = 0; /* XID len Hi  */
		dlc[i++] = DLC_MODEMPROT_DISABLE_V42_V42BIS |
			DLC_MODEMPROT_DISABLE_MNP_MNP5 |
			DLC_MODEMPROT_DISABLE_V42_DETECT |
			DLC_MODEMPROT_DISABLE_COMPRESSION;
	}
	dlc[0] = (byte)(i - 1);
/* HexDump ("DLC", sizeof(dlc), &dlc[0]); */
	add_p(plci, DLC, dlc);
	return (0);
}


/*------------------------------------------------------------------*/
/* send a request for the signaling entity                          */
/*------------------------------------------------------------------*/

static void sig_req(PLCI *plci, byte req, byte Id)
{
	if (!plci) return;
	if (plci->adapter->adapter_disabled) return;
	dbug(1, dprintf("sig_req(%x)", req));
	if (req == REMOVE)
		plci->sig_remove_id = plci->Sig.Id;
	if (plci->req_in == plci->req_in_start) {
		plci->req_in += 2;
		plci->RBuffer[plci->req_in++] = 0;
	}
	PUT_WORD(&plci->RBuffer[plci->req_in_start], plci->req_in-plci->req_in_start - 2);
	plci->RBuffer[plci->req_in++] = Id;   /* sig/nl flag */
	plci->RBuffer[plci->req_in++] = req;  /* request */
	plci->RBuffer[plci->req_in++] = 0;    /* channel */
	plci->req_in_start = plci->req_in;
}

/*------------------------------------------------------------------*/
/* send a request for the network layer entity                      */
/*------------------------------------------------------------------*/

static void nl_req_ncci(PLCI *plci, byte req, byte ncci)
{
	if (!plci) return;
	if (plci->adapter->adapter_disabled) return;
	dbug(1, dprintf("nl_req %02x %02x %02x", plci->Id, req, ncci));
	if (req == REMOVE)
	{
		plci->nl_remove_id = plci->NL.Id;
		ncci_remove(plci, 0, (byte)(ncci != 0));
		ncci = 0;
	}
	if (plci->req_in == plci->req_in_start) {
		plci->req_in += 2;
		plci->RBuffer[plci->req_in++] = 0;
	}
	PUT_WORD(&plci->RBuffer[plci->req_in_start], plci->req_in-plci->req_in_start - 2);
	plci->RBuffer[plci->req_in++] = 1;    /* sig/nl flag */
	plci->RBuffer[plci->req_in++] = req;  /* request */
	plci->RBuffer[plci->req_in++] = plci->adapter->ncci_ch[ncci];   /* channel */
	plci->req_in_start = plci->req_in;
}

static void send_req(PLCI *plci)
{
	ENTITY *e;
	word l;
/*  word i; */

	if (!plci) return;
	if (plci->adapter->adapter_disabled) return;
	channel_xmit_xon(plci);

	/* if nothing to do, return */
	if (plci->req_in == plci->req_out) return;
	dbug(1, dprintf("send_req(in=%d,out=%d)", plci->req_in, plci->req_out));

	if (plci->nl_req || plci->sig_req) return;

	l = GET_WORD(&plci->RBuffer[plci->req_out]);
	plci->req_out += 2;
	plci->XData[0].P = &plci->RBuffer[plci->req_out];
	plci->req_out += l;
	if (plci->RBuffer[plci->req_out] == 1)
	{
		e = &plci->NL;
		plci->req_out++;
		e->Req = plci->nl_req = plci->RBuffer[plci->req_out++];
		e->ReqCh = plci->RBuffer[plci->req_out++];
		if (!(e->Id & 0x1f))
		{
			e->Id = NL_ID;
			plci->RBuffer[plci->req_out - 4] = CAI;
			plci->RBuffer[plci->req_out - 3] = 1;
			plci->RBuffer[plci->req_out - 2] = (plci->Sig.Id == 0xff) ? 0 : plci->Sig.Id;
			plci->RBuffer[plci->req_out - 1] = 0;
			l += 3;
			plci->nl_global_req = plci->nl_req;
		}
		dbug(1, dprintf("%x:NLREQ(%x:%x:%x)", plci->adapter->Id, e->Id, e->Req, e->ReqCh));
	}
	else
	{
		e = &plci->Sig;
		if (plci->RBuffer[plci->req_out])
			e->Id = plci->RBuffer[plci->req_out];
		plci->req_out++;
		e->Req = plci->sig_req = plci->RBuffer[plci->req_out++];
		e->ReqCh = plci->RBuffer[plci->req_out++];
		if (!(e->Id & 0x1f))
			plci->sig_global_req = plci->sig_req;
		dbug(1, dprintf("%x:SIGREQ(%x:%x:%x)", plci->adapter->Id, e->Id, e->Req, e->ReqCh));
	}
	plci->XData[0].PLength = l;
	e->X = plci->XData;
	plci->adapter->request(e);
	dbug(1, dprintf("send_ok"));
}

static void send_data(PLCI *plci)
{
	DIVA_CAPI_ADAPTER *a;
	DATA_B3_DESC *data;
	NCCI   *ncci_ptr;
	word ncci;

	if (!plci->nl_req && plci->ncci_ring_list)
	{
		a = plci->adapter;
		ncci = plci->ncci_ring_list;
		do
		{
			ncci = a->ncci_next[ncci];
			ncci_ptr = &(a->ncci[ncci]);
			if (!(a->ncci_ch[ncci]
			      && (a->ch_flow_control[a->ncci_ch[ncci]] & N_OK_FC_PENDING)))
			{
				if (ncci_ptr->data_pending)
				{
					if ((a->ncci_state[ncci] == CONNECTED)
					    || (a->ncci_state[ncci] == INC_ACT_PENDING)
					    || (plci->send_disc == ncci))
					{
						data = &(ncci_ptr->DBuffer[ncci_ptr->data_out]);
						if ((plci->B2_prot == B2_V120_ASYNC)
						    || (plci->B2_prot == B2_V120_ASYNC_V42BIS)
						    || (plci->B2_prot == B2_V120_BIT_TRANSPARENT))
						{
							plci->NData[1].P = TransmitBufferGet(plci->appl, data->P);
							plci->NData[1].PLength = data->Length;
							if (data->Flags & 0x10)
								plci->NData[0].P = v120_break_header;
							else
								plci->NData[0].P = v120_default_header;
							plci->NData[0].PLength = 1;
							plci->NL.XNum = 2;
							plci->NL.Req = plci->nl_req = (byte)((data->Flags & 0x07) << 4 | N_DATA);
						}
						else
						{
							plci->NData[0].P = TransmitBufferGet(plci->appl, data->P);
							plci->NData[0].PLength = data->Length;
							if (data->Flags & 0x10)
								plci->NL.Req = plci->nl_req = (byte)N_UDATA;

							else if ((plci->B3_prot == B3_RTP) && (data->Flags & 0x01))
								plci->NL.Req = plci->nl_req = (byte)N_BDATA;

							else
								plci->NL.Req = plci->nl_req = (byte)((data->Flags & 0x07) << 4 | N_DATA);
						}
						plci->NL.X = plci->NData;
						plci->NL.ReqCh = a->ncci_ch[ncci];
						dbug(1, dprintf("%x:DREQ(%x:%x)", a->Id, plci->NL.Id, plci->NL.Req));
						plci->data_sent = true;
						plci->data_sent_ptr = data->P;
						a->request(&plci->NL);
					}
					else {
						cleanup_ncci_data(plci, ncci);
					}
				}
				else if (plci->send_disc == ncci)
				{
					/* dprintf("N_DISC"); */
					plci->NData[0].PLength = 0;
					plci->NL.ReqCh = a->ncci_ch[ncci];
					plci->NL.Req = plci->nl_req = N_DISC;
					a->request(&plci->NL);
					plci->command = _DISCONNECT_B3_R;
					plci->send_disc = 0;
				}
			}
		} while (!plci->nl_req && (ncci != plci->ncci_ring_list));
		plci->ncci_ring_list = ncci;
	}
}

static void listen_check(DIVA_CAPI_ADAPTER *a)
{
	word i, j;
	PLCI *plci;
	byte activnotifiedcalls = 0;

	dbug(1, dprintf("listen_check(%d,%d)", a->listen_active, a->max_listen));
	if (!remove_started && !a->adapter_disabled)
	{
		for (i = 0; i < a->max_plci; i++)
		{
			plci = &(a->plci[i]);
			if (plci->notifiedcall) activnotifiedcalls++;
		}
		dbug(1, dprintf("listen_check(%d)", activnotifiedcalls));

		for (i = a->listen_active; i < ((word)(a->max_listen + activnotifiedcalls)); i++) {
			if ((j = get_plci(a))) {
				a->listen_active++;
				plci = &a->plci[j - 1];
				plci->State = LISTENING;

				add_p(plci, OAD, "\x01\xfd");

				add_p(plci, KEY, "\x04\x43\x41\x32\x30");

				add_p(plci, CAI, "\x01\xc0");
				add_p(plci, UID, "\x06\x43\x61\x70\x69\x32\x30");
				add_p(plci, LLI, "\x01\xc4");                  /* support Dummy CR FAC + MWI + SpoofNotify */
				add_p(plci, SHIFT | 6, NULL);
				add_p(plci, SIN, "\x02\x00\x00");
				plci->internal_command = LISTEN_SIG_ASSIGN_PEND;     /* do indicate_req if OK  */
				sig_req(plci, ASSIGN, DSIG_ID);
				send_req(plci);
			}
		}
	}
}

/*------------------------------------------------------------------*/
/* functions for all parameters sent in INDs                        */
/*------------------------------------------------------------------*/

static void IndParse(PLCI *plci, word *parms_id, byte **parms, byte multiIEsize)
{
	word ploc;            /* points to current location within packet */
	byte w;
	byte wlen;
	byte codeset, lock;
	byte *in;
	word i;
	word code;
	word mIEindex = 0;
	ploc = 0;
	codeset = 0;
	lock = 0;

	in = plci->Sig.RBuffer->P;
	for (i = 0; i < parms_id[0]; i++)   /* multiIE parms_id contains just the 1st */
	{                            /* element but parms array is larger      */
		parms[i] = (byte *)"";
	}
	for (i = 0; i < multiIEsize; i++)
	{
		parms[i] = (byte *)"";
	}

	while (ploc < plci->Sig.RBuffer->length - 1) {

		/* read information element id and length                   */
		w = in[ploc];

		if (w & 0x80) {
/*    w &=0xf0; removed, cannot detect congestion levels */
/*    upper 4 bit masked with w==SHIFT now               */
			wlen = 0;
		}
		else {
			wlen = (byte)(in[ploc + 1] + 1);
		}
		/* check if length valid (not exceeding end of packet)      */
		if ((ploc + wlen) > 270) return;
		if (lock & 0x80) lock &= 0x7f;
		else codeset = lock;

		if ((w & 0xf0) == SHIFT) {
			codeset = in[ploc];
			if (!(codeset & 0x08)) lock = (byte)(codeset & 7);
			codeset &= 7;
			lock |= 0x80;
		}
		else {
			if (w == ESC && wlen >= 3) code = in[ploc + 2] | 0x800;
			else code = w;
			code |= (codeset << 8);

			for (i = 1; i < parms_id[0] + 1 && parms_id[i] != code; i++);

			if (i < parms_id[0] + 1) {
				if (!multiIEsize) { /* with multiIEs use next field index,          */
					mIEindex = i - 1;    /* with normal IEs use same index like parms_id */
				}

				parms[mIEindex] = &in[ploc + 1];
				dbug(1, dprintf("mIE[%d]=0x%x", *parms[mIEindex], in[ploc]));
				if (parms_id[i] == OAD
				    || parms_id[i] == CONN_NR
				    || parms_id[i] == CAD) {
					if (in[ploc + 2] & 0x80) {
						in[ploc + 0] = (byte)(in[ploc + 1] + 1);
						in[ploc + 1] = (byte)(in[ploc + 2] & 0x7f);
						in[ploc + 2] = 0x80;
						parms[mIEindex] = &in[ploc];
					}
				}
				mIEindex++;       /* effects multiIEs only */
			}
		}

		ploc += (wlen + 1);
	}
	return;
}

/*------------------------------------------------------------------*/
/* try to match a cip from received BC and HLC                      */
/*------------------------------------------------------------------*/

static byte ie_compare(byte *ie1, byte *ie2)
{
	word i;
	if (!ie1 || !ie2) return false;
	if (!ie1[0]) return false;
	for (i = 0; i < (word)(ie1[0] + 1); i++) if (ie1[i] != ie2[i]) return false;
	return true;
}

static word find_cip(DIVA_CAPI_ADAPTER *a, byte *bc, byte *hlc)
{
	word i;
	word j;

	for (i = 9; i && !ie_compare(bc, cip_bc[i][a->u_law]); i--);

	for (j = 16; j < 29 &&
		     (!ie_compare(bc, cip_bc[j][a->u_law]) || !ie_compare(hlc, cip_hlc[j])); j++);
	if (j == 29) return i;
	return j;
}


static byte AddInfo(byte **add_i,
		    byte **fty_i,
		    byte *esc_chi,
		    byte *facility)
{
	byte i;
	byte j;
	byte k;
	byte flen;
	byte len = 0;
	/* facility is a nested structure */
	/* FTY can be more than once      */

	if (esc_chi[0] && !(esc_chi[esc_chi[0]] & 0x7f))
	{
		add_i[0] = (byte *)"\x02\x02\x00"; /* use neither b nor d channel */
	}

	else
	{
		add_i[0] = (byte *)"";
	}
	if (!fty_i[0][0])
	{
		add_i[3] = (byte *)"";
	}
	else
	{    /* facility array found  */
		for (i = 0, j = 1; i < MAX_MULTI_IE && fty_i[i][0]; i++)
		{
			dbug(1, dprintf("AddIFac[%d]", fty_i[i][0]));
			len += fty_i[i][0];
			len += 2;
			flen = fty_i[i][0];
			facility[j++] = 0x1c; /* copy fac IE */
			for (k = 0; k <= flen; k++, j++)
			{
				facility[j] = fty_i[i][k];
/*      dbug(1, dprintf("%x ",facility[j])); */
			}
		}
		facility[0] = len;
		add_i[3] = facility;
	}
/*  dbug(1, dprintf("FacArrLen=%d ",len)); */
	len = add_i[0][0] + add_i[1][0] + add_i[2][0] + add_i[3][0];
	len += 4;                          /* calculate length of all */
	return (len);
}

/*------------------------------------------------------------------*/
/* voice and codec features                                         */
/*------------------------------------------------------------------*/

static void SetVoiceChannel(PLCI *plci, byte *chi, DIVA_CAPI_ADAPTER *a)
{
	byte voice_chi[] = "\x02\x18\x01";
	byte channel;

	channel = chi[chi[0]] & 0x3;
	dbug(1, dprintf("ExtDevON(Ch=0x%x)", channel));
	voice_chi[2] = (channel) ? channel : 1;
	add_p(plci, FTY, "\x02\x01\x07");             /* B On, default on 1 */
	add_p(plci, ESC, voice_chi);                  /* Channel */
	sig_req(plci, TEL_CTRL, 0);
	send_req(plci);
	if (a->AdvSignalPLCI)
	{
		adv_voice_write_coefs(a->AdvSignalPLCI, ADV_VOICE_WRITE_ACTIVATION);
	}
}

static void VoiceChannelOff(PLCI *plci)
{
	dbug(1, dprintf("ExtDevOFF"));
	add_p(plci, FTY, "\x02\x01\x08");             /* B Off */
	sig_req(plci, TEL_CTRL, 0);
	send_req(plci);
	if (plci->adapter->AdvSignalPLCI)
	{
		adv_voice_clear_config(plci->adapter->AdvSignalPLCI);
	}
}


static word AdvCodecSupport(DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl,
			    byte hook_listen)
{
	word j;
	PLCI *splci;

	/* check if hardware supports handset with hook states (adv.codec) */
	/* or if just a on board codec is supported                        */
	/* the advanced codec plci is just for internal use                */

	/* diva Pro with on-board codec:                                   */
	if (a->profile.Global_Options & HANDSET)
	{
		/* new call, but hook states are already signalled */
		if (a->AdvCodecFLAG)
		{
			if (a->AdvSignalAppl != appl || a->AdvSignalPLCI)
			{
				dbug(1, dprintf("AdvSigPlci=0x%x", a->AdvSignalPLCI));
				return 0x2001; /* codec in use by another application */
			}
			if (plci != NULL)
			{
				a->AdvSignalPLCI = plci;
				plci->tel = ADV_VOICE;
			}
			return 0;                      /* adv codec still used */
		}
		if ((j = get_plci(a)))
		{
			splci = &a->plci[j - 1];
			splci->tel = CODEC_PERMANENT;
			/* hook_listen indicates if a facility_req with handset/hook support */
			/* was sent. Otherwise if just a call on an external device was made */
			/* the codec will be used but the hook info will be discarded (just  */
			/* the external controller is in use                                 */
			if (hook_listen) splci->State = ADVANCED_VOICE_SIG;
			else
			{
				splci->State = ADVANCED_VOICE_NOSIG;
				if (plci)
				{
					plci->spoofed_msg = SPOOFING_REQUIRED;
				}
				/* indicate D-ch connect if  */
			}                                        /* codec is connected OK     */
			if (plci != NULL)
			{
				a->AdvSignalPLCI = plci;
				plci->tel = ADV_VOICE;
			}
			a->AdvSignalAppl = appl;
			a->AdvCodecFLAG = true;
			a->AdvCodecPLCI = splci;
			add_p(splci, CAI, "\x01\x15");
			add_p(splci, LLI, "\x01\x00");
			add_p(splci, ESC, "\x02\x18\x00");
			add_p(splci, UID, "\x06\x43\x61\x70\x69\x32\x30");
			splci->internal_command = PERM_COD_ASSIGN;
			dbug(1, dprintf("Codec Assign"));
			sig_req(splci, ASSIGN, DSIG_ID);
			send_req(splci);
		}
		else
		{
			return 0x2001; /* wrong state, no more plcis */
		}
	}
	else if (a->profile.Global_Options & ON_BOARD_CODEC)
	{
		if (hook_listen) return 0x300B;               /* Facility not supported */
		/* no hook with SCOM      */
		if (plci != NULL) plci->tel = CODEC;
		dbug(1, dprintf("S/SCOM codec"));
		/* first time we use the scom-s codec we must shut down the internal   */
		/* handset application of the card. This can be done by an assign with */
		/* a cai with the 0x80 bit set. Assign return code is 'out of resource'*/
		if (!a->scom_appl_disable) {
			if ((j = get_plci(a))) {
				splci = &a->plci[j - 1];
				add_p(splci, CAI, "\x01\x80");
				add_p(splci, UID, "\x06\x43\x61\x70\x69\x32\x30");
				sig_req(splci, ASSIGN, 0xC0);  /* 0xc0 is the TEL_ID */
				send_req(splci);
				a->scom_appl_disable = true;
			}
			else{
				return 0x2001; /* wrong state, no more plcis */
			}
		}
	}
	else return 0x300B;               /* Facility not supported */

	return 0;
}


static void CodecIdCheck(DIVA_CAPI_ADAPTER *a, PLCI *plci)
{

	dbug(1, dprintf("CodecIdCheck"));

	if (a->AdvSignalPLCI == plci)
	{
		dbug(1, dprintf("PLCI owns codec"));
		VoiceChannelOff(a->AdvCodecPLCI);
		if (a->AdvCodecPLCI->State == ADVANCED_VOICE_NOSIG)
		{
			dbug(1, dprintf("remove temp codec PLCI"));
			plci_remove(a->AdvCodecPLCI);
			a->AdvCodecFLAG  = 0;
			a->AdvCodecPLCI  = NULL;
			a->AdvSignalAppl = NULL;
		}
		a->AdvSignalPLCI = NULL;
	}
}

/* -------------------------------------------------------------------
   Ask for physical address of card on PCI bus
   ------------------------------------------------------------------- */
static void diva_ask_for_xdi_sdram_bar(DIVA_CAPI_ADAPTER *a,
				       IDI_SYNC_REQ *preq) {
	a->sdram_bar = 0;
	if (diva_xdi_extended_features & DIVA_CAPI_XDI_PROVIDES_SDRAM_BAR) {
		ENTITY *e = (ENTITY *)preq;

		e->user[0] = a->Id - 1;
		preq->xdi_sdram_bar.info.bar    = 0;
		preq->xdi_sdram_bar.Req         = 0;
		preq->xdi_sdram_bar.Rc           = IDI_SYNC_REQ_XDI_GET_ADAPTER_SDRAM_BAR;

		(*(a->request))(e);

		a->sdram_bar = preq->xdi_sdram_bar.info.bar;
		dbug(3, dprintf("A(%d) SDRAM BAR = %08x", a->Id, a->sdram_bar));
	}
}

/* -------------------------------------------------------------------
   Ask XDI about extended features
   ------------------------------------------------------------------- */
static void diva_get_extended_adapter_features(DIVA_CAPI_ADAPTER *a) {
	IDI_SYNC_REQ *preq;
	char buffer[((sizeof(preq->xdi_extended_features) + 4) > sizeof(ENTITY)) ? (sizeof(preq->xdi_extended_features) + 4) : sizeof(ENTITY)];

	char features[4];
	preq = (IDI_SYNC_REQ *)&buffer[0];

	if (!diva_xdi_extended_features) {
		ENTITY *e = (ENTITY *)preq;
		diva_xdi_extended_features |= 0x80000000;

		e->user[0] = a->Id - 1;
		preq->xdi_extended_features.Req = 0;
		preq->xdi_extended_features.Rc  = IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES;
		preq->xdi_extended_features.info.buffer_length_in_bytes = sizeof(features);
		preq->xdi_extended_features.info.features = &features[0];

		(*(a->request))(e);

		if (features[0] & DIVA_XDI_EXTENDED_FEATURES_VALID) {
			/*
			  Check features located in the byte '0'
			*/
			if (features[0] & DIVA_XDI_EXTENDED_FEATURE_CMA) {
				diva_xdi_extended_features |= DIVA_CAPI_USE_CMA;
			}
			if (features[0] & DIVA_XDI_EXTENDED_FEATURE_RX_DMA) {
				diva_xdi_extended_features |= DIVA_CAPI_XDI_PROVIDES_RX_DMA;
				dbug(1, dprintf("XDI provides RxDMA"));
			}
			if (features[0] & DIVA_XDI_EXTENDED_FEATURE_SDRAM_BAR) {
				diva_xdi_extended_features |= DIVA_CAPI_XDI_PROVIDES_SDRAM_BAR;
			}
			if (features[0] & DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC) {
				diva_xdi_extended_features |= DIVA_CAPI_XDI_PROVIDES_NO_CANCEL;
				dbug(3, dprintf("XDI provides NO_CANCEL_RC feature"));
			}

		}
	}

	diva_ask_for_xdi_sdram_bar(a, preq);
}

/*------------------------------------------------------------------*/
/* automatic law                                                    */
/*------------------------------------------------------------------*/
/* called from OS specific part after init time to get the Law              */
/* a-law (Euro) and u-law (us,japan) use different BCs in the Setup message */
void AutomaticLaw(DIVA_CAPI_ADAPTER *a)
{
	word j;
	PLCI *splci;

	if (a->automatic_law) {
		return;
	}
	if ((j = get_plci(a))) {
		diva_get_extended_adapter_features(a);
		splci = &a->plci[j - 1];
		a->automatic_lawPLCI = splci;
		a->automatic_law = 1;
		add_p(splci, CAI, "\x01\x80");
		add_p(splci, UID, "\x06\x43\x61\x70\x69\x32\x30");
		splci->internal_command = USELAW_REQ;
		splci->command = 0;
		splci->number = 0;
		sig_req(splci, ASSIGN, DSIG_ID);
		send_req(splci);
	}
}

/* called from OS specific part if an application sends an Capi20Release */
word CapiRelease(word Id)
{
	word i, j, appls_found;
	PLCI *plci;
	APPL   *this;
	DIVA_CAPI_ADAPTER *a;

	if (!Id)
	{
		dbug(0, dprintf("A: CapiRelease(Id==0)"));
		return (_WRONG_APPL_ID);
	}

	this = &application[Id - 1];               /* get application pointer */

	for (i = 0, appls_found = 0; i < max_appl; i++)
	{
		if (application[i].Id)       /* an application has been found        */
		{
			appls_found++;
		}
	}

	for (i = 0; i < max_adapter; i++)             /* scan all adapters...    */
	{
		a = &adapter[i];
		if (a->request)
		{
			a->Info_Mask[Id - 1] = 0;
			a->CIP_Mask[Id - 1] = 0;
			a->Notification_Mask[Id - 1] = 0;
			a->codec_listen[Id - 1] = NULL;
			a->requested_options_table[Id - 1] = 0;
			for (j = 0; j < a->max_plci; j++)           /* and all PLCIs connected */
			{                                      /* with this application   */
				plci = &a->plci[j];
				if (plci->Id)                         /* if plci owns no application */
				{                                    /* it may be not jet connected */
					if (plci->State == INC_CON_PENDING
					    || plci->State == INC_CON_ALERT)
					{
						if (test_c_ind_mask_bit(plci, (word)(Id - 1)))
						{
							clear_c_ind_mask_bit(plci, (word)(Id - 1));
							if (c_ind_mask_empty(plci))
							{
								sig_req(plci, HANGUP, 0);
								send_req(plci);
								plci->State = OUTG_DIS_PENDING;
							}
						}
					}
					if (test_c_ind_mask_bit(plci, (word)(Id - 1)))
					{
						clear_c_ind_mask_bit(plci, (word)(Id - 1));
						if (c_ind_mask_empty(plci))
						{
							if (!plci->appl)
							{
								plci_remove(plci);
								plci->State = IDLE;
							}
						}
					}
					if (plci->appl == this)
					{
						plci->appl = NULL;
						plci_remove(plci);
						plci->State = IDLE;
					}
				}
			}
			listen_check(a);

			if (a->flag_dynamic_l1_down)
			{
				if (appls_found == 1)            /* last application does a capi release */
				{
					if ((j = get_plci(a)))
					{
						plci = &a->plci[j - 1];
						plci->command = 0;
						add_p(plci, OAD, "\x01\xfd");
						add_p(plci, CAI, "\x01\x80");
						add_p(plci, UID, "\x06\x43\x61\x70\x69\x32\x30");
						add_p(plci, SHIFT | 6, NULL);
						add_p(plci, SIN, "\x02\x00\x00");
						plci->internal_command = REM_L1_SIG_ASSIGN_PEND;
						sig_req(plci, ASSIGN, DSIG_ID);
						add_p(plci, FTY, "\x02\xff\x06"); /* l1 down */
						sig_req(plci, SIG_CTRL, 0);
						send_req(plci);
					}
				}
			}
			if (a->AdvSignalAppl == this)
			{
				this->NullCREnable = false;
				if (a->AdvCodecPLCI)
				{
					plci_remove(a->AdvCodecPLCI);
					a->AdvCodecPLCI->tel = 0;
					a->AdvCodecPLCI->adv_nl = 0;
				}
				a->AdvSignalAppl = NULL;
				a->AdvSignalPLCI = NULL;
				a->AdvCodecFLAG = 0;
				a->AdvCodecPLCI = NULL;
			}
		}
	}

	this->Id = 0;

	return GOOD;
}

static word plci_remove_check(PLCI *plci)
{
	if (!plci) return true;
	if (!plci->NL.Id && c_ind_mask_empty(plci))
	{
		if (plci->Sig.Id == 0xff)
			plci->Sig.Id = 0;
		if (!plci->Sig.Id)
		{
			dbug(1, dprintf("plci_remove_complete(%x)", plci->Id));
			dbug(1, dprintf("tel=0x%x,Sig=0x%x", plci->tel, plci->Sig.Id));
			if (plci->Id)
			{
				CodecIdCheck(plci->adapter, plci);
				clear_b1_config(plci);
				ncci_remove(plci, 0, false);
				plci_free_msg_in_queue(plci);
				channel_flow_control_remove(plci);
				plci->Id = 0;
				plci->State = IDLE;
				plci->channels = 0;
				plci->appl = NULL;
				plci->notifiedcall = 0;
			}
			listen_check(plci->adapter);
			return true;
		}
	}
	return false;
}


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

static byte plci_nl_busy(PLCI *plci)
{
	/* only applicable for non-multiplexed protocols */
	return (plci->nl_req
		|| (plci->ncci_ring_list
		    && plci->adapter->ncci_ch[plci->ncci_ring_list]
		    && (plci->adapter->ch_flow_control[plci->adapter->ncci_ch[plci->ncci_ring_list]] & N_OK_FC_PENDING)));
}


/*------------------------------------------------------------------*/
/* DTMF facilities                                                  */
/*------------------------------------------------------------------*/


static struct
{
	byte send_mask;
	byte listen_mask;
	byte character;
	byte code;
} dtmf_digit_map[] =
{
	{ 0x01, 0x01, 0x23, DTMF_DIGIT_TONE_CODE_HASHMARK },
	{ 0x01, 0x01, 0x2a, DTMF_DIGIT_TONE_CODE_STAR },
	{ 0x01, 0x01, 0x30, DTMF_DIGIT_TONE_CODE_0 },
	{ 0x01, 0x01, 0x31, DTMF_DIGIT_TONE_CODE_1 },
	{ 0x01, 0x01, 0x32, DTMF_DIGIT_TONE_CODE_2 },
	{ 0x01, 0x01, 0x33, DTMF_DIGIT_TONE_CODE_3 },
	{ 0x01, 0x01, 0x34, DTMF_DIGIT_TONE_CODE_4 },
	{ 0x01, 0x01, 0x35, DTMF_DIGIT_TONE_CODE_5 },
	{ 0x01, 0x01, 0x36, DTMF_DIGIT_TONE_CODE_6 },
	{ 0x01, 0x01, 0x37, DTMF_DIGIT_TONE_CODE_7 },
	{ 0x01, 0x01, 0x38, DTMF_DIGIT_TONE_CODE_8 },
	{ 0x01, 0x01, 0x39, DTMF_DIGIT_TONE_CODE_9 },
	{ 0x01, 0x01, 0x41, DTMF_DIGIT_TONE_CODE_A },
	{ 0x01, 0x01, 0x42, DTMF_DIGIT_TONE_CODE_B },
	{ 0x01, 0x01, 0x43, DTMF_DIGIT_TONE_CODE_C },
	{ 0x01, 0x01, 0x44, DTMF_DIGIT_TONE_CODE_D },
	{ 0x01, 0x00, 0x61, DTMF_DIGIT_TONE_CODE_A },
	{ 0x01, 0x00, 0x62, DTMF_DIGIT_TONE_CODE_B },
	{ 0x01, 0x00, 0x63, DTMF_DIGIT_TONE_CODE_C },
	{ 0x01, 0x00, 0x64, DTMF_DIGIT_TONE_CODE_D },

	{ 0x04, 0x04, 0x80, DTMF_SIGNAL_NO_TONE },
	{ 0x00, 0x04, 0x81, DTMF_SIGNAL_UNIDENTIFIED_TONE },
	{ 0x04, 0x04, 0x82, DTMF_SIGNAL_DIAL_TONE },
	{ 0x04, 0x04, 0x83, DTMF_SIGNAL_PABX_INTERNAL_DIAL_TONE },
	{ 0x04, 0x04, 0x84, DTMF_SIGNAL_SPECIAL_DIAL_TONE },
	{ 0x04, 0x04, 0x85, DTMF_SIGNAL_SECOND_DIAL_TONE },
	{ 0x04, 0x04, 0x86, DTMF_SIGNAL_RINGING_TONE },
	{ 0x04, 0x04, 0x87, DTMF_SIGNAL_SPECIAL_RINGING_TONE },
	{ 0x04, 0x04, 0x88, DTMF_SIGNAL_BUSY_TONE },
	{ 0x04, 0x04, 0x89, DTMF_SIGNAL_CONGESTION_TONE },
	{ 0x04, 0x04, 0x8a, DTMF_SIGNAL_SPECIAL_INFORMATION_TONE },
	{ 0x04, 0x04, 0x8b, DTMF_SIGNAL_COMFORT_TONE },
	{ 0x04, 0x04, 0x8c, DTMF_SIGNAL_HOLD_TONE },
	{ 0x04, 0x04, 0x8d, DTMF_SIGNAL_RECORD_TONE },
	{ 0x04, 0x04, 0x8e, DTMF_SIGNAL_CALLER_WAITING_TONE },
	{ 0x04, 0x04, 0x8f, DTMF_SIGNAL_CALL_WAITING_TONE },
	{ 0x04, 0x04, 0x90, DTMF_SIGNAL_PAY_TONE },
	{ 0x04, 0x04, 0x91, DTMF_SIGNAL_POSITIVE_INDICATION_TONE },
	{ 0x04, 0x04, 0x92, DTMF_SIGNAL_NEGATIVE_INDICATION_TONE },
	{ 0x04, 0x04, 0x93, DTMF_SIGNAL_WARNING_TONE },
	{ 0x04, 0x04, 0x94, DTMF_SIGNAL_INTRUSION_TONE },
	{ 0x04, 0x04, 0x95, DTMF_SIGNAL_CALLING_CARD_SERVICE_TONE },
	{ 0x04, 0x04, 0x96, DTMF_SIGNAL_PAYPHONE_RECOGNITION_TONE },
	{ 0x04, 0x04, 0x97, DTMF_SIGNAL_CPE_ALERTING_SIGNAL },
	{ 0x04, 0x04, 0x98, DTMF_SIGNAL_OFF_HOOK_WARNING_TONE },
	{ 0x04, 0x04, 0xbf, DTMF_SIGNAL_INTERCEPT_TONE },
	{ 0x04, 0x04, 0xc0, DTMF_SIGNAL_MODEM_CALLING_TONE },
	{ 0x04, 0x04, 0xc1, DTMF_SIGNAL_FAX_CALLING_TONE },
	{ 0x04, 0x04, 0xc2, DTMF_SIGNAL_ANSWER_TONE },
	{ 0x04, 0x04, 0xc3, DTMF_SIGNAL_REVERSED_ANSWER_TONE },
	{ 0x04, 0x04, 0xc4, DTMF_SIGNAL_ANSAM_TONE },
	{ 0x04, 0x04, 0xc5, DTMF_SIGNAL_REVERSED_ANSAM_TONE },
	{ 0x04, 0x04, 0xc6, DTMF_SIGNAL_BELL103_ANSWER_TONE },
	{ 0x04, 0x04, 0xc7, DTMF_SIGNAL_FAX_FLAGS },
	{ 0x04, 0x04, 0xc8, DTMF_SIGNAL_G2_FAX_GROUP_ID },
	{ 0x00, 0x04, 0xc9, DTMF_SIGNAL_HUMAN_SPEECH },
	{ 0x04, 0x04, 0xca, DTMF_SIGNAL_ANSWERING_MACHINE_390 },
	{ 0x02, 0x02, 0xf1, DTMF_MF_DIGIT_TONE_CODE_1 },
	{ 0x02, 0x02, 0xf2, DTMF_MF_DIGIT_TONE_CODE_2 },
	{ 0x02, 0x02, 0xf3, DTMF_MF_DIGIT_TONE_CODE_3 },
	{ 0x02, 0x02, 0xf4, DTMF_MF_DIGIT_TONE_CODE_4 },
	{ 0x02, 0x02, 0xf5, DTMF_MF_DIGIT_TONE_CODE_5 },
	{ 0x02, 0x02, 0xf6, DTMF_MF_DIGIT_TONE_CODE_6 },
	{ 0x02, 0x02, 0xf7, DTMF_MF_DIGIT_TONE_CODE_7 },
	{ 0x02, 0x02, 0xf8, DTMF_MF_DIGIT_TONE_CODE_8 },
	{ 0x02, 0x02, 0xf9, DTMF_MF_DIGIT_TONE_CODE_9 },
	{ 0x02, 0x02, 0xfa, DTMF_MF_DIGIT_TONE_CODE_0 },
	{ 0x02, 0x02, 0xfb, DTMF_MF_DIGIT_TONE_CODE_K1 },
	{ 0x02, 0x02, 0xfc, DTMF_MF_DIGIT_TONE_CODE_K2 },
	{ 0x02, 0x02, 0xfd, DTMF_MF_DIGIT_TONE_CODE_KP },
	{ 0x02, 0x02, 0xfe, DTMF_MF_DIGIT_TONE_CODE_S1 },
	{ 0x02, 0x02, 0xff, DTMF_MF_DIGIT_TONE_CODE_ST },

};

#define DTMF_DIGIT_MAP_ENTRIES ARRAY_SIZE(dtmf_digit_map)


static void dtmf_enable_receiver(PLCI *plci, byte enable_mask)
{
	word min_digit_duration, min_gap_duration;

	dbug(1, dprintf("[%06lx] %s,%d: dtmf_enable_receiver %02x",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__, enable_mask));

	if (enable_mask != 0)
	{
		min_digit_duration = (plci->dtmf_rec_pulse_ms == 0) ? 40 : plci->dtmf_rec_pulse_ms;
		min_gap_duration = (plci->dtmf_rec_pause_ms == 0) ? 40 : plci->dtmf_rec_pause_ms;
		plci->internal_req_buffer[0] = DTMF_UDATA_REQUEST_ENABLE_RECEIVER;
		PUT_WORD(&plci->internal_req_buffer[1], min_digit_duration);
		PUT_WORD(&plci->internal_req_buffer[3], min_gap_duration);
		plci->NData[0].PLength = 5;

		PUT_WORD(&plci->internal_req_buffer[5], INTERNAL_IND_BUFFER_SIZE);
		plci->NData[0].PLength += 2;
		capidtmf_recv_enable(&(plci->capidtmf_state), min_digit_duration, min_gap_duration);

	}
	else
	{
		plci->internal_req_buffer[0] = DTMF_UDATA_REQUEST_DISABLE_RECEIVER;
		plci->NData[0].PLength = 1;

		capidtmf_recv_disable(&(plci->capidtmf_state));

	}
	plci->NData[0].P = plci->internal_req_buffer;
	plci->NL.X = plci->NData;
	plci->NL.ReqCh = 0;
	plci->NL.Req = plci->nl_req = (byte) N_UDATA;
	plci->adapter->request(&plci->NL);
}


static void dtmf_send_digits(PLCI *plci, byte *digit_buffer, word digit_count)
{
	word w, i;

	dbug(1, dprintf("[%06lx] %s,%d: dtmf_send_digits %d",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__, digit_count));

	plci->internal_req_buffer[0] = DTMF_UDATA_REQUEST_SEND_DIGITS;
	w = (plci->dtmf_send_pulse_ms == 0) ? 40 : plci->dtmf_send_pulse_ms;
	PUT_WORD(&plci->internal_req_buffer[1], w);
	w = (plci->dtmf_send_pause_ms == 0) ? 40 : plci->dtmf_send_pause_ms;
	PUT_WORD(&plci->internal_req_buffer[3], w);
	for (i = 0; i < digit_count; i++)
	{
		w = 0;
		while ((w < DTMF_DIGIT_MAP_ENTRIES)
		       && (digit_buffer[i] != dtmf_digit_map[w].character))
		{
			w++;
		}
		plci->internal_req_buffer[5 + i] = (w < DTMF_DIGIT_MAP_ENTRIES) ?
			dtmf_digit_map[w].code : DTMF_DIGIT_TONE_CODE_STAR;
	}
	plci->NData[0].PLength = 5 + digit_count;
	plci->NData[0].P = plci->internal_req_buffer;
	plci->NL.X = plci->NData;
	plci->NL.ReqCh = 0;
	plci->NL.Req = plci->nl_req = (byte) N_UDATA;
	plci->adapter->request(&plci->NL);
}


static void dtmf_rec_clear_config(PLCI *plci)
{

	dbug(1, dprintf("[%06lx] %s,%d: dtmf_rec_clear_config",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__));

	plci->dtmf_rec_active = 0;
	plci->dtmf_rec_pulse_ms = 0;
	plci->dtmf_rec_pause_ms = 0;

	capidtmf_init(&(plci->capidtmf_state), plci->adapter->u_law);

}


static void dtmf_send_clear_config(PLCI *plci)
{

	dbug(1, dprintf("[%06lx] %s,%d: dtmf_send_clear_config",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__));

	plci->dtmf_send_requests = 0;
	plci->dtmf_send_pulse_ms = 0;
	plci->dtmf_send_pause_ms = 0;
}


static void dtmf_prepare_switch(dword Id, PLCI *plci)
{

	dbug(1, dprintf("[%06lx] %s,%d: dtmf_prepare_switch",
			UnMapId(Id), (char *)(FILE_), __LINE__));

	while (plci->dtmf_send_requests != 0)
		dtmf_confirmation(Id, plci);
}


static word dtmf_save_config(dword Id, PLCI *plci, byte Rc)
{

	dbug(1, dprintf("[%06lx] %s,%d: dtmf_save_config %02x %d",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));

	return (GOOD);
}


static word dtmf_restore_config(dword Id, PLCI *plci, byte Rc)
{
	word Info;

	dbug(1, dprintf("[%06lx] %s,%d: dtmf_restore_config %02x %d",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));

	Info = GOOD;
	if (plci->B1_facilities & B1_FACILITY_DTMFR)
	{
		switch (plci->adjust_b_state)
		{
		case ADJUST_B_RESTORE_DTMF_1:
			plci->internal_command = plci->adjust_b_command;
			if (plci_nl_busy(plci))
			{
				plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1;
				break;
			}
			dtmf_enable_receiver(plci, plci->dtmf_rec_active);
			plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_2;
			break;
		case ADJUST_B_RESTORE_DTMF_2:
			if ((Rc != OK) && (Rc != OK_FC))
			{
				dbug(1, dprintf("[%06lx] %s,%d: Reenable DTMF receiver failed %02x",
						UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
				Info = _WRONG_STATE;
				break;
			}
			break;
		}
	}
	return (Info);
}


static void dtmf_command(dword Id, PLCI *plci, byte Rc)
{
	word internal_command, Info;
	byte mask;
	byte result[4];

	dbug(1, dprintf("[%06lx] %s,%d: dtmf_command %02x %04x %04x %d %d %d %d",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command,
			plci->dtmf_cmd, plci->dtmf_rec_pulse_ms, plci->dtmf_rec_pause_ms,
			plci->dtmf_send_pulse_ms, plci->dtmf_send_pause_ms));

	Info = GOOD;
	result[0] = 2;
	PUT_WORD(&result[1], DTMF_SUCCESS);
	internal_command = plci->internal_command;
	plci->internal_command = 0;
	mask = 0x01;
	switch (plci->dtmf_cmd)
	{

	case DTMF_LISTEN_TONE_START:
		mask <<= 1;
	case DTMF_LISTEN_MF_START:
		mask <<= 1;

	case DTMF_LISTEN_START:
		switch (internal_command)
		{
		default:
			adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities |
								  B1_FACILITY_DTMFR), DTMF_COMMAND_1);
		case DTMF_COMMAND_1:
			if (adjust_b_process(Id, plci, Rc) != GOOD)
			{
				dbug(1, dprintf("[%06lx] %s,%d: Load DTMF failed",
						UnMapId(Id), (char *)(FILE_), __LINE__));
				Info = _FACILITY_NOT_SUPPORTED;
				break;
			}
			if (plci->internal_command)
				return;
		case DTMF_COMMAND_2:
			if (plci_nl_busy(plci))
			{
				plci->internal_command = DTMF_COMMAND_2;
				return;
			}
			plci->internal_command = DTMF_COMMAND_3;
			dtmf_enable_receiver(plci, (byte)(plci->dtmf_rec_active | mask));
			return;
		case DTMF_COMMAND_3:
			if ((Rc != OK) && (Rc != OK_FC))
			{
				dbug(1, dprintf("[%06lx] %s,%d: Enable DTMF receiver failed %02x",
						UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
				Info = _FACILITY_NOT_SUPPORTED;
				break;
			}

			plci->tone_last_indication_code = DTMF_SIGNAL_NO_TONE;

			plci->dtmf_rec_active |= mask;
			break;
		}
		break;


	case DTMF_LISTEN_TONE_STOP:
		mask <<= 1;
	case DTMF_LISTEN_MF_STOP:
		mask <<= 1;

	case DTMF_LISTEN_STOP:
		switch (internal_command)
		{
		default:
			plci->dtmf_rec_active &= ~mask;
			if (plci->dtmf_rec_active)
				break;
/*
  case DTMF_COMMAND_1:
  if (plci->dtmf_rec_active)
  {
  if (plci_nl_busy (plci))
  {
  plci->internal_command = DTMF_COMMAND_1;
  return;
  }
  plci->dtmf_rec_active &= ~mask;
  plci->internal_command = DTMF_COMMAND_2;
  dtmf_enable_receiver (plci, false);
  return;
  }
  Rc = OK;
  case DTMF_COMMAND_2:
  if ((Rc != OK) && (Rc != OK_FC))
  {
  dbug (1, dprintf("[%06lx] %s,%d: Disable DTMF receiver failed %02x",
  UnMapId (Id), (char far *)(FILE_), __LINE__, Rc));
  Info = _FACILITY_NOT_SUPPORTED;
  break;
  }
*/
			adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities &
								  ~(B1_FACILITY_DTMFX | B1_FACILITY_DTMFR)), DTMF_COMMAND_3);
		case DTMF_COMMAND_3:
			if (adjust_b_process(Id, plci, Rc) != GOOD)
			{
				dbug(1, dprintf("[%06lx] %s,%d: Unload DTMF failed",
						UnMapId(Id), (char *)(FILE_), __LINE__));
				Info = _FACILITY_NOT_SUPPORTED;
				break;
			}
			if (plci->internal_command)
				return;
			break;
		}
		break;


	case DTMF_SEND_TONE:
		mask <<= 1;
	case DTMF_SEND_MF:
		mask <<= 1;

	case DTMF_DIGITS_SEND:
		switch (internal_command)
		{
		default:
			adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities |
								  ((plci->dtmf_parameter_length != 0) ? B1_FACILITY_DTMFX | B1_FACILITY_DTMFR : B1_FACILITY_DTMFX)),
					   DTMF_COMMAND_1);
		case DTMF_COMMAND_1:
			if (adjust_b_process(Id, plci, Rc) != GOOD)
			{
				dbug(1, dprintf("[%06lx] %s,%d: Load DTMF failed",
						UnMapId(Id), (char *)(FILE_), __LINE__));
				Info = _FACILITY_NOT_SUPPORTED;
				break;
			}
			if (plci->internal_command)
				return;
		case DTMF_COMMAND_2:
			if (plci_nl_busy(plci))
			{
				plci->internal_command = DTMF_COMMAND_2;
				return;
			}
			plci->dtmf_msg_number_queue[(plci->dtmf_send_requests)++] = plci->number;
			plci->internal_command = DTMF_COMMAND_3;
			dtmf_send_digits(plci, &plci->saved_msg.parms[3].info[1], plci->saved_msg.parms[3].length);
			return;
		case DTMF_COMMAND_3:
			if ((Rc != OK) && (Rc != OK_FC))
			{
				dbug(1, dprintf("[%06lx] %s,%d: Send DTMF digits failed %02x",
						UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
				if (plci->dtmf_send_requests != 0)
					(plci->dtmf_send_requests)--;
				Info = _FACILITY_NOT_SUPPORTED;
				break;
			}
			return;
		}
		break;
	}
	sendf(plci->appl, _FACILITY_R | CONFIRM, Id & 0xffffL, plci->number,
	      "wws", Info, SELECTOR_DTMF, result);
}


static byte dtmf_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL   *appl, API_PARSE *msg)
{
	word Info;
	word i, j;
	byte mask;
	API_PARSE dtmf_parms[5];
	byte result[40];

	dbug(1, dprintf("[%06lx] %s,%d: dtmf_request",
			UnMapId(Id), (char *)(FILE_), __LINE__));

	Info = GOOD;
	result[0] = 2;
	PUT_WORD(&result[1], DTMF_SUCCESS);
	if (!(a->profile.Global_Options & GL_DTMF_SUPPORTED))
	{
		dbug(1, dprintf("[%06lx] %s,%d: Facility not supported",
				UnMapId(Id), (char *)(FILE_), __LINE__));
		Info = _FACILITY_NOT_SUPPORTED;
	}
	else if (api_parse(&msg[1].info[1], msg[1].length, "w", dtmf_parms))
	{
		dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
				UnMapId(Id), (char *)(FILE_), __LINE__));
		Info = _WRONG_MESSAGE_FORMAT;
	}

	else if ((GET_WORD(dtmf_parms[0].info) == DTMF_GET_SUPPORTED_DETECT_CODES)
		 || (GET_WORD(dtmf_parms[0].info) == DTMF_GET_SUPPORTED_SEND_CODES))
	{
		if (!((a->requested_options_table[appl->Id - 1])
		      & (1L << PRIVATE_DTMF_TONE)))
		{
			dbug(1, dprintf("[%06lx] %s,%d: DTMF unknown request %04x",
					UnMapId(Id), (char *)(FILE_), __LINE__, GET_WORD(dtmf_parms[0].info)));
			PUT_WORD(&result[1], DTMF_UNKNOWN_REQUEST);
		}
		else
		{
			for (i = 0; i < 32; i++)
				result[4 + i] = 0;
			if (GET_WORD(dtmf_parms[0].info) == DTMF_GET_SUPPORTED_DETECT_CODES)
			{
				for (i = 0; i < DTMF_DIGIT_MAP_ENTRIES; i++)
				{
					if (dtmf_digit_map[i].listen_mask != 0)
						result[4 + (dtmf_digit_map[i].character >> 3)] |= (1 << (dtmf_digit_map[i].character & 0x7));
				}
			}
			else
			{
				for (i = 0; i < DTMF_DIGIT_MAP_ENTRIES; i++)
				{
					if (dtmf_digit_map[i].send_mask != 0)
						result[4 + (dtmf_digit_map[i].character >> 3)] |= (1 << (dtmf_digit_map[i].character & 0x7));
				}
			}
			result[0] = 3 + 32;
			result[3] = 32;
		}
	}

	else if (plci == NULL)
	{
		dbug(1, dprintf("[%06lx] %s,%d: Wrong PLCI",
				UnMapId(Id), (char *)(FILE_), __LINE__));
		Info = _WRONG_IDENTIFIER;
	}
	else
	{
		if (!plci->State
		    || !plci->NL.Id || plci->nl_remove_id)
		{
			dbug(1, dprintf("[%06lx] %s,%d: Wrong state",
					UnMapId(Id), (char *)(FILE_), __LINE__));
			Info = _WRONG_STATE;
		}
		else
		{
			plci->command = 0;
			plci->dtmf_cmd = GET_WORD(dtmf_parms[0].info);
			mask = 0x01;
			switch (plci->dtmf_cmd)
			{

			case DTMF_LISTEN_TONE_START:
			case DTMF_LISTEN_TONE_STOP:
				mask <<= 1;
			case DTMF_LISTEN_MF_START:
			case DTMF_LISTEN_MF_STOP:
				mask <<= 1;
				if (!((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[appl->Id - 1])
				      & (1L << PRIVATE_DTMF_TONE)))
				{
					dbug(1, dprintf("[%06lx] %s,%d: DTMF unknown request %04x",
							UnMapId(Id), (char *)(FILE_), __LINE__, GET_WORD(dtmf_parms[0].info)));
					PUT_WORD(&result[1], DTMF_UNKNOWN_REQUEST);
					break;
				}

			case DTMF_LISTEN_START:
			case DTMF_LISTEN_STOP:
				if (!(a->manufacturer_features & MANUFACTURER_FEATURE_HARDDTMF)
				    && !(a->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE))
				{
					dbug(1, dprintf("[%06lx] %s,%d: Facility not supported",
							UnMapId(Id), (char *)(FILE_), __LINE__));
					Info = _FACILITY_NOT_SUPPORTED;
					break;
				}
				if (mask & DTMF_LISTEN_ACTIVE_FLAG)
				{
					if (api_parse(&msg[1].info[1], msg[1].length, "wwws", dtmf_parms))
					{
						plci->dtmf_rec_pulse_ms = 0;
						plci->dtmf_rec_pause_ms = 0;
					}
					else
					{
						plci->dtmf_rec_pulse_ms = GET_WORD(dtmf_parms[1].info);
						plci->dtmf_rec_pause_ms = GET_WORD(dtmf_parms[2].info);
					}
				}
				start_internal_command(Id, plci, dtmf_command);
				return (false);


			case DTMF_SEND_TONE:
				mask <<= 1;
			case DTMF_SEND_MF:
				mask <<= 1;
				if (!((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[appl->Id - 1])
				      & (1L << PRIVATE_DTMF_TONE)))
				{
					dbug(1, dprintf("[%06lx] %s,%d: DTMF unknown request %04x",
							UnMapId(Id), (char *)(FILE_), __LINE__, GET_WORD(dtmf_parms[0].info)));
					PUT_WORD(&result[1], DTMF_UNKNOWN_REQUEST);
					break;
				}

			case DTMF_DIGITS_SEND:
				if (api_parse(&msg[1].info[1], msg[1].length, "wwws", dtmf_parms))
				{
					dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
							UnMapId(Id), (char *)(FILE_), __LINE__));
					Info = _WRONG_MESSAGE_FORMAT;
					break;
				}
				if (mask & DTMF_LISTEN_ACTIVE_FLAG)
				{
					plci->dtmf_send_pulse_ms = GET_WORD(dtmf_parms[1].info);
					plci->dtmf_send_pause_ms = GET_WORD(dtmf_parms[2].info);
				}
				i = 0;
				j = 0;
				while ((i < dtmf_parms[3].length) && (j < DTMF_DIGIT_MAP_ENTRIES))
				{
					j = 0;
					while ((j < DTMF_DIGIT_MAP_ENTRIES)
					       && ((dtmf_parms[3].info[i + 1] != dtmf_digit_map[j].character)
						   || ((dtmf_digit_map[j].send_mask & mask) == 0)))
					{
						j++;
					}
					i++;
				}
				if (j == DTMF_DIGIT_MAP_ENTRIES)
				{
					dbug(1, dprintf("[%06lx] %s,%d: Incorrect DTMF digit %02x",
							UnMapId(Id), (char *)(FILE_), __LINE__, dtmf_parms[3].info[i]));
					PUT_WORD(&result[1], DTMF_INCORRECT_DIGIT);
					break;
				}
				if (plci->dtmf_send_requests >= ARRAY_SIZE(plci->dtmf_msg_number_queue))
				{
					dbug(1, dprintf("[%06lx] %s,%d: DTMF request overrun",
							UnMapId(Id), (char *)(FILE_), __LINE__));
					Info = _WRONG_STATE;
					break;
				}
				api_save_msg(dtmf_parms, "wwws", &plci->saved_msg);
				start_internal_command(Id, plci, dtmf_command);
				return (false);

			default:
				dbug(1, dprintf("[%06lx] %s,%d: DTMF unknown request %04x",
						UnMapId(Id), (char *)(FILE_), __LINE__, plci->dtmf_cmd));
				PUT_WORD(&result[1], DTMF_UNKNOWN_REQUEST);
			}
		}
	}
	sendf(appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
	      "wws", Info, SELECTOR_DTMF, result);
	return (false);
}


static void dtmf_confirmation(dword Id, PLCI *plci)
{
	word i;
	byte result[4];

	dbug(1, dprintf("[%06lx] %s,%d: dtmf_confirmation",
			UnMapId(Id), (char *)(FILE_), __LINE__));

	result[0] = 2;
	PUT_WORD(&result[1], DTMF_SUCCESS);
	if (plci->dtmf_send_requests != 0)
	{
		sendf(plci->appl, _FACILITY_R | CONFIRM, Id & 0xffffL, plci->dtmf_msg_number_queue[0],
		      "wws", GOOD, SELECTOR_DTMF, result);
		(plci->dtmf_send_requests)--;
		for (i = 0; i < plci->dtmf_send_requests; i++)
			plci->dtmf_msg_number_queue[i] = plci->dtmf_msg_number_queue[i + 1];
	}
}


static void dtmf_indication(dword Id, PLCI *plci, byte *msg, word length)
{
	word i, j, n;

	dbug(1, dprintf("[%06lx] %s,%d: dtmf_indication",
			UnMapId(Id), (char *)(FILE_), __LINE__));

	n = 0;
	for (i = 1; i < length; i++)
	{
		j = 0;
		while ((j < DTMF_DIGIT_MAP_ENTRIES)
		       && ((msg[i] != dtmf_digit_map[j].code)
			   || ((dtmf_digit_map[j].listen_mask & plci->dtmf_rec_active) == 0)))
		{
			j++;
		}
		if (j < DTMF_DIGIT_MAP_ENTRIES)
		{

			if ((dtmf_digit_map[j].listen_mask & DTMF_TONE_LISTEN_ACTIVE_FLAG)
			    && (plci->tone_last_indication_code == DTMF_SIGNAL_NO_TONE)
			    && (dtmf_digit_map[j].character != DTMF_SIGNAL_UNIDENTIFIED_TONE))
			{
				if (n + 1 == i)
				{
					for (i = length; i > n + 1; i--)
						msg[i] = msg[i - 1];
					length++;
					i++;
				}
				msg[++n] = DTMF_SIGNAL_UNIDENTIFIED_TONE;
			}
			plci->tone_last_indication_code = dtmf_digit_map[j].character;

			msg[++n] = dtmf_digit_map[j].character;
		}
	}
	if (n != 0)
	{
		msg[0] = (byte) n;
		sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "wS", SELECTOR_DTMF, msg);
	}
}


/*------------------------------------------------------------------*/
/* DTMF parameters                                                  */
/*------------------------------------------------------------------*/

static void dtmf_parameter_write(PLCI *plci)
{
	word i;
	byte parameter_buffer[DTMF_PARAMETER_BUFFER_SIZE + 2];

	dbug(1, dprintf("[%06lx] %s,%d: dtmf_parameter_write",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__));

	parameter_buffer[0] = plci->dtmf_parameter_length + 1;
	parameter_buffer[1] = DSP_CTRL_SET_DTMF_PARAMETERS;
	for (i = 0; i < plci->dtmf_parameter_length; i++)
		parameter_buffer[2 + i] = plci->dtmf_parameter_buffer[i];
	add_p(plci, FTY, parameter_buffer);
	sig_req(plci, TEL_CTRL, 0);
	send_req(plci);
}


static void dtmf_parameter_clear_config(PLCI *plci)
{

	dbug(1, dprintf("[%06lx] %s,%d: dtmf_parameter_clear_config",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__));

	plci->dtmf_parameter_length = 0;
}


static void dtmf_parameter_prepare_switch(dword Id, PLCI *plci)
{

	dbug(1, dprintf("[%06lx] %s,%d: dtmf_parameter_prepare_switch",
			UnMapId(Id), (char *)(FILE_), __LINE__));

}


static word dtmf_parameter_save_config(dword Id, PLCI *plci, byte Rc)
{

	dbug(1, dprintf("[%06lx] %s,%d: dtmf_parameter_save_config %02x %d",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));

	return (GOOD);
}


static word dtmf_parameter_restore_config(dword Id, PLCI *plci, byte Rc)
{
	word Info;

	dbug(1, dprintf("[%06lx] %s,%d: dtmf_parameter_restore_config %02x %d",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));

	Info = GOOD;
	if ((plci->B1_facilities & B1_FACILITY_DTMFR)
	    && (plci->dtmf_parameter_length != 0))
	{
		switch (plci->adjust_b_state)
		{
		case ADJUST_B_RESTORE_DTMF_PARAMETER_1:
			plci->internal_command = plci->adjust_b_command;
			if (plci->sig_req)
			{
				plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_PARAMETER_1;
				break;
			}
			dtmf_parameter_write(plci);
			plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_PARAMETER_2;
			break;
		case ADJUST_B_RESTORE_DTMF_PARAMETER_2:
			if ((Rc != OK) && (Rc != OK_FC))
			{
				dbug(1, dprintf("[%06lx] %s,%d: Restore DTMF parameters failed %02x",
						UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
				Info = _WRONG_STATE;
				break;
			}
			break;
		}
	}
	return (Info);
}


/*------------------------------------------------------------------*/
/* Line interconnect facilities                                     */
/*------------------------------------------------------------------*/


LI_CONFIG   *li_config_table;
word li_total_channels;


/*------------------------------------------------------------------*/
/* translate a CHI information element to a channel number          */
/* returns 0xff - any channel                                       */
/*         0xfe - chi wrong coding                                  */
/*         0xfd - D-channel                                         */
/*         0x00 - no channel                                        */
/*         else channel number / PRI: timeslot                      */
/* if channels is provided we accept more than one channel.         */
/*------------------------------------------------------------------*/

static byte chi_to_channel(byte *chi, dword *pchannelmap)
{
	int p;
	int i;
	dword map;
	byte excl;
	byte ofs;
	byte ch;

	if (pchannelmap) *pchannelmap = 0;
	if (!chi[0]) return 0xff;
	excl = 0;

	if (chi[1] & 0x20) {
		if (chi[0] == 1 && chi[1] == 0xac) return 0xfd; /* exclusive d-channel */
		for (i = 1; i < chi[0] && !(chi[i] & 0x80); i++);
		if (i == chi[0] || !(chi[i] & 0x80)) return 0xfe;
		if ((chi[1] | 0xc8) != 0xe9) return 0xfe;
		if (chi[1] & 0x08) excl = 0x40;

		/* int. id present */
		if (chi[1] & 0x40) {
			p = i + 1;
			for (i = p; i < chi[0] && !(chi[i] & 0x80); i++);
			if (i == chi[0] || !(chi[i] & 0x80)) return 0xfe;
		}

		/* coding standard, Number/Map, Channel Type */
		p = i + 1;
		for (i = p; i < chi[0] && !(chi[i] & 0x80); i++);
		if (i == chi[0] || !(chi[i] & 0x80)) return 0xfe;
		if ((chi[p] | 0xd0) != 0xd3) return 0xfe;

		/* Number/Map */
		if (chi[p] & 0x10) {

			/* map */
			if ((chi[0] - p) == 4) ofs = 0;
			else if ((chi[0] - p) == 3) ofs = 1;
			else return 0xfe;
			ch = 0;
			map = 0;
			for (i = 0; i < 4 && p < chi[0]; i++) {
				p++;
				ch += 8;
				map <<= 8;
				if (chi[p]) {
					for (ch = 0; !(chi[p] & (1 << ch)); ch++);
					map |= chi[p];
				}
			}
			ch += ofs;
			map <<= ofs;
		}
		else {

			/* number */
			p = i + 1;
			ch = chi[p] & 0x3f;
			if (pchannelmap) {
				if ((byte)(chi[0] - p) > 30) return 0xfe;
				map = 0;
				for (i = p; i <= chi[0]; i++) {
					if ((chi[i] & 0x7f) > 31) return 0xfe;
					map |= (1L << (chi[i] & 0x7f));
				}
			}
			else {
				if (p != chi[0]) return 0xfe;
				if (ch > 31) return 0xfe;
				map = (1L << ch);
			}
			if (chi[p] & 0x40) return 0xfe;
		}
		if (pchannelmap) *pchannelmap = map;
		else if (map != ((dword)(1L << ch))) return 0xfe;
		return (byte)(excl | ch);
	}
	else {  /* not PRI */
		for (i = 1; i < chi[0] && !(chi[i] & 0x80); i++);
		if (i != chi[0] || !(chi[i] & 0x80)) return 0xfe;
		if (chi[1] & 0x08) excl = 0x40;

		switch (chi[1] | 0x98) {
		case 0x98: return 0;
		case 0x99:
			if (pchannelmap) *pchannelmap = 2;
			return excl | 1;
		case 0x9a:
			if (pchannelmap) *pchannelmap = 4;
			return excl | 2;
		case 0x9b: return 0xff;
		case 0x9c: return 0xfd; /* d-ch */
		default: return 0xfe;
		}
	}
}


static void mixer_set_bchannel_id_esc(PLCI *plci, byte bchannel_id)
{
	DIVA_CAPI_ADAPTER *a;
	PLCI *splci;
	byte old_id;

	a = plci->adapter;
	old_id = plci->li_bchannel_id;
	if (a->li_pri)
	{
		if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci))
			li_config_table[a->li_base + (old_id - 1)].plci = NULL;
		plci->li_bchannel_id = (bchannel_id & 0x1f) + 1;
		if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL)
			li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci;
	}
	else
	{
		if (((bchannel_id & 0x03) == 1) || ((bchannel_id & 0x03) == 2))
		{
			if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci))
				li_config_table[a->li_base + (old_id - 1)].plci = NULL;
			plci->li_bchannel_id = bchannel_id & 0x03;
			if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI != plci) && (a->AdvSignalPLCI->tel == ADV_VOICE))
			{
				splci = a->AdvSignalPLCI;
				if (li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci == NULL)
				{
					if ((splci->li_bchannel_id != 0)
					    && (li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci == splci))
					{
						li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci = NULL;
					}
					splci->li_bchannel_id = 3 - plci->li_bchannel_id;
					li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci = splci;
					dbug(1, dprintf("[%06lx] %s,%d: adv_voice_set_bchannel_id_esc %d",
							(dword)((splci->Id << 8) | UnMapController(splci->adapter->Id)),
							(char *)(FILE_), __LINE__, splci->li_bchannel_id));
				}
			}
			if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL)
				li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci;
		}
	}
	if ((old_id == 0) && (plci->li_bchannel_id != 0)
	    && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
	{
		mixer_clear_config(plci);
	}
	dbug(1, dprintf("[%06lx] %s,%d: mixer_set_bchannel_id_esc %d %d",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__, bchannel_id, plci->li_bchannel_id));
}


static void mixer_set_bchannel_id(PLCI *plci, byte *chi)
{
	DIVA_CAPI_ADAPTER *a;
	PLCI *splci;
	byte ch, old_id;

	a = plci->adapter;
	old_id = plci->li_bchannel_id;
	ch = chi_to_channel(chi, NULL);
	if (!(ch & 0x80))
	{
		if (a->li_pri)
		{
			if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci))
				li_config_table[a->li_base + (old_id - 1)].plci = NULL;
			plci->li_bchannel_id = (ch & 0x1f) + 1;
			if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL)
				li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci;
		}
		else
		{
			if (((ch & 0x1f) == 1) || ((ch & 0x1f) == 2))
			{
				if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci))
					li_config_table[a->li_base + (old_id - 1)].plci = NULL;
				plci->li_bchannel_id = ch & 0x1f;
				if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI != plci) && (a->AdvSignalPLCI->tel == ADV_VOICE))
				{
					splci = a->AdvSignalPLCI;
					if (li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci == NULL)
					{
						if ((splci->li_bchannel_id != 0)
						    && (li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci == splci))
						{
							li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci = NULL;
						}
						splci->li_bchannel_id = 3 - plci->li_bchannel_id;
						li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci = splci;
						dbug(1, dprintf("[%06lx] %s,%d: adv_voice_set_bchannel_id %d",
								(dword)((splci->Id << 8) | UnMapController(splci->adapter->Id)),
								(char *)(FILE_), __LINE__, splci->li_bchannel_id));
					}
				}
				if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL)
					li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci;
			}
		}
	}
	if ((old_id == 0) && (plci->li_bchannel_id != 0)
	    && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
	{
		mixer_clear_config(plci);
	}
	dbug(1, dprintf("[%06lx] %s,%d: mixer_set_bchannel_id %02x %d",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__, ch, plci->li_bchannel_id));
}


#define MIXER_MAX_DUMP_CHANNELS 34

static void mixer_calculate_coefs(DIVA_CAPI_ADAPTER *a)
{
	static char hex_digit_table[0x10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
	word n, i, j;
	char *p;
	char hex_line[2 * MIXER_MAX_DUMP_CHANNELS + MIXER_MAX_DUMP_CHANNELS / 8 + 4];

	dbug(1, dprintf("[%06lx] %s,%d: mixer_calculate_coefs",
			(dword)(UnMapController(a->Id)), (char *)(FILE_), __LINE__));

	for (i = 0; i < li_total_channels; i++)
	{
		li_config_table[i].channel &= LI_CHANNEL_ADDRESSES_SET;
		if (li_config_table[i].chflags != 0)
			li_config_table[i].channel |= LI_CHANNEL_INVOLVED;
		else
		{
			for (j = 0; j < li_total_channels; j++)
			{
				if (((li_config_table[i].flag_table[j]) != 0)
				    || ((li_config_table[j].flag_table[i]) != 0))
				{
					li_config_table[i].channel |= LI_CHANNEL_INVOLVED;
				}
				if (((li_config_table[i].flag_table[j] & LI_FLAG_CONFERENCE) != 0)
				    || ((li_config_table[j].flag_table[i] & LI_FLAG_CONFERENCE) != 0))
				{
					li_config_table[i].channel |= LI_CHANNEL_CONFERENCE;
				}
			}
		}
	}
	for (i = 0; i < li_total_channels; i++)
	{
		for (j = 0; j < li_total_channels; j++)
		{
			li_config_table[i].coef_table[j] &= ~(LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC);
			if (li_config_table[i].flag_table[j] & LI_FLAG_CONFERENCE)
				li_config_table[i].coef_table[j] |= LI_COEF_CH_CH;
		}
	}
	for (n = 0; n < li_total_channels; n++)
	{
		if (li_config_table[n].channel & LI_CHANNEL_CONFERENCE)
		{
			for (i = 0; i < li_total_channels; i++)
			{
				if (li_config_table[i].channel & LI_CHANNEL_CONFERENCE)
				{
					for (j = 0; j < li_total_channels; j++)
					{
						li_config_table[i].coef_table[j] |=
							li_config_table[i].coef_table[n] & li_config_table[n].coef_table[j];
					}
				}
			}
		}
	}
	for (i = 0; i < li_total_channels; i++)
	{
		if (li_config_table[i].channel & LI_CHANNEL_INVOLVED)
		{
			li_config_table[i].coef_table[i] &= ~LI_COEF_CH_CH;
			for (j = 0; j < li_total_channels; j++)
			{
				if (li_config_table[i].coef_table[j] & LI_COEF_CH_CH)
					li_config_table[i].flag_table[j] |= LI_FLAG_CONFERENCE;
			}
			if (li_config_table[i].flag_table[i] & LI_FLAG_CONFERENCE)
				li_config_table[i].coef_table[i] |= LI_COEF_CH_CH;
		}
	}
	for (i = 0; i < li_total_channels; i++)
	{
		if (li_config_table[i].channel & LI_CHANNEL_INVOLVED)
		{
			for (j = 0; j < li_total_channels; j++)
			{
				if (li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT)
					li_config_table[i].coef_table[j] |= LI_COEF_CH_CH;
				if (li_config_table[i].flag_table[j] & LI_FLAG_MONITOR)
					li_config_table[i].coef_table[j] |= LI_COEF_CH_PC;
				if (li_config_table[i].flag_table[j] & LI_FLAG_MIX)
					li_config_table[i].coef_table[j] |= LI_COEF_PC_CH;
				if (li_config_table[i].flag_table[j] & LI_FLAG_PCCONNECT)
					li_config_table[i].coef_table[j] |= LI_COEF_PC_PC;
			}
			if (li_config_table[i].chflags & LI_CHFLAG_MONITOR)
			{
				for (j = 0; j < li_total_channels; j++)
				{
					if (li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT)
					{
						li_config_table[i].coef_table[j] |= LI_COEF_CH_PC;
						if (li_config_table[j].chflags & LI_CHFLAG_MIX)
							li_config_table[i].coef_table[j] |= LI_COEF_PC_CH | LI_COEF_PC_PC;
					}
				}
			}
			if (li_config_table[i].chflags & LI_CHFLAG_MIX)
			{
				for (j = 0; j < li_total_channels; j++)
				{
					if (li_config_table[j].flag_table[i] & LI_FLAG_INTERCONNECT)
						li_config_table[j].coef_table[i] |= LI_COEF_PC_CH;
				}
			}
			if (li_config_table[i].chflags & LI_CHFLAG_LOOP)
			{
				for (j = 0; j < li_total_channels; j++)
				{
					if (li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT)
					{
						for (n = 0; n < li_total_channels; n++)
						{
							if (li_config_table[n].flag_table[i] & LI_FLAG_INTERCONNECT)
							{
								li_config_table[n].coef_table[j] |= LI_COEF_CH_CH;
								if (li_config_table[j].chflags & LI_CHFLAG_MIX)
								{
									li_config_table[n].coef_table[j] |= LI_COEF_PC_CH;
									if (li_config_table[n].chflags & LI_CHFLAG_MONITOR)
										li_config_table[n].coef_table[j] |= LI_COEF_CH_PC | LI_COEF_PC_PC;
								}
								else if (li_config_table[n].chflags & LI_CHFLAG_MONITOR)
									li_config_table[n].coef_table[j] |= LI_COEF_CH_PC;
							}
						}
					}
				}
			}
		}
	}
	for (i = 0; i < li_total_channels; i++)
	{
		if (li_config_table[i].channel & LI_CHANNEL_INVOLVED)
		{
			if (li_config_table[i].chflags & (LI_CHFLAG_MONITOR | LI_CHFLAG_MIX | LI_CHFLAG_LOOP))
				li_config_table[i].channel |= LI_CHANNEL_ACTIVE;
			if (li_config_table[i].chflags & LI_CHFLAG_MONITOR)
				li_config_table[i].channel |= LI_CHANNEL_RX_DATA;
			if (li_config_table[i].chflags & LI_CHFLAG_MIX)
				li_config_table[i].channel |= LI_CHANNEL_TX_DATA;
			for (j = 0; j < li_total_channels; j++)
			{
				if ((li_config_table[i].flag_table[j] &
				     (LI_FLAG_INTERCONNECT | LI_FLAG_PCCONNECT | LI_FLAG_CONFERENCE | LI_FLAG_MONITOR))
				    || (li_config_table[j].flag_table[i] &
					(LI_FLAG_INTERCONNECT | LI_FLAG_PCCONNECT | LI_FLAG_CONFERENCE | LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX)))
				{
					li_config_table[i].channel |= LI_CHANNEL_ACTIVE;
				}
				if (li_config_table[i].flag_table[j] & (LI_FLAG_PCCONNECT | LI_FLAG_MONITOR))
					li_config_table[i].channel |= LI_CHANNEL_RX_DATA;
				if (li_config_table[j].flag_table[i] & (LI_FLAG_PCCONNECT | LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX))
					li_config_table[i].channel |= LI_CHANNEL_TX_DATA;
			}
			if (!(li_config_table[i].channel & LI_CHANNEL_ACTIVE))
			{
				li_config_table[i].coef_table[i] |= LI_COEF_PC_CH | LI_COEF_CH_PC;
				li_config_table[i].channel |= LI_CHANNEL_TX_DATA | LI_CHANNEL_RX_DATA;
			}
		}
	}
	for (i = 0; i < li_total_channels; i++)
	{
		if (li_config_table[i].channel & LI_CHANNEL_INVOLVED)
		{
			j = 0;
			while ((j < li_total_channels) && !(li_config_table[i].flag_table[j] & LI_FLAG_ANNOUNCEMENT))
				j++;
			if (j < li_total_channels)
			{
				for (j = 0; j < li_total_channels; j++)
				{
					li_config_table[i].coef_table[j] &= ~(LI_COEF_CH_CH | LI_COEF_PC_CH);
					if (li_config_table[i].flag_table[j] & LI_FLAG_ANNOUNCEMENT)
						li_config_table[i].coef_table[j] |= LI_COEF_PC_CH;
				}
			}
		}
	}
	n = li_total_channels;
	if (n > MIXER_MAX_DUMP_CHANNELS)
		n = MIXER_MAX_DUMP_CHANNELS;
	p = hex_line;
	for (j = 0; j < n; j++)
	{
		if ((j & 0x7) == 0)
			*(p++) = ' ';
		*(p++) = hex_digit_table[li_config_table[j].curchnl >> 4];
		*(p++) = hex_digit_table[li_config_table[j].curchnl & 0xf];
	}
	*p = '\0';
	dbug(1, dprintf("[%06lx] CURRENT %s",
			(dword)(UnMapController(a->Id)), (char *)hex_line));
	p = hex_line;
	for (j = 0; j < n; j++)
	{
		if ((j & 0x7) == 0)
			*(p++) = ' ';
		*(p++) = hex_digit_table[li_config_table[j].channel >> 4];
		*(p++) = hex_digit_table[li_config_table[j].channel & 0xf];
	}
	*p = '\0';
	dbug(1, dprintf("[%06lx] CHANNEL %s",
			(dword)(UnMapController(a->Id)), (char *)hex_line));
	p = hex_line;
	for (j = 0; j < n; j++)
	{
		if ((j & 0x7) == 0)
			*(p++) = ' ';
		*(p++) = hex_digit_table[li_config_table[j].chflags >> 4];
		*(p++) = hex_digit_table[li_config_table[j].chflags & 0xf];
	}
	*p = '\0';
	dbug(1, dprintf("[%06lx] CHFLAG  %s",
			(dword)(UnMapController(a->Id)), (char *)hex_line));
	for (i = 0; i < n; i++)
	{
		p = hex_line;
		for (j = 0; j < n; j++)
		{
			if ((j & 0x7) == 0)
				*(p++) = ' ';
			*(p++) = hex_digit_table[li_config_table[i].flag_table[j] >> 4];
			*(p++) = hex_digit_table[li_config_table[i].flag_table[j] & 0xf];
		}
		*p = '\0';
		dbug(1, dprintf("[%06lx] FLAG[%02x]%s",
				(dword)(UnMapController(a->Id)), i, (char *)hex_line));
	}
	for (i = 0; i < n; i++)
	{
		p = hex_line;
		for (j = 0; j < n; j++)
		{
			if ((j & 0x7) == 0)
				*(p++) = ' ';
			*(p++) = hex_digit_table[li_config_table[i].coef_table[j] >> 4];
			*(p++) = hex_digit_table[li_config_table[i].coef_table[j] & 0xf];
		}
		*p = '\0';
		dbug(1, dprintf("[%06lx] COEF[%02x]%s",
				(dword)(UnMapController(a->Id)), i, (char *)hex_line));
	}
}


static struct
{
	byte mask;
	byte line_flags;
} mixer_write_prog_pri[] =
{
	{ LI_COEF_CH_CH, 0 },
	{ LI_COEF_CH_PC, MIXER_COEF_LINE_TO_PC_FLAG },
	{ LI_COEF_PC_CH, MIXER_COEF_LINE_FROM_PC_FLAG },
	{ LI_COEF_PC_PC, MIXER_COEF_LINE_TO_PC_FLAG | MIXER_COEF_LINE_FROM_PC_FLAG }
};

static struct
{
	byte from_ch;
	byte to_ch;
	byte mask;
	byte xconnect_override;
} mixer_write_prog_bri[] =
{
	{ 0, 0, LI_COEF_CH_CH, 0x01 },  /* B      to B      */
	{ 1, 0, LI_COEF_CH_CH, 0x01 },  /* Alt B  to B      */
	{ 0, 0, LI_COEF_PC_CH, 0x80 },  /* PC     to B      */
	{ 1, 0, LI_COEF_PC_CH, 0x01 },  /* Alt PC to B      */
	{ 2, 0, LI_COEF_CH_CH, 0x00 },  /* IC     to B      */
	{ 3, 0, LI_COEF_CH_CH, 0x00 },  /* Alt IC to B      */
	{ 0, 0, LI_COEF_CH_PC, 0x80 },  /* B      to PC     */
	{ 1, 0, LI_COEF_CH_PC, 0x01 },  /* Alt B  to PC     */
	{ 0, 0, LI_COEF_PC_PC, 0x01 },  /* PC     to PC     */
	{ 1, 0, LI_COEF_PC_PC, 0x01 },  /* Alt PC to PC     */
	{ 2, 0, LI_COEF_CH_PC, 0x00 },  /* IC     to PC     */
	{ 3, 0, LI_COEF_CH_PC, 0x00 },  /* Alt IC to PC     */
	{ 0, 2, LI_COEF_CH_CH, 0x00 },  /* B      to IC     */
	{ 1, 2, LI_COEF_CH_CH, 0x00 },  /* Alt B  to IC     */
	{ 0, 2, LI_COEF_PC_CH, 0x00 },  /* PC     to IC     */
	{ 1, 2, LI_COEF_PC_CH, 0x00 },  /* Alt PC to IC     */
	{ 2, 2, LI_COEF_CH_CH, 0x00 },  /* IC     to IC     */
	{ 3, 2, LI_COEF_CH_CH, 0x00 },  /* Alt IC to IC     */
	{ 1, 1, LI_COEF_CH_CH, 0x01 },  /* Alt B  to Alt B  */
	{ 0, 1, LI_COEF_CH_CH, 0x01 },  /* B      to Alt B  */
	{ 1, 1, LI_COEF_PC_CH, 0x80 },  /* Alt PC to Alt B  */
	{ 0, 1, LI_COEF_PC_CH, 0x01 },  /* PC     to Alt B  */
	{ 3, 1, LI_COEF_CH_CH, 0x00 },  /* Alt IC to Alt B  */
	{ 2, 1, LI_COEF_CH_CH, 0x00 },  /* IC     to Alt B  */
	{ 1, 1, LI_COEF_CH_PC, 0x80 },  /* Alt B  to Alt PC */
	{ 0, 1, LI_COEF_CH_PC, 0x01 },  /* B      to Alt PC */
	{ 1, 1, LI_COEF_PC_PC, 0x01 },  /* Alt PC to Alt PC */
	{ 0, 1, LI_COEF_PC_PC, 0x01 },  /* PC     to Alt PC */
	{ 3, 1, LI_COEF_CH_PC, 0x00 },  /* Alt IC to Alt PC */
	{ 2, 1, LI_COEF_CH_PC, 0x00 },  /* IC     to Alt PC */
	{ 1, 3, LI_COEF_CH_CH, 0x00 },  /* Alt B  to Alt IC */
	{ 0, 3, LI_COEF_CH_CH, 0x00 },  /* B      to Alt IC */
	{ 1, 3, LI_COEF_PC_CH, 0x00 },  /* Alt PC to Alt IC */
	{ 0, 3, LI_COEF_PC_CH, 0x00 },  /* PC     to Alt IC */
	{ 3, 3, LI_COEF_CH_CH, 0x00 },  /* Alt IC to Alt IC */
	{ 2, 3, LI_COEF_CH_CH, 0x00 }   /* IC     to Alt IC */
};

static byte mixer_swapped_index_bri[] =
{
	18,  /* B      to B      */
	19,  /* Alt B  to B      */
	20,  /* PC     to B      */
	21,  /* Alt PC to B      */
	22,  /* IC     to B      */
	23,  /* Alt IC to B      */
	24,  /* B      to PC     */
	25,  /* Alt B  to PC     */
	26,  /* PC     to PC     */
	27,  /* Alt PC to PC     */
	28,  /* IC     to PC     */
	29,  /* Alt IC to PC     */
	30,  /* B      to IC     */
	31,  /* Alt B  to IC     */
	32,  /* PC     to IC     */
	33,  /* Alt PC to IC     */
	34,  /* IC     to IC     */
	35,  /* Alt IC to IC     */
	0,   /* Alt B  to Alt B  */
	1,   /* B      to Alt B  */
	2,   /* Alt PC to Alt B  */
	3,   /* PC     to Alt B  */
	4,   /* Alt IC to Alt B  */
	5,   /* IC     to Alt B  */
	6,   /* Alt B  to Alt PC */
	7,   /* B      to Alt PC */
	8,   /* Alt PC to Alt PC */
	9,   /* PC     to Alt PC */
	10,  /* Alt IC to Alt PC */
	11,  /* IC     to Alt PC */
	12,  /* Alt B  to Alt IC */
	13,  /* B      to Alt IC */
	14,  /* Alt PC to Alt IC */
	15,  /* PC     to Alt IC */
	16,  /* Alt IC to Alt IC */
	17   /* IC     to Alt IC */
};

static struct
{
	byte mask;
	byte from_pc;
	byte to_pc;
} xconnect_write_prog[] =
{
	{ LI_COEF_CH_CH, false, false },
	{ LI_COEF_CH_PC, false, true },
	{ LI_COEF_PC_CH, true, false },
	{ LI_COEF_PC_PC, true, true }
};


static void xconnect_query_addresses(PLCI *plci)
{
	DIVA_CAPI_ADAPTER *a;
	word w, ch;
	byte *p;

	dbug(1, dprintf("[%06lx] %s,%d: xconnect_query_addresses",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__));

	a = plci->adapter;
	if (a->li_pri && ((plci->li_bchannel_id == 0)
			  || (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci != plci)))
	{
		dbug(1, dprintf("[%06x] %s,%d: Channel id wiped out",
				(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
				(char *)(FILE_), __LINE__));
		return;
	}
	p = plci->internal_req_buffer;
	ch = (a->li_pri) ? plci->li_bchannel_id - 1 : 0;
	*(p++) = UDATA_REQUEST_XCONNECT_FROM;
	w = ch;
	*(p++) = (byte) w;
	*(p++) = (byte)(w >> 8);
	w = ch | XCONNECT_CHANNEL_PORT_PC;
	*(p++) = (byte) w;
	*(p++) = (byte)(w >> 8);
	plci->NData[0].P = plci->internal_req_buffer;
	plci->NData[0].PLength = p - plci->internal_req_buffer;
	plci->NL.X = plci->NData;
	plci->NL.ReqCh = 0;
	plci->NL.Req = plci->nl_req = (byte) N_UDATA;
	plci->adapter->request(&plci->NL);
}


static void xconnect_write_coefs(PLCI *plci, word internal_command)
{

	dbug(1, dprintf("[%06lx] %s,%d: xconnect_write_coefs %04x",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__, internal_command));

	plci->li_write_command = internal_command;
	plci->li_write_channel = 0;
}


static byte xconnect_write_coefs_process(dword Id, PLCI *plci, byte Rc)
{
	DIVA_CAPI_ADAPTER *a;
	word w, n, i, j, r, s, to_ch;
	dword d;
	byte *p;
	struct xconnect_transfer_address_s   *transfer_address;
	byte ch_map[MIXER_CHANNELS_BRI];

	dbug(1, dprintf("[%06x] %s,%d: xconnect_write_coefs_process %02x %d",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->li_write_channel));

	a = plci->adapter;
	if ((plci->li_bchannel_id == 0)
	    || (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci != plci))
	{
		dbug(1, dprintf("[%06x] %s,%d: Channel id wiped out",
				UnMapId(Id), (char *)(FILE_), __LINE__));
		return (true);
	}
	i = a->li_base + (plci->li_bchannel_id - 1);
	j = plci->li_write_channel;
	p = plci->internal_req_buffer;
	if (j != 0)
	{
		if ((Rc != OK) && (Rc != OK_FC))
		{
			dbug(1, dprintf("[%06lx] %s,%d: LI write coefs failed %02x",
					UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
			return (false);
		}
	}
	if (li_config_table[i].adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
	{
		r = 0;
		s = 0;
		if (j < li_total_channels)
		{
			if (li_config_table[i].channel & LI_CHANNEL_ADDRESSES_SET)
			{
				s = ((li_config_table[i].send_b.card_address.low | li_config_table[i].send_b.card_address.high) ?
				     (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_PC | LI_COEF_PC_PC)) &
					((li_config_table[i].send_pc.card_address.low | li_config_table[i].send_pc.card_address.high) ?
					 (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_PC_CH));
			}
			r = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
			while ((j < li_total_channels)
			       && ((r == 0)
				   || (!(li_config_table[j].channel & LI_CHANNEL_ADDRESSES_SET))
				   || (!li_config_table[j].adapter->li_pri
				       && (j >= li_config_table[j].adapter->li_base + MIXER_BCHANNELS_BRI))
				   || (((li_config_table[j].send_b.card_address.low != li_config_table[i].send_b.card_address.low)
					|| (li_config_table[j].send_b.card_address.high != li_config_table[i].send_b.card_address.high))
				       && (!(a->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT)
					   || !(li_config_table[j].adapter->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT)))
				   || ((li_config_table[j].adapter->li_base != a->li_base)
				       && !(r & s &
					    ((li_config_table[j].send_b.card_address.low | li_config_table[j].send_b.card_address.high) ?
					     (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_PC_CH | LI_COEF_PC_PC)) &
					    ((li_config_table[j].send_pc.card_address.low | li_config_table[j].send_pc.card_address.high) ?
					     (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_CH_PC))))))
			{
				j++;
				if (j < li_total_channels)
					r = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
			}
		}
		if (j < li_total_channels)
		{
			plci->internal_command = plci->li_write_command;
			if (plci_nl_busy(plci))
				return (true);
			to_ch = (a->li_pri) ? plci->li_bchannel_id - 1 : 0;
			*(p++) = UDATA_REQUEST_XCONNECT_TO;
			do
			{
				if (li_config_table[j].adapter->li_base != a->li_base)
				{
					r &= s &
						((li_config_table[j].send_b.card_address.low | li_config_table[j].send_b.card_address.high) ?
						 (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_PC_CH | LI_COEF_PC_PC)) &
						((li_config_table[j].send_pc.card_address.low | li_config_table[j].send_pc.card_address.high) ?
						 (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_CH_PC));
				}
				n = 0;
				do
				{
					if (r & xconnect_write_prog[n].mask)
					{
						if (xconnect_write_prog[n].from_pc)
							transfer_address = &(li_config_table[j].send_pc);
						else
							transfer_address = &(li_config_table[j].send_b);
						d = transfer_address->card_address.low;
						*(p++) = (byte) d;
						*(p++) = (byte)(d >> 8);
						*(p++) = (byte)(d >> 16);
						*(p++) = (byte)(d >> 24);
						d = transfer_address->card_address.high;
						*(p++) = (byte) d;
						*(p++) = (byte)(d >> 8);
						*(p++) = (byte)(d >> 16);
						*(p++) = (byte)(d >> 24);
						d = transfer_address->offset;
						*(p++) = (byte) d;
						*(p++) = (byte)(d >> 8);
						*(p++) = (byte)(d >> 16);
						*(p++) = (byte)(d >> 24);
						w = xconnect_write_prog[n].to_pc ? to_ch | XCONNECT_CHANNEL_PORT_PC : to_ch;
						*(p++) = (byte) w;
						*(p++) = (byte)(w >> 8);
						w = ((li_config_table[i].coef_table[j] & xconnect_write_prog[n].mask) == 0) ? 0x01 :
							(li_config_table[i].adapter->u_law ?
							 (li_config_table[j].adapter->u_law ? 0x80 : 0x86) :
							 (li_config_table[j].adapter->u_law ? 0x7a : 0x80));
						*(p++) = (byte) w;
						*(p++) = (byte) 0;
						li_config_table[i].coef_table[j] ^= xconnect_write_prog[n].mask << 4;
					}
					n++;
				} while ((n < ARRAY_SIZE(xconnect_write_prog))
					 && ((p - plci->internal_req_buffer) + 16 < INTERNAL_REQ_BUFFER_SIZE));
				if (n == ARRAY_SIZE(xconnect_write_prog))
				{
					do
					{
						j++;
						if (j < li_total_channels)
							r = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
					} while ((j < li_total_channels)
						 && ((r == 0)
						     || (!(li_config_table[j].channel & LI_CHANNEL_ADDRESSES_SET))
						     || (!li_config_table[j].adapter->li_pri
							 && (j >= li_config_table[j].adapter->li_base + MIXER_BCHANNELS_BRI))
						     || (((li_config_table[j].send_b.card_address.low != li_config_table[i].send_b.card_address.low)
							  || (li_config_table[j].send_b.card_address.high != li_config_table[i].send_b.card_address.high))
							 && (!(a->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT)
							     || !(li_config_table[j].adapter->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT)))
						     || ((li_config_table[j].adapter->li_base != a->li_base)
							 && !(r & s &
							      ((li_config_table[j].send_b.card_address.low | li_config_table[j].send_b.card_address.high) ?
							       (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_PC_CH | LI_COEF_PC_PC)) &
							      ((li_config_table[j].send_pc.card_address.low | li_config_table[j].send_pc.card_address.high) ?
							       (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_CH_PC))))));
				}
			} while ((j < li_total_channels)
				 && ((p - plci->internal_req_buffer) + 16 < INTERNAL_REQ_BUFFER_SIZE));
		}
		else if (j == li_total_channels)
		{
			plci->internal_command = plci->li_write_command;
			if (plci_nl_busy(plci))
				return (true);
			if (a->li_pri)
			{
				*(p++) = UDATA_REQUEST_SET_MIXER_COEFS_PRI_SYNC;
				w = 0;
				if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
					w |= MIXER_FEATURE_ENABLE_TX_DATA;
				if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
					w |= MIXER_FEATURE_ENABLE_RX_DATA;
				*(p++) = (byte) w;
				*(p++) = (byte)(w >> 8);
			}
			else
			{
				*(p++) = UDATA_REQUEST_SET_MIXER_COEFS_BRI;
				w = 0;
				if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI)
				    && (ADV_VOICE_NEW_COEF_BASE + sizeof(word) <= a->adv_voice_coef_length))
				{
					w = GET_WORD(a->adv_voice_coef_buffer + ADV_VOICE_NEW_COEF_BASE);
				}
				if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
					w |= MIXER_FEATURE_ENABLE_TX_DATA;
				if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
					w |= MIXER_FEATURE_ENABLE_RX_DATA;
				*(p++) = (byte) w;
				*(p++) = (byte)(w >> 8);
				for (j = 0; j < sizeof(ch_map); j += 2)
				{
					if (plci->li_bchannel_id == 2)
					{
						ch_map[j] = (byte)(j + 1);
						ch_map[j + 1] = (byte) j;
					}
					else
					{
						ch_map[j] = (byte) j;
						ch_map[j + 1] = (byte)(j + 1);
					}
				}
				for (n = 0; n < ARRAY_SIZE(mixer_write_prog_bri); n++)
				{
					i = a->li_base + ch_map[mixer_write_prog_bri[n].to_ch];
					j = a->li_base + ch_map[mixer_write_prog_bri[n].from_ch];
					if (li_config_table[i].channel & li_config_table[j].channel & LI_CHANNEL_INVOLVED)
					{
						*p = (mixer_write_prog_bri[n].xconnect_override != 0) ?
							mixer_write_prog_bri[n].xconnect_override :
							((li_config_table[i].coef_table[j] & mixer_write_prog_bri[n].mask) ? 0x80 : 0x01);
						if ((i >= a->li_base + MIXER_BCHANNELS_BRI) || (j >= a->li_base + MIXER_BCHANNELS_BRI))
						{
							w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
							li_config_table[i].coef_table[j] ^= (w & mixer_write_prog_bri[n].mask) << 4;
						}
					}
					else
					{
						*p = 0x00;
						if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI->tel == ADV_VOICE))
						{
							w = (plci == a->AdvSignalPLCI) ? n : mixer_swapped_index_bri[n];
							if (ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w < a->adv_voice_coef_length)
								*p = a->adv_voice_coef_buffer[ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w];
						}
					}
					p++;
				}
			}
			j = li_total_channels + 1;
		}
	}
	else
	{
		if (j <= li_total_channels)
		{
			plci->internal_command = plci->li_write_command;
			if (plci_nl_busy(plci))
				return (true);
			if (j < a->li_base)
				j = a->li_base;
			if (a->li_pri)
			{
				*(p++) = UDATA_REQUEST_SET_MIXER_COEFS_PRI_SYNC;
				w = 0;
				if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
					w |= MIXER_FEATURE_ENABLE_TX_DATA;
				if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
					w |= MIXER_FEATURE_ENABLE_RX_DATA;
				*(p++) = (byte) w;
				*(p++) = (byte)(w >> 8);
				for (n = 0; n < ARRAY_SIZE(mixer_write_prog_pri); n++)
				{
					*(p++) = (byte)((plci->li_bchannel_id - 1) | mixer_write_prog_pri[n].line_flags);
					for (j = a->li_base; j < a->li_base + MIXER_CHANNELS_PRI; j++)
					{
						w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
						if (w & mixer_write_prog_pri[n].mask)
						{
							*(p++) = (li_config_table[i].coef_table[j] & mixer_write_prog_pri[n].mask) ? 0x80 : 0x01;
							li_config_table[i].coef_table[j] ^= mixer_write_prog_pri[n].mask << 4;
						}
						else
							*(p++) = 0x00;
					}
					*(p++) = (byte)((plci->li_bchannel_id - 1) | MIXER_COEF_LINE_ROW_FLAG | mixer_write_prog_pri[n].line_flags);
					for (j = a->li_base; j < a->li_base + MIXER_CHANNELS_PRI; j++)
					{
						w = ((li_config_table[j].coef_table[i] & 0xf) ^ (li_config_table[j].coef_table[i] >> 4));
						if (w & mixer_write_prog_pri[n].mask)
						{
							*(p++) = (li_config_table[j].coef_table[i] & mixer_write_prog_pri[n].mask) ? 0x80 : 0x01;
							li_config_table[j].coef_table[i] ^= mixer_write_prog_pri[n].mask << 4;
						}
						else
							*(p++) = 0x00;
					}
				}
			}
			else
			{
				*(p++) = UDATA_REQUEST_SET_MIXER_COEFS_BRI;
				w = 0;
				if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI)
				    && (ADV_VOICE_NEW_COEF_BASE + sizeof(word) <= a->adv_voice_coef_length))
				{
					w = GET_WORD(a->adv_voice_coef_buffer + ADV_VOICE_NEW_COEF_BASE);
				}
				if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
					w |= MIXER_FEATURE_ENABLE_TX_DATA;
				if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
					w |= MIXER_FEATURE_ENABLE_RX_DATA;
				*(p++) = (byte) w;
				*(p++) = (byte)(w >> 8);
				for (j = 0; j < sizeof(ch_map); j += 2)
				{
					if (plci->li_bchannel_id == 2)
					{
						ch_map[j] = (byte)(j + 1);
						ch_map[j + 1] = (byte) j;
					}
					else
					{
						ch_map[j] = (byte) j;
						ch_map[j + 1] = (byte)(j + 1);
					}
				}
				for (n = 0; n < ARRAY_SIZE(mixer_write_prog_bri); n++)
				{
					i = a->li_base + ch_map[mixer_write_prog_bri[n].to_ch];
					j = a->li_base + ch_map[mixer_write_prog_bri[n].from_ch];
					if (li_config_table[i].channel & li_config_table[j].channel & LI_CHANNEL_INVOLVED)
					{
						*p = ((li_config_table[i].coef_table[j] & mixer_write_prog_bri[n].mask) ? 0x80 : 0x01);
						w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
						li_config_table[i].coef_table[j] ^= (w & mixer_write_prog_bri[n].mask) << 4;
					}
					else
					{
						*p = 0x00;
						if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI->tel == ADV_VOICE))
						{
							w = (plci == a->AdvSignalPLCI) ? n : mixer_swapped_index_bri[n];
							if (ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w < a->adv_voice_coef_length)
								*p = a->adv_voice_coef_buffer[ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w];
						}
					}
					p++;
				}
			}
			j = li_total_channels + 1;
		}
	}
	plci->li_write_channel = j;
	if (p != plci->internal_req_buffer)
	{
		plci->NData[0].P = plci->internal_req_buffer;
		plci->NData[0].PLength = p - plci->internal_req_buffer;
		plci->NL.X = plci->NData;
		plci->NL.ReqCh = 0;
		plci->NL.Req = plci->nl_req = (byte) N_UDATA;
		plci->adapter->request(&plci->NL);
	}
	return (true);
}


static void mixer_notify_update(PLCI *plci, byte others)
{
	DIVA_CAPI_ADAPTER *a;
	word i, w;
	PLCI *notify_plci;
	byte msg[sizeof(CAPI_MSG_HEADER) + 6];

	dbug(1, dprintf("[%06lx] %s,%d: mixer_notify_update %d",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__, others));

	a = plci->adapter;
	if (a->profile.Global_Options & GL_LINE_INTERCONNECT_SUPPORTED)
	{
		if (others)
			plci->li_notify_update = true;
		i = 0;
		do
		{
			notify_plci = NULL;
			if (others)
			{
				while ((i < li_total_channels) && (li_config_table[i].plci == NULL))
					i++;
				if (i < li_total_channels)
					notify_plci = li_config_table[i++].plci;
			}
			else
			{
				if ((plci->li_bchannel_id != 0)
				    && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
				{
					notify_plci = plci;
				}
			}
			if ((notify_plci != NULL)
			    && !notify_plci->li_notify_update
			    && (notify_plci->appl != NULL)
			    && (notify_plci->State)
			    && notify_plci->NL.Id && !notify_plci->nl_remove_id)
			{
				notify_plci->li_notify_update = true;
				((CAPI_MSG *) msg)->header.length = 18;
				((CAPI_MSG *) msg)->header.appl_id = notify_plci->appl->Id;
				((CAPI_MSG *) msg)->header.command = _FACILITY_R;
				((CAPI_MSG *) msg)->header.number = 0;
				((CAPI_MSG *) msg)->header.controller = notify_plci->adapter->Id;
				((CAPI_MSG *) msg)->header.plci = notify_plci->Id;
				((CAPI_MSG *) msg)->header.ncci = 0;
				((CAPI_MSG *) msg)->info.facility_req.Selector = SELECTOR_LINE_INTERCONNECT;
				((CAPI_MSG *) msg)->info.facility_req.structs[0] = 3;
				PUT_WORD(&(((CAPI_MSG *) msg)->info.facility_req.structs[1]), LI_REQ_SILENT_UPDATE);
				((CAPI_MSG *) msg)->info.facility_req.structs[3] = 0;
				w = api_put(notify_plci->appl, (CAPI_MSG *) msg);
				if (w != _QUEUE_FULL)
				{
					if (w != 0)
					{
						dbug(1, dprintf("[%06lx] %s,%d: Interconnect notify failed %06x %d",
								(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
								(char *)(FILE_), __LINE__,
								(dword)((notify_plci->Id << 8) | UnMapController(notify_plci->adapter->Id)), w));
					}
					notify_plci->li_notify_update = false;
				}
			}
		} while (others && (notify_plci != NULL));
		if (others)
			plci->li_notify_update = false;
	}
}


static void mixer_clear_config(PLCI *plci)
{
	DIVA_CAPI_ADAPTER *a;
	word i, j;

	dbug(1, dprintf("[%06lx] %s,%d: mixer_clear_config",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__));

	plci->li_notify_update = false;
	plci->li_plci_b_write_pos = 0;
	plci->li_plci_b_read_pos = 0;
	plci->li_plci_b_req_pos = 0;
	a = plci->adapter;
	if ((plci->li_bchannel_id != 0)
	    && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
	{
		i = a->li_base + (plci->li_bchannel_id - 1);
		li_config_table[i].curchnl = 0;
		li_config_table[i].channel = 0;
		li_config_table[i].chflags = 0;
		for (j = 0; j < li_total_channels; j++)
		{
			li_config_table[j].flag_table[i] = 0;
			li_config_table[i].flag_table[j] = 0;
			li_config_table[i].coef_table[j] = 0;
			li_config_table[j].coef_table[i] = 0;
		}
		if (!a->li_pri)
		{
			li_config_table[i].coef_table[i] |= LI_COEF_CH_PC_SET | LI_COEF_PC_CH_SET;
			if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI))
			{
				i = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
				li_config_table[i].curchnl = 0;
				li_config_table[i].channel = 0;
				li_config_table[i].chflags = 0;
				for (j = 0; j < li_total_channels; j++)
				{
					li_config_table[i].flag_table[j] = 0;
					li_config_table[j].flag_table[i] = 0;
					li_config_table[i].coef_table[j] = 0;
					li_config_table[j].coef_table[i] = 0;
				}
				if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
				{
					i = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
					li_config_table[i].curchnl = 0;
					li_config_table[i].channel = 0;
					li_config_table[i].chflags = 0;
					for (j = 0; j < li_total_channels; j++)
					{
						li_config_table[i].flag_table[j] = 0;
						li_config_table[j].flag_table[i] = 0;
						li_config_table[i].coef_table[j] = 0;
						li_config_table[j].coef_table[i] = 0;
					}
				}
			}
		}
	}
}


static void mixer_prepare_switch(dword Id, PLCI *plci)
{

	dbug(1, dprintf("[%06lx] %s,%d: mixer_prepare_switch",
			UnMapId(Id), (char *)(FILE_), __LINE__));

	do
	{
		mixer_indication_coefs_set(Id, plci);
	} while (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos);
}


static word mixer_save_config(dword Id, PLCI *plci, byte Rc)
{
	DIVA_CAPI_ADAPTER *a;
	word i, j;

	dbug(1, dprintf("[%06lx] %s,%d: mixer_save_config %02x %d",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));

	a = plci->adapter;
	if ((plci->li_bchannel_id != 0)
	    && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
	{
		i = a->li_base + (plci->li_bchannel_id - 1);
		for (j = 0; j < li_total_channels; j++)
		{
			li_config_table[i].coef_table[j] &= 0xf;
			li_config_table[j].coef_table[i] &= 0xf;
		}
		if (!a->li_pri)
			li_config_table[i].coef_table[i] |= LI_COEF_CH_PC_SET | LI_COEF_PC_CH_SET;
	}
	return (GOOD);
}


static word mixer_restore_config(dword Id, PLCI *plci, byte Rc)
{
	DIVA_CAPI_ADAPTER *a;
	word Info;

	dbug(1, dprintf("[%06lx] %s,%d: mixer_restore_config %02x %d",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));

	Info = GOOD;
	a = plci->adapter;
	if ((plci->B1_facilities & B1_FACILITY_MIXER)
	    && (plci->li_bchannel_id != 0)
	    && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
	{
		switch (plci->adjust_b_state)
		{
		case ADJUST_B_RESTORE_MIXER_1:
			if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
			{
				plci->internal_command = plci->adjust_b_command;
				if (plci_nl_busy(plci))
				{
					plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_1;
					break;
				}
				xconnect_query_addresses(plci);
				plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_2;
				break;
			}
			plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_5;
			Rc = OK;
		case ADJUST_B_RESTORE_MIXER_2:
		case ADJUST_B_RESTORE_MIXER_3:
		case ADJUST_B_RESTORE_MIXER_4:
			if ((Rc != OK) && (Rc != OK_FC) && (Rc != 0))
			{
				dbug(1, dprintf("[%06lx] %s,%d: Adjust B query addresses failed %02x",
						UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
				Info = _WRONG_STATE;
				break;
			}
			if (Rc == OK)
			{
				if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_2)
					plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_3;
				else if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_4)
					plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_5;
			}
			else if (Rc == 0)
			{
				if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_2)
					plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_4;
				else if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_3)
					plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_5;
			}
			if (plci->adjust_b_state != ADJUST_B_RESTORE_MIXER_5)
			{
				plci->internal_command = plci->adjust_b_command;
				break;
			}
		case ADJUST_B_RESTORE_MIXER_5:
			xconnect_write_coefs(plci, plci->adjust_b_command);
			plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_6;
			Rc = OK;
		case ADJUST_B_RESTORE_MIXER_6:
			if (!xconnect_write_coefs_process(Id, plci, Rc))
			{
				dbug(1, dprintf("[%06lx] %s,%d: Write mixer coefs failed",
						UnMapId(Id), (char *)(FILE_), __LINE__));
				Info = _FACILITY_NOT_SUPPORTED;
				break;
			}
			if (plci->internal_command)
				break;
			plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_7;
		case ADJUST_B_RESTORE_MIXER_7:
			break;
		}
	}
	return (Info);
}


static void mixer_command(dword Id, PLCI *plci, byte Rc)
{
	DIVA_CAPI_ADAPTER *a;
	word i, internal_command;

	dbug(1, dprintf("[%06lx] %s,%d: mixer_command %02x %04x %04x",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command,
			plci->li_cmd));

	a = plci->adapter;
	internal_command = plci->internal_command;
	plci->internal_command = 0;
	switch (plci->li_cmd)
	{
	case LI_REQ_CONNECT:
	case LI_REQ_DISCONNECT:
	case LI_REQ_SILENT_UPDATE:
		switch (internal_command)
		{
		default:
			if (plci->li_channel_bits & LI_CHANNEL_INVOLVED)
			{
				adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities |
									  B1_FACILITY_MIXER), MIXER_COMMAND_1);
			}
		case MIXER_COMMAND_1:
			if (plci->li_channel_bits & LI_CHANNEL_INVOLVED)
			{
				if (adjust_b_process(Id, plci, Rc) != GOOD)
				{
					dbug(1, dprintf("[%06lx] %s,%d: Load mixer failed",
							UnMapId(Id), (char *)(FILE_), __LINE__));
					break;
				}
				if (plci->internal_command)
					return;
			}
			plci->li_plci_b_req_pos = plci->li_plci_b_write_pos;
			if ((plci->li_channel_bits & LI_CHANNEL_INVOLVED)
			    || ((get_b1_facilities(plci, plci->B1_resource) & B1_FACILITY_MIXER)
				&& (add_b1_facilities(plci, plci->B1_resource, (word)(plci->B1_facilities &
										      ~B1_FACILITY_MIXER)) == plci->B1_resource)))
			{
				xconnect_write_coefs(plci, MIXER_COMMAND_2);
			}
			else
			{
				do
				{
					mixer_indication_coefs_set(Id, plci);
				} while (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos);
			}
		case MIXER_COMMAND_2:
			if ((plci->li_channel_bits & LI_CHANNEL_INVOLVED)
			    || ((get_b1_facilities(plci, plci->B1_resource) & B1_FACILITY_MIXER)
				&& (add_b1_facilities(plci, plci->B1_resource, (word)(plci->B1_facilities &
										      ~B1_FACILITY_MIXER)) == plci->B1_resource)))
			{
				if (!xconnect_write_coefs_process(Id, plci, Rc))
				{
					dbug(1, dprintf("[%06lx] %s,%d: Write mixer coefs failed",
							UnMapId(Id), (char *)(FILE_), __LINE__));
					if (plci->li_plci_b_write_pos != plci->li_plci_b_req_pos)
					{
						do
						{
							plci->li_plci_b_write_pos = (plci->li_plci_b_write_pos == 0) ?
								LI_PLCI_B_QUEUE_ENTRIES - 1 : plci->li_plci_b_write_pos - 1;
							i = (plci->li_plci_b_write_pos == 0) ?
								LI_PLCI_B_QUEUE_ENTRIES - 1 : plci->li_plci_b_write_pos - 1;
						} while ((plci->li_plci_b_write_pos != plci->li_plci_b_req_pos)
							 && !(plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG));
					}
					break;
				}
				if (plci->internal_command)
					return;
			}
			if (!(plci->li_channel_bits & LI_CHANNEL_INVOLVED))
			{
				adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities &
									  ~B1_FACILITY_MIXER), MIXER_COMMAND_3);
			}
		case MIXER_COMMAND_3:
			if (!(plci->li_channel_bits & LI_CHANNEL_INVOLVED))
			{
				if (adjust_b_process(Id, plci, Rc) != GOOD)
				{
					dbug(1, dprintf("[%06lx] %s,%d: Unload mixer failed",
							UnMapId(Id), (char *)(FILE_), __LINE__));
					break;
				}
				if (plci->internal_command)
					return;
			}
			break;
		}
		break;
	}
	if ((plci->li_bchannel_id == 0)
	    || (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci != plci))
	{
		dbug(1, dprintf("[%06x] %s,%d: Channel id wiped out %d",
				UnMapId(Id), (char *)(FILE_), __LINE__, (int)(plci->li_bchannel_id)));
	}
	else
	{
		i = a->li_base + (plci->li_bchannel_id - 1);
		li_config_table[i].curchnl = plci->li_channel_bits;
		if (!a->li_pri && (plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI))
		{
			i = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
			li_config_table[i].curchnl = plci->li_channel_bits;
			if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
			{
				i = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
				li_config_table[i].curchnl = plci->li_channel_bits;
			}
		}
	}
}


static void li_update_connect(dword Id, DIVA_CAPI_ADAPTER *a, PLCI *plci,
			      dword plci_b_id, byte connect, dword li_flags)
{
	word i, ch_a, ch_a_v, ch_a_s, ch_b, ch_b_v, ch_b_s;
	PLCI *plci_b;
	DIVA_CAPI_ADAPTER *a_b;

	a_b = &(adapter[MapController((byte)(plci_b_id & 0x7f)) - 1]);
	plci_b = &(a_b->plci[((plci_b_id >> 8) & 0xff) - 1]);
	ch_a = a->li_base + (plci->li_bchannel_id - 1);
	if (!a->li_pri && (plci->tel == ADV_VOICE)
	    && (plci == a->AdvSignalPLCI) && (Id & EXT_CONTROLLER))
	{
		ch_a_v = ch_a + MIXER_IC_CHANNEL_BASE;
		ch_a_s = (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ?
			a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id) : ch_a_v;
	}
	else
	{
		ch_a_v = ch_a;
		ch_a_s = ch_a;
	}
	ch_b = a_b->li_base + (plci_b->li_bchannel_id - 1);
	if (!a_b->li_pri && (plci_b->tel == ADV_VOICE)
	    && (plci_b == a_b->AdvSignalPLCI) && (plci_b_id & EXT_CONTROLLER))
	{
		ch_b_v = ch_b + MIXER_IC_CHANNEL_BASE;
		ch_b_s = (a_b->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ?
			a_b->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci_b->li_bchannel_id) : ch_b_v;
	}
	else
	{
		ch_b_v = ch_b;
		ch_b_s = ch_b;
	}
	if (connect)
	{
		li_config_table[ch_a].flag_table[ch_a_v] &= ~LI_FLAG_MONITOR;
		li_config_table[ch_a].flag_table[ch_a_s] &= ~LI_FLAG_MONITOR;
		li_config_table[ch_a_v].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX);
		li_config_table[ch_a_s].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX);
	}
	li_config_table[ch_a].flag_table[ch_b_v] &= ~LI_FLAG_MONITOR;
	li_config_table[ch_a].flag_table[ch_b_s] &= ~LI_FLAG_MONITOR;
	li_config_table[ch_b_v].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX);
	li_config_table[ch_b_s].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX);
	if (ch_a_v == ch_b_v)
	{
		li_config_table[ch_a_v].flag_table[ch_b_v] &= ~LI_FLAG_CONFERENCE;
		li_config_table[ch_a_s].flag_table[ch_b_s] &= ~LI_FLAG_CONFERENCE;
	}
	else
	{
		if (li_config_table[ch_a_v].flag_table[ch_b_v] & LI_FLAG_CONFERENCE)
		{
			for (i = 0; i < li_total_channels; i++)
			{
				if (i != ch_a_v)
					li_config_table[ch_a_v].flag_table[i] &= ~LI_FLAG_CONFERENCE;
			}
		}
		if (li_config_table[ch_a_s].flag_table[ch_b_v] & LI_FLAG_CONFERENCE)
		{
			for (i = 0; i < li_total_channels; i++)
			{
				if (i != ch_a_s)
					li_config_table[ch_a_s].flag_table[i] &= ~LI_FLAG_CONFERENCE;
			}
		}
		if (li_config_table[ch_b_v].flag_table[ch_a_v] & LI_FLAG_CONFERENCE)
		{
			for (i = 0; i < li_total_channels; i++)
			{
				if (i != ch_a_v)
					li_config_table[i].flag_table[ch_a_v] &= ~LI_FLAG_CONFERENCE;
			}
		}
		if (li_config_table[ch_b_v].flag_table[ch_a_s] & LI_FLAG_CONFERENCE)
		{
			for (i = 0; i < li_total_channels; i++)
			{
				if (i != ch_a_s)
					li_config_table[i].flag_table[ch_a_s] &= ~LI_FLAG_CONFERENCE;
			}
		}
	}
	if (li_flags & LI_FLAG_CONFERENCE_A_B)
	{
		li_config_table[ch_b_v].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE;
		li_config_table[ch_b_s].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE;
		li_config_table[ch_b_v].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE;
		li_config_table[ch_b_s].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE;
	}
	if (li_flags & LI_FLAG_CONFERENCE_B_A)
	{
		li_config_table[ch_a_v].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE;
		li_config_table[ch_a_v].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE;
		li_config_table[ch_a_s].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE;
		li_config_table[ch_a_s].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE;
	}
	if (li_flags & LI_FLAG_MONITOR_A)
	{
		li_config_table[ch_a].flag_table[ch_a_v] |= LI_FLAG_MONITOR;
		li_config_table[ch_a].flag_table[ch_a_s] |= LI_FLAG_MONITOR;
	}
	if (li_flags & LI_FLAG_MONITOR_B)
	{
		li_config_table[ch_a].flag_table[ch_b_v] |= LI_FLAG_MONITOR;
		li_config_table[ch_a].flag_table[ch_b_s] |= LI_FLAG_MONITOR;
	}
	if (li_flags & LI_FLAG_ANNOUNCEMENT_A)
	{
		li_config_table[ch_a_v].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT;
		li_config_table[ch_a_s].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT;
	}
	if (li_flags & LI_FLAG_ANNOUNCEMENT_B)
	{
		li_config_table[ch_b_v].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT;
		li_config_table[ch_b_s].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT;
	}
	if (li_flags & LI_FLAG_MIX_A)
	{
		li_config_table[ch_a_v].flag_table[ch_a] |= LI_FLAG_MIX;
		li_config_table[ch_a_s].flag_table[ch_a] |= LI_FLAG_MIX;
	}
	if (li_flags & LI_FLAG_MIX_B)
	{
		li_config_table[ch_b_v].flag_table[ch_a] |= LI_FLAG_MIX;
		li_config_table[ch_b_s].flag_table[ch_a] |= LI_FLAG_MIX;
	}
	if (ch_a_v != ch_a_s)
	{
		li_config_table[ch_a_v].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE;
		li_config_table[ch_a_s].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE;
	}
	if (ch_b_v != ch_b_s)
	{
		li_config_table[ch_b_v].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE;
		li_config_table[ch_b_s].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE;
	}
}


static void li2_update_connect(dword Id, DIVA_CAPI_ADAPTER *a, PLCI *plci,
			       dword plci_b_id, byte connect, dword li_flags)
{
	word ch_a, ch_a_v, ch_a_s, ch_b, ch_b_v, ch_b_s;
	PLCI *plci_b;
	DIVA_CAPI_ADAPTER *a_b;

	a_b = &(adapter[MapController((byte)(plci_b_id & 0x7f)) - 1]);
	plci_b = &(a_b->plci[((plci_b_id >> 8) & 0xff) - 1]);
	ch_a = a->li_base + (plci->li_bchannel_id - 1);
	if (!a->li_pri && (plci->tel == ADV_VOICE)
	    && (plci == a->AdvSignalPLCI) && (Id & EXT_CONTROLLER))
	{
		ch_a_v = ch_a + MIXER_IC_CHANNEL_BASE;
		ch_a_s = (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ?
			a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id) : ch_a_v;
	}
	else
	{
		ch_a_v = ch_a;
		ch_a_s = ch_a;
	}
	ch_b = a_b->li_base + (plci_b->li_bchannel_id - 1);
	if (!a_b->li_pri && (plci_b->tel == ADV_VOICE)
	    && (plci_b == a_b->AdvSignalPLCI) && (plci_b_id & EXT_CONTROLLER))
	{
		ch_b_v = ch_b + MIXER_IC_CHANNEL_BASE;
		ch_b_s = (a_b->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ?
			a_b->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci_b->li_bchannel_id) : ch_b_v;
	}
	else
	{
		ch_b_v = ch_b;
		ch_b_s = ch_b;
	}
	if (connect)
	{
		li_config_table[ch_b].flag_table[ch_b_v] &= ~LI_FLAG_MONITOR;
		li_config_table[ch_b].flag_table[ch_b_s] &= ~LI_FLAG_MONITOR;
		li_config_table[ch_b_v].flag_table[ch_b] &= ~LI_FLAG_MIX;
		li_config_table[ch_b_s].flag_table[ch_b] &= ~LI_FLAG_MIX;
		li_config_table[ch_b].flag_table[ch_b] &= ~LI_FLAG_PCCONNECT;
		li_config_table[ch_b].chflags &= ~(LI_CHFLAG_MONITOR | LI_CHFLAG_MIX | LI_CHFLAG_LOOP);
	}
	li_config_table[ch_b_v].flag_table[ch_a_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
	li_config_table[ch_b_s].flag_table[ch_a_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
	li_config_table[ch_b_v].flag_table[ch_a_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
	li_config_table[ch_b_s].flag_table[ch_a_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
	li_config_table[ch_a_v].flag_table[ch_b_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
	li_config_table[ch_a_v].flag_table[ch_b_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
	li_config_table[ch_a_s].flag_table[ch_b_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
	li_config_table[ch_a_s].flag_table[ch_b_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
	if (li_flags & LI2_FLAG_INTERCONNECT_A_B)
	{
		li_config_table[ch_b_v].flag_table[ch_a_v] |= LI_FLAG_INTERCONNECT;
		li_config_table[ch_b_s].flag_table[ch_a_v] |= LI_FLAG_INTERCONNECT;
		li_config_table[ch_b_v].flag_table[ch_a_s] |= LI_FLAG_INTERCONNECT;
		li_config_table[ch_b_s].flag_table[ch_a_s] |= LI_FLAG_INTERCONNECT;
	}
	if (li_flags & LI2_FLAG_INTERCONNECT_B_A)
	{
		li_config_table[ch_a_v].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT;
		li_config_table[ch_a_v].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT;
		li_config_table[ch_a_s].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT;
		li_config_table[ch_a_s].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT;
	}
	if (li_flags & LI2_FLAG_MONITOR_B)
	{
		li_config_table[ch_b].flag_table[ch_b_v] |= LI_FLAG_MONITOR;
		li_config_table[ch_b].flag_table[ch_b_s] |= LI_FLAG_MONITOR;
	}
	if (li_flags & LI2_FLAG_MIX_B)
	{
		li_config_table[ch_b_v].flag_table[ch_b] |= LI_FLAG_MIX;
		li_config_table[ch_b_s].flag_table[ch_b] |= LI_FLAG_MIX;
	}
	if (li_flags & LI2_FLAG_MONITOR_X)
		li_config_table[ch_b].chflags |= LI_CHFLAG_MONITOR;
	if (li_flags & LI2_FLAG_MIX_X)
		li_config_table[ch_b].chflags |= LI_CHFLAG_MIX;
	if (li_flags & LI2_FLAG_LOOP_B)
	{
		li_config_table[ch_b_v].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT;
		li_config_table[ch_b_s].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT;
		li_config_table[ch_b_v].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT;
		li_config_table[ch_b_s].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT;
	}
	if (li_flags & LI2_FLAG_LOOP_PC)
		li_config_table[ch_b].flag_table[ch_b] |= LI_FLAG_PCCONNECT;
	if (li_flags & LI2_FLAG_LOOP_X)
		li_config_table[ch_b].chflags |= LI_CHFLAG_LOOP;
	if (li_flags & LI2_FLAG_PCCONNECT_A_B)
		li_config_table[ch_b_s].flag_table[ch_a_s] |= LI_FLAG_PCCONNECT;
	if (li_flags & LI2_FLAG_PCCONNECT_B_A)
		li_config_table[ch_a_s].flag_table[ch_b_s] |= LI_FLAG_PCCONNECT;
	if (ch_a_v != ch_a_s)
	{
		li_config_table[ch_a_v].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE;
		li_config_table[ch_a_s].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE;
	}
	if (ch_b_v != ch_b_s)
	{
		li_config_table[ch_b_v].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE;
		li_config_table[ch_b_s].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE;
	}
}


static word li_check_main_plci(dword Id, PLCI *plci)
{
	if (plci == NULL)
	{
		dbug(1, dprintf("[%06lx] %s,%d: Wrong PLCI",
				UnMapId(Id), (char *)(FILE_), __LINE__));
		return (_WRONG_IDENTIFIER);
	}
	if (!plci->State
	    || !plci->NL.Id || plci->nl_remove_id
	    || (plci->li_bchannel_id == 0))
	{
		dbug(1, dprintf("[%06lx] %s,%d: Wrong state",
				UnMapId(Id), (char *)(FILE_), __LINE__));
		return (_WRONG_STATE);
	}
	li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci = plci;
	return (GOOD);
}


static PLCI *li_check_plci_b(dword Id, PLCI *plci,
			     dword plci_b_id, word plci_b_write_pos, byte *p_result)
{
	byte ctlr_b;
	PLCI *plci_b;

	if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos :
	     LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 2)
	{
		dbug(1, dprintf("[%06lx] %s,%d: LI request overrun",
				UnMapId(Id), (char *)(FILE_), __LINE__));
		PUT_WORD(p_result, _REQUEST_NOT_ALLOWED_IN_THIS_STATE);
		return (NULL);
	}
	ctlr_b = 0;
	if ((plci_b_id & 0x7f) != 0)
	{
		ctlr_b = MapController((byte)(plci_b_id & 0x7f));
		if ((ctlr_b > max_adapter) || ((ctlr_b != 0) && (adapter[ctlr_b - 1].request == NULL)))
			ctlr_b = 0;
	}
	if ((ctlr_b == 0)
	    || (((plci_b_id >> 8) & 0xff) == 0)
	    || (((plci_b_id >> 8) & 0xff) > adapter[ctlr_b - 1].max_plci))
	{
		dbug(1, dprintf("[%06lx] %s,%d: LI invalid second PLCI %08lx",
				UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id));
		PUT_WORD(p_result, _WRONG_IDENTIFIER);
		return (NULL);
	}
	plci_b = &(adapter[ctlr_b - 1].plci[((plci_b_id >> 8) & 0xff) - 1]);
	if (!plci_b->State
	    || !plci_b->NL.Id || plci_b->nl_remove_id
	    || (plci_b->li_bchannel_id == 0))
	{
		dbug(1, dprintf("[%06lx] %s,%d: LI peer in wrong state %08lx",
				UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id));
		PUT_WORD(p_result, _REQUEST_NOT_ALLOWED_IN_THIS_STATE);
		return (NULL);
	}
	li_config_table[plci_b->adapter->li_base + (plci_b->li_bchannel_id - 1)].plci = plci_b;
	if (((byte)(plci_b_id & ~EXT_CONTROLLER)) !=
	    ((byte)(UnMapController(plci->adapter->Id) & ~EXT_CONTROLLER))
	    && (!(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
		|| !(plci_b->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)))
	{
		dbug(1, dprintf("[%06lx] %s,%d: LI not on same ctrl %08lx",
				UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id));
		PUT_WORD(p_result, _WRONG_IDENTIFIER);
		return (NULL);
	}
	if (!(get_b1_facilities(plci_b, add_b1_facilities(plci_b, plci_b->B1_resource,
							  (word)(plci_b->B1_facilities | B1_FACILITY_MIXER))) & B1_FACILITY_MIXER))
	{
		dbug(1, dprintf("[%06lx] %s,%d: Interconnect peer cannot mix %d",
				UnMapId(Id), (char *)(FILE_), __LINE__, plci_b->B1_resource));
		PUT_WORD(p_result, _REQUEST_NOT_ALLOWED_IN_THIS_STATE);
		return (NULL);
	}
	return (plci_b);
}


static PLCI *li2_check_plci_b(dword Id, PLCI *plci,
			      dword plci_b_id, word plci_b_write_pos, byte *p_result)
{
	byte ctlr_b;
	PLCI *plci_b;

	if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos :
	     LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 2)
	{
		dbug(1, dprintf("[%06lx] %s,%d: LI request overrun",
				UnMapId(Id), (char *)(FILE_), __LINE__));
		PUT_WORD(p_result, _WRONG_STATE);
		return (NULL);
	}
	ctlr_b = 0;
	if ((plci_b_id & 0x7f) != 0)
	{
		ctlr_b = MapController((byte)(plci_b_id & 0x7f));
		if ((ctlr_b > max_adapter) || ((ctlr_b != 0) && (adapter[ctlr_b - 1].request == NULL)))
			ctlr_b = 0;
	}
	if ((ctlr_b == 0)
	    || (((plci_b_id >> 8) & 0xff) == 0)
	    || (((plci_b_id >> 8) & 0xff) > adapter[ctlr_b - 1].max_plci))
	{
		dbug(1, dprintf("[%06lx] %s,%d: LI invalid second PLCI %08lx",
				UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id));
		PUT_WORD(p_result, _WRONG_IDENTIFIER);
		return (NULL);
	}
	plci_b = &(adapter[ctlr_b - 1].plci[((plci_b_id >> 8) & 0xff) - 1]);
	if (!plci_b->State
	    || !plci_b->NL.Id || plci_b->nl_remove_id
	    || (plci_b->li_bchannel_id == 0)
	    || (li_config_table[plci_b->adapter->li_base + (plci_b->li_bchannel_id - 1)].plci != plci_b))
	{
		dbug(1, dprintf("[%06lx] %s,%d: LI peer in wrong state %08lx",
				UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id));
		PUT_WORD(p_result, _WRONG_STATE);
		return (NULL);
	}
	if (((byte)(plci_b_id & ~EXT_CONTROLLER)) !=
	    ((byte)(UnMapController(plci->adapter->Id) & ~EXT_CONTROLLER))
	    && (!(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
		|| !(plci_b->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)))
	{
		dbug(1, dprintf("[%06lx] %s,%d: LI not on same ctrl %08lx",
				UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id));
		PUT_WORD(p_result, _WRONG_IDENTIFIER);
		return (NULL);
	}
	if (!(get_b1_facilities(plci_b, add_b1_facilities(plci_b, plci_b->B1_resource,
							  (word)(plci_b->B1_facilities | B1_FACILITY_MIXER))) & B1_FACILITY_MIXER))
	{
		dbug(1, dprintf("[%06lx] %s,%d: Interconnect peer cannot mix %d",
				UnMapId(Id), (char *)(FILE_), __LINE__, plci_b->B1_resource));
		PUT_WORD(p_result, _WRONG_STATE);
		return (NULL);
	}
	return (plci_b);
}


static byte mixer_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL   *appl, API_PARSE *msg)
{
	word Info;
	word i;
	dword d, li_flags, plci_b_id;
	PLCI *plci_b;
	API_PARSE li_parms[3];
	API_PARSE li_req_parms[3];
	API_PARSE li_participant_struct[2];
	API_PARSE li_participant_parms[3];
	word participant_parms_pos;
	byte result_buffer[32];
	byte *result;
	word result_pos;
	word plci_b_write_pos;

	dbug(1, dprintf("[%06lx] %s,%d: mixer_request",
			UnMapId(Id), (char *)(FILE_), __LINE__));

	Info = GOOD;
	result = result_buffer;
	result_buffer[0] = 0;
	if (!(a->profile.Global_Options & GL_LINE_INTERCONNECT_SUPPORTED))
	{
		dbug(1, dprintf("[%06lx] %s,%d: Facility not supported",
				UnMapId(Id), (char *)(FILE_), __LINE__));
		Info = _FACILITY_NOT_SUPPORTED;
	}
	else if (api_parse(&msg[1].info[1], msg[1].length, "ws", li_parms))
	{
		dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
				UnMapId(Id), (char *)(FILE_), __LINE__));
		Info = _WRONG_MESSAGE_FORMAT;
	}
	else
	{
		result_buffer[0] = 3;
		PUT_WORD(&result_buffer[1], GET_WORD(li_parms[0].info));
		result_buffer[3] = 0;
		switch (GET_WORD(li_parms[0].info))
		{
		case LI_GET_SUPPORTED_SERVICES:
			if (appl->appl_flags & APPL_FLAG_OLD_LI_SPEC)
			{
				result_buffer[0] = 17;
				result_buffer[3] = 14;
				PUT_WORD(&result_buffer[4], GOOD);
				d = 0;
				if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_CH_CH)
					d |= LI_CONFERENCING_SUPPORTED;
				if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_CH_PC)
					d |= LI_MONITORING_SUPPORTED;
				if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_PC_CH)
					d |= LI_ANNOUNCEMENTS_SUPPORTED | LI_MIXING_SUPPORTED;
				if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
					d |= LI_CROSS_CONTROLLER_SUPPORTED;
				PUT_DWORD(&result_buffer[6], d);
				if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
				{
					d = 0;
					for (i = 0; i < li_total_channels; i++)
					{
						if ((li_config_table[i].adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
						    && (li_config_table[i].adapter->li_pri
							|| (i < li_config_table[i].adapter->li_base + MIXER_BCHANNELS_BRI)))
						{
							d++;
						}
					}
				}
				else
				{
					d = a->li_pri ? a->li_channels : MIXER_BCHANNELS_BRI;
				}
				PUT_DWORD(&result_buffer[10], d / 2);
				PUT_DWORD(&result_buffer[14], d);
			}
			else
			{
				result_buffer[0] = 25;
				result_buffer[3] = 22;
				PUT_WORD(&result_buffer[4], GOOD);
				d = LI2_ASYMMETRIC_SUPPORTED | LI2_B_LOOPING_SUPPORTED | LI2_X_LOOPING_SUPPORTED;
				if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_CH_PC)
					d |= LI2_MONITORING_SUPPORTED | LI2_REMOTE_MONITORING_SUPPORTED;
				if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_PC_CH)
					d |= LI2_MIXING_SUPPORTED | LI2_REMOTE_MIXING_SUPPORTED;
				if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_PC_PC)
					d |= LI2_PC_LOOPING_SUPPORTED;
				if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
					d |= LI2_CROSS_CONTROLLER_SUPPORTED;
				PUT_DWORD(&result_buffer[6], d);
				d = a->li_pri ? a->li_channels : MIXER_BCHANNELS_BRI;
				PUT_DWORD(&result_buffer[10], d / 2);
				PUT_DWORD(&result_buffer[14], d - 1);
				if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
				{
					d = 0;
					for (i = 0; i < li_total_channels; i++)
					{
						if ((li_config_table[i].adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
						    && (li_config_table[i].adapter->li_pri
							|| (i < li_config_table[i].adapter->li_base + MIXER_BCHANNELS_BRI)))
						{
							d++;
						}
					}
				}
				PUT_DWORD(&result_buffer[18], d / 2);
				PUT_DWORD(&result_buffer[22], d - 1);
			}
			break;

		case LI_REQ_CONNECT:
			if (li_parms[1].length == 8)
			{
				appl->appl_flags |= APPL_FLAG_OLD_LI_SPEC;
				if (api_parse(&li_parms[1].info[1], li_parms[1].length, "dd", li_req_parms))
				{
					dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
							UnMapId(Id), (char *)(FILE_), __LINE__));
					Info = _WRONG_MESSAGE_FORMAT;
					break;
				}
				plci_b_id = GET_DWORD(li_req_parms[0].info) & 0xffff;
				li_flags = GET_DWORD(li_req_parms[1].info);
				Info = li_check_main_plci(Id, plci);
				result_buffer[0] = 9;
				result_buffer[3] = 6;
				PUT_DWORD(&result_buffer[4], plci_b_id);
				PUT_WORD(&result_buffer[8], GOOD);
				if (Info != GOOD)
					break;
				result = plci->saved_msg.info;
				for (i = 0; i <= result_buffer[0]; i++)
					result[i] = result_buffer[i];
				plci_b_write_pos = plci->li_plci_b_write_pos;
				plci_b = li_check_plci_b(Id, plci, plci_b_id, plci_b_write_pos, &result[8]);
				if (plci_b == NULL)
					break;
				li_update_connect(Id, a, plci, plci_b_id, true, li_flags);
				plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_LAST_FLAG;
				plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1;
				plci->li_plci_b_write_pos = plci_b_write_pos;
			}
			else
			{
				appl->appl_flags &= ~APPL_FLAG_OLD_LI_SPEC;
				if (api_parse(&li_parms[1].info[1], li_parms[1].length, "ds", li_req_parms))
				{
					dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
							UnMapId(Id), (char *)(FILE_), __LINE__));
					Info = _WRONG_MESSAGE_FORMAT;
					break;
				}
				li_flags = GET_DWORD(li_req_parms[0].info) & ~(LI2_FLAG_INTERCONNECT_A_B | LI2_FLAG_INTERCONNECT_B_A);
				Info = li_check_main_plci(Id, plci);
				result_buffer[0] = 7;
				result_buffer[3] = 4;
				PUT_WORD(&result_buffer[4], Info);
				result_buffer[6] = 0;
				if (Info != GOOD)
					break;
				result = plci->saved_msg.info;
				for (i = 0; i <= result_buffer[0]; i++)
					result[i] = result_buffer[i];
				plci_b_write_pos = plci->li_plci_b_write_pos;
				participant_parms_pos = 0;
				result_pos = 7;
				li2_update_connect(Id, a, plci, UnMapId(Id), true, li_flags);
				while (participant_parms_pos < li_req_parms[1].length)
				{
					result[result_pos] = 6;
					result_pos += 7;
					PUT_DWORD(&result[result_pos - 6], 0);
					PUT_WORD(&result[result_pos - 2], GOOD);
					if (api_parse(&li_req_parms[1].info[1 + participant_parms_pos],
						      (word)(li_parms[1].length - participant_parms_pos), "s", li_participant_struct))
					{
						dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
								UnMapId(Id), (char *)(FILE_), __LINE__));
						PUT_WORD(&result[result_pos - 2], _WRONG_MESSAGE_FORMAT);
						break;
					}
					if (api_parse(&li_participant_struct[0].info[1],
						      li_participant_struct[0].length, "dd", li_participant_parms))
					{
						dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
								UnMapId(Id), (char *)(FILE_), __LINE__));
						PUT_WORD(&result[result_pos - 2], _WRONG_MESSAGE_FORMAT);
						break;
					}
					plci_b_id = GET_DWORD(li_participant_parms[0].info) & 0xffff;
					li_flags = GET_DWORD(li_participant_parms[1].info);
					PUT_DWORD(&result[result_pos - 6], plci_b_id);
					if (sizeof(result) - result_pos < 7)
					{
						dbug(1, dprintf("[%06lx] %s,%d: LI result overrun",
								UnMapId(Id), (char *)(FILE_), __LINE__));
						PUT_WORD(&result[result_pos - 2], _WRONG_STATE);
						break;
					}
					plci_b = li2_check_plci_b(Id, plci, plci_b_id, plci_b_write_pos, &result[result_pos - 2]);
					if (plci_b != NULL)
					{
						li2_update_connect(Id, a, plci, plci_b_id, true, li_flags);
						plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id |
							((li_flags & (LI2_FLAG_INTERCONNECT_A_B | LI2_FLAG_INTERCONNECT_B_A |
								      LI2_FLAG_PCCONNECT_A_B | LI2_FLAG_PCCONNECT_B_A)) ? 0 : LI_PLCI_B_DISC_FLAG);
						plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1;
					}
					participant_parms_pos = (word)((&li_participant_struct[0].info[1 + li_participant_struct[0].length]) -
								       (&li_req_parms[1].info[1]));
				}
				result[0] = (byte)(result_pos - 1);
				result[3] = (byte)(result_pos - 4);
				result[6] = (byte)(result_pos - 7);
				i = (plci_b_write_pos == 0) ? LI_PLCI_B_QUEUE_ENTRIES - 1 : plci_b_write_pos - 1;
				if ((plci_b_write_pos == plci->li_plci_b_read_pos)
				    || (plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG))
				{
					plci->li_plci_b_queue[plci_b_write_pos] = LI_PLCI_B_SKIP_FLAG | LI_PLCI_B_LAST_FLAG;
					plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1;
				}
				else
					plci->li_plci_b_queue[i] |= LI_PLCI_B_LAST_FLAG;
				plci->li_plci_b_write_pos = plci_b_write_pos;
			}
			mixer_calculate_coefs(a);
			plci->li_channel_bits = li_config_table[a->li_base + (plci->li_bchannel_id - 1)].channel;
			mixer_notify_update(plci, true);
			sendf(appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
			      "wwS", Info, SELECTOR_LINE_INTERCONNECT, result);
			plci->command = 0;
			plci->li_cmd = GET_WORD(li_parms[0].info);
			start_internal_command(Id, plci, mixer_command);
			return (false);

		case LI_REQ_DISCONNECT:
			if (li_parms[1].length == 4)
			{
				appl->appl_flags |= APPL_FLAG_OLD_LI_SPEC;
				if (api_parse(&li_parms[1].info[1], li_parms[1].length, "d", li_req_parms))
				{
					dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
							UnMapId(Id), (char *)(FILE_), __LINE__));
					Info = _WRONG_MESSAGE_FORMAT;
					break;
				}
				plci_b_id = GET_DWORD(li_req_parms[0].info) & 0xffff;
				Info = li_check_main_plci(Id, plci);
				result_buffer[0] = 9;
				result_buffer[3] = 6;
				PUT_DWORD(&result_buffer[4], GET_DWORD(li_req_parms[0].info));
				PUT_WORD(&result_buffer[8], GOOD);
				if (Info != GOOD)
					break;
				result = plci->saved_msg.info;
				for (i = 0; i <= result_buffer[0]; i++)
					result[i] = result_buffer[i];
				plci_b_write_pos = plci->li_plci_b_write_pos;
				plci_b = li_check_plci_b(Id, plci, plci_b_id, plci_b_write_pos, &result[8]);
				if (plci_b == NULL)
					break;
				li_update_connect(Id, a, plci, plci_b_id, false, 0);
				plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_DISC_FLAG | LI_PLCI_B_LAST_FLAG;
				plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1;
				plci->li_plci_b_write_pos = plci_b_write_pos;
			}
			else
			{
				appl->appl_flags &= ~APPL_FLAG_OLD_LI_SPEC;
				if (api_parse(&li_parms[1].info[1], li_parms[1].length, "s", li_req_parms))
				{
					dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
							UnMapId(Id), (char *)(FILE_), __LINE__));
					Info = _WRONG_MESSAGE_FORMAT;
					break;
				}
				Info = li_check_main_plci(Id, plci);
				result_buffer[0] = 7;
				result_buffer[3] = 4;
				PUT_WORD(&result_buffer[4], Info);
				result_buffer[6] = 0;
				if (Info != GOOD)
					break;
				result = plci->saved_msg.info;
				for (i = 0; i <= result_buffer[0]; i++)
					result[i] = result_buffer[i];
				plci_b_write_pos = plci->li_plci_b_write_pos;
				participant_parms_pos = 0;
				result_pos = 7;
				while (participant_parms_pos < li_req_parms[0].length)
				{
					result[result_pos] = 6;
					result_pos += 7;
					PUT_DWORD(&result[result_pos - 6], 0);
					PUT_WORD(&result[result_pos - 2], GOOD);
					if (api_parse(&li_req_parms[0].info[1 + participant_parms_pos],
						      (word)(li_parms[1].length - participant_parms_pos), "s", li_participant_struct))
					{
						dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
								UnMapId(Id), (char *)(FILE_), __LINE__));
						PUT_WORD(&result[result_pos - 2], _WRONG_MESSAGE_FORMAT);
						break;
					}
					if (api_parse(&li_participant_struct[0].info[1],
						      li_participant_struct[0].length, "d", li_participant_parms))
					{
						dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
								UnMapId(Id), (char *)(FILE_), __LINE__));
						PUT_WORD(&result[result_pos - 2], _WRONG_MESSAGE_FORMAT);
						break;
					}
					plci_b_id = GET_DWORD(li_participant_parms[0].info) & 0xffff;
					PUT_DWORD(&result[result_pos - 6], plci_b_id);
					if (sizeof(result) - result_pos < 7)
					{
						dbug(1, dprintf("[%06lx] %s,%d: LI result overrun",
								UnMapId(Id), (char *)(FILE_), __LINE__));
						PUT_WORD(&result[result_pos - 2], _WRONG_STATE);
						break;
					}
					plci_b = li2_check_plci_b(Id, plci, plci_b_id, plci_b_write_pos, &result[result_pos - 2]);
					if (plci_b != NULL)
					{
						li2_update_connect(Id, a, plci, plci_b_id, false, 0);
						plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_DISC_FLAG;
						plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1;
					}
					participant_parms_pos = (word)((&li_participant_struct[0].info[1 + li_participant_struct[0].length]) -
								       (&li_req_parms[0].info[1]));
				}
				result[0] = (byte)(result_pos - 1);
				result[3] = (byte)(result_pos - 4);
				result[6] = (byte)(result_pos - 7);
				i = (plci_b_write_pos == 0) ? LI_PLCI_B_QUEUE_ENTRIES - 1 : plci_b_write_pos - 1;
				if ((plci_b_write_pos == plci->li_plci_b_read_pos)
				    || (plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG))
				{
					plci->li_plci_b_queue[plci_b_write_pos] = LI_PLCI_B_SKIP_FLAG | LI_PLCI_B_LAST_FLAG;
					plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1;
				}
				else
					plci->li_plci_b_queue[i] |= LI_PLCI_B_LAST_FLAG;
				plci->li_plci_b_write_pos = plci_b_write_pos;
			}
			mixer_calculate_coefs(a);
			plci->li_channel_bits = li_config_table[a->li_base + (plci->li_bchannel_id - 1)].channel;
			mixer_notify_update(plci, true);
			sendf(appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
			      "wwS", Info, SELECTOR_LINE_INTERCONNECT, result);
			plci->command = 0;
			plci->li_cmd = GET_WORD(li_parms[0].info);
			start_internal_command(Id, plci, mixer_command);
			return (false);

		case LI_REQ_SILENT_UPDATE:
			if (!plci || !plci->State
			    || !plci->NL.Id || plci->nl_remove_id
			    || (plci->li_bchannel_id == 0)
			    || (li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci != plci))
			{
				dbug(1, dprintf("[%06lx] %s,%d: Wrong state",
						UnMapId(Id), (char *)(FILE_), __LINE__));
				return (false);
			}
			plci_b_write_pos = plci->li_plci_b_write_pos;
			if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos :
			     LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 2)
			{
				dbug(1, dprintf("[%06lx] %s,%d: LI request overrun",
						UnMapId(Id), (char *)(FILE_), __LINE__));
				return (false);
			}
			i = (plci_b_write_pos == 0) ? LI_PLCI_B_QUEUE_ENTRIES - 1 : plci_b_write_pos - 1;
			if ((plci_b_write_pos == plci->li_plci_b_read_pos)
			    || (plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG))
			{
				plci->li_plci_b_queue[plci_b_write_pos] = LI_PLCI_B_SKIP_FLAG | LI_PLCI_B_LAST_FLAG;
				plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1;
			}
			else
				plci->li_plci_b_queue[i] |= LI_PLCI_B_LAST_FLAG;
			plci->li_plci_b_write_pos = plci_b_write_pos;
			plci->li_channel_bits = li_config_table[a->li_base + (plci->li_bchannel_id - 1)].channel;
			plci->command = 0;
			plci->li_cmd = GET_WORD(li_parms[0].info);
			start_internal_command(Id, plci, mixer_command);
			return (false);

		default:
			dbug(1, dprintf("[%06lx] %s,%d: LI unknown request %04x",
					UnMapId(Id), (char *)(FILE_), __LINE__, GET_WORD(li_parms[0].info)));
			Info = _FACILITY_NOT_SUPPORTED;
		}
	}
	sendf(appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
	      "wwS", Info, SELECTOR_LINE_INTERCONNECT, result);
	return (false);
}


static void mixer_indication_coefs_set(dword Id, PLCI *plci)
{
	dword d;
	byte result[12];

	dbug(1, dprintf("[%06lx] %s,%d: mixer_indication_coefs_set",
			UnMapId(Id), (char *)(FILE_), __LINE__));

	if (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos)
	{
		do
		{
			d = plci->li_plci_b_queue[plci->li_plci_b_read_pos];
			if (!(d & LI_PLCI_B_SKIP_FLAG))
			{
				if (plci->appl->appl_flags & APPL_FLAG_OLD_LI_SPEC)
				{
					if (d & LI_PLCI_B_DISC_FLAG)
					{
						result[0] = 5;
						PUT_WORD(&result[1], LI_IND_DISCONNECT);
						result[3] = 2;
						PUT_WORD(&result[4], _LI_USER_INITIATED);
					}
					else
					{
						result[0] = 7;
						PUT_WORD(&result[1], LI_IND_CONNECT_ACTIVE);
						result[3] = 4;
						PUT_DWORD(&result[4], d & ~LI_PLCI_B_FLAG_MASK);
					}
				}
				else
				{
					if (d & LI_PLCI_B_DISC_FLAG)
					{
						result[0] = 9;
						PUT_WORD(&result[1], LI_IND_DISCONNECT);
						result[3] = 6;
						PUT_DWORD(&result[4], d & ~LI_PLCI_B_FLAG_MASK);
						PUT_WORD(&result[8], _LI_USER_INITIATED);
					}
					else
					{
						result[0] = 7;
						PUT_WORD(&result[1], LI_IND_CONNECT_ACTIVE);
						result[3] = 4;
						PUT_DWORD(&result[4], d & ~LI_PLCI_B_FLAG_MASK);
					}
				}
				sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0,
				      "ws", SELECTOR_LINE_INTERCONNECT, result);
			}
			plci->li_plci_b_read_pos = (plci->li_plci_b_read_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ?
				0 : plci->li_plci_b_read_pos + 1;
		} while (!(d & LI_PLCI_B_LAST_FLAG) && (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos));
	}
}


static void mixer_indication_xconnect_from(dword Id, PLCI *plci, byte *msg, word length)
{
	word i, j, ch;
	struct xconnect_transfer_address_s s,   *p;
	DIVA_CAPI_ADAPTER *a;

	dbug(1, dprintf("[%06lx] %s,%d: mixer_indication_xconnect_from %d",
			UnMapId(Id), (char *)(FILE_), __LINE__, (int)length));

	a = plci->adapter;
	i = 1;
	for (i = 1; i < length; i += 16)
	{
		s.card_address.low = msg[i] | (msg[i + 1] << 8) | (((dword)(msg[i + 2])) << 16) | (((dword)(msg[i + 3])) << 24);
		s.card_address.high = msg[i + 4] | (msg[i + 5] << 8) | (((dword)(msg[i + 6])) << 16) | (((dword)(msg[i + 7])) << 24);
		s.offset = msg[i + 8] | (msg[i + 9] << 8) | (((dword)(msg[i + 10])) << 16) | (((dword)(msg[i + 11])) << 24);
		ch = msg[i + 12] | (msg[i + 13] << 8);
		j = ch & XCONNECT_CHANNEL_NUMBER_MASK;
		if (!a->li_pri && (plci->li_bchannel_id == 2))
			j = 1 - j;
		j += a->li_base;
		if (ch & XCONNECT_CHANNEL_PORT_PC)
			p = &(li_config_table[j].send_pc);
		else
			p = &(li_config_table[j].send_b);
		p->card_address.low = s.card_address.low;
		p->card_address.high = s.card_address.high;
		p->offset = s.offset;
		li_config_table[j].channel |= LI_CHANNEL_ADDRESSES_SET;
	}
	if (plci->internal_command_queue[0]
	    && ((plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_2)
		|| (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_3)
		|| (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_4)))
	{
		(*(plci->internal_command_queue[0]))(Id, plci, 0);
		if (!plci->internal_command)
			next_internal_command(Id, plci);
	}
	mixer_notify_update(plci, true);
}


static void mixer_indication_xconnect_to(dword Id, PLCI *plci, byte *msg, word length)
{

	dbug(1, dprintf("[%06lx] %s,%d: mixer_indication_xconnect_to %d",
			UnMapId(Id), (char *)(FILE_), __LINE__, (int) length));

}


static byte mixer_notify_source_removed(PLCI *plci, dword plci_b_id)
{
	word plci_b_write_pos;

	plci_b_write_pos = plci->li_plci_b_write_pos;
	if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos :
	     LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 1)
	{
		dbug(1, dprintf("[%06lx] %s,%d: LI request overrun",
				(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
				(char *)(FILE_), __LINE__));
		return (false);
	}
	plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_DISC_FLAG;
	plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1;
	plci->li_plci_b_write_pos = plci_b_write_pos;
	return (true);
}


static void mixer_remove(PLCI *plci)
{
	DIVA_CAPI_ADAPTER *a;
	PLCI *notify_plci;
	dword plci_b_id;
	word i, j;

	dbug(1, dprintf("[%06lx] %s,%d: mixer_remove",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__));

	a = plci->adapter;
	plci_b_id = (plci->Id << 8) | UnMapController(plci->adapter->Id);
	if (a->profile.Global_Options & GL_LINE_INTERCONNECT_SUPPORTED)
	{
		if ((plci->li_bchannel_id != 0)
		    && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
		{
			i = a->li_base + (plci->li_bchannel_id - 1);
			if ((li_config_table[i].curchnl | li_config_table[i].channel) & LI_CHANNEL_INVOLVED)
			{
				for (j = 0; j < li_total_channels; j++)
				{
					if ((li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT)
					    || (li_config_table[j].flag_table[i] & LI_FLAG_INTERCONNECT))
					{
						notify_plci = li_config_table[j].plci;
						if ((notify_plci != NULL)
						    && (notify_plci != plci)
						    && (notify_plci->appl != NULL)
						    && !(notify_plci->appl->appl_flags & APPL_FLAG_OLD_LI_SPEC)
						    && (notify_plci->State)
						    && notify_plci->NL.Id && !notify_plci->nl_remove_id)
						{
							mixer_notify_source_removed(notify_plci, plci_b_id);
						}
					}
				}
				mixer_clear_config(plci);
				mixer_calculate_coefs(a);
				mixer_notify_update(plci, true);
			}
			li_config_table[i].plci = NULL;
			plci->li_bchannel_id = 0;
		}
	}
}


/*------------------------------------------------------------------*/
/* Echo canceller facilities                                        */
/*------------------------------------------------------------------*/


static void ec_write_parameters(PLCI *plci)
{
	word w;
	byte parameter_buffer[6];

	dbug(1, dprintf("[%06lx] %s,%d: ec_write_parameters",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__));

	parameter_buffer[0] = 5;
	parameter_buffer[1] = DSP_CTRL_SET_LEC_PARAMETERS;
	PUT_WORD(&parameter_buffer[2], plci->ec_idi_options);
	plci->ec_idi_options &= ~LEC_RESET_COEFFICIENTS;
	w = (plci->ec_tail_length == 0) ? 128 : plci->ec_tail_length;
	PUT_WORD(&parameter_buffer[4], w);
	add_p(plci, FTY, parameter_buffer);
	sig_req(plci, TEL_CTRL, 0);
	send_req(plci);
}


static void ec_clear_config(PLCI *plci)
{

	dbug(1, dprintf("[%06lx] %s,%d: ec_clear_config",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__));

	plci->ec_idi_options = LEC_ENABLE_ECHO_CANCELLER |
		LEC_MANUAL_DISABLE | LEC_ENABLE_NONLINEAR_PROCESSING;
	plci->ec_tail_length = 0;
}


static void ec_prepare_switch(dword Id, PLCI *plci)
{

	dbug(1, dprintf("[%06lx] %s,%d: ec_prepare_switch",
			UnMapId(Id), (char *)(FILE_), __LINE__));

}


static word ec_save_config(dword Id, PLCI *plci, byte Rc)
{

	dbug(1, dprintf("[%06lx] %s,%d: ec_save_config %02x %d",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));

	return (GOOD);
}


static word ec_restore_config(dword Id, PLCI *plci, byte Rc)
{
	word Info;

	dbug(1, dprintf("[%06lx] %s,%d: ec_restore_config %02x %d",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));

	Info = GOOD;
	if (plci->B1_facilities & B1_FACILITY_EC)
	{
		switch (plci->adjust_b_state)
		{
		case ADJUST_B_RESTORE_EC_1:
			plci->internal_command = plci->adjust_b_command;
			if (plci->sig_req)
			{
				plci->adjust_b_state = ADJUST_B_RESTORE_EC_1;
				break;
			}
			ec_write_parameters(plci);
			plci->adjust_b_state = ADJUST_B_RESTORE_EC_2;
			break;
		case ADJUST_B_RESTORE_EC_2:
			if ((Rc != OK) && (Rc != OK_FC))
			{
				dbug(1, dprintf("[%06lx] %s,%d: Restore EC failed %02x",
						UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
				Info = _WRONG_STATE;
				break;
			}
			break;
		}
	}
	return (Info);
}


static void ec_command(dword Id, PLCI *plci, byte Rc)
{
	word internal_command, Info;
	byte result[8];

	dbug(1, dprintf("[%06lx] %s,%d: ec_command %02x %04x %04x %04x %d",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command,
			plci->ec_cmd, plci->ec_idi_options, plci->ec_tail_length));

	Info = GOOD;
	if (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC)
	{
		result[0] = 2;
		PUT_WORD(&result[1], EC_SUCCESS);
	}
	else
	{
		result[0] = 5;
		PUT_WORD(&result[1], plci->ec_cmd);
		result[3] = 2;
		PUT_WORD(&result[4], GOOD);
	}
	internal_command = plci->internal_command;
	plci->internal_command = 0;
	switch (plci->ec_cmd)
	{
	case EC_ENABLE_OPERATION:
	case EC_FREEZE_COEFFICIENTS:
	case EC_RESUME_COEFFICIENT_UPDATE:
	case EC_RESET_COEFFICIENTS:
		switch (internal_command)
		{
		default:
			adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities |
								  B1_FACILITY_EC), EC_COMMAND_1);
		case EC_COMMAND_1:
			if (adjust_b_process(Id, plci, Rc) != GOOD)
			{
				dbug(1, dprintf("[%06lx] %s,%d: Load EC failed",
						UnMapId(Id), (char *)(FILE_), __LINE__));
				Info = _FACILITY_NOT_SUPPORTED;
				break;
			}
			if (plci->internal_command)
				return;
		case EC_COMMAND_2:
			if (plci->sig_req)
			{
				plci->internal_command = EC_COMMAND_2;
				return;
			}
			plci->internal_command = EC_COMMAND_3;
			ec_write_parameters(plci);
			return;
		case EC_COMMAND_3:
			if ((Rc != OK) && (Rc != OK_FC))
			{
				dbug(1, dprintf("[%06lx] %s,%d: Enable EC failed %02x",
						UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
				Info = _FACILITY_NOT_SUPPORTED;
				break;
			}
			break;
		}
		break;

	case EC_DISABLE_OPERATION:
		switch (internal_command)
		{
		default:
		case EC_COMMAND_1:
			if (plci->B1_facilities & B1_FACILITY_EC)
			{
				if (plci->sig_req)
				{
					plci->internal_command = EC_COMMAND_1;
					return;
				}
				plci->internal_command = EC_COMMAND_2;
				ec_write_parameters(plci);
				return;
			}
			Rc = OK;
		case EC_COMMAND_2:
			if ((Rc != OK) && (Rc != OK_FC))
			{
				dbug(1, dprintf("[%06lx] %s,%d: Disable EC failed %02x",
						UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
				Info = _FACILITY_NOT_SUPPORTED;
				break;
			}
			adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities &
								  ~B1_FACILITY_EC), EC_COMMAND_3);
		case EC_COMMAND_3:
			if (adjust_b_process(Id, plci, Rc) != GOOD)
			{
				dbug(1, dprintf("[%06lx] %s,%d: Unload EC failed",
						UnMapId(Id), (char *)(FILE_), __LINE__));
				Info = _FACILITY_NOT_SUPPORTED;
				break;
			}
			if (plci->internal_command)
				return;
			break;
		}
		break;
	}
	sendf(plci->appl, _FACILITY_R | CONFIRM, Id & 0xffffL, plci->number,
	      "wws", Info, (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) ?
	      PRIV_SELECTOR_ECHO_CANCELLER : SELECTOR_ECHO_CANCELLER, result);
}


static byte ec_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL   *appl, API_PARSE *msg)
{
	word Info;
	word opt;
	API_PARSE ec_parms[3];
	byte result[16];

	dbug(1, dprintf("[%06lx] %s,%d: ec_request",
			UnMapId(Id), (char *)(FILE_), __LINE__));

	Info = GOOD;
	result[0] = 0;
	if (!(a->man_profile.private_options & (1L << PRIVATE_ECHO_CANCELLER)))
	{
		dbug(1, dprintf("[%06lx] %s,%d: Facility not supported",
				UnMapId(Id), (char *)(FILE_), __LINE__));
		Info = _FACILITY_NOT_SUPPORTED;
	}
	else
	{
		if (appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC)
		{
			if (api_parse(&msg[1].info[1], msg[1].length, "w", ec_parms))
			{
				dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
						UnMapId(Id), (char *)(FILE_), __LINE__));
				Info = _WRONG_MESSAGE_FORMAT;
			}
			else
			{
				if (plci == NULL)
				{
					dbug(1, dprintf("[%06lx] %s,%d: Wrong PLCI",
							UnMapId(Id), (char *)(FILE_), __LINE__));
					Info = _WRONG_IDENTIFIER;
				}
				else if (!plci->State || !plci->NL.Id || plci->nl_remove_id)
				{
					dbug(1, dprintf("[%06lx] %s,%d: Wrong state",
							UnMapId(Id), (char *)(FILE_), __LINE__));
					Info = _WRONG_STATE;
				}
				else
				{
					plci->command = 0;
					plci->ec_cmd = GET_WORD(ec_parms[0].info);
					plci->ec_idi_options &= ~(LEC_MANUAL_DISABLE | LEC_RESET_COEFFICIENTS);
					result[0] = 2;
					PUT_WORD(&result[1], EC_SUCCESS);
					if (msg[1].length >= 4)
					{
						opt = GET_WORD(&ec_parms[0].info[2]);
						plci->ec_idi_options &= ~(LEC_ENABLE_NONLINEAR_PROCESSING |
									  LEC_ENABLE_2100HZ_DETECTOR | LEC_REQUIRE_2100HZ_REVERSALS);
						if (!(opt & EC_DISABLE_NON_LINEAR_PROCESSING))
							plci->ec_idi_options |= LEC_ENABLE_NONLINEAR_PROCESSING;
						if (opt & EC_DETECT_DISABLE_TONE)
							plci->ec_idi_options |= LEC_ENABLE_2100HZ_DETECTOR;
						if (!(opt & EC_DO_NOT_REQUIRE_REVERSALS))
							plci->ec_idi_options |= LEC_REQUIRE_2100HZ_REVERSALS;
						if (msg[1].length >= 6)
						{
							plci->ec_tail_length = GET_WORD(&ec_parms[0].info[4]);
						}
					}
					switch (plci->ec_cmd)
					{
					case EC_ENABLE_OPERATION:
						plci->ec_idi_options &= ~LEC_FREEZE_COEFFICIENTS;
						start_internal_command(Id, plci, ec_command);
						return (false);

					case EC_DISABLE_OPERATION:
						plci->ec_idi_options = LEC_ENABLE_ECHO_CANCELLER |
							LEC_MANUAL_DISABLE | LEC_ENABLE_NONLINEAR_PROCESSING |
							LEC_RESET_COEFFICIENTS;
						start_internal_command(Id, plci, ec_command);
						return (false);

					case EC_FREEZE_COEFFICIENTS:
						plci->ec_idi_options |= LEC_FREEZE_COEFFICIENTS;
						start_internal_command(Id, plci, ec_command);
						return (false);

					case EC_RESUME_COEFFICIENT_UPDATE:
						plci->ec_idi_options &= ~LEC_FREEZE_COEFFICIENTS;
						start_internal_command(Id, plci, ec_command);
						return (false);

					case EC_RESET_COEFFICIENTS:
						plci->ec_idi_options |= LEC_RESET_COEFFICIENTS;
						start_internal_command(Id, plci, ec_command);
						return (false);

					default:
						dbug(1, dprintf("[%06lx] %s,%d: EC unknown request %04x",
								UnMapId(Id), (char *)(FILE_), __LINE__, plci->ec_cmd));
						PUT_WORD(&result[1], EC_UNSUPPORTED_OPERATION);
					}
				}
			}
		}
		else
		{
			if (api_parse(&msg[1].info[1], msg[1].length, "ws", ec_parms))
			{
				dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
						UnMapId(Id), (char *)(FILE_), __LINE__));
				Info = _WRONG_MESSAGE_FORMAT;
			}
			else
			{
				if (GET_WORD(ec_parms[0].info) == EC_GET_SUPPORTED_SERVICES)
				{
					result[0] = 11;
					PUT_WORD(&result[1], EC_GET_SUPPORTED_SERVICES);
					result[3] = 8;
					PUT_WORD(&result[4], GOOD);
					PUT_WORD(&result[6], 0x0007);
					PUT_WORD(&result[8], LEC_MAX_SUPPORTED_TAIL_LENGTH);
					PUT_WORD(&result[10], 0);
				}
				else if (plci == NULL)
				{
					dbug(1, dprintf("[%06lx] %s,%d: Wrong PLCI",
							UnMapId(Id), (char *)(FILE_), __LINE__));
					Info = _WRONG_IDENTIFIER;
				}
				else if (!plci->State || !plci->NL.Id || plci->nl_remove_id)
				{
					dbug(1, dprintf("[%06lx] %s,%d: Wrong state",
							UnMapId(Id), (char *)(FILE_), __LINE__));
					Info = _WRONG_STATE;
				}
				else
				{
					plci->command = 0;
					plci->ec_cmd = GET_WORD(ec_parms[0].info);
					plci->ec_idi_options &= ~(LEC_MANUAL_DISABLE | LEC_RESET_COEFFICIENTS);
					result[0] = 5;
					PUT_WORD(&result[1], plci->ec_cmd);
					result[3] = 2;
					PUT_WORD(&result[4], GOOD);
					plci->ec_idi_options &= ~(LEC_ENABLE_NONLINEAR_PROCESSING |
								  LEC_ENABLE_2100HZ_DETECTOR | LEC_REQUIRE_2100HZ_REVERSALS);
					plci->ec_tail_length = 0;
					if (ec_parms[1].length >= 2)
					{
						opt = GET_WORD(&ec_parms[1].info[1]);
						if (opt & EC_ENABLE_NON_LINEAR_PROCESSING)
							plci->ec_idi_options |= LEC_ENABLE_NONLINEAR_PROCESSING;
						if (opt & EC_DETECT_DISABLE_TONE)
							plci->ec_idi_options |= LEC_ENABLE_2100HZ_DETECTOR;
						if (!(opt & EC_DO_NOT_REQUIRE_REVERSALS))
							plci->ec_idi_options |= LEC_REQUIRE_2100HZ_REVERSALS;
						if (ec_parms[1].length >= 4)
						{
							plci->ec_tail_length = GET_WORD(&ec_parms[1].info[3]);
						}
					}
					switch (plci->ec_cmd)
					{
					case EC_ENABLE_OPERATION:
						plci->ec_idi_options &= ~LEC_FREEZE_COEFFICIENTS;
						start_internal_command(Id, plci, ec_command);
						return (false);

					case EC_DISABLE_OPERATION:
						plci->ec_idi_options = LEC_ENABLE_ECHO_CANCELLER |
							LEC_MANUAL_DISABLE | LEC_ENABLE_NONLINEAR_PROCESSING |
							LEC_RESET_COEFFICIENTS;
						start_internal_command(Id, plci, ec_command);
						return (false);

					default:
						dbug(1, dprintf("[%06lx] %s,%d: EC unknown request %04x",
								UnMapId(Id), (char *)(FILE_), __LINE__, plci->ec_cmd));
						PUT_WORD(&result[4], _FACILITY_SPECIFIC_FUNCTION_NOT_SUPP);
					}
				}
			}
		}
	}
	sendf(appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
	      "wws", Info, (appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) ?
	      PRIV_SELECTOR_ECHO_CANCELLER : SELECTOR_ECHO_CANCELLER, result);
	return (false);
}


static void ec_indication(dword Id, PLCI *plci, byte *msg, word length)
{
	byte result[8];

	dbug(1, dprintf("[%06lx] %s,%d: ec_indication",
			UnMapId(Id), (char *)(FILE_), __LINE__));

	if (!(plci->ec_idi_options & LEC_MANUAL_DISABLE))
	{
		if (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC)
		{
			result[0] = 2;
			PUT_WORD(&result[1], 0);
			switch (msg[1])
			{
			case LEC_DISABLE_TYPE_CONTIGNUOUS_2100HZ:
				PUT_WORD(&result[1], EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ);
				break;
			case LEC_DISABLE_TYPE_REVERSED_2100HZ:
				PUT_WORD(&result[1], EC_BYPASS_DUE_TO_REVERSED_2100HZ);
				break;
			case LEC_DISABLE_RELEASED:
				PUT_WORD(&result[1], EC_BYPASS_RELEASED);
				break;
			}
		}
		else
		{
			result[0] = 5;
			PUT_WORD(&result[1], EC_BYPASS_INDICATION);
			result[3] = 2;
			PUT_WORD(&result[4], 0);
			switch (msg[1])
			{
			case LEC_DISABLE_TYPE_CONTIGNUOUS_2100HZ:
				PUT_WORD(&result[4], EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ);
				break;
			case LEC_DISABLE_TYPE_REVERSED_2100HZ:
				PUT_WORD(&result[4], EC_BYPASS_DUE_TO_REVERSED_2100HZ);
				break;
			case LEC_DISABLE_RELEASED:
				PUT_WORD(&result[4], EC_BYPASS_RELEASED);
				break;
			}
		}
		sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) ?
		      PRIV_SELECTOR_ECHO_CANCELLER : SELECTOR_ECHO_CANCELLER, result);
	}
}



/*------------------------------------------------------------------*/
/* Advanced voice                                                   */
/*------------------------------------------------------------------*/

static void adv_voice_write_coefs(PLCI *plci, word write_command)
{
	DIVA_CAPI_ADAPTER *a;
	word i;
	byte *p;

	word w, n, j, k;
	byte ch_map[MIXER_CHANNELS_BRI];

	byte coef_buffer[ADV_VOICE_COEF_BUFFER_SIZE + 2];

	dbug(1, dprintf("[%06lx] %s,%d: adv_voice_write_coefs %d",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__, write_command));

	a = plci->adapter;
	p = coef_buffer + 1;
	*(p++) = DSP_CTRL_OLD_SET_MIXER_COEFFICIENTS;
	i = 0;
	while (i + sizeof(word) <= a->adv_voice_coef_length)
	{
		PUT_WORD(p, GET_WORD(a->adv_voice_coef_buffer + i));
		p += 2;
		i += 2;
	}
	while (i < ADV_VOICE_OLD_COEF_COUNT * sizeof(word))
	{
		PUT_WORD(p, 0x8000);
		p += 2;
		i += 2;
	}

	if (!a->li_pri && (plci->li_bchannel_id == 0))
	{
		if ((li_config_table[a->li_base].plci == NULL) && (li_config_table[a->li_base + 1].plci != NULL))
		{
			plci->li_bchannel_id = 1;
			li_config_table[a->li_base].plci = plci;
			dbug(1, dprintf("[%06lx] %s,%d: adv_voice_set_bchannel_id %d",
					(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
					(char *)(FILE_), __LINE__, plci->li_bchannel_id));
		}
		else if ((li_config_table[a->li_base].plci != NULL) && (li_config_table[a->li_base + 1].plci == NULL))
		{
			plci->li_bchannel_id = 2;
			li_config_table[a->li_base + 1].plci = plci;
			dbug(1, dprintf("[%06lx] %s,%d: adv_voice_set_bchannel_id %d",
					(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
					(char *)(FILE_), __LINE__, plci->li_bchannel_id));
		}
	}
	if (!a->li_pri && (plci->li_bchannel_id != 0)
	    && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
	{
		i = a->li_base + (plci->li_bchannel_id - 1);
		switch (write_command)
		{
		case ADV_VOICE_WRITE_ACTIVATION:
			j = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
			k = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
			if (!(plci->B1_facilities & B1_FACILITY_MIXER))
			{
				li_config_table[j].flag_table[i] |= LI_FLAG_CONFERENCE | LI_FLAG_MIX;
				li_config_table[i].flag_table[j] |= LI_FLAG_CONFERENCE | LI_FLAG_MONITOR;
			}
			if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
			{
				li_config_table[k].flag_table[i] |= LI_FLAG_CONFERENCE | LI_FLAG_MIX;
				li_config_table[i].flag_table[k] |= LI_FLAG_CONFERENCE | LI_FLAG_MONITOR;
				li_config_table[k].flag_table[j] |= LI_FLAG_CONFERENCE;
				li_config_table[j].flag_table[k] |= LI_FLAG_CONFERENCE;
			}
			mixer_calculate_coefs(a);
			li_config_table[i].curchnl = li_config_table[i].channel;
			li_config_table[j].curchnl = li_config_table[j].channel;
			if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
				li_config_table[k].curchnl = li_config_table[k].channel;
			break;

		case ADV_VOICE_WRITE_DEACTIVATION:
			for (j = 0; j < li_total_channels; j++)
			{
				li_config_table[i].flag_table[j] = 0;
				li_config_table[j].flag_table[i] = 0;
			}
			k = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
			for (j = 0; j < li_total_channels; j++)
			{
				li_config_table[k].flag_table[j] = 0;
				li_config_table[j].flag_table[k] = 0;
			}
			if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
			{
				k = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
				for (j = 0; j < li_total_channels; j++)
				{
					li_config_table[k].flag_table[j] = 0;
					li_config_table[j].flag_table[k] = 0;
				}
			}
			mixer_calculate_coefs(a);
			break;
		}
		if (plci->B1_facilities & B1_FACILITY_MIXER)
		{
			w = 0;
			if (ADV_VOICE_NEW_COEF_BASE + sizeof(word) <= a->adv_voice_coef_length)
				w = GET_WORD(a->adv_voice_coef_buffer + ADV_VOICE_NEW_COEF_BASE);
			if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
				w |= MIXER_FEATURE_ENABLE_TX_DATA;
			if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
				w |= MIXER_FEATURE_ENABLE_RX_DATA;
			*(p++) = (byte) w;
			*(p++) = (byte)(w >> 8);
			for (j = 0; j < sizeof(ch_map); j += 2)
			{
				ch_map[j] = (byte)(j + (plci->li_bchannel_id - 1));
				ch_map[j + 1] = (byte)(j + (2 - plci->li_bchannel_id));
			}
			for (n = 0; n < ARRAY_SIZE(mixer_write_prog_bri); n++)
			{
				i = a->li_base + ch_map[mixer_write_prog_bri[n].to_ch];
				j = a->li_base + ch_map[mixer_write_prog_bri[n].from_ch];
				if (li_config_table[i].channel & li_config_table[j].channel & LI_CHANNEL_INVOLVED)
				{
					*(p++) = ((li_config_table[i].coef_table[j] & mixer_write_prog_bri[n].mask) ? 0x80 : 0x01);
					w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
					li_config_table[i].coef_table[j] ^= (w & mixer_write_prog_bri[n].mask) << 4;
				}
				else
				{
					*(p++) = (ADV_VOICE_NEW_COEF_BASE + sizeof(word) + n < a->adv_voice_coef_length) ?
						a->adv_voice_coef_buffer[ADV_VOICE_NEW_COEF_BASE + sizeof(word) + n] : 0x00;
				}
			}
		}
		else
		{
			for (i = ADV_VOICE_NEW_COEF_BASE; i < a->adv_voice_coef_length; i++)
				*(p++) = a->adv_voice_coef_buffer[i];
		}
	}
	else

	{
		for (i = ADV_VOICE_NEW_COEF_BASE; i < a->adv_voice_coef_length; i++)
			*(p++) = a->adv_voice_coef_buffer[i];
	}
	coef_buffer[0] = (p - coef_buffer) - 1;
	add_p(plci, FTY, coef_buffer);
	sig_req(plci, TEL_CTRL, 0);
	send_req(plci);
}


static void adv_voice_clear_config(PLCI *plci)
{
	DIVA_CAPI_ADAPTER *a;

	word i, j;


	dbug(1, dprintf("[%06lx] %s,%d: adv_voice_clear_config",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__));

	a = plci->adapter;
	if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI))
	{
		a->adv_voice_coef_length = 0;

		if (!a->li_pri && (plci->li_bchannel_id != 0)
		    && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
		{
			i = a->li_base + (plci->li_bchannel_id - 1);
			li_config_table[i].curchnl = 0;
			li_config_table[i].channel = 0;
			li_config_table[i].chflags = 0;
			for (j = 0; j < li_total_channels; j++)
			{
				li_config_table[i].flag_table[j] = 0;
				li_config_table[j].flag_table[i] = 0;
				li_config_table[i].coef_table[j] = 0;
				li_config_table[j].coef_table[i] = 0;
			}
			li_config_table[i].coef_table[i] |= LI_COEF_CH_PC_SET | LI_COEF_PC_CH_SET;
			i = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
			li_config_table[i].curchnl = 0;
			li_config_table[i].channel = 0;
			li_config_table[i].chflags = 0;
			for (j = 0; j < li_total_channels; j++)
			{
				li_config_table[i].flag_table[j] = 0;
				li_config_table[j].flag_table[i] = 0;
				li_config_table[i].coef_table[j] = 0;
				li_config_table[j].coef_table[i] = 0;
			}
			if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
			{
				i = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
				li_config_table[i].curchnl = 0;
				li_config_table[i].channel = 0;
				li_config_table[i].chflags = 0;
				for (j = 0; j < li_total_channels; j++)
				{
					li_config_table[i].flag_table[j] = 0;
					li_config_table[j].flag_table[i] = 0;
					li_config_table[i].coef_table[j] = 0;
					li_config_table[j].coef_table[i] = 0;
				}
			}
		}

	}
}


static void adv_voice_prepare_switch(dword Id, PLCI *plci)
{

	dbug(1, dprintf("[%06lx] %s,%d: adv_voice_prepare_switch",
			UnMapId(Id), (char *)(FILE_), __LINE__));

}


static word adv_voice_save_config(dword Id, PLCI *plci, byte Rc)
{

	dbug(1, dprintf("[%06lx] %s,%d: adv_voice_save_config %02x %d",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));

	return (GOOD);
}


static word adv_voice_restore_config(dword Id, PLCI *plci, byte Rc)
{
	DIVA_CAPI_ADAPTER *a;
	word Info;

	dbug(1, dprintf("[%06lx] %s,%d: adv_voice_restore_config %02x %d",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));

	Info = GOOD;
	a = plci->adapter;
	if ((plci->B1_facilities & B1_FACILITY_VOICE)
	    && (plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI))
	{
		switch (plci->adjust_b_state)
		{
		case ADJUST_B_RESTORE_VOICE_1:
			plci->internal_command = plci->adjust_b_command;
			if (plci->sig_req)
			{
				plci->adjust_b_state = ADJUST_B_RESTORE_VOICE_1;
				break;
			}
			adv_voice_write_coefs(plci, ADV_VOICE_WRITE_UPDATE);
			plci->adjust_b_state = ADJUST_B_RESTORE_VOICE_2;
			break;
		case ADJUST_B_RESTORE_VOICE_2:
			if ((Rc != OK) && (Rc != OK_FC))
			{
				dbug(1, dprintf("[%06lx] %s,%d: Restore voice config failed %02x",
						UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
				Info = _WRONG_STATE;
				break;
			}
			break;
		}
	}
	return (Info);
}




/*------------------------------------------------------------------*/
/* B1 resource switching                                            */
/*------------------------------------------------------------------*/

static byte b1_facilities_table[] =
{
	0x00,  /* 0  No bchannel resources      */
	0x00,  /* 1  Codec (automatic law)      */
	0x00,  /* 2  Codec (A-law)              */
	0x00,  /* 3  Codec (y-law)              */
	0x00,  /* 4  HDLC for X.21              */
	0x00,  /* 5  HDLC                       */
	0x00,  /* 6  External Device 0          */
	0x00,  /* 7  External Device 1          */
	0x00,  /* 8  HDLC 56k                   */
	0x00,  /* 9  Transparent                */
	0x00,  /* 10 Loopback to network        */
	0x00,  /* 11 Test pattern to net        */
	0x00,  /* 12 Rate adaptation sync       */
	0x00,  /* 13 Rate adaptation async      */
	0x00,  /* 14 R-Interface                */
	0x00,  /* 15 HDLC 128k leased line      */
	0x00,  /* 16 FAX                        */
	0x00,  /* 17 Modem async                */
	0x00,  /* 18 Modem sync HDLC            */
	0x00,  /* 19 V.110 async HDLC           */
	0x12,  /* 20 Adv voice (Trans,mixer)    */
	0x00,  /* 21 Codec connected to IC      */
	0x0c,  /* 22 Trans,DTMF                 */
	0x1e,  /* 23 Trans,DTMF+mixer           */
	0x1f,  /* 24 Trans,DTMF+mixer+local     */
	0x13,  /* 25 Trans,mixer+local          */
	0x12,  /* 26 HDLC,mixer                 */
	0x12,  /* 27 HDLC 56k,mixer             */
	0x2c,  /* 28 Trans,LEC+DTMF             */
	0x3e,  /* 29 Trans,LEC+DTMF+mixer       */
	0x3f,  /* 30 Trans,LEC+DTMF+mixer+local */
	0x2c,  /* 31 RTP,LEC+DTMF               */
	0x3e,  /* 32 RTP,LEC+DTMF+mixer         */
	0x3f,  /* 33 RTP,LEC+DTMF+mixer+local   */
	0x00,  /* 34 Signaling task             */
	0x00,  /* 35 PIAFS                      */
	0x0c,  /* 36 Trans,DTMF+TONE            */
	0x1e,  /* 37 Trans,DTMF+TONE+mixer      */
	0x1f   /* 38 Trans,DTMF+TONE+mixer+local*/
};


static word get_b1_facilities(PLCI *plci, byte b1_resource)
{
	word b1_facilities;

	b1_facilities = b1_facilities_table[b1_resource];
	if ((b1_resource == 9) || (b1_resource == 20) || (b1_resource == 25))
	{

		if (!(((plci->requested_options_conn | plci->requested_options) & (1L << PRIVATE_DTMF_TONE))
		      || (plci->appl && (plci->adapter->requested_options_table[plci->appl->Id - 1] & (1L << PRIVATE_DTMF_TONE)))))

		{
			if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_SEND)
				b1_facilities |= B1_FACILITY_DTMFX;
			if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE)
				b1_facilities |= B1_FACILITY_DTMFR;
		}
	}
	if ((b1_resource == 17) || (b1_resource == 18))
	{
		if (plci->adapter->manufacturer_features & (MANUFACTURER_FEATURE_V18 | MANUFACTURER_FEATURE_VOWN))
			b1_facilities |= B1_FACILITY_DTMFX | B1_FACILITY_DTMFR;
	}
/*
  dbug (1, dprintf("[%06lx] %s,%d: get_b1_facilities %d %04x",
  (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
  (char far *)(FILE_), __LINE__, b1_resource, b1_facilites));
*/
	return (b1_facilities);
}


static byte add_b1_facilities(PLCI *plci, byte b1_resource, word b1_facilities)
{
	byte b;

	switch (b1_resource)
	{
	case 5:
	case 26:
		if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
			b = 26;
		else
			b = 5;
		break;

	case 8:
	case 27:
		if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
			b = 27;
		else
			b = 8;
		break;

	case 9:
	case 20:
	case 22:
	case 23:
	case 24:
	case 25:
	case 28:
	case 29:
	case 30:
	case 36:
	case 37:
	case 38:
		if (b1_facilities & B1_FACILITY_EC)
		{
			if (b1_facilities & B1_FACILITY_LOCAL)
				b = 30;
			else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
				b = 29;
			else
				b = 28;
		}

		else if ((b1_facilities & (B1_FACILITY_DTMFX | B1_FACILITY_DTMFR | B1_FACILITY_MIXER))
			 && (((plci->requested_options_conn | plci->requested_options) & (1L << PRIVATE_DTMF_TONE))
			     || (plci->appl && (plci->adapter->requested_options_table[plci->appl->Id - 1] & (1L << PRIVATE_DTMF_TONE)))))
		{
			if (b1_facilities & B1_FACILITY_LOCAL)
				b = 38;
			else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
				b = 37;
			else
				b = 36;
		}

		else if (((plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_HARDDTMF)
			  && !(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE))
			 || ((b1_facilities & B1_FACILITY_DTMFR)
			     && ((b1_facilities & B1_FACILITY_MIXER)
				 || !(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE)))
			 || ((b1_facilities & B1_FACILITY_DTMFX)
			     && ((b1_facilities & B1_FACILITY_MIXER)
				 || !(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_SEND))))
		{
			if (b1_facilities & B1_FACILITY_LOCAL)
				b = 24;
			else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
				b = 23;
			else
				b = 22;
		}
		else
		{
			if (b1_facilities & B1_FACILITY_LOCAL)
				b = 25;
			else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
				b = 20;
			else
				b = 9;
		}
		break;

	case 31:
	case 32:
	case 33:
		if (b1_facilities & B1_FACILITY_LOCAL)
			b = 33;
		else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
			b = 32;
		else
			b = 31;
		break;

	default:
		b = b1_resource;
	}
	dbug(1, dprintf("[%06lx] %s,%d: add_b1_facilities %d %04x %d %04x",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__,
			b1_resource, b1_facilities, b, get_b1_facilities(plci, b)));
	return (b);
}


static void adjust_b1_facilities(PLCI *plci, byte new_b1_resource, word new_b1_facilities)
{
	word removed_facilities;

	dbug(1, dprintf("[%06lx] %s,%d: adjust_b1_facilities %d %04x %04x",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__, new_b1_resource, new_b1_facilities,
			new_b1_facilities & get_b1_facilities(plci, new_b1_resource)));

	new_b1_facilities &= get_b1_facilities(plci, new_b1_resource);
	removed_facilities = plci->B1_facilities & ~new_b1_facilities;

	if (removed_facilities & B1_FACILITY_EC)
		ec_clear_config(plci);


	if (removed_facilities & B1_FACILITY_DTMFR)
	{
		dtmf_rec_clear_config(plci);
		dtmf_parameter_clear_config(plci);
	}
	if (removed_facilities & B1_FACILITY_DTMFX)
		dtmf_send_clear_config(plci);


	if (removed_facilities & B1_FACILITY_MIXER)
		mixer_clear_config(plci);

	if (removed_facilities & B1_FACILITY_VOICE)
		adv_voice_clear_config(plci);
	plci->B1_facilities = new_b1_facilities;
}


static void adjust_b_clear(PLCI *plci)
{

	dbug(1, dprintf("[%06lx] %s,%d: adjust_b_clear",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__));

	plci->adjust_b_restore = false;
}


static word adjust_b_process(dword Id, PLCI *plci, byte Rc)
{
	word Info;
	byte b1_resource;
	NCCI *ncci_ptr;
	API_PARSE bp[2];

	dbug(1, dprintf("[%06lx] %s,%d: adjust_b_process %02x %d",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));

	Info = GOOD;
	switch (plci->adjust_b_state)
	{
	case ADJUST_B_START:
		if ((plci->adjust_b_parms_msg == NULL)
		    && (plci->adjust_b_mode & ADJUST_B_MODE_SWITCH_L1)
		    && ((plci->adjust_b_mode & ~(ADJUST_B_MODE_SAVE | ADJUST_B_MODE_SWITCH_L1 |
						 ADJUST_B_MODE_NO_RESOURCE | ADJUST_B_MODE_RESTORE)) == 0))
		{
			b1_resource = (plci->adjust_b_mode == ADJUST_B_MODE_NO_RESOURCE) ?
				0 : add_b1_facilities(plci, plci->B1_resource, plci->adjust_b_facilities);
			if (b1_resource == plci->B1_resource)
			{
				adjust_b1_facilities(plci, b1_resource, plci->adjust_b_facilities);
				break;
			}
			if (plci->adjust_b_facilities & ~get_b1_facilities(plci, b1_resource))
			{
				dbug(1, dprintf("[%06lx] %s,%d: Adjust B nonsupported facilities %d %d %04x",
						UnMapId(Id), (char *)(FILE_), __LINE__,
						plci->B1_resource, b1_resource, plci->adjust_b_facilities));
				Info = _WRONG_STATE;
				break;
			}
		}
		if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
		{

			mixer_prepare_switch(Id, plci);


			dtmf_prepare_switch(Id, plci);
			dtmf_parameter_prepare_switch(Id, plci);


			ec_prepare_switch(Id, plci);

			adv_voice_prepare_switch(Id, plci);
		}
		plci->adjust_b_state = ADJUST_B_SAVE_MIXER_1;
		Rc = OK;
	case ADJUST_B_SAVE_MIXER_1:
		if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
		{

			Info = mixer_save_config(Id, plci, Rc);
			if ((Info != GOOD) || plci->internal_command)
				break;

		}
		plci->adjust_b_state = ADJUST_B_SAVE_DTMF_1;
		Rc = OK;
	case ADJUST_B_SAVE_DTMF_1:
		if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
		{

			Info = dtmf_save_config(Id, plci, Rc);
			if ((Info != GOOD) || plci->internal_command)
				break;

		}
		plci->adjust_b_state = ADJUST_B_REMOVE_L23_1;
	case ADJUST_B_REMOVE_L23_1:
		if ((plci->adjust_b_mode & ADJUST_B_MODE_REMOVE_L23)
		    && plci->NL.Id && !plci->nl_remove_id)
		{
			plci->internal_command = plci->adjust_b_command;
			if (plci->adjust_b_ncci != 0)
			{
				ncci_ptr = &(plci->adapter->ncci[plci->adjust_b_ncci]);
				while (ncci_ptr->data_pending)
				{
					plci->data_sent_ptr = ncci_ptr->DBuffer[ncci_ptr->data_out].P;
					data_rc(plci, plci->adapter->ncci_ch[plci->adjust_b_ncci]);
				}
				while (ncci_ptr->data_ack_pending)
					data_ack(plci, plci->adapter->ncci_ch[plci->adjust_b_ncci]);
			}
			nl_req_ncci(plci, REMOVE,
				    (byte)((plci->adjust_b_mode & ADJUST_B_MODE_CONNECT) ? plci->adjust_b_ncci : 0));
			send_req(plci);
			plci->adjust_b_state = ADJUST_B_REMOVE_L23_2;
			break;
		}
		plci->adjust_b_state = ADJUST_B_REMOVE_L23_2;
		Rc = OK;
	case ADJUST_B_REMOVE_L23_2:
		if ((Rc != OK) && (Rc != OK_FC))
		{
			dbug(1, dprintf("[%06lx] %s,%d: Adjust B remove failed %02x",
					UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
			Info = _WRONG_STATE;
			break;
		}
		if (plci->adjust_b_mode & ADJUST_B_MODE_REMOVE_L23)
		{
			if (plci_nl_busy(plci))
			{
				plci->internal_command = plci->adjust_b_command;
				break;
			}
		}
		plci->adjust_b_state = ADJUST_B_SAVE_EC_1;
		Rc = OK;
	case ADJUST_B_SAVE_EC_1:
		if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
		{

			Info = ec_save_config(Id, plci, Rc);
			if ((Info != GOOD) || plci->internal_command)
				break;

		}
		plci->adjust_b_state = ADJUST_B_SAVE_DTMF_PARAMETER_1;
		Rc = OK;
	case ADJUST_B_SAVE_DTMF_PARAMETER_1:
		if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
		{

			Info = dtmf_parameter_save_config(Id, plci, Rc);
			if ((Info != GOOD) || plci->internal_command)
				break;

		}
		plci->adjust_b_state = ADJUST_B_SAVE_VOICE_1;
		Rc = OK;
	case ADJUST_B_SAVE_VOICE_1:
		if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
		{
			Info = adv_voice_save_config(Id, plci, Rc);
			if ((Info != GOOD) || plci->internal_command)
				break;
		}
		plci->adjust_b_state = ADJUST_B_SWITCH_L1_1;
	case ADJUST_B_SWITCH_L1_1:
		if (plci->adjust_b_mode & ADJUST_B_MODE_SWITCH_L1)
		{
			if (plci->sig_req)
			{
				plci->internal_command = plci->adjust_b_command;
				break;
			}
			if (plci->adjust_b_parms_msg != NULL)
				api_load_msg(plci->adjust_b_parms_msg, bp);
			else
				api_load_msg(&plci->B_protocol, bp);
			Info = add_b1(plci, bp,
				      (word)((plci->adjust_b_mode & ADJUST_B_MODE_NO_RESOURCE) ? 2 : 0),
				      plci->adjust_b_facilities);
			if (Info != GOOD)
			{
				dbug(1, dprintf("[%06lx] %s,%d: Adjust B invalid L1 parameters %d %04x",
						UnMapId(Id), (char *)(FILE_), __LINE__,
						plci->B1_resource, plci->adjust_b_facilities));
				break;
			}
			plci->internal_command = plci->adjust_b_command;
			sig_req(plci, RESOURCES, 0);
			send_req(plci);
			plci->adjust_b_state = ADJUST_B_SWITCH_L1_2;
			break;
		}
		plci->adjust_b_state = ADJUST_B_SWITCH_L1_2;
		Rc = OK;
	case ADJUST_B_SWITCH_L1_2:
		if ((Rc != OK) && (Rc != OK_FC))
		{
			dbug(1, dprintf("[%06lx] %s,%d: Adjust B switch failed %02x %d %04x",
					UnMapId(Id), (char *)(FILE_), __LINE__,
					Rc, plci->B1_resource, plci->adjust_b_facilities));
			Info = _WRONG_STATE;
			break;
		}
		plci->adjust_b_state = ADJUST_B_RESTORE_VOICE_1;
		Rc = OK;
	case ADJUST_B_RESTORE_VOICE_1:
	case ADJUST_B_RESTORE_VOICE_2:
		if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
		{
			Info = adv_voice_restore_config(Id, plci, Rc);
			if ((Info != GOOD) || plci->internal_command)
				break;
		}
		plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_PARAMETER_1;
		Rc = OK;
	case ADJUST_B_RESTORE_DTMF_PARAMETER_1:
	case ADJUST_B_RESTORE_DTMF_PARAMETER_2:
		if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
		{

			Info = dtmf_parameter_restore_config(Id, plci, Rc);
			if ((Info != GOOD) || plci->internal_command)
				break;

		}
		plci->adjust_b_state = ADJUST_B_RESTORE_EC_1;
		Rc = OK;
	case ADJUST_B_RESTORE_EC_1:
	case ADJUST_B_RESTORE_EC_2:
		if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
		{

			Info = ec_restore_config(Id, plci, Rc);
			if ((Info != GOOD) || plci->internal_command)
				break;

		}
		plci->adjust_b_state = ADJUST_B_ASSIGN_L23_1;
	case ADJUST_B_ASSIGN_L23_1:
		if (plci->adjust_b_mode & ADJUST_B_MODE_ASSIGN_L23)
		{
			if (plci_nl_busy(plci))
			{
				plci->internal_command = plci->adjust_b_command;
				break;
			}
			if (plci->adjust_b_mode & ADJUST_B_MODE_CONNECT)
				plci->call_dir |= CALL_DIR_FORCE_OUTG_NL;
			if (plci->adjust_b_parms_msg != NULL)
				api_load_msg(plci->adjust_b_parms_msg, bp);
			else
				api_load_msg(&plci->B_protocol, bp);
			Info = add_b23(plci, bp);
			if (Info != GOOD)
			{
				dbug(1, dprintf("[%06lx] %s,%d: Adjust B invalid L23 parameters %04x",
						UnMapId(Id), (char *)(FILE_), __LINE__, Info));
				break;
			}
			plci->internal_command = plci->adjust_b_command;
			nl_req_ncci(plci, ASSIGN, 0);
			send_req(plci);
			plci->adjust_b_state = ADJUST_B_ASSIGN_L23_2;
			break;
		}
		plci->adjust_b_state = ADJUST_B_ASSIGN_L23_2;
		Rc = ASSIGN_OK;
	case ADJUST_B_ASSIGN_L23_2:
		if ((Rc != OK) && (Rc != OK_FC) && (Rc != ASSIGN_OK))
		{
			dbug(1, dprintf("[%06lx] %s,%d: Adjust B assign failed %02x",
					UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
			Info = _WRONG_STATE;
			break;
		}
		if (plci->adjust_b_mode & ADJUST_B_MODE_ASSIGN_L23)
		{
			if (Rc != ASSIGN_OK)
			{
				plci->internal_command = plci->adjust_b_command;
				break;
			}
		}
		if (plci->adjust_b_mode & ADJUST_B_MODE_USER_CONNECT)
		{
			plci->adjust_b_restore = true;
			break;
		}
		plci->adjust_b_state = ADJUST_B_CONNECT_1;
	case ADJUST_B_CONNECT_1:
		if (plci->adjust_b_mode & ADJUST_B_MODE_CONNECT)
		{
			plci->internal_command = plci->adjust_b_command;
			if (plci_nl_busy(plci))
				break;
			nl_req_ncci(plci, N_CONNECT, 0);
			send_req(plci);
			plci->adjust_b_state = ADJUST_B_CONNECT_2;
			break;
		}
		plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1;
		Rc = OK;
	case ADJUST_B_CONNECT_2:
	case ADJUST_B_CONNECT_3:
	case ADJUST_B_CONNECT_4:
		if ((Rc != OK) && (Rc != OK_FC) && (Rc != 0))
		{
			dbug(1, dprintf("[%06lx] %s,%d: Adjust B connect failed %02x",
					UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
			Info = _WRONG_STATE;
			break;
		}
		if (Rc == OK)
		{
			if (plci->adjust_b_mode & ADJUST_B_MODE_CONNECT)
			{
				get_ncci(plci, (byte)(Id >> 16), plci->adjust_b_ncci);
				Id = (Id & 0xffff) | (((dword)(plci->adjust_b_ncci)) << 16);
			}
			if (plci->adjust_b_state == ADJUST_B_CONNECT_2)
				plci->adjust_b_state = ADJUST_B_CONNECT_3;
			else if (plci->adjust_b_state == ADJUST_B_CONNECT_4)
				plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1;
		}
		else if (Rc == 0)
		{
			if (plci->adjust_b_state == ADJUST_B_CONNECT_2)
				plci->adjust_b_state = ADJUST_B_CONNECT_4;
			else if (plci->adjust_b_state == ADJUST_B_CONNECT_3)
				plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1;
		}
		if (plci->adjust_b_state != ADJUST_B_RESTORE_DTMF_1)
		{
			plci->internal_command = plci->adjust_b_command;
			break;
		}
		Rc = OK;
	case ADJUST_B_RESTORE_DTMF_1:
	case ADJUST_B_RESTORE_DTMF_2:
		if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
		{

			Info = dtmf_restore_config(Id, plci, Rc);
			if ((Info != GOOD) || plci->internal_command)
				break;

		}
		plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_1;
		Rc = OK;
	case ADJUST_B_RESTORE_MIXER_1:
	case ADJUST_B_RESTORE_MIXER_2:
	case ADJUST_B_RESTORE_MIXER_3:
	case ADJUST_B_RESTORE_MIXER_4:
	case ADJUST_B_RESTORE_MIXER_5:
	case ADJUST_B_RESTORE_MIXER_6:
	case ADJUST_B_RESTORE_MIXER_7:
		if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
		{

			Info = mixer_restore_config(Id, plci, Rc);
			if ((Info != GOOD) || plci->internal_command)
				break;

		}
		plci->adjust_b_state = ADJUST_B_END;
	case ADJUST_B_END:
		break;
	}
	return (Info);
}


static void adjust_b1_resource(dword Id, PLCI *plci, API_SAVE   *bp_msg, word b1_facilities, word internal_command)
{

	dbug(1, dprintf("[%06lx] %s,%d: adjust_b1_resource %d %04x",
			UnMapId(Id), (char *)(FILE_), __LINE__,
			plci->B1_resource, b1_facilities));

	plci->adjust_b_parms_msg = bp_msg;
	plci->adjust_b_facilities = b1_facilities;
	plci->adjust_b_command = internal_command;
	plci->adjust_b_ncci = (word)(Id >> 16);
	if ((bp_msg == NULL) && (plci->B1_resource == 0))
		plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_NO_RESOURCE | ADJUST_B_MODE_SWITCH_L1;
	else
		plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_SWITCH_L1 | ADJUST_B_MODE_RESTORE;
	plci->adjust_b_state = ADJUST_B_START;
	dbug(1, dprintf("[%06lx] %s,%d: Adjust B1 resource %d %04x...",
			UnMapId(Id), (char *)(FILE_), __LINE__,
			plci->B1_resource, b1_facilities));
}


static void adjust_b_restore(dword Id, PLCI *plci, byte Rc)
{
	word internal_command;

	dbug(1, dprintf("[%06lx] %s,%d: adjust_b_restore %02x %04x",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));

	internal_command = plci->internal_command;
	plci->internal_command = 0;
	switch (internal_command)
	{
	default:
		plci->command = 0;
		if (plci->req_in != 0)
		{
			plci->internal_command = ADJUST_B_RESTORE_1;
			break;
		}
		Rc = OK;
	case ADJUST_B_RESTORE_1:
		if ((Rc != OK) && (Rc != OK_FC))
		{
			dbug(1, dprintf("[%06lx] %s,%d: Adjust B enqueued failed %02x",
					UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
		}
		plci->adjust_b_parms_msg = NULL;
		plci->adjust_b_facilities = plci->B1_facilities;
		plci->adjust_b_command = ADJUST_B_RESTORE_2;
		plci->adjust_b_ncci = (word)(Id >> 16);
		plci->adjust_b_mode = ADJUST_B_MODE_RESTORE;
		plci->adjust_b_state = ADJUST_B_START;
		dbug(1, dprintf("[%06lx] %s,%d: Adjust B restore...",
				UnMapId(Id), (char *)(FILE_), __LINE__));
	case ADJUST_B_RESTORE_2:
		if (adjust_b_process(Id, plci, Rc) != GOOD)
		{
			dbug(1, dprintf("[%06lx] %s,%d: Adjust B restore failed",
					UnMapId(Id), (char *)(FILE_), __LINE__));
		}
		if (plci->internal_command)
			break;
		break;
	}
}


static void reset_b3_command(dword Id, PLCI *plci, byte Rc)
{
	word Info;
	word internal_command;

	dbug(1, dprintf("[%06lx] %s,%d: reset_b3_command %02x %04x",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));

	Info = GOOD;
	internal_command = plci->internal_command;
	plci->internal_command = 0;
	switch (internal_command)
	{
	default:
		plci->command = 0;
		plci->adjust_b_parms_msg = NULL;
		plci->adjust_b_facilities = plci->B1_facilities;
		plci->adjust_b_command = RESET_B3_COMMAND_1;
		plci->adjust_b_ncci = (word)(Id >> 16);
		plci->adjust_b_mode = ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_ASSIGN_L23 | ADJUST_B_MODE_CONNECT;
		plci->adjust_b_state = ADJUST_B_START;
		dbug(1, dprintf("[%06lx] %s,%d: Reset B3...",
				UnMapId(Id), (char *)(FILE_), __LINE__));
	case RESET_B3_COMMAND_1:
		Info = adjust_b_process(Id, plci, Rc);
		if (Info != GOOD)
		{
			dbug(1, dprintf("[%06lx] %s,%d: Reset failed",
					UnMapId(Id), (char *)(FILE_), __LINE__));
			break;
		}
		if (plci->internal_command)
			return;
		break;
	}
/*  sendf (plci->appl, _RESET_B3_R | CONFIRM, Id, plci->number, "w", Info);*/
	sendf(plci->appl, _RESET_B3_I, Id, 0, "s", "");
}


static void select_b_command(dword Id, PLCI *plci, byte Rc)
{
	word Info;
	word internal_command;
	byte esc_chi[3];

	dbug(1, dprintf("[%06lx] %s,%d: select_b_command %02x %04x",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));

	Info = GOOD;
	internal_command = plci->internal_command;
	plci->internal_command = 0;
	switch (internal_command)
	{
	default:
		plci->command = 0;
		plci->adjust_b_parms_msg = &plci->saved_msg;
		if ((plci->tel == ADV_VOICE) && (plci == plci->adapter->AdvSignalPLCI))
			plci->adjust_b_facilities = plci->B1_facilities | B1_FACILITY_VOICE;
		else
			plci->adjust_b_facilities = plci->B1_facilities & ~B1_FACILITY_VOICE;
		plci->adjust_b_command = SELECT_B_COMMAND_1;
		plci->adjust_b_ncci = (word)(Id >> 16);
		if (plci->saved_msg.parms[0].length == 0)
		{
			plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_SWITCH_L1 |
				ADJUST_B_MODE_NO_RESOURCE;
		}
		else
		{
			plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_SWITCH_L1 |
				ADJUST_B_MODE_ASSIGN_L23 | ADJUST_B_MODE_USER_CONNECT | ADJUST_B_MODE_RESTORE;
		}
		plci->adjust_b_state = ADJUST_B_START;
		dbug(1, dprintf("[%06lx] %s,%d: Select B protocol...",
				UnMapId(Id), (char *)(FILE_), __LINE__));
	case SELECT_B_COMMAND_1:
		Info = adjust_b_process(Id, plci, Rc);
		if (Info != GOOD)
		{
			dbug(1, dprintf("[%06lx] %s,%d: Select B protocol failed",
					UnMapId(Id), (char *)(FILE_), __LINE__));
			break;
		}
		if (plci->internal_command)
			return;
		if (plci->tel == ADV_VOICE)
		{
			esc_chi[0] = 0x02;
			esc_chi[1] = 0x18;
			esc_chi[2] = plci->b_channel;
			SetVoiceChannel(plci->adapter->AdvCodecPLCI, esc_chi, plci->adapter);
		}
		break;
	}
	sendf(plci->appl, _SELECT_B_REQ | CONFIRM, Id, plci->number, "w", Info);
}


static void fax_connect_ack_command(dword Id, PLCI *plci, byte Rc)
{
	word internal_command;

	dbug(1, dprintf("[%06lx] %s,%d: fax_connect_ack_command %02x %04x",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));

	internal_command = plci->internal_command;
	plci->internal_command = 0;
	switch (internal_command)
	{
	default:
		plci->command = 0;
	case FAX_CONNECT_ACK_COMMAND_1:
		if (plci_nl_busy(plci))
		{
			plci->internal_command = FAX_CONNECT_ACK_COMMAND_1;
			return;
		}
		plci->internal_command = FAX_CONNECT_ACK_COMMAND_2;
		plci->NData[0].P = plci->fax_connect_info_buffer;
		plci->NData[0].PLength = plci->fax_connect_info_length;
		plci->NL.X = plci->NData;
		plci->NL.ReqCh = 0;
		plci->NL.Req = plci->nl_req = (byte) N_CONNECT_ACK;
		plci->adapter->request(&plci->NL);
		return;
	case FAX_CONNECT_ACK_COMMAND_2:
		if ((Rc != OK) && (Rc != OK_FC))
		{
			dbug(1, dprintf("[%06lx] %s,%d: FAX issue CONNECT ACK failed %02x",
					UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
			break;
		}
	}
	if ((plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
	    && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
	{
		if (plci->B3_prot == 4)
			sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
		else
			sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer);
		plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
	}
}


static void fax_edata_ack_command(dword Id, PLCI *plci, byte Rc)
{
	word internal_command;

	dbug(1, dprintf("[%06lx] %s,%d: fax_edata_ack_command %02x %04x",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));

	internal_command = plci->internal_command;
	plci->internal_command = 0;
	switch (internal_command)
	{
	default:
		plci->command = 0;
	case FAX_EDATA_ACK_COMMAND_1:
		if (plci_nl_busy(plci))
		{
			plci->internal_command = FAX_EDATA_ACK_COMMAND_1;
			return;
		}
		plci->internal_command = FAX_EDATA_ACK_COMMAND_2;
		plci->NData[0].P = plci->fax_connect_info_buffer;
		plci->NData[0].PLength = plci->fax_edata_ack_length;
		plci->NL.X = plci->NData;
		plci->NL.ReqCh = 0;
		plci->NL.Req = plci->nl_req = (byte) N_EDATA;
		plci->adapter->request(&plci->NL);
		return;
	case FAX_EDATA_ACK_COMMAND_2:
		if ((Rc != OK) && (Rc != OK_FC))
		{
			dbug(1, dprintf("[%06lx] %s,%d: FAX issue EDATA ACK failed %02x",
					UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
			break;
		}
	}
}


static void fax_connect_info_command(dword Id, PLCI *plci, byte Rc)
{
	word Info;
	word internal_command;

	dbug(1, dprintf("[%06lx] %s,%d: fax_connect_info_command %02x %04x",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));

	Info = GOOD;
	internal_command = plci->internal_command;
	plci->internal_command = 0;
	switch (internal_command)
	{
	default:
		plci->command = 0;
	case FAX_CONNECT_INFO_COMMAND_1:
		if (plci_nl_busy(plci))
		{
			plci->internal_command = FAX_CONNECT_INFO_COMMAND_1;
			return;
		}
		plci->internal_command = FAX_CONNECT_INFO_COMMAND_2;
		plci->NData[0].P = plci->fax_connect_info_buffer;
		plci->NData[0].PLength = plci->fax_connect_info_length;
		plci->NL.X = plci->NData;
		plci->NL.ReqCh = 0;
		plci->NL.Req = plci->nl_req = (byte) N_EDATA;
		plci->adapter->request(&plci->NL);
		return;
	case FAX_CONNECT_INFO_COMMAND_2:
		if ((Rc != OK) && (Rc != OK_FC))
		{
			dbug(1, dprintf("[%06lx] %s,%d: FAX setting connect info failed %02x",
					UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
			Info = _WRONG_STATE;
			break;
		}
		if (plci_nl_busy(plci))
		{
			plci->internal_command = FAX_CONNECT_INFO_COMMAND_2;
			return;
		}
		plci->command = _CONNECT_B3_R;
		nl_req_ncci(plci, N_CONNECT, 0);
		send_req(plci);
		return;
	}
	sendf(plci->appl, _CONNECT_B3_R | CONFIRM, Id, plci->number, "w", Info);
}


static void fax_adjust_b23_command(dword Id, PLCI *plci, byte Rc)
{
	word Info;
	word internal_command;

	dbug(1, dprintf("[%06lx] %s,%d: fax_adjust_b23_command %02x %04x",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));

	Info = GOOD;
	internal_command = plci->internal_command;
	plci->internal_command = 0;
	switch (internal_command)
	{
	default:
		plci->command = 0;
		plci->adjust_b_parms_msg = NULL;
		plci->adjust_b_facilities = plci->B1_facilities;
		plci->adjust_b_command = FAX_ADJUST_B23_COMMAND_1;
		plci->adjust_b_ncci = (word)(Id >> 16);
		plci->adjust_b_mode = ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_ASSIGN_L23;
		plci->adjust_b_state = ADJUST_B_START;
		dbug(1, dprintf("[%06lx] %s,%d: FAX adjust B23...",
				UnMapId(Id), (char *)(FILE_), __LINE__));
	case FAX_ADJUST_B23_COMMAND_1:
		Info = adjust_b_process(Id, plci, Rc);
		if (Info != GOOD)
		{
			dbug(1, dprintf("[%06lx] %s,%d: FAX adjust failed",
					UnMapId(Id), (char *)(FILE_), __LINE__));
			break;
		}
		if (plci->internal_command)
			return;
	case FAX_ADJUST_B23_COMMAND_2:
		if (plci_nl_busy(plci))
		{
			plci->internal_command = FAX_ADJUST_B23_COMMAND_2;
			return;
		}
		plci->command = _CONNECT_B3_R;
		nl_req_ncci(plci, N_CONNECT, 0);
		send_req(plci);
		return;
	}
	sendf(plci->appl, _CONNECT_B3_R | CONFIRM, Id, plci->number, "w", Info);
}


static void fax_disconnect_command(dword Id, PLCI *plci, byte Rc)
{
	word internal_command;

	dbug(1, dprintf("[%06lx] %s,%d: fax_disconnect_command %02x %04x",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));

	internal_command = plci->internal_command;
	plci->internal_command = 0;
	switch (internal_command)
	{
	default:
		plci->command = 0;
		plci->internal_command = FAX_DISCONNECT_COMMAND_1;
		return;
	case FAX_DISCONNECT_COMMAND_1:
	case FAX_DISCONNECT_COMMAND_2:
	case FAX_DISCONNECT_COMMAND_3:
		if ((Rc != OK) && (Rc != OK_FC) && (Rc != 0))
		{
			dbug(1, dprintf("[%06lx] %s,%d: FAX disconnect EDATA failed %02x",
					UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
			break;
		}
		if (Rc == OK)
		{
			if ((internal_command == FAX_DISCONNECT_COMMAND_1)
			    || (internal_command == FAX_DISCONNECT_COMMAND_2))
			{
				plci->internal_command = FAX_DISCONNECT_COMMAND_2;
			}
		}
		else if (Rc == 0)
		{
			if (internal_command == FAX_DISCONNECT_COMMAND_1)
				plci->internal_command = FAX_DISCONNECT_COMMAND_3;
		}
		return;
	}
}



static void rtp_connect_b3_req_command(dword Id, PLCI *plci, byte Rc)
{
	word Info;
	word internal_command;

	dbug(1, dprintf("[%06lx] %s,%d: rtp_connect_b3_req_command %02x %04x",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));

	Info = GOOD;
	internal_command = plci->internal_command;
	plci->internal_command = 0;
	switch (internal_command)
	{
	default:
		plci->command = 0;
	case RTP_CONNECT_B3_REQ_COMMAND_1:
		if (plci_nl_busy(plci))
		{
			plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_1;
			return;
		}
		plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_2;
		nl_req_ncci(plci, N_CONNECT, 0);
		send_req(plci);
		return;
	case RTP_CONNECT_B3_REQ_COMMAND_2:
		if ((Rc != OK) && (Rc != OK_FC))
		{
			dbug(1, dprintf("[%06lx] %s,%d: RTP setting connect info failed %02x",
					UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
			Info = _WRONG_STATE;
			break;
		}
		if (plci_nl_busy(plci))
		{
			plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_2;
			return;
		}
		plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_3;
		plci->NData[0].PLength = plci->internal_req_buffer[0];
		plci->NData[0].P = plci->internal_req_buffer + 1;
		plci->NL.X = plci->NData;
		plci->NL.ReqCh = 0;
		plci->NL.Req = plci->nl_req = (byte) N_UDATA;
		plci->adapter->request(&plci->NL);
		break;
	case RTP_CONNECT_B3_REQ_COMMAND_3:
		return;
	}
	sendf(plci->appl, _CONNECT_B3_R | CONFIRM, Id, plci->number, "w", Info);
}


static void rtp_connect_b3_res_command(dword Id, PLCI *plci, byte Rc)
{
	word internal_command;

	dbug(1, dprintf("[%06lx] %s,%d: rtp_connect_b3_res_command %02x %04x",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));

	internal_command = plci->internal_command;
	plci->internal_command = 0;
	switch (internal_command)
	{
	default:
		plci->command = 0;
	case RTP_CONNECT_B3_RES_COMMAND_1:
		if (plci_nl_busy(plci))
		{
			plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_1;
			return;
		}
		plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_2;
		nl_req_ncci(plci, N_CONNECT_ACK, (byte)(Id >> 16));
		send_req(plci);
		return;
	case RTP_CONNECT_B3_RES_COMMAND_2:
		if ((Rc != OK) && (Rc != OK_FC))
		{
			dbug(1, dprintf("[%06lx] %s,%d: RTP setting connect resp info failed %02x",
					UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
			break;
		}
		if (plci_nl_busy(plci))
		{
			plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_2;
			return;
		}
		sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
		plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_3;
		plci->NData[0].PLength = plci->internal_req_buffer[0];
		plci->NData[0].P = plci->internal_req_buffer + 1;
		plci->NL.X = plci->NData;
		plci->NL.ReqCh = 0;
		plci->NL.Req = plci->nl_req = (byte) N_UDATA;
		plci->adapter->request(&plci->NL);
		return;
	case RTP_CONNECT_B3_RES_COMMAND_3:
		return;
	}
}



static void hold_save_command(dword Id, PLCI *plci, byte Rc)
{
	byte SS_Ind[] = "\x05\x02\x00\x02\x00\x00"; /* Hold_Ind struct*/
	word Info;
	word internal_command;

	dbug(1, dprintf("[%06lx] %s,%d: hold_save_command %02x %04x",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));

	Info = GOOD;
	internal_command = plci->internal_command;
	plci->internal_command = 0;
	switch (internal_command)
	{
	default:
		if (!plci->NL.Id)
			break;
		plci->command = 0;
		plci->adjust_b_parms_msg = NULL;
		plci->adjust_b_facilities = plci->B1_facilities;
		plci->adjust_b_command = HOLD_SAVE_COMMAND_1;
		plci->adjust_b_ncci = (word)(Id >> 16);
		plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_REMOVE_L23;
		plci->adjust_b_state = ADJUST_B_START;
		dbug(1, dprintf("[%06lx] %s,%d: HOLD save...",
				UnMapId(Id), (char *)(FILE_), __LINE__));
	case HOLD_SAVE_COMMAND_1:
		Info = adjust_b_process(Id, plci, Rc);
		if (Info != GOOD)
		{
			dbug(1, dprintf("[%06lx] %s,%d: HOLD save failed",
					UnMapId(Id), (char *)(FILE_), __LINE__));
			break;
		}
		if (plci->internal_command)
			return;
	}
	sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", 3, SS_Ind);
}


static void retrieve_restore_command(dword Id, PLCI *plci, byte Rc)
{
	byte SS_Ind[] = "\x05\x03\x00\x02\x00\x00"; /* Retrieve_Ind struct*/
	word Info;
	word internal_command;

	dbug(1, dprintf("[%06lx] %s,%d: retrieve_restore_command %02x %04x",
			UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));

	Info = GOOD;
	internal_command = plci->internal_command;
	plci->internal_command = 0;
	switch (internal_command)
	{
	default:
		plci->command = 0;
		plci->adjust_b_parms_msg = NULL;
		plci->adjust_b_facilities = plci->B1_facilities;
		plci->adjust_b_command = RETRIEVE_RESTORE_COMMAND_1;
		plci->adjust_b_ncci = (word)(Id >> 16);
		plci->adjust_b_mode = ADJUST_B_MODE_ASSIGN_L23 | ADJUST_B_MODE_USER_CONNECT | ADJUST_B_MODE_RESTORE;
		plci->adjust_b_state = ADJUST_B_START;
		dbug(1, dprintf("[%06lx] %s,%d: RETRIEVE restore...",
				UnMapId(Id), (char *)(FILE_), __LINE__));
	case RETRIEVE_RESTORE_COMMAND_1:
		Info = adjust_b_process(Id, plci, Rc);
		if (Info != GOOD)
		{
			dbug(1, dprintf("[%06lx] %s,%d: RETRIEVE restore failed",
					UnMapId(Id), (char *)(FILE_), __LINE__));
			break;
		}
		if (plci->internal_command)
			return;
	}
	sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", 3, SS_Ind);
}


static void init_b1_config(PLCI *plci)
{

	dbug(1, dprintf("[%06lx] %s,%d: init_b1_config",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__));

	plci->B1_resource = 0;
	plci->B1_facilities = 0;

	plci->li_bchannel_id = 0;
	mixer_clear_config(plci);


	ec_clear_config(plci);


	dtmf_rec_clear_config(plci);
	dtmf_send_clear_config(plci);
	dtmf_parameter_clear_config(plci);

	adv_voice_clear_config(plci);
	adjust_b_clear(plci);
}


static void clear_b1_config(PLCI *plci)
{

	dbug(1, dprintf("[%06lx] %s,%d: clear_b1_config",
			(dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
			(char *)(FILE_), __LINE__));

	adv_voice_clear_config(plci);
	adjust_b_clear(plci);

	ec_clear_config(plci);


	dtmf_rec_clear_config(plci);
	dtmf_send_clear_config(plci);
	dtmf_parameter_clear_config(plci);


	if ((plci->li_bchannel_id != 0)
	    && (li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci == plci))
	{
		mixer_clear_config(plci);
		li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci = NULL;
		plci->li_bchannel_id = 0;
	}

	plci->B1_resource = 0;
	plci->B1_facilities = 0;
}


/* -----------------------------------------------------------------
   XON protocol local helpers
   ----------------------------------------------------------------- */
static void channel_flow_control_remove(PLCI *plci) {
	DIVA_CAPI_ADAPTER *a = plci->adapter;
	word i;
	for (i = 1; i < MAX_NL_CHANNEL + 1; i++) {
		if (a->ch_flow_plci[i] == plci->Id) {
			a->ch_flow_plci[i] = 0;
			a->ch_flow_control[i] = 0;
		}
	}
}

static void channel_x_on(PLCI *plci, byte ch) {
	DIVA_CAPI_ADAPTER *a = plci->adapter;
	if (a->ch_flow_control[ch] & N_XON_SENT) {
		a->ch_flow_control[ch] &= ~N_XON_SENT;
	}
}

static void channel_x_off(PLCI *plci, byte ch, byte flag) {
	DIVA_CAPI_ADAPTER *a = plci->adapter;
	if ((a->ch_flow_control[ch] & N_RX_FLOW_CONTROL_MASK) == 0) {
		a->ch_flow_control[ch] |= (N_CH_XOFF | flag);
		a->ch_flow_plci[ch] = plci->Id;
		a->ch_flow_control_pending++;
	}
}

static void channel_request_xon(PLCI *plci, byte ch) {
	DIVA_CAPI_ADAPTER *a = plci->adapter;

	if (a->ch_flow_control[ch] & N_CH_XOFF) {
		a->ch_flow_control[ch] |= N_XON_REQ;
		a->ch_flow_control[ch] &= ~N_CH_XOFF;
		a->ch_flow_control[ch] &= ~N_XON_CONNECT_IND;
	}
}

static void channel_xmit_extended_xon(PLCI *plci) {
	DIVA_CAPI_ADAPTER *a;
	int max_ch = ARRAY_SIZE(a->ch_flow_control);
	int i, one_requested = 0;

	if ((!plci) || (!plci->Id) || ((a = plci->adapter) == NULL)) {
		return;
	}

	for (i = 0; i < max_ch; i++) {
		if ((a->ch_flow_control[i] & N_CH_XOFF) &&
		    (a->ch_flow_control[i] & N_XON_CONNECT_IND) &&
		    (plci->Id == a->ch_flow_plci[i])) {
			channel_request_xon(plci, (byte)i);
			one_requested = 1;
		}
	}

	if (one_requested) {
		channel_xmit_xon(plci);
	}
}

/*
  Try to xmit next X_ON
*/
static int find_channel_with_pending_x_on(DIVA_CAPI_ADAPTER *a, PLCI *plci) {
	int max_ch = ARRAY_SIZE(a->ch_flow_control);
	int i;

	if (!(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL)) {
		return (0);
	}

	if (a->last_flow_control_ch >= max_ch) {
		a->last_flow_control_ch = 1;
	}
	for (i = a->last_flow_control_ch; i < max_ch; i++) {
		if ((a->ch_flow_control[i] & N_XON_REQ) &&
		    (plci->Id == a->ch_flow_plci[i])) {
			a->last_flow_control_ch = i + 1;
			return (i);
		}
	}

	for (i = 1; i < a->last_flow_control_ch; i++) {
		if ((a->ch_flow_control[i] & N_XON_REQ) &&
		    (plci->Id == a->ch_flow_plci[i])) {
			a->last_flow_control_ch = i + 1;
			return (i);
		}
	}

	return (0);
}

static void channel_xmit_xon(PLCI *plci) {
	DIVA_CAPI_ADAPTER *a = plci->adapter;
	byte ch;

	if (plci->nl_req || !plci->NL.Id || plci->nl_remove_id) {
		return;
	}
	if ((ch = (byte)find_channel_with_pending_x_on(a, plci)) == 0) {
		return;
	}
	a->ch_flow_control[ch] &= ~N_XON_REQ;
	a->ch_flow_control[ch] |= N_XON_SENT;

	plci->NL.Req = plci->nl_req = (byte)N_XON;
	plci->NL.ReqCh         = ch;
	plci->NL.X             = plci->NData;
	plci->NL.XNum          = 1;
	plci->NData[0].P       = &plci->RBuffer[0];
	plci->NData[0].PLength = 0;

	plci->adapter->request(&plci->NL);
}

static int channel_can_xon(PLCI *plci, byte ch) {
	APPL *APPLptr;
	DIVA_CAPI_ADAPTER *a;
	word NCCIcode;
	dword count;
	word Num;
	word i;

	APPLptr = plci->appl;
	a = plci->adapter;

	if (!APPLptr)
		return (0);

	NCCIcode = a->ch_ncci[ch] | (((word) a->Id) << 8);

	/* count all buffers within the Application pool    */
	/* belonging to the same NCCI. XON if a first is    */
	/* used.                                            */
	count = 0;
	Num = 0xffff;
	for (i = 0; i < APPLptr->MaxBuffer; i++) {
		if (NCCIcode == APPLptr->DataNCCI[i]) count++;
		if (!APPLptr->DataNCCI[i] && Num == 0xffff) Num = i;
	}
	if ((count > 2) || (Num == 0xffff)) {
		return (0);
	}
	return (1);
}


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

static word CPN_filter_ok(byte *cpn, DIVA_CAPI_ADAPTER *a, word offset)
{
	return 1;
}



/**********************************************************************************/
/* function groups the listening applications according to the CIP mask and the   */
/* Info_Mask. Each group gets just one Connect_Ind. Some application manufacturer */
/* are not multi-instance capable, so they start e.g. 30 applications what causes */
/* big problems on application level (one call, 30 Connect_Ind, ect). The         */
/* function must be enabled by setting "a->group_optimization_enabled" from the   */
/* OS specific part (per adapter).                                                */
/**********************************************************************************/
static void group_optimization(DIVA_CAPI_ADAPTER *a, PLCI *plci)
{
	word i, j, k, busy, group_found;
	dword info_mask_group[MAX_CIP_TYPES];
	dword cip_mask_group[MAX_CIP_TYPES];
	word appl_number_group_type[MAX_APPL];
	PLCI *auxplci;

	set_group_ind_mask(plci); /* all APPLs within this inc. call are allowed to dial in */

	if (!a->group_optimization_enabled)
	{
		dbug(1, dprintf("No group optimization"));
		return;
	}

	dbug(1, dprintf("Group optimization = 0x%x...", a->group_optimization_enabled));

	for (i = 0; i < MAX_CIP_TYPES; i++)
	{
		info_mask_group[i] = 0;
		cip_mask_group[i] = 0;
	}
	for (i = 0; i < MAX_APPL; i++)
	{
		appl_number_group_type[i] = 0;
	}
	for (i = 0; i < max_appl; i++) /* check if any multi instance capable application is present */
	{  /* group_optimization set to 1 means not to optimize multi-instance capable applications (default) */
		if (application[i].Id && (application[i].MaxNCCI) > 1 && (a->CIP_Mask[i]) && (a->group_optimization_enabled == 1))
		{
			dbug(1, dprintf("Multi-Instance capable, no optimization required"));
			return; /* allow good application unfiltered access */
		}
	}
	for (i = 0; i < max_appl; i++) /* Build CIP Groups */
	{
		if (application[i].Id && a->CIP_Mask[i])
		{
			for (k = 0, busy = false; k < a->max_plci; k++)
			{
				if (a->plci[k].Id)
				{
					auxplci = &a->plci[k];
					if (auxplci->appl == &application[i]) /* application has a busy PLCI */
					{
						busy = true;
						dbug(1, dprintf("Appl 0x%x is busy", i + 1));
					}
					else if (test_c_ind_mask_bit(auxplci, i)) /* application has an incoming call pending */
					{
						busy = true;
						dbug(1, dprintf("Appl 0x%x has inc. call pending", i + 1));
					}
				}
			}

			for (j = 0, group_found = 0; j <= (MAX_CIP_TYPES) && !busy && !group_found; j++)     /* build groups with free applications only */
			{
				if (j == MAX_CIP_TYPES)       /* all groups are in use but group still not found */
				{                           /* the MAX_CIP_TYPES group enables all calls because of field overflow */
					appl_number_group_type[i] = MAX_CIP_TYPES;
					group_found = true;
					dbug(1, dprintf("Field overflow appl 0x%x", i + 1));
				}
				else if ((info_mask_group[j] == a->CIP_Mask[i]) && (cip_mask_group[j] == a->Info_Mask[i]))
				{                                      /* is group already present ?                  */
					appl_number_group_type[i] = j | 0x80;  /* store the group number for each application */
					group_found = true;
					dbug(1, dprintf("Group 0x%x found with appl 0x%x, CIP=0x%lx", appl_number_group_type[i], i + 1, info_mask_group[j]));
				}
				else if (!info_mask_group[j])
				{                                      /* establish a new group                       */
					appl_number_group_type[i] = j | 0x80;  /* store the group number for each application */
					info_mask_group[j] = a->CIP_Mask[i]; /* store the new CIP mask for the new group    */
					cip_mask_group[j] = a->Info_Mask[i]; /* store the new Info_Mask for this new group  */
					group_found = true;
					dbug(1, dprintf("New Group 0x%x established with appl 0x%x, CIP=0x%lx", appl_number_group_type[i], i + 1, info_mask_group[j]));
				}
			}
		}
	}

	for (i = 0; i < max_appl; i++) /* Build group_optimization_mask_table */
	{
		if (appl_number_group_type[i]) /* application is free, has listens and is member of a group */
		{
			if (appl_number_group_type[i] == MAX_CIP_TYPES)
			{
				dbug(1, dprintf("OverflowGroup 0x%x, valid appl = 0x%x, call enabled", appl_number_group_type[i], i + 1));
			}
			else
			{
				dbug(1, dprintf("Group 0x%x, valid appl = 0x%x", appl_number_group_type[i], i + 1));
				for (j = i + 1; j < max_appl; j++)   /* search other group members and mark them as busy        */
				{
					if (appl_number_group_type[i] == appl_number_group_type[j])
					{
						dbug(1, dprintf("Appl 0x%x is member of group 0x%x, no call", j + 1, appl_number_group_type[j]));
						clear_group_ind_mask_bit(plci, j);           /* disable call on other group members */
						appl_number_group_type[j] = 0;       /* remove disabled group member from group list */
					}
				}
			}
		}
		else                                                 /* application should not get a call */
		{
			clear_group_ind_mask_bit(plci, i);
		}
	}

}



/* OS notifies the driver about a application Capi_Register */
word CapiRegister(word id)
{
	word i, j, appls_found;

	PLCI *plci;
	DIVA_CAPI_ADAPTER *a;

	for (i = 0, appls_found = 0; i < max_appl; i++)
	{
		if (application[i].Id && (application[i].Id != id))
		{
			appls_found++;                       /* an application has been found */
		}
	}

	if (appls_found) return true;
	for (i = 0; i < max_adapter; i++)                   /* scan all adapters...    */
	{
		a = &adapter[i];
		if (a->request)
		{
			if (a->flag_dynamic_l1_down)  /* remove adapter from L1 tristate (Huntgroup) */
			{
				if (!appls_found)           /* first application does a capi register   */
				{
					if ((j = get_plci(a)))                    /* activate L1 of all adapters */
					{
						plci = &a->plci[j - 1];
						plci->command = 0;
						add_p(plci, OAD, "\x01\xfd");
						add_p(plci, CAI, "\x01\x80");
						add_p(plci, UID, "\x06\x43\x61\x70\x69\x32\x30");
						add_p(plci, SHIFT | 6, NULL);
						add_p(plci, SIN, "\x02\x00\x00");
						plci->internal_command = START_L1_SIG_ASSIGN_PEND;
						sig_req(plci, ASSIGN, DSIG_ID);
						add_p(plci, FTY, "\x02\xff\x07"); /* l1 start */
						sig_req(plci, SIG_CTRL, 0);
						send_req(plci);
					}
				}
			}
		}
	}
	return false;
}

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

/* Functions for virtual Switching e.g. Transfer by join, Conference */

static void VSwitchReqInd(PLCI *plci, dword Id, byte **parms)
{
	word i;
	/* Format of vswitch_t:
	   0 byte length
	   1 byte VSWITCHIE
	   2 byte VSWITCH_REQ/VSWITCH_IND
	   3 byte reserved
	   4 word VSwitchcommand
	   6 word returnerror
	   8... Params
	*/
	if (!plci ||
	    !plci->appl ||
	    !plci->State ||
	    plci->Sig.Ind == NCR_FACILITY
		)
		return;

	for (i = 0; i < MAX_MULTI_IE; i++)
	{
		if (!parms[i][0]) continue;
		if (parms[i][0] < 7)
		{
			parms[i][0] = 0; /* kill it */
			continue;
		}
		dbug(1, dprintf("VSwitchReqInd(%d)", parms[i][4]));
		switch (parms[i][4])
		{
		case VSJOIN:
			if (!plci->relatedPTYPLCI ||
			    (plci->ptyState != S_ECT && plci->relatedPTYPLCI->ptyState != S_ECT))
			{ /* Error */
				break;
			}
			/* remember all necessary informations */
			if (parms[i][0] != 11 || parms[i][8] != 3) /* Length Test */
			{
				break;
			}
			if (parms[i][2] == VSWITCH_IND && parms[i][9] == 1)
			{   /* first indication after ECT-Request on Consultation Call */
				plci->vswitchstate = parms[i][9];
				parms[i][9] = 2; /* State */
				/* now ask first Call to join */
			}
			else if (parms[i][2] == VSWITCH_REQ && parms[i][9] == 3)
			{ /* Answer of VSWITCH_REQ from first Call */
				plci->vswitchstate = parms[i][9];
				/* tell consultation call to join
				   and the protocol capabilities of the first call */
			}
			else
			{ /* Error */
				break;
			}
			plci->vsprot = parms[i][10]; /* protocol */
			plci->vsprotdialect = parms[i][11]; /* protocoldialect */
			/* send join request to related PLCI */
			parms[i][1] = VSWITCHIE;
			parms[i][2] = VSWITCH_REQ;

			plci->relatedPTYPLCI->command = 0;
			plci->relatedPTYPLCI->internal_command = VSWITCH_REQ_PEND;
			add_p(plci->relatedPTYPLCI, ESC, &parms[i][0]);
			sig_req(plci->relatedPTYPLCI, VSWITCH_REQ, 0);
			send_req(plci->relatedPTYPLCI);
			break;
		case VSTRANSPORT:
		default:
			if (plci->relatedPTYPLCI &&
			    plci->vswitchstate == 3 &&
			    plci->relatedPTYPLCI->vswitchstate == 3)
			{
				add_p(plci->relatedPTYPLCI, ESC, &parms[i][0]);
				sig_req(plci->relatedPTYPLCI, VSWITCH_REQ, 0);
				send_req(plci->relatedPTYPLCI);
			}
			break;
		}
		parms[i][0] = 0; /* kill it */
	}
}


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

static int diva_get_dma_descriptor(PLCI *plci, dword   *dma_magic) {
	ENTITY e;
	IDI_SYNC_REQ *pReq = (IDI_SYNC_REQ *)&e;

	if (!(diva_xdi_extended_features & DIVA_CAPI_XDI_PROVIDES_RX_DMA)) {
		return (-1);
	}

	pReq->xdi_dma_descriptor_operation.Req = 0;
	pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION;

	pReq->xdi_dma_descriptor_operation.info.operation = IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC;
	pReq->xdi_dma_descriptor_operation.info.descriptor_number  = -1;
	pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL;
	pReq->xdi_dma_descriptor_operation.info.descriptor_magic   = 0;

	e.user[0] = plci->adapter->Id - 1;
	plci->adapter->request((ENTITY *)pReq);

	if (!pReq->xdi_dma_descriptor_operation.info.operation &&
	    (pReq->xdi_dma_descriptor_operation.info.descriptor_number >= 0) &&
	    pReq->xdi_dma_descriptor_operation.info.descriptor_magic) {
		*dma_magic = pReq->xdi_dma_descriptor_operation.info.descriptor_magic;
		dbug(3, dprintf("dma_alloc, a:%d (%d-%08x)",
				plci->adapter->Id,
				pReq->xdi_dma_descriptor_operation.info.descriptor_number,
				*dma_magic));
		return (pReq->xdi_dma_descriptor_operation.info.descriptor_number);
	} else {
		dbug(1, dprintf("dma_alloc failed"));
		return (-1);
	}
}

static void diva_free_dma_descriptor(PLCI *plci, int nr) {
	ENTITY e;
	IDI_SYNC_REQ *pReq = (IDI_SYNC_REQ *)&e;

	if (nr < 0) {
		return;
	}

	pReq->xdi_dma_descriptor_operation.Req = 0;
	pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION;

	pReq->xdi_dma_descriptor_operation.info.operation = IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE;
	pReq->xdi_dma_descriptor_operation.info.descriptor_number  = nr;
	pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL;
	pReq->xdi_dma_descriptor_operation.info.descriptor_magic   = 0;

	e.user[0] = plci->adapter->Id - 1;
	plci->adapter->request((ENTITY *)pReq);

	if (!pReq->xdi_dma_descriptor_operation.info.operation) {
		dbug(1, dprintf("dma_free(%d)", nr));
	} else {
		dbug(1, dprintf("dma_free failed (%d)", nr));
	}
}

/*------------------------------------------------------------------*/
back to top