Add support for ip.access RSL link on port 3003

diff --git a/src/abis_nm.c b/src/abis_nm.c
index 159424a..e3346a6 100644
--- a/src/abis_nm.c
+++ b/src/abis_nm.c
@@ -537,16 +537,26 @@
 {
 	struct abis_om_hdr *oh = msgb_l2(mb);
 	struct abis_om_fom_hdr *foh = msgb_l3(mb);
+	int nack = 0;
 	int ret;
 
-	DEBUGP(DNM, "Software Activate Request, ACKing and Activating\n");
+	DEBUGP(DNM, "Software Activate Request ");
 
-	ret =  abis_nm_sw_act_req_ack(mb->trx->bts, foh->obj_class,
+	if (foh->obj_class >= 0xf0 && foh->obj_class <= 0xf3) {
+		DEBUGPC(DNM, "NACKing for GPRS obj_class 0x%02x\n", foh->obj_class);
+		nack = 1;
+	} else
+		DEBUGPC(DNM, "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->obj_inst.ts_nr, nack,
 				      foh->data, oh->length-sizeof(*foh));
 
+	if (nack)
+		return ret;
+
 	/* FIXME: properly parse attributes */
 	return ipacc_sw_activate(mb->trx->bts, foh->obj_class,
 				 foh->obj_inst.bts_nr,
@@ -1290,18 +1300,27 @@
 }
 
 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)
+			u_int8_t i2, u_int8_t i3, int nack, u_int8_t *attr, int att_len)
 {
 	struct abis_om_hdr *oh;
 	struct msgb *msg = nm_msgb_alloc();
+	u_int8_t msgtype = NM_MT_SW_ACT_REQ_ACK;
+	u_int8_t len = att_len;
+
+	if (nack) {
+		len += 2;
+		msgtype = NM_MT_SW_ACT_REQ_NACK;
+	}
 
 	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, ...*/
+	fill_om_fom_hdr(oh, att_len, msgtype, obj_class, i1, i2, i3);
+
 	if (attr) {
 		u_int8_t *ptr = msgb_put(msg, att_len);
 		memcpy(ptr, attr, att_len);
 	}
+	if (nack)
+		msgb_tv_put(msg, NM_ATT_NACK_CAUSES, NM_NACK_OBJCLASS_NOTSUPP);
 
 	return abis_nm_sendmsg(bts, msg);
 }
@@ -1779,3 +1798,43 @@
 
 	return abis_nm_sendmsg(bts, msg);
 }
+
+/* ip.access nanoBTS specific commands */
+
+static const char ipaccess_magic[] = "com.ipaccess";
+
+int abis_nm_ipaccess_msg(struct gsm_bts *bts, u_int8_t msg_type,
+			 u_int8_t obj_class, u_int8_t bts_nr,
+			 u_int8_t trx_nr, u_int8_t ts_nr,
+			 u_int8_t *attr, int attr_len)
+{
+	struct msgb *msg = nm_msgb_alloc();
+	struct abis_om_hdr *oh;
+	struct abis_om_fom_hdr *foh;
+	u_int8_t *data;
+
+	/* construct the 12.21 OM header, observe the erroneous length */
+	oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh));
+	fill_om_hdr(oh, sizeof(*foh) + attr_len);
+	oh->mdisc = ABIS_OM_MDISC_MANUF;
+
+	/* add the ip.access magic */
+	data = msgb_put(msg, sizeof(ipaccess_magic)+1);
+	*data++ = sizeof(ipaccess_magic);
+	memcpy(data, ipaccess_magic, sizeof(ipaccess_magic));
+
+	/* fill the 12.21 FOM header */
+	foh = (struct abis_om_fom_hdr *) msgb_put(msg, sizeof(*foh));
+	foh->msg_type = msg_type;
+	foh->obj_class = obj_class;
+	foh->obj_inst.bts_nr = bts_nr;
+	foh->obj_inst.trx_nr = trx_nr;
+	foh->obj_inst.ts_nr = ts_nr;
+
+	if (attr && attr_len) {
+		data = msgb_put(msg, attr_len);
+		memcpy(data, attr, attr_len);
+	}
+
+	return abis_nm_sendmsg(bts, msg);
+}
diff --git a/src/bsc_hack.c b/src/bsc_hack.c
index f3be6c2..5b6e4a3 100644
--- a/src/bsc_hack.c
+++ b/src/bsc_hack.c
@@ -302,6 +302,11 @@
 	NM_ATT_ARFCN_LIST, 0x00, 0x02, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff,
 };
 
