/* * CAPI connection handling * * heavily based on capiconn.* of pppdcapiplugin by * Carsten Paeth (calle@calle.in-berlin.de) * * Copyright 2004 Armin Schindler * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include /* snprintf */ #include #include #include "capiconn.h" /* xxxxxxxxxxxxxxxxxx */ static _cmsg cmdcmsg; static _cmsg gcmsg; /* -------- defines -------------------------------------------------- */ #ifndef CAPI_MAXDATAWINDOW #define CAPI_MAXDATAWINDOW 8 #endif /* -------- type definitions ----------------------------------------- */ struct capiconn_context { struct capiconn_context *next; unsigned appid; struct capiconn_callbacks *cb; int ncontr; struct capi_contr *contr_list; /* statistic */ unsigned long nrecvctlpkt; unsigned long nrecvdatapkt; unsigned long nsentctlpkt; unsigned long nsentdatapkt; }; struct capi_contr { struct capi_contr *next; struct capiconn_context *ctx; unsigned contrnr; struct capi_contrinfo cinfo; unsigned ddilen; /* * LISTEN state */ int state; _cdword infomask; _cdword cipmask; _cdword cipmask2; /* * ID of capi message sent */ _cword msgid; /* * B-Channels */ int nbchan; struct capi_connection { struct capi_connection *next; struct capi_contr *contr; struct capiconn_context *ctx; struct capi_conninfo conninfo; unsigned incoming:1, disconnecting:1, localdisconnect:1, callednumbercomplete:1; _cword disconnectreason; _cword disconnectreason_b3; _cdword plci; _cdword ncci; /* ncci for CONNECT_ACTIVE_IND */ _cword msgid; /* to identfy CONNECT_CONF */ int state; struct capi_ncci { struct capi_connection *plcip; struct capiconn_context *ctx; _cdword ncci; _cword msgid; /* to identfy CONNECT_B3_CONF */ int state; int oldstate; /* */ _cword datahandle; struct ncci_datahandle_queue { struct ncci_datahandle_queue *next; _cword datahandle; unsigned char *data; } *ackqueue; int ackqueuelen; } *nccip; } *connections; }; typedef struct capi_ncci capi_ncci; typedef struct capi_contr capi_contr; typedef struct ncci_datahandle_queue ncci_datahandle_queue; /* -------- data definitions ----------------------------------------- */ capiconn_context *context_list = 0; /* -------- context handling ----------------------------------------- */ capiconn_context * capiconn_getcontext(unsigned appid, capiconn_callbacks *cb) { capiconn_context *ctx; if ((ctx = ((*cb->malloc)(sizeof(capiconn_context)))) == 0) return 0; memset(ctx, 0, sizeof(capiconn_context)); ctx->appid = appid; ctx->cb = cb; ctx->next = context_list; context_list = ctx; return ctx; }; static void free_all_cards(capiconn_context *ctx) { } int capiconn_freecontext(capiconn_context *ctx) { capiconn_context **pp; for (pp = &context_list; *pp; pp = &(*pp)->next) { if (*pp == ctx) { *pp = (*pp)->next; free_all_cards(ctx); (*ctx->cb->free)(ctx); return 0; } } return -1; } static inline capiconn_context *find_context(unsigned appid) { capiconn_context *p; for (p = context_list; p; p = p->next) if (p->appid == appid) return p; return 0; } int capiconn_addcontr(capiconn_context *ctx, unsigned contr, capi_contrinfo *cinfo) { capiconn_callbacks *cb = ctx->cb; capi_contr *card; if (!(card = (capi_contr *) (*cb->malloc)(sizeof(capi_contr)))) return CAPICONN_NO_MEMORY; memset(card, 0, sizeof(capi_contr)); card->contrnr = contr; card->cinfo = *cinfo; card->ctx = ctx; if (card->cinfo.ddi) card->ddilen = strlen(card->cinfo.ddi); card->next = ctx->contr_list; ctx->contr_list = card; ctx->ncontr++; return CAPICONN_OK; } /* ------------------------------------------------------------------- */ static capi_contr *findcontrbynumber(capiconn_context *ctx, unsigned contr) { capi_contr *p; for (p = ctx->contr_list; p; p = p->next) if (p->contrnr == contr) break; return p; } /* -------- plci management ------------------------------------------ */ static capi_connection *new_plci(capi_contr * card, int incoming) { capiconn_context *ctx = card->ctx; capiconn_callbacks *cb = ctx->cb; capi_connection *plcip; plcip = (capi_connection *) (*cb->malloc)(sizeof(capi_connection)); if (plcip == 0) return 0; memset(plcip, 0, sizeof(capi_connection)); plcip->contr = card; plcip->ctx = ctx; plcip->incoming = incoming; plcip->state = ST_PLCI_NONE; plcip->plci = 0; plcip->msgid = 0; plcip->next = card->connections; card->connections = plcip; return plcip; } static capi_connection *find_plci_by_plci(capi_contr * card, _cdword plci) { capi_connection *p; for (p = card->connections; p; p = p->next) if (p->plci == plci) return p; return 0; } static capi_connection *find_plci_by_msgid(capi_contr * card, _cword msgid) { capi_connection *p; for (p = card->connections; p; p = p->next) if (p->msgid == msgid) return p; return 0; } static capi_connection *find_plci_by_ncci(capi_contr * card, _cdword ncci) { capi_connection *p; for (p = card->connections; p; p = p->next) if (p->plci == (ncci & 0xffff)) return p; return 0; } static void free_plci(capi_contr * card, capi_connection * plcip) { capiconn_context *ctx = card->ctx; capiconn_callbacks *cb = ctx->cb; capi_connection **pp; for (pp = &card->connections; *pp; pp = &(*pp)->next) { if (*pp == plcip) { *pp = (*pp)->next; (*cb->free)(plcip); return; } } (*cb->errmsg)("free_plci %p (0x%x) not found, Huh?", plcip, plcip->plci); } /* -------- ncci management ------------------------------------------ */ static inline capi_ncci *new_ncci(capi_contr * card, capi_connection * plcip, _cdword ncci) { capiconn_context *ctx = card->ctx; capiconn_callbacks *cb = ctx->cb; capi_ncci *nccip; nccip = (capi_ncci *) (*cb->malloc)(sizeof(capi_ncci)); if (nccip == 0) return 0; memset(nccip, 0, sizeof(capi_ncci)); nccip->ctx = ctx; nccip->ncci = ncci; nccip->state = ST_NCCI_NONE; nccip->plcip = plcip; nccip->datahandle = 0; plcip->nccip = nccip; plcip->ncci = ncci; return nccip; } static inline capi_ncci *find_ncci(capi_contr * card, _cdword ncci) { capi_connection *plcip; if ((plcip = find_plci_by_ncci(card, ncci)) == 0) return 0; return plcip->nccip; } static inline capi_ncci *find_ncci_by_msgid(capi_contr * card, _cdword ncci, _cword msgid) { capi_connection *plcip; if ((plcip = find_plci_by_ncci(card, ncci)) == 0) return 0; return plcip->nccip; } static void free_ncci(capi_contr * card, capi_ncci *nccip) { capiconn_callbacks *cb = card->ctx->cb; nccip->plcip->nccip = 0; (*cb->free)(nccip); } /* ------------------------------------------------------------------- */ static void clr_conninfo1(capiconn_context *ctx, capi_conninfo *p) { capiconn_callbacks *cb = ctx->cb; if (p->callednumber) { (*cb->free)(p->callednumber); p->callednumber = 0; } if (p->callingnumber) { (*cb->free)(p->callingnumber); p->callingnumber = 0; } } static void clr_conninfo2(capiconn_context *ctx, capi_conninfo *p) { capiconn_callbacks *cb = ctx->cb; if (p->b1config) { (*cb->free)(p->b1config); p->b1config = 0; } if (p->b2config) { (*cb->free)(p->b2config); p->b2config = 0; } if (p->b3config) { (*cb->free)(p->b3config); p->b3config = 0; } if (p->bchaninfo) { (*cb->free)(p->bchaninfo); p->bchaninfo = 0; } if (p->ncpi) { (*cb->free)(p->ncpi); p->ncpi = 0; } } static void clr_conninfo(capiconn_context *ctx, capi_conninfo *p) { clr_conninfo1(ctx, p); clr_conninfo2(ctx, p); } static int set_conninfo1a(capiconn_context *ctx, capi_conninfo *p, _cword cipvalue, char *callednumber, char *callingnumber) { capiconn_callbacks *cb = ctx->cb; _cbyte len; p->cipvalue = cipvalue; if ((p->callednumber = (*cb->malloc)(128)) == 0) goto fail; if (callednumber) { len = (_cbyte)strlen(callednumber); if (callednumber[0] & 0x80) { memcpy(p->callednumber+1, callednumber, len); p->callednumber[0] = len; p->callednumber[len+1] = 0; } else { memcpy(p->callednumber+2, callednumber, len); p->callednumber[0] = len+1; p->callednumber[1] = 0x81; p->callednumber[len+2] = 0; } } else { p->callednumber[0] = 0; } if ((p->callingnumber = (*cb->malloc)(128)) == 0) goto fail; if (callingnumber) { len = (_cbyte)strlen(callingnumber); memcpy(p->callingnumber+3, callingnumber, len); p->callingnumber[0] = len+2; p->callingnumber[1] = 0x00; p->callingnumber[2] = 0x80; p->callingnumber[len+3] = 0; } else { p->callingnumber[0] = 0; } return 0; fail: clr_conninfo1(ctx, p); return -1; } static int set_conninfo1b(capiconn_context *ctx, capi_conninfo *p, _cword cipvalue, _cstruct callednumber, _cstruct callingnumber) { capiconn_callbacks *cb = ctx->cb; _cbyte len; p->cipvalue = cipvalue; if ((p->callednumber = (*cb->malloc)(128)) == 0) goto fail; len = callednumber[0]; memcpy(p->callednumber, callednumber, len+1); p->callednumber[len+1] = 0; if ((p->callingnumber = (*cb->malloc)(128)) == 0) goto fail; len = callingnumber[0]; memcpy(p->callingnumber, callingnumber, len+1); p->callingnumber[len+1] = 0; return 0; fail: clr_conninfo1(ctx, p); return -1; } static void extend_callednumber(capiconn_context *ctx, capi_conninfo *p, char *number, _cbyte len) { capiconn_callbacks *cb = ctx->cb; _cbyte *curnumber = p->callednumber+2; _cbyte clen = p->callednumber[0]-2; (*cb->debugmsg)("extend number %*.*s (len=%d)", (int)len, (int)len, number, len); if (len >= clen && memcmp(curnumber, number, clen) == 0) { memcpy(p->callednumber + 2, number, len); p->callednumber[0] = 2 + len; } else { memcpy(p->callednumber + p->callednumber[0], number, len); p->callednumber[0] += len; } p->callednumber[p->callednumber[0]+1] = 0; (*cb->debugmsg)("extended to %s", p->callednumber+2); } static int set_conninfo2(capiconn_context *ctx, capi_conninfo *p, _cword b1proto, _cword b2proto, _cword b3proto, _cstruct b1config, _cstruct b2config, _cstruct b3config, _cstruct bchaninfo, _cstruct ncpi) { capiconn_callbacks *cb = ctx->cb; p->b1proto = b1proto; p->b2proto = b2proto; p->b3proto = b3proto; if (b1config) { if ((p->b1config = (*cb->malloc)(b1config[0]+1)) == 0) goto fail; memcpy(p->b1config, b1config, b1config[0]+1); } if (b2config) { if ((p->b2config = (*cb->malloc)(b2config[0]+1)) == 0) goto fail; memcpy(p->b2config, b2config, b2config[0]+1); } if (b3config) { if ((p->b3config = (*cb->malloc)(b3config[0]+1)) == 0) goto fail; memcpy(p->b3config, b3config, b3config[0]+1); } if (bchaninfo) { if ((p->bchaninfo = (*cb->malloc)(bchaninfo[0]+1)) == 0) goto fail; memcpy(p->bchaninfo, bchaninfo, bchaninfo[0]+1); } if (ncpi) { if ((p->ncpi = (*cb->malloc)(ncpi[0]+1)) == 0) goto fail; memcpy(p->ncpi, ncpi, ncpi[0]+1); } return 0; fail: clr_conninfo2(ctx, p); return -1; } capi_conninfo *capiconn_getinfo(capi_connection *p) { p->conninfo.appid = p->ctx->appid; p->conninfo.plci = p->plci; p->conninfo.plci_state = p->state; p->conninfo.ncci = p->ncci; p->conninfo.ncci_state = p->nccip ? p->nccip->state : ST_NCCI_NONE; p->conninfo.isincoming = p->incoming ? 1 : 0; p->conninfo.disconnect_was_local = p->localdisconnect ? 1 : 0; p->conninfo.disconnectreason = p->disconnectreason; p->conninfo.disconnectreason_b3 = p->disconnectreason_b3; return &p->conninfo; } /* ------------------------------------------------------------------- */ static int capi_add_ack(capi_ncci *nccip, _cword datahandle, unsigned char *data) { capiconn_context *ctx = nccip->ctx; capiconn_callbacks *cb = ctx->cb; ncci_datahandle_queue *n, **pp; if (nccip->ackqueuelen >= CAPI_MAXDATAWINDOW) return 0; n = (ncci_datahandle_queue *) (*cb->malloc)(sizeof(ncci_datahandle_queue)); if (!n) { (cb->errmsg)("capiconn: cb->malloc ncci_datahandle failed"); return -1; } n->next = 0; n->datahandle = datahandle; n->data = data; for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) ; *pp = n; nccip->ackqueuelen++; return 0; } static unsigned char *capi_del_ack(capi_ncci *nccip, _cword datahandle) { capiconn_context *ctx = nccip->ctx; capiconn_callbacks *cb = ctx->cb; ncci_datahandle_queue **pp, *p; unsigned char *data; for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) { if ((*pp)->datahandle == datahandle) { p = *pp; data = p->data; *pp = (*pp)->next; (*cb->free)(p); nccip->ackqueuelen--; return data; } } return 0; } /* -------- convert and send capi message ---------------------------- */ static void send_message(capi_contr * card, _cmsg * cmsg) { capiconn_context *ctx = card->ctx; capiconn_callbacks *cb = ctx->cb; capi_cmsg2message(cmsg, cmsg->buf); (*cb->capi_put_message) (ctx->appid, cmsg->buf); ctx->nsentctlpkt++; } /* -------- state machine -------------------------------------------- */ struct listenstatechange { int actstate; int nextstate; int event; }; static struct listenstatechange listentable[] = { {ST_LISTEN_NONE, ST_LISTEN_WAIT_CONF, EV_LISTEN_REQ}, {ST_LISTEN_ACTIVE, ST_LISTEN_ACTIVE_WAIT_CONF, EV_LISTEN_REQ}, {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_ERROR}, {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_ERROR}, {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY}, {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY}, {ST_LISTEN_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK}, {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK}, { 0, 0, 0 }, }; static void listen_change_state(capi_contr * card, int event) { capiconn_context *ctx = card->ctx; capiconn_callbacks *cb = ctx->cb; struct listenstatechange *p = listentable; while (p->event) { if (card->state == p->actstate && p->event == event) { (*cb->debugmsg)("controller %d: listen_change_state %d -> %d", card->contrnr, card->state, p->nextstate); card->state = p->nextstate; return; } p++; } (*cb->debugmsg)("controller %d: listen_change_state state=%d event=%d ????", card->contrnr, card->state, event); } /* ------------------------------------------------------------------ */ static void p0(capi_contr * card, capi_connection * plcip) { capiconn_context *ctx = card->ctx; capiconn_callbacks *cb = ctx->cb; if (*cb->disconnected) (*cb->disconnected)(plcip, plcip->localdisconnect, plcip->disconnectreason, plcip->disconnectreason_b3); clr_conninfo(ctx, &plcip->conninfo); free_plci(card, plcip); } /* ------------------------------------------------------------------ */ struct plcistatechange { int actstate; int nextstate; int event; void (*changefunc) (capi_contr * card, capi_connection * plci); }; static struct plcistatechange plcitable[] = { /* P-0 */ {ST_PLCI_NONE, ST_PLCI_OUTGOING, EV_PLCI_CONNECT_REQ, 0}, {ST_PLCI_NONE, ST_PLCI_ALLOCATED, EV_PLCI_FACILITY_IND_UP, 0}, {ST_PLCI_NONE, ST_PLCI_INCOMING, EV_PLCI_CONNECT_IND, 0}, {ST_PLCI_NONE, ST_PLCI_RESUMEING, EV_PLCI_RESUME_REQ, 0}, /* P-0.1 */ {ST_PLCI_OUTGOING, ST_PLCI_NONE, EV_PLCI_CONNECT_CONF_ERROR, p0}, {ST_PLCI_OUTGOING, ST_PLCI_ALLOCATED, EV_PLCI_CONNECT_CONF_OK, 0}, /* P-1 */ {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0}, {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, /* P-ACT */ {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, {ST_PLCI_ACTIVE, ST_PLCI_HELD, EV_PLCI_HOLD_IND, 0}, {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_SUSPEND_IND, 0}, /* P-2 */ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0}, {ST_PLCI_INCOMING, ST_PLCI_FACILITY_IND, EV_PLCI_FACILITY_IND_UP, 0}, {ST_PLCI_INCOMING, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_RESP, 0}, {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CD_IND, 0}, /* P-3 */ {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0}, {ST_PLCI_FACILITY_IND, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_ACTIVE_IND, 0}, {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, /* P-4 */ {ST_PLCI_ACCEPTING, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0}, {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, /* P-5 */ {ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, /* P-6 */ {ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0}, /* P-0.Res */ {ST_PLCI_RESUMEING, ST_PLCI_NONE, EV_PLCI_RESUME_CONF_ERROR, p0}, {ST_PLCI_RESUMEING, ST_PLCI_RESUME, EV_PLCI_RESUME_CONF_OK, 0}, /* P-RES */ {ST_PLCI_RESUME, ST_PLCI_ACTIVE, EV_PLCI_RESUME_IND, 0}, /* P-HELD */ {ST_PLCI_HELD, ST_PLCI_ACTIVE, EV_PLCI_RETRIEVE_IND, 0}, { 0, 0, 0, 0 }, }; static void plci_change_state(capi_contr * card, capi_connection * plci, int event) { capiconn_callbacks *cb = card->ctx->cb; struct plcistatechange *p = plcitable; while (p->event) { if (plci->state == p->actstate && p->event == event) { (cb->debugmsg)("plci_change_state:0x%x %d -> %d event=%d", plci->plci, plci->state, p->nextstate, event); plci->state = p->nextstate; if (p->changefunc) p->changefunc(card, plci); return; } p++; } (*cb->errmsg)("plci_change_state:0x%x state=%d event=%d ????", card->contrnr, plci->plci, plci->state, event); } /* ------------------------------------------------------------------ */ static void n0(capi_contr * card, capi_ncci * ncci) { capiconn_context *ctx = card->ctx; capi_fill_DISCONNECT_REQ(&gcmsg, ctx->appid, card->msgid++, ncci->plcip->plci, 0, /* BChannelinformation */ 0, /* Keypadfacility */ 0, /* Useruserdata */ /* $$$$ */ 0 /* Facilitydataarray */ ); send_message(card, &gcmsg); plci_change_state(card, ncci->plcip, EV_PLCI_DISCONNECT_REQ); free_ncci(card, ncci); } /* ------------------------------------------------------------------ */ struct nccistatechange { int actstate; int nextstate; int event; void (*changefunc) (capi_contr * card, capi_ncci * ncci); }; static struct nccistatechange nccitable[] = { /* N-0 */ {ST_NCCI_NONE, ST_NCCI_OUTGOING, EV_NCCI_CONNECT_B3_REQ, 0}, {ST_NCCI_NONE, ST_NCCI_INCOMING, EV_NCCI_CONNECT_B3_IND, 0}, /* N-0.1 */ {ST_NCCI_OUTGOING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_CONF_OK, 0}, {ST_NCCI_OUTGOING, ST_NCCI_NONE, EV_NCCI_CONNECT_B3_CONF_ERROR, n0}, /* N-1 */ {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_CONNECT_B3_REJECT, 0}, {ST_NCCI_INCOMING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_RESP, 0}, {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, /* N-2 */ {ST_NCCI_ALLOCATED, ST_NCCI_ACTIVE, EV_NCCI_CONNECT_B3_ACTIVE_IND, 0}, {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, /* N-ACT */ {ST_NCCI_ACTIVE, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0}, {ST_NCCI_ACTIVE, ST_NCCI_RESETING, EV_NCCI_RESET_B3_REQ, 0}, {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, /* N-3 */ {ST_NCCI_RESETING, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0}, {ST_NCCI_RESETING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, {ST_NCCI_RESETING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, /* N-4 */ {ST_NCCI_DISCONNECTING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, {ST_NCCI_DISCONNECTING, ST_NCCI_PREVIOUS, EV_NCCI_DISCONNECT_B3_CONF_ERROR,0}, /* N-5 */ {ST_NCCI_DISCONNECTED, ST_NCCI_NONE, EV_NCCI_DISCONNECT_B3_RESP, n0}, { 0, 0, 0, 0 }, }; static void ncci_change_state(capi_contr * card, capi_ncci * ncci, int event) { capiconn_callbacks *cb = card->ctx->cb; struct nccistatechange *p = nccitable; while (p->event) { if (ncci->state == p->actstate && p->event == event) { (*cb->debugmsg)("ncci_change_state:0x%x %d -> %d event=%d", ncci->ncci, ncci->state, p->nextstate, event); if (p->nextstate == ST_NCCI_PREVIOUS) { ncci->state = ncci->oldstate; ncci->oldstate = p->actstate; } else { ncci->oldstate = p->actstate; ncci->state = p->nextstate; } if (p->changefunc) p->changefunc(card, ncci); return; } p++; } (*cb->errmsg)("ncci_change_state:0x%x state=%d event=%d ????", ncci->ncci, ncci->state, event); } /* ------------------------------------------------------------------- */ static void handle_controller(capiconn_context *ctx, _cmsg * cmsg) { capi_contr *card = findcontrbynumber(ctx, cmsg->adr.adrController&0x7f); capiconn_callbacks *cb = ctx->cb; if (!card) { (*cb->errmsg)("capiconn: %s from unknown controller 0x%x", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrController & 0x7f); return; } switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) { case CAPI_LISTEN_CONF: /* Controller */ (*cb->debugmsg)("contr %d: listenconf Info=0x%04x (%s) infomask=0x%x cipmask=0x%x capimask2=0x%x", card->contrnr, cmsg->Info, capi_info2str(cmsg->Info), card->infomask, card->cipmask, card->cipmask2); if (cmsg->Info) { listen_change_state(card, EV_LISTEN_CONF_ERROR); } else if (card->cipmask == 0) { listen_change_state(card, EV_LISTEN_CONF_EMPTY); } else { listen_change_state(card, EV_LISTEN_CONF_OK); } break; case CAPI_MANUFACTURER_IND: /* Controller */ goto ignored; case CAPI_MANUFACTURER_CONF: /* Controller */ goto ignored; case CAPI_FACILITY_IND: /* Controller/plci/ncci */ goto ignored; case CAPI_FACILITY_CONF: /* Controller/plci/ncci */ goto ignored; case CAPI_INFO_IND: /* Controller/plci */ goto ignored; case CAPI_INFO_CONF: /* Controller/plci */ goto ignored; default: (*cb->errmsg)("got %s from controller 0x%x ???", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrController); } return; ignored: (*cb->infomsg)("%s from controller 0x%x ignored", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrController); } static void check_incoming_complete(capi_connection *plcip) { capi_contr *card = plcip->contr; capiconn_context *ctx = card->ctx; capiconn_callbacks *cb = ctx->cb; capi_contrinfo *cip = &card->cinfo; unsigned ddilen = plcip->contr->ddilen; if (ddilen) { unsigned len = plcip->conninfo.callednumber[0]-2; char *num = plcip->conninfo.callednumber+2; char *start; int ndigits; if ((start = strstr(num, cip->ddi)) != 0) len = strlen(start); ndigits = len - ddilen; if (ndigits < cip->ndigits) { (*cb->debugmsg)("%d digits missing (%s)", cip->ndigits-ndigits, num); return; } } if (plcip->callednumbercomplete) return; plcip->callednumbercomplete = 1; if (*cb->incoming) (*cb->incoming)(plcip, plcip->contr->contrnr, plcip->conninfo.cipvalue, plcip->conninfo.callednumber+2, plcip->conninfo.callingnumber+3); if (plcip->state == ST_PLCI_INCOMING) { /* call not accepted, rejected or ignored */ capi_fill_ALERT_REQ(&gcmsg, ctx->appid, card->msgid++, plcip->plci, /* adr */ 0, /* BChannelinformation */ 0, /* Keypadfacility */ 0, /* Useruserdata */ 0, /* Facilitydataarray */ 0 /* Sending Complete */ ); plcip->msgid = gcmsg.Messagenumber; send_message(card, &gcmsg); } } static void handle_incoming_call(capi_contr * card, _cmsg * cmsg) { capiconn_context *ctx = card->ctx; capiconn_callbacks *cb = ctx->cb; capi_connection *plcip; if ((plcip = new_plci(card, 1)) == 0) { (*cb->errmsg)("incoming call on contr %d: no memory, sorry.", card->contrnr); goto ignore; } plcip->plci = cmsg->adr.adrPLCI; if (set_conninfo1b(ctx, &plcip->conninfo, cmsg->CIPValue, cmsg->CalledPartyNumber, cmsg->CallingPartyNumber) < 0) { free_plci(card, plcip); goto ignore; } plci_change_state(card, plcip, EV_PLCI_CONNECT_IND); (*cb->debugmsg)("incoming call contr=%d cip=%d %s -> %s", card->contrnr, cmsg->CIPValue, plcip->conninfo.callingnumber + 3, plcip->conninfo.callednumber + 2); if (cb->incoming == 0) goto ignore; check_incoming_complete(plcip); return; ignore: capi_fill_CONNECT_RESP(&cmdcmsg, ctx->appid, card->msgid++, cmsg->adr.adrPLCI, 1, /* ignore call */ 0, 0, 0, 0, 0, 0, 0, /* Globalconfiguration */ 0, /* ConnectedNumber */ 0, /* ConnectedSubaddress */ 0, /* LLC */ 0, /* BChannelinformation */ 0, /* Keypadfacility */ 0, /* Useruserdata */ 0 /* Facilitydataarray */ ); capi_cmsg2message(&cmdcmsg, cmdcmsg.buf); send_message(card, &cmdcmsg); } static int handle_charge_info(capi_connection *plcip, _cmsg *cmsg) { capiconn_context *ctx = plcip->ctx; capiconn_callbacks *cb = ctx->cb; unsigned char *p = cmsg->InfoElement; unsigned long charge = 0; if ((cmsg->InfoNumber & 0x4000) && p[0] == 4) { unsigned char *pp = &cmsg->InfoElement[1]; charge |= ((unsigned)pp[0]); charge |= ((unsigned)pp[1]) << 8; charge |= ((unsigned)pp[2]) << 16; charge |= ((unsigned)pp[3]) << 24; if (cb->chargeinfo) { if (cmsg->InfoNumber & 0x1) (*cb->chargeinfo)(plcip, charge, 0); else (*cb->chargeinfo)(plcip, charge, 1); } return 1; } else if (cmsg->InfoNumber == 0x28) { if (p[0] > 10 && memcmp("*AOC2*12*", p+1, 9) == 0) { int i, len = p[0]-10; if (len > 8) len = 8; for (i=0; i < len; i++) charge = charge * 10 + (p[10+i] - '0'); if (cb->chargeinfo) (*cb->chargeinfo)(plcip, charge, 0); return 1; } else if (p[0] > 7 && memcmp("FR.", p+1, 3) == 0) { int i, len = p[0]-3; for (i=0; p[3+i] != '.' && i < len; i++) charge = charge * 10 + (p[3+i] - '0'); charge = charge * 10; if (p[3+i] == '.' && i+1 < len) charge += (p[3+i+1] - '0'); if (cb->chargeinfo) (*cb->chargeinfo)(plcip, charge, 0); return 1; } } else if (cmsg->InfoNumber == 0x602) { if (p[0] > 1 && p[1] == 0x01) { int i, len = p[0]-1; for (i=0; i < len; i++) charge = charge * 10 + (p[1+i] - '0'); if (cb->chargeinfo) (*cb->chargeinfo)(plcip, charge, 0); return 1; } } return 0; } static int handle_callednumber_info(capi_connection *plcip, _cmsg *cmsg) { unsigned char *p = cmsg->InfoElement; if (cmsg->InfoNumber == 0x0070) { extend_callednumber(plcip->ctx, &plcip->conninfo, p+2, p[0]-1); check_incoming_complete(plcip); return 1; } return 0; } static int handle_cause_info(capi_connection *plcip, _cmsg *cmsg) { capiconn_context *ctx = plcip->ctx; capiconn_callbacks *cb = ctx->cb; unsigned char *p = cmsg->InfoElement; if (cmsg->InfoNumber == 0x0008) { char buf[128]; char *s, *end; int i; s = buf; end = s + sizeof(buf)-1; *end = 0; for (i=0; i < p[0]; i++) { snprintf(s, end-s, " %02x", p[i+1]); s += strlen(s); } (*cb->debugmsg)("cause bytes for plci 0x%x:%s", cmsg->adr.adrPLCI, buf); return 1; } return 0; } static void handle_plci(capiconn_context *ctx, _cmsg * cmsg) { capi_contr *card = findcontrbynumber(ctx, cmsg->adr.adrController&0x7f); capiconn_callbacks *cb = ctx->cb; capi_connection *plcip; if (!card) { (*cb->errmsg)("capiconn: %s from unknown controller 0x%x", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrController & 0x7f); return; } switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) { case CAPI_DISCONNECT_IND: /* plci */ if (cmsg->Reason) { (*cb->debugmsg)("%s reason 0x%x (%s) for plci 0x%x", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Reason, capi_info2str(cmsg->Reason), cmsg->adr.adrPLCI); } if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) { capi_cmsg_answer(cmsg); send_message(card, cmsg); goto notfound; } plcip->disconnectreason = cmsg->Reason; plcip->disconnecting = 1; plci_change_state(card, plcip, EV_PLCI_DISCONNECT_IND); capi_cmsg_answer(cmsg); send_message(card, cmsg); plci_change_state(card, plcip, EV_PLCI_DISCONNECT_RESP); break; case CAPI_DISCONNECT_CONF: /* plci */ if (cmsg->Info) { (*cb->infomsg)("%s info 0x%x (%s) for plci 0x%x", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Info, capi_info2str(cmsg->Info), cmsg->adr.adrPLCI); } if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) goto notfound; plcip->disconnecting = 1; break; case CAPI_ALERT_CONF: /* plci */ if (cmsg->Info) { (*cb->infomsg)("%s info 0x%x (%s) for plci 0x%x", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Info, capi_info2str(cmsg->Info), cmsg->adr.adrPLCI); } break; case CAPI_CONNECT_IND: /* plci */ handle_incoming_call(card, cmsg); break; case CAPI_CONNECT_CONF: /* plci */ if (cmsg->Info) { (*cb->infomsg)("%s info 0x%x (%s) for plci 0x%x", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Info, capi_info2str(cmsg->Info), cmsg->adr.adrPLCI); } if (!(plcip = find_plci_by_msgid(card, cmsg->Messagenumber))) goto notfound; plcip->plci = cmsg->adr.adrPLCI; if (cmsg->Info) { plci_change_state(card, plcip, EV_PLCI_CONNECT_CONF_ERROR); } else { plci_change_state(card, plcip, EV_PLCI_CONNECT_CONF_OK); } break; case CAPI_CONNECT_ACTIVE_IND: /* plci */ if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) goto notfound; if (plcip->incoming) { capi_cmsg_answer(cmsg); send_message(card, cmsg); plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND); } else { capi_ncci *nccip; capi_cmsg_answer(cmsg); send_message(card, cmsg); nccip = new_ncci(card, plcip, cmsg->adr.adrPLCI); if (!nccip) { (*cb->errmsg)("no mem for ncci on contr %d, sorry", card->contrnr); break; /* $$$$ */ } capi_fill_CONNECT_B3_REQ(cmsg, ctx->appid, card->msgid++, plcip->plci, /* adr */ plcip->conninfo.ncpi); nccip->msgid = cmsg->Messagenumber; send_message(card, cmsg); plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND); ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_REQ); } break; case CAPI_INFO_IND: /* Controller/plci */ if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) goto notfound; if (handle_charge_info(plcip, cmsg)) { capi_cmsg_answer(cmsg); send_message(card, cmsg); } else if (handle_callednumber_info(plcip, cmsg)) { capi_cmsg_answer(cmsg); send_message(card, cmsg); } else if (handle_cause_info(plcip, cmsg)) { capi_cmsg_answer(cmsg); send_message(card, cmsg); } else { capi_cmsg_answer(cmsg); send_message(card, cmsg); } break; case CAPI_CONNECT_ACTIVE_CONF: /* plci */ goto ignored; case CAPI_SELECT_B_PROTOCOL_CONF: /* plci */ goto ignored; case CAPI_FACILITY_IND: /* Controller/plci/ncci */ goto ignored; case CAPI_FACILITY_CONF: /* Controller/plci/ncci */ goto ignored; case CAPI_INFO_CONF: /* Controller/plci */ goto ignored; default: (*cb->errmsg)("got %s for plci 0x%x ???", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrPLCI); } return; ignored: (*cb->infomsg)("%s for plci 0x%x ignored", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrPLCI); return; notfound: (*cb->debugmsg)("%s: plci 0x%x not found", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrPLCI); return; } static void handle_ncci(capiconn_context *ctx, _cmsg * cmsg) { capi_contr *card = findcontrbynumber(ctx, cmsg->adr.adrController&0x7f); capiconn_callbacks *cb = ctx->cb; capi_connection *plcip; capi_ncci *nccip; unsigned char *data; if (!card) { (*cb->errmsg)("capidrv: %s from unknown controller 0x%x", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrController & 0x7f); return; } switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) { case CAPI_CONNECT_B3_ACTIVE_IND: /* ncci */ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) goto notfound; capi_cmsg_answer(cmsg); send_message(card, cmsg); ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_ACTIVE_IND); (*cb->debugmsg)("ncci 0x%x up", nccip->ncci); (*cb->connected)(nccip->plcip, cmsg->NCPI); break; case CAPI_CONNECT_B3_ACTIVE_CONF: /* ncci */ goto ignored; case CAPI_CONNECT_B3_IND: /* ncci */ plcip = find_plci_by_ncci(card, cmsg->adr.adrNCCI); if (plcip) { nccip = new_ncci(card, plcip, cmsg->adr.adrNCCI); if (nccip) { ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_IND); capi_fill_CONNECT_B3_RESP(cmsg, ctx->appid, card->msgid++, nccip->ncci, /* adr */ 0, /* Reject */ 0 /* NCPI */ ); send_message(card, cmsg); ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_RESP); break; } (*cb->errmsg)("capidrv-%d: no mem for ncci, sorry", card->contrnr); } else { (*cb->errmsg)("capidrv-%d: %s: plci for ncci 0x%x not found", card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrNCCI); } capi_fill_CONNECT_B3_RESP(cmsg, ctx->appid, card->msgid++, cmsg->adr.adrNCCI, 2, /* Reject */ plcip->conninfo.ncpi); send_message(card, cmsg); break; case CAPI_CONNECT_B3_CONF: /* ncci */ if (!(nccip = find_ncci_by_msgid(card, cmsg->adr.adrNCCI, cmsg->Messagenumber))) goto notfound; nccip->ncci = cmsg->adr.adrNCCI; nccip->plcip->ncci = cmsg->adr.adrNCCI; if (cmsg->Info) { (*cb->infomsg)("%s info 0x%x (%s) for ncci 0x%x", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Info, capi_info2str(cmsg->Info), cmsg->adr.adrNCCI); } if (cmsg->Info) ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_CONF_ERROR); else ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_CONF_OK); break; case CAPI_CONNECT_B3_T90_ACTIVE_IND: /* ncci */ capi_cmsg_answer(cmsg); send_message(card, cmsg); break; case CAPI_DATA_B3_IND: /* ncci */ /* handled in handle_data() */ goto ignored; case CAPI_DATA_B3_CONF: /* ncci */ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) goto notfound; data = capi_del_ack(nccip, cmsg->DataHandle); if (data == 0) break; if (cb->datasent) (*cb->datasent)(nccip->plcip, data); break; case CAPI_DISCONNECT_B3_IND: /* ncci */ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) goto notfound; nccip->plcip->disconnectreason_b3 = cmsg->Reason_B3; nccip->plcip->disconnecting = 1; ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_IND); capi_cmsg_answer(cmsg); send_message(card, cmsg); ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_RESP); break; case CAPI_DISCONNECT_B3_CONF: /* ncci */ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) goto notfound; if (cmsg->Info) { (*cb->infomsg)("%s info 0x%x (%s) for ncci 0x%x", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Info, capi_info2str(cmsg->Info), cmsg->adr.adrNCCI); ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_CONF_ERROR); } break; case CAPI_RESET_B3_IND: /* ncci */ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) goto notfound; ncci_change_state(card, nccip, EV_NCCI_RESET_B3_IND); capi_cmsg_answer(cmsg); send_message(card, cmsg); break; case CAPI_RESET_B3_CONF: /* ncci */ goto ignored; /* $$$$ */ case CAPI_FACILITY_IND: /* Controller/plci/ncci */ goto ignored; case CAPI_FACILITY_CONF: /* Controller/plci/ncci */ goto ignored; default: (*cb->errmsg)("%d: got %s for ncci 0x%x ???", card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrNCCI); } return; ignored: (*cb->infomsg)("%s for ncci 0x%x ignored", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrNCCI); return; notfound: (*cb->errmsg)("%s: ncci 0x%x not found", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrNCCI); } static void handle_data(capiconn_context *ctx, _cmsg * cmsg) { capi_contr *card = findcontrbynumber(ctx,cmsg->adr.adrController&0x7f); capiconn_callbacks *cb = ctx->cb; capi_ncci *nccip; unsigned char *data; if (!card) { (*cb->errmsg)("%s from unknown controller 0x%x", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrController & 0x7f); return; } if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) { (*cb->errmsg)("%s: ncci 0x%x not found", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrNCCI); return; } data = (unsigned char *)cmsg->Data; if (cb->received) (*cb->received)(nccip->plcip, data, cmsg->DataLength); capi_cmsg_answer(cmsg); send_message(card, cmsg); } static _cmsg s_cmsg; void capiconn_inject(unsigned applid, unsigned char *msg) { capiconn_context *ctx = find_context(applid); if (!ctx) return; capi_message2cmsg(&s_cmsg, msg); if (s_cmsg.Command == CAPI_DATA_B3 && s_cmsg.Subcommand == CAPI_IND) { handle_data(ctx, &s_cmsg); ctx->nrecvdatapkt++; return; } if ((s_cmsg.adr.adrController & 0xffffff00) == 0) handle_controller(ctx, &s_cmsg); else if ((s_cmsg.adr.adrPLCI & 0xffff0000) == 0) handle_plci(ctx, &s_cmsg); else handle_ncci(ctx, &s_cmsg); ctx->nrecvctlpkt++; } /* ------------------------------------------------------------------- */ capi_connection *capiconn_connect( capiconn_context *ctx, unsigned contr, _cword cipvalue, char *callednumber, /* remote number */ char *callingnumber, /* own number */ _cword b1proto, _cword b2proto, _cword b3proto, _cstruct b1config, _cstruct b2config, _cstruct b3config, _cstruct bchaninfo, _cstruct ncpi) { capi_contr *card = findcontrbynumber(ctx, contr); capiconn_callbacks *cb = ctx->cb; capi_connection *plcip; if (!card) { (*cb->errmsg)("controller %d not found", contr); return 0; } if ((plcip = new_plci(card, 0)) == 0) { (*cb->errmsg)("no mem for plci"); return 0; } if (set_conninfo1a(ctx, &plcip->conninfo, cipvalue, callednumber, callingnumber) < 0) { clr_conninfo1(ctx, &plcip->conninfo); free_plci(card, plcip); (*cb->errmsg)("no mem for connection info (1a)"); return 0; } if (set_conninfo2(ctx, &plcip->conninfo, b1proto, b2proto, b3proto, b1config, b2config, b3config, bchaninfo, ncpi) < 0) { clr_conninfo1(ctx, &plcip->conninfo); clr_conninfo2(ctx, &plcip->conninfo); free_plci(card, plcip); (*cb->errmsg)("no mem for connection info (2)"); return 0; } capi_fill_CONNECT_REQ(&cmdcmsg, ctx->appid, card->msgid++, card->contrnr, /* adr */ plcip->conninfo.cipvalue, plcip->conninfo.callednumber, plcip->conninfo.callingnumber, 0, /* CalledPartySubaddress */ 0, /* CallingPartySubaddress */ plcip->conninfo.b1proto, plcip->conninfo.b2proto, plcip->conninfo.b3proto, plcip->conninfo.b1config, plcip->conninfo.b2config, plcip->conninfo.b3config, 0, /* Globalconfiguration */ 0, /* BC */ 0, /* LLC */ 0, /* HLC */ plcip->conninfo.bchaninfo, /* BChannelinformation */ 0, /* Keypadfacility */ 0, /* Useruserdata */ 0, /* Facilitydataarray */ 0 /* SendingComplete */ ); plcip->msgid = cmdcmsg.Messagenumber; plci_change_state(card, plcip, EV_PLCI_CONNECT_REQ); send_message(card, &cmdcmsg); return plcip; } int capiconn_accept( capi_connection *plcip, _cword b1proto, _cword b2proto, _cword b3proto, _cstruct b1config, _cstruct b2config, _cstruct b3config, _cstruct ncpi) { capi_contr *card = plcip->contr; capiconn_context *ctx = card->ctx; capiconn_callbacks *cb = ctx->cb; if (plcip->state != ST_PLCI_INCOMING) return CAPICONN_WRONG_STATE; if (set_conninfo2(ctx, &plcip->conninfo, b1proto, b2proto, b3proto, b1config, b2config, b3config, 0, ncpi) < 0) { clr_conninfo2(ctx, &plcip->conninfo); (*cb->errmsg)("no mem for connection info (2)"); return CAPICONN_NO_MEMORY; } (*cb->debugmsg)("accept plci 0x%04x %d,%d,%d", plcip->plci, (int)plcip->conninfo.b1proto, (int)plcip->conninfo.b2proto, (int)plcip->conninfo.b3proto); capi_fill_CONNECT_RESP(&cmdcmsg, ctx->appid, card->msgid++, plcip->plci, /* adr */ 0, /* Reject */ plcip->conninfo.b1proto, plcip->conninfo.b2proto, plcip->conninfo.b3proto, plcip->conninfo.b1config, plcip->conninfo.b2config, plcip->conninfo.b3config, 0, /* Globalconfiguration */ 0, /* ConnectedNumber */ 0, /* ConnectedSubaddress */ 0, /* LLC */ 0, /* BChannelinformation */ 0, /* Keypadfacility */ 0, /* Useruserdata */ 0 /* Facilitydataarray */ ); capi_cmsg2message(&cmdcmsg, cmdcmsg.buf); plci_change_state(card, plcip, EV_PLCI_CONNECT_RESP); send_message(card, &cmdcmsg); return CAPICONN_OK; } int capiconn_ignore(capi_connection *plcip) { capi_contr *card = plcip->contr; capiconn_context *ctx = card->ctx; if (plcip->state != ST_PLCI_INCOMING) return CAPICONN_WRONG_STATE; capi_fill_CONNECT_RESP(&cmdcmsg, ctx->appid, card->msgid++, plcip->plci, /* adr */ 1, /* ignore call */ 0, 0, 0, 0, 0, 0, 0, /* Globalconfiguration */ 0, /* ConnectedNumber */ 0, /* ConnectedSubaddress */ 0, /* LLC */ 0, /* BChannelinformation */ 0, /* Keypadfacility */ 0, /* Useruserdata */ 0 /* Facilitydataarray */ ); capi_cmsg2message(&cmdcmsg, cmdcmsg.buf); plci_change_state(card, plcip, EV_PLCI_CONNECT_RESP); send_message(card, &cmdcmsg); return CAPICONN_OK; } int capiconn_reject(capi_connection *plcip) { capi_contr *card = plcip->contr; capiconn_context *ctx = card->ctx; if (plcip->state != ST_PLCI_INCOMING) return CAPICONN_WRONG_STATE; capi_fill_CONNECT_RESP(&cmdcmsg, ctx->appid, card->msgid++, plcip->plci, /* adr */ 2, /* Reject, normal call clearing */ 0, 0, 0, 0, 0, 0, 0, /* Globalconfiguration */ 0, /* ConnectedNumber */ 0, /* ConnectedSubaddress */ 0, /* LLC */ 0, /* BChannelinformation */ 0, /* Keypadfacility */ 0, /* Useruserdata */ 0 /* Facilitydataarray */ ); capi_cmsg2message(&cmdcmsg, cmdcmsg.buf); plci_change_state(card, plcip, EV_PLCI_CONNECT_RESP); send_message(card, &cmdcmsg); return CAPICONN_OK; } int capiconn_disconnect(capi_connection *plcip, _cstruct ncpi) { capi_contr *card = plcip->contr; capiconn_context *ctx = card->ctx; if (plcip->disconnecting) return CAPICONN_ALREADY_DISCONNECTING; if (plcip->nccip) { plcip->disconnecting = 1; plcip->localdisconnect = 1; capi_fill_DISCONNECT_B3_REQ(&cmdcmsg, ctx->appid, card->msgid++, plcip->ncci, ncpi); ncci_change_state(card, plcip->nccip, EV_NCCI_DISCONNECT_B3_REQ); send_message(card, &cmdcmsg); return CAPICONN_OK; } if (plcip->state == ST_PLCI_INCOMING) { plcip->disconnecting = 1; plcip->localdisconnect = 1; return capiconn_reject(plcip); } if (plcip->plci) { plcip->disconnecting = 1; plcip->localdisconnect = 1; capi_fill_DISCONNECT_REQ(&cmdcmsg, ctx->appid, card->msgid++, plcip->plci, 0, /* BChannelinformation */ 0, /* Keypadfacility */ 0, /* Useruserdata */ 0 /* Facilitydataarray */ ); plci_change_state(card, plcip, EV_PLCI_DISCONNECT_REQ); send_message(card, &cmdcmsg); return CAPICONN_OK; } return CAPICONN_WRONG_STATE; } static _cmsg sendcmsg; int capiconn_send(capi_connection *plcip, unsigned char *data, unsigned len) { capi_contr *card = plcip->contr; capiconn_context *ctx = card->ctx; capiconn_callbacks *cb = ctx->cb; capi_ncci *nccip; _cword datahandle; nccip = plcip->nccip; if (!nccip || nccip->state != ST_NCCI_ACTIVE) return CAPICONN_WRONG_STATE; datahandle = nccip->datahandle; capi_fill_DATA_B3_REQ(&sendcmsg, ctx->appid, card->msgid++, nccip->ncci, /* adr */ data, /* Data */ len, /* DataLength */ datahandle, /* DataHandle */ 0 /* Flags */ ); if (capi_add_ack(nccip, datahandle, data) < 0) return CAPICONN_NOT_SENT; capi_cmsg2message(&sendcmsg, sendcmsg.buf); (*cb->capi_put_message) (ctx->appid, sendcmsg.buf); nccip->datahandle++; ctx->nsentdatapkt++; return CAPICONN_OK; } /* -------- listen handling ------------------------------------------ */ static void send_listen(capi_contr *card) { capiconn_context *ctx = card->ctx; card->infomask = 0; card->infomask |= (1<<0); /* cause information */ card->infomask |= (1<<2); /* Display */ card->infomask |= (1<<6); /* Charge Info */ if (card->ddilen) card->infomask |= (1<<7); /* Called Party Number */ card->infomask |= (1<<8); /* Channel Info */ capi_fill_LISTEN_REQ(&cmdcmsg, ctx->appid, card->msgid++, card->contrnr, card->infomask, card->cipmask, card->cipmask2, 0, 0); send_message(card, &cmdcmsg); listen_change_state(card, EV_LISTEN_REQ); } int capiconn_listen(capiconn_context *ctx, unsigned contr, unsigned cipmask, unsigned cipmask2) { capi_contr *card = findcontrbynumber(ctx, contr & 0x7f); if (card == 0) return CAPICONN_NO_CONTROLLER; card->cipmask = cipmask; /* 0x1FFF03FF */ card->cipmask2 = cipmask2; /* 0 */ send_listen(card); return CAPICONN_OK; } int capiconn_listenstate(capiconn_context *ctx, unsigned contr) { capi_contr *card = findcontrbynumber(ctx, contr & 0x7f); if (card == 0) return CAPICONN_NO_CONTROLLER; if (card->state != ST_LISTEN_NONE && card->state != ST_LISTEN_ACTIVE) return CAPICONN_WRONG_STATE; return CAPICONN_OK; }