isdn4k-utils/pppdcapiplugin/capiplugin.c

285 lines
6.1 KiB
C

/*
* capiconnect.c - pppd plugin to implement a `minconnect' option.
*
* Copyright 2000 Carsten Paeth (calle@calle.de)
*
* 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.
*/
#include <stddef.h>
#include <time.h>
#include "pppd.h"
#include "capiconn.h"
#include <malloc.h>
#include <string.h>
#include <dlfcn.h>
static capiconn_context *ctx;
static capi_connection *conn = 0;
static int isconnected = 0;
static unsigned applid;
static int opt_contr = 1;
static char *opt_numberprefix = 0;
static char *opt_number = 0;
static char *opt_msn = 0;
static char *opt_proto = 0;
static option_t my_options[] = {
{
"controller", o_int, &opt_contr,
"capi controller"
},
{
"number", o_string, &opt_number,
"number to call"
},
{
"numberprefix", o_string, &opt_numberprefix,
"prefix for number"
},
{
"msn", o_string, &opt_msn,
"number to call from"
},
{
"protocol", o_string, &opt_proto,
"protocol x75 or hdlc"
},
{ NULL }
};
/* -------------------------------------------------------------------- */
static void dodisconnect(void)
{
if (!conn)
return;
(void)capiconn_disconnect(conn, 0);
while (conn) {
unsigned char *msg = 0;
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
if (capi20_waitformessage(applid, &tv) == 0) {
if (capi20_get_message (applid, &msg) == 0)
capiconn_inject(applid, msg);
}
}
}
static void makeconnection(void)
{
char number[256];
if (opt_number == 0) {
fatal("capiplugin: no number");
return;
}
snprintf(number, sizeof(number), "%s%s",
opt_numberprefix ? opt_numberprefix : "",
opt_number);
if (opt_proto == 0 || strcasecmp(opt_proto, "hdlc") == 0) {
conn = capiconn_connect(ctx,
opt_contr, /* contr */
2, /* cipvalue */
number,
opt_msn,
0, 1, 0,
0, 0, 0, 0, 0);
} else if (strcasecmp(opt_proto, "x75") == 0) {
conn = capiconn_connect(ctx,
opt_contr, /* contr */
2, /* cipvalue */
number,
opt_msn,
0, 0, 0,
0, 0, 0, 0, 0);
} else if (strcasecmp(opt_proto, "modem") == 0) {
conn = capiconn_connect(ctx,
opt_contr, /* contr */
1, /* cipvalue */
number,
opt_msn,
8, 1, 0,
0, 0, 0, 0, 0);
} else {
fatal("capiplugin: unknown protocol \"%s\"", opt_proto);
return;
}
while (!isconnected && conn) {
unsigned char *msg = 0;
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
if (capi20_waitformessage(applid, &tv) == 0) {
if (capi20_get_message (applid, &msg) == 0)
capiconn_inject(applid, msg);
}
}
if (!conn)
fatal("capiplugin: couldn't make connection");
}
static void init_capiconn(void)
{
static capi_contrinfo cinfo = { 0 , 0, 0 };
static int init = 0;
if (init)
return;
init = 1;
if (capiconn_addcontr(ctx, opt_contr, &cinfo) != CAPICONN_OK) {
(void)capiconn_freecontext(ctx);
(void)capi20_release(applid);
fatal("capiplugin: add controller %d failed", opt_contr);
return;
}
}
static int capi_new_phase_hook(int phase)
{
switch (phase) {
case PHASE_DEAD:
info("capiplugin: phase dead");
dodisconnect();
break;
case PHASE_INITIALIZE:
info("capiplugin: phase initialize");
break;
case PHASE_SERIALCONN:
info("capiplugin: phase serialconn");
init_capiconn();
makeconnection();
break;
case PHASE_DORMANT:
info("capiplugin: phase dormant");
break;
case PHASE_ESTABLISH:
info("capiplugin: phase establish");
break;
case PHASE_AUTHENTICATE:
info("capiplugin: phase authenticate");
break;
case PHASE_CALLBACK:
info("capiplugin: phase callback");
break;
case PHASE_NETWORK:
info("capiplugin: phase network");
break;
case PHASE_RUNNING:
info("capiplugin: phase running");
break;
case PHASE_TERMINATE:
info("capiplugin: phase terminate");
break;
case PHASE_DISCONNECT:
info("capiplugin: phase disconnect");
break;
case PHASE_HOLDOFF:
info("capiplugin: phase holdoff");
break;
}
return 0;
}
/* -------------------------------------------------------------------- */
static char *conninfo(capi_connection *p)
{
static char buf[1024];
capi_conninfo *cp = capiconn_getinfo(p);
snprintf(buf, sizeof(buf),
"plci=0x%x ncci=0x%x %s",
cp->plci,
cp->ncci,
cp->isincoming ? "incoming" : "outgoing"
);
return buf;
}
static void disconnected(capi_connection *cp,
int localdisconnect,
unsigned reason,
unsigned reason_b3)
{
info("capiplugin: disconnected(%s): %s: 0x%04x (0x%04x) - %s",
conninfo(cp),
localdisconnect ? "local" : "remote",
reason, reason_b3, capi_info2str(reason));
conn = 0;
isconnected = 0;
}
static void connected(capi_connection *cp, _cstruct NCPI)
{
capi_conninfo *p = capiconn_getinfo(cp);
char buf[PATH_MAX];
char *tty;
tty = capi20ext_get_tty_devname(p->appid, p->ncci, buf, sizeof(buf));
info("capiplugin: connected(%s) %s", conninfo(cp), tty);
strcpy(devnam, tty);
isconnected = 1;
}
void put_message(unsigned appid, unsigned char *msg)
{
unsigned err;
err = capi20_put_message (appid, msg);
if (err)
fatal("capiplugin: putmessage(appid=%u) = 0x%x", appid, err);
}
/* -------------------------------------------------------------------- */
capiconn_callbacks callbacks = {
malloc: malloc,
free: free,
disconnected: disconnected,
incoming: 0,
connected: connected,
received: 0,
datasent: 0,
chargeinfo: 0,
capi_put_message: put_message,
debugmsg: dbglog,
infomsg: info,
errmsg: error
};
void plugin_init(void)
{
int err;
info("plugin_init: capiconnect");
add_options(my_options);
if ((err = capi20_register (30, 8, 2048, &applid)) != 0) {
fatal("capiplugin: CAPI_REGISTER failed - 0x%04x", err);
return;
}
if (capi20ext_set_flags(applid, 1) < 0) {
(void)capi20_release(applid);
fatal("capiplugin: failed to set highjacking mode");
return;
}
if ((ctx = capiconn_getcontext(applid, &callbacks)) == 0) {
(void)capi20_release(applid);
fatal("capiplugin: get_context failed");
return;
}
new_phase_hook = capi_new_phase_hook;
}