/* * 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; word CIP; 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]); CIP = GET_WORD(parms[0].info); 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 Number = 0; 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) Number = 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: Number = 0; 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 Info; word i; byte result[4]; dbug (1, dprintf ("[%06lx] %s,%d: dtmf_confirmation", UnMapId (Id), (char *)(FILE_), __LINE__)); Info = GOOD; 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, Info; dbug (1, dprintf ("[%06lx] %s,%d: mixer_command %02x %04x %04x", UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command, plci->li_cmd)); Info = GOOD; 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__)); Info = _FACILITY_NOT_SUPPORTED; 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)); } Info = _FACILITY_NOT_SUPPORTED; 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__)); Info = _FACILITY_NOT_SUPPORTED; 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; DIVA_CAPI_ADAPTER *a; byte result[12]; dbug (1, dprintf ("[%06lx] %s,%d: mixer_indication_coefs_set", UnMapId (Id), (char *)(FILE_), __LINE__)); a = plci->adapter; 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 (¶meter_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 (¶meter_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 Info; word internal_command; dbug (1, dprintf ("[%06lx] %s,%d: fax_connect_ack_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_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 Info; word internal_command; dbug (1, dprintf ("[%06lx] %s,%d: fax_edata_ack_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_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 Info; 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)); Info = GOOD; 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)); Info = _WRONG_STATE; 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)); } } /*------------------------------------------------------------------*/