summaryrefslogtreecommitdiffstats
path: root/src/host/virt_phy/src/gsmtapl1_if.c
blob: 8202046f92794c2b91dba6a52dd68744f05970f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/* GSMTAP layer1 is transmits gsmtap messages over a virtual layer 1.*/

/* (C) 2016 by Sebastian Stumpf <sebastian.stumpf87@googlemail.com>
 * (C) 2017 by Harald Welte <laforge@gnumonks.org>
 *
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <osmocom/core/gsmtap.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/rsl.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/core/msgb.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <l1ctl_proto.h>
#include <virtphy/virtual_um.h>
#include <virtphy/l1ctl_sock.h>
#include <virtphy/virt_l1_model.h>
#include <virtphy/l1ctl_sap.h>
#include <virtphy/gsmtapl1_if.h>
#include <virtphy/logging.h>
#include <virtphy/virt_l1_sched.h>

static char *pseudo_lchan_name(uint16_t arfcn, uint8_t ts, uint8_t ss, uint8_t sub_type)
{
	static char lname[64];
	snprintf(lname, sizeof(lname), "(arfcn=%u,ts=%u,ss=%u,type=%s)",
		arfcn, ts, ss, get_value_string(gsmtap_gsm_channel_names, sub_type));
	return lname;
}

/**
 * Replace l11 header of given msgb by a gsmtap header and send it over the virt um.
 */
void gsmtapl1_tx_to_virt_um_inst(struct l1_model_ms *ms, uint32_t fn, uint8_t tn, struct msgb *msg)
{
	struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data;
	struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *)l1h->data;
	struct gsmtap_hdr *gh;
	struct msgb *outmsg;	/* msg to send with gsmtap header prepended */
	uint16_t arfcn = ms->state.serving_cell.arfcn;	/* arfcn of the cell we currently camp on */
	uint8_t signal_dbm = 63;	/* signal strength */
	uint8_t snr = 63;	/* signal noise ratio, 63 is best */
	uint8_t *data = msgb_l2(msg);	/* data to transmit (whole message without l1 header) */
	uint8_t data_len = msgb_l2len(msg);	/* length of data */

	uint8_t rsl_chantype;	/* rsl chan type (8.58, 9.3.1) */
	uint8_t subslot;	/* multiframe subslot to send msg in (tch -> 0-26, bcch/ccch -> 0-51) */
	uint8_t timeslot;	/* tdma timeslot to send in (0-7) */
	uint8_t gsmtap_chan;	/* the gsmtap channel */

	rsl_dec_chan_nr(ul->chan_nr, &rsl_chantype, &subslot, &timeslot);
	gsmtap_chan = chantype_rsl2gsmtap(rsl_chantype, ul->link_id);

	/* arfcn needs to be flagged to be able to distinguish between uplink and downlink */
	outmsg = gsmtap_makemsg(arfcn | GSMTAP_ARFCN_F_UPLINK, timeslot,
				gsmtap_chan, subslot, fn, signal_dbm, snr, data,
				data_len);
	if (outmsg) {
		outmsg->l1h = msgb_data(outmsg);
		gh = msgb_l1(outmsg);
		if (virt_um_write_msg(ms->vui, outmsg) == -1) {
			LOGPMS(DVIRPHY, LOGL_ERROR, ms, "%s Tx go GSMTAP failed: %s\n",
				pseudo_lchan_name(gh->arfcn, gh->timeslot, gh->sub_slot, gh->sub_type),
				strerror(errno));
		} else {
			DEBUGPMS(DVIRPHY, ms, "%s: Tx to GSMTAP: %s\n",
				pseudo_lchan_name(gh->arfcn, gh->timeslot, gh->sub_slot, gh->sub_type),
				osmo_hexdump(data, data_len));
		}
	} else
		LOGPMS(DVIRPHY, LOGL_ERROR, ms, "GSMTAP msg could not be created!\n");

	/* free message */
	msgb_free(msg);
}

/**
 * @see virt_prim_fbsb.c
 */
extern void prim_fbsb_sync(struct l1_model_ms *ms, struct msgb *msg);

/**
 * @see virt_prim_pm.c
 */
extern uint16_t prim_pm_set_sig_strength(struct l1_model_ms *ms, uint16_t arfcn, int16_t sig_lev);

