blob: 37824d3ac38b304725ea36f8bc7d6258dfbf5297 [file] [log] [blame]
Harald Welted4ba7ff2017-07-17 21:00:48 +02001/* Test Port that stacks on top of L1CTL test port and performs LAPDm encoding/decoding, so the user can send
2 * and receive LAPDm frames in decoded TTCN-3 data types. This is particularly useful for sending/receiving
3 * all kinds of hand-crafted LAPDm frames for testing of the remote LAPDm layer */
4module LAPDm_RAW_PT {
5 import from GSM_Types all;
6 import from Osmocom_Types all;
7 import from L1CTL_Types all;
8 import from L1CTL_PortType all;
9 import from LAPDm_Types all;
10
11 /* request to tune to a given ARFCN and start BCCH decoding */
12 type record BCCH_tune_req {
13 Arfcn arfcn,
14 boolean combined_ccch
15 }
16
17 /* ask for a dedicated channel to be established */
18 type record DCCH_establish_req {
19 uint8_t ra
20 }
21
22 type record DCCH_establish_res {
23 ChannelDescription chan_desc optional,
24 charstring err optional
25 }
26
27 type record DCCH_release_req {
28 }
29
30 /* PH-DATA.ind / PH-DATA.req */
31 type record LAPDm_ph_data {
32 boolean sacch,
33 GsmSapi sapi,
34 LapdmFrame lapdm
35 }
36
37 /* port from our (internal) point of view */
38 type port LAPDm_SP_PT message {
39 in BCCH_tune_req,
40 DCCH_establish_req,
41 DCCH_release_req,
42 LAPDm_ph_data;
43 out DCCH_establish_res,
44 LAPDm_ph_data;
45 } with {extension "internal"};
46
47 /* port from user (external) point of view */
48 type port LAPDm_PT message {
49 in DCCH_establish_res,
50 LAPDm_ph_data;
51 out BCCH_tune_req,
52 DCCH_establish_req,
53 DCCH_release_req,
54 LAPDm_ph_data;
55 } with {extension "internal"};
56
57 function LAPDmStart() runs on lapdm_CT {
58 f_init();
59 ScanEvents();
60 }
61
62 /* TS 44.004 Figure 5.1 */
63 type enumerated ph_state_enum {
64 PH_STATE_NULL,
65 PH_STATE_BCH,
66 PH_STATE_SEARCHING_BCH,
67 PH_STATE_TUNING_DCH,
68 PH_STATE_DCH
69 }
70
71 type component lapdm_CT {
72 var charstring l1ctl_sock_path := "/tmp/osmocom_l2";
73
74 /* L1CTL port towards the bottom */
75 port L1CTL_PT L1CTL;
76 /* Port towards L2 */
77 port LAPDm_SP_PT LAPDM_SP;
78
79 /* physical layer state */
80 var ph_state_enum ph_state := PH_STATE_NULL;
81
82 /* channel description of the currently active DCH */
83 var ChannelDescription chan_desc;
84 };
85
86 /* wrapper function to log state transitions */
87 private function set_ph_state(ph_state_enum new_state) runs on lapdm_CT {
88 log("PH-STATE ", ph_state, " -> ", new_state);
89 ph_state := new_state;
90 }
91
92 private function f_init() runs on lapdm_CT {
93 L1CTL.send(L1CTL_connect:{path:=l1ctl_sock_path});
94 L1CTL.receive(L1CTL_connect_result:{result_code := SUCCESS, err:=omit});
95
96 L1CTL.send(t_L1ctlResetReq(L1CTL_RES_T_SCHED));
97 L1CTL.receive;
98 set_ph_state(PH_STATE_NULL);
99 }
100
101 /* release the dedicated radio channel */
102 private function f_release_dcch() runs on lapdm_CT {
103 L1CTL.send(t_L1CTL_DM_REL_REQ(chan_desc.chan_nr));
104 set_ph_state(PH_STATE_NULL);
105 }
106
107 /* tune to given ARFCN and start BCCH/CCCH decoding */
108 private function f_tune_bcch(Arfcn arfcn, boolean combined) runs on lapdm_CT {
109 var L1ctlCcchMode mode := CCCH_MODE_NON_COMBINED;
110 if (combined) {
111 mode := CCCH_MODE_COMBINED;
112 }
113
114 if (ph_state == PH_STATE_DCH) {
115 /* release any previous DCH */
116 f_release_dcch();
117 }
118
119 set_ph_state(PH_STATE_SEARCHING_BCH);
120
121 /* send FB/SB req to sync to cell */
122 f_L1CTL_FBSB(L1CTL, arfcn, mode);
123 set_ph_state(PH_STATE_BCH);
124 }
125
126 /* master function establishing a dedicated radio channel */
127 private function f_establish_dcch(uint8_t ra) runs on lapdm_CT {
128 var ImmediateAssignment imm_ass;
129 var GsmFrameNumber rach_fn;
130
131 /* send RACH request and obtain FN at which it was sent */
132 rach_fn := f_L1CTL_RACH(L1CTL, ra);
133 //if (not rach_fn) { return; }
134
135 /* wait for receiving matching IMM ASS */
136 imm_ass := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, rach_fn)
137 //if (not imm_ass) { return; }
138 set_ph_state(PH_STATE_TUNING_DCH);
139
140 /* store/save channel description */
141 chan_desc := imm_ass.chan_desc;
142
143 /* send DM_EST_REQ */
144 f_L1CTL_DM_EST_REQ_IA(L1CTL, imm_ass);
145 set_ph_state(PH_STATE_DCH);
146 }
147
148 function ScanEvents() runs on lapdm_CT {
149 var L1ctlDlMessage dl;
150 var BCCH_tune_req bt;
151 var LAPDm_ph_data lpd;
152 var DCCH_establish_req est_req;
153 var DCCH_establish_res est_res;
154
155 while (true) {
156 if (ph_state == PH_STATE_NULL) {
157 alt {
158 [] LAPDM_SP.receive(BCCH_tune_req:?) -> value bt {
159 f_tune_bcch(bt.arfcn, bt.combined_ccch);
160 }
161
162 [] LAPDM_SP.receive {}
163 [] L1CTL.receive {}
164
165 }
166 } else if (ph_state == PH_STATE_BCH or ph_state == PH_STATE_SEARCHING_BCH) {
167 alt {
168 [] LAPDM_SP.receive(BCCH_tune_req:?) -> value bt {
169 f_tune_bcch(bt.arfcn, bt.combined_ccch);
170 }
171
172 /* forward CCCH SAPI from L1CTL to User */
173 [] L1CTL.receive(t_L1CTL_DATA_IND(t_RslChanNr_BCCH(0))) -> value dl {
174 lpd.sacch := false;
175 lpd.sapi := 0;
176 lpd.lapdm.bbis := dec_LapdmFrameBbis(dl.payload.data_ind.payload);
177 LAPDM_SP.send(lpd);
178 }
179
180 /* forward BCCH SAPI from L1CTL to User */
181 [] L1CTL.receive(t_L1CTL_DATA_IND(t_RslChanNr_PCH_AGCH(0))) -> value dl {
182 lpd.sacch := false;
183 lpd.sapi := 0;
184 lpd.lapdm.bbis := dec_LapdmFrameBbis(dl.payload.data_ind.payload);
185 LAPDM_SP.send(lpd);
186 }
187
188 /* Establish dedicated channel */
189 [] LAPDM_SP.receive(DCCH_establish_req:?) -> value est_req {
190 var DCCH_establish_res res;
191 f_establish_dcch(est_req.ra);
192 if (ph_state == PH_STATE_DCH) {
193 res := { chan_desc, omit };
194 } else {
195 res := { omit, "Unable to esetablish DCCH" };
196 }
197 LAPDM_SP.send(res);
198 }
199
200 [] LAPDM_SP.receive {}
201 [] L1CTL.receive {}
202
203 }
204
205 } else if (ph_state == PH_STATE_TUNING_DCH or ph_state == PH_STATE_DCH) {
206 alt {
207
208 /* decode any received DATA frames for the dedicated channel and pass them up */
209 [] L1CTL.receive(t_L1CTL_DATA_IND(chan_desc.chan_nr)) -> value dl {
210 if (dl.dl_info.link_id.c == SACCH) {
211 lpd.sacch := true;
212 /* FIXME: how to deal with UI frames in B4 format (lo length!) */
213 } else {
214 lpd.sacch := false;
215 }
216 lpd.sapi := dl.dl_info.link_id.sapi;
217 lpd.lapdm.b := dec_LapdmFrameB(dl.payload.data_ind.payload);
218 LAPDM_SP.send(lpd);
219 }
220
221 /* encode any LAPDm record from user and pass it on to L1CTL */
222 [] LAPDM_SP.receive(LAPDm_ph_data:?) -> value lpd {
223 var octetstring buf;
224 var RslLinkId link_id;
225 if (lpd.sacch) {
226 link_id := valueof(ts_RslLinkID_SACCH(lpd.sapi));
227 } else {
228 link_id := valueof(ts_RslLinkID_DCCH(lpd.sapi));
229 }
230 buf := enc_LapdmFrame(lpd.lapdm);
231 L1CTL.send(t_L1CTL_DATA_REQ(chan_desc.chan_nr, link_id, buf));
232 }
233
234 /* Release dedicated channel */
235 [] LAPDM_SP.receive(DCCH_release_req:?) {
236 /* go back to BCCH */
237 f_release_dcch();
238 }
239
240 [] LAPDM_SP.receive {}
241 [] L1CTL.receive {}
242
243
244 }
245 }
246 } /* while (1) */
247 }
248}