blob: 00da04b0093af06efe5929c645bb7737a831dbba [file] [log] [blame]
Philipp Maier22401432017-03-24 17:59:26 +01001/* (C) 2016 by Sysmocom s.f.m.c. GmbH
2 * All Rights Reserved
3 *
4 * Author: Philipp Maier
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
Harald Welte20725b92017-05-15 12:50:04 +020021#include "config.h"
22
Philipp Maier22401432017-03-24 17:59:26 +010023#include <osmocom/core/utils.h>
24#include <osmocom/core/msgb.h>
Harald Welte95871da2017-05-15 12:11:36 +020025#include <osmocom/core/byteswap.h>
Philipp Maier22401432017-03-24 17:59:26 +010026#include <string.h>
Philipp Maier22401432017-03-24 17:59:26 +010027#include <errno.h>
28#include <osmocom/gsm/protocol/gsm_08_08.h>
29
30#define IP_V4_ADDR_LEN 4
31#define IP_V6_ADDR_LEN 16
32#define IP_PORT_LEN 2
33
Philipp Maiere0c65302017-03-28 17:05:40 +020034#define CHANNEL_TYPE_ELEMENT_MAXLEN 11
35#define CHANNEL_TYPE_ELEMENT_MINLEN 3
Philipp Maier14e76b92017-03-28 18:36:52 +020036#define ENCRYPT_INFO_ELEMENT_MINLEN 1
Philipp Maier6f725d62017-03-24 18:03:17 +010037
Harald Welte20725b92017-05-15 12:50:04 +020038#ifdef HAVE_SYS_SOCKET_H
39
40#include <sys/socket.h>
41#include <netinet/in.h>
Harald Welte96e2a002017-06-12 21:44:18 +020042
43/*! \addtogroup gsm0808
44 * @{
45 */
46
47/*! \brief Encode TS 08.08 AoIP transport address IE
48 * \param[out] msg Message Buffer to which to append IE
49 * \param[in] ss Socket Address to be used in IE
50 * \returns number of bytes added to \a msg */
Philipp Maier22401432017-03-24 17:59:26 +010051uint8_t gsm0808_enc_aoip_trasp_addr(struct msgb *msg,
52 const struct sockaddr_storage *ss)
53{
54 /* See also 3GPP TS 48.008 3.2.2.102 AoIP Transport Layer Address */
55 struct sockaddr_in *sin;
56 struct sockaddr_in6 *sin6;
57 uint16_t port = 0;
58 uint8_t *ptr;
59 uint8_t *old_tail;
60 uint8_t *tlv_len;
61
62 OSMO_ASSERT(msg);
63 OSMO_ASSERT(ss);
64 OSMO_ASSERT(ss->ss_family == AF_INET || ss->ss_family == AF_INET6);
65
66 msgb_put_u8(msg, GSM0808_IE_AOIP_TRASP_ADDR);
67 tlv_len = msgb_put(msg,1);
68 old_tail = msg->tail;
69
70 switch (ss->ss_family) {
71 case AF_INET:
72 sin = (struct sockaddr_in *)ss;
Harald Welte95871da2017-05-15 12:11:36 +020073 port = osmo_ntohs(sin->sin_port);
Philipp Maier22401432017-03-24 17:59:26 +010074 ptr = msgb_put(msg, IP_V4_ADDR_LEN);
75 memcpy(ptr, &sin->sin_addr.s_addr, IP_V4_ADDR_LEN);
76 break;
77 case AF_INET6:
78 sin6 = (struct sockaddr_in6 *)ss;
Harald Welte95871da2017-05-15 12:11:36 +020079 port = osmo_ntohs(sin6->sin6_port);
Philipp Maier22401432017-03-24 17:59:26 +010080 ptr = msgb_put(msg, IP_V6_ADDR_LEN);
81 memcpy(ptr, sin6->sin6_addr.s6_addr, IP_V6_ADDR_LEN);
82 break;
83 }
84
85 msgb_put_u16(msg, port);
86
87 *tlv_len = (uint8_t) (msg->tail - old_tail);
88 return *tlv_len + 2;
89}
90
Harald Welte96e2a002017-06-12 21:44:18 +020091/*! \brief Decode TS 08.08 AoIP transport address IE
92 * \param[out] ss Caller-provided memory where decoded socket addr is stored
93 * \param[in] elem pointer to IE value
94 * \param[in] len length of \a elem in bytes
95 * \returns number of bytes parsed */
Philipp Maier22401432017-03-24 17:59:26 +010096int gsm0808_dec_aoip_trasp_addr(struct sockaddr_storage *ss,
97 const uint8_t *elem, uint8_t len)
98{
99 /* See also 3GPP TS 48.008 3.2.2.102 AoIP Transport Layer Address */
100 struct sockaddr_in sin;
101 struct sockaddr_in6 sin6;
102 const uint8_t *old_elem = elem;
103
104 OSMO_ASSERT(ss);
105 if (!elem)
106 return -EINVAL;
107 if (len <= 0)
108 return -EINVAL;
109
110 memset(ss, 0, sizeof(*ss));
111
112 switch (len) {
113 case IP_V4_ADDR_LEN + IP_PORT_LEN:
114 memset(&sin, 0, sizeof(sin));
115 sin.sin_family = AF_INET;
116
117 memcpy(&sin.sin_addr.s_addr, elem, IP_V4_ADDR_LEN);
118 elem += IP_V4_ADDR_LEN;
119 sin.sin_port = osmo_load16le(elem);
120 elem += IP_PORT_LEN;
121
122 memcpy(ss, &sin, sizeof(sin));
123 break;
124 case IP_V6_ADDR_LEN + IP_PORT_LEN:
125 memset(&sin6, 0, sizeof(sin6));
126 sin6.sin6_family = AF_INET6;
127
128 memcpy(sin6.sin6_addr.s6_addr, elem, IP_V6_ADDR_LEN);
129 elem += IP_V6_ADDR_LEN;
130 sin6.sin6_port = osmo_load16le(elem);
131 elem += IP_PORT_LEN;
132
133 memcpy(ss, &sin6, sizeof(sin6));
134 break;
135 default:
136 /* Malformed element! */
137 return -EINVAL;
138 break;
139 }
140
141 return (int)(elem - old_elem);
142}
Philipp Maier6f725d62017-03-24 18:03:17 +0100143
Harald Welte20725b92017-05-15 12:50:04 +0200144#endif /* HAVE_SYS_SOCKET_H */
145
Philipp Maier6f725d62017-03-24 18:03:17 +0100146/* Helper function for gsm0808_enc_speech_codec()
147 * and gsm0808_enc_speech_codec_list() */
148static uint8_t enc_speech_codec(struct msgb *msg,
149 const struct gsm0808_speech_codec *sc)
150{
151 /* See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
152 uint8_t header = 0;
153 uint8_t *old_tail;
Philipp Maierbb839662017-06-01 17:11:19 +0200154 bool type_extended;
155
156 /* Note: Extended codec types are codec types that require 8 instead
157 * of 4 bit to fully specify the selected codec. In the following,
158 * we check if we work with an extended type or not. We also check
159 * if the codec type is valid at all. */
160 switch(sc->type) {
161 case GSM0808_SCT_FR1:
162 case GSM0808_SCT_FR2:
163 case GSM0808_SCT_FR3:
164 case GSM0808_SCT_FR4:
165 case GSM0808_SCT_FR5:
166 case GSM0808_SCT_HR1:
167 case GSM0808_SCT_HR3:
168 case GSM0808_SCT_HR4:
169 case GSM0808_SCT_HR6:
170 type_extended = false;
171 break;
172 case GSM0808_SCT_CSD:
173 type_extended = true;
174 break;
175 default:
176 /* Invalid codec type specified */
177 OSMO_ASSERT(false);
178 break;
179 }
Philipp Maier6f725d62017-03-24 18:03:17 +0100180
181 old_tail = msg->tail;
182
183 if (sc->fi)
184 header |= (1 << 7);
185 if (sc->pi)
186 header |= (1 << 6);
187 if (sc->pt)
188 header |= (1 << 5);
189 if (sc->tf)
190 header |= (1 << 4);
Philipp Maierbb839662017-06-01 17:11:19 +0200191
192 if (type_extended) {
Philipp Maier6f725d62017-03-24 18:03:17 +0100193 header |= 0x0f;
194 msgb_put_u8(msg, header);
Philipp Maierbb839662017-06-01 17:11:19 +0200195 msgb_put_u8(msg, sc->type);
Philipp Maier6f725d62017-03-24 18:03:17 +0100196 } else {
197 OSMO_ASSERT(sc->type < 0x0f);
198 header |= sc->type;
199 msgb_put_u8(msg, header);
Philipp Maier6f725d62017-03-24 18:03:17 +0100200 }
201
Philipp Maierbb839662017-06-01 17:11:19 +0200202 /* Note: Whether a configuration is present or not depends on the
203 * selected codec type. If present, it can either consist of one
204 * or two octets, depending on the codec type */
205 switch (sc->type) {
206 case GSM0808_SCT_FR3:
207 case GSM0808_SCT_HR3:
208 case GSM0808_SCT_HR6:
Philipp Maier6f725d62017-03-24 18:03:17 +0100209 msgb_put_u16(msg, sc->cfg);
Philipp Maierbb839662017-06-01 17:11:19 +0200210 break;
211 case GSM0808_SCT_FR4:
212 case GSM0808_SCT_FR5:
213 case GSM0808_SCT_HR4:
214 case GSM0808_SCT_CSD:
215 OSMO_ASSERT((sc->cfg & 0xff00) == 0)
216 msgb_put_u8(msg, (uint8_t) sc->cfg & 0xff);
217 break;
218 default:
219 OSMO_ASSERT(sc->cfg == 0);
220 break;
221 }
Philipp Maier6f725d62017-03-24 18:03:17 +0100222
223 return (uint8_t) (msg->tail - old_tail);
224}
225
Harald Welte96e2a002017-06-12 21:44:18 +0200226/*! \brief Encode TS 08.08 Speech Codec IE
227 * \param[out] msg Message Buffer to which IE will be appended
228 * \param[in] sc Speech Codec to be encoded into IE
229 * \returns number of bytes appended to \a msg */
Philipp Maier6f725d62017-03-24 18:03:17 +0100230uint8_t gsm0808_enc_speech_codec(struct msgb *msg,
231 const struct gsm0808_speech_codec *sc)
232{
233 uint8_t *old_tail;
234 uint8_t *tlv_len;
235
236 OSMO_ASSERT(msg);
237 OSMO_ASSERT(sc);
238
239 msgb_put_u8(msg, GSM0808_IE_SPEECH_CODEC);
240 tlv_len = msgb_put(msg, 1);
241 old_tail = msg->tail;
242
243 enc_speech_codec(msg, sc);
244
245 *tlv_len = (uint8_t) (msg->tail - old_tail);
246 return *tlv_len + 2;
247}
248
Harald Welte96e2a002017-06-12 21:44:18 +0200249/*! \brief Decode TS 08.08 Speech Codec IE
250 * \param[out] sc Caller-allocated memory for Speech Codec
251 * \param[in] elem IE value to be decoded
252 * \param[in] len Length of \a elem in bytes
253 * \returns number of bytes parsed; negative on error */
Philipp Maier6f725d62017-03-24 18:03:17 +0100254int gsm0808_dec_speech_codec(struct gsm0808_speech_codec *sc,
255 const uint8_t *elem, uint8_t len)
256{
257 /* See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
258 uint8_t header;
259 const uint8_t *old_elem = elem;
260
261 OSMO_ASSERT(sc);
262 if (!elem)
263 return -EINVAL;
264 if (len <= 0)
265 return -EINVAL;
266
267 memset(sc, 0, sizeof(*sc));
268
269 header = *elem;
270
Philipp Maier85a6af22017-04-28 10:55:05 +0200271 /* An extended codec type needs at least two fields,
272 * bail if the input data length is not sufficient. */
Philipp Maier6f725d62017-03-24 18:03:17 +0100273 if ((header & 0x0F) == 0x0F && len < 2)
274 return -EINVAL;
Philipp Maier6f725d62017-03-24 18:03:17 +0100275
276 elem++;
277 len--;
278
279 if (header & (1 << 7))
280 sc->fi = true;
281 if (header & (1 << 6))
282 sc->pi = true;
283 if (header & (1 << 5))
284 sc->pt = true;
285 if (header & (1 << 4))
286 sc->tf = true;
287
288 if ((header & 0x0F) != 0x0F) {
289 sc->type = (header & 0x0F);
Philipp Maierbb839662017-06-01 17:11:19 +0200290 } else {
291 sc->type = *elem;
292 elem++;
293 len--;
Philipp Maier6f725d62017-03-24 18:03:17 +0100294 }
295
Philipp Maierbb839662017-06-01 17:11:19 +0200296 /* Note: Whether a configuration is present or not depends on the
297 * selected codec type. If present, it can either consist of one or
298 * two octets depending on the codec type */
299 switch (sc->type) {
300 case GSM0808_SCT_FR1:
301 case GSM0808_SCT_FR2:
302 case GSM0808_SCT_HR1:
303 break;
304 case GSM0808_SCT_HR4:
305 case GSM0808_SCT_CSD:
306 case GSM0808_SCT_FR4:
307 case GSM0808_SCT_FR5:
308 if (len < 1)
309 return -EINVAL;
310 sc->cfg = *elem;
311 elem++;
312 break;
313 case GSM0808_SCT_FR3:
314 case GSM0808_SCT_HR3:
315 case GSM0808_SCT_HR6:
316 if (len < 2)
317 return -EINVAL;
318 sc->cfg = osmo_load16be(elem);
319 elem += 2;
320 break;
321 default:
322 /* Invalid codec type => malformed speech codec element! */
323 return -EINVAL;
324 break;
325 }
Philipp Maier6f725d62017-03-24 18:03:17 +0100326
327 return (int)(elem - old_elem);
328}
329
Harald Welte96e2a002017-06-12 21:44:18 +0200330/*! \brief Encode TS 08.08 Speech Codec list
331 * \param[out] msg Message Buffer to which IE is to be appended
332 * \param[in] scl Speech Codec List to be encoded into IE
333 * \returns number of bytes added to \a msg */
Philipp Maier6f725d62017-03-24 18:03:17 +0100334uint8_t gsm0808_enc_speech_codec_list(struct msgb *msg,
335 const struct gsm0808_speech_codec_list *scl)
336{
337 uint8_t *old_tail;
338 uint8_t *tlv_len;
339 unsigned int i;
340 uint8_t rc;
341 unsigned int bytes_used = 0;
342
343 OSMO_ASSERT(msg);
344 OSMO_ASSERT(scl);
345
346 /* Empty list */
347 OSMO_ASSERT(scl->len >= 1);
348
349 msgb_put_u8(msg, GSM0808_IE_SPEECH_CODEC_LIST);
350 tlv_len = msgb_put(msg, 1);
351 old_tail = msg->tail;
352
353 for (i = 0; i < scl->len; i++) {
354 rc = enc_speech_codec(msg, &scl->codec[i]);
355 OSMO_ASSERT(rc >= 1);
356 bytes_used += rc;
357 OSMO_ASSERT(bytes_used <= 255);
358 }
359
360 *tlv_len = (uint8_t) (msg->tail - old_tail);
361 return *tlv_len + 2;
362}
363
Harald Welte96e2a002017-06-12 21:44:18 +0200364/*! \brief Decode TS 08.08 Speech Codec list IE
365 * \param[out] scl Caller-provided memory to store codec list
366 * \param[in] elem IE value to be decoded
367 * \param[in] len Length of \a elem in bytes
368 * \returns number of bytes parsed; negative on error */
Philipp Maier6f725d62017-03-24 18:03:17 +0100369int gsm0808_dec_speech_codec_list(struct gsm0808_speech_codec_list *scl,
370 const uint8_t *elem, uint8_t len)
371{
372 const uint8_t *old_elem = elem;
373 unsigned int i;
374 int rc;
375 uint8_t decoded = 0;
376
377 OSMO_ASSERT(scl);
378 if (!elem)
379 return -EINVAL;
380 if (len <= 0)
381 return -EINVAL;
382
383 memset(scl, 0, sizeof(*scl));
384
385 for (i = 0; i < ARRAY_SIZE(scl->codec); i++) {
386 if (len <= 0)
387 break;
388
389 rc = gsm0808_dec_speech_codec(&scl->codec[i], elem, len);
390 if (rc < 1)
391 return -EINVAL;
392
393 elem+=rc;
394 len -= rc;
395 decoded++;
396 }
397
398 scl->len = decoded;
399
400 /* Empty list */
401 if (decoded < 1) {
402 return -EINVAL;
403 }
404
405 return (int)(elem - old_elem);
406}
Philipp Maiere0c65302017-03-28 17:05:40 +0200407
Harald Welte96e2a002017-06-12 21:44:18 +0200408/*! \brief Encode TS 08.08 Channel Type IE
409 * \param[out] msg Message Buffer to which IE is to be appended
410 * \param[in] ct Channel Type to be encoded
411 * \returns number of bytes added to \a msg */
Philipp Maiere0c65302017-03-28 17:05:40 +0200412uint8_t gsm0808_enc_channel_type(struct msgb *msg,
413 const struct gsm0808_channel_type *ct)
414{
415 unsigned int i;
416 uint8_t byte;
417 uint8_t *old_tail;
418 uint8_t *tlv_len;
419
420 OSMO_ASSERT(msg);
421 OSMO_ASSERT(ct);
422 OSMO_ASSERT(ct->perm_spch_len <= CHANNEL_TYPE_ELEMENT_MAXLEN - 2);
423
424 /* FIXME: Implement encoding support for Data
425 * and Speech + CTM Text Telephony */
426 if ((ct->ch_indctr & 0x0f) != GSM0808_CHAN_SPEECH
427 && (ct->ch_indctr & 0x0f) != GSM0808_CHAN_SIGN)
428 OSMO_ASSERT(false);
429
430 msgb_put_u8(msg, GSM0808_IE_CHANNEL_TYPE);
431 tlv_len = msgb_put(msg, 1);
432 old_tail = msg->tail;
433
434 msgb_put_u8(msg, ct->ch_indctr & 0x0f);
435 msgb_put_u8(msg, ct->ch_rate_type);
436
437 for (i = 0; i < ct->perm_spch_len; i++) {
438 byte = ct->perm_spch[i];
439
440 if (i < ct->perm_spch_len - 1)
441 byte |= 0x80;
442 msgb_put_u8(msg, byte);
443 }
444
445 *tlv_len = (uint8_t) (msg->tail - old_tail);
446 return *tlv_len + 2;
447}
448
Harald Welte96e2a002017-06-12 21:44:18 +0200449/*! \brief Decode TS 08.08 Channel Type IE
450 * \param[out] ct Caller-provided memory to store channel type
451 * \param[in] elem IE Value to be decoded
452 * \param[in] len Length of \a elem in bytes
453 * \returns number of bytes parsed; negative on error */
Philipp Maiere0c65302017-03-28 17:05:40 +0200454int gsm0808_dec_channel_type(struct gsm0808_channel_type *ct,
455 const uint8_t *elem, uint8_t len)
456{
457 unsigned int i;
458 uint8_t byte;
459 const uint8_t *old_elem = elem;
460
461 OSMO_ASSERT(ct);
462 if (!elem)
463 return -EINVAL;
464 if (len <= 0)
465 return -EINVAL;
466
467 memset(ct, 0, sizeof(*ct));
468
469 ct->ch_indctr = (*elem) & 0x0f;
470 elem++;
471 ct->ch_rate_type = (*elem) & 0x0f;
472 elem++;
473
474 for (i = 0; i < ARRAY_SIZE(ct->perm_spch); i++) {
475 byte = *elem;
476 elem++;
477 ct->perm_spch[i] = byte & 0x7f;
478 if ((byte & 0x80) == 0x00)
479 break;
480 }
481 ct->perm_spch_len = i + 1;
482
483 return (int)(elem - old_elem);
484}
Philipp Maier14e76b92017-03-28 18:36:52 +0200485
Harald Welte96e2a002017-06-12 21:44:18 +0200486/*! \brief Encode TS 08.08 Encryption Information IE
487 * \param[out] msg Message Buffer to which IE is to be appended
488 * \param[in] ei Encryption Information to be encoded
489 * \returns number of bytes appended to \a msg */
Philipp Maier14e76b92017-03-28 18:36:52 +0200490uint8_t gsm0808_enc_encrypt_info(struct msgb *msg,
491 const struct gsm0808_encrypt_info *ei)
492{
493 unsigned int i;
494 uint8_t perm_algo = 0;
495 uint8_t *ptr;
496 uint8_t *old_tail;
497 uint8_t *tlv_len;
498
499 OSMO_ASSERT(msg);
500 OSMO_ASSERT(ei);
501 OSMO_ASSERT(ei->key_len <= ARRAY_SIZE(ei->key));
502 OSMO_ASSERT(ei->perm_algo_len <= ENCRY_INFO_PERM_ALGO_MAXLEN);
503
504 msgb_put_u8(msg, GSM0808_IE_ENCRYPTION_INFORMATION);
505 tlv_len = msgb_put(msg, 1);
506 old_tail = msg->tail;
507
508 for (i = 0; i < ei->perm_algo_len; i++) {
509 /* Note: gsm_08_08.h defines the permitted algorithms
510 * as an enum which ranges from 0x01 to 0x08 */
511 OSMO_ASSERT(ei->perm_algo[i] != 0);
512 OSMO_ASSERT(ei->perm_algo[i] <= ENCRY_INFO_PERM_ALGO_MAXLEN);
513 perm_algo |= (1 << (ei->perm_algo[i] - 1));
514 }
515
516 msgb_put_u8(msg, perm_algo);
517 ptr = msgb_put(msg, ei->key_len);
518 memcpy(ptr, ei->key, ei->key_len);
519
520 *tlv_len = (uint8_t) (msg->tail - old_tail);
521 return *tlv_len + 2;
522}
523
Harald Welte96e2a002017-06-12 21:44:18 +0200524/*! \brief Decode TS 08.08 Encryption Information IE
525 * \param[out] ei Caller-provided memory to store encryption information
526 * \param[in] elem IE value to be decoded
527 * \param[in] len Length of \a elem in bytes
528 * \returns number of bytes parsed; negative on error */
Philipp Maier14e76b92017-03-28 18:36:52 +0200529int gsm0808_dec_encrypt_info(struct gsm0808_encrypt_info *ei,
530 const uint8_t *elem, uint8_t len)
531{
532 uint8_t perm_algo;
533 unsigned int i;
534 unsigned int perm_algo_len = 0;
535 const uint8_t *old_elem = elem;
536
537 OSMO_ASSERT(ei);
538 if (!elem)
539 return -EINVAL;
540 if (len <= 0)
541 return -EINVAL;
542
543 memset(ei, 0, sizeof(*ei));
544
545 perm_algo = *elem;
546 elem++;
547
548 for (i = 0; i < ENCRY_INFO_PERM_ALGO_MAXLEN; i++) {
549 if (perm_algo & (1 << i)) {
550 ei->perm_algo[perm_algo_len] = i + 1;
551 perm_algo_len++;
552 }
553 }
554 ei->perm_algo_len = perm_algo_len;
555
556 ei->key_len = len - 1;
557 memcpy(ei->key, elem, ei->key_len);
558 elem+=ei->key_len;
559
560 return (int)(elem - old_elem);
561}
Philipp Maier783047e2017-03-29 11:35:50 +0200562
Harald Welte96e2a002017-06-12 21:44:18 +0200563/*! \brief Encode TS 08.08 Cell Identifier List IE
564 * \param[out] msg Message Buffer to which IE is to be appended
565 * \param[in] cil Cell ID List to be encoded
566 * \returns number of bytes appended to \a msg */
Philipp Maier783047e2017-03-29 11:35:50 +0200567uint8_t gsm0808_enc_cell_id_list(struct msgb *msg,
568 const struct gsm0808_cell_id_list *cil)
569{
570 uint8_t *old_tail;
571 uint8_t *tlv_len;
572 unsigned int i;
573
574 OSMO_ASSERT(msg);
575 OSMO_ASSERT(cil);
576
577 msgb_put_u8(msg, GSM0808_IE_CELL_IDENTIFIER_LIST);
578 tlv_len = msgb_put(msg, 1);
579 old_tail = msg->tail;
580
581 msgb_put_u8(msg, cil->id_discr & 0x0f);
582
583 switch (cil->id_discr) {
584 case CELL_IDENT_LAC:
585 OSMO_ASSERT(cil->id_list_len <= CELL_ID_LIST_LAC_MAXLEN)
586 for (i=0;i<cil->id_list_len;i++) {
587 msgb_put_u16(msg, cil->id_list_lac[i]);
588 }
589 break;
590 case CELL_IDENT_BSS:
591 /* Does not have any list items */
592 break;
593 default:
594 /* FIXME: Implement support for all identifier list elements */
595 OSMO_ASSERT(false);
596 }
597
598 *tlv_len = (uint8_t) (msg->tail - old_tail);
599 return *tlv_len + 2;
600}
601
Harald Welte96e2a002017-06-12 21:44:18 +0200602/*! \brief Decode Cell Identifier List IE
603 * \param[out] cil Caller-provided memory to store Cell ID list
604 * \param[in] elem IE value to be decoded
605 * \param[in] len Length of \a elem in bytes
606 * \returns number of bytes parsed; negative on error */
Philipp Maier783047e2017-03-29 11:35:50 +0200607int gsm0808_dec_cell_id_list(struct gsm0808_cell_id_list *cil,
608 const uint8_t *elem, uint8_t len)
609{
610 uint8_t id_discr;
611 const uint8_t *old_elem = elem;
612 unsigned int item_count = 0;
613
614 OSMO_ASSERT(cil);
615 if (!elem)
616 return -EINVAL;
617 if (len <= 0)
618 return -EINVAL;
619
620 memset(cil, 0, sizeof(*cil));
621
622 id_discr = *elem & 0x0f;
623 elem++;
624 len--;
625
626 cil->id_discr = id_discr;
627
628 switch (id_discr) {
629 case CELL_IDENT_LAC:
630 while (len >= 2) {
631 cil->id_list_lac[item_count] = osmo_load16be(elem);
632 elem += 2;
633 item_count++;
634 len -= 2;
635 }
636 case CELL_IDENT_BSS:
637 /* Does not have any list items */
638 break;
639 default:
640 /* FIXME: Implement support for all identifier list elements */
641 return -EINVAL;
642 }
643
644 cil->id_list_len = item_count;
645 return (int)(elem - old_elem);
646}
Harald Welte96e2a002017-06-12 21:44:18 +0200647
Philipp Maier3149b0d2017-06-02 13:22:34 +0200648/*! \brief Convert the representation of the permitted speech codec identifier
649 * that is used in struct gsm0808_channel_type to the speech codec
650 * representation we use in struct gsm0808_speech_codec.
651 * \param[in] perm_spch to be converted (see also gsm0808_permitted_speech)
652 * \returns GSM speech codec type; negative on error */
653int gsm0808_chan_type_to_speech_codec(uint8_t perm_spch)
654{
655 /*! The speech codec type, which is used in the channel type field to
656 * signal the permitted speech versions (codecs) has a different
657 * encoding than the type field in the speech codec type element
658 * (See also 3GPP TS 48.008, 3.2.2.11 and 3.2.2.103) */
659
660 switch (perm_spch) {
661 case GSM0808_PERM_FR1:
662 return GSM0808_SCT_FR1;
663 case GSM0808_PERM_FR2:
664 return GSM0808_SCT_FR2;
665 case GSM0808_PERM_FR3:
666 return GSM0808_SCT_FR3;
667 case GSM0808_PERM_FR4:
668 return GSM0808_SCT_FR4;
669 case GSM0808_PERM_FR5:
670 return GSM0808_SCT_FR5;
671 case GSM0808_PERM_HR1:
672 return GSM0808_SCT_HR1;
673 case GSM0808_PERM_HR3:
674 return GSM0808_SCT_HR3;
675 case GSM0808_PERM_HR4:
676 return GSM0808_SCT_HR4;
677 case GSM0808_PERM_HR6:
678 return GSM0808_SCT_HR6;
679 }
680
681 /* Invalid input */
682 return -EINVAL;
683}
684
Philipp Maier884ba0f2017-06-02 13:49:16 +0200685/*! \brief Extrapolate a speech codec field from a given permitted speech
686 * parameter (channel type).
687 * \param[out] sc Caller provided memory to store the resulting speech codec
688 * \param[in] perm_spch value that is used to derive the speech codec info
689 * (see also: enum gsm0808_speech_codec_type in gsm0808_utils.h)
690 * \returns zero when successful; negative on error */
691int gsm0808_speech_codec_from_chan_type(struct gsm0808_speech_codec *sc,
692 uint8_t perm_spch)
693{
694 int rc;
695
696 memset(sc, 0, sizeof(*sc));
697
698 /* Determine codec type */
699 rc = gsm0808_chan_type_to_speech_codec(perm_spch);
700 if (rc < 0)
701 return -EINVAL;
702 sc->type = (uint8_t) rc;
703
704 /* Depending on the speech codec type, pick a default codec
705 * configuration that exactly matches the configuration on the
706 * air interface. */
707 switch (sc->type) {
708 case GSM0808_SCT_FR3:
709 sc->cfg = GSM0808_SC_CFG_DEFAULT_FR_AMR;
710 break;
711 case GSM0808_SCT_FR4:
712 sc->cfg = GSM0808_SC_CFG_DEFAULT_OFR_AMR_WB;
713 break;
714 case GSM0808_SCT_FR5:
715 sc->cfg = GSM0808_SC_CFG_DEFAULT_FR_AMR_WB;
716 break;
717 case GSM0808_SCT_HR3:
718 sc->cfg = GSM0808_SC_CFG_DEFAULT_HR_AMR;
719 break;
720 case GSM0808_SCT_HR4:
721 sc->cfg = GSM0808_SC_CFG_DEFAULT_OHR_AMR_WB;
722 break;
723 case GSM0808_SCT_HR6:
724 sc->cfg = GSM0808_SC_CFG_DEFAULT_OHR_AMR;
725 break;
726 default:
727 /* Note: Not all codec types specify a default setting,
728 * in this case, we just set the field to zero. */
729 sc->cfg = 0;
730 }
731
732 /* Tag all codecs as "Full IP"
733 * (see als 3GPP TS 48.008 3.2.2.103) */
734 sc->fi = true;
735
736 return 0;
737}
738
Harald Welte96e2a002017-06-12 21:44:18 +0200739/*! @} */