aboutsummaryrefslogtreecommitdiffstats
path: root/src/osmo-bsc/data_rate_pref.c
blob: 44a733fb27f55b41698d07b41e049c63adf1b22e (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
/*
 * (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 * All Rights Reserved
 *
 * Author: Oliver Smith
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/bsc/data_rate_pref.h>
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/lchan.h>

static int gsm0808_data_rate_transp_to_gsm0858(enum gsm0808_data_rate_transp rate)
{
	switch (rate) {
	case GSM0808_DATA_RATE_TRANSP_32k0:
		return RSL_CMOD_CSD_T_32k0;
	case GSM0808_DATA_RATE_TRANSP_28k8:
		return RSL_CMOD_CSD_T_29k0;
	case GSM0808_DATA_RATE_TRANSP_14k4:
		return RSL_CMOD_CSD_T_14k4;
	case GSM0808_DATA_RATE_TRANSP_9k6:
		return RSL_CMOD_CSD_T_9k6;
	case GSM0808_DATA_RATE_TRANSP_4k8:
		return RSL_CMOD_CSD_T_4k8;
	case GSM0808_DATA_RATE_TRANSP_2k4:
		return RSL_CMOD_CSD_T_2k4;
	case GSM0808_DATA_RATE_TRANSP_1k2:
		return RSL_CMOD_CSD_T_1k2;
	case GSM0808_DATA_RATE_TRANSP_600:
		return RSL_CMOD_CSD_T_600;
	case GSM0808_DATA_RATE_TRANSP_1200_75:
		return RSL_CMOD_CSD_T_1200_75;
	default:
		LOGP(DMSC, LOGL_ERROR, "Unsupported transparent data rate 0x%x\n", rate);
		return -1;
	}
}

static int gsm0808_data_rate_transp_to_gsm0408(enum gsm0808_data_rate_transp rate)
{
	switch (rate) {
	case GSM0808_DATA_RATE_TRANSP_14k4:
		return GSM48_CMODE_DATA_14k5;
	case GSM0808_DATA_RATE_TRANSP_9k6:
		return GSM48_CMODE_DATA_12k0;
	case GSM0808_DATA_RATE_TRANSP_4k8:
		return GSM48_CMODE_DATA_6k0;
	case GSM0808_DATA_RATE_TRANSP_2k4:
	case GSM0808_DATA_RATE_TRANSP_1k2:
	case GSM0808_DATA_RATE_TRANSP_600:
	case GSM0808_DATA_RATE_TRANSP_1200_75:
		return GSM48_CMODE_DATA_3k6;
	default:
		LOGP(DMSC, LOGL_ERROR, "Unsupported transparent data rate 0x%x\n", rate);
		return -1;
	}
}

static int gsm0808_data_rate_non_transp_to_gsm0408(enum gsm0808_data_rate_non_transp rate, bool full_rate)
{
	switch (rate) {
	case GSM0808_DATA_RATE_NON_TRANSP_12000_6000:
		if (full_rate)
			return GSM48_CMODE_DATA_12k0;
		return GSM48_CMODE_DATA_6k0;
	case GSM0808_DATA_RATE_NON_TRANSP_14k5:
		return GSM48_CMODE_DATA_14k5;
	case GSM0808_DATA_RATE_NON_TRANSP_12k0:
		return GSM48_CMODE_DATA_12k0;
	case GSM0808_DATA_RATE_NON_TRANSP_6k0:
		return GSM48_CMODE_DATA_6k0;
	default:
		LOGP(DMSC, LOGL_ERROR, "Unsupported non-transparent data rate 0x%x\n", rate);
		return -1;
	}
}

static int gsm0808_data_rate_non_transp_to_gsm0858(enum gsm0808_data_rate_non_transp rate, bool full_rate)
{
	switch (rate) {
	case GSM0808_DATA_RATE_NON_TRANSP_12000_6000:
		if (full_rate)
			return RSL_CMOD_CSD_NT_12k0;
		return RSL_CMOD_CSD_NT_6k0;
	case GSM0808_DATA_RATE_NON_TRANSP_14k5:
		return RSL_CMOD_CSD_NT_14k5;
	case GSM0808_DATA_RATE_NON_TRANSP_12k0:
		return RSL_CMOD_CSD_NT_12k0;
	case GSM0808_DATA_RATE_NON_TRANSP_6k0:
		return RSL_CMOD_CSD_NT_6k0;
	case GSM0808_DATA_RATE_NON_TRANSP_43k5:
		return RSL_CMOD_CSD_NT_43k5;
	case GSM0808_DATA_RATE_NON_TRANSP_29k0:
		return RSL_CMOD_CSD_NT_28k8;
	default:
		LOGP(DMSC, LOGL_ERROR, "Unsupported non-transparent data rate 0x%x\n", rate);
		return -1;
	}
}

static enum gsm48_chan_mode match_non_transp_data_rate(const struct gsm0808_channel_type *ct, bool full_rate)
{
	/* FIXME: Handle ct->data_rate_allowed too if it is set. Find the best
	 * match by comparing the preferred ct->data_rate + all allowed
	 * ct->data_rate_allowed against what's most suitable for the BTS. */

	return gsm0808_data_rate_non_transp_to_gsm0858(ct->data_rate, full_rate);
}

/*! Match the GSM 08.08 channel type received from the MSC to suitable data for
 * the BTS, the GSM 04.08 channel mode, channel rate (FR/HR) and GSM 08.58
 * data rate.
 *  \param[out] ch_mode_rate resulting channel rate, channel mode and data rate
 *  \param[in] ct GSM 08.08 channel type received from MSC.
 *  \param[in] full_rate true means FR is preferred, false means HR
 *  \returns 0 on success, -1 in case no match was found */
int match_data_rate_pref(struct channel_mode_and_rate *ch_mode_rate,
			 const struct gsm0808_channel_type *ct,
			 const bool full_rate)
{
	int rc;
	*ch_mode_rate = (struct channel_mode_and_rate){};
	ch_mode_rate->chan_rate = full_rate ? CH_RATE_FULL : CH_RATE_HALF;
	ch_mode_rate->data_transparent = ct->data_transparent;

	if (ct->data_transparent) {
		rc = gsm0808_data_rate_transp_to_gsm0858(ct->data_rate);
		if (rc == -1)
			return -1;
		ch_mode_rate->data_rate.t = rc;

		rc = gsm0808_data_rate_transp_to_gsm0408(ct->data_rate);
		if (rc == -1)
			return -1;
		ch_mode_rate->chan_mode = rc;
	} else {
		rc = match_non_transp_data_rate(ct, full_rate);
		if (rc == -1)
			return -1;
		ch_mode_rate->data_rate.nt = rc;

		rc = gsm0808_data_rate_non_transp_to_gsm0408(ct->data_rate, full_rate);
		if (rc == -1)
			return -1;
		ch_mode_rate->chan_mode = rc;
	}

	return 0;
}