abis: Create a routine that can parse all SW Descriptions of a SW Config

Be able to parse the entire SW Config IE. Parse the SW Descruption
into a struct provided by the caller.
diff --git a/openbsc/src/libbsc/abis_nm.c b/openbsc/src/libbsc/abis_nm.c
index e95c0a9..7485a6c 100644
--- a/openbsc/src/libbsc/abis_nm.c
+++ b/openbsc/src/libbsc/abis_nm.c
@@ -359,7 +359,8 @@
 	return abis_nm_sendmsg(bts, msg);
 }
 
-static int abis_nm_parse_sw_descr(const uint8_t *sw_descr, int sw_descr_len)
+int abis_nm_parse_sw_config(const uint8_t *sw_descr, const size_t sw_descr_len,
+			struct abis_nm_sw_descr *desc, const int res_len)
 {
 	static const struct tlv_definition sw_descr_def = {
 		.def = {
@@ -368,38 +369,56 @@
 		},
 	};
 
-	uint8_t tag;
-	uint16_t tag_len;
-	const uint8_t *val;
-	int ofs = 0, len;
+	size_t pos = 0;
+	int desc_pos = 0;
 
-	/* Classic TLV parsing doesn't work well with SW_DESCR because of it's
-	 * nested nature and the fact you have to assume it contains only two sub
-	 * tags NM_ATT_FILE_VERSION & NM_ATT_FILE_ID to parse it */
+	for (pos = 0; pos < sw_descr_len && desc_pos < res_len; ++desc_pos) {
+		uint8_t tag;
+		uint16_t tag_len;
+		const uint8_t *val;
+		int len;
 
-	if (sw_descr[0] != NM_ATT_SW_DESCR) {
-		DEBUGP(DNM, "SW_DESCR attribute identifier not found!\n");
-		return -1;
+		memset(&desc[desc_pos], 0, sizeof(desc[desc_pos]));
+		desc[desc_pos].start = &sw_descr[pos];
+
+		/* Classic TLV parsing doesn't work well with SW_DESCR because of it's
+		 * nested nature and the fact you have to assume it contains only two sub
+		 * tags NM_ATT_FILE_VERSION & NM_ATT_FILE_ID to parse it */
+		if (sw_descr[pos] != NM_ATT_SW_DESCR) {
+			LOGP(DNM, LOGL_ERROR,
+				"SW_DESCR attribute identifier not found!\n");
+			return -1;
+		}
+
+		pos += 1;
+		len = tlv_parse_one(&tag, &tag_len, &val,
+			&sw_descr_def, &sw_descr[pos], sw_descr_len - pos);
+		if (len < 0 || (tag != NM_ATT_FILE_ID)) {
+			LOGP(DNM, LOGL_ERROR,
+				"FILE_ID attribute identifier not found!\n");
+			return -2;
+		}
+		desc[desc_pos].file_id = val;
+		desc[desc_pos].file_id_len = tag_len;
+		pos += len;
+
+
+		len = tlv_parse_one(&tag, &tag_len, &val,
+			&sw_descr_def, &sw_descr[pos], sw_descr_len - pos);
+		if (len < 0 || (tag != NM_ATT_FILE_VERSION)) {
+			LOGP(DNM, LOGL_ERROR,
+				"FILE_VERSION attribute identifier not found!\n");
+			return -3;
+		}
+		desc[desc_pos].file_ver = val;
+		desc[desc_pos].file_ver_len = tag_len;
+		pos += len;
+
+		/* final size */
+		desc[desc_pos].len = &sw_descr[pos] - desc[desc_pos].start;
 	}
-	ofs += 1;
 
-	len = tlv_parse_one(&tag, &tag_len, &val,
-		&sw_descr_def, &sw_descr[ofs], sw_descr_len-ofs);
-	if (len < 0 || (tag != NM_ATT_FILE_ID)) {
-		DEBUGP(DNM, "FILE_ID attribute identifier not found!\n");
-		return -2;
-	}
-	ofs += len;
-
-	len = tlv_parse_one(&tag, &tag_len, &val,
-		&sw_descr_def, &sw_descr[ofs], sw_descr_len-ofs);
-	if (len < 0 || (tag != NM_ATT_FILE_VERSION)) {
-		DEBUGP(DNM, "FILE_VERSION attribute identifier not found!\n");
-		return -3;
-	}
-	ofs += len;
-
-	return ofs;
+	return desc_pos;
 }
 
 static int abis_nm_rx_sw_act_req(struct msgb *mb)
@@ -409,7 +428,8 @@
 	struct e1inp_sign_link *sign_link = mb->dst;
 	struct tlv_parsed tp;
 	const uint8_t *sw_config;
-	int ret, sw_config_len, sw_descr_len;
+	int ret, sw_config_len;
+	struct abis_nm_sw_descr sw_descr[1];
 
 	abis_nm_debugp_foh(DNM, foh);
 
@@ -439,16 +459,19 @@
 		DEBUGP(DNM, "Found SW config: %s\n", osmo_hexdump(sw_config, sw_config_len));
 	}
 
-		/* Use the first SW_DESCR present in SW config */
-	sw_descr_len = abis_nm_parse_sw_descr(sw_config, sw_config_len);
-	if (sw_descr_len < 0)
+	/* Parse up to two sw descriptions from the data */
+	ret = abis_nm_parse_sw_config(sw_config, sw_config_len,
+				&sw_descr[0], ARRAY_SIZE(sw_descr));
+	if (ret <= 0) {
+		LOGP(DNM, LOGL_ERROR, "Failed to parse SW Config.\n");
 		return -EINVAL;
+	}
 
 	return ipacc_sw_activate(sign_link->trx->bts, foh->obj_class,
 				 foh->obj_inst.bts_nr,
 				 foh->obj_inst.trx_nr,
 				 foh->obj_inst.ts_nr,
-				 sw_config, sw_descr_len);
+				 sw_descr[0].start, sw_descr[0].len);
 }
 
 /* Receive a CHANGE_ADM_STATE_ACK, parse the TLV and update local state */