The OpenTag C API for internal, function-based programming on the server-side is called OTAPI. OTAPI is not defined in DASH7 Mode 2, it is an API purpose-built for OpenTag that contains functionality that can be used to work with DASH7.
OTAPI declarations are contained in OTlib and spread across several files that have the name “OTAPI” in them.
OTAPI function implementations are stored in the code modules that are appropriate to the features that each OTAPI function needs to use. For example, OTAPI functions that work directly with session data with be stored in the session module, OTAPI functions that need to work with M2QP data are stored in the M2QP module, etc. Below is a list of the current modules and code files, stored in OTlib, that contain some OTAPI function implementations.
Existing OTAPI functions are very low-level, and they have a direct relationship with OTlib functions. Therefore, using OTAPI functions is like “building with small blocks.” It often takes many OTAPI functions, organized in a specific way, in order to build a dialog or achieve whatever other sort of functionality is desired. Therefore, using OTAPI functions correctly requires some knowledge of the DASH7 specification. Users may build extensions to OTAPI that bundle multiple OTAPI calls into single routines, but the nature of these “bundling” routines is very application-specific. OTlib must be very lightweight and generic, so the OTAPI is also very generic. In time, many easy-to-use, application-specific bundles will be available to OpenTag users looking for simpler interfaces.
OTAPI functions have a mostly consistent design.
ot_u16 otapi_function(ot_u8* status, void* template)
Return Value
OTAPI function MUST all return a 16 bit value (ot_u16). In the C header, this is an unsigned 16 bit integer, although the data can be anything.
Status Argument
OTAPI functions should take an 8 bit pointer value (ot_u8*), which reports a status value (zero on failure, non-zero on success). Functions that do not take a status argument are special cases, and these functions can be assumed to always run successfully (implicit status is non-zero).
Template Argument
OTAPI functions should take a template pointer to a data structure. This is the input data that goes into the OTAPI function, which allows easy transition from an ALP-type input stream to C-function argument. It is not always (void*), but it is always a structure pointer.
ot_u16 otapi_function(...);
Special-case OTAPI functions are occasionally used when there is a substantial code-efficiency gain that can be attained by using a special case form rather than the common form (above). New OTAPI functions should not use special-case form.
Templates fit into the Common Form OTAPI function. The same templates are often used elsewhere in OpenTag, in order to make the data transition between OTAPI to OTlib nearly effortless, with as little data-copying as possible and no double-bufferring unless it is absolutely necessary.
Below is the code from OTAPI_tmpl.h, which can also be found in the Doxygen documentation.
#include "OT_types.h" #include "OT_config.h" #define __SIZEOF(TMPL) (__SIZEOF_##TMPL) #define __ALLOCOF(TMPL) ((__SIZEOF_##TMPL + (PLATFORM_WORD_SIZE-1))/PLATFORM_WORD_SIZE) /// Veelite Templates #define __SIZEOF_vladdr_tmpl (1+1) typedef struct { ot_u8 block; ot_u8 id; } vladdr_tmpl; #define __SIZEOF_vlheader_tmpl (1+1+1+1+2+2) typedef struct { ot_u8 block; ot_u8 id; ot_u8 permissions; ot_u8 is_mirrored; ot_u16 length; ot_u16 alloc; } vlheader_tmpl; #define __SIZEOF_vldata_tmpl (2+2+(1*PLATFORM_POINTER_SIZE)) typedef struct { ot_u16 offset; ot_u16 bytes; ot_u8* data; } vldata_tmpl; /// Session Template #define __SIZEOF_session_tmpl (1+1+1+1+1+2) typedef struct { ot_u8 channel; ot_u8 subnet; ot_u8 subnetmask; ot_u8 flags; ot_u8 flagmask; ot_u16 timeout; } session_tmpl; /// Mode 2 MAC/Protocol Templates typedef enum { ADDR_unicast = 0x00, ADDR_broadcast = 0x40, ADDR_anycast = 0x80, ADDR_multicast = 0xC0 } addr_type; #define __SIZEOF_adv_tmpl (2+2+2) typedef struct { ot_u16 count; ot_u16 duty_on; ot_u16 duty_off; } adv_tmpl; #define __SIZEOF_chanlist_tmpl (2+(1*PLATFORM_POINTER_SIZE)) typedef struct { ot_u16 length; ot_u8* list; } chanlist_tmpl; #define __SIZEOF_csma_tmpl (1+2+2+2) typedef struct { ot_u8 csma_type; ot_u16 csma_guard_us; ot_u16 csma_guess_us; // usually determined by algorithm ot_u16 csma_timeout; // usually determined by algorithm } csma_tmpl; #define __SIZEOF_id_tmpl (1+(1*PLATFORM_POINTER_SIZE)) typedef struct { ot_u8 length; ot_u8* value; } id_tmpl; #define __SIZEOF_idlist_tmpl (1+1+(1*PLATFORM_POINTER_SIZE)) typedef struct { ot_u8 idcount; ot_u8 listlen; ot_u8* idlist; } idlist_tmpl; #define __SIZEOF_routing_tmpl (1+1+(3*__ALLOCOF(id_tmpl)) typedef struct { ot_u8 hop_code; ot_u8 hop_ext; id_tmpl dlog; id_tmpl orig; id_tmpl dest; } routing_tmpl; /// Mode 2 Protocol Templates #define __SIZEOF_ack_tmpl (1+1+(1*PLATFORM_POINTER_SIZE)) typedef struct { ot_u8 count; ot_u8 length; ot_u8* list; } ack_tmpl; #define __SIZEOF_command_tmpl (1+1+1) typedef struct { ot_u8 type; ot_u8 opcode; ot_u8 extension; } command_tmpl; typedef enum { CMDEXT_none = 0, CMDEXT_no_response = 0x02, CMDEXT_no_csma = 0x04, CMDEXT_ca_raind = (1<<3), CMDEXT_ca_aind = (2<<3), CMDEXT_ca_mac = (7<<3) } command_extensions; typedef enum { CMD_announce_file = 0, CMD_announce_series = 1, CMD_inventory_on_file = 2, CMD_inventory_on_series = 3, CMD_udp_on_file = 4, CMD_udp_on_series = 5, CMD_collect_file_on_file = 6, CMD_collect_series_on_file = 7, CMD_collect_file_on_series = 8, CMD_collect_series_on_series = 9, CMD_request_datastream = 12, CMD_propose_datastream = 13, CMD_ack_datastream = 14, CMD_reserved = 15 } command_opcodes; typedef enum { CMDTYPE_response = 0, CMDTYPE_error = (1 << 4), CMDTYPE_na2p_request = (2 << 4), CMDTYPE_a2p_init_request = (4 << 4), CMDTYPE_a2p_inter_request = (5 << 4), CMDTYPE_a2p_final_request = (7 << 4) } command_types; #define __SIZEOF_error_tmpl (1+1+(1*PLATFORM_POINTER_SIZE)) typedef struct { ot_u8 code; ot_u8 subcode; ot_u8* data; } error_tmpl; #define __SIZEOF_dialog_tmpl (2+2+(1*PLATFORM_POINTER_SIZE)) typedef struct { ot_u16 timeout; ot_u16 channels; ot_u8* chanlist; } dialog_tmpl; #define __SIZEOF_dp_tmpl (2+1+1+(1*PLATFORM_POINTER_SIZE)) typedef struct { ot_u16 data_condition; ot_u8 length; ot_u8 pid; ot_u8* data; } dp_tmpl; #define __SIZEOF_isfcall_tmpl (1+1+2+2) typedef struct { ot_u8 is_series; ot_u8 isf_id; ot_s16 max_return; ot_s16 offset; } isfcall_tmpl; #define __SIZEOF_isfcomp_tmpl (1+1+2) typedef struct { ot_u8 is_series; ot_u8 isf_id; ot_s16 offset; } isfcomp_tmpl; #define __SIZEOF_isfreturn_tmpl (1+1+1+(1*PLATFORM_POINTER_SIZE)) typedef struct { ot_u8 isf_id; ot_u8 offset; ot_u8 length; ot_u8* data; } isfreturn_tmpl; #define __SIZEOF_isfseriesreturn_tmpl (1+1+2+2+(2*PLATFORM_POINTER_SIZE)) typedef struct { ot_u8 isfs_id; ot_u8 series_length; ot_s16 contents_offset; ot_s16 content_length; ot_u8* series_data; ot_u8* contents_data; } isfseriesreturn_tmpl; typedef enum { QCODE_ismasked = 0x80, QCODE_nonnull = 0, QCODE_notequal = 0x20, QCODE_equal = 0x21, QCODE_lessthan = 0x22, QCODE_lessthan_equal = 0x23, QCODE_greaterthan = 0x24, QCODE_greaterthan_equal = 0x25, QCODE_search = 0x26 } query_codes; #define __SIZEOF_query_tmpl (1+1+(2*PLATFORM_POINTER_SIZE)) typedef struct { ot_u8 code; ot_u8 length; ot_u8* mask; ot_u8* value; } query_tmpl; #define __SIZEOF_shell_tmpl (1+1+1+(1*PLATFORM_POINTER_SIZE)) typedef struct { ot_u8 req_port; ot_u8 resp_port; ot_u8 data_length; ot_u8* data; } shell_tmpl;
Below is the code from OTAPI_c.h, which can also be found in the Doxygen documentation. Here is where all of the OTAPI functions are defined.
#include "OT_types.h" #include "OT_config.h" #include "OTAPI_tmpl.h" /****************************************************************************** * System API Functions (implemented in system.c) * * @note Session management is typically done via the System module. * * Request building process notes: * 1. Determin session parameters and store into a session_tmpl struct * 2. Call otapi_new_session() with the session_tmpl as a parameter * 3. Call otapi_open_request() * 4. Call transport protocol constructors (i.e. M2QP otapi functions) * 5. Call otapi_close_request() * 6. Call otapi_start_dialog() or otapi_start_flood() to send the request * * This process allows total configurability of the request. If your * application can be simplified, and it does not need total configurability, * you can pretty easily write subroutines that wrap this process into a single * function that is simple to use. *****************************************************************************/ #define OTAPI_SYSTEM_FUNCTIONS 6 /** @brief Refreshes system settings, wipes sessions, and brings to idle. * @param none * @retval ot_u16 0/1 on failure/success * @ingroup OTAPI_c * @sa sys_refresh() * * Despite the name, otapi_sysinit() is a wrapper for sys_refresh() and not * sys_init(). sys_refresh(), and hence otapi_sysinit(), will restart OpenTag * without clobbering the user application callbacks & data objects. */ ot_u16 otapi_sysinit(); /** @brief Manually creates a new ad-hoc session and prepares system. * @param s_tmpl (session_tmpl*) Session parameters * @retval ot_u16 The session number (see otapi_session_number) * @ingroup OTAPI_c * @sa otapi_session_number() * @sa otapi_new_request() * @sa otapi_start_flood() * * Call this when some event occurs that makes you want to send a DASH7 packet. * A session needs to exist for the DASH7 engine to do its job. All sessions * that you would generate by this function are ad-hoc, meaning they are not * scheduled for some time in the future (they happen right away). Scheduled * sessions are reserved for internal DASH7 usage. */ ot_u16 otapi_new_session(session_tmpl* s_tmpl); /** @brief Manually creates (and opens) a request frame in the top session * @param addr (addr_type) enumerated addressing method (unicast, broadcast, etc) * @param routing (routing_tmpl*) A routing_tmpl * @retval ot_u16 The post-op length of the TX queue in bytes * @ingroup OTAPI_c * @sa otapi_close_request() * @sa otapi_new_session() * * Most often you will call this immediately after otapi_new_session(). Any * kind of event-generated frame/packet in DASH7 is always a request. A DASH7 * response is defined as something that follows a request, so if you are * starting a new ad-hoc session, the outgoing packet will always be a request. * * The second input parameter, routing, is a void pointer and must be used * according to the value of addr. * - If addr is BROADCAST or MULTICAST, routing should = NULL. * - If addr is UNICAST, routing should be of type (id_tmpl*). * - If addr is ANYCAST, routing should be of type (routing_tmpl*) * * As you can see, Anycast addressing is the only form of request addressing * that supports multi-hop routing. */ ot_u16 otapi_open_request(addr_type addr, routing_tmpl* routing); /** @brief Manually finishes a request frame in the top session * @retval ot_u16 The post-op length of the TX queue in bytes * @ingroup OTAPI_c * @sa otapi_open_request() * @sa otapi_start_flood() * @sa otapi_start_dialog() * * In certain cases, footer data needs to be appended to the frame (mostly, * this is when you are doing some kind of encryption). In these cases, * otapi_close_request() will make sure to put it in the TX queue after the * frame data. * * Best practice is to always call otapi_close_request() when you are done * building the request. When there is no encryption, you technically do not * need to call it, but this can lead to bugs in your application if you * decide to add encryption later. (If you are not encrypting, it adds maybe * only 8 instruction cycles to your runtime) */ ot_u16 otapi_close_request(); /** @brief Begins a DASH7 M2AdvP flood onto the top session. * @param flood_duration (ot_u16) Number of ticks for the flood duration * @retval ot_u16 0/1 on failure/success of the flood initialization * @ingroup OTAPI_c * @sa otapi_new_session() * @sa otapi_new_request() * @sa otapi_start_dialog() * * This function is implemented in a way that matches a portion of the DASH7 * Mode 2 spec. To understand the data I/O completely, refer to the spec. * * An M2AdvP flood is a way to take control of a channel so that a session can * be guaranteed to occur at some point in the near future (precisely, a few * ticks after the time of flood initialization + flood_duration). * * To initialize a flood, a session has to be started with otapi_new_session(). * Then call otapi_open_request(), whatever M2QP functions you need to build * that request, and otapi_close_request(). Then call otapi_start_flood() to * begin the flood. When the flood is over, OpenTag will automatically begin * the request dialog (no need to call otapi_start_dialog()). */ ot_u16 otapi_start_flood(ot_u16 flood_duration); /** @brief Begins a DASH7 dialog onto the top session, without a flood. * @retval ot_u16 0/1 on failure/success of the dialog initialization * @ingroup OTAPI_c * @sa otapi_new_session() * @sa otapi_new_request() * @sa otapi_start_flood() * * Call this when you want to kick-off a request that has been already built * using the normal request-building process. * * If you are using a flood prior to the request, use otapi_start_flood() and * not this function. It will automatically kick-off the request dialog when * the flood is complete. * * If you are not using a flood prior to the request, then use this function. */ ot_u16 otapi_start_dialog(); /****************************************************************************** * Session API Functions (implemented in session.c) * The session API is special-purpose and does not follow the standard form. * * @note Session management is generally automatic. These functions just * allow future exploration at the API/App level *****************************************************************************/ #define OTAPI_SESSION_FUNCTIONS 3 /** @brief Returns a 16 bit value uniquely corresponding to the top session * @param none * @retval ot_u16 The 16 bit session value * @ingroup OTAPI_c * * The session value is actually just a concatenation of the 8 bit "channel" * element (channel ID) and the 8 bit "dialog_id" element (random dialog num) * that are part of a session structure. The session stack may contain only * one scheduled channel for each of the supported channels at any given time, * so the session value will always be unique. * * The return value is always 0 for the case when the stack is empty. */ ot_u16 otapi_session_number(); /** @brief Deletes all expired sessions in the session stack * @param none * @retval ot_u16 The size of the session stack after flushing * @ingroup OTAPI_c */ ot_u16 otapi_flush_sessions(); /** @brief Indicates if a given channel is already in the session stack. * @param chan_id (ot_u8) channel ID to check for blocking * @retval ot_u16 0/1 if supplied channel ID is unblocked/blocked * @ingroup OTAPI_c * * The session stack can only contain one session of a given channel at any * given time. Sessions are not typically very long, so this feature should * not be a hinderance to DASH7 operation. On the other hand, it does allow * simplicity by removing needs for priorities and such in session mgmt. */ ot_u16 otapi_is_session_blocked(ot_u8 chan_id); /****************************************************************************** * M2QP API Functions (implemented in M2QP.c) * The M2QP API follows the standard form. Future API extensions will follow * the form: ot_u16 otapi_function(ot_u8* status, void* data_type) *****************************************************************************/ #define OTAPI_M2QP_FUNCTIONS 11 /** @brief Writes M2QP command parameters onto the request TX queue * @param status (ot_u8*) returns a status code (0 = error) * @param command (command_tmpl*) command input parameter template * @retval ot_u16 post-op length of the TX queue * @ingroup OTAPI_c * * This function is implemented in a way that matches a portion of the DASH7 * Mode 2 spec. To understand the data I/O completely, refer to the spec. * */ ot_u16 otapi_put_command_tmpl(ot_u8* status, command_tmpl* command); /** @brief Write A2P/NA2P dialog parameters onto the request TX queue * @param status (ot_u8*) returns a status code (0 = error) * @param dialog (dialog_tmpl*) dialog input parameter template * @retval ot_u16 post-op length of the TX queue * @ingroup OTAPI_c * * API dir_cmd code == 0x02 * * This function is implemented in a way that matches a portion of the DASH7 * Mode 2 spec. To understand the data I/O completely, refer to the spec. * * One spec-driven usage note is that, by setting rx_channels = 0, the chanlist * argument will be ignored and the pre-existing settings (from the request TX) * will be used automatically. */ ot_u16 otapi_put_dialog_tmpl(ot_u8* status, dialog_tmpl* dialog); /** @brief Write M2QP Query parameters onto the end of the TX queue * @param status (ot_u8*) returns a status code (0 = error) * @param query (query_tmpl*) query input parameter template * @retval ot_u16 post-op length of the TX queue * @ingroup OTAPI_c * * API dir_cmd == 0x03 * * This function is implemented in a way that matches a portion of the DASH7 * Mode 2 spec. To understand the data I/O completely, refer to the spec. * * query->length param: the query token and mask are the same length, which is * the number of bytes set in this parameter. * * query->code param: b7 is 0/1 if the mask is disabled/enabled. * (see Mode 2 spec for further definition) */ ot_u16 otapi_put_query_tmpl(ot_u8* status, query_tmpl* query); /** @brief Write a device ack list onto the end of the TX queue * @param status (ot_u8*) returns a status code (0 = error) * @param ack (ack_tmpl*) ack input parameter template * @retval ot_u16 post-op length of the TX queue * @ingroup OTAPI_c * * API dir_cmd == 0x04 */ ot_u16 otapi_put_ack_tmpl(ot_u8* status, ack_tmpl* ack); /** @brief Writes an error tmpl to a response. Not currently implemented. * @param status (ot_u8*) returns a status code (0 = error) * @param error (error_tmpl*) error input parameter template * @retval ot_u16 post-op length of the TX queue * @ingroup OTAPI_c * * API dir_cmd == 0x05 * * This function is implemented in a way that matches a portion of the DASH7 * Mode 2 spec. To understand the data I/O completely, refer to the spec. */ ot_u16 otapi_put_error_tmpl(ot_u8* status, error_tmpl* error); /** @brief Writes ISF comparison data to the request queue. * @param status (ot_u8*) returns a status code (0 = error) * @param isfcomp (comp_tmpl*) isf comparison data for request * @retval ot_u16 post-op length of the TX queue * @ingroup OTAPI_c * * API dir_cmd == 0x06 * * This function is implemented in a way that matches a portion of the DASH7 * Mode 2 spec. To understand the data I/O completely, refer to the spec. */ ot_u16 otapi_put_isf_comp(ot_u8* status, isfcomp_tmpl* isfcomp); /** @brief Writes ISF call data to the request queue. * @param status (ot_u8*) returns a status code (0 = error) * @param isfcomp (comp_tmpl*) isf call data for request * @retval ot_u16 post-op length of the TX queue * @ingroup OTAPI_c * * API dir_cmd == 0x07 * * This function is implemented in a way that matches a portion of the DASH7 * Mode 2 spec. To understand the data I/O completely, refer to the spec. */ ot_u16 otapi_put_isf_call(ot_u8* status, isfcall_tmpl* isfcall); /** @brief "Calls" ISF or ISF series and writes the return data to the TX Queue * @param status (ot_u8*) returns a status code (0 = error) * @param isfcall (isfcall_tmpl*) ISF call template (matches typedef) * @retval ot_u16 post-op length of the TX queue * @ingroup OTAPI_c * * API dir_cmd == 0x08 * * This function is implemented in a way that matches a portion of the DASH7 * Mode 2 spec. To understand the data I/O completely, refer to the spec. */ ot_u16 otapi_put_isf_return(ot_u8* status, isfcall_tmpl* isfcall); /** @brief Writes the request datastream command, including read directives * @param status (ot_u8*) returns a status code (0 = error) * @param dsq (Queue*) Queue where the request datastream is stored * @retval ot_u16 post-op length of the TX queue * @ingroup OTAPI_c * * API dir_cmd == 0x09 * * This function is implemented in a way that matches a portion of the DASH7 * Mode 2 spec. To understand the data I/O completely, refer to the spec. * * The request datastream must be non-null, and point to a datastream that * carries one or more application subprotocol read directives. The request * datastream (read directives) must be able to fit inside the request frame * of this initial request. * * The remainder of the datastream handshaking and transfer process is managed * automatically. The complete, received datastream will be loaded into the * internal datastream queue, and it can be logged if desired. */ ot_u16 otapi_put_reqds(ot_u8* status, Queue* dsq); /** @brief Writes the propose datastream command, including optional write directives * @param status (ot_u8*) returns a status code (0 = error) * @param dsq (Queue*) Queue where the propose [write] datastream is stored * @retval ot_u16 post-op length of the TX queue * @ingroup OTAPI_c * * API dir_cmd == 0x0A * * This function is implemented in a way that matches a portion of the DASH7 * Mode 2 spec. To understand the data I/O completely, refer to the spec. * * If the proposed datastream is too long to fit inside the initial request * frame, it will be placed inside a subsequent datastream transfer. * * The remainder of the datastream handshaking and transfer process is managed * automatically. The complete, transmitted datastream will be loaded into an * internal datastream queue prior to transmission. The recipient of the * datastream typically writes it to veelite. */ ot_u16 otapi_put_propds(ot_u8* status, Queue* dsq); /** @brief Writes shell data to the request queue. * @param status (ot_u8*) returns a status code (0 = error) * @param shell (shell_tmpl*) Shell template * @retval ot_u16 post-op length of the TX queue * @ingroup OTAPI_c * * API dir_cmd == 0x0B * * This function is implemented in a way that matches a portion of the DASH7 * Mode 2 spec. To understand the data I/O completely, refer to the spec. */ ot_u16 otapi_put_shell_tmpl(ot_u8* status, shell_tmpl* shell);