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/tests/bssmap_le/bssmap_le_test.c b/tests/bssmap_le/bssmap_le_test.c
new file mode 100644
index 0000000..59c7ed2
--- /dev/null
+++ b/tests/bssmap_le/bssmap_le_test.c
@@ -0,0 +1,177 @@
+#include <stdio.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/bssmap_le.h>
+
+struct bssmap_le_pdu bssmap_le_test_pdus[] = {
+	{
+		.msg_type = BSSMAP_LE_MSGT_RESET,
+		.reset = GSM0808_CAUSE_EQUIPMENT_FAILURE,
+	},
+	{
+		.msg_type = BSSMAP_LE_MSGT_RESET_ACK,
+	},
+	{
+		.msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_REQ,
+		.perform_loc_req = {
+			.location_type = {
+				.location_information = BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC,
+			},
+
+			.cell_id = {
+				.id_discr = CELL_IDENT_LAC_AND_CI,
+				.id.lac_and_ci = {
+					.lac = 23,
+					.ci = 42,
+				},
+			},
+
+			.lcs_client_type_present = true,
+			.lcs_client_type = BSSMAP_LE_LCS_CTYPE_VALUE_ADDED_UNSPECIFIED,
+
+			.imsi = {
+				.type = GSM_MI_TYPE_IMSI,
+				.imsi = "1234567890",
+			},
+
+			.imei = {
+				.type = GSM_MI_TYPE_IMEI,
+				.imei = "123456789012345",
+			},
+
+			.apdu_present = true,
+			.apdu = {
+				.msg_type = BSSLAP_MSGT_TA_LAYER3,
+				.ta_layer3 = {
+					.ta = 23,
+				},
+			},
+		},
+	},
+	{
+		.msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_RESP,
+		.perform_loc_resp = {
+			.location_estimate_present = true,
+			.location_estimate = {
+				.ell_point_unc_circle = {
+					.h = { .type = GAD_TYPE_ELL_POINT_UNC_CIRCLE },
+					.lat = { 1, 2, 3 },
+					.lon = { 4, 5, 6 },
+					.unc = 123,
+				},
+			},
+		},
+	},
+	{
+		.msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_RESP,
+		.perform_loc_resp = {
+			.lcs_cause = {
+				.present = true,
+				.cause_val = LCS_CAUSE_REQUEST_ABORTED,
+			},
+		},
+	},
+	{
+		.msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_RESP,
+		.perform_loc_resp = {
+			.lcs_cause = {
+				.present = true,
+				.cause_val = LCS_CAUSE_POS_METH_FAILURE,
+				.diag_val_present = true,
+				.diag_val = 23,
+			},
+		},
+	},
+	{
+		.msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_ABORT,
+		.perform_loc_abort = {
+			.present = true,
+			.cause_val = LCS_CAUSE_REQUEST_ABORTED,
+		},
+	},
+	{
+		.msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO,
+		.conn_oriented_info = {
+			.apdu = {
+				.msg_type = BSSLAP_MSGT_TA_REQUEST,
+			},
+		},
+	},
+	{
+		.msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO,
+		.conn_oriented_info = {
+			.apdu = {
+				.msg_type = BSSLAP_MSGT_TA_RESPONSE,
+				.ta_response = {
+					.cell_id = 23,
+					.ta = 42,
+				},
+			},
+		},
+	},
+	{
+		.msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO,
+		.conn_oriented_info = {
+			.apdu = {
+				.msg_type = BSSLAP_MSGT_REJECT,
+				.reject = BSSLAP_CAUSE_CONGESTION,
+			},
+		},
+	},
+};
+
+void test_bssmap_le_enc_dec()
+{
+	struct bssmap_le_pdu *pdu;
+	printf("--- %s\n", __func__);
+
+	for (pdu = bssmap_le_test_pdus; (pdu - bssmap_le_test_pdus) < ARRAY_SIZE(bssmap_le_test_pdus); pdu++) {
+		struct msgb *msg;
+		struct bssap_le_pdu enc_pdu = {
+			.discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
+			.bssmap_le = *pdu,
+		};
+		struct bssap_le_pdu dec_pdu;
+		struct osmo_bssap_le_err *err;
+		void *loop_ctx;
+		int rc;
+
+		msg = osmo_bssap_le_enc(&enc_pdu);
+		if (!msg) {
+			printf("[%ld] %s: ERROR: failed to encode pdu\n", (pdu - bssmap_le_test_pdus),
+			       osmo_bssmap_le_msgt_name(pdu->msg_type));
+			goto loop_end;
+		}
+		loop_ctx = msg;
+
+		memset(&dec_pdu, 0xff, sizeof(dec_pdu));
+		rc = osmo_bssap_le_dec(&dec_pdu, &err, loop_ctx, msg);
+		if (rc) {
+			printf("[%ld] %s: ERROR: failed to decode pdu: %s\n", (pdu - bssmap_le_test_pdus),
+			       osmo_bssmap_le_msgt_name(pdu->msg_type), err->logmsg);
+			printf("     encoded data: %s\n", osmo_hexdump(msg->data, msg->len));
+			goto loop_end;
+		}
+
+		if (memcmp(&enc_pdu, &dec_pdu, sizeof(dec_pdu))) {
+			printf("[%ld] %s: ERROR: decoded PDU != encoded PDU\n", (pdu - bssmap_le_test_pdus),
+			       osmo_bssmap_le_msgt_name(pdu->msg_type));
+			printf("     original struct: %s\n", osmo_hexdump((void*)&enc_pdu, sizeof(enc_pdu)));
+			printf("      decoded struct: %s\n", osmo_hexdump((void*)&dec_pdu, sizeof(dec_pdu)));
+			printf("        encoded data: %s\n", osmo_hexdump(msg->data, msg->len));
+			goto loop_end;
+		}
+
+		printf("[%ld] %s: ok (encoded len = %d)\n", (pdu - bssmap_le_test_pdus),
+		       osmo_bssmap_le_msgt_name(pdu->msg_type), msg->len);
+
+loop_end:
+		msgb_free(msg);
+	}
+}
+
+int main()
+{
+	test_bssmap_le_enc_dec();
+	return 0;
+}
diff --git a/tests/bssmap_le/bssmap_le_test.ok b/tests/bssmap_le/bssmap_le_test.ok
new file mode 100644
index 0000000..a6f0dee
--- /dev/null
+++ b/tests/bssmap_le/bssmap_le_test.ok
@@ -0,0 +1,11 @@
+--- test_bssmap_le_enc_dec
+[0] RESET: ok (encoded len = 6)
+[1] RESET ACKNOWLEDGE: ok (encoded len = 3)
+[2] PERFORM LOCATION REQUEST: ok (encoded len = 41)
+[3] PERFORM LOCATION RESPONSE: ok (encoded len = 13)
+[4] PERFORM LOCATION RESPONSE: ok (encoded len = 6)
+[5] PERFORM LOCATION RESPONSE: ok (encoded len = 7)
+[6] PERFORM LOCATION ABORT: ok (encoded len = 6)
+[7] CONNECTION ORIENTED INFORMATON: ok (encoded len = 8)
+[8] CONNECTION ORIENTED INFORMATON: ok (encoded len = 13)
+[9] CONNECTION ORIENTED INFORMATON: ok (encoded len = 10)