Indigresso Wiki

Open Source Stuff for DASH7

User Tools

Site Tools


M2QP Transport Module (OTlib)

M2QP (Mode 2 Query Protocol) is the basis of the DASH7 Mode 2 Transport Layer. OpenTag implements all of M2QP as a C code module within OTlib.

Implementation Notes

The module is completely process oriented, including a few callbacks for user applications. The [opentag:kernel:main|Kernel]] must activate M2QP functions indirectly via the Network Module during normal RX frame processing, and it may, in certain cases like beacon frame generation, call M2QP functions directly. The user application or internal code blocks may also use the Query C API or ALP API to build M2QP transport commands from templates.

Data Elements

M2QP data elements are meant for managing queries and transport commands, and they are not generally required for any modules apart from M2QP itself. If you wish to use them, do so carefully, because M2QP operation depends on their values.

  • m2qp (m2qp_struct: M2QP.h)
    • cmd (cmd_data: M2QP.h)
      • code (ot_u8) – transport frame command code
      • ext (ot_u8) – optional transport frame command code extension
    • qdata (query_data: M2QP.h)
      • comp_id (ot_u8) – query comparison ID, from transport code
      • comp_offset (ot_int) – query comparison offset amount
    • qtmpl (query_tmpl: OTAPI_tmpl.h)
      • code (ot_u8) – selector for query method
      • length (ot_u8) – length of query in bytes
      • mask (ot_u8*) – query bitmask string, optionally NULL if no mask
      • value (ot_u8*) – query string, must be non-null
    • signal (m2qp_sigs: M2QP.h)
      • shell_request (ot_sigresp: M2QP.h) – callback for shell request transport frames
      • error_response (ot_sigresp) – callback for error response transport frames
      • std_response (ot_sigresp) – callback for standard response transport frames
      • a2p_response (ot_sigresp) – callback for A2P response transport frames
      • dspkt_response (ot_sigresp) – callback for datastream packet responses
      • dsack_response (ot_sigresp) – callback for datastream ack responses


M2QP includes several callbacks that are available to user applications. They can be enabled or disabled at compile-time through application configuration methods.

Other Notes

A lot of the M2QP functionality is based on queries. DASH7 Mode 2 has extremely powerful, yet compact, querying capabilities, and these capabilities are mostly in the domain of M2QP. Querying requires sufficient methods for performing arithmetic or string-based comparisons (see the DASH7 wiki pages for more on this), but it also requires methods for accessing data. In order to keep code size small and maximize the The OpenTag M2QP module uses a single, universal data access routine for reading or writing to one or more ISFB files. Differences in the comparison method are applied by an extensible function that is supplied into the access+comparison routine as a function pointer.


The code from M2QP.h is pasted below, although you can also check the doxygen code documentation.

#include "OT_types.h"
#include "OTAPI_tmpl.h"
#include "session.h"
#include "system.h"
#include "queue.h"

// Mode 2 Application Subprotocol IDs
#define M2SPID_NULL             (0x00)
#define M2SPID_FILEDATAv1       (0x01)
#define M2SPID_SENSORv1         (0x02)
#define M2SPID_SEC_INITv1       (0x11)
#define M2SPID_SEC_FINISHv1     (0x12)
#define M2SPID_SEC_BCASTKEYv1   (0x13)
#define M2SPID_SEC_PUBKEYv1     (0x14)

// M2QP Transport Types
// NA2P = "Non Arbitrated 2-Party [Transport]"
// A2P  = "Arbitrated 2-Party [Transport]"  
//      -> Formal behavior is for sequential query casting (or retries).  
//      -> Can also be hacked to do many very cool things, without violating spec
#define M2TT_MASK               0x70
#define M2TT_RESPONSE           0x00
#define M2TT_ERROR              0x10
#define M2TT_REQNA2P            0x20        //NA2P Request (Normal)
#define M2TT_REQA2P_I           0x40        //Initial A2P Request
#define M2TT_REQA2P_X           0x50        //Intermediate A2P Request
#define M2TT_REQA2P_T           0x70        //Terminal (Final) A2P Request

