Indigresso Wiki

Open Source Stuff for DASH7

User Tools

Site Tools


opentag:otlib:mpipe

MPipe (Message Pipe)

“MPipe” is a message pipe interface that is used for client-server communication in OpenTag. In most cases, MPipe is a wireline link, such as SPI, UART, USB, etc. It could potentially be a wireless link, however. In any case, MPipe is designed to be a point-to-point link between two implicitly addressed devices. The protocol is extremely light, and it performs no addressing.

MPipe 1, MPipe 2

There are two versions of MPipe: 1 and 2. Version 1 is the original, NDEF-based protocol that came with all versions of OpenTag BEFORE v0.5. MPipe 2 is included in OpenTag v0.5. Likewise, OTcom versions BEFORE 3.x use MPipe 1, and starting with v3.x MPipe 2 is used.

MPipe Feature Overview

Versions 1 & 2

  • No explicit addressing
  • Uses a single ALP/NDEF record per packet
  • Max packet length = ALP/NDEF Overhead + MPipe Overhead + 255 byte payload = ~268 bytes
  • Contains CRC16 for data integrity
  • Contains a sequence ID for keeping data in order
  • Optional ability for ACK and NACK

Version 1 Only

  • Designed with fully in-place memory access in mind.
    • Header is shared with 6-byte NDEF “Type=Unknown” header
    • Additional control data is placed in a 4 byte footer
  • No extensibility beyond what is available in the applied subset of NDEF

Version 2 Only

  • Designed with energy optimization and client implementation in mind
    • Includes a wake-up and sync component to the header
    • New header is 8 bytes fixed length, and orthogonal to the ALP interior
    • No more footer
  • Requires translation for compatibility with formal NDEF specification, but full compatibility and interchange with DASH7 ALP.
  • Some extensibility available in header, for example to support crypto in the future.

At present, MPipe has been implemented over an RS-232 style UART (i.e. TTY or COM port) as well as a USB CDC ACM “virtual COM” port. When using USB, often USB will do some packet data integrity management of its own. In these implementations, you may consider removing the MPipe CRC if you are confident there is not possibility of malicious client that might try to send corrupted data by virtue of a USB overflow hack (vulnerability actually depends a lot on the USB implementation of the MCU). MPipe 2 has some design features that are intended to make it a lot less vulnerable to these sorts of hacks (or bugs!).

Data Integrity

MPipe 1 and 2 both use CRC16 and sequence IDs to allow receiving devices to determine if the incoming packet is erroneous. Data retransmissions can be actuated by an ACK/NACK method of notifying the sender.

  • Sequence Number is a 16 bit (v1) or 8 bit (v2) unsigned integer that counts-up whenever a new packet is generated. It does not count-up during ACK/NACKs or retransmissions.
  • CRC16 is a 16 bit CRC field. The actual CRC16 method is unspecified, but by convention the same CRC16 method used by DASH7 is used by MPipe.
  • An ACK is an MPipe packet with 0 payload, the same sequence number as the last-received packet, and ID (v1) or CONTROL (v2) set to 0.
  • A NACK is an MPipe packet with 0 payload, the same sequence number as the last-received packet, and ID set to non-zero (v1) or CONTROL set to 1 (v2).
  • ACKing/NACKing is not mandatory! For USB CDC ACM links, it is not helpful because USB already does its own ACKing at low level.

Version 1 Header (NDEF)

The NDEF header is implemented via the OTlib NDEF library, and thus it follows the normal requirements of OpenTag's stripped-down version of NDEF. In summary, OpenTag NDEF headers are always 6 bytes in length, and structured as below.

Message Flags Type Length Payload Length ID Length ID Payload MPipe Footer
1 Byte 1 Byte 1 Byte 1 Byte 2 Bytes N Bytes 4 Bytes
0xDD 0 N 2 ALP Ctrl Data Sequence+CRC
  • The “ALP Ctrl” data corresponds to a Protocol ID (1 Byte) and a Command Descriptor (1 Byte). For more information on this, check the ALP Module.
  • The short record length is always used. Therefore, the maximum size of an MPipe frame payload is 255 bytes. Also, 4 bytes must be included for the MPipe footer, making 265 bytes (6+255+4) the maximum size of a total MPipe frame. Therefore, a good minimum allocation for an MPipe Queue is 265 bytes, although custom implementations can be smaller as long as the client is aware.

Version 2 Header

Version 2 uses an orthogonal header, and the NDEF header from v1 is replaced with an interior ALP Header. The footer from v1 is now in the v2 header, leading to a somewhat unusual-looking implementation of CRC (the implementation is actually no less elegant).

Sync Word CRC16 Payload Length Sequence Control Payload
2 Bytes 2 Bytes 2 Bytes 1 Byte 1 Byte N Bytes
0xFF55 N 1 ALP Record
Low Power Wakeup

