mirror of https://github.com/MelwareDE/ITunD
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1793 lines
47 KiB
1793 lines
47 KiB
/*
|
|
* CAPI connection handling
|
|
*
|
|
* heavily based on capiconn.* of pppdcapiplugin by
|
|
* Carsten Paeth (calle@calle.in-berlin.de)
|
|
*
|
|
* Copyright 2004 SYSGO Real-Time Solutions AG
|
|
* Klein-Winternheim, Germany
|
|
* All rights reserved.
|
|
*
|
|
* Author: Armin Schindler <armin.schindler@sysgo.com>
|
|
*
|
|
* $Id$
|
|
*
|
|
* 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 <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* 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 <stdio.h> /* snprintf */
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "isdntun.h"
|
|
#include "capiconn.h"
|
|
|
|
/* xxxxxxxxxxxxxxxxxx */
|
|
static _cmsg cmdcmsg;
|
|
static _cmsg cmsg;
|
|
|
|
/* -------- defines -------------------------------------------------- */
|
|
|
|
#ifndef CAPI_MAXDATAWINDOW
|
|
#define CAPI_MAXDATAWINDOW 4
|
|
#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(&cmsg,
|
|
ctx->appid,
|
|
card->msgid++,
|
|
ncci->plcip->plci,
|
|
0, /* BChannelinformation */
|
|
0, /* Keypadfacility */
|
|
0, /* Useruserdata */ /* $$$$ */
|
|
0 /* Facilitydataarray */
|
|
);
|
|
send_message(card, &cmsg);
|
|
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(&cmsg,
|
|
ctx->appid,
|
|
card->msgid++,
|
|
plcip->plci, /* adr */
|
|
0, /* BChannelinformation */
|
|
0, /* Keypadfacility */
|
|
0, /* Useruserdata */
|
|
0, /* Facilitydataarray */
|
|
0 /* SendingComplete */
|
|
);
|
|
plcip->msgid = cmsg.Messagenumber;
|
|
send_message(card, &cmsg);
|
|
}
|
|
}
|
|
|
|
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,
|
|
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 *p = &cmsg->InfoElement[1];
|
|
charge |= ((unsigned)p[0]);
|
|
charge |= ((unsigned)p[1]) << 8;
|
|
charge |= ((unsigned)p[2]) << 16;
|
|
charge |= ((unsigned)p[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;
|
|
}
|