Implement ITU-T I.460 multiplex / demultiplex

This implements a multiplexer and de-multiplexer for the ITU-T I.460
standard.  The latter covers the transmission of sub-slots of 32/16/8k
inside 64k timeslots.

Change-Id: Id522f06e73b77332b437b7a27e4966872da70eda
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 0d0327a..5e810e6 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -35,6 +35,7 @@
 		 context/context_test					\
                  gsm0502/gsm0502_test					\
                  dtx/dtx_gsm0503_test					\
+                 i460_mux/i460_mux_test					\
 		 $(NULL)
 
 if ENABLE_MSGFILE
@@ -269,6 +270,9 @@
 exec_exec_test_SOURCES = exec/exec_test.c
 exec_exec_test_LDADD = $(LDADD)
 
+i460_mux_i460_mux_test_SOURCES = i460_mux/i460_mux_test.c
+i460_mux_i460_mux_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+
 # The `:;' works around a Bash 3.2 bug when the output is not writeable.
 $(srcdir)/package.m4: $(top_srcdir)/configure.ac
 	:;{ \
@@ -346,6 +350,7 @@
 	     gsm0502/gsm0502_test.ok \
 	     dtx/dtx_gsm0503_test.ok \
 	     exec/exec_test.ok exec/exec_test.err \
+	     i460_mux/i460_mux_test.ok \
 	     $(NULL)
 
 DISTCLEANFILES = atconfig atlocal conv/gsm0503_test_vectors.c
diff --git a/tests/i460_mux/i460_mux_test.c b/tests/i460_mux/i460_mux_test.c
new file mode 100644
index 0000000..53144fd
--- /dev/null
+++ b/tests/i460_mux/i460_mux_test.c
@@ -0,0 +1,397 @@
+
+#include <osmocom/core/utils.h>
+
+#include <osmocom/gsm/i460_mux.h>
+
+static void bits_cb(void *user_data, const ubit_t *bits, unsigned int num_bits)
+{
+	char *str = user_data;
+	printf("demux_bits_cb '%s': %s\n", str, osmo_ubit_dump(bits, num_bits));
+}
+
+
+const struct osmo_i460_schan_desc scd64 = {
+	.rate = OSMO_I460_RATE_64k,
+	.bit_offset = 0,
+	.demux = {
+		.num_bits = 40,
+		.out_cb_bits = bits_cb,
+		.out_cb_bytes = NULL,
+		.user_data = "64k",
+	},
+};
+
+const struct osmo_i460_schan_desc scd32_0 = {
+	.rate = OSMO_I460_RATE_32k,
+	.bit_offset = 0,
+	.demux = {
+		.num_bits = 40,
+		.out_cb_bits = bits_cb,
+		.out_cb_bytes = NULL,
+		.user_data = "32k_0",
+	},
+};
+const struct osmo_i460_schan_desc scd32_4 = {
+	.rate = OSMO_I460_RATE_32k,
+	.bit_offset = 4,
+	.demux = {
+		.num_bits = 40,
+		.out_cb_bits = bits_cb,
+		.out_cb_bytes = NULL,
+		.user_data = "32k_4",
+	},
+};
+
+const struct osmo_i460_schan_desc scd16_0 = {
+	.rate = OSMO_I460_RATE_16k,
+	.bit_offset = 0,
+	.demux = {
+		.num_bits = 40,
+		.out_cb_bits = bits_cb,
+		.out_cb_bytes = NULL,
+		.user_data = "16k_0",
+	},
+};
+const struct osmo_i460_schan_desc scd16_2 = {
+	.rate = OSMO_I460_RATE_16k,
+	.bit_offset = 2,
+	.demux = {
+		.num_bits = 40,
+		.out_cb_bits = bits_cb,
+		.out_cb_bytes = NULL,
+		.user_data = "16k_2",
+	},
+};
+const struct osmo_i460_schan_desc scd16_4 = {
+	.rate = OSMO_I460_RATE_16k,
+	.bit_offset = 4,
+	.demux = {
+		.num_bits = 40,
+		.out_cb_bits = bits_cb,
+		.out_cb_bytes = NULL,
+		.user_data = "16k_4",
+	},
+};
+const struct osmo_i460_schan_desc scd16_6 = {
+	.rate = OSMO_I460_RATE_16k,
+	.bit_offset = 6,
+	.demux = {
+		.num_bits = 40,
+		.out_cb_bits = bits_cb,
+		.out_cb_bytes = NULL,
+		.user_data = "16k_6",
+	},
+};
+
+const struct osmo_i460_schan_desc scd8_0 = {
+	.rate = OSMO_I460_RATE_8k,
+	.bit_offset = 0,
+	.demux = {
+		.num_bits = 40,
+		.out_cb_bits = bits_cb,
+		.out_cb_bytes = NULL,
+		.user_data = "8k_0",
+	},
+};
+const struct osmo_i460_schan_desc scd8_1 = {
+	.rate = OSMO_I460_RATE_8k,
+	.bit_offset = 1,
+	.demux = {
+		.num_bits = 40,
+		.out_cb_bits = bits_cb,
+		.out_cb_bytes = NULL,
+		.user_data = "8k_1",
+	},
+};
+const struct osmo_i460_schan_desc scd8_2 = {
+	.rate = OSMO_I460_RATE_8k,
+	.bit_offset = 2,
+	.demux = {
+		.num_bits = 40,
+		.out_cb_bits = bits_cb,
+		.out_cb_bytes = NULL,
+		.user_data = "8k_2",
+	},
+};
+const struct osmo_i460_schan_desc scd8_3 = {
+	.rate = OSMO_I460_RATE_8k,
+	.bit_offset = 3,
+	.demux = {
+		.num_bits = 40,
+		.out_cb_bits = bits_cb,
+		.out_cb_bytes = NULL,
+		.user_data = "8k_3",
+	},
+};
+const struct osmo_i460_schan_desc scd8_4 = {
+	.rate = OSMO_I460_RATE_8k,
+	.bit_offset = 4,
+	.demux = {
+		.num_bits = 40,
+		.out_cb_bits = bits_cb,
+		.out_cb_bytes = NULL,
+		.user_data = "8k_4",
+	},
+};
+const struct osmo_i460_schan_desc scd8_5 = {
+	.rate = OSMO_I460_RATE_8k,
+	.bit_offset = 5,
+	.demux = {
+		.num_bits = 40,
+		.out_cb_bits = bits_cb,
+		.out_cb_bytes = NULL,
+		.user_data = "8k_5",
+	},
+};
+const struct osmo_i460_schan_desc scd8_6 = {
+	.rate = OSMO_I460_RATE_8k,
+	.bit_offset = 6,
+	.demux = {
+		.num_bits = 40,
+		.out_cb_bits = bits_cb,
+		.out_cb_bytes = NULL,
+		.user_data = "8k_6",
+	},
+};
+const struct osmo_i460_schan_desc scd8_7 = {
+	.rate = OSMO_I460_RATE_8k,
+	.bit_offset = 7,
+	.demux = {
+		.num_bits = 40,
+		.out_cb_bits = bits_cb,
+		.out_cb_bytes = NULL,
+		.user_data = "8k_7",
+	},
+};
+
+static void test_no_subchan(void)
+{
+	struct osmo_i460_timeslot _ts, *ts = &_ts;
+
+	/* Initialization */
+	printf("\n==> %s\n", __func__);
+	osmo_i460_ts_init(ts);
+
+	/* feed in some data; expect nothing to happen */
+	const uint8_t nothing[128] = { 0, };
+	osmo_i460_demux_in(ts, nothing, sizeof(nothing));
+
+	/* pull bytes out of mux (should be all 0xff) */
+	uint8_t buf[128];
+	osmo_i460_mux_out(ts, buf, sizeof(buf));
+	printf("out: %s\n", osmo_hexdump(buf, sizeof(buf)));
+}
+
+static struct msgb *gen_alternating_bitmsg(unsigned int num_bits)
+{
+	struct msgb *msg = msgb_alloc(num_bits, "mux-in");
+	int i;
+	for (i = 0; i < num_bits; i++)
+		msgb_put_u8(msg, i & 1);
+	return msg;
+}
+
+static void test_64k_subchan(void)
+{
+	struct osmo_i460_timeslot _ts, *ts = &_ts;
+
+	/* Initialization */
+	printf("\n==> %s\n", __func__);
+	osmo_i460_ts_init(ts);
+	osmo_i460_subchan_add(NULL, ts, &scd64);
+
+	/* demux */
+	uint8_t sequence[128];
+	int i;
+	for (i = 0; i < sizeof(sequence); i++)
+		sequence[i] = i;
+	osmo_i460_demux_in(ts, sequence, sizeof(sequence));
+
+	/* mux */
+	struct msgb *msg = gen_alternating_bitmsg(128);
+	osmo_i460_mux_enqueue(&ts->schan[0], msg);
+
+	uint8_t buf[16];
+	osmo_i460_mux_out(ts, buf, sizeof(buf));
+	printf("mux_out: %s\n", osmo_hexdump(buf, sizeof(buf)));
+
+	osmo_i460_subchan_del(&ts->schan[0]);
+}
+
+static void test_32k_subchan(void)
+{
+	struct osmo_i460_timeslot _ts, *ts = &_ts;
+
+	/* Initialization */
+	printf("\n==> %s\n", __func__);
+	osmo_i460_ts_init(ts);
+	osmo_i460_subchan_add(NULL, ts, &scd32_0);
+	osmo_i460_subchan_add(NULL, ts, &scd32_4);
+
+	/* demux */
+	uint8_t sequence[10];
+	int i;
+	for (i = 0; i < sizeof(sequence); i++)
+		sequence[i] = 0;
+	sequence[0] = 0x0f;
+	sequence[1] = 0xf0;
+	sequence[2] = 0xff;
+	osmo_i460_demux_in(ts, sequence, sizeof(sequence));
+
+	/* mux */
+
+	/* test with only a single channel active */
+	for (i = 0; i < 2; i++) {
+		struct msgb *msg = gen_alternating_bitmsg(128);
+		osmo_i460_mux_enqueue(&ts->schan[i], msg);
+		printf("%s-single-%u\n", __func__, i);
+
+		uint8_t buf[16];
+		int j;
+		for (j = 0; j < 3; j++) {
+			osmo_i460_mux_out(ts, buf, sizeof(buf));
+			printf("mux_out: %s\n", osmo_hexdump(buf, sizeof(buf)));
+		}
+	}
+
+	for (i = 0; i < 4; i++)
+		osmo_i460_subchan_del(&ts->schan[i]);
+}
+
+
+
+static void test_16k_subchan(void)
+{
+	struct osmo_i460_timeslot _ts, *ts = &_ts;
+
+	/* Initialization */
+	printf("\n==> %s\n", __func__);
+	osmo_i460_ts_init(ts);
+	osmo_i460_subchan_add(NULL, ts, &scd16_0);
+	osmo_i460_subchan_add(NULL, ts, &scd16_2);
+	osmo_i460_subchan_add(NULL, ts, &scd16_4);
+	osmo_i460_subchan_add(NULL, ts, &scd16_6);
+
+	/* demux */
+	uint8_t sequence[20];
+	int i;
+	for (i = 0; i < sizeof(sequence); i++)
+		sequence[i] = 0;
+	sequence[0] = 0x03;
+	sequence[1] = 0x0c;
+	sequence[2] = 0x30;
+	sequence[3] = 0xc0;
+	sequence[4] = 0xff;
+	osmo_i460_demux_in(ts, sequence, sizeof(sequence));
+
+	/* mux */
+
+	/* test with only a single channel active */
+	for (i = 0; i < 4; i++) {
+		struct msgb *msg = gen_alternating_bitmsg(128);
+		osmo_i460_mux_enqueue(&ts->schan[i], msg);
+		printf("%s-single-%u\n", __func__, i);
+
+		uint8_t buf[16];
+		int j;
+		for (j = 0; j < 5; j++) {
+			osmo_i460_mux_out(ts, buf, sizeof(buf));
+			printf("mux_out: %s\n", osmo_hexdump(buf, sizeof(buf)));
+		}
+	}
+
+	for (i = 0; i < 4; i++)
+		osmo_i460_subchan_del(&ts->schan[i]);
+}
+
+
+static void test_8k_subchan(void)
+{
+	struct osmo_i460_timeslot _ts, *ts = &_ts;
+
+	/* Initialization */
+	printf("\n==> %s\n", __func__);
+	osmo_i460_ts_init(ts);
+	osmo_i460_subchan_add(NULL, ts, &scd8_0);
+	osmo_i460_subchan_add(NULL, ts, &scd8_1);
+	osmo_i460_subchan_add(NULL, ts, &scd8_2);
+	osmo_i460_subchan_add(NULL, ts, &scd8_3);
+	osmo_i460_subchan_add(NULL, ts, &scd8_4);
+	osmo_i460_subchan_add(NULL, ts, &scd8_5);
+	osmo_i460_subchan_add(NULL, ts, &scd8_6);
+	osmo_i460_subchan_add(NULL, ts, &scd8_7);
+
+	/* demux */
+	uint8_t sequence[40];
+	int i;
+	for (i = 0; i < sizeof(sequence); i++)
+		sequence[i] = 0;
+	i = 0;
+	sequence[i++] = 0x01;
+	sequence[i++] = 0x02;
+	sequence[i++] = 0x04;
+	sequence[i++] = 0x08;
+	sequence[i++] = 0x0f;
+	sequence[i++] = 0x10;
+	sequence[i++] = 0x20;
+	sequence[i++] = 0x40;
+	sequence[i++] = 0x80;
+	sequence[i++] = 0xf0;
+	sequence[i++] = 0xff;
+	osmo_i460_demux_in(ts, sequence, sizeof(sequence));
+
+	/* mux */
+
+	/* test with only a single channel active */
+	for (i = 0; i < 8; i++) {
+		struct msgb *msg = gen_alternating_bitmsg(64);
+		osmo_i460_mux_enqueue(&ts->schan[i], msg);
+		printf("%s-single-%u\n", __func__, i);
+
+		uint8_t buf[16];
+		int j;
+		for (j = 0; j < 5; j++) {
+			osmo_i460_mux_out(ts, buf, sizeof(buf));
+			printf("mux_out: %s\n", osmo_hexdump(buf, sizeof(buf)));
+		}
+	}
+
+	for (i = 0; i < 8; i++)
+		osmo_i460_subchan_del(&ts->schan[i]);
+}
+
+/* activate only one sub-channel; expect unused bits to be '1' */
+static void test_unused_subchan(void)
+{
+	struct osmo_i460_timeslot _ts, *ts = &_ts;
+
+	/* Initialization */
+	printf("\n==> %s\n", __func__);
+	osmo_i460_ts_init(ts);
+	osmo_i460_subchan_add(NULL, ts, &scd16_0);
+
+	/* mux */
+	struct msgb *msg = gen_alternating_bitmsg(128);
+	memset(msgb_data(msg), 0, msgb_length(msg));
+	osmo_i460_mux_enqueue(&ts->schan[0], msg);
+	printf("%s-single\n", __func__);
+
+	uint8_t buf[16];
+	int j;
+	for (j = 0; j < 5; j++) {
+		osmo_i460_mux_out(ts, buf, sizeof(buf));
+		printf("mux_out: %s\n", osmo_hexdump(buf, sizeof(buf)));
+	}
+
+	osmo_i460_subchan_del(&ts->schan[0]);
+}
+
+int main(int argc, char **argv)
+{
+	test_no_subchan();
+	test_64k_subchan();
+	test_32k_subchan();
+	test_16k_subchan();
+	test_8k_subchan();
+	test_unused_subchan();
+}
diff --git a/tests/i460_mux/i460_mux_test.ok b/tests/i460_mux/i460_mux_test.ok
new file mode 100644
index 0000000..b94fb7b
--- /dev/null
+++ b/tests/i460_mux/i460_mux_test.ok
@@ -0,0 +1,115 @@
+
+==> test_no_subchan
+out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
+
+==> test_64k_subchan
+demux_bits_cb '64k': 0000000000000001000000100000001100000100000001010000011000000111000010000000100100001010000010110000110000001101000011100000111100010000000100010001001000010011000101000001010100010110000101110001100000011001000110100001101100011100000111010001111000011111001000000010000100100010001000110010010000100101001001100010011100101000001010010010101000101011001011000010110100101110001011110011000000110001001100100011001100110100001101010011011000110111001110000011100100111010001110110011110000111101001111100011111101000000010000010100001001000011010001000100010101000110010001110100100001001001010010100100101101001100010011010100111001001111010100000101000101010010010100110101010001010101010101100101011101011000010110010101101001011011010111000101110101011110010111110110000001100001011000100110001101100100011001010110011001100111011010000110100101101010011010110110110001101101011011100110111101110000011100010111001001110011011101000111010101110110011101110111100001111001011110100111101101111100011111010111111001111111
+mux_out: 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 
+
+==> test_32k_subchan
+demux_bits_cb '32k_0': 1111000011110000000000000000000000000000
+demux_bits_cb '32k_4': 0000111111110000000000000000000000000000
+test_32k_subchan-single-0
+mux_out: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 
+mux_out: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
+test_32k_subchan-single-1
+mux_out: 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 
+mux_out: 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
+
+==> test_16k_subchan
+demux_bits_cb '16k_0': 1100000011000000000000000000000000000000
+demux_bits_cb '16k_2': 0011000011000000000000000000000000000000
+demux_bits_cb '16k_4': 0000110011000000000000000000000000000000
+demux_bits_cb '16k_6': 0000001111000000000000000000000000000000
+test_16k_subchan-single-0
+mux_out: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 
+mux_out: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 
+mux_out: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 
+mux_out: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
+test_16k_subchan-single-1
+mux_out: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 
+mux_out: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 
+mux_out: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 
+mux_out: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
+test_16k_subchan-single-2
+mux_out: df df df df df df df df df df df df df df df df 
+mux_out: df df df df df df df df df df df df df df df df 
+mux_out: df df df df df df df df df df df df df df df df 
+mux_out: df df df df df df df df df df df df df df df df 
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
+test_16k_subchan-single-3
+mux_out: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 
+mux_out: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 
+mux_out: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 
+mux_out: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
+
+==> test_8k_subchan
+demux_bits_cb '8k_0': 1000100000100000000000000000000000000000
+demux_bits_cb '8k_1': 0100100000100000000000000000000000000000
+demux_bits_cb '8k_2': 0010100000100000000000000000000000000000
+demux_bits_cb '8k_3': 0001100000100000000000000000000000000000
+demux_bits_cb '8k_4': 0000010001100000000000000000000000000000
+demux_bits_cb '8k_5': 0000001001100000000000000000000000000000
+demux_bits_cb '8k_6': 0000000101100000000000000000000000000000
+demux_bits_cb '8k_7': 0000000011100000000000000000000000000000
+test_8k_subchan-single-0
+mux_out: fe ff fe ff fe ff fe ff fe ff fe ff fe ff fe ff 
+mux_out: fe ff fe ff fe ff fe ff fe ff fe ff fe ff fe ff 
+mux_out: fe ff fe ff fe ff fe ff fe ff fe ff fe ff fe ff 
+mux_out: fe ff fe ff fe ff fe ff fe ff fe ff fe ff fe ff 
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
+test_8k_subchan-single-1
+mux_out: fd ff fd ff fd ff fd ff fd ff fd ff fd ff fd ff 
+mux_out: fd ff fd ff fd ff fd ff fd ff fd ff fd ff fd ff 
+mux_out: fd ff fd ff fd ff fd ff fd ff fd ff fd ff fd ff 
+mux_out: fd ff fd ff fd ff fd ff fd ff fd ff fd ff fd ff 
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
+test_8k_subchan-single-2
+mux_out: fb ff fb ff fb ff fb ff fb ff fb ff fb ff fb ff 
+mux_out: fb ff fb ff fb ff fb ff fb ff fb ff fb ff fb ff 
+mux_out: fb ff fb ff fb ff fb ff fb ff fb ff fb ff fb ff 
+mux_out: fb ff fb ff fb ff fb ff fb ff fb ff fb ff fb ff 
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
+test_8k_subchan-single-3
+mux_out: f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff 
+mux_out: f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff 
+mux_out: f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff 
+mux_out: f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff 
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
+test_8k_subchan-single-4
+mux_out: ef ff ef ff ef ff ef ff ef ff ef ff ef ff ef ff 
+mux_out: ef ff ef ff ef ff ef ff ef ff ef ff ef ff ef ff 
+mux_out: ef ff ef ff ef ff ef ff ef ff ef ff ef ff ef ff 
+mux_out: ef ff ef ff ef ff ef ff ef ff ef ff ef ff ef ff 
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
+test_8k_subchan-single-5
+mux_out: df ff df ff df ff df ff df ff df ff df ff df ff 
+mux_out: df ff df ff df ff df ff df ff df ff df ff df ff 
+mux_out: df ff df ff df ff df ff df ff df ff df ff df ff 
+mux_out: df ff df ff df ff df ff df ff df ff df ff df ff 
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
+test_8k_subchan-single-6
+mux_out: bf ff bf ff bf ff bf ff bf ff bf ff bf ff bf ff 
+mux_out: bf ff bf ff bf ff bf ff bf ff bf ff bf ff bf ff 
+mux_out: bf ff bf ff bf ff bf ff bf ff bf ff bf ff bf ff 
+mux_out: bf ff bf ff bf ff bf ff bf ff bf ff bf ff bf ff 
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
+test_8k_subchan-single-7
+mux_out: 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 
+mux_out: 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 
+mux_out: 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 
+mux_out: 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
+
+==> test_unused_subchan
+test_unused_subchan-single
+mux_out: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc 
+mux_out: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc 
+mux_out: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc 
+mux_out: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc 
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
diff --git a/tests/testsuite.at b/tests/testsuite.at
index bab5730..4ff6671 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -375,3 +375,9 @@
 cat $abs_srcdir/exec/exec_test.err > experr
 AT_CHECK([$abs_top_builddir/tests/exec/exec_test], [0], [expout], [experr])
 AT_CLEANUP
+
+AT_SETUP([i460_mux])
+AT_KEYWORDS([i460_mux])
+cat $abs_srcdir/i460_mux/i460_mux_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/i460_mux/i460_mux_test], [0], [expout], [ignore])
+AT_CLEANUP