changeset 902:8ddb16a37273

tree org: move TCH and VM utils from miscutil to tchtools
author Mychaela Falconia <falcon@freecalypso.org>
date Wed, 28 Dec 2022 07:52:30 +0000
parents 2e6764022292
children a7ad6b39e01b
files .hgignore Makefile miscutil/Makefile miscutil/fc-fr2tch.c miscutil/fc-gsm2vm.c miscutil/fc-tch2fr.c miscutil/fc-vm2hex.c miscutil/gsm0610.c tchtools/Makefile tchtools/fc-fr2tch.c tchtools/fc-gsm2vm.c tchtools/fc-tch2fr.c tchtools/fc-vm2hex.c tchtools/gsm0610.c
diffstat 14 files changed, 674 insertions(+), 657 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Tue Dec 13 03:24:36 2022 +0000
+++ b/.hgignore	Wed Dec 28 07:52:30 2022 +0000
@@ -34,14 +34,10 @@
 ^loadtools/fc-xram$
 
 ^miscutil/arfcn2ti$
-^miscutil/fc-fr2tch$
-^miscutil/fc-gsm2vm$
 ^miscutil/fc-pulse-dtr$
 ^miscutil/fc-pulse-rts$
 ^miscutil/fc-rgbconv$
 ^miscutil/fc-serterm$
-^miscutil/fc-tch2fr$
-^miscutil/fc-vm2hex$
 ^miscutil/imei-luhn$
 ^miscutil/make-imeisv$
 ^miscutil/mokosrec2bin$
@@ -72,6 +68,11 @@
 ^target-utils/tf-breakin/embed\.c$
 ^target-utils/tf-breakin/mkembed$
 
+^tchtools/fc-fr2tch$
+^tchtools/fc-gsm2vm$
+^tchtools/fc-tch2fr$
+^tchtools/fc-vm2hex$
+
 ^toolchain/binutils-2\.21\.1/
 ^toolchain/binutils-build/
 ^toolchain/gcc-4\.5\.4/
--- a/Makefile	Tue Dec 13 03:24:36 2022 +0000
+++ b/Makefile	Wed Dec 28 07:52:30 2022 +0000
@@ -1,6 +1,6 @@
 CC=	gcc
 CFLAGS=	-O2
-PROGDIR=ffstools loadtools miscutil ringtools rvinterf uptools
+PROGDIR=ffstools loadtools miscutil ringtools rvinterf tchtools uptools
 LIBDIR=	libpwon librftab libserial
 SUBDIR=	${PROGDIR} ${LIBDIR}
 
--- a/miscutil/Makefile	Tue Dec 13 03:24:36 2022 +0000
+++ b/miscutil/Makefile	Wed Dec 28 07:52:30 2022 +0000
@@ -1,8 +1,7 @@
 CC=	gcc
 CFLAGS=	-O2
-PROGS=	arfcn2ti fc-fr2tch fc-gsm2vm fc-pulse-dtr fc-pulse-rts fc-rgbconv \
-	fc-serterm fc-tch2fr fc-vm2hex imei-luhn make-imeisv mokosrec2bin \
-	srec-regions ti2arfcn
+PROGS=	arfcn2ti fc-pulse-dtr fc-pulse-rts fc-rgbconv fc-serterm imei-luhn \
+	make-imeisv mokosrec2bin srec-regions ti2arfcn
 SCRIPTS=c139explore pirexplore
 
 INSTALL_PREFIX=	/opt/freecalypso
@@ -11,20 +10,11 @@
 
 all:	${PROGS}
 
-FR2TCH_OBJS=	fc-fr2tch.o gsm0610.o
-GSM2VM_OBJS=	fc-gsm2vm.o gsm0610.o
-TCH2FR_OBJS=	fc-tch2fr.o gsm0610.o
 SERTERM_OBJS=	fc-serterm.o ttypassthru.o ../libserial/libserial.a
 
 arfcn2ti:	arfcn2ti.c
 	${CC} ${CFLAGS} -o $@ $@.c
 
-fc-fr2tch:	${FR2TCH_OBJS}
-	${CC} ${CFLAGS} -o $@ ${FR2TCH_OBJS}
-
-fc-gsm2vm:	${GSM2VM_OBJS}
-	${CC} ${CFLAGS} -o $@ ${GSM2VM_OBJS}
-
 fc-pulse-dtr:	fc-pulse-dtr.c
 	${CC} ${CFLAGS} -o $@ $@.c
 
@@ -40,12 +30,6 @@
 ttypassthru.o:	../loadtools/ttypassthru.c
 	${CC} ${CFLAGS} -c -o $@ $<
 
-fc-tch2fr:	${TCH2FR_OBJS}
-	${CC} ${CFLAGS} -o $@ ${TCH2FR_OBJS}
-
-fc-vm2hex:	fc-vm2hex.c
-	${CC} ${CFLAGS} -o $@ $@.c
-
 imei-luhn:	imei-luhn.c
 	${CC} ${CFLAGS} -o $@ $@.c
 
