diff --git a/Makefile.defs.in b/Makefile.defs.in index af20d76..7d7579b 100644 --- a/Makefile.defs.in +++ b/Makefile.defs.in @@ -34,7 +34,7 @@ CFLAGS += -fstack-protector-all CFLAGS += -Wall CFLAGS += -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations CFLAGS += -Wdeclaration-after-statement -Wsign-compare -Winit-self -CFLAGS += -Wformat-nonliteral -Wformat-security -Wmissing-format-attribute +CFLAGS += -Wformat-nonliteral -Wformat-security # -Wmissing-format-attribute CFLAGS += -Wcast-align -Wundef -Wbad-function-cast # -Wshadow CFLAGS += -Waggregate-return -Wunused -Wwrite-strings diff --git a/configure.ac b/configure.ac index de64a46..5f939d8 100644 --- a/configure.ac +++ b/configure.ac @@ -25,6 +25,8 @@ AC_PROG_MKDIR_P AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_SED +AC_PROG_LEX +AC_PROG_YACC AC_ARG_ENABLE([doc], AS_HELP_STRING([--enable-doc], [build documentation [no]]), @@ -47,6 +49,12 @@ AC_CHECK_LIB(ev, event_init, EVENT_LDFLAGS=$EVENTLIB AC_SUBST(EVENT_LDFLAGS) +AC_CHECK_LIB([readline], [rl_callback_handler_install], , + AC_MSG_ERROR([No suitable version of libreadline found])) + +AC_CHECK_LIB([SDL], [SDL_Init], , + AC_MSG_ERROR([No suitable version of libSDL found])) + # Checks for header files. AC_HEADER_STDC AC_HEADER_ASSERT diff --git a/include/cli.h b/include/cli.h new file mode 100644 index 0000000..f288719 --- /dev/null +++ b/include/cli.h @@ -0,0 +1,37 @@ +#ifndef DECTMON_CLI_H +#define DECTMON_CLI_H + +#define YYLTYPE struct location +#define YYLTYPE_IS_TRIVIAL 0 +#define YYENABLE_NLS 0 + +struct location { + off_t token_offset; + off_t line_offset; + + unsigned int first_line; + unsigned int last_line; + unsigned int first_column; + unsigned int last_column; +}; + +struct parser_state { + unsigned int lineno; + unsigned int column; + off_t token_offset; + off_t line_offset; +}; + +extern void parser_init(struct parser_state *state); +extern int yyparse(void *, struct parser_state *state); + +extern void *scanner_init(struct parser_state *state); +extern void scanner_destroy(struct parser_state *state); + +extern void scanner_push_buffer(void *scanner, const char *buffer); + +extern void cli_display(const char *fmt, va_list ap); +extern int cli_init(FILE *file); +extern void cli_exit(void); + +#endif /* DECTMON_CLI_H */ diff --git a/include/dectmon.h b/include/dectmon.h index f916970..5a4684c 100644 --- a/include/dectmon.h +++ b/include/dectmon.h @@ -1,6 +1,7 @@ #ifndef _DECTMON_H #define _DECTMON_H +#include #include #include #include @@ -17,6 +18,7 @@ enum { extern const char *auth_pin; extern uint32_t dumpopts; +extern uint32_t debug_mask; struct dect_ops; extern int dect_event_ops_init(struct dect_ops *ops); @@ -25,15 +27,21 @@ extern void dect_event_loop(void); extern void dect_event_ops_cleanup(void); extern void dect_dummy_ops_init(struct dect_ops *ops); +extern void dectmon_log(const char *fmt, ...); extern void dect_hexdump(const char *prefix, const uint8_t *buf, size_t size); +extern struct list_head dect_handles; + struct dect_handle_priv { struct list_head list; const char *cluster; - struct dect_timer *lock_timer; - struct dect_ari pari; - struct list_head pt_list; + struct dect_handle *dh; + struct dect_timer *lock_timer; + bool locked; + struct dect_ari pari; + + struct list_head pt_list; struct dect_tbc *slots[DECT_FRAME_SIZE]; }; @@ -52,6 +60,8 @@ struct dect_pt { uint8_t uak[DECT_AUTH_KEY_LEN]; uint8_t dck[DECT_CIPHER_KEY_LEN]; + struct dect_audio_handle *ah; + enum dect_mm_procedures procedure; uint8_t last_msg; @@ -72,6 +82,9 @@ struct dect_msg_buf; extern void dect_dl_data_ind(struct dect_handle *dh, struct dect_dl *dl, struct dect_msg_buf *mb); +extern void dect_dl_u_data_ind(struct dect_handle *dh, struct dect_dl *dl, + bool dir, struct dect_msg_buf *mb); + struct dect_lc { uint16_t lsig; struct dect_msg_buf *rx_buf; @@ -121,4 +134,19 @@ extern void dect_dsc_keystream(uint64_t iv, const uint8_t *key, uint8_t *output, unsigned int len); extern uint64_t dect_dsc_iv(uint32_t mfn, uint8_t framenum); +/* Audio */ + +#include "../src/ccitt-adpcm/g72x.h" + +struct dect_audio_handle { + struct g72x_state codec[2]; + struct dect_msg_buf *queue[2]; +}; + +extern int dect_audio_init(void); +extern struct dect_audio_handle *dect_audio_open(void); +extern void dect_audio_close(struct dect_audio_handle *ah); +extern void dect_audio_queue(struct dect_audio_handle *ah, unsigned int queue, + struct dect_msg_buf *mb); + #endif /* _DECTMON_H */ diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 0000000..5a7bbcb --- /dev/null +++ b/include/utils.h @@ -0,0 +1,76 @@ +#ifndef _DECTMON_UTILS_H +#define _DECTMON_UTILS_H + +#include +#include +#include + +#define __init __attribute__((constructor)) +#define __exit __attribute__((destructor)) +#define __must_check __attribute__((warn_unused_result)) +#define __maybe_unused __attribute__((unused)) +#define __noreturn __attribute__((__noreturn__)) +#define __aligned(x) __attribute__((aligned(x))) +#define __packed __attribute__((packed)) +#define __visible __attribute__((visibility("default"))) + +#define BUG() assert(0) + +/* Force a compilation error if condition is true */ +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) +#define BUILD_BUG_ON_ZERO(e) (sizeof(char[1 - 2 * !!(e)]) - 1) + +#define __must_be_array(a) \ + BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(typeof(a), typeof(&a[0]))) + +#define array_size(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) +#define field_sizeof(t, f) (sizeof(((t *)NULL)->f)) + +#define div_round_up(n, d) (((n) + (d) - 1) / (d)) + +#define min(x, y) ({ \ + typeof(x) _min1 = (x); \ + typeof(y) _min2 = (y); \ + (void) (&_min1 == &_min2); \ + _min1 < _min2 ? _min1 : _min2; }) + +#define max(x, y) ({ \ + typeof(x) _max1 = (x); \ + typeof(y) _max2 = (y); \ + (void) (&_max1 == &_max2); \ + _max1 > _max2 ? _max1 : _max2; }) + +static inline unsigned int fls(uint64_t v) +{ + unsigned int len = 0; + + while (v) { + v >>= 1; + len++; + } + return len; +} + +#define ptrlist_init(head) \ + do { \ + *(head) = NULL; \ + } while (0) + +#define ptrlist_add_tail(new, head) \ + do { \ + typeof(new) *pprev; \ + pprev = (head); \ + while (*pprev != NULL) \ + pprev = &(*pprev)->next; \ + *pprev = new; \ + } while (0) + +#define ptrlist_dequeue_head(head) \ + ({ \ + typeof(*head) elem = *(head); \ + if (elem != NULL) \ + *(head) = elem->next; \ + elem; \ + }) + +#endif /* _DECTMON_UTILS_H */ diff --git a/src/.gitignore b/src/.gitignore index f9e7749..5932393 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1 +1,4 @@ +cmd-parser.[ch] +cmd-scanner.[ch] + dectmon diff --git a/src/Makefile.in b/src/Makefile.in index c89c94c..560b588 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -10,4 +10,12 @@ dectmon-obj += dsc.o dectmon-obj += mac.o dectmon-obj += dlc.o dectmon-obj += nwk.o +dectmon-obj += cmd-scanner.o +dectmon-obj += cmd-parser.o +dectmon-obj += cli.o +dectmon-obj += audio.o dectmon-obj += main.o + +dectmon-obj += ccitt-adpcm/g711.o +dectmon-obj += ccitt-adpcm/g72x.o +dectmon-obj += ccitt-adpcm/g721.o diff --git a/src/audio.c b/src/audio.c new file mode 100644 index 0000000..60338e2 --- /dev/null +++ b/src/audio.c @@ -0,0 +1,120 @@ +#include +#include + +#include +#include + +void dect_audio_queue(struct dect_audio_handle *ah, unsigned int queue, + struct dect_msg_buf *mb) +{ + SDL_LockAudio(); + ptrlist_add_tail(mb, &ah->queue[queue]); + SDL_UnlockAudio(); +} + +static void dect_decode_g721(struct g72x_state *codec, + int16_t *dst, const uint8_t *src, + unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len * 2; i += 2) { + dst[i + 0] = g721_decoder(src[i / 2] >> 4, + AUDIO_ENCODING_LINEAR, codec); + dst[i + 1] = g721_decoder(src[i / 2] & 0x0f, + AUDIO_ENCODING_LINEAR, codec); + } +} + +static void dect_audio_dequeue(void *data, uint8_t *stream, int len) +{ + struct dect_audio_handle *ah = data; + struct dect_msg_buf *mb; + int16_t buf[len], *dptr; + unsigned int i, copy, n; + + len /= 4; + for (i = 0; i < array_size(ah->queue); i++) { + dptr = buf; + n = len; + + while (1) { + if (ah->queue[i] == NULL) { + dectmon_log("audio underrun queue %u, missing %u bytes\n", + i, n * 4); + memset(dptr, 0, n * 4); + break; + } + + mb = ah->queue[i]; + copy = mb->len; + if (copy > n) + copy = n; + + dect_decode_g721(&ah->codec[i], dptr, mb->data, copy); + dect_mbuf_pull(mb, copy); + if (mb->len == 0) { + ah->queue[i] = mb->next; + free(mb); + } + + n -= copy; + if (n == 0) + break; + dptr += 2 * copy; + } + SDL_MixAudio(stream, (uint8_t *)buf, 4 * len, SDL_MIX_MAXVOLUME); + } +} + +struct dect_audio_handle *dect_audio_open(void) +{ + struct dect_audio_handle *ah; + SDL_AudioSpec spec = { + .freq = 8000, + .format = AUDIO_S16SYS, + .channels = 1, + .samples = 512, + .callback = dect_audio_dequeue, + }; + + ah = malloc(sizeof(*ah)); + if (ah == NULL) + goto err1; + + ptrlist_init(&ah->queue[0]); + g72x_init_state(&ah->codec[0]); + + ptrlist_init(&ah->queue[1]); + g72x_init_state(&ah->codec[1]); + + spec.userdata = ah; + if (SDL_OpenAudio(&spec, NULL) < 0) + goto err2; + SDL_PauseAudio(0); + + return ah; + +err2: + free(ah); +err1: + return NULL; +} + +void dect_audio_close(struct dect_audio_handle *ah) +{ + struct dect_msg_buf *mb; + unsigned int i; + + SDL_CloseAudio(); + for (i = 0; i < array_size(ah->queue); i++) { + while ((mb = ptrlist_dequeue_head(&ah->queue[i])) != NULL) + free(mb); + } + free(ah); +} + +int dect_audio_init(void) +{ + return SDL_Init(SDL_INIT_AUDIO); +} diff --git a/src/ccitt-adpcm/README b/src/ccitt-adpcm/README new file mode 100644 index 0000000..23b0e7d --- /dev/null +++ b/src/ccitt-adpcm/README @@ -0,0 +1,94 @@ +The files in this directory comprise ANSI-C language reference implementations +of the CCITT (International Telegraph and Telephone Consultative Committee) +G.711, G.721 and G.723 voice compressions. They have been tested on Sun +SPARCstations and passed 82 out of 84 test vectors published by CCITT +(Dec. 20, 1988) for G.721 and G.723. [The two remaining test vectors, +which the G.721 decoder implementation for u-law samples did not pass, +may be in error because they are identical to two other vectors for G.723_40.] + +This source code is released by Sun Microsystems, Inc. to the public domain. +Please give your acknowledgement in product literature if this code is used +in your product implementation. + +Sun Microsystems supports some CCITT audio formats in Solaris 2.0 system +software. However, Sun's implementations have been optimized for higher +performance on SPARCstations. + + +The source files for CCITT conversion routines in this directory are: + + g72x.h header file for g721.c, g723_24.c and g723_40.c + g711.c CCITT G.711 u-law and A-law compression + g72x.c common denominator of G.721 and G.723 ADPCM codes + g721.c CCITT G.721 32Kbps ADPCM coder (with g72x.c) + g723_24.c CCITT G.723 24Kbps ADPCM coder (with g72x.c) + g723_40.c CCITT G.723 40Kbps ADPCM coder (with g72x.c) + + +Simple conversions between u-law, A-law, and 16-bit linear PCM are invoked +as follows: + + unsigned char ucode, acode; + short pcm_val; + + ucode = linear2ulaw(pcm_val); + ucode = alaw2ulaw(acode); + + acode = linear2alaw(pcm_val); + acode = ulaw2alaw(ucode); + + pcm_val = ulaw2linear(ucode); + pcm_val = alaw2linear(acode); + + +The other CCITT compression routines are invoked as follows: + + #include "g72x.h" + + struct g72x_state state; + int sample, code; + + g72x_init_state(&state); + code = {g721,g723_24,g723_40}_encoder(sample, coding, &state); + sample = {g721,g723_24,g723_40}_decoder(code, coding, &state); + +where + coding = AUDIO_ENCODING_ULAW for 8-bit u-law samples + AUDIO_ENCODING_ALAW for 8-bit A-law samples + AUDIO_ENCODING_LINEAR for 16-bit linear PCM samples + + + +This directory also includes the following sample programs: + + encode.c CCITT ADPCM encoder + decode.c CCITT ADPCM decoder + Makefile makefile for the sample programs + + +The sample programs contain examples of how to call the various compression +routines and pack/unpack the bits. The sample programs read byte streams from +stdin and write to stdout. The input/output data is raw data (no file header +or other identifying information is embedded). The sample programs are +invoked as follows: + + encode [-3|4|5] [-a|u|l] outfile + decode [-3|4|5] [-a|u|l] outfile +where: + -3 encode to (decode from) G.723 24kbps (3-bit) data + -4 encode to (decode from) G.721 32kbps (4-bit) data [the default] + -5 encode to (decode from) G.723 40kbps (5-bit) data + -a encode from (decode to) A-law data + -u encode from (decode to) u-law data [the default] + -l encode from (decode to) 16-bit linear data + +Examples: + # Read 16-bit linear and output G.721 + encode -4 -l g721file + + # Read 40Kbps G.723 and output A-law + decode -5 -a alawfile + + # Compress and then decompress u-law data using 24Kbps G.723 + encode -3 ulawout + diff --git a/src/ccitt-adpcm/decode.c b/src/ccitt-adpcm/decode.c new file mode 100644 index 0000000..cf8c739 --- /dev/null +++ b/src/ccitt-adpcm/decode.c @@ -0,0 +1,113 @@ +/* + * decode.c + * + * CCITT ADPCM decoder + * + * Usage : decode [-3|4|5] [-a|u|l] < infile > outfile + */ +#include +#include "g72x.h" + + +/* + * Unpack input codes and pass them back as bytes. + * Returns 1 if there is residual input, returns -1 if eof, else returns 0. + */ +int +unpack_input( + unsigned char *code, + int bits) +{ + static unsigned int in_buffer = 0; + static int in_bits = 0; + unsigned char in_byte; + + if (in_bits < bits) { + if (fread(&in_byte, sizeof (char), 1, stdin) != 1) { + *code = 0; + return (-1); + } + in_buffer |= (in_byte << in_bits); + in_bits += 8; + } + *code = in_buffer & ((1 << bits) - 1); + in_buffer >>= bits; + in_bits -= bits; + return (in_bits > 0); +} + + +main( + int argc, + char **argv) +{ + short sample; + unsigned char code; + int n; + struct g72x_state state; + int out_coding; + int out_size; + int (*dec_routine)(); + int dec_bits; + + g72x_init_state(&state); + out_coding = AUDIO_ENCODING_ULAW; + out_size = sizeof (char); + dec_routine = g721_decoder; + dec_bits = 4; + + /* Process encoding argument, if any */ + while ((argc > 1) && (argv[1][0] == '-')) { + switch (argv[1][1]) { + case '3': + dec_routine = g723_24_decoder; + dec_bits = 3; + break; + case '4': + dec_routine = g721_decoder; + dec_bits = 4; + break; + case '5': + dec_routine = g723_40_decoder; + dec_bits = 5; + break; + case 'u': + out_coding = AUDIO_ENCODING_ULAW; + out_size = sizeof (char); + break; + case 'a': + out_coding = AUDIO_ENCODING_ALAW; + out_size = sizeof (char); + break; + case 'l': + out_coding = AUDIO_ENCODING_LINEAR; + out_size = sizeof (short); + break; + default: +fprintf(stderr, "CCITT ADPCM Decoder -- usage:\n"); +fprintf(stderr, "\tdecode [-3|4|5] [-a|u|l] < infile > outfile\n"); +fprintf(stderr, "where:\n"); +fprintf(stderr, "\t-3\tProcess G.723 24kbps (3-bit) input data\n"); +fprintf(stderr, "\t-4\tProcess G.721 32kbps (4-bit) input data [default]\n"); +fprintf(stderr, "\t-5\tProcess G.723 40kbps (5-bit) input data\n"); +fprintf(stderr, "\t-a\tGenerate 8-bit A-law data\n"); +fprintf(stderr, "\t-u\tGenerate 8-bit u-law data [default]\n"); +fprintf(stderr, "\t-l\tGenerate 16-bit linear PCM data\n"); + exit(1); + } + argc--; + argv++; + } + + /* Read and unpack input codes and process them */ + while (unpack_input(&code, dec_bits) >= 0) { + sample = (*dec_routine)(code, out_coding, &state); + if (out_size == 2) { + fwrite(&sample, out_size, 1, stdout); + } else { + code = (unsigned char)sample; + fwrite(&code, out_size, 1, stdout); + } + } + fclose(stdout); +} diff --git a/src/ccitt-adpcm/encode.c b/src/ccitt-adpcm/encode.c new file mode 100644 index 0000000..571fbe8 --- /dev/null +++ b/src/ccitt-adpcm/encode.c @@ -0,0 +1,119 @@ +/* + * encode.c + * + * CCITT ADPCM encoder + * + * Usage : encode [-3|4|5] [-a|u|l] < infile > outfile + */ +#include +#include "g72x.h" + + +/* + * Pack output codes into bytes and write them to stdout. + * Returns 1 if there is residual output, else returns 0. + */ +int +pack_output( + unsigned code, + int bits) +{ + static unsigned int out_buffer = 0; + static int out_bits = 0; + unsigned char out_byte; + + out_buffer |= (code << out_bits); + out_bits += bits; + if (out_bits >= 8) { + out_byte = out_buffer & 0xff; + out_bits -= 8; + out_buffer >>= 8; + fwrite(&out_byte, sizeof (char), 1, stdout); + } + return (out_bits > 0); +} + + +main( + int argc, + char **argv) +{ + struct g72x_state state; + unsigned char sample_char; + short sample_short; + unsigned char code; + int resid; + int in_coding; + int in_size; + unsigned *in_buf; + int (*enc_routine)(); + int enc_bits; + + g72x_init_state(&state); + + /* Set defaults to u-law input, G.721 output */ + in_coding = AUDIO_ENCODING_ULAW; + in_size = sizeof (char); + in_buf = (unsigned *)&sample_char; + enc_routine = g721_encoder; + enc_bits = 4; + + /* Process encoding argument, if any */ + while ((argc > 1) && (argv[1][0] == '-')) { + switch (argv[1][1]) { + case '3': + enc_routine = g723_24_encoder; + enc_bits = 3; + break; + case '4': + enc_routine = g721_encoder; + enc_bits = 4; + break; + case '5': + enc_routine = g723_40_encoder; + enc_bits = 5; + break; + case 'u': + in_coding = AUDIO_ENCODING_ULAW; + in_size = sizeof (char); + in_buf = (unsigned *)&sample_char; + break; + case 'a': + in_coding = AUDIO_ENCODING_ALAW; + in_size = sizeof (char); + in_buf = (unsigned *)&sample_char; + break; + case 'l': + in_coding = AUDIO_ENCODING_LINEAR; + in_size = sizeof (short); + in_buf = (unsigned *)&sample_short; + break; + default: +fprintf(stderr, "CCITT ADPCM Encoder -- usage:\n"); +fprintf(stderr, "\tencode [-3|4|5] [-a|u|l] < infile > outfile\n"); +fprintf(stderr, "where:\n"); +fprintf(stderr, "\t-3\tGenerate G.723 24kbps (3-bit) data\n"); +fprintf(stderr, "\t-4\tGenerate G.721 32kbps (4-bit) data [default]\n"); +fprintf(stderr, "\t-5\tGenerate G.723 40kbps (5-bit) data\n"); +fprintf(stderr, "\t-a\tProcess 8-bit A-law input data\n"); +fprintf(stderr, "\t-u\tProcess 8-bit u-law input data [default]\n"); +fprintf(stderr, "\t-l\tProcess 16-bit linear PCM input data\n"); + exit(1); + } + argc--; + argv++; + } + + /* Read input file and process */ + while (fread(in_buf, in_size, 1, stdin) == 1) { + code = (*enc_routine)(in_size == 2 ? sample_short : sample_char, + in_coding, &state); + resid = pack_output(code, enc_bits); + } + + /* Write zero codes until all residual codes are written out */ + while (resid) { + resid = pack_output(0, enc_bits); + } + fclose(stdout); +} diff --git a/src/ccitt-adpcm/g711.c b/src/ccitt-adpcm/g711.c new file mode 100644 index 0000000..f9eab50 --- /dev/null +++ b/src/ccitt-adpcm/g711.c @@ -0,0 +1,285 @@ +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#include "g72x.h" + +/* + * g711.c + * + * u-law, A-law and linear PCM conversions. + */ +#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ +#define QUANT_MASK (0xf) /* Quantization field mask. */ +#define NSEGS (8) /* Number of A-law segments. */ +#define SEG_SHIFT (4) /* Left shift for segment number. */ +#define SEG_MASK (0x70) /* Segment field mask. */ + +static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF, + 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF}; + +/* copy from CCITT G.711 specifications */ +unsigned char _u2a[128] = { /* u- to A-law conversions */ + 1, 1, 2, 2, 3, 3, 4, 4, + 5, 5, 6, 6, 7, 7, 8, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 27, 29, 31, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, + 46, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128}; + +unsigned char _a2u[128] = { /* A- to u-law conversions */ + 1, 3, 5, 7, 9, 11, 13, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 32, 33, 33, 34, 34, 35, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 48, 49, 49, + 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 64, + 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}; + +static int +search( + int val, + short *table, + int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (val <= *table++) + return (i); + } + return (size); +} + +/* + * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law + * + * linear2alaw() accepts an 16-bit integer and encodes it as A-law data. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +unsigned char +linear2alaw( + int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char aval; + + if (pcm_val >= 0) { + mask = 0xD5; /* sign (7th) bit = 1 */ + } else { + mask = 0x55; /* sign bit = 0 */ + pcm_val = -pcm_val - 8; + } + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm_val, seg_end, 8); + + /* Combine the sign, segment, and quantization bits. */ + + if (seg >= 8) /* out of range, return maximum value. */ + return (0x7F ^ mask); + else { + aval = seg << SEG_SHIFT; + if (seg < 2) + aval |= (pcm_val >> 4) & QUANT_MASK; + else + aval |= (pcm_val >> (seg + 3)) & QUANT_MASK; + return (aval ^ mask); + } +} + +/* + * alaw2linear() - Convert an A-law value to 16-bit linear PCM + * + */ +int +alaw2linear( + unsigned char a_val) +{ + int t; + int seg; + + a_val ^= 0x55; + + t = (a_val & QUANT_MASK) << 4; + seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; + switch (seg) { + case 0: + t += 8; + break; + case 1: + t += 0x108; + break; + default: + t += 0x108; + t <<= seg - 1; + } + return ((a_val & SIGN_BIT) ? t : -t); +} + +#define BIAS (0x84) /* Bias for linear code. */ + +/* + * linear2ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +unsigned char +linear2ulaw( + int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char uval; + + /* Get the sign and the magnitude of the value. */ + if (pcm_val < 0) { + pcm_val = BIAS - pcm_val; + mask = 0x7F; + } else { + pcm_val += BIAS; + mask = 0xFF; + } + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm_val, seg_end, 8); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + if (seg >= 8) /* out of range, return maximum value. */ + return (0x7F ^ mask); + else { + uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF); + return (uval ^ mask); + } + +} + +/* + * ulaw2linear() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +int +ulaw2linear( + unsigned char u_val) +{ + int t; + + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & QUANT_MASK) << 3) + BIAS; + t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; + + return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); +} + +/* A-law to u-law conversion */ +unsigned char +alaw2ulaw( + unsigned char aval) +{ + aval &= 0xff; + return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) : + (0x7F ^ _a2u[aval ^ 0x55])); +} + +/* u-law to A-law conversion */ +unsigned char +ulaw2alaw( + unsigned char uval) +{ + uval &= 0xff; + return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) : + (0x55 ^ (_u2a[0x7F ^ uval] - 1))); +} diff --git a/src/ccitt-adpcm/g721.c b/src/ccitt-adpcm/g721.c new file mode 100644 index 0000000..445f177 --- /dev/null +++ b/src/ccitt-adpcm/g721.c @@ -0,0 +1,173 @@ +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * g721.c + * + * Description: + * + * g721_encoder(), g721_decoder() + * + * These routines comprise an implementation of the CCITT G.721 ADPCM + * coding algorithm. Essentially, this implementation is identical to + * the bit level description except for a few deviations which + * take advantage of work station attributes, such as hardware 2's + * complement arithmetic and large memory. Specifically, certain time + * consuming operations such as multiplications are replaced + * with lookup tables and software 2's complement operations are + * replaced with hardware 2's complement. + * + * The deviation from the bit level specification (lookup tables) + * preserves the bit level performance specifications. + * + * As outlined in the G.721 Recommendation, the algorithm is broken + * down into modules. Each section of code below is preceded by + * the name of the module which it is implementing. + * + */ +#include "g72x.h" + +static short qtab_721[7] = {-124, 80, 178, 246, 300, 349, 400}; +/* + * Maps G.721 code word to reconstructed scale factor normalized log + * magnitude values. + */ +static short _dqlntab[16] = {-2048, 4, 135, 213, 273, 323, 373, 425, + 425, 373, 323, 273, 213, 135, 4, -2048}; + +/* Maps G.721 code word to log of scale factor multiplier. */ +static short _witab[16] = {-12, 18, 41, 64, 112, 198, 355, 1122, + 1122, 355, 198, 112, 64, 41, 18, -12}; +/* + * Maps G.721 code words to a set of values whose long and short + * term averages are computed and then compared to give an indication + * how stationary (steady state) the signal is. + */ +static short _fitab[16] = {0, 0, 0, 0x200, 0x200, 0x200, 0x600, 0xE00, + 0xE00, 0x600, 0x200, 0x200, 0x200, 0, 0, 0}; + +/* + * g721_encoder() + * + * Encodes the input vale of linear PCM, A-law or u-law data sl and returns + * the resulting code. -1 is returned for unknown input coding value. + */ +int +g721_encoder( + int sl, + int in_coding, + struct g72x_state *state_ptr) +{ + short sezi, se, sez; /* ACCUM */ + short d; /* SUBTA */ + short sr; /* ADDB */ + short y; /* MIX */ + short dqsez; /* ADDC */ + short dq, i; + + switch (in_coding) { /* linearize input sample to 14-bit PCM */ + case AUDIO_ENCODING_ALAW: + sl = alaw2linear(sl) >> 2; + break; + case AUDIO_ENCODING_ULAW: + sl = ulaw2linear(sl) >> 2; + break; + case AUDIO_ENCODING_LINEAR: + sl >>= 2; /* 14-bit dynamic range */ + break; + default: + return (-1); + } + + sezi = predictor_zero(state_ptr); + sez = sezi >> 1; + se = (sezi + predictor_pole(state_ptr)) >> 1; /* estimated signal */ + + d = sl - se; /* estimation difference */ + + /* quantize the prediction difference */ + y = step_size(state_ptr); /* quantizer step size */ + i = quantize(d, y, qtab_721, 7); /* i = ADPCM code */ + + dq = reconstruct(i & 8, _dqlntab[i], y); /* quantized est diff */ + + sr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq; /* reconst. signal */ + + dqsez = sr + sez - se; /* pole prediction diff. */ + + update(4, y, _witab[i] << 5, _fitab[i], dq, sr, dqsez, state_ptr); + + return (i); +} + +/* + * g721_decoder() + * + * Description: + * + * Decodes a 4-bit code of G.721 encoded data of i and + * returns the resulting linear PCM, A-law or u-law value. + * return -1 for unknown out_coding value. + */ +int +g721_decoder( + int i, + int out_coding, + struct g72x_state *state_ptr) +{ + short sezi, sei, sez, se; /* ACCUM */ + short y; /* MIX */ + short sr; /* ADDB */ + short dq; + short dqsez; + + i &= 0x0f; /* mask to get proper bits */ + sezi = predictor_zero(state_ptr); + sez = sezi >> 1; + sei = sezi + predictor_pole(state_ptr); + se = sei >> 1; /* se = estimated signal */ + + y = step_size(state_ptr); /* dynamic quantizer step size */ + + dq = reconstruct(i & 0x08, _dqlntab[i], y); /* quantized diff. */ + + sr = (dq < 0) ? (se - (dq & 0x3FFF)) : se + dq; /* reconst. signal */ + + dqsez = sr - se + sez; /* pole prediction diff. */ + + update(4, y, _witab[i] << 5, _fitab[i], dq, sr, dqsez, state_ptr); + + switch (out_coding) { + case AUDIO_ENCODING_ALAW: + return (tandem_adjust_alaw(sr, se, y, i, 8, qtab_721)); + case AUDIO_ENCODING_ULAW: + return (tandem_adjust_ulaw(sr, se, y, i, 8, qtab_721)); + case AUDIO_ENCODING_LINEAR: + return (sr << 2); /* sr was 14-bit dynamic range */ + default: + return (-1); + } +} diff --git a/src/ccitt-adpcm/g723_24.c b/src/ccitt-adpcm/g723_24.c new file mode 100644 index 0000000..452f4da --- /dev/null +++ b/src/ccitt-adpcm/g723_24.c @@ -0,0 +1,158 @@ +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * g723_24.c + * + * Description: + * + * g723_24_encoder(), g723_24_decoder() + * + * These routines comprise an implementation of the CCITT G.723 24 Kbps + * ADPCM coding algorithm. Essentially, this implementation is identical to + * the bit level description except for a few deviations which take advantage + * of workstation attributes, such as hardware 2's complement arithmetic. + * + */ +#include "g72x.h" + +/* + * Maps G.723_24 code word to reconstructed scale factor normalized log + * magnitude values. + */ +static short _dqlntab[8] = {-2048, 135, 273, 373, 373, 273, 135, -2048}; + +/* Maps G.723_24 code word to log of scale factor multiplier. */ +static short _witab[8] = {-128, 960, 4384, 18624, 18624, 4384, 960, -128}; + +/* + * Maps G.723_24 code words to a set of values whose long and short + * term averages are computed and then compared to give an indication + * how stationary (steady state) the signal is. + */ +static short _fitab[8] = {0, 0x200, 0x400, 0xE00, 0xE00, 0x400, 0x200, 0}; + +static short qtab_723_24[3] = {8, 218, 331}; + +/* + * g723_24_encoder() + * + * Encodes a linear PCM, A-law or u-law input sample and returns its 3-bit code. + * Returns -1 if invalid input coding value. + */ +int +g723_24_encoder( + int sl, + int in_coding, + struct g72x_state *state_ptr) +{ + short sei, sezi, se, sez; /* ACCUM */ + short d; /* SUBTA */ + short y; /* MIX */ + short sr; /* ADDB */ + short dqsez; /* ADDC */ + short dq, i; + + switch (in_coding) { /* linearize input sample to 14-bit PCM */ + case AUDIO_ENCODING_ALAW: + sl = alaw2linear(sl) >> 2; + break; + case AUDIO_ENCODING_ULAW: + sl = ulaw2linear(sl) >> 2; + break; + case AUDIO_ENCODING_LINEAR: + sl >>= 2; /* sl of 14-bit dynamic range */ + break; + default: + return (-1); + } + + sezi = predictor_zero(state_ptr); + sez = sezi >> 1; + sei = sezi + predictor_pole(state_ptr); + se = sei >> 1; /* se = estimated signal */ + + d = sl - se; /* d = estimation diff. */ + + /* quantize prediction difference d */ + y = step_size(state_ptr); /* quantizer step size */ + i = quantize(d, y, qtab_723_24, 3); /* i = ADPCM code */ + dq = reconstruct(i & 4, _dqlntab[i], y); /* quantized diff. */ + + sr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq; /* reconstructed signal */ + + dqsez = sr + sez - se; /* pole prediction diff. */ + + update(3, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr); + + return (i); +} + +/* + * g723_24_decoder() + * + * Decodes a 3-bit CCITT G.723_24 ADPCM code and returns + * the resulting 16-bit linear PCM, A-law or u-law sample value. + * -1 is returned if the output coding is unknown. + */ +int +g723_24_decoder( + int i, + int out_coding, + struct g72x_state *state_ptr) +{ + short sezi, sei, sez, se; /* ACCUM */ + short y; /* MIX */ + short sr; /* ADDB */ + short dq; + short dqsez; + + i &= 0x07; /* mask to get proper bits */ + sezi = predictor_zero(state_ptr); + sez = sezi >> 1; + sei = sezi + predictor_pole(state_ptr); + se = sei >> 1; /* se = estimated signal */ + + y = step_size(state_ptr); /* adaptive quantizer step size */ + dq = reconstruct(i & 0x04, _dqlntab[i], y); /* unquantize pred diff */ + + sr = (dq < 0) ? (se - (dq & 0x3FFF)) : (se + dq); /* reconst. signal */ + + dqsez = sr - se + sez; /* pole prediction diff. */ + + update(3, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr); + + switch (out_coding) { + case AUDIO_ENCODING_ALAW: + return (tandem_adjust_alaw(sr, se, y, i, 4, qtab_723_24)); + case AUDIO_ENCODING_ULAW: + return (tandem_adjust_ulaw(sr, se, y, i, 4, qtab_723_24)); + case AUDIO_ENCODING_LINEAR: + return (sr << 2); /* sr was of 14-bit dynamic range */ + default: + return (-1); + } +} diff --git a/src/ccitt-adpcm/g723_40.c b/src/ccitt-adpcm/g723_40.c new file mode 100644 index 0000000..4858baf --- /dev/null +++ b/src/ccitt-adpcm/g723_40.c @@ -0,0 +1,178 @@ +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * g723_40.c + * + * Description: + * + * g723_40_encoder(), g723_40_decoder() + * + * These routines comprise an implementation of the CCITT G.723 40Kbps + * ADPCM coding algorithm. Essentially, this implementation is identical to + * the bit level description except for a few deviations which + * take advantage of workstation attributes, such as hardware 2's + * complement arithmetic. + * + * The deviation from the bit level specification (lookup tables), + * preserves the bit level performance specifications. + * + * As outlined in the G.723 Recommendation, the algorithm is broken + * down into modules. Each section of code below is preceded by + * the name of the module which it is implementing. + * + */ +#include "g72x.h" + +/* + * Maps G.723_40 code word to ructeconstructed scale factor normalized log + * magnitude values. + */ +static short _dqlntab[32] = {-2048, -66, 28, 104, 169, 224, 274, 318, + 358, 395, 429, 459, 488, 514, 539, 566, + 566, 539, 514, 488, 459, 429, 395, 358, + 318, 274, 224, 169, 104, 28, -66, -2048}; + +/* Maps G.723_40 code word to log of scale factor multiplier. */ +static short _witab[32] = {448, 448, 768, 1248, 1280, 1312, 1856, 3200, + 4512, 5728, 7008, 8960, 11456, 14080, 16928, 22272, + 22272, 16928, 14080, 11456, 8960, 7008, 5728, 4512, + 3200, 1856, 1312, 1280, 1248, 768, 448, 448}; + +/* + * Maps G.723_40 code words to a set of values whose long and short + * term averages are computed and then compared to give an indication + * how stationary (steady state) the signal is. + */ +static short _fitab[32] = {0, 0, 0, 0, 0, 0x200, 0x200, 0x200, + 0x200, 0x200, 0x400, 0x600, 0x800, 0xA00, 0xC00, 0xC00, + 0xC00, 0xC00, 0xA00, 0x800, 0x600, 0x400, 0x200, 0x200, + 0x200, 0x200, 0x200, 0, 0, 0, 0, 0}; + +static short qtab_723_40[15] = {-122, -16, 68, 139, 198, 250, 298, 339, + 378, 413, 445, 475, 502, 528, 553}; + +/* + * g723_40_encoder() + * + * Encodes a 16-bit linear PCM, A-law or u-law input sample and retuens + * the resulting 5-bit CCITT G.723 40Kbps code. + * Returns -1 if the input coding value is invalid. + */ +int +g723_40_encoder( + int sl, + int in_coding, + struct g72x_state *state_ptr) +{ + short sei, sezi, se, sez; /* ACCUM */ + short d; /* SUBTA */ + short y; /* MIX */ + short sr; /* ADDB */ + short dqsez; /* ADDC */ + short dq, i; + + switch (in_coding) { /* linearize input sample to 14-bit PCM */ + case AUDIO_ENCODING_ALAW: + sl = alaw2linear(sl) >> 2; + break; + case AUDIO_ENCODING_ULAW: + sl = ulaw2linear(sl) >> 2; + break; + case AUDIO_ENCODING_LINEAR: + sl >>= 2; /* sl of 14-bit dynamic range */ + break; + default: + return (-1); + } + + sezi = predictor_zero(state_ptr); + sez = sezi >> 1; + sei = sezi + predictor_pole(state_ptr); + se = sei >> 1; /* se = estimated signal */ + + d = sl - se; /* d = estimation difference */ + + /* quantize prediction difference */ + y = step_size(state_ptr); /* adaptive quantizer step size */ + i = quantize(d, y, qtab_723_40, 15); /* i = ADPCM code */ + + dq = reconstruct(i & 0x10, _dqlntab[i], y); /* quantized diff */ + + sr = (dq < 0) ? se - (dq & 0x7FFF) : se + dq; /* reconstructed signal */ + + dqsez = sr + sez - se; /* dqsez = pole prediction diff. */ + + update(5, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr); + + return (i); +} + +/* + * g723_40_decoder() + * + * Decodes a 5-bit CCITT G.723 40Kbps code and returns + * the resulting 16-bit linear PCM, A-law or u-law sample value. + * -1 is returned if the output coding is unknown. + */ +int +g723_40_decoder( + int i, + int out_coding, + struct g72x_state *state_ptr) +{ + short sezi, sei, sez, se; /* ACCUM */ + short y; /* MIX */ + short sr; /* ADDB */ + short dq; + short dqsez; + + i &= 0x1f; /* mask to get proper bits */ + sezi = predictor_zero(state_ptr); + sez = sezi >> 1; + sei = sezi + predictor_pole(state_ptr); + se = sei >> 1; /* se = estimated signal */ + + y = step_size(state_ptr); /* adaptive quantizer step size */ + dq = reconstruct(i & 0x10, _dqlntab[i], y); /* estimation diff. */ + + sr = (dq < 0) ? (se - (dq & 0x7FFF)) : (se + dq); /* reconst. signal */ + + dqsez = sr - se + sez; /* pole prediction diff. */ + + update(5, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr); + + switch (out_coding) { + case AUDIO_ENCODING_ALAW: + return (tandem_adjust_alaw(sr, se, y, i, 0x10, qtab_723_40)); + case AUDIO_ENCODING_ULAW: + return (tandem_adjust_ulaw(sr, se, y, i, 0x10, qtab_723_40)); + case AUDIO_ENCODING_LINEAR: + return (sr << 2); /* sr was of 14-bit dynamic range */ + default: + return (-1); + } +} diff --git a/src/ccitt-adpcm/g72x.c b/src/ccitt-adpcm/g72x.c new file mode 100644 index 0000000..ca17c35 --- /dev/null +++ b/src/ccitt-adpcm/g72x.c @@ -0,0 +1,565 @@ +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * g72x.c + * + * Common routines for G.721 and G.723 conversions. + */ + +#include +#include "g72x.h" + +static short power2[15] = {1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80, + 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000}; + +/* + * quan() + * + * quantizes the input val against the table of size short integers. + * It returns i if table[i - 1] <= val < table[i]. + * + * Using linear search for simple coding. + */ +static int +quan( + int val, + short *table, + int size) +{ + int i; + + for (i = 0; i < size; i++) + if (val < *table++) + break; + return (i); +} + +/* + * fmult() + * + * returns the integer product of the 14-bit integer "an" and + * "floating point" representation (4-bit exponent, 6-bit mantessa) "srn". + */ +static int +fmult( + int an, + int srn) +{ + short anmag, anexp, anmant; + short wanexp, wanmant; + short retval; + + anmag = (an > 0) ? an : ((-an) & 0x1FFF); + anexp = quan(anmag, power2, 15) - 6; + anmant = (anmag == 0) ? 32 : + (anexp >= 0) ? anmag >> anexp : anmag << -anexp; + wanexp = anexp + ((srn >> 6) & 0xF) - 13; + + wanmant = (anmant * (srn & 077) + 0x30) >> 4; + retval = (wanexp >= 0) ? ((wanmant << wanexp) & 0x7FFF) : + (wanmant >> -wanexp); + + return (((an ^ srn) < 0) ? -retval : retval); +} + +/* + * g72x_init_state() + * + * This routine initializes and/or resets the g72x_state structure + * pointed to by 'state_ptr'. + * All the initial state values are specified in the CCITT G.721 document. + */ +void +g72x_init_state( + struct g72x_state *state_ptr) +{ + int cnta; + + state_ptr->yl = 34816; + state_ptr->yu = 544; + state_ptr->dms = 0; + state_ptr->dml = 0; + state_ptr->ap = 0; + for (cnta = 0; cnta < 2; cnta++) { + state_ptr->a[cnta] = 0; + state_ptr->pk[cnta] = 0; + state_ptr->sr[cnta] = 32; + } + for (cnta = 0; cnta < 6; cnta++) { + state_ptr->b[cnta] = 0; + state_ptr->dq[cnta] = 32; + } + state_ptr->td = 0; +} + +/* + * predictor_zero() + * + * computes the estimated signal from 6-zero predictor. + * + */ +int +predictor_zero( + struct g72x_state *state_ptr) +{ + int i; + int sezi; + + sezi = fmult(state_ptr->b[0] >> 2, state_ptr->dq[0]); + for (i = 1; i < 6; i++) /* ACCUM */ + sezi += fmult(state_ptr->b[i] >> 2, state_ptr->dq[i]); + return (sezi); +} +/* + * predictor_pole() + * + * computes the estimated signal from 2-pole predictor. + * + */ +int +predictor_pole( + struct g72x_state *state_ptr) +{ + return (fmult(state_ptr->a[1] >> 2, state_ptr->sr[1]) + + fmult(state_ptr->a[0] >> 2, state_ptr->sr[0])); +} +/* + * step_size() + * + * computes the quantization step size of the adaptive quantizer. + * + */ +int +step_size( + struct g72x_state *state_ptr) +{ + int y; + int dif; + int al; + + if (state_ptr->ap >= 256) + return (state_ptr->yu); + else { + y = state_ptr->yl >> 6; + dif = state_ptr->yu - y; + al = state_ptr->ap >> 2; + if (dif > 0) + y += (dif * al) >> 6; + else if (dif < 0) + y += (dif * al + 0x3F) >> 6; + return (y); + } +} + +/* + * quantize() + * + * Given a raw sample, 'd', of the difference signal and a + * quantization step size scale factor, 'y', this routine returns the + * ADPCM codeword to which that sample gets quantized. The step + * size scale factor division operation is done in the log base 2 domain + * as a subtraction. + */ +int +quantize( + int d, /* Raw difference signal sample */ + int y, /* Step size multiplier */ + short *table, /* quantization table */ + int size) /* table size of short integers */ +{ + short dqm; /* Magnitude of 'd' */ + short exp; /* Integer part of base 2 log of 'd' */ + short mant; /* Fractional part of base 2 log */ + short dl; /* Log of magnitude of 'd' */ + short dln; /* Step size scale factor normalized log */ + int i; + + /* + * LOG + * + * Compute base 2 log of 'd', and store in 'dl'. + */ + dqm = abs(d); + exp = quan(dqm >> 1, power2, 15); + mant = ((dqm << 7) >> exp) & 0x7F; /* Fractional portion. */ + dl = (exp << 7) + mant; + + /* + * SUBTB + * + * "Divide" by step size multiplier. + */ + dln = dl - (y >> 2); + + /* + * QUAN + * + * Obtain codword i for 'd'. + */ + i = quan(dln, table, size); + if (d < 0) /* take 1's complement of i */ + return ((size << 1) + 1 - i); + else if (i == 0) /* take 1's complement of 0 */ + return ((size << 1) + 1); /* new in 1988 */ + else + return (i); +} +/* + * reconstruct() + * + * Returns reconstructed difference signal 'dq' obtained from + * codeword 'i' and quantization step size scale factor 'y'. + * Multiplication is performed in log base 2 domain as addition. + */ +int +reconstruct( + int sign, /* 0 for non-negative value */ + int dqln, /* G.72x codeword */ + int y) /* Step size multiplier */ +{ + short dql; /* Log of 'dq' magnitude */ + short dex; /* Integer part of log */ + short dqt; + short dq; /* Reconstructed difference signal sample */ + + dql = dqln + (y >> 2); /* ADDA */ + + if (dql < 0) { + return ((sign) ? -0x8000 : 0); + } else { /* ANTILOG */ + dex = (dql >> 7) & 15; + dqt = 128 + (dql & 127); + dq = (dqt << 7) >> (14 - dex); + return ((sign) ? (dq - 0x8000) : dq); + } +} + + +/* + * update() + * + * updates the state variables for each output code + */ +void +update( + int code_size, /* distinguish 723_40 with others */ + int y, /* quantizer step size */ + int wi, /* scale factor multiplier */ + int fi, /* for long/short term energies */ + int dq, /* quantized prediction difference */ + int sr, /* reconstructed signal */ + int dqsez, /* difference from 2-pole predictor */ + struct g72x_state *state_ptr) /* coder state pointer */ +{ + int cnt; + short mag, exp; /* Adaptive predictor, FLOAT A */ + short a2p = 0; /* LIMC */ + short a1ul; /* UPA1 */ + short pks1; /* UPA2 */ + short fa1; + char tr; /* tone/transition detector */ + short ylint, thr2, dqthr; + short ylfrac, thr1; + short pk0; + + pk0 = (dqsez < 0) ? 1 : 0; /* needed in updating predictor poles */ + + mag = dq & 0x7FFF; /* prediction difference magnitude */ + /* TRANS */ + ylint = state_ptr->yl >> 15; /* exponent part of yl */ + ylfrac = (state_ptr->yl >> 10) & 0x1F; /* fractional part of yl */ + thr1 = (32 + ylfrac) << ylint; /* threshold */ + thr2 = (ylint > 9) ? 31 << 10 : thr1; /* limit thr2 to 31 << 10 */ + dqthr = (thr2 + (thr2 >> 1)) >> 1; /* dqthr = 0.75 * thr2 */ + if (state_ptr->td == 0) /* signal supposed voice */ + tr = 0; + else if (mag <= dqthr) /* supposed data, but small mag */ + tr = 0; /* treated as voice */ + else /* signal is data (modem) */ + tr = 1; + + /* + * Quantizer scale factor adaptation. + */ + + /* FUNCTW & FILTD & DELAY */ + /* update non-steady state step size multiplier */ + state_ptr->yu = y + ((wi - y) >> 5); + + /* LIMB */ + if (state_ptr->yu < 544) /* 544 <= yu <= 5120 */ + state_ptr->yu = 544; + else if (state_ptr->yu > 5120) + state_ptr->yu = 5120; + + /* FILTE & DELAY */ + /* update steady state step size multiplier */ + state_ptr->yl += state_ptr->yu + ((-state_ptr->yl) >> 6); + + /* + * Adaptive predictor coefficients. + */ + if (tr == 1) { /* reset a's and b's for modem signal */ + state_ptr->a[0] = 0; + state_ptr->a[1] = 0; + state_ptr->b[0] = 0; + state_ptr->b[1] = 0; + state_ptr->b[2] = 0; + state_ptr->b[3] = 0; + state_ptr->b[4] = 0; + state_ptr->b[5] = 0; + } else { /* update a's and b's */ + pks1 = pk0 ^ state_ptr->pk[0]; /* UPA2 */ + + /* update predictor pole a[1] */ + a2p = state_ptr->a[1] - (state_ptr->a[1] >> 7); + if (dqsez != 0) { + fa1 = (pks1) ? state_ptr->a[0] : -state_ptr->a[0]; + if (fa1 < -8191) /* a2p = function of fa1 */ + a2p -= 0x100; + else if (fa1 > 8191) + a2p += 0xFF; + else + a2p += fa1 >> 5; + + if (pk0 ^ state_ptr->pk[1]) + /* LIMC */ + if (a2p <= -12160) + a2p = -12288; + else if (a2p >= 12416) + a2p = 12288; + else + a2p -= 0x80; + else if (a2p <= -12416) + a2p = -12288; + else if (a2p >= 12160) + a2p = 12288; + else + a2p += 0x80; + } + + /* TRIGB & DELAY */ + state_ptr->a[1] = a2p; + + /* UPA1 */ + /* update predictor pole a[0] */ + state_ptr->a[0] -= state_ptr->a[0] >> 8; + if (dqsez != 0) { + if (pks1 == 0) + state_ptr->a[0] += 192; + else + state_ptr->a[0] -= 192; + } + + /* LIMD */ + a1ul = 15360 - a2p; + if (state_ptr->a[0] < -a1ul) + state_ptr->a[0] = -a1ul; + else if (state_ptr->a[0] > a1ul) + state_ptr->a[0] = a1ul; + + /* UPB : update predictor zeros b[6] */ + for (cnt = 0; cnt < 6; cnt++) { + if (code_size == 5) /* for 40Kbps G.723 */ + state_ptr->b[cnt] -= state_ptr->b[cnt] >> 9; + else /* for G.721 and 24Kbps G.723 */ + state_ptr->b[cnt] -= state_ptr->b[cnt] >> 8; + if (dq & 0x7FFF) { /* XOR */ + if ((dq ^ state_ptr->dq[cnt]) >= 0) + state_ptr->b[cnt] += 128; + else + state_ptr->b[cnt] -= 128; + } + } + } + + for (cnt = 5; cnt > 0; cnt--) + state_ptr->dq[cnt] = state_ptr->dq[cnt-1]; + /* FLOAT A : convert dq[0] to 4-bit exp, 6-bit mantissa f.p. */ + if (mag == 0) { + state_ptr->dq[0] = (dq >= 0) ? 0x20 : 0xFC20; + } else { + exp = quan(mag, power2, 15); + state_ptr->dq[0] = (dq >= 0) ? + (exp << 6) + ((mag << 6) >> exp) : + (exp << 6) + ((mag << 6) >> exp) - 0x400; + } + + state_ptr->sr[1] = state_ptr->sr[0]; + /* FLOAT B : convert sr to 4-bit exp., 6-bit mantissa f.p. */ + if (sr == 0) { + state_ptr->sr[0] = 0x20; + } else if (sr > 0) { + exp = quan(sr, power2, 15); + state_ptr->sr[0] = (exp << 6) + ((sr << 6) >> exp); + } else if (sr > -32768) { + mag = -sr; + exp = quan(mag, power2, 15); + state_ptr->sr[0] = (exp << 6) + ((mag << 6) >> exp) - 0x400; + } else + state_ptr->sr[0] = 0xFC20; + + /* DELAY A */ + state_ptr->pk[1] = state_ptr->pk[0]; + state_ptr->pk[0] = pk0; + + /* TONE */ + if (tr == 1) /* this sample has been treated as data */ + state_ptr->td = 0; /* next one will be treated as voice */ + else if (a2p < -11776) /* small sample-to-sample correlation */ + state_ptr->td = 1; /* signal may be data */ + else /* signal is voice */ + state_ptr->td = 0; + + /* + * Adaptation speed control. + */ + state_ptr->dms += (fi - state_ptr->dms) >> 5; /* FILTA */ + state_ptr->dml += (((fi << 2) - state_ptr->dml) >> 7); /* FILTB */ + + if (tr == 1) + state_ptr->ap = 256; + else if (y < 1536) /* SUBTC */ + state_ptr->ap += (0x200 - state_ptr->ap) >> 4; + else if (state_ptr->td == 1) + state_ptr->ap += (0x200 - state_ptr->ap) >> 4; + else if (abs((state_ptr->dms << 2) - state_ptr->dml) >= + (state_ptr->dml >> 3)) + state_ptr->ap += (0x200 - state_ptr->ap) >> 4; + else + state_ptr->ap += (-state_ptr->ap) >> 4; +} + +/* + * tandem_adjust(sr, se, y, i, sign) + * + * At the end of ADPCM decoding, it simulates an encoder which may be receiving + * the output of this decoder as a tandem process. If the output of the + * simulated encoder differs from the input to this decoder, the decoder output + * is adjusted by one level of A-law or u-law codes. + * + * Input: + * sr decoder output linear PCM sample, + * se predictor estimate sample, + * y quantizer step size, + * i decoder input code, + * sign sign bit of code i + * + * Return: + * adjusted A-law or u-law compressed sample. + */ +int +tandem_adjust_alaw( + int sr, /* decoder output linear PCM sample */ + int se, /* predictor estimate sample */ + int y, /* quantizer step size */ + int i, /* decoder input code */ + int sign, + short *qtab) +{ + unsigned char sp; /* A-law compressed 8-bit code */ + short dx; /* prediction error */ + char id; /* quantized prediction error */ + int sd; /* adjusted A-law decoded sample value */ + int im; /* biased magnitude of i */ + int imx; /* biased magnitude of id */ + + if (sr <= -32768) + sr = -1; + sp = linear2alaw((sr >> 1) << 3); /* short to A-law compression */ + dx = (alaw2linear(sp) >> 2) - se; /* 16-bit prediction error */ + id = quantize(dx, y, qtab, sign - 1); + + if (id == i) { /* no adjustment on sp */ + return (sp); + } else { /* sp adjustment needed */ + /* ADPCM codes : 8, 9, ... F, 0, 1, ... , 6, 7 */ + im = i ^ sign; /* 2's complement to biased unsigned */ + imx = id ^ sign; + + if (imx > im) { /* sp adjusted to next lower value */ + if (sp & 0x80) { + sd = (sp == 0xD5) ? 0x55 : + ((sp ^ 0x55) - 1) ^ 0x55; + } else { + sd = (sp == 0x2A) ? 0x2A : + ((sp ^ 0x55) + 1) ^ 0x55; + } + } else { /* sp adjusted to next higher value */ + if (sp & 0x80) + sd = (sp == 0xAA) ? 0xAA : + ((sp ^ 0x55) + 1) ^ 0x55; + else + sd = (sp == 0x55) ? 0xD5 : + ((sp ^ 0x55) - 1) ^ 0x55; + } + return (sd); + } +} + +int +tandem_adjust_ulaw( + int sr, /* decoder output linear PCM sample */ + int se, /* predictor estimate sample */ + int y, /* quantizer step size */ + int i, /* decoder input code */ + int sign, + short *qtab) +{ + unsigned char sp; /* u-law compressed 8-bit code */ + short dx; /* prediction error */ + char id; /* quantized prediction error */ + int sd; /* adjusted u-law decoded sample value */ + int im; /* biased magnitude of i */ + int imx; /* biased magnitude of id */ + + if (sr <= -32768) + sr = 0; + sp = linear2ulaw(sr << 2); /* short to u-law compression */ + dx = (ulaw2linear(sp) >> 2) - se; /* 16-bit prediction error */ + id = quantize(dx, y, qtab, sign - 1); + if (id == i) { + return (sp); + } else { + /* ADPCM codes : 8, 9, ... F, 0, 1, ... , 6, 7 */ + im = i ^ sign; /* 2's complement to biased unsigned */ + imx = id ^ sign; + if (imx > im) { /* sp adjusted to next lower value */ + if (sp & 0x80) + sd = (sp == 0xFF) ? 0x7E : sp + 1; + else + sd = (sp == 0) ? 0 : sp - 1; + + } else { /* sp adjusted to next higher value */ + if (sp & 0x80) + sd = (sp == 0x80) ? 0x80 : sp - 1; + else + sd = (sp == 0x7F) ? 0xFE : sp + 1; + } + return (sd); + } +} diff --git a/src/ccitt-adpcm/g72x.h b/src/ccitt-adpcm/g72x.h new file mode 100644 index 0000000..3426176 --- /dev/null +++ b/src/ccitt-adpcm/g72x.h @@ -0,0 +1,148 @@ +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * g72x.h + * + * Header file for CCITT conversion routines. + * + */ +#ifndef _G72X_H +#define _G72X_H + +#define AUDIO_ENCODING_ULAW (1) /* ISDN u-law */ +#define AUDIO_ENCODING_ALAW (2) /* ISDN A-law */ +#define AUDIO_ENCODING_LINEAR (3) /* PCM 2's-complement (0-center) */ + +/* + * The following is the definition of the state structure + * used by the G.721/G.723 encoder and decoder to preserve their internal + * state between successive calls. The meanings of the majority + * of the state structure fields are explained in detail in the + * CCITT Recommendation G.721. The field names are essentially indentical + * to variable names in the bit level description of the coding algorithm + * included in this Recommendation. + */ +struct g72x_state { + long yl; /* Locked or steady state step size multiplier. */ + short yu; /* Unlocked or non-steady state step size multiplier. */ + short dms; /* Short term energy estimate. */ + short dml; /* Long term energy estimate. */ + short ap; /* Linear weighting coefficient of 'yl' and 'yu'. */ + + short a[2]; /* Coefficients of pole portion of prediction filter. */ + short b[6]; /* Coefficients of zero portion of prediction filter. */ + short pk[2]; /* + * Signs of previous two samples of a partially + * reconstructed signal. + */ + short dq[6]; /* + * Previous 6 samples of the quantized difference + * signal represented in an internal floating point + * format. + */ + short sr[2]; /* + * Previous 2 samples of the quantized difference + * signal represented in an internal floating point + * format. + */ + char td; /* delayed tone detect, new in 1988 version */ +}; + +/* External function definitions. */ + +extern void g72x_init_state(struct g72x_state *); +extern int predictor_zero(struct g72x_state *state_ptr); +extern int predictor_pole(struct g72x_state *state_ptr); +extern int step_size(struct g72x_state *state_ptr); + +extern int quantize( + int d, + int y, + short *table, + int size); +extern int reconstruct( + int sign, + int dqln, + int y); +extern void update( + int code_size, + int y, + int wi, + int fi, + int dq, + int sr, + int dqsez, + struct g72x_state *state_ptr); + +extern int tandem_adjust_alaw( + int sr, + int se, + int y, + int i, + int sign, + short *qtab); +extern int tandem_adjust_ulaw( + int sr, + int se, + int y, + int i, + int sign, + short *qtab); + +extern int g721_encoder( + int sample, + int in_coding, + struct g72x_state *state_ptr); +extern int g721_decoder( + int code, + int out_coding, + struct g72x_state *state_ptr); +extern int g723_24_encoder( + int sample, + int in_coding, + struct g72x_state *state_ptr); +extern int g723_24_decoder( + int code, + int out_coding, + struct g72x_state *state_ptr); +extern int g723_40_encoder( + int sample, + int in_coding, + struct g72x_state *state_ptr); +extern int g723_40_decoder( + int code, + int out_coding, + struct g72x_state *state_ptr); + +extern unsigned char linear2alaw(int pcm_val); +extern int alaw2linear(unsigned char a_val); +extern unsigned char linear2ulaw(int pcm_val); +extern int ulaw2linear(unsigned char u_val); +extern unsigned char alaw2ulaw(unsigned char aval); +extern unsigned char ulaw2alaw(unsigned char uval); + +#endif /* !_G72X_H */ diff --git a/src/cli.c b/src/cli.c new file mode 100644 index 0000000..3b79bc4 --- /dev/null +++ b/src/cli.c @@ -0,0 +1,177 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DECTMON_HISTFILE ".dectmon_history" + +static struct event cli_event; +static char histfile[PATH_MAX]; +static struct parser_state state; +static void *scanner; + +void cli_display(const char *fmt, va_list ap) +{ + int point, end; + + point = rl_point; + end = rl_end; + rl_point = rl_end = 0; + rl_save_prompt(); + rl_clear_message(); + + vfprintf(rl_outstream, fmt, ap); + + rl_restore_prompt(); + rl_point = point; + rl_end = end; + rl_forced_update_display(); +} + +static void cli_read_callback(int fd, short mask, void *data) +{ + rl_callback_read_char(); +} + +static void cli_complete(char *line) +{ + HIST_ENTRY *hist; + char *c; + + if (line == NULL) + return; + + /* avoid empty lines in history */ + for (c = line; c != '\0'; c++) { + if (!isspace(*c)) + break; + } + if (*c == '\0') + return; + + /* avoid duplicate lines in history */ + hist = history_get(where_history()); + if (hist == NULL || + ((where_history() != history_length - 1) || + strcmp(hist->line, line))) + add_history(line); + + rl_replace_line("", 1); + scanner_push_buffer(scanner, line); + yyparse(scanner, &state); + rl_crlf(); + free(line); +} + +static const char *keywords[] = { + "debug", + "cluster", + "portable", + "tbc", + "show", + "set", + "on", + "off", + "lce", + "cc", + "ss", + "mm", + "clms", + "llme", + "MNCC_SETUP-req", + "MNCC_INFO-req", + "sending-complete", + "keypad", + "basic-service", + "escape-to-proprietary", + "info", + "service", + "class", + "LIA", + "message", + "dect/isdn", + "normal", + "internal", + "emergency", + "external-handover", + "QA&M", + "basic-speech", + "GSM", + "UMTS", + "LRMS", + "GSM-SMS", + "wideband-speech", + "SUOTA-class-4", + "SUOTA-class-3", + "other", + "emc", + "content", + NULL +}; + +static char *cli_command_generator(const char *text, int state) +{ + static unsigned int idx, len; + const char *name; + + if (state == 0) { + idx = 0; + len = strlen(text); + } + + while ((name = keywords[idx]) != NULL) { + idx++; + if (!strncasecmp(name, text, len)) + return strdup(name); + } + return NULL; +} + +static char **cli_completion(const char *text, int start, int end) +{ + return rl_completion_matches(text, cli_command_generator); +} + +int cli_init(FILE *file) +{ + const char *prompt = NULL; + const char *home; + int fd; + + parser_init(&state); + scanner = scanner_init(&state); + + rl_instream = file; + fd = fileno(rl_instream); + rl_readline_name = "dectmon"; + if (isatty(fd)) + prompt = "dectmon > "; + + rl_outstream = stdout; + + rl_callback_handler_install(prompt, cli_complete); + rl_attempted_completion_function = cli_completion; + + home = getenv("HOME"); + if (home == NULL) + home = "."; + snprintf(histfile, sizeof(histfile), "%s/%s", home, DECTMON_HISTFILE); + read_history(histfile); + history_set_pos(history_length); + + event_set(&cli_event, fd, EV_READ | EV_PERSIST, cli_read_callback, NULL); + event_add(&cli_event, NULL); + + return 0; +} + +void cli_exit(void) +{ + rl_callback_handler_remove(); + rl_deprep_terminal(); + write_history(histfile); +} diff --git a/src/cmd-parser.y b/src/cmd-parser.y new file mode 100644 index 0000000..0211371 --- /dev/null +++ b/src/cmd-parser.y @@ -0,0 +1,472 @@ +%{ + +#include +#include +#include +#include + +#include "cmd-parser.h" +#include "cmd-scanner.h" + +static void yyerror(struct location *loc, void *scanner, + struct parser_state *state, const char *s) +{ + unsigned int plen = strlen("dectmon > "); + unsigned int i; + char buf[256]; + + memset(buf, ' ', sizeof(buf)); + for (i = loc->first_column - 1 + plen; i < loc->last_column + plen; i++) + buf[i] = '^'; + buf[i] = '\0'; + dectmon_log("%s\n", buf); + dectmon_log("%s\n", s); +} + +static struct dect_handle *parser_get_handle(void) +{ + struct dect_handle_priv *priv; + + priv = list_first_entry(&dect_handles, struct dect_handle_priv, list); + return priv->dh; +} + +void parser_init(struct parser_state *state) +{ + memset(state, 0, sizeof(*state)); +} + +static void location_init(void *scanner, struct parser_state *state, + struct location *loc) +{ + memset(loc, 0, sizeof(*loc)); +} + +static void location_update(struct location *loc, struct location *rhs, int n) +{ + if (n) { + loc->token_offset = rhs[1].token_offset; + loc->line_offset = rhs[1].line_offset; + loc->first_line = rhs[1].first_line; + loc->first_column = rhs[1].first_column; + loc->last_line = rhs[n].last_line; + loc->last_column = rhs[n].last_column; + } else { + loc->token_offset = rhs[0].token_offset; + loc->line_offset = rhs[0].line_offset; + loc->first_line = rhs[0].last_line; + loc->last_line = rhs[0].last_line; + loc->first_column = rhs[0].last_column; + loc->last_column = rhs[0].last_column; + } +} + +#define YYLLOC_DEFAULT(Current, Rhs, N) location_update(&Current, Rhs, N) + +%} + +/* Declarations */ + +%pure-parser +%error-verbose +%parse-param { void *scanner } +%parse-param { struct parser_state *state } +%lex-param { scanner } +%locations + +%initial-action { + location_init(scanner, state, &yylloc); +} + +%union { + uint64_t val; + const char *string; + struct dect_ie_common *ie; + struct dect_mncc_setup_param *mncc_setup_param; + struct dect_mncc_info_param *mncc_info_param; +} + +%token TOKEN_EOF 0 "end of file" +%token JUNK "junk" +%token NEWLINE "newline" + +%token STRING "string" +%token NUMBER "number" + +%token TOK_DEBUG "debug" +%token CLUSTER "cluster" +%token PORTABLE "portable" +%token TBC "tbc" + +%token SHOW "show" +%token SET "set" + +%token ON "on" +%token OFF "off" + +%token LCE "lce" +%token CC "cc" +%token SS "ss" +%token CLMS "clms" +%token MM "mm" +%token LLME "llme" + +%token MNCC_SETUP_REQ "MNCC_SETUP-req" +%token MNCC_INFO_REQ "MNCC_INFO-req" + +%token SENDING_COMPLETE "sending-complete" +%token KEYPAD "keypad" +%token BASIC_SERVICE "basic-service" +%token ESCAPE_TO_PROPRIETARY "escape-to-proprietary" + +%token INFO "info" +%token SERVICE "service" +%token CLASS "class" + +%token LIA "LIA" +%token MESSAGE "message" +%token DECT_ISDN "dect/isdn" +%token NORMAL "normal" +%token INTERNAL "internal" +%token EMERGENCY "emergency" +%token EXTERNAL_HO "external-handover" +%token QA_M "QA&M" + +%token BASIC_SPEECH "basic-speech" +%token GSM "GSM" +%token UMTS "UMTS" +%token LRMS "LRMS" +%token GSM_SMS "GSM-SMS" +%token WIDEBAND_SPEECH "wideband-speech" +%token SUOTA_CLASS_4 "SUOTA-class-4" +%token SUOTA_CLASS_3 "SUOTA-class-3" +%token OTHER "other" + +%token EMC "EMC" +%token CONTENT "content" + +%type debug_subsys on_off + +%type mncc_setup_param_alloc +%type mncc_setup_params mncc_setup_param + +%type mncc_info_param_alloc +%type mncc_info_params mncc_info_param + +%type keypad_ie keypad_ie_alloc + +%type sending_complete_ie + +%type basic_service_ie basic_service_ie_alloc +%type basic_service_ie_params basic_service_ie_param +%type basic_service_ie_class basic_service_ie_service + +%type etp_ie etp_ie_alloc +%type etp_ie_params etp_ie_param + +%% + +input : /* empty */ + | input line + ; + +line : cluster_stmt + | portable_stmt + | tbc_stmt + | debug_stmt + | cc_primitive + ; + +cluster_stmt : CLUSTER SHOW + { + struct dect_handle_priv *priv; + + dectmon_log("Cluster\t\tLocked\t\tPARI\n"); + list_for_each_entry(priv, &dect_handles, list) { + dectmon_log("%s\t%s\t\tEMC: %.4x FPN: %.5x\n", + priv->cluster, priv->locked ? "Yes" : "No", + priv->pari.emc, priv->pari.fpn); + } + } + ; + +portable_stmt : PORTABLE SHOW + { + char ipei[DECT_IPEI_STRING_LEN]; + struct dect_handle_priv *priv; + struct dect_pt *pt; + + dectmon_log("Cluster\t\tIPEI\n"); + list_for_each_entry(priv, &dect_handles, list) { + list_for_each_entry(pt, &priv->pt_list, list) { + dect_format_ipei_string(&pt->portable_identity->ipui.pun.n.ipei, + ipei); + dectmon_log("%s\t%s\n", priv->cluster, ipei); + } + } + } + ; + +tbc_stmt : TBC SHOW + { + struct dect_handle_priv *priv; + struct dect_tbc *tbc; + unsigned int i; + + dectmon_log("Cluster\t\tPMID\tFMID\tSlots\tCiphered\n"); + list_for_each_entry(priv, &dect_handles, list) { + for (i = 0; i < DECT_HALF_FRAME_SIZE; i++) { + tbc = priv->slots[i]; + if (tbc == NULL) + continue; + dectmon_log("%s\t%.5x\t%.3x\t%u/%u\t%s\n", + priv->cluster, + tbc->pmid, tbc->fmid, + tbc->slot1, tbc->slot2, + tbc->ciphered ? "Yes" : "No"); + } + } + } + ; + +debug_stmt : TOK_DEBUG SET debug_subsys on_off + { + if ($4) + debug_mask |= (1 << $3); + else + debug_mask &= ~(1 << $3); + } + ; + +debug_subsys : LCE { $$ = DECT_DEBUG_LCE; } + | CC { $$ = DECT_DEBUG_CC; } + | SS { $$ = DECT_DEBUG_SS; } + | CLMS { $$ = DECT_DEBUG_CLMS; } + | MM { $$ = DECT_DEBUG_MM; } + | LLME { $$ = DECT_DEBUG_NL; } + ; + +on_off : ON { $$ = true; } + | OFF { $$ = false; } + ; + +cc_primitive : mncc_setup_req + | mncc_info_req + ; + +/* + * MNCC_SETUP-req + */ + +mncc_setup_req : MNCC_SETUP_REQ mncc_setup_param_alloc '(' mncc_setup_params ')' + { + struct dect_handle *dh = parser_get_handle(); + struct dect_ipui ipui = {}; + struct dect_call *call; + + call = dect_call_alloc(dh); + dect_mncc_setup_req(dh, call, &ipui, $2); + } + +mncc_setup_param_alloc : + { + $$ = dect_ie_collection_alloc(parser_get_handle(), + sizeof(struct dect_mncc_setup_param)); + } + ; + +mncc_setup_params : mncc_setup_param + { + $$ = $-1; + } + | mncc_setup_params ',' mncc_setup_param + ; + +mncc_setup_param : keypad_ie + { + $-1->keypad = (struct dect_ie_keypad *)$1; + } + | sending_complete_ie + { + $-1->sending_complete = (struct dect_ie_sending_complete *)$1; + } + | basic_service_ie + { + $-1->basic_service = (struct dect_ie_basic_service *)$1; + } + | etp_ie + { + $-1->escape_to_proprietary = (struct dect_ie_escape_to_proprietary *)$1; + } + ; + +/* + * MNCC_INFO-req + */ + +mncc_info_req : MNCC_INFO_REQ mncc_info_param_alloc '(' mncc_info_params ')' + { + } + ; + +mncc_info_param_alloc : + { + $$ = dect_ie_collection_alloc(parser_get_handle(), + sizeof(struct dect_mncc_info_param)); + } + ; + +mncc_info_params : mncc_info_param + { + $$ = $-1; + } + | mncc_info_params ',' mncc_info_param + ; + +mncc_info_param : keypad_ie + { + $-1->keypad = (struct dect_ie_keypad *)$1; + } + | sending_complete_ie + { + $-1->sending_complete = (struct dect_ie_sending_complete *)$1; + } + | etp_ie + { + $-1->escape_to_proprietary = (struct dect_ie_escape_to_proprietary *)$1; + } + ; + +/* + * Keypad IE + */ + +keypad_ie : KEYPAD keypad_ie_alloc '(' keypad_ie_param ')' + { + $$ = $2; + } + ; + +keypad_ie_alloc : + { + $$ = dect_ie_alloc(parser_get_handle(), sizeof(struct dect_ie_keypad)); + } + +keypad_ie_param : INFO '=' STRING + { + struct dect_ie_keypad *ie = dect_ie_container(ie, $-1); + + ie->len = strlen($3); + memcpy(ie->info, $3, ie->len); + } + ; + +/* + * Sending complete IE + */ + +sending_complete_ie : SENDING_COMPLETE + { + $$ = dect_ie_alloc(parser_get_handle(), sizeof(struct dect_ie_sending_complete)); + } + ; + +/* + * Basic Service IE + */ + +basic_service_ie : BASIC_SERVICE basic_service_ie_alloc '(' basic_service_ie_params ')' + { + $$ = $2; + } + ; + +basic_service_ie_alloc : + { + $$ = dect_ie_alloc(parser_get_handle(), sizeof(struct dect_ie_basic_service)); + } + ; + +basic_service_ie_params : basic_service_ie_param + { + $$ = $-1; + } + | basic_service_ie_params ',' basic_service_ie_param + ; + +basic_service_ie_param : CLASS '=' basic_service_ie_class + { + struct dect_ie_basic_service *ie = dect_ie_container(ie, $-1); + + ie->class = $3; + } + | SERVICE '=' basic_service_ie_service + { + struct dect_ie_basic_service *ie = dect_ie_container(ie, $-1); + + ie->service = $3; + } + ; + +basic_service_ie_class : LIA { $$ = DECT_CALL_CLASS_LIA_SERVICE_SETUP; } + | MESSAGE { $$ = DECT_CALL_CLASS_MESSAGE; } + | DECT_ISDN { $$ = DECT_CALL_CLASS_DECT_ISDN; } + | NORMAL { $$ = DECT_CALL_CLASS_NORMAL; } + | INTERNAL { $$ = DECT_CALL_CLASS_INTERNAL; } + | EMERGENCY { $$ = DECT_CALL_CLASS_EMERGENCY; } + | SERVICE { $$ = DECT_CALL_CLASS_SERVICE; } + | EXTERNAL_HO { $$ = DECT_CALL_CLASS_EXTERNAL_HO; } + | SS { $$ = DECT_CALL_CLASS_SUPPLEMENTARY_SERVICE; } + | QA_M { $$ = DECT_CALL_CLASS_QA_M; } + ; + +basic_service_ie_service: BASIC_SPEECH { $$ = DECT_SERVICE_BASIC_SPEECH_DEFAULT; } + | GSM { $$ = DECT_SERVICE_DECT_GSM_IWP; } + | UMTS { $$ = DECT_SERVICE_UMTS_IWP; } + | LRMS { $$ = DECT_SERVICE_LRMS; } + | GSM_SMS { $$ = DECT_SERVICE_GSM_IWP_SMS; } + | WIDEBAND_SPEECH { $$ = DECT_SERVICE_WIDEBAND_SPEECH; } + | SUOTA_CLASS_4 { $$ = DECT_SERVICE_SUOTA_CLASS_4_DPRS_MANAGEMENT; } + | SUOTA_CLASS_3 { $$ = DECT_SERVICE_SUOTA_CLASS_3_DPRS_MANAGEMENT; } + | OTHER { $$ = DECT_SERVICE_OTHER; } + ; + +/* + * Escape-to-proprietary IE + */ + +etp_ie : ESCAPE_TO_PROPRIETARY etp_ie_alloc '(' etp_ie_params ')' + { + $$ = $2; + } + ; + +etp_ie_alloc : + { + $$ = dect_ie_alloc(parser_get_handle(), sizeof(struct dect_ie_escape_to_proprietary)); + } + ; + +etp_ie_params : etp_ie_param + { + $$ = $-1; + } + | etp_ie_params ',' etp_ie_param + ; + +etp_ie_param : EMC '=' NUMBER + { + struct dect_ie_escape_to_proprietary *ie = dect_ie_container(ie, $-1); + + ie->emc = $3; + } + | CONTENT '=' STRING + { + struct dect_ie_escape_to_proprietary *ie = dect_ie_container(ie, $-1); + + ie->len = strlen($3); + memcpy(ie->content, $3, ie->len); + } + ; + +%% diff --git a/src/cmd-scanner.l b/src/cmd-scanner.l new file mode 100644 index 0000000..b32c898 --- /dev/null +++ b/src/cmd-scanner.l @@ -0,0 +1,182 @@ +%{ + +#include +#include +#include "cmd-parser.h" + +static void init_pos(struct parser_state *state) +{ + state->lineno = 1; + state->column = 1; + state->token_offset = 0; + state->line_offset = 0; +} + +static void update_pos(struct parser_state *state, struct location *loc, + int len) +{ + loc->first_line = state->lineno; + loc->first_column = state->column; + loc->last_column = state->column + len - 1; + state->column += len; +} + +static void update_offset(struct parser_state *state, struct location *loc, + unsigned int len) +{ + state->token_offset += len; + loc->token_offset = state->token_offset; + loc->line_offset = state->line_offset; +} + +static void reset_pos(struct parser_state *state, struct location *loc) +{ + state->line_offset = state->token_offset; + state->lineno += 1; + state->column = 1; + loc->line_offset = state->line_offset; +} + +#define YY_USER_ACTION { \ + update_pos(yyget_extra(yyscanner), yylloc, yyleng); \ + update_offset(yyget_extra(yyscanner), yylloc, yyleng); \ +} + +/* avoid warnings with -Wmissing-prototypes */ +extern int yyget_column(yyscan_t); +extern void yyset_column(int, yyscan_t); + +%} + +%option reentrant +%option noyywrap +%option nounput +%option bison-bridge +%option bison-locations +%option debug +%option yylineno +%option nodefault +%option warn +%option case-insensitive + +space [ ] +tab \t +newline \n +digit [0-9] +hexdigit [0-9a-fA-F] +decstring {digit}+ +hexstring 0[xX]{hexdigit}+ +range ({decstring}?:{decstring}?) +letter [a-zA-Z] +string ({letter})({letter}|{digit}|[/\-_\.])* +quotedstring \"[^"]*\" +comment #.*$ +slash \/ +%% + +"(" { return '('; } +")" { return ')'; } +"=" { return '='; } +"," { return ','; } + +"debug" { return TOK_DEBUG; } +"cluster" { return CLUSTER; } +"portable" { return PORTABLE; } +"tbc" { return TBC; } + +"show" { return SHOW; } +"set" { return SET; } + +"on" { return ON; } +"off" { return OFF; } + +"lce" { return LCE; } +"cc" { return CC; } +"ss" { return SS; } +"clms" { return CLMS; } +"mm" { return MM; } +"llme" { return LLME; } + +"MNCC_SETUP-req" { return MNCC_SETUP_REQ; } +"MNCC_INFO-req" { return MNCC_INFO_REQ; } + +"sending-complete" { return SENDING_COMPLETE; } +"keypad" { return KEYPAD; } +"basic-service" { return BASIC_SERVICE; } +"escape-to-proprietary" { return ESCAPE_TO_PROPRIETARY; } + +"info" { return INFO; } +"service" { return SERVICE; } +"class" { return CLASS; } + +"LIA" { return LIA; } +"message" { return MESSAGE; } +"dect/isdn" { return DECT_ISDN; } +"normal" { return NORMAL; } +"internal" { return INTERNAL; } +"emergency" { return EMERGENCY; } +"external-handover" { return EXTERNAL_HO; } +"QA&M" { return QA_M; } + +"basic-speech" { return BASIC_SPEECH; } +"GSM" { return GSM; } +"UMTS" { return UMTS; } +"LRMS" { return LRMS; } +"GSM-SMS" { return GSM_SMS; } +"wideband-speech" { return WIDEBAND_SPEECH; } +"SUOTA-class-4" { return SUOTA_CLASS_4; } +"SUOTA-class-3" { return SUOTA_CLASS_3; } +"other" { return OTHER; } + +"EMC" { return EMC; } +"content" { return CONTENT; } + +{string} { + yylval->string = strdup(yytext); + return STRING; + } + +{decstring} { + yylval->val = strtoul(yytext, NULL, 0); + return NUMBER; + } + +\\{newline} { + reset_pos(yyget_extra(yyscanner), yylloc); + } + +{newline} { + reset_pos(yyget_extra(yyscanner), yylloc); + return NEWLINE; + } + +{space}+ + +. { return JUNK; } + +%% + +void scanner_push_buffer(void *scanner, const char *buffer) +{ + struct parser_state *state = yyget_extra(scanner); + YY_BUFFER_STATE b; + + b = yy_scan_string(buffer, scanner); + init_pos(state); +} + +void *scanner_init(struct parser_state *state) +{ + yyscan_t scanner; + + yylex_init(&scanner); + yyset_extra(state, scanner); + yyset_out(NULL, scanner); + + return scanner; +} + +void scanner_destroy(struct parser_state *scanner) +{ + yylex_destroy(scanner); +} diff --git a/src/debug.c b/src/debug.c index 5fd1091..210b2eb 100644 --- a/src/debug.c +++ b/src/debug.c @@ -36,7 +36,7 @@ void dect_hexdump(const char *prefix, const uint8_t *buf, size_t size) if (off == BLOCKSIZE - 1 || i == size - 1) { abuf[off + 1] = '\0'; - printf("%s: %-*s |%s|\n", prefix, 64 - plen, hbuf, abuf); + dectmon_log("%s: %-*s |%s|\n", prefix, 64 - plen, hbuf, abuf); } } } diff --git a/src/dlc.c b/src/dlc.c index f49a119..d294a39 100644 --- a/src/dlc.c +++ b/src/dlc.c @@ -19,7 +19,7 @@ #define dlc_print(fmt, args...) \ do { \ if (dumpopts & DECTMON_DUMP_DLC) \ - printf(fmt, ## args); \ + dectmon_log(fmt, ## args); \ } while (0) #if 1 diff --git a/src/mac.c b/src/mac.c index 14bdae3..513667a 100644 --- a/src/mac.c +++ b/src/mac.c @@ -22,7 +22,7 @@ #define mac_print(fmt, args...) \ do { \ if (dumpopts & DECTMON_DUMP_MAC) \ - printf(fmt, ## args); \ + dectmon_log(fmt, ## args); \ } while (0) /* @@ -529,8 +529,8 @@ static int dect_parse_tail_msg(struct dect_tail_msg *tm, */ #define tbc_log(tbc, fmt, args...) \ - printf("TBC: PMID: %.5x FMID: %.3x: " fmt, \ - (tbc)->pmid, (tbc)->fmid, ## args) + dectmon_log("TBC: PMID: %.5x FMID: %.3x: " fmt, \ + (tbc)->pmid, (tbc)->fmid, ## args) static void dect_tbc_release(struct dect_handle *dh, struct dect_tbc *tbc) { @@ -682,6 +682,9 @@ static void dect_tbc_rcv(struct dect_handle *dh, struct dect_tbc *tbc, dect_mbuf_pull(mb, 10); } break; + case DECT_BI_UTYPE_0: + dect_dl_u_data_ind(dh, &tbc->dl, slot < 12, mb); + break; default: break; } @@ -695,7 +698,6 @@ static void dect_tbc_rcv(struct dect_handle *dh, struct dect_tbc *tbc, case DECT_TM_TYPE_ENCCTRL: switch (tm->encctl.cmd) { case DECT_ENCCTRL_START_REQUEST: - printf("\n"); break; case DECT_ENCCTRL_START_CONFIRM: case DECT_ENCCTRL_START_GRANT: diff --git a/src/main.c b/src/main.c index 889efe3..1139df1 100644 --- a/src/main.c +++ b/src/main.c @@ -14,18 +14,36 @@ #include #include #include +#include #define DECT_MAX_CLUSTERS 16 #define DECT_LOCK_TIMEOUT 15 #define cluster_log(priv, fmt, args...) \ - printf("%s: " fmt, \ - ((struct dect_handle_priv *)dect_handle_priv(dh))->cluster, \ - ## args) + dectmon_log("%s: " fmt, \ + ((struct dect_handle_priv *)dect_handle_priv(dh))->cluster, \ + ## args) -static LIST_HEAD(dect_handles); +LIST_HEAD(dect_handles); static unsigned int locked; +static FILE *logfile; + +void dectmon_log(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + cli_display(fmt, ap); + va_end(ap); + + if (logfile) { + va_start(ap, fmt); + vfprintf(logfile, fmt, ap); + va_end(ap); + } +} + static struct dect_handle_priv *dect_handle_lookup(const struct dect_ari *pari) { struct dect_handle_priv *priv; @@ -53,9 +71,6 @@ static void dect_mac_me_info_ind(struct dect_handle *dh, if (pari != NULL) { if (dect_handle_lookup(pari) == NULL) { - cluster_log(dh, "MAC_ME_INFO-ind: EMC: %.4x FPN: %.5x\n", - pari->emc, pari->fpn); - dect_llme_mac_me_info_res(dh, pari); priv->pari = *pari; dect_timer_start(dh, priv->lock_timer, DECT_LOCK_TIMEOUT); @@ -67,6 +82,7 @@ static void dect_mac_me_info_ind(struct dect_handle *dh, locked, priv->pari.emc, priv->pari.fpn); dect_timer_stop(dh, priv->lock_timer); + priv->locked = true; } } else { locked--; @@ -74,6 +90,7 @@ static void dect_mac_me_info_ind(struct dect_handle *dh, locked, priv->pari.emc, priv->pari.fpn); memset(&priv->pari, 0, sizeof(priv->pari)); + priv->locked = false; dect_llme_scan_req(dh); } } @@ -98,6 +115,19 @@ static struct dect_ops ops = { .raw_ops = &raw_ops, }; +uint32_t debug_mask = ~0; + +static void dect_debug(enum dect_debug_subsys subsys, const char *fmt, + va_list ap) +{ + char buf[1024]; + + if (debug_mask & (1 << subsys)) { + vsnprintf(buf, sizeof(buf), fmt, ap); + dectmon_log("%s", buf); + } +} + #define OPTSTRING "c:sm:d:n:p:h" enum { @@ -107,6 +137,7 @@ enum { OPT_DUMP_DLC = 'd', OPT_DUMP_NWK = 'n', OPT_AUTH_PIN = 'p', + OPT_LOGFILE = 'l', OPT_HELP = 'h', }; @@ -117,6 +148,7 @@ static const struct option dectmon_opts[] = { { .name = "dump-dlc", .has_arg = true, .flag = 0, .val = OPT_DUMP_DLC, }, { .name = "dump-nwk", .has_arg = true, .flag = 0, .val = OPT_DUMP_NWK, }, { .name = "auth-pin", .has_arg = true, .flag = 0, .val = OPT_AUTH_PIN, }, + { .name = "logfile", .has_arg = true, .flag = 0, .val = OPT_LOGFILE, }, { .name = "help", .has_arg = false, .flag = 0, .val = OPT_HELP, }, { }, }; @@ -138,6 +170,7 @@ static void dectmon_help(const char *progname) " -d/--dump-dlc=yes/no Dump DLC layer messages (default: no)\n" " -n/--dump-nwk=yes/no Dump NWK layer messages (default: yes)\n" " -p/--auth-pin=PIN Authentication PIN for Key Allocation\n" + " -l/--logfile=NAME Log output to file\n" " -h/--help Show this help text\n" "\n", progname); @@ -170,6 +203,7 @@ static struct dect_handle *dectmon_open_handle(struct dect_ops *ops, priv = dect_handle_priv(dh); priv->cluster = cluster; + priv->dh = dh; priv->lock_timer = dect_timer_alloc(dh); if (priv->lock_timer == NULL) @@ -215,6 +249,11 @@ int main(int argc, char **argv) case OPT_AUTH_PIN: auth_pin = optarg; break; + case OPT_LOGFILE: + logfile = fopen(optarg, "a"); + if (logfile == NULL) + pexit("fopen"); + break; case OPT_HELP: dectmon_help(argv[0]); exit(0); @@ -226,6 +265,10 @@ int main(int argc, char **argv) dect_event_ops_init(&ops); dect_dummy_ops_init(&ops); + dect_audio_init(); + + cli_init(stdin); + dect_set_debug_hook(dect_debug); if (ncluster == 0) ncluster = 1; @@ -242,5 +285,6 @@ int main(int argc, char **argv) } dect_event_loop(); + cli_exit(); return 0; } diff --git a/src/nwk.c b/src/nwk.c index f1098b2..5569c00 100644 --- a/src/nwk.c +++ b/src/nwk.c @@ -200,7 +200,7 @@ static void dect_pt_track_key_allocation(struct dect_handle *dh, break; default: if (pt->procedure == DECT_MM_KEY_ALLOCATION) { - printf("unexpected message during key allocation\n"); + dectmon_log("unexpected message during key allocation\n"); goto release; } return; @@ -217,7 +217,7 @@ static void dect_pt_track_key_allocation(struct dect_handle *dh, dect_auth_a12(ks, pt->rand_f->value, dck, &res1); if (res1 == pt->res->value) { - printf("authentication ok\n"); + dectmon_log("authentication ok\n"); dect_auth_a21(k, pt->rs->value, ks); @@ -229,7 +229,7 @@ static void dect_pt_track_key_allocation(struct dect_handle *dh, dect_pt_write_uak(pt); } else - printf("authentication failed\n"); + dectmon_log("authentication failed\n"); release: dect_ie_release(dh, pt->rs); @@ -273,7 +273,7 @@ static void dect_pt_track_auth(struct dect_handle *dh, break; default: if (pt->procedure == DECT_MM_AUTHENTICATION) { - printf("unexpected message during authentication\n"); + dectmon_log("unexpected message during authentication\n"); goto release; } return; @@ -289,13 +289,13 @@ static void dect_pt_track_auth(struct dect_handle *dh, dect_auth_a12(ks, pt->rand_f->value, dck, &res1.value); if (res1.value == pt->res->value) { - printf("authentication successful\n"); + dectmon_log("authentication successful\n"); if (pt->auth_type->flags & DECT_AUTH_FLAG_UPC) { dect_hexdump("DCK", dck, sizeof(dck)); memcpy(pt->dck, dck, sizeof(pt->dck)); } } else - printf("authentication failed\n"); + dectmon_log("authentication failed\n"); release: dect_ie_release(dh, pt->auth_type); @@ -321,6 +321,40 @@ static void dect_pt_track_ciphering(struct dect_handle *dh, } } +static void dect_pt_track_audio(struct dect_handle *dh, + struct dect_pt *pt, uint8_t msgtype, + const struct dect_sfmt_ie *ie, + struct dect_ie_common *common) +{ + struct dect_ie_progress_indicator *progress_indicator; + + switch (msgtype) { + case DECT_CC_SETUP: + case DECT_CC_SETUP_ACK: + case DECT_CC_CALL_PROC: + case DECT_CC_INFO: + case DECT_CC_ALERTING: + if (ie->id != DECT_IE_PROGRESS_INDICATOR) + break; + progress_indicator = (void *)common; + if (progress_indicator->progress != + DECT_PROGRESS_INBAND_INFORMATION_NOW_AVAILABLE) + break; + /* fall through */ + case DECT_CC_CONNECT: + if (pt->ah == NULL) + pt->ah = dect_audio_open(); + break; + case DECT_CC_RELEASE: + case DECT_CC_RELEASE_COM: + if (pt->ah != NULL) { + dect_audio_close(pt->ah); + pt->ah = NULL; + } + break; + } +} + void dect_dl_data_ind(struct dect_handle *dh, struct dect_dl *dl, struct dect_msg_buf *mb) { @@ -334,9 +368,9 @@ void dect_dl_data_ind(struct dect_handle *dh, struct dect_dl *dl, msgtype = mb->data[1]; - printf("\n"); + dectmon_log("\n"); dect_hexdump("NWK", mb->data, mb->len); - printf("{%s} message:\n", nwk_msg_types[msgtype]); + dectmon_log("{%s} message:\n", nwk_msg_types[msgtype]); dect_mbuf_pull(mb, 2); while (mb->len) { @@ -357,9 +391,31 @@ void dect_dl_data_ind(struct dect_handle *dh, struct dect_dl *dl, dect_pt_track_key_allocation(dh, dl->pt, msgtype, &ie, common); dect_pt_track_auth(dh, dl->pt, msgtype, &ie, common); dect_pt_track_ciphering(dh, dl->pt, msgtype, &ie, common); + dect_pt_track_audio(dh, dl->pt, msgtype, &ie, common); } __dect_ie_put(dh, common); dect_mbuf_pull(mb, ie.len); } } + +void dect_dl_u_data_ind(struct dect_handle *dh, struct dect_dl *dl, bool dir, + struct dect_msg_buf *mb) +{ + struct dect_pt *pt = dl->pt; + struct dect_msg_buf *clone; + + if (pt == NULL || pt->ah == NULL) + return; + + /* Clone message buffer - audio is processed asynchronously, so we can't + * use the on-stack buffer + */ + clone = dect_mbuf_alloc(dh); + if (clone == NULL) + return; + + mb->len = 40; + memcpy(dect_mbuf_put(clone, mb->len), mb->data, mb->len); + dect_audio_queue(pt->ah, dir, clone); +}