* rename NM_MT_BS11_LOGOFF to NM_MT_BS11_LMT_LOGOFF
* add more BS11 specific attributes
* define all valid BS11 PA power classes
* add callback function to software load
* introduce SWL load function for BS-11 style SWL file lists
* separate activation of software from loading of software
* add function to obtain BS-11 serial number

diff --git a/include/openbsc/abis_nm.h b/include/openbsc/abis_nm.h
index 1e59a44..db02ae6 100644
--- a/include/openbsc/abis_nm.h
+++ b/include/openbsc/abis_nm.h
@@ -196,8 +196,8 @@
 	NM_MT_BS11_RESTART_ACK,
 	NM_MT_BS11_DISCONNECT		= 0xe9,
 	NM_MT_BS11_DISCONNECT_ACK,
-	NM_MT_BS11_LOGOFF		= 0xec,
-	NM_MT_BS11_LOGOFF_ACK,
+	NM_MT_BS11_LMT_LOGOFF		= 0xec,
+	NM_MT_BS11_LMT_LOGOFF_ACK,
 	NM_MT_BS11_RECONNECT		= 0xf1,
 	NM_MT_BS11_RECONNECT_ACK,
 };
@@ -296,6 +296,11 @@
 	NM_ATT_MEAS_RES,
 	NM_ATT_MEAS_TYPE,
 
+	NM_ATT_BS11_ESN_FW_CODE_NO	= 0x4c,
+	NM_ATT_BS11_ESN_HW_CODE_NO	= 0x4f,
+
+	NM_ATT_BS11_ESN_PCB_SERIAL	= 0x55,
+
 	NM_ATT_BS11_ALL_TEST_CATG	= 0x60,
 	NM_ATT_BS11_BTSLS_HOPPING,
 	NM_ATT_BS11_CELL_ALLOC_NR,
@@ -330,7 +335,7 @@
 	NM_ATT_BS11_L1_REM_ALM_TYPE	= 0xb0,
 	NM_ATT_BS11_SW_LOAD_INTENDED	= 0xbb,
 	NM_ATT_BS11_SW_LOAD_SAFETY	= 0xbc,
-	NM_ATT_BS11_SW_LOAD_SSTORED	= 0xbd,
+	NM_ATT_BS11_SW_LOAD_STORED	= 0xbd,
 
 	NM_ATT_BS11_VENDOR_NAME		= 0xc1,
 	NM_ATT_BS11_LMT_LOGON_SESSION	= 0xc6,
@@ -388,17 +393,24 @@
 };
 
 enum abis_bs11_trx_power {
-	BS11_TRX_POWER_30mW	= 0x09,
+	BS11_TRX_POWER_GSM_2W	= 0x06,
+	BS11_TRX_POWER_GSM_250mW= 0x07,
+	BS11_TRX_POWER_GSM_80mW	= 0x08,
+	BS11_TRX_POWER_GSM_30mW	= 0x09,
+	BS11_TRX_POWER_DCS_3W	= 0x0a,
+	BS11_TRX_POWER_DCS_1W6	= 0x0b,
+	BS11_TRX_POWER_DCS_500mW= 0x0c,
+	BS11_TRX_POWER_DCS_160mW= 0x0d,
 };
 
 enum abis_bs11_state {
 	BS11_STATE_SOFTWARE_RQD		= 0x01,
-	BS11_STATE_NORMAL		= 0x03,
 	BS11_STATE_LOAD_SMU_SAFETY	= 0x21,
 	BS11_STATE_WARM_UP		= 0x51,
 	BS11_STATE_WAIT_MIN_CFG		= 0x62,
 	BS11_STATE_MAINTENANCE		= 0x72,
 	BS11_STATE_WAIT_MIN_CFG_2	= 0xA2,
+	BS11_STATE_NORMAL		= 0x03,
 };
 
 /* PUBLIC */
@@ -430,7 +442,10 @@
 int abis_nm_raw_msg(struct gsm_bts *bts, int len, u_int8_t *msg);
 int abis_nm_event_reports(struct gsm_bts *bts, int on);
 int abis_nm_reset_resource(struct gsm_bts *bts);
