Indigresso Wiki

Open Source Stuff for DASH7

User Tools

Site Tools


opentag:capi

C API

The C API is just callable C functions, each of which is a simplified wrapper for a sequence lower-level OTlib functions. The C API can be used to build requests, create events, and do some other sandboxed features that are part of OpenTag's standard OTlib functionality (in other words, if you are an OpenTag kung-fu master, you can just call the OTlib low-level functions directly to get the same effect). That said, the C API functions are cleaner and simpler to use, and just as good if you don't need the deepest level of functionality.

Relevant Files

To use the C API, you need to include OTAPI.h in your code. OTAPI.h will automatically include the appropriate sub-components, based on how OT_config.h is configured. The C API functions are defined in the OTAPI_c.h file. Many of these functions use templates as input. The templates are defined in OTAPI_tmpl.h. These are the two most important OTAPI headers to read in order to learn about the C API. The C API functions are implemented within the .c modules that best match their featureset. For example, the otapi_system functions are implemented in system.c and the otapi_session functions in session.c.

Using Low Level Functions

Experts might want to bypass the C API entirely, in favor of using the low level OpenTag functions themselves. The low level function architecture is not documented as much as the API architecture, but you can get an idea of how it works by seeing howe the OTAPI functions are implemented.

Extensibility

New OTAPI C functions can be added very easily, if new functionality is desired. Hypothetically, you could add as many new OTAPI C functions as can fit on your platform. Realistically, there are some limitations due to the linkages to other APIs (DASHForth and ALP).

General Form

The general form of a C API function is:
ot_u16 otapi_function_name(ot_u8* status, void* tmpl)

  • The status parameter should be OR'ed or added when there is some sort of problem with the input template.
  • The tmpl parameter is a pointer to some data-structure type that contains relevant input data. In the actual function prototype (defined in OTAPI_c.h), it should not use “void*” type unless the input template is variadic.
  • The ot_u16 return value is an unsigned integer. It can have different values, but in all cases “0” means that nothing happened. In most usages, the return value corresponds to a number of bytes present on the TX queue.

Special Form

Some C API functions do not obey the General Form. However, all C API functions return an ot_u16 value. Special Form functions typically are Session or System functions that do not do any queue-based data processing.

Linking to DASHForth

C API functions can be easily linked to DASHForth words if they are written in the general form (above). The practical limit for DASHForth words is something like 1024, and currently 20 API words are reserved.

  • DASHForth can ignore the status input (production) or use it to generate error messages (debug). The production/debug classification is controllable at a pseudo-runtime level (Forth is a unique language, check it out).
  • The template input is Loaded onto the Forth data stack in reverse, to comply with Forth's postfix architecture.
  • One thing to consider is the return value of the otapi function. This 16 bit value it is what gets pushed onto the Forth data stack after the word is processed.

Linking to ALP

Linking a C API function to ALP is easy. All C API functions are attributed with a two byte identifier, including a class identifier (Directive ID) and a command identifier (Directive Command). In the documentation below, the [class:command] is presented in the heading for each C API function.

  • The practical limit for ALP-API is 16 classes and 127 commands per class.
  • The status value is returned via the ALP message pipe (MPipe), preceeding the 16 bit return value
  • The template input shall be streamed onto the message pipe. Any strings/arrays that the template requires shall be inlined into the ALP directive data. Data containers are always transported as BIG ENDIAN.

Some Examples

Using the C API is like “building with Legos.” In the future, API subroutines will probably be made available that serve as simplified templates. However, the maximum level of functionality can only be achieved through usage of the full C API.

One thing you might notice about the design of the C API is that it uses a message-based function call method (using data structures to load parameters) instead of a pure C stack method. The benefit of this method is that C API functions can be directly ported to any type of messaging architecture (which allows direct translation of the application layer).

Setting up a dialog using broadcast announcement (a beacon)

session_tmpl s_tmpl;
command_tmpl cmd;
dialog_tmpl dialog;
isfcall_tmpl filedata;
ot_u8 dialog_chanlist[4];
ot_u8 protocol_status;

s_tmpl.channel    = 0x01;    //use channel 01, 
s_tmpl.subnet     = 0x0F;    //use subnet 0F (universal)
s_tmpl.subnetmask = 0x0F;    //use subnet mask 0F (universal)
s_tmpl.flags      = 0;       //use default network frame flags
s_tmpl.flagmask   = 0;       //use default network frame flags
s_tmpl.timeout    = 0;       //Run this session immediately (no delay)

