e1input: rework generic (virtual and real) E1 line operations

struct e1inp_line_ops {
       int     (*sign_link_up)(struct msgb *msg, struct e1inp_line *line);
       int     (*sign_link)(struct msgb *msg, struct e1inp_sign_link *link);
       int     (*error)(struct msgb *msg, int error);
};

The description of the operations is the following:

* sign_link_up is invoked if the signalling link becomes up. In A-bis
over IP, we have to wait until the other peer identifies itself as
a BTS/BSC device, then this function is invoked. This function is not
used by ISDN drivers, the signalling link can be set up just after
the line is created.

* sign_link is called if we receive OML/RSL message. This function
is used both by ISDN and A-bis over IP drivers.

* error is called if we receive a malformed message. It is used both
by ISDN and A-bis over IP drivers.
diff --git a/src/input/dahdi.c b/src/input/dahdi.c
index ba8ce02..8e5e635 100644
--- a/src/input/dahdi.c
+++ b/src/input/dahdi.c
@@ -121,8 +121,11 @@
 	DEBUGP(DMI, "<= len = %d, sapi(%d) tei(%d)", ret, sapi, tei);
 
 	idata = lapd_receive(e1i_ts->driver.dahdi.lapd, msg->data, msg->len, &ilen, &prim);
-	if (!idata && prim == 0)
+	if (!idata && prim == 0) {
+		if (line->ops.error)
+			line->ops.error(NULL, -EBADMSG);
 		return -EIO;
+	}
 
 	msgb_pull(msg, 2);
 
@@ -149,7 +152,8 @@
 		ret = e1inp_rx_ts(e1i_ts, msg, tei, sapi);
 		break;
 	default:
-		printf("ERROR: unknown prim\n");
+		if (line->ops.error)
+			line->ops.error(NULL, -EBADMSG);
 		break;
 	}
 
diff --git a/src/input/hsl.c b/src/input/hsl.c
index b7c7897..97475cf 100644
--- a/src/input/hsl.c
+++ b/src/input/hsl.c
@@ -77,13 +77,15 @@
 	struct e1inp_line *line = bfd->data;
 	unsigned int ts_nr = bfd->priv_nr;
 	struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
+	struct e1inp_sign_link *link;
+	struct ipaccess_head *hh;
 	struct msgb *msg;
 	int ret = 0, error;
 
 	msg = ipaccess_read_msg(bfd, &error);
 	if (!msg) {
-		if (e1i_ts->line->rx_err)
-			e1i_ts->line->rx_err(error);
+		if (e1i_ts->line->ops.error)
+			e1i_ts->line->ops.error(NULL, error);
 		if (error == 0) {
 			osmo_fd_unregister(bfd);
 			close(bfd->fd);
@@ -93,9 +95,53 @@
 	}
 	DEBUGP(DMI, "RX %u: %s\n", ts_nr, osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
 
-	/* XXX better use e1inp_rx_ts. */
-	if (e1i_ts->line->rx)
-		e1i_ts->line->rx(msg, e1i_ts);
+	hh = (struct ipaccess_head *) msg->data;
+	if (hh->proto == HSL_PROTO_DEBUG) {
+		LOGP(DINP, LOGL_NOTICE, "HSL debug: %s\n", msg->data + sizeof(*hh));
+		msgb_free(msg);
+		return ret;
+	}
+
+	/* HSL proprietary RSL extension */
+	if (hh->proto == 0 && (msg->l2h[0] == 0x81 || msg->l2h[0] == 0x80)) {
+		if (!line->ops.sign_link_up) {
+			LOGP(DINP, LOGL_ERROR, "Fix your application, no "
+				"action set if the signalling link "
+				"becomes ready.\n");
+			return -EINVAL;
+		}
+		ret = line->ops.sign_link_up(msg, line);
+		if (ret < 0) {
+			/* FIXME: close connection */
+			if (line->ops.error)
+				line->ops.error(msg, -EBADMSG);
+			return ret;
+		} else if (ret == 1)
+			return 0;
+		/* else: continue... */
+	}
+
+#ifdef HSL_SR_1_0
+	/* HSL for whatever reason chose to use 0x81 instead of 0x80 for FOM */
+	if (hh->proto == 255 && msg->l2h[0] == (ABIS_OM_MDISC_FOM | 0x01))
+		msg->l2h[0] = ABIS_OM_MDISC_FOM;
+#endif
+	link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0);
+	if (!link) {
+		LOGP(DINP, LOGL_ERROR, "no matching signalling link for "
+			"hh->proto=0x%02x\n", hh->proto);
+		msgb_free(msg);
+		return -EIO;
+	}
+	msg->dst = link;
+
+	/* XXX: better use e1inp_ts_rx? */
+	if (!e1i_ts->line->ops.sign_link) {
+		LOGP(DINP, LOGL_ERROR, "Fix your application, "
+			"no action set for signalling messages.\n");
+		return -ENOENT;
+	}
+	e1i_ts->line->ops.sign_link(msg, link);
 
 	return ret;
 }
