initial checkin of Osmocom E1TS (E1 timeslot) test port for TITAN

Change-Id: Ib03a6a5b3d7e4864bf797e4a69b87a64515dd953
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1bba67a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.o
+*.d
+*.so
+compile
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3534f2f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,277 @@
+Eclipse Public License - v 2.0

+

+    THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE

+    PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION

+    OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.

+

+1. DEFINITIONS

+

+"Contribution" means:

+

+  a) in the case of the initial Contributor, the initial content

+     Distributed under this Agreement, and

+

+  b) in the case of each subsequent Contributor:

+     i) changes to the Program, and

+     ii) additions to the Program;

+  where such changes and/or additions to the Program originate from

+  and are Distributed by that particular Contributor. A Contribution

+  "originates" from a Contributor if it was added to the Program by

+  such Contributor itself or anyone acting on such Contributor's behalf.

+  Contributions do not include changes or additions to the Program that

+  are not Modified Works.

+

+"Contributor" means any person or entity that Distributes the Program.

+

+"Licensed Patents" mean patent claims licensable by a Contributor which

+are necessarily infringed by the use or sale of its Contribution alone

+or when combined with the Program.

+

+"Program" means the Contributions Distributed in accordance with this

+Agreement.

+

+"Recipient" means anyone who receives the Program under this Agreement

+or any Secondary License (as applicable), including Contributors.

+

+"Derivative Works" shall mean any work, whether in Source Code or other

+form, that is based on (or derived from) the Program and for which the

+editorial revisions, annotations, elaborations, or other modifications

+represent, as a whole, an original work of authorship.

+

+"Modified Works" shall mean any work in Source Code or other form that

+results from an addition to, deletion from, or modification of the

+contents of the Program, including, for purposes of clarity any new file

+in Source Code form that contains any contents of the Program. Modified

+Works shall not include works that contain only declarations,

+interfaces, types, classes, structures, or files of the Program solely

+in each case in order to link to, bind by name, or subclass the Program

+or Modified Works thereof.

+

+"Distribute" means the acts of a) distributing or b) making available

+in any manner that enables the transfer of a copy.

+

+"Source Code" means the form of a Program preferred for making

+modifications, including but not limited to software source code,

+documentation source, and configuration files.

+

+"Secondary License" means either the GNU General Public License,

+Version 2.0, or any later versions of that license, including any

+exceptions or additional permissions as identified by the initial

+Contributor.

+

+2. GRANT OF RIGHTS

+

+  a) Subject to the terms of this Agreement, each Contributor hereby

+  grants Recipient a non-exclusive, worldwide, royalty-free copyright

+  license to reproduce, prepare Derivative Works of, publicly display,

+  publicly perform, Distribute and sublicense the Contribution of such

+  Contributor, if any, and such Derivative Works.

+

+  b) Subject to the terms of this Agreement, each Contributor hereby

+  grants Recipient a non-exclusive, worldwide, royalty-free patent

+  license under Licensed Patents to make, use, sell, offer to sell,

+  import and otherwise transfer the Contribution of such Contributor,

+  if any, in Source Code or other form. This patent license shall

+  apply to the combination of the Contribution and the Program if, at

+  the time the Contribution is added by the Contributor, such addition

+  of the Contribution causes such combination to be covered by the

+  Licensed Patents. The patent license shall not apply to any other

+  combinations which include the Contribution. No hardware per se is

+  licensed hereunder.

+

+  c) Recipient understands that although each Contributor grants the

+  licenses to its Contributions set forth herein, no assurances are

+  provided by any Contributor that the Program does not infringe the

+  patent or other intellectual property rights of any other entity.

+  Each Contributor disclaims any liability to Recipient for claims

+  brought by any other entity based on infringement of intellectual

+  property rights or otherwise. As a condition to exercising the

+  rights and licenses granted hereunder, each Recipient hereby

+  assumes sole responsibility to secure any other intellectual

+  property rights needed, if any. For example, if a third party

+  patent license is required to allow Recipient to Distribute the

+  Program, it is Recipient's responsibility to acquire that license

+  before distributing the Program.

+

+  d) Each Contributor represents that to its knowledge it has

+  sufficient copyright rights in its Contribution, if any, to grant

+  the copyright license set forth in this Agreement.

+

+  e) Notwithstanding the terms of any Secondary License, no

+  Contributor makes additional grants to any Recipient (other than

+  those set forth in this Agreement) as a result of such Recipient's

+  receipt of the Program under the terms of a Secondary License

+  (if permitted under the terms of Section 3).

+

+3. REQUIREMENTS

+

+3.1 If a Contributor Distributes the Program in any form, then:

+

+  a) the Program must also be made available as Source Code, in

+  accordance with section 3.2, and the Contributor must accompany

+  the Program with a statement that the Source Code for the Program

+  is available under this Agreement, and informs Recipients how to

+  obtain it in a reasonable manner on or through a medium customarily

+  used for software exchange; and

+

+  b) the Contributor may Distribute the Program under a license

+  different than this Agreement, provided that such license:

+     i) effectively disclaims on behalf of all other Contributors all

+     warranties and conditions, express and implied, including

+     warranties or conditions of title and non-infringement, and

+     implied warranties or conditions of merchantability and fitness

+     for a particular purpose;

+

+     ii) effectively excludes on behalf of all other Contributors all

+     liability for damages, including direct, indirect, special,

+     incidental and consequential damages, such as lost profits;

+

