updated sbmp to the esp branch

master
Ondřej Hruška 8 years ago
parent 5d596af366
commit 0d399967fd
  1. 4
      .gitmodules
  2. 28
      esp_meas.pro
  3. 1
      sbmp
  4. 110
      sbmp-/README.md
  5. 144
      sbmp-/crc32.c
  6. 51
      sbmp-/crc32.h
  7. 25
      sbmp-/sbmp.h
  8. 105
      sbmp-/sbmp_checksum.c
  9. 39
      sbmp-/sbmp_checksum.h
  10. 53
      sbmp-/sbmp_config.h
  11. 69
      sbmp-/sbmp_datagram.c
  12. 71
      sbmp-/sbmp_datagram.h
  13. 442
      sbmp-/sbmp_frame.c
  14. 205
      sbmp-/sbmp_frame.h
  15. 15
      sbmp-/sbmp_logging.h
  16. 416
      sbmp-/sbmp_session.c
  17. 207
      sbmp-/sbmp_session.h

4
.gitmodules vendored

@ -0,0 +1,4 @@
[submodule "sbmp"]
path = sbmp
url = git@github.com:MightyPork/sbmp.git
branch = esp8266

@ -43,11 +43,12 @@ SOURCES += \
user/io.c \
user/user_main.c \
user/uart_driver.c \
sbmp/crc32.c \
sbmp/sbmp_checksum.c \
sbmp/sbmp_datagram.c \
sbmp/sbmp_frame.c \
sbmp/sbmp_session.c \
sbmp/library/crc32.c \
sbmp/library/sbmp_checksum.c \
sbmp/library/sbmp_datagram.c \
sbmp/library/sbmp_frame.c \
sbmp/library/sbmp_session.c \
sbmp/library/sbmp_bulk.c \
user/datalink.c \
user/serial.c \
user/uptime.c
@ -111,14 +112,15 @@ HEADERS += \
esp_iot_sdk_v1.5.2/include/upgrade.h \
esp_iot_sdk_v1.5.2/include/user_interface.h \
user/uart_driver.h \
sbmp/crc32.h \
sbmp/sbmp.h \
sbmp/sbmp_checksum.h \
sbmp/sbmp_config.h \
sbmp/sbmp_datagram.h \
sbmp/sbmp_frame.h \
sbmp/sbmp_logging.h \
sbmp/sbmp_session.h \
sbmp/library/crc32.h \
sbmp/library/sbmp.h \
sbmp/library/sbmp_checksum.h \
sbmp/library/sbmp_config.h \
sbmp/library/sbmp_datagram.h \
sbmp/library/sbmp_frame.h \
sbmp/library/sbmp_logging.h \
sbmp/library/sbmp_session.h \
sbmp/library/sbmp_bulk.h \
user/datalink.h \
user/serial.h \
libesphttpd/include/logging.h \

@ -0,0 +1 @@
Subproject commit 5548d820959ddba33ffb7d2ff76a3ee8d95e1be6