cmd.opcode        = CMD_announcement_of_file;
cmd.type          = 0;       //Type is only important for Multicasting
cmd.extension     = 0;       //Not using extension (extension includes advanced features)

dialog.timeout    = 100;     //100 tick response timeout (97.7 ms)
dialog.channels   = 1;       //Allow responses on 1 channel
dialog_chanlist[0]= 0x02;    //Hop to channel 02 to receive responses
dialog.chanlist   = dialog_chanlist;

filedata.is_series= 0;       //Include data from a single ISF file
filedata.isf_id   = 0x01;    //Use ISF File 0x01 (Device Parameters)
filedata.offset   = 0;       //Start data at beginning of file (0 bytes offset)

otapi_new_session(&s_tmpl);
otapi_open_request(BROADCAST, NULL);
otapi_put_command_tmpl(&protocol_status, &cmd);
otapi_put_dialog_tmpl(&protocol_status, &dialog);
otapi_put_isf_return(&protocol_status, &filedata);
otapi_close_request();

// If protocol_status != 1, something was done incorrectly.  In that case, we will
// log an error message (there is no standard for error messages, so you could put
// in whatever you want).  If no error, then send the request.
if (protocol_status == 1) {
    otapi_start_dialog();
}
else {
    otapi_log_msg(7, "ERR_API", 30, "Command was built incorrectly");
}

C API Function Manifest

Session Functions

The session C API functions create sessions and give information about the session status. The system module does most of these things automatically, but you can use the session API if you are cooking up some type of enhanced server-side application. The session C API functions are declared in OTAPI_c.h and implemented in session.c.

About the Session Stack

Sessions are stored in a volatile stack. It is volatile, because old sessions are replaced by newly added sessions when the stack gets full. The stack is insertion-sorted whenever a new session is added. A typical stack is 4 sessions deep, which is more than enough for 99% of use-cases (you can increase of decrease the size of the stack via OT_config.h).

About Sessions

Sessions are scheduled dialogs, or currently ongoing dialogs. Dialogs/Sessions have certain attributes that are maintained throughout the duration of the session. These attributes are stored in the session stack.

otapi_session_number() [0x80:01]

Returns a 16 bit value uniquely corresponding to the top session
arguments: none
returns: The 16 bit session value Dir Cmd: 0x01

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.

otapi_flush_sessions() [0x80:02]

Deletes all expired sessions in the session stack
arguments: none
returns: The size of the session stack after flushing Dir Cmd: 0x02

otapi_is_session_blocked(ot_u8 chan_id) [0x80:03]

Indicates if a given channel is already in the session stack.
arguments: chan_id (ot_u8) channel ID to check for blocking
returns: 0/1 if supplied channel ID is unblocked/blocked Dir Cmd: 0x03

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.

System Functions

System API functions control the System Module, which itself is responsible for managing the Session Module (therefore, direct access to the Session API is purely optional). The System Module also is responsible for managing the flow of protocol data into and out-of OpenTag. Much of this I/O is automatic. However, API-users are able to manually invoke DASH7 dialogs via the System API.

otapi_sysinit() - [0x81:01]

Initializes the system module from the configuration file in veelite.
Arguments: none
Returns: (ot_u16) 0/1 on failure/success

There are some configuration files in the ISF data that control the way the the system works. When calling this function, data from these files are buffered into the system control matrix. Considering this, you should call this function anytime the matrix is cleared or anytime you want to reset the configuration of the device to the configuration file parameters.

otapi_new_session(session_tmpl* s_tmpl) - [0x81:02]

Manually creates a new ad-hoc session and prepares system.
Arguments: (session_tmpl*) Session parameters
Returns: (ot_u16) The session number (see otapi_session_number)

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.

otapi_open_request(addr_type addr) - [0x81:03]

Manually creates (and opens) a request frame in the top session
Arguments: (addr_type) enumerated addressing method (unicast, broadcast, etc)
Returns: (ot_u16) The post-op length of the TX queue in bytes

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.

otapi_close_request() - [0x81:04]

Manually finishes a request frame in the top session
Returns: (ot_u16) The post-op length of the TX queue in bytes

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)

otapi_start_flood(ot_u16 flood_duration) - [0x81:05]