+     iii) does not attempt to limit or alter the recipients' rights

+     in the Source Code under section 3.2; and

+

+     iv) requires any subsequent distribution of the Program by any

+     party to be under a license that satisfies the requirements

+     of this section 3.

+

+3.2 When the Program is Distributed as Source Code:

+

+  a) it must be made available under this Agreement, or if the

+  Program (i) is combined with other material in a separate file or

+  files made available under a Secondary License, and (ii) the initial

+  Contributor attached to the Source Code the notice described in

+  Exhibit A of this Agreement, then the Program may be made available

+  under the terms of such Secondary Licenses, and

+

+  b) a copy of this Agreement must be included with each copy of

+  the Program.

+

+3.3 Contributors may not remove or alter any copyright, patent,

+trademark, attribution notices, disclaimers of warranty, or limitations

+of liability ("notices") contained within the Program from any copy of

+the Program which they Distribute, provided that Contributors may add

+their own appropriate notices.

+

+4. COMMERCIAL DISTRIBUTION

+

+Commercial distributors of software may accept certain responsibilities

+with respect to end users, business partners and the like. While this

+license is intended to facilitate the commercial use of the Program,

+the Contributor who includes the Program in a commercial product

+offering should do so in a manner which does not create potential

+liability for other Contributors. Therefore, if a Contributor includes

+the Program in a commercial product offering, such Contributor

+("Commercial Contributor") hereby agrees to defend and indemnify every

+other Contributor ("Indemnified Contributor") against any losses,

+damages and costs (collectively "Losses") arising from claims, lawsuits

+and other legal actions brought by a third party against the Indemnified

+Contributor to the extent caused by the acts or omissions of such

+Commercial Contributor in connection with its distribution of the Program

+in a commercial product offering. The obligations in this section do not

+apply to any claims or Losses relating to any actual or alleged

+intellectual property infringement. In order to qualify, an Indemnified

+Contributor must: a) promptly notify the Commercial Contributor in

+writing of such claim, and b) allow the Commercial Contributor to control,

+and cooperate with the Commercial Contributor in, the defense and any

+related settlement negotiations. The Indemnified Contributor may

+participate in any such claim at its own expense.

+

+For example, a Contributor might include the Program in a commercial

+product offering, Product X. That Contributor is then a Commercial

+Contributor. If that Commercial Contributor then makes performance

+claims, or offers warranties related to Product X, those performance

+claims and warranties are such Commercial Contributor's responsibility

+alone. Under this section, the Commercial Contributor would have to

+defend claims against the other Contributors related to those performance

+claims and warranties, and if a court requires any other Contributor to

+pay any damages as a result, the Commercial Contributor must pay

+those damages.

+

+5. NO WARRANTY

+

+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT

+PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"

+BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR

+IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF

+TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR

+PURPOSE. Each Recipient is solely responsible for determining the

+appropriateness of using and distributing the Program and assumes all

+risks associated with its exercise of rights under this Agreement,

+including but not limited to the risks and costs of program errors,

+compliance with applicable laws, damage to or loss of data, programs

+or equipment, and unavailability or interruption of operations.

+

+6. DISCLAIMER OF LIABILITY

+

+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT

+PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS

+SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,

+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST

+PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN

+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)

+ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE

+EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE

+POSSIBILITY OF SUCH DAMAGES.

+

+7. GENERAL

+

+If any provision of this Agreement is invalid or unenforceable under

+applicable law, it shall not affect the validity or enforceability of

+the remainder of the terms of this Agreement, and without further

+action by the parties hereto, such provision shall be reformed to the

+minimum extent necessary to make such provision valid and enforceable.

+

+If Recipient institutes patent litigation against any entity

+(including a cross-claim or counterclaim in a lawsuit) alleging that the

+Program itself (excluding combinations of the Program with other software

+or hardware) infringes such Recipient's patent(s), then such Recipient's

+rights granted under Section 2(b) shall terminate as of the date such

+litigation is filed.

+

+All Recipient's rights under this Agreement shall terminate if it

+fails to comply with any of the material terms or conditions of this

+Agreement and does not cure such failure in a reasonable period of

+time after becoming aware of such noncompliance. If all Recipient's

+rights under this Agreement terminate, Recipient agrees to cease use

+and distribution of the Program as soon as reasonably practicable.

+However, Recipient's obligations under this Agreement and any licenses

+granted by Recipient relating to the Program shall continue and survive.

+

+Everyone is permitted to copy and distribute copies of this Agreement,

+but in order to avoid inconsistency the Agreement is copyrighted and

+may only be modified in the following manner. The Agreement Steward

+reserves the right to publish new versions (including revisions) of

+this Agreement from time to time. No one other than the Agreement

+Steward has the right to modify this Agreement. The Eclipse Foundation

+is the initial Agreement Steward. The Eclipse Foundation may assign the

+responsibility to serve as the Agreement Steward to a suitable separate

+entity. Each new version of the Agreement will be given a distinguishing

+version number. The Program (including Contributions) may always be

+Distributed subject to the version of the Agreement under which it was

+received. In addition, after a new version of the Agreement is published,

+Contributor may elect to Distribute the Program (including its

+Contributions) under the new version.

+

+Except as expressly stated in Sections 2(a) and 2(b) above, Recipient

+receives no rights or licenses to the intellectual property of any

+Contributor under this Agreement, whether expressly, by implication,

+estoppel or otherwise. All rights in the Program not expressly granted

