add gsm0808_{enc,dec}_cell_id

Clarify semantics and micro-optimise for the case of single Cell Identifer IEs.
Test in gsm0808_test.c

So far we have gsm0808_enc_cell_id_list2(), but there also exist instances of
single Cell Identifiers (3GPP TS 48.008 3.2.2.17).

It is possible to decode the same using the cell identifier list API, but this
forces the caller to also keep a full struct gsm0808_cell_id_list2 with all its
127 entries around.

E.g. for handover, there are two Cell Identifiers (Serving and Target); I'd
need two full cell id lists for each, and these would be dynamically allocated
for each handover operation, whether it uses them or not.

Related: OS#2283 (inter-BSC HO, BSC side)
Change-Id: I9f9c528965775698ab62ac386af0516192c4b0cc
diff --git a/src/gsm/gsm0808_utils.c b/src/gsm/gsm0808_utils.c
index 996456e..73d9362 100644
--- a/src/gsm/gsm0808_utils.c
+++ b/src/gsm/gsm0808_utils.c
@@ -988,6 +988,57 @@
 	return added;
 }
 
+/*! Encode Cell Identifier IE (3GPP TS 48.008 3.2.2.17).
+ *  \param[out] msg Message Buffer to which IE is to be appended
+ *  \param[in] ci Cell ID to be encoded
+ *  \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_cell_id(struct msgb *msg, const struct gsm0808_cell_id *ci)
+{
+	uint8_t rc;
+	uint8_t *ie_tag;
+	struct gsm0808_cell_id_list2 cil = {
+		.id_discr = ci->id_discr,
+		.id_list = { ci->id },
+		.id_list_len = 1,
+	};
+
+	OSMO_ASSERT(msg);
+	OSMO_ASSERT(ci);
+
+	ie_tag = msg->tail;
+	rc = gsm0808_enc_cell_id_list2(msg, &cil);
+
+	if (rc <= 0)
+		return rc;
+
+	*ie_tag = GSM0808_IE_CELL_IDENTIFIER;
+	return rc;
+}
+
+/*! Decode Cell Identifier IE (3GPP TS 48.008 3.2.2.17).
+ *  \param[out] ci Caller-provided memory to store Cell ID.
+ *  \param[in] elem IE value to be decoded.
+ *  \param[in] len Length of \a elem in bytes.
+ *  \returns number of bytes parsed; negative on error */
+int gsm0808_dec_cell_id(struct gsm0808_cell_id *ci, const uint8_t *elem, uint8_t len)
+{
+	struct gsm0808_cell_id_list2 cil;
+	int rc;
+	rc = gsm0808_dec_cell_id_list2(&cil, elem, len);
+	if (rc < 0)
+		return rc;
+	if (cil.id_discr == CELL_IDENT_BSS || cil.id_discr == CELL_IDENT_NO_CELL) {
+		if (cil.id_list_len != 0)
+			return -EINVAL;
+	} else {
+		if (cil.id_list_len != 1)
+			return -EINVAL;
+	}
+	ci->id_discr = cil.id_discr;
+	ci->id = cil.id_list[0];
+	return rc;
+}
+
 /*! Convert the representation of the permitted speech codec identifier
  *  that is used in struct gsm0808_channel_type to the speech codec
  *  representation we use in struct gsm0808_speech_codec.
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index 4d009e0..388fcc0 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -177,6 +177,8 @@
 gsm0808_dec_cell_id_list;
 gsm0808_dec_cell_id_list2;
 gsm0808_cell_id_list_add;
+gsm0808_enc_cell_id;
+gsm0808_dec_cell_id;
 gsm0808_chan_type_to_speech_codec;
 gsm0808_speech_codec_from_chan_type;
 gsm0808_speech_codec_type_names;