summaryrefslogtreecommitdiffstats
path: root/library/LAPDm_RAW_PT.ttcn
diff options
context:
space:
mode:
Diffstat (limited to 'library/LAPDm_RAW_PT.ttcn')
-rw-r--r--library/LAPDm_RAW_PT.ttcn245
1 files changed, 245 insertions, 0 deletions
diff --git a/library/LAPDm_RAW_PT.ttcn b/library/LAPDm_RAW_PT.ttcn
new file mode 100644
index 0000000..06225bb
--- /dev/null
+++ b/library/LAPDm_RAW_PT.ttcn
@@ -0,0 +1,245 @@
+/* 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 GSM_RR_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 {
+ f_connect_reset(L1CTL, l1ctl_sock_path);
+ 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) */
+ }
+}