+under this Agreement are reserved. Nothing in this Agreement is intended

+to be enforceable by any entity that is not a Contributor or Recipient.

+No third-party beneficiary rights are created under this Agreement.

+

+Exhibit A - Form of Secondary Licenses Notice

+

+"This Source Code may also be made available under the following 

+Secondary Licenses when the conditions for such availability set forth 

+in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),

+version(s), and exceptions or additional permissions here}."

+

+  Simply including a copy of this Agreement, including this Exhibit A

+  is not sufficient to license the Source Code under Secondary Licenses.

+

+  If it is not possible or desirable to put the notice in a particular

+  file, then You may include the notice in a location (such as a LICENSE

+  file in a relevant directory) where a recipient would be likely to

+  look for such a notice.

+

+  You may add additional accurate notices of copyright ownership.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..111e0da
--- /dev/null
+++ b/README.md
@@ -0,0 +1,34 @@
+titan.TestPorts.E1TS
+====================
+
+This implements a TTCN-3 E1 Timeslot test port for Eclipse TITAN.  Running on a (Linux) computer,
+it allows you to write TTCN-3 tests sending and receiving data on 64kBps E1 timeslots.
+
+In order to interface both physical and virtual E1 lines, this port currently relies on
+osmo-e1d, the Osmocom E1 daemon [1].  Adding back-ends for other drivers such as DAHDI should
+not be hard, if needed at a later point.
+
+The timeslots can either be opened in RAW (transparent) 64kBps mode, or with a HDLC controller.
+
+The idea of this module is to be able to write abstract test suites in TTCN-3 which interface
+IUT (Implementations under Test) over E1.
+
+
+GIT repository
+--------------
+
+You can clone from the official titan.TestPorts.E1TS repository using
+
+	git clone https://git.osmocom.org/titan.TestPorts.E1TS
+
+There's a cgit interface at <https://cgit.osmocom.org/titan.TestPorts.E1TS/>
+
+Documentation
+-------------
+
+This is still very much a Work-In-Progress, and hence there's no documentation yet, sorry.
+
+References
+----------
+
+[1] osmo-e1d homepage at https://osmocom.org/projects/osmo-e1d/wiki
diff --git a/example/E1TS_PT.cc b/example/E1TS_PT.cc
new file mode 120000
index 0000000..6db7ab4
--- /dev/null
+++ b/example/E1TS_PT.cc
@@ -0,0 +1 @@
+../src/E1TS_PT.cc
\ No newline at end of file
diff --git a/example/E1TS_PT.hh b/example/E1TS_PT.hh
new file mode 120000
index 0000000..7a5df55
--- /dev/null
+++ b/example/E1TS_PT.hh
@@ -0,0 +1 @@
+../src/E1TS_PT.hh
\ No newline at end of file
diff --git a/example/E1TS_PortType.ttcn b/example/E1TS_PortType.ttcn
new file mode 120000
index 0000000..b30bdda
--- /dev/null
+++ b/example/E1TS_PortType.ttcn
@@ -0,0 +1 @@
+../src/E1TS_PortType.ttcn
\ No newline at end of file
diff --git a/example/E1TS_PortTypes.ttcn b/example/E1TS_PortTypes.ttcn
new file mode 120000
index 0000000..049f24c
--- /dev/null
+++ b/example/E1TS_PortTypes.ttcn
@@ -0,0 +1 @@
+../src/E1TS_PortTypes.ttcn
\ No newline at end of file
diff --git a/example/E1TS_Test.ttcn b/example/E1TS_Test.ttcn
new file mode 100644
index 0000000..5bed3e4
--- /dev/null
+++ b/example/E1TS_Test.ttcn
@@ -0,0 +1,42 @@
+module E1TS_Test {
+
+import from E1TS_PortType all;
+import from E1TS_PortTypes all;
+
+type component test_CT {
+	port E1TS_PT E1;
+}
+
+testcase TC_selftest() runs on test_CT {
+	map(self:E1, system:E1);
+	var template (value) E1TS_identity ts_id := ts_E1TS_ID(0,0,2);
+
+	E1.send(ts_E1TS_open(23, ts_id, E1TS_MODE_RAW, "e1d"));
+	E1.receive(tr_E1TS_result(23, 0));
+
+	var integer rx_count := 0, rx_bytes := 0;
+	var E1TS_unitdata rx_ud;
+	timer T := 5.0;
+	T.start;
+	while (true) {
+		alt {
+		[] E1.receive(tr_E1TS_unitdata(ts_id,?)) -> value rx_ud {
+			rx_bytes := rx_bytes + lengthof(rx_ud.data);
+			rx_count := rx_count + 1;
+			repeat;
+			}
+		[] T.timeout {
+			log(rx_count, " messages received, total bytes ", rx_bytes);
+			mtc.stop
+			};
+		}
+	}
+}
+
+control {
+	execute( TC_selftest() );
+}
+
+
+
+}
diff --git a/example/Makefile b/example/Makefile
new file mode 100644
index 0000000..8028ef2
--- /dev/null
+++ b/example/Makefile
@@ -0,0 +1,241 @@
+# This Makefile was generated by the Makefile Generator
+# of the TTCN-3 Test Executor version CRL 113 200/6 R6B
+# for Harald Welte (laforge@nataraja) on Thu Jul  2 18:32:11 2020
+
+# Copyright (c) 2000-2019 Ericsson Telecom AB
+
+# The following make commands are available:
+# - make, make all      Builds the executable test suite.
+# - make archive        Archives all source files.
+# - make check          Checks the semantics of TTCN-3 and ASN.1modules.
+# - make port           Generates port skeletons.
+# - make clean          Removes all generated files.
+# - make compile        Translates TTCN-3 and ASN.1 modules to C++.
+# - make dep            Creates/updates dependency list.
+# - make executable     Builds the executable test suite.
+# - make library        Builds the library archive.
+# - make objects        Builds the object files without linking the executable.
+# - make shared_objects Builds the shared object files without linking the executable.
+# - make preprocess     Preprocess TTCN-3 files.
+# WARNING! This Makefile can be used with GNU make only.
+# Other versions of make may report syntax errors in it.
+
+#
+# Do NOT touch this line...
+#
+.PHONY: all shared_objects executable library objects check port clean dep archive preprocess
+
+.SUFFIXES: .d
+
+#
+# Set these variables...
+#
+
+ifndef TTCN3_DIR
+ifneq (,$(wildcard /usr/include/titan/))
+TTCN3_DIR = /usr
+TTCN3_SUBDIR = /titan
+else
+TTCN3_DIR = /usr
+TTCN3_SUBDIR = 
+endif
+endif
+
+# Your platform: (SOLARIS, SOLARIS8, LINUX, FREEBSD or WIN32)
+PLATFORM = LINUX
+
+# Your C++ compiler:
+# (if you change the platform, you may need to change the compiler)
+CXX = env CCACHE_SLOPPINESS=time_macros ccache g++
+
+# C preprocessor used for TTCN-3 files:
+CPP = cpp
+
+# Flags for the C++ preprocessor (and makedepend as well):
+CPPFLAGS = -D$(PLATFORM) -DMAKEDEPEND_RUN -DUSE_SCTP -I$(TTCN3_DIR)/include$(TTCN3_SUBDIR) $(shell pkg-config --cflags libosmo-e1d libosmocore)
+
+# Flags for dependency generation
+CXXDEPFLAGS = -MM
+
+# Flags for preprocessing TTCN-3 files:
+CPPFLAGS_TTCN3 = $(shell pkg-config --cflags libosmo-e1d libosmocore)
+
+# Flags for the C++ compiler:
+CXXFLAGS =  -fPIC  
+
+# Flags for the linker:
+LDFLAGS = -L /usr/lib/titan -fPIC 
+
+ifeq ($(PLATFORM), WIN32)
+# Silence linker warnings.
+LDFLAGS += -Wl,--enable-auto-import,--enable-runtime-pseudo-reloc
+endif
+
+# Utility to create library files
+AR = ar
+ARFLAGS = 
+
+# Flags for the TTCN-3 and ASN.1 compiler:
+COMPILER_FLAGS = -L -U 5 -D
+
+# Execution mode: (either ttcn3 or ttcn3-parallel)
+TTCN3_LIB = ttcn3-parallel-dynamic
+
+# The path of your libxml2 installation:
+# If you do not have your own one, leave it unchanged.
+XMLDIR = $(TTCN3_DIR)
+
+# Directory to store the archived source files:
+ARCHIVE_DIR = backup
+
+#
+# You may change these variables. Add your files if necessary...
+#
+
+# TTCN-3 modules of this project:
+TTCN3_MODULES = E1TS_Test.ttcn E1TS_PortType.ttcn E1TS_PortTypes.ttcn
+
+# TTCN-3 modules to preprocess:
+TTCN3_PP_MODULES =
+
+# Files to include in TTCN-3 preprocessed modules:
+TTCN3_INCLUDES =
+
+# ASN.1 modules of this project:
+ASN1_MODULES =
+
+# TTCN-3 source files generated by the C preprocessor:
+PREPROCESSED_TTCN3_MODULES =
+
+# C++ source & header files generated from the TTCN-3 & ASN.1 modules of
+# this project:
+GENERATED_SOURCES = $(TTCN3_MODULES:.ttcn=.cc) E1TS_Test_part_1.cc E1TS_Test_part_2.cc E1TS_Test_part_3.cc E1TS_Test_part_4.cc E1TS_PortType_part_1.cc E1TS_PortType_part_2.cc E1TS_PortType_part_3.cc E1TS_PortType_part_4.cc E1TS_PortTypes_part_1.cc E1TS_PortTypes_part_2.cc E1TS_PortTypes_part_3.cc E1TS_PortTypes_part_4.cc $(TTCN3_PP_MODULES:.ttcnpp=.cc) $(ASN1_MODULES:.asn=.cc)
+GENERATED_HEADERS = $(TTCN3_MODULES:.ttcn=.hh) $(TTCN3_PP_MODULES:.ttcnpp=.hh) $(ASN1_MODULES:.asn=.hh)
+
+# C/C++ Source & header files of Test Ports, external functions and
+# other modules:
+USER_SOURCES = E1TS_PT.cc
+USER_HEADERS = $(USER_SOURCES:.cc=.hh)
+
+# Shared object files of this project:
+SHARED_OBJECTS = $(GENERATED_SOURCES:.cc=.so) $(USER_SOURCES:.cc=.so)
+
+# Object files of this project that are needed for the executable test suite:
+OBJECTS = $(GENERATED_OBJECTS) $(USER_OBJECTS)
+
+GENERATED_OBJECTS = $(GENERATED_SOURCES:.cc=.o)
+
+USER_OBJECTS = $(USER_SOURCES:.cc=.o)
+
+DEPFILES = $(USER_OBJECTS:.o=.d)  $(GENERATED_OBJECTS:.o=.d)
+
+# Other files of the project (Makefile, configuration files, etc.)
+# that will be added to the archived source files:
+OTHER_FILES = Makefile
+
+# The name of the executable test suite:
+EXECUTABLE = E1TS_Test
+
+
+
+LIBRARY = lib$(EXECUTABLE).so
+
+TARGET = $(EXECUTABLE)
+
+#
+# Do not modify these unless you know what you are doing...
+# Platform specific additional libraries:
+#
+SOLARIS_LIBS = -lsocket -lnsl -lxml2
+SOLARIS8_LIBS = -lsocket -lnsl -lxml2
+LINUX_LIBS = -lxml2 $(shell pkg-config --libs libosmo-e1d libosmocore)
+FREEBSD_LIBS = -lxml2
+WIN32_LIBS = -lxml2
+
+#
+# Rules for building the executable...
+#
+
+all: $(TARGET) ;
+
+shared_objects: $(SHARED_OBJECTS) ;
+
+executable: $(EXECUTABLE) ;
+
+library: $(LIBRARY) ;
+
+objects: $(OBJECTS) compile;
+
+$(EXECUTABLE): $(SHARED_OBJECTS)
+	if $(CXX) $(LDFLAGS) -o $@ -Wl,--no-as-needed $^ \
+	-L$(TTCN3_DIR)/lib$(TTCN3_SUBDIR) -l$(TTCN3_LIB) \
+	-L$(OPENSSL_DIR)/lib -lcrypto \
+	-L$(XMLDIR)/lib $($(PLATFORM)_LIBS); \
+	then : ; else $(TTCN3_DIR)/bin/titanver $(OBJECTS); exit 1; fi
+
+$(LIBRARY): $(OBJECTS)
+	$(CXX) -shared -o $@ $(OBJECTS)
+
+.cc.o .c.o:
+	$(CXX) -c $(CPPFLAGS) $(CXXFLAGS) -o $@ $<
+
+.cc.d .c.d:
+	@echo Creating dependency file for '$<'; set -e; \
+	$(CXX) $(CXXDEPFLAGS) $(CPPFLAGS) $(CXXFLAGS) $< \
+	| sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \
+	[ -s $@ ] || rm -f $@
+
+%.so: %.o
+	$(CXX) -shared -o $@ $<
+
+%.ttcn: %.ttcnpp $(TTCN3_INCLUDES)
+	$(CPP) -x c -nostdinc $(CPPFLAGS_TTCN3) $< $@
+
+preprocess: $(PREPROCESSED_TTCN3_MODULES) ;
+
+$(GENERATED_SOURCES) $(GENERATED_HEADERS): compile
+	@if [ ! -f $@ ]; then $(RM) compile; $(MAKE) compile; fi
+
+check: $(TTCN3_MODULES) $(PREPROCESSED_TTCN3_MODULES) $(ASN1_MODULES)
+	$(TTCN3_DIR)/bin/ttcn3_compiler -s $(COMPILER_FLAGS) $^
+
+port: $(TTCN3_MODULES)  $(PREPROCESSED_TTCN3_MODULES) $(ASN1_MODULES)
+	$(TTCN3_DIR)/bin/ttcn3_compiler -t $(COMPILER_FLAGS) $^
+
+compile: $(TTCN3_MODULES)  $(PREPROCESSED_TTCN3_MODULES) $(ASN1_MODULES)
+	$(TTCN3_DIR)/bin/ttcn3_compiler $(COMPILER_FLAGS) $^ - $?
+	touch $@
+
+clean:
+	-$(RM) $(EXECUTABLE) $(LIBRARY) $(OBJECTS) $(GENERATED_HEADERS) \
+	$(GENERATED_SOURCES) $(SHARED_OBJECTS) $(PREPROCESSED_TTCN3_MODULES) compile $(DEPFILES) \
+	tags *.log
+
+dep: $(GENERATED_SOURCES) $(USER_SOURCES) ; 
+
+ifeq ($(findstring n,$(MAKEFLAGS)),)
+ifeq ($(filter clean check port compile archive diag preprocess,$(MAKECMDGOALS)),)
+-include $(DEPFILES)
+endif
+endif
+
+archive:
+	mkdir -p $(ARCHIVE_DIR)
+	tar -cvhf - $(TTCN3_MODULES) $(TTCN3_PP_MODULES) \
+	$(TTCN3_INCLUDES) $(ASN1_MODULES) \
+	$(USER_HEADERS) $(USER_SOURCES) $(OTHER_FILES) \
+	| gzip >$(ARCHIVE_DIR)/`basename $(TARGET) .exe`-`date '+%y%m%d-%H%M'`.tgz
+
+diag:
+	$(TTCN3_DIR)/bin/ttcn3_compiler -v 2>&1
+	$(TTCN3_DIR)/bin/mctr_cli -v 2>&1
+	$(CXX) -v 2>&1
+	@echo TTCN3_DIR=$(TTCN3_DIR)
+	@echo OPENSSL_DIR=$(OPENSSL_DIR)
+	@echo XMLDIR=$(XMLDIR)
+	@echo PLATFORM=$(PLATFORM)
+
+#
+# Add your rules here if necessary...
+#
+
diff --git a/src/E1TS_PT.cc b/src/E1TS_PT.cc
new file mode 100644
index 0000000..9bb7dc4
--- /dev/null
+++ b/src/E1TS_PT.cc
@@ -0,0 +1,285 @@
+/* Copyright (c) 2020 Harald Welte <laforge@osmocom.org> */
+
+#include <iterator>
+
+#include "E1TS_PT.hh"
+#include "E1TS_PortType.hh"
+
+extern "C" {
+#include <osmocom/core/application.h>
+}
+
+#include <poll.h>
+#include <unistd.h>
+
+using namespace E1TS__PortTypes;
+
+namespace E1TS__PortType {
+
+/* somehow std::map() won't work wit E1TS_identity as key */
+DerivedId::DerivedId(const E1TS__identity &id)
+{
+	interface_nr = id.interface__nr();
+	line_nr = id.line__nr();
+	ts_nr = id.ts__nr();
+}
+
+bool operator<(const DerivedId &fk, const DerivedId &lk) {
+	if (fk.interface_nr < lk.interface_nr)
+		return true;
+	else if (fk.interface_nr == lk.interface_nr) {
+		if (fk.line_nr < lk.line_nr)
+			return true;
+		else if (fk.line_nr == lk.line_nr) {
+			if (fk.ts_nr < lk.line_nr)
+				return true;
+		}
+	}
+	return false;
+}
+
+
+QueueEntry::QueueEntry(const uint8_t *pdata, unsigned int plen):
+	len(plen)
+{
+	data = (uint8_t *) malloc(len);
+	memcpy(data, pdata, len);
+}
+
+QueueEntry::~QueueEntry()
+{
+	free(data);
+}
+
+E1_Timeslot::E1_Timeslot(E1TS__PT_PROVIDER &pt, E1TS__identity id, E1TS__mode mode, int fd)
+	: m_pt(pt), m_id(id), m_mode(mode), m_fd(fd)
+{
+	m_pt.log("creating %d:%d:%d fd=%d",
+		 (int)m_id.interface__nr(), (int)m_id.line__nr(), (int)m_id.ts__nr(), m_fd);
+}
+
+E1_Timeslot::~E1_Timeslot()
+{
+	m_pt.log("destroying %d:%d:%d fd=%d",
+		 (int)m_id.interface__nr(), (int)m_id.line__nr(), (int)m_id.ts__nr(), m_fd);
+
+	close(m_fd);
+
+	/* iterate over tx-queue and free all elements */
+	while (!m_tx_queue.empty()) {
+		struct QueueEntry *qe = m_tx_queue.front();
+		printf("qe=%p\n", qe);
+		m_tx_queue.pop();
+		delete qe;
+	}
+}
+
+/* enqueue to-be-transmitted data */
+int E1_Timeslot::enqueue_tx(const uint8_t *data, unsigned int len)
+{
+	struct QueueEntry *qe = new QueueEntry(data, len);
+	if (!qe)
+		return 0;
+	m_tx_queue.push(qe);
+
+	return 1;
+}
+
+/* dequeue + write next-to-be-transmitted data from queue */
+int E1_Timeslot::dequeue_tx(void)
+{
+	struct QueueEntry *qe;
+	int rc;
+
+	if (m_tx_queue.empty()) {
+		/* queue is empty; unsubscribe write-events */
+		return 0;
+	}
+
+	qe = m_tx_queue.front();
+	m_tx_queue.pop();
+	rc = write(m_fd, qe->data, qe->len);
+	if (rc < 0) {
+		TTCN_error("error during write: %s\n", strerror(errno));
+		/* FIXME: close/delete fd */
+	}
+	else if (rc < qe->len)
+		TTCN_error("could only write %u of %u bytes\n", rc, qe->len);
+
+	delete qe;
+
+	return 1;
+}
+
+
+
+E1TS__PT_PROVIDER::E1TS__PT_PROVIDER(const char *par_port_name)
+	: PORT(par_port_name)
+{
+	osmo_init_logging2(NULL, NULL);
+}
+
+E1TS__PT_PROVIDER::~E1TS__PT_PROVIDER()
+{
+}
+
+void E1TS__PT_PROVIDER::log(const char *fmt, ...)
+{
+	TTCN_Logger::begin_event(TTCN_WARNING);
+	TTCN_Logger::log_event("E1TS Test port (%s): ", get_name());
+	va_list args;
+	va_start(args, fmt);
+	TTCN_Logger::log_event_va_list(fmt, args);
+	va_end(args);
+	TTCN_Logger::end_event();
+}
+
+void E1TS__PT_PROVIDER::set_parameter(const char *parameter_name, const char *parameter_value)
+{
+	if (!strcmp(parameter_name, "e1d_socket_path"))
+		m_e1d_socket_path = parameter_value;
+	else
+		TTCN_error("Unsupported E1TS test port parameter `%s'.", parameter_name);
+}
+
+void E1TS__PT_PROVIDER::Handle_Fd_Event(int fd, boolean is_readable, boolean is_writable,
+					boolean is_error)
+{
+	uint8_t buf[65535];
+	E1_Timeslot *ts;
+	int rc;
+
+	/* find E1TS_identity by fd */
+	ts = ts_by_fd(fd);
+
+	if (!ts)
+		TTCN_error("Unknown file descriptor %d\n", fd);
+
+	if (is_readable) {
+		rc = read(fd, buf, sizeof(buf));
+		if (rc > 0)
+			incoming_message(E1TS__unitdata(ts->m_id, OCTETSTRING(rc, buf)));
+		else if (rc == 0) {
+			TTCN_error("EOF on E1TS fd, closing");
+			m_ts_by_id.erase(m_ts_by_id.find(ts->m_id));
+			m_ts_by_fd.erase(m_ts_by_fd.find(ts->m_fd));
+			Handler_Remove_Fd(ts->m_fd);
+			delete ts;
+		}
+	}
+
+	if (is_writable) {
+		/* dequeue next message; unregister for 'write' if nothing to write */
+		if (ts->dequeue_tx() == 0)
+			Handler_Remove_Fd_Write(ts->m_fd);
+	}
+}
+
+void E1TS__PT_PROVIDER::user_map(const char * /*system_port*/)
+{
+	m_e1d_clnt = osmo_e1dp_client_create(NULL, m_e1d_socket_path);
+}
+
+void E1TS__PT_PROVIDER::user_unmap(const char * /*system_port*/)
+{
+	/* close/destroy all timeslots */
+	for (auto it = m_ts_by_id.begin(); it != m_ts_by_id.end(); it++) {
+		E1_Timeslot *ts = it->second;
+		Handler_Remove_Fd(ts->m_fd);
+		delete ts;
+	}
+	m_ts_by_id.clear();
+	m_ts_by_fd.clear();
+
+	/* close client connection to daemon */
+	osmo_e1dp_client_destroy(m_e1d_clnt);
+}
+
+void E1TS__PT_PROVIDER::user_start()
+{
+}
+
+void E1TS__PT_PROVIDER::user_stop()
+{
+}
+
+static enum osmo_e1dp_ts_mode e1dp_mode(E1TS__mode in)
+{
+	switch (in) {
+	case E1TS__PortTypes::E1TS__mode::E1TS__MODE__RAW:
+		return E1DP_TSMODE_RAW;
+	case E1TS__PortTypes::E1TS__mode::E1TS__MODE__HDLCFCS:
+		return E1DP_TSMODE_HDLCFCS;
+	default:
+		TTCN_error("Unknown E1TS_mode %d\n", in);
+	}
+}
+
+E1_Timeslot *E1TS__PT_PROVIDER::ts_by_fd(int fd)
+{
+	auto it = m_ts_by_fd.find(fd);
+	if (it == m_ts_by_fd.end()) {
+		TTCN_error("couldn't find FD for identity");
+		return NULL;
+	} else
+		return it->second;
+}
+
+
+E1_Timeslot *E1TS__PT_PROVIDER::ts_by_id(const E1TS__identity& id)
+{
+	auto it = m_ts_by_id.find(id);
+	if (it == m_ts_by_id.end())
+		return NULL;
+	else
+		return it->second;
+}
+
+
+void E1TS__PT_PROVIDER::outgoing_send(const E1TS__open& send_par)
+{
+	int fd;
+	enum osmo_e1dp_ts_mode mode = e1dp_mode(send_par.mode());
+
+	fd = osmo_e1dp_client_ts_open(m_e1d_clnt, send_par.id().interface__nr(),
+				      send_par.id().line__nr(), send_par.id().ts__nr(), mode);
+
+	if (fd >= 0) {
+		E1_Timeslot *ts = new E1_Timeslot(*this, send_par.id(), send_par.mode(), fd);
+		m_ts_by_id.insert(std::make_pair(send_par.id(), ts));
+		m_ts_by_fd.insert(std::make_pair(fd, ts));
+		Handler_Add_Fd_Read(fd);
+		incoming_message(E1TS__result(send_par.req__hdl(), 0));
+	} else {
+		incoming_message(E1TS__result(send_par.req__hdl(), fd));
+	}
+}
+
+void E1TS__PT_PROVIDER::outgoing_send(const E1TS__close& send_par)
+{
+	/* find fd by map */
+	E1_Timeslot *ts = ts_by_id(send_par.id());
+
+	if (!ts)
+		return;
+
+	m_ts_by_id.erase(m_ts_by_id.find(send_par.id()));
+	m_ts_by_fd.erase(m_ts_by_fd.find(ts->m_fd));
+	Handler_Remove_Fd(ts->m_fd);
+	delete ts;
+}
+
+void E1TS__PT_PROVIDER::outgoing_send(const E1TS__unitdata& send_par)
+{
+	/* find fd by map */
+	E1_Timeslot *ts = ts_by_id(send_par.id());
+
+	if (!ts)
+		return;
+
+	ts->enqueue_tx(send_par.data(), send_par.data().lengthof());
+	Handler_Add_Fd_Write(ts->m_fd);
+}
+
+
+} /* namespace */
diff --git a/src/E1TS_PT.hh b/src/E1TS_PT.hh
new file mode 100644
index 0000000..9b7c75f
--- /dev/null
+++ b/src/E1TS_PT.hh
@@ -0,0 +1,100 @@
+#pragma once
+
+#include <map>
+#include <queue>
+
+#include <TTCN3.hh>
+
+extern "C" {
+#include <osmocom/e1d/proto_clnt.h>
+}
+
+#include "E1TS_PortTypes.hh"
+
+namespace E1TS__PortType {
+
+using namespace E1TS__PortTypes;
+
+class E1TS__PT_PROVIDER;
+
+class DerivedId {
+public:
+	DerivedId(const E1TS__identity &id);
+	unsigned int interface_nr;
+	unsigned int line_nr;
+	unsigned int ts_nr;
+};
+
+class QueueEntry {
+public:
+	QueueEntry(const uint8_t *pdata, unsigned int plen);
+	~QueueEntry();
+
+	uint8_t *data;
+	unsigned int len;
+};
+
+class E1_Timeslot {
+public:
+	E1_Timeslot(E1TS__PT_PROVIDER &pr, E1TS__identity id, E1TS__mode mode, int fd);
+	~E1_Timeslot();
+
+	int enqueue_tx(const uint8_t *data, unsigned int len);
+	int dequeue_tx(void);
+
+	E1TS__identity m_id;
+	int m_fd;
+
+private:
+	E1TS__PT_PROVIDER &m_pt;
+	E1TS__mode m_mode;
+	std::queue<QueueEntry *> m_tx_queue;
+};
+
+
+class E1TS__PT_PROVIDER : public PORT {
+public:
+	E1TS__PT_PROVIDER(const char *par_port_name);
+	~E1TS__PT_PROVIDER();
+
+	void set_parameter(const char *parameter_name, const char *parameter_value);
+
+private:
+	void Handle_Fd_Event(int fd, boolean is_readable, boolean is_writable, boolean is_error);
+
+protected:
+	void user_map(const char *system_port);
+	void user_unmap(const char *system_port);
+
+	void user_start();
+	void user_stop();
+
+	void outgoing_send(const E1TS__open& send_par);
+	void outgoing_send(const E1TS__close& send_par);
+	void outgoing_send(const E1TS__unitdata& send_par);
+
+	virtual void incoming_message(const E1TS__result& incoming_par) = 0;
+	virtual void incoming_message(const E1TS__unitdata &incoming_par) = 0;
+
+public:
+	void log(const char *fmt, ...);
+
+private:
+	/* parameter */
+	const char *m_e1d_socket_path = E1DP_DEFAULT_SOCKET;
+
+	E1_Timeslot *ts_by_fd(int fd);
+	E1_Timeslot *ts_by_id(const E1TS__identity &id);
+
+	/* client to the E1 Daemon */
+	struct osmo_e1dp_client *m_e1d_clnt;
+
+	/* per-timeslot file descriptors */
+	std::map<DerivedId, E1_Timeslot *> m_ts_by_id;
+	std::map<int, E1_Timeslot *> m_ts_by_fd;
+
+};
+
+
+
+}
diff --git a/src/E1TS_PortType.ttcn b/src/E1TS_PortType.ttcn
new file mode 100644
index 0000000..fdaa6f3
--- /dev/null
+++ b/src/E1TS_PortType.ttcn
@@ -0,0 +1,12 @@
+module E1TS_PortType {
+import from E1TS_PortTypes all;
+
+type port E1TS_PT message {
+	out E1TS_open;
+	in E1TS_result;
+
+	out E1TS_unitdata;
+	in E1TS_unitdata;
+} with { extension "provider" };
+
+}
diff --git a/src/E1TS_PortTypes.ttcn b/src/E1TS_PortTypes.ttcn
new file mode 100644
index 0000000..bccb996
--- /dev/null
+++ b/src/E1TS_PortTypes.ttcn
@@ -0,0 +1,87 @@
+module E1TS_PortTypes {
+
+type record E1TS_identity {
+	integer	interface_nr,
+	integer	line_nr,
+	integer ts_nr
+};
+
+type enumerated E1TS_mode {
+	E1TS_MODE_RAW,
+	E1TS_MODE_HDLCFCS
+};
+
+type record E1TS_open {
+	integer req_hdl,
+	E1TS_identity id,
+	E1TS_mode mode,
+	charstring driver
+};
+
+type record E1TS_close {
+	E1TS_identity id
+};
+
+type record E1TS_result {
+	integer req_hdl,
+	integer status
+};
+
+type record E1TS_unitdata {
+	E1TS_identity id,
+	octetstring data
+};
+
+template (value) E1TS_identity ts_E1TS_ID(template (value) integer if_nr,
+					  template (value) integer li_nr,
+					  template (value) integer ts_nr) := {
+	interface_nr := if_nr,
+	line_nr := li_nr,
+	ts_nr := ts_nr
+}
+
+template (present) E1TS_identity tr_E1TS_ID(template (present) integer if_nr,
+					    template (present) integer li_nr,
+					    template (present) integer ts_nr) := {
+	interface_nr := if_nr,
+	line_nr := li_nr,
+	ts_nr := ts_nr
+}
+
+template (value) E1TS_open ts_E1TS_open(template (value) integer req_hdl,
+				        template (value) E1TS_identity id,
+				        template (value) E1TS_mode mode := E1TS_MODE_RAW,
+				        template (value) charstring driver := "e1d") := {
+	req_hdl := req_hdl,
+	id := id,
+	mode := mode,
+	driver := driver
+}
+
+
+template (value) E1TS_close ts_E1TS_close(template (value) E1TS_identity id) := {
+	id := id
+}
+
+template (present) E1TS_result tr_E1TS_result(template (present) integer req_hdl := ?,
+					      template (present) integer status := ?) := {
+	req_hdl := req_hdl,
+	status := status
+}
+
+template (value) E1TS_unitdata ts_E1TS_unitdata(template (value) E1TS_identity id,
+						template (value) octetstring data) := {
+	id := id,
+	data := data
+}
+
+template (present) E1TS_unitdata tr_E1TS_unitdata(template (present) E1TS_identity id,
+						  template (present) octetstring data) := {
+	id := id,
+	data := data
+}
+
+
+
+
+}