#include #include #include #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); }