From 0c3b232b6a0314006abc59f0f628cefc1da19340 Mon Sep 17 00:00:00 2001 From: kkeil Date: Wed, 27 Aug 2003 07:33:02 +0000 Subject: [PATCH] Initial revision --- .cvsignore | 3 + Makefile | 63 + example/Makefile | 32 + example/loadfirm.c | 288 +++++ example/logger.c | 477 ++++++++ example/test_file | Bin 0 -> 870 bytes example/test_file.in | 0 example/test_file.out | 1 + example/test_x75.in | 11 + example/test_x75.out | 4 + example/testcon.c | 868 +++++++++++++ example/testcon.in | 0 example/testcon_l2.c | 869 +++++++++++++ example/testnet.c | 963 +++++++++++++++ i4lnet/Makefile | 39 + i4lnet/bchannel.c | 1379 +++++++++++++++++++++ i4lnet/fsm.c | 163 +++ i4lnet/fsm.h | 53 + i4lnet/g711.c | 928 ++++++++++++++ i4lnet/isdn_debug.c | 151 +++ i4lnet/isdn_msg.c | 113 ++ i4lnet/manager.c | 286 +++++ i4lnet/net_if.c | 708 +++++++++++ i4lnet/net_l2.c | 2072 +++++++++++++++++++++++++++++++ i4lnet/net_l2.h | 118 ++ i4lnet/net_l3.c | 2213 ++++++++++++++++++++++++++++++++++ i4lnet/net_l3.h | 257 ++++ i4lnet/nettst.c | 320 +++++ i4lnet/sndloop.c | 145 +++ i4lnet/sndloop2.c | 145 +++ i4lnet/tei.c | 441 +++++++ i4lnet/tone.c | 147 +++ include/bchannel.h | 37 + include/g711.h | 68 ++ include/helper.h | 58 + include/ibuffer.h | 119 ++ include/isdn_debug.h | 36 + include/isdn_msg.h | 187 +++ include/isdn_net.h | 296 +++++ include/isound.h | 24 + include/l3dss1.h | 173 +++ include/mISDNlib.h | 402 ++++++ include/tone.h | 29 + lib/Makefile | 19 + lib/device.c | 684 +++++++++++ lib/layer.c | 138 +++ lib/stack.c | 141 +++ lib/status.c | 79 ++ tenovis/Makefile | 34 + tenovis/lib/Makefile | 20 + tenovis/lib/tenovis.h | 68 ++ tenovis/lib/tenovis_device.c | 171 +++ tenovis/lib/tenovis_int.h | 47 + tenovis/lib/tenovis_intern.c | 293 +++++ tenovis/testlib.c | 277 +++++ tenovis/tstlib.c | 156 +++ voip/Makefile | 83 ++ voip/Makefile.org | 83 ++ voip/cfg.lex | 115 ++ voip/example/pingi2.voip.cfg | 36 + voip/example/rec_ctrl.sample | 7 + voip/globals.h | 19 + voip/iapplication.h | 137 +++ voip/options.h | 13 + voip/prep.conf | 9 + voip/read_cfg.c | 143 +++ voip/rtp.h | 133 ++ voip/rtpacket.c | 858 +++++++++++++ voip/rtpacket.h | 32 + voip/testlog | 3 + voip/tstcfg | 13 + voip/tstparse.c | 29 + voip/vitimer.h | 32 + voip/voip_appl.c | 1539 +++++++++++++++++++++++ voip/voip_isdn.c | 832 +++++++++++++ voip/voip_isdn_app.c | 509 ++++++++ voip/voip_timer.c | 138 +++ voip/voipisdn.spec | 53 + 78 files changed, 21629 insertions(+) create mode 100644 .cvsignore create mode 100644 Makefile create mode 100644 example/Makefile create mode 100644 example/loadfirm.c create mode 100644 example/logger.c create mode 100755 example/test_file create mode 100755 example/test_file.in create mode 100755 example/test_file.out create mode 100755 example/test_x75.in create mode 100644 example/test_x75.out create mode 100644 example/testcon.c create mode 100755 example/testcon.in create mode 100644 example/testcon_l2.c create mode 100644 example/testnet.c create mode 100644 i4lnet/Makefile create mode 100644 i4lnet/bchannel.c create mode 100644 i4lnet/fsm.c create mode 100644 i4lnet/fsm.h create mode 100644 i4lnet/g711.c create mode 100644 i4lnet/isdn_debug.c create mode 100644 i4lnet/isdn_msg.c create mode 100644 i4lnet/manager.c create mode 100644 i4lnet/net_if.c create mode 100644 i4lnet/net_l2.c create mode 100644 i4lnet/net_l2.h create mode 100644 i4lnet/net_l3.c create mode 100644 i4lnet/net_l3.h create mode 100644 i4lnet/nettst.c create mode 100644 i4lnet/sndloop.c create mode 100644 i4lnet/sndloop2.c create mode 100644 i4lnet/tei.c create mode 100644 i4lnet/tone.c create mode 100644 include/bchannel.h create mode 100644 include/g711.h create mode 100644 include/helper.h create mode 100644 include/ibuffer.h create mode 100644 include/isdn_debug.h create mode 100644 include/isdn_msg.h create mode 100644 include/isdn_net.h create mode 100644 include/isound.h create mode 100644 include/l3dss1.h create mode 100644 include/mISDNlib.h create mode 100644 include/tone.h create mode 100644 lib/Makefile create mode 100644 lib/device.c create mode 100644 lib/layer.c create mode 100644 lib/stack.c create mode 100644 lib/status.c create mode 100644 tenovis/Makefile create mode 100644 tenovis/lib/Makefile create mode 100644 tenovis/lib/tenovis.h create mode 100644 tenovis/lib/tenovis_device.c create mode 100644 tenovis/lib/tenovis_int.h create mode 100644 tenovis/lib/tenovis_intern.c create mode 100644 tenovis/testlib.c create mode 100644 tenovis/tstlib.c create mode 100644 voip/Makefile create mode 100644 voip/Makefile.org create mode 100644 voip/cfg.lex create mode 100644 voip/example/pingi2.voip.cfg create mode 100644 voip/example/rec_ctrl.sample create mode 100644 voip/globals.h create mode 100644 voip/iapplication.h create mode 100644 voip/options.h create mode 100644 voip/prep.conf create mode 100644 voip/read_cfg.c create mode 100644 voip/rtp.h create mode 100644 voip/rtpacket.c create mode 100644 voip/rtpacket.h create mode 100644 voip/testlog create mode 100644 voip/tstcfg create mode 100644 voip/tstparse.c create mode 100644 voip/vitimer.h create mode 100644 voip/voip_appl.c create mode 100644 voip/voip_isdn.c create mode 100644 voip/voip_isdn_app.c create mode 100644 voip/voip_timer.c create mode 100644 voip/voipisdn.spec diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..c91cfcd --- /dev/null +++ b/.cvsignore @@ -0,0 +1,3 @@ +*.o +*.a +*~ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..46fc355 --- /dev/null +++ b/Makefile @@ -0,0 +1,63 @@ +mISDN_DIR := $(PWD) +export mISDN_DIR + +INCLUDEDIR := $(mISDN_DIR)/include +export INCLUDEDIR + +CFLAGS:= -g -Wall -O2 -I $(INCLUDEDIR) +export CFLAGS + +mISDNLIB := $(PWD)/lib/libmISDN.a +mISDNNETLIB := $(PWD)/i4lnet/libmisdnnet.a +export mISDNLIB +export mISDNNETLIB + +SUBDIRS := lib example + +SUBDIRS += $(shell if test -d i4lnet ; then echo i4lnet; fi) +SUBDIRS += $(shell if test -d tenovis ; then echo tenovis; fi) +SUBDIRS += $(shell if test -d voip ; then echo voip; fi) + +LIBS := lib/libmISDN.a + +all: + make TARGET=$@ subdirs + +subdirs: + set -e; for i in $(SUBDIRS) ; do $(MAKE) -C $$i $(TARGET); done + +clean: + make TARGET=$@ subdirs + rm -f *.o *~ DEADJOE $(INCLUDEDIR)/*~ $(INCLUDEDIR)/DEADJOE + +distclean: clean + make TARGET=$@ subdirs + rm -f *.o *~ testlog + +MAINDIR := $(shell basename $(PWD)) +ARCHIVDIR = /usr/src/packages/SOURCES +ARCHIVOPT := -v +# VERSION := $(shell date +"%Y%m%d") +VERSION := 20030423 + +ARCHIVNAME := $(ARCHIVDIR)/$(MAINDIR)-$(VERSION).tar.bz2 + +archiv: distclean + cd ../; tar c $(ARCHIVOPT) -f - $(MAINDIR) | bzip2 > $(ARCHIVNAME) + +basearchiv: ARCHIVOPT += --exclude i4lnet --exclude voip --exclude tenovis +basearchiv: ARCHIVNAME := $(ARCHIVDIR)/$(MAINDIR)_base-$(VERSION).tar.bz2 +basearchiv: archiv + +mainarchiv: ARCHIVOPT += --exclude voip --exclude tenovis +mainarchiv: ARCHIVNAME := $(ARCHIVDIR)/$(MAINDIR)_main-$(VERSION).tar.bz2 +mainarchiv: archiv + +tenovisarchiv: ARCHIVOPT += --exclude voip --exclude i4lnet +tenovisarchiv: ARCHIVNAME := $(ARCHIVDIR)/$(MAINDIR)_tenovis-$(VERSION).tar.bz2 +tenovisarchiv: archiv + +voiparchiv: ARCHIVOPT += --exclude tenovis +voiparchiv: ARCHIVNAME := $(ARCHIVDIR)/$(MAINDIR)_voip-$(VERSION).tar.bz2 +voiparchiv: archiv + diff --git a/example/Makefile b/example/Makefile new file mode 100644 index 0000000..c44b2d4 --- /dev/null +++ b/example/Makefile @@ -0,0 +1,32 @@ +LIBINCL := $(INCLUDEDIR)/mISDNlib.h +PROGS := testcon testnet testcon_l2 loadfirm logger + +all: $(PROGS) + +testcon: testcon.o $(mISDNLIB) + +testnet: testnet.o $(mISDNLIB) + +testcon_l2: testcon_l2.o $(mISDNLIB) + +loadfirm: loadfirm.o $(mISDNLIB) + +logger: logger.o $(mISDNLIB) + + +testcon.o : testcon.c ../include/l3dss1.h $(LIBINCL) + +testnet.o : testnet.c ../include/l3dss1.h $(LIBINCL) + +testcon_l2.o : testcon_l2.c ../include/l3dss1.h $(LIBINCL) + +loadfirm.o: loadfirm.c $(LIBINCL) + +logger.o: logger.c $(LIBINCL) + + +clean: + rm -f *.o *~ DEADJOE + +distclean: clean + rm -f *.a $(PROGS) diff --git a/example/loadfirm.c b/example/loadfirm.c new file mode 100644 index 0000000..6579543 --- /dev/null +++ b/example/loadfirm.c @@ -0,0 +1,288 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mISDNlib.h" + +void usage(pname) +char *pname; +{ + fprintf(stderr,"Call with %s [options] [firmware filename]\n",pname); + fprintf(stderr,"\n Valid options are:\n"); + fprintf(stderr,"\n"); + fprintf(stderr," -? Usage ; printout this information\n"); + fprintf(stderr," -c use card number n (default 1)\n"); + fprintf(stderr," -v Printing debug info level n\n"); + fprintf(stderr," 0 only recived messages are printed\n"); + fprintf(stderr," 1 send count\n"); + fprintf(stderr," 2 send status\n"); + fprintf(stderr," 3 send contens\n"); + fprintf(stderr," 4 device read count\n"); + fprintf(stderr," 5 stdin line parsing\n"); + fprintf(stderr," 6 stdin line raw contens\n"); + fprintf(stderr," 7 filenumber/select status\n"); + fprintf(stderr,"\n"); +} + +typedef struct _devinfo { + int device; + int mode; + int d_stid; + int d_adress; + int b_stid[2]; + int b_adress[2]; + int used_bchannel; +} devinfo_t; + +#define MAX_SIZE 0x10000 + +static int VerifyOn=0; +unsigned char *firmware; + +int download_firmware(devinfo_t *di, int size) { + unsigned char *p, buf[2048], rbuf[128]; + iframe_t *frm = (iframe_t *)buf; + iframe_t *rfrm = (iframe_t *)rbuf; + int cnt, ret = 0; + int *adr, l; + + frm->prim = PH_CONTROL | REQUEST; + frm->dinfo = 0; + frm->addr = di->b_adress[di->used_bchannel] | IF_DOWN; + frm->len = 8; + adr = (int *)&frm->data.i; + *adr++ = HW_FIRM_START; + *adr++ = size; + + ret = mISDN_write(di->device, buf, frm->len+mISDN_HEADER_LEN, 100000); + if (ret < 0) + fprintf(stdout,"send_data write error %d %s\n", errno, + strerror(errno)); + else if (VerifyOn>3) + fprintf(stdout,"send_data write ret=%d\n", ret); + + ret = mISDN_read(di->device, rbuf, 128, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"read ret=%d\n", ret); + cnt = 0; + p = firmware; + while (cnt=size) + l = size - cnt; + frm->prim = PH_CONTROL | REQUEST; + frm->dinfo = 0; + frm->addr = di->b_adress[di->used_bchannel] | IF_DOWN; + frm->len = l + 8; + adr = (int *)&frm->data.i; + *adr++ = HW_FIRM_DATA; + *adr++ = l; + memcpy(adr, firmware + cnt, l); + ret = mISDN_write(di->device, buf, frm->len+mISDN_HEADER_LEN, 100000); + if (ret < 0) + fprintf(stdout,"send_data write error %d %s\n", errno, + strerror(errno)); + else if (VerifyOn>3) + fprintf(stdout,"send_data write ret=%d\n", ret); + ret = mISDN_read(di->device, rbuf, 128, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"read ret=%d\n", ret); + cnt += l; + } + frm->prim = PH_CONTROL | REQUEST; + frm->dinfo = 0; + frm->addr = di->b_adress[di->used_bchannel] | IF_DOWN; + frm->len = 4; + adr = (int *)&frm->data.i; + *adr++ = HW_FIRM_END; + ret = mISDN_write(di->device, buf, frm->len+mISDN_HEADER_LEN, 100000); + if (ret < 0) + fprintf(stdout,"send_data write error %d %s\n", errno, + strerror(errno)); + else if (VerifyOn>3) + fprintf(stdout,"send_data write ret=%d\n", ret); + ret = mISDN_read(di->device, rbuf, 128, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"read ret=%d\n", ret); + + ret = mISDN_clear_stack(di->device, di->b_stid[di->used_bchannel]); + if (VerifyOn>3) + fprintf(stdout,"clear_stack ret=%d\n", ret); + return(0); +} + +int do_setup(devinfo_t *di, int cardnr) { + unsigned char buf[2048]; + iframe_t *frm = (iframe_t *)buf; + int ret = 0; + int i; + stack_info_t *stinf; + mISDN_pid_t pid; + layer_info_t li; + + ret = mISDN_get_stack_count(di->device); + if (VerifyOn>1) + fprintf(stdout,"%d stacks found(%d)\n", ret, cardnr); + if (ret < cardnr) { + fprintf(stdout,"cannot config card nr %d only %d cards\n", + cardnr, ret); + return(2); + } + ret = mISDN_get_stack_info(di->device, cardnr, buf, 1024); + if (ret<=0) { + fprintf(stdout,"cannot get stackinfo err: %d\n", ret); + return(3); + } + stinf = (stack_info_t *)&frm->data.p; + if (VerifyOn>1) + mISDNprint_stack_info(stdout, stinf); + di->d_stid = stinf->id; + for (i=0;i<2;i++) { + if (stinf->childcnt>i) + di->b_stid[i] = stinf->child[i]; + else + di->b_stid[i] = 0; + } + + di->used_bchannel = 0; + + memset(&li, 0, sizeof(layer_info_t)); + strcpy(&li.name[0], "B L3"); + li.object_id = -1; + li.extentions = 0; + li.pid.protocol[3] = ISDN_PID_L3_B_TRANS; + li.pid.layermask = ISDN_LAYER(3); + li.st = di->b_stid[di->used_bchannel]; + ret = mISDN_new_layer(di->device, &li); + if (ret<=0) { + fprintf(stdout, "new_layer ret(%d)\n", ret); + return(4); + } + di->b_adress[di->used_bchannel] = ret; + if (VerifyOn>2) + fprintf(stdout,"b_adress%d %08x\n", + di->used_bchannel+1, ret); + memset(&pid, 0, sizeof(mISDN_pid_t)); + pid.protocol[1] = ISDN_PID_L1_B_64TRANS; + pid.protocol[2] = ISDN_PID_L2_B_TRANS; + pid.protocol[3] = ISDN_PID_L3_B_TRANS; + pid.layermask = ISDN_LAYER(1) | ISDN_LAYER(2)| ISDN_LAYER(3); + ret = mISDN_set_stack(di->device, + di->b_stid[di->used_bchannel], &pid); + if (ret) { + fprintf(stdout, "set_stack ret(%d)\n", ret); + return(5); + } + return(0); +} + +int +read_firmware(unsigned char *fname) +{ + FILE *infile; + int cnt; + + if (!(infile = fopen(fname, "rb"))) { + fprintf(stderr, "cannot open file %s\n", fname); + exit(-1); + } + firmware = (unsigned char *) malloc(MAX_SIZE); + if (!firmware) { + fprintf(stderr, "cannot get %d byte memory\n", MAX_SIZE+4); + exit(-1); + } + cnt = fread(firmware, 1, MAX_SIZE, infile); + fclose(infile); + if (cnt==MAX_SIZE) { + fprintf(stderr, "wrong filesize\n"); + exit(-1); + } + return(cnt); +} + +int main(argc,argv) +int argc; +char *argv[]; + +{ + char FileName[200]; + int aidx=1,para=1; + char sw; + int len,err; + devinfo_t mISDN; + int cardnr =1; + + fprintf(stderr,"loadfirm 1.0\n"); + strcpy(FileName,"ISAR.BIN"); + if (argc<1) { + fprintf(stderr,"Error: Not enough arguments please check\n"); + usage(argv[0]); + exit(1); + } else { + do { + if (argv[aidx] && argv[aidx][0]=='-') { + sw=argv[aidx][1]; + switch (sw) { + case 'v': + case 'V': + VerifyOn=1; + if (argv[aidx][2]) { + VerifyOn=atol(&argv[aidx][2]); + } + break; + case 'c': + if (argv[aidx][2]) { + cardnr=atol(&argv[aidx][2]); + } + break; + case '?' : + usage(argv[0]); + exit(1); + break; + default : fprintf(stderr,"Unknown Switch %c\n",sw); + usage(argv[0]); + exit(1); + break; + } + } else { + if (para==1) { + if (argc > 1) + strcpy(FileName,argv[aidx]); + para++; + } else { + fprintf(stderr,"Undefined argument %s\n",argv[aidx]); + usage(argv[0]); + exit(1); + } + } + aidx++; + } while (aidx(mISDN.device = mISDN_open())) { + printf("TestmISDN cannot open mISDN due to %s\n", + strerror(errno)); + return(1); + } + + len = read_firmware(FileName); + if (VerifyOn) + fprintf(stdout,"read firmware from %s %d bytes\n", FileName, len); + err = do_setup(&mISDN, cardnr); + if (err) + fprintf(stdout,"do_setup error %d\n", err); + else + download_firmware(&mISDN, len); + free(firmware); + err=mISDN_close(mISDN.device); + if (err) + fprintf(stdout,"mISDN_close: error(%d): %s\n", err, + strerror(err)); + + return(0); +} diff --git a/example/logger.c b/example/logger.c new file mode 100644 index 0000000..608b28b --- /dev/null +++ b/example/logger.c @@ -0,0 +1,477 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mISDNlib.h" +#include "l3dss1.h" + +void usage(pname) +char *pname; +{ + fprintf(stderr,"Call with %s [options] [filename]\n",pname); + fprintf(stderr,"\n"); + fprintf(stderr,"\n Valid options are:\n"); + fprintf(stderr,"\n"); + fprintf(stderr," -? Usage ; printout this information\n"); + fprintf(stderr," -c use card number n (default 1)\n"); + fprintf(stderr," -F use function n (default 0)\n"); + fprintf(stderr," 0 normal logging\n"); + fprintf(stderr,"\n"); +} + +typedef struct _devinfo { + int device; + int cardnr; + int func; + char phonenr[32]; + int d_stid; + int layer1; + int layer2; + int layer3; + int b_stid[2]; + int b_adress[2]; + int used_bchannel; + int save; + int flag; + int val; + int cr; + int si; + int bl1_prot; + int bl2_prot; + int bl3_prot; +} devinfo_t; + +#define FLG_SEND_TONE 0x0001 +#define FLG_SEND_DATA 0x0002 +#define FLG_BCHANNEL_SETUP 0x0010 +#define FLG_BCHANNEL_DOACTIVE 0x0020 +#define FLG_BCHANNEL_ACTIVE 0x0040 +#define FLG_BCHANNEL_ACTDELAYED 0x0080 +#define FLG_CALL_ORGINATE 0x0100 +#define FLG_BCHANNEL_EARLY 0x0200 + + +#define MAX_REC_BUF 4000 +#define MAX_DATA_BUF 1024 + +static int VerifyOn=0; + +static devinfo_t *init_di = NULL; + +#define MsgHead(ptr, cref, mty) \ + *ptr++ = 0x8; \ + if (cref == -1) { \ + *ptr++ = 0x0; \ + } else { \ + *ptr++ = 0x1; \ + *ptr++ = cref^0x80; \ + } \ + *ptr++ = mty + +int printhexdata(FILE *f, int len, u_char *p) +{ + while(len--) { + fprintf(f, "%02x", *p++); + if (len) + fprintf(f, " "); + } + fprintf(f, "\n"); + return(0); +} + +int process_dchannel(devinfo_t *di, int len, iframe_t *frm) +{ + write(di->save, frm, len); + if (frm->prim == (PH_DATA | INDICATION) && (frm->len >0)) { + if (VerifyOn>5) + printhexdata(stdout, frm->len, &frm->data.i); + } + return(0); +} + +int setup_bchannel(devinfo_t *di) { + mISDN_pid_t pid; + int ret; + layer_info_t li; + + + if ((di->used_bchannel<0) || (di->used_bchannel>1)) { + fprintf(stdout, "wrong channel %d\n", di->used_bchannel); + return(0); + } + memset(&li, 0, sizeof(layer_info_t)); + strcpy(&li.name[0], "B L3"); + li.object_id = -1; + li.extentions = 0; + li.pid.protocol[3] = di->bl3_prot; + li.pid.layermask = ISDN_LAYER(3); + li.st = di->b_stid[di->used_bchannel]; + ret = mISDN_new_layer(di->device, &li); + if (ret<0) { + fprintf(stdout, "new_layer ret(%d)\n", ret); + return(0); + } + if (ret) { + di->b_adress[di->used_bchannel] = ret; + if (VerifyOn>2) + fprintf(stdout,"b_adress%d %08x\n", + di->used_bchannel+1, ret); + memset(&pid, 0, sizeof(mISDN_pid_t)); + pid.protocol[1] = di->bl1_prot; + pid.protocol[2] = di->bl2_prot; + pid.protocol[3] = di->bl3_prot; + pid.layermask = ISDN_LAYER(1) | ISDN_LAYER(2)| ISDN_LAYER(3); + if (di->flag & FLG_CALL_ORGINATE) + pid.global = 1; + ret = mISDN_set_stack(di->device, + di->b_stid[di->used_bchannel], &pid); + if (ret) { + fprintf(stdout, "set_stack ret(%d)\n", ret); + return(0); + } + ret = di->b_adress[di->used_bchannel]; + } + return(ret); +} + +int activate_bchan(devinfo_t *di) { + unsigned char buf[128]; + iframe_t *rfrm; + int ret; + + ret = mISDN_write_frame(di->device, buf, + di->b_adress[di->used_bchannel] | IF_DOWN, + DL_ESTABLISH | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"DL_ESTABLISH write ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 128, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"DL_ESTABLISH read ret=%d\n", ret); + rfrm = (iframe_t *)buf; + if (ret>0) { + if (rfrm->prim == (DL_ESTABLISH | CONFIRM)) { + di->flag |= FLG_BCHANNEL_ACTIVE; + } + } + return(ret); +} + +int deactivate_bchan(devinfo_t *di) { + unsigned char buf[128]; + int ret; + + ret = mISDN_write_frame(di->device, buf, + di->b_adress[di->used_bchannel] | IF_DOWN, + DL_RELEASE | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"DL_RELEASE write ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 128, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"DL_RELEASE read ret=%d\n", ret); + di->flag &= ~FLG_BCHANNEL_ACTIVE; + di->flag &= ~FLG_BCHANNEL_SETUP; + ret = mISDN_clear_stack(di->device, di->b_stid[di->used_bchannel]); + if (VerifyOn>3) + fprintf(stdout,"clear_stack ret=%d\n", ret); + return(ret); +} + +int read_mutiplexer(devinfo_t *di) { + unsigned char *p, *msg, buf[MAX_REC_BUF]; + iframe_t *rfrm; + int timeout = TIMEOUT_10SEC; + int ret = 0; + int len; + + rfrm = (iframe_t *)buf; + /* Main loop */ + while (1){ + ret = mISDN_read(di->device, buf, MAX_REC_BUF, timeout); + if (VerifyOn>3) + fprintf(stdout,"readloop ret=%d\n", ret); + if (ret == -1) { + fprintf(stdout,"readloop read error\n"); + break; + } + if (ret >= 16) { + if (VerifyOn>4) + fprintf(stdout,"readloop addr(%x) prim(%x) len(%d)\n", + rfrm->addr, rfrm->prim, rfrm->len); + if (rfrm->addr == (di->b_adress[di->used_bchannel] | IF_DOWN)) { + /* B-Channel related messages */ + if (rfrm->prim == (DL_DATA | INDICATION)) { + /* received data, save it */ + write(di->save, &rfrm->data.i, rfrm->len); + } + /* D-Channel related messages */ + } else if (rfrm->addr == (di->layer2 | IF_DOWN)) { + if (VerifyOn>4) + fprintf(stdout,"readloop addr(%x) prim(%x)len(%d)\n", + rfrm->addr, rfrm->prim, rfrm->len); + process_dchannel(di, ret, rfrm); + } else { + if (VerifyOn) + fprintf(stdout,"readloop unknown addr(%x) prim((%x)len(%d)\n", + rfrm->addr, rfrm->prim, rfrm->len); + } + } + } + return(0); +} + + +int +add_dlayer2(devinfo_t *di, int prot) +{ + layer_info_t li; + stack_info_t si; + interface_info_t ii; + int lid, ret; + + memset(&li, 0, sizeof(layer_info_t)); + strcpy(&li.name[0], "user L2"); + li.object_id = -1; + li.extentions = 0; + li.pid.protocol[2] = prot; + li.pid.layermask = ISDN_LAYER(2); + li.st = di->d_stid; + lid = mISDN_new_layer(di->device, &li); + if (lid<0) + return(12); + di->layer2 = lid; + if (!di->layer2) + return(13); + + /* + * EXT_IF_CREATE | EXT_IF_EXCLUSIV sorgen dafuer, das wenn die L3 + * Schnittstelle schon benutzt ist, eine neue L2 Instanz erzeugt + * wird + */ + + ii.extentions = EXT_IF_CREATE | EXT_IF_EXCLUSIV; + ii.owner = di->layer2; + ii.peer = di->layer1; + ii.stat = IF_DOWN; + ret = mISDN_connect(di->device, &ii); + if (ret) + return(13); + ii.owner = di->layer2; + ii.stat = IF_DOWN; + ret = mISDN_get_interface_info(di->device, &ii); + if (ret != 0) + return(14); + if (ii.peer == di->layer1) + fprintf(stdout, "Layer 1 not cloned\n"); + else + fprintf(stdout, "Layer 1 %08x cloned from %08x\n", + ii.peer, di->layer1); + return(0); +} + +int do_setup(devinfo_t *di) { + unsigned char buf[1024]; + iframe_t *frm = (iframe_t *)buf; + int i, ret = 0; + stack_info_t *stinf; + status_info_t *si; + + di->bl2_prot = ISDN_PID_L2_B_TRANS; + di->bl3_prot = ISDN_PID_L3_B_TRANS; + switch (di->func) { + case 0: + di->bl1_prot = ISDN_PID_L1_B_64TRANS; + di->si = 1; + break; + default: + fprintf(stdout,"unknown program function %d\n", + di->func); + return(1); + } + ret = mISDN_get_stack_count(di->device); + if (VerifyOn>1) + fprintf(stdout,"%d stacks found\n", ret); + if (ret < di->cardnr) { + fprintf(stdout,"cannot config card nr %d only %d cards\n", + di->cardnr, ret); + return(2); + } + ret = mISDN_get_stack_info(di->device, di->cardnr, buf, 1024); + if (ret<=0) { + fprintf(stdout,"cannot get stackinfo err: %d\n", ret); + return(3); + } + stinf = (stack_info_t *)&frm->data.p; + if (VerifyOn>1) + mISDNprint_stack_info(stdout, stinf); + di->d_stid = stinf->id; + for (i=0;i<2;i++) { + if (stinf->childcnt>i) + di->b_stid[i] = stinf->child[i]; + else + di->b_stid[i] = 0; + } + + di->layer1 = mISDN_get_layerid(di->device, di->d_stid, 1); + if (di->layer1<0) { + fprintf(stdout,"cannot get layer1\n"); + return(4); + } + if (VerifyOn>1) + fprintf(stdout,"layer1 id %08x\n", di->layer1); + + di->layer2 = mISDN_get_layerid(di->device, di->d_stid, 2); + if (VerifyOn>1) + fprintf(stdout,"layer2 id %08x\n", di->layer2); + + if (di->layer2) { + fprintf(stdout,"layer 2 allready present\n"); + return(5); + } + + ret = add_dlayer2(di, ISDN_PID_L2_LAPD); + if (ret) + return(ret); + + ret = mISDN_get_status_info(di->device, di->layer1, buf, 1024); + if (ret > mISDN_HEADER_LEN) { + si = (status_info_t *)&frm->data.p; + mISDNprint_status(stdout, si); + } else + fprintf(stdout,"mISDN_get_status_info ret(%d)\n", ret); + ret = mISDN_get_status_info(di->device, di->layer2, buf, 1024); + if (ret > mISDN_HEADER_LEN) { + si = (status_info_t *)&frm->data.p; + mISDNprint_status(stdout, si); + } else + fprintf(stdout,"mISDN_get_status_info ret(%d)\n", ret); + sleep(1); + init_di = di; + return(0); +} + +void +close_di(devinfo_t *di) { + unsigned char buf[1024]; + int ret = 0; + + init_di = NULL; + ret = mISDN_write_frame(di->device, buf, di->layer3 | IF_DOWN, + MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 1024, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"read ret=%d\n", ret); +} + +static void +term_handler(int sig) +{ + if (init_di) + close_di(init_di); +} + +int main(argc,argv) +int argc; +char *argv[]; + +{ + char FileName[200],FileNameOut[200]; + int aidx=1,para=1, idx; + char sw; + devinfo_t mISDN; + int err; + + fprintf(stderr,"TestmISDN 1.0\n"); + strcpy(FileName, "test_file"); + memset(&mISDN, 0, sizeof(mISDN)); + mISDN.cardnr = 1; + mISDN.func = 0; + mISDN.phonenr[0] = 0; + + signal(SIGTERM, term_handler); + signal(SIGINT, term_handler); + signal(SIGPIPE, term_handler); + + if (argc<1) { + fprintf(stderr,"Error: Not enough arguments please check\n"); + usage(argv[0]); + exit(1); + } else { + do { + if (argv[aidx] && argv[aidx][0]=='-') { + sw=argv[aidx][1]; + switch (sw) { + case 'v': + case 'V': + VerifyOn=1; + if (argv[aidx][2]) { + VerifyOn=atol(&argv[aidx][2]); + } + break; + case 'c': + if (argv[aidx][2]) { + mISDN.cardnr=atol(&argv[aidx][2]); + } + break; + case 'F': + if (argv[aidx][2]) { + mISDN.func=atol(&argv[aidx][2]); + } + break; + case '?' : + usage(argv[0]); + exit(1); + break; + default : fprintf(stderr,"Unknown Switch %c\n",sw); + usage(argv[0]); + exit(1); + break; + } + } else { + if (para==1) { + if (argc > 1) + strcpy(FileName,argv[aidx]); + para++; + } else { + fprintf(stderr,"Undefined argument %s\n",argv[aidx]); + usage(argv[0]); + exit(1); + } + } + aidx++; + } while (aidx(mISDN.device = mISDN_open())) { + printf("TestmISDN cannot open mISDN due to %s\n", + strerror(errno)); + return(1); + } + sprintf(FileNameOut,"%s",FileName); + if (0>(mISDN.save = open(FileName, O_WRONLY|O_CREAT|O_TRUNC,S_IRWXU))) { + printf("TestmISDN cannot open %s due to %s\n",FileName, + strerror(errno)); + close(mISDN.device); + return(1); + } + if (VerifyOn>8) + fprintf(stdout,"fileno %d/%d\n",mISDN.save, mISDN.device); + err = do_setup(&mISDN); + if (err) + fprintf(stdout,"do_setup error %d\n", err); + else + read_mutiplexer(&mISDN); + close(mISDN.save); + err=mISDN_close(mISDN.device); + if (err) + fprintf(stdout,"mISDN_close: error(%d): %s\n", err, + strerror(err)); + + return(0); +} diff --git a/example/test_file b/example/test_file new file mode 100755 index 0000000000000000000000000000000000000000..a38709c06a8236ad3ec22f84b2e32581eba8df4d GIT binary patch literal 870 zcmZQ%P;hKw5@cvNHuE$K0|NuoN=8;js8~Ws;1P7O2U}-=)iJDOWP_=5;9Gr)8?2Xu zjf1h7SAwwMR?h)!H6#Kb7U#HeJzwUE_-8EAhKBcmKsV+XSNAU^{A!+|UYaWo$X(5NVw z_=Ba>U}3}w6Fbni^CC2i@QNw;6Q_4UFg(mr%wq<7tsZ7A!|rLP(S#mwFrGpaTEL7d z#J~vhDHwnaVnpKu(}Ba&&8I+q|M#Dn|ExLFYPf}`XM!zcSX~Sga+nJ762odnc)Tiz zLX*O31||;1Cgh}W;O6wx;80!71d@VlIdFOUX?Rj##*-9?R@m@x^C@srSj_^m2{|c9 w;z?ƮW9/++4WyB;00\ķ&1Tش.4yWb>50,39;D~U>9<|ۻEk'2@¯/#կۺ8&.A9484,3ouѾĿزW3'6ߪ1$!*Eڮ־\_B>RvA/%)[ƾչ¿ӽ# )FI"'>;5+-V>,0ѵ7%%6OI𹨧ڸٴu+5(#.@뒴h=_{///+/8;02287տש8/=@V>=;72=PP;A+%+61,3T];XYWõ߳+"3=\Y?<855]?1/20++3;57ZBESܻ<%)659X<797=aսV0_\( )<<.0CC54Xnl.$#-//:<7;;=T?1..//-2:54:=^aS2*%! %+-.259==\uIWWY1,0:9128<=;]ܻm\<<934622:97>A@Fy~ݾSRU@BA<9>><>[AQMuڿSAA:155/0458>?\QغbWT^9;=:;==\WWUٻTV[7683/245:=>BO߿oD_[==>>?]@CW`ۿrSE?:;93268:>X]R߾bFDBZ?YX[ABDSOsھhF_X;98669<=XAWdKRGD@]^__BEWRhsr`FB\=<=;9?PB^WpRobsGPdTGftPgZEFX?\7,*+.;]d߾C98BqZ?}B[>DW[[CCmںMQrreD>@sFS]747Addb~TDAEyܺm|ߪ뙧(-ЯaC>[PPڒ k3]5+% .ƥV?-"$6Ѽa^;63=Rݽ߳ /ġ2"!'.?t-%&**+-7𴫭W2.2:]ECDv׸ .|7-Aï6*\߿_0)+9aAFruԨ  $:أ  k"^5k5ܧ'%ޡQ3-.2@<3ɟ8 ,Qk#D''ҧO),歨_749Y|[\Pݨ< "쬝됔8 6뗜)k+קoY|7((,6骠X-5춬낂1 ;! +6)ۥZڒ<3..0=Ī:2^!E5 KWk"񫣡:&SE8+*:ܨQ6>IJަ)  (_; ?V'R(/٬X/(/hRdǼϲ &#k#=*-B­+(aX-(/eE[O߾ؾ"V(k"4. +9񯜘*k.h;/+5Kú®  #Ӓ$/I) "%'/䨝TkDP7));նެ  kt#&A2$ )m)0]-%-q¼Ӻ&  16!.5&"::!#X\:11Eݻ{r. *F))٘T,0ݩg9ѻ1"'XۻsqԲX %3$#˜듗4*󩟡R+ #;عA76Fƿrװ1k )?(&ל5*GS-# '>YGM޹ҹ< #B;+'&+9}Z/#%+4^޺WXX=:>DQܹռ?"!?D3-'(1E;/8D/#+TuDϷ\=^>1?dz6!#XګQ:.(+6Gۭ<&#'.6=iMR`\57^gԾĹ1#$?dzD;.-1YDZV5.((*.\پC>@A^SasĽ5##<ӵf[424>춯B"#-,*/QM<0>ݼܻ2!k!;㺩~G7/0:u*,/*%'=ƿC2?SFܵE)&k+>չP705=`Ҽ4/1+&%2i=DWTԽ/("!5[籦C527\PgӺY9:.'$,Gؿ_WUm߷>,&,7V;57<^]yT:1222;@SKwؿ1)!$2XӲS?:8;>\iݷRX.(*;4,(##&)2P޿yC_BPpw{Qgedt׿=-!".YҲT800/3Xn׸T:-))-5YUIeI߹D-#&7O_5/.1GպuMws>+%)?Ҽ`@:57:[uܸEYFv<))/?^VD\^FQKnPRmiRQѵ7)"7ԵA6//;R迵Q=558[cT@]ESVO`VPREEp,+aOY8..6VǷE6/18@῿DY>ZAVt~meQwbyvQBC-)T]60,/:PT>3/2됙]3)##1¥1%$+6FѺB'!7S*#-R}X;\Pح/7:0'%'+1R+$5ƴC&*؟'!?r;36?Sٮ W럯6#%4 "-^į+&G;+!!=4#"2p:?WShMwík ՘V*k;(&8ձ9".>_Z,'&.Ш*#-hC۬1%:188-׬G\==GӺ" EG2$+B-Eö,$?h#k ݝ'&Xo%$*3=Vۧ&!,GڿY-['&/Wںm/(1ת۬ "= 1,--,/F+.T# ,,0>gbƮ2")Vе/ /)0@6,#".ۜF'!&4R}c´$k֜X9915[:%*U \A嘖U0$ =;)&)-:n&k!쪣^2,.;ݩ6*.ZZ C++[) .£Y/*')?V-((-[D80:T?=YDѷ0 %ë&k=>&$:^5+&+YDZ.99$".㪠U2.;U¯C 9.'Ǣe*)b].()/DV834>Sg@\RPAEdǺ- ?(4ީC'#3㳩W3+*/>WS[X[[W~aO۾: %E3&8Ǩ6(%(/@]3+),d=.+/WR[P//]޿oEf.=;' "*Er9)$%1Э.# '3]{S74;_R׿TVPؼ׺+\=)'_R.''-;p: .Ӻټ}6)/V׼FV½Ұ.<,#/QS4*&&.Gt4&&<ѿT;7XTq޸޳4"-î@+&$(.D;+&(-:w>)!!(7@۵TRSWGV࿲ۼP4k k,F\.$$+?з^.(%*:<0-.,.7lnVyTDAܼ9"55%!$+1V\,&%'.9PQ:.-.1>CoĿEEgߵ|% %rc/)$"!*G~:,$$(+6PڳpZ;3/36?޿IfoKŸ'"A;*$!'2C֯A5.&$(.:ApܶlB<67;>EIݾŸ=#+]¬C3+(+.9Pq:+***+.?ݹ{AX;<\@Tܻ6!,=d=3,*,4_羯@2/+''*3ZPѼF??X=ZGp۽* ".?^CRټ+!k%-DG3)(*,0<_.(%$#%,;vսPCA@]@aܾ1&k '=ث?-*()*/>R/,,&#$'>_]дP{}>>ٵ;( (:Ұn9::?>YWQiۻKVB@Y><=>=?[_BGQirmSDZ]889574;=<@UiؾqTD[X>8;:;;<^\CQloPB?Y87536269<\ByOPG][=<:8;:>X\EFaKRDZX;574436;;YEfrؽwRW]\<<<8<;>?ZGFf|pRD??;453434;<;7:9<=?BASoKF@?=7251226<;_RqľcfAZ<<:5:9:=>DBTp~B[=;203///7:S=3<2>13F5@]QEۿ@=<4.0...088>UrmCBZ896865>;?_WiQ¾ܼa]882,+,,+-5:>W޾bG>9<6356;7>BESeݹm^42/)''())09?Rֽ|EZ=8/29/6:>B?}qO|PQOÿT?--*"!#$%'0>A˻]7530,095=@dr}n{_[FDCFƿؼA7,%%  "&)1_P[9/,--.1Tݸmm=-(!"*,?uX5/..,19GսCB:/-.//3?QhӼ²Qn/*'*3I۪B>.+),-/<ڶ3/2*&%227_Ѻ߸POV.),:ջF/+('&)3?vE8(#!$&&2CĹOR][Ry=&%;ﭣB/(!(-:ֵE*$%!&WԥG787=и?*k /T+ *^l9%'5DZ@>/.6={ƻ}  /^/% &>ݭ/ $/&-w9R0+5GY[ﰤ뗪9 k~Q+#-5{y1&# "804;`Y6{C6[ڽIJڱ8 kk#뛧P4.)%?ڮR4$/F98DzPV5+2CQ?A㼵خ߽< k"e0" ([٧5%'Y}Y(!'-7a{ҽüշD'  $Y8,4٦4$$+/G]>3--2:\ܾؿЮ/ 2ݬ떞B+% #55"$+9ؤo6../,3Rֽڻ;  \랧Q+ &),0qM=,#!&.6_ežV[X\@]_R¿Ѷ\% (S>,,266DyIiPSGOܶ^6421.,-/9DٽniyREDTWRSQGV{~w|yKbuKq`mSttpsmncqmQSVERO}hIKqlmwSmMiiPpPrKPlmce~O}|~bQIobwhv~h`qbMiul}mKOpiq~P|poy~~~KOqlqlop~~qpOsqpsiisr~r}IqMpMrsus}wumOK}qtpMKq}}~~ws~wrsKw}mIs}sprprv~KItrqp}}s~qrspM~Or}qK~qr|sspMsvIIOu~ir~}rK~}spvsMp}IS_aPwqu~fvOqvs`h}cOvO|}qru~MIdM~r|}lqKqMa{Od~i|eqlbvI`}mnaMvKrllpph~~ssnnsdKOqOtp`cPRMlewsa~rPKiuRqI`froEQynrSw`TDPPmbsPbqe|vnIPs|qhmaDehFiRsqmdqWWPRbPyMvVoQd{}whVMtidPPv{qKr|߿W=K=ZPRwPTRyP}UwPlSdqcTStp~RD[A?ZRPftvoaSVfPRASGQVnmSRgA|hvSVQEPYMph|}FCm޽gFTaDA^==?iFBOASeInabbPewfSaC_@QU^[TVIgqcqPohST?669QF=?=<<˹D8//48fDQTRRP^|V_FuqIK|IPWGTdTVKprgYGFZAG}pV_@GCrc|ƾOlUMSrmMw{GUdVtV?XPSo}yRvPWݼKPQdV>13898gڽ\2-047;;G׽R@A>;?]D_^Tپ~A^^]CTdcnIMPRRy|wSWRP|~IcrQRaodC]VrۼSA:/,--09ZEvݿؿٶMQU+k  37+()+-7G()8˷I]=;;\QӺ  ߝ됐?(4@U[0!*[ƽG0,/5;<>Bײ떐W  $.!/|5./-&"$2b>0,.;_RUcѾ)G#"4Ը=,&'($$-lhY-(*/^t׿ۿٱ  *_ #2S됗^/*)%"/Ͼr<-+-7[GQܼݺF 65!(3O뛥B9-&!$7gro9108:?CTݽݮR 6딛7#1QE6,"#6Kým7//58=GQظ¸F  0X!'.?௝듔l>)'/>Drd?5:8::>SB7,'"$/?Sܻt8.)"#,5?UöſR=1-+**+1;EwӾF<60,(&&(-3:?Cpƾܿػ޿A1--.--,2XUm߼VY93.+*+-/39=@Pܾ]8540/.07?FVRgKڿdB?:61../2479<[GbڽA<9841014;AEFRdqؾڿnW_?<72/244568=]WSe~߿W><=722227?\^FTPڿپnVB\=831233457<\Da|ݽG><<633229?Z^GWQڿؾyTD]?842233568;ZDa}ܼbDZ=977546;?\CPwؿbTCA\<75545789;?AWfr߾~G\>;87989=Z_CVQdrٿKPVDB[;7666899:=[EQpټWAY:8::8:=>[@CFSrڿؾeQGC_>97656888;?BRIܽPC?<<<<<<>?Z@EWSbٿtPSVDAZ<98899:;>@VoܿeEBZ=>?>>X>>[_BWeؿfSSVA]X;88999;?_VnپoTFA][YX???[_CGRyKٿeUVTE_]?<:<==?\CTmݿOQGC@\[ZXY[@DVS{w}pOPVGFDA__]Z[@BDWTfIvaTFEEA@BBBFSfyvrPTWGWEB^[Z]ADEFTdqpdSWECBBAABGTQiq{SVGEBA\Z]EVGQh|shRVGFFFGGWTPhwpKb{op|nQTVEEEFVRQge{{uInPRSRQRRPg{hwIMtegPPytm{QRQgydyow}pq~~O|r}ssp}K|uO}uwvbiw}|~~|qrqrqI~K~psqpIKMpMtwK}EQi~sqsqO}psIpqi_Vtqrq}psIKtq|pKrssIs~IKq}qpKrIrtpp|OsMstspOw}MwI|gSfh~ts}~dusIpqvtIswppI}|rOwrqpu||qrsIwOr|psI|K~~sOssp}~|pppss~rMIIqM~}I~rrrI~KpwmO|MOoppspv~prKswtt|qr|prplqrtrKOulrqbdriusvr~I~uMr|q}~mss|bvsqrKqIlwmr|}}|K~u}Ow|rMqohepivppvnOFFAW{Pvo{wimn~rotKeqKepMtV`pQcwwccwKQMIQlqgiKInaPrtQvpe{`cpiM}u~|tbP|nRvvoyPybrmPR{SRCFe~mRyiuylQTIMQQRhuuoPiuvuIIrI}MKsOOchm}qbh~rr}q|~~Kp~srIrqKq|~qqs|qOsihKpK|}vpqptsOw~qsKrr}thrOmIqp}vetrusrw|rs~Mummw}|p~s|~|ps~~s}ptuI~psrrss}wv~|}}uMup~MMsrpwqKrspcO|pqotqvKrIpKKp}rncmwuronMMqwKq~qIv~Or|vuM~mOh~I|iw|}I`w|huwKK}Iq}O~}qKss|qrMl|ldIseusqq}pIoPa}}y`PRR{mmItr~OMIutup|qIwytfl|TQQPPTetvreEFGFduhFWGWrQSSF^EGQvq{~rF>::=EڿSRڼG^=9>X98XVwvQA\ZY\BQmpmyPccSg`PfRUQmRadipF=6006>Dgu~v}ߵX*!%0GİF=8215:[޺T:-*&$)0Xwû~W?<>]@Wqھ{.k &=<+(&&(-@F:,%#"'.:,)-3;=RVt٬떘d) $^뛥+&8Q:%!)[ǽ=#$9Z>TҶB`Sֺߴ둘E-  "6- (4AX&#(6ͪ/$:n]K~ļZ|a}ٵe9"k %B%%*2y.$!"$+v۶D/.8=/9T־|׶뛭^ 'ŤB+%##%/K>/)$$.hS^B@O99_RSBRƺƿlM\ (٦F7-' ".`]/%!**0Z7`]0q@5=üRع^妛$kk c^-"!,Xï.!)/%!.rչPEiQ<7=Q{7k4ĽD-&,6:[ɯA9T|$/@*'[8;mS;ٴ$k#9XѭC=82*,;S>*'('&,=TƼRGGGDݼ4$(.\ګQ>/-15<_ѸIDX/**-13=`޺STtcgD/&#*6Tٴ^:::>YAܿSD?==>?@QOڿQe|QQڲtY247+),/42>PQ@]B]>\WFRlIwcMMbRMPTSUTQQf|qpvPRQQGWQPo}}~rKMOOvMMcu|IvM|utI}psr|~rsrp|IvwwKIIssqqrqssI|qqIOp|~qsspqspqspq}}Iqrrrsssrssqsssspqq}}pprsssrrqsp~|pqprsrssrsrprrsrrpspsqrppqrqqqsq}~qq|~qrrrsqrrssp~~~qsqrssssqqsrqrqrrrqpqrrsrssrsrqsrrrpqsps|s}rrprpprsrrrspqssrqqrsqssssssqsqrprsrrsrsssppqsqrpsrqrqsqppqrqrqrrqsrqssrsrrssrrp~r}srsqssssrqspsqqsprsssqsssrspsssssrrpqsssqrssqrsrqpp|psrssqqsssrrpqrrsssrsqsrsssrrqqpqsrqsrr~qpprqsrrppsrrsssqps@0>gWrcP~qomOu}pppIrsrqqssqrOr}KrsrpsrMp||qsrsspspqqs}qpKrsKKq~~r~q~}IqsqrsO}r|qr{>XVPWFbs~iq|D^PmWDRrpawPqoufMbeMbOIItpsbssqupuKq|McIlIp|oq~|s~MbnlqvupIs~r|r|}v}qqdpKsI|t|OIuI~|soOoKsqqq}Kun}Kq~}OvOtIKK|}rqm}KuI}uKqwlpprO~|}KqOtP}~mu~|pvpqrM|lKppq}quvh}sM|pI|~vOsu}oq}it}|}qpqyOwqqpwpKMiwqqpp~Mqty}KormusqOKsrM}MO|ususurrpqqctmdrsswrKpKOpwI|d~Kv~IK}IIK|ePuKqI}KenhswIuro}u|rucK~hMrvOrqu{dqIsr~}I|q{I~ppgRIqvwdTVWSQPqqsPP`vPertps|q|u}~qKruqwpucbTF`QoMwuwgqsI~}swpeSPs|ptmshMQOSPdOvMRytsKhbah|OK~rwdqI~mbatmdKvwKrItMl~hui|IvoKOSQR{KalhvyibhsowSp`n`Q|IlbQKdRUqgirPnRS|pp}}tr|OGFfDD}KQTSIVPsTBWEECRaK~wKvopQgQUPppStnm`fvRbvhvsMUTSiWFmw{smrbbsy|}PPRFcRhgfiSPqTsRobptrlePQuTotRsrfqInhmTEVVBEGWPwoslf{IQhhPQ^X><647=Y]RٽؿOsIp~bbQWGFBDWVPgbqsspKv۽MQF\=9355149=XWlݽyQEY>><::=XY]T{cؾnB<70-++,+.19YWRE^X;8=;:=?@BEgs׿o^53/+()**,0<]UӿQCB?58;688?]?VIcn=00+%$&&&)0;Yf޹OB^?5173359\>AO~sM»|[3.+$""$$&-5?Tù|_X<4/44279^^EcK|lyOÿA4,*%!"$$*2?Gո}[;60..5\<0=q@G~[WAֶ<+)) #"",:]TD883-+/5P׻P>6//,-/9W@`AV[CC_;GmIhۿFa7'#$"$#-@aѷn;///,+/;SoBٺn]V=Z@Z8ERI`ߺߺUF8( $ $$-@նIU:.-/-.39BEֿuAAZ>;Y\CSsڻ?C/&! "()1mֽB\4.-/128]rtߺcg@A==:<^[f|ۼڰF?^*! #!%(*>¹DX>.+.264>ݺMG]CX4<=?>Vظٯٮ:@X&!$!+++AY683,,7=8B{^\Z=;;ZAQܽ޺ܽ>57/'#%&'(,2Z}۳QECDEGUVVGC\?X[@GSyndel}p}v``}|iQSVFA^^__@DWSPi|}nQWB@_]\]_@AEQ`Klbo{aaPSTVGFEEFGWURP{uc`TFDCA^@A@BFUTPp|PSTFAA@\X[][\DVVQqڿOTUG_ZZY=>Z[[BUShMQG_X<976678;?^ERpؾUGEX;<<89===\EFTڿۿwSFZ953//0237=[CPܽU^\?98976:<=YCG}ۿdG@>731..0347<[EgݾR[XX968969<730..1458\BGQܾQE[=840./2569<[Goݾ|QW_?=;9999;>X\AWdܿܿdG@?:61//2589940//269:=ZEgٿqRW@Y>=<:99:=X]AESIؾؼQF@X:61/038;;>[DPؽP]Y^?::::;;=Y_FQ`t۾ؼQGA?:51//379:<9:<>\ACGRlڿڿpRF_Y;500369:;=ZCQپfVC_]Z>=<:;=Y@CEVgKؿڽuSF_>830049::<;=>[BEGTf~۽~fWA?941159::<>ZEy۾PECA@^X>>==>[@EGVQl۽soW@?95237:::<>\Go۾qeVCB@^Y?>>>X\_EVTPi޽qiTA?96459;:;<>^WmݾIQTFA@^[?>?Z\^AEWTPtݽ}fW_>9559<<<=?ZCRqۼfVFEEA^\YXY\@CEWQtd{RVAY=;:>^]Z^@Ci~QGDBCB@_]^_DQ`dOq}|Ovwvvuidnbbl|PUFADTRVUWSaKKaRSRQQSVUUTQPdOIspMtbhgPfP`dbMqhpssOt|ptePQSVFDDGWVTSPevr}bPRSTRTVTSQimMq}MwtbnydnhcmvwMqsqs~}~}}MwwOI~ppprsqq}~~rq}~srs~pp|~~~~||~qrq~~}|KI}K||~rpqrqqs}}rqr}~pqrsqrsrprrqq~q~~}}qppsrrrrqprrr~qqpp~prqsspsrsqs~}rspqspqrqqsssrsrsrrrp|~rprssssrrsr}pqqssppqqrsqrqssrrsssqprsrssrrqpsssr}rpq~ss~rsrqrpqspsqq~~}qr~~qqqrssqsspp~p~rqqrsspppqspsrrsrqqssrrs~rssp}~qrsrspqrqqsrqsqsrprrrqprssss~rrqprsqqprrrsssrrQ_:4;BַW>?Al{A[BdIWGGWRohDATܻG?\GfKEY_QommhOrQGVSoKodybpqKhmlbuvK|ePP`b|ps~qqKq|pKvOKswho~r}s}vuqpOMbusƾ8.1\Ҿ?=Y?ZAgֿ@_SܾTCCRRB_W|OfhPUTMmDZ6-,2ErQ<53>漳enOտI/&*;׺W?XGлa[\fտೣ0k*ޫY<0)),Q:15Tݺ=2/4^smWD???E¿UAG}ۻP+._X5)&,I;3>ۼ?)$'4qDX>>ZR۷@_tIGDGݣ 5E<-%#-֤92:PĿK/""-{D848YV½@=BѽmS`ޥ! (C7,%'G;+5P޴Z+*->aA;;_VTt|RSQ߿gÿmRPhƼDk 6\7'$WW79fػA/.S]*!-^ڱQ==TۿWڼCT߸PY=R;3T8)# 'VX=Tý:++0AA;5>DfVfܼvSRĨO,Q4& 5B_nܵ?,')\S?8=Xd}mB_fƸPQdɾ8=i7'#0ޮc?-%*9n齬V-)3AC<:YQPmVGٸyES~ɺP&k/X/4<߯qݾE=>>h~@:EgúUBUCPtEZ\@TIyRt|UDAYCکP)$'0tKSUOTG]FػaFFSqirQ__D}SGSM|fGCWIPS~g^XAweVDGufTuPWRr}us|`Rl~Pau|eK~QRTSSgdTEQMstq}vdTFWiwpwiep~QQh{onc~RBETt~pyeopm`KwTQvsaPM~itnKqmM~QaMqcdfRl~hPP{{lM||~q}aMMeS|~SeIuRIrldKybqomubQObhtIcbutvdrlc}PadRsQh}M~srKr~lQoOh|PP|PaupripvmOp|dPVVInePru{b~aWQgRv}rhQ~uctPSflMiS~QQvhRP{dvilP}|}~vO~}MtKlMqO~{wvpwhpmIcMIq}vs}s|q}bntunIiaIK|c~lpgweqImhcdRn~srrKt}IIuvwprbb|~ciaRotp}dcbRc}Qgw~qqutcKqmrwM~hu|pfobMd}dvO{nsprrO|~m|MvdoOcsvQqg`rQIl|P}wwtoprurrvuw|}u}Il~fvMsc`}Oi}RTK`mPM{{cpqI`}sQgc~pionemrgrp|wswMO}se~~Olnlhqp}plK{mvOuspK}tnp~q`u|bOtKs~~s|vKgIKtOy~srPigasiy~II~OqsOoewOlKsObpimwqthpsw~Oq}pqbunqKmrtmqwwu~~hb}t}~Q{}PRlOKmvIccqe|q}qpwp~hvquu~csIM}puq~ppOssqcp~~MKhwwb}sprpMfcwuiburMqsqKburs}rss~~KK}qpsppqrsrsrsprpqsssrqp~rsqqrqspppqssqqrrqqsspprrprqqsspqrqqqssss}}sssssqqsssrqqsrqqrpsrsprrpsqrssrpsqpqqrqppssrssrrrsqqssqq~}rpsssrssrrqsrsqrqssqqrqrpsqsq~}~srrsrrsrssrq}~qsspqssssssrrsssppsprrqqq~qrsssrrrrrssrqqqrsrrspqrspqss~srsqrqpqqrpssrsqsrqqsrsqqrsrssrsqsssr}p~rrssrq}~qssqqqrrssrpqsrqqqssssrrssrsp~rrsqqsrs~qspsrssrprssssqrqrssrssrrrssrsssssrrppssrqrqp~rsrsp|~pqssrsrsp}}rppqrrsspqrsqsrs~}~MOOwmuwvwK~sphQSVFEB@@CEGSPeKuaRGFEEFFGUTSPb~}KwioingPPQQQQQPQPghtw}s}IIOwuOrugQSWFDEEEWUSRgMpImgPSTTVGGWGWSRalIKuhhoaaocmw|qpth`QRTVVSQQPnw|qmnaPQRRQP`ydcMqwQVD_]\\^^_BEGTn~mPSVFECAABCCEWTRPlq~K|OOIs}c{QVVVFDCCCDWSaIltؾ`RWB@@_^]\[]DWPnMؿſC;433569=]FPcnQSShؼnRSSGDZ>=;[ES}qIrq߼T4,().3;?X\^DVTWWSMǽwSGBA^X><XZBVSPs׽dQGC@[X=;;>DDB_CþcaPyPTPWe¿߽Y(%3FQBXZ_Ggn־vGCBWVWE^Y=<<>[AUذ5..9ôD1-0?M1!5IܿS>:<^Qr^;;?ESPSAY=<>Z@Gٺ=87o^759Afؽ45ԻP=:?@O۴_87:\T`hUBZ>?]aF];[sջTBX_DIgؾݹ/!<ٶU?=XWydM>55;_SnuTA]Y[@GT\:57lߵ\739Dlٶ+">Q]\^VPUS߯Q:47=BTSTVECDBEcR=<4\ݳDX??>]ER`FZYC_/09ջA;[[FloݾĽ=kk/w]97?ƭnWY:77?SP]?XgS=++>󶭲qGYVUZEBP߻ٶѾ<$8ftOǹR4,3gF4-/=aRSSI@4..9ݻsTYCtQd߸۳C$)1?oA73>Oڵ^98<[GWVQoquhV:/%.>rRd;8]Qܿľڹ:! )1:ڵh;8;Cp_[Y><;]TE?9766;Cs}uQRvڿ¾7%!&+:T_DEGVܰE:56>|TZ826;;<;>Dٿ`STOpھ۸X.&#" #*:nWAAsܸ\><;;ZSiC]]>;5=RuRess}Q|ݾqҿ@=1)$!*8FUݼDG{žv_98:>WhbW@==<\Va~RygڿĻ@8)%"%,2=Y~ip}ǽy@>=>;[RMOvDC\[>^VvhigߺG_5)%%(,3;\p}tG[>;:\QQTSGRE^Z>GSOwoI|ؿ۷IW\.(%&,/6;EvƽMF[=:<>]R>[V@\7=V~|qIavwĿQ[-)&(,-4:T¼ID\=>\CA\CFWA]@CSSdM|sĻR9-))+,.5ZSiغPCZ?]@X?\^A\Z]EQSlrضM?/,,.-/4?]GWTlڼU<5/00/37?_EVQٽQTV_>??>=;=X@CVguB=877777@sy@RQwPUMwrv߿PFeK[584>|߼ڻ~yD\X@ampbaG=?RU<5?GSBBRqSFڹR\?Z\\RoPPC^AEP|fc~PEAUVSlDVEETVSTWlIUAPܸw_BR`c}hIEA__Vql`luQDVy}RTfO`GFSPSVUVTTQcfvSWPRWEPoQfPcbefd@YBCYXZ=>[^V޿ػrinVX=:=_G{rhTg~RmܽrREDfvVCFSRv}POvorwv}mm{bcPSS`bK|IpsP^?;7468>^DcڽvRPhwleQY;7/,'(.23.*'&"#(/8YwD;9;>XAqܾOW_84036:YVؽPCVTUۿۿX0,)%"$(-5>򿳮D><<;>_nٻTD=71389=BؾOWUTSTRغؿھIn>.,*&&'*1:]㽵F[==>?Ayݾ`F>7357;>]lۿQVPQRQ}ܾD2.-*&&)/7;T޹gC^?=<[Vlھݾ@1Y)/3\>:CۼE_n]Fr۲һ߼q>:/)'&'&,1\Aܿ-52(/Ft67.+*.54XTٶ}|hupORC?>>^;GR>PB{Q_`RSpOPB;61,-.67>RpڿھRBZ=>;=YDZD=AIFT|VrRoWc¿sC>99.-18::WcoG]Z?\^ABVhg{Ogmr||tIu}wP[;<7039=>ATrۿپKPC]Z^DSA^MiEGQpdbM~~{qIV\??:7;?Y]FRؿ|gUGEECBDCEGWVSatrspp`WFB_^ACFVSPmqpwh{aQSUSRVTSRRPPQhtu~}MmvbhuvOI}~|ePRUWVVVSPymrodPUGGEDWWFVRQPhKs}Ichn`PPPPevOO~vbdPQQQTRQabvrMbPTUGGVWWSQfc|rImbheyPQPQRPhvKbd`QRPdhtw|KvaQRUVVTTTSQehOpOchdygPfPgaa{dhuq~uogQP`{eoniMp~te`QSTSRRQPP{bwrMOtyg{a`{`fyilw}p|wogP`de{dmwOq|ubyPPPggyoiOIq~}wMvcbbcdyPSSnOmKrubvM}prq~prpprq~K}~}I||~~sssq}wuOwK}pqrqqr~|OI}psIvvOw|rs}KMMKOO|||qrsrqIwvKKKpsrrr~~qqqprqssssppsrssssrrspq}p|~|~}prpsrrrrsssssssssssqspqqrrpsqqssssrsprrq~~q~~}~~qqsssrpsrrqsqp~}ppssprqpprsp~prsrqrqssssrqqprsrsqqppssrrrsrsspqsrrrsqrsqrrrqqqssrrqprrsrqsssqsq}p~~}rsrrrq|srq}qrrssprsrsrqrrqrssrrr~ppqr~ssprspqrpqpqp~srrppprqrsssqrsrsrqssrqqrrrrp}~qssqpp~qqrr~qsrsqqqqpssssrrsspqpq~ppssssqsrpppqsrqrrp~pssrssrqsrrrsqqsrssqrsprsrrrqqrqpsssrprsprrrrqsspqrrrsssssrsssrsppqsqsqprqqsqrqpqrrqrsqssqssrsrrsrsssqprsrssqppqrrsqqqrrprrqrqsssqrsqssssqqsrrssqrqrqrsqssrrsssqqqrsrspqqqsrrsrrqrrrsssrrssrrssrrpssrrsqsppsqsspqqqssr~pq|I}prrrqrqrpqqpqrqsrpsqssqqsssrssrsqrprsssqqqssspsqqsqrrrqssrrsqppqrqrsssrqqrrpqrrpqrsrspprrssprrsssspqqsrsrrrssrsrqssrssrrsqrsrsqq}pqsqssrppsppsssrqrsssrsrs~qr}qsssrsqsqrsssrsrqqpqsssqssqrrpppqsssrrspsrpsrssqprsqrrrsssrseSRbgPlMQSSgOs~|KrrIutu}}|~qsr~||psssssqp~~}}qrrrrqrsssqqsqrrqsrqsrqqrsssqsrqrqpppprqssssrrqsqrqrrpssssssssrqss~~pppsssqrqqpsrqsqsrprrssssrrprrssrsqrssssqrpqqqr~pqpprqssssssssrsrrpsssrsrssrsssrqrqrsrsrqsprqrrqrrrqsrsqprsqrqsqprrssqsrrqssssrrsrrrqqqqrssssrrrssrrsqqrqpsssrqprrrqrsssrrsqqssrpqrsssrqrssrqrqsq~pqqsqpqqssqqrsq~||}pprprpp~}~K|~qssqpppqpprsqsrrsrqqrsrpqp~rsprpqrsprspqpqq}qrrprssrsqrprrssqqpsrsrqssrsrsrspssrss~sqrsrsspssrppqsrqsssrpqqq~rqssrpsrssrsssrqq~qqqrsqsrqpq}||}qqrqqrrpssssqqqpqprqsssrsssr~sssspqqrrqsrsssrpsssrsqrrrrrrqrqpssqssspsqqqqsssrqsrrrrqrsqqrrsrrpqspqpssq}Irrqsqqsq}|qsrsqpsssrssssqpssqrppqpsrqqr~srssrsq~pssqpqsrpqppsqpssrrspqprssqsrrqrqrsspsqrsssrssrpsssrssqqrsrqqrrqrqqsrqsrrrsss|qIPs|uMv}qMsq|KqM}It|pr|K~r~}ssr~ps~pwp~qrsprqsrss~ss|rs~srqqq}qsqsqsqssI}}r|ssslsp|spq|rp|~sIqKM|qs~}qI~sqw}~srqps}spspspqqrrsrs}r~psrsrssrr~rqq|qr~pprsrp~rrsqsqp}qqrqsrsqsrp}ppspqrrsrrprIIq}rpsq}~Oq~rp|rrppI|I|qprsrrsrppqrpq~~srsqssspprsqssqs~rr~qrqrsprrssqpp}spqsrpsrspp~ssrqqssrqp~}}qprqqrsqqrpprp}pqssprsrpqrsqrsrpsssqrrsqrsssrsq}~rsq~prrsqrqqsq~qsqssrqssqrqqrrsr}qq~qsrssqp~}psqsrsrprrssqrrssqrqrrrqrsqprssrrqprssssrprrsssssr~prppqrq~rqrsssrqqssssspsrprsppqqrssqpssrsrsrrrqqsrsrsqrp}qprqqqssrrrrrqqssrqrrpqsprrpq~}rpqrpsrrqrrsss}~rqssqrrsprrsrrssrpqrssrrrrqpqrqsspqqrr}Irqqp|prp}sqs}Ipqpr}wssrp~~qsqq}Irsrrrssrrssssrsssspsqsrpqrrrssqqpqrsqrsqsrssqqrrsqsqsssrrsrssrqrrssssrqqsrqqprsrqrrrsqqrrsrqssqqrp~prsp~psqsspsrrsssqp~}qrrqrprsqrssqsrsprssrsqqrprrsqpsqqpppsqpssrqssrsss~qpqssssrsssssssqsqrrrsqrpssrsssqqrsrrqrqrr~qsrsrI}pssssssssrsrqrrsssqqrssrrsqrrrsssrq~rpppsqpsrqsrssrqsqpsqprsrqrs}pprrssssq~qqqrqssrqprsssrrsqqrqqqqsrspssqsq}pspssrpppqsssssqrrssspprrssqqqrrqqssrrsrsrrrpp}rrsqsrsp~}rqspssrsrpqqpssrqrrrsrqprsssrsrrpppsqssspqsq~qssrsqprrqqqrrsrqssrrqsqpssqrssssrssqppprspqsrqsqp~qqrrssrsssspqrpqpprrqssqrrrsrqppqpssssrqsss~~qqqsrqssrqrrppqqrqqrpsssrrssspprprqpsqrssrsrqprqsrrrsssssrqqrrsssqssqqppprsrsrsrspsqppp~ssppqppsspqrssrrsqrqppqrsrqqsrsqrrrsrspsrsrpprsqrrssprrqsrsssqpqpsssqqsqrqssqpsrrsspI~qrprrrppssrrsqqsrpp~IpqrsqrsprprsrrpsssqrssKR\;\lݽ¹PA[:60049_BDPVDWWCDESu}une{PSaqqqqsMd{euMcmrsr}OwuK|MMOM|q|>$&P>.'8;,(2©:4;401-,1>AҵTܺD0,.8GtG=320-.,,5Rr]3,(''()+/:FؿiQTTVTPlKM}||~SFCCW{qsO{acOsqpyRSRPdtsp~~iaPRWU{~bEB_AWSP|`SVURQSTVVSuaRfdvQgnfSGGPKifUSlwM}wnTVQt{F_BFSp{GEWVo|{iOPVSnQ`hlDDC_BTnIQFGFS߾mPcC\XDi|oWBDSo{nhW3335;TӾfWZY=<<649=;_ݺuY7-)0^PnuB4/4:FڪvEPmD^Z=Gt<<_ٯm601_s[9/)7ݹE?BֵI6+*1=A.&.B޵C8=UոTDV:57=ARQ]^QpW_Z[[TIFY>]տuZ?4,6TPMw][oýP@@ݾQSF^^EZ4;DVsѿU4+.>澺]94:ScP]=61007FԾԿ?3,.9DS\X>WպFADPB;4/1>޼S[;6678<^GPܼORW_><;=]WwISDACEU.":(.]# %A.k &Z^G<ߴ뛵<0"!!*9\=)$&(6e¼T=>XP\9/.7[RürVY=_gvr|fSSms~tqdGACWW[<54<<30129`VѶ92.8ա2k(E$<2..ЦE]/D**?S,#1/qR?<),%={,FtmѸKP׾B/2t= ,Į\7:(428\:ܺ߻Y;=::S|V}OeGE¿GVY;57Qڴ[/,.6G{PݿngR@=42 ls -l +insgesamt 0 +test@pingi:~ > logout diff --git a/example/test_x75.out b/example/test_x75.out new file mode 100644 index 0000000..0e38149 --- /dev/null +++ b/example/test_x75.out @@ -0,0 +1,4 @@ +test +test +ls -l +logout diff --git a/example/testcon.c b/example/testcon.c new file mode 100644 index 0000000..5ee9eaa --- /dev/null +++ b/example/testcon.c @@ -0,0 +1,868 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mISDNlib.h" +#include "l3dss1.h" + +void usage(pname) +char *pname; +{ + fprintf(stderr,"Call with %s [options] [filename]\n",pname); + fprintf(stderr,"\n"); + fprintf(stderr," filename filename.in incoming data\n"); + fprintf(stderr," filename.out outgoing data\n"); + fprintf(stderr," data is alaw for voice\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"\n Valid options are:\n"); + fprintf(stderr,"\n"); + fprintf(stderr," -? Usage ; printout this information\n"); + fprintf(stderr," -c use card number n (default 1)\n"); + fprintf(stderr," -F use function n (default 0)\n"); + fprintf(stderr," 0 send and recive voice\n"); + fprintf(stderr," 1 send touchtones\n"); + fprintf(stderr," 2 recive touchtones\n"); + fprintf(stderr," 3 send and recive hdlc data\n"); + fprintf(stderr," 4 send and recive X75 data\n"); + fprintf(stderr," 5 send and recive voice early B connect\n"); + fprintf(stderr," -n Phonenumber to dial\n"); + fprintf(stderr," -vn Printing debug info level n\n"); + fprintf(stderr,"\n"); +} + +typedef struct _devinfo { + int device; + int cardnr; + int func; + char phonenr[32]; + int d_stid; + int layer1; + int layer2; + int layer3; + int b_stid[2]; + int b_adress[2]; + int used_bchannel; + int save; + int play; + FILE *fplay; + int flag; + int val; + int cr; + int si; + int bl1_prot; + int bl2_prot; + int bl3_prot; +} devinfo_t; + +#define FLG_SEND_TONE 0x0001 +#define FLG_SEND_DATA 0x0002 +#define FLG_BCHANNEL_SETUP 0x0010 +#define FLG_BCHANNEL_DOACTIVE 0x0020 +#define FLG_BCHANNEL_ACTIVE 0x0040 +#define FLG_BCHANNEL_ACTDELAYED 0x0080 +#define FLG_CALL_ORGINATE 0x0100 +#define FLG_BCHANNEL_EARLY 0x0200 + + +#define MAX_REC_BUF 4000 +#define MAX_DATA_BUF 1024 + +static int VerifyOn=0; +static char tt_char[] = "0123456789ABCD*#"; + +#define PLAY_SIZE 64 + +#define MsgHead(ptr, cref, mty) \ + *ptr++ = 0x8; \ + if (cref == -1) { \ + *ptr++ = 0x0; \ + } else { \ + *ptr++ = 0x1; \ + *ptr++ = cref^0x80; \ + } \ + *ptr++ = mty + +int play_msg(devinfo_t *di) { + unsigned char buf[PLAY_SIZE+mISDN_HEADER_LEN]; + iframe_t *frm = (iframe_t *)buf; + int len, ret; + + if (di->play<0) + return(0); + len = read(di->play, buf + mISDN_HEADER_LEN, PLAY_SIZE); + if (len<0) { + printf("play_msg err %d: \"%s\"\n", errno, strerror(errno)); + close(di->play); + di->play = -1; + } + + frm->addr = di->b_adress[di->used_bchannel] | IF_DOWN; + frm->prim = DL_DATA | REQUEST; + frm->dinfo = 0; + frm->len = len; + ret = mISDN_write(di->device, buf, len+mISDN_HEADER_LEN, 8000); + if (ret < 0) + fprintf(stdout,"play write error %d %s\n", errno, strerror(errno)); + else if (VerifyOn>3) + fprintf(stdout,"play write ret=%d\n", ret); + return(ret); +} + +int send_data(devinfo_t *di) { + unsigned char buf[MAX_DATA_BUF+mISDN_HEADER_LEN]; + iframe_t *frm = (iframe_t *)buf; + unsigned char *data; + int len, ret; + + if (di->play<0 || !di->fplay) + return(0); + if (!(data = fgets(buf + mISDN_HEADER_LEN, MAX_DATA_BUF, di->fplay))) { + close(di->play); + di->play = -1; + data = buf + mISDN_HEADER_LEN; + data[0] = 4; /* ctrl-D */ + data[1] = 0; + } + len = strlen(data); + if (len==0) { + close(di->play); + di->play = -1; + data[0] = 4; /* ctrl-D */ + len = 1; + } + + frm->addr = di->b_adress[di->used_bchannel] | IF_DOWN; + frm->prim = DL_DATA | REQUEST; + frm->dinfo = 0; + frm->len = len; + ret = mISDN_write(di->device, buf, len+mISDN_HEADER_LEN, 100000); + if (ret < 0) + fprintf(stdout,"send_data write error %d %s\n", errno, strerror(errno)); + else if (VerifyOn>3) + fprintf(stdout,"send_data write ret=%d\n", ret); + return(ret); +} + +int setup_bchannel(devinfo_t *di) { + mISDN_pid_t pid; + int ret; + layer_info_t li; + + + if ((di->used_bchannel<0) || (di->used_bchannel>1)) { + fprintf(stdout, "wrong channel %d\n", di->used_bchannel); + return(0); + } + memset(&li, 0, sizeof(layer_info_t)); + strcpy(&li.name[0], "B L3"); + li.object_id = -1; + li.extentions = 0; + li.pid.protocol[3] = di->bl3_prot; + li.pid.layermask = ISDN_LAYER(3); + li.st = di->b_stid[di->used_bchannel]; + ret = mISDN_new_layer(di->device, &li); + if (ret<0) { + fprintf(stdout, "new_layer ret(%d)\n", ret); + return(0); + } + if (ret) { + di->b_adress[di->used_bchannel] = ret; + if (VerifyOn>2) + fprintf(stdout,"b_adress%d %08x\n", + di->used_bchannel+1, ret); + memset(&pid, 0, sizeof(mISDN_pid_t)); + pid.protocol[1] = di->bl1_prot; + pid.protocol[2] = di->bl2_prot; + pid.protocol[3] = di->bl3_prot; + pid.layermask = ISDN_LAYER(1) | ISDN_LAYER(2)| ISDN_LAYER(3); + if (di->flag & FLG_CALL_ORGINATE) + pid.global = 1; + ret = mISDN_set_stack(di->device, + di->b_stid[di->used_bchannel], &pid); + if (ret) { + fprintf(stdout, "set_stack ret(%d)\n", ret); + return(0); + } + ret = di->b_adress[di->used_bchannel]; + } + return(ret); +} + +int send_SETUP(devinfo_t *di, int SI, char *PNr) { + unsigned char *np, *p, *msg, buf[1024]; + int len, ret; + + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_SETUP); + *p++ = 0xa1; /* complete indicator */ + *p++ = IE_BEARER; + if (SI == 1) { /* Audio */ + *p++ = 0x3; /* Length */ + *p++ = 0x90; /* Coding Std. CCITT, 3.1 kHz audio */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + *p++ = 0xa3; /* A-Law Audio */ + } else { /* default Datatransmission 64k */ + *p++ = 0x2; /* Length */ + *p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inf */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + } + *p++ = IE_CALLED_PN; + np = PNr; + *p++ = strlen(np) + 1; + /* Classify as AnyPref. */ + *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */ + while (*np) + *p++ = *np++ & 0x7f; + len = p - msg; + ret = mISDN_write_frame(di->device, buf, di->layer3 | IF_DOWN, + DL_DATA | REQUEST, 0, len, msg, TIMEOUT_1SEC); + return(ret); +} + +int activate_bchan(devinfo_t *di) { + unsigned char buf[128]; + iframe_t *rfrm; + int ret; + + ret = mISDN_write_frame(di->device, buf, + di->b_adress[di->used_bchannel] | IF_DOWN, + DL_ESTABLISH | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"DL_ESTABLISH write ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 128, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"DL_ESTABLISH read ret=%d\n", ret); + rfrm = (iframe_t *)buf; + if (ret>0) { + if (rfrm->prim == (DL_ESTABLISH | CONFIRM)) { + di->flag |= FLG_BCHANNEL_ACTIVE; + } + } + return(ret); +} + +int deactivate_bchan(devinfo_t *di) { + unsigned char buf[128]; + int ret; + + ret = mISDN_write_frame(di->device, buf, + di->b_adress[di->used_bchannel] | IF_DOWN, + DL_RELEASE | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"DL_RELEASE write ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 128, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"DL_RELEASE read ret=%d\n", ret); + di->flag &= ~FLG_BCHANNEL_ACTIVE; + di->flag &= ~FLG_BCHANNEL_SETUP; + ret = mISDN_clear_stack(di->device, di->b_stid[di->used_bchannel]); + if (VerifyOn>3) + fprintf(stdout,"clear_stack ret=%d\n", ret); + return(ret); +} + +int send_touchtone(devinfo_t *di, int tone) { + iframe_t frm; + int tval, ret; + + if (VerifyOn>1) + fprintf(stdout,"send_touchtone %c\n", DTMF_TONE_MASK & tone); + tval = DTMF_TONE_VAL | tone; + ret = mISDN_write_frame(di->device, &frm, + di->b_adress[di->used_bchannel] | IF_DOWN, + PH_CONTROL | REQUEST, 0, 4, &tval, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"tt send ret=%d\n", ret); + return(ret); +} + +int read_mutiplexer(devinfo_t *di) { + unsigned char *p, *msg, buf[MAX_REC_BUF]; + iframe_t *rfrm; + int timeout = TIMEOUT_10SEC; + int ret = 0; + int len; + + rfrm = (iframe_t *)buf; + /* Main loop */ + +start_again: + while ((ret = mISDN_read(di->device, buf, MAX_REC_BUF, timeout))) { + if (VerifyOn>3) + fprintf(stdout,"readloop ret=%d\n", ret); + if (ret >= 16) { + if (VerifyOn>4) + fprintf(stdout,"readloop addr(%x) prim(%x) len(%d)\n", + rfrm->addr, rfrm->prim, rfrm->len); + if (rfrm->addr == (di->b_adress[di->used_bchannel] | IF_DOWN)) { + /* B-Channel related messages */ + if (rfrm->prim == (DL_DATA | INDICATION)) { + /* received data, save it */ + write(di->save, &rfrm->data.i, rfrm->len); + } else if (rfrm->prim == (DL_DATA | CONFIRM)) { + /* get ACK of send data, so we can + * send more + */ + if (VerifyOn>5) + fprintf(stdout,"DL_DATA_CNF\n"); + switch (di->func) { + case 0: + case 2: + if (di->play > -1) + play_msg(di); + break; + } + } else if (rfrm->prim == (PH_CONTROL | INDICATION)) { + if ((rfrm->len == 4) && + ((rfrm->data.i & ~DTMF_TONE_MASK) + == DTMF_TONE_VAL)) { + fprintf(stdout,"GOT TT %c\n", + DTMF_TONE_MASK & rfrm->data.i); + } else + fprintf(stdout,"unknown PH_CONTROL len %d/val %x\n", + rfrm->len, rfrm->data.i); + } + /* D-Channel related messages */ + } else if ((ret > 19) && (buf[19] == MT_CONNECT) && + (di->flag & FLG_CALL_ORGINATE)) { + /* We got connect, so bring B-channel up */ + if (!(di->flag & FLG_BCHANNEL_EARLY)) { + if (!(di->flag & FLG_BCHANNEL_ACTDELAYED)) + activate_bchan(di); + else + di->flag |= FLG_BCHANNEL_DOACTIVE; + } + /* send a CONNECT_ACKNOWLEDGE */ + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_CONNECT_ACKNOWLEDGE); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + /* if here is outgoing data, send first part */ + switch (di->func) { + case 0: + case 2: + case 5: + if (di->play > -1) + play_msg(di); + break; + case 1: + /* send next after 2 sec */ + timeout = 2*TIMEOUT_1SEC; + di->flag |= FLG_SEND_TONE; + break; + case 3: + case 4: + /* setup B after 1 sec */ + timeout = 1*TIMEOUT_1SEC; + break; + } + } else if ((ret > 19) && (buf[19] == MT_CONNECT_ACKNOWLEDGE) && + (!(di->flag & FLG_CALL_ORGINATE))) { + /* We got connect ack, so bring B-channel up */ + if (!(di->flag & FLG_BCHANNEL_EARLY)) { + if (!(di->flag & FLG_BCHANNEL_ACTDELAYED)) + activate_bchan(di); + else + di->flag |= FLG_BCHANNEL_DOACTIVE; + } + /* if here is outgoing data, send first part */ + switch (di->func) { + case 0: + case 2: + case 5: + if (di->play > -1) + play_msg(di); + break; + case 1: + /* send next after 2 sec */ + timeout = 2*TIMEOUT_1SEC; + di->flag |= FLG_SEND_TONE; + break; + case 3: + case 4: + /* setup B after 1 sec */ + timeout = 1*TIMEOUT_1SEC; + break; + } + } else if ((ret > 19) && (buf[19] == MT_DISCONNECT)) { + /* send a RELEASE */ + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_RELEASE); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + } else if ((ret > 19) && (buf[19] == MT_RELEASE)) { + /* on a disconnecting msg leave loop */ + /* send a RELEASE_COMPLETE */ + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_RELEASE_COMPLETE); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + return(2); + } else if ((ret > 19) && (buf[19] == MT_RELEASE_COMPLETE)) { + /* on a disconnecting msg leave loop */ + return(1); + } + } + } + if (di->flag & FLG_SEND_TONE) { + if (di->val) { + di->val--; + send_touchtone(di, tt_char[di->val]); + } else { + /* After last tone disconnect */ + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_DISCONNECT); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + di->flag &= ~FLG_SEND_TONE; + } + goto start_again; + } else if (di->flag & FLG_SEND_DATA) { + if (di->play > -1) + send_data(di); + else + di->flag &= ~FLG_SEND_DATA; + goto start_again; + } else if (di->flag & FLG_BCHANNEL_DOACTIVE) { + ret = activate_bchan(di); + if (!ret) { + fprintf(stdout,"error on activate_bchan\n"); + return(0); + } + di->flag &= ~FLG_BCHANNEL_DOACTIVE; + /* send next after 1 sec */ + timeout = 1*TIMEOUT_1SEC; + di->flag |= FLG_SEND_DATA; + goto start_again; + } + return(0); +} + +int do_connection(devinfo_t *di) { + unsigned char *p, *msg, buf[1024]; + iframe_t *rfrm; + int len, idx, ret = 0; + int bchannel; + + rfrm = (iframe_t *)buf; + + if (strlen(di->phonenr)) { + di->flag |= FLG_CALL_ORGINATE; + di->cr = 0x81; + send_SETUP(di, di->si, di->phonenr); + } + bchannel= -1; + /* Wait for a SETUP message or a CALL_PROCEEDING */ + while ((ret = mISDN_read(di->device, buf, 1024, 3*TIMEOUT_10SEC))) { + if (VerifyOn>3) + fprintf(stdout,"readloop ret=%d\n", ret); + if (ret >= 20) { + if (((!(di->flag & FLG_CALL_ORGINATE)) && + (buf[19] == MT_SETUP)) || + ((di->flag & FLG_CALL_ORGINATE) && + (buf[19] == MT_CALL_PROCEEDING))) { + if (!(di->flag & FLG_CALL_ORGINATE)) + di->cr = buf[18]; + idx = 20; + while (idx 0) { + /* setup a B-channel stack */ + di->used_bchannel = bchannel -1; + switch (di->func) { + case 5: + di->flag |= FLG_BCHANNEL_EARLY; + case 0: + case 1: + case 2: + case 3: + case 4: + ret = setup_bchannel(di); + if (ret) + di->flag |= FLG_BCHANNEL_SETUP; + else { + fprintf(stdout,"error on setup_bchannel\n"); + goto clean_up; + } + if (di->flag & FLG_BCHANNEL_EARLY) { + ret = activate_bchan(di); + if (!ret) { + fprintf(stdout,"error on activate_bchan\n"); + goto clean_up; + } + } + break; + } + if (!(di->flag & FLG_CALL_ORGINATE)) { + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_CONNECT); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + } + if (!read_mutiplexer(di)) { /* timed out */ + /* send a RELEASE_COMPLETE */ + fprintf(stdout,"read_mutiplexer timed out sending RELEASE_COMPLETE\n"); + p = msg = buf + mISDN_HEADER_LEN;; + MsgHead(p, di->cr, MT_RELEASE_COMPLETE); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + } + deactivate_bchan(di); + } else { + fprintf(stdout,"no channel or no connection\n"); + } +clean_up: + sleep(1); + ret = mISDN_write_frame(di->device, buf, di->layer3 | IF_DOWN, + DL_RELEASE | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 1024, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"read ret=%d\n", ret); + sleep(1); + ret = mISDN_write_frame(di->device, buf, di->layer3 | IF_DOWN, + MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 1024, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"read ret=%d\n", ret); + return(0); +} + + + +int +add_dlayer3(devinfo_t *di, int prot) +{ + layer_info_t li; + stack_info_t si; + interface_info_t ii; + int lid, ret; + + if (di->layer3) { + memset(&si, 0, sizeof(stack_info_t)); + si.extentions = EXT_STACK_CLONE; + si.mgr = -1; + si.id = di->d_stid; + ret = mISDN_new_stack(di->device, &si); + if (ret <= 0) { + fprintf(stdout, "clone stack failed ret(%d)\n", ret); + return(11); + } + di->d_stid = ret; + } + memset(&li, 0, sizeof(layer_info_t)); + strcpy(&li.name[0], "user L3"); + li.object_id = -1; + li.extentions = 0; + li.pid.protocol[3] = prot; + li.pid.layermask = ISDN_LAYER(3); + li.st = di->d_stid; + lid = mISDN_new_layer(di->device, &li); + if (lid<0) + return(12); + di->layer3 = lid; + if (!di->layer3) + return(13); + + /* + * EXT_IF_CREATE | EXT_IF_EXCLUSIV sorgen dafuer, das wenn die L3 + * Schnittstelle schon benutzt ist, eine neue L2 Instanz erzeugt + * wird + */ + + ii.extentions = EXT_IF_CREATE | EXT_IF_EXCLUSIV; + ii.owner = di->layer3; + ii.peer = di->layer2; + ii.stat = IF_DOWN; + ret = mISDN_connect(di->device, &ii); + if (ret) + return(13); + ii.owner = di->layer3; + ii.stat = IF_DOWN; + ret = mISDN_get_interface_info(di->device, &ii); + if (ret != 0) + return(14); + if (ii.peer == di->layer2) + fprintf(stdout, "Layer 2 not cloned\n"); + else + fprintf(stdout, "Layer 2 %08x cloned from %08x\n", + ii.peer, di->layer2); + di->layer2 = ii.peer; + return(0); +} + +int do_setup(devinfo_t *di) { + unsigned char buf[1024]; + iframe_t *frm = (iframe_t *)buf; + int i, ret = 0; + stack_info_t *stinf; + status_info_t *si; + + di->bl2_prot = ISDN_PID_L2_B_TRANS; + di->bl3_prot = ISDN_PID_L3_B_TRANS; + switch (di->func) { + case 0: + case 5: + di->bl1_prot = ISDN_PID_L1_B_64TRANS; + di->si = 1; + break; + case 1: + di->bl1_prot = ISDN_PID_L1_B_64TRANS; + di->bl2_prot = ISDN_PID_L2_B_TRANSDTMF; + di->si = 1; + di->val= 8; /* send 8 touch tons (7 ... 0) */ + break; + case 2: + di->bl1_prot = ISDN_PID_L1_B_64TRANS; + di->bl2_prot = ISDN_PID_L2_B_TRANSDTMF; + di->si = 1; + break; + case 3: + di->bl1_prot = ISDN_PID_L1_B_64HDLC; + di->si = 7; + break; + case 4: + di->bl1_prot = ISDN_PID_L1_B_64HDLC; + di->bl2_prot = ISDN_PID_L2_B_X75SLP; + di->si = 7; + di->flag |= FLG_BCHANNEL_ACTDELAYED; + break; + default: + fprintf(stdout,"unknown program function %d\n", + di->func); + return(1); + } + + ret = mISDN_get_stack_count(di->device); + if (VerifyOn>1) + fprintf(stdout,"%d stacks found\n", ret); + if (ret < di->cardnr) { + fprintf(stdout,"cannot config card nr %d only %d cards\n", + di->cardnr, ret); + return(2); + } + ret = mISDN_get_stack_info(di->device, di->cardnr, buf, 1024); + if (ret<=0) { + fprintf(stdout,"cannot get stackinfo err: %d\n", ret); + return(3); + } + stinf = (stack_info_t *)&frm->data.p; + if (VerifyOn>1) + mISDNprint_stack_info(stdout, stinf); + di->d_stid = stinf->id; + for (i=0;i<2;i++) { + if (stinf->childcnt>i) + di->b_stid[i] = stinf->child[i]; + else + di->b_stid[i] = 0; + } + + di->layer1 = mISDN_get_layerid(di->device, di->d_stid, 1); + if (di->layer1<0) { + fprintf(stdout,"cannot get layer1\n"); + return(4); + } + if (VerifyOn>1) + fprintf(stdout,"layer1 id %08x\n", di->layer1); + + di->layer2 = mISDN_get_layerid(di->device, di->d_stid, 2); + if (di->layer2<0) { + fprintf(stdout,"cannot get layer2\n"); + return(5); + } + if (VerifyOn>1) + fprintf(stdout,"layer2 id %08x\n", di->layer2); + + di->layer3 = mISDN_get_layerid(di->device, di->d_stid, 3); + if (di->layer3<0) { + fprintf(stdout,"cannot get layer3\n"); + di->layer3 = 0; + } + if (VerifyOn>1) + fprintf(stdout,"layer3 id %08x\n", di->layer3); + + + ret = add_dlayer3(di, ISDN_PID_L3_DSS1USER); + if (ret) + return(ret); + ret = mISDN_write_frame(di->device, buf, di->layer3 | IF_DOWN, + DL_ESTABLISH | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"dl_etablish write ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 1024, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"dl_etablish read ret=%d\n", ret); + if (ret>0) { + if (frm->prim != (DL_ESTABLISH | CONFIRM)) + return(6); + } else { + fprintf(stdout,"DL_ESTABLISH | REQUEST return(%d)\n", ret); + return(7); + } + ret = mISDN_get_status_info(di->device, di->layer1, buf, 1024); + if (ret > mISDN_HEADER_LEN) { + si = (status_info_t *)&frm->data.p; + mISDNprint_status(stdout, si); + } else + fprintf(stdout,"mISDN_get_status_info ret(%d)\n", ret); + ret = mISDN_get_status_info(di->device, di->layer2, buf, 1024); + if (ret > mISDN_HEADER_LEN) { + si = (status_info_t *)&frm->data.p; + mISDNprint_status(stdout, si); + } else + fprintf(stdout,"mISDN_get_status_info ret(%d)\n", ret); + sleep(1); + return(0); +} + +int main(argc,argv) +int argc; +char *argv[]; + +{ + char FileName[200],FileNameOut[200]; + int aidx=1,para=1, idx; + char sw; + devinfo_t mISDN; + int err; + + fprintf(stderr,"TestmISDN 1.0\n"); + strcpy(FileName, "test_file"); + memset(&mISDN, 0, sizeof(mISDN)); + mISDN.cardnr = 1; + mISDN.func = 0; + mISDN.phonenr[0] = 0; + if (argc<1) { + fprintf(stderr,"Error: Not enough arguments please check\n"); + usage(argv[0]); + exit(1); + } else { + do { + if (argv[aidx] && argv[aidx][0]=='-') { + sw=argv[aidx][1]; + switch (sw) { + case 'v': + case 'V': + VerifyOn=1; + if (argv[aidx][2]) { + VerifyOn=atol(&argv[aidx][2]); + } + break; + case 'c': + if (argv[aidx][2]) { + mISDN.cardnr=atol(&argv[aidx][2]); + } + break; + case 'F': + if (argv[aidx][2]) { + mISDN.func=atol(&argv[aidx][2]); + } + break; + case 'n': + if (!argv[aidx][2]) { + idx = 0; + aidx++; + } else { + idx=2; + } + if (aidx<=argc) { + strcpy(mISDN.phonenr, &argv[aidx][idx]); + } else { + fprintf(stderr," Switch %c without value\n",sw); + exit(1); + } + break; + case '?' : + usage(argv[0]); + exit(1); + break; + default : fprintf(stderr,"Unknown Switch %c\n",sw); + usage(argv[0]); + exit(1); + break; + } + } else { + if (para==1) { + if (argc > 1) + strcpy(FileName,argv[aidx]); + para++; + } else { + fprintf(stderr,"Undefined argument %s\n",argv[aidx]); + usage(argv[0]); + exit(1); + } + } + aidx++; + } while (aidx(mISDN.device = mISDN_open())) { + printf("TestmISDN cannot open mISDN due to %s\n", + strerror(errno)); + return(1); + } + sprintf(FileNameOut,"%s.out",FileName); + sprintf(FileName,"%s.in",FileName); + if (0>(mISDN.save = open(FileName, O_WRONLY|O_CREAT|O_TRUNC,S_IRWXU))) { + printf("TestmISDN cannot open %s due to %s\n",FileName, + strerror(errno)); + close(mISDN.device); + return(1); + } + if (0>(mISDN.play = open(FileNameOut, O_RDONLY))) { + printf("TestmISDN cannot open %s due to %s\n",FileNameOut, + strerror(errno)); + mISDN.play = -1; + } else + mISDN.fplay = fdopen(mISDN.play, "r"); + if (VerifyOn>8) + fprintf(stdout,"fileno %d/%d/%d\n",mISDN.save, mISDN.play, + mISDN.device); + err = do_setup(&mISDN); + if (err) + fprintf(stdout,"do_setup error %d\n", err); + else + do_connection(&mISDN); + close(mISDN.save); + if (mISDN.play>=0) + close(mISDN.play); + err=mISDN_close(mISDN.device); + if (err) + fprintf(stdout,"mISDN_close: error(%d): %s\n", err, + strerror(err)); + + return(0); +} diff --git a/example/testcon.in b/example/testcon.in new file mode 100755 index 0000000..e69de29 diff --git a/example/testcon_l2.c b/example/testcon_l2.c new file mode 100644 index 0000000..64b32b0 --- /dev/null +++ b/example/testcon_l2.c @@ -0,0 +1,869 @@ +/* Beispiel fuer ein L2 B-channel modul statt dem L3 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mISDNlib.h" +#include "l3dss1.h" + +void usage(pname) +char *pname; +{ + fprintf(stderr,"Call with %s [options] [filename]\n",pname); + fprintf(stderr,"\n"); + fprintf(stderr," filename filename.in incoming data\n"); + fprintf(stderr," filename.out outgoing data\n"); + fprintf(stderr," data is alaw for voice\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"\n Valid options are:\n"); + fprintf(stderr,"\n"); + fprintf(stderr," -? Usage ; printout this information\n"); + fprintf(stderr," -c use card number n (default 1)\n"); + fprintf(stderr," -F use function n (default 0)\n"); + fprintf(stderr," 0 send and recive voice\n"); + fprintf(stderr," 1 send touchtones\n"); + fprintf(stderr," 2 recive touchtones\n"); + fprintf(stderr," 3 send and recive hdlc data\n"); + fprintf(stderr," 4 send and recive X75 data\n"); + fprintf(stderr," 5 send and recive voice early B connect\n"); + fprintf(stderr," -n Phonenumber to dial\n"); + fprintf(stderr," -vn Printing debug info level n\n"); + fprintf(stderr,"\n"); +} + +typedef struct _devinfo { + int device; + int cardnr; + int func; + char phonenr[32]; + int d_stid; + int layer1; + int layer2; + int layer3; + int b_stid[2]; + int b_adress[2]; + int used_bchannel; + int save; + int play; + FILE *fplay; + int flag; + int val; + int cr; + int si; + int bl1_prot; + int bl2_prot; + int bl3_prot; +} devinfo_t; + +#define FLG_SEND_TONE 0x0001 +#define FLG_SEND_DATA 0x0002 +#define FLG_BCHANNEL_SETUP 0x0010 +#define FLG_BCHANNEL_DOACTIVE 0x0020 +#define FLG_BCHANNEL_ACTIVE 0x0040 +#define FLG_BCHANNEL_ACTDELAYED 0x0080 +#define FLG_CALL_ORGINATE 0x0100 +#define FLG_BCHANNEL_EARLY 0x0200 + +#define MAX_REC_BUF 4000 +#define MAX_DATA_BUF 1024 + +#define ISDN_PID_L2_B_USER 0x420000ff + +static int VerifyOn=0; + +char tt_char[]="0123456789ABCD*#"; + +#define PLAY_SIZE 64 + +#define MsgHead(ptr, cref, mty) \ + *ptr++ = 0x8; \ + if (cref == -1) { \ + *ptr++ = 0x0; \ + } else { \ + *ptr++ = 0x1; \ + *ptr++ = cref^0x80; \ + } \ + *ptr++ = mty + +int play_msg(devinfo_t *di) { + unsigned char buf[PLAY_SIZE+mISDN_HEADER_LEN]; + iframe_t *frm = (iframe_t *)buf; + int len, ret; + + if (di->play<0) + return(0); + len = read(di->play, buf + mISDN_HEADER_LEN, PLAY_SIZE); + if (len<0) { + printf("play_msg err %d: \"%s\"\n", errno, strerror(errno)); + close(di->play); + di->play = -1; + } + + frm->addr = di->b_adress[di->used_bchannel] | IF_DOWN; + frm->prim = PH_DATA | REQUEST; + frm->dinfo = 0; + frm->len = len; + ret = mISDN_write(di->device, buf, len+mISDN_HEADER_LEN, 8000); + if (ret < 0) + fprintf(stdout,"play write error %d %s\n", errno, strerror(errno)); + else if (VerifyOn>3) + fprintf(stdout,"play write ret=%d\n", ret); + return(ret); +} + +int send_data(devinfo_t *di) { + unsigned char buf[MAX_DATA_BUF+mISDN_HEADER_LEN]; + iframe_t *frm = (iframe_t *)buf; + unsigned char *data; + int len, ret; + + if (di->play<0 || !di->fplay) + return(0); + if (!(data = fgets(buf + mISDN_HEADER_LEN, MAX_DATA_BUF, di->fplay))) { + close(di->play); + di->play = -1; + data = buf + mISDN_HEADER_LEN; + data[0] = 4; /* ctrl-D */ + data[1] = 0; + } + len = strlen(data); + if (len==0) { + close(di->play); + di->play = -1; + data[0] = 4; /* ctrl-D */ + len = 1; + } + + frm->addr = di->b_adress[di->used_bchannel] | IF_DOWN; + frm->prim = PH_DATA | REQUEST; + frm->dinfo = 0; + frm->len = len; + ret = mISDN_write(di->device, buf, len+mISDN_HEADER_LEN, 100000); + if (ret < 0) + fprintf(stdout,"send_data write error %d %s\n", errno, strerror(errno)); + else if (VerifyOn>3) + fprintf(stdout,"send_data write ret=%d\n", ret); + return(ret); +} + +int setup_bchannel(devinfo_t *di) { + mISDN_pid_t pid; + int ret; + layer_info_t li; + + + if ((di->used_bchannel<0) || (di->used_bchannel>1)) { + fprintf(stdout, "wrong channel %d\n", di->used_bchannel); + return(0); + } + memset(&li, 0, sizeof(layer_info_t)); + strcpy(&li.name[0], "B L2"); + li.object_id = -1; + li.extentions = 0; + li.pid.protocol[2] = di->bl2_prot; + li.pid.layermask = ISDN_LAYER(2); + li.st = di->b_stid[di->used_bchannel]; + ret = mISDN_new_layer(di->device, &li); + if (ret<0) { + fprintf(stdout, "new_layer ret(%d)\n", ret); + return(0); + } + if (ret) { + di->b_adress[di->used_bchannel] = ret; + if (VerifyOn>2) + fprintf(stdout,"b_adress%d %08x\n", + di->used_bchannel+1, ret); + memset(&pid, 0, sizeof(mISDN_pid_t)); + pid.protocol[1] = di->bl1_prot; + pid.protocol[2] = di->bl2_prot; +// pid.protocol[3] = di->bl3_prot; + pid.layermask = ISDN_LAYER(1) | ISDN_LAYER(2); // | ISDN_LAYER(3); + if (di->flag & FLG_CALL_ORGINATE) + pid.global = 1; + ret = mISDN_set_stack(di->device, + di->b_stid[di->used_bchannel], &pid); + if (ret) { + fprintf(stdout, "set_stack ret(%d)\n", ret); + return(0); + } + ret = di->b_adress[di->used_bchannel]; + } + return(ret); +} + +int send_SETUP(devinfo_t *di, int SI, char *PNr) { + unsigned char *np, *p, *msg, buf[1024]; + int len, ret; + + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_SETUP); + *p++ = 0xa1; /* complete indicator */ + *p++ = IE_BEARER; + if (SI == 1) { /* Audio */ + *p++ = 0x3; /* Length */ + *p++ = 0x90; /* Coding Std. CCITT, 3.1 kHz audio */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + *p++ = 0xa3; /* A-Law Audio */ + } else { /* default Datatransmission 64k */ + *p++ = 0x2; /* Length */ + *p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inf */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + } + *p++ = IE_CALLED_PN; + np = PNr; + *p++ = strlen(np) + 1; + /* Classify as AnyPref. */ + *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */ + while (*np) + *p++ = *np++ & 0x7f; + len = p - msg; + ret = mISDN_write_frame(di->device, buf, di->layer3 | IF_DOWN, + DL_DATA | REQUEST, 0, len, msg, TIMEOUT_1SEC); + return(ret); +} + +int activate_bchan(devinfo_t *di) { + unsigned char buf[128]; + iframe_t *rfrm; + int ret; + + ret = mISDN_write_frame(di->device, buf, + di->b_adress[di->used_bchannel] | IF_DOWN, + PH_ACTIVATE | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"PH_ACTIVATE write ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 128, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"PH_ACTIVATE read ret=%d\n", ret); + rfrm = (iframe_t *)buf; + if (ret>0) { + if (rfrm->prim == (PH_ACTIVATE | CONFIRM)) { + di->flag |= FLG_BCHANNEL_ACTIVE; + } + } + return(ret); +} + +int deactivate_bchan(devinfo_t *di) { + unsigned char buf[128]; + int ret; + + ret = mISDN_write_frame(di->device, buf, + di->b_adress[di->used_bchannel] | IF_DOWN, + DL_RELEASE | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"DL_RELEASE write ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 128, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"DL_RELEASE read ret=%d\n", ret); + di->flag &= ~FLG_BCHANNEL_ACTIVE; + di->flag &= ~FLG_BCHANNEL_SETUP; + ret = mISDN_clear_stack(di->device, di->b_stid[di->used_bchannel]); + if (VerifyOn>3) + fprintf(stdout,"clear_stack ret=%d\n", ret); + return(ret); +} + +int send_touchtone(devinfo_t *di, int tone) { + iframe_t frm; + int tval, ret; + + if (VerifyOn>1) + fprintf(stdout,"send_touchtone %c\n", DTMF_TONE_MASK & tone); + tval = DTMF_TONE_VAL | tone; + ret = mISDN_write_frame(di->device, &frm, + di->b_adress[di->used_bchannel] | IF_DOWN, + PH_CONTROL | REQUEST, 0, 4, &tval, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"tt send ret=%d\n", ret); + return(ret); +} + +int read_mutiplexer(devinfo_t *di) { + unsigned char *p, *msg, buf[MAX_REC_BUF]; + iframe_t *rfrm; + int timeout = TIMEOUT_10SEC; + int ret = 0; + int len; + + rfrm = (iframe_t *)buf; + /* Main loop */ + +start_again: + while ((ret = mISDN_read(di->device, buf, MAX_REC_BUF, timeout))) { + if (VerifyOn>3) + fprintf(stdout,"readloop ret=%d\n", ret); + if (ret >= 16) { + if (VerifyOn>4) + fprintf(stdout,"readloop addr(%x) prim(%x) len(%d)\n", + rfrm->addr, rfrm->prim, rfrm->len); + if (rfrm->addr == (di->b_adress[di->used_bchannel] | IF_DOWN)) { + /* B-Channel related messages */ + if (rfrm->prim == (PH_DATA | INDICATION)) { + /* received data, save it */ + write(di->save, &rfrm->data.i, rfrm->len); + } else if (rfrm->prim == (PH_DATA | CONFIRM)) { + /* get ACK of send data, so we can + * send more + */ + if (VerifyOn>5) + fprintf(stdout,"PH_DATA_CNF\n"); + switch (di->func) { + case 0: + case 2: + if (di->play > -1) + play_msg(di); + break; + } + } else if (rfrm->prim == (PH_CONTROL | INDICATION)) { + if ((rfrm->len == 4) && + ((rfrm->data.i & ~DTMF_TONE_MASK) + == DTMF_TONE_VAL)) { + fprintf(stdout,"GOT TT %c\n", + DTMF_TONE_MASK & rfrm->data.i); + } else + fprintf(stdout,"unknown PH_CONTROL len %d/val %x\n", + rfrm->len, rfrm->data.i); + } + /* D-Channel related messages */ + } else if ((ret > 19) && (buf[19] == MT_CONNECT) && + (di->flag & FLG_CALL_ORGINATE)) { + /* We got connect, so bring B-channel up */ + if (!(di->flag & FLG_BCHANNEL_EARLY)) { + if (!(di->flag & FLG_BCHANNEL_ACTDELAYED)) + activate_bchan(di); + else + di->flag |= FLG_BCHANNEL_DOACTIVE; + } + /* send a CONNECT_ACKNOWLEDGE */ + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_CONNECT_ACKNOWLEDGE); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + /* if here is outgoing data, send first part */ + switch (di->func) { + case 0: + case 2: + case 5: + if (di->play > -1) + play_msg(di); + break; + case 1: + /* send next after 2 sec */ + timeout = 2*TIMEOUT_1SEC; + di->flag |= FLG_SEND_TONE; + break; + case 3: + case 4: + /* setup B after 1 sec */ + timeout = 1*TIMEOUT_1SEC; + break; + } + } else if ((ret > 19) && (buf[19] == MT_CONNECT_ACKNOWLEDGE) && + (!(di->flag & FLG_CALL_ORGINATE))) { + /* We got connect ack, so bring B-channel up */ + if (!(di->flag & FLG_BCHANNEL_EARLY)) { + if (!(di->flag & FLG_BCHANNEL_ACTDELAYED)) + activate_bchan(di); + else + di->flag |= FLG_BCHANNEL_DOACTIVE; + } + /* if here is outgoing data, send first part */ + switch (di->func) { + case 0: + case 2: + case 5: + if (di->play > -1) + play_msg(di); + break; + case 1: + /* send next after 2 sec */ + timeout = 2*TIMEOUT_1SEC; + di->flag |= FLG_SEND_TONE; + break; + case 3: + case 4: + /* setup B after 1 sec */ + timeout = 1*TIMEOUT_1SEC; + break; + } + } else if ((ret > 19) && (buf[19] == MT_DISCONNECT)) { + /* send a RELEASE */ + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_RELEASE); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + } else if ((ret > 19) && (buf[19] == MT_RELEASE)) { + /* on a disconnecting msg leave loop */ + /* send a RELEASE_COMPLETE */ + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_RELEASE_COMPLETE); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + return(2); + } else if ((ret > 19) && (buf[19] == MT_RELEASE_COMPLETE)) { + /* on a disconnecting msg leave loop */ + return(1); + } + } + } + if (di->flag & FLG_SEND_TONE) { + if (di->val) { + di->val--; + send_touchtone(di, tt_char[di->val]); + } else { + /* After last tone disconnect */ + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_DISCONNECT); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + di->flag &= ~FLG_SEND_TONE; + } + goto start_again; + } else if (di->flag & FLG_SEND_DATA) { + if (di->play > -1) + send_data(di); + else + di->flag &= ~FLG_SEND_DATA; + goto start_again; + } else if (di->flag & FLG_BCHANNEL_DOACTIVE) { + ret = activate_bchan(di); + if (!ret) { + fprintf(stdout,"error on activate_bchan\n"); + return(0); + } + di->flag &= ~FLG_BCHANNEL_DOACTIVE; + /* send next after 1 sec */ + timeout = 1*TIMEOUT_1SEC; + di->flag |= FLG_SEND_DATA; + goto start_again; + } + return(0); +} + +int do_connection(devinfo_t *di) { + unsigned char *p, *msg, buf[1024]; + iframe_t *rfrm; + int len, idx, ret = 0; + int bchannel; + + rfrm = (iframe_t *)buf; + + if (strlen(di->phonenr)) { + di->flag |= FLG_CALL_ORGINATE; + di->cr = 0x81; + send_SETUP(di, di->si, di->phonenr); + } + bchannel= -1; + /* Wait for a SETUP message or a CALL_PROCEEDING */ + while ((ret = mISDN_read(di->device, buf, 1024, 3*TIMEOUT_10SEC))) { + if (VerifyOn>3) + fprintf(stdout,"readloop ret=%d\n", ret); + if (ret >= 20) { + if (((!(di->flag & FLG_CALL_ORGINATE)) && + (buf[19] == MT_SETUP)) || + ((di->flag & FLG_CALL_ORGINATE) && + (buf[19] == MT_CALL_PROCEEDING))) { + if (!(di->flag & FLG_CALL_ORGINATE)) + di->cr = buf[18]; + idx = 20; + while (idx 0) { + /* setup a B-channel stack */ + di->used_bchannel = bchannel -1; + switch (di->func) { + case 5: + di->flag |= FLG_BCHANNEL_EARLY; + case 0: + case 1: + case 2: + case 3: + case 4: + ret = setup_bchannel(di); + if (ret) + di->flag |= FLG_BCHANNEL_SETUP; + else { + fprintf(stdout,"error on setup_bchannel\n"); + goto clean_up; + } + if (di->flag & FLG_BCHANNEL_EARLY) { + ret = activate_bchan(di); + if (!ret) { + fprintf(stdout,"error on activate_bchan\n"); + goto clean_up; + } + } + break; + } + if (!(di->flag & FLG_CALL_ORGINATE)) { + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_CONNECT); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + } + if (!read_mutiplexer(di)) { /* timed out */ + /* send a RELEASE_COMPLETE */ + fprintf(stdout,"read_mutiplexer timed out sending RELEASE_COMPLETE\n"); + p = msg = buf + mISDN_HEADER_LEN;; + MsgHead(p, di->cr, MT_RELEASE_COMPLETE); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + } + deactivate_bchan(di); + } else { + fprintf(stdout,"no channel or no connection\n"); + } +clean_up: + sleep(1); + ret = mISDN_write_frame(di->device, buf, di->layer3 | IF_DOWN, + DL_RELEASE | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 1024, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"read ret=%d\n", ret); + sleep(1); + ret = mISDN_write_frame(di->device, buf, di->layer3 | IF_DOWN, + MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 1024, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"read ret=%d\n", ret); + return(0); +} + + + +int +add_dlayer3(devinfo_t *di, int prot) +{ + layer_info_t li; + stack_info_t si; + interface_info_t ii; + int lid, ret; + + if (di->layer3) { + memset(&si, 0, sizeof(stack_info_t)); + si.extentions = EXT_STACK_CLONE; + si.mgr = -1; + si.id = di->d_stid; + ret = mISDN_new_stack(di->device, &si); + if (ret <= 0) { + fprintf(stdout, "clone stack failed ret(%d)\n", ret); + return(11); + } + di->d_stid = ret; + } + memset(&li, 0, sizeof(layer_info_t)); + strcpy(&li.name[0], "user L3"); + li.object_id = -1; + li.extentions = 0; + li.pid.protocol[3] = prot; + li.pid.layermask = ISDN_LAYER(3); + li.st = di->d_stid; + lid = mISDN_new_layer(di->device, &li); + if (lid<0) + return(12); + di->layer3 = lid; + if (!di->layer3) + return(13); + + /* + * EXT_IF_CREATE | EXT_IF_EXCLUSIV sorgen dafuer, das wenn die L3 + * Schnittstelle schon benutzt ist, eine neue L2 Instanz erzeugt + * wird + */ + + ii.extentions = EXT_IF_CREATE | EXT_IF_EXCLUSIV; + ii.owner = di->layer3; + ii.peer = di->layer2; + ii.stat = IF_DOWN; + ret = mISDN_connect(di->device, &ii); + if (ret) + return(13); + ii.owner = di->layer3; + ii.stat = IF_DOWN; + ret = mISDN_get_interface_info(di->device, &ii); + if (ret != 0) + return(14); + if (ii.peer == di->layer2) + fprintf(stdout, "Layer 2 not cloned\n"); + else + fprintf(stdout, "Layer 2 %08x cloned from %08x\n", + ii.peer, di->layer2); + di->layer2 = ii.peer; + return(0); +} + +int do_setup(devinfo_t *di) { + unsigned char buf[1024]; + iframe_t *frm = (iframe_t *)buf; + int i, ret = 0; + stack_info_t *stinf; + status_info_t *si; + + di->bl2_prot = ISDN_PID_L2_B_USER; + di->bl3_prot = ISDN_PID_L3_B_TRANS; + switch (di->func) { + case 0: + case 5: + di->bl1_prot = ISDN_PID_L1_B_64TRANS; + di->si = 1; + break; + case 1: + di->bl1_prot = ISDN_PID_L1_B_64TRANS; + di->bl2_prot = ISDN_PID_L2_B_TRANSDTMF; + di->si = 1; + di->val= 8; /* send 8 touch tons (7 ... 0) */ + break; + case 2: + di->bl1_prot = ISDN_PID_L1_B_64TRANS; + di->bl2_prot = ISDN_PID_L2_B_TRANSDTMF; + di->si = 1; + break; + case 3: + di->bl1_prot = ISDN_PID_L1_B_64HDLC; + di->si = 7; + break; + case 4: + fprintf(stdout,"X.75 not supported with L2 user\n"); + return(1); + default: + fprintf(stdout,"unknown program function %d\n", + di->func); + return(1); + } + + ret = mISDN_get_stack_count(di->device); + if (VerifyOn>1) + fprintf(stdout,"%d stacks found\n", ret); + if (ret < di->cardnr) { + fprintf(stdout,"cannot config card nr %d only %d cards\n", + di->cardnr, ret); + return(2); + } + ret = mISDN_get_stack_info(di->device, di->cardnr, buf, 1024); + if (ret<=0) { + fprintf(stdout,"cannot get stackinfo err: %d\n", ret); + return(3); + } + stinf = (stack_info_t *)&frm->data.p; + if (VerifyOn>1) + mISDNprint_stack_info(stdout, stinf); + di->d_stid = stinf->id; + for (i=0;i<2;i++) { + if (stinf->childcnt>i) + di->b_stid[i] = stinf->child[i]; + else + di->b_stid[i] = 0; + } + + di->layer1 = mISDN_get_layerid(di->device, di->d_stid, 1); + if (di->layer1<0) { + fprintf(stdout,"cannot get layer1\n"); + return(4); + } + if (VerifyOn>1) + fprintf(stdout,"layer1 id %08x\n", di->layer1); + + di->layer2 = mISDN_get_layerid(di->device, di->d_stid, 2); + if (di->layer2<0) { + fprintf(stdout,"cannot get layer2\n"); + return(5); + } + if (VerifyOn>1) + fprintf(stdout,"layer2 id %08x\n", di->layer2); + + di->layer3 = mISDN_get_layerid(di->device, di->d_stid, 3); + if (di->layer3<0) { + fprintf(stdout,"cannot get layer3\n"); + di->layer3 = 0; + } + if (VerifyOn>1) + fprintf(stdout,"layer3 id %08x\n", di->layer3); + + + ret = add_dlayer3(di, ISDN_PID_L3_DSS1USER); + if (ret) + return(ret); + ret = mISDN_write_frame(di->device, buf, di->layer3 | IF_DOWN, + DL_ESTABLISH | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"dl_etablish write ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 1024, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"dl_etablish read ret=%d\n", ret); + if (ret>0) { + if (frm->prim != (DL_ESTABLISH | CONFIRM)) + return(6); + } else { + fprintf(stdout,"DL_ESTABLISH | REQUEST return(%d)\n", ret); + return(7); + } + ret = mISDN_get_status_info(di->device, di->layer1, buf, 1024); + if (ret > mISDN_HEADER_LEN) { + si = (status_info_t *)&frm->data.p; + mISDNprint_status(stdout, si); + } else + fprintf(stdout,"mISDN_get_status_info ret(%d)\n", ret); + ret = mISDN_get_status_info(di->device, di->layer2, buf, 1024); + if (ret > mISDN_HEADER_LEN) { + si = (status_info_t *)&frm->data.p; + mISDNprint_status(stdout, si); + } else + fprintf(stdout,"mISDN_get_status_info ret(%d)\n", ret); + sleep(1); + return(0); +} + +int main(argc,argv) +int argc; +char *argv[]; + +{ + char FileName[200],FileNameOut[200]; + int aidx=1,para=1, idx; + char sw; + devinfo_t mISDN; + int err; + + fprintf(stderr,"TestmISDN 1.0\n"); + strcpy(FileName, "test_file"); + memset(&mISDN, 0, sizeof(mISDN)); + mISDN.cardnr = 1; + mISDN.func = 0; + mISDN.phonenr[0] = 0; + if (argc<1) { + fprintf(stderr,"Error: Not enough arguments please check\n"); + usage(argv[0]); + exit(1); + } else { + do { + if (argv[aidx] && argv[aidx][0]=='-') { + sw=argv[aidx][1]; + switch (sw) { + case 'v': + case 'V': + VerifyOn=1; + if (argv[aidx][2]) { + VerifyOn=atol(&argv[aidx][2]); + } + break; + case 'c': + if (argv[aidx][2]) { + mISDN.cardnr=atol(&argv[aidx][2]); + } + break; + case 'F': + if (argv[aidx][2]) { + mISDN.func=atol(&argv[aidx][2]); + } + break; + case 'n': + if (!argv[aidx][2]) { + idx = 0; + aidx++; + } else { + idx=2; + } + if (aidx<=argc) { + strcpy(mISDN.phonenr, &argv[aidx][idx]); + } else { + fprintf(stderr," Switch %c without value\n",sw); + exit(1); + } + break; + case '?' : + usage(argv[0]); + exit(1); + break; + default : fprintf(stderr,"Unknown Switch %c\n",sw); + usage(argv[0]); + exit(1); + break; + } + } else { + if (para==1) { + if (argc > 1) + strcpy(FileName,argv[aidx]); + para++; + } else { + fprintf(stderr,"Undefined argument %s\n",argv[aidx]); + usage(argv[0]); + exit(1); + } + } + aidx++; + } while (aidx(mISDN.device = mISDN_open())) { + printf("TestmISDN cannot open mISDN due to %s\n", + strerror(errno)); + return(1); + } + sprintf(FileNameOut,"%s.out",FileName); + sprintf(FileName,"%s.in",FileName); + if (0>(mISDN.save = open(FileName, O_WRONLY|O_CREAT|O_TRUNC,S_IRWXU))) { + printf("TestmISDN cannot open %s due to %s\n",FileName, + strerror(errno)); + close(mISDN.device); + return(1); + } + if (0>(mISDN.play = open(FileNameOut, O_RDONLY))) { + printf("TestmISDN cannot open %s due to %s\n",FileNameOut, + strerror(errno)); + mISDN.play = -1; + } else + mISDN.fplay = fdopen(mISDN.play, "r"); + if (VerifyOn>8) + fprintf(stdout,"fileno %d/%d/%d\n",mISDN.save, mISDN.play, + mISDN.device); + err = do_setup(&mISDN); + if (err) + fprintf(stdout,"do_setup error %d\n", err); + else + do_connection(&mISDN); + close(mISDN.save); + if (mISDN.play>=0) + close(mISDN.play); + err=mISDN_close(mISDN.device); + if (err) + fprintf(stdout,"mISDN_close: error(%d): %s\n", err, + strerror(err)); + + return(0); +} diff --git a/example/testnet.c b/example/testnet.c new file mode 100644 index 0000000..46f0ed4 --- /dev/null +++ b/example/testnet.c @@ -0,0 +1,963 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mISDNlib.h" +#include "l3dss1.h" + + +unsigned char ulaw_to_Alaw[256] = { + 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, + 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, + 0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, + 0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, + 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, 0x02, + 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, 0x1a, + 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, 0x12, + 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x6b, + 0x68, 0x69, 0x6e, 0x6f, 0x6c, 0x6d, 0x62, 0x63, + 0x60, 0x61, 0x66, 0x67, 0x64, 0x65, 0x7b, 0x79, + 0x7e, 0x7f, 0x7c, 0x7d, 0x72, 0x73, 0x70, 0x71, + 0x76, 0x77, 0x74, 0x75, 0x4b, 0x49, 0x4f, 0x4d, + 0x42, 0x43, 0x40, 0x41, 0x46, 0x47, 0x44, 0x45, + 0x5a, 0x5b, 0x58, 0x59, 0x5e, 0x5f, 0x5c, 0x5d, + 0x52, 0x52, 0x53, 0x53, 0x50, 0x50, 0x51, 0x51, + 0x56, 0x56, 0x57, 0x57, 0x54, 0x54, 0x55, 0xd5, + 0xaa, 0xab, 0xa8, 0xa9, 0xae, 0xaf, 0xac, 0xad, + 0xa2, 0xa3, 0xa0, 0xa1, 0xa6, 0xa7, 0xa4, 0xa5, + 0xba, 0xbb, 0xb8, 0xb9, 0xbe, 0xbf, 0xbc, 0xbd, + 0xb2, 0xb3, 0xb0, 0xb1, 0xb6, 0xb7, 0xb4, 0xb5, + 0x8b, 0x88, 0x89, 0x8e, 0x8f, 0x8c, 0x8d, 0x82, + 0x83, 0x80, 0x81, 0x86, 0x87, 0x84, 0x85, 0x9a, + 0x9b, 0x98, 0x99, 0x9e, 0x9f, 0x9c, 0x9d, 0x92, + 0x93, 0x90, 0x91, 0x96, 0x97, 0x94, 0x95, 0xeb, + 0xe8, 0xe9, 0xee, 0xef, 0xec, 0xed, 0xe2, 0xe3, + 0xe0, 0xe1, 0xe6, 0xe7, 0xe4, 0xe5, 0xfb, 0xf9, + 0xfe, 0xff, 0xfc, 0xfd, 0xf2, 0xf3, 0xf0, 0xf1, + 0xf6, 0xf7, 0xf4, 0xf5, 0xcb, 0xc9, 0xcf, 0xcd, + 0xc2, 0xc3, 0xc0, 0xc1, 0xc6, 0xc7, 0xc4, 0xc5, + 0xda, 0xdb, 0xd8, 0xd9, 0xde, 0xdf, 0xdc, 0xdd, + 0xd2, 0xd2, 0xd3, 0xd3, 0xd0, 0xd0, 0xd1, 0xd1, + 0xd6, 0xd6, 0xd7, 0xd7, 0xd4, 0xd4, 0xd5, 0xd5, + +}; + +unsigned char Alaw_to_ulaw[256] = { + 0x29, 0x2a, 0x27, 0x28, 0x2d, 0x2e, 0x2b, 0x2c, + 0x21, 0x22, 0x1f, 0x20, 0x25, 0x26, 0x23, 0x24, + 0x39, 0x3a, 0x37, 0x38, 0x3d, 0x3e, 0x3b, 0x3c, + 0x31, 0x32, 0x2f, 0x30, 0x35, 0x36, 0x33, 0x34, + 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, + 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, + 0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, + 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, + 0x62, 0x63, 0x60, 0x61, 0x66, 0x67, 0x64, 0x65, + 0x5d, 0x5d, 0x5c, 0x5c, 0x5f, 0x5f, 0x5e, 0x5e, + 0x74, 0x76, 0x70, 0x72, 0x7c, 0x7e, 0x78, 0x7a, + 0x6a, 0x6b, 0x68, 0x69, 0x6e, 0x6f, 0x6c, 0x6d, + 0x48, 0x49, 0x46, 0x47, 0x4c, 0x4d, 0x4a, 0x4b, + 0x40, 0x41, 0x3f, 0x3f, 0x44, 0x45, 0x42, 0x43, + 0x56, 0x57, 0x54, 0x55, 0x5a, 0x5b, 0x58, 0x59, + 0x4f, 0x4f, 0x4e, 0x4e, 0x52, 0x53, 0x50, 0x51, + 0xa9, 0xaa, 0xa7, 0xa8, 0xad, 0xae, 0xab, 0xac, + 0xa1, 0xa2, 0x9f, 0xa0, 0xa5, 0xa6, 0xa3, 0xa4, + 0xb9, 0xba, 0xb7, 0xb8, 0xbd, 0xbe, 0xbb, 0xbc, + 0xb1, 0xb2, 0xaf, 0xb0, 0xb5, 0xb6, 0xb3, 0xb4, + 0x8a, 0x8b, 0x88, 0x89, 0x8e, 0x8f, 0x8c, 0x8d, + 0x82, 0x83, 0x80, 0x81, 0x86, 0x87, 0x84, 0x85, + 0x9a, 0x9b, 0x98, 0x99, 0x9e, 0x9f, 0x9c, 0x9d, + 0x92, 0x93, 0x90, 0x91, 0x96, 0x97, 0x94, 0x95, + 0xe2, 0xe3, 0xe0, 0xe1, 0xe6, 0xe7, 0xe4, 0xe5, + 0xdd, 0xdd, 0xdc, 0xdc, 0xdf, 0xdf, 0xde, 0xde, + 0xf4, 0xf6, 0xf0, 0xf2, 0xfc, 0xfe, 0xf8, 0xfa, + 0xea, 0xeb, 0xe8, 0xe9, 0xee, 0xef, 0xec, 0xed, + 0xc8, 0xc9, 0xc6, 0xc7, 0xcc, 0xcd, 0xca, 0xcb, + 0xc0, 0xc1, 0xbf, 0xbf, 0xc4, 0xc5, 0xc2, 0xc3, + 0xd6, 0xd7, 0xd4, 0xd5, 0xda, 0xdb, 0xd8, 0xd9, + 0xcf, 0xcf, 0xce, 0xce, 0xd2, 0xd3, 0xd0, 0xd1, + +}; + +void usage(pname) +char *pname; +{ + fprintf(stderr,"Call with %s [options] [filename]\n",pname); + fprintf(stderr,"\n"); + fprintf(stderr," filename filename.in incoming data\n"); + fprintf(stderr," filename.out outgoing data\n"); + fprintf(stderr," data is sun audio 8khz 8bi for voice\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"\n Valid options are:\n"); + fprintf(stderr,"\n"); + fprintf(stderr," -? Usage ; printout this information\n"); + fprintf(stderr," -c use card number n (default 1)\n"); + fprintf(stderr," -d Display text (default \"Test Display\")\n"); + fprintf(stderr," -m Called PN (default 789)\n"); + fprintf(stderr," -n Calling PN (default keine)\n"); + fprintf(stderr," -F use function n (default 0)\n"); + fprintf(stderr," 0 outgoing call\n"); + fprintf(stderr," 1 incomming call\n"); + fprintf(stderr," -vn Printing debug info level n\n"); + fprintf(stderr,"\n"); +} + +typedef struct _devinfo { + int device; + int cardnr; + int func; + char phonenr[32]; + char display[32]; + char msn[32]; + int d_stid; + int layer1; + int layer2; + int layer3; + int b_stid[2]; + int b_adress[2]; + int used_bchannel; + int save; + int play; + FILE *fplay; + int flag; + int val; + int cr; + int si; + int bl1_prot; + int bl2_prot; + int bl3_prot; +} devinfo_t; + +#define FLG_SEND_TONE 0x0001 +#define FLG_SEND_DATA 0x0002 +#define FLG_BCHANNEL_SETUP 0x0010 +#define FLG_BCHANNEL_DOACTIVE 0x0020 +#define FLG_BCHANNEL_ACTIVE 0x0040 +#define FLG_BCHANNEL_ACTDELAYED 0x0080 +#define FLG_CALL_ORGINATE 0x0100 + + +#define MAX_REC_BUF 4000 +#define MAX_DATA_BUF 1024 + +static int VerifyOn=0; + +char tt_char[]="0123456789ABCD*#"; + +#define PLAY_SIZE 64 + +#define MsgHead(ptr, cref, mty) \ + *ptr++ = 0x8; \ + if (cref == -1) { \ + *ptr++ = 0x0; \ + } else { \ + *ptr++ = 0x1; \ + *ptr++ = cref^0x80; \ + } \ + *ptr++ = mty + +int save_alaw(devinfo_t *di, unsigned char *buf, int len) { + int i; + unsigned char *p = buf; + + for (i=0; isave, buf, len); + return(len); +} + +int play_msg(devinfo_t *di) { + unsigned char buf[PLAY_SIZE+mISDN_HEADER_LEN], *p; + iframe_t *frm = (iframe_t *)buf; + int len, ret, i; + + if (di->play<0) + return(0); + len = read(di->play, buf + mISDN_HEADER_LEN, PLAY_SIZE); + if (len<0) { + printf("play_msg err %d: \"%s\"\n", errno, strerror(errno)); + close(di->play); + di->play = -1; + } + p = buf + mISDN_HEADER_LEN; + for (i=0; iaddr = di->b_adress[di->used_bchannel] | IF_DOWN; + frm->prim = DL_DATA | REQUEST; + frm->dinfo = 0; + frm->len = len; + ret = mISDN_write(di->device, buf, len+mISDN_HEADER_LEN, 8000); + if (ret < 0) + fprintf(stdout,"play write error %d %s\n", errno, strerror(errno)); + else if (VerifyOn>3) + fprintf(stdout,"play write ret=%d\n", ret); + return(ret); +} + +int send_data(devinfo_t *di) { + unsigned char buf[MAX_DATA_BUF+mISDN_HEADER_LEN]; + iframe_t *frm = (iframe_t *)buf; + unsigned char *data; + int len, ret; + + if (di->play<0 || !di->fplay) + return(0); + if (!(data = fgets(buf + mISDN_HEADER_LEN, MAX_DATA_BUF, di->fplay))) { + close(di->play); + di->play = -1; + data = buf + mISDN_HEADER_LEN; + data[0] = 4; /* ctrl-D */ + data[1] = 0; + } + len = strlen(data); + if (len==0) { + close(di->play); + di->play = -1; + data[0] = 4; /* ctrl-D */ + len = 1; + } + + frm->addr = di->b_adress[di->used_bchannel] | IF_DOWN; + frm->prim = DL_DATA | REQUEST; + frm->dinfo = 0; + frm->len = len; + ret = mISDN_write(di->device, buf, len+mISDN_HEADER_LEN, 100000); + if (ret < 0) + fprintf(stdout,"send_data write error %d %s\n", errno, strerror(errno)); + else if (VerifyOn>3) + fprintf(stdout,"send_data write ret=%d\n", ret); + return(ret); +} + +int setup_bchannel(devinfo_t *di) { + mISDN_pid_t pid; + int ret; + layer_info_t li; + + + if ((di->used_bchannel<0) || (di->used_bchannel>1)) { + fprintf(stdout, "wrong channel %d\n", di->used_bchannel); + return(0); + } + memset(&li, 0, sizeof(layer_info_t)); + strcpy(&li.name[0], "B L3"); + li.object_id = -1; + li.extentions = 0; + li.pid.protocol[3] = di->bl3_prot; + li.pid.layermask = ISDN_LAYER(3); + li.st = di->b_stid[di->used_bchannel]; + ret = mISDN_new_layer(di->device, &li); + if (ret<0) { + fprintf(stdout, "new_layer ret(%d)\n", ret); + return(0); + } + if (ret) { + di->b_adress[di->used_bchannel] = ret; + if (VerifyOn>2) + fprintf(stdout,"b_adress%d %08x\n", + di->used_bchannel+1, ret); + memset(&pid, 0, sizeof(mISDN_pid_t)); + pid.protocol[1] = di->bl1_prot; + pid.protocol[2] = di->bl2_prot; + pid.protocol[3] = di->bl3_prot; + pid.layermask = ISDN_LAYER(1) | ISDN_LAYER(2)| ISDN_LAYER(3); + if (di->flag & FLG_CALL_ORGINATE) + pid.global = 1; + ret = mISDN_set_stack(di->device, + di->b_stid[di->used_bchannel], &pid); + if (ret) { + fprintf(stdout, "set_stack ret(%d)\n", ret); + return(0); + } + ret = di->b_adress[di->used_bchannel]; + } + return(ret); +} + +int send_SETUP(devinfo_t *di, int SI, char *PNr) { + unsigned char *np, *p, *msg, buf[1024]; + int len, ret; + + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_SETUP); + *p++ = 0xa1; /* complete indicator */ + *p++ = IE_BEARER; + if (SI == 1) { /* Audio */ + *p++ = 0x3; /* Length */ + *p++ = 0x90; /* Coding Std. CCITT, 3.1 kHz audio */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + *p++ = 0xa3; /* A-Law Audio */ + } else { /* default Datatransmission 64k */ + *p++ = 0x2; /* Length */ + *p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inf */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + } + *p++ = IE_CHANNEL_ID; + *p++ = 0x1; /* Length */ + *p++ = 0x80 | (1 + di->used_bchannel); + if (strlen(di->display)) { + *p++ = IE_DISPLAY; + *p++ = strlen(di->display); + np = di->display; + while(*np) + *p++ = *np++ & 0x7f; + } + if (strlen(di->msn)) { + *p++ = IE_CALLING_PN; + *p++ = strlen(PNr) +2; + *p++ = 0x01; + *p++ = 0x80; + np = PNr; + while(*np) + *p++ = *np++ & 0x7f; + } + if (PNr && strlen(PNr)) { + *p++ = IE_CALLED_PN; + np = di->msn; + *p++ = strlen(np) + 1; + /* Classify as AnyPref. */ + *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */ + while (*np) + *p++ = *np++ & 0x7f; + } + len = p - msg; + ret = mISDN_write_frame(di->device, buf, di->layer3 | IF_DOWN, + DL_UNITDATA | REQUEST, 0, len, msg, TIMEOUT_1SEC); + return(ret); +} + +int activate_bchan(devinfo_t *di) { + unsigned char buf[128]; + iframe_t *rfrm; + int ret; + + ret = mISDN_write_frame(di->device, buf, + di->b_adress[di->used_bchannel] | IF_DOWN, + DL_ESTABLISH | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"DL_ESTABLISH write ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 128, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"DL_ESTABLISH read ret=%d\n", ret); + rfrm = (iframe_t *)buf; + if (ret>0) { + if (rfrm->prim == (DL_ESTABLISH | CONFIRM)) { + di->flag |= FLG_BCHANNEL_ACTIVE; + } + } + return(ret); +} + +int deactivate_bchan(devinfo_t *di) { + unsigned char buf[128]; + int ret; + + ret = mISDN_write_frame(di->device, buf, + di->b_adress[di->used_bchannel] | IF_DOWN, + DL_RELEASE | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"DL_RELEASE write ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 128, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"DL_RELEASE read ret=%d\n", ret); + di->flag &= ~FLG_BCHANNEL_ACTIVE; + di->flag &= ~FLG_BCHANNEL_SETUP; + ret = mISDN_clear_stack(di->device, di->b_stid[di->used_bchannel]); + if (VerifyOn>3) + fprintf(stdout,"clear_stack ret=%d\n", ret); + return(ret); +} + +int send_touchtone(devinfo_t *di, int tone) { + iframe_t frm; + int tval, ret; + + if (VerifyOn>1) + fprintf(stdout,"send_touchtone %c\n", DTMF_TONE_MASK & tone); + tval = DTMF_TONE_VAL | tone; + ret = mISDN_write_frame(di->device, &frm, + di->b_adress[di->used_bchannel] | IF_DOWN, + PH_CONTROL | REQUEST, 0, 4, &tval, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"tt send ret=%d\n", ret); + return(ret); +} + +int read_mutiplexer(devinfo_t *di) { + unsigned char *p, *msg, buf[MAX_REC_BUF]; + iframe_t *rfrm; + int timeout = TIMEOUT_10SEC; + int ret = 0; + int len; + + rfrm = (iframe_t *)buf; + /* Main loop */ + +start_again: + while ((ret = mISDN_read(di->device, buf, MAX_REC_BUF, timeout))) { + if (VerifyOn>3) + fprintf(stdout,"readloop ret=%d\n", ret); + if (ret >= 16) { + if (VerifyOn>4) + fprintf(stdout,"readloop addr(%x) prim(%x) len(%d)\n", + rfrm->addr, rfrm->prim, rfrm->len); + if (rfrm->addr == (di->b_adress[di->used_bchannel] | IF_DOWN)) { + /* B-Channel related messages */ + if (rfrm->prim == (DL_DATA | INDICATION)) { + /* received data, save it */ + save_alaw(di, (unsigned char *)&rfrm->data.i, + rfrm->len); + } else if (rfrm->prim == (DL_DATA | CONFIRM)) { + /* get ACK of send data, so we can + * send more + */ + if (VerifyOn>5) + fprintf(stdout,"DL_DATA_CNF\n"); + switch (di->func) { + case 0: + case 1: + if (di->play > -1) + play_msg(di); + break; + } + } else if (rfrm->prim == (PH_CONTROL | INDICATION)) { + if ((rfrm->len == 4) && + ((rfrm->data.i & ~DTMF_TONE_MASK) + == DTMF_TONE_VAL)) { + fprintf(stdout,"GOT TT %c\n", + DTMF_TONE_MASK & rfrm->data.i); + } else + fprintf(stdout,"unknown PH_CONTROL len %d/val %x\n", + rfrm->len, rfrm->data.i); + } + /* D-Channel related messages */ + } else if ((ret > 19) && (buf[19] == MT_CONNECT) && + (di->flag & FLG_CALL_ORGINATE)) { + /* We got connect, so bring B-channel up */ + if (!(di->flag & FLG_BCHANNEL_ACTDELAYED)) + activate_bchan(di); + else + di->flag |= FLG_BCHANNEL_DOACTIVE; + /* send a CONNECT_ACKNOWLEDGE */ + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_CONNECT_ACKNOWLEDGE); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + /* if here is outgoing data, send first part */ + switch (di->func) { + case 0: + case 1: + if (di->play > -1) + play_msg(di); + break; + } + } else if ((ret > 19) && (buf[19] == MT_CONNECT_ACKNOWLEDGE) && + (!(di->flag & FLG_CALL_ORGINATE))) { + /* We got connect ack, so bring B-channel up */ + if (!(di->flag & FLG_BCHANNEL_ACTDELAYED)) + activate_bchan(di); + else + di->flag |= FLG_BCHANNEL_DOACTIVE; + /* if here is outgoing data, send first part */ + switch (di->func) { + case 0: + case 1: + if (di->play > -1) + play_msg(di); + break; + } + } else if ((ret > 19) && (buf[19] == MT_DISCONNECT)) { + /* send a RELEASE */ + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_RELEASE); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + } else if ((ret > 19) && (buf[19] == MT_RELEASE)) { + /* on a disconnecting msg leave loop */ + /* send a RELEASE_COMPLETE */ + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_RELEASE_COMPLETE); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + return(2); + } else if ((ret > 19) && (buf[19] == MT_RELEASE_COMPLETE)) { + /* on a disconnecting msg leave loop */ + return(1); + } + } + } + if (di->flag & FLG_SEND_TONE) { + if (di->val) { + di->val--; + send_touchtone(di, tt_char[di->val]); + } else { + /* After last tone disconnect */ + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_DISCONNECT); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + di->flag &= ~FLG_SEND_TONE; + } + goto start_again; + } else if (di->flag & FLG_SEND_DATA) { + if (di->play > -1) + send_data(di); + else + di->flag &= ~FLG_SEND_DATA; + goto start_again; + } else if (di->flag & FLG_BCHANNEL_DOACTIVE) { + ret = activate_bchan(di); + if (!ret) { + fprintf(stdout,"error on activate_bchan\n"); + return(0); + } + di->flag &= ~FLG_BCHANNEL_DOACTIVE; + /* send next after 1 sec */ + timeout = 1*TIMEOUT_1SEC; + di->flag |= FLG_SEND_DATA; + goto start_again; + } + return(0); +} + +int do_connection(devinfo_t *di) { + unsigned char *p, *msg, buf[1024]; + iframe_t *rfrm; + int len, idx, ret = 0; + int bchannel; + time_t tim; + struct tm *ts; + + rfrm = (iframe_t *)buf; + + if (di->func == 0) { + di->flag |= FLG_CALL_ORGINATE; + di->cr = 0x81; + send_SETUP(di, di->si, di->phonenr); + } + bchannel= di->used_bchannel + 1; + /* Wait for a SETUP message or a CALL_PROCEEDING */ + while ((ret = mISDN_read(di->device, buf, 1024, 3*TIMEOUT_10SEC))) { + if (VerifyOn>3) + fprintf(stdout,"readloop ret=%d\n", ret); + if (ret >= 20) { + if (((!(di->flag & FLG_CALL_ORGINATE)) && + (buf[19] == MT_SETUP)) || + ((di->flag & FLG_CALL_ORGINATE) && + (buf[19] == MT_ALERTING))) { + + if (!(di->flag & FLG_CALL_ORGINATE)) + di->cr = buf[18]; + idx = 20; + break; + } + } + } + fprintf(stdout,"bchannel %d\n", bchannel); + if (bchannel > 0) { + /* setup a B-channel stack */ + switch (di->func) { + case 0: + case 1: + case 2: + case 3: + case 4: + ret = setup_bchannel(di); + if (ret) + di->flag |= FLG_BCHANNEL_SETUP; + else { + fprintf(stdout,"error on setup_bchannel\n"); + goto clean_up; + } + break; + } + if (!(di->flag & FLG_CALL_ORGINATE)) { + p = msg = buf + mISDN_HEADER_LEN; + MsgHead(p, di->cr, MT_CONNECT); + time(&tim); + ts = localtime(&tim); + if (ts->tm_year > 99) + ts->tm_year -=100; + printf("Time %d:%d %d/%d/%d\n", + ts->tm_hour, ts->tm_min, + ts->tm_mday, ts->tm_mon+1, ts->tm_year); + *p++ = IE_CHANNEL_ID; + *p++ = 0x1; /* Length */ + *p++ = 0x80 | (1 + di->used_bchannel); + *p++ = IE_DISPLAY; + *p++ = strlen(di->display); + for (idx=0; idx < strlen(di->display); idx++) + *p++ = di->display[idx]; + *p++ = IE_DATE; + *p++ = 5; + *p++ = ts->tm_year; + *p++ = ts->tm_mon+1; + *p++ = ts->tm_mday; + *p++ = ts->tm_hour; + *p++ = ts->tm_min; + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + } + if (!read_mutiplexer(di)) { /* timed out */ + /* send a RELEASE_COMPLETE */ + fprintf(stdout,"read_mutiplexer timed out sending RELEASE_COMPLETE\n"); + p = msg = buf + mISDN_HEADER_LEN;; + MsgHead(p, di->cr, MT_RELEASE_COMPLETE); + len = p - msg; + ret = mISDN_write_frame(di->device, buf, + di->layer3 | IF_DOWN, DL_DATA | REQUEST, + 0, len, msg, TIMEOUT_1SEC); + } + deactivate_bchan(di); + } else { + fprintf(stdout,"no channel or no connection\n"); + } +clean_up: + sleep(1); + ret = mISDN_write_frame(di->device, buf, di->layer3 | IF_DOWN, + DL_RELEASE | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 1024, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"read ret=%d\n", ret); + sleep(1); + ret = mISDN_write_frame(di->device, buf, di->layer3 | IF_DOWN, + MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 1024, TIMEOUT_10SEC); + if (VerifyOn>3) + fprintf(stdout,"read ret=%d\n", ret); + return(0); +} + + + +int +add_dlayer3(devinfo_t *di, int prot) +{ + layer_info_t li; + stack_info_t si; + interface_info_t ii; + int lid, ret; + + if (di->layer3) { + memset(&si, 0, sizeof(stack_info_t)); + si.extentions = EXT_STACK_CLONE; + si.mgr = -1; + si.id = di->d_stid; + ret = mISDN_new_stack(di->device, &si); + if (ret <= 0) { + fprintf(stdout, "clone stack failed ret(%d)\n", ret); + return(11); + } + di->d_stid = ret; + } + memset(&li, 0, sizeof(layer_info_t)); + strcpy(&li.name[0], "user L3"); + li.object_id = -1; + li.extentions = 0; + li.pid.protocol[3] = prot; + li.pid.layermask = ISDN_LAYER(3); + li.st = di->d_stid; + lid = mISDN_new_layer(di->device, &li); + if (lid<0) + return(12); + di->layer3 = lid; + if (!di->layer3) + return(13); + + /* + * EXT_IF_CREATE | EXT_IF_EXCLUSIV sorgen dafuer, das wenn die L3 + * Schnittstelle schon benutzt ist, eine neue L2 Instanz erzeugt + * wird + */ + + ii.extentions = EXT_IF_CREATE | EXT_IF_EXCLUSIV; + ii.owner = di->layer3; + ii.peer = di->layer2; + ii.stat = IF_DOWN; + ret = mISDN_connect(di->device, &ii); + if (ret) + return(13); + ii.owner = di->layer3; + ii.stat = IF_DOWN; + ret = mISDN_get_interface_info(di->device, &ii); + if (ret != 0) + return(14); + if (ii.peer == di->layer2) + fprintf(stdout, "Layer 2 not cloned\n"); + else + fprintf(stdout, "Layer 2 %08x cloned from %08x\n", + ii.peer, di->layer2); + di->layer2 = ii.peer; + return(0); +} + +int do_setup(devinfo_t *di) { + unsigned char buf[1024]; + iframe_t *frm = (iframe_t *)buf; + int i, ret = 0; + stack_info_t *stinf; + status_info_t *si; + + di->bl2_prot = ISDN_PID_L2_B_TRANS; + di->bl3_prot = ISDN_PID_L3_B_TRANS; + di->bl1_prot = ISDN_PID_L1_B_64TRANS; + di->si = 1; + + ret = mISDN_get_stack_count(di->device); + if (VerifyOn>1) + fprintf(stdout,"%d stacks found\n", ret); + if (ret < di->cardnr) { + fprintf(stdout,"cannot config card nr %d only %d cards\n", + di->cardnr, ret); + return(2); + } + ret = mISDN_get_stack_info(di->device, di->cardnr, buf, 1024); + if (ret<=0) { + fprintf(stdout,"cannot get stackinfo err: %d\n", ret); + return(3); + } + stinf = (stack_info_t *)&frm->data.p; + if (VerifyOn>1) + mISDNprint_stack_info(stdout, stinf); + di->d_stid = stinf->id; + for (i=0;i<2;i++) { + if (stinf->childcnt>i) + di->b_stid[i] = stinf->child[i]; + else + di->b_stid[i] = 0; + } + + di->layer1 = mISDN_get_layerid(di->device, di->d_stid, 1); + if (di->layer1<0) { + fprintf(stdout,"cannot get layer1\n"); + return(4); + } + if (VerifyOn>1) + fprintf(stdout,"layer1 id %08x\n", di->layer1); + + di->layer2 = mISDN_get_layerid(di->device, di->d_stid, 2); + if (di->layer2<0) { + fprintf(stdout,"cannot get layer2\n"); + return(5); + } + if (VerifyOn>1) + fprintf(stdout,"layer2 id %08x\n", di->layer2); + + di->layer3 = mISDN_get_layerid(di->device, di->d_stid, 3); + if (di->layer3<0) { + fprintf(stdout,"cannot get layer3\n"); + di->layer3 = 0; + } + if (VerifyOn>1) + fprintf(stdout,"layer3 id %08x\n", di->layer3); + + + ret = add_dlayer3(di, ISDN_PID_L3_DSS1NET); + if (ret) + return(ret); + ret = mISDN_write_frame(di->device, buf, di->layer3 | IF_DOWN, + DL_ESTABLISH | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"dl_etablish write ret=%d\n", ret); + ret = mISDN_read(di->device, buf, 1024, TIMEOUT_1SEC); + if (VerifyOn>3) + fprintf(stdout,"dl_etablish read ret=%d\n", ret); + if (ret>0) { + if (frm->prim != (DL_ESTABLISH | CONFIRM)) { + fprintf(stdout,"DL_ESTABLISH | REQUEST return prim:%x\n", + frm->prim); +// return(6); + } + } else { + fprintf(stdout,"DL_ESTABLISH | REQUEST return(%d)\n", ret); +// return(7); + } + ret = mISDN_get_status_info(di->device, di->layer1, buf, 1024); + if (ret > mISDN_HEADER_LEN) { + si = (status_info_t *)&frm->data.p; + mISDNprint_status(stdout, si); + } else + fprintf(stdout,"mISDN_get_status_info ret(%d)\n", ret); + ret = mISDN_get_status_info(di->device, di->layer2, buf, 1024); + if (ret > mISDN_HEADER_LEN) { + si = (status_info_t *)&frm->data.p; + mISDNprint_status(stdout, si); + } else + fprintf(stdout,"mISDN_get_status_info ret(%d)\n", ret); + sleep(1); + return(0); +} + +int main(argc,argv) +int argc; +char *argv[]; + +{ + char FileName[200],FileNameOut[200]; + int aidx=1,para=1, idx; + char sw; + devinfo_t mISDN; + int err; + + fprintf(stderr,"Test HFCNet 1.0\n"); + strcpy(FileName, "test_file"); + memset(&mISDN, 0, sizeof(mISDN)); + mISDN.cardnr = 1; + mISDN.func = 0; + strcpy(mISDN.display, "Test Display"); + strcpy(mISDN.msn, "789"); + mISDN.phonenr[0] = 0; + if (argc<1) { + fprintf(stderr,"Error: Not enough arguments please check\n"); + usage(argv[0]); + exit(1); + } else { + do { + if (argv[aidx] && argv[aidx][0]=='-') { + sw=argv[aidx][1]; + switch (sw) { + case 'v': + case 'V': + VerifyOn=1; + if (argv[aidx][2]) { + VerifyOn=atol(&argv[aidx][2]); + } + break; + case 'c': + if (argv[aidx][2]) { + mISDN.cardnr=atol(&argv[aidx][2]); + } + break; + case 'F': + if (argv[aidx][2]) { + mISDN.func=atol(&argv[aidx][2]); + } + break; + case 'd': + if (!argv[aidx][2]) { + idx = 0; + aidx++; + } else { + idx=2; + } + if (aidx<=argc) { + strcpy(mISDN.display, &argv[aidx][idx]); + } else { + fprintf(stderr," Switch %c without value\n",sw); + exit(1); + } + break; + case 'm': + if (!argv[aidx][2]) { + idx = 0; + aidx++; + } else { + idx=2; + } + if (aidx<=argc) { + strcpy(mISDN.msn, &argv[aidx][idx]); + } else { + fprintf(stderr," Switch %c without value\n",sw); + exit(1); + } + break; + case 'n': + if (!argv[aidx][2]) { + idx = 0; + aidx++; + } else { + idx=2; + } + if (aidx<=argc) { + strcpy(mISDN.phonenr, &argv[aidx][idx]); + } else { + fprintf(stderr," Switch %c without value\n",sw); + exit(1); + } + break; + case '?' : + usage(argv[0]); + exit(1); + break; + default : fprintf(stderr,"Unknown Switch %c\n",sw); + usage(argv[0]); + exit(1); + break; + } + } else { + if (para==1) { + if (argc > 1) + strcpy(FileName,argv[aidx]); + para++; + } else { + fprintf(stderr,"Undefined argument %s\n",argv[aidx]); + usage(argv[0]); + exit(1); + } + } + aidx++; + } while (aidx(mISDN.device = mISDN_open())) { + printf("TestmISDN cannot open mISDN due to %s\n", + strerror(errno)); + return(1); + } + sprintf(FileNameOut,"%s.out",FileName); + sprintf(FileName,"%s.in",FileName); + printf("TestmISDN open in %s\n",FileName); + if (0>(mISDN.save = open(FileName, O_WRONLY|O_CREAT|O_TRUNC,S_IRWXU))) { + printf("TestmISDN cannot open %s due to %s\n",FileName, + strerror(errno)); + close(mISDN.device); + return(1); + } + printf("TestmISDN open out %s\n",FileNameOut); + if (0>(mISDN.play = open(FileNameOut, O_RDONLY))) { + printf("TestmISDN cannot open %s due to %s\n",FileNameOut, + strerror(errno)); + mISDN.play = -1; + } else + mISDN.fplay = fdopen(mISDN.play, "r"); + printf("TestmISDN files open\n"); + if (VerifyOn>8) + fprintf(stdout,"fileno %d/%d/%d\n",mISDN.save, mISDN.play, + mISDN.device); + err = do_setup(&mISDN); + if (err) + fprintf(stdout,"do_setup error %d\n", err); + else + do_connection(&mISDN); + close(mISDN.save); + if (mISDN.play>=0) + close(mISDN.play); + err=mISDN_close(mISDN.device); + if (err) + fprintf(stdout,"mISDN_close: error(%d): %s\n", err, + strerror(err)); + + return(0); +} diff --git a/i4lnet/Makefile b/i4lnet/Makefile new file mode 100644 index 0000000..30ade46 --- /dev/null +++ b/i4lnet/Makefile @@ -0,0 +1,39 @@ + +all: libisdnnet.a + +ISDNNETOBJ = net_if.o isdn_debug.o isdn_msg.o fsm.o net_l2.o tei.o net_l3.o \ + manager.o tone.o bchannel.o g711.o + +libisdnnet.a: $(ISDNNETOBJ) + rm -f $@ + ar cr $@ $(ISDNNETOBJ) + +isdn_msg.o : isdn_msg.c $(INCLUDEDIR)/isdn_msg.h $(INCLUDEDIR)/isdn_net.h + +isdn_debug.o : isdn_debug.c $(INCLUDEDIR)/isdn_debug.h + +net_l2.o : net_l2.c net_l2.h $(INCLUDEDIR)/isdn_net.h fsm.h + +fsm.o : fsm.c fsm.h $(INCLUDEDIR)/isdn_net.h + +tei.o : tei.c net_l2.h $(INCLUDEDIR)/isdn_net.h + +net_l3.o : net_l3.c $(INCLUDEDIR)/isdn_net.h net_l3.h + +manager.o : manager.c $(INCLUDEDIR)/isdn_net.h $(INCLUDEDIR)/bchannel.h + +net_if.o : net_if.c $(INCLUDEDIR)/isdn_net.h + +tone.o: tone.c $(INCLUDEDIR)/tone.h $(INCLUDEDIR)/bchannel.h \ + $(INCLUDEDIR)/isdn_net.h $(INCLUDEDIR)/ibuffer.h + +bchannel.o: bchannel.c $(INCLUDEDIR)/isdn_net.h $(INCLUDEDIR)/tone.h \ + $(INCLUDEDIR)/bchannel.h net_l3.h $(INCLUDEDIR)/ibuffer.h + +g711.o: g711.c $(INCLUDEDIR)/g711.h + +clean: + rm -f *.o *~ DEADJOE + +distclean: clean + rm -f *.a diff --git a/i4lnet/bchannel.c b/i4lnet/bchannel.c new file mode 100644 index 0000000..1f8fee1 --- /dev/null +++ b/i4lnet/bchannel.c @@ -0,0 +1,1379 @@ +#include +#include +#include +#include "isdn_net.h" +#include "helper.h" +#include "tone.h" +#include "bchannel.h" +#include "net_l3.h" +#include "l3dss1.h" + +static int +open_record_files(bchannel_t *bc) +{ + int ret = -EINVAL; + + if (bc->manager->application) + ret = bc->manager->application(bc->manager, + PR_APP_OPEN_RECFILES, bc); + return(ret); +} + +static int +close_record_files(bchannel_t *bc) +{ + int ret = -EINVAL; + + if (bc->manager->application) + ret = bc->manager->application(bc->manager, + PR_APP_CLOSE_RECFILES, bc); + return(ret); +} + +static int +setup_bchannel(bchannel_t *bc) { + struct { + int id; + mISDN_pid_t pid; + } para; + + if ((bc->channel<1) || (bc->channel>2)) { + eprint("wrong channel %d\n", bc->channel); + return(-EINVAL); + } + dprint(DBGM_BC,"%s:ch%d bst(%d)\n", __FUNCTION__, + bc->channel, bc->bstate); + if ((bc->bstate != BC_BSTATE_NULL) && + (bc->bstate != BC_BSTATE_CLEANUP)) + return(-EBUSY); + memset(¶.pid, 0, sizeof(mISDN_pid_t)); + para.pid.protocol[1] = bc->l1_prot; + if (FLG_BC_RAWDEVICE & bc->Flags) { + para.pid.protocol[2] = ISDN_PID_L2_B_RAWDEV; + para.pid.protocol[3] = ISDN_PID_L3_B_USER; + para.pid.layermask = ISDN_LAYER(1) | ISDN_LAYER(2) | + ISDN_LAYER(3); + } else { + para.pid.protocol[2] = ISDN_PID_L2_B_USER; + para.pid.layermask = ISDN_LAYER(1) | ISDN_LAYER(2); + } + if (bc->Flags & FLG_BC_CALL_ORGINATE) + para.pid.global = 1; + para.id = bc->l3id; + bc->bstate = BC_BSTATE_SETUP; + if (!bc->sbuf) { + bc->sbuf = init_ibuffer(2048); + if (bc->sbuf) { + bc->sbuf->rsem = &bc->work; + bc->sbuf->wsem = &bc->work; + } + } + if_link(bc->manager->nst, (ifunc_t)bc->manager->man2stack, + BC_SETUP | REQUEST, bc->channel, sizeof(para), ¶, 0); + return(0); +} + +static int +activate_bchannel(bchannel_t *bc) +{ + dprint(DBGM_BC,"%s:ch%d bst(%d)\n", __FUNCTION__, + bc->channel, bc->bstate); + if (!bc->b_addr) { + wprint("%s:ch%d not setup\n", __FUNCTION__, + bc->channel); + return(-EINVAL); + } + if ((bc->bstate == BC_BSTATE_SETUP) || + (bc->bstate == BC_BSTATE_DEACTIVATE)) { + bc->bstate = BC_BSTATE_ACTIVATE; + return(if_link(bc->manager->nst, + (ifunc_t)bc->manager->man2stack, + PH_ACTIVATE | REQUEST, bc->b_addr | IF_DOWN, + 0, NULL, 0)); + } else + return(-EBUSY); +} + +static int +deactivate_bchannel(bchannel_t *bc) +{ + dprint(DBGM_BC,"%s:ch%d bst(%d)\n", __FUNCTION__, + bc->channel, bc->bstate); + if (!bc->b_addr) { + wprint("%s:ch%d not setup\n", __FUNCTION__, + bc->channel); + return(-EINVAL); + } + if ((bc->bstate == BC_BSTATE_ACTIVATE) || + (bc->bstate == BC_BSTATE_ACTIV)) { + bc->bstate = BC_BSTATE_DEACTIVATE; + return(if_link(bc->manager->nst, + (ifunc_t)bc->manager->man2stack, + PH_DEACTIVATE | REQUEST, bc->b_addr | IF_DOWN, + 0, NULL, 0)); + } else + return(-EBUSY); +} + +static int +bc_cleanup(bchannel_t *bc) +{ + int ret; + + dprint(DBGM_BC,"%s:ch%d bst(%d)\n", __FUNCTION__, + bc->channel, bc->bstate); + if (!bc->b_addr) { + wprint("%s:ch%d not setup\n", __FUNCTION__, + bc->channel); + } + if (!bc->l3id) { + wprint("%s:ch%d no l3id\n", __FUNCTION__, + bc->channel); + return(-EINVAL); + } + if ((bc->bstate == BC_BSTATE_DEACTIVATE) || + (bc->bstate == BC_BSTATE_SETUP)) { + bc->bstate = BC_BSTATE_CLEANUP; + ret = if_link(bc->manager->nst, + (ifunc_t)bc->manager->man2stack, BC_CLEANUP | REQUEST, + bc->l3id, 0, NULL, 0); + } else + ret = EBUSY; + return(ret); +} + +static int +clear_bc(bchannel_t *bc) +{ + pthread_mutex_lock(&bc->lock); + free_ibuffer(bc->sbuf); + bc->sbuf = NULL; + free_ibuffer(bc->rbuf); + bc->rbuf = NULL; + if (bc->Flags & FLG_BC_RECORDING) + close_record_files(bc); + bc->Flags = 0; + bc->nr[0] = 0; + bc->msn[0] = 0; + bc->display[0] = 0; + bc->usednr = NULL; + bc->smsg = NULL; + pthread_mutex_unlock(&bc->lock); + if ((bc->bstate == BC_BSTATE_ACTIV) || + (bc->bstate == BC_BSTATE_ACTIVATE)) + deactivate_bchannel(bc); + return(0); +} + +static int +do_b_activated(bchannel_t *bc, mISDN_head_t *hh, msg_t *msg) { + dprint(DBGM_BC,"%s:ch%d state(%d/%d) Flags(%x) smsg(%p)\n", __FUNCTION__, + bc->channel, bc->cstate, bc->bstate, bc->Flags, bc->smsg); + clear_ibuffer(bc->rbuf); + if (!(bc->Flags & FLG_BC_KEEP_SEND)) + clear_ibuffer(bc->sbuf); + if (bc->sbuf && bc->sbuf->wsem) + sem_post(bc->sbuf->wsem); + if (bc->bstate == BC_BSTATE_ACTIVATE) + bc->bstate = BC_BSTATE_ACTIV; + free_msg(msg); + return(0); +} + +static int +do_b_deactivated(bchannel_t *bc, mISDN_head_t *hh, msg_t *msg) { + dprint(DBGM_BC,"%s:ch%d Flags(%x) smsg(%p)\n", __FUNCTION__, + bc->channel, bc->Flags, bc->smsg); + bc_cleanup(bc); + free_msg(msg); + return(0); +} + +static int +do_b_setup_conf(bchannel_t *bc, mISDN_head_t *hh, msg_t *msg) +{ + int *addr; + + addr = (int *)msg->data; + bc->b_addr = *addr; + activate_bchannel(bc); + free_msg(msg); + return(0); +} + + +static int +do_b_cleanup_conf(bchannel_t *bc, mISDN_head_t *hh, msg_t *msg) +{ + dprint(DBGM_BC,"%s:ch%d bst(%d)\n", __FUNCTION__, + bc->channel, bc->bstate); + bc->b_addr = 0; + if (bc->cstate == BC_CSTATE_NULL) { + bc->l3id = 0; + bc->cstate = BC_CSTATE_NULL; + } + bc->bstate = BC_BSTATE_NULL; + free_msg(msg); + return(0); +} + + +static int +do_b_data_cnf(bchannel_t *bc, mISDN_head_t *hh, msg_t *msg) +{ + bc->smsg = NULL; + if (bc->sbuf && bc->sbuf->rsem) + sem_post(bc->sbuf->rsem); + free_msg(msg); + return(0); +} + +static int +do_b_data_ind(bchannel_t *bc, mISDN_head_t *hh, msg_t *msg) +{ + int len; + int ret = 0; + + if (bc->bstate != BC_BSTATE_ACTIV) + return(-EBUSY); + dprint(DBGM_BCDATA, "%s:ch%d get %d bytes\n", __FUNCTION__, + bc->channel, msg->len); + if (bc->rbuf) { + len = ibuf_freecount(bc->rbuf); + if (msg->len > len) + ret = -ENOSPC; + else { + ibuf_memcpy_w(bc->rbuf, msg->data, msg->len); + } + if (bc->rbuf->rsem) + sem_post(bc->rbuf->rsem); + } else + ret = -EINVAL; + dprint(DBGM_BCDATA, "%s: finish ret %d\n", __FUNCTION__, ret); + if (bc->Flags & FLG_BC_RECORD) { + if (bc->Flags & FLG_BC_RECORDING) { + write(bc->rrid, msg->data, msg->len); + } else { + if (!open_record_files(bc)) + write(bc->rrid, msg->data, msg->len); + } + } else if (bc->Flags & FLG_BC_RECORDING) { + close_record_files(bc); + } + if (!ret) + free_msg(msg); + return(ret); +} + +static int +b_send(bchannel_t *bc) +{ + int len = 0, ret = -EINVAL; + u_char *p; + + if (bc->smsg) + goto out; + if (bc->bstate != BC_BSTATE_ACTIV) + goto out; + len = ibuf_usedcount(bc->sbuf); + if (!len) + goto out; + if (len > MAX_DATA_SIZE) + len = MAX_DATA_SIZE; + dprint(DBGM_BCDATA, "%s:ch%d %d bytes\n", __FUNCTION__, bc->channel, len); + bc->smsg = prep_l3data_msg(PH_DATA | REQUEST, bc->b_addr | IF_DOWN, + 0, len, NULL); + if (!bc->smsg) { + len = -ENOMEM; + goto out; + } + p = msg_put(bc->smsg, len); + ibuf_memcpy_r(p, bc->sbuf, len); + if (bc->Flags & FLG_BC_RECORD) { + if (bc->Flags & FLG_BC_RECORDING) { + write(bc->rsid, p, len); + } else { + if (!open_record_files(bc)) + write(bc->rsid, p, len); + } + } else if (bc->Flags & FLG_BC_RECORDING) { + close_record_files(bc); + } + if (bc->manager->man2stack) + ret = bc->manager->man2stack(bc->manager->nst, bc->smsg); + if (ret) { + free_msg(bc->smsg); + bc->smsg = NULL; + len = ret; + } + if (bc->sbuf->wsem) + sem_post(bc->sbuf->wsem); +out: + return(len); +} + +/* call handling */ + +static int +add_nr(bchannel_t *bc, unsigned char *cpn) +{ + if (bc->nr[0]) { + if (*cpn>1) { + memcpy(bc->nr + bc->nr[0] + 1, cpn + 2, *cpn -1); + bc->nr[0] += *cpn -1; + } else + dprint(DBGM_BC,"%s: cpn len %d\n", __FUNCTION__, *cpn); + } else if (*cpn) + memcpy(bc->nr, cpn, *cpn + 1); + dprint(DBGM_BC,"%s: nr:%s\n", __FUNCTION__, &bc->nr[2]); + return(0); +} + +static int +send_setup_ack(bchannel_t *bc) +{ + SETUP_ACKNOWLEDGE_t *sa; + msg_t *msg; + int len, ret; + unsigned char *p; + + dprint(DBGM_BC,"%s: bc%d l3id(%x)\n", __FUNCTION__, + bc->channel, bc->l3id); + msg = prep_l3data_msg(CC_SETUP_ACKNOWLEDGE | REQUEST, bc->l3id, + sizeof(SETUP_ACKNOWLEDGE_t), 128, NULL); + if (!msg) + return(-ENOMEM); + sa = (SETUP_ACKNOWLEDGE_t *)(msg->data + mISDN_HEAD_SIZE); + pthread_mutex_lock(&bc->lock); + bc->cstate = BC_CSTATE_OVERLAP_REC; + if (!(bc->Flags & FLG_BC_SENT_CID)) { + bc->Flags |= FLG_BC_SENT_CID; + sa->CHANNEL_ID = msg_put(msg, 2); + sa->CHANNEL_ID[0] = 1; + sa->CHANNEL_ID[1] = 0x88 | bc->channel; + } + pthread_mutex_unlock(&bc->lock); + if (bc->Flags & FLG_BC_PROGRESS) { + sa->PROGRESS = p = msg_put(msg, 3);; + *p++ = 2; + *p++ = 0x80 | CAUSE_LOC_PNET_LOCUSER; + *p++ = 0x80 | PROGRESS_TONE; + setup_bchannel(bc); + } + if (bc->display[0]) { + len = strlen(bc->display); + sa->DISPLAY = p = msg_put(msg, len+1); + *p++ = len; + strcpy(p, bc->display); + bc->display[0] = 0; + } + ret = -EINVAL; + if (bc->manager->man2stack) + ret = bc->manager->man2stack(bc->manager->nst, msg); + if (ret) + free_msg(msg); + return(ret); +} + +static int +send_setup(bchannel_t *bc) +{ + SETUP_t *setup; + msg_t *msg; + int len, ret; + unsigned char *p; + + if (bc->cstate != BC_CSTATE_OCALL) { + dprint(DBGM_BC,"%s: bc%d state(%d/%d) not OCALL\n", __FUNCTION__, + bc->channel, bc->cstate, bc->bstate); + return(-EINVAL); + } + bc->l3id = 0xfff0 | bc->channel; + msg = prep_l3data_msg(CC_SETUP | REQUEST, bc->l3id, + sizeof(SETUP_t), 256, NULL); + if (!msg) + return(-ENOMEM); + setup = (SETUP_t *)(msg->data + mISDN_HEAD_SIZE); + switch (bc->l1_prot) { + case ISDN_PID_L1_B_64TRANS: + bc->bc[0] = 3; + bc->bc[1] = 0x80; + bc->bc[2] = 0x90; + bc->bc[3] = 0xa3; + break; + default: + dprint(DBGM_BC,"%s: no protocol %x\n", __FUNCTION__, + bc->l1_prot); + free_msg(msg); + return(-ENOPROTOOPT); + } + setup->BEARER = p = msg_put(msg, bc->bc[0] + 1); + memcpy(p, bc->bc, bc->bc[0] + 1); + bc->Flags |= FLG_BC_SENT_CID; + setup->CHANNEL_ID = msg_put(msg, 2); + setup->CHANNEL_ID[0] = 1; + setup->CHANNEL_ID[1] = 0x88 | bc->channel; + if (bc->display[0]) { + len = strlen(bc->display); + setup->DISPLAY = p = msg_put(msg, len+1); + *p++ = len; + strcpy(p, bc->display); + bc->display[0] = 0; + } + if (bc->nr[0]) { + setup->CALLING_PN = p = msg_put(msg, bc->nr[0] + 1); + memcpy(p, bc->nr, bc->nr[0] + 1); + } + if (bc->clisub[0]) { + setup->CALLING_SUB = p = msg_put(msg, bc->clisub[0] + 1); + memcpy(p, bc->clisub, bc->clisub[0] + 1); + bc->clisub[0] = 0; + } + if (bc->msn[0]) { + setup->CALLED_PN = p = msg_put(msg, bc->msn[0] + 1);; + memcpy(p, bc->msn, bc->msn[0] + 1); + } + if (bc->cldsub[0]) { + setup->CALLED_SUB = p = msg_put(msg, bc->cldsub[0] + 1); + memcpy(p, bc->cldsub, bc->cldsub[0] + 1); + bc->cldsub[0] = 0; + } + if (bc->fac[0]) { + setup->FACILITY = p = msg_put(msg, bc->fac[0] + 1); + memcpy(p, bc->fac, bc->fac[0] + 1); + bc->fac[0] = 0; + } + if (bc->uu[0]) { + setup->USER_USER = p = msg_put(msg, bc->uu[0] + 1); + memcpy(p, bc->uu, bc->uu[0] + 1); + bc->uu[0] = 0; + } + ret = -EINVAL; + if (bc->manager->man2stack) + ret = bc->manager->man2stack(bc->manager->nst, msg); + if (ret) + free_msg(msg); + return(ret); +} + +static int +send_proceeding(bchannel_t *bc) +{ + CALL_PROCEEDING_t *proc; + msg_t *msg; + int len, ret; + unsigned char *p; + + msg = prep_l3data_msg(CC_PROCEEDING | REQUEST, bc->l3id, + sizeof(CALL_PROCEEDING_t), 128, NULL); + if (!msg) + return(-ENOMEM); + proc = (CALL_PROCEEDING_t *)(msg->data + mISDN_HEAD_SIZE); + pthread_mutex_lock(&bc->lock); + bc->cstate = BC_CSTATE_PROCEED; + if (!(bc->Flags & FLG_BC_SENT_CID)) { + bc->Flags |= FLG_BC_SENT_CID; + proc->CHANNEL_ID = msg_put(msg, 2); + proc->CHANNEL_ID[0] = 1; + proc->CHANNEL_ID[1] = 0x88 | bc->channel; + } + pthread_mutex_unlock(&bc->lock); + if (bc->display[0]) { + len = strlen(bc->display); + proc->DISPLAY = p = msg_put(msg, len+1); + *p++ = len; + strcpy(p, bc->display); + bc->display[0] = 0; + } + ret = -EINVAL; + if (bc->manager->man2stack) + ret = bc->manager->man2stack(bc->manager->nst, msg); + if (ret) + free_msg(msg); + if (bc->manager->application) { + bc->Flags |= FLG_BC_APPLICATION; + len = bc->manager->application(bc->manager, PR_APP_ICALL, bc); + dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__, + bc->channel, len); + } + return(ret); +} + +static int +send_alert(bchannel_t *bc) +{ + ALERTING_t *at; + msg_t *msg; + int len, ret; + unsigned char *p; + + dprint(DBGM_BC, "%s: bc%d flg(%x) display(%s)\n", __FUNCTION__, + bc->channel, bc->Flags, bc->display); + msg = prep_l3data_msg(CC_ALERTING | REQUEST, bc->l3id, + sizeof(ALERTING_t), 128, NULL); + if (!msg) + return(-ENOMEM); + at = (ALERTING_t *)(msg->data + mISDN_HEAD_SIZE); + pthread_mutex_lock(&bc->lock); + bc->cstate = BC_CSTATE_PROCEED; + if (!(bc->Flags & FLG_BC_SENT_CID)) { + bc->Flags |= FLG_BC_SENT_CID; + at->CHANNEL_ID = msg_put(msg, 2); + at->CHANNEL_ID[0] = 1; + at->CHANNEL_ID[1] = 0x88 | bc->channel; + } + if (bc->Flags & FLG_BC_PROGRESS) { + bc->Flags &= ~FLG_BC_PROGRESS; + set_tone(bc, FLG_BC_TONE_ALERT); + at->PROGRESS = p = msg_put(msg, 3);; + *p++ = 2; + *p++ = 0x80 | CAUSE_LOC_PNET_LOCUSER; + *p++ = 0x80 | PROGRESS_TONE; + setup_bchannel(bc); + } + pthread_mutex_unlock(&bc->lock); + if (bc->display[0]) { + len = strlen(bc->display); + at->DISPLAY = p = msg_put(msg, len+1); + *p++ = len; + strcpy(p, bc->display); + bc->display[0] = 0; + } + if (bc->fac[0]) { + at->FACILITY = p = msg_put(msg, bc->fac[0] + 1); + memcpy(p, bc->fac, bc->fac[0] + 1); + bc->fac[0] = 0; + } + if (bc->uu[0]) { + at->USER_USER = p = msg_put(msg, bc->uu[0] + 1); + memcpy(p, bc->uu, bc->uu[0] + 1); + bc->uu[0] = 0; + } + ret = -EINVAL; + if (bc->manager->man2stack) + ret = bc->manager->man2stack(bc->manager->nst, msg); + if (ret) + free_msg(msg); + return(ret); +} + +static int +send_connect(bchannel_t *bc) +{ + CONNECT_t *conn; + time_t tim; + struct tm *ts; + msg_t *msg; + int len, ret; + unsigned char *p; + + msg = prep_l3data_msg(CC_CONNECT | REQUEST, bc->l3id, + sizeof(CONNECT_t), 128, NULL); + if (!msg) + return(-ENOMEM); + conn = (CONNECT_t *)(msg->data + mISDN_HEAD_SIZE); + pthread_mutex_lock(&bc->lock); + bc->cstate = BC_CSTATE_PROCEED; + bc->Flags &= ~FLG_BC_TONE; + if (!(bc->Flags & FLG_BC_SENT_CID)) { + bc->Flags |= FLG_BC_SENT_CID; + conn->CHANNEL_ID = msg_put(msg, 2); + conn->CHANNEL_ID[0] = 1; + conn->CHANNEL_ID[1] = 0x88 | bc->channel; + } + pthread_mutex_unlock(&bc->lock); + if (bc->display[0]) { + len = strlen(bc->display); + conn->DISPLAY = p = msg_put(msg, len+1); + *p++ = len; + strcpy(p, bc->display); + bc->display[0] = 0; + } + if (bc->fac[0]) { + conn->FACILITY = p = msg_put(msg, bc->fac[0] + 1); + memcpy(p, bc->fac, bc->fac[0] + 1); + bc->fac[0] = 0; + } + if (bc->uu[0]) { + conn->USER_USER = p = msg_put(msg, bc->uu[0] + 1); + memcpy(p, bc->uu, bc->uu[0] + 1); + bc->uu[0] = 0; + } + time(&tim); + ts = localtime(&tim); + if (ts->tm_year > 99) + ts->tm_year -=100; + conn->DATE = p = msg_put(msg, 6); + *p++ = 5; + *p++ = ts->tm_year; + *p++ = ts->tm_mon+1; + *p++ = ts->tm_mday; + *p++ = ts->tm_hour; + *p++ = ts->tm_min; + + ret = -EINVAL; + if (bc->manager->man2stack) + ret = bc->manager->man2stack(bc->manager->nst, msg); + if (ret) + free_msg(msg); + return(ret); +} + +static int +send_connect_ack(bchannel_t *bc) +{ + CONNECT_ACKNOWLEDGE_t *ca; + msg_t *msg; + int len, ret; + unsigned char *p; + + msg = prep_l3data_msg(CC_CONNECT | RESPONSE, bc->l3id, + sizeof(CONNECT_ACKNOWLEDGE_t), 128, NULL); + if (!msg) + return(-ENOMEM); + setup_bchannel(bc); + ca = (CONNECT_ACKNOWLEDGE_t *)(msg->data + mISDN_HEAD_SIZE); + pthread_mutex_lock(&bc->lock); + bc->cstate = BC_CSTATE_ACTIV; + bc->Flags &= ~FLG_BC_TONE; + if (!(bc->Flags & FLG_BC_SENT_CID)) { + bc->Flags |= FLG_BC_SENT_CID; + ca->CHANNEL_ID = msg_put(msg, 2); + ca->CHANNEL_ID[0] = 1; + ca->CHANNEL_ID[1] = 0x88 | bc->channel; + } + pthread_mutex_unlock(&bc->lock); + if (bc->display[0]) { + len = strlen(bc->display); + ca->DISPLAY = p = msg_put(msg, len+1); + *p++ = len; + strcpy(p, bc->display); + bc->display[0] = 0; + } + ret = -EINVAL; + if (bc->manager->man2stack) + ret = bc->manager->man2stack(bc->manager->nst, msg); + if (ret) + free_msg(msg); + return(ret); +} + +static int +send_disc(bchannel_t *bc) +{ + DISCONNECT_t *disc; + msg_t *msg; + int len, ret; + unsigned char *p; + + msg = prep_l3data_msg(CC_DISCONNECT | REQUEST, bc->l3id, + sizeof(DISCONNECT_t), 128, NULL); + if (!msg) + return(-ENOMEM); + disc = (DISCONNECT_t *)(msg->data + mISDN_HEAD_SIZE); + pthread_mutex_lock(&bc->lock); + bc->cstate = BC_CSTATE_DISCONNECT; + pthread_mutex_unlock(&bc->lock); + if (bc->cause_val) { + disc->CAUSE = p = msg_put(msg, 3); + *p++ = 2; + *p++ = 0x80 | bc->cause_loc; + *p++ = 0x80 | bc->cause_val; + } + if (bc->Flags & FLG_BC_PROGRESS) { + set_tone(bc, FLG_BC_TONE_BUSY); + disc->PROGRESS = p = msg_put(msg, 3);; + *p++ = 2; + *p++ = 0x80 | CAUSE_LOC_PNET_LOCUSER; + *p++ = 0x80 | PROGRESS_TONE; + setup_bchannel(bc); + } + if (bc->display[0]) { + len = strlen(bc->display); + disc->DISPLAY = p = msg_put(msg, len+1); + *p++ = len; + strcpy(p, bc->display); + bc->display[0] = 0; + } + if (bc->fac[0]) { + disc->FACILITY = p = msg_put(msg, bc->fac[0] + 1); + memcpy(p, bc->fac, bc->fac[0] + 1); + bc->fac[0] = 0; + } + if (bc->uu[0]) { + disc->USER_USER = p = msg_put(msg, bc->uu[0] + 1); + memcpy(p, bc->uu, bc->uu[0] + 1); + bc->uu[0] = 0; + } + ret = -EINVAL; + if (bc->manager->man2stack) + ret = bc->manager->man2stack(bc->manager->nst, msg); + if (ret) + free_msg(msg); + return(ret); +} + +static int +send_facility(bchannel_t *bc) +{ + FACILITY_t *fac; + msg_t *msg; + int len, ret; + unsigned char *p; + + msg = prep_l3data_msg(CC_FACILITY | REQUEST, bc->l3id, + sizeof(FACILITY_t), 128, NULL); + if (!msg) + return(-ENOMEM); + fac = (FACILITY_t *)(msg->data + mISDN_HEAD_SIZE); + if (bc->display[0]) { + len = strlen(bc->display); + fac->DISPLAY = p = msg_put(msg, len+1); + *p++ = len; + strcpy(p, bc->display); + bc->display[0] = 0; + } + if (bc->fac[0]) { + fac->FACILITY = p = msg_put(msg, bc->fac[0] + 1); + memcpy(p, bc->fac, bc->fac[0] + 1); + bc->fac[0] = 0; + } + ret = -EINVAL; + if (bc->manager->man2stack) + ret = bc->manager->man2stack(bc->manager->nst, msg); + if (ret) + free_msg(msg); + return(ret); +} + +static int +send_userinfo(bchannel_t *bc) +{ + USER_INFORMATION_t *ui; + msg_t *msg; + int ret; + unsigned char *p; + + msg = prep_l3data_msg(CC_USER_INFORMATION | REQUEST, bc->l3id, + sizeof(USER_INFORMATION_t), 128, NULL); + if (!msg) + return(-ENOMEM); + ui = (USER_INFORMATION_t *)(msg->data + mISDN_HEAD_SIZE); + if (bc->uu[0]) { + ui->USER_USER = p = msg_put(msg, bc->uu[0] + 1); + memcpy(p, bc->uu, bc->uu[0] + 1); + bc->uu[0] = 0; + } + ret = -EINVAL; + if (bc->manager->man2stack) + ret = bc->manager->man2stack(bc->manager->nst, msg); + if (ret) + free_msg(msg); + return(ret); +} + +static int +send_rel(bchannel_t *bc) +{ + RELEASE_t *rel; + msg_t *msg; + int len, ret; + unsigned char *p; + + msg = prep_l3data_msg(CC_RELEASE | REQUEST, bc->l3id, + sizeof(RELEASE_t), 128, NULL); + if (!msg) + return(-ENOMEM); + rel = (RELEASE_t *)(msg->data + mISDN_HEAD_SIZE); + pthread_mutex_lock(&bc->lock); + bc->cstate = BC_CSTATE_RELEASE; + pthread_mutex_unlock(&bc->lock); + if (bc->cause_val) { + rel->CAUSE = p = msg_put(msg, 3); + *p++ = 2; + *p++ = 0x80 | bc->cause_loc; + *p++ = 0x80 | bc->cause_val; + } + if (bc->display[0]) { + len = strlen(bc->display); + rel->DISPLAY = p = msg_put(msg, len+1); + *p++ = len; + strcpy(p, bc->display); + bc->display[0] = 0; + } + if (bc->fac[0]) { + rel->FACILITY = p = msg_put(msg, bc->fac[0] + 1); + memcpy(p, bc->fac, bc->fac[0] + 1); + bc->fac[0] = 0; + } + if (bc->uu[0]) { + rel->USER_USER = p = msg_put(msg, bc->uu[0] + 1); + memcpy(p, bc->uu, bc->uu[0] + 1); + bc->uu[0] = 0; + } + ret = -EINVAL; + if (bc->manager->man2stack) + ret = bc->manager->man2stack(bc->manager->nst, msg); + if (ret) + free_msg(msg); + return(ret); +} + +static int +send_relcomp(bchannel_t *bc, int l3id, int cause) { + RELEASE_COMPLETE_t *rc; + msg_t *msg; + int ret, len; + unsigned char *p; + + msg = prep_l3data_msg(CC_RELEASE_COMPLETE | REQUEST, l3id, + sizeof(RELEASE_COMPLETE_t), 128, NULL); + if (!msg) + return(-ENOMEM); + rc = (RELEASE_COMPLETE_t *)(msg->data + mISDN_HEAD_SIZE); + clear_bc(bc); + pthread_mutex_lock(&bc->lock); + bc->cstate = BC_CSTATE_NULL; + pthread_mutex_unlock(&bc->lock); + if (cause) { + bc->cause_loc = CAUSE_LOC_PNET_LOCUSER; + bc->cause_val = cause; + rc->CAUSE = msg_put(msg, 3); + rc->CAUSE[0] = 2; + rc->CAUSE[1] = 0x80 | CAUSE_LOC_PNET_LOCUSER; + rc->CAUSE[2] = 0x80 | cause; + } + if (bc->display[0]) { + len = strlen(bc->display); + rc->DISPLAY = p = msg_put(msg, len+1); + *p++ = len; + strcpy(p, bc->display); + bc->display[0] = 0; + } + if (bc->fac[0]) { + rc->FACILITY = p = msg_put(msg, bc->fac[0] + 1); + memcpy(p, bc->fac, bc->fac[0] + 1); + bc->fac[0] = 0; + } + if (bc->uu[0]) { + rc->USER_USER = p = msg_put(msg, bc->uu[0] + 1); + memcpy(p, bc->uu, bc->uu[0] + 1); + bc->uu[0] = 0; + } + ret = -EINVAL; + if (bc->manager->man2stack) + ret = bc->manager->man2stack(bc->manager->nst, msg); + if (ret) + free_msg(msg); + return(ret); +} + +static int +info_ind(bchannel_t *bc, void *arg) +{ + INFORMATION_t *info = arg; + int ret; + + if (info->CALLED_PN) { + set_tone(bc, FLG_BC_TONE_SILENCE); + add_nr(bc, info->CALLED_PN); + ret = match_nr(bc->manager, bc->nr, &bc->usednr); + dprint(DBGM_BC, "%s: match_nr ret(%d)\n", __FUNCTION__, + ret); + if (!ret) { + send_proceeding(bc); + } else if (ret == 2 || info->COMPLETE) { + bc->Flags |= FLG_BC_PROGRESS; + set_tone(bc, FLG_BC_TONE_BUSY); + bc->cause_loc = CAUSE_LOC_PNET_LOCUSER; + bc->cause_val = CAUSE_UNASSIGNED_NUMBER; + send_disc(bc); + } + } + return(0); +} + +static int +setup_ind(bchannel_t *bc, int l3id, void *arg) +{ + SETUP_t *setup = arg; + int cause,ret; + + if (bc->cstate != BC_CSTATE_ICALL) + return(send_relcomp(bc, l3id, CAUSE_NOTCOMPAT_STATE)); + bc->l3id = l3id; + cause = CAUSE_INCOMPATIBLE_DEST; + if (setup->BEARER) { + memcpy(bc->bc, setup->BEARER, setup->BEARER[0] +1); + if (setup->BEARER[0] == 3) { + if ((setup->BEARER[1] == 0x80) && + (setup->BEARER[2] == 0x90) && + (setup->BEARER[3] == 0xa3)) { + cause = 0; + bc->l1_prot = ISDN_PID_L1_B_64TRANS; + } + } + } else + cause = CAUSE_MANDATORY_IE_MISS; + if (cause) + return(send_relcomp(bc, bc->l3id, cause)); + if (setup->CALLING_PN) + memcpy(bc->msn, setup->CALLING_PN, setup->CALLING_PN[0] + 1); + else + bc->msn[0] = 0; + if (setup->CALLING_SUB) + memcpy(bc->clisub, setup->CALLING_SUB, + setup->CALLING_SUB[0] + 1); + else + bc->clisub[0] = 0; + if (setup->CALLED_SUB) + memcpy(bc->cldsub, setup->CALLED_SUB, + setup->CALLED_SUB[0] + 1); + else + bc->cldsub[0] = 0; + if (setup->FACILITY) + memcpy(bc->fac, setup->FACILITY, setup->FACILITY[0] + 1); + else + bc->fac[0] = 0; + if (setup->USER_USER) + memcpy(bc->uu, setup->USER_USER, setup->USER_USER[0] + 1); + else + bc->uu[0] = 0; + if (!bc->sbuf) + bc->sbuf = init_ibuffer(2048); + set_tone(bc, FLG_BC_TONE_DIAL); + if (!setup->CALLED_PN) { + bc->Flags |= FLG_BC_PROGRESS; + send_setup_ack(bc); + } else { + set_tone(bc, FLG_BC_TONE_SILENCE); + bc->Flags |= FLG_BC_PROGRESS; + add_nr(bc, setup->CALLED_PN); + ret = match_nr(bc->manager, bc->nr, &bc->usednr); + dprint(DBGM_BC, "%s: match_nr ret(%d)\n", __FUNCTION__, + ret); + if (!ret) { + send_proceeding(bc); + } else if (ret == 2 || setup->COMPLETE) { + return(send_relcomp(bc, bc->l3id, CAUSE_UNASSIGNED_NUMBER)); + } else { + send_setup_ack(bc); + } + } + return(0); +} + +static int +conn_ind(bchannel_t *bc, void *arg) +{ + CONNECT_t *conn = arg; + int ret; + + if (conn) { + if (conn->FACILITY) + memcpy(bc->fac, conn->FACILITY, conn->FACILITY[0] + 1); + else + bc->fac[0] = 0; + if (conn->USER_USER) + memcpy(bc->uu, conn->USER_USER, conn->USER_USER[0] + 1); + else + bc->uu[0] = 0; + } + if ((bc->Flags & FLG_BC_APPLICATION) && bc->manager->application) { + setup_bchannel(bc); + ret = bc->manager->application(bc->manager, PR_APP_CONNECT, bc); + dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__, + bc->channel, ret); + if (!ret) { + send_connect_ack(bc); + } + } + return(0); +} + +static int +alert_ind(bchannel_t *bc, void *arg) +{ + ALERTING_t *alert = arg; + int ret; + + pthread_mutex_lock(&bc->lock); + bc->cstate = BC_CSTATE_ALERTING; + pthread_mutex_unlock(&bc->lock); + if (alert->FACILITY) + memcpy(bc->fac, alert->FACILITY, alert->FACILITY[0] + 1); + else + bc->fac[0] = 0; + if (alert->USER_USER) + memcpy(bc->uu, alert->USER_USER, alert->USER_USER[0] + 1); + else + bc->uu[0] = 0; + if ((bc->Flags & FLG_BC_APPLICATION) && bc->manager->application) { + ret = bc->manager->application(bc->manager, PR_APP_ALERT, bc); + dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__, + bc->channel, ret); + } + return(0); +} + +static int +facility_ind(bchannel_t *bc, void *arg) +{ + FACILITY_t *fac = arg; + int ret; + + if (fac) { + if (fac->FACILITY) + memcpy(bc->fac, fac->FACILITY, fac->FACILITY[0] + 1); + else + bc->fac[0] = 0; + } + if ((bc->Flags & FLG_BC_APPLICATION) && bc->manager->application) { + ret = bc->manager->application(bc->manager, PR_APP_FACILITY, bc); + dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__, + bc->channel, ret); + } + return(0); +} + +static int +userinfo_ind(bchannel_t *bc, void *arg) +{ + USER_INFORMATION_t *ui = arg; + int ret; + + if (ui) { + if (ui->USER_USER) + memcpy(bc->uu, ui->USER_USER, ui->USER_USER[0] + 1); + else + bc->uu[0] = 0; + } + if ((bc->Flags & FLG_BC_APPLICATION) && bc->manager->application) { + ret = bc->manager->application(bc->manager, PR_APP_USERUSER, bc); + dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__, + bc->channel, ret); + } + return(0); +} + +static int +disc_ind(bchannel_t *bc, void *arg) +{ + DISCONNECT_t *disc = arg; + int cause = 0; + int ret; + + if (disc->CAUSE) { + if (disc->CAUSE[0] >1) { + dprint(DBGM_BC, "%s: loc(%d) cause(%d)\n", __FUNCTION__, + disc->CAUSE[1] & 0xf, disc->CAUSE[2] & 0x7f); + bc->cause_loc = disc->CAUSE[1] & 0xf; + bc->cause_val = disc->CAUSE[2] & 0x7f; + } else { + dprint(DBGM_BC, "%s: cause len %d\n", __FUNCTION__, + disc->CAUSE[0]); + cause = CAUSE_INVALID_CONTENTS; + } + } else { + cause = CAUSE_MANDATORY_IE_MISS; + } + if (cause) { + bc->cause_loc = CAUSE_LOC_PNET_LOCUSER; + bc->cause_val = cause; + } + pthread_mutex_lock(&bc->lock); + bc->cstate = BC_CSTATE_DISCONNECT; + pthread_mutex_unlock(&bc->lock); + send_rel(bc); + if (disc->FACILITY) + memcpy(bc->fac, disc->FACILITY, disc->FACILITY[0] + 1); + else + bc->fac[0] = 0; + if (disc->USER_USER) + memcpy(bc->uu, disc->USER_USER, disc->USER_USER[0] + 1); + else + bc->uu[0] = 0; + if ((bc->Flags & FLG_BC_APPLICATION) && bc->manager->application) { + ret = bc->manager->application(bc->manager, PR_APP_HANGUP, bc); + dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__, + bc->channel, ret); + } + return(0); +} + +static int +rel_ind(bchannel_t *bc, void *arg) +{ + RELEASE_t *rel = arg; + int ret; + + if (rel) { + if (rel->FACILITY) + memcpy(bc->fac, rel->FACILITY, rel->FACILITY[0] + 1); + else + bc->fac[0] = 0; + if (rel->USER_USER) + memcpy(bc->uu, rel->USER_USER, rel->USER_USER[0] + 1); + else + bc->uu[0] = 0; + if (rel->CAUSE) { + if (rel->CAUSE[0] > 1) { + dprint(DBGM_BC, "%s: loc(%d) cause(%d)\n", __FUNCTION__, + rel->CAUSE[1] & 0xf, rel->CAUSE[2] & 0x7f); + bc->cause_loc = rel->CAUSE[1] & 0xf; + bc->cause_val = rel->CAUSE[2] & 0x7f; + } + } + } + if ((bc->Flags & FLG_BC_APPLICATION) && bc->manager->application) { + ret = bc->manager->application(bc->manager, PR_APP_CLEAR, bc); + dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__, + bc->channel, ret); + } + clear_bc(bc); + pthread_mutex_lock(&bc->lock); + bc->cstate = BC_CSTATE_NULL; + pthread_mutex_unlock(&bc->lock); + return(0); +} + +static int +relcmpl_ind(bchannel_t *bc, void *arg) +{ + RELEASE_COMPLETE_t *rc = arg; + int ret; + + if (rc) { + if (rc->FACILITY) + memcpy(bc->fac, rc->FACILITY, rc->FACILITY[0] + 1); + else + bc->fac[0] = 0; + if (rc->USER_USER) + memcpy(bc->uu, rc->USER_USER, rc->USER_USER[0] + 1); + else + bc->uu[0] = 0; + if (rc->CAUSE) { + if (rc->CAUSE[0] > 1) { + dprint(DBGM_BC, "%s: loc(%d) cause(%d)\n", __FUNCTION__, + rc->CAUSE[1] & 0xf, rc->CAUSE[2] & 0x7f); + bc->cause_loc = rc->CAUSE[1] & 0xf; + bc->cause_val = rc->CAUSE[2] & 0x7f; + } + } + } + if ((bc->Flags & FLG_BC_APPLICATION) && bc->manager->application) { + ret = bc->manager->application(bc->manager, PR_APP_CLEAR, bc); + dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__, + bc->channel, ret); + } + clear_bc(bc); + pthread_mutex_lock(&bc->lock); + bc->cstate = BC_CSTATE_NULL; + pthread_mutex_unlock(&bc->lock); + return(0); +} + +static int +relcr_ind(bchannel_t *bc, void *arg) +{ + int ret, *err = arg; + + dprint(DBGM_BC, "%s: bc%d cause(%x)\n", __FUNCTION__, + bc->channel, *err); + if ((bc->Flags & FLG_BC_APPLICATION) && bc->manager->application) { + ret = bc->manager->application(bc->manager, PR_APP_CLEAR, bc); + dprint(DBGM_BC, "%s: bc%d application ret(%d)\n", __FUNCTION__, + bc->channel, ret); + } + if (bc->cstate != BC_CSTATE_NULL) { + clear_bc(bc); + pthread_mutex_lock(&bc->lock); + bc->cstate = BC_CSTATE_NULL; + pthread_mutex_unlock(&bc->lock); + } + return(0); +} + +static void +cleanup_bchannel(void *arg) +{ + bchannel_t *bc = arg; + + dprint(DBGM_BC,"%s: bc %d\n", __FUNCTION__, bc->channel); + pthread_mutex_lock(&bc->lock); + msg_queue_purge(&bc->workq); + bc->smsg = NULL; + free_ibuffer(bc->sbuf); + bc->sbuf = NULL; + free_ibuffer(bc->rbuf); + bc->rbuf = NULL; + bc->cstate = BC_CSTATE_NULL; + while(1) + if (!sem_trywait(&bc->work)) + break; + pthread_mutex_unlock(&bc->lock); + dprint(DBGM_BC,"%s: bc %d end\n", __FUNCTION__, bc->channel); +} + +static void * +main_bc_task(void *arg) +{ + bchannel_t *bc = arg; + msg_t *msg; + int ret, id; + mISDN_head_t *hh; + + pthread_cleanup_push(cleanup_bchannel, (void *)bc); + dprint(DBGM_BC,"%s bc %d\n", __FUNCTION__, bc->channel); + while(1) { + + sem_wait(&bc->work); + if (bc->Flags & FLG_BC_TERMINATE) + pthread_exit(NULL); + if (!bc->smsg) { + if (bc->Flags & FLG_BC_TONE) + tone_handler(bc); + if (ibuf_usedcount(bc->sbuf)) + b_send(bc); + } + msg = msg_dequeue(&bc->workq); + if (msg) { + hh = (mISDN_head_t *)msg->data; + msg_pull(msg, mISDN_HEAD_SIZE); + dprint(DBGM_BC,"%s: bc%d st(%d/%d) prim(%x) dinfo(%x) len(%d)\n", __FUNCTION__, + bc->channel, bc->cstate, bc->bstate, hh->prim, hh->dinfo, msg->len); + ret = -EINVAL; + switch(hh->prim) { + case PH_DATA | INDICATION: + ret = do_b_data_ind(bc, hh, msg); + break; + case PH_DATA | CONFIRM: + ret = do_b_data_cnf(bc, hh, msg); + break; + case PH_ACTIVATE | INDICATION: + case PH_ACTIVATE | CONFIRM: + ret = do_b_activated(bc, hh, msg); + break; + case PH_DEACTIVATE | INDICATION: + case PH_DEACTIVATE | CONFIRM: + ret = do_b_deactivated(bc, hh, msg); + break; + case BC_SETUP | CONFIRM: + ret = do_b_setup_conf(bc, hh, msg); + break; + case BC_SETUP | SUB_ERROR: + case BC_CLEANUP | SUB_ERROR: + wprint("%s:ch%d %x error %x\n", __FUNCTION__, + bc->channel, hh->prim, *((int *)msg->data)); + case BC_CLEANUP | CONFIRM: + ret = do_b_cleanup_conf(bc, hh, msg); + break; + + case CC_SETUP | INDICATION: + setup_ind(bc, hh->dinfo, msg->data); + break; + case CC_SETUP | CONFIRM: + bc->l3id = *((int *)msg->data); + break; + case CC_NEW_CR | INDICATION: + pthread_mutex_lock(&bc->lock); + id = *((int *)msg->data); + msg_push(msg, mISDN_HEAD_SIZE); + if (bc->manager && bc->manager->man2stack) + ret = bc->manager->man2stack( + bc->manager->nst, msg); + bc->l3id = id; + pthread_mutex_unlock(&bc->lock); + break; + case CC_RELEASE_CR | INDICATION: + relcr_ind(bc, msg->data); + break; + case CC_INFORMATION | INDICATION: + info_ind(bc, msg->data); + break; + case CC_ALERTING | INDICATION: + alert_ind(bc, msg->data); + break; + case CC_CONNECT | INDICATION: + conn_ind(bc, msg->data); + break; + case CC_FACILITY | INDICATION: + facility_ind(bc, msg->data); + break; + case CC_USER_INFORMATION | INDICATION: + userinfo_ind(bc, msg->data); + break; + case CC_DISCONNECT | INDICATION: + disc_ind(bc, msg->data); + break; + case CC_RELEASE | INDICATION: + rel_ind(bc, msg->data); + break; + case CC_RELEASE | CONFIRM: + rel_ind(bc, NULL); + break; + case CC_RELEASE_COMPLETE | INDICATION: + relcmpl_ind(bc, msg->data); + break; + + case CC_SETUP | REQUEST: + send_setup(bc); + break; + case CC_ALERTING | REQUEST: + send_alert(bc); + break; + case CC_CONNECT | REQUEST: + send_connect(bc); + break; + case CC_DISCONNECT | REQUEST: + send_disc(bc); + break; + case CC_FACILITY | REQUEST: + send_facility(bc); + break; + case CC_USER_INFORMATION | REQUEST: + send_userinfo(bc); + break; + case CC_TIMEOUT | INDICATION: + dprint(DBGM_MAN,"%s: bc%d got CC_TIMEOUT\n", __FUNCTION__, + bc->channel); + break; + default: + wprint("%s:ch%d unhandled prim(%x) di(%x)\n", __FUNCTION__, + bc->channel, hh->prim, hh->dinfo); + break; + } + if (ret) + free_msg(msg); + } + } + pthread_cleanup_pop(1); + return(NULL); +} + + +int +init_bchannel(bchannel_t *bc, int channel) +{ + int ret; + + bc->channel = channel; + msg_queue_init(&bc->workq); + bc->cstate = BC_CSTATE_NULL; + bc->bstate = BC_BSTATE_NULL; + pthread_mutex_init(&bc->lock, NULL); + sem_init (&bc->work, 0, 0); + ret = pthread_create(&bc->tid, NULL, main_bc_task, (void *)bc); + dprint(DBGM_BC, "%s: create bc%d thread %ld ret %d\n", __FUNCTION__, + channel, bc->tid, ret); + return(0); +} + +int +term_bchannel(bchannel_t *bc) +{ + dprint(DBGM_BC, "%s: bc%d\n", __FUNCTION__, bc->channel); + bc->Flags |= FLG_BC_TERMINATE; + sem_post(&bc->work); + return(0); +} diff --git a/i4lnet/fsm.c b/i4lnet/fsm.c new file mode 100644 index 0000000..147a4c5 --- /dev/null +++ b/i4lnet/fsm.c @@ -0,0 +1,163 @@ +/* $Id: fsm.c,v 0.9 2003/08/27 07:33:03 kkeil Exp $ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * Thanks to Jan den Ouden + * Fritz Elfert + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include +#include +#include "fsm.h" + +#define FSM_TIMER_DEBUG 0 + +void +FsmNew(struct Fsm *fsm, + struct FsmNode *fnlist, int fncount) +{ + int i; + + fsm->jumpmatrix = (FSMFNPTR *) + malloc(sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count); + if (!fsm->jumpmatrix) + return; + memset(fsm->jumpmatrix, 0, sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count); + for (i = 0; i < fncount; i++) + if ((fnlist[i].state>=fsm->state_count) || (fnlist[i].event>=fsm->event_count)) { + eprint("FsmNew Error line %d st(%ld/%ld) ev(%ld/%ld)\n", + i,(long)fnlist[i].state,(long)fsm->state_count, + (long)fnlist[i].event,(long)fsm->event_count); + } else + fsm->jumpmatrix[fsm->state_count * fnlist[i].event + + fnlist[i].state] = (FSMFNPTR) fnlist[i].routine; +} + +void +FsmFree(struct Fsm *fsm) +{ + free(fsm->jumpmatrix); +} + +int +FsmEvent(struct FsmInst *fi, int event, void *arg) +{ + FSMFNPTR r; + + if ((fi->state>=fi->fsm->state_count) || (event >= fi->fsm->event_count)) { + eprint("FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n", + (long)fi->state,(long)fi->fsm->state_count,event,(long)fi->fsm->event_count); + return(1); + } + r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; + if (r) { + if (fi->debug) + fi->printdebug(fi, "State %s Event %s", + fi->fsm->strState[fi->state], + fi->fsm->strEvent[event]); + r(fi, event, arg); + return (0); + } else { + if (fi->debug) + fi->printdebug(fi, "State %s Event %s no action", + fi->fsm->strState[fi->state], + fi->fsm->strEvent[event]); + return (!0); + } +} + +void +FsmChangeState(struct FsmInst *fi, int newstate) +{ + fi->state = newstate; + if (fi->debug) + fi->printdebug(fi, "ChangeState %s", + fi->fsm->strState[newstate]); +} + +static int +FsmExpireTimer(struct FsmTimer *ft) +{ +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft); +#endif + FsmEvent(ft->fi, ft->event, ft->arg); + return(0); +} + +void +FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft) +{ + ft->fi = fi; + ft->tl.function = (void *)FsmExpireTimer; + ft->tl.data = (long) ft; +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmInitTimer %lx", (long) ft); +#endif + init_timer(&ft->tl, ft->fi->nst); +} + +void +FsmDelTimer(struct FsmTimer *ft, int where) +{ +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmDelTimer %lx %d", (long) ft, where); +#endif + del_timer(&ft->tl); +} + +void +FsmRemoveTimer(struct FsmTimer *ft) +{ + remove_timer(&ft->tl); +} + +int +FsmAddTimer(struct FsmTimer *ft, + int millisec, int event, void *arg, int where) +{ + +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmAddTimer %lx %d %d", + (long) ft, millisec, where); +#endif + + if (timer_pending(&ft->tl)) { + wprint("FsmAddTimer: timer already active!\n"); + ft->fi->printdebug(ft->fi, "FsmAddTimer already active!"); + return -1; + } + init_timer(&ft->tl, ft->fi->nst); + ft->event = event; + ft->arg = arg; + ft->tl.expires = millisec; + add_timer(&ft->tl); + return 0; +} + +void +FsmRestartTimer(struct FsmTimer *ft, + int millisec, int event, void *arg, int where) +{ + +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmRestartTimer %lx %d %d", + (long) ft, millisec, where); +#endif + + if (timer_pending(&ft->tl)) + del_timer(&ft->tl); + init_timer(&ft->tl, ft->fi->nst); + ft->event = event; + ft->arg = arg; + ft->tl.expires = millisec; + add_timer(&ft->tl); +} diff --git a/i4lnet/fsm.h b/i4lnet/fsm.h new file mode 100644 index 0000000..76c9310 --- /dev/null +++ b/i4lnet/fsm.h @@ -0,0 +1,53 @@ +/* $Id: fsm.h,v 0.9 2003/08/27 07:33:03 kkeil Exp $ + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + + +/* Statemachine */ + +#include "isdn_net.h" + +struct FsmInst; + +typedef void (* FSMFNPTR)(struct FsmInst *, int, void *); + +struct Fsm { + FSMFNPTR *jumpmatrix; + int state_count, event_count; + char **strEvent, **strState; +}; + +struct FsmInst { + struct Fsm *fsm; + net_stack_t *nst; + int state; + int debug; + void *userdata; + int userint; + void (*printdebug) (struct FsmInst *, char *, ...); +}; + +struct FsmNode { + int state, event; + void (*routine) (struct FsmInst *, int, void *); +}; + +struct FsmTimer { + struct FsmInst *fi; + itimer_t tl; + int event; + void *arg; +}; + +extern void FsmNew(struct Fsm *, struct FsmNode *, int); +extern void FsmFree(struct Fsm *); +extern int FsmEvent(struct FsmInst *, int , void *); +extern void FsmChangeState(struct FsmInst *, int); +extern void FsmInitTimer(struct FsmInst *, struct FsmTimer *); +extern int FsmAddTimer(struct FsmTimer *, int, int, void *, int); +extern void FsmRestartTimer(struct FsmTimer *, int, int, void *, int); +extern void FsmDelTimer(struct FsmTimer *, int); +extern void FsmRemoveTimer(struct FsmTimer *); + diff --git a/i4lnet/g711.c b/i4lnet/g711.c new file mode 100644 index 0000000..c679013 --- /dev/null +++ b/i4lnet/g711.c @@ -0,0 +1,928 @@ +/* + * This source code is quick table lookup implementation of + * convert 16 bit linear PCM and A-law u-law (ITU G.711) codings + * Tables are generated using ITU G.711 example code from + * Sun Microsystems, Inc. + * + * (C)2001 Karsten Keil kkeil@suse.de + * + * + * + */ + +#include "g711.h" + +unsigned char _l2u[4096] = { + 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, + 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0xef, + 0xef, 0xee, 0xee, 0xed, 0xed, 0xec, 0xec, 0xeb, + 0xeb, 0xea, 0xea, 0xe9, 0xe9, 0xe8, 0xe8, 0xe7, + 0xe7, 0xe6, 0xe6, 0xe5, 0xe5, 0xe4, 0xe4, 0xe3, + 0xe3, 0xe2, 0xe2, 0xe1, 0xe1, 0xe0, 0xe0, 0xdf, + 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdc, 0xdb, + 0xdb, 0xdb, 0xdb, 0xda, 0xda, 0xda, 0xda, 0xd9, + 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7, + 0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd6, 0xd5, + 0xd5, 0xd5, 0xd5, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1, + 0xd1, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xd0, 0xcf, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, + 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xca, + 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xc9, + 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8, + 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc7, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc4, + 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1, + 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, +}; + +unsigned char _l2A[2048] = { + 0xd5, 0xd4, 0xd7, 0xd6, 0xd1, 0xd0, 0xd3, 0xd2, + 0xdd, 0xdc, 0xdf, 0xde, 0xd9, 0xd8, 0xdb, 0xda, + 0xc5, 0xc4, 0xc7, 0xc6, 0xc1, 0xc0, 0xc3, 0xc2, + 0xcd, 0xcc, 0xcf, 0xce, 0xc9, 0xc8, 0xcb, 0xca, + 0xf5, 0xf5, 0xf4, 0xf4, 0xf7, 0xf7, 0xf6, 0xf6, + 0xf1, 0xf1, 0xf0, 0xf0, 0xf3, 0xf3, 0xf2, 0xf2, + 0xfd, 0xfd, 0xfc, 0xfc, 0xff, 0xff, 0xfe, 0xfe, + 0xf9, 0xf9, 0xf8, 0xf8, 0xfb, 0xfb, 0xfa, 0xfa, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, + 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, + 0xed, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, + 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, + 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, + 0xeb, 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, +}; + +signed short _u2l[256] = { + -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, + -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, + -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, + -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, -2, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 2, +}; + +signed short _A2l[256] = { + -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, + -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, + -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, + -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944, + -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136, + -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472, + -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568, + -344, -328, -376, -360, -280, -264, -312, -296, + -472, -456, -504, -488, -408, -392, -440, -424, + -88, -72, -120, -104, -24, -8, -56, -40, + -216, -200, -248, -232, -152, -136, -184, -168, + -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, -592, + -944, -912, -1008, -976, -816, -784, -880, -848, + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, + 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, + 88, 72, 120, 104, 24, 8, 56, 40, + 216, 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, + 688, 656, 752, 720, 560, 528, 624, 592, + 944, 912, 1008, 976, 816, 784, 880, 848, +}; + +unsigned char _u2A[256] = { + 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, + 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, + 0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, + 0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, + 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, + 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, + 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, 0x12, + 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x6a, + 0x68, 0x69, 0x6e, 0x6f, 0x6c, 0x6d, 0x62, 0x63, + 0x60, 0x61, 0x66, 0x67, 0x64, 0x65, 0x7a, 0x78, + 0x7e, 0x7f, 0x7c, 0x7d, 0x72, 0x73, 0x70, 0x71, + 0x76, 0x77, 0x74, 0x75, 0x4b, 0x49, 0x4f, 0x4d, + 0x42, 0x43, 0x40, 0x41, 0x46, 0x47, 0x44, 0x45, + 0x5a, 0x5b, 0x58, 0x59, 0x5e, 0x5f, 0x5c, 0x5d, + 0x52, 0x52, 0x53, 0x53, 0x50, 0x50, 0x51, 0x51, + 0x56, 0x56, 0x57, 0x57, 0x54, 0x54, 0x55, 0x55, + 0xaa, 0xab, 0xa8, 0xa9, 0xae, 0xaf, 0xac, 0xad, + 0xa2, 0xa3, 0xa0, 0xa1, 0xa6, 0xa7, 0xa4, 0xa5, + 0xba, 0xbb, 0xb8, 0xb9, 0xbe, 0xbf, 0xbc, 0xbd, + 0xb2, 0xb3, 0xb0, 0xb1, 0xb6, 0xb7, 0xb4, 0xb5, + 0x8a, 0x8b, 0x88, 0x89, 0x8e, 0x8f, 0x8c, 0x8d, + 0x82, 0x83, 0x80, 0x81, 0x86, 0x87, 0x84, 0x85, + 0x9b, 0x98, 0x99, 0x9e, 0x9f, 0x9c, 0x9d, 0x92, + 0x93, 0x90, 0x91, 0x96, 0x97, 0x94, 0x95, 0xea, + 0xe8, 0xe9, 0xee, 0xef, 0xec, 0xed, 0xe2, 0xe3, + 0xe0, 0xe1, 0xe6, 0xe7, 0xe4, 0xe5, 0xfa, 0xf8, + 0xfe, 0xff, 0xfc, 0xfd, 0xf2, 0xf3, 0xf0, 0xf1, + 0xf6, 0xf7, 0xf4, 0xf5, 0xcb, 0xc9, 0xcf, 0xcd, + 0xc2, 0xc3, 0xc0, 0xc1, 0xc6, 0xc7, 0xc4, 0xc5, + 0xda, 0xdb, 0xd8, 0xd9, 0xde, 0xdf, 0xdc, 0xdd, + 0xd2, 0xd2, 0xd3, 0xd3, 0xd0, 0xd0, 0xd1, 0xd1, + 0xd6, 0xd6, 0xd7, 0xd7, 0xd4, 0xd4, 0xd5, 0xd5, +}; + +unsigned char _A2u[256] = { + 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, + 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, + 0x39, 0x3a, 0x37, 0x38, 0x3d, 0x3e, 0x3b, 0x3c, + 0x31, 0x32, 0x30, 0x30, 0x35, 0x36, 0x33, 0x34, + 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, + 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, + 0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, + 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, + 0x62, 0x63, 0x60, 0x61, 0x66, 0x67, 0x64, 0x65, + 0x5d, 0x5d, 0x5c, 0x5c, 0x5f, 0x5f, 0x5e, 0x5e, + 0x74, 0x76, 0x70, 0x72, 0x7c, 0x7e, 0x78, 0x7a, + 0x6a, 0x6b, 0x68, 0x69, 0x6e, 0x6f, 0x6c, 0x6d, + 0x48, 0x49, 0x46, 0x47, 0x4c, 0x4d, 0x4a, 0x4b, + 0x40, 0x41, 0x3f, 0x3f, 0x44, 0x45, 0x42, 0x43, + 0x56, 0x57, 0x54, 0x55, 0x5a, 0x5b, 0x58, 0x59, + 0x4f, 0x4f, 0x4e, 0x4e, 0x52, 0x53, 0x50, 0x51, + 0xaa, 0xab, 0xa8, 0xa9, 0xae, 0xaf, 0xac, 0xad, + 0xa2, 0xa3, 0xa0, 0xa1, 0xa6, 0xa7, 0xa4, 0xa5, + 0xb9, 0xba, 0xb7, 0xb8, 0xbd, 0xbe, 0xbb, 0xbc, + 0xb1, 0xb2, 0xb0, 0xb0, 0xb5, 0xb6, 0xb3, 0xb4, + 0x8a, 0x8b, 0x88, 0x89, 0x8e, 0x8f, 0x8c, 0x8d, + 0x82, 0x83, 0x80, 0x81, 0x86, 0x87, 0x84, 0x85, + 0x9a, 0x9b, 0x98, 0x99, 0x9e, 0x9f, 0x9c, 0x9d, + 0x92, 0x93, 0x90, 0x91, 0x96, 0x97, 0x94, 0x95, + 0xe2, 0xe3, 0xe0, 0xe1, 0xe6, 0xe7, 0xe4, 0xe5, + 0xdd, 0xdd, 0xdc, 0xdc, 0xdf, 0xdf, 0xde, 0xde, + 0xf4, 0xf6, 0xf0, 0xf2, 0xfc, 0xfe, 0xf8, 0xfa, + 0xea, 0xeb, 0xe8, 0xe9, 0xee, 0xef, 0xec, 0xed, + 0xc8, 0xc9, 0xc6, 0xc7, 0xcc, 0xcd, 0xca, 0xcb, + 0xc0, 0xc1, 0xbf, 0xbf, 0xc4, 0xc5, 0xc2, 0xc3, + 0xd6, 0xd7, 0xd4, 0xd5, 0xda, 0xdb, 0xd8, 0xd9, + 0xcf, 0xcf, 0xce, 0xce, 0xd2, 0xd3, 0xd0, 0xd1, +}; + diff --git a/i4lnet/isdn_debug.c b/i4lnet/isdn_debug.c new file mode 100644 index 0000000..397ad0e --- /dev/null +++ b/i4lnet/isdn_debug.c @@ -0,0 +1,151 @@ +#include +#include "isdn_debug.h" + + +static unsigned int debug_mask = 0; +static FILE *debug_file = NULL; +static FILE *warn_file = NULL; +static FILE *error_file = NULL; + +int +debug_init(unsigned int mask, char *dfile, char *wfile, char *efile) +{ + if (dfile) { + if (debug_file && (debug_file != stdout)) + debug_file = freopen(dfile, "a", debug_file); + else + debug_file = fopen(dfile, "a"); + if (!debug_file) { + debug_file = stdout; + fprintf(debug_file, + "%s: cannot open %s for debug log, using stdout\n", + __FUNCTION__, dfile); + } + } else { + if (!debug_file) { + debug_file = stdout; + fprintf(debug_file, + "%s: using stdout for debug log\n", __FUNCTION__); + } + } + if (wfile) { + if (warn_file && (warn_file != stderr)) + warn_file = freopen(wfile, "a", warn_file); + else + warn_file = fopen(wfile, "a"); + if (!warn_file) { + warn_file = stderr; + fprintf(warn_file, + "%s: cannot open %s for warning log, using stderr\n", + __FUNCTION__, wfile); + } + } else { + if (!warn_file) { + warn_file = stderr; + fprintf(warn_file, + "%s: using stderr for warning log\n", __FUNCTION__); + } + } + if (efile) { + if (error_file && (error_file != stderr)) + error_file = freopen(efile, "a", error_file); + else + error_file = fopen(efile, "a"); + if (!error_file) { + error_file = stderr; + fprintf(error_file, + "%s: cannot open %s for error log, using stderr\n", + __FUNCTION__, efile); + } + } else { + if (!error_file) { + error_file = stderr; + fprintf(error_file, + "%s: using stderr for error log\n", __FUNCTION__); + } + } + debug_mask = mask; + fprintf(debug_file, "%s: debug_mask = %x\n", __FUNCTION__, debug_mask); + return(0); +} + +void +debug_close(void) +{ + fprintf(debug_file, "%s: debug channel now closed\n", __FUNCTION__); + if (debug_file && (debug_file != stdout)) + fclose(debug_file); + fprintf(warn_file, "%s: warn channel now closed\n", __FUNCTION__); + if (warn_file && (warn_file != stderr)) + fclose(warn_file); + fprintf(error_file, "%s: error channel now closed\n", __FUNCTION__); + if (error_file && (error_file != stderr)) + fclose(error_file); +} + +int +dprint(unsigned int mask, const char *fmt, ...) +{ + int ret = 0; + va_list args; + + va_start(args, fmt); + if (debug_mask & mask) { + ret = vfprintf(debug_file, fmt, args); + if (debug_file != stdout) + fflush(debug_file); + } + va_end(args); + return(ret); +} + +int +wprint(const char *fmt, ...) +{ + int ret = 0; + va_list args; + + va_start(args, fmt); + ret = vfprintf(warn_file, fmt, args); + fflush(warn_file); + va_end(args); + return(ret); +} + + +int +eprint(const char *fmt, ...) +{ + int ret = 0; + va_list args; + + va_start(args, fmt); + ret = vfprintf(error_file, fmt, args); + fflush(error_file); + va_end(args); + return(ret); +} + +int +dhexprint(unsigned int mask, char *head, unsigned char *buf, int len) +{ + int ret = 0; + char *p,*obuf; + + if (debug_mask & mask) { + obuf = malloc(3*(len+1)); + if (!obuf) + return(-ENOMEM); + p = obuf; + while (len) { + p += sprintf(p,"%02x ", *buf); + buf++; + len--; + } + p--; + *p=0; + ret = fprintf(debug_file, "%s %s\n", head, obuf); + free(obuf); + } + return(ret); +} diff --git a/i4lnet/isdn_msg.c b/i4lnet/isdn_msg.c new file mode 100644 index 0000000..1875396 --- /dev/null +++ b/i4lnet/isdn_msg.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include "isdn_msg.h" +#include "isdn_debug.h" + +static msg_queue_t _free_queue; +msg_queue_t *free_queue; + +void +msg_init(void) +{ + free_queue = & _free_queue; + msg_queue_init(free_queue); + free_queue->maxlen = 40; +} + + +static int alloc_msg_cnt = 0; + +msg_t * +_new_msg(int size) +{ + msg_t *m; + + if (size <= MAX_MSG_SIZE) + size = MAX_MSG_SIZE; + else + goto err; + m = malloc(sizeof(msg_t)); + if (!m) + goto err; + m->size = size; + alloc_msg_cnt++; + return(m); +err: + eprint("%s: no mem for size %d msg\n", __FUNCTION__, + size); + return(NULL); +} + +msg_t * +alloc_msg(int size) +{ + msg_t *m; + + if (size > MAX_MSG_SIZE) + return(NULL); + if (msg_queue_len(free_queue)) + m = msg_dequeue(free_queue); + else + m = _new_msg(size); + if (!m) { + eprint("%s: no mem for msg len (%d)\n", __FUNCTION__, + size); + return(NULL); + } + m->list = NULL; + m->prev = NULL; + m->next = NULL; + m->head = &m->__data[0]; + m->data = m->head + DEFAULT_HEADROOM; + m->tail = m->data; + m->end = m->head + m->size; + m->len = 0; + dprint(DBGM_MSG,"%s: %d msg(%p) at %p %p\n", __FUNCTION__, + alloc_msg_cnt, m, __builtin_return_address(0), + __builtin_return_address(1)); + return(m); +} + + +void +free_msg(msg_t *msg) { + if (!msg) { + wprint("free NULL msg\n"); + return; + } + dprint(DBGM_MSG,"%s: %d/%d msg(%p) at %p %p\n", __FUNCTION__, + alloc_msg_cnt, free_queue->len, msg, + __builtin_return_address(0), __builtin_return_address(1)); + if (msg->list) { + if (msg->list == free_queue) + wprint("%s: free twice msg(%p)\n", __FUNCTION__, + msg); + else + wprint("%s: msg(%p) in queue(%p)\n", __FUNCTION__, + msg, msg->list); + return; + } + if (free_queue->len>=free_queue->maxlen) { + alloc_msg_cnt--; + dprint(DBGM_MSG, "free msg no free_queue %d/%d\n", + free_queue->len, free_queue->maxlen); + free(msg); + return; + } + msg_queue_head(free_queue, msg); +} + +msg_t * +msg_copy(msg_t *msg) { + msg_t *nmsg; + + dprint(DBGM_MSG,"%s: old(%p)\n", __FUNCTION__, msg); + nmsg = alloc_msg(msg->size); + if (!nmsg) + return(NULL); + dprint(DBGM_MSG,"%s: new(%p) size(%d)\n", __FUNCTION__, + nmsg, msg->size); + memcpy(nmsg, msg, sizeof(msg_t)); + return(nmsg); +} diff --git a/i4lnet/manager.c b/i4lnet/manager.c new file mode 100644 index 0000000..94cd22a --- /dev/null +++ b/i4lnet/manager.c @@ -0,0 +1,286 @@ +#include +#include +#include +#include "isdn_net.h" +#include "l3dss1.h" +#include "net_l2.h" +#include "net_l3.h" +#include "bchannel.h" +#include "helper.h" + +int +match_nr(manager_t *mgr, unsigned char *nx, nr_list_t **nrx) +{ + int l,i,ret = 2; + unsigned char *p; + nr_list_t *nr = mgr->nrlist; + + if (!nrx) + return(3); + l = nx[0] - 1; + if (l<=0) + return(3); + while(nr) { + p = nx + 2; + dprint(DBGM_MAN,"%s: cpn(%s) nr(%s)\n", __FUNCTION__, + p, nr->nr); + for(i=0;ilen;i++) { + if (*p != nr->nr[i]) + break; + if ((i+1) == nr->len) { + *nrx = nr; + return(0); + } + if (l == (i+1)) { + ret = 1; + break; + } + p++; + } + nr = nr->next; + } + return(ret); +} + +static int +manager2stack(void *dat, void *arg) +{ + net_stack_t *nst = dat; + msg_t *msg = arg; + mISDN_head_t *hh; + + dprint(DBGM_MAN, "%s:dat(%p) arg(%p)\n", __FUNCTION__, + dat, arg); + if (!nst | !arg) + return(-EINVAL); + hh = (mISDN_head_t *)msg->data; + dprint(DBGM_MAN, "%s: prim(%x) dinfo(%x) msg->len(%d)\n", __FUNCTION__, + hh->prim, hh->dinfo, msg->len); + if (hh->prim == (CC_NEW_CR | INDICATION)) /* high prio */ + msg_queue_head(&nst->wqueue, arg); + else + msg_queue_tail(&nst->wqueue, arg); + sem_post(&nst->work); + return(0); +} + +static int +stack2manager(void *dat, void *arg) { + manager_t *mgr = dat; + msg_t *msg = arg; + mISDN_head_t *hh; + + if (!msg || !mgr) + return(-EINVAL); + hh = (mISDN_head_t *)msg->data; + dprint(DBGM_MAN, "%s: prim(%x) dinfo(%x) msg->len(%d) bid(%x/%x)\n", __FUNCTION__, + hh->prim, hh->dinfo, msg->len, mgr->bc[0].l3id, mgr->bc[1].l3id); + if (hh->prim == (CC_SETUP | INDICATION)) { + SETUP_t *setup; + RELEASE_COMPLETE_t *rc; + unsigned char cause[4]; + + setup = (SETUP_t*)(msg->data + mISDN_HEAD_SIZE); + pthread_mutex_lock(&mgr->bc[0].lock); + if (mgr->bc[0].cstate == BC_CSTATE_NULL) { + mgr->bc[0].cstate = BC_CSTATE_ICALL; + msg_queue_tail(&mgr->bc[0].workq, msg); + pthread_mutex_unlock(&mgr->bc[0].lock); + sem_post(&mgr->bc[0].work); + return(0); + } + pthread_mutex_unlock(&mgr->bc[0].lock); + pthread_mutex_lock(&mgr->bc[1].lock); + if (mgr->bc[1].cstate == BC_CSTATE_NULL) { + mgr->bc[1].cstate = BC_CSTATE_ICALL; + msg_queue_tail(&mgr->bc[1].workq, msg); + pthread_mutex_unlock(&mgr->bc[1].lock); + sem_post(&mgr->bc[1].work); + return(0); + } + pthread_mutex_unlock(&mgr->bc[1].lock); + /* No channel available */ + cause[0] = 2; + cause[1] = 0x80 | CAUSE_LOC_PNET_LOCUSER; + if (setup->CHANNEL_ID) + cause[2] = 0x80 | CAUSE_CHANNEL_UNACCEPT; + else + cause[2] = 0x80 | CAUSE_NO_CHANNEL; + prep_l3data_msg(CC_RELEASE_COMPLETE | REQUEST, hh->dinfo, + sizeof(RELEASE_COMPLETE_t), 3, msg); + rc = (RELEASE_COMPLETE_t *)(msg->data + mISDN_HEAD_SIZE); + rc->CAUSE = msg_put(msg, 3); + memcpy(rc->CAUSE, &cause, 3); + if (manager2stack(mgr->nst, msg)) + free_msg(msg); + } else if (hh->dinfo == mgr->bc[0].l3id) { + msg_queue_tail(&mgr->bc[0].workq, msg); + sem_post(&mgr->bc[0].work); + } else if (hh->dinfo == mgr->bc[1].l3id) { + msg_queue_tail(&mgr->bc[1].workq, msg); + sem_post(&mgr->bc[1].work); + } else { + wprint("%s: prim(%x) dinfo(%x) msg->len(%d) not handled\n", __FUNCTION__, + hh->prim, hh->dinfo, msg->len); + return(-ESRCH); + } + return(0); +} + +static int +appl2bc(manager_t *mgr, int prim, void *arg) +{ + bchannel_t *bc = arg; + msg_t *msg; + + dprint(DBGM_MAN, "%s(%p,%x,%p)\n", __FUNCTION__, + mgr, prim, arg); + if (!mgr || !bc) + return(-EINVAL); + if (prim == PR_APP_OCHANNEL) { + bchannel_t **bcp = arg; + + pthread_mutex_lock(&mgr->bc[0].lock); + if (mgr->bc[0].cstate == BC_CSTATE_NULL) { + mgr->bc[0].cstate = BC_CSTATE_OCALL; + pthread_mutex_unlock(&mgr->bc[0].lock); + *bcp = &mgr->bc[0]; + return(1); + } + pthread_mutex_unlock(&mgr->bc[0].lock); + pthread_mutex_lock(&mgr->bc[1].lock); + if (mgr->bc[1].cstate == BC_CSTATE_NULL) { + mgr->bc[1].cstate = BC_CSTATE_OCALL; + pthread_mutex_unlock(&mgr->bc[1].lock); + *bcp = &mgr->bc[1]; + return(2); + } + pthread_mutex_unlock(&mgr->bc[1].lock); + /* No channel available */ + return(-EBUSY); + } else if (prim == PR_APP_OCALL) { + pthread_mutex_lock(&bc->lock); + msg = create_link_msg(CC_SETUP | REQUEST, bc->l3id, 0, + NULL, 0); + if (!msg) + return(-ENOMEM); + msg_queue_tail(&bc->workq, msg); + sem_post(&bc->work); + pthread_mutex_unlock(&bc->lock); + } else if (prim == PR_APP_ALERT) { + pthread_mutex_lock(&bc->lock); + msg = create_link_msg(CC_ALERTING | REQUEST, bc->l3id, 0, + NULL, 0); + if (!msg) + return(-ENOMEM); + msg_queue_tail(&bc->workq, msg); + sem_post(&bc->work); + pthread_mutex_unlock(&bc->lock); + } else if (prim == PR_APP_CONNECT) { + pthread_mutex_lock(&bc->lock); + msg = create_link_msg(CC_CONNECT | REQUEST, bc->l3id, 0, + NULL, 0); + if (!msg) + return(-ENOMEM); + msg_queue_tail(&bc->workq, msg); + sem_post(&bc->work); + pthread_mutex_unlock(&bc->lock); + } else if (prim == PR_APP_HANGUP) { + pthread_mutex_lock(&bc->lock); + msg = create_link_msg(CC_DISCONNECT | REQUEST, bc->l3id, 0, + NULL, 0); + if (!msg) + return(-ENOMEM); + msg_queue_tail(&bc->workq, msg); + sem_post(&bc->work); + pthread_mutex_unlock(&bc->lock); + } else if (prim == PR_APP_FACILITY) { + pthread_mutex_lock(&bc->lock); + msg = create_link_msg(CC_FACILITY | REQUEST, bc->l3id, + 0, NULL, 0); + if (!msg) + return(-ENOMEM); + msg_queue_tail(&bc->workq, msg); + sem_post(&bc->work); + pthread_mutex_unlock(&bc->lock); + } else if (prim == PR_APP_USERUSER) { + pthread_mutex_lock(&bc->lock); + msg = create_link_msg(CC_USER_INFORMATION | REQUEST, bc->l3id, + 0, NULL, 0); + if (!msg) + return(-ENOMEM); + msg_queue_tail(&bc->workq, msg); + sem_post(&bc->work); + pthread_mutex_unlock(&bc->lock); + } else { + wprint("%s(%p,%x,%p) unhandled\n", __FUNCTION__, + mgr, prim, arg); + } + return(0); +} + +int +init_manager(manager_t **mlist, afunc_t application) +{ + manager_t *mgr; + int ret; + + *mlist = NULL; + mgr = malloc(sizeof(manager_t)); + if (!mgr) + return(-ENOMEM); + memset(mgr, 0, sizeof(manager_t)); + mgr->nst = malloc(sizeof(net_stack_t)); + if (!mgr->nst) { + free(mgr); + return(-ENOMEM); + } + memset(mgr->nst, 0, sizeof(net_stack_t)); + ret = do_net_stack_setup(mgr->nst); + if (ret) { + free(mgr->nst); + free(mgr); + return(ret); + } + mgr->application = application; + mgr->app_bc = appl2bc; + mgr->man2stack = manager2stack; + mgr->nst->l3_manager = stack2manager; + mgr->nst->manager = mgr; + Isdnl2Init(mgr->nst); + Isdnl3Init(mgr->nst); + mgr->bc[0].manager = mgr; + mgr->bc[1].manager = mgr; + init_bchannel(&mgr->bc[0], 1); + init_bchannel(&mgr->bc[1], 2); + *mlist = mgr; + return(0); +} + +int +cleanup_manager(manager_t *mgr) +{ + int ret, *retv; + + dprint(DBGM_MAN,"%s\n", __FUNCTION__); + term_bchannel(&mgr->bc[0]); + term_bchannel(&mgr->bc[1]); + cleanup_Isdnl3(mgr->nst); + cleanup_Isdnl2(mgr->nst); + do_net_stack_cleanup(mgr->nst); + ret = pthread_join(mgr->bc[0].tid, (void *)&retv); + dprint(DBGM_MAN,"%s: join ret(%d) bc1 retv(%p)\n", __FUNCTION__, + ret, retv); + ret = pthread_join(mgr->bc[1].tid, (void *)&retv); + dprint(DBGM_MAN,"%s: join ret(%d) bc2 retv(%p)\n", __FUNCTION__, + ret, retv); + while(mgr->nrlist) { + nr_list_t *nr = mgr->nrlist; + + REMOVE_FROM_LISTBASE(nr, mgr->nrlist); + free(nr); + } + free(mgr->nst); + free(mgr); + return(0); +} diff --git a/i4lnet/net_if.c b/i4lnet/net_if.c new file mode 100644 index 0000000..86fbf7b --- /dev/null +++ b/i4lnet/net_if.c @@ -0,0 +1,708 @@ +#include +#include +#include +#include +#include "isdn_net.h" +#include "bchannel.h" +#include "helper.h" + + +int +do_net_stack_setup(net_stack_t *nst) +{ + int ret; + unsigned char buf[1024]; + int i,cnt; + iframe_t *frm = (iframe_t *)buf; + stack_info_t *stinf; + layer_info_t li; + interface_info_t ii; + + + if (!nst) + return(-EINVAL); + if (nst->device) + return(-EBUSY); + ret = mISDN_open(); + if (0 > ret) { + wprint("cannot open mISDN due to %s\n", + strerror(errno)); + return(ret); + } + nst->device = ret; + cnt = mISDN_get_stack_count(nst->device); + if (cnt < 1) { + mISDN_close(nst->device); + wprint("no cards found ret(%d)\n", cnt); + return(-ENODEV); + } + for (i=1; i<=cnt; i++) { + ret = mISDN_get_stack_info(nst->device, i, buf, 1024); + if (ret<=0) + dprint(DBGM_NET, "cannot get stackinfo err: %d\n", ret); + stinf = (stack_info_t *)&frm->data.p; +// mISDNprint_stack_info(stdout, stinf); + if ((stinf->pid.protocol[0] == ISDN_PID_L0_NT_S0) && + (stinf->pid.protocol[1] == ISDN_PID_L1_NT_S0)) { + if (stinf->instcnt == 1) { + nst->cardnr = i; + nst->d_stid = stinf->id; + nst->b_stid[0] = stinf->child[0]; + nst->b_stid[1] = stinf->child[1]; + dprint(DBGM_NET, "bst1 %x bst2 %x\n", + nst->b_stid[0], nst->b_stid[1]); + break; + } else + dprint(DBGM_NET, "stack %d instcnt is %d\n", + i, stinf->instcnt); + } else + dprint(DBGM_NET, "stack %d protocol %x\n", + i, stinf->pid.protocol[0]); + } + if (i>cnt) { + mISDN_close(nst->device); + wprint("no NT cards found\n"); + return(-ENODEV); + } + nst->l1_id = mISDN_get_layerid(nst->device, nst->d_stid, 1); + if (nst->l1_id < 0) { + mISDN_close(nst->device); + eprint("no layer1 id found\n"); + return(-EINVAL); + } + dprint(DBGM_NET, "found NT card stack card%d dst(%x) l1(%x)\n", + nst->cardnr, nst->d_stid, nst->l1_id); + memset(&li, 0, sizeof(layer_info_t)); + strcpy(&li.name[0], "net l2"); + li.object_id = -1; + li.extentions = 0; + li.pid.protocol[2] = ISDN_PID_L2_LAPD_NET; + li.pid.layermask = ISDN_LAYER(2); + li.st = nst->d_stid; + nst->l2_id = mISDN_new_layer(nst->device, &li); + if (nst->l2_id<=0) { + eprint("cannot add layer2 error %d %s\n", + nst->l2_id, strerror(-nst->l2_id)); + mISDN_close(nst->device); + return(nst->l2_id); + } + ii.extentions = EXT_IF_EXCLUSIV; + ii.owner = nst->l2_id; + ii.peer = nst->l1_id; + ii.stat = IF_DOWN; + ret = mISDN_connect(nst->device, &ii); + if (ret) { + eprint("cannot connect layer1 error %d %s\n", + ret, strerror(-ret)); + mISDN_close(nst->device); + return(ret); + } + dprint(DBGM_NET, "add nt net layer2 %x\n", + nst->l2_id); + msg_queue_init(&nst->down_queue); + msg_queue_init(&nst->rqueue); + msg_queue_init(&nst->wqueue); + pthread_mutex_init(&nst->lock, NULL); + ret = sem_init (&nst->work, 0, 0); + if (ret) { + eprint("cannot init semaphore ret(%d) %d %s\n", + ret, errno, strerror(errno)); + return(ret); + } + return(0); +} + +int +do_net_stack_cleanup(net_stack_t *nst) +{ + int ret; + + msg_queue_purge(&nst->down_queue); + msg_queue_purge(&nst->rqueue); + msg_queue_purge(&nst->wqueue); + if (nst->phd_down_msg) + free_msg(nst->phd_down_msg); + nst->phd_down_msg = NULL; + mISDN_close(nst->device); + ret = sem_destroy(&nst->work); + if (ret) { + eprint("cannot destroy semaphore ret(%d) %d %s\n", + ret, errno, strerror(errno)); + return(ret); + } + ret = pthread_mutex_destroy(&nst->lock); + if (ret) { + eprint("cannot destroy mutex ret(%d) %s\n", + ret, strerror(ret)); + return(ret); + } + return(0); +} + +static itimer_t +*get_timer(net_stack_t *nst, int id) +{ + itimer_t *it = nst->tlist; + + while(it) { + if (it->id == id) + break; + it = it->next; + } + return(it); +} + +int +init_timer(itimer_t *it, net_stack_t *nst) +{ + iframe_t frm; + int ret; + + if (!nst) + return(-ENODEV); + if (!get_timer(nst, it->id)) { + it->id = (int)it; + it->Flags = 0; + it->nst = nst; + it->prev = NULL; + if (nst->tlist) { + nst->tlist->prev = it; + it->next = nst->tlist; + } + nst->tlist = it; + } +// dprint(DBGM_NET, "init timer(%x)\n", it->id); + if (test_bit(FLG_TIMER_RUNING, &it->Flags)) + dprint(DBGM_NET, "init timer(%x) while running\n", it->id); + ret = mISDN_write_frame(it->nst->device, &frm, it->id, + MGR_INITTIMER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (ret) + wprint("cannot init timer %p err(%d) %s\n", + it, errno, strerror(errno)); + return(ret); +} + +int +remove_timer(itimer_t *it) +{ + iframe_t frm; + int ret; + + + if (!it->nst) + return(-ENODEV); + if (!get_timer(it->nst, it->id)) + return(-ENODEV); + + ret = mISDN_write_frame(it->nst->device, &frm, it->id, + MGR_REMOVETIMER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (ret) + wprint("cannot remove timer %p err(%d) %s\n", + it, errno, strerror(errno)); + REMOVE_FROM_LISTBASE(it, it->nst->tlist); + return(ret); +} + +int +add_timer(itimer_t *it) +{ + iframe_t frm; + int ret; + + if (!it->nst) + return(-ENODEV); + if (!get_timer(it->nst, it->id)) + return(-ENODEV); + if (timer_pending(it)) + return(-EBUSY); +// dprint(DBGM_NET, "add timer(%x)\n", it->id); + test_and_set_bit(FLG_TIMER_RUNING, &it->Flags); + ret = mISDN_write_frame(it->nst->device, &frm, it->id, + MGR_ADDTIMER | REQUEST, it->expires, 0, NULL, TIMEOUT_1SEC); + if (ret) + wprint("cannot add timer %p (%d ms) err(%d) %s\n", + it, it->expires, errno, strerror(errno)); + return(ret); +} + +int +del_timer(itimer_t *it) +{ + iframe_t frm; + int ret; + + if (!it->nst) + return(-ENODEV); + if (!get_timer(it->nst, it->id)) + return(-ENODEV); +// dprint(DBGM_NET, "del timer(%x)\n", it->id); + test_and_clear_bit(FLG_TIMER_RUNING, &it->Flags); + ret = mISDN_write_frame(it->nst->device, &frm, it->id, + MGR_DELTIMER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if (ret) + wprint("cannot del timer %p (%d ms) err(%d) %s\n", + it, it->expires, errno, strerror(errno)); + return(ret); +} + +int +timer_pending(itimer_t *it) +{ + return(test_bit(FLG_TIMER_RUNING, &it->Flags)); +} + +static int +handle_timer(net_stack_t *nst, int id) +{ + itimer_t *it; + int ret = 0; + + it = get_timer(nst, id); + if (!it) + return(-ENODEV); +// dprint(DBGM_NET, "handle timer(%x)\n", it->id); + test_and_clear_bit(FLG_TIMER_RUNING, &it->Flags); + if (it->function) + ret = it->function(it->data); + return(ret); +} + +int +write_dmsg(net_stack_t *nst, msg_t *msg) +{ + iframe_t *frm; + mISDN_head_t *hh; + + hh = (mISDN_head_t *)msg->data; + dprint(DBGM_NET, "%s: msg(%p) len(%d) pr(%x) di(%x)\n", __FUNCTION__, + msg, msg->len, hh->prim, hh->dinfo); + msg_pull(msg, mISDN_HEAD_SIZE); + frm = (iframe_t *)msg_push(msg, IFRAME_HEAD_SIZE); + frm->prim = hh->prim; + frm->dinfo = hh->dinfo; + frm->addr = nst->l2_id | IF_DOWN; + frm->len = msg->len - IFRAME_HEAD_SIZE; + if (frm->prim == PH_DATA_REQ) { + frm->dinfo = (int)msg; + if (nst->phd_down_msg) { + msg_queue_tail(&nst->down_queue, msg); + return(0); + } + nst->phd_down_msg = msg; + } + mISDN_write(nst->device, msg->data, msg->len, -1); + free_msg(msg); + return(0); +} + +int +phd_conf(net_stack_t *nst, iframe_t *frm, msg_t *msg) +{ + dprint(DBGM_NET, "%s: di(%x)\n", __FUNCTION__, frm->dinfo); + if (frm->dinfo == (int)nst->phd_down_msg) { + free_msg(msg); + nst->phd_down_msg = msg_dequeue(&nst->down_queue); + if (nst->phd_down_msg) { + mISDN_write(nst->device, nst->phd_down_msg->data, + nst->phd_down_msg->len, -1); + free_msg(nst->phd_down_msg); + } + return(0); + } else { + wprint("%s: not matching %p/%#x\n", __FUNCTION__, + nst->phd_down_msg, frm->dinfo); + return(-EINVAL); + } +} + +static int +do_net_read(net_stack_t *nst) +{ + msg_t *msg; + iframe_t *frm; + int ret; + + msg = alloc_msg(MAX_MSG_SIZE); + if (!msg) + return(-ENOMEM); + ret = mISDN_read(nst->device, msg->data, MAX_MSG_SIZE, -1); + if (ret<0) { + free_msg(msg); + if (errno == EAGAIN) + return(0); + else + return(-errno); + } + if (!ret) { + wprint("do_net_read read nothing\n"); + free_msg(msg); + return(-EINVAL); + } + __msg_trim(msg, ret); + frm = (iframe_t *)msg->data; + + dprint(DBGM_NET,"%s: prim(%x) addr(%x)\n", __FUNCTION__, + frm->prim, frm->addr); + switch (frm->prim) { + case MGR_INITTIMER | CONFIRM: + case MGR_ADDTIMER | CONFIRM: + case MGR_DELTIMER | CONFIRM: + case MGR_REMOVETIMER | CONFIRM: +// dprint(DBGM_NET, "timer(%x) cnf(%x)\n", +// frm->addr, frm->prim); + free_msg(msg); + return(0); + } + msg_queue_tail(&nst->rqueue, msg); + sem_post(&nst->work); + return(0); +} + +static int +b_message(net_stack_t *nst, int ch, iframe_t *frm, msg_t *msg) +{ + mISDN_head_t *hh; + + msg_pull(msg, IFRAME_HEAD_SIZE); + hh = (mISDN_head_t *)msg_push(msg, mISDN_HEAD_SIZE); + hh->prim = frm->prim; + hh->dinfo = nst->bcid[ch]; + if (nst->l3_manager) + return(nst->l3_manager(nst->manager, msg)); + return(-EINVAL); + +} + +static int +do_readmsg(net_stack_t *nst, msg_t *msg) +{ + iframe_t *frm; + int ret = -EINVAL; + + if (!nst || !msg) + return(-EINVAL); + frm = (iframe_t *)msg->data; + + dprint(DBGM_NET,"%s: prim(%x) addr(%x)\n", __FUNCTION__, + frm->prim, frm->addr); + if (frm->prim == (MGR_TIMER | INDICATION)) { + mISDN_write_frame(nst->device, msg->data, frm->addr, + MGR_TIMER | RESPONSE, 0, 0, NULL, TIMEOUT_1SEC); + ret = handle_timer(nst, frm->addr); + free_msg(msg); + return(0); + } + if ((frm->addr & IF_ADDRMASK) == nst->l2_id) { + if (nst->l1_l2) { + ret = nst->l1_l2(nst, msg); + } + } else if (nst->b_addr[0] && + ((frm->addr & IF_ADDRMASK) == nst->b_addr[0])) { + ret = b_message(nst, 0, frm, msg); + } else if (nst->b_addr[1] && + ((frm->addr & IF_ADDRMASK) == nst->b_addr[1])) { + ret = b_message(nst, 1, frm, msg); + } else if (nst->b_stid[0] == frm->addr) { + ret = b_message(nst, 0, frm, msg); + } else if (nst->b_stid[1] == frm->addr) { + ret = b_message(nst, 1, frm, msg); + } else if (frm->prim == (MGR_DELLAYER | CONFIRM)) { + dprint(DBGM_NET,"%s: MGR_DELLAYER CONFIRM addr(%x)\n", __FUNCTION__, + frm->addr); + free_msg(msg); + return(0); + } else { + wprint("%s: unhandled msg(%d) prim(%x) addr(%x) dinfo(%x)\n", __FUNCTION__, + frm->len, frm->prim, frm->addr, frm->dinfo); + } + return(ret); +} + +static int +setup_bchannel(net_stack_t *nst, mISDN_head_t *hh, msg_t *msg) { + mISDN_pid_t *pid; + int ret, ch, *id; + layer_info_t li; + unsigned char buf[32]; + + if ((hh->dinfo < 1) || (hh->dinfo > 2)) { + eprint("wrong channel %d\n", hh->dinfo); + return(-EINVAL); + } + ch = hh->dinfo -1; + dprint(DBGM_NET,"%s:ch%d\n", __FUNCTION__, hh->dinfo); + msg_pull(msg, mISDN_HEAD_SIZE); + id = (int *)msg->data; + nst->bcid[ch] = *id; + msg_pull(msg, sizeof(int)); + pid = (mISDN_pid_t *)msg->data; + memset(&li, 0, sizeof(layer_info_t)); + li.object_id = -1; + li.extentions = 0; + li.st = nst->b_stid[ch]; + if (pid->protocol[2] == ISDN_PID_L2_B_USER) { + strcpy(&li.name[0], "B L2"); + li.pid.protocol[2] = ISDN_PID_L2_B_USER; + li.pid.layermask = ISDN_LAYER(2); + } else { + strcpy(&li.name[0], "B L3"); + li.pid.protocol[3] = pid->protocol[3]; + li.pid.layermask = ISDN_LAYER(3); + } + if (nst->b_addr[ch]) + wprint("%s: b_addr[%d] %x in use\n", __FUNCTION__, + ch, nst->b_addr[ch]); + ret = mISDN_new_layer(nst->device, &li); + if (ret<=0) { + wprint("%s: new_layer ret(%d)\n", __FUNCTION__, ret); + goto error; + } + if (ret) { + nst->b_addr[ch] = ret; + dprint(DBGM_NET,"%s: b_address%d %08x\n", __FUNCTION__, + hh->dinfo, ret); + ret = mISDN_set_stack(nst->device, nst->b_stid[ch], + pid); + if (ret) { + wprint("set_stack ret(%d)\n", ret); + mISDN_write_frame(nst->device, buf, + nst->b_addr[ch], MGR_DELLAYER | REQUEST, + 0, 0, NULL, TIMEOUT_1SEC); + nst->b_addr[ch] = 0; + goto error; + } + if_link(nst->manager, (ifunc_t)nst->l3_manager, + BC_SETUP | CONFIRM, nst->bcid[ch], sizeof(int), + &nst->b_addr[ch], 0); + free_msg(msg); + return(0); + } +error: + if_link(nst->manager, (ifunc_t)nst->l3_manager, BC_SETUP | SUB_ERROR, + nst->bcid[ch], sizeof(int), &ret, 0); + free_msg(msg); + return(0); +} + +static int +cleanup_bc(net_stack_t *nst, mISDN_head_t *hh, msg_t *msg) +{ + unsigned char buf[32]; + int ch; + + if (hh->dinfo == nst->bcid[0]) + ch = 0; + else if (hh->dinfo == nst->bcid[1]) + ch = 1; + else { + wprint("%s:not channel match %x %x/%x\n", __FUNCTION__, + hh->dinfo, nst->bcid[0], nst->bcid[1]); + + if_link(nst->manager, (ifunc_t)nst->l3_manager, + BC_CLEANUP | SUB_ERROR, hh->dinfo, 0, NULL, 0); + free_msg(msg); + return(0); + } + dprint(DBGM_NET,"%s:ch%d\n", __FUNCTION__, ch + 1); + mISDN_clear_stack(nst->device, nst->b_stid[ch]); + if (nst->b_addr[ch]) + mISDN_write_frame(nst->device, buf, nst->b_addr[ch], + MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + if_link(nst->manager, (ifunc_t)nst->l3_manager, + BC_CLEANUP | CONFIRM, hh->dinfo, 0, NULL, 0); + nst->b_addr[ch] = 0; + free_msg(msg); + return(0); +} + +static int +l1_request(net_stack_t *nst, mISDN_head_t *hh, msg_t *msg) +{ + iframe_t *frm; + + hh = (mISDN_head_t *)msg->data; + dprint(DBGM_NET, "%s: msg(%p) len(%d) pr(%x) di(%x)\n", __FUNCTION__, + msg, msg->len, hh->prim, hh->dinfo); + msg_pull(msg, mISDN_HEAD_SIZE); + frm = (iframe_t *)msg_push(msg, IFRAME_HEAD_SIZE); + frm->prim = hh->prim; + frm->addr = hh->dinfo; + if (frm->prim == PH_DATA_REQ) + frm->dinfo = (int)msg; + else + frm->dinfo = 0; + frm->len = msg->len - IFRAME_HEAD_SIZE; + mISDN_write(nst->device, msg->data, msg->len, -1); + free_msg(msg); + return(0); +} + +static int +do_writemsg(net_stack_t *nst, msg_t *msg) +{ + mISDN_head_t *hh; + int ret = -EINVAL; + + if (!nst || !msg) + return(-EINVAL); + hh = (mISDN_head_t *)msg->data; + dprint(DBGM_NET,"%s: prim(%x) dinfo(%x)\n", __FUNCTION__, + hh->prim, hh->dinfo); + if ((hh->prim & LAYER_MASK) == MSG_L1_PRIM) { + ret = l1_request(nst, hh, msg); + } else if (hh->prim == (BC_SETUP | REQUEST)) { + ret = setup_bchannel(nst, hh, msg); + } else if (hh->prim == (BC_CLEANUP | REQUEST)) { + ret = cleanup_bc(nst, hh, msg); + } else if (hh->prim == (CC_NEW_CR | INDICATION)) { + msg_pull(msg, mISDN_HEAD_SIZE); + if (hh->dinfo == nst->bcid[0]) { + nst->bcid[0] = *((int *)msg->data); + free_msg(msg); + ret = 0; + } else if (hh->dinfo == nst->bcid[1]) { + nst->bcid[1] = *((int *)msg->data); + free_msg(msg); + ret = 0; + } else + ret = -ENXIO; + } else if ((hh->prim & LAYER_MASK) == MSG_L3_PRIM) { + if (nst->manager_l3) + ret = nst->manager_l3(nst, msg); + } else { + wprint("%s: prim(%x) dinfo(%x) unhandled msg(%d)\n", __FUNCTION__, + hh->prim, hh->dinfo, msg->len); + } + return(ret); +} + +static void * +main_readloop(void *arg) +{ + net_stack_t *nst = arg; + int lp = 1; + int sel, ret; + int maxfd; + fd_set rfd; + fd_set efd; + pthread_t tid; + + + tid = pthread_self(); + dprint(DBGM_NET, "%s: tid %ld\n", __FUNCTION__, tid); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + while(lp) { +// dprint(DBGM_NET, "%s: begin dev %d\n", __FUNCTION__, nst->device); + maxfd = nst->device; + FD_ZERO(&rfd); + FD_SET(nst->device, &rfd); + FD_ZERO(&efd); + FD_SET(nst->device, &efd); + maxfd++; +restart: + sel = mISDN_select(maxfd, &rfd, NULL, &efd, NULL); + if (sel < 0) { + if (errno == EINTR) { + if (test_bit(FLG_NST_TERMINATION, &nst->flag)) + break; + dprint(DBGM_NET, "%s: select restart\n", __FUNCTION__); + goto restart; + } + wprint("%s: error(%d) in select %s\n", __FUNCTION__, + errno, strerror(errno)); + break; + } + if (sel) { + if (FD_ISSET(nst->device, &rfd)) { + ret = do_net_read(nst); + if (ret) { + dprint(DBGM_NET, "%s: rdfunc ret(%d)\n", __FUNCTION__, ret); + } + } + if (FD_ISSET(nst->device, &efd)) { + dprint(DBGM_NET, "%s: exception\n", __FUNCTION__); + } + } + } + dprint(DBGM_NET,"%s: fall trough, abort\n", __FUNCTION__); + pthread_mutex_lock(&nst->lock); + test_and_set_bit(FLG_NST_READER_ABORT, &nst->flag); + pthread_mutex_unlock(&nst->lock); + sem_post(&nst->work); + return(NULL); +} + +void * +do_netthread(void *arg) { + net_stack_t *nst = arg; + int ret; + pthread_t tid; + void *retval = NULL; + + /* create reader thread */ + tid = pthread_self(); + dprint(DBGM_NET, "%s: tid %ld\n", __FUNCTION__, tid); + ret = pthread_create(&nst->reader, NULL, main_readloop, (void *)nst); + tid = pthread_self(); + dprint(DBGM_NET, "%s: tid %ld crated %ld\n", __FUNCTION__, tid, nst->reader); + if (ret) { + eprint("%s: cannot create reader %d\n", __FUNCTION__, + ret); + return(NULL); + } + while(1) { + msg_t *msg; + + sem_wait(&nst->work); + msg = msg_dequeue(&nst->wqueue); + if (msg) { + ret = do_writemsg(nst, msg); + if (ret) { + wprint("%s: do_writemsg return %d\n", __FUNCTION__, + ret); + free_msg(msg); + } + } + + msg = msg_dequeue(&nst->rqueue); + if (msg) { + ret = do_readmsg(nst, msg); + if (ret) { + wprint("%s: do_readmsg return %d\n", __FUNCTION__, + ret); + free_msg(msg); + } + } + pthread_mutex_lock(&nst->lock); + if (test_and_clear_bit(FLG_NST_READER_ABORT, &nst->flag)) { + pthread_mutex_unlock(&nst->lock); + dprint(DBGM_NET,"%s: reader aborted\n", __FUNCTION__); + ret = pthread_join(nst->reader, &retval); + dprint(DBGM_NET,"%s: join ret(%d) reader retval %p\n", __FUNCTION__, + ret, retval); + break; + } + if (test_bit(FLG_NST_TERMINATION, &nst->flag)) { + pthread_mutex_unlock(&nst->lock); + dprint(DBGM_NET,"%s: reader cancel\n", __FUNCTION__); + ret = pthread_cancel(nst->reader); + dprint(DBGM_NET,"%s: cancel reader ret(%d)\n", __FUNCTION__, + ret); + ret = pthread_join(nst->reader, &retval); + dprint(DBGM_NET,"%s: join ret(%d) reader retval %p\n", __FUNCTION__, + ret, retval); + break; + } + pthread_mutex_unlock(&nst->lock); + } + return(retval); +} + +int +term_netstack(net_stack_t *nst) +{ + test_and_set_bit(FLG_NST_TERMINATION, &nst->flag); + sem_post(&nst->work); + return(0); +} diff --git a/i4lnet/net_l2.c b/i4lnet/net_l2.c new file mode 100644 index 0000000..0c1f82f --- /dev/null +++ b/i4lnet/net_l2.c @@ -0,0 +1,2072 @@ +/* $Id: net_l2.c,v 0.9 2003/08/27 07:33:03 kkeil Exp $ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/mISDN.cert + * + */ + +#include +#include "net_l2.h" +#include "helper.h" +// #include "debug.h" + +const char *l2_revision = "$Revision: 0.9 $"; + +static void l2m_debug(struct FsmInst *fi, char *fmt, ...); + +static int debug = 0xff; + +static +struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL}; + +enum { + ST_L2_1, + ST_L2_2, + ST_L2_3, + ST_L2_4, + ST_L2_5, + ST_L2_6, + ST_L2_7, + ST_L2_8, +}; + +#define L2_STATE_COUNT (ST_L2_8+1) + +static char *strL2State[] = +{ + "ST_L2_1", + "ST_L2_2", + "ST_L2_3", + "ST_L2_4", + "ST_L2_5", + "ST_L2_6", + "ST_L2_7", + "ST_L2_8", +}; + +enum { + EV_L2_UI, + EV_L2_SABME, + EV_L2_DISC, + EV_L2_DM, + EV_L2_UA, + EV_L2_FRMR, + EV_L2_SUPER, + EV_L2_I, + EV_L2_DL_DATA, + EV_L2_ACK_PULL, + EV_L2_DL_UNITDATA, + EV_L2_DL_ESTABLISH_REQ, + EV_L2_DL_RELEASE_REQ, + EV_L2_MDL_ASSIGN, + EV_L2_MDL_REMOVE, + EV_L2_MDL_ERROR, + EV_L1_DEACTIVATE, + EV_L2_T200, + EV_L2_T203, + EV_L2_SET_OWN_BUSY, + EV_L2_CLEAR_OWN_BUSY, + EV_L2_FRAME_ERROR, +}; + +#define L2_EVENT_COUNT (EV_L2_FRAME_ERROR+1) + +static char *strL2Event[] = +{ + "EV_L2_UI", + "EV_L2_SABME", + "EV_L2_DISC", + "EV_L2_DM", + "EV_L2_UA", + "EV_L2_FRMR", + "EV_L2_SUPER", + "EV_L2_I", + "EV_L2_DL_DATA", + "EV_L2_ACK_PULL", + "EV_L2_DL_UNITDATA", + "EV_L2_DL_ESTABLISH_REQ", + "EV_L2_DL_RELEASE_REQ", + "EV_L2_MDL_ASSIGN", + "EV_L2_MDL_REMOVE", + "EV_L2_MDL_ERROR", + "EV_L1_DEACTIVATE", + "EV_L2_T200", + "EV_L2_T203", + "EV_L2_SET_OWN_BUSY", + "EV_L2_CLEAR_OWN_BUSY", + "EV_L2_FRAME_ERROR", +}; + +static int l2addrsize(layer2_t *l2); + +static int +l2up(layer2_t *l2, u_int prim, int dinfo, msg_t *msg) +{ + return(if_newhead(l2->nst, l2->nst->l2_l3, prim, dinfo, msg)); +} + +static int +l2up_create(layer2_t *l2, u_int prim, int dinfo, int len, void *arg) +{ + return(if_link(l2->nst, l2->nst->l2_l3, prim, dinfo, len, arg, 0)); +} + +static int +l2down_msg(layer2_t *l2, msg_t *msg) { + int ret; + + ret = write_dmsg(l2->nst, msg); + if (ret) + dprint(DBGM_L2, "l2down_msg: error %d\n", ret); + return(ret); +} + +static int +l2down(layer2_t *l2, u_int prim, int dinfo, msg_t *msg) +{ + mISDN_newhead(prim, dinfo, msg); + return(l2down_msg(l2, msg)); +} + +static int +l2down_create(layer2_t *l2, u_int prim, int dinfo, int len, void *arg) +{ + msg_t *msg; + int err; + + msg = create_link_msg(prim, dinfo, len, arg, 0); + if (!msg) + return(-ENOMEM); + err = l2down_msg(l2, msg); + if (err) + free_msg(msg); + return(err); +} + +static int +l2mgr(layer2_t *l2, u_int prim, void *arg) { + long c = (long)arg; + + dprint(DBGM_L2, "l2mgr: prim %x %c\n", prim, (char)c); + return(0); +} + +static void +set_peer_busy(layer2_t *l2) { + test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + if (msg_queue_len(&l2->i_queue) || msg_queue_len(&l2->ui_queue)) + test_and_set_bit(FLG_L2BLOCK, &l2->flag); +} + +static void +clear_peer_busy(layer2_t *l2) { + if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag)) + test_and_clear_bit(FLG_L2BLOCK, &l2->flag); +} + +static void +InitWin(layer2_t *l2) +{ + int i; + + for (i = 0; i < MAX_WINDOW; i++) + l2->windowar[i] = NULL; +} + +static int +freewin(layer2_t *l2) +{ + int i, cnt = 0; + + for (i = 0; i < MAX_WINDOW; i++) { + if (l2->windowar[i]) { + cnt++; + free_msg(l2->windowar[i]); + l2->windowar[i] = NULL; + } + } + return cnt; +} + +static void +ReleaseWin(layer2_t *l2) +{ + int cnt; + + if((cnt = freewin(l2))) + dprint(DBGM_L2, "isdnl2 freed %d msguffs in release\n", cnt); +} + +inline unsigned int +cansend(layer2_t *l2) +{ + unsigned int p1; + + if(test_bit(FLG_MOD128, &l2->flag)) + p1 = (l2->vs - l2->va) % 128; + else + p1 = (l2->vs - l2->va) % 8; + return ((p1 < l2->window) && !test_bit(FLG_PEER_BUSY, &l2->flag)); +} + +inline void +clear_exception(layer2_t *l2) +{ + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + test_and_clear_bit(FLG_REJEXC, &l2->flag); + test_and_clear_bit(FLG_OWN_BUSY, &l2->flag); + clear_peer_busy(l2); +} + +inline int +l2headersize(layer2_t *l2, int ui) +{ + return (((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) + + (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1)); +} + +inline int +l2addrsize(layer2_t *l2) +{ + return (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1); +} + +static int +sethdraddr(layer2_t *l2, u_char *header, int rsp) +{ + u_char *ptr = header; + int crbit = rsp; + + if (test_bit(FLG_LAPD, &l2->flag)) { + if (test_bit(FLG_LAPD_NET, &l2->flag)) + crbit = !crbit; + *ptr++ = (l2->sapi << 2) | (crbit ? 2 : 0); + *ptr++ = (l2->tei << 1) | 1; + return (2); + } else { + if (test_bit(FLG_ORIG, &l2->flag)) + crbit = !crbit; + if (crbit) + *ptr++ = l2->addr.B; + else + *ptr++ = l2->addr.A; + return (1); + } +} + +inline static void +enqueue_super(layer2_t *l2, msg_t *msg) +{ + if (l2down(l2, PH_DATA | REQUEST, DINFO_SKB, msg)) + free_msg(msg); +} + +#define enqueue_ui(a, b) enqueue_super(a, b) + +inline int +IsUI(u_char * data) +{ + return ((data[0] & 0xef) == UI); +} + +inline int +IsUA(u_char * data) +{ + return ((data[0] & 0xef) == UA); +} + +inline int +IsDM(u_char * data) +{ + return ((data[0] & 0xef) == DM); +} + +inline int +IsDISC(u_char * data) +{ + return ((data[0] & 0xef) == DISC); +} + +inline int +IsRR(u_char * data, layer2_t *l2) +{ + if (test_bit(FLG_MOD128, &l2->flag)) + return (data[0] == RR); + else + return ((data[0] & 0xf) == 1); +} + +inline int +IsSFrame(u_char * data, layer2_t *l2) +{ + register u_char d = *data; + + if (!test_bit(FLG_MOD128, &l2->flag)) + d &= 0xf; + return(((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c)); +} + +inline int +IsSABME(u_char * data, layer2_t *l2) +{ + u_char d = data[0] & ~0x10; + + return (test_bit(FLG_MOD128, &l2->flag) ? d == SABME : d == SABM); +} + +inline int +IsREJ(u_char * data, layer2_t *l2) +{ + return (test_bit(FLG_MOD128, &l2->flag) ? data[0] == REJ : (data[0] & 0xf) == REJ); +} + +inline int +IsFRMR(u_char * data) +{ + return ((data[0] & 0xef) == FRMR); +} + +inline int +IsRNR(u_char * data, layer2_t *l2) +{ + return (test_bit(FLG_MOD128, &l2->flag) ? data[0] == RNR : (data[0] & 0xf) == RNR); +} + +int +iframe_error(layer2_t *l2, msg_t *msg) +{ + int i = l2addrsize(l2) + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1); + int rsp = *msg->data & 0x2; + + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (rsp) + return 'L'; + if (msg->len < i) + return 'N'; + if ((msg->len - i) > l2->maxlen) + return 'O'; + return 0; +} + +int +super_error(layer2_t *l2, msg_t *msg) +{ + if (msg->len != l2addrsize(l2) + + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1)) + return 'N'; + return 0; +} + +int +unnum_error(layer2_t *l2, msg_t *msg, int wantrsp) +{ + int rsp = (*msg->data & 0x2) >> 1; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (rsp != wantrsp) + return 'L'; + if (msg->len != l2addrsize(l2) + 1) + return 'N'; + return 0; +} + +int +UI_error(layer2_t *l2, msg_t *msg) +{ + int rsp = *msg->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (rsp) + return 'L'; + if (msg->len > l2->maxlen + l2addrsize(l2) + 1) + return 'O'; + return 0; +} + +int +FRMR_error(layer2_t *l2, msg_t *msg) +{ + int headers = l2addrsize(l2) + 1; + u_char *datap = msg->data + headers; + int rsp = *msg->data & 0x2; + + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (!rsp) + return 'L'; + if (test_bit(FLG_MOD128, &l2->flag)) { + if (msg->len < headers + 5) + return 'N'; + else + l2m_debug(&l2->l2m, "FRMR information %2x %2x %2x %2x %2x", + datap[0], datap[1], datap[2], + datap[3], datap[4]); + } else { + if (msg->len < headers + 3) + return 'N'; + else + l2m_debug(&l2->l2m, "FRMR information %2x %2x %2x", + datap[0], datap[1], datap[2]); + } + return 0; +} + +static unsigned int +legalnr(layer2_t *l2, unsigned int nr) +{ + if(test_bit(FLG_MOD128, &l2->flag)) + return ((nr - l2->va) % 128) <= ((l2->vs - l2->va) % 128); + else + return ((nr - l2->va) % 8) <= ((l2->vs - l2->va) % 8); +} + +static void +setva(layer2_t *l2, unsigned int nr) +{ + while (l2->va != nr) { + (l2->va)++; + if(test_bit(FLG_MOD128, &l2->flag)) + l2->va %= 128; + else + l2->va %= 8; + l2up(l2, DL_DATA | CONFIRM, (int)l2->windowar[l2->sow], NULL); + free_msg(l2->windowar[l2->sow]); + l2->windowar[l2->sow] = NULL; + l2->sow = (l2->sow + 1) % l2->window; + } +} + +static void +send_uframe(layer2_t *l2, msg_t *msg, u_char cmd, u_char cr) +{ + u_char tmp[MAX_HEADER_LEN]; + int i; + + i = sethdraddr(l2, tmp, cr); + tmp[i++] = cmd; + if (msg) + msg_trim(msg, 0); + else if ((msg = alloc_msg(i + mISDN_HEAD_SIZE))) + msg_reserve(msg, mISDN_HEAD_SIZE); + else { + dprint(DBGM_L2,"%s: can't alloc msguff\n", __FUNCTION__); + return; + } + memcpy(msg_put(msg, i), tmp, i); + msg_push(msg, mISDN_HEAD_SIZE); + enqueue_super(l2, msg); +} + + +inline u_char +get_PollFlag(layer2_t *l2, msg_t * msg) +{ + return (msg->data[l2addrsize(l2)] & 0x10); +} + +inline u_char +get_PollFlagFree(layer2_t *l2, msg_t *msg) +{ + u_char PF; + + PF = get_PollFlag(l2, msg); + free_msg(msg); + return (PF); +} + +inline void +start_t200(layer2_t *l2, int i) +{ + FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i); + test_and_set_bit(FLG_T200_RUN, &l2->flag); +} + +inline void +restart_t200(layer2_t *l2, int i) +{ + FsmRestartTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i); + test_and_set_bit(FLG_T200_RUN, &l2->flag); +} + +inline void +stop_t200(layer2_t *l2, int i) +{ + if(test_and_clear_bit(FLG_T200_RUN, &l2->flag)) + FsmDelTimer(&l2->t200, i); +} + +inline void +st5_dl_release_l2l3(layer2_t *l2) +{ + int pr; + + if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) { + pr = DL_RELEASE | CONFIRM; + } else { + pr = DL_RELEASE | INDICATION; + } + l2up_create(l2, pr, CES(l2), 0, NULL); +} + +inline void +lapb_dl_release_l2l3(layer2_t *l2, int f) +{ + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE | REQUEST, 0, 0, NULL); + l2up_create(l2, DL_RELEASE | f, CES(l2), 0, NULL); +} + +static void +establishlink(struct FsmInst *fi) +{ + layer2_t *l2 = fi->userdata; + u_char cmd; + + clear_exception(l2); + l2->rc = 0; + cmd = (test_bit(FLG_MOD128, &l2->flag) ? SABME : SABM) | 0x10; + send_uframe(l2, NULL, cmd, CMD); + FsmDelTimer(&l2->t203, 1); + restart_t200(l2, 1); + test_and_clear_bit(FLG_PEND_REL, &l2->flag); + freewin(l2); + FsmChangeState(fi, ST_L2_5); +} + +static void +l2_mdl_error_ua(struct FsmInst *fi, int event, void *arg) +{ + msg_t *msg = arg; + layer2_t *l2 = fi->userdata; + + if (get_PollFlagFree(l2, msg)) + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'C'); + else + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'D'); + +} + +static void +l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg) +{ + msg_t *msg = arg; + layer2_t *l2 = fi->userdata; + + if (get_PollFlagFree(l2, msg)) + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'B'); + else { + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'E'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); + } +} + +static void +l2_st8_mdl_error_dm(struct FsmInst *fi, int event, void *arg) +{ + msg_t *msg = arg; + layer2_t *l2 = fi->userdata; + + if (get_PollFlagFree(l2, msg)) + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'B'); + else { + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'E'); + } + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); +} + +static void +l2_go_st3(struct FsmInst *fi, int event, void *arg) +{ + free_msg((msg_t *)arg); + FsmChangeState(fi, ST_L2_3); +} + +static void +l2_mdl_assign(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + mISDN_head_t *hh; + + FsmChangeState(fi, ST_L2_3); + msg_trim(msg, 0); + hh = (mISDN_head_t *)msg_put(msg, mISDN_HEAD_SIZE); + hh->prim = MDL_ASSIGN | INDICATION; + hh->dinfo = 0; + if (l2_tei(l2->tm, msg)) + free_msg(msg); +} + +static void +l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + msg_queue_tail(&l2->ui_queue, msg); + FsmChangeState(fi, ST_L2_2); + if ((msg = create_link_msg(MDL_ASSIGN | INDICATION, 0, 0, NULL, 0))) { + if (l2_tei(l2->tm, msg)) + free_msg(msg); + } +} + +static void +l2_queue_ui(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + msg_queue_tail(&l2->ui_queue, msg); +} + +static void +tx_ui(layer2_t *l2) +{ + msg_t *msg; + u_char header[MAX_HEADER_LEN]; + int i; + + i = sethdraddr(l2, header, CMD); + if (test_bit(FLG_LAPD_NET, &l2->flag)) + header[1] = 0xff; /* tei 127 */ + header[i++] = UI; + while ((msg = msg_dequeue(&l2->ui_queue))) { + msg_pull(msg, mISDN_HEAD_SIZE); + memcpy(msg_push(msg, i), header, i); + msg_push(msg, mISDN_HEAD_SIZE); + enqueue_ui(l2, msg); + } +} + +static void +l2_send_ui(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + msg_queue_tail(&l2->ui_queue, msg); + tx_ui(l2); +} + +static void +l2_got_ui(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + msg_pull(msg, l2headersize(l2, 1)); +/* + * in states 1-3 for broadcast + */ + msg_push(msg, mISDN_HEAD_SIZE); + if (l2up(l2, DL_UNITDATA | INDICATION, CES(l2), msg)) + free_msg(msg); +} + +static void +l2_establish(struct FsmInst *fi, int event, void *arg) +{ + msg_t *msg = arg; + layer2_t *l2 = fi->userdata; + + if (!test_bit(FLG_LAPD_NET, &l2->flag)) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + } + free_msg(msg); +} + +static void +l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg) +{ + msg_t *msg = arg; + layer2_t *l2 = fi->userdata; + + msg_queue_purge(&l2->i_queue); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + test_and_clear_bit(FLG_PEND_REL, &l2->flag); + free_msg(msg); +} + +static void +l2_l3_reestablish(struct FsmInst *fi, int event, void *arg) +{ + msg_t *msg = arg; + layer2_t *l2 = fi->userdata; + + msg_queue_purge(&l2->i_queue); + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + free_msg(msg); +} + +static void +l2_release(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + msg_trim(msg, mISDN_HEAD_SIZE); + if (l2up(l2, DL_RELEASE | CONFIRM, CES(l2), msg)) + free_msg(msg); +} + +static void +l2_pend_rel(struct FsmInst *fi, int event, void *arg) +{ + msg_t *msg = arg; + layer2_t *l2 = fi->userdata; + + test_and_set_bit(FLG_PEND_REL, &l2->flag); + free_msg(msg); +} + +static void +l2_disconnect(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + msg_queue_purge(&l2->i_queue); + freewin(l2); + FsmChangeState(fi, ST_L2_6); + l2->rc = 0; + send_uframe(l2, NULL, DISC | 0x10, CMD); + FsmDelTimer(&l2->t203, 1); + restart_t200(l2, 2); + if (msg) + free_msg(msg); +} + +static void +l2_start_multi(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + send_uframe(l2, NULL, UA | get_PollFlag(l2, msg), RSP); + + clear_exception(l2); + l2->vs = 0; + l2->va = 0; + l2->vr = 0; + l2->sow = 0; + FsmChangeState(fi, ST_L2_7); + FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); + msg_trim(msg, 0); + msg_push(msg, mISDN_HEAD_SIZE); + if (l2up(l2, DL_ESTABLISH | INDICATION, CES(l2), msg)) + free_msg(msg); +} + +static void +l2_send_UA(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + send_uframe(l2, msg, UA | get_PollFlag(l2, msg), RSP); +} + +static void +l2_send_DM(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + send_uframe(l2, msg, DM | get_PollFlag(l2, msg), RSP); +} + +static void +l2_restart_multi(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + int est = 0; + + send_uframe(l2, msg, UA | get_PollFlag(l2, msg), RSP); + + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'F'); + + if (l2->vs != l2->va) { + msg_queue_purge(&l2->i_queue); + est = 1; + } + + clear_exception(l2); + l2->vs = 0; + l2->va = 0; + l2->vr = 0; + l2->sow = 0; + FsmChangeState(fi, ST_L2_7); + stop_t200(l2, 3); + FsmRestartTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); + + if (est) + l2up_create(l2, DL_ESTABLISH | INDICATION, CES(l2), 0, NULL); + + if (msg_queue_len(&l2->i_queue) && cansend(l2)) + FsmEvent(fi, EV_L2_ACK_PULL, NULL); +} + +static void +l2_stop_multi(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + FsmChangeState(fi, ST_L2_4); + FsmDelTimer(&l2->t203, 3); + stop_t200(l2, 4); + + send_uframe(l2, msg, UA | get_PollFlag(l2, msg), RSP); + msg_queue_purge(&l2->i_queue); + freewin(l2); + lapb_dl_release_l2l3(l2, INDICATION); +} + +static void +l2_connected(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + int pr=-1; + + if (!get_PollFlag(l2, msg)) { + l2_mdl_error_ua(fi, event, arg); + return; + } + free_msg(msg); + if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) + l2_disconnect(fi, event, NULL); + if (test_and_clear_bit(FLG_L3_INIT, &l2->flag)) { + pr = DL_ESTABLISH | CONFIRM; + } else if (l2->vs != l2->va) { + msg_queue_purge(&l2->i_queue); + pr = DL_ESTABLISH | INDICATION; + } + stop_t200(l2, 5); + + l2->vr = 0; + l2->vs = 0; + l2->va = 0; + l2->sow = 0; + FsmChangeState(fi, ST_L2_7); + FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 4); + if (pr != -1) + l2up_create(l2, pr, CES(l2), 0, NULL); + + if (msg_queue_len(&l2->i_queue) && cansend(l2)) + FsmEvent(fi, EV_L2_ACK_PULL, NULL); +} + +static void +l2_released(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + if (!get_PollFlag(l2, msg)) { + l2_mdl_error_ua(fi, event, arg); + return; + } + free_msg(msg); + stop_t200(l2, 6); + lapb_dl_release_l2l3(l2, CONFIRM); + FsmChangeState(fi, ST_L2_4); +} + +static void +l2_reestablish(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + if (!get_PollFlagFree(l2, msg)) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + } +} + +static void +l2_st5_dm_release(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + if (get_PollFlagFree(l2, msg)) { + stop_t200(l2, 7); + if (!test_bit(FLG_L3_INIT, &l2->flag)) + msg_queue_purge(&l2->i_queue); + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE | REQUEST, 0, 0, NULL); + st5_dl_release_l2l3(l2); + FsmChangeState(fi, ST_L2_4); + } +} + +static void +l2_st6_dm_release(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + if (get_PollFlagFree(l2, msg)) { + stop_t200(l2, 8); + lapb_dl_release_l2l3(l2, CONFIRM); + FsmChangeState(fi, ST_L2_4); + } +} + +void +enquiry_cr(layer2_t *l2, u_char typ, u_char cr, u_char pf) +{ + msg_t *msg; + u_char tmp[MAX_HEADER_LEN]; + int i; + + i = sethdraddr(l2, tmp, cr); + if (test_bit(FLG_MOD128, &l2->flag)) { + tmp[i++] = typ; + tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0); + } else + tmp[i++] = (l2->vr << 5) | typ | (pf ? 0x10 : 0); + if (!(msg = alloc_msg(i + mISDN_HEAD_SIZE))) { + dprint(DBGM_L2, "isdnl2 can't alloc sbbuff for enquiry_cr\n"); + return; + } else + msg_reserve(msg, mISDN_HEAD_SIZE); + memcpy(msg_put(msg, i), tmp, i); + msg_push(msg, mISDN_HEAD_SIZE); + enqueue_super(l2, msg); +} + +inline void +enquiry_response(layer2_t *l2) +{ + if (test_bit(FLG_OWN_BUSY, &l2->flag)) + enquiry_cr(l2, RNR, RSP, 1); + else + enquiry_cr(l2, RR, RSP, 1); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); +} + +inline void +transmit_enquiry(layer2_t *l2) +{ + if (test_bit(FLG_OWN_BUSY, &l2->flag)) + enquiry_cr(l2, RNR, CMD, 1); + else + enquiry_cr(l2, RR, CMD, 1); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + start_t200(l2, 9); +} + + +static void +nrerrorrecovery(struct FsmInst *fi) +{ + layer2_t *l2 = fi->userdata; + + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'J'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); +} + +static void +invoke_retransmission(layer2_t *l2, unsigned int nr) +{ + unsigned int p1; + + if (l2->vs != nr) { + while (l2->vs != nr) { + (l2->vs)--; + if(test_bit(FLG_MOD128, &l2->flag)) { + l2->vs %= 128; + p1 = (l2->vs - l2->va) % 128; + } else { + l2->vs %= 8; + p1 = (l2->vs - l2->va) % 8; + } + p1 = (p1 + l2->sow) % l2->window; +// if (test_bit(FLG_LAPB, &l2->flag)) +// st->l1.bcs->tx_cnt += l2->windowar[p1]->len + l2headersize(l2, 0); + msg_queue_head(&l2->i_queue, l2->windowar[p1]); + l2->windowar[p1] = NULL; + } + FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL); + } +} + +static void +l2_st7_got_super(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + int PollFlag, rsp, typ = RR; + unsigned int nr; + + rsp = *msg->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + + msg_pull(msg, l2addrsize(l2)); + if (IsRNR(msg->data, l2)) { + set_peer_busy(l2); + typ = RNR; + } else + clear_peer_busy(l2); + if (IsREJ(msg->data, l2)) + typ = REJ; + + if (test_bit(FLG_MOD128, &l2->flag)) { + PollFlag = (msg->data[1] & 0x1) == 0x1; + nr = msg->data[1] >> 1; + } else { + PollFlag = (msg->data[0] & 0x10); + nr = (msg->data[0] >> 5) & 0x7; + } + free_msg(msg); + + if (PollFlag) { + if (rsp) + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'A'); + else + enquiry_response(l2); + } + if (legalnr(l2, nr)) { + if (typ == REJ) { + setva(l2, nr); + invoke_retransmission(l2, nr); + stop_t200(l2, 10); + if (FsmAddTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 6)) + l2m_debug(&l2->l2m, "Restart T203 ST7 REJ"); + } else if ((nr == l2->vs) && (typ == RR)) { + setva(l2, nr); + stop_t200(l2, 11); + FsmRestartTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 7); + } else if ((l2->va != nr) || (typ == RNR)) { + setva(l2, nr); + if (typ != RR) + FsmDelTimer(&l2->t203, 9); + restart_t200(l2, 12); + } + if (msg_queue_len(&l2->i_queue) && (typ == RR)) + FsmEvent(fi, EV_L2_ACK_PULL, NULL); + } else + nrerrorrecovery(fi); +} + +static void +l2_feed_i_if_reest(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + +// if (test_bit(FLG_LAPB, &l2->flag)) +// st->l1.bcs->tx_cnt += msg->len + l2headersize(l2, 0); + if (!test_bit(FLG_L3_INIT, &l2->flag)) + msg_queue_tail(&l2->i_queue, msg); + else + free_msg(msg); +} + +static void +l2_feed_i_pull(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + +// if (test_bit(FLG_LAPB, &l2->flag)) +// st->l1.bcs->tx_cnt += msg->len + l2headersize(l2, 0); + msg_queue_tail(&l2->i_queue, msg); + FsmEvent(fi, EV_L2_ACK_PULL, NULL); +} + +static void +l2_feed_iqueue(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + +// if (test_bit(FLG_LAPB, &l2->flag)) +// st->l1.bcs->tx_cnt += msg->len + l2headersize(l2, 0); + msg_queue_tail(&l2->i_queue, msg); +} + +static void +l2_got_iframe(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + int PollFlag, ns, i; + unsigned int nr; + + i = l2addrsize(l2); + if (test_bit(FLG_MOD128, &l2->flag)) { + PollFlag = ((msg->data[i + 1] & 0x1) == 0x1); + ns = msg->data[i] >> 1; + nr = (msg->data[i + 1] >> 1) & 0x7f; + } else { + PollFlag = (msg->data[i] & 0x10); + ns = (msg->data[i] >> 1) & 0x7; + nr = (msg->data[i] >> 5) & 0x7; + } + if (test_bit(FLG_OWN_BUSY, &l2->flag)) { + free_msg(msg); + if (PollFlag) + enquiry_response(l2); + } else if (l2->vr == ns) { + (l2->vr)++; + if(test_bit(FLG_MOD128, &l2->flag)) + l2->vr %= 128; + else + l2->vr %= 8; + test_and_clear_bit(FLG_REJEXC, &l2->flag); + if (PollFlag) + enquiry_response(l2); + else + test_and_set_bit(FLG_ACK_PEND, &l2->flag); + msg_pull(msg, l2headersize(l2, 0)); + msg_push(msg, mISDN_HEAD_SIZE); + if (l2up(l2, DL_DATA | INDICATION, CES(l2), msg)) + free_msg(msg); + } else { + /* n(s)!=v(r) */ + free_msg(msg); + if (test_and_set_bit(FLG_REJEXC, &l2->flag)) { + if (PollFlag) + enquiry_response(l2); + } else { + enquiry_cr(l2, REJ, RSP, PollFlag); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + } + } + if (legalnr(l2, nr)) { + if (!test_bit(FLG_PEER_BUSY, &l2->flag) && (fi->state == ST_L2_7)) { + if (nr == l2->vs) { + stop_t200(l2, 13); + FsmRestartTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 7); + } else if (nr != l2->va) + restart_t200(l2, 14); + } + setva(l2, nr); + } else { + nrerrorrecovery(fi); + return; + } + if (msg_queue_len(&l2->i_queue) && (fi->state == ST_L2_7)) + FsmEvent(fi, EV_L2_ACK_PULL, NULL); + if (test_and_clear_bit(FLG_ACK_PEND, &l2->flag)) + enquiry_cr(l2, RR, RSP, 0); +} + +static void +l2_got_tei(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + mISDN_head_t *hh = (mISDN_head_t *)msg->data; + + l2->tei = hh->dinfo; + free_msg(msg); + if (fi->state == ST_L2_3) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + } else + FsmChangeState(fi, ST_L2_4); + if (msg_queue_len(&l2->ui_queue)) + tx_ui(l2); +} + +static void +l2_st5_tout_200(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + } else if (l2->rc == l2->N200) { + FsmChangeState(fi, ST_L2_4); + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + msg_queue_purge(&l2->i_queue); + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'G'); + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE | REQUEST, 0, 0, NULL); + st5_dl_release_l2l3(l2); + } else { + l2->rc++; + FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + send_uframe(l2, NULL, (test_bit(FLG_MOD128, &l2->flag) ? + SABME : SABM) | 0x10, CMD); + } +} + +static void +l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + } else if (l2->rc == l2->N200) { + FsmChangeState(fi, ST_L2_4); + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'H'); + lapb_dl_release_l2l3(l2, CONFIRM); + } else { + l2->rc++; + FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, + NULL, 9); + send_uframe(l2, NULL, DISC | 0x10, CMD); + } +} + +static void +l2_st7_tout_200(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + return; + } + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + l2->rc = 0; + FsmChangeState(fi, ST_L2_8); + transmit_enquiry(l2); + l2->rc++; +} + +static void +l2_st8_tout_200(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + return; + } + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + if (l2->rc == l2->N200) { + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'I'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); + } else { + transmit_enquiry(l2); + l2->rc++; + } +} + +static void +l2_st7_tout_203(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 9); + return; + } + FsmChangeState(fi, ST_L2_8); + transmit_enquiry(l2); + l2->rc = 0; +} + +static void +l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg, *omsg; + u_char header[MAX_HEADER_LEN]; + int i; + int unsigned p1; + + if (!cansend(l2)) + return; + + msg = msg_dequeue(&l2->i_queue); + if (!msg) + return; + + if(test_bit(FLG_MOD128, &l2->flag)) + p1 = (l2->vs - l2->va) % 128; + else + p1 = (l2->vs - l2->va) % 8; + p1 = (p1 + l2->sow) % l2->window; + if (l2->windowar[p1]) { + dprint(DBGM_L2, "isdnl2 try overwrite ack queue entry %d\n", + p1); + free_msg(l2->windowar[p1]); + } + l2->windowar[p1] = msg; + msg = msg_clone(msg); + + i = sethdraddr(l2, header, CMD); + + if (test_bit(FLG_MOD128, &l2->flag)) { + header[i++] = l2->vs << 1; + header[i++] = l2->vr << 1; + l2->vs = (l2->vs + 1) % 128; + } else { + header[i++] = (l2->vr << 5) | (l2->vs << 1); + l2->vs = (l2->vs + 1) % 8; + } + + p1 = msg_headroom(msg); + msg_pull(msg, mISDN_HEAD_SIZE); + if (p1 >= i) + memcpy(msg_push(msg, i), header, i); + else { + dprint(DBGM_L2, + "isdnl2 pull_iqueue msg header(%d/%d) too short\n", i, p1); + omsg = msg; + msg = alloc_msg(omsg->len + i + mISDN_HEAD_SIZE); + if (!msg) { + free_msg(omsg); + dprint(DBGM_L2,"%s: no msg mem\n", __FUNCTION__); + return; + } + msg_reserve(msg, mISDN_HEAD_SIZE); + memcpy(msg_put(msg, i), header, i); + memcpy(msg_put(msg, omsg->len), omsg->data, omsg->len); + free_msg(omsg); + } + msg_push(msg, mISDN_HEAD_SIZE); + l2down(l2, PH_DATA_REQ, DINFO_SKB, msg); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + if (!test_and_set_bit(FLG_T200_RUN, &l2->flag)) { + FsmDelTimer(&l2->t203, 13); + FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 11); + } +} + +static void +l2_st8_got_super(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + int PollFlag, rsp, rnr = 0; + unsigned int nr; + + rsp = *msg->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + + msg_pull(msg, l2addrsize(l2)); + + if (IsRNR(msg->data, l2)) { + set_peer_busy(l2); + rnr = 1; + } else + clear_peer_busy(l2); + + if (test_bit(FLG_MOD128, &l2->flag)) { + PollFlag = (msg->data[1] & 0x1) == 0x1; + nr = msg->data[1] >> 1; + } else { + PollFlag = (msg->data[0] & 0x10); + nr = (msg->data[0] >> 5) & 0x7; + } + free_msg(msg); + if (rsp && PollFlag) { + if (legalnr(l2, nr)) { + if (rnr) { + restart_t200(l2, 15); + } else { + stop_t200(l2, 16); + FsmAddTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 5); + setva(l2, nr); + } + invoke_retransmission(l2, nr); + FsmChangeState(fi, ST_L2_7); + if (msg_queue_len(&l2->i_queue) && cansend(l2)) + FsmEvent(fi, EV_L2_ACK_PULL, NULL); + } else + nrerrorrecovery(fi); + } else { + if (!rsp && PollFlag) + enquiry_response(l2); + if (legalnr(l2, nr)) { + setva(l2, nr); + } else + nrerrorrecovery(fi); + } +} + +static void +l2_got_FRMR(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + msg_pull(msg, l2addrsize(l2) + 1); + + if (!(msg->data[0] & 1) || ((msg->data[0] & 3) == 1) || /* I or S */ + (IsUA(msg->data) && (fi->state == ST_L2_7))) { + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'K'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); + } + free_msg(msg); +} + +static void +l2_st24_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + msg_queue_purge(&l2->ui_queue); + l2->tei = -1; + FsmChangeState(fi, ST_L2_1); + free_msg(msg); +} + +static void +l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + msg_queue_purge(&l2->ui_queue); + l2->tei = -1; + msg_trim(msg, mISDN_HEAD_SIZE); + if (l2up(l2, DL_RELEASE | INDICATION, CES(l2), msg)) + free_msg(msg); + FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + msg_queue_purge(&l2->i_queue); + msg_queue_purge(&l2->ui_queue); + freewin(l2); + l2->tei = -1; + stop_t200(l2, 17); + st5_dl_release_l2l3(l2); + FsmChangeState(fi, ST_L2_1); + free_msg(msg); +} + +static void +l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + msg_queue_purge(&l2->ui_queue); + l2->tei = -1; + stop_t200(l2, 18); + if (l2up(l2, DL_RELEASE | CONFIRM, CES(l2), msg)) + free_msg(msg); + FsmChangeState(fi, ST_L2_1); +} + +static void +l2_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + msg_queue_purge(&l2->i_queue); + msg_queue_purge(&l2->ui_queue); + freewin(l2); + l2->tei = -1; + stop_t200(l2, 17); + FsmDelTimer(&l2->t203, 19); + if (l2up(l2, DL_RELEASE | INDICATION, CES(l2), msg)) + free_msg(msg); + FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + msg_queue_purge(&l2->i_queue); + msg_queue_purge(&l2->ui_queue); + if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag)) + if (!l2up(l2, DL_RELEASE | INDICATION, CES(l2), msg)) + return; + free_msg(msg); +} + +static void +l2_st5_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + msg_queue_purge(&l2->i_queue); + msg_queue_purge(&l2->ui_queue); + freewin(l2); + stop_t200(l2, 19); + st5_dl_release_l2l3(l2); + FsmChangeState(fi, ST_L2_4); + free_msg(msg); +} + +static void +l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + msg_queue_purge(&l2->ui_queue); + stop_t200(l2, 20); + if (l2up(l2, DL_RELEASE | CONFIRM, CES(l2), msg)) + free_msg(msg); + FsmChangeState(fi, ST_L2_4); +} + +static void +l2_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + msg_queue_purge(&l2->i_queue); + msg_queue_purge(&l2->ui_queue); + freewin(l2); + stop_t200(l2, 19); + FsmDelTimer(&l2->t203, 19); + if (l2up(l2, DL_RELEASE | INDICATION, CES(l2), msg)) + free_msg(msg); + FsmChangeState(fi, ST_L2_4); +} + +static void +l2_set_own_busy(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + if(!test_and_set_bit(FLG_OWN_BUSY, &l2->flag)) { + enquiry_cr(l2, RNR, RSP, 0); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + } + if (msg) + free_msg(msg); +} + +static void +l2_clear_own_busy(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + msg_t *msg = arg; + + if(!test_and_clear_bit(FLG_OWN_BUSY, &l2->flag)) { + enquiry_cr(l2, RR, RSP, 0); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + } + if (msg) + free_msg(msg); +} + +static void +l2_frame_error(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + + l2mgr(l2, MDL_ERROR | INDICATION, arg); +} + +static void +l2_frame_error_reest(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + + l2mgr(l2, MDL_ERROR | INDICATION, arg); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); +} + +static struct FsmNode L2FnList[] = +{ + {ST_L2_1, EV_L2_DL_ESTABLISH_REQ, l2_mdl_assign}, + {ST_L2_2, EV_L2_DL_ESTABLISH_REQ, l2_go_st3}, + {ST_L2_4, EV_L2_DL_ESTABLISH_REQ, l2_establish}, + {ST_L2_5, EV_L2_DL_ESTABLISH_REQ, l2_discard_i_setl3}, + {ST_L2_7, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, + {ST_L2_8, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, + {ST_L2_4, EV_L2_DL_RELEASE_REQ, l2_release}, + {ST_L2_5, EV_L2_DL_RELEASE_REQ, l2_pend_rel}, + {ST_L2_7, EV_L2_DL_RELEASE_REQ, l2_disconnect}, + {ST_L2_8, EV_L2_DL_RELEASE_REQ, l2_disconnect}, + {ST_L2_5, EV_L2_DL_DATA, l2_feed_i_if_reest}, + {ST_L2_7, EV_L2_DL_DATA, l2_feed_i_pull}, + {ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue}, + {ST_L2_1, EV_L2_DL_UNITDATA, l2_queue_ui_assign}, + {ST_L2_2, EV_L2_DL_UNITDATA, l2_queue_ui}, + {ST_L2_3, EV_L2_DL_UNITDATA, l2_queue_ui}, + {ST_L2_4, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_5, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_6, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_7, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_8, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ERROR, l2_st24_tei_remove}, + {ST_L2_3, EV_L2_MDL_ERROR, l2_st3_tei_remove}, + {ST_L2_4, EV_L2_MDL_REMOVE, l2_st24_tei_remove}, + {ST_L2_5, EV_L2_MDL_REMOVE, l2_st5_tei_remove}, + {ST_L2_6, EV_L2_MDL_REMOVE, l2_st6_tei_remove}, + {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_4, EV_L2_SABME, l2_start_multi}, + {ST_L2_5, EV_L2_SABME, l2_send_UA}, + {ST_L2_6, EV_L2_SABME, l2_send_DM}, + {ST_L2_7, EV_L2_SABME, l2_restart_multi}, + {ST_L2_8, EV_L2_SABME, l2_restart_multi}, + {ST_L2_4, EV_L2_DISC, l2_send_DM}, + {ST_L2_5, EV_L2_DISC, l2_send_DM}, + {ST_L2_6, EV_L2_DISC, l2_send_UA}, + {ST_L2_7, EV_L2_DISC, l2_stop_multi}, + {ST_L2_8, EV_L2_DISC, l2_stop_multi}, + {ST_L2_4, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_5, EV_L2_UA, l2_connected}, + {ST_L2_6, EV_L2_UA, l2_released}, + {ST_L2_7, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_8, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_4, EV_L2_DM, l2_reestablish}, + {ST_L2_5, EV_L2_DM, l2_st5_dm_release}, + {ST_L2_6, EV_L2_DM, l2_st6_dm_release}, + {ST_L2_7, EV_L2_DM, l2_mdl_error_dm}, + {ST_L2_8, EV_L2_DM, l2_st8_mdl_error_dm}, + {ST_L2_1, EV_L2_UI, l2_got_ui}, + {ST_L2_2, EV_L2_UI, l2_got_ui}, + {ST_L2_3, EV_L2_UI, l2_got_ui}, + {ST_L2_4, EV_L2_UI, l2_got_ui}, + {ST_L2_5, EV_L2_UI, l2_got_ui}, + {ST_L2_6, EV_L2_UI, l2_got_ui}, + {ST_L2_7, EV_L2_UI, l2_got_ui}, + {ST_L2_8, EV_L2_UI, l2_got_ui}, + {ST_L2_7, EV_L2_FRMR, l2_got_FRMR}, + {ST_L2_8, EV_L2_FRMR, l2_got_FRMR}, + {ST_L2_7, EV_L2_SUPER, l2_st7_got_super}, + {ST_L2_8, EV_L2_SUPER, l2_st8_got_super}, + {ST_L2_7, EV_L2_I, l2_got_iframe}, + {ST_L2_8, EV_L2_I, l2_got_iframe}, + {ST_L2_5, EV_L2_T200, l2_st5_tout_200}, + {ST_L2_6, EV_L2_T200, l2_st6_tout_200}, + {ST_L2_7, EV_L2_T200, l2_st7_tout_200}, + {ST_L2_8, EV_L2_T200, l2_st8_tout_200}, + {ST_L2_7, EV_L2_T203, l2_st7_tout_203}, + {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue}, + {ST_L2_7, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, + {ST_L2_8, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, + {ST_L2_7, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, + {ST_L2_8, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, + {ST_L2_4, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_5, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_6, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_7, EV_L2_FRAME_ERROR, l2_frame_error_reest}, + {ST_L2_8, EV_L2_FRAME_ERROR, l2_frame_error_reest}, + {ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistant_da}, + {ST_L2_2, EV_L1_DEACTIVATE, l2_st24_tei_remove}, + {ST_L2_3, EV_L1_DEACTIVATE, l2_st3_tei_remove}, + {ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistant_da}, + {ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistant_da}, + {ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistant_da}, + {ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da}, +}; + +#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode)) + +static layer2_t * +select_l2(net_stack_t *nst, int sapi, int tei) { + layer2_t *l2; + + l2 = nst->layer2; + while (l2) { + if ((l2->sapi == sapi) && (l2->tei == tei)) + break; + l2 = l2->next; + } + return(l2); +} + +static int +ph_data_mux(net_stack_t *nst, iframe_t *frm, msg_t *msg) +{ + u_char *datap; + layer2_t *l2; + int ret = -EINVAL; + int psapi, ptei; + mISDN_head_t *hh; + int c = 0; + + datap = msg_pull(msg, IFRAME_HEAD_SIZE); + if (msg->len <= 2) { + dprint(DBGM_L2, "%s: msg (%d)too shoort\n", __FUNCTION__, + msg->len); + msg_push(msg, IFRAME_HEAD_SIZE); + return(ret); + } + psapi = *datap++; + ptei = *datap++; + if ((psapi & 1) || !(ptei & 1)) { + dprint(DBGM_L2, "l2 D-channel frame wrong EA0/EA1\n"); + msg_push(msg, IFRAME_HEAD_SIZE); + return(ret); + } + psapi >>= 2; + ptei >>= 1; + dprint(DBGM_L2, "%s: sapi(%d) tei(%d)\n", __FUNCTION__, psapi, ptei); + if (ptei == GROUP_TEI) { + if (psapi == TEI_SAPI) { + hh = (mISDN_head_t *)msg_push(msg, mISDN_HEAD_SIZE); + hh->prim = MDL_UNITDATA | INDICATION; + return(tei_mux(nst, msg)); + } else { + dprint(DBGM_L2, "%s: unknown tei(%d) msg\n", __FUNCTION__, + ptei); + } + } + l2 = select_l2(nst, psapi, ptei); + if (!l2) { + dprint(DBGM_L2, "%s: no l2 for sapi(%d) tei(%d)\n", __FUNCTION__, + psapi, ptei); + return(-ENXIO); + } + if (!(*datap & 1)) { /* I-Frame */ + if(!(c = iframe_error(l2, msg))) + ret = FsmEvent(&l2->l2m, EV_L2_I, msg); + } else if (IsSFrame(datap, l2)) { /* S-Frame */ + if(!(c = super_error(l2, msg))) + ret = FsmEvent(&l2->l2m, EV_L2_SUPER, msg); + } else if (IsUI(datap)) { + if(!(c = UI_error(l2, msg))) + ret = FsmEvent(&l2->l2m, EV_L2_UI, msg); + } else if (IsSABME(datap, l2)) { + if(!(c = unnum_error(l2, msg, CMD))) + ret = FsmEvent(&l2->l2m, EV_L2_SABME, msg); + } else if (IsUA(datap)) { + if(!(c = unnum_error(l2, msg, RSP))) + ret = FsmEvent(&l2->l2m, EV_L2_UA, msg); + } else if (IsDISC(datap)) { + if(!(c = unnum_error(l2, msg, CMD))) + ret = FsmEvent(&l2->l2m, EV_L2_DISC, msg); + } else if (IsDM(datap)) { + if(!(c = unnum_error(l2, msg, RSP))) + ret = FsmEvent(&l2->l2m, EV_L2_DM, msg); + } else if (IsFRMR(datap)) { + if(!(c = FRMR_error(l2, msg))) + ret = FsmEvent(&l2->l2m, EV_L2_FRMR, msg); + } else { + c = 'L'; + } + if (c) { + dprint(DBGM_L2, "l2 D-channel frame error %c\n",c); + FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *)(long)c); + } + if (ret) + free_msg(msg); + return(0); +} + +int +msg_mux(net_stack_t *nst, iframe_t *frm, msg_t *msg) +{ + layer2_t *l2; + int ret = -EINVAL; + msg_t *nmsg; + + dprint(DBGM_L2, "%s: msg len(%d)\n", __FUNCTION__, msg->len); + dprint(DBGM_L2, "%s: adr(%x) pr(%x) di(%x) len(%d)\n", __FUNCTION__, + frm->addr, frm->prim, frm->dinfo, frm->len); + l2 = nst->layer2; + while(l2) { + if (frm->prim == (PH_CONTROL | INDICATION)) { + if (frm->dinfo == HW_D_BLOCKED) + test_and_set_bit(FLG_DCHAN_BUSY, &l2->flag); + else if (frm->dinfo == HW_D_NOBLOCKED) + test_and_clear_bit(FLG_DCHAN_BUSY, &l2->flag); + l2 = l2->next; + continue; + } + if (l2->next) { + nmsg = msg_copy(msg); + } else + nmsg = msg; + ret = -EINVAL; + switch (frm->prim) { + case (PH_ACTIVATE | CONFIRM): + case (PH_ACTIVATE | INDICATION): + test_and_set_bit(FLG_L1_ACTIV, &l2->flag); + if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag)) + ret = FsmEvent(&l2->l2m, + EV_L2_DL_ESTABLISH_REQ, nmsg); + break; + case (PH_DEACTIVATE | INDICATION): + case (PH_DEACTIVATE | CONFIRM): + test_and_clear_bit(FLG_L1_ACTIV, &l2->flag); + ret = FsmEvent(&l2->l2m, EV_L1_DEACTIVATE, nmsg); + break; + default: + l2m_debug(&l2->l2m, "l2 unknown pr %x", frm->prim); + break; + } + if (ret) + free_msg(nmsg); + ret = 0; + l2 = l2->next; + } + if (ret) + free_msg(msg); + return(0); +} + +int +l2muxer(net_stack_t *nst, msg_t *msg) +{ + iframe_t *frm; + int ret = -EINVAL; + + frm = (iframe_t *)msg->data; + switch(frm->prim) { + case (PH_DATA_IND): + ret = ph_data_mux(nst, frm, msg); + break; + case (PH_DATA | CONFIRM): + ret = phd_conf(nst, frm, msg); + break; + case (PH_ACTIVATE | CONFIRM): + case (PH_ACTIVATE | INDICATION): + case (PH_CONTROL | INDICATION): + case (PH_DEACTIVATE | INDICATION): + case (PH_DEACTIVATE | CONFIRM): + ret = msg_mux(nst, frm, msg); + break; + default: + dprint(DBGM_L2, "%s: pr %x\n", __FUNCTION__, frm->prim); + break; + } + return(ret); +} + +static int +l2from_up(net_stack_t *nst, msg_t *msg) { + layer2_t *l2; + mISDN_head_t *hh; + int ret = -EINVAL; + + if (!msg) + return(ret); + hh = (mISDN_head_t *)msg->data; + if (msg->len < mISDN_FRAME_MIN) + return(ret); + dprint(DBGM_L2, "%s: prim(%x) dinfo(%x)\n", __FUNCTION__, + hh->prim, hh->dinfo); + l2 = select_l2(nst, SAPITEI(hh->dinfo)); + if (!l2) { + dprint(DBGM_L2, "%s: no l2 for sapi(%d) tei(%d)\n", __FUNCTION__, + SAPITEI(hh->dinfo)); + return(-ENXIO); + } + switch (hh->prim) { + case (DL_DATA | REQUEST): + ret = FsmEvent(&l2->l2m, EV_L2_DL_DATA, msg); + break; + case (DL_UNITDATA | REQUEST): + ret = FsmEvent(&l2->l2m, EV_L2_DL_UNITDATA, msg); + break; + case (DL_ESTABLISH | REQUEST): + if (test_bit(FLG_L1_ACTIV, &l2->flag)) { + if (test_bit(FLG_LAPD, &l2->flag) || + test_bit(FLG_ORIG, &l2->flag)) { + ret = FsmEvent(&l2->l2m, + EV_L2_DL_ESTABLISH_REQ, msg); + } + } else { + if (test_bit(FLG_LAPD, &l2->flag) || + test_bit(FLG_ORIG, &l2->flag)) { + test_and_set_bit(FLG_ESTAB_PEND, + &l2->flag); + } + ret = l2down(l2, PH_ACTIVATE | REQUEST, 0, msg); + } + break; + case (DL_RELEASE | REQUEST): + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE | REQUEST, + 0, 0, NULL); + ret = FsmEvent(&l2->l2m, EV_L2_DL_RELEASE_REQ, msg); + break; + case (MDL_ASSIGN | REQUEST): + ret = FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, msg); + break; + case (MDL_REMOVE | REQUEST): + ret = FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, msg); + break; + case (MDL_ERROR | RESPONSE): + ret = FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, msg); + case (MDL_STATUS | REQUEST): + l2up_create(l2, MDL_STATUS | CONFIRM, hh->dinfo, 1, + (void *)l2->tei); + break; + default: + l2m_debug(&l2->l2m, "l2 unknown pr %04x", hh->prim); + } + return(ret); +} + +int +tei_l2(layer2_t *l2, msg_t *msg) +{ + mISDN_head_t *hh = (mISDN_head_t *)msg->data; + int ret = -EINVAL; + + if (!l2 || !msg) + return(ret); + dprint(DBGM_L2, "%s: prim(%x)\n", __FUNCTION__, hh->prim); + if (msg->len < mISDN_FRAME_MIN) + return(ret); + switch(hh->prim) { + case (MDL_UNITDATA | REQUEST): + ret = l2down(l2, PH_DATA_REQ, hh->dinfo, msg); + break; + case (MDL_ASSIGN | REQUEST): + ret = FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, msg); + break; + case (MDL_REMOVE | REQUEST): + ret = FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, msg); + break; + case (MDL_ERROR | RESPONSE): + ret = FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, msg); + break; + case (MDL_FINDTEI | REQUEST): + ret = l2down_msg(l2, msg); + break; + } + return(ret); +} + +static void +l2m_debug(struct FsmInst *fi, char *fmt, ...) +{ + char tbuf[128]; + va_list args; + + va_start(args, fmt); + vsprintf(tbuf, fmt, args); + dprint(DBGM_L2, "L2 %s\n", tbuf); + va_end(args); +} + +static void +release_l2(layer2_t *l2) +{ + dprint(DBGM_L2, "%s: sapi(%d) tei(%d) state(%d)\n", __FUNCTION__, + l2->sapi, l2->tei, l2->l2m.state); + FsmRemoveTimer(&l2->t200); + FsmRemoveTimer(&l2->t203); + msg_queue_purge(&l2->i_queue); + msg_queue_purge(&l2->ui_queue); + ReleaseWin(l2); + if (test_bit(FLG_LAPD, &l2->flag)) + release_tei(l2->tm); + REMOVE_FROM_LISTBASE(l2, l2->nst->layer2); + free(l2); +} + +layer2_t * +new_dl2(net_stack_t *nst, int tei) { + layer2_t *nl2; + + if (!(nl2 = malloc(sizeof(layer2_t)))) { + dprint(DBGM_L2, "malloc layer2 failed\n"); + return(NULL); + } + memset(nl2, 0, sizeof(layer2_t)); + nl2->nst = nst; + nl2->debug = debug; + test_and_set_bit(FLG_LAPD, &nl2->flag); + test_and_set_bit(FLG_LAPD_NET, &nl2->flag); + test_and_set_bit(FLG_FIXED_TEI, &nl2->flag); + test_and_set_bit(FLG_MOD128, &nl2->flag); + nl2->sapi = 0; + nl2->tei = tei; + nl2->maxlen = MAX_DFRAME_LEN; + nl2->window = 1; + nl2->T200 = 1000; + nl2->N200 = 3; + nl2->T203 = 10000; + if (create_teimgr(nl2)) { + free(nl2); + return(NULL); + } + msg_queue_init(&nl2->i_queue); + msg_queue_init(&nl2->ui_queue); + InitWin(nl2); + nl2->l2m.fsm = &l2fsm; + nl2->l2m.state = ST_L2_4; + nl2->l2m.debug = debug; + nl2->l2m.nst = nl2->nst; + nl2->l2m.userdata = nl2; + nl2->l2m.userint = 0; + nl2->l2m.printdebug = l2m_debug; + FsmInitTimer(&nl2->l2m, &nl2->t200); + FsmInitTimer(&nl2->l2m, &nl2->t203); + APPEND_TO_LIST(nl2, nst->layer2); + return(nl2); +} + +int Isdnl2Init(net_stack_t *nst) +{ + layer2_t *l2; + msg_t *msg; + + l2fsm.state_count = L2_STATE_COUNT; + l2fsm.event_count = L2_EVENT_COUNT; + l2fsm.strEvent = strL2Event; + l2fsm.strState = strL2State; + FsmNew(&l2fsm, L2FnList, L2_FN_COUNT); + TEIInit(); + nst->l1_l2 = l2muxer; + nst->l3_l2 = l2from_up; + l2 = new_dl2(nst, 127); + if (l2) { + if ((msg = create_link_msg(MDL_REMOVE | INDICATION, 127, + 0, NULL, 0))) { + if (l2_tei(l2->tm, msg)) + free_msg(msg); + } + } + return(0); +} + +void cleanup_Isdnl2(net_stack_t *nst) +{ + if(nst->layer2) { + dprint(DBGM_L2, "%s: l2 list not empty\n", __FUNCTION__); + while(nst->layer2) + release_l2(nst->layer2); + } + TEIFree(); + FsmFree(&l2fsm); +} diff --git a/i4lnet/net_l2.h b/i4lnet/net_l2.h new file mode 100644 index 0000000..5721009 --- /dev/null +++ b/i4lnet/net_l2.h @@ -0,0 +1,118 @@ +/* $Id: net_l2.h,v 0.9 2003/08/27 07:33:03 kkeil Exp $ + * + * Layer 2 defines + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#ifndef NET_L2_H +#define NET_L2_H + +#include +#include "mISDNlib.h" +#include "isdn_net.h" +#include "fsm.h" +#ifdef MEMDBG +#include "memdbg.h" +#endif + +#define MAX_WINDOW 8 + +typedef struct _teimgr { + int ri; + struct FsmInst tei_m; + struct FsmTimer t201; + int T201; + int debug; + int val; + struct _layer2 *l2; +} teimgr_t; + +struct _layer2 { + struct _layer2 *prev; + struct _layer2 *next; + int sapi; + int tei; + laddr_t addr; + int maxlen; + teimgr_t *tm; + u_int flag; + u_int vs, va, vr; + int rc; + u_int window; + u_int sow; + struct FsmInst l2m; + struct FsmTimer t200, t203; + int T200, N200, T203; + int debug; + msg_t *windowar[MAX_WINDOW]; + net_stack_t *nst; + msg_queue_t i_queue; + msg_queue_t ui_queue; +}; + +#define SAPITEI(ces) (ces>>8)&0xff, ces&0xff + +static inline int CES(layer2_t *l2) { + return(l2->tei | (l2->sapi << 8)); +} + +/* from mISDN_l2.c */ +extern layer2_t *new_dl2(net_stack_t *nst, int tei); +extern int tei_l2(layer2_t *l2, msg_t *msg); +extern int Isdnl2Init(net_stack_t *nst); +extern void cleanup_Isdnl2(net_stack_t *nst); + + +/* from tei.c */ +extern int tei_mux(net_stack_t *nst, msg_t *msg); +extern int l2_tei(teimgr_t *tm, msg_t *msg); +extern int create_teimgr(layer2_t *l2); +extern void release_tei(teimgr_t *tm); +extern int TEIInit(void); +extern void TEIFree(void); + +#define GROUP_TEI 127 +#define TEI_SAPI 63 +#define CTRL_SAPI 0 + +#define RR 0x01 +#define RNR 0x05 +#define REJ 0x09 +#define SABME 0x6f +#define SABM 0x2f +#define DM 0x0f +#define UI 0x03 +#define DISC 0x43 +#define UA 0x63 +#define FRMR 0x87 +#define XID 0xaf + +#define CMD 0 +#define RSP 1 + +#define LC_FLUSH_WAIT 1 + +#define FLG_LAPB 0 +#define FLG_LAPD 1 +#define FLG_ORIG 2 +#define FLG_MOD128 3 +#define FLG_PEND_REL 4 +#define FLG_L3_INIT 5 +#define FLG_T200_RUN 6 +#define FLG_ACK_PEND 7 +#define FLG_REJEXC 8 +#define FLG_OWN_BUSY 9 +#define FLG_PEER_BUSY 10 +#define FLG_DCHAN_BUSY 11 +#define FLG_L1_ACTIV 12 +#define FLG_ESTAB_PEND 13 +#define FLG_PTP 14 +#define FLG_FIXED_TEI 15 +#define FLG_L2BLOCK 16 +#define FLG_L1_BUSY 17 +#define FLG_LAPD_NET 18 +#define FLG_TEI_T201_1 19 + +#endif diff --git a/i4lnet/net_l3.c b/i4lnet/net_l3.c new file mode 100644 index 0000000..6b688fa --- /dev/null +++ b/i4lnet/net_l3.c @@ -0,0 +1,2213 @@ +/* $Id: net_l3.c,v 0.9 2003/08/27 07:33:03 kkeil Exp $ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/mISDN.cert + * + */ + +#include +#include +#include "mISDNlib.h" +#include "net_l3.h" +#include "l3dss1.h" +#include "helper.h" +// #include "debug.h" + +const char *l3_revision = "$Revision: 0.9 $"; + +#define PROTO_DIS_EURO 8 + +#define L3_DEB_WARN 1 +#define L3_DEB_PROTERR 2 +#define L3_DEB_STATE 4 +#define L3_DEB_PROC 8 +#define L3_DEB_CHECK 16 + +enum { + ST_L3_LC_REL, + ST_L3_LC_ESTAB_WAIT, + ST_L3_LC_REL_DELAY, + ST_L3_LC_REL_WAIT, + ST_L3_LC_ESTAB, +}; + +enum { + IMSG_END_PROC, + IMSG_END_PROC_M, + IMSG_L2_DATA, + IMSG_L4_DATA, + IMSG_TIMER_EXPIRED, + IMSG_MASTER_L2_DATA, + IMSG_ALERTING_IND, + IMSG_CONNECT_IND, + IMSG_SEL_PROC, + IMSG_RELEASE_CHILDS, +}; + +static int send_proc(layer3_proc_t *proc, int op, void *arg); +static int l3_msg(layer3_t *l3, u_int pr, int dinfo, void *arg); +static int mISDN_l3up(layer3_proc_t *, msg_t *); +static int l3down(layer3_t *l3, u_int prim, int dinfo, msg_t *msg); + +struct _l3_msg { + int mt; + msg_t *msg; +}; + +struct stateentry { + int state; + int primitive; + void (*rout) (layer3_proc_t *, int, void *); +}; + +#define SBIT(state) (1<l3->debug & L3_DEB_STATE) + l3_debug(pc->l3, "newstate cr %d %d%s --> %d%s", + pc->callref & 0x7F, + pc->state, pc->master ? "i" : "", + state, pc->master ? "i" : ""); + pc->state = state; +} + +static void +L3ExpireTimer(L3Timer_t *t) +{ + if (t->pc->l3->debug & L3_DEB_STATE) + l3_debug(t->pc->l3, "timer nr %x expired", t->nr); + send_proc(t->pc, IMSG_TIMER_EXPIRED, &t->nr); +} + +void +L3InitTimer(layer3_proc_t *pc, L3Timer_t *t) +{ + t->pc = pc; + t->tl.function = (void *) L3ExpireTimer; + t->tl.data = (long) t; + init_timer(&t->tl, pc->l3->nst); +} + +void +L3DelTimer(L3Timer_t *t) +{ + del_timer(&t->tl); +} + +int +L3AddTimer(L3Timer_t *t, int millisec, int timer_nr) +{ + if (timer_pending(&t->tl)) { + dprint(DBGM_L3, "L3AddTimer: timer already active!\n"); + return -1; + } + init_timer(&t->tl, t->pc->l3->nst); + t->nr = timer_nr; + t->tl.expires = millisec; + add_timer(&t->tl); + return 0; +} + +void +StopAllL3Timer(layer3_proc_t *pc) +{ + L3DelTimer(&pc->timer1); + L3DelTimer(&pc->timer2); +} + +void +RemoveAllL3Timer(layer3_proc_t *pc) +{ + int ret; + + ret = remove_timer(&pc->timer1.tl); + if (ret) + dprint(DBGM_L3, "RemoveL3Timer1: ret %d\n", ret); + ret = remove_timer(&pc->timer2.tl); + if (ret) + dprint(DBGM_L3, "RemoveL3Timer2: ret %d\n", ret); +} + +static layer3_proc_t * +create_proc(layer3_t *l3, int ces, int cr, layer3_proc_t *master) +{ + layer3_proc_t *l3p; + + l3p = malloc(sizeof(layer3_proc_t)); + if (l3p) { + memset(l3p, 0, sizeof(layer3_proc_t)); + l3p->l3 = l3; + l3p->ces = ces; + l3p->callref = cr; + l3p->master = master; + L3InitTimer(l3p, &l3p->timer1); + L3InitTimer(l3p, &l3p->timer2); + if (master) { + APPEND_TO_LIST(l3p, master->child); + } + } + return(l3p); +} + +static layer3_proc_t * +find_proc(layer3_proc_t *master, int ces, int cr) +{ + layer3_proc_t *p = master; + layer3_proc_t *cp; + + dprint(DBGM_L3, "%s: ces(%x) cr(%x)\n", __FUNCTION__, + ces, cr); + while(p) { + dprint(DBGM_L3, "%s: proc %p ces(%x) cr(%x)\n", __FUNCTION__, + p, p->ces, p->callref); + if ((p->ces == ces) && (p->callref == cr)) + break; + if (p->child) { + cp = find_proc(p->child, ces, cr); + if (cp) + return(cp); + } + if (((p->ces & 0xfffffff0) == 0xfff0) && (p->callref == cr)) + break; + p = p->next; + } + return(p); +} + +u_char * +findie(u_char * p, int size, u_char ie, int wanted_set) +{ + int l, codeset, maincodeset; + u_char *pend = p + size; + + /* skip protocol discriminator, callref and message type */ + p++; + l = (*p++) & 0xf; + p += l; + p++; + codeset = 0; + maincodeset = 0; + /* while there are bytes left... */ + while (p < pend) { + if ((*p & 0xf0) == 0x90) { + codeset = *p & 0x07; + if (!(*p & 0x08)) + maincodeset = codeset; + } + if (codeset == wanted_set) { + if (*p == ie) { + /* improved length check (Werner Cornelius) */ + if (!(*p & 0x80)) { + if ((pend - p) < 2) + return(NULL); + if (*(p+1) > (pend - (p+2))) + return(NULL); + p++; /* points to len */ + } + return (p); + } else if ((*p > ie) && !(*p & 0x80)) + return (NULL); + } + if (!(*p & 0x80)) { + p++; + l = *p; + p += l; + codeset = maincodeset; + } + p++; + } + return (NULL); +} + +u_char * +find_and_copy_ie(u_char * p, int size, u_char ie, int wanted_set, msg_t *msg) +{ + u_char *iep, *mp; + int l; + + iep = findie(p, size, ie, wanted_set); + if (iep) { + l = 1; + if (!(ie & 0x80)) + l += *iep; + mp = msg_put(msg, l); + memcpy(mp, iep, l); + iep = mp; + } + return(iep); +} + +static void MsgStart(layer3_proc_t *pc, u_char mt) { + pc->op = &pc->obuf[0]; + *pc->op++ = 8; + if (pc->callref == -1) { /* dummy cr */ + *pc->op++ = 0; + } else { + *pc->op++ = 1; + *pc->op++ = pc->callref ^ 0x80; + } + *pc->op++ = mt; +} + +static void AddvarIE(layer3_proc_t *pc, u_char ie, u_char *iep) { + u_char len = *iep; + + *pc->op++ = ie; + *pc->op++ = *iep++; + while(len--) + *pc->op++ = *iep++; +} + +static int SendMsg(layer3_proc_t *pc, int state) { + int l; + int ret; + msg_t *msg; + + l = pc->op - &pc->obuf[0]; + if (!(msg = l3_alloc_msg(l))) + return(-ENOMEM); + memcpy(msg_put(msg, l), &pc->obuf[0], l); + dhexprint(DBGM_L3DATA, "l3 oframe:", &pc->obuf[0], l); + if (state != -1) + newl3state(pc, state); + if ((ret = l3_msg(pc->l3, DL_DATA | REQUEST, pc->ces, msg))) + free_msg(msg); + return(ret); +} + +static int +l3dss1_message(layer3_proc_t *pc, u_char mt) +{ + msg_t *msg; + u_char *p; + int ret; + + if (!(msg = l3_alloc_msg(4))) + return(-ENOMEM); + p = msg_put(msg, 4); + *p++ = 8; + *p++ = 1; + *p++ = pc->callref ^ 0x80; + *p++ = mt; + dhexprint(DBGM_L3DATA, "l3 oframe:", msg->data, 4); + if ((ret=l3_msg(pc->l3, DL_DATA | REQUEST, pc->ces, msg))) + free_msg(msg); + return(ret); +} + +static void +l3dss1_message_cause(layer3_proc_t *pc, u_char mt, u_char cause) +{ + MsgStart(pc, mt); + if (cause) { + *pc->op++ = IE_CAUSE; + *pc->op++ = 0x2; + *pc->op++ = 0x80 | CAUSE_LOC_PNET_LOCUSER; + *pc->op++ = 0x80 | cause; + } + SendMsg(pc, -1); +} + +static void +l3dss1_status_send(layer3_proc_t *pc, u_char cause) +{ + + MsgStart(pc, MT_STATUS); + *pc->op++ = IE_CAUSE; + *pc->op++ = 2; + *pc->op++ = 0x80 | CAUSE_LOC_USER; + *pc->op++ = 0x80 | cause; + + *pc->op++ = IE_CALL_STATE; + *pc->op++ = 1; + *pc->op++ = pc->state & 0x3f; + SendMsg(pc, -1); +} + +static void +l3dss1_msg_without_setup(layer3_proc_t *pc, u_char cause) +{ + /* This routine is called if here was no SETUP made (checks in dss1up and in + * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code + * MT_STATUS_ENQUIRE in the NULL state is handled too + */ + switch (cause) { + case 81: /* invalid callreference */ + case 88: /* incomp destination */ + case 96: /* mandory IE missing */ + case 100: /* invalid IE contents */ + case 101: /* incompatible Callstate */ + l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause); + break; + default: + dprint(DBGM_L3, "mISDN l3dss1_msg_without_setup wrong cause %d\n", + cause); + } + send_proc(pc, IMSG_END_PROC, NULL); +} + +static int +l3dss1_check_messagetype_validity(layer3_proc_t *pc, int mt, void *arg) +{ + switch (mt) { + case MT_ALERTING: + case MT_CALL_PROCEEDING: + case MT_CONNECT: + case MT_CONNECT_ACKNOWLEDGE: + case MT_DISCONNECT: + case MT_INFORMATION: + case MT_FACILITY: + case MT_NOTIFY: + case MT_PROGRESS: + case MT_RELEASE: + case MT_RELEASE_COMPLETE: + case MT_SETUP: + case MT_SETUP_ACKNOWLEDGE: + case MT_RESUME_ACKNOWLEDGE: + case MT_RESUME_REJECT: + case MT_SUSPEND_ACKNOWLEDGE: + case MT_SUSPEND_REJECT: + case MT_USER_INFORMATION: + case MT_RESTART: + case MT_RESTART_ACKNOWLEDGE: + case MT_CONGESTION_CONTROL: + case MT_STATUS: + case MT_STATUS_ENQUIRY: + case MT_RESUME: /* RESUME only in user->net */ + case MT_SUSPEND: /* SUSPEND only in user->net */ + if (pc->l3->debug & L3_DEB_CHECK) + l3_debug(pc->l3, "l3dss1_check_messagetype_validity mt(%x) OK", mt); + break; + default: + if (pc->l3->debug & (L3_DEB_CHECK | L3_DEB_WARN)) + l3_debug(pc->l3, "l3dss1_check_messagetype_validity mt(%x) fail", mt); + l3dss1_status_send(pc, CAUSE_MT_NOTIMPLEMENTED); + return(1); + } + return(0); +} + +static void +l3dss1_std_ie_err(layer3_proc_t *pc, int ret) { + + if (pc->l3->debug & L3_DEB_CHECK) + l3_debug(pc->l3, "check_infoelements ret %d", ret); + switch(ret) { + case 0: + break; + case ERR_IE_COMPREHENSION: + l3dss1_status_send(pc, CAUSE_MANDATORY_IE_MISS); + break; + case ERR_IE_UNRECOGNIZED: + l3dss1_status_send(pc, CAUSE_IE_NOTIMPLEMENTED); + break; + case ERR_IE_LENGTH: + l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); + break; + case ERR_IE_SEQUENCE: + default: + break; + } +} + +static u_char * +l3dss1_get_channel_id(layer3_proc_t *pc, msg_t *omsg, msg_t *nmsg) { + u_char *sp, *p; + + if ((sp = p = findie(omsg->data, omsg->len, IE_CHANNEL_ID, 0))) { + if (*p != 1) { /* len for BRI = 1 */ + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "wrong chid len %d", *p); + pc->err = -2; + return (NULL); + } + p++; + if (*p & 0x60) { /* only base rate interface */ + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "wrong chid %x", *p); + pc->err = -3; + return (NULL); + } + pc->bc = *p & 3; + p = sp; + sp = msg_put(nmsg, 1 + *p); + memcpy(sp, p, 1 + *p); + } else + pc->err = -1; + return(sp); +} + +static u_char * +l3dss1_get_cause(layer3_proc_t *pc, msg_t *omsg, msg_t *nmsg) { + u_char l; + u_char *p, *sp; + + if ((sp = p = findie(omsg->data, omsg->len, IE_CAUSE, 0))) { + l = *p++; + if (l>30) { + pc->err = 1; + return(NULL); + } + if (l) + l--; + else { + pc->err = 2; + return(NULL); + } + if (l && !(*p & 0x80)) { + l--; + p++; /* skip recommendation */ + } + p++; + if (l) { + if (!(*p & 0x80)) { + pc->err = 3; + return(NULL); + } + pc->err = *p & 0x7F; + } else { + pc->err = 4; + return(NULL); + } + if (nmsg) { + p = sp; + sp = msg_put(nmsg, 1 + *p); + memcpy(sp, p, 1 + *p); + } + } else + pc->err = -1; + return(sp); +} + +static void +l3dss1_status_enq(layer3_proc_t *proc, int pr, void *arg) +{ +} + +static void +l3dss1_facility(layer3_proc_t *pc, int pr, void *arg) +{ + msg_t *umsg,*msg = arg; + FACILITY_t *fac; + + umsg = prep_l3data_msg(CC_FACILITY | INDICATION, pc->ces | + (pc->callref << 16), sizeof(FACILITY_t), msg->len, NULL); + if (!umsg) + return; + fac = (FACILITY_t *)(umsg->data + mISDN_HEAD_SIZE); + fac->FACILITY = + find_and_copy_ie(msg->data, msg->len, IE_FACILITY, 0, umsg); + if (mISDN_l3up(pc, umsg)) + free_msg(umsg); +} + +static void +l3dss1_userinfo(layer3_proc_t *pc, int pr, void *arg) +{ + msg_t *umsg,*msg = arg; + USER_INFORMATION_t *ui; + + umsg = prep_l3data_msg(CC_USER_INFORMATION | INDICATION, pc->ces | + (pc->callref << 16), sizeof(USER_INFORMATION_t), msg->len, NULL); + if (!umsg) + return; + ui = (USER_INFORMATION_t *)(umsg->data + mISDN_HEAD_SIZE); + ui->USER_USER = + find_and_copy_ie(msg->data, msg->len, IE_USER_USER, 0, umsg); + if (mISDN_l3up(pc, umsg)) + free_msg(umsg); +} + +static void +l3dss1_setup(layer3_proc_t *pc, int pr, void *arg) +{ + u_char *p; + int bcfound = 0; + msg_t *umsg,*msg = arg; + int err = 0; + SETUP_t *setup; + + umsg = prep_l3data_msg(CC_SETUP | INDICATION, pc->ces | + (pc->callref << 16), sizeof(SETUP_t), msg->len, NULL); + if (!umsg) + return; + setup = (SETUP_t *)(umsg->data + mISDN_HEAD_SIZE); + /* + * Bearer Capabilities + */ + /* only the first occurence 'll be detected ! */ + if ((p = setup->BEARER = find_and_copy_ie(msg->data, msg->len, + IE_BEARER, 0, umsg))) { + if ((p[0] < 2) || (p[0] > 11)) + err = 1; + else { + switch (p[1] & 0x7f) { + case 0x00: /* Speech */ + case 0x10: /* 3.1 Khz audio */ + case 0x08: /* Unrestricted digital information */ + case 0x09: /* Restricted digital information */ + case 0x11: + /* Unrestr. digital information with + * tones/announcements ( or 7 kHz audio + */ + case 0x18: /* Video */ + break; + default: + err = 2; + break; + } + switch (p[2] & 0x7f) { + case 0x40: /* packed mode */ + case 0x10: /* 64 kbit */ + case 0x11: /* 2*64 kbit */ + case 0x13: /* 384 kbit */ + case 0x15: /* 1536 kbit */ + case 0x17: /* 1920 kbit */ + break; + default: + err = 3; + break; + } + } + if (err) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "setup with wrong bearer(l=%d:%x,%x)", + p[0], p[1], p[2]); + l3dss1_msg_without_setup(pc, CAUSE_INVALID_CONTENTS); + free_msg(umsg); + return; + } + } else { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "setup without bearer capabilities"); + /* ETS 300-104 1.3.3 */ + l3dss1_msg_without_setup(pc, CAUSE_MANDATORY_IE_MISS); + free_msg(umsg); + return; + } + /* + * Channel Identification + */ + if ((setup->CHANNEL_ID = l3dss1_get_channel_id(pc, msg, umsg))) { + if (pc->bc) { + bcfound++; + } else { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "setup without bchannel, call waiting"); + bcfound++; + } + } else if (pc->err != -1) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "setup with wrong chid ret %d", pc->err); + } + /* Now we are on none mandatory IEs */ + + setup->COMPLETE = + find_and_copy_ie(msg->data, msg->len, IE_COMPLETE, 0, umsg); + setup->FACILITY = + find_and_copy_ie(msg->data, msg->len, IE_FACILITY, 0, umsg); + setup->PROGRESS = + find_and_copy_ie(msg->data, msg->len, IE_PROGRESS, 0, umsg); + setup->NET_FAC = + find_and_copy_ie(msg->data, msg->len, IE_NET_FAC, 0, umsg); + setup->SIGNAL = + find_and_copy_ie(msg->data, msg->len, IE_SIGNAL, 0, umsg); + setup->CALLED_PN = + find_and_copy_ie(msg->data, msg->len, IE_CALLED_PN, 0, umsg); + setup->CALLED_SUB = + find_and_copy_ie(msg->data, msg->len, IE_CALLED_SUB, 0, umsg); + setup->CALLING_PN = + find_and_copy_ie(msg->data, msg->len, IE_CALLING_PN, 0, umsg); + setup->CALLING_SUB = + find_and_copy_ie(msg->data, msg->len, IE_CALLING_SUB, 0, umsg); + setup->REDIR_NR = + find_and_copy_ie(msg->data, msg->len, IE_REDIR_NR, 0, umsg); + setup->LLC = + find_and_copy_ie(msg->data, msg->len, IE_LLC, 0, umsg); + setup->HLC = + find_and_copy_ie(msg->data, msg->len, IE_HLC, 0, umsg); + setup->USER_USER = + find_and_copy_ie(msg->data, msg->len, IE_USER_USER, 0, umsg); + newl3state(pc, 1); + L3DelTimer(&pc->timer2); + L3AddTimer(&pc->timer2, T_CTRL, 0x31f); + if (err) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, err); + if (mISDN_l3up(pc, umsg)) + free_msg(umsg); +} + + +static void +l3dss1_disconnect(layer3_proc_t *pc, int pr, void *arg) +{ + msg_t *umsg,*msg = arg; + DISCONNECT_t *disc; + + umsg = prep_l3data_msg(CC_DISCONNECT | INDICATION, pc->ces | + (pc->callref << 16), sizeof(DISCONNECT_t), msg->len, NULL); + if (!umsg) + return; + disc = (DISCONNECT_t *)(umsg->data + mISDN_HEAD_SIZE); + StopAllL3Timer(pc); + newl3state(pc, 11); + if (!(disc->CAUSE = l3dss1_get_cause(pc, msg, umsg))) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "DISC get_cause ret(%d)", pc->err); + } + disc->FACILITY = + find_and_copy_ie(msg->data, msg->len, IE_FACILITY, 0, umsg); + disc->SIGNAL = + find_and_copy_ie(msg->data, msg->len, IE_SIGNAL, 0, umsg); + disc->USER_USER = + find_and_copy_ie(msg->data, msg->len, IE_USER_USER, 0, umsg); + if (mISDN_l3up(pc, umsg)) + free_msg(umsg); +} + +static void +l3dss1_disconnect_i(layer3_proc_t *pc, int pr, void *arg) +{ + msg_t *umsg,*msg = arg; + DISCONNECT_t *disc; + u_char cause = 0; + + umsg = prep_l3data_msg(CC_DISCONNECT | INDICATION, pc->ces | + (pc->callref << 16), sizeof(DISCONNECT_t), msg->len, NULL); + if (!umsg) + return; + disc = (DISCONNECT_t *)(umsg->data + mISDN_HEAD_SIZE); + StopAllL3Timer(pc); + if (!(disc->CAUSE = l3dss1_get_cause(pc, msg, umsg))) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "DISC get_cause ret(%d)", pc->err); + if (pc->err<0) + cause = CAUSE_MANDATORY_IE_MISS; + else if (pc->err>0) + cause = CAUSE_INVALID_CONTENTS; + } + disc->FACILITY = + find_and_copy_ie(msg->data, msg->len, IE_FACILITY, 0, umsg); + disc->SIGNAL = + find_and_copy_ie(msg->data, msg->len, IE_SIGNAL, 0, umsg); + disc->USER_USER = + find_and_copy_ie(msg->data, msg->len, IE_USER_USER, 0, umsg); + if (cause) + l3dss1_message_cause(pc, MT_RELEASE, cause); + else + l3dss1_message(pc, MT_RELEASE); + newl3state(pc, 19); + test_and_clear_bit(FLG_L3P_TIMER308_1, &pc->Flags); + L3AddTimer(&pc->timer1, T308, 0x308); + if (mISDN_l3up(pc, umsg)) + free_msg(umsg); +} + +static void +l3dss1_information(layer3_proc_t *pc, int pr, void *arg) { + msg_t *umsg, *msg = arg; + INFORMATION_t *info; + + umsg = prep_l3data_msg(CC_INFORMATION | INDICATION, pc->ces | + (pc->callref << 16), sizeof(INFORMATION_t), msg->len, NULL); + if (!umsg) + return; + info = (INFORMATION_t *)(umsg->data + mISDN_HEAD_SIZE); + info->COMPLETE = + find_and_copy_ie(msg->data, msg->len, IE_COMPLETE, 0, umsg); + info->KEYPAD = + find_and_copy_ie(msg->data, msg->len, IE_KEYPAD, 0, umsg); + info->SIGNAL = + find_and_copy_ie(msg->data, msg->len, IE_SIGNAL, 0, umsg); + info->CALLED_PN = + find_and_copy_ie(msg->data, msg->len, IE_CALLED_PN, 0, umsg); + if (pc->state == 2) { /* overlap receiving */ + L3DelTimer(&pc->timer1); + L3AddTimer(&pc->timer1, T302, 0x302); + } + if (mISDN_l3up(pc, umsg)) + free_msg(umsg); +} + +static void +l3dss1_release(layer3_proc_t *pc, int pr, void *arg) +{ + msg_t *umsg, *msg = arg; + RELEASE_t *rel; + int cause=0; + + umsg = prep_l3data_msg(CC_RELEASE | INDICATION, pc->ces | + (pc->callref << 16), sizeof(RELEASE_t), msg->len, NULL); + if (!umsg) + return; + rel = (RELEASE_t *)(umsg->data + mISDN_HEAD_SIZE); + StopAllL3Timer(pc); + if (!(rel->CAUSE = l3dss1_get_cause(pc, msg, umsg))) { + if (pc->state != 12) + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "REL get_cause ret(%d)", + pc->err); + if ((pc->err<0) && (pc->state != 12)) + cause = CAUSE_MANDATORY_IE_MISS; + else if (pc->err>0) + cause = CAUSE_INVALID_CONTENTS; + } + rel->FACILITY = + find_and_copy_ie(msg->data, msg->len, IE_FACILITY, 0, umsg); + rel->SIGNAL = + find_and_copy_ie(msg->data, msg->len, IE_SIGNAL, 0, umsg); + rel->USER_USER = + find_and_copy_ie(msg->data, msg->len, IE_USER_USER, 0, umsg); + if (cause) + l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause); + else + l3dss1_message(pc, MT_RELEASE_COMPLETE); + if (mISDN_l3up(pc, umsg)) + free_msg(umsg); + newl3state(pc, 0); + send_proc(pc, IMSG_END_PROC_M, NULL); +} + +static void +l3dss1_release_i(layer3_proc_t *pc, int pr, void *arg) +{ + l3dss1_message(pc, MT_RELEASE_COMPLETE); + send_proc(pc, IMSG_END_PROC_M, NULL); +} + +static void +l3dss1_release_cmpl(layer3_proc_t *pc, int pr, void *arg) +{ + msg_t *umsg, *msg = arg; + RELEASE_COMPLETE_t *relc; + + umsg = prep_l3data_msg(CC_RELEASE_COMPLETE | INDICATION, pc->ces | + (pc->callref << 16), sizeof(RELEASE_COMPLETE_t), msg->len, NULL); + if (!umsg) + return; + relc = (RELEASE_COMPLETE_t *)(umsg->data + mISDN_HEAD_SIZE); + StopAllL3Timer(pc); + newl3state(pc, 0); + if (!(relc->CAUSE = l3dss1_get_cause(pc, msg, umsg))) { + if (pc->err > 0) + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "RELCMPL get_cause err(%d)", + pc->err); + } + relc->FACILITY = + find_and_copy_ie(msg->data, msg->len, IE_FACILITY, 0, umsg); + relc->SIGNAL = + find_and_copy_ie(msg->data, msg->len, IE_SIGNAL, 0, umsg); + relc->USER_USER = + find_and_copy_ie(msg->data, msg->len, IE_USER_USER, 0, umsg); + if (mISDN_l3up(pc, umsg)) + free_msg(umsg); + send_proc(pc, IMSG_END_PROC_M, NULL); +} + +static void +l3dss1_release_cmpl_i(layer3_proc_t *pc, int pr, void *arg) +{ + send_proc(pc, IMSG_END_PROC_M, NULL); +} + +static void +l3dss1_alerting_i(layer3_proc_t *pc, int pr, void *arg) +{ + msg_t *umsg, *msg = arg; + ALERTING_t *al; + + dprint(DBGM_L3,"%s\n", __FUNCTION__); + if (!pc->master) { + L3DelTimer(&pc->timer1); + newl3state(pc, 7); + return; + } + umsg = prep_l3data_msg(CC_ALERTING | INDICATION, pc->master->ces | + (pc->master->callref << 16), sizeof(ALERTING_t), msg->len, NULL); + if (!umsg) + return; + al = (ALERTING_t *)(umsg->data + mISDN_HEAD_SIZE); + L3DelTimer(&pc->timer1); /* T304 */ + newl3state(pc, 7); + al->BEARER = + find_and_copy_ie(msg->data, msg->len, IE_BEARER, 0, umsg); + al->FACILITY = + find_and_copy_ie(msg->data, msg->len, IE_FACILITY, 0, umsg); + al->PROGRESS = + find_and_copy_ie(msg->data, msg->len, IE_PROGRESS, 0, umsg); + al->SIGNAL = + find_and_copy_ie(msg->data, msg->len, IE_SIGNAL, 0, umsg); + al->HLC = + find_and_copy_ie(msg->data, msg->len, IE_HLC, 0, umsg); + al->USER_USER = + find_and_copy_ie(msg->data, msg->len, IE_USER_USER, 0, umsg); + if (!mISDN_l3up(pc->master, umsg)) + return; + free_msg(umsg); +} + +static void +l3dss1_call_proc(layer3_proc_t *pc, int pr, void *arg) +{ + msg_t *umsg, *msg = arg; + int ret = 0; + u_char cause; + CALL_PROCEEDING_t *cp; + + umsg = prep_l3data_msg(CC_PROCEEDING | INDICATION, pc->ces | + (pc->callref << 16), sizeof(CALL_PROCEEDING_t), msg->len, NULL); + if (!umsg) + return; + cp = (CALL_PROCEEDING_t *)(umsg->data + mISDN_HEAD_SIZE); + if ((cp->CHANNEL_ID = l3dss1_get_channel_id(pc, msg, umsg))) { + if ((0 == pc->bc) || (3 == pc->bc)) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "setup answer with wrong chid %x", pc->bc); + l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); + free_msg(umsg); + return; + } + } else if (1 == pc->state) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "setup answer wrong chid (ret %d)", pc->err); + if (pc->err == -1) + cause = CAUSE_MANDATORY_IE_MISS; + else + cause = CAUSE_INVALID_CONTENTS; + l3dss1_status_send(pc, cause); + free_msg(umsg); + return; + } + /* Now we are on none mandatory IEs */ + cp->BEARER = + find_and_copy_ie(msg->data, msg->len, IE_BEARER, 0, umsg); + cp->FACILITY = + find_and_copy_ie(msg->data, msg->len, IE_FACILITY, 0, umsg); + cp->PROGRESS = + find_and_copy_ie(msg->data, msg->len, IE_PROGRESS, 0, umsg); + cp->DISPLAY = + find_and_copy_ie(msg->data, msg->len, IE_DISPLAY, 0, umsg); + cp->HLC = + find_and_copy_ie(msg->data, msg->len, IE_HLC, 0, umsg); + L3DelTimer(&pc->timer1); + newl3state(pc, 3); + L3AddTimer(&pc->timer1, T310, 0x310); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, ret); + if (mISDN_l3up(pc, umsg)) + free_msg(umsg); +} + +static void +l3dss1_connect_i(layer3_proc_t *pc, int pr, void *arg) +{ + msg_t *umsg, *msg = arg; + CONNECT_t *conn; + + if (!pc->master) { + L3DelTimer(&pc->timer1); + newl3state(pc, 8); + return; + } + umsg = prep_l3data_msg(CC_CONNECT | INDICATION, pc->master->ces | + (pc->master->callref << 16), sizeof(CONNECT_t), msg->len, NULL); + if (!umsg) + return; + conn = (CONNECT_t *)(umsg->data + mISDN_HEAD_SIZE); + L3DelTimer(&pc->timer1); /* T310 */ + newl3state(pc, 8); + conn->BEARER = + find_and_copy_ie(msg->data, msg->len, IE_BEARER, 0, umsg); + conn->FACILITY = + find_and_copy_ie(msg->data, msg->len, IE_FACILITY, 0, umsg); + conn->PROGRESS = + find_and_copy_ie(msg->data, msg->len, IE_PROGRESS, 0, umsg); + conn->DISPLAY = + find_and_copy_ie(msg->data, msg->len, IE_DISPLAY, 0, umsg); + conn->DATE = + find_and_copy_ie(msg->data, msg->len, IE_DATE, 0, umsg); + conn->SIGNAL = + find_and_copy_ie(msg->data, msg->len, IE_SIGNAL, 0, umsg); + conn->CONNECT_PN = + find_and_copy_ie(msg->data, msg->len, IE_CONNECT_PN, 0, umsg); + conn->CONNECT_SUB = + find_and_copy_ie(msg->data, msg->len, IE_CONNECT_SUB, 0, umsg); + conn->HLC = + find_and_copy_ie(msg->data, msg->len, IE_HLC, 0, umsg); + conn->LLC = + find_and_copy_ie(msg->data, msg->len, IE_LLC, 0, umsg); + conn->USER_USER = + find_and_copy_ie(msg->data, msg->len, IE_USER_USER, 0, umsg); + if (send_proc(pc, IMSG_CONNECT_IND, umsg)) + free_msg(umsg); +} + +static struct stateentry datastatelist[] = +{ + {ALL_STATES, + MT_STATUS_ENQUIRY, l3dss1_status_enq}, + {ALL_STATES, + MT_FACILITY, l3dss1_facility}, + {SBIT(19), + MT_STATUS, l3dss1_release_cmpl}, + {SBIT(0), + MT_SETUP, l3dss1_setup}, + {SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25), + MT_ALERTING, l3dss1_alerting_i}, + {SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25), + MT_CONNECT, l3dss1_connect_i}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(10), + MT_DISCONNECT, l3dss1_disconnect}, + {SBIT(7) | SBIT(8) | SBIT(9), + MT_DISCONNECT, l3dss1_disconnect_i}, + {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | + SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), + MT_INFORMATION, l3dss1_information}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(10) | + SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17), + MT_RELEASE, l3dss1_release}, + {SBIT(7) | SBIT(8) | SBIT(9) | SBIT(19) | SBIT(25), + MT_RELEASE, l3dss1_release_i}, + {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(10) | + SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17), + MT_RELEASE_COMPLETE, l3dss1_release_cmpl}, + {SBIT(4) | SBIT(7) | SBIT(10), + MT_USER_INFORMATION, l3dss1_userinfo}, + {SBIT(7) | SBIT(8) | SBIT(9) | SBIT(19) | SBIT(25), + MT_RELEASE_COMPLETE, l3dss1_release_cmpl_i}, +}; + +#define DATASLLEN \ + (sizeof(datastatelist) / sizeof(struct stateentry)) + +static int +create_child_proc(layer3_proc_t *pc, int mt, msg_t *msg, int state) { + mISDN_head_t *hh; + struct _l3_msg l3m; + layer3_proc_t *p3i; + + hh = (mISDN_head_t *)msg->data; + msg_pull(msg, mISDN_HEAD_SIZE); + p3i = create_proc(pc->l3, hh->dinfo, pc->callref, pc); + if (!p3i) { + l3_debug(pc->l3, "cannot create child\n"); + return(-ENOMEM); + } + p3i->state = pc->state; + if (pc->state != -1) + newl3state(pc, state); + l3m.mt = mt; + l3m.msg = msg; + send_proc(p3i, IMSG_L2_DATA, &l3m); + return(0); +} + +static void +l3dss1_alerting_m(layer3_proc_t *pc, int pr, void *arg) +{ + dprint(DBGM_L3,"%s\n", __FUNCTION__); + L3DelTimer(&pc->timer1); + create_child_proc(pc, pr, arg, 7); +} + +static void +l3dss1_connect_m(layer3_proc_t *pc, int pr, void *arg) +{ + dprint(DBGM_L3,"%s\n", __FUNCTION__); + L3DelTimer(&pc->timer1); + create_child_proc(pc, pr, arg, 8); +} + +static void +l3dss1_release_m(layer3_proc_t *pc, int pr, void *arg) +{ + l3dss1_message(pc, MT_RELEASE_COMPLETE); +} + +static void +l3dss1_release_mx(layer3_proc_t *pc, int pr, void *arg) +{ + msg_t *msg = arg; + + msg_pull(msg, mISDN_HEAD_SIZE); + l3dss1_release(pc, pr, msg); +} + +static void +l3dss1_release_cmpl_m(layer3_proc_t *pc, int pr, void *arg) +{ + msg_t *msg = arg; + u_char *p; + + if (pc->state == 6) { + msg_pull(msg, mISDN_HEAD_SIZE); + if ((p = l3dss1_get_cause(pc, msg, NULL))) { + dprint(DBGM_L3,"%s cause (%d/%d)\n", __FUNCTION__, + pc->cause, pc->err); + switch(pc->cause) { + case CAUSE_USER_BUSY: + break; + case CAUSE_CALL_REJECTED: + if (pc->err == CAUSE_USER_BUSY) + pc->cause = pc->err; + break; + default: + pc->cause = pc->err; + } + } + test_and_set_bit(FLG_L3P_GOTRELCOMP, &pc->Flags); + } +} + +static void +l3dss1_release_cmpl_mx(layer3_proc_t *pc, int pr, void *arg) +{ + msg_t *msg = arg; + + msg_pull(msg, mISDN_HEAD_SIZE); + l3dss1_release_cmpl(pc, pr, msg); +} + +static void +l3dss1_information_mx(layer3_proc_t *pc, int pr, void *arg) +{ + msg_t *msg = arg; + + msg_pull(msg, mISDN_HEAD_SIZE); + l3dss1_information(pc, pr, msg); +} + +static struct stateentry mdatastatelist[] = +{ + +// TODO CALL_PROCEEDING + + {SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25), + MT_ALERTING, l3dss1_alerting_m}, + {SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25), + MT_CONNECT, l3dss1_connect_m}, + {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | + SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), + MT_INFORMATION, l3dss1_information_mx}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(10) | SBIT(11) | + SBIT(12) | SBIT(15) | SBIT(17), + MT_RELEASE, l3dss1_release_mx}, + {SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(22) | SBIT(25), + MT_RELEASE, l3dss1_release_m}, + {SBIT(19), MT_RELEASE, l3dss1_release_cmpl}, + {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(10) | + SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19), + MT_RELEASE_COMPLETE, l3dss1_release_cmpl_mx}, + {SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(22) | SBIT(25), + MT_RELEASE_COMPLETE, l3dss1_release_cmpl_m}, +}; + +#define MDATASLLEN \ + (sizeof(mdatastatelist) / sizeof(struct stateentry)) + +static void +l3dss1_setup_ack_req(layer3_proc_t *pc, int pr, void *arg) +{ + SETUP_ACKNOWLEDGE_t *setup_ack = arg; + + if (setup_ack) { + MsgStart(pc, MT_SETUP_ACKNOWLEDGE); + if (setup_ack->CHANNEL_ID) { + if (setup_ack->CHANNEL_ID[0] == 1) + pc->bc = setup_ack->CHANNEL_ID[1] & 3; + AddvarIE(pc, IE_CHANNEL_ID, setup_ack->CHANNEL_ID); + } + if (setup_ack->FACILITY) + AddvarIE(pc, IE_FACILITY, setup_ack->FACILITY); + if (setup_ack->PROGRESS) + AddvarIE(pc, IE_PROGRESS, setup_ack->PROGRESS); + if (setup_ack->DISPLAY) + AddvarIE(pc, IE_DISPLAY, setup_ack->DISPLAY); + SendMsg(pc, 2); + } else { + newl3state(pc, 2); + l3dss1_message(pc, MT_SETUP_ACKNOWLEDGE); + } + L3DelTimer(&pc->timer1); + L3AddTimer(&pc->timer1, T302, 0x302); +} + +static void +l3dss1_proceed_req(layer3_proc_t *pc, int pr, void *arg) +{ + CALL_PROCEEDING_t *cproc = arg; + + L3DelTimer(&pc->timer1); + if (cproc) { + MsgStart(pc, MT_CALL_PROCEEDING); + if (cproc->BEARER) + AddvarIE(pc, IE_BEARER, cproc->BEARER); + if (cproc->CHANNEL_ID) { + if (cproc->CHANNEL_ID[0] == 1) + pc->bc = cproc->CHANNEL_ID[1] & 3; + AddvarIE(pc, IE_CHANNEL_ID, cproc->CHANNEL_ID); + } + if (cproc->FACILITY) + AddvarIE(pc, IE_FACILITY, cproc->FACILITY); + if (cproc->PROGRESS) + AddvarIE(pc, IE_PROGRESS, cproc->PROGRESS); + if (cproc->DISPLAY) + AddvarIE(pc, IE_DISPLAY, cproc->DISPLAY); + if (cproc->HLC) + AddvarIE(pc, IE_HLC, cproc->HLC); + SendMsg(pc, 3); + } else { + newl3state(pc, 3); + l3dss1_message(pc, MT_CALL_PROCEEDING); + } +} + +static void +l3dss1_alert_req(layer3_proc_t *pc, int pr, void *arg) +{ + ALERTING_t *alert = arg; + + if (alert) { + MsgStart(pc, MT_ALERTING); + if (alert->BEARER) + AddvarIE(pc, IE_BEARER, alert->BEARER); + if (alert->CHANNEL_ID) { + if (alert->CHANNEL_ID[0] == 1) + pc->bc = alert->CHANNEL_ID[1] & 3; + AddvarIE(pc, IE_CHANNEL_ID, alert->CHANNEL_ID); + } + if (alert->FACILITY) + AddvarIE(pc, IE_FACILITY, alert->FACILITY); + if (alert->PROGRESS) + AddvarIE(pc, IE_PROGRESS, alert->PROGRESS); + if (alert->DISPLAY) + AddvarIE(pc, IE_DISPLAY, alert->DISPLAY); + if (alert->HLC) + AddvarIE(pc, IE_HLC, alert->HLC); + if (alert->USER_USER) + AddvarIE(pc, IE_USER_USER, alert->USER_USER); + SendMsg(pc, 4); + } else { + newl3state(pc, 4); + l3dss1_message(pc, MT_ALERTING); + } + L3DelTimer(&pc->timer1); +} + +static void +l3dss1_setup_req(layer3_proc_t *pc, int pr, void *arg) +{ + SETUP_t *setup = arg; + msg_t *msg; + int l; + + MsgStart(pc, MT_SETUP); + if (setup->COMPLETE) + *pc->op++ = IE_COMPLETE; + if (setup->BEARER) + AddvarIE(pc, IE_BEARER, setup->BEARER); + if (setup->CHANNEL_ID) { + if (setup->CHANNEL_ID[0] == 1) + pc->bc = setup->CHANNEL_ID[1] & 3; + AddvarIE(pc, IE_CHANNEL_ID, setup->CHANNEL_ID); + } + if (setup->FACILITY) + AddvarIE(pc, IE_FACILITY, setup->FACILITY); + if (setup->PROGRESS) + AddvarIE(pc, IE_PROGRESS, setup->PROGRESS); + if (setup->NET_FAC) + AddvarIE(pc, IE_NET_FAC, setup->NET_FAC); + if (setup->DISPLAY) + AddvarIE(pc, IE_DISPLAY, setup->DISPLAY); + if (setup->KEYPAD) + AddvarIE(pc, IE_KEYPAD, setup->KEYPAD); + if (setup->CALLING_PN) + AddvarIE(pc, IE_CALLING_PN, setup->CALLING_PN); + if (setup->CALLING_SUB) + AddvarIE(pc, IE_CALLING_SUB, setup->CALLING_SUB); + if (setup->CALLED_PN) + AddvarIE(pc, IE_CALLED_PN, setup->CALLED_PN); + if (setup->CALLED_SUB) + AddvarIE(pc, IE_CALLED_SUB, setup->CALLED_SUB); + if (setup->LLC) + AddvarIE(pc, IE_LLC, setup->LLC); + if (setup->HLC) + AddvarIE(pc, IE_HLC, setup->HLC); + if (setup->USER_USER) + AddvarIE(pc, IE_USER_USER, setup->USER_USER); + + l = pc->op - &pc->obuf[0]; + if (!(msg = l3_alloc_msg(l))) + return; + memcpy(msg_put(msg, l), &pc->obuf[0], l); + newl3state(pc, 6); + dhexprint(DBGM_L3DATA, "l3 oframe:", &pc->obuf[0], l); + if (l3_msg(pc->l3, DL_UNITDATA | REQUEST, 127, msg)) + free_msg(msg); + L3DelTimer(&pc->timer1); + test_and_clear_bit(FLG_L3P_TIMER303_1, &pc->Flags); + L3AddTimer(&pc->timer1, T303, 0x303); + test_and_set_bit(FLG_L3P_TIMER312, &pc->Flags); + L3DelTimer(&pc->timer2); + L3AddTimer(&pc->timer2, T312, 0x312); +} + +static void +l3dss1_disconnect_req(layer3_proc_t *pc, int pr, void *arg); + +static void +l3dss1_connect_req(layer3_proc_t *pc, int pr, void *arg) +{ + CONNECT_t *conn = arg; + + L3DelTimer(&pc->timer1); + if (conn && conn->CHANNEL_ID) { + if (conn->CHANNEL_ID[0] == 1) + pc->bc = conn->CHANNEL_ID[1] & 3; + } + if (!pc->bc) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "D-chan connect for waiting call"); + l3dss1_disconnect_req(pc, pr, NULL); + return; + } + if (conn) { + MsgStart(pc, MT_CONNECT); + if (conn->BEARER) + AddvarIE(pc, IE_BEARER, conn->BEARER); + if (conn->CHANNEL_ID) + AddvarIE(pc, IE_CHANNEL_ID, conn->CHANNEL_ID); + if (conn->FACILITY) + AddvarIE(pc, IE_FACILITY, conn->FACILITY); + if (conn->PROGRESS) + AddvarIE(pc, IE_PROGRESS, conn->PROGRESS); + if (conn->DISPLAY) + AddvarIE(pc, IE_DISPLAY, conn->DISPLAY); + if (conn->DATE) + AddvarIE(pc, IE_DATE, conn->DATE); + if (conn->CONNECT_PN) + AddvarIE(pc, IE_CONNECT_PN, conn->CONNECT_PN); + if (conn->CONNECT_SUB) + AddvarIE(pc, IE_CONNECT_SUB, conn->CONNECT_SUB); + if (conn->LLC) + AddvarIE(pc, IE_LLC, conn->LLC); + if (conn->HLC) + AddvarIE(pc, IE_HLC, conn->HLC); + if (conn->USER_USER) + AddvarIE(pc, IE_USER_USER, conn->USER_USER); + SendMsg(pc, 10); + } else { + newl3state(pc, 10); + l3dss1_message(pc, MT_CONNECT); + } +} + +static void +l3dss1_connect_res(layer3_proc_t *pc, int pr, void *arg) +{ + CONNECT_ACKNOWLEDGE_t *connack = arg; + int cause; + + L3DelTimer(&pc->timer1); + send_proc(pc, IMSG_SEL_PROC, NULL); + if (connack && connack->CHANNEL_ID) { + if (connack->CHANNEL_ID[0] == 1) + pc->bc = connack->CHANNEL_ID[1] & 3; + } + if (!pc->bc) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "D-chan connect for waiting call"); + l3dss1_disconnect_req(pc, pr, NULL); + return; + } + if (connack) { + MsgStart(pc, MT_CONNECT_ACKNOWLEDGE); + if (connack->CHANNEL_ID) + AddvarIE(pc, IE_CHANNEL_ID, connack->CHANNEL_ID); + if (connack->DISPLAY) + AddvarIE(pc, IE_DISPLAY, connack->DISPLAY); + if (connack->SIGNAL) + AddvarIE(pc, IE_SIGNAL, connack->SIGNAL); + SendMsg(pc, 10); + } else { + newl3state(pc, 10); + l3dss1_message(pc, MT_CONNECT_ACKNOWLEDGE); + } + cause = CAUSE_NONSELECTED_USER; + send_proc(pc, IMSG_RELEASE_CHILDS, &cause); +} + +static void +l3dss1_disconnect_req(layer3_proc_t *pc, int pr, void *arg) +{ + DISCONNECT_t *disc = arg; + + StopAllL3Timer(pc); + if (disc) { + MsgStart(pc, MT_DISCONNECT); + if (disc->CAUSE){ + AddvarIE(pc, IE_CAUSE, disc->CAUSE); + } else { + *pc->op++ = IE_CAUSE; + *pc->op++ = 2; + *pc->op++ = 0x80 | CAUSE_LOC_PNET_LOCUSER; + *pc->op++ = 0x80 | CAUSE_NORMALUNSPECIFIED; + } + if (disc->FACILITY) + AddvarIE(pc, IE_FACILITY, disc->FACILITY); + if (disc->PROGRESS) + AddvarIE(pc, IE_PROGRESS, disc->PROGRESS); + if (disc->DISPLAY) + AddvarIE(pc, IE_DISPLAY, disc->DISPLAY); + if (disc->USER_USER) + AddvarIE(pc, IE_USER_USER, disc->USER_USER); + SendMsg(pc, 12); + } else { + newl3state(pc, 12); + l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_NORMALUNSPECIFIED); + } + L3AddTimer(&pc->timer1, T305, 0x305); +} + +static void +l3dss1_facility_req(layer3_proc_t *pc, int pr, void *arg) +{ + FACILITY_t *fac = arg; + + if (fac) { + MsgStart(pc, MT_FACILITY); + if (fac->FACILITY) + AddvarIE(pc, IE_FACILITY, fac->FACILITY); + else + return; + if (fac->DISPLAY) + AddvarIE(pc, IE_DISPLAY, fac->DISPLAY); + SendMsg(pc, -1); + } +} + +static void +l3dss1_userinfo_req(layer3_proc_t *pc, int pr, void *arg) +{ + USER_INFORMATION_t *ui = arg; + + if (ui) { + MsgStart(pc, MT_USER_INFORMATION); + if (ui->USER_USER) + AddvarIE(pc, IE_USER_USER, ui->USER_USER); + else + return; + SendMsg(pc, -1); + } +} + +static void +l3dss1_disconnect_req_out(layer3_proc_t *pc, int pr, void *arg) +{ + DISCONNECT_t *disc = arg; + int cause; + + if (pc->master) { /* child */ + l3dss1_disconnect_req_out(pc->master, pr, arg); + return; + } + L3DelTimer(&pc->timer1); + if (disc) { + if (disc->CAUSE){ + cause = disc->CAUSE[2] & 0x7f; + } else { + cause = CAUSE_NORMALUNSPECIFIED; + } + } + send_proc(pc, IMSG_RELEASE_CHILDS, &cause); + if (test_bit(FLG_L3P_TIMER312, &pc->Flags)) { + newl3state(pc, 22); + } else { + if_link(pc->l3->nst->manager, (ifunc_t)pc->l3->nst->l3_manager, + CC_RELEASE | CONFIRM, pc->ces | + (pc->callref << 16), 0, NULL, 0); + newl3state(pc, 0); + if (!pc->child) + send_proc(pc, IMSG_END_PROC_M, NULL); + } +} + +static void +l3dss1_release_req(layer3_proc_t *pc, int pr, void *arg) +{ + RELEASE_t *rel = arg; + + StopAllL3Timer(pc); + if (rel) { + MsgStart(pc, MT_RELEASE); + if (rel->CAUSE) + AddvarIE(pc, IE_CAUSE, rel->CAUSE); + if (rel->FACILITY) + AddvarIE(pc, IE_FACILITY, rel->FACILITY); + if (rel->DISPLAY) + AddvarIE(pc, IE_DISPLAY, rel->DISPLAY); + if (rel->USER_USER) + AddvarIE(pc, IE_USER_USER, rel->USER_USER); + SendMsg(pc, 19); + } else { + newl3state(pc, 19); + l3dss1_message(pc, MT_RELEASE); + } + test_and_clear_bit(FLG_L3P_TIMER308_1, &pc->Flags); + L3AddTimer(&pc->timer1, T308, 0x308); +} + +static void +l3dss1_release_cmpl_req(layer3_proc_t *pc, int pr, void *arg) +{ + RELEASE_COMPLETE_t *rcmpl = arg; + + StopAllL3Timer(pc); + if (rcmpl) { + MsgStart(pc, MT_RELEASE_COMPLETE); + if (rcmpl->CAUSE) + AddvarIE(pc, IE_CAUSE, rcmpl->CAUSE); + if (rcmpl->FACILITY) + AddvarIE(pc, IE_FACILITY, rcmpl->FACILITY); + if (rcmpl->DISPLAY) + AddvarIE(pc, IE_DISPLAY, rcmpl->DISPLAY); + if (rcmpl->USER_USER) + AddvarIE(pc, IE_USER_USER, rcmpl->USER_USER); + SendMsg(pc, 0); + } else { + newl3state(pc, 0); + l3dss1_message(pc, MT_RELEASE_COMPLETE); + } + send_proc(pc, IMSG_END_PROC_M, NULL); +} + +static void +l3dss1_t303(layer3_proc_t *pc, int pr, void *arg) +{ + int l; + msg_t *msg; + RELEASE_COMPLETE_t *relc; + + L3DelTimer(&pc->timer1); + if (test_bit(FLG_L3P_GOTRELCOMP, &pc->Flags)) { + StopAllL3Timer(pc); + msg = prep_l3data_msg(CC_RELEASE_COMPLETE | INDICATION, + pc->ces | (pc->callref << 16), + sizeof(RELEASE_COMPLETE_t), 3, NULL); + if (!msg) + return; + relc = (RELEASE_COMPLETE_t *)(msg->data + mISDN_HEAD_SIZE); + newl3state(pc, 0); + relc->CAUSE = msg_put(msg, 3); + relc->CAUSE[0] = 2; + relc->CAUSE[1] = 0x80; + if (pc->cause) + relc->CAUSE[2] = pc->cause | 0x80; + else + relc->CAUSE[2] = CAUSE_NORMALUNSPECIFIED | 0x80; + if (mISDN_l3up(pc, msg)) + free_msg(msg); + send_proc(pc, IMSG_END_PROC_M, NULL); + return; + } + if (!test_and_set_bit(FLG_L3P_TIMER303_1, &pc->Flags)) { + if (pc->obuf[3] == MT_SETUP) { + l = pc->op - &pc->obuf[0]; + dhexprint(DBGM_L3DATA, "l3 oframe:", &pc->obuf[0], l); + if ((msg = l3_alloc_msg(l))) { + memcpy(msg_put(msg, l), &pc->obuf[0], l); + if (l3_msg(pc->l3, DL_UNITDATA | REQUEST, 127, msg)) + free_msg(msg); + } + L3DelTimer(&pc->timer2); + L3AddTimer(&pc->timer2, T312, 0x312); + test_and_set_bit(FLG_L3P_TIMER312, + &pc->Flags); + L3AddTimer(&pc->timer1, T303, 0x303); + return; + } + } + msg = prep_l3data_msg(CC_RELEASE_COMPLETE | INDICATION, + pc->ces | (pc->callref << 16), + sizeof(RELEASE_COMPLETE_t), 3, NULL); + if (!msg) + return; + relc = (RELEASE_COMPLETE_t *)(msg->data + mISDN_HEAD_SIZE); + relc->CAUSE = msg_put(msg, 3); + relc->CAUSE[0] = 2; + relc->CAUSE[1] = 0x85; + relc->CAUSE[2] = CAUSE_NOUSER_RESPONDING | 0x80; + if (mISDN_l3up(pc, msg)) + free_msg(msg); + newl3state(pc, 22); +} + +static void +l3dss1_t308(layer3_proc_t *pc, int pr, void *arg) +{ + if (!test_and_set_bit(FLG_L3P_TIMER308_1, &pc->Flags)) { + newl3state(pc, 19); + L3DelTimer(&pc->timer1); + l3dss1_message(pc, MT_RELEASE); + L3AddTimer(&pc->timer1, T308, 0x308); + } else { + int t = 0x308; + + StopAllL3Timer(pc); + newl3state(pc, 0); + if_link(pc->l3->nst->manager, (ifunc_t)pc->l3->nst->l3_manager, + CC_TIMEOUT | INDICATION,pc->ces | (pc->callref << 16), + sizeof(int), &t, 0); + send_proc(pc, IMSG_END_PROC_M, NULL); + } +} + +static void +l3dss1_t312(layer3_proc_t *pc, int pr, void *arg) +{ + int t = 0x312; + + test_and_clear_bit(FLG_L3P_TIMER312, &pc->Flags); + L3DelTimer(&pc->timer2); + l3_debug(pc->l3, "%s: state %d", __FUNCTION__, pc->state); + if (pc->state == 22) { + StopAllL3Timer(pc); + if (!pc->child) { + if_link(pc->l3->nst->manager, (ifunc_t)pc->l3->nst->l3_manager, + CC_TIMEOUT | INDICATION,pc->ces | + (pc->callref << 16), sizeof(int), &t, 0); + send_proc(pc, IMSG_END_PROC_M, NULL); + } + } +} + +/* *INDENT-OFF* */ +static struct stateentry downstatelist[] = +{ +#if 0 + {SBIT(0), + CC_RESUME | REQUEST, l3dss1_resume_req}, + {SBIT(12), + CC_RELEASE | REQUEST, l3dss1_release_req}, + {ALL_STATES, + CC_RESTART | REQUEST, l3dss1_restart}, + {SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25), + CC_CONNECT | REQUEST, l3dss1_connect_req}, + {SBIT(10), + CC_SUSPEND | REQUEST, l3dss1_suspend_req}, +#endif + {ALL_STATES, + CC_RELEASE_COMPLETE | REQUEST, l3dss1_release_cmpl_req}, + {SBIT(0), + CC_SETUP | REQUEST, l3dss1_setup_req}, + {SBIT(1), + CC_SETUP_ACKNOWLEDGE | REQUEST, l3dss1_setup_ack_req}, + {SBIT(1) | SBIT(2), + CC_PROCEEDING | REQUEST, l3dss1_proceed_req}, + {SBIT(1) | SBIT(2) | SBIT(3), + CC_ALERTING | REQUEST, l3dss1_alert_req}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4), + CC_CONNECT | REQUEST, l3dss1_connect_req}, + {SBIT(8), + CC_CONNECT | RESPONSE, l3dss1_connect_res}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(10), + CC_DISCONNECT | REQUEST, l3dss1_disconnect_req}, + {SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(25), + CC_DISCONNECT | REQUEST, l3dss1_disconnect_req_out}, +// TODO CC_RELEASE only in SBIT(11) + {SBIT(6) | SBIT(7) | SBIT(11) | SBIT(25), + CC_RELEASE | REQUEST, l3dss1_release_req}, + {ALL_STATES, + CC_FACILITY | REQUEST, l3dss1_facility_req}, + {SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10), + CC_USER_INFORMATION | REQUEST, l3dss1_userinfo_req}, + {SBIT(6), + CC_T303, l3dss1_t303}, + {SBIT(19), + CC_T308, l3dss1_t308}, + {ALL_STATES, + CC_T312, l3dss1_t312}, +}; + +#define DOWNSLLEN \ + (sizeof(downstatelist) / sizeof(struct stateentry)) + +static int +imsg_intrelease(layer3_proc_t *master, layer3_proc_t *child) +{ + int cause; + + if ((!master) || (!child)) + return(-EINVAL); + dprint(DBGM_L3, "%s: m/c(%x/%x) state(%d/%d) m->c(%p)\n", __FUNCTION__, + master->ces, child->ces, master->state, child->state, + master->child); + switch (master->state) { + case 0: + if (!master->child) { + send_proc(master, IMSG_END_PROC, master); + } + break; + case 6: + case 10: + case 19: + break; + case 7: + case 9: + case 25: + if (master->child || + test_bit(FLG_L3P_TIMER312, &master->Flags)) { + /* TODO: save cause */ + } else { + send_proc(master, IMSG_END_PROC, NULL); + } + break; + case 8: + if (master->selces == child->ces) { + cause = CAUSE_NONSELECTED_USER; + send_proc(master, IMSG_RELEASE_CHILDS, &cause); + if (test_bit(FLG_L3P_TIMER312, &master->Flags)) { + newl3state(master, 22); + } else { + if (!master->child) + send_proc(master, + IMSG_END_PROC, NULL); + } + } + break; + case 22: + if (!master->child) { + send_proc(master, IMSG_END_PROC, NULL); + } + break; + } + return(0); +} + +static int +send_proc(layer3_proc_t *proc, int op, void *arg) +{ + int i; + layer3_proc_t *selp; + struct _l3_msg *l3m = arg; + struct _l3_msg l3msg; + + if (proc->l3->debug & L3_DEB_PROC) + l3_debug(proc->l3, "%s: proc(%x,%d) op(%d)", __FUNCTION__, + proc->ces, proc->callref, op); + switch(op) { + case IMSG_END_PROC: + case IMSG_END_PROC_M: + RemoveAllL3Timer(proc); + if (!proc->master && !arg) { + if_link(proc->l3->nst->manager, + (ifunc_t)proc->l3->nst->l3_manager, + CC_RELEASE_CR | INDICATION, + proc->ces | (proc->callref << 16), + sizeof(int), &proc->err, 0); + } + while (proc->child) + send_proc(proc->child, IMSG_END_PROC, NULL); + if (proc->next) + proc->next->prev = proc->prev; + if (proc->prev) + proc->prev->next = proc->next; + if (proc == proc->l3->proc) + proc->l3->proc = proc->next; + if (proc->master) { + if (proc->master->child == proc) + proc->master->child = proc->next; + if (op == IMSG_END_PROC_M) + imsg_intrelease(proc->master, proc); + } + free(proc); + break; + case IMSG_L2_DATA: + for (i = 0; i < DATASLLEN; i++) + if ((l3m->mt == datastatelist[i].primitive) && + ((1 << proc->state) & datastatelist[i].state)) + break; + if (i == DATASLLEN) { + if (proc->l3->debug & L3_DEB_STATE) { + l3_debug(proc->l3, "dss1 state %d mt %#x unhandled", + proc->state, l3m->mt); + } + if ((MT_RELEASE_COMPLETE != l3m->mt) && (MT_RELEASE != l3m->mt)) { + // l3dss1_status_send(proc, CAUSE_NOTCOMPAT_STATE); + } + } else { + if (proc->l3->debug & L3_DEB_STATE) { + l3_debug(proc->l3, "dss1 state %d mt %x", + proc->state, l3m->mt); + } + datastatelist[i].rout(proc, l3m->mt, l3m->msg); + } + break; + case IMSG_MASTER_L2_DATA: + for (i = 0; i < MDATASLLEN; i++) + if ((l3m->mt == mdatastatelist[i].primitive) && + ((1 << proc->state) & mdatastatelist[i].state)) + break; + if (i == MDATASLLEN) { + if (proc->l3->debug & L3_DEB_STATE) { + l3_debug(proc->l3, "dss1 state %d mt %#x unhandled", + proc->state, l3m->mt); + } + if ((MT_RELEASE_COMPLETE != l3m->mt) && (MT_RELEASE != l3m->mt)) { + // l3dss1_status_send(proc, CAUSE_NOTCOMPAT_STATE); + } + } else { + if (proc->l3->debug & L3_DEB_STATE) { + l3_debug(proc->l3, "dss1 state %d mt %x", + proc->state, l3m->mt); + } + mdatastatelist[i].rout(proc, l3m->mt, l3m->msg); + } + break; + case IMSG_TIMER_EXPIRED: + i = *((int *)arg); + l3_debug(proc->l3, "%s: timer %x", __FUNCTION__, i); + l3m = &l3msg; + l3m->mt = CC_TIMER | (i<<8); + l3m->msg = NULL; + case IMSG_L4_DATA: + for (i = 0; i < DOWNSLLEN; i++) + if ((l3m->mt == downstatelist[i].primitive) && + ((1 << proc->state) & downstatelist[i].state)) + break; + if (i == DOWNSLLEN) { + if (proc->l3->debug & L3_DEB_STATE) { + l3_debug(proc->l3, "dss1 state %d L4 %#x unhandled", + proc->state, l3m->mt); + } + } else { + if (proc->l3->debug & L3_DEB_STATE) { + l3_debug(proc->l3, "dss1 state %d L4 %x", + proc->state, l3m->mt); + } + if (l3m->msg) + downstatelist[i].rout(proc, l3m->mt, + l3m->msg->data); + else + downstatelist[i].rout(proc, l3m->mt, + NULL); + } + break; + case IMSG_CONNECT_IND: + selp = proc; + proc = proc->master; + if (!proc) + return(-EINVAL); + proc->selces = selp->ces; + newl3state(proc, 8); + return(mISDN_l3up(proc, arg)); + case IMSG_SEL_PROC: + selp = find_proc(proc->child, proc->selces, + proc->callref); + i = proc->selces | (proc->callref << 16); + if_link(proc->l3->nst->manager, + (ifunc_t)proc->l3->nst->l3_manager, + CC_NEW_CR | INDICATION, proc->ces | + (proc->callref << 16), sizeof(int), &i, 0); + proc->ces = proc->selces; + send_proc(selp, IMSG_END_PROC, NULL); + break; + case IMSG_RELEASE_CHILDS: + { + RELEASE_t *rel; + char cause[3]; + + cause[0] = 2; + cause[1] = CAUSE_LOC_PNET_LOCUSER | 0x80; + cause[2] = *((int *)arg) | 0x80; + l3msg.mt = CC_RELEASE | REQUEST; + l3msg.msg = alloc_msg(sizeof(RELEASE_t)); + if (!l3msg.msg) + return(-ENOMEM); + rel = (RELEASE_t *)msg_put(l3msg.msg, + sizeof(RELEASE_t)); + memset(rel, 0, sizeof(RELEASE_t)); + rel->CAUSE = cause; + selp = proc->child; + while(selp) { + layer3_proc_t *next = selp->next; + + send_proc(selp, IMSG_L4_DATA, &l3msg); + selp = next; + } + free_msg(l3msg.msg); + } + break; + } + return(0); +} + +static int +dl_data_mux(layer3_t *l3, mISDN_head_t *hh, msg_t *msg) +{ + layer3_proc_t *proc; + int ret = -EINVAL; + int cr; + struct _l3_msg l3m; + + if (!l3) + return(ret); + dprint(DBGM_L3, "%s: len(%d)\n", __FUNCTION__, msg->len); + dhexprint(DBGM_L3DATA, "l3 iframe:", msg->data, msg->len); + if (msg->len < 3) { + l3_debug(l3, "dss1 frame too short(%d)", msg->len); + free_msg(msg); + return(0); + } + if (msg->data[0] != PROTO_DIS_EURO) { + if (l3->debug & L3_DEB_PROTERR) { + l3_debug(l3, "dss1%sunexpected discriminator %x message len %d", + (hh->prim == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + msg->data[0], msg->len); + } + free_msg(msg); + return(0); + } + dprint(DBGM_L3, "%s: dis(%x)\n", __FUNCTION__, msg->data[0]); + cr = getcallref(msg->data); + dprint(DBGM_L3, "%s: cr(%x)\n", __FUNCTION__, cr); + if (msg->len < ((msg->data[1] & 0x0f) + 3)) { + l3_debug(l3, "dss1 frame too short(%d)", msg->len); + free_msg(msg); + return(0); + } + l3m.msg = msg; + l3m.mt = msg->data[msg->data[1] + 2]; + if (cr == -2) { /* wrong Callref */ + if (l3->debug & L3_DEB_WARN) + l3_debug(l3, "dss1 wrong Callref"); + free_msg(msg); + return(0); + } else if (cr == -1) { /* Dummy Callref */ +// if (l3m.mt == MT_FACILITY) +// l3dss1_facility(l3->dummy, hh->prim, msg); +// else if (l3->debug & L3_DEB_WARN) + l3_debug(l3, "dss1 dummy Callref (no facility msg)"); + free_msg(msg); + return(0); + } else if ((((msg->data[1] & 0x0f) == 1) && (0==(cr & 0x7f))) || + (((msg->data[1] & 0x0f) == 2) && (0==(cr & 0x7fff)))) { + /* Global CallRef */ + if (l3->debug & L3_DEB_STATE) + l3_debug(l3, "dss1 Global CallRef"); +// global_handler(l3, l3m.mt, msg); + free_msg(msg); + return(0); + } + dprint(DBGM_L3, "%s: mt(%x)\n", __FUNCTION__, l3m.mt); + proc = find_proc(l3->proc, hh->dinfo, cr); + dprint(DBGM_L3, "%s: proc(%p)\n", __FUNCTION__, proc); + if (!proc) { + if (l3m.mt == MT_SETUP) { + /* Setup creates a new transaction process */ + if (msg->data[2] & 0x80) { + /* Setup with wrong CREF flag */ + if (l3->debug & L3_DEB_STATE) + l3_debug(l3, "dss1 wrong CRef flag"); + free_msg(msg); + return(0); + } + dprint(DBGM_L3, "%s: MT_SETUP\n", __FUNCTION__); + if (!(proc = create_proc(l3, hh->dinfo, cr, NULL))) { + /* May be to answer with RELEASE_COMPLETE and + * CAUSE 0x2f "Resource unavailable", but this + * need a new_l3_process too ... arghh + */ + free_msg(msg); + return(0); + } + dprint(DBGM_L3, "%s: proc(%p)\n", __FUNCTION__, proc); + APPEND_TO_LIST(proc, l3->proc); + } else { + dprint(DBGM_L3, "%s: mt(%x) do not create proc\n", __FUNCTION__, + l3m.mt); + free_msg(msg); + return(0); + } + } + if ((proc->ces & 0xfffffff0) == 0xfff0) { + dprint(DBGM_L3, "%s: master state %d found\n", __FUNCTION__, + proc->state); + msg_push(msg, mISDN_HEAD_SIZE); + send_proc(proc, IMSG_MASTER_L2_DATA, &l3m); + } else + send_proc(proc, IMSG_L2_DATA, &l3m); + free_msg(msg); + return(0); +} + +int +l3_muxer(net_stack_t *nst, msg_t *msg) +{ + mISDN_head_t *hh; + int ret = -EINVAL; + + hh = (mISDN_head_t *)msg->data; + dprint(DBGM_L3, "%s: msg len(%d)\n", __FUNCTION__, msg->len); + dprint(DBGM_L3, "%s: pr(%x) di(%x)\n", __FUNCTION__, + hh->prim, hh->dinfo); + msg_pull(msg, mISDN_HEAD_SIZE); + if (hh->prim == (DL_DATA | INDICATION)) { + ret = dl_data_mux(nst->layer3, hh, msg); + } else { + ret = l3_msg(nst->layer3, hh->prim, hh->dinfo, msg); + } + if (ret) + free_msg(msg); + ret = 0; + return(ret); +} + +static int +manager_l3(net_stack_t *nst, msg_t *msg) +{ + mISDN_head_t *hh; + layer3_proc_t *proc; + struct _l3_msg l3m; + + hh = (mISDN_head_t *)msg->data; + dprint(DBGM_L3, "%s: msg len(%d)\n", __FUNCTION__, msg->len); + dprint(DBGM_L3, "%s: pr(%x) di(%x)\n", __FUNCTION__, + hh->prim, hh->dinfo); + msg_pull(msg, mISDN_HEAD_SIZE); + proc = find_proc(nst->layer3->proc, hh->dinfo & 0xffff, + (hh->dinfo>>16)& 0xffff); + if (!proc) { + if (hh->prim == (CC_SETUP | REQUEST)) { + int l4id; + nst->layer3->next_cr++; + if (nst->layer3->next_cr>126) + nst->layer3->next_cr = 1; + proc = create_proc(nst->layer3, hh->dinfo & 0xffff, + nst->layer3->next_cr | 0x80, NULL); + // TODO Errorhandling + dprint(DBGM_L3, "%s: proc(%p)\n", __FUNCTION__, proc); + APPEND_TO_LIST(proc, nst->layer3->proc); + l4id = proc->ces | (proc->callref << 16); + if_link(nst->manager, (ifunc_t)nst->l3_manager, CC_SETUP | + CONFIRM, hh->dinfo, sizeof(int), &l4id, 0); + } + } + if (!proc) { + dprint(DBGM_L3, "%s: pr(%x) no proc id %x found\n", __FUNCTION__, + hh->prim, hh->dinfo); + free_msg(msg); + return(0); + } + l3m.mt = hh->prim; + if (msg->len) + l3m.msg = msg; + else { + dprint(DBGM_L3, "%s: pr(%x) id(%x) zero param\n", __FUNCTION__, + hh->prim, hh->dinfo); + l3m.msg = NULL; + } + send_proc(proc, IMSG_L4_DATA, &l3m); + free_msg(msg); + return(0); +} + +static void +release_l3(layer3_t *l3) { + dprint(DBGM_L3, "%s(%p)\n", __FUNCTION__, l3); + while(l3->proc) { + dprint(DBGM_L3, "%s: rel_proc ces(%x)\n", __FUNCTION__, + l3->proc->ces); + send_proc(l3->proc, IMSG_END_PROC, NULL); + } + msg_queue_purge(&l3->squeue); + REMOVE_FROM_LISTBASE(l3, l3->nst->layer3); + free(l3); +} + +static int +mISDN_l3up(layer3_proc_t *l3p, msg_t *msg) +{ + int err = -EINVAL; + + if (!l3p || !l3p->l3 || !l3p->l3->nst) + return(-EINVAL); + if (l3p->l3->nst->l3_manager) + err = l3p->l3->nst->l3_manager(l3p->l3->nst->manager, msg); + if (err) + dprint(DBGM_L3, "%s: error %d\n", __FUNCTION__, err); + return(err); +} + +static int +l3down(layer3_t *l3, u_int prim, int dinfo, msg_t *msg) { + int err = -EINVAL; + + if (!msg) + err = if_link(l3->nst, l3->nst->l3_l2, prim, dinfo, 0, NULL, 0); + else + err = if_addhead(l3->nst, l3->nst->l3_l2, prim, dinfo, msg); + return(err); +} + +static void +send_squeue(layer3_t *l3) +{ + msg_t *msg; + + while((msg = msg_dequeue(&l3->squeue))) { + if (l3->nst->l3_l2(l3->nst, msg)) + free_msg(msg); + } +} + +static int +l3_msg(layer3_t *l3, u_int pr, int dinfo, void *arg) +{ + msg_t *msg = arg; + + dprint(DBGM_L3, "%s: pr(%x) di(%x) arg(%p)\n", __FUNCTION__, + pr, dinfo, arg); + switch (pr) { + case (DL_UNITDATA | REQUEST): + return(l3down(l3, pr, dinfo, arg)); + case (DL_DATA | REQUEST): + if (l3->l2_state == ST_L3_LC_ESTAB) { + return(l3down(l3, pr, dinfo, arg)); + } else { + mISDN_addhead(pr, dinfo, msg); + msg_queue_tail(&l3->squeue, msg); + l3->l2_state = ST_L3_LC_ESTAB_WAIT; + l3down(l3, DL_ESTABLISH | REQUEST, dinfo, NULL); + return(0); + } + break; + case (DL_DATA | CONFIRM): + break; + case (DL_ESTABLISH | REQUEST): + if (l3->l2_state != ST_L3_LC_ESTAB) { + l3down(l3, pr, dinfo, NULL); + l3->l2_state = ST_L3_LC_ESTAB_WAIT; + } + break; + case (DL_ESTABLISH | CONFIRM): + if (l3->l2_state != ST_L3_LC_REL_WAIT) { + l3->l2_state = ST_L3_LC_ESTAB; + send_squeue(l3); + } + break; + case (DL_ESTABLISH | INDICATION): + if (l3->l2_state == ST_L3_LC_REL) { + l3->l2_state = ST_L3_LC_ESTAB; + send_squeue(l3); + } + break; + case (DL_RELEASE | INDICATION): + if (l3->l2_state == ST_L3_LC_ESTAB) { + l3->l2_state = ST_L3_LC_REL; + } + break; + case (DL_RELEASE | CONFIRM): + if (l3->l2_state == ST_L3_LC_REL_WAIT) { + l3->l2_state = ST_L3_LC_REL; + } + break; + case (DL_RELEASE | REQUEST): + if (l3->l2_state == ST_L3_LC_ESTAB) { + l3down(l3, pr, dinfo, NULL); + l3->l2_state = ST_L3_LC_REL_WAIT; + } + break; + } + if (msg) + free_msg(msg); + return(0); +} + +int Isdnl3Init(net_stack_t *nst) +{ + layer3_t *l3; + + l3 = malloc(sizeof(layer3_t)); + if (!l3) + return(-ENOMEM); + memset(l3, 0, sizeof(layer3_t)); + l3->nst = nst; + nst->l2_l3 = l3_muxer; + nst->manager_l3 = manager_l3; + l3->debug = 0xff; + msg_queue_init(&l3->squeue); + l3->l2_state = ST_L3_LC_REL; + APPEND_TO_LIST(l3, nst->layer3); + return(0); +} + +void cleanup_Isdnl3(net_stack_t *nst) +{ + if (nst->layer3) { + dprint(DBGM_L3, "%s: l3 list not empty\n", __FUNCTION__); + while(nst->layer3) + release_l3(nst->layer3); + } +} diff --git a/i4lnet/net_l3.h b/i4lnet/net_l3.h new file mode 100644 index 0000000..38e7d2e --- /dev/null +++ b/i4lnet/net_l3.h @@ -0,0 +1,257 @@ +/* $Id: net_l3.h,v 0.9 2003/08/27 07:33:03 kkeil Exp $ + * + * Layer 3 defines + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#ifndef NET_L3_H +#define NET_L3_H + +#include "isdn_net.h" + +typedef struct _layer3_proc layer3_proc_t; +typedef struct _L3Timer L3Timer_t; + +struct _L3Timer { + layer3_proc_t *pc; + itimer_t tl; + int nr; +}; + +struct _layer3_proc { + layer3_proc_t *prev; + layer3_proc_t *next; + layer3_proc_t *child; + layer3_proc_t *master; + layer3_t *l3; + int callref; + int ces; + int selces; + int state; + int Flags; + L3Timer_t timer1; + L3Timer_t timer2; + int bc; + int err; + int cause; + u_char obuf[MAX_DFRAME_LEN]; + u_char *op; +}; + +#define FLG_L3P_TIMER312 1 +#define FLG_L3P_TIMER303_1 2 +#define FLG_L3P_TIMER308_1 3 +#define FLG_L3P_GOTRELCOMP 4 + +struct _layer3 { + layer3_t *prev; + layer3_t *next; + msg_queue_t squeue; + int l2_state; + int next_cr; + int debug; + net_stack_t *nst; + layer3_proc_t *proc; +}; + +static inline msg_t *l3_alloc_msg(int size) +{ + msg_t *msg; + + msg = alloc_msg(size+MAX_HEADER_LEN); + if (msg) + msg_reserve(msg, MAX_HEADER_LEN); + return(msg); +} + +extern int Isdnl3Init(net_stack_t *); +extern void cleanup_Isdnl3(net_stack_t *); +extern void display_NR_IE(u_char *, char *); + +/* l3 pointer arrays */ + +typedef struct _ALERTING { + u_char *BEARER; + u_char *CHANNEL_ID; + u_char *FACILITY; + u_char *PROGRESS; + u_char *DISPLAY; + u_char *SIGNAL; + u_char *HLC; + u_char *USER_USER; +} ALERTING_t; + +typedef struct _CALL_PROCEEDING { + u_char *BEARER; + u_char *CHANNEL_ID; + u_char *FACILITY; + u_char *PROGRESS; + u_char *DISPLAY; + u_char *HLC; +} CALL_PROCEEDING_t; + +typedef struct _CONNECT { + u_char *BEARER; + u_char *CHANNEL_ID; + u_char *FACILITY; + u_char *PROGRESS; + u_char *DISPLAY; + u_char *DATE; + u_char *SIGNAL; + u_char *CONNECT_PN; + u_char *CONNECT_SUB; + u_char *LLC; + u_char *HLC; + u_char *USER_USER; +} CONNECT_t; + +typedef struct _CONNECT_ACKNOWLEDGE { + u_char *CHANNEL_ID; + u_char *DISPLAY; + u_char *SIGNAL; +} CONNECT_ACKNOWLEDGE_t; + +typedef struct _DISCONNECT { + u_char *CAUSE; + u_char *FACILITY; + u_char *PROGRESS; + u_char *DISPLAY; + u_char *SIGNAL; + u_char *USER_USER; +} DISCONNECT_t; + +typedef struct _INFORMATION { + u_char *COMPLETE; + u_char *DISPLAY; + u_char *KEYPAD; + u_char *SIGNAL; + u_char *CALLED_PN; +} INFORMATION_t; + +typedef struct _NOTIFY { + u_char *BEARER; + u_char *NOTIFY; + u_char *DISPLAY; +} NOTIFY_t; + +typedef struct _PROGRESS { + u_char *BEARER; + u_char *CAUSE; + u_char *FACILITY; + u_char *PROGRESS; + u_char *DISPLAY; + u_char *HLC; + u_char *USER_USER; +} PROGRESS_t; + +typedef struct _RELEASE { + u_char *CAUSE; + u_char *FACILITY; + u_char *DISPLAY; + u_char *SIGNAL; + u_char *USER_USER; +} RELEASE_t; + +typedef struct _RELEASE_COMPLETE { + u_char *CAUSE; + u_char *FACILITY; + u_char *DISPLAY; + u_char *SIGNAL; + u_char *USER_USER; +} RELEASE_COMPLETE_t; + +typedef struct _RESUME { + u_char *CALL_ID; + u_char *FACILITY; +} RESUME_t; + +typedef struct _RESUME_ACKNOWLEDGE { + u_char *CHANNEL_ID; + u_char *FACILITY; + u_char *DISPLAY; +} RESUME_ACKNOWLEDGE_t; + +typedef struct _RESUME_REJECT { + u_char *CAUSE; + u_char *DISPLAY; +} RESUME_REJECT_t; + +typedef struct _SETUP { + u_char *COMPLETE; + u_char *BEARER; + u_char *CHANNEL_ID; + u_char *FACILITY; + u_char *PROGRESS; + u_char *NET_FAC; + u_char *DISPLAY; + u_char *KEYPAD; + u_char *SIGNAL; + u_char *CALLING_PN; + u_char *CALLING_SUB; + u_char *CALLED_PN; + u_char *CALLED_SUB; + u_char *REDIR_NR; + u_char *LLC; + u_char *HLC; + u_char *USER_USER; +} SETUP_t; + +typedef struct _SETUP_ACKNOWLEDGE { + u_char *CHANNEL_ID; + u_char *FACILITY; + u_char *PROGRESS; + u_char *DISPLAY; + u_char *SIGNAL; +} SETUP_ACKNOWLEDGE_t; + +typedef struct _STATUS { + u_char *CAUSE; + u_char *CALL_STATE; + u_char *DISPLAY; +} STATUS_t; + +typedef struct _STATUS_ENQUIRY { + u_char *DISPLAY; +} STATUS_ENQUIRY_t; + +typedef struct _SUSPEND { + u_char *CALL_ID; + u_char *FACILITY; +} SUSPEND_t; + +typedef struct _SUSPEND_ACKNOWLEDGE { + u_char *FACILITY; + u_char *DISPLAY; +} SUSPEND_ACKNOWLEDGE_t; + +typedef struct _SUSPEND_REJECT { + u_char *CAUSE; + u_char *DISPLAY; +} SUSPEND_REJECT_t; + +typedef struct _CONGESTION_CONTROL { + u_char *CONGESTION; + u_char *CAUSE; + u_char *DISPLAY; +} CONGESTION_CONTROL_t; + +typedef struct _USER_INFORMATION { + u_char *MORE_DATA; + u_char *USER_USER; +} USER_INFORMATION_t; + +typedef struct _RESTART { + u_char *CHANNEL_ID; + u_char *DISPLAY; + u_char *RESTART_IND; +} RESTART_t; + +typedef struct _FACILITY { + u_char *FACILITY; + u_char *DISPLAY; +} FACILITY_t; + + +#endif diff --git a/i4lnet/nettst.c b/i4lnet/nettst.c new file mode 100644 index 0000000..44a85ed --- /dev/null +++ b/i4lnet/nettst.c @@ -0,0 +1,320 @@ +#include +#include +#include +#include +#include "isdn_net.h" +#include "net_l2.h" +#include "net_l3.h" +#include "net_l4.h" +#include "l3dss1.h" +#include "helper.h" +#include "bchannel.h" +#include "tone.h" + +net_stack_t kern_if; + +itimer_t timer1; + +static void +do_cleanup(net_stack_t *nst) +{ + fprintf(stderr, "%s\n", __FUNCTION__); + cleanup_Isdnl4(nst); + cleanup_Isdnl3(nst); + cleanup_Isdnl2(nst); + do_net_stack_cleanup(nst); +} + +static void +term_handler(int sig) +{ + pthread_t tid; + + tid = pthread_self(); + fprintf(stderr,"signal %d received from thread %ld\n", sig, tid); + test_and_set_bit(FLG_KIF_TERMINATION, &kern_if.flag); + sem_post(&kern_if.network); +} + +static int +man_down(net_stack_t *nst, msg_t *msg) +{ + msg_queue_tail(&nst->wqueue, msg); + sem_post(&nst->network); + return(0); +} + +static int +do_disconnect(layer4_t *l4) +{ + l4->cause_loc = CAUSE_LOC_PNET_LOCUSER; + l4->cause_val = CAUSE_NORMAL_CLEARING; + l4->progress = PROGRESS_TONE; + if_link(l4->nst, man_down, MAN_CLEAR_CALL | REQUEST, + l4->channel, 0, NULL, 0); + return(0); +} + +static int +do_connect(layer4_t *l4) +{ + if_link(l4->nst, man_down, MAN_CONNECT | REQUEST, + l4->channel, 0, NULL, 0); + return(0); +} + +static int +do_alert(layer4_t *l4) +{ + if_link(l4->nst, man_down, MAN_ALERT | REQUEST, + l4->channel, 0, NULL, 0); + return(0); +} + +static int +clear_call(layer4_t *l4) +{ + if (l4->sdata) { + layer4_t *peer = l4->sdata; + + if (l4->cause_val) { + peer->cause_loc = l4->cause_loc; + peer->cause_val = l4->cause_val; + } else { + peer->cause_loc = CAUSE_LOC_PNET_LOCUSER; + peer->cause_val = CAUSE_NORMALUNSPECIFIED; + } + peer->progress = PROGRESS_TONE; + peer->sbuf = NULL; + peer->sdata = NULL; + peer->rdata = NULL; + if (peer->nst) + if_link(peer->nst, man_down, MAN_CLEAR_CALL | + REQUEST, peer->channel, 0, NULL, 0); + } + l4->sdata = NULL; + l4->rdata = NULL; + l4->sbuf = NULL; + return(0); +} + +static int +alert_call(layer4_t *l4) +{ + if (l4->sdata) + do_alert(l4->sdata); + return(0); +} + +static int +connect_call(layer4_t *l4) +{ + strcpy(l4->display,"connect ack"); + if_link(l4->nst, man_down, MAN_CONNECT | RESPONSE, + l4->channel, 0, NULL, 0); + del_timer(&timer1); + if (l4->sdata) + do_connect(l4->sdata); + return(0); +} + +static int +route_call(layer4_t *l4) +{ + layer4_t *newl4; + + fprintf(stderr, "%s: msn ", __FUNCTION__); + display_NR_IE(l4->msn); + fprintf(stderr, "%s: nr ", __FUNCTION__); + display_NR_IE(l4->nr); + if (l4->usednr->typ == NR_TYPE_INTERN) { + newl4 = get_free_channel(&kern_if, -1, NULL); + if (!newl4) { + l4->cause_loc = CAUSE_LOC_PNET_LOCUSER; + l4->cause_val = CAUSE_USER_BUSY; + l4->progress = PROGRESS_TONE; + if_link(l4->nst, man_down, MAN_CLEAR_CALL | REQUEST, + l4->channel, 0, NULL, 0); + return(0); + } + l4->sdata = newl4; + l4->rdata = newl4; + newl4->sdata = l4; + newl4->rdata = l4; + l4->sbuf = &newl4->rbuf; + newl4->sbuf = &l4->rbuf; + newl4->msn[0] = l4->usednr->len +1; + newl4->msn[1] = 0x81; + memcpy(&newl4->msn[2], l4->usednr->nr, l4->usednr->len); + if (l4->msn[0]) + memcpy(newl4->nr, l4->msn, l4->msn[0] + 1); + newl4->l1_prot = ISDN_PID_L1_B_64TRANS; + if_link(newl4->nst, man_down, MAN_SETUP | REQUEST, + newl4->channel, 0, NULL, 0); + } else if (l4->usednr->typ == NR_TYPE_AUDIO) { + l4->sdata = NULL; + l4->rdata = NULL; + strcpy(l4->display,"connect to AUDIO"); + do_connect(l4); + l4->display[0] = 0; + deactivate_bchannel(l4); + setup_bchannel_rawdev(l4); + activate_bchannel(l4); + } else if (l4->usednr->typ == NR_TYPE_VOIP) { + l4->sdata = NULL; + l4->rdata = NULL; + sprintf(l4->display,"calling %s", l4->usednr->name); + do_alert(l4); + sprintf(l4->display,"connect to %s", l4->usednr->name); + do_connect(l4); + l4->display[0] = 0; + deactivate_bchannel(l4); + setup_bchannel_rawdev(l4); + activate_bchannel(l4); + } + return(0); +} + +static int +manager(net_stack_t *nst, msg_t *msg) { + mISDN_head_t *hh; + layer4_t *l4; + + if (!msg) + return(-EINVAL); + hh = (mISDN_head_t *)msg->data; + msg_pull(msg, mISDN_HEAD_SIZE); + fprintf(stderr, "%s: prim(%x) msg->len(%d)\n", __FUNCTION__, + hh->prim, msg->len); + if (hh->dinfo == 1) { + l4 = &nst->layer4[0]; + } else if (hh->dinfo == 2) { + l4 = &nst->layer4[1]; + } else { + return(-EINVAL); + } + switch(hh->prim) { + case MAN_SETUP | INDICATION: + fprintf(stderr, "%s: setup id(%x)\n", __FUNCTION__, + hh->dinfo); + route_call(l4); + break; + case MAN_ALERT | INDICATION: + fprintf(stderr, "%s: connect id(%x)\n", __FUNCTION__, + hh->dinfo); + alert_call(l4); + break; + case MAN_CONNECT | INDICATION: + fprintf(stderr, "%s: connect id(%x)\n", __FUNCTION__, + hh->dinfo); + connect_call(l4); + break; + case MAN_CLEAR_CALL | INDICATION: + fprintf(stderr, "%s: clear call id(%x)\n", __FUNCTION__, + hh->dinfo); + clear_call(l4); + break; + default: + fprintf(stderr, "%s: unhandled prim(%x) msg->len(%d)\n", __FUNCTION__, + hh->prim, msg->len); + break; + } + free_msg(msg); + return(0); +} + + +int main(argc,argv) +int argc; +char *argv[]; + +{ + int ret, *retp; + nr_list_t *nr1,*nr2,*nr3,*nr4,*nr5; + layer4_t *l4; + if_action_t mISDNrd,*hrd; + + + nr1 = malloc(sizeof(nr_list_t)); + nr2 = malloc(sizeof(nr_list_t)); + nr3 = malloc(sizeof(nr_list_t)); + nr4 = malloc(sizeof(nr_list_t)); + nr5 = malloc(sizeof(nr_list_t)); + memset(nr1, 0, sizeof(nr_list_t)); + memset(nr2, 0, sizeof(nr_list_t)); + memset(nr3, 0, sizeof(nr_list_t)); + memset(nr4, 0, sizeof(nr_list_t)); + memset(nr5, 0, sizeof(nr_list_t)); + nr1->len = 5; + strcpy(nr1->nr,"12345"); + nr1->typ = NR_TYPE_INTERN; + nr2->len = 4; + strcpy(nr2->nr,"4566"); + nr2->typ = NR_TYPE_INTERN; + nr3->len = 3; + strcpy(nr3->nr,"789"); + nr3->typ = NR_TYPE_AUDIO; + nr4->len = 3; + strcpy(nr4->nr,"147"); + strcpy(nr4->name, "pingi2"); + nr4->typ = NR_TYPE_VOIP; + nr5->len = 3; + strcpy(nr5->nr,"258"); + strcpy(nr5->name, "pingi2"); + nr5->typ = NR_TYPE_VOIP; + msg_init(); + ret = do_net_stack_setup(&kern_if); + if (ret) { + fprintf(stderr, "error in do_net_stack_setup %d\n", ret); + return(0); + } + APPEND_TO_LIST(nr1, kern_if.nrlist); + APPEND_TO_LIST(nr2, kern_if.nrlist); + APPEND_TO_LIST(nr3, kern_if.nrlist); + APPEND_TO_LIST(nr4, kern_if.nrlist); + APPEND_TO_LIST(nr5, kern_if.nrlist); + Isdnl2Init(&kern_if); + Isdnl3Init(&kern_if); + Isdnl4Init(&kern_if); + kern_if.l4_mgr = manager; + init_bhandler(&kern_if); + memset(&timer1, 0, sizeof(itimer_t)); + signal(SIGTERM, term_handler); + signal(SIGINT, term_handler); + signal(SIGPIPE, term_handler); + if (argc>1) { + l4 = get_free_channel(&kern_if, -1, NULL); + if (l4) { + l4->msn[0] = 4; + l4->msn[1] = 0x81; + l4->msn[2] = '8'; + l4->msn[3] = '8'; + l4->msn[4] = '8'; + + l4->nr[0] = 4; + l4->nr[1] = 0x81; + l4->nr[2] = '1'; + l4->nr[3] = '2'; + l4->nr[4] = '3'; + l4->l1_prot = ISDN_PID_L1_B_64TRANS; + if_link(l4->nst, man_down, MAN_SETUP | REQUEST, + l4->channel, 0, NULL, 0); + del_timer(&timer1); + timer1.function = (void *)do_disconnect; + timer1.data = (long)l4; + init_timer(&timer1, &kern_if); + timer1.expires = 8000; + add_timer(&timer1); + } + } + hrd = &mISDNrd; + memset(hrd, 0, sizeof(if_action_t)); + hrd->nst = &kern_if; + hrd->fd = kern_if.device; + hrd->function = do_net_read; + APPEND_TO_LIST(hrd, kern_if.rd); + retp = do_netthread(&kern_if); + fprintf(stderr, "do_main_loop returns(%p)\n", retp); + do_cleanup(&kern_if); + return(0); +} diff --git a/i4lnet/sndloop.c b/i4lnet/sndloop.c new file mode 100644 index 0000000..03bc25b --- /dev/null +++ b/i4lnet/sndloop.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned char ulaw_to_Alaw[256] = { + 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, + 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, + 0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, + 0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, + 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, 0x02, + 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, 0x1a, + 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, 0x12, + 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x6b, + 0x68, 0x69, 0x6e, 0x6f, 0x6c, 0x6d, 0x62, 0x63, + 0x60, 0x61, 0x66, 0x67, 0x64, 0x65, 0x7b, 0x79, + 0x7e, 0x7f, 0x7c, 0x7d, 0x72, 0x73, 0x70, 0x71, + 0x76, 0x77, 0x74, 0x75, 0x4b, 0x49, 0x4f, 0x4d, + 0x42, 0x43, 0x40, 0x41, 0x46, 0x47, 0x44, 0x45, + 0x5a, 0x5b, 0x58, 0x59, 0x5e, 0x5f, 0x5c, 0x5d, + 0x52, 0x52, 0x53, 0x53, 0x50, 0x50, 0x51, 0x51, + 0x56, 0x56, 0x57, 0x57, 0x54, 0x54, 0x55, 0xd5, + 0xaa, 0xab, 0xa8, 0xa9, 0xae, 0xaf, 0xac, 0xad, + 0xa2, 0xa3, 0xa0, 0xa1, 0xa6, 0xa7, 0xa4, 0xa5, + 0xba, 0xbb, 0xb8, 0xb9, 0xbe, 0xbf, 0xbc, 0xbd, + 0xb2, 0xb3, 0xb0, 0xb1, 0xb6, 0xb7, 0xb4, 0xb5, + 0x8b, 0x88, 0x89, 0x8e, 0x8f, 0x8c, 0x8d, 0x82, + 0x83, 0x80, 0x81, 0x86, 0x87, 0x84, 0x85, 0x9a, + 0x9b, 0x98, 0x99, 0x9e, 0x9f, 0x9c, 0x9d, 0x92, + 0x93, 0x90, 0x91, 0x96, 0x97, 0x94, 0x95, 0xeb, + 0xe8, 0xe9, 0xee, 0xef, 0xec, 0xed, 0xe2, 0xe3, + 0xe0, 0xe1, 0xe6, 0xe7, 0xe4, 0xe5, 0xfb, 0xf9, + 0xfe, 0xff, 0xfc, 0xfd, 0xf2, 0xf3, 0xf0, 0xf1, + 0xf6, 0xf7, 0xf4, 0xf5, 0xcb, 0xc9, 0xcf, 0xcd, + 0xc2, 0xc3, 0xc0, 0xc1, 0xc6, 0xc7, 0xc4, 0xc5, + 0xda, 0xdb, 0xd8, 0xd9, 0xde, 0xdf, 0xdc, 0xdd, + 0xd2, 0xd2, 0xd3, 0xd3, 0xd0, 0xd0, 0xd1, 0xd1, + 0xd6, 0xd6, 0xd7, 0xd7, 0xd4, 0xd4, 0xd5, 0xd5, + +}; + +unsigned char Alaw_to_ulaw[256] = { + 0x29, 0x2a, 0x27, 0x28, 0x2d, 0x2e, 0x2b, 0x2c, + 0x21, 0x22, 0x1f, 0x20, 0x25, 0x26, 0x23, 0x24, + 0x39, 0x3a, 0x37, 0x38, 0x3d, 0x3e, 0x3b, 0x3c, + 0x31, 0x32, 0x2f, 0x30, 0x35, 0x36, 0x33, 0x34, + 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, + 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, + 0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, + 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, + 0x62, 0x63, 0x60, 0x61, 0x66, 0x67, 0x64, 0x65, + 0x5d, 0x5d, 0x5c, 0x5c, 0x5f, 0x5f, 0x5e, 0x5e, + 0x74, 0x76, 0x70, 0x72, 0x7c, 0x7e, 0x78, 0x7a, + 0x6a, 0x6b, 0x68, 0x69, 0x6e, 0x6f, 0x6c, 0x6d, + 0x48, 0x49, 0x46, 0x47, 0x4c, 0x4d, 0x4a, 0x4b, + 0x40, 0x41, 0x3f, 0x3f, 0x44, 0x45, 0x42, 0x43, + 0x56, 0x57, 0x54, 0x55, 0x5a, 0x5b, 0x58, 0x59, + 0x4f, 0x4f, 0x4e, 0x4e, 0x52, 0x53, 0x50, 0x51, + 0xa9, 0xaa, 0xa7, 0xa8, 0xad, 0xae, 0xab, 0xac, + 0xa1, 0xa2, 0x9f, 0xa0, 0xa5, 0xa6, 0xa3, 0xa4, + 0xb9, 0xba, 0xb7, 0xb8, 0xbd, 0xbe, 0xbb, 0xbc, + 0xb1, 0xb2, 0xaf, 0xb0, 0xb5, 0xb6, 0xb3, 0xb4, + 0x8a, 0x8b, 0x88, 0x89, 0x8e, 0x8f, 0x8c, 0x8d, + 0x82, 0x83, 0x80, 0x81, 0x86, 0x87, 0x84, 0x85, + 0x9a, 0x9b, 0x98, 0x99, 0x9e, 0x9f, 0x9c, 0x9d, + 0x92, 0x93, 0x90, 0x91, 0x96, 0x97, 0x94, 0x95, + 0xe2, 0xe3, 0xe0, 0xe1, 0xe6, 0xe7, 0xe4, 0xe5, + 0xdd, 0xdd, 0xdc, 0xdc, 0xdf, 0xdf, 0xde, 0xde, + 0xf4, 0xf6, 0xf0, 0xf2, 0xfc, 0xfe, 0xf8, 0xfa, + 0xea, 0xeb, 0xe8, 0xe9, 0xee, 0xef, 0xec, 0xed, + 0xc8, 0xc9, 0xc6, 0xc7, 0xcc, 0xcd, 0xca, 0xcb, + 0xc0, 0xc1, 0xbf, 0xbf, 0xc4, 0xc5, 0xc2, 0xc3, + 0xd6, 0xd7, 0xd4, 0xd5, 0xda, 0xdb, 0xd8, 0xd9, + 0xcf, 0xcf, 0xce, 0xce, 0xd2, 0xd3, 0xd0, 0xd1, +}; + + +int main(argc,argv) +int argc; +char *argv[]; +{ + int audio_out; + int mISDN_in; + int format; + int cnt, wcnt, i; + int n,sel; + fd_set fdr; + unsigned char buf[128]; + + if (argc<=1) + exit(1); + audio_out = open("/dev/audio", O_WRONLY | O_NONBLOCK); + if (0 > audio_out) { + fprintf(stderr, "cannot open /dev/audio for write:%s\n", + strerror(errno)); + exit(1); + } + mISDN_in = open(argv[1], O_RDONLY | O_NONBLOCK); + if (0 > mISDN_in) { + close(audio_out); + fprintf(stderr, "cannot open %s for read:%s\n", + argv[1], strerror(errno)); + exit(1); + } + format = AFMT_MU_LAW; + fprintf(stdout, "audio format %x\n", format); + if (ioctl(audio_out, SNDCTL_DSP_SETFMT, &format) == -1) { + fprintf(stderr, "ioctl SNDCTL_DSP_SETFMT %s\n", + strerror(errno)); + } else + fprintf(stdout, "audio format %x\n", format); + while(1) { + cnt = read(mISDN_in, buf, 128); + fprintf(stdout, "mISDN_in %d bytes\n", cnt); + if (cnt>0) { + for (i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned char ulaw_to_Alaw[256] = { + 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, + 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, + 0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, + 0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, + 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, 0x02, + 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, 0x1a, + 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, 0x12, + 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x6b, + 0x68, 0x69, 0x6e, 0x6f, 0x6c, 0x6d, 0x62, 0x63, + 0x60, 0x61, 0x66, 0x67, 0x64, 0x65, 0x7b, 0x79, + 0x7e, 0x7f, 0x7c, 0x7d, 0x72, 0x73, 0x70, 0x71, + 0x76, 0x77, 0x74, 0x75, 0x4b, 0x49, 0x4f, 0x4d, + 0x42, 0x43, 0x40, 0x41, 0x46, 0x47, 0x44, 0x45, + 0x5a, 0x5b, 0x58, 0x59, 0x5e, 0x5f, 0x5c, 0x5d, + 0x52, 0x52, 0x53, 0x53, 0x50, 0x50, 0x51, 0x51, + 0x56, 0x56, 0x57, 0x57, 0x54, 0x54, 0x55, 0xd5, + 0xaa, 0xab, 0xa8, 0xa9, 0xae, 0xaf, 0xac, 0xad, + 0xa2, 0xa3, 0xa0, 0xa1, 0xa6, 0xa7, 0xa4, 0xa5, + 0xba, 0xbb, 0xb8, 0xb9, 0xbe, 0xbf, 0xbc, 0xbd, + 0xb2, 0xb3, 0xb0, 0xb1, 0xb6, 0xb7, 0xb4, 0xb5, + 0x8b, 0x88, 0x89, 0x8e, 0x8f, 0x8c, 0x8d, 0x82, + 0x83, 0x80, 0x81, 0x86, 0x87, 0x84, 0x85, 0x9a, + 0x9b, 0x98, 0x99, 0x9e, 0x9f, 0x9c, 0x9d, 0x92, + 0x93, 0x90, 0x91, 0x96, 0x97, 0x94, 0x95, 0xeb, + 0xe8, 0xe9, 0xee, 0xef, 0xec, 0xed, 0xe2, 0xe3, + 0xe0, 0xe1, 0xe6, 0xe7, 0xe4, 0xe5, 0xfb, 0xf9, + 0xfe, 0xff, 0xfc, 0xfd, 0xf2, 0xf3, 0xf0, 0xf1, + 0xf6, 0xf7, 0xf4, 0xf5, 0xcb, 0xc9, 0xcf, 0xcd, + 0xc2, 0xc3, 0xc0, 0xc1, 0xc6, 0xc7, 0xc4, 0xc5, + 0xda, 0xdb, 0xd8, 0xd9, 0xde, 0xdf, 0xdc, 0xdd, + 0xd2, 0xd2, 0xd3, 0xd3, 0xd0, 0xd0, 0xd1, 0xd1, + 0xd6, 0xd6, 0xd7, 0xd7, 0xd4, 0xd4, 0xd5, 0xd5, + +}; + +unsigned char Alaw_to_ulaw[256] = { + 0x29, 0x2a, 0x27, 0x28, 0x2d, 0x2e, 0x2b, 0x2c, + 0x21, 0x22, 0x1f, 0x20, 0x25, 0x26, 0x23, 0x24, + 0x39, 0x3a, 0x37, 0x38, 0x3d, 0x3e, 0x3b, 0x3c, + 0x31, 0x32, 0x2f, 0x30, 0x35, 0x36, 0x33, 0x34, + 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, + 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, + 0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, + 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, + 0x62, 0x63, 0x60, 0x61, 0x66, 0x67, 0x64, 0x65, + 0x5d, 0x5d, 0x5c, 0x5c, 0x5f, 0x5f, 0x5e, 0x5e, + 0x74, 0x76, 0x70, 0x72, 0x7c, 0x7e, 0x78, 0x7a, + 0x6a, 0x6b, 0x68, 0x69, 0x6e, 0x6f, 0x6c, 0x6d, + 0x48, 0x49, 0x46, 0x47, 0x4c, 0x4d, 0x4a, 0x4b, + 0x40, 0x41, 0x3f, 0x3f, 0x44, 0x45, 0x42, 0x43, + 0x56, 0x57, 0x54, 0x55, 0x5a, 0x5b, 0x58, 0x59, + 0x4f, 0x4f, 0x4e, 0x4e, 0x52, 0x53, 0x50, 0x51, + 0xa9, 0xaa, 0xa7, 0xa8, 0xad, 0xae, 0xab, 0xac, + 0xa1, 0xa2, 0x9f, 0xa0, 0xa5, 0xa6, 0xa3, 0xa4, + 0xb9, 0xba, 0xb7, 0xb8, 0xbd, 0xbe, 0xbb, 0xbc, + 0xb1, 0xb2, 0xaf, 0xb0, 0xb5, 0xb6, 0xb3, 0xb4, + 0x8a, 0x8b, 0x88, 0x89, 0x8e, 0x8f, 0x8c, 0x8d, + 0x82, 0x83, 0x80, 0x81, 0x86, 0x87, 0x84, 0x85, + 0x9a, 0x9b, 0x98, 0x99, 0x9e, 0x9f, 0x9c, 0x9d, + 0x92, 0x93, 0x90, 0x91, 0x96, 0x97, 0x94, 0x95, + 0xe2, 0xe3, 0xe0, 0xe1, 0xe6, 0xe7, 0xe4, 0xe5, + 0xdd, 0xdd, 0xdc, 0xdc, 0xdf, 0xdf, 0xde, 0xde, + 0xf4, 0xf6, 0xf0, 0xf2, 0xfc, 0xfe, 0xf8, 0xfa, + 0xea, 0xeb, 0xe8, 0xe9, 0xee, 0xef, 0xec, 0xed, + 0xc8, 0xc9, 0xc6, 0xc7, 0xcc, 0xcd, 0xca, 0xcb, + 0xc0, 0xc1, 0xbf, 0xbf, 0xc4, 0xc5, 0xc2, 0xc3, + 0xd6, 0xd7, 0xd4, 0xd5, 0xda, 0xdb, 0xd8, 0xd9, + 0xcf, 0xcf, 0xce, 0xce, 0xd2, 0xd3, 0xd0, 0xd1, +}; + +//#define AUDIOF "/usr/share/sounds/au/linus-english.au" +#define AUDIOF "/dev/audio" +int main(argc,argv) +int argc; +char *argv[]; +{ + int audio_in; + int mISDN_out; + int format; + int cnt, wcnt, i; + int n,sel; + fd_set fdw; + unsigned char buf[128]; + + if (argc<=1) + exit(1); + audio_in = open(AUDIOF, O_RDONLY); + if (0 > audio_in) { + fprintf(stderr, "cannot open " AUDIOF " for read:%s\n", + strerror(errno)); + exit(1); + } + mISDN_out = open(argv[1], O_WRONLY | O_NONBLOCK); + if (0 > mISDN_out) { + close(audio_in); + fprintf(stderr, "cannot open %s for write:%s\n", + argv[1], strerror(errno)); + exit(1); + } + while(1) { + cnt = read(audio_in, buf, 128); + fprintf(stdout, "audio_in %d bytes\n", cnt); + if (cnt>0) { + for (i=0;i0) { + fprintf(stdout, "mISDN_out%d bytes\n", wcnt); + } else if (errno == EAGAIN) { + FD_ZERO(&fdw); + FD_SET(mISDN_out, &fdw); + n = mISDN_out; + n++; + sel = select(n, NULL, &fdw, NULL, NULL); + if (sel<1) { + fprintf(stdout, "sel %d : %s\n", + sel, strerror(errno)); + break; + } + } else { + fprintf(stdout, "mISDN_out: %s\n", + strerror(errno)); + } + } else { + fprintf(stdout, "audio_in: %s\n", + strerror(errno)); + break; + } + } + close(audio_in); + close(mISDN_out); +} diff --git a/i4lnet/tei.c b/i4lnet/tei.c new file mode 100644 index 0000000..436af18 --- /dev/null +++ b/i4lnet/tei.c @@ -0,0 +1,441 @@ +/* $Id: tei.c,v 0.9 2003/08/27 07:33:03 kkeil Exp $ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/mISDN.cert + * + */ +#define __NO_VERSION__ +#include +#include "net_l2.h" +// #include "helper.h" +// #include "debug.h" +// #include + +const char *tei_revision = "$Revision: 0.9 $"; + +#define ID_REQUEST 1 +#define ID_ASSIGNED 2 +#define ID_DENIED 3 +#define ID_CHK_REQ 4 +#define ID_CHK_RES 5 +#define ID_REMOVE 6 +#define ID_VERIFY 7 + +#define TEI_ENTITY_ID 0xf + +static +struct Fsm teifsm = +{NULL, 0, 0, NULL, NULL}; + +enum { + ST_TEI_NOP, + ST_TEI_REMOVE, + ST_TEI_IDVERIFY, +}; + +#define TEI_STATE_COUNT (ST_TEI_IDVERIFY+1) + +static char *strTeiState[] = +{ + "ST_TEI_NOP", + "ST_TEI_REMOVE", + "ST_TEI_IDVERIFY", +}; + +enum { + EV_IDREQ, + EV_ASSIGN, + EV_ASSIGN_REQ, + EV_CHECK_RES, + EV_CHECK_REQ, + EV_REMOVE, + EV_VERIFY, + EV_T201, +}; + +#define TEI_EVENT_COUNT (EV_T201+1) + +static char *strTeiEvent[] = +{ + "EV_IDREQ", + "EV_ASSIGN", + "EV_ASSIGN_REQ", + "EV_CHECK_RES", + "EV_CHECK_REQ", + "EV_REMOVE", + "EV_VERIFY", + "EV_T201", +}; + +static layer2_t +*new_tei_req(net_stack_t *nst) +{ + layer2_t *l2; + int tei; + + for (tei=64;tei<127;tei++) { + l2 = nst->layer2; + while(l2) { + if (l2->tei == tei) + break; + l2 = l2->next; + } + if (!l2) + break; + } + if (tei==127) /* all tei in use */ + return(NULL); + l2 = new_dl2(nst, tei); + return(l2); +} + +unsigned int +random_ri(void) +{ + long int x; + + x = random(); + return (x & 0xffff); +} + +static layer2_t * +find_tei(net_stack_t *nst, int tei) +{ + layer2_t *l2; + + l2 = nst->layer2; + while(l2) { + if (l2->tei == tei) + break; + l2 = l2->next; + } + return(l2); +} + +static void +put_tei_msg(teimgr_t *tm, u_char m_id, unsigned int ri, u_char tei) +{ + msg_t *msg; + u_char bp[8]; + + bp[0] = (TEI_SAPI << 2); + if (test_bit(FLG_LAPD_NET, &tm->l2->flag)) + bp[0] |= 2; /* CR:=1 for net command */ + bp[1] = (GROUP_TEI << 1) | 0x1; + bp[2] = UI; + bp[3] = TEI_ENTITY_ID; + bp[4] = ri >> 8; + bp[5] = ri & 0xff; + bp[6] = m_id; + bp[7] = (tei << 1) | 1; + msg = create_link_msg(MDL_UNITDATA | REQUEST, DINFO_SKB, 8, bp, 0); + if (!msg) { + dprint(DBGM_TEI, "mISDN: No msg for TEI manager\n"); + return; + } + if (tei_l2(tm->l2, msg)) + free_msg(msg); +} + +static void +tei_assign_req(struct FsmInst *fi, int event, void *arg) +{ + teimgr_t *tm = fi->userdata; + u_char *dp = arg; + + if (tm->l2->tei == -1) { + tm->tei_m.printdebug(&tm->tei_m, + "net tei assign request without tei"); + return; + } + tm->ri = ((unsigned int) *dp++ << 8); + tm->ri += *dp++; + if (tm->debug) + tm->tei_m.printdebug(&tm->tei_m, + "net assign request ri %d teim %d", tm->ri, *dp); + put_tei_msg(tm, ID_ASSIGNED, tm->ri, tm->l2->tei); + FsmChangeState(fi, ST_TEI_NOP); +} + +static void +tei_id_chk_res(struct FsmInst *fi, int event, void *arg) +{ + teimgr_t *tm = fi->userdata; + int *ri = arg; + + + if (tm->debug) + tm->tei_m.printdebug(fi, "identity %d check response ri %x/%x", + tm->l2->tei, *ri, tm->ri); + if (tm->ri != -1) { + FsmDelTimer(&tm->t201, 4); + tm->tei_m.printdebug(fi, "duplicat %d response", tm->l2->tei); + tm->val = tm->l2->tei; + put_tei_msg(tm, ID_REMOVE, 0, tm->val); + FsmAddTimer(&tm->t201, tm->T201, EV_T201, NULL, 2); + FsmChangeState(&tm->tei_m, ST_TEI_REMOVE); + } else + tm->ri = *ri; +} + +static void +tei_id_remove(struct FsmInst *fi, int event, void *arg) +{ + teimgr_t *tm = fi->userdata; + int *tei = arg; + + if (tm->debug) + tm->tei_m.printdebug(fi, "identity remove tei %d/%d", *tei, tm->l2->tei); + tm->val = *tei; + put_tei_msg(tm, ID_REMOVE, 0, tm->val); + FsmAddTimer(&tm->t201, tm->T201, EV_T201, NULL, 2); + FsmChangeState(&tm->tei_m, ST_TEI_REMOVE); +} + +static void +tei_id_verify(struct FsmInst *fi, int event, void *arg) +{ + teimgr_t *tm = fi->userdata; + + if (tm->debug) + tm->tei_m.printdebug(fi, "id verify request for tei %d", + tm->l2->tei); + tm->ri = -1; + put_tei_msg(tm, ID_CHK_REQ, 0, tm->l2->tei); + FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY); + test_and_set_bit(FLG_TEI_T201_1, &tm->l2->flag); + FsmAddTimer(&tm->t201, tm->T201, EV_T201, NULL, 2); +} + +static void +tei_id_remove_tout(struct FsmInst *fi, int event, void *arg) +{ + teimgr_t *tm = fi->userdata; + + if (tm->debug) + tm->tei_m.printdebug(fi, "remove req(2) tei %d", + tm->l2->tei); + put_tei_msg(tm, ID_REMOVE, 0, tm->val); + FsmChangeState(fi, ST_TEI_NOP); +} + +static void +tei_id_ver_tout(struct FsmInst *fi, int event, void *arg) +{ + teimgr_t *tm = fi->userdata; + + if (tm->debug) + tm->tei_m.printdebug(fi, "verify tout tei %d", + tm->l2->tei); + if (test_and_clear_bit(FLG_TEI_T201_1, &tm->l2->flag)) { + put_tei_msg(tm, ID_CHK_REQ, 0, tm->l2->tei); + tm->ri = -1; + FsmAddTimer(&tm->t201, tm->T201, EV_T201, NULL, 3); + } else { + FsmChangeState(fi, ST_TEI_NOP); + if (tm->ri == -1) { + tm->tei_m.printdebug(fi, "tei %d check no response", + tm->l2->tei); + // remove tei + } else + tm->tei_m.printdebug(fi, "tei %d check ok", + tm->l2->tei); + } +} + +int +l2_tei(teimgr_t *tm, msg_t *msg) +{ + mISDN_head_t *hh; + int ret = -EINVAL; + + if (!tm || !msg) + return(ret); + hh = (mISDN_head_t *)msg->data; + dprint(DBGM_TEI, "%s: prim(%x)\n", __FUNCTION__, hh->prim); + if (msg->len < mISDN_FRAME_MIN) + return(ret); + switch(hh->prim) { + case (MDL_REMOVE | INDICATION): + FsmEvent(&tm->tei_m, EV_REMOVE, &hh->dinfo); + break; + case (MDL_ERROR | REQUEST): + if (!test_bit(FLG_FIXED_TEI, &tm->l2->flag)) + FsmEvent(&tm->tei_m, EV_VERIFY, NULL); + break; + } + free_msg(msg); + return(0); +} + +static void +tei_debug(struct FsmInst *fi, char *fmt, ...) +{ + teimgr_t *tm = fi->userdata; + char tbuf[128]; + va_list args; + + va_start(args, fmt); + vsprintf(tbuf, fmt, args); + dprint(DBGM_L2, "tei%d %s\n", tm->l2->tei, tbuf); + va_end(args); +} + +static struct FsmNode TeiFnList[] = +{ + {ST_TEI_NOP, EV_ASSIGN_REQ, tei_assign_req}, + {ST_TEI_NOP, EV_VERIFY, tei_id_verify}, + {ST_TEI_NOP, EV_REMOVE, tei_id_remove}, + {ST_TEI_REMOVE, EV_T201, tei_id_remove_tout}, + {ST_TEI_IDVERIFY, EV_T201, tei_id_ver_tout}, + {ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove}, + {ST_TEI_IDVERIFY, EV_CHECK_RES, tei_id_chk_res}, +}; + +#define TEI_FN_COUNT (sizeof(TeiFnList)/sizeof(struct FsmNode)) + +void +release_tei(teimgr_t *tm) +{ + FsmDelTimer(&tm->t201, 1); + free(tm); +} + +int +create_teimgr(layer2_t *l2) { + teimgr_t *ntei; + + if (!l2) { + eprint("create_tei no layer2\n"); + return(-EINVAL); + } + if (!(ntei = malloc(sizeof(teimgr_t)))) { + eprint("kmalloc teimgr failed\n"); + return(-ENOMEM); + } + memset(ntei, 0, sizeof(teimgr_t)); + ntei->l2 = l2; + ntei->T201 = 1000; /* T201 1000 milliseconds */ + ntei->debug = l2->debug; + ntei->tei_m.nst = l2->nst; + ntei->tei_m.debug = l2->debug; + ntei->tei_m.userdata = ntei; + ntei->tei_m.printdebug = tei_debug; + ntei->tei_m.fsm = &teifsm; + ntei->tei_m.state = ST_TEI_NOP; + FsmInitTimer(&ntei->tei_m, &ntei->t201); + l2->tm = ntei; + return(0); +} + +int +tei_mux(net_stack_t *nst, msg_t *msg) +{ + mISDN_head_t *hh; + u_char *dp; + int mt; + layer2_t *l2; + unsigned int ri, ai; + + hh = (mISDN_head_t *)msg->data; + dprint(DBGM_TEI, "%s: prim(%x) len(%d)\n", __FUNCTION__, + hh->prim, msg->len); + if (msg->len < mISDN_FRAME_MIN) + return(-EINVAL); + if (hh->prim != (MDL_UNITDATA | INDICATION)) { + wprint("%s: prim(%x) unhandled\n", __FUNCTION__, + hh->prim); + return(-EINVAL); + } + msg_pull(msg, mISDN_HEAD_SIZE); + if (msg->len < 8) { + wprint("short tei mgr frame %d/8\n", msg->len); + return(-EINVAL); + } + dp = msg->data + 2; + if ((*dp & 0xef) != UI) { + wprint("tei mgr frame is not ui %x\n", *dp); + return(-EINVAL); + } + dp++; + if (*dp++ != TEI_ENTITY_ID) { + /* wrong management entity identifier, ignore */ + dp--; + wprint("tei handler wrong entity id %x\n", *dp); + return(-EINVAL); + } else { + mt = *(dp+2); + ri = ((unsigned int) *dp++ << 8); + ri += *dp++; + dp++; + ai = (unsigned int) *dp++; + ai >>= 1; + dprint(DBGM_TEI, "tei handler mt %x ri(%x) ai(%d)\n", + mt, ri, ai); + if (mt == ID_REQUEST) { + if (ai != 127) { + wprint("%s: ID_REQUEST ai(%d) not 127\n", __FUNCTION__, + ai); + return(-EINVAL); + } + l2 = new_tei_req(nst); + if (!l2) { + wprint("%s: no free tei\n", __FUNCTION__); + return(-EBUSY); + } + l2->tm->ri = ri; + put_tei_msg(l2->tm, ID_ASSIGNED, ri, l2->tei); + free_msg(msg); + return(0); + } + l2 = find_tei(nst, ai); + if (mt == ID_VERIFY) { + if (l2) { + FsmEvent(&l2->tm->tei_m, EV_VERIFY, &ai); + } else { + l2 = find_tei(nst, 127); + if (!l2) { + wprint("%s: no 127 manager\n", __FUNCTION__); + return(-EINVAL); + } + FsmEvent(&l2->tm->tei_m, EV_REMOVE, &ai); + } + } else if (mt == ID_CHK_RES) { + if (l2) { + FsmEvent(&l2->tm->tei_m, EV_CHECK_RES, &ri); + } else { + l2 = find_tei(nst, 127); + if (!l2) { + wprint("%s: no 127 manager\n", __FUNCTION__); + return(-EINVAL); + } + FsmEvent(&l2->tm->tei_m, EV_REMOVE, &ai); + } + } else { + wprint("%s: wrong mt %x", __FUNCTION__, mt); + return(-EINVAL); + } + } + free_msg(msg); + return(0); +} + + + +int TEIInit(void) +{ + teifsm.state_count = TEI_STATE_COUNT; + teifsm.event_count = TEI_EVENT_COUNT; + teifsm.strEvent = strTeiEvent; + teifsm.strState = strTeiState; + FsmNew(&teifsm, TeiFnList, TEI_FN_COUNT); + return(0); +} + +void TEIFree(void) +{ + FsmFree(&teifsm); +} diff --git a/i4lnet/tone.c b/i4lnet/tone.c new file mode 100644 index 0000000..2812cf7 --- /dev/null +++ b/i4lnet/tone.c @@ -0,0 +1,147 @@ +#include +#include "isdn_net.h" +#include "tone.h" +#include "bchannel.h" + +/* + * These are 10 periods (24 ms) of the 425 Hz tone used by most inband + * signals. + * Its quiet not exacly 425 Hz, but 416,66667, which fit very well + * the 15% tolerance + */ + +const unsigned char tone_425[TONE_425_SIZE] = { + 0xd5, 0x81, 0xb6, 0xbf, 0xbb, 0xba, 0xb8, 0xb2, + 0x8a, 0x9d, 0x15, 0x0e, 0x33, 0x39, 0x3a, 0x3b, + 0x3e, 0x31, 0x0d, 0x65, 0x85, 0xb4, 0xbd, 0xb8, + 0xba, 0xb8, 0xbd, 0xb4, 0x85, 0x65, 0x0d, 0x31, + 0x3e, 0x3b, 0x3a, 0x39, 0x33, 0x0e, 0x15, 0x9d, + 0x8a, 0xb2, 0xb8, 0xba, 0xbb, 0xbf, 0xb6, 0x81, + 0xd5, 0x01, 0x36, 0x3f, 0x3b, 0x3a, 0x38, 0x32, + 0x0a, 0x1d, 0x95, 0x8e, 0xb3, 0xb9, 0xba, 0xbb, + 0xbe, 0xb1, 0x8d, 0xe5, 0x05, 0x34, 0x3d, 0x38, + 0x3a, 0x38, 0x3d, 0x34, 0x05, 0xe5, 0x8d, 0xb1, + 0xbe, 0xbb, 0xba, 0xb9, 0xb3, 0x8e, 0x95, 0x1d, + 0x0a, 0x32, 0x38, 0x3a, 0x3b, 0x3f, 0x36, 0x01, + 0xd5, 0x81, 0xb6, 0xbf, 0xbb, 0xba, 0xb8, 0xb2, + 0x8a, 0x9d, 0x15, 0x0e, 0x33, 0x39, 0x3a, 0x3b, + 0x3e, 0x31, 0x0d, 0x65, 0x85, 0xb4, 0xbd, 0xb8, + 0xba, 0xb8, 0xbd, 0xb4, 0x85, 0x65, 0x0d, 0x31, + 0x3e, 0x3b, 0x3a, 0x39, 0x33, 0x0e, 0x15, 0x9d, + 0x8a, 0xb2, 0xb8, 0xba, 0xbb, 0xbf, 0xb6, 0x81, + 0xd5, 0x01, 0x36, 0x3f, 0x3b, 0x3a, 0x38, 0x32, + 0x0a, 0x1d, 0x95, 0x8e, 0xb3, 0xb9, 0xba, 0xbb, + 0xbe, 0xb1, 0x8d, 0xe5, 0x05, 0x34, 0x3d, 0x38, + 0x3a, 0x38, 0x3d, 0x34, 0x05, 0xe5, 0x8d, 0xb1, + 0xbe, 0xbb, 0xba, 0xb9, 0xb3, 0x8e, 0x95, 0x1d, + 0x0a, 0x32, 0x38, 0x3a, 0x3b, 0x3f, 0x36, 0x01 +}; + +/* + * These are 10 ms of silence + */ + +const unsigned char tone_SILENCE[TONE_SILENCE_SIZE] = { + 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, + 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, + 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, + 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, + 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, + 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, + 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, + 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, + 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, + 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5 +}; + +int tone_handler(bchannel_t *bc) { + const unsigned char *tp; + int len; + + dprint(DBGM_TONE, "%s:ch%d Flags %x\n", __FUNCTION__, + bc->channel, bc->Flags); + if (bc->bstate != BC_BSTATE_ACTIV) + return(1); + if (bc->smsg) + return(2); + if (!(bc->Flags & FLG_BC_TONE)) + return(3); + if (bc->Flags & FLG_BC_TONE_DIAL) { + tp = tone_425; + len = TONE_425_SIZE; + } else if (bc->Flags & FLG_BC_TONE_ALERT) { + if (bc->Flags & FLG_BC_TONE_SILENCE) { + if (bc->ttime > TONE_ALERT_SILENCE_TIME) { + bc->ttime = 0; + tp = tone_425; + len = TONE_425_SIZE; + bc->Flags &= ~FLG_BC_TONE_SILENCE; + } else { + tp = tone_SILENCE; + len = TONE_SILENCE_SIZE; + } + } else { + if (bc->ttime > TONE_ALERT_TIME) { + bc->ttime = 0; + tp = tone_SILENCE; + len = TONE_SILENCE_SIZE; + bc->Flags |= FLG_BC_TONE_SILENCE; + } else { + tp = tone_425; + len = TONE_425_SIZE; + } + } + } else if (bc->Flags & FLG_BC_TONE_BUSY) { + if (bc->Flags & FLG_BC_TONE_SILENCE) { + if (bc->ttime > TONE_BUSY_SILENCE_TIME) { + bc->ttime = 0; + tp = tone_425; + len = TONE_425_SIZE; + bc->Flags &= ~FLG_BC_TONE_SILENCE; + } else { + tp = tone_SILENCE; + len = TONE_SILENCE_SIZE; + } + } else { + if (bc->ttime > TONE_BUSY_TIME) { + bc->ttime = 0; + tp = tone_SILENCE; + len = TONE_SILENCE_SIZE; + bc->Flags |= FLG_BC_TONE_SILENCE; + } else { + tp = tone_425; + len = TONE_425_SIZE; + } + } + } else if (bc->Flags & FLG_BC_TONE_SILENCE) { + tp = tone_SILENCE; + len = TONE_SILENCE_SIZE; + } else + return(4); + if (len > ibuf_freecount(bc->sbuf)) { + dprint(DBGM_TONE, "%s:ch%d not sbuf %d/%d\n", __FUNCTION__, + bc->channel, len, ibuf_freecount(bc->sbuf)); + return(5); + } + if (bc->sbuf) { + bc->ttime += len*125; + ibuf_memcpy_w(bc->sbuf, (unsigned char *)tp, len); + sem_post(bc->sbuf->rsem); + } + return(0); +} + +int +set_tone(bchannel_t *bc, int tone) +{ + bc->Flags &= ~FLG_BC_TONE; + bc->Flags |= tone; + bc->ttime = 0; + if (tone) { + if (bc->sbuf) { + bc->sbuf->rsem = &bc->work; + bc->sbuf->wsem = &bc->work; + } + } + return(bc->Flags & FLG_BC_TONE); +} diff --git a/include/bchannel.h b/include/bchannel.h new file mode 100644 index 0000000..be2d7da --- /dev/null +++ b/include/bchannel.h @@ -0,0 +1,37 @@ +#ifndef BCHANNEL_H +#define BCHANNEL_H + +enum { + BC_CSTATE_NULL, + BC_CSTATE_ICALL, + BC_CSTATE_OCALL, + BC_CSTATE_OVERLAP_REC, + BC_CSTATE_PROCEED, + BC_CSTATE_ALERTING, + BC_CSTATE_ACTIV, + BC_CSTATE_DISCONNECT, + BC_CSTATE_DISCONNECTED, + BC_CSTATE_RELEASE, +}; + +enum { + BC_BSTATE_NULL, + BC_BSTATE_SETUP, + BC_BSTATE_ACTIVATE, + BC_BSTATE_ACTIV, + BC_BSTATE_DEACTIVATE, + BC_BSTATE_CLEANUP, +}; + +#define BC_SETUP 0x0e0100 +#define BC_CLEANUP 0x0e0200 + +#define ISDN_PID_L2_B_USER 0x420000ff +#define ISDN_PID_L3_B_USER 0x430000ff + + +extern int init_bchannel(bchannel_t *bc, int channel); +extern int term_bchannel(bchannel_t *bc); + + +#endif diff --git a/include/g711.h b/include/g711.h new file mode 100644 index 0000000..f7f1a30 --- /dev/null +++ b/include/g711.h @@ -0,0 +1,68 @@ +/* + * This source code is quick table lookup implementation of + * convert 16 bit linear PCM and A-law u-law (ITU G.711) codings + * Tables are generated using ITU G.711 example code from + * Sun Microsystems, Inc. + * + * (C)2001 Karsten Keil kkeil@suse.de + * + * + * + */ + +#ifndef G711_H +#define G711_H + +extern unsigned char _l2u[4096]; +extern unsigned char _l2A[2048]; +extern unsigned char _u2A[256]; +extern unsigned char _A2u[256]; +extern signed short _u2l[256]; +extern signed short _A2l[256]; + +static __inline__ unsigned char linear2ulaw(signed short l) +{ + unsigned char mask; + + mask = (l<0) ? 0x7f : 0xff; + if (l<0) + l = -l; + if (l<4) + return(0xff & mask); + l -= 4; + l >>= 3; + return(_l2u[l] & mask); +} + +static __inline__ unsigned char linear2alaw(signed short l) +{ + unsigned char mask; + + mask = (l<0) ? 0x7f : 0xff; + if (l<0) + l = -l; + l >>= 4; + return(_l2A[l] & mask); +} + +static __inline__ signed short ulaw2linear(unsigned char u) +{ + return(_u2l[u]); +} + +static __inline__ signed short alaw2linear(unsigned char a) +{ + return(_A2l[a]); +} + +static __inline__ unsigned char ulaw2alaw(unsigned char u) +{ + return(_u2A[u]); +} + +static __inline__ unsigned char alaw2ulaw(unsigned char a) +{ + return(_A2u[a]); +} + +#endif diff --git a/include/helper.h b/include/helper.h new file mode 100644 index 0000000..e15ecf8 --- /dev/null +++ b/include/helper.h @@ -0,0 +1,58 @@ +/* $Id: helper.h,v 0.9 2003/08/27 07:33:02 kkeil Exp $ + * + * Basic declarations, defines and prototypes + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ +#ifndef _mISDN_HELPER_H +#define _mISDN_HELPER_H +#ifdef MEMDBG +#include "memdbg.h" +#endif + +#define int_error() \ + fprintf(stderr, "mISDN: INTERNAL ERROR in %s:%d\n", \ + __FILE__, __LINE__) + +#define int_errtxt(fmt, arg...) \ + fprintf(stderr, "mISDN: INTERNAL ERROR in %s:%d " fmt "\n", \ + __FILE__, __LINE__, ## arg) + +#define APPEND_TO_LIST(item,base) \ + if (item->prev || item->next) \ + int_errtxt("APPEND not clean %p<-%p->%p", \ + item->prev, item, item->next); \ + item->next = NULL; \ + item->prev = base; \ + while (item->prev && item->prev->next) \ + item->prev = item->prev->next; \ + if (item->prev == item) { \ + int_errtxt("APPEND DUP %p", item); \ + } else \ + if (base) { \ + item->prev->next = item; \ + } else \ + base = item + +#define INSERT_INTO_LIST(newi,nexti,base) \ + newi->next = nexti; \ + newi->prev = nexti->prev; \ + if (newi->prev) \ + newi->prev->next = newi; \ + nexti->prev = newi; \ + if (base == nexti) \ + base = newi + +#define REMOVE_FROM_LIST(item) \ + if (item->prev) \ + item->prev->next = item->next; \ + if (item->next) \ + item->next->prev = item->prev + +#define REMOVE_FROM_LISTBASE(item,base) \ + REMOVE_FROM_LIST(item); \ + if (item == base) \ + base = item->next + +#endif diff --git a/include/ibuffer.h b/include/ibuffer.h new file mode 100644 index 0000000..6c963f8 --- /dev/null +++ b/include/ibuffer.h @@ -0,0 +1,119 @@ +#ifndef IBUFFER_H +#define IBUFFER_H +#include +#include +#include +#include + +/* ibuffer stuff */ + +typedef struct _ibuffer ibuffer_t; + +struct _ibuffer { + int size; + unsigned char *buffer; + int ridx; + int widx; + sem_t *rsem; + sem_t *wsem; +}; + +static inline void +clear_ibuffer(ibuffer_t *ib) +{ + if (!ib) + return; + ib->ridx = 0; + ib->widx = 0; +} + +static inline ibuffer_t * +init_ibuffer(int size) +{ + ibuffer_t *ib; + + ib = malloc(sizeof(ibuffer_t)); + if (!ib) + return(NULL); + memset(ib, 0, sizeof(ibuffer_t)); + ib->buffer = malloc(size); + if (!ib->buffer) { + free(ib); + return(NULL); + } + ib->size = size; + return(ib); +} + +static inline void +free_ibuffer(ibuffer_t *ib) +{ + if (!ib) + return; + if (ib->buffer) + free(ib->buffer); + free(ib); +} + +static inline int +ibuf_usedcount(ibuffer_t *ib) +{ + int l; + + if (!ib) + return(0); + l = ib->widx - ib->ridx; + if (l<0) + l += ib->size; + return(l); +} + + +static inline int +ibuf_freecount(ibuffer_t *ib) +{ + if (!ib) + return(0); + return(ib->size - ibuf_usedcount(ib)); +} + +static inline void +ibuf_memcpy_w(ibuffer_t *ib, void *data, int len) +{ + unsigned char *p = data; + int frag; + + frag = ib->size - ib->widx; + if (frag < len) { + memcpy(&ib->buffer[ib->widx], p, frag); + p += frag; + frag = len - frag; + ib->widx = 0; + } else + frag = len; + memcpy(&ib->buffer[ib->widx], p, frag); + ib->widx += frag; + ib->widx %= ib->size; +} + +static inline void +ibuf_memcpy_r(void *data, ibuffer_t *ib, int len) +{ + unsigned char *p = data; + int frag; + + frag = ib->size - ib->ridx; + if (frag < len) { + memcpy(p, &ib->buffer[ib->ridx], frag); + p += frag; + frag = len - frag; + ib->ridx = 0; + } else + frag = len; + memcpy(p, &ib->buffer[ib->ridx], frag); + ib->ridx += frag; + ib->ridx %= ib->size; +} + +#endif + diff --git a/include/isdn_debug.h b/include/isdn_debug.h new file mode 100644 index 0000000..dd689e0 --- /dev/null +++ b/include/isdn_debug.h @@ -0,0 +1,36 @@ +#ifndef ISDN_DEBUG_H +#define ISDN_DEBUG_H + +#include +#include +#include + +#define DBGM_NET 0x00000001 +#define DBGM_MSG 0x00000002 +#define DBGM_FSM 0x00000004 +#define DBGM_TEI 0x00000010 +#define DBGM_L2 0x00000020 +#define DBGM_L3 0x00000040 +#define DBGM_L3DATA 0x00000080 +#define DBGM_BC 0x00000100 +#define DBGM_TONE 0x00000200 +#define DBGM_BCDATA 0x00000400 +#define DBGM_MAN 0x00001000 +#define DBGM_APPL 0x00002000 +#define DBGM_ISDN 0x00004000 +#define DBGM_SOCK 0x00010000 +#define DBGM_CONN 0x00020000 +#define DBGM_CDATA 0x00040000 +#define DBGM_DDATA 0x00080000 +#define DBGM_SOUND 0x00100000 +#define DBGM_SDATA 0x00200000 +#define DBGM_TOPLEVEL 0x40000000 +#define DBGM_ALL 0xffffffff + +extern int dprint(unsigned int mask, const char *fmt, ...); +extern int eprint(const char *fmt, ...); +extern int wprint(const char *fmt, ...); +extern int debug_init(unsigned int, char *, char *, char *); +extern void debug_close(void); +extern int dhexprint(unsigned int, char *, unsigned char *, int); +#endif diff --git a/include/isdn_msg.h b/include/isdn_msg.h new file mode 100644 index 0000000..ff6abe0 --- /dev/null +++ b/include/isdn_msg.h @@ -0,0 +1,187 @@ +#ifndef ISDN_MSG_H +#define ISDN_MSG_H + +#include +#include + +#define MAX_MSG_SIZE 2080 +#define DEFAULT_HEADROOM 16 + +typedef struct _msg { + struct _msg *prev; + struct _msg *next; + struct _msg_queue *list; + int len; + int size; + unsigned char *head; + unsigned char *data; + unsigned char *tail; + unsigned char *end; + unsigned char __data[MAX_MSG_SIZE]; +} msg_t; + +typedef struct _msg_queue { + struct _msg *prev; + struct _msg *next; + pthread_mutex_t lock; + int len; + int maxlen; +} msg_queue_t; + +extern void msg_init(void); +extern msg_t *alloc_msg(int); +extern void free_msg(msg_t *); +extern msg_queue_t *free_queue; +extern msg_t *msg_copy(msg_t *msg); + +#define msg_clone(m) msg_copy(m) + +static inline void +msg_queue_init(msg_queue_t *q) +{ + pthread_mutex_init(&q->lock, NULL); + q->len = 0; + q->prev = (msg_t *)q; + q->next = (msg_t *)q; +} + +static inline int msg_queue_len(msg_queue_t *list_) +{ + return(list_->len); +} + +static inline void msg_queue_head(msg_queue_t *list, msg_t *newm) +{ + msg_t *prev, *next; + + pthread_mutex_lock(&list->lock); + newm->list = list; + list->len++; + prev = (msg_t *)list; + next = prev->next; + newm->next = next; + newm->prev = prev; + next->prev = newm; + prev->next = newm; + pthread_mutex_unlock(&list->lock); +} + + +static inline void msg_queue_tail(msg_queue_t *list, msg_t *newm) +{ + msg_t *prev, *next; + + pthread_mutex_lock(&list->lock); + newm->list = list; + list->len++; + next = (msg_t *)list; + prev = next->prev; + newm->next = next; + newm->prev = prev; + next->prev = newm; + prev->next = newm; + pthread_mutex_unlock(&list->lock); +} + + +static inline msg_t *msg_dequeue(msg_queue_t *list) +{ + msg_t *next, *prev, *result; + + pthread_mutex_lock(&list->lock); + prev = (msg_t *) list; + next = prev->next; + result = NULL; + if (next != prev) { + result = next; + next = next->next; + list->len--; + next->prev = prev; + prev->next = next; + result->next = NULL; + result->prev = NULL; + result->list = NULL; + } + pthread_mutex_unlock(&list->lock); + return result; +} + +static __inline__ void msg_queue_purge(msg_queue_t *list) +{ + msg_t *msg; + + while ((msg = msg_dequeue(list))!=NULL) + free_msg(msg); +} + +static __inline__ unsigned char *msg_put(msg_t *msg, unsigned int len) +{ + unsigned char *tmp=msg->tail; + msg->tail+=len; + msg->len+=len; + if(msg->tail>msg->end) + { + fprintf(stderr, "msg_over_panic msg(%p) data(%p) head(%p)\n", + msg, msg->data, msg->head); + return(NULL); + } + return tmp; +} + +static __inline__ unsigned char *msg_push(msg_t *msg, unsigned int len) +{ + msg->data-=len; + msg->len+=len; + if(msg->data < msg->head) + { + fprintf(stderr, "msg_under_panic msg(%p) data(%p) head(%p)\n", + msg, msg->data, msg->head); + return(NULL); + } + return msg->data; +} + + +static __inline__ char *__msg_pull(msg_t *msg, unsigned int len) +{ + msg->len-=len; + return msg->data+=len; +} + +static __inline__ unsigned char * msg_pull(msg_t *msg, unsigned int len) +{ + if (len > msg->len) + return NULL; + return __msg_pull(msg,len); +} + +static __inline__ int msg_headroom(msg_t *msg) +{ + return msg->data-msg->head; +} + +static __inline__ int msg_tailroom(msg_t *msg) +{ + return msg->end-msg->tail; +} + +static __inline__ void msg_reserve(msg_t *msg, unsigned int len) +{ + msg->data+=len; + msg->tail+=len; +} + +static __inline__ void __msg_trim(msg_t *msg, unsigned int len) +{ + msg->len = len; + msg->tail = msg->data+len; +} + +static __inline__ void msg_trim(msg_t *msg, unsigned int len) +{ + if (msg->len > len) { + __msg_trim(msg, len); + } +} + +#endif diff --git a/include/isdn_net.h b/include/isdn_net.h new file mode 100644 index 0000000..7877386 --- /dev/null +++ b/include/isdn_net.h @@ -0,0 +1,296 @@ +#ifndef ISDN_NET_H +#define ISDN_NET_H + +#include +#include +#include +#include +#include "mISDNlib.h" +#include "isdn_msg.h" +#include "isdn_debug.h" +#include "ibuffer.h" + +#define MSN_LEN 32 +#define SUBADR_LEN 24 +#define UUS_LEN 256 +#define FAC_LEN 132 +#ifndef mISDN_FRAME_MIN +#define mISDN_FRAME_MIN 8 +#endif + +typedef struct _layer2 layer2_t; +typedef struct _layer3 layer3_t; +typedef struct _layer4 layer4_t; +typedef struct _bchannel bchannel_t; +typedef struct _mISDNif mISDNif_t; +typedef struct _mISDNinstance mISDNinstance_t; +typedef struct _net_stack net_stack_t; +typedef struct _manager manager_t; +typedef struct _nr_list nr_list_t; +typedef int (*ifunc_t)(net_stack_t *, msg_t *); +typedef int (*bfunc_t)(void *, void *); +typedef int (*afunc_t)(manager_t *, int, void *); + + +#define MAX_BDATA_SIZE 2048 + +struct _bchannel { + sem_t work; + msg_queue_t workq; + pthread_t tid; + manager_t *manager; + void *app; + int channel; + pthread_mutex_t lock; + int cstate; + int bstate; + int l3id; + int b_addr; + int Flags; + int ttime; + nr_list_t *usednr; + int l1_prot; + unsigned char bc[8]; + unsigned char uu[UUS_LEN]; + unsigned char fac[FAC_LEN]; + unsigned char nr[MSN_LEN]; + unsigned char msn[MSN_LEN]; + unsigned char clisub[SUBADR_LEN]; + unsigned char cldsub[SUBADR_LEN]; + int cause_loc; + int cause_val; + unsigned char display[84]; + msg_t *smsg; + ibuffer_t *rbuf; + ibuffer_t *sbuf; + int rrid; + int rsid; +}; + +struct _manager { + manager_t *prev; + manager_t *next; + bchannel_t bc[2]; + nr_list_t *nrlist; + net_stack_t *nst; + bfunc_t man2stack; + afunc_t application; + afunc_t app_bc; + pthread_t tid; + sem_t work; + msg_queue_t workq; +}; + +#define PR_APP_CHECK_NR 1 +#define PR_APP_ICALL 2 +#define PR_APP_OCHANNEL 3 +#define PR_APP_OCALL 4 +#define PR_APP_ALERT 5 +#define PR_APP_CONNECT 6 +#define PR_APP_HANGUP 7 +#define PR_APP_CLEAR 8 +#define PR_APP_USERUSER 9 +#define PR_APP_FACILITY 10 +#define PR_APP_OPEN_RECFILES 11 +#define PR_APP_CLOSE_RECFILES 12 + +#define FLG_NST_READER_ABORT 1 +#define FLG_NST_TERMINATION 2 + +struct _net_stack { + int device; + int cardnr; + int d_stid; + int l0_id; + int l1_id; + int l2_id; + msg_t *phd_down_msg; + layer2_t *layer2; + layer3_t *layer3; + ifunc_t l1_l2; + ifunc_t l2_l3; + ifunc_t l3_l2; + ifunc_t manager_l3; + bfunc_t l3_manager; + manager_t *manager; + msg_queue_t down_queue; + msg_queue_t rqueue; + msg_queue_t wqueue; + sem_t work; + pthread_mutex_t lock; + pthread_t reader; + int b_stid[2]; + int b_addr[2]; + int bcid[2]; + int flag; + struct _itimer *tlist; +}; + +struct _nr_list { + nr_list_t *prev; + nr_list_t *next; + unsigned char len; + unsigned char nr[MSN_LEN]; + unsigned char name[64]; + int typ; + int flags; +}; + +#define NR_TYPE_INTERN 1 +#define NR_TYPE_AUDIO 2 +#define NR_TYPE_VOIP 3 + +typedef struct _itimer { + struct _itimer *prev; + struct _itimer *next; + net_stack_t *nst; + int id; + int expires; + int Flags; + unsigned long data; + int (*function)(unsigned long); +} itimer_t; + +#define FLG_TIMER_RUNING 1 + +#define FLG_BC_USE 0x00000001 +#define FLG_BC_SENT_CID 0x00000002 +#define FLG_BC_CALL_ORGINATE 0x00000004 +#define FLG_BC_PROGRESS 0x00000008 +#define FLG_BC_APPLICATION 0x00000010 +#define FLG_BC_TONE_DIAL 0x00000100 +#define FLG_BC_TONE_BUSY 0x00000200 +#define FLG_BC_TONE_ALERT 0x00000400 +#define FLG_BC_TONE_SILENCE 0x00000800 +#define FLG_BC_TONE_NONE 0x00000000 +#define FLG_BC_TONE 0x00000F00 +#define FLG_BC_RECORD 0x00010000 +#define FLG_BC_RECORDING 0x00020000 +#define FLG_BC_RAWDEVICE 0x01000000 +#define FLG_BC_KEEP_SEND 0x02000000 +#define FLG_BC_TERMINATE 0x08000000 + +#define MSG_L1_PRIM 0x010000 +#define MSG_L2_PRIM 0x020000 +#define MSG_L3_PRIM 0x030000 + +extern int do_net_stack_setup(net_stack_t *); +extern int do_net_stack_cleanup(net_stack_t *nst); +extern void *do_netthread(void *); +extern int term_netstack(net_stack_t *nst); + +extern int init_manager(manager_t **mlist, afunc_t application); +extern int cleanup_manager(manager_t *mgr); + +extern int write_dmsg(net_stack_t *, msg_t *); +extern int phd_conf(net_stack_t *, iframe_t *, msg_t *); + +extern int init_timer(itimer_t *, net_stack_t *); +extern int add_timer(itimer_t *); +extern int del_timer(itimer_t *); +extern int remove_timer(itimer_t *it); +extern int timer_pending(itimer_t *); + +extern u_char *findie(u_char *, int, u_char, int); +extern u_char *find_and_copy_ie(u_char *, int, u_char, int, msg_t *); +extern void display_NR_IE(u_char *, char *); + +extern int match_nr(manager_t *mgr, unsigned char *nx, nr_list_t **nrx); + +typedef struct _mISDN_head { + u_int prim; + int dinfo; +} mISDN_head_t; + +#define mISDN_HEAD_SIZE sizeof(mISDN_head_t) + +/* interface msg help routines */ + +static inline void mISDN_newhead(u_int prim, int dinfo, msg_t *msg) +{ + mISDN_head_t *hh = (mISDN_head_t *)msg->data; + + hh->prim = prim; + hh->dinfo = dinfo; +} + +static inline int if_newhead(void *arg, ifunc_t func, u_int prim, int dinfo, + msg_t *msg) +{ + if (!msg) + return(-ENXIO); + mISDN_newhead(prim, dinfo, msg); + return(func(arg, msg)); +} + +static inline void mISDN_addhead(u_int prim, int dinfo, msg_t *msg) +{ + mISDN_head_t *hh = (mISDN_head_t *)msg_push(msg, mISDN_HEAD_SIZE); + + hh->prim = prim; + hh->dinfo = dinfo; +} + + +static inline int if_addhead(void *arg, ifunc_t func, u_int prim, int dinfo, + msg_t *msg) +{ + if (!msg) + return(-ENXIO); + mISDN_addhead(prim, dinfo, msg); + return(func(arg, msg)); +} + + +static inline msg_t *create_link_msg(u_int prim, int dinfo, + int len, void *arg, int reserve) +{ + msg_t *msg; + + if (!(msg = alloc_msg(len + mISDN_HEAD_SIZE + reserve))) { + wprint("%s: no msg size %d+%d+%d\n", __FUNCTION__, + len, mISDN_HEAD_SIZE, reserve); + return(NULL); + } else + msg_reserve(msg, reserve + mISDN_HEAD_SIZE); + if (len) + memcpy(msg_put(msg, len), arg, len); + mISDN_addhead(prim, dinfo, msg); + return(msg); +} + +static inline int if_link(void *farg, ifunc_t func, u_int prim, int dinfo, int len, + void *arg, int reserve) +{ + msg_t *msg; + int err; + + if (!(msg = create_link_msg(prim, dinfo, len, arg, reserve))) + return(-ENOMEM); + err = func(farg, msg); + if (err) + free_msg(msg); + return(err); +} + +static inline msg_t *prep_l3data_msg(u_int prim, int dinfo, int ssize, int dsize, msg_t *old) +{ + if (!old) { + old = alloc_msg(ssize + dsize + mISDN_HEAD_SIZE + DEFAULT_HEADROOM); + if (!old) { + wprint("%s: no msg size %d+%d+%d\n", __FUNCTION__, + ssize, dsize, mISDN_HEAD_SIZE + DEFAULT_HEADROOM); + return(NULL); + } + } else { + old->data = old->head + DEFAULT_HEADROOM; + old->tail = old->data; + old->len = 0; + } + memset(msg_put(old, ssize + mISDN_HEAD_SIZE), 0, + ssize + mISDN_HEAD_SIZE); + mISDN_newhead(prim, dinfo, old); + return(old); +} + +#endif diff --git a/include/isound.h b/include/isound.h new file mode 100644 index 0000000..2af9068 --- /dev/null +++ b/include/isound.h @@ -0,0 +1,24 @@ +#ifndef ISOUND_H +#define ISOUND_H + +#include "ibuffer.h" + +typedef struct _isound isound_t; + +#define MAX_AUDIO_READ 2048 + +struct _isound { + ibuffer_t *sbuf; + ibuffer_t *rbuf; + int Flag; + int data; + unsigned char rtmp[MAX_AUDIO_READ]; + unsigned char wtmp[MAX_AUDIO_READ]; + int wlen; + int widx; + sem_t work; + pthread_t rd_t; + pthread_t wr_t; +}; + +#endif diff --git a/include/l3dss1.h b/include/l3dss1.h new file mode 100644 index 0000000..58979b4 --- /dev/null +++ b/include/l3dss1.h @@ -0,0 +1,173 @@ +/* $Id: l3dss1.h,v 0.9 2003/08/27 07:33:02 kkeil Exp $ + * + * DSS1 (Euro) D-channel protocol defines + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#ifndef l3dss1_process + +#define T301 180000 +#define T302 15000 +#define T303 4000 +#define T304 30000 +#define T305 30000 +#define T308 4000 +/* for layer 1 certification T309 < layer1 T3 (e.g. 4000) */ +/* This makes some tests easier and quicker */ +#define T309 40000 +#define T310 30000 +#define T312 6000 +#define T313 4000 +#define T318 4000 +#define T319 4000 +#define N303 1 +#define T_CTRL 180000 + +/* private TIMER events */ +#define CC_TIMER 0x000001 +#define CC_T301 0x030101 +#define CC_T302 0x030201 +#define CC_T303 0x030301 +#define CC_T304 0x030401 +#define CC_T305 0x030501 +#define CC_T308 0x030801 +#define CC_T309 0x030901 +#define CC_T310 0x031001 +#define CC_T312 0x031201 +#define CC_T313 0x031301 +#define CC_T318 0x031801 +#define CC_T319 0x031901 +#define CC_TCTRL 0x031f01 +/* + * Message-Types + */ + +#define MT_ALERTING 0x01 +#define MT_CALL_PROCEEDING 0x02 +#define MT_CONNECT 0x07 +#define MT_CONNECT_ACKNOWLEDGE 0x0f +#define MT_PROGRESS 0x03 +#define MT_SETUP 0x05 +#define MT_SETUP_ACKNOWLEDGE 0x0d +#define MT_RESUME 0x26 +#define MT_RESUME_ACKNOWLEDGE 0x2e +#define MT_RESUME_REJECT 0x22 +#define MT_SUSPEND 0x25 +#define MT_SUSPEND_ACKNOWLEDGE 0x2d +#define MT_SUSPEND_REJECT 0x21 +#define MT_USER_INFORMATION 0x20 +#define MT_DISCONNECT 0x45 +#define MT_RELEASE 0x4d +#define MT_RELEASE_COMPLETE 0x5a +#define MT_RESTART 0x46 +#define MT_RESTART_ACKNOWLEDGE 0x4e +#define MT_SEGMENT 0x60 +#define MT_CONGESTION_CONTROL 0x79 +#define MT_INFORMATION 0x7b +#define MT_FACILITY 0x62 +#define MT_NOTIFY 0x6e +#define MT_STATUS 0x7d +#define MT_STATUS_ENQUIRY 0x75 + +#define IE_SEGMENT 0x00 +#define IE_BEARER 0x04 +#define IE_CAUSE 0x08 +#define IE_CALL_ID 0x10 +#define IE_CALL_STATE 0x14 +#define IE_CHANNEL_ID 0x18 +#define IE_FACILITY 0x1c +#define IE_PROGRESS 0x1e +#define IE_NET_FAC 0x20 +#define IE_NOTIFY 0x27 +#define IE_DISPLAY 0x28 +#define IE_DATE 0x29 +#define IE_KEYPAD 0x2c +#define IE_SIGNAL 0x34 +#define IE_INFORATE 0x40 +#define IE_E2E_TDELAY 0x42 +#define IE_TDELAY_SEL 0x43 +#define IE_PACK_BINPARA 0x44 +#define IE_PACK_WINSIZE 0x45 +#define IE_PACK_SIZE 0x46 +#define IE_CUG 0x47 +#define IE_REV_CHARGE 0x4a +#define IE_CONNECT_PN 0x4c +#define IE_CONNECT_SUB 0x4d +#define IE_CALLING_PN 0x6c +#define IE_CALLING_SUB 0x6d +#define IE_CALLED_PN 0x70 +#define IE_CALLED_SUB 0x71 +#define IE_REDIR_NR 0x74 +#define IE_TRANS_SEL 0x78 +#define IE_RESTART_IND 0x79 +#define IE_LLC 0x7c +#define IE_HLC 0x7d +#define IE_USER_USER 0x7e +#define IE_ESCAPE 0x7f +#define IE_SHIFT 0x90 +#define IE_MORE_DATA 0xa0 +#define IE_COMPLETE 0xa1 +#define IE_CONGESTION 0xb0 +#define IE_REPEAT 0xd0 + +#define IE_MANDATORY 0x0100 +/* mandatory not in every case */ +#define IE_MANDATORY_1 0x0200 + +#define ERR_IE_COMPREHENSION 1 +#define ERR_IE_UNRECOGNIZED -1 +#define ERR_IE_LENGTH -2 +#define ERR_IE_SEQUENCE -3 + +#define CAUSE_LOC_USER 0 +#define CAUSE_LOC_PNET_LOCUSER 1 + +#define CAUSE_UNASSIGNED_NUMBER 1 +#define CAUSE_NO_TRANSIT_ROUTE 2 +#define CAUSE_NO_ROUTE 3 +#define CAUSE_CHANNEL_UNACCEPT 6 +#define CAUSE_NORMAL_CLEARING 16 +#define CAUSE_USER_BUSY 17 +#define CAUSE_NOUSER_RESPONDING 18 +#define CAUSE_CALL_REJECTED 21 +#define CAUSE_NONSELECTED_USER 26 +#define CAUSE_INVALID_NUMBER 28 +#define CAUSE_STATUS_RESPONSE 30 +#define CAUSE_NORMALUNSPECIFIED 31 +#define CAUSE_NO_CHANNEL 34 +#define CAUSE_TEMPORARY_FAILURE 41 +#define CAUSE_INVALID_CALLREF 81 +#define CAUSE_INCOMPATIBLE_DEST 88 +#define CAUSE_MANDATORY_IE_MISS 96 +#define CAUSE_MT_NOTIMPLEMENTED 97 +#define CAUSE_IE_NOTIMPLEMENTED 99 +#define CAUSE_INVALID_CONTENTS 100 +#define CAUSE_NOTCOMPAT_STATE 101 +#define CAUSE_TIMER_EXPIRED 102 +#define CAUSE_PROTOCOL_ERROR 111 + +#define NO_CAUSE 254 + +#define PROGRESS_TONE 8 + +#else /* only l3dss1_process */ + +/* l3dss1 specific data in l3 process */ +typedef struct + { unsigned char invoke_id; /* used invoke id in remote ops, 0 = not active */ + ulong ll_id; /* remebered ll id */ + u_char remote_operation; /* handled remote operation, 0 = not active */ + int proc; /* rememered procedure */ + ulong remote_result; /* result of remote operation for statcallb */ + char uus1_data[35]; /* data send during alerting or disconnect */ + } dss1_proc_priv; + +/* l3dss1 specific data in protocol stack */ +typedef struct + { unsigned char last_invoke_id; /* last used value for invoking */ + unsigned char invoke_used[32]; /* 256 bits for 256 values */ + } dss1_stk_priv; + +#endif /* only l3dss1_process */ diff --git a/include/mISDNlib.h b/include/mISDNlib.h new file mode 100644 index 0000000..ab0d014 --- /dev/null +++ b/include/mISDNlib.h @@ -0,0 +1,402 @@ +#ifndef _mISDN_LIB_H +#define _mISDN_LIB_H + +/* API library to use with /dev/mISDN */ + +/* we need somme extentions */ +#define _GNU_SOURCE + +typedef unsigned short u16; + +#include +#include +#include + +#define mISDN_INBUFFER_SIZE 0x20000 +#define mISDN_HEADER_LEN 16 + +#define TIMEOUT_1SEC 1000000 +#define TIMEOUT_10SEC 10000000 +#define TIMEOUT_INFINIT -1 + +// extern void xxxxxxxxxx(void); + +/* Prototypes from device.c */ + +/* mISDN_open(void) + * + * opens a mISDN device and allocate buffers for it + * + * parameter: + * none + * + * return: + * the file descriptor or -1 on error and the error cause in errno + */ +extern int mISDN_open(void); + +/* mISDN_close(int fid) + * + * close the mISDN device and frees related memory. + * + * parameter: + * fid - file descriptor returned by mISDN_open + * + * return: + * 0 on success or -1 on error and the error cause in errno + * + */ +extern int mISDN_close(int fid); + +/* mISDN_read(int fid, void *buf, size_t count, int utimeout) + * + * read one message from device or buffer + * + * parameter: + * fid - FILE descriptor returned by mISDN_open + * buf - pointer to readbuffer + * count - maximum length of read data + * utimeout - maximum time in microseconds to wait for data, if -1 + * wait until some data is ready + * + * return: + * length of read data or -1 on error and the error cause in errno + * + */ +extern int mISDN_read(int fid, void *buf, size_t count, int utimeout); + +/* mISDN_read_frame(int fid, void *buf, size_t count, u_int addr, + * u_int msgtype, int utimeout) + * + * read one message for address (addr) and message type (msgtype) + * from device or buffer + * + * parameter: + * fid - FILE descriptor returned by mISDN_open + * buf - pointer to readbuffer + * count - maximum length of read data + * addr - address of frame + * msgtype - message type of frame + * utimeout - maximum time in microseconds to wait for data, if -1 + * wait until some data is ready + * + * return: + * length of read data or -1 on error and the error cause in errno + * + */ +extern int mISDN_read_frame(int fid, void *buf, size_t count, u_int addr, + u_int msgtype, int utimeout); + +/* mISDN_write(int fid, void *buf, size_t count, int utimeout) + * + * write a message to device + * + * parameter: + * fid - FILE descriptor returned by mISDN_open + * buf - pointer to data to write + * count - length of data + * utimeout - maximum time in microseconds to wait for device ready to + * accept new data, if -1 wait until device is ready + * + * return: + * length of written data or -1 on error and the error cause in errno + * + */ +extern int mISDN_write(int fid, void *buf, size_t count, int utimeout); + +/* mISDN_write_frame(int fid, void *fbuf, u_int addr, u_int msgtype, + * int dinfo, int dlen, void *dbuf, int utimeout) + * + * write a frame to device + * + * parameter: + * fid - FILE descriptor returned by mISDN_open + * fbuf - buffer for frame, caller has to provide a big enougth buffer + * count - length of data + * addr - address for frame + * msgtype - message type of frame + * dinfo - data info parameter + * dlen - len of dbuf data, if negativ it is an error code (dbuf len=0) + * dbuf - pointer to frame payload data + * utimeout - maximum time in microseconds to wait for device ready to + * accept new data, if -1 wait until device is ready + * + * return: + * 0 if successfull or -1 on error and the error cause in errno + * + */ +extern int mISDN_write_frame(int fid, void *fbuf, u_int addr, u_int msgtype, + int dinfo, int dlen, void *dbuf, int utimeout); + +/* int mISDN_select(int n, fd_set *readfds, fd_set *writefds, + * fd_set *exceptfds, struct timeval *timeout) + * + * select call which handles mISDN readbuffer + * + * parameters and use see man select + * + */ + +extern int mISDN_select(int n, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout); + + +/* Prototypes from stack.c */ + +/* mISDN_get_stack_count(int fid) + * + * get number of ISDN stacks + * + * parameter: + * fid - FILE descriptor returned by mISDN_open + * + * return: + * count of ISDN stacks, negativ values for error + * + */ +extern int mISDN_get_stack_count(int fid); + +/* mISDN_get_stack_info(int fid, int stack, void *info, size_t max_len) + * + * get the info about ISDN stack + * + * parameter: + * fid - FILE descriptor returned by mISDN_open + * stack - ID of the stack + * info - buffer to store info + * max_len - size of above buffer + * + * return: + * length of stored info, negativ values are errors + * + */ +extern int mISDN_get_stack_info(int fid, int stack, void *info, size_t max_len); + +/* mISDN_new_stack(int fid, stack_info_t *s_info) + * + * create a new stack + * + * parameter: + * fid - FILE descriptor returned by mISDN_open + * s_info - info for the new stack + * + * return: + * stack id or negativ error code + * + */ +extern int mISDN_new_stack(int fid, stack_info_t *s_info); + +/* mISDN_set_stack(int fid, int stack, mISDN_pid_t *pid) + * + * setup a stack for the given protocol + * + * parameter: + * fid - FILE descriptor returned by mISDN_open + * stack - stack id + * pid - description of the stack protocol + * + * return: + * 0 on sucess other values are errors + * + */ +extern int mISDN_set_stack(int fid, int stack, mISDN_pid_t *pid); + +/* mISDN_clear_stack(int fid, int stack) + * + * clear the protocol stack + * + * parameter: + * fid - FILE descriptor returned by mISDN_open + * stack - stack id + * + * return: + * 0 on sucess other values are errors + * + */ +extern int mISDN_clear_stack(int fid, int stack); + +/* mISDNprint_stack_info(FILE *file, stack_info_t *s_info) + * + * print out the stack_info in readable output + * + * parameter: + * file - stream to print to + * s_info - stack_info + * + * return: + * nothing + * + */ +extern void mISDNprint_stack_info(FILE *file, stack_info_t *s_info); + +/* Prototypes from layer.c */ + +/* mISDN_get_layerid(int fid, int stack, int layer) + * + * get the id of the layer given by stack and layer number + * + * parameter: + * fid - FILE descriptor returned by mISDN_open + * stack - ID of the stack + * layer - layer number + * + * return: + * layer id or negativ error code + * + */ +extern int mISDN_get_layerid(int fid, int stack, int layer); + +/* mISDN_new_layer(int fid, layer_info_t *l_info) + * + * create a new layer + * + * parameter: + * fid - FILE descriptor returned by mISDN_open + * l_info - info for the layer + * + * return: + * layer id or negativ error code + * + */ +extern int mISDN_new_layer(int fid, layer_info_t *l_info); + +/* mISDN_connect(int fid, interface_info_t *i_info) + * + * create a new connection + * + * parameter: + * fid - FILE descriptor returned by mISDN_open + * i_info - info for the connection + * + * return: + * 0 on success or error code + * + */ +extern int mISDN_connect(int fid, interface_info_t *i_info); + +/* mISDN_get_layer_info(int fid, int lid, void *info, size_t max_len) + * + * get the info about ISDN layer + * + * parameter: + * fid - FILE descriptor returned by mISDN_open + * lid - ID of the layer + * info - buffer to store info + * max_len - size of above buffer + * + * return: + * length of stored info, negativ values are errors + * + */ +extern int mISDN_get_layer_info(int fid, int lid, void *info, size_t max_len); + +/* mISDN_get_interface_info(int fid, interface_info_t *i_info) + * + * get the info about ISDN layer interface + * + * parameter: + * fid - FILE descriptor returned by mISDN_open + * i_info - contains the info about layer (i_info->owner) and + * which interface (i_info->stat) and gives requested info back + * + * return: + * 0 on sucess other values are errors + * + */ +extern int mISDN_get_interface_info(int fid, interface_info_t *i_info); + +/* Prototypes and defines for status.c */ + +/* l1 status_info */ +typedef struct _status_info_l1 { + int len; + int typ; + int protocol; + int status; + int state; + int Flags; + int T3; + int delay; + int debug; +} status_info_l1_t; + +/* State values for l1 state machine (status_info_l1_t state field) */ +extern char *strL1SState[]; + + +/* l2 status_info */ +typedef struct _laddr { + u_char A; + u_char B; +} laddr_t; + + +typedef struct _status_info_l2 { + int len; + int typ; + int protocol; + int state; + int sapi; + int tei; + laddr_t addr; + int maxlen; + u_int flag; + u_int vs; + u_int va; + u_int vr; + int rc; + u_int window; + u_int sow; + int T200; + int N200; + int T203; + int len_i_queue; + int len_ui_queue; + int len_d_queue; + int debug; + int tei_state; + int tei_ri; + int T202; + int N202; + int tei_debug; +} status_info_l2_t; + +/* State values for l2 state machine (status_info_l2_t state field) */ +extern char *strL2State[]; + +/* mISDN_get_status_info(int fid, int id, void *info, size_t max_len) + * + * get status infos about layer instances in the ISDN stack + * + * parameter: + * fid - FILE descriptor returned by mISDN_open + * id - ID of the instance + * info - buffer to store info + * max_len - size of above buffer + * + * return: + * length of stored info, negativ values are errors + * + */ +extern int mISDN_get_status_info(int fid, int id, void *info, size_t max_len); + +/* mISDNprint_status(FILE *file, status_info_t *si) + * + * print out the status in readable output + * + * parameter: + * file - stream to print to + * s_info - status_info + * + * return: + * 0 on success or -1 if no known status_info + * + */ +extern int mISDNprint_status(FILE *file, status_info_t *si); + +/* private functions */ + +extern int set_wrrd_atomic(int fid); +extern int clear_wrrd_atomic(int fid); + +#endif diff --git a/include/tone.h b/include/tone.h new file mode 100644 index 0000000..5b2658a --- /dev/null +++ b/include/tone.h @@ -0,0 +1,29 @@ +#ifndef TONE_H +#define TONE_H + +/* + * These are 10 periods of the 425 Hz tone used by most inband signals + * Its actually not exacly 425 Hz, but 416,66667, which fit very well + * the 15% tolerance + */ + +#define TONE_425_SIZE 192 +extern const unsigned char tone_425[TONE_425_SIZE]; + +/* + * These are 10 ms of silence + */ + +#define TONE_SILENCE_SIZE 80 +extern const unsigned char tone_SILENCE[TONE_SILENCE_SIZE]; + +/* Values for tone pattern */ +#define TONE_ALERT_SILENCE_TIME 4000000 +#define TONE_ALERT_TIME 1000000 +#define TONE_BUSY_SILENCE_TIME 500000 +#define TONE_BUSY_TIME 500000 + +extern int tone_handler(bchannel_t *bc); +extern int set_tone(bchannel_t *bc, int tone); + +#endif diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..6f200c6 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,19 @@ + +all: libmISDN.a + +libmISDN.a: device.o layer.o stack.o status.o + rm -f $@ + ar -r $@ $^ + ar -s $@ + +device.o : device.c ../include/mISDNlib.h +layer.o : layer.c ../include/mISDNlib.h +stack.o : stack.c ../include/mISDNlib.h +status.o : status.c ../include/mISDNlib.h + +clean: + rm -f *.o *~ DEADJOE + +distclean: clean + rm -f *.a + \ No newline at end of file diff --git a/lib/device.c b/lib/device.c new file mode 100644 index 0000000..44626bd --- /dev/null +++ b/lib/device.c @@ -0,0 +1,684 @@ +#include "mISDNlib.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* API library to use with /dev/mISDN */ + +typedef struct _mISDNdev { + struct _mISDNdev *prev; + struct _mISDNdev *next; + pthread_mutex_t rmutex; + pthread_mutex_t wmutex; + int Flags; + int fid; + int isize; + unsigned char *inbuf; + unsigned char *irp; + unsigned char *iend; +} mISDNdev_t; + +#define FLG_mISDN_WRRD_ATOMIC 1 + +mISDNdev_t *devlist = NULL; + +static pthread_mutex_t devlist_lock = PTHREAD_MUTEX_INITIALIZER; + +#define mISDN_DEVICE "/dev/mISDN" + +#if 0 +void +xxxxxxxxxx(void) { +if (devlist) + fprintf(stderr, "xxxxxxxxxx dev %p prev %p next %p\n", devlist, devlist->prev, devlist->next); +else + fprintf(stderr, "xxxxxxxxxx devlist %p\n", devlist); +} +#endif + +int +mISDN_open(void) +{ + int fid; + mISDNdev_t *dev; + + + if (0>(fid = open(mISDN_DEVICE, O_RDWR | O_NONBLOCK))) + return(fid); + pthread_mutex_lock(&devlist_lock); + dev = devlist; + while(dev) { + if (dev->fid==fid) + break; + dev = dev->next; + } + pthread_mutex_unlock(&devlist_lock); + if (dev) { + fprintf(stderr, "%s: device %d (%p) has allready fid(%d)\n", + __FUNCTION__, dev->fid, dev, fid); + close(fid); + errno = EBUSY; + return(-1); + } + dev = malloc(sizeof(mISDNdev_t)); + if (!dev) { + close(fid); + errno = ENOMEM; + return(-1); + } + memset(dev, 0, sizeof(mISDNdev_t)); + dev->fid = fid; + dev->isize = mISDN_INBUFFER_SIZE; + dev->inbuf = malloc(dev->isize); + if (!dev->inbuf) { + free(dev); + close(fid); + errno = ENOMEM; + return(-1); + } + dev->irp = dev->inbuf; + dev->iend = dev->inbuf; + pthread_mutex_init(&dev->rmutex, NULL); + pthread_mutex_init(&dev->wmutex, NULL); + + pthread_mutex_lock(&devlist_lock); + dev->prev = devlist; + while(dev->prev && dev->prev->next) + dev->prev = dev->prev->next; + if (devlist) + dev->prev->next = dev; + else + devlist = dev; + pthread_mutex_unlock(&devlist_lock); + return(fid); +} + +int +mISDN_close(int fid) +{ + mISDNdev_t *dev = devlist; + int ret; + + pthread_mutex_lock(&devlist_lock); + while(dev) { + if (dev->fid==fid) + break; + dev = dev->next; + } + + if (!dev) { + pthread_mutex_unlock(&devlist_lock); + errno = ENODEV; + return(-1); + } + if (dev->prev) + dev->prev->next = dev->next; + if (dev->next) + dev->next->prev = dev->prev; + if (devlist==dev) + devlist=dev->next; + pthread_mutex_lock(&dev->rmutex); + if (dev->inbuf) + free(dev->inbuf); + dev->inbuf = NULL; + pthread_mutex_unlock(&dev->rmutex); + ret = pthread_mutex_destroy(&dev->rmutex); + if (ret) + fprintf(stderr, "%s: rmutex destroy returns %d\n", + __FUNCTION__, ret); + pthread_mutex_lock(&dev->wmutex); + pthread_mutex_unlock(&dev->wmutex); + ret = pthread_mutex_destroy(&dev->wmutex); + if (ret) + fprintf(stderr, "%s: wmutex destroy returns %d\n", + __FUNCTION__, ret); + pthread_mutex_unlock(&devlist_lock); + free(dev); + return(close(fid)); +} + +static int +mISDN_remove_iframe(mISDNdev_t *dev, iframe_t *frm) +{ + u_char *ep; + int len; + + if (frm->len > 0) + len = mISDN_HEADER_LEN + frm->len; + else + len = mISDN_HEADER_LEN; + ep = (u_char *)frm; + ep += len; + if (ep >= dev->iend) + dev->iend = (u_char *)frm; + else { + memcpy(frm, ep, dev->iend - ep); + dev->iend -= len; + } + + return(dev->iend - dev->irp); +} + +int +mISDN_read(int fid, void *buf, size_t count, int utimeout) { + mISDNdev_t *dev; + int ret = 0, len, sel; + fd_set in; + iframe_t *ifr; + struct timeval tout; +#ifdef MUTEX_TIMELOCK + struct timespec ts; +#endif + pthread_mutex_lock(&devlist_lock); + dev = devlist; + while(dev) { + if (dev->fid==fid) + break; + dev = dev->next; + } + pthread_mutex_unlock(&devlist_lock); + if (!dev) { + errno = ENODEV; + return(-1); + } + if (utimeout != -1) { + tout.tv_sec = utimeout/1000000; + tout.tv_usec = utimeout%1000000; +#ifdef MUTEX_TIMELOCK + if (utimeout == 0) { + ret = pthread_mutex_trylock(&dev->rmutex); + if (ret) { + fprintf(stderr, "%s: mutex_trylock (%d)\n", + __FUNCTION__, ret); + errno = ret; + return(-1); + } + } else { +#ifdef CLOCK_REALTIME + clock_gettime(CLOCK_REALTIME, &ts); +#else + { + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv,&tz); + TIMEVAL_TO_TIMESPEC(&tv,&ts); + } +#endif + ts.tv_sec += tout.tv_sec; + ts.tv_nsec += 1000*tout.tv_usec; + if (ts.tv_nsec > 1000000000L) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000L; + } + ret = pthread_mutex_timedlock(&dev->rmutex, &ts); + if (ret) { + fprintf(stderr, "%s: mutex_timedlock (%d)\n", + __FUNCTION__, ret); + errno = ret; + return(-1); + } + } +#else + pthread_mutex_lock(&dev->rmutex); +#endif + } else + pthread_mutex_lock(&dev->rmutex); + + if (dev->Flags & FLG_mISDN_WRRD_ATOMIC) { +// fprintf(stderr, "%s: WRRD_ATOMIC try again\n", __FUNCTION__); + errno = EAGAIN; + ret = -1; + goto out; + } + len = dev->iend - dev->irp; + if (!len) { + dev->irp = dev->iend = dev->inbuf; + pthread_mutex_unlock(&dev->rmutex); + FD_ZERO(&in); + FD_SET(fid, &in); + if (utimeout != -1) { + sel = select(fid + 1, &in, NULL, NULL, &tout); + } else + sel = select(fid + 1, &in, NULL, NULL, NULL); + if (sel<0) { +// fprintf(stderr, "%s: select err(%d)\n", __FUNCTION__, errno); + return(sel); + } else if (sel==0) { +// fprintf(stderr, "%s: select timed out\n", __FUNCTION__); + return(0); + } + if (FD_ISSET(fid, &in)) { +#ifdef MUTEX_TIMELOCK + if (!utimeout) { + ret = pthread_mutex_trylock(&dev->rmutex); + if (ret) { + fprintf(stderr, "%s: mutex_trylock (%d)\n", + __FUNCTION__, ret); + errno = ret; + return(-1); + } + } else +#endif + pthread_mutex_lock(&dev->rmutex); + len = dev->isize - (dev->iend - dev->irp); + if (len<=0) { + errno = ENOSPC; + ret = -1; + goto out; + } + if (dev->Flags & FLG_mISDN_WRRD_ATOMIC) { +// fprintf(stderr, "%s: WRRD_ATOMIC try again\n", __FUNCTION__); + errno = EAGAIN; + ret = -1; + goto out; + } + len = read(fid, dev->iend, len); +// fprintf(stderr, "%s: read %d\n", __FUNCTION__, len); + if (len <= 0) { + ret = len; + goto out; + } + dev->iend += len; + len = dev->iend - dev->irp; + } else { + return(0); + } + } + if (len < mISDN_HEADER_LEN) { + dev->iend = dev->irp; + fprintf(stderr, "%s: frame too short:%d\n", + __FUNCTION__, len); + errno = EINVAL; + ret = -1; + goto out; + } + ifr = (iframe_t *)dev->irp; + if (ifr->len > 0) { + if ((ifr->len + mISDN_HEADER_LEN) > len) { + dev->iend = dev->irp; + errno = EINVAL; + ret = -1; + goto out; + } + len = ifr->len + mISDN_HEADER_LEN; + } else + len = mISDN_HEADER_LEN; + if (len>count) { + errno = ENOSPC; + ret = -1; + goto out; + } + memcpy(buf, dev->irp, len); + dev->irp += len; + ret = len; +out: + pthread_mutex_unlock(&dev->rmutex); + return(ret); +} + +static iframe_t * +mISDN_find_iframe(mISDNdev_t *dev, u_int addr, u_int prim) { + iframe_t *frm; + u_char *rp; + + rp = dev->irp; + while(rpiend) { + if ((dev->iend - rp) < mISDN_HEADER_LEN) { + return(NULL); + } + frm = (iframe_t *)rp; + if ((frm->addr == addr) && (frm->prim == prim)) + return(frm); + if (frm->len > 0) + rp += mISDN_HEADER_LEN + frm->len; + else + rp += mISDN_HEADER_LEN; + } + return(NULL); +} + + +int +mISDN_read_frame(int fid, void *buf, size_t count, u_int addr, u_int msgtype, + int utimeout) +{ + mISDNdev_t *dev; + int len, sel, first, ret = 0; + fd_set in; + iframe_t *ifr; + struct timeval tout; +#ifdef MUTEX_TIMELOCK + struct timespec ts; +#endif + + pthread_mutex_lock(&devlist_lock); + dev = devlist; + while(dev) { + if (dev->fid==fid) + break; + dev = dev->next; + } + pthread_mutex_unlock(&devlist_lock); + if (!dev) { + errno = ENODEV; + return(-1); + } + if (utimeout != -1) { + tout.tv_sec = utimeout/1000000; + tout.tv_usec = utimeout%1000000; +#ifdef MUTEX_TIMELOCK + if (utimeout == 0) { + ret = pthread_mutex_trylock(&dev->rmutex); + if (ret) { + fprintf(stderr, "%s: mutex_trylock (%d)\n", + __FUNCTION__, ret); + errno = ret; + return(-1); + } + } else { +#ifdef CLOCK_REALTIME + clock_gettime(CLOCK_REALTIME, &ts); +#else + { + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv,&tz); + TIMEVAL_TO_TIMESPEC(&tv,&ts); + } +#endif + ts.tv_sec += tout.tv_sec; + ts.tv_nsec += 1000*tout.tv_usec; + if (ts.tv_nsec > 1000000000L) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000L; + } + ret = pthread_mutex_timedlock(&dev->rmutex, &ts); + if (ret) { + fprintf(stderr, "%s: mutex_timedlock (%d)\n", + __FUNCTION__, ret); + errno = ret; + return(-1); + } + } +#else + pthread_mutex_lock(&dev->rmutex); +#endif + } else + pthread_mutex_lock(&dev->rmutex); + + first = 1; + while((utimeout == -1) || tout.tv_sec || tout.tv_usec || first) { + if (!first || !(dev->iend - dev->irp)) { + FD_ZERO(&in); + FD_SET(fid, &in); + if (utimeout != -1) + sel = select(fid + 1, &in, NULL, NULL, &tout); + else + sel = select(fid + 1, &in, NULL, NULL, NULL); + if (sel<0) { +// fprintf(stderr, "%s: select err(%d)\n", __FUNCTION__, errno); + ret = sel; + goto out; + } else if (sel==0) { +// fprintf(stderr, "%s: select timed out\n", __FUNCTION__); + ret = 0; + goto out; + } + if (FD_ISSET(fid, &in)) { + len = dev->isize - (dev->iend - dev->irp); + if (len<=0) { + errno = ENOSPC; + ret = -1; + goto out; + } + len = read(fid, dev->iend, len); +// fprintf(stderr, "%s: read %d\n", __FUNCTION__, len); + if (len <= 0) { + ret = len; + goto out; + } + dev->iend += len; + } else + continue; + } + if (dev->iend - dev->irp) { + ifr = mISDN_find_iframe(dev, addr, msgtype); + if (ifr) { + if (ifr->len > 0) { +#if 0 + if ((ifr->len + mISDN_HEADER_LEN) > len) { + dev->irp = dev->iend; + errno = EINVAL; + ret = -1; + goto out; + } +#endif + len = ifr->len + mISDN_HEADER_LEN; + } else + len = mISDN_HEADER_LEN; + if (len > count) { + errno = ENOSPC; + ret = -1; + goto out; + } + memcpy(buf, ifr, len); + mISDN_remove_iframe(dev, ifr); + ret = len; + goto out; + } + } + first = 0; + } +out: + pthread_mutex_unlock(&dev->rmutex); + return(ret); +} + +int +mISDN_write(int fid, void *buf, size_t count, int utimeout) { + mISDNdev_t *dev; + int len, sel; + fd_set out; + struct timeval tout; +#ifdef MUTEX_TIMELOCK + struct timespec ts; + int ret; +#endif + + pthread_mutex_lock(&devlist_lock); + dev = devlist; + while(dev) { + if (dev->fid==fid) + break; + dev = dev->next; + } + pthread_mutex_unlock(&devlist_lock); + if (!dev) { + errno = ENODEV; + return(-1); + } + FD_ZERO(&out); + FD_SET(fid, &out); + if (utimeout != -1) { + tout.tv_sec = utimeout/1000000; + tout.tv_usec = utimeout%1000000; + sel = select(fid + 1, NULL, &out, NULL, &tout); + } else + sel = select(fid + 1, NULL, &out, NULL, NULL); + if (sel<=0) + return(sel); + if (!FD_ISSET(fid, &out)) + return(0); + if (utimeout != -1) { +#ifdef MUTEX_TIMELOCK + if (utimeout == 0) { + ret = pthread_mutex_trylock(&dev->wmutex); + if (ret) { + fprintf(stderr, "%s: mutex_trylock (%d)\n", + __FUNCTION__, ret); + errno = ret; + return(-1); + } + } else { +#ifdef CLOCK_REALTIME + clock_gettime(CLOCK_REALTIME, &ts); +#else + { + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv,&tz); + TIMEVAL_TO_TIMESPEC(&tv,&ts); + } +#endif + ts.tv_sec += tout.tv_sec; + ts.tv_nsec += 1000*tout.tv_usec; + if (ts.tv_nsec > 1000000000L) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000L; + } + ret = pthread_mutex_timedlock(&dev->wmutex, &ts); + if (ret) { + fprintf(stderr, "%s: mutex_timedlock (%d)\n", + __FUNCTION__, ret); + errno = ret; + return(-1); + } + } +#else + pthread_mutex_lock(&dev->wmutex); +#endif + } else + pthread_mutex_lock(&dev->wmutex); + len = write(fid, buf, count); + pthread_mutex_unlock(&dev->wmutex); + return(len); +} + +int +mISDN_write_frame(int fid, void *fbuf, u_int addr, u_int msgtype, + int dinfo, int dlen, void *dbuf, int utimeout) +{ + iframe_t *ifr = fbuf; + int len = mISDN_HEADER_LEN; + int ret; + + if (!fbuf) { + errno = EINVAL; + return(-1); + } + if ((dlen > 0) && !dbuf) { + errno = EINVAL; + return(-1); + } + ifr->addr = addr; + ifr->prim = msgtype; + ifr->dinfo= dinfo; + ifr->len = dlen; + if (dlen>0) { + len += dlen; + memcpy(&ifr->data.i, dbuf, dlen); + } + ret = mISDN_write(fid, ifr, len, utimeout); + if (ret == len) + ret = 0; + else if (ret>=0) { + errno = ENOSPC; + ret = -1; + } + return(ret); +} + +int +mISDN_select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) +{ + mISDNdev_t *dev = devlist; + + if (readfds) { + pthread_mutex_lock(&devlist_lock); + while(dev) { + if (FD_ISSET(dev->fid, readfds)) { + if (dev->iend - dev->irp) { + pthread_mutex_unlock(&devlist_lock); + FD_ZERO(readfds); + FD_SET(dev->fid, readfds); + if (writefds) + FD_ZERO(writefds); + if (exceptfds) + FD_ZERO(exceptfds); + return(1); + } + } + dev = dev->next; + } + pthread_mutex_unlock(&devlist_lock); + } + + return(select(n, readfds, writefds, exceptfds, timeout)); +} + +int +set_wrrd_atomic(int fid) +{ + mISDNdev_t *dev; + int ret; + + pthread_mutex_lock(&devlist_lock); + dev = devlist; + while(dev) { + if (dev->fid==fid) + break; + dev = dev->next; + } + pthread_mutex_unlock(&devlist_lock); + if (!dev) { + return(-1); + } + pthread_mutex_lock(&dev->rmutex); + if (dev->Flags & FLG_mISDN_WRRD_ATOMIC) + ret = 1; + else { + ret = 0; + dev->Flags |= FLG_mISDN_WRRD_ATOMIC; + } + pthread_mutex_unlock(&dev->rmutex); + return(ret); +} + +int +clear_wrrd_atomic(int fid) +{ + mISDNdev_t *dev; + int ret; + + pthread_mutex_lock(&devlist_lock); + dev = devlist; + while(dev) { + if (dev->fid==fid) + break; + dev = dev->next; + } + pthread_mutex_unlock(&devlist_lock); + if (!dev) { + return(-1); + } + if (dev->Flags & FLG_mISDN_WRRD_ATOMIC) { + dev->Flags &= ~FLG_mISDN_WRRD_ATOMIC; + ret = 0; + } else { + ret = 1; + } + return(ret); +} diff --git a/lib/layer.c b/lib/layer.c new file mode 100644 index 0000000..f98b624 --- /dev/null +++ b/lib/layer.c @@ -0,0 +1,138 @@ +#include +#include +#include "mISDNlib.h" + +int +mISDN_get_layerid(int fid, int stack, int layer) +{ + iframe_t ifr; + int ret; + + set_wrrd_atomic(fid); + ret = mISDN_write_frame(fid, &ifr, stack, MGR_GETLAYERID | REQUEST, + layer, 0, NULL, TIMEOUT_1SEC); + if (ret) { + clear_wrrd_atomic(fid); + return(ret); + } + ret = mISDN_read_frame(fid, &ifr, sizeof(iframe_t), + stack, MGR_GETLAYERID | CONFIRM, TIMEOUT_1SEC); + clear_wrrd_atomic(fid); + if (ret != mISDN_HEADER_LEN) { + if (ret>0) + ret = -EINVAL; + + } else { + if (ifr.len) + ret = ifr.len; + else + ret = ifr.dinfo; + } + return(ret); +} + +int +mISDN_new_layer(int fid, layer_info_t *l_info) +{ + unsigned char buf[sizeof(layer_info_t) + mISDN_HEADER_LEN]; + iframe_t *ifr = (iframe_t *)buf; + int ret; + + set_wrrd_atomic(fid); + ret = mISDN_write_frame(fid, buf, 0, MGR_NEWLAYER | REQUEST, + 0, sizeof(layer_info_t), l_info, TIMEOUT_1SEC); +// fprintf(stderr, "%s: wret %d\n", __FUNCTION__, ret); + if (ret) { + clear_wrrd_atomic(fid); + return(ret); + } + ret = mISDN_read_frame(fid, ifr, sizeof(layer_info_t) + mISDN_HEADER_LEN, + 0, MGR_NEWLAYER | CONFIRM, TIMEOUT_1SEC); + clear_wrrd_atomic(fid); +// fprintf(stderr, "%s: rret %d\n", __FUNCTION__, ret); + if (ret<0) + return(ret); + if (ret < (mISDN_HEADER_LEN + sizeof(int))) { + if (ret == mISDN_HEADER_LEN) + ret = ifr->len; + else if (ret>0) + ret = -EINVAL; + } else + ret = ifr->data.i; +// fprintf(stderr, "%s: ret %x\n", __FUNCTION__, ret); + return(ret); +} + +int +mISDN_connect(int fid, interface_info_t *i_info) +{ + unsigned char buf[sizeof(interface_info_t) + mISDN_HEADER_LEN]; + iframe_t ifr; + int ret; + + set_wrrd_atomic(fid); + ret = mISDN_write_frame(fid, buf, 0, MGR_CONNECT | REQUEST, + 0, sizeof(interface_info_t), i_info, TIMEOUT_1SEC); + if (ret) { + clear_wrrd_atomic(fid); + return(ret); + } + ret = mISDN_read_frame(fid, &ifr, sizeof(iframe_t), + 0, MGR_CONNECT | CONFIRM, TIMEOUT_1SEC); + clear_wrrd_atomic(fid); + if (ret != mISDN_HEADER_LEN) { + if (ret > 0) + ret = -1; + } else { + if (ifr.len) + ret = ifr.len; + else + ret = ifr.data.i; + } + return(ret); +} + +int +mISDN_get_layer_info(int fid, int lid, void *info, size_t max_len) +{ + iframe_t ifr; + int ret; + + set_wrrd_atomic(fid); + ret = mISDN_write_frame(fid, &ifr, lid, MGR_GETLAYER | REQUEST, + 0, 0, NULL, TIMEOUT_1SEC); + if (ret) { + clear_wrrd_atomic(fid); + return(ret); + } + ret = mISDN_read_frame(fid, info, max_len, + lid, MGR_GETLAYER | CONFIRM, TIMEOUT_1SEC); + clear_wrrd_atomic(fid); + return(ret); +} + +int +mISDN_get_interface_info(int fid, interface_info_t *i_info) +{ + unsigned char buf[sizeof(interface_info_t) + mISDN_HEADER_LEN]; + iframe_t *ifr = (iframe_t *)buf; + int ret; + + set_wrrd_atomic(fid); + ret = mISDN_write_frame(fid, ifr, i_info->owner, MGR_GETIF | REQUEST, + i_info->stat, 0, NULL, TIMEOUT_1SEC); + if (ret) { + clear_wrrd_atomic(fid); + return(ret); + } + ret = mISDN_read_frame(fid, ifr, sizeof(interface_info_t) + mISDN_HEADER_LEN, + i_info->owner, MGR_GETIF | CONFIRM, TIMEOUT_1SEC); + clear_wrrd_atomic(fid); + if (ret==mISDN_HEADER_LEN) { + ret = ifr->data.i; + } else if (ret == (sizeof(interface_info_t) + mISDN_HEADER_LEN)) { + ret = 0; + memcpy(i_info, &ifr->data.p, sizeof(interface_info_t)); + } + return(ret); +} diff --git a/lib/stack.c b/lib/stack.c new file mode 100644 index 0000000..3bc4650 --- /dev/null +++ b/lib/stack.c @@ -0,0 +1,141 @@ +#include +#include +#include "mISDNlib.h" +// #include + +int +mISDN_get_stack_count(int fid) +{ + iframe_t ifr; + int ret; + + set_wrrd_atomic(fid); + ret = mISDN_write_frame(fid, &ifr, 0, MGR_GETSTACK | REQUEST, + 0, 0, NULL, TIMEOUT_1SEC); + if (ret) { + clear_wrrd_atomic(fid); + return(ret); + } + ret = mISDN_read_frame(fid, &ifr, sizeof(iframe_t), 0, + MGR_GETSTACK | CONFIRM, TIMEOUT_1SEC); + clear_wrrd_atomic(fid); + if (ret != mISDN_HEADER_LEN) { + if (ret > 0) + ret = -EINVAL; + } else { + if (ifr.len) + ret = ifr.len; + else + ret = ifr.dinfo; + } + return(ret); +} + +int +mISDN_new_stack(int fid, stack_info_t *s_info) +{ + u_char buf[sizeof(stack_info_t) + mISDN_HEADER_LEN]; + iframe_t ifr; + int ret; + + set_wrrd_atomic(fid); + ret = mISDN_write_frame(fid, buf, 0, MGR_NEWSTACK | REQUEST, + 0, sizeof(stack_info_t), s_info, TIMEOUT_1SEC); + if (ret) { + clear_wrrd_atomic(fid); + return(ret); + } + ret = mISDN_read_frame(fid, &ifr, sizeof(iframe_t), 0, + MGR_NEWSTACK | CONFIRM, TIMEOUT_1SEC); + clear_wrrd_atomic(fid); + if (ret == mISDN_HEADER_LEN) { + if (ifr.len) + ret = ifr.len; + else + ret = ifr.dinfo; + } + return(ret); +} + +int +mISDN_set_stack(int fid, int stack, mISDN_pid_t *pid) +{ + u_char buf[sizeof(mISDN_pid_t) + mISDN_HEADER_LEN]; + iframe_t ifr; + int ret; + + set_wrrd_atomic(fid); + ret = mISDN_write_frame(fid, buf, stack, MGR_SETSTACK | REQUEST, + 0, sizeof(mISDN_pid_t), pid, TIMEOUT_1SEC); + if (ret) { + clear_wrrd_atomic(fid); + return(ret); + } + ret = mISDN_read_frame(fid, &ifr, sizeof(iframe_t), + stack, MGR_SETSTACK | CONFIRM, TIMEOUT_1SEC); + clear_wrrd_atomic(fid); + if (ret == mISDN_HEADER_LEN) + ret = ifr.len; + else if (ret>0) + ret = -EINVAL; + return(ret); +} + +int +mISDN_clear_stack(int fid, int stack) +{ + iframe_t ifr; + int ret; + + set_wrrd_atomic(fid); + ret = mISDN_write_frame(fid, &ifr, stack, MGR_CLEARSTACK | REQUEST, + 0, 0, NULL, TIMEOUT_1SEC); + if (ret) { + clear_wrrd_atomic(fid); + return(ret); + } + ret = mISDN_read_frame(fid, &ifr, sizeof(iframe_t), + stack, MGR_CLEARSTACK | CONFIRM, TIMEOUT_1SEC); + clear_wrrd_atomic(fid); + if (ret == mISDN_HEADER_LEN) + ret = ifr.len; + else if (ret>0) + ret = -EINVAL; + return(ret); +} + +int +mISDN_get_stack_info(int fid, int stack, void *info, size_t max_len) +{ + iframe_t ifr; + int ret; + + set_wrrd_atomic(fid); + + ret = mISDN_write_frame(fid, &ifr, stack, MGR_GETSTACK | REQUEST, + 0, 0, NULL, TIMEOUT_1SEC); + if (ret) { + clear_wrrd_atomic(fid); + return(ret); + } + ret = mISDN_read_frame(fid, info, max_len, + stack, MGR_GETSTACK | CONFIRM, TIMEOUT_1SEC); + clear_wrrd_atomic(fid); + return(ret); +} + +void +mISDNprint_stack_info(FILE *file, stack_info_t *s_info) +{ + int i; + + fprintf(file, "stack id %08x\n", s_info->id); + fprintf(file, " ext %08x\n", s_info->extentions); + for(i=0;i<=MAX_LAYER_NR;i++) + fprintf(file, " prot%d %08x\n", i, s_info->pid.protocol[i]); + for(i=0;iinstcnt;i++) + fprintf(file, " inst%d %08x\n", i, s_info->inst[i]); + fprintf(file, " mgr %08x\n", s_info->mgr); + for(i=0;ichildcnt;i++) + fprintf(file, " child%d %08x\n", i, s_info->child[i]); +} diff --git a/lib/status.c b/lib/status.c new file mode 100644 index 0000000..6102bb7 --- /dev/null +++ b/lib/status.c @@ -0,0 +1,79 @@ +#include +#include +#include "mISDNlib.h" + +/* State values for l1 state machine (status_info_l1_t state field) */ +char *strL1SState[] = +{ + "ST_L1_F2", + "ST_L1_F3", + "ST_L1_F4", + "ST_L1_F5", + "ST_L1_F6", + "ST_L1_F7", + "ST_L1_F8", +}; + + +/* State values for l2 state machine (status_info_l2_t state field) */ +char *strL2State[] = +{ + "ST_L2_1", + "ST_L2_2", + "ST_L2_3", + "ST_L2_4", + "ST_L2_5", + "ST_L2_6", + "ST_L2_7", + "ST_L2_8", +}; + +int +mISDN_get_status_info(int fid, int id, void *info, size_t max_len) +{ + iframe_t ifr; + int ret; + + set_wrrd_atomic(fid); + ret = mISDN_write_frame(fid, &ifr, id, MGR_STATUS | REQUEST, + 0, 0, NULL, TIMEOUT_1SEC); + if (ret) { + clear_wrrd_atomic(fid); + return(ret); + } + ret = mISDN_read_frame(fid, info, max_len, + id, MGR_STATUS | CONFIRM, TIMEOUT_1SEC); + clear_wrrd_atomic(fid); + return(ret); +} + +/* not complete now */ + +int +mISDNprint_status(FILE *file, status_info_t *si) +{ + int ret=0; + status_info_l1_t *si1; + status_info_l2_t *si2; + + switch(si->typ) { + case STATUS_INFO_L1: + si1 = (status_info_l1_t *)si; + fprintf(file," prot:%x status:%d state:%s Flags:%x\n", + si1->protocol, si1->status, + strL1SState[si1->state], si1->Flags); + break; + case STATUS_INFO_L2: + si2 = (status_info_l2_t *)si; + fprintf(file," prot:%x tei:%d state:%s flag:%x\n", + si2->protocol, si2->tei, + strL2State[si2->state], si2->flag); + break; + default: + fprintf(file, "unknown status type %d\n", si->typ); + break; + } + return(ret); +} + + diff --git a/tenovis/Makefile b/tenovis/Makefile new file mode 100644 index 0000000..2396ffc --- /dev/null +++ b/tenovis/Makefile @@ -0,0 +1,34 @@ +LIBINCL := $(INCLUDEDIR)/mISDNlib.h + +TENOVISLIB := lib/libtenovis.a + +TENOVISINC := lib/tenovis.h + +SUBDIRS := lib + +PROGS := testlib tstlib + +all: sublib $(PROGS) + +testlib: testlib.o $(TENOVISLIB) $(mISDNLIB) + +tstlib: tstlib.o $(TENOVISLIB) $(mISDNLIB) + + +testlib.o : testlib.c ../include/l3dss1.h $(LIBINCL) $(TENOVISINC) + +tstlib.o : tstlib.c $(LIBINCL) $(TENOVISINC) + +sublib: + $(MAKE) -C lib lib + +subdirs: + set -e; for i in $(SUBDIRS) ; do $(MAKE) -C $$i $(TARGET); done + +clean: + make TARGET=$@ subdirs + rm -f *.o *~ DEADJOE + +distclean: clean + make TARGET=$@ subdirs + rm -f *.a $(PROGS) diff --git a/tenovis/lib/Makefile b/tenovis/lib/Makefile new file mode 100644 index 0000000..24c01a2 --- /dev/null +++ b/tenovis/lib/Makefile @@ -0,0 +1,20 @@ + +lib: libtenovis.a + +libtenovis.a: tenovis_device.o tenovis_intern.o + rm -f $@ + ar -r $@ $^ + ar -s $@ + +tenovis_device.o : tenovis_device.c tenovis.h tenovis_int.h \ + ../../include/mISDNlib.h + +tenovis_intern.o : tenovis_intern.c tenovis.h tenovis_int.h \ + ../../include/mISDNlib.h + +clean: + rm -f *.o *~ DEADJOE + +distclean: clean + rm -f *.a + diff --git a/tenovis/lib/tenovis.h b/tenovis/lib/tenovis.h new file mode 100644 index 0000000..b3eeb5e --- /dev/null +++ b/tenovis/lib/tenovis.h @@ -0,0 +1,68 @@ +/* Interface for Tenovis */ + +/* + * int DL3open(void); + * + * DL3open() opens a device through which the D channel can be accessed. + * + * Returns a file descriptor on success or -1 in case of an error. + * The file descriptor is used in all other DL3* calls and for select(). + * + */ + +extern int DL3open(void); + +/* + * + * int DL3close(int DL3fd) + * + * DL3close(int DL3fd) closes the DL3fd previously opened DL3open(). + * + * The file descriptor DL3fd must not be used after DL3close() was called ! + * + * Parameter: + * DL3fd : file descriptor assigned by DL3open + * + * Returnvalue: + * 0 on success or -1 if the file descriptor was already closed or + * is not valid. + * + */ + +extern int DL3close(int DL3fd); + +/* + * int DL3write(int DL3fd, const void *buf, size_t count); + * + * Sends a message to the layer 3 of the D channel stack. + * + * Parameter: + * DL3fd : file descriptor assigned by DL3open + * buf : pointer to the message buffer + * count : the length of the message in bytes + * + * Returnvalue: + * 0 on success or -1 on error in which case errno is set. + * + * + */ + +extern int DL3write(int DL3fd, const void *buf, size_t count); + +/* + * size_t DL3read(int DL3fd, void *buf, size_t count); + * + * Reads a message from the Layer 3 of the D channel stack. + * + * Parameter: + * DL3fd : file descriptor assigned by DL3open + * buf : pointer to the message buffer + * count : the maximum message size which can read + * + * Returnvalue: + * the length of the message in bytes or -1 in case of an error + * -2 if it was an internal (not L3) message + */ + +extern size_t DL3read(int DL3fd, void *buf, size_t count); + diff --git a/tenovis/lib/tenovis_device.c b/tenovis/lib/tenovis_device.c new file mode 100644 index 0000000..6a461a1 --- /dev/null +++ b/tenovis/lib/tenovis_device.c @@ -0,0 +1,171 @@ +/* + * Interface for Tenovis + * device functions + * + */ + +#include "tenovis_int.h" +#include "tenovis.h" + +/* + * int DL3open(void); + * + * DL3open() opens a device through which the D channel can be accessed. + * + * Returns a file descriptor on success or -1 in case of an error. + * The file descriptor is used in all other DL3* calls and for select(). + * + */ + +int +DL3open(void) +{ + int fid, ret; + tenovisdev_t *dev; + + if (0>(fid = mISDN_open())) + return(fid); + dev = get_tdevice(fid); + if (dev) { + fprintf(stderr, __FUNCTION__ " device %d (%p) has allready fid(%d)\n", + dev->fid, dev, fid); + close(fid); + errno = EBUSY; + return(-1); + } + dev = alloc_tdevice(fid); + if (!dev) { + return(-1); + } + ret = setup_tdevice(dev); + if (ret) + ret = -1; + else + ret = fid; + return(ret); +} + +/* + * + * int DL3close(int DL3fd) + * + * DL3close(int DL3fd) closes the DL3fd previously opened DL3open(). + * + * The file descriptor DL3fd must not be used after DL3close() was called ! + * + * Parameter: + * DL3fd : file descriptor assigned by DL3open + * + * Returnvalue: + * 0 on success or -1 if the file descriptor was already closed or + * is not valid. + * + */ + +int +DL3close(int DL3fd) +{ + tenovisdev_t *dev; + int ret; + + dev = get_tdevice(DL3fd); + if (!dev) { + errno = ENODEV; + return(-1); + } + shutdown_tdevice(dev); + ret = free_tdevice(dev); + return(ret); +} + + +/* + * int DL3write(int DL3fd, const void *buf, size_t count); + * + * Sends a message to the layer 3 of the D channel stack. + * + * Parameter: + * DL3fd : file descriptor assigned by DL3open + * buf : pointer to the message buffer + * count : the length of the message in bytes + * + * Returnvalue: + * 0 on success or -1 on error in which case errno is set. + * + * + */ + +extern int DL3write(int DL3fd, const void *buf, size_t count) +{ + tenovisdev_t *dev; + int ret; + + dev = get_tdevice(DL3fd); + if (!dev) { + errno = ENODEV; + return(-1); + } + if (!count) + return(0); + ret = mISDN_write_frame(dev->fid, dev->buf.p, dev->tlid | IF_UP, + DL_DATA | INDICATION, 0, count, (void *)buf, TIMEOUT_1SEC); + return(ret); +} + +/* + * size_t DL3read(int DL3fd, void *buf, size_t count); + * + * Reads a message from the Layer 3 of the D channel stack. + * + * Parameter: + * DL3fd : file descriptor assigned by DL3open + * buf : pointer to the message buffer + * count : the maximum message size which can read + * + * Returnvalue: + * the length of the message in bytes or -1 in case of an error + * -2 if it was an internal (not L3) message + */ + +extern size_t DL3read(int DL3fd, void *buf, size_t count) +{ + tenovisdev_t *dev; + int ret; + + dev = get_tdevice(DL3fd); + if (!dev) { + errno = ENODEV; + return(-1); + } + if (!buf) { + errno = EINVAL; + return(-1); + } + if (!count) + return(0); + if (count > dev->size - IFRAME_HEAD_SIZE) + count = dev->size - IFRAME_HEAD_SIZE; + ret = mISDN_read(dev->fid, dev->buf.p, count + IFRAME_HEAD_SIZE, + TIMEOUT_10SEC); +#ifdef PRINTDEBUG + fprintf(stdout, __FUNCTION__": mISDN_read ret(%d) adr(%x) pr(%x) di(%x) l(%d)\n", + ret, dev->buf.f->addr, dev->buf.f->prim, + dev->buf.f->dinfo, dev->buf.f->len); +#endif + if (ret <= 0) + return(ret); + if (dev->buf.f->addr == (dev->tlid | IF_UP)) { + if (dev->buf.f->prim == (DL_DATA | REQUEST)) { + if (dev->buf.f->len > count) { + errno = ENOSPC; + return(-1); + } + ret = dev->buf.f->len; + memcpy(buf, &dev->buf.f->data.p, ret); + return(ret); + } + } + ret = intern_read(dev); + return(ret); +} + diff --git a/tenovis/lib/tenovis_int.h b/tenovis/lib/tenovis_int.h new file mode 100644 index 0000000..3e60c27 --- /dev/null +++ b/tenovis/lib/tenovis_int.h @@ -0,0 +1,47 @@ + +#include "mISDNlib.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define PRINTDEBUG +#undef PRINTDEBUG + +#define TN_INBUFFER_SIZE 4000 + +typedef struct _tenovisdev { + struct _tenovisdev *prev; + struct _tenovisdev *next; + int fid; + unsigned int dstid; + unsigned int dl0id; + unsigned int dl1id; + unsigned int dl2id; + unsigned int dl3id; + unsigned int dl4id; + unsigned int tlid; + unsigned int hwid; + unsigned int Flags; + pthread_mutex_t mutex; + int size; + union { + unsigned char *p; + iframe_t *f; + } buf; +} tenovisdev_t; + +#define TN_FLG_L2_ACTIV 0x0001 + +extern tenovisdev_t *get_tdevice(int fid); +extern tenovisdev_t *alloc_tdevice(int fid); +extern int free_tdevice(tenovisdev_t *dev); +extern int setup_tdevice(tenovisdev_t *dev); +extern int shutdown_tdevice(tenovisdev_t *dev); +extern int intern_read(tenovisdev_t *dev); diff --git a/tenovis/lib/tenovis_intern.c b/tenovis/lib/tenovis_intern.c new file mode 100644 index 0000000..5d96f03 --- /dev/null +++ b/tenovis/lib/tenovis_intern.c @@ -0,0 +1,293 @@ +/* + * Internal functions for Tenovis lib + * + */ + +#include "tenovis_int.h" +#include "tenovis.h" + +static tenovisdev_t *tdevlist = NULL; +static pthread_mutex_t tdevlist_lock = PTHREAD_MUTEX_INITIALIZER; + +tenovisdev_t * +alloc_tdevice(int fid) +{ + tenovisdev_t *dev; + + dev = malloc(sizeof(tenovisdev_t)); + if (!dev) { + close(fid); + errno = ENODEV; + return(NULL); + } + memset(dev, 0, sizeof(tenovisdev_t)); + dev->fid = fid; + dev->size = TN_INBUFFER_SIZE; + dev->buf.p = malloc(dev->size); + if (!dev->buf.p) { + close(fid); + free(dev); + errno = ENODEV; + return(NULL); + } + pthread_mutex_init(&dev->mutex, NULL); + pthread_mutex_lock(&tdevlist_lock); + dev->prev = tdevlist; + while(dev->prev && dev->prev->next) + dev->prev = dev->prev->next; + if (tdevlist) + dev->prev->next = dev; + else + tdevlist = dev; + pthread_mutex_unlock(&tdevlist_lock); + return(dev); +} + +tenovisdev_t * +get_tdevice(int fid) +{ + tenovisdev_t *dev; + + pthread_mutex_lock(&tdevlist_lock); + dev = tdevlist; + while(dev) { + if (dev->fid==fid) + break; + dev = dev->next; + } + pthread_mutex_unlock(&tdevlist_lock); + return(dev); +} + +int +free_tdevice(tenovisdev_t *dev) +{ + int ret; + + if (dev->prev) + dev->prev->next = dev->next; + if (dev->next) + dev->next->prev = dev->prev; + if (tdevlist==dev) + tdevlist=dev->next; + pthread_mutex_lock(&dev->mutex); + if (dev->buf.p) + free(dev->buf.p); + dev->buf.p = NULL; + pthread_mutex_unlock(&dev->mutex); + ret = pthread_mutex_destroy(&dev->mutex); + if (ret) + fprintf(stderr, __FUNCTION__ "mutex destroy returns %d\n", + ret); + ret = mISDN_close(dev->fid); + free(dev); + return(ret); +} + +int +setup_tdevice(tenovisdev_t *dev) +{ + int ret; + stack_info_t *stinf; + layer_info_t linf; + interface_info_t iinf; + + ret = mISDN_write_frame(dev->fid, dev->buf.p, 0, + MGR_SETDEVOPT | REQUEST, FLG_mISDNPORT_ONEFRAME, + 0, NULL, TIMEOUT_1SEC); +#ifdef PRINTDEBUG + fprintf(stdout, "MGR_SETDEVOPT ret(%d)\n", ret); +#endif + ret = mISDN_read(dev->fid, dev->buf.p, 1024, TIMEOUT_10SEC); +#ifdef PRINTDEBUG + fprintf(stdout, "mISDN_read ret(%d) pr(%x) di(%x) l(%d)\n", + ret, dev->buf.f->prim, dev->buf.f->dinfo, dev->buf.f->len); +#endif + ret = mISDN_get_stack_count(dev->fid); +#ifdef PRINTDEBUG + fprintf(stdout, "stackcnt %d\n", ret); +#endif + if (ret <= 0) { + free_tdevice(dev); + errno = ENODEV; + return(-1); + } + ret = mISDN_get_stack_info(dev->fid, 1, dev->buf.p, dev->size); + stinf = (stack_info_t *)&dev->buf.f->data.p; +#ifdef PRINTDEBUG + mISDNprint_stack_info(stdout, stinf); + fprintf(stdout, "ext(%x) instcnt(%d) childcnt(%d)\n", + stinf->extentions, stinf->instcnt, stinf->childcnt); +#endif + dev->dstid = stinf->id; + dev->dl0id = mISDN_get_layerid(dev->fid, dev->dstid, 0); + dev->dl1id = mISDN_get_layerid(dev->fid, dev->dstid, 1); + dev->dl2id = mISDN_get_layerid(dev->fid, dev->dstid, 2); + dev->dl3id = mISDN_get_layerid(dev->fid, dev->dstid, 3); + dev->dl4id = mISDN_get_layerid(dev->fid, dev->dstid, 4); +#ifdef PRINTDEBUG + fprintf(stdout, " dl0id = %08x\n", dev->dl0id); + fprintf(stdout, " dl1id = %08x\n", dev->dl1id); + fprintf(stdout, " dl2id = %08x\n", dev->dl2id); + fprintf(stdout, " dl3id = %08x\n", dev->dl3id); + fprintf(stdout, " dl4id = %08x\n", dev->dl4id); + memset(&iinf, 0, sizeof(interface_info_t)); + iinf.owner = dev->dl2id; + iinf.stat = IF_UP; + ret = mISDN_get_interface_info(dev->fid, &iinf); + fprintf(stdout, "l2 up own(%x) -> peer(%x)\n", + iinf.owner, iinf.peer); + memset(&iinf, 0, sizeof(interface_info_t)); + iinf.owner = dev->dl2id; + iinf.stat = IF_DOWN; + ret = mISDN_get_interface_info(dev->fid, &iinf); + fprintf(stdout, "l2 down own(%x) -> peer(%x)\n", + iinf.owner, iinf.peer); + memset(&iinf, 0, sizeof(interface_info_t)); + iinf.owner = dev->dl3id; + iinf.stat = IF_UP; + ret = mISDN_get_interface_info(dev->fid, &iinf); + fprintf(stdout, "l3 up own(%x) -> peer(%x)\n", + iinf.owner, iinf.peer); + memset(&iinf, 0, sizeof(interface_info_t)); + iinf.owner = dev->dl3id; + iinf.stat = IF_DOWN; + ret = mISDN_get_interface_info(dev->fid, &iinf); + fprintf(stdout, "l3 down own(%x) -> peer(%x)\n", + iinf.owner, iinf.peer); +#endif + memset(&linf, 0, sizeof(layer_info_t)); + strcpy(&linf.name[0], "tenovis L2"); + linf.object_id = -1; + linf.extentions = EXT_INST_MIDDLE; + linf.pid.protocol[stinf->instcnt] = ISDN_PID_ANY; + linf.pid.layermask = ISDN_LAYER(stinf->instcnt); + linf.st = dev->dstid; + dev->tlid = mISDN_new_layer(dev->fid, &linf); +#ifdef PRINTDEBUG + fprintf(stdout, "mISDN_new_layer ret(%x)\n", dev->tlid); + ret = mISDN_get_stack_info(dev->fid, 1, dev->buf.p, dev->size); + stinf = (stack_info_t *)&dev->buf.f->data.p; + mISDNprint_stack_info(stdout, stinf); +#endif + memset(&iinf, 0, sizeof(interface_info_t)); + iinf.extentions = EXT_INST_MIDDLE; + iinf.owner = dev->tlid; + iinf.peer = dev->dl3id; + iinf.stat = IF_UP; + ret = mISDN_write_frame(dev->fid, dev->buf.p, dev->tlid, + MGR_SETIF | REQUEST, 0, sizeof(interface_info_t), + &iinf, TIMEOUT_1SEC); +#ifdef PRINTDEBUG + fprintf(stdout, "mISDN_write_frame ret(%d)\n", ret); +#endif + ret = mISDN_read(dev->fid, dev->buf.p, 1024, TIMEOUT_10SEC); +#ifdef PRINTDEBUG + fprintf(stdout, "mISDN_read ret(%d) pr(%x) di(%x) l(%d)\n", + ret, dev->buf.f->prim, dev->buf.f->dinfo, + dev->buf.f->len); +#endif + memset(&iinf, 0, sizeof(interface_info_t)); + iinf.extentions = EXT_INST_MIDDLE; + iinf.owner = dev->tlid; + iinf.peer = dev->dl2id; + iinf.stat = IF_DOWN; + ret = mISDN_write_frame(dev->fid, dev->buf.p, dev->tlid, + MGR_SETIF | REQUEST, 0, sizeof(interface_info_t), + &iinf, TIMEOUT_1SEC); +#ifdef PRINTDEBUG + fprintf(stdout, "mISDN_write_frame ret(%d)\n", ret); +#endif + ret = mISDN_read(dev->fid, dev->buf.p, 1024, TIMEOUT_10SEC); +#ifdef PRINTDEBUG + fprintf(stdout, "mISDN_read ret(%d) pr(%x) di(%x) l(%d)\n", + ret, dev->buf.f->prim, dev->buf.f->dinfo, + dev->buf.f->len); + memset(&iinf, 0, sizeof(interface_info_t)); + iinf.owner = dev->dl2id; + iinf.stat = IF_UP; + ret = mISDN_get_interface_info(dev->fid, &iinf); + fprintf(stdout, "l2 up own(%x) -> peer(%x)\n", + iinf.owner, iinf.peer); + memset(&iinf, 0, sizeof(interface_info_t)); + iinf.owner = dev->dl2id; + iinf.stat = IF_DOWN; + ret = mISDN_get_interface_info(dev->fid, &iinf); + fprintf(stdout, "l2 down own(%x) -> peer(%x)\n", + iinf.owner, iinf.peer); + memset(&iinf, 0, sizeof(interface_info_t)); + iinf.owner = dev->dl3id; + iinf.stat = IF_UP; + ret = mISDN_get_interface_info(dev->fid, &iinf); + fprintf(stdout, "l3 up own(%x) -> peer(%x)\n", + iinf.owner, iinf.peer); + memset(&iinf, 0, sizeof(interface_info_t)); + iinf.owner = dev->dl3id; + iinf.stat = IF_DOWN; + ret = mISDN_get_interface_info(dev->fid, &iinf); + fprintf(stdout, "l3 down own(%x) -> peer(%x)\n", + iinf.owner, iinf.peer); +#endif + return(0); +} + +int +shutdown_tdevice(tenovisdev_t *dev) +{ + int ret; + + pthread_mutex_lock(&dev->mutex); + if (dev->buf.p) { + if (dev->tlid) { + ret = mISDN_write_frame(dev->fid, dev->buf.p, + dev->tlid, MGR_DELLAYER | REQUEST, + 0, 0, NULL, TIMEOUT_1SEC); +#ifdef PRINTDEBUG + fprintf(stdout, "MGR_DELLAYER ret(%d)\n", ret); +#endif + ret = mISDN_read(dev->fid, dev->buf.p, 1024, + TIMEOUT_10SEC); +#ifdef PRINTDEBUG + fprintf(stdout, "mISDN_read ret(%d) pr(%x) di(%x) l(%d)\n", + ret, dev->buf.f->prim, dev->buf.f->dinfo, + dev->buf.f->len); +#endif + } + } + pthread_mutex_unlock(&dev->mutex); + return(0); +} + +int +intern_read(tenovisdev_t *dev) +{ + int ret; + +#ifdef PRINTDEBUG + fprintf(stdout, __FUNCTION__" addr(%x) prim(%x)\n", + dev->buf.f->addr, dev->buf.f->prim); +#endif + if (dev->buf.f->addr == (dev->tlid | IF_UP)) { + if (dev->buf.f->prim == (DL_ESTABLISH | REQUEST)) { + dev->Flags |= TN_FLG_L2_ACTIV; + ret = mISDN_write_frame(dev->fid, dev->buf.p, + dev->tlid | IF_UP, DL_ESTABLISH | CONFIRM, + 0, 0, NULL, TIMEOUT_1SEC); +#ifdef PRINTDEBUG + fprintf(stdout, __FUNCTION__": estab cnf ret(%d)\n", + ret); +#endif + } else if (dev->buf.f->prim == (DL_RELEASE | REQUEST)) { + dev->Flags &= ~TN_FLG_L2_ACTIV; + ret = mISDN_write_frame(dev->fid, dev->buf.p, + dev->tlid | IF_UP, DL_RELEASE | CONFIRM, + 0, 0, NULL, TIMEOUT_1SEC); +#ifdef PRINTDEBUG + fprintf(stdout, __FUNCTION__": rel cnf ret(%d)\n", + ret); +#endif + } + } + return(-2); +} + diff --git a/tenovis/testlib.c b/tenovis/testlib.c new file mode 100644 index 0000000..093df5a --- /dev/null +++ b/tenovis/testlib.c @@ -0,0 +1,277 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mISDNlib.h" +#include "lib/tenovis.h" +#include "l3dss1.h" + + +int tid; +int hid; +int dl3id; +int l2activ = 0; + +#define BUFFER_SIZE 2048 + +u_char buffer[BUFFER_SIZE]; +u_char msg[BUFFER_SIZE]; +iframe_t *frame; + + +int +init_lowdchannel(void) +{ + int ret; + stack_info_t *stinf; + layer_info_t linf; + interface_info_t iinf; + int dstid, dl2id; + + frame = (iframe_t *)buffer; + hid = mISDN_open(); + if (hid < 0) + return(-1); + + ret = mISDN_write_frame(hid, buffer, 0, + MGR_SETDEVOPT | REQUEST, FLG_mISDNPORT_ONEFRAME, + 0, NULL, TIMEOUT_1SEC); + fprintf(stdout, "MGR_SETDEVOPT ret(%d)\n", ret); + ret = mISDN_read(hid, buffer, BUFFER_SIZE, TIMEOUT_10SEC); + fprintf(stdout, "mISDN_read ret(%d) pr(%x) di(%x) l(%d)\n", + ret, frame->prim, frame->dinfo, frame->len); + ret = mISDN_get_stack_count(hid); + fprintf(stdout, "stackcnt %d\n", ret); + if (ret <= 0) { + mISDN_close(hid); + return(-1); + } + ret = mISDN_get_stack_info(hid, 1, buffer, BUFFER_SIZE); + stinf = (stack_info_t *)&frame->data.p; + mISDNprint_stack_info(stdout, stinf); + fprintf(stdout, "ext(%x) instcnt(%d) childcnt(%d)\n", + stinf->extentions, stinf->instcnt, stinf->childcnt); + dstid = stinf->id; + dl2id = mISDN_get_layerid(hid, dstid, 2); + fprintf(stdout, " dl2id = %08x\n", dl2id); + memset(&iinf, 0, sizeof(interface_info_t)); + iinf.owner = dl2id; + iinf.stat = IF_UP; + ret = mISDN_get_interface_info(hid, &iinf); + fprintf(stdout, "l2 up own(%x) -> peer(%x)\n", + iinf.owner, iinf.peer); + memset(&iinf, 0, sizeof(interface_info_t)); + iinf.owner = dl2id; + iinf.stat = IF_DOWN; + ret = mISDN_get_interface_info(hid, &iinf); + fprintf(stdout, "l2 down own(%x) -> peer(%x)\n", + iinf.owner, iinf.peer); + + memset(&linf, 0, sizeof(layer_info_t)); + strcpy(&linf.name[0], "tst L3"); + linf.object_id = -1; + linf.extentions = EXT_INST_MIDDLE; + linf.pid.protocol[stinf->instcnt] = ISDN_PID_ANY; + linf.pid.layermask = ISDN_LAYER(stinf->instcnt); + linf.st = dstid; + dl3id = mISDN_new_layer(hid, &linf); + fprintf(stdout, "mISDN_new_layer ret(%x)\n", dl3id); + + ret = mISDN_get_stack_info(hid, 1, buffer, BUFFER_SIZE); + stinf = (stack_info_t *)&frame->data.p; + mISDNprint_stack_info(stdout, stinf); + + memset(&iinf, 0, sizeof(interface_info_t)); + iinf.extentions = EXT_INST_MIDDLE; + iinf.owner = dl3id; + iinf.peer = dl2id; + iinf.stat = IF_DOWN; + ret = mISDN_write_frame(hid, buffer, dl3id, + MGR_SETIF | REQUEST, 0, sizeof(interface_info_t), + &iinf, TIMEOUT_1SEC); + fprintf(stdout, "mISDN_write_frame ret(%d)\n", ret); + ret = mISDN_read(hid, buffer, 1024, TIMEOUT_10SEC); + fprintf(stdout, "mISDN_read ret(%d) pr(%x) di(%x) l(%d)\n", + ret, frame->prim, frame->dinfo, + frame->len); + memset(&iinf, 0, sizeof(interface_info_t)); + iinf.owner = dl2id; + iinf.stat = IF_UP; + ret = mISDN_get_interface_info(hid, &iinf); + fprintf(stdout, "l2 up own(%x) -> peer(%x)\n", + iinf.owner, iinf.peer); + memset(&iinf, 0, sizeof(interface_info_t)); + iinf.owner = dl2id; + iinf.stat = IF_DOWN; + ret = mISDN_get_interface_info(hid, &iinf); + fprintf(stdout, "l2 down own(%x) -> peer(%x)\n", + iinf.owner, iinf.peer); + return(0); +} + + +int +close_lowdchannel(void) +{ + int ret; + + ret = mISDN_write_frame(hid, buffer, dl3id, MGR_DELLAYER | REQUEST, + 0, 0, NULL, TIMEOUT_1SEC); + fprintf(stdout, "MGR_DELLAYER ret(%d)\n", ret); + ret = mISDN_read(hid, buffer, 1024, TIMEOUT_10SEC); + fprintf(stdout, "mISDN_read ret(%d) pr(%x) di(%x) l(%d)\n", + ret, frame->prim, frame->dinfo, frame->len); + return(0); +} + +int +process_lowdchannel(int len) +{ + int ret; + + fprintf(stderr, "mISDN_read pr(%x) di(%x) l(%d)\n", + frame->prim, frame->dinfo, frame->len); + if (frame->prim == (DL_DATA | INDICATION)) { + ret = DL3write(tid, &frame->data.p, frame->len); + fprintf(stderr, "DL3write ret(%d)\n", ret); + } else if (frame->prim == (DL_UNITDATA | INDICATION)) { + ret = DL3write(tid, &frame->data.p, frame->len); + fprintf(stderr, "DL3write ret(%d)\n", ret); + } else if (frame->prim == (DL_ESTABLISH | CONFIRM)) { + fprintf(stderr, "estab cnf\n"); + l2activ = 1; + } else if (frame->prim == (DL_ESTABLISH | INDICATION)) { + fprintf(stderr, "estab ind\n"); + l2activ = 1; + } else if (frame->prim == (DL_RELEASE | CONFIRM)) { + fprintf(stderr, "release cnf\n"); + l2activ = 0; + } else if (frame->prim == (DL_RELEASE | INDICATION)) { + fprintf(stderr, "release ind\n"); + l2activ = 0; + } + return(0); +} + +int +send_lowdchannel(int len) +{ + int ret; + + if (!l2activ) { + ret = mISDN_write_frame(hid, buffer, dl3id | IF_DOWN, + DL_ESTABLISH | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + fprintf(stderr, "estab req ret(%d)\n", ret); + ret = mISDN_read(hid, buffer, 1024, TIMEOUT_10SEC); + fprintf(stderr, "estab read ret(%d)\n", ret); + if (ret >= 16) { + if (frame->prim == (DL_ESTABLISH | CONFIRM)) { + fprintf(stderr, "estab cnf\n"); + l2activ = 1; + } + if (frame->prim == (DL_ESTABLISH | INDICATION)) { + fprintf(stderr, "estab ind\n"); + l2activ = 1; + } + } + } + ret = mISDN_write_frame(hid, buffer, dl3id | IF_DOWN, + DL_DATA | REQUEST, 0, len, msg, TIMEOUT_1SEC); + return(ret); +} + + +static void +term_handler(int sig) +{ + int err; + + fprintf(stderr,"signal %d received\n", sig); + close_lowdchannel(); + err = DL3close(tid); + fprintf(stderr,"DL3close returns %d\n", err); + exit(0); +} + + +int main(argc,argv) +int argc; +char *argv[]; +{ + int ret, n; + int err; + fd_set in; + + fprintf(stderr,"%s\n", argv[0]); + tid = DL3open(); + fprintf(stderr,"DL3open returns %d\n", tid); + if (tid<0) { + fprintf(stderr,"DL3open error %s\n", strerror(errno)); + exit(1); + } + if (init_lowdchannel()) { + DL3close(tid); + exit(1); + } + signal(SIGTERM, term_handler); + signal(SIGINT, term_handler); + signal(SIGPIPE, term_handler); + while(1) { + FD_ZERO(&in); + n = fileno(stdin); + FD_SET(n, &in); + if (n +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mISDNlib.h" +#include "lib/tenovis.h" +#include "l3dss1.h" + +#define make_dss1_head(f, cr, mt) \ + *f++ = 8;\ + *f++ = 1;\ + *f++ = cr;\ + *f++ = mt + +int tid; +u_char callref; + +static void +term_handler(int sig) +{ + int err; + + fprintf(stderr,"signal %d received\n", sig); + err = DL3close(tid); + fprintf(stderr,"DL3close returns %d\n", err); + exit(0); +} + + +int +handle_msg(u_char *imsg, int len) +{ + u_char mt,cr; + u_char omsg[2048], *p; + int ret; + + cr = imsg[2]; + callref = cr; + mt = imsg[3]; + p = omsg; + switch(mt) { + case MT_SETUP: + make_dss1_head(p, (cr ^ 0x80), MT_CALL_PROCEEDING); + *p++ = IE_CHANNEL_ID; + *p++ = 3; + *p++ = 0xa1; + *p++ = 0x83; + *p++ = 0x05; + ret = DL3write(tid, omsg, p - omsg); + fprintf(stderr,"CALLP write ret %d\n", ret); + p = omsg; + make_dss1_head(p, (cr ^ 0x80), MT_ALERTING); + ret = DL3write(tid, omsg, p - omsg); + fprintf(stderr,"ALERT write ret %d\n", ret); + p = omsg; + make_dss1_head(p, (cr ^ 0x80), MT_CONNECT); + ret = DL3write(tid, omsg, p - omsg); + fprintf(stderr,"CONN write ret %d\n", ret); + break; + case MT_DISCONNECT: + make_dss1_head(p, (cr ^= 0x80), MT_RELEASE); + ret = DL3write(tid, omsg, p - omsg); + fprintf(stderr,"REL write ret %d\n", ret); + break; + case MT_RELEASE: + make_dss1_head(p, (cr ^= 0x80), MT_RELEASE_COMPLETE); + ret = DL3write(tid, omsg, p - omsg); + fprintf(stderr,"RELC write ret %d\n", ret); + break; + case MT_RELEASE_COMPLETE: + fprintf(stderr,"got RELC len(%d)\n", len); + break; + case MT_STATUS: + fprintf(stderr,"got STATUS cr(%x) len(%d)\n", + cr, len); + break; + default: + fprintf(stderr,"got mt(%x) cr(%x) len(%d)\n", + mt, cr, len); + break; + } + return(0); +} + +int main(argc,argv) +int argc; +char *argv[]; + +{ + u_char imsg[2048]; + u_char omsg[2048], *p; + int ret, n, loop = 1; + int err; + fd_set in; + + fprintf(stderr,"%s\n", argv[0]); + tid = DL3open(); + fprintf(stderr,"DL3open returns %d\n", tid); + if (tid<0) { + fprintf(stderr,"DL3open error %s\n", strerror(errno)); + exit(1); + } + signal(SIGTERM, term_handler); + signal(SIGINT, term_handler); + signal(SIGPIPE, term_handler); + while(loop) { + FD_ZERO(&in); + n = fileno(stdin); + FD_SET(n, &in); + if (n0) + handle_msg(imsg, ret); + } + } + err = DL3close(tid); + fprintf(stderr,"DL3close returns %d\n", err); + return(0); +} diff --git a/voip/Makefile b/voip/Makefile new file mode 100644 index 0000000..44c7ee3 --- /dev/null +++ b/voip/Makefile @@ -0,0 +1,83 @@ +# ifndef SF_DIR +# SF_DIR = /home/kkeil/speak_freely-7.2 +# endif + +mISDNLIB = $(mISDN_DIR)/lib/libmISDN.a +ISDNNETLIB = $(mISDN_DIR)/i4lnet/libisdnnet.a + +HLIBINCL = $(mISDN_DIR)/include/mISDNlib.h +INETINCL = $(mISDN_DIR)/include/isdn_net.h + +EXTRA_CFLAGS := +EXTRA_INCLUDE := +EXTRA_LIB := + +GSM_DIR := ../../gsm-1.0-pl6 + +ifdef GSM_DIR +EXTRA_CFLAGS += -DGSM_COMPRESSION +EXTRA_INCLUDE += -I$(GSM_DIR)/inc +EXTRA_LIB += $(GSM_DIR)/lib/libgsm.a +endif + +PROGRAMMS = voipisdn + +all: $(PROGRAMMS) + +INTERNET_PORT = 2074 + +CARGS = -DInternet_Port=$(INTERNET_PORT) + +CCFLAGS = -O3 -DLINUX -DM_LITTLE_ENDIAN + +LFLAGS = -lncurses -lm -lrt -lpthread + +DEBUG = -g -DHEXDUMP + +CFLAGS := $(CFLAGS) $(DEBUG) $(EXTRA_INCLUDE) \ + $(CARGS) $(DUPLEX) $(CCFLAGS) $(DOMAIN) $(EXTRA_CFLAGS) + +LEX = flex -8 + +VOIPISDNOBJ = voip_isdn.o rtpacket.o voip_timer.o \ + read_cfg.o voip_appl.o voip_isdn_app.o + +voipisdn: $(VOIPISDNOBJ) $(ISDNNETLIB) $(mISDNLIB) $(EXTRA_LIB) \ + $(HLIBINCL) $(INETINCL) \ + globals.h iapplication.h + $(CC) $(VOIPISDNOBJ) $(ISDNNETLIB) $(mISDNLIB) $(EXTRA_LIB) \ + $(LFLAGS) -o $@ + +rtpacket.o: rtpacket.c rtpacket.h \ + $(mISDN_DIR)/include/g711.h + +voip_timer.o: voip_timer.c vitimer.h + +voip_appl.o: voip_appl.c $(mISDN_DIR)/include/g711.h \ + globals.h rtpacket.h iapplication.h \ + $(HLIBINCL) $(INETINCL) + +voip_isdn_app.o: voip_isdn_app.c \ + globals.h rtpacket.h iapplication.h \ + $(HLIBINCL) $(INETINCL) + +voip_isdn.o: voip_isdn.c $(mISDN_DIR)/include/g711.h \ + globals.h rtpacket.h iapplication.h \ + $(HLIBINCL) $(INETINCL) + +cfg_lex.c: cfg.lex + $(LEX) cfg.lex + mv lex.yy.c cfg_lex.c + +read_cfg.o: read_cfg.c cfg_lex.c cfg.lex iapplication.h + +tstparse.o: tstparse.c + +tstparse: tstparse.o read_cfg.o + +clean: + rm -f *.o cfg_lex.c DEADJOE + find ./ -name '*~' -exec rm {} \; + +distclean: clean + rm -f *.a $(PROGRAMMS) tstparse diff --git a/voip/Makefile.org b/voip/Makefile.org new file mode 100644 index 0000000..5fe9849 --- /dev/null +++ b/voip/Makefile.org @@ -0,0 +1,83 @@ +# ifndef SF_DIR +# SF_DIR = /home/kkeil/speak_freely-7.2 +# endif + +HISAXLIB = $(HISAX_DIR)/lib/libhisax.a +ISDNNETLIB = $(HISAX_DIR)/i4lnet/libisdnnet.a + +HLIBINCL = $(HISAX_DIR)/include/hisaxlib.h +INETINCL = $(HISAX_DIR)/include/isdn_net.h + +EXTRA_CFLAGS := +EXTRA_INCLUDE := +EXTRA_LIB := + +GSM_DIR := ../../gsm-1.0-pl6 + +ifdef GSM_DIR +EXTRA_CFLAGS += -DGSM_COMPRESSION +EXTRA_INCLUDE += -I$(GSM_DIR)/inc +EXTRA_LIB += $(GSM_DIR)/lib/libgsm.a +endif + +PROGRAMMS = voipisdn + +all: $(PROGRAMMS) + +INTERNET_PORT = 2074 + +CARGS = -DInternet_Port=$(INTERNET_PORT) + +CCFLAGS = -O3 -DLINUX -DM_LITTLE_ENDIAN + +LFLAGS = -lncurses -lm -lrt -lpthread + +DEBUG = -g -DHEXDUMP + +CFLAGS := $(CFLAGS) $(DEBUG) $(EXTRA_INCLUDE) \ + $(CARGS) $(DUPLEX) $(CCFLAGS) $(DOMAIN) $(EXTRA_CFLAGS) + +LEX = flex -8 + +VOIPISDNOBJ = voip_isdn.o rtpacket.o voip_timer.o \ + read_cfg.o voip_appl.o voip_isdn_app.o + +voipisdn: $(VOIPISDNOBJ) $(ISDNNETLIB) $(HISAXLIB) $(EXTRA_LIB) \ + $(HLIBINCL) $(INETINCL) \ + globals.h iapplication.h + $(CC) $(VOIPISDNOBJ) $(ISDNNETLIB) $(HISAXLIB) $(EXTRA_LIB) \ + $(LFLAGS) -o $@ + +rtpacket.o: rtpacket.c rtpacket.h \ + $(HISAX_DIR)/include/g711.h + +voip_timer.o: voip_timer.c vitimer.h + +voip_appl.o: voip_appl.c $(HISAX_DIR)/include/g711.h \ + globals.h rtpacket.h iapplication.h \ + $(HLIBINCL) $(INETINCL) + +voip_isdn_app.o: voip_isdn_app.c \ + globals.h rtpacket.h iapplication.h \ + $(HLIBINCL) $(INETINCL) + +voip_isdn.o: voip_isdn.c $(HISAX_DIR)/include/g711.h \ + globals.h rtpacket.h iapplication.h \ + $(HLIBINCL) $(INETINCL) + +cfg_lex.c: cfg.lex + $(LEX) cfg.lex + mv lex.yy.c cfg_lex.c + +read_cfg.o: read_cfg.c cfg_lex.c cfg.lex iapplication.h + +tstparse.o: tstparse.c + +tstparse: tstparse.o read_cfg.o + +clean: + rm -f *.o cfg_lex.c DEADJOE + find ./ -name '*~' -exec rm {} \; + +distclean: clean + rm -f *.a $(PROGRAMMS) tstparse diff --git a/voip/cfg.lex b/voip/cfg.lex new file mode 100644 index 0000000..ef0f4b7 --- /dev/null +++ b/voip/cfg.lex @@ -0,0 +1,115 @@ +WSP [ \t] +NWSP [^ \t\n] +VCHR [A-Za-z_] +VCHRZ [A-Za-z_0-9] +VCHRX [A-Za-z\-\._0-9] +VCHRP [A-Za-z\-\.\/_0-9] +MSN [Mm][Ss][Nn] +AUDIONR [Aa][Uu][Dd][Ii][Oo][Nn][Rr] +VOIPNR [Vv][Oo][Ii][Pp][Nn][Rr] +DEBUG [Dd][Ee][Bb][Uu][Gg] +PORT [Pp][Oo][Rr][Tt] +GSM [Gg][Ss][Mm] +RECORD [Rr][Ee][Cc][Oo][Rr][Dd] +FILE [Ff][Ii][Ll][Ee] +PATH [Pp][Aa][Tt][Hh] +CTRL [Cc][Tt][Rr][Ll] +ZIF [0-9] +HZIF [0-9a-fA-F] +HEX 0[Xx]{HZIF}+ +NR {ZIF}+ +NAME {VCHRX}+ +PATHSTR {VCHRP}+ + +%START Normal Comment Number Name NumValue PathValue + +%% + int AktState=0; + ulong val=0; + nr_list_t *new_nr = NULL; + +{ +^#.* ; +{DEBUG}{WSP}+ { + BEGIN NumValue; + AktState = ST_DEB; + } +{RECORD}{CTRL}{FILE}{WSP}+ { + BEGIN PathValue; + AktState = ST_RCF; +} + +{RECORD}{FILE}{PATH}{WSP}+ { + BEGIN PathValue; + AktState = ST_RFP; +} + +{PORT}{WSP}+ { + BEGIN NumValue; + AktState = ST_PORT; + } +{MSN}{WSP}+ { + BEGIN Number; + AktState = ST_MSN; + new_nr = getnewnr(NR_TYPE_INTERN); + } +{AUDIONR}{WSP}+ { + BEGIN Number; + AktState = ST_AUDIO; + new_nr = getnewnr(NR_TYPE_AUDIO); + } +{VOIPNR}{WSP}+ { + BEGIN Number; + AktState = ST_VNR; + new_nr = getnewnr(NR_TYPE_VOIP); + } +{GSM}{WSP}* { + add_cfgflag(AktState, new_nr, FLAG_GSM); + } +{WSP}+ ; +[^ \t\n] { + yyless(0); + BEGIN Name; + } +\n { + new_nr = NULL; + } +} + +{ +{WSP}* ; +{NR} { + add_cfgnr(AktState, new_nr, yytext, yyleng); + BEGIN Normal; + } +} +{ +{WSP}* ; +{NAME} { + add_cfgname(AktState, new_nr, yytext, yyleng); + BEGIN Normal; + } +} +{ +{WSP}* ; +{HEX} { + val = strtol(yytext, NULL, 16); + add_cfgval(AktState, new_nr, val); + AktState = ST_NORM; + BEGIN Normal; + } +{NR} { + val = strtol(yytext, NULL, 0); + add_cfgval(AktState, new_nr, val); + AktState = ST_NORM; + BEGIN Normal; + } +} +{ +{WSP}* ; +{PATHSTR} { + add_path(AktState, yytext, yyleng); + AktState = ST_NORM; + BEGIN Normal; + } +} diff --git a/voip/example/pingi2.voip.cfg b/voip/example/pingi2.voip.cfg new file mode 100644 index 0000000..e050179 --- /dev/null +++ b/voip/example/pingi2.voip.cfg @@ -0,0 +1,36 @@ +# +# tstsetup voipisdn +# +# +# File to control recording of channels +# see rec_ctrl.sample +# +RecordCtrlFile /var/tmp/rec_ctrl + +# +# Path to diretory where record files are saved +# Record filenames are _.r (from phone) +# _.s (to phone) +# +RecordFilePath /var/tmp/ + +# +# MSN +# local number +# +MSN 12345 +MSN 888 +# +# VOIPNR [GSM] +# foreign numbers on hostname or address +# option: using of GSM +# +VOIPNR 4566 pictra_client GSM +VOIPNR 4568 pictra_client +VOIPNR 4567 10.23.200.1 +# +# AUDIONR +# number for calling the local soundcard +# +AUDIONR 789 + diff --git a/voip/example/rec_ctrl.sample b/voip/example/rec_ctrl.sample new file mode 100644 index 0000000..0cab4c4 --- /dev/null +++ b/voip/example/rec_ctrl.sample @@ -0,0 +1,7 @@ +# +# for each channel one line with a value: +# 0 don't record channel +# 1 record channel +# +1 +0 diff --git a/voip/globals.h b/voip/globals.h new file mode 100644 index 0000000..60b7580 --- /dev/null +++ b/voip/globals.h @@ -0,0 +1,19 @@ +#ifndef FLAG_GSM +#define FLAG_GSM 0x0020 +#endif + +extern int parse_cfg(char *, manager_t *); + +#ifdef INIT_GLOBALS + int global_debug = 0; + int rtp_port = Internet_Port; + int default_flags = 0; + char RecordCtrlFile[1024] = {0,}; + char RecordFilePath[1024] = {0,}; +#else +extern int global_debug; +extern int rtp_port; +extern int default_flags; +extern char RecordCtrlFile[1024]; +extern char RecordFilePath[1024]; +#endif diff --git a/voip/iapplication.h b/voip/iapplication.h new file mode 100644 index 0000000..db1b645 --- /dev/null +++ b/voip/iapplication.h @@ -0,0 +1,137 @@ +#ifndef IAPPLICATION_H +#define IAPPLICATION_H + +#include "vitimer.h" +#ifdef GSM_COMPRESSION +#include +#endif + +#define AP_MODE_IDLE 0 +#define AP_MODE_INTERN_CALL 1 +#define AP_MODE_AUDIO_CALL 2 +#define AP_MODE_VOIP_OCALL 3 +#define AP_MODE_VOIP_ICALL 4 + +#define AP_FLG_AUDIO_ACTIV 1 +#define AP_FLG_VOIP_ALERTING 2 +#define AP_FLG_VOIP_ACTIV 4 + +#define AP_FLG_VOIP_NEW_CONN 0x01000000 +#define AP_FLG_VOIP_PEER_VALID 0x02000000 +#define AP_FLG_VOIP_SENT_BYE 0x04000000 +#define AP_FLG_VOIP_PEER_BYE 0x08000000 +#define AP_FLG_VOIP_PEER_SF 0x10000000 + +#define AP_FLG_AUDIO_USED 0x00000100 +#define AP_FLG_VOIP_ABORT 0x80000000 + +#define AP_PR_VOIP_ISDN 1 +#define AP_PR_VOIP_NEW 2 +#define AP_PR_VOIP_SPEAKFREE 3 +#define AP_PR_VOIP_BYE 4 + +#define MAX_HOST_SIZE 64 +#define MAX_NETBUFFER_SIZE 8040 + +#define SLOW_TIMEOUT_s 10 +#define SLOW_TIMEOUT_us 0 +#define NORMAL_TIMEOUT_s 0 +#define NORMAL_TIMEOUT_us (320*125) + +#define SNDFLG_ULAW 0x00000001 +#define SNDFLG_ALAW 0x00000002 +#define SNDFLG_LINEAR16 0x00000004 +#define SNDFLG_COMPR_GSM 0x00000100 + +typedef struct _iapplication iapplication_t; +typedef struct _vapplication vapplication_t; +typedef struct _vconnection vconnection_t; + +struct _iapplication { + iapplication_t *prev; + iapplication_t *next; + manager_t *mgr; + vapplication_t *vapp; + void *data1; + void *data2; + vconnection_t *con; + void *para; + vi_timer_t timer; + pthread_t tid; + int Flags; + int mode; +}; + +struct _vapplication { + manager_t *mgr_lst; + char hostname[MAX_HOST_SIZE]; + unsigned int flags; + struct timeval tout; + int debug; + int port; + int dsock; + int csock; + struct sockaddr_in daddr; + struct sockaddr_in caddr; + struct sockaddr_in from; + int fromlen; + iapplication_t *iapp_lst; + int rlen; + union { + unsigned char d[MAX_NETBUFFER_SIZE]; + } buf; +}; + +struct _vconnection { + int sock; + struct sockaddr_in cpeer; + struct sockaddr_in dpeer; + char rmtname[256]; + char con_hostname[32]; + unsigned int own_ssrc; + unsigned int peer_ssrc; + unsigned int timestamp; + unsigned short seq; + unsigned short lastseq; + unsigned char oc; + unsigned char pc; + msg_queue_t aqueue; + msg_t *amsg; + int rlen; + unsigned char *rbuf; + unsigned int sndflags; + int pkt_size; + int slen; +#ifdef GSM_COMPRESSION + gsm r_gsm; + gsm s_gsm; +#endif + unsigned char sbuf[1024]; + unsigned char dbuf[1152]; + unsigned char cbuf[1024]; +}; + +extern pthread_t run_voip(vapplication_t *v); +extern void *voip_sender(void *arg); + +extern void clear_connection(iapplication_t *); +extern void free_application(iapplication_t *); +extern unsigned long getnew_ssrc(vapplication_t *); +extern iapplication_t *new_application(vapplication_t *); +extern vconnection_t *new_connection(iapplication_t *, struct in_addr *); +extern int SendCtrl(iapplication_t *); + +extern int voip_application_handler(iapplication_t *, int, + unsigned char *); +extern int setup_voip(iapplication_t *, bchannel_t *); +extern int close_voip(iapplication_t *, bchannel_t *); + +extern int setup_voip_ocall(iapplication_t *, bchannel_t *); +extern int alert_voip(iapplication_t *, bchannel_t *); +extern int facility_voip(iapplication_t *, bchannel_t *); +extern int useruser_voip(iapplication_t *, bchannel_t *); +extern int connect_voip(iapplication_t *, bchannel_t *); +extern int disconnect_voip(iapplication_t *, bchannel_t *); +extern int release_voip(iapplication_t *, bchannel_t *); + +#endif diff --git a/voip/options.h b/voip/options.h new file mode 100644 index 0000000..78f030f --- /dev/null +++ b/voip/options.h @@ -0,0 +1,13 @@ + + int debugforce; + int whichport; + int jitter; + int jitteridlet; + int record; + int hexdump; + int blowfish_spec; + BF_KEY blowfishkey; + char ideakey[17]; + char deskey[9]; + char *curotp; + char *pgppass; diff --git a/voip/prep.conf b/voip/prep.conf new file mode 100644 index 0000000..4659ff8 --- /dev/null +++ b/voip/prep.conf @@ -0,0 +1,9 @@ +#undef PUSH_TO_TALK +#undef USE_CURSES +#undef AUDIO_DEVICE_FILE +#undef HALF_DUPLEX +#undef HEWLETT_PACKARD +#define UNIX5 +#undef UNIX420 +#undef Solaris +#undef sgi diff --git a/voip/read_cfg.c b/voip/read_cfg.c new file mode 100644 index 0000000..aac9956 --- /dev/null +++ b/voip/read_cfg.c @@ -0,0 +1,143 @@ +#include +#include + +#include "isdn_net.h" +#include "helper.h" +#define INIT_GLOBALS +#include "globals.h" + +static manager_t *akt_mgr; + +int add_cfgnr(int state, nr_list_t *nr, char *t, int l); +int add_cfgname(int state, nr_list_t *nr, char *t, int l); +int add_cfgval(int state, nr_list_t *nr, ulong val); +nr_list_t *getnewnr(int typ); +int add_cfgflag(int state, nr_list_t *nr, int flag); +int add_path(int state, char *t, int l); + +enum { + ST_NORM, + ST_MSN, + ST_VNR, + ST_AUDIO, + ST_DEB, + ST_PORT, + ST_RFP, + ST_RCF, +}; + +#include "cfg_lex.c" + +int yywrap(void) +{ + return(1); +} + +int parse_cfg(char *FName, manager_t *mgr) { + int ret; + + yyin = fopen(FName, "r"); + if (!yyin) { + fprintf(stderr,"cannot open cfg file %s\n", FName); + return(1); + } else + fprintf(stderr,"parsing cfg file %s\n", FName); + akt_mgr = mgr; + BEGIN Normal; + ret = yylex(); + fclose(yyin); + return(ret); +} + +nr_list_t * +getnewnr(int typ) +{ + nr_list_t *nr; + + nr = malloc(sizeof(nr_list_t)); + memset(nr, 0, sizeof(nr_list_t)); + nr->typ = typ; + return(nr); +} + +int +add_cfgnr(int state, nr_list_t *nr, char *t, int l) +{ +// printf("%s(%d,%p,%s)\n", __FUNCTION__, state, nr, t); + if (nr) { + switch(state) { + default: + strcpy(nr->nr, t); + nr->len = l; + APPEND_TO_LIST(nr, akt_mgr->nrlist); + break; + } + } + return(0); +} + +int +add_cfgname(int state, nr_list_t *nr, char *t, int l) +{ +// printf("%s(%d,%p,%s)\n", __FUNCTION__, state, nr, t); + if (nr) { + switch(state) { + default: + strcpy(nr->name, t); + break; + } + } + return(0); +} + +int +add_cfgval(int state, nr_list_t *nr, ulong val) +{ + if (nr) { + } else { + switch(state) { + case ST_DEB: + global_debug = val; + break; + case ST_PORT: + rtp_port = val; + break; + } + } + return(0); +} + +int +add_cfgflag(int state, nr_list_t *nr, int flag) +{ + if (nr) { + nr->flags ^= flag; + } else { + default_flags ^= flag; + } + return(0); +} + +int +add_path(int state, char *t, int l) +{ +// printf("%s(%d,%s)\n", __FUNCTION__, state, t); + if (l<1) + return(0); + switch(state) { + default: + fprintf(stderr, "%s: Unknown state(%d) text(%s)\n", __FUNCTION__, + state, t); + case ST_RCF: + strcpy(RecordCtrlFile, t); + break; + case ST_RFP: + strcpy(RecordFilePath, t); + if (RecordFilePath[l-1] != '/') { + RecordFilePath[l] = '/'; + RecordFilePath[l+1] = 0; + } + break; + } + return(0); +} diff --git a/voip/rtp.h b/voip/rtp.h new file mode 100644 index 0000000..618b8a8 --- /dev/null +++ b/voip/rtp.h @@ -0,0 +1,133 @@ +/* +* rtp.h -- RTP header file +* RTP draft: November 1994 version +* +* $Id: rtp.h,v 0.9 2003/08/27 07:33:03 kkeil Exp $ +*/ + +#define RTP_SEQ_MOD (1<<16) +#define RTP_TS_MOD (0xffffffff) +/* +* Current type value. +*/ +#define RTP_VERSION 2 + +#define RTP_MAX_SDES 256 /* maximum text length for SDES */ + +typedef enum { + RTCP_SR = 200, + RTCP_RR = 201, + RTCP_SDES = 202, + RTCP_BYE = 203, + RTCP_APP = 204 +} rtcp_type_t; + +typedef enum { + RTCP_SDES_END = 0, + RTCP_SDES_CNAME = 1, + RTCP_SDES_NAME = 2, + RTCP_SDES_EMAIL = 3, + RTCP_SDES_PHONE = 4, + RTCP_SDES_LOC = 5, + RTCP_SDES_TOOL = 6, + RTCP_SDES_NOTE = 7, + RTCP_SDES_PRIV = 8, + RTCP_SDES_IMG = 9, + RTCP_SDES_DOOR = 10, + RTCP_SDES_SOURCE = 11 +} rtcp_sdes_type_t; + +typedef enum { + AE_PCMU, + AE_1016, + AE_G721, + AE_GSM, + AE_G723, + AE_IDVI, + AE_LPC, + AE_PCMA, + AE_L16, + AE_G728, + AE_MAX +} audio_encoding_t; + +typedef struct { + audio_encoding_t encoding; /* type of encoding (differs) */ + unsigned sample_rate; /* sample frames per second */ + unsigned channels; /* number of interleaved channels */ +} audio_descr_t; + +typedef struct { + unsigned int version:2; /* protocol version */ + unsigned int p:1; /* padding flag */ + unsigned int x:1; /* header extension flag */ + unsigned int cc:4; /* CSRC count */ + unsigned int m:1; /* marker bit */ + unsigned int pt:7; /* payload type */ + u_int16_t seq; /* sequence number */ + u_int32_t ts; /* timestamp */ + u_int32_t ssrc; /* synchronization source */ + u_int32_t csrc[1]; /* optional CSRC list */ +} rtp_hdr_t; + +typedef struct { + unsigned int version:2; /* protocol version */ + unsigned int p:1; /* padding flag */ + unsigned int count:5; /* varies by payload type */ + unsigned int pt:8; /* payload type */ + u_int16_t length; /* packet length in words, without this word */ +} rtcp_common_t; + +/* reception report */ +typedef struct { + u_int32_t ssrc; /* data source being reported */ + unsigned int fraction:8; /* fraction lost since last SR/RR */ + int lost:24; /* cumulative number of packets lost (signed!) */ + u_int32_t last_seq; /* extended last sequence number received */ + u_int32_t jitter; /* interarrival jitter */ + u_int32_t lsr; /* last SR packet from this source */ + u_int32_t dlsr; /* delay since last SR packet */ +} rtcp_rr_t; + +typedef struct { + u_int8_t type; /* type of SDES item (rtcp_sdes_type_t) */ + u_int8_t length; /* length of SDES item (in octets) */ + char data[1]; /* text, not zero-terminated */ +} rtcp_sdes_item_t; + +/* one RTCP packet */ +typedef struct { + rtcp_common_t common; /* common header */ + union { + /* sender report (SR) */ + struct { + u_int32_t ssrc; /* source this RTCP packet refers to */ + u_int32_t ntp_sec; /* NTP timestamp */ + u_int32_t ntp_frac; + u_int32_t rtp_ts; /* RTP timestamp */ + u_int32_t psent; /* packets sent */ + u_int32_t osent; /* octets sent */ + /* variable-length list */ + rtcp_rr_t rr[1]; + } sr; + + /* reception report (RR) */ + struct { + u_int32_t ssrc; /* source this generating this report */ + /* variable-length list */ + rtcp_rr_t rr[1]; + } rr; + + /* BYE */ + struct { + u_int32_t src[1]; /* list of sources */ + /* can't express trailing text */ + } bye; + + /* source description (SDES) */ + struct rtcp_sdes_t { + u_int32_t src; /* first SSRC/CSRC */ + rtcp_sdes_item_t item[1]; /* list of SDES items */ + } sdes; + } r; +} rtcp_t; diff --git a/voip/rtpacket.c b/voip/rtpacket.c new file mode 100644 index 0000000..c8bc655 --- /dev/null +++ b/voip/rtpacket.c @@ -0,0 +1,858 @@ +/* + + RTP input packet construction and parsing + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "g711.h" +#include "rtpacket.h" + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +audio_descr_t adt[] = { +/* enc sample ch */ + {AE_PCMU, 8000, 1}, /* 0 PCMU */ + {AE_MAX, 8000, 1}, /* 1 1016 */ + {AE_G721, 8000, 1}, /* 2 G721 */ + {AE_GSM, 8000, 1}, /* 3 GSM */ + {AE_G723, 8000, 1}, /* 4 Unassigned */ + {AE_IDVI, 8000, 1}, /* 5 DVI4 */ + {AE_IDVI, 16000, 1}, /* 6 DVI4 */ + {AE_LPC, 8000, 1}, /* 7 LPC */ + {AE_PCMA, 8000, 1}, /* 8 PCMA */ + {AE_MAX, 0, 1}, /* 9 G722 */ + {AE_L16, 44100, 2}, /* 10 L16 */ + {AE_L16, 44100, 1}, /* 11 L16 */ + {AE_MAX, 0, 1}, /* 12 */ +}; + +#define MAX_MISORDER 100 +#define MAX_DROPOUT 3000 + +/* ISRTP -- Determine if a packet is RTP or not. If so, convert + in place into a sound buffer. */ + +int isrtp(pkt, len) + unsigned char *pkt; + int len; +{ +#ifdef RationalWorld + rtp_hdr_t *rh = (rtp_hdr_t *) pkt; +#endif + + unsigned int r_version, r_p, r_x, r_cc, r_m, r_pt, + r_seq, r_ts; + + /* Tear apart the header in a byte- and bit field-order + independent fashion. */ + + r_version = (pkt[0] >> 6) & 3; + r_p = !!(pkt[0] & 0x20); + r_x = !!(pkt[0] & 0x10); + r_cc = pkt[0] & 0xF; + r_m = !!(pkt[1] & 0x80); + r_pt = pkt[1] & 0x1F; + r_seq = ntohs(*((short *) (pkt + 2))); + r_ts = ntohl(*((long *) (pkt + 4))); + + if ( +#ifdef RationalWorld + rh->version == RTP_VERSION && /* Version ID correct */ + rh->pt < ELEMENTS(adt) && /* Payload type credible */ + adt[rh->pt].sample_rate != 0 && /* Defined payload type */ + /* Padding, if present, is plausible */ + (!rh->p || (pkt[len - 1] < (len - (12 + 4 * rh->cc)))) +#else + r_version == RTP_VERSION && /* Version ID correct */ + r_pt < ELEMENTS(adt) && /* Payload type credible */ + adt[r_pt].sample_rate != 0 && /* Defined payload type */ + /* Padding, if present, is plausible */ + (!r_p || (pkt[len - 1] < (len - (12 + 4 * r_cc)))) +#endif + ) { + unsigned char *payload; + int lex, paylen; + + /* Length of fixed header extension, if any */ + lex = r_x ? (ntohs(*((short *) (pkt + 2 + 12 + 4 * r_cc))) + 1) * 4 : 0; + payload = pkt + (12 + 4 * r_cc) + lex; /* Start of payload */ + paylen = len - ((12 + 4 * r_cc) + /* Length of payload */ + lex + (r_p ? pkt[len - 1] : 0)); + return TRUE; + } + return FALSE; +} + +/* ISVALIDRTCPPACKET -- Consistency check a packet to see if + is a compliant RTCP packet. Note that + since this must also accept Speak Freely + SDES packets, the test on the protocol is + not as tight as were it exclusively for + RTP. */ + +int isValidRTCPpacket(p, len) + unsigned char *p; + int len; +{ + unsigned char *end; + + if (((((p[0] >> 6) & 3) != RTP_VERSION) && /* Version incorrect ? */ + ((((p[0] >> 6) & 3) != 1))) || /* Allow Speak Freely too */ + ((p[0] & 0x20) != 0) || /* Padding in first packet ? */ + ((p[1] != RTCP_SR) && (p[1] != RTCP_RR))) { /* First item not SR or RR ? */ + return FALSE; + } + end = p + len; + + do { + /* Advance to next subpacket */ + p += (ntohs(*((short *) (p + 2))) + 1) * 4; + } while (p < end && (((p[0] >> 6) & 3) == RTP_VERSION)); + + return p == end; +} + +/* ISRTCPBYEPACKET -- Test if this RTCP packet contains a BYE. */ + +int isRTCPByepacket(p, len) + unsigned char *p; + int len; +{ + unsigned char *end; + int sawbye = FALSE; + /* Version incorrect ? */ + if ((((p[0] >> 6) & 3) != RTP_VERSION && ((p[0] >> 6) & 3) != 1) || + ((p[0] & 0x20) != 0) || /* Padding in first packet ? */ + ((p[1] != RTCP_SR) && (p[1] != RTCP_RR))) { /* First item not SR or RR ? */ + return FALSE; + } + end = p + len; + + do { + if (p[1] == RTCP_BYE) { + sawbye = TRUE; + } + /* Advance to next subpacket */ + p += (ntohs(*((short *) (p + 2))) + 1) * 4; + } while (p < end && (((p[0] >> 6) & 3) == RTP_VERSION)); + + return (p == end) && sawbye; +} + +/* ISRTCPAPPPACKET -- Test if this RTCP packet contains a APP item + with a given name. If so, returns a pointer + to the APP sub-packet in app_ptr. */ + +int isRTCPAPPpacket(p, len, name, app_ptr) + unsigned char *p; + int len; + char *name; + unsigned char **app_ptr; +{ + unsigned char *end; + + *app_ptr = NULL; + /* Version incorrect ? */ + if ((((p[0] >> 6) & 3) != RTP_VERSION && ((p[0] >> 6) & 3) != 1) || + ((p[0] & 0x20) != 0) || /* Padding in first packet ? */ + ((p[1] != RTCP_SR) && (p[1] != RTCP_RR))) { /* First item not SR or RR ? */ + return FALSE; + } + end = p + len; + + do { + if ((p[1] == RTCP_APP) && (memcmp(p + 8, name, 4) == 0)) { + *app_ptr = p; + return TRUE; + } + /* Advance to next subpacket */ + p += (ntohs(*((short *) (p + 2))) + 1) * 4; + } while (p < end && (((p[0] >> 6) & 3) == RTP_VERSION)); + + return FALSE; +} + +#if 0 +/* RTP_MAKE_SDES -- Generate a source description for this + user, based either on information obtained + from the password file or supplied by + environment variables. Strict construction + of the RTP specification requires every + SDES packet to be a composite which begins + with a sender or receiver report. If the + "strict" argument is true, we'll comply with + this. Unfortunately, Look Who's Listening + Server code was not aware of this little + twist when originally implemented, so it will + take some time to transition all the running + servers to composite packet aware code. */ + +int rtp_make_sdes(pkt, ssrc_i, port, strict) + char **pkt; + unsigned long ssrc_i; + int port, strict; +{ + unsigned char zp[1500]; + unsigned char *p = zp; + rtcp_t *rp; + unsigned char *ap; + char *sp, *ep; + int l, hl; + struct passwd *pw; + char s[256], ev[1024]; + +#define addSDES(item, text) *ap++ = item; *ap++ = l = strlen(text); \ + bcopy(text, ap, l); ap += l + + hl = 0; + if (strict) { + *p++ = RTP_VERSION << 6; + *p++ = RTCP_RR; + *p++ = 0; + *p++ = 1; + *((long *) p) = htonl(ssrc_i); + p += 4; + hl = 8; + } + + rp = (rtcp_t *) p; +#ifdef RationalWorld + rp->common.version = RTP_VERSION; + rp->common.p = 0; + rp->common.count = 1; + rp->common.pt = RTCP_SDES; +#else + *((short *) p) = htons((RTP_VERSION << 14) | RTCP_SDES | (1 << 8)); +#endif + rp->r.sdes.src = htonl(ssrc_i); + + ap = (unsigned char *) rp->r.sdes.item; + + ep = getenv("SPEAKFREE_ID"); + if (ep != NULL) { + if (strlen(ep) == 0) { + ep = NULL; + } else { + strcpy(ev, ep); + ep = ev; + } + } + + /* Build canonical name for this user. This is generally + a name which can be used for "talk" and "finger", and + is not necessarily the user's E-mail address. */ + + if ((sp = getenv("SPEAKFREE_CNAME")) != NULL && strlen(sp) > 0) { + /* If strict, drop leading asterisk that's used to request an + unlisted entry on an LWL server. */ + if (strict && sp[0] == '*') { + sp++; + } + addSDES(RTCP_SDES_CNAME, sp); + } else { + pw = getpwuid(getuid()); + if (pw != NULL) { + char dn[64]; + char hn[MAXHOSTNAMELEN]; + + dn[0] = hn[0] = 0; + getdomainname(dn, sizeof dn); + gethostname(hn, sizeof hn); + if (dn[0] != 0) { + sprintf(s, "%s@%s", pw->pw_name, dn); + } else { + struct hostent *h; + struct in_addr naddr; + + h = gethostbyname(hn); + if (strchr(h->h_name, '.') != NULL) { + sprintf(s, "%s@%s", pw->pw_name, h->h_name); + } else { + bcopy(h->h_addr, &naddr, sizeof naddr); + sprintf(s, "%s@%s", pw->pw_name, inet_ntoa(naddr)); + } + } + addSDES(RTCP_SDES_CNAME, s); + if (ep == NULL && pw->pw_gecos != NULL) { + char *gc = strchr(pw->pw_gecos, ','); + + if (gc != NULL) { + *gc = 0; + } + addSDES(RTCP_SDES_NAME, pw->pw_gecos); + } + } else { +#ifdef Solaris + { char s[12]; + sysinfo(SI_HW_SERIAL, s, 12); + sprintf(s, "Unknown@%s.hostid.net", s); + } +#else + sprintf(s, "Unknown@%lu.hostid.net", gethostid()); +#endif + addSDES(RTCP_SDES_CNAME, s); + } + } + + /* If a SPEAKFREE_ID environment variable is present, + parse the items it contains. Format: + + SPEAKFREE_ID=::: + + */ + + if (ep != NULL) { + int i; + static int items[] = { RTCP_SDES_NAME, RTCP_SDES_EMAIL, + RTCP_SDES_PHONE, RTCP_SDES_LOC }; + char *np; + + for (i = 0; i < ELEMENTS(items); i++) { + while (*ep && isspace(*ep)) { + ep++; + } + if (*ep == 0) { + break; + } + if ((np = strchr(ep, ':')) != NULL) { + *np++ = 0; + } else { + np = NULL; + } + if (strlen(ep) > 0) { + /* If strict, drop leading asterisk that's used to request an + unlisted entry on an LWL server. */ + if (strict && items[i] == RTCP_SDES_EMAIL && ep[0] == '*') { + ep++; + } + addSDES(items[i], ep); + } + if (np == NULL) { + break; + } + ep = np; + } + } + + addSDES(RTCP_SDES_TOOL, "Speak Freely for Unix"); + + if (!strict) { + + /* If a port number is specified, add a PRIV item indicating + the port we're communicating on. */ + + if (port >= 0) { + char s[20]; + + sprintf(s, "\001P%d", port); + addSDES(RTCP_SDES_PRIV, s); + } + } + + *ap++ = RTCP_SDES_END; + *ap++ = 0; + + l = ap - p; + + rp->common.length = htons(((l + 3) / 4) - 1); + l = hl + ((ntohs(rp->common.length) + 1) * 4); + + /* Okay, if the total length of this packet is not an odd + multiple of 4 bytes, we're going to put a pad at the + end of it. Why? Because we may encrypt the packet + later and that requires it be a multiple of 8 bytes, + and we don't want the encryption code to have to + know all about our weird composite packet structure. + Oh yes, there's no reason to do this if strict isn't + set, since we never encrypt packets sent to a Look + Who's Listening server. + + Why an odd multiple of 4 bytes, I head you ask? + Because when we encrypt an RTCP packet, we're required + to prefix it with four random bytes to deter a known + plaintext attack, and since the total buffer we + encrypt, including the random bytes, has to be a + multiple of 8 bytes, the message needs to be an odd + multiple of 4. */ + + if (strict) { + int pl = (l & 4) ? l : l + 4; + + if (pl > l) { + int pad = pl - l; + + bzero(zp + l, pad); /* Clear pad area to zero */ + zp[pl - 1] = pad; /* Put pad byte count at end of packet */ + p[0] |= 0x20; /* Set the "P" bit in the header of the + SDES (last in message) packet */ + /* If we've added an additional word to + the packet, adjust the length in the + SDES message, which must include the + pad */ + rp->common.length = htons(ntohs(rp->common.length) + ((pad) / 4)); + l = pl; /* Include pad in length of packet */ + } + } + + *pkt = (char *) malloc(l); + if (*pkt != NULL) { + bcopy(zp, *pkt, l); + return l; + } + return 0; +} + +/* RTP_MAKE_SDES_S-- Generate a source description for this + servers to composite packet aware code. */ + +int rtp_make_sdes_s(pkt, strict, r) + char **pkt; + struct rtcp_sdes_request *r; + int strict; +{ + unsigned char zp[1500]; + unsigned char *p = zp; + unsigned long *ssrc; + rtcp_t *rp; + unsigned char *ap; + int l, hl, i; + +#define addSDES(item, text) *ap++ = item; *ap++ = l = strlen(text); \ + bcopy(text, ap, l); ap += l + + ssrc = (unsigned long *)r->ssrc; + hl = 0; + if (strict) { + *p++ = RTP_VERSION << 6; + *p++ = RTCP_RR; + *p++ = 0; + *p++ = 1; + *((long *) p) = htonl(*ssrc); + p += 4; + hl = 8; + } + + rp = (rtcp_t *) p; +#ifdef RationalWorld + rp->common.version = RTP_VERSION; + rp->common.p = 0; + rp->common.count = 1; + rp->common.pt = RTCP_SDES; +#else + *((short *) p) = htons((RTP_VERSION << 14) | RTCP_SDES | (1 << 8)); +#endif + rp->r.sdes.src = htonl(*ssrc); + + ap = (unsigned char *) rp->r.sdes.item; + + for (i = 0; i < r->nitems; i++) { + addSDES(r->item[i].r_item, r->item[i].r_text); + } + + *ap++ = RTCP_SDES_END; + *ap++ = 0; + + l = ap - p; + + rp->common.length = htons(((l + 3) / 4) - 1); + l = hl + ((ntohs(rp->common.length) + 1) * 4); + + /* Okay, if the total length of this packet is not an odd + multiple of 4 bytes, we're going to put a pad at the + end of it. Why? Because we may encrypt the packet + later and that requires it be a multiple of 8 bytes, + and we don't want the encryption code to have to + know all about our weird composite packet structure. + Oh yes, there's no reason to do this if strict isn't + set, since we never encrypt packets sent to a Look + Who's Listening server. + + Why an odd multiple of 4 bytes, I head you ask? + Because when we encrypt an RTCP packet, we're required + to prefix it with four random bytes to deter a known + plaintext attack, and since the total buffer we + encrypt, including the random bytes, has to be a + multiple of 8 bytes, the message needs to be an odd + multiple of 4. */ + + if (strict) { + int pl = (l & 4) ? l : l + 4; + + if (pl > l) { + int pad = pl - l; + + bzero(zp + l, pad); /* Clear pad area to zero */ + zp[pl - 1] = pad; /* Put pad byte count at end of packet */ + p[0] |= 0x20; /* Set the "P" bit in the header of the + SDES (last in message) packet */ + /* If we've added an additional word to + the packet, adjust the length in the + SDES message, which must include the + pad */ + rp->common.length = htons(ntohs(rp->common.length) + ((pad) / 4)); + l = pl; /* Include pad in length of packet */ + } + } + + if (*pkt == NULL) + *pkt = (char *) malloc(l); + if (*pkt != NULL) { + bcopy(zp, *pkt, l); + return l; + } + return 0; +} + +#endif + +/* RTP_MAKE_BYE -- Create a "BYE" RTCP packet for this connection. */ + +int rtp_make_bye(p, ssrc_i, raison, strict) + unsigned char *p; + unsigned long ssrc_i; + char *raison; + int strict; +{ + rtcp_t *rp; + unsigned char *ap, *zp; + int l, hl; + + /* If requested, prefix the packet with a null receiver + report. This is required by the RTP spec, but is not + required in packets sent only to the Look Who's Listening + server. */ + + zp = p; + hl = 0; + if (strict) { + *p++ = RTP_VERSION << 6; + *p++ = RTCP_RR; + *p++ = 0; + *p++ = 1; + *((long *) p) = htonl(ssrc_i); + p += 4; + hl = 8; + } + + rp = (rtcp_t *) p; +#ifdef RationalWorld + rp->common.version = RTP_VERSION; + rp->common.p = 0; + rp->common.count = 1; + rp->common.pt = RTCP_BYE; +#else + *((short *) p) = htons((RTP_VERSION << 14) | RTCP_BYE | (1 << 8)); +#endif + rp->r.bye.src[0] = htonl(ssrc_i); + + ap = (unsigned char *) rp->r.sdes.item; + + l = 0; + if (raison != NULL) { + l = strlen(raison); + if (l > 0) { + *ap++ = l; + bcopy(raison, ap, l); + ap += l; + } + } + + while ((ap - p) & 3) { + *ap++ = 0; + } + l = ap - p; + + rp->common.length = htons((l / 4) - 1); + + l = hl + ((ntohs(rp->common.length) + 1) * 4); + + /* If strict, pad the composite packet to an odd multiple of 4 + bytes so that if we decide to encrypt it we don't have to worry + about padding at that point. */ + + if (strict) { + int pl = (l & 4) ? l : l + 4; + + if (pl > l) { + int pad = pl - l; + + bzero(zp + l, pad); /* Clear pad area to zero */ + zp[pl - 1] = pad; /* Put pad byte count at end of packet */ + p[0] |= 0x20; /* Set the "P" bit in the header of the + SDES (last in message) packet */ + /* If we've added an additional word to + the packet, adjust the length in the + SDES message, which must include the + pad */ + rp->common.length = htons(ntohs(rp->common.length) + ((pad) / 4)); + l = pl; /* Include pad in length of packet */ + } + } + + return l; +} + +/* RTP_MAKE_APP -- Create a "APP" (application-defined) RTCP packet + for this connection with the given type (name) + and content. */ + +int rtp_make_app(p, ssrc_i, strict, type, content, len) + unsigned char *p, *content; + unsigned long ssrc_i; + int strict, len; + char *type; +{ + rtcp_t *rp; + unsigned char *ap, *zp; + int l, hl; + + /* If requested, prefix the packet with a null receiver + report. This is required by the RTP spec, but is not + required in packets sent only to other copies of Speak + Freely. */ + + zp = p; + hl = 0; + if (strict) { + *p++ = RTP_VERSION << 6; + *p++ = RTCP_RR; + *p++ = 0; + *p++ = 1; + *((long *) p) = htonl(ssrc_i); + p += 4; + hl = 8; + } + + rp = (rtcp_t *) p; + *((short *) p) = htons((RTP_VERSION << 14) | RTCP_APP | (1 << 8)); + rp->r.bye.src[0] = htonl(ssrc_i); + + ap = p + 8; + bcopy(type, ap, 4); + ap += 4; + + bcopy(content, ap, len); + ap += len; + + while ((ap - p) & 3) { + *ap++ = 0; + } + l = ap - p; + + rp->common.length = htons((l / 4) - 1); + + l = hl + ((ntohs(rp->common.length) + 1) * 4); + + /* If strict, pad the composite packet to an odd multiple of 4 + bytes so that if we decide to encrypt it we don't have to worry + about padding at that point. */ + + if (strict) { + int pl = (l & 4) ? l : l + 4; + + if (pl > l) { + int pad = pl - l; + + bzero(zp + l, pad); /* Clear pad area to zero */ + zp[pl - 1] = pad; /* Put pad byte count at end of packet */ + p[0] |= 0x20; /* Set the "P" bit in the header of the + SDES (last in message) packet */ + /* If we've added an additional word to + the packet, adjust the length in the + SDES message, which must include the + pad */ + rp->common.length = htons(ntohs(rp->common.length) + ((pad) / 4)); + l = pl; /* Include pad in length of packet */ + } + } + + return l; +} + +#if 0 +/* RTPOUT -- Convert a sound buffer into an RTP packet, given the + SSRC, timestamp, and sequence number appropriate for the + next packet sent to this connection. */ + +int rtpout(sb, ssrc_i, timestamp_i, seq_i, spurt) + soundbuf *sb; + unsigned long ssrc_i, timestamp_i; + unsigned short seq_i; + int spurt; +{ + soundbuf rp; + rtp_hdr_t *rh = (rtp_hdr_t *) &rp; + int pl = 0; + +#ifdef RationalWorld + rh->version = RTP_VERSION; + rh->p = 0; + rh->x = 0; + rh->cc = 0; + rh->m = !!spurt; +#else + *((short *) rh) = htons((RTP_VERSION << 14) | (spurt ? 0x80 : 0)); +#endif + rh->seq = htons(seq_i); + rh->ts = htonl(timestamp_i); + rh->ssrc = htonl(ssrc_i); + + /* GSM */ + + if (sb->compression & fCompGSM) { +#ifdef RationalWorld + rh->pt = 3; +#else + ((char *) rh)[1] = 3; +#endif + bcopy(sb->buffer.buffer_val + 2, ((char *) &rp) + 12, + (int) sb->buffer.buffer_len - 2); + pl = (sb->buffer.buffer_len - 2) + 12; + + /* ADPCM */ + + } else if (sb->compression & fCompADPCM) { +#ifdef RationalWorld + rh->pt = 5; +#else + ((char *) rh)[1] = 5; +#endif + bcopy(sb->buffer.buffer_val, ((char *) &rp) + 12 + 4, + (int) sb->buffer.buffer_len - 3); + bcopy(sb->buffer.buffer_val + ((int) sb->buffer.buffer_len - 3), + ((char *) &rp) + 12, 3); + ((char *) &rp)[15] = 0; + pl = (sb->buffer.buffer_len + 1) + 12; + + /* LPC */ + + } else if (sb->compression & fCompLPC) { + int i, n = (sb->buffer.buffer_len - 2) / 14; + char *ip = (char *) (sb->buffer.buffer_val + 2), + *op = (char *) &rp + 12; +#ifdef RationalWorld + rh->pt = 7; +#else + ((char *) rh)[1] = 7; +#endif + for (i = 0; i < n; i++) { + bcopy(ip, op, 3); + bcopy(ip + 4, op + 3, 10); + op[13] = 0; + ip += 14; + op += 14; + } + pl = 12 + 14 * n; + + } else if (sb->compression & fEnculaw) { +#ifdef RationalWorld + rh->pt = 0; +#else + ((char *) rh)[1] = 0; +#endif + bcopy(sb->buffer.buffer_val, ((char *) &rp) + 12, + (int) sb->buffer.buffer_len); + pl = (int) sb->buffer.buffer_len + 12; + } else { /* default Uncompressed PCMA samples fEncAlaw */ +#ifdef RationalWorld + rh->pt = 8; +#else + ((char *) rh)[1] = 8; +#endif + bcopy(sb->buffer.buffer_val, ((char *) &rp) + 12, + (int) sb->buffer.buffer_len); + pl = (int) sb->buffer.buffer_len + 12; + } + if (pl > 0) { + bcopy((char *) &rp, (char *) sb, pl); + } + return pl; +} + +#endif + +/* PARSESDES -- Look for an SDES message in a possibly composite + RTCP packet and extract pointers to selected items + into the caller's structure. */ + +int parseSDES(packet, r) + unsigned char *packet; + struct rtcp_sdes_request *r; +{ + int i, success = FALSE; + unsigned char *p = packet; + + /* Initialise all the results in the request packet to NULL. */ + + for (i = 0; i < r->nitems; i++) { + r->item[i].r_text = NULL; + } + + /* Walk through the individual items in a possibly composite + packet until we locate an SDES. This allows us to accept + packets that comply with the RTP standard that all RTCP packets + begin with an SR or RR. */ + + while ((p[0] >> 6 & 3) == RTP_VERSION || (p[0] >> 6 & 3) == 1) { + if ((p[1] == RTCP_SDES) && ((p[0] & 0x1F) > 0)) { + unsigned char *cp = p + 8, + *lp = cp + (ntohs(*((short *) (p + 2))) + 1) * 4; + + bcopy(p + 4, r->ssrc, 4); + while (cp < lp) { + unsigned char itype = *cp; + + if (itype == RTCP_SDES_END) { + break; + } + + /* Search for a match in the request and fill the + first unused matching item. We do it this way to + permit retrieval of multiple PRIV items in the same + packet. */ + + for (i = 0; i < r->nitems; i++) { + if (r->item[i].r_item == itype && + r->item[i].r_text == NULL) { + r->item[i].r_text = (char *) cp; + success = TRUE; + break; + } + } + cp += cp[1] + 2; + } + break; + } + /* If not of interest to us, skip to next subpacket. */ + p += (ntohs(*((short *) (p + 2))) + 1) * 4; + } + return success; +} + +/* COPYSDESITEM -- Copy an SDES item to a zero-terminated user + string. */ + +void copySDESitem(s, d) + char *s, *d; +{ + int len = s[1] & 0xFF; + + bcopy(s + 2, d, len); + d[len] = 0; +} + diff --git a/voip/rtpacket.h b/voip/rtpacket.h new file mode 100644 index 0000000..8387ee9 --- /dev/null +++ b/voip/rtpacket.h @@ -0,0 +1,32 @@ +#ifndef H_RTPACKET_H +#define H_RTPACKET_H + +#include "rtp.h" + +#define ELEMENTS(array) (sizeof(array)/sizeof((array)[0])) +#define fEnculaw 0x100000 +#define fEncAlaw 0x200000 + +struct rtcp_sdes_request_item { + unsigned char r_item; + char *r_text; +}; + +struct rtcp_sdes_request { + int nitems; /* Number of items requested */ + unsigned char ssrc[4]; /* Source identifier */ + struct rtcp_sdes_request_item item[10]; /* Request items */ +}; + +extern int isrtp(unsigned char *, int); +extern int isValidRTCPpacket(unsigned char *, int); +extern int isRTCPByepacket(unsigned char *, int); +extern int isRTCPAPPpacket(unsigned char *, int, char *, unsigned char **); +extern int rtp_make_sdes(char **, unsigned long, int, int); +extern int rtp_make_sdes_s(char **, int, struct rtcp_sdes_request *); +extern int rtp_make_bye(unsigned char *, unsigned long, char *, int); +extern int rtp_make_app(unsigned char *, unsigned long, int, char *, + unsigned char *, int); +extern int parseSDES(unsigned char *, struct rtcp_sdes_request *); +extern void copySDESitem(char *, char *); +#endif diff --git a/voip/testlog b/voip/testlog new file mode 100644 index 0000000..a94c3f7 --- /dev/null +++ b/voip/testlog @@ -0,0 +1,3 @@ +debug_init: debug_mask = 0 +debug_init: debug_mask = 0 +debug_close: debug channel now closed diff --git a/voip/tstcfg b/voip/tstcfg new file mode 100644 index 0000000..5ce4f65 --- /dev/null +++ b/voip/tstcfg @@ -0,0 +1,13 @@ +# +# tstsetup voip +# +DEBUG 066 +PORT 2074 +GSM + +MSN 12345 +MSN 888 +VOIPNR 4566 pictra_client GSM +VOIPNR 4567 10.23.200.1 +AUDIONR 789 + diff --git a/voip/tstparse.c b/voip/tstparse.c new file mode 100644 index 0000000..b420e94 --- /dev/null +++ b/voip/tstparse.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include "isdn_net.h" +#include "globals.h" + +extern int parse_cfg(char *, manager_t *); + +int main(argc,argv) +int argc; +char *argv[]; +{ + manager_t mgr; + nr_list_t *nr; + + memset(&mgr, 0, sizeof(manager_t)); + printf("Start: Debug %x Port %d Flags %x\n", + global_debug, rtp_port, default_flags); + parse_cfg("tstcfg", &mgr); + printf("AfterParse: Debug %x Port %d Flags %x\n", + global_debug, rtp_port, default_flags); + nr = mgr.nrlist; + while(nr) { + printf("nr(%s) len(%d) flg(%x) typ(%d) name(%s)\n", + nr->nr, nr->len, nr->flags, nr->typ, nr->name); + nr = nr->next; + } + return(0); +} \ No newline at end of file diff --git a/voip/vitimer.h b/voip/vitimer.h new file mode 100644 index 0000000..64e1950 --- /dev/null +++ b/voip/vitimer.h @@ -0,0 +1,32 @@ +#ifndef VITIMER_H +#define VITIMER_H + +#include +#include +#include +#include +#include + +typedef struct _vi_timer vi_timer_t; +typedef int (*timef_t)(void *, unsigned long, struct timeval *); + +struct _vi_timer { + vi_timer_t *prev; + vi_timer_t *next; + struct timeval tv; + void *data; + unsigned long val; + timef_t func; +}; + +extern int run_vitimer(void); +extern void remove_vitimer(vi_timer_t *); +extern int init_vitimer(vi_timer_t *, void *, unsigned long, timef_t); +extern int add_vitimer_abs(vi_timer_t *, struct timeval *); +extern int add_vitimer_rel(vi_timer_t *, struct timeval *); +extern void clean_vitimer(void); +extern struct timeval *get_next_vitimer_time(void); +extern int get_next_vitimer_dist(struct timeval *); + + +#endif diff --git a/voip/voip_appl.c b/voip/voip_appl.c new file mode 100644 index 0000000..343f861 --- /dev/null +++ b/voip/voip_appl.c @@ -0,0 +1,1539 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "helper.h" +#include "g711.h" +#include "isdn_net.h" +#include "isound.h" +#include "rtpacket.h" +#include "iapplication.h" + + +#define RANDOM_DEVICE "/dev/urandom" + +#define RTP_PAD_FLAG 0x20 + +static int +init_voipsocks(vapplication_t *v) +{ + v->dsock = socket(AF_INET, SOCK_DGRAM, 0); + if (v->dsock < 0) { + perror("opening data socket"); + return 1; + } + v->csock = socket(AF_INET, SOCK_DGRAM, 0); + if (v->csock < 0) { + perror("opening control socket"); + return 1; + } + + if (v->csock < v->dsock) { + int tmpsock = v->dsock; + v->dsock = v->csock; + v->csock = tmpsock; + } + + /* Create caddr/daddr with wildcards. */ + + v->daddr.sin_family = v->caddr.sin_family = AF_INET; + v->daddr.sin_addr.s_addr = v->caddr.sin_addr.s_addr = INADDR_ANY; + v->daddr.sin_port = htons(v->port); + if (bind(v->dsock, (struct sockaddr *) &v->daddr, + sizeof(struct sockaddr_in)) < 0) { + perror("binding data socket"); + return 1; + } + v->caddr.sin_port = htons(v->port + 1); + if (bind(v->csock, (struct sockaddr *) &v->caddr, + sizeof(struct sockaddr_in)) < 0) { + perror("binding control socket"); + return 1; + } + dprint(DBGM_SOCK, "socket ports #%d/#%d\n", + ntohs(v->daddr.sin_port), ntohs(v->caddr.sin_port)); + return(0); +} + +int +SendBye(iapplication_t *ia, char *bye) +{ + vconnection_t *c; + int len; + + c = ia->con; + if (!c) + return(-EINVAL); + if (ia->Flags & (AP_FLG_VOIP_SENT_BYE | AP_FLG_VOIP_PEER_BYE)) + return(0); + len = rtp_make_bye(c->cbuf, c->own_ssrc, bye, 1); + dprint(DBGM_SOCK, "C socket send %d bytes to %s\n", + len, inet_ntoa(c->cpeer.sin_addr)); + ia->Flags |= AP_FLG_VOIP_SENT_BYE; + if (len) { + dhexprint(DBGM_CDATA, "send ctrl packet:", c->cbuf, len); + len = sendto(c->sock, c->cbuf, len, 0, + (struct sockaddr *)&c->cpeer, sizeof(c->cpeer)); + if (len < 0) { + eprint("cannot send ctrl msg errno(%d)\n", errno); + return(-errno); + } + } else { + eprint("cannot compose Bye message\n"); + return(-EINVAL); + } + return(0); +} + +void +clear_connection(iapplication_t *ia) +{ + vconnection_t *c = ia->con; + + if (c) { + dprint(DBGM_SOCK, "clear connection to %s ssrc(%lx/%lx)\n", + c->rmtname, c->own_ssrc, c->peer_ssrc); + if (ia->Flags & AP_FLG_VOIP_PEER_VALID) { + msg_queue_purge(&c->aqueue); + if (c->amsg) + free_msg(c->amsg); + SendBye(ia, "closing"); + } + if (c->sock) + close(c->sock); + if (c->r_gsm) + gsm_destroy(c->r_gsm); + if (c->s_gsm) + gsm_destroy(c->s_gsm); + free(c); + ia->con = NULL; + } +} + +void +free_application(iapplication_t *ia) +{ + vconnection_t *c = ia->con; + + if (!ia) + return; + if (c) + clear_connection(ia); + REMOVE_FROM_LISTBASE(ia, ia->vapp->iapp_lst); + free(ia); +} + +static void +close_voipsocks(vapplication_t *v) +{ + dprint(DBGM_SOCK, "socket closed\n"); + while(v->iapp_lst) { + free_application(v->iapp_lst); + } + close(v->csock); + close(v->dsock); + v->csock = 0; + v->dsock = 0; +} + +static iapplication_t * +get_connection(vapplication_t *v, unsigned long ssrc, int exact) +{ + iapplication_t *ip; + + ip = v->iapp_lst; + while (ip) { + if (ip->con) { + dprint(DBGM_SOCK, "ip(%p) %x %s/%s %x/%x\n", + ip, ip->Flags, + inet_ntoa(v->from.sin_addr), + inet_ntoa(ip->con->cpeer.sin_addr), + ssrc, ip->con->peer_ssrc); + if (memcmp(&v->from.sin_addr, &(ip->con->cpeer.sin_addr), + sizeof(struct in_addr)) == 0) { + if (ip->Flags & AP_FLG_VOIP_PEER_VALID) { + if (ip->Flags & AP_FLG_VOIP_PEER_SF) { + return(ip); + } else if (ip->con->peer_ssrc == ssrc) { + return(ip); + } + } else if (!exact) { + if (!get_connection(v, ssrc, 1)) { + ip->con->peer_ssrc = ssrc; + ip->Flags |= AP_FLG_VOIP_PEER_VALID; + return(ip); + } + } + } + } + ip = ip->next; + } + return(NULL); +} + +unsigned long +my_random_ul(void) { + int rd; + unsigned long r; + + rd = open(RANDOM_DEVICE, O_RDONLY); + if (rd<0) + return(random()); + read(rd, &r, sizeof(r)); + close(rd); + return(r); +} + +unsigned long +getnew_ssrc(vapplication_t *v) +/* this excludes 0 as value, but I think its OK */ +{ + unsigned long ssrc = 0; + iapplication_t *ip; + + while(!ssrc) { + ssrc = my_random_ul(); + ip = v->iapp_lst; + while (ip) { + if (ip->con) { + if (ip->con->peer_ssrc == ssrc) { + ssrc = 0; + break; + } + if (ip->con->own_ssrc == ssrc) { + ssrc = 0; + break; + } + } + ip = ip->next; + } + } + return(ssrc); +} + +vconnection_t * +new_connection(iapplication_t *ia, struct in_addr *addr) +{ + vconnection_t *c; + + c = malloc(sizeof(vconnection_t)); + if (!c) + return(NULL); + memset(c, 0, sizeof(vconnection_t)); + msg_queue_init(&c->aqueue); + c->sock = socket(AF_INET, SOCK_DGRAM, 0); + c->cpeer.sin_family = c->dpeer.sin_family = AF_INET; + memcpy(&c->cpeer.sin_addr, addr, sizeof(struct in_addr)); + memcpy(&c->dpeer.sin_addr, addr, sizeof(struct in_addr)); + c->dpeer.sin_port = htons(ia->vapp->port); + c->cpeer.sin_port = htons(ia->vapp->port + 1); +#if 0 + /* Compute the number of sound samples needed to fill a + packet of TINY_PACKETS bytes. */ + + if (rtp) { + sound_packet = (gsmcompress | lpccompress | adpcmcompress) ? (160 * 4) + : 320; + } else if (vat) { + sound_packet = (gsmcompress | lpccompress | adpcmcompress) ? (160 * 4) + : 320; + } else { + sound_packet = ((TINY_PACKETS - ((sizeof(soundbuf) - BUFL))) * + (compressing ? 2 : 1)); + if (gsmcompress) { + sound_packet = compressing ? 3200 : 1600; + } else if (adpcmcompress) { + sound_packet *= 2; + sound_packet -= 4; /* Leave room for state at the end */ + } else if (lpccompress) { + sound_packet = 10 * LPC_FRAME_SIZE; + } else if (lpc10compress) { + sound_packet = compressing ? 3600 : 1800; + } + } + +#ifdef SHOW_PACKET_SIZE + printf("Samples per packet = %d\n", sound_packet); +#endif + lread = sound_packet; +#endif + /* default size */ + c->pkt_size = 320; + return(c); +} + +iapplication_t * +new_application(vapplication_t *v) +{ + iapplication_t *ip; + + ip = malloc(sizeof(iapplication_t)); + if (!ip) + return(NULL); + memset(ip, 0, sizeof(iapplication_t)); + ip->vapp = v; + APPEND_TO_LIST(ip, v->iapp_lst); + return(ip); +} + +static iapplication_t * +new_peer_connection(vapplication_t *v, unsigned long ssrc, int version) +{ + iapplication_t *ip; + vconnection_t *c; + struct hostent *h; + + ip = new_application(v); + if (!ip) + return(NULL); + c = new_connection(ip, &v->from.sin_addr); + if (!c) { + free_application(ip); + return(NULL); + } + h = gethostbyaddr((char *) &v->from.sin_addr, sizeof(struct in_addr), + AF_INET); + if (h == NULL) { + strcpy(c->rmtname, inet_ntoa(v->from.sin_addr)); + } else { + strcpy(c->rmtname, h->h_name); + } + strncpy(c->con_hostname, c->rmtname, 31); + c->peer_ssrc = ssrc; + c->own_ssrc = getnew_ssrc(v); + ip->Flags |= AP_FLG_VOIP_PEER_VALID; + if (version == 1) /* speakfreely without RTP */ + ip->Flags |= AP_FLG_VOIP_PEER_SF; + ip->con = c; + return(ip); +} + +static int +play_data(iapplication_t *ip) +{ + isound_t *is = ip->data2; + int maxlen; + unsigned char *buf = ip->con->rbuf; + + if (!is || !is->rbuf) { + wprint("ip->data2 %p\n", is); + return(-1); + } + maxlen = ibuf_freecount(is->rbuf); + dprint(DBGM_SOUND, "%s: %d data max(%d)\n", __FUNCTION__, + ip->con->rlen, maxlen); + if (maxlen > ip->con->rlen) + maxlen = ip->con->rlen; + else if (maxlen < ip->con->rlen) { + dprint(DBGM_SOUND, "pb shortage, skip %d bytes (%d/%d/%d/%d/%d)\n", + ip->con->rlen - maxlen, ip->con->rlen, maxlen, + is->rbuf->ridx, is->rbuf->widx, is->rbuf->size); + wprint("playbuffer shortage, skip %d bytes\n", + ip->con->rlen - maxlen); + buf += (ip->con->rlen - maxlen); + } + if (maxlen) + ibuf_memcpy_w(is->rbuf, buf, maxlen); + if (is->rbuf->rsem) + sem_post(is->rbuf->rsem); + return(maxlen); +} + +static int +receive_data(vapplication_t *v) { + iapplication_t *iap; + unsigned long ssrc, ts; + unsigned short seq; + unsigned char cc, payl; + unsigned char ver; + rtp_hdr_t *rh; + + if (v->rlen < 12) + return(-2); + rh = (rtp_hdr_t *)&v->buf.d; + ver = (v->buf.d[0] >> 6) & 3; + if (ver != RTP_VERSION) { + dprint(DBGM_SOCK, "rtp version %d\n", + ver); + return(-3); + } + ssrc = ntohl(rh->ssrc); + iap = get_connection(v, ssrc, 1); + if (!iap) { /* data from not known source ignored before a CTRL packet */ + dprint(DBGM_SOCK, "rtp data ignored from %s ssrc(%lx)\n", + inet_ntoa(v->from.sin_addr), ssrc); + return(-4); + } + cc = v->buf.d[0] & 0xf; + payl = v->buf.d[1] & 0x7f; + seq = ntohs(rh->seq); + ts = ntohl(rh->ts); + iap->con->rlen = v->rlen - 4*cc - 12; + if (v->buf.d[0] & RTP_PAD_FLAG) { + iap->con->rlen -= v->buf.d[v->rlen-1]; + } + dprint(DBGM_SOCK, "rtp data len(%d) pl(%x) seq(%d) ts(%lx)\n", + iap->con->rlen, payl, seq, ts); + if (iap->con->rlen <= 0) { + dprint(DBGM_SOCK, "rtp data len error %d\n", iap->con->rlen); + return(-5); + } + iap->con->rbuf = v->buf.d + 4*cc + 12; + if (payl == 8) { /* alaw */ + /* default */ + } else if (payl == 0) { /* ulaw */ + int i; + + for (i=0;icon->rlen;i++) + iap->con->rbuf[i] = ulaw2alaw(iap->con->rbuf[i]); +#ifdef GSM_COMPRESSION + } else if (payl == 3) { /* GSM */ + int i; + gsm_signal gs[640], *gp; + u_char buf[640], *p; + + if (!(iap->con->sndflags & SNDFLG_COMPR_GSM)) { + iap->con->sndflags |= SNDFLG_COMPR_GSM; + iap->con->pkt_size = 640; + } + if (!iap->con->r_gsm) + iap->con->r_gsm = gsm_create(); + if (iap->con->rlen != 4*33) { + eprint("%s wrong GSM Data size %d/%d\n", __FUNCTION__, + iap->con->rlen, 4*33); + return(-6); + } + gp = gs; + p = iap->con->rbuf; + for (i=0;i<4; i++) { + gsm_decode(iap->con->r_gsm, p, gp); + p += 33; + gp += 160; + } + gp = gs; + p = iap->con->rbuf = buf; + iap->con->rlen = 640; + for (i=0;i<640;i++) + *p++ = linear2alaw(*gp++); +#endif + } else { + dprint(DBGM_SOCK, "rtp data payload %x not supported\n", payl); + return(-7); + } + return(play_data(iap)); +} + +static int +receive_ctrl(vapplication_t *v) { + iapplication_t *iap; + unsigned char *app; + unsigned long ssrc; + int ver; + + if (v->rlen < 8) + return(1); + ver = (v->buf.d[0] >> 6) & 3; + if ((ver != RTP_VERSION) && (ver != 1)) { + dprint(DBGM_SOCK, "rtp version %d\n", + ver); + return(2); + } + ssrc = ntohl(*((unsigned long *)&v->buf.d[4])); + iap = get_connection(v, ssrc, 0); + if (!iap) { /* New connection */ + if (isRTCPByepacket(v->buf.d, v->rlen)) { + dprint(DBGM_CONN, "bye in new connection from %s ignored\n", + inet_ntoa(v->from.sin_addr)); + return(3); + } + dprint(DBGM_CONN, "new connection from %s ssrc(%x)\n", + inet_ntoa(v->from.sin_addr), ssrc); + iap = new_peer_connection(v, ssrc, ver); + if (!iap) { + return(4); + } + voip_application_handler(iap, AP_PR_VOIP_NEW, NULL); + } else { + if (isRTCPByepacket(v->buf.d, v->rlen)) { + iap->Flags |= AP_FLG_VOIP_PEER_BYE; + dprint(DBGM_CONN, "connection from %s bye\n", + inet_ntoa(v->from.sin_addr)); + } + } + if (isRTCPAPPpacket(v->buf.d, v->rlen, "ISDN", &app)) { + return(voip_application_handler(iap, AP_PR_VOIP_ISDN, app)); + } + if (iap->Flags & AP_FLG_VOIP_PEER_BYE) { + clear_connection(iap); + voip_application_handler(iap, AP_PR_VOIP_BYE, NULL); + } + return(0); +#if 0 + /* See if this connection is active. If not, initialise a new + connection. */ + + busyreject = FALSE; + newconn = FALSE; + c = conn; + while (c != NULL) { + if (memcmp(&from.sin_addr, &(c->con_addr), + sizeof(struct in_addr)) == 0) { + break; + } + c = c->con_next; + } + /* Initialise fields in connection. Only fields which need to + be reinitialised when a previously idle host resumes activity + need be set here. */ + + if (newconn) { + c->face_file = NULL; + c->face_filename[0] = 0; + c->face_viewer = 0; + c->face_stat = FSinit; + c->face_address = 0L; + c->face_retry = 0; + c->con_compmodes = -1; + c->con_protocol = PROTOCOL_UNKNOWN; + c->con_rseq = -1; + c->con_reply_current = FALSE; + c->con_busy = 0; + bcopy("\221\007\311\201", c->con_session, 4); + lpc_init(&c->lpcc); + busyreject = isBusy(); + } + + if (c != NULL) { + /* Reset connection timeout. */ + c->con_timeout = 0; + + if (newconn) { + if (showhosts) { + fprintf(stdout, "%s: %s %s %s\n", prog, etime(), c->con_hostname, + busyreject ? "sending busy signal" : "connect"); + } + } + if (busyreject) { + continue; + } + + /* Request face data from the other end, starting with + block zero. If the connection was created itself + by a face data request, don't request the face from + the other end; wait, instead, for some sound to + arrive. We use face_stat to decide when to make the + request rather than newconn, since a connection may + have been created by a face request from the other + end, which didn't trigger a reciprocal request by + us. */ + + if (!control && (c->con_protocol == PROTOCOL_SPEAKFREE) && + (c->face_stat == FSinit) && + isSoundPacket(ntohl(sb.compression)) && + (ntohl(sb.compression) & fFaceOffer)) { + c->face_address = 0; + c->face_timeout = 0; + c->face_retry = 0; + c->face_stat = FSreply; /* Activate request from timeout */ + faceTransferActive++; /* Mark face transfer underway */ + if (faceTransferActive == 1) { + windtimer(); /* Set timer to fast cadence */ + } + } + + } else { + continue; + } + + wasrtp = FALSE; + +#ifdef CRYPTO + + /* If a DES key is present and we're talking RTP or VAT + protocol we must decrypt the packet at this point. + We decrypt the packet if: + + 1. A DES key was given on the command line, and + either: + + a) The packet arrived on the control port + (and hence must be from an RTP/VAT client), or + + b) The protocol has already been detected as + RTP or VAT by reception of a valid control + port message. */ + + if ((control || (c->con_protocol == PROTOCOL_RTP) || + (c->con_protocol == PROTOCOL_VAT)) && + rtpdeskey[0]) { + + /* One more little twist. If this packet arrived on the + control channel, see if it passes all the tests for a + valid RTCP packet. If so, we'll assume it isn't + encrypted. RTP utilities have the option of either + encrypting their control packets or sending them in + the clear, so a hack like this is the only way we have + to guess whether something we receive is encrypted. */ + + if (!isValidRTCPpacket((unsigned char *) &sb, rll)) { + des_key_schedule sched; + des_cblock ivec; + int drll = rll; + char *whichkey; + static int toggle = 0; + + bzero(ivec, 8); + drll = (rll + 7) & (~7); + if (Debug) { + fprintf(stdout, "Decrypting %d VAT/RTP bytes with DES key.\r\n", + drll); + } + if (drll > rll) { + /* Should only happen for VAT protocol. Zero the rest of + the DES encryption block to guarantee consistency. */ + bzero(((char *) &sb) + rll, drll - rll); + } + + /* If the protocol is unknown, toggle back and forth + between the RTP and VAT DES keys until we crack the + packet and set the protocol. */ + + if (c->con_protocol == PROTOCOL_UNKNOWN || + c->con_protocol == PROTOCOL_VATRTP_CRYPT || + c->con_protocol == PROTOCOL_SPEAKFREE) { + whichkey = toggle == 0 ? vatdeskey : + (toggle == 1 ? rtpdeskey : NULL); + toggle = (toggle + 1) % 3; + } else { + whichkey = c->con_protocol == PROTOCOL_VAT ? + vatdeskey : rtpdeskey; + } + if (whichkey != NULL) { + des_set_key((des_cblock *) (whichkey + 1), sched); + des_ncbc_encrypt((des_cblock *) &sb, + (des_cblock *) &sb, rll, sched, + (des_cblock *) ivec, DES_DECRYPT); + + /* Just one more thing. In RTP (unlike VAT), when + an RTCP control packet is encrypted, 4 bytes of + random data are prefixed to the packet to prevent + known-plaintext attacks. We have to strip this + prefix after decrypting. */ + + if (control && ((*(((char *) &sb) + 4) & 0xC0) == 0x80)) { + rll -= 4; + bcopy(((char *) &sb) + 4, (char *) &sb, rll); + } + } +#ifdef HEXDUMP + if (hexdump) { + xd(stdout, (unsigned char *)&sb, rll, TRUE); + } +#endif + } + } +#endif + + /* If this packet arrived on the session control port, dispatch + it to the appropriate handler for its protocol. */ + + if (control) { + short protocol = PROTOCOL_VATRTP_CRYPT; + unsigned char *p = (unsigned char *) &sb; + unsigned char *apkt; + int proto = (p[0] >> 6) & 3; + + if (proto == 0) { /* VAT */ + /* To avoid spoofing by bad encryption keys, require + a proper ID message be seen before we'll flip into + VAT protocol. */ + if (((p[1] == 1) || (p[1] == 3)) || + ((c->con_protocol == PROTOCOL_VAT) && (p[1] == 2))) { + protocol = PROTOCOL_VAT; + bcopy(p + 2, c->con_session, 2); /* Save conference ID */ + + if (p[1] == 1 && showhosts) { + char uname[256]; + + bcopy(p + 4, uname, rll - 4); + uname[rll - 4] = 0; + if (strcmp(uname, c->con_uname) != 0) { + strcpy(c->con_uname, uname); + fprintf(stdout, "%s: %s sending from %s.\n", prog, + c->con_uname, c->con_hostname); + } + } + + /* Handling of VAT IDLIST could be a lot more elegant + than this. */ + + if (p[1] == 3 && showhosts) { + char *uname; + + uname = (char *) malloc(rll); + if (uname != NULL) { + unsigned char *bp = p, *ep = p + rll; + int i = bp[4]; + + bp += 8; + uname[0] = 0; + *ep = 0; + while (--i >= 0 && bp < ep) { + bp += 4; + strcat(uname, "\t"); + strcat(uname, (char *) bp); + while (isspace(uname[strlen(uname) - 1])) { + uname[strlen(uname) - 1] = 0; + } + strcat(uname, "\n"); + bp += (strlen((char *) bp) + 3) & ~3; + } + if (strncmp(uname, c->con_uname, (sizeof c->con_uname - 1)) != 0) { + strncpy(c->con_uname, uname, sizeof c->con_uname); + if (strlen(uname) > ((sizeof c->con_uname) - 1)) { + c->con_uname[((sizeof c->con_uname) - 1)] = 0; + } + fprintf(stdout, "%s: now in conference at %s:\n%s", prog, + c->con_hostname, uname); + } + free(uname); + } + } + + /* If it's a DONE packet, reset protocol to unknown. */ + + if (p[1] == 2) { + c->con_protocol = protocol = PROTOCOL_UNKNOWN; + c->con_timeout = hosttimeout - 1; + c->con_uname[0] = c->con_email[0] = 0; + if (showhosts) { + fprintf(stdout, "%s: %s VAT connection closed.\n", + prog, c->con_hostname); + } + } + } + + } else if (proto == RTP_VERSION || proto == 1) { /* RTP */ + if (isValidRTCPpacket((unsigned char *) &sb, rll)) { + protocol = (proto == 1) ? PROTOCOL_SPEAKFREE : PROTOCOL_RTP; + bcopy(p + 4, c->con_session, 4); /* Save SSRC */ + + /* If it's a BYE packet, reset protocol to unknown. */ + + if (isRTCPAPPpacket((unsigned char *) &sb, rll, + RTCP_APP_TEXT_CHAT, &apkt) && apkt != NULL) { + char *ident = c->con_hostname; + + /* To identify the sender, get successively more + personal depending on the information we have at + hand, working down from hostname (which may just + be an IP address if we couldn't resolve the host, + through E-mail address, to user name. */ + + if (c->con_email[0] != 0) { + ident = c->con_email; + } + if (c->con_uname[0] != 0) { + ident = ident = c->con_uname; + } + + printf("%s: %s\n", ident, (char *) (apkt + 12)); + + /* Otherwise, it's presumably an SDES, from which we + should update the user identity information for the + connection. */ + + } else { + struct rtcp_sdes_request rp; + + rp.nitems = 5; + rp.item[0].r_item = RTCP_SDES_CNAME; + rp.item[1].r_item = RTCP_SDES_NAME; + rp.item[2].r_item = RTCP_SDES_EMAIL; + rp.item[3].r_item = RTCP_SDES_TOOL; + rp.item[4].r_item = RTCP_SDES_NOTE; + if (parseSDES((unsigned char *) &sb, &rp)) { + char uname[256], email[256]; + + uname[0] = email[0] = 0; + if (rp.item[1].r_text != NULL) { + copySDESitem(rp.item[1].r_text, uname); + if (rp.item[2].r_text != NULL) { + copySDESitem(rp.item[2].r_text, email); + } else if (rp.item[2].r_text != NULL) { + copySDESitem(rp.item[0].r_text, email); + } + } else if (rp.item[2].r_text != NULL) { + copySDESitem(rp.item[2].r_text, uname); + } else if (rp.item[0].r_text != NULL) { + copySDESitem(rp.item[0].r_text, uname); + } + if (rp.item[4].r_text != NULL) { + copySDESitem(rp.item[4].r_text, hm_note); + h_appl_mgr(1, hm_note, c->con_hostname); + } + if (strcmp(uname, c->con_uname) != 0 || + strcmp(email, c->con_email) != 0) { + strcpy(c->con_uname, uname); + strcpy(c->con_email, email); + if (showhosts && uname[0]) { + fprintf(stdout, "%s: %s", prog, uname); + if (email[0]) { + fprintf(stdout, " (%s)", email); + } + fprintf(stdout, " sending from %s.\n", + c->con_hostname); + } + } + } + } + } else { + if (Debug) { + fprintf(stdout, "Invalid RTCP packet received.\n"); + } + } + } else { + if (Debug) { + fprintf(stdout, "Bogus protocol 3 control message.\n"); + } + } + + /* If protocol changed, update in connection and, if appropriate, + update the reply command. */ + + if (protocol != c->con_protocol) { + static char *pname[] = { + "Speak Freely", + "VAT", + "RTP", + "VAT/RTP encrypted", + "Unknown" + }; + + c->con_protocol = protocol; + if (showhosts) { + fprintf(stdout, "%s: %s sending in %s protocol.\n", + prog, c->con_hostname, pname[protocol]); + } + c->con_reply_current = FALSE; + } + continue; + } + + /* If this message is tagged with our Speak Freely protocol + bit, force protocol back to Speak Freely. This allows us + to switch back to Speak Freely after receiving packets in + VAT. We can still get confused if we receive a packet from + an older version of Speak Freely that doesn't tag. */ + + if (c->con_protocol == PROTOCOL_VAT || + c->con_protocol == PROTOCOL_VATRTP_CRYPT) { + unsigned char *p = (unsigned char *) &sb; + + if (((p[0] >> 6) & 3) == 1) { + c->con_protocol = PROTOCOL_SPEAKFREE; + } + } + + /* If this is a VAT packet, translate it into a sound buffer. */ + + if (((c->con_protocol == PROTOCOL_VAT)) && + (bcmp(((unsigned char *) &sb) + 2, c->con_session, 2) == 0) && + isvat((unsigned char *) &sb, rll)) { + if (sb.buffer.buffer_len == 0) { + if (Debug) { + fprintf(stdout, "Ignoring unparseable VAT packet.\n"); + } + continue; + } + wasrtp = TRUE; + + /* If this is an RTP packet, transmogrify it into a sound + buffer we can understand. */ + + } else if ((c->con_protocol == PROTOCOL_RTP) && + (bcmp(((unsigned char *) &sb) + 8, c->con_session, 4) == 0) && + isrtp((unsigned char *) &sb, rll)) { + if (sb.buffer.buffer_len == 0) { + if (Debug) { + fprintf(stdout, "Ignoring unparseable RTP packet.\n"); + } + continue; + } + wasrtp = TRUE; + } + + if (!wasrtp) { + int xbl; + + /* Convert relevant fields from network to host + byte order, if necessary. */ + + sb.compression = ntohl(sb.compression); + sb.buffer.buffer_len = ntohl(sb.buffer.buffer_len); + + if (sb.compression & fCompRobust) { + int aseq = (sb.buffer.buffer_len >> 24) & 0xFF; + + if (aseq == c->con_rseq) { + continue; + } + c->con_rseq = aseq; + sb.buffer.buffer_len &= 0xFFFFFFL; + } + + /* Now if this is a valid Speak Freely packet (as + opposed to a VAT packet masquerading as one, or + an encrypted VAT or RTP packet we don't have the + proper key to decode), the length received from the + socket will exactly equal the buffer length plus + the size of the header. This is a reasonably + good validity check, well worth it considering the + horrors treating undecipherable garbage as a sound + buffer could inflict on us. */ + + xbl = sb.buffer.buffer_len + (sizeof(struct soundbuf) - BUFL); + + /* If this packet is encrypted with an algorithm which requires + padding the packet to an 8-byte boundary, adjust the actual + content length to account for the padding. */ + + if ((sb.compression & (fEncDES | fEncIDEA | fEncBF | fEncPGP)) != 0) { + xbl = ((sb.buffer.buffer_len + 7) & (~7)) + + (sizeof(struct soundbuf) - BUFL); + } + + /* If packet is compressed with LPC-10, compensate for "packet + stuffing". */ + + if ((sb.compression & fCompLPC10) && (sb.buffer.buffer_len >= 16)) { + xbl -= 16; + } + if (xbl != rll) { + if (Debug) { + fprintf(stdout, + "Sound buffer length %d doesn't match %d byte packet. Hdr=%08lX\n", + xbl, rll, sb.compression); + } + if (showhosts && c->con_protocol != PROTOCOL_UNKNOWN) { + fprintf(stdout, "%s: %s sending in unknown protocol or encryption.\n", + prog, c->con_hostname); + } + c->con_protocol = PROTOCOL_UNKNOWN; + continue; + } + + /* It does appear to be a genuine Speak Freely sound + buffer. On that basis, set the protocol to Speak Freely + even if the buffer isn't explicitly tagged. */ + + if (c->con_protocol != PROTOCOL_SPEAKFREE) { + c->con_protocol = PROTOCOL_SPEAKFREE; + if (showhosts) { + fprintf(stdout, "%s: %s sending in Speak Freely protocol.\n", prog, c->con_hostname); + } + c->con_reply_current = FALSE; + } + } + + /* If this is a face request and we have a face file open, + respond to it. Note that servicing of face file data requests + is stateless. */ + + if (sb.compression & fFaceData) { + if (sb.compression & faceRequest) { + long l; + + /* Request for face data. */ + + if (facefile != NULL) { + fseek(facefile, sb.buffer.buffer_len, 0); + *((long *) sb.buffer.buffer_val) = htonl(sb.buffer.buffer_len); + l = fread(sb.buffer.buffer_val + sizeof(long), + 1, 512 - (sizeof(long) + (sizeof(soundbuf) - BUFL)), facefile); + sb.compression = fProtocol | fFaceData | faceReply; + if (Debug) { + fprintf(stdout, "%s: sending %ld bytes of face data at %d to %s\n", + prog, l, ntohl(*((long *) sb.buffer.buffer_val)), + c->con_hostname); + } + l += sizeof(long); + } else { + /* No face file. Shut down requestor. */ + sb.compression = fProtocol | fFaceData | faceLess; + l = 0; + } + bcopy((char *) &(from.sin_addr), (char *) &(name.sin_addr), + sizeof(struct in_addr)); + + sb.compression = htonl(sb.compression); + sb.buffer.buffer_len = htonl(l); + if (sendto(sock, (char *) &sb, + (int) ((sizeof(struct soundbuf) - BUFL) + l), + 0, (struct sockaddr *) &(name), sizeof name) < 0) { + perror("sending face image data"); + } + } else if (sb.compression & faceReply) { + + /* Face data packet received from remote server. */ + + if ((c->face_file == NULL) && (sb.buffer.buffer_len > 0)) { + sprintf(c->face_filename, "%sSF-%s.bmp", FACEDIR, c->con_hostname); + c->face_file = fopen(c->face_filename, "w"); + } + if (c->face_file != NULL) { + if (sb.buffer.buffer_len > sizeof(long)) { + long lp = ntohl(*((long *) sb.buffer.buffer_val)); + + if (lp == c->face_address) { + fseek(c->face_file, lp, 0); + fwrite(sb.buffer.buffer_val + sizeof(long), + sb.buffer.buffer_len - sizeof(long), 1, + c->face_file); + if (Debug) { + fprintf(stdout, "%s: writing %ld bytes at %ld in face file %s\n", + prog, sb.buffer.buffer_len - sizeof(long), + lp, c->face_filename); + } + c->face_address += sb.buffer.buffer_len - sizeof(long); + /* Timeout will make next request after the + configured interval. */ + c->face_stat = FSreply; + c->face_retry = 0; + } else { + if (Debug) { + fprintf(stdout, "%s: discarded %ld bytes for %ld in face file %s, expected data for %ld\n", + prog, sb.buffer.buffer_len - sizeof(long), + lp, c->face_filename, c->face_address); + } + } + } else { + pid_t cpid; + + if (Debug) { + fprintf(stdout, "%s: closing face file %s\n", + prog, c->face_filename); + } + fclose(c->face_file); + c->face_file = NULL; + c->face_stat = FScomplete; + faceTransferActive--; + + /* Start viewer to display face. We terminate + audio output (if active) before doing this since + we don't know the nature of the audio output + resource. If it's an open file handle which + would be inherited by the child process, that + would hang the audio device as long as the + viewer is active. */ + + if (audiok) { + audiok = FALSE; + if (Debug) { + fprintf(stdout, "%s: releasing audio before viewer fork().\n", prog); + } + } + cpid = fork(); + if (cpid == 0) { + char geom[30], *gp1 = NULL, *gp2 = NULL; + +#ifdef NEEDED + /* These should be reset by the execlp(). */ + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGALRM, SIG_DFL); + signal(SIGCHLD, SIG_DFL); +#endif + + /* Now we need to close any shared resources + that might have been inherited from the parent + process to avoid their being locked up for the + duration of the viewer's execution. */ + + close(sock); + if (record != NULL) { + fclose(record); + } + if (facefile != NULL) { + fclose(facefile); + } +#ifdef FACE_SET_GEOMETRY + /* Attempt to reasonably place successive face windows + on the screen to avoid the user's having to place + them individually (for window managers with + interactivePlacement enabled). */ + +#define faceInterval 120 /* Interval, in pixels, between successive faces */ + sprintf(geom, "-0+%d", facesDisplayed * faceInterval); + gp1 = "-geometry"; + gp2 = geom; +#endif + execlp("xv", "xv", c->face_filename, gp1, gp2, (char *) 0); + perror("launching face image viewer"); + facesDisplayed--; + exit(0); + /* Leave face image around, for the moment, so the user can + try to view it manually. */ + } else if (cpid == (pid_t) -1) { + perror("creating face image viewer process"); + } else { + c->face_viewer = cpid; + facesDisplayed++; + } + } + } + } else if (sb.compression & faceLess) { + if (c->face_file != NULL) { + fclose(c->face_file); + unlink(c->face_filename); + } + c->face_stat = FSabandoned; + faceTransferActive--; + if (Debug) { + fprintf(stdout, "%s: no face image available for %s\n", + prog, c->con_hostname); + } + } + continue; /* Done with packet */ + } + + /* If the packet requests loop-back, immediately dispatch it + back to the host who sent it to us. To prevent an infinite + loop-back cycle, we clear the loop-back bit in the header + before sending the message. We leave the host of origin + unchanged, allowing the sender to identify the packet as + one he originated. */ + + if (sb.compression & fLoopBack) { + bcopy((char *) &(from.sin_addr), (char *) &(name.sin_addr), + sizeof(struct in_addr)); + sb.compression &= ~fLoopBack; /* Prevent infinite loopback */ + + sb.compression = htonl(sb.compression); + sb.buffer.buffer_len = htonl(sb.buffer.buffer_len); + if (sendto(sock, (char *) &sb, rll, + 0, (struct sockaddr *) &(name), sizeof name) < 0) { + perror("sending datagram message"); + } + sb.compression = ntohl(sb.compression); + sb.buffer.buffer_len = ntohl(sb.buffer.buffer_len); + } + + + /* If this packet has been "stuffed" for maximum efficiency, + un-stuff it at this point. */ + + if ((sb.compression & fCompLPC10) && (sb.buffer.buffer_len >= 16)) { + bcopy(sb.sendinghost, (char *) &sb + rll, + sizeof sb.sendinghost); + rll += sizeof sb.sendinghost; + } + + +#ifdef CRYPTO + if ((sb.compression & fKeyPGP)) { + char cmd[256], f[40], kmd[16]; + FILE *kfile; + FILE *pipe; + struct MD5Context md5c; + + MD5Init(&md5c); + MD5Update(&md5c, sb.buffer.buffer_val, sb.buffer.buffer_len); + MD5Final(kmd, &md5c); + + if (memcmp(c->keymd5, kmd, 16) != 0) { + bcopy(kmd, c->keymd5, 16); + sprintf(f, "/tmp/.SF_SKEY%d", getpid()); + + kfile = fopen(f, "w"); + if (kfile == NULL) { + fprintf(stdout, "Cannot open encrypted session key file %s\n", f); + } else { + fwrite(sb.buffer.buffer_val, sb.buffer.buffer_len, 1, kfile); + fclose(kfile); +#ifdef ZZZ + if (pgppass == NULL) { + static char s[256]; + + fprintf(stdout, "Enter PGP pass phrase: "); + if (fgets(s, sizeof s, stdin) != NULL) { + s[strlen(s) - 1] = 0; + pgppass = s; + } + } +#endif + sprintf(cmd, "pgp -f +nomanual +verbose=0 +armor=off %s%s%s <%s", + pgppass ? "-z\"" : "", pgppass ? pgppass : "", + pgppass ? "\" " : "", f); +#ifdef PGP_DEBUG + fprintf(stdout, "Decoding session key with: %s\n", cmd); +#else + if (Debug) { + fprintf(stdout, "%s: decoding PGP session key.\n", prog); + } +#endif + pipe = popen(cmd, "r"); + if (pipe == NULL) { + fprintf(stdout, "Unable to open pipe to: %s\n", cmd); + } else { + int lr; + + /* Okay, explanation time again. On some systems + (Silicon Graphics, for example), the timer tick + alarm signal can cause the pending read from the + PGP key pipe to return an "Interrupted system + call" status (EINTR) with (as far as I've ever + seen and I sincerely hope it's always) zero bytes + read. This happens frequently when the timer is + running and the user takes longer to enter the + secret key pass phrase than the timer tick. So, + if this happens we keep on re-issuing the pipe + read until the phrase allows PGP to finish the + job. */ + + while ((lr = fread(c->pgpkey, 1, 17, pipe)) != 17 && + (errno == EINTR)) ; + if (lr == 17) { + c->pgpkey[0] = TRUE; +#ifdef PGP_DEBUG + { + int i; + + fprintf(stdout, "Session key for %s:", c->con_hostname); + for (i = 0; i < 16; i++) { + fprintf(stdout, " %02X", c->pgpkey[i + 1] & 0xFF); + } + fprintf(stdout, "\n"); + } +#else + if (Debug) { + fprintf(stdout, "%s: PGP session key decoded.\n", prog); + } +#endif + } else { + c->pgpkey[0] = FALSE; + fprintf(stdout, "%s: Error decoding PGP session key.\n", prog); +#ifdef PGP_DEBUG + fprintf(stdout, "Read status from pipe: %d\n", lr); + perror("reading decoded PGP key from pipe"); +#endif + } + pclose(pipe); + } + unlink(f); + } + } + } else +#endif + { + playbuffer(&sb, c); + } +#endif +} + +int +SendCtrl(iapplication_t *ia) +{ + vconnection_t *c; + unsigned char *p; + int len; + + c = ia->con; + if (!c) + return(-EINVAL); + if (ia->Flags & (AP_FLG_VOIP_SENT_BYE | AP_FLG_VOIP_PEER_BYE)) + return(-EBUSY); + if (!c->amsg) { + c->amsg = msg_dequeue(&c->aqueue); + if (c->amsg) + c->oc++; + } + if (!c->amsg) { /* make RR */ + c->amsg = alloc_msg(4); + if (!c->amsg) + return(-ENOMEM); + p = msg_put(c->amsg, 4); + *p++ = c->pc; + *p++ = c->oc; + *p++ = 0; + *p++ = 0x81; /* RR */ + len = rtp_make_app(c->cbuf, c->own_ssrc, 1, "ISDN", + c->amsg->data, c->amsg->len); + free_msg(c->amsg); + c->amsg = NULL; + } else { + p = c->amsg->data; + *p++ = c->pc; + *p = c->oc; + len = rtp_make_app(c->cbuf, c->own_ssrc, 1, "ISDN", + c->amsg->data, c->amsg->len); + } + dprint(DBGM_SOCK, "C socket send %d bytes to %s\n", + len, inet_ntoa(c->cpeer.sin_addr)); + if (len) { + dhexprint(DBGM_CDATA, "send ctrl packet:", c->cbuf, len); + len = sendto(c->sock, c->cbuf, len, 0, + (struct sockaddr *)&c->cpeer, sizeof(c->cpeer)); + if (len < 0) { + eprint("cannot send ctrl msg errno(%d)\n", errno); + return(-errno); + } + } else { + eprint("cannot compose APP message\n"); + return(-EINVAL); + } + return(0); +} + +static void * +voipscan(void *arg) { + int ret; + vapplication_t *v = arg; + fd_set fdset; + struct timeval timeout; + + init_voipsocks(v); + while (!(v->flags & AP_FLG_VOIP_ABORT)) { + run_vitimer(); + FD_ZERO(&fdset); + FD_SET(v->dsock, &fdset); + FD_SET(v->csock, &fdset); + if (get_next_vitimer_dist(&timeout)) { + timeout = v->tout; + } + ret = select(v->csock + 1, &fdset, NULL, NULL, &timeout); + if (ret < 0) { /* error */ + dprint(DBGM_SOCK, "socket select errno %d: %s\n", + errno, strerror(errno)); + continue; + } + if (ret == 0) { /* timeout */ + dprint(DBGM_SOCK, "socket select timeout\n"); + continue; + } + if (FD_ISSET(v->dsock, &fdset)) { /* data packet */ + v->fromlen = sizeof(struct sockaddr_in); + v->rlen = recvfrom(v->dsock, v->buf.d, MAX_NETBUFFER_SIZE, + 0, (struct sockaddr *) &v->from, &v->fromlen); + if (v->rlen <= 0) { + dprint(DBGM_SOCK, "D socket rlen(%d)\n", + v->rlen); + continue; + } + dhexprint(DBGM_DDATA, "data packet:", v->buf.d, v->rlen); + ret = receive_data(v); + if (ret<0) + dprint(DBGM_SOCK, "receive_data ret(%d)\n", ret); + } + if (FD_ISSET(v->csock, &fdset)) { /* ctrl packet */ + v->fromlen = sizeof(struct sockaddr_in); + v->rlen = recvfrom(v->csock, v->buf.d, MAX_NETBUFFER_SIZE, + 0, (struct sockaddr *) &v->from, &v->fromlen); + if (v->rlen <= 0) { + dprint(DBGM_SOCK, "C socket rlen(%d)\n", + v->rlen); + continue; + } + dhexprint(DBGM_CDATA, "ctrl packet:", v->buf.d, v->rlen); + ret = receive_ctrl(v); + if (ret) + dprint(DBGM_SOCK, "receive_ctrl ret(%d)\n", ret); + } + } + close_voipsocks(v); + return(NULL); +} + +pthread_t +run_voip(vapplication_t *v) { + int ret; + pthread_t tid; + + ret = pthread_create(&tid, NULL, voipscan, (void *)v); + return(tid); +} + +static int +rtpout_ap(iapplication_t *iap) +{ + int len, r; + rtp_hdr_t *rh; + + rh = (rtp_hdr_t *)iap->con->dbuf; + iap->con->dbuf[0] = RTP_VERSION << 6; + rh->seq = htons(iap->con->seq); + rh->ts = htonl(iap->con->timestamp); + rh->ssrc = htonl(iap->con->own_ssrc); + +#ifdef GSM_COMPRESSION + if (iap->con->sndflags & SNDFLG_COMPR_GSM) { /* GSM */ + int i; + gsm_signal gs[640], *gp; + u_char *p; + + if (!iap->con->s_gsm) + iap->con->s_gsm = gsm_create(); + if (iap->con->slen != 4*160) { + eprint("%s wrong GSM Data size %d/%d\n", __FUNCTION__, + iap->con->rlen, 4*160); + return(0); + } + gp = gs; + p = iap->con->sbuf; + for (i=0;i<640;i++) + *gp++ = alaw2linear(*p++); + p = &iap->con->dbuf[12]; + gp = gs; + for (i=0;i<4; i++) { + gsm_encode(iap->con->s_gsm, gp, p); + p += 33; + gp += 160; + } + len = 4*33 + 12; + iap->con->dbuf[1] = 3; + } else +#endif + { + iap->con->dbuf[1] = 8; + memcpy(&iap->con->dbuf[12], iap->con->sbuf, iap->con->slen); + len = 12 + iap->con->slen; + } + r = len % 4; + if (r) { + int i; + r = 4 - r; + for (i=0; icon->dbuf[len++] = 0; + iap->con->dbuf[len-1] = r; + iap->con->dbuf[0] |= RTP_PAD_FLAG; + } + return(len); +} + +static int +send_sdata(iapplication_t *ap) +{ + int len, ret; + + len = rtpout_ap(ap); + ap->con->seq++; + ap->con->timestamp += ap->con->slen; + if (len) + ret = sendto(ap->con->sock, ap->con->dbuf, len, 0, + (struct sockaddr *) &ap->con->dpeer, + sizeof(struct sockaddr_in)); + else + ret = 0; + return(ret); +} + +void * +voip_sender(void *arg) +{ + iapplication_t *ap = arg; + isound_t *is; + int avail, ret = 0; + + + while (!(ap->Flags & AP_FLG_VOIP_ABORT)) { + is = ap->data2; + if (!is || !is->sbuf) { + dprint(DBGM_SOUND, "application data2 NULL\n"); + break; + } + if (!ap->con) { + dprint(DBGM_SOUND, "application ap->con NULL\n"); + break; + } + avail = ibuf_usedcount(is->sbuf); + if (avail >= ap->con->pkt_size) { + ap->con->slen = ap->con->pkt_size; + ibuf_memcpy_r(ap->con->sbuf, is->sbuf, ap->con->slen); + if (is->sbuf->wsem) + sem_post(is->sbuf->wsem); +#if 0 + register unsigned char *start = bs; + register int j; + int squelched = (squelch > 0), osl = soundel; + + /* If entire buffer is less than squelch, ditch it. If + we haven't received sqdelay samples since the last + squelch event, continue to transmit. */ + + if (sqdelay > 0 && sqwait > 0) { + if (debugging) { + printf("Squelch countdown: %d samples left.\n", + sqwait); + } + sqwait -= soundel; + squelched = FALSE; + } else if (squelch > 0) { + for (j = 0; j < soundel; j++) { +#ifdef USQUELCH + if (((*start++ & 0x7F) ^ 0x7F) > squelch) +#else + int samp = alaw2linear(*start++); + + if (samp < 0) { + samp = -samp; + } + if (samp > squelch) +#endif + { + squelched = FALSE; + sqwait = sqdelay; + break; + } + } + } + + if (squelched) { + if (debugging) { + printf("Entire buffer squelched.\n"); + } + spurt = TRUE; + } else { + netbuf.compression = fProtocol | (ring ? (fSetDest | fDestSpkr) : 0); + netbuf.compression |= debugging ? fDebug : 0; + netbuf.compression |= loopback ? fLoopBack : 0; + ring = FALSE; + if (compressing) { + int is = soundel, os = soundel / 2; + + rate_flow(buf, buf, &is, &os); + soundel = os; + netbuf.compression |= fComp2X; + } + netbuf.buffer.buffer_len = soundel; + if (!sendpkt(&netbuf)) { + exiting(); + return(2); + } + if (debugging && !vat && !rtp) { + fprintf(stdout, "Sent %d audio samples in %d bytes.\r\n", + osl, soundel); + } + } +#endif + ret = send_sdata(ap); + dprint(DBGM_SOUND, "send_sdata ret %d\n", ret); + if (ret<=0) { + dprint(DBGM_SOUND, "send_sdata ret %d\n", ret); + ap->Flags |= AP_FLG_VOIP_ABORT; + } + } else { + sem_wait(&is->work); + } + } + return((void *)ret); +} + diff --git a/voip/voip_isdn.c b/voip/voip_isdn.c new file mode 100644 index 0000000..0062788 --- /dev/null +++ b/voip/voip_isdn.c @@ -0,0 +1,832 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "g711.h" +#include +#include +#include +#include +#include "isdn_net.h" +#include "l3dss1.h" +#include "helper.h" +#include "bchannel.h" +#include "tone.h" +#include "isound.h" +#include "globals.h" + +#include "iapplication.h" + +vapplication_t voip; + +static int +getnext_record(FILE *f) +{ + int opt = 0; + char line[128]; + + while(!feof(f)) { + if (fgets(line, 128, f)) { +// fprintf(stderr, "%s: line:%s", __FUNCTION__, line); + if (line[0]=='\n') + continue; + if (line[0]==0) + continue; + if (line[0]=='#') + continue; + sscanf(line,"%d", &opt); + return(opt); + } + } + return(0); +} + +int +read_rec_ctrlfile(void) +{ + FILE *f; + int opt; + manager_t *mgr = voip.mgr_lst; + + if (RecordCtrlFile[0] == 0) { + dprint(DBGM_TOPLEVEL, "%s: no RecordCtrlFile\n", __FUNCTION__); + return(-ENOENT); + } + f = fopen(RecordCtrlFile, "r"); + if (!f) { + dprint(DBGM_TOPLEVEL, "%s: cannot open %s: %s\n", __FUNCTION__, + RecordCtrlFile, strerror(errno)); + return(-errno); + } + while(mgr) { + opt = getnext_record(f); + dprint(DBGM_TOPLEVEL, "%s: mgr %p ch1: %d\n", __FUNCTION__, + mgr, opt); + if (opt) { + mgr->bc[0].Flags |= FLG_BC_RECORD; + } else { + mgr->bc[0].Flags &= ~FLG_BC_RECORD; + } + opt = getnext_record(f); + dprint(DBGM_TOPLEVEL, "%s: mgr %p ch2: %d\n", __FUNCTION__, + mgr, opt); + if (opt) { + mgr->bc[1].Flags |= FLG_BC_RECORD; + } else { + mgr->bc[1].Flags &= ~FLG_BC_RECORD; + } + mgr = mgr->next; + } + fclose(f); + return(0); +} + +static void +sig_usr1_handler(int sig) +{ + dprint(DBGM_TOPLEVEL, "%s: got sig(%d)\n", __FUNCTION__, sig); + read_rec_ctrlfile(); + signal(SIGUSR1, sig_usr1_handler); +} + +#if 0 +static void +sig_segfault(int sig, siginfo_t *si, void *arg) { + int i,*ip = arg; + + dprint(DBGM_TOPLEVEL, "segfault %d, %p, %p\n", + sig, si, arg); + if (si) { + dprint(DBGM_TOPLEVEL, "si: sig(%d) err(%d) code(%d) pid(%d)\n", + si->si_signo, si->si_errno, si->si_code, si->si_pid); + dprint(DBGM_TOPLEVEL, "si: status(%x) value(%x)\n", + si->si_status, si->si_value.sival_int); + dprint(DBGM_TOPLEVEL, "si: int(%x) ptr(%p) addr(%p)\n", + si->si_int, si->si_ptr, si->si_addr); + } + if (ip) { + ip -= 10; + for(i=0;i<20;i++) + dprint(DBGM_TOPLEVEL, "ip %3d: %x\n", i-10, ip[i]); + } + ip = (int *)si; + if (ip) { + ip -= 10; + for(i=0;i<20;i++) + dprint(DBGM_TOPLEVEL, "si %3d: %x\n", i-10, ip[i]); + } + +} +#endif + +static void +term_handler(int sig) +{ + pthread_t tid; + manager_t *mgr = voip.mgr_lst; + + tid = pthread_self(); + dprint(DBGM_TOPLEVEL,"signal %d received from thread %ld\n", sig, tid); + voip.flags |= AP_FLG_VOIP_ABORT; + while(mgr) { + term_netstack(mgr->nst); + term_bchannel(&mgr->bc[0]); + term_bchannel(&mgr->bc[1]); + mgr = mgr->next; + } +} + +#if 0 + +static void +child_handler(int sig) +{ + manager_t *mgr = voip.mgr_lst; + pid_t pid; + int stat; + + dprint(DBGM_TOPLEVEL,"signal %d received\n", sig); + while (mgr) { + if (mgr->bc[0].pid) { + pid = waitpid(mgr->bc[0].pid, &stat, WNOHANG); + dprint(DBGM_TOPLEVEL, "%s: waitpid(%d) stat(%x) ret(%d)\n", __FUNCTION__, + mgr->bc[0].pid, stat, pid); + if (mgr->bc[0].pid == pid) { + mgr->bc[0].pid = 0; +// if (mgr->bc[0].state == BC_STATE_ACTIV) + break; + } + } + if (mgr->bc[1].pid) { + pid = waitpid(mgr->bc[1].pid, &stat, WNOHANG); + dprint(DBGM_TOPLEVEL, "%s: waitpid(%d) stat(%x) ret(%d)\n", __FUNCTION__, + mgr->bc[1].pid, stat, pid); + if (mgr->bc[1].pid == pid) { + mgr->bc[1].pid = 0; +// if (mgr->bc[1].state == BC_STATE_ACTIV) + break; + } + } + mgr = mgr->next; + } + signal(SIGCHLD, child_handler); +} + +#endif + +static void * +read_audio(void *arg) +{ + isound_t *ia = arg; + pthread_t tid; + fd_set rfd, efd; + int ret,i; + + tid = pthread_self(); + dprint(DBGM_TOPLEVEL, "%s: tid %ld\n", __FUNCTION__, tid); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + while(1) { + FD_ZERO(&rfd); + FD_ZERO(&efd); + FD_SET(ia->data, &rfd); + FD_SET(ia->data, &efd); + ret = select(ia->data +1, &rfd, NULL, &efd, NULL); + if (ret < 0) { + dprint(DBGM_TOPLEVEL, "%s: select error %d %s\n", __FUNCTION__, + errno, strerror(errno)); + if (errno == EAGAIN) + continue; + if (errno == EINTR) + continue; + } + if (FD_ISSET(ia->data, &rfd)) { + ret = read(ia->data, ia->rtmp, MAX_AUDIO_READ); + if (ret < 0) { + dprint(DBGM_TOPLEVEL, "%s: read error %d %s\n", __FUNCTION__, + errno, strerror(errno)); + if (errno == EAGAIN) + continue; + continue; + } + if (!ret) { + dprint(DBGM_TOPLEVEL, "%s: zero read\n", __FUNCTION__); + continue; + } + if (ret > ibuf_freecount(ia->rbuf)) { + dprint(DBGM_TOPLEVEL, "%s: rbuf overflow %d/%d\n", __FUNCTION__, + ret, ibuf_freecount(ia->rbuf)); + ret = ibuf_freecount(ia->rbuf); + } + for (i=0; irtmp[i] = ulaw2alaw(ia->rtmp[i]); + ibuf_memcpy_w(ia->rbuf, ia->rtmp, ret); + if (ia->rbuf->rsem) + sem_post(ia->rbuf->rsem); + } + if (FD_ISSET(ia->data, &efd)) { + dprint(DBGM_TOPLEVEL, "%s: exception\n", __FUNCTION__); + break; + } + } + return NULL; +} + +static void * +work_audio(void *arg) +{ + isound_t *ia = arg; + pthread_t tid; + int ret, i; + + tid = pthread_self(); + dprint(DBGM_TOPLEVEL, "%s: tid %ld\n", __FUNCTION__, tid); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + while(1) { + sem_wait(&ia->work); + if (ia->wlen) { + ret = write(ia->data, &ia->wtmp[ia->widx], ia->wlen); + if (ret == -1) + continue; + if (ret < ia->wlen) { + ia->wlen -= ret; + ia->widx += ret; + continue; + } + ia->wlen = 0; + ia->widx = 0; + } + if ((ia->wlen = ibuf_usedcount(ia->sbuf))) { + ibuf_memcpy_r(ia->wtmp, ia->sbuf, ia->wlen); + for (i=0; iwlen; i++) + ia->wtmp[i] = alaw2ulaw(ia->wtmp[i]); + ret = write(ia->data, &ia->wtmp[0], ia->wlen); + if (ret == -1) + continue; + if (ret < ia->wlen) { + ia->wlen -= ret; + ia->widx = ret; + continue; + } + ia->wlen = 0; + } + } + return(NULL); +} + +int +setup_voip(iapplication_t *ap, bchannel_t *bc) +{ + isound_t *ia; + int ret; + + dprint(DBGM_APPL, "%s(%p, %p)\n", __FUNCTION__, ap, bc); + if (!bc) + return(-EINVAL); + if (!ap) + return(-EINVAL); + if (!bc->sbuf) + return(-EINVAL); + if (bc->rbuf) + return(-EINVAL); + bc->rbuf = init_ibuffer(2048); + if (!bc->rbuf) + return(-ENOMEM); + bc->rbuf->wsem = &bc->work; + ia = malloc(sizeof(isound_t)); + if (!ia) + return(-ENOMEM); + memset(ia, 0, sizeof(isound_t)); + ap->data2 = ia; + sem_init(&ia->work, 0, 0); + ia->sbuf = bc->rbuf; + ia->rbuf = bc->sbuf; + bc->sbuf->wsem = &ia->work; + bc->rbuf->rsem = &ia->work; + ret = pthread_create(&ap->tid, NULL, voip_sender, (void *)ap); + dprint(DBGM_APPL, "%s: create voip_sender %ld ret %d\n", __FUNCTION__, + ap->tid, ret); + return(ret); +} + +int +close_voip(iapplication_t *ap, bchannel_t *bc) +{ + isound_t *ia; + int ret, *retval; + + dprint(DBGM_APPL, "%s(%p, %p)\n", __FUNCTION__, ap, bc); + if (!bc) + return(-EINVAL); + if (!ap) + return(-EINVAL); + ia = ap->data2; + ap->data2 = NULL; + ap->Flags &= ~AP_FLG_VOIP_ACTIV; + if (!ia) + return(-EINVAL); + ret = pthread_cancel(ap->tid); + dprint(DBGM_APPL, "%s: cancel sender ret(%d)\n", __FUNCTION__, + ret); + ret = pthread_join(ap->tid, (void *)&retval); + dprint(DBGM_APPL, "%s: join sender ret(%d) rval(%p)\n", __FUNCTION__, + ret, retval); + ia->sbuf = NULL; + ia->rbuf = NULL; + if (bc->sbuf) + bc->sbuf->wsem = NULL; + if (bc->rbuf) + free_ibuffer(bc->rbuf); + bc->rbuf = NULL; + ret = sem_destroy(&ia->work); + dprint(DBGM_APPL, "%s: sem_destroy work %d\n", __FUNCTION__, + ret); + free(ia); + return(0); +} + + +static int +setup_audio(iapplication_t *ap, bchannel_t *bc) +{ + isound_t *ia; + int ret; + + if (!bc) + return(-EINVAL); + if (!ap) + return(-EINVAL); + if (!bc->sbuf) + return(-EINVAL); + if (bc->rbuf) + return(-EINVAL); + bc->rbuf = init_ibuffer(2048); + if (!bc->rbuf) + return(-ENOMEM); + bc->rbuf->wsem = &bc->work; + ia = malloc(sizeof(isound_t)); + if (!ia) + return(-ENOMEM); + memset(ia, 0, sizeof(isound_t)); + sem_init(&ia->work, 0, 0); + ia->data = open("/dev/audio", O_RDWR | O_NONBLOCK); + if (ia->data < 0) { + free(ia); + dprint(DBGM_TOPLEVEL, "%s: open rdwr %d %s\n", __FUNCTION__, + errno, strerror(errno)); + return(-errno); + } + ap->data2 = ia; + ia->sbuf = bc->rbuf; + ia->rbuf = bc->sbuf; + bc->sbuf->wsem = &ia->work; + bc->rbuf->rsem = &ia->work; + ret = pthread_create(&ia->rd_t, NULL, read_audio, (void *)ia); + dprint(DBGM_TOPLEVEL, "%s: create rd_t %ld ret %d\n", __FUNCTION__, + ia->rd_t, ret); + ret = pthread_create(&ia->wr_t, NULL, work_audio, (void *)ia); + dprint(DBGM_TOPLEVEL, "%s: create wr_t %ld ret %d\n", __FUNCTION__, + ia->wr_t, ret); + return(0); +} + +static int +close_audio(iapplication_t *ap, bchannel_t *bc) +{ + isound_t *ia; + int ret, *retval; + + if (!bc) + return(-EINVAL); + if (!ap) + return(-EINVAL); + ia = ap->data2; + ap->data2 = NULL; + if (!ia) + return(-EINVAL); + close(ia->data); + ret = pthread_cancel(ia->rd_t); + dprint(DBGM_TOPLEVEL, "%s: cancel rd_t ret(%d)\n", __FUNCTION__, + ret); + ret = pthread_cancel(ia->wr_t); + dprint(DBGM_TOPLEVEL, "%s: cancel wr_t ret(%d)\n", __FUNCTION__, + ret); + ret = pthread_join(ia->rd_t, (void *)&retval); + dprint(DBGM_TOPLEVEL, "%s: join rd_t ret(%d) rval(%p)\n", __FUNCTION__, + ret, retval); + ret = pthread_join(ia->wr_t, (void *)&retval); + dprint(DBGM_TOPLEVEL, "%s: join wr_t ret(%d) rval(%p)\n", __FUNCTION__, + ret, retval); + ia->sbuf = NULL; + ia->rbuf = NULL; + if (bc->sbuf) + bc->sbuf->wsem = NULL; + if (bc->rbuf) + free_ibuffer(bc->rbuf); + bc->rbuf = NULL; + ret = sem_destroy(&ia->work); + dprint(DBGM_TOPLEVEL, "%s: sem_destroy work %d\n", __FUNCTION__, + ret); + free(ia); + return(0); +} + +static int +route_call(iapplication_t *ap, bchannel_t *bc) +{ + bchannel_t *newbc = NULL; + int ret; + + if (bc) { + display_NR_IE(bc->msn, __FUNCTION__": msn"); + display_NR_IE(bc->nr, __FUNCTION__": nr"); + } + ap->data1 = bc; + if (!bc) + return(-EINVAL); + read_rec_ctrlfile(); + if (bc->usednr->typ == NR_TYPE_INTERN) { + ap->mode = AP_MODE_INTERN_CALL; + ret = ap->mgr->app_bc(ap->mgr, PR_APP_OCHANNEL, &newbc); + if (0 >= ret) + dprint(DBGM_TOPLEVEL, "%s: no free channel ret(%d)\n", __FUNCTION__, + ret); + if (!newbc) { + bc->cause_loc = CAUSE_LOC_PNET_LOCUSER; + bc->cause_val = CAUSE_USER_BUSY; + ap->mgr->app_bc(ap->mgr, PR_APP_HANGUP, bc); + return(0); + } + newbc->app = ap; + ap->data2 = newbc; + newbc->Flags |= FLG_BC_APPLICATION; + newbc->msn[0] = bc->usednr->len +1; + newbc->msn[1] = 0x81; + memcpy(&newbc->msn[2], bc->usednr->nr, bc->usednr->len); + if (bc->msn[0]) + memcpy(newbc->nr, bc->msn, bc->msn[0] + 1); + newbc->l1_prot = ISDN_PID_L1_B_64TRANS; + ap->mgr->app_bc(ap->mgr, PR_APP_OCALL, newbc); + } else if (bc->usednr->typ == NR_TYPE_AUDIO) { + if (ap->vapp->flags & AP_FLG_AUDIO_USED) { + bc->cause_loc = CAUSE_LOC_PNET_LOCUSER; + bc->cause_val = CAUSE_USER_BUSY; + ap->mgr->app_bc(ap->mgr, PR_APP_HANGUP, bc); + return(0); + } else + ap->vapp->flags |= AP_FLG_AUDIO_USED; + ap->mode = AP_MODE_AUDIO_CALL; + bc->Flags |= FLG_BC_PROGRESS; + ap->mgr->app_bc(ap->mgr, PR_APP_ALERT, bc); + ret = setup_audio(ap, bc); + if (ret) { + ap->vapp->flags &= ~AP_FLG_AUDIO_USED; + bc->cause_loc = CAUSE_LOC_PNET_LOCUSER; + bc->cause_val = CAUSE_INCOMPATIBLE_DEST; + ap->mgr->app_bc(ap->mgr, PR_APP_HANGUP, bc); + return(0); + } + ap->Flags |= AP_FLG_AUDIO_ACTIV; + strcpy(bc->display,"connect to AUDIO"); + ap->mgr->app_bc(ap->mgr, PR_APP_CONNECT, bc); + } else if (bc->usednr->typ == NR_TYPE_VOIP) { + ap->mode = AP_MODE_VOIP_OCALL; + ret = setup_voip_ocall(ap, bc); + } + return(0); +} + +static int +connect_call(iapplication_t *ap, bchannel_t *bc) +{ + bchannel_t *peer = NULL; + int ret; + + read_rec_ctrlfile(); + if (ap->mode == AP_MODE_INTERN_CALL) { + if (ap->data1 == bc) { + peer = ap->data2; + } else if (ap->data2 == bc) { + peer = ap->data1; + } + if (peer) { + ap->mgr->app_bc(ap->mgr, PR_APP_CONNECT, peer); + bc->rbuf = peer->sbuf; + peer->rbuf = bc->sbuf; + if (bc->sbuf) + bc->sbuf->rsem = &peer->work; + if (peer->sbuf) + peer->sbuf->rsem = &bc->work; + } else { + return(-EINVAL); + } + } else if (ap->mode == AP_MODE_VOIP_OCALL) { + bc = ap->data1; + ap->Flags &= ~AP_FLG_VOIP_ALERTING; + sprintf(bc->display,"connect to %s", bc->usednr->name); + ap->mgr->app_bc(ap->mgr, PR_APP_CONNECT, bc); + } else if (ap->mode == AP_MODE_VOIP_ICALL) { + ret = connect_voip(ap, bc); + if (!ret) { + ap->Flags |= AP_FLG_VOIP_ACTIV; + ap->mgr->app_bc(ap->mgr, PR_APP_CONNECT, bc); + } + return(ret); + } + return(0); +} + +static int +hangup_call(iapplication_t *ap, bchannel_t *bc) +{ + if ((ap->mode == AP_MODE_VOIP_OCALL) || + (ap->mode == AP_MODE_VOIP_ICALL)) { + if (ap->Flags & AP_FLG_VOIP_ACTIV) { + close_voip(ap, bc); + } + return(disconnect_voip(ap, bc)); + } + return(0); +} + +static int +clear_call(iapplication_t *ap, bchannel_t *bc) +{ + bchannel_t *peer = NULL; + + if (ap->mode == AP_MODE_INTERN_CALL) { + if (ap->data1 == bc) { + peer = ap->data2; + ap->data1 = NULL; + } else if (ap->data2 == bc) { + peer = ap->data1; + ap->data2= NULL; + } + bc->rbuf = NULL; + if (bc->sbuf) + bc->sbuf->rsem = &bc->work; + if (peer) { + peer->Flags |= FLG_BC_PROGRESS; + peer->cause_loc = bc->cause_loc; + peer->cause_val = bc->cause_val; + peer->rbuf = NULL; + if (peer->sbuf) + peer->sbuf->rsem = &peer->work; + ap->mgr->app_bc(ap->mgr, PR_APP_HANGUP, peer); + } else { + free_application(ap); + } + if (bc) + bc->app = NULL; + } else if (ap->mode == AP_MODE_AUDIO_CALL) { + if (ap->Flags & AP_FLG_AUDIO_ACTIV) { + close_audio(ap, bc); + ap->Flags &= ~AP_FLG_AUDIO_ACTIV; + ap->vapp->flags &= ~AP_FLG_AUDIO_USED; + } + if (bc) + bc->app = NULL; + free_application(ap); + } else if (ap->mode == AP_MODE_VOIP_OCALL) { + if (ap->Flags & AP_FLG_VOIP_ACTIV) { + close_voip(ap, bc); + } + release_voip(ap, bc); + ap->mode = AP_MODE_IDLE; + free_application(ap); + } else if (ap->mode == AP_MODE_VOIP_ICALL) { + if (ap->Flags & AP_FLG_VOIP_ACTIV) { + close_voip(ap, bc); + } + release_voip(ap, bc); + ap->mode = AP_MODE_IDLE; + free_application(ap); + } + return(0); +} + +static int +alert_call(iapplication_t *ap, bchannel_t *bc) +{ + bchannel_t *peer = NULL; + + if (ap->mode == AP_MODE_VOIP_ICALL) { + return(alert_voip(ap, bc)); + } else if (ap->mode == AP_MODE_INTERN_CALL) { + if (bc != ap->data2) + return(0); + peer = ap->data1; + if (!peer) + return(0); + peer->Flags |= FLG_BC_PROGRESS; + ap->mgr->app_bc(ap->mgr, PR_APP_ALERT, peer); + } + return(0); +} + +static int +facility_info(iapplication_t *ap, bchannel_t *bc) +{ + if ((ap->mode == AP_MODE_VOIP_ICALL) || + (ap->mode == AP_MODE_VOIP_OCALL)) { + return(facility_voip(ap, bc)); + } + return(0); +} + +static int +useruser_info(iapplication_t *ap, bchannel_t *bc) +{ + if ((ap->mode == AP_MODE_VOIP_ICALL) || + (ap->mode == AP_MODE_VOIP_OCALL)) { + return(useruser_voip(ap, bc)); + } + return(0); +} + +static int +open_recfiles(iapplication_t *ap, bchannel_t *bc) +{ + char filename[2048]; + struct timeval tv; + int ret; + + if (!bc) + return(-EINVAL); + if (!RecordFilePath[0]) { + dprint(DBGM_TOPLEVEL, "%s: RecordFilePath not set\n", __FUNCTION__); + return(-EINVAL); + } + gettimeofday(&tv, NULL); + sprintf(filename, "%s%08lx_%02d.r", + RecordFilePath, tv.tv_sec, bc->channel); + dprint(DBGM_TOPLEVEL, "%s: rf.r:%s\n", __FUNCTION__, + filename); + if (bc->rrid > 0) + close(bc->rrid); + bc->rrid = open(filename, O_WRONLY|O_CREAT|O_TRUNC,S_IRWXU); + if (bc->rrid < 0) { + ret = -errno; + dprint(DBGM_TOPLEVEL, "%s: rf.r error %s\n", __FUNCTION__, + strerror(errno)); + + return(ret); + } + filename[strlen(filename)-1] = 's'; + dprint(DBGM_TOPLEVEL, "%s: rf.s:%s\n", __FUNCTION__, + filename); + if (bc->rsid > 0) + close(bc->rsid); + bc->rsid = open(filename, O_WRONLY|O_CREAT|O_TRUNC,S_IRWXU); + if (bc->rsid < 0) { + ret = -errno; + dprint(DBGM_TOPLEVEL, "%s: rf.s error %s\n", __FUNCTION__, + strerror(errno)); + close(bc->rrid); + bc->rrid = -1; + return(ret); + } + bc->Flags |= FLG_BC_RECORDING; + return(0); +} + +static int +close_recfiles(iapplication_t *ap, bchannel_t *bc) +{ + if (!bc) + return(-EINVAL); + if (bc->rrid > 0) + close(bc->rrid); + bc->rrid = -1; + if (bc->rsid > 0) + close(bc->rsid); + bc->rsid = -1; + bc->Flags &= ~FLG_BC_RECORDING; + return(0); +} + +static int +application_handler(manager_t *mgr, int prim, void *arg) +{ + bchannel_t *bc = arg; + iapplication_t *appl = NULL; + + if (!bc) + return(-EINVAL); + if (prim == PR_APP_ICALL) { + appl = new_application(&voip); + if (!appl) + return(-EBUSY); + appl->mgr = mgr; + bc->app = appl; + return(route_call(appl, bc)); + return(-EBUSY); + } + appl = bc->app; + if (!appl) + return(-EINVAL); + if (prim == PR_APP_CONNECT) { + return(connect_call(appl, bc)); + } else if (prim == PR_APP_ALERT) { + return(alert_call(appl, bc)); + } else if (prim == PR_APP_FACILITY) { + return(facility_info(appl, bc)); + } else if (prim == PR_APP_USERUSER) { + return(useruser_info(appl, bc)); + } else if (prim == PR_APP_HANGUP) { + return(hangup_call(appl, bc)); + } else if (prim == PR_APP_CLEAR) { + return(clear_call(appl, bc)); + } else if (prim == PR_APP_OPEN_RECFILES) { + return(open_recfiles(appl, bc)); + } else if (prim == PR_APP_CLOSE_RECFILES) { + return(close_recfiles(appl, bc)); + } + return(-EINVAL); +} + +int main(argc,argv) +int argc; +char *argv[]; + +{ + int ret, *retp; + char host_cfg[MAX_HOST_SIZE+16]; + pthread_t voip_id; + nr_list_t *nr; + + debug_init(global_debug, "testlog", NULL, NULL); + memset(&voip, 0, sizeof(vapplication_t)); + voip.tout.tv_sec = NORMAL_TIMEOUT_s; + voip.tout.tv_usec = NORMAL_TIMEOUT_us; + msg_init(); + ret = init_manager(&voip.mgr_lst, application_handler); + if (ret) { + fprintf(stderr, "error in init_manager %d\n", ret); + exit(1); + } + parse_cfg("voip.cfg", voip.mgr_lst); + if (gethostname(voip.hostname, MAX_HOST_SIZE)) { + fprintf(stderr, "error getting hostname: %s\n", + strerror(errno)); + exit(1); + } + sprintf(host_cfg, "%s.voip.cfg", voip.hostname); + parse_cfg(host_cfg, voip.mgr_lst); + debug_init(global_debug, NULL, NULL, NULL); + voip.port = rtp_port; + voip.flags = default_flags; + voip.debug = global_debug; + fprintf(stderr, "%s: debug(%x) port(%d)\n", __FUNCTION__, + global_debug, rtp_port); + nr = voip.mgr_lst->nrlist; + while(nr) { + dprint(DBGM_TOPLEVEL, "nr(%s) len(%d) flg(%x) typ(%d) name(%s)\n", + nr->nr, nr->len, nr->flags, nr->typ, nr->name); + nr = nr->next; + } + signal(SIGTERM, term_handler); + signal(SIGINT, term_handler); + signal(SIGPIPE, term_handler); + signal(SIGUSR1, sig_usr1_handler); + signal(SIGALRM, SIG_IGN); + read_rec_ctrlfile(); +#if 0 + signal(SIGCHLD, child_handler); +#endif +#if 0 + { + static struct sigaction sa; + + sa.sa_handler = NULL; + sa.sa_restorer = NULL; + sa.sa_sigaction = sig_segfault; + sa.sa_flags = SA_ONESHOT | SA_SIGINFO; + sigemptyset(&sa.sa_mask); + ret = sigaction(SIGSEGV, &sa, NULL); + fprintf(stderr, "sigaction ret(%d)\n", + ret); + } +#endif + voip_id = run_voip(&voip); + retp = do_netthread(voip.mgr_lst->nst); + fprintf(stderr, "do_main_loop returns(%p)\n", retp); + while (voip.mgr_lst) { + manager_t *next = voip.mgr_lst->next; + cleanup_manager(voip.mgr_lst); + voip.mgr_lst = next; + } + voip.flags |= AP_FLG_VOIP_ABORT; + ret = pthread_join(voip_id, (void *)&retp); + fprintf(stderr, "%s: join voipscan ret(%d) rval(%p)\n", __FUNCTION__, + ret, retp); + debug_close(); + return(0); +} diff --git a/voip/voip_isdn_app.c b/voip/voip_isdn_app.c new file mode 100644 index 0000000..2e82f64 --- /dev/null +++ b/voip/voip_isdn_app.c @@ -0,0 +1,509 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "isdn_net.h" +#include "l3dss1.h" +#include "globals.h" +#include "iapplication.h" + + +static void MsgAddIE(msg_t *msg, u_char ie, u_char *iep, int reset) { + int l; + u_char *p; + + if (ie & 0x80) + l = 1; + else { + if (iep && *iep) + l = 2 + *iep; + else + return; + } + p = msg_put(msg, l); + *p++ = ie; + l--; + if (l) { + memcpy(p, iep, l); + if (reset) + *iep = 0; + } +} + +static msg_t * +make_msg_head(int size, u_char mt) { + u_char *p; + msg_t *msg; + + msg = alloc_msg(size); + if (msg) { + p = msg_put(msg, 3); + p++; + p++; + *p++ = mt; + } + return(msg); +} + +int +alert_voip(iapplication_t *ap, bchannel_t *bc) +{ + msg_t *msg; + + if (!ap->con) + return(-EBUSY); + msg = make_msg_head(1024, MT_ALERTING); + if (msg) { + MsgAddIE(msg, IE_FACILITY, bc->fac, 1); + MsgAddIE(msg, IE_DISPLAY, bc->display, 1); + MsgAddIE(msg, IE_USER_USER, bc->uu, 1); + msg_queue_tail(&ap->con->aqueue, msg); + return(SendCtrl(ap)); + } + return(-ENOMEM); +} + +int +facility_voip(iapplication_t *ap, bchannel_t *bc) +{ + msg_t *msg; + + if (!ap->con) + return(-EBUSY); + msg = make_msg_head(1024, MT_FACILITY); + if (msg) { + MsgAddIE(msg, IE_FACILITY, bc->fac, 1); + MsgAddIE(msg, IE_DISPLAY, bc->display, 1); + msg_queue_tail(&ap->con->aqueue, msg); + return(SendCtrl(ap)); + } + return(-ENOMEM); +} + + +int +useruser_voip(iapplication_t *ap, bchannel_t *bc) +{ + msg_t *msg; + + if (!ap->con) + return(-EBUSY); + msg = make_msg_head(1024, MT_USER_INFORMATION); + if (msg) { + MsgAddIE(msg, IE_USER_USER, bc->uu, 1); + msg_queue_tail(&ap->con->aqueue, msg); + return(SendCtrl(ap)); + } + return(-ENOMEM); +} + + +int +connect_voip(iapplication_t *ap, bchannel_t *bc) +{ + int ret; + msg_t *msg; + + if (!ap->con) + return(-EBUSY); + ret = setup_voip(ap, bc); + if (ret) { + bc->cause_loc = CAUSE_LOC_PNET_LOCUSER; + bc->cause_val = CAUSE_NO_ROUTE; + ap->mgr->app_bc(ap->mgr, PR_APP_HANGUP, bc); + return(-EBUSY); + } + msg = make_msg_head(1024, MT_CONNECT); + if (msg) { + MsgAddIE(msg, IE_FACILITY, bc->fac, 1); + MsgAddIE(msg, IE_DISPLAY, bc->display, 1); + MsgAddIE(msg, IE_USER_USER, bc->uu, 1); + msg_queue_tail(&ap->con->aqueue, msg); + return(SendCtrl(ap)); + } + return(-ENOMEM); +} + +int +disconnect_voip(iapplication_t *ap, bchannel_t *bc) +{ + msg_t *msg; + u_char cause[8]; + + if (!ap->con) + return(-EBUSY); + msg = make_msg_head(1024, MT_DISCONNECT); + if (msg) { + cause[0] = 2; + cause[1] = 0x80 | bc->cause_loc; + cause[2] = 0x80 | bc->cause_val; + MsgAddIE(msg, IE_CAUSE, cause, 0); + MsgAddIE(msg, IE_FACILITY, bc->fac, 1); + MsgAddIE(msg, IE_DISPLAY, bc->display, 1); + MsgAddIE(msg, IE_USER_USER, bc->uu, 1); + msg_queue_tail(&ap->con->aqueue, msg); + return(SendCtrl(ap)); + } + return(-ENOMEM); +} + +int +release_voip(iapplication_t *ap, bchannel_t *bc) +{ + msg_t *msg; + u_char cause[8]; + + if (!ap->con) + return(-EBUSY); + msg = make_msg_head(1024, MT_RELEASE); + if (msg) { + cause[0] = 2; + cause[1] = 0x80 | bc->cause_loc; + cause[2] = 0x80 | bc->cause_val; + MsgAddIE(msg, IE_CAUSE, cause, 0); + MsgAddIE(msg, IE_FACILITY, bc->fac, 1); + MsgAddIE(msg, IE_DISPLAY, bc->display, 1); + MsgAddIE(msg, IE_USER_USER, bc->uu, 1); + msg_queue_tail(&ap->con->aqueue, msg); + return(SendCtrl(ap)); + } + return(-ENOMEM); +} + +int +setup_voip_ocall(iapplication_t *ap, bchannel_t *bc) { + msg_t *msg; + struct in_addr addr; + struct hostent *h; + + if (!ap->con) { + if ((addr.s_addr = inet_addr(bc->usednr->name)) == -1) { + h = gethostbyname(bc->usednr->name); + if (!h) { + return(-ENXIO); + } + memcpy(&addr.s_addr, h->h_addr, sizeof(addr.s_addr)); + } + ap->con = new_connection(ap, &addr); + if (!ap->con) { + return(-ENOMEM); + } + if (bc->usednr->flags & FLAG_GSM) { + ap->con->pkt_size = 640; + ap->con->sndflags |= SNDFLG_COMPR_GSM; + } + ap->con->own_ssrc = getnew_ssrc(ap->vapp); + } + msg = make_msg_head(1024, MT_SETUP); + if (msg) { + MsgAddIE(msg, IE_FACILITY, bc->fac, 1); + MsgAddIE(msg, IE_BEARER, bc->bc, 0); + MsgAddIE(msg, IE_DISPLAY, bc->display, 1); + MsgAddIE(msg, IE_CALLING_PN, bc->msn, 0); + MsgAddIE(msg, IE_CALLING_SUB, bc->clisub, 1); + MsgAddIE(msg, IE_CALLED_PN, bc->nr, 0); + MsgAddIE(msg, IE_CALLED_SUB, bc->cldsub, 1); + MsgAddIE(msg, IE_USER_USER, bc->uu, 1); + msg_queue_tail(&ap->con->aqueue, msg); + return(SendCtrl(ap)); + } + return(-ENOMEM); +} + +static int +parse_isdn_extra(unsigned char *arg, int len, bchannel_t *bc) +{ + unsigned char *p; + + p = findie(arg, len, IE_DISPLAY, 0); + if (p) { + memcpy(bc->display, p + 1, *p); + bc->display[*p] = 0; + } + p = findie(arg, len, IE_USER_USER, 0); + if (p) + memcpy(bc->uu, p, *p + 1); + p = findie(arg, len, IE_FACILITY, 0); + if (p) + memcpy(bc->fac, p, *p + 1); + return(0); +} + +static int +parse_isdn_setup(iapplication_t *appl, unsigned char *arg, int len) +{ + manager_t *mgr = appl->vapp->mgr_lst; + unsigned char *own, *p; + nr_list_t *nrx; + bchannel_t *bc = NULL; + int ret; + + if (appl->mgr) { /* allready setup */ + SendCtrl(appl); + return(-EINVAL); + } + own = findie(arg, len, IE_CALLED_PN, 0); + if (!own) { + SendCtrl(appl); + return(-EINVAL); + } + while(mgr) { + nrx = NULL; + if (!match_nr(mgr, own, &nrx)) { + ret = mgr->app_bc(mgr, PR_APP_OCHANNEL, &bc); + if (0 >= ret) + eprint( "%s: no free channel ret(%d)\n", __FUNCTION__, + ret); + if (!bc) { + eprint( "%s: no free channel\n", __FUNCTION__); + } else { + appl->mgr = mgr; + appl->data1 = bc; + appl->mode = AP_MODE_VOIP_ICALL; + bc->app = appl; + bc->usednr = nrx; + break; + } + } + mgr = mgr->next; + } + if (!mgr) { + } else { + p = findie(arg, len, IE_CALLING_PN, 0); + if (p) + memcpy(bc->nr, p, *p + 1); + p = findie(arg, len, IE_CALLING_SUB, 0); + if (p) + memcpy(bc->clisub, p, *p + 1); + p = findie(arg, len, IE_CALLED_SUB, 0); + if (p) + memcpy(bc->cldsub, p, *p + 1); + parse_isdn_extra(arg, len, bc); + bc->Flags |= FLG_BC_APPLICATION; + memcpy(bc->msn, own, own[0] + 1); + bc->l1_prot = ISDN_PID_L1_B_64TRANS; + if (!bc->display[0] && appl->con) { + strcpy(bc->display, appl->con->con_hostname); + } + mgr->app_bc(mgr, PR_APP_OCALL, bc); + } + SendCtrl(appl); + return(0); +} + +static int +parse_isdn_alert(iapplication_t *appl, unsigned char *arg, int len) +{ + bchannel_t *bc = appl->data1; + + if (!appl->mgr || !bc) { + SendCtrl(appl); + return(-EINVAL); + } + parse_isdn_extra(arg, len, bc); + if (!bc->display[0] && bc->usednr) { + strcpy(bc->display, bc->usednr->name); + } + bc->Flags |= FLG_BC_PROGRESS; + appl->mgr->app_bc(appl->mgr, PR_APP_ALERT, bc); + SendCtrl(appl); + return(0); +} + +int +parse_isdn_connect(iapplication_t *appl, unsigned char *arg, int len) +{ + bchannel_t *bc = appl->data1; + int ret; + + if (!appl->mgr || !bc) { + SendCtrl(appl); + return(-EINVAL); + } + parse_isdn_extra(arg, len, bc); + ret = setup_voip(appl, bc); + if (ret) { + bc->cause_loc = CAUSE_LOC_PNET_LOCUSER; + bc->cause_val = CAUSE_NO_ROUTE; + appl->mgr->app_bc(appl->mgr, PR_APP_HANGUP, bc); + return(0); + } + appl->Flags |= AP_FLG_VOIP_ACTIV; + appl->mgr->app_bc(appl->mgr, PR_APP_CONNECT, bc); + SendCtrl(appl); + return(0); +} + +static int +parse_isdn_disc(iapplication_t *appl, unsigned char *arg, int len) +{ + bchannel_t *bc = appl->data1; + unsigned char *p; + + if (!appl->mgr || !bc) { + SendCtrl(appl); + return(-EINVAL); + } + p = findie(arg, len, IE_CAUSE, 0); + if (p) { + if (*p++ > 1) { + bc->cause_loc = *p++ & 0xf; + bc->cause_val = *p++ & 0x7f; + } else { + bc->cause_loc = CAUSE_LOC_PNET_LOCUSER; + bc->cause_val = CAUSE_NORMAL_CLEARING; + } + } else { + bc->cause_loc = CAUSE_LOC_PNET_LOCUSER; + bc->cause_val = CAUSE_NORMAL_CLEARING; + } + parse_isdn_extra(arg, len, bc); + if (appl->Flags & AP_FLG_VOIP_ACTIV) { + close_voip(appl, bc); + } + bc->Flags |= FLG_BC_PROGRESS; + appl->mgr->app_bc(appl->mgr, PR_APP_HANGUP, bc); + SendCtrl(appl); + return(0); +} + +static int +parse_isdn_release(iapplication_t *appl, unsigned char *arg, int len) +{ + bchannel_t *bc = appl->data1; + unsigned char *p; + + if (!appl->mgr || !bc) { + SendCtrl(appl); + return(-EINVAL); + } + p = findie(arg, len, IE_CAUSE, 0); + if (p) { + if (*p++ > 1) { + bc->cause_loc = *p++ & 0xf; + bc->cause_val = *p++ & 0x7f; + } else { + bc->cause_loc = CAUSE_LOC_PNET_LOCUSER; + bc->cause_val = CAUSE_NORMAL_CLEARING; + } + } else { + bc->cause_loc = CAUSE_LOC_PNET_LOCUSER; + bc->cause_val = CAUSE_NORMAL_CLEARING; + } + parse_isdn_extra(arg, len, bc); + if (appl->Flags & AP_FLG_VOIP_ACTIV) { + close_voip(appl, bc); + } + bc->Flags |= FLG_BC_PROGRESS; + appl->mgr->app_bc(appl->mgr, PR_APP_HANGUP, bc); + SendCtrl(appl); + return(0); +} + +static int +parse_isdn_uinfo(iapplication_t *appl, unsigned char *arg, int len) +{ + bchannel_t *bc = appl->data1; + + if (!appl->mgr || !bc) { + SendCtrl(appl); + return(-EINVAL); + } + parse_isdn_extra(arg, len, bc); + appl->mgr->app_bc(appl->mgr, PR_APP_USERUSER, bc); + SendCtrl(appl); + return(0); +} + +static int +parse_isdn_fac(iapplication_t *appl, unsigned char *arg, int len) +{ + bchannel_t *bc = appl->data1; + + if (!appl->mgr || !bc) { + SendCtrl(appl); + return(-EINVAL); + } + parse_isdn_extra(arg, len, bc); + appl->mgr->app_bc(appl->mgr, PR_APP_FACILITY, bc); + SendCtrl(appl); + return(0); +} + +static int +parse_isdn_packet(iapplication_t *appl, unsigned char *arg) { + unsigned char *p, oc, pc, pr, a_pc; + int len; + vconnection_t *con; + + con = appl->con; + if (!con) + return(-EINVAL); + p = arg + 2; + len = ntohs(*((unsigned short *)p)); + len *= 4; + p = arg + 12; + len -= 8; + if (len<=0) + return(-EINVAL); + + oc = *p++; + pc = *p; + *p++ = 0; /* to use L3 findie, fake a dummy CR L3 frame */ + pr = *p++; + dprint(DBGM_ISDN, "%s: pr(%02x) own(%d/%d) peer(%d/%d)\n", __FUNCTION__, + pr, oc, con->oc, pc, con->pc); + a_pc = con->pc; + a_pc++; + if (con->oc == oc) { + if (con->amsg) { + free_msg(con->amsg); + con->amsg = NULL; + } + } + if (a_pc != pc) { + } else + con->pc = pc; + + if (pr == 0) { /* escape to private MTs */ + pr = *p++; + if (pr == 0x81) { /* RR */ + return(0); + } + return(-EINVAL); + } else if (pr == MT_SETUP) { + return(parse_isdn_setup(appl, arg + 12, len)); + } else if (pr == MT_ALERTING) { + return(parse_isdn_alert(appl, arg + 12, len)); + } else if (pr == MT_CONNECT) { + return(parse_isdn_connect(appl, arg + 12, len)); + } else if (pr == MT_DISCONNECT) { + return(parse_isdn_disc(appl, arg + 12, len)); + } else if (pr == MT_RELEASE) { + return(parse_isdn_release(appl, arg + 12, len)); + } else if (pr == MT_USER_INFORMATION) { + return(parse_isdn_uinfo(appl, arg + 12, len)); + } else if (pr == MT_FACILITY) { + return(parse_isdn_fac(appl, arg + 12, len)); + } + return(0); +} + +int +voip_application_handler(iapplication_t *appl, int prim, unsigned char *arg) { + + dprint(DBGM_APPL, "%s(%p, %x, %p)\n", __FUNCTION__, + appl, prim, arg); + + if (prim == AP_PR_VOIP_NEW) { + + } else if (prim == AP_PR_VOIP_ISDN) { + return(parse_isdn_packet(appl, arg)); + } + return(-EINVAL); +} + diff --git a/voip/voip_timer.c b/voip/voip_timer.c new file mode 100644 index 0000000..7b06bbe --- /dev/null +++ b/voip/voip_timer.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include +#include "vitimer.h" + +static vi_timer_t *timerlist = NULL; + +static void +ins_vitimer(vi_timer_t *iti) { + iti->prev = timerlist; + + iti->next = NULL; + if (timerlist) { + if (timercmp(&timerlist->tv, &iti->tv, >)) { + iti->prev = NULL; + iti->next = timerlist; + } + } + while(iti->prev && iti->prev->next) { + if (timercmp(&iti->prev->next->tv, &iti->tv, >)) + break; + iti->prev = iti->prev->next; + } + if (iti->prev) { + iti->next = iti->prev->next; + iti->prev->next = iti; + } else { + timerlist = iti; + } + if (iti->next) + iti->next->prev = iti; +} + +int +run_vitimer(void) +{ + int cnt = 0; + struct timeval act, del; + + gettimeofday(&act, NULL); + while(timerlist) { + if (timercmp(&timerlist->tv, &act, >)) + break; + timersub(&act, &timerlist->tv, &del); + timerlist->func(timerlist->data, timerlist->val, &del); + remove_vitimer(timerlist); + } + return(cnt); +} + +void +remove_vitimer(vi_timer_t *iti) { + if (iti->prev) + iti->prev->next = iti->next; + if (iti->next) + iti->next->prev = iti->prev; + if (timerlist == iti) + timerlist = iti->next; + iti->prev = NULL; + iti->next = NULL; +} + +int +init_vitimer(vi_timer_t *iti, void *data, unsigned long val, timef_t f) +{ + if (!iti) { + return(-EINVAL); + } + iti->data = data; + iti->val = val; + iti->func = f; + return(0); +} + +int +add_vitimer_abs(vi_timer_t *iti, struct timeval *tv) +{ + if (!iti) { + return(-EINVAL); + } + iti->tv = *tv; + ins_vitimer(iti); + run_vitimer(); + return(0); +} + + +int +add_vitimer_rel(vi_timer_t *iti, struct timeval *tv) +{ + struct timeval act; + + gettimeofday(&act, NULL); + if (!iti) { + return(-EINVAL); + } + timeradd(&act, tv, &iti->tv); + ins_vitimer(iti); + run_vitimer(); + return(0); +} + + +void +clean_vitimer(void) { + while(timerlist) + remove_vitimer(timerlist); +} + +struct timeval +*get_next_vitimer_time(void) +{ + if (timerlist) + return(&timerlist->tv); + else + return(NULL); +} + +int +get_next_vitimer_dist(struct timeval *tv) +{ + struct timeval act; + + if (!tv) + return(-EINVAL); + if (!timerlist) + return(-EINVAL); + gettimeofday(&act, NULL); + if (timercmp(&timerlist->tv, &act, <)) { + tv->tv_sec = 0; + tv->tv_usec = 0; + } else + timersub(&timerlist->tv, &act, tv); + return(0); +} diff --git a/voip/voipisdn.spec b/voip/voipisdn.spec new file mode 100644 index 0000000..d95a918 --- /dev/null +++ b/voip/voipisdn.spec @@ -0,0 +1,53 @@ +Vendor: SuSE GmbH, Nuernberg, Germany +Distribution: SuSE Linux 7.3 (i386) +Name: voipisdn +Packager: feedback@suse.de +Copyright: GPL +Group: Applications/Communications +Provides: voipisdn +Autoreqprov: on +Version: 20030423 +Release: 1 +Summary: Voice Communication Over Data Networks +Source1: hisax_voip-%{version}.tar.bz2 +Source2: gsm-1.0.7.tar.gz +BuildRoot: /var/tmp/%{name}-build + +%description +Voice Communication Over Data TCP/IP Networks ISDN gateway + +Authors: +-------- + Karsten Keil + +SuSE series: net + +%prep +%setup -T -c -n voipisdn +%setup -n voipisdn -D -T -a 1 +%setup -n voipisdn -D -T -a 2 + +%build + +cd gsm-1.0-pl6 +make +cd .. + +cd hisax +make all + +%install +rm -rf RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT/usr/bin +install -c -s -m 755 hisax/voip/voipisdn $RPM_BUILD_ROOT/usr/bin/ + +%{?suse_check} + +%clean +rm -rf RPM_BUILD_ROOT + + +%files +%defattr(-,root,root) +/usr/bin/* +