diff --git a/src/input/ipaccess.c b/src/input/ipaccess.c
index ef773ba..92819ac 100644
--- a/src/input/ipaccess.c
+++ b/src/input/ipaccess.c
@@ -170,18 +170,65 @@
 	return msg;
 }
 
+/* base handling of the ip.access protocol */
+int ipaccess_rcvmsg_base(struct msgb *msg,
+			 struct osmo_fd *bfd)
+{
+	uint8_t msg_type = *(msg->l2h);
+	int ret = 0;
+
+	switch (msg_type) {
+	case IPAC_MSGT_PING:
+		ret = ipaccess_send_pong(bfd->fd);
+		break;
+	case IPAC_MSGT_PONG:
+		DEBUGP(DMI, "PONG!\n");
+		break;
+	case IPAC_MSGT_ID_ACK:
+		DEBUGP(DMI, "ID_ACK? -> ACK!\n");
+		ret = ipaccess_send_id_ack(bfd->fd);
+		break;
+	}
+	return 0;
+}
+
+static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
+			   struct osmo_fd *bfd)
+{
+	uint8_t msg_type = *(msg->l2h);
+
+	/* handle base messages */
+	ipaccess_rcvmsg_base(msg, bfd);
+
+	switch (msg_type) {
+	case IPAC_MSGT_ID_RESP:
+		DEBUGP(DMI, "ID_RESP\n");
+		if (!line->ops.sign_link_up) {
+			LOGP(DINP, LOGL_ERROR, "Fix your application, "
+				"no action set if the signalling link "
+				"becomes ready\n");
+			return -EINVAL;
+		}
+		line->ops.sign_link_up(msg, line);
+		break;
+	}
+	return 0;
+}
+
 static int handle_ts1_read(struct osmo_fd *bfd)
 {
 	struct e1inp_line *line = bfd->data;
 	unsigned int ts_nr = bfd->priv_nr;
 	struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
+	struct e1inp_sign_link *link;
+	struct ipaccess_head *hh;
 	struct msgb *msg;
 	int ret = 0, error;
 
 	msg = ipaccess_read_msg(bfd, &error);
 	if (!msg) {
-		if (e1i_ts->line->rx_err)
-			e1i_ts->line->rx_err(error);
+		if (e1i_ts->line->ops.error)
+			e1i_ts->line->ops.error(NULL, error);
 		if (error == 0) {
 		        osmo_fd_unregister(bfd);
 		        close(bfd->fd);
@@ -191,10 +238,32 @@
 	}
 	DEBUGP(DMI, "RX %u: %s\n", ts_nr, osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
 
-	/* XXX better use e1inp_ts_rx. */
+	hh = (struct ipaccess_head *) msg->data;
+	if (hh->proto == IPAC_PROTO_IPACCESS) {
+		ipaccess_rcvmsg(line, msg, bfd);
+		msgb_free(msg);
+		return ret;
+	}
+	/* BIG FAT WARNING: bfd might no longer exist here, since ipaccess_rcvmsg()
+	 * might have free'd it !!! */
 
-	if (e1i_ts->line->rx)
-		e1i_ts->line->rx(msg, e1i_ts);
+	link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0);
+	if (!link) {
+		LOGP(DINP, LOGL_ERROR, "no matching signalling link for "
+			"hh->proto=0x%02x\n", hh->proto);
+		msgb_free(msg);
+		return -EIO;
+	}
+	msg->dst = link;
+
+	/* XXX better use e1inp_ts_rx? */
+	if (!e1i_ts->line->ops.sign_link) {
+		LOGP(DINP, LOGL_ERROR, "Fix your application, "
+			"no action set for signalling messages.\n");
+		return -ENOENT;
+	}
+	e1i_ts->line->ops.sign_link(msg, link);
+
 	return ret;
 }
 
diff --git a/src/input/misdn.c b/src/input/misdn.c
index c97169a..afa9032 100644
--- a/src/input/misdn.c
+++ b/src/input/misdn.c
@@ -115,7 +115,8 @@
 	}
 
 	if (alen != sizeof(l2addr)) {
-		fprintf(stderr, "%s error len\n", __func__);
+		if (line->ops.error)
+			line->ops.error(NULL, -EBADMSG);
 		return -EINVAL;
 	}
 
@@ -178,6 +179,8 @@
 		l2addr.channel, l2addr.sapi, l2addr.tei);
 		break;
 	default:
+		if (line->ops.error)
+			line->ops.error(NULL, -EBADMSG);
 		break;
 	}
 	return ret;