-int abis_nm_software_load(struct gsm_bts *bts, const char *fname, u_int8_t win);
+int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
+			  u_int8_t win_size, gsm_cbfn *cbfn, void *cb_data);
+int abis_nm_software_activate(struct gsm_bts *bts, const char *fname,
+			      gsm_cbfn *cbfn, void *cb_data);
 
 /* Siemens / BS-11 specific */
 int abis_nm_bs11_reset_resource(struct gsm_bts *bts);
@@ -446,5 +461,7 @@
 int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on);
 int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password);
 int abis_nm_bs11_get_state(struct gsm_bts *bts);
+int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname,
+			  u_int8_t win_size, gsm_cbfn *cbfn);
 
 #endif /* _NM_H */
diff --git a/src/abis_nm.c b/src/abis_nm.c
index 2cff36f..2591320 100644
--- a/src/abis_nm.c
+++ b/src/abis_nm.c
@@ -26,6 +26,8 @@
 #include <unistd.h>
 #include <stdio.h>
 #include <fcntl.h>
+#include <malloc.h>
+#include <libgen.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -291,6 +293,9 @@
 
 struct abis_nm_sw {
 	struct gsm_bts *bts;
+	gsm_cbfn *cbfn;
+	void *cb_data;
+
 	/* this will become part of the SW LOAD INITIATE */
 	u_int8_t obj_class;
 	u_int8_t obj_instance[3];
@@ -416,6 +421,7 @@
 
 	return abis_nm_sendmsg(sw->bts, msg);
 }
