mISDNuser/capi20/mplci.c

299 lines
6.5 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 "m_capi.h"
#include "mc_buffer.h"
#include <mISDN/q931.h>
struct mPLCI *new_mPLCI(struct pController *pc, int pid, struct lPLCI *lp)
{
struct mPLCI *plci;
int plci_nr = 1;
plci = pc->plciL;
if (plci) {
plci_nr = (plci->plci >> 8) & 0xff;
plci_nr++;
if (plci_nr > 255)
return NULL;
}
plci = calloc(1, sizeof(*plci));
if (plci) {
plci->plci = pc->profile.ncontroller | (plci_nr << 8);
plci->pid = pid;
plci->pc = pc;
plci->lPLCIs = lp;
plci->next = pc->plciL;
pc->plciL = plci;
}
return plci;
}
int free_mPLCI(struct mPLCI *plci)
{
struct mPLCI *p;
struct pController *pc;
while (plci->lPLCIs) {
lPLCI_free(plci->lPLCIs);
}
if (plci->nAppl) {
wprint("Application count (%d) mismatch for PLCI %04x\n", plci->nAppl, plci->plci);
}
pc = plci->pc;
p = pc->plciL;
if (p == plci)
pc->plciL = plci->next;
else {
while (p && p->next) {
if (p->next == plci) {
p->next = plci->next;
break;
}
p = p->next;
}
}
free(plci);
return 0;
}
int plciAttach_lPLCI(struct mPLCI *plci, struct lPLCI *lp)
{
struct lPLCI *test = get_lPLCI4Id(plci, lp->lc->Appl->AppId);
if (test) {
eprint("lPLCI for application %d already attached\n", lp->lc->Appl->AppId);
return -1;
}
lp->PLCI = plci;
lp->next = plci->lPLCIs;
plci->lPLCIs = lp;
plci->nAppl++;
return plci->nAppl;
}
void plciDetachlPLCI(struct lPLCI *lp)
{
struct lPLCI *o;
struct mPLCI *p;
p = lp->PLCI;
if (p->lPLCIs == lp) {
p->lPLCIs = lp->next;
p->nAppl--;
} else {
o = p->lPLCIs;
while (o && o->next) {
if (o->next == lp) {
o->next = lp->next;
p->nAppl--;
break;
}
o = o->next;
}
}
if (p->nAppl == 0 && p->lPLCIs == NULL) {
dprint(MIDEBUG_PLCI, "All lPLCIs are gone remove PLCI %04x now\n", p->plci);
free_mPLCI(p);
}
}
static void plciHandleSetupInd(struct mPLCI *plci, int pr, struct mc_buf *mc)
{
uint16_t CIPValue;
uint32_t CIPmask;
struct pController *pc;
struct lController *lc;
struct lPLCI *lp;
uint8_t found = 0;
int ret;
CIPValue = q931CIPValue(mc);
pc = plci->pc;
CIPmask = 1 << CIPValue;
dprint(MIDEBUG_PLCI, "PLCI %04x: Check CIPvalue %d (%08x) with CIPmask %08x\n", plci->plci, CIPValue, CIPmask, pc->CIPmask);
if (CIPValue && ((CIPmask & pc->CIPmask) || (pc->CIPmask & 1))) {
/* at least one Application is listen for this service */
lc = pc->lClist;
while (lc) {
if ((lc->CIPmask & CIPmask) || (lc->CIPmask & 1)) {
ret = lPLCICreate(&lp, lc, plci);
if (!ret) {
ret = plciAttach_lPLCI(plci, lp);
if (0 < ret) {
found++;
lPLCI_l3l4(lp, pr, mc);
} else {
wprint("Cannot attach lPLCI on PLCI %04x return %d\n", plci->plci, ret);
lPLCI_free(lp);
}
} else {
wprint("Cannot create lPLCI for PLCI %04x\n", plci->plci);
}
}
lc = lc->nextA;
}
}
if (found == 0) {
struct l3_msg *l3m;
l3m = alloc_l3_msg();
if (l3m) {
if (!mi_encode_cause(l3m, CAUSE_INCOMPATIBLE_DEST, CAUSE_LOC_USER, 0, NULL)) {
ret = pc->l3->to_layer3(pc->l3, MT_RELEASE_COMPLETE, plci->pid, l3m);
if (ret) {
wprint("Error %d - %s on sending %s to pid %x\n", ret, strerror(-ret),
_mi_msg_type2str(MT_RELEASE_COMPLETE), plci->pid);
free_l3_msg(l3m);
}
}
} else
eprint("Cannot allocate l3 message plci %x\n", plci->plci);
free_mPLCI(plci);
}
}
int plci_l3l4(struct mPLCI *plci, int pr, struct l3_msg *l3m)
{
struct lPLCI *lp, *nxt;
struct mc_buf *mc;
mc = alloc_mc_buf();
if (!mc) {
wprint("PLCI %04x: Cannot allocate mc_buf for %s\n", plci->plci, _mi_msg_type2str(pr));
return -ENOMEM;
}
mc->l3m = l3m;
switch (pr) {
case MT_SETUP:
plciHandleSetupInd(plci, pr, mc);
break;
default:
lp = plci->lPLCIs;
while (lp) {
/* maybe lPLCI_l3l4() will free lp, so get the next now */
nxt = lp->next;
lPLCI_l3l4(lp, pr, mc);
lp = nxt;
}
break;
}
free_mc_buf(mc);
return 0;
}
int mPLCISendMessage(struct lController *lc, struct mc_buf *mc)
{
struct mPLCI *plci;
struct lPLCI *lp;
int ret;
switch (mc->cmsg.Command) {
case CAPI_CONNECT:
plci = new_mPLCI(lc->Contr, 0, NULL);
if (plci) {
ret = lPLCICreate(&lp, lc, plci);
if (!ret) {
ret = plciAttach_lPLCI(plci, lp);
if (0 < ret)
ret = lPLCISendMessage(lp, mc);
else {
wprint("Cannot attach lPLCI on PLCI %04x return %d\n", plci->plci, ret);
ret = CapiMsgOSResourceErr;
}
} else {
wprint("Cannot create lPLCI for PLCI %04x\n", plci->plci);
ret = CapiMsgOSResourceErr;
}
} else {
wprint("Cannot create PLCI for controller %d\n", lc->Contr->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 lPLCI *lp;
if (!plci)
return NULL;
lp = plci->lPLCIs;
while (lp) {
if (appId == lp->lc->Appl->AppId)
break;
lp = lp->next;
}
return lp;
}
struct mPLCI *getPLCI4pid(struct pController *pc, int pid)
{
struct mPLCI *plci;
if (pc)
plci = pc->plciL;
else
plci = NULL;
while (plci) {
if (plci->pid == pid)
break;
plci = plci->next;
}
return plci;
}
struct mPLCI *getPLCI4Id(struct pController *pc, uint32_t id)
{
struct mPLCI *plci;
if (pc)
plci = pc->plciL;
else
plci = NULL;
while (plci) {
if (plci->plci == id)
break;
plci = plci->next;
}
return plci;
}
int plciL4L3(struct mPLCI *plci, int mt, struct l3_msg *l3m)
{
int ret;
ret = plci->pc->l3->to_layer3(plci->pc->l3, mt, plci->pid, l3m);
if (ret < 0) {
wprint("Error sending %s to controller %d pid %x %s msg\n", _mi_msg_type2str(mt),
plci->pc->profile.ncontroller, plci->pid, l3m ? "with" : "no");
if (l3m)
free_l3_msg(l3m);
}
dprint(MIDEBUG_PLCI, "Sending %s to layer3 pid %x controller %d %s msg\n", _mi_msg_type2str(mt),
plci->pc->profile.ncontroller, plci->pid, l3m ? "with" : "no");
return ret;
}