@ -1,110 +0,0 @@
SBMP library, v1.3
==================
This is a reference implementation of SBMP, which should be usable in embedded
environment.
The library cosists of a **Framing layer**, **Datagram middleware** and a **Session layer**.
How to use
----------
The framing layer is isolated in the `sbmp_frame` module, and can be used on it's own,
if sessions are not needed. That gives you reliable data transfer with error detection.
If you want to *get the most out of SBMP*, use the session layer. Session layer functions are
namespaced `sbmp_ep_` ("ep" stands for endpoint).
All header files are included in `sbmp.h`, which is the only file you should
`#include` in your application.
Read comments in the examples to see how to use the library.
Configuration & porting
-----------------------
You can customize a lot of aspects of SBMP in `sbmp_config.h`.
If you included the library as a submodule, and want to avoid manual edits, you can set
the options using compiler flags (eg. to disable CRC32: `-DSBMP_HAS_CRC32=0`).
**What to change for Arduino**
Basically disable all you can in the config file.
- You don't want `malloc()`
- Logging is useless if there's only one USART anyway
- CRC32 can be replaced with XOR if you don't care about reliability that much.
This gains you a huge size reduction, and the whole library will take only around
2.5 kB flash and 150 B ram (of course, not including your Rx buffer).
It works really well for ATmega328P - the usual Arduino chip.
Example for AVR
---------------
This example uses the [AVR C boilerplate](https://github.com/MightyPork/avr-c-boilerplate).
It's here just to show how to set up SBMP in your AVR application.
**NOTE:** This example uses static allocation of the struct and buffer.
If you pass NULLs to the init function, it will `malloc()` it for you
and return a pointer to the Endpoint.
```c
#include <avr/io.h> // register definitions
#include <avr/interrupt.h> // interrupt vectors
#include <stdint.h>
#include <stdbool.h>
// AVR C boilerplate libs
#include "lib/iopins.h"
#include "lib/usart.h"
// SBMP
#include "sbmp/sbmp.h"
// SBMP globals
#define RXBUF_LEN 128
static uint8_t rxbuf[RXBUF_LEN];
static SBMP_Endpoint ep;
/** USART receive handler */
ISR(USART_RX_vect)
{
// Get the byte and pass it to SBMP
uint8_t b = usart_rx();
sbmp_ep_receive(&ep, b); // WARN: This can fail. Should check retval.
}
/** Message received from SBMP */
void message_received(SBMP_Datagram *dg)
{
// ...
}
int main()
{
usart_init(BAUD_115200);
usart_isr_rx_enable(true); // enable the interrupt
// init SBMP
sbmp_ep_init(&ep, rxbuf, RXBUF_LEN, message_received, usart_tx);
// ...additional SBMP configuration if needed...
// enable rx, tx
sbmp_ep_enable(&ep, true);
// globally enable interrupts (for the USART_RX handler)
sei();
while (1) {
// ... do stuff
}
}
```

@ -1,144 +0,0 @@
#include "esp8266.h"
#include "crc32.h"
#include "sbmp_config.h"
#if SBMP_HAS_CRC32
// This file was downloaded from some forum, and modified a bit.
/**********************************************************************\
|* Demonstration program to compute the 32-bit CRC used as the frame *|
|* check sequence in ADCCP (ANSI X3.66, also known as FIPS PUB 71 *|
|* and FED-STD-1003, the U.S. versions of CCITT's X.25 link-level *|
|* protocol). The 32-bit FCS was added via the Federal Register, *|
|* 1 June 1982, p.23798. I presume but don't know for certain that *|
|* this polynomial is or will be included in CCITT V.41, which *|
|* defines the 16-bit CRC (often called CRC-CCITT) polynomial. FIPS *|
|* PUB 78 says that the 32-bit FCS reduces otherwise undetected *|
|* errors by a factor of 10^-5 over 16-bit FCS. *|
\**********************************************************************/
/* Copyright (C) 1986 Gary S. Brown. You may use this program, or
code or tables extracted from it, as desired without restriction.*/
/* First, the polynomial itself and its table of feedback terms. The */
/* polynomial is */
/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */
/* Note that we take it "backwards" and put the highest-order term in */
/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */
/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */
/* the MSB being 1. */
/* Note that the usual hardware shift register implementation, which */
/* is what we're using (we're merely optimizing it by doing eight-bit */
/* chunks at a time) shifts bits into the lowest-order term. In our */
/* implementation, that means shifting towards the right. Why do we */
/* do it this way? Because the calculated CRC must be transmitted in */
/* order from highest-order term to lowest-order term. UARTs transmit */
/* characters in order from LSB to MSB. By storing the CRC this way, */
/* we hand it to the UART in the order low-byte to high-byte; the UART */
/* sends each low-bit to hight-bit; and the result is transmission bit */
/* by bit from highest- to lowest-order term without requiring any bit */
/* shuffling on our part. Reception works similarly. */
/* The feedback terms table consists of 256, 32-bit entries. Notes: */
/* */
/* 1. The table can be generated at runtime if desired; code to do so */
/* is shown later. It might not be obvious, but the feedback */
/* terms simply represent the results of eight shift/xor opera- */
/* tions for all combinations of data and CRC register values. */
/* */
/* 2. The CRC accumulation logic is the same for all CRC polynomials, */
/* be they sixteen or thirty-two bits wide. You simply choose the */
/* appropriate table. Alternatively, because the table can be */
/* generated at runtime, you can start by generating the table for */
/* the polynomial in question and use exactly the same "updcrc", */
/* if your application needn't simultaneously handle two CRC */
/* polynomials. (Note, however, that XMODEM is strange.) */
/* */
/* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */
/* of course, 32-bit entries work OK if the high 16 bits are zero. */
/* */
/* 4. The values must be right-shifted by eight bits by the "updcrc" */
/* logic; the shift must be unsigned (bring in zeroes). On some */
/* hardware you could probably optimize the shift in assembler by */
/* using byte-swap instructions. */
static uint32_t FLASH_DATA crc_32_tab[] = { /* CRC polynomial 0xedb88320 */
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
#define UPDC32(octet, crc) (crc_32_tab[((crc) ^ ((uint8_t)octet)) & 0xff] ^ ((crc) >> 8))
uint32_t FLASH_FN crc32_begin(void)
{
return 0xFFFFFFFF;
}
uint32_t FLASH_FN crc32_update(uint32_t crc_scratch, uint8_t ch)
{
return UPDC32(ch, crc_scratch);
}
uint32_t FLASH_FN crc32_end(uint32_t crc_scratch)
{
return ~crc_scratch;
}
uint32_t FLASH_FN crc32buf(uint8_t *buf, size_t len)
{
uint32_t scratch = crc32_begin();
for (; len; --len, ++buf) {
scratch = crc32_update(scratch, *buf);
}
return crc32_end(scratch);
}
#endif /* SBMP_HAS_CRC32 */

@ -1,51 +0,0 @@
#ifndef CRC32_H
#define CRC32_H
#include "sbmp_config.h"
#if SBMP_HAS_CRC32
/**
* This module is used by the framing layer to calculate CRC32.
*
* If your hardware provides a hardware checksum calculator,
* you can modify the .c file to use that instead of
* doing it in software.
*/
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
/**
* @brief Calculate CRC32 of a buffer.
* @param buf : buffer to checksum
* @param len : buffer size
* @return the CRC32 checksum
*/
uint32_t crc32buf(uint8_t *buf, size_t len);
/**
* @brief Start calculating a checksum of a block of data.
* @return the scratch value, for use in the update and end functions.
*/
uint32_t crc32_begin(void);
/**
* @brief Update the CRC32 scratch value with a byte of the data block.
* @param crc_scratch : old scratch value pointer.
* @param b : received byte
* @return updated scratch
*/
uint32_t crc32_update(uint32_t crc_scratch, uint8_t b);
/**
* @brief Finish the CRC calculation.
* @param crc_scratch : your scratch buffer.
* @return the final CRC32 value.
*/
uint32_t crc32_end(uint32_t crc_scratch);
#endif /* SBMP_HAS_CRC32 */
#endif /* CRC32_H */

@ -1,25 +0,0 @@
#ifndef SBMP_H
#define SBMP_H
/**
* This is the main SBMP header file.
*
* This is the only header file you should need to
* #include in your application code.
*/
#define SBMP_VER "1.3"
#include "sbmp_config.h"
// Common utils & the frame parser
#include "sbmp_logging.h"
#include "sbmp_checksum.h"
#include "sbmp_frame.h"
// Datagram & session layer
#include "sbmp_datagram.h"
#include "sbmp_session.h"
#endif /* SBMP_H */

@ -1,105 +0,0 @@
#include "esp8266.h"
#include <stdbool.h>
#include "sbmp_config.h"
#include "sbmp_checksum.h"
#if SBMP_HAS_CRC32
#include "crc32.h"
#endif
/** Get nr of bytes in a checksum */
uint8_t FLASH_FN chksum_length(SBMP_CksumType cksum_type)
{
switch (cksum_type) {
case SBMP_CKSUM_CRC32: return 4;
case SBMP_CKSUM_XOR: return 1;
case SBMP_CKSUM_NONE: return 0;
default:
return 4; // assume all unknown checksums are 4 bytes long
}
}
/** Start calculating a checksum */
void FLASH_FN cksum_begin(SBMP_CksumType type, uint32_t *scratch)
{
switch (type) {
#if SBMP_HAS_CRC32
case SBMP_CKSUM_CRC32:
*scratch = crc32_begin();
break;
#endif
case SBMP_CKSUM_XOR:
*scratch = 0;
break;
case SBMP_CKSUM_NONE: // fall-through
default:
;
}
}
/** Update the checksum calculation with an incoming byte */
void FLASH_FN cksum_update(SBMP_CksumType type, uint32_t *scratch, uint8_t byte)
{
switch (type) {
#if SBMP_HAS_CRC32
case SBMP_CKSUM_CRC32:
*scratch = crc32_update(*scratch, byte);
break;
#endif
case SBMP_CKSUM_XOR:
*scratch ^= byte;
break;
case SBMP_CKSUM_NONE: // fall-through
default:
;
}
}
/** Stop the checksum calculation, get the result */
void FLASH_FN cksum_end(SBMP_CksumType type, uint32_t *scratch)
{
switch (type) {
#if SBMP_HAS_CRC32
case SBMP_CKSUM_CRC32:
*scratch = crc32_end(*scratch);
#endif
case SBMP_CKSUM_XOR:
// scratch already contains the checksum
break;
case SBMP_CKSUM_NONE: // fall-through
default:
*scratch = 0;
break;
}
}
/** Check if the calculated checksum matches the received one */
bool FLASH_FN cksum_verify(SBMP_CksumType type, uint32_t *scratch, uint32_t received_cksum)
{
cksum_end(type, scratch);
// scratch now contains the checksum
switch (type) {
#if SBMP_HAS_CRC32
case SBMP_CKSUM_CRC32: // fall-through
#endif
case SBMP_CKSUM_XOR:
return (*scratch == received_cksum);
case SBMP_CKSUM_NONE: // fall-through
default:
// unknown checksum type
return true; // assume it's OK
}
}

@ -1,39 +0,0 @@
#ifndef SBMP_CHECKSUM_H
#define SBMP_CHECKSUM_H
/**
* Checksum functions for the SBMP framing layer.
*/
#include <stdint.h>
#include <stdbool.h>
#include "sbmp_config.h"
/** Checksum types */
typedef enum {
SBMP_CKSUM_NONE = 0, /*!< No checksum */
SBMP_CKSUM_CRC32 = 32, /*!< ISO CRC-32 */
SBMP_CKSUM_XOR = 1, /*!< Simple XOR check, good for small micros (Arduino) */
} SBMP_CksumType;
/** Get nr of bytes in a checksum */
uint8_t chksum_length(SBMP_CksumType cksum_type);
/** Start calculating a checksum. Updates scratch. */
void cksum_begin(SBMP_CksumType type, uint32_t *scratch);
/** Update the checksum calculation with an incoming byte. Updates scratch. */
void cksum_update(SBMP_CksumType type, uint32_t *scratch, uint8_t byte);
/** Stop the checksum calculation, get the result */
void cksum_end(SBMP_CksumType type, uint32_t *scratch);
/** Check if the calculated checksum matches the received one */
bool cksum_verify(SBMP_CksumType type, uint32_t *scratch, uint32_t received_cksum);
#endif /* SBMP_CHECKSUM_H */

@ -1,53 +0,0 @@
#ifndef SBMP_CONFIG_H
#define SBMP_CONFIG_H
/* --- Configuration ------------------- */
#ifndef SBMP_LOGGING
/**
* @brief Enable logging.
*
* Logging functions are WEAK stubs in sbmp_logging.
*
* Disable logging to free up memory taken by the messages.
*/
#define SBMP_LOGGING 1
#endif
#ifndef SBMP_MALLOC
/**
* @brief Enable malloc if NULL is passed.
*
* This lets you malloc() the struct / buffer if you pass NULL
* to the init functions.
*
* Disable malloc to free up memory taken by the malloc routine.
* If disabled, init funcs will return NULL if NULL is passed
* as argument.
*/
#define SBMP_MALLOC 1
#endif
#ifndef SBMP_HAS_CRC32
/**
* @brief Add support for CRC32
*
* Disabling CRC32 will reduce program size (for small micros).
* If CRC32 is disabled, XOR will be used as the preferred checksum
* method.
*
* Received CRC32'd messages will be accepted without checking.
*
* If handshake is used, the peer will detect that CRC32 is not
* supported here, and should start using XOR.
*/
#define SBMP_HAS_CRC32 1
#endif
/* ------------------------------------- */
#endif // SBMP_CONFIG_H

@ -1,69 +0,0 @@
#include "esp8266.h"
#include "sbmp_config.h"
#include "sbmp_logging.h"
#include "sbmp_datagram.h"
SBMP_Datagram FLASH_FN *sbmp_dg_parse(SBMP_Datagram *dg, const uint8_t *payload, uint16_t length)
{
if (length < 3) {
sbmp_error("Can't parse datagram, payload too short.");
return NULL; // shorter than the minimal no-payload datagram
}
// S.N. (2 B) | Dg Type (1 B) | Payload
#if SBMP_MALLOC
if (dg == NULL) {
// request to allocate
dg = malloc(sizeof(SBMP_Datagram));
}
#else
if (dg == NULL) {
return NULL; // fail
}
#endif
dg->session = (uint16_t)((payload[0]) | (payload[1] << 8));
dg->type = payload[2];
dg->length = length - 3;
dg->payload = payload + 3; // pointer arith
return dg;
}
/** Start a datagram transmission */
bool FLASH_FN sbmp_dg_start(SBMP_FrmInst *frm, SBMP_CksumType cksum_type, uint16_t session, SBMP_DgType type, uint16_t length)
{
if (length > (0xFFFF - 3)) {
sbmp_error("Can't send a datagram, payload too long.");
return false;
}
if (frm->tx_status != FRM_STATE_IDLE) {
sbmp_error("Can't state datagram, SBMP tx not IDLE.");
return false;
}
if (! sbmp_frm_start(frm, cksum_type, length + 3)) return false;
sbmp_frm_send_byte(frm, session & 0xFF);
sbmp_frm_send_byte(frm, (session >> 8) & 0xFF);
sbmp_frm_send_byte(frm, type);
return true;
}
/** Send a whole datagram in one go */
bool FLASH_FN sbmp_dg_send(SBMP_FrmInst *frm, SBMP_CksumType cksum_type, SBMP_Datagram *dg)
{
if (! sbmp_dg_start(frm, cksum_type, dg->session, dg->type, dg->length)) {
sbmp_error("Failed to start datagram.");
return false;
}
size_t n = sbmp_frm_send_buffer(frm, dg->payload, dg->length);
return (n == dg->length);
}

@ -1,71 +0,0 @@
#ifndef SBMP_DATAGRAM_H
#define SBMP_DATAGRAM_H
/**
* SBMP datagram layer
*
* This layer is coupled to the framing layer, and
* builds a datagram abstraction on top of it.
*
* This layer sits right under the session layer,
* and is not very useful on it's own.
*/
#include <stdint.h>
#include "sbmp_config.h"
#include "sbmp_frame.h"
typedef uint8_t SBMP_DgType;
#define SBMP_DG_HSK_START 0x00
#define SBMP_DG_HSK_ACCEPT 0x01
#define SBMP_DG_HSK_CONFLICT 0x02
/**
* SBMP datagram object.
*/
typedef struct {
const uint8_t *payload; /*!< Datagram payload */
SBMP_DgType type; /*!< Datagram type ID */
uint16_t length; /*!< Datagram length (bytes) */
uint16_t session; /*!< Datagram session number */
} SBMP_Datagram;
/**
* @brief Convert a received buffer payload to a datagram.
*
* If the payload is < 3 bytes long, datagram can't be createdn and NULL
* is returned instead. The caller should then free the payload buffer.
*
* @param dg : datagram variable to populate, or NULL to allocate
* @param frame_payload : frame payload to parse
* @param length : frame payload length
* @return datagram (allocated if dg was NULL), or NULL if parsing failed.
*/
SBMP_Datagram *sbmp_dg_parse(SBMP_Datagram *dg_or_null, const uint8_t *frame_payload, uint16_t length);
/**
* @brief Start a datagram (and the frame)
*
* @param state : SBMP state struct
* @param cksum_type : Checksum type to use for the frame; 0 - none, 32 - CRC32
* @param session : session number
* @param type : Datagram type ID
* @param length : Datagram payload length (bytes)
* @return success
*/
bool sbmp_dg_start(SBMP_FrmInst *frm, SBMP_CksumType cksum_type, uint16_t session, SBMP_DgType type, uint16_t length);
/**
* @brief Send a complete prepared datagram, also starts the frame.
*
* @param state : SBMP state struct
* @param cksum_type : Checksum type to use for the frame; 0 - none, 32 - CRC32
* @param dg : Datagram struct containing DG settings and the payload.
* @return success
*/
bool sbmp_dg_send(SBMP_FrmInst *frm, SBMP_CksumType cksum_type, SBMP_Datagram *dg);
#endif /* SBMP_DATAGRAM_H */

@ -1,442 +0,0 @@
#include "esp8266.h"
#include <stdarg.h>
#include "sbmp_config.h"
#include "sbmp_logging.h"
#include "sbmp_checksum.h"
#include "sbmp_frame.h"
// protos
static void call_frame_rx_callback(SBMP_FrmInst *frm);
/** Allocate the state struct & init all fields */
SBMP_FrmInst FLASH_FN *sbmp_frm_init(
SBMP_FrmInst *frm,
uint8_t *buffer,
uint16_t buffer_size,
void (*rx_handler)(uint8_t *, uint16_t, void *),
void (*tx_func)(uint8_t))
{
#if SBMP_MALLOC
if (frm == NULL) {
// caller wants us to allocate it
frm = malloc(sizeof(SBMP_FrmInst));
}
if (buffer == NULL) {
// caller wants us to allocate it
buffer = malloc(buffer_size);
}
#else
if (frm == NULL || buffer == NULL) {
return NULL; // malloc not enabled, fail
}
#endif
frm->rx_handler = rx_handler;
frm->rx_buffer = buffer;
frm->rx_buffer_cap = buffer_size;
frm->user_token = NULL; // NULL if not set
frm->tx_func = tx_func;
frm->rx_enabled = false;
frm->tx_enabled = false;
sbmp_frm_reset(frm);
return frm;
}
/** Reset the internal state */
void FLASH_FN sbmp_frm_reset(SBMP_FrmInst *frm)
{
sbmp_frm_reset_rx(frm);
sbmp_frm_reset_tx(frm);
}
/** Enable or disable Rx */
void FLASH_FN sbmp_frm_enable_rx(SBMP_FrmInst *frm, bool enable)
{
frm->rx_enabled = enable;
}
/** Enable or disable Tx */
void FLASH_FN sbmp_frm_enable_tx(SBMP_FrmInst *frm, bool enable)
{
frm->tx_enabled = enable;
}
/** Enable or disable both Rx and Tx */
void FLASH_FN sbmp_frm_enable(SBMP_FrmInst *frm, bool enable)
{
sbmp_frm_enable_rx(frm, enable);
sbmp_frm_enable_tx(frm, enable);
}
/** Set user token */
void FLASH_FN sbmp_frm_set_user_token(SBMP_FrmInst *frm, void *token)
{
frm->user_token = token;
}
/** Reset the receiver state */
void FLASH_FN sbmp_frm_reset_rx(SBMP_FrmInst *frm)
{
frm->rx_buffer_i = 0;
frm->rx_length = 0;
frm->mb_buf = 0;
frm->mb_cnt = 0;
frm->rx_hdr_xor = 0;
frm->rx_cksum_scratch = 0;
frm->rx_cksum_type = SBMP_CKSUM_NONE;
frm->rx_status = FRM_STATE_IDLE;
// printf("---- RX RESET STATE ----\n");
}
/** Reset the transmitter state */
void FLASH_FN sbmp_frm_reset_tx(SBMP_FrmInst *frm)
{
frm->tx_status = FRM_STATE_IDLE;
frm->tx_remain = 0;
frm->tx_cksum_scratch = 0;
frm->tx_cksum_type = SBMP_CKSUM_NONE;
// printf("---- TX RESET STATE ----\n");
}
/** Update a header XOR */
static inline FLASH_FN
void hdrxor_update(SBMP_FrmInst *frm, uint8_t rxbyte)
{
frm->rx_hdr_xor ^= rxbyte;
}
/** Check header xor against received value */
static inline FLASH_FN
bool hdrxor_verify(SBMP_FrmInst *frm, uint8_t rx_xor)
{
return frm->rx_hdr_xor == rx_xor;
}
/** Append a byte to the rx buffer */
static inline FLASH_FN
void append_rx_byte(SBMP_FrmInst *frm, uint8_t b)
{
frm->rx_buffer[frm->rx_buffer_i++] = b;
}
/** Set n-th byte (0 = LSM) to a value */
static inline
void FLASH_FN set_byte(uint32_t *acc, uint8_t pos, uint8_t byte)
{
*acc |= (uint32_t)(byte << (pos * 8));
}
/**
* Call the message handler with the payload.
*
*/
static void FLASH_FN call_frame_rx_callback(SBMP_FrmInst *frm)
{
if (frm->rx_handler == NULL) {
sbmp_error("frame_handler is null!");
return;
}
frm->rx_status = FRM_STATE_WAIT_HANDLER;
frm->rx_handler(frm->rx_buffer, frm->rx_length, frm->user_token);
}
/**
* @brief Receive a byte
*
* SOF 8 | CKSUM_TYPE 8 | LEN 16 | PAYLOAD | CKSUM 0/4
*
* @param state
* @param rxbyte
* @return status
*/
SBMP_RxStatus FLASH_FN sbmp_frm_receive(SBMP_FrmInst *frm, uint8_t rxbyte)
{
if (! frm->rx_enabled) {
return SBMP_RX_DISABLED;
}
SBMP_RxStatus retval = SBMP_RX_OK;
switch (frm->rx_status) {
case FRM_STATE_WAIT_HANDLER:
retval = SBMP_RX_BUSY;
break;
case FRM_STATE_IDLE:
// can be first byte of a packet
if (rxbyte == 0x01) { // start byte
hdrxor_update(frm, rxbyte);
frm->rx_status = FRM_STATE_CKSUM_TYPE;
} else {
// bad char
retval = SBMP_RX_INVALID;
}
break;
case FRM_STATE_CKSUM_TYPE:
frm->rx_cksum_type = rxbyte; // checksum type received
hdrxor_update(frm, rxbyte);
// next will be length
frm->rx_status = FRM_STATE_LENGTH;
// clear MB for 2-byte length
frm->mb_buf = 0;
frm->mb_cnt = 0;
break;
case FRM_STATE_LENGTH:
// append to the multi-byte buffer
set_byte(&frm->mb_buf, frm->mb_cnt++, rxbyte);
hdrxor_update(frm, rxbyte);
// if last of the MB field
if (frm->mb_cnt == 2) {
// next will be the payload
uint16_t len = (uint16_t) frm->mb_buf;
frm->rx_length = len;
if (len == 0) {
sbmp_error("Rx packet with no payload!");
sbmp_frm_reset_rx(frm); // abort
break;
}
frm->rx_status = FRM_STATE_HDRXOR;
}
break;
case FRM_STATE_HDRXOR:
if (! hdrxor_verify(frm, rxbyte)) {
sbmp_error("Header XOR mismatch!");
sbmp_frm_reset_rx(frm); // abort
break;
}
// Check if not too long
if (frm->rx_length > frm->rx_buffer_cap) {
sbmp_error("Rx packet too long - %d!", (uint16_t)frm->rx_length);
// discard the rest + checksum
frm->rx_status = FRM_STATE_DISCARD;
frm->rx_length += chksum_length(frm->rx_cksum_type);
break;
}
frm->rx_status = FRM_STATE_PAYLOAD;
cksum_begin(frm->rx_cksum_type, &frm->rx_cksum_scratch);
break;
case FRM_STATE_DISCARD:
frm->rx_buffer_i++;
if (frm->rx_buffer_i == frm->rx_length) {
// done
sbmp_frm_reset_rx(frm); // go IDLE
}
break;
case FRM_STATE_PAYLOAD:
append_rx_byte(frm, rxbyte);
cksum_update(frm->rx_cksum_type, &frm->rx_cksum_scratch, rxbyte);
if (frm->rx_buffer_i == frm->rx_length) {
// payload rx complete
if (frm->rx_cksum_type != SBMP_CKSUM_NONE) {
// receive the checksum
frm->rx_status = FRM_STATE_CKSUM;
// clear MB for the 4-byte length
frm->mb_buf = 0;
frm->mb_cnt = 0;
} else {
// no checksum
// fire the callback
call_frame_rx_callback(frm);
// clear
sbmp_frm_reset_rx(frm);
}
}
break;
case FRM_STATE_CKSUM:
// append to the multi-byte buffer
set_byte(&frm->mb_buf, frm->mb_cnt++, rxbyte);
// if last of the MB field
if (frm->mb_cnt == chksum_length(frm->rx_cksum_type)) {
if (cksum_verify(frm->rx_cksum_type, &frm->rx_cksum_scratch, frm->mb_buf)) {
call_frame_rx_callback(frm);
} else {
sbmp_error("Rx checksum mismatch!");
}
// clear, enter IDLE
sbmp_frm_reset_rx(frm);
}
break;
}
return retval;
}
/** Send a frame header */
bool FLASH_FN sbmp_frm_start(SBMP_FrmInst *frm, SBMP_CksumType cksum_type, uint16_t length)
{
if (! frm->tx_enabled) {
sbmp_error("Can't tx, not enabled.");
return false;
}
if (frm->tx_status != FRM_STATE_IDLE) {
sbmp_error("Can't tx, busy.");
return false;
}
if (frm->tx_func == NULL) {
sbmp_error("Can't tx, no tx func!");
return false;
}
if (cksum_type == SBMP_CKSUM_CRC32 && !SBMP_HAS_CRC32) {
sbmp_error("CRC32 disabled, using XOR for Tx.");
cksum_type = SBMP_CKSUM_XOR;
}
sbmp_frm_reset_tx(frm);
frm->tx_cksum_type = cksum_type;
frm->tx_remain = length;
frm->tx_status = FRM_STATE_PAYLOAD;
// Send the header
uint16_t len = (uint16_t) length;
uint8_t hdr[4] = {
0x01,
cksum_type,
len & 0xFF,
(len >> 8) & 0xFF
};
uint8_t hdr_xor = 0;
for (int i = 0; i < 4; i++) {
hdr_xor ^= hdr[i];
frm->tx_func(hdr[i]);
}
frm->tx_func(hdr_xor);
cksum_begin(frm->tx_cksum_type, &frm->tx_cksum_scratch);
return true;
}
/** End frame and enter idle mode */
static void FLASH_FN end_frame(SBMP_FrmInst *frm)
{
if (!frm->tx_enabled) {
sbmp_error("Can't tx, not enabled.");
return;
}
cksum_end(frm->tx_cksum_type, &frm->tx_cksum_scratch);
uint32_t cksum = frm->tx_cksum_scratch;
switch (frm->tx_cksum_type) {
case SBMP_CKSUM_NONE:
break; // do nothing
case SBMP_CKSUM_XOR:
// 1-byte checksum
frm->tx_func(cksum & 0xFF);
break;
case SBMP_CKSUM_CRC32:
frm->tx_func(cksum & 0xFF);
frm->tx_func((cksum >> 8) & 0xFF);
frm->tx_func((cksum >> 16) & 0xFF);
frm->tx_func((cksum >> 24) & 0xFF);
}
frm->tx_status = FRM_STATE_IDLE; // tx done
}
/** Send a byte in the currently open frame */
bool FLASH_FN sbmp_frm_send_byte(SBMP_FrmInst *frm, uint8_t byte)
{
if (!frm->tx_enabled) {
sbmp_error("Can't tx, not enabled.");
return false;
}
if (frm->tx_remain == 0 || frm->tx_status != FRM_STATE_PAYLOAD) {
return false;
}
frm->tx_func(byte);
cksum_update(frm->tx_cksum_type, &frm->tx_cksum_scratch, byte);
frm->tx_remain--;
if (frm->tx_remain == 0) {
end_frame(frm); // checksum & go idle
}
return true;
}
/** Send a buffer in the currently open frame */
uint16_t FLASH_FN sbmp_frm_send_buffer(SBMP_FrmInst *frm, const uint8_t *buffer, uint16_t length)
{
if (! frm->tx_enabled) {
sbmp_error("Can't tx, not enabled.");
return false;
}
if (frm->tx_status != FRM_STATE_PAYLOAD) {
return false; // invalid call
}
if (length == 0) {
end_frame(frm); // checksum & go idle
return 0;
}
if (frm->tx_remain == 0) {
return false; // write past EOF (this shouldn't happen)
}
uint16_t remain = length;
while (frm->tx_status == FRM_STATE_PAYLOAD && remain-- > 0) {
if (! sbmp_frm_send_byte(frm, *buffer++)) {
remain++; // "push back"
break;
}
}
return (length - remain);
}

@ -1,205 +0,0 @@
#ifndef SBMP_FRAME_H
#define SBMP_FRAME_H
/**
* SBMP Frame parser.
*
* This is the lowest level of the SBMP stack.
*
* You can use the frame parser on it's own, if you don't the higher layers.
* You will still get checksums and other benefits.
*/
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "sbmp_config.h"
#include "sbmp_checksum.h"
/**
* Status returned from the byte rx function.
*/
typedef enum {
SBMP_RX_OK = 0, /*!< Byte received */
SBMP_RX_BUSY, /*!< SBMP busy with the last packet, try again */
SBMP_RX_INVALID, /*!< The byte was rejected - can be ASCII debug, or just garbage. */
SBMP_RX_DISABLED, /*!< The byte was rejected, because the frame parser is not enabled yet. */
} SBMP_RxStatus;
/** SBMP internal state (context). Allows having multiple SBMP interfaces. */
typedef struct SBMP_FrmInstance_struct SBMP_FrmInst;
/**
* @brief Initialize the SBMP internal state struct
*
* @param frm : Pointer to the state struct to populate. NULL to allocate.
* @param buffer : Buffer for Rx. NULL to allocate.
* @param buffer_size : size of the Rx buffer (or how many bytes to allocate)
* @param rx_handler : callback when frame is received
* @param tx_func : function for sending bytes (USART Tx)
* @return pointer to the state struct (passed or allocated)
*/
SBMP_FrmInst *sbmp_frm_init(
SBMP_FrmInst *frm_or_null,
uint8_t *buffer_or_null,
uint16_t buffer_size,
void (*rx_handler)(uint8_t *payload, uint16_t length, void *user_token),
void (*tx_func)(uint8_t byte)
);
/**
* @brief Set the user token value.
*
* The token will be passed to the Frame Rx callback.
* Can be used to identify higher level layer instance.
*
* If the token is not set, NULL will be used in the callback.
*
* @param frm : Framing layer instance
* @param token : pointer to arbitrary object.
*/
void sbmp_frm_set_user_token(SBMP_FrmInst *frm, void *token);
/**
* @brief Reset the SBMP frm state, discard partial messages (both rx and tx).
* @param frm : Framing layer instance
*/
void sbmp_frm_reset(SBMP_FrmInst *frm);
/** Reset the receiver state only */
void sbmp_frm_reset_rx(SBMP_FrmInst *frm);
/** Reset the transmitter state only */
void sbmp_frm_reset_tx(SBMP_FrmInst *frm);
/**
* @brief Enable or disable both Rx and Tx
* @param frm : Framing layer instance
* @param enable : true - enable, false - disable
*/
void sbmp_frm_enable(SBMP_FrmInst *frm, bool enable);
/**
* @brief Enable or disable Rx & the frame parser
* @param frm : Framing layer instance
* @param enable_rx : true - enable, false - disable
*/
void sbmp_frm_enable_rx(SBMP_FrmInst *frm, bool enable_rx);
/**
* @brief Enable or disable the frame builder & Tx
* @param frm : Framing layer instance
* @param enable_tx : true - enable, false - disable
*/
void sbmp_frm_enable_tx(SBMP_FrmInst *frm, bool enable_tx);
/**
* @brief Handle an incoming byte
*
* @param frm : Framing layer instance
* @param rxbyte : byte received
* @return status
*/
SBMP_RxStatus sbmp_frm_receive(SBMP_FrmInst *frm, uint8_t rxbyte);
/**
* @brief Start a frame transmission
*
* @param frm : Framing layer instance
* @param cksum_type : checksum to use (0, 32)
* @param length : payload length
* @return true if frame was started.
*/
bool sbmp_frm_start(SBMP_FrmInst *frm, SBMP_CksumType cksum_type, uint16_t length);
/**
* @brief Send one byte in the open frame.
*
* If the payload was completed, checksum is be added and
* the frame is closed.
*
* @param frm : Framing layer instance
* @param byte : byte to send
* @return true on success (value did fit in a frame)
*/
bool sbmp_frm_send_byte(SBMP_FrmInst *frm, uint8_t byte);
/**
* @brief Send a data buffer (or a part).
*
* If the payload was completed, checksum is be added and
* the frame is closed.
*
* @param frm : Framing layer instance
* @param buffer : buffer of bytes to send
* @param length : buffer length (byte count)
* @return actual sent length (until payload is full)
*/
uint16_t sbmp_frm_send_buffer(SBMP_FrmInst *frm, const uint8_t *buffer, uint16_t length);
// ---- Internal frame struct ------------------------
/** SBMP framing layer Rx / Tx state */
enum SBMP_FrmStatus {
FRM_STATE_IDLE, /*!< Ready to start/accept a frame */
FRM_STATE_CKSUM_TYPE, /*!< Rx, waiting for checksum type (1 byte) */
FRM_STATE_LENGTH, /*!< Rx, waiting for payload length (2 bytes) */
FRM_STATE_HDRXOR, /*!< Rx, waiting for header XOR (1 byte) */
FRM_STATE_PAYLOAD, /*!< Rx or Tx, payload rx/tx in progress. */
FRM_STATE_DISCARD, /*!< Discard rx_length worth of bytes, then end */
FRM_STATE_CKSUM, /*!< Rx, waiting for checksum (4 bytes) */
FRM_STATE_WAIT_HANDLER, /*!< Rx, waiting for rx callback to process the payload */
};
/**
* Internal state of the SBMP node
* Placed in the header to allow static allocation.
*/
struct SBMP_FrmInstance_struct {
// --- reception ---
uint32_t mb_buf; /*!< Multi-byte value buffer */
uint8_t mb_cnt;
uint8_t rx_hdr_xor; /*!< Header xor scratch field */
enum SBMP_FrmStatus rx_status;
bool rx_enabled; /*!< Enable Rx. Disabled -> reject incoming bytes. */
bool tx_enabled; /*!< Enable Rx. Disabled -> reject incoming bytes. */
uint8_t *rx_buffer; /*!< Incoming packet buffer */
uint16_t rx_buffer_i; /*!< Buffer cursor */
uint16_t rx_buffer_cap; /*!< Buffer capacity */
uint16_t rx_length; /*!< Total payload length */
SBMP_CksumType rx_cksum_type; /*!< Current packet's checksum type */
uint32_t rx_cksum_scratch; /*!< crc aggregation field for received data */
void (*rx_handler)(uint8_t *payload, uint16_t length, void *user_token); /*!< Message received handler */
void *user_token; /*!< Arbitrary pointer set by the user. Passed to callbacks.
Can be used to identify instance of a higher layer. */
// --- transmission ---
uint16_t tx_remain; /*!< Number of remaining bytes to transmit */
SBMP_CksumType tx_cksum_type;
uint32_t tx_cksum_scratch; /*!< crc aggregation field for transmit */
enum SBMP_FrmStatus tx_status;
// output functions. Only tx_func is needed.
void (*tx_func)(uint8_t byte); /*!< Function to send one byte */
};
// ------------------------------------
#endif /* SBMP_FRAME_H */

@ -1,15 +0,0 @@
#ifndef SBMP_COMMON_H
#define SBMP_COMMON_H
#include "sbmp_config.h"
#include "esp8266.h"
/**
* SBMP logging utils
*/
// do-nothing definitions
#define sbmp_error(fmt, ...) os_printf("\x1b[31;1mSBMP][E] "fmt"\x1b[0m\r\n", ##__VA_ARGS__)
#define sbmp_info(fmt, ...) os_printf("\x1b[32;1m[SBMP][i] "fmt"\x1b[0m\r\n", ##__VA_ARGS__)
#endif /* SBMP_COMMON_H */

@ -1,416 +0,0 @@
#include "esp8266.h"
#include "sbmp_config.h"
#include "sbmp_logging.h"
#include "sbmp_session.h"
// protos
static void handle_hsk_datagram(SBMP_Endpoint *ep, SBMP_Datagram *dg);
// lsb, msb for uint16_t
#define U16_LSB(x) ((x) & 0xFF)
#define U16_MSB(x) ((x >> 8) & 0xFF)
// length of the payload sent with a handshake packet.
#define HSK_PAYLOAD_LEN 3
// Datagram header length - 2 B sesn, 1 B type
#define DATAGRA_HEADER_LEN 3
/** Rx handler that is assigned to the framing layer */
static void FLASH_FN ep_rx_handler(uint8_t *buf, uint16_t len, void *token)
{
// endpoint pointer is stored in the user token
SBMP_Endpoint *ep = (SBMP_Endpoint *)token;
if (NULL != sbmp_dg_parse(&ep->static_dg, buf, len)) {
// payload parsed OK
// check if handshake datagram, else call user callback.
handle_hsk_datagram(ep, &ep->static_dg);
}
}
/**
* @brief Initialize the endpoint.
*
* @param ep : Endpoint var pointer, or NULL to allocate one.
* @param buffer : Rx buffer. NULL to allocate one.
* @param buffer_size : Rx buffer length
* @return the endpoint struct pointer (allocated if ep was NULL)
*/
SBMP_Endpoint FLASH_FN *sbmp_ep_init(
SBMP_Endpoint *ep,
uint8_t *buffer,
uint16_t buffer_size,
void (*dg_rx_handler)(SBMP_Datagram *dg),
void (*tx_func)(uint8_t byte))
{
if (ep == NULL) {
// request to allocate it
#if SBMP_MALLOC
ep = malloc(sizeof(SBMP_Endpoint));
#else
return NULL; // fail
#endif
}
// set up the framing layer
sbmp_frm_init(&ep->frm, buffer, buffer_size, ep_rx_handler, tx_func);
// set token, so callback knows what EP it's for.
sbmp_frm_set_user_token(&ep->frm, (void *) ep);
ep->rx_handler = dg_rx_handler;
ep->buffer_size = buffer_size; // sent to the peer
#if SBMP_HAS_CRC32
ep->peer_pref_cksum = SBMP_CKSUM_CRC32;
ep->pref_cksum = SBMP_CKSUM_CRC32;
#else
ep->peer_pref_cksum = SBMP_CKSUM_XOR;
ep->pref_cksum = SBMP_CKSUM_XOR;
#endif
// reset state information
sbmp_ep_reset(ep);
return ep;
}
/**
* @brief Reset an endpoint and it's Framing Layer
*
* This discards all state information.
*
* @param ep : Endpoint
*/
void FLASH_FN sbmp_ep_reset(SBMP_Endpoint *ep)
{
ep->next_session = 0;
ep->origin = 0;
// init the handshake status
ep->hsk_session = 0;
ep->hsk_state = SBMP_HSK_NOT_STARTED;
ep->peer_buffer_size = 0xFFFF; // max possible buffer
sbmp_frm_reset(&ep->frm);
}
// --- Customizing settings ---
/** Set session number (good to randomize before first message) */
void FLASH_FN sbmp_ep_seed_session(SBMP_Endpoint *ep, uint16_t sesn)
{
ep->next_session = sesn & 0x7FFF;
}
/** Set the origin bit (bypass handshake) */
void FLASH_FN sbmp_ep_set_origin(SBMP_Endpoint *endp, bool bit)
{
endp->origin = bit;
}
/** Set the preferred checksum. */
void FLASH_FN sbmp_ep_set_preferred_cksum(SBMP_Endpoint *endp, SBMP_CksumType cksum_type)
{
if (cksum_type == SBMP_CKSUM_CRC32 && !SBMP_HAS_CRC32) {
sbmp_error("CRC32 not avail, using XOR instead.");
cksum_type = SBMP_CKSUM_XOR;
}
endp->pref_cksum = cksum_type;
}
/** Enable or disable RX in the FrmInst backing this Endpoint */
void FLASH_FN sbmp_ep_enable_rx(SBMP_Endpoint *ep, bool enable_rx)
{
sbmp_frm_enable_rx(&ep->frm, enable_rx);
}
/** Enable or disable TX in the FrmInst backing this Endpoint */
void FLASH_FN sbmp_ep_enable_tx(SBMP_Endpoint *ep, bool enable_tx)
{
sbmp_frm_enable_tx(&ep->frm, enable_tx);
}
/** Enable or disable Rx & TX in the FrmInst backing this Endpoint */
void FLASH_FN sbmp_ep_enable(SBMP_Endpoint *ep, bool enable)
{
sbmp_frm_enable(&ep->frm, enable);
}
// ---
/** Get a new session number */
static uint16_t FLASH_FN next_session(SBMP_Endpoint *ep)
{
uint16_t sesn = ep->next_session;
if (++ep->next_session == 0x8000) {
// overflow into the origin bit
ep->next_session = 0; // start from zero
}
return sesn | (uint16_t)(ep->origin << 15); // add the origin bit
}
// --- Header/body send funcs ---
/** Start a message as a reply */
bool FLASH_FN sbmp_ep_start_response(SBMP_Endpoint *ep, SBMP_DgType type, uint16_t length, uint16_t sesn)
{
uint16_t peer_accepts = ep->peer_buffer_size - DATAGRA_HEADER_LEN;
if (length > peer_accepts) {
sbmp_error("Msg too long (%d B), peer accepts max %d B.", length, peer_accepts);
return false;
}
return sbmp_dg_start(&ep->frm, ep->peer_pref_cksum, sesn, type, length);
}
/** Start a message in a new session */
bool FLASH_FN sbmp_ep_start_session(SBMP_Endpoint *ep, SBMP_DgType type, uint16_t length, uint16_t *sesn_ptr)
{
uint16_t sn = next_session(ep);
bool suc = sbmp_ep_start_response(ep, type, length, sn);
if (suc) {
if (sesn_ptr != NULL) *sesn_ptr = sn;
}
return suc;
}
/** Send one byte in the current message */
bool FLASH_FN sbmp_ep_send_byte(SBMP_Endpoint *ep, uint8_t byte)
{
return sbmp_frm_send_byte(&ep->frm, byte);
}
/** Send a data buffer (or a part) in the current message */
uint16_t FLASH_FN sbmp_ep_send_buffer(SBMP_Endpoint *ep, const uint8_t *buffer, uint16_t length)
{
return sbmp_frm_send_buffer(&ep->frm, buffer, length);
}
/** Rx, pass to framing layer */
SBMP_RxStatus FLASH_FN sbmp_ep_receive(SBMP_Endpoint *ep, uint8_t byte)
{
return sbmp_frm_receive(&ep->frm, byte);
}
// --- All-in-one send funcs ---
/** Send a message in a session. */
bool FLASH_FN sbmp_ep_send_response(
SBMP_Endpoint *ep,
SBMP_DgType type,
const uint8_t *buffer,
uint16_t length,
uint16_t sesn,
uint16_t *sent_bytes_ptr)
{
bool suc = sbmp_ep_start_response(ep, type, length, sesn);
if (suc) {
uint16_t sent = sbmp_ep_send_buffer(ep, buffer, length);
if (sent_bytes_ptr != NULL) *sent_bytes_ptr = sent;
}
return suc;
}
/** Send message in a new session */
bool FLASH_FN sbmp_ep_send_message(
SBMP_Endpoint *ep,
SBMP_DgType type,
const uint8_t *buffer,
uint16_t length,
uint16_t *sesn_ptr,
uint16_t *sent_bytes_ptr)
{
// This juggling with session nr is because it wouldn't work
// without actual hardware delay otherwise.
uint16_t sn = next_session(ep);;
uint16_t old_sesn = 0;
if (sesn_ptr != NULL) {
old_sesn = *sesn_ptr;
*sesn_ptr = sn;
}
bool suc = sbmp_ep_send_response(ep, type, buffer, length, sn, sent_bytes_ptr);
if (!suc) {
if (sesn_ptr != NULL) {
*sesn_ptr = old_sesn; // restore
}
}
return suc;
}
// --- Handshake ---
/**
* Prepare a buffer to send to peer during handshake
*
* The buffer is long HSK_PAYLOAD_LEN bytes
*/
static void FLASH_FN populate_hsk_buf(SBMP_Endpoint *ep, uint8_t* buf)
{
// [ pref_crc 1B | buf_size 2B ]
buf[0] = ep->pref_cksum;
buf[1] = U16_LSB(ep->buffer_size);
buf[2] = U16_MSB(ep->buffer_size);
}
/** Parse peer info from received handhsake dg payload */
static void FLASH_FN parse_peer_hsk_buf(SBMP_Endpoint *ep, const uint8_t* buf)
{
ep->peer_pref_cksum = buf[0];
ep->peer_buffer_size = (uint16_t)(buf[1] | (buf[2] << 8));
sbmp_info("HSK success, peer buf %d, pref cksum %d",
ep->peer_buffer_size,
ep->peer_pref_cksum);
// check if checksum available
if (ep->peer_pref_cksum == SBMP_CKSUM_CRC32 && !SBMP_HAS_CRC32) {
sbmp_error("CRC32 not avail, using XOR as peer's pref cksum.");
ep->peer_pref_cksum = SBMP_CKSUM_XOR;
}
}
/**
* @brief Start a handshake (origin bit arbitration)
* @param ep : Endpoint state
*/
bool FLASH_FN sbmp_ep_start_handshake(SBMP_Endpoint *ep)
{
if (ep->hsk_state == SBMP_HSK_AWAIT_REPLY) {
// busy now
sbmp_error("Can't start HSK, in progress already.");
return false;
}
ep->hsk_state = 0;
uint8_t buf[HSK_PAYLOAD_LEN];
populate_hsk_buf(ep, buf);
ep->hsk_state = SBMP_HSK_AWAIT_REPLY;
bool suc = sbmp_ep_send_message(ep, SBMP_DG_HSK_START, buf, 3, &ep->hsk_session, NULL);
if (!suc) {
ep->hsk_state = SBMP_HSK_NOT_STARTED;
}
return suc;
}
/** Get hsk state */
SBMP_HandshakeStatus FLASH_FN sbmp_ep_handshake_status(SBMP_Endpoint *ep)
{
return ep->hsk_state;
}
/**
* @brief Process handshake datagrams & update handshake state accordingly.
*
* Non-handshake datagrams are passed on to the user Rx callback.
*
* @param ep : endpoint
* @param dg : datagram
*/
static void FLASH_FN handle_hsk_datagram(SBMP_Endpoint *ep, SBMP_Datagram *dg)
{
bool hsk_start = (dg->type == SBMP_DG_HSK_START);
bool hsk_accept = (dg->type == SBMP_DG_HSK_ACCEPT);
bool hsk_conflict = (dg->type == SBMP_DG_HSK_CONFLICT);
if (hsk_start || hsk_accept || hsk_conflict) {
// prepare payload to send in response
uint8_t our_info_pld[HSK_PAYLOAD_LEN];
populate_hsk_buf(ep, our_info_pld);
//printf("hsk_state = %d, hsk_ses %d, dg_ses %d\n",ep->hsk_state, ep->hsk_session, dg->session);
if (hsk_start) {
// peer requests origin
sbmp_info("Rx HSK request");
if (ep->hsk_state == SBMP_HSK_AWAIT_REPLY) {
// conflict occured - we're already waiting for a reply.
sbmp_ep_send_response(ep, SBMP_DG_HSK_CONFLICT, our_info_pld, HSK_PAYLOAD_LEN, dg->session, NULL);
ep->hsk_state = SBMP_HSK_CONFLICT;
sbmp_error("HSK conflict");
} else {
// we're idle, accept the request.
bool peer_origin = (dg->session & 0x8000) >> 15;
sbmp_ep_set_origin(ep, !peer_origin);
// read peer's info
if (dg->length >= HSK_PAYLOAD_LEN) {
parse_peer_hsk_buf(ep, dg->payload);
}
ep->hsk_state = SBMP_HSK_SUCCESS;
// Send Accept response
sbmp_ep_send_response(ep, SBMP_DG_HSK_ACCEPT, our_info_pld, HSK_PAYLOAD_LEN, dg->session, NULL);
}
} else if (hsk_accept) {
// peer accepted our request
sbmp_info("Rx HSK accept");
if (ep->hsk_state != SBMP_HSK_AWAIT_REPLY || ep->hsk_session != dg->session) {
// but we didn't send any request
sbmp_error("Rx unexpected HSK accept, ignoring.");
} else {
// OK, we were waiting for this reply
// read peer's info
if (dg->length >= HSK_PAYLOAD_LEN) {
parse_peer_hsk_buf(ep, dg->payload);
}
ep->hsk_state = SBMP_HSK_SUCCESS;
}
} else if (hsk_conflict) {
// peer rejected our request due to conflict
sbmp_info("Rx HSK conflict");
if (ep->hsk_state != SBMP_HSK_AWAIT_REPLY || ep->hsk_session != dg->session) {
// but we didn't send any request
sbmp_error("Rx unexpected HSK conflict, ignoring.");
} else {
// Acknowledge the conflict
// reset everything
sbmp_frm_reset(&ep->frm);
ep->hsk_state = SBMP_HSK_CONFLICT;
}
}
} else {
// Not a HSK message
ep->rx_handler(dg);
}
}

@ -1,207 +0,0 @@
#ifndef SBMP_SESSION_H
#define SBMP_SESSION_H
/**
* SBMP session layer
*
* This layer provides an abstraction over all of SBMP.
*
* Start by creating "endpoint" with sbmp_ep_init(), then configure and enable it.
*
* Next you should trigger a handshake, which assigns your endpoint the origin bit,
* and obtains information about your peer (it's buffer size and preferred checksum).
*
* You can still interact with the framing layer directly, but it shouldn't be needed.
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include "sbmp_config.h"
#include "sbmp_datagram.h"
#include "sbmp_frame.h"
typedef enum {
SBMP_HSK_NOT_STARTED = 0, /*!< Initial state, unconfigured */
SBMP_HSK_SUCCESS = 1, /*!< Handshake done, origin assigned. */
SBMP_HSK_AWAIT_REPLY = 2, /*!< Request sent, awaiting a reply */
SBMP_HSK_CONFLICT = 3, /*!< Conflict occured during HSK */
} SBMP_HandshakeStatus;
/** SBMP Endpoint (session) structure */
typedef struct {
bool origin; /*!< Local origin bit */
uint16_t next_session; /*!< Next session number */
void (*rx_handler)(SBMP_Datagram *dg); /*!< Datagram receive handler */
SBMP_FrmInst frm; /*!< Framing layer internal state */
// Handshake
SBMP_HandshakeStatus hsk_state; /*!< Handshake progress */
uint16_t hsk_session; /*!< Session number of the handshake request message */
uint16_t peer_buffer_size; /*!< Peer's buffer size (obtained during handshake) */
SBMP_CksumType peer_pref_cksum; /*!< Peer's preferred checksum type */
// Our info for the peer
uint16_t buffer_size; /*!< Our buffer size */
SBMP_CksumType pref_cksum; /*!< Our preferred checksum */
SBMP_Datagram static_dg; /*!< Static datagram, used when DG is pased to a callback.
This way the datagram remains valid until next Frm Rx,
not only until the callback ends. Disabling the EP in the Rx
callback lets you preserve the Dg for a longer period -
i.e. put it in a global var, where a loop retrieves it. */
} SBMP_Endpoint;
/**
* @brief Initialize the endpoint.
*
* @note about the Rx handler:
* The datagram is valid until a new payload byte is received by the Frm.
* Disable the endpoint (-> thus also Frm) in the callback if you need
* to keep the Dg longer. Then re-enable it after the Dg is processed.
*
* @param ep : Endpoint struct pointer, or NULL to allocate one.
* @param buffer : Rx buffer. NULL to allocate one.
* @param buffer_size : Rx buffer length
* @param dg_rx_handler : Datagram received handler.
* @param tx_func : Function to send a byte to USART
* @return the endpoint struct pointer (allocated if ep was NULL)
*/
SBMP_Endpoint *sbmp_ep_init(SBMP_Endpoint *ep,
uint8_t *buffer,
uint16_t buffer_size,
void (*dg_rx_handler)(SBMP_Datagram *dg),
void (*tx_func)(uint8_t byte));
/**
* @brief Reset an endpoint and it's Framing Layer
*
* This discards all state information.
*
* @param ep : Endpoint
*/
void sbmp_ep_reset(SBMP_Endpoint *ep);
/** Set session number (good to randomize before starting a handshake) */
void sbmp_ep_seed_session(SBMP_Endpoint *ep, uint16_t sesn);
/**
* Set the origin bit.
* This can be used instead of handshake if origins are pre-assigned.
*/
void sbmp_ep_set_origin(SBMP_Endpoint *endp, bool bit);
/** Set the preferred checksum for this peer. */
void sbmp_ep_set_preferred_cksum(SBMP_Endpoint *endp, SBMP_CksumType cksum_type);
/**
* @brief Start a message as a reply, with CRC32
*
* @param ep : Endpoint struct
* @param type : Datagram type ID
* @param length : Datagram payload length (bytes)
* @param sesn : Session number of message this is a reply to.
* @return success
*/
bool sbmp_ep_start_response(SBMP_Endpoint *ep, SBMP_DgType type, uint16_t length, uint16_t sesn);
/**
* @brief Start a message in a new session, with CRC32
*
* @param ep : Endpoint struct
* @param type : Datagram type ID
* @param length : Datagram payload length (bytes)
* @param sesn : Var to store session number, NULL = don't store.
* @return success
*/
bool sbmp_ep_start_session(SBMP_Endpoint *ep, SBMP_DgType type, uint16_t length, uint16_t *sesn_ptr);
/**
* @brief Send one byte in the current message
*
* @param ep : Endpoint struct
* @param byte : byte to send
* @return true on success
*/
bool sbmp_ep_send_byte(SBMP_Endpoint *ep, uint8_t byte);
/**
* @brief Send a data buffer (or a part) in the current message
*
* @param ep : Endpoint struct
* @param buffer : buffer of bytes to send
* @param length : buffer length (byte count)
* @return actual sent length (until payload is full)
*/
uint16_t sbmp_ep_send_buffer(SBMP_Endpoint *ep, const uint8_t *buffer, uint16_t length);
/**
* @brief Send a message in a new session.
*
* @param ep : Endpoint struct
* @param type : Datagram type ID
* @param buffer : Buffer with data to send
* @param length : Datagram payload length (bytes)
* @param sesn_ptr : Session number
* @param sent_bytes_ptr : Var to store NR of sent bytes. NULL = don't store.
* @return success
*/
bool sbmp_ep_send_response(
SBMP_Endpoint *ep,
SBMP_DgType type,
const uint8_t *buffer,
uint16_t length,
uint16_t sesn_ptr,
uint16_t *sent_bytes_ptr);
/**
* @brief Send a message in a new session.
*
* @param ep : Endpoint struct
* @param type : Datagram type ID
* @param buffer : Buffer with data to send
* @param length : Datagram payload length (bytes)
* @param sesn_ptr : Var to store session number. NULL = don't store.
* @param sent_bytes_ptr : Var to store NR of sent bytes. NULL = don't store.
* @return success
*/
bool sbmp_ep_send_message(
SBMP_Endpoint *ep,
SBMP_DgType type,
const uint8_t *buffer,
uint16_t length,
uint16_t *sesn,
uint16_t *sent_bytes);
/**
* @brief Start a handshake (origin bit arbitration)
* @param ep : Endpoint struct
*/
bool sbmp_ep_start_handshake(SBMP_Endpoint *ep);
/**
* @brief Receive a byte from USART (is passed to the framing layer)
* @param ep : Endpoint struct
* @param byte : Byte received from USART
* @return true if byte was consumed
*/
SBMP_RxStatus sbmp_ep_receive(SBMP_Endpoint *ep, uint8_t byte);
/** Get current handshake state */
SBMP_HandshakeStatus sbmp_ep_handshake_status(SBMP_Endpoint *ep);
/** Enable or disable both Rx and TX in the FrmInst backing this Endpoint */
void sbmp_ep_enable(SBMP_Endpoint *ep, bool enable);
/** Enable or disable RX in the FrmInst backing this Endpoint */
void sbmp_ep_enable_rx(SBMP_Endpoint *ep, bool enable_rx);
/** Enable or disable TX in the FrmInst backing this Endpoint */
void sbmp_ep_enable_tx(SBMP_Endpoint *ep, bool enable_tx);
#endif /* SBMP_SESSION_H */
Loading…
Cancel
Save