isdn4k-utils/ant-phone/src/isdn_capi.c

465 lines
10 KiB
C

/* $Id$
*
* This file is part of ANT (Ant is Not a Telephone)
*
* Authors:
* - Martin Bachem, m.bachem@gmx.de
*
* heavy capitalized on:
* + isdn4k-utils/[pppdcapiplugin,capiiinfo]
* + capiconn samples capiintest/capiouttest
*
* 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.
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */
#ifdef HAVE_LIBCAPI20
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <malloc.h>
#include <capi20.h>
#include <linux/capi.h>
#include <unistd.h>
#include <stddef.h>
#include "globals.h"
#include "callerid.h"
#include "session.h"
#include "isdn_capi.h"
#include "capiconn.h"
static char msgbuf[4096];
/* callback for capiconn, TODO: don't use global session */
static void
ant_capi_incoming(capi_connection * cp, unsigned contr,
unsigned cipvalue, char *callednumber,
char *callingnumber)
{
switch (session.state) {
case STATE_READY:
/* caller id update */
session.ring_time = time(NULL);
/* save callee's number */
free(session.to);
session.to = strdup(callednumber);
cid_add_line(&session, CALL_IN, NULL, session.to);
/* save caller's number */
free(session.from);
session.from = strdup(callingnumber);
cid_set_from(&session, session.from);
if (session_set_state(&session, STATE_RINGING))
session_set_state(&session,
STATE_RINGING_QUIET);
session.cipvalue = cipvalue;
session.cp = cp;
break;
default:
if (debug)
printf
("ERROR: incoming call at state %i\n",
session.state);
}
}
static char *
ant_capi_conninfo(capi_connection * p)
{
static char buf[1024];
capi_conninfo *cp = capiconn_getinfo(p);
char *callingnumber = "";
char *callednumber = "";
if (cp->callingnumber && cp->callingnumber[0] > 2)
callingnumber = cp->callingnumber + 3;
if (cp->callednumber && cp->callednumber[0] > 1)
callednumber = cp->callednumber + 2;
if (debug) {
snprintf(buf, sizeof(buf),
"\"%s\" -> \"%s\" %s (pcli=0x%x/ncci=0x%x)",
callingnumber, callednumber,
cp->isincoming ? "incoming" : "outgoing",
cp->plci, cp->ncci);
} else {
snprintf(buf, sizeof(buf),
"\"%s\" -> \"%s\" %s",
callingnumber, callednumber,
cp->isincoming ? "incoming" : "outgoing");
}
buf[sizeof(buf) - 1] = 0;
return buf;
}
#define OUTSIZE 128
static int senddata(capi_connection *cp)
{
char *data;
data = (char *)malloc(OUTSIZE);
if (!data) {
printf ("ERROR: malloc failed\n");
return -1;
}
if (capiconn_send(cp, data, OUTSIZE) == CAPICONN_OK) {
return 0;
}
free(data);
return -1;
}
/* callback for capiconn, TODO: don't use global session */
static void
ant_capi_connected(capi_connection * cp, _cstruct NCPI)
{
capi_conninfo *p = capiconn_getinfo(cp);
printf ("connected(%s) NCPIlen=%d\n", ant_capi_conninfo(cp), NCPI[0]);
session.vcon_time = time(NULL); /* for caller id monitor */
cid_set_date(&session, session.vcon_time);
session_set_state(&session, STATE_CONVERSATION);
if (p->b1proto == 1 && p->b2proto == 1 && p->b3proto == 0) {
printf ("enabling DTMF\n");
(void)capiconn_dtmf_setstate(cp, 1);
}
// senddata(cp);
}
/* callback for capiconn, TODO: don't use global session */
static void
ant_capi_disconnected(capi_connection * cp, int localdisconnect,
unsigned reason, unsigned reason_b3)
{
switch (session.state) {
case STATE_RINGING:
case STATE_RINGING_QUIET:
/* caller giving up */
session_set_state(&session, STATE_READY);
cid_set_duration(&session, _("(RUNG)"));
cid_mark_row(&session, session.cid_num - 1, TRUE);
break;
default:
if (debug)
printf
("ERROR: got disconnect at state %i\n",
session.state);
}
}
/* callback for GTK widget */
int
ant_capi_pickup(session_t * session)
{
switch (session->state) {
case STATE_RINGING:
case STATE_RINGING_QUIET:
switch (session->cipvalue) {
case 1: /* Speech */
case 4: /* 3.1 KHz audio */
case 5: /* 7 KHz audio */
case 16: /* Telephony */
case 26: /* 7kHz telephony */
(void)capiconn_accept(session->cp, 1, 1, 0, 0, 0, 0, 0);
break;
case 2: /* unrestricted digital information */
case 3: /* restricted digital infomation */
/* HDLC: 0,1,0 X75: 0,0,0 X75+V42Bis: 0,8,0 */
/* x25overx75: 0,0,2 */
(void)capiconn_accept(session->cp, 0, 1, 0, 0, 0, 0, 0);
/*(void)capiconn_accept(cp, 0, 0, 2, 0, 0, 0, 0);*/
break;
case 17: /* Group 2/3 facsimile */
(void)capiconn_accept(session->cp, 4, 4, 4, 0, 0, 0, 0);
break;
default:
(void)capiconn_ignore(session->cp);
break;
}
break;
default:
if (debug)
printf
("ERROR: got disconnect at state %i\n",
session->state);
}
}
/* callback for GTK widget */
int
ant_capi_hangup(session_t * session)
{
switch (session->state) {
/* abort dialing */
case STATE_DIALING:
break;
/* reject call */
case STATE_RINGING:
case STATE_RINGING_QUIET:
session_set_state(session, STATE_READY);
cid_set_duration(session, _("(REJECTED)"));
(void) capiconn_reject(session->cp);
break;
/* hang up (while b-channel is open) */
case STATE_CONVERSATION:
(void)capiconn_disconnect(session->cp, 0);
session_deinit_conversation(session, 1); /* 1 == we hang up ourselves ;) */
session_set_state(session, STATE_READY);
cid_set_duration(session, NULL);
break;
default:
if (debug)
printf
("ERROR: got disconnect at state %i\n",
session->state);
}
}
static void
ant_capi_chargeinfo(capi_connection * cp, unsigned long charge,
int inunits)
{
if (debug) {
if (inunits) {
printf("%s: charge in units: %ul",
ant_capi_conninfo(cp), charge);
} else {
printf("%s: charge in currency: %ul",
ant_capi_conninfo(cp), charge);
}
}
}
static int
ant_capi_put_message(unsigned appid, unsigned char *msg)
{
unsigned err;
err = capi20_put_message(appid, msg);
if (err && debug) {
fprintf(stderr, "putmessage(appid=%d) - %s 0x%x", appid,
capi_info2str(err), err);
}
return 0;
}
static void
ant_capi_msg(const char *prefix, const char *format, va_list args)
{
char *s = msgbuf;
char *e = msgbuf + sizeof(msgbuf);
(void) snprintf(s, e - s, "%s", prefix);
s += strlen(s);
(void) vsnprintf(s, e - s, format, args);
s += strlen(s);
printf("%s\n", msgbuf);
}
static void
ant_capi_err(const char *format, ...)
{
va_list args;
if (debug) {
va_start(args, format);
ant_capi_msg("CAPI Error: ", format, args);
va_end(args);
}
}
static void
ant_capi_dbg(const char *format, ...)
{
va_list args;
if (debug > 1) {
va_start(args, format);
ant_capi_msg("CAPI Debug: ", format, args);
va_end(args);
}
}
static void
ant_capi_info(const char *format, ...)
{
va_list args;
if (debug > 2) {
va_start(args, format);
ant_capi_msg("CAPI Info: ", format, args);
va_end(args);
}
}
static char *conninfo(capi_connection *p)
{
static char buf[1024];
capi_conninfo *cp = capiconn_getinfo(p);
snprintf(buf, sizeof(buf),
"appl=%d plci=0x%x ncci=0x%x %s",
cp->appid,
cp->plci,
cp->ncci,
cp->isincoming ? "incoming" : "outgoing"
);
return buf;
}
static void
ant_capi_rx(capi_connection * cp, unsigned char *data, unsigned datalen)
{
printf ("received(%s): len=%u\n", conninfo(cp), datalen);
}
static void
ant_capi_tx(capi_connection * cp, unsigned char *buf)
{
printf ("sent(%s): %p\n", conninfo(cp), buf);
free(buf);
senddata(cp);
// printf ("sent(%s)\n", conninfo(cp));
}
static void
ant_capi_dtmf_rx(capi_connection * cp,
unsigned char *data, unsigned datalen)
{
ant_capi_info("dtmf_reveived(%s): %ld \"%-*.*s\"",
conninfo(cp), datalen, (int) datalen, (int) datalen, data);
}
capiconn_callbacks ant_capi_callbacks = {
malloc: malloc,
free: free,
disconnected: ant_capi_disconnected,
incoming: ant_capi_incoming,
connected: ant_capi_connected,
chargeinfo: ant_capi_chargeinfo,
/*
received: ant_capi_rx,
datasent: ant_capi_tx,
dtmf_received: ant_capi_dtmf_rx,
*/
received: 0,
datasent: 0,
dtmf_received: 0,
capi_put_message: ant_capi_put_message,
debugmsg: (void (*)(const char *,...)) ant_capi_dbg,
infomsg: (void (*)(const char *,...)) ant_capi_info,
errmsg: (void (*)(const char *,...)) ant_capi_err
};
int
ant_capi_init(session_t * session)
{
int serrno, err;
if (CAPI20_ISINSTALLED() != CapiNoError) {
fprintf(stderr, "CAPI not installed - %s (%d)\n",
strerror(errno), errno);
return -2;
}
if ((err = capi20_register(2, 8, 2048, &session->applid)) != 0) {
serrno = errno;
fprintf(stderr,
"CAPI_REGISTER failed - %s (0x%04x) [%s (%d)]\n",
capi_info2str(err), err, strerror(serrno), errno);
return (-3);
}
if (capi20ext_set_flags(session->applid, 1) < 0) {
serrno = errno;
(void) capi20_release(session->applid);
fprintf(stderr,
"CAPI: failed to set highjacking mode - %s (%d)\n",
strerror(serrno), serrno);
return (-4);
}
if ((session->ctx =
capiconn_getcontext(session->applid,
&ant_capi_callbacks)) == 0) {
(void) capi20_release(session->applid);
fprintf(stderr, "CAPI: get_context failed\n");
return (-5);
}
session->cinfo.ddi = 0;
session->cinfo.ndigits = 0;
if (capiconn_addcontr
(session->ctx, session->capi_contr,
&session->cinfo) != CAPICONN_OK) {
(void) capiconn_freecontext(session->ctx);
(void) capi20_release(session->applid);
fprintf(stderr, "CAPI: add controller %d failed",
session->capi_contr);
return (-1);
}
return (0);
}
void
ant_capi_listen(session_t * session)
{
/*
call
conn = capiconn_connect(ctx,
session->opt_contr, // contr
1, // cipvalue
number_to_call,
session->msn,
1, 1, 0,
0, 0, 0, 0, 0);
*/
(void) capiconn_listen(session->ctx, session->capi_contr,
0x1FFF03FF, 0);
} void
ant_capi_free(session_t * session)
{
(void) capiconn_freecontext(session->ctx);
(void) capi20_release(session->applid);
}
/* attention: get called periodically by timer defined TIMER_DELAY in gtk.c,
* so be quick ;)
*/
void
ant_capi_messages(session_t * session)
{
unsigned char *msg = 0;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100;
if (capi20_waitformessage(session->applid, &tv) == 0) {
if (capi20_get_message(session->applid, &msg) == 0)
capiconn_inject(session->applid, msg);
}
}
#endif /* HAVE_LIBCAPI20 */