+
 /* Activate the specified software into the BTS */
 static int sw_activate(struct abis_nm_sw *sw)
 {
@@ -518,11 +524,19 @@
 		switch (foh->msg_type) {
 		case NM_MT_LOAD_INIT_ACK:
 			/* fill window with segments */
+			if (sw->cbfn)
+				sw->cbfn(GSM_HOOK_NM_SWLOAD,
+					 NM_MT_LOAD_INIT_ACK, mb,
+					 sw->cb_data, NULL);
 			rc = sw_fill_window(sw);
 			sw->state = SW_STATE_WAIT_SEGACK;
 			break;
 		case NM_MT_LOAD_INIT_NACK:
 			DEBUGP(DNM, "Software Load Init NACK\n");
+			if (sw->cbfn)
+				sw->cbfn(GSM_HOOK_NM_SWLOAD,
+					 NM_MT_LOAD_INIT_NACK, mb,
+					 sw->cb_data, NULL);
 			sw->state = SW_STATE_ERROR;
 			break;
 		}
@@ -547,26 +561,42 @@
 		switch (foh->msg_type) {
 		case NM_MT_LOAD_END_ACK:
 			sw_close_file(sw);
-			/* send activate request */
-			sw->state = SW_STATE_WAIT_ACTACK;
-			rc = sw_activate(sw);
+			DEBUGP(DNM, "Software Load End (BTS %u)\n",
+				sw->bts->nr);
+			sw->state = SW_STATE_NONE;
+			if (sw->cbfn)
+				sw->cbfn(GSM_HOOK_NM_SWLOAD,
+					 NM_MT_LOAD_END_ACK, mb,
+					 sw->cb_data, NULL);
 			break;
 		case NM_MT_LOAD_END_NACK:
 			DEBUGP(DNM, "Software Load End NACK\n");
 			sw->state = SW_STATE_ERROR;
+			if (sw->cbfn)
+				sw->cbfn(GSM_HOOK_NM_SWLOAD,
+					 NM_MT_LOAD_END_NACK, mb,
+					 sw->cb_data, NULL);
 			break;
 		}
 	case SW_STATE_WAIT_ACTACK:
 		switch (foh->msg_type) {
 		case NM_MT_ACTIVATE_SW_ACK:
 			/* we're done */
+			DEBUGP(DNM, "Activate Software DONE!\n");
 			sw->state = SW_STATE_NONE;
 			rc = 0;
-			DEBUGP(DMM, "DONE!\n");
+			if (sw->cbfn)
+				sw->cbfn(GSM_HOOK_NM_SWLOAD,
+					 NM_MT_ACTIVATE_SW_ACK, mb,
+					 sw->cb_data, NULL);
 			break;
 		case NM_MT_ACTIVATE_SW_NACK:
 			DEBUGP(DNM, "Activate Software NACK\n");
 			sw->state = SW_STATE_ERROR;
+			if (sw->cbfn)
+				sw->cbfn(GSM_HOOK_NM_SWLOAD,
+					 NM_MT_ACTIVATE_SW_NACK, mb,
+					 sw->cb_data, NULL);
 			break;
 		}
 	case SW_STATE_NONE:
@@ -583,11 +613,14 @@
 
 /* Load the specified software into the BTS */
 int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
-			  u_int8_t win_size)
+			  u_int8_t win_size, gsm_cbfn *cbfn, void *cb_data)
 {
 	struct abis_nm_sw *sw = &g_sw;
 	int rc;
 
+	DEBUGP(DNM, "Software Load (BTS %u, File \"%s\")\n",
+		bts->nr, fname);
+
 	if (sw->state != SW_STATE_NONE)
 		return -EBUSY;
 
@@ -598,6 +631,8 @@
 	sw->obj_instance[2] = 0xff;
 	sw->window_size = win_size;
 	sw->state = SW_STATE_WAIT_INITACK;
+	sw->cbfn = cbfn;
+	sw->cb_data = cb_data;
 
 	rc = sw_open_file(sw, fname);
 	if (rc < 0) {
@@ -624,6 +659,39 @@
 	return percent;
 }
 
+/* Activate the specified software into the BTS */
+int abis_nm_software_activate(struct gsm_bts *bts, const char *fname,
+			      gsm_cbfn *cbfn, void *cb_data)
+{
+	struct abis_nm_sw *sw = &g_sw;
+	int rc;
+
+	DEBUGP(DNM, "Activating Software (BTS %u, File \"%s\")\n",
+		bts->nr, fname);
+
+	if (sw->state != SW_STATE_NONE)
+		return -EBUSY;
+
+	sw->bts = bts;
+	sw->obj_class = NM_OC_SITE_MANAGER;
+	sw->obj_instance[0] = 0xff;
+	sw->obj_instance[1] = 0xff;
+	sw->obj_instance[2] = 0xff;
+	sw->state = SW_STATE_WAIT_ACTACK;
+	sw->cbfn = cbfn;
+	sw->cb_data = cb_data;
+
+	/* Open the file in order to fill some sw struct members */
+	rc = sw_open_file(sw, fname);
+	if (rc < 0) {
+		sw->state = SW_STATE_NONE;
+		return rc;
+	}
+	sw_close_file(sw);
+
+	return sw_activate(sw);
+}
+
 static void fill_nm_channel(struct abis_nm_channel *ch, u_int8_t bts_port,
 		       u_int8_t ts_nr, u_int8_t subslot_nr)
 {
@@ -891,7 +959,7 @@
 		msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_NAME,
 			     sizeof(bs11_logon_c9), bs11_logon_c9);
 	} else {
-		fill_om_fom_hdr(oh, 0, NM_MT_BS11_LOGOFF,
+		fill_om_fom_hdr(oh, 0, NM_MT_BS11_LMT_LOGOFF,
 				NM_OC_BS11_A3, 0xff, 0xff, 0xff);
 	}
 	
@@ -919,3 +987,198 @@
 {
 	return __simple_cmd(bts, NM_MT_BS11_GET_STATE);
 }
+
+/* BS11 SWL */
+
+struct abis_nm_bs11_sw {
+	struct gsm_bts *bts;
+	char swl_fname[PATH_MAX];
+	u_int8_t win_size;
+	struct llist_head file_list;
+	gsm_cbfn *user_cb;	/* specified by the user */
+};
+static struct abis_nm_bs11_sw _g_bs11_sw, *g_bs11_sw = &_g_bs11_sw;
+
+struct file_list_entry {
+	struct llist_head list;
+	char fname[PATH_MAX];
+};
+
+struct file_list_entry *fl_dequeue(struct llist_head *queue)
+{
+	struct llist_head *lh;
+
+	if (llist_empty(queue))
+		return NULL;
+
+	lh = queue->next;
+	llist_del(lh);
+	
+	return llist_entry(lh, struct file_list_entry, list);
+}
+
+static int bs11_read_swl_file(struct abis_nm_bs11_sw *bs11_sw)
+{
+	char linebuf[255];
+	struct llist_head *lh, *lh2;
+	FILE *swl;
+	int rc = 0;
+
+	swl = fopen(bs11_sw->swl_fname, "r");
+	if (!swl)
+		return -ENODEV;
+
+	/* zero the stale file list, if any */
+	llist_for_each_safe(lh, lh2, &bs11_sw->file_list) {
+		llist_del(lh);
+		free(lh);
+	}
+
+	while (fgets(linebuf, sizeof(linebuf), swl)) {
+		char file_id[12+1];
+		char file_version[80+1];
+		struct file_list_entry *fle;
+		static char dir[PATH_MAX];
+
+		if (strlen(linebuf) < 4)
+			continue;
+		printf("linebuf='%s'\n", linebuf);
+		rc = sscanf(linebuf+4, "%12s:%80s\r\n", file_id, file_version);
+		if (rc < 0) {
+			perror("ERR parsing SWL file");
+			rc = -EINVAL;
+			goto out;
+		}
+		if (rc < 2)
+			continue;
+
+		fle = malloc(sizeof(*fle));
+		if (!fle) {
+			rc = -ENOMEM;
+			goto out;
+		}
+		memset(fle, 0, sizeof(*fle));
+
+		/* construct new filename */
+		strncpy(dir, bs11_sw->swl_fname, sizeof(dir));
+		strncat(fle->fname, dirname(dir), sizeof(fle->fname) - 1);
+		strcat(fle->fname, "/");
+		strncat(fle->fname, file_id, sizeof(fle->fname) - 1 -strlen(fle->fname));
+		printf("fname='%s'\n", fle->fname);
+		
+		llist_add_tail(&fle->list, &bs11_sw->file_list);
+	}
+
+out:
+	fclose(swl);
+	return rc;
+}
+
+/* bs11 swload specific callback, passed to abis_nm core swload */
+static int bs11_swload_cbfn(unsigned int hook, unsigned int event,
+			    struct msgb *msg, void *data, void *param)
+{
+	struct abis_nm_bs11_sw *bs11_sw = data;
+	struct file_list_entry *fle;
+	int rc = 0;
+
+	printf("bs11_swload_cbfn(%u, %u, %p, %p, %p)\n", hook, event, msg, data, param);
+
+	switch (event) {
+	case NM_MT_LOAD_END_ACK:
+		fle = fl_dequeue(&bs11_sw->file_list);
+		if (fle) {
+			/* start download the next file of our file list */
+			rc = abis_nm_software_load(bs11_sw->bts, fle->fname,
+						   bs11_sw->win_size,
+						   &bs11_swload_cbfn, bs11_sw);
+			free(fle);
+		} else {
+			/* activate the SWL */
+			rc = abis_nm_software_activate(bs11_sw->bts,
+							bs11_sw->swl_fname,
+							bs11_swload_cbfn,
+							bs11_sw);
+		}
+		break;
+	case NM_MT_LOAD_END_NACK:
+	case NM_MT_LOAD_INIT_ACK:
+	case NM_MT_LOAD_INIT_NACK:
+	case NM_MT_ACTIVATE_SW_NACK:
+	case NM_MT_ACTIVATE_SW_ACK:
+	default:
+		/* fallthrough to the user callback */
+		rc = bs11_sw->user_cb(hook, event, msg, NULL, NULL);
+		break;
+	}
+
+	return rc;
+}
+
+/* Siemens provides a SWL file that is a mere listing of all the other
+ * files that are part of a software release.  We need to upload first
+ * the list file, and then each file that is listed in the list file */
+int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname,
+			  u_int8_t win_size, gsm_cbfn *cbfn)
+{
+	struct abis_nm_bs11_sw *bs11_sw = g_bs11_sw;
+	struct file_list_entry *fle;
+	int rc = 0;
+
+	INIT_LLIST_HEAD(&bs11_sw->file_list);
+	bs11_sw->bts = bts;
+	bs11_sw->win_size = win_size;
+	bs11_sw->user_cb = cbfn;
+
+	strncpy(bs11_sw->swl_fname, fname, sizeof(bs11_sw->swl_fname));
+	rc = bs11_read_swl_file(bs11_sw);
+	if (rc < 0)
+		return rc;
+
+	/* dequeue next item in file list */
+	fle = fl_dequeue(&bs11_sw->file_list);
+	if (!fle)
+		return -EINVAL;
+
+	/* start download the next file of our file list */
+	rc = abis_nm_software_load(bts, fle->fname, win_size,
+				   bs11_swload_cbfn, bs11_sw);
+	free(fle);
+	return rc;
+}
+
+static u_int8_t req_attr_btse[] = {
+	NM_ATT_ADM_STATE, NM_ATT_BS11_LMT_LOGON_SESSION,
+	NM_ATT_BS11_LMT_LOGIN_TIME, NM_ATT_BS11_LMT_USER_ACC_LEV,
+	NM_ATT_BS11_LMT_USER_NAME,
+
+	0xaf, NM_ATT_BS11_RX_OFFSET, NM_ATT_BS11_VENDOR_NAME,
+
+	NM_ATT_BS11_SW_LOAD_INTENDED, NM_ATT_BS11_SW_LOAD_SAFETY,
+
+	NM_ATT_BS11_SW_LOAD_STORED };
+
+static u_int8_t req_attr_btsm[] = {
+	NM_ATT_ABIS_CHANNEL, NM_ATT_TEI, NM_ATT_BS11_ABIS_EXT_TIME,
+	NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xce, NM_ATT_FILE_ID,
+	NM_ATT_FILE_VERSION, NM_ATT_OPER_STATE, 0xe8, NM_ATT_BS11_ALL_TEST_CATG,
+	NM_ATT_SW_DESCR, NM_ATT_GET_ARI };
+	
+static u_int8_t req_attr[] = { 
+	NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xa8, NM_ATT_OPER_STATE,
+	0xd5, 0xa1, NM_ATT_BS11_ESN_FW_CODE_NO, NM_ATT_BS11_ESN_HW_CODE_NO,
+	0x42, NM_ATT_BS11_ESN_PCB_SERIAL };
+
+int abis_nm_bs11_get_serno(struct gsm_bts *bts)
+{
+	struct abis_om_hdr *oh;
+	struct msgb *msg = nm_msgb_alloc();
+
+	oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+	/* SiemensHW CCTRL object */
+	fill_om_fom_hdr(oh, 2+sizeof(req_attr), NM_MT_GET_ATTR, NM_OC_BS11,
+			0x03, 0x00, 0x00);
+	msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(req_attr), req_attr);
+
+	return abis_nm_sendmsg(bts, msg);
+}
diff --git a/src/bs11_config.c b/src/bs11_config.c
index 457e90f..6166c84 100644
--- a/src/bs11_config.c
+++ b/src/bs11_config.c
@@ -52,16 +52,16 @@
 static enum bs11cfg_state bs11cfg_state = STATE_NONE;
 
 static const u_int8_t obj_li_attr[] = { 
-	0xa0, 0x09, 0x00,
-	0xab, 0x00, 
-	0xac, 0x00,
+	NM_ATT_BS11_BIT_ERR_THESH, 0x09, 0x00,
+	NM_ATT_BS11_L1_PROT_TYPE, 0x00, 
+	NM_ATT_BS11_LINE_CFG, 0x00,
 };
 static const u_int8_t obj_bbsig0_attr[] = {
-	0x3d, 0x02, 0x00, 0x00,
-	0x3f, 0x01, 0x00,
+	NM_ATT_BS11_RSSI_OFFS, 0x02, 0x00, 0x00,
+	NM_ATT_BS11_DIVERSITY, 0x01, 0x00,
 };
 static const u_int8_t obj_pa0_attr[] = {
-	NM_ATT_BS11_TXPWR, 0x01, BS11_TRX_POWER_30mW,
+	NM_ATT_BS11_TXPWR, 0x01, BS11_TRX_POWER_GSM_30mW,
 };
 static const char *trx1_password = "1111111111";
 #define TEI_OML	25