// M2QP Opcodes
#define M2OP_MASK               0x0F
#define M2OP_ANN_F              0x00        //Announcement of File
#define M2OP_ANN_S              0x01        //Announcement of File Series
#define M2OP_INV_F              0x02        //Inventory from File
#define M2OP_INV_S              0x03        //Inventory from File Series
#define M2OP_UDP_F              0x04
#define M2OP_UDP_S              0x05
#define M2OP_COL_FF             0x06        //Collection: File/File
#define M2OP_COL_SF             0x07        //Collection: Series/File
#define M2OP_COL_FS             0x08        //Collection: File/Series
#define M2OP_COL_SS             0x09        //Collection: Series/Series
#define M2OP_DS_REQUEST         0x0C        //Datastream Request
#define M2OP_DS_PROPOSE         0x0D        //Datastream Propose
#define M2OP_DS_ACK             0x0E        //Datastream Acknowledge
#define M2OP_RFU                0x0F        //Reserved

// M2QP Command Extension Options
#define M2CE_NORESP             (0x01 << 1)
#define M2CE_NOCSMA             (0x01 << 2)
#define M2CE_CA_MASK            (0x07 << 3)
#define M2CE_CA_RIGD            (0x00 << 3)
#define M2CE_CA_RAIND           (0x01 << 3)
#define M2CE_CA_AIND            (0x02 << 3)
#define M2CE_SCRAP              (0x01 << 6)
#define M2CE_NOACK              (0x01 << 7)

// Mode 2 Query Comparisons 
#define M2QC_MASKED             (0x80)
#define M2QC_VAL_EXISTS         (0x00)
#define M2QC_ALU                (0x20)
#define M2QC_ALU_NE             (0x20)
#define M2QC_ALU_EQ             (0x21)
#define M2QC_ALU_LT             (0x22)
#define M2QC_ALU_LTE            (0x23)
#define M2QC_ALU_GT             (0x24)
#define M2QC_ALU_GTE            (0x25)
#define M2QC_COR_SEARCH         (0x40)
#define M2QC_COR_THRMASK        (0x1F)
#define M2QC_ERROR              (0xFF)

