diff options
author | Kirill Zakharenko <earwin@gmail.com> | 2020-05-01 18:21:58 +0300 |
---|---|---|
committer | Kirill Zakharenko <earwin@gmail.com> | 2020-05-01 18:21:58 +0300 |
commit | 357ab3abba5a1be0a66f8ae6c6a0460ddb4b7c06 (patch) | |
tree | ad0979169f0d7911bb503466036f36f1489bc714 /src | |
parent | 459224a99391fa9adf22a45cb6a63d56703aa3a8 (diff) | |
parent | d87d6f177837c848381e362d3880509ba275cac8 (diff) |
Merge master into fairwaves/production
Diffstat (limited to 'src')
33 files changed, 1258 insertions, 153 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 34cda0fb..16119d98 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -49,7 +49,7 @@ endif endif BUILT_SOURCES = crc8gen.c crc16gen.c crc32gen.c crc64gen.c -EXTRA_DIST = conv_acc_sse_impl.h +EXTRA_DIST = conv_acc_sse_impl.h crcXXgen.c.tpl libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined diff --git a/src/bitvec.c b/src/bitvec.c index 0c263ad6..d7f32fbd 100644 --- a/src/bitvec.c +++ b/src/bitvec.c @@ -45,6 +45,7 @@ #include <osmocom/core/bits.h> #include <osmocom/core/bitvec.h> #include <osmocom/core/panic.h> +#include <osmocom/core/utils.h> #define BITNUM_FROM_COMP(byte, bit) ((byte*8)+bit) @@ -291,7 +292,7 @@ int bitvec_fill(struct bitvec *bv, unsigned int num_bits, enum bit_value fill) return 0; } -/*! pad all remaining bits up to num_bits +/*! pad all remaining bits up to a given bit number * \return 0 on success; negative otherwise */ int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit) { @@ -399,7 +400,7 @@ int bitvec_set_bytes(struct bitvec *bv, const uint8_t *bytes, unsigned int count * \return pointer to allocated vector; NULL in case of error */ struct bitvec *bitvec_alloc(unsigned int size, TALLOC_CTX *ctx) { - struct bitvec *bv = talloc_zero(ctx, struct bitvec); + struct bitvec *bv = talloc(ctx, struct bitvec); if (!bv) return NULL; @@ -418,6 +419,8 @@ struct bitvec *bitvec_alloc(unsigned int size, TALLOC_CTX *ctx) * \param[in] bit vector to free */ void bitvec_free(struct bitvec *bv) { + if (bv == NULL) + return; talloc_free(bv->data); talloc_free(bv); } @@ -428,7 +431,7 @@ void bitvec_free(struct bitvec *bv) * \return number of bytes (= bits) copied */ unsigned int bitvec_pack(const struct bitvec *bv, uint8_t *buffer) { - unsigned int i = 0; + unsigned int i; for (i = 0; i < bv->data_len; i++) buffer[i] = bv->data[i]; @@ -441,7 +444,7 @@ unsigned int bitvec_pack(const struct bitvec *bv, uint8_t *buffer) * \return number of bytes (= bits) copied */ unsigned int bitvec_unpack(struct bitvec *bv, const uint8_t *buffer) { - unsigned int i = 0; + unsigned int i; for (i = 0; i < bv->data_len; i++) bv->data[i] = buffer[i]; @@ -455,17 +458,13 @@ unsigned int bitvec_unpack(struct bitvec *bv, const uint8_t *buffer) */ int bitvec_unhex(struct bitvec *bv, const char *src) { - unsigned i; - unsigned val; - unsigned write_index = 0; - unsigned digits = bv->data_len * 2; + int rc; - for (i = 0; i < digits; i++) { - if (sscanf(src + i, "%1x", &val) < 1) { - return 1; - } - bitvec_write_field(bv, &write_index, val, 4); - } + rc = osmo_hexparse(src, bv->data, bv->data_len); + if (rc < 0) /* turn -1 into 1 in case of error */ + return 1; + + bv->cur_bit = rc * 8; return 0; } @@ -497,7 +496,7 @@ uint64_t bitvec_read_field(struct bitvec *bv, unsigned int *read_index, unsigned * \param[in] bv The boolean vector to work on * \param[in,out] write_index Where writing supposed to start in the vector * \param[in] len How many bits to write - * \returns next write index or negative value on error + * \returns 0 on success, negative value on error */ int bitvec_write_field(struct bitvec *bv, unsigned int *write_index, uint64_t val, unsigned int len) { diff --git a/src/codec/gsm690.c b/src/codec/gsm690.c index 19557164..8ab1df12 100644 --- a/src/codec/gsm690.c +++ b/src/codec/gsm690.c @@ -216,8 +216,9 @@ const uint16_t gsm690_4_75_bitorder[95] = { 92, 31, 52, 65, 86, }; +/* See also RFC 4867 ยง3.6, Table 1, Column "Total speech bits" */ static const uint8_t amr_len_by_ft[16] = { - 12, 13, 15, 17, 19, 20, 26, 31, 7, 0, 0, 0, 0, 0, 0, 0 + 12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0 }; const struct value_string osmo_amr_type_names[] = { diff --git a/src/coding/Makefile.am b/src/coding/Makefile.am index f47fe457..b023668e 100644 --- a/src/coding/Makefile.am +++ b/src/coding/Makefile.am @@ -20,7 +20,8 @@ libosmocoding_la_SOURCES = \ gsm0503_mapping.c \ gsm0503_tables.c \ gsm0503_parity.c \ - gsm0503_coding.c + gsm0503_coding.c \ + gsm0503_amr_dtx.c libosmocoding_la_LDFLAGS = \ $(LTLDFLAGS_OSMOCODING) \ -version-info \ diff --git a/src/coding/gsm0503_amr_dtx.c b/src/coding/gsm0503_amr_dtx.c new file mode 100644 index 00000000..724cf091 --- /dev/null +++ b/src/coding/gsm0503_amr_dtx.c @@ -0,0 +1,314 @@ +/* + * (C) 2020 by sysmocom - s.f.m.c. GmbH, Author: Philipp Maier + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * 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 <stdio.h> +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#include <osmocom/core/bits.h> +#include <osmocom/core/conv.h> +#include <osmocom/core/utils.h> +#include <osmocom/coding/gsm0503_amr_dtx.h> +#include <osmocom/coding/gsm0503_parity.h> +#include <osmocom/gsm/gsm0503.h> + +/* See also: 3GPP TS 05.03, chapter 3.10.1.3, 3.10.5.2 Identification marker */ +static const ubit_t id_marker_1[] = { 1, 0, 1, 1, 0, 0, 0, 0, 1 }; + +/* See also: 3GPP TS 05.03, chapter 3.9.1.3, 3.10.2.2, 3.10.2.2 Identification marker */ +static const ubit_t id_marker_0[] = { 0, 1, 0, 0, 1, 1, 1, 1, 0 }; + +/* See also: 3GPP TS 05.03, chapter 3.9 Adaptive multi rate speech channel at full rate (TCH/AFS) */ +static const ubit_t codec_mode_1_sid[] = { 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0 }; +static const ubit_t codec_mode_2_sid[] = { 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0 }; +static const ubit_t codec_mode_3_sid[] = { 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1 }; +static const ubit_t codec_mode_4_sid[] = { 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1 }; + +const struct value_string gsm0503_amr_dtx_frame_names[] = { + { AFS_SID_FIRST, "AFS_SID_FIRST" }, + { AFS_SID_UPDATE, "AFS_SID_UPDATE" }, + { AFS_ONSET, "AFS_ONSET" }, + { AHS_SID_UPDATE, "AHS_SID_UPDATE" }, + { AHS_SID_FIRST_P1, "AHS_SID_FIRST_P1" }, + { AHS_SID_FIRST_P2, "AHS_SID_FIRST_P2" }, + { AHS_ONSET, "AHS_ONSET" }, + { AHS_SID_FIRST_INH, "AHS_SID_FIRST_INH" }, + { AHS_SID_UPDATE_INH, "AHS_SID_UPDATE_INH" }, + { AMR_OTHER, "NON DTX FRAME (OTHER)" }, + { 0, NULL } +}; + +static bool detect_afs_id_marker(int *n_errors, int *n_bits_total, const ubit_t * ubits, uint8_t offset, uint8_t count, + const ubit_t * id_marker, uint8_t id_marker_len) +{ + unsigned int i, k; + unsigned int id_bit_nr = 0; + int errors = 0; + int bits = 0; + + /* Override coded in-band data */ + ubits += offset; + + /* Check for identification marker bits */ + for (i = 0; i < count; i++) { + for (k = 0; k < 4; k++) { + if (id_marker[id_bit_nr % id_marker_len] != *ubits) + errors++; + id_bit_nr++; + ubits++; + bits++; + } + + /* Jump to the next block of 4 bits */ + ubits += 4; + } + + *n_errors = errors; + *n_bits_total = bits; + + /* Tolerate up to 1/8 errornous bits */ + return *n_errors < *n_bits_total / 8; +} + +static bool detect_ahs_id_marker(int *n_errors, int *n_bits_total, const ubit_t * ubits, const ubit_t * id_marker) +{ + unsigned int i, k; + int errors = 0; + int bits = 0; + + /* Override coded in-band data */ + ubits += 16; + + /* Check first identification marker bits (23*9 bits) */ + for (i = 0; i < 23; i++) { + for (k = 0; k < 9; k++) { + if (id_marker[k] != *ubits) + errors++; + ubits++; + bits++; + } + } + + /* Check remaining identification marker bits (5 bits) */ + for (k = 0; k < 5; k++) { + if (id_marker[k] != *ubits) + errors++; + ubits++; + bits++; + } + + *n_errors = errors; + *n_bits_total = bits; + + /* Tolerate up to 1/8 errornous bits */ + return *n_errors < *n_bits_total / 8; +} + +static bool detect_interleaved_ahs_id_marker(int *n_errors, int *n_bits_total, const ubit_t * ubits, uint8_t offset, + uint8_t n_bits, const ubit_t * id_marker, uint8_t id_marker_len) +{ + unsigned int i, k; + int errors = 0; + int bits = 0; + uint8_t full_rounds = n_bits / id_marker_len; + uint8_t remainder = n_bits % id_marker_len; + + /* Override coded in-band data */ + ubits += offset; + + /* Check first identification marker bits (23*9 bits) */ + for (i = 0; i < full_rounds; i++) { + for (k = 0; k < id_marker_len; k++) { + if (id_marker[k] != *ubits) + errors++; + ubits += 2; + bits++; + } + } + + /* Check remaining identification marker bits (5 bits) */ + for (k = 0; k < remainder; k++) { + if (id_marker[k] != *ubits) + errors++; + ubits += 2; + bits++; + } + + *n_errors = errors; + *n_bits_total = bits; + + /* Tolerate up to 1/8 errornous bits */ + return *n_errors < *n_bits_total / 8; +} + +/* Detect a an FR AMR SID_FIRST frame by its identifcation marker */ +static bool detect_afs_sid_first(int *n_errors, int *n_bits_total, const ubit_t * ubits) +{ + return detect_afs_id_marker(n_errors, n_bits_total, ubits, 32, 53, id_marker_0, 9); +} + +/* Detect an FR AMR SID_FIRST frame by its identification marker */ +static bool detect_afs_sid_update(int *n_errors, int *n_bits_total, const ubit_t * ubits) +{ + return detect_afs_id_marker(n_errors, n_bits_total, ubits, 36, 53, id_marker_0, 9); +} + +/* Detect an FR AMR SID_FIRST frame by its repeating coded inband data */ +static bool detect_afs_onset(int *n_errors, int *n_bits_total, const ubit_t * ubits) +{ + bool rc; + + rc = detect_afs_id_marker(n_errors, n_bits_total, ubits, 4, 57, codec_mode_1_sid, 16); + if (rc) + return true; + + rc = detect_afs_id_marker(n_errors, n_bits_total, ubits, 4, 57, codec_mode_2_sid, 16); + if (rc) + return true; + + rc = detect_afs_id_marker(n_errors, n_bits_total, ubits, 4, 57, codec_mode_3_sid, 16); + if (rc) + return true; + + rc = detect_afs_id_marker(n_errors, n_bits_total, ubits, 4, 57, codec_mode_4_sid, 16); + if (rc) + return true; + + return false; +} + +/* Detect an HR AMR SID UPDATE frame by its identification marker */ +static bool detect_ahs_sid_update(int *n_errors, int *n_bits_total, const ubit_t * ubits) +{ + return detect_ahs_id_marker(n_errors, n_bits_total, ubits, id_marker_1); +} + +/* Detect an HR AMR SID FIRST (part 1) frame by its identification marker */ +static bool detect_ahs_sid_first_p1(int *n_errors, int *n_bits_total, const ubit_t * ubits) +{ + return detect_ahs_id_marker(n_errors, n_bits_total, ubits, id_marker_0); +} + +/* Detect an HR AMR SID FIRST (part 2) frame by its repeating coded inband data */ +static bool detect_ahs_sid_first_p2(int *n_errors, int *n_bits_total, const ubit_t * ubits) +{ + bool rc; + + rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 0, 114, codec_mode_1_sid, 16); + if (rc) + return true; + + rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 0, 114, codec_mode_2_sid, 16); + if (rc) + return true; + + rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 0, 114, codec_mode_3_sid, 16); + if (rc) + return true; + + rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 0, 114, codec_mode_4_sid, 16); + if (rc) + return true; + + return false; +} + +/* Detect an HR AMR ONSET frame by its repeating coded inband data */ +static bool detect_ahs_onset(int *n_errors, int *n_bits_total, const ubit_t * ubits) +{ + bool rc; + + rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 1, 114, codec_mode_1_sid, 16); + if (rc) + return true; + + rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 1, 114, codec_mode_2_sid, 16); + if (rc) + return true; + + rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 1, 114, codec_mode_3_sid, 16); + if (rc) + return true; + + rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 1, 114, codec_mode_4_sid, 16); + if (rc) + return true; + + return false; +} + +/* Detect an HR AMR SID FIRST INHIBIT frame by its identification marker */ +static bool detect_ahs_sid_first_inh(int *n_errors, int *n_bits_total, const ubit_t * ubits) +{ + return detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 33, 212, id_marker_1, 9); +} + +/* Detect an HR AMR SID UPDATE INHIBIT frame by its identification marker */ +static bool detect_ahs_sid_update_inh(int *n_errors, int *n_bits_total, const ubit_t * ubits) +{ + return detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 33, 212, id_marker_0, 9); +} + +/*! Detect FR AMR DTX frame in unmapped, deinterleaved frame bits. + * \param[in] ubits input bits (456 bit). + * \param[out] n_errors number of errornous bits. + * \param[out] n_bits_total number of checked bits. + * \returns dtx frame type. */ +enum gsm0503_amr_dtx_frames gsm0503_detect_afs_dtx_frame(int *n_errors, int *n_bits_total, const ubit_t * ubits) +{ + if (detect_afs_sid_first(n_errors, n_bits_total, ubits)) + return AFS_SID_FIRST; + if (detect_afs_sid_update(n_errors, n_bits_total, ubits)) + return AFS_SID_UPDATE; + if (detect_afs_onset(n_errors, n_bits_total, ubits)) + return AFS_ONSET; + + *n_errors = 0; + *n_bits_total = 0; + return AMR_OTHER; +} + +/*! Detect HR AMR DTX frame in unmapped, deinterleaved frame bits. + * \param[in] ubits input bits (456 bit). + * \param[out] n_errors number of errornous bits. + * \param[out] n_bits_total number of checked bits. + * \returns dtx frame type, */ +enum gsm0503_amr_dtx_frames gsm0503_detect_ahs_dtx_frame(int *n_errors, int *n_bits_total, const ubit_t * ubits) +{ + if (detect_ahs_sid_update(n_errors, n_bits_total, ubits)) + return AHS_SID_UPDATE; + if (detect_ahs_sid_first_inh(n_errors, n_bits_total, ubits)) + return AHS_SID_FIRST_INH; + if (detect_ahs_sid_update_inh(n_errors, n_bits_total, ubits)) + return AHS_SID_UPDATE_INH; + if (detect_ahs_sid_first_p1(n_errors, n_bits_total, ubits)) + return AHS_SID_FIRST_P1; + if (detect_ahs_sid_first_p2(n_errors, n_bits_total, ubits)) + return AHS_SID_FIRST_P2; + if (detect_ahs_onset(n_errors, n_bits_total, ubits)) + return AHS_ONSET; + + *n_errors = 0; + *n_bits_total = 0; + return AMR_OTHER; +} diff --git a/src/coding/gsm0503_coding.c b/src/coding/gsm0503_coding.c index 7385d233..1bec56ea 100644 --- a/src/coding/gsm0503_coding.c +++ b/src/coding/gsm0503_coding.c @@ -47,6 +47,7 @@ #include <osmocom/coding/gsm0503_tables.h> #include <osmocom/coding/gsm0503_coding.h> #include <osmocom/coding/gsm0503_parity.h> +#include <osmocom/coding/gsm0503_amr_dtx.h> /*! \mainpage libosmocoding Documentation * @@ -1168,7 +1169,7 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p, } /* - * EGPRS PDTCH UL block encoding + * EGPRS PDTCH DL block encoding */ static int egprs_type3_map(ubit_t *bursts, const ubit_t *hc, const ubit_t *dc, int usf) { @@ -1176,7 +1177,7 @@ static int egprs_type3_map(ubit_t *bursts, const ubit_t *hc, const ubit_t *dc, i ubit_t iB[456]; const ubit_t *hl_hn = gsm0503_pdtch_hl_hn_ubit[3]; - gsm0503_mcs1_dl_interleave(gsm0503_usf2six[usf], hc, dc, iB); + gsm0503_mcs1_dl_interleave(gsm0503_usf2twelve_ubit[usf], hc, dc, iB); for (i = 0; i < 4; i++) { gsm0503_xcch_burst_map(&iB[i * 114], &bursts[i * 116], @@ -1332,7 +1333,7 @@ static int egprs_parse_dl_cps(struct egprs_cps *cps, * \param[out] bursts caller-allocated buffer for unpacked burst bits * \param[in] l2_data L2 (MAC) block to be encoded * \param[in] l2_len length of l2_data in bytes, used to determine MCS - * \returns 0 on success; negative on error */ + * \returns number of bits encoded; negative on error */ int gsm0503_pdtch_egprs_encode(ubit_t *bursts, const uint8_t *l2_data, uint8_t l2_len) { @@ -1427,7 +1428,7 @@ bad_header: * \param[out] bursts caller-allocated buffer for unpacked burst bits * \param[in] l2_data L2 (MAC) block to be encoded * \param[in] l2_len length of l2_data in bytes, used to determine CS - * \returns 0 on success; negative on error */ + * \returns number of bits encoded; negative on error */ int gsm0503_pdtch_encode(ubit_t *bursts, const uint8_t *l2_data, uint8_t l2_len) { ubit_t iB[456], cB[676]; @@ -1635,6 +1636,39 @@ static void tch_amr_disassemble(ubit_t *d_bits, const uint8_t *tch_data, int len d_bits[i] = (tch_data[j >> 3] >> (7 - (j & 7))) & 1; } +/* Append STI and MI bits to the SID_UPDATE frame, see also + * 3GPP TS 26.101, chapter 4.2.3 AMR Core Frame with comfort noise bits */ +static void tch_amr_sid_update_append(ubit_t *sid_update, uint8_t sti, uint8_t mi) +{ + /* Zero out the space that had been used by the CRC14 */ + memset(sid_update + 35, 0, 14); + + /* Append STI and MI parameters */ + sid_update[35] = sti & 1; + sid_update[36] = mi & 1; + sid_update[37] = mi >> 1 & 1; + sid_update[38] = mi >> 2 & 1; +} + +/* Extract a SID UPDATE fram the sbits of an FR AMR frame */ +static void extract_afs_sid_update(sbit_t *sid_update, const sbit_t *sbits) +{ + + unsigned int i; + + sbits += 32; + + for (i = 0; i < 53; i++) { + sid_update[0] = sbits[0]; + sid_update[1] = sbits[1]; + sid_update[2] = sbits[2]; + sid_update[3] = sbits[3]; + sid_update += 4; + sbits += 8; + } + +} + /* re-arrange according to TS 05.03 Table 2 (receiver) */ static void tch_fr_d_to_b(ubit_t *b_bits, const ubit_t *d_bits) { @@ -2101,10 +2135,37 @@ int gsm0503_tch_afs_decode(uint8_t *tch_data, const sbit_t *bursts, int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft, uint8_t *cmr, int *n_errors, int *n_bits_total) { + return gsm0503_tch_afs_decode_dtx(tch_data, bursts, codec_mode_req, + codec, codecs, ft, cmr, n_errors, + n_bits_total, NULL); +} + +/*! Perform channel decoding of a TCH/AFS channel according TS 05.03 + * \param[out] tch_data Codec frame in RTP payload format + * \param[in] bursts buffer containing the symbols of 8 bursts + * \param[in] codec_mode_req is this CMR (1) or CMC (0) + * \param[in] codec array of active codecs (active codec set) + * \param[in] codecs number of codecs in \a codec + * \param ft Frame Type; Input if \a codec_mode_req = 1, Output * otherwise + * \param[out] cmr Output in \a codec_mode_req = 1 + * \param[out] n_errors Number of detected bit errors + * \param[out] n_bits_total Total number of bits + * \param[inout] dtx DTX frame type output, previous DTX frame type input + * \returns (>=4) length of bytes used in \a tch_data output buffer; ([0,3]) + * codec out of range; negative on error + */ +int gsm0503_tch_afs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts, + int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft, + uint8_t *cmr, int *n_errors, int *n_bits_total, uint8_t *dtx) +{ sbit_t iB[912], cB[456], h; ubit_t d[244], p[6], conv[250]; int i, j, k, best = 0, rv, len, steal = 0, id = 0; + ubit_t cBd[456]; *n_errors = 0; *n_bits_total = 0; + static ubit_t sid_first_dummy[64] = { 0 }; + sbit_t sid_update_enc[256]; + uint8_t dtx_prev; for (i=0; i<8; i++) { gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], &h, i >> 2); @@ -2123,6 +2184,50 @@ int gsm0503_tch_afs_decode(uint8_t *tch_data, const sbit_t *bursts, return GSM_MACBLOCK_LEN; } + /* Determine the DTX frame type (SID_UPDATE, ONSET etc...) */ + if (dtx) { + osmo_sbit2ubit(cBd, cB, 456); + dtx_prev = *dtx; + *dtx = gsm0503_detect_afs_dtx_frame(n_errors, n_bits_total, cBd); + + if (dtx_prev == AFS_SID_UPDATE && *dtx == AMR_OTHER) { + /* NOTE: The AFS_SID_UPDATE frame is splitted into + * two half rate frames. If the id marker frame + * (AFS_SID_UPDATE) is detected the following frame + * contains the actual comfort noised data part of + * (AFS_SID_UPDATE_CN). */ + *dtx = AFS_SID_UPDATE_CN; + + extract_afs_sid_update(sid_update_enc, cB); + osmo_conv_decode_ber(&gsm0503_tch_axs_sid_update, + sid_update_enc, conv, n_errors, + n_bits_total); + rv = osmo_crc16gen_check_bits(&gsm0503_amr_crc14, conv, + 35, conv + 35); + if (rv != 0) { + /* Error checking CRC14 for an AMR SID_UPDATE frame */ + return -1; + } + + tch_amr_sid_update_append(conv, 1, + (codec_mode_req) ? codec[*ft] + : codec[id]); + tch_amr_reassemble(tch_data, conv, 39); + len = 5; + goto out; + } else if (*dtx == AFS_SID_FIRST) { + tch_amr_sid_update_append(sid_first_dummy, 0, + (codec_mode_req) ? codec[*ft] + : codec[id]); + tch_amr_reassemble(tch_data, conv, 39); + len = 5; + goto out; + } else if (*dtx == AFS_ONSET) { + len = 0; + goto out; + } + } + for (i = 0; i < 4; i++) { for (j = 0, k = 0; j < 8; j++) k += abs(((int)gsm0503_afs_ic_sbit[i][j]) - ((int)cB[j])); @@ -2283,6 +2388,7 @@ int gsm0503_tch_afs_decode(uint8_t *tch_data, const sbit_t *bursts, return -1; } +out: /* Change codec request / indication, if frame is valid */ if (codec_mode_req) *cmr = id; @@ -2480,9 +2586,36 @@ int gsm0503_tch_ahs_decode(uint8_t *tch_data, const sbit_t *bursts, int odd, int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft, uint8_t *cmr, int *n_errors, int *n_bits_total) { + return gsm0503_tch_ahs_decode_dtx(tch_data, bursts, odd, codec_mode_req, + codec, codecs, ft, cmr, n_errors, + n_bits_total, NULL); +} + +/*! Perform channel decoding of a TCH/AFS channel according TS 05.03 + * \param[out] tch_data Codec frame in RTP payload format + * \param[in] bursts buffer containing the symbols of 8 bursts + * \param[in] odd Is this an odd (1) or even (0) frame number? + * \param[in] codec_mode_req is this CMR (1) or CMC (0) + * \param[in] codec array of active codecs (active codec set) + * \param[in] codecs number of codecs in \a codec + * \param ft Frame Type; Input if \a codec_mode_req = 1, Output * otherwise + * \param[out] cmr Output in \a codec_mode_req = 1 + * \param[out] n_errors Number of detected bit errors + * \param[out] n_bits_total Total number of bits + * \param[inout] dtx DTX frame type output, previous DTX frame type input + * \returns (>=4) length of bytes used in \a tch_data output buffer; ([0,3]) + * codec out of range; negative on error + */ +int gsm0503_tch_ahs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts, int odd, + int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft, + uint8_t *cmr, int *n_errors, int *n_bits_total, uint8_t *dtx) +{ sbit_t iB[912], cB[456], h; ubit_t d[244], p[6], conv[135]; int i, j, k, best = 0, rv, len, steal = 0, id = 0; + ubit_t cBd[456]; + static ubit_t sid_first_dummy[64] = { 0 }; + uint8_t dtx_prev; /* only unmap the stealing bits */ if (!odd) { @@ -2526,6 +2659,52 @@ int gsm0503_tch_ahs_decode(uint8_t *tch_data, const sbit_t *bursts, int odd, gsm0503_tch_hr_deinterleave(cB, iB); + /* Determine the DTX frame type (SID_UPDATE, ONSET etc...) */ + if (dtx) { + osmo_sbit2ubit(cBd, cB, 456); + dtx_prev = *dtx; + *dtx = gsm0503_detect_ahs_dtx_frame(n_errors, n_bits_total, cBd); + + if (dtx_prev == AHS_SID_UPDATE && *dtx == AMR_OTHER) { + /* NOTE: The AHS_SID_UPDATE frame is splitted into + * two half rate frames. If the id marker frame + * (AHS_SID_UPDATE) is detected the following frame + * contains the actual comfort noised data part of + * (AHS_SID_UPDATE_CN). */ + *dtx = AHS_SID_UPDATE_CN; + + osmo_conv_decode_ber(&gsm0503_tch_axs_sid_update, + cB + 16, conv, n_errors, + n_bits_total); + rv = osmo_crc16gen_check_bits(&gsm0503_amr_crc14, conv, + 35, conv + 35); + if (rv != 0) { + /* Error checking CRC14 for an AMR SID_UPDATE frame */ + return -1; + } + + tch_amr_sid_update_append(conv, 1, + (codec_mode_req) ? codec[*ft] + : codec[id]); + tch_amr_reassemble(tch_data, conv, 39); + len = 5; + goto out; + } else if (*dtx == AHS_SID_FIRST_P2) { + tch_amr_sid_update_append(sid_first_dummy, 0, + (codec_mode_req) ? codec[*ft] + : codec[id]); + tch_amr_reassemble(tch_data, sid_first_dummy, 39); + len = 5; + goto out; + } else if (*dtx == AHS_SID_UPDATE || *dtx == AHS_ONSET + || *dtx == AHS_SID_FIRST_INH + || *dtx == AHS_SID_UPDATE_INH + || *dtx == AHS_SID_FIRST_P1) { + len = 0; + goto out; + } + } + for (i = 0; i < 4; i++) { for (j = 0, k = 0; j < 4; j++) k += abs(((int)gsm0503_ahs_ic_sbit[i][j]) - ((int)cB[j])); @@ -2670,6 +2849,7 @@ int gsm0503_tch_ahs_decode(uint8_t *tch_data, const sbit_t *bursts, int odd, return -1; } +out: /* Change codec request / indication, if frame is valid */ if (codec_mode_req) *cmr = id; @@ -2879,7 +3059,7 @@ static inline int16_t rach_decode_ber(const sbit_t *burst, uint8_t bsic, bool is osmo_ubit2pbit_ext(ra, 0, conv, 0, nbits, 1); - return is_11bit ? osmo_load16le(ra) : ra[0]; + return is_11bit ? ((ra[0] << 3) | (ra[1] & 0x07)) : ra[0]; } /*! Decode the Extended (11-bit) RACH according to 3GPP TS 45.003 @@ -2974,7 +3154,8 @@ int gsm0503_rach_ext_encode(ubit_t *burst, uint16_t ra11, uint8_t bsic, bool is_ uint8_t ra[2] = { 0 }, nbits = 8; if (is_11bit) { - osmo_store16le(ra11, ra); + ra[0] = (uint8_t) (ra11 >> 3); + ra[1] = (uint8_t) (ra11 & 0x07); nbits = 11; } else ra[0] = (uint8_t)ra11; diff --git a/src/coding/gsm0503_parity.c b/src/coding/gsm0503_parity.c index 874114ff..a8daacc7 100644 --- a/src/coding/gsm0503_parity.c +++ b/src/coding/gsm0503_parity.c @@ -134,4 +134,15 @@ const struct osmo_crc8gen_code gsm0503_amr_crc6 = { .remainder = 0x3f, }; +/*! GSM AMR parity (SID_UPDATE) + * + * g(x) = x^14 + x^13 + x^5 + x^3 + x^2 + 1 + */ +const struct osmo_crc16gen_code gsm0503_amr_crc14 = { + .bits = 14, + .poly = 0x202d, + .init = 0x0000, + .remainder = 0x3fff, +}; + /*! @} */ diff --git a/src/coding/gsm0503_tables.c b/src/coding/gsm0503_tables.c index 5fe634bf..df0abeed 100644 --- a/src/coding/gsm0503_tables.c +++ b/src/coding/gsm0503_tables.c @@ -63,6 +63,9 @@ const sbit_t gsm0503_pdtch_edge_hl_hn_sbit[3][8] = { { -127,-127, -127, 127, 127,-127, -127,-127 }, }; +/* + * 3GPP TS 05.03 sec 5.1.2.2 "Block code". Rows re-ordered to be indxed by USF in host bit order. + */ const ubit_t gsm0503_usf2six[8][6] = { { 0,0,0, 0,0,0 }, { 1,0,0, 1,0,1 }, @@ -74,6 +77,9 @@ const ubit_t gsm0503_usf2six[8][6] = { { 1,1,1, 0,0,0 }, }; +/* + * 3GPP TS 05.03 sec 5.1.4.2 "Block code". Rows re-ordered to be indxed by USF in host bit order. + */ const ubit_t gsm0503_usf2twelve_ubit[8][12] = { { 0,0,0, 0,0,0, 0,0,0, 0,0,0 }, { 1,1,0, 1,0,0, 0,0,1, 0,1,1 }, diff --git a/src/coding/libosmocoding.map b/src/coding/libosmocoding.map index 87b38864..325b6d80 100644 --- a/src/coding/libosmocoding.map +++ b/src/coding/libosmocoding.map @@ -56,6 +56,7 @@ gsm0503_sch_crc10; gsm0503_tch_fr_crc3; gsm0503_tch_efr_crc8; gsm0503_amr_crc6; +gsm0503_amr_crc14; gsm0503_xcch_burst_unmap; gsm0503_xcch_burst_map; @@ -106,8 +107,10 @@ gsm0503_tch_hr_encode; gsm0503_tch_hr_decode; gsm0503_tch_afs_encode; gsm0503_tch_afs_decode; +gsm0503_tch_afs_decode_dtx; gsm0503_tch_ahs_encode; gsm0503_tch_ahs_decode; +gsm0503_tch_ahs_decode_dtx; gsm0503_rach_ext_encode; gsm0503_rach_ext_decode; gsm0503_rach_ext_decode_ber; @@ -116,6 +119,10 @@ gsm0503_rach_decode; gsm0503_rach_decode_ber; gsm0503_sch_encode; gsm0503_sch_decode; +gsm0503_amr_dtx_frame_names; +gsm0503_amr_dtx_frame_name; +gsm0503_detect_afs_dtx_frame; +gsm0503_detect_ahs_dtx_frame; local: *; }; @@ -36,6 +36,7 @@ #include <stdlib.h> #include <string.h> +#include <osmocom/core/utils.h> #include <osmocom/core/bits.h> #include <osmocom/core/conv.h> @@ -87,6 +88,7 @@ osmo_conv_encode_init(struct osmo_conv_encoder *encoder, const struct osmo_conv_code *code) { memset(encoder, 0x00, sizeof(struct osmo_conv_encoder)); + OSMO_ASSERT(code != NULL); encoder->code = code; } @@ -23,6 +23,7 @@ #include "config.h" #ifndef EMBEDDED +#define _GNU_SOURCE #include <unistd.h> #include <errno.h> @@ -31,6 +32,7 @@ #include <stdio.h> #include <dirent.h> #include <sys/types.h> +#include <pwd.h> #include <osmocom/core/logging.h> #include <osmocom/core/utils.h> @@ -192,23 +194,34 @@ int osmo_close_all_fds_above(int last_fd_to_keep) /* Seems like POSIX has no header file for this, and even glibc + __USE_GNU doesn't help */ extern char **environ; -/*! call an external shell command without waiting for it. +/*! call an external shell command as 'user' without waiting for it. * * This mimics the behavior of system(3), with the following differences: * - it doesn't wait for completion of the child process * - it closes all non-stdio file descriptors by iterating /proc/self/fd * - it constructs a reduced environment where only whitelisted keys survive * - it (optionally) appends additional variables to the environment + * - it (optionally) changes the user ID to that of 'user' (requires execution as root) * * \param[in] command the shell command to be executed, see system(3) * \param[in] env_whitelist A white-list of keys for environment variables * \param[in] addl_env any additional environment variables to be appended + * \param[in] user name of the user to which we should switch before executing the command * \returns PID of generated child process; negative on error */ -int osmo_system_nowait(const char *command, const char **env_whitelist, char **addl_env) +int osmo_system_nowait2(const char *command, const char **env_whitelist, char **addl_env, const char *user) { + struct passwd _pw, *pw; + int getpw_buflen = sysconf(_SC_GETPW_R_SIZE_MAX); int rc; + if (user) { + char buf[getpw_buflen]; + getpwnam_r(user, &_pw, buf, sizeof(buf), &pw); + if (!pw) + return -EINVAL; + } + rc = fork(); if (rc == 0) { /* we are in the child */ @@ -217,11 +230,34 @@ int osmo_system_nowait(const char *command, const char **env_whitelist, char **a /* close all file descriptors above stdio */ osmo_close_all_fds_above(2); + /* man execle: "an array of pointers *must* be terminated by a null pointer" */ + new_env[0] = NULL; + /* build the new environment */ - if (env_whitelist) - osmo_environment_filter(new_env, ARRAY_SIZE(new_env), environ, env_whitelist); - if (addl_env) - osmo_environment_append(new_env, ARRAY_SIZE(new_env), addl_env); + if (env_whitelist) { + rc = osmo_environment_filter(new_env, ARRAY_SIZE(new_env), environ, env_whitelist); + if (rc < 0) + return rc; + } + if (addl_env) { + rc = osmo_environment_append(new_env, ARRAY_SIZE(new_env), addl_env); + if (rc < 0) + return rc; + } + + /* drop privileges */ + if (pw) { + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0) { + perror("setresgid() during privilege drop"); + exit(1); + } + + if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0) { + perror("setresuid() during privilege drop"); + exit(1); + } + + } /* if we want to behave like system(3), we must go via the shell */ execle("/bin/sh", "sh", "-c", command, (char *) NULL, new_env); @@ -235,4 +271,23 @@ int osmo_system_nowait(const char *command, const char **env_whitelist, char **a } } +/*! call an external shell command without waiting for it. + * + * This mimics the behavior of system(3), with the following differences: + * - it doesn't wait for completion of the child process + * - it closes all non-stdio file descriptors by iterating /proc/self/fd + * - it constructs a reduced environment where only whitelisted keys survive + * - it (optionally) appends additional variables to the environment + * + * \param[in] command the shell command to be executed, see system(3) + * \param[in] env_whitelist A white-list of keys for environment variables + * \param[in] addl_env any additional environment variables to be appended + * \returns PID of generated child process; negative on error + */ +int osmo_system_nowait(const char *command, const char **env_whitelist, char **addl_env) +{ + return osmo_system_nowait2(command, env_whitelist, addl_env, NULL); +} + + #endif /* EMBEDDED */ diff --git a/src/gb/gprs_bssgp_util.c b/src/gb/gprs_bssgp_util.c index 669dfb86..77089491 100644 --- a/src/gb/gprs_bssgp_util.c +++ b/src/gb/gprs_bssgp_util.c @@ -43,7 +43,7 @@ struct gprs_ns_inst *bssgp_nsi; static const struct value_string bssgp_cause_strings[] = { { BSSGP_CAUSE_PROC_OVERLOAD, "Processor overload" }, { BSSGP_CAUSE_EQUIP_FAIL, "Equipment Failure" }, - { BSSGP_CAUSE_TRASIT_NET_FAIL, "Transit netowkr service failure" }, + { BSSGP_CAUSE_TRASIT_NET_FAIL, "Transit network service failure" }, { BSSGP_CAUSE_CAPA_GREATER_0KPBS, "Transmission capacity modified" }, { BSSGP_CAUSE_UNKNOWN_MS, "Unknown MS" }, { BSSGP_CAUSE_UNKNOWN_BVCI, "Unknown BVCI" }, diff --git a/src/gb/gprs_ns.c b/src/gb/gprs_ns.c index 1391f506..9ac3b9e2 100644 --- a/src/gb/gprs_ns.c +++ b/src/gb/gprs_ns.c @@ -51,7 +51,7 @@ * * There can be multiple BSSGP virtual connections over one (group of) NSVC's. BSSGP will * therefore identify the BSSGP virtual connection by a BVCI passed down to NS. - * NS then has to firgure out which NSVC's are responsible for this BVCI. + * NS then has to figure out which NSVC's are responsible for this BVCI. * Those mappings are administratively configured. * * This implementation has the following limitations: @@ -320,7 +320,8 @@ struct gprs_nsvc *gprs_nsvc_create2(struct gprs_ns_inst *nsi, uint16_t nsvci, return NULL; } - LOGP(DNS, LOGL_INFO, "NSVCI=%u Creating NS-VC\n", nsvci); + LOGP(DNS, LOGL_INFO, "NSVCI=%u Creating NS-VC with Signal weight %u, Data weight %u\n", + nsvci, sig_weight, data_weight); nsvc = talloc_zero(nsi, struct gprs_nsvc); if (!nsvc) @@ -1080,7 +1081,7 @@ int gprs_ns_tx_sns_size_ack(struct gprs_nsvc *nsvc, uint8_t *cause) * \param[in] msg struct msgb to be trasnmitted * * This function obtains the NS-VC by the msgb_nsei(msg) and then checks - * if the NS-VC is ALIVEV and not BLOCKED. After that, it adds a NS + * if the NS-VC is ALIVE and not BLOCKED. After that, it adds a NS * header for the NS-UNITDATA message type and sends it off. * * Section 9.2.10: transmit side / NS-UNITDATA-REQUEST primitive diff --git a/src/gsm/gsm48049.c b/src/gsm/gsm48049.c index 5e743563..3ab907c9 100644 --- a/src/gsm/gsm48049.c +++ b/src/gsm/gsm48049.c @@ -95,7 +95,7 @@ const struct tlv_definition cbsp_att_tlvdef = { [CBSP_IEI_RR_LOADING_LIST] = { TLV_TYPE_TL16V }, [CBSP_IEI_CAUSE] = { TLV_TYPE_TV }, [CBSP_IEI_DCS] = { TLV_TYPE_TV }, - [CBSP_IEI_RECOVERY_IND] { TLV_TYPE_TV }, + [CBSP_IEI_RECOVERY_IND] = { TLV_TYPE_TV }, [CBSP_IEI_MSG_ID] = { TLV_TYPE_FIXED, 2 }, [CBSP_IEI_EMERG_IND] = { TLV_TYPE_TV }, [CBSP_IEI_WARN_TYPE] = { TLV_TYPE_FIXED, 2 }, diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map index efca0a5f..a518b289 100644 --- a/src/gsm/libosmogsm.map +++ b/src/gsm/libosmogsm.map @@ -135,6 +135,7 @@ gsm0503_tch_ahs_6_7; gsm0503_tch_ahs_5_9; gsm0503_tch_ahs_5_15; gsm0503_tch_ahs_4_75; +gsm0503_tch_axs_sid_update; gsm0503_mcs1_dl_hdr; gsm0503_mcs1_ul_hdr; gsm0503_mcs1; diff --git a/src/gsmtap_util.c b/src/gsmtap_util.c index 2fb18a48..9a0ac027 100644 --- a/src/gsmtap_util.c +++ b/src/gsmtap_util.c @@ -54,18 +54,25 @@ /*! convert RSL channel number to GSMTAP channel type * \param[in] rsl_chantype RSL channel type * \param[in] link_id RSL link identifier + * \param[in] user_plane Is this voice/csd user plane (1) or signaling (0) * \returns GSMTAP channel type */ -uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id) +uint8_t chantype_rsl2gsmtap2(uint8_t rsl_chantype, uint8_t link_id, bool user_plane) { uint8_t ret = GSMTAP_CHANNEL_UNKNOWN; switch (rsl_chantype) { case RSL_CHAN_Bm_ACCHs: - ret = GSMTAP_CHANNEL_TCH_F; + if (user_plane) + ret = GSMTAP_CHANNEL_VOICE_F; + else + ret = GSMTAP_CHANNEL_FACCH_F; break; case RSL_CHAN_Lm_ACCHs: - ret = GSMTAP_CHANNEL_TCH_H; + if (user_plane) + ret = GSMTAP_CHANNEL_VOICE_H; + else + ret = GSMTAP_CHANNEL_FACCH_H; break; case RSL_CHAN_SDCCH4_ACCH: ret = GSMTAP_CHANNEL_SDCCH4; @@ -86,6 +93,12 @@ uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id) case RSL_CHAN_OSMO_PDCH: ret = GSMTAP_CHANNEL_PDCH; break; + case RSL_CHAN_OSMO_CBCH4: + ret = GSMTAP_CHANNEL_CBCH51; + break; + case RSL_CHAN_OSMO_CBCH8: + ret = GSMTAP_CHANNEL_CBCH52; + break; } if (link_id & 0x40) @@ -94,6 +107,16 @@ uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id) return ret; } +/*! convert RSL channel number to GSMTAP channel type + * \param[in] rsl_chantype RSL channel type + * \param[in] link_id RSL link identifier + * \returns GSMTAP channel type + */ +uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id) +{ + return chantype_rsl2gsmtap2(rsl_chantype, link_id, false); +} + /*! convert GSMTAP channel type to RSL channel number + Link ID * \param[in] gsmtap_chantype GSMTAP channel type * \param[out] rsl_chantype RSL channel mumber @@ -103,10 +126,12 @@ void chantype_gsmtap2rsl(uint8_t gsmtap_chantype, uint8_t *rsl_chantype, uint8_t *link_id) { switch (gsmtap_chantype & ~GSMTAP_CHANNEL_ACCH & 0xff) { - case GSMTAP_CHANNEL_TCH_F: // TCH/F, FACCH/F + case GSMTAP_CHANNEL_FACCH_F: + case GSMTAP_CHANNEL_VOICE_F: // TCH/F *rsl_chantype = RSL_CHAN_Bm_ACCHs; break; - case GSMTAP_CHANNEL_TCH_H: // TCH/H, FACCH/H + case GSMTAP_CHANNEL_FACCH_H: + case GSMTAP_CHANNEL_VOICE_H: // TCH/H *rsl_chantype = RSL_CHAN_Lm_ACCHs; break; case GSMTAP_CHANNEL_SDCCH4: // SDCCH/4 @@ -461,8 +486,8 @@ const struct value_string gsmtap_gsm_channel_names[] = { { GSMTAP_CHANNEL_SDCCH, "SDCCH" }, { GSMTAP_CHANNEL_SDCCH4, "SDCCH/4" }, { GSMTAP_CHANNEL_SDCCH8, "SDCCH/8" }, - { GSMTAP_CHANNEL_TCH_F, "TCH/F/FACCH/F" }, - { GSMTAP_CHANNEL_TCH_H, "TCH/H/FACCH/H" }, + { GSMTAP_CHANNEL_FACCH_F, "FACCH/F" }, + { GSMTAP_CHANNEL_FACCH_H, "FACCH/H" }, { GSMTAP_CHANNEL_PACCH, "PACCH" }, { GSMTAP_CHANNEL_CBCH52, "CBCH" }, { GSMTAP_CHANNEL_PDCH, "PDCH" } , @@ -471,8 +496,10 @@ const struct value_string gsmtap_gsm_channel_names[] = { { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_SDCCH, "LSACCH" }, { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_SDCCH4, "SACCH/4" }, { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_SDCCH8, "SACCH/8" }, - { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_TCH_F, "SACCH/F" }, - { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_TCH_H, "SACCH/H" }, + { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_FACCH_F, "SACCH/F" }, + { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_FACCH_H, "SACCH/H" }, + { GSMTAP_CHANNEL_VOICE_F, "TCH/F" }, + { GSMTAP_CHANNEL_VOICE_H, "TCH/H" }, { 0, NULL } }; diff --git a/src/select.c b/src/select.c index b997122e..8e312054 100644 --- a/src/select.c +++ b/src/select.c @@ -382,6 +382,7 @@ int osmo_timerfd_setup(struct osmo_fd *ofd, int (*cb)(struct osmo_fd *, unsigned rc = osmo_fd_register(ofd); if (rc < 0) { + osmo_fd_unregister(ofd); close(ofd->fd); ofd->fd = -1; return rc; @@ -392,6 +393,65 @@ int osmo_timerfd_setup(struct osmo_fd *ofd, int (*cb)(struct osmo_fd *, unsigned #endif /* HAVE_SYS_TIMERFD_H */ +#ifdef HAVE_SYS_SIGNALFD_H +#include <sys/signalfd.h> + +static int signalfd_callback(struct osmo_fd *ofd, unsigned int what) +{ + struct osmo_signalfd *osfd = ofd->data; + struct signalfd_siginfo fdsi; + int rc; + + rc = read(ofd->fd, &fdsi, sizeof(fdsi)); + if (rc < 0) { + osmo_fd_unregister(ofd); + close(ofd->fd); + ofd->fd = -1; + return rc; + } + + osfd->cb(osfd, &fdsi); + + return 0; +}; + +/*! create a signalfd and register it with osmocom select loop. + * \param[in] ctx talloc context from which osmo_signalfd is to be allocated + * \param[in] set of signals to be accept via this file descriptor + * \param[in] cb call-back function to be called for each arriving signal + * \param[in] data opaque user-provided data to pass to callback + * \returns pointer to newly-allocated + registered osmo_signalfd; NULL on error */ +struct osmo_signalfd * +osmo_signalfd_setup(void *ctx, sigset_t set, osmo_signalfd_cb *cb, void *data) +{ + struct osmo_signalfd *osfd = talloc_size(ctx, sizeof(*osfd)); + int fd, rc; + + if (!osfd) + return NULL; + + osfd->data = data; + osfd->sigset = set; + osfd->cb = cb; + + fd = signalfd(-1, &osfd->sigset, SFD_NONBLOCK); + if (fd < 0) { + talloc_free(osfd); + return NULL; + } + + osmo_fd_setup(&osfd->ofd, fd, OSMO_FD_READ, signalfd_callback, osfd, 0); + rc = osmo_fd_register(&osfd->ofd); + if (rc < 0) { + close(fd); + talloc_free(osfd); + return NULL; + } + + return osfd; +} + +#endif /* HAVE_SYS_SIGNALFD_H */ /*! @} */ diff --git a/src/sim/Makefile.am b/src/sim/Makefile.am index c608757e..0539dd98 100644 --- a/src/sim/Makefile.am +++ b/src/sim/Makefile.am @@ -4,24 +4,26 @@ LIBVERSION=1:2:1 AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include -AM_CFLAGS = -fPIC -Wall $(PCSC_CFLAGS) $(TALLOC_CFLAGS) +AM_CFLAGS = -fPIC -Wall $(TALLOC_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) -if ENABLE_PCSC -# FIXME: only build the PC/SC dependent part conditional, but always build other parts - noinst_HEADERS = sim_int.h gsm_int.h +if !EMBEDDED lib_LTLIBRARIES = libosmosim.la -libosmosim_la_SOURCES = core.c reader.c reader_pcsc.c class_tables.c \ +libosmosim_la_SOURCES = core.c reader.c class_tables.c \ card_fs_sim.c card_fs_usim.c card_fs_uicc.c \ - card_fs_isim.c card_fs_tetra.c + card_fs_isim.c card_fs_hpsim.c card_fs_tetra.c libosmosim_la_LDFLAGS = -version-info $(LIBVERSION) libosmosim_la_LIBADD = \ $(top_builddir)/src/libosmocore.la \ $(top_builddir)/src/gsm/libosmogsm.la \ - $(TALLOC_LIBS) \ - $(PCSC_LIBS) - + $(TALLOC_LIBS) +if ENABLE_PCSC +AM_CFLAGS += $(PCSC_CFLAGS) +libosmosim_la_SOURCES += reader_pcsc.c +libosmosim_la_LIBADD += $(PCSC_LIBS) endif + +endif # !EMBEDDED diff --git a/src/sim/card_fs_hpsim.c b/src/sim/card_fs_hpsim.c new file mode 100644 index 00000000..4a5f7d9a --- /dev/null +++ b/src/sim/card_fs_hpsim.c @@ -0,0 +1,76 @@ +/*! \file card_fs_hpsim.c + * 3GPP HPSIM specific structures / routines. */ +/* + * (C) 2020 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include <errno.h> +#include <string.h> + +#include <osmocom/sim/sim.h> +#include <osmocom/core/talloc.h> +#include <osmocom/gsm/gsm48.h> + +#include "sim_int.h" +#include "gsm_int.h" + +/* TS 31.104 Version 15.0.0 Release 15 / Chapter 7.1.3 */ +const struct osim_card_sw ts31_104_sw[] = { + { + 0x9862, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, + .u.str = "Security management - Authentication error, incorrect MAC", + }, + OSIM_CARD_SW_LAST +}; + +/* TS 31.104 Version 15.0.0 Release 15 / Chapter 4.2 */ +static const struct osim_file_desc hpsim_ef_in_adf_hpsim[] = { + EF_LIN_FIX_N(0x6F06, 0x06, "EF.ARR", 0, 1, 256, + "Access Rule TLV data objects"), + EF_TRANSP_N(0x6F07, 0x07, "EF.IMST", 0, 9, 9, + "IMSI"), + EF_TRANSP_N(0x6FAD, 0x03, "EF_AD", 0, 4, 8, + "Administrative Data"), +}; + +/* Annex E - TS 101 220 */ +static const uint8_t adf_hpsim_aid[] = { 0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x0A }; + +struct osim_card_app_profile *osim_aprof_hpsim(void *ctx) +{ + struct osim_card_app_profile *aprof; + struct osim_file_desc *iadf; + + aprof = talloc_zero(ctx, struct osim_card_app_profile); + aprof->name = "3GPP HPSIM"; + aprof->sw = ts31_104_sw; + aprof->aid_len = sizeof(adf_hpsim_aid); + memcpy(aprof->aid, adf_hpsim_aid, aprof->aid_len); + + /* ADF.HPSIM with its EF siblings */ + iadf = alloc_adf_with_ef(aprof, adf_hpsim_aid, sizeof(adf_hpsim_aid), "ADF.HPSIM", + hpsim_ef_in_adf_hpsim, ARRAY_SIZE(hpsim_ef_in_adf_hpsim)); + aprof->adf = iadf; + + return aprof; +} diff --git a/src/sim/card_fs_isim.c b/src/sim/card_fs_isim.c index e6ba0d09..f11c0294 100644 --- a/src/sim/card_fs_isim.c +++ b/src/sim/card_fs_isim.c @@ -1,7 +1,7 @@ /*! \file card_fs_isim.c * 3GPP ISIM specific structures / routines. */ /* - * (C) 2014 by Harald Welte <laforge@gnumonks.org> + * (C) 2014-2020 by Harald Welte <laforge@gnumonks.org> * * All Rights Reserved * @@ -34,22 +34,19 @@ #include "sim_int.h" #include "gsm_int.h" -/* TS 31.103 Version 11.2.0 Release 11 / Chapoter 7.1.3 */ +/* TS 31.103 Version 15.5.0 Release 15 / Chapter 7.1.3 */ const struct osim_card_sw ts31_103_sw[] = { { 0x9862, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - Authentication error, incorrect MAC", + }, { + 0x9864, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, + .u.str = "Security management - Authentication error, security context not supported", }, OSIM_CARD_SW_LAST }; -static const struct osim_card_sw *isim_card_sws[] = { - ts31_103_sw, - ts102221_uicc_sw, - NULL -}; - -/* TS 31.103 Version 11.2.0 Release 11 / Chapoter 4.2 */ +/* TS 31.103 Version 15.5.0 Release 15 / Chapter 4.2 */ static const struct osim_file_desc isim_ef_in_adf_isim[] = { EF_TRANSP_N(0x6F02, 0x02, "EF.IMPI", 0, 1, 256, "IMS private user identity"), @@ -81,28 +78,34 @@ static const struct osim_file_desc isim_ef_in_adf_isim[] = { "Short message service parameters"), EF_LIN_FIX_N(0x6FE7, SFI_NONE, "EF.UICCIARI", F_OPTIONAL, 1, 256, "UICC IARI"), + EF_TRANSP_N(0x6FF7, SFI_NONE, "EF_FromPreferred", F_OPTIONAL, 1, 1, + "From Preferred"), + EF_TRANSP_N(0x6FF8, SFI_NONE, "EF_IMSConfigData", F_OPTIONAL, 3, 128, + "IMS Configuration Data"), + EF_TRANSP_N(0x6FFC, SFI_NONE, "EF_XCAPConfigData", F_OPTIONAL, 1, 128, + "XCAP Configuration Data"), + EF_LIN_FIX_N(0x6FFA, SFI_NONE, "EF_WebRTCURI", F_OPTIONAL, 3, 128, + "WebRTC URI"), }; /* Annex E - TS 101 220 */ static const uint8_t adf_isim_aid[] = { 0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x04 }; -struct osim_card_profile *osim_cprof_isim(void *ctx) +struct osim_card_app_profile *osim_aprof_isim(void *ctx) { - struct osim_card_profile *cprof; - struct osim_file_desc *mf; - - cprof = talloc_zero(ctx, struct osim_card_profile); - cprof->name = "3GPP ISIM"; - cprof->sws = isim_card_sws; - - mf = alloc_df(cprof, 0x3f00, "MF"); + struct osim_card_app_profile *aprof; + struct osim_file_desc *iadf; - cprof->mf = mf; + aprof = talloc_zero(ctx, struct osim_card_app_profile); + aprof->name = "3GPP ISIM"; + aprof->sw = ts31_103_sw; + aprof->aid_len = sizeof(adf_isim_aid); + memcpy(aprof->aid, adf_isim_aid, aprof->aid_len); /* ADF.USIM with its EF siblings */ - add_adf_with_ef(mf, adf_isim_aid, sizeof(adf_isim_aid), - "ADF.ISIM", isim_ef_in_adf_isim, - ARRAY_SIZE(isim_ef_in_adf_isim)); + iadf = alloc_adf_with_ef(aprof, adf_isim_aid, sizeof(adf_isim_aid), "ADF.ISIM", + isim_ef_in_adf_isim, ARRAY_SIZE(isim_ef_in_adf_isim)); + aprof->adf = iadf; - return cprof; + return aprof; } diff --git a/src/sim/card_fs_sim.c b/src/sim/card_fs_sim.c index 3f541f7b..55ce9af7 100644 --- a/src/sim/card_fs_sim.c +++ b/src/sim/card_fs_sim.c @@ -431,7 +431,7 @@ int osim_int_cprof_add_gsm(struct osim_file_desc *mf) add_df_with_ef(gsm, 0x5F33, "DF.ACeS", NULL, 0); add_df_with_ef(gsm, 0x5F3C, "DF.MExE", sim_ef_in_mexe, ARRAY_SIZE(sim_ef_in_mexe)); - add_df_with_ef(gsm, 0x5F40, "DF.EIA/TIA-533", NULL, 0); + add_df_with_ef(gsm, 0x5F40, "DF.EIA-TIA-533", NULL, 0); add_df_with_ef(gsm, 0x5F60, "DF.CTS", NULL, 0); add_df_with_ef(gsm, 0x5F70, "DF.SoLSA", sim_ef_in_solsa, ARRAY_SIZE(sim_ef_in_solsa)); diff --git a/src/sim/card_fs_uicc.c b/src/sim/card_fs_uicc.c index af6061cf..5dcaaa12 100644 --- a/src/sim/card_fs_uicc.c +++ b/src/sim/card_fs_uicc.c @@ -1,7 +1,7 @@ /*! \file card_fs_uicc.c * ETSI UICC specific structures / routines. */ /* - * (C) 2012 by Harald Welte <laforge@gnumonks.org> + * (C) 2012-2020 by Harald Welte <laforge@gnumonks.org> * * All Rights Reserved * @@ -25,8 +25,12 @@ #include <osmocom/sim/sim.h> +#include <osmocom/core/talloc.h> #include <osmocom/gsm/tlv.h> +#include "sim_int.h" +#include "gsm_int.h" + /* TS 102 221 V10.0.0 / 10.2.1 */ const struct osim_card_sw ts102221_uicc_sw[] = { { @@ -171,6 +175,23 @@ const struct osim_card_sw ts102221_uicc_sw[] = { OSIM_CARD_SW_LAST }; +static const struct osim_card_sw *uicc_card_sws[] = { + ts102221_uicc_sw, + NULL +}; + +/* TS 102 221 Chapter 13.1 */ +static const struct osim_file_desc uicc_ef_in_mf[] = { + EF_LIN_FIX_N(0x2f00, SFI_NONE, "EF.DIR", 0, 1, 32, + "Application directory"), + EF_TRANSP_N(0x2FE2, SFI_NONE, "EF.ICCID", 0, 10, 10, + "ICC Identification"), + EF_TRANSP_N(0x2F05, SFI_NONE, "EF.PL", 0, 2, 20, + "Preferred Languages"), + EF_LIN_FIX_N(0x2F06, SFI_NONE, "EF.ARR", F_OPTIONAL, 1, 256, + "Access Rule Reference"), +}; + const struct value_string ts102221_fcp_vals[14] = { { UICC_FCP_T_FCP, "File control parameters" }, { UICC_FCP_T_FILE_SIZE, "File size" }, @@ -209,3 +230,39 @@ const struct tlv_definition ts102221_fcp_tlv_def = { /* Annex E - TS 101 220 */ static const uint8_t __attribute__((__unused__)) adf_uicc_aid[] = { 0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x01 }; + +struct osim_card_profile *osim_cprof_uicc(void *ctx, bool have_df_gsm) +{ + struct osim_card_profile *cprof; + struct osim_file_desc *mf; + int rc; + + cprof = talloc_zero(ctx, struct osim_card_profile); + cprof->name = "3GPP UICC"; + cprof->sws = uicc_card_sws; // FIXME: extend later + + mf = alloc_df(cprof, 0x3f00, "MF"); + + cprof->mf = mf; + + /* Core UICC Files */ + add_filedesc(mf, uicc_ef_in_mf, ARRAY_SIZE(uicc_ef_in_mf)); + + /* DF.TELECOM hierarchy as sub-directory of MF */ + rc = osim_int_cprof_add_telecom(mf); + if (rc != 0) { + talloc_free(cprof); + return NULL; + } + + if (have_df_gsm) { + /* DF.GSM as sub-directory of MF */ + rc = osim_int_cprof_add_gsm(mf); + if (rc != 0) { + talloc_free(cprof); + return NULL; + } + } + + return cprof; +} diff --git a/src/sim/card_fs_usim.c b/src/sim/card_fs_usim.c index 9e9fc878..4c8f79c4 100644 --- a/src/sim/card_fs_usim.c +++ b/src/sim/card_fs_usim.c @@ -1,7 +1,7 @@ /*! \file card_fs_usim.c * 3GPP USIM specific structures / routines. */ /* - * (C) 2012-2014 by Harald Welte <laforge@gnumonks.org> + * (C) 2012-2020 by Harald Welte <laforge@gnumonks.org> * * All Rights Reserved * @@ -32,7 +32,7 @@ #include "sim_int.h" #include "gsm_int.h" -/* TS 31.102 Version 7.7.0 / Chapter 7.3 */ +/* TS 31.102 Version 15.7.0 Release 15 / Chapter 7.3 */ const struct osim_card_sw ts31_102_sw[] = { { 0x9862, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, @@ -43,29 +43,17 @@ const struct osim_card_sw ts31_102_sw[] = { }, { 0x9865, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - Key freshness error", + }, { + 0x9866, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, + .u.str = "Security management - Authentication error, no memory space available", + }, { + 0x9867, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, + .u.str = "Security management - Authentication error, no memory space available in EF_MUK", }, OSIM_CARD_SW_LAST }; -static const struct osim_card_sw *usim_card_sws[] = { - ts31_102_sw, - ts102221_uicc_sw, - NULL -}; - -/* TS 102 221 Chapter 13.1 */ -static const struct osim_file_desc uicc_ef_in_mf[] = { - EF_LIN_FIX_N(0x2f00, SFI_NONE, "EF.DIR", 0, 1, 32, - "Application directory"), - EF_TRANSP_N(0x2FE2, SFI_NONE, "EF.ICCID", 0, 10, 10, - "ICC Identification"), - EF_TRANSP_N(0x2F05, SFI_NONE, "EF.PL", 0, 2, 20, - "Preferred Languages"), - EF_LIN_FIX_N(0x2F06, SFI_NONE, "EF.ARR", F_OPTIONAL, 1, 256, - "Access Rule Reference"), -}; - -/* 31.102 Chapter 4.4.3 */ +/* 31.102 Version 15.7.0 Release 15 / Chapter 4.4.3 */ static const struct osim_file_desc usim_ef_in_df_gsm_access[] = { EF_TRANSP_N(0x4f20, 0x01, "EF.Kc", 0, 9, 9, "Ciphering Key Kc"), @@ -77,7 +65,7 @@ static const struct osim_file_desc usim_ef_in_df_gsm_access[] = { "Investigation Scan"), }; -/* 31.102 Chapter 4.2 */ +/* 31.102 Version 15.7.0 Release 15 / Chapter 4.2 */ static const struct osim_file_desc usim_ef_in_adf_usim[] = { EF_TRANSP(0x6F05, 0x02, "EF.LI", 0, 2, 16, "Language Indication", &gsm_lp_decode, NULL), @@ -161,7 +149,7 @@ static const struct osim_file_desc usim_ef_in_adf_usim[] = { "Key for hidden phone book entries"), EF_LIN_FIX_N(0x6F4D, SFI_NONE, "EF.BDN", F_OPTIONAL, 15, 32, "Barred Dialling Numbers"), - EF_LIN_FIX_N(0x6F4E, SFI_NONE, "EF.EXT4", F_OPTIONAL, 13, 13, + EF_LIN_FIX_N(0x6F55, SFI_NONE, "EF.EXT4", F_OPTIONAL, 13, 13, "Extension 4"), EF_LIN_FIX_N(0x6F58, SFI_NONE, "EF.CMI", F_OPTIONAL, 2, 16, "Comparison Method Information"), @@ -261,6 +249,39 @@ static const struct osim_file_desc usim_ef_in_adf_usim[] = { "UICC IARI"), EF_TRANSP_N(0x6FEC, SFI_NONE, "EF.PWS", F_OPTIONAL, 1, 32, "Public Warning System"), + EF_LIN_FIX_N(0x6FED, SFI_NONE, "EF_FDNURI", F_OPTIONAL, 1, 128, + "Fixed Dialling Numbers URI"), + EF_LIN_FIX_N(0x6FEE, SFI_NONE, "EF_BDNURI", F_OPTIONAL, 1, 128, + "Barred Dialling Numbers URI"), + EF_LIN_FIX_N(0x6FEF, SFI_NONE, "EF_SDNURI", F_OPTIONAL, 1, 128, + "Service Dialling Numbers URI"), + EF_LIN_FIX_N(0x6FF0, SFI_NONE, "EF_IWL", F_OPTIONAL, 18, 32, + "IMEI(SV) White Lists"), + EF_CYCLIC_N(0x6FF1, SFI_NONE, "EF_IPS", F_OPTIONAL, 4, 4, + "IMEI(SV) Pairing Status"), + EF_LIN_FIX_N(0x6FF2, SFI_NONE, "EF_IPD", F_OPTIONAL, 10, 16, + "IMEI(SV) of Pairing Device"), + EF_TRANSP_N(0x6FF3, SFI_NONE, "EF_ePDGId", F_OPTIONAL, 1, 128, + "Home ePDG Identifier"), + EF_TRANSP_N(0x6FF4, SFI_NONE, "EF_ePDGSelection", F_OPTIONAL, 1, 128, + "ePDG Selection Information"), + EF_TRANSP_N(0x6FF5, SFI_NONE, "EF_ePDGIdEm", F_OPTIONAL, 1, 128, + "Emergency ePDG Identifier"), + EF_TRANSP_N(0x6FF6, SFI_NONE, "EF_ePDGSelectionEm", F_OPTIONAL, 1, 128, + "ePDG Selection Information for Emergency Services"), + EF_TRANSP_N(0x6FF7, SFI_NONE, "EF_FromPreferred", F_OPTIONAL, 1, 1, + "From Preferred"), + EF_TRANSP_N(0x6FF8, SFI_NONE, "EF_IMSConfigData", F_OPTIONAL, 3, 128, + "IMS Configuration Data"), + /* EF TVCONFIG (TV Configuration) has no fixed FID */ + EF_TRANSP_N(0x6FF9, SFI_NONE, "EF_3GPPPSDATAOFF", F_OPTIONAL, 4, 4, + "3GPP PS Data Off"), + EF_LIN_FIX_N(0x6FFA, SFI_NONE, "EF_3GPPPSDATAOFFservicelist", F_OPTIONAL, 1, 128, + "3GPP PS Data Off Service List"), + EF_TRANSP_N(0x6FFC, SFI_NONE, "EF_XCAPConfigData", F_OPTIONAL, 1, 128, + "XCAP Configuration Data"), + EF_TRANSP_N(0x6FFD, SFI_NONE, "EF_EARFCNList", F_OPTIONAL, 1, 128, + "EARFCN list for MTC/NB-IOT UEs"), }; @@ -330,27 +351,21 @@ static const struct osim_file_desc usim_ef_in_df_hnb[] = { /* Annex E - TS 101 220 */ static const uint8_t adf_usim_aid[] = { 0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x02 }; -struct osim_card_profile *osim_cprof_usim(void *ctx) +struct osim_card_app_profile *osim_aprof_usim(void *ctx) { - struct osim_card_profile *cprof; - struct osim_file_desc *mf, *uadf; - int rc; - - cprof = talloc_zero(ctx, struct osim_card_profile); - cprof->name = "3GPP USIM"; - cprof->sws = usim_card_sws; - - mf = alloc_df(cprof, 0x3f00, "MF"); + struct osim_card_app_profile *aprof; + struct osim_file_desc *uadf; - cprof->mf = mf; - - /* Core UICC Files */ - add_filedesc(mf, uicc_ef_in_mf, ARRAY_SIZE(uicc_ef_in_mf)); + aprof = talloc_zero(ctx, struct osim_card_app_profile); + aprof->name = "3GPP USIM"; + aprof->sw = ts31_102_sw; + aprof->aid_len = sizeof(adf_usim_aid); + memcpy(aprof->aid, adf_usim_aid, aprof->aid_len); /* ADF.USIM with its EF siblings */ - uadf = add_adf_with_ef(mf, adf_usim_aid, sizeof(adf_usim_aid), - "ADF.USIM", usim_ef_in_adf_usim, - ARRAY_SIZE(usim_ef_in_adf_usim)); + uadf = alloc_adf_with_ef(aprof, adf_usim_aid, sizeof(adf_usim_aid), + "ADF.USIM", usim_ef_in_adf_usim, ARRAY_SIZE(usim_ef_in_adf_usim)); + aprof->adf = uadf; /* DFs under ADF.USIM */ add_df_with_ef(uadf, 0x5F3A, "DF.PHONEBOOK", NULL, 0); @@ -369,13 +384,5 @@ struct osim_card_profile *osim_cprof_usim(void *ctx) /* OMA BCAST Smart Card Profile */ add_df_with_ef(uadf, 0x5F80, "DF.BCAST", NULL, 0); - /* DF.GSM and DF.TELECOM hierarchy as sub-directory of MF */ - rc = osim_int_cprof_add_gsm(mf); - rc |= osim_int_cprof_add_telecom(mf); - if (rc != 0) { - talloc_free(cprof); - return NULL; - } - - return cprof; + return aprof; } diff --git a/src/sim/core.c b/src/sim/core.c index b93633c1..8b2d6f92 100644 --- a/src/sim/core.c +++ b/src/sim/core.c @@ -1,7 +1,7 @@ /*! \file core.c * Core routines for SIM/UICC/USIM access. */ /* - * (C) 2012 by Harald Welte <laforge@gnumonks.org> + * (C) 2012-2020 by Harald Welte <laforge@gnumonks.org> * * All Rights Reserved * @@ -27,10 +27,13 @@ #include <stdlib.h> #include <stdint.h> #include <string.h> +#include <errno.h> #include <osmocom/core/talloc.h> #include <osmocom/sim/sim.h> +#include "sim_int.h" + struct osim_decoded_data *osim_file_decode(struct osim_file *file, int len, uint8_t *data) { @@ -154,21 +157,19 @@ add_df_with_ef(struct osim_file_desc *parent, } struct osim_file_desc * -add_adf_with_ef(struct osim_file_desc *parent, +alloc_adf_with_ef(void *ctx, const uint8_t *adf_name, uint8_t adf_name_len, const char *name, const struct osim_file_desc *in, int num) { struct osim_file_desc *df; - df = alloc_df(parent, 0xffff, name); + df = alloc_df(ctx, 0xffff, name); if (!df) return NULL; df->type = TYPE_ADF; df->df_name = adf_name; df->df_name_len = adf_name_len; - df->parent = parent; - llist_add_tail(&df->list, &parent->child_list); add_filedesc(df, in, num); return df; @@ -187,6 +188,22 @@ osim_file_desc_find_name(struct osim_file_desc *parent, const char *name) } struct osim_file_desc * +osim_file_desc_find_aid(struct osim_file_desc *parent, const uint8_t *aid, uint8_t aid_len) +{ + struct osim_file_desc *ofd; + llist_for_each_entry(ofd, &parent->child_list, list) { + if (ofd->type != TYPE_ADF) + continue; + if (aid_len > ofd->df_name_len) + continue; + if (!memcmp(ofd->df_name, aid, aid_len)) { + return ofd; + } + } + return NULL; +} + +struct osim_file_desc * osim_file_desc_find_fid(struct osim_file_desc *parent, uint16_t fid) { struct osim_file_desc *ofd; @@ -213,6 +230,88 @@ osim_file_desc_find_sfid(struct osim_file_desc *parent, uint8_t sfid) } +/*********************************************************************** + * Application Profiles + Applications + ***********************************************************************/ + +static LLIST_HEAD(g_app_profiles); + +/*! Register an application profile. Typically called at early start-up. */ +void osim_app_profile_register(struct osim_card_app_profile *aprof) +{ + OSMO_ASSERT(!osim_app_profile_find_by_name(aprof->name)); + OSMO_ASSERT(!osim_app_profile_find_by_aid(aprof->aid, aprof->aid_len)); + llist_add_tail(&aprof->list, &g_app_profiles); +} + +/*! Find any registered application profile based on its name (e.g. "ADF.USIM") */ +const struct osim_card_app_profile * +osim_app_profile_find_by_name(const char *name) +{ + struct osim_card_app_profile *ap; + + llist_for_each_entry(ap, &g_app_profiles, list) { + if (!strcmp(name, ap->name)) + return ap; + } + return NULL; +} + +/*! Find any registered application profile based on its AID */ +const struct osim_card_app_profile * +osim_app_profile_find_by_aid(const uint8_t *aid, uint8_t aid_len) +{ + struct osim_card_app_profile *ap; + + llist_for_each_entry(ap, &g_app_profiles, list) { + if (ap->aid_len > aid_len) + continue; + if (!memcmp(ap->aid, aid, ap->aid_len)) + return ap; + } + return NULL; +} + +struct osim_card_app_hdl * +osim_card_hdl_find_app(struct osim_card_hdl *ch, const uint8_t *aid, uint8_t aid_len) +{ + struct osim_card_app_hdl *ah; + + if (aid_len > MAX_AID_LEN) + return NULL; + + llist_for_each_entry(ah, &ch->apps, list) { + if (!memcmp(ah->aid, aid, aid_len)) + return ah; + } + return NULL; +} + +/*! Add an application to a given card */ +int osim_card_hdl_add_app(struct osim_card_hdl *ch, const uint8_t *aid, uint8_t aid_len, + const char *label) +{ + struct osim_card_app_hdl *ah; + + if (aid_len > MAX_AID_LEN) + return -EINVAL; + + if (osim_card_hdl_find_app(ch, aid, aid_len)) + return -EEXIST; + + ah = talloc_zero(ch, struct osim_card_app_hdl); + if (!ah) + return -ENOMEM; + + memcpy(ah->aid, aid, aid_len); + ah->aid_len = aid_len; + ah->prof = osim_app_profile_find_by_aid(ah->aid, ah->aid_len); + if (label) + ah->label = talloc_strdup(ah, label); + llist_add_tail(&ah->list, &ch->apps); + return 0; +} + /*! Generate an APDU message and initialize APDU command header * \param[in] cla CLASS byte * \param[in] ins INSTRUCTION byte @@ -349,3 +448,12 @@ int default_decode(struct osim_decoded_data *dd, return 0; } + +int osim_init(void *ctx) +{ + osim_app_profile_register(osim_aprof_usim(ctx)); + osim_app_profile_register(osim_aprof_isim(ctx)); + osim_app_profile_register(osim_aprof_hpsim(ctx)); + + return 0; +} diff --git a/src/sim/reader.c b/src/sim/reader.c index d1a9ae60..d5292baa 100644 --- a/src/sim/reader.c +++ b/src/sim/reader.c @@ -35,6 +35,7 @@ #include <osmocom/core/msgb.h> #include <osmocom/sim/sim.h> +#include "config.h" #include "sim_int.h" @@ -242,9 +243,11 @@ struct osim_reader_hdl *osim_reader_open(enum osim_reader_driver driver, int idx struct osim_reader_hdl *rh; switch (driver) { +#ifdef HAVE_PCSC case OSIM_READER_DRV_PCSC: ops = &pcsc_reader_ops; break; +#endif default: return NULL; } diff --git a/src/sim/reader_pcsc.c b/src/sim/reader_pcsc.c index 9e05e3c0..c37380a3 100644 --- a/src/sim/reader_pcsc.c +++ b/src/sim/reader_pcsc.c @@ -87,11 +87,14 @@ static struct osim_reader_hdl *pcsc_reader_open(int num, const char *id, void *c num_readers++; } - if (num != num_readers) + if (num != num_readers) { + SCardFreeMemory(st->hContext, mszReaders); goto end; + } st->name = talloc_strdup(rh, ptr); st->dwActiveProtocol = -1; + SCardFreeMemory(st->hContext, mszReaders); return rh; end: @@ -118,6 +121,7 @@ static struct osim_card_hdl *pcsc_card_open(struct osim_reader_hdl *rh, card = talloc_zero(rh, struct osim_card_hdl); INIT_LLIST_HEAD(&card->channels); + INIT_LLIST_HEAD(&card->apps); card->reader = rh; rh->card = card; diff --git a/src/sim/sim_int.h b/src/sim/sim_int.h index 885011ed..a96a9cd2 100644 --- a/src/sim/sim_int.h +++ b/src/sim/sim_int.h @@ -12,8 +12,6 @@ struct osim_decoded_element * element_alloc_sub(struct osim_decoded_element *ee, const char *name, enum osim_element_type type, enum osim_element_repr repr); -extern const struct osim_card_sw ts102221_uicc_sw[0]; - int default_decode(struct osim_decoded_data *dd, const struct osim_file_desc *desc, int len, uint8_t *data); @@ -26,11 +24,15 @@ add_df_with_ef(struct osim_file_desc *parent, const struct osim_file_desc *in, int num); struct osim_file_desc * -add_adf_with_ef(struct osim_file_desc *parent, - const uint8_t *adf_name, uint8_t adf_name_len, - const char *name, const struct osim_file_desc *in, - int num); +alloc_adf_with_ef(void *ctx, const uint8_t *adf_name, uint8_t adf_name_len, + const char *name, const struct osim_file_desc *in, int num); extern const struct osim_reader_ops pcsc_reader_ops; +void osim_app_profile_register(struct osim_card_app_profile *aprof); + +struct osim_card_app_profile *osim_aprof_usim(void *ctx); +struct osim_card_app_profile *osim_aprof_isim(void *ctx); +struct osim_card_app_profile *osim_aprof_hpsim(void *ctx); + #endif diff --git a/src/socket.c b/src/socket.c index 9b1d30ec..503ceaf4 100644 --- a/src/socket.c +++ b/src/socket.c @@ -41,6 +41,7 @@ #include <sys/socket.h> #include <sys/types.h> #include <sys/un.h> +#include <net/if.h> #include <netinet/in.h> #include <arpa/inet.h> @@ -1194,6 +1195,27 @@ int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl) } } +/*! Set the network device to which we should bind the multicast socket + * \param[in] fd file descriptor of related socket + * \param[in] ifname name of network interface to user for multicast + * \returns 0 on success; negative otherwise */ +int osmo_sock_mcast_iface_set(int fd, const char *ifname) +{ + unsigned int ifindex; + struct ip_mreqn mr; + + /* first, resolve interface name to ifindex */ + ifindex = if_nametoindex(ifname); + if (ifindex == 0) + return -errno; + + /* next, configure kernel to use that ifindex for this sockets multicast traffic */ + memset(&mr, 0, sizeof(mr)); + mr.imr_ifindex = ifindex; + return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mr, sizeof(mr)); +} + + /*! Enable/disable receiving all multicast packets, even for non-subscribed groups * \param[in] fd file descriptor of related socket * \param[in] enable Enable or Disable receiving of all packets diff --git a/src/timer.c b/src/timer.c index 0b2e3dd3..d3129a73 100644 --- a/src/timer.c +++ b/src/timer.c @@ -40,10 +40,10 @@ #include <osmocom/core/linuxlist.h> /* These store the amount of time that we wait until next timer expires. */ -static struct timeval nearest; -static struct timeval *nearest_p; +static __thread struct timeval nearest; +static __thread struct timeval *nearest_p; -static struct rb_root timer_root = RB_ROOT; +static __thread struct rb_root timer_root = RB_ROOT; static void __add_timer(struct osmo_timer_list *timer) { diff --git a/src/usb/Makefile.am b/src/usb/Makefile.am index bca39bfe..2dee434b 100644 --- a/src/usb/Makefile.am +++ b/src/usb/Makefile.am @@ -12,8 +12,8 @@ if ENABLE_LIBUSB lib_LTLIBRARIES = libosmousb.la libosmousb_la_SOURCES = osmo_libusb.c -libosmosim_la_LDFLAGS = -version-info $(LIBVERSION) -libosmosim_la_LIBADD = \ +libosmousb_la_LDFLAGS = -version-info $(LIBVERSION) +libosmousb_la_LIBADD = \ $(top_builddir)/src/libosmocore.la \ $(TALLOC_LIBS) \ $(LIBUSB_LIBS) diff --git a/src/usb/osmo_libusb.c b/src/usb/osmo_libusb.c index 5b012b86..3c8a22a8 100644 --- a/src/usb/osmo_libusb.c +++ b/src/usb/osmo_libusb.c @@ -86,6 +86,7 @@ static void osmo_usb_added_cb(int fd, short events, void *user_data) struct osmo_fd *ofd = talloc_zero(OTC_GLOBAL, struct osmo_fd); libusb_context *luctx = user_data; unsigned int when = 0; + int rc; if (events & POLLIN) when |= OSMO_FD_READ; @@ -93,7 +94,9 @@ static void osmo_usb_added_cb(int fd, short events, void *user_data) when |= OSMO_FD_WRITE; osmo_fd_setup(ofd, fd, when, osmo_usb_fd_cb, luctx, 0); - osmo_fd_register(ofd); + rc = osmo_fd_register(ofd); + if (rc) + LOGP(DLUSB, LOGL_ERROR, "osmo_fd_register() failed with rc=%d\n", rc); } /* called by libusb if it wants to remove a file-descriptor */ @@ -220,6 +223,158 @@ libusb_device **osmo_libusb_find_matching_usb_devs(void *ctx, struct libusb_cont return out; } +/*! Find a USB device of matching VendorID/ProductID at given path. + * \param[in] luctx libusb context on which to operate + * \param[in] dev_ids zer-oterminated array of VendorId/ProductId tuples + * \param[in] path string representation of USB path + * \returns libusb_device if there was exactly one match; NULL otherwise */ +libusb_device *osmo_libusb_find_matching_dev_path(struct libusb_context *luctx, + const struct dev_id *dev_ids, + const char *path) +{ + libusb_device **list; + libusb_device *match = NULL; + unsigned int i; + int rc; + + rc = libusb_get_device_list(luctx, &list); + if (rc <= 0) + return NULL; + + for (i = 0; list[i] != NULL; i++) { + struct libusb_device_descriptor dev_desc; + libusb_device *dev = list[i]; + char pathbuf[128]; + + rc = libusb_get_device_descriptor(dev, &dev_desc); + if (rc < 0) { + LOGP(DLUSB, LOGL_ERROR, "couldn't get device descriptor\n"); + continue; + } + + /* check if device doesn't match */ + if (!match_dev_ids(&dev_desc, dev_ids)) + continue; + + /* check if path doesn't match */ + if (path) { + osmo_libusb_dev_get_path_buf(pathbuf, sizeof(pathbuf), dev); + if (strcmp(pathbuf, path)) + continue; + } + + if (match) { + /* we already have a match, but now found a second -> FAIL */ + libusb_free_device_list(list, 1); + LOGP(DLUSB, LOGL_ERROR, "Found more than one matching USB device\n"); + return NULL; + } else + match = dev; + } + + if (!match) { + /* no match: free the list with automatic unref of all devices */ + libusb_free_device_list(list, 1); + return NULL; + } + + /* unref all devices *except* the match we found */ + for (i = 0; list[i] != NULL; i++) { + libusb_device *dev = list[i]; + if (dev != match) + libusb_unref_device(dev); + } + /* free the list *without* automatic unref of all devices */ + libusb_free_device_list(list, 0); + return match; +} + +/*! Find a USB device of matching VendorID/ProductID and given iSerial string. + * \param[in] luctx libusb context on which to operate + * \param[in] dev_ids zer-oterminated array of VendorId/ProductId tuples + * \param[in] serial string representation of serial number + * \returns libusb_device if there was exactly one match; NULL otherwise */ +libusb_device *osmo_libusb_find_matching_dev_serial(struct libusb_context *luctx, + const struct dev_id *dev_ids, + const char *serial) +{ + libusb_device **list; + libusb_device *match = NULL; + unsigned int i; + int rc; + + rc = libusb_get_device_list(luctx, &list); + if (rc <= 0) + return NULL; + + for (i = 0; list[i] != NULL; i++) { + struct libusb_device_descriptor dev_desc; + libusb_device *dev = list[i]; + + rc = libusb_get_device_descriptor(dev, &dev_desc); + if (rc < 0) { + LOGP(DLUSB, LOGL_ERROR, "couldn't get device descriptor\n"); + continue; + } + + /* check if device doesn't match */ + if (!match_dev_ids(&dev_desc, dev_ids)) + continue; + + /* check if serial number string doesn't match */ + if (serial) { + char strbuf[256]; + libusb_device_handle *devh; + rc = libusb_open(dev, &devh); + if (rc < 0) { + LOGP(DLUSB, LOGL_ERROR, "Cannot open USB Device: %s\n", + libusb_strerror(rc)); + /* there's no point in continuing here, as we don't know if there + * are multiple matches if we cannot read the iSerial string of all + * devices with matching vid/pid */ + libusb_free_device_list(list, 1); + return NULL; + } + rc = libusb_get_string_descriptor_ascii(devh, dev_desc.iSerialNumber, + (uint8_t *) strbuf, sizeof(strbuf)); + if (rc < 0) { + LOGP(DLUSB, LOGL_ERROR, "Cannot read USB Descriptor: %s\n", + libusb_strerror(rc)); + libusb_close(devh); + continue; + } + libusb_close(devh); + if (strcmp(strbuf, serial)) + continue; + } + + if (match) { + /* we already have a match, but now found a second -> FAIL */ + libusb_free_device_list(list, 1); + LOGP(DLUSB, LOGL_ERROR, "Found more than one matching USB device\n"); + return NULL; + } else + match = dev; + } + + if (!match) { + /* no match: free the list with automatic unref of all devices */ + libusb_free_device_list(list, 1); + return NULL; + } + + /* unref all devices *except* the match we found */ + for (i = 0; list[i] != NULL; i++) { + libusb_device *dev = list[i]; + if (dev != match) + libusb_unref_device(dev); + } + /* free the list *without* automatic unref of all devices */ + libusb_free_device_list(list, 0); + return match; +} + + /*! find a matching interface among all interfaces of the given USB device. * \param[in] dev USB device in which we shall search * \param[in] class USB Interface Class to look for @@ -276,7 +431,7 @@ int osmo_libusb_dev_find_matching_interfaces(libusb_device *dev, int class, int out[out_idx].vendor = dev_desc.idVendor; out[out_idx].product = dev_desc.idProduct; out[out_idx].addr = addr; - strncpy(out[out_idx].path, path, sizeof(out[out_idx].path)-1); + OSMO_STRLCPY_ARRAY(out[out_idx].path, path); out[out_idx].path[sizeof(out[out_idx].path)-1] = '\0'; out[out_idx].configuration = conf_desc->bConfigurationValue; out[out_idx].interface = if_desc->bInterfaceNumber; diff --git a/src/vty/logging_vty.c b/src/vty/logging_vty.c index 6d908d9e..c51b4373 100644 --- a/src/vty/logging_vty.c +++ b/src/vty/logging_vty.c @@ -351,18 +351,18 @@ DEFUN(logging_level, int category = log_parse_category(argv[0]); int level = log_parse_level(argv[1]); - ACQUIRE_VTY_LOG_TGT_WITH_LOCK(vty, tgt); - if (level < 0) { vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE); - RET_WITH_UNLOCK(CMD_WARNING); + return CMD_WARNING; } if (category < 0) { vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE); - RET_WITH_UNLOCK(CMD_WARNING); + return CMD_WARNING; } + ACQUIRE_VTY_LOG_TGT_WITH_LOCK(vty, tgt); + tgt->categories[category].enabled = 1; tgt->categories[category].loglevel = level; diff --git a/src/vty/tdef_vty.c b/src/vty/tdef_vty.c index 4549a61c..fe6d48ba 100644 --- a/src/vty/tdef_vty.c +++ b/src/vty/tdef_vty.c @@ -361,10 +361,10 @@ static char *timer_doc_string(const char *prefix, const char *suffix) * The given timer definitions group is stored in a global pointer, so this can be done only once per main() scope. * It would also be possible to have distinct timer groups on separate VTY subnodes, with a "manual" implementation, but * not with this API. - * \param[in] parent_node VTY node id at which to add the timer group commands, e.g. CONFIG_NODE. + * \param[in] parent_cfg_node VTY node at which to add the timer configuration commands, e.g. CONFIG_NODE. * \param[in] groups Global timer groups definition. */ -void osmo_tdef_vty_groups_init(enum node_type parent_node, struct osmo_tdef_group *groups) +void osmo_tdef_vty_groups_init(unsigned int parent_cfg_node, struct osmo_tdef_group *groups) { struct osmo_tdef_group *g; OSMO_ASSERT(!global_tdef_groups); @@ -380,7 +380,7 @@ void osmo_tdef_vty_groups_init(enum node_type parent_node, struct osmo_tdef_grou cfg_timer_cmd.doc = timer_doc_string("Configure or show timers\n", OSMO_TDEF_VTY_DOC_SET); install_element_ve(&show_timer_cmd); - install_element(parent_node, &cfg_timer_cmd); + install_element(parent_cfg_node, &cfg_timer_cmd); } /*! Write the global osmo_tdef_group configuration to VTY, as previously passed to osmo_tdef_vty_groups_init(). |