--- a/miscutil/fc-fr2tch.c	Tue Dec 13 03:24:36 2022 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * This utility converts a GSM 06.10 speech recording from the format that is
- * commonly accepted as standard in the Unix/Linux world (libgsm format) into
- * hex strings of TCH bits to be fed to the GSM 05.03 channel encoder by way
- * of a TI Calypso GSM device, a FreeCalypso GSM firmware version with the
- * TCH rerouting feature, and fc-shell's tch play command.
- */
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-main(argc, argv)
-	char **argv;
-{
-	FILE *inf, *outf;
-	u_char libgsm_bytes[33], tidsp_bytes[33];
-	int cc, i, gotsome = 0;
-
-	if (argc != 3) {
-		fprintf(stderr, "usage: %s infile outfile\n", argv[0]);
-		exit(1);
-	}
-	inf = fopen(argv[1], "r");
-	if (!inf) {
-		perror(argv[1]);
-		exit(1);
-	}
-	outf = fopen(argv[2], "w");
-	if (!outf) {
-		perror(argv[2]);
-		exit(1);
-	}
-	for (;;) {
-		cc = fread(libgsm_bytes, 1, 33, inf);
-		if (cc < 33)
-			break;
-		if ((libgsm_bytes[0] & 0xF0) != 0xD0) {
-invalid:		fprintf(stderr, "error: %s is not in libgsm format\n",
-				argv[1]);
-			exit(1);
-		}
-		gsm0610_libgsm_to_tidsp(libgsm_bytes, tidsp_bytes);
-		for (i = 0; i < 33; i++)
-			fprintf(outf, "%02X", tidsp_bytes[i]);
-		putc('\n', outf);
-		gotsome = 1;
-	}
-	fclose(outf);
-	if (cc) {
-		if (gotsome)
-			fprintf(stderr,
-			"warning: extra non-33 bytes at the end of %s\n",
-				argv[1]);
-		else
-			goto invalid;
-	}
-	exit(0);
-}
--- a/miscutil/fc-gsm2vm.c	Tue Dec 13 03:24:36 2022 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * This utility converts a GSM 06.10 speech recording from the format that is
- * commonly accepted as standard in the Unix/Linux world (libgsm format) into
- * a voice memo file that can be uploaded into the FFS of a FreeCalypso device
- * and played with the audio_vm_play_start() API or the AT@VMP command that
- * invokes the latter.
- */
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-main(argc, argv)
-	char **argv;
-{
-	FILE *inf, *outf;
-	u_char libgsm_bytes[33], tidsp_bytes[34], arm_bytes[34];
-	static u_char header_bytes[6] = {0x00, 0xC4, 0x00, 0x00, 0x00, 0x00};
-	static u_char endmarker[2] = {0xFF, 0xFB};
-	int cc, i, gotsome = 0;
-
-	if (argc != 3) {
-		fprintf(stderr, "usage: %s infile outfile\n", argv[0]);
-		exit(1);
-	}
-	inf = fopen(argv[1], "r");
-	if (!inf) {
-		perror(argv[1]);
-		exit(1);
-	}
-	outf = fopen(argv[2], "w");
-	if (!outf) {
-		perror(argv[2]);
-		exit(1);
-	}
-	tidsp_bytes[33] = 0;
-	for (;;) {
-		cc = fread(libgsm_bytes, 1, 33, inf);
-		if (cc < 33)
-			break;
-		if ((libgsm_bytes[0] & 0xF0) != 0xD0) {
-invalid:		fprintf(stderr, "error: %s is not in libgsm format\n",
-				argv[1]);
-			exit(1);
-		}
-		gsm0610_libgsm_to_tidsp(libgsm_bytes, tidsp_bytes);
-		for (i = 0; i < 34; i += 2) {
-			arm_bytes[i] = tidsp_bytes[i+1];
-			arm_bytes[i+1] = tidsp_bytes[i];
-		}
-		fwrite(header_bytes, 1, 6, outf);
-		fwrite(arm_bytes, 1, 34, outf);
-		gotsome = 1;
-	}
-	fwrite(endmarker, 1, 2, outf);
-	fclose(outf);
-	if (cc) {
-		if (gotsome)
-			fprintf(stderr,
-			"warning: extra non-33 bytes at the end of %s\n",
-				argv[1]);
-		else
-			goto invalid;
-	}
-	exit(0);
-}
--- a/miscutil/fc-tch2fr.c	Tue Dec 13 03:24:36 2022 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +0,0 @@
-/*
- * Our experimental Calypso firmware enables us to capture the output of
- * the GSM 05.03 channel decoder in the DSP, i.e., the bits leaving the
- * channel decoder and going into the speech decoder.  Our fc-shell utility
- * allows saving this capture to a file; the captured booty includes not only
- * the expected 260 bits per frame, but also some DSP status words which are
- * not fully understood, but which are believed to contain indications as to
- * whether the decoded speech frame is good or bad.
- *
- * My first naive thought was to save the captured speech frames in libgsm
- * format so I could then play them with the 'play' command (SoX package)
- * under Linux, but the problem with this naive approach is that the bad frames
- * indication is lost, and some of the saved "speech" frames will contain
- * utter garbage, resulting in very unkind-on-ears noises if that file is
- * then played.  I don't know what the proper solution should be; I don't know
- * what the commercial cellphone implementations of the GSM 06.10 speech decoder
- * (buried in black box DSPs) do when they get bad frames from the channel
- * decoder.
- *
- * The present utility reproduces the naive behaviour of my previous
- * implementation of fc-shell's tch record command: it takes hex files written
- * by the current implementation of tch record in fc-shell, DISREGARDS the
- * DSP status words, and blindly converts each 260-bit frame (good or bad)
- * into libgsm format.
- */
-
-#include <sys/types.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-static
-decode_hex_digit(ch)
-{
-	if (isdigit(ch))
-		return(ch - '0');
-	else if (isupper(ch))
-		return(ch - 'A' + 10);
-	else
-		return(ch - 'a' + 10);
-}
-
-main(argc, argv)
-	char **argv;
-{
-	FILE *inf, *outf;
-	char linebuf[128];
-	int lineno;
-	char *cp;
-	int i, j;
-	u_char tidsp_bytes[33], libgsm_bytes[33];
-
-	if (argc != 3) {
-		fprintf(stderr, "usage: %s infile outfile\n", argv[0]);
-		exit(1);
-	}
-	inf = fopen(argv[1], "r");
-	if (!inf) {
-		perror(argv[1]);
-		exit(1);
-	}
-	outf = fopen(argv[2], "w");
-	if (!outf) {
-		perror(argv[2]);
-		exit(1);
-	}
-	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) {
-		/* skip DSP status words */
-		cp = linebuf;
-		for (i = 0; i < 3; i++) {
-			for (j = 0; j < 4; j++) {
-				if (!isxdigit(*cp++)) {
-invalid:				fprintf(stderr,
-				    "error: %s is not in the expected format\n",
-						argv[1]);
-					exit(1);
-				}
-			}
-			if (*cp++ != ' ')
-				goto invalid;
-		}
-		/* read the frame bits */
-		for (i = 0; i < 33; i++) {
-			if (!isxdigit(cp[0]) || !isxdigit(cp[1]))
-				goto invalid;
-			tidsp_bytes[i] = (decode_hex_digit(cp[0]) << 4) |
-					  decode_hex_digit(cp[1]);
-			cp += 2;
-		}
-		gsm0610_tidsp_to_libgsm(tidsp_bytes, libgsm_bytes);
-		fwrite(libgsm_bytes, 1, 33, outf);
-	}
-	exit(0);
-}
--- a/miscutil/fc-vm2hex.c	Tue Dec 13 03:24:36 2022 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-/*
- * This utility converts the old-fashioned (non-AMR) voice memo files
- * read out of FFS into hex strings that can be analyzed by a human
- * or further fed to fc-tch2fr.
- */
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-char *infname;
-FILE *inf, *outf;
-
-static unsigned
-get_word()
-{
-	u_char b[2];
-	int i, c;
-
-	for (i = 0; i < 2; i++) {
-		c = getc(inf);
-		if (c < 0) {
-			fprintf(stderr, "error: premature EOF in %s\n",
-				infname);
-			exit(1);
-		}
-		b[i] = c;
-	}
-	return((b[1] << 8) | b[0]);
-}
-
-convert_speech_sample()
-{
-	u_char bytes[34];
-	int i, dp;
-	unsigned word;
-
-	dp = 0;
-	for (i = 0; i < 17; i++) {
-		word = get_word();
-		bytes[dp++] = word >> 8;
-		bytes[dp++] = word;
-	}
-	for (i = 0; i < 33; i++)
-		fprintf(outf, "%02X", bytes[i]);
-}
-
-main(argc, argv)
-	char **argv;
-{
-	unsigned first_word;
-
-	if (argc < 2 || argc > 3) {
-		fprintf(stderr, "usage: %s infile [outfile]\n", argv[0]);
-		exit(1);
-	}
-	infname = argv[1];
-	inf = fopen(infname, "r");
-	if (!inf) {
-		perror(infname);
-		exit(1);
-	}
-	if (argc > 2) {
-		outf = fopen(argv[2], "w");
-		if (!outf) {
-			perror(argv[2]);
-			exit(1);
-		}
-	} else
-		outf = stdout;
-
-	for (;;) {
-		first_word = get_word();
-		if (first_word == 0xFBFF)	/* SC_VM_END_MASK */
-			break;
-		fprintf(outf, "%04X", first_word);
-		if (first_word & 0x8000) {	/* B_VM_SPEECH */
-			fprintf(outf, " %04X", get_word());
-			fprintf(outf, " %04X", get_word());
-			putc(' ', outf);
-			convert_speech_sample();
-		}
-		putc('\n', outf);
-	}
-	exit(0);
-}
--- a/miscutil/gsm0610.c	Tue Dec 13 03:24:36 2022 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,329 +0,0 @@
-/*
- * This code (lifted from OsmocomBB) facilitates the conversion of 33-byte
- * GSM 06.10 encoded speech frames between the bit order at the 06.10 interface
- * in the de facto standard libgsm implementation and the bit order at the
- * 05.03 channel encoder interface, as realized in TI's Calypso DSP.
- */
-
-#include <sys/types.h>
-#include <string.h>
-#include <strings.h>
-
-/* GSM FR - subjective importance bit ordering */
-	/* This array encodes GSM 05.03 Table 2.
-	 * It's also GSM 06.10 Table A.2.1a
-	 *
-	 * It converts between serial parameter output by the encoder and the
-	 * order needed before channel encoding.
-	 */
-const u_short gsm0610_bitorder[260] = {
-	0,	/* LARc0:5 */
-	47,	/* Xmaxc0:5 */
-	103,	/* Xmaxc1:5 */
-	159,	/* Xmaxc2:5 */
-	215,	/* Xmaxc3:5 */
-	1,	/* LARc0:4 */
-	6,	/* LARc1:5 */
-	12,	/* LARc2:4 */
-	2,	/* LARc0:3 */
-	7,	/* LARc1:4 */
-	13,	/* LARc2:3 */
-	17,	/* LARc3:4 */
-	36,	/* Nc0:6 */
-	92,	/* Nc1:6 */
-	148,	/* Nc2:6 */
-	204,	/* Nc3:6 */
-	48,	/* Xmaxc0:4 */
-	104,	/* Xmaxc1:4 */
-	160,	/* Xmaxc2:4 */
-	216,	/* Xmaxc3:4 */
-	8,	/* LARc1:3 */
-	22,	/* LARc4:3 */
-	26,	/* LARc5:3 */
-	37,	/* Nc0:5 */
-	93,	/* Nc1:5 */
-	149,	/* Nc2:5 */
-	205,	/* Nc3:5 */
-	38,	/* Nc0:4 */
-	94,	/* Nc1:4 */
-	150,	/* Nc2:4 */
-	206,	/* Nc3:4 */
-	39,	/* Nc0:3 */
-	95,	/* Nc1:3 */
-	151,	/* Nc2:3 */
-	207,	/* Nc3:3 */
-	40,	/* Nc0:2 */
-	96,	/* Nc1:2 */
-	152,	/* Nc2:2 */
-	208,	/* Nc3:2 */
-	49,	/* Xmaxc0:3 */
-	105,	/* Xmaxc1:3 */
-	161,	/* Xmaxc2:3 */
-	217,	/* Xmaxc3:3 */
-	3,	/* LARc0:2 */
-	18,	/* LARc3:3 */
-	30,	/* LARc6:2 */
-	41,	/* Nc0:1 */
-	97,	/* Nc1:1 */
-	153,	/* Nc2:1 */
-	209,	/* Nc3:1 */
-	23,	/* LARc4:2 */
-	27,	/* LARc5:2 */
-	43,	/* bc0:1 */
-	99,	/* bc1:1 */
-	155,	/* bc2:1 */
-	211,	/* bc3:1 */
-	42,	/* Nc0:0 */
-	98,	/* Nc1:0 */
-	154,	/* Nc2:0 */
-	210,	/* Nc3:0 */
-	45,	/* Mc0:1 */
-	101,	/* Mc1:1 */
-	157,	/* Mc2:1 */
-	213,	/* Mc3:1 */
-	4,	/* LARc0:1 */
-	9,	/* LARc1:2 */
-	14,	/* LARc2:2 */
-	33,	/* LARc7:2 */
-	19,	/* LARc3:2 */
-	24,	/* LARc4:1 */
-	31,	/* LARc6:1 */
-	44,	/* bc0:0 */
-	100,	/* bc1:0 */
-	156,	/* bc2:0 */
-	212,	/* bc3:0 */
-	50,	/* Xmaxc0:2 */
-	106,	/* Xmaxc1:2 */
-	162,	/* Xmaxc2:2 */
-	218,	/* Xmaxc3:2 */
-	53,	/* xmc0_0:2 */
-	56,	/* xmc0_1:2 */
-	59,	/* xmc0_2:2 */
-	62,	/* xmc0_3:2 */
-	65,	/* xmc0_4:2 */
-	68,	/* xmc0_5:2 */
-	71,	/* xmc0_6:2 */
-	74,	/* xmc0_7:2 */
-	77,	/* xmc0_8:2 */
-	80,	/* xmc0_9:2 */
-	83,	/* xmc0_10:2 */
-	86,	/* xmc0_11:2 */
-	89,	/* xmc0_12:2 */
-	109,	/* xmc1_0:2 */
-	112,	/* xmc1_1:2 */
-	115,	/* xmc1_2:2 */
-	118,	/* xmc1_3:2 */
-	121,	/* xmc1_4:2 */
-	124,	/* xmc1_5:2 */
-	127,	/* xmc1_6:2 */
-	130,	/* xmc1_7:2 */
-	133,	/* xmc1_8:2 */
-	136,	/* xmc1_9:2 */
-	139,	/* xmc1_10:2 */
-	142,	/* xmc1_11:2 */
-	145,	/* xmc1_12:2 */
-	165,	/* xmc2_0:2 */
-	168,	/* xmc2_1:2 */
-	171,	/* xmc2_2:2 */
-	174,	/* xmc2_3:2 */
-	177,	/* xmc2_4:2 */
-	180,	/* xmc2_5:2 */
-	183,	/* xmc2_6:2 */
-	186,	/* xmc2_7:2 */
-	189,	/* xmc2_8:2 */
-	192,	/* xmc2_9:2 */
-	195,	/* xmc2_10:2 */
-	198,	/* xmc2_11:2 */
-	201,	/* xmc2_12:2 */
-	221,	/* xmc3_0:2 */
-	224,	/* xmc3_1:2 */
-	227,	/* xmc3_2:2 */
-	230,	/* xmc3_3:2 */
-	233,	/* xmc3_4:2 */
-	236,	/* xmc3_5:2 */
-	239,	/* xmc3_6:2 */
-	242,	/* xmc3_7:2 */
-	245,	/* xmc3_8:2 */
-	248,	/* xmc3_9:2 */
-	251,	/* xmc3_10:2 */
-	254,	/* xmc3_11:2 */
-	257,	/* xmc3_12:2 */
-	46,	/* Mc0:0 */
-	102,	/* Mc1:0 */
-	158,	/* Mc2:0 */
-	214,	/* Mc3:0 */
-	51,	/* Xmaxc0:1 */
-	107,	/* Xmaxc1:1 */
-	163,	/* Xmaxc2:1 */
-	219,	/* Xmaxc3:1 */
-	54,	/* xmc0_0:1 */
-	57,	/* xmc0_1:1 */
-	60,	/* xmc0_2:1 */
-	63,	/* xmc0_3:1 */
-	66,	/* xmc0_4:1 */
-	69,	/* xmc0_5:1 */
-	72,	/* xmc0_6:1 */
-	75,	/* xmc0_7:1 */
-	78,	/* xmc0_8:1 */
-	81,	/* xmc0_9:1 */
-	84,	/* xmc0_10:1 */
-	87,	/* xmc0_11:1 */
-	90,	/* xmc0_12:1 */
-	110,	/* xmc1_0:1 */
-	113,	/* xmc1_1:1 */
-	116,	/* xmc1_2:1 */
-	119,	/* xmc1_3:1 */
-	122,	/* xmc1_4:1 */
-	125,	/* xmc1_5:1 */
-	128,	/* xmc1_6:1 */
-	131,	/* xmc1_7:1 */
-	134,	/* xmc1_8:1 */
-	137,	/* xmc1_9:1 */
-	140,	/* xmc1_10:1 */
-	143,	/* xmc1_11:1 */
-	146,	/* xmc1_12:1 */
-	166,	/* xmc2_0:1 */
-	169,	/* xmc2_1:1 */
-	172,	/* xmc2_2:1 */
-	175,	/* xmc2_3:1 */
-	178,	/* xmc2_4:1 */
-	181,	/* xmc2_5:1 */
-	184,	/* xmc2_6:1 */
-	187,	/* xmc2_7:1 */
-	190,	/* xmc2_8:1 */
-	193,	/* xmc2_9:1 */
-	196,	/* xmc2_10:1 */
-	199,	/* xmc2_11:1 */
-	202,	/* xmc2_12:1 */
-	222,	/* xmc3_0:1 */
-	225,	/* xmc3_1:1 */
-	228,	/* xmc3_2:1 */
-	231,	/* xmc3_3:1 */
-	234,	/* xmc3_4:1 */
-	237,	/* xmc3_5:1 */
-	240,	/* xmc3_6:1 */
-	243,	/* xmc3_7:1 */
-	246,	/* xmc3_8:1 */
-	249,	/* xmc3_9:1 */
-	252,	/* xmc3_10:1 */
-	255,	/* xmc3_11:1 */
-	258,	/* xmc3_12:1 */
-	5,	/* LARc0:0 */
-	10,	/* LARc1:1 */
-	15,	/* LARc2:1 */
-	28,	/* LARc5:1 */
-	32,	/* LARc6:0 */
-	34,	/* LARc7:1 */
-	35,	/* LARc7:0 */
-	16,	/* LARc2:0 */
-	20,	/* LARc3:1 */
-	21,	/* LARc3:0 */
-	25,	/* LARc4:0 */
-	52,	/* Xmaxc0:0 */
-	108,	/* Xmaxc1:0 */
-	164,	/* Xmaxc2:0 */
-	220,	/* Xmaxc3:0 */
-	55,	/* xmc0_0:0 */
-	58,	/* xmc0_1:0 */
-	61,	/* xmc0_2:0 */
-	64,	/* xmc0_3:0 */
-	67,	/* xmc0_4:0 */
-	70,	/* xmc0_5:0 */
-	73,	/* xmc0_6:0 */
-	76,	/* xmc0_7:0 */
-	79,	/* xmc0_8:0 */
-	82,	/* xmc0_9:0 */
-	85,	/* xmc0_10:0 */
-	88,	/* xmc0_11:0 */
-	91,	/* xmc0_12:0 */
-	111,	/* xmc1_0:0 */
-	114,	/* xmc1_1:0 */
-	117,	/* xmc1_2:0 */
-	120,	/* xmc1_3:0 */
-	123,	/* xmc1_4:0 */
-	126,	/* xmc1_5:0 */
-	129,	/* xmc1_6:0 */
-	132,	/* xmc1_7:0 */
-	135,	/* xmc1_8:0 */
-	138,	/* xmc1_9:0 */
-	141,	/* xmc1_10:0 */
-	144,	/* xmc1_11:0 */
-	147,	/* xmc1_12:0 */
-	167,	/* xmc2_0:0 */
-	170,	/* xmc2_1:0 */
-	173,	/* xmc2_2:0 */
-	176,	/* xmc2_3:0 */
-	179,	/* xmc2_4:0 */
-	182,	/* xmc2_5:0 */
-	185,	/* xmc2_6:0 */
-	188,	/* xmc2_7:0 */
-	191,	/* xmc2_8:0 */
-	194,	/* xmc2_9:0 */
-	197,	/* xmc2_10:0 */
-	200,	/* xmc2_11:0 */
-	203,	/* xmc2_12:0 */
-	223,	/* xmc3_0:0 */
-	226,	/* xmc3_1:0 */
-	229,	/* xmc3_2:0 */
-	232,	/* xmc3_3:0 */
-	235,	/* xmc3_4:0 */
-	238,	/* xmc3_5:0 */
-	241,	/* xmc3_6:0 */
-	244,	/* xmc3_7:0 */
-	247,	/* xmc3_8:0 */
-	250,	/* xmc3_9:0 */
-	253,	/* xmc3_10:0 */
-	256,	/* xmc3_11:0 */
-	259,	/* xmc3_12:0 */
-	11,	/* LARc1:0 */
-	29,	/* LARc5:0 */
-};
-
-static int
-msb_get_bit(buf, bn)
-	u_char *buf;
-{
-	int pos_byte = bn >> 3;
-	int pos_bit  = 7 - (bn & 7);
-
-	return (buf[pos_byte] >> pos_bit) & 1;
-}
-
-static void
-msb_set_bit(buf, bn, bit)
-	u_char *buf;
-{
-	int pos_byte = bn >> 3;
-	int pos_bit  = 7 - (bn & 7);
-
-	buf[pos_byte] |= (bit << pos_bit);
-}
-
-void
-gsm0610_tidsp_to_libgsm(inbytes, outbytes)
-	u_char *inbytes, *outbytes;
-{
-	int i, di, si;
-
-	bzero(outbytes, 33);
-	outbytes[0] = 0xD0;
-	for (i = 0; i < 260; i++) {
-		di = gsm0610_bitorder[i];
-		si = (i > 181) ? i + 4 : i;
-		msb_set_bit(outbytes, 4 + di, msb_get_bit(inbytes, si));
-        }
-}
-
-void
-gsm0610_libgsm_to_tidsp(inbytes, outbytes)
-	u_char *inbytes, *outbytes;
-{
-	int i, di, si;
-
-	bzero(outbytes, 33);
-	for (i = 0; i < 260; i++) {
-		si = gsm0610_bitorder[i];
-		di = (i > 181) ? i + 4 : i;
-		msb_set_bit(outbytes, di, msb_get_bit(inbytes, 4 + si));
-        }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tchtools/Makefile	Wed Dec 28 07:52:30 2022 +0000
@@ -0,0 +1,32 @@
+CC=	gcc
+CFLAGS=	-O2
+PROGS=	fc-fr2tch fc-gsm2vm fc-tch2fr fc-vm2hex
+
+INSTALL_PREFIX=	/opt/freecalypso
+
+INSTBIN=${INSTALL_PREFIX}/bin
+
+all:	${PROGS}
+
+FR2TCH_OBJS=	fc-fr2tch.o gsm0610.o
+GSM2VM_OBJS=	fc-gsm2vm.o gsm0610.o
+TCH2FR_OBJS=	fc-tch2fr.o gsm0610.o
+
+fc-fr2tch:	${FR2TCH_OBJS}
+	${CC} ${CFLAGS} -o $@ ${FR2TCH_OBJS}
+
+fc-gsm2vm:	${GSM2VM_OBJS}
+	${CC} ${CFLAGS} -o $@ ${GSM2VM_OBJS}
+
+fc-tch2fr:	${TCH2FR_OBJS}
+	${CC} ${CFLAGS} -o $@ ${TCH2FR_OBJS}
+
+fc-vm2hex:	fc-vm2hex.c
+	${CC} ${CFLAGS} -o $@ $@.c
+
+install:
+	mkdir -p ${INSTBIN}
+	install -c ${PROGS} ${INSTBIN}
+
+clean:
+	rm -f ${PROGS} *.o *errs *.out
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tchtools/fc-fr2tch.c	Wed Dec 28 07:52:30 2022 +0000
@@ -0,0 +1,59 @@
+/*
+ * This utility converts a GSM 06.10 speech recording from the format that is
+ * commonly accepted as standard in the Unix/Linux world (libgsm format) into
+ * hex strings of TCH bits to be fed to the GSM 05.03 channel encoder by way
+ * of a TI Calypso GSM device, a FreeCalypso GSM firmware version with the
+ * TCH rerouting feature, and fc-shell's tch play command.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+main(argc, argv)
+	char **argv;
+{
+	FILE *inf, *outf;
+	u_char libgsm_bytes[33], tidsp_bytes[33];
+	int cc, i, gotsome = 0;
+
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s infile outfile\n", argv[0]);
+		exit(1);
+	}
+	inf = fopen(argv[1], "r");
+	if (!inf) {
+		perror(argv[1]);
+		exit(1);
+	}
+	outf = fopen(argv[2], "w");
+	if (!outf) {
+		perror(argv[2]);
+		exit(1);
+	}
+	for (;;) {
+		cc = fread(libgsm_bytes, 1, 33, inf);
+		if (cc < 33)
+			break;
+		if ((libgsm_bytes[0] & 0xF0) != 0xD0) {
+invalid:		fprintf(stderr, "error: %s is not in libgsm format\n",
+				argv[1]);
+			exit(1);
+		}
+		gsm0610_libgsm_to_tidsp(libgsm_bytes, tidsp_bytes);
+		for (i = 0; i < 33; i++)
+			fprintf(outf, "%02X", tidsp_bytes[i]);
+		putc('\n', outf);
+		gotsome = 1;
+	}
+	fclose(outf);
+	if (cc) {
+		if (gotsome)
+			fprintf(stderr,
+			"warning: extra non-33 bytes at the end of %s\n",
+				argv[1]);
+		else
+			goto invalid;
+	}
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tchtools/fc-gsm2vm.c	Wed Dec 28 07:52:30 2022 +0000
@@ -0,0 +1,66 @@
+/*
+ * This utility converts a GSM 06.10 speech recording from the format that is
+ * commonly accepted as standard in the Unix/Linux world (libgsm format) into
+ * a voice memo file that can be uploaded into the FFS of a FreeCalypso device
+ * and played with the audio_vm_play_start() API or the AT@VMP command that
+ * invokes the latter.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+main(argc, argv)
+	char **argv;
+{
+	FILE *inf, *outf;
+	u_char libgsm_bytes[33], tidsp_bytes[34], arm_bytes[34];
+	static u_char header_bytes[6] = {0x00, 0xC4, 0x00, 0x00, 0x00, 0x00};
+	static u_char endmarker[2] = {0xFF, 0xFB};
+	int cc, i, gotsome = 0;
+
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s infile outfile\n", argv[0]);
+		exit(1);
+	}
+	inf = fopen(argv[1], "r");
+	if (!inf) {
+		perror(argv[1]);
+		exit(1);
+	}
+	outf = fopen(argv[2], "w");
+	if (!outf) {
+		perror(argv[2]);
+		exit(1);
+	}
+	tidsp_bytes[33] = 0;
+	for (;;) {
+		cc = fread(libgsm_bytes, 1, 33, inf);
+		if (cc < 33)
+			break;
+		if ((libgsm_bytes[0] & 0xF0) != 0xD0) {
+invalid:		fprintf(stderr, "error: %s is not in libgsm format\n",
+				argv[1]);
+			exit(1);
+		}
+		gsm0610_libgsm_to_tidsp(libgsm_bytes, tidsp_bytes);
+		for (i = 0; i < 34; i += 2) {
+			arm_bytes[i] = tidsp_bytes[i+1];
+			arm_bytes[i+1] = tidsp_bytes[i];
+		}
+		fwrite(header_bytes, 1, 6, outf);
+		fwrite(arm_bytes, 1, 34, outf);
+		gotsome = 1;
+	}
+	fwrite(endmarker, 1, 2, outf);
+	fclose(outf);
+	if (cc) {
+		if (gotsome)
+			fprintf(stderr,
+			"warning: extra non-33 bytes at the end of %s\n",
+				argv[1]);
+		else
+			goto invalid;
+	}
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tchtools/fc-tch2fr.c	Wed Dec 28 07:52:30 2022 +0000
@@ -0,0 +1,94 @@
+/*
+ * Our experimental Calypso firmware enables us to capture the output of
+ * the GSM 05.03 channel decoder in the DSP, i.e., the bits leaving the
+ * channel decoder and going into the speech decoder.  Our fc-shell utility
+ * allows saving this capture to a file; the captured booty includes not only
+ * the expected 260 bits per frame, but also some DSP status words which are
+ * not fully understood, but which are believed to contain indications as to
+ * whether the decoded speech frame is good or bad.
+ *
+ * My first naive thought was to save the captured speech frames in libgsm
+ * format so I could then play them with the 'play' command (SoX package)
+ * under Linux, but the problem with this naive approach is that the bad frames
+ * indication is lost, and some of the saved "speech" frames will contain
+ * utter garbage, resulting in very unkind-on-ears noises if that file is
+ * then played.  I don't know what the proper solution should be; I don't know
+ * what the commercial cellphone implementations of the GSM 06.10 speech decoder
+ * (buried in black box DSPs) do when they get bad frames from the channel
+ * decoder.
+ *
+ * The present utility reproduces the naive behaviour of my previous
+ * implementation of fc-shell's tch record command: it takes hex files written
+ * by the current implementation of tch record in fc-shell, DISREGARDS the
+ * DSP status words, and blindly converts each 260-bit frame (good or bad)
+ * into libgsm format.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static
+decode_hex_digit(ch)
+{
+	if (isdigit(ch))
+		return(ch - '0');
+	else if (isupper(ch))
+		return(ch - 'A' + 10);
+	else
+		return(ch - 'a' + 10);
+}
+
+main(argc, argv)
+	char **argv;
+{
+	FILE *inf, *outf;
+	char linebuf[128];
+	int lineno;
+	char *cp;
+	int i, j;
+	u_char tidsp_bytes[33], libgsm_bytes[33];
+
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s infile outfile\n", argv[0]);
+		exit(1);
+	}
+	inf = fopen(argv[1], "r");
+	if (!inf) {
+		perror(argv[1]);
+		exit(1);
+	}
+	outf = fopen(argv[2], "w");
+	if (!outf) {
+		perror(argv[2]);
+		exit(1);
+	}
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) {
+		/* skip DSP status words */
+		cp = linebuf;
+		for (i = 0; i < 3; i++) {
+			for (j = 0; j < 4; j++) {
+				if (!isxdigit(*cp++)) {
+invalid:				fprintf(stderr,
+				    "error: %s is not in the expected format\n",
+						argv[1]);
+					exit(1);
+				}
+			}
+			if (*cp++ != ' ')
+				goto invalid;
+		}
+		/* read the frame bits */
+		for (i = 0; i < 33; i++) {
+			if (!isxdigit(cp[0]) || !isxdigit(cp[1]))
+				goto invalid;
+			tidsp_bytes[i] = (decode_hex_digit(cp[0]) << 4) |
+					  decode_hex_digit(cp[1]);
+			cp += 2;
+		}
+		gsm0610_tidsp_to_libgsm(tidsp_bytes, libgsm_bytes);
+		fwrite(libgsm_bytes, 1, 33, outf);
+	}
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tchtools/fc-vm2hex.c	Wed Dec 28 07:52:30 2022 +0000
@@ -0,0 +1,86 @@
+/*
+ * This utility converts the old-fashioned (non-AMR) voice memo files
+ * read out of FFS into hex strings that can be analyzed by a human
+ * or further fed to fc-tch2fr.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+char *infname;
+FILE *inf, *outf;
+
+static unsigned
+get_word()
+{
+	u_char b[2];
+	int i, c;
+
+	for (i = 0; i < 2; i++) {
+		c = getc(inf);
+		if (c < 0) {
+			fprintf(stderr, "error: premature EOF in %s\n",
+				infname);
+			exit(1);
+		}
+		b[i] = c;
+	}
+	return((b[1] << 8) | b[0]);
+}
+
+convert_speech_sample()
+{
+	u_char bytes[34];
+	int i, dp;
+	unsigned word;
+
+	dp = 0;
+	for (i = 0; i < 17; i++) {
+		word = get_word();
+		bytes[dp++] = word >> 8;
+		bytes[dp++] = word;
+	}
+	for (i = 0; i < 33; i++)
+		fprintf(outf, "%02X", bytes[i]);
+}
+
+main(argc, argv)
+	char **argv;
+{
+	unsigned first_word;
+
+	if (argc < 2 || argc > 3) {
+		fprintf(stderr, "usage: %s infile [outfile]\n", argv[0]);
+		exit(1);
+	}
+	infname = argv[1];
+	inf = fopen(infname, "r");
+	if (!inf) {
+		perror(infname);
+		exit(1);
+	}
+	if (argc > 2) {
+		outf = fopen(argv[2], "w");
+		if (!outf) {
+			perror(argv[2]);
+			exit(1);
+		}
+	} else
+		outf = stdout;
+
+	for (;;) {
+		first_word = get_word();
+		if (first_word == 0xFBFF)	/* SC_VM_END_MASK */
+			break;
+		fprintf(outf, "%04X", first_word);
+		if (first_word & 0x8000) {	/* B_VM_SPEECH */
+			fprintf(outf, " %04X", get_word());
+			fprintf(outf, " %04X", get_word());
+			putc(' ', outf);
+			convert_speech_sample();
+		}
+		putc('\n', outf);
+	}
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tchtools/gsm0610.c	Wed Dec 28 07:52:30 2022 +0000
@@ -0,0 +1,329 @@
+/*
+ * This code (lifted from OsmocomBB) facilitates the conversion of 33-byte
+ * GSM 06.10 encoded speech frames between the bit order at the 06.10 interface
+ * in the de facto standard libgsm implementation and the bit order at the
+ * 05.03 channel encoder interface, as realized in TI's Calypso DSP.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <strings.h>
+
+/* GSM FR - subjective importance bit ordering */
+	/* This array encodes GSM 05.03 Table 2.
+	 * It's also GSM 06.10 Table A.2.1a
+	 *
+	 * It converts between serial parameter output by the encoder and the
+	 * order needed before channel encoding.
+	 */
+const u_short gsm0610_bitorder[260] = {
+	0,	/* LARc0:5 */
+	47,	/* Xmaxc0:5 */
+	103,	/* Xmaxc1:5 */
+	159,	/* Xmaxc2:5 */
+	215,	/* Xmaxc3:5 */
+	1,	/* LARc0:4 */
+	6,	/* LARc1:5 */
+	12,	/* LARc2:4 */
+	2,	/* LARc0:3 */
+	7,	/* LARc1:4 */
+	13,	/* LARc2:3 */
+	17,	/* LARc3:4 */
+	36,	/* Nc0:6 */
+	92,	/* Nc1:6 */
+	148,	/* Nc2:6 */
+	204,	/* Nc3:6 */
+	48,	/* Xmaxc0:4 */
+	104,	/* Xmaxc1:4 */
+	160,	/* Xmaxc2:4 */
+	216,	/* Xmaxc3:4 */
+	8,	/* LARc1:3 */
+	22,	/* LARc4:3 */
+	26,	/* LARc5:3 */
+	37,	/* Nc0:5 */
+	93,	/* Nc1:5 */
+	149,	/* Nc2:5 */
+	205,	/* Nc3:5 */
+	38,	/* Nc0:4 */
+	94,	/* Nc1:4 */
+	150,	/* Nc2:4 */
+	206,	/* Nc3:4 */
+	39,	/* Nc0:3 */
+	95,	/* Nc1:3 */
+	151,	/* Nc2:3 */
+	207,	/* Nc3:3 */
+	40,	/* Nc0:2 */
+	96,	/* Nc1:2 */
+	152,	/* Nc2:2 */
+	208,	/* Nc3:2 */
+	49,	/* Xmaxc0:3 */
+	105,	/* Xmaxc1:3 */
+	161,	/* Xmaxc2:3 */
+	217,	/* Xmaxc3:3 */
+	3,	/* LARc0:2 */
+	18,	/* LARc3:3 */
+	30,	/* LARc6:2 */
+	41,	/* Nc0:1 */
+	97,	/* Nc1:1 */
+	153,	/* Nc2:1 */
+	209,	/* Nc3:1 */
+	23,	/* LARc4:2 */
+	27,	/* LARc5:2 */
+	43,	/* bc0:1 */
+	99,	/* bc1:1 */
+	155,	/* bc2:1 */
+	211,	/* bc3:1 */
+	42,	/* Nc0:0 */
+	98,	/* Nc1:0 */
+	154,	/* Nc2:0 */
+	210,	/* Nc3:0 */
+	45,	/* Mc0:1 */
+	101,	/* Mc1:1 */
+	157,	/* Mc2:1 */
+	213,	/* Mc3:1 */
+	4,	/* LARc0:1 */
+	9,	/* LARc1:2 */
+	14,	/* LARc2:2 */
+	33,	/* LARc7:2 */
+	19,	/* LARc3:2 */
+	24,	/* LARc4:1 */
+	31,	/* LARc6:1 */
+	44,	/* bc0:0 */
+	100,	/* bc1:0 */
+	156,	/* bc2:0 */
+	212,	/* bc3:0 */
+	50,	/* Xmaxc0:2 */
+	106,	/* Xmaxc1:2 */
+	162,	/* Xmaxc2:2 */
+	218,	/* Xmaxc3:2 */
+	53,	/* xmc0_0:2 */
+	56,	/* xmc0_1:2 */
+	59,	/* xmc0_2:2 */
+	62,	/* xmc0_3:2 */
+	65,	/* xmc0_4:2 */
+	68,	/* xmc0_5:2 */
+	71,	/* xmc0_6:2 */
+	74,	/* xmc0_7:2 */
+	77,	/* xmc0_8:2 */
+	80,	/* xmc0_9:2 */
+	83,	/* xmc0_10:2 */
+	86,	/* xmc0_11:2 */
+	89,	/* xmc0_12:2 */
+	109,	/* xmc1_0:2 */
+	112,	/* xmc1_1:2 */
+	115,	/* xmc1_2:2 */
+	118,	/* xmc1_3:2 */
+	121,	/* xmc1_4:2 */
+	124,	/* xmc1_5:2 */
+	127,	/* xmc1_6:2 */
+	130,	/* xmc1_7:2 */
+	133,	/* xmc1_8:2 */
+	136,	/* xmc1_9:2 */
+	139,	/* xmc1_10:2 */
+	142,	/* xmc1_11:2 */
+	145,	/* xmc1_12:2 */
+	165,	/* xmc2_0:2 */
+	168,	/* xmc2_1:2 */
+	171,	/* xmc2_2:2 */
+	174,	/* xmc2_3:2 */
+	177,	/* xmc2_4:2 */
+	180,	/* xmc2_5:2 */
+	183,	/* xmc2_6:2 */
+	186,	/* xmc2_7:2 */
+	189,	/* xmc2_8:2 */
+	192,	/* xmc2_9:2 */
+	195,	/* xmc2_10:2 */
+	198,	/* xmc2_11:2 */
+	201,	/* xmc2_12:2 */
+	221,	/* xmc3_0:2 */
+	224,	/* xmc3_1:2 */
+	227,	/* xmc3_2:2 */
+	230,	/* xmc3_3:2 */
+	233,	/* xmc3_4:2 */
+	236,	/* xmc3_5:2 */
+	239,	/* xmc3_6:2 */
+	242,	/* xmc3_7:2 */
+	245,	/* xmc3_8:2 */
+	248,	/* xmc3_9:2 */
+	251,	/* xmc3_10:2 */
+	254,	/* xmc3_11:2 */
+	257,	/* xmc3_12:2 */
+	46,	/* Mc0:0 */
+	102,	/* Mc1:0 */
+	158,	/* Mc2:0 */
+	214,	/* Mc3:0 */
+	51,	/* Xmaxc0:1 */
+	107,	/* Xmaxc1:1 */
+	163,	/* Xmaxc2:1 */
+	219,	/* Xmaxc3:1 */
+	54,	/* xmc0_0:1 */
+	57,	/* xmc0_1:1 */
+	60,	/* xmc0_2:1 */
+	63,	/* xmc0_3:1 */
+	66,	/* xmc0_4:1 */
+	69,	/* xmc0_5:1 */
+	72,	/* xmc0_6:1 */
+	75,	/* xmc0_7:1 */
+	78,	/* xmc0_8:1 */
+	81,	/* xmc0_9:1 */
+	84,	/* xmc0_10:1 */
+	87,	/* xmc0_11:1 */
+	90,	/* xmc0_12:1 */
+	110,	/* xmc1_0:1 */
+	113,	/* xmc1_1:1 */
+	116,	/* xmc1_2:1 */
+	119,	/* xmc1_3:1 */
+	122,	/* xmc1_4:1 */
+	125,	/* xmc1_5:1 */
+	128,	/* xmc1_6:1 */
+	131,	/* xmc1_7:1 */
+	134,	/* xmc1_8:1 */
+	137,	/* xmc1_9:1 */
+	140,	/* xmc1_10:1 */
+	143,	/* xmc1_11:1 */
+	146,	/* xmc1_12:1 */
+	166,	/* xmc2_0:1 */
+	169,	/* xmc2_1:1 */
+	172,	/* xmc2_2:1 */
+	175,	/* xmc2_3:1 */
+	178,	/* xmc2_4:1 */
+	181,	/* xmc2_5:1 */
+	184,	/* xmc2_6:1 */
+	187,	/* xmc2_7:1 */
+	190,	/* xmc2_8:1 */
+	193,	/* xmc2_9:1 */
+	196,	/* xmc2_10:1 */
+	199,	/* xmc2_11:1 */
+	202,	/* xmc2_12:1 */
+	222,	/* xmc3_0:1 */
+	225,	/* xmc3_1:1 */
+	228,	/* xmc3_2:1 */
+	231,	/* xmc3_3:1 */
+	234,	/* xmc3_4:1 */
+	237,	/* xmc3_5:1 */
+	240,	/* xmc3_6:1 */
+	243,	/* xmc3_7:1 */
+	246,	/* xmc3_8:1 */
+	249,	/* xmc3_9:1 */
+	252,	/* xmc3_10:1 */
+	255,	/* xmc3_11:1 */
+	258,	/* xmc3_12:1 */
+	5,	/* LARc0:0 */
+	10,	/* LARc1:1 */
+	15,	/* LARc2:1 */
+	28,	/* LARc5:1 */
+	32,	/* LARc6:0 */
+	34,	/* LARc7:1 */
+	35,	/* LARc7:0 */
+	16,	/* LARc2:0 */
+	20,	/* LARc3:1 */
+	21,	/* LARc3:0 */
+	25,	/* LARc4:0 */
+	52,	/* Xmaxc0:0 */
+	108,	/* Xmaxc1:0 */
+	164,	/* Xmaxc2:0 */
+	220,	/* Xmaxc3:0 */
+	55,	/* xmc0_0:0 */
+	58,	/* xmc0_1:0 */
+	61,	/* xmc0_2:0 */
+	64,	/* xmc0_3:0 */
+	67,	/* xmc0_4:0 */
+	70,	/* xmc0_5:0 */
+	73,	/* xmc0_6:0 */
+	76,	/* xmc0_7:0 */
+	79,	/* xmc0_8:0 */
+	82,	/* xmc0_9:0 */
+	85,	/* xmc0_10:0 */
+	88,	/* xmc0_11:0 */
+	91,	/* xmc0_12:0 */
+	111,	/* xmc1_0:0 */
+	114,	/* xmc1_1:0 */
+	117,	/* xmc1_2:0 */
+	120,	/* xmc1_3:0 */
+	123,	/* xmc1_4:0 */
+	126,	/* xmc1_5:0 */
+	129,	/* xmc1_6:0 */
+	132,	/* xmc1_7:0 */
+	135,	/* xmc1_8:0 */
+	138,	/* xmc1_9:0 */
+	141,	/* xmc1_10:0 */
+	144,	/* xmc1_11:0 */
+	147,	/* xmc1_12:0 */
+	167,	/* xmc2_0:0 */
+	170,	/* xmc2_1:0 */
+	173,	/* xmc2_2:0 */
+	176,	/* xmc2_3:0 */
+	179,	/* xmc2_4:0 */
+	182,	/* xmc2_5:0 */
+	185,	/* xmc2_6:0 */
+	188,	/* xmc2_7:0 */
+	191,	/* xmc2_8:0 */
+	194,	/* xmc2_9:0 */
+	197,	/* xmc2_10:0 */
+	200,	/* xmc2_11:0 */
+	203,	/* xmc2_12:0 */
+	223,	/* xmc3_0:0 */
+	226,	/* xmc3_1:0 */
+	229,	/* xmc3_2:0 */
+	232,	/* xmc3_3:0 */
+	235,	/* xmc3_4:0 */
+	238,	/* xmc3_5:0 */
+	241,	/* xmc3_6:0 */
+	244,	/* xmc3_7:0 */
+	247,	/* xmc3_8:0 */
+	250,	/* xmc3_9:0 */
+	253,	/* xmc3_10:0 */
+	256,	/* xmc3_11:0 */
+	259,	/* xmc3_12:0 */
+	11,	/* LARc1:0 */
+	29,	/* LARc5:0 */
+};
+
+static int
+msb_get_bit(buf, bn)
+	u_char *buf;
+{
+	int pos_byte = bn >> 3;
+	int pos_bit  = 7 - (bn & 7);
+
+	return (buf[pos_byte] >> pos_bit) & 1;
+}
+
+static void
+msb_set_bit(buf, bn, bit)
+	u_char *buf;
+{
+	int pos_byte = bn >> 3;
+	int pos_bit  = 7 - (bn & 7);
+
+	buf[pos_byte] |= (bit << pos_bit);
+}
+
+void
+gsm0610_tidsp_to_libgsm(inbytes, outbytes)
+	u_char *inbytes, *outbytes;
+{
+	int i, di, si;
+
+	bzero(outbytes, 33);
+	outbytes[0] = 0xD0;
+	for (i = 0; i < 260; i++) {
+		di = gsm0610_bitorder[i];
+		si = (i > 181) ? i + 4 : i;
+		msb_set_bit(outbytes, 4 + di, msb_get_bit(inbytes, si));
+        }
+}
+
+void
+gsm0610_libgsm_to_tidsp(inbytes, outbytes)
+	u_char *inbytes, *outbytes;
+{
+	int i, di, si;
+
+	bzero(outbytes, 33);
+	for (i = 0; i < 260; i++) {
+		si = gsm0610_bitorder[i];
+		di = (i > 181) ? i + 4 : i;
+		msb_set_bit(outbytes, di, msb_get_bit(inbytes, 4 + si));
+        }
+}