u-isdn/isdn_3/prot_ETS.c

2477 lines
53 KiB
C

#include "primitives.h"
#include "phone.h"
#include "streamlib.h"
#include "phone_ETSI.h"
#include "q_data.h"
#include "isdn_23.h"
#include "isdn3_phone.h"
#include "isdn_34.h"
#include "kernel.h"
#include "q_data.h"
#include "phone_ETSI.h"
#include "sapi.h"
#undef HAS_SUSPEND
#define FAC_PENDING 002
#define RUN_ET_T301 01
#define RUN_ET_T302 02
#define RUN_ET_T303 04
#define RUN_ET_T304 010
#define RUN_ET_T305 020
#define RUN_ET_T308 040
#ifdef HAS_RECOVERY
#define RUN_ET_T309 0100
#endif
#define RUN_ET_T310 0200
#define RUN_ET_T313 0400
#define RUN_ET_T314 01000
#define RUN_ET_T316 02000
#define RUN_ET_T317 04000
#ifdef HAS_SUSPEND
#define RUN_ET_T318 010000
#define RUN_ET_T319 020000
#endif
#define RUN_ET_T321 040000
#define RUN_ET_T322 0100000
#define RUN_ET_TCONN 0200000
#define RUN_ET_TALERT 0400000
#define REP_T303 01000000
#define RUN_ET_TFOO 02000000
#define VAL_ET_T301 ( 180 *HZ) /* timeout when calling out */
#define VAL_ET_T302 ( 15 *HZ)
#define VAL_ET_T303 ( 10 *HZ) /* was 4. L1 startup, TEI, L2 startup ... */
#define VAL_ET_T304 ( 30 *HZ)
#define VAL_ET_T305 ( 30 *HZ)
#define VAL_ET_T308 ( 8 *HZ) /* was 4. Hmmm... */
#ifdef HAS_RECOVERY
#define VAL_ET_T309 ( 90 *HZ)
#endif
#define VAL_ET_T310 ( 50 *HZ)
#define VAL_ET_T313 ( 8 *HZ) /* Should be 4 */
#define VAL_ET_T316 ( 120 *HZ)
#define VAL_ET_T317 ( 90 *HZ)
#ifdef HAS_SUSPEND
#define VAL_ET_T318 ( 4 *HZ)
#define VAL_ET_T319 ( 4 *HZ)
#endif
#define VAL_ET_T321 ( 30 *HZ)
#define VAL_ET_T322 ( 4 *HZ)
#define VAL_ET_TCONN ( 40 *HZ) /* timer for connection establishment */
#define VAL_ET_TALERT ( 1 *HZ) /* timer for delaying an ALERT response */
#define VAL_ET_TFOO ( 7*HZ)
static void ET_T301 (isdn3_conn conn);
static void ET_T302 (isdn3_conn conn);
static void ET_T303 (isdn3_conn conn);
static void ET_T304 (isdn3_conn conn);
static void ET_T305 (isdn3_conn conn);
static void ET_T308 (isdn3_conn conn);
#ifdef HAS_RECOVERY
static void ET_T309 (isdn3_conn conn);
#endif
static void ET_T310 (isdn3_conn conn);
static void ET_T313 (isdn3_conn conn);
static void ET_T316 (isdn3_conn conn);
static void ET_T317 (isdn3_conn conn);
#ifdef HAS_SUSPEND
static void ET_T318 (isdn3_conn conn);
static void ET_T319 (isdn3_conn conn);
#endif
static void ET_T321 (isdn3_conn conn);
static void ET_T322 (isdn3_conn conn);
static void ET_TCONN (isdn3_conn conn);
static void ET_TALERT (isdn3_conn conn);
static void ET_TFOO (isdn3_conn conn);
static int send_disc (isdn3_conn conn, char release, mblk_t * data);
static void Xreport_terminate (isdn3_conn conn, uchar_t * data, int len, ushort_t cause, int deb_line);
struct e_info {
unsigned char flags;
unsigned char bearer_len,llc_len,ulc_len;
unsigned char bearer[30],llc[10],ulc[10];
unsigned char nr[MAXNR+2];
unsigned char lnr[MAXNR+2];
};
static void
phone_timerup (isdn3_conn conn)
{
rtimer (ET_T301, conn);
rtimer (ET_T302, conn);
rtimer (ET_T303, conn);
rtimer (ET_T304, conn);
rtimer (ET_T305, conn);
rtimer (ET_T308, conn);
#ifdef HAS_RECOVERY
rtimer (ET_T309, conn);
#endif
rtimer (ET_T310, conn);
rtimer (ET_T313, conn);
rtimer (ET_T316, conn);
rtimer (ET_T317, conn);
#ifdef HAS_SUSPEND
rtimer (ET_T318, conn);
rtimer (ET_T319, conn);
#endif
rtimer (ET_T321, conn);
rtimer (ET_T322, conn);
rtimer (ET_TCONN, conn);
rtimer (ET_TALERT, conn);
rtimer (ET_TFOO, conn);
}
#define setstate(a,b) Xsetstate((a),(b),__LINE__)
static void
Xsetstate (isdn3_conn conn, uchar_t state, int deb_line)
{
if(log_34 & 2)printf ("Conn PostET:%d %ld: State %d --> %d\n", deb_line, conn->call_ref, conn->state, state);
if (conn->state == state)
return;
switch (conn->state) {
case 1:
untimer (ET_T303, conn);
break;
case 2:
untimer (ET_T304, conn);
break;
case 3:
untimer (ET_T310, conn);
break;
case 8:
untimer (ET_T313, conn);
break;
case 7:
untimer (ET_TCONN, conn);
break;
case 6:
untimer (ET_TCONN, conn);
untimer (ET_TALERT, conn);
break;
case 11:
untimer (ET_T305, conn);
break;
#ifdef HAS_SUSPEND
case 15:
untimer (ET_T319, conn);
break;
case 17:
untimer (ET_T318, conn);
break;
#endif
case 19:
untimer (ET_T308, conn);
break;
case 25:
untimer (ET_T302, conn);
break;
case 99:
untimer (ET_TFOO, conn);
break;
}
conn->state = state;
if(state == 0 || state > 10)
Xreport_terminate (conn, NULL,0,0, deb_line);
switch (conn->state) {
case 1:
timer (ET_T303, conn);
break;
case 2:
timer (ET_T304, conn);
break;
case 3:
timer (ET_T310, conn);
break;
case 6:
ftimer (ET_TALERT, conn);
/* FALL THRU */
case 7:
ftimer (ET_TCONN, conn);
break;
case 8:
timer (ET_T313, conn);
break;
case 11:
timer (ET_T305, conn);
break;
#ifdef HAS_SUSPEND
case 15:
timer (ET_T319, conn);
break;
case 17:
timer (ET_T318, conn);
break;
#endif
case 19:
timer (ET_T308, conn);
break;
case 25:
timer (ET_T302, conn);
break;
case 99:
timer (ET_TFOO, conn);
break;
}
}
static void
report_addfac (mblk_t * mb, uchar_t * data, ushort_t len)
{
QD_INIT (data, len) return;
QD {
QD_CASE (0, PT_E0_facility):
if (qd_len == 0) {
printf("FacL 1 is %d\n",qd_len);
break;
}
if((*qd_data & 0x1F) != 0x11) {
printf("Fac 2 is %x\n",*qd_data);
break;
}
while(qd_len > 0 && !(*qd_data & 0x80)) {
qd_data++; qd_len--;
}
if(qd_len < 2) {
printf("FacL 2 is %d\n",qd_len);
break;
}
qd_data++; qd_len--;
if((*qd_data & 0xE0) != 0xA0) {
printf("Fac 3 is %x\n",*qd_data);
break;
}
switch(*qd_data & 0x1F) {
case 1: /* invoke */
{
unsigned char nlen, ilen;
int ident;
qd_data++; qd_len--;
if(qd_len < 1) {
printf("FacL 4 is %d\n",qd_len);
break;
}
if(*qd_data & 0x80) { /* length format */
printf("Fac 4 is %x\n",*qd_data);
break;
}
nlen = *qd_data++; qd_len--;
if(qd_len < nlen) {
printf("FacL 5 is %d %d\n",qd_len,nlen);
return;
}
qd_len -= nlen;
if(nlen < 2) {
printf("FacL 6 is %d\n",nlen);
return;
}
if(*qd_data != 0x02) {
printf("Fac 5 is %x\n",*qd_data);
return;
}
qd_data++; nlen--;
if(*qd_data & 0x80) { /* length format */
printf("Fac 6 is %x\n",*qd_data);
break;
}
ilen = *qd_data++; nlen--;
if(ilen > nlen || ilen == 0) {
printf("FacL 6a is %d %d\n",ilen,nlen);
return;
}
nlen -= ilen;
ident = 0;
while(ilen > 0) {
ident = (ident << 8) | (*qd_data++ & 0xFF);
ilen--;
}
if(nlen < 2) {
printf("FacL 7 is %d\n",nlen);
return;
}
if(*qd_data != 0x02)
return;
qd_data++; nlen--;
ilen = *qd_data++; nlen--;
if(ilen > nlen || ilen == 0) {
printf("FacL 8 is %d %d\n",ilen,nlen);
return;
}
nlen -= ilen;
ident = 0;
while(ilen > 0) {
ident = (ident << 8) | (*qd_data++ & 0xFF);
ilen--;
}
#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7))
#define UNUSED __attribute__((unused))
#else
#define UNUSED
#endif
#define FOO1(s,a,b) \
while(nlen > 1) { \
int ilen = qd_data[1]; \
if(nlen < ilen+2) { \
printf("FooL" ##s " is %d,%d\n",nlen,ilen); \
return; \
} \
nlen -= ilen+2; \
if((*qd_data & 0xFF) == (a)) { \
int nlen UNUSED = ilen; \
qd_data += 2; \
b; \
} else { \
printf("Foo " ##s " is %x\n",*qd_data & 0xFF); \
qd_data += ilen+2; \
} \
}
switch(ident) {
default:
printf("Fac 8 is %x\n",ident);
break;
case 0x22: /* during */
FOO1("1A",0x30,FOO1("1C",0xA1,FOO1("1D",0x30,FOO1("1E",0x02,({
ident = 0;
while(ilen > 0) {
ident = (ident<<8) | *qd_data++;
ilen--;
}
m_putsx (mb, ARG_CHARGE);
m_puti (mb, ident);
})))))
break;
case 0x24: /* final */
FOO1("2A",0x30,FOO1("2B",0x30,FOO1("2C",0xA1,FOO1("2D",0x30,FOO1("2E",0x02,({
ident = 0;
while(ilen > 0) {
ident = (ident<<8) | *qd_data++;
ilen--;
}
m_putsx (mb, ARG_CHARGE);
m_puti (mb, ident);
}))))))
break;
}
#undef FOO1
}
break;
case 2: /* return result */
printf("Fac 1 is %x\n",*qd_data);
break;
case 3: /* return error */
printf("Fac 1 is %x\n",*qd_data);
break;
default:
printf("Fac 1 is %x\n",*qd_data);
break;
}
}
}
static uchar_t
report_addcause (mblk_t * mb, uchar_t * data, int len)
{
int qd_len;
uchar_t *qd_data;
uchar_t loc, rec = 0;
qd_data = qd_find (data, len, 0, PT_E0_cause, &qd_len);
if (qd_data == NULL)
return 0;
if (qd_len < 1)
return 0;
m_putsx (mb, ARG_CAUSE);
loc = *qd_data & 0x7F;
if (!(loc & 0x80) && (qd_len > 0)) {
rec = *++qd_data & 0x7F;
--qd_len;
}
if(qd_len > 0)
m_putsx2(mb, et_causetoid(*qd_data&0x7F));
while(qd_len > 0 && !(*qd_data++ & 0x80))
--qd_len;
m_putx(mb,loc);
if(rec != 0) {
m_putx(mb,rec);
}
if(qd_len > 0)
m_puthex(mb,qd_data,qd_len);
return *qd_data;
}
static void
report_addisplay (mblk_t * mb, uchar_t * data, int len, isdn3_conn conn)
{
int qd_len;
uchar_t *qd_data;
qd_data = qd_find (data, len, 0, PT_E0_display, &qd_len);
if (qd_data == NULL)
return;
if (qd_len < 1)
return;
m_putsx (mb, ID_E0_display);
m_puts (mb, qd_data, qd_len);
/*
* The Dutch PTT apparently sends a DISPLAY string with 'N40*999#'
* -- replace 999 with the numbr of units.
* Needless to say, that's _really_ stupid, but what to do ...
*/
if((qd_len > 5) &&
(qd_data[0] == 'N') &&
(qd_data[1] == '4') &&
(qd_data[2] == '0') &&
(qd_data[3] == '*') &&
(qd_data[qd_len-1] == '#') ) {
long flags = isdn3_flags(conn->card->info,-1,-1);
if(flags & FL_BUG2) {
m_putsx (mb, ARG_CHARGE);
m_puts (mb, qd_data+4,qd_len-5);
}
}
}
static void
report_addprogress (mblk_t * mb, uchar_t * data, int len)
{
QD_INIT (data, len)
QD {
QD_CASE (0, PT_E0_progress):
if (qd_len < 3)
continue;
m_putsx (mb, ID_E0_progress);
m_puti (mb, qd_data[2]);
m_puti (mb, qd_data[1] & 0x0F);
m_puti (mb,(qd_data[1] & 0x60) >> 5);
}
QD_EXIT;
}
static void
report_adddate (mblk_t * mb, uchar_t * data, int len)
{
int qd_len;
uchar_t *qd_data;
qd_data = qd_find (data, len, 6, PT_E0_date, &qd_len);
if (qd_data == NULL)
return;
if (qd_len < 1)
return;
m_putsx (mb, ID_E0_date);
m_puts (mb, qd_data, qd_len);
}
#if 0
static int
get_state(isdn3_conn conn, uchar_t *data, int len, uchar_t *state)
{
int qd_len;
uchar_t *qd_data;
qd_data = qd_find (data, len, 0, PT_E0_callState, &qd_len);
if (qd_data == NULL)
return 0;
if(qd_len < 1)
return 0;
*state = *qd_data;
return 1;
}
#endif
static int
get_hex(isdn3_conn conn, uchar_t * data, int len, uchar_t *addr, uchar_t *addrlen, uchar_t maxlen, uchar_t what)
{
int qd_len;
uchar_t *qd_data;
qd_data = qd_find (data, len, 0, what, &qd_len);
if (qd_data == NULL)
return 0;
if(qd_len > maxlen)
qd_len = maxlen;
bcopy(qd_data,addr,qd_len);
*addrlen = qd_len;
return 1;
}
static int
get_nr (isdn3_conn conn, uchar_t * data, int len, uchar_t *nrpos, uchar_t what)
{
int qd_len;
uchar_t *qd_data;
*nrpos = '\0';
qd_data = qd_find (data, len, 0, what, &qd_len);
if (qd_data == NULL)
return 0;
if (qd_len >= MAXNR)
qd_len = MAXNR-1;
if (qd_len < 1)
return 0;
switch(*qd_data & 0x70) {
case 0x00: /* unknown */
{
long flags = isdn3_flags(conn->card->info,-1,-1);
if(flags & FL_BUG1) {
if(qd_data[0] == 0x00 && qd_data[1] == 0x83)
*nrpos++ = '='; /* at least one PBX is stupid */
else if(qd_data[0] == 0x01 && qd_data[1] == 0x80)
*nrpos++ = '='; /* another terminally stupid PBX */
else if(qd_data[0] == 0x81)
*nrpos++='.'; /* the first PBX, above, again */
}
}
break;
case 0x10: /* international */
*nrpos++='+';
break;
case 0x20: /* national */
*nrpos++='=';
break;
case 0x30: /* network specific */
break;
case 0x40: /* subscriber */
/* There is at least one _really_ broken box out there */
if((qd_len < 4) && (what == PT_E0_destAddr)) {
long flags = isdn3_flags(conn->card->info,-1,-1);
if(flags & FL_BUG1) {
*nrpos++='.';
break;
}
}
*nrpos++='-';
break;
case 0x60: /* abbreviated */
*nrpos++='.';
break;
case 0x70: /* extension */
*nrpos++='.';
break;
}
while (qd_len-- > 0 && (*qd_data++ & 0x80) == 0) ;
if (qd_len < 1)
return 0;
while (qd_len-- > 0) {
*nrpos++ = *qd_data++;
}
*nrpos = '\0';
return 1;
}
static int
get_chanID (isdn3_conn conn, uchar_t * data, int len)
{
int qd_len;
uchar_t *qd_data;
qd_data = qd_find (data, len, 0, PT_E0_chanID, &qd_len);
if (qd_data == NULL)
return 0;
if (qd_len < 1)
return 0;
switch (*qd_data & 0x60) {
case 0x00:
case 0x40:
conn->bchan = *qd_data & 0x03;
if (conn->bchan == 3) {
if (conn->card == NULL)
conn->bchan = 0;
else
conn->bchan = isdn3_free_b (conn->card);
} else if (conn->bchan != 0) {
if(!(conn->minorstate & MS_BCHAN)) {
conn->minorstate |= MS_BCHAN;
isdn3_setup_conn(conn,EST_SETUP);
}
}
break;
case 0x60:
case 0x20:
if ((*qd_data & 0x03) != 1) {
conn->bchan = 0;
break;
}
if (*qd_data & 0x40)
while((--qd_len > 0) && !(*++qd_data & 0x80)) ;
if (qd_len < 3)
return 0;
if (qd_data[1] != 0x83)
return 0;
conn->bchan = qd_data[2];
if (conn->bchan != 0) {
if(!(conn->minorstate & MS_BCHAN)) {
conn->minorstate |= MS_BCHAN;
isdn3_setup_conn(conn,EST_SETUP);
}
}
if (conn->card != NULL && conn->card->bchans < conn->bchan)
return 0;
break;
default:
return 0;
}
return 1;
}
static int
report_setup (isdn3_conn conn, uchar_t * data, int len)
/* Send SETUP up */
{
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);
report_addisplay (mb, data, len, conn);
report_addprogress (mb, data, len);
if ((err = isdn3_at_send (conn, mb, 0)) < 0) {
freemsg (mb);
setstate (conn, 99);
return err;
}
return err;
}
static int
report_generic (isdn3_conn conn, uchar_t * data, int len, ushort_t id)
{
int err = 0;
mblk_t *mb = allocb (256, BPRI_MED);
if (mb == NULL) {
setstate (conn, 0);
return -ENOMEM;
}
m_putid (mb, IND_INFO);
m_putid (mb, id);
conn_info (conn, mb);
report_addisplay (mb, data, len, conn);
report_addfac (mb, data, len);
report_addprogress (mb, data, len);
if ((err = isdn3_at_send (conn, mb, 0)) < 0) {
freemsg (mb);
setstate (conn, 0);
return err;
}
return err;
}
static int
report_user_info (isdn3_conn conn, uchar_t * data, int len)
{
int err = 0;
int qd_len;
uchar_t *qd_data;
mblk_t *mb = allocb (256, BPRI_MED);
if (mb == NULL) {
setstate (conn, 0);
return -ENOMEM;
}
m_putid (mb, IND_INFO);
m_putid (mb, ID_ET_USER_INFO);
conn_info (conn, mb);
qd_data = qd_find (data, len, 0, PT_E0_userInfo, &qd_len);
if (qd_data == NULL)
return 0;
m_putsx (mb, ID_E0_userInfo);
m_puti (mb,*qd_data);
m_puthex (mb, qd_data+1, qd_len-1);
qd_data = qd_find (data, len, 0, PT_E0_moreData, &qd_len);
if (qd_data != NULL)
m_putsx (mb, ID_E0_moreData);
if ((err = isdn3_at_send (conn, mb, 0)) < 0) {
freemsg (mb);
setstate (conn, 99);
return err;
}
return err;
}
static int
report_notify (isdn3_conn conn, uchar_t * data, int len)
{
int qd_len;
uchar_t *qd_data;
qd_data = qd_find (data, len, 0, PT_E0_notifyInd, &qd_len);
if (qd_data == NULL)
return 0;
if(qd_len < 1)
return 0;
if ((*qd_data & 0x7F) == 0)
return (isdn3_setup_conn (conn, EST_DISCONNECT /* was INTERRUPT */ ) != 0);
else if ((*qd_data & 0x7F) == 1)
return (isdn3_setup_conn (conn, EST_CONNECT) != 0);
else
return 0;
}
static int
report_conn (isdn3_conn conn, uchar_t * data, int len)
{
int err = 0;
mblk_t *mb = allocb (256, BPRI_MED);
if (mb == NULL) {
setstate (conn, 0);
return -ENOMEM;
}
m_putid (mb, IND_CONN);
conn_info (conn, mb);
report_addisplay (mb, data, len, conn);
report_addfac (mb, data, len);
report_adddate (mb, data, len);
if ((err = isdn3_at_send (conn, mb, 0)) < 0) {
freemsg (mb);
setstate (conn, 99);
return err;
}
return err;
}
static int
report_conn_ack (isdn3_conn conn, uchar_t * data, int len)
{
int err = 0;
mblk_t *mb = allocb (256, BPRI_MED);
if (mb == NULL) {
setstate (conn, 0);
return -ENOMEM;
}
m_putid (mb, IND_INFO);
m_putid (mb, ID_ET_CONN_ACK);
conn_info (conn, mb);
report_addisplay (mb, data, len, conn);
report_addfac (mb, data, len);
report_adddate (mb, data, len);
if ((err = isdn3_at_send (conn, mb, 0)) < 0) {
freemsg (mb);
setstate (conn, 99);
return err;
}
return err;
}
#define report_terminate(a,b,c,d) Xreport_terminate((a),(b),(c),(d),__LINE__)
static void
Xreport_terminate (isdn3_conn conn, uchar_t * data, int len, ushort_t cause, int deb_line)
{
int err = 0;
mblk_t *mb = allocb (256, BPRI_MED);
extern int log_34;
if(log_34 & 2)
printf("\nET Terminate at %d.\n",deb_line);
if (mb == NULL) {
setstate (conn, 0);
return;
}
if (conn->state == 0 || conn->state == 99)
m_putid (mb, IND_DISCONNECT);
else
m_putid (mb, IND_DISCONNECTING);
if(cause != 0) {
m_putsx(mb,ARG_CAUSE);
m_putsx2(mb,cause);
}
conn_info (conn, mb);
if (data != NULL) {
report_addisplay (mb, data, len, conn);
if(cause == 0)
report_addcause (mb, data, len);
report_addfac (mb, data, len);
}
if ((err = isdn3_at_send (conn, mb, 0)) < 0) {
freemsg (mb);
setstate (conn, 99);
return;
}
return;
}
#define checkterm(a,b,c) Xcheckterm((a),(b),(c),__LINE__)
static void
Xcheckterm (isdn3_conn conn, uchar_t * data, int len, int deb_line)
{
if (conn->state == 0)
isdn3_killconn (conn, 1); /* XXX */
}
static void
ET_T301 (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_T301\n");
conn->timerflags &= ~RUN_ET_T301;
switch (conn->state) {
}
checkterm (conn, NULL, 0);
}
static void
ET_T302 (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_T302\n");
conn->timerflags &= ~RUN_ET_T302;
switch (conn->state) {
case 25:
/* Timeout ind */
/* no state change */
break;
}
checkterm (conn, NULL, 0);
}
static void
ET_T303 (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_T303\n");
/* DSS1: retry the setup. We decide not to bother. */
conn->timerflags &= ~RUN_ET_T303;
switch (conn->state) {
case 1:
send_disc (conn, 0,NULL);
isdn3_setup_conn (conn, EST_DISCONNECT);
setstate (conn, 99);
report_terminate (conn,NULL,0,ID_NOREPLY);
break;
}
checkterm (conn, NULL, 0);
}
static void
ET_T304 (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_T304\n");
conn->timerflags &= ~RUN_ET_T304;
switch (conn->state) {
case 2:
{
mblk_t *data = allocb(16,BPRI_MED);
if(data != NULL) {
unsigned char * qd_d;
int qd_len = 0;
if ((qd_d = qd_insert ((uchar_t *) data->b_rptr, &qd_len, 0, PT_E0_cause, 2, 0)) == NULL) {
freemsg(data);
data = NULL;
} else {
*qd_d++ = 0x80;
*qd_d++ = 0x80 | ET_TimerRecovery;
data->b_wptr += qd_len;
}
}
if(phone_sendback (conn, MT_ET_DISC, data) != 0)
freemsg(data);
}
/* send error up */
setstate (conn,11);
}
checkterm (conn, NULL, 0);
}
static void
ET_T305 (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_T305\n");
conn->timerflags &= ~RUN_ET_T305;
switch (conn->state) {
case 11:
phone_sendback(conn, MT_ET_REL, NULL);
setstate(conn,19);
}
checkterm (conn, NULL, 0);
}
static void
ET_T308 (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_T308\n");
conn->timerflags &= ~RUN_ET_T308;
switch (conn->state) {
case 19:
{
mblk_t *data;
data = allocb(16,BPRI_MED);
if(data != NULL) {
unsigned char * qd_d;
int qd_len = 0;
if ((qd_d = qd_insert ((uchar_t *) data->b_rptr, &qd_len, 0, PT_E0_cause, 2, 0)) == NULL) {
freemsg(data);
} else {
*qd_d++ = 0x80;
*qd_d++ = 0x80 | ET_TimerRecovery;
data->b_wptr += qd_len;
if (phone_sendback (conn, MT_ET_DISC, data) < 0)
freemsg(data);
}
}
}
setstate(conn,99);
break;
}
checkterm (conn, NULL, 0);
}
#ifdef HAS_RECOVERY
static void
ET_T309 (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_T309\n");
conn->timerflags &= ~RUN_ET_T309;
switch (conn->state) {
}
checkterm (conn, NULL, 0);
}
#endif
static void
ET_T310 (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_T310\n");
conn->timerflags &= ~RUN_ET_T310;
switch (conn->state) {
case 3:
{
mblk_t *data = allocb(16,BPRI_MED);
if(data != NULL) {
unsigned char * qd_d;
int qd_len = 0;
if ((qd_d = qd_insert ((uchar_t *) data->b_rptr, &qd_len, 0, PT_E0_cause, 2, 0)) == NULL) {
freemsg(data);
data = NULL;
} else {
*qd_d++ = 0x80;
*qd_d++ = 0x80 | ET_TimerRecovery;
data->b_wptr += qd_len;
}
}
if(phone_sendback (conn, MT_ET_DISC, data) != 0)
freemsg(data);
}
/* send error up */
setstate (conn,11);
break;
}
checkterm (conn, NULL, 0);
}
static void
ET_T313 (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_T313\n");
conn->timerflags &= ~RUN_ET_T313;
switch (conn->state) {
case 8:
{
mblk_t *data = allocb(16,BPRI_MED);
if(data != NULL) {
unsigned char * qd_d;
int qd_len = 0;
if ((qd_d = qd_insert ((uchar_t *) data->b_rptr, &qd_len, 0, PT_E0_cause, 2, 0)) == NULL) {
freemsg(data);
data = NULL;
} else {
*qd_d++ = 0x80;
*qd_d++ = 0x80 | ET_TimerRecovery;
data->b_wptr += qd_len;
}
}
if(phone_sendback (conn, MT_ET_DISC, data) != 0)
freemsg(data);
}
/* send error up */
setstate (conn,11);
break;
}
checkterm (conn, NULL, 0);
}
static void
ET_T314 (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_T314\n");
conn->timerflags &= ~RUN_ET_T314;
switch (conn->state) {
}
checkterm (conn, NULL, 0);
}
static void
ET_T316 (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_T316\n");
conn->timerflags &= ~RUN_ET_T316;
switch (conn->state) {
}
checkterm (conn, NULL, 0);
}
static void
ET_T317 (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_T317\n");
conn->timerflags &= ~RUN_ET_T317;
switch (conn->state) {
}
checkterm (conn, NULL, 0);
}
#ifdef HAS_SUSPEND
static void
ET_T318 (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_T318\n");
conn->timerflags &= ~RUN_ET_T318;
switch (conn->state) {
case 17:
/* Err */
phone_sendback(conn,MT_ET_REL,NULL);
setstate (conn, 19);
break;
}
checkterm (conn, NULL, 0);
}
static void
ET_T319 (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_T319\n");
conn->timerflags &= ~RUN_ET_T319;
switch (conn->state) {
case 15:
/* Err */
setstate (conn, 10);
break;
}
checkterm (conn, NULL, 0);
}
#endif
static void
ET_T321 (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_T321\n");
conn->timerflags &= ~RUN_ET_T321;
switch (conn->state) {
case 17:
/* Err */
setstate (conn, 99);
break;
}
checkterm (conn, NULL, 0);
}
static void
ET_T322 (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_T322\n");
conn->timerflags &= ~RUN_ET_T322;
switch (conn->state) {
case 17:
/* Err */
setstate (conn, 99);
break;
}
checkterm (conn, NULL, 0);
}
static void
release_postET (isdn3_conn conn, uchar_t minor, char force)
{
switch (conn->state) {
case 1:
untimer (ET_T303, conn);
if (force) {
/* technically an error */
}
break;
case 2:
untimer (ET_T304, conn);
if (force) {
/* technically an error */
}
break;
case 3:
untimer (ET_T310, conn);
if (force) {
/* technically an error */
}
break;
case 8:
untimer (ET_T313, conn);
/* FALL THRU */
case 4:
case 7:
case 10:
if (force) {
/* technically an error */
}
/* FALL THRU */
case 14:
break;
case 6:
break;
#ifdef HAS_SUSPEND
case 15:
untimer (ET_T319, conn);
if (force) {
/* technically an error */
}
break;
case 17:
untimer (ET_T318, conn);
if (force) {
/* technically an error */
}
break;
#endif
case 11:
untimer (ET_T305, conn);
if (!force) {
/* Technically an error */
force = 1;
}
case 12:
force = 1;
break;
case 19:
/* Technically an error */
return;
default:
setstate (conn, 99);
return;
}
if (force) {
phone_sendback (conn, MT_ET_REL, NULL);
switch (conn->state) {
#ifdef HAS_SUSPEND
case 15:
#endif
case 10:
case 4:
case 3:
case 2:
case 11:
isdn3_setup_conn (conn, EST_DISCONNECT);
}
conn->bchan = 0;
setstate (conn, 19);
} else {
mblk_t *data = allocb(16,BPRI_MED);
if(data != NULL) {
unsigned char * qd_d;
int qd_len = 0;
if ((qd_d = qd_insert ((uchar_t *) data->b_rptr, &qd_len, 0, PT_E0_cause, 2, 0)) == NULL) {
freemsg(data);
data = NULL;
} else {
*qd_d++ = 0x80;
*qd_d++ = 0x80 | ET_TimerRecovery;
data->b_wptr += qd_len;
}
}
if(phone_sendback (conn, MT_ET_DISC, data) != 0)
freemsg(data);
setstate (conn, 11);
}
}
static void ET_TFOO(isdn3_conn conn)
{
setstate(conn, 0);
isdn3_killconn (conn, 1); /* XXX */
}
static void
ET_TCONN (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_TCONN\n");
conn->timerflags &= ~RUN_ET_TCONN;
conn->lockit++;
switch (conn->state) {
case 7: {
mblk_t *mb = NULL;
int qd_len = 0;
streamchar *qd_d;
if((mb = allocb(20,BPRI_LO)) != NULL) {
if ((qd_d = qd_insert ((uchar_t *) mb->b_rptr, &qd_len, 0, PT_E0_cause, 2, 0)) == NULL) {
freeb(mb);
mb = NULL;
} else {
*qd_d++ = 0x80; /* CCITT; User */
if(!(conn->minorstate & MS_BCHAN))
*qd_d = ET_UserBusy;
else if(!(conn->minorstate & MS_SETUP_MASK))
*qd_d = ET_NoAnswer; /* XXX ;-) */
else
*qd_d = ET_OutOfOrder;
*qd_d |= 0x80;
mb->b_wptr = mb->b_rptr + qd_len;
}
}
if(send_disc (conn, 0, mb) != 0 && mb != NULL)
freemsg(mb);
goto term;
}
case 6:
setstate (conn, 99);
break;
}
term:
conn->lockit--;
checkterm (conn, NULL, 0);
}
static void
ET_TALERT (isdn3_conn conn)
{
if(log_34 & 2)printf ("Timer ET_TALERT\n");
conn->timerflags &= ~RUN_ET_TALERT;
conn->lockit++;
switch (conn->state) {
case 6:
{
mblk_t *asn = NULL;
if(phone_sendback (conn, MT_ET_ALERT, asn) != 0 && asn != NULL)
freemsg(asn);
setstate (conn, 7);
}
break;
}
conn->lockit--;
checkterm (conn, NULL, 0);
}
static int
recv (isdn3_conn conn, uchar_t msgtype, char isUI, uchar_t * data, ushort_t len)
{
struct e_info *info;
printf (" ET: Recv %x in state %d\n", msgtype, conn->state);
if(conn->p_data == NULL) {
if((conn->p_data = malloc(sizeof(struct e_info))) == NULL) {
return -ENOMEM;
}
bzero(conn->p_data,sizeof(struct e_info));
}
info = (struct e_info *)conn->p_data;
switch(msgtype) {
case MT_ET_STAT_ENQ:
report_generic (conn, data, len, ID_ET_STAT_ENQ);
{
mblk_t *asn = NULL;
int qd_len = 0;
uchar_t *qd_d;
if ((asn = allocb (32, BPRI_MED)) != NULL) {
if ((qd_d = qd_insert ((uchar_t *) asn->b_rptr, &qd_len, 0, PT_E0_cause, 2, 1)) != NULL) {
*(uchar_t *) qd_d++ = 0x80;
*(ushort_t *) qd_d = 0x80;
}
if ((qd_d = qd_insert ((uchar_t *) asn->b_rptr, &qd_len, 0, PT_E0_callState, 1, 1)) != NULL) {
*(ushort_t *) qd_d = conn->state;
}
asn->b_wptr = asn->b_rptr + qd_len;
}
if(phone_sendback (conn, MT_ET_STAT, asn) != 0 && asn != NULL)
freemsg(asn);
}
break;
case MT_ET_INFO:
if (conn->state == 0 || conn->state == 1 || conn->state == 6
|| conn->state == 17 || conn->state == 19
|| conn->state == 25)
goto do_continue;
report_generic (conn, data, len, ID_ET_INFO);
break;
case MT_ET_FAC:
report_generic (conn, data, len, ID_ET_FAC);
break;
case MT_ET_STAT:
{
int qd_len;
uchar_t *qd_data;
uchar_t state = 0;
qd_data = qd_find (data, len, 0, PT_E0_callState, &qd_len);
if (qd_data != NULL && qd_len > 0)
state = *qd_data;
if((conn->state == 0 || conn->state == 19) && state != 0) {
phone_sendback (conn, MT_ET_REL, NULL);
setstate (conn, 19);
}
}
report_generic (conn, data, len, ID_ET_STAT);
break;
case MT_ET_REL_COM:
if (conn->state == 0 || conn->state == 1 || conn->state == 19)
goto do_continue;
isdn3_setup_conn (conn, EST_DISCONNECT);
ComEx:
if(conn->state == 6 || conn->state == 7 || conn->state == 99)
setstate (conn, 99);
else
setstate (conn, 0);
report_terminate (conn, data, len,0);
break;
case MT_ET_REL:
if (conn->state == 0 || conn->state == 1 || conn->state == 6
|| conn->state == 11 || conn->state == 12
|| conn->state == 15
|| conn->state == 17 || conn->state == 19)
goto do_continue;
phone_sendback (conn, MT_ET_REL_COM, NULL);
isdn3_setup_conn (conn, EST_DISCONNECT);
goto ComEx;
case MT_ET_DISC:
if (conn->state == 0 || conn->state == 1 || conn->state == 6
|| conn->state == 11 || conn->state == 12
|| conn->state == 15
|| conn->state == 17 || conn->state == 19)
goto do_continue;
isdn3_setup_conn (conn, EST_DISCONNECT);
setstate (conn, 12);
report_terminate (conn, data, len,0);
(void)send_disc (conn, 1, NULL);
break;
default:
do_continue:
switch (conn->state) {
case 0:
switch (msgtype) {
case MT_ET_SETUP:
conn->minorstate |= MS_INCOMING;
setstate (conn, 6);
(void) get_chanID (conn, data, len);
if (isdn3_setup_conn (conn, EST_LISTEN) != 0) {
setstate(conn,99);
goto pack_err;
}
get_nr (conn, data, len, info->nr, PT_E0_origAddr);
get_nr (conn, data, len, info->lnr, PT_E0_destAddr);
get_hex(conn,data,len,info->bearer,&info->bearer_len,sizeof(info->bearer),PT_E0_bearer_cap);
get_hex(conn,data,len,info->llc,&info->llc_len,sizeof(info->llc),PT_E0_compatLo);
get_hex(conn,data,len,info->ulc,&info->ulc_len,sizeof(info->ulc),PT_E0_compatHi);
{
long flags = isdn3_flags(conn->card->info,-1,-1);
if(flags & FL_ANS_IMMED) {
untimer(ET_TALERT,conn);
ET_TALERT(conn);
}
}
report_setup (conn, data, len);
break;
case MT_ET_REL:
phone_sendback (conn, MT_ET_REL_COM, NULL);
report_terminate (conn, data, len,0);
break;
case MT_ET_REL_COM:
break;
default:
/* Send CAUSE */
phone_sendback (conn, MT_ET_REL, NULL);
setstate (conn, 19);
break;
}
break;
case 1:
switch (msgtype) {
case MT_ET_SETUP_ACK:
(void) get_chanID (conn, data, len);
if (isdn3_setup_conn (conn, EST_LISTEN) != 0)
goto pack_err;
report_generic (conn, data, len, ID_ET_SETUP_ACK);
setstate (conn, 2);
break;
case MT_ET_PROCEEDING:
(void) get_chanID (conn, data, len);
if (isdn3_setup_conn (conn, EST_LISTEN) != 0)
goto pack_err;
report_generic (conn, data, len, MT_ET_PROCEEDING);
setstate (conn, 3);
break;
case MT_ET_ALERT:
(void) get_chanID (conn, data, len);
if (isdn3_setup_conn (conn, EST_LISTEN) != 0)
goto pack_err;
report_generic (conn, data, len, ID_ET_ALERT);
setstate (conn, 4);
break;
case MT_ET_CONN:
(void) get_chanID (conn, data, len);
if (!(conn->minorstate & MS_BCHAN))
goto pack_err;
if (isdn3_setup_conn (conn, EST_CONNECT) != 0)
goto pack_err;
report_conn (conn, data, len);
phone_sendback (conn, MT_ET_CONN_ACK, NULL);
setstate (conn, 10);
break;
case MT_ET_REL:
/* send REL up -- done when dropping out below */
phone_sendback (conn, MT_ET_REL_COM, NULL);
setstate (conn, 0);
report_terminate (conn, data, len,0);
break;
case MT_ET_DISC:
isdn3_setup_conn (conn, EST_DISCONNECT);
setstate (conn, 12);
report_terminate (conn, data, len,0);
(void)send_disc (conn, 1, NULL);
break;
case MT_ET_REL_COM:
isdn3_setup_conn (conn, EST_DISCONNECT);
setstate (conn, 0);
report_terminate (conn, data, len,0);
break;
default:
goto msg_err;
}
break;
case 2:
switch (msgtype) {
case MT_ET_PROGRESS:
report_generic (conn, data, len, ID_ET_PROGRESS);
break;
case MT_ET_PROCEEDING:
(void) get_chanID (conn, data, len);
if (isdn3_setup_conn (conn, EST_LISTEN) != 0)
goto pack_err;
report_generic (conn, data, len, ID_ET_PROCEEDING);
setstate (conn, 3);
break;
case MT_ET_ALERT:
(void) get_chanID (conn, data, len);
if (isdn3_setup_conn (conn, EST_LISTEN) != 0)
goto pack_err;
report_generic (conn, data, len, ID_ET_ALERT);
setstate (conn, 4);
break;
case MT_ET_CONN:
(void) get_chanID (conn, data, len);
if (!(conn->minorstate & MS_BCHAN))
goto pack_err;
if (isdn3_setup_conn (conn, EST_CONNECT) != 0)
goto pack_err;
report_conn (conn, data, len);
phone_sendback (conn, MT_ET_CONN_ACK, NULL);
setstate (conn, 10);
break;
default:
goto msg_err;
}
break;
case 3:
switch (msgtype) {
case MT_ET_ALERT:
report_generic (conn, data, len, ID_ET_PROGRESS);
setstate (conn, 4);
break;
case MT_ET_CONN:
(void) get_chanID (conn, data, len);
if (!(conn->minorstate & MS_BCHAN))
goto pack_err;
if (isdn3_setup_conn (conn, EST_CONNECT) != 0)
goto pack_err;
report_conn (conn, data, len);
phone_sendback (conn, MT_ET_CONN_ACK, NULL);
setstate (conn, 10);
break;
default:
goto msg_err;
}
break;
case 4:
switch (msgtype) {
case MT_ET_CONN:
(void) get_chanID (conn, data, len);
if (!(conn->minorstate & MS_BCHAN))
goto pack_err;
if (isdn3_setup_conn (conn, EST_CONNECT) != 0)
goto pack_err;
report_conn (conn, data, len);
phone_sendback (conn, MT_ET_CONN_ACK, NULL);
setstate (conn, 10);
break;
default:
goto msg_err;
}
break;
case 6:
switch (msgtype) {
case MT_ET_SETUP:
(void) get_chanID (conn, data, len);
break;
case MT_ET_REL:
phone_sendback (conn, MT_ET_REL_COM, NULL);
setstate (conn, 99);
report_terminate (conn, data, len,0);
break;
case MT_ET_DISC:
isdn3_setup_conn (conn, EST_DISCONNECT);
setstate (conn, 12);
report_terminate (conn, data, len,0);
(void)send_disc (conn, 1, NULL);
break;
default:
goto msg_err;
}
break;
case 7:
switch (msgtype) {
case MT_ET_SETUP:
(void) get_chanID (conn, data, len);
if (isdn3_setup_conn (conn, EST_LISTEN) != 0)
goto pack_err;
break;
case MT_ET_REL:
/* send REL up */
phone_sendback (conn, MT_ET_REL_COM, NULL);
setstate (conn, 99);
break;
case MT_ET_DISC:
isdn3_setup_conn (conn, EST_DISCONNECT);
setstate (conn, 12);
report_terminate (conn, data, len,0);
(void)send_disc (conn, 1, NULL);
break;
default:
goto msg_err;
}
break;
case 8:
switch (msgtype) {
case MT_ET_CONN_ACK:
(void) get_chanID (conn, data, len);
if (!(conn->minorstate & MS_BCHAN))
goto pack_err;
if (isdn3_setup_conn (conn, EST_CONNECT) != 0)
goto pack_err;
report_conn_ack (conn, data, len);
setstate (conn, 10);
break;
case MT_ET_SETUP:
break;
case MT_ET_REL:
/* send REL up */
phone_sendback (conn, MT_ET_REL_COM, NULL);
setstate (conn, 0);
break;
case MT_ET_DISC:
isdn3_setup_conn (conn, EST_DISCONNECT);
setstate (conn, 12);
report_terminate (conn, data, len,0);
(void)send_disc (conn, 1, NULL);
break;
default:
goto msg_err;
}
break;
case 9:
switch (msgtype) {
case MT_ET_CONN_ACK:
(void) get_chanID (conn, data, len);
if (!(conn->minorstate & MS_BCHAN))
goto pack_err;
if (isdn3_setup_conn (conn, EST_CONNECT) != 0)
goto pack_err;
report_conn_ack (conn, data, len);
setstate (conn, 10);
break;
case MT_ET_SETUP:
break;
case MT_ET_REL:
/* send REL up */
phone_sendback (conn, MT_ET_REL_COM, NULL);
setstate (conn, 0);
report_terminate (conn, data, len,0);
break;
case MT_ET_DISC:
isdn3_setup_conn (conn, EST_DISCONNECT);
setstate (conn, 12);
report_terminate (conn, data, len,0);
(void)send_disc (conn, 1, NULL);
break;
default:
goto msg_err;
}
break;
case 10:
switch (msgtype) {
case MT_ET_SETUP:
break;
case MT_ET_NOTIFY:
report_notify (conn, data, len);
break;
case MT_ET_USER_INFO:
report_user_info (conn, data, len);
break;
case MT_ET_REL:
phone_sendback (conn, MT_ET_REL_COM, NULL);
setstate (conn, 0);
report_terminate (conn, data, len,0);
break;
default:
goto msg_err;
}
break;
case 14:
switch (msgtype) {
case MT_ET_SETUP:
break;
case MT_ET_CONN_ACK:
(void) get_chanID (conn, data, len);
if (!(conn->minorstate & MS_BCHAN))
goto pack_err;
if (isdn3_setup_conn (conn, EST_CONNECT) != 0)
goto pack_err;
report_conn_ack (conn, data, len);
setstate (conn, 10);
break;
case MT_ET_REL:
phone_sendback (conn, MT_ET_REL_COM, NULL);
setstate (conn, 0);
report_terminate (conn, data, len,0);
break;
case MT_ET_DISC:
isdn3_setup_conn (conn, EST_DISCONNECT);
setstate (conn, 12);
report_terminate (conn, data, len,0);
(void)send_disc (conn, 1, NULL);
break;
default:
goto msg_err;
}
break;
#ifdef HAS_SUSPEND
case 15:
switch (msgtype) {
case MT_ET_SETUP:
break;
case MT_ET_SUSP_ACK:
/* send SUSP_ACK up */
setstate (conn, 99);
break;
case MT_ET_SUSP_REJ:
/* send SUSP_REJ up */
setstate (conn, 10);
break;
case MT_ET_REL:
phone_sendback (conn, MT_ET_REL_COM, NULL);
setstate (conn, 0);
report_terminate (conn, data, len,0);
break;
case MT_ET_DISC:
isdn3_setup_conn (conn, EST_DISCONNECT);
setstate (conn, 12);
report_terminate (conn, data, len,0);
(void)send_disc (conn, 1, NULL);
break;
case MT_ET_RES_REJ:
case MT_ET_REG_ACK:
case MT_ET_REG_REJ:
case MT_ET_CANC_ACK:
case MT_ET_CANC_REJ:
/* Fehlermeldung */
break;
default:
goto msg_err;
}
break;
case 17:
switch (msgtype) {
case MT_ET_RES_ACK:
/* send RES_ACK up */
(void) get_chanID (conn, data, len);
if (isdn3_setup_conn (conn, EST_CONNECT) != 0)
goto pack_err;
setstate (conn, 10);
break;
case MT_ET_RES_REJ:
/* send RES_REJ up */
setstate (conn, 99);
break;
case MT_ET_REL:
phone_sendback (conn, MT_ET_REL_COM, NULL);
setstate (conn, 0);
report_terminate (conn, data, len,0);
break;
case MT_ET_DISC:
isdn3_setup_conn (conn, EST_DISCONNECT);
common_17_REL:
phone_sendback (conn, MT_ET_REL, NULL);
setstate (conn, 19);
report_terminate (conn, data, len,0);
break;
case MT_ET_SUSP_ACK:
case MT_ET_REG_ACK:
case MT_ET_REG_REJ:
case MT_ET_CANC_ACK:
case MT_ET_CANC_REJ:
/* Fehlermeldung */
setstate (conn, 4);
break;
default:
goto msg_err;
}
break;
#endif
case 11:
switch (msgtype) {
case MT_ET_SETUP:
break;
case MT_ET_DISC:
/* Release B chan */
phone_sendback (conn, MT_ET_REL, NULL);
setstate (conn, 19);
report_terminate (conn, data, len,0);
break;
case MT_ET_REL:
phone_sendback (conn, MT_ET_REL_COM, NULL);
setstate (conn, 0);
report_terminate (conn, data, len,0);
break;
default:
goto msg_err;
}
break;
case 12:
switch (msgtype) {
case MT_ET_SETUP:
break;
case MT_ET_REL:
phone_sendback (conn, MT_ET_REL_COM, NULL);
setstate (conn, 0);
report_terminate (conn, data, len,0);
break;
default:
goto msg_err;
}
break;
case 19:
switch (msgtype) {
case MT_ET_SETUP:
break;
case MT_ET_REL:
case MT_ET_REL_COM:
setstate (conn, 0);
report_terminate (conn, data, len,0);
break;
default:
goto msg_err;
}
break;
case 25:
switch (msgtype) {
}
break;
case 99:
break;
default:
setstate (conn, 99);
break;
}
}
out:
checkterm (conn, data, len);
return 0;
pack_err:
release_postET (conn, 0, 1);
goto out;
msg_err: /* XXX TODO */
return 0;
}
static int
chstate (isdn3_conn conn, uchar_t ind, short add)
{
switch (ind) {
case DL_ESTABLISH_IND:
case DL_ESTABLISH_CONF:
if (conn->talk->state & PHONE_UP) { /* Reestablishment */
switch (conn->state) {
case 1:
case 3:
case 4:
case 8:
case 10:
#ifdef HAS_SUSPEND
case 15:
case 17:
#endif
case 12:
case 20:
case 21:
/* Error */
/* FALL THRU */
case 11:
case 19:
break;
case 2:
{
mblk_t *data = allocb(16,BPRI_MED);
if(data != NULL) {
unsigned char * qd_d;
int qd_len = 0;
if ((qd_d = qd_insert ((uchar_t *) data->b_rptr, &qd_len, 0, PT_E0_cause, 2, 0)) == NULL) {
freemsg(data);
data = NULL;
} else {
*qd_d++ = 0x80;
*qd_d++ = 0x80 | ET_TempFailure;
data->b_wptr += qd_len;
}
}
if(phone_sendback (conn, MT_ET_DISC, data) != 0)
freemsg(data);
}
/* Error */
setstate (conn, 11);
break;
case 7:
break;
case 14:
{
mblk_t *data = allocb(16,BPRI_MED);
if(data != NULL) {
unsigned char * qd_d;
int qd_len = 0;
if ((qd_d = qd_insert ((uchar_t *) data->b_rptr, &qd_len, 0, PT_E0_cause, 2, 0)) == NULL) {
freemsg(data);
data = NULL;
} else {
*qd_d++ = 0x80;
*qd_d++ = 0x80 | ET_TempFailure;
data->b_wptr += qd_len;
}
}
if(phone_sendback (conn, MT_ET_DISC, data) != 0)
freemsg(data);
timer (ET_T305, conn);
}
break;
}
checkterm (conn, NULL, 0);
} else
phone_timerup (conn);
break;
case MDL_ERROR_IND:
if(!(add & ERR_G))
break;
/* FALL THRU */
case DL_RELEASE_IND:
case DL_RELEASE_CONF:
case PH_DEACTIVATE_CONF:
case PH_DEACTIVATE_IND:
case PH_DISCONNECT_IND:
setstate (conn, 99);
checkterm (conn, NULL, 0);
break;
}
return 0;
}
static int
send_disc (isdn3_conn conn, char release, mblk_t * data)
{
int err = 0;
mblk_t *owndata = NULL;
switch (conn->state) {
case 0:
break;
#ifdef HAS_SUSPEND
case 15:
case 17:
return -EBUSY;
#endif
case 12:
if (((err = phone_sendback (conn, MT_ET_REL, data)) != 0) && (data != NULL))
freemsg(data);
setstate (conn, 19);
break;
case 11:
if (release) {
goto common_off_noconn; /* Is this confirming? */
} else
return -EBUSY;
case 1:
case 2:
case 3:
#ifdef HAS_SUSPEND
case 15:
#endif
case 4:
default:
common_off:
if(data == NULL) {
owndata = data = allocb(16,BPRI_MED);
if(data != NULL) {
unsigned char * qd_d;
int qd_len = 0;
if ((qd_d = qd_insert ((uchar_t *) data->b_rptr, &qd_len, 0, PT_E0_cause, 2, 0)) == NULL) {
data = NULL;
freemsg(owndata);
} else {
*qd_d++ = 0x80;
*qd_d++ = 0x80 | ET_NormalClear;
data->b_wptr += qd_len;
}
}
}
if ((err = phone_sendback (conn, MT_ET_DISC, data)) == 0)
data = NULL;
setstate (conn, 11);
break;
case 6:
common_off_noconn:
if(release > 1)
goto common_off; /* XXX experimental */
if ((err = phone_sendback (conn, MT_ET_REL_COM, data)) != 0 && data != NULL)
freemsg(data);
setstate (conn, 99);
break;
case 19:
case 99:
break;
}
if(data != NULL && data == owndata && err != 0)
freemsg(owndata);
return err;
}
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 e_info *info;
long cause = -1;
char forceit = 0;
if(conn->p_data == NULL) {
if((conn->p_data = malloc(sizeof(struct e_info))) == NULL) {
return -ENOMEM;
}
bzero(conn->p_data,sizeof(struct e_info));
}
conn->lockit++;
info = conn->p_data;
if (data != NULL) {
oldpos = data->b_rptr;
while ((err = m_getsx (data, &typ)) == 0) {
switch (typ) {
case ARG_FASTDROP:
if (conn->state == 6 || conn->state == 7 ||
conn->state == 8)
forceit = 1;
break;
case ARG_FORCE:
forceit = 1;
break;
case ARG_CAUSE:
{
ushort_t causeid;
if (m_getid (data, &causeid) != 0)
break;
cause = et_idtocause(causeid);
}
break;
case ARG_LLC:
{
int len = m_gethexlen(data);
if (len > 0) {
if (len > sizeof(info->llc))
len = sizeof(info->llc);
info->llc_len = len;
m_gethex(data,info->llc,len);
}
}
break;
case ARG_ULC:
{
int len = m_gethexlen(data);
if (len > 0) {
if (len > sizeof(info->ulc))
len = sizeof(info->ulc);
info->ulc_len = len;
m_gethex(data,info->ulc,len);
}
}
break;
case ARG_BEARER:
{
int len = m_gethexlen(data);
if (len > 0) {
if (len > sizeof(info->bearer))
len = sizeof(info->bearer);
info->bearer_len = len;
m_gethex(data,info->bearer,len);
}
}
break;
case ARG_SUPPRESS:
suppress = 1;
break;
case ARG_LNUMBER:
m_getskip (data);
if ((err = m_getstr (data, (char *) info->lnr, MAXNR)) != 0) {
printf("GetStr LNumber: ");
conn->lockit--;
return err;
}
break;
case ARG_NUMBER:
m_getskip (data);
if ((err = m_getstr (data, (char *) info->nr, MAXNR)) != 0) {
printf("GetStr Number: ");
conn->lockit--;
return err;
}
break;
default:;
}
}
data->b_rptr = oldpos;
}
err = 0;
switch (id) {
case CMD_DIAL:
{
if (data == NULL) {
printf("DataNull: ");
conn->lockit--;
return -EINVAL;
}
conn->minorstate |= MS_OUTGOING | MS_WANTCONN;
conn->lockit++;
isdn3_setup_conn (conn, EST_SETUP);
conn->lockit--;
switch (conn->state) {
case 0:
if (conn->minor == 0) {
printf("ConnMinorZero: ");
conn->lockit--;
return -ENOENT;
}
{
mblk_t *asn = allocb (256, BPRI_MED);
int qd_len = 0;
uchar_t *qd_d;
if (asn == NULL) {
conn->lockit--;
return -ENOMEM;
}
if (info->bearer_len > 0) {
if ((qd_d = qd_insert ((uchar_t *) asn->b_rptr, &qd_len, 0, PT_E0_bearer_cap, info->bearer_len, 0)) == NULL) {
conn->lockit--;
return -EIO;
}
bcopy(info->bearer,qd_d,info->bearer_len);
}
if (info->llc_len > 0) {
if ((qd_d = qd_insert ((uchar_t *) asn->b_rptr, &qd_len, 0, PT_E0_compatLo, info->llc_len, 0)) == NULL) {
conn->lockit--;
return -EIO;
}
bcopy(info->llc,qd_d,info->llc_len);
}
if (info->ulc_len > 0) {
if ((qd_d = qd_insert ((uchar_t *) asn->b_rptr, &qd_len, 0, PT_E0_compatLo, info->ulc_len, 0)) == NULL) {
conn->lockit--;
return -EIO;
}
bcopy(info->ulc,qd_d,info->ulc_len);
}
if (info->nr[0] != '\0') {
int i = 0, j;
uchar_t nrtype;
switch(info->nr[0]) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
nrtype = 0x00; break;
case '+': /* international */
nrtype = 0x11; i = 1; break;
case '=': /* national */
nrtype = 0x21; i = 1; break;
case '-': /* subscriber */
nrtype = 0x41; i = 1; break;
case '.': /* abbreviated */
case '/': /* abbreviated */
nrtype = 0x61; i = 1; break;
default:
nrtype = 0x00; i = 1;
break;
}
for (j = i; j < MAXNR; j++)
if (info->nr[j] == '\0')
break;
if ((qd_d = qd_insert ((uchar_t *) asn->b_rptr, &qd_len, 0, PT_E0_destAddr, j - i + 1, 0)) == NULL) {
conn->lockit--;
return -EIO;
}
*qd_d++ = nrtype | 0x80;
qd_d -= i; /* compensate for i-offset of the number */
while (i <= --j)
qd_d[j] = info->nr[j];
}
if (info->lnr[0] != '\0') {
int i = 0, j;
uchar_t nrtype;
switch(info->lnr[0]) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
nrtype = 0x00; break;
case '+': /* international */
nrtype = 0x11; i = 1; break;
case '=': /* national */
nrtype = 0x21; i = 1; break;
case '-': /* subscriber */
nrtype = 0x41; i = 1; break;
#if 0 /* use "unknown" instead... MSN spec */
case '.': /* abbreviated, able-to-dial */
case '/': /* abbreviated, unable-to-dial */
nrtype = 0x61; i = 1; break;
#endif
default:
nrtype = 0x00; i = 1;
break;
}
for (j = i; j < MAXNR; j++)
if (info->lnr[j] == '\0')
break;
if ((qd_d = qd_insert ((uchar_t *) asn->b_rptr, &qd_len, 0, PT_E0_origAddr, j - i + 1, 0)) == NULL) {
conn->lockit--;
return -EIO;
}
*qd_d++ = nrtype | 0x80;
qd_d -= i; /* compensate for i-offset of the number */
while (i <= --j)
qd_d[j] = info->lnr[j];
}
if (conn->bchan != 0) {
int basic = (conn->card ? conn->card->bchans <= 2 : 1);
if ((qd_d = qd_insert ((uchar_t *) asn->b_rptr, &qd_len, 0, PT_E0_chanID, basic ? 1 : 3, 0)) == NULL) {
conn->lockit--;
return -EIO;
}
if (basic) {
*qd_d = 0x80 | conn->bchan;
} else {
*qd_d++ = 0xA1;
*qd_d++ = 0x83;
*qd_d++ = conn->bchan;
}
conn->bchan = 0; /* Network will tell us which
* to use */
}
/* NSF: SemiPerm etc. */
asn->b_wptr = asn->b_rptr + qd_len;
if ((err = phone_sendback (conn, MT_ET_SETUP, asn)) != 0) {
freeb (asn);
printf("SendBack: ");
conn->lockit--;
return err;
}
setstate (conn, 1);
}
break;
default:
printf("Default %d: ", conn->state);
conn->lockit--;
return -EBUSY;
}
}
break;
case CMD_PREPANSWER:
switch (conn->state) {
case 6:
{
mblk_t *asn = NULL;
setstate (conn, 7);
if(phone_sendback (conn, MT_ET_ALERT, asn) != 0 && asn != NULL)
freemsg(asn);
}
break;
default:
printf("BadState1: ");
err = -EINVAL;
}
break;
case CMD_ANSWER:
{
mblk_t *asn = NULL;
if (data != NULL) {
int qd_len = 0;
uchar_t *qd_d;
if ((asn = allocb (256, BPRI_MED)) == NULL) {
conn->lockit--;
return -ENOMEM;
}
if (info->llc_len > 0) {
if ((qd_d = qd_insert ((uchar_t *) asn->b_rptr, &qd_len, 6, PT_E0_compatLo, info->llc_len, 0)) == NULL) {
conn->lockit--;
return -EIO;
}
bcopy(info->llc,qd_d,info->llc_len);
}
#if 0
if (info->ulc_len > 0) {
if ((qd_d = qd_insert ((uchar_t *) asn->b_rptr, &qd_len, 6, PT_E0_compatLo, info->ulc_len, 0)) == NULL) {
conn->lockit--;
return -EIO;
}
bcopy(info->ulc,qd_d,info->ulc_len);
}
if (info->bearer_len > 0) {
if ((qd_d = qd_insert ((uchar_t *) asn->b_rptr, &qd_len, 6, PT_E0_bearer_cap, info->bearer_len, 0)) == NULL) {
conn->lockit--;
return -EIO;
}
bcopy(info->bearer,qd_d,info->bearer_len);
}
#endif
#if 0 /* error? Seems to be bad */
if (conn->bchan != 0) {
int basic = (conn->card ? conn->card->bchans <= 2 : 1);
if ((qd_d = qd_insert ((uchar_t *) asn->b_rptr, &qd_len, 0, PT_E0_chanID, basic ? 1 : 3, 0)) == NULL) {
conn->lockit--;
return -EIO;
}
if (basic) {
*qd_d = 0x80 | conn->bchan;
} else {
*qd_d++ = 0xA1;
*qd_d++ = 0x83;
*qd_d++ = conn->bchan;
}
}
#endif
asn->b_wptr = asn->b_rptr + qd_len;
}
switch (conn->state) {
case 6:
case 7:
break;
default:
printf("BadState2 ");
err = -EINVAL;
break;
}
conn->minorstate |= MS_WANTCONN;
isdn3_setup_conn (conn, EST_SETUP);
if (((conn->delay > 0) && (conn->minorstate & MS_DELAYING))
|| !(conn->minorstate & MS_INITPROTO)
|| ((conn->state == 7) && !(conn->minorstate & MS_BCHAN))) {
if (data != NULL)
data->b_rptr = oldpos;
isdn3_repeat (conn, id, data);
if (asn != NULL)
freemsg (asn);
conn->lockit--;
return 0;
}
if(!(conn->minorstate & MS_BCHAN)) {
setstate (conn, 7);
err = phone_sendback (conn, MT_ET_ALERT, asn);
} else {
setstate (conn, 8);
err = phone_sendback (conn, MT_ET_CONN, asn);
}
if (err == 0)
asn = NULL;
if(conn->state == 7) {
isdn3_repeat (conn, id, data);
data = NULL;
} else
isdn3_setup_conn (conn, EST_LISTEN);
if (asn != NULL)
freemsg (asn);
}
break;
case CMD_OFF:
{
mblk_t *mb = NULL;
if(cause != -1) {
int len;
uchar_t *dp;
if ((mb = allocb (16, BPRI_LO)) == NULL)
break;
len = mb->b_wptr - mb->b_rptr;
dp = qd_insert ((uchar_t *) mb->b_rptr, &len, 0, PT_E0_cause, 2, 0);
if (dp != NULL) {
mb->b_wptr = mb->b_rptr + len;
*dp++ = 0x80;
*dp = cause | 0x80;
}
}
conn->minorstate &= ~MS_WANTCONN;
/* set Data */
if (conn->state == 6 && cause == -1) {
setstate(conn,99);
if(mb != NULL)
freemsg(mb);
} else if (send_disc (conn, forceit << 1, mb) != 0 && mb != NULL)
freemsg (mb);
isdn3_setup_conn (conn, EST_DISCONNECT);
err = 0;
}
break;
default:
printf("UnknownCmd ");
err = -EINVAL;
break;
}
if (data != NULL && err == 0)
freemsg (data);
checkterm (conn, NULL, 0);
conn->lockit--;
return err;
}
static void
killconn (isdn3_conn conn, char force)
{
if (force) {
untimer (ET_T301, conn);
untimer (ET_T302, conn);
untimer (ET_T303, conn);
untimer (ET_T304, conn);
untimer (ET_T305, conn);
untimer (ET_T308, conn);
#ifdef HAS_RECOVERY
untimer (ET_T309, conn);
#endif
untimer (ET_T310, conn);
untimer (ET_T313, conn);
untimer (ET_T314, conn);
untimer (ET_T316, conn);
untimer (ET_T317, conn);
#ifdef HAS_SUSPEND
untimer (ET_T318, conn);
untimer (ET_T319, conn);
#endif
untimer (ET_T321, conn);
untimer (ET_T322, conn);
untimer (ET_TALERT, conn);
untimer (ET_TCONN, conn);
untimer (ET_TFOO, conn);
}
if(conn->state != 0) {
if(conn->state == 99)
setstate(conn,0);
else if(force)
(void) phone_sendback (conn, MT_ET_REL, NULL);
else
(void) send_disc (conn, 1, NULL);
}
}
static void report (isdn3_conn conn, mblk_t *mb)
{
struct e_info *info = conn->p_data;
if (info == NULL)
return;
if(info->bearer_len > 0) {
m_putsx(mb, ARG_BEARER);
m_puthex(mb, info->bearer,info->bearer_len);
}
if(info->ulc_len > 0) {
m_putsx(mb, ARG_ULC);
m_puthex(mb, info->ulc,info->ulc_len);
}
if(info->llc_len > 0) {
m_putsx(mb, ARG_LLC);
m_puthex(mb, info->llc,info->llc_len);
}
if (info->nr[0] != 0) {
m_putsx (mb, ARG_NUMBER);
m_putsz (mb, info->nr);
}
if (info->lnr[0] != 0) {
m_putsx (mb, ARG_LNUMBER);
m_putsz (mb, info->lnr);
}
}
struct _isdn3_prot prot_ETSI =
{
NULL, SAPI_PHONE_DSS1,
NULL, &chstate, &report, &recv, NULL, &sendcmd, &killconn, NULL,
};