907 lines
23 KiB
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);
|
|
}
|
|
|
|
|