/* * Copyright (c) Eicon Networks, 2002. * This source file is supplied for the use with Eicon Networks range of DIVA Server Adapters. * Eicon File Revision : 2.1 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "platform.h" #include "di_defs.h" #include "pc.h" #include "capi20.h" #include "divacapi.h" #include "mdm_msg.h" #include "divasync.h" #define FILE_ "MESSAGE.C" #define dprintf /*------------------------------------------------------------------*/ /* This is options supported for all adapters that are server by */ /* XDI driver. Allo it is not necessary to ask it from every adapter*/ /* and it is not necessary to save it separate for every adapter */ /* Macrose defined here have only local meaning */ /*------------------------------------------------------------------*/ static dword diva_xdi_extended_features = 0; #define DIVA_CAPI_USE_CMA 0x00000001 #define DIVA_CAPI_XDI_PROVIDES_SDRAM_BAR 0x00000002 #define DIVA_CAPI_XDI_PROVIDES_NO_CANCEL 0x00000004 #define DIVA_CAPI_XDI_PROVIDES_RX_DMA 0x00000008 /* CAPI can request to process all return codes self only if: protocol code supports this && xdi supports this */ #define DIVA_CAPI_SUPPORTS_NO_CANCEL(__a__) (((__a__)->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL) && ((__a__)->manufacturer_features & MANUFACTURER_FEATURE_OK_FC_LABEL) && (diva_xdi_extended_features & DIVA_CAPI_XDI_PROVIDES_NO_CANCEL)) /*------------------------------------------------------------------*/ /* local function prototypes */ /*------------------------------------------------------------------*/ static void group_optimization(DIVA_CAPI_ADAPTER *a, PLCI *plci); static void set_group_ind_mask(PLCI *plci); static void clear_group_ind_mask_bit(PLCI *plci, word b); static byte test_group_ind_mask_bit(PLCI *plci, word b); void AutomaticLaw(DIVA_CAPI_ADAPTER *); word CapiRelease(word); word CapiRegister(word); word api_put(APPL *, CAPI_MSG *); static word api_parse(byte *, word, byte *, API_PARSE *); static void api_save_msg(API_PARSE *in, byte *format, API_SAVE *out); static void api_load_msg(API_SAVE *in, API_PARSE *out); word api_remove_start(void); void api_remove_complete(void); static void plci_remove(PLCI *); static void diva_get_extended_adapter_features(DIVA_CAPI_ADAPTER *a); static void diva_ask_for_xdi_sdram_bar(DIVA_CAPI_ADAPTER *, IDI_SYNC_REQ *); void callback(ENTITY *); static void control_rc(PLCI *, byte, byte, byte, byte, byte); static void data_rc(PLCI *, byte); static void data_ack(PLCI *, byte); static void sig_ind(PLCI *); static void SendInfo(PLCI *, dword, byte **, byte); static void SendSetupInfo(APPL *, PLCI *, dword, byte **, byte); static void SendSSExtInd(APPL *, PLCI *plci, dword Id, byte **parms); static void VSwitchReqInd(PLCI *plci, dword Id, byte **parms); static void nl_ind(PLCI *); static byte connect_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte connect_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte connect_a_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte disconnect_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte disconnect_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte listen_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte info_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte info_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte alert_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte facility_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte facility_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte connect_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte connect_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte connect_b3_a_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte disconnect_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte disconnect_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte data_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte data_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte reset_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte reset_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte connect_b3_t90_a_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte select_b_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte manufacturer_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static byte manufacturer_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); static word get_plci(DIVA_CAPI_ADAPTER *); static void add_p(PLCI *, byte, byte *); static void add_s(PLCI *plci, byte code, API_PARSE *p); static void add_ss(PLCI *plci, byte code, API_PARSE *p); static void add_ie(PLCI *plci, byte code, byte *p, word p_length); static void add_d(PLCI *, word, byte *); static void add_ai(PLCI *, API_PARSE *); static word add_b1(PLCI *, API_PARSE *, word, word); static word add_b23(PLCI *, API_PARSE *); static word add_modem_b23(PLCI *plci, API_PARSE *bp_parms); static void sig_req(PLCI *, byte, byte); static void nl_req_ncci(PLCI *, byte, byte); static void send_req(PLCI *); static void send_data(PLCI *); static word plci_remove_check(PLCI *); static void listen_check(DIVA_CAPI_ADAPTER *); static byte AddInfo(byte **, byte **, byte *, byte *); static byte getChannel(API_PARSE *); static void IndParse(PLCI *, word *, byte **, byte); static byte ie_compare(byte *, byte *); static word find_cip(DIVA_CAPI_ADAPTER *, byte *, byte *); static word CPN_filter_ok(byte *cpn, DIVA_CAPI_ADAPTER *, word); /* XON protocol helpers */ static void channel_flow_control_remove(PLCI *plci); static void channel_x_off(PLCI *plci, byte ch, byte flag); static void channel_x_on(PLCI *plci, byte ch); static void channel_request_xon(PLCI *plci, byte ch); static void channel_xmit_xon(PLCI *plci); static int channel_can_xon(PLCI *plci, byte ch); static void channel_xmit_extended_xon(PLCI *plci); static byte SendMultiIE(PLCI *plci, dword Id, byte **parms, byte ie_type, dword info_mask, byte setupParse); static word AdvCodecSupport(DIVA_CAPI_ADAPTER *, PLCI *, APPL *, byte); static void CodecIdCheck(DIVA_CAPI_ADAPTER *, PLCI *); static void SetVoiceChannel(PLCI *, byte *, DIVA_CAPI_ADAPTER *); static void VoiceChannelOff(PLCI *plci); static void adv_voice_write_coefs(PLCI *plci, word write_command); static void adv_voice_clear_config(PLCI *plci); static word get_b1_facilities(PLCI *plci, byte b1_resource); static byte add_b1_facilities(PLCI *plci, byte b1_resource, word b1_facilities); static void adjust_b1_facilities(PLCI *plci, byte new_b1_resource, word new_b1_facilities); static word adjust_b_process(dword Id, PLCI *plci, byte Rc); static void adjust_b1_resource(dword Id, PLCI *plci, API_SAVE *bp_msg, word b1_facilities, word internal_command); static void adjust_b_restore(dword Id, PLCI *plci, byte Rc); static void reset_b3_command(dword Id, PLCI *plci, byte Rc); static void select_b_command(dword Id, PLCI *plci, byte Rc); static void fax_connect_ack_command(dword Id, PLCI *plci, byte Rc); static void fax_edata_ack_command(dword Id, PLCI *plci, byte Rc); static void fax_connect_info_command(dword Id, PLCI *plci, byte Rc); static void fax_adjust_b23_command(dword Id, PLCI *plci, byte Rc); static void fax_disconnect_command(dword Id, PLCI *plci, byte Rc); static void hold_save_command(dword Id, PLCI *plci, byte Rc); static void retrieve_restore_command(dword Id, PLCI *plci, byte Rc); static void init_b1_config(PLCI *plci); static void clear_b1_config(PLCI *plci); static void dtmf_command(dword Id, PLCI *plci, byte Rc); static byte dtmf_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg); static void dtmf_confirmation(dword Id, PLCI *plci); static void dtmf_indication(dword Id, PLCI *plci, byte *msg, word length); static void dtmf_parameter_write(PLCI *plci); static void mixer_set_bchannel_id_esc(PLCI *plci, byte bchannel_id); static void mixer_set_bchannel_id(PLCI *plci, byte *chi); static void mixer_clear_config(PLCI *plci); static void mixer_notify_update(PLCI *plci, byte others); static void mixer_command(dword Id, PLCI *plci, byte Rc); static byte mixer_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg); static void mixer_indication_coefs_set(dword Id, PLCI *plci); static void mixer_indication_xconnect_from(dword Id, PLCI *plci, byte *msg, word length); static void mixer_indication_xconnect_to(dword Id, PLCI *plci, byte *msg, word length); static void mixer_remove(PLCI *plci); static void ec_command(dword Id, PLCI *plci, byte Rc); static byte ec_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg); static void ec_indication(dword Id, PLCI *plci, byte *msg, word length); static void rtp_connect_b3_req_command(dword Id, PLCI *plci, byte Rc); static void rtp_connect_b3_res_command(dword Id, PLCI *plci, byte Rc); static int diva_get_dma_descriptor(PLCI *plci, dword *dma_magic); static void diva_free_dma_descriptor(PLCI *plci, int nr); /*------------------------------------------------------------------*/ /* external function prototypes */ /*------------------------------------------------------------------*/ extern byte MapController(byte); extern byte UnMapController(byte); #define MapId(Id)(((Id) & 0xffffff00L) | MapController((byte)(Id))) #define UnMapId(Id)(((Id) & 0xffffff00L) | UnMapController((byte)(Id))) void sendf(APPL *, word, dword, word, byte *, ...); void *TransmitBufferSet(APPL *appl, dword ref); void *TransmitBufferGet(APPL *appl, void *p); void TransmitBufferFree(APPL *appl, void *p); void *ReceiveBufferGet(APPL *appl, int Num); int fax_head_line_time(char *buffer); /*------------------------------------------------------------------*/ /* Global data definitions */ /*------------------------------------------------------------------*/ extern byte max_adapter; extern byte max_appl; extern DIVA_CAPI_ADAPTER *adapter; extern APPL *application; static byte remove_started = false; static PLCI dummy_plci; static struct _ftable { word command; byte *format; byte (*function)(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); } ftable[] = { {_DATA_B3_R, "dwww", data_b3_req}, {_DATA_B3_I | RESPONSE, "w", data_b3_res}, {_INFO_R, "ss", info_req}, {_INFO_I | RESPONSE, "", info_res}, {_CONNECT_R, "wsssssssss", connect_req}, {_CONNECT_I | RESPONSE, "wsssss", connect_res}, {_CONNECT_ACTIVE_I | RESPONSE, "", connect_a_res}, {_DISCONNECT_R, "s", disconnect_req}, {_DISCONNECT_I | RESPONSE, "", disconnect_res}, {_LISTEN_R, "dddss", listen_req}, {_ALERT_R, "s", alert_req}, {_FACILITY_R, "ws", facility_req}, {_FACILITY_I | RESPONSE, "ws", facility_res}, {_CONNECT_B3_R, "s", connect_b3_req}, {_CONNECT_B3_I | RESPONSE, "ws", connect_b3_res}, {_CONNECT_B3_ACTIVE_I | RESPONSE, "", connect_b3_a_res}, {_DISCONNECT_B3_R, "s", disconnect_b3_req}, {_DISCONNECT_B3_I | RESPONSE, "", disconnect_b3_res}, {_RESET_B3_R, "s", reset_b3_req}, {_RESET_B3_I | RESPONSE, "", reset_b3_res}, {_CONNECT_B3_T90_ACTIVE_I | RESPONSE, "ws", connect_b3_t90_a_res}, {_CONNECT_B3_T90_ACTIVE_I | RESPONSE, "", connect_b3_t90_a_res}, {_SELECT_B_REQ, "s", select_b_req}, {_MANUFACTURER_R, "dws", manufacturer_req}, {_MANUFACTURER_I | RESPONSE, "dws", manufacturer_res}, {_MANUFACTURER_I | RESPONSE, "", manufacturer_res} }; static byte *cip_bc[29][2] = { { "", "" }, /* 0 */ { "\x03\x80\x90\xa3", "\x03\x80\x90\xa2" }, /* 1 */ { "\x02\x88\x90", "\x02\x88\x90" }, /* 2 */ { "\x02\x89\x90", "\x02\x89\x90" }, /* 3 */ { "\x03\x90\x90\xa3", "\x03\x90\x90\xa2" }, /* 4 */ { "\x03\x91\x90\xa5", "\x03\x91\x90\xa5" }, /* 5 */ { "\x02\x98\x90", "\x02\x98\x90" }, /* 6 */ { "\x04\x88\xc0\xc6\xe6", "\x04\x88\xc0\xc6\xe6" }, /* 7 */ { "\x04\x88\x90\x21\x8f", "\x04\x88\x90\x21\x8f" }, /* 8 */ { "\x03\x91\x90\xa5", "\x03\x91\x90\xa5" }, /* 9 */ { "", "" }, /* 10 */ { "", "" }, /* 11 */ { "", "" }, /* 12 */ { "", "" }, /* 13 */ { "", "" }, /* 14 */ { "", "" }, /* 15 */ { "\x03\x80\x90\xa3", "\x03\x80\x90\xa2" }, /* 16 */ { "\x03\x90\x90\xa3", "\x03\x90\x90\xa2" }, /* 17 */ { "\x02\x88\x90", "\x02\x88\x90" }, /* 18 */ { "\x02\x88\x90", "\x02\x88\x90" }, /* 19 */ { "\x02\x88\x90", "\x02\x88\x90" }, /* 20 */ { "\x02\x88\x90", "\x02\x88\x90" }, /* 21 */ { "\x02\x88\x90", "\x02\x88\x90" }, /* 22 */ { "\x02\x88\x90", "\x02\x88\x90" }, /* 23 */ { "\x02\x88\x90", "\x02\x88\x90" }, /* 24 */ { "\x02\x88\x90", "\x02\x88\x90" }, /* 25 */ { "\x03\x91\x90\xa5", "\x03\x91\x90\xa5" }, /* 26 */ { "\x03\x91\x90\xa5", "\x03\x91\x90\xa5" }, /* 27 */ { "\x02\x88\x90", "\x02\x88\x90" } /* 28 */ }; static byte *cip_hlc[29] = { "", /* 0 */ "", /* 1 */ "", /* 2 */ "", /* 3 */ "", /* 4 */ "", /* 5 */ "", /* 6 */ "", /* 7 */ "", /* 8 */ "", /* 9 */ "", /* 10 */ "", /* 11 */ "", /* 12 */ "", /* 13 */ "", /* 14 */ "", /* 15 */ "\x02\x91\x81", /* 16 */ "\x02\x91\x84", /* 17 */ "\x02\x91\xa1", /* 18 */ "\x02\x91\xa4", /* 19 */ "\x02\x91\xa8", /* 20 */ "\x02\x91\xb1", /* 21 */ "\x02\x91\xb2", /* 22 */ "\x02\x91\xb5", /* 23 */ "\x02\x91\xb8", /* 24 */ "\x02\x91\xc1", /* 25 */ "\x02\x91\x81", /* 26 */ "\x03\x91\xe0\x01", /* 27 */ "\x03\x91\xe0\x02" /* 28 */ }; /*------------------------------------------------------------------*/ #define V120_HEADER_LENGTH 1 #define V120_HEADER_EXTEND_BIT 0x80 #define V120_HEADER_BREAK_BIT 0x40 #define V120_HEADER_C1_BIT 0x04 #define V120_HEADER_C2_BIT 0x08 #define V120_HEADER_FLUSH_COND (V120_HEADER_BREAK_BIT | V120_HEADER_C1_BIT | V120_HEADER_C2_BIT) static byte v120_default_header[] = { 0x83 /* Ext, BR , res, res, C2 , C1 , B , F */ }; static byte v120_break_header[] = { 0xc3 | V120_HEADER_BREAK_BIT /* Ext, BR , res, res, C2 , C1 , B , F */ }; /*------------------------------------------------------------------*/ /* API_PUT function */ /*------------------------------------------------------------------*/ word api_put(APPL *appl, CAPI_MSG *msg) { word i, j, k, l, n; word ret; byte c; byte controller; DIVA_CAPI_ADAPTER *a; PLCI *plci; NCCI *ncci_ptr; word ncci; CAPI_MSG *m; API_PARSE msg_parms[MAX_MSG_PARMS + 1]; if (msg->header.length < sizeof(msg->header) || msg->header.length > MAX_MSG_SIZE) { dbug(1, dprintf("bad len")); return _BAD_MSG; } controller = (byte)((msg->header.controller & 0x7f) - 1); /* controller starts with 0 up to (max_adapter - 1) */ if (controller >= max_adapter) { dbug(1, dprintf("invalid ctrl")); return _BAD_MSG; } a = &adapter[controller]; plci = NULL; if ((msg->header.plci != 0) && (msg->header.plci <= a->max_plci) && !a->adapter_disabled) { dbug(1, dprintf("plci=%x", msg->header.plci)); plci = &a->plci[msg->header.plci - 1]; ncci = GET_WORD(&msg->header.ncci); if (plci->Id && (plci->appl || (plci->State == INC_CON_PENDING) || (plci->State == INC_CON_ALERT) || (msg->header.command == (_DISCONNECT_I | RESPONSE))) && ((ncci == 0) || (msg->header.command == (_DISCONNECT_B3_I | RESPONSE)) || ((ncci < MAX_NCCI + 1) && (a->ncci_plci[ncci] == plci->Id)))) { i = plci->msg_in_read_pos; j = plci->msg_in_write_pos; if (j >= i) { if (j + msg->header.length + MSG_IN_OVERHEAD <= MSG_IN_QUEUE_SIZE) i += MSG_IN_QUEUE_SIZE - j; else j = 0; } else { n = (((CAPI_MSG *)(plci->msg_in_queue))->header.length + MSG_IN_OVERHEAD + 3) & 0xfffc; if (i > MSG_IN_QUEUE_SIZE - n) i = MSG_IN_QUEUE_SIZE - n + 1; i -= j; } if (i <= ((msg->header.length + MSG_IN_OVERHEAD + 3) & 0xfffc)) { dbug(0, dprintf("Q-FULL1(msg) - len=%d write=%d read=%d wrap=%d free=%d", msg->header.length, plci->msg_in_write_pos, plci->msg_in_read_pos, plci->msg_in_wrap_pos, i)); return _QUEUE_FULL; } c = false; if ((((byte *) msg) < ((byte *)(plci->msg_in_queue))) || (((byte *) msg) >= ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue))) { if (plci->msg_in_write_pos != plci->msg_in_read_pos) c = true; } if (msg->header.command == _DATA_B3_R) { if (msg->header.length < 20) { dbug(1, dprintf("DATA_B3 REQ wrong length %d", msg->header.length)); return _BAD_MSG; } ncci_ptr = &(a->ncci[ncci]); n = ncci_ptr->data_pending; l = ncci_ptr->data_ack_pending; k = plci->msg_in_read_pos; while (k != plci->msg_in_write_pos) { if (k == plci->msg_in_wrap_pos) k = 0; if ((((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->header.command == _DATA_B3_R) && (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->header.ncci == ncci)) { n++; if (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->info.data_b3_req.Flags & 0x0004) l++; } k += (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->header.length + MSG_IN_OVERHEAD + 3) & 0xfffc; } if ((n >= MAX_DATA_B3) || (l >= MAX_DATA_ACK)) { dbug(0, dprintf("Q-FULL2(data) - pending=%d/%d ack_pending=%d/%d", ncci_ptr->data_pending, n, ncci_ptr->data_ack_pending, l)); return _QUEUE_FULL; } if (plci->req_in || plci->internal_command) { if ((((byte *) msg) >= ((byte *)(plci->msg_in_queue))) && (((byte *) msg) < ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue))) { dbug(0, dprintf("Q-FULL3(requeue)")); return _QUEUE_FULL; } c = true; } } else { if (plci->req_in || plci->internal_command) c = true; else { plci->command = msg->header.command; plci->number = msg->header.number; } } if (c) { dbug(1, dprintf("enqueue msg(0x%04x,0x%x,0x%x) - len=%d write=%d read=%d wrap=%d free=%d", msg->header.command, plci->req_in, plci->internal_command, msg->header.length, plci->msg_in_write_pos, plci->msg_in_read_pos, plci->msg_in_wrap_pos, i)); if (j == 0) plci->msg_in_wrap_pos = plci->msg_in_write_pos; m = (CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[j]); for (i = 0; i < msg->header.length; i++) ((byte *)(plci->msg_in_queue))[j++] = ((byte *) msg)[i]; if (m->header.command == _DATA_B3_R) { m->info.data_b3_req.Data = (dword)(long)(TransmitBufferSet(appl, m->info.data_b3_req.Data)); } j = (j + 3) & 0xfffc; *((APPL **)(&((byte *)(plci->msg_in_queue))[j])) = appl; plci->msg_in_write_pos = j + MSG_IN_OVERHEAD; return 0; } } else { plci = NULL; } } dbug(1, dprintf("com=%x", msg->header.command)); for (j = 0; j < MAX_MSG_PARMS + 1; j++) msg_parms[j].length = 0; for (i = 0, ret = _BAD_MSG; i < ARRAY_SIZE(ftable); i++) { if (ftable[i].command == msg->header.command) { /* break loop if the message is correct, otherwise continue scan */ /* (for example: CONNECT_B3_T90_ACT_RES has two specifications) */ if (!api_parse(msg->info.b, (word)(msg->header.length - 12), ftable[i].format, msg_parms)) { ret = 0; break; } for (j = 0; j < MAX_MSG_PARMS + 1; j++) msg_parms[j].length = 0; } } if (ret) { dbug(1, dprintf("BAD_MSG")); if (plci) plci->command = 0; return ret; } c = ftable[i].function(GET_DWORD(&msg->header.controller), msg->header.number, a, plci, appl, msg_parms); channel_xmit_extended_xon(plci); if (c == 1) send_req(plci); if (c == 2 && plci) plci->req_in = plci->req_in_start = plci->req_out = 0; if (plci && !plci->req_in) plci->command = 0; return 0; } /*------------------------------------------------------------------*/ /* api_parse function, check the format of api messages */ /*------------------------------------------------------------------*/ static word api_parse(byte *msg, word length, byte *format, API_PARSE *parms) { word i; word p; for (i = 0, p = 0; format[i]; i++) { if (parms) { parms[i].info = &msg[p]; } switch (format[i]) { case 'b': p += 1; break; case 'w': p += 2; break; case 'd': p += 4; break; case 's': if (msg[p] == 0xff) { parms[i].info += 2; parms[i].length = msg[p + 1] + (msg[p + 2] << 8); p += (parms[i].length + 3); } else { parms[i].length = msg[p]; p += (parms[i].length + 1); } break; } if (p > length) return true; } if (parms) parms[i].info = NULL; return false; } static void api_save_msg(API_PARSE *in, byte *format, API_SAVE *out) { word i, j, n = 0; byte *p; p = out->info; for (i = 0; format[i] != '\0'; i++) { out->parms[i].info = p; out->parms[i].length = in[i].length; switch (format[i]) { case 'b': n = 1; break; case 'w': n = 2; break; case 'd': n = 4; break; case 's': n = in[i].length + 1; break; } for (j = 0; j < n; j++) *(p++) = in[i].info[j]; } out->parms[i].info = NULL; out->parms[i].length = 0; } static void api_load_msg(API_SAVE *in, API_PARSE *out) { word i; i = 0; do { out[i].info = in->parms[i].info; out[i].length = in->parms[i].length; } while (in->parms[i++].info); } /*------------------------------------------------------------------*/ /* CAPI remove function */ /*------------------------------------------------------------------*/ word api_remove_start(void) { word i; word j; if (!remove_started) { remove_started = true; for (i = 0; i < max_adapter; i++) { if (adapter[i].request) { for (j = 0; j < adapter[i].max_plci; j++) { if (adapter[i].plci[j].Sig.Id) plci_remove(&adapter[i].plci[j]); } } } return 1; } else { for (i = 0; i < max_adapter; i++) { if (adapter[i].request) { for (j = 0; j < adapter[i].max_plci; j++) { if (adapter[i].plci[j].Sig.Id) return 1; } } } } api_remove_complete(); return 0; } /*------------------------------------------------------------------*/ /* internal command queue */ /*------------------------------------------------------------------*/ static void init_internal_command_queue(PLCI *plci) { word i; dbug(1, dprintf("%s,%d: init_internal_command_queue", (char *)(FILE_), __LINE__)); plci->internal_command = 0; for (i = 0; i < MAX_INTERNAL_COMMAND_LEVELS; i++) plci->internal_command_queue[i] = NULL; } static void start_internal_command(dword Id, PLCI *plci, t_std_internal_command command_function) { word i; dbug(1, dprintf("[%06lx] %s,%d: start_internal_command", UnMapId(Id), (char *)(FILE_), __LINE__)); if (plci->internal_command == 0) { plci->internal_command_queue[0] = command_function; (*command_function)(Id, plci, OK); } else { i = 1; while (plci->internal_command_queue[i] != NULL) i++; plci->internal_command_queue[i] = command_function; } } static void next_internal_command(dword Id, PLCI *plci) { word i; dbug(1, dprintf("[%06lx] %s,%d: next_internal_command", UnMapId(Id), (char *)(FILE_), __LINE__)); plci->internal_command = 0; plci->internal_command_queue[0] = NULL; while (plci->internal_command_queue[1] != NULL) { for (i = 0; i < MAX_INTERNAL_COMMAND_LEVELS - 1; i++) plci->internal_command_queue[i] = plci->internal_command_queue[i + 1]; plci->internal_command_queue[MAX_INTERNAL_COMMAND_LEVELS - 1] = NULL; (*(plci->internal_command_queue[0]))(Id, plci, OK); if (plci->internal_command != 0) return; plci->internal_command_queue[0] = NULL; } } /*------------------------------------------------------------------*/ /* NCCI allocate/remove function */ /*------------------------------------------------------------------*/ static dword ncci_mapping_bug = 0; static word get_ncci(PLCI *plci, byte ch, word force_ncci) { DIVA_CAPI_ADAPTER *a; word ncci, i, j, k; a = plci->adapter; if (!ch || a->ch_ncci[ch]) { ncci_mapping_bug++; dbug(1, dprintf("NCCI mapping exists %ld %02x %02x %02x-%02x", ncci_mapping_bug, ch, force_ncci, a->ncci_ch[a->ch_ncci[ch]], a->ch_ncci[ch])); ncci = ch; } else { if (force_ncci) ncci = force_ncci; else { if ((ch < MAX_NCCI + 1) && !a->ncci_ch[ch]) ncci = ch; else { ncci = 1; while ((ncci < MAX_NCCI + 1) && a->ncci_ch[ncci]) ncci++; if (ncci == MAX_NCCI + 1) { ncci_mapping_bug++; i = 1; do { j = 1; while ((j < MAX_NCCI + 1) && (a->ncci_ch[j] != i)) j++; k = j; if (j < MAX_NCCI + 1) { do { j++; } while ((j < MAX_NCCI + 1) && (a->ncci_ch[j] != i)); } } while ((i < MAX_NL_CHANNEL + 1) && (j < MAX_NCCI + 1)); if (i < MAX_NL_CHANNEL + 1) { dbug(1, dprintf("NCCI mapping overflow %ld %02x %02x %02x-%02x-%02x", ncci_mapping_bug, ch, force_ncci, i, k, j)); } else { dbug(1, dprintf("NCCI mapping overflow %ld %02x %02x", ncci_mapping_bug, ch, force_ncci)); } ncci = ch; } } a->ncci_plci[ncci] = plci->Id; a->ncci_state[ncci] = IDLE; if (!plci->ncci_ring_list) plci->ncci_ring_list = ncci; else a->ncci_next[ncci] = a->ncci_next[plci->ncci_ring_list]; a->ncci_next[plci->ncci_ring_list] = (byte) ncci; } a->ncci_ch[ncci] = ch; a->ch_ncci[ch] = (byte) ncci; dbug(1, dprintf("NCCI mapping established %ld %02x %02x %02x-%02x", ncci_mapping_bug, ch, force_ncci, ch, ncci)); } return (ncci); } static void ncci_free_receive_buffers(PLCI *plci, word ncci) { DIVA_CAPI_ADAPTER *a; APPL *appl; word i, ncci_code; dword Id; a = plci->adapter; Id = (((dword) ncci) << 16) | (((word)(plci->Id)) << 8) | a->Id; if (ncci) { if (a->ncci_plci[ncci] == plci->Id) { if (!plci->appl) { ncci_mapping_bug++; dbug(1, dprintf("NCCI mapping appl expected %ld %08lx", ncci_mapping_bug, Id)); } else { appl = plci->appl; ncci_code = ncci | (((word) a->Id) << 8); for (i = 0; i < appl->MaxBuffer; i++) { if ((appl->DataNCCI[i] == ncci_code) && (((byte)(appl->DataFlags[i] >> 8)) == plci->Id)) { appl->DataNCCI[i] = 0; } } } } } else { for (ncci = 1; ncci < MAX_NCCI + 1; ncci++) { if (a->ncci_plci[ncci] == plci->Id) { if (!plci->appl) { ncci_mapping_bug++; dbug(1, dprintf("NCCI mapping no appl %ld %08lx", ncci_mapping_bug, Id)); } else { appl = plci->appl; ncci_code = ncci | (((word) a->Id) << 8); for (i = 0; i < appl->MaxBuffer; i++) { if ((appl->DataNCCI[i] == ncci_code) && (((byte)(appl->DataFlags[i] >> 8)) == plci->Id)) { appl->DataNCCI[i] = 0; } } } } } } } static void cleanup_ncci_data(PLCI *plci, word ncci) { NCCI *ncci_ptr; if (ncci && (plci->adapter->ncci_plci[ncci] == plci->Id)) { ncci_ptr = &(plci->adapter->ncci[ncci]); if (plci->appl) { while (ncci_ptr->data_pending != 0) { if (!plci->data_sent || (ncci_ptr->DBuffer[ncci_ptr->data_out].P != plci->data_sent_ptr)) TransmitBufferFree(plci->appl, ncci_ptr->DBuffer[ncci_ptr->data_out].P); (ncci_ptr->data_out)++; if (ncci_ptr->data_out == MAX_DATA_B3) ncci_ptr->data_out = 0; (ncci_ptr->data_pending)--; } } ncci_ptr->data_out = 0; ncci_ptr->data_pending = 0; ncci_ptr->data_ack_out = 0; ncci_ptr->data_ack_pending = 0; } } static void ncci_remove(PLCI *plci, word ncci, byte preserve_ncci) { DIVA_CAPI_ADAPTER *a; dword Id; word i; a = plci->adapter; Id = (((dword) ncci) << 16) | (((word)(plci->Id)) << 8) | a->Id; if (!preserve_ncci) ncci_free_receive_buffers(plci, ncci); if (ncci) { if (a->ncci_plci[ncci] != plci->Id) { ncci_mapping_bug++; dbug(1, dprintf("NCCI mapping doesn't exist %ld %08lx %02x", ncci_mapping_bug, Id, preserve_ncci)); } else { cleanup_ncci_data(plci, ncci); dbug(1, dprintf("NCCI mapping released %ld %08lx %02x %02x-%02x", ncci_mapping_bug, Id, preserve_ncci, a->ncci_ch[ncci], ncci)); a->ch_ncci[a->ncci_ch[ncci]] = 0; if (!preserve_ncci) { a->ncci_ch[ncci] = 0; a->ncci_plci[ncci] = 0; a->ncci_state[ncci] = IDLE; i = plci->ncci_ring_list; while ((i != 0) && (a->ncci_next[i] != plci->ncci_ring_list) && (a->ncci_next[i] != ncci)) i = a->ncci_next[i]; if ((i != 0) && (a->ncci_next[i] == ncci)) { if (i == ncci) plci->ncci_ring_list = 0; else if (plci->ncci_ring_list == ncci) plci->ncci_ring_list = i; a->ncci_next[i] = a->ncci_next[ncci]; } a->ncci_next[ncci] = 0; } } } else { for (ncci = 1; ncci < MAX_NCCI + 1; ncci++) { if (a->ncci_plci[ncci] == plci->Id) { cleanup_ncci_data(plci, ncci); dbug(1, dprintf("NCCI mapping released %ld %08lx %02x %02x-%02x", ncci_mapping_bug, Id, preserve_ncci, a->ncci_ch[ncci], ncci)); a->ch_ncci[a->ncci_ch[ncci]] = 0; if (!preserve_ncci) { a->ncci_ch[ncci] = 0; a->ncci_plci[ncci] = 0; a->ncci_state[ncci] = IDLE; a->ncci_next[ncci] = 0; } } } if (!preserve_ncci) plci->ncci_ring_list = 0; } } /*------------------------------------------------------------------*/ /* PLCI remove function */ /*------------------------------------------------------------------*/ static void plci_free_msg_in_queue(PLCI *plci) { word i; if (plci->appl) { i = plci->msg_in_read_pos; while (i != plci->msg_in_write_pos) { if (i == plci->msg_in_wrap_pos) i = 0; if (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[i]))->header.command == _DATA_B3_R) { TransmitBufferFree(plci->appl, (byte *)(long)(((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[i]))->info.data_b3_req.Data)); } i += (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[i]))->header.length + MSG_IN_OVERHEAD + 3) & 0xfffc; } } plci->msg_in_write_pos = MSG_IN_QUEUE_SIZE; plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE; plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE; } static void plci_remove(PLCI *plci) { if (!plci) { dbug(1, dprintf("plci_remove(no plci)")); return; } init_internal_command_queue(plci); dbug(1, dprintf("plci_remove(%x,tel=%x)", plci->Id, plci->tel)); if (plci_remove_check(plci)) { return; } if (plci->Sig.Id == 0xff) { dbug(1, dprintf("D-channel X.25 plci->NL.Id:%0x", plci->NL.Id)); if (plci->NL.Id && !plci->nl_remove_id) { nl_req_ncci(plci, REMOVE, 0); send_req(plci); } } else { if (!plci->sig_remove_id && (plci->Sig.Id || (plci->req_in != plci->req_out) || (plci->nl_req || plci->sig_req))) { sig_req(plci, HANGUP, 0); send_req(plci); } } ncci_remove(plci, 0, false); plci_free_msg_in_queue(plci); plci->channels = 0; plci->appl = NULL; if ((plci->State == INC_CON_PENDING) || (plci->State == INC_CON_ALERT)) plci->State = OUTG_DIS_PENDING; } /*------------------------------------------------------------------*/ /* Application Group function helpers */ /*------------------------------------------------------------------*/ static void set_group_ind_mask(PLCI *plci) { word i; for (i = 0; i < C_IND_MASK_DWORDS; i++) plci->group_optimization_mask_table[i] = 0xffffffffL; } static void clear_group_ind_mask_bit(PLCI *plci, word b) { plci->group_optimization_mask_table[b >> 5] &= ~(1L << (b & 0x1f)); } static byte test_group_ind_mask_bit(PLCI *plci, word b) { return ((plci->group_optimization_mask_table[b >> 5] & (1L << (b & 0x1f))) != 0); } /*------------------------------------------------------------------*/ /* c_ind_mask operations for arbitrary MAX_APPL */ /*------------------------------------------------------------------*/ static void clear_c_ind_mask(PLCI *plci) { word i; for (i = 0; i < C_IND_MASK_DWORDS; i++) plci->c_ind_mask_table[i] = 0; } static byte c_ind_mask_empty(PLCI *plci) { word i; i = 0; while ((i < C_IND_MASK_DWORDS) && (plci->c_ind_mask_table[i] == 0)) i++; return (i == C_IND_MASK_DWORDS); } static void set_c_ind_mask_bit(PLCI *plci, word b) { plci->c_ind_mask_table[b >> 5] |= (1L << (b & 0x1f)); } static void clear_c_ind_mask_bit(PLCI *plci, word b) { plci->c_ind_mask_table[b >> 5] &= ~(1L << (b & 0x1f)); } static byte test_c_ind_mask_bit(PLCI *plci, word b) { return ((plci->c_ind_mask_table[b >> 5] & (1L << (b & 0x1f))) != 0); } static void dump_c_ind_mask(PLCI *plci) { static char hex_digit_table[0x10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; word i, j, k; dword d; char *p; char buf[40]; for (i = 0; i < C_IND_MASK_DWORDS; i += 4) { p = buf + 36; *p = '\0'; for (j = 0; j < 4; j++) { if (i + j < C_IND_MASK_DWORDS) { d = plci->c_ind_mask_table[i + j]; for (k = 0; k < 8; k++) { *(--p) = hex_digit_table[d & 0xf]; d >>= 4; } } else if (i != 0) { for (k = 0; k < 8; k++) *(--p) = ' '; } *(--p) = ' '; } dbug(1, dprintf("c_ind_mask =%s", (char *) p)); } } #define dump_plcis(a) /*------------------------------------------------------------------*/ /* translation function for each message */ /*------------------------------------------------------------------*/ static byte connect_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *parms) { word ch; word i; word Info; byte LinkLayer; API_PARSE *ai; API_PARSE *bp; API_PARSE ai_parms[5]; word channel = 0; dword ch_mask; byte m; static byte esc_chi[35] = {0x02, 0x18, 0x01}; static byte lli[2] = {0x01, 0x00}; byte noCh = 0; word dir = 0; byte *p_chi = ""; for (i = 0; i < 5; i++) ai_parms[i].length = 0; dbug(1, dprintf("connect_req(%d)", parms->length)); Info = _WRONG_IDENTIFIER; if (a) { if (a->adapter_disabled) { dbug(1, dprintf("adapter disabled")); Id = ((word)1 << 8) | a->Id; sendf(appl, _CONNECT_R | CONFIRM, Id, Number, "w", 0); sendf(appl, _DISCONNECT_I, Id, 0, "w", _L1_ERROR); return false; } Info = _OUT_OF_PLCI; if ((i = get_plci(a))) { Info = 0; plci = &a->plci[i - 1]; plci->appl = appl; plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE; /* check 'external controller' bit for codec support */ if (Id & EXT_CONTROLLER) { if (AdvCodecSupport(a, plci, appl, 0)) { plci->Id = 0; sendf(appl, _CONNECT_R | CONFIRM, Id, Number, "w", _WRONG_IDENTIFIER); return 2; } } ai = &parms[9]; bp = &parms[5]; ch = 0; if (bp->length)LinkLayer = bp->info[3]; else LinkLayer = 0; if (ai->length) { ch = 0xffff; if (!api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms)) { ch = 0; if (ai_parms[0].length) { ch = GET_WORD(ai_parms[0].info + 1); if (ch > 4) ch = 0; /* safety -> ignore ChannelID */ if (ch == 4) /* explizit CHI in message */ { /* check length of B-CH struct */ if ((ai_parms[0].info)[3] >= 1) { if ((ai_parms[0].info)[4] == CHI) { p_chi = &((ai_parms[0].info)[5]); } else { p_chi = &((ai_parms[0].info)[3]); } if (p_chi[0] > 35) /* check length of channel ID */ { Info = _WRONG_MESSAGE_FORMAT; } } else Info = _WRONG_MESSAGE_FORMAT; } if (ch == 3 && ai_parms[0].length >= 7 && ai_parms[0].length <= 36) { dir = GET_WORD(ai_parms[0].info + 3); ch_mask = 0; m = 0x3f; for (i = 0; i + 5 <= ai_parms[0].length; i++) { if (ai_parms[0].info[i + 5] != 0) { if ((ai_parms[0].info[i + 5] | m) != 0xff) Info = _WRONG_MESSAGE_FORMAT; else { if (ch_mask == 0) channel = i; ch_mask |= 1L << i; } } m = 0; } if (ch_mask == 0) Info = _WRONG_MESSAGE_FORMAT; if (!Info) { if ((ai_parms[0].length == 36) || (ch_mask != ((dword)(1L << channel)))) { esc_chi[0] = (byte)(ai_parms[0].length - 2); for (i = 0; i + 5 <= ai_parms[0].length; i++) esc_chi[i + 3] = ai_parms[0].info[i + 5]; } else esc_chi[0] = 2; esc_chi[2] = (byte)channel; plci->b_channel = (byte)channel; /* not correct for ETSI ch 17..31 */ add_p(plci, LLI, lli); add_p(plci, ESC, esc_chi); plci->State = LOCAL_CONNECT; if (!dir) plci->call_dir |= CALL_DIR_FORCE_OUTG_NL; /* dir 0=DTE, 1=DCE */ } } } } else Info = _WRONG_MESSAGE_FORMAT; } dbug(1, dprintf("ch=%x,dir=%x,p_ch=%d", ch, dir, channel)); plci->command = _CONNECT_R; plci->number = Number; /* x.31 or D-ch free SAPI in LinkLayer? */ if (ch == 1 && LinkLayer != 3 && LinkLayer != 12) noCh = true; if ((ch == 0 || ch == 2 || noCh || ch == 3 || ch == 4) && !Info) { /* B-channel used for B3 connections (ch==0), or no B channel */ /* is used (ch==2) or perm. connection (3) is used do a CALL */ if (noCh) Info = add_b1(plci, &parms[5], 2, 0); /* no resource */ else Info = add_b1(plci, &parms[5], ch, 0); add_s(plci, OAD, &parms[2]); add_s(plci, OSA, &parms[4]); add_s(plci, BC, &parms[6]); add_s(plci, LLC, &parms[7]); add_s(plci, HLC, &parms[8]); if (a->Info_Mask[appl->Id - 1] & 0x200) { /* early B3 connect (CIP mask bit 9) no release after a disc */ add_p(plci, LLI, "\x01\x01"); } if (GET_WORD(parms[0].info) < 29) { add_p(plci, BC, cip_bc[GET_WORD(parms[0].info)][a->u_law]); add_p(plci, HLC, cip_hlc[GET_WORD(parms[0].info)]); } add_p(plci, UID, "\x06\x43\x61\x70\x69\x32\x30"); sig_req(plci, ASSIGN, DSIG_ID); } else if (ch == 1) { /* D-Channel used for B3 connections */ plci->Sig.Id = 0xff; Info = 0; } if (!Info && ch != 2 && !noCh) { Info = add_b23(plci, &parms[5]); if (!Info) { if (!(plci->tel && !plci->adv_nl))nl_req_ncci(plci, ASSIGN, 0); } } if (!Info) { if (ch == 0 || ch == 2 || ch == 3 || noCh || ch == 4) { if (plci->spoofed_msg == SPOOFING_REQUIRED) { api_save_msg(parms, "wsssssssss", &plci->saved_msg); plci->spoofed_msg = CALL_REQ; plci->internal_command = BLOCK_PLCI; plci->command = 0; dbug(1, dprintf("Spoof")); send_req(plci); return false; } if (ch == 4)add_p(plci, CHI, p_chi); add_s(plci, CPN, &parms[1]); add_s(plci, DSA, &parms[3]); if (noCh) add_p(plci, ESC, "\x02\x18\xfd"); /* D-channel, no B-L3 */ add_ai(plci, &parms[9]); if (!dir)sig_req(plci, CALL_REQ, 0); else { plci->command = PERM_LIST_REQ; plci->appl = appl; sig_req(plci, LISTEN_REQ, 0); send_req(plci); return false; } } send_req(plci); return false; } plci->Id = 0; } } sendf(appl, _CONNECT_R | CONFIRM, Id, Number, "w", Info); return 2; } static byte connect_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *parms) { word i, Info; word Reject; static byte cau_t[] = {0, 0, 0x90, 0x91, 0xac, 0x9d, 0x86, 0xd8, 0x9b}; static byte esc_t[] = {0x03, 0x08, 0x00, 0x00}; API_PARSE *ai; API_PARSE ai_parms[5]; word ch = 0; if (!plci) { dbug(1, dprintf("connect_res(no plci)")); return 0; /* no plci, no send */ } dbug(1, dprintf("connect_res(State=0x%x)", plci->State)); for (i = 0; i < 5; i++) ai_parms[i].length = 0; ai = &parms[5]; dbug(1, dprintf("ai->length=%d", ai->length)); if (ai->length) { if (!api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms)) { dbug(1, dprintf("ai_parms[0].length=%d/0x%x", ai_parms[0].length, GET_WORD(ai_parms[0].info + 1))); ch = 0; if (ai_parms[0].length) { ch = GET_WORD(ai_parms[0].info + 1); dbug(1, dprintf("BCH-I=0x%x", ch)); } } } if (plci->State == INC_CON_CONNECTED_ALERT) { dbug(1, dprintf("Connected Alert Call_Res")); if (a->Info_Mask[appl->Id - 1] & 0x200) { /* early B3 connect (CIP mask bit 9) no release after a disc */ add_p(plci, LLI, "\x01\x01"); } add_s(plci, CONN_NR, &parms[2]); add_s(plci, LLC, &parms[4]); add_ai(plci, &parms[5]); plci->State = INC_CON_ACCEPT; sig_req(plci, CALL_RES, 0); return 1; } else if (plci->State == INC_CON_PENDING || plci->State == INC_CON_ALERT) { clear_c_ind_mask_bit(plci, (word)(appl->Id - 1)); dump_c_ind_mask(plci); Reject = GET_WORD(parms[0].info); dbug(1, dprintf("Reject=0x%x", Reject)); if (Reject) { if (c_ind_mask_empty(plci)) { if ((Reject & 0xff00) == 0x3400) { esc_t[2] = ((byte)(Reject & 0x00ff)) | 0x80; add_p(plci, ESC, esc_t); add_ai(plci, &parms[5]); sig_req(plci, REJECT, 0); } else if (Reject == 1 || Reject > 9) { add_ai(plci, &parms[5]); sig_req(plci, HANGUP, 0); } else { esc_t[2] = cau_t[(Reject&0x000f)]; add_p(plci, ESC, esc_t); add_ai(plci, &parms[5]); sig_req(plci, REJECT, 0); } plci->appl = appl; } else { sendf(appl, _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED); } } else { plci->appl = appl; if (Id & EXT_CONTROLLER) { if (AdvCodecSupport(a, plci, appl, 0)) { dbug(1, dprintf("connect_res(error from AdvCodecSupport)")); sig_req(plci, HANGUP, 0); return 1; } if (plci->tel == ADV_VOICE && a->AdvCodecPLCI) { Info = add_b23(plci, &parms[1]); if (Info) { dbug(1, dprintf("connect_res(error from add_b23)")); sig_req(plci, HANGUP, 0); return 1; } if (plci->adv_nl) { nl_req_ncci(plci, ASSIGN, 0); } } } else { plci->tel = 0; if (ch != 2) { Info = add_b23(plci, &parms[1]); if (Info) { dbug(1, dprintf("connect_res(error from add_b23 2)")); sig_req(plci, HANGUP, 0); return 1; } } nl_req_ncci(plci, ASSIGN, 0); } if (plci->spoofed_msg == SPOOFING_REQUIRED) { api_save_msg(parms, "wsssss", &plci->saved_msg); plci->spoofed_msg = CALL_RES; plci->internal_command = BLOCK_PLCI; plci->command = 0; dbug(1, dprintf("Spoof")); } else { add_b1(plci, &parms[1], ch, plci->B1_facilities); if (a->Info_Mask[appl->Id - 1] & 0x200) { /* early B3 connect (CIP mask bit 9) no release after a disc */ add_p(plci, LLI, "\x01\x01"); } add_s(plci, CONN_NR, &parms[2]); add_s(plci, LLC, &parms[4]); add_ai(plci, &parms[5]); plci->State = INC_CON_ACCEPT; sig_req(plci, CALL_RES, 0); } for (i = 0; i < max_appl; i++) { if (test_c_ind_mask_bit(plci, i)) { sendf(&application[i], _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED); } } } } return 1; } static byte connect_a_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg) { dbug(1, dprintf("connect_a_res")); return false; } static byte disconnect_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg) { word Info; word i; dbug(1, dprintf("disconnect_req")); Info = _WRONG_IDENTIFIER; if (plci) { if (plci->State == INC_CON_PENDING || plci->State == INC_CON_ALERT) { clear_c_ind_mask_bit(plci, (word)(appl->Id - 1)); plci->appl = appl; for (i = 0; i < max_appl; i++) { if (test_c_ind_mask_bit(plci, i)) sendf(&application[i], _DISCONNECT_I, Id, 0, "w", 0); } plci->State = OUTG_DIS_PENDING; } if (plci->Sig.Id && plci->appl) { Info = 0; if (plci->Sig.Id != 0xff) { if (plci->State != INC_DIS_PENDING) { add_ai(plci, &msg[0]); sig_req(plci, HANGUP, 0); plci->State = OUTG_DIS_PENDING; return 1; } } else { if (plci->NL.Id && !plci->nl_remove_id) { mixer_remove(plci); nl_req_ncci(plci, REMOVE, 0); sendf(appl, _DISCONNECT_R | CONFIRM, Id, Number, "w", 0); sendf(appl, _DISCONNECT_I, Id, 0, "w", 0); plci->State = INC_DIS_PENDING; } return 1; } } } if (!appl) return false; sendf(appl, _DISCONNECT_R | CONFIRM, Id, Number, "w", Info); return false; } static byte disconnect_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg) { dbug(1, dprintf("disconnect_res")); if (plci) { /* clear ind mask bit, just in case of collsion of */ /* DISCONNECT_IND and CONNECT_RES */ clear_c_ind_mask_bit(plci, (word)(appl->Id - 1)); ncci_free_receive_buffers(plci, 0); if (plci_remove_check(plci)) { return 0; } if (plci->State == INC_DIS_PENDING || plci->State == SUSPENDING) { if (c_ind_mask_empty(plci)) { if (plci->State != SUSPENDING) plci->State = IDLE; dbug(1, dprintf("chs=%d", plci->channels)); if (!plci->channels) { plci_remove(plci); } } } } return 0; } static byte listen_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *parms) { word Info; byte i; dbug(1, dprintf("listen_req(Appl=0x%x)", appl->Id)); Info = _WRONG_IDENTIFIER; if (a) { Info = 0; a->Info_Mask[appl->Id - 1] = GET_DWORD(parms[0].info); a->CIP_Mask[appl->Id - 1] = GET_DWORD(parms[1].info); dbug(1, dprintf("CIP_MASK=0x%lx", GET_DWORD(parms[1].info))); if (a->Info_Mask[appl->Id - 1] & 0x200) { /* early B3 connect provides */ a->Info_Mask[appl->Id - 1] |= 0x10; /* call progression infos */ } /* check if external controller listen and switch listen on or off*/ if (Id&EXT_CONTROLLER && GET_DWORD(parms[1].info)) { if (a->profile.Global_Options & ON_BOARD_CODEC) { dummy_plci.State = IDLE; a->codec_listen[appl->Id - 1] = &dummy_plci; a->TelOAD[0] = (byte)(parms[3].length); for (i = 1; parms[3].length >= i && i < 22; i++) { a->TelOAD[i] = parms[3].info[i]; } a->TelOAD[i] = 0; a->TelOSA[0] = (byte)(parms[4].length); for (i = 1; parms[4].length >= i && i < 22; i++) { a->TelOSA[i] = parms[4].info[i]; } a->TelOSA[i] = 0; } else Info = 0x2002; /* wrong controller, codec not supported */ } else{ /* clear listen */ a->codec_listen[appl->Id - 1] = (PLCI *)0; } } sendf(appl, _LISTEN_R | CONFIRM, Id, Number, "w", Info); if (a) listen_check(a); return false; } static byte info_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg) { word i; API_PARSE *ai; PLCI *rc_plci = NULL; API_PARSE ai_parms[5]; word Info = 0; dbug(1, dprintf("info_req")); for (i = 0; i < 5; i++) ai_parms[i].length = 0; ai = &msg[1]; if (ai->length) { if (api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms)) { dbug(1, dprintf("AddInfo wrong")); Info = _WRONG_MESSAGE_FORMAT; } } if (!a) Info = _WRONG_STATE; if (!Info && plci) { /* no fac, with CPN, or KEY */ rc_plci = plci; if (!ai_parms[3].length && plci->State && (msg[0].length || ai_parms[1].length)) { /* overlap sending option */ dbug(1, dprintf("OvlSnd")); add_s(plci, CPN, &msg[0]); add_s(plci, KEY, &ai_parms[1]); sig_req(plci, INFO_REQ, 0); send_req(plci); return false; } if (plci->State && ai_parms[2].length) { /* User_Info option */ dbug(1, dprintf("UUI")); add_s(plci, UUI, &ai_parms[2]); sig_req(plci, USER_DATA, 0); } else if (plci->State && ai_parms[3].length) { /* Facility option */ dbug(1, dprintf("FAC")); add_s(plci, CPN, &msg[0]); add_ai(plci, &msg[1]); sig_req(plci, FACILITY_REQ, 0); } else { Info = _WRONG_STATE; } } else if ((ai_parms[1].length || ai_parms[2].length || ai_parms[3].length) && !Info) { /* NCR_Facility option -> send UUI and Keypad too */ dbug(1, dprintf("NCR_FAC")); if ((i = get_plci(a))) { rc_plci = &a->plci[i - 1]; appl->NullCREnable = true; rc_plci->internal_command = C_NCR_FAC_REQ; rc_plci->appl = appl; add_p(rc_plci, CAI, "\x01\x80"); add_p(rc_plci, UID, "\x06\x43\x61\x70\x69\x32\x30"); sig_req(rc_plci, ASSIGN, DSIG_ID); send_req(rc_plci); } else { Info = _OUT_OF_PLCI; } if (!Info) { add_s(rc_plci, CPN, &msg[0]); add_ai(rc_plci, &msg[1]); sig_req(rc_plci, NCR_FACILITY, 0); send_req(rc_plci); return false; /* for application controlled supplementary services */ } } if (!rc_plci) { Info = _WRONG_MESSAGE_FORMAT; } if (!Info) { send_req(rc_plci); } else { /* appl is not assigned to a PLCI or error condition */ dbug(1, dprintf("localInfoCon")); sendf(appl, _INFO_R | CONFIRM, Id, Number, "w", Info); } return false; } static byte info_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg) { dbug(1, dprintf("info_res")); return false; } static byte alert_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg) { word Info; byte ret; dbug(1, dprintf("alert_req")); Info = _WRONG_IDENTIFIER; ret = false; if (plci) { Info = _ALERT_IGNORED; if (plci->State != INC_CON_ALERT) { Info = _WRONG_STATE; if (plci->State == INC_CON_PENDING) { Info = 0; plci->State = INC_CON_ALERT; add_ai(plci, &msg[0]); sig_req(plci, CALL_ALERT, 0); ret = 1; } } } sendf(appl, _ALERT_R | CONFIRM, Id, Number, "w", Info); return ret; } static byte facility_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg) { word Info = 0; word i = 0; word selector; word SSreq; long relatedPLCIvalue; DIVA_CAPI_ADAPTER *relatedadapter; byte *SSparms = ""; byte RCparms[] = "\x05\x00\x00\x02\x00\x00"; byte SSstruct[] = "\x09\x00\x00\x06\x00\x00\x00\x00\x00\x00"; API_PARSE *parms; API_PARSE ss_parms[11]; PLCI *rplci; byte cai[15]; dword d; API_PARSE dummy; dbug(1, dprintf("facility_req")); for (i = 0; i < 9; i++) ss_parms[i].length = 0; parms = &msg[1]; if (!a) { dbug(1, dprintf("wrong Ctrl")); Info = _WRONG_IDENTIFIER; } selector = GET_WORD(msg[0].info); if (!Info) { switch (selector) { case SELECTOR_HANDSET: Info = AdvCodecSupport(a, plci, appl, HOOK_SUPPORT); break; case SELECTOR_SU_SERV: if (!msg[1].length) { Info = _WRONG_MESSAGE_FORMAT; break; } SSreq = GET_WORD(&(msg[1].info[1])); PUT_WORD(&RCparms[1], SSreq); SSparms = RCparms; switch (SSreq) { case S_GET_SUPPORTED_SERVICES: if ((i = get_plci(a))) { rplci = &a->plci[i - 1]; rplci->appl = appl; add_p(rplci, CAI, "\x01\x80"); add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30"); sig_req(rplci, ASSIGN, DSIG_ID); send_req(rplci); } else { PUT_DWORD(&SSstruct[6], MASK_TERMINAL_PORTABILITY); SSparms = (byte *)SSstruct; break; } rplci->internal_command = GETSERV_REQ_PEND; rplci->number = Number; rplci->appl = appl; sig_req(rplci, S_SUPPORTED, 0); send_req(rplci); return false; break; case S_LISTEN: if (parms->length == 7) { if (api_parse(&parms->info[1], (word)parms->length, "wbd", ss_parms)) { dbug(1, dprintf("format wrong")); Info = _WRONG_MESSAGE_FORMAT; break; } } else { Info = _WRONG_MESSAGE_FORMAT; break; } a->Notification_Mask[appl->Id - 1] = GET_DWORD(ss_parms[2].info); if (a->Notification_Mask[appl->Id - 1] & SMASK_MWI) /* MWI active? */ { if ((i = get_plci(a))) { rplci = &a->plci[i - 1]; rplci->appl = appl; add_p(rplci, CAI, "\x01\x80"); add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30"); sig_req(rplci, ASSIGN, DSIG_ID); send_req(rplci); } else { break; } rplci->internal_command = GET_MWI_STATE; rplci->number = Number; sig_req(rplci, MWI_POLL, 0); send_req(rplci); } break; case S_HOLD: api_parse(&parms->info[1], (word)parms->length, "ws", ss_parms); if (plci && plci->State && plci->SuppState == IDLE) { plci->SuppState = HOLD_REQUEST; plci->command = C_HOLD_REQ; add_s(plci, CAI, &ss_parms[1]); sig_req(plci, CALL_HOLD, 0); send_req(plci); return false; } else Info = 0x3010; /* wrong state */ break; case S_RETRIEVE: if (plci && plci->State && plci->SuppState == CALL_HELD) { if (Id & EXT_CONTROLLER) { if (AdvCodecSupport(a, plci, appl, 0)) { Info = 0x3010; /* wrong state */ break; } } else plci->tel = 0; plci->SuppState = RETRIEVE_REQUEST; plci->command = C_RETRIEVE_REQ; if (plci->spoofed_msg == SPOOFING_REQUIRED) { plci->spoofed_msg = CALL_RETRIEVE; plci->internal_command = BLOCK_PLCI; plci->command = 0; dbug(1, dprintf("Spoof")); return false; } else { sig_req(plci, CALL_RETRIEVE, 0); send_req(plci); return false; } } else Info = 0x3010; /* wrong state */ break; case S_SUSPEND: if (parms->length) { if (api_parse(&parms->info[1], (word)parms->length, "wbs", ss_parms)) { dbug(1, dprintf("format wrong")); Info = _WRONG_MESSAGE_FORMAT; break; } } if (plci && plci->State) { add_s(plci, CAI, &ss_parms[2]); plci->command = SUSPEND_REQ; sig_req(plci, SUSPEND, 0); plci->State = SUSPENDING; send_req(plci); } else Info = 0x3010; /* wrong state */ break; case S_RESUME: if (!(i = get_plci(a))) { Info = _OUT_OF_PLCI; break; } rplci = &a->plci[i - 1]; rplci->appl = appl; rplci->number = Number; rplci->tel = 0; rplci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE; /* check 'external controller' bit for codec support */ if (Id & EXT_CONTROLLER) { if (AdvCodecSupport(a, rplci, appl, 0)) { rplci->Id = 0; Info = 0x300A; break; } } if (parms->length) { if (api_parse(&parms->info[1], (word)parms->length, "wbs", ss_parms)) { dbug(1, dprintf("format wrong")); rplci->Id = 0; Info = _WRONG_MESSAGE_FORMAT; break; } } dummy.length = 0; dummy.info = "\x00"; add_b1(rplci, &dummy, 0, 0); if (a->Info_Mask[appl->Id - 1] & 0x200) { /* early B3 connect (CIP mask bit 9) no release after a disc */ add_p(rplci, LLI, "\x01\x01"); } add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30"); sig_req(rplci, ASSIGN, DSIG_ID); send_req(rplci); add_s(rplci, CAI, &ss_parms[2]); rplci->command = RESUME_REQ; sig_req(rplci, RESUME, 0); rplci->State = RESUMING; send_req(rplci); break; case S_CONF_BEGIN: /* Request */ case S_CONF_DROP: case S_CONF_ISOLATE: case S_CONF_REATTACH: if (api_parse(&parms->info[1], (word)parms->length, "wbd", ss_parms)) { dbug(1, dprintf("format wrong")); Info = _WRONG_MESSAGE_FORMAT; break; } if (plci && plci->State && ((plci->SuppState == IDLE) || (plci->SuppState == CALL_HELD))) { d = GET_DWORD(ss_parms[2].info); if (d >= 0x80) { dbug(1, dprintf("format wrong")); Info = _WRONG_MESSAGE_FORMAT; break; } plci->ptyState = (byte)SSreq; plci->command = 0; cai[0] = 2; switch (SSreq) { case S_CONF_BEGIN: cai[1] = CONF_BEGIN; plci->internal_command = CONF_BEGIN_REQ_PEND; break; case S_CONF_DROP: cai[1] = CONF_DROP; plci->internal_command = CONF_DROP_REQ_PEND; break; case S_CONF_ISOLATE: cai[1] = CONF_ISOLATE; plci->internal_command = CONF_ISOLATE_REQ_PEND; break; case S_CONF_REATTACH: cai[1] = CONF_REATTACH; plci->internal_command = CONF_REATTACH_REQ_PEND; break; } cai[2] = (byte)d; /* Conference Size resp. PartyId */ add_p(plci, CAI, cai); sig_req(plci, S_SERVICE, 0); send_req(plci); return false; } else Info = 0x3010; /* wrong state */ break; case S_ECT: case S_3PTY_BEGIN: case S_3PTY_END: case S_CONF_ADD: if (parms->length == 7) { if (api_parse(&parms->info[1], (word)parms->length, "wbd", ss_parms)) { dbug(1, dprintf("format wrong")); Info = _WRONG_MESSAGE_FORMAT; break; } } else if (parms->length == 8) /* workaround for the T-View-S */ { if (api_parse(&parms->info[1], (word)parms->length, "wbdb", ss_parms)) { dbug(1, dprintf("format wrong")); Info = _WRONG_MESSAGE_FORMAT; break; } } else { Info = _WRONG_MESSAGE_FORMAT; break; } if (!msg[1].length) { Info = _WRONG_MESSAGE_FORMAT; break; } if (!plci) { Info = _WRONG_IDENTIFIER; break; } relatedPLCIvalue = GET_DWORD(ss_parms[2].info); relatedPLCIvalue &= 0x0000FFFF; dbug(1, dprintf("PTY/ECT/addCONF,relPLCI=%lx", relatedPLCIvalue)); /* controller starts with 0 up to (max_adapter - 1) */ if (((relatedPLCIvalue & 0x7f) == 0) || (MapController((byte)(relatedPLCIvalue & 0x7f)) == 0) || (MapController((byte)(relatedPLCIvalue & 0x7f)) > max_adapter)) { if (SSreq == S_3PTY_END) { dbug(1, dprintf("wrong Controller use 2nd PLCI=PLCI")); rplci = plci; } else { Info = 0x3010; /* wrong state */ break; } } else { relatedadapter = &adapter[MapController((byte)(relatedPLCIvalue & 0x7f)) - 1]; relatedPLCIvalue >>= 8; /* find PLCI PTR*/ for (i = 0, rplci = NULL; i < relatedadapter->max_plci; i++) { if (relatedadapter->plci[i].Id == (byte)relatedPLCIvalue) { rplci = &relatedadapter->plci[i]; } } if (!rplci || !relatedPLCIvalue) { if (SSreq == S_3PTY_END) { dbug(1, dprintf("use 2nd PLCI=PLCI")); rplci = plci; } else { Info = 0x3010; /* wrong state */ break; } } } /* dbug(1, dprintf("rplci:%x", rplci)); dbug(1, dprintf("plci:%x", plci)); dbug(1, dprintf("rplci->ptyState:%x", rplci->ptyState)); dbug(1, dprintf("plci->ptyState:%x", plci->ptyState)); dbug(1, dprintf("SSreq:%x", SSreq)); dbug(1, dprintf("rplci->internal_command:%x", rplci->internal_command)); dbug(1, dprintf("rplci->appl:%x", rplci->appl)); dbug(1, dprintf("rplci->Id:%x", rplci->Id)); */ /* send PTY/ECT req, cannot check all states because of US stuff */ if (!rplci->internal_command && rplci->appl) { plci->command = 0; rplci->relatedPTYPLCI = plci; plci->relatedPTYPLCI = rplci; rplci->ptyState = (byte)SSreq; if (SSreq == S_ECT) { rplci->internal_command = ECT_REQ_PEND; cai[1] = ECT_EXECUTE; rplci->vswitchstate = 0; rplci->vsprot = 0; rplci->vsprotdialect = 0; plci->vswitchstate = 0; plci->vsprot = 0; plci->vsprotdialect = 0; } else if (SSreq == S_CONF_ADD) { rplci->internal_command = CONF_ADD_REQ_PEND; cai[1] = CONF_ADD; } else { rplci->internal_command = PTY_REQ_PEND; cai[1] = (byte)(SSreq - 3); } rplci->number = Number; if (plci != rplci) /* explicit invocation */ { cai[0] = 2; cai[2] = plci->Sig.Id; dbug(1, dprintf("explicit invocation")); } else { dbug(1, dprintf("implicit invocation")); cai[0] = 1; } add_p(rplci, CAI, cai); sig_req(rplci, S_SERVICE, 0); send_req(rplci); return false; } else { dbug(0, dprintf("Wrong line")); Info = 0x3010; /* wrong state */ break; } break; case S_CALL_DEFLECTION: if (api_parse(&parms->info[1], (word)parms->length, "wbwss", ss_parms)) { dbug(1, dprintf("format wrong")); Info = _WRONG_MESSAGE_FORMAT; break; } if (!plci) { Info = _WRONG_IDENTIFIER; break; } /* reuse unused screening indicator */ ss_parms[3].info[3] = (byte)GET_WORD(&(ss_parms[2].info[0])); plci->command = 0; plci->internal_command = CD_REQ_PEND; appl->CDEnable = true; cai[0] = 1; cai[1] = CALL_DEFLECTION; add_p(plci, CAI, cai); add_p(plci, CPN, ss_parms[3].info); sig_req(plci, S_SERVICE, 0); send_req(plci); return false; break; case S_CALL_FORWARDING_START: if (api_parse(&parms->info[1], (word)parms->length, "wbdwwsss", ss_parms)) { dbug(1, dprintf("format wrong")); Info = _WRONG_MESSAGE_FORMAT; break; } if ((i = get_plci(a))) { rplci = &a->plci[i - 1]; rplci->appl = appl; add_p(rplci, CAI, "\x01\x80"); add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30"); sig_req(rplci, ASSIGN, DSIG_ID); send_req(rplci); } else { Info = _OUT_OF_PLCI; break; } /* reuse unused screening indicator */ rplci->internal_command = CF_START_PEND; rplci->appl = appl; rplci->number = Number; appl->S_Handle = GET_DWORD(&(ss_parms[2].info[0])); cai[0] = 2; cai[1] = 0x70 | (byte)GET_WORD(&(ss_parms[3].info[0])); /* Function */ cai[2] = (byte)GET_WORD(&(ss_parms[4].info[0])); /* Basic Service */ add_