summaryrefslogtreecommitdiffstats
path: root/src/host/virt_phy/src/gsmtapl1_if.c
blob: ac1c2b5f8477f26c6242c026da64cb50a3a369fe (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
/* 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 <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>

/**
 * 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, 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, "Gsmtap msg could not send to virt um - "
			     "(arfcn=%u, type=%u, subtype=%u, timeslot=%u, subslot=%u)\n",
			     gh->arfcn, gh->type, gh->sub_type, gh->timeslot,
			     gh->sub_slot);
		} else {
			DEBUGPMS(DVIRPHY, ms, "Sending gsmtap msg to virt um - "
			       "(arfcn=%u, type=%u, subtype=%u, timeslot=%u, subslot=%u)\n",
			       gh->arfcn, gh->type, gh->sub_type, gh->timeslot,
			       gh->sub_slot);
		}
	} 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) {
		LOGPMS(DVIRPHY, LOGL_NOTICE, ms,
		     "Ignoring gsmtap msg from virt um - msg arfcn=%d not equal synced arfcn=%d!\n",
		     arfcn, ms->state.serving_cell.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:
		/* 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 gsmtap msg from virt um - channel type is uplink only!\n");
		break;
	case GSMTAP_CHANNEL_SDCCH:
	case GSMTAP_CHANNEL_CCCH:
	case GSMTAP_CHANNEL_PACCH:
	case GSMTAP_CHANNEL_PDCH:
	case GSMTAP_CHANNEL_PTCCH:
	case GSMTAP_CHANNEL_CBCH51:
	case GSMTAP_CHANNEL_CBCH52:
		LOGPMS(DVIRPHY, LOGL_NOTICE, ms,
		     "Ignoring gsmtap msg from virt um - channel type not supported!\n");
		break;
	default:
		LOGPMS(DVIRPHY, LOGL_NOTICE, ms,
		     "Ignoring gsmtap msg from virt um - channel type unknown.\n");
		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 */

	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);

	/* generally ignore all uplink messages received */
	if (arfcn & GSMTAP_ARFCN_F_UPLINK) {
		LOGP(DVIRPHY, LOGL_NOTICE, "Ignoring gsmtap msg from virt um - uplink flag set!\n");
		goto freemsg;
	}

	DEBUGP(DVIRPHY, "Receiving gsmtap msg from virt um - "
	       "(arfcn=%u, framenumber=%u, type=%s, subtype=%s, timeslot=%u, subslot=%u, rsl_chan_type=0x%2x, link_id=0x%2x, chan_nr=0x%2x)\n",
	       arfcn, fn, get_value_string(gsmtap_type_names, gh->type),
	       get_value_string(gsmtap_gsm_channel_names, gsmtap_chantype), timeslot,
	       subslot, rsl_chantype, link_id, chan_nr);

	/* 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);
}