@@ -84,7 +84,7 @@
 /* create all objects for an initial configuration */
 static int create_objects(struct gsm_bts *bts, int trx1)
 {
-	abis_nm_bs11_factory_logon(bts, 1);
+	//abis_nm_bs11_factory_logon(bts, 1);
 	abis_nm_bs11_create_object(bts, BS11_OBJ_LI, 0, sizeof(obj_li_attr),
 				   obj_li_attr);
 	abis_nm_bs11_create_object(bts, BS11_OBJ_GPSU, 0, 0, NULL);
@@ -118,12 +118,12 @@
 	abis_nm_bs11_conn_oml(bts, 0, 1, 0xff);
 	abis_nm_bs11_set_oml_tei(bts, TEI_OML);
 
-	abis_nm_bs11_set_trx_power(&bts->trx[0], BS11_TRX_POWER_30mW);
+	abis_nm_bs11_set_trx_power(&bts->trx[0], BS11_TRX_POWER_GSM_30mW);
 
 	if (trx1)
-		abis_nm_bs11_set_trx_power(&bts->trx[1], BS11_TRX_POWER_30mW);
+		abis_nm_bs11_set_trx_power(&bts->trx[1], BS11_TRX_POWER_GSM_30mW);
 
-	abis_nm_bs11_factory_logon(bts, 0);
+	//abis_nm_bs11_factory_logon(bts, 0);
 	
 	return 0;
 }
