e1cap_dump: Add 16k sub-channel demux + filter

We can now filter a given 16k sub-slot out of the capture data and
export it to stdout.
diff --git a/src/e1cap_dump.c b/src/e1cap_dump.c
index b732449..48741f4 100644
--- a/src/e1cap_dump.c
+++ b/src/e1cap_dump.c
@@ -7,6 +7,7 @@
 #include <osmocom/core/signal.h>
 #include <osmocom/core/logging.h>
 #include <osmocom/core/application.h>
+#include <osmocom/abis/subchan_demux.h>
 
 #include "storage.h"
 #include "recorder.h"
@@ -21,6 +22,8 @@
 static enum mode g_mode = MODE_PRINT;
 static int g_filter_line = -1;
 static int g_filter_slot = -1;
+static int g_filter_subslot = -1;
+static struct osmo_e1cap_pkthdr *g_last_pkthdr;
 
 static char *timeval2str(struct timeval *tv)
 {
@@ -36,21 +39,51 @@
 	return buf;
 }
 
+static void handle_data(struct osmo_e1cap_pkthdr *pkt, const uint8_t *data, int len)
+{
+	switch (g_mode) {
+	case MODE_PRINT:
+		printf("%s %02u/%02u %u (%u): %s\n",
+			timeval2str(&pkt->ts),
+			pkt->line_nr, pkt->ts_nr, pkt->capture_mode,
+			pkt->len,
+			osmo_hexdump_nospc(data, len));
+		break;
+	case MODE_BIN:
+		write(1, data, len);
+		break;
+	}
+}
+
+static int subch_demux_out_cb(struct subch_demux *dmx, int ch, uint8_t *data,
+			      int len, void *c)
+{
+	OSMO_ASSERT(ch == g_filter_subslot);
+	handle_data(g_last_pkthdr, data, len);
+
+	return 0;
+}
+
 static int handle_options(int argc, char **argv)
 {
 	int opt;
 
-	while ((opt = getopt(argc, argv, "l:s:b")) != -1) {
+	while ((opt = getopt(argc, argv, "l:s:bu:")) != -1) {
 		switch (opt) {
-		case 'l':
+		case 'l': /* Filter on E1 Line Number */
 			g_filter_line = atoi(optarg);
 			break;
-		case 's':
+		case 's': /* Filter on E1 Slot Number */
 			g_filter_slot = atoi(optarg);
 			break;
-		case 'b':
+		case 'b': /* Raw binary output mode (for piping) */
 			g_mode = MODE_BIN;
 			break;
+		case 'u': /* 16k Sub-channel demux + filter */
+			g_filter_subslot = atoi(optarg);
+			if (g_filter_subslot < 0 || g_filter_subslot > 3)
+				exit(2);
+			break;
 		default:
 			fprintf(stderr, "Unknown option '%c'\n", opt);
 			exit(EXIT_FAILURE);
@@ -66,6 +99,7 @@
 	struct osmo_e1cap_file *f;
 	struct osmo_e1cap_pkthdr *pkt;
 	unsigned long num_pkt = 0;
+	struct subch_demux smux;
 
 	printf("sizeof(timeval) = %zu\n", sizeof(struct timeval));
 	printf("sizeof(osmo_e1cap_pkthdr) = %zu\n", sizeof(*pkt));
@@ -83,26 +117,28 @@
 		exit(1);
 	}
 
+	if (g_filter_subslot >= 0) {
+		smux.out_cb = subch_demux_out_cb;
+		subch_demux_init(&smux);
+		subch_demux_activate(&smux, g_filter_subslot);
+	}
+
 	while ((pkt = osmo_e1cap_read_next(f))) {
 		num_pkt++;
+		g_last_pkthdr = pkt;
 
 		if (g_filter_line >= 0 && pkt->line_nr != g_filter_line)
 			continue;
 		if (g_filter_slot >= 0 && pkt->ts_nr != g_filter_slot)
 			continue;
 
-		switch (g_mode) {
-		case MODE_PRINT:
-			printf("%s %02u/%02u %u (%u): %s\n",
-				timeval2str(&pkt->ts),
-				pkt->line_nr, pkt->ts_nr, pkt->capture_mode,
-				pkt->len,
-				osmo_hexdump_nospc(pkt->data, pkt->len));
-			break;
-		case MODE_BIN:
-			write(1, pkt->data, pkt->len);
-			break;
+		if (g_filter_subslot >= 0) {
+			subch_demux_in(&smux, pkt->data, pkt->len);
+			continue;
 		}
+
+		handle_data(pkt, pkt->data, pkt->len);
+
 		talloc_free(pkt);
 	}