u-isdn/isdn_3/capi.c

2073 lines
52 KiB
C

#define __PHONE_R
#include "primitives.h"
#include "phone.h"
#include "dump.h"
#include "streamlib.h"
#include "phone_1TR6.h"
#include "q_data.h"
#include "isdn_23.h"
#include "isdn3_phone.h"
#include <sys/errno.h>
#include <sys/param.h>
#include "prot_1TR6_0.h"
#include "prot_1TR6_1.h"
#include "prot_ETS.h"
#include "capi.h"
#include "../cards/capi/capi.h"
#include "isdn_12.h"
#include "asm/byteorder.h"
#include "sapi.h"
#define NBOARD 4 /* number of D channels on one card */
#define ST_pbx 2 /* Bits! */
#if NITALK <=NBOARD+5
#error "Need NITALK > NBOARD+5"
#endif
#define tappl talki
#define regnum talki[NBOARD+0] /* current interface I'm talking about */
#define message_id talki[NBOARD+1] /* Expected msgid for current interface */
#define tstate talki[NBOARD+2] /* State of the current interface */
#define chanmask talki[NBOARD+3] /* channels we have used up */
#if NICONN <= 15
#error "Need NICONN > 15"
#endif
#define ncci0 conni[0]
#define waitflags conni[1] /* what're we waiting for */
#define msgid0 conni[2] /* for connect_resp */
#define WF_CONNECTACTIVE_IND 3 /* conni[WF_*] for IDs pertaining to that message */
#define WF_CONNECTB3ACTIVE_IND 4
#define WF_DISCONNECTB3_IND 5
#define WF_DISCONNECT_IND 6
#define WF_SELECTB2_CONF 7
#define WF_SELECTB3_CONF 8
#define WF_LISTENB3_CONF 9
#define WF_CONNECTB3_IND 10
#define WF_CONNECTB3_CONF 11
#define WF_DISCONNECTB3_CONF 12
#define WF_DISCONNECT_CONF 13
#define WF_CONNECT_CONF 14
#define WF_CONTROL_EAZ 15
/* Card states */
#define STATE_BOOTING 1
#define STATE_OPENING 2
#define STATE_REGISTER 3
#define STATE_CONF_FIRST 10
#define STATE_CONF_LAST 99
#define STATE_RUNNING 100
#define STATE_DEAD 255
#define RUN_CAPI_TCONN 01
#define RUN_CAPI_TWAITEAZ 02
#define RUN_CAPI_TFOO 04
#define VAL_CAPI_TCONN ( 40 *HZ) /* timer for delaying an ALERT response */
#define VAL_CAPI_TWAITEAZ ( HZ/2) /* timer for waiting for the EAZ */
#define VAL_CAPI_TFOO ( 10 * HZ) /* timer for waiting for teardown */
static void CAPI_TCONN (isdn3_conn conn);
static void CAPI_TWAITEAZ (isdn3_conn conn);
static void CAPI_TFOO (isdn3_conn conn);
/* Connection states:
0: unknown
1: outgoing, wait for EAZ
2: outgoing, wait for CONNECT_CONF
3: outgoing, wait for CONNECTACTIVE_IND SELECTB2_CONF SELECTB3_CONF
4: outgoing, wait for CONNECTB3_CONF CONNECTB3ACTIVE_IND
6: incoming, wait for EAZ
7: incoming, wait for master program
8: incoming, wait for SELECTB2_CONF SELECTB3_CONF
9: incoming, wait for LISTENB3_CONF CONNECTACTIVE_IND CONNECTB3_CONF CONNECTB3ACTIVE_IND
15: CONNECTED
20: wait for DISCONNECTB3_CONF DISCONNECTB3_IND
21: wait for DISCONNECT_CONF DISCONNECT_IND
22: wait for DISCONNECTB3_CONF DISCONNECTB3_IND DISCONNECT_CONF DISCONNECT_IND
99: delay wait
*/
struct capi_info {
unsigned short service;
unsigned char subcard;
unsigned char bchan;
unsigned char flags;
#define INF_SPV 01
unsigned char lnr[MAXNR];
unsigned char nr[MAXNR];
};
extern void log_printmsg (void *log, const char *text, mblk_t * mp, const char*);
ushort_t capi_infotoid(ushort_t info);
static int report_incoming (isdn3_conn conn);
static int send_disconnect(isdn3_conn conn, char do_L3, ushort_t cause);
static void checkterm (isdn3_conn conn);
static void
capi_timerup (isdn3_conn conn)
{
rtimer (CAPI_TCONN, conn);
rtimer (CAPI_TWAITEAZ, conn);
rtimer (CAPI_TFOO, conn);
}
#define setstate(a,b) Xsetstate(__LINE__,(a),(b))
static void
Xsetstate(unsigned int deb_line, isdn3_conn conn, uchar_t state)
{
printf ("Conn CAPI:%d %05lx: State %d --> %d\n", deb_line, conn->call_ref, conn->state, state);
if(conn->state == state)
return;
if(conn->state >= 1 && conn->state < 15 && (state < 1 || state >= 15))
untimer(CAPI_TCONN, conn);
switch(conn->state) {
case 6:
untimer(CAPI_TWAITEAZ, conn);
break;
case 99:
untimer(CAPI_TFOO, conn);
break;
}
if(state >= 1 && state < 15 && (conn->state < 1 || conn->state >= 15))
timer(CAPI_TCONN,conn);
conn->state = state;
switch(state) {
case 6:
timer(CAPI_TWAITEAZ,conn);
break;
case 99:
timer(CAPI_TFOO,conn);
break;
}
if(state == 0 || state >= 20) {
if(conn->bchan != 0) {
conn->bchan = 0;
conn->talk->chanmask &=~ (1<<conn->bchan);
conn->minorstate &=~ MS_BCHAN;
/* XXX send a clearing msg down? */
}
} else if(conn->bchan == 0) {
int ch; unsigned long chm;
for(ch=1,chm = 1;chm; chm <<= 1, ch++)
if(!(conn->talk->chanmask & chm))
break;
if(chm) {
conn->bchan = ch;
conn->minorstate |= MS_BCHAN;
conn->talk->chanmask |= chm;
isdn3_setup_conn (conn, EST_NO_CHANGE);
}
}
}
static ushort_t newmsgid(isdn3_talk talk)
{
talk->message_id = ((talk->message_id + 1) & 0x3FFF) | 0x4000;
return talk->message_id;
}
static int
capi_send(isdn3_talk talk, ushort_t appl, ushort_t msgtype, mblk_t *data, ushort_t msgid)
{
int err;
mblk_t *mb;
struct CAPI_every_header *capi;
/* build a message */
mb = allocb(sizeof(*capi), BPRI_LO);
if(mb == NULL)
return -ENOMEM;
capi = ((struct CAPI_every_header *)mb->b_wptr)++;
bzero(capi,sizeof(*capi));
capi->len = sizeof(*capi) + (data ? msgdsize(data) : 0);
capi->appl = appl;
capi->PRIM_type = msgtype;
capi->messid = msgid;
if(data != NULL)
linkb(mb,data);
if((err = isdn3_send(talk,AS_DATA,mb)) < 0)
freeb(mb);
return err;
}
static int
capi_sendctl(isdn3_talk talk, ushort_t appl, ushort_t code, mblk_t *data, ushort_t msgid)
{
int err;
mblk_t *mb;
/* build a message */
struct CAPI_control_req *capi;
mb = allocb(sizeof(*capi), BPRI_LO);
if(mb == NULL)
return -ENOMEM;
capi = ((struct CAPI_control_req *)mb->b_wptr)++;
bzero(capi,sizeof(*capi));
capi->type = code;
capi->datalen = msgdsize(data);
linkb(mb,data);
if((err = capi_send(talk,appl,CAPI_CONTROL_REQ,mb,msgid)) < 0)
freeb(mb);
return err;
}
/* open:
* 0001 ID LLHH capiLen LLHH appl LLHH type LLHH messid LLHH control
* LLHH type CCCC protocol ...
*/
static void
CAPI_TWAITEAZ(isdn3_conn conn)
{
printf("CAPI_TWAITEAZ %05lx\n",conn->call_ref);
conn->timerflags &= ~RUN_CAPI_TWAITEAZ;
if(conn->state != 6)
return;
report_incoming(conn);
}
static void
CAPI_TFOO(isdn3_conn conn)
{
printf("CAPI_TFOO %05lx\n",conn->call_ref);
conn->timerflags &= ~RUN_CAPI_TFOO;
setstate(conn,0);
checkterm(conn);
}
static void
CAPI_TCONN(isdn3_conn conn)
{
printf("CAPI_TCONN %05lx\n",conn->call_ref);
conn->timerflags &= ~RUN_CAPI_TCONN;
if(conn->state < 1 || conn->state >= 15)
return;
send_disconnect(conn,0,0);
}
static int
send_setup(isdn3_conn conn)
{
int err;
struct CAPI_selectb2_req *c2;
struct CAPI_selectb3_req *c3;
struct dlpd *dl;
mblk_t *m2,*m3;
m2 = allocb(sizeof(*c2)+sizeof(*dl),BPRI_MED);
m3 = allocb(sizeof(*c3),BPRI_MED);
if(m2 == NULL || m3 == NULL) {
if(m2 != NULL)
freemsg(m2);
if(m3 != NULL)
freemsg(m3);
return -ENOMEM;
}
c2 = ((typeof(c2))m2->b_wptr)++;
dl = ((typeof(dl))m2->b_wptr)++;
c3 = ((typeof(c3))m3->b_wptr)++;
bzero(c2,sizeof(*c2));
bzero(dl,sizeof(*dl));
bzero(c3,sizeof(*c3));
c2->plci = conn->call_ref;
c2->B2_proto = 0x02; /* transparent HDLC */
c2->dlpdlen = sizeof(*dl);
dl->data_length = 4096;
c3->plci = conn->call_ref;
c3->B3_proto = 0x04; /* transparent */
err = capi_send(conn->talk,conn->call_ref >> 16, CAPI_SELECTB2_REQ, m2, conn->conni[WF_SELECTB2_CONF] = newmsgid(conn->talk));
if(err < 0) {
freemsg(m2);
freemsg(m3);
} else {
err = capi_send(conn->talk,conn->call_ref >> 16, CAPI_SELECTB3_REQ, m3, conn->conni[WF_SELECTB3_CONF] = newmsgid(conn->talk));
if(err < 0)
freemsg(m3);
else {
conn->waitflags = 1 << WF_SELECTB2_CONF;
conn->waitflags |= 1 << WF_SELECTB3_CONF;
}
}
return err;
}
static int
send_open(isdn3_talk talk)
{
struct apiopen *capi;
int err;
mblk_t *data;
#ifdef _euro_
#define DEFPROFILE "u_dss1_pmp"
#endif
#ifndef DEFPROFILE
#define DEFPROFILE "u_1tr6_pmp"
#endif
char profile[32] = DEFPROFILE;
{ /* Find correct driver name */
int err; char skip = 0;
mblk_t *info = talk->card->info;
if(info != NULL) {
streamchar *sta = info->b_rptr;
ushort_t idx;
while(m_getid(info,&idx) == 0) {
long sap;
switch(idx) {
case ARG_PROTOCOL:
if (m_geti(info,&sap) == 0) {
skip = (sap != SAPI_CAPI);
}
break;
case ARG_SUBPROT:
if (m_geti(info,&sap) == 0 && !skip) {
switch(sap) {
case SAPI_CAPI_BINTEC:
skip=0;
break;
default:
/* Wrong card. TODO: Do something! */
info->b_rptr = sta;
return -ENXIO;
}
}
break;
case ARG_SUBCARD:
if (m_geti(info,&sap) == 0 && !skip)
skip = (sap != talk->regnum+1);
break;
case ARG_STACK:
if(skip)
break;
if((err = m_getstr(info,profile,sizeof(profile)-1)) < 0)
strcpy(profile,DEFPROFILE);
break;
case ARG_PBX:
if(skip)
break;
talk->state |= 1<<(talk->regnum+ST_pbx);
break;
}
}
info->b_rptr = sta;
}
}
data = allocb(sizeof(*capi), BPRI_MED);
if(data == NULL) /* XXX error processing */
return -ENOMEM;
capi = ((struct apiopen *)data->b_wptr)++;
bzero(capi,sizeof(*capi));
strcpy(capi->protocol,profile);
capi->teid = ~0;
capi->t3id = ~0;
capi->contrl = 0;
capi->time = time(NULL);
if((err = capi_sendctl(talk,talk->tappl[talk->regnum],CONTROL_API_OPEN,data,newmsgid(talk))) < 0) {
freemsg(data);
return err;
}
talk->tstate = STATE_OPENING;
return err;
}
static int
chstate (isdn3_talk talk, uchar_t ind, short add)
{
printf("CAPI: chstate %d %d, in state %ld\n",ind,add,talk->tstate);
switch (ind) {
case PH_ACTIVATE_NOTE:
case DL_ESTABLISH_IND:
case DL_ESTABLISH_CONF:
talk->regnum = 0;
talk->state |= IS_UP;
{
isdn3_conn conn, nconn;
for(conn = talk->conn; conn != NULL; conn = nconn) {
nconn = conn->next;
capi_timerup(conn);
}
}
if(talk->tstate != STATE_BOOTING) {
/* TODO: reset / restart / XXX the card? */
break;
}
send_open(talk);
break;
case MDL_ERROR_IND:
case DL_RELEASE_IND:
case DL_RELEASE_CONF:
case PH_DEACTIVATE_CONF:
case PH_DEACTIVATE_IND:
case PH_DISCONNECT_IND:
talk->regnum = 0;
talk->tstate = STATE_BOOTING;
talk->state &=~ IS_UP;
{
isdn3_conn conn, nconn;
for(conn = talk->conn; conn != NULL; conn = nconn) {
nconn = conn->next;
setstate(conn,0);
checkterm(conn);
}
}
break;
}
return 0;
}
static isdn3_conn
capi_findconn(isdn3_talk talk, ushort_t appl, ushort_t plci)
{
isdn3_conn conn;
ulong_t cref = (appl << 16) | plci;
for(conn = talk->conn; conn != NULL; conn = conn->next) {
if(conn->call_ref == cref) {
printf(" (conn %05lx) ",conn->call_ref);
return conn;
}
}
return NULL;
}
static isdn3_conn
capi_findconn3(isdn3_talk talk, ushort_t appl, ushort_t ncci)
{
isdn3_conn conn;
for(conn = talk->conn; conn != NULL; conn = conn->next) {
if((conn->call_ref >> 16) == appl && conn->ncci0 == ncci) {
printf(" (conn %05lx) ",conn->call_ref);
return conn;
}
}
return NULL;
}
static isdn3_conn
capi_findconnm(isdn3_talk talk, ushort_t appl, ushort_t msgid, ushort_t index)
{
isdn3_conn conn;
for(conn = talk->conn; conn != NULL; conn = conn->next) {
if((conn->call_ref >> 16) == appl && conn->conni[index] == msgid) {
printf(" (conn %05lx) ",conn->call_ref);
return conn;
}
}
return NULL;
}
static int
report_incoming (isdn3_conn conn)
{
int err = 0;
mblk_t *mb = allocb (256, BPRI_MED);
if (mb == NULL) {
setstate (conn, 0);
return -ENOMEM;
}
m_putid (mb, IND_INCOMING);
conn_info (conn, mb);
if ((err = isdn3_at_send (conn, mb, 0)) != 0) {
freemsg (mb);
setstate (conn, 0);
return err;
}
return err;
}
static void
report_addcause(mblk_t *mb, ushort_t info, ushort_t cause)
{
extern ushort_t n1_causetoid(uchar_t id);
m_putsx(mb,ARG_CAUSE);
if(cause != 0)
m_putsx2(mb,n1_causetoid(cause&0x7F));
m_putsx2(mb,capi_infotoid(info));
}
static void
report_nocard (isdn3_talk talk, ushort_t info)
{
mblk_t *mb = allocb (64, BPRI_MED);
talk->state = STATE_DEAD;
m_putid (mb, IND_NOCARD);
m_putlx (mb, talk->card->id);
report_addcause(mb,info,0);
if (isdn3_at_send (NULL, mb, 0) < 0)
freemsg (mb);
return;
}
static void
report_terminate (isdn3_conn conn, ushort_t info, ushort_t cause)
{
int err = 0;
mblk_t *mb = allocb (256, BPRI_MED);
if (conn->minorstate & MS_TERM_SENT) {
m_putid (mb, IND_INFO);
m_putid (mb, ID_N1_REL);
} else {
conn->minorstate |= MS_TERM_SENT;
m_putid (mb, IND_DISC);
}
report_addcause(mb,info,cause);
conn_info (conn, mb);
if ((err = isdn3_at_send (conn, mb, 0)) != 0) {
freemsg (mb);
setstate (conn, 0);
return;
}
return;
}
static void
checkterm (isdn3_conn conn)
{
if (conn->state == 0) {
report_terminate (conn,0,0);
isdn3_killconn (conn, 1); /* XXX */
}
}
static int
send_disconnect(isdn3_conn conn, char do_L3, ushort_t cause)
{
int err;
if(conn->state >= 21)
return 0;
if(conn->state == 0)
return 0;
if((conn->state >= 20) && (cause == N1_LocalProcErr))
cause = 0;
conn->waitflags = 0;
report_terminate(conn,0,cause);
switch(conn->state) {
case 6:
case 7:
case 8:
{
struct CAPI_connect_resp *c3;
mblk_t *m3 = allocb(sizeof(*c3),BPRI_MED);
if(m3 == NULL)
return -ENOMEM;
c3 = ((typeof(c3))m3->b_wptr)++;
bzero(c3,sizeof(*c3));
c3->plci = conn->call_ref;
c3->reject = cause ? ((cause >= 0x100) ? cause : cause | 0x3480) : 0x3480|N1_CallRejected;
if((err = capi_send(conn->talk,conn->call_ref >> 16,CAPI_CONNECT_RESP,m3,
newmsgid(conn->talk))) < 0) {
setstate(conn,0);
freemsg(m3);
} else
setstate(conn,99);
}
break;
case 15:
if(do_L3) {
struct CAPI_disconnectb3_req *c3;
mblk_t *m3 = allocb(sizeof(*c3),BPRI_MED);
if(m3 == NULL)
return -ENOMEM;
c3 = ((typeof(c3))m3->b_wptr)++;
bzero(c3,sizeof(*c3));
c3->ncci = conn->ncci0;
if((err = capi_send(conn->talk,conn->call_ref >> 16,CAPI_DISCONNECTB3_REQ,m3,
conn->conni[WF_DISCONNECTB3_CONF] = newmsgid(conn->talk))) < 0) {
freemsg(m3);
} else {
conn->waitflags |= 1 << WF_DISCONNECTB3_CONF;
conn->waitflags |= 1 << WF_DISCONNECTB3_IND;
setstate(conn,20);
}
}
/* FALL THRU */
default:
{
struct CAPI_disconnect_req *c3;
mblk_t *m3 = allocb(sizeof(*c3),BPRI_MED);
if(m3 == NULL)
return -ENOMEM;
c3 = ((typeof(c3))m3->b_wptr)++;
bzero(c3,sizeof(*c3));
c3->plci = conn->call_ref;
c3->cause = cause;
if((err = capi_send(conn->talk,conn->call_ref >> 16,CAPI_DISCONNECT_REQ,m3,
conn->conni[WF_DISCONNECT_CONF] = newmsgid(conn->talk))) < 0) {
freemsg(m3);
} else {
conn->waitflags |= 1 << WF_DISCONNECT_CONF;
conn->waitflags |= 1 << WF_DISCONNECT_IND;
if(conn->state < 20)
setstate(conn,21);
else
setstate(conn,22);
}
}
}
return err;
}
static int
send_dialout(isdn3_conn conn)
{
int err;
struct CAPI_connect_req *c2;
struct capi_info *info = conn->p_data;
mblk_t *m2;
int llen = strlen(info->lnr);
if(info == NULL)
return -ENXIO;
m2 = allocb(sizeof(*c2)+strlen(info->nr)+(llen ? llen+1 : 0)+((info->flags & INF_SPV) != 0),BPRI_MED);
if(m2 == NULL)
return -ENOMEM;
c2 = ((typeof(c2))m2->b_wptr)++;
bzero(c2,sizeof(*c2));
c2->infomask = 0xC00000FF;
{ /* Find correct info mask */
int err; char skip = 0;
mblk_t *inf = conn->talk->card->info;
if(inf != NULL) {
streamchar *sta = inf->b_rptr;
ushort_t idx;
while(m_getid(inf,&idx) == 0) {
long sap;
switch(idx) {
case ARG_PROTOCOL:
if (m_geti(inf,&sap) == 0) {
skip = (sap != SAPI_CAPI);
}
break;
case ARG_SUBPROT:
if (m_geti(inf,&sap) == 0 && !skip) {
switch(sap) {
case SAPI_CAPI_BINTEC:
skip=0;
break;
default:
/* Wrong card. TODO: Do something! */
inf->b_rptr = sta;
return -ENXIO;
}
}
break;
case ARG_SUBCARD:
if (m_geti(inf,&sap) == 0 && !skip)
skip = (sap != info->subcard);
break;
case ARG_LISTEN:
{
long x;
if(skip)
break;
if((err = m_getx(inf,&x)) >= 0) {
if((err = m_getx(inf,&x)) >= 0) {
if((err = m_getx(inf,&x)) >= 0) {
c2->infomask = x;
}
}
}
}
break;
}
}
inf->b_rptr = sta;
}
}
c2->channel = (info->bchan ? info->bchan : CAPI_ANYBCHANNEL);
c2->DST_service = info->service >> 8;
c2->DST_addinfo = info->service;
c2->telnolen = strlen(info->nr);
strncpy(m2->b_wptr,info->nr,c2->telnolen);
m2->b_wptr += strlen(info->nr);
if(info->flags & INF_SPV) {
c2->telnolen++;
*m2->b_wptr++ = 'S';
}
if(llen > 0) && (conn->talk->state & (1<<(info->subcard+ST_pbx)))) {
c2->SRC_eaz = 0;
if(info->lnr[0] >= '0' && info->lnr[0] <= '9') {
memcpy(m2->b_wptr+1,info->lnr,llen);
} else {
llen--;
memcpy(m2->b_wptr+1,info->lnr+1,llen);
}
*m2->b_wptr = llen;
m2->b_wptr += llen+1;
} else if(llen > 0)
c2->SRC_eaz = info->lnr[llen-1];
conn->call_ref = conn->talk->tappl[info->subcard]<<16;
if((err = capi_send(conn->talk,conn->talk->tappl[info->subcard],CAPI_CONNECT_REQ,m2,conn->conni[WF_CONNECT_CONF]=newmsgid(conn->talk))) < 0)
freemsg(m2);
else {
conn->waitflags = 1<<WF_CONNECT_CONF;
setstate(conn,2);
}
return err;
}
static int
after_active(isdn3_conn conn)
{
int err;
if(conn->waitflags)
return 0; /* not yet */
switch(conn->state) {
default:
err = -EIO;
if(send_disconnect(conn,0,N1_LocalProcErr) < 0)
setstate(conn,0);
break;
case 4:
case 9:
setstate(conn,15);
isdn3_setup_conn (conn, EST_CONNECT);
err = 0;
break;
}
return err;
}
static int
after_selectb(isdn3_conn conn)
{
int err;
if(conn->waitflags)
return 0; /* not yet */
switch(conn->state) {
case 2: /* active */
{
struct CAPI_connect_resp *c3;
mblk_t *m3 = allocb(sizeof(*c3),BPRI_MED);
if(m3 == NULL) {
err = -ENOMEM;
break;
}
c3 = ((typeof(c3))m3->b_wptr)++;
bzero(c3,sizeof(*c3));
c3->plci = conn->call_ref;
if((err = capi_send(conn->talk,conn->call_ref>>16,CAPI_CONNECT_RESP,m3,conn->msgid0)) < 0)
freemsg(m3);
else {
conn->waitflags = 1<<WF_CONNECTB3_CONF;
conn->waitflags |= 1<<WF_CONNECTB3ACTIVE_IND;
setstate(conn,4);
}
}
break;
case 8: /* passive */
{
struct CAPI_listenb3_req *c2;
struct CAPI_connect_resp *c3;
mblk_t *m2 = allocb(sizeof(*c2),BPRI_MED);
mblk_t *m3 = allocb(sizeof(*c3),BPRI_MED);
if(m2 == NULL || m3 == NULL) {
if(m2 != NULL)
freemsg(m2);
if(m3 != NULL)
freemsg(m3);
err = -ENOMEM;
break;
}
c2 = ((typeof(c2))m2->b_wptr)++;
c3 = ((typeof(c3))m3->b_wptr)++;
bzero(c2,sizeof(*c2));
bzero(c3,sizeof(*c3));
c2->plci = conn->call_ref;
c3->plci = conn->call_ref;
if((err = capi_send(conn->talk,conn->call_ref>>16,CAPI_LISTENB3_REQ,m3,conn->conni[WF_LISTENB3_CONF] = newmsgid(conn->talk))) < 0) {
freemsg(m2);
freemsg(m3);
} else if((err = capi_send(conn->talk,conn->call_ref>>16,CAPI_CONNECT_RESP,m3,conn->msgid0)) < 0)
freemsg(m3);
else {
conn->waitflags = 1<<WF_LISTENB3_CONF;
conn->waitflags |= 1<<WF_CONNECTACTIVE_IND;
conn->waitflags |= 1<<WF_CONNECTB3_IND;
conn->waitflags |= 1<<WF_CONNECTB3ACTIVE_IND;
setstate(conn,9);
}
}
break;
default:
printf("CAPI error: wrong state %d in after_select\n",conn->state);
err = send_disconnect(conn,0,N1_LocalProcErr);
if(err < 0)
setstate(conn,0);
}
if(err < 0)
send_disconnect(conn,0,N1_OutOfOrder);
return err;
}
static int
recv (isdn3_talk talk, char isUI, mblk_t * data)
{
struct CAPI_every_header *capi;
streamchar *origmb;
int err = 0;
isdn3_conn conn = 0;
if(talk->state == STATE_DEAD)
return -ENXIO;
printf("CAPI: recv %d, in state %ld\n",isUI,talk->tstate);
origmb = data->b_rptr;
if(data->b_wptr-data->b_rptr < sizeof(*capi))
goto less_room;
capi = ((typeof(capi))data->b_rptr)++;
switch(capi->PRIM_type) {
default:
printf("CAPI: Unknown primary type 0x%04x\n",capi->PRIM_type);
err = -ENXIO;
goto printit;
case CAPI_DISCONNECTB3_CONF:
{
struct CAPI_disconnectb3_conf *c2;
if(data->b_wptr-data->b_rptr < sizeof(*c2))
goto less_room;
c2 = ((typeof(c2))data->b_rptr)++;
if((conn = capi_findconn3(talk,capi->appl,c2->ncci)) == NULL) {
printf("CAPI error: DISCONNECTB3_CONF has unknown cref3 %x%04x\n",capi->appl,c2->ncci);
err = -ENXIO;
break;
}
if(c2->info != 0) {
printf("CAPI error: DISCONNECTB3_CONF returns %04x\n",c2->info);
send_disconnect(conn,0,N1_OutOfOrder);
break;
}
if(conn->waitflags & (1<<WF_DISCONNECTB3_CONF)) {
conn->waitflags &=~ (1<<WF_DISCONNECTB3_CONF);
if(conn->waitflags == 0) {
setstate(conn,0);
break;
}
} else {
printf("CAPI error: DISCONNECTB3_CONF in wrong state %d\n",conn->state);
err = -EIO;
break;
}
}
break;
case CAPI_DISCONNECTB3_IND:
{
struct CAPI_disconnectb3_ind *c2;
if(data->b_wptr-data->b_rptr < sizeof(*c2))
goto less_room;
c2 = ((typeof(c2))data->b_rptr)++;
if((conn = capi_findconn3(talk,capi->appl,c2->ncci)) == NULL) {
printf("CAPI error: DISCONNECTB3_IND has unknown cref3 %x%04x\n",capi->appl,c2->ncci);
err = -ENXIO;
break;
}
if((c2->info == 0) && (conn->waitflags & (1<<WF_DISCONNECTB3_IND))) {
conn->waitflags &=~ (1<<WF_DISCONNECTB3_IND);
if(conn->waitflags == 0) {
report_terminate(conn,0,0);
if(conn->state == 20) {
err = send_disconnect(conn,0,0);
if(err < 0)
setstate(conn,0);
} else
setstate(conn,0);
}
} else if((c2->info != 0) || (conn->state >= 20)) {
printf("CAPI error: DISCONNECTB3_IND in wrong state %d, info %04x\n",conn->state,c2->info);
isdn3_setup_conn (conn, EST_DISCONNECT);
send_disconnect(conn,0,N1_OutOfOrder);
report_terminate(conn,c2->info,0);
}
{
int err3 = 0;
struct CAPI_disconnectb3_resp *c3;
mblk_t *m3 = allocb(sizeof(*c3),BPRI_MED);
if(m3 == NULL)
err3 = -ENOMEM;
if(err3 == 0) {
c3 = ((typeof(c3))data->b_wptr)++;
bzero(c3,sizeof(*c3));
c3->ncci = c2->ncci;
if((err3 = capi_send(talk,capi->appl,CAPI_DISCONNECTB3_RESP,m3,capi->messid)) < 0)
freemsg(m3);
}
if(err == 0)
err = send_disconnect(conn,0,0);
if(err == 0)
err = err3;
}
}
break;
case CAPI_DISCONNECT_IND:
{
struct CAPI_disconnect_ind *c2;
if(data->b_wptr-data->b_rptr < sizeof(*c2))
goto less_room;
c2 = ((typeof(c2))data->b_rptr)++;
if((conn = capi_findconn(talk,capi->appl,c2->plci)) == NULL) {
printf("CAPI error: DISCONNECT_IND has unknown cref %x%04x\n",capi->appl,c2->plci);
err = -ENXIO;
break;
}
if((c2->info == 0) && (conn->waitflags & (1<<WF_DISCONNECT_IND))) {
conn->waitflags &=~ (1<<WF_DISCONNECT_IND);
if(conn->waitflags == 0) {
report_terminate(conn,0,0);
setstate(conn,0);
}
} else if((c2->info != 0) || ((conn->state >= 21) && (conn->state < 99))) {
printf("CAPI error: DISCONNECT_IND in wrong state %d, info %04x\n",conn->state,c2->info);
isdn3_setup_conn (conn, EST_DISCONNECT);
report_terminate(conn,c2->info,0);
}
{
int err3 = 0;
struct CAPI_disconnect_resp *c3;
mblk_t *m3 = allocb(sizeof(*c3),BPRI_MED);
if(m3 == NULL)
err3 = -ENOMEM;
if(err3 == 0) {
c3 = ((typeof(c3))data->b_wptr)++;
bzero(c3,sizeof(*c3));
c3->plci = c2->plci;
if((err3 = capi_send(talk,capi->appl,CAPI_DISCONNECT_RESP,m3,capi->messid)) < 0)
freemsg(m3);
}
if(err == 0)
err = send_disconnect(conn,0,0);
if(err == 0)
err = err3;
}
}
break;
case CAPI_DISCONNECT_CONF:
{
struct CAPI_disconnect_conf *c2;
if(data->b_wptr-data->b_rptr < sizeof(*c2))
goto less_room;
c2 = ((typeof(c2))data->b_rptr)++;
if((conn = capi_findconn(talk,capi->appl,c2->plci)) == NULL) {
printf("CAPI error: DISCONNECT_CONF has unknown cref %x%04x\n",capi->appl,c2->plci);
err = -ENXIO;
break;
}
if((c2->info == 0) || (conn->waitflags & (1<<WF_DISCONNECT_CONF))) {
conn->waitflags &=~ (1<<WF_DISCONNECT_CONF);
if(conn->waitflags == 0) {
report_terminate(conn,c2->info,0);
setstate(conn,0);
}
} else {
printf("CAPI error: DISCONNECT_CONF in wrong state %d, info %04x\n",conn->state,c2->info);
isdn3_setup_conn (conn, EST_DISCONNECT);
setstate(conn,0);
report_terminate(conn,c2->info,0);
}
}
break;
case CAPI_CONNECT_CONF:
{
struct CAPI_connect_conf *c2;
if(data->b_wptr-data->b_rptr < sizeof(*c2))
goto less_room;
c2 = ((typeof(c2))data->b_rptr)++;
if((c2->info == 0) && ((conn = capi_findconn(talk,capi->appl,c2->plci)) != NULL)) {
printf("CAPI error: CONNECT_CONF has known cref %x%04x\n",capi->appl,c2->plci);
report_terminate(conn,c2->info,0);
setstate(conn,0);
err = -ENXIO;
break;
}
if((conn = capi_findconnm(talk,capi->appl,capi->messid,WF_CONNECT_CONF)) == NULL) {
printf("CAPI error: CONNECT_CONF has unknown msgid %04x.%04x\n",capi->appl,capi->messid);
err = -ENXIO;
break;
}
conn->call_ref = (capi->appl << 16) | c2->plci;
if((c2->info == 0) && (conn->waitflags & (1<<WF_CONNECT_CONF))) {
conn->waitflags &=~ (1<<WF_CONNECT_CONF);
if(conn->waitflags == 0) {
if((err = send_setup(conn)) < 0) {
if(send_disconnect(conn,0,N1_OutOfOrder) < 0) {
setstate(conn,0);
}
} else {
conn->waitflags |= 1<<WF_CONNECTACTIVE_IND;
setstate(conn,3);
}
}
} else {
printf("CAPI error: CONNECT_CONF in wrong state %d, info %04x\n",conn->state,c2->info);
isdn3_setup_conn (conn, EST_DISCONNECT);
report_terminate(conn,c2->info,0);
if(send_disconnect(conn,0,N1_LocalProcErr) < 0)
setstate(conn,0);
}
}
break;
case CAPI_SELECTB2_CONF:
{
struct CAPI_selectb2_conf *c2;
if(data->b_wptr-data->b_rptr < sizeof(*c2))
goto less_room;
c2 = ((typeof(c2))data->b_rptr)++;
if((conn = capi_findconn(talk,capi->appl,c2->plci)) == NULL) {
printf("CAPI error: SELECTB2_CONF has unknown cref %x%04x\n",capi->appl,c2->plci);
err = -ENXIO;
break;
}
if((c2->info == 0) && (conn->waitflags & (1<<WF_SELECTB2_CONF))) {
conn->waitflags &=~ (1<<WF_SELECTB2_CONF);
err = after_selectb(conn);
} else {
printf("CAPI error: SELECTB2_CONF in wrong state %d, info %04x\n",conn->state,c2->info);
report_terminate(conn,c2->info,0);
if(send_disconnect(conn,0,N1_OutOfOrder) < 0)
setstate(conn,0);
}
}
break;
case CAPI_SELECTB3_CONF:
{
struct CAPI_selectb3_conf *c2;
if(data->b_wptr-data->b_rptr < sizeof(*c2))
goto less_room;
c2 = ((typeof(c2))data->b_rptr)++;
if((conn = capi_findconn(talk,capi->appl,c2->plci)) == NULL) {
printf("CAPI error: SELECTB3_CONF has unknown cref %x%04x\n",capi->appl,c2->plci);
err = -ENXIO;
break;
}
if((c2->info == 0) && (conn->waitflags & (1<<WF_SELECTB3_CONF))) {
conn->waitflags &=~ (1<<WF_SELECTB3_CONF);
err = after_selectb(conn);
} else {
printf("CAPI error: SELECTB3_CONF in wrong state %d, info %04x\n",conn->state,c2->info);
report_terminate(conn,c2->info,0);
if(send_disconnect(conn,0,N1_OutOfOrder) < 0)
setstate(conn,0);
}
}
break;
case CAPI_CONNECTACTIVE_IND:
{
struct CAPI_connectactive_ind *c2;
if(data->b_wptr-data->b_rptr < sizeof(*c2))
goto less_room;
c2 = ((typeof(c2))data->b_rptr)++;
if((conn = capi_findconn(talk,capi->appl,c2->plci)) == NULL) {
printf("CAPI error: CONNECTACTIVE_IND has unknown cref %x%04x\n",capi->appl,c2->plci);
err = -ENXIO;
break;
}
if(conn->waitflags & (1<<WF_CONNECTACTIVE_IND)) {
conn->waitflags &=~ (1<<WF_CONNECTACTIVE_IND);
err = after_active(conn);
} else {
printf("CAPI error: CONNECTACTIVE_IND in wrong state %d\n",conn->state);
if(send_disconnect(conn,0,N1_OutOfOrder) < 0)
setstate(conn,0);
}
{
int err3 = 0;
struct CAPI_connectactive_resp *c3;
mblk_t *m3 = allocb(sizeof(*c3),BPRI_MED);
if(m3 == NULL)
err3 = -ENOMEM;
if(err3 == 0) {
c3 = ((typeof(c3))data->b_wptr)++;
bzero(c3,sizeof(*c3));
c3->plci = c2->plci;
if((err3 = capi_send(talk,capi->appl,CAPI_CONNECTACTIVE_RESP,m3,capi->messid)) < 0)
freemsg(m3);
}
if(err == 0)
err = err3;
}
}
break;
case CAPI_LISTENB3_CONF:
{
struct CAPI_listenb3_conf *c2;
if(data->b_wptr-data->b_rptr < sizeof(*c2))
goto less_room;
c2 = ((typeof(c2))data->b_rptr)++;
if((conn = capi_findconn(talk,capi->appl,c2->plci)) == NULL) {
printf("CAPI error: LISTENB3_CONF has unknown cref %x%04x\n",capi->appl,c2->plci);
err = -ENXIO;
break;
}
if((c2->info == 0) && (conn->waitflags & (1<<WF_LISTENB3_CONF))) {
conn->waitflags &=~ (1<<WF_LISTENB3_CONF);
err = after_active(conn);
} else {
err = -EIO;
printf("CAPI error: LISTENB3_CONF in wrong state %d, info %04x\n",conn->state,c2->info);
if(send_disconnect(conn,0,N1_OutOfOrder) < 0)
setstate(conn,0);
}
}
break;
case CAPI_CONNECTB3_CONF:
{
struct CAPI_connectb3_conf *c2;
if(data->b_wptr-data->b_rptr < sizeof(*c2))
goto less_room;
c2 = ((typeof(c2))data->b_rptr)++;
if((conn = capi_findconn(talk,capi->appl,c2->plci)) == NULL) {
printf("CAPI error: CONNECTB3_CONF has unknown cref %x%04x\n",capi->appl,c2->plci);
err = -ENXIO;
break;
}
if((c2->info == 0) && (conn->waitflags & (1<<WF_CONNECTB3_CONF))) {
conn->waitflags &=~ (1<<WF_CONNECTB3_CONF);
{
mblk_t *mz = allocb(64,BPRI_MED);
if(mz == NULL) {
err = -ENOMEM;
goto exSendD;
}
m_putid(mz,CMD_CARDPROT);
m_putsx(mz,ARG_ASSOC);
m_puti(mz,capi->appl);
m_puti(mz,c2->plci);
m_puti(mz,c2->ncci);
err = isdn3_send(conn->talk,AS_PROTO,mz);
if(err < 0) {
freemsg(mz);
goto exSendD;
}
conn->ncci0 = c2->ncci;
}
err = after_active(conn);
} else {
err = -EIO;
printf("CAPI error: CONNECTB3_CONF in wrong state %d, info %04x\n",conn->state,c2->info);
exSendD:
if(send_disconnect(conn,0,N1_OutOfOrder) < 0)
setstate(conn,0);
}
}
break;
case CAPI_CONNECTB3ACTIVE_IND:
{
struct CAPI_connectb3active_ind *c2;
if(data->b_wptr-data->b_rptr < sizeof(*c2))
goto less_room;
c2 = ((typeof(c2))data->b_rptr)++;
if((conn = capi_findconn3(talk,capi->appl,c2->ncci)) == NULL) {
printf("CAPI error: CONNECTB3ACTIVE_IND has unknown cref %x%04x\n",capi->appl,c2->ncci);
err = -ENXIO;
break;
}
if(conn->waitflags & (1<<WF_CONNECTB3ACTIVE_IND)) {
conn->waitflags &=~ (1<<WF_CONNECTB3ACTIVE_IND);
err = after_active(conn);
} else {
printf("CAPI error: CONNECTB3ACTIVE_IND in wrong state %d\n",conn->state);
if(send_disconnect(conn,0,N1_OutOfOrder) < 0)
setstate(conn,0);
}
{
int err3 = 0;
struct CAPI_connectb3active_resp *c3;
mblk_t *m3 = allocb(sizeof(*c3),BPRI_MED);
if(m3 == NULL)
err3 = -ENOMEM;
if(err3 == 0) {
c3 = ((typeof(c3))data->b_wptr)++;
bzero(c3,sizeof(*c3));
c3->ncci = c2->ncci;
if((err3 = capi_send(talk,capi->appl,CAPI_CONNECTB3ACTIVE_RESP,m3,capi->messid)) < 0)
freemsg(m3);
}
if(err == 0)
err = err3;
}
}
break;
case CAPI_CONNECT_IND:
{
struct CAPI_connect_ind *c2;
if(data->b_wptr-data->b_rptr < sizeof(*c2))
goto less_room;
c2 = ((typeof(c2))data->b_rptr)++;
if(data->b_wptr-data->b_rptr < c2->telnolen)
goto less_room;
if((conn = capi_findconn(talk,capi->appl,c2->plci)) != NULL) { /* Duplicate. Hmmm. */
printf("CAPI error: Incoming call has dup cref %x%04x for conn %d\n",capi->appl, c2->plci,conn->conn_id);
err = 0;
break;
} else {
conn = isdn3_new_conn (talk);
if (conn == NULL)
err = -ENOMEM;
else {
if((conn->p_data = malloc(sizeof(struct capi_info))) == NULL) {
err = -ENOMEM;
} else {
struct capi_info *info = conn->p_data;
bzero(info,sizeof(*info));
{
int i;
for(i=0;i < talk->card->dchans; i++) {
if(capi->appl == talk->tappl[i]) {
info->subcard = i;
break;
}
}
}
if(c2->DST_eaz) {
info->lnr[0] = '/';
info->lnr[1] = c2->DST_eaz;
info->lnr[2] = '\0';
} else
info->lnr[0] = '\0';
info->service = (c2->DST_service << 8) | c2->DST_addinfo;
if(c2->telnolen > 1) {
int nrlen = c2->telnolen;
switch(*data->b_rptr) {
case 0x91: /* international number */
case 0xA1: /* national number */
data->b_rptr++; nrlen--;
if(data->b_rptr[nrlen-1] == 'S') /* SPV */
nrlen--;
if(nrlen >= MAXNR)
nrlen = MAXNR-1;
bcopy(data->b_rptr,info->nr,nrlen);
break;
default:
err = -EINVAL;
break;
}
}
conn->call_ref = (capi->appl << 16) | c2->plci;
conn->msgid0 = capi->messid;
if(err >= 0) {
setstate(conn,6);
break;
}
}
}
}
/* Reject. */
{
struct CAPI_connect_resp *c3;
mblk_t *mp = allocb(sizeof(*c3),BPRI_MED);
if(mp != NULL) {
c3 = ((struct CAPI_connect_resp *)mp->b_wptr)++;
c3->plci = c2->plci;
c3->reject = N1_OutOfOrder;
if((err = capi_send(talk,capi->appl,CAPI_CONNECT_RESP,mp,capi->messid)) < 0)
freemsg(mp);
}
}
}
break;
case CAPI_INFO_IND:
{
struct CAPI_info_ind *c2;
mblk_t *m3;
struct capi_info *info;
if(data->b_wptr-data->b_rptr < sizeof(*c2))
goto less_room;
c2 = ((typeof(c2))data->b_rptr)++;
if((conn = capi_findconn(talk,capi->appl,c2->plci)) == NULL) {
printf("CAPI error: INFO_IND has unknown cref %x%04x\n",capi->appl,c2->plci);
err = -ENXIO;
break;
}
if(conn->p_data == NULL) {
if((conn->p_data = malloc(sizeof(struct capi_info))) == NULL) {
err = -ENOMEM;
break;
}
bzero(conn->p_data,sizeof(struct capi_info));
}
info = conn->p_data;
m3 = allocb(256,BPRI_MED);
if(m3 != NULL) {
switch(c2->info_number) {
case AI_CAUSE:
#if 0
{
m_putid(m3,IND_INFO);
m_putid(m3,ID_N1_INFO);
m_putsx(m3,ARG_CAUSE);
m_puts(m3,data->b_rptr,c2->infolen);
}
#endif
break;
case AI_DISPLAY:
{
m_putid(m3,IND_INFO);
m_putid(m3,ID_N1_INFO);
m_putsx(m3,ID_N0_display);
m_puts(m3,data->b_rptr,c2->infolen);
}
break;
case AI_DAD:
switch(*data->b_rptr) {
case 0x81:
{
int nrlen = c2->infolen;
int haslen = strlen(info->lnr);
if(haslen == 0) {
haslen = 1;
info->lnr[0] = '/';
}
data->b_rptr++; nrlen--;
if(nrlen >= MAXNR-haslen)
nrlen = MAXNR-haslen-1;
bcopy(data->b_rptr,info->lnr+haslen,nrlen);
info->lnr[nrlen+1]='\0';
setstate(conn,7);
report_incoming(conn);
}
break;
default:
err = -EINVAL;
break;
}
break;
case AI_UUINFO:
break;
case AI_CHARGE:
break;
case AI_DATE:
{
m_putid(m3,IND_INFO);
m_putid(m3,ID_N1_INFO);
m_putsx(m3,ID_N6_date);
m_puts(m3,data->b_rptr,c2->infolen);
}
break;
case AI_CPS:
break;
default:
printf("CAPI error: unknown INFO_IND ID %x\n",c2->info_number);
freemsg(m3); m3 = NULL;
break;
}
}
if(m3 != NULL) {
conn_info (conn, m3);
if ((err = isdn3_at_send (conn, m3, 0)) < 0)
freemsg(m3);
}
}
break;
/* Non-connection-related messages */
case CAPI_REGISTER_CONF:
if(talk->tstate != STATE_REGISTER) {
printf("CAPI error: bad state\n");
break;
}
#if 0
if(talk->message_id != capi->messid) {
printf("CAPI error: register bad ID %d, want %ld\n",capi->messid,talk->message_id);
break;
}
#endif
talk->tstate = STATE_CONF_LAST;
talk->tappl[talk->regnum] = capi->appl;
{
mblk_t *info = talk->card->info;
if(info != NULL) {
streamchar *sta = info->b_rptr;
ushort_t idx;
char skip = 0;
while(m_getid(info,&idx) == 0) {
long sap;
switch(idx) {
case ARG_PROTOCOL:
if (m_geti(info,&sap) == 0) {
skip = (sap != SAPI_CAPI);
}
break;
case ARG_SUBPROT:
if (m_geti(info,&sap) == 0 && !skip) {
switch(sap) {
case SAPI_CAPI_BINTEC:
skip=0;
break;
default:
/* Wrong card. Do something! */
info->b_rptr = sta;
return -ENXIO;
}
}
break;
case ARG_SUBCARD:
if (m_geti(info,&sap) == 0 && !skip)
skip = (sap != talk->regnum+1);
break;
case ARG_EAZ:
if(skip)
break;
if(talk->state & 1<<(talk->regnum+ST_pbx))
break;
{
char eaz;
struct eazmapping *ce;
mblk_t *mp;
int len;
if((err = m_getc(info,&eaz)) < 0)
break;
if((len = m_getstrlen(info)) < 0)
break;
mp = allocb(sizeof(*ce)+len, BPRI_MED);
if(mp == NULL)
break;
ce = ((struct eazmapping *)mp->b_wptr)++;
bzero(ce,sizeof(*ce));
ce->eaz = eaz;
ce->telnolen = len;
if((err = m_getstr(info,mp->b_wptr,len)) < 0) {
freemsg(mp);
break;
}
mp->b_wptr += len;
if((err = capi_sendctl(talk,talk->tappl[talk->regnum],CONTROL_EAZMAPPING,mp,newmsgid(talk))) < 0) {
freemsg(mp);
break;
}
}
break; /* ARG_EAZ */
}
}
info->b_rptr = sta;
}
}
{ /* Tell the board to listen to everything */
struct CAPI_listen_req *c2;
mblk_t *mp = allocb(sizeof(*c2),BPRI_MED);
if(mp == NULL)
return -ENOMEM;
c2 = ((struct CAPI_listen_req *)mp->b_wptr)++;
bzero(c2,sizeof(*c2));
c2->info_mask = 0xC00000FF;
c2->eaz_mask = 0x03FF;
c2->service_mask = 0xE7BF;
{ /* Find correct masks */
int err; char skip = 0;
mblk_t *info = talk->card->info;
if(info != NULL) {
streamchar *sta = info->b_rptr;
ushort_t idx;
while(m_getid(info,&idx) == 0) {
long sap;
switch(idx) {
case ARG_PROTOCOL:
if (m_geti(info,&sap) == 0) {
skip = (sap != SAPI_CAPI);
}
break;
case ARG_SUBPROT:
if (m_geti(info,&sap) == 0 && !skip) {
switch(sap) {
case SAPI_CAPI_BINTEC:
skip=0;
break;
default:
/* Wrong card. TODO: Do something! */
info->b_rptr = sta;
return -ENXIO;
}
}
break;
case ARG_SUBCARD:
if (m_geti(info,&sap) == 0 && !skip)
skip = (sap != talk->regnum+1);
break;
case ARG_LISTEN:
{
long x;
if(skip)
break;
if((err = m_getx(info,&x)) >= 0) {
c2->eaz_mask = x;
if((err = m_getx(info,&x)) >= 0) {
c2->service_mask = x;
if((err = m_getx(info,&x)) >= 0) {
c2->info_mask = x;
}
}
}
}
break;
}
}
info->b_rptr = sta;
}
}
if((err = capi_send(talk,talk->tappl[talk->regnum],CAPI_LISTEN_REQ,mp,newmsgid(talk))) < 0) {
freemsg(mp);
return err;
}
talk->tstate--;
}
break;
case CAPI_LISTEN_CONF:
{
struct CAPI_listen_conf *c2;
c2 = ((typeof(c2))data->b_rptr)++;
if(c2->info != 0) {
printf("CAPI error: LISTEN failed %04x\n",c2->info);
report_nocard(talk,c2->info);
return -EIO;
}
if(talk->tstate < STATE_CONF_FIRST || talk->tstate > STATE_CONF_LAST) {
printf("CAPI error: LISTEN return in bad state\n");
return -EIO;
}
if(++talk->tstate == STATE_CONF_LAST) {
talk->tstate = STATE_RUNNING;
if(++talk->regnum < talk->card->dchans)
send_open(talk);
}
}
break;
case CAPI_ALIVE_IND:
if(talk->tstate < STATE_CONF_FIRST) {
printf("CAPI: ALIVE_REQ in state %ld\n",talk->tstate);
goto printit;
}
err = capi_send(talk,capi->appl,CAPI_ALIVE_RESP,NULL,capi->messid);
break;
case CAPI_CONTROL_CONF:
{
struct CAPI_control_conf *c2;
if(data->b_wptr-data->b_rptr < sizeof(*c2))
goto less_room;
c2 = ((struct CAPI_control_conf *)data->b_rptr)++;
switch(c2->type) {
default:
printf("CAPI: Unknown control_conf type 0x%02x\n",c2->type);
goto printit;
case CONTROL_EAZMAPPING:
{
if(talk->tstate == STATE_RUNNING) {
if((conn = capi_findconnm(talk,capi->appl,capi->messid,WF_CONTROL_EAZ)) == NULL) {
printf("CAPI error: CONTROL_EAZMAPPING has unknown conn\n");
err = -ENXIO;
break;
}
if((c2->info == 0) && (conn->waitflags & (1<<WF_CONTROL_EAZ))) {
conn->waitflags &=~ (1<<WF_CONTROL_EAZ);
err = after_active(conn);
} else {
err = -EIO;
printf("CAPI error: CONTROL_EAZ in wrong state %d, info %04x\n",conn->state,c2->info);
if(send_disconnect(conn,0,N1_OutOfOrder) < 0)
setstate(conn,0);
}
} else if(talk->tstate < STATE_CONF_FIRST || talk->tstate > STATE_CONF_LAST) {
printf("CAPI error: CONTROL_EAZ in wrong state %d, info %04x\n",conn->state,c2->info);
}
if(c2->info != 0) {
printf("CAPI error: EAZMAPPING failed %04x\n",c2->info);
report_nocard(talk,c2->info);
return -EIO;
}
if(++talk->tstate == STATE_CONF_LAST) {
printf("CAPI: EAZMAPPING after LISTEN_CONF ???\n");
talk->tstate = STATE_RUNNING;
if(++talk->regnum < talk->card->dchans)
send_open(talk);
}
}
break;
case CONTROL_API_OPEN: /* Boot: Step One */
if(talk->tstate != STATE_OPENING) {
printf("CAPI error: API_OPEN reply for bad state\n");
return -EINVAL;
}
if(c2->info <= 0) {
printf("CAPI error: open failed!\n");
report_nocard(talk,c2->info);
return -EIO;
}
if(talk->card->dchans != c2->info) {
printf("CAPI: %d D channels -- that should be %d\n",c2->info,talk->card->dchans);
}
{
struct CAPI_register_req *c3;
mblk_t *mp = allocb(sizeof(*c3), BPRI_MED);
if(mp == NULL) /* XXX error processing */
return -ENOMEM;
c3 = ((struct CAPI_register_req *)mp->b_wptr)++;
bzero(c3,sizeof(*c3));
c3->nmess = 10;
c3->nconn = talk->card->bchans + (talk->card->bchans >> 1) + 1;;
c3->ndblock = c3->nconn*10;
c3->dblocksiz = 4096;
if((err = capi_send(talk,talk->tappl[talk->regnum],CAPI_REGISTER_REQ,mp,newmsgid(talk))) < 0) {
freemsg(mp);
break;
}
talk->tstate = STATE_REGISTER;
}
}
}
break;
}
if(err >= 0)
freemsg(data);
return err;
less_room:
printf("CAPI error: too few data bytes\n");
printit:
data->b_rptr = origmb;
log_printmsg(NULL,"CAPI recvErr",data,"=");
if(conn != NULL)
checkterm(conn);
return err ? err : -EINVAL;
}
static int
send (isdn3_conn conn, mblk_t * data)
{
printf("CAPI: send %05lx\n",conn->call_ref);
return -ENXIO;
#if 0
isdn3_prot prot = isdn3_findprot (conn->card->info, conn->subprotocol);
if (prot != NULL && prot->send != NULL)
return (*prot->send) (conn, data);
else {
isdn3_killconn (conn, 1);
return EINVAL;
}
#endif
}
static int
sendcmd (isdn3_conn conn, ushort_t id, mblk_t * data)
{
streamchar *oldpos = NULL;
int err = 0;
ushort_t typ;
/* uchar_t suppress = 0; */
/* uchar_t svc = 0; */
struct capi_info *info;
int force = 0;
uchar_t cause = 0;
if(conn->talk->state == STATE_DEAD)
return -ENXIO;
printf("CAPI: sendcmd %05lx %04x: ",conn->call_ref,id);
{
mblk_t *mb = data;
if(mb == NULL) printf("NULL"); else
while(mb != NULL) {
dumpascii(mb->b_rptr,mb->b_wptr-mb->b_rptr);
mb = mb->b_cont;
}
}
printf("\n");
if(conn->p_data == NULL) {
if((conn->p_data = malloc(sizeof(struct capi_info))) == NULL) {
return -ENOMEM;
}
bzero(conn->p_data,sizeof(struct capi_info));
((struct capi_info *)conn->p_data)->subcard = 0;
}
conn->lockit++;
info = conn->p_data;
if (data != NULL) {
unsigned long x;
oldpos = data->b_rptr;
while ((err = m_getsx (data, &typ)) == 0) {
switch(typ) {
case ARG_CAUSE:
{
extern uchar_t n1_idtocause(ushort_t);
ushort_t causeid;
if (m_getid (data, &causeid) != 0)
break;
cause = n1_idtocause(causeid);
}
break;
case ARG_SUBCARD:
if ((err = m_geti (data, &x)) != 0) {
printf("GetX Subcard: ");
RetErr:
data->b_rptr = oldpos;
conn->lockit--;
return err;
}
if(x < 1 || x > conn->talk->card->dchans) {
err = -ENXIO;
goto RetErr;
}
if(id == CMD_DIAL)
info->subcard = x-1;
else if(info->subcard != x-1) {
printf("GetX Subcard.: %d != %ld-1",info->subcard,x);
err = -EINVAL;
goto RetErr;
}
break;
case ARG_SERVICE:
if ((err = m_getx (data, &x)) != 0) {
printf("GetX Service: ");
goto RetErr;
}
info->service = x;
break;
case ARG_SPV:
info->flags |= INF_SPV;
break;
case ARG_CHANNEL:
if ((err = m_geti (data, &x)) != 0) {
printf("GetX Bchan: ");
goto RetErr;
}
info->bchan = x;
break;
case ARG_LNUMBER:
m_getskip (data);
if ((err = m_getstr (data, (char *) info->lnr, MAXNR)) != 0) {
printf("GetStr LNumber: ");
goto RetErr;
}
break;
case ARG_NUMBER:
m_getskip (data);
if ((err = m_getstr (data, (char *) info->nr, MAXNR)) != 0) {
printf("GetStr Number: ");
goto RetErr;
}
break;
case ARG_FORCE:
force = 1;
break;
default:;
}
}
data->b_rptr = oldpos;
}
err = 0;
switch (id) {
case CMD_ANSWER:
{
if (data == NULL) {
printf("DataNull: ");
conn->lockit--;
return -EINVAL;
}
if(conn->state != 7) {
printf("CAPI error: ANSWER in bad state!\n");
conn->lockit--;
return -EINVAL;
}
err = send_setup(conn);
if(err == 0) {
setstate(conn,8);
freemsg(data);
} else
send_disconnect(conn,0,N1_OutOfOrder);
}
break;
case CMD_DIAL:
{
if (data == NULL) {
printf("DataNull: ");
conn->lockit--;
return -EINVAL;
}
if(conn->state != 0) {
printf("CAPI error: DIAL in bad state!\n");
conn->lockit--;
return -EINVAL;
}
err = send_dialout(conn);
}
break;
case CMD_OFF:
err = send_disconnect(conn,!force,cause);
if(err < 0)
setstate(conn,0);
break;
default:
err = -ENXIO;
break;
}
#if 0
isdn3_prot prot = isdn3_findprot (conn->card->info, conn->subprotocol);
if (prot != NULL)
return (*prot->sendcmd) (conn, id, data);
else {
printf("\n !*!*! ProtNull / %ld::%s !*!*!\n",conn->subprotocol,
conn->card->info ? (char *)conn->card->info->b_rptr : "none");
isdn3_killconn (conn, 1);
return EINVAL;
}
#endif
checkterm(conn);
return err;
}
static void
report (isdn3_conn conn, mblk_t * data)
{
struct capi_info *info;
printf("CAPI: report %05lx: ",conn->call_ref);
{
mblk_t *mb = data;
if(mb == NULL) printf("NULL"); else
while(mb != NULL) {
dumpascii(mb->b_rptr,mb->b_wptr-mb->b_rptr);
mb = mb->b_cont;
}
}
printf("\n");
info = conn->p_data;
if (info == NULL)
return;
if (info->nr[0] != 0) {
m_putsx (data, ARG_NUMBER);
m_putsz (data, info->nr);
}
if (info->lnr[0] != 0) {
m_putsx (data, ARG_LNUMBER);
m_putsz (data, info->lnr);
}
if (info->service != 0) {
m_putsx (data, ARG_SERVICE);
m_putx (data, info->service);
}
if(info->subcard != (unsigned char) ~0) {
m_putsx (data, ARG_SUBCARD);
m_puti (data,info->subcard+1);
}
}
static void
ckill (isdn3_talk talk, char force)
{
printf("CAPI: ckill %d\n",force);
}
static void
killconn (isdn3_conn conn, char force)
{
printf("CAPI: killconn %05lx: %d\n",conn->call_ref,force);
conn->lockit++;
if (force) {
untimer (CAPI_TCONN, conn);
untimer (CAPI_TWAITEAZ, conn);
untimer (CAPI_TFOO, conn);
}
if(conn->state == 0) {
conn->lockit--;
return;
}
send_disconnect(conn, !force, 0);
conn->lockit--;
checkterm(conn);
}
static void
hook (isdn3_conn conn)
{
printf("CAPI: hook %05lx\n",conn->call_ref);
}
static void
newcard (isdn3_card card)
{
isdn3_talk talk;
printf("CAPI: newcard\n");
talk = isdn3_findtalk (card, &CAPI_hndl, card->info, 1);
if(talk == NULL) {
printf("CAPI: newcard: talker not found ???\n");
return;
}
talk->message_id = 0x4000;
talk->tstate = STATE_BOOTING;
if(card->is_up)
chstate (talk, DL_ESTABLISH_CONF,0);
}
static ulong_t
modeflags (long protocol)
{
printf("CAPI: modeflags %lx\n",protocol);
return 1 << M_HDLC || 1 << CHM_INTELLIGENT;
}
static int
proto(struct _isdn3_conn * conn, mblk_t **data, char down)
{
mblk_t *mb = *data;
printf("CAPI: proto %05lx %s: ",conn->call_ref, down ? "down" : "up");
if(mb == NULL) printf("NULL"); else
while(mb != NULL) {
dumpascii(mb->b_rptr,mb->b_wptr-mb->b_rptr);
mb = mb->b_cont;
}
printf("\n");
return 0;
}
static void
init (void)
{
printf("CAPI: init\n");
}
struct _isdn3_hndl CAPI_hndl =
{
NULL, /* SAPI */ 65,0,
&init, &newcard, &modeflags, &chstate, &report, &recv, &send,
&sendcmd, &ckill, &killconn, &hook, &proto,
};
ushort_t
capi_infotoid(ushort_t info)
{
extern ushort_t n1_causetoid(uchar_t);
if((info & 0xFF00) == 0x3400)
return n1_causetoid(info & 0x7F);
switch(info) {
default: return CHAR2('?','?');
case CAPI_E_REGISTER: return ID_E_REGISTER;
case CAPI_E_APPLICATION: return ID_E_APPLICATION;
case CAPI_E_MSGLENGTH: return ID_E_MSGLENGTH;
case CAPI_E_COMMAND: return ID_E_COMMAND;
case CAPI_E_QUEUEFULL: return ID_E_QUEUEFULL;
case CAPI_E_NOMSG: return ID_E_NOMSG;
case CAPI_E_MSGOVERFLOW: return ID_E_MSGOVERFLOW;
case CAPI_E_DEINSTALL: return ID_E_DEINSTALL;
case CAPI_E_CONTROLLER: return ID_E_CONTROLLER;
case CAPI_E_PLCI: return ID_E_PLCI;
case CAPI_E_NCCI: return ID_E_NCCI;
case CAPI_E_TYPE: return ID_E_TYPE;
case CAPI_E_BCHANNEL: return ID_E_BCHANNEL;
case CAPI_E_INFOMASK: return ID_E_INFOMASK;
case CAPI_E_EAZMASK: return ID_E_EAZMASK;
case CAPI_E_SIMASK: return ID_E_SIMASK;
case CAPI_E_B2PROTO: return ID_E_B2PROTO;
case CAPI_E_DLPD: return ID_E_DLPD;
case CAPI_E_B3PROTO: return ID_E_B3PROTO;
case CAPI_E_NCPD: return ID_E_NCPD;
case CAPI_E_NCPI: return ID_E_NCPI;
case CAPI_E_DATAB3FLAGS: return ID_E_DATAB3FLAGS;
case CAPI_E_CONTROLLERFAILED: return ID_E_CONTROLLERFAILED;
case CAPI_E_REGCONFLICT: return ID_E_REGCONFLICT;
case CAPI_E_CMDNOTSUPPORTED: return ID_E_CMDNOTSUPPORTED;
case CAPI_E_PLCIACT: return ID_E_PLCIACT;
case CAPI_E_NCCIACT: return ID_E_NCCIACT;
case CAPI_E_B2NOTSUPPORT: return ID_E_B2NOTSUPPORT;
case CAPI_E_B2STATE: return ID_E_B2STATE;
case CAPI_E_B3NOTSUPPORT: return ID_E_B3NOTSUPPORT;
case CAPI_E_B3STATE: return ID_E_B3STATE;
case CAPI_E_B2DLPDPARA: return ID_E_B2DLPDPARA;
case CAPI_E_B3NCPDPARA: return ID_E_B3NCPDPARA;
case CAPI_E_B3NCPIPARA: return ID_E_B3NCPIPARA;
case CAPI_E_DATALEN: return ID_E_DATALEN;
case CAPI_E_DTMF: return ID_E_DTMF;
case CAPI_E_NOL1: return ID_E_NOL1;
case CAPI_E_NOL2: return ID_E_NOL2;
case CAPI_E_SETUPBCHANLAYER1: return ID_E_SETUPBCHANLAYER1;
case CAPI_E_SETUPBCHANLAYER2: return ID_E_SETUPBCHANLAYER2;
case CAPI_E_ABORTDCHANLAYER1: return ID_E_ABORTDCHANLAYER1;
case CAPI_E_ABORTDCHANLAYER2: return ID_E_ABORTDCHANLAYER2;
case CAPI_E_ABORTDCHANLAYER3: return ID_E_ABORTDCHANLAYER3;
case CAPI_E_ABORTBCHANLAYER1: return ID_E_ABORTBCHANLAYER1;
case CAPI_E_ABORTBCHANLAYER2: return ID_E_ABORTBCHANLAYER2;
case CAPI_E_ABORTBCHANLAYER3: return ID_E_ABORTBCHANLAYER3;
case CAPI_E_REBCHANLAYER2: return ID_E_REBCHANLAYER2;
case CAPI_E_REBCHANLAYER3: return ID_E_REBCHANLAYER3;
case CAPI_E_NOFAX: return ID_E_NOFAX;
case CAPI_E_BADLINE: return ID_E_BADLINE;
case CAPI_E_NOANSWER: return ID_E_NOANSWER;
case CAPI_E_REMDISC: return ID_E_REMDISC;
case CAPI_E_NOCMD: return ID_E_NOCMD;
case CAPI_E_INCOMPAT: return ID_E_INCOMPAT;
case CAPI_E_BADDATA: return ID_E_BADDATA;
case CAPI_E_PROTO: return ID_E_PROTO;
}
}