+static unsigned char nanobts_attr_e0[] = {
+	0x85, 0x00,
+	0x81, 0x0b, 0xbb,	/* TCP PORT for RSL */
+};
+
 int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
 		   struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
 {
@@ -362,6 +367,9 @@
 		case NM_OC_BASEB_TRANSC:
 			trx = container_of(obj, struct gsm_bts_trx, bb_transc);
 			if (new_state->availability == 5) {
+				abis_nm_ipaccess_msg(trx->bts, 0xe0, NM_OC_BASEB_TRANSC,
+						     trx->bts->nr, trx->nr, 0xff,
+						     nanobts_attr_e0, sizeof(nanobts_attr_e0));
 				abis_nm_opstart(trx->bts, NM_OC_BASEB_TRANSC, 
 						trx->bts->nr, trx->nr, 0xff);
 				abis_nm_chg_adm_state(trx->bts, NM_OC_BASEB_TRANSC, 
diff --git a/src/e1_config.c b/src/e1_config.c
index 9863c9e..8d9ac66 100644
--- a/src/e1_config.c
+++ b/src/e1_config.c
@@ -71,7 +71,7 @@
 int ia_config(struct gsm_bts *bts)
 {
 	struct e1inp_line *line;
-	struct e1inp_ts *sign_ts;
+	struct e1inp_ts *sign_ts, *rsl_ts;
 	struct e1inp_sign_link *oml_link, *rsl_link;
 
 	line = malloc(sizeof(*line));
@@ -81,12 +81,14 @@
 
 	/* create E1 timeslots for signalling and TRAU frames */
 	e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN);
+	e1inp_ts_config(&line->ts[2-1], line, E1INP_TS_TYPE_SIGN);
 
 	/* create signalling links for TS1 */
 	sign_ts = &line->ts[1-1];
+	rsl_ts = &line->ts[2-1];
 	oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML,
 					  bts->c0, 0, 0xff);
-	rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL,
+	rsl_link = e1inp_sign_link_create(rsl_ts, E1INP_SIGN_RSL,
 					  bts->c0, 0, 0);
 
 	/* create back-links from bts/trx */
diff --git a/src/input/ipaccess.c b/src/input/ipaccess.c
index 2258267..bfccbc7 100644
--- a/src/input/ipaccess.c
+++ b/src/input/ipaccess.c
@@ -44,6 +44,7 @@
 /* data structure for one E1 interface with A-bis */
 struct ia_e1_handle {
 	struct bsc_fd listen_fd;
+	struct bsc_fd rsl_listen_fd;
 };
 
 #define TS1_ALLOC_SIZE	300
@@ -98,6 +99,7 @@
 
 /* FIXME: this is per BTS */
 static int oml_up = 0;
+static int rsl_up = 0;
 
 static int handle_ts1_read(struct bsc_fd *bfd)
 {
@@ -134,8 +136,8 @@
 	ret = recv(bfd->fd, msg->l2h, hh->len, 0);
 	if (ret < hh->len) {
 		fprintf(stderr, "short read!\n");
-		//msgb_free(msg);
-		//return -EIO;
+		msgb_free(msg);
+		return -EIO;
 	}
 	msgb_put(msg, ret);
 
@@ -143,7 +145,7 @@
 		return ipaccess_rcvmsg(msg, bfd->fd);
 
 	if (debug_mask & DMI) { 
-		fprintf(stdout, "RX: ");
+		fprintf(stdout, "RX %u: ", ts_nr);
 		hexdump(msgb_l2(msg), ret);
 	}
 
@@ -157,6 +159,10 @@
 
 	switch (hh->proto) {
 	case PROTO_RSL:
+		if (!rsl_up) {
+			e1inp_event(e1i_ts, EVT_E1_TEI_UP, 0, PROTO_RSL);
+			rsl_up = 1;
+		}
 		ret = abis_rsl_rcvmsg(msg);
 		break;
 	case PROTO_OML:
@@ -212,7 +218,7 @@
 	}
 
 	if (debug_mask & DMI) {
-		fprintf(stdout, "TX: ");
+		fprintf(stdout, "TX %u: ", ts_nr);
 		hexdump(l2_data, hh->len);
 	}
 
@@ -294,7 +300,7 @@
 		socklen_t sa_len = sizeof(sa);
 
 		if (bfd->fd) {
-			printf("dumping old fd\n");
+			printf("dumping old OML fd\n");
 			if (bfd->fd != -1) {
 				bsc_unregister_fd(bfd);
 				close(bfd->fd);
@@ -305,7 +311,7 @@
 			perror("accept");
 			return bfd->fd;
 		}
-		printf("accept()ed new RSL/OML fd\n");
+		printf("accept()ed new OML fd\n");
 		bfd->data = line;
 		bfd->priv_nr = 1;
 		bfd->cb = ipaccess_fd_cb;
@@ -319,11 +325,88 @@
 	return 0;
 }
 
-int ipaccess_setup(struct e1inp_line *line)
+static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
+{
+	struct e1inp_line *line = listen_bfd->data;
+	int ret;
+
+	if (what & BSC_FD_READ) {
+		int idx = 1;
+		struct e1inp_ts *e1i_ts = &line->ts[idx];
+		struct bsc_fd *bfd = &e1i_ts->driver.ipaccess.fd;
+		struct sockaddr_in sa;
+		socklen_t sa_len = sizeof(sa);
+
+		if (bfd->fd) {
+			printf("dumping old RSL fd\n");
+			if (bfd->fd != -1) {
+				bsc_unregister_fd(bfd);
+				close(bfd->fd);
+			}
+		}
+		bfd->fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
+		if (bfd->fd < 0) {
+			perror("accept");
+			return bfd->fd;
+		}
+		printf("accept()ed new RSL fd\n");
+		bfd->data = line;
+		bfd->priv_nr = 2;
+		bfd->cb = ipaccess_fd_cb;
+		bfd->when = BSC_FD_READ;
+		ret = bsc_register_fd(bfd);
+		if (ret < 0) {
+			fprintf(stderr, "could not register FD\n");
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static int make_sock(struct bsc_fd *bfd, u_int16_t port,
+		     struct e1inp_line *line,
+		     int (*cb)(struct bsc_fd *fd, unsigned int what))
 {
 	struct sockaddr_in addr;
+	int ret, on = 1;
+	
+	bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	bfd->cb = cb;
+	bfd->when = BSC_FD_READ;
+	bfd->data = line;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(port);
+	addr.sin_addr.s_addr = INADDR_ANY;
+
+	setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+	ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
+	if (ret < 0) {
+		fprintf(stderr, "could not bind l2 socket %s\n",
+			strerror(errno));
+		return -EIO;
+	}
+
+	ret = listen(bfd->fd, 1);
+	if (ret < 0) {
+		perror("listen");
+		return ret;
+	}
+	
+	ret = bsc_register_fd(bfd);
+	if (ret < 0) {
+		perror("register_listen_fd");
+		return ret;
+	}
+	return 0;
+}
+
+int ipaccess_setup(struct e1inp_line *line)
+{
 	struct ia_e1_handle *e1h;
-	int sk, ret, on = 1;
+	int ret;
 
 	/* register the driver with the core */
 	/* FIXME: do this in the plugin initializer function */
@@ -339,36 +422,11 @@
 	line->driver = &ipaccess_driver;
 	line->driver_data = e1h;
 
-	e1h->listen_fd.fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-	e1h->listen_fd.cb = listen_fd_cb;
-	e1h->listen_fd.when = BSC_FD_READ;
-	e1h->listen_fd.data = line;
+	/* Listen for OML connections */
+	ret = make_sock(&e1h->listen_fd, 3002, line, listen_fd_cb);
 
-	memset(&addr, 0, sizeof(addr));
-	addr.sin_family = AF_INET;
-	addr.sin_port = htons(3002);
-	addr.sin_addr.s_addr = INADDR_ANY;
-
-	setsockopt(e1h->listen_fd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-
-	ret = bind(e1h->listen_fd.fd, (struct sockaddr *) &addr, sizeof(addr));
-	if (ret < 0) {
-		fprintf(stderr, "could not bind l2 socket %s\n",
-			strerror(errno));
-		return -EIO;
-	}
-
-	ret = listen(e1h->listen_fd.fd, 1);
-	if (ret < 0) {
-		perror("listen");
-		return ret;
-	}
-	
-	ret = bsc_register_fd(&e1h->listen_fd);
-	if (ret < 0) {
-		perror("register_listen_fd");
-		return ret;
-	}
+	/* Listen for RSL connections */
+	ret = make_sock(&e1h->rsl_listen_fd, 3003, line, rsl_listen_fd_cb);
 
 	ret = ia_e1_setup(line);
 	if (ret)