The first byte of the sync word, FF, is designed so that a serial link can be completely shut-down, and a line interrupt can be used to wakeup the system from deep sleep in time to turn on the serial subsystems and receive the rest of the packet.

Client-Friendly

Client implementations that search through unbounded sets of data can search for the FF55 sync word. This is especially helpful on Windows, which doesn't have any method for generating a new-received-data signal. POSIX can do that easily, but in any case it doesn't need to anymore.

CRC in Header

The CRC16 value is computed on the Payload Length, Sequence, Control, and Payload. Then it is deposited onto the header. To check the CRC of a received packet, simply do a block computation over the same data field and subtract it from the received CRC (It should yield 0).

Control

The control bytes is used by ACK and NACK, but in the near future it may also have some of its bits used to specify that cryptography is being used on the payload.

OTlib/mpipe.h

The only Mpipe file in OTlib is the Mpipe header file. It defines the common interface that all MPipe implementations must abide. The MPipe implementation itself is stored in the platform section, under the device that is implementing the platform (i.e. CC430, STM32F10x, etc). The code from mpipe.h is pasted below, although you can also check the doxygen code documentation.

#include "OT_types.h"
#include "OT_config.h"

///@todo when more hardware is supported by mpipe, variations of this will be
///      specified.  In certain implementations, this is superfluous
typedef enum {
    MPIPE_9600bps    = 0,
    MPIPE_28800bps   = 1,
    MPIPE_57600bps   = 2, 
    MPIPE_115200bps  = 3,
    MPIPE_230400bps  = 4,
    MPIPE_460800bps  = 5
} mpipe_speed;


///@note Priority might be altered in future implementations.
typedef enum {
    MPIPE_Low       = 0,
    MPIPE_High      = 1,
    MPIPE_Broadcast = 2,
    MPIPE_Ack       = 3
} mpipe_priority;

//Definitions to support legacy code (deprecated)
#define lo_priority ((mpipe_priority)0)
#define hi_priority ((mpipe_priority)1)


///@note States might be altered in future implementations.
typedef enum {
    MPIPE_Idle          = 0,
    MPIPE_RxHeader      = 1,
    MPIPE_RxPayload     = 2,
    MPIPE_TxAck_Wait    = 3,
    MPIPE_TxAck_Done    = 4,
    MPIPE_Tx_Wait       = 5,
    MPIPE_Tx_Done       = 6,
    MPIPE_RxAck         = 7,
    MPIPE_RxAckHeader   = 8
} mpipe_state;




/** @brief  Returns the number of bytes the MPipe needs for its footer
  * @param  None
  * @retval ot_u8       Number of bytes of the footer
  * @ingroup Mpipe
  *
  * All this does is return a constant.  It is here to prevent magic numbers
  * from being used, and to allow different MPipe implementations to have 
  * different footer sizes.
  */
ot_u8 mpipe_footerbytes();



/** @brief  Initializes Mpipe module (run at boot time)
  * @param  port_id     (void*) Implementation-dependent port identifier 
  * @retval ot_int      0 on success, negative on error
  * @ingroup Mpipe
  *
  * About port_id: on POSIX systems this will be a string representing a file
  * in the /dev/ directory somewhere (often /dev/tty...).  On embedded sytems,
  * it might be a pointer to a peripheral configuration register bank.  Check
  * with the Platform layer implementation -- in many cases it is unused and can
  * be ignored (set to NULL).
  */
ot_int mpipe_init(void* port_id);


/** @brief  Kills the Mpipe: not always implemented
  * @param  None 
  * @retval None
  * @ingroup Mpipe
  */
void mpipe_kill();



/** @brief  Provides manual blocking to MPIPE transfers
  * @param  None 
  * @retval None
  * @ingroup Mpipe
  *
  * Puts the MCU to sleep until the transfer is complete.  This is most useful
  * for preventing subsequent calls to MPIPE from interfering with each other.
  */
void mpipe_wait();



/** @brief  Sets the baud-rate of the mpipe.
  * @param  speed       (mpipe_speed) baud rate of the pipe
  * @retval None 
  * @ingroup Mpipe
  *
  * This function sets or resets data rate controlling attributes of the Mpipe.
  * In certain Mpipe implementations, data rate is irrelevant, and for these all
  * calls to mpipe_setspeed() will do the same thing.
  */
void mpipe_setspeed(mpipe_speed speed);



/** @brief  Returns the Mpipe state
  * @param  None
  * @retval (mpipe_state)   State enum
  * @ingroup Mpipe
  *
  * This is similar to a call to a mutex flag.  In Mpipe implementations that 
  * are blocking, this function will always return non-zero (because it can't be
  * called while Mpipe is active).  In Non-blocking implementations of Mpipe, it 
  * should be used to prevent usage of OpenTag features that depend on Mpipe 
  * (e.g. basically everything)
  */