@@ -172,6 +172,7 @@
 	return 0;
 }
 
+/* select.c callback in case we can write to the RS232 */
 static int handle_ser_write(struct bsc_fd *bfd)
 {
 	struct serial_handle *sh = bfd->data;
@@ -203,6 +204,7 @@
 
 #define SERIAL_ALLOC_SIZE	300
 
+/* select.c callback in case we can read from the RS232 */
 static int handle_ser_read(struct bsc_fd *bfd)
 {
 	struct serial_handle *sh = bfd->data;
@@ -265,6 +267,7 @@
 	return rc;
 }
 
+/* select.c callback */
 static int serial_fd_cb(struct bsc_fd *bfd, unsigned int what)
 {
 	int rc = 0;
@@ -296,7 +299,44 @@
 	return 0;
 }
 
+/* callback function passed to the ABIS OML code */
+static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *msg,
+		       void *data, void *param)
+{
+	if (hook != GSM_HOOK_NM_SWLOAD)
+		return 0;
 
+	switch (event) {
+	case NM_MT_LOAD_INIT_ACK:
+		fprintf(stdout, "Software Load Initiate ACK\n");
+		break;
+	case NM_MT_LOAD_INIT_NACK:
+		fprintf(stderr, "ERROR: Software Load Initiate NACK\n");
+		exit(5);
+		break;
+	case NM_MT_LOAD_END_ACK:
+		/* FIXME: activate in case we want to */
+		if (data)
+			abis_nm_software_activate(g_bts, fname_safety,
+						  swload_cbfn, g_bts);
+		break;
+	case NM_MT_LOAD_END_NACK:
+		fprintf(stderr, "ERROR: Software Load End NACK\n");
+		exit(3);
+		break;
+	case NM_MT_ACTIVATE_SW_NACK:
+		fprintf(stderr, "ERROR: Activate Software NACK\n");
+		exit(4);
+		break;
+	case NM_MT_ACTIVATE_SW_ACK:
+		bs11cfg_state = STATE_NONE;
+		
+		break;
+	}
+	return 0;
+}
+
+/* handle a response from the BTS to a GET STATE command */
 static int handle_state_resp(u_int8_t state)
 {
 	int rc = 0;
@@ -315,10 +355,13 @@
 	case BS11_STATE_SOFTWARE_RQD:
 		printf("Software required...\n");
 		bs11cfg_state = STATE_SWLOAD;
-		/* send safety load */
+		/* send safety load. Use g_bts as private 'param'
+		 * argument, so our swload_cbfn can distinguish
+		 * a safety load from a regular software */
 		if (file_is_readable(fname_safety))
 			rc = abis_nm_software_load(g_bts, fname_safety,
-						   win_size);
+						   win_size, swload_cbfn,
+						   g_bts);
 		else
 			fprintf(stderr, "No valid Safety Load file \"%s\"\n",
 				fname_safety);