static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg, uint32_t fn,
				uint16_t arfcn, uint8_t timeslot, uint8_t subslot,
				uint8_t gsmtap_chantype, uint8_t chan_nr, uint8_t link_id,
				uint8_t snr_db)
{
	struct l1_model_ms *ms = lsc->priv;
	uint8_t signal_dbm = dbm2rxlev(prim_pm_set_sig_strength(ms, arfcn & GSMTAP_ARFCN_MASK, MAX_SIG_LEV_DBM));	/* Power measurement with each received massage */

	gsm_fn2gsmtime(&ms->state.downlink_time, fn);

	/* we do not forward messages to l23 if we are in network search state */
	if (ms->state.state == MS_STATE_IDLE_SEARCHING)
		return;

	/* forward downlink msg to fbsb sync routine if we are in sync state */
	if (ms->state.state == MS_STATE_IDLE_SYNCING) {
		prim_fbsb_sync(ms, msg);
		return;
	}
	/* generally ignore all messages coming from another arfcn than the camped one */
	if (ms->state.serving_cell.arfcn != arfcn) {
		return;
	}

	virt_l1_sched_sync_time(ms, ms->state.downlink_time, 0);
	virt_l1_sched_execute(ms, fn);

	/* switch case with removed ACCH flag */
	switch (gsmtap_chantype & ~GSMTAP_CHANNEL_ACCH & 0xff) {
	case GSMTAP_CHANNEL_TCH_H:
	case GSMTAP_CHANNEL_TCH_F:
#if 0
		/* TODO: handle voice */
		if (!facch && !tch_acch) {
			l1ctl_tx_traffic_ind(msg, arfcn, link_id, chan_nr, fn,
					     snr, signal_dbm, 0, 0);
		}
#endif
	case GSMTAP_CHANNEL_SDCCH4:
	case GSMTAP_CHANNEL_SDCCH8:
		/* only forward messages on dedicated channels to l2, if
		 * the timeslot and subslot is fitting */
		if (ms->state.dedicated.tn == timeslot
		    && ms->state.dedicated.subslot == subslot) {
			l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, snr_db, signal_dbm, 0, 0);
		}
		break;
	case GSMTAP_CHANNEL_AGCH:
	case GSMTAP_CHANNEL_PCH:
	case GSMTAP_CHANNEL_BCCH:
	case GSMTAP_CHANNEL_CBCH51:
	case GSMTAP_CHANNEL_CBCH52:
		/* save to just forward here, as upper layer ignores messages that
		 * do not fit the current state (e.g.  gsm48_rr.c:2159) */
		l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, snr_db, signal_dbm, 0, 0);
		break;
	case GSMTAP_CHANNEL_RACH:
		LOGPMS(DVIRPHY, LOGL_NOTICE, ms, "Ignoring unexpected RACH in downlink ?!?\n");
		break;
	case GSMTAP_CHANNEL_SDCCH:
	case GSMTAP_CHANNEL_CCCH:
	case GSMTAP_CHANNEL_PACCH:
	case GSMTAP_CHANNEL_PDCH:
	case GSMTAP_CHANNEL_PTCCH:
		LOGPMS(DVIRPHY, LOGL_NOTICE, ms, "Ignoring unsupported channel type %s\n",
			get_value_string(gsmtap_gsm_channel_names, gsmtap_chantype));
		break;
	default:
		LOGPMS(DVIRPHY, LOGL_NOTICE, ms, "Ignoring unknown channel type %s\n",
			get_value_string(gsmtap_gsm_channel_names, gsmtap_chantype));
		break;
	}
}

/**
 * Receive a gsmtap message from the virt um.
 *
 * As we do not have a downlink scheduler, but not all dl messages must be processed and thus forwarded to l2, this function also implements some message filtering.
 * E.g. we do not forward:
 * - uplink messages
 * - messages with a wrong arfcn
 * - if in MS_STATE_IDLE_SEARCHING
 */
void gsmtapl1_rx_from_virt_um_inst_cb(struct virt_um_inst *vui,
				      struct msgb *msg)
{
	struct l1ctl_sock_inst *lsi = vui->priv;
	struct l1ctl_sock_client *lsc;

	if (!msg)
		return;

	struct gsmtap_hdr *gh = msgb_l1(msg);
	uint32_t fn = ntohl(gh->frame_number);	/* frame number of the rcv msg */
	uint16_t arfcn = ntohs(gh->arfcn);	/* arfcn of the received msg */
	uint8_t gsmtap_chantype = gh->sub_type;	/* gsmtap channel type */
	uint8_t snr = gh->snr_db;	/* signal noise ratio */
	uint8_t subslot = gh->sub_slot;	/* multiframe subslot to send msg in (tch -> 0-26, bcch/ccch -> 0-51) */
	uint8_t timeslot = gh->timeslot;	/* tdma timeslot to send in (0-7) */
	uint8_t rsl_chantype;	/* rsl chan type (8.58, 9.3.1) */
	uint8_t link_id;	/* rsl link id tells if this is an ssociated or dedicated link */
	uint8_t chan_nr;	/* encoded rsl channel type, timeslot and mf subslot */
	struct gsm_time gtime;

	msg->l2h = msgb_pull(msg, sizeof(*gh));
	chantype_gsmtap2rsl(gsmtap_chantype, &rsl_chantype, &link_id);
	/* see TS 08.58 -> 9.3.1 for channel number encoding */
	chan_nr = rsl_enc_chan_nr(rsl_chantype, subslot, timeslot);

	gsm_fn2gsmtime(&gtime, fn);

	DEBUGP(DVIRPHY, "%s Rx from VirtUM: FN=%s chan_nr=0x%02x link_id=0x%02x\n",
		pseudo_lchan_name(arfcn, timeslot, subslot, gsmtap_chantype),
		osmo_dump_gsmtime(&gtime), chan_nr, link_id);

	/* generally ignore all uplink messages received */
	if (arfcn & GSMTAP_ARFCN_F_UPLINK) {
		LOGP(DVIRPHY, LOGL_NOTICE, "Ignoring unexpected uplink message in downlink!\n");
		goto freemsg;
	}

	/* dispatch the incoming DL message from GSMTAP to each of the registered L1CTL instances */
	llist_for_each_entry(lsc, &lsi->clients, list) {
		l1ctl_from_virt_um(lsc, msg, fn, arfcn, timeslot, subslot, gsmtap_chantype,
				   chan_nr, link_id, snr);
	}

freemsg:
	talloc_free(msg);
}