blob: d3a868d1cd99e9d687d89c00c654f31fd17c5c06 [file] [log] [blame]
Harald Welte1e908662010-03-07 23:39:54 +01001/* GSM Mobile Radio Interface Layer 3 messages
2 * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
3
4/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
Harald Welteb1ac2b92010-04-09 07:50:18 +02005 * (C) 2009-2010 by Andreas Eversberg
Harald Welte1e908662010-03-07 23:39:54 +01006 *
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 */
24
25
26#include <stdint.h>
27#include <string.h>
28#include <errno.h>
29
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010030#include <osmocom/core/utils.h>
31#include <osmocom/core/msgb.h>
32#include <osmocom/gsm/tlv.h>
33#include <osmocom/gsm/mncc.h>
34#include <osmocom/gsm/protocol/gsm_04_08.h>
35#include <osmocom/gsm/gsm48_ie.h>
Harald Welte1e908662010-03-07 23:39:54 +010036
Harald Welte96e2a002017-06-12 21:44:18 +020037/*! \addtogroup gsm0408
38 * @{
39 */
40
Harald Welte1e908662010-03-07 23:39:54 +010041static const char bcd_num_digits[] = {
42 '0', '1', '2', '3', '4', '5', '6', '7',
43 '8', '9', '*', '#', 'a', 'b', 'c', '\0'
44};
45
Neels Hofmeyr87e45502017-06-20 00:17:59 +020046/*! decode a 'called/calling/connect party BCD number' as in 10.5.4.7
Harald Welte96e2a002017-06-12 21:44:18 +020047 * \param[out] Caller-provided output buffer
48 * \param[in] bcd_lv Length-Value portion of to-be-decoded IE
49 * \param[in] h_len Length of an optional heder between L and V portion
50 * \returns - in case of success; negative on error */
Harald Welte1e908662010-03-07 23:39:54 +010051int gsm48_decode_bcd_number(char *output, int output_len,
52 const uint8_t *bcd_lv, int h_len)
53{
54 uint8_t in_len = bcd_lv[0];
55 int i;
56
57 for (i = 1 + h_len; i <= in_len; i++) {
58 /* lower nibble */
59 output_len--;
60 if (output_len <= 1)
61 break;
62 *output++ = bcd_num_digits[bcd_lv[i] & 0xf];
63
64 /* higher nibble */
65 output_len--;
66 if (output_len <= 1)
67 break;
68 *output++ = bcd_num_digits[bcd_lv[i] >> 4];
69 }
70 if (output_len >= 1)
71 *output++ = '\0';
72
73 return 0;
74}
75
Neels Hofmeyr87e45502017-06-20 00:17:59 +020076/*! convert a single ASCII character to call-control BCD */
Harald Welte1e908662010-03-07 23:39:54 +010077static int asc_to_bcd(const char asc)
78{
79 int i;
80
81 for (i = 0; i < ARRAY_SIZE(bcd_num_digits); i++) {
82 if (bcd_num_digits[i] == asc)
83 return i;
84 }
85 return -EINVAL;
86}
87
Neels Hofmeyr87e45502017-06-20 00:17:59 +020088/*! convert a ASCII phone number to 'called/calling/connect party BCD number'
Harald Welte96e2a002017-06-12 21:44:18 +020089 * \param[out] bcd_lv Caller-provided output buffer
90 * \param[in] max_len Maximum Length of \a bcd_lv
91 * \param[in] h_len Length of an optional heder between L and V portion
92 * \param[in] input phone number as 0-terminated ASCII
93 * \returns number of bytes used in \a bcd_lv */
Harald Welte1e908662010-03-07 23:39:54 +010094int gsm48_encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len,
95 int h_len, const char *input)
96{
97 int in_len = strlen(input);
98 int i;
99 uint8_t *bcd_cur = bcd_lv + 1 + h_len;
100
101 /* two digits per byte, plus type byte */
102 bcd_lv[0] = in_len/2 + h_len;
103 if (in_len % 2)
104 bcd_lv[0]++;
105
106 if (bcd_lv[0] > max_len)
107 return -EIO;
108
109 for (i = 0; i < in_len; i++) {
110 int rc = asc_to_bcd(input[i]);
111 if (rc < 0)
112 return rc;
113 if (i % 2 == 0)
114 *bcd_cur = rc;
115 else
116 *bcd_cur++ |= (rc << 4);
117 }
118 /* append padding nibble in case of odd length */
119 if (i % 2)
120 *bcd_cur++ |= 0xf0;
121
122 /* return how many bytes we used */
123 return (bcd_cur - bcd_lv);
124}
125
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200126/*! Decode TS 04.08 Bearer Capability IE (10.5.4.5)
Harald Welte96e2a002017-06-12 21:44:18 +0200127 * \param[out] Caller-provided memory for decoded output
128 * \[aram[in] LV portion of TS 04.08 Bearer Capability
129 * \returns 0 on success; negative on error */
Harald Welte1e908662010-03-07 23:39:54 +0100130int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
131 const uint8_t *lv)
132{
133 uint8_t in_len = lv[0];
134 int i, s;
135
136 if (in_len < 1)
137 return -EINVAL;
138
139 bcap->speech_ver[0] = -1; /* end of list, of maximum 7 values */
140
141 /* octet 3 */
142 bcap->transfer = lv[1] & 0x07;
143 bcap->mode = (lv[1] & 0x08) >> 3;
144 bcap->coding = (lv[1] & 0x10) >> 4;
145 bcap->radio = (lv[1] & 0x60) >> 5;
146
Harald Weltec8a0b932012-08-24 21:27:26 +0200147 switch (bcap->transfer) {
148 case GSM_MNCC_BCAP_SPEECH:
Harald Welte1e908662010-03-07 23:39:54 +0100149 i = 1;
150 s = 0;
151 while(!(lv[i] & 0x80)) {
152 i++; /* octet 3a etc */
153 if (in_len < i)
154 return 0;
155 bcap->speech_ver[s++] = lv[i] & 0x0f;
156 bcap->speech_ver[s] = -1; /* end of list */
157 if (i == 2) /* octet 3a */
158 bcap->speech_ctm = (lv[i] & 0x20) >> 5;
159 if (s == 7) /* maximum speech versions + end of list */
160 return 0;
161 }
Harald Weltec8a0b932012-08-24 21:27:26 +0200162 break;
163 case GSM_MNCC_BCAP_UNR_DIG:
164 case GSM_MNCC_BCAP_FAX_G3:
165 i = 1;
166 while(!(lv[i] & 0x80)) {
167 i++; /* octet 3a etc */
168 if (in_len < i)
169 return 0;
170 /* ignore them */
171 }
172 /* octet 4: skip */
173 i++;
174 /* octet 5 */
175 i++;
176 if (in_len < i)
177 return 0;
178 bcap->data.rate_adaption = (lv[i] >> 3) & 3;
179 bcap->data.sig_access = lv[i] & 7;
180 while(!(lv[i] & 0x80)) {
181 i++; /* octet 5a etc */
182 if (in_len < i)
183 return 0;
184 /* ignore them */
185 }
186 /* octet 6 */
187 i++;
188 if (in_len < i)
189 return 0;
190 bcap->data.async = lv[i] & 1;
191 if (!(lv[i] & 0x80)) {
192 i++;
193 if (in_len < i)
194 return 0;
195 /* octet 6a */
196 bcap->data.nr_stop_bits = ((lv[i] >> 7) & 1) + 1;
197 if (lv[i] & 0x10)
198 bcap->data.nr_data_bits = 8;
199 else
200 bcap->data.nr_data_bits = 7;
201 bcap->data.user_rate = lv[i] & 0xf;
202
203 if (!(lv[i] & 0x80)) {
204 i++;
205 if (in_len < i)
206 return 0;
207 /* octet 6b */
208 bcap->data.parity = lv[i] & 7;
209 bcap->data.interm_rate = (lv[i] >> 5) & 3;
210
211 /* octet 6c */
212 if (!(lv[i] & 0x80)) {
213 i++;
214 if (in_len < i)
215 return 0;
216 bcap->data.transp = (lv[i] >> 5) & 3;
217 bcap->data.modem_type = lv[i] & 0x1F;
218 }
219 }
220
221 }
222 break;
223 default:
Harald Welte1e908662010-03-07 23:39:54 +0100224 i = 1;
225 while (!(lv[i] & 0x80)) {
226 i++; /* octet 3a etc */
227 if (in_len < i)
228 return 0;
229 /* ignore them */
230 }
231 /* FIXME: implement OCTET 4+ parsing */
Harald Weltec8a0b932012-08-24 21:27:26 +0200232 break;
Harald Welte1e908662010-03-07 23:39:54 +0100233 }
234
235 return 0;
236}
237
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200238/*! Encode TS 04.08 Bearer Capability IE (10.5.4.5)
Harald Welte96e2a002017-06-12 21:44:18 +0200239 * \param[out] msg Message Buffer to which IE is to be appended
240 * \param[in] lv_only Write only LV portion (1) or TLV (0)
241 * \param[in] bcap Decoded Bearer Capability to be encoded
242 * \returns 0 on success; negative on error */
Harald Welte1e908662010-03-07 23:39:54 +0100243int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only,
244 const struct gsm_mncc_bearer_cap *bcap)
245{
246 uint8_t lv[32 + 1];
247 int i = 1, s;
248
249 lv[1] = bcap->transfer;
250 lv[1] |= bcap->mode << 3;
251 lv[1] |= bcap->coding << 4;
252 lv[1] |= bcap->radio << 5;
253
Harald Weltec8a0b932012-08-24 21:27:26 +0200254 switch (bcap->transfer) {
255 case GSM_MNCC_BCAP_SPEECH:
Harald Welte1e908662010-03-07 23:39:54 +0100256 for (s = 0; bcap->speech_ver[s] >= 0; s++) {
257 i++; /* octet 3a etc */
258 lv[i] = bcap->speech_ver[s];
259 if (i == 2) /* octet 3a */
260 lv[i] |= bcap->speech_ctm << 5;
261 }
262 lv[i] |= 0x80; /* last IE of octet 3 etc */
Harald Weltec8a0b932012-08-24 21:27:26 +0200263 break;
264 case GSM48_BCAP_ITCAP_UNR_DIG_INF:
265 case GSM48_BCAP_ITCAP_FAX_G3:
266 lv[i++] |= 0x80; /* last IE of octet 3 etc */
267 /* octet 4 */
268 lv[i++] = 0xb8;
269 /* octet 5 */
270 lv[i++] = 0x80 | ((bcap->data.rate_adaption & 3) << 3)
271 | (bcap->data.sig_access & 7);
272 /* octet 6 */
273 lv[i++] = 0x20 | (bcap->data.async & 1);
274 /* octet 6a */
275 lv[i++] = (bcap->data.user_rate & 0xf) |
276 (bcap->data.nr_data_bits == 8 ? 0x10 : 0x00) |
277 (bcap->data.nr_stop_bits == 2 ? 0x40 : 0x00);
278 /* octet 6b */
279 lv[i++] = (bcap->data.parity & 7) |
280 ((bcap->data.interm_rate & 3) << 5);
281 /* octet 6c */
282 lv[i] = 0x80 | (bcap->data.modem_type & 0x1f);
283 break;
284 default:
285 return -EINVAL;
Harald Welte1e908662010-03-07 23:39:54 +0100286 }
287
288 lv[0] = i;
289 if (lv_only)
290 msgb_lv_put(msg, lv[0], lv+1);
291 else
292 msgb_tlv_put(msg, GSM48_IE_BEARER_CAP, lv[0], lv+1);
293
294 return 0;
295}
296
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200297/*! Decode TS 04.08 Call Control Capabilities IE (10.5.4.5a)
Harald Welte96e2a002017-06-12 21:44:18 +0200298 * \param[out] Caller-provided memory for decoded CC capabilities
299 * \param[in] lv Length-Value of IE
300 * \retursns 0 on success; negative on error */
Harald Welte1e908662010-03-07 23:39:54 +0100301int gsm48_decode_cccap(struct gsm_mncc_cccap *ccap, const uint8_t *lv)
302{
303 uint8_t in_len = lv[0];
304
305 if (in_len < 1)
306 return -EINVAL;
307
308 /* octet 3 */
309 ccap->dtmf = lv[1] & 0x01;
310 ccap->pcp = (lv[1] & 0x02) >> 1;
311
312 return 0;
313}
314
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200315/*! Encodoe TS 04.08 Call Control Capabilities (10.5.4.5a)
Harald Welte96e2a002017-06-12 21:44:18 +0200316 * \param[out] msg Message Buffer to which to append IE (as TLV)
317 * \param[in] ccap Decoded CC Capabilities to be encoded
318 * \returns 0 on success; negative on error */
Harald Welte1e908662010-03-07 23:39:54 +0100319int gsm48_encode_cccap(struct msgb *msg,
320 const struct gsm_mncc_cccap *ccap)
321{
322 uint8_t lv[2];
323
324 lv[0] = 1;
325 lv[1] = 0;
326 if (ccap->dtmf)
327 lv [1] |= 0x01;
328 if (ccap->pcp)
329 lv [1] |= 0x02;
330
331 msgb_tlv_put(msg, GSM48_IE_CC_CAP, lv[0], lv+1);
332
333 return 0;
334}
335
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200336/*! Decode TS 04.08 Called Party BCD Number IE (10.5.4.7)
Harald Welte96e2a002017-06-12 21:44:18 +0200337 * \param[out] called Caller-provided memory for decoded number
338 * \param[in] lv Length-Value portion of IE
339 * \returns 0 on success; negative on error */
Harald Welte1e908662010-03-07 23:39:54 +0100340int gsm48_decode_called(struct gsm_mncc_number *called,
341 const uint8_t *lv)
342{
343 uint8_t in_len = lv[0];
344
345 if (in_len < 1)
346 return -EINVAL;
347
348 /* octet 3 */
349 called->plan = lv[1] & 0x0f;
350 called->type = (lv[1] & 0x70) >> 4;
351
352 /* octet 4..N */
353 gsm48_decode_bcd_number(called->number, sizeof(called->number), lv, 1);
354
355 return 0;
356}
357
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200358/*! Encode TS 04.08 Called Party IE (10.5.4.7)
Harald Welte96e2a002017-06-12 21:44:18 +0200359 * \param[out] msg Mesage Buffer to which to append IE (as TLV)
360 * \param[in] called MNCC Number to encode/append
361 * \returns 0 on success; negative on error */
Harald Welte1e908662010-03-07 23:39:54 +0100362int gsm48_encode_called(struct msgb *msg,
363 const struct gsm_mncc_number *called)
364{
365 uint8_t lv[18];
366 int ret;
367
368 /* octet 3 */
Sylvain Munaut47ee6932010-09-20 20:59:23 +0200369 lv[1] = 0x80; /* no extension */
370 lv[1] |= called->plan;
Harald Welte1e908662010-03-07 23:39:54 +0100371 lv[1] |= called->type << 4;
372
373 /* octet 4..N, octet 2 */
374 ret = gsm48_encode_bcd_number(lv, sizeof(lv), 1, called->number);
375 if (ret < 0)
376 return ret;
377
378 msgb_tlv_put(msg, GSM48_IE_CALLED_BCD, lv[0], lv+1);
379
380 return 0;
381}
382
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200383/*! Decode TS 04.08 Caller ID
Harald Welte96e2a002017-06-12 21:44:18 +0200384 * \param[out] called Caller-provided memory for decoded number
385 * \param[in] lv Length-Value portion of IE
386 * \returns 0 on success; negative on error */
Harald Welte1e908662010-03-07 23:39:54 +0100387int gsm48_decode_callerid(struct gsm_mncc_number *callerid,
388 const uint8_t *lv)
389{
390 uint8_t in_len = lv[0];
391 int i = 1;
392
393 if (in_len < 1)
394 return -EINVAL;
395
396 /* octet 3 */
397 callerid->plan = lv[1] & 0x0f;
398 callerid->type = (lv[1] & 0x70) >> 4;
399
400 /* octet 3a */
401 if (!(lv[1] & 0x80)) {
402 callerid->screen = lv[2] & 0x03;
403 callerid->present = (lv[2] & 0x60) >> 5;
404 i = 2;
405 }
406
407 /* octet 4..N */
408 gsm48_decode_bcd_number(callerid->number, sizeof(callerid->number), lv, i);
409
410 return 0;
411}
412
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200413/*! Encode TS 04.08 Caller ID IE
Harald Welte96e2a002017-06-12 21:44:18 +0200414 * \param[out] msg Mesage Buffer to which to append IE (as TLV)
415 * \param[in] ie IE Identifier (tag)
416 * \param[in] max_len maximum generated output in bytes
417 * \param[in] callerid MNCC Number to encode/append
418 * \returns 0 on success; negative on error */
Harald Welte1e908662010-03-07 23:39:54 +0100419int gsm48_encode_callerid(struct msgb *msg, int ie, int max_len,
420 const struct gsm_mncc_number *callerid)
421{
422 uint8_t lv[max_len - 1];
423 int h_len = 1;
424 int ret;
425
426 /* octet 3 */
427 lv[1] = callerid->plan;
428 lv[1] |= callerid->type << 4;
429
430 if (callerid->present || callerid->screen) {
431 /* octet 3a */
432 lv[2] = callerid->screen;
433 lv[2] |= callerid->present << 5;
434 lv[2] |= 0x80;
435 h_len++;
436 } else
437 lv[1] |= 0x80;
438
439 /* octet 4..N, octet 2 */
440 ret = gsm48_encode_bcd_number(lv, sizeof(lv), h_len, callerid->number);
441 if (ret < 0)
442 return ret;
443
444 msgb_tlv_put(msg, ie, lv[0], lv+1);
445
446 return 0;
447}
448
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200449/*! Decode TS 04.08 Cause IE (10.5.4.11)
Harald Welte96e2a002017-06-12 21:44:18 +0200450 * \param[out] cause Caller-provided memory for output
451 * \param[in] lv LV portion of Cause IE
452 * \returns 0 on success; negative on error */
Harald Welte1e908662010-03-07 23:39:54 +0100453int gsm48_decode_cause(struct gsm_mncc_cause *cause,
454 const uint8_t *lv)
455{
456 uint8_t in_len = lv[0];
457 int i;
458
459 if (in_len < 2)
460 return -EINVAL;
461
462 cause->diag_len = 0;
463
464 /* octet 3 */
465 cause->location = lv[1] & 0x0f;
466 cause->coding = (lv[1] & 0x60) >> 5;
467
468 i = 1;
469 if (!(lv[i] & 0x80)) {
470 i++; /* octet 3a */
471 if (in_len < i+1)
472 return 0;
473 cause->rec = 1;
474 cause->rec_val = lv[i] & 0x7f;
475 }
476 i++;
477
478 /* octet 4 */
479 cause->value = lv[i] & 0x7f;
480 i++;
481
482 if (in_len < i) /* no diag */
483 return 0;
484
485 if (in_len - (i-1) > 32) /* maximum 32 octets */
486 return 0;
487
488 /* octet 5-N */
489 memcpy(cause->diag, lv + i, in_len - (i-1));
490 cause->diag_len = in_len - (i-1);
491
492 return 0;
493}
494
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200495/*! Encode TS 04.08 Cause IE (10.5.4.11)
Harald Welte96e2a002017-06-12 21:44:18 +0200496 * \param[out] msg Message Buffer to which to append IE
497 * \param[in] lv_only Encode as LV (1) or TLV (0)
498 * \param[in] cause Cause value to be encoded
499 * \returns 0 on success; negative on error */
Harald Welte1e908662010-03-07 23:39:54 +0100500int gsm48_encode_cause(struct msgb *msg, int lv_only,
501 const struct gsm_mncc_cause *cause)
502{
503 uint8_t lv[32+4];
504 int i;
505
506 if (cause->diag_len > 32)
507 return -EINVAL;
508
509 /* octet 3 */
510 lv[1] = cause->location;
511 lv[1] |= cause->coding << 5;
512
513 i = 1;
514 if (cause->rec) {
515 i++; /* octet 3a */
516 lv[i] = cause->rec_val;
517 }
518 lv[i] |= 0x80; /* end of octet 3 */
519
520 /* octet 4 */
521 i++;
522 lv[i] = 0x80 | cause->value;
523
524 /* octet 5-N */
525 if (cause->diag_len) {
526 memcpy(lv + i, cause->diag, cause->diag_len);
527 i += cause->diag_len;
528 }
529
530 lv[0] = i;
531 if (lv_only)
532 msgb_lv_put(msg, lv[0], lv+1);
533 else
534 msgb_tlv_put(msg, GSM48_IE_CAUSE, lv[0], lv+1);
535
536 return 0;
537}
538
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200539/*! Decode TS 04.08 Calling Number IE (10.5.4.9) */
Harald Welte1e908662010-03-07 23:39:54 +0100540int gsm48_decode_calling(struct gsm_mncc_number *calling,
541 const uint8_t *lv)
542{
543 return gsm48_decode_callerid(calling, lv);
544}
545
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200546/*! Encode TS 04.08 Calling Number IE (10.5.4.9) */
Harald Welte1e908662010-03-07 23:39:54 +0100547int gsm48_encode_calling(struct msgb *msg,
548 const struct gsm_mncc_number *calling)
549{
550 return gsm48_encode_callerid(msg, GSM48_IE_CALLING_BCD, 14, calling);
551}
552
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200553/*! Decode TS 04.08 Connected Number IE (10.5.4.13) */
Harald Welte1e908662010-03-07 23:39:54 +0100554int gsm48_decode_connected(struct gsm_mncc_number *connected,
555 const uint8_t *lv)
556{
557 return gsm48_decode_callerid(connected, lv);
558}
559
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200560/*! Encode TS 04.08 Connected Number IE (10.5.4.13) */
Harald Welte1e908662010-03-07 23:39:54 +0100561int gsm48_encode_connected(struct msgb *msg,
562 const struct gsm_mncc_number *connected)
563{
564 return gsm48_encode_callerid(msg, GSM48_IE_CONN_BCD, 14, connected);
565}
566
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200567/*! Decode TS 04.08 Redirecting Number IE (10.5.4.21b) */
Harald Welte1e908662010-03-07 23:39:54 +0100568int gsm48_decode_redirecting(struct gsm_mncc_number *redirecting,
569 const uint8_t *lv)
570{
571 return gsm48_decode_callerid(redirecting, lv);
572}
573
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200574/*! Encode TS 04.08 Redirecting Number IE (10.5.4.21b) */
Harald Welte1e908662010-03-07 23:39:54 +0100575int gsm48_encode_redirecting(struct msgb *msg,
576 const struct gsm_mncc_number *redirecting)
577{
578 return gsm48_encode_callerid(msg, GSM48_IE_REDIR_BCD, 19, redirecting);
579}
580
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200581/*! Decode TS 04.08 Facility IE (10.5.4.15) */
Harald Welte1e908662010-03-07 23:39:54 +0100582int gsm48_decode_facility(struct gsm_mncc_facility *facility,
583 const uint8_t *lv)
584{
585 uint8_t in_len = lv[0];
586
587 if (in_len < 1)
588 return -EINVAL;
589
590 if (in_len > sizeof(facility->info))
591 return -EINVAL;
592
593 memcpy(facility->info, lv+1, in_len);
594 facility->len = in_len;
595
596 return 0;
597}
598
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200599/*! Encode TS 04.08 Facility IE (10.5.4.15) */
Harald Welte1e908662010-03-07 23:39:54 +0100600int gsm48_encode_facility(struct msgb *msg, int lv_only,
601 const struct gsm_mncc_facility *facility)
602{
603 uint8_t lv[GSM_MAX_FACILITY + 1];
604
605 if (facility->len < 1 || facility->len > GSM_MAX_FACILITY)
606 return -EINVAL;
607
608 memcpy(lv+1, facility->info, facility->len);
609 lv[0] = facility->len;
610 if (lv_only)
611 msgb_lv_put(msg, lv[0], lv+1);
612 else
613 msgb_tlv_put(msg, GSM48_IE_FACILITY, lv[0], lv+1);
614
615 return 0;
616}
617
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200618/*! Decode TS 04.08 Notify IE (10.5.4.20) */
Harald Welte1e908662010-03-07 23:39:54 +0100619int gsm48_decode_notify(int *notify, const uint8_t *v)
620{
621 *notify = v[0] & 0x7f;
622
623 return 0;
624}
625
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200626/*! Encode TS 04.08 Notify IE (10.5.4.20) */
Harald Welte1e908662010-03-07 23:39:54 +0100627int gsm48_encode_notify(struct msgb *msg, int notify)
628{
629 msgb_v_put(msg, notify | 0x80);
630
631 return 0;
632}
633
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200634/*! Decode TS 04.08 Signal IE (10.5.4.23) */
Harald Welte1e908662010-03-07 23:39:54 +0100635int gsm48_decode_signal(int *signal, const uint8_t *v)
636{
637 *signal = v[0];
638
639 return 0;
640}
641
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200642/*! Encode TS 04.08 Signal IE (10.5.4.23) */
Harald Welte1e908662010-03-07 23:39:54 +0100643int gsm48_encode_signal(struct msgb *msg, int signal)
644{
645 msgb_tv_put(msg, GSM48_IE_SIGNAL, signal);
646
647 return 0;
648}
649
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200650/*! Decode TS 04.08 Keypad IE (10.5.4.17) */
Harald Welte1e908662010-03-07 23:39:54 +0100651int gsm48_decode_keypad(int *keypad, const uint8_t *lv)
652{
653 uint8_t in_len = lv[0];
654
655 if (in_len < 1)
656 return -EINVAL;
657
658 *keypad = lv[1] & 0x7f;
659
660 return 0;
661}
662
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200663/*! Encode TS 04.08 Keypad IE (10.5.4.17) */
Harald Welte1e908662010-03-07 23:39:54 +0100664int gsm48_encode_keypad(struct msgb *msg, int keypad)
665{
666 msgb_tv_put(msg, GSM48_IE_KPD_FACILITY, keypad);
667
668 return 0;
669}
670
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200671/*! Decode TS 04.08 Progress IE (10.5.4.21) */
Harald Welte1e908662010-03-07 23:39:54 +0100672int gsm48_decode_progress(struct gsm_mncc_progress *progress,
673 const uint8_t *lv)
674{
675 uint8_t in_len = lv[0];
676
677 if (in_len < 2)
678 return -EINVAL;
679
680 progress->coding = (lv[1] & 0x60) >> 5;
681 progress->location = lv[1] & 0x0f;
682 progress->descr = lv[2] & 0x7f;
683
684 return 0;
685}
686
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200687/*! Encode TS 04.08 Progress IE (10.5.4.21) */
Harald Welte1e908662010-03-07 23:39:54 +0100688int gsm48_encode_progress(struct msgb *msg, int lv_only,
689 const struct gsm_mncc_progress *p)
690{
691 uint8_t lv[3];
692
693 lv[0] = 2;
694 lv[1] = 0x80 | ((p->coding & 0x3) << 5) | (p->location & 0xf);
695 lv[2] = 0x80 | (p->descr & 0x7f);
696 if (lv_only)
697 msgb_lv_put(msg, lv[0], lv+1);
698 else
699 msgb_tlv_put(msg, GSM48_IE_PROGR_IND, lv[0], lv+1);
700
701 return 0;
702}
703
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200704/*! Decode TS 04.08 User-User IE (10.5.4.25) */
Harald Welte1e908662010-03-07 23:39:54 +0100705int gsm48_decode_useruser(struct gsm_mncc_useruser *uu,
706 const uint8_t *lv)
707{
708 uint8_t in_len = lv[0];
709 char *info = uu->info;
710 int info_len = sizeof(uu->info);
711 int i;
712
713 if (in_len < 1)
714 return -EINVAL;
715
716 uu->proto = lv[1];
717
718 for (i = 2; i <= in_len; i++) {
719 info_len--;
720 if (info_len <= 1)
721 break;
722 *info++ = lv[i];
723 }
724 if (info_len >= 1)
725 *info++ = '\0';
726
727 return 0;
728}
729
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200730/*! Encode TS 04.08 User-User IE (10.5.4.25) */
Harald Welte1e908662010-03-07 23:39:54 +0100731int gsm48_encode_useruser(struct msgb *msg, int lv_only,
732 const struct gsm_mncc_useruser *uu)
733{
734 uint8_t lv[GSM_MAX_USERUSER + 2];
735
736 if (strlen(uu->info) > GSM_MAX_USERUSER)
737 return -EINVAL;
738
739 lv[0] = 1 + strlen(uu->info);
740 lv[1] = uu->proto;
741 memcpy(lv + 2, uu->info, strlen(uu->info));
742 if (lv_only)
743 msgb_lv_put(msg, lv[0], lv+1);
744 else
745 msgb_tlv_put(msg, GSM48_IE_USER_USER, lv[0], lv+1);
746
747 return 0;
748}
749
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200750/*! Decode TS 04.08 SS Version IE (10.5.4.24) */
Harald Welte1e908662010-03-07 23:39:54 +0100751int gsm48_decode_ssversion(struct gsm_mncc_ssversion *ssv,
752 const uint8_t *lv)
753{
754 uint8_t in_len = lv[0];
755
756 if (in_len < 1 || in_len < sizeof(ssv->info))
757 return -EINVAL;
758
759 memcpy(ssv->info, lv + 1, in_len);
760 ssv->len = in_len;
761
762 return 0;
763}
764
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200765/*! Encode TS 04.08 SS Version IE (10.5.4.24) */
Harald Welte1e908662010-03-07 23:39:54 +0100766int gsm48_encode_ssversion(struct msgb *msg,
767 const struct gsm_mncc_ssversion *ssv)
768{
769 uint8_t lv[GSM_MAX_SSVERSION + 1];
770
771 if (ssv->len > GSM_MAX_SSVERSION)
772 return -EINVAL;
773
774 lv[0] = ssv->len;
775 memcpy(lv + 1, ssv->info, ssv->len);
776 msgb_tlv_put(msg, GSM48_IE_SS_VERS, lv[0], lv+1);
777
778 return 0;
779}
780
781/* decode 'more data' does not require a function, because it has no value */
782
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200783/*! Encode TS 04.08 More Data IE (10.5.4.19) */
Harald Welte1e908662010-03-07 23:39:54 +0100784int gsm48_encode_more(struct msgb *msg)
785{
786 uint8_t *ie;
787
788 ie = msgb_put(msg, 1);
789 ie[0] = GSM48_IE_MORE_DATA;
790
791 return 0;
792}
793
Sylvain Munaut71fd42f2011-09-01 22:05:29 +0200794static int32_t smod(int32_t n, int32_t m)
795{
796 int32_t res;
797
798 res = n % m;
799
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200800 if (res <= 0)
Sylvain Munaut71fd42f2011-09-01 22:05:29 +0200801 res += m;
802
Sylvain Munaut71fd42f2011-09-01 22:05:29 +0200803 return res;
804}
805
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200806/*! Decode TS 04.08 Cell Channel Description IE (10.5.2.1b) and other frequency lists
Harald Welte96e2a002017-06-12 21:44:18 +0200807 * \param[out] f Caller-provided output memory
808 * \param[in] cd Cell Channel Description IE
809 * \param[in] len Length of \a cd in bytes
810 * \returns 0 on success; negative on error */
Harald Welte1523d702010-08-04 11:46:44 +0200811int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd,
812 uint8_t len, uint8_t mask, uint8_t frqt)
813{
814 int i;
815
816 /* NOTES:
817 *
818 * The Range format uses "SMOD" computation.
819 * e.g. "n SMOD m" equals "((n - 1) % m) + 1"
820 * A cascade of multiple SMOD computations is simpified:
821 * "(n SMOD m) SMOD o" equals "(((n - 1) % m) % o) + 1"
822 *
823 * The Range format uses 16 octets of data in SYSTEM INFORMATION.
824 * When used in dedicated messages, the length can be less.
825 * In this case the ranges are decoded for all frequencies that
826 * fit in the block of given length.
827 */
828
829 /* tabula rasa */
830 for (i = 0; i < 1024; i++)
831 f[i].mask &= ~frqt;
832
833 /* 00..XXX. */
834 if ((cd[0] & 0xc0 & mask) == 0x00) {
835 /* Bit map 0 format */
836 if (len < 16)
837 return -EINVAL;
838 for (i = 1; i <= 124; i++)
839 if ((cd[15 - ((i-1) >> 3)] & (1 << ((i-1) & 7))))
840 f[i].mask |= frqt;
841
842 return 0;
843 }
844
845 /* 10..0XX. */
846 if ((cd[0] & 0xc8 & mask) == 0x80) {
847 /* Range 1024 format */
848 uint16_t w[17]; /* 1..16 */
849 struct gsm48_range_1024 *r = (struct gsm48_range_1024 *)cd;
850
851 if (len < 2)
852 return -EINVAL;
853 memset(w, 0, sizeof(w));
854 if (r->f0)
855 f[0].mask |= frqt;
856 w[1] = (r->w1_hi << 8) | r->w1_lo;
857 if (len >= 4)
858 w[2] = (r->w2_hi << 1) | r->w2_lo;
859 if (len >= 5)
860 w[3] = (r->w3_hi << 2) | r->w3_lo;
861 if (len >= 6)
862 w[4] = (r->w4_hi << 2) | r->w4_lo;
863 if (len >= 7)
864 w[5] = (r->w5_hi << 2) | r->w5_lo;
865 if (len >= 8)
866 w[6] = (r->w6_hi << 2) | r->w6_lo;
867 if (len >= 9)
868 w[7] = (r->w7_hi << 2) | r->w7_lo;
869 if (len >= 10)
870 w[8] = (r->w8_hi << 1) | r->w8_lo;
871 if (len >= 10)
872 w[9] = r->w9;
873 if (len >= 11)
874 w[10] = r->w10;
875 if (len >= 12)
876 w[11] = (r->w11_hi << 6) | r->w11_lo;
877 if (len >= 13)
878 w[12] = (r->w12_hi << 5) | r->w12_lo;
879 if (len >= 14)
880 w[13] = (r->w13_hi << 4) | r->w13_lo;
881 if (len >= 15)
882 w[14] = (r->w14_hi << 3) | r->w14_lo;
883 if (len >= 16)
884 w[15] = (r->w15_hi << 2) | r->w15_lo;
885 if (len >= 16)
886 w[16] = r->w16;
887 if (w[1])
888 f[w[1]].mask |= frqt;
889 if (w[2])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200890 f[smod(w[1] - 512 + w[2], 1023)].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200891 if (w[3])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200892 f[smod(w[1] + w[3], 1023)].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200893 if (w[4])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200894 f[smod(w[1] - 512 + smod(w[2] - 256 + w[4], 511), 1023)].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200895 if (w[5])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200896 f[smod(w[1] + smod(w[3] - 256 + w[5], 511), 1023)].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200897 if (w[6])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200898 f[smod(w[1] - 512 + smod(w[2] + w[6], 511), 1023)].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200899 if (w[7])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200900 f[smod(w[1] + smod(w[3] + w[7], 511), 1023)].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200901 if (w[8])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200902 f[smod(w[1] - 512 + smod(w[2] - 256 + smod(w[4] - 128 + w[8] , 255), 511), 1023)].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200903 if (w[9])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200904 f[smod(w[1] + smod(w[3] - 256 + smod(w[5] - 128 + w[9] , 255), 511), 1023)].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200905 if (w[10])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200906 f[smod(w[1] - 512 + smod(w[2] + smod(w[6] - 128 + w[10], 255), 511), 1023)].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200907 if (w[11])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200908 f[smod(w[1] + smod(w[3] + smod(w[7] - 128 + w[11], 255), 511), 1023)].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200909 if (w[12])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200910 f[smod(w[1] - 512 + smod(w[2] - 256 + smod(w[4] + w[12], 255), 511), 1023)].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200911 if (w[13])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200912 f[smod(w[1] + smod(w[3] - 256 + smod(w[5] + w[13], 255), 511), 1023)].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200913 if (w[14])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200914 f[smod(w[1] - 512 + smod(w[2] + smod(w[6] + w[14], 255), 511), 1023)].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200915 if (w[15])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200916 f[smod(w[1] + smod(w[3] + smod(w[7] + w[15], 255), 511), 1023)].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200917 if (w[16])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200918 f[smod(w[1] - 512 + smod(w[2] - 256 + smod(w[4] - 128 + smod(w[8] - 64 + w[16], 127), 255), 511), 1023)].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200919
920 return 0;
921 }
922 /* 10..100. */
923 if ((cd[0] & 0xce & mask) == 0x88) {
924 /* Range 512 format */
925 uint16_t w[18]; /* 1..17 */
926 struct gsm48_range_512 *r = (struct gsm48_range_512 *)cd;
927
928 if (len < 4)
929 return -EINVAL;
930 memset(w, 0, sizeof(w));
931 w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
932 w[1] = (r->w1_hi << 2) | r->w1_lo;
933 if (len >= 5)
934 w[2] = (r->w2_hi << 2) | r->w2_lo;
935 if (len >= 6)
936 w[3] = (r->w3_hi << 2) | r->w3_lo;
937 if (len >= 7)
938 w[4] = (r->w4_hi << 1) | r->w4_lo;
939 if (len >= 7)
940 w[5] = r->w5;
941 if (len >= 8)
942 w[6] = r->w6;
943 if (len >= 9)
944 w[7] = (r->w7_hi << 6) | r->w7_lo;
945 if (len >= 10)
946 w[8] = (r->w8_hi << 4) | r->w8_lo;
947 if (len >= 11)
948 w[9] = (r->w9_hi << 2) | r->w9_lo;
949 if (len >= 11)
950 w[10] = r->w10;
951 if (len >= 12)
952 w[11] = r->w11;
953 if (len >= 13)
954 w[12] = (r->w12_hi << 4) | r->w12_lo;
955 if (len >= 14)
956 w[13] = (r->w13_hi << 2) | r->w13_lo;
957 if (len >= 14)
958 w[14] = r->w14;
959 if (len >= 15)
960 w[15] = r->w15;
961 if (len >= 16)
962 w[16] = (r->w16_hi << 3) | r->w16_lo;
963 if (len >= 16)
964 w[17] = r->w17;
965 f[w[0]].mask |= frqt;
966 if (w[1])
967 f[(w[0] + w[1]) % 1024].mask |= frqt;
968 if (w[2])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200969 f[(w[0] + smod(w[1] - 256 + w[2], 511)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200970 if (w[3])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200971 f[(w[0] + smod(w[1] + w[3], 511)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200972 if (w[4])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200973 f[(w[0] + smod(w[1] - 256 + smod(w[2] - 128 + w[4], 255), 511)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200974 if (w[5])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200975 f[(w[0] + smod(w[1] + smod(w[3] - 128 + w[5], 255), 511)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200976 if (w[6])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200977 f[(w[0] + smod(w[1] - 256 + smod(w[2] + w[6], 255), 511)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200978 if (w[7])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200979 f[(w[0] + smod(w[1] + smod(w[3] + w[7], 255), 511)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200980 if (w[8])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200981 f[(w[0] + smod(w[1] - 256 + smod(w[2] - 128 + smod(w[4] - 64 + w[8] , 127), 255), 511)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200982 if (w[9])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200983 f[(w[0] + smod(w[1] + smod(w[3] - 128 + smod(w[5] - 64 + w[9] , 127), 255), 511)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200984 if (w[10])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200985 f[(w[0] + smod(w[1] - 256 + smod(w[2] + smod(w[6] - 64 + w[10], 127), 255), 511)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200986 if (w[11])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200987 f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 64 + w[11], 127), 255), 511)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200988 if (w[12])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200989 f[(w[0] + smod(w[1] - 256 + smod(w[2] - 128 + smod(w[4] + w[12], 127), 255), 511)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200990 if (w[13])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200991 f[(w[0] + smod(w[1] + smod(w[3] - 128 + smod(w[5] + w[13], 127), 255), 511)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200992 if (w[14])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200993 f[(w[0] + smod(w[1] - 256 + smod(w[2] + smod(w[6] + w[14], 127), 255), 511)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200994 if (w[15])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200995 f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] + w[15], 127), 255), 511)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200996 if (w[16])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200997 f[(w[0] + smod(w[1] - 256 + smod(w[2] - 128 + smod(w[4] - 64 + smod(w[8] - 32 + w[16], 63), 127), 255), 511)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +0200998 if (w[17])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +0200999 f[(w[0] + smod(w[1] + smod(w[3] - 128 + smod(w[5] - 64 + smod(w[9] - 32 + w[17], 63), 127), 255), 511)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001000
1001 return 0;
1002 }
1003 /* 10..101. */
1004 if ((cd[0] & 0xce & mask) == 0x8a) {
1005 /* Range 256 format */
1006 uint16_t w[22]; /* 1..21 */
1007 struct gsm48_range_256 *r = (struct gsm48_range_256 *)cd;
1008
1009 if (len < 4)
1010 return -EINVAL;
1011 memset(w, 0, sizeof(w));
1012 w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
1013 w[1] = (r->w1_hi << 1) | r->w1_lo;
1014 if (len >= 4)
1015 w[2] = r->w2;
1016 if (len >= 5)
1017 w[3] = r->w3;
1018 if (len >= 6)
1019 w[4] = (r->w4_hi << 5) | r->w4_lo;
1020 if (len >= 7)
1021 w[5] = (r->w5_hi << 3) | r->w5_lo;
1022 if (len >= 8)
1023 w[6] = (r->w6_hi << 1) | r->w6_lo;
1024 if (len >= 8)
1025 w[7] = r->w7;
1026 if (len >= 9)
1027 w[8] = (r->w8_hi << 4) | r->w8_lo;
1028 if (len >= 10)
1029 w[9] = (r->w9_hi << 1) | r->w9_lo;
1030 if (len >= 10)
1031 w[10] = r->w10;
1032 if (len >= 11)
1033 w[11] = (r->w11_hi << 3) | r->w11_lo;
1034 if (len >= 11)
1035 w[12] = r->w12;
1036 if (len >= 12)
1037 w[13] = r->w13;
1038 if (len >= 13)
Jacob Erlbeck85bc5492014-01-13 14:21:23 +01001039 w[14] = (r->w14_hi << 2) | r->w14_lo;
Harald Welte1523d702010-08-04 11:46:44 +02001040 if (len >= 13)
Jacob Erlbeck85bc5492014-01-13 14:21:23 +01001041 w[15] = r->w15;
Harald Welte1523d702010-08-04 11:46:44 +02001042 if (len >= 14)
1043 w[16] = (r->w16_hi << 3) | r->w16_lo;
1044 if (len >= 14)
1045 w[17] = r->w17;
1046 if (len >= 15)
Jacob Erlbeck85bc5492014-01-13 14:21:23 +01001047 w[18] = (r->w18_hi << 3) | r->w18_lo;
Harald Welte1523d702010-08-04 11:46:44 +02001048 if (len >= 15)
Jacob Erlbeck85bc5492014-01-13 14:21:23 +01001049 w[19] = r->w19;
Harald Welte1523d702010-08-04 11:46:44 +02001050 if (len >= 16)
1051 w[20] = (r->w20_hi << 3) | r->w20_lo;
1052 if (len >= 16)
1053 w[21] = r->w21;
1054 f[w[0]].mask |= frqt;
1055 if (w[1])
1056 f[(w[0] + w[1]) % 1024].mask |= frqt;
1057 if (w[2])
Sylvain Munaut71fd42f2011-09-01 22:05:29 +02001058 f[(w[0] + smod(w[1] - 128 + w[2], 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001059 if (w[3])
Sylvain Munaut71fd42f2011-09-01 22:05:29 +02001060 f[(w[0] + smod(w[1] + w[3], 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001061 if (w[4])
Sylvain Munaut71fd42f2011-09-01 22:05:29 +02001062 f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + w[4], 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001063 if (w[5])
Sylvain Munaut71fd42f2011-09-01 22:05:29 +02001064 f[(w[0] + smod(w[1] + smod(w[3] - 64 + w[5], 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001065 if (w[6])
Sylvain Munaut71fd42f2011-09-01 22:05:29 +02001066 f[(w[0] + smod(w[1] - 128 + smod(w[2] + w[6], 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001067 if (w[7])
Sylvain Munaut71fd42f2011-09-01 22:05:29 +02001068 f[(w[0] + smod(w[1] + smod(w[3] + w[7], 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001069 if (w[8])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001070 f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + smod(w[4] - 32 + w[8] , 63), 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001071 if (w[9])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001072 f[(w[0] + smod(w[1] + smod(w[3] - 64 + smod(w[5] - 32 + w[9] , 63), 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001073 if (w[10])
Sylvain Munaut71fd42f2011-09-01 22:05:29 +02001074 f[(w[0] + smod(w[1] - 128 + smod(w[2] + smod(w[6] - 32 + w[10], 63), 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001075 if (w[11])
Sylvain Munaut71fd42f2011-09-01 22:05:29 +02001076 f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 32 + w[11], 63), 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001077 if (w[12])
Sylvain Munaut71fd42f2011-09-01 22:05:29 +02001078 f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + smod(w[4] + w[12], 63), 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001079 if (w[13])
Sylvain Munaut71fd42f2011-09-01 22:05:29 +02001080 f[(w[0] + smod(w[1] + smod(w[3] - 64 + smod(w[5] + w[13], 63), 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001081 if (w[14])
Sylvain Munaut71fd42f2011-09-01 22:05:29 +02001082 f[(w[0] + smod(w[1] - 128 + smod(w[2] + smod(w[6] + w[14], 63), 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001083 if (w[15])
Sylvain Munaut71fd42f2011-09-01 22:05:29 +02001084 f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] + w[15], 63), 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001085 if (w[16])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001086 f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + smod(w[4] - 32 + smod(w[8] - 16 + w[16], 31), 63), 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001087 if (w[17])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001088 f[(w[0] + smod(w[1] + smod(w[3] - 64 + smod(w[5] - 32 + smod(w[9] - 16 + w[17], 31), 63), 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001089 if (w[18])
Sylvain Munaut71fd42f2011-09-01 22:05:29 +02001090 f[(w[0] + smod(w[1] - 128 + smod(w[2] + smod(w[6] - 32 + smod(w[10] - 16 + w[18], 31), 63), 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001091 if (w[19])
Sylvain Munaut71fd42f2011-09-01 22:05:29 +02001092 f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 32 + smod(w[11] - 16 + w[19], 31), 63), 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001093 if (w[20])
Sylvain Munaut71fd42f2011-09-01 22:05:29 +02001094 f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + smod(w[4] + smod(w[12] - 16 + w[20], 31), 63), 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001095 if (w[21])
Sylvain Munaut71fd42f2011-09-01 22:05:29 +02001096 f[(w[0] + smod(w[1] + smod(w[3] - 64 + smod(w[5] + smod(w[13] - 16 + w[21], 31), 63), 127), 255)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001097
1098 return 0;
1099 }
1100 /* 10..110. */
1101 if ((cd[0] & 0xce & mask) == 0x8c) {
1102 /* Range 128 format */
1103 uint16_t w[29]; /* 1..28 */
1104 struct gsm48_range_128 *r = (struct gsm48_range_128 *)cd;
1105
1106 if (len < 3)
1107 return -EINVAL;
1108 memset(w, 0, sizeof(w));
1109 w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
1110 w[1] = r->w1;
1111 if (len >= 4)
1112 w[2] = r->w2;
1113 if (len >= 5)
1114 w[3] = (r->w3_hi << 4) | r->w3_lo;
1115 if (len >= 6)
1116 w[4] = (r->w4_hi << 1) | r->w4_lo;
1117 if (len >= 6)
1118 w[5] = r->w5;
1119 if (len >= 7)
1120 w[6] = (r->w6_hi << 3) | r->w6_lo;
1121 if (len >= 7)
1122 w[7] = r->w7;
1123 if (len >= 8)
1124 w[8] = r->w8;
1125 if (len >= 8)
1126 w[9] = r->w9;
1127 if (len >= 9)
1128 w[10] = r->w10;
1129 if (len >= 9)
1130 w[11] = r->w11;
1131 if (len >= 10)
1132 w[12] = r->w12;
1133 if (len >= 10)
1134 w[13] = r->w13;
1135 if (len >= 11)
1136 w[14] = r->w14;
1137 if (len >= 11)
1138 w[15] = r->w15;
1139 if (len >= 12)
1140 w[16] = r->w16;
1141 if (len >= 12)
1142 w[17] = r->w17;
1143 if (len >= 13)
1144 w[18] = (r->w18_hi << 1) | r->w18_lo;
1145 if (len >= 13)
1146 w[19] = r->w19;
1147 if (len >= 13)
1148 w[20] = r->w20;
1149 if (len >= 14)
1150 w[21] = (r->w21_hi << 2) | r->w21_lo;
1151 if (len >= 14)
1152 w[22] = r->w22;
1153 if (len >= 14)
1154 w[23] = r->w23;
1155 if (len >= 15)
1156 w[24] = r->w24;
1157 if (len >= 15)
1158 w[25] = r->w25;
1159 if (len >= 16)
1160 w[26] = (r->w26_hi << 1) | r->w26_lo;
1161 if (len >= 16)
1162 w[27] = r->w27;
1163 if (len >= 16)
1164 w[28] = r->w28;
1165 f[w[0]].mask |= frqt;
1166 if (w[1])
1167 f[(w[0] + w[1]) % 1024].mask |= frqt;
1168 if (w[2])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001169 f[(w[0] + smod(w[1] - 64 + w[2], 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001170 if (w[3])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001171 f[(w[0] + smod(w[1] + w[3], 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001172 if (w[4])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001173 f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + w[4], 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001174 if (w[5])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001175 f[(w[0] + smod(w[1] + smod(w[3] - 32 + w[5], 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001176 if (w[6])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001177 f[(w[0] + smod(w[1] - 64 + smod(w[2] + w[6], 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001178 if (w[7])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001179 f[(w[0] + smod(w[1] + smod(w[3] + w[7], 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001180 if (w[8])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001181 f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] - 16 + w[8] , 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001182 if (w[9])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001183 f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] - 16 + w[9] , 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001184 if (w[10])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001185 f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] - 16 + w[10], 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001186 if (w[11])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001187 f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 16 + w[11], 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001188 if (w[12])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001189 f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] + w[12], 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001190 if (w[13])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001191 f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] + w[13], 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001192 if (w[14])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001193 f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] + w[14], 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001194 if (w[15])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001195 f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] + w[15], 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001196 if (w[16])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001197 f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] - 16 + smod(w[8] - 8 + w[16], 15), 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001198 if (w[17])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001199 f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] - 16 + smod(w[9] - 8 + w[17], 15), 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001200 if (w[18])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001201 f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] - 16 + smod(w[10] - 8 + w[18], 15), 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001202 if (w[19])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001203 f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 16 + smod(w[11] - 8 + w[19], 15), 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001204 if (w[20])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001205 f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] + smod(w[12] - 8 + w[20], 15), 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001206 if (w[21])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001207 f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] + smod(w[13] - 8 + w[21], 15), 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001208 if (w[22])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001209 f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] + smod(w[14] - 8 + w[22], 15), 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001210 if (w[23])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001211 f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] + smod(w[15] - 8 + w[23], 15), 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001212 if (w[24])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001213 f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] - 16 + smod(w[8] + w[24], 15), 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001214 if (w[25])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001215 f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] - 16 + smod(w[9] + w[25], 15), 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001216 if (w[26])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001217 f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] - 16 + smod(w[10] + w[26], 15), 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001218 if (w[27])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001219 f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 16 + smod(w[11] + w[27], 15), 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001220 if (w[28])
Andreas.Eversbergeaac0cf2011-09-02 20:12:19 +02001221 f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] + smod(w[12] + w[28], 15), 31), 63), 127)) % 1024].mask |= frqt;
Harald Welte1523d702010-08-04 11:46:44 +02001222
1223 return 0;
1224 }
1225 /* 10..111. */
1226 if ((cd[0] & 0xce & mask) == 0x8e) {
1227 /* Variable bitmap format (can be any length >= 3) */
1228 uint16_t orig = 0;
1229 struct gsm48_var_bit *r = (struct gsm48_var_bit *)cd;
1230
1231 if (len < 3)
1232 return -EINVAL;
1233 orig = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
1234 f[orig].mask |= frqt;
1235 for (i = 1; 2 + (i >> 3) < len; i++)
1236 if ((cd[2 + (i >> 3)] & (0x80 >> (i & 7))))
1237 f[(orig + i) % 1024].mask |= frqt;
1238
1239 return 0;
1240 }
1241
1242 return 0;
1243}
Harald Welte96e2a002017-06-12 21:44:18 +02001244/*! @} */