246 lines
6.3 KiB
Plaintext
246 lines
6.3 KiB
Plaintext
/* 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) */
|
|
}
|
|
}
|