summaryrefslogtreecommitdiffstats
path: root/lapdm
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2017-07-19 20:17:31 +0200
committerHarald Welte <laforge@gnumonks.org>2017-07-19 20:17:31 +0200
commitb2f94c4baa7f4bfdc2fd2b28d19087a2177f3df6 (patch)
treebc8537dc56a2d22f413926d56d6e1e9e151f945b /lapdm
parent5b1ce8c4f49004b101f794d94060f5666b532ce2 (diff)
move lapd to lapdm (it was a mis-nomer)
Diffstat (limited to 'lapdm')
-rw-r--r--lapdm/L1CTL_Test.ttcn202
-rw-r--r--lapdm/LAPDm_RAW_PT.ttcn248
-rw-r--r--lapdm/LAPDm_Types.ttcn202
-rwxr-xr-xlapdm/gen_links.sh22
-rwxr-xr-xlapdm/regen_makefile.sh9
5 files changed, 683 insertions, 0 deletions
diff --git a/lapdm/L1CTL_Test.ttcn b/lapdm/L1CTL_Test.ttcn
new file mode 100644
index 0000000..e780de8
--- /dev/null
+++ b/lapdm/L1CTL_Test.ttcn
@@ -0,0 +1,202 @@
+module L1CTL_Test {
+ import from GSM_Types all;
+ import from Osmocom_Types all;
+ import from LAPDm_RAW_PT all;
+ import from LAPDm_Types all;
+
+ type component dummy_CT {
+ port LAPDm_PT LAPDM;
+ var lapdm_CT lapdm_component;
+ };
+
+ function f_init() runs on dummy_CT {
+ /* create the LAPDm component */
+ lapdm_component := lapdm_CT.create;
+ /* connect our own LAPDM port to the LAPDM Service Provider of the LAPDm component */
+ connect(self:LAPDM, lapdm_component:LAPDM_SP);
+ /* connect the LAPDm compoent's lower-side port to the system L1CTL port (which is internally
+ * connected to the Unix Domain Socket test port */
+ map(lapdm_component:L1CTL, system:L1CTL);
+
+ /* start the LAPDm parallel component calling it's local function LAPDmStart */
+ lapdm_component.start(LAPDmStart());
+ }
+
+ /* master function establishing a dedicated radio channel (takes care of RACH/IMM.ASS handling) */
+ function f_establish_dcch() runs on dummy_CT {
+ var BCCH_tune_req tune_req := { arfcn := { false, 871 }, combined_ccch := true };
+ var DCCH_establish_req est_req := { ra := 23 };
+
+ LAPDM.send(tune_req);
+ LAPDM.send(est_req);
+ LAPDM.receive(DCCH_establish_res:?);
+ }
+
+ /* helper function releasing dedicated radio channel physically (no Um signaling!) */
+ function f_release_dcch() runs on dummy_CT {
+ var DCCH_release_req rel_req := {};
+ LAPDM.send(rel_req);
+ }
+
+ template LAPDm_ph_data t_PH_DATA(template GsmSapi sapi, template boolean sacch, template LapdmFrame frame) := {
+ sacch := sacch,
+ sapi := sapi,
+ lapdm := frame
+ }
+ /* template for a valid SABM frame */
+ template LapdmFrame LAPDm_B_SABM(template GsmSapi sapi, octetstring payload) := {
+ b := {
+ addr := tr_LapdmAddr(sapi, false),
+ ctrl := t_LapdmCtrlSABM(true),
+ len := lengthof(payload),
+ m := false,
+ el := 1,
+ payload := payload
+ }
+ }
+
+ /* template for a valid UA frame */
+ template LapdmFrame tr_LAPDm_B_UA(template GsmSapi sapi, template octetstring payload) := {
+ b := {
+ addr := tr_LapdmAddr(sapi, false),
+ ctrl := t_LapdmCtrlUA(true),
+ len := ?,
+ m := false,
+ el := 1,
+ payload := payload
+ }
+ }
+
+ /* template for a valid UA frame */
+ template LapdmFrame LAPDm_B_UA(template GsmSapi sapi, octetstring payload) := {
+ b := {
+ addr := tr_LapdmAddr(sapi, false),
+ ctrl := t_LapdmCtrlUA(true),
+ len := lengthof(payload),
+ m := false,
+ el := 1,
+ payload := payload
+ }
+ }
+
+ /* template for a valid UI frame */
+ template LapdmFrame LAPDm_B_UI(template GsmSapi sapi, octetstring payload) := {
+ b := {
+ addr := tr_LapdmAddr(sapi, true),
+ ctrl := t_LapdmCtrlUI(false),
+ len := lengthof(payload),
+ m := false,
+ el := 1,
+ payload := payload
+ }
+ }
+
+ function f_test_sabm_results_in_ua(uint8_t sapi, boolean use_sacch, octetstring payload) runs on dummy_CT return boolean {
+ var LAPDm_ph_data phd;
+ var boolean result := false;
+ timer T := 5.0;
+
+ f_establish_dcch();
+ LAPDM.send(t_PH_DATA(sapi, use_sacch, LAPDm_B_SABM(sapi, payload)));
+ log("====> expecting ", t_PH_DATA(sapi, use_sacch, LAPDm_B_UA(sapi, payload)));
+ T.start
+ alt {
+ [] LAPDM.receive(t_PH_DATA(?, use_sacch, LAPDm_B_UA(sapi, payload))) { result := true; }
+ [] LAPDM.receive(t_PH_DATA(?, use_sacch, ?)) -> value phd { log("Other msg on DCH: ", phd); repeat; }
+ [] LAPDM.receive(t_PH_DATA(?, ?, ?)) -> value phd { log("Other PH-DATA: ", phd); repeat; }
+ [] T.timeout { }
+ }
+ f_release_dcch();
+ return result;
+ }
+
+ testcase TC_sabm_ua_dcch_sapi0() runs on dummy_CT {
+ f_init();
+ if (not f_test_sabm_results_in_ua(0, false, 'FEFE'O)) {
+ setverdict(fail);
+ }
+ setverdict(pass);
+ }
+
+ testcase TC_sabm_ua_dcch_sapi0_nopayload() runs on dummy_CT {
+ f_init();
+ if (f_test_sabm_results_in_ua(0, false, ''O)) {
+ setverdict(fail, "Initial SABM/UA must contain L3 payload but BTS accepts without");
+ }
+ setverdict(pass);
+ }
+
+ testcase TC_sabm_ua_dcch_sapi3() runs on dummy_CT {
+ f_init();
+ if (f_test_sabm_results_in_ua(3, false, 'FEFE'O)) {
+ setverdict(fail, "Initial SABM/UA must be on SAPI0, but BTS accepts SAPI=3");
+ }
+ setverdict(pass);
+ }
+
+ testcase TC_sabm_ua_dcch_sapi4() runs on dummy_CT {
+ f_init();
+ if (f_test_sabm_results_in_ua(4, false, 'FEFE'O)) {
+ setverdict(fail, "Initial SABM/UA must be on SAPI0, but BTS accepts SAPI=4");
+ }
+ setverdict(pass);
+ }
+
+ testcase TC_sabm_contention() runs on dummy_CT {
+ var LAPDm_ph_data phd;
+ const octetstring payload := '0102030405'O;
+ const GsmSapi sapi := 0;
+ const boolean use_sacch := false;
+ timer T := 5.0;
+
+ f_init();
+
+ f_establish_dcch();
+ /* first frame is our real SABM */
+ LAPDM.send(t_PH_DATA(sapi, use_sacch, LAPDm_B_SABM(sapi, payload)));
+ /* second frame is a SABM with different payload, which BTS has to ignore according to 8.4.1.4 */
+ LAPDM.send(t_PH_DATA(sapi, use_sacch, LAPDm_B_SABM(sapi, 'ABCDEF'O)));
+ log("====> expecting ", t_PH_DATA(sapi, use_sacch, LAPDm_B_UA(sapi, payload)));
+ T.start
+ alt {
+ [] LAPDM.receive(t_PH_DATA(?, use_sacch, LAPDm_B_UA(sapi, payload))) { setverdict(pass); repeat; }
+ [] LAPDM.receive(t_PH_DATA(?, use_sacch, tr_LAPDm_B_UA(sapi, ?))) {
+ setverdict(fail, "Second SABM was responded to during contention resolution");
+ }
+ [] LAPDM.receive { repeat };
+ [] T.timeout { }
+ }
+ f_release_dcch();
+ }
+
+
+ testcase TC_foo() runs on dummy_CT {
+/*
+ var LapdmFrame lf := valueof(LAPDm_B_UA(0, ''O));
+ log("ENC UA: ", enc_LapdmFrame(lf));
+ lf := valueof(LAPDm_B_UI(0, ''O));
+ log("ENC UI B: ", enc_LapdmFrame(lf));
+ log("ENC UI B: ", enc_LapdmFrameB(lf.b));
+
+ log("DEC UI AF: ", dec_LapdmAddressField('03'O));
+*/
+ log("DEC UI CU: ", dec_LapdmCtrlU('03'O));
+ log("DEC UI CT: ", dec_LapdmCtrl('03'O));
+
+ log("DEC UA: ", dec_LapdmFrameB('017301'O));
+ log("DEC UI: ", dec_LapdmFrameA('030301'O));
+ log("DEC I: ", dec_LapdmFrameA('030001'O));
+ log("DEC S: ", dec_LapdmFrameA('030101'O));
+ log("DEC: ", dec_LapdmFrameB('030301'O));
+ log("DEC: ", dec_LapdmFrameB('0303012B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B'O));
+ }
+
+ control {
+ execute(TC_foo());
+ execute(TC_sabm_ua_dcch_sapi0());
+ execute(TC_sabm_ua_dcch_sapi0_nopayload());
+ execute(TC_sabm_ua_dcch_sapi3());
+ execute(TC_sabm_ua_dcch_sapi4());
+ execute(TC_sabm_contention());
+ }
+}
diff --git a/lapdm/LAPDm_RAW_PT.ttcn b/lapdm/LAPDm_RAW_PT.ttcn
new file mode 100644
index 0000000..37824d3
--- /dev/null
+++ b/lapdm/LAPDm_RAW_PT.ttcn
@@ -0,0 +1,248 @@
+/* Test Port that stacks on top of L1CTL test port and performs LAPDm encoding/decoding, so the user can send
+ * and receive LAPDm frames in decoded TTCN-3 data types. This is particularly useful for sending/receiving
+ * all kinds of hand-crafted LAPDm frames for testing of the remote LAPDm layer */
+module LAPDm_RAW_PT {
+ import from GSM_Types all;
+ import from Osmocom_Types all;
+ import from L1CTL_Types all;
+ import from L1CTL_PortType all;
+ import from LAPDm_Types all;
+
+ /* request to tune to a given ARFCN and start BCCH decoding */
+ type record BCCH_tune_req {
+ Arfcn arfcn,
+ boolean combined_ccch
+ }
+
+ /* ask for a dedicated channel to be established */
+ type record DCCH_establish_req {
+ uint8_t ra
+ }
+
+ type record DCCH_establish_res {
+ ChannelDescription chan_desc optional,
+ charstring err optional
+ }
+
+ type record DCCH_release_req {
+ }
+
+ /* PH-DATA.ind / PH-DATA.req */
+ type record LAPDm_ph_data {
+ boolean sacch,
+ GsmSapi sapi,
+ LapdmFrame lapdm
+ }
+
+ /* port from our (internal) point of view */
+ type port LAPDm_SP_PT message {
+ in BCCH_tune_req,
+ DCCH_establish_req,
+ DCCH_release_req,
+ LAPDm_ph_data;
+ out DCCH_establish_res,
+ LAPDm_ph_data;
+ } with {extension "internal"};
+
+ /* port from user (external) point of view */
+ type port LAPDm_PT message {
+ in DCCH_establish_res,
+ LAPDm_ph_data;
+ out BCCH_tune_req,
+ DCCH_establish_req,
+ DCCH_release_req,
+ LAPDm_ph_data;
+ } with {extension "internal"};
+
+ function LAPDmStart() runs on lapdm_CT {
+ f_init();
+ ScanEvents();
+ }
+
+ /* TS 44.004 Figure 5.1 */
+ type enumerated ph_state_enum {
+ PH_STATE_NULL,
+ PH_STATE_BCH,
+ PH_STATE_SEARCHING_BCH,
+ PH_STATE_TUNING_DCH,
+ PH_STATE_DCH
+ }
+
+ type component lapdm_CT {
+ var charstring l1ctl_sock_path := "/tmp/osmocom_l2";
+
+ /* L1CTL port towards the bottom */
+ port L1CTL_PT L1CTL;
+ /* Port towards L2 */
+ port LAPDm_SP_PT LAPDM_SP;
+
+ /* physical layer state */
+ var ph_state_enum ph_state := PH_STATE_NULL;
+
+ /* channel description of the currently active DCH */
+ var ChannelDescription chan_desc;
+ };
+
+ /* wrapper function to log state transitions */
+ private function set_ph_state(ph_state_enum new_state) runs on lapdm_CT {
+ log("PH-STATE ", ph_state, " -> ", new_state);
+ ph_state := new_state;
+ }
+
+ private function f_init() runs on lapdm_CT {
+ L1CTL.send(L1CTL_connect:{path:=l1ctl_sock_path});
+ L1CTL.receive(L1CTL_connect_result:{result_code := SUCCESS, err:=omit});
+
+ L1CTL.send(t_L1ctlResetReq(L1CTL_RES_T_SCHED));
+ L1CTL.receive;
+ set_ph_state(PH_STATE_NULL);
+ }
+
+ /* release the dedicated radio channel */
+ private function f_release_dcch() runs on lapdm_CT {
+ L1CTL.send(t_L1CTL_DM_REL_REQ(chan_desc.chan_nr));
+ set_ph_state(PH_STATE_NULL);
+ }
+
+ /* tune to given ARFCN and start BCCH/CCCH decoding */
+ private function f_tune_bcch(Arfcn arfcn, boolean combined) runs on lapdm_CT {
+ var L1ctlCcchMode mode := CCCH_MODE_NON_COMBINED;
+ if (combined) {
+ mode := CCCH_MODE_COMBINED;
+ }
+
+ if (ph_state == PH_STATE_DCH) {
+ /* release any previous DCH */
+ f_release_dcch();
+ }
+
+ set_ph_state(PH_STATE_SEARCHING_BCH);
+
+ /* send FB/SB req to sync to cell */
+ f_L1CTL_FBSB(L1CTL, arfcn, mode);
+ set_ph_state(PH_STATE_BCH);
+ }
+
+ /* master function establishing a dedicated radio channel */
+ private function f_establish_dcch(uint8_t ra) runs on lapdm_CT {
+ var ImmediateAssignment imm_ass;
+ var GsmFrameNumber rach_fn;
+
+ /* send RACH request and obtain FN at which it was sent */
+ rach_fn := f_L1CTL_RACH(L1CTL, ra);
+ //if (not rach_fn) { return; }
+
+ /* wait for receiving matching IMM ASS */
+ imm_ass := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, rach_fn)
+ //if (not imm_ass) { return; }
+ set_ph_state(PH_STATE_TUNING_DCH);
+
+ /* store/save channel description */
+ chan_desc := imm_ass.chan_desc;
+
+ /* send DM_EST_REQ */
+ f_L1CTL_DM_EST_REQ_IA(L1CTL, imm_ass);
+ set_ph_state(PH_STATE_DCH);
+ }
+
+ function ScanEvents() runs on lapdm_CT {
+ var L1ctlDlMessage dl;
+ var BCCH_tune_req bt;
+ var LAPDm_ph_data lpd;
+ var DCCH_establish_req est_req;
+ var DCCH_establish_res est_res;
+
+ while (true) {
+ if (ph_state == PH_STATE_NULL) {
+ alt {
+ [] LAPDM_SP.receive(BCCH_tune_req:?) -> value bt {
+ f_tune_bcch(bt.arfcn, bt.combined_ccch);
+ }
+
+ [] LAPDM_SP.receive {}
+ [] L1CTL.receive {}
+
+ }
+ } else if (ph_state == PH_STATE_BCH or ph_state == PH_STATE_SEARCHING_BCH) {
+ alt {
+ [] LAPDM_SP.receive(BCCH_tune_req:?) -> value bt {
+ f_tune_bcch(bt.arfcn, bt.combined_ccch);
+ }
+
+ /* forward CCCH SAPI from L1CTL to User */
+ [] L1CTL.receive(t_L1CTL_DATA_IND(t_RslChanNr_BCCH(0))) -> value dl {
+ lpd.sacch := false;
+ lpd.sapi := 0;
+ lpd.lapdm.bbis := dec_LapdmFrameBbis(dl.payload.data_ind.payload);
+ LAPDM_SP.send(lpd);
+ }
+
+ /* forward BCCH SAPI from L1CTL to User */
+ [] L1CTL.receive(t_L1CTL_DATA_IND(t_RslChanNr_PCH_AGCH(0))) -> value dl {
+ lpd.sacch := false;
+ lpd.sapi := 0;
+ lpd.lapdm.bbis := dec_LapdmFrameBbis(dl.payload.data_ind.payload);
+ LAPDM_SP.send(lpd);
+ }
+
+ /* Establish dedicated channel */
+ [] LAPDM_SP.receive(DCCH_establish_req:?) -> value est_req {
+ var DCCH_establish_res res;
+ f_establish_dcch(est_req.ra);
+ if (ph_state == PH_STATE_DCH) {
+ res := { chan_desc, omit };
+ } else {
+ res := { omit, "Unable to esetablish DCCH" };
+ }
+ LAPDM_SP.send(res);
+ }
+
+ [] LAPDM_SP.receive {}
+ [] L1CTL.receive {}
+
+ }
+
+ } else if (ph_state == PH_STATE_TUNING_DCH or ph_state == PH_STATE_DCH) {
+ alt {
+
+ /* decode any received DATA frames for the dedicated channel and pass them up */
+ [] L1CTL.receive(t_L1CTL_DATA_IND(chan_desc.chan_nr)) -> value dl {
+ if (dl.dl_info.link_id.c == SACCH) {
+ lpd.sacch := true;
+ /* FIXME: how to deal with UI frames in B4 format (lo length!) */
+ } else {
+ lpd.sacch := false;
+ }
+ lpd.sapi := dl.dl_info.link_id.sapi;
+ lpd.lapdm.b := dec_LapdmFrameB(dl.payload.data_ind.payload);
+ LAPDM_SP.send(lpd);
+ }
+
+ /* encode any LAPDm record from user and pass it on to L1CTL */
+ [] LAPDM_SP.receive(LAPDm_ph_data:?) -> value lpd {
+ var octetstring buf;
+ var RslLinkId link_id;
+ if (lpd.sacch) {
+ link_id := valueof(ts_RslLinkID_SACCH(lpd.sapi));
+ } else {
+ link_id := valueof(ts_RslLinkID_DCCH(lpd.sapi));
+ }
+ buf := enc_LapdmFrame(lpd.lapdm);
+ L1CTL.send(t_L1CTL_DATA_REQ(chan_desc.chan_nr, link_id, buf));
+ }
+
+ /* Release dedicated channel */
+ [] LAPDM_SP.receive(DCCH_release_req:?) {
+ /* go back to BCCH */
+ f_release_dcch();
+ }
+
+ [] LAPDM_SP.receive {}
+ [] L1CTL.receive {}
+
+
+ }
+ }
+ } /* while (1) */
+ }
+}
diff --git a/lapdm/LAPDm_Types.ttcn b/lapdm/LAPDm_Types.ttcn
new file mode 100644
index 0000000..988e015
--- /dev/null
+++ b/lapdm/LAPDm_Types.ttcn
@@ -0,0 +1,202 @@
+/* LAPDm definitiona according to 3GPP TS 44.006 */
+/* (C) 2017 bh Harald Welte <laforge@gnumonks.org> */
+module LAPDm_Types {
+
+ import from General_Types all;
+ import from Osmocom_Types all;
+
+ type uint3_t LapdmSapi;
+ type BIT2 LapdmSBits;
+ type BIT3 LapdmUBits;
+ type BIT2 LapdmU2Bits;
+
+ type record LapdmLengthIndicator {
+ uint6_t len,
+ boolean m,
+ uint1_t el
+ } with { variant "FIELDORDER(msb)" };
+
+ template LapdmLengthIndicator t_LapdmLengthIndicator(template uint6_t len, boolean m := false) := {
+ len := len,
+ m := m,
+ el := 1
+ };
+
+ /* TS 44.006 Figure 4 */
+ type record LapdmAddressField {
+ BIT1 spare,
+ uint2_t lpd,
+ LapdmSapi sapi,
+ boolean c_r,
+ boolean ea
+ } with { variant "FIELDORDER(msb)" };
+
+ template LapdmAddressField tr_LapdmAddr(template LapdmSapi sapi, template boolean c_r) := {
+ spare := '0'B,
+ lpd := 0,
+ sapi := sapi,
+ c_r := c_r,
+ ea := true
+ };
+
+ type record LapdmCtrlI {
+ BIT1 spare,
+ uint3_t n_s,
+ boolean p,
+ uint3_t n_r
+ } with { variant "FIELDORDER(lsb)" };
+
+ type record LapdmCtrlS {
+ BIT2 spare,
+ LapdmSBits s,
+ boolean p_f,
+ uint3_t n_r
+ } with { variant "FIELDORDER(lsb)" };
+
+ type record LapdmCtrlU {
+ BIT2 spare,
+ LapdmU2Bits u2,
+ boolean p_f,
+ LapdmUBits u
+ } with { variant "FIELDORDER(lsb)" };
+
+ /* TS 44.006 Table 3 */
+ type union LapdmCtrl {
+ LapdmCtrlS s,
+ LapdmCtrlU u,
+ LapdmCtrlI i,
+ uint8_t other
+ } with { variant "TAG(u, spare = '11'B;
+ s, spare = '01'B;
+ i, spare = '0'B;
+ other, OTHERWISE)" };
+ /* )" }; */
+
+ /* TS 44.006 Table 4 */
+
+ template LapdmCtrl t_LapdmCtrlS := {
+ s := { spare := '01'B, s := ?, p_f := ?, n_r := ? }
+ };
+
+ template LapdmCtrl t_LapdmCtrlU := {
+ u := { spare := '11'B, u2 := ?, p_f := ?, u := ? }
+ };
+
+ /* TS 44.006 Table 4 */
+ template LapdmCtrl t_LapdmCtrlI(template uint3_t nr, template uint3_t ns, template boolean p) := {
+ i := { spare := '0'B, n_s := ns, p := p, n_r := nr }
+ };
+
+ template LapdmCtrl t_LapdmCtrlRR(template uint3_t nr, template boolean pf) modifies t_LapdmCtrlS := {
+ s := { s:= '00'B, p_f := pf, n_r := nr }
+ };
+
+ template LapdmCtrl t_LapdmCtrlRNR(template uint3_t nr, template boolean pf) modifies t_LapdmCtrlS := {
+ s := { s:= '01'B, p_f := pf, n_r := nr }
+ };
+
+ template LapdmCtrl t_LapdmCtrlREJ(template uint3_t nr, template boolean pf) modifies t_LapdmCtrlS := {
+ s := { s:= '10'B, p_f := pf, n_r := nr }
+ };
+
+ template LapdmCtrl t_LapdmCtrlSABM(template boolean p) modifies t_LapdmCtrlU := {
+ u := { u2 := '11'B, p_f := p, u := '001'B }
+ };
+
+ template LapdmCtrl t_LapdmCtrlDM(template boolean f) modifies t_LapdmCtrlU := {
+ u := { u2 := '11'B, p_f := f, u := '000'B }
+ };
+
+ template LapdmCtrl t_LapdmCtrlUI(template boolean p) modifies t_LapdmCtrlU := {
+ u := { u2 := '00'B, p_f := p, u := '000'B }
+ };
+
+ template LapdmCtrl t_LapdmCtrlDISC(template boolean p) modifies t_LapdmCtrlU := {
+ u := { u2 := '00'B, p_f := p, u := '010'B }
+ };
+
+ template LapdmCtrl t_LapdmCtrlUA(template boolean f) modifies t_LapdmCtrlU := {
+ u := { u2 := '00'B, p_f := f, u := '011'B }
+ };
+
+ external function dec_LapdmAddressField(in octetstring stream) return LapdmAddressField
+ with { extension "prototype(convert) decode(RAW)" };
+
+ external function dec_LapdmCtrl(in octetstring stream) return LapdmCtrl
+ with { extension "prototype(convert) decode(RAW)" };
+
+ external function dec_LapdmCtrlU(in octetstring stream) return LapdmCtrlU
+ with { extension "prototype(convert) decode(RAW)" };
+
+ external function dec_LapdmLengthIndicator(in octetstring stream) return LapdmLengthIndicator
+ with { extension "prototype(convert) decode(RAW)" };
+
+ /* Format A is used on DCCHs for frames where there is no information field */
+ type record LapdmFrameA {
+ LapdmAddressField addr,
+ LapdmCtrl ctrl,
+ LapdmLengthIndicator len
+ } with { variant "" };
+
+ external function enc_LapdmFrameA(in LapdmFrameA si) return octetstring
+ with { extension "prototype(convert) encode(RAW)" };
+ external function dec_LapdmFrameA(in octetstring stream) return LapdmFrameA
+ with { extension "prototype(convert) decode(RAW)" };
+
+ /* Formats B, Bter and B4 are used on DCCHs for frames containing an information field:
+ /* - format Bter is used on request of higher layers if and only if short L2 header type 1 is
+ * supported and a UI command is to be transmitted on SAPI 0 */
+ /* - format B4 is used for UI frames transmitted by the network on SACCH; */
+ /* - format B is applied in all other cases. */
+ /* Format Bbis is used only on BCCH, PCH, NCH, and AGCH.
+
+ /* Format B */
+ type record LapdmFrameB {
+ LapdmAddressField addr,
+ LapdmCtrl ctrl,
+ uint6_t len,
+ boolean m,
+ uint1_t el (1),
+ octetstring payload
+ } with { variant (len) "LENGTHTO(payload)"
+ variant "FIELDORDER(msb)" };
+
+ external function enc_LapdmFrameB(in LapdmFrameB si) return octetstring
+ with { extension "prototype(convert) encode(RAW)" };
+ external function dec_LapdmFrameB(in octetstring stream) return LapdmFrameB
+ with { extension "prototype(convert) decode(RAW)" };
+
+ /* Format B4 */
+ type record LapdmFrameB4 {
+ LapdmAddressField addr,
+ LapdmCtrl ctrl,
+ octetstring payload
+ } with { variant "" };
+
+ external function enc_LapdmFrameB4(in LapdmFrameB4 si) return octetstring
+ with { extension "prototype(convert) encode(RAW)" };
+ external function dec_LapdmFrameB4(in octetstring stream) return LapdmFrameB4
+ with { extension "prototype(convert) decode(RAW)" };
+
+ type record LapdmFrameBbis {
+ octetstring payload
+ } with { variant "" };
+
+ external function enc_LapdmFrameBbis(in LapdmFrameBbis si) return octetstring
+ with { extension "prototype(convert) encode(RAW)" };
+ external function dec_LapdmFrameBbis(in octetstring stream) return LapdmFrameBbis
+ with { extension "prototype(convert) decode(RAW)" };
+
+ type union LapdmFrame {
+ LapdmFrameA a,
+ LapdmFrameB b,
+ LapdmFrameBbis bbis,
+ LapdmFrameB4 b4
+ } with { variant "" };
+
+ external function enc_LapdmFrame(in LapdmFrame si) return octetstring
+ with { extension "prototype(convert) encode(RAW)" };
+ /* automatic decoding to the generic LapdmFrame will not work, you have to call one of the
+ * type-specific decoder routines above */
+
+} with { encode "RAW"; /*variant "FIELDORDER(msb)" */}
diff --git a/lapdm/gen_links.sh b/lapdm/gen_links.sh
new file mode 100755
index 0000000..d849751
--- /dev/null
+++ b/lapdm/gen_links.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+BASEDIR=~/projects/git
+
+gen_links() {
+ DIR=$1
+ FILES=$*
+ for f in $FILES; do
+ echo "Linking $f"
+ ln -sf $DIR/$f $f
+ done
+}
+
+DIR=$BASEDIR/titan.TestPorts.UNIX_DOMAIN_SOCKETasp/src
+FILES="UD_PT.cc UD_PT.hh UD_PortType.ttcn UD_Types.ttcn"
+gen_links $DIR $FILES
+
+
+
+DIR=../library
+FILES="General_Types.ttcn GSM_Types.ttcn Osmocom_Types.ttcn L1CTL_PortType.ttcn L1CTL_Types.ttcn"
+gen_links $DIR $FILES
diff --git a/lapdm/regen_makefile.sh b/lapdm/regen_makefile.sh
new file mode 100755
index 0000000..8d321ac
--- /dev/null
+++ b/lapdm/regen_makefile.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+FILES="*.ttcn UD_PT.cc UD_PT.hh"
+
+ttcn3_makefilegen -f L1CTL_Test.ttcn $FILES
+sed -i -e 's/# TTCN3_DIR = /TTCN3_DIR = \/usr/' Makefile
+sed -i -e 's/LDFLAGS = /LDFLAGS = -L \/usr\/lib\/titan `pkg-config --libs libnetfilter_conntrack`/' Makefile
+sed -i -e 's/TTCN3_LIB = ttcn3-parallel/TTCN3_LIB = ttcn3/' Makefile
+sed -i -e 's/CPPFLAGS = -D$(PLATFORM) -I$(TTCN3_DIR)\/include/CPPFLAGS = -D$(PLATFORM) -I$(TTCN3_DIR)\/include -I\/usr\/include\/titan/' Makefile