diff options
author | Patrick McHardy <kaber@trash.net> | 2009-05-06 18:41:57 +0200 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2009-05-06 18:41:57 +0200 |
commit | 0406a88b39a9c978d47ababf9fd93d9e244e1a4a (patch) | |
tree | 89a082e027d10bb77c9f817cbdc1accb72424ec4 /src |
Import libdect
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'src')
-rw-r--r-- | src/.gitignore | 1 | ||||
-rw-r--r-- | src/Makefile.in | 19 | ||||
-rw-r--r-- | src/cc.c | 1128 | ||||
-rw-r--r-- | src/ccitt-adpcm/README | 94 | ||||
-rw-r--r-- | src/ccitt-adpcm/decode.c | 113 | ||||
-rw-r--r-- | src/ccitt-adpcm/encode.c | 119 | ||||
-rw-r--r-- | src/ccitt-adpcm/g711.c | 285 | ||||
-rw-r--r-- | src/ccitt-adpcm/g721.c | 173 | ||||
-rw-r--r-- | src/ccitt-adpcm/g723_24.c | 158 | ||||
-rw-r--r-- | src/ccitt-adpcm/g723_40.c | 178 | ||||
-rw-r--r-- | src/ccitt-adpcm/g72x.c | 565 | ||||
-rw-r--r-- | src/ccitt-adpcm/g72x.h | 148 | ||||
-rw-r--r-- | src/dsaa.c | 211 | ||||
-rw-r--r-- | src/identities.c | 182 | ||||
-rw-r--r-- | src/keypad.c | 80 | ||||
-rw-r--r-- | src/lce.c | 790 | ||||
-rw-r--r-- | src/libdect.c | 81 | ||||
-rw-r--r-- | src/mm.c | 154 | ||||
-rw-r--r-- | src/netlink.c | 139 | ||||
-rw-r--r-- | src/s_msg.c | 991 | ||||
-rw-r--r-- | src/ss.c | 49 | ||||
-rw-r--r-- | src/utils.c | 142 |
22 files changed, 5800 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..bd99de1 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +libdect.so diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..0a12706 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,19 @@ +CFLAGS += -fPIC +LIBS += dect + +dect-destdir := usr/lib + +dect-obj += libdect.o +dect-obj += identities.o +dect-obj += s_msg.o +dect-obj += lce.o +dect-obj += cc.o +dect-obj += ss.o +dect-obj += keypad.o +dect-obj += dsaa.o +dect-obj += netlink.o +dect-obj += utils.o + +dect-obj += ccitt-adpcm/g711.o +dect-obj += ccitt-adpcm/g72x.o +dect-obj += ccitt-adpcm/g721.o diff --git a/src/cc.c b/src/cc.c new file mode 100644 index 0000000..062f2ad --- /dev/null +++ b/src/cc.c @@ -0,0 +1,1128 @@ +/* + * DECT Call Control (CC) + * + * Copyright (c) 2009 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <linux/dect.h> + +#include <libdect.h> +#include <utils.h> +#include <s_fmt.h> +#include <lce.h> +#include <cc.h> + +static const struct dect_sfmt_ie_desc cc_setup_msg_desc[] = { + DECT_SFMT_IE(S_VL_IE_PORTABLE_IDENTITY, IE_MANDATORY, IE_MANDATORY, 0), + DECT_SFMT_IE(S_VL_IE_FIXED_IDENTITY, IE_MANDATORY, IE_MANDATORY, 0), + DECT_SFMT_IE(S_VL_IE_NWK_ASSIGNED_IDENTITY, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_DO_IE_BASIC_SERVICE, IE_MANDATORY, IE_MANDATORY, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_IWU_ATTRIBUTES, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CALL_ATTRIBUTES, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CONNECTION_ATTRIBUTES, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_VL_IE_CIPHER_INFO, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CONNECTION_IDENTITY, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_FACILITY, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_PROGRESS_INDICATOR, IE_OPTIONAL, IE_NONE, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_DO_IE_SINGLE_DISPLAY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_DO_IE_SINGLE_KEYPAD, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_DO_IE_SIGNAL, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_FEATURE_ACTIVATE, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_FEATURE_INDICATE, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_NETWORK_PARAMETER, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_EXT_HO_INDICATOR, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_TERMINAL_CAPABILITY, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_END_TO_END_COMPATIBILITY, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_RATE_PARAMETERS, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_TRANSIT_DELAY, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_WINDOW_SIZE, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CALLING_PARTY_NUMBER, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CALLED_PARTY_NUMBER, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CALLED_PARTY_SUBADDR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_SE_IE_SENDING_COMPLETE, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_SEGMENTED_INFO, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_VL_IE_IWU_TO_IWU, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_IWU_PACKET, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CALLING_PARTY_NAME, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CODEC_LIST, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CALL_INFORMATION, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_ESCAPE_TO_PROPRIETARY, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE_END_MSG +}; + +static const struct dect_sfmt_ie_desc cc_info_msg_desc[] = { + DECT_SFMT_IE(S_VL_IE_LOCATION_AREA, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_NWK_ASSIGNED_IDENTITY, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_FACILITY, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_PROGRESS_INDICATOR, IE_OPTIONAL, IE_NONE, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_DO_IE_SINGLE_DISPLAY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_DO_IE_SINGLE_KEYPAD, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_DO_IE_SIGNAL, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_FEATURE_ACTIVATE, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_FEATURE_INDICATE, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_NETWORK_PARAMETER, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_EXT_HO_INDICATOR, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_CALLING_PARTY_NUMBER, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CALLED_PARTY_NUMBER, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CALLED_PARTY_SUBADDR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_SE_IE_SENDING_COMPLETE, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_DO_IE_TEST_HOOK_CONTROL, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_IWU_TO_IWU, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_VL_IE_IWU_PACKET, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CALLING_PARTY_NAME, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CODEC_LIST, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CALL_INFORMATION, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_ESCAPE_TO_PROPRIETARY, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE_END_MSG +}; + +static const struct dect_sfmt_ie_desc cc_setup_ack_msg_desc[] = { + DECT_SFMT_IE(S_VL_IE_INFO_TYPE, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_PORTABLE_IDENTITY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_FIXED_IDENTITY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_LOCATION_AREA, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_IWU_ATTRIBUTES, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_CALL_ATTRIBUTES, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_CONNECTION_ATTRIBUTES, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_CONNECTION_IDENTITY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_FACILITY, IE_OPTIONAL, IE_NONE, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_PROGRESS_INDICATOR, IE_OPTIONAL, IE_NONE, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_DO_IE_SINGLE_DISPLAY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_DO_IE_SIGNAL, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_FEATURE_INDICATE, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_NETWORK_PARAMETER, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_EXT_HO_INDICATOR, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_TRANSIT_DELAY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_WINDOW_SIZE, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_SE_IE_DELIMITER_REQUEST, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_IWU_TO_IWU, IE_OPTIONAL, IE_NONE, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_VL_IE_IWU_PACKET, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_CODEC_LIST, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_ESCAPE_TO_PROPRIETARY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE_END_MSG +}; + +static const struct dect_sfmt_ie_desc cc_call_proc_msg_desc[] = { + DECT_SFMT_IE(S_VL_IE_IWU_ATTRIBUTES, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_CALL_ATTRIBUTES, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_CONNECTION_ATTRIBUTES, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_CONNECTION_IDENTITY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_FACILITY, IE_OPTIONAL, IE_NONE, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_PROGRESS_INDICATOR, IE_OPTIONAL, IE_NONE, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_DO_IE_SINGLE_DISPLAY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_DO_IE_SIGNAL, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_FEATURE_INDICATE, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_TRANSIT_DELAY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_WINDOW_SIZE, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_IWU_TO_IWU, IE_OPTIONAL, IE_NONE, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_VL_IE_IWU_PACKET, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_CODEC_LIST, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_ESCAPE_TO_PROPRIETARY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE_END_MSG +}; + +static const struct dect_sfmt_ie_desc cc_alerting_msg_desc[] = { + DECT_SFMT_IE(S_VL_IE_IWU_ATTRIBUTES, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CALL_ATTRIBUTES, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CONNECTION_ATTRIBUTES, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CONNECTION_IDENTITY, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_FACILITY, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_PROGRESS_INDICATOR, IE_OPTIONAL, IE_NONE, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_DO_IE_SINGLE_DISPLAY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_DO_IE_SIGNAL, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_FEATURE_INDICATE, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_TERMINAL_CAPABILITY, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_TRANSIT_DELAY, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_WINDOW_SIZE, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_IWU_TO_IWU, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_VL_IE_IWU_PACKET, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CODEC_LIST, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_ESCAPE_TO_PROPRIETARY, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE_END_MSG +}; + +static const struct dect_sfmt_ie_desc cc_connect_msg_desc[] = { + DECT_SFMT_IE(S_VL_IE_IWU_ATTRIBUTES, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CALL_ATTRIBUTES, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CONNECTION_ATTRIBUTES, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CONNECTION_IDENTITY, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_FACILITY, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_PROGRESS_INDICATOR, IE_OPTIONAL, IE_NONE, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_DO_IE_SINGLE_DISPLAY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_DO_IE_SIGNAL, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_FEATURE_INDICATE, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_NETWORK_PARAMETER, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_EXT_HO_INDICATOR, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_TERMINAL_CAPABILITY, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_TRANSIT_DELAY, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_WINDOW_SIZE, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_SEGMENTED_INFO, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_VL_IE_IWU_TO_IWU, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_IWU_PACKET, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CODEC_LIST, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_ESCAPE_TO_PROPRIETARY, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE_END_MSG +}; + +static const struct dect_sfmt_ie_desc cc_connect_ack_msg_desc[] = { + DECT_SFMT_IE(S_DO_IE_SINGLE_DISPLAY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_FEATURE_INDICATE, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_IWU_TO_IWU, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_VL_IE_IWU_PACKET, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_ESCAPE_TO_PROPRIETARY, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE_END_MSG +}; + +static const struct dect_sfmt_ie_desc cc_release_msg_desc[] = { + DECT_SFMT_IE(S_DO_IE_RELEASE_REASON, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_FACILITY, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_PROGRESS_INDICATOR, IE_OPTIONAL, IE_NONE, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_VL_IE_FEATURE_INDICATE, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_SEGMENTED_INFO, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_VL_IE_IWU_TO_IWU, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_IWU_PACKET, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_ESCAPE_TO_PROPRIETARY, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE_END_MSG +}; + +static const struct dect_sfmt_ie_desc cc_release_com_msg_desc[] = { + DECT_SFMT_IE(S_DO_IE_RELEASE_REASON, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_IDENTITY_TYPE, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_LOCATION_AREA, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_IWU_ATTRIBUTES, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CONNECTION_ATTRIBUTES, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_FACILITY, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_DO_IE_SINGLE_DISPLAY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_FEATURE_INDICATE, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_NETWORK_PARAMETER, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_SO_IE_REPEAT_INDICATOR, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_SEGMENTED_INFO, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_VL_IE_IWU_TO_IWU, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_IWU_PACKET, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_ESCAPE_TO_PROPRIETARY, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE_END_MSG +}; + +static const struct dect_sfmt_ie_desc cc_service_change_msg_desc[] = { + DECT_SFMT_IE_END_MSG +}; + +static const struct dect_sfmt_ie_desc cc_service_accept_msg_desc[] = { + DECT_SFMT_IE_END_MSG +}; + +static const struct dect_sfmt_ie_desc cc_service_reject_msg_desc[] = { + DECT_SFMT_IE_END_MSG +}; + +static const struct dect_sfmt_ie_desc cc_notify_msg_desc[] = { + DECT_SFMT_IE_END_MSG +}; + +static const struct dect_sfmt_ie_desc cc_iwu_info_msg_desc[] = { + DECT_SFMT_IE_END_MSG +}; + +#define cc_debug(call, fmt, args...) \ + dect_debug("CC: call %p (%s): " fmt "\n", \ + (call), call_states[(call)->state], ## args) + +static const char *call_states[DECT_CC_STATE_MAX + 1] = { + [DECT_CC_CALL_INITIATED] = "CALL INITIATED", + [DECT_CC_OVERLAP_SENDING] = "OVERLAP SENDING", + [DECT_CC_CALL_PROCEEDING] = "CALL PROCEEDING", + [DECT_CC_CALL_DELIVERED] = "CALL DELIVERED", + [DECT_CC_CALL_PRESENT] = "CALL PRESENT", + [DECT_CC_CALL_RECEIVED] = "CALL RECEIVED", + [DECT_CC_CONNECT_PENDING] = "CONNECT PENDING", + [DECT_CC_ACTIVE] = "ACTIVE", + [DECT_CC_RELEASE_PENDING] = "RELEASE PENDING", + [DECT_CC_OVERLAP_RECEIVING] = "OVERLAP RECEIVING", + [DECT_CC_INCOMING_CALL_PROCEEDING] = "INCOMING CALL PROCEEDING", +}; + +void *dect_call_priv(struct dect_call *call) +{ + return call->priv; +} + +const struct dect_ipui *dect_call_portable_identity(const struct dect_call *call) +{ + return &call->pt_id->ipui; +} + +int dect_dl_u_data_req(const struct dect_handle *dh, struct dect_call *call, + struct dect_msg_buf *mb) +{ + ssize_t size; + + if (call->lu_sap == NULL) { + cc_debug(call, "U-Plane U_DATA-req, but still unconnected"); + return 0; + } + //cc_debug(call, "U-Plane U_DATA-req"); + //dect_mbuf_dump(mb, "LU1"); + size = send(call->lu_sap->fd, mb->data, mb->len, 0); + if (size != ((ssize_t)mb->len)) + cc_debug(call, "sending %u bytes failed: err=%d\n", + mb->len, errno); + return 0; +} + +static void dect_cc_lu_event(struct dect_handle *dh, struct dect_fd *fd, + uint32_t event) +{ + struct dect_call *call = fd->data; + struct dect_msg_buf *mb; + ssize_t len; + + //cc_debug(call, "U-Plane U_DATA-ind"); + mb = dect_mbuf_alloc(dh); + if (mb == NULL) + return; + + len = recv(call->lu_sap->fd, mb->data, sizeof(mb->head), 0); + if (len < 0) + return; + mb->len = len; + + //dect_mbuf_dump(mb, "LU1"); + dh->ops->cc_ops->dl_u_data_ind(dh, call, mb); +} + +static int dect_call_connect_uplane(const struct dect_handle *dh, + struct dect_call *call) +{ + struct sockaddr_dect_lu addr; + + call->lu_sap = dect_socket(dh, SOCK_STREAM, DECT_LU1_SAP); + if (call->lu_sap == NULL) + goto err1; + + dect_transaction_get_ulei(&addr, &call->transaction); + if (connect(call->lu_sap->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) + goto err2; + + call->lu_sap->data = call; + call->lu_sap->callback = dect_cc_lu_event; + if (dect_register_fd(dh, call->lu_sap, DECT_FD_READ) < 0) + goto err2; + cc_debug(call, "U-Plane connected"); + return 0; + +err2: + dect_close(dh, call->lu_sap); + call->lu_sap = NULL; +err1: + cc_debug(call, "U-Plane connect failed: %s", strerror(errno)); + return -1; +} + +static void dect_call_disconnect_uplane(const struct dect_handle *dh, + struct dect_call *call) +{ + dect_unregister_fd(dh, call->lu_sap); + dect_close(dh, call->lu_sap); + call->lu_sap = NULL; +} + +struct dect_call *dect_call_alloc(const struct dect_handle *dh) +{ + struct dect_call *call; + + call = dect_zalloc(dh, sizeof(*call) + dh->ops->cc_ops->priv_size); + if (call == NULL) + goto err1; + + call->setup_timer = dect_alloc_timer(dh); + if (call->setup_timer == NULL) + goto err2; + call->setup_timer->data = call; + + call->state = DECT_CC_NULL; + return call; + +err2: + dect_free(dh, call); +err1: + return NULL; +} + +static void dect_call_destroy(const struct dect_handle *dh, + struct dect_call *call) +{ + if (call->state == DECT_CC_CALL_PRESENT) + dect_stop_timer(dh, call->setup_timer); + dect_free(dh, call->setup_timer); + dect_free(dh, call); +} + +static int dect_cc_send_msg(struct dect_handle *dh, struct dect_call *call, + const struct dect_sfmt_ie_desc *desc, + const struct dect_msg_common *msg, + enum dect_cc_msg_types type, const char *prefix) +{ + return dect_lce_send(dh, &call->transaction, desc, msg, type, prefix); +} + +static void dect_cc_setup_timer(struct dect_handle *dh, struct dect_timer *timer) +{ + struct dect_call *call = timer->data; + struct dect_ie_release_reason release_reason; + struct dect_mncc_release_param param = { + .release_reason = &release_reason, + }; + + cc_debug(call, "setup timer"); + // release-com + + dect_ie_init(&release_reason); + release_reason.reason = DECT_RELEASE_TIMER_EXPIRY; + dh->ops->cc_ops->mncc_reject_ind(dh, call, ¶m); + + dect_close_transaction(dh, &call->transaction); + dect_call_destroy(dh, call); +} + +int dect_mncc_setup_req(struct dect_handle *dh, struct dect_call *call, + const struct dect_ipui *ipui, + const struct dect_mncc_setup_param *param) +{ + struct dect_ie_portable_identity portable_identity; + struct dect_ie_fixed_identity fixed_identity; + struct dect_cc_setup_msg msg = { + .portable_identity = &portable_identity, + .fixed_identity = &fixed_identity, + .basic_service = param->basic_service, + .iwu_attributes = param->iwu_attributes, + .cipher_info = param->cipher_info, + .facility = param->facility, + .progress_indicator = param->progress_indicator, + .display = param->display, + .keypad = param->keypad, + .signal = param->signal, + .feature_activate = param->feature_activate, + .feature_indicate = param->feature_indicate, + .network_parameter = param->network_parameter, + .terminal_capability = param->terminal_capability, + .end_to_end_compatibility = param->end_to_end_compatibility, + .rate_parameters = param->rate_parameters, + .transit_delay = param->transit_delay, + .window_size = param->window_size, + .calling_party_number = param->calling_party_number, + .called_party_number = param->called_party_number, + .called_party_subaddress = param->called_party_subaddress, + .calling_party_name = param->calling_party_name, + .sending_complete = param->sending_complete, + .iwu_to_iwu = param->iwu_to_iwu, + .iwu_packet = param->iwu_packet, + }; + + cc_debug(call, "setup request"); + + call->transaction.pd = DECT_S_PD_CC; + if (dect_open_transaction(dh, &call->transaction, ipui) < 0) + goto err1; + + fixed_identity.type = ID_TYPE_PARK; + memcpy(&fixed_identity.ari, &dh->pari, sizeof(fixed_identity.ari)); + portable_identity.type = ID_TYPE_IPUI; + portable_identity.ipui = *ipui; + + if (dect_cc_send_msg(dh, call, cc_setup_msg_desc, &msg.common, + CC_SETUP, "CC-SETUP") < 0) + goto err2; + call->state = DECT_CC_CALL_PRESENT; + + call->setup_timer->callback = dect_cc_setup_timer; + dect_start_timer(dh, call->setup_timer, DECT_CC_SETUP_TIMEOUT); + return 0; + +err2: + dect_close_transaction(dh, &call->transaction); +err1: + return -1; +} + +int dect_mncc_setup_ack_req(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_setup_ack_param *param) +{ + struct dect_cc_setup_ack_msg msg = { + .portable_identity = call->pt_id, + .fixed_identity = call->ft_id, + .info_type = param->info_type, + .location_area = param->location_area, + .display = param->display, + .signal = param->signal, + .feature_indicate = param->feature_indicate, + .transit_delay = param->transit_delay, + .window_size = param->window_size, + .delimiter_request = param->delimiter_request, + .iwu_to_iwu = param->iwu_to_iwu, + .iwu_packet = param->iwu_packet, + }; + + dect_cc_send_msg(dh, call, cc_setup_ack_msg_desc, &msg.common, + CC_SETUP_ACK, "CC-SETUP_ACK"); + return 0; +} + +int dect_mncc_reject_req(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_release_param *param) +{ + return 0; +} + +int dect_mncc_call_proc_req(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_call_proc_param *param) +{ + struct dect_cc_call_proc_msg msg = { + .facility = param->facility, + .progress_indicator = param->progress_indicator, + .display = param->display, + .signal = param->signal, + .feature_indicate = param->feature_indicate, + .transit_delay = param->transit_delay, + .window_size = param->window_size, + .iwu_to_iwu = param->iwu_to_iwu, + .iwu_packet = param->iwu_packet, + }; + + dect_cc_send_msg(dh, call, cc_call_proc_msg_desc, &msg.common, + CC_CALL_PROC, "CC-CALL_PROC"); + return 0; +} +int dect_mncc_alert_req(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_alert_param *param) +{ + struct dect_cc_alerting_msg msg = { + .facility = param->facility, + //.progress_indicator = param->progress_indicator, + .display = param->display, + .signal = param->signal, + .feature_indicate = param->feature_indicate, + .terminal_capability = param->terminal_capability, + .transit_delay = param->transit_delay, + .window_size = param->window_size, + .iwu_to_iwu = param->iwu_to_iwu, + .iwu_packet = param->iwu_packet, + }; + + // FIXME FIXME FIXME FIXME + if (param->progress_indicator.list.next != NULL) { + init_list_head(&msg.progress_indicator.list); + dect_ie_list_move(&msg.progress_indicator, + (struct dect_ie_repeat_indicator *)¶m->progress_indicator); + } + + dect_cc_send_msg(dh, call, cc_alerting_msg_desc, &msg.common, + CC_ALERTING, "CC-ALERTING"); + return 0; +} + +int dect_mncc_connect_req(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_connect_param *param) +{ + struct dect_cc_connect_msg msg = { + .facility = param->facility, + .progress_indicator = param->progress_indicator, + .display = param->display, + .signal = param->signal, + .feature_indicate = param->feature_indicate, + .terminal_capability = param->terminal_capability, + .transit_delay = param->transit_delay, + .window_size = param->window_size, + .iwu_to_iwu = param->iwu_to_iwu, + .iwu_packet = param->iwu_packet, + }; + + dect_cc_send_msg(dh, call, cc_connect_msg_desc, &msg.common, + CC_CONNECT, "CC-CONNECT"); + + dect_call_connect_uplane(dh, call); + return 0; +} + +int dect_mncc_connect_res(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_connect_param *param) +{ + struct dect_cc_connect_ack_msg msg = { + .display = param->display, + .feature_indicate = param->feature_indicate, + //.iwu_to_iwu = param->iwu_to_iwu, + .iwu_packet = param->iwu_packet, + }; + + dect_call_connect_uplane(dh, call); + if (dect_cc_send_msg(dh, call, cc_connect_ack_msg_desc, &msg.common, + CC_CONNECT_ACK, "CC-CONNECT_ACK") < 0) + goto err1; + + call->state = DECT_CC_ACTIVE; + return 0; + +err1: + dect_call_disconnect_uplane(dh, call); + return -1; +} + +int dect_mncc_release_req(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_release_param *param) +{ + struct dect_cc_release_msg msg = { + .release_reason = param->release_reason, + .facility = param->facility, + .display = param->display, + .feature_indicate = param->feature_indicate, + .iwu_to_iwu = param->iwu_to_iwu, + .iwu_packet = param->iwu_packet, + }; + + dect_cc_send_msg(dh, call, cc_release_msg_desc, &msg.common, + CC_RELEASE, "CC-RELEASE"); + call->state = DECT_CC_RELEASE_PENDING; + return 0; +} + +int dect_mncc_release_res(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_release_param *param) +{ + struct dect_cc_release_com_msg msg = { + .release_reason = param->release_reason, + .identity_type = param->identity_type, + .location_area = param->location_area, + .iwu_attributes = param->iwu_attributes, + //.facility = param->facility, + .display = param->display, + .feature_indicate = param->feature_indicate, + .network_parameter = param->network_parameter, + .iwu_to_iwu = param->iwu_to_iwu, + .iwu_packet = param->iwu_packet, + }; + + dect_cc_send_msg(dh, call, cc_release_com_msg_desc, &msg.common, + CC_RELEASE_COM, "CC-RELEASE_COM"); + + dect_call_disconnect_uplane(dh, call); + dect_close_transaction(dh, &call->transaction); + dect_call_destroy(dh, call); + return 0; +} + +int dect_mncc_facility_req(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_facility_param *param) +{ + return 0; +} + +int dect_mncc_info_req(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_info_param *param) +{ + struct dect_cc_info_msg msg = { + .location_area = param->location_area, + .nwk_assigned_identity = param->nwk_assigned_identity, + .facility = param->facility, +// .progress_indicator = param->progress_indicator, + .display = param->display, + .keypad = param->keypad, + .signal = param->signal, + .feature_activate = param->feature_activate, + .feature_indicate = param->feature_indicate, + .network_parameter = param->network_parameter, + .called_party_number = param->called_party_number, + .called_party_subaddress = param->called_party_subaddress, + .calling_party_number = param->calling_party_number, + .calling_party_name = param->calling_party_name, + .sending_complete = param->sending_complete, + .iwu_to_iwu = param->iwu_to_iwu, + .iwu_packet = param->iwu_packet, + }; + + // FIXME FIXME FIXME FIXME + if (param->progress_indicator.list.next != NULL) { + init_list_head(&msg.progress_indicator.list); + dect_ie_list_move(&msg.progress_indicator, + (struct dect_ie_repeat_indicator *)¶m->progress_indicator); + } + + dect_cc_send_msg(dh, call, cc_info_msg_desc, &msg.common, + CC_INFO, "CC-INFO"); + return 0; +} + +int dect_mncc_modify_req(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_modify_param *param) +{ + return 0; +} + +int dect_mncc_modify_res(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_modify_param *param) +{ + return 0; +} + +int dect_mncc_hold_req(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_hold_param *param) +{ + return 0; +} + +int dect_mncc_hold_res(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_hold_param *param) +{ + return 0; +} + +int dect_mncc_retrieve_req(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_hold_param *param) +{ + return 0; +} + +int dect_mncc_retrieve_res(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_hold_param *param) +{ + return 0; +} + +int dect_mncc_iwu_info_req(struct dect_handle *dh, struct dect_call *call, + const struct dect_mncc_iwu_info_param *param) +{ + return 0; +} + +static void dect_mncc_alert_ind(struct dect_handle *dh, struct dect_call *call, + const struct dect_cc_alerting_msg *msg) +{ + struct dect_mncc_alert_param param = { + .facility = msg->facility, + .progress_indicator = msg->progress_indicator, + .display = msg->display, + .signal = msg->signal, + .feature_indicate = msg->feature_indicate, + .terminal_capability = msg->terminal_capability, + .transit_delay = msg->transit_delay, + .window_size = msg->window_size, + .iwu_to_iwu = msg->iwu_to_iwu, + .iwu_packet = msg->iwu_packet, + }; + + dh->ops->cc_ops->mncc_alert_ind(dh, call, ¶m); +} + +static void dect_cc_rcv_alerting(struct dect_handle *dh, struct dect_call *call, + struct dect_msg_buf *mb) +{ + struct dect_cc_alerting_msg msg; + + dect_mbuf_dump(mb, "CC-ALERTING"); + if (call->state != DECT_CC_CALL_PRESENT) + ; + + if (dect_parse_sfmt_msg(dh, cc_alerting_msg_desc, &msg.common, mb) < 0) + return; + + dect_mncc_alert_ind(dh, call, &msg); + dect_msg_free(dh, cc_alerting_msg_desc, &msg.common); + call->state = DECT_CC_CALL_RECEIVED; +} + +static void dect_cc_rcv_call_proc(struct dect_handle *dh, struct dect_call *call, + struct dect_msg_buf *mb) +{ + struct dect_cc_call_proc_msg msg; + + dect_mbuf_dump(mb, "CC-CALL_PROC"); + if (dect_parse_sfmt_msg(dh, cc_call_proc_msg_desc, &msg.common, mb) < 0) + return; +} + +static void dect_mncc_connect_ind(struct dect_handle *dh, struct dect_call *call, + struct dect_cc_connect_msg *msg) +{ + struct dect_mncc_connect_param param = { + .facility = msg->facility, + .progress_indicator = msg->progress_indicator, + .display = msg->display, + .signal = msg->signal, + .feature_indicate = msg->feature_indicate, + .terminal_capability = msg->terminal_capability, + .transit_delay = msg->transit_delay, + .window_size = msg->window_size, + .iwu_to_iwu = msg->iwu_to_iwu, + .iwu_packet = msg->iwu_packet, + }; + + dh->ops->cc_ops->mncc_connect_ind(dh, call, ¶m); +} + +static void dect_cc_rcv_connect(struct dect_handle *dh, struct dect_call *call, + struct dect_msg_buf *mb) +{ + struct dect_cc_connect_msg msg; + + if (call->state != DECT_CC_CALL_PRESENT && + call->state != DECT_CC_CALL_RECEIVED) + ; + + dect_mbuf_dump(mb, "CC-CONNECT"); + if (dect_parse_sfmt_msg(dh, cc_connect_msg_desc, &msg.common, mb) < 0) + return; + + if (call->setup_timer != NULL) { + dect_stop_timer(dh, call->setup_timer); + dect_free(dh, call->setup_timer); + call->setup_timer = NULL; + } + + dect_mncc_connect_ind(dh, call, &msg); + dect_msg_free(dh, cc_connect_msg_desc, &msg.common); +} + +static void dect_cc_rcv_setup_ack(struct dect_handle *dh, struct dect_call *call, + struct dect_msg_buf *mb) +{ + struct dect_cc_setup_ack_msg msg; + + dect_mbuf_dump(mb, "CC-SETUP_ACK"); + if (dect_parse_sfmt_msg(dh, cc_setup_ack_msg_desc, &msg.common, mb) < 0) + return; + + dect_msg_free(dh, cc_setup_ack_msg_desc, &msg.common); +} + +static void dect_cc_rcv_connect_ack(struct dect_handle *dh, struct dect_call *call, + struct dect_msg_buf *mb) +{ + struct dect_cc_connect_ack_msg msg; + + dect_mbuf_dump(mb, "CC-CONNECT_ACK"); + if (dect_parse_sfmt_msg(dh, cc_connect_ack_msg_desc, &msg.common, mb) < 0) + return; + + dect_msg_free(dh, cc_connect_ack_msg_desc, &msg.common); +} + +static void dect_mncc_release_ind(struct dect_handle *dh, struct dect_call *call, + const struct dect_cc_release_msg *msg) +{ + struct dect_mncc_release_param param = { + .release_reason = msg->release_reason, + .facility = msg->facility, + .display = msg->display, + .feature_indicate = msg->feature_indicate, + .iwu_to_iwu = msg->iwu_to_iwu, + .iwu_packet = msg->iwu_packet, + }; + + dh->ops->cc_ops->mncc_release_ind(dh, call, ¶m); +} + +static void dect_cc_rcv_release(struct dect_handle *dh, struct dect_call *call, + struct dect_msg_buf *mb) +{ + struct dect_cc_release_msg msg; + + dect_mbuf_dump(mb, "CC-RELEASE"); + if (dect_parse_sfmt_msg(dh, cc_release_msg_desc, &msg.common, mb) < 0) + return; + + dect_mncc_release_ind(dh, call, &msg); + dect_msg_free(dh, cc_release_msg_desc, &msg.common); +} + +static void dect_mncc_release_cfm(struct dect_handle *dh, struct dect_call *call, + const struct dect_cc_release_com_msg *msg) +{ + struct dect_mncc_release_param param = { + .release_reason = msg->release_reason, + .identity_type = msg->identity_type, + .location_area = msg->location_area, + .iwu_attributes = msg->iwu_attributes, + .facility = msg->facility, + .display = msg->display, + .feature_indicate = msg->feature_indicate, + .network_parameter = msg->network_parameter, + .iwu_to_iwu = msg->iwu_to_iwu, + .iwu_packet = msg->iwu_packet, + + }; + + dh->ops->cc_ops->mncc_release_cfm(dh, call, ¶m); +} + +static void dect_cc_rcv_release_com(struct dect_handle *dh, struct dect_call *call, + struct dect_msg_buf *mb) +{ + struct dect_cc_release_com_msg msg; + + dect_mbuf_dump(mb, "CC-RELEASE_COM"); + if (dect_parse_sfmt_msg(dh, cc_release_com_msg_desc, &msg.common, mb) < 0) + return; + + if (call->state == DECT_CC_RELEASE_PENDING) + dect_mncc_release_cfm(dh, call, &msg); + else { + struct dect_mncc_release_param param = { + .release_reason = msg.release_reason, + .facility = msg.facility, + .iwu_to_iwu = msg.iwu_to_iwu, + .iwu_packet = msg.iwu_packet, + }; + dh->ops->cc_ops->mncc_release_ind(dh, call, ¶m); + } + + dect_msg_free(dh, cc_release_com_msg_desc, &msg.common); + + if (call->lu_sap != NULL) + dect_call_disconnect_uplane(dh, call); + dect_close_transaction(dh, &call->transaction); + dect_call_destroy(dh, call); +} + +static void dect_cc_rcv_iwu_info(struct dect_handle *dh, struct dect_call *call, + struct dect_msg_buf *mb) +{ + struct dect_cc_iwu_info_msg msg; + + dect_mbuf_dump(mb, "CC-IWU_INFO"); + if (dect_parse_sfmt_msg(dh, cc_iwu_info_msg_desc, &msg.common, mb) < 0) + return; + + dect_msg_free(dh, cc_iwu_info_msg_desc, &msg.common); +} + +static void dect_cc_rcv_notify(struct dect_handle *dh, struct dect_call *call, + struct dect_msg_buf *mb) +{ + struct dect_cc_notify_msg msg; + + dect_mbuf_dump(mb, "CC-NOTIFY"); + if (dect_parse_sfmt_msg(dh, cc_notify_msg_desc, &msg.common, mb) < 0) + return; + + dect_msg_free(dh, cc_notify_msg_desc, &msg.common); +} + +static void dect_mncc_info_ind(struct dect_handle *dh, struct dect_call *call, + struct dect_cc_info_msg *msg) +{ + struct dect_mncc_info_param param = { + .location_area = msg->location_area, + .nwk_assigned_identity = msg->nwk_assigned_identity, + .facility = msg->facility, + .progress_indicator = msg->progress_indicator, + .display = msg->display, + .keypad = msg->keypad, + .signal = msg->signal, + .feature_activate = msg->feature_activate, + .feature_indicate = msg->feature_indicate, + .network_parameter = msg->network_parameter, + .called_party_number = msg->called_party_number, + .called_party_subaddress = msg->called_party_subaddress, + .calling_party_number = msg->calling_party_number, + .calling_party_name = msg->calling_party_name, + .sending_complete = msg->sending_complete, + .iwu_to_iwu = msg->iwu_to_iwu, + .iwu_packet = msg->iwu_packet, + }; + + dh->ops->cc_ops->mncc_info_ind(dh, call, ¶m); +} + +static void dect_cc_rcv_info(struct dect_handle *dh, struct dect_call *call, + struct dect_msg_buf *mb) +{ + struct dect_cc_info_msg msg; + + dect_mbuf_dump(mb, "CC-INFO"); + if (call->state == DECT_CC_CALL_INITIATED || + call->state == DECT_CC_CALL_PRESENT) + ; + + if (dect_parse_sfmt_msg(dh, cc_info_msg_desc, &msg.common, mb) < 0) + return; + + dect_mncc_info_ind(dh, call, &msg); + dect_msg_free(dh, cc_info_msg_desc, &msg.common); +} + +static void dect_cc_rcv(struct dect_handle *dh, struct dect_transaction *ta, + struct dect_msg_buf *mb) +{ + struct dect_call *call = container_of(ta, struct dect_call, transaction); + + cc_debug(call, "receive msg type %x", mb->type); + switch (mb->type) { + case CC_ALERTING: + return dect_cc_rcv_alerting(dh, call, mb); + case CC_CALL_PROC: + return dect_cc_rcv_call_proc(dh, call, mb); + case CC_CONNECT: + return dect_cc_rcv_connect(dh, call, mb); + case CC_SETUP_ACK: + return dect_cc_rcv_setup_ack(dh, call, mb); + case CC_CONNECT_ACK: + return dect_cc_rcv_connect_ack(dh, call, mb); + case CC_SERVICE_CHANGE: + case CC_SERVICE_ACCEPT: + case CC_SERVICE_REJECT: + case CC_RELEASE: + return dect_cc_rcv_release(dh, call, mb); + case CC_RELEASE_COM: + return dect_cc_rcv_release_com(dh, call, mb); + case CC_IWU_INFO: + return dect_cc_rcv_iwu_info(dh, call, mb); + case CC_NOTIFY: + return dect_cc_rcv_notify(dh, call, mb); + case CC_INFO: + return dect_cc_rcv_info(dh, call, mb); + } +} + +static void dect_mncc_setup_ind(struct dect_handle *dh, + struct dect_call *call, + struct dect_cc_setup_msg *msg) +{ + struct dect_mncc_setup_param param = { + .basic_service = msg->basic_service, + .cipher_info = msg->cipher_info, + .display = msg->display, + .keypad = msg->keypad, + .signal = msg->signal, + .feature_activate = msg->feature_activate, + .feature_indicate = msg->feature_indicate, + .network_parameter = msg->network_parameter, + .terminal_capability = msg->terminal_capability, + .end_to_end_compatibility = msg->end_to_end_compatibility, + .rate_parameters = msg->rate_parameters, + .transit_delay = msg->transit_delay, + .window_size = msg->window_size, + .called_party_number = msg->called_party_number, + .called_party_subaddress = msg->called_party_subaddress, + .calling_party_number = msg->calling_party_number, + .calling_party_name = msg->calling_party_name, + .sending_complete = msg->sending_complete, + .iwu_to_iwu = msg->iwu_to_iwu, + .iwu_packet = msg->iwu_packet, + }; + + dect_ie_list_move(¶m.iwu_attributes, &msg->iwu_attributes); + dect_ie_list_move(¶m.facility, &msg->facility); + dect_ie_list_move(¶m.progress_indicator, &msg->progress_indicator); + + dh->ops->cc_ops->mncc_setup_ind(dh, call, ¶m); +} + +static void dect_cc_rcv_setup(struct dect_handle *dh, + const struct dect_transaction *req, + struct dect_msg_buf *mb) +{ + struct dect_ie_connection_attributes *connection_attributes; + struct dect_ie_call_attributes *call_attributes; + struct dect_cc_setup_msg msg; + struct dect_call *call; + + dect_mbuf_dump(mb, "CC-SETUP"); + if (dect_parse_sfmt_msg(dh, cc_setup_msg_desc, &msg.common, mb) < 0) + return; + + dect_foreach_ie(call_attributes, msg.call_attributes) + dect_debug("call attributes\n"); + + dect_foreach_ie(connection_attributes, msg.connection_attributes) + dect_debug("connection attributes\n"); + + call = dect_call_alloc(dh); + if (call == NULL) + goto out; + call->ft_id = dect_ie_hold(msg.fixed_identity); + call->pt_id = dect_ie_hold(msg.portable_identity); + call->state = DECT_CC_CALL_INITIATED; + dect_confirm_transaction(dh, &call->transaction, req); + cc_debug(call, "new call"); + + dect_mncc_setup_ind(dh, call, &msg); +out: + dect_msg_free(dh, cc_setup_msg_desc, &msg.common); +} + +static void dect_cc_open(struct dect_handle *dh, + const struct dect_transaction *req, + struct dect_msg_buf *mb) +{ + dect_debug("CC: unknown transaction msg type: %x\n", mb->type); + + switch (mb->type) { + case CC_SETUP: + return dect_cc_rcv_setup(dh, req, mb); + case CC_RELEASE: + case CC_RELEASE_COM: + break; + default: + // send release-com + break; + } +} + +static void dect_cc_shutdown(struct dect_handle *dh, + struct dect_transaction *ta) +{ + struct dect_call *call = container_of(ta, struct dect_call, transaction); + + cc_debug(call, "shutdown"); + dh->ops->cc_ops->mncc_reject_ind(dh, call, NULL); + dect_close_transaction(dh, &call->transaction); + dect_call_destroy(dh, call); +} + +static const struct dect_nwk_protocol cc_protocol = { + .name = "Call Control", + .pd = DECT_S_PD_CC, + .max_transactions = 7, + .open = dect_cc_open, + .shutdown = dect_cc_shutdown, + .rcv = dect_cc_rcv, +}; + +static void __init dect_cc_init(void) +{ + dect_lce_register_protocol(&cc_protocol); +} 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] <infile >outfile + decode [-3|4|5] [-a|u|l] <infile >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 <pcmfile >g721file + + # Read 40Kbps G.723 and output A-law + decode -5 -a <g723file >alawfile + + # Compress and then decompress u-law data using 24Kbps G.723 + encode -3 <ulawin | deoced -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 <stdio.h> +#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 <stdio.h> +#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 <stdlib.h> +#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/dsaa.c b/src/dsaa.c new file mode 100644 index 0000000..48db952 --- /dev/null +++ b/src/dsaa.c @@ -0,0 +1,211 @@ +/* + * From: https://dedected.org/trac/wiki/DSAA-Reversing + * + * FIXME: LICENSE? Copyrights? + */ + +#include <stdlib.h> +#include <stdarg.h> +#include <libdect.h> + +static const uint8_t sbox[256] = { + 176, 104, 111, 246, 125, 232, 22, 133, + 57, 124, 127, 222, 67, 240, 89, 169, + 251, 128, 50, 174, 95, 37, 140, 245, + 148, 107, 216, 234, 136, 152, 194, 41, + 207, 58, 80, 150, 28, 8, 149, 244, + 130, 55, 10, 86, 44, 255, 79, 196, + 96, 165, 131, 33, 48, 248, 243, 40, + 250, 147, 73, 52, 66, 120, 191, 252, + 97, 198, 241, 167, 26, 83, 3, 77, + 134, 211, 4, 135, 126, 143, 160, 183, + 49, 179, 231, 14, 47, 204, 105, 195, + 192, 217, 200, 19, 220, 139, 1, 82, + 193, 72, 239, 175, 115, 221, 92, 46, + 25, 145, 223, 34, 213, 61, 13, 163, + 88, 129, 62, 253, 98, 68, 36, 45, + 182, 141, 90, 5, 23, 190, 39, 84, + 93, 157, 214, 173, 108, 237, 100, 206, + 242, 114, 63, 212, 70, 164, 16, 162, + 59, 137, 151, 76, 110, 116, 153, 228, + 227, 187, 238, 112, 0, 189, 101, 32, + 15, 122, 233, 158, 155, 199, 181, 99, + 230, 170, 225, 138, 197, 7, 6, 30, + 94, 29, 53, 56, 119, 20, 17, 226, + 185, 132, 24, 159, 42, 203, 218, 247, + 166, 178, 102, 123, 177, 156, 109, 106, + 249, 254, 202, 201, 168, 65, 188, 121, + 219, 184, 103, 186, 172, 54, 171, 146, + 75, 215, 229, 154, 118, 205, 21, 31, + 78, 74, 87, 113, 27, 85, 9, 81, + 51, 12, 180, 142, 43, 224, 208, 91, + 71, 117, 69, 64, 2, 209, 60, 236, + 35, 235, 11, 210, 161, 144, 38, 18, +}; + +static void bitperm(uint8_t start, uint8_t step, uint8_t * key) +{ + static uint8_t copy[8]; + unsigned int i; + + memcpy(copy, key, 8); + memset(key, 0, 8); + + for (i = 0; i < 64; i++) { + key[start/8] |= ((copy[i / 8] & (1 << (i % 8))) >> + (i % 8)) << (start % 8); + start += step; + start %= 64; + } +} + +#if 0 +static void bitperm1(uint8_t * key) +{ + bitperm(46, 35, key); +} + +static void bitperm2(uint8_t * key) +{ + bitperm(25, 47, key); +} + +static void bitperm3(uint8_t * key) +{ + bitperm(60, 27, key); +} + +static void bitperm4(uint8_t * key) +{ + bitperm(55, 39, key); +} +#endif + +static const uint8_t mix_factor[3][8] = { + {2, 2, 2, 2, 3, 3, 3, 3}, + {2, 2, 3, 3, 2, 2, 3, 3}, + {2, 3, 2, 3, 2, 3, 2, 3}, +}; + +static const uint8_t mix_index[3][8] = { + {4, 5, 6, 7, 0, 1, 2, 3}, + {2, 3, 0, 1, 6, 7, 4, 5}, + {1, 0, 3, 2, 5, 4, 7, 6}, +}; + +static void mix(uint8_t start, uint8_t alg, uint8_t * key) +{ + unsigned int i; + uint8_t copy[8]; + + memcpy(copy, key, 8); + for (i=0; i<8; i++) + key[i] = copy[mix_index[alg][i]] + mix_factor[alg][i] * copy[i]; +} + +static void mix1(uint8_t * key) +{ + mix(4, 0, key); +} + +static void mix2(uint8_t * key) +{ + mix(2, 1, key); +} + +static void mix3(uint8_t * key) +{ + mix(1, 2, key); +} + +static void sub(uint8_t * s, uint8_t * t) +{ + unsigned int i; + + for (i = 0; i < 8; i++) + s[i] = sbox[s[i] ^ t[i]]; +} + +/* return in s */ +static void cassable(uint8_t start, uint8_t step, uint8_t * t, uint8_t * s) +{ + unsigned int i; + + for(i = 0; i < 2; i++) { + bitperm(start, step, t); + sub(s, t); + mix1(s); + + bitperm(start, step, t); + sub(s, t); + mix2(s); + + bitperm(start, step, t); + sub(s, t); + mix3(s); + } +} + +/* return in rand, modifies key */ +static void step1(uint8_t * rand, uint8_t * key) +{ + + uint8_t tmp[8]; + + memcpy(tmp, rand, 8); + + cassable(46, 35, tmp, key); + cassable(25, 47, key, rand); + + memcpy(key, rand, 8); +} + +static void step2(uint8_t * rand, uint8_t * key) +{ + + uint8_t tmp[8]; + + memcpy(tmp, rand, 8); + + cassable(60, 27, tmp, key); + cassable(55, 39, key, rand); + + memcpy(key, rand, 8); +} + +static void rev(uint8_t * v, uint8_t n) +{ + unsigned int i; + uint8_t tmp; + + for (i = 0; i < n / 2; i++) { + tmp = v[i]; + v[i] = v[n - i - 1]; + v[n - i - 1] = tmp; + } +} + +void dsaa_main(uint8_t * rand, uint8_t * key, uint8_t * out); +void dsaa_main(uint8_t * rand, uint8_t * key, uint8_t * out) +{ + uint8_t a[8]; + uint8_t b[8]; + + rev(rand, 8); + rev(key, 16); + + step1(rand, key + 4); + + memcpy(a, key + 4, 8); + + memcpy(key + 4, key + 12, 4); + memcpy(b, a, 8); + step2(b, key); + + rev(a, 8); + rev(key, 8); + + memcpy(out, key, 4); + memcpy(out + 4, a, 8); + memcpy(out + 12, key + 4, 4); +} diff --git a/src/identities.c b/src/identities.c new file mode 100644 index 0000000..4a66f15 --- /dev/null +++ b/src/identities.c @@ -0,0 +1,182 @@ +/* + * DECT Identities + * + * Copyright (c) 2009 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <asm/byteorder.h> + +#include <libdect.h> +#include <identities.h> +#include <utils.h> + + +uint8_t dect_parse_ari(struct dect_ari *ari, uint64_t a) +{ + ari->arc = (a & DECT_ARI_ARC_MASK) >> DECT_ARI_ARC_SHIFT; + switch (ari->arc) { + case DECT_ARC_A: + ari->emc = (a & DECT_ARI_A_EMC_MASK) >> DECT_ARI_A_EMC_SHIFT; + ari->fpn = (a & DECT_ARI_A_FPN_MASK) >> DECT_ARI_A_FPN_SHIFT; + dect_debug("ARI class A: EMC: %.4x FPN: %.5x\n", + ari->emc, ari->fpn); + return DECT_ARC_A_LEN; + case DECT_ARC_B: + ari->eic = (a & DECT_ARI_B_EIC_MASK) >> DECT_ARI_B_EIC_SHIFT; + ari->fpn = (a & DECT_ARI_B_FPN_MASK) >> DECT_ARI_B_FPN_SHIFT; + ari->fps = (a & DECT_ARI_B_FPS_MASK) >> DECT_ARI_B_FPS_SHIFT; + return DECT_ARC_B_LEN; + case DECT_ARC_C: + ari->poc = (a & DECT_ARI_C_POC_MASK) >> DECT_ARI_C_POC_SHIFT; + ari->fpn = (a & DECT_ARI_C_FPN_MASK) >> DECT_ARI_C_FPN_SHIFT; + ari->fps = (a & DECT_ARI_C_FPS_MASK) >> DECT_ARI_C_FPS_SHIFT; + return DECT_ARC_C_LEN; + case DECT_ARC_D: + ari->gop = (a & DECT_ARI_D_GOP_MASK) >> DECT_ARI_D_GOP_SHIFT; + ari->fpn = (a & DECT_ARI_D_FPN_MASK) >> DECT_ARI_D_FPN_SHIFT; + return DECT_ARC_D_LEN; + case DECT_ARC_E: + ari->fil = (a & DECT_ARI_E_FIL_MASK) >> DECT_ARI_E_FIL_SHIFT; + ari->fpn = (a & DECT_ARI_E_FPN_MASK) >> DECT_ARI_E_FPN_SHIFT; + return DECT_ARC_E_LEN; + default: + return 0; + } +} + +uint64_t dect_build_ari(const struct dect_ari *ari) +{ + uint64_t a = 0; + + a |= (uint64_t)ari->arc << DECT_ARI_ARC_SHIFT; + switch (ari->arc) { + case DECT_ARC_A: + a |= (uint64_t)ari->emc << DECT_ARI_A_EMC_SHIFT; + a |= (uint64_t)ari->fpn << DECT_ARI_A_FPN_SHIFT; + break; + case DECT_ARC_B: + a |= (uint64_t)ari->eic << DECT_ARI_B_EIC_SHIFT; + a |= (uint64_t)ari->fpn << DECT_ARI_B_FPN_SHIFT; + a |= (uint64_t)ari->fps << DECT_ARI_B_FPS_SHIFT; + break; + case DECT_ARC_C: + a |= (uint64_t)ari->poc << DECT_ARI_C_POC_SHIFT; + a |= (uint64_t)ari->fpn << DECT_ARI_C_FPN_SHIFT; + a |= (uint64_t)ari->fps << DECT_ARI_C_FPS_SHIFT; + break; + case DECT_ARC_D: + a |= (uint64_t)ari->gop << DECT_ARI_D_GOP_SHIFT; + a |= (uint64_t)ari->fpn << DECT_ARI_D_FPN_SHIFT; + break; + case DECT_ARC_E: + a |= (uint64_t)ari->fil << DECT_ARI_E_FIL_SHIFT; + a |= (uint64_t)ari->fpn << DECT_ARI_E_FPN_SHIFT; + break; + } + return a; +} + +static bool dect_parse_ipei(struct dect_ipei *ipei, uint64_t i) +{ + ipei->emc = (i & DECT_IPEI_EMC_MASK) >> DECT_IPEI_EMC_SHIFT; + ipei->psn = (i & DECT_IPEI_PSN_MASK); + dect_debug("IPEI: EMC: %.4x PSN: %.5x\n", ipei->emc, ipei->psn); + return true; +} + +static uint64_t dect_build_ipei(const struct dect_ipei *ipei) +{ + uint64_t i = 0; + + i |= (uint64_t)ipei->emc << DECT_IPEI_EMC_SHIFT; + i |= (uint64_t)ipei->psn; + return i; +} + +bool dect_parse_ipui(struct dect_ipui *ipui, const uint8_t *ptr, uint8_t len) +{ + uint64_t tmp; + + tmp = __be64_to_cpu(*(__be64 *)&ptr[0]) >> 24; + + ipui->put = ptr[0] & DECT_IPUI_PUT_MASK; + switch (ipui->put) { + case DECT_IPUI_N: + if (len != 40) + return false; + return dect_parse_ipei(&ipui->pun.n.ipei, tmp); + case DECT_IPUI_O: + case DECT_IPUI_P: + case DECT_IPUI_Q: + case DECT_IPUI_R: + case DECT_IPUI_S: + case DECT_IPUI_T: + case DECT_IPUI_U: + default: + dect_debug("IPUI: unhandled type %u\n", ipui->put); + return false; + } +} + +uint8_t dect_build_ipui(uint8_t *ptr, const struct dect_ipui *ipui) +{ + uint64_t tmp; + + switch (ipui->put) { + case DECT_IPUI_N: + tmp = dect_build_ipei(&ipui->pun.n.ipei); + break; + case DECT_IPUI_O: + case DECT_IPUI_P: + case DECT_IPUI_Q: + case DECT_IPUI_R: + case DECT_IPUI_S: + case DECT_IPUI_T: + case DECT_IPUI_U: + return 0; + default: + return 0; + } + + ptr[0] = ipui->put; + ptr[0] |= (tmp >> 32) & ~DECT_IPUI_PUT_MASK; + ptr[1] = tmp >> 24; + ptr[2] = tmp >> 16; + ptr[3] = tmp >> 8; + ptr[4] = tmp >> 0; + return 40; +} + +bool dect_ipui_cmp(const struct dect_ipui *i1, const struct dect_ipui *i2) +{ + return memcmp(i1, i2, sizeof(*i1)); +} + +void dect_default_individual_tpui(struct dect_tpui *tpui, + const struct dect_ipui *ipui) +{ + tpui->tpui = DECT_TPUI_DEFAULT_INDIVIDUAL_ID; + + switch (ipui->put) { + case DECT_IPUI_N: + tpui->tpui |= ipui->pun.n.ipei.psn & + DECT_TPUI_DEFAULT_INDIVIDUAL_IPUI_MASK; + break; + case DECT_IPUI_O: + case DECT_IPUI_P: + case DECT_IPUI_Q: + case DECT_IPUI_R: + case DECT_IPUI_S: + case DECT_IPUI_T: + case DECT_IPUI_U: + return; + } +} diff --git a/src/keypad.c b/src/keypad.c new file mode 100644 index 0000000..bbca36f --- /dev/null +++ b/src/keypad.c @@ -0,0 +1,80 @@ +/* + * DECT Keypad Protocol helpers + * + * Copyright (c) 2009 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <stdio.h> +#include <stdint.h> +#include <libdect.h> +#include <dect/keypad.h> +#include <utils.h> + +struct dect_keypad_buffer { + struct dect_timer *timer; + struct dect_ie_keypad keypad; + uint8_t timeout; + void *priv; + void (*complete)(struct dect_handle *, void *, + struct dect_ie_keypad *); +}; + +static void dect_keypad_timer(struct dect_handle *dh, struct dect_timer *timer) +{ + struct dect_keypad_buffer *kb = timer->data; + + kb->complete(dh, kb->priv, &kb->keypad); +} + +void dect_keypad_append(struct dect_handle *dh, struct dect_keypad_buffer *kb, + const struct dect_ie_keypad *keypad, + bool sending_complete) +{ + unsigned int len; + + if (keypad->len > 0) + dect_stop_timer(dh, kb->timer); + + len = sizeof(kb->keypad.info) - kb->keypad.len; + len = min((unsigned int)keypad->len, len); + memcpy(kb->keypad.info + kb->keypad.len, keypad->info, len); + kb->keypad.len += len; + + if (sending_complete || kb->keypad.len == sizeof(kb->keypad.info)) + kb->complete(dh, kb->priv, &kb->keypad); + else if (keypad->len > 0) + dect_start_timer(dh, kb->timer, kb->timeout); +} + +struct dect_keypad_buffer * +dect_keypad_buffer_init(const struct dect_handle *dh, uint8_t timeout, + void (*complete)(struct dect_handle *, void *priv, + struct dect_ie_keypad *keypad), + void *priv) +{ + struct dect_keypad_buffer *kb; + + kb = dect_zalloc(dh, sizeof(*kb)); + if (kb == NULL) + goto err1; + + kb->timer = dect_alloc_timer(dh); + if (kb->timer == NULL) + goto err2; + kb->timer->callback = dect_keypad_timer; + kb->timer->data = kb; + + kb->complete = complete; + kb->priv = priv; + kb->timeout = timeout; + return kb; + +err1: + dect_free(dh, kb); +err2: + return NULL; +} diff --git a/src/lce.c b/src/lce.c new file mode 100644 index 0000000..43d3ec7 --- /dev/null +++ b/src/lce.c @@ -0,0 +1,790 @@ +/* + * DECT Link Control Entity (LCE) + * + * Copyright (c) 2009 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <linux/byteorder/little_endian.h> +#include <linux/dect.h> +#include <asm/byteorder.h> + +#include <libdect.h> +#include <identities.h> +#include <utils.h> +#include <s_fmt.h> +#include <b_fmt.h> +#include <lce.h> +#include <ss.h> + +static const struct dect_sfmt_ie_desc lce_page_response_msg[] = { + DECT_SFMT_IE(S_VL_IE_PORTABLE_IDENTITY, IE_NONE, IE_MANDATORY, 0), + DECT_SFMT_IE(S_VL_IE_FIXED_IDENTITY, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_NWK_ASSIGNED_IDENTITY, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CIPHER_INFO, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_ESCAPE_TO_PROPRIETARY, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE_END_MSG +}; + +static const struct dect_sfmt_ie_desc lce_page_reject_msg[] = { + DECT_SFMT_IE(S_VL_IE_PORTABLE_IDENTITY, IE_MANDATORY, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_FIXED_IDENTITY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_REJECT_REASON, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_ESCAPE_TO_PROPRIETARY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE_END_MSG +}; + +static const struct dect_nwk_protocol *protocols[DECT_S_PD_MAX + 1]; + +void dect_lce_register_protocol(const struct dect_nwk_protocol *protocol) +{ + protocols[protocol->pd] = protocol; +} + +struct dect_msg_buf *dect_mbuf_alloc(const struct dect_handle *dh) +{ + struct dect_msg_buf *mb; + + mb = dect_malloc(dh, sizeof(*mb)); + if (mb == NULL) + return NULL; + memset(mb->head, 0, sizeof(mb->head)); + mb->data = mb->head; + mb->len = 0; + mb->type = 0; + return mb; +} + +static ssize_t dect_mbuf_rcv(const struct dect_fd *dfd, struct dect_msg_buf *mb) +{ + ssize_t len; + + memset(mb, 0, sizeof(*mb)); + mb->data = mb->head; + len = recv(dfd->fd, mb->data, sizeof(mb->head), 0); + if (len < 0) + return len; + mb->len = len; + return len; +} + +#if 0 +/* + * Location Table + */ + +static struct dect_lte *dect_lte_get_by_ipui(const struct dect_handle *dh, + const struct dect_ipui *ipui) +{ + struct dect_lte *lte; + + list_for_each_entry(lte, &dh->ldb.entries, list) { + if (!dect_ipui_cmp(<e->ipui, ipui)) + return lte; + } + return NULL; +} + +static struct dect_lte *dect_lte_alloc(const struct dect_handle *dh, + const struct dect_ipui *ipui) +{ + struct dect_lte *lte; + + lte = dect_malloc(dh, sizeof(*lte)); + if (lte == NULL) + return NULL; + memcpy(<e->ipui, ipui, sizeof(lte->ipui)); + return lte; +} +#endif + +/* + * Paging + */ + +static int dect_lce_broadcast(const struct dect_handle *dh, + const uint8_t *msg, size_t len) +{ + ssize_t size; + + dect_hexdump("BROADCAST", msg, len); + size = send(dh->b_sap->fd, msg, len, 0); + assert(size == (ssize_t)len); + return 0; +} + +int dect_lce_group_ring(struct dect_handle *dh, enum dect_ring_patterns pattern) +{ + struct dect_short_page_msg msg; + uint16_t page; + + msg.hdr = DECT_LCE_PAGE_W_FLAG; + msg.hdr |= DECT_LCE_PAGE_GENERAL_VOICE; + + page = pattern << DECT_LCE_SHORT_PAGE_RING_PATTERN_SHIFT; + page = 0; + page |= DECT_TPUI_CBI & DECT_LCE_SHORT_PAGE_TPUI_MASK; + msg.information = __cpu_to_be16(page); + + return dect_lce_broadcast(dh, &msg.hdr, sizeof(msg)); +} + +static int dect_lce_page(const struct dect_handle *dh, + const struct dect_ipui *ipui) +{ + struct dect_short_page_msg msg; + struct dect_tpui tpui; + uint16_t page; + + dect_default_individual_tpui(&tpui, ipui); + + msg.hdr = DECT_LCE_PAGE_GENERAL_VOICE; + page = tpui.tpui & DECT_LCE_SHORT_PAGE_TPUI_MASK; + msg.information = __cpu_to_be16(page); + + return dect_lce_broadcast(dh, &msg.hdr, sizeof(msg)); +} + +static void dect_lce_rcv_short_page(struct dect_handle *dh, + struct dect_msg_buf *mb) +{ + struct dect_short_page_msg *msg = (void *)mb->data; + uint8_t hdr; + bool w; + + w = msg->hdr & DECT_LCE_PAGE_W_FLAG; + hdr = msg->hdr & DECT_LCE_PAGE_HDR_MASK; + dect_debug("short page: w=%u hdr=%u information=%x\n", + w, hdr, __be16_to_cpu(msg->information)); +} + +static void dect_lce_bsap_event(struct dect_handle *dh, struct dect_fd *dfd, + uint32_t events) +{ + struct dect_msg_buf _mb, *mb = &_mb; + + if (dect_mbuf_rcv(dfd, mb) < 0) + return; + dect_mbuf_dump(mb, "BCAST RX"); + + switch (mb->len) { + case 3: + return dect_lce_rcv_short_page(dh, mb); + default: + break; + } +} + +/* + * Data links + */ + +#define ddl_debug(ddl, fmt, args...) \ + dect_debug("link %d (%s): " fmt "\n", \ + (ddl)->dfd ? (ddl)->dfd->fd : -1, \ + ddl_states[(ddl)->state], ## args) + +static const char *ddl_states[DECT_DATA_LINK_STATE_MAX + 1] = { + [DECT_DATA_LINK_RELEASED] = "RELEASED", + [DECT_DATA_LINK_ESTABLISHED] = "ESTABLISHED", + [DECT_DATA_LINK_ESTABLISH_PENDING] = "ESTABLISH_PENDING", + [DECT_DATA_LINK_RELEASE_PENDING] = "RELEASE_PENDING", + [DECT_DATA_LINK_SUSPENDED] = "SUSPENDED", + [DECT_DATA_LINK_SUSPEND_PENDING] = "SUSPEND_PENDING", + [DECT_DATA_LINK_RESUME_PENDING] = "RESUME_PENDING", +}; + +static struct dect_data_link *dect_ddl_get_by_ipui(const struct dect_handle *dh, + const struct dect_ipui *ipui) +{ + struct dect_data_link *ddl; + + list_for_each_entry(ddl, &dh->links, list) { + if (!dect_ipui_cmp(&ddl->ipui, ipui)) + return ddl; + } + return NULL; +} + +static struct dect_transaction * +dect_ddl_transaction_lookup(const struct dect_data_link *ddl, uint8_t pd, uint8_t tv) +{ + struct dect_transaction *ta; + + list_for_each_entry(ta, &ddl->transactions, list) { + if (ta->pd == pd && ta->tv == tv) + return ta; + } + return NULL; +} + +static void dect_ddl_destroy(struct dect_handle *dh, struct dect_data_link *ddl) +{ + struct dect_msg_buf *mb, *next; + + ddl_debug(ddl, "destroy"); + assert(ddl->sdu_timer == NULL); + assert(list_empty(&ddl->transactions)); + + list_del(&ddl->list); + list_for_each_entry_safe(mb, next, &ddl->msg_queue, list) + dect_free(dh, mb); + if (ddl->dfd != NULL) { + dect_unregister_fd(dh, ddl->dfd); + dect_close(dh, ddl->dfd); + } + dect_free(dh, ddl); +} + +static struct dect_data_link *dect_ddl_alloc(const struct dect_handle *dh) +{ + struct dect_data_link *ddl; + + ddl = dect_zalloc(dh, sizeof(*ddl)); + if (ddl == NULL) + return NULL; + ddl->state = DECT_DATA_LINK_RELEASED; + init_list_head(&ddl->list); + init_list_head(&ddl->transactions); + init_list_head(&ddl->msg_queue); + ddl_debug(ddl, "alloc"); + return ddl; +} + +static void dect_ddl_sdu_timer(struct dect_handle *dh, struct dect_timer *timer) +{ + struct dect_data_link *ddl = timer->data; + + ddl_debug(ddl, "SDU timer"); + dect_free(dh, ddl->sdu_timer); + ddl->sdu_timer = NULL; + dect_ddl_destroy(dh, ddl); +} + +static int dect_ddl_schedule_sdu_timer(const struct dect_handle *dh, + struct dect_data_link *ddl) +{ + ddl->sdu_timer = dect_alloc_timer(dh); + if (ddl->sdu_timer == NULL) + return -1; + ddl->sdu_timer->data = ddl; + ddl->sdu_timer->callback = dect_ddl_sdu_timer; + dect_start_timer(dh, ddl->sdu_timer, DECT_DDL_ESTABLISH_SDU_TIMEOUT); + ddl_debug(ddl, "start SDU timer"); + return 0; +} + +static void dect_ddl_stop_sdu_timer(const struct dect_handle *dh, + struct dect_data_link *ddl) +{ + ddl_debug(ddl, "stop SDU timer"); + dect_stop_timer(dh, ddl->sdu_timer); + dect_free(dh, ddl->sdu_timer); + ddl->sdu_timer = NULL; +} + +static int dect_send(const struct dect_handle *dh, + const struct dect_data_link *ddl, + struct dect_msg_buf *mb) +{ + ssize_t len; + + dect_mbuf_dump(mb, "TX"); + len = send(ddl->dfd->fd, mb->data, mb->len, 0); + assert(len == (ssize_t)mb->len); + dect_free(dh, mb); + return len; +} + +/** + * dect_send - Queue a S-Format message for transmission to the LCE + * + */ +int dect_lce_send(const struct dect_handle *dh, + const struct dect_transaction *ta, + const struct dect_sfmt_ie_desc *desc, + const struct dect_msg_common *msg, uint8_t type, + const char *prefix) +{ + struct dect_data_link *ddl = ta->link; + struct dect_msg_buf *mb; + + mb = dect_mbuf_alloc(dh); + if (mb == NULL) + return -1; + + dect_mbuf_reserve(mb, DECT_S_HDR_SIZE); + dect_build_sfmt_msg(dh, desc, msg, mb); + + if (ddl->sdu_timer != NULL) + dect_ddl_stop_sdu_timer(dh, ddl); + + dect_mbuf_push(mb, DECT_S_HDR_SIZE); + mb->data[1] = type; + mb->data[0] = ta->pd; + mb->data[0] |= ta->tv << DECT_S_TI_TV_SHIFT; + if (ta->role == DECT_TRANSACTION_RESPONDER) + mb->data[0] |= DECT_S_TI_F_FLAG; + + switch (ddl->state) { + case DECT_DATA_LINK_ESTABLISHED: + return dect_send(dh, ddl, mb); + case DECT_DATA_LINK_ESTABLISH_PENDING: + list_add_tail(&mb->list, &ddl->msg_queue); + return 0; + default: + BUG(); + } +} + +/** + * dect_ddl_establish - Establish an outgoing data link + * + */ +static void dect_lce_data_link_event(struct dect_handle *dh, + struct dect_fd *dfd, uint32_t events); + +static struct dect_data_link *dect_ddl_establish(struct dect_handle *dh, + const struct dect_ipui *ipui) +{ + struct dect_data_link *ddl; + + //lte = dect_lte_get_by_ipui(dh, lte); + ddl = dect_ddl_alloc(dh); + if (ddl == NULL) + goto err1; + ddl->state = DECT_DATA_LINK_ESTABLISH_PENDING; + + if (dh->mode == DECT_MODE_FP) { + memcpy(&ddl->ipui, ipui, sizeof(ddl->ipui)); + dect_lce_page(dh, ipui); + } else { + ddl->dfd = dect_socket(dh, SOCK_SEQPACKET, DECT_S_SAP); + if (ddl->dfd == NULL) + goto err2; + + ddl->dlei.dect_family = AF_DECT; + ddl->dlei.dect_ari = dect_build_ari(&dh->pari) >> 24; + ddl->dlei.dect_pmid = 0xe98a1; + ddl->dlei.dect_lln = 1; + ddl->dlei.dect_sapi = 0; + + ddl->dfd->callback = dect_lce_data_link_event; + ddl->dfd->data = ddl; + if (dect_register_fd(dh, ddl->dfd, DECT_FD_WRITE) < 0) + goto err2; + + if (connect(ddl->dfd->fd, (struct sockaddr *)&ddl->dlei, + sizeof(ddl->dlei)) < 0 && errno != EAGAIN) + perror("connect\n"); + } + + list_add_tail(&ddl->list, &dh->links); + return ddl; + +err2: + dect_free(dh, ddl); +err1: + return NULL; +} + +#if 0 +int dect_send_reject(const struct dect_handle *dh, + const struct dect_transaction *ta, + enum dect_reject_reasons reason) +{ + struct dect_ie_reject_reason reject_reason; + struct dect_lce_page_reject msg = { + .portable_identity = NULL, + .reject_reason = &reject_reason, + }; + + dect_send(dh, ta, mb); +} +#endif + +static void dect_ddl_complete_direct_establish(struct dect_handle *dh, + struct dect_data_link *ddl) +{ + struct dect_msg_buf *mb, *mb_next; + + ddl->state = DECT_DATA_LINK_ESTABLISHED; + ddl_debug(ddl, "complete direct link establishment"); + + /* Send queued messages */ + list_for_each_entry_safe(mb, mb_next, &ddl->msg_queue, list) { + list_del(&mb->list); + dect_send(dh, ddl, mb); + } + + dect_unregister_fd(dh, ddl->dfd); + dect_register_fd(dh, ddl->dfd, DECT_FD_READ); +} + +static void dect_ddl_complete_indirect_establish(struct dect_handle *dh, + struct dect_data_link *ddl, + struct dect_data_link *req) +{ + struct dect_transaction *ta, *ta_next; + struct dect_msg_buf *mb, *mb_next; + + ddl_debug(ddl, "complete indirect link establishment req %p", req); + /* Transfer transactions to the new link */ + list_for_each_entry_safe(ta, ta_next, &req->transactions, list) { + ddl_debug(ta->link, "transfer transaction to link %p\n", ddl); + list_move_tail(&ta->list, &ddl->transactions); + ta->link = ddl; + } + + /* Send queued messages */ + list_for_each_entry_safe(mb, mb_next, &req->msg_queue, list) { + list_del(&mb->list); + dect_send(dh, ddl, mb); + } + + /* Release pending link */ + dect_ddl_destroy(dh, req); +} + +static void dect_lce_rcv_page_response(struct dect_handle *dh, + const struct dect_transaction *ta, + struct dect_msg_buf *mb) +{ + struct dect_lce_page_response msg; + struct dect_data_link *i, *req = NULL; + + ddl_debug(ta->link, "LCE-PAGE-RESPONSE"); + if (dect_parse_sfmt_msg(dh, lce_page_response_msg, &msg.common, mb) < 0) + return; + + dect_debug("portable_identity: %p\n", msg.portable_identity); + dect_debug("fixed identity: %p\n", msg.fixed_identity); + dect_debug("nwk assigned identity: %p\n", msg.nwk_assigned_identity); + dect_debug("cipher info: %p\n", msg.cipher_info); + dect_debug("escape: %p\n", msg.escape_to_proprietary); + + list_for_each_entry(i, &dh->links, list) { + if (dect_ipui_cmp(&i->ipui, &msg.portable_identity->ipui)) + continue; + if (i->state != DECT_DATA_LINK_ESTABLISH_PENDING) + continue; + req = i; + break; + } + + if (req != NULL) + dect_ddl_complete_indirect_establish(dh, ta->link, req); + else { + /* send page reject */ + dect_ddl_destroy(dh, ta->link); + } + + dect_msg_free(dh, lce_page_response_msg, &msg.common); +} + +static void dect_lce_rcv_page_reject(struct dect_handle *dh, + struct dect_transaction *ta, + struct dect_msg_buf *mb) +{ + struct dect_lce_page_reject msg; + + ddl_debug(ta->link, "LCE-PAGE-REJECT"); + if (dect_parse_sfmt_msg(dh, lce_page_reject_msg, &msg.common, mb) < 0) + return; + dect_msg_free(dh, lce_page_reject_msg, &msg.common); +} + +static void dect_lce_rcv(struct dect_handle *dh, struct dect_transaction *ta, + struct dect_msg_buf *mb) +{ + switch (mb->type) { + case DECT_LCE_PAGE_REJECT: + return dect_lce_rcv_page_reject(dh, ta, mb); + default: + ddl_debug(ta->link, "LCE: unknown message type %x", mb->type); + return; + } +} + +static void dect_lce_open(struct dect_handle *dh, + const struct dect_transaction *ta, + struct dect_msg_buf *mb) +{ + switch (mb->type) { + case DECT_LCE_PAGE_RESPONSE: + return dect_lce_rcv_page_response(dh, ta, mb); + default: + ddl_debug(ta->link, "LCE: unknown message type %x", mb->type); + return; + } +} + +static const struct dect_nwk_protocol lce_protocol = { + .name = "Link Control", + .pd = DECT_S_PD_LCE, + .max_transactions = 1, + .open = dect_lce_open, + .rcv = dect_lce_rcv, +}; + +static void dect_ddl_rcv_msg(struct dect_handle *dh, struct dect_data_link *ddl) +{ + struct dect_msg_buf _mb, *mb = &_mb; + struct dect_transaction *ta; + uint8_t pd, tv; + bool f; + + if (ddl->sdu_timer != NULL) + dect_ddl_stop_sdu_timer(dh, ddl); + + if (dect_mbuf_rcv(ddl->dfd, mb) < 0) + return; + dect_mbuf_dump(mb, "RX"); + + if (mb->len < DECT_S_HDR_SIZE) + return; + f = (mb->data[0] & DECT_S_TI_F_FLAG); + tv = (mb->data[0] & DECT_S_TI_TV_MASK) >> DECT_S_TI_TV_SHIFT; + pd = (mb->data[0] & DECT_S_PD_MASK); + mb->type = (mb->data[1] & DECT_S_PD_MSG_TYPE_MASK); + dect_mbuf_pull(mb, DECT_S_HDR_SIZE); + + if (pd >= array_size(protocols) || protocols[pd] == NULL) { + ddl_debug(ddl, "unknown protocol %u\n", pd); + return; + } + + if (tv == DECT_TV_CONNECTIONLESS) + return dect_clss_rcv(dh, mb); + + ta = dect_ddl_transaction_lookup(ddl, pd, tv); + if (ta == NULL) { + struct dect_transaction req = { + .link = ddl, + .pd = pd, + .role = DECT_TRANSACTION_RESPONDER, + .tv = tv, + }; + ddl_debug(ddl, "new transaction: protocol: %s F: %u TV: %u", + protocols[pd]->name, f, tv); + protocols[pd]->open(dh, &req, mb); + } else + protocols[pd]->rcv(dh, ta, mb); +} + +static void dect_lce_data_link_event(struct dect_handle *dh, + struct dect_fd *dfd, uint32_t events) +{ + struct dect_data_link *ddl = dfd->data; + + if (events & DECT_FD_WRITE) { + switch (ddl->state) { + case DECT_DATA_LINK_ESTABLISH_PENDING: + dect_ddl_complete_direct_establish(dh, ddl); + break; + default: + break; + } + } + + if (events & DECT_FD_READ) { + dect_ddl_rcv_msg(dh, ddl); + } +} + +static int dect_transaction_alloc_tv(const struct dect_data_link *ddl, + const struct dect_nwk_protocol *protocol) +{ + uint16_t tv; + + for (tv = 0; tv < protocol->max_transactions; tv++) { + if (dect_ddl_transaction_lookup(ddl, protocol->pd, tv)) + continue; + return tv; + } + return -1; +} + +int dect_open_transaction(struct dect_handle *dh, struct dect_transaction *ta, + const struct dect_ipui *ipui) +{ + struct dect_data_link *ddl; + int tv; + + ddl = dect_ddl_get_by_ipui(dh, ipui); + if (ddl == NULL) { + ddl = dect_ddl_establish(dh, ipui); + if (ddl == NULL) + return -1; + } + + ddl_debug(ddl, "open transaction"); + tv = dect_transaction_alloc_tv(ddl, protocols[ta->pd]); + if (tv < 0) + return -1; + + ta->link = ddl; + ta->role = DECT_TRANSACTION_INITIATOR; + ta->tv = tv; + + list_add_tail(&ta->list, &ddl->transactions); + return 0; +} + +void dect_confirm_transaction(struct dect_handle *dh, struct dect_transaction *ta, + const struct dect_transaction *req) +{ + ta->link = req->link; + ta->tv = req->tv; + ta->role = req->role; + ta->pd = req->pd; + + ddl_debug(req->link, "confirm transaction"); + list_add_tail(&ta->list, &req->link->transactions); +} + +void dect_close_transaction(struct dect_handle *dh, struct dect_transaction *ta) +{ + struct dect_data_link *ddl = ta->link; + + ddl_debug(ddl, "close transaction"); + list_del(&ta->list); + list_for_each_entry(ta, &ddl->transactions, list) + dect_debug("\ttrans %p proto %u TV %u\n", ta, ta->pd, ta->tv); + if (!list_empty(&ddl->transactions)) + return; + dect_ddl_destroy(dh, ddl); +} + +void dect_transaction_get_ulei(struct sockaddr_dect_lu *addr, + const struct dect_transaction *ta) +{ + struct dect_data_link *ddl = ta->link; + + memset(addr, 0, sizeof(*addr)); + addr->dect_family = AF_DECT; + addr->dect_ari = ddl->dlei.dect_ari; + addr->dect_pmid = ddl->dlei.dect_pmid; + addr->dect_lcn = ddl->dlei.dect_lcn; +} + +static void dect_lce_ssap_listener_event(struct dect_handle *dh, + struct dect_fd *dfd, uint32_t events) +{ + struct dect_data_link *ddl; + struct dect_fd *nfd; + + ddl = dect_ddl_alloc(dh); + if (ddl == NULL) + goto err1; + + nfd = dect_accept(dh, dfd, (struct sockaddr *)&ddl->dlei, + sizeof(ddl->dlei)); + if (nfd == NULL) + goto err2; + ddl->dfd = nfd; + + nfd->callback = dect_lce_data_link_event; + nfd->data = ddl; + if (dect_register_fd(dh, nfd, DECT_FD_READ) < 0) + goto err3; + + ddl->state = DECT_DATA_LINK_ESTABLISHED; + if (dect_ddl_schedule_sdu_timer(dh, ddl) < 0) + goto err4; + + list_add_tail(&ddl->list, &dh->links); + ddl_debug(ddl, "new link: PMID: %x LCN: %u LLN: %u SAPI: %u", + ddl->dlei.dect_pmid, ddl->dlei.dect_lcn, + ddl->dlei.dect_lln, ddl->dlei.dect_sapi); + return; + +err4: + dect_unregister_fd(dh, nfd); +err3: + dect_close(dh, nfd); +err2: + dect_free(dh, ddl); +err1: + return; +} + +int dect_lce_init(struct dect_handle *dh) +{ + struct sockaddr_dect_ssap s_addr; + struct sockaddr_dect b_addr; + + /* Open B-SAP socket */ + dh->b_sap = dect_socket(dh, SOCK_DGRAM, DECT_B_SAP); + if (dh->b_sap == NULL) + goto err1; + + b_addr.dect_family = AF_DECT; + b_addr.dect_index = dh->index; + if (bind(dh->b_sap->fd, (struct sockaddr *)&b_addr, sizeof(b_addr)) < 0) + goto err2; + + dh->b_sap->callback = dect_lce_bsap_event; + if (dect_register_fd(dh, dh->b_sap, DECT_FD_READ) < 0) + goto err2; + + /* Open S-SAP listener socket */ + dh->s_sap = dect_socket(dh, SOCK_SEQPACKET, DECT_S_SAP); + if (dh->s_sap == NULL) + goto err3; + + memset(&s_addr, 0, sizeof(s_addr)); + s_addr.dect_family = AF_DECT; + s_addr.dect_lln = 1; + s_addr.dect_sapi = 0; + + if (bind(dh->s_sap->fd, (struct sockaddr *)&s_addr, sizeof(s_addr)) < 0) + goto err4; + if (listen(dh->s_sap->fd, 10) < 0) + goto err4; + + dh->s_sap->callback = dect_lce_ssap_listener_event; + if (dect_register_fd(dh, dh->s_sap, DECT_FD_READ) < 0) + goto err4; + + protocols[DECT_S_PD_LCE] = &lce_protocol; + return 0; + +err4: + dect_close(dh, dh->s_sap); +err3: + dect_unregister_fd(dh, dh->b_sap); +err2: + dect_close(dh, dh->b_sap); +err1: + return -1; +} + +void dect_lce_exit(struct dect_handle *dh) +{ + struct dect_data_link *ddl, *ddl_next; + struct dect_transaction *ta, *ta_next; + LIST_HEAD(transactions); + + list_for_each_entry_safe(ddl, ddl_next, &dh->links, list) { + ddl_debug(ddl, "shutdown"); + list_splice_init(&ddl->transactions, &transactions); + list_for_each_entry_safe(ta, ta_next, &transactions, list) + protocols[ta->pd]->shutdown(dh, ta); + } + + dect_unregister_fd(dh, dh->s_sap); + dect_close(dh, dh->s_sap); + + dect_unregister_fd(dh, dh->b_sap); + dect_close(dh, dh->b_sap); +} diff --git a/src/libdect.c b/src/libdect.c new file mode 100644 index 0000000..a3c006e --- /dev/null +++ b/src/libdect.c @@ -0,0 +1,81 @@ +/* + * libdect public API functions + * + * Copyright (c) 2009 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <unistd.h> + +#include <libdect.h> +#include <netlink.h> +#include <utils.h> +#include <lce.h> + +static void __fmtstring(1, 0) (*debug_hook)(const char *fmt, va_list ap); + +void dect_set_debug_hook(void (*fn)(const char *fmt, va_list ap)) +{ + debug_hook = fn; +} + +void __fmtstring(1, 2) dect_debug(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (debug_hook != NULL) + debug_hook(fmt, ap); + else + vprintf(fmt, ap); + va_end(ap); +} + +struct dect_handle *dect_alloc_handle(struct dect_ops *ops) +{ + struct dect_handle *dh; + + if (ops->malloc == NULL) + ops->malloc = malloc; + if (ops->free == NULL) + ops->free = free; + + dh = ops->malloc(sizeof(*dh)); + if (dh == NULL) + return NULL; + dh->ops = ops; + init_list_head(&dh->links); + return dh; +} + +int dect_init(struct dect_handle *dh) +{ + int err; + + err = dect_netlink_init(dh); + if (err < 0) + goto err1; + + err = dect_lce_init(dh); + if (err < 0) + goto err2; + return 0; + +err2: + dect_netlink_exit(dh); +err1: + return err; +} + +void dect_close_handle(struct dect_handle *dh) +{ + dect_lce_exit(dh); + dect_netlink_exit(dh); + dect_free(dh, dh); +} diff --git a/src/mm.c b/src/mm.c new file mode 100644 index 0000000..0ae11f6 --- /dev/null +++ b/src/mm.c @@ -0,0 +1,154 @@ +/* + * DECT Mobility Management (MM) + * + * Copyright (c) 2009 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <linux/dect.h> + +#include <libdect.h> +#include <utils.h> +#include <s_fmt.h> +#include <lce.h> +#include <mm.h> + +static const struct dect_sfmt_ie_desc mm_access_rights_request_msg_desc[] = { + DECT_SFMT_IE(S_VL_IE_PORTABLE_IDENTITY, IE_NONE, IE_MANDATORY, 0), + DECT_SFMT_IE(S_VL_IE_AUTH_TYPE, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CIPHER_INFO, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_SETUP_CAPABILITY, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_TERMINAL_CAPABILITY, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_IWU_TO_IWU, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_MODEL_IDENTIFIER, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CODEC_LIST, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_ESCAPE_TO_PROPRIETARY, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE_END_MSG +}; + +static const struct dect_sfmt_ie_desc mm_access_rights_reject_msg_desc[] = { + DECT_SFMT_IE(S_VL_IE_REJECT_REASON, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_DURATION, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_IWU_TO_IWU, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_ESCAPE_TO_PROPRIETARY, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE_END_MSG +}; + +#define mm_debug(fmt, args...) \ + dect_debug("MM: " fmt "\n", ## args) + +int dect_mm_access_rights_req(struct dect_handle *dh, + const struct dect_mm_access_rights_param *param) +{ + static struct dect_transaction transaction; + struct dect_ipui ipui; + struct dect_mm_access_rights_request_msg msg = { + .portable_identity = param->portable_identity, + .auth_type = param->auth_type, + .cipher_info = param->cipher_info, + .setup_capability = NULL, + //.terminal_capability = param->terminal_capability, + .model_identifier = param->model_identifier, + .codec_list = NULL, + .escape_to_proprietary = NULL, + }; + + mm_debug("access rights request"); + transaction.pd = DECT_S_PD_MM; + + if (dect_open_transaction(dh, &transaction, &ipui) < 0) + goto err1; + + if (dect_lce_send(dh, &transaction, mm_access_rights_request_msg_desc, + &msg.common, DECT_MM_ACCESS_RIGHTS_REQUEST, + "MM-ACCESS_RIGHTS_REQUEST") < 0) + goto err2; + return 0; + +err2: + dect_close_transaction(dh, &transaction); +err1: + return -1; +} + +static void dect_mm_rcv_access_rights_reject(struct dect_handle *dh, + struct dect_msg_buf *mb) +{ + struct dect_mm_access_rights_reject_msg msg; + + if (dect_parse_sfmt_msg(dh, mm_access_rights_reject_msg_desc, &msg.common, mb) < 0) + return; +} + +static void dect_mm_rcv(struct dect_handle *dh, struct dect_transaction *ta, + struct dect_msg_buf *mb) +{ + mm_debug("receive msg type %x", mb->type); + switch (mb->type) { + case DECT_MM_AUTHENTICATION_REQUEST: + case DECT_MM_AUTHENTICATION_REPLY: + case DECT_MM_KEY_ALLOCATE: + case DECT_MM_AUTHENTICATION_REJECT: + case DECT_MM_ACCESS_RIGHTS_REQUEST: + break; + case DECT_MM_ACCESS_RIGHTS_ACCEPT: + break; + case DECT_MM_ACCESS_RIGHTS_REJECT: + return dect_mm_rcv_access_rights_reject(dh, mb); + case DECT_MM_ACCESS_RIGHTS_TERMINATE_REQUEST: + case DECT_MM_ACCESS_RIGHTS_TERMINATE_ACCEPT: + case DECT_MM_ACCESS_RIGHTS_TERMINATE_REJECT: + case DECT_MM_CIPHER_REQUEST: + case DECT_MM_CIPHER_SUGGEST: + case DECT_MM_CIPHER_REJECT: + case DECT_MM_INFO_REQUEST: + case DECT_MM_INFO_ACCEPT: + case DECT_MM_INFO_SUGGEST: + case DECT_MM_INFO_REJECT: + case DECT_MM_LOCATE_REQUEST: + case DECT_MM_LOCATE_ACCEPT: + case DECT_MM_DETACH: + case DECT_MM_LOCATE_REJECT: + case DECT_MM_IDENTITY_REQUEST: + case DECT_MM_IDENTITY_REPLY: + case DECT_MM_TEMPORARY_IDENTITY_ASSIGN: + case DECT_MM_TEMPORARY_IDENTITY_ASSIGN_ACK: + case DECT_MM_TEMPORARY_IDENTITY_ASSIGN_REJ: + break; + } +} + +static void dect_mm_open(struct dect_handle *dh, + const struct dect_transaction *req, + struct dect_msg_buf *mb) +{ + dect_debug("MM: unknown transaction msg type: %x\n", mb->type); + + switch (mb->type) { + default: + break; + } +} + +static const struct dect_nwk_protocol mm_protocol = { + .name = "Mobility Management", + .pd = DECT_S_PD_MM, + .max_transactions = 1, + .open = dect_mm_open, + //.shutdown = dect_mm_shutdown, + .rcv = dect_mm_rcv, +}; + +static void __init dect_mm_init(void) +{ + dect_lce_register_protocol(&mm_protocol); +} diff --git a/src/netlink.c b/src/netlink.c new file mode 100644 index 0000000..72099e4 --- /dev/null +++ b/src/netlink.c @@ -0,0 +1,139 @@ +/* + * DECT Netlink Interface + * + * Copyright (c) 2009 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <stdint.h> +#include <stdbool.h> +#include <stdarg.h> + +#include <netlink/netlink.h> +#include <netlink/object.h> +#include <netlink/msg.h> +#include <netlink/dect/cluster.h> +#include <netlink/dect/ari.h> + +#include <libdect.h> +#include <netlink.h> +#include <utils.h> + +static void dect_netlink_event(struct dect_handle *dh, struct dect_fd *fd, + uint32_t event) +{ + nl_recvmsgs_default(dh->nlsock); +} + +static void dect_netlink_set_callback(struct dect_handle *dh, + nl_recvmsg_msg_cb_t func, + void *arg) +{ + nl_socket_modify_cb(dh->nlsock, NL_CB_VALID, NL_CB_CUSTOM, func, arg); +} + +static void dect_netlink_parse_ari(struct dect_ari *ari, const struct nl_dect_ari *nlari) +{ + ari->arc = nl_dect_ari_get_class(nlari); + switch (ari->arc) { + case DECT_ARC_A: + ari->emc = nl_dect_ari_get_emc(nlari); + ari->fpn = nl_dect_ari_get_fpn(nlari); + dect_debug("ARI class A: EMC: %.4x FPN: %.5x\n", + ari->emc, ari->fpn); + break; + case DECT_ARC_B: + ari->eic = nl_dect_ari_get_eic(nlari); + ari->fpn = nl_dect_ari_get_fpn(nlari); + ari->fps = nl_dect_ari_get_fps(nlari); + break; + case DECT_ARC_C: + ari->poc = nl_dect_ari_get_poc(nlari); + ari->fpn = nl_dect_ari_get_fpn(nlari); + ari->fps = nl_dect_ari_get_fps(nlari); + break; + case DECT_ARC_D: + ari->gop = nl_dect_ari_get_gop(nlari); + ari->fpn = nl_dect_ari_get_fpn(nlari); + break; + case DECT_ARC_E: + ari->fil = nl_dect_ari_get_fil(nlari); + ari->fpn = nl_dect_ari_get_fpn(nlari); + break; + } +} + +static void get_cluster_cb(struct nl_object *obj, void *arg) +{ + struct dect_handle *dh = arg; + struct nl_dect_cluster *cl = (struct nl_dect_cluster *)obj; + + dh->index = nl_dect_cluster_get_index(cl); + dh->mode = nl_dect_cluster_get_mode(cl); + dect_netlink_parse_ari(&dh->pari, nl_dect_cluster_get_pari(cl)); +} + +static int dect_netlink_get_cluster_cb(struct nl_msg *msg, void *arg) +{ + return nl_msg_parse(msg, get_cluster_cb, arg); +} + +int dect_netlink_init(struct dect_handle *dh) +{ + struct nl_dect_cluster *cl; + int err; + + dh->nlsock = nl_socket_alloc(); + if (dh->nlsock == NULL) + goto err1; + + err = nl_connect(dh->nlsock, NETLINK_DECT); + if (err < 0) + goto err2; + + err = nl_socket_set_nonblocking(dh->nlsock); + if (err < 0) + goto err2; + + dh->nlfd = dect_alloc_fd(dh); + if (dh->nlfd == NULL) + goto err2; + dh->nlfd->fd = nl_socket_get_fd(dh->nlsock); + + dh->nlfd->callback = dect_netlink_event; + if (dect_register_fd(dh, dh->nlfd, DECT_FD_READ)) + goto err3; + + cl = nl_dect_cluster_alloc(); + if (cl == NULL) + goto err4; + nl_dect_cluster_set_name(cl, "cluster0"); + + dect_netlink_set_callback(dh, dect_netlink_get_cluster_cb, dh); + err = nl_dect_cluster_query(dh->nlsock, cl, 0); + dect_netlink_set_callback(dh, NULL, NULL); + if (err < 0) + goto err5; + + return 0; +err5: + nl_dect_cluster_put(cl); +err4: + dect_unregister_fd(dh, dh->nlfd); +err3: + dect_free(dh, dh->nlfd); +err2: + nl_close(dh->nlsock); +err1: + return -1; +} + +void dect_netlink_exit(struct dect_handle *dh) +{ + dect_unregister_fd(dh, dh->nlfd); + nl_close(dh->nlsock); + dect_free(dh, dh->nlfd); +} diff --git a/src/s_msg.c b/src/s_msg.c new file mode 100644 index 0000000..1eac658 --- /dev/null +++ b/src/s_msg.c @@ -0,0 +1,991 @@ +/* + * DECT S-Format messages + * + * Copyright (c) 2009 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <asm/byteorder.h> + +#include <libdect.h> +#include <identities.h> +#include <utils.h> +#include <s_fmt.h> +#include <lce.h> + +static struct dect_ie_common *dect_ie_alloc(const struct dect_handle *dh, + unsigned int size) +{ + struct dect_ie_common *ie; + + ie = dect_zalloc(dh, size); + if (ie == NULL) + return NULL; + __dect_ie_init(ie); + return ie; +} + +static int dect_sfmt_parse_repeat_indicator(const struct dect_handle *dh, + struct dect_ie_common **ie, + const struct dect_sfmt_ie *src) +{ + struct dect_ie_repeat_indicator *dst = dect_ie_container(dst, *ie); + + init_list_head(&dst->list); + dst->type = src->data[0] & DECT_SFMT_IE_FIXED_VAL_MASK; + switch (dst->type) { + case DECT_SFMT_IE_LIST_NORMAL: + case DECT_SFMT_IE_LIST_PRIORITIZED: + return 0; + default: + dect_debug("invalid list type\n"); + return -1; + } +} + +static int dect_sfmt_build_repeat_indicator(struct dect_sfmt_ie *dst, + const struct dect_ie_common *ie) +{ + struct dect_ie_repeat_indicator *src = dect_ie_container(src, ie); + + dect_debug("build repeat indicator list %p %p\n", src->list.prev, src->list.next); + dst->data[0] = src->type; + return 0; +} + +static int dect_sfmt_parse_empty_single_octet(const struct dect_handle *dh, + struct dect_ie_common **ie, + const struct dect_sfmt_ie *src) +{ + return 0; +} + +static int dect_sfmt_build_empty_single_octet(struct dect_sfmt_ie *dst, + const struct dect_ie_common *ie) +{ + dst->data[0] = 0; + return 0; +} + +static const char *call_classes[DECT_CALL_CLASS_MAX + 1] = { + [DECT_CALL_CLASS_MESSAGE] = "message call", + [DECT_CALL_CLASS_DECT_ISDN] = "DECT/ISDN IIP", + [DECT_CALL_CLASS_NORMAL] = "normal call", + [DECT_CALL_CLASS_INTERNAL] = "internal call", + [DECT_CALL_CLASS_EMERGENCY] = "emergency call", + [DECT_CALL_CLASS_SERVICE] = "service call", + [DECT_CALL_CLASS_EXTERNAL_HO] = "external handover call", + [DECT_CALL_CLASS_SUPPLEMENTARY_SERVICE] = "supplementary service call", + [DECT_CALL_CLASS_QA_M] = "QA&M call", +}; + +static const char *basic_services[DECT_SERVICE_MAX + 1] = { + [DECT_SERVICE_BASIC_SPEECH_DEFAULT] = "basic speech default attributes", + [DECT_SERVICE_DECT_GSM_IWP] = "DECT GSM IWP profile", + [DECT_SERVICE_UMTS_IWP] = "DECT UMTS IWP", + [DECT_SERVICE_LRMS] = "LRMS (E-profile) service", + [DECT_SERVICE_GSM_IWP_SMS] = "GSM IWP SMS", + [DECT_SERVICE_WIDEBAND_SPEECH] = "Wideband speech", + [DECT_SERVICE_OTHER] = "Other", +}; + +static void dect_sfmt_dump_basic_service(const struct dect_ie_common *_ie) +{ + const struct dect_ie_basic_service *ie = dect_ie_container(ie, _ie); + + dect_debug("basic service:\n\tcall class: %s\n\tservice: %s\n", + call_classes[ie->class], basic_services[ie->service]); +} + +static int dect_sfmt_parse_basic_service(const struct dect_handle *dh, + struct dect_ie_common **ie, + const struct dect_sfmt_ie *src) +{ + struct dect_ie_basic_service *dst = dect_ie_container(dst, *ie); + + dst->class = src->data[1] >> DECT_BASIC_SERVICE_CALL_CLASS_SHIFT; + dst->service = src->data[1] & DECT_BASIC_SERVICE_SERVICE_MASK; + dect_sfmt_dump_basic_service(*ie); + return 0; +} + +static int dect_sfmt_build_basic_service(struct dect_sfmt_ie *dst, + const struct dect_ie_common *ie) +{ + struct dect_ie_basic_service *src = dect_ie_container(src, ie); + + dst->data[1] = src->class << DECT_BASIC_SERVICE_CALL_CLASS_SHIFT; + dst->data[1] |= src->service; + return 0; +} + +static int dect_sfmt_parse_single_display(const struct dect_handle *dh, + struct dect_ie_common **ie, + const struct dect_sfmt_ie *src) +{ + struct dect_ie_display *dst = dect_ie_container(dst, *ie); + + dst->info[0] = src->data[1]; + dst->len = 1; + dect_debug("single display: '%c'\n", dst->info[0]); + return 0; +} + +static int dect_sfmt_build_single_display(struct dect_sfmt_ie *dst, + const struct dect_ie_common *src) +{ + struct dect_ie_display *ie = dect_ie_container(ie, src); + + dst->data[1] = ie->info[0]; + return 0; +} + +static int dect_sfmt_parse_single_keypad(const struct dect_handle *dh, + struct dect_ie_common **ie, + const struct dect_sfmt_ie *src) +{ + struct dect_ie_keypad *dst = dect_ie_container(dst, *ie); + + dst->info[0] = src->data[1]; + dst->len = 1; + dect_debug("single keypad: '%c'\n", dst->info[0]); + return 0; +} + +static int dect_sfmt_parse_release_reason(const struct dect_handle *dh, + struct dect_ie_common **ie, + const struct dect_sfmt_ie *src) +{ + struct dect_ie_release_reason *dst = dect_ie_container(dst, *ie); + + dst->reason = src->data[1]; + dect_debug("release reason: %x\n", dst->reason); + return 0; +} + +static int dect_sfmt_build_release_reason(struct dect_sfmt_ie *dst, + const struct dect_ie_common *ie) +{ + struct dect_ie_release_reason *src = dect_ie_container(src, ie); + + dst->data[1] = src->reason; + return 0; +} + +static int dect_sfmt_parse_signal(const struct dect_handle *dh, + struct dect_ie_common **ie, + const struct dect_sfmt_ie *src) +{ + struct dect_ie_signal *dst = dect_ie_container(dst, *ie); + + dst->code = src->data[1]; + return 0; +} + +static int dect_sfmt_build_signal(struct dect_sfmt_ie *dst, + const struct dect_ie_common *src) +{ + struct dect_ie_signal *ie = dect_ie_container(ie, src); + + dst->data[1] = ie->code; + return 0; +} + +static int dect_sfmt_parse_timer_restart(const struct dect_handle *dh, + struct dect_ie_common **ie, + const struct dect_sfmt_ie *src) +{ + struct dect_ie_timer_restart *dst = dect_ie_container(dst, *ie); + + dst->code = src->data[1]; + switch (dst->code) { + case DECT_TIMER_RESTART: + case DECT_TIMER_STOP: + return 0; + default: + return -1; + } +} + +static int dect_sfmt_parse_portable_identity(const struct dect_handle *dh, + struct dect_ie_common **ie, + const struct dect_sfmt_ie *src) +{ + struct dect_ie_portable_identity *dst = dect_ie_container(dst, *ie); + uint8_t len; + + if (src->len < S_VL_IE_PORTABLE_IDENTITY_MIN_SIZE) + return -1; + if (!(src->data[2] & 0x80)) + return -1; + + dst->type = src->data[2] & S_VL_IE_PORTABLE_IDENTITY_TYPE_MASK; + len = src->data[3] & S_VL_IE_PORTABLE_IDENTITY_LENGTH_MASK; + + switch (dst->type) { + case ID_TYPE_IPUI: + if (!dect_parse_ipui(&dst->ipui, src->data + 4, len)) + dect_debug("parsing failed\n"); + return 0; + case ID_TYPE_IPEI: + return 0; + case ID_TYPE_TPUI: + return 0; + default: + dect_debug("invalid type %u\n", dst->type); + return -1; + } +} + +static int dect_sfmt_build_portable_identity(struct dect_sfmt_ie *dst, + const struct dect_ie_common *src) +{ + const struct dect_ie_portable_identity *ie = dect_ie_container(ie, src); + uint8_t len; + + switch (ie->type) { + case ID_TYPE_IPUI: + len = dect_build_ipui(&dst->data[4], &ie->ipui); + if (len == 0) + return -1; + break; + case ID_TYPE_IPEI: + case ID_TYPE_TPUI: + return -1; + default: + return -1; + } + + dst->data[3] = 0x80 | len; + dst->data[2] = 0x80 | ie->type; + dst->len = 9; + return 0; +} + +static int dect_sfmt_parse_fixed_identity(const struct dect_handle *dh, + struct dect_ie_common **ie, + const struct dect_sfmt_ie *src) +{ + struct dect_ie_fixed_identity *dst = dect_ie_container(dst, *ie); + uint8_t len, ari_len; + uint64_t ari; + + if (src->len < S_VL_IE_FIXED_IDENTITY_MIN_SIZE) + return -1; + if (!(src->data[2] & 0x80)) + return -1; + + dst->type = src->data[2] & S_VL_IE_FIXED_IDENTITY_TYPE_MASK; + len = src->data[3] & S_VL_IE_FIXED_IDENTITY_LENGTH_MASK; + + ari = __be64_to_cpu(*(__be64 *)&src->data[4]); + ari_len = dect_parse_ari(&dst->ari, ari << 1); + if (ari_len == 0) + return -1; + + switch (dst->type) { + case ID_TYPE_ARI: + case ID_TYPE_PARK: + return ari_len + 1 == len; + case ID_TYPE_ARI_RPN: + case ID_TYPE_ARI_WRS: + return 0; + default: + dect_debug("invalid type %u\n", dst->type); + return -1; + } +} + +static int dect_sfmt_build_fixed_identity(struct dect_sfmt_ie *dst, + const struct dect_ie_common *ie) +{ + struct dect_ie_fixed_identity *src = dect_ie_container(src, ie); + uint64_t ari; + + ari = dect_build_ari(&src->ari) >> 1; + dst->data[8] = ari >> 24; + dst->data[7] = ari >> 32; + dst->data[6] = ari >> 40; + dst->data[5] = ari >> 48; + dst->data[4] = ari >> 56; + dst->data[3] = 0x80 | (DECT_ARC_A_LEN + 1); + dst->data[2] = 0x80 | src->type; + dst->len = 9; + return 0; +} + +static int dect_sfmt_parse_progress_indicator(const struct dect_handle *dh, + struct dect_ie_common **ie, + const struct dect_sfmt_ie *src) +{ + struct dect_ie_progress_indicator *dst = dect_ie_container(dst, *ie); + + dst->location = src->data[2] & DECT_SFMT_IE_PROGRESS_INDICATOR_LOCATION_MASK; + dst->progress = src->data[3]; + return 0; +} + +static int dect_sfmt_build_progress_indicator(struct dect_sfmt_ie *dst, + const struct dect_ie_common *ie) +{ + struct dect_ie_progress_indicator *src = dect_ie_container(src, ie); + + dst->data[3] = 0x80 | src->progress; + dst->data[2] = 0x80 | src->location; + dst->len = 4; + return 0; +} + +static int dect_sfmt_build_multi_display(struct dect_sfmt_ie *dst, + const struct dect_ie_common *ie) +{ + struct dect_ie_display *src = dect_ie_container(src, ie); + + memcpy(dst->data + 2, src->info, src->len); + dst->len = src->len + 2; + return 0; +} + +static int dect_sfmt_parse_multi_keypad(const struct dect_handle *dh, + struct dect_ie_common **ie, + const struct dect_sfmt_ie *src) +{ + struct dect_ie_keypad *dst = dect_ie_container(dst, *ie); + + dst->len = src->len - 2; + memcpy(dst->info, src->data + 2, src->len - 2); + dect_debug("multi-keypad: '%.*s'\n", dst->len, dst->info); + return 0; +} + +static int dect_sfmt_parse_reject_reason(const struct dect_handle *dh, + struct dect_ie_common **ie, + const struct dect_sfmt_ie *src) +{ + struct dect_ie_reject_reason *dst = dect_ie_container(dst, *ie); + + dst->reason = src->data[2]; + dect_debug("reject reason: %x\n", dst->reason); + return 0; +} + +static int dect_sfmt_parse_escape_to_proprietary(const struct dect_handle *dh, + struct dect_ie_common **ie, + const struct dect_sfmt_ie *src) +{ + struct dect_ie_escape_to_proprietary *dst = dect_ie_container(dst, *ie); + uint8_t dtype; + + dtype = (src->data[2] & DECT_ESC_TO_PROPRIETARY_IE_DESC_TYPE_MASK); + if (dtype != DECT_ESC_TO_PROPRIETARY_IE_DESC_EMC) + return -1; + dst->emc = __be16_to_cpu(*(__be16 *)&src->data[3]); + dect_debug("EMC %x\n", dst->emc); + return 0; +} + +static const struct dect_ie_handler { + const char *name; + size_t size; + int (*parse)(const struct dect_handle *dh, + struct dect_ie_common **dst, + const struct dect_sfmt_ie *ie); + int (*build)(struct dect_sfmt_ie *dst, + const struct dect_ie_common *ie); +} dect_ie_handlers[256] = { + [S_SO_IE_REPEAT_INDICATOR] = { + .name = "repeat indicator", + .parse = dect_sfmt_parse_repeat_indicator, + .build = dect_sfmt_build_repeat_indicator, + }, + [S_SE_IE_SENDING_COMPLETE] = { + .name = "sending complete", + .size = sizeof(struct dect_ie_sending_complete), + .parse = dect_sfmt_parse_empty_single_octet, + .build = dect_sfmt_build_empty_single_octet, + }, + [S_SE_IE_DELIMITER_REQUEST] = { + .name = "delimiter request", + .size = sizeof(struct dect_ie_delimiter_request), + .parse = dect_sfmt_parse_empty_single_octet, + .build = dect_sfmt_build_empty_single_octet, + }, + [S_SE_IE_USE_TPUI] = { + .name = "use TPUI", + .size = sizeof(struct dect_ie_use_tpui), + .parse = dect_sfmt_parse_empty_single_octet, + .build = dect_sfmt_build_empty_single_octet, + }, + [S_DO_IE_BASIC_SERVICE] = { + .name = "basic service", + .size = sizeof(struct dect_ie_basic_service), + .parse = dect_sfmt_parse_basic_service, + .build = dect_sfmt_build_basic_service, + }, + [S_DO_IE_RELEASE_REASON] = { + .name = "release reason", + .size = sizeof(struct dect_ie_release_reason), + .parse = dect_sfmt_parse_release_reason, + .build = dect_sfmt_build_release_reason, + }, + [S_DO_IE_SIGNAL] = { + .name = "signal", + .size = sizeof(struct dect_ie_signal), + .parse = dect_sfmt_parse_signal, + .build = dect_sfmt_build_signal, + }, + [S_DO_IE_TIMER_RESTART] = { + .name = "timer restart", + .size = sizeof(struct dect_ie_timer_restart), + .parse = dect_sfmt_parse_timer_restart, + }, + [S_DO_IE_TEST_HOOK_CONTROL] = { + .name = "test hook control", + }, + [S_DO_IE_SINGLE_DISPLAY] = { + .name = "single display", + .size = sizeof(struct dect_ie_display), + .parse = dect_sfmt_parse_single_display, + .build = dect_sfmt_build_single_display, + }, + [S_DO_IE_SINGLE_KEYPAD] = { + .name = "single keypad", + .size = sizeof(struct dect_ie_keypad), + .parse = dect_sfmt_parse_single_keypad, + }, + [S_VL_IE_INFO_TYPE] = { + .name = "info type", + .size = sizeof(struct dect_ie_info_type), + }, + [S_VL_IE_IDENTITY_TYPE] = { + .name = "identity type", + .size = sizeof(struct dect_ie_identity_type) + }, + [S_VL_IE_PORTABLE_IDENTITY] = { + .name = "portable identity", + .size = sizeof(struct dect_ie_portable_identity), + .parse = dect_sfmt_parse_portable_identity, + .build = dect_sfmt_build_portable_identity, + }, + [S_VL_IE_FIXED_IDENTITY] = { + .name = "fixed identity", + .size = sizeof(struct dect_ie_fixed_identity), + .parse = dect_sfmt_parse_fixed_identity, + .build = dect_sfmt_build_fixed_identity, + }, + [S_VL_IE_LOCATION_AREA] = { + .name = "location area", + .size = sizeof(struct dect_ie_location_area), + }, + [S_VL_IE_NWK_ASSIGNED_IDENTITY] = { + .name = "NWK assigned identity", + .size = sizeof(struct dect_ie_nwk_assigned_identity), + }, + [S_VL_IE_AUTH_TYPE] = { + .name = "auth type", + .size = sizeof(struct dect_ie_auth_type), + }, + [S_VL_IE_ALLOCATION_TYPE] = { + .name = "allocation type", + .size = sizeof(struct dect_ie_allocation_type), + }, + [S_VL_IE_RAND] = { + .name = "RAND", + .size = sizeof(struct dect_ie_rand), + }, + [S_VL_IE_RES] = { + .name = "RES", + .size = sizeof(struct dect_ie_res), + }, + [S_VL_IE_RS] = { + .name = "RS", + .size = sizeof(struct dect_ie_rs), + }, + [S_VL_IE_IWU_ATTRIBUTES] = { + .name = "IWU attributes", + .size = sizeof(struct dect_ie_iwu_attributes), + }, + [S_VL_IE_CALL_ATTRIBUTES] = { + .name = "call attributes", + .size = sizeof(struct dect_ie_call_attributes), + }, + [S_VL_IE_SERVICE_CHANGE_INFO] = { + .name = "service change info", + .size = sizeof(struct dect_ie_service_change_info), + }, + [S_VL_IE_CONNECTION_ATTRIBUTES] = { + .name = "connection attributes", + .size = sizeof(struct dect_ie_connection_attributes), + }, + [S_VL_IE_CIPHER_INFO] = { + .name = "cipher info", + .size = sizeof(struct dect_ie_cipher_info), + }, + [S_VL_IE_CALL_IDENTITY] = { + .name = "call identity", + .size = sizeof(struct dect_ie_call_identity), + }, + [S_VL_IE_CONNECTION_IDENTITY] = { + .name = "connection identity", + .size = sizeof(struct dect_ie_connection_identity), + }, + [S_VL_IE_FACILITY] = { + .name = "facility", + .size = sizeof(struct dect_ie_facility), + }, + [S_VL_IE_PROGRESS_INDICATOR] = { + .name = "progress indicator", + .size = sizeof(struct dect_ie_progress_indicator), + .parse = dect_sfmt_parse_progress_indicator, + .build = dect_sfmt_build_progress_indicator, + }, + [S_VL_IE_MMS_GENERIC_HEADER] = { + .name = "MMS generic header", + .size = sizeof(struct dect_ie_mms_generic_header), + }, + [S_VL_IE_MMS_OBJECT_HEADER] = { + .name = "MMS object header", + .size = sizeof(struct dect_ie_mms_object_header), + }, + [S_VL_IE_MMS_EXTENDED_HEADER] = { + .name = "MMS extended header", + .size = sizeof(struct dect_ie_mms_extended_header), + }, + [S_VL_IE_TIME_DATE] = { + .name = "time-date", + .size = sizeof(struct dect_ie_time_date), + }, + [S_VL_IE_MULTI_DISPLAY] = { + .name = "multi display", + .size = sizeof(struct dect_ie_display), + .build = dect_sfmt_build_multi_display, + }, + [S_VL_IE_MULTI_KEYPAD] = { + .name = "multi keypad", + .size = sizeof(struct dect_ie_keypad), + .parse = dect_sfmt_parse_multi_keypad, + }, + [S_VL_IE_FEATURE_ACTIVATE] = { + .name = "feature activate", + .size = sizeof(struct dect_ie_feature_activate), + }, + [S_VL_IE_FEATURE_INDICATE] = { + .name = "feature indicate", + .size = sizeof(struct dect_ie_feature_indicate), + }, + [S_VL_IE_NETWORK_PARAMETER] = { + .name = "network parameter", + .size = sizeof(struct dect_ie_network_parameter), + }, + [S_VL_IE_EXT_HO_INDICATOR] = { + .name = "ext H/O indicator", + .size = sizeof(struct dect_ie_ext_ho_indicator), + }, + [S_VL_IE_ZAP_FIELD] = { + .name = "ZAP field", + .size = sizeof(struct dect_ie_zap_field), + }, + [S_VL_IE_SERVICE_CLASS] = { + .name = "service class", + .size = sizeof(struct dect_ie_service_class), + }, + [S_VL_IE_KEY] = { + .name = "key", + .size = sizeof(struct dect_ie_key), + }, + [S_VL_IE_REJECT_REASON] = { + .name = "reject reason", + .size = sizeof(struct dect_ie_reject_reason), + .parse = dect_sfmt_parse_reject_reason, + }, + [S_VL_IE_SETUP_CAPABILITY] = { + .name = "setup capability", + .size = sizeof(struct dect_ie_setup_capability), + }, + [S_VL_IE_TERMINAL_CAPABILITY] = { + .name = "terminal capability", + .size = sizeof(struct dect_ie_terminal_capability), + }, + [S_VL_IE_END_TO_END_COMPATIBILITY] = { + .name = "end-to-end compatibility", + .size = sizeof(struct dect_ie_end_to_end_compatibility), + }, + [S_VL_IE_RATE_PARAMETERS] = { + .name = "rate parameters", + .size = sizeof(struct dect_ie_rate_parameters), + }, + [S_VL_IE_TRANSIT_DELAY] = { + .name = "transit delay", + .size = sizeof(struct dect_ie_transit_delay), + }, + [S_VL_IE_WINDOW_SIZE] = { + .name = "window size", + .size = sizeof(struct dect_ie_window_size), + }, + [S_VL_IE_CALLING_PARTY_NUMBER] = { + .name = "calling party number", + .size = sizeof(struct dect_ie_calling_party_number), + }, + [S_VL_IE_CALLING_PARTY_NAME] = { + .name = "calling party name", + .size = sizeof(struct dect_ie_calling_party_name), + }, + [S_VL_IE_CALLED_PARTY_NUMBER] = { + .name = "called party number", + .size = sizeof(struct dect_ie_called_party_number), + }, + [S_VL_IE_CALLED_PARTY_SUBADDR] = { + .name = "called party subaddress", + .size = sizeof(struct dect_ie_called_party_subaddress), + }, + [S_VL_IE_DURATION] = { + .name = "duration", + .size = sizeof(struct dect_ie_duration), + }, + [S_VL_IE_SEGMENTED_INFO] = { + .name = "segmented info", + .size = sizeof(struct dect_ie_segmented_info), + }, + [S_VL_IE_ALPHANUMERIC] = { + .name = "alphanumeric", + .size = sizeof(struct dect_ie_alphanumeric), + }, + [S_VL_IE_IWU_TO_IWU] = { + .name = "IWU-to-IWU", + .size = sizeof(struct dect_ie_iwu_to_iwu), + }, + [S_VL_IE_MODEL_IDENTIFIER] = { + .name = "model identifier", + .size = sizeof(struct dect_ie_model_identifier), + }, + [S_VL_IE_IWU_PACKET] = { + .name = "IWU-packet", + .size = sizeof(struct dect_ie_iwu_packet), + }, + [S_VL_IE_ESCAPE_TO_PROPRIETARY] = { + .name = "escape to proprietary", + .size = sizeof(struct dect_ie_escape_to_proprietary), + .parse = dect_sfmt_parse_escape_to_proprietary, + }, + [S_VL_IE_CODEC_LIST] = { + .name = "codec list", + .size = sizeof(struct dect_ie_codec_list), + }, + [S_VL_IE_EVENTS_NOTIFICATION] = { + .name = "events notification", + .size = sizeof(struct dect_ie_events_notification), + }, + [S_VL_IE_CALL_INFORMATION] = { + .name = "call information", + .size = sizeof(struct dect_ie_call_information), + }, + [S_VL_IE_ESCAPE_FOR_EXTENSION] = { + .name = "escape for extension", + }, +}; + +static struct dect_ie_common ** +dect_next_ie(const struct dect_sfmt_ie_desc *desc, struct dect_ie_common **ie) +{ + if (desc->type == S_SO_IE_REPEAT_INDICATOR) + return ((void *)ie) + sizeof(struct dect_ie_repeat_indicator); + else if (!(desc->flags & DECT_SFMT_IE_REPEAT)) + return ie + 1; + else + return ie; +} + +static void dect_msg_ie_init(const struct dect_sfmt_ie_desc *desc, + struct dect_ie_common **ie) +{ + struct dect_ie_repeat_indicator *rep; + + if (desc->flags & DECT_SFMT_IE_END) + return; + + //dect_debug("init message IE %p: <%s>\n", + // ie, dect_ie_handlers[desc->type].name); + + if (desc->type == S_SO_IE_REPEAT_INDICATOR) { + rep = dect_ie_container(rep, (struct dect_ie_common *)ie); + init_list_head(&rep->list); + } else if (!(desc->flags & DECT_SFMT_IE_REPEAT)) + *ie = NULL; +} + +static int dect_parse_sfmt_ie_header(struct dect_sfmt_ie *ie, + const struct dect_msg_buf *mb) +{ + uint8_t val; + + if (mb->len < 1) + return -1; + + ie->id = mb->data[0] & DECT_SFMT_IE_FIXED_LEN; + if (ie->id & DECT_SFMT_IE_FIXED_LEN) { + ie->id |= (mb->data[0] & DECT_SFMT_IE_FIXED_ID_MASK); + val = (mb->data[0] & DECT_SFMT_IE_FIXED_VAL_MASK); + if (ie->id != S_SO_IE_DOUBLE_OCTET_ELEMENT) { + ie->len = 1; + if (ie->id == S_SO_IE_EXT_PREFIX) + ie->id |= val; + } else { + if (mb->len < 2) + return -1; + ie->id |= val; + ie->len = 2; + } + } else { + if (mb->len < 2U || mb->len < 2U + mb->data[1]) + return -1; + ie->id = mb->data[0]; + ie->len = mb->data[1] + 2; + } + ie->data = mb->data; + + dect_debug("found IE: <%s> (%x) len: %u\n", dect_ie_handlers[ie->id].name, + ie->id, ie->len); + return 0; +} + +static int dect_build_sfmt_ie_header(struct dect_sfmt_ie *dst, uint8_t id) +{ + if (id & DECT_SFMT_IE_FIXED_LEN) { + dst->data[0] |= id; + if ((id & DECT_SFMT_IE_FIXED_ID_MASK) != + (S_SO_IE_DOUBLE_OCTET_ELEMENT & DECT_SFMT_IE_FIXED_ID_MASK)) + dst->len = 1; + else + dst->len = 2; + } else { + if (dst->len == 2) + dst->len = 0; + else { + assert(dst->len > 2); + dst->data[1] = dst->len - 2; + dst->data[0] = id; + } + } + return 0; +} + +static int dect_parse_sfmt_ie(const struct dect_handle *dh, + const struct dect_sfmt_ie_desc *desc, + struct dect_ie_common **dst, + struct dect_sfmt_ie *ie) +{ + const struct dect_ie_handler *ieh; + int err = -1; + + ieh = &dect_ie_handlers[ie->id]; + if (ieh->parse == NULL) + goto err1; + + if (ieh->size > 0) { + *dst = dect_ie_alloc(dh, ieh->size); + if (*dst == NULL) + goto err1; + } + + dect_debug("parse: IE <%s> dst %p len %u\n", ieh->name, *dst, ie->len); + err = ieh->parse(dh, dst, ie); + if (err < 0) + goto err2; + return 0; + +err2: + dect_free(dh, *dst); + *dst = NULL; +err1: + dect_debug("smsg: IE parsing error\n"); + return err; +} + +enum dect_sfmt_error dect_parse_sfmt_msg(const struct dect_handle *dh, + const struct dect_sfmt_ie_desc *desc, + struct dect_msg_common *_dst, + struct dect_msg_buf *mb) +{ + struct dect_ie_common **dst = &_dst->ie[0]; + struct dect_sfmt_ie _ie[2], *ie; + uint8_t idx = 0; + + dect_msg_ie_init(desc, dst); + while (mb->len > 0) { + /* Parse the next information element header */ + ie = &_ie[idx++ % array_size(_ie)];; + if (dect_parse_sfmt_ie_header(ie, mb) < 0) + return -1; + + /* Treat empty variable length IEs as absent */ + if (!(ie->id & DECT_SFMT_IE_FIXED_LEN) && ie->len == 2) + goto next; + + /* Locate a matching member in the description and apply + * policy checks. */ + while (1) { + if (desc->flags & DECT_SFMT_IE_END) + goto out; + + switch (desc->f_p) { + case DECT_SFMT_IE_MANDATORY: + if (desc->type == ie->id) + goto found; + return DECT_SFMT_MANDATORY_IE_MISSING; + case DECT_SFMT_IE_NONE: + if (desc->type == ie->id) + return -1; + break; + case DECT_SFMT_IE_OPTIONAL: + if (desc->type == ie->id) + goto found; + if (desc->type == S_DO_IE_SINGLE_DISPLAY && + ie->id == S_VL_IE_MULTI_DISPLAY) + goto found; + if (desc->type == S_DO_IE_SINGLE_KEYPAD && + ie->id == S_VL_IE_MULTI_KEYPAD) + goto found; + break; + } + + dst = dect_next_ie(desc, dst); + desc++; + dect_msg_ie_init(desc, dst); + } +found: + /* Ignore corrupt optional IEs */ + if (dect_parse_sfmt_ie(dh, desc, dst, ie) < 0 && + desc->f_p == DECT_SFMT_IE_MANDATORY) + return DECT_SFMT_MANDATORY_IE_ERROR; + +next: + dect_mbuf_pull(mb, ie->len); + + dst = dect_next_ie(desc, dst); + desc++; + dect_msg_ie_init(desc, dst); + } +out: + while (!(desc->flags & DECT_SFMT_IE_END)) { + dect_debug("clear missing IE: <%s>\n", dect_ie_handlers[desc->type].name); + if (desc->f_p == DECT_SFMT_IE_MANDATORY) + return DECT_SFMT_MANDATORY_IE_MISSING; + dst = dect_next_ie(desc, dst); + desc++; + dect_msg_ie_init(desc, dst); + } + + return DECT_SFMT_OK; +} + +static enum dect_sfmt_error +dect_build_sfmt_ie(const struct dect_handle *dh, + const struct dect_sfmt_ie_desc *desc, + struct dect_msg_buf *mb, + struct dect_ie_common *ie) +{ + const struct dect_ie_handler *ieh; + uint16_t type = desc->type; + struct dect_sfmt_ie dst; + enum dect_sfmt_error err = 0; + + if (desc->p_f == DECT_SFMT_IE_NONE) + return DECT_SFMT_INVALID_IE; + + if (type == S_DO_IE_SINGLE_DISPLAY) { + struct dect_ie_display *display = dect_ie_container(display, ie); + if (display->len > 1) + type = S_VL_IE_MULTI_DISPLAY; + } + if (type == S_DO_IE_SINGLE_KEYPAD) { + struct dect_ie_keypad *keypad = dect_ie_container(keypad, ie); + if (keypad->len > 1) + type = S_VL_IE_MULTI_KEYPAD; + } + + ieh = &dect_ie_handlers[type]; + if (ieh->build == NULL) + goto err1; + + dect_debug("build IE: %s %p\n", ieh->name, ie); + dst.data = mb->data + mb->len; + dst.len = 0; + err = ieh->build(&dst, ie); + if (err < 0) + goto err1; + + dect_build_sfmt_ie_header(&dst, type); + mb->len += dst.len; + return 0; + +err1: + return err; +} + +enum dect_sfmt_error dect_build_sfmt_msg(const struct dect_handle *dh, + const struct dect_sfmt_ie_desc *desc, + const struct dect_msg_common *_src, + struct dect_msg_buf *mb) +{ + struct dect_ie_common * const *src = &_src->ie[0], **next, *rsrc; + struct dect_ie_repeat_indicator *rep; + enum dect_sfmt_error err; + + while (!(desc->flags & DECT_SFMT_IE_END)) { + next = dect_next_ie(desc, (struct dect_ie_common **)src); + + if (desc->type == S_SO_IE_REPEAT_INDICATOR) { + rep = (struct dect_ie_repeat_indicator *)src; + if (rep->list.next == NULL || list_empty(&rep->list)) { + desc++; + goto next; + } + + if (rep->list.next->next != &rep->list) + err = dect_build_sfmt_ie(dh, desc, mb, &rep->common); + desc++; + + assert(desc->flags & DECT_SFMT_IE_REPEAT); + assert(!list_empty(&rep->list)); + list_for_each_entry(rsrc, &rep->list, list) { + dect_debug("list elem %p next %p\n", rsrc, rsrc->list.next); + err = dect_build_sfmt_ie(dh, desc, mb, rsrc); + } + } else { + if (*src == NULL) + goto next; + err = dect_build_sfmt_ie(dh, desc, mb, *src); + } +next: + src = next; + desc++; + } + + return DECT_SFMT_OK; +} + +void dect_msg_free(const struct dect_handle *dh, + const struct dect_sfmt_ie_desc *desc, + struct dect_msg_common *msg) +{ + struct dect_ie_common **ie = &msg->ie[0], **next; + + while (!(desc->flags & DECT_SFMT_IE_END)) { + next = dect_next_ie(desc, ie); + + //dect_debug("free %s %p\n", dect_ie_handlers[desc->type].name, ie); + if (desc->type == S_SO_IE_REPEAT_INDICATOR) + desc++; + else if (*ie != NULL && --(*ie)->refcnt == 0) + dect_free(dh, *ie); + + ie = next; + desc++; + } +} diff --git a/src/ss.c b/src/ss.c new file mode 100644 index 0000000..3fcaee0 --- /dev/null +++ b/src/ss.c @@ -0,0 +1,49 @@ +/* + * DECT Supplementary Services (SS) + * + * Copyright (c) 2009 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <linux/dect.h> + +#include <libdect.h> +#include <utils.h> +#include <s_fmt.h> +#include <lce.h> +#include <ss.h> + + +static const struct dect_sfmt_ie_desc ciss_facility_msg_desc[] = { + DECT_SFMT_IE(S_VL_IE_FACILITY, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_DO_IE_SINGLE_DISPLAY, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_DO_IE_SINGLE_KEYPAD, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_FEATURE_ACTIVATE, IE_NONE, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_FEATURE_INDICATE, IE_OPTIONAL, IE_NONE, 0), + DECT_SFMT_IE(S_VL_IE_IWU_TO_IWU, IE_OPTIONAL, IE_OPTIONAL, DECT_SFMT_IE_REPEAT), + DECT_SFMT_IE(S_VL_IE_ESCAPE_TO_PROPRIETARY, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_TIME_DATE, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_EVENTS_NOTIFICATION, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE(S_VL_IE_CALL_INFORMATION, IE_OPTIONAL, IE_OPTIONAL, 0), + DECT_SFMT_IE_END_MSG +}; + +void dect_clss_rcv(struct dect_handle *dh, struct dect_msg_buf *mb) +{ + struct dect_ciss_facility_msg msg; + + if (mb->type != CISS_FACILITY) + return; + + if (dect_parse_sfmt_msg(dh, ciss_facility_msg_desc, &msg.common, mb) < 0) + return; +} diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..e318fcb --- /dev/null +++ b/src/utils.c @@ -0,0 +1,142 @@ +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <libdect.h> +#include <utils.h> + +#ifndef SOCK_NONBLOCK +#define SOCK_NONBLOCK O_NONBLOCK +#endif + +void dect_hexdump(const char *prefix, const uint8_t *buf, size_t size) +{ + unsigned int i; + + for (i = 0; i < size; i++) { + if (i % 16 == 0) + dect_debug("%s%s: ", i ? "\n" : "", prefix); + dect_debug("%.2x ", buf[i]); + } + dect_debug("\n\n"); +} + +void *dect_malloc(const struct dect_handle *dh, size_t size) +{ + return dh->ops->malloc(size); +} + +void *dect_zalloc(const struct dect_handle *dh, size_t size) +{ + void *ptr; + + ptr = dect_malloc(dh, size); + if (ptr != NULL) + memset(ptr, 0, size); + return ptr; +} + +void dect_free(const struct dect_handle *dh, void *ptr) +{ + dh->ops->free(ptr); +} + +struct dect_timer *dect_alloc_timer(const struct dect_handle *dh) +{ + return dect_malloc(dh, sizeof(struct dect_timer) + + dh->ops->event_ops->timer_priv_size); +} + +void dect_start_timer(const struct dect_handle *dh, + struct dect_timer *timer, unsigned int timeout) +{ + struct timeval tv = { + .tv_sec = timeout, + }; + + dh->ops->event_ops->start_timer(dh, timer, &tv); +} + +void dect_stop_timer(const struct dect_handle *dh, struct dect_timer *timer) +{ + dh->ops->event_ops->stop_timer(dh, timer); +} + +struct dect_fd *dect_alloc_fd(const struct dect_handle *dh) +{ + struct dect_fd *dfd; + + dfd = dect_malloc(dh, sizeof(struct dect_fd) + + dh->ops->event_ops->fd_priv_size); + if (dfd == NULL) + return NULL; + dfd->fd = -1; + return dfd; +} + +int dect_register_fd(const struct dect_handle *dh, struct dect_fd *dfd, + uint32_t events) +{ + return dh->ops->event_ops->register_fd(dh, dfd, events); +} + +void dect_unregister_fd(const struct dect_handle *dh, struct dect_fd *dfd) +{ + dh->ops->event_ops->unregister_fd(dh, dfd); +} + +void dect_close(const struct dect_handle *dh, struct dect_fd *dfd) +{ + if (dfd->fd >= 0) + close(dfd->fd); + dect_free(dh, dfd); +} + +struct dect_fd *dect_socket(const struct dect_handle *dh, int type, int protocol) +{ + struct dect_fd *dfd; + + dfd = dect_alloc_fd(dh); + if (dfd == NULL) + goto err1; + + dfd->fd = socket(AF_DECT, type | SOCK_NONBLOCK, protocol); + if (dfd->fd < 0) + goto err2; + + return dfd; + +err2: + dect_close(dh, dfd); +err1: + return NULL; +} + +struct dect_fd *dect_accept(const struct dect_handle *dh, + const struct dect_fd *dfd, + struct sockaddr *addr, socklen_t len) +{ + struct dect_fd *nfd; + + nfd = dect_alloc_fd(dh); + if (nfd == NULL) + goto err1; + + nfd->fd = accept(dfd->fd, addr, &len); + if (nfd->fd < 0) + goto err2; + if (fcntl(nfd->fd, F_SETFL, O_NONBLOCK) < 0) + goto err2; + + return nfd; + +err2: + dect_close(dh, nfd); +err1: + return NULL; +} |