dect
/
libdect
Archived
13
0
Fork 0
This repository has been archived on 2022-02-17. You can view files and clone it, but cannot push or open issues or pull requests.
libdect/src/s_msg.c

992 lines
26 KiB
C

/*
* 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++;
}
}