/** PM2 data types
  * These are not really supposed to be used externally, but leaving them
  * exposed does not hurt anything, and it makes module testing much easier.
typedef union {
    ot_s32  slong[4];
    ot_u32  ulong[4];
    ot_s16  sshort[8];
    ot_u16  ushort[8];
    ot_u8   ubyte[16];
} dstr_16;

typedef union {
    ot_s32  slong[2];
    ot_u32  ulong[2];
    ot_s16  sshort[4];
    ot_u16  ushort[4];
    ot_u8   ubyte[8];
} dstr_8;

typedef struct {
    ot_u8   comp_id;
    ot_int  comp_offset;  
} query_data;

typedef struct {
    ot_u8   code;
    ot_u8   ext;
} cmd_data;

/** ot_sigresp function pointer type
  * param1 = Device ID pointer of responding device
  * param2 = length of packet payload
  * param3 = data pointer to packet payload
  * Do not alter the Device ID data in your callback!!! 
  * It points to m2np.rt.dlog from M2_network.h
typedef ot_bool (*ot_sigresp)(id_tmpl*, ot_int, ot_u8*);

typedef struct {
    ot_sigresp shell_request;
        ot_sigresp error_response;
        ot_sigresp std_response;
        ot_sigresp a2p_response;
#       if (M2_FEATURE(DATASTREAM) == ENABLED)    
            ot_sigresp dspkt_response;
            ot_sigresp dsack_response;
#       endif
#   endif

} m2qp_sigs;

// potentially used in datastream frame fragmentation, but this feature is not
// implemented yet, and this structure may change markedly

typedef struct {
    cmd_data        cmd;        // internal usage
    query_data      qdata;      // internal usage
    query_tmpl      qtmpl;
        m2qp_sigs   signal;
#   endif
} m2qp_struct;

extern m2qp_struct m2qp;

/** Protocol Automated Interface Functions "pm2_put"

/** @brief A shortcut function to prepare beacons.
  * @param  cmd_code    (ot_u8) beacon command (must be announcement)
  * @param  cmd_ext     (ot_u8) beacon command extension (optional)
  * @param  input       (Queue*) queue pointer to File/File Series Call Template
  * @retval none
  * @ingroup M2QP
  * The cmd_ext param should be 0 if you are not using it.
ot_int m2qp_put_beacon(ot_u8 cmd_code, ot_u8 cmd_ext, Queue* input);

/** @brief A shortcut function to prepare beacons.
  * @param  cmd_code    (ot_u8) beacon command (must be announcement)
  * @param  cmd_ext     (ot_u8) beacon command extension (optional)
  * @param  input       (Queue*) queue pointer to File/File Series Call Template
  * @retval none
  * @ingroup M2QP
  * The cmd_ext param should be 0 if you are not using it.
void m2qp_put_na2ptmpl( ot_u16 rx_timeout, 
                        ot_u8 rx_channels, 
                        ot_u8* rx_chanlist);

void m2qp_put_a2ptmpl(  ot_u16 rx_timeout, 
                        ot_u8 csma_guard, 
                        ot_u8 rx_channels, 
                        ot_u8* rx_chanlist);

void m2qp_set_suppliedid( ot_bool vid, ot_u8* supplied_addr );

///@todo I might be able to remove these
ot_int m2qp_put_isfs( ot_u8* isf_template );
ot_int m2qp_put_isf( ot_u8 isf_id, ot_u8 offset, ot_u16 max_length );

 * M2QP Parsing Functions *

/** @brief A Null Callback for ot_sigresp types (response callbacks)
  * @param  responder_id    (id_tmpl*) pointer to m2np.rt.txer
  * @param  payload_length  (ot_int) length of the response payload, in bytes
  * @param  payload         (ot_u8*) pointer to payload data
  * @retval ot_bool         Returning False will abort remaining response processing
  * @ingroup M2QP
  * This function does nothing.  m2qp_init() will set all callbacks to this 
  * function.  It is exposed in order to illustrate the arguments for ot_sigresp 
  * callbacks, so the user can write M2QP callbacks correctly.
ot_bool m2qp_sigresp_null(id_tmpl* responder_id, ot_int payload_length, ot_u8* payload);

/** @brief  Initializes the M2QP Module
  * @param  none
  * @retval none
  * @ingroup M2QP
  * @sa sys_init()
  * Run this function after a reboot, or any time that the protocol module
  * memory needs to be re-initialized.  sys_init() calls it, so as long as
  * you call sys_init() on startup, you're covered.
void m2qp_init();

/** @brief  Parses an incoming Protocol Frame (Request, Response, Data)
  * @param  none
  * @retval ot_int      Zero on success, Non-zero on Failure.
  * @ingroup M2QP
  * The incoming Protocol Frame must be present, in full, in the RX queue at the
  * time of calling.  pm2_parse_frame() does not check CRC, but it does perform
  * decryption in cases when the frame is encrypted.
ot_int m2qp_parse_frame(m2session* session);

/** @brief  Parses a datastream packet, processes the data in the case where the
  *         packet is error-free, and prepare an ACK response.
  * @param  none
  * @retval none
  * @ingroup M2QP
  * Run this in the system module when a datastream frame is received with errors.
void m2qp_parse_dspkt(m2session* session);

/** @brief  Marks a datastream frame as a broken frame, and handles stream processing
  * @param  none
  * @retval none
  * @ingroup M2QP
  * Run this in the system module when a datastream frame is received with errors.
void m2qp_mark_dsframe();

 * Protocol UDB Functions *

/** @brief Breaks down UDB Comparison Template, and runs the comparison
  * @param  is_series: (ot_u8) 0 is for ISF Comp, non-zero for ISFS Comp
  * @retval ot_int:     Score value for the comparison (see notes below)
  * @ingroup Protocol_Special
  * This is used to run the comparisions typical of anycast and multicast routed
  * requests.  It pulls the template from the RX Queue, and the Queue cursor 
  * should be set up on the first byte of the template.
  * The score value (output) is structured as follows:
  * - negative values:  An error has occurred.
  *                     The value returned is -1 * [Mode 2 Error Code]
  * - zero (0):         The comparison did not match (Score is 0)
  * - positive:         The comparison did match.  The specific value indicates
  *                     the score, the higher the better.  
  * @note Comparison scoring
  * In some comparisons, the score is just binary (pass/fail).  In others, there
  * is a real score.  So far, the only type of scoring is correlation, where the
  * maximum score is equal to the length in bytes of the compare token.  For
  * these types of comparisons, a threshold value is specified in the comparison
  * input data.  If the score is below threshold, it will be returned as 0.  If 
  * it is equal or higher, the actual score will be returned.
ot_int m2qp_isf_comp(ot_u8 is_series, id_tmpl* user_id);

/** @brief Breaks down ISF Call Template, and queues the ISF Return Template
  * @param  is_series: (ot_u8) 0 is for ISF Call, non-zero for ISFS Call
  * @retval ot_int:     Number of total data bytes in the called dataset
  * @ingroup Protocol_Special
  * Parses and manages a UDB Element or UDB List Call Template, writing to the 
  * TX queue the UDB Element or UDB List Return Template.
   * The score value (output) is structured as follows:
  * - negative values:  An error has occurred.
  *                     The value returned is -1 * [Mode 2 Error Code]
  * - non-negative:     The number of UDB data bytes in the called dataset.
  *                     This does not include UDB header bytes, which are 
  *                     2 bytes for each UDB element in the called dataset.
ot_int m2qp_isf_call( ot_u8 is_series, Queue* input_q, id_tmpl* user_id );

/** @brief Toolkit function for accessing UDB data, processing it, and returning a value
  * @param  is_series     (ot_u8)   0 is for UDB Element, non-zero for UDB List
  * @param  isf_id        (ot_int)  ID for the UDB Element or List
  * @param  offset        (ot_int)  Byte offset into the UDB dataset
  * @param  window_bytes  (ot_int)  Number of bytes, following offset, to process
  * @param  load_function (ot_int (*)(ot_int*, ot_u8) ) Processing function
  * @retval ot_int        A running sum of returns from the processing function
  * @ingroup Protocol_Special
  * @sa pm2_isf_comp()
  * @sa pm2_isf_call() 
  * @sa sub_load_charcorrelation()
  * @sa sub_load_comparison()
  * @sa sub_load_return()
  * In a nutshell, this function can do basically anything to any kind of UDB
  * dataset.  It is typically only used by pm2_isf_comp() or pm2_isf_call().
  * It is one of the cooler and more useful functions in OpenTag.
  * The processing function is a subroutine that takes in an index pointer (this
  * is issued by pm2_load_isf() ) and a byte of data.  It can do whatever it
  * wants to the data, and return whatever it wants.  There are currently three
  * subroutines used as processing functions for different tasks: 
  * sub_load_charcorrelation(), sub_load_comparison(), sub_load_return()
ot_int m2qp_load_isf(   ot_u8       is_series, 
                        ot_u8       isf_id, 
                        ot_int      offset, 
                        ot_int      window_bytes,
                        ot_int      (*load_function)(ot_int*, ot_u8),
                        id_tmpl*    user_id );

opentag/otlib/m2qp.txt · Last modified: 2012/03/12 00:57 by jpnorair