285 lines
6.1 KiB
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;
|
|
}
|