Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: private/rci_binary_output.h
- Revision:
- 0:1c358ea10753
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/private/rci_binary_output.h Tue Dec 03 13:34:02 2013 +0000
@@ -0,0 +1,677 @@
+/*
+ * Copyright (c) 2013 Digi International Inc.,
+ * All rights not expressly granted are reserved.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
+ * =======================================================================
+ */
+static void rci_set_output_error(rci_t * const rci, unsigned int const id, char const * const hint, rci_output_state_t state)
+{
+ rci_global_error(rci, id, hint);
+ set_rci_output_state(rci, state);
+ state_call(rci, rci_parser_state_output);
+}
+
+static connector_bool_t rci_output_data(rci_t * const rci, rci_buffer_t * const output, uint8_t const * const data, size_t const bytes)
+{
+ connector_bool_t const overflow = connector_bool(rci_buffer_remaining(output) < bytes);
+
+ if (overflow)
+ {
+ rci->status = rci_status_flush_output;
+ }
+ else
+ {
+ memcpy(rci_buffer_position(output), data, bytes);
+ rci_buffer_advance(output, bytes);
+ }
+
+ return overflow;
+}
+
+static size_t get_bytes_followed(uint32_t value)
+{
+ /* Bytes needed for the value:
+ * 1 byte is 0 to 0x7F
+ * 2 bytes is 0 to 0x1FFF
+ * 3 bytes is 0 to 0xFFFF
+ * 5 bytes is 0 to 0xFFFFFFFF
+ *
+ * Get additional bytes needed according to the value.
+ */
+ size_t bytes = 0;
+ if (value <= UINT32_C(0x7F))
+ {
+ bytes = 0;
+ }
+ else if (value < UINT32_C(0x2000))
+ {
+ bytes = 1;
+ }
+ else if (value < UINT32_C(0x10000))
+ {
+ bytes = 2;
+ }
+ else
+ {
+ bytes = 4;
+ }
+
+ return bytes;
+}
+
+static connector_bool_t rci_output_uint32(rci_t * const rci, uint32_t const value)
+{
+ connector_bool_t overflow;
+ size_t const bytes_follow = get_bytes_followed(value);
+
+ rci_buffer_t * const output = &rci->buffer.output;
+ size_t const total_bytes = bytes_follow + 1;
+
+ overflow = connector_bool(rci_buffer_remaining(output) < total_bytes);
+ if (overflow)
+ {
+ rci->status = rci_status_flush_output;
+ }
+ else
+ {
+ uint8_t * const rci_ber = rci_buffer_position(output);
+
+ /*
+ * opcode
+ * 7 6 5 4 3 2 1 0 bit
+ * ---------------
+ * 0 X X X X X X X (0 : 0x7F)
+ * 1 0 0 X X X X X + 1 byte followed (0: 0x1FFF)
+ * 1 0 1 - - - 0 0 + 2 bytes followed (0: 0xFFFF)
+ * 1 0 1 - - - 0 1 + 4 bytes followed (0: 0xFFFFFFFF)
+ * 1 0 1 - - - 1 0 + 8 bytes followed (0: 0xFFFFFFFFFFFFFFFF)
+ * 1 1 0 - - - - - Reserved
+ * 1 1 1 0 0 0 0 0 NONUM (No Value)
+ * 1 1 1 0 0 0 0 1 TRM (Terminator)
+ */
+ switch (total_bytes)
+ {
+ case record_bytes(rci_ber):
+ {
+ /* one byte with range [0, 0x7F] */
+ uint8_t const data = (uint8_t)value;
+ message_store_u8(rci_ber, value, data);
+ break;
+ }
+ case record_bytes(rci_ber_u8):
+ {
+ #define MAX_ONE_BYTE_FOLLOW_VALUE UINT32_C(0x1FFF)
+
+ /* two bytes with range [0, 0x1FFF] */
+ uint8_t * const rci_ber_u8 = rci_ber;
+ uint8_t data;
+ uint16_t data_value = (uint16_t)value;
+
+ ASSERT(value <= MAX_ONE_BYTE_FOLLOW_VALUE);
+
+ data = BINARY_RCI_SIZE_ALTERNATE_FLAG;
+ data |= HIGH8(data_value);
+ message_store_u8(rci_ber_u8, opcode, data);
+
+ data = LOW8(data_value);
+ message_store_u8(rci_ber_u8, value, data);
+
+ #undef MAX_ONE_BYTE_FOLLOW_VALUE
+ break;
+ }
+ case record_bytes(rci_ber_u16):
+ {
+ /* 3 bytes with range [0, 0xFFFF] */
+ uint8_t * const rci_ber_u16 = rci_ber;
+ uint8_t const opcode = BINARY_RCI_SET_MULTI_FOLLOW_BYTES(binary_rci_two_follow_byte);
+ uint16_t const data = (uint16_t)value;
+
+ message_store_u8(rci_ber_u16, opcode, opcode);
+ message_store_be16(rci_ber_u16, value, data);
+ break;
+ }
+ case record_bytes(rci_ber_u32):
+ {
+ /* 5 bytes with range [0, 0xFFFFFFFF */
+ uint8_t * const rci_ber_u32 = rci_ber;
+ uint8_t const opcode = BINARY_RCI_SET_MULTI_FOLLOW_BYTES(binary_rci_four_follow_byte);
+
+ message_store_u8(rci_ber_u32, opcode, opcode);
+ message_store_be32(rci_ber_u32, value, value);
+
+ break;
+
+ }
+ }
+ rci_buffer_advance(output, total_bytes);
+ }
+ return overflow;
+}
+
+static connector_bool_t rci_output_string(rci_t * const rci, char const * const string, size_t const length)
+{
+
+ rci_buffer_t * const output = &rci->buffer.output;
+ connector_bool_t overflow = connector_true;
+
+ /* output: | length | string | */
+ if (!rcistr_valid(&rci->output.content))
+ {
+ /* set up the data and its length */
+ overflow = rci_output_uint32(rci, length);
+ if (overflow) goto done;
+ rci->output.content.data = (uint8_t *)string;
+ rci->output.content.length = length;
+ }
+
+ if (rci->output.content.length > 0)
+ {
+ size_t const avail_bytes = rci_buffer_remaining(output);
+ size_t const write_bytes = (rci->output.content.length < avail_bytes) ? rci->output.content.length : avail_bytes;
+
+ overflow = rci_output_data(rci, output, (uint8_t *)rci->output.content.data, write_bytes);
+ if (overflow) goto done;
+
+ rci->output.content.data += write_bytes;
+ rci->output.content.length -= write_bytes;
+
+ }
+
+ if (rci->output.content.length > 0)
+ {
+ overflow = connector_true;
+ rci->status = rci_status_flush_output;
+ }
+ else
+ {
+ clear_rcistr(&rci->output.content);
+ }
+
+done:
+ return overflow;
+}
+
+#if defined RCI_PARSER_USES_IPV4
+static connector_bool_t rci_output_ipv4(rci_t * const rci, char const * const string)
+{
+ rci_buffer_t * const output = &rci->buffer.output;
+ connector_bool_t overflow = connector_true;
+ size_t const avail_bytes = rci_buffer_remaining(output);
+
+ if (avail_bytes < sizeof(uint32_t))
+ {
+ goto done;
+ }
+ else
+ {
+ uint32_t ipv4 = 0;
+ uint8_t * const rci_ber_u32 = rci_buffer_position(output);
+ int dot_count = 0;
+ size_t i;
+ size_t length = strlen(string);
+ char aux_string[4] = {'\0', '\0', '\0', '\0'}; /* Three chars plus terminator. */
+
+ size_t index = 0;
+
+ for (i = 0; i < length; i++)
+ {
+ if (index > sizeof(aux_string) - 1)
+ break;
+
+ if (string[i] != '.')
+ {
+ aux_string[index++] = string[i];
+ }
+
+ if (string[i] == '.' || i == (length -1))
+ {
+ long int val;
+ char * endptr;
+
+ val = strtol(aux_string, &endptr, 10);
+ if (endptr == NULL || *endptr != '\0' || val < 0 || val > 255)
+ {
+ break;
+ }
+
+ ipv4 = (ipv4 << 8) | val;
+ dot_count++;
+ index = 0;
+ memset(aux_string, '\0', sizeof aux_string);
+ }
+ }
+ if (dot_count != 4)
+ {
+ connector_request_id_t request_id;
+ request_id.remote_config_request = connector_request_id_remote_config_group_process;
+ notify_error_status(rci->service_data->connector_ptr->callback, connector_class_id_remote_config, request_id, connector_invalid_data_range);
+ rci->status = rci_status_error;
+ overflow = connector_false;
+ connector_debug_printf("Invalid IPv4 \"%s\"\n", string);
+ goto done;
+ }
+
+ message_store_u8(rci_ber_u32, opcode, sizeof ipv4);
+ message_store_be32(rci_ber_u32, value, ipv4);
+
+ rci_buffer_advance(output, record_bytes(rci_ber_u32));
+
+ overflow = connector_false;
+ }
+
+done:
+ return overflow;
+}
+#endif
+
+static connector_bool_t rci_output_uint8(rci_t * const rci, uint8_t const value)
+{
+ uint8_t const data = value;
+ rci_buffer_t * const output = &rci->buffer.output;
+
+ return rci_output_data(rci, output, &data, sizeof data);
+}
+
+#if defined RCI_PARSER_USES_FLOAT
+static connector_bool_t rci_output_float(rci_t * const rci, float const value)
+{
+ float const float_value = value;
+ uint32_t float_integer;
+
+ ASSERT(sizeof value == sizeof float_integer);
+
+ memcpy(&float_integer, &float_value, sizeof float_integer);
+
+ return rci_output_uint32(rci, float_integer);
+}
+#endif
+
+#define rci_output_no_value(rci) rci_output_uint8((rci), BINARY_RCI_NO_VALUE)
+#define rci_output_terminator(rci) rci_output_uint8((rci), BINARY_RCI_TERMINATOR)
+
+static void rci_output_command_id(rci_t * const rci)
+{
+ connector_remote_config_t const * const remote_config = &rci->shared.callback_data;
+ uint32_t command_id = 0;
+
+ switch (rci->shared.callback_data.group.type)
+ {
+ case connector_remote_group_setting:
+ switch (rci->shared.callback_data.action)
+ {
+ case connector_remote_action_set:
+ command_id = rci_command_set_setting;
+ break;
+ case connector_remote_action_query:
+ command_id = rci_command_query_setting;
+ break;
+ }
+ break;
+ case connector_remote_group_state:
+ switch (rci->shared.callback_data.action)
+ {
+ case connector_remote_action_set:
+ command_id = rci_command_set_state;
+ break;
+ case connector_remote_action_query:
+ command_id = rci_command_query_state;
+ break;
+ }
+ break;
+ }
+
+ {
+ connector_bool_t const overflow = rci_output_uint32(rci, command_id);
+
+ if (!overflow)
+ {
+ if (remote_config->error_id != connector_success)
+ state_call(rci, rci_parser_state_error);
+ else
+ state_call(rci, rci_parser_state_traverse);
+ }
+ }
+
+ return;
+}
+
+static void rci_output_group_id(rci_t * const rci)
+{
+ connector_remote_config_t const * const remote_config = &rci->shared.callback_data;
+ uint32_t encoding_data;
+
+ if (!have_group_id(rci))
+ {
+ state_call(rci, rci_parser_state_error);
+ goto done;
+ }
+
+ encoding_data = encode_group_id(get_group_id(rci));
+
+ if (get_group_index(rci) > 1)
+ encoding_data |= BINARY_RCI_ATTRIBUTE_BIT;
+
+ {
+ connector_bool_t const overflow = rci_output_uint32(rci, encoding_data);
+
+ if (!overflow)
+ {
+ if (remote_config->error_id != connector_success)
+ state_call(rci, rci_parser_state_error);
+ else
+ set_rci_output_state(rci, rci_output_state_group_attribute);
+ }
+ }
+
+done:
+ return;
+}
+
+static connector_bool_t encode_attribute(rci_t * const rci, unsigned int const index)
+{
+ uint32_t encoding_data;
+ connector_bool_t overflow = connector_false;
+
+ if (index > 1)
+ {
+ /* attribute output
+ * bit |7 | 6 5 | 4 3 2 1 0|
+ * |x | 0 1 | - index -|
+ */
+ #define BINARY_RCI_ATTRIBUTE_INDEX_MAX 31
+ #define BINARY_RCI_ATTRIBUTE_TYPE_INDEX 0x20
+
+ ASSERT(index <= BINARY_RCI_ATTRIBUTE_INDEX_MAX);
+ encoding_data = index | BINARY_RCI_ATTRIBUTE_TYPE_INDEX;
+
+ overflow = rci_output_uint32(rci, encoding_data);
+ }
+
+ return overflow;
+}
+
+static void rci_output_group_attribute(rci_t * const rci)
+{
+ unsigned int const index = get_group_index(rci);
+ connector_bool_t overflow = encode_attribute(rci, index);
+
+ if (!overflow)
+ state_call(rci, rci_parser_state_traverse);
+}
+
+
+static void rci_output_field_id(rci_t * const rci)
+{
+ connector_remote_config_t const * const remote_config = &rci->shared.callback_data;
+
+ if (!have_element_id(rci))
+ {
+ state_call(rci, rci_parser_state_error);
+ goto done;
+ }
+
+ {
+ /* output field id */
+ uint32_t id = encode_element_id(get_element_id(rci));
+
+ if (remote_config->error_id != connector_success) id |= BINARY_RCI_FIELD_TYPE_INDICATOR_BIT;
+
+ {
+ connector_bool_t const overflow = rci_output_uint32(rci, id);
+
+ if (overflow) goto done;
+
+ if (remote_config->error_id != connector_success)
+ state_call(rci, rci_parser_state_error);
+ else
+ set_rci_output_state(rci, rci_output_state_field_value);
+ }
+
+ }
+
+done:
+ return;
+}
+
+
+static void rci_output_field_value(rci_t * const rci)
+{
+ connector_group_element_t const * const element = get_current_element(rci);
+ connector_element_value_type_t const type = element->type;
+
+ connector_bool_t overflow = connector_false;
+
+
+ switch (rci->shared.callback_data.action)
+ {
+ case connector_remote_action_set:
+ overflow = rci_output_no_value(rci);
+ goto done;
+
+ case connector_remote_action_query:
+ break;
+ }
+
+ switch (type)
+ {
+#if defined RCI_PARSER_USES_STRINGS
+
+#if defined RCI_PARSER_USES_STRING
+ case connector_element_type_string:
+#endif
+
+#if defined RCI_PARSER_USES_MULTILINE_STRING
+ case connector_element_type_multiline_string:
+#endif
+
+#if defined RCI_PARSER_USES_PASSWORD
+ case connector_element_type_password:
+#endif
+
+#if defined RCI_PARSER_USES_FQDNV4
+ case connector_element_type_fqdnv4:
+#endif
+
+#if defined RCI_PARSER_USES_FQDNV6
+ case connector_element_type_fqdnv6:
+#endif
+
+#if defined RCI_PARSER_USES_DATETIME
+ case connector_element_type_datetime:
+#endif
+ ASSERT(rci->shared.value.string_value != NULL);
+ overflow = rci_output_string(rci, rci->shared.value.string_value, strlen(rci->shared.value.string_value));
+ break;
+#endif
+
+#if defined RCI_PARSER_USES_IPV4
+ case connector_element_type_ipv4:
+ ASSERT(rci->shared.value.string_value != NULL);
+ overflow = rci_output_ipv4(rci, rci->shared.value.string_value);
+ break;
+#endif
+
+#if defined RCI_PARSER_USES_INT32
+ case connector_element_type_int32:
+ overflow = rci_output_uint32(rci, rci->shared.value.signed_integer_value);
+ break;
+#endif
+
+#if (defined RCI_PARSER_USES_UNSIGNED_INTEGER)
+#if defined RCI_PARSER_USES_UINT32
+ case connector_element_type_uint32:
+#endif
+
+#if defined RCI_PARSER_USES_HEX32
+ case connector_element_type_hex32:
+#endif
+
+#if defined RCI_PARSER_USES_0X_HEX32
+ case connector_element_type_0x_hex32:
+#endif
+
+ overflow = rci_output_uint32(rci, rci->shared.value.unsigned_integer_value);
+ break;
+#endif
+
+#if defined RCI_PARSER_USES_FLOAT
+ case connector_element_type_float:
+ overflow = rci_output_float(rci, rci->shared.value.float_value);
+ break;
+#endif
+
+#if defined RCI_PARSER_USES_ENUM
+ case connector_element_type_enum:
+ overflow = rci_output_uint32(rci, rci->shared.value.enum_value);
+ break;
+#endif
+
+#if defined RCI_PARSER_USES_ON_OFF
+ case connector_element_type_on_off:
+ overflow = rci_output_uint32(rci, rci->shared.value.on_off_value);
+ break;
+#endif
+
+#if defined RCI_PARSER_USES_BOOLEAN
+ case connector_element_type_boolean:
+ overflow = rci_output_uint32(rci, rci->shared.value.boolean_value);
+ break;
+#endif
+ }
+
+done:
+ if (!overflow)
+ state_call(rci, rci_parser_state_traverse);
+
+}
+
+static void rci_output_field_terminator(rci_t * const rci)
+{
+ connector_remote_config_t const * const remote_config = &rci->shared.callback_data;
+
+ connector_bool_t const overflow = rci_output_terminator(rci);
+ if (!overflow)
+ {
+ invalidate_element_id(rci);
+
+ if (remote_config->error_id != connector_success)
+ state_call(rci, rci_parser_state_error);
+ else
+ state_call(rci, rci_parser_state_traverse);
+ }
+ return;
+}
+
+static void rci_output_group_terminator(rci_t * const rci)
+{
+ connector_remote_config_t const * const remote_config = &rci->shared.callback_data;
+
+ if (remote_config->error_id != connector_success)
+ {
+ state_call(rci, rci_parser_state_error);
+ }
+ else
+ {
+ connector_bool_t const overflow = rci_output_terminator(rci);
+ if (overflow) goto done;
+
+ set_rci_output_state(rci, rci_output_state_response_done);
+ }
+
+ invalidate_group_id(rci);
+
+done:
+ return;
+}
+
+static void rci_generate_output(rci_t * const rci)
+{
+ rci_buffer_t * const output = &rci->buffer.output;
+
+ if ((rci_buffer_remaining(output) != 0))
+ {
+ rci_debug_printf("output: %s\n", rci_output_state_t_as_string(rci->output.state));
+
+ switch (rci->output.state)
+ {
+ case rci_output_state_command_id:
+ rci_output_command_id(rci);
+ break;
+
+ case rci_output_state_group_id:
+ rci_output_group_id(rci);
+ break;
+
+ case rci_output_state_group_attribute:
+ rci_output_group_attribute(rci);
+ break;
+
+ case rci_output_state_field_id:
+ rci_output_field_id(rci);
+ break;
+
+ case rci_output_state_field_value:
+ rci_output_field_value(rci);
+ break;
+
+ case rci_output_state_field_terminator:
+ rci_output_field_terminator(rci);
+ break;
+
+ case rci_output_state_group_terminator:
+ rci_output_group_terminator(rci);
+ break;
+
+ case rci_output_state_response_done:
+
+ if (get_rci_input_state(rci) == rci_input_state_done)
+ {
+ trigger_rci_callback(rci, connector_request_id_remote_config_session_end);
+ set_rci_output_state(rci, rci_output_state_done);
+ }
+ else
+ {
+ state_call(rci, rci_parser_state_input);
+ set_rci_input_state(rci, rci_input_state_command_id);
+ }
+ break;
+
+ case rci_output_state_done:
+ {
+ connector_remote_config_t const * const remote_config = &rci->shared.callback_data;
+ if (remote_config->error_id != connector_success)
+ state_call(rci, rci_parser_state_error);
+ else
+ rci->status = rci_status_complete;
+ break;
+ }
+ }
+ }
+ else if ((rci_buffer_used(&rci->buffer.output) > 0) && (rci->status == rci_status_busy))
+ {
+ /* We are here since we have no space left for more output data and we have output data waiting to be sent.
+ So set up the state to send out the output data and come back to rci */
+
+ rci->status = rci_status_flush_output;
+ }
+
+
+#if defined RCI_DEBUG
+ {
+ size_t const bytes = rci_buffer_used(&rci->buffer.output);
+ if (bytes > 0)
+ {
+ connector_debug_hexvalue("Response", rci->buffer.output.start, bytes);
+ }
+ }
+#endif
+
+ return;
+}
+
+