mpipe_state mpipe_status();



/** @brief  Attaches a callback for TX done
  * @param  signal      (void (*)(ot_int))  Signal handler callback
  * @retval None
  * @ingroup Mpipe
  *
  * The signal handler prototype is of the POSIX specification.  Certain 
  * platform implementations may ignore the signal handler, but, generally, in
  * MCU implementations the Mpipe ISR function will call "signal(0)" before it
  * returns.
  * 
  * @note Only available when OT_FEATURE_MPIPE_CALLBACKS is ENABLED in OT_config.h
  */
void mpipe_setsig_txdone(void (*signal)(ot_int));



/** @brief  Attaches a callback for RX done
  * @param  signal      (void (*)(ot_int))  Signal handler callback
  * @retval None
  * @ingroup Mpipe
  *
  * The signal handler prototype is of the POSIX specification.  Certain
  * platform implementations may ignore the signal handler, but, generally, in
  * MCU implementations the Mpipe ISR function will call "signal(0)" before it
  * returns.
  * 
  * @note Only available when OT_FEATURE_MPIPE_CALLBACKS is ENABLED in OT_config.h
  */
void mpipe_setsig_rxdone(void (*signal)(ot_int));



/** @brief  Attaches a callback for RX detect
  * @param  signal      (void (*)(ot_int))  Signal handler callback
  * @retval None
  * @ingroup Mpipe
  *
  * The signal handler prototype is of the POSIX specification.  Certain
  * platform implementations may ignore the signal handler, but, generally, in
  * MCU implementations the Mpipe ISR function will call "signal(0)" before it
  * returns.
  * 
  * @note Only available when OT_FEATURE_MPIPE_CALLBACKS is ENABLED in OT_config.h
  */
void mpipe_setsig_rxdetect(void (*signal)(ot_int));



/** @brief  Transmits an NDEF structured datastream over the MPIPE
  * @param  data        (ot_u8*) Pointer to start of NDEF stream
  * @param  blocking    (ot_bool) True/False for blocking/non-blocking call
  * @param  data_priority (mpipe_priority) Priority of the TX
  * @retval ot_int      Negative on error, or number of bytes remaining for TX
  * @ingroup Mpipe
  * @sa mpipe_rxndef, mpipe_status
  *
  * The blocking parameter is sometimes unused or irrelevant, so check with the
  * documentation for the specific implementation.  When used, the blocking
  * parameter will guarantee data integrity by blocking resource use of any kind
  * during the data transfer.
  *
  * The data_priority parameter is also partially implementation dependent.  The
  * priority of an in-progress tx/rx transfer is saved in the module, so if a TX
  * is invoked while another transfer is underway, it will cancel the transfer
  * underway if its own priority is higher.
  *
  * The return value is the number of bytes remaining for TX.  If blocking is
  * used, therefore, it will always return 0 on success.  If blocking is not 
  * used, then the return value will be the total number of bytes to transmit,
  * which can vary depending on the MPIPE protocol.  If a negative value is
  * returned, it means that the priority of the planned transmission is not
  * higher than a tx/rx transfer currently underway, so the transmission has not
  * been invoked
  */
ot_int mpipe_txndef(ot_u8* data, ot_bool blocking, mpipe_priority data_priority);





/** @brief  Receives an NDEF structed datastream over the MPIPE
  * @param  data        (ot_u8*) Byte array to place received data
  * @param  blocking    (ot_bool) True/False for blocking/non-blocking call
  * @param  data_priority (mpipe_priority) Priority of the TX
  * @retval ot_int      error when negative
  * @ingroup Mpipe
  * @sa mpipe_txndef, mpipe_status
  *
  * The blocking parameter and data_priority parameters are dealt-with in the 
  * same way as they are with mpipe_txndef(), therefore the return value also
  * behaves in the same way (typically returning 0).
  */
ot_int mpipe_rxndef(ot_u8* data, ot_bool blocking, mpipe_priority data_priority);




/** @brief  ISR Mpipe subroutine for Mpipe inclusion into global ISR function
  * @param  None
  * @retval None
  * @ingroup Mpipe
  *
  * Check the Mpipe driver implementation for usage notes.  In cases where the
  * Mpipe resources are not included into the user's application, the Mpipe 
  * driver should manage everything related to this ISR transparently.  In cases
  * where the user app includes Mpipe resources for its own purposes, you will
  * need to call this ISR subroutine somewhere in that resource's ISR.
  *
  * Example: if the Mpipe is DMA-driven, and you use the DMA for more than just
  * the Mpipe, your outsourced DMA ISR should call this function as part of the
  * servicing of the DMA channel chosen for Mpipe.
  */
void mpipe_isr();
opentag/otlib/mpipe.txt · Last modified: 2013/11/15 02:16 by jpnorair