257 lines
6.4 KiB
C
257 lines
6.4 KiB
C
/*****************************************************************************\
|
|
** **
|
|
** Linux Call Router **
|
|
** **
|
|
**---------------------------------------------------------------------------**
|
|
** Copyright: Andreas Eversberg **
|
|
** **
|
|
** mISDN bchannel access (for Asterisk) **
|
|
** **
|
|
\*****************************************************************************/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "main.h"
|
|
#include <unistd.h>
|
|
#include <poll.h>
|
|
#include <errno.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
extern "C" {
|
|
#include <net_l2.h>
|
|
}
|
|
|
|
#if 0
|
|
#ifndef ISDN_PID_L2_B_USER
|
|
#define ISDN_PID_L2_B_USER 0x420000ff
|
|
#endif
|
|
#ifndef ISDN_PID_L3_B_USER
|
|
#define ISDN_PID_L3_B_USER 0x430000ff
|
|
#endif
|
|
#endif
|
|
#ifndef ISDN_PID_L4_B_USER
|
|
#define ISDN_PID_L4_B_USER 0x440000ff
|
|
#endif
|
|
|
|
/* used for udevice */
|
|
int entity = 0;
|
|
|
|
/* the device handler and port list */
|
|
int mISDNdevice = -1;
|
|
|
|
|
|
/* open mISDN device */
|
|
void mISDNdevice_open(void)
|
|
{
|
|
/* open mISDNdevice if not already open */
|
|
if (mISDNdevice < 0)
|
|
{
|
|
ret = mISDN_open();
|
|
if (ret < 0)
|
|
{
|
|
PERROR("cannot open mISDN device ret=%d errno=%d (%s) Check for mISDN modules!\nAlso did you create \"/dev/mISDN\"? Do: \"mknod /dev/mISDN c 46 0\"\n", ret, errno, strerror(errno));
|
|
return(NULL);
|
|
}
|
|
mISDNdevice = ret;
|
|
PDEBUG(DEBUG_ISDN, "mISDN device opened.\n");
|
|
}
|
|
}
|
|
|
|
/* close mISDN device */
|
|
void mISDNdevice_close(void)
|
|
{
|
|
if (mISDNdevice > -1)
|
|
{
|
|
mISDN_close();
|
|
PDEBUG(DEBUG_ISDN, "mISDN device closed.\n");
|
|
}
|
|
}
|
|
|
|
/* create bchannel layer */
|
|
unsigned long mISDN_createlayer(unsigned long stid)
|
|
{
|
|
unsigned long addr;
|
|
|
|
/* create new layer */
|
|
PDEBUG(DEBUG_BCHANNEL, "creating new layer for bchannel stid=0%x.\n" , stid);
|
|
memset(&li, 0, sizeof(li));
|
|
memset(&pid, 0, sizeof(pid));
|
|
li.object_id = -1;
|
|
li.extentions = 0;
|
|
li.st = stid;
|
|
UCPY(li.name, "B L4");
|
|
li.pid.layermask = ISDN_LAYER((4));
|
|
li.pid.protocol[4] = ISDN_PID_L4_B_USER;
|
|
ret = mISDN_new_layer(mISDNdevice, &li);
|
|
if (ret)
|
|
{
|
|
failed_new_layer:
|
|
PERROR("mISDN_new_layer() failed to add bchannel stid=0%x.\n", stid);
|
|
goto failed;
|
|
}
|
|
addr = li.id;
|
|
if (!li.id)
|
|
{
|
|
goto failed_new_layer;
|
|
}
|
|
PDEBUG(DEBUG_BCHANNEL, "new layer (addr=0x%x)\n", addr);
|
|
|
|
/* create new stack */
|
|
pid.protocol[1] = ISDN_PID_L1_B_64TRANS;
|
|
pid.protocol[2] = ISDN_PID_L2_B_TRANS;
|
|
pid.protocol[3] = ISDN_PID_L3_B_DSP;
|
|
pid.protocol[4] = ISDN_PID_L4_B_USER;
|
|
pid.layermask = ISDN_LAYER((1)) | ISDN_LAYER((2)) | ISDN_LAYER((3)) | ISDN_LAYER((4));
|
|
ret = mISDN_set_stack(mISDNdevice, stid, &pid);
|
|
if (ret)
|
|
{
|
|
stack_error:
|
|
PERROR("mISDN_set_stack() failed (ret=%d) to add bchannel stid=0x%x\n", ret, stid);
|
|
mISDN_write_frame(mISDNdevice, buff, addr, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC);
|
|
goto failed;
|
|
}
|
|
ret = mISDN_get_setstack_ind(mISDNdevice, addr);
|
|
if (ret)
|
|
goto stack_error;
|
|
|
|
/* get layer id */
|
|
addr = mISDN_get_layerid(mISDNdevice, stid, 4);
|
|
if (!addr)
|
|
goto stack_error;
|
|
}
|
|
|
|
/* destroy bchannel layer */
|
|
void mISDN_destroylayer(unsigned long stid, unsigned long addr)
|
|
{
|
|
/* remove our stack only if set */
|
|
if (addr)
|
|
{
|
|
PDEBUG(DEBUG_BCHANNEL, "free stack (addr=0x%x)\n", addr);
|
|
mISDN_clear_stack(mISDNdevice, stid);
|
|
mISDN_write_frame(mISDNdevice, buff, addr | FLG_MSG_DOWN, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC);
|
|
}
|
|
}
|
|
|
|
/* do activation and deactivation of bchannel */
|
|
static void mISDN_bchannelactivate(unsigned long addr, int activate)
|
|
{
|
|
iframe_t act;
|
|
|
|
/* activate bchannel */
|
|
act.prim = (activate?DL_ESTABLISH:DL_RELEASE) | REQUEST;
|
|
act.addr = addr | FLG_MSG_DOWN;
|
|
act.dinfo = 0;
|
|
act.len = 0;
|
|
mISDN_write(mISDNdevice, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC);
|
|
}
|
|
|
|
/* handle all mISDN messages */
|
|
int mISDN_handler(void)
|
|
{
|
|
int ret;
|
|
msg_t *msg;
|
|
iframe_t *frm;
|
|
struct mISDNport *mISDNport;
|
|
class PmISDN *isdnport;
|
|
net_stack_t *nst;
|
|
msg_t *dmsg;
|
|
mISDNuser_head_t *hh;
|
|
int i;
|
|
struct chan_bchannel *bchannel;
|
|
|
|
/* no device, no read */
|
|
if (mISDNdevice < 0)
|
|
return(0);
|
|
|
|
/* get message from kernel */
|
|
if (!(msg = alloc_msg(MAX_MSG_SIZE)))
|
|
return(1);
|
|
ret = mISDN_read(mISDNdevice, msg->data, MAX_MSG_SIZE, 0);
|
|
if (ret < 0)
|
|
{
|
|
free_msg(msg);
|
|
if (errno == EAGAIN)
|
|
return(0);
|
|
FATAL("Failed to do mISDN_read()\n");
|
|
}
|
|
if (!ret)
|
|
{
|
|
free_msg(msg);
|
|
// printf("%s: ERROR: mISDN_read() returns nothing\n");
|
|
return(0);
|
|
}
|
|
msg->len = ret;
|
|
frm = (iframe_t *)msg->data;
|
|
|
|
/* global prim */
|
|
switch(frm->prim)
|
|
{
|
|
case MGR_DELLAYER | CONFIRM:
|
|
case MGR_INITTIMER | CONFIRM:
|
|
case MGR_ADDTIMER | CONFIRM:
|
|
case MGR_DELTIMER | CONFIRM:
|
|
case MGR_REMOVETIMER | CONFIRM:
|
|
free_msg(msg);
|
|
return(1);
|
|
}
|
|
|
|
/* look for channel instance, that has the address of this message */
|
|
bchannel = bchannel_first;
|
|
while(bchannel)
|
|
{
|
|
if (frm->addr == bchannel->b_addr)
|
|
break;
|
|
bchannel = chan->next;
|
|
}
|
|
if (!bchannel)
|
|
{
|
|
PERROR("message belongs to no bchannel: prim(0x%x) addr(0x%x) msg->len(%d)\n", frm->prim, frm->addr, msg->len);
|
|
goto out;
|
|
}
|
|
|
|
/* b-message */
|
|
switch(frm->prim)
|
|
{
|
|
/* we don't care about confirms, we use rx data to sync tx */
|
|
case PH_DATA | CONFIRM:
|
|
case DL_DATA | CONFIRM:
|
|
break;
|
|
|
|
/* we receive audio data, we respond to it AND we send tones */
|
|
case PH_DATA | INDICATION:
|
|
case DL_DATA | INDICATION:
|
|
case PH_CONTROL | INDICATION:
|
|
i = 0;
|
|
bchannel_receive(bchannel, frm);
|
|
break;
|
|
|
|
case PH_ACTIVATE | INDICATION:
|
|
case DL_ESTABLISH | INDICATION:
|
|
case PH_ACTIVATE | CONFIRM:
|
|
case DL_ESTABLISH | CONFIRM:
|
|
PDEBUG(DEBUG_BCHANNEL, "DL_ESTABLISH confirm: bchannel is now activated (address 0x%x).\n", frm->addr);
|
|
bchannel_event(mISDNport, i, B_EVENT_ACTIVATED);
|
|
break;
|
|
|
|
case PH_DEACTIVATE | INDICATION:
|
|
case DL_RELEASE | INDICATION:
|
|
case PH_DEACTIVATE | CONFIRM:
|
|
case DL_RELEASE | CONFIRM:
|
|
PDEBUG(DEBUG_BCHANNEL, "DL_RELEASE confirm: bchannel is now de-activated (address 0x%x).\n", frm->addr);
|
|
bchannel_event(mISDNport, i, B_EVENT_DEACTIVATED);
|
|
break;
|
|
|
|
default:
|
|
PERROR("child message not handled: prim(0x%x) addr(0x%x) msg->len(%d)\n", frm->prim, frm->addr, msg->len);
|
|
}
|
|
|
|
out:
|
|
free_msg(msg);
|
|
return(1);
|
|
}
|
|
|