New version of capiconn.

This commit is contained in:
Carsten Paeth 2004-06-14 11:33:07 +00:00
parent 7f5bd829f3
commit c66178810c
3 changed files with 310 additions and 55 deletions

View File

@ -10,6 +10,9 @@
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
* *
* $Log$ * $Log$
* Revision 1.8 2004/01/16 15:27:13 calle
* remove several warnings.
*
* Revision 1.7 2002/05/03 11:57:49 calle * Revision 1.7 2002/05/03 11:57:49 calle
* Bugfix of Bugfix. * Bugfix of Bugfix.
* *
@ -34,15 +37,11 @@
* Plugin for pppd to support PPP over CAPI2.0. * Plugin for pppd to support PPP over CAPI2.0.
* *
*/ */
#include <stdio.h> /* snprintf */
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "capiconn.h" #include "capiconn.h"
static char *revision = "$Revision$"; static char *revision = "$Revision$";
/* xxxxxxxxxxxxxxxxxx */
static _cmsg cmdcmsg; static _cmsg cmdcmsg;
static _cmsg cmsg; static _cmsg cmsg;
@ -52,6 +51,37 @@ static _cmsg cmsg;
#define CAPI_MAXDATAWINDOW 8 #define CAPI_MAXDATAWINDOW 8
#endif #endif
/* -------- util functions ------------------------------------------- */
static inline int capimsg_addu8(void *m, int off, _cbyte val)
{
((_cbyte *)m)[off] = val;
return off+1;
}
static inline int capimsg_addu16(void *m, int off, _cword val)
{
((_cbyte *)m)[off] = val & 0xff;
((_cbyte *)m)[off+1] = (val >> 8) & 0xff;
return off+2;
}
static inline int capimsg_addu32(void *m, int off, _cdword val)
{
((_cbyte *)m)[off] = val & 0xff;
((_cbyte *)m)[off+1] = (val >> 8) & 0xff;
((_cbyte *)m)[off+2] = (val >> 16) & 0xff;
((_cbyte *)m)[off+3] = (val >> 24) & 0xff;
return off+4;
}
static inline int capimsg_addcstruct(void *m, int off, _cbyte len, _cbyte *val)
{
if (len == 0 || val == 0) return capimsg_addu8(m, off, 0);
memcpy(m+off, val, len);
return off+len;
}
/* -------- type definitions ----------------------------------------- */ /* -------- type definitions ----------------------------------------- */
struct capiconn_context { struct capiconn_context {
@ -105,8 +135,7 @@ struct capi_contr {
unsigned incoming:1, unsigned incoming:1,
disconnecting:1, disconnecting:1,
localdisconnect:1, localdisconnect:1;
callednumbercomplete:1;
_cword disconnectreason; _cword disconnectreason;
_cword disconnectreason_b3; _cword disconnectreason_b3;
@ -131,6 +160,7 @@ struct capi_contr {
} *ackqueue; } *ackqueue;
int ackqueuelen; int ackqueuelen;
} *nccip; } *nccip;
void *userdata;
} *connections; } *connections;
}; };
@ -426,15 +456,16 @@ static int set_conninfo1a(capiconn_context *ctx,
if (callednumber[0] & 0x80) { if (callednumber[0] & 0x80) {
memcpy(p->callednumber+1, callednumber, len); memcpy(p->callednumber+1, callednumber, len);
p->callednumber[0] = len; p->callednumber[0] = len;
p->callednumber[len+1] = 0; p->callednumber[len+1] = 0;
} else { } else {
memcpy(p->callednumber+2, callednumber, len); memcpy(p->callednumber+2, callednumber, len);
p->callednumber[0] = len+1; p->callednumber[0] = len+1;
p->callednumber[1] = 0x81; p->callednumber[1] = 0x81;
p->callednumber[len+2] = 0; p->callednumber[len+2] = 0;
} }
} else { } else {
p->callednumber[0] = 0; p->callednumber[0] = 0;
p->callednumber[1] = 0;
} }
if ((p->callingnumber = (*cb->malloc)(128)) == 0) if ((p->callingnumber = (*cb->malloc)(128)) == 0)
goto fail; goto fail;
@ -443,10 +474,13 @@ static int set_conninfo1a(capiconn_context *ctx,
memcpy(p->callingnumber+3, callingnumber, len); memcpy(p->callingnumber+3, callingnumber, len);
p->callingnumber[0] = len+2; p->callingnumber[0] = len+2;
p->callingnumber[1] = 0x00; p->callingnumber[1] = 0x00;
p->callingnumber[2] = 0x80; p->callingnumber[2] = 0x80;
p->callingnumber[len+3] = 0; p->callingnumber[len+3] = 0;
} else { } else {
p->callingnumber[0] = 0; p->callingnumber[0] = 2;
p->callingnumber[1] = 0x00;
p->callingnumber[2] = 0x80;
p->callingnumber[3] = 0;
} }
return 0; return 0;
fail: fail:
@ -465,21 +499,33 @@ static int set_conninfo1b(capiconn_context *ctx,
p->cipvalue = cipvalue; p->cipvalue = cipvalue;
if ((p->callednumber = (*cb->malloc)(128)) == 0) if ((p->callednumber = (*cb->malloc)(128)) == 0)
goto fail; goto fail;
len = callednumber[0]; if (callednumber && callednumber[0] >= 2) {
memcpy(p->callednumber, callednumber, len+1); len = callednumber[0];
p->callednumber[len+1] = 0; memcpy(p->callednumber, callednumber, len+1);
p->callednumber[len+1] = 0;
if ((p->callingnumber = (*cb->malloc)(128)) == 0) } else {
goto fail; p->callednumber[0] = 1;
len = callingnumber[0]; p->callednumber[1] = 0x81;
memcpy(p->callingnumber, callingnumber, len+1); p->callednumber[2] = 0;
p->callingnumber[len+1] = 0; }
return 0; if ((p->callingnumber = (*cb->malloc)(128)) == 0)
goto fail;
if (callingnumber && callingnumber[0] >= 3) {
len = callingnumber[0];
memcpy(p->callingnumber, callingnumber, len+1);
p->callingnumber[len+1] = 0;
} else {
p->callingnumber[0] = 2;
p->callingnumber[1] = 0x00;
p->callingnumber[2] = 0x80;
p->callingnumber[3] = 0;
}
return 0;
fail: fail:
clr_conninfo1(ctx, p); clr_conninfo1(ctx, p);
return -1; return -1;
} }
static void extend_callednumber(capiconn_context *ctx, capi_conninfo *p, static void extend_callednumber(capiconn_context *ctx, capi_conninfo *p,
@ -570,7 +616,7 @@ static int capi_add_ack(capi_ncci *nccip,
ncci_datahandle_queue *n, **pp; ncci_datahandle_queue *n, **pp;
if (nccip->ackqueuelen >= CAPI_MAXDATAWINDOW) if (nccip->ackqueuelen >= CAPI_MAXDATAWINDOW)
return 0; return 1;
n = (ncci_datahandle_queue *) n = (ncci_datahandle_queue *)
(*cb->malloc)(sizeof(ncci_datahandle_queue)); (*cb->malloc)(sizeof(ncci_datahandle_queue));
if (!n) { if (!n) {
@ -603,6 +649,7 @@ static unsigned char *capi_del_ack(capi_ncci *nccip, _cword datahandle)
return data; return data;
} }
} }
(*cb->errmsg)("datahandle %u not found", datahandle);
return 0; return 0;
} }
@ -948,6 +995,8 @@ static void handle_controller(capiconn_context *ctx, _cmsg * cmsg)
cmsg->adr.adrController); cmsg->adr.adrController);
} }
static unsigned char SendingComplete[5] = { 4, 1, 0, 0, 0 };
static void check_incoming_complete(capi_connection *plcip) static void check_incoming_complete(capi_connection *plcip)
{ {
capi_contr *card = plcip->contr; capi_contr *card = plcip->contr;
@ -971,12 +1020,6 @@ static void check_incoming_complete(capi_connection *plcip)
return; return;
} }
} }
if (plcip->callednumbercomplete)
return;
plcip->callednumbercomplete = 1;
if (*cb->incoming) if (*cb->incoming)
(*cb->incoming)(plcip, (*cb->incoming)(plcip,
plcip->contr->contrnr, plcip->contr->contrnr,
@ -993,7 +1036,8 @@ static void check_incoming_complete(capi_connection *plcip)
0, /* BChannelinformation */ 0, /* BChannelinformation */
0, /* Keypadfacility */ 0, /* Keypadfacility */
0, /* Useruserdata */ 0, /* Useruserdata */
0 /* Facilitydataarray */ 0, /* Facilitydataarray */
SendingComplete
); );
plcip->msgid = cmsg.Messagenumber; plcip->msgid = cmsg.Messagenumber;
send_message(card, &cmsg); send_message(card, &cmsg);
@ -1023,8 +1067,8 @@ static void handle_incoming_call(capi_contr * card, _cmsg * cmsg)
(*cb->debugmsg)("incoming call contr=%d cip=%d %s -> %s", (*cb->debugmsg)("incoming call contr=%d cip=%d %s -> %s",
card->contrnr, card->contrnr,
cmsg->CIPValue, cmsg->CIPValue,
plcip->conninfo.callingnumber + 3, plcip->conninfo.callingnumber+2,
plcip->conninfo.callednumber + 2); plcip->conninfo.callednumber+3);
if (cb->incoming == 0) if (cb->incoming == 0)
goto ignore; goto ignore;
@ -1120,24 +1164,17 @@ static int handle_callednumber_info(capi_connection *plcip, _cmsg *cmsg)
return 0; return 0;
} }
static int handle_cause_info(capi_connection *plcip, _cmsg *cmsg) static int handle_dtmf_info(capi_connection *plcip, _cmsg *cmsg)
{ {
capiconn_context *ctx = plcip->ctx; capiconn_context *ctx = plcip->ctx;
capiconn_callbacks *cb = ctx->cb; capiconn_callbacks *cb = ctx->cb;
unsigned char *p = cmsg->InfoElement; if (cmsg->InfoNumber == 0x002c) {
if (cmsg->InfoNumber == 0x0008) { if (cb->dtmf_received)
char buf[128]; (*cb->dtmf_received)(plcip,
char *s, *end; cmsg->InfoElement+1,
int i; cmsg->InfoElement[0]);
s = buf; end = s + sizeof(buf)-1; return 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; return 0;
} }
@ -1262,7 +1299,7 @@ static void handle_plci(capiconn_context *ctx, _cmsg * cmsg)
} else if (handle_callednumber_info(plcip, cmsg)) { } else if (handle_callednumber_info(plcip, cmsg)) {
capi_cmsg_answer(cmsg); capi_cmsg_answer(cmsg);
send_message(card, cmsg); send_message(card, cmsg);
} else if (handle_cause_info(plcip, cmsg)) { } else if (handle_dtmf_info(plcip, cmsg)) {
capi_cmsg_answer(cmsg); capi_cmsg_answer(cmsg);
send_message(card, cmsg); send_message(card, cmsg);
} else { } else {
@ -1276,6 +1313,16 @@ static void handle_plci(capiconn_context *ctx, _cmsg * cmsg)
case CAPI_SELECT_B_PROTOCOL_CONF: /* plci */ case CAPI_SELECT_B_PROTOCOL_CONF: /* plci */
goto ignored; goto ignored;
case CAPI_FACILITY_IND: /* Controller/plci/ncci */ case CAPI_FACILITY_IND: /* Controller/plci/ncci */
if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI)))
goto notfound;
if (cmsg->FacilitySelector == 1) { /* DTMF */
(*cb->dtmf_received)(plcip,
cmsg->FacilityIndicationParameter+1,
cmsg->FacilityIndicationParameter[0]);
capi_cmsg_answer(cmsg);
send_message(card, cmsg);
break;
}
goto ignored; goto ignored;
case CAPI_FACILITY_CONF: /* Controller/plci/ncci */ case CAPI_FACILITY_CONF: /* Controller/plci/ncci */
goto ignored; goto ignored;
@ -1448,8 +1495,40 @@ static void handle_ncci(capiconn_context *ctx, _cmsg * cmsg)
case CAPI_FACILITY_IND: /* Controller/plci/ncci */ case CAPI_FACILITY_IND: /* Controller/plci/ncci */
goto ignored; goto ignored;
case CAPI_FACILITY_CONF: /* Controller/plci/ncci */ case CAPI_FACILITY_CONF: /* Controller/plci/ncci */
goto ignored; 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);
} else if ( cmsg->FacilitySelector == 1
&& cmsg->FacilityConfirmationParameter[0] > 0
&& cmsg->FacilityConfirmationParameter[1]) {
switch (cmsg->FacilityConfirmationParameter[1]) {
case 0:
break;
case 1:
(*cb->infomsg)("%s incorrect DTMF digit for ncci 0x%x",
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->adr.adrNCCI);
break;
case 2:
(*cb->infomsg)("%s Unknown DTMF request for ncci 0x%x",
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->adr.adrNCCI);
break;
default:
(*cb->infomsg)("%s DTMF errcode %d for ncci 0x%x",
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->FacilityConfirmationParameter[1],
cmsg->adr.adrNCCI);
break;
}
}
break;
default: default:
(*cb->errmsg)("capidrv-%d: got %s for ncci 0x%x ???", (*cb->errmsg)("capidrv-%d: got %s for ncci 0x%x ???",
@ -1771,6 +1850,53 @@ int capiconn_disconnect(capi_connection *plcip, _cstruct ncpi)
static _cmsg sendcmsg; static _cmsg sendcmsg;
// #define TX_STATS
#ifdef TX_STATS
static FILE *dump_open(void)
{
struct stat sb;
if (0 == stat("/var/tmp/voipd.txt", &sb)
&& sb.st_size > 60000) {
unlink("/var/tmp/voipd.txt");
}
FILE *fp = fopen("/var/tmp/voipd.txt", "a");
return fp;
}
static void ncci_dump(capi_ncci *nccip, char *header)
{
FILE *fp = dump_open();
if (fp) {
time_t t = time(0);
fprintf(fp, "\n%02u:%02u:%02u %s\n", t / (60*60), (t / 60) % 60, t % 60, header);
// txstat_dump(&nccip->txstats, fp);
// fprintf(fp, "Tx Packet Time Distribution:\n");
// timedist_dump(&nccip->txpackettdist, fp);
// fprintf(fp, "Tx DATA B3 CONF Time Distribution:\n");
// timedist_dump(&nccip->txconftdist, fp);
// fprintf(fp, "Tx Underrun Time Distribution:\n");
// timedist_dump(&nccip->txUnderruntdist, fp);
rtp_stats_display_fp(ortp_get_global_stats(), "RTP Global", fp);
// fprintf(fp, "*** Mediastraemer Fifos:\n");
// ms_fifo_dump(fp);
fclose(fp);
}
}
#endif
int capiconn_send(capi_connection *plcip, int capiconn_send(capi_connection *plcip,
unsigned char *data, unsigned char *data,
unsigned len) unsigned len)
@ -1786,6 +1912,25 @@ int capiconn_send(capi_connection *plcip,
if (!nccip || nccip->state != ST_NCCI_ACTIVE) if (!nccip || nccip->state != ST_NCCI_ACTIVE)
return CAPICONN_WRONG_STATE; return CAPICONN_WRONG_STATE;
#ifdef TX_STATS
{
struct timeval tv;
struct timezone tz;
unsigned tstamp;
static unsigned next_dump_tstamp = 0;
tstamp = gettimeofday(&tv, &tz);
tstamp = tv.tv_sec * 1000 + (tv.tv_usec/1000);
// timedist_event(&nccip->txpackettdist, tstamp);
if (tstamp >= next_dump_tstamp) {
next_dump_tstamp = tstamp + 120*1000;
ncci_dump(nccip, "ncci periodic");
}
}
#endif // TX_STATS
datahandle = nccip->datahandle; datahandle = nccip->datahandle;
capi_fill_DATA_B3_REQ(&sendcmsg, ctx->appid, card->msgid++, capi_fill_DATA_B3_REQ(&sendcmsg, ctx->appid, card->msgid++,
nccip->ncci, /* adr */ nccip->ncci, /* adr */
@ -1795,16 +1940,91 @@ int capiconn_send(capi_connection *plcip,
0 /* Flags */ 0 /* Flags */
); );
if (capi_add_ack(nccip, datahandle, data) < 0) if (capi_add_ack(nccip, datahandle, data) != 0)
return CAPICONN_NOT_SENT; return CAPICONN_NOT_SENT;
capi_cmsg2message(&sendcmsg, sendcmsg.buf); capi_cmsg2message(&sendcmsg, sendcmsg.buf);
(*cb->capi_put_message) (ctx->appid, sendcmsg.buf); if((*cb->capi_put_message) (ctx->appid, sendcmsg.buf) < 0) {
capi_del_ack(nccip, datahandle);
return CAPICONN_NOT_SENT;
}
nccip->datahandle++; nccip->datahandle++;
ctx->nsentdatapkt++; ctx->nsentdatapkt++;
return CAPICONN_OK; return CAPICONN_OK;
} }
int capiconn_dtmf_setstate(capi_connection *plcip, int on)
{
_cbyte fparam[32];
int off;
capi_contr *card = plcip->contr;
capiconn_context *ctx = card->ctx;
capiconn_callbacks *cb = ctx->cb;
capi_ncci *nccip;
nccip = plcip->nccip;
if (!nccip || nccip->state != ST_NCCI_ACTIVE)
return CAPICONN_WRONG_STATE;
if (cb->dtmf_received == 0)
return CAPICONN_NOT_SUPPORTED;
capi_cmsg_header(&sendcmsg, ctx->appid,
CAPI_FACILITY, CAPI_REQ, card->msgid++, nccip->ncci);
sendcmsg.FacilitySelector = 1;
off = 1;
off = capimsg_addu16(fparam, off, on ? 1 : 2); /* Function */
off = capimsg_addu16(fparam, off, 40); /* Tone-duration */
off = capimsg_addu16(fparam, off, 40); /* Gap-dureation */
off = capimsg_addcstruct(fparam, off, 0, 0); /* DTMF-digits */
off = capimsg_addcstruct(fparam, off, 0, 0); /* DTMF-characteristics */
fparam[0] = off;
sendcmsg.FacilityRequestParameter = fparam;
capi_cmsg2message(&sendcmsg, sendcmsg.buf);
(*cb->capi_put_message) (ctx->appid, sendcmsg.buf);
return CAPICONN_OK;
}
int capiconn_dtmf_send(capi_connection *plcip, char *digits)
{
_cbyte fparam[256];
int off;
capi_contr *card = plcip->contr;
capiconn_context *ctx = card->ctx;
capiconn_callbacks *cb = ctx->cb;
capi_ncci *nccip;
nccip = plcip->nccip;
if (!nccip || nccip->state != ST_NCCI_ACTIVE)
return CAPICONN_WRONG_STATE;
capi_cmsg_header(&sendcmsg, ctx->appid,
CAPI_FACILITY, CAPI_REQ, card->msgid++, nccip->ncci);
sendcmsg.FacilitySelector = 1;
off = 1;
off = capimsg_addu16(fparam, off, 4); /* Function */
off = capimsg_addu16(fparam, off, 40); /* Tone-duration */
off = capimsg_addu16(fparam, off, 40); /* Gap-dureation */
off = capimsg_addcstruct(fparam, off, strlen(digits), digits);
off = capimsg_addcstruct(fparam, off, 0, 0); /* DTMF-characteristics */
fparam[0] = off;
sendcmsg.FacilityRequestParameter = fparam;
capi_cmsg2message(&sendcmsg, sendcmsg.buf);
(*cb->capi_put_message) (ctx->appid, sendcmsg.buf);
return CAPICONN_OK;
}
void capiconn_set_userdata(capi_connection *plcip, void *userdata)
{
plcip->userdata = userdata;
}
void *capiconn_get_userdata(capi_connection *plcip)
{
return plcip->userdata;
}
/* -------- listen handling ------------------------------------------ */ /* -------- listen handling ------------------------------------------ */
@ -1813,7 +2033,6 @@ static void send_listen(capi_contr *card)
capiconn_context *ctx = card->ctx; capiconn_context *ctx = card->ctx;
card->infomask = 0; card->infomask = 0;
card->infomask |= (1<<0); /* cause information */
card->infomask |= (1<<2); /* Display */ card->infomask |= (1<<2); /* Display */
card->infomask |= (1<<6); /* Charge Info */ card->infomask |= (1<<6); /* Charge Info */
if (card->ddilen) card->infomask |= (1<<7); /* Called Party Number */ if (card->ddilen) card->infomask |= (1<<7); /* Called Party Number */

View File

@ -10,6 +10,11 @@
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
* *
* $Log$ * $Log$
* Revision 1.3 2001/01/25 14:45:41 calle
* - listen always (for info messages)
* - show versions on startup
* - wait for capifs if needed
*
* Revision 1.2 2000/10/25 10:01:47 calle * Revision 1.2 2000/10/25 10:01:47 calle
* (c) in all files * (c) in all files
* *
@ -17,7 +22,6 @@
* Plugin for pppd to support PPP over CAPI2.0. * Plugin for pppd to support PPP over CAPI2.0.
* *
*/ */
#ifndef __CAPICONN_H__ #ifndef __CAPICONN_H__
#define __CAPICONN_H__ #define __CAPICONN_H__
@ -60,6 +64,7 @@
#define CAPICONN_OK 0 #define CAPICONN_OK 0
#define CAPICONN_NO_CONTROLLER -1 #define CAPICONN_NO_CONTROLLER -1
#define CAPICONN_NO_MEMORY -2 #define CAPICONN_NO_MEMORY -2
#define CAPICONN_NOT_SUPPORTED -3
#define CAPICONN_WRONG_STATE 1 #define CAPICONN_WRONG_STATE 1
#define CAPICONN_NOT_SENT 2 #define CAPICONN_NOT_SENT 2
#define CAPICONN_ALREADY_DISCONNECTING 3 #define CAPICONN_ALREADY_DISCONNECTING 3
@ -253,10 +258,18 @@ struct capiconn_callbacks
void (*chargeinfo)(capi_connection *, void (*chargeinfo)(capi_connection *,
unsigned long charge, unsigned long charge,
int inunits); int inunits);
/*
* DTMF received
*/
void (*dtmf_received)(capi_connection *,
unsigned char *data,
unsigned datalen);
/* /*
* capi functions * capi functions
*/ */
void (*capi_put_message) (unsigned appid, unsigned char *msg); int (*capi_put_message) (unsigned appid, unsigned char *msg);
/* /*
* message functions * message functions
@ -409,6 +422,12 @@ typedef struct capi_conninfo capi_conninfo;
*/ */
capi_conninfo *capiconn_getinfo(capi_connection *p); capi_conninfo *capiconn_getinfo(capi_connection *p);
/*
* userdata per connection
*/
void capiconn_set_userdata(capi_connection *plcip, void *userdata);
void *capiconn_get_userdata(capi_connection *plcip);
/* /*
* returncodes: * returncodes:
* CAPICONN_OK - Listen request sent * CAPICONN_OK - Listen request sent
@ -427,4 +446,20 @@ int capiconn_listen(capiconn_context *ctx,
*/ */
int capiconn_listenstate(capiconn_context *ctx, unsigned contr); int capiconn_listenstate(capiconn_context *ctx, unsigned contr);
/*
* returncode:
* CAPICONN_OK - request sent to CAPI
* CAPICONN_NOT_SUPPORTED - DTMF not supported
* CAPICONN_WRONG_STATE - Connection is not connected
*/
int capiconn_dtmf_setstate(capi_connection *, int on);
/*
* returncode:
* CAPICONN_OK - request sent to CAPI
* CAPICONN_NOT_SUPPORTED - DTMF not supported
* CAPICONN_WRONG_STATE - Connection is not connected
*/
int capiconn_dtmf_send(capi_connection *, char *digits);
#endif /* __CAPICONN_H__ */ #endif /* __CAPICONN_H__ */

View File

@ -1475,6 +1475,7 @@ capiconn_callbacks callbacks = {
received: 0, received: 0,
datasent: 0, datasent: 0,
chargeinfo: chargeinfo, chargeinfo: chargeinfo,
dtmf_received: 0,
capi_put_message: put_message, capi_put_message: put_message,