mISDNuser/bridge/bridge.c

907 lines
23 KiB
C

/*****************************************************************************\
** **
** isdnbridge **
** **
**---------------------------------------------------------------------------**
** Copyright: Andreas Eversberg (GPL) **
** **
** user space utility to bridge two mISDN ports via layer 1 **
** **
\*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <errno.h>
#include <mISDN/mISDNif.h>
#include <mISDN/af_isdn.h>
int portcount = 0; /* counts all open ports for finding pair */
int mISDNsocket = -1;
/* quit flag */
int quit = 0;
/* option stuff */
int nooutput = 0;
int traffic = 0;
int debug = 0;
pid_t dsp_pid = 0;
/* mISDN port structure list */
struct mISDNport {
struct mISDNport *next, *prev;
int count; /* port count */
int portnum; /* port number */
int l1link; /* if l1 is available (only works with nt-mode) */
time_t l1establish; /* time until establishing after link failure */
int ntmode; /* is TRUE if port is nt mode */
int pri; /* is TRUE if port is a primary rate interface */
int d_sock;
int b_num; /* number of ports */
int b_sock[256];
int b_state[256]; /* state 0 = IDLE */
unsigned char que_frm[2048]; /* queue while layer 1 is down */
int que_len;
};
struct mISDNport *mISDNport_first = NULL;
enum { B_STATE_IDLE, B_STATE_ACTIVATING, B_STATE_ACTIVE, B_STATE_DEACTIVATING };
/*
* show state
*/
static void show_state(struct mISDNport *m1)
{
struct mISDNport *m2 = m1;
if (nooutput)
return;
if (m1->count & 1)
m1 = m1->prev;
else
m2 = m2->next;
printf("Port %2d %s <-> Port %2d %s\n", m1->portnum, (m1->l1link)?"ACTIVE":"inactive", m2->portnum, (m2->l1link)?"ACTIVE":"inactive");
}
/*
* show traffic
*/
static void show_traffic(struct mISDNport *m1, unsigned char *data, int len)
{
struct mISDNport *m2 = m1;
int right, i;
if (nooutput)
return;
if (m1->count & 1)
{
m1 = m1->prev;
right = 0;
} else
{
m2 = m2->next;
right = 1;
}
printf("Port %2d %s Port %2d :", m1->portnum, right?"-->":"<--", m2->portnum);
i = 0;
while(i < len)
{
printf(" %02x", data[i]);
i++;
}
printf("\n");
}
/*
* debug output
*/
#define PDEBUG(fmt, arg...) _printdebug(__FUNCTION__, __LINE__, fmt, ## arg)
static void _printdebug(const char *function, int line, const char *fmt, ...)
{
char buffer[4096];
va_list args;
if (!debug || nooutput)
return;
va_start(args,fmt);
vsnprintf(buffer, sizeof(buffer)-1, fmt, args);
buffer[sizeof(buffer)-1]=0;
va_end(args);
printf("%s, line %d: %s", function, line, buffer);
}
/*
* signal handler to interrupt main loop
*/
static void sighandler(int sigset)
{
if (sigset == SIGHUP)
return;
if (sigset == SIGPIPE)
return;
fprintf(stderr, "Signal received: %d\n", sigset);
if (!quit)
quit = 1;
}
/*
* send control information to the channel (dsp-module)
*/
static void ph_control(int sock, int c1, int c2)
{
unsigned char data[MISDN_HEADER_LEN+sizeof(int)+sizeof(int)];
struct mISDNhead *hh = (struct mISDNhead *)data;
int len;
int *d = (int *)(data + MISDN_HEADER_LEN);
hh->prim = PH_CONTROL_REQ;
hh->id = 0;
len = MISDN_HEADER_LEN + sizeof(unsigned long)*2;
*d++ = c1;
*d++ = c2;
len = sendto(sock, hh, len, 0, NULL, 0);
if (len <= 0)
fprintf(stderr, "Failed to send to socket %d\n", sock);
}
void ph_control_block(int sock, int c1, void *c2, int c2_len)
{
unsigned char data[MISDN_HEADER_LEN+sizeof(int)+c2_len];
struct mISDNhead *hh = (struct mISDNhead *)data;
int len;
int *d = (int *)(data + MISDN_HEADER_LEN);
hh->prim = PH_CONTROL_REQ;
hh->id = 0;
len = MISDN_HEADER_LEN + sizeof(unsigned long) + c2_len;
*d++ = c1;
memcpy(d, c2, c2_len);
len = sendto(sock, hh, len, 0, NULL, 0);
if (len <= 0)
fprintf(stderr, "Failed to send to socket %d\n", sock);
}
/*
* activate / deactivate bchannel
*/
static void bchannel_activate(struct mISDNport *mISDNport, int i)
{
struct mISDNhead hh;
int len;
/* we must activate if we are deactivated */
if (mISDNport->b_state[i] == B_STATE_IDLE)
{
/* activate bchannel */
PDEBUG("activating bchannel (index %d), because currently idle.\n", i);
hh.prim = PH_ACTIVATE_REQ;
hh.id = 0;
len = sendto(mISDNport->b_sock[i], &hh, MISDN_HEADER_LEN, 0, NULL, 0);
if (len <= 0)
fprintf(stderr, "Failed to send to socket %d of port %d channel index %d\n", mISDNport->b_sock[i], mISDNport->portnum, i);
mISDNport->b_state[i] = B_STATE_ACTIVATING;
return;
}
/* if we are active, we configure our channel */
if (mISDNport->b_state[i] == B_STATE_ACTIVE)
{
/* it is an error if this channel is not associated with a port object */
PDEBUG("during activation, we set rxoff.\n");
ph_control(mISDNport->b_sock[i], DSP_RECEIVE_OFF, 0);
PDEBUG("during activation, we add conference to %d.\n", ((mISDNport->count&(~1)) << 23) + (i<<16) + dsp_pid);
ph_control(mISDNport->b_sock[i], DSP_CONF_JOIN, ((mISDNport->count&(~1)) << 23) + (i<<16) + dsp_pid);
#if 0
if (sadks->crypt)
{
PDEBUG("during activation, we set crypt to crypt=%d.\n", mISDNport->b_port[i]->p_m_crypt);
ph_control_block(mISDNport->b_addr[i], BF_ENABLE_KEY, mISDNport->b_port[i]->p_m_crypt_key, mISDNport->b_port[i]->p_m_crypt_key_len);
}
#endif
}
}
static void bchannel_deactivate(struct mISDNport *mISDNport, int i)
{
struct mISDNhead hh;
int len;
if (mISDNport->b_state[i] == B_STATE_ACTIVE)
{
ph_control(mISDNport->b_sock[i], DSP_CONF_SPLIT, 0);
ph_control(mISDNport->b_sock[i], DSP_RECEIVE_ON, 0);
/* deactivate bchannel */
PDEBUG("deactivating bchannel (index %d), because currently active.\n", i);
hh.prim = PH_DEACTIVATE_REQ;
hh.id = 0;
len = sendto(mISDNport->b_sock[i], &hh, MISDN_HEADER_LEN, 0, NULL, 0);
if (len <= 0)
fprintf(stderr, "Failed to send to socket %d of port %d channel index %d\n", mISDNport->b_sock[i], mISDNport->portnum, i);
mISDNport->b_state[i] = B_STATE_DEACTIVATING;
return;
}
}
/*
* main loop for processing messages from mISDN device
*/
int mISDN_handler(void)
{
struct mISDNport *mISDNport;
int i;
unsigned char data[2048];
struct mISDNhead *hh = (struct mISDNhead *)data;
int len;
int work = 0;
/* handle all ports */
mISDNport = mISDNport_first;
while(mISDNport)
{
len = recv(mISDNport->d_sock, data, sizeof(data), 0);
if (len >= (int)MISDN_HEADER_LEN)
{
work = 1;
/* d-message */
switch(hh->prim)
{
case PH_ACTIVATE_CNF:
case PH_ACTIVATE_IND:
PDEBUG("Received PH_ACTIVATE for port %d.\n", mISDNport->portnum);
if (!mISDNport->l1link)
{
mISDNport->l1link = 1;
show_state(mISDNport);
}
if (mISDNport->que_len)
{
PDEBUG("Data in que, due to inactive link on port %d.\n", mISDNport->portnum);
len = sendto(mISDNport->d_sock, mISDNport->que_frm, mISDNport->que_len, 0, NULL, 0);
if (len <= 0)
fprintf(stderr, "Failed to send to socket %d of port %d\n", mISDNport->d_sock, mISDNport->portnum);
mISDNport->que_len = 0;
}
break;
case PH_DEACTIVATE_CNF:
case PH_DEACTIVATE_IND:
PDEBUG("Received PH_DEACTIVATE for port %d.\n", mISDNport->portnum);
if (mISDNport->l1link)
{
mISDNport->l1link = 0;
show_state(mISDNport);
}
mISDNport->que_len = 0;
break;
case PH_CONTROL_CNF:
case PH_CONTROL_IND:
PDEBUG("Received PH_CONTROL for port %d.\n", mISDNport->portnum);
break;
case PH_DATA_IND:
if (traffic)
show_traffic(mISDNport, data + MISDN_HEADER_LEN, len-MISDN_HEADER_LEN);
PDEBUG("GOT data from %s port %d prim 0x%x id 0x%x\n", (mISDNport->ntmode)?"NT":"TE", mISDNport->portnum, hh->prim, hh->id);
if (mISDNport->count & 1)
{
if (mISDNport->prev == NULL)
{
printf("soft error, no prev where expected.\n");
exit (0);
}
/* sending to previous port */
PDEBUG("sending to %s port %d prim 0x%x id 0x%x\n", (mISDNport->prev->ntmode)?"NT":"TE", mISDNport->prev->portnum, hh->prim, hh->id);
hh->prim = PH_DATA_REQ;
if (mISDNport->prev->l1link)
{
len = sendto(mISDNport->prev->d_sock, data, len, 0, NULL, 0);
if (len <= 0)
fprintf(stderr, "Failed to send to socket %d of port %d\n", mISDNport->d_sock, mISDNport->portnum);
} else {
PDEBUG("layer 1 is down, so we queue and activate link.\n");
memcpy(mISDNport->prev->que_frm, data, len);
mISDNport->prev->que_len = len;
hh->prim = PH_ACTIVATE_REQ;
hh->id = 0;
len = sendto(mISDNport->prev->d_sock, data, MISDN_HEADER_LEN, 0, NULL, 0);
if (len <= 0)
fprintf(stderr, "Failed to send to socket %d of port %d\n", mISDNport->d_sock, mISDNport->portnum);
}
} else {
if (mISDNport->next == NULL)
{
printf("soft error, no next where expected.\n");
exit (0);
}
/* sending to next port */
PDEBUG("sending to %s port %d prim 0x%x id 0x%x\n", (mISDNport->next->ntmode)?"NT":"TE", mISDNport->next->portnum, hh->prim, hh->id);
hh->prim = PH_DATA_REQ;
if (mISDNport->next->l1link)
{
len = sendto(mISDNport->next->d_sock, data, len, 0, NULL, 0);
if (len <= 0)
fprintf(stderr, "Failed to send to socket %d of port %d\n", mISDNport->d_sock, mISDNport->portnum);
} else {
PDEBUG("layer 1 is down, so we queue and activate link.\n");
memcpy(mISDNport->next->que_frm, data, len);
mISDNport->next->que_len = len;
hh->prim = PH_ACTIVATE_REQ;
hh->id = 0;
len = sendto(mISDNport->next->d_sock, data, MISDN_HEADER_LEN, 0, NULL, 0);
if (len <= 0)
fprintf(stderr, "Failed to send to socket %d of port %d\n", mISDNport->d_sock, mISDNport->portnum);
}
}
break;
case PH_DATA_CNF:
//PDEBUG("GOT confirm from %s port %d prim 0x%x id 0x%x\n", (mISDNport->ntmode)?"NT":"TE", mISDNport->portnum, hh->prim, hh->id);
break;
case PH_DATA_REQ:
//PDEBUG("GOT strange PH_DATA REQUEST from %s port %d prim 0x%x id 0x%x 0x%x\n", (mISDNport->ntmode)?"NT":"TE", mISDNport->portnum, hh->prim, hh->id);
break;
default:
break;
}
}
i = 0;
while(i < mISDNport->b_num)
{
len = recv(mISDNport->b_sock[i], data, sizeof(data), 0);
if (len >= (int)MISDN_HEADER_LEN)
{
work = 1;
/* b-message */
switch(hh->prim)
{
/* we don't care about confirms, we use rx data to sync tx */
case PH_DATA_CNF:
//case DL_DATA_CNF:
break;
/* we receive audio data, we respond to it AND we send tones */
case PH_DATA_IND:
case DL_DATA_IND:
PDEBUG("got B-channel data, this should not happen all the time. (just a few until DSP release tx-data are ok)\n");
break;
case PH_CONTROL_IND:
break;
case PH_ACTIVATE_IND:
case DL_ESTABLISH_IND:
case PH_ACTIVATE_CNF:
case DL_ESTABLISH_CNF:
PDEBUG("DL_ESTABLISH confirm: bchannel is now activated (port %d index %i).\n", mISDNport->portnum, i);
mISDNport->b_state[i] = B_STATE_ACTIVE;
bchannel_activate(mISDNport, i);
break;
case PH_DEACTIVATE_IND:
case DL_RELEASE_IND:
case PH_DEACTIVATE_CNF:
case DL_RELEASE_CNF:
PDEBUG("DL_RELEASE confirm: bchannel is now de-activated (port %d index %i).\n", mISDNport->portnum, i);
mISDNport->b_state[i] = B_STATE_IDLE;
break;
default:
PDEBUG("unknown bchannel data (port %d index %i).\n", mISDNport->portnum, i);
}
}
i++;
}
mISDNport = mISDNport->next;
}
return(work);
}
/*
* global function to add a new card (port)
*/
struct mISDNport *mISDN_port_open(int port, int nt_mode, int hdlc)
{
int ret;
struct mISDNhead hh;
struct mISDNport *mISDNport, **mISDNportp, *mISDNport_prev;
int i, cnt;
int bri = 0, pri = 0, pots = 0;
int nt = 0, te = 0;
struct mISDN_devinfo devinfo;
unsigned long on = 1;
struct sockaddr_mISDN addr;
/* query port's requirements */
ret = ioctl(mISDNsocket, IMGETCOUNT, &cnt);
if (ret < 0)
{
fprintf(stderr, "Cannot get number of mISDN devices. (ioctl IMGETCOUNT failed ret=%d)\n", ret);
return(NULL);
}
if (cnt <= 0)
{
fprintf(stderr, "Found no card. Please be sure to load card drivers.\n");
return(NULL);
}
if (port < 0)
{
fprintf(stderr, "Port number cannot be negative\n");
return(NULL);
}
devinfo.id = port;
ret = ioctl(mISDNsocket, IMGETDEVINFO, &devinfo);
if (ret < 0)
{
fprintf(stderr, "Cannot get device information for port %d. (ioctl IMGETDEVINFO failed ret=%d)\n", port, ret);
return(NULL);
}
/* output the port info */
if (devinfo.Dprotocols & (1 << ISDN_P_TE_S0))
{
bri = 1;
te = 1;
}
if (devinfo.Dprotocols & (1 << ISDN_P_NT_S0))
{
bri = 1;
nt = 1;
}
if (devinfo.Dprotocols & (1 << ISDN_P_TE_E1))
{
pri = 1;
te = 1;
}
if (devinfo.Dprotocols & (1 << ISDN_P_NT_E1))
{
pri = 1;
nt = 1;
}
#ifdef ISDN_P_FXS
if (devinfo.Dprotocols & (1 << ISDN_P_FXS))
{
pots = 1;
te = 1;
}
#endif
#ifdef ISDN_P_FXO
if (devinfo.Dprotocols & (1 << ISDN_P_FXO))
{
pots = 1;
nt = 1;
}
#endif
if (bri && pri)
{
fprintf(stderr, "Port %d supports BRI and PRI?? What kind of controller is that?. (Can't use this!)\n", port);
return(NULL);
}
if (pots && !bri && !pri)
{
fprintf(stderr, "Port %d supports POTS, we can't!\n", port);
return(NULL);
}
if (!bri && !pri)
{
fprintf(stderr, "Port %d does not support BRI nor PRI!\n", port);
return(NULL);
}
if (!nt && !te)
{
fprintf(stderr, "Port %d does not support NT-mode nor TE-mode!\n", port);
return(NULL);
}
if (!nt && nt_mode)
{
fprintf(stderr, "Port %d does not support NT-mode as requested!\n", port);
return(NULL);
}
if (!te && !nt_mode)
{
fprintf(stderr, "Port %d does not support TE-mode as requested!\n", port);
return(NULL);
}
/* add mISDNport structure */
mISDNport = mISDNport_first;
mISDNportp = &mISDNport_first;
mISDNport_prev = NULL;
while(mISDNport)
{
mISDNport_prev=mISDNport;
mISDNportp = &mISDNport->next;
mISDNport = mISDNport->next;
}
mISDNport = (struct mISDNport *)calloc(1, sizeof(struct mISDNport));
if (!mISDNport)
{
fprintf(stderr, "Cannot alloc mISDNport structure\n");
return(NULL);
}
memset(mISDNport, 0, sizeof(struct mISDNport));
*mISDNportp = mISDNport;
mISDNport->prev = mISDNport_prev;
/* allocate ressources of port */
mISDNport->count = portcount++;
mISDNport->portnum = port;
mISDNport->b_num = devinfo.nrbchan;
mISDNport->ntmode = nt_mode;
mISDNport->pri = pri;
/* open dchannel */
if (nt_mode)
mISDNport->d_sock = socket(PF_ISDN, SOCK_DGRAM, pri?ISDN_P_NT_E1:ISDN_P_NT_S0);
else
mISDNport->d_sock = socket(PF_ISDN, SOCK_DGRAM, pri?ISDN_P_TE_E1:ISDN_P_TE_S0);
if (mISDNport->d_sock < 0)
{
return(NULL);
}
/* set nonblocking io */
ret = ioctl(mISDNport->d_sock, FIONBIO, &on);
if (ret < 0)
{
fprintf(stderr, "Error: Failed to set dchannel-socket into nonblocking IO\n");
return(NULL);
}
/* bind socket to dchannel */
memset(&addr, 0, sizeof(addr));
addr.family = AF_ISDN;
addr.dev = mISDNport->portnum;
addr.channel = 0;
ret = bind(mISDNport->d_sock, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0)
{
fprintf(stderr, "Error: Failed to bind dchannel-socket.\n");
return(NULL);
}
PDEBUG("Port %d (%s) opened with %d b-channels.\n", port, devinfo.name, mISDNport->b_num);
/* open bchannels */
i = 0;
while(i < mISDNport->b_num)
{
mISDNport->b_sock[i] = -1;
i++;
}
i = 0;
while(i < mISDNport->b_num)
{
mISDNport->b_sock[i] = socket(PF_ISDN, SOCK_DGRAM, (hdlc)?ISDN_P_B_L2DSPHDLC:ISDN_P_B_L2DSP);
if (mISDNport->b_sock[i] < 0)
{
fprintf(stderr, "Error: Failed to open bchannel-socket for index %d with mISDN-DSP layer. Did you load mISDNdsp.ko?\n", i);
return(NULL);
}
/* set nonblocking io */
ret = ioctl(mISDNport->b_sock[i], FIONBIO, &on);
if (ret < 0)
{
fprintf(stderr, "Error: Failed to set bchannel-socket index %d into nonblocking IO\n", i);
return(NULL);
}
/* bind socket to bchannel */
memset(&addr, 0, sizeof(addr));
addr.family = AF_ISDN;
addr.dev = mISDNport->portnum;
addr.channel = i+1+(i>=15);
ret = bind(mISDNport->b_sock[i], (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0)
{
fprintf(stderr, "Error: Failed to bind bchannel-socket for index %d with mISDN-DSP layer. Did you load mISDNdsp.ko?\n", i);
return(NULL);
}
i++;
}
/* try to activate link layer 1 */
hh.prim = PH_ACTIVATE_REQ;
hh.id = 0;
ret = sendto(mISDNport->d_sock, &hh, MISDN_HEADER_LEN, 0, NULL, 0);
if (ret <= 0)
fprintf(stderr, "Failed to send to socket %d of port %d\n", mISDNport->d_sock, mISDNport->portnum);
/* initially, we assume that the link is down */
mISDNport->l1link = 0;
PDEBUG("using 'mISDN_dsp.o' module\n");
printf("Port %d (%s) %s %s %d b-channels\n", mISDNport->portnum, devinfo.name, (mISDNport->ntmode)?"NT-mode":"TE-mode", pri?"PRI":"BRI", mISDNport->b_num);
return(mISDNport);
}
/*
* global function to free ALL cards (ports)
*/
void mISDN_port_close(void)
{
struct mISDNport *mISDNport, *mISDNporttemp;
int i;
/* free all ports */
mISDNport = mISDNport_first;
while(mISDNport)
{
i = 0;
while(i < mISDNport->b_num)
{
bchannel_deactivate(mISDNport, i);
PDEBUG("freeing %s port %d bchannel (index %d).\n", (mISDNport->ntmode)?"NT":"TE", mISDNport->portnum, i);
if (mISDNport->b_sock[i])
close(mISDNport->b_sock[i]);
i++;
}
PDEBUG("freeing d-stack.\n");
if (mISDNport->d_sock)
close(mISDNport->d_sock);
mISDNporttemp = mISDNport;
mISDNport = mISDNport->next;
memset(mISDNporttemp, 0, sizeof(struct mISDNport));
free(mISDNporttemp);
}
mISDNport_first = NULL;
}
/*
* main routine and loop
*/
int main(int argc, char *argv[])
{
struct mISDNport *mISDNport_a, *mISDNport_b;
int i, j, nt_a, nt_b;
int forking = 0, hdlc = 0;
dsp_pid = getpid();
if (argc <= 1)
{
usage:
printf("Usage: %s [--<option> [...]] te|nt <port a> te|nt <port b> \\\n"
" [te|nt <port c> te|nt <port d> [...]]\n\n", argv[0]);
printf("Example: %s --traffic te 0 nt 1 # bridges port 0 (TE-mode) with port 1 (NT-mode)\n", argv[0]);
printf("Bridges given pairs of ports. The number of given ports must be even.\n");
printf("Each pair of ports must be the same interface size (equal channel number).\n");
printf("Both ports may have same mode, e.g. 'te', to bridge ISDN leased line.\n");
printf("Also bridging a card to ISDN over IP tunnel is possible. (L1oIP)\n");
printf("Use the following options before listing mode and ports:\n");
printf("--af_isdn <nr> use a alternativ address family number.\n");
printf("--fork will make a daemon fork.\n");
printf("--traffic will show D-channel traffic.\n");
printf("--hdlc will bridge all bchannel via HDLC.\n");
printf("--debug will show debug info.\n");
return(0);
}
if (strstr("help", argv[1]))
goto usage;
/* try to open raw socket to check kernel */
mISDNsocket = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
if (mISDNsocket < 0)
{
fprintf(stderr, "Cannot open mISDN due to '%s'. (Does your Kernel support socket based mISDN?)\n", strerror(errno));
return(mISDNsocket);
}
/* read options and ports */
i = 1;
while (i < argc)
{
usleep(200000);
if (!strcmp(argv[i], "--af_isdn"))
{
i++;
if (i == argc) {
fprintf(stderr, "Expecting address family number after --af_isdn\n\n");
goto error;
}
j = strtol(argv[i], NULL, 0);
if (set_af_isdn(j) < 0) {
fprintf(stderr, "Wrong address family number %s\n", argv[i]);
goto error;
}
continue;
}
if (!strcmp(argv[i], "--traffic"))
{
traffic = 1;
i++;
continue;
}
if (!strcmp(argv[i], "--fork"))
{
forking = 1;
i++;
continue;
}
if (!strcmp(argv[i], "--hdlc"))
{
hdlc = 1;
i++;
continue;
}
if (!strcmp(argv[i], "--debug"))
{
debug = 1;
i++;
continue;
}
/* get mode a */
if (!strcasecmp(argv[i], "te"))
nt_a = 0;
else if (!strcasecmp(argv[i], "nt"))
nt_a = 1;
else {
fprintf(stderr, "Expecting 'te' or 'nt' keyword for argument #%d\n\n", i);
goto error;
}
i++; // first port
if (i == argc)
{
fprintf(stderr, "Expecting port a number after given mode.\n\n");
goto error;
}
/* open port a */
mISDNport_a = mISDN_port_open(strtol(argv[i], NULL, 0), nt_a, hdlc);
if (!mISDNport_a)
goto error;
printf("port A: #%d %s, %d b-channels\n", mISDNport_a->portnum, (mISDNport_a->ntmode)?"NT-mode":"TE-mode", mISDNport_a->b_num);
i++; // second mode
if (i == argc)
{
fprintf(stderr, "The number of ports given are not even.\nYou may only bridge two or more pairs of ports.\n\n");
goto error;
}
/* get mode b */
if (!strcasecmp(argv[i], "te"))
nt_b = 0;
else if (!strcasecmp(argv[i], "nt"))
nt_b = 1;
else {
fprintf(stderr, "Expecting 'te' or 'nt' keyword for argument #%d\n\n", i);
goto error;
}
i++; // second port
if (i == argc)
{
fprintf(stderr, "Expecting port b number after given mode.\n\n");
goto error;
}
/* open port b */
mISDNport_b = mISDN_port_open(strtol(argv[i], NULL, 0), nt_b, hdlc);
if (!mISDNport_b)
goto error;
printf("port B: #%d %s, %d b-channels\n", mISDNport_b->portnum, (mISDNport_b->ntmode)?"NT-mode":"TE-mode", mISDNport_b->b_num);
i++; // next port / arg
if (mISDNport_a->b_num != mISDNport_b->b_num)
{
fprintf(stderr, "The pair of ports are not compatible for bridging.\n");
fprintf(stderr, "The number ob B-channels are different: port(%d)=%d, port(%d)=%d\n", mISDNport_a->portnum, mISDNport_a->b_num, mISDNport_b->portnum, mISDNport_b->b_num);
mISDN_port_close();
goto error;
}
/* opening and bridge each pair of bchannels */
j = 0;
while(j < mISDNport_a->b_num)
{
bchannel_activate(mISDNport_a, j);
usleep(5000);
while(mISDN_handler())
;
j++;
}
j = 0;
while(j < mISDNport_b->b_num)
{
bchannel_activate(mISDNport_b, j);
usleep(5000);
while(mISDN_handler())
;
j++;
}
}
printf("%s now started\n",argv[0]);
/* forking */
if (forking) {
pid_t pid;
/* do daemon fork */
pid = fork();
if (pid < 0)
{
fprintf(stderr, "Cannot fork!\n");
goto free;
}
if (pid != 0)
{
exit(0);
}
usleep(30000);
printf("\n");
/* do second fork */
pid = fork();
if (pid < 0)
{
fprintf(stderr, "Cannot fork!\n");
goto free;
}
if (pid != 0)
{
printf("PBX: Starting daemon.\n");
exit(0);
}
nooutput = 1;
}
/* signal handlers */
signal(SIGINT,sighandler);
signal(SIGHUP,sighandler);
signal(SIGTERM,sighandler);
signal(SIGPIPE,sighandler);
while(!quit)
{
//mISDNport_a->l1link = 1;
if (!mISDN_handler())
usleep(30000);
}
/* remove signal handler */
signal(SIGINT,SIG_DFL);
signal(SIGHUP,SIG_DFL);
signal(SIGTERM,SIG_DFL);
signal(SIGPIPE,SIG_DFL);
free:
mISDN_port_close();
close(mISDNsocket);
return(0);
error:
mISDN_port_close();
close(mISDNsocket);
return(-1);
}