From a9f752ad693d203695b5626d753a4ca672b9aa4c Mon Sep 17 00:00:00 2001 From: MelwareDE Date: Thu, 2 Jun 2005 18:33:27 +0000 Subject: [PATCH] Added support for ISDN CAPI interface using libcapi20. --- COPYRIGHT | 3 + Makefile | 4 +- README | 4 + capiconn.c | 1778 ++++++++++++++++++++++++++++++++++++++++++++++++++++ capiconn.h | 429 +++++++++++++ pager.h | 14 + scr.c | 1 + tty.c | 465 +++++++++++++- yaps.c | 38 +- yaps.doc | 21 +- yaps.html | 12 + yaps.lsm | 2 +- 12 files changed, 2717 insertions(+), 54 deletions(-) create mode 100644 capiconn.c create mode 100644 capiconn.h diff --git a/COPYRIGHT b/COPYRIGHT index 1d94d4d..b3ce6f5 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,5 +1,8 @@ Copyright (c) 1997 Ulrich Dessauer All rights reserved. + + Support for ISDN CAPI copyright (c) 2004-2005 by + Armin Schindler You may distribute under the terms of the GNU General Public License. diff --git a/Makefile b/Makefile index d88e3ee..bde5fc5 100644 --- a/Makefile +++ b/Makefile @@ -7,9 +7,9 @@ CFLAGS := $(CFLAGS) # DEFS = -DCFGFILE=\"$(YAPS_CFGFILE)\" -DLCFGFILE=\"$(YAPS_LCFGFILE)\" \ -DLIBDIR=\"$(YAPS_LIBDIR)\" -LIBS = -L. -lpager $(LLIBS) +LIBS = -L. -lpager $(LLIBS) -lcapi20 OBJS = data.o util.o cfg.o tty.o cv.o asc.o scr.o tap.o ucp.o \ - slang.o lua.o #mem.o + slang.o lua.o capiconn.o #mem.o YOBJS = yaps.o valid.o DSTFLE = $(YAPS_BINDIR)/yaps diff --git a/README b/README index bff7b4e..029f87e 100644 --- a/README +++ b/README @@ -14,6 +14,9 @@ Copyright: Copyright (c) 1997 Ulrich Dessauer All rights reserved. + Support for ISDN-CAPI copyright (c) 2004-2005 by + Armin Schindler + You may distribute under the terms of the GNU General Public License. @@ -106,6 +109,7 @@ Makefile, Config: Make rules yaps.rc: Sample configuration file yaps.c: The driver program valid.c, valid.h: Support routines for the driver program +capiconn.c, capiconn.h: CAPI support remaining *.c, *.h: The paging library yaps.html, yaps.doc: Documentation in HTML and plain text yaps.1: A minimal manual page diff --git a/capiconn.c b/capiconn.c new file mode 100644 index 0000000..742a4e4 --- /dev/null +++ b/capiconn.c @@ -0,0 +1,1778 @@ +/* + * 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 */ + ); + 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, /* 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, /* BC */ + 0, /* LLC */ + 0, /* HLC */ + plcip->conninfo.bchaninfo, /* BChannelinformation */ + 0, /* Keypadfacility */ + 0, /* Useruserdata */ + 0 /* Facilitydataarray */ + ); + + 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, /* 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, /* 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, /* 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; +} diff --git a/capiconn.h b/capiconn.h new file mode 100644 index 0000000..6c61ca6 --- /dev/null +++ b/capiconn.h @@ -0,0 +1,429 @@ +/* + * 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. + */ + +#ifndef __CAPICONN_H__ +#define __CAPICONN_H__ + +#include +#include + +/* + * CAPI_MESSAGES: + * capiconn_inject(): + * inject capi message into state machine + * + * capiconn_context: + * capiconn_getcontext(): + * get a context and supply callback functions + * capiconn_freecontext(): + * free a context allocated with "capiconn_getcontext" + * capiconn_addcontr(): + * add a controller to the context + * capi_connection: + * capiconn_connect(): + * connection setup, return a capi_connection. + * capiconn_accept(): + * accept an incoming connection + * capiconn_ignore(): + * ignore an incoming connection + * capiconn_reject(): + * reject an incoming connection + * capiconn_send(): + * send data to the connection + * capiconn_disconnect(): + * disconnect a connection + * capiconn_getinfo() + * get infos about the connection + * capiconn_listen(): + * setup listen + */ + +/* -------- returncodes -------------------------------------------------- */ + +#define CAPICONN_OK 0 +#define CAPICONN_NO_CONTROLLER -1 +#define CAPICONN_NO_MEMORY -2 +#define CAPICONN_WRONG_STATE 1 +#define CAPICONN_NOT_SENT 2 +#define CAPICONN_ALREADY_DISCONNECTING 3 + +/* -------- states for CAPI2.0 machine ----------------------------------- */ + +/* + * LISTEN state machine + */ +#define ST_LISTEN_NONE 0 /* L-0 */ +#define ST_LISTEN_WAIT_CONF 1 /* L-0.1 */ +#define ST_LISTEN_ACTIVE 2 /* L-1 */ +#define ST_LISTEN_ACTIVE_WAIT_CONF 3 /* L-1.1 */ + + +#define EV_LISTEN_REQ 1 /* L-0 -> L-0.1 + L-1 -> L-1.1 */ +#define EV_LISTEN_CONF_ERROR 2 /* L-0.1 -> L-0 + L-1.1 -> L-1 */ +#define EV_LISTEN_CONF_EMPTY 3 /* L-0.1 -> L-0 + L-1.1 -> L-0 */ +#define EV_LISTEN_CONF_OK 4 /* L-0.1 -> L-1 + L-1.1 -> L.1 */ + +/* + * per plci state machine + */ +#define ST_PLCI_NONE 0 /* P-0 */ +#define ST_PLCI_OUTGOING 1 /* P-0.1 */ +#define ST_PLCI_ALLOCATED 2 /* P-1 */ +#define ST_PLCI_ACTIVE 3 /* P-ACT */ +#define ST_PLCI_INCOMING 4 /* P-2 */ +#define ST_PLCI_FACILITY_IND 5 /* P-3 */ +#define ST_PLCI_ACCEPTING 6 /* P-4 */ +#define ST_PLCI_DISCONNECTING 7 /* P-5 */ +#define ST_PLCI_DISCONNECTED 8 /* P-6 */ +#define ST_PLCI_RESUMEING 9 /* P-0.Res */ +#define ST_PLCI_RESUME 10 /* P-Res */ +#define ST_PLCI_HELD 11 /* P-HELD */ + +#define EV_PLCI_CONNECT_REQ 1 /* P-0 -> P-0.1 + */ +#define EV_PLCI_CONNECT_CONF_ERROR 2 /* P-0.1 -> P-0 + */ +#define EV_PLCI_CONNECT_CONF_OK 3 /* P-0.1 -> P-1 + */ +#define EV_PLCI_FACILITY_IND_UP 4 /* P-0 -> P-1 + */ +#define EV_PLCI_CONNECT_IND 5 /* P-0 -> P-2 + */ +#define EV_PLCI_CONNECT_ACTIVE_IND 6 /* P-1 -> P-ACT + */ +#define EV_PLCI_CONNECT_REJECT 7 /* P-2 -> P-5 + P-3 -> P-5 + */ +#define EV_PLCI_DISCONNECT_REQ 8 /* P-1 -> P-5 + P-2 -> P-5 + P-3 -> P-5 + P-4 -> P-5 + P-ACT -> P-5 + P-Res -> P-5 (*) + P-HELD -> P-5 (*) + */ +#define EV_PLCI_DISCONNECT_IND 9 /* P-1 -> P-6 + P-2 -> P-6 + P-3 -> P-6 + P-4 -> P-6 + P-5 -> P-6 + P-ACT -> P-6 + P-Res -> P-6 (*) + P-HELD -> P-6 (*) + */ +#define EV_PLCI_FACILITY_IND_DOWN 10 /* P-0.1 -> P-5 + P-1 -> P-5 + P-ACT -> P-5 + P-2 -> P-5 + P-3 -> P-5 + P-4 -> P-5 + */ +#define EV_PLCI_DISCONNECT_RESP 11 /* P-6 -> P-0 + */ +#define EV_PLCI_CONNECT_RESP 12 /* P-6 -> P-0 + */ + +#define EV_PLCI_RESUME_REQ 13 /* P-0 -> P-0.Res + */ +#define EV_PLCI_RESUME_CONF_OK 14 /* P-0.Res -> P-Res + */ +#define EV_PLCI_RESUME_CONF_ERROR 15 /* P-0.Res -> P-0 + */ +#define EV_PLCI_RESUME_IND 16 /* P-Res -> P-ACT + */ +#define EV_PLCI_HOLD_IND 17 /* P-ACT -> P-HELD + */ +#define EV_PLCI_RETRIEVE_IND 18 /* P-HELD -> P-ACT + */ +#define EV_PLCI_SUSPEND_IND 19 /* P-ACT -> P-5 + */ +#define EV_PLCI_CD_IND 20 /* P-2 -> P-5 + */ + +/* + * per ncci state machine + */ +#define ST_NCCI_PREVIOUS -1 +#define ST_NCCI_NONE 0 /* N-0 */ +#define ST_NCCI_OUTGOING 1 /* N-0.1 */ +#define ST_NCCI_INCOMING 2 /* N-1 */ +#define ST_NCCI_ALLOCATED 3 /* N-2 */ +#define ST_NCCI_ACTIVE 4 /* N-ACT */ +#define ST_NCCI_RESETING 5 /* N-3 */ +#define ST_NCCI_DISCONNECTING 6 /* N-4 */ +#define ST_NCCI_DISCONNECTED 7 /* N-5 */ + +#define EV_NCCI_CONNECT_B3_REQ 1 /* N-0 -> N-0.1 */ +#define EV_NCCI_CONNECT_B3_IND 2 /* N-0 -> N.1 */ +#define EV_NCCI_CONNECT_B3_CONF_OK 3 /* N-0.1 -> N.2 */ +#define EV_NCCI_CONNECT_B3_CONF_ERROR 4 /* N-0.1 -> N.0 */ +#define EV_NCCI_CONNECT_B3_REJECT 5 /* N-1 -> N-4 */ +#define EV_NCCI_CONNECT_B3_RESP 6 /* N-1 -> N-2 */ +#define EV_NCCI_CONNECT_B3_ACTIVE_IND 7 /* N-2 -> N-ACT */ +#define EV_NCCI_RESET_B3_REQ 8 /* N-ACT -> N-3 */ +#define EV_NCCI_RESET_B3_IND 9 /* N-3 -> N-ACT */ +#define EV_NCCI_DISCONNECT_B3_IND 10 /* N-4 -> N.5 */ +#define EV_NCCI_DISCONNECT_B3_CONF_ERROR 11 /* N-4 -> previous */ +#define EV_NCCI_DISCONNECT_B3_REQ 12 /* N-1 -> N-4 + N-2 -> N-4 + N-3 -> N-4 + N-ACT -> N-4 */ +#define EV_NCCI_DISCONNECT_B3_RESP 13 /* N-5 -> N-0 */ + +/* ----------------------------------------------------------------------- */ + +char *capiconn_version(void); + +/* -------- context ------------------------------------------------------ */ + +struct capi_connection; typedef struct capi_connection capi_connection; +struct capiconn_context; typedef struct capiconn_context capiconn_context; + +struct capiconn_callbacks +{ + /* ---------- memory functions ----------------- */ + + void *(*malloc)(size_t size); + void (*free)(void *buf); + + /* ---------- connection callbacks ------------- */ + + /* + * the capi_connection will be destoried after + * calling this function + */ + void (*disconnected)(capi_connection *, + int localdisconnect, + unsigned reason, + unsigned reason_b3); + /* + * The application should call capiconn_accept() + * capiconn_ignore() or capiconn_reject() inside + * of the function. If not called an ALERT_REQ will + * be sent, to let the application time to decide ... + */ + void (*incoming)(capi_connection *, + unsigned contr, + unsigned chipvalue, + char *callednumber, + char *callingnumber); + + /* + * Channel is ready to send and receive data + */ + void (*connected)(capi_connection *, _cstruct); + + /* + * Data received on channel, the data pointer + * can not be used, after this function is called. + */ + void (*received)(capi_connection *, + unsigned char *data, + unsigned datalen); + + /* + * called for every call to capiconn_send(). + */ + void (*datasent)(capi_connection *, unsigned char *); + + /* + * charge info received + */ + void (*chargeinfo)(capi_connection *, + unsigned long charge, + int inunits); + /* + * capi functions + */ + void (*capi_put_message) (unsigned appid, unsigned char *msg); + + /* + * message functions + */ + void (*debugmsg)(const char *fmt, ...); + void (*infomsg)(const char *fmt, ...); + void (*errmsg)(const char *fmt, ...); +}; +typedef struct capiconn_callbacks capiconn_callbacks; + +/* -------- context functions -------------------------------------------- */ + +capiconn_context *capiconn_getcontext(unsigned appid, capiconn_callbacks *env); + +int capiconn_freecontext(capiconn_context *ctx); + +/* -------- inject capi message into state machine ----------------------- */ + +void capiconn_inject(unsigned appid, unsigned char *msg); + +/* -------- add controller to context ------------------------------------ */ + +struct capi_contrinfo { + int bchannels; + char *ddi; + int ndigits; /* Durchwahllaenge */ +}; +typedef struct capi_contrinfo capi_contrinfo; + +/* + * returncodes: + * CAPICONN_OK - controller added to the context + * CAPICONN_NO_MEMORY - callback "malloc" returns no memory. + */ +int capiconn_addcontr(capiconn_context *ctx, + unsigned contr, capi_contrinfo *cinfo); + +/* -------- initiate a connection & disconnect a connection -------------- */ + +/* + * returncodes: + * a capi connection or 0 if + * - controller not found + * - memory problems + */ + +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); + +/* + * returncodes: + * CAPICONN_OK - disconnect initiated + * CAPICONN_ALREADY_DISCONNECTING - disconnect already initiated + * CAPICONN_WRONG_STATE - should not happen + */ +int capiconn_disconnect(capi_connection *connection, _cstruct ncpi); + +/* -------- reaction on incoming calls ----------------------------------- */ + +/* + * returncodes: + * CAPICONN_OK - Accept initiated + * CAPICONN_WRONG_STATE - "conn" ist not in state "incoming" + * CAPICONN_NO_MEMORY - callback "malloc" returns no memory. + */ +int capiconn_accept( + capi_connection *conn, + _cword b1proto, + _cword b2proto, + _cword b3proto, + _cstruct b1config, + _cstruct b2config, + _cstruct b3config, + _cstruct ncpi); + +/* + * returncodes: + * CAPICONN_OK - call will be ignored + * CAPICONN_WRONG_STATE - "conn" ist not in state "incoming" + */ +int capiconn_ignore(capi_connection *conn); + +/* + * returncodes: + * CAPICONN_OK - call will be rejected + * CAPICONN_WRONG_STATE - "conn" ist not in state "incoming" + */ +int capiconn_reject(capi_connection *conn); + +/* + * returncode: + * CAPICONN_OK - Data sent to CAPI + * CAPICONN_NOT_SENT - Data not sent (8 messages not acked) + * CAPICONN_WRONG_STATE - Connection is not connected + */ +int capiconn_send(capi_connection *plcip, + unsigned char *data, + unsigned len); + +/* + * get info about connection + */ + +struct capi_conninfo { + unsigned appid; + + unsigned plci; + int plci_state; + unsigned ncci; + int ncci_state; + unsigned isincoming:1, + disconnect_was_local; + unsigned disconnectreason; + unsigned disconnectreason_b3; + + /* user supplied */ + _cword cipvalue; + _cstruct callednumber; + _cstruct callingnumber; + _cword b1proto; + _cword b2proto; + _cword b3proto; + _cstruct b1config; + _cstruct b2config; + _cstruct b3config; + _cstruct bchaninfo; + _cstruct ncpi; +}; +typedef struct capi_conninfo capi_conninfo; + +/* + * returncode: + * always a pointer to the conninfo. + * the conninfo will not be update automaticly, + * you always have to call this function to get + * the actual informations. + */ +capi_conninfo *capiconn_getinfo(capi_connection *p); + +/* + * returncodes: + * CAPICONN_OK - Listen request sent + * CAPICONN_NO_CONTROLLER - Controller "contr" not added with + * capiconn_addcontr() + */ +int capiconn_listen(capiconn_context *ctx, + unsigned contr, unsigned cipmask, unsigned cipmask2); + +/* + * returncodes: + * CAPICONN_OK - got ack for listen request + * CAPICONN_NO_CONTROLLER - Controller "contr" not added with + * capiconn_addcontr() + * CAPICONN_WRONG_STATE - got no ack for listen request + */ +int capiconn_listenstate(capiconn_context *ctx, unsigned contr); + +#endif /* __CAPICONN_H__ */ diff --git a/pager.h b/pager.h index 47f91c3..289382c 100644 --- a/pager.h +++ b/pager.h @@ -204,4 +204,18 @@ extern void ucp_postdeinit (void); extern int verbose; extern int (*verbout) (char *, ...); /*}}}*/ +/*{{{ CAPI */ +extern int device_is_capi(char *dev); +extern int hyla_is_capi(void *sp); +extern void *hyla_capi_init(char *dev); +extern void hyla_capi_disconnect(void *sp); +extern void *hyla_capi_close(void *sp); +extern int hyla_capi_connect(void *sp, char *dial, char *number); +extern int hyla_capi_send(void *sp, char *str, int len); +extern size_t hyla_capi_read(void *sp, void *buf, size_t count); +extern void hyla_capi_iflush(void *sp); +extern void hyla_capi_oflush(void *sp); +extern void hyla_capi_drain(void *sp); +extern void hyla_capi_sendbreak(void *sp, int duration); +/*}}}*/ # endif /* __PAGER_H */ diff --git a/scr.c b/scr.c index 5b65ab6..3a31551 100644 --- a/scr.c +++ b/scr.c @@ -2,6 +2,7 @@ # include "config.h" # include # include +# include # include "pager.h" # include "script.h" diff --git a/tty.c b/tty.c index 8c8d0ee..107377a 100644 --- a/tty.c +++ b/tty.c @@ -78,6 +78,11 @@ typedef struct { void (*callback) (void *, string_t *, char_t, void *); void *data; Bool suspend; + /* CAPI */ + int is_capi; + int controller; + char *msn; + int connected; } serial; typedef struct _expect { @@ -88,6 +93,9 @@ typedef struct _expect { struct _expect *next; } expect; + +static int data_ready (serial *s, int fd, int *msec); + /*}}}*/ /*{{{ support routines */ static char * @@ -186,24 +194,6 @@ msleep (int msec) } } -static inline int -data_ready (int fd, int *msec) -{ - int ret = 0; - struct timeval tv; - fd_set fset; - - FD_ZERO (& fset); - FD_SET (fd, & fset); - tv.tv_sec = *msec / 1000; - tv.tv_usec = (*msec % 1000) * 1000; - if (((ret = select (fd + 1, & fset, NULL, NULL, & tv)) > 0) && FD_ISSET (fd, & fset)) { - *msec = tv.tv_sec * 1000 + (tv.tv_usec / 1000); - return 1; - } - return ret; -} - static Bool do_lock (serial *s, char *dev, char *prefix, char *method) { @@ -366,6 +356,7 @@ tty_open (char *dev, char *lckprefix, char *lckmethod) # ifndef NDEBUG s -> magic = MAGIC; # endif /* NDEBUG */ + s -> is_capi = 0; if (do_lock (s, dev, lckprefix, lckmethod)) { if ((s -> fd = open (dev, O_RDWR)) != -1) { n = tcgetattr (s -> fd, & s -> sav); @@ -401,6 +392,9 @@ tty_close (void *sp) serial *s = (serial *) sp; MCHK (s); + if (s -> is_capi) { + return(hyla_capi_close(sp)); + } if (s) { if (s -> fd != -1) { tcsetattr (s -> fd, TCSANOW, & s -> sav); @@ -424,6 +418,11 @@ tty_reopen (void *sp, int msec) serial *s = (serial *) sp; MCHK (s); + + if (s -> is_capi) { + return((s -> connected) ? True : False); + } + if (s -> fd != -1) { close (s -> fd); if (msec > 0) @@ -445,14 +444,19 @@ tty_hangup (void *sp, int msec) MCHK (s); V (2, ("[Hangup] ")); if (s && (s -> fd != -1)) { - tmp = s -> tty; - cfsetispeed (& tmp, B0); - cfsetospeed (& tmp, B0); - if (tcsetattr (s -> fd, TCSANOW, & tmp) != -1) { - msleep (msec); - tcsetattr (s -> fd, TCSANOW, & s -> tty); + if (s -> is_capi) { + hyla_capi_disconnect(sp); + return; + } else { + tmp = s -> tty; + cfsetispeed (& tmp, B0); + cfsetospeed (& tmp, B0); + if (tcsetattr (s -> fd, TCSANOW, & tmp) != -1) { + msleep (msec); + tcsetattr (s -> fd, TCSANOW, & s -> tty); + } + tty_reopen (s, msec); } - tty_reopen (s, msec); } V (2, ("\n")); } @@ -598,7 +602,14 @@ tty_send (void *sp, char *str, int len) return -1; V (2, ("[Send] %s", mkprint (str, len))); sent = 0; - while (len > 0) + while (len > 0) { + if (s -> is_capi) { + if (hyla_capi_send(sp, str, len)) { + sent = len; + len = 0; + } + break; + } if ((n = write (s -> fd, str, len)) > 0) { str += n; sent += n; @@ -612,6 +623,7 @@ tty_send (void *sp, char *str, int len) msleep (200); else if (errno != EINTR) break; + } V (2, ("\n")); return sent; } @@ -640,6 +652,14 @@ addline (serial *s, char ch) } } +static size_t dev_read(serial *s, int fd, void *buf, size_t count) +{ + if (s -> is_capi) + return(hyla_capi_read(s, buf, count)); + + return(read(fd, buf, count)); +} + static int do_expect (serial *s, int tout, expect *base) { @@ -657,8 +677,8 @@ do_expect (serial *s, int tout, expect *base) msec = (tout > 0) ? tout * 1000 : 0; ret = 0; while (! ret) - if ((n = data_ready (s -> fd, & msec)) > 0) { - while ((n = read (s -> fd, & ch, 1)) == 1) { + if ((n = data_ready (s, s -> fd, & msec)) > 0) { + while ((n = dev_read (s, s -> fd, & ch, 1)) == 1) { addline (s, ch); V (3, ("%s", mkprint (& ch, 1))); for (run = base; run; run = run -> next) @@ -900,7 +920,10 @@ handle_command (void *sp, char *str) case 'b': if (s && (s -> fd != -1)) { V (2, (" Brk %d", mult)); - tcsendbreak (s -> fd, mult); + if (s -> is_capi) + hyla_capi_sendbreak(s, mult); + else + tcsendbreak (s -> fd, mult); } break; case 'h': @@ -912,19 +935,28 @@ handle_command (void *sp, char *str) case 'o': if (s && (s -> fd != -1)) { V (2, (" Drain")); - tcdrain (s -> fd); + if (s -> is_capi) + hyla_capi_drain(s); + else + tcdrain (s -> fd); } break; case '<': if (s && (s -> fd != -1)) { V (2, (" Iflush")); - tcflush (s -> fd, TCIFLUSH); + if (s -> is_capi) + hyla_capi_iflush(s); + else + tcflush (s -> fd, TCIFLUSH); } break; case '>': if (s && (s -> fd != -1)) { V (2, (" Oflush")); - tcflush (s -> fd, TCOFLUSH); + if (s -> is_capi) + hyla_capi_oflush(s); + else + tcflush (s -> fd, TCOFLUSH); } break; case 'f': @@ -1110,8 +1142,8 @@ tty_mdrain (void *sp, int msecs) if (msecs < 0) msecs = 0; do { - if ((n = data_ready (s -> fd, & msecs)) > 0) { - while ((m = read (s -> fd, & ch, 1)) == 1) { + if ((n = data_ready (s, s -> fd, & msecs)) > 0) { + while ((m = dev_read (s, s -> fd, & ch, 1)) == 1) { addline (s, ch); V (2, ("%s", mkprint (& ch, 1))); } @@ -1130,3 +1162,368 @@ tty_drain (void *sp, int secs) tty_mdrain (sp, secs * 1000); } /*}}}*/ + +/* + * CAPI + */ + +#include "capiconn.h" + +static capiconn_context *ctx; +static unsigned applid; +static capi_contrinfo cinfo = { 0 , 0, 0 }; +static struct capi_connection *capi_conn = NULL; +static serial *capi_serial; +static int conn_in_progress = 0; +static int capi_data_sent; +static unsigned char capi_data[4096]; +static int capi_data_len = 0; + +static void handle_messages(void) +{ + unsigned char *msg = 0; + struct timeval tv; + tv.tv_sec = 1; + tv.tv_usec = 0; +again: + if (capi20_waitformessage(applid, &tv) == 0) { + if (capi20_get_message(applid, &msg) == 0) + capiconn_inject(applid, msg); + tv.tv_sec = 0; + tv.tv_usec = 0; + goto again; + } +} + +int device_is_capi(char *dev) +{ + if (!dev) + return(0); + + if (strlen(dev) < 4) + return(0); + + if (strncasecmp(dev, "capi", 4)) + return(0); + + return(1); +} + +int hyla_is_capi(void *sp) +{ + serial *s = (serial *) sp; + return(s->is_capi); +} + +static void capi_log(const char *format, ...) +{ + char buffer[2048]; + va_list ap; + + va_start(ap, format); + vsprintf(buffer, format, ap); + strcat(buffer, "\n"); + va_end(ap); + V (3, (buffer)); +} + +static void chargeinfo(capi_connection *cp, unsigned long charge, int inunits) +{ + if (inunits) { + V (3, ("charge in units: %d\n", charge)); + } else { + V (3, ("charge in currency: %d\n", charge)); + } +} + +static void put_message(unsigned appid, unsigned char *msg) +{ + unsigned err; + err = capi20_put_message (appid, msg); + if (err) + V (2, ("capi putmessage %s 0x%x\n", + capi_info2str(err), err)); +} + +static void connected(capi_connection *cp, _cstruct NCPI) +{ + capi_serial -> connected = 1; + conn_in_progress = 0; + V (3, ("CAPI connected\n")); +} + +static void disconnected(capi_connection *cp, + int localdisconnect, + unsigned reason, + unsigned reason_b3) +{ + capi_serial -> connected = 0; + conn_in_progress = 0; + capi_conn = NULL; + V (3, ("CAPI disconnected\n")); +} + +static void handle_capi_sent(capi_connection *con, unsigned char* data) +{ + capi_data_sent = 1; +} + +static void handle_capi_data(capi_connection *con, unsigned char* data, unsigned int len) +{ + int mlen = ((sizeof(capi_data) - capi_data_len) < len) ? + (sizeof(capi_data) - capi_data_len) : len; + + if (mlen) { + memcpy(&capi_data[capi_data_len], data, mlen); + capi_data_len += mlen; + } else { + V (3, ("\ncapi data buffer overflow\n")); + } +} + +capiconn_callbacks callbacks = { + malloc: malloc, + free: free, + + disconnected: disconnected, + incoming: 0, + connected: connected, + received: handle_capi_data, + datasent: handle_capi_sent, + chargeinfo: chargeinfo, + capi_put_message: put_message, + debugmsg: (void (*)(const char *, ...))capi_log, + infomsg: (void (*)(const char *, ...))capi_log, + errmsg: (void (*)(const char *, ...))capi_log +}; + +void *hyla_capi_init(char *dev) +{ + serial *s; + char *p, *p1, *p2; + + if (s = (serial *) malloc (sizeof (serial))) { + if ((capi20_register (1, 8, 2048, &applid)) != 0) { + free (s); + V (3, ("Error CAPI register\n")); + return(NULL); + } + +# ifndef NDEBUG + s -> magic = MAGIC; +# endif /* NDEBUG */ + + s -> device = strdup (dev); + p = skipch(s -> device, '/'); + p1 = p; + p2 = skipch(p, '/'); + if (*p1) + s -> controller = strtol(p1, NULL, 10); + else + s -> controller = 1; + if (*p2) + s -> msn = strdup(p2); + else + s -> msn = NULL; + + V (3, ("CAPI controller=%d MSN=%s\n", s -> controller, s -> msn)); + + if ((ctx = capiconn_getcontext(applid, &callbacks)) != 0) { + if (capiconn_addcontr(ctx, s -> controller, &cinfo) != CAPICONN_OK) { + (void)capiconn_freecontext(ctx); + (void)capi20_release(applid); + if (s -> msn) + free (s -> msn); + if (s -> device) + free (s -> device); + free (s); + s = NULL; + } + s -> fd = capi20_fileno(applid); + s -> is_capi = 1; + s -> connected = 0; + s -> line = NULL; + s -> sep = NULL; + s -> callback = NULL; + s -> data = NULL; + s -> suspend = False; + capi_serial = s; + } else { + (void)capi20_release(applid); + V (3, ("Error CAPI-conn register\n")); + if (s -> msn) + free (s -> msn); + if (s -> device) + free (s -> device); + free (s); + s = NULL; + } + } + return s; +} + +void hyla_capi_disconnect(void *sp) +{ + serial *s = (serial *) sp; + time_t t; + + if (!s || !s -> connected) + return; + + (void)capiconn_disconnect(capi_conn, 0); + + t = time(NULL) + 3; /* 3 seconds */ + do { + handle_messages(); + } while ((time(NULL) < t) && (s -> connected)); +} + +void *hyla_capi_close(void *sp) +{ + serial *s = (serial *) sp; + + MCHK (s); + if (s) { + hyla_capi_disconnect(sp); + + (void)capi20_release(applid); + (void)capiconn_freecontext(ctx); + + if (s -> device) + free (s -> device); + if (s -> msn) + free (s -> msn); + if (s -> sep) + free (s -> sep); + if (s -> line) + sfree (s -> line); + free (s); + capi_serial = NULL; + capi_conn = NULL; + } + return(NULL); +} + +int hyla_capi_connect(void *sp, char *dial, char *number) +{ + serial *s = (serial *) sp; + struct capi_connection *cp; + time_t t; + char num[128]; + + conn_in_progress = 1; + + if (!dial) + dial = ""; + + sprintf(num, "%s%s", dial, number); + + /* X.75 connect */ + cp = capiconn_connect(ctx, + s -> controller, + 2, /* cip */ + num, + s -> msn, + 0, 0, 0, + 0, 0, 0, + 0, 0); + + capi_conn = cp; + + t = time(NULL) + 10; /* 10 seconds */ + do { + handle_messages(); + } while ((time(NULL) < t) && (conn_in_progress)); + + if ((!s -> connected) && (capi_conn)) + (void)capiconn_disconnect(capi_conn, 0); + + return(s -> connected); +} + +int hyla_capi_send(void *sp, char *str, int len) +{ + serial *s = (serial *) sp; + time_t t; + + if (!s || !s -> connected) + return(0); + + capi_data_sent = 0; + + capiconn_send(capi_conn, str, len); + + t = time(NULL) + 5; /* 5 seconds */ + do { + handle_messages(); + } while ((time(NULL) < t) && (!capi_data_sent)); + + return(capi_data_sent); +} + +size_t hyla_capi_read(void *sp, void *buf, size_t count) +{ + serial *s = (serial *) sp; + size_t ret=0; + + if (!capi_data_len) + handle_messages(); + + if (!s || !s -> connected) + return(-1); + + ret = (count < capi_data_len) ? count : capi_data_len; + + memcpy(buf, capi_data, ret); + + capi_data_len -= ret; + + memmove(capi_data, capi_data+ret, capi_data_len); + + return(ret); +} + +void hyla_capi_iflush(void *sp) +{ + capi_data_len = 0; +} + +void hyla_capi_oflush(void *sp) +{ + /* not necessary */ +} + +void hyla_capi_drain(void *sp) +{ + /* not necessary */ +} + +void hyla_capi_sendbreak(void *sp, int duration) +{ + /* ... */ +} + +static int +data_ready (serial *s, int fd, int *msec) +{ + int ret = 0; + struct timeval tv; + fd_set fset; + + if (s -> is_capi) { + if (capi_data_len) + return(1); + if (!s -> connected) + return(0); + } + + FD_ZERO (& fset); + FD_SET (fd, & fset); + tv.tv_sec = *msec / 1000; + tv.tv_usec = (*msec % 1000) * 1000; + if (((ret = select (fd + 1, & fset, NULL, NULL, & tv)) > 0) && FD_ISSET (fd, & fset)) { + *msec = tv.tv_sec * 1000 + (tv.tv_usec / 1000); + return 1; + } + return ret; +} diff --git a/yaps.c b/yaps.c index 2f9b7a9..854449e 100644 --- a/yaps.c +++ b/yaps.c @@ -22,7 +22,7 @@ # include "valid.h" /*}}}*/ /*{{{ general definitions */ -# define VERSION "0.96 (alpha software)" +# define VERSION "0.96.c2 (alpha software, CAPI support V2)" # ifndef LIBDIR # define LIBDIR NULL @@ -389,7 +389,10 @@ do_dial (void *cfg, char *service, char *pagerid, char **modem) s2 = p2; p2 = skipch (p2, ','); V (3, ("Trying to open %s for modem %s\n", s2, sav)); - if (sp = tty_open (s2, lckpre, lckmethod)) { + if (device_is_capi(s2)) { + if (sp = hyla_capi_init(s2)) + break; + } else if (sp = tty_open (s2, lckpre, lckmethod)) { if (tty_setup (sp, True, True, rspeed, bpb, sb, parity) != -1) { if (! (n = setjmp (env))) { dojump = True; @@ -425,7 +428,20 @@ do_dial (void *cfg, char *service, char *pagerid, char **modem) s2 = p2; p2 = skipch (p2, ','); siz = strlen (dial) + strlen (phone) + 64; - if (tmp = malloc (siz + 2)) { + if (hyla_is_capi(sp)) { + if ((!dial) || (!isdigit(*dial))) + dial = ""; + V (3, ("Trying do dial %s%s\n", + (dial) ? dial : "", s2)); + if (hyla_capi_connect(sp, dial, s2)) { + V (1, ("Dial successful\n")); + while (*p2) + ++p2; + } else { + if (!*p2) + sp = hyla_capi_close(sp); + } + } else if (tmp = malloc (siz + 2)) { for (n = 0, m = 0; dial[n]; ) { if (dial[n] == '%') { ++n; @@ -1130,7 +1146,7 @@ remove_invalids (char *str, char *rmv) static message * create_messages (void *cfg, char *service, serv *sbase, char **argv, int argc, char *mfile, int *mcnt, - char *callid, char *sig, Bool trunc, Bool force, date_t *delay, date_t *expire, Bool rds) + char *callid, char *sig, Bool ytrunc, Bool force, date_t *delay, date_t *expire, Bool rds) { char **recv; int rcnt, rsiz; @@ -1430,14 +1446,14 @@ create_messages (void *cfg, char *service, serv *sbase, char **argv, int argc, c } for (n = 0; n < cnt; ++n) { sr = mg[n].service; - if (! trunc) + if (! ytrunc) ttrunc = cfg_bget (cfg, sr, CFG_TRUNCATE, False); else - ttrunc = trunc; + ttrunc = ytrunc; msize = cfg_iget (cfg, sr, CFG_MAXSIZE, 0); msplit = cfg_bget (cfg, sr, CFG_MAYSPLIT, False); if ((msize > 0) && (mg[n].msg -> len > msize)) - if (trunc) + if (ytrunc) mg[n].msg -> len = msize; else if (! msplit) { fprintf (stderr, "Unable to split message for service %s\n", sr); @@ -2168,7 +2184,7 @@ main (int argc, char **argv) serv *sbase, *sprev, *stmp; char *chk; char *service; - Bool trunc; + Bool ytrunc; char *callid; char *sig; Bool force; @@ -2191,7 +2207,7 @@ main (int argc, char **argv) cfgfile = CFGFILE; service = NULL; - trunc = False; + ytrunc = False; callid = NULL; sig = NULL; force = False; @@ -2219,7 +2235,7 @@ main (int argc, char **argv) service = optarg; break; case 't': - trunc = True; + ytrunc = True; break; case 'c': callid = optarg; @@ -2373,7 +2389,7 @@ main (int argc, char **argv) fprintf (stderr, "No services found\n"); return 1; } - if (! (mg = create_messages (cfg, service, sbase, argv + optind, argc - optind, mfile, & mcnt, callid, sig, trunc, force, delay, expire, rds))) + if (! (mg = create_messages (cfg, service, sbase, argv + optind, argc - optind, mfile, & mcnt, callid, sig, ytrunc, force, delay, expire, rds))) return 1; while (sbase) { stmp = sbase; diff --git a/yaps.doc b/yaps.doc index 0a7bf8b..511d248 100644 --- a/yaps.doc +++ b/yaps.doc @@ -401,12 +401,15 @@ Modem section currently supported: device - The filename for the device where the modem is attached to. + The filename for the device where the modem is attached to. If + the CAPI interface should be used, the parameter must be coded + as capi// Note, there must not be a '/' at the + beginning, when using CAPI. lock-prefix This is the pathname to prefix the basename of the modemdevice to create lockfiles. This can be used to enable more than one - application to use the modem. + application to use the modem. Not used by CAPI. lock-method This is a comma seperated list of locking mechanism. Currently @@ -420,23 +423,29 @@ Modem section basename of the device. + timeout= tries to lock the device secs seconds. + Not used by CAPI. + init - This is the init sequence to initialize the modem. + This is the init sequence to initialize the modem. Not used by + CAPI. dial This is the dial sequence to dial a phone number with the modem. An \L in the string will be replaced by the phone - number. + number. If the CAPI interface is used, this entry just defines + a possible needed prefix to dial out, e.g. '0'. timeout This is the default timeout to wait for responses of the modem. + Not used by CAPI. reset - This is the sequence to reset the modem. + This is the sequence to reset the modem. Not used by CAPI. local-init This is used to customize an existing modem entry for different - purpose (e.g. force a specific connect rate, etc.) + purpose (e.g. force a specific connect rate, etc.) Not used by + CAPI. Beside this the section may contain protocol specific entries to adapt the protocol for this service. diff --git a/yaps.html b/yaps.html index 0cfe242..047b3ee 100644 --- a/yaps.html +++ b/yaps.html @@ -388,10 +388,15 @@ supported:
device <text>
The filename for the device where the modem is attached to. + If the CAPI interface should be used, the parameter + must be coded as + capi/<controller>/<MSN> + Note, there must not be a '/' at the beginning, when using CAPI.
lock-prefix <text>
This is the pathname to prefix the basename of the modemdevice to create lockfiles. This can be used to enable more than one application to use the modem. + Not used by CAPI.
lock-method <text>
This is a comma seperated list of locking mechanism. Currently these flags are supported: @@ -406,18 +411,25 @@ flags are supported:
  • timeout=<secs> tries to lock the device secs seconds. + Not used by CAPI.
    init <text>
    This is the init sequence to initialize the modem. + Not used by CAPI.
    dial <text>
    This is the dial sequence to dial a phone number with the modem. An \L in the string will be replaced by the phone number. + If the CAPI interface is used, this entry just defines a + possible needed prefix to dial out, e.g. '0'.
    timeout <num>
    This is the default timeout to wait for responses of the modem. + Not used by CAPI.
    reset <text>
    This is the sequence to reset the modem. + Not used by CAPI.
    local-init <text>
    This is used to customize an existing modem entry for different purpose (e.g. force a specific connect rate, etc.) + Not used by CAPI.
  • Beside this the section may contain protocol specific entries to adapt the diff --git a/yaps.lsm b/yaps.lsm index fbb44d8..8d1f973 100644 --- a/yaps.lsm +++ b/yaps.lsm @@ -1,6 +1,6 @@ Begin3 Title: yaps -Version: 0.96 +Version: 0.96.c2 Entered-date: 14JUN97 Description: This is a standalone program to send messages to paging devices over a modem gateway using well defined protocols.