* more extensive A-bis OML support

diff --git a/src/abis_nm.c b/src/abis_nm.c
index 313b9dc..c670dcb 100644
--- a/src/abis_nm.c
+++ b/src/abis_nm.c
@@ -68,7 +68,7 @@
 	NM_MT_LOAD_ABORT,
 	NM_MT_LOAD_END_ACK,
 	NM_MT_LOAD_END_NACK,
-	NM_MT_SW_ACT_REQ,
+	//NM_MT_SW_ACT_REQ,
 	NM_MT_ACTIVATE_SW_ACK,
 	NM_MT_ACTIVATE_SW_NACK,
 	NM_MT_SW_ACTIVATED_REP,
@@ -170,16 +170,47 @@
 
 static int abis_nm_rcvmsg_sw(struct msgb *mb);
 
+const char *oc_names[] = {
+	[NM_OC_SITE_MANAGER]	= "SITE MANAGER",
+	[NM_OC_BTS]		= "BTS",
+	[NM_OC_RADIO_CARRIER]	= "RADIO CARRIER",
+	[NM_OC_BASEB_TRANSC]	= "BASEBAND TRANSCEIVER",
+	[NM_OC_CHANNEL]		= "CHANNEL",
+};
+
+static const char *obj_class_name(u_int8_t oc)
+{
+	if (oc >= ARRAY_SIZE(oc_names))
+		return "UNKNOWN";
+	return oc_names[oc];
+}
+
+static const char *opstate_name(u_int8_t os)
+{
+	switch (os) {
+	case 1:
+		return "Disabled";
+	case 2:
+		return "Enabled";
+	case 0xff:
+		return "NULL";
+	default:
+		return "RFU";
+	}
+}
+
+
 static int abis_nm_rx_statechg_rep(struct msgb *mb)
 {
 	struct abis_om_fom_hdr *foh = msgb_l3(mb);
 	u_int8_t *data = &foh->data[0];
 	
-	DEBUGP(DNM, "STATE CHG: OC=%02x INST=(%02x,%02x,%02x) ",
-		foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr,
+	DEBUGP(DNM, "STATE CHG: OC=%s(%02x) INST=(%02x,%02x,%02x) ",
+		obj_class_name(foh->obj_class), foh->obj_class, 
+		foh->obj_inst.bts_nr, foh->obj_inst.trx_nr,
 		foh->obj_inst.ts_nr);
 	if (*data++ == NM_ATT_OPER_STATE)
-		DEBUGPC(DNM, "OP_STATE=%02x ", *data++);
+		DEBUGPC(DNM, "OP_STATE=%s ", opstate_name(*data++));
 	if (*data++ == NM_ATT_AVAIL_STATUS) {
 		u_int8_t att_len = *data++;
 		while (att_len--)
@@ -200,6 +231,9 @@
 	case NM_MT_STATECHG_EVENT_REP:
 		return abis_nm_rx_statechg_rep(mb);
 		break;
+	case NM_MT_SW_ACTIVATED_REP:
+		DEBUGP(DNM, "Software Activated Report\n");
+		break;
 	};
 
 	DEBUGP(DNM, "reporting NM MT 0x%02x\n", mt);
@@ -207,6 +241,46 @@
 	return 0;
 }
 
+/* Activate the specified software into the BTS */
+static int ipacc_sw_activate(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0, u_int8_t i1,
+			     u_int8_t i2, u_int8_t *sw_desc, u_int8_t swdesc_len)
+{
+	struct abis_om_hdr *oh;
+	struct msgb *msg = nm_msgb_alloc();
+	u_int8_t len = swdesc_len;
+	u_int8_t *trailer;
+
+	oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+	fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, obj_class, i0, i1, i2);
+
+	trailer = msgb_put(msg, swdesc_len);
+	memcpy(trailer, sw_desc, swdesc_len);
+
+	return abis_nm_sendmsg(bts, msg);
+}
+
+static int abis_nm_rx_sw_act_req(struct msgb *mb)
+{
+	struct abis_om_hdr *oh = msgb_l2(mb);
+	struct abis_om_fom_hdr *foh = msgb_l3(mb);
+	int ret;
+
+	DEBUGP(DNM, "Software Activate Request, ACKing and Activating\n");
+
+	ret =  abis_nm_sw_act_req_ack(mb->trx->bts, foh->obj_class,
+				      foh->obj_inst.bts_nr,
+				      foh->obj_inst.trx_nr,
+				      foh->obj_inst.ts_nr,
+				      foh->data, oh->length-sizeof(*foh));
+
+	/* FIXME: properly parse attributes */
+	return ipacc_sw_activate(mb->trx->bts, foh->obj_class,
+				 foh->obj_inst.bts_nr,
+				 foh->obj_inst.trx_nr,
+				 foh->obj_inst.ts_nr,
+				 foh->data + oh->length-sizeof(*foh)-22, 22);
+}
+
 /* Receive a OML NM Message from BTS */
 static int abis_nm_rcvmsg_fom(struct msgb *mb)
 {
@@ -240,6 +314,9 @@
 #endif
 
 	switch (mt) {
+	case NM_MT_SW_ACT_REQ:
+		return abis_nm_rx_sw_act_req(mb);
+		break;
 	case NM_MT_BS11_LMT_SESSION:
 		DEBUGP(DNM, "LMT Event: \n");
 		break;
@@ -871,6 +948,23 @@
 	return abis_nm_sendmsg(bts, msg);
 }
 
+int abis_nm_sw_act_req_ack(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i1,
+			u_int8_t i2, u_int8_t i3, u_int8_t *attr, int att_len)
+{
+	struct abis_om_hdr *oh;
+	struct msgb *msg = nm_msgb_alloc();
+
+	oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+	fill_om_fom_hdr(oh, att_len, NM_MT_SW_ACT_REQ_ACK, obj_class, i1, i2, i3);
+	/* FIXME: don't send ARFCN list, hopping sequence, mAIO, ...*/
+	if (attr) {
+		u_int8_t *ptr = msgb_put(msg, att_len);
+		memcpy(ptr, attr, att_len);
+	}
+
+	return abis_nm_sendmsg(bts, msg);
+}
+
 int abis_nm_raw_msg(struct gsm_bts *bts, int len, u_int8_t *rawmsg)
 {
 	struct msgb *msg = nm_msgb_alloc();
@@ -898,6 +992,33 @@
 	return abis_nm_sendmsg(bts, msg);
 }
 
+/* Chapter 8.9.2 */
+int abis_nm_opstart(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0, u_int8_t i1, u_int8_t i2)
+{
+	struct abis_om_hdr *oh;
+	struct msgb *msg = nm_msgb_alloc();
+
+	oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+	fill_om_fom_hdr(oh, 0, NM_MT_OPSTART, obj_class, i0, i1, i2);
+
+	return abis_nm_sendmsg(bts, msg);
+}
+
+/* Chapter 8.8.5 */
+int abis_nm_chg_adm_state(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0,
+			  u_int8_t i1, u_int8_t i2, u_int8_t adm_state)
+{
+	struct abis_om_hdr *oh;
+	struct msgb *msg = nm_msgb_alloc();
+
+	oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+	fill_om_fom_hdr(oh, 2, NM_MT_CHG_ADM_STATE, obj_class, i0, i1, i2);
+	msgb_tv_put(msg, NM_ATT_ADM_STATE, adm_state);
+
+	return abis_nm_sendmsg(bts, msg);
+}
+
+
 int abis_nm_event_reports(struct gsm_bts *bts, int on)
 {
 	if (on == 0)