add BSSMAP-LE coding for Location Services

BSSMAP-LE: add Lb-interface messages between BSC and SMLC:
- Reset
- Reset Acknowledge
- Perform Location Request, possibly containing BSSLAP TA Layer3
- Perform Location Response
- Perform Location Abort
- Connection Oriented Information containing any BSSLAP APDU

Add encoding and decoding tests.

Change-Id: I271e59b794bafc0a7ae0eabbf58918f6d7df431d
diff --git a/include/osmocom/gsm/bssmap_le.h b/include/osmocom/gsm/bssmap_le.h
new file mode 100644
index 0000000..1c750c8
--- /dev/null
+++ b/include/osmocom/gsm/bssmap_le.h
@@ -0,0 +1,81 @@
+/*! \addtogroup bssmap_le
+ *  @{
+ *  \file bssmap_le.h
+ * Message encoding and decoding for 3GPP TS 49.031 BSSMAP-LE.
+ */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.de>
+ *
+ * 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.
+ *
+ */
+#pragma once
+
+#include <osmocom/gsm/protocol/gsm_49_031.h>
+
+struct osmo_bsslap_err;
+struct osmo_gad_err;
+
+struct osmo_bssmap_le_err {
+	int rc;
+	enum bssmap_le_msgt msg_type;
+	enum bssmap_le_iei iei;
+	enum lcs_cause cause;
+	struct osmo_bsslap_err *bsslap_err;
+	struct osmo_gad_err *gad_err;
+	char *logmsg;
+};
+
+struct osmo_bssap_le_err {
+	int rc;
+	struct osmo_bssmap_le_err *bssmap_le_err;
+	void *dtap_err;
+	char *logmsg;
+};
+
+enum bssmap_le_msgt osmo_bssmap_le_msgt(const uint8_t *data, uint8_t len);
+
+extern const struct value_string osmo_bssmap_le_msgt_names[];
+static inline const char *osmo_bssmap_le_msgt_name(enum bssmap_le_msgt val)
+{ return get_value_string(osmo_bssmap_le_msgt_names, val); }
+
+extern const struct value_string osmo_bssmap_le_iei_names[];
+static inline const char *osmo_bssmap_le_iei_name(enum bssmap_le_iei val)
+{ return get_value_string(osmo_bssmap_le_iei_names, val); }
+
+int osmo_lcs_cause_enc(struct msgb *msg, const struct lcs_cause_ie *lcs_cause);
+int osmo_lcs_cause_dec(struct lcs_cause_ie *lcs_cause,
+		       enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
+		       struct osmo_bssmap_le_err **err, void *err_ctx,
+		       const uint8_t *data, uint8_t len);
+
+int osmo_bssap_le_pdu_to_str_buf(char *buf, size_t buflen, const struct bssap_le_pdu *bssap_le);
+char *osmo_bssap_le_pdu_to_str_c(void *ctx, const struct bssap_le_pdu *bssap_le);
+
+struct msgb *osmo_bssap_le_enc(const struct bssap_le_pdu *pdu);
+int osmo_bssap_le_dec(struct bssap_le_pdu *pdu, struct osmo_bssap_le_err **err, void *err_ctx, struct msgb *msg);
+
+uint8_t osmo_bssmap_le_ie_enc_location_type(struct msgb *msg, const struct bssmap_le_location_type *location_type);
+int osmo_bssmap_le_ie_dec_location_type(struct bssmap_le_location_type *lt,
+					enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
+					struct osmo_bssmap_le_err **err, void *err_ctx,
+					const uint8_t *elem, uint8_t len);
+
+/*! @} */
diff --git a/include/osmocom/gsm/protocol/gsm_49_031.h b/include/osmocom/gsm/protocol/gsm_49_031.h
index b44a07e..c6152e1 100644
--- a/include/osmocom/gsm/protocol/gsm_49_031.h
+++ b/include/osmocom/gsm/protocol/gsm_49_031.h
@@ -29,6 +29,10 @@
 
 #include <stdint.h>
 #include <stdbool.h>
+#include <osmocom/gsm/protocol/gsm_48_071.h>
+#include <osmocom/gsm/protocol/gsm_23_032.h>
+#include <osmocom/gsm/gsm0808_utils.h>
+#include <osmocom/gsm/gsm48.h>
 
 /*! 3GPP TS 49.031 10.13 LCS Cause, also in 3GPP TS 48.008 3.2.2.66, which simply refers to the former. */
 enum lcs_cause {
@@ -58,4 +62,151 @@
 	uint8_t diag_val;
 };
 