@@ -334,8 +377,8 @@
 		bs11cfg_state = STATE_SWLOAD;
 		/* send software (FIXME: over A-bis?) */
 		if (file_is_readable(fname_software))
-			rc = abis_nm_software_load(g_bts, fname_software,
-						   win_size);
+			rc = abis_nm_bs11_load_swl(g_bts, fname_software,
+						   win_size, swload_cbfn);
 		else
 			fprintf(stderr, "No valid Software file \"%s\"\n",
 				fname_software);
@@ -351,6 +394,7 @@
 	return rc;
 }
 
+/* handle a fully-received message/packet from the RS232 port */
 static int handle_serial_msg(struct msgb *rx_msg)
 {
 	struct abis_om_hdr *oh;
@@ -379,6 +423,9 @@
 			bs11cfg_state = STATE_LOGON_ACK;
 		rc = 0;
 		break;
+	case NM_MT_BS11_LMT_LOGOFF_ACK:
+		exit(0);
+		break;
 	case NM_MT_BS11_GET_STATE_ACK:
 		rc = handle_state_resp(foh->data[2]);
 		break;
@@ -409,19 +456,19 @@
 static void print_banner(void)
 {
 	printf("bs11_config (C) 2009 by Harald Welte and Dieter Spaar\n");
-	printf("THIS SOFTWARE IS FREE SOFTWARE WIH NO WARRANTY\n\n");
+	printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
 }
 
 static void print_help(void)
 {
 	printf("Supported arguments:\n");
-	printf("\t--help\t\t\t-h\tPrint this help text\n");
-	printf("\t--port /dev/ttyXXX\t-p\tSpecify serial port\n");
-	printf("\t--with-trx1\t\t-t\tAssume the BS-11 has 2 TRX\n");
-	printf("\t--software file\t\t-s\tSpecify Software file\n");
-	printf("\t--safety file\t\t-S\tSpecify Safety Load file\n");
-	printf("\t--delay file\t\t-d\tSpecify delay\n");
-	printf("\t--win-size num\t\t-w\tSpecify Window Size\n");
+	printf("\t-h --help\t\t\tPrint this help text\n");
+	printf("\t-p --port </dev/ttyXXX>\t\tSpecify serial port\n");
+	printf("\t-t --with-trx1\t\t\tAssume the BS-11 has 2 TRX\n");
+	printf("\t-s --software <file>\t\tSpecify Software file\n");
+	printf("\t-S --safety <file>\t\tSpecify Safety Load file\n");
+	printf("\t-d --delay <file>\t\tSpecify delay\n");
+	printf("\t-w --win-size <num>\t\tSpecify Window Size\n");
 }
 
 static void handle_options(int argc, char **argv)
@@ -479,7 +526,7 @@
 	fprintf(stdout, "signal %u received\n", signal);
 
 	switch (signal) {
-	case SIGABRT:
+	case SIGINT:
 		abis_nm_bs11_factory_logon(g_bts, 0);
 		break;
 	}
@@ -538,9 +585,10 @@
 		exit(1);
 	}
 
-	signal(SIGABRT, &signal_handler);
+	signal(SIGINT, &signal_handler);
 
 	abis_nm_bs11_factory_logon(g_bts, 1);
+	//abis_nm_bs11_get_serno(g_bts);
 
 	while (1) {
 		bsc_select_main();