1380 lines
32 KiB
C
1380 lines
32 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include "isdn_net.h"
|
|
#include "helper.h"
|
|
#include "tone.h"
|
|
#include "bchannel.h"
|
|
#include "net_l3.h"
|
|
#include "l3dss1.h"
|
|
|
|
static int
|
|
open_record_files(bchannel_t *bc)
|
|
{
|
|
int ret = -EINVAL;
|
|
|
|
if (bc->manager->application)
|
|
ret = bc->manager->application(bc->manager,
|
|
PR_APP_OPEN_RECFILES, bc);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
close_record_files(bchannel_t *bc)
|
|
{
|
|
int ret = -EINVAL;
|
|
|
|
if (bc->manager->application)
|
|
ret = bc->manager->application(bc->manager,
|
|
PR_APP_CLOSE_RECFILES, bc);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
setup_bchannel(bchannel_t *bc) {
|
|
struct {
|
|
int id;
|
|
mISDN_pid_t pid;
|
|
} para;
|
|
|
|
if ((bc->channel<1) || (bc->channel>2)) {
|
|
eprint("wrong channel %d\n", bc->channel);
|
|
return(-EINVAL);
|
|
}
|
|
dprint(DBGM_BC,"%s:ch%d bst(%d)\n", __FUNCTION__,
|
|
bc->channel, bc->bstate);
|
|
if ((bc->bstate != BC_BSTATE_NULL) &&
|
|
(bc->bstate != BC_BSTATE_CLEANUP))
|
|
return(-EBUSY);
|
|
memset(¶.pid, 0, sizeof(mISDN_pid_t));
|
|
para.pid.protocol[1] = bc->l1_prot;
|
|
if (FLG_BC_RAWDEVICE & bc->Flags) {
|
|
para.pid.protocol[2] = ISDN_PID_L2_B_RAWDEV;
|
|
para.pid.protocol[3] = ISDN_PID_L3_B_USER;
|
|
para.pid.layermask = ISDN_LAYER(1) | ISDN_LAYER(2) |
|
|
ISDN_LAYER(3);
|
|
} else {
|
|
para.pid.protocol[2] = ISDN_PID_L2_B_USER;
|
|
para.pid.layermask = ISDN_LAYER(1) | ISDN_LAYER(2);
|
|
}
|
|
if (bc->Flags & FLG_BC_CALL_ORGINATE)
|
|
para.pid.global = 1;
|
|
para.id = bc->l3id;
|
|
bc->bstate = BC_BSTATE_SETUP;
|
|
if (!bc->sbuf) {
|
|
bc->sbuf = init_ibuffer(2048);
|
|
if (bc->sbuf) {
|
|
bc->sbuf->rsem = &bc->work;
|
|
bc->sbuf->wsem = &bc->work;
|
|
}
|
|
}
|
|
if_link(bc->manager->nst, (ifunc_t)bc->manager->man2stack,
|
|
BC_SETUP | REQUEST, bc->channel, sizeof(para), ¶, 0);
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
activate_bchannel(bchannel_t *bc)
|
|
{
|
|
dprint(DBGM_BC,"%s:ch%d bst(%d)\n", __FUNCTION__,
|
|
bc->channel, bc->bstate);
|
|
if (!bc->b_addr) {
|
|
wprint("%s:ch%d not setup\n", __FUNCTION__,
|
|
bc->channel);
|
|
return(-EINVAL);
|
|
}
|
|
if ((bc->bstate == BC_BSTATE_SETUP) ||
|
|
(bc->bstate == BC_BSTATE_DEACTIVATE)) {
|
|
bc->bstate = BC_BSTATE_ACTIVATE;
|
|
return(if_link(bc->manager->nst,
|
|
(ifunc_t)bc->manager->man2stack,
|
|
PH_ACTIVATE | REQUEST, bc->b_addr | IF_DOWN,
|
|
0, NULL, 0));
|
|
} else
|
|
return(-EBUSY);
|
|
}
|
|
|
|
static int
|
|
deactivate_bchannel(bchannel_t *bc)
|
|
{
|
|
dprint(DBGM_BC,"%s:ch%d bst(%d)\n", __FUNCTION__,
|
|
bc->channel, bc->bstate);
|
|
if (!bc->b_addr) {
|
|
wprint("%s:ch%d not setup\n", __FUNCTION__,
|
|
bc->channel);
|
|
return(-EINVAL);
|
|
}
|
|
if ((bc->bstate == BC_BSTATE_ACTIVATE) ||
|
|
(bc->bstate == BC_BSTATE_ACTIV)) {
|
|
bc->bstate = BC_BSTATE_DEACTIVATE;
|
|
return(if_link(bc->manager->nst,
|
|
(ifunc_t)bc->manager->man2stack,
|
|
PH_DEACTIVATE | REQUEST, bc->b_addr | IF_DOWN,
|
|
0, NULL, 0));
|
|
} else
|
|
return(-EBUSY);
|
|
}
|
|
|
|
static int
|
|
bc_cleanup(bchannel_t *bc)
|
|
{
|
|
int ret;
|
|
|
|
dprint(DBGM_BC,"%s:ch%d bst(%d)\n", __FUNCTION__,
|
|
bc->channel, bc->bstate);
|
|
if (!bc->b_addr) {
|
|
wprint("%s:ch%d not setup\n", __FUNCTION__,
|
|
bc->channel);
|
|
}
|
|
if (!bc->l3id) {
|
|
wprint("%s:ch%d no l3id\n", __FUNCTION__,
|
|
bc->channel);
|
|
return(-EINVAL);
|
|
}
|
|
if ((bc->bstate == BC_BSTATE_DEACTIVATE) ||
|
|
(bc->bstate == BC_BSTATE_SETUP)) {
|
|
bc->bstate = BC_BSTATE_CLEANUP;
|
|
ret = if_link(bc->manager->nst,
|
|
(ifunc_t)bc->manager->man2stack, BC_CLEANUP | REQUEST,
|
|
bc->l3id, 0, NULL, 0);
|
|
} else
|
|
ret = EBUSY;
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
clear_bc(bchannel_t *bc)
|
|
{
|
|
pthread_mutex_lock(&bc->lock);
|
|
free_ibuffer(bc->sbuf);
|
|
bc->sbuf = NULL;
|
|
free_ibuffer(bc->rbuf);
|
|
bc->rbuf = NULL;
|
|
if (bc->Flags & FLG_BC_RECORDING)
|
|
close_record_files(bc);
|
|
bc->Flags = 0;
|
|
bc->nr[0] = 0;
|
|
bc->msn[0] = 0;
|
|
bc->display[0] = 0;
|
|
bc->usednr = NULL;
|
|
bc->smsg = NULL;
|
|
pthread_mutex_unlock(&bc->lock);
|
|
if ((bc->bstate == BC_BSTATE_ACTIV) ||
|
|
(bc->bstate == BC_BSTATE_ACTIVATE))
|
|
deactivate_bchannel(bc);
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
do_b_activated(bchannel_t *bc, mISDN_head_t *hh, msg_t *msg) {
|
|
dprint(DBGM_BC,"%s:ch%d state(%d/%d) Flags(%x) smsg(%p)\n", __FUNCTION__,
|
|
bc->channel, bc->cstate, bc->bstate, bc->Flags, bc->smsg);
|
|
clear_ibuffer(bc->rbuf);
|
|
if (!(bc->Flags & FLG_BC_KEEP_SEND))
|
|
clear_ibuffer(bc->sbuf);
|
|
if (bc->sbuf && bc->sbuf->wsem)
|
|
sem_post(bc->sbuf->wsem);
|
|
if (bc->bstate == BC_BSTATE_ACTIVATE)
|
|
bc->bstate = BC_BSTATE_ACTIV;
|
|
free_msg(msg);
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
do_b_deactivated(bchannel_t *bc, mISDN_head_t *hh, msg_t *msg) {
|
|
dprint(DBGM_BC,"%s:ch%d Flags(%x) smsg(%p)\n", __FUNCTION__,
|
|
bc->channel, bc->Flags, bc->smsg);
|
|
bc_cleanup(bc);
|
|
free_msg(msg);
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
do_b_setup_conf(bchannel_t *bc, mISDN_head_t *hh, msg_t *msg)
|
|
{
|
|
int *addr;
|
|
|
|
addr = (int *)msg->data;
|
|
bc->b_addr = *addr;
|
|
activate_bchannel(bc);
|
|
free_msg(msg);
|
|
return(0);
|
|
}
|
|
|
|
|
|
static int
|
|
do_b_cleanup_conf(bchannel_t *bc, mISDN_head_t *hh, msg_t *msg)
|
|
{
|
|
dprint(DBGM_BC,"%s:ch%d bst(%d)\n", __FUNCTION__,
|
|
bc->channel, bc->bstate);
|
|
bc->b_addr = 0;
|
|
if (bc->cstate == BC_CSTATE_NULL) {
|
|
bc->l3id = 0;
|
|
bc->cstate = BC_CSTATE_NULL;
|
|
}
|
|
bc->bstate = BC_BSTATE_NULL;
|
|
free_msg(msg);
|
|
return(0);
|
|
}
|
|
|
|
|
|
static int
|
|
do_b_data_cnf(bchannel_t *bc, mISDN_head_t *hh, msg_t *msg)
|
|
{
|
|
bc->smsg = NULL;
|
|
if (bc->sbuf && bc->sbuf->rsem)
|
|
sem_post(bc->sbuf->rsem);
|
|
free_msg(msg);
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
do_b_data_ind(bchannel_t *bc, mISDN_head_t *hh, msg_t *msg)
|
|
{
|
|
int len;
|
|
int ret = 0;
|
|
|
|
if (bc->bstate != BC_BSTATE_ACTIV)
|
|
return(-EBUSY);
|
|
dprint(DBGM_BCDATA, "%s:ch%d get %d bytes\n", __FUNCTION__,
|
|
bc->channel, msg->len);
|
|
if (bc->rbuf) {
|
|
len = ibuf_freecount(bc->rbuf);
|
|
if (msg->len > len)
|
|
ret = -ENOSPC;
|
|
else {
|
|
ibuf_memcpy_w(bc->rbuf, msg->data, msg->len);
|
|
}
|
|
if (bc->rbuf->rsem)
|
|
sem_post(bc->rbuf->rsem);
|
|
} else
|
|
ret = -EINVAL;
|
|
dprint(DBGM_BCDATA, "%s: finish ret %d\n", __FUNCTION__, ret);
|
|
if (bc->Flags & FLG_BC_RECORD) {
|
|
if (bc->Flags & FLG_BC_RECORDING) {
|
|
write(bc->rrid, msg->data, msg->len);
|
|
} else {
|
|
if (!open_record_files(bc))
|
|
write(bc->rrid, msg->data, msg->len);
|
|
}
|
|
} else if (bc->Flags & FLG_BC_RECORDING) {
|
|
close_record_files(bc);
|
|
}
|
|
if (!ret)
|
|
free_msg(msg);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
b_send(bchannel_t *bc)
|
|
{
|
|
int len = 0, ret = -EINVAL;
|
|
u_char *p;
|
|
|
|
if (bc->smsg)
|
|
goto out;
|
|
if (bc->bstate != BC_BSTATE_ACTIV)
|
|
goto out;
|
|
len = ibuf_usedcount(bc->sbuf);
|
|
if (!len)
|
|
goto out;
|
|
if (len > MAX_DATA_SIZE)
|
|
len = MAX_DATA_SIZE;
|
|
dprint(DBGM_BCDATA, "%s:ch%d %d bytes\n", __FUNCTION__, bc->channel, len);
|
|
bc->smsg = prep_l3data_msg(PH_DATA | REQUEST, bc->b_addr | IF_DOWN,
|
|
0, len, NULL);
|
|
if (!bc->smsg) {
|
|
len = -ENOMEM;
|
|
goto out;
|
|
}
|
|
p = msg_put(bc->smsg, len);
|
|
ibuf_memcpy_r(p, bc->sbuf, len);
|
|
if (bc->Flags & FLG_BC_RECORD) {
|
|
if (bc->Flags & FLG_BC_RECORDING) {
|
|
write(bc->rsid, p, len);
|
|
} else {
|
|
if (!open_record_files(bc))
|
|
write(bc->rsid, p, len);
|
|
}
|
|
} else if (bc->Flags & FLG_BC_RECORDING) {
|
|
close_record_files(bc);
|
|
}
|
|
if (bc->manager->man2stack)
|
|
ret = bc->manager->man2stack(bc->manager->nst, bc->smsg);
|
|
if (ret) {
|
|
free_msg(bc->smsg);
|
|
bc->smsg = NULL;
|
|
len = ret;
|
|
}
|
|
if (bc->sbuf->wsem)
|
|
sem_post(bc->sbuf->wsem);
|
|
out:
|
|
return(len);
|
|
}
|
|
|
|
/* call handling */
|
|
|
|
static int
|
|
add_nr(bchannel_t *bc, unsigned char *cpn)
|
|
{
|
|
if (bc->nr[0]) {
|
|
if (*cpn>1) {
|
|
memcpy(bc->nr + bc->nr[0] + 1, cpn + 2, *cpn -1);
|
|
bc->nr[0] += *cpn -1;
|
|
} else
|
|
dprint(DBGM_BC,"%s: cpn len %d\n", __FUNCTION__, *cpn);
|
|
} else if (*cpn)
|
|
memcpy(bc->nr, cpn, *cpn + 1);
|
|
dprint(DBGM_BC,"%s: nr:%s\n", __FUNCTION__, &bc->nr[2]);
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
send_setup_ack(bchannel_t *bc)
|
|
{
|
|
SETUP_ACKNOWLEDGE_t *sa;
|
|
msg_t *msg;
|
|
int len, ret;
|
|
unsigned char *p;
|
|
|
|
dprint(DBGM_BC,"%s: bc%d l3id(%x)\n", __FUNCTION__,
|
|
bc->channel, bc->l3id);
|
|
msg = prep_l3data_msg(CC_SETUP_ACKNOWLEDGE | REQUEST, bc->l3id,
|
|
sizeof(SETUP_ACKNOWLEDGE_t), 128, NULL);
|
|
if (!msg)
|
|
return(-ENOMEM);
|
|
sa = (SETUP_ACKNOWLEDGE_t *)(msg->data + mISDN_HEAD_SIZE);
|
|
pthread_mutex_lock(&bc->lock);
|
|
bc->cstate = BC_CSTATE_OVERLAP_REC;
|
|
if (!(bc->Flags & FLG_BC_SENT_CID)) {
|
|
bc->Flags |= FLG_BC_SENT_CID;
|
|
sa->CHANNEL_ID = msg_put(msg, 2);
|
|
sa->CHANNEL_ID[0] = 1;
|
|
sa->CHANNEL_ID[1] = 0x88 | bc->channel;
|
|
}
|
|
pthread_mutex_unlock(&bc->lock);
|
|
if (bc->Flags & FLG_BC_PROGRESS) {
|
|
sa->PROGRESS = p = msg_put(msg, 3);;
|
|
*p++ = 2;
|
|
*p++ = 0x80 | CAUSE_LOC_PNET_LOCUSER;
|
|
*p++ = 0x80 | PROGRESS_TONE;
|
|
setup_bchannel(bc);
|
|
}
|
|
if (bc->display[0]) {
|
|
len = strlen(bc->display);
|
|
sa->DISPLAY = p = msg_put(msg, len+1);
|
|
*p++ = len;
|
|
strcpy(p, bc->display);
|
|
bc->display[0] = 0;
|
|
}
|
|
ret = -EINVAL;
|
|
if (bc->manager->man2stack)
|
|
ret = bc->manager->man2stack(bc->manager->nst, msg);
|
|
if (ret)
|
|
free_msg(msg);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
send_setup(bchannel_t *bc)
|
|
{
|
|
SETUP_t *setup;
|
|
msg_t *msg;
|
|
int len, ret;
|
|
unsigned char *p;
|
|
|
|
if (bc->cstate != BC_CSTATE_OCALL) {
|
|
dprint(DBGM_BC,"%s: bc%d state(%d/%d) not OCALL\n", __FUNCTION__,
|
|
bc->channel, bc->cstate, bc->bstate);
|
|
return(-EINVAL);
|
|
}
|
|
bc->l3id = 0xfff0 | bc->channel;
|
|
msg = prep_l3data_msg(CC_SETUP | REQUEST, bc->l3id,
|
|
sizeof(SETUP_t), 256, NULL);
|
|
if (!msg)
|
|
return(-ENOMEM);
|
|
setup = (SETUP_t *)(msg->data + mISDN_HEAD_SIZE);
|
|
switch (bc->l1_prot) {
|
|
case ISDN_PID_L1_B_64TRANS:
|
|
bc->bc[0] = 3;
|
|
bc->bc[1] = 0x80;
|
|
bc->bc[2] = 0x90;
|
|
bc->bc[3] = 0xa3;
|
|
break;
|
|
default:
|
|
dprint(DBGM_BC,"%s: no protocol %x\n", __FUNCTION__,
|
|
bc->l1_prot);
|
|
free_msg(msg);
|
|
return(-ENOPROTOOPT);
|
|
}
|
|
setup->BEARER = p = msg_put(msg, bc->bc[0] + 1);
|
|
memcpy(p, bc->bc, bc->bc[0] + 1);
|
|
bc->Flags |= FLG_BC_SENT_CID;
|
|
setup->CHANNEL_ID = msg_put(msg, 2);
|
|
setup->CHANNEL_ID[0] = 1;
|
|
setup->CHANNEL_ID[1] = 0x88 | bc->channel;
|
|
if (bc->display[0]) {
|
|
len = strlen(bc->display);
|
|
setup->DISPLAY = p = msg_put(msg, len+1);
|
|
*p++ = len;
|
|
strcpy(p, bc->display);
|
|
bc->display[0] = 0;
|
|
}
|
|
if (bc->nr[0]) {
|
|
setup->CALLING_PN = p = msg_put(msg, bc->nr[0] + 1);
|
|
memcpy(p, bc->nr, bc->nr[0] + 1);
|
|
}
|
|
if (bc->clisub[0]) {
|
|
setup->CALLING_SUB = p = msg_put(msg, bc->clisub[0] + 1);
|
|
memcpy(p, bc->clisub, bc->clisub[0] + 1);
|
|
bc->clisub[0] = 0;
|
|
}
|
|
if (bc->msn[0]) {
|
|
setup->CALLED_PN = p = msg_put(msg, bc->msn[0] + 1);;
|
|
memcpy(p, bc->msn, bc->msn[0] + 1);
|
|
}
|
|
if (bc->cldsub[0]) {
|
|
setup->CALLED_SUB = p = msg_put(msg, bc->cldsub[0] + 1);
|
|
memcpy(p, bc->cldsub, bc->cldsub[0] + 1);
|
|
bc->cldsub[0] = 0;
|
|
}
|
|
if (bc->fac[0]) {
|
|
setup->FACILITY = p = msg_put(msg, bc->fac[0] + 1);
|
|
memcpy(p, bc->fac, bc->fac[0] + 1);
|
|
bc->fac[0] = 0;
|
|
}
|
|
if (bc->uu[0]) {
|
|
setup->USER_USER = p = msg_put(msg, bc->uu[0] + 1);
|
|
memcpy(p, bc->uu, bc->uu[0] + 1);
|
|
bc->uu[0] = 0;
|
|
}
|
|
ret = -EINVAL;
|
|
if (bc->manager->man2stack)
|
|
ret = bc->manager->man2stack(bc->manager->nst, msg);
|
|
if (ret)
|
|
free_msg(msg);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
send_proceeding(bchannel_t *bc)
|
|
{
|
|
CALL_PROCEEDING_t *proc;
|
|
msg_t *msg;
|
|
int len, ret;
|
|
unsigned char *p;
|
|
|
|
msg = prep_l3data_msg(CC_PROCEEDING | REQUEST, bc->l3id,
|
|
sizeof(CALL_PROCEEDING_t), 128, NULL);
|
|
if (!msg)
|
|
return(-ENOMEM);
|
|
proc = (CALL_PROCEEDING_t *)(msg->data + mISDN_HEAD_SIZE);
|
|
pthread_mutex_lock(&bc->lock);
|
|
bc->cstate = BC_CSTATE_PROCEED;
|
|
if (!(bc->Flags & FLG_BC_SENT_CID)) {
|
|
bc->Flags |= FLG_BC_SENT_CID;
|
|
proc->CHANNEL_ID = msg_put(msg, 2);
|
|
proc->CHANNEL_ID[0] = 1;
|
|
proc->CHANNEL_ID[1] = 0x88 | bc->channel;
|
|
}
|
|
pthread_mutex_unlock(&bc->lock);
|
|
if (bc->display[0]) {
|
|
len = strlen(bc->display);
|
|
proc->DISPLAY = p = msg_put(msg, len+1);
|
|
*p++ = len;
|
|
strcpy(p, bc->display);
|
|
bc->display[0] = 0;
|
|
}
|
|
ret = -EINVAL;
|
|
if (bc->manager->man2stack)
|
|
ret = bc->manager->man2stack(bc->manager->nst, msg);
|
|
if (ret)
|
|
free_msg(msg);
|
|
if (bc->manager->application) {
|
|
bc->Flags |= FLG_BC_APPLICATION;
|
|
len = bc->manager->application(bc->manager, PR_APP_ICALL, bc);
|
|
dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__,
|
|
bc->channel, len);
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
send_alert(bchannel_t *bc)
|
|
{
|
|
ALERTING_t *at;
|
|
msg_t *msg;
|
|
int len, ret;
|
|
unsigned char *p;
|
|
|
|
dprint(DBGM_BC, "%s: bc%d flg(%x) display(%s)\n", __FUNCTION__,
|
|
bc->channel, bc->Flags, bc->display);
|
|
msg = prep_l3data_msg(CC_ALERTING | REQUEST, bc->l3id,
|
|
sizeof(ALERTING_t), 128, NULL);
|
|
if (!msg)
|
|
return(-ENOMEM);
|
|
at = (ALERTING_t *)(msg->data + mISDN_HEAD_SIZE);
|
|
pthread_mutex_lock(&bc->lock);
|
|
bc->cstate = BC_CSTATE_PROCEED;
|
|
if (!(bc->Flags & FLG_BC_SENT_CID)) {
|
|
bc->Flags |= FLG_BC_SENT_CID;
|
|
at->CHANNEL_ID = msg_put(msg, 2);
|
|
at->CHANNEL_ID[0] = 1;
|
|
at->CHANNEL_ID[1] = 0x88 | bc->channel;
|
|
}
|
|
if (bc->Flags & FLG_BC_PROGRESS) {
|
|
bc->Flags &= ~FLG_BC_PROGRESS;
|
|
set_tone(bc, FLG_BC_TONE_ALERT);
|
|
at->PROGRESS = p = msg_put(msg, 3);;
|
|
*p++ = 2;
|
|
*p++ = 0x80 | CAUSE_LOC_PNET_LOCUSER;
|
|
*p++ = 0x80 | PROGRESS_TONE;
|
|
setup_bchannel(bc);
|
|
}
|
|
pthread_mutex_unlock(&bc->lock);
|
|
if (bc->display[0]) {
|
|
len = strlen(bc->display);
|
|
at->DISPLAY = p = msg_put(msg, len+1);
|
|
*p++ = len;
|
|
strcpy(p, bc->display);
|
|
bc->display[0] = 0;
|
|
}
|
|
if (bc->fac[0]) {
|
|
at->FACILITY = p = msg_put(msg, bc->fac[0] + 1);
|
|
memcpy(p, bc->fac, bc->fac[0] + 1);
|
|
bc->fac[0] = 0;
|
|
}
|
|
if (bc->uu[0]) {
|
|
at->USER_USER = p = msg_put(msg, bc->uu[0] + 1);
|
|
memcpy(p, bc->uu, bc->uu[0] + 1);
|
|
bc->uu[0] = 0;
|
|
}
|
|
ret = -EINVAL;
|
|
if (bc->manager->man2stack)
|
|
ret = bc->manager->man2stack(bc->manager->nst, msg);
|
|
if (ret)
|
|
free_msg(msg);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
send_connect(bchannel_t *bc)
|
|
{
|
|
CONNECT_t *conn;
|
|
time_t tim;
|
|
struct tm *ts;
|
|
msg_t *msg;
|
|
int len, ret;
|
|
unsigned char *p;
|
|
|
|
msg = prep_l3data_msg(CC_CONNECT | REQUEST, bc->l3id,
|
|
sizeof(CONNECT_t), 128, NULL);
|
|
if (!msg)
|
|
return(-ENOMEM);
|
|
conn = (CONNECT_t *)(msg->data + mISDN_HEAD_SIZE);
|
|
pthread_mutex_lock(&bc->lock);
|
|
bc->cstate = BC_CSTATE_PROCEED;
|
|
bc->Flags &= ~FLG_BC_TONE;
|
|
if (!(bc->Flags & FLG_BC_SENT_CID)) {
|
|
bc->Flags |= FLG_BC_SENT_CID;
|
|
conn->CHANNEL_ID = msg_put(msg, 2);
|
|
conn->CHANNEL_ID[0] = 1;
|
|
conn->CHANNEL_ID[1] = 0x88 | bc->channel;
|
|
}
|
|
pthread_mutex_unlock(&bc->lock);
|
|
if (bc->display[0]) {
|
|
len = strlen(bc->display);
|
|
conn->DISPLAY = p = msg_put(msg, len+1);
|
|
*p++ = len;
|
|
strcpy(p, bc->display);
|
|
bc->display[0] = 0;
|
|
}
|
|
if (bc->fac[0]) {
|
|
conn->FACILITY = p = msg_put(msg, bc->fac[0] + 1);
|
|
memcpy(p, bc->fac, bc->fac[0] + 1);
|
|
bc->fac[0] = 0;
|
|
}
|
|
if (bc->uu[0]) {
|
|
conn->USER_USER = p = msg_put(msg, bc->uu[0] + 1);
|
|
memcpy(p, bc->uu, bc->uu[0] + 1);
|
|
bc->uu[0] = 0;
|
|
}
|
|
time(&tim);
|
|
ts = localtime(&tim);
|
|
if (ts->tm_year > 99)
|
|
ts->tm_year -=100;
|
|
conn->DATE = p = msg_put(msg, 6);
|
|
*p++ = 5;
|
|
*p++ = ts->tm_year;
|
|
*p++ = ts->tm_mon+1;
|
|
*p++ = ts->tm_mday;
|
|
*p++ = ts->tm_hour;
|
|
*p++ = ts->tm_min;
|
|
|
|
ret = -EINVAL;
|
|
if (bc->manager->man2stack)
|
|
ret = bc->manager->man2stack(bc->manager->nst, msg);
|
|
if (ret)
|
|
free_msg(msg);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
send_connect_ack(bchannel_t *bc)
|
|
{
|
|
CONNECT_ACKNOWLEDGE_t *ca;
|
|
msg_t *msg;
|
|
int len, ret;
|
|
unsigned char *p;
|
|
|
|
msg = prep_l3data_msg(CC_CONNECT | RESPONSE, bc->l3id,
|
|
sizeof(CONNECT_ACKNOWLEDGE_t), 128, NULL);
|
|
if (!msg)
|
|
return(-ENOMEM);
|
|
setup_bchannel(bc);
|
|
ca = (CONNECT_ACKNOWLEDGE_t *)(msg->data + mISDN_HEAD_SIZE);
|
|
pthread_mutex_lock(&bc->lock);
|
|
bc->cstate = BC_CSTATE_ACTIV;
|
|
bc->Flags &= ~FLG_BC_TONE;
|
|
if (!(bc->Flags & FLG_BC_SENT_CID)) {
|
|
bc->Flags |= FLG_BC_SENT_CID;
|
|
ca->CHANNEL_ID = msg_put(msg, 2);
|
|
ca->CHANNEL_ID[0] = 1;
|
|
ca->CHANNEL_ID[1] = 0x88 | bc->channel;
|
|
}
|
|
pthread_mutex_unlock(&bc->lock);
|
|
if (bc->display[0]) {
|
|
len = strlen(bc->display);
|
|
ca->DISPLAY = p = msg_put(msg, len+1);
|
|
*p++ = len;
|
|
strcpy(p, bc->display);
|
|
bc->display[0] = 0;
|
|
}
|
|
ret = -EINVAL;
|
|
if (bc->manager->man2stack)
|
|
ret = bc->manager->man2stack(bc->manager->nst, msg);
|
|
if (ret)
|
|
free_msg(msg);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
send_disc(bchannel_t *bc)
|
|
{
|
|
DISCONNECT_t *disc;
|
|
msg_t *msg;
|
|
int len, ret;
|
|
unsigned char *p;
|
|
|
|
msg = prep_l3data_msg(CC_DISCONNECT | REQUEST, bc->l3id,
|
|
sizeof(DISCONNECT_t), 128, NULL);
|
|
if (!msg)
|
|
return(-ENOMEM);
|
|
disc = (DISCONNECT_t *)(msg->data + mISDN_HEAD_SIZE);
|
|
pthread_mutex_lock(&bc->lock);
|
|
bc->cstate = BC_CSTATE_DISCONNECT;
|
|
pthread_mutex_unlock(&bc->lock);
|
|
if (bc->cause_val) {
|
|
disc->CAUSE = p = msg_put(msg, 3);
|
|
*p++ = 2;
|
|
*p++ = 0x80 | bc->cause_loc;
|
|
*p++ = 0x80 | bc->cause_val;
|
|
}
|
|
if (bc->Flags & FLG_BC_PROGRESS) {
|
|
set_tone(bc, FLG_BC_TONE_BUSY);
|
|
disc->PROGRESS = p = msg_put(msg, 3);;
|
|
*p++ = 2;
|
|
*p++ = 0x80 | CAUSE_LOC_PNET_LOCUSER;
|
|
*p++ = 0x80 | PROGRESS_TONE;
|
|
setup_bchannel(bc);
|
|
}
|
|
if (bc->display[0]) {
|
|
len = strlen(bc->display);
|
|
disc->DISPLAY = p = msg_put(msg, len+1);
|
|
*p++ = len;
|
|
strcpy(p, bc->display);
|
|
bc->display[0] = 0;
|
|
}
|
|
if (bc->fac[0]) {
|
|
disc->FACILITY = p = msg_put(msg, bc->fac[0] + 1);
|
|
memcpy(p, bc->fac, bc->fac[0] + 1);
|
|
bc->fac[0] = 0;
|
|
}
|
|
if (bc->uu[0]) {
|
|
disc->USER_USER = p = msg_put(msg, bc->uu[0] + 1);
|
|
memcpy(p, bc->uu, bc->uu[0] + 1);
|
|
bc->uu[0] = 0;
|
|
}
|
|
ret = -EINVAL;
|
|
if (bc->manager->man2stack)
|
|
ret = bc->manager->man2stack(bc->manager->nst, msg);
|
|
if (ret)
|
|
free_msg(msg);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
send_facility(bchannel_t *bc)
|
|
{
|
|
FACILITY_t *fac;
|
|
msg_t *msg;
|
|
int len, ret;
|
|
unsigned char *p;
|
|
|
|
msg = prep_l3data_msg(CC_FACILITY | REQUEST, bc->l3id,
|
|
sizeof(FACILITY_t), 128, NULL);
|
|
if (!msg)
|
|
return(-ENOMEM);
|
|
fac = (FACILITY_t *)(msg->data + mISDN_HEAD_SIZE);
|
|
if (bc->display[0]) {
|
|
len = strlen(bc->display);
|
|
fac->DISPLAY = p = msg_put(msg, len+1);
|
|
*p++ = len;
|
|
strcpy(p, bc->display);
|
|
bc->display[0] = 0;
|
|
}
|
|
if (bc->fac[0]) {
|
|
fac->FACILITY = p = msg_put(msg, bc->fac[0] + 1);
|
|
memcpy(p, bc->fac, bc->fac[0] + 1);
|
|
bc->fac[0] = 0;
|
|
}
|
|
ret = -EINVAL;
|
|
if (bc->manager->man2stack)
|
|
ret = bc->manager->man2stack(bc->manager->nst, msg);
|
|
if (ret)
|
|
free_msg(msg);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
send_userinfo(bchannel_t *bc)
|
|
{
|
|
USER_INFORMATION_t *ui;
|
|
msg_t *msg;
|
|
int ret;
|
|
unsigned char *p;
|
|
|
|
msg = prep_l3data_msg(CC_USER_INFORMATION | REQUEST, bc->l3id,
|
|
sizeof(USER_INFORMATION_t), 128, NULL);
|
|
if (!msg)
|
|
return(-ENOMEM);
|
|
ui = (USER_INFORMATION_t *)(msg->data + mISDN_HEAD_SIZE);
|
|
if (bc->uu[0]) {
|
|
ui->USER_USER = p = msg_put(msg, bc->uu[0] + 1);
|
|
memcpy(p, bc->uu, bc->uu[0] + 1);
|
|
bc->uu[0] = 0;
|
|
}
|
|
ret = -EINVAL;
|
|
if (bc->manager->man2stack)
|
|
ret = bc->manager->man2stack(bc->manager->nst, msg);
|
|
if (ret)
|
|
free_msg(msg);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
send_rel(bchannel_t *bc)
|
|
{
|
|
RELEASE_t *rel;
|
|
msg_t *msg;
|
|
int len, ret;
|
|
unsigned char *p;
|
|
|
|
msg = prep_l3data_msg(CC_RELEASE | REQUEST, bc->l3id,
|
|
sizeof(RELEASE_t), 128, NULL);
|
|
if (!msg)
|
|
return(-ENOMEM);
|
|
rel = (RELEASE_t *)(msg->data + mISDN_HEAD_SIZE);
|
|
pthread_mutex_lock(&bc->lock);
|
|
bc->cstate = BC_CSTATE_RELEASE;
|
|
pthread_mutex_unlock(&bc->lock);
|
|
if (bc->cause_val) {
|
|
rel->CAUSE = p = msg_put(msg, 3);
|
|
*p++ = 2;
|
|
*p++ = 0x80 | bc->cause_loc;
|
|
*p++ = 0x80 | bc->cause_val;
|
|
}
|
|
if (bc->display[0]) {
|
|
len = strlen(bc->display);
|
|
rel->DISPLAY = p = msg_put(msg, len+1);
|
|
*p++ = len;
|
|
strcpy(p, bc->display);
|
|
bc->display[0] = 0;
|
|
}
|
|
if (bc->fac[0]) {
|
|
rel->FACILITY = p = msg_put(msg, bc->fac[0] + 1);
|
|
memcpy(p, bc->fac, bc->fac[0] + 1);
|
|
bc->fac[0] = 0;
|
|
}
|
|
if (bc->uu[0]) {
|
|
rel->USER_USER = p = msg_put(msg, bc->uu[0] + 1);
|
|
memcpy(p, bc->uu, bc->uu[0] + 1);
|
|
bc->uu[0] = 0;
|
|
}
|
|
ret = -EINVAL;
|
|
if (bc->manager->man2stack)
|
|
ret = bc->manager->man2stack(bc->manager->nst, msg);
|
|
if (ret)
|
|
free_msg(msg);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
send_relcomp(bchannel_t *bc, int l3id, int cause) {
|
|
RELEASE_COMPLETE_t *rc;
|
|
msg_t *msg;
|
|
int ret, len;
|
|
unsigned char *p;
|
|
|
|
msg = prep_l3data_msg(CC_RELEASE_COMPLETE | REQUEST, l3id,
|
|
sizeof(RELEASE_COMPLETE_t), 128, NULL);
|
|
if (!msg)
|
|
return(-ENOMEM);
|
|
rc = (RELEASE_COMPLETE_t *)(msg->data + mISDN_HEAD_SIZE);
|
|
clear_bc(bc);
|
|
pthread_mutex_lock(&bc->lock);
|
|
bc->cstate = BC_CSTATE_NULL;
|
|
pthread_mutex_unlock(&bc->lock);
|
|
if (cause) {
|
|
bc->cause_loc = CAUSE_LOC_PNET_LOCUSER;
|
|
bc->cause_val = cause;
|
|
rc->CAUSE = msg_put(msg, 3);
|
|
rc->CAUSE[0] = 2;
|
|
rc->CAUSE[1] = 0x80 | CAUSE_LOC_PNET_LOCUSER;
|
|
rc->CAUSE[2] = 0x80 | cause;
|
|
}
|
|
if (bc->display[0]) {
|
|
len = strlen(bc->display);
|
|
rc->DISPLAY = p = msg_put(msg, len+1);
|
|
*p++ = len;
|
|
strcpy(p, bc->display);
|
|
bc->display[0] = 0;
|
|
}
|
|
if (bc->fac[0]) {
|
|
rc->FACILITY = p = msg_put(msg, bc->fac[0] + 1);
|
|
memcpy(p, bc->fac, bc->fac[0] + 1);
|
|
bc->fac[0] = 0;
|
|
}
|
|
if (bc->uu[0]) {
|
|
rc->USER_USER = p = msg_put(msg, bc->uu[0] + 1);
|
|
memcpy(p, bc->uu, bc->uu[0] + 1);
|
|
bc->uu[0] = 0;
|
|
}
|
|
ret = -EINVAL;
|
|
if (bc->manager->man2stack)
|
|
ret = bc->manager->man2stack(bc->manager->nst, msg);
|
|
if (ret)
|
|
free_msg(msg);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
info_ind(bchannel_t *bc, void *arg)
|
|
{
|
|
INFORMATION_t *info = arg;
|
|
int ret;
|
|
|
|
if (info->CALLED_PN) {
|
|
set_tone(bc, FLG_BC_TONE_SILENCE);
|
|
add_nr(bc, info->CALLED_PN);
|
|
ret = match_nr(bc->manager, bc->nr, &bc->usednr);
|
|
dprint(DBGM_BC, "%s: match_nr ret(%d)\n", __FUNCTION__,
|
|
ret);
|
|
if (!ret) {
|
|
send_proceeding(bc);
|
|
} else if (ret == 2 || info->COMPLETE) {
|
|
bc->Flags |= FLG_BC_PROGRESS;
|
|
set_tone(bc, FLG_BC_TONE_BUSY);
|
|
bc->cause_loc = CAUSE_LOC_PNET_LOCUSER;
|
|
bc->cause_val = CAUSE_UNASSIGNED_NUMBER;
|
|
send_disc(bc);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
setup_ind(bchannel_t *bc, int l3id, void *arg)
|
|
{
|
|
SETUP_t *setup = arg;
|
|
int cause,ret;
|
|
|
|
if (bc->cstate != BC_CSTATE_ICALL)
|
|
return(send_relcomp(bc, l3id, CAUSE_NOTCOMPAT_STATE));
|
|
bc->l3id = l3id;
|
|
cause = CAUSE_INCOMPATIBLE_DEST;
|
|
if (setup->BEARER) {
|
|
memcpy(bc->bc, setup->BEARER, setup->BEARER[0] +1);
|
|
if (setup->BEARER[0] == 3) {
|
|
if ((setup->BEARER[1] == 0x80) &&
|
|
(setup->BEARER[2] == 0x90) &&
|
|
(setup->BEARER[3] == 0xa3)) {
|
|
cause = 0;
|
|
bc->l1_prot = ISDN_PID_L1_B_64TRANS;
|
|
}
|
|
}
|
|
} else
|
|
cause = CAUSE_MANDATORY_IE_MISS;
|
|
if (cause)
|
|
return(send_relcomp(bc, bc->l3id, cause));
|
|
if (setup->CALLING_PN)
|
|
memcpy(bc->msn, setup->CALLING_PN, setup->CALLING_PN[0] + 1);
|
|
else
|
|
bc->msn[0] = 0;
|
|
if (setup->CALLING_SUB)
|
|
memcpy(bc->clisub, setup->CALLING_SUB,
|
|
setup->CALLING_SUB[0] + 1);
|
|
else
|
|
bc->clisub[0] = 0;
|
|
if (setup->CALLED_SUB)
|
|
memcpy(bc->cldsub, setup->CALLED_SUB,
|
|
setup->CALLED_SUB[0] + 1);
|
|
else
|
|
bc->cldsub[0] = 0;
|
|
if (setup->FACILITY)
|
|
memcpy(bc->fac, setup->FACILITY, setup->FACILITY[0] + 1);
|
|
else
|
|
bc->fac[0] = 0;
|
|
if (setup->USER_USER)
|
|
memcpy(bc->uu, setup->USER_USER, setup->USER_USER[0] + 1);
|
|
else
|
|
bc->uu[0] = 0;
|
|
if (!bc->sbuf)
|
|
bc->sbuf = init_ibuffer(2048);
|
|
set_tone(bc, FLG_BC_TONE_DIAL);
|
|
if (!setup->CALLED_PN) {
|
|
bc->Flags |= FLG_BC_PROGRESS;
|
|
send_setup_ack(bc);
|
|
} else {
|
|
set_tone(bc, FLG_BC_TONE_SILENCE);
|
|
bc->Flags |= FLG_BC_PROGRESS;
|
|
add_nr(bc, setup->CALLED_PN);
|
|
ret = match_nr(bc->manager, bc->nr, &bc->usednr);
|
|
dprint(DBGM_BC, "%s: match_nr ret(%d)\n", __FUNCTION__,
|
|
ret);
|
|
if (!ret) {
|
|
send_proceeding(bc);
|
|
} else if (ret == 2 || setup->COMPLETE) {
|
|
return(send_relcomp(bc, bc->l3id, CAUSE_UNASSIGNED_NUMBER));
|
|
} else {
|
|
send_setup_ack(bc);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
conn_ind(bchannel_t *bc, void *arg)
|
|
{
|
|
CONNECT_t *conn = arg;
|
|
int ret;
|
|
|
|
if (conn) {
|
|
if (conn->FACILITY)
|
|
memcpy(bc->fac, conn->FACILITY, conn->FACILITY[0] + 1);
|
|
else
|
|
bc->fac[0] = 0;
|
|
if (conn->USER_USER)
|
|
memcpy(bc->uu, conn->USER_USER, conn->USER_USER[0] + 1);
|
|
else
|
|
bc->uu[0] = 0;
|
|
}
|
|
if ((bc->Flags & FLG_BC_APPLICATION) && bc->manager->application) {
|
|
setup_bchannel(bc);
|
|
ret = bc->manager->application(bc->manager, PR_APP_CONNECT, bc);
|
|
dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__,
|
|
bc->channel, ret);
|
|
if (!ret) {
|
|
send_connect_ack(bc);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
alert_ind(bchannel_t *bc, void *arg)
|
|
{
|
|
ALERTING_t *alert = arg;
|
|
int ret;
|
|
|
|
pthread_mutex_lock(&bc->lock);
|
|
bc->cstate = BC_CSTATE_ALERTING;
|
|
pthread_mutex_unlock(&bc->lock);
|
|
if (alert->FACILITY)
|
|
memcpy(bc->fac, alert->FACILITY, alert->FACILITY[0] + 1);
|
|
else
|
|
bc->fac[0] = 0;
|
|
if (alert->USER_USER)
|
|
memcpy(bc->uu, alert->USER_USER, alert->USER_USER[0] + 1);
|
|
else
|
|
bc->uu[0] = 0;
|
|
if ((bc->Flags & FLG_BC_APPLICATION) && bc->manager->application) {
|
|
ret = bc->manager->application(bc->manager, PR_APP_ALERT, bc);
|
|
dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__,
|
|
bc->channel, ret);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
facility_ind(bchannel_t *bc, void *arg)
|
|
{
|
|
FACILITY_t *fac = arg;
|
|
int ret;
|
|
|
|
if (fac) {
|
|
if (fac->FACILITY)
|
|
memcpy(bc->fac, fac->FACILITY, fac->FACILITY[0] + 1);
|
|
else
|
|
bc->fac[0] = 0;
|
|
}
|
|
if ((bc->Flags & FLG_BC_APPLICATION) && bc->manager->application) {
|
|
ret = bc->manager->application(bc->manager, PR_APP_FACILITY, bc);
|
|
dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__,
|
|
bc->channel, ret);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
userinfo_ind(bchannel_t *bc, void *arg)
|
|
{
|
|
USER_INFORMATION_t *ui = arg;
|
|
int ret;
|
|
|
|
if (ui) {
|
|
if (ui->USER_USER)
|
|
memcpy(bc->uu, ui->USER_USER, ui->USER_USER[0] + 1);
|
|
else
|
|
bc->uu[0] = 0;
|
|
}
|
|
if ((bc->Flags & FLG_BC_APPLICATION) && bc->manager->application) {
|
|
ret = bc->manager->application(bc->manager, PR_APP_USERUSER, bc);
|
|
dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__,
|
|
bc->channel, ret);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
disc_ind(bchannel_t *bc, void *arg)
|
|
{
|
|
DISCONNECT_t *disc = arg;
|
|
int cause = 0;
|
|
int ret;
|
|
|
|
if (disc->CAUSE) {
|
|
if (disc->CAUSE[0] >1) {
|
|
dprint(DBGM_BC, "%s: loc(%d) cause(%d)\n", __FUNCTION__,
|
|
disc->CAUSE[1] & 0xf, disc->CAUSE[2] & 0x7f);
|
|
bc->cause_loc = disc->CAUSE[1] & 0xf;
|
|
bc->cause_val = disc->CAUSE[2] & 0x7f;
|
|
} else {
|
|
dprint(DBGM_BC, "%s: cause len %d\n", __FUNCTION__,
|
|
disc->CAUSE[0]);
|
|
cause = CAUSE_INVALID_CONTENTS;
|
|
}
|
|
} else {
|
|
cause = CAUSE_MANDATORY_IE_MISS;
|
|
}
|
|
if (cause) {
|
|
bc->cause_loc = CAUSE_LOC_PNET_LOCUSER;
|
|
bc->cause_val = cause;
|
|
}
|
|
pthread_mutex_lock(&bc->lock);
|
|
bc->cstate = BC_CSTATE_DISCONNECT;
|
|
pthread_mutex_unlock(&bc->lock);
|
|
send_rel(bc);
|
|
if (disc->FACILITY)
|
|
memcpy(bc->fac, disc->FACILITY, disc->FACILITY[0] + 1);
|
|
else
|
|
bc->fac[0] = 0;
|
|
if (disc->USER_USER)
|
|
memcpy(bc->uu, disc->USER_USER, disc->USER_USER[0] + 1);
|
|
else
|
|
bc->uu[0] = 0;
|
|
if ((bc->Flags & FLG_BC_APPLICATION) && bc->manager->application) {
|
|
ret = bc->manager->application(bc->manager, PR_APP_HANGUP, bc);
|
|
dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__,
|
|
bc->channel, ret);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
rel_ind(bchannel_t *bc, void *arg)
|
|
{
|
|
RELEASE_t *rel = arg;
|
|
int ret;
|
|
|
|
if (rel) {
|
|
if (rel->FACILITY)
|
|
memcpy(bc->fac, rel->FACILITY, rel->FACILITY[0] + 1);
|
|
else
|
|
bc->fac[0] = 0;
|
|
if (rel->USER_USER)
|
|
memcpy(bc->uu, rel->USER_USER, rel->USER_USER[0] + 1);
|
|
else
|
|
bc->uu[0] = 0;
|
|
if (rel->CAUSE) {
|
|
if (rel->CAUSE[0] > 1) {
|
|
dprint(DBGM_BC, "%s: loc(%d) cause(%d)\n", __FUNCTION__,
|
|
rel->CAUSE[1] & 0xf, rel->CAUSE[2] & 0x7f);
|
|
bc->cause_loc = rel->CAUSE[1] & 0xf;
|
|
bc->cause_val = rel->CAUSE[2] & 0x7f;
|
|
}
|
|
}
|
|
}
|
|
if ((bc->Flags & FLG_BC_APPLICATION) && bc->manager->application) {
|
|
ret = bc->manager->application(bc->manager, PR_APP_CLEAR, bc);
|
|
dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__,
|
|
bc->channel, ret);
|
|
}
|
|
clear_bc(bc);
|
|
pthread_mutex_lock(&bc->lock);
|
|
bc->cstate = BC_CSTATE_NULL;
|
|
pthread_mutex_unlock(&bc->lock);
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
relcmpl_ind(bchannel_t *bc, void *arg)
|
|
{
|
|
RELEASE_COMPLETE_t *rc = arg;
|
|
int ret;
|
|
|
|
if (rc) {
|
|
if (rc->FACILITY)
|
|
memcpy(bc->fac, rc->FACILITY, rc->FACILITY[0] + 1);
|
|
else
|
|
bc->fac[0] = 0;
|
|
if (rc->USER_USER)
|
|
memcpy(bc->uu, rc->USER_USER, rc->USER_USER[0] + 1);
|
|
else
|
|
bc->uu[0] = 0;
|
|
if (rc->CAUSE) {
|
|
if (rc->CAUSE[0] > 1) {
|
|
dprint(DBGM_BC, "%s: loc(%d) cause(%d)\n", __FUNCTION__,
|
|
rc->CAUSE[1] & 0xf, rc->CAUSE[2] & 0x7f);
|
|
bc->cause_loc = rc->CAUSE[1] & 0xf;
|
|
bc->cause_val = rc->CAUSE[2] & 0x7f;
|
|
}
|
|
}
|
|
}
|
|
if ((bc->Flags & FLG_BC_APPLICATION) && bc->manager->application) {
|
|
ret = bc->manager->application(bc->manager, PR_APP_CLEAR, bc);
|
|
dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__,
|
|
bc->channel, ret);
|
|
}
|
|
clear_bc(bc);
|
|
pthread_mutex_lock(&bc->lock);
|
|
bc->cstate = BC_CSTATE_NULL;
|
|
pthread_mutex_unlock(&bc->lock);
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
relcr_ind(bchannel_t *bc, void *arg)
|
|
{
|
|
int ret, *err = arg;
|
|
|
|
dprint(DBGM_BC, "%s: bc%d cause(%x)\n", __FUNCTION__,
|
|
bc->channel, *err);
|
|
if ((bc->Flags & FLG_BC_APPLICATION) && bc->manager->application) {
|
|
ret = bc->manager->application(bc->manager, PR_APP_CLEAR, bc);
|
|
dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__,
|
|
bc->channel, ret);
|
|
}
|
|
if (bc->cstate != BC_CSTATE_NULL) {
|
|
clear_bc(bc);
|
|
pthread_mutex_lock(&bc->lock);
|
|
bc->cstate = BC_CSTATE_NULL;
|
|
pthread_mutex_unlock(&bc->lock);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static void
|
|
cleanup_bchannel(void *arg)
|
|
{
|
|
bchannel_t *bc = arg;
|
|
|
|
dprint(DBGM_BC,"%s: bc %d\n", __FUNCTION__, bc->channel);
|
|
pthread_mutex_lock(&bc->lock);
|
|
msg_queue_purge(&bc->workq);
|
|
bc->smsg = NULL;
|
|
free_ibuffer(bc->sbuf);
|
|
bc->sbuf = NULL;
|
|
free_ibuffer(bc->rbuf);
|
|
bc->rbuf = NULL;
|
|
bc->cstate = BC_CSTATE_NULL;
|
|
while(1)
|
|
if (!sem_trywait(&bc->work))
|
|
break;
|
|
pthread_mutex_unlock(&bc->lock);
|
|
dprint(DBGM_BC,"%s: bc %d end\n", __FUNCTION__, bc->channel);
|
|
}
|
|
|
|
static void *
|
|
main_bc_task(void *arg)
|
|
{
|
|
bchannel_t *bc = arg;
|
|
msg_t *msg;
|
|
int ret, id;
|
|
mISDN_head_t *hh;
|
|
|
|
pthread_cleanup_push(cleanup_bchannel, (void *)bc);
|
|
dprint(DBGM_BC,"%s bc %d\n", __FUNCTION__, bc->channel);
|
|
while(1) {
|
|
|
|
sem_wait(&bc->work);
|
|
if (bc->Flags & FLG_BC_TERMINATE)
|
|
pthread_exit(NULL);
|
|
if (!bc->smsg) {
|
|
if (bc->Flags & FLG_BC_TONE)
|
|
tone_handler(bc);
|
|
if (ibuf_usedcount(bc->sbuf))
|
|
b_send(bc);
|
|
}
|
|
msg = msg_dequeue(&bc->workq);
|
|
if (msg) {
|
|
hh = (mISDN_head_t *)msg->data;
|
|
msg_pull(msg, mISDN_HEAD_SIZE);
|
|
dprint(DBGM_BC,"%s: bc%d st(%d/%d) prim(%x) dinfo(%x) len(%d)\n", __FUNCTION__,
|
|
bc->channel, bc->cstate, bc->bstate, hh->prim, hh->dinfo, msg->len);
|
|
ret = -EINVAL;
|
|
switch(hh->prim) {
|
|
case PH_DATA | INDICATION:
|
|
ret = do_b_data_ind(bc, hh, msg);
|
|
break;
|
|
case PH_DATA | CONFIRM:
|
|
ret = do_b_data_cnf(bc, hh, msg);
|
|
break;
|
|
case PH_ACTIVATE | INDICATION:
|
|
case PH_ACTIVATE | CONFIRM:
|
|
ret = do_b_activated(bc, hh, msg);
|
|
break;
|
|
case PH_DEACTIVATE | INDICATION:
|
|
case PH_DEACTIVATE | CONFIRM:
|
|
ret = do_b_deactivated(bc, hh, msg);
|
|
break;
|
|
case BC_SETUP | CONFIRM:
|
|
ret = do_b_setup_conf(bc, hh, msg);
|
|
break;
|
|
case BC_SETUP | SUB_ERROR:
|
|
case BC_CLEANUP | SUB_ERROR:
|
|
wprint("%s:ch%d %x error %x\n", __FUNCTION__,
|
|
bc->channel, hh->prim, *((int *)msg->data));
|
|
case BC_CLEANUP | CONFIRM:
|
|
ret = do_b_cleanup_conf(bc, hh, msg);
|
|
break;
|
|
|
|
case CC_SETUP | INDICATION:
|
|
setup_ind(bc, hh->dinfo, msg->data);
|
|
break;
|
|
case CC_SETUP | CONFIRM:
|
|
bc->l3id = *((int *)msg->data);
|
|
break;
|
|
case CC_NEW_CR | INDICATION:
|
|
pthread_mutex_lock(&bc->lock);
|
|
id = *((int *)msg->data);
|
|
msg_push(msg, mISDN_HEAD_SIZE);
|
|
if (bc->manager && bc->manager->man2stack)
|
|
ret = bc->manager->man2stack(
|
|
bc->manager->nst, msg);
|
|
bc->l3id = id;
|
|
pthread_mutex_unlock(&bc->lock);
|
|
break;
|
|
case CC_RELEASE_CR | INDICATION:
|
|
relcr_ind(bc, msg->data);
|
|
break;
|
|
case CC_INFORMATION | INDICATION:
|
|
info_ind(bc, msg->data);
|
|
break;
|
|
case CC_ALERTING | INDICATION:
|
|
alert_ind(bc, msg->data);
|
|
break;
|
|
case CC_CONNECT | INDICATION:
|
|
conn_ind(bc, msg->data);
|
|
break;
|
|
case CC_FACILITY | INDICATION:
|
|
facility_ind(bc, msg->data);
|
|
break;
|
|
case CC_USER_INFORMATION | INDICATION:
|
|
userinfo_ind(bc, msg->data);
|
|
break;
|
|
case CC_DISCONNECT | INDICATION:
|
|
disc_ind(bc, msg->data);
|
|
break;
|
|
case CC_RELEASE | INDICATION:
|
|
rel_ind(bc, msg->data);
|
|
break;
|
|
case CC_RELEASE | CONFIRM:
|
|
rel_ind(bc, NULL);
|
|
break;
|
|
case CC_RELEASE_COMPLETE | INDICATION:
|
|
relcmpl_ind(bc, msg->data);
|
|
break;
|
|
|
|
case CC_SETUP | REQUEST:
|
|
send_setup(bc);
|
|
break;
|
|
case CC_ALERTING | REQUEST:
|
|
send_alert(bc);
|
|
break;
|
|
case CC_CONNECT | REQUEST:
|
|
send_connect(bc);
|
|
break;
|
|
case CC_DISCONNECT | REQUEST:
|
|
send_disc(bc);
|
|
break;
|
|
case CC_FACILITY | REQUEST:
|
|
send_facility(bc);
|
|
break;
|
|
case CC_USER_INFORMATION | REQUEST:
|
|
send_userinfo(bc);
|
|
break;
|
|
case CC_TIMEOUT | INDICATION:
|
|
dprint(DBGM_MAN,"%s: bc%d got CC_TIMEOUT\n", __FUNCTION__,
|
|
bc->channel);
|
|
break;
|
|
default:
|
|
wprint("%s:ch%d unhandled prim(%x) di(%x)\n", __FUNCTION__,
|
|
bc->channel, hh->prim, hh->dinfo);
|
|
break;
|
|
}
|
|
if (ret)
|
|
free_msg(msg);
|
|
}
|
|
}
|
|
pthread_cleanup_pop(1);
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
int
|
|
init_bchannel(bchannel_t *bc, int channel)
|
|
{
|
|
int ret;
|
|
|
|
bc->channel = channel;
|
|
msg_queue_init(&bc->workq);
|
|
bc->cstate = BC_CSTATE_NULL;
|
|
bc->bstate = BC_BSTATE_NULL;
|
|
pthread_mutex_init(&bc->lock, NULL);
|
|
sem_init (&bc->work, 0, 0);
|
|
ret = pthread_create(&bc->tid, NULL, main_bc_task, (void *)bc);
|
|
dprint(DBGM_BC, "%s: create bc%d thread %ld ret %d\n", __FUNCTION__,
|
|
channel, bc->tid, ret);
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
term_bchannel(bchannel_t *bc)
|
|
{
|
|
dprint(DBGM_BC, "%s: bc%d\n", __FUNCTION__, bc->channel);
|
|
bc->Flags |= FLG_BC_TERMINATE;
|
|
sem_post(&bc->work);
|
|
return(0);
|
|
}
|