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