mISDNuser/capi20/mplci.c

394 lines
10 KiB
C

/*
* mplci.c
*
* Author Karsten Keil <kkeil@linux-pingi.de>
*
* Copyright 2011 by Karsten Keil <kkeil@linux-pingi.de>
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* version 2.1 as published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU LESSER GENERAL PUBLIC LICENSE for more details.
*
*/
#include <sched.h>
#include "m_capi.h"
#include "mc_buffer.h"
#include <mISDN/q931.h>
struct mPLCI *new_mPLCI(struct pController *pc, unsigned int pid)
{
struct mPLCI *plci;
int ret;
plci = calloc(1, sizeof(*plci));
if (!plci) {
eprint("Controller:%x PID:%x no memory for PLCI\n", pc->mNr, pid);
return NULL;
}
ret = init_cobj_registered(&plci->cobj, &pc->cobjPLCI, Cot_PLCI, 0x01ff);
if (ret) {
eprint("Controller:%x PID:%x Error on init - no IDs left\n", pc->mNr, pid);
free(plci);
plci = NULL;
} else {
plci->cobj.id2 = pid;
plci->pc = pc;
}
return plci;
}
void dump_controller_plci(struct pController *pc)
{
struct mCAPIobj *co;
struct mPLCI *plci;
pthread_rwlock_rdlock(&pc->cobjPLCI.lock);
co = pc->cobjPLCI.listhead;
while (co) {
plci = container_of(co, struct mPLCI, cobj);
iprint("%s refcnt:%d number lPLCI:%d %s%s\n", CAPIobjIDstr(co), co->refcnt, co->itemcnt,
plci->alerting ? "alerting " : "", plci->outgoing ? "outgoing" : "incoming");
pthread_rwlock_rdlock(&co->lock);
if (co->listhead)
dump_Lplcis(container_of(co->listhead, struct lPLCI, cobj));
pthread_rwlock_unlock(&co->lock);
co = co->next;
}
pthread_rwlock_unlock(&pc->cobjPLCI.lock);
}
void Free_PLCI(struct mCAPIobj *co)
{
struct mPLCI *plci = container_of(co, struct mPLCI, cobj);
if (!co->cleaned) {
delist_cobj(co);
co->cleaned = 1;
}
plci->pc = NULL;
if (co->parent) {
put_cobj(co->parent);
co->parent = NULL;
}
dprint(MIDEBUG_PLCI, "%s: freeing done\n", CAPIobjIDstr(co));
free_capiobject(co, plci);
}
static void cleanup_mPLCI(struct mPLCI *plci)
{
struct pController *pc = plci->pc;
struct mCAPIobj *co;
struct lPLCI *lp;
plci->cobj.cleaned = 1;
co = get_next_cobj(&plci->cobj, NULL);
while (co) {
lp = container_of(co, struct lPLCI, cobj);
cleanup_lPLCI(lp);
co = get_next_cobj(&plci->cobj, co);
}
if (plci->cobj.itemcnt) {
wprint("%s: lPLCI count %d not zero\n", CAPIobjIDstr(&plci->cobj), plci->cobj.itemcnt);
}
if (pc) {
plci->pc = NULL;
delist_cobj(&plci->cobj);
} else
eprint("%s: no pcontroller assigned\n", CAPIobjIDstr(&plci->cobj));
}
void plciDetachlPLCI(struct lPLCI *lp)
{
struct mPLCI *p;
int mt;
struct l3_msg *l3m;
p = p4lPLCI(lp);
if (!p) {
eprint("%s: detach no PLCI\n", CAPIobjIDstr(&lp->cobj));
return;
}
if (lp->rel_req) {
/* need to store cause for final answer */
if (lp->cause_loc == CAUSE_LOC_USER) {
dprint(MIDEBUG_PLCI, "%s: set final cause plci:#%d lplci:#%d\n",
CAPIobjIDstr(&lp->cobj), p->cause, lp->cause);
if (p->cause <= 0) {
p->cause = lp->cause;
} else if (lp->cause < p->cause) {
/* for now we prefer lower values, maybe need changed */
p->cause = lp->cause;
}
p->cause_loc = CAUSE_LOC_USER;
} else
wprint("%s: cause got owerwritten loc:#%d cause #%d - not stored\n",
CAPIobjIDstr(&lp->cobj), lp->cause_loc, lp->cause);
}
delist_cobj(&lp->cobj);
if (p->cobj.itemcnt == 0) {
if (p->cause > 0) {
dprint(MIDEBUG_PLCI, "%s: last lPLCI gone clear call cause #%d\n",
CAPIobjIDstr(&p->cobj), p->cause);
l3m = alloc_l3_msg();
if (!l3m) {
eprint("%s: disconnect not send no l3m\n", CAPIobjIDstr(&p->cobj));
} else {
if (p->alerting)
mt = MT_DISCONNECT;
else
mt = MT_RELEASE_COMPLETE;
mi_encode_cause(l3m, p->cause, p->cause_loc, 0, NULL);
plciL4L3(p, mt, l3m);
}
}
dprint(MIDEBUG_PLCI, "%s: All lPLCIs are gone remove PLCI now\n", CAPIobjIDstr(&p->cobj));
cleanup_mPLCI(p);
}
}
static void plciHandleSetupInd(struct mPLCI *plci, int pr, struct mc_buf *mc)
{
uint32_t CIPmask, cipm;
struct pController *pc;
struct mCAPIobj *co;
struct lController *lc;
struct lPLCI *lp;
uint8_t found = 0;
int cause = CAUSE_INCOMPATIBLE_DEST;
int ret, *fds, *cur;
if (!mc || !mc->l3m) {
eprint("%s: SETUP without message\n", CAPIobjIDstr(&plci->cobj));
return;
}
CIPmask = q931CIPMask(mc);
pc = plci->pc;
dprint(MIDEBUG_PLCI, "%s: check CIPMask(%08x) with controller CIPmask %08x chanIE:%s\n",
CAPIobjIDstr(&plci->cobj), CIPmask, pc->CIPmask, mc->l3m->channel_id ? "yes" : "no");
if (CIPmask & pc->CIPmask) {
/* at least one Application is listen for this service */
co = get_next_cobj(&pc->cobjLC, NULL);
while (co) {
lc = container_of(co, struct lController, cobj);
cipm = lc->CIPmask & CIPmask;
if ((lc->CIPmask & CIPmask) || (lc->CIPmask & 1)) {
ret = lPLCICreate(&lp, lc, plci, cipm);
if (ret == 0) {
found++;
put_cobj(&lp->cobj);
} else {
wprint("%s: cannot create lPLCI\n", CAPIobjIDstr(&plci->cobj));
}
}
co = get_next_cobj(&pc->cobjLC, co);
}
if (plci->cobj.itemcnt) {
/* at least one lplci was created */
fds = calloc(found, sizeof(int));
if (!fds)
eprint("%s: cannot allocate fds buffer for %d fd - will crash soon\n", CAPIobjIDstr(&plci->cobj), found);
cur = fds;
pthread_rwlock_rdlock(&plci->cobj.lock);
co = plci->cobj.listhead;
while (co) {
lp = container_of(co, struct lPLCI, cobj);
*cur++ = lp->Appl->fd;
co = co->next;
}
pthread_rwlock_unlock(&plci->cobj.lock);
/* disable answers until all controller are informed */
send_master_control(MICD_CTRL_DISABLE_POLL, found * sizeof(int), fds);
sched_yield(); /* make sure that the disable could be processed */
co = get_next_cobj(&plci->cobj, NULL);
while (co) {
lp = container_of(co, struct lPLCI, cobj);
lPLCI_l3l4(lp, pr, mc);
dprint(MIDEBUG_PLCI, "%s: SETUP %s\n",
CAPIobjIDstr(&lp->cobj), lp->ignored ? "ignored - no B-channel" : "delivered");
if (lp->ignored)
cleanup_lPLCI(lp);
co = get_next_cobj(&plci->cobj, co);
}
/* Now enable answers again */
send_master_control(MICD_CTRL_ENABLE_POLL, found * sizeof(int), fds);
free(fds);
}
}
if (found == 0) {
struct l3_msg *l3m;
l3m = alloc_l3_msg();
if (l3m) {
dprint(MIDEBUG_PLCI, "%s: send %s cause #%d (0x%02x) to layer3\n",
CAPIobjIDstr(&plci->cobj), _mi_msg_type2str(MT_RELEASE_COMPLETE), cause, cause);
if (!mi_encode_cause(l3m, cause, CAUSE_LOC_USER, 0, NULL)) {
ret = pc->l3->to_layer3(pc->l3, MT_RELEASE_COMPLETE, plci->cobj.id2, l3m);
if (ret) {
wprint("%s: Error %d - %s on sending %s to pid %x\n", CAPIobjIDstr(&plci->cobj), ret,
strerror(-ret), _mi_msg_type2str(MT_RELEASE_COMPLETE), plci->cobj.id2);
free_l3_msg(l3m);
}
}
} else
eprint("%s: cannot allocate l3 message plci\n", CAPIobjIDstr(&plci->cobj));
cleanup_mPLCI(plci);
}
}
int plci_l3l4(struct mPLCI *plci, int pr, struct l3_msg *l3m)
{
struct mc_buf *mc;
struct mCAPIobj *co;
mc = alloc_mc_buf();
if (!mc) {
wprint("%s: Cannot allocate mc_buf for %s\n", CAPIobjIDstr(&plci->cobj), _mi_msg_type2str(pr));
return -ENOMEM;
}
mc->l3m = l3m;
switch (pr) {
case MT_SETUP:
plciHandleSetupInd(plci, pr, mc);
break;
default:
co = get_next_cobj(&plci->cobj, NULL);
while (co) {
lPLCI_l3l4(container_of(co, struct lPLCI, cobj), pr, mc);
co = get_next_cobj(&plci->cobj, co);
}
break;
}
free_mc_buf(mc);
return 0;
}
int mPLCISendMessage(struct lController *lc, struct mc_buf *mc)
{
struct mPLCI *plci;
struct lPLCI *lp;
int ret;
struct pController *pc;
pc = p4lController(lc);
switch (mc->cmsg.Command) {
case CAPI_CONNECT:
plci = new_mPLCI(pc, 0);
if (plci) {
ret = lPLCICreate(&lp, lc, plci, 0);
if (!ret) {
ret = lPLCISendMessage(lp, mc);
put_cobj(&lp->cobj);
} else {
wprint("%s: cannot create lPLCI Appl-%03d", CAPIobjIDstr(&plci->cobj), lc->cobj.id2);
ret = CapiMsgOSResourceErr;
}
put_cobj(&plci->cobj);
} else {
wprint("Cannot create PLCI for controller %d\n", pc->profile.ncontroller);
ret = CapiMsgOSResourceErr;
}
break;
default:
wprint("Message %s not handled yet\n", capi20_cmd2str(mc->cmsg.Command, mc->cmsg.Subcommand));
ret = CapiMessageNotSupportedInCurrentState;
break;
}
return ret;
}
struct lPLCI *get_lPLCI4Id(struct mPLCI *plci, uint16_t appId)
{
struct mCAPIobj *co;
if (!plci)
return NULL;
co = get_next_cobj(&plci->cobj, NULL);
while (co) {
if (appId == co->id2)
break;
co = get_next_cobj(&plci->cobj, co);
}
return co ? container_of(co, struct lPLCI, cobj) : NULL;
}
struct mPLCI *getPLCI4pid(struct pController *pc, int pid)
{
struct mCAPIobj *co;
co = get_next_cobj(&pc->cobjPLCI, NULL);
while (co) {
if (co->id2 == pid)
break;
co = get_next_cobj(&pc->cobjPLCI, co);
}
return co ? container_of(co, struct mPLCI, cobj) : NULL;
}
struct mPLCI *getPLCI4Id(struct pController *pc, uint32_t id)
{
struct mCAPIobj *co;
co = get_next_cobj(&pc->cobjPLCI, NULL);
while (co) {
if (co->id == id)
break;
co = get_next_cobj(&pc->cobjPLCI, co);
}
return co ? container_of(co, struct mPLCI, cobj) : NULL;
}
int plciL4L3(struct mPLCI *plci, int mt, struct l3_msg *l3m)
{
int ret;
ret = plci->pc->l3->to_layer3(plci->pc->l3, mt, plci->cobj.id2, l3m);
if (ret < 0) {
wprint("%s: Error sending %s to controller %d pid %x %s msg\n", CAPIobjIDstr(&plci->cobj), _mi_msg_type2str(mt),
plci->pc->profile.ncontroller, plci->cobj.id2, l3m ? "with" : "no");
if (l3m)
free_l3_msg(l3m);
}
dprint(MIDEBUG_PLCI, "%s: Sending %s to layer3 %s msg\n",
CAPIobjIDstr(&plci->cobj), _mi_msg_type2str(mt), l3m ? "with" : "no");
return ret;
}
unsigned int plci_new_pid(struct mPLCI *plci)
{
return request_new_pid(plci->pc->l3);
}
void release_lController(struct lController *lc)
{
struct mCAPIobj *cop, *colp;
struct lPLCI *lp;
struct pController *pc = p4lController(lc);
if (pc) {
cop = get_next_cobj(&pc->cobjPLCI, NULL);
while (cop) {
colp = get_next_cobj(cop, NULL);
while (colp) {
lp = container_of(colp, struct lPLCI, cobj);
if (lc == lp->lc) {
if (!lp->rel_req) {
dprint(MIDEBUG_PLCI, "%s do release\n", CAPIobjIDstr(colp));
lPLCIRelease(lp);
} else
dprint(MIDEBUG_PLCI, "%s: release already done\n", CAPIobjIDstr(colp));
}
colp = get_next_cobj(cop, colp);
}
cop = get_next_cobj(&pc->cobjPLCI, cop);
}
}
}