Begins a DASH7 M2AdvP flood onto the top session.
Arguments: flood_duration (ot_u16) Number of ticks for the flood duration
Returns: (ot_u16) 0/1 on failure/success of the flood initialization

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()).

otapi_start_dialog() - [0x81:06]

Begins a DASH7 dialog onto the top session, without a flood.
Returns: (ot_u16) 0/1 on failure/success of the dialog initialization

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.

Query Functions

After a Dialog is created with the System API, the Dialog is populated with the Query API. The user should be familiar with the basic templating design of the Mode 2 Protocols (particularly M2QP).

otapi_put_command_tmpl(ot_u8* status, command_tmpl* cmd) - [0x82:01]

Write M2QP Command parameters onto the end of the TX queue
Argument status: (ot_u8*) returns a status code (nonzero = error)
Argument cmd: (command_tmpl*) command input parameter template
Returns: (ot_u16) post-op length of the TX queue

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.

otapi_put_dialog_tmpl(ot_u8* status, dialog_tmpl* dialog) - [0x82:02]

Write A2P/NA2P dialog parameters onto the request TX queue
Argument status: (ot_u8*) returns a status code (nonzero = error)
Argument dialog: (dialog_tmpl*) dialog input parameter template
Returns: (ot_u16) post-op length of the TX queue

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.

otapi_put_query_tmpl(ot_u8* status, query_tmpl* query) - [0x82:03]

Write M2QP Query parameters onto the end of the TX queue
Argument status: (ot_u8*) returns a status code (nonzero = error)
Argument query: (query_tmpl*) query input parameter template
Returns: (ot_u16) post-op length of the TX queue

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)

otapi_put_ack_tmpl(ot_u8* status, ack_tmpl* ack) - [0x82:04]

Write a device ack list onto the end of the TX queue
Argument status: (ot_u8*) returns a status code (nonzero = error)
Argument ack: (ack_tmpl*) ack input parameter template
Returns: (ot_u16) post-op length of the TX queue

otapi_put_error_tmpl(ot_u8* status, error_tmpl* error) - [0x82:05]

Writes an error tmpl to a response. Not currently implemented.
Argument status: (ot_u8*) returns a status code (nonzero = error)
Argument error: (error_tmpl*) error input parameter template
Returns: (ot_u16) post-op length of the TX queue

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.

otapi_put_isf_comp(ot_u8* status, isfcomp_tmpl* isfcomp) - [0x82:06]

Writes ISF comparison data to the request queue.
Argument status: (ot_u8*) returns a status code (nonzero = error)
Argument isfcomp: (comp_tmpl*) isf comparison data for request
Returns: (ot_u16) post-op length of the TX queue

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.

otapi_put_isf_call(ot_u8* status, isfcall_tmpl* isfcall) - [0x82:07]

Writes ISF call data to the request queue.
Argument status: (ot_u8*) returns a status code (nonzero = error)
Argument isfcomp: (comp_tmpl*) isf call data for request
Returns: (ot_u16) post-op length of the TX queue

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.

otapi_put_isf_return(ot_u8* status, isfcall_tmpl* isfcall) - [0x82:08]

“Calls” ISF or ISF series and writes the return data to the TX Queue
Argument status: (ot_u8*) returns a status code (nonzero = error)
Argument isfcall: (isfcall_tmpl*) ISF call template (matches typedef)
Returns: (ot_u16) post-op length of the TX queue

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.

otapi_put_reqds(ot_u8* status, Queue* dsq) - [0x82:09]

Writes the request datastream command, including read directives
Argument status: (ot_u8*) returns a status code (nonzero = error)
Argument dsq: (Queue*) Queue where the request datastream is stored
Returns: (ot_u16) post-op length of the TX queue

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.

otapi_put_propds(ot_u8* status, Queue* dsq) - [0x82:0A]

Writes the propose datastream command, including optional write directives
Argument status: (ot_u8*) returns a status code (nonzero = error)
Argument dsq: (Queue*) Queue where the propose [write] datastream is stored
Returns: (ot_u16) post-op length of the TX queue

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.

otapi_put_shell_tmpl(ot_u8* status, shell_tmpl* shell) - [0x82:0B]

Writes shell data to the request queue.
Argument status: (ot_u8*) returns a status code (nonzero = error)
Argument shell: (shell_tmpl*) Shell template
Returns: (ot_u16) post-op length of the TX queue

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.

opentag/capi.txt · Last modified: 2011/10/01 01:11 by jpnorair