+enum bssap_le_msg_discr {
+	BSSAP_LE_MSG_DISCR_BSSMAP_LE = 0,
+};
+
+enum bssmap_le_msgt {
+	BSSMAP_LE_MSGT_PERFORM_LOC_REQ = 0x2b,
+	BSSMAP_LE_MSGT_PERFORM_LOC_RESP = 0x2d,
+	BSSMAP_LE_MSGT_PERFORM_LOC_ABORT = 0x2e,
+	BSSMAP_LE_MSGT_PERFORM_LOC_INFO = 0x2f,
+	BSSMAP_LE_MSGT_ASSIST_INFO_REQ = 0x20,
+	BSSMAP_LE_MSGT_ASSIST_INFO_RESP = 0x21,
+	BSSMAP_LE_MSGT_CONN_ORIENTED_INFO = 0x2a,
+	BSSMAP_LE_MSGT_CONN_LESS_INFO = 0x3a,
+	BSSMAP_LE_MSGT_RESET = 0x30,
+	BSSMAP_LE_MSGT_RESET_ACK = 0x31,
+};
+
+enum bssmap_le_iei {
+	BSSMAP_LE_IEI_LCS_QoS = 0x3e,
+	BSSMAP_LE_IEI_LCS_PRIORITY = 0x43,
+	BSSMAP_LE_IEI_LOCATION_TYPE = 0x44,
+	BSSMAP_LE_IEI_GANSS_LOCATION_TYPE = 0x82,
+	BSSMAP_LE_IEI_GEO_LOCATION = 0x45,
+	BSSMAP_LE_IEI_POSITIONING_DATA = 0x46,
+	BSSMAP_LE_IEI_GANSS_POS_DATA = 0x83,
+	BSSMAP_LE_IEI_VELOCITY_DATA = 0x55,
+	BSSMAP_LE_IEI_LCS_CAUSE = 0x47,
+	BSSMAP_LE_IEI_LCS_CLIENT_TYPE = 0x48,
+	BSSMAP_LE_IEI_APDU = 0x49,
+	BSSMAP_LE_IEI_NET_ELEM_ID = 0x4a,
+	BSSMAP_LE_IEI_REQ_GPS_ASS_D = 0x4b,
+	BSSMAP_LE_IEI_REQ_GANSS_ASS_D = 0x41,
+	BSSMAP_LE_IEI_DECIPH_KEYS = 0x4c,
+	BSSMAP_LE_IEI_RET_ERR_REQ = 0x4d,
+	BSSMAP_LE_IEI_RET_ERR_CAUSE = 0x4e,
+	BSSMAP_LE_IEI_SEGMENTATION = 0x4f,
+	BSSMAP_LE_IEI_CLASSMARK3_INFO = 0x13,
+	BSSMAP_LE_IEI_CAUSE = 0x4,
+	BSSMAP_LE_IEI_CELL_ID = 0x5,
+	BSSMAP_LE_IEI_CHOSEN_CHAN = 0x21,
+	BSSMAP_LE_IEI_IMSI = 0x0,
+	BSSMAP_LE_IEI_LCS_CAPABILITY = 0x50,
+	BSSMAP_LE_IEI_PKT_MEAS_REP = 0x51,
+	BSSMAP_LE_IEI_CELL_ID_LIST = 0x52,
+	BSSMAP_LE_IEI_IMEI = 0x80,
+	BSSMAP_LE_IEI_BSS_MLAT_CAP = 0x84,
+	BSSMAP_LE_IEI_CELL_INFO_LIST = 0x85,
+	BSSMAP_LE_IEI_BTS_RX_ACC_LVL = 0x86,
+	BSSMAP_LE_IEI_MLAT_METHOD = 0x87,
+	BSSMAP_LE_IEI_MLAT_TA = 0x88,
+	BSSMAP_LE_IEI_MS_SYNC_ACC = 0x89,
+	BSSMAP_LE_IEI_SHORT_ID_SET = 0x8a,
+	BSSMAP_LE_IEI_RANDOM_ID_SET = 0x8b,
+	BSSMAP_LE_IEI_SHORT_BSS_ID = 0x8c,
+	BSSMAP_LE_IEI_RANDOM_ID = 0x8d,
+	BSSMAP_LE_IEI_SHORT_ID = 0x8e,
+	BSSMAP_LE_IEI_COVERAGE_CLASS = 0x8f,
+	BSSMAP_LE_IEI_MTA_ACC_SEC_RQD = 0x90,
+};
+
+enum bssmap_le_apdu_proto {
+	BSSMAP_LE_APDU_PROT_RESERVED = 0,
+	BSSMAP_LE_APDU_PROT_BSSLAP = 1,
+	BSSMAP_LE_APDU_PROT_LLP = 2,
+	BSSMAP_LE_APDU_PROT_SMLCPP = 3,
+};
+
+enum bssmap_le_location_information {
+	BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC = 0x0,
+	BSSMAP_LE_LOC_INFO_ASSIST_TARGET_MS = 0x1,
+	BSSMAP_LE_LOC_INFO_BC_DECIPHER_KEYS = 0x2,
+};
+
+enum bssmap_le_positioning_method {
+	BSSMAP_LE_POS_METHOD_OMITTED = 0x0,
+	BSSMAP_LE_POS_METHOD_MOBILE_ASSISTED_E_OTD = 0x1,
+	BSSMAP_LE_POS_METHOD_MOBILE_BASED_E_OTD = 0x2,
+	BSSMAP_LE_POS_METHOD_ASSISTED_GPS = 0x3,
+};
+
+struct bssmap_le_location_type {
+	enum bssmap_le_location_information location_information;
+	enum bssmap_le_positioning_method positioning_method;
+};
+
+enum bssmap_le_lcs_client_type {
+	BSSMAP_LE_LCS_CTYPE_VALUE_ADDED_UNSPECIFIED = 0x0,
+	BSSMAP_LE_LCS_CTYPE_PLMN_OPER_UNSPECIFIED = 0x20,
+	BSSMAP_LE_LCS_CTYPE_PLMN_OPER_BCAST_SERVICE = 0x21,
+	BSSMAP_LE_LCS_CTYPE_PLMN_OPER_OAM = 0x22,
+	BSSMAP_LE_LCS_CTYPE_PLMN_OPER_ANON_STATS = 0x23,
+	BSSMAP_LE_LCS_CTYPE_PLMN_OPER_TGT_MS_SVC = 0x24,
+	BSSMAP_LE_LCS_CTYPE_EMERG_SVC_UNSPECIFIED = 0x30,
+	BSSMAP_LE_LCS_CTYPE_LI_UNSPECIFIED = 0x40,
+};
+
+struct bssmap_le_perform_loc_req {
+	struct bssmap_le_location_type location_type;
+	struct gsm0808_cell_id cell_id;
+
+	bool lcs_client_type_present;
+	enum bssmap_le_lcs_client_type lcs_client_type;
+
+	struct osmo_mobile_identity imsi;
+	struct osmo_mobile_identity imei;
+
+	bool apdu_present;
+	struct bsslap_pdu apdu;
+
+	bool more_items; /*!< always set this to false */
+};
+
+struct bssmap_le_perform_loc_resp {
+	bool location_estimate_present;
+	union gad_raw location_estimate;
+
+	struct lcs_cause_ie lcs_cause;
+
+	bool more_items; /*!< always set this to false */
+};
+
+struct bssmap_le_conn_oriented_info {
+	struct bsslap_pdu apdu;
+
+	bool more_items; /*!< always set this to false */
+};
+
+struct bssmap_le_pdu {
+	enum bssmap_le_msgt msg_type;
+	union {
+		enum gsm0808_cause reset;
+		/* reset_ack consists only of the message type */
+		struct bssmap_le_perform_loc_req perform_loc_req;
+		struct bssmap_le_perform_loc_resp perform_loc_resp;
+		struct lcs_cause_ie perform_loc_abort;
+		struct bssmap_le_conn_oriented_info conn_oriented_info;
+	};
+};
+
+struct bssap_le_pdu {
+	enum bssap_le_msg_discr discr;
+	union {
+		struct bssmap_le_pdu bssmap_le;
+		/* future: add DTAP PDU, currently not implemented */
+	};
+};
+
 /*! @} */