init
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
/********************************** (C) COPYRIGHT *******************************
|
||||
* File Name : ch32v20x_conf.h
|
||||
* Author : WCH
|
||||
* Version : V1.0.0
|
||||
* Date : 2021/06/06
|
||||
* Description : Library configuration file.
|
||||
*********************************************************************************
|
||||
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
|
||||
* Attention: This software (modified or not) and binary are used for
|
||||
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
|
||||
*******************************************************************************/
|
||||
#ifndef __CH32V20x_CONF_H
|
||||
#define __CH32V20x_CONF_H
|
||||
|
||||
#include "ch32v20x_adc.h"
|
||||
#include "ch32v20x_bkp.h"
|
||||
#include "ch32v20x_can.h"
|
||||
#include "ch32v20x_crc.h"
|
||||
#include "ch32v20x_dbgmcu.h"
|
||||
#include "ch32v20x_dma.h"
|
||||
#include "ch32v20x_exti.h"
|
||||
#include "ch32v20x_flash.h"
|
||||
#include "ch32v20x_gpio.h"
|
||||
#include "ch32v20x_i2c.h"
|
||||
#include "ch32v20x_iwdg.h"
|
||||
#include "ch32v20x_pwr.h"
|
||||
#include "ch32v20x_rcc.h"
|
||||
#include "ch32v20x_rtc.h"
|
||||
#include "ch32v20x_spi.h"
|
||||
#include "ch32v20x_tim.h"
|
||||
#include "ch32v20x_usart.h"
|
||||
#include "ch32v20x_wwdg.h"
|
||||
#include "ch32v20x_it.h"
|
||||
#include "ch32v20x_misc.h"
|
||||
|
||||
|
||||
#endif /* __CH32V20x_CONF_H */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/********************************** (C) COPYRIGHT *******************************
|
||||
* File Name : ch32v20x_it.c
|
||||
* Author : WCH
|
||||
* Version : V1.0.0
|
||||
* Date : 2023/12/29
|
||||
* Description : Main Interrupt Service Routines.
|
||||
*********************************************************************************
|
||||
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
|
||||
* Attention: This software (modified or not) and binary are used for
|
||||
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
|
||||
*******************************************************************************/
|
||||
#include "ch32v20x_it.h"
|
||||
|
||||
void NMI_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
|
||||
void HardFault_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
|
||||
|
||||
/*********************************************************************
|
||||
* @fn NMI_Handler
|
||||
*
|
||||
* @brief This function handles NMI exception.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void NMI_Handler(void)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* @fn HardFault_Handler
|
||||
*
|
||||
* @brief This function handles Hard Fault exception.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void HardFault_Handler(void)
|
||||
{
|
||||
NVIC_SystemReset();
|
||||
while (1)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/********************************** (C) COPYRIGHT *******************************
|
||||
* File Name : ch32v20x_it.h
|
||||
* Author : WCH
|
||||
* Version : V1.0.0
|
||||
* Date : 2021/06/06
|
||||
* Description : This file contains the headers of the interrupt handlers.
|
||||
*********************************************************************************
|
||||
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
|
||||
* Attention: This software (modified or not) and binary are used for
|
||||
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
|
||||
*******************************************************************************/
|
||||
#ifndef __CH32V20x_IT_H
|
||||
#define __CH32V20x_IT_H
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
#endif /* __CH32V20x_IT_H */
|
||||
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
/********************************** (C) COPYRIGHT *******************************
|
||||
* File Name : temperature.c
|
||||
* Author : WCH
|
||||
* Version : V1.0.0
|
||||
* Date : 2023/11/17
|
||||
* Description : Temperature program body.
|
||||
*********************************************************************************
|
||||
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
|
||||
* Attention: This software (modified or not) and binary are used for
|
||||
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
|
||||
*******************************************************************************/
|
||||
|
||||
/*
|
||||
*@Note
|
||||
*Internal temperature sensor routine:
|
||||
*Through the ADC channel 16, the output voltage value and temperature value of the internal
|
||||
*temperature sensor are collected.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "debug.h"
|
||||
#include "lib/telemetry/telemetry.h"
|
||||
|
||||
/* Global Variable */
|
||||
int16_t Calibrattion_Val = 0;
|
||||
|
||||
/*********************************************************************
|
||||
* @fn ADC_Function_Init
|
||||
*
|
||||
* @brief Initializes ADC collection.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void ADC_Function_Init(void)
|
||||
{
|
||||
ADC_InitTypeDef ADC_InitStructure={0};
|
||||
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE );
|
||||
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
|
||||
|
||||
ADC_DeInit(ADC1);
|
||||
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
|
||||
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
|
||||
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
|
||||
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
|
||||
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
|
||||
ADC_InitStructure.ADC_NbrOfChannel = 1;
|
||||
ADC_Init(ADC1, &ADC_InitStructure);
|
||||
|
||||
ADC_Cmd(ADC1, ENABLE);
|
||||
|
||||
ADC_BufferCmd(ADC1, DISABLE); //disable buffer
|
||||
ADC_ResetCalibration(ADC1);
|
||||
while(ADC_GetResetCalibrationStatus(ADC1));
|
||||
ADC_StartCalibration(ADC1);
|
||||
while(ADC_GetCalibrationStatus(ADC1));
|
||||
Calibrattion_Val = Get_CalibrationValue(ADC1);
|
||||
|
||||
ADC_BufferCmd(ADC1, ENABLE); //enable buffer
|
||||
|
||||
ADC_TempSensorVrefintCmd(ENABLE);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* @fn Get_ADC_Val
|
||||
*
|
||||
* @brief Returns ADCx conversion result data.
|
||||
*
|
||||
* @param ch - ADC channel.
|
||||
* ADC_Channel_0 - ADC Channel0 selected.
|
||||
* ADC_Channel_1 - ADC Channel1 selected.
|
||||
* ADC_Channel_2 - ADC Channel2 selected.
|
||||
* ADC_Channel_3 - ADC Channel3 selected.
|
||||
* ADC_Channel_4 - ADC Channel4 selected.
|
||||
* ADC_Channel_5 - ADC Channel5 selected.
|
||||
* ADC_Channel_6 - ADC Channel6 selected.
|
||||
* ADC_Channel_7 - ADC Channel7 selected.
|
||||
* ADC_Channel_8 - ADC Channel8 selected.
|
||||
* ADC_Channel_9 - ADC Channel9 selected.
|
||||
* ADC_Channel_10 - ADC Channel10 selected.
|
||||
* ADC_Channel_11 - ADC Channel11 selected.
|
||||
* ADC_Channel_12 - ADC Channel12 selected.
|
||||
* ADC_Channel_13 - ADC Channel13 selected.
|
||||
* ADC_Channel_14 - ADC Channel14 selected.
|
||||
* ADC_Channel_15 - ADC Channel15 selected.
|
||||
* ADC_Channel_16 - ADC Channel16 selected.
|
||||
* ADC_Channel_17 - ADC Channel17 selected.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
uint16_t Get_ADC_Val(uint8_t ch)
|
||||
{
|
||||
uint16_t val;
|
||||
|
||||
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );
|
||||
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
|
||||
|
||||
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));
|
||||
|
||||
val = ADC_GetConversionValue(ADC1);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* @fn Get_ADC_Average
|
||||
*
|
||||
* @brief Returns ADCx conversion result average data.
|
||||
*
|
||||
* @param ch - ADC channel.
|
||||
* ADC_Channel_0 - ADC Channel0 selected.
|
||||
* ADC_Channel_1 - ADC Channel1 selected.
|
||||
* ADC_Channel_2 - ADC Channel2 selected.
|
||||
* ADC_Channel_3 - ADC Channel3 selected.
|
||||
* ADC_Channel_4 - ADC Channel4 selected.
|
||||
* ADC_Channel_5 - ADC Channel5 selected.
|
||||
* ADC_Channel_6 - ADC Channel6 selected.
|
||||
* ADC_Channel_7 - ADC Channel7 selected.
|
||||
* ADC_Channel_8 - ADC Channel8 selected.
|
||||
* ADC_Channel_9 - ADC Channel9 selected.
|
||||
* ADC_Channel_10 - ADC Channel10 selected.
|
||||
* ADC_Channel_11 - ADC Channel11 selected.
|
||||
* ADC_Channel_12 - ADC Channel12 selected.
|
||||
* ADC_Channel_13 - ADC Channel13 selected.
|
||||
* ADC_Channel_14 - ADC Channel14 selected.
|
||||
* ADC_Channel_15 - ADC Channel15 selected.
|
||||
* ADC_Channel_16 - ADC Channel16 selected.
|
||||
* ADC_Channel_17 - ADC Channel17 selected.
|
||||
*
|
||||
* @return val - The Data conversion value.
|
||||
*/
|
||||
uint16_t Get_ADC_Average(uint8_t ch,uint8_t times)
|
||||
{
|
||||
u32 temp_val=0;
|
||||
uint8_t t;
|
||||
uint16_t val;
|
||||
|
||||
for(t=0;t<times;t++)
|
||||
{
|
||||
temp_val+=Get_ADC_Val(ch);
|
||||
Delay_Ms(5);
|
||||
}
|
||||
|
||||
val = temp_val/times;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* @fn Get_ConversionVal
|
||||
*
|
||||
* @brief Get Conversion Value.
|
||||
*
|
||||
* @param val - Sampling value
|
||||
*
|
||||
* @return val+Calibrattion_Val - Conversion Value.
|
||||
*/
|
||||
uint16_t Get_ConversionVal(int16_t val)
|
||||
{
|
||||
if((val+Calibrattion_Val)<0|| val==0) return 0;
|
||||
if((Calibrattion_Val+val)>4095||val==4095) return 4095;
|
||||
return (val+Calibrattion_Val);
|
||||
}
|
||||
|
||||
s32 TempSensor_Volt_To_Temper_x10(s32 Value)
|
||||
{
|
||||
s32 Temper_x10;
|
||||
s32 Refer_Volt, Refer_Temper;
|
||||
s32 k = 43; // slope in mV/¡ãC
|
||||
|
||||
// Read factory calibration values
|
||||
Refer_Volt = (s32)((*(u32 *)0x1FFFF720) & 0x0000FFFF); // mV at reference temp
|
||||
Refer_Temper = (s32)(((*(u32 *)0x1FFFF720) >> 16) & 0x0000FFFF); // ¡ãC
|
||||
|
||||
// Compute temperature in decicelsius
|
||||
// Formula: T_x10 = Tref*10 - ((V - Vref)*100 + k/2) / k
|
||||
// Multiply (V - Vref) by 100 to get tenths of ¡ãC
|
||||
Temper_x10 = Refer_Temper * 10 - ((Value - Refer_Volt) * 100 + (k / 2)) / k;
|
||||
|
||||
return Temper_x10;
|
||||
}
|
||||
|
||||
|
||||
s32 getTemperature(void)
|
||||
{
|
||||
uint16_t ADC_val;
|
||||
s32 val_mv;
|
||||
|
||||
ADC_val = Get_ADC_Average( ADC_Channel_TempSensor, 10 );
|
||||
|
||||
ADC_val = Get_ConversionVal(ADC_val);
|
||||
|
||||
val_mv = (ADC_val*3300/4096);
|
||||
|
||||
return TempSensor_Volt_To_Temper(val_mv);
|
||||
}
|
||||
|
||||
int16_t getDeciTemperature(void)
|
||||
{
|
||||
uint16_t ADC_val;
|
||||
s32 val_mv;
|
||||
|
||||
ADC_val = Get_ADC_Average( ADC_Channel_TempSensor, 10 );
|
||||
|
||||
ADC_val = Get_ConversionVal(ADC_val);
|
||||
|
||||
val_mv = (ADC_val*3300/4096);
|
||||
|
||||
s32 temp_x10 = TempSensor_Volt_To_Temper_x10(val_mv);
|
||||
|
||||
int16_t temp16 = (int16_t)temp_x10; // store in 2 bytes
|
||||
return temp16;
|
||||
}
|
||||
|
||||
s32 getVoltage(void)
|
||||
{
|
||||
uint16_t ADC_val;
|
||||
s32 val_mv;
|
||||
|
||||
ADC_val = Get_ADC_Average( ADC_Channel_1, 10 );
|
||||
|
||||
ADC_val = Get_ConversionVal(ADC_val);
|
||||
|
||||
|
||||
val_mv = (ADC_val*3300/4096);
|
||||
|
||||
return val_mv;
|
||||
}
|
||||
|
||||
// Helper: convert signed 24-bit int to 3 bytes (big-endian)
|
||||
void int24_to_bytes(int32_t value, uint8_t *bytes) {
|
||||
if (value < 0) value += 0x1000000; // 2's complement for 24-bit
|
||||
bytes[0] = (value >> 16) & 0xFF;
|
||||
bytes[1] = (value >> 8) & 0xFF;
|
||||
bytes[2] = value & 0xFF;
|
||||
}
|
||||
|
||||
// Encode GPS into CayenneLPP payload
|
||||
void encode_gps(uint8_t channel, int32_t lat, int32_t lon, int32_t alt, uint8_t *payload) {
|
||||
payload[0] = channel;
|
||||
payload[1] = LPP_GPS; // GPS type
|
||||
|
||||
//int32_t latInt = lat * 10000;
|
||||
//int32_t lonInt = lon * 10000;
|
||||
//int32_t altInt = alt * 100;
|
||||
|
||||
int24_to_bytes(lat, &payload[2]);
|
||||
int24_to_bytes(lon, &payload[5]);
|
||||
int24_to_bytes(alt, &payload[8]);
|
||||
//int24_to_bytes(latInt, &payload[2]);
|
||||
//int24_to_bytes(lonInt, &payload[5]);
|
||||
//int24_to_bytes(altInt, &payload[8]);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
#ifndef TEMPERATURE_HEADER
|
||||
#define TEMPERATURE_HEADER
|
||||
|
||||
/********************************** (C) COPYRIGHT *******************************
|
||||
* File Name : temperature.c
|
||||
* Author : WCH
|
||||
* Version : V1.0.0
|
||||
* Date : 2023/11/17
|
||||
* Description : Temperature program body.
|
||||
*********************************************************************************
|
||||
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
|
||||
* Attention: This software (modified or not) and binary are used for
|
||||
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
|
||||
*******************************************************************************/
|
||||
|
||||
/*
|
||||
*@Note
|
||||
*Internal temperature sensor routine:
|
||||
*Through the ADC channel 16, the output voltage value and temperature value of the internal
|
||||
*temperature sensor are collected.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Global Variable */
|
||||
extern int16_t Calibrattion_Val;
|
||||
|
||||
/*********************************************************************
|
||||
* @fn ADC_Function_Init
|
||||
*
|
||||
* @brief Initializes ADC collection.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void ADC_Function_Init(void);
|
||||
|
||||
/*********************************************************************
|
||||
* @fn Get_ADC_Val
|
||||
*
|
||||
* @brief Returns ADCx conversion result data.
|
||||
*
|
||||
* @param ch - ADC channel.
|
||||
* ADC_Channel_0 - ADC Channel0 selected.
|
||||
* ADC_Channel_1 - ADC Channel1 selected.
|
||||
* ADC_Channel_2 - ADC Channel2 selected.
|
||||
* ADC_Channel_3 - ADC Channel3 selected.
|
||||
* ADC_Channel_4 - ADC Channel4 selected.
|
||||
* ADC_Channel_5 - ADC Channel5 selected.
|
||||
* ADC_Channel_6 - ADC Channel6 selected.
|
||||
* ADC_Channel_7 - ADC Channel7 selected.
|
||||
* ADC_Channel_8 - ADC Channel8 selected.
|
||||
* ADC_Channel_9 - ADC Channel9 selected.
|
||||
* ADC_Channel_10 - ADC Channel10 selected.
|
||||
* ADC_Channel_11 - ADC Channel11 selected.
|
||||
* ADC_Channel_12 - ADC Channel12 selected.
|
||||
* ADC_Channel_13 - ADC Channel13 selected.
|
||||
* ADC_Channel_14 - ADC Channel14 selected.
|
||||
* ADC_Channel_15 - ADC Channel15 selected.
|
||||
* ADC_Channel_16 - ADC Channel16 selected.
|
||||
* ADC_Channel_17 - ADC Channel17 selected.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
uint16_t Get_ADC_Val(uint8_t ch);
|
||||
|
||||
/*********************************************************************
|
||||
* @fn Get_ADC_Average
|
||||
*
|
||||
* @brief Returns ADCx conversion result average data.
|
||||
*
|
||||
* @param ch - ADC channel.
|
||||
* ADC_Channel_0 - ADC Channel0 selected.
|
||||
* ADC_Channel_1 - ADC Channel1 selected.
|
||||
* ADC_Channel_2 - ADC Channel2 selected.
|
||||
* ADC_Channel_3 - ADC Channel3 selected.
|
||||
* ADC_Channel_4 - ADC Channel4 selected.
|
||||
* ADC_Channel_5 - ADC Channel5 selected.
|
||||
* ADC_Channel_6 - ADC Channel6 selected.
|
||||
* ADC_Channel_7 - ADC Channel7 selected.
|
||||
* ADC_Channel_8 - ADC Channel8 selected.
|
||||
* ADC_Channel_9 - ADC Channel9 selected.
|
||||
* ADC_Channel_10 - ADC Channel10 selected.
|
||||
* ADC_Channel_11 - ADC Channel11 selected.
|
||||
* ADC_Channel_12 - ADC Channel12 selected.
|
||||
* ADC_Channel_13 - ADC Channel13 selected.
|
||||
* ADC_Channel_14 - ADC Channel14 selected.
|
||||
* ADC_Channel_15 - ADC Channel15 selected.
|
||||
* ADC_Channel_16 - ADC Channel16 selected.
|
||||
* ADC_Channel_17 - ADC Channel17 selected.
|
||||
*
|
||||
* @return val - The Data conversion value.
|
||||
*/
|
||||
uint16_t Get_ADC_Average(uint8_t ch,uint8_t times);
|
||||
|
||||
/*********************************************************************
|
||||
* @fn Get_ConversionVal
|
||||
*
|
||||
* @brief Get Conversion Value.
|
||||
*
|
||||
* @param val - Sampling value
|
||||
*
|
||||
* @return val+Calibrattion_Val - Conversion Value.
|
||||
*/
|
||||
uint16_t Get_ConversionVal(int16_t val);
|
||||
|
||||
int32_t getTemperature(void);
|
||||
|
||||
int16_t getDeciTemperature(void);
|
||||
|
||||
int32_t getVoltage(void);
|
||||
|
||||
void encode_gps(uint8_t channel, int32_t lat, int32_t lon, int32_t alt, uint8_t *payload);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "base64.h"
|
||||
|
||||
static const char b64_enc_table[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
void base64_encode (const uint8_t *in, size_t ilen, char *out) {
|
||||
size_t out_len = 0;
|
||||
for (size_t i = 0; i < ilen; i += 3) {
|
||||
uint32_t triple = 0;
|
||||
int remain = ilen - i;
|
||||
|
||||
triple |= in[i] << 16;
|
||||
if (remain > 1)
|
||||
triple |= in[i + 1] << 8;
|
||||
if (remain > 2)
|
||||
triple |= in[i + 2];
|
||||
|
||||
out[out_len++] = b64_enc_table[(triple >> 18) & 0x3F];
|
||||
out[out_len++] = b64_enc_table[(triple >> 12) & 0x3F];
|
||||
out[out_len++] = (remain > 1) ? b64_enc_table[(triple >> 6) & 0x3F] : '=';
|
||||
out[out_len++] = (remain > 2) ? b64_enc_table[triple & 0x3F] : '=';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef BASE64_HEADER_FILE
|
||||
#define BASE64_HEADER_FILE
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void base64_encode (const uint8_t *in, size_t ilen, char *out);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* cifra - embedded cryptography library
|
||||
* Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
*
|
||||
* To the extent possible under law, the author(s) have dedicated all
|
||||
* copyright and related and neighboring rights to this software to the
|
||||
* public domain worldwide. This software is distributed without any
|
||||
* warranty.
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication
|
||||
* along with this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "cf_config.h"
|
||||
#include "aes.h"
|
||||
#include "handy.h"
|
||||
#include "bitops.h"
|
||||
#include "tassert.h"
|
||||
|
||||
static const uint8_t S[256] =
|
||||
{
|
||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe,
|
||||
0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4,
|
||||
0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7,
|
||||
0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3,
|
||||
0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09,
|
||||
0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3,
|
||||
0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe,
|
||||
0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
|
||||
0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92,
|
||||
0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c,
|
||||
0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19,
|
||||
0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
|
||||
0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2,
|
||||
0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5,
|
||||
0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25,
|
||||
0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
||||
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86,
|
||||
0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e,
|
||||
0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42,
|
||||
0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16};
|
||||
|
||||
static const uint8_t Rcon[11] =
|
||||
{
|
||||
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36};
|
||||
|
||||
#ifdef INLINE_FUNCS
|
||||
static inline uint32_t word4 (uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3) {
|
||||
return b0 << 24 | b1 << 16 | b2 << 8 | b3;
|
||||
}
|
||||
|
||||
static inline uint8_t byte (uint32_t w, unsigned x) {
|
||||
/* nb. bytes are numbered 0 (leftmost, top)
|
||||
* to 3 (rightmost). */
|
||||
x = 3 - x;
|
||||
return (w >> (x * 8)) & 0xff;
|
||||
}
|
||||
|
||||
static uint32_t round_constant (uint32_t i) {
|
||||
return Rcon[i] << 24;
|
||||
}
|
||||
|
||||
static uint32_t rot_word (uint32_t w) {
|
||||
/* Takes
|
||||
* word [a0,a1,a2,a3]
|
||||
* returns
|
||||
* word [a1,a2,a3,a0]
|
||||
*
|
||||
*/
|
||||
return rotl32 (w, 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define word4(a, b, c, d) (((uint32_t)(a) << 24) | ((uint32_t)(b) << 16) | ((uint32_t)(c) << 8) | (d))
|
||||
#define byte(w, x) ((w >> ((3 - (x)) << 3)) & 0xff)
|
||||
#define round_constant(i) ((uint32_t)(Rcon[i]) << 24)
|
||||
#define rot_word(w) rotl32 ((w), 8)
|
||||
|
||||
static uint32_t sub_word (uint32_t w, const uint8_t *sbox) {
|
||||
uint8_t a = byte (w, 0),
|
||||
b = byte (w, 1),
|
||||
c = byte (w, 2),
|
||||
d = byte (w, 3);
|
||||
#if CF_CACHE_SIDE_CHANNEL_PROTECTION
|
||||
select_u8x4 (&a, &b, &c, &d, sbox, 256);
|
||||
#else
|
||||
a = sbox[a];
|
||||
b = sbox[b];
|
||||
c = sbox[c];
|
||||
d = sbox[d];
|
||||
#endif
|
||||
return word4 (a, b, c, d);
|
||||
}
|
||||
|
||||
static void aes_schedule (cf_aes_context *ctx, const uint8_t *key, size_t nkey) {
|
||||
size_t i,
|
||||
nb = AES_BLOCKSZ / 4,
|
||||
nk = nkey / 4,
|
||||
n = nb * (ctx->rounds + 1);
|
||||
uint32_t *w = ctx->ks;
|
||||
|
||||
/* First words are just the key. */
|
||||
for (i = 0; i < nk; i++) {
|
||||
w[i] = read32_be (key + i * 4);
|
||||
}
|
||||
|
||||
uint32_t i_div_nk = 1;
|
||||
uint32_t i_mod_nk = 0;
|
||||
|
||||
for (; i < n; i++, i_mod_nk++) {
|
||||
uint32_t temp = w[i - 1];
|
||||
|
||||
if (i_mod_nk == nk) {
|
||||
i_div_nk++;
|
||||
i_mod_nk = 0;
|
||||
}
|
||||
|
||||
if (i_mod_nk == 0)
|
||||
temp = sub_word (rot_word (temp), S) ^ round_constant (i_div_nk);
|
||||
else if (nk > 6 && i_mod_nk == 4)
|
||||
temp = sub_word (temp, S);
|
||||
|
||||
w[i] = w[i - nk] ^ temp;
|
||||
}
|
||||
}
|
||||
|
||||
void cf_aes_init (cf_aes_context *ctx, const uint8_t *key, size_t nkey) {
|
||||
memset (ctx, 0, sizeof *ctx);
|
||||
|
||||
switch (nkey) {
|
||||
#if CF_AES_MAXROUNDS >= AES128_ROUNDS
|
||||
case 16:
|
||||
ctx->rounds = AES128_ROUNDS;
|
||||
aes_schedule (ctx, key, nkey);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if CF_AES_MAXROUNDS >= AES192_ROUNDS
|
||||
case 24:
|
||||
ctx->rounds = AES192_ROUNDS;
|
||||
aes_schedule (ctx, key, nkey);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if CF_AES_MAXROUNDS >= AES256_ROUNDS
|
||||
case 32:
|
||||
ctx->rounds = AES256_ROUNDS;
|
||||
aes_schedule (ctx, key, nkey);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void add_round_key (uint32_t state[4], const uint32_t rk[4]) {
|
||||
state[0] ^= rk[0];
|
||||
state[1] ^= rk[1];
|
||||
state[2] ^= rk[2];
|
||||
state[3] ^= rk[3];
|
||||
}
|
||||
|
||||
static void sub_block (uint32_t state[4]) {
|
||||
state[0] = sub_word (state[0], S);
|
||||
state[1] = sub_word (state[1], S);
|
||||
state[2] = sub_word (state[2], S);
|
||||
state[3] = sub_word (state[3], S);
|
||||
}
|
||||
|
||||
static void shift_rows (uint32_t state[4]) {
|
||||
uint32_t u, v, x, y;
|
||||
|
||||
u = word4 (byte (state[0], 0),
|
||||
byte (state[1], 1),
|
||||
byte (state[2], 2),
|
||||
byte (state[3], 3));
|
||||
|
||||
v = word4 (byte (state[1], 0),
|
||||
byte (state[2], 1),
|
||||
byte (state[3], 2),
|
||||
byte (state[0], 3));
|
||||
|
||||
x = word4 (byte (state[2], 0),
|
||||
byte (state[3], 1),
|
||||
byte (state[0], 2),
|
||||
byte (state[1], 3));
|
||||
|
||||
y = word4 (byte (state[3], 0),
|
||||
byte (state[0], 1),
|
||||
byte (state[1], 2),
|
||||
byte (state[2], 3));
|
||||
|
||||
state[0] = u;
|
||||
state[1] = v;
|
||||
state[2] = x;
|
||||
state[3] = y;
|
||||
}
|
||||
|
||||
static uint32_t gf_poly_mul2 (uint32_t x) {
|
||||
return ((x & 0x7f7f7f7f) << 1) ^
|
||||
(((x & 0x80808080) >> 7) * 0x1b);
|
||||
}
|
||||
|
||||
static uint32_t mix_column (uint32_t x) {
|
||||
uint32_t x2 = gf_poly_mul2 (x);
|
||||
return x2 ^ rotr32 (x ^ x2, 24) ^ rotr32 (x, 16) ^ rotr32 (x, 8);
|
||||
}
|
||||
|
||||
static void mix_columns (uint32_t state[4]) {
|
||||
state[0] = mix_column (state[0]);
|
||||
state[1] = mix_column (state[1]);
|
||||
state[2] = mix_column (state[2]);
|
||||
state[3] = mix_column (state[3]);
|
||||
}
|
||||
|
||||
void cf_aes_encrypt (const cf_aes_context *ctx,
|
||||
const uint8_t in[AES_BLOCKSZ],
|
||||
uint8_t out[AES_BLOCKSZ]) {
|
||||
assert (ctx->rounds == AES128_ROUNDS ||
|
||||
ctx->rounds == AES192_ROUNDS ||
|
||||
ctx->rounds == AES256_ROUNDS);
|
||||
|
||||
uint32_t state[4] = {
|
||||
read32_be (in + 0),
|
||||
read32_be (in + 4),
|
||||
read32_be (in + 8),
|
||||
read32_be (in + 12)};
|
||||
|
||||
const uint32_t *round_keys = ctx->ks;
|
||||
add_round_key (state, round_keys);
|
||||
round_keys += 4;
|
||||
|
||||
for (uint32_t round = 1; round < ctx->rounds; round++) {
|
||||
sub_block (state);
|
||||
shift_rows (state);
|
||||
mix_columns (state);
|
||||
add_round_key (state, round_keys);
|
||||
round_keys += 4;
|
||||
}
|
||||
|
||||
sub_block (state);
|
||||
shift_rows (state);
|
||||
add_round_key (state, round_keys);
|
||||
|
||||
write32_be (state[0], out + 0);
|
||||
write32_be (state[1], out + 4);
|
||||
write32_be (state[2], out + 8);
|
||||
write32_be (state[3], out + 12);
|
||||
}
|
||||
|
||||
#if CF_AES_ENCRYPT_ONLY == 0
|
||||
static const uint8_t S_inv[256] =
|
||||
{
|
||||
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81,
|
||||
0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e,
|
||||
0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23,
|
||||
0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66,
|
||||
0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72,
|
||||
0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65,
|
||||
0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46,
|
||||
0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
|
||||
0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca,
|
||||
0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91,
|
||||
0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6,
|
||||
0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8,
|
||||
0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f,
|
||||
0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2,
|
||||
0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8,
|
||||
0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
|
||||
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93,
|
||||
0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb,
|
||||
0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6,
|
||||
0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d};
|
||||
|
||||
static void inv_sub_block (uint32_t state[4]) {
|
||||
state[0] = sub_word (state[0], S_inv);
|
||||
state[1] = sub_word (state[1], S_inv);
|
||||
state[2] = sub_word (state[2], S_inv);
|
||||
state[3] = sub_word (state[3], S_inv);
|
||||
}
|
||||
|
||||
static void inv_shift_rows (uint32_t state[4]) {
|
||||
uint32_t u, v, x, y;
|
||||
|
||||
u = word4 (byte (state[0], 0),
|
||||
byte (state[3], 1),
|
||||
byte (state[2], 2),
|
||||
byte (state[1], 3));
|
||||
|
||||
v = word4 (byte (state[1], 0),
|
||||
byte (state[0], 1),
|
||||
byte (state[3], 2),
|
||||
byte (state[2], 3));
|
||||
|
||||
x = word4 (byte (state[2], 0),
|
||||
byte (state[1], 1),
|
||||
byte (state[0], 2),
|
||||
byte (state[3], 3));
|
||||
|
||||
y = word4 (byte (state[3], 0),
|
||||
byte (state[2], 1),
|
||||
byte (state[1], 2),
|
||||
byte (state[0], 3));
|
||||
|
||||
state[0] = u;
|
||||
state[1] = v;
|
||||
state[2] = x;
|
||||
state[3] = y;
|
||||
}
|
||||
|
||||
static uint32_t inv_mix_column (uint32_t x) {
|
||||
uint32_t x2 = gf_poly_mul2 (x),
|
||||
x4 = gf_poly_mul2 (x2),
|
||||
x9 = x ^ gf_poly_mul2 (x4),
|
||||
x11 = x2 ^ x9,
|
||||
x13 = x4 ^ x9;
|
||||
|
||||
return x ^ x2 ^ x13 ^ rotr32 (x11, 24) ^ rotr32 (x13, 16) ^ rotr32 (x9, 8);
|
||||
}
|
||||
|
||||
static void inv_mix_columns (uint32_t state[4]) {
|
||||
state[0] = inv_mix_column (state[0]);
|
||||
state[1] = inv_mix_column (state[1]);
|
||||
state[2] = inv_mix_column (state[2]);
|
||||
state[3] = inv_mix_column (state[3]);
|
||||
}
|
||||
|
||||
void cf_aes_decrypt (const cf_aes_context *ctx,
|
||||
const uint8_t in[AES_BLOCKSZ],
|
||||
uint8_t out[AES_BLOCKSZ]) {
|
||||
assert (ctx->rounds == AES128_ROUNDS ||
|
||||
ctx->rounds == AES192_ROUNDS ||
|
||||
ctx->rounds == AES256_ROUNDS);
|
||||
|
||||
uint32_t state[4] = {
|
||||
read32_be (in + 0),
|
||||
read32_be (in + 4),
|
||||
read32_be (in + 8),
|
||||
read32_be (in + 12)};
|
||||
|
||||
const uint32_t *round_keys = &ctx->ks[ctx->rounds << 2];
|
||||
add_round_key (state, round_keys);
|
||||
round_keys -= 4;
|
||||
|
||||
for (uint32_t round = ctx->rounds - 1; round != 0; round--) {
|
||||
inv_shift_rows (state);
|
||||
inv_sub_block (state);
|
||||
add_round_key (state, round_keys);
|
||||
inv_mix_columns (state);
|
||||
round_keys -= 4;
|
||||
}
|
||||
|
||||
inv_shift_rows (state);
|
||||
inv_sub_block (state);
|
||||
add_round_key (state, round_keys);
|
||||
|
||||
write32_be (state[0], out + 0);
|
||||
write32_be (state[1], out + 4);
|
||||
write32_be (state[2], out + 8);
|
||||
write32_be (state[3], out + 12);
|
||||
}
|
||||
#else
|
||||
void cf_aes_decrypt (const cf_aes_context *ctx,
|
||||
const uint8_t in[AES_BLOCKSZ],
|
||||
uint8_t out[AES_BLOCKSZ]) {
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
|
||||
void cf_aes_finish (cf_aes_context *ctx) {
|
||||
mem_clean (ctx, sizeof *ctx);
|
||||
}
|
||||
|
||||
const cf_prp cf_aes = {
|
||||
.blocksz = AES_BLOCKSZ,
|
||||
.encrypt = (cf_prp_block)cf_aes_encrypt,
|
||||
.decrypt = (cf_prp_block)cf_aes_decrypt};
|
||||
|
||||
int aes_encrypt_ecb (const uint8_t *key, const uint8_t keyLen, const uint8_t *input, size_t ilen,
|
||||
uint8_t *output) {
|
||||
if (ilen % 16 != 0)
|
||||
return -1;
|
||||
|
||||
cf_aes_context ctx;
|
||||
cf_aes_init (&ctx, key, keyLen);
|
||||
|
||||
for (size_t i = 0; i < ilen; i += 16) {
|
||||
cf_aes_encrypt (&ctx, input + i, output + i);
|
||||
}
|
||||
|
||||
cf_aes_finish (&ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aes_decrypt_ecb (const uint8_t *key, const uint8_t keyLen, const uint8_t *input, size_t ilen,
|
||||
uint8_t *output) {
|
||||
if (ilen % 16 != 0)
|
||||
return -1;
|
||||
|
||||
cf_aes_context ctx;
|
||||
cf_aes_init (&ctx, key, keyLen);
|
||||
|
||||
for (size_t i = 0; i < ilen; i += 16) {
|
||||
cf_aes_decrypt (&ctx, input + i, output + i);
|
||||
}
|
||||
|
||||
|
||||
cf_aes_finish (&ctx);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* cifra - embedded cryptography library
|
||||
* Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
*
|
||||
* To the extent possible under law, the author(s) have dedicated all
|
||||
* copyright and related and neighboring rights to this software to the
|
||||
* public domain worldwide. This software is distributed without any
|
||||
* warranty.
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication
|
||||
* along with this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The AES block cipher
|
||||
* ====================
|
||||
*
|
||||
* This is a small, simple implementation of AES. Key expansion is done
|
||||
* first, filling in a :c:type:`cf_aes_context`. Then encryption and
|
||||
* decryption can be performed as desired.
|
||||
*
|
||||
* Usually you don't want to use AES directly; you should use it via
|
||||
* a :doc:`block cipher mode <modes>`.
|
||||
*/
|
||||
|
||||
#ifndef AES_H
|
||||
#define AES_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "prp.h"
|
||||
|
||||
/* .. c:macro:: AES_BLOCKSZ
|
||||
* AES has a 128-bit block size. This quantity is in bytes.
|
||||
*/
|
||||
#define AES_BLOCKSZ 16
|
||||
|
||||
/* --- Size configuration --- */
|
||||
|
||||
/* .. c:macro:: AES128_ROUNDS
|
||||
* .. c:macro:: AES192_ROUNDS
|
||||
* .. c:macro:: AES256_ROUNDS
|
||||
*
|
||||
* Round counts for different key sizes.
|
||||
*/
|
||||
#define AES128_ROUNDS 10
|
||||
#define AES192_ROUNDS 12
|
||||
#define AES256_ROUNDS 14
|
||||
|
||||
/* .. c:macro:: CF_AES_MAXROUNDS
|
||||
*
|
||||
* You can reduce the maximum number of rounds this implementation
|
||||
* supports. This reduces the storage needed by :c:type:`cf_aes_context`.
|
||||
*
|
||||
* The default is :c:macro:`AES256_ROUNDS` and is good for all key
|
||||
* sizes.
|
||||
*/
|
||||
#ifndef CF_AES_MAXROUNDS
|
||||
# define CF_AES_MAXROUNDS AES256_ROUNDS
|
||||
#endif
|
||||
|
||||
/* .. c:macro:: CF_AES_ENCRYPT_ONLY
|
||||
*
|
||||
* Define this to 1 if you don't need to decrypt anything.
|
||||
* This saves space. :c:func:`cf_aes_decrypt` calls `abort(3)`.
|
||||
*/
|
||||
#ifndef CF_AES_ENCRYPT_ONLY
|
||||
# define CF_AES_ENCRYPT_ONLY 0
|
||||
#endif
|
||||
|
||||
/* .. c:type:: cf_aes_context
|
||||
* This type represents an expanded AES key. Create one
|
||||
* using :c:func:`cf_aes_init`, make use of one using
|
||||
* :c:func:`cf_aes_encrypt` or :c:func:`cf_aes_decrypt`.
|
||||
*
|
||||
* The contents of this structure are equivalent to the
|
||||
* original key material. You should clean the
|
||||
* contents of this structure with :c:func:`cf_aes_finish`
|
||||
* when you're done.
|
||||
*
|
||||
* .. c:member:: cf_aes_context.rounds
|
||||
*
|
||||
* Number of rounds to use, set by :c:func:`cf_aes_init`.
|
||||
*
|
||||
* This depends on the original key size, and will be
|
||||
* :c:macro:`AES128_ROUNDS`, :c:macro:`AES192_ROUNDS` or
|
||||
* :c:macro:`AES256_ROUNDS`.
|
||||
*
|
||||
* .. c:member:: cf_aes_context.ks
|
||||
*
|
||||
* Expanded key material. Filled in by :c:func:`cf_aes_init`.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint32_t rounds;
|
||||
uint32_t ks[AES_BLOCKSZ / 4 * (CF_AES_MAXROUNDS + 1)];
|
||||
} cf_aes_context;
|
||||
|
||||
/* .. c:function:: $DECL
|
||||
* This function does AES key expansion. It destroys
|
||||
* existing contents of :c:data:`ctx`.
|
||||
*
|
||||
* :param ctx: expanded key context, filled in by this function.
|
||||
* :param key: pointer to key material, of :c:data:`nkey` bytes.
|
||||
* :param nkey: length of key material. Must be `16`, `24` or `32`.
|
||||
*/
|
||||
extern void cf_aes_init(cf_aes_context *ctx,
|
||||
const uint8_t *key,
|
||||
size_t nkey);
|
||||
|
||||
/* .. c:function:: $DECL
|
||||
* Encrypts the given block, from :c:data:`in` to :c:data:`out`.
|
||||
* These may alias.
|
||||
*
|
||||
* Fails at runtime if :c:data:`ctx` is invalid.
|
||||
*
|
||||
* :param ctx: expanded key context
|
||||
* :param in: input block (read)
|
||||
* :param out: output block (written)
|
||||
*/
|
||||
extern void cf_aes_encrypt(const cf_aes_context *ctx,
|
||||
const uint8_t in[AES_BLOCKSZ],
|
||||
uint8_t out[AES_BLOCKSZ]);
|
||||
|
||||
/* .. c:function:: $DECL
|
||||
* Decrypts the given block, from :c:data:`in` to :c:data:`out`.
|
||||
* These may alias.
|
||||
*
|
||||
* Fails at runtime if :c:data:`ctx` is invalid.
|
||||
*
|
||||
* :param ctx: expanded key context
|
||||
* :param in: input block (read)
|
||||
* :param out: output block (written)
|
||||
*/
|
||||
extern void cf_aes_decrypt(const cf_aes_context *ctx,
|
||||
const uint8_t in[AES_BLOCKSZ],
|
||||
uint8_t out[AES_BLOCKSZ]);
|
||||
|
||||
/* .. c:function:: $DECL
|
||||
* Erase scheduled key material.
|
||||
*
|
||||
* Call this when you're done to erase the round keys. */
|
||||
extern void cf_aes_finish(cf_aes_context *ctx);
|
||||
|
||||
/* .. c:var:: const cf_prp cf_aes
|
||||
* Abstract interface to AES. See :c:type:`cf_prp` for
|
||||
* more information. */
|
||||
extern const cf_prp cf_aes;
|
||||
|
||||
int aes_decrypt_ecb (const uint8_t *key, const uint8_t keyLen, const uint8_t *input, size_t ilen,
|
||||
uint8_t *output);
|
||||
|
||||
int aes_encrypt_ecb (const uint8_t *key, const uint8_t keyLen, const uint8_t *input, size_t ilen,
|
||||
uint8_t *output);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* cifra - embedded cryptography library
|
||||
* Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
*
|
||||
* To the extent possible under law, the author(s) have dedicated all
|
||||
* copyright and related and neighboring rights to this software to the
|
||||
* public domain worldwide. This software is distributed without any
|
||||
* warranty.
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication
|
||||
* along with this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#ifndef BITOPS_H
|
||||
#define BITOPS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* Assorted bitwise and common operations used in ciphers. */
|
||||
|
||||
/** Circularly rotate right x by n bits.
|
||||
* 0 > n > 32. */
|
||||
static inline uint32_t rotr32(uint32_t x, unsigned n)
|
||||
{
|
||||
return (x >> n) | (x << (32 - n));
|
||||
}
|
||||
|
||||
/** Circularly rotate left x by n bits.
|
||||
* 0 > n > 32. */
|
||||
static inline uint32_t rotl32(uint32_t x, unsigned n)
|
||||
{
|
||||
return (x << n) | (x >> (32 - n));
|
||||
}
|
||||
|
||||
/** Circularly rotate right x by n bits.
|
||||
* 0 > n > 64. */
|
||||
static inline uint64_t rotr64(uint64_t x, unsigned n)
|
||||
{
|
||||
return (x >> n) | (x << (64 - n));
|
||||
}
|
||||
|
||||
/** Circularly rotate left x by n bits.
|
||||
* 0 > n > 64. */
|
||||
static inline uint64_t rotl64(uint64_t x, unsigned n)
|
||||
{
|
||||
return (x << n) | (x >> (64 - n));
|
||||
}
|
||||
|
||||
/** Read 4 bytes from buf, as a 32-bit big endian quantity. */
|
||||
static inline uint32_t read32_be(const uint8_t buf[4])
|
||||
{
|
||||
return (buf[0] << 24) |
|
||||
(buf[1] << 16) |
|
||||
(buf[2] << 8) |
|
||||
(buf[3]);
|
||||
}
|
||||
|
||||
/** Read 4 bytes from buf, as a 32-bit little endian quantity. */
|
||||
static inline uint32_t read32_le(const uint8_t buf[4])
|
||||
{
|
||||
return (buf[3] << 24) |
|
||||
(buf[2] << 16) |
|
||||
(buf[1] << 8) |
|
||||
(buf[0]);
|
||||
}
|
||||
|
||||
/** Read 8 bytes from buf, as a 64-bit big endian quantity. */
|
||||
static inline uint64_t read64_be(const uint8_t buf[8])
|
||||
{
|
||||
uint32_t hi = read32_be(buf),
|
||||
lo = read32_be(buf + 4);
|
||||
return ((uint64_t)hi) << 32 |
|
||||
lo;
|
||||
}
|
||||
|
||||
/** Read 8 bytes from buf, as a 64-bit little endian quantity. */
|
||||
static inline uint64_t read64_le(const uint8_t buf[8])
|
||||
{
|
||||
uint32_t hi = read32_le(buf + 4),
|
||||
lo = read32_le(buf);
|
||||
return ((uint64_t)hi) << 32 |
|
||||
lo;
|
||||
}
|
||||
|
||||
/** Encode v as a 32-bit big endian quantity into buf. */
|
||||
static inline void write32_be(uint32_t v, uint8_t buf[4])
|
||||
{
|
||||
*buf++ = (v >> 24) & 0xff;
|
||||
*buf++ = (v >> 16) & 0xff;
|
||||
*buf++ = (v >> 8) & 0xff;
|
||||
*buf = v & 0xff;
|
||||
}
|
||||
|
||||
/** Encode v as a 32-bit little endian quantity into buf. */
|
||||
static inline void write32_le(uint32_t v, uint8_t buf[4])
|
||||
{
|
||||
*buf++ = v & 0xff;
|
||||
*buf++ = (v >> 8) & 0xff;
|
||||
*buf++ = (v >> 16) & 0xff;
|
||||
*buf = (v >> 24) & 0xff;
|
||||
}
|
||||
|
||||
/** Encode v as a 64-bit big endian quantity into buf. */
|
||||
static inline void write64_be(uint64_t v, uint8_t buf[8])
|
||||
{
|
||||
*buf++ = (v >> 56) & 0xff;
|
||||
*buf++ = (v >> 48) & 0xff;
|
||||
*buf++ = (v >> 40) & 0xff;
|
||||
*buf++ = (v >> 32) & 0xff;
|
||||
*buf++ = (v >> 24) & 0xff;
|
||||
*buf++ = (v >> 16) & 0xff;
|
||||
*buf++ = (v >> 8) & 0xff;
|
||||
*buf = v & 0xff;
|
||||
}
|
||||
|
||||
/** Encode v as a 64-bit little endian quantity into buf. */
|
||||
static inline void write64_le(uint64_t v, uint8_t buf[8])
|
||||
{
|
||||
*buf++ = v & 0xff;
|
||||
*buf++ = (v >> 8) & 0xff;
|
||||
*buf++ = (v >> 16) & 0xff;
|
||||
*buf++ = (v >> 24) & 0xff;
|
||||
*buf++ = (v >> 32) & 0xff;
|
||||
*buf++ = (v >> 40) & 0xff;
|
||||
*buf++ = (v >> 48) & 0xff;
|
||||
*buf = (v >> 56) & 0xff;
|
||||
}
|
||||
|
||||
/** out = in ^ b8.
|
||||
* out and in may alias. */
|
||||
static inline void xor_b8(uint8_t *out, const uint8_t *in, uint8_t b8, size_t len)
|
||||
{
|
||||
for (size_t i = 0; i < len; i++)
|
||||
out[i] = in[i] ^ b8;
|
||||
}
|
||||
|
||||
/** out = x ^ y.
|
||||
* out, x and y may alias. */
|
||||
static inline void xor_bb(uint8_t *out, const uint8_t *x, const uint8_t *y, size_t len)
|
||||
{
|
||||
for (size_t i = 0; i < len; i++)
|
||||
out[i] = x[i] ^ y[i];
|
||||
}
|
||||
|
||||
/* out ^= x
|
||||
* out and x may alias. */
|
||||
static inline void xor_words(uint32_t *out, const uint32_t *x, size_t nwords)
|
||||
{
|
||||
for (size_t i = 0; i < nwords; i++)
|
||||
out[i] ^= x[i];
|
||||
}
|
||||
|
||||
/** Produce 0xffffffff if x == y, zero otherwise, without branching. */
|
||||
static inline uint32_t mask_u32(uint32_t x, uint32_t y)
|
||||
{
|
||||
uint32_t diff = x ^ y;
|
||||
uint32_t diff_is_zero = ~diff & (diff - 1);
|
||||
return - (diff_is_zero >> 31);
|
||||
}
|
||||
|
||||
/** Product 0xff if x == y, zero otherwise, without branching. */
|
||||
static inline uint8_t mask_u8(uint32_t x, uint32_t y)
|
||||
{
|
||||
uint32_t diff = x ^ y;
|
||||
uint8_t diff_is_zero = ~diff & (diff - 1);
|
||||
return - (diff_is_zero >> 7);
|
||||
}
|
||||
|
||||
/** Select the ith entry from the given table of n values, in a side channel-silent
|
||||
* way. */
|
||||
static inline uint32_t select_u32(uint32_t i, volatile const uint32_t *tab, uint32_t n)
|
||||
{
|
||||
uint32_t r = 0;
|
||||
|
||||
for (uint32_t ii = 0; ii < n; ii++)
|
||||
{
|
||||
uint32_t mask = mask_u32(i, ii);
|
||||
r = (r & ~mask) | (tab[ii] & mask);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Select the ith entry from the given table of n values, in a side channel-silent
|
||||
* way. */
|
||||
static inline uint8_t select_u8(uint32_t i, volatile const uint8_t *tab, uint32_t n)
|
||||
{
|
||||
uint8_t r = 0;
|
||||
|
||||
for (uint32_t ii = 0; ii < n; ii++)
|
||||
{
|
||||
uint8_t mask = mask_u8(i, ii);
|
||||
r = (r & ~mask) | (tab[ii] & mask);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Select the ath, bth, cth and dth entries from the given table of n values,
|
||||
* placing the results into a, b, c and d. */
|
||||
static inline void select_u8x4(uint8_t *a, uint8_t *b, uint8_t *c, uint8_t *d,
|
||||
volatile const uint8_t *tab, uint32_t n)
|
||||
{
|
||||
uint8_t ra = 0,
|
||||
rb = 0,
|
||||
rc = 0,
|
||||
rd = 0;
|
||||
uint8_t mask;
|
||||
|
||||
for (uint32_t i = 0; i < n; i++)
|
||||
{
|
||||
uint8_t item = tab[i];
|
||||
|
||||
mask = mask_u8(*a, i); ra = (ra & ~mask) | (item & mask);
|
||||
mask = mask_u8(*b, i); rb = (rb & ~mask) | (item & mask);
|
||||
mask = mask_u8(*c, i); rc = (rc & ~mask) | (item & mask);
|
||||
mask = mask_u8(*d, i); rd = (rd & ~mask) | (item & mask);
|
||||
}
|
||||
|
||||
*a = ra;
|
||||
*b = rb;
|
||||
*c = rc;
|
||||
*d = rd;
|
||||
}
|
||||
|
||||
/** out ^= if0 or if1, depending on the value of bit. */
|
||||
static inline void select_xor128(uint32_t out[4],
|
||||
const uint32_t if0[4],
|
||||
const uint32_t if1[4],
|
||||
uint8_t bit)
|
||||
{
|
||||
uint32_t mask1 = mask_u32(bit, 1);
|
||||
uint32_t mask0 = ~mask1;
|
||||
|
||||
out[0] ^= (if0[0] & mask0) | (if1[0] & mask1);
|
||||
out[1] ^= (if0[1] & mask0) | (if1[1] & mask1);
|
||||
out[2] ^= (if0[2] & mask0) | (if1[2] & mask1);
|
||||
out[3] ^= (if0[3] & mask0) | (if1[3] & mask1);
|
||||
}
|
||||
|
||||
/** Increments the integer stored at v (of non-zero length len)
|
||||
* with the least significant byte first. */
|
||||
static inline void incr_le(uint8_t *v, size_t len)
|
||||
{
|
||||
size_t i = 0;
|
||||
while (1)
|
||||
{
|
||||
if (++v[i] != 0)
|
||||
return;
|
||||
i++;
|
||||
if (i == len)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/** Increments the integer stored at v (of non-zero length len)
|
||||
* with the most significant byte last. */
|
||||
static inline void incr_be(uint8_t *v, size_t len)
|
||||
{
|
||||
len--;
|
||||
while (1)
|
||||
{
|
||||
if (++v[len] != 0)
|
||||
return;
|
||||
if (len == 0)
|
||||
return;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
/** Copies len bytes from in to out, with in shifted left by offset bits
|
||||
* to the right. */
|
||||
static inline void copy_bytes_unaligned(uint8_t *out, const uint8_t *in, size_t len, uint8_t offset)
|
||||
{
|
||||
uint8_t byte_off = offset / 8;
|
||||
uint8_t bit_off = offset & 7;
|
||||
uint8_t rmask = (1 << bit_off) - 1;
|
||||
uint8_t lmask = ~rmask;
|
||||
|
||||
for (size_t i = 0; i < len; i++)
|
||||
{
|
||||
out[i] = (in[i + byte_off] << bit_off) & lmask;
|
||||
out[i] |= (in[i + byte_off + 1] >> (8 - bit_off)) & rmask;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t count_trailing_zeroes(uint32_t x)
|
||||
{
|
||||
return (uint32_t) __builtin_ctzl(x);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* cifra - embedded cryptography library
|
||||
* Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
*
|
||||
* To the extent possible under law, the author(s) have dedicated all
|
||||
* copyright and related and neighboring rights to this software to the
|
||||
* public domain worldwide. This software is distributed without any
|
||||
* warranty.
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication
|
||||
* along with this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#include "blockwise.h"
|
||||
#include "bitops.h"
|
||||
#include "handy.h"
|
||||
#include "tassert.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void cf_blockwise_accumulate(uint8_t *partial, size_t *npartial, size_t nblock,
|
||||
const void *inp, size_t nbytes,
|
||||
cf_blockwise_in_fn process,
|
||||
void *ctx)
|
||||
{
|
||||
cf_blockwise_accumulate_final(partial, npartial, nblock,
|
||||
inp, nbytes,
|
||||
process, process, ctx);
|
||||
}
|
||||
|
||||
void cf_blockwise_accumulate_final(uint8_t *partial, size_t *npartial, size_t nblock,
|
||||
const void *inp, size_t nbytes,
|
||||
cf_blockwise_in_fn process,
|
||||
cf_blockwise_in_fn process_final,
|
||||
void *ctx)
|
||||
{
|
||||
const uint8_t *bufin = inp;
|
||||
assert(partial && *npartial < nblock);
|
||||
assert(inp || !nbytes);
|
||||
assert(process && ctx);
|
||||
|
||||
/* If we have partial data, copy in to buffer. */
|
||||
if (*npartial && nbytes)
|
||||
{
|
||||
size_t space = nblock - *npartial;
|
||||
size_t taken = MIN(space, nbytes);
|
||||
|
||||
memcpy(partial + *npartial, bufin, taken);
|
||||
|
||||
bufin += taken;
|
||||
nbytes -= taken;
|
||||
*npartial += taken;
|
||||
|
||||
/* If that gives us a full block, process it. */
|
||||
if (*npartial == nblock)
|
||||
{
|
||||
if (nbytes == 0)
|
||||
process_final(ctx, partial);
|
||||
else
|
||||
process(ctx, partial);
|
||||
*npartial = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* now nbytes < nblock or *npartial == 0. */
|
||||
|
||||
/* If we have a full block of data, process it directly. */
|
||||
while (nbytes >= nblock)
|
||||
{
|
||||
/* Partial buffer must be empty, or we're ignoring extant data */
|
||||
assert(*npartial == 0);
|
||||
|
||||
if (nbytes == nblock)
|
||||
process_final(ctx, bufin);
|
||||
else
|
||||
process(ctx, bufin);
|
||||
bufin += nblock;
|
||||
nbytes -= nblock;
|
||||
}
|
||||
|
||||
/* Finally, if we have remaining data, buffer it. */
|
||||
while (nbytes)
|
||||
{
|
||||
size_t space = nblock - *npartial;
|
||||
size_t taken = MIN(space, nbytes);
|
||||
|
||||
memcpy(partial + *npartial, bufin, taken);
|
||||
|
||||
bufin += taken;
|
||||
nbytes -= taken;
|
||||
*npartial += taken;
|
||||
|
||||
/* If we started with *npartial, we must have copied it
|
||||
* in first. */
|
||||
assert(*npartial < nblock);
|
||||
}
|
||||
}
|
||||
|
||||
void cf_blockwise_xor(uint8_t *partial, size_t *npartial, size_t nblock,
|
||||
const void *inp, void *outp, size_t nbytes,
|
||||
cf_blockwise_out_fn process, void *ctx)
|
||||
{
|
||||
const uint8_t *inb = inp;
|
||||
uint8_t *outb = outp;
|
||||
|
||||
assert(partial && *npartial < nblock);
|
||||
assert(inp || !nbytes);
|
||||
assert(process && ctx);
|
||||
|
||||
while (nbytes)
|
||||
{
|
||||
/* If we're out of material, and need more, produce a block. */
|
||||
if (*npartial == 0)
|
||||
{
|
||||
process(ctx, partial);
|
||||
*npartial = nblock;
|
||||
}
|
||||
|
||||
size_t offset = nblock - *npartial;
|
||||
size_t taken = MIN(*npartial, nbytes);
|
||||
xor_bb(outb, inb, partial + offset, taken);
|
||||
*npartial -= taken;
|
||||
nbytes -= taken;
|
||||
outb += taken;
|
||||
inb += taken;
|
||||
}
|
||||
}
|
||||
|
||||
void cf_blockwise_acc_byte(uint8_t *partial, size_t *npartial,
|
||||
size_t nblock,
|
||||
uint8_t byte, size_t nbytes,
|
||||
cf_blockwise_in_fn process,
|
||||
void *ctx)
|
||||
{
|
||||
/* only memset the whole of the block once */
|
||||
int filled = 0;
|
||||
|
||||
while (nbytes)
|
||||
{
|
||||
size_t start = *npartial;
|
||||
size_t count = MIN(nbytes, nblock - start);
|
||||
|
||||
if (!filled)
|
||||
memset(partial + start, byte, count);
|
||||
|
||||
if (start == 0 && count == nblock)
|
||||
filled = 1;
|
||||
|
||||
if (start + count == nblock)
|
||||
{
|
||||
process(ctx, partial);
|
||||
*npartial = 0;
|
||||
} else {
|
||||
*npartial += count;
|
||||
}
|
||||
|
||||
nbytes -= count;
|
||||
}
|
||||
}
|
||||
|
||||
void cf_blockwise_acc_pad(uint8_t *partial, size_t *npartial,
|
||||
size_t nblock,
|
||||
uint8_t fbyte, uint8_t mbyte, uint8_t lbyte,
|
||||
size_t nbytes,
|
||||
cf_blockwise_in_fn process,
|
||||
void *ctx)
|
||||
{
|
||||
|
||||
switch (nbytes)
|
||||
{
|
||||
case 0: break;
|
||||
case 1: fbyte ^= lbyte;
|
||||
cf_blockwise_accumulate(partial, npartial, nblock, &fbyte, 1, process, ctx);
|
||||
break;
|
||||
case 2:
|
||||
cf_blockwise_accumulate(partial, npartial, nblock, &fbyte, 1, process, ctx);
|
||||
cf_blockwise_accumulate(partial, npartial, nblock, &lbyte, 1, process, ctx);
|
||||
break;
|
||||
default:
|
||||
cf_blockwise_accumulate(partial, npartial, nblock, &fbyte, 1, process, ctx);
|
||||
|
||||
/* If the middle and last bytes differ, then process the last byte separately.
|
||||
* Otherwise, just extend the middle block size. */
|
||||
if (lbyte != mbyte)
|
||||
{
|
||||
cf_blockwise_acc_byte(partial, npartial, nblock, mbyte, nbytes - 2, process, ctx);
|
||||
cf_blockwise_accumulate(partial, npartial, nblock, &lbyte, 1, process, ctx);
|
||||
} else {
|
||||
cf_blockwise_acc_byte(partial, npartial, nblock, mbyte, nbytes - 1, process, ctx);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* cifra - embedded cryptography library
|
||||
* Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
*
|
||||
* To the extent possible under law, the author(s) have dedicated all
|
||||
* copyright and related and neighboring rights to this software to the
|
||||
* public domain worldwide. This software is distributed without any
|
||||
* warranty.
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication
|
||||
* along with this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#ifndef BLOCKWISE_H
|
||||
#define BLOCKWISE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* Processing function for cf_blockwise_accumulate. */
|
||||
typedef void (*cf_blockwise_in_fn)(void *ctx, const uint8_t *data);
|
||||
|
||||
/* Processing function for cf_blockwise_xor. */
|
||||
typedef void (*cf_blockwise_out_fn)(void *ctx, uint8_t *data);
|
||||
|
||||
/* This function manages the common abstraction of accumulating input in
|
||||
* a buffer, and processing it when a full block is available.
|
||||
*
|
||||
* partial is the buffer (maintained by the caller)
|
||||
* on entry, npartial is the currently valid count of used bytes on
|
||||
* the front of partial.
|
||||
* on exit, npartial is updated to reflect the status of partial.
|
||||
* nblock is the blocksize to accumulate -- partial must be at least
|
||||
* this long!
|
||||
* input is the new data to process, of length nbytes.
|
||||
* process is the processing function, passed ctx and a pointer
|
||||
* to the data to process (always exactly nblock bytes long!)
|
||||
* which may not neccessarily be the same as partial.
|
||||
*/
|
||||
void cf_blockwise_accumulate(uint8_t *partial, size_t *npartial,
|
||||
size_t nblock,
|
||||
const void *input, size_t nbytes,
|
||||
cf_blockwise_in_fn process,
|
||||
void *ctx);
|
||||
|
||||
/* This function manages the common abstraction of accumulating input in
|
||||
* a buffer, and processing it when a full block is available.
|
||||
* This version supports calling a different processing function for
|
||||
* the last block.
|
||||
*
|
||||
* partial is the buffer (maintained by the caller)
|
||||
* on entry, npartial is the currently valid count of used bytes on
|
||||
* the front of partial.
|
||||
* on exit, npartial is updated to reflect the status of partial.
|
||||
* nblock is the blocksize to accumulate -- partial must be at least
|
||||
* this long!
|
||||
* input is the new data to process, of length nbytes.
|
||||
* process is the processing function, passed ctx and a pointer
|
||||
* to the data to process (always exactly nblock bytes long!)
|
||||
* which may not neccessarily be the same as partial.
|
||||
* process_final is called last (but may not be called at all if
|
||||
* all input is buffered).
|
||||
*/
|
||||
void cf_blockwise_accumulate_final(uint8_t *partial, size_t *npartial,
|
||||
size_t nblock,
|
||||
const void *input, size_t nbytes,
|
||||
cf_blockwise_in_fn process,
|
||||
cf_blockwise_in_fn process_final,
|
||||
void *ctx);
|
||||
|
||||
/* This function manages XORing an input stream with a keystream
|
||||
* to produce an output stream. The keystream is produced in blocks
|
||||
* (ala a block cipher in counter mode).
|
||||
*
|
||||
* partial is the keystream buffer (maintained by the caller)
|
||||
* on entry, *npartial is the currently valid count of bytes in partial:
|
||||
* unused bytes are at the *end*. So *npartial = 4 means the last four
|
||||
* bytes of partial are usable as keystream.
|
||||
* on exit, npartial is updated to reflect the new state of partial.
|
||||
* nblock is the blocksize to accumulate -- partial must be at least
|
||||
* this long!
|
||||
* input is the new data to process, of length nbytes.
|
||||
* output is where to write input xored with the keystream -- also length
|
||||
* nbytes.
|
||||
* process is the processing function, passed ctx and partial which it
|
||||
* should fill with fresh key stream.
|
||||
*/
|
||||
void cf_blockwise_xor(uint8_t *partial, size_t *npartial,
|
||||
size_t nblock,
|
||||
const void *input, void *output, size_t nbytes,
|
||||
cf_blockwise_out_fn newblock,
|
||||
void *ctx);
|
||||
|
||||
/* This function processes a single byte a number of times. It's useful
|
||||
* for padding, and more efficient than calling cf_blockwise_accumulate
|
||||
* a bunch of times.
|
||||
*
|
||||
* partial is the buffer (maintained by the caller)
|
||||
* on entry, npartial is the currently valid count of used bytes on
|
||||
* the front of partial.
|
||||
* on exit, npartial is updated to reflect the status of partial.
|
||||
* nblock is the blocksize to accumulate -- partial must be at least
|
||||
* this long!
|
||||
* process is the processing function, passed ctx and a pointer
|
||||
* to the data to process (always exactly nblock bytes long!)
|
||||
* which may not neccessarily be the same as partial.
|
||||
* byte is the byte to process, nbytes times.
|
||||
*/
|
||||
void cf_blockwise_acc_byte(uint8_t *partial, size_t *npartial,
|
||||
size_t nblock,
|
||||
uint8_t byte, size_t nbytes,
|
||||
cf_blockwise_in_fn process,
|
||||
void *ctx);
|
||||
|
||||
/* This function attempts to process patterns of bytes common in
|
||||
* block cipher padding.
|
||||
*
|
||||
* This takes three bytes:
|
||||
* - a first byte, fbyte,
|
||||
* - a middle byte, mbyte,
|
||||
* - a last byte, lbyte.
|
||||
*
|
||||
* If nbytes is zero, nothing happens.
|
||||
* If nbytes is one, the byte fbyte ^ lbyte is processed.
|
||||
* If nbytes is two, the fbyte then lbyte are processed.
|
||||
* If nbytes is three or more, fbyte, then one or more mbytes, then fbyte
|
||||
* is processed.
|
||||
*
|
||||
* partial is the buffer (maintained by the caller)
|
||||
* on entry, npartial is the currently valid count of used bytes on
|
||||
* the front of partial.
|
||||
* on exit, npartial is updated to reflect the status of partial.
|
||||
* nblock is the blocksize to accumulate -- partial must be at least
|
||||
* this long!
|
||||
* process is the processing function, passed ctx and a pointer
|
||||
* to the data to process (always exactly nblock bytes long!)
|
||||
* which may not neccessarily be the same as partial.
|
||||
*/
|
||||
void cf_blockwise_acc_pad(uint8_t *partial, size_t *npartial,
|
||||
size_t nblock,
|
||||
uint8_t fbyte, uint8_t mbyte, uint8_t lbyte,
|
||||
size_t nbytes,
|
||||
cf_blockwise_in_fn process,
|
||||
void *ctx);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* cifra - embedded cryptography library
|
||||
* Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
*
|
||||
* To the extent possible under law, the author(s) have dedicated all
|
||||
* copyright and related and neighboring rights to this software to the
|
||||
* public domain worldwide. This software is distributed without any
|
||||
* warranty.
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication
|
||||
* along with this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#ifndef CF_CONFIG_H
|
||||
#define CF_CONFIG_H
|
||||
|
||||
/**
|
||||
* Library configuration
|
||||
* =====================
|
||||
*/
|
||||
|
||||
/* .. c:macro:: CF_SIDE_CHANNEL_PROTECTION
|
||||
* Define this as 1 if you need all available side channel protections.
|
||||
* **This option may alter the ABI**.
|
||||
*
|
||||
* This has a non-trivial performance penalty. Where a
|
||||
* side-channel free option is cheap or free (like checking
|
||||
* a MAC) this is always done in a side-channel free way.
|
||||
*
|
||||
* The default is **on** for all available protections.
|
||||
*/
|
||||
#ifndef CF_SIDE_CHANNEL_PROTECTION
|
||||
# define CF_SIDE_CHANNEL_PROTECTION 1
|
||||
#endif
|
||||
|
||||
/* .. c:macro:: CF_TIME_SIDE_CHANNEL_PROTECTION
|
||||
* Define this as 1 if you need timing/branch prediction side channel
|
||||
* protection.
|
||||
*
|
||||
* You probably want this. The default is on. */
|
||||
#ifndef CF_TIME_SIDE_CHANNEL_PROTECTION
|
||||
# define CF_TIME_SIDE_CHANNEL_PROTECTION CF_SIDE_CHANNEL_PROTECTION
|
||||
#endif
|
||||
|
||||
/* .. c:macro:: CF_CACHE_SIDE_CHANNEL_PROTECTION
|
||||
* Define this as 1 if you need cache side channel protection.
|
||||
*
|
||||
* If you have a microcontroller with no cache, you can turn this off
|
||||
* without negative effects.
|
||||
*
|
||||
* The default is on. This will have some performance impact,
|
||||
* especially on AES.
|
||||
*/
|
||||
#ifndef CF_CACHE_SIDE_CHANNEL_PROTECTION
|
||||
# define CF_CACHE_SIDE_CHANNEL_PROTECTION CF_SIDE_CHANNEL_PROTECTION
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* cifra - embedded cryptography library
|
||||
* Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
*
|
||||
* To the extent possible under law, the author(s) have dedicated all
|
||||
* copyright and related and neighboring rights to this software to the
|
||||
* public domain worldwide. This software is distributed without any
|
||||
* warranty.
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication
|
||||
* along with this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#include "chash.h"
|
||||
#include "handy.h"
|
||||
#include "tassert.h"
|
||||
|
||||
void cf_hash(const cf_chash *h, const void *m, size_t nm, uint8_t *out)
|
||||
{
|
||||
cf_chash_ctx ctx;
|
||||
assert(h);
|
||||
h->init(&ctx);
|
||||
h->update(&ctx, m, nm);
|
||||
h->digest(&ctx, out);
|
||||
mem_clean(&ctx, sizeof ctx);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* cifra - embedded cryptography library
|
||||
* Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
*
|
||||
* To the extent possible under law, the author(s) have dedicated all
|
||||
* copyright and related and neighboring rights to this software to the
|
||||
* public domain worldwide. This software is distributed without any
|
||||
* warranty.
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication
|
||||
* along with this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#ifndef CHASH_H
|
||||
#define CHASH_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* General hash function description
|
||||
* =================================
|
||||
* This allows us to make use of hash functions without depending
|
||||
* on a specific one. This is useful in implementing, for example,
|
||||
* :doc:`HMAC <hmac>`.
|
||||
*/
|
||||
|
||||
/* .. c:type:: cf_chash_init
|
||||
* Hashing initialisation function type.
|
||||
*
|
||||
* Functions of this type should initialise the context in preparation
|
||||
* for hashing a message with `cf_chash_update` functions.
|
||||
*
|
||||
* :rtype: void
|
||||
* :param ctx: hash function-specific context structure.
|
||||
*/
|
||||
typedef void (*cf_chash_init)(void *ctx);
|
||||
|
||||
/* .. c:type:: cf_chash_update
|
||||
* Hashing data processing function type.
|
||||
*
|
||||
* Functions of this type hash `count` bytes of data at `data`,
|
||||
* updating the contents of `ctx`.
|
||||
*
|
||||
* :rtype: void
|
||||
* :param ctx: hash function-specific context structure.
|
||||
* :param data: input data to hash.
|
||||
* :param count: number of bytes to hash.
|
||||
*/
|
||||
typedef void (*cf_chash_update)(void *ctx, const void *data, size_t count);
|
||||
|
||||
/* .. c:type:: cf_chash_digest
|
||||
* Hashing completion function type.
|
||||
*
|
||||
* Functions of this type complete a hashing operation,
|
||||
* writing :c:member:`cf_chash.hashsz` bytes to `hash`.
|
||||
*
|
||||
* This function does not change `ctx` -- any padding which needs doing
|
||||
* must be done seperately (in a copy of `ctx`, say).
|
||||
*
|
||||
* This means you can interlave `_update` and `_digest` calls to
|
||||
* learn `H(A)` and `H(A || B)` without hashing `A` twice.
|
||||
*
|
||||
* :rtype: void
|
||||
* :param ctx: hash function-specific context structure.
|
||||
* :param hash: location to write hash result.
|
||||
*/
|
||||
typedef void (*cf_chash_digest)(const void *ctx, uint8_t *hash);
|
||||
|
||||
/* .. c:type:: cf_chash
|
||||
* This type describes an incremental hash function in an abstract way.
|
||||
*
|
||||
* .. c:member:: cf_chash.hashsz
|
||||
* The hash function's output, in bytes.
|
||||
*
|
||||
* .. c:member:: cf_chash.blocksz
|
||||
* The hash function's internal block size, in bytes.
|
||||
*
|
||||
* .. c:member:: cf_chash.init
|
||||
* Context initialisation function.
|
||||
*
|
||||
* .. c:member:: cf_chash:update
|
||||
* Data processing function.
|
||||
*
|
||||
* .. c:member:: cf_chash:digest
|
||||
* Completion function.
|
||||
*
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
size_t hashsz;
|
||||
size_t blocksz;
|
||||
|
||||
cf_chash_init init;
|
||||
cf_chash_update update;
|
||||
cf_chash_digest digest;
|
||||
} cf_chash;
|
||||
|
||||
/* .. c:macro:: CF_CHASH_MAXCTX
|
||||
* The maximum size of a :c:type:`cf_chash_ctx`. This allows
|
||||
* use to put a structure in automatic storage that can
|
||||
* store working data for any supported hash function. */
|
||||
#define CF_CHASH_MAXCTX 390
|
||||
|
||||
/* .. c:macro:: CF_CHASH_MAXBLK
|
||||
* Maximum hash function block size (in bytes). */
|
||||
#define CF_CHASH_MAXBLK 128
|
||||
|
||||
/* .. c:macro:: CF_MAXHASH
|
||||
* Maximum hash function output (in bytes). */
|
||||
#define CF_MAXHASH 64
|
||||
|
||||
/* .. c:type:: cf_chash_ctx
|
||||
* A type usable with any `cf_chash` as a context. */
|
||||
typedef union
|
||||
{
|
||||
uint8_t ctx[CF_CHASH_MAXCTX];
|
||||
uint16_t uint16_t;
|
||||
uint32_t u32;
|
||||
uint64_t u64;
|
||||
} cf_chash_ctx;
|
||||
|
||||
/* .. c:function:: $DECL
|
||||
* One shot hashing: `out = h(m)`.
|
||||
*
|
||||
* Using the hash function `h`, `nm` bytes at `m` are hashed and `h->hashsz` bytes
|
||||
* of result is written to the buffer `out`.
|
||||
*
|
||||
* :param h: hash function description.
|
||||
* :param m: message buffer.
|
||||
* :param nm: message length.
|
||||
* :param out: hash result buffer (written).
|
||||
*/
|
||||
void cf_hash(const cf_chash *h, const void *m, size_t nm, uint8_t *out);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,86 @@
|
||||
#ifndef HANDY_H
|
||||
#define HANDY_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Handy CPP defines and C inline functions.
|
||||
*/
|
||||
|
||||
/* Evaluates to the number of items in array-type variable arr. */
|
||||
#define ARRAYCOUNT(arr) (sizeof arr / sizeof arr[0])
|
||||
|
||||
/* Normal MIN/MAX macros. Evaluate argument expressions only once. */
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) \
|
||||
({ typeof (x) __x = (x); \
|
||||
typeof (y) __y = (y); \
|
||||
__x < __y ? __x : __y; })
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(x, y) \
|
||||
({ typeof (x) __x = (x); \
|
||||
typeof (y) __y = (y); \
|
||||
__x > __y ? __x : __y; })
|
||||
#endif
|
||||
|
||||
/* Swap two values. Uses GCC type inference magic. */
|
||||
#ifndef SWAP
|
||||
#define SWAP(x, y) \
|
||||
do { \
|
||||
typeof (x) __tmp = (x); \
|
||||
(x) = (y); \
|
||||
(y) = __tmp; \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/** Stringify its argument. */
|
||||
#define STRINGIFY(x) STRINGIFY_(x)
|
||||
#define STRINGIFY_(x) #x
|
||||
|
||||
/* Error handling macros.
|
||||
*
|
||||
* These expect a zero = success, non-zero = error convention.
|
||||
*/
|
||||
|
||||
/** Error: return.
|
||||
*
|
||||
* If the expression fails, return the error from this function. */
|
||||
#define ER(expr) do { typeof (expr) err_ = (expr); if (err_) return err_; } while (0)
|
||||
|
||||
/** Error: goto.
|
||||
*
|
||||
* If the expression fails, goto x_err. Assumes defn of label
|
||||
* x_err and 'error_type err'. */
|
||||
#define EG(expr) do { err = (expr); if (err) goto x_err; } while (0)
|
||||
|
||||
/** Like memset(ptr, 0, len), but not allowed to be removed by
|
||||
* compilers. */
|
||||
static inline void mem_clean(volatile void *v, size_t len)
|
||||
{
|
||||
if (len)
|
||||
{
|
||||
memset((void *) v, 0, len);
|
||||
(void) *((volatile uint8_t *) v);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns 1 if len bytes at va equal len bytes at vb, 0 if they do not.
|
||||
* Does not leak length of common prefix through timing. */
|
||||
static inline unsigned mem_eq(const void *va, const void *vb, size_t len)
|
||||
{
|
||||
const volatile uint8_t *a = va;
|
||||
const volatile uint8_t *b = vb;
|
||||
uint8_t diff = 0;
|
||||
|
||||
while (len--)
|
||||
{
|
||||
diff |= *a++ ^ *b++;
|
||||
}
|
||||
|
||||
return !diff;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* cifra - embedded cryptography library
|
||||
* Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
*
|
||||
* To the extent possible under law, the author(s) have dedicated all
|
||||
* copyright and related and neighboring rights to this software to the
|
||||
* public domain worldwide. This software is distributed without any
|
||||
* warranty.
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication
|
||||
* along with this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#include "hmac.h"
|
||||
#include "chash.h"
|
||||
#include "bitops.h"
|
||||
#include "handy.h"
|
||||
#include "tassert.h"
|
||||
#include "sha2.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void cf_hmac_init(cf_hmac_ctx *ctx,
|
||||
const cf_chash *hash,
|
||||
const uint8_t *key, size_t nkey)
|
||||
{
|
||||
assert(ctx);
|
||||
assert(hash);
|
||||
|
||||
mem_clean(ctx, sizeof *ctx);
|
||||
ctx->hash = hash;
|
||||
|
||||
/* Prepare key: */
|
||||
uint8_t k[CF_CHASH_MAXBLK];
|
||||
|
||||
/* Shorten long keys. */
|
||||
if (nkey > hash->blocksz)
|
||||
{
|
||||
/* Standard doesn't cover case where blocksz < hashsz.
|
||||
* FIPS186-1 seems to want to append a negative number of zero bytes.
|
||||
* In any case, we only have a k buffer of CF_CHASH_MAXBLK! */
|
||||
assert(hash->hashsz <= hash->blocksz);
|
||||
|
||||
cf_hash(hash, key, nkey, k);
|
||||
key = k;
|
||||
nkey = hash->hashsz;
|
||||
}
|
||||
|
||||
/* Right zero-pad short keys. */
|
||||
if (k != key)
|
||||
memcpy(k, key, nkey);
|
||||
if (hash->blocksz > nkey)
|
||||
memset(k + nkey, 0, hash->blocksz - nkey);
|
||||
|
||||
/* Start inner hash computation */
|
||||
uint8_t blk[CF_CHASH_MAXBLK];
|
||||
|
||||
xor_b8(blk, k, 0x36, hash->blocksz);
|
||||
hash->init(&ctx->inner);
|
||||
hash->update(&ctx->inner, blk, hash->blocksz);
|
||||
|
||||
/* And outer. */
|
||||
xor_b8(blk, k, 0x5c, hash->blocksz);
|
||||
hash->init(&ctx->outer);
|
||||
hash->update(&ctx->outer, blk, hash->blocksz);
|
||||
|
||||
mem_clean(blk, sizeof blk);
|
||||
mem_clean(k, sizeof k);
|
||||
}
|
||||
|
||||
void cf_hmac_update(cf_hmac_ctx *ctx, const void *data, size_t ndata)
|
||||
{
|
||||
assert(ctx && ctx->hash);
|
||||
|
||||
ctx->hash->update(&ctx->inner, data, ndata);
|
||||
}
|
||||
|
||||
void cf_hmac_finish(cf_hmac_ctx *ctx, uint8_t *out)
|
||||
{
|
||||
assert(ctx && ctx->hash);
|
||||
assert(out);
|
||||
|
||||
uint8_t innerh[CF_MAXHASH];
|
||||
ctx->hash->digest(&ctx->inner, innerh);
|
||||
|
||||
ctx->hash->update(&ctx->outer, innerh, ctx->hash->hashsz);
|
||||
ctx->hash->digest(&ctx->outer, out);
|
||||
|
||||
mem_clean(ctx, sizeof *ctx);
|
||||
}
|
||||
|
||||
void cf_hmac(const uint8_t *key, size_t nkey,
|
||||
const uint8_t *msg, size_t nmsg,
|
||||
uint8_t *out,
|
||||
const cf_chash *hash)
|
||||
{
|
||||
cf_hmac_ctx ctx;
|
||||
|
||||
assert(out);
|
||||
assert(hash);
|
||||
|
||||
cf_hmac_init(&ctx, hash, key, nkey);
|
||||
cf_hmac_update(&ctx, msg, nmsg);
|
||||
cf_hmac_finish(&ctx, out);
|
||||
}
|
||||
|
||||
// HMAC-SHA256
|
||||
int hmac_sha256 (const uint8_t *key, size_t keylen,
|
||||
const uint8_t *input, size_t ilen,
|
||||
uint8_t *output) {
|
||||
cf_hmac_ctx ctx;
|
||||
cf_hmac_init (&ctx, &cf_sha256, key, keylen);
|
||||
cf_hmac_update (&ctx, input, ilen);
|
||||
cf_hmac_finish (&ctx, output);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* cifra - embedded cryptography library
|
||||
* Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
*
|
||||
* To the extent possible under law, the author(s) have dedicated all
|
||||
* copyright and related and neighboring rights to this software to the
|
||||
* public domain worldwide. This software is distributed without any
|
||||
* warranty.
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication
|
||||
* along with this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#ifndef HMAC_H
|
||||
#define HMAC_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "chash.h"
|
||||
|
||||
/**
|
||||
* HMAC
|
||||
* ====
|
||||
* This is a one-shot and incremental interface to computing
|
||||
* HMAC with any hash function.
|
||||
*
|
||||
* (Note: HMAC with SHA3 is possible, but is probably not a
|
||||
* sensible thing to want.)
|
||||
*/
|
||||
|
||||
/* .. c:type:: cf_hmac_ctx
|
||||
* HMAC incremental interface context.
|
||||
*
|
||||
* .. c:member:: cf_hmac_ctx.hash
|
||||
* Hash function description.
|
||||
*
|
||||
* .. c:member:: cf_hmac_ctx.inner
|
||||
* Inner hash computation.
|
||||
*
|
||||
* .. c:member:: cf_hmac_ctx.outer
|
||||
* Outer hash computation.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
const cf_chash *hash;
|
||||
cf_chash_ctx inner;
|
||||
cf_chash_ctx outer;
|
||||
} cf_hmac_ctx;
|
||||
|
||||
/* .. c:function:: $DECL
|
||||
* Set up ctx for computing a HMAC using the given hash and key. */
|
||||
void cf_hmac_init(cf_hmac_ctx *ctx,
|
||||
const cf_chash *hash,
|
||||
const uint8_t *key, size_t nkey);
|
||||
|
||||
/* .. c:function:: $DECL
|
||||
* Input data. */
|
||||
void cf_hmac_update(cf_hmac_ctx *ctx,
|
||||
const void *data, size_t ndata);
|
||||
|
||||
/* .. c:function:: $DECL
|
||||
* Finish and compute HMAC.
|
||||
* `ctx->hash->hashsz` bytes are written to `out`. */
|
||||
void cf_hmac_finish(cf_hmac_ctx *ctx, uint8_t *out);
|
||||
|
||||
/* .. c:function:: $DECL
|
||||
* One shot interface: compute `HMAC_hash(key, msg)`, writing the
|
||||
* answer (which is `hash->hashsz` long) to `out`.
|
||||
*
|
||||
* This function does not fail. */
|
||||
void cf_hmac(const uint8_t *key, size_t nkey,
|
||||
const uint8_t *msg, size_t nmsg,
|
||||
uint8_t *out,
|
||||
const cf_chash *hash);
|
||||
|
||||
int hmac_sha256 (const uint8_t *key, size_t keylen,
|
||||
const uint8_t *input, size_t ilen,
|
||||
uint8_t *output);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* cifra - embedded cryptography library
|
||||
* Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
*
|
||||
* To the extent possible under law, the author(s) have dedicated all
|
||||
* copyright and related and neighboring rights to this software to the
|
||||
* public domain worldwide. This software is distributed without any
|
||||
* warranty.
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication
|
||||
* along with this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#ifndef PRP_H
|
||||
#define PRP_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* General block cipher description
|
||||
* ================================
|
||||
* This allows us to implement block cipher modes which can work
|
||||
* with different block ciphers.
|
||||
*/
|
||||
|
||||
/* .. c:type:: cf_prp_block
|
||||
* Block processing function type.
|
||||
*
|
||||
* The `in` and `out` blocks may alias.
|
||||
*
|
||||
* :rtype: void
|
||||
* :param ctx: block cipher-specific context object.
|
||||
* :param in: input block.
|
||||
* :param out: output block.
|
||||
*/
|
||||
typedef void (*cf_prp_block)(void *ctx, const uint8_t *in, uint8_t *out);
|
||||
|
||||
/* .. c:type:: cf_prp
|
||||
* Describes an PRP in a general way.
|
||||
*
|
||||
* .. c:member:: cf_prp.blocksz
|
||||
* Block size in bytes. Must be no more than :c:macro:`CF_MAXBLOCK`.
|
||||
*
|
||||
* .. c:member:: cf_prp.encrypt
|
||||
* Block encryption function.
|
||||
*
|
||||
* .. c:member:: cf_prp.decrypt
|
||||
* Block decryption function.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
size_t blocksz;
|
||||
cf_prp_block encrypt;
|
||||
cf_prp_block decrypt;
|
||||
} cf_prp;
|
||||
|
||||
/* .. c:macro:: CF_MAXBLOCK
|
||||
* The maximum block cipher blocksize we support, in bytes.
|
||||
*/
|
||||
#define CF_MAXBLOCK 16
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* cifra - embedded cryptography library
|
||||
* Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
*
|
||||
* To the extent possible under law, the author(s) have dedicated all
|
||||
* copyright and related and neighboring rights to this software to the
|
||||
* public domain worldwide. This software is distributed without any
|
||||
* warranty.
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication
|
||||
* along with this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#ifndef SHA2_H
|
||||
#define SHA2_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "chash.h"
|
||||
|
||||
/**
|
||||
* SHA224/SHA256
|
||||
* =============
|
||||
*/
|
||||
|
||||
/* .. c:macro:: CF_SHA256_HASHSZ
|
||||
* The output size of SHA256: 32 bytes. */
|
||||
#define CF_SHA256_HASHSZ 32
|
||||
|
||||
/* .. c:macro:: CF_SHA256_BLOCKSZ
|
||||
* The block size of SHA256: 64 bytes. */
|
||||
#define CF_SHA256_BLOCKSZ 64
|
||||
|
||||
/* .. c:type:: cf_sha256_context
|
||||
* Incremental SHA256 hashing context.
|
||||
*
|
||||
* .. c:member:: cf_sha256_context.H
|
||||
* Intermediate values.
|
||||
*
|
||||
* .. c:member:: cf_sha256_context.partial
|
||||
* Unprocessed input.
|
||||
*
|
||||
* .. c:member:: cf_sha256_context.npartial
|
||||
* Number of bytes of unprocessed input.
|
||||
*
|
||||
* .. c:member:: cf_sha256_context.blocks
|
||||
* Number of full blocks processed.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint32_t H[8]; /* State. */
|
||||
uint8_t partial[CF_SHA256_BLOCKSZ]; /* Partial block of input. */
|
||||
uint32_t blocks; /* Number of full blocks processed into H. */
|
||||
size_t npartial; /* Number of bytes in prefix of partial. */
|
||||
} cf_sha256_context;
|
||||
|
||||
/* .. c:function:: $DECL
|
||||
* Sets up `ctx` ready to hash a new message.
|
||||
*/
|
||||
extern void cf_sha256_init(cf_sha256_context *ctx);
|
||||
|
||||
/* .. c:function:: $DECL
|
||||
* Hashes `nbytes` at `data`. Copies the data if there isn't enough to make
|
||||
* a full block.
|
||||
*/
|
||||
extern void cf_sha256_update(cf_sha256_context *ctx, const void *data, size_t nbytes);
|
||||
|
||||
/* .. c:function:: $DECL
|
||||
* Finishes the hash operation, writing `CF_SHA256_HASHSZ` bytes to `hash`.
|
||||
*
|
||||
* This leaves `ctx` unchanged.
|
||||
*/
|
||||
extern void cf_sha256_digest(const cf_sha256_context *ctx, uint8_t hash[CF_SHA256_HASHSZ]);
|
||||
|
||||
/* .. c:function:: $DECL
|
||||
* Finishes the hash operation, writing `CF_SHA256_HASHSZ` bytes to `hash`.
|
||||
*
|
||||
* This destroys `ctx`, but uses less stack than :c:func:`cf_sha256_digest`.
|
||||
*/
|
||||
extern void cf_sha256_digest_final(cf_sha256_context *ctx, uint8_t hash[CF_SHA256_HASHSZ]);
|
||||
|
||||
/* .. c:var:: cf_sha256
|
||||
* Abstract interface to SHA256. See :c:type:`cf_chash` for more information.
|
||||
*/
|
||||
extern const cf_chash cf_sha256;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* cifra - embedded cryptography library
|
||||
* Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
*
|
||||
* To the extent possible under law, the author(s) have dedicated all
|
||||
* copyright and related and neighboring rights to this software to the
|
||||
* public domain worldwide. This software is distributed without any
|
||||
* warranty.
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication
|
||||
* along with this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "sha2.h"
|
||||
#include "blockwise.h"
|
||||
#include "bitops.h"
|
||||
#include "handy.h"
|
||||
#include "tassert.h"
|
||||
|
||||
static const uint32_t K[64] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
||||
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
||||
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
||||
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
||||
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
||||
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
||||
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
};
|
||||
|
||||
# define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
|
||||
# define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
|
||||
# define BSIG0(x) (rotr32((x), 2) ^ rotr32((x), 13) ^ rotr32((x), 22))
|
||||
# define BSIG1(x) (rotr32((x), 6) ^ rotr32((x), 11) ^ rotr32((x), 25))
|
||||
# define SSIG0(x) (rotr32((x), 7) ^ rotr32((x), 18) ^ ((x) >> 3))
|
||||
# define SSIG1(x) (rotr32((x), 17) ^ rotr32((x), 19) ^ ((x) >> 10))
|
||||
|
||||
void cf_sha256_init(cf_sha256_context *ctx)
|
||||
{
|
||||
memset(ctx, 0, sizeof *ctx);
|
||||
ctx->H[0] = 0x6a09e667;
|
||||
ctx->H[1] = 0xbb67ae85;
|
||||
ctx->H[2] = 0x3c6ef372;
|
||||
ctx->H[3] = 0xa54ff53a;
|
||||
ctx->H[4] = 0x510e527f;
|
||||
ctx->H[5] = 0x9b05688c;
|
||||
ctx->H[6] = 0x1f83d9ab;
|
||||
ctx->H[7] = 0x5be0cd19;
|
||||
}
|
||||
|
||||
static void sha256_update_block(void *vctx, const uint8_t *inp)
|
||||
{
|
||||
cf_sha256_context *ctx = vctx;
|
||||
|
||||
/* This is a 16-word window into the whole W array. */
|
||||
uint32_t W[16];
|
||||
|
||||
uint32_t a = ctx->H[0],
|
||||
b = ctx->H[1],
|
||||
c = ctx->H[2],
|
||||
d = ctx->H[3],
|
||||
e = ctx->H[4],
|
||||
f = ctx->H[5],
|
||||
g = ctx->H[6],
|
||||
h = ctx->H[7],
|
||||
Wt;
|
||||
|
||||
for (size_t t = 0; t < 64; t++)
|
||||
{
|
||||
/* For W[0..16] we process the input into W.
|
||||
* For W[16..64] we compute the next W value:
|
||||
*
|
||||
* W[t] = SSIG1(W[t - 2]) + W[t - 7] + SSIG0(W[t - 15]) + W[t - 16];
|
||||
*
|
||||
* But all W indices are reduced mod 16 into our window.
|
||||
*/
|
||||
if (t < 16)
|
||||
{
|
||||
W[t] = Wt = read32_be(inp);
|
||||
inp += 4;
|
||||
} else {
|
||||
Wt = SSIG1(W[(t - 2) % 16]) +
|
||||
W[(t - 7) % 16] +
|
||||
SSIG0(W[(t - 15) % 16]) +
|
||||
W[(t - 16) % 16];
|
||||
W[t % 16] = Wt;
|
||||
}
|
||||
|
||||
uint32_t T1 = h + BSIG1(e) + CH(e, f, g) + K[t] + Wt;
|
||||
uint32_t T2 = BSIG0(a) + MAJ(a, b, c);
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + T1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = T1 + T2;
|
||||
}
|
||||
|
||||
ctx->H[0] += a;
|
||||
ctx->H[1] += b;
|
||||
ctx->H[2] += c;
|
||||
ctx->H[3] += d;
|
||||
ctx->H[4] += e;
|
||||
ctx->H[5] += f;
|
||||
ctx->H[6] += g;
|
||||
ctx->H[7] += h;
|
||||
|
||||
ctx->blocks++;
|
||||
}
|
||||
|
||||
void cf_sha256_update(cf_sha256_context *ctx, const void *data, size_t nbytes)
|
||||
{
|
||||
cf_blockwise_accumulate(ctx->partial, &ctx->npartial, sizeof ctx->partial,
|
||||
data, nbytes,
|
||||
sha256_update_block, ctx);
|
||||
}
|
||||
|
||||
void cf_sha256_digest(const cf_sha256_context *ctx, uint8_t hash[CF_SHA256_HASHSZ])
|
||||
{
|
||||
cf_sha256_context ours = *ctx;
|
||||
cf_sha256_digest_final(&ours, hash);
|
||||
}
|
||||
|
||||
void cf_sha256_digest_final(cf_sha256_context *ctx, uint8_t hash[CF_SHA256_HASHSZ])
|
||||
{
|
||||
uint64_t digested_bytes = ctx->blocks;
|
||||
digested_bytes = digested_bytes * CF_SHA256_BLOCKSZ + ctx->npartial;
|
||||
uint64_t digested_bits = digested_bytes * 8;
|
||||
|
||||
size_t padbytes = CF_SHA256_BLOCKSZ - ((digested_bytes + 8) % CF_SHA256_BLOCKSZ);
|
||||
|
||||
/* Hash 0x80 00 ... block first. */
|
||||
cf_blockwise_acc_pad(ctx->partial, &ctx->npartial, sizeof ctx->partial,
|
||||
0x80, 0x00, 0x00, padbytes,
|
||||
sha256_update_block, ctx);
|
||||
|
||||
/* Now hash length. */
|
||||
uint8_t buf[8];
|
||||
write64_be(digested_bits, buf);
|
||||
cf_sha256_update(ctx, buf, 8);
|
||||
|
||||
/* We ought to have got our padding calculation right! */
|
||||
assert(ctx->npartial == 0);
|
||||
|
||||
write32_be(ctx->H[0], hash + 0);
|
||||
write32_be(ctx->H[1], hash + 4);
|
||||
write32_be(ctx->H[2], hash + 8);
|
||||
write32_be(ctx->H[3], hash + 12);
|
||||
write32_be(ctx->H[4], hash + 16);
|
||||
write32_be(ctx->H[5], hash + 20);
|
||||
write32_be(ctx->H[6], hash + 24);
|
||||
write32_be(ctx->H[7], hash + 28);
|
||||
|
||||
memset(ctx, 0, sizeof *ctx);
|
||||
}
|
||||
|
||||
const cf_chash cf_sha256 = {
|
||||
.hashsz = CF_SHA256_HASHSZ,
|
||||
.blocksz = CF_SHA256_BLOCKSZ,
|
||||
.init = (cf_chash_init) cf_sha256_init,
|
||||
.update = (cf_chash_update) cf_sha256_update,
|
||||
.digest = (cf_chash_digest) cf_sha256_digest
|
||||
};
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* cifra - embedded cryptography library
|
||||
* Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
*
|
||||
* To the extent possible under law, the author(s) have dedicated all
|
||||
* copyright and related and neighboring rights to this software to the
|
||||
* public domain worldwide. This software is distributed without any
|
||||
* warranty.
|
||||
*
|
||||
* You should have received a copy of the CC0 Public Domain Dedication
|
||||
* along with this software. If not, see
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#ifndef TASSERT_H
|
||||
#define TASSERT_H
|
||||
|
||||
/* Tiny assert
|
||||
* -----------
|
||||
*
|
||||
* This is an assert(3) definition which doesn't include any
|
||||
* strings, but just branches to abort(3) on failure.
|
||||
*/
|
||||
|
||||
#ifndef FULL_FAT_ASSERT
|
||||
# include <stdlib.h>
|
||||
# define assert(expr) do { if (!(expr)) abort(); } while (0)
|
||||
#else
|
||||
# include <assert.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,276 @@
|
||||
#include "config.h"
|
||||
#include "ch32v20x_flash.h"
|
||||
//#include "ch32v30x_rng.h"
|
||||
#include "lib/cifra/sha2.h"
|
||||
#include "lib/monocypher/monocypher-ed25519.h"
|
||||
#include "sx1262.h"
|
||||
#include "util/hexdump.h"
|
||||
#include "util/log.h"
|
||||
|
||||
PersistentData_t persistent;
|
||||
|
||||
LoRaSettings currentLoRaSettings;
|
||||
|
||||
#define TAG "Config"
|
||||
|
||||
NodeEntry *getNextNode() {
|
||||
uint32_t oldest_timestamp = UINT32_MAX;
|
||||
NodeEntry *selectedNode = &(persistent.contacts[0]);
|
||||
for (int i = 0; i < CONTACT_COUNT; i++) {
|
||||
NodeEntry *curNode = &(persistent.contacts[i]);
|
||||
if (curNode->flags & NODE_ENTRY_FAV_FLAG) {
|
||||
continue;
|
||||
}
|
||||
if (curNode->last_seen_lt == 0) {
|
||||
selectedNode = curNode;
|
||||
break;
|
||||
}
|
||||
if (curNode->last_seen_lt < oldest_timestamp) {
|
||||
oldest_timestamp = curNode->last_seen_lt;
|
||||
selectedNode = curNode;
|
||||
}
|
||||
}
|
||||
return selectedNode;
|
||||
}
|
||||
|
||||
NodeEntry *getNode (uint8_t hash) {
|
||||
NodeEntry *selectedNode = NULL;
|
||||
for (int i = 0; i < CONTACT_COUNT; i++) {
|
||||
NodeEntry *curNode = &(persistent.contacts[i]);
|
||||
if (curNode->pubKey[0] == hash) {
|
||||
selectedNode = curNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return selectedNode;
|
||||
}
|
||||
|
||||
NodeEntry *getNodePrefix (const uint8_t *hash) {
|
||||
NodeEntry *selectedNode = NULL;
|
||||
for (int i = 0; i < CONTACT_COUNT; i++) {
|
||||
NodeEntry *curNode = &(persistent.contacts[i]);
|
||||
if (memcmp (curNode->pubKey, hash, 4)) {
|
||||
selectedNode = curNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return selectedNode;
|
||||
}
|
||||
|
||||
Channel *getChannel (uint8_t hash, uint8_t ignoreCount) {
|
||||
Channel *selectedChannel = NULL;
|
||||
Channel *finalChannel = NULL;
|
||||
uint8_t matchCount = 0;
|
||||
for (int i = 0; i < ChannelCount; i++) {
|
||||
Channel *curChannel = &(persistent.channels[i]);
|
||||
if (curChannel->hash == hash) {
|
||||
selectedChannel = curChannel;
|
||||
if (++matchCount > ignoreCount) {
|
||||
finalChannel = selectedChannel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return finalChannel;
|
||||
}
|
||||
|
||||
void addChannel (char *name, const uint8_t *key) {
|
||||
|
||||
uint32_t oldest_timestamp = UINT32_MAX;
|
||||
Channel *selectedChannel = &(persistent.channels[0]);
|
||||
for (int i = 0; i < ChannelCount; i++) {
|
||||
Channel *curChan = &(persistent.channels[i]);
|
||||
|
||||
if (curChan->timestamp == 0) {
|
||||
selectedChannel = curChan;
|
||||
MESH_LOGD (TAG, "Deciding on channel index %d because of timestamp 0, name is %s", i, name);
|
||||
break;
|
||||
}
|
||||
if (strlen (curChan->name) == 0) {
|
||||
selectedChannel = curChan;
|
||||
MESH_LOGD (TAG, "Deciding on channel index %d because of name len 0, name is %s", i, name);
|
||||
break;
|
||||
}
|
||||
|
||||
if (curChan->timestamp < oldest_timestamp) {
|
||||
oldest_timestamp = curChan->timestamp;
|
||||
selectedChannel = curChan;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
memset (selectedChannel->name, 0, sizeof (selectedChannel->name));
|
||||
strncpy (selectedChannel->name, name, sizeof (selectedChannel->name));
|
||||
memcpy (selectedChannel->key, key, sizeof (selectedChannel->key));
|
||||
// Buffer for the digest
|
||||
uint8_t hash[CF_SHA256_HASHSZ];
|
||||
|
||||
// Context
|
||||
cf_sha256_context ctx;
|
||||
|
||||
// 1. Initialize
|
||||
cf_sha256_init (&ctx);
|
||||
|
||||
// 2. Feed in your data
|
||||
cf_sha256_update (&ctx, selectedChannel->key, sizeof (selectedChannel->key));
|
||||
|
||||
// 3. Compute digest
|
||||
cf_sha256_digest (&ctx, hash);
|
||||
selectedChannel->hash = hash[0];
|
||||
selectedChannel->timestamp = RTC_GetCounter();
|
||||
}
|
||||
|
||||
void printNodeDB() {
|
||||
puts ("Node database:");
|
||||
for (int i = 0; i < CONTACT_COUNT; i++) {
|
||||
const NodeEntry *node = &(persistent.contacts[i]);
|
||||
if (node->last_seen_lt == 0)
|
||||
continue; // skip inactive nodes
|
||||
|
||||
iprintf ("Node %d:\n", i);
|
||||
iprintf (" Name: %s\n", node->name);
|
||||
hexdump ("Pubkey", node->pubKey, sizeof (node->pubKey));
|
||||
hexdump ("Secret", node->secret, sizeof (node->secret));
|
||||
iprintf ("\n");
|
||||
|
||||
iprintf (" GPS: lat=%d, lon=%d\n", node->gps_latitude, node->gps_longitude);
|
||||
iprintf (" Path: ... (not expanded, add if needed)\n");
|
||||
iprintf (" Flags: 0x%02X\n", node->flags);
|
||||
iprintf (" Type: 0x%02X\n", node->type);
|
||||
iprintf (" Authenticated: %s\n", node->authenticated ? "Yes" : "No");
|
||||
iprintf (" Last Seen (remote ts): %d\n", node->last_seen_rt);
|
||||
iprintf (" Last Seen (local ts): %d\n", node->last_seen_lt);
|
||||
iprintf (" Sync timestamp: %d\n", node->sync_timestamp);
|
||||
iprintf ("--------------------------------------\n");
|
||||
}
|
||||
}
|
||||
|
||||
void loadConfig() {
|
||||
memcpy (&persistent, FLASH_USER_PAGE_ADDR, sizeof (persistent));
|
||||
memcpy(¤tLoRaSettings, &(persistent.loraSettings), sizeof(currentLoRaSettings));
|
||||
uint32_t crcSum = *((uint32_t *)(((uint8_t *)&persistent) + (sizeof (persistent) - 2)));
|
||||
memset ((((uint8_t *)&persistent) + (sizeof (persistent) - sizeof (crcSum))), 0, 4);
|
||||
CRC_ResetDR();
|
||||
uint32_t currentSum = CRC_CalcBlockCRC ((uint32_t *)&persistent, sizeof (persistent) - 2);
|
||||
|
||||
if (currentSum != crcSum) {
|
||||
memset (&persistent, 0, sizeof (persistent));
|
||||
}
|
||||
}
|
||||
|
||||
void saveConfig() {
|
||||
CRC_ResetDR();
|
||||
uint32_t currentSum = CRC_CalcBlockCRC ((uint32_t *)&persistent, sizeof (persistent) - 2);
|
||||
memcpy ((((uint8_t *)&persistent) + (sizeof (persistent) - sizeof (currentSum))), (uint8_t *)currentSum, 4);
|
||||
FLASH_Unlock();
|
||||
FLASH_ErasePage_Fast (1919);
|
||||
FLASH_ProgramPage_Fast (1919, (uint32_t *)&persistent);
|
||||
FLASH_Lock();
|
||||
}
|
||||
|
||||
/*
|
||||
void genSeed (uint8_t *seedOut) {
|
||||
RCC_AHBPeriphClockCmd (RCC_AHBPeriph_RNG, ENABLE);
|
||||
RNG_Cmd (ENABLE);
|
||||
uint32_t random;
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
while (RNG_GetFlagStatus (RNG_FLAG_DRDY) == RESET) {
|
||||
Delay_Ms(10);
|
||||
}
|
||||
random = RNG_GetRandomNumber();
|
||||
|
||||
memcpy (&(seedOut[i * 4]), &random, sizeof (random));
|
||||
}
|
||||
RCC_AHBPeriphClockCmd (RCC_AHBPeriph_RNG, DISABLE);
|
||||
RNG_Cmd (DISABLE);
|
||||
}
|
||||
*/
|
||||
|
||||
const uint8_t publicChannelPSK[16] = {0x8b, 0x33, 0x87, 0xe9, 0xc5, 0xcd, 0xea, 0x6a, 0xc9, 0xe5, 0xed, 0xba, 0xa1, 0x15, 0xcd, 0x72};
|
||||
const uint8_t BRNTestChannelPSK[16] = {0x44, 0x81, 0xda, 0x0e, 0x4e, 0x03, 0xc4, 0x9e, 0x84, 0x77, 0x25, 0xd8, 0x3a, 0x93, 0xbf, 0x80};
|
||||
|
||||
const char *getStringRole (uint8_t role) {
|
||||
switch (role) {
|
||||
case NODE_TYPE_CHAT_NODE:
|
||||
return "Chat node";
|
||||
|
||||
case NODE_TYPE_REPEATER:
|
||||
return "Repeater";
|
||||
|
||||
case NODE_TYPE_ROOM_SERVER:
|
||||
return "Room server";
|
||||
|
||||
case NODE_TYPE_SENSOR:
|
||||
return "Sensor";
|
||||
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void populateDefaults() {
|
||||
|
||||
uint8_t seed[32];
|
||||
//memcpy(seed, "vFt0FRugSOeqnkshImMCVfgHM5vBxz4", 32); //chat node identity
|
||||
//memcpy (seed, "vFt0FRugSOeqnkshImMCVfgHM5vBxz3", 32); // repeater identity
|
||||
memcpy (seed, "vFt0FRugSOeqnkshImMCVfgHM5vBxy1", 32); // repeater identity
|
||||
// genSeed(seed); //random identity
|
||||
|
||||
crypto_ed25519_key_pair(persistent.privkey, persistent.pubkey, seed);
|
||||
|
||||
|
||||
persistent.nodeType = NODE_TYPE_CHAT_NODE;
|
||||
//persistent.nodeType = NODE_TYPE_REPEATER;
|
||||
memset (persistent.password, 0, sizeof (persistent.password));
|
||||
strcpy (persistent.password, "hesielko");
|
||||
//strcpy (persistent.nodeName, "BRN WCHNode RISCV");
|
||||
strcpy (persistent.nodeName, "BRN WCH Mini");
|
||||
|
||||
persistent.adcMultiplier = 0;
|
||||
|
||||
persistent.loraSettings.txPowerInDbm = 20;
|
||||
persistent.loraSettings.frequencyInHz = 869554000;
|
||||
persistent.loraSettings.spreadingFactor = 8;
|
||||
persistent.loraSettings.bandwidth = SX126X_LORA_BW_62_5;
|
||||
persistent.loraSettings.codingRate = SX126X_LORA_CR_4_8;
|
||||
persistent.loraSettings.preambleLength = 16;
|
||||
persistent.loraSettings.tcxoVoltage = 2200; // ebyte
|
||||
// persistent.tcxoVoltage = 1800; // heltec
|
||||
|
||||
memcpy(¤tLoRaSettings, &(persistent.loraSettings), sizeof(currentLoRaSettings));
|
||||
|
||||
addChannel ("Public", publicChannelPSK);
|
||||
addChannel ("BRNTest", BRNTestChannelPSK);
|
||||
|
||||
persistent.doRepeat = 1;
|
||||
persistent.allowReadOnly = 1;
|
||||
|
||||
|
||||
persistent.latitude = 48190900;
|
||||
persistent.longitude = 17030300;
|
||||
persistent.altitude = 23400;
|
||||
}
|
||||
|
||||
void LoraApply() {
|
||||
|
||||
MESH_LOGW (TAG, "LoraInit");
|
||||
LoRaInit();
|
||||
|
||||
|
||||
char useRegulatorLDO = 0;
|
||||
|
||||
LoRaDebugPrint (0);
|
||||
uint16_t loraBeginStat = LoRaBegin (currentLoRaSettings.frequencyInHz, currentLoRaSettings.txPowerInDbm, currentLoRaSettings.tcxoVoltage, useRegulatorLDO);
|
||||
if (loraBeginStat != 0) {
|
||||
MESH_LOGE (TAG, "Does not recognize the module");
|
||||
while (1) {
|
||||
Delay_Ms(1000);
|
||||
MESH_LOGE (TAG, "CRITICAL: LoRa not found, halted");
|
||||
}
|
||||
}
|
||||
|
||||
char crcOn = 1;
|
||||
char invertIrq = 0;
|
||||
|
||||
LoRaConfig (currentLoRaSettings.spreadingFactor, currentLoRaSettings.bandwidth, currentLoRaSettings.codingRate, currentLoRaSettings.preambleLength, 0, crcOn, invertIrq);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#ifndef CONFIG_HEADER
|
||||
#define CONFIG_HEADER
|
||||
#include "stdint.h"
|
||||
#include "string.h"
|
||||
#include "meshcore/packetstructs.h"
|
||||
|
||||
#define FLASH_USER_PAGE_ADDR ((const void *)(0x08077F00))
|
||||
|
||||
#define ChannelCount 8
|
||||
//#define CONTACT_COUNT 100
|
||||
#define CONTACT_COUNT 16
|
||||
|
||||
#define VERSION "v0.0.1 - BRN Systems RISC-V"
|
||||
|
||||
#define BOARD "WCH CH32V307"
|
||||
|
||||
typedef struct LoRaSettings {
|
||||
int8_t txPowerInDbm;
|
||||
uint32_t frequencyInHz;
|
||||
uint8_t spreadingFactor;
|
||||
uint8_t bandwidth;
|
||||
uint8_t codingRate;
|
||||
uint16_t preambleLength;
|
||||
uint16_t tcxoVoltage;
|
||||
} LoRaSettings;
|
||||
|
||||
extern LoRaSettings currentLoRaSettings;
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic; // e.g. 0xDEADBEEF
|
||||
|
||||
uint8_t privkey[32]; // Ed25519 private
|
||||
uint8_t pubkey[32]; // Ed25519 public
|
||||
|
||||
uint8_t nodeType;
|
||||
char nodeName[32];
|
||||
|
||||
int32_t latitude;
|
||||
int32_t longitude;
|
||||
int32_t altitude;
|
||||
|
||||
LoRaSettings loraSettings;
|
||||
|
||||
Channel channels[ChannelCount];
|
||||
NodeEntry contacts[CONTACT_COUNT];
|
||||
|
||||
uint8_t password[16];
|
||||
uint8_t guestPassword[16];
|
||||
|
||||
uint8_t doRepeat;
|
||||
uint8_t allowReadOnly;
|
||||
uint16_t adcMultiplier;
|
||||
uint16_t airtimeFactor;
|
||||
uint32_t crc32; // integrity check
|
||||
} PersistentData_t;
|
||||
|
||||
extern PersistentData_t persistent;
|
||||
|
||||
void saveConfig();
|
||||
void loadConfig();
|
||||
|
||||
void printNodeDB();
|
||||
|
||||
NodeEntry *getNextNode();
|
||||
|
||||
NodeEntry *getNode (uint8_t hash);
|
||||
|
||||
Channel *getChannel (uint8_t hash, uint8_t ignoreCount);
|
||||
|
||||
void addChannel (char *name, const uint8_t *key);
|
||||
|
||||
NodeEntry *getNodePrefix (const uint8_t *hash);
|
||||
|
||||
const char *getStringRole (uint8_t role);
|
||||
|
||||
void populateDefaults();
|
||||
|
||||
void LoraApply();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,503 @@
|
||||
// Monocypher version 4.0.3
|
||||
//
|
||||
// This file is dual-licensed. Choose whichever licence you want from
|
||||
// the two licences listed below.
|
||||
//
|
||||
// The first licence is a regular 2-clause BSD licence. The second licence
|
||||
// is the CC-0 from Creative Commons. It is intended to release Monocypher
|
||||
// to the public domain. The BSD licence serves as a fallback option.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-2-Clause OR CC0-1.0
|
||||
//
|
||||
// ------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2017-2019, Loup Vaillant
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// ------------------------------------------------------------------------
|
||||
//
|
||||
// Written in 2017-2019 by Loup Vaillant
|
||||
//
|
||||
// To the extent possible under law, the author(s) have dedicated all copyright
|
||||
// and related neighboring rights to this software to the public domain
|
||||
// worldwide. This software is distributed without any warranty.
|
||||
//
|
||||
// You should have received a copy of the CC0 Public Domain Dedication along
|
||||
// with this software. If not, see
|
||||
// <https://creativecommons.org/publicdomain/zero/1.0/>
|
||||
|
||||
#include "monocypher-ed25519.h"
|
||||
|
||||
#ifdef MONOCYPHER_CPP_NAMESPACE
|
||||
namespace MONOCYPHER_CPP_NAMESPACE {
|
||||
#endif
|
||||
|
||||
/////////////////
|
||||
/// Utilities ///
|
||||
/////////////////
|
||||
#define FOR(i, min, max) for (size_t i = min; i < max; i++)
|
||||
#define COPY(dst, src, size) FOR(_i_, 0, size) (dst)[_i_] = (src)[_i_]
|
||||
#define ZERO(buf, size) FOR(_i_, 0, size) (buf)[_i_] = 0
|
||||
#define WIPE_CTX(ctx) crypto_wipe(ctx , sizeof(*(ctx)))
|
||||
#define WIPE_BUFFER(buffer) crypto_wipe(buffer, sizeof(buffer))
|
||||
#define MIN(a, b) ((a) <= (b) ? (a) : (b))
|
||||
typedef uint8_t u8;
|
||||
typedef uint64_t u64;
|
||||
|
||||
// Returns the smallest positive integer y such that
|
||||
// (x + y) % pow_2 == 0
|
||||
// Basically, it's how many bytes we need to add to "align" x.
|
||||
// Only works when pow_2 is a power of 2.
|
||||
// Note: we use ~x+1 instead of -x to avoid compiler warnings
|
||||
static size_t align(size_t x, size_t pow_2)
|
||||
{
|
||||
return (~x + 1) & (pow_2 - 1);
|
||||
}
|
||||
|
||||
static u64 load64_be(const u8 s[8])
|
||||
{
|
||||
return((u64)s[0] << 56)
|
||||
| ((u64)s[1] << 48)
|
||||
| ((u64)s[2] << 40)
|
||||
| ((u64)s[3] << 32)
|
||||
| ((u64)s[4] << 24)
|
||||
| ((u64)s[5] << 16)
|
||||
| ((u64)s[6] << 8)
|
||||
| (u64)s[7];
|
||||
}
|
||||
|
||||
static void store64_be(u8 out[8], u64 in)
|
||||
{
|
||||
out[0] = (u8)(in >> 56);
|
||||
out[1] = (u8)(in >> 48);
|
||||
out[2] = (u8)(in >> 40);
|
||||
out[3] = (u8)(in >> 32);
|
||||
out[4] = (u8)(in >> 24);
|
||||
out[5] = (u8)(in >> 16);
|
||||
out[6] = (u8)(in >> 8);
|
||||
out[7] = (u8) in ;
|
||||
}
|
||||
|
||||
static void load64_be_buf (u64 *dst, const u8 *src, size_t size) {
|
||||
FOR(i, 0, size) { dst[i] = load64_be(src + i*8); }
|
||||
}
|
||||
|
||||
///////////////
|
||||
/// SHA 512 ///
|
||||
///////////////
|
||||
static u64 rot(u64 x, int c ) { return (x >> c) | (x << (64 - c)); }
|
||||
static u64 ch (u64 x, u64 y, u64 z) { return (x & y) ^ (~x & z); }
|
||||
static u64 maj(u64 x, u64 y, u64 z) { return (x & y) ^ ( x & z) ^ (y & z); }
|
||||
static u64 big_sigma0(u64 x) { return rot(x, 28) ^ rot(x, 34) ^ rot(x, 39); }
|
||||
static u64 big_sigma1(u64 x) { return rot(x, 14) ^ rot(x, 18) ^ rot(x, 41); }
|
||||
static u64 lit_sigma0(u64 x) { return rot(x, 1) ^ rot(x, 8) ^ (x >> 7); }
|
||||
static u64 lit_sigma1(u64 x) { return rot(x, 19) ^ rot(x, 61) ^ (x >> 6); }
|
||||
|
||||
static const u64 K[80] = {
|
||||
0x428a2f98d728ae22,0x7137449123ef65cd,0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc,
|
||||
0x3956c25bf348b538,0x59f111f1b605d019,0x923f82a4af194f9b,0xab1c5ed5da6d8118,
|
||||
0xd807aa98a3030242,0x12835b0145706fbe,0x243185be4ee4b28c,0x550c7dc3d5ffb4e2,
|
||||
0x72be5d74f27b896f,0x80deb1fe3b1696b1,0x9bdc06a725c71235,0xc19bf174cf692694,
|
||||
0xe49b69c19ef14ad2,0xefbe4786384f25e3,0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65,
|
||||
0x2de92c6f592b0275,0x4a7484aa6ea6e483,0x5cb0a9dcbd41fbd4,0x76f988da831153b5,
|
||||
0x983e5152ee66dfab,0xa831c66d2db43210,0xb00327c898fb213f,0xbf597fc7beef0ee4,
|
||||
0xc6e00bf33da88fc2,0xd5a79147930aa725,0x06ca6351e003826f,0x142929670a0e6e70,
|
||||
0x27b70a8546d22ffc,0x2e1b21385c26c926,0x4d2c6dfc5ac42aed,0x53380d139d95b3df,
|
||||
0x650a73548baf63de,0x766a0abb3c77b2a8,0x81c2c92e47edaee6,0x92722c851482353b,
|
||||
0xa2bfe8a14cf10364,0xa81a664bbc423001,0xc24b8b70d0f89791,0xc76c51a30654be30,
|
||||
0xd192e819d6ef5218,0xd69906245565a910,0xf40e35855771202a,0x106aa07032bbd1b8,
|
||||
0x19a4c116b8d2d0c8,0x1e376c085141ab53,0x2748774cdf8eeb99,0x34b0bcb5e19b48a8,
|
||||
0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb,0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3,
|
||||
0x748f82ee5defb2fc,0x78a5636f43172f60,0x84c87814a1f0ab72,0x8cc702081a6439ec,
|
||||
0x90befffa23631e28,0xa4506cebde82bde9,0xbef9a3f7b2c67915,0xc67178f2e372532b,
|
||||
0xca273eceea26619c,0xd186b8c721c0c207,0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178,
|
||||
0x06f067aa72176fba,0x0a637dc5a2c898a6,0x113f9804bef90dae,0x1b710b35131c471b,
|
||||
0x28db77f523047d84,0x32caab7b40c72493,0x3c9ebe0a15c9bebc,0x431d67c49c100d4c,
|
||||
0x4cc5d4becb3e42b6,0x597f299cfc657e2a,0x5fcb6fab3ad6faec,0x6c44198c4a475817
|
||||
};
|
||||
|
||||
static void sha512_compress(crypto_sha512_ctx *ctx)
|
||||
{
|
||||
u64 a = ctx->hash[0]; u64 b = ctx->hash[1];
|
||||
u64 c = ctx->hash[2]; u64 d = ctx->hash[3];
|
||||
u64 e = ctx->hash[4]; u64 f = ctx->hash[5];
|
||||
u64 g = ctx->hash[6]; u64 h = ctx->hash[7];
|
||||
|
||||
FOR (j, 0, 16) {
|
||||
u64 in = K[j] + ctx->input[j];
|
||||
u64 t1 = big_sigma1(e) + ch (e, f, g) + h + in;
|
||||
u64 t2 = big_sigma0(a) + maj(a, b, c);
|
||||
h = g; g = f; f = e; e = d + t1;
|
||||
d = c; c = b; b = a; a = t1 + t2;
|
||||
}
|
||||
size_t i16 = 0;
|
||||
FOR(i, 1, 5) {
|
||||
i16 += 16;
|
||||
FOR (j, 0, 16) {
|
||||
ctx->input[j] += lit_sigma1(ctx->input[(j- 2) & 15]);
|
||||
ctx->input[j] += lit_sigma0(ctx->input[(j-15) & 15]);
|
||||
ctx->input[j] += ctx->input[(j- 7) & 15];
|
||||
u64 in = K[i16 + j] + ctx->input[j];
|
||||
u64 t1 = big_sigma1(e) + ch (e, f, g) + h + in;
|
||||
u64 t2 = big_sigma0(a) + maj(a, b, c);
|
||||
h = g; g = f; f = e; e = d + t1;
|
||||
d = c; c = b; b = a; a = t1 + t2;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->hash[0] += a; ctx->hash[1] += b;
|
||||
ctx->hash[2] += c; ctx->hash[3] += d;
|
||||
ctx->hash[4] += e; ctx->hash[5] += f;
|
||||
ctx->hash[6] += g; ctx->hash[7] += h;
|
||||
}
|
||||
|
||||
// Write 1 input byte
|
||||
static void sha512_set_input(crypto_sha512_ctx *ctx, u8 input)
|
||||
{
|
||||
size_t word = ctx->input_idx >> 3;
|
||||
size_t byte = ctx->input_idx & 7;
|
||||
ctx->input[word] |= (u64)input << (8 * (7 - byte));
|
||||
}
|
||||
|
||||
// Increment a 128-bit "word".
|
||||
static void sha512_incr(u64 x[2], u64 y)
|
||||
{
|
||||
x[1] += y;
|
||||
if (x[1] < y) {
|
||||
x[0]++;
|
||||
}
|
||||
}
|
||||
|
||||
void crypto_sha512_init(crypto_sha512_ctx *ctx)
|
||||
{
|
||||
ctx->hash[0] = 0x6a09e667f3bcc908;
|
||||
ctx->hash[1] = 0xbb67ae8584caa73b;
|
||||
ctx->hash[2] = 0x3c6ef372fe94f82b;
|
||||
ctx->hash[3] = 0xa54ff53a5f1d36f1;
|
||||
ctx->hash[4] = 0x510e527fade682d1;
|
||||
ctx->hash[5] = 0x9b05688c2b3e6c1f;
|
||||
ctx->hash[6] = 0x1f83d9abfb41bd6b;
|
||||
ctx->hash[7] = 0x5be0cd19137e2179;
|
||||
ctx->input_size[0] = 0;
|
||||
ctx->input_size[1] = 0;
|
||||
ctx->input_idx = 0;
|
||||
ZERO(ctx->input, 16);
|
||||
}
|
||||
|
||||
void crypto_sha512_update(crypto_sha512_ctx *ctx,
|
||||
const u8 *message, size_t message_size)
|
||||
{
|
||||
// Avoid undefined NULL pointer increments with empty messages
|
||||
if (message_size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Align ourselves with word boundaries
|
||||
if ((ctx->input_idx & 7) != 0) {
|
||||
size_t nb_bytes = MIN(align(ctx->input_idx, 8), message_size);
|
||||
FOR (i, 0, nb_bytes) {
|
||||
sha512_set_input(ctx, message[i]);
|
||||
ctx->input_idx++;
|
||||
}
|
||||
message += nb_bytes;
|
||||
message_size -= nb_bytes;
|
||||
}
|
||||
|
||||
// Align ourselves with block boundaries
|
||||
if ((ctx->input_idx & 127) != 0) {
|
||||
size_t nb_words = MIN(align(ctx->input_idx, 128), message_size) >> 3;
|
||||
load64_be_buf(ctx->input + (ctx->input_idx >> 3), message, nb_words);
|
||||
ctx->input_idx += nb_words << 3;
|
||||
message += nb_words << 3;
|
||||
message_size -= nb_words << 3;
|
||||
}
|
||||
|
||||
// Compress block if needed
|
||||
if (ctx->input_idx == 128) {
|
||||
sha512_incr(ctx->input_size, 1024); // size is in bits
|
||||
sha512_compress(ctx);
|
||||
ctx->input_idx = 0;
|
||||
ZERO(ctx->input, 16);
|
||||
}
|
||||
|
||||
// Process the message block by block
|
||||
FOR (i, 0, message_size >> 7) { // number of blocks
|
||||
load64_be_buf(ctx->input, message, 16);
|
||||
sha512_incr(ctx->input_size, 1024); // size is in bits
|
||||
sha512_compress(ctx);
|
||||
ctx->input_idx = 0;
|
||||
ZERO(ctx->input, 16);
|
||||
message += 128;
|
||||
}
|
||||
message_size &= 127;
|
||||
|
||||
if (message_size != 0) {
|
||||
// Remaining words
|
||||
size_t nb_words = message_size >> 3;
|
||||
load64_be_buf(ctx->input, message, nb_words);
|
||||
ctx->input_idx += nb_words << 3;
|
||||
message += nb_words << 3;
|
||||
message_size -= nb_words << 3;
|
||||
|
||||
// Remaining bytes
|
||||
FOR (i, 0, message_size) {
|
||||
sha512_set_input(ctx, message[i]);
|
||||
ctx->input_idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void crypto_sha512_final(crypto_sha512_ctx *ctx, u8 hash[64])
|
||||
{
|
||||
// Add padding bit
|
||||
if (ctx->input_idx == 0) {
|
||||
ZERO(ctx->input, 16);
|
||||
}
|
||||
sha512_set_input(ctx, 128);
|
||||
|
||||
// Update size
|
||||
sha512_incr(ctx->input_size, ctx->input_idx * 8);
|
||||
|
||||
// Compress penultimate block (if any)
|
||||
if (ctx->input_idx > 111) {
|
||||
sha512_compress(ctx);
|
||||
ZERO(ctx->input, 14);
|
||||
}
|
||||
// Compress last block
|
||||
ctx->input[14] = ctx->input_size[0];
|
||||
ctx->input[15] = ctx->input_size[1];
|
||||
sha512_compress(ctx);
|
||||
|
||||
// Copy hash to output (big endian)
|
||||
FOR (i, 0, 8) {
|
||||
store64_be(hash + i*8, ctx->hash[i]);
|
||||
}
|
||||
|
||||
WIPE_CTX(ctx);
|
||||
}
|
||||
|
||||
void crypto_sha512(u8 hash[64], const u8 *message, size_t message_size)
|
||||
{
|
||||
crypto_sha512_ctx ctx;
|
||||
crypto_sha512_init (&ctx);
|
||||
crypto_sha512_update(&ctx, message, message_size);
|
||||
crypto_sha512_final (&ctx, hash);
|
||||
}
|
||||
|
||||
////////////////////
|
||||
/// HMAC SHA 512 ///
|
||||
////////////////////
|
||||
void crypto_sha512_hmac_init(crypto_sha512_hmac_ctx *ctx,
|
||||
const u8 *key, size_t key_size)
|
||||
{
|
||||
// hash key if it is too long
|
||||
if (key_size > 128) {
|
||||
crypto_sha512(ctx->key, key, key_size);
|
||||
key = ctx->key;
|
||||
key_size = 64;
|
||||
}
|
||||
// Compute inner key: padded key XOR 0x36
|
||||
FOR (i, 0, key_size) { ctx->key[i] = key[i] ^ 0x36; }
|
||||
FOR (i, key_size, 128) { ctx->key[i] = 0x36; }
|
||||
// Start computing inner hash
|
||||
crypto_sha512_init (&ctx->ctx);
|
||||
crypto_sha512_update(&ctx->ctx, ctx->key, 128);
|
||||
}
|
||||
|
||||
void crypto_sha512_hmac_update(crypto_sha512_hmac_ctx *ctx,
|
||||
const u8 *message, size_t message_size)
|
||||
{
|
||||
crypto_sha512_update(&ctx->ctx, message, message_size);
|
||||
}
|
||||
|
||||
void crypto_sha512_hmac_final(crypto_sha512_hmac_ctx *ctx, u8 hmac[64])
|
||||
{
|
||||
// Finish computing inner hash
|
||||
crypto_sha512_final(&ctx->ctx, hmac);
|
||||
// Compute outer key: padded key XOR 0x5c
|
||||
FOR (i, 0, 128) {
|
||||
ctx->key[i] ^= 0x36 ^ 0x5c;
|
||||
}
|
||||
// Compute outer hash
|
||||
crypto_sha512_init (&ctx->ctx);
|
||||
crypto_sha512_update(&ctx->ctx, ctx->key , 128);
|
||||
crypto_sha512_update(&ctx->ctx, hmac, 64);
|
||||
crypto_sha512_final (&ctx->ctx, hmac); // outer hash
|
||||
WIPE_CTX(ctx);
|
||||
}
|
||||
|
||||
void crypto_sha512_hmac(u8 hmac[64], const u8 *key, size_t key_size,
|
||||
const u8 *message, size_t message_size)
|
||||
{
|
||||
crypto_sha512_hmac_ctx ctx;
|
||||
crypto_sha512_hmac_init (&ctx, key, key_size);
|
||||
crypto_sha512_hmac_update(&ctx, message, message_size);
|
||||
crypto_sha512_hmac_final (&ctx, hmac);
|
||||
}
|
||||
|
||||
////////////////////
|
||||
/// HKDF SHA 512 ///
|
||||
////////////////////
|
||||
void crypto_sha512_hkdf_expand(u8 *okm, size_t okm_size,
|
||||
const u8 *prk, size_t prk_size,
|
||||
const u8 *info, size_t info_size)
|
||||
{
|
||||
int not_first = 0;
|
||||
u8 ctr = 1;
|
||||
u8 blk[64];
|
||||
|
||||
while (okm_size > 0) {
|
||||
size_t out_size = MIN(okm_size, sizeof(blk));
|
||||
|
||||
crypto_sha512_hmac_ctx ctx;
|
||||
crypto_sha512_hmac_init(&ctx, prk , prk_size);
|
||||
if (not_first) {
|
||||
// For some reason HKDF uses some kind of CBC mode.
|
||||
// For some reason CTR mode alone wasn't enough.
|
||||
// Like what, they didn't trust HMAC in 2010? Really??
|
||||
crypto_sha512_hmac_update(&ctx, blk , sizeof(blk));
|
||||
}
|
||||
crypto_sha512_hmac_update(&ctx, info, info_size);
|
||||
crypto_sha512_hmac_update(&ctx, &ctr, 1);
|
||||
crypto_sha512_hmac_final(&ctx, blk);
|
||||
|
||||
COPY(okm, blk, out_size);
|
||||
|
||||
not_first = 1;
|
||||
okm += out_size;
|
||||
okm_size -= out_size;
|
||||
ctr++;
|
||||
}
|
||||
WIPE_BUFFER(blk);
|
||||
}
|
||||
|
||||
void crypto_sha512_hkdf(u8 *okm , size_t okm_size,
|
||||
const u8 *ikm , size_t ikm_size,
|
||||
const u8 *salt, size_t salt_size,
|
||||
const u8 *info, size_t info_size)
|
||||
{
|
||||
// Extract
|
||||
u8 prk[64];
|
||||
crypto_sha512_hmac(prk, salt, salt_size, ikm, ikm_size);
|
||||
|
||||
// Expand
|
||||
crypto_sha512_hkdf_expand(okm, okm_size, prk, sizeof(prk), info, info_size);
|
||||
|
||||
WIPE_BUFFER(prk);
|
||||
}
|
||||
|
||||
///////////////
|
||||
/// Ed25519 ///
|
||||
///////////////
|
||||
void crypto_ed25519_key_pair(u8 secret_key[64], u8 public_key[32], u8 seed[32])
|
||||
{
|
||||
u8 a[64];
|
||||
COPY(a, seed, 32); // a[ 0..31] = seed
|
||||
crypto_wipe(seed, 32);
|
||||
COPY(secret_key, a, 32); // secret key = seed
|
||||
crypto_sha512(a, a, 32); // a[ 0..31] = scalar
|
||||
crypto_eddsa_trim_scalar(a, a); // a[ 0..31] = trimmed scalar
|
||||
crypto_eddsa_scalarbase(public_key, a); // public key = [trimmed scalar]B
|
||||
COPY(secret_key + 32, public_key, 32); // secret key includes public half
|
||||
WIPE_BUFFER(a);
|
||||
}
|
||||
|
||||
static void hash_reduce(u8 h[32],
|
||||
const u8 *a, size_t a_size,
|
||||
const u8 *b, size_t b_size,
|
||||
const u8 *c, size_t c_size,
|
||||
const u8 *d, size_t d_size)
|
||||
{
|
||||
u8 hash[64];
|
||||
crypto_sha512_ctx ctx;
|
||||
crypto_sha512_init (&ctx);
|
||||
crypto_sha512_update(&ctx, a, a_size);
|
||||
crypto_sha512_update(&ctx, b, b_size);
|
||||
crypto_sha512_update(&ctx, c, c_size);
|
||||
crypto_sha512_update(&ctx, d, d_size);
|
||||
crypto_sha512_final (&ctx, hash);
|
||||
crypto_eddsa_reduce(h, hash);
|
||||
}
|
||||
|
||||
static void ed25519_dom_sign(u8 signature[64], const u8 secret_key[64],
|
||||
const u8 *dom, size_t dom_size,
|
||||
const u8 *message, size_t message_size)
|
||||
{
|
||||
u8 a[64]; // secret scalar and prefix
|
||||
u8 r[32]; // secret deterministic "random" nonce
|
||||
u8 h[32]; // publically verifiable hash of the message (not wiped)
|
||||
u8 R[32]; // first half of the signature (allows overlapping inputs)
|
||||
const u8 *pk = secret_key + 32;
|
||||
|
||||
crypto_sha512(a, secret_key, 32);
|
||||
crypto_eddsa_trim_scalar(a, a);
|
||||
hash_reduce(r, dom, dom_size, a + 32, 32, message, message_size, 0, 0);
|
||||
crypto_eddsa_scalarbase(R, r);
|
||||
hash_reduce(h, dom, dom_size, R, 32, pk, 32, message, message_size);
|
||||
COPY(signature, R, 32);
|
||||
crypto_eddsa_mul_add(signature + 32, h, a, r);
|
||||
|
||||
WIPE_BUFFER(a);
|
||||
WIPE_BUFFER(r);
|
||||
}
|
||||
|
||||
void crypto_ed25519_sign(u8 signature [64], const u8 secret_key[64],
|
||||
const u8 *message, size_t message_size)
|
||||
{
|
||||
ed25519_dom_sign(signature, secret_key, 0, 0, message, message_size);
|
||||
}
|
||||
|
||||
int crypto_ed25519_check(const u8 signature[64], const u8 public_key[32],
|
||||
const u8 *msg, size_t msg_size)
|
||||
{
|
||||
u8 h_ram[32];
|
||||
hash_reduce(h_ram, signature, 32, public_key, 32, msg, msg_size, 0, 0);
|
||||
return crypto_eddsa_check_equation(signature, public_key, h_ram);
|
||||
}
|
||||
|
||||
static const u8 domain[34] = "SigEd25519 no Ed25519 collisions\1";
|
||||
|
||||
void crypto_ed25519_ph_sign(uint8_t signature[64], const uint8_t secret_key[64],
|
||||
const uint8_t message_hash[64])
|
||||
{
|
||||
ed25519_dom_sign(signature, secret_key, domain, sizeof(domain),
|
||||
message_hash, 64);
|
||||
}
|
||||
|
||||
int crypto_ed25519_ph_check(const uint8_t sig[64], const uint8_t pk[32],
|
||||
const uint8_t msg_hash[64])
|
||||
{
|
||||
u8 h_ram[32];
|
||||
hash_reduce(h_ram, domain, sizeof(domain), sig, 32, pk, 32, msg_hash, 64);
|
||||
return crypto_eddsa_check_equation(sig, pk, h_ram);
|
||||
}
|
||||
|
||||
|
||||
#ifdef MONOCYPHER_CPP_NAMESPACE
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,140 @@
|
||||
// Monocypher version 4.0.3
|
||||
//
|
||||
// This file is dual-licensed. Choose whichever licence you want from
|
||||
// the two licences listed below.
|
||||
//
|
||||
// The first licence is a regular 2-clause BSD licence. The second licence
|
||||
// is the CC-0 from Creative Commons. It is intended to release Monocypher
|
||||
// to the public domain. The BSD licence serves as a fallback option.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-2-Clause OR CC0-1.0
|
||||
//
|
||||
// ------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2017-2019, Loup Vaillant
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// ------------------------------------------------------------------------
|
||||
//
|
||||
// Written in 2017-2019 by Loup Vaillant
|
||||
//
|
||||
// To the extent possible under law, the author(s) have dedicated all copyright
|
||||
// and related neighboring rights to this software to the public domain
|
||||
// worldwide. This software is distributed without any warranty.
|
||||
//
|
||||
// You should have received a copy of the CC0 Public Domain Dedication along
|
||||
// with this software. If not, see
|
||||
// <https://creativecommons.org/publicdomain/zero/1.0/>
|
||||
|
||||
#ifndef ED25519_H
|
||||
#define ED25519_H
|
||||
|
||||
#include "monocypher.h"
|
||||
|
||||
#ifdef MONOCYPHER_CPP_NAMESPACE
|
||||
namespace MONOCYPHER_CPP_NAMESPACE {
|
||||
#elif defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
////////////////////////
|
||||
/// Type definitions ///
|
||||
////////////////////////
|
||||
|
||||
// Do not rely on the size or content on any of those types,
|
||||
// they may change without notice.
|
||||
typedef struct {
|
||||
uint64_t hash[8];
|
||||
uint64_t input[16];
|
||||
uint64_t input_size[2];
|
||||
size_t input_idx;
|
||||
} crypto_sha512_ctx;
|
||||
|
||||
typedef struct {
|
||||
uint8_t key[128];
|
||||
crypto_sha512_ctx ctx;
|
||||
} crypto_sha512_hmac_ctx;
|
||||
|
||||
|
||||
// SHA 512
|
||||
// -------
|
||||
void crypto_sha512_init (crypto_sha512_ctx *ctx);
|
||||
void crypto_sha512_update(crypto_sha512_ctx *ctx,
|
||||
const uint8_t *message, size_t message_size);
|
||||
void crypto_sha512_final (crypto_sha512_ctx *ctx, uint8_t hash[64]);
|
||||
void crypto_sha512(uint8_t hash[64],
|
||||
const uint8_t *message, size_t message_size);
|
||||
|
||||
// SHA 512 HMAC
|
||||
// ------------
|
||||
void crypto_sha512_hmac_init(crypto_sha512_hmac_ctx *ctx,
|
||||
const uint8_t *key, size_t key_size);
|
||||
void crypto_sha512_hmac_update(crypto_sha512_hmac_ctx *ctx,
|
||||
const uint8_t *message, size_t message_size);
|
||||
void crypto_sha512_hmac_final(crypto_sha512_hmac_ctx *ctx, uint8_t hmac[64]);
|
||||
void crypto_sha512_hmac(uint8_t hmac[64],
|
||||
const uint8_t *key , size_t key_size,
|
||||
const uint8_t *message, size_t message_size);
|
||||
|
||||
// SHA 512 HKDF
|
||||
// ------------
|
||||
void crypto_sha512_hkdf_expand(uint8_t *okm, size_t okm_size,
|
||||
const uint8_t *prk, size_t prk_size,
|
||||
const uint8_t *info, size_t info_size);
|
||||
void crypto_sha512_hkdf(uint8_t *okm , size_t okm_size,
|
||||
const uint8_t *ikm , size_t ikm_size,
|
||||
const uint8_t *salt, size_t salt_size,
|
||||
const uint8_t *info, size_t info_size);
|
||||
|
||||
// Ed25519
|
||||
// -------
|
||||
// Signatures (EdDSA with curve25519 + SHA-512)
|
||||
// --------------------------------------------
|
||||
void crypto_ed25519_key_pair(uint8_t secret_key[64],
|
||||
uint8_t public_key[32],
|
||||
uint8_t seed[32]);
|
||||
void crypto_ed25519_sign(uint8_t signature [64],
|
||||
const uint8_t secret_key[64],
|
||||
const uint8_t *message, size_t message_size);
|
||||
int crypto_ed25519_check(const uint8_t signature [64],
|
||||
const uint8_t public_key[32],
|
||||
const uint8_t *message, size_t message_size);
|
||||
|
||||
// Pre-hash variants
|
||||
void crypto_ed25519_ph_sign(uint8_t signature [64],
|
||||
const uint8_t secret_key [64],
|
||||
const uint8_t message_hash[64]);
|
||||
int crypto_ed25519_ph_check(const uint8_t signature [64],
|
||||
const uint8_t public_key [32],
|
||||
const uint8_t message_hash[64]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // ED25519_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,321 @@
|
||||
// Monocypher version 4.0.3
|
||||
//
|
||||
// This file is dual-licensed. Choose whichever licence you want from
|
||||
// the two licences listed below.
|
||||
//
|
||||
// The first licence is a regular 2-clause BSD licence. The second licence
|
||||
// is the CC-0 from Creative Commons. It is intended to release Monocypher
|
||||
// to the public domain. The BSD licence serves as a fallback option.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-2-Clause OR CC0-1.0
|
||||
//
|
||||
// ------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2017-2019, Loup Vaillant
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// ------------------------------------------------------------------------
|
||||
//
|
||||
// Written in 2017-2019 by Loup Vaillant
|
||||
//
|
||||
// To the extent possible under law, the author(s) have dedicated all copyright
|
||||
// and related neighboring rights to this software to the public domain
|
||||
// worldwide. This software is distributed without any warranty.
|
||||
//
|
||||
// You should have received a copy of the CC0 Public Domain Dedication along
|
||||
// with this software. If not, see
|
||||
// <https://creativecommons.org/publicdomain/zero/1.0/>
|
||||
|
||||
#ifndef MONOCYPHER_H
|
||||
#define MONOCYPHER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef MONOCYPHER_CPP_NAMESPACE
|
||||
namespace MONOCYPHER_CPP_NAMESPACE {
|
||||
#elif defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Constant time comparisons
|
||||
// -------------------------
|
||||
|
||||
// Return 0 if a and b are equal, -1 otherwise
|
||||
int crypto_verify16(const uint8_t a[16], const uint8_t b[16]);
|
||||
int crypto_verify32(const uint8_t a[32], const uint8_t b[32]);
|
||||
int crypto_verify64(const uint8_t a[64], const uint8_t b[64]);
|
||||
|
||||
|
||||
// Erase sensitive data
|
||||
// --------------------
|
||||
void crypto_wipe(void *secret, size_t size);
|
||||
|
||||
|
||||
// Authenticated encryption
|
||||
// ------------------------
|
||||
void crypto_aead_lock(uint8_t *cipher_text,
|
||||
uint8_t mac [16],
|
||||
const uint8_t key [32],
|
||||
const uint8_t nonce[24],
|
||||
const uint8_t *ad, size_t ad_size,
|
||||
const uint8_t *plain_text, size_t text_size);
|
||||
int crypto_aead_unlock(uint8_t *plain_text,
|
||||
const uint8_t mac [16],
|
||||
const uint8_t key [32],
|
||||
const uint8_t nonce[24],
|
||||
const uint8_t *ad, size_t ad_size,
|
||||
const uint8_t *cipher_text, size_t text_size);
|
||||
|
||||
// Authenticated stream
|
||||
// --------------------
|
||||
typedef struct {
|
||||
uint64_t counter;
|
||||
uint8_t key[32];
|
||||
uint8_t nonce[8];
|
||||
} crypto_aead_ctx;
|
||||
|
||||
void crypto_aead_init_x(crypto_aead_ctx *ctx,
|
||||
const uint8_t key[32], const uint8_t nonce[24]);
|
||||
void crypto_aead_init_djb(crypto_aead_ctx *ctx,
|
||||
const uint8_t key[32], const uint8_t nonce[8]);
|
||||
void crypto_aead_init_ietf(crypto_aead_ctx *ctx,
|
||||
const uint8_t key[32], const uint8_t nonce[12]);
|
||||
|
||||
void crypto_aead_write(crypto_aead_ctx *ctx,
|
||||
uint8_t *cipher_text,
|
||||
uint8_t mac[16],
|
||||
const uint8_t *ad , size_t ad_size,
|
||||
const uint8_t *plain_text, size_t text_size);
|
||||
int crypto_aead_read(crypto_aead_ctx *ctx,
|
||||
uint8_t *plain_text,
|
||||
const uint8_t mac[16],
|
||||
const uint8_t *ad , size_t ad_size,
|
||||
const uint8_t *cipher_text, size_t text_size);
|
||||
|
||||
|
||||
// General purpose hash (BLAKE2b)
|
||||
// ------------------------------
|
||||
|
||||
// Direct interface
|
||||
void crypto_blake2b(uint8_t *hash, size_t hash_size,
|
||||
const uint8_t *message, size_t message_size);
|
||||
|
||||
void crypto_blake2b_keyed(uint8_t *hash, size_t hash_size,
|
||||
const uint8_t *key, size_t key_size,
|
||||
const uint8_t *message, size_t message_size);
|
||||
|
||||
// Incremental interface
|
||||
typedef struct {
|
||||
// Do not rely on the size or contents of this type,
|
||||
// for they may change without notice.
|
||||
uint64_t hash[8];
|
||||
uint64_t input_offset[2];
|
||||
uint64_t input[16];
|
||||
size_t input_idx;
|
||||
size_t hash_size;
|
||||
} crypto_blake2b_ctx;
|
||||
|
||||
void crypto_blake2b_init(crypto_blake2b_ctx *ctx, size_t hash_size);
|
||||
void crypto_blake2b_keyed_init(crypto_blake2b_ctx *ctx, size_t hash_size,
|
||||
const uint8_t *key, size_t key_size);
|
||||
void crypto_blake2b_update(crypto_blake2b_ctx *ctx,
|
||||
const uint8_t *message, size_t message_size);
|
||||
void crypto_blake2b_final(crypto_blake2b_ctx *ctx, uint8_t *hash);
|
||||
|
||||
|
||||
// Password key derivation (Argon2)
|
||||
// --------------------------------
|
||||
#define CRYPTO_ARGON2_D 0
|
||||
#define CRYPTO_ARGON2_I 1
|
||||
#define CRYPTO_ARGON2_ID 2
|
||||
|
||||
typedef struct {
|
||||
uint32_t algorithm; // Argon2d, Argon2i, Argon2id
|
||||
uint32_t nb_blocks; // memory hardness, >= 8 * nb_lanes
|
||||
uint32_t nb_passes; // CPU hardness, >= 1 (>= 3 recommended for Argon2i)
|
||||
uint32_t nb_lanes; // parallelism level (single threaded anyway)
|
||||
} crypto_argon2_config;
|
||||
|
||||
typedef struct {
|
||||
const uint8_t *pass;
|
||||
const uint8_t *salt;
|
||||
uint32_t pass_size;
|
||||
uint32_t salt_size; // 16 bytes recommended
|
||||
} crypto_argon2_inputs;
|
||||
|
||||
typedef struct {
|
||||
const uint8_t *key; // may be NULL if no key
|
||||
const uint8_t *ad; // may be NULL if no additional data
|
||||
uint32_t key_size; // 0 if no key (32 bytes recommended otherwise)
|
||||
uint32_t ad_size; // 0 if no additional data
|
||||
} crypto_argon2_extras;
|
||||
|
||||
extern const crypto_argon2_extras crypto_argon2_no_extras;
|
||||
|
||||
void crypto_argon2(uint8_t *hash, uint32_t hash_size, void *work_area,
|
||||
crypto_argon2_config config,
|
||||
crypto_argon2_inputs inputs,
|
||||
crypto_argon2_extras extras);
|
||||
|
||||
|
||||
// Key exchange (X-25519)
|
||||
// ----------------------
|
||||
|
||||
// Shared secrets are not quite random.
|
||||
// Hash them to derive an actual shared key.
|
||||
void crypto_x25519_public_key(uint8_t public_key[32],
|
||||
const uint8_t secret_key[32]);
|
||||
void crypto_x25519(uint8_t raw_shared_secret[32],
|
||||
const uint8_t your_secret_key [32],
|
||||
const uint8_t their_public_key [32]);
|
||||
|
||||
// Conversion to EdDSA
|
||||
void crypto_x25519_to_eddsa(uint8_t eddsa[32], const uint8_t x25519[32]);
|
||||
|
||||
// scalar "division"
|
||||
// Used for OPRF. Be aware that exponential blinding is less secure
|
||||
// than Diffie-Hellman key exchange.
|
||||
void crypto_x25519_inverse(uint8_t blind_salt [32],
|
||||
const uint8_t private_key[32],
|
||||
const uint8_t curve_point[32]);
|
||||
|
||||
// "Dirty" versions of x25519_public_key().
|
||||
// Use with crypto_elligator_rev().
|
||||
// Leaks 3 bits of the private key.
|
||||
void crypto_x25519_dirty_small(uint8_t pk[32], const uint8_t sk[32]);
|
||||
void crypto_x25519_dirty_fast (uint8_t pk[32], const uint8_t sk[32]);
|
||||
|
||||
|
||||
// Signatures
|
||||
// ----------
|
||||
|
||||
// EdDSA with curve25519 + BLAKE2b
|
||||
void crypto_eddsa_key_pair(uint8_t secret_key[64],
|
||||
uint8_t public_key[32],
|
||||
uint8_t seed[32]);
|
||||
void crypto_eddsa_sign(uint8_t signature [64],
|
||||
const uint8_t secret_key[64],
|
||||
const uint8_t *message, size_t message_size);
|
||||
int crypto_eddsa_check(const uint8_t signature [64],
|
||||
const uint8_t public_key[32],
|
||||
const uint8_t *message, size_t message_size);
|
||||
|
||||
// Conversion to X25519
|
||||
void crypto_eddsa_to_x25519(uint8_t x25519[32], const uint8_t eddsa[32]);
|
||||
|
||||
// EdDSA building blocks
|
||||
void crypto_eddsa_trim_scalar(uint8_t out[32], const uint8_t in[32]);
|
||||
void crypto_eddsa_reduce(uint8_t reduced[32], const uint8_t expanded[64]);
|
||||
void crypto_eddsa_mul_add(uint8_t r[32],
|
||||
const uint8_t a[32],
|
||||
const uint8_t b[32],
|
||||
const uint8_t c[32]);
|
||||
void crypto_eddsa_scalarbase(uint8_t point[32], const uint8_t scalar[32]);
|
||||
int crypto_eddsa_check_equation(const uint8_t signature[64],
|
||||
const uint8_t public_key[32],
|
||||
const uint8_t h_ram[32]);
|
||||
|
||||
|
||||
// Chacha20
|
||||
// --------
|
||||
|
||||
// Specialised hash.
|
||||
// Used to hash X25519 shared secrets.
|
||||
void crypto_chacha20_h(uint8_t out[32],
|
||||
const uint8_t key[32],
|
||||
const uint8_t in [16]);
|
||||
|
||||
// Unauthenticated stream cipher.
|
||||
// Don't forget to add authentication.
|
||||
uint64_t crypto_chacha20_djb(uint8_t *cipher_text,
|
||||
const uint8_t *plain_text,
|
||||
size_t text_size,
|
||||
const uint8_t key[32],
|
||||
const uint8_t nonce[8],
|
||||
uint64_t ctr);
|
||||
uint32_t crypto_chacha20_ietf(uint8_t *cipher_text,
|
||||
const uint8_t *plain_text,
|
||||
size_t text_size,
|
||||
const uint8_t key[32],
|
||||
const uint8_t nonce[12],
|
||||
uint32_t ctr);
|
||||
uint64_t crypto_chacha20_x(uint8_t *cipher_text,
|
||||
const uint8_t *plain_text,
|
||||
size_t text_size,
|
||||
const uint8_t key[32],
|
||||
const uint8_t nonce[24],
|
||||
uint64_t ctr);
|
||||
|
||||
|
||||
// Poly 1305
|
||||
// ---------
|
||||
|
||||
// This is a *one time* authenticator.
|
||||
// Disclosing the mac reveals the key.
|
||||
// See crypto_lock() on how to use it properly.
|
||||
|
||||
// Direct interface
|
||||
void crypto_poly1305(uint8_t mac[16],
|
||||
const uint8_t *message, size_t message_size,
|
||||
const uint8_t key[32]);
|
||||
|
||||
// Incremental interface
|
||||
typedef struct {
|
||||
// Do not rely on the size or contents of this type,
|
||||
// for they may change without notice.
|
||||
uint8_t c[16]; // chunk of the message
|
||||
size_t c_idx; // How many bytes are there in the chunk.
|
||||
uint32_t r [4]; // constant multiplier (from the secret key)
|
||||
uint32_t pad[4]; // random number added at the end (from the secret key)
|
||||
uint32_t h [5]; // accumulated hash
|
||||
} crypto_poly1305_ctx;
|
||||
|
||||
void crypto_poly1305_init (crypto_poly1305_ctx *ctx, const uint8_t key[32]);
|
||||
void crypto_poly1305_update(crypto_poly1305_ctx *ctx,
|
||||
const uint8_t *message, size_t message_size);
|
||||
void crypto_poly1305_final (crypto_poly1305_ctx *ctx, uint8_t mac[16]);
|
||||
|
||||
|
||||
// Elligator 2
|
||||
// -----------
|
||||
|
||||
// Elligator mappings proper
|
||||
void crypto_elligator_map(uint8_t curve [32], const uint8_t hidden[32]);
|
||||
int crypto_elligator_rev(uint8_t hidden[32], const uint8_t curve [32],
|
||||
uint8_t tweak);
|
||||
|
||||
// Easy to use key pair generation
|
||||
void crypto_elligator_key_pair(uint8_t hidden[32], uint8_t secret_key[32],
|
||||
uint8_t seed[32]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // MONOCYPHER_H
|
||||
@@ -0,0 +1,292 @@
|
||||
#include "ch32v20x_misc.h"
|
||||
#include "stdint.h"
|
||||
#include "rtc.h"
|
||||
|
||||
_calendar_obj calendar;
|
||||
|
||||
|
||||
uint8_t const table_week[12] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
|
||||
const uint8_t mon_table[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
|
||||
/*********************************************************************
|
||||
* @fn RTC_NVIC_Config
|
||||
*
|
||||
* @brief Initializes RTC Int.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void RTC_NVIC_Config (void) {
|
||||
NVIC_InitTypeDef NVIC_InitStructure = {0};
|
||||
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
|
||||
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
|
||||
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
|
||||
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
|
||||
NVIC_Init (&NVIC_InitStructure);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* @fn Is_Leap_Year
|
||||
*
|
||||
* @brief Judging whether it is a leap year.
|
||||
*
|
||||
* @param year
|
||||
*
|
||||
* @return 1 - Yes
|
||||
* 0 - No
|
||||
*/
|
||||
uint8_t Is_Leap_Year (uint16_t year) {
|
||||
if (year % 4 == 0) {
|
||||
if (year % 100 == 0) {
|
||||
if (year % 400 == 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
} else
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t month_from_str(const char *m) {
|
||||
static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
|
||||
for (uint8_t i = 0; i < 12; i++) {
|
||||
if (m[0] == months[i*3] &&
|
||||
m[1] == months[i*3 + 1] &&
|
||||
m[2] == months[i*3 + 2])
|
||||
return i + 1; // 1¨C12
|
||||
}
|
||||
return 0; // should never happen
|
||||
}
|
||||
|
||||
void RTC_Set_From_BuildTime(void) {
|
||||
const char *date = __DATE__;
|
||||
const char *time = __TIME__;
|
||||
|
||||
uint16_t year =
|
||||
(date[7] - '0') * 1000 +
|
||||
(date[8] - '0') * 100 +
|
||||
(date[9] - '0') * 10 +
|
||||
(date[10] - '0');
|
||||
|
||||
uint8_t month = month_from_str(date);
|
||||
uint8_t day =
|
||||
(date[4] == ' ' ? 0 : (date[4] - '0') * 10) +
|
||||
(date[5] - '0');
|
||||
|
||||
uint8_t hour =
|
||||
(time[0] - '0') * 10 + (time[1] - '0') - 1;
|
||||
uint8_t min =
|
||||
(time[3] - '0') * 10 + (time[4] - '0');
|
||||
uint8_t sec =
|
||||
(time[6] - '0') * 10 + (time[7] - '0');
|
||||
|
||||
RTC_Set(year, month, day, hour, min, sec);
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
* @fn RTC_Set
|
||||
*
|
||||
* @brief Set Time.
|
||||
*
|
||||
* @param Struct of _calendar_obj
|
||||
*
|
||||
* @return 1 - error
|
||||
* 0 - success
|
||||
*/
|
||||
uint8_t RTC_Set (uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec) {
|
||||
uint16_t t;
|
||||
u32 seccount = 0;
|
||||
if (syear < 1970 || syear > 2099)
|
||||
return 1;
|
||||
for (t = 1970; t < syear; t++) {
|
||||
if (Is_Leap_Year (t))
|
||||
seccount += 31622400;
|
||||
else
|
||||
seccount += 31536000;
|
||||
}
|
||||
smon -= 1;
|
||||
for (t = 0; t < smon; t++) {
|
||||
seccount += (u32)mon_table[t] * 86400;
|
||||
if (Is_Leap_Year (syear) && t == 1)
|
||||
seccount += 86400;
|
||||
}
|
||||
seccount += (u32)(sday - 1) * 86400;
|
||||
seccount += (u32)hour * 3600;
|
||||
seccount += (u32)min * 60;
|
||||
seccount += sec;
|
||||
|
||||
RCC_APB1PeriphClockCmd (RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
|
||||
PWR_BackupAccessCmd (ENABLE);
|
||||
RTC_SetCounter (seccount);
|
||||
RTC_WaitForLastTask();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* @fn RTC_Alarm_Set
|
||||
*
|
||||
* @brief Set Alarm Time.
|
||||
*
|
||||
* @param Struct of _calendar_obj
|
||||
*
|
||||
* @return 1 - error
|
||||
* 0 - success
|
||||
*/
|
||||
uint8_t RTC_Alarm_Set (uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec) {
|
||||
uint16_t t;
|
||||
u32 seccount = 0;
|
||||
if (syear < 1970 || syear > 2099)
|
||||
return 1;
|
||||
for (t = 1970; t < syear; t++) {
|
||||
if (Is_Leap_Year (t))
|
||||
seccount += 31622400;
|
||||
else
|
||||
seccount += 31536000;
|
||||
}
|
||||
smon -= 1;
|
||||
for (t = 0; t < smon; t++) {
|
||||
seccount += (u32)mon_table[t] * 86400;
|
||||
if (Is_Leap_Year (syear) && t == 1)
|
||||
seccount += 86400;
|
||||
}
|
||||
seccount += (u32)(sday - 1) * 86400;
|
||||
seccount += (u32)hour * 3600;
|
||||
seccount += (u32)min * 60;
|
||||
seccount += sec;
|
||||
|
||||
RCC_APB1PeriphClockCmd (RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
|
||||
PWR_BackupAccessCmd (ENABLE);
|
||||
RTC_SetAlarm (seccount);
|
||||
RTC_WaitForLastTask();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* @fn RTC_Get_Week
|
||||
*
|
||||
* @brief Get the current day of the week.
|
||||
*
|
||||
* @param year/month/day
|
||||
*
|
||||
* @return week
|
||||
*/
|
||||
uint8_t RTC_Get_Week (uint16_t year, uint8_t month, uint8_t day) {
|
||||
uint16_t temp2;
|
||||
uint8_t yearH, yearL;
|
||||
|
||||
yearH = year / 100;
|
||||
yearL = year % 100;
|
||||
if (yearH > 19)
|
||||
yearL += 100;
|
||||
temp2 = yearL + yearL / 4;
|
||||
temp2 = temp2 % 7;
|
||||
temp2 = temp2 + day + table_week[month - 1];
|
||||
if (yearL % 4 == 0 && month < 3)
|
||||
temp2--;
|
||||
return (temp2 % 7);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* @fn RTC_Get
|
||||
*
|
||||
* @brief Get current time.
|
||||
*
|
||||
* @return 1 - error
|
||||
* 0 - success
|
||||
*/
|
||||
uint8_t RTC_Get (void) {
|
||||
static uint16_t daycnt = 0;
|
||||
u32 timecount = 0;
|
||||
u32 temp = 0;
|
||||
uint16_t temp1 = 0;
|
||||
timecount = RTC_GetCounter();
|
||||
temp = timecount / 86400;
|
||||
if (daycnt != temp) {
|
||||
daycnt = temp;
|
||||
temp1 = 1970;
|
||||
while (temp >= 365) {
|
||||
if (Is_Leap_Year (temp1)) {
|
||||
if (temp >= 366)
|
||||
temp -= 366;
|
||||
else {
|
||||
break;
|
||||
}
|
||||
} else
|
||||
temp -= 365;
|
||||
temp1++;
|
||||
}
|
||||
calendar.w_year = temp1;
|
||||
temp1 = 0;
|
||||
while (temp >= 28) {
|
||||
if (Is_Leap_Year (calendar.w_year) && temp1 == 1) {
|
||||
if (temp >= 29)
|
||||
temp -= 29;
|
||||
else
|
||||
break;
|
||||
} else {
|
||||
if (temp >= mon_table[temp1])
|
||||
temp -= mon_table[temp1];
|
||||
else
|
||||
break;
|
||||
}
|
||||
temp1++;
|
||||
}
|
||||
calendar.w_month = temp1 + 1;
|
||||
calendar.w_date = temp + 1;
|
||||
}
|
||||
temp = timecount % 86400;
|
||||
calendar.hour = temp / 3600;
|
||||
calendar.min = (temp % 3600) / 60;
|
||||
calendar.sec = (temp % 3600) % 60;
|
||||
calendar.week = RTC_Get_Week (calendar.w_year, calendar.w_month, calendar.w_date);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* @fn RTC_Init
|
||||
*
|
||||
* @brief Initializes RTC collection.
|
||||
*
|
||||
* @return 1 - Init Fail
|
||||
* 0 - Init Success
|
||||
*/
|
||||
uint8_t RTC_Init (void) {
|
||||
uint8_t temp = 0;
|
||||
RCC_APB1PeriphClockCmd (RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
|
||||
PWR_BackupAccessCmd (ENABLE);
|
||||
RTC_ClearITPendingBit (RTC_IT_ALR);
|
||||
RTC_ClearITPendingBit (RTC_IT_SEC);
|
||||
|
||||
/* Is it the first configuration */
|
||||
|
||||
BKP_DeInit();
|
||||
RCC_LSEConfig (RCC_LSE_ON);
|
||||
while (RCC_GetFlagStatus (RCC_FLAG_LSERDY) == RESET && temp < 250) {
|
||||
temp++;
|
||||
Delay_Ms(20);
|
||||
}
|
||||
if (temp >= 250)
|
||||
return 1;
|
||||
RCC_RTCCLKConfig (RCC_RTCCLKSource_LSE);
|
||||
RCC_RTCCLKCmd (ENABLE);
|
||||
RTC_WaitForLastTask();
|
||||
RTC_WaitForSynchro();
|
||||
RTC_ITConfig (RTC_IT_SEC, DISABLE);
|
||||
RTC_ITConfig (RTC_IT_ALR, DISABLE);
|
||||
RTC_ITConfig (RTC_IT_OW, DISABLE);
|
||||
RTC_WaitForLastTask();
|
||||
RTC_EnterConfigMode();
|
||||
RTC_SetPrescaler (32767);
|
||||
RTC_WaitForLastTask();
|
||||
//RTC_Set (2025, 9, 7, 11, 33, 30); /* Setup Time */
|
||||
RTC_Set_From_BuildTime();
|
||||
RTC_ExitConfigMode();
|
||||
BKP_WriteBackupRegister (BKP_DR1, 0XA1A1);
|
||||
|
||||
RTC_NVIC_Config();
|
||||
RTC_Get();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#include "stdint.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
volatile uint8_t hour;
|
||||
volatile uint8_t min;
|
||||
volatile uint8_t sec;
|
||||
|
||||
volatile uint16_t w_year;
|
||||
volatile uint8_t w_month;
|
||||
volatile uint8_t w_date;
|
||||
volatile uint8_t week;
|
||||
} _calendar_obj;
|
||||
|
||||
extern _calendar_obj calendar;
|
||||
|
||||
|
||||
extern uint8_t const table_week[12];
|
||||
extern const uint8_t mon_table[12];
|
||||
|
||||
/*********************************************************************
|
||||
* @fn RTC_NVIC_Config
|
||||
*
|
||||
* @brief Initializes RTC Int.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void RTC_NVIC_Config (void);
|
||||
/*********************************************************************
|
||||
* @fn Is_Leap_Year
|
||||
*
|
||||
* @brief Judging whether it is a leap year.
|
||||
*
|
||||
* @param year
|
||||
*
|
||||
* @return 1 - Yes
|
||||
* 0 - No
|
||||
*/
|
||||
uint8_t Is_Leap_Year (uint16_t year);
|
||||
|
||||
void RTC_Set_From_BuildTime(void);
|
||||
|
||||
/*********************************************************************
|
||||
* @fn RTC_Set
|
||||
*
|
||||
* @brief Set Time.
|
||||
*
|
||||
* @param Struct of _calendar_obj
|
||||
*
|
||||
* @return 1 - error
|
||||
* 0 - success
|
||||
*/
|
||||
uint8_t RTC_Set (uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec) ;
|
||||
|
||||
/*********************************************************************
|
||||
* @fn RTC_Alarm_Set
|
||||
*
|
||||
* @brief Set Alarm Time.
|
||||
*
|
||||
* @param Struct of _calendar_obj
|
||||
*
|
||||
* @return 1 - error
|
||||
* 0 - success
|
||||
*/
|
||||
uint8_t RTC_Alarm_Set (uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec) ;
|
||||
/*********************************************************************
|
||||
* @fn RTC_Get_Week
|
||||
*
|
||||
* @brief Get the current day of the week.
|
||||
*
|
||||
* @param year/month/day
|
||||
*
|
||||
* @return week
|
||||
*/
|
||||
uint8_t RTC_Get_Week (uint16_t year, uint8_t month, uint8_t day) ;
|
||||
|
||||
/*********************************************************************
|
||||
* @fn RTC_Get
|
||||
*
|
||||
* @brief Get current time.
|
||||
*
|
||||
* @return 1 - error
|
||||
* 0 - success
|
||||
*/
|
||||
uint8_t RTC_Get (void) ;
|
||||
|
||||
/*********************************************************************
|
||||
* @fn RTC_Init
|
||||
*
|
||||
* @brief Initializes RTC collection.
|
||||
*
|
||||
* @return 1 - Init Fail
|
||||
* 0 - Init Success
|
||||
*/
|
||||
uint8_t RTC_Init (void);
|
||||
@@ -0,0 +1,93 @@
|
||||
#ifndef TELEMETRY_HEADER
|
||||
#define TELEMETRY_HEADER
|
||||
|
||||
#define TELEM_CHANNEL_SELF 1 // LPP data channel for 'self' device
|
||||
|
||||
#define LPP_DIGITAL_INPUT 0 // 1 byte
|
||||
#define LPP_DIGITAL_OUTPUT 1 // 1 byte
|
||||
#define LPP_ANALOG_INPUT 2 // 2 bytes, 0.01 signed
|
||||
#define LPP_ANALOG_OUTPUT 3 // 2 bytes, 0.01 signed
|
||||
#define LPP_GENERIC_SENSOR 100 // 4 bytes, unsigned
|
||||
#define LPP_LUMINOSITY 101 // 2 bytes, 1 lux unsigned
|
||||
#define LPP_PRESENCE 102 // 1 byte, bool
|
||||
#define LPP_TEMPERATURE 103 // 2 bytes, 0.1¡ãC signed
|
||||
#define LPP_RELATIVE_HUMIDITY 104 // 1 byte, 0.5% unsigned
|
||||
#define LPP_ACCELEROMETER 113 // 2 bytes per axis, 0.001G
|
||||
#define LPP_BAROMETRIC_PRESSURE 115 // 2 bytes 0.1hPa unsigned
|
||||
#define LPP_VOLTAGE 116 // 2 bytes 0.01V unsigned
|
||||
#define LPP_CURRENT 117 // 2 bytes 0.001A unsigned
|
||||
#define LPP_FREQUENCY 118 // 4 bytes 1Hz unsigned
|
||||
#define LPP_PERCENTAGE 120 // 1 byte 1-100% unsigned
|
||||
#define LPP_ALTITUDE 121 // 2 byte 1m signed
|
||||
#define LPP_CONCENTRATION 125 // 2 bytes, 1 ppm unsigned
|
||||
#define LPP_POWER 128 // 2 byte, 1W, unsigned
|
||||
#define LPP_DISTANCE 130 // 4 byte, 0.001m, unsigned
|
||||
#define LPP_ENERGY 131 // 4 byte, 0.001kWh, unsigned
|
||||
#define LPP_DIRECTION 132 // 2 bytes, 1deg, unsigned
|
||||
#define LPP_UNIXTIME 133 // 4 bytes, unsigned
|
||||
#define LPP_GYROMETER 134 // 2 bytes per axis, 0.01 ¡ã/s
|
||||
#define LPP_COLOUR 135 // 1 byte per RGB Color
|
||||
#define LPP_GPS 136 // 3 byte lon/lat 0.0001 ¡ã, 3 bytes alt 0.01 meter
|
||||
#define LPP_SWITCH 142 // 1 byte, 0/1
|
||||
#define LPP_POLYLINE 240 // 1 byte size, 1 byte delta factor, 3 byte lon/lat 0.0001¡ã * factor, n (size-8) bytes deltas
|
||||
|
||||
// Only Data Size
|
||||
#define LPP_DIGITAL_INPUT_SIZE 1
|
||||
#define LPP_DIGITAL_OUTPUT_SIZE 1
|
||||
#define LPP_ANALOG_INPUT_SIZE 2
|
||||
#define LPP_ANALOG_OUTPUT_SIZE 2
|
||||
#define LPP_GENERIC_SENSOR_SIZE 4
|
||||
#define LPP_LUMINOSITY_SIZE 2
|
||||
#define LPP_PRESENCE_SIZE 1
|
||||
#define LPP_TEMPERATURE_SIZE 2
|
||||
#define LPP_RELATIVE_HUMIDITY_SIZE 1
|
||||
#define LPP_ACCELEROMETER_SIZE 6
|
||||
#define LPP_BAROMETRIC_PRESSURE_SIZE 2
|
||||
#define LPP_VOLTAGE_SIZE 2
|
||||
#define LPP_CURRENT_SIZE 2
|
||||
#define LPP_FREQUENCY_SIZE 4
|
||||
#define LPP_PERCENTAGE_SIZE 1
|
||||
#define LPP_ALTITUDE_SIZE 2
|
||||
#define LPP_POWER_SIZE 2
|
||||
#define LPP_DISTANCE_SIZE 4
|
||||
#define LPP_ENERGY_SIZE 4
|
||||
#define LPP_DIRECTION_SIZE 2
|
||||
#define LPP_UNIXTIME_SIZE 4
|
||||
#define LPP_GYROMETER_SIZE 6
|
||||
#define LPP_GPS_SIZE 9
|
||||
#define LPP_SWITCH_SIZE 1
|
||||
#define LPP_CONCENTRATION_SIZE 2
|
||||
#define LPP_COLOUR_SIZE 3
|
||||
#define LPP_MIN_POLYLINE_SIZE 8
|
||||
|
||||
|
||||
// Multipliers
|
||||
#define LPP_DIGITAL_INPUT_MULT 1
|
||||
#define LPP_DIGITAL_OUTPUT_MULT 1
|
||||
#define LPP_ANALOG_INPUT_MULT 100
|
||||
#define LPP_ANALOG_OUTPUT_MULT 100
|
||||
#define LPP_GENERIC_SENSOR_MULT 1
|
||||
#define LPP_LUMINOSITY_MULT 1
|
||||
#define LPP_PRESENCE_MULT 1
|
||||
#define LPP_TEMPERATURE_MULT 10
|
||||
#define LPP_RELATIVE_HUMIDITY_MULT 2
|
||||
#define LPP_ACCELEROMETER_MULT 1000
|
||||
#define LPP_BAROMETRIC_PRESSURE_MULT 10
|
||||
#define LPP_VOLTAGE_MULT 100
|
||||
#define LPP_CURRENT_MULT 1000
|
||||
#define LPP_FREQUENCY_MULT 1
|
||||
#define LPP_PERCENTAGE_MULT 1
|
||||
#define LPP_ALTITUDE_MULT 1
|
||||
#define LPP_POWER_MULT 1
|
||||
#define LPP_DISTANCE_MULT 1000
|
||||
#define LPP_ENERGY_MULT 1000
|
||||
#define LPP_DIRECTION_MULT 1
|
||||
#define LPP_UNIXTIME_MULT 1
|
||||
#define LPP_GYROMETER_MULT 100
|
||||
#define LPP_GPS_LAT_LON_MULT 10000
|
||||
#define LPP_GPS_ALT_MULT 100
|
||||
#define LPP_SWITCH_MULT 1
|
||||
#define LPP_CONCENTRATION_MULT 1
|
||||
#define LPP_COLOUR_MULT 1
|
||||
|
||||
#endif
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
/********************************** (C) COPYRIGHT *******************************
|
||||
* File Name : main.c
|
||||
* Author : WCH
|
||||
* Version : V1.0.0
|
||||
* Date : 2021/06/06
|
||||
* Description : Main program body.
|
||||
*********************************************************************************
|
||||
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
|
||||
* Attention: This software (modified or not) and binary are used for
|
||||
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
|
||||
*******************************************************************************/
|
||||
|
||||
/*
|
||||
*@Note
|
||||
*task1 and task2 alternate printing
|
||||
*/
|
||||
|
||||
//#include "ch32v30x_rng.h"
|
||||
#include "ch32v20x.h"
|
||||
#include "meshcore/meshframing.h"
|
||||
#include "meshcore/packets/advert.h"
|
||||
#include "meshcore/packets/control.h"
|
||||
#include "meshcore/packets/encrypted.h"
|
||||
#include "meshcore/packets/group.h"
|
||||
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "sx1262.h"
|
||||
#include "util/hexdump.h"
|
||||
#include "util/log.h"
|
||||
#include "string.h"
|
||||
#include "meshcore/meshcore.h"
|
||||
#include "lib/config.h"
|
||||
#include "lib/rtc/rtc.h"
|
||||
#include "lib/monocypher/monocypher-ed25519.h"
|
||||
#include "meshcore/stats.h"
|
||||
#include "lib/adc/temperature.h"
|
||||
|
||||
#define TAG "MeshCore"
|
||||
|
||||
|
||||
static TIM_TypeDef *runtimeTIM = TIM2; // use TIM2 for example
|
||||
|
||||
void vConfigureTimerForRunTimeStats (void) {
|
||||
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
|
||||
|
||||
// Reset the timer
|
||||
TIM_DeInit (runtimeTIM);
|
||||
|
||||
// Set timer for max period, upcounting
|
||||
TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // Assuming 72 MHz clock -> 1 MHz timer tick (1 ?s)
|
||||
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
|
||||
TIM_TimeBaseStructure.TIM_Period = 0xFFFF; // Max 16-bit value
|
||||
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
|
||||
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
|
||||
|
||||
TIM_TimeBaseInit (runtimeTIM, &TIM_TimeBaseStructure);
|
||||
TIM_Cmd (runtimeTIM, ENABLE);
|
||||
}
|
||||
|
||||
uint32_t ulGetRunTimeCounterValue (void) {
|
||||
return TIM_GetCounter (runtimeTIM);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* @fn task1_task
|
||||
*
|
||||
* @brief task1 program.
|
||||
*
|
||||
* @param *pvParameters - Parameters point of task1
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
|
||||
// uint8_t bufIn[260];
|
||||
|
||||
uint8_t bootedUp = 0;
|
||||
|
||||
/*********************************************************************
|
||||
* @fn main
|
||||
*
|
||||
* @brief ; program.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
|
||||
int main (void) {
|
||||
|
||||
NVIC_PriorityGroupConfig (NVIC_PriorityGroup_1);
|
||||
SystemCoreClockUpdate();
|
||||
Delay_Init();
|
||||
|
||||
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
USART_InitTypeDef USART_InitStructure;
|
||||
|
||||
RCC_APB2PeriphClockCmd (RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
|
||||
GPIO_Init (GPIOA, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||||
GPIO_Init (GPIOA, &GPIO_InitStructure);
|
||||
|
||||
USART_InitStructure.USART_BaudRate = 115200;
|
||||
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
|
||||
USART_InitStructure.USART_StopBits = USART_StopBits_1;
|
||||
USART_InitStructure.USART_Parity = USART_Parity_No;
|
||||
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
|
||||
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
|
||||
|
||||
USART_Init (USART1, &USART_InitStructure);
|
||||
USART_Cmd (USART1, ENABLE);
|
||||
|
||||
printf("hello there");
|
||||
|
||||
|
||||
|
||||
MESH_LOGD (TAG, "SystemClk:%d\r\n", SystemCoreClock);
|
||||
MESH_LOGD (TAG, "ChipID:%08x\r\n", DBGMCU_GetCHIPID());
|
||||
|
||||
while (1) {
|
||||
// loadConfig();
|
||||
populateDefaults();
|
||||
|
||||
LoraApply();
|
||||
|
||||
ADC_Function_Init();
|
||||
RTC_Init();
|
||||
startupTime = RTC_GetCounter();
|
||||
memset (&stats, 0, sizeof (stats));
|
||||
|
||||
|
||||
/*
|
||||
DiscoverRequestPayload discReq;
|
||||
discReq.prefixOnly = 0;
|
||||
discReq.since = 0;
|
||||
discReq.tag = RTC_GetCounter();
|
||||
discReq.typeFilter = 0xFF;
|
||||
sendDiscoverRequest (&discReq);
|
||||
|
||||
*/
|
||||
sendAdvert (0);
|
||||
bootedUp = 1;
|
||||
uint64_t ticker = 0;
|
||||
|
||||
while (1) {
|
||||
if (USART_GetFlagStatus (USART1, USART_FLAG_RXNE) == SET) {
|
||||
char x;
|
||||
x = USART_ReceiveData (USART1);
|
||||
if (x == 'M') {
|
||||
MESH_LOGI (TAG, "Sending message\n");
|
||||
char tempBuf[180];
|
||||
sniprintf (tempBuf, 180, "SySTick is %llu", ticker);
|
||||
makeSendGroupMessage (tempBuf, 1);
|
||||
}
|
||||
if (x == '0') {
|
||||
MESH_LOGI (TAG, "Sending zero hop advert\n");
|
||||
sendAdvert (0);
|
||||
}
|
||||
if (x == 'F') {
|
||||
MESH_LOGI (TAG, "Sending flood advert\n");
|
||||
sendAdvert (1);
|
||||
}
|
||||
if (x == 'N') {
|
||||
printNodeDB();
|
||||
}
|
||||
if (x == 'D') {
|
||||
PlainTextMessagePayload plainTextMessage;
|
||||
plainTextMessage.timestamp = RTC_GetCounter();
|
||||
plainTextMessage.textType = 0;
|
||||
plainTextMessage.attempt = 0;
|
||||
snprintf (plainTextMessage.message, sizeof (plainTextMessage.message), "Sending message at SySTick is %llu", ticker);
|
||||
iprintf ("Sending a direct message to the first node\n");
|
||||
sendEncryptedTextMessage (&(persistent.contacts[0]), &plainTextMessage);
|
||||
}
|
||||
if (x == 'C') {
|
||||
for (uint8_t i = 0; i < ChannelCount; i++) {
|
||||
Channel *channel = &(persistent.channels[i]);
|
||||
if (strlen (channel->name) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (channel->timestamp == 0) {
|
||||
continue;
|
||||
}
|
||||
iprintf ("Channel index %d, named %s, timestamp is %d, hash is %d\n", i, channel->name, channel->timestamp, channel->hash);
|
||||
hexdump ("Pubkey", channel->key, sizeof (channel->key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int8_t rssi, snr, rawsnr;
|
||||
FrameStruct frame;
|
||||
if (ReadFrame (&frame, &rssi, &snr, &rawsnr)) {
|
||||
hexdump ("Whole frame", frame.payload, frame.payloadLen);
|
||||
stats.lastSNR = rawsnr;
|
||||
// stats.lastSNR = snr; //TODO figure out which to use
|
||||
stats.lastRSSI = rssi;
|
||||
MESH_LOGI (TAG, "rssi=%d[dBm] snr=%d[dB] rawsnr=%d[quarter dB]", rssi, snr, rawsnr);
|
||||
// frame = decodeFrame (bufIn, rxLen);
|
||||
processFrame (&frame);
|
||||
if (persistent.doRepeat) {
|
||||
retransmitFrame (&frame);
|
||||
}
|
||||
memset (&frame, 0, sizeof (FrameStruct)); // prepare for the next round
|
||||
}
|
||||
|
||||
int lost = GetPacketLost();
|
||||
if (lost != 0) {
|
||||
MESH_LOGW (TAG, "%d packets lost", lost);
|
||||
}
|
||||
ticker++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "meshcore.h"
|
||||
#include "lib/monocypher/monocypher-ed25519.h"
|
||||
#include "meshcore/packets/advert.h"
|
||||
#include "meshcore/packets/anonymous.h"
|
||||
#include "meshcore/packets/control.h"
|
||||
#include "meshcore/packets/encrypted.h"
|
||||
#include "meshcore/packets/group.h"
|
||||
#include "meshcore/stats.h"
|
||||
#include "lib/base64.h"
|
||||
#include "lib/cifra/aes.h"
|
||||
#include "lib/cifra/sha2.h"
|
||||
#include "lib/cifra/hmac.h"
|
||||
#include "lib/config.h"
|
||||
#include "meshframing.h"
|
||||
#include "meshcore/packetstructs.h"
|
||||
|
||||
#define TAG "MeshCore"
|
||||
|
||||
// requires at least a 256 byte data
|
||||
|
||||
|
||||
void processFrame (FrameStruct *frame) {
|
||||
printframeHeader (frame);
|
||||
if (frame->header & PAYLOAD_VERSION_3) { // more than the version 0
|
||||
MESH_LOGW (TAG, "Frame too new, got version %d instead of 0", (frame->header & PAYLOAD_VERSION_3) >> 6);
|
||||
}
|
||||
|
||||
unsigned char frameType = frame->header & PAYLOAD_TYPE_MASK;
|
||||
|
||||
unsigned char index = 0;
|
||||
|
||||
stats.packetsReceivedCount++;
|
||||
|
||||
if ((frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_FLOOD ||
|
||||
(frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
stats.receivedFloodCount++;
|
||||
}
|
||||
|
||||
if ((frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_DIRECT ||
|
||||
(frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_DIRECT) {
|
||||
stats.receivedDirectCount++;
|
||||
}
|
||||
|
||||
|
||||
if (frameType == PAYLOAD_TYPE_ANON_REQ) {
|
||||
decodeAnonReq (frame);
|
||||
|
||||
} else if (frameType == PAYLOAD_TYPE_PATH || frameType == PAYLOAD_TYPE_REQ || frameType == PAYLOAD_TYPE_RESPONSE || frameType == PAYLOAD_TYPE_TXT_MSG) {
|
||||
iprintf (" Typexd: 0x%02X\n", frameType);
|
||||
decodeEncryptedPayload (frame);
|
||||
|
||||
} else if (frameType == PAYLOAD_TYPE_ACK) {
|
||||
uint32_t checkSum = frame->payload[index++];
|
||||
checkSum |= frame->payload[index++] << 8;
|
||||
checkSum |= frame->payload[index++] << 16;
|
||||
checkSum |= frame->payload[index++] << 24;
|
||||
// TODO add checking
|
||||
|
||||
} else if (frameType == PAYLOAD_TYPE_ADVERT) {
|
||||
decodeAdvertisement (frame);
|
||||
} else if (frameType == PAYLOAD_TYPE_GRP_TXT || frameType == PAYLOAD_TYPE_GRP_DATA) {
|
||||
decodeGroupMessage (frame);
|
||||
} else if (frameType == PAYLOAD_TYPE_TRACE) {
|
||||
|
||||
} else if (frameType == PAYLOAD_TYPE_MULTIPART) {
|
||||
|
||||
} else if (frameType == PAYLOAD_TYPE_CONTROL) {
|
||||
if (frame->path.pathLen == 0) {
|
||||
decodeControlFrame (frame);
|
||||
}
|
||||
frame->header = 0xFF;
|
||||
|
||||
} else if (frameType == PAYLOAD_TYPE_RAW_CUSTOM) {
|
||||
// not implemented
|
||||
} else {
|
||||
stats.packetsReceivedCount--;
|
||||
}
|
||||
MESH_LOGD (TAG, "Processed frame");
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef MESHCORE_HEADER
|
||||
#define MESHCORE_HEADER
|
||||
|
||||
#include "packetstructs.h"
|
||||
#include "string.h"
|
||||
#include "sx1262.h"
|
||||
#include "lib/cifra/aes.h"
|
||||
#include "lib/cifra/sha2.h"
|
||||
#include "lib/cifra/hmac.h"
|
||||
#include "util/log.h"
|
||||
#include <ctype.h>
|
||||
#include "stdio.h"
|
||||
|
||||
void processFrame (FrameStruct *frame);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,562 @@
|
||||
#include "meshframing.h"
|
||||
#include "ch32v20x_gpio.h"
|
||||
#include "lib/config.h"
|
||||
#include "meshcore/stats.h"
|
||||
#include "string.h"
|
||||
#include "stdio.h"
|
||||
#include "sx1262.h"
|
||||
#include "util/hexdump.h"
|
||||
#include "util/log.h"
|
||||
#include "lib/cifra/sha2.h"
|
||||
|
||||
#define TAG "Meshframing"
|
||||
|
||||
int ReadFrame (FrameStruct *frame, int8_t *rssiPacket, int8_t *snrPacket, int8_t *rawSnr) {
|
||||
uint16_t irqRegs = GetIrqStatus();
|
||||
// uint8_t status = GetStatus();
|
||||
|
||||
if (irqRegs & SX126X_IRQ_RX_DONE) {
|
||||
// ClearIrqStatus(SX126X_IRQ_RX_DONE);
|
||||
ClearIrqStatus (SX126X_IRQ_ALL);
|
||||
|
||||
uint8_t offset = 0;
|
||||
uint8_t payloadLength = 0;
|
||||
GetRxBufferStatus (&payloadLength, &offset);
|
||||
|
||||
if (payloadLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
GetPacketStatus (rssiPacket, snrPacket, rawSnr);
|
||||
|
||||
memset (frame, 0, sizeof (FrameStruct));
|
||||
WaitForIdle (BUSY_WAIT, "start ReadBuffer", 1);
|
||||
|
||||
uint8_t cmd[3] = {SX126X_CMD_READ_BUFFER, offset, SX126X_CMD_NOP};
|
||||
|
||||
GPIO_WriteBit (GPIOA, GPIO_Pin_4, 0); // NSS
|
||||
|
||||
uint16_t curPayloadIndex = 0;
|
||||
uint8_t pathIndex = 0;
|
||||
uint8_t transportIndex = 0;
|
||||
uint8_t state = 0; // 0=header, 1=transport, 2=pathLen, 3=path, 4=payload
|
||||
|
||||
for (uint16_t i = 0; i < payloadLength + sizeof (cmd); i++) {
|
||||
uint8_t out = (i < sizeof (cmd)) ? cmd[i] : 0xFF;
|
||||
|
||||
// Wait TX ready
|
||||
while (!SPI_I2S_GetFlagStatus (SPI1, SPI_I2S_FLAG_TXE));
|
||||
SPI_I2S_SendData (SPI1, out);
|
||||
|
||||
// Wait RX ready
|
||||
while (!SPI_I2S_GetFlagStatus (SPI1, SPI_I2S_FLAG_RXNE));
|
||||
uint8_t in = (uint8_t)SPI_I2S_ReceiveData (SPI1);
|
||||
|
||||
// Only process payload bytes
|
||||
if (i >= sizeof (cmd)) {
|
||||
switch (state) {
|
||||
case 0: // header
|
||||
frame->header = in;
|
||||
state = ((frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_DIRECT ||
|
||||
(frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD)
|
||||
? 1
|
||||
: 2;
|
||||
break;
|
||||
|
||||
case 1: // transportCodes[4]
|
||||
frame->transportCodes[transportIndex++] = in;
|
||||
if (transportIndex >= 4)
|
||||
state = 2;
|
||||
break;
|
||||
|
||||
case 2: // pathLen
|
||||
frame->path.pathLen = in;
|
||||
if (frame->path.pathLen > 64) {
|
||||
frame->path.pathLen = 64;
|
||||
}
|
||||
pathIndex = 0;
|
||||
state = (frame->path.pathLen > 0) ? 3 : 4;
|
||||
break;
|
||||
|
||||
case 3: // path
|
||||
frame->path.path[pathIndex++] = in;
|
||||
if (pathIndex >= frame->path.pathLen)
|
||||
state = 4;
|
||||
break;
|
||||
|
||||
case 4: // payload
|
||||
frame->payload[curPayloadIndex++] = in;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
frame->payloadLen = curPayloadIndex;
|
||||
|
||||
GPIO_WriteBit (GPIOA, GPIO_Pin_4, 1);
|
||||
|
||||
WaitForIdle (BUSY_WAIT, "end ReadBuffer", 0);
|
||||
return payloadLength;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
FrameStruct decodeFrame (unsigned char *data, unsigned char dataLen) {
|
||||
FrameStruct frame;
|
||||
memset (&frame, 0, sizeof (frame));
|
||||
unsigned char index = 0;
|
||||
frame.header = data[index++];
|
||||
if ((frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_DIRECT ||
|
||||
(frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
memcpy (frame.transportCodes, data + index, 4);
|
||||
index += 4;
|
||||
}
|
||||
frame.path.pathLen = data[index++];
|
||||
|
||||
memcpy (frame.path.path, data + index, frame.path.pathLen);
|
||||
index += frame.path.pathLen;
|
||||
frame.payloadLen = dataLen - index;
|
||||
memcpy (frame.payload, data + index, frame.payloadLen);
|
||||
|
||||
return frame;
|
||||
}
|
||||
*/
|
||||
void printframeHeader (const FrameStruct *frame) {
|
||||
switch (frame->header & ROUTE_TYPE_MASK) {
|
||||
case ROUTE_TYPE_TRANSPORT_FLOOD:
|
||||
iprintf ("transport flood");
|
||||
break;
|
||||
|
||||
case ROUTE_TYPE_FLOOD:
|
||||
iprintf ("flood");
|
||||
break;
|
||||
|
||||
case ROUTE_TYPE_DIRECT:
|
||||
iprintf ("direct");
|
||||
break;
|
||||
|
||||
case ROUTE_TYPE_TRANSPORT_DIRECT:
|
||||
iprintf ("transport direct");
|
||||
break;
|
||||
}
|
||||
|
||||
iprintf (", payload type is ");
|
||||
|
||||
switch (frame->header & PAYLOAD_TYPE_MASK) {
|
||||
case PAYLOAD_TYPE_REQ:
|
||||
iprintf ("request");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_RESPONSE:
|
||||
iprintf ("response");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_TXT_MSG:
|
||||
iprintf ("text message");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_ACK:
|
||||
iprintf ("acknowledgement");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_ADVERT:
|
||||
iprintf ("advert");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_GRP_TXT:
|
||||
iprintf ("group text");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_GRP_DATA:
|
||||
iprintf ("group data");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_ANON_REQ:
|
||||
iprintf ("anon request");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_PATH:
|
||||
iprintf ("path");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_TRACE:
|
||||
iprintf ("trace");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_MULTIPART:
|
||||
iprintf ("multipart");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_CONTROL:
|
||||
iprintf ("control");
|
||||
break;
|
||||
|
||||
case PAYLOAD_TYPE_RAW_CUSTOM:
|
||||
iprintf ("raw");
|
||||
break;
|
||||
}
|
||||
char version[2];
|
||||
version[0] = (frame->header >> 6) + '0';
|
||||
version[1] = 0;
|
||||
|
||||
iprintf (", payload version is %s ", version);
|
||||
|
||||
if ((frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_DIRECT ||
|
||||
(frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
iprintf ("Transport codes: %d %d\n", *((uint16_t *)frame->transportCodes),
|
||||
*((uint16_t *)&(frame->transportCodes[2])));
|
||||
}
|
||||
iprintf ("Path is %d nodes long", frame->path.pathLen);
|
||||
|
||||
for (uint8_t pathIndex = 0; pathIndex < frame->path.pathLen; pathIndex++) {
|
||||
iprintf ("node %d - %02X, ", pathIndex, frame->path.path[pathIndex]);
|
||||
}
|
||||
putchar ('\n');
|
||||
}
|
||||
|
||||
void LoRaTransmit (const FrameStruct *frame) {
|
||||
addToNotReTX(frame);
|
||||
uint16_t len = 2; // header + path_len
|
||||
|
||||
if (
|
||||
((frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_DIRECT) ||
|
||||
((frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD)
|
||||
) {
|
||||
len += 4;
|
||||
}
|
||||
|
||||
len += frame->path.pathLen;
|
||||
len += frame->payloadLen;
|
||||
|
||||
if (len > 255) {
|
||||
MESH_LOGE(TAG, "%d is too big for sx1262", len);
|
||||
}
|
||||
|
||||
uint16_t irqStatus;
|
||||
char rv = 0;
|
||||
|
||||
size_t outCounter = 0;
|
||||
|
||||
if (txActive == 0) {
|
||||
txActive = 1;
|
||||
|
||||
if (PacketParams[2] == 0x00) { // explicit header, variable length
|
||||
PacketParams[3] = len;
|
||||
}
|
||||
|
||||
WriteCommand (SX126X_CMD_SET_PACKET_PARAMS, PacketParams, 6);
|
||||
ClearIrqStatus (SX126X_IRQ_ALL);
|
||||
|
||||
WaitForIdle (BUSY_WAIT, "start WriteBuffer", 1);
|
||||
|
||||
uint8_t cmdBuf[2] = {SX126X_CMD_WRITE_BUFFER, 0x00};
|
||||
uint8_t cmdLen = sizeof (cmdBuf);
|
||||
|
||||
GPIO_WriteBit (GPIOA, GPIO_Pin_4, 0);
|
||||
|
||||
uint16_t payloadIndex = 0;
|
||||
uint8_t state = 0;
|
||||
uint8_t pathIndex = 0;
|
||||
uint8_t transportIndex = 0;
|
||||
|
||||
for (uint16_t i = 0; i < len + cmdLen; i++) {
|
||||
uint8_t out = 0xFF;
|
||||
|
||||
if (i < cmdLen) {
|
||||
out = cmdBuf[i];
|
||||
} else {
|
||||
switch (state) {
|
||||
case 0: // header
|
||||
out = frame->header;
|
||||
outCounter = 0;
|
||||
state = ((frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_DIRECT ||
|
||||
(frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD)
|
||||
? 1
|
||||
: 2;
|
||||
break;
|
||||
|
||||
case 1: // transport codes
|
||||
out = frame->transportCodes[transportIndex++];
|
||||
if (transportIndex >= 4)
|
||||
state = 2;
|
||||
break;
|
||||
|
||||
case 2: // path length
|
||||
out = frame->path.pathLen;
|
||||
pathIndex = 0;
|
||||
state = (frame->path.pathLen > 0) ? 3 : 4;
|
||||
break;
|
||||
|
||||
case 3: // path
|
||||
out = frame->path.path[pathIndex++];
|
||||
if (pathIndex >= frame->path.pathLen)
|
||||
state = 4;
|
||||
break;
|
||||
|
||||
case 4: // payload
|
||||
out = frame->payload[payloadIndex++];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (SPI_I2S_GetFlagStatus (SPI1, SPI_I2S_FLAG_TXE) == RESET);
|
||||
outCounter++;
|
||||
SPI_I2S_SendData (SPI1, out);
|
||||
|
||||
while (SPI_I2S_GetFlagStatus (SPI1, SPI_I2S_FLAG_RXNE) == RESET);
|
||||
(void)SPI_I2S_ReceiveData (SPI1); // discard
|
||||
}
|
||||
|
||||
GPIO_WriteBit (GPIOA, GPIO_Pin_4, 1);
|
||||
|
||||
WaitForIdle (BUSY_WAIT, "end WriteBuffer", 0);
|
||||
|
||||
MESH_LOGD(TAG,
|
||||
"TX payloadLen=%u pathLen=%u totalLen=%u header=0x%02X",
|
||||
frame->payloadLen,
|
||||
frame->path.pathLen,
|
||||
len,
|
||||
frame->header);
|
||||
|
||||
|
||||
MESH_LOGD(TAG, "Starting tx mode, sent %d, should %d", outCounter, len);
|
||||
|
||||
SetTx (3000);
|
||||
|
||||
MESH_LOGD(TAG, "SetTx running");
|
||||
|
||||
irqStatus = GetIrqStatus();
|
||||
while (!(irqStatus & (SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT))) {
|
||||
Delay_Ms(10);
|
||||
irqStatus = GetIrqStatus();
|
||||
if (debugPrint) {
|
||||
MESH_LOGD(TAG, "irq: 0x%04X", irqStatus);
|
||||
}
|
||||
}
|
||||
|
||||
MESH_LOGD(TAG, "Finished tx");
|
||||
txActive = 0;
|
||||
SetRx (0xFFFFFF);
|
||||
|
||||
if (irqStatus & SX126X_IRQ_TX_DONE) {
|
||||
rv = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (rv == 0) {
|
||||
txLost++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void sendFrame (const FrameStruct *frame) {
|
||||
uint8_t txBuf[256];
|
||||
size_t offset = 0;
|
||||
|
||||
txBuf[offset++] = frame->header;
|
||||
|
||||
if ((frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_DIRECT ||
|
||||
(frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
memcpy (txBuf + offset, frame->transportCodes, 4);
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
if ((frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_FLOOD ||
|
||||
(frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
stats.sentFloodCount++;
|
||||
}
|
||||
|
||||
if ((frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_DIRECT ||
|
||||
(frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_DIRECT) {
|
||||
stats.sentDirectCount++;
|
||||
}
|
||||
|
||||
stats.packetsSentCount++;
|
||||
|
||||
uint8_t pathLen = frame->path.pathLen;
|
||||
|
||||
|
||||
if (pathLen > 64) {
|
||||
pathLen = 64;
|
||||
}
|
||||
|
||||
txBuf[offset++] = pathLen;
|
||||
|
||||
memcpy (txBuf + offset, frame->path.path, pathLen);
|
||||
hexdump ("TxDump Path", frame->path.path, frame->path.pathLen);
|
||||
offset += pathLen;
|
||||
|
||||
uint16_t maxPayloadLen = 256 - offset;
|
||||
|
||||
uint16_t payloadLen = frame->payloadLen > maxPayloadLen ? maxPayloadLen : frame->payloadLen;
|
||||
|
||||
memcpy (txBuf + offset, frame->payload, payloadLen);
|
||||
offset += payloadLen;
|
||||
|
||||
|
||||
hexdump ("TxDump", txBuf, offset);
|
||||
hexdump ("TxDumpPayload", frame->payload, frame->payloadLen);
|
||||
TickType_t start, end;
|
||||
start = xTaskGetTickCount();
|
||||
LoRaSend (txBuf, offset, SX126x_TXMODE_SYNC);
|
||||
end = xTaskGetTickCount();
|
||||
tickAirtime += end - start;
|
||||
}
|
||||
*/
|
||||
|
||||
KnownPacketHistoryType dontReTXHistory[NON_RETX_HISTORY];
|
||||
uint8_t dontReTXHistoryIndex = 0;
|
||||
|
||||
void getFrameHash(const FrameStruct *frame, uint8_t *hash) {
|
||||
cf_sha256_context ctx;
|
||||
|
||||
// 1. Initialize
|
||||
cf_sha256_init (&ctx);
|
||||
|
||||
// 2. Feed in your data
|
||||
cf_sha256_update (&ctx, frame->payload, frame->payloadLen);
|
||||
|
||||
// 3. Compute digest
|
||||
cf_sha256_digest (&ctx, hash);
|
||||
}
|
||||
|
||||
void addToNotReTX(const FrameStruct *frame) {
|
||||
KnownPacketHistoryType * knownHist = &(dontReTXHistory[dontReTXHistoryIndex]);
|
||||
|
||||
getFrameHash(frame, knownHist->hash);
|
||||
knownHist->populated = 1;
|
||||
|
||||
dontReTXHistoryIndex++;
|
||||
if (dontReTXHistoryIndex >= NON_RETX_HISTORY) {
|
||||
dontReTXHistoryIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void retransmitFrame (FrameStruct *frame) {
|
||||
|
||||
uint8_t hashTmp[CF_SHA256_HASHSZ];
|
||||
getFrameHash(frame, hashTmp);
|
||||
|
||||
uint8_t found = 0;
|
||||
for (uint8_t i = 0; i < NON_RETX_HISTORY; i++) {
|
||||
KnownPacketHistoryType * knownHist = &(dontReTXHistory[dontReTXHistoryIndex]);
|
||||
if (knownHist->populated == 0) {
|
||||
continue;
|
||||
}
|
||||
if (memcmp(knownHist->hash, hashTmp, CF_SHA256_HASHSZ) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* -------- FLOOD -------- */
|
||||
if (frame->header & ROUTE_TYPE_FLOOD ||
|
||||
frame->header & ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
|
||||
if (frame->header != DONT_RETRANSMIT_HEADER &&
|
||||
frame->path.pathLen + 1 < MAX_FLOOD_TTL) {
|
||||
|
||||
// append self to END of path
|
||||
frame->path.path[frame->path.pathLen++] = persistent.pubkey[0];
|
||||
LoRaTransmit (frame);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------- DIRECT -------- */
|
||||
if (frame->header & ROUTE_TYPE_DIRECT ||
|
||||
frame->header & ROUTE_TYPE_TRANSPORT_DIRECT) {
|
||||
|
||||
// are we the next hop?
|
||||
if (frame->path.pathLen > 0 &&
|
||||
frame->path.path[0] == persistent.pubkey[0]) {
|
||||
|
||||
// remove self from START of path
|
||||
frame->path.pathLen--;
|
||||
memmove (frame->path.path,
|
||||
frame->path.path + 1,
|
||||
frame->path.pathLen);
|
||||
|
||||
// forward only if there's another hop
|
||||
if (frame->path.pathLen > 0) {
|
||||
LoRaTransmit (frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify MAC + Decrypt
|
||||
|
||||
int encrypt_then_mac (const uint8_t *aes_key, const uint8_t keySize, const uint8_t *plaintext, size_t plen, uint8_t *output, size_t *olen) {
|
||||
if (plen == 0)
|
||||
return -1;
|
||||
|
||||
size_t padded_len = ((plen + 15) / 16) * 16;
|
||||
|
||||
// prepare padded buffer
|
||||
uint8_t padded[padded_len];
|
||||
memset (padded, 0, padded_len); // zero padding
|
||||
memcpy (padded, plaintext, plen); // copy plaintext
|
||||
|
||||
// ciphertext will go right after HMAC
|
||||
uint8_t *ciphertext = output + HMAC_SIZE;
|
||||
|
||||
// encrypt plaintext
|
||||
aes_encrypt_ecb (aes_key, 16, padded, padded_len, ciphertext);
|
||||
|
||||
// compute HMAC over ciphertext
|
||||
uint8_t mac[32]; // full SHA-256
|
||||
hmac_sha256 (aes_key, keySize, ciphertext, padded_len, mac);
|
||||
|
||||
// copy only HMAC_SIZE bytes of MAC
|
||||
memcpy (output, mac, HMAC_SIZE);
|
||||
|
||||
// return total length = HMAC + ciphertext
|
||||
*olen = HMAC_SIZE + padded_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_then_decrypt (const uint8_t *aes_key, const uint8_t keySize, const uint8_t *input, size_t ilen, uint8_t *plaintext) {
|
||||
if (ilen <= HMAC_SIZE)
|
||||
return -1;
|
||||
|
||||
const uint8_t *mac = input;
|
||||
const uint8_t *ciphertext = input + HMAC_SIZE;
|
||||
size_t clen = ilen - HMAC_SIZE;
|
||||
|
||||
if (clen % 16 != 0)
|
||||
return -2; // must be multiple of block size
|
||||
|
||||
uint8_t calc_mac[32]; // full SHA-256
|
||||
hmac_sha256 (aes_key, keySize, ciphertext, clen, calc_mac);
|
||||
|
||||
if (memcmp (mac, calc_mac, HMAC_SIZE) != 0)
|
||||
return -2;
|
||||
|
||||
return aes_decrypt_ecb (aes_key, 16, ciphertext, clen, plaintext);
|
||||
}
|
||||
|
||||
uint16_t getTransportCode (const FrameStruct *frame) {
|
||||
uint16_t code;
|
||||
/*
|
||||
// compute HMAC over ciphertext
|
||||
uint8_t mac[32]; // full SHA-256
|
||||
cf_hmac_ctx ctx;
|
||||
cf_hmac_init (&ctx, &cf_sha256, key, sizeof(key));
|
||||
cf_hmac_update (&ctx, &(frame->header), sizeof (frame->header));
|
||||
cf_hmac_update (&ctx, frame->payload, frame->payloadLen);
|
||||
cf_hmac_finish (&ctx, mac);
|
||||
|
||||
// copy only HMAC_SIZE bytes of MAC
|
||||
memcpy (&code, mac, HMAC_SIZE);
|
||||
*/
|
||||
if (code == 0) { // reserve codes 0000 and FFFF
|
||||
code++;
|
||||
} else if (code == 0xFFFF) {
|
||||
code--;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef MESHCORE_FRAME_HEADER
|
||||
#define MESHCORE_FRAME_HEADER
|
||||
|
||||
#include "stddef.h"
|
||||
#include "stdint.h"
|
||||
#include "lib/cifra/aes.h"
|
||||
#include "lib/cifra/hmac.h"
|
||||
#include "packetstructs.h"
|
||||
|
||||
|
||||
#define KEY_SIZE 16 // 128-bit AES
|
||||
#define HMAC_SIZE 2 // meshcore size
|
||||
#define MAX_FLOOD_TTL 64
|
||||
//#define NON_RETX_HISTORY 64
|
||||
#define NON_RETX_HISTORY 8
|
||||
|
||||
int ReadFrame (FrameStruct *frame, int8_t *rssiPacket, int8_t *snrPacket, int8_t *rawSnr);
|
||||
|
||||
void LoRaTransmit (const FrameStruct *frame);
|
||||
|
||||
/*
|
||||
FrameStruct decodeFrame (unsigned char *data, unsigned char dataLen);
|
||||
|
||||
void sendFrame (const FrameStruct * frame);
|
||||
*/
|
||||
|
||||
void printFrameHeader (const FrameStruct *frame);
|
||||
|
||||
// CALL LAST, PATH GETS MODIFIED
|
||||
void retransmitFrame (FrameStruct *frame);
|
||||
|
||||
// Verify MAC + Decrypt
|
||||
|
||||
int encrypt_then_mac (const uint8_t *aes_key, const uint8_t keySize, const uint8_t *plaintext, size_t plen, uint8_t *output, size_t *olen);
|
||||
|
||||
int mac_then_decrypt (const uint8_t *aes_key, const uint8_t keySize, const uint8_t *input, size_t ilen, uint8_t *plaintext);
|
||||
|
||||
void addToNotReTX(const FrameStruct *frame);
|
||||
|
||||
typedef struct KnownPacketHistoryType {
|
||||
uint8_t hash[32];
|
||||
uint8_t populated;
|
||||
} KnownPacketHistoryType;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "ack.h"
|
||||
#include "lib/cifra/sha2.h"
|
||||
#include "lib/config.h"
|
||||
#include "meshcore/meshframing.h"
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "Ack"
|
||||
|
||||
void sendDiscreteAck (uint8_t *data, const uint8_t len, uint8_t *senderPubKey) {
|
||||
FrameStruct frame;
|
||||
frame.path.pathLen = 0;
|
||||
memset (&frame, 0, sizeof (frame));
|
||||
|
||||
// 1. Header
|
||||
frame.header =
|
||||
ROUTE_TYPE_FLOOD | // currently flood
|
||||
PAYLOAD_TYPE_ACK |
|
||||
PAYLOAD_VERSION_0;
|
||||
|
||||
// Buffer for the digest
|
||||
uint8_t hash[CF_SHA256_HASHSZ];
|
||||
|
||||
// Context
|
||||
cf_sha256_context ctx;
|
||||
|
||||
// 1. Initialize
|
||||
cf_sha256_init (&ctx);
|
||||
|
||||
// 2. Feed in your data
|
||||
cf_sha256_update (&ctx, data, len);
|
||||
|
||||
cf_sha256_update (&ctx, senderPubKey, sizeof (persistent.pubkey));
|
||||
|
||||
// 3. Compute digest
|
||||
cf_sha256_digest (&ctx, hash);
|
||||
|
||||
memcpy (frame.payload, hash, 4);
|
||||
// 5. Finalize
|
||||
frame.payloadLen = 4;
|
||||
|
||||
|
||||
LoRaTransmit (&frame);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef ACK_HEADER
|
||||
#define ACK_HEADER
|
||||
|
||||
#include "meshcore/packetstructs.h"
|
||||
|
||||
void sendDiscreteAck (uint8_t *data, const uint8_t len, uint8_t *senderPubKey);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,302 @@
|
||||
#include "ch32v20x_rtc.h"
|
||||
#include "lib/config.h"
|
||||
#include "lib/monocypher/monocypher-ed25519.h"
|
||||
#include "meshcore/meshframing.h"
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include <string.h>
|
||||
#include "advert.h"
|
||||
#include "util/hexdump.h"
|
||||
#include "lib/base64.h"
|
||||
#include "util/log.h"
|
||||
|
||||
#define TAG "Advert"
|
||||
|
||||
static void build_ad_message (const FrameStruct *frame,
|
||||
uint8_t *out,
|
||||
size_t *out_len) {
|
||||
size_t idx = 0;
|
||||
|
||||
// pubkey + timestamp (your 36 bytes)
|
||||
memcpy (out + idx, frame->payload, 36);
|
||||
idx += 36;
|
||||
|
||||
// rest of payload after signature region
|
||||
memcpy (out + idx,
|
||||
&frame->payload[100],
|
||||
frame->payloadLen - 100);
|
||||
|
||||
idx += (frame->payloadLen - 100);
|
||||
|
||||
*out_len = idx;
|
||||
}
|
||||
|
||||
void ed25519_sign_ad (FrameStruct *frame) {
|
||||
uint8_t *signature = &frame->payload[36];
|
||||
|
||||
uint8_t message[256];
|
||||
size_t m = 0;
|
||||
|
||||
// pubkey + timestamp (0..35)
|
||||
memcpy (message + m, frame->payload, 36);
|
||||
m += 36;
|
||||
|
||||
// skip signature [36..99]
|
||||
|
||||
// payload after signature
|
||||
memcpy (message + m,
|
||||
frame->payload + 100,
|
||||
frame->payloadLen - 100);
|
||||
m += frame->payloadLen - 100;
|
||||
|
||||
crypto_ed25519_sign (signature,
|
||||
persistent.privkey,
|
||||
message,
|
||||
m);
|
||||
}
|
||||
|
||||
int ed25519_verify_ad (const FrameStruct *frame) {
|
||||
const uint8_t *signature = &frame->payload[36];
|
||||
|
||||
uint8_t message[256];
|
||||
size_t m = 0;
|
||||
|
||||
if (signature[63] & 224)
|
||||
return 0;
|
||||
|
||||
// pubkey + timestamp
|
||||
memcpy (message + m, frame->payload, 36);
|
||||
m += 36;
|
||||
|
||||
// skip signature [36..99]
|
||||
|
||||
// payload after signature
|
||||
memcpy (message + m,
|
||||
frame->payload + 100,
|
||||
frame->payloadLen - 100);
|
||||
m += frame->payloadLen - 100;
|
||||
|
||||
return crypto_ed25519_check (signature,
|
||||
frame->payload, // pubkey at start
|
||||
message,
|
||||
m) == 0;
|
||||
}
|
||||
|
||||
void sendAdvert (uint8_t shouldFlood) {
|
||||
FrameStruct frame;
|
||||
size_t offset = 0;
|
||||
|
||||
frame.header = (shouldFlood ? ROUTE_TYPE_FLOOD : ROUTE_TYPE_DIRECT) | PAYLOAD_TYPE_ADVERT | PAYLOAD_VERSION_0;
|
||||
|
||||
/* ---- public key ---- */
|
||||
memcpy (frame.payload + offset, persistent.pubkey, 32);
|
||||
offset += 32;
|
||||
|
||||
/* ---- timestamp ---- */
|
||||
uint32_t timestamp = RTC_GetCounter();
|
||||
memcpy (frame.payload + offset, ×tamp, sizeof (timestamp));
|
||||
offset += sizeof (timestamp);
|
||||
|
||||
/* ---- reserve signature ---- */
|
||||
uint8_t *signature_pos = frame.payload + offset;
|
||||
offset += 64;
|
||||
|
||||
/* ---- build app data directly into payload ---- */
|
||||
size_t app_start = offset;
|
||||
|
||||
uint8_t dataFlags = ADVERTISEMENT_FLAG_HAS_NAME;
|
||||
if (persistent.nodeType == NODE_TYPE_CHAT_NODE)
|
||||
dataFlags |= ADVERTISEMENT_FLAG_IS_CHAT_NODE;
|
||||
else if (persistent.nodeType == NODE_TYPE_REPEATER)
|
||||
dataFlags |= ADVERTISEMENT_FLAG_IS_REAPEATER;
|
||||
else if (persistent.nodeType == NODE_TYPE_ROOM_SERVER)
|
||||
dataFlags |= ADVERTISEMENT_FLAG_IS_ROOM_SERVER;
|
||||
else if (persistent.nodeType == NODE_TYPE_SENSOR)
|
||||
dataFlags |= ADVERTISEMENT_FLAG_IS_SENSOR;
|
||||
|
||||
frame.payload[offset++] = dataFlags;
|
||||
|
||||
if (dataFlags & ADVERTISEMENT_FLAG_HAS_LOCATION) {
|
||||
memcpy (frame.payload + offset, &persistent.latitude, sizeof (persistent.latitude));
|
||||
offset += sizeof (persistent.latitude);
|
||||
|
||||
memcpy (frame.payload + offset, &persistent.longitude, sizeof (persistent.longitude));
|
||||
offset += sizeof (persistent.longitude);
|
||||
}
|
||||
/*
|
||||
|
||||
if (dataFlags & ADVERTISEMENT_FLAG_RFU1) {
|
||||
memcpy(frame.payload + offset, &persistent.rfu1, sizeof(persistent.rfu1));
|
||||
offset += sizeof(persistent.rfu1);
|
||||
}
|
||||
|
||||
if (dataFlags & ADVERTISEMENT_FLAG_RFU2) {
|
||||
memcpy(frame.payload + offset, &persistent.rfu2, sizeof(persistent.rfu2));
|
||||
offset += sizeof(persistent.rfu2);
|
||||
}
|
||||
*/
|
||||
|
||||
if (dataFlags & ADVERTISEMENT_FLAG_HAS_NAME) {
|
||||
size_t nameLen = strlen (persistent.nodeName);
|
||||
memcpy (frame.payload + offset, persistent.nodeName, nameLen);
|
||||
offset += nameLen;
|
||||
}
|
||||
|
||||
size_t app_len = offset - app_start;
|
||||
|
||||
frame.payloadLen = offset;
|
||||
/* ---- sign directly over payload ---- */
|
||||
ed25519_sign_ad (&frame);
|
||||
|
||||
/* ---- debug ---- */
|
||||
hexdump ("Public key", frame.payload, 32);
|
||||
hexdump ("Signature", signature_pos, 64);
|
||||
hexdump ("Appdata", frame.payload + app_start, app_len);
|
||||
iprintf ("Timestamp is %lu\n", timestamp);
|
||||
iprintf ("NodeName %s\n", persistent.nodeName);
|
||||
|
||||
/* ---- send ---- */
|
||||
frame.payloadLen = offset;
|
||||
frame.path.pathLen = 0;
|
||||
LoRaTransmit (&frame);
|
||||
}
|
||||
|
||||
void decodeAdvertisement (const FrameStruct *frame) {
|
||||
AdvertisementPayload advert;
|
||||
memset (&advert, 0, sizeof (advert));
|
||||
|
||||
advert.valid = 0;
|
||||
if (frame->payloadLen < 101) {
|
||||
MESH_LOGW (TAG, "Advertisement frame too short (%d < 101)", frame->payloadLen);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ed25519_verify_ad (frame)) {
|
||||
MESH_LOGW (TAG, "Incorrect signature");
|
||||
return;
|
||||
} else {
|
||||
MESH_LOGI (TAG, "Advertisement signature ok.");
|
||||
}
|
||||
advert.valid = 1;
|
||||
|
||||
unsigned char index = 0;
|
||||
|
||||
memcpy (advert.pubKey, frame->payload + index, 32);
|
||||
index += 32;
|
||||
|
||||
memcpy (&advert.timestamp, frame->payload + index, 4);
|
||||
index += 4;
|
||||
|
||||
memcpy (advert.signature, frame->payload + index, 64);
|
||||
index += 64;
|
||||
|
||||
advert.dataFlags = frame->payload[index++];
|
||||
|
||||
uint8_t expectedLen = 101;
|
||||
|
||||
if (advert.dataFlags & ADVERTISEMENT_FLAG_HAS_LOCATION) {
|
||||
expectedLen += 8;
|
||||
}
|
||||
|
||||
if (advert.dataFlags & ADVERTISEMENT_FLAG_RFU1) {
|
||||
expectedLen += 2;
|
||||
}
|
||||
if (advert.dataFlags & ADVERTISEMENT_FLAG_RFU2) {
|
||||
expectedLen += 2;
|
||||
}
|
||||
|
||||
if (frame->payloadLen < expectedLen) {
|
||||
MESH_LOGW (TAG, "Advertisement frame with data too short (%d < %d)", frame->payloadLen, expectedLen);
|
||||
return;
|
||||
}
|
||||
|
||||
if (advert.dataFlags & ADVERTISEMENT_FLAG_HAS_LOCATION) {
|
||||
memcpy (&advert.latitude, frame->payload + index, 4);
|
||||
index += 4;
|
||||
memcpy (&advert.longitude, frame->payload + index, 4);
|
||||
index += 4;
|
||||
}
|
||||
|
||||
if (advert.dataFlags & ADVERTISEMENT_FLAG_RFU1) {
|
||||
memcpy (&advert.rfu1, frame->payload + index, 2);
|
||||
index += 2;
|
||||
}
|
||||
if (advert.dataFlags & ADVERTISEMENT_FLAG_RFU2) {
|
||||
memcpy (&advert.rfu2, frame->payload + index, 2);
|
||||
index += 2;
|
||||
}
|
||||
unsigned char nameLen = frame->payloadLen - index;
|
||||
|
||||
if (nameLen > 31) {
|
||||
nameLen = 31; // leave space for null
|
||||
}
|
||||
memcpy (advert.nodeName, frame->payload + index, nameLen);
|
||||
advert.nodeName[nameLen] = 0;
|
||||
|
||||
|
||||
printAdvertisement (&advert);
|
||||
saveAdvert (&advert);
|
||||
}
|
||||
|
||||
void saveAdvert (const AdvertisementPayload *advert) {
|
||||
NodeEntry *node = getNode (advert->pubKey[0]);
|
||||
|
||||
if (node == NULL) {
|
||||
node = getNextNode();
|
||||
memset (node, 0, sizeof (NodeEntry));
|
||||
}
|
||||
|
||||
memcpy (node->name, advert->nodeName, sizeof (node->name));
|
||||
memcpy (node->pubKey, advert->pubKey, sizeof (node->pubKey));
|
||||
|
||||
// -------------------------------
|
||||
// Ed25519 ¡ú X25519 conversion
|
||||
// -------------------------------
|
||||
uint8_t peer_x[32];
|
||||
crypto_eddsa_to_x25519 (peer_x, advert->pubKey);
|
||||
|
||||
// -------------------------------
|
||||
// IMPORTANT: derive correct scalar
|
||||
// (THIS fixes your HMAC mismatch)
|
||||
// -------------------------------
|
||||
uint8_t scalar[64];
|
||||
uint8_t scalarOut[32];
|
||||
|
||||
crypto_sha512 (scalar, persistent.privkey, 32);
|
||||
crypto_eddsa_trim_scalar (scalarOut, scalar);
|
||||
|
||||
// -------------------------------
|
||||
// X25519 Diffie-Hellman
|
||||
// -------------------------------
|
||||
crypto_x25519 (node->secret,
|
||||
scalarOut,
|
||||
peer_x);
|
||||
|
||||
|
||||
node->gps_latitude = advert->latitude;
|
||||
node->gps_longitude = advert->longitude;
|
||||
|
||||
// add path
|
||||
|
||||
node->type = advert->dataFlags & 0x0F;
|
||||
node->last_seen_lt = RTC_GetCounter();
|
||||
node->last_seen_rt = advert->timestamp;
|
||||
}
|
||||
|
||||
void printAdvertisement (const AdvertisementPayload *advert) {
|
||||
iprintf (
|
||||
"%s on %ld with type %s on %s location %ld %ld\n",
|
||||
advert->dataFlags & ADVERTISEMENT_FLAG_HAS_NAME
|
||||
? advert->nodeName
|
||||
: "nameless node",
|
||||
advert->timestamp,
|
||||
(advert->dataFlags & 0x07) == 0x04 ? "sensor"
|
||||
: (advert->dataFlags & 0x07) == 0x03 ? "room server"
|
||||
: (advert->dataFlags & 0x07) == 0x02 ? "repeater"
|
||||
: "chat node",
|
||||
advert->dataFlags & 0x80 ? "known" : "unknown",
|
||||
advert->latitude,
|
||||
advert->longitude);
|
||||
|
||||
hexdump ("Public key", advert->pubKey, 32);
|
||||
hexdump ("Signature", advert->signature, 64);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef ADVERT_HEADER
|
||||
#define ADVERT_HEADER
|
||||
|
||||
#include "meshcore/packetstructs.h"
|
||||
|
||||
void sendAdvert (uint8_t shouldFlood);
|
||||
|
||||
void decodeAdvertisement (const FrameStruct *frame);
|
||||
|
||||
void printAdvertisement (const AdvertisementPayload *advert);
|
||||
|
||||
void saveAdvert (const AdvertisementPayload *advert);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,234 @@
|
||||
#include "ch32v20x_rtc.h"
|
||||
#include "lib/config.h"
|
||||
#include "lib/monocypher/monocypher-ed25519.h"
|
||||
#include "meshcore/meshframing.h"
|
||||
#include "meshcore/packets/encrypted.h"
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "anonymous.h"
|
||||
#include "util/hexdump.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define TAG "Anonymous"
|
||||
|
||||
void sendAnonymousRequest (const NodeEntry *targetNode, const uint8_t *password, uint32_t sync) {
|
||||
uint8_t passwordLen = strlen ((const char *)password);
|
||||
FrameStruct frame;
|
||||
frame.path.pathLen = 0;
|
||||
uint8_t offset = 0;
|
||||
|
||||
// 1. Frame header
|
||||
frame.header = ((targetNode->path.pathLen > 0) ? ROUTE_TYPE_DIRECT : ROUTE_TYPE_FLOOD) | PAYLOAD_TYPE_ANON_REQ | PAYLOAD_VERSION_0;
|
||||
|
||||
// 2. Payload header (unencrypted)
|
||||
frame.payload[offset++] = targetNode->pubKey[0];
|
||||
|
||||
memcpy (frame.payload + offset, persistent.pubkey, 32);
|
||||
offset += 32;
|
||||
|
||||
// 3. Build plaintext payload
|
||||
uint8_t plaintext[32];
|
||||
uint8_t p = 0;
|
||||
|
||||
uint32_t last_seen_rt = RTC_GetCounter();
|
||||
plaintext[p++] = (last_seen_rt >> 0) & 0xFF;
|
||||
plaintext[p++] = (last_seen_rt >> 8) & 0xFF;
|
||||
plaintext[p++] = (last_seen_rt >> 16) & 0xFF;
|
||||
plaintext[p++] = (last_seen_rt >> 24) & 0xFF;
|
||||
|
||||
if (targetNode->type == NODE_TYPE_ROOM_SERVER) {
|
||||
plaintext[p++] = (sync >> 0) & 0xFF;
|
||||
plaintext[p++] = (sync >> 8) & 0xFF;
|
||||
plaintext[p++] = (sync >> 16) & 0xFF;
|
||||
plaintext[p++] = (sync >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
if (passwordLen > 16) {
|
||||
passwordLen = 16;
|
||||
}
|
||||
memcpy (plaintext + p, password, passwordLen);
|
||||
p += passwordLen;
|
||||
|
||||
size_t outputLen;
|
||||
// 4. Encrypt + MAC
|
||||
encrypt_then_mac (
|
||||
targetNode->secret,
|
||||
32,
|
||||
plaintext,
|
||||
p,
|
||||
frame.payload + offset,
|
||||
&outputLen);
|
||||
|
||||
offset += outputLen;
|
||||
|
||||
// 5. Finalize and send
|
||||
frame.payloadLen = offset;
|
||||
memcpy (&(frame.path), &(targetNode->path), sizeof (frame.path));
|
||||
|
||||
hexdump ("Anon payload", frame.payload, frame.payloadLen);
|
||||
|
||||
LoRaTransmit (&frame);
|
||||
}
|
||||
|
||||
void printAnonRequest (const AnonymousRequestPayload *req, int isRoomServer) {
|
||||
if (!req)
|
||||
return;
|
||||
|
||||
iprintf ("AnonymousRequestPayload at %p\n", (void *)req);
|
||||
iprintf (" destination hash: 0x%02X\n", req->destinationHash);
|
||||
|
||||
iprintf (" sender pubKey: ");
|
||||
for (int i = 0; i < sizeof (req->pubKey); i++) {
|
||||
iprintf ("%02X", req->pubKey[i]);
|
||||
}
|
||||
iprintf ("\n");
|
||||
|
||||
iprintf (" cipher MAC: 0x%04X\n", req->cipherMAC);
|
||||
|
||||
iprintf (" decrypted payload (%u bytes):\n", req->payloadLen);
|
||||
uint8_t index = 0;
|
||||
|
||||
// timestamp (first 4 bytes)
|
||||
if (req->payloadLen >= 4) {
|
||||
uint32_t timestamp = req->payload[index++];
|
||||
timestamp |= req->payload[index++] << 8;
|
||||
timestamp |= req->payload[index++] << 16;
|
||||
timestamp |= req->payload[index++] << 24;
|
||||
iprintf (" timestamp: %u\n", timestamp);
|
||||
}
|
||||
|
||||
// room server sync timestamp
|
||||
if (isRoomServer && req->payloadLen >= index + 4) {
|
||||
uint32_t syncTimestamp = req->payload[index++];
|
||||
syncTimestamp |= req->payload[index++] << 8;
|
||||
syncTimestamp |= req->payload[index++] << 16;
|
||||
syncTimestamp |= req->payload[index++] << 24;
|
||||
iprintf (" sync timestamp: %u\n", syncTimestamp);
|
||||
}
|
||||
|
||||
|
||||
// remaining bytes = password
|
||||
if (index < req->payloadLen) {
|
||||
uint8_t passwordLen = req->payloadLen - index;
|
||||
if (passwordLen > 16)
|
||||
passwordLen = 16;
|
||||
passwordLen = strnlen (&(req->payload[index]), passwordLen);
|
||||
iprintf (" password: ");
|
||||
for (uint8_t i = 0; i < passwordLen; i++) {
|
||||
iprintf ("%c", req->payload[i + index]);
|
||||
}
|
||||
iprintf ("\n");
|
||||
}
|
||||
}
|
||||
|
||||
size_t strnlen (const char *s, size_t maxLen) {
|
||||
size_t len = 0;
|
||||
while (len < maxLen && s[len] != '\0') {
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void decodeAnonReq (const FrameStruct *frame) {
|
||||
uint8_t index = 0;
|
||||
AnonymousRequestPayload anonReq;
|
||||
|
||||
anonReq.destinationHash = frame->payload[index++];
|
||||
memcpy (anonReq.pubKey, &(frame->payload[index]), sizeof (anonReq.pubKey));
|
||||
index += sizeof (anonReq.pubKey);
|
||||
anonReq.cipherMAC = frame->payload[index];
|
||||
anonReq.cipherMAC |= frame->payload[index + 1] << 8;
|
||||
|
||||
NodeEntry *foundNode = getNode (anonReq.pubKey[0]);
|
||||
|
||||
if (foundNode == NULL) {
|
||||
foundNode = getNextNode();
|
||||
memset (foundNode, 0, sizeof (NodeEntry));
|
||||
|
||||
strcpy (foundNode->name, "Anonymous node");
|
||||
foundNode->path.pathLen = 0;
|
||||
|
||||
memcpy (foundNode->pubKey,
|
||||
anonReq.pubKey,
|
||||
sizeof (foundNode->pubKey));
|
||||
|
||||
// --- X25519 conversion + DH ---
|
||||
uint8_t peer_x[32];
|
||||
|
||||
crypto_eddsa_to_x25519 (peer_x, anonReq.pubKey);
|
||||
|
||||
crypto_x25519 (foundNode->secret,
|
||||
persistent.privkey,
|
||||
peer_x);
|
||||
|
||||
foundNode->gps_latitude = 0;
|
||||
foundNode->gps_longitude = 0;
|
||||
foundNode->type = 0;
|
||||
foundNode->last_seen_lt = RTC_GetCounter();
|
||||
|
||||
MESH_LOGI (TAG, "New anonymous node created: %s", foundNode->name);
|
||||
} else {
|
||||
MESH_LOGD (TAG,
|
||||
"Existing node found for pubKey[0]=0x%02X",
|
||||
anonReq.pubKey[0]);
|
||||
}
|
||||
|
||||
mac_then_decrypt (foundNode->secret, 32, &(frame->payload[index]), frame->payloadLen - index, anonReq.payload);
|
||||
anonReq.payloadLen = frame->payloadLen - index - 2;
|
||||
|
||||
hexdump ("AnonReq payload", anonReq.payload, anonReq.payloadLen);
|
||||
|
||||
uint8_t index2 = 0;
|
||||
foundNode->last_seen_rt = anonReq.payload[index2++];
|
||||
foundNode->last_seen_rt |= anonReq.payload[index2++] << 8;
|
||||
foundNode->last_seen_rt |= anonReq.payload[index2++] << 16;
|
||||
foundNode->last_seen_rt |= anonReq.payload[index2++] << 24;
|
||||
|
||||
if (persistent.nodeType == NODE_TYPE_ROOM_SERVER) {
|
||||
foundNode->sync_timestamp = anonReq.payload[index2++];
|
||||
foundNode->sync_timestamp |= anonReq.payload[index2++] << 8;
|
||||
foundNode->sync_timestamp |= anonReq.payload[index2++] << 16;
|
||||
foundNode->sync_timestamp |= anonReq.payload[index2++] << 24;
|
||||
}
|
||||
|
||||
printAnonRequest (&anonReq, persistent.nodeType == NODE_TYPE_ROOM_SERVER);
|
||||
|
||||
uint8_t passwordLen = anonReq.payloadLen - index2;
|
||||
if (passwordLen > 16) {
|
||||
passwordLen = 16;
|
||||
}
|
||||
|
||||
passwordLen = strnlen (&(anonReq.payload[index2]), passwordLen);
|
||||
|
||||
MESH_LOGI (TAG, "Password len is %d.", passwordLen);
|
||||
uint8_t passwordBuf[16];
|
||||
memcpy (passwordBuf, &(anonReq.payload[index2]), passwordLen);
|
||||
|
||||
if (memcmp (passwordBuf, persistent.password, passwordLen) == 0) {
|
||||
foundNode->authenticated = 1;
|
||||
MESH_LOGI (TAG, "Password correct, node %s authenticated.", foundNode->name);
|
||||
|
||||
MESH_LOGI (TAG, "Login response sent to node %s.", foundNode->name);
|
||||
} else {
|
||||
MESH_LOGW (TAG, "Password incorrect for node %s.", foundNode->name);
|
||||
}
|
||||
|
||||
Response resp;
|
||||
resp.tag = RTC_GetCounter();
|
||||
uint8_t index3 = 0;
|
||||
uint32_t randOut = rand();
|
||||
resp.data[index3++] = RESP_SERVER_LOGIN_OK;
|
||||
resp.data[index3++] = 0; // legacy
|
||||
resp.data[index3++] = foundNode->authenticated; // isadmin
|
||||
resp.data[index3++] = foundNode->authenticated ? PERM_ACL_ADMIN : PERM_ACL_GUEST; // permissions
|
||||
resp.data[index3++] = randOut & 0xFF;
|
||||
resp.data[index3++] = (randOut >> 8) & 0xFF;
|
||||
resp.data[index3++] = (randOut >> 16) & 0xFF;
|
||||
resp.data[index3++] = (randOut >> 24) & 0xFF;
|
||||
resp.data[index3++] = FIRMWARE_VER_LEVEL;
|
||||
resp.dataLen = index3;
|
||||
if ((frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_FLOOD ||
|
||||
(frame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
sendPathBack (foundNode, &(frame->path));
|
||||
}
|
||||
sendEncryptedResponse (foundNode, &resp);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef ANONYMOUS_HEADER
|
||||
#define ANONYMOUS_HEADER
|
||||
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "lib/config.h"
|
||||
|
||||
void decodeAnonReq (const FrameStruct *frame);
|
||||
|
||||
void sendAnonymousRequest (const NodeEntry *targetNode, const uint8_t *password, uint32_t sync);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,148 @@
|
||||
#include "lib/config.h"
|
||||
#include "meshcore/meshframing.h"
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "control.h"
|
||||
#include "meshcore/stats.h"
|
||||
#include "string.h"
|
||||
#include "util/hexdump.h"
|
||||
#include "util/log.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define TAG "Control"
|
||||
|
||||
void sendDiscoverRequest (const DiscoverRequestPayload *discReq) {
|
||||
|
||||
FrameStruct frame;
|
||||
frame.path.pathLen = 0;
|
||||
uint8_t offset = 0;
|
||||
|
||||
// Build payload
|
||||
frame.payload[offset++] = (discReq->prefixOnly & 0x01) | CONTROL_DATA_FLAG_TYPE_NODE_DISCOVER_REQ;
|
||||
|
||||
frame.payload[offset++] = discReq->typeFilter;
|
||||
|
||||
frame.payload[offset++] = (discReq->tag >> 0) & 0xFF;
|
||||
frame.payload[offset++] = (discReq->tag >> 8) & 0xFF;
|
||||
frame.payload[offset++] = (discReq->tag >> 16) & 0xFF;
|
||||
frame.payload[offset++] = (discReq->tag >> 24) & 0xFF;
|
||||
|
||||
// optional `since`
|
||||
if (discReq->since != 0) { // or another condition if you want to always include
|
||||
frame.payload[offset++] = (discReq->since >> 0) & 0xFF;
|
||||
frame.payload[offset++] = (discReq->since >> 8) & 0xFF;
|
||||
frame.payload[offset++] = (discReq->since >> 16) & 0xFF;
|
||||
frame.payload[offset++] = (discReq->since >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
frame.payloadLen = offset;
|
||||
|
||||
LoRaTransmit (&frame);
|
||||
}
|
||||
|
||||
void sendDiscoverResponse (const DiscoverResponsePayload *discResp) {
|
||||
FrameStruct frame;
|
||||
frame.header = ROUTE_TYPE_DIRECT | PAYLOAD_TYPE_CONTROL | PAYLOAD_VERSION_0;
|
||||
frame.path.pathLen = 0;
|
||||
uint8_t offset = 0;
|
||||
|
||||
/* Control type + node type (lower nibble) */
|
||||
frame.payload[offset++] =
|
||||
(discResp->nodeType & 0x0F) |
|
||||
CONTROL_DATA_FLAG_DISCOVER_RESP;
|
||||
|
||||
/* SNR */
|
||||
frame.payload[offset++] = (uint8_t)discResp->snr;
|
||||
|
||||
/* Tag (LE) */
|
||||
frame.payload[offset++] = (discResp->tag >> 0) & 0xFF;
|
||||
frame.payload[offset++] = (discResp->tag >> 8) & 0xFF;
|
||||
frame.payload[offset++] = (discResp->tag >> 16) & 0xFF;
|
||||
frame.payload[offset++] = (discResp->tag >> 24) & 0xFF;
|
||||
|
||||
/* Pubkey */
|
||||
if (discResp->pubkeyLen > 0) {
|
||||
memcpy (&frame.payload[offset],
|
||||
discResp->pubkey,
|
||||
discResp->pubkeyLen);
|
||||
offset += discResp->pubkeyLen;
|
||||
}
|
||||
|
||||
frame.payloadLen = offset;
|
||||
|
||||
LoRaTransmit (&frame);
|
||||
}
|
||||
|
||||
void printDiscoverRequest (const DiscoverRequestPayload *p) {
|
||||
iprintf ("=== Discover Request ===\n");
|
||||
iprintf ("prefixOnly : %u\n", p->prefixOnly);
|
||||
iprintf ("typeFilter : 0x%02X\n", p->typeFilter);
|
||||
iprintf ("tag : 0x%08lX\n", (unsigned long)p->tag);
|
||||
iprintf ("since : 0x%08lX\n", (unsigned long)p->since);
|
||||
}
|
||||
|
||||
void printDiscoverResponse (const DiscoverResponsePayload *p) {
|
||||
iprintf ("=== Discover Response ===\n");
|
||||
iprintf ("nodeType : %u\n", p->nodeType);
|
||||
iprintf ("snr : %u\n", p->snr);
|
||||
iprintf ("tag : 0x%08lX\n", (unsigned long)p->tag);
|
||||
|
||||
hexdump ("pubkey : ", p->pubkey, p->pubkeyLen);
|
||||
iprintf ("\n");
|
||||
}
|
||||
|
||||
void decodeControlFrame (const FrameStruct *frame) {
|
||||
uint8_t index = 0;
|
||||
uint8_t type = frame->payload[index] & 0xF0;
|
||||
if (type == CONTROL_DATA_FLAG_TYPE_NODE_DISCOVER_REQ) {
|
||||
DiscoverRequestPayload discReq;
|
||||
discReq.prefixOnly = frame->payload[index++] & 0x01;
|
||||
|
||||
discReq.typeFilter = frame->payload[index++];
|
||||
|
||||
discReq.tag = frame->payload[index++];
|
||||
discReq.tag |= frame->payload[index++] << 8;
|
||||
discReq.tag |= frame->payload[index++] << 16;
|
||||
discReq.tag |= frame->payload[index++] << 24;
|
||||
|
||||
if (index < frame->payloadLen) {
|
||||
discReq.since = frame->payload[index++];
|
||||
discReq.since |= frame->payload[index++] << 8;
|
||||
discReq.since |= frame->payload[index++] << 16;
|
||||
discReq.since |= frame->payload[index++] << 24;
|
||||
}
|
||||
printDiscoverRequest (&discReq);
|
||||
if ((discReq.typeFilter >> 1) & persistent.nodeType) {
|
||||
DiscoverResponsePayload discResp;
|
||||
|
||||
discResp.tag = discReq.tag;
|
||||
discResp.nodeType = persistent.nodeType;
|
||||
discResp.pubkeyLen = sizeof (persistent.pubkey);
|
||||
memcpy (discResp.pubkey, persistent.pubkey, discResp.pubkeyLen);
|
||||
discResp.snr = stats.lastSNR; // hopefully the correct one
|
||||
|
||||
|
||||
MESH_LOGD (TAG, "Replying to a discover request with tag %d", discResp.tag);
|
||||
sendDiscoverResponse (&discResp);
|
||||
printDiscoverResponse (&discResp);
|
||||
}
|
||||
|
||||
} else if (type == CONTROL_DATA_FLAG_DISCOVER_RESP) {
|
||||
DiscoverResponsePayload discResp;
|
||||
discResp.nodeType = frame->payload[index++] & 0x0F;
|
||||
|
||||
discResp.snr = frame->payload[index++];
|
||||
|
||||
discResp.tag = frame->payload[index++];
|
||||
discResp.tag |= frame->payload[index++] << 8;
|
||||
discResp.tag |= frame->payload[index++] << 16;
|
||||
discResp.tag |= frame->payload[index++] << 24;
|
||||
|
||||
uint8_t remainingLen = frame->payloadLen - index;
|
||||
|
||||
uint8_t pubKeyLen = (remainingLen > 8) ? sizeof (discResp.pubkey) : 8;
|
||||
discResp.pubkeyLen = pubKeyLen;
|
||||
memcpy (discResp.pubkey, &(frame->payload[index]), discResp.pubkeyLen);
|
||||
index += pubKeyLen;
|
||||
printDiscoverResponse (&discResp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef CONTROL_HEADER
|
||||
#define CONTROL_HEADER
|
||||
|
||||
#include "meshcore/packetstructs.h"
|
||||
|
||||
void sendDiscoverRequest (const DiscoverRequestPayload *discReq);
|
||||
|
||||
void sendDiscoverResponse (const DiscoverResponsePayload *discResp);
|
||||
|
||||
void printDiscoverRequest (const DiscoverRequestPayload *p);
|
||||
|
||||
void printDiscoverResponse (const DiscoverResponsePayload *p);
|
||||
|
||||
void decodeControlFrame (const FrameStruct *frame);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "meshcore/packetstructs.h"
|
||||
|
||||
#define TAG "Custom"
|
||||
@@ -0,0 +1,5 @@
|
||||
#ifndef CUSTOM_HEADER
|
||||
#define CUSTOM_HEADER
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,837 @@
|
||||
#include "lib/config.h"
|
||||
#include "ch32v20x.h"
|
||||
#include "lib/telemetry/telemetry.h"
|
||||
#include "meshcore/meshframing.h"
|
||||
#include "meshcore/packets/ack.h"
|
||||
#include "meshcore/packets/advert.h"
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "meshcore/stats.h"
|
||||
#include "util/hexdump.h"
|
||||
#include "util/log.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "encrypted.h"
|
||||
#include "lib/adc/temperature.h"
|
||||
#include "lib/rtc/rtc.h"
|
||||
#include "sx1262.h"
|
||||
|
||||
#define TAG "EncryptedMessage"
|
||||
|
||||
void sendEncryptedFrame (const NodeEntry *targetNode, uint8_t payloadType, const uint8_t *plain, size_t plainLen) {
|
||||
FrameStruct frame;
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
size_t offset = 0;
|
||||
|
||||
// 1. Header
|
||||
frame.header =
|
||||
(targetNode->path.pathLen > 0 ? ROUTE_TYPE_DIRECT : ROUTE_TYPE_FLOOD) | // currently flood
|
||||
payloadType |
|
||||
PAYLOAD_VERSION_0;
|
||||
|
||||
// 2. Destination + source
|
||||
frame.payload[offset++] = targetNode->pubKey[0];
|
||||
frame.payload[offset++] = persistent.pubkey[0];
|
||||
|
||||
// 4. Encrypt + MAC
|
||||
size_t encLen;
|
||||
encrypt_then_mac (
|
||||
targetNode->secret,
|
||||
32,
|
||||
plain,
|
||||
plainLen,
|
||||
frame.payload + offset,
|
||||
&encLen);
|
||||
|
||||
MESH_LOGD(TAG, "Plain len: %d, enc len: %d", plainLen, encLen);
|
||||
|
||||
offset += encLen;
|
||||
|
||||
// 5. Finalize
|
||||
frame.payloadLen = offset;
|
||||
memcpy (&frame.path, &targetNode->path, sizeof (frame.path));
|
||||
|
||||
hexdump ("Encrypted frame", frame.payload, frame.payloadLen);
|
||||
LoRaTransmit (&frame);
|
||||
MESH_LOGD (TAG, "Encrypted frame tx finish\n");
|
||||
}
|
||||
|
||||
void sendEncryptedTextMessage (const NodeEntry *targetNode, const PlainTextMessagePayload *msg) {
|
||||
if (targetNode == NULL) {
|
||||
MESH_LOGW (TAG, "Node is null");
|
||||
return;
|
||||
}
|
||||
if (targetNode->last_seen_lt == 0) {
|
||||
MESH_LOGW (TAG, "Node is not populated");
|
||||
return;
|
||||
}
|
||||
uint8_t buf[256];
|
||||
uint8_t index = 0;
|
||||
|
||||
uint8_t msgLen = strlen (msg->message) + 1;
|
||||
buf[index++] = msg->timestamp;
|
||||
buf[index++] = msg->timestamp >> 8;
|
||||
buf[index++] = msg->timestamp >> 16;
|
||||
buf[index++] = msg->timestamp >> 24;
|
||||
|
||||
buf[index++] = (msg->textType << 2) | (msg->attempt & 0x03);
|
||||
memcpy (&buf[index], msg->message, msgLen);
|
||||
index += msgLen;
|
||||
|
||||
sendEncryptedFrame (
|
||||
targetNode,
|
||||
PAYLOAD_TYPE_TXT_MSG,
|
||||
buf,
|
||||
index);
|
||||
}
|
||||
|
||||
void sendEncryptedResponse (const NodeEntry *targetNode, const Response *resp) {
|
||||
uint8_t buf[256];
|
||||
uint8_t index = 0;
|
||||
|
||||
buf[index++] = (resp->tag) & 0xFF;
|
||||
buf[index++] = (resp->tag >> 8) & 0xFF;
|
||||
buf[index++] = (resp->tag >> 16) & 0xFF;
|
||||
buf[index++] = (resp->tag >> 24) & 0xFF;
|
||||
|
||||
memcpy (&(buf[index]), resp->data, resp->dataLen);
|
||||
index += resp->dataLen;
|
||||
|
||||
sendEncryptedFrame (
|
||||
targetNode,
|
||||
PAYLOAD_TYPE_RESPONSE,
|
||||
buf,
|
||||
index);
|
||||
}
|
||||
|
||||
void sendEncryptedRequest (const NodeEntry *targetNode, const Request *req) {
|
||||
uint8_t buf[256];
|
||||
uint8_t index = 0;
|
||||
|
||||
buf[index++] = req->timestamp;
|
||||
buf[index++] = req->timestamp >> 8;
|
||||
buf[index++] = req->timestamp >> 16;
|
||||
buf[index++] = req->timestamp >> 24;
|
||||
|
||||
buf[index++] = req->requestType;
|
||||
memcpy (&(buf[index]), req->data, req->dataLen);
|
||||
index += req->dataLen;
|
||||
|
||||
sendEncryptedFrame (
|
||||
targetNode,
|
||||
PAYLOAD_TYPE_REQ,
|
||||
buf,
|
||||
index);
|
||||
}
|
||||
|
||||
void sendEncryptedPathPayload (const NodeEntry *targetNode, const ReturnedPathPayload *path) {
|
||||
uint8_t buf[256];
|
||||
uint8_t index = 0;
|
||||
|
||||
buf[index++] = path->path.pathLen;
|
||||
memcpy (&buf[index], path->path.path, path->path.pathLen);
|
||||
index += path->path.pathLen;
|
||||
|
||||
if (path->extra.dataLen > 0) {
|
||||
buf[index++] = path->extra.type;
|
||||
memcpy (&buf[index], path->extra.data, path->extra.dataLen);
|
||||
} else {
|
||||
buf[index++] = 0xFF;
|
||||
uint32_t timestamp = RTC_GetCounter();
|
||||
buf[index++] = timestamp;
|
||||
buf[index++] = timestamp >> 8;
|
||||
buf[index++] = timestamp >> 16;
|
||||
buf[index++] = timestamp >> 24;
|
||||
}
|
||||
|
||||
sendEncryptedFrame (
|
||||
targetNode,
|
||||
PAYLOAD_TYPE_PATH,
|
||||
buf,
|
||||
index);
|
||||
}
|
||||
|
||||
void printRequest (const Request *req) {
|
||||
iprintf ("Request:\n");
|
||||
iprintf (" Timestamp: %u\n", req->timestamp);
|
||||
iprintf (" Type: 0x%02X\n", req->requestType);
|
||||
iprintf (" Data: ");
|
||||
hexdump (" Data", req->data, req->dataLen);
|
||||
}
|
||||
|
||||
void printResponse (const Response *resp) {
|
||||
iprintf ("Response:\n");
|
||||
iprintf (" Tag: %u\n", resp->tag);
|
||||
iprintf (" Data: ");
|
||||
hexdump (" Data", resp->data, resp->dataLen);
|
||||
}
|
||||
|
||||
void printPlainTextMessage (const PlainTextMessagePayload *msg) {
|
||||
iprintf ("PlainTextMessage:\n");
|
||||
iprintf (" Timestamp: %u\n", msg->timestamp);
|
||||
iprintf (" Attempt: %u\n", msg->attempt);
|
||||
iprintf (" TextType: %u\n", msg->textType);
|
||||
iprintf (" Message: %.*s\n", (int)strlen (msg->message), msg->message);
|
||||
}
|
||||
|
||||
void printReturnedPathPayload (const ReturnedPathPayload *path) {
|
||||
iprintf ("ReturnedPathPayload:\n");
|
||||
iprintf (" Path Length: %u\n", path->path.pathLen);
|
||||
iprintf (" Path: ");
|
||||
hexdump (" Path:", path->path.path, path->path.pathLen);
|
||||
iprintf (" Extra Type: %u\n", path->extra.type);
|
||||
iprintf (" Extra Data: ");
|
||||
hexdump (" Extra data:", path->extra.data, path->extra.dataLen);
|
||||
}
|
||||
|
||||
void printEncryptedPayload (const EncryptedPayloadStruct *enc) {
|
||||
iprintf ("EncryptedPayload:\n");
|
||||
iprintf (" Type: 0x%02X\n", enc->type);
|
||||
iprintf (" DestinationHash: 0x%02X\n", enc->destinationHash);
|
||||
iprintf (" SourceHash: 0x%02X\n", enc->sourceHash);
|
||||
iprintf (" CipherMAC: 0x%04X\n", enc->cipherMAC);
|
||||
iprintf (" PayloadLen: %zu\n", enc->payloadLen);
|
||||
iprintf (" Payload: ");
|
||||
for (size_t i = 0; i < enc->payloadLen; i++) {
|
||||
iprintf ("%02X ", enc->payload[i]);
|
||||
}
|
||||
iprintf ("\n");
|
||||
}
|
||||
|
||||
void decodeEncryptedPayload (const FrameStruct *frame) {
|
||||
EncryptedPayloadStruct enc;
|
||||
memset (&enc, 0, sizeof (enc));
|
||||
enc.path = &(frame->path);
|
||||
enc.origFrame = frame;
|
||||
enc.type = frame->header & PAYLOAD_TYPE_MASK;
|
||||
unsigned char index = 0;
|
||||
|
||||
enc.destinationHash = frame->payload[index++];
|
||||
enc.sourceHash = frame->payload[index++];
|
||||
enc.cipherMAC = frame->payload[index];
|
||||
enc.cipherMAC |= frame->payload[index + 1] << 8;
|
||||
|
||||
if (enc.destinationHash != persistent.pubkey[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
MESH_LOGI (TAG, "Finding remote node, sourceHash is %d", enc.sourceHash);
|
||||
|
||||
NodeEntry *remNode = getNode (enc.sourceHash);
|
||||
|
||||
enc.remNode = remNode;
|
||||
|
||||
if (remNode == NULL) {
|
||||
MESH_LOGW (TAG, "Node not in DB");
|
||||
return;
|
||||
}
|
||||
remNode->last_seen_lt = RTC_GetCounter();
|
||||
MESH_LOGI (TAG, "Found node with index %d", remNode - persistent.contacts);
|
||||
|
||||
|
||||
if (mac_then_decrypt (remNode->secret, 32, &(frame->payload[index]), frame->payloadLen - index, enc.payload) != 0) {
|
||||
MESH_LOGW (TAG, "HMAC failed on encrypted message %s", remNode->name);
|
||||
} else {
|
||||
enc.payloadLen = frame->payloadLen - HMAC_SIZE;
|
||||
MESH_LOGI (TAG, "HMAC success from %s, %u bytes long", remNode->name, enc.payloadLen);
|
||||
sendDiscreteAck (enc.payload, 5 + strlen ((char *)&enc.payload[5]), remNode->pubKey);
|
||||
}
|
||||
|
||||
iprintf (" Typexdd: 0x%02X\n", enc.type);
|
||||
if (enc.payloadLen > 0) {
|
||||
parseEncryptedPayload (&enc);
|
||||
}
|
||||
}
|
||||
|
||||
void sendPathBack (const NodeEntry *node, const Path *path) {
|
||||
ReturnedPathPayload retPath;
|
||||
retPath.extra.dataLen = 0; // redo to send the resp in path
|
||||
retPath.extra.type = 0xFF;
|
||||
|
||||
retPath.path.pathLen = path->pathLen;
|
||||
memcpy (retPath.path.path, path->path, path->pathLen);
|
||||
|
||||
sendEncryptedPathPayload (node, &retPath);
|
||||
}
|
||||
|
||||
void parseEncryptedPayload (const EncryptedPayloadStruct *enc) {
|
||||
// printEncryptedPayload(&enc);
|
||||
|
||||
iprintf ("EncryptedPayload:\n");
|
||||
iprintf (" Type: 0x%02X\n", enc->type);
|
||||
iprintf (" DestinationHash: 0x%02X\n", enc->destinationHash);
|
||||
iprintf (" SourceHash: 0x%02X\n", enc->sourceHash);
|
||||
iprintf (" CipherMAC: 0x%04X\n", enc->cipherMAC);
|
||||
iprintf (" PayloadLen: %u\n", enc->payloadLen);
|
||||
hexdump (" Payload: ", enc->payload, enc->payloadLen);
|
||||
iprintf ("\n");
|
||||
|
||||
uint8_t index = 0;
|
||||
if (enc->type == PAYLOAD_TYPE_PATH) {
|
||||
ReturnedPathPayload retPath;
|
||||
retPath.path.pathLen = enc->payload[index++];
|
||||
if (retPath.path.pathLen > 64) {
|
||||
MESH_LOGW (TAG, "Path too long\n");
|
||||
return;
|
||||
}
|
||||
memcpy (retPath.path.path, &(enc->payload[index]), retPath.path.pathLen);
|
||||
index += retPath.path.pathLen;
|
||||
retPath.extra.type = enc->payload[index++];
|
||||
retPath.extra.dataLen = enc->payloadLen - index;
|
||||
memcpy (retPath.extra.data, &(enc->payload[index]), retPath.extra.dataLen);
|
||||
|
||||
if ((enc->origFrame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_FLOOD ||
|
||||
(enc->origFrame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
sendPathBack (enc->remNode, enc->path);
|
||||
}
|
||||
|
||||
} else if (enc->type == PAYLOAD_TYPE_REQ) {
|
||||
Request req;
|
||||
req.timestamp = enc->payload[index++];
|
||||
req.timestamp |= enc->payload[index++] << 8;
|
||||
req.timestamp |= enc->payload[index++] << 16;
|
||||
req.timestamp |= enc->payload[index++] << 24;
|
||||
enc->remNode->last_seen_rt = req.timestamp;
|
||||
req.requestType = enc->payload[index++];
|
||||
req.dataLen = enc->payloadLen - index;
|
||||
memcpy (req.data, &(enc->payload[index]), req.dataLen);
|
||||
printRequest (&req);
|
||||
if ((enc->origFrame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_FLOOD ||
|
||||
(enc->origFrame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
sendPathBack (enc->remNode, enc->path);
|
||||
}
|
||||
switch (req.requestType) {
|
||||
case REQUEST_GET_STATS: {
|
||||
Response resp;
|
||||
resp.tag = RTC_GetCounter();
|
||||
stats.totalUpTimeSeconds = RTC_GetCounter() - startupTime;
|
||||
//stats.totalAirTimeSeconds = TICKS_TO_MS (tickAirtime / 1000);
|
||||
stats.totalAirTimeSeconds = 0;
|
||||
memcpy (resp.data, &stats, sizeof (stats));
|
||||
resp.dataLen = sizeof (stats);
|
||||
sendEncryptedResponse (enc->remNode, &resp);
|
||||
break;
|
||||
}
|
||||
case REQUEST_KEEPALIVE:
|
||||
break;
|
||||
case REQUEST_GET_TELEMETRY_DATA: {
|
||||
Response resp;
|
||||
resp.tag = req.timestamp;
|
||||
enc->remNode->last_seen_rt = req.timestamp;
|
||||
resp.dataLen = 0;
|
||||
resp.data[resp.dataLen++] = TELEM_CHANNEL_SELF;
|
||||
resp.data[resp.dataLen++] = LPP_TEMPERATURE;
|
||||
|
||||
int16_t dataTemp = getDeciTemperature();
|
||||
|
||||
resp.data[resp.dataLen++] = (dataTemp >> 8) & 0xFF;
|
||||
|
||||
resp.data[resp.dataLen++] = dataTemp & 0xFF;
|
||||
|
||||
resp.data[resp.dataLen++] = TELEM_CHANNEL_SELF;
|
||||
resp.data[resp.dataLen++] = LPP_VOLTAGE;
|
||||
|
||||
int16_t dataVolt = stats.millivolts / 10;
|
||||
|
||||
resp.data[resp.dataLen++] = (dataVolt >> 8) & 0xFF;
|
||||
|
||||
resp.data[resp.dataLen++] = dataVolt & 0xFF;
|
||||
|
||||
resp.data[resp.dataLen++] = 2;
|
||||
resp.data[resp.dataLen++] = LPP_VOLTAGE;
|
||||
|
||||
dataVolt = 1973;
|
||||
|
||||
resp.data[resp.dataLen++] = (dataVolt >> 8) & 0xFF;
|
||||
|
||||
resp.data[resp.dataLen++] = dataVolt & 0xFF;
|
||||
|
||||
if (enc->remNode->authenticated) {
|
||||
|
||||
resp.data[resp.dataLen++] = 2; // channel 2
|
||||
resp.data[resp.dataLen++] = LPP_TEMPERATURE;
|
||||
|
||||
int16_t jokeTemp = 6942;
|
||||
|
||||
resp.data[resp.dataLen++] = (jokeTemp >> 8) & 0xFF;
|
||||
|
||||
resp.data[resp.dataLen++] = jokeTemp & 0xFF;
|
||||
|
||||
|
||||
encode_gps (TELEM_CHANNEL_SELF, persistent.latitude, persistent.longitude, persistent.altitude, &(resp.data[resp.dataLen]));
|
||||
// encode_gps(TELEM_CHANNEL_SELF, 48.1909f, 17.0303f, 234.0f, &(resp.data[resp.dataLen]));
|
||||
|
||||
resp.dataLen += LPP_GPS_SIZE + 2;
|
||||
}
|
||||
|
||||
|
||||
sendEncryptedResponse (enc->remNode, &resp);
|
||||
|
||||
iprintf ("Sent response, the temperature is %d decicelsius\n", dataTemp);
|
||||
|
||||
break;
|
||||
}
|
||||
case REQUEST_GET_MIN_MAX_AVG:
|
||||
break;
|
||||
case REQUEST_GET_ACCESS_LIST:
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (enc->type == PAYLOAD_TYPE_RESPONSE) {
|
||||
Response resp;
|
||||
resp.tag = enc->payload[index++];
|
||||
resp.tag |= enc->payload[index++] << 8;
|
||||
resp.tag |= enc->payload[index++] << 16;
|
||||
resp.tag |= enc->payload[index++] << 24;
|
||||
resp.dataLen = enc->payloadLen - index;
|
||||
memcpy (resp.data, &(enc->payload[index]), resp.dataLen);
|
||||
printResponse (&resp);
|
||||
|
||||
} else if (enc->type == PAYLOAD_TYPE_TXT_MSG) {
|
||||
PlainTextMessagePayload plaintext;
|
||||
plaintext.timestamp = enc->payload[index++];
|
||||
plaintext.timestamp |= enc->payload[index++] << 8;
|
||||
plaintext.timestamp |= enc->payload[index++] << 16;
|
||||
plaintext.timestamp |= enc->payload[index++] << 24;
|
||||
enc->remNode->last_seen_rt = plaintext.timestamp;
|
||||
plaintext.attempt = enc->payload[index] & 0x03;
|
||||
plaintext.textType = enc->payload[index++] >> 2;
|
||||
memcpy (plaintext.message, &(enc->payload[index]), enc->payloadLen - index);
|
||||
if ((enc->origFrame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_FLOOD ||
|
||||
(enc->origFrame->header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
sendPathBack (enc->remNode, enc->path);
|
||||
}
|
||||
switch (plaintext.textType) {
|
||||
case TXT_TYPE_PLAIN:
|
||||
iprintf ("Plaintext message from %s, attempt %d, timestamp %d: %s", enc->remNode->name, plaintext.attempt, plaintext.timestamp, plaintext.message);
|
||||
break;
|
||||
|
||||
case TXT_TYPE_CLI_DATA:
|
||||
if (enc->remNode->authenticated) {
|
||||
processCommand (plaintext.message, enc->remNode);
|
||||
}
|
||||
break;
|
||||
|
||||
case TXT_TYPE_SIGNED_PLAIN: {
|
||||
uint8_t senderPubKeyPrefix[4];
|
||||
memcpy (senderPubKeyPrefix, plaintext.message, sizeof (senderPubKeyPrefix));
|
||||
NodeEntry *senderNode = getNodePrefix (senderPubKeyPrefix);
|
||||
iprintf ("Plaintext message from server %s, sender is %s, attempt %d, timestamp %d: %s", enc->remNode->name, senderNode->name, plaintext.attempt, plaintext.timestamp, &(plaintext.message[4]));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MESH_LOGW (TAG, "Unknown text type: %d", plaintext.textType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #define STR_EQ_LIT(s, lit) (memcmp ((s), (lit), sizeof (lit) - 1) == 0)
|
||||
#define STR_EQ_LIT(s, lit) (strcmp (s, lit) == 0)
|
||||
|
||||
static int32_t parse_coord_micro(const char *s)
|
||||
{
|
||||
int32_t sign = 1;
|
||||
int32_t int_part = 0;
|
||||
int32_t frac_part = 0;
|
||||
int32_t frac_div = 1;
|
||||
|
||||
if (*s == '-') {
|
||||
sign = -1;
|
||||
s++;
|
||||
}
|
||||
|
||||
// integer part
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
int_part = int_part * 10 + (*s - '0');
|
||||
s++;
|
||||
}
|
||||
|
||||
if (*s == '.') {
|
||||
s++;
|
||||
while (*s >= '0' && *s <= '9' && frac_div < 1000000) {
|
||||
frac_part = frac_part * 10 + (*s - '0');
|
||||
frac_div *= 10;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
// scale to microdegrees
|
||||
while (frac_div < 1000000) {
|
||||
frac_part *= 10;
|
||||
frac_div *= 10;
|
||||
}
|
||||
|
||||
return sign * (int_part * 1000000 + frac_part);
|
||||
}
|
||||
|
||||
void processCommand (char *cmd, NodeEntry *remNode) {
|
||||
PlainTextMessagePayload replyPayload;
|
||||
replyPayload.timestamp = RTC_GetCounter();
|
||||
replyPayload.attempt = 0;
|
||||
replyPayload.textType = TXT_TYPE_CLI_DATA;
|
||||
|
||||
uint8_t *reply = replyPayload.message;
|
||||
reply[0] = 0;
|
||||
|
||||
while (*cmd == ' ') cmd++; // skip leading spaces
|
||||
|
||||
// Optional CLI prefix (xx|) for companion radio
|
||||
if (strlen (cmd) > 4 && cmd[2] == '|') {
|
||||
memcpy (reply, cmd, 3);
|
||||
reply += 3;
|
||||
cmd += 3;
|
||||
}
|
||||
|
||||
/* ---------------- System ---------------- */
|
||||
if (STR_EQ_LIT (cmd, "reboot")) {
|
||||
NVIC_SystemReset();
|
||||
} else if (STR_EQ_LIT (cmd, "advert")) {
|
||||
sendAdvert (1); // 1500ms delay in reference
|
||||
strcpy ((char *)reply, "OK - Advert sent");
|
||||
} else if (STR_EQ_LIT (cmd, "clear stats")) {
|
||||
memset (&stats, 0, sizeof (stats));
|
||||
strcpy ((char *)reply, "(OK - stats reset)");
|
||||
}
|
||||
|
||||
else if (STR_EQ_LIT (cmd, "ver")) {
|
||||
sprintf ((char *)reply, "%s (Build: %s)", VERSION, __DATE__);
|
||||
}
|
||||
|
||||
else if (STR_EQ_LIT (cmd, "board")) {
|
||||
sprintf ((char *)reply, "%s", BOARD);
|
||||
}
|
||||
|
||||
/* ---------------- Clock ---------------- */
|
||||
else if (STR_EQ_LIT (cmd, "clock")) {
|
||||
RTC_Get();
|
||||
sprintf ((char *)reply, "%02d:%02d:%02d - %d/%d/%d UTC",
|
||||
calendar.hour, calendar.min, calendar.sec,
|
||||
calendar.w_date, calendar.w_month, calendar.w_year);
|
||||
} else if (STR_EQ_LIT (cmd, "time ")) {
|
||||
uint32_t secs = atoi (&cmd[5]);
|
||||
uint32_t curr = RTC_GetCounter();
|
||||
if (secs > curr) {
|
||||
RTC_SetCounter (secs);
|
||||
RTC_Get();
|
||||
sprintf ((char *)reply, "OK - clock set: %02d:%02d:%02d - %d/%d/%d UTC",
|
||||
calendar.hour, calendar.min, calendar.sec,
|
||||
calendar.w_date, calendar.w_month, calendar.w_year);
|
||||
} else {
|
||||
strcpy ((char *)reply, "(ERR: clock cannot go backwards)");
|
||||
}
|
||||
} else if (STR_EQ_LIT (cmd, "clock sync")) {
|
||||
uint32_t curr = RTC_GetCounter();
|
||||
uint32_t sender = replyPayload.timestamp;
|
||||
if (sender > curr) {
|
||||
RTC_SetCounter (sender + 1);
|
||||
RTC_Get();
|
||||
sprintf ((char *)reply, "OK - clock set: %02d:%02d:%02d - %d/%d/%d UTC",
|
||||
calendar.hour, calendar.min, calendar.sec,
|
||||
calendar.w_date, calendar.w_month, calendar.w_year);
|
||||
} else {
|
||||
strcpy ((char *)reply, "ERR: clock cannot go backwards");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
else if (STR_EQ_LIT (cmd, "tempradio ")) {
|
||||
char tmp[64];
|
||||
strcpy (tmp, &cmd[10]);
|
||||
|
||||
const char *parts[5];
|
||||
int num = mesh_ParseTextParts (tmp, parts, 5); // assume helper
|
||||
float freq = num > 0 ? strtof (parts[0], NULL) : 0.0f;
|
||||
float bw = num > 1 ? strtof (parts[1], NULL) : 0.0f;
|
||||
uint8_t sf = num > 2 ? atoi (parts[2]) : 0;
|
||||
uint8_t cr = num > 3 ? atoi (parts[3]) : 0;
|
||||
int timeout_mins = num > 4 ? atoi (parts[4]) : 0;
|
||||
if (freq >= 300.0f && freq <= 2500.0f && bw >= 7.0f && bw <= 500.0f &&
|
||||
sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8 && timeout_mins > 0) {
|
||||
Callbacks_ApplyTempRadioParams (freq, bw, sf, cr, timeout_mins);
|
||||
|
||||
LoraApply();
|
||||
sprintf ((char *)reply, "OK - temp params for %d mins", timeout_mins);
|
||||
} else {
|
||||
strcpy ((char *)reply, "Error, invalid params");
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/* ---------------- Password ---------------- */
|
||||
else if (STR_EQ_LIT (cmd, "password ")) {
|
||||
strncpy (persistent.password, &cmd[9], sizeof (persistent.password));
|
||||
// savePrefs(); TODO add this
|
||||
sprintf ((char *)reply, "password now: %s", persistent.password);
|
||||
}
|
||||
|
||||
/* ---------------- GET / SET Config ---------------- */
|
||||
else if (STR_EQ_LIT (cmd, "get ")) {
|
||||
const char *config = &cmd[4];
|
||||
|
||||
/*
|
||||
if (memcmp (config, "af", 2) == 0) {
|
||||
sprintf (reply, "> %f", persistent.airtimeFactor);
|
||||
*/
|
||||
/*
|
||||
} else if (memcmp (config, "int.thresh", 10) == 0) {
|
||||
sprintf (reply, "> %d", (uint32_t)_prefs->interference_threshold);
|
||||
} else if (memcmp (config, "agc.reset.interval", 18) == 0) {
|
||||
sprintf (reply, "> %d", ((uint32_t)_prefs->agc_reset_interval) * 4);
|
||||
} else if (memcmp (config, "multi.acks", 10) == 0) {
|
||||
sprintf (reply, "> %d", (uint32_t)_prefs->multi_acks);
|
||||
} else if (memcmp (config, "allow.read.only", 15) == 0) {
|
||||
sprintf (reply, "> %s", _prefs->allow_read_only ? "on" : "off");
|
||||
} else if (memcmp (config, "flood.advert.interval", 21) == 0) {
|
||||
sprintf (reply, "> %d", (uint32_t)_prefs->flood_advert_interval);
|
||||
} else if (memcmp (config, "advert.interval", 15) == 0) {
|
||||
sprintf (reply, "> %d", ((uint32_t)_prefs->advert_interval) * 2);1
|
||||
*/
|
||||
//} else
|
||||
if (memcmp (config, "guest.password", 14) == 0) {
|
||||
sprintf (reply, "> %s", persistent.guestPassword);
|
||||
} else if (memcmp (config, "name", 4) == 0) {
|
||||
sprintf (reply, "> %s", persistent.nodeName);
|
||||
} else if (memcmp (config, "repeat", 6) == 0) {
|
||||
sprintf (reply, "> %s", persistent.doRepeat ? "on" : "off");
|
||||
} else if (memcmp (config, "lat", 3) == 0) {
|
||||
sprintf (reply, "> %d", persistent.latitude);
|
||||
} else if (memcmp (config, "lon", 3) == 0) {
|
||||
sprintf (reply, "> %d", persistent.longitude);
|
||||
/*
|
||||
} else if (memcmp (config, "radio", 5) == 0) {
|
||||
char freq[16], bw[16];
|
||||
snprintf(freq, sizeof(freq), "%lf", persistent.frequencyInHz / 1000000.0);
|
||||
snprintf(bw, sizeof(bw), "%lf", loraBwToFloat(persistent.bandwidth));
|
||||
sprintf (reply, "> %s,%s,%d,%d", freq, bw, persistent.spreadingFactor, persistent.codingRate + 4);
|
||||
} else if (memcmp (config, "rxdelay", 7) == 0) {
|
||||
sprintf (reply, "> %s", StrHelper::ftoa (_prefs->rx_delay_base));
|
||||
} else if (memcmp (config, "txdelay", 7) == 0) {
|
||||
sprintf (reply, "> %s", StrHelper::ftoa (_prefs->tx_delay_factor));
|
||||
} else if (memcmp (config, "flood.max", 9) == 0) {
|
||||
sprintf (reply, "> %d", (uint32_t)_prefs->flood_max);
|
||||
} else if (memcmp (config, "direct.txdelay", 14) == 0) {
|
||||
sprintf (reply, "> %s", StrHelper::ftoa (_prefs->direct_tx_delay_factor));
|
||||
} else if (memcmp (config, "tx", 2) == 0 && (config[2] == 0 || config[2] == ' ')) {
|
||||
sprintf (reply, "> %d", (uint32_t)_prefs->tx_power_dbm);
|
||||
} else if (memcmp (config, "freq", 4) == 0) {
|
||||
sprintf (reply, "> %s", StrHelper::ftoa (_prefs->freq));
|
||||
*/
|
||||
} else if (memcmp (config, "public.key", 10) == 0) {
|
||||
strcpy (reply, "> ");
|
||||
hexdump_compact (persistent.pubkey, sizeof (persistent.pubkey), &(reply[2]), 70);
|
||||
} else if (memcmp (config, "role", 4) == 0) {
|
||||
sprintf (reply, "> %s", getStringRole (persistent.nodeType));
|
||||
} else if (memcmp (config, "adc.multiplier", 14) == 0) {
|
||||
sprintf (reply, "> %d.%d", persistent.adcMultiplier / 1000, persistent.adcMultiplier % 1000);
|
||||
} else {
|
||||
sprintf (reply, "??: %s", config);
|
||||
}
|
||||
|
||||
} else if (STR_EQ_LIT (cmd, "set ")) {
|
||||
const char *config = &cmd[4];
|
||||
|
||||
|
||||
/*
|
||||
if (memcmp (config, "af ", 3) == 0) {
|
||||
persistent.airtimeFactor = atof (&config[3]);
|
||||
// savePrefs();
|
||||
strcpy (reply, "OK");
|
||||
*/
|
||||
/*
|
||||
} else if (memcmp (config, "int.thresh ", 11) == 0) {
|
||||
_prefs->interference_threshold = atoi (&config[11]);
|
||||
// savePrefs();
|
||||
strcpy (reply, "OK");
|
||||
} else if (memcmp (config, "agc.reset.interval ", 19) == 0) {
|
||||
_prefs->agc_reset_interval = atoi (&config[19]) / 4;
|
||||
// savePrefs();
|
||||
sprintf (reply, "OK - interval rounded to %d", ((uint32_t)_prefs->agc_reset_interval) * 4);
|
||||
} else if (memcmp (config, "multi.acks ", 11) == 0) {
|
||||
_prefs->multi_acks = atoi (&config[11]);
|
||||
// savePrefs();
|
||||
strcpy (reply, "OK");
|
||||
*/
|
||||
//} else
|
||||
if (memcmp (config, "allow.read.only ", 16) == 0) {
|
||||
if (memcmp (&config[16], "on", 2) == 0) {
|
||||
persistent.allowReadOnly = 1;
|
||||
strcpy (reply, "OK");
|
||||
} else if (memcmp (&config[16], "off", 3) == 0) {
|
||||
persistent.allowReadOnly = 0;
|
||||
strcpy (reply, "OK");
|
||||
}
|
||||
// savePrefs();
|
||||
/*
|
||||
} else if (memcmp (config, "flood.advert.interval ", 22) == 0) {
|
||||
int hours = _atoi (&config[22]);
|
||||
if ((hours > 0 && hours < 3) || (hours > 48)) {
|
||||
strcpy (reply, "Error: interval range is 3-48 hours");
|
||||
} else {
|
||||
_prefs->flood_advert_interval = (uint8_t)hours;
|
||||
_callbacks->updateFloodAdvertTimer();
|
||||
savePrefs();
|
||||
strcpy (reply, "OK");
|
||||
}
|
||||
} else if (memcmp (config, "advert.interval ", 16) == 0) {
|
||||
int mins = _atoi (&config[16]);
|
||||
if ((mins > 0 && mins < MIN_LOCAL_ADVERT_INTERVAL) || (mins > 240)) {
|
||||
sprintf (reply, "Error: interval range is %d-240 minutes", MIN_LOCAL_ADVERT_INTERVAL);
|
||||
} else {
|
||||
_prefs->advert_interval = (uint8_t)(mins / 2);
|
||||
_callbacks->updateAdvertTimer();
|
||||
savePrefs();
|
||||
strcpy (reply, "OK");
|
||||
}
|
||||
*/
|
||||
} else if (memcmp (config, "guest.password ", 15) == 0) {
|
||||
strncpy (persistent.guestPassword, &config[15], sizeof (persistent.guestPassword));
|
||||
// savePrefs();
|
||||
strcpy (reply, "OK");
|
||||
} else if (memcmp (config, "name ", 5) == 0) {
|
||||
strncpy (persistent.nodeName, &config[5], sizeof (persistent.nodeName));
|
||||
// savePrefs();
|
||||
strcpy (reply, "OK");
|
||||
} else if (memcmp (config, "repeat ", 7) == 0) {
|
||||
if (memcmp (&config[7], "off", 3) == 0) {
|
||||
persistent.doRepeat = 0;
|
||||
} else if (memcmp (&config[7], "on", 2) == 0) {
|
||||
persistent.doRepeat = 1;
|
||||
}
|
||||
// savePrefs();
|
||||
strcpy (reply, persistent.doRepeat ? "OK - repeat is now ON" : "OK - repeat is now OFF");
|
||||
/*
|
||||
} else if (memcmp (config, "radio ", 6) == 0) {
|
||||
strcpy (tmp, &config[6]);
|
||||
const char *parts[4];
|
||||
int num = mesh::Utils::parseTextParts (tmp, parts, 4);
|
||||
float freq = num > 0 ? strtof (parts[0], 0) : 0.0f;
|
||||
float bw = num > 1 ? strtof (parts[1], 0) : 0.0f;
|
||||
uint8_t sf = num > 2 ? atoi (parts[2]) : 0;
|
||||
uint8_t cr = num > 3 ? atoi (parts[3]) : 0;
|
||||
if (freq >= 300.0f && freq <= 2500.0f && sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7.0f && bw <= 500.0f) {
|
||||
_prefs->sf = sf;
|
||||
_prefs->cr = cr;
|
||||
_prefs->freq = freq;
|
||||
_prefs->bw = bw;
|
||||
_callbacks->savePrefs();
|
||||
strcpy (reply, "OK - reboot to apply");
|
||||
} else {
|
||||
strcpy (reply, "Error, invalid radio params");
|
||||
}
|
||||
*/
|
||||
} else if (memcmp (config, "lat ", 4) == 0) {
|
||||
persistent.latitude = parse_coord_micro(&config[4]);
|
||||
// savePrefs();
|
||||
strcpy (reply, "OK");
|
||||
} else if (memcmp (config, "lon ", 4) == 0) {
|
||||
persistent.longitude = parse_coord_micro(&config[4]);
|
||||
// savePrefs();
|
||||
strcpy (reply, "OK");
|
||||
/*
|
||||
} else if (memcmp (config, "rxdelay ", 8) == 0) {
|
||||
float db = atof (&config[8]);
|
||||
if (db >= 0) {
|
||||
_prefs->rx_delay_base = db;
|
||||
savePrefs();
|
||||
strcpy (reply, "OK");
|
||||
} else {
|
||||
strcpy (reply, "Error, cannot be negative");
|
||||
}
|
||||
} else if (memcmp (config, "txdelay ", 8) == 0) {
|
||||
float f = atof (&config[8]);
|
||||
if (f >= 0) {
|
||||
_prefs->tx_delay_factor = f;
|
||||
savePrefs();
|
||||
strcpy (reply, "OK");
|
||||
} else {
|
||||
strcpy (reply, "Error, cannot be negative");
|
||||
}
|
||||
} else if (memcmp (config, "flood.max ", 10) == 0) {
|
||||
uint8_t m = atoi (&config[10]);
|
||||
if (m <= 64) {
|
||||
_prefs->flood_max = m;
|
||||
savePrefs();
|
||||
strcpy (reply, "OK");
|
||||
} else {
|
||||
strcpy (reply, "Error, max 64");
|
||||
}
|
||||
} else if (memcmp (config, "direct.txdelay ", 15) == 0) {
|
||||
float f = atof (&config[15]);
|
||||
if (f >= 0) {
|
||||
_prefs->direct_tx_delay_factor = f;
|
||||
savePrefs();
|
||||
strcpy (reply, "OK");
|
||||
} else {
|
||||
strcpy (reply, "Error, cannot be negative");
|
||||
}
|
||||
} else if (memcmp (config, "tx ", 3) == 0) {
|
||||
_prefs->tx_power_dbm = atoi (&config[3]);
|
||||
savePrefs();
|
||||
_callbacks->setTxPower (_prefs->tx_power_dbm);
|
||||
strcpy (reply, "OK");
|
||||
*/
|
||||
/*
|
||||
} else if (memcmp (config, "freq ", 5) == 0) {
|
||||
double freq = atof (&config[5]);
|
||||
uint32_t newFreq = mhzToHzLimited (persistent.loraSettings.frequencyInHz);
|
||||
if (newFreq != 0) {
|
||||
persistent.loraSettings.frequencyInHz = newFreq;
|
||||
}
|
||||
// savePrefs();
|
||||
strcpy (reply, "OK - reboot to apply");
|
||||
*/
|
||||
/*
|
||||
} else if (memcmp (config, "adc.multiplier ", 15) == 0) {
|
||||
persistent.adcMultiplier = atof (&config[15]);
|
||||
if (persistent.adcMultiplier == 0.0f) {
|
||||
strcpy (reply, "OK - using default board multiplier");
|
||||
} else {
|
||||
sprintf (reply, "OK - multiplier set to %.3f", persistent.adcMultiplier);
|
||||
}
|
||||
// savePrefs();
|
||||
*/
|
||||
} else {
|
||||
sprintf (reply, "unknown config: %s", config);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------- Stats ---------------- */
|
||||
else if (STR_EQ_LIT (cmd, "stats-packets")) {
|
||||
sprintf (reply,
|
||||
"{\"recv\":%u,\"sent\":%u,\"flood_tx\":%u,\"direct_tx\":%u,\"flood_rx\":%u,\"direct_rx\":%u}",
|
||||
stats.packetsReceivedCount,
|
||||
stats.packetsSentCount,
|
||||
stats.sentFloodCount,
|
||||
stats.sentDirectCount,
|
||||
stats.receivedFloodCount,
|
||||
stats.receivedDirectCount);
|
||||
} else if (STR_EQ_LIT (cmd, "stats-radio")) {
|
||||
sprintf (reply,
|
||||
"{\"noise_floor\":%d,\"last_rssi\":%d,\"last_snr\":%d.00,\"tx_air_secs\":%u,\"rx_air_secs\":%u}",
|
||||
stats.noiseFloor,
|
||||
stats.lastRSSI,
|
||||
stats.lastSNR / 4,
|
||||
stats.totalAirTimeSeconds,
|
||||
stats.total_rx_air_time_secs);
|
||||
} else if (STR_EQ_LIT (cmd, "stats-core")) {
|
||||
stats.totalUpTimeSeconds = RTC_GetCounter() - startupTime;
|
||||
//stats.totalAirTimeSeconds = TICKS_TO_MS (tickAirtime / 1000);
|
||||
stats.totalAirTimeSeconds = 0;
|
||||
sprintf (reply,
|
||||
"{\"battery_mv\":%u,\"uptime_secs\":%u,\"errors\":%u,\"queue_len\":%u}",
|
||||
getVoltage(),
|
||||
stats.totalUpTimeSeconds,
|
||||
stats.err_events,
|
||||
stats.txQueueLength);
|
||||
}
|
||||
|
||||
/* ---------------- Unknown ---------------- */
|
||||
else {
|
||||
strcpy ((char *)reply, "Unknown command");
|
||||
}
|
||||
|
||||
sendEncryptedTextMessage (remNode, &replyPayload);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef ENCRYPTED_HEADER
|
||||
#define ENCRYPTED_HEADER
|
||||
|
||||
#include "lib/config.h"
|
||||
#include "meshcore/meshframing.h"
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "util/log.h"
|
||||
#include <string.h>
|
||||
|
||||
#define MIN_LOCAL_ADVERT_INTERVAL 60
|
||||
|
||||
void sendEncryptedFrame (const NodeEntry *targetNode, uint8_t payloadType, const uint8_t *plain, size_t plainLen);
|
||||
|
||||
void sendEncryptedTextMessage (const NodeEntry *targetNode, const PlainTextMessagePayload *msg);
|
||||
|
||||
void sendEncryptedResponse (const NodeEntry *targetNode, const Response *resp);
|
||||
|
||||
void sendEncryptedRequest (const NodeEntry *targetNode, const Request *req);
|
||||
|
||||
void sendEncryptedPathPayload (const NodeEntry *targetNode, const ReturnedPathPayload *path);
|
||||
|
||||
void decodeEncryptedPayload (const FrameStruct *frame);
|
||||
|
||||
void parseEncryptedPayload (const EncryptedPayloadStruct *enc);
|
||||
|
||||
void processCommand(char * cmd, NodeEntry * remNode);
|
||||
|
||||
void sendPathBack (const NodeEntry *node, const Path *path);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,107 @@
|
||||
#include "ch32v20x_rtc.h"
|
||||
#include "meshcore/meshframing.h"
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "group.h"
|
||||
#include "lib/config.h"
|
||||
#include "util/hexdump.h"
|
||||
#include "util/log.h"
|
||||
#include "string.h"
|
||||
|
||||
#define TAG "GroupMessage"
|
||||
|
||||
void sendGroupMessage (const GroupTextMessage *msg) {
|
||||
// Prepare values locally instead of modifying msg
|
||||
Channel *channel = &(persistent.channels[msg->keyIndex]);
|
||||
uint8_t flags = 0;
|
||||
int32_t timestamp = RTC_GetCounter();
|
||||
|
||||
FrameStruct frame;
|
||||
frame.header = ROUTE_TYPE_FLOOD | PAYLOAD_TYPE_GRP_TXT | PAYLOAD_VERSION_0;
|
||||
frame.path.pathLen = 0;
|
||||
size_t offset = 0;
|
||||
frame.payload[offset++] = channel->hash;
|
||||
|
||||
// Build encryption buffer directly on stack (no extra large buffer)
|
||||
uint8_t buf[180]; // enough for timestamp + flags + text
|
||||
size_t buf_offset = 0;
|
||||
|
||||
memcpy (buf + buf_offset, ×tamp, sizeof (timestamp));
|
||||
buf_offset += sizeof (timestamp);
|
||||
|
||||
buf[buf_offset++] = flags;
|
||||
|
||||
size_t textLen = strlen ((const char *)msg->text);
|
||||
if (buf_offset + textLen > sizeof (buf)) {
|
||||
textLen = sizeof (buf) - buf_offset;
|
||||
}
|
||||
memcpy (buf + buf_offset, msg->text, textLen);
|
||||
buf_offset += textLen;
|
||||
|
||||
hexdump ("TxDumpDec", buf, buf_offset);
|
||||
|
||||
// Encrypt and MAC directly into frame payload after channelHash
|
||||
size_t olen = 0;
|
||||
|
||||
encrypt_then_mac (channel->key, 16, buf, buf_offset, &frame.payload[offset], &olen);
|
||||
|
||||
frame.payloadLen = olen + 1; // +1 for channelHash
|
||||
|
||||
channel->timestamp = timestamp;
|
||||
|
||||
LoRaTransmit (&frame);
|
||||
}
|
||||
|
||||
void makeSendGroupMessage (char *txt, uint8_t keyIndex) {
|
||||
GroupTextMessage msg;
|
||||
strcpy ((char *)msg.text, persistent.nodeName);
|
||||
strcat ((char *)msg.text, ": ");
|
||||
strcat ((char *)msg.text, txt);
|
||||
msg.keyIndex = keyIndex;
|
||||
sendGroupMessage (&msg);
|
||||
return;
|
||||
}
|
||||
|
||||
void decodeGroupMessage (const FrameStruct *frame) {
|
||||
GroupTextMessage msg;
|
||||
memset (&msg, 0, sizeof (msg));
|
||||
if ((frame->header & PAYLOAD_TYPE_MASK) != PAYLOAD_TYPE_GRP_TXT) {
|
||||
MESH_LOGW (TAG, "Not a group text");
|
||||
return;
|
||||
}
|
||||
unsigned char index = 0;
|
||||
msg.channelHash = frame->payload[index++];
|
||||
unsigned char tmp[184];
|
||||
|
||||
|
||||
Channel *channel = NULL;
|
||||
|
||||
uint8_t i = 0;
|
||||
do {
|
||||
channel = getChannel (msg.channelHash, i);
|
||||
if (channel == NULL) {
|
||||
MESH_LOGW (TAG, "Channel hash %d not found", msg.channelHash);
|
||||
return;
|
||||
}
|
||||
if (mac_then_decrypt (channel->key, 16, frame->payload + index, frame->payloadLen - index, tmp) != 0) {
|
||||
MESH_LOGW (TAG, "HMAC failed on grouphash key %d", msg.channelHash);
|
||||
i++;
|
||||
continue;
|
||||
} else {
|
||||
unsigned char plaintextLen = frame->payloadLen - index;
|
||||
index = 0;
|
||||
|
||||
memcpy (&msg.timestamp, tmp + index, 4);
|
||||
index += 4;
|
||||
msg.flags = tmp[index++];
|
||||
|
||||
memcpy (msg.text, tmp + index, plaintextLen - index);
|
||||
|
||||
channel->timestamp = RTC_GetCounter();
|
||||
|
||||
iprintf ("Message from channel %s: %s\n", channel->name, msg.text);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
} while (channel != NULL);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef GROUP_HEADER
|
||||
#define GROUP_HEADER
|
||||
|
||||
#include "stdint.h"
|
||||
#include "meshcore/packetstructs.h"
|
||||
|
||||
void sendGroupMessage (const GroupTextMessage *msg);
|
||||
|
||||
void makeSendGroupMessage (char *txt, uint8_t keyIndex);
|
||||
|
||||
void decodeGroupMessage (const FrameStruct *frame);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,4 @@
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "multipart.h"
|
||||
|
||||
#define TAG "Multipart"
|
||||
@@ -0,0 +1,4 @@
|
||||
#ifndef MULTIPART_HEADER
|
||||
#define MULTIPART_HEADER
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,4 @@
|
||||
#include "meshcore/packetstructs.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define TAG "Trace"
|
||||
@@ -0,0 +1,5 @@
|
||||
#ifndef TRACE_HEADER
|
||||
#define TRACE_HEADER
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,248 @@
|
||||
#ifndef PACKETSTRUCTS_FILE
|
||||
#define PACKETSTRUCTS_FILE
|
||||
|
||||
#include <stdint.h>
|
||||
#include "stddef.h"
|
||||
#define ROUTE_TYPE_MASK 0x03
|
||||
#define PAYLOAD_TYPE_MASK 0x3C
|
||||
#define PAYLOAD_VERSION_MASK 0xC0
|
||||
|
||||
#define DONT_RETRANSMIT_HEADER 0xFF
|
||||
|
||||
#define RESP_SERVER_LOGIN_OK 0
|
||||
|
||||
#define FIRMWARE_VER_LEVEL 1
|
||||
|
||||
#define PERM_ACL_ROLE_MASK 3 // lower 2 bits
|
||||
#define PERM_ACL_GUEST 0
|
||||
#define PERM_ACL_READ_ONLY 1
|
||||
#define PERM_ACL_READ_WRITE 2
|
||||
#define PERM_ACL_ADMIN 3
|
||||
|
||||
#define NODE_ENTRY_FAV_FLAG (1 << 0)
|
||||
|
||||
|
||||
#define MAX_PATH_LEN 64
|
||||
|
||||
|
||||
#define TXT_TYPE_PLAIN 0 // a plain text message
|
||||
#define TXT_TYPE_CLI_DATA 1 // a CLI command
|
||||
#define TXT_TYPE_SIGNED_PLAIN 2 // plain text, signed by sender
|
||||
|
||||
typedef enum RouteType {
|
||||
ROUTE_TYPE_TRANSPORT_FLOOD = 0x00,
|
||||
ROUTE_TYPE_FLOOD = 0x01,
|
||||
ROUTE_TYPE_DIRECT = 0x02,
|
||||
ROUTE_TYPE_TRANSPORT_DIRECT = 0x03,
|
||||
} RouteType;
|
||||
|
||||
typedef enum PayloadType {
|
||||
PAYLOAD_TYPE_REQ = 0x00 << 2,
|
||||
PAYLOAD_TYPE_RESPONSE = 0x01 << 2,
|
||||
PAYLOAD_TYPE_TXT_MSG = 0x02 << 2,
|
||||
PAYLOAD_TYPE_ACK = 0x03 << 2,
|
||||
PAYLOAD_TYPE_ADVERT = 0x04 << 2,
|
||||
PAYLOAD_TYPE_GRP_TXT = 0x05 << 2,
|
||||
PAYLOAD_TYPE_GRP_DATA = 0x06 << 2,
|
||||
PAYLOAD_TYPE_ANON_REQ = 0x07 << 2,
|
||||
PAYLOAD_TYPE_PATH = 0x08 << 2,
|
||||
PAYLOAD_TYPE_TRACE = 0x09 << 2,
|
||||
PAYLOAD_TYPE_MULTIPART = 0x0A << 2,
|
||||
PAYLOAD_TYPE_CONTROL = 0x0B << 2,
|
||||
PAYLOAD_TYPE_RAW_CUSTOM = 0x0F << 2,
|
||||
} PayloadType;
|
||||
|
||||
typedef struct DiscoverRequestPayload {
|
||||
uint8_t prefixOnly;
|
||||
uint8_t typeFilter;
|
||||
uint32_t tag;
|
||||
uint32_t since;
|
||||
} DiscoverRequestPayload;
|
||||
|
||||
typedef struct DiscoverResponsePayload {
|
||||
uint8_t nodeType;
|
||||
int8_t snr;
|
||||
uint32_t tag;
|
||||
uint8_t pubkey[32];
|
||||
uint8_t pubkeyLen;
|
||||
} DiscoverResponsePayload;
|
||||
|
||||
typedef struct AnonymousRequestPayload {
|
||||
uint8_t destinationHash;
|
||||
uint8_t pubKey[32];
|
||||
uint16_t cipherMAC;
|
||||
uint8_t payloadLen;
|
||||
uint8_t payload[149];
|
||||
} AnonymousRequestPayload;
|
||||
|
||||
typedef enum PayloadVersion {
|
||||
PAYLOAD_VERSION_0 = 0 << 6,
|
||||
PAYLOAD_VERSION_1 = 1 << 6,
|
||||
PAYLOAD_VERSION_2 = 2 << 6,
|
||||
PAYLOAD_VERSION_3 = 3 << 6,
|
||||
} PayloadVersion;
|
||||
|
||||
typedef struct Path {
|
||||
uint8_t pathLen;
|
||||
uint8_t path[MAX_PATH_LEN];
|
||||
} Path;
|
||||
|
||||
typedef struct FrameStruct {
|
||||
uint8_t header;
|
||||
uint8_t transportCodes[4];
|
||||
Path path;
|
||||
size_t payloadLen;
|
||||
uint8_t payload[184];
|
||||
} FrameStruct;
|
||||
|
||||
typedef enum RequestType {
|
||||
REQUEST_GET_STATS = 0x01,
|
||||
REQUEST_KEEPALIVE = 0x02,
|
||||
REQUEST_GET_TELEMETRY_DATA = 0x03,
|
||||
REQUEST_GET_MIN_MAX_AVG = 0x04,
|
||||
REQUEST_GET_ACCESS_LIST = 0x05,
|
||||
} RequestType;
|
||||
|
||||
typedef struct Request {
|
||||
uint32_t timestamp;
|
||||
uint8_t requestType;
|
||||
uint8_t dataLen;
|
||||
uint8_t data[180]; // hopefully correct len
|
||||
} Request;
|
||||
|
||||
typedef struct Response {
|
||||
uint32_t tag; // sender timestamp
|
||||
uint8_t dataLen;
|
||||
uint8_t data[180]; // hopefully correct len
|
||||
} Response;
|
||||
|
||||
typedef struct Node {
|
||||
uint8_t pubKey[32];
|
||||
char name[32];
|
||||
int32_t latitude;
|
||||
int32_t longitude;
|
||||
int32_t lastSeen;
|
||||
char flags;
|
||||
} Node;
|
||||
|
||||
typedef struct {
|
||||
char name[32];
|
||||
unsigned char pubKey[32];
|
||||
unsigned char secret[32];
|
||||
|
||||
int32_t gps_latitude;
|
||||
int32_t gps_longitude;
|
||||
|
||||
Path path;
|
||||
|
||||
uint8_t flags;
|
||||
uint8_t type;
|
||||
|
||||
uint8_t authenticated;
|
||||
|
||||
uint32_t last_seen_rt; // remote timestamp
|
||||
uint32_t last_seen_lt; // local timestamp
|
||||
|
||||
uint32_t sync_timestamp;
|
||||
} NodeEntry;
|
||||
|
||||
typedef struct EncryptedPayloadStruct {
|
||||
uint8_t destinationHash;
|
||||
uint8_t sourceHash;
|
||||
uint16_t cipherMAC;
|
||||
uint8_t payloadLen;
|
||||
uint8_t payload[180];
|
||||
uint8_t type;
|
||||
NodeEntry *remNode;
|
||||
const Path *path;
|
||||
const FrameStruct *origFrame;
|
||||
} EncryptedPayloadStruct;
|
||||
|
||||
typedef enum NodeType {
|
||||
NODE_TYPE_CHAT_NODE = 1,
|
||||
NODE_TYPE_REPEATER = 2,
|
||||
NODE_TYPE_ROOM_SERVER = 3,
|
||||
NODE_TYPE_SENSOR = 4
|
||||
} NodeType;
|
||||
|
||||
typedef enum ControlDataFlags {
|
||||
CONTROL_DATA_FLAG_TYPE_NODE_DISCOVER_REQ = 0x80,
|
||||
CONTROL_DATA_FLAG_DISCOVER_RESP = 0x90
|
||||
} ControlDataFlags;
|
||||
|
||||
typedef enum AdvertisementPayloadFlags {
|
||||
ADVERTISEMENT_FLAG_IS_CHAT_NODE = 0x01,
|
||||
ADVERTISEMENT_FLAG_IS_REAPEATER = 0x02,
|
||||
ADVERTISEMENT_FLAG_IS_ROOM_SERVER = 0x03,
|
||||
ADVERTISEMENT_FLAG_IS_SENSOR = 0x04,
|
||||
ADVERTISEMENT_FLAG_HAS_LOCATION = 0x10,
|
||||
ADVERTISEMENT_FLAG_RFU1 = 0x20,
|
||||
ADVERTISEMENT_FLAG_RFU2 = 0x40,
|
||||
ADVERTISEMENT_FLAG_HAS_NAME = 0x80,
|
||||
} AdvertisementPayloadFlags;
|
||||
|
||||
typedef struct AdvertisementPayload {
|
||||
uint8_t pubKey[32];
|
||||
int32_t timestamp;
|
||||
uint8_t signature[64];
|
||||
uint8_t valid;
|
||||
uint8_t dataFlags;
|
||||
int32_t latitude;
|
||||
int32_t longitude;
|
||||
int16_t rfu1;
|
||||
int16_t rfu2;
|
||||
char nodeName[32];
|
||||
|
||||
} AdvertisementPayload;
|
||||
|
||||
typedef struct Extra {
|
||||
uint8_t type;
|
||||
uint8_t dataLen;
|
||||
uint8_t data[180]; // hopefully long enough
|
||||
} Extra;
|
||||
|
||||
typedef struct ReturnedPathPayload {
|
||||
Path path;
|
||||
Extra extra;
|
||||
} ReturnedPathPayload;
|
||||
|
||||
typedef struct PlainTextMessagePayload {
|
||||
uint32_t timestamp;
|
||||
uint8_t textType;
|
||||
uint8_t attempt;
|
||||
uint8_t message[180]; // hopefully long enough
|
||||
} PlainTextMessagePayload;
|
||||
|
||||
typedef struct GroupTextMessage {
|
||||
uint8_t channelHash;
|
||||
uint8_t keyIndex;
|
||||
int32_t timestamp;
|
||||
uint8_t flags;
|
||||
uint8_t text[190];
|
||||
} GroupTextMessage;
|
||||
|
||||
typedef struct RepeaterStats {
|
||||
uint16_t millivolts;
|
||||
uint16_t txQueueLength;
|
||||
int16_t noiseFloor;
|
||||
int16_t lastRSSI;
|
||||
uint32_t packetsReceivedCount;
|
||||
uint32_t packetsSentCount;
|
||||
uint32_t totalAirTimeSeconds;
|
||||
uint32_t totalUpTimeSeconds;
|
||||
uint32_t sentFloodCount, sentDirectCount;
|
||||
uint32_t receivedFloodCount, receivedDirectCount;
|
||||
uint16_t err_events; // was 'n_full_events'
|
||||
int16_t lastSNR; // x 4
|
||||
uint16_t n_direct_dups, n_flood_dups;
|
||||
uint32_t total_rx_air_time_secs;
|
||||
} RepeaterStats;
|
||||
|
||||
typedef struct Channel {
|
||||
char name[32];
|
||||
uint8_t key[16];
|
||||
uint8_t hash;
|
||||
int32_t timestamp;
|
||||
} Channel;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "stats.h"
|
||||
|
||||
RepeaterStats stats;
|
||||
|
||||
uint32_t startupTime = 0;
|
||||
uint32_t tickAirtime = 0;
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef STATS_HEADER
|
||||
#define STATS_HEADER
|
||||
#include "meshcore/packetstructs.h"
|
||||
|
||||
extern RepeaterStats stats;
|
||||
|
||||
extern uint32_t startupTime;
|
||||
|
||||
extern uint32_t tickAirtime;
|
||||
|
||||
#endif
|
||||
+991
@@ -0,0 +1,991 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "sx1262.h"
|
||||
#include "ch32v20x_gpio.h"
|
||||
#include "util/log.h"
|
||||
#define TAG "SX1262"
|
||||
|
||||
// Global Stuff
|
||||
uint8_t PacketParams[6];
|
||||
char txActive;
|
||||
int txLost = 0;
|
||||
char debugPrint;
|
||||
|
||||
// Arduino compatible macros
|
||||
#define delayMicroseconds(us) esp_rom_delay_us (us)
|
||||
#define delay(ms) esp_rom_delay_us (ms * 1000)
|
||||
|
||||
void LoRaError (int error) {
|
||||
if (debugPrint) {
|
||||
MESH_LOGE (TAG, "LoRaErrorDefault=%d", error);
|
||||
}
|
||||
while (1) {
|
||||
iprintf("GONE WRONG\n");
|
||||
Delay_Ms(1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Convert bandwidth constant to float in kHz
|
||||
float loraBwToFloat(uint8_t bw) {
|
||||
switch (bw) {
|
||||
case SX126X_LORA_BW_7_8: return 7.8f;
|
||||
case SX126X_LORA_BW_10_4: return 10.4f;
|
||||
case SX126X_LORA_BW_15_6: return 15.6f;
|
||||
case SX126X_LORA_BW_20_8: return 20.8f;
|
||||
case SX126X_LORA_BW_31_25: return 31.25f;
|
||||
case SX126X_LORA_BW_41_7: return 41.7f;
|
||||
case SX126X_LORA_BW_62_5: return 62.5f;
|
||||
case SX126X_LORA_BW_125_0: return 125.0f;
|
||||
case SX126X_LORA_BW_250_0: return 250.0f;
|
||||
case SX126X_LORA_BW_500_0: return 500.0f;
|
||||
default: return 0.0f; // unknown
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
// Convert float bandwidth (kHz) to constant
|
||||
uint8_t floatToLoraBw(float bw) {
|
||||
if (bw == 7.8f) return SX126X_LORA_BW_7_8;
|
||||
if (bw == 10.4f) return SX126X_LORA_BW_10_4;
|
||||
if (bw == 15.6f) return SX126X_LORA_BW_15_6;
|
||||
if (bw == 20.8f) return SX126X_LORA_BW_20_8;
|
||||
if (bw == 31.25f) return SX126X_LORA_BW_31_25;
|
||||
if (bw == 41.7f) return SX126X_LORA_BW_41_7;
|
||||
if (bw == 62.5f) return SX126X_LORA_BW_62_5;
|
||||
if (bw == 125.0f) return SX126X_LORA_BW_125_0;
|
||||
if (bw == 250.0f) return SX126X_LORA_BW_250_0;
|
||||
if (bw == 500.0f) return SX126X_LORA_BW_500_0;
|
||||
return 0xFF; // invalid
|
||||
}
|
||||
*/
|
||||
/*
|
||||
// Convert coding rate constant to float
|
||||
float loraCrToFloat(uint8_t cr) {
|
||||
switch (cr) {
|
||||
case SX126X_LORA_CR_4_5: return 4.0f / 5.0f;
|
||||
case SX126X_LORA_CR_4_6: return 4.0f / 6.0f;
|
||||
case SX126X_LORA_CR_4_7: return 4.0f / 7.0f;
|
||||
case SX126X_LORA_CR_4_8: return 4.0f / 8.0f;
|
||||
default: return 0.0f;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
// Convert float coding rate to constant
|
||||
uint8_t floatToLoraCr(float cr) {
|
||||
if (cr == 4.0f / 5.0f) return SX126X_LORA_CR_4_5;
|
||||
if (cr == 4.0f / 6.0f) return SX126X_LORA_CR_4_6;
|
||||
if (cr == 4.0f / 7.0f) return SX126X_LORA_CR_4_7;
|
||||
if (cr == 4.0f / 8.0f) return SX126X_LORA_CR_4_8;
|
||||
return 0xFF;
|
||||
}
|
||||
*/
|
||||
/*
|
||||
// Convert MHz to Hz with limit check
|
||||
uint32_t mhzToHzLimited(double freqMHz) {
|
||||
double freqHz = freqMHz * 1000000.0;
|
||||
if (freqHz > 860000000.0 && freqHz < 880000000.0) {
|
||||
return (uint32_t)freqHz;
|
||||
}
|
||||
return 0; // out of range
|
||||
}
|
||||
*/
|
||||
|
||||
void LoRaInit (void) {
|
||||
|
||||
txActive = 0;
|
||||
debugPrint = 0;
|
||||
|
||||
GPIO_InitTypeDef GPIO_InitStructure = {0};
|
||||
SPI_InitTypeDef SPI_InitStructure = {0};
|
||||
|
||||
|
||||
// RCC_AHBPeriphClockCmd (RCC_AHBPeriph_SDIO, ENABLE);
|
||||
RCC_APB1PeriphClockCmd (RCC_APB1Periph_USART2, ENABLE);
|
||||
|
||||
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_SPI1, ENABLE);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //nss
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init (GPIOA, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //sck
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init (GPIOA, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //miso
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||||
GPIO_Init (GPIOA, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //mosi
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init (GPIOA, &GPIO_InitStructure);
|
||||
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
GPIO_Init (GPIOB, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; //busy
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||||
GPIO_Init (GPIOB, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||||
GPIO_Init (GPIOB, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //reset lora
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
GPIO_Init (GPIOB, &GPIO_InitStructure);
|
||||
|
||||
|
||||
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
|
||||
|
||||
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
|
||||
|
||||
|
||||
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
|
||||
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
|
||||
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
|
||||
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
|
||||
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
|
||||
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
|
||||
SPI_InitStructure.SPI_CRCPolynomial = 7;
|
||||
SPI_Init (SPI1, &SPI_InitStructure);
|
||||
|
||||
GPIO_WriteBit (GPIOB, GPIO_Pin_0, 1);
|
||||
|
||||
SPI_Cmd (SPI1, ENABLE);
|
||||
}
|
||||
|
||||
void spi_write_byte (uint8_t *DataOut, size_t DataLength) {
|
||||
spi_read_byte (NULL, DataOut, DataLength);
|
||||
}
|
||||
|
||||
// Full-duplex transfer: TX and RX buffers
|
||||
void spi_read_byte (uint8_t *rx, const uint8_t *tx, size_t len) {
|
||||
GPIO_WriteBit (GPIOA, GPIO_Pin_4, 0);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint8_t out = tx ? tx[i] : 0xFF;
|
||||
|
||||
// Wait TX ready
|
||||
while (SPI_I2S_GetFlagStatus (SPI1, SPI_I2S_FLAG_TXE) == RESET);
|
||||
if (debugPrint) {
|
||||
MESH_LOGV (TAG, "Sent %02X over spi.\n", out);
|
||||
}
|
||||
SPI_I2S_SendData (SPI1, out);
|
||||
// Wait RX ready
|
||||
|
||||
while (SPI_I2S_GetFlagStatus (SPI1, SPI_I2S_FLAG_RXNE) == RESET);
|
||||
|
||||
uint8_t in = (uint8_t)SPI_I2S_ReceiveData (SPI1);
|
||||
if (debugPrint) {
|
||||
MESH_LOGV (TAG, "Got %02X over spi.\n", in);
|
||||
}
|
||||
if (rx)
|
||||
rx[i] = in;
|
||||
}
|
||||
GPIO_WriteBit (GPIOA, GPIO_Pin_4, 1);
|
||||
}
|
||||
|
||||
// Full-duplex transfer: TX and RX buffers
|
||||
void spi_read_byte_offs (uint8_t *rx, const uint8_t *tx, size_t len, uint8_t off) {
|
||||
GPIO_WriteBit (GPIOA, GPIO_Pin_4, 0);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint8_t out = 0xFF;
|
||||
if (i < off) {
|
||||
out = tx ? tx[i] : 0xFF;
|
||||
}
|
||||
|
||||
// Wait TX ready
|
||||
while (SPI_I2S_GetFlagStatus (SPI1, SPI_I2S_FLAG_TXE) == RESET);
|
||||
if (debugPrint) {
|
||||
MESH_LOGV (TAG, "Sent %02X over spi.\n", out);
|
||||
}
|
||||
SPI_I2S_SendData (SPI1, out);
|
||||
// Wait RX ready
|
||||
|
||||
while (SPI_I2S_GetFlagStatus (SPI1, SPI_I2S_FLAG_RXNE) == RESET);
|
||||
|
||||
uint8_t in = (uint8_t)SPI_I2S_ReceiveData (SPI1);
|
||||
if (debugPrint) {
|
||||
MESH_LOGV (TAG, "Got %02X over spi.\n", in);
|
||||
}
|
||||
if (rx && i >= off) {
|
||||
rx[i - off] = in;
|
||||
}
|
||||
}
|
||||
GPIO_WriteBit (GPIOA, GPIO_Pin_4, 1);
|
||||
}
|
||||
|
||||
int16_t LoRaBegin (uint32_t frequencyInHz, int8_t txPowerInDbm, uint16_t tcxoVoltage, char useRegulatorLDO) {
|
||||
if (txPowerInDbm > 22) {
|
||||
txPowerInDbm = 22;
|
||||
}
|
||||
if (txPowerInDbm < -3) {
|
||||
txPowerInDbm = -3;
|
||||
}
|
||||
|
||||
ResetLora();
|
||||
|
||||
uint8_t wk[2];
|
||||
ReadRegister (SX126X_REG_LORA_SYNC_WORD_MSB, wk, 2); // 0x0740
|
||||
uint16_t syncWord = (wk[0] << 8) + wk[1];
|
||||
MESH_LOGI (TAG, "syncWord=0x%x", syncWord);
|
||||
if (syncWord != SX126X_SYNC_WORD_PUBLIC && syncWord != SX126X_SYNC_WORD_PRIVATE) {
|
||||
MESH_LOGE (TAG, "SX126x error, maybe no SPI connection");
|
||||
return ERR_INVALID_MODE;
|
||||
}
|
||||
|
||||
MESH_LOGI (TAG, "SX126x installed");
|
||||
SetStandby (SX126X_STANDBY_RC);
|
||||
|
||||
SetDio2AsRfSwitchCtrl (1);
|
||||
//MESH_LOGI (TAG, "tcxoVoltage=%f", tcxoVoltage);
|
||||
// set TCXO control, if requested
|
||||
if (tcxoVoltage > 0) {
|
||||
SetDio3AsTcxoCtrl (tcxoVoltage, RADIO_TCXO_SETUP_TIME); // Configure the radio to use a TCXO controlled by DIO3
|
||||
}
|
||||
|
||||
Calibrate (SX126X_CALIBRATE_IMAGE_ON | SX126X_CALIBRATE_ADC_BULK_P_ON | SX126X_CALIBRATE_ADC_BULK_N_ON | SX126X_CALIBRATE_ADC_PULSE_ON | SX126X_CALIBRATE_PLL_ON | SX126X_CALIBRATE_RC13M_ON | SX126X_CALIBRATE_RC64K_ON);
|
||||
|
||||
MESH_LOGI (TAG, "useRegulatorLDO=%d", useRegulatorLDO);
|
||||
if (useRegulatorLDO) {
|
||||
SetRegulatorMode (SX126X_REGULATOR_LDO); // set regulator mode: LDO
|
||||
} else {
|
||||
SetRegulatorMode (SX126X_REGULATOR_DC_DC); // set regulator mode: DC-DC
|
||||
}
|
||||
|
||||
SetBufferBaseAddress (0, 0);
|
||||
SetPaConfig (0x04, 0x07, 0x00, 0x01); // PA Optimal Settings +22 dBm
|
||||
SetOvercurrentProtection (60); // current max 60mA for the whole device
|
||||
SetPowerConfig (txPowerInDbm, SX126X_PA_RAMP_200U); // 0 fuer Empfaenger
|
||||
SetRfFrequency (frequencyInHz);
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
void FixInvertedIQ (uint8_t iqConfig) {
|
||||
// fixes IQ configuration for inverted IQ
|
||||
// see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.4 for details
|
||||
// When exchanging LoRa packets with inverted IQ polarity, some packet losses may be observed for longer packets.
|
||||
// Workaround: Bit 2 at address 0x0736 must be set to:
|
||||
// “0” when using inverted IQ polarity (see the SetPacketParam(...) command)
|
||||
// “1” when using standard IQ polarity
|
||||
|
||||
// read current IQ configuration
|
||||
uint8_t iqConfigCurrent = 0;
|
||||
ReadRegister (SX126X_REG_IQ_POLARITY_SETUP, &iqConfigCurrent, 1); // 0x0736
|
||||
|
||||
// set correct IQ configuration
|
||||
// if(iqConfig == SX126X_LORA_IQ_STANDARD) {
|
||||
if (iqConfig == SX126X_LORA_IQ_INVERTED) {
|
||||
iqConfigCurrent &= 0xFB; // using inverted IQ polarity
|
||||
} else {
|
||||
iqConfigCurrent |= 0x04; // using standard IQ polarity
|
||||
}
|
||||
|
||||
// update with the new value
|
||||
WriteRegister (SX126X_REG_IQ_POLARITY_SETUP, &iqConfigCurrent, 1); // 0x0736
|
||||
}
|
||||
|
||||
void LoRaConfig (uint8_t spreadingFactor, uint8_t bandwidth, uint8_t codingRate, uint16_t preambleLength, uint8_t payloadLen, char crcOn, char invertIrq) {
|
||||
SetStopRxTimerOnPreambleDetect (0);
|
||||
SetLoRaSymbNumTimeout (0);
|
||||
SetPacketType (SX126X_PACKET_TYPE_LORA); // SX126x.ModulationParams.PacketType : MODEM_LORA
|
||||
uint8_t ldro = 0; // LowDataRateOptimize OFF
|
||||
SetModulationParams (spreadingFactor, bandwidth, codingRate, ldro);
|
||||
|
||||
PacketParams[0] = (preambleLength >> 8) & 0xFF;
|
||||
PacketParams[1] = preambleLength;
|
||||
if (payloadLen) {
|
||||
PacketParams[2] = 0x01; // Fixed length packet (implicit header)
|
||||
PacketParams[3] = payloadLen;
|
||||
} else {
|
||||
PacketParams[2] = 0x00; // Variable length packet (explicit header)
|
||||
PacketParams[3] = 0xFF;
|
||||
}
|
||||
|
||||
if (crcOn)
|
||||
PacketParams[4] = SX126X_LORA_CRC_ON;
|
||||
else
|
||||
PacketParams[4] = SX126X_LORA_CRC_OFF;
|
||||
|
||||
if (invertIrq)
|
||||
PacketParams[5] = 0x01; // Inverted LoRa I and Q signals setup
|
||||
else
|
||||
PacketParams[5] = 0x00; // Standard LoRa I and Q signals setup
|
||||
|
||||
// fixes IQ configuration for inverted IQ
|
||||
FixInvertedIQ (PacketParams[5]);
|
||||
|
||||
WriteCommand (SX126X_CMD_SET_PACKET_PARAMS, PacketParams, 6); // 0x8C
|
||||
|
||||
// Do not use DIO interruptst
|
||||
SetDioIrqParams (SX126X_IRQ_ALL, // all interrupts enabled
|
||||
SX126X_IRQ_NONE, // interrupts on DIO1
|
||||
SX126X_IRQ_NONE, // interrupts on DIO2
|
||||
SX126X_IRQ_NONE // interrupts on DIO3
|
||||
);
|
||||
|
||||
|
||||
MESH_LOGI (TAG, "Almost done setting LoRa");
|
||||
// Receive state no receive timeoout
|
||||
SetRx (0xFFFFFF);
|
||||
}
|
||||
|
||||
void LoRaDebugPrint (char enable) {
|
||||
debugPrint = enable;
|
||||
}
|
||||
|
||||
uint8_t LoRaReceive (uint8_t *pData, int16_t len) {
|
||||
uint8_t rxLen = 0;
|
||||
uint16_t irqRegs = GetIrqStatus();
|
||||
// uint8_t status = GetStatus();
|
||||
|
||||
if (irqRegs & SX126X_IRQ_RX_DONE) {
|
||||
// ClearIrqStatus(SX126X_IRQ_RX_DONE);
|
||||
ClearIrqStatus (SX126X_IRQ_ALL);
|
||||
rxLen = ReadBuffer (pData, len);
|
||||
}
|
||||
|
||||
return rxLen;
|
||||
}
|
||||
|
||||
char LoRaSend (uint8_t *pData, int16_t len, uint8_t mode) {
|
||||
uint16_t irqStatus;
|
||||
char rv = 0;
|
||||
|
||||
if (txActive == 0) {
|
||||
txActive = 1;
|
||||
if (PacketParams[2] == 0x00) { // Variable length packet (explicit header)
|
||||
PacketParams[3] = len;
|
||||
}
|
||||
WriteCommand (SX126X_CMD_SET_PACKET_PARAMS, PacketParams, 6); // 0x8C
|
||||
|
||||
// ClearIrqStatus(SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT);
|
||||
ClearIrqStatus (SX126X_IRQ_ALL);
|
||||
|
||||
WriteBuffer (pData, len);
|
||||
SetTx (5000);
|
||||
|
||||
if (mode & SX126x_TXMODE_SYNC) {
|
||||
irqStatus = GetIrqStatus();
|
||||
while ((!(irqStatus & SX126X_IRQ_TX_DONE)) && (!(irqStatus & SX126X_IRQ_TIMEOUT))) {
|
||||
Delay_Ms(1);
|
||||
irqStatus = GetIrqStatus();
|
||||
}
|
||||
if (debugPrint) {
|
||||
MESH_LOGI (TAG, "irqStatus=0x%x", irqStatus);
|
||||
if (irqStatus & SX126X_IRQ_TX_DONE) {
|
||||
MESH_LOGI (TAG, "SX126X_IRQ_TX_DONE");
|
||||
}
|
||||
if (irqStatus & SX126X_IRQ_TIMEOUT) {
|
||||
MESH_LOGI (TAG, "SX126X_IRQ_TIMEOUT");
|
||||
}
|
||||
}
|
||||
txActive = 0;
|
||||
|
||||
SetRx (0xFFFFFF);
|
||||
|
||||
if (irqStatus & SX126X_IRQ_TX_DONE) {
|
||||
rv = 1;
|
||||
}
|
||||
} else {
|
||||
rv = 1;
|
||||
}
|
||||
}
|
||||
if (debugPrint) {
|
||||
MESH_LOGI (TAG, "Send rv=0x%x", rv);
|
||||
}
|
||||
if (rv == 0)
|
||||
txLost++;
|
||||
return rv;
|
||||
}
|
||||
|
||||
char ReceiveMode (void) {
|
||||
uint16_t irq;
|
||||
char rv = 0;
|
||||
|
||||
if (txActive == 0) {
|
||||
rv = 1;
|
||||
} else {
|
||||
irq = GetIrqStatus();
|
||||
if (irq & (SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT)) {
|
||||
SetRx (0xFFFFFF);
|
||||
txActive = 0;
|
||||
rv = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void GetPacketStatus (int8_t *rssiPacket, int8_t *snrPacket, int8_t *rawSnr) {
|
||||
uint8_t buf[4];
|
||||
ReadCommand (SX126X_CMD_GET_PACKET_STATUS, buf, 4); // 0x14
|
||||
*rssiPacket = (buf[3] >> 1) * -1;
|
||||
(buf[2] < 128) ? (*snrPacket = buf[2] >> 2) : (*snrPacket = ((buf[2] - 256) >> 2));
|
||||
*rawSnr = buf[2];
|
||||
}
|
||||
|
||||
void SetTxPower (int8_t txPowerInDbm) {
|
||||
SetPowerConfig (txPowerInDbm, SX126X_PA_RAMP_200U);
|
||||
}
|
||||
|
||||
void ResetLora (void) {
|
||||
Delay_Ms(10);
|
||||
GPIO_WriteBit (GPIOB, GPIO_Pin_0, 0);
|
||||
Delay_Ms(20);
|
||||
GPIO_WriteBit (GPIOB, GPIO_Pin_0, 1);
|
||||
MESH_LOGW (TAG, "Waiting for idle");
|
||||
Delay_Ms(10);
|
||||
// ensure BUSY is low (state meachine ready)
|
||||
WaitForIdle (BUSY_WAIT, "Reset", 1);
|
||||
}
|
||||
|
||||
void Wakeup (void) {
|
||||
GetStatus();
|
||||
}
|
||||
|
||||
void SetStandby (uint8_t mode) {
|
||||
uint8_t data = mode;
|
||||
WriteCommand (SX126X_CMD_SET_STANDBY, &data, 1); // 0x80
|
||||
}
|
||||
|
||||
uint8_t GetStatus (void) {
|
||||
uint8_t rv;
|
||||
ReadCommand (SX126X_CMD_GET_STATUS, &rv, 1); // 0xC0
|
||||
return rv;
|
||||
}
|
||||
|
||||
static uint8_t voltage_to_tcxo(uint16_t voltage)
|
||||
{
|
||||
if (voltage == 1650) return SX126X_DIO3_OUTPUT_1_6;
|
||||
if (voltage == 1750) return SX126X_DIO3_OUTPUT_1_7;
|
||||
if (voltage == 1850) return SX126X_DIO3_OUTPUT_1_8;
|
||||
if (voltage == 2300) return SX126X_DIO3_OUTPUT_2_2;
|
||||
if (voltage == 2500) return SX126X_DIO3_OUTPUT_2_4;
|
||||
if (voltage == 2850) return SX126X_DIO3_OUTPUT_2_7;
|
||||
if (voltage == 3100) return SX126X_DIO3_OUTPUT_3_0;
|
||||
return SX126X_DIO3_OUTPUT_3_3;
|
||||
}
|
||||
|
||||
void SetDio3AsTcxoCtrl (uint16_t voltage, uint32_t delay) {
|
||||
uint8_t buf[4];
|
||||
|
||||
// buf[0] = tcxoVoltage & 0x07;
|
||||
buf[0] = voltage_to_tcxo(voltage);
|
||||
|
||||
uint32_t delayValue = (delay * 1000) / 15625;
|
||||
|
||||
buf[1] = (delayValue >> 16) & 0xFF;
|
||||
buf[2] = (delayValue >> 8) & 0xFF;
|
||||
buf[3] = delayValue & 0xFF;
|
||||
|
||||
WriteCommand (SX126X_CMD_SET_DIO3_AS_TCXO_CTRL, buf, 4); // 0x97
|
||||
}
|
||||
|
||||
void Calibrate (uint8_t calibParam) {
|
||||
uint8_t data = calibParam;
|
||||
WriteCommand (SX126X_CMD_CALIBRATE, &data, 1); // 0x89
|
||||
}
|
||||
void SetDio2AsRfSwitchCtrl (uint8_t enable) {
|
||||
uint8_t data = enable;
|
||||
WriteCommand (SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL, &data, 1); // 0x9D
|
||||
}
|
||||
|
||||
void SetRfFrequency (uint32_t frequency) {
|
||||
uint8_t buf[4];
|
||||
|
||||
CalibrateImage (frequency);
|
||||
|
||||
//freq = (uint32_t)((double)frequency / (double)FREQ_STEP);
|
||||
uint32_t freq = ((uint64_t)frequency << 25) / XTAL_FREQ;
|
||||
buf[0] = (uint8_t)((freq >> 24) & 0xFF);
|
||||
buf[1] = (uint8_t)((freq >> 16) & 0xFF);
|
||||
buf[2] = (uint8_t)((freq >> 8) & 0xFF);
|
||||
buf[3] = (uint8_t)(freq & 0xFF);
|
||||
WriteCommand (SX126X_CMD_SET_RF_FREQUENCY, buf, 4); // 0x86
|
||||
}
|
||||
|
||||
void CalibrateImage (uint32_t frequency) {
|
||||
uint8_t calFreq[2];
|
||||
|
||||
if (frequency > 900000000) {
|
||||
calFreq[0] = 0xE1;
|
||||
calFreq[1] = 0xE9;
|
||||
} else if (frequency > 850000000) {
|
||||
calFreq[0] = 0xD7;
|
||||
calFreq[1] = 0xDB;
|
||||
} else if (frequency > 770000000) {
|
||||
calFreq[0] = 0xC1;
|
||||
calFreq[1] = 0xC5;
|
||||
} else if (frequency > 460000000) {
|
||||
calFreq[0] = 0x75;
|
||||
calFreq[1] = 0x81;
|
||||
} else if (frequency > 425000000) {
|
||||
calFreq[0] = 0x6B;
|
||||
calFreq[1] = 0x6F;
|
||||
}
|
||||
WriteCommand (SX126X_CMD_CALIBRATE_IMAGE, calFreq, 2); // 0x98
|
||||
}
|
||||
|
||||
void SetRegulatorMode (uint8_t mode) {
|
||||
uint8_t data = mode;
|
||||
WriteCommand (SX126X_CMD_SET_REGULATOR_MODE, &data, 1); // 0x96
|
||||
}
|
||||
|
||||
void SetBufferBaseAddress (uint8_t txBaseAddress, uint8_t rxBaseAddress) {
|
||||
uint8_t buf[2];
|
||||
|
||||
buf[0] = txBaseAddress;
|
||||
buf[1] = rxBaseAddress;
|
||||
WriteCommand (SX126X_CMD_SET_BUFFER_BASE_ADDRESS, buf, 2); // 0x8F
|
||||
}
|
||||
|
||||
void SetPowerConfig (int8_t power, uint8_t rampTime) {
|
||||
uint8_t buf[2];
|
||||
|
||||
if (power > 22) {
|
||||
power = 22;
|
||||
} else if (power < -3) {
|
||||
power = -3;
|
||||
}
|
||||
|
||||
buf[0] = power;
|
||||
buf[1] = (uint8_t)rampTime;
|
||||
WriteCommand (SX126X_CMD_SET_TX_PARAMS, buf, 2); // 0x8E
|
||||
}
|
||||
|
||||
void SetPaConfig (uint8_t paDutyCycle, uint8_t hpMax, uint8_t deviceSel, uint8_t paLut) {
|
||||
uint8_t buf[4];
|
||||
|
||||
buf[0] = paDutyCycle;
|
||||
buf[1] = hpMax;
|
||||
buf[2] = deviceSel;
|
||||
buf[3] = paLut;
|
||||
WriteCommand (SX126X_CMD_SET_PA_CONFIG, buf, 4); // 0x95
|
||||
}
|
||||
|
||||
void SetOvercurrentProtection(uint16_t current_mA)
|
||||
{
|
||||
if (current_mA <= 140)
|
||||
{
|
||||
uint8_t buf = (current_mA * 10) / 25;
|
||||
WriteRegister(SX126X_REG_OCP_CONFIGURATION, &buf, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void SetSyncWord (int16_t sync) {
|
||||
uint8_t buf[2];
|
||||
|
||||
buf[0] = (uint8_t)((sync >> 8) & 0x00FF);
|
||||
buf[1] = (uint8_t)(sync & 0x00FF);
|
||||
WriteRegister (SX126X_REG_LORA_SYNC_WORD_MSB, buf, 2); // 0x0740
|
||||
}
|
||||
|
||||
void SetDioIrqParams (uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask) {
|
||||
uint8_t buf[8];
|
||||
|
||||
buf[0] = (uint8_t)((irqMask >> 8) & 0x00FF);
|
||||
buf[1] = (uint8_t)(irqMask & 0x00FF);
|
||||
buf[2] = (uint8_t)((dio1Mask >> 8) & 0x00FF);
|
||||
buf[3] = (uint8_t)(dio1Mask & 0x00FF);
|
||||
buf[4] = (uint8_t)((dio2Mask >> 8) & 0x00FF);
|
||||
buf[5] = (uint8_t)(dio2Mask & 0x00FF);
|
||||
buf[6] = (uint8_t)((dio3Mask >> 8) & 0x00FF);
|
||||
buf[7] = (uint8_t)(dio3Mask & 0x00FF);
|
||||
WriteCommand (SX126X_CMD_SET_DIO_IRQ_PARAMS, buf, 8); // 0x08
|
||||
}
|
||||
|
||||
void SetStopRxTimerOnPreambleDetect (char enable) {
|
||||
MESH_LOGI (TAG, "SetStopRxTimerOnPreambleDetect enable=%d", enable);
|
||||
// uint8_t data = (uint8_t)enable;
|
||||
uint8_t data = 0;
|
||||
if (enable)
|
||||
data = 1;
|
||||
WriteCommand (SX126X_CMD_STOP_TIMER_ON_PREAMBLE, &data, 1); // 0x9F
|
||||
}
|
||||
|
||||
void SetLoRaSymbNumTimeout (uint8_t SymbNum) {
|
||||
uint8_t data = SymbNum;
|
||||
WriteCommand (SX126X_CMD_SET_LORA_SYMB_NUM_TIMEOUT, &data, 1); // 0xA0
|
||||
}
|
||||
|
||||
void SetPacketType (uint8_t packetType) {
|
||||
uint8_t data = packetType;
|
||||
WriteCommand (SX126X_CMD_SET_PACKET_TYPE, &data, 1); // 0x01
|
||||
}
|
||||
|
||||
void SetModulationParams (uint8_t spreadingFactor, uint8_t bandwidth, uint8_t codingRate, uint8_t lowDataRateOptimize) {
|
||||
uint8_t data[4];
|
||||
// currently only LoRa supported
|
||||
data[0] = spreadingFactor;
|
||||
data[1] = bandwidth;
|
||||
data[2] = codingRate;
|
||||
data[3] = lowDataRateOptimize;
|
||||
WriteCommand (SX126X_CMD_SET_MODULATION_PARAMS, data, 4); // 0x8B
|
||||
}
|
||||
|
||||
void SetCadParams (uint8_t cadSymbolNum, uint8_t cadDetPeak, uint8_t cadDetMin, uint8_t cadExitMode, uint32_t cadTimeout) {
|
||||
uint8_t data[7];
|
||||
data[0] = cadSymbolNum;
|
||||
data[1] = cadDetPeak;
|
||||
data[2] = cadDetMin;
|
||||
data[3] = cadExitMode;
|
||||
data[4] = (uint8_t)((cadTimeout >> 16) & 0xFF);
|
||||
data[5] = (uint8_t)((cadTimeout >> 8) & 0xFF);
|
||||
data[6] = (uint8_t)(cadTimeout & 0xFF);
|
||||
WriteCommand (SX126X_CMD_SET_CAD_PARAMS, data, 7); // 0x88
|
||||
}
|
||||
|
||||
void SetCad() {
|
||||
uint8_t data = 0;
|
||||
WriteCommand (SX126X_CMD_SET_CAD, &data, 0); // 0xC5
|
||||
}
|
||||
|
||||
uint16_t GetIrqStatus (void) {
|
||||
uint8_t data[3];
|
||||
ReadCommand (SX126X_CMD_GET_IRQ_STATUS, data, 3); // 0x12
|
||||
return (data[1] << 8) | data[2];
|
||||
}
|
||||
|
||||
void ClearIrqStatus (uint16_t irq) {
|
||||
uint8_t buf[2];
|
||||
|
||||
buf[0] = (uint8_t)(((uint16_t)irq >> 8) & 0x00FF);
|
||||
buf[1] = (uint8_t)((uint16_t)irq & 0x00FF);
|
||||
WriteCommand (SX126X_CMD_CLEAR_IRQ_STATUS, buf, 2); // 0x02
|
||||
}
|
||||
|
||||
void SetRx (uint32_t timeout) {
|
||||
if (debugPrint) {
|
||||
MESH_LOGI (TAG, "----- SetRx timeout=%d", timeout);
|
||||
}
|
||||
SetStandby (SX126X_STANDBY_RC);
|
||||
uint8_t buf[3];
|
||||
buf[0] = (uint8_t)((timeout >> 16) & 0xFF);
|
||||
buf[1] = (uint8_t)((timeout >> 8) & 0xFF);
|
||||
buf[2] = (uint8_t)(timeout & 0xFF);
|
||||
WriteCommand (SX126X_CMD_SET_RX, buf, 3); // 0x82
|
||||
|
||||
for (int retry = 0; retry < 10; retry++) {
|
||||
if ((GetStatus() & 0x70) == 0x50)
|
||||
break;
|
||||
Delay_Ms(1);
|
||||
}
|
||||
if ((GetStatus() & 0x70) != 0x50) {
|
||||
MESH_LOGE (TAG, "SetRx Illegal Status");
|
||||
LoRaError (ERR_INVALID_SETRX_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
void SetTx (uint32_t timeoutInMs) {
|
||||
if (debugPrint) {
|
||||
MESH_LOGI (TAG, "----- SetTx timeoutInMs=%d", timeoutInMs);
|
||||
}
|
||||
SetStandby (SX126X_STANDBY_RC);
|
||||
uint8_t buf[3];
|
||||
uint32_t tout = timeoutInMs;
|
||||
if (timeoutInMs != 0) {
|
||||
uint32_t timeoutInUs = timeoutInMs * 1000;
|
||||
tout = timeoutInUs * 64;
|
||||
}
|
||||
if (debugPrint) {
|
||||
MESH_LOGI (TAG, "SetTx timeoutInMs=%d"
|
||||
" tout=%d",
|
||||
timeoutInMs, tout);
|
||||
}
|
||||
buf[0] = (uint8_t)((tout >> 16) & 0xFF);
|
||||
buf[1] = (uint8_t)((tout >> 8) & 0xFF);
|
||||
buf[2] = (uint8_t)(tout & 0xFF);
|
||||
WriteCommand (SX126X_CMD_SET_TX, buf, 3); // 0x83
|
||||
|
||||
if (debugPrint) {
|
||||
MESH_LOGD(TAG, "Written command, retrying");
|
||||
}
|
||||
|
||||
for (int retry = 0; retry < 20; retry++) {
|
||||
if ((GetStatus() & 0x70) == 0x60)
|
||||
break;
|
||||
Delay_Ms(1);
|
||||
}
|
||||
if (debugPrint) {
|
||||
MESH_LOGI(TAG, "Broke out");
|
||||
}
|
||||
if ((GetStatus() & 0x70) != 0x60) {
|
||||
MESH_LOGE (TAG, "SetTx Illegal Status");
|
||||
LoRaError (ERR_INVALID_SETTX_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
int GetPacketLost() {
|
||||
return txLost;
|
||||
}
|
||||
|
||||
uint8_t GetRssiInst() {
|
||||
uint8_t buf[2];
|
||||
ReadCommand (SX126X_CMD_GET_RSSI_INST, buf, 2); // 0x15
|
||||
return buf[1];
|
||||
}
|
||||
|
||||
void GetRxBufferStatus (uint8_t *payloadLength, uint8_t *rxStartBufferPointer) {
|
||||
uint8_t buf[3];
|
||||
ReadCommand (SX126X_CMD_GET_RX_BUFFER_STATUS, buf, 3); // 0x13
|
||||
*payloadLength = buf[1];
|
||||
*rxStartBufferPointer = buf[2];
|
||||
}
|
||||
|
||||
void WaitForIdleBegin (unsigned long timeout, char *text) {
|
||||
// ensure BUSY is low (state meachine ready)
|
||||
char stop = 0;
|
||||
for (int retry = 0; retry < 10; retry++) {
|
||||
if (retry == 9)
|
||||
stop = 1;
|
||||
char ret = WaitForIdle (BUSY_WAIT, text, stop);
|
||||
if (ret == 1)
|
||||
break;
|
||||
MESH_LOGW (TAG, "WaitForIdle fail retry=%d", retry);
|
||||
Delay_Ms(10);
|
||||
}
|
||||
}
|
||||
|
||||
char WaitForIdle (unsigned long timeout, char *text, char stop) {
|
||||
char ret = 1;
|
||||
Delay_Ms(1);
|
||||
for (unsigned long i = 0; i < timeout; i++) {
|
||||
if (GPIO_ReadInputDataBit (GPIOB, GPIO_Pin_14) == 0) //todo change to actual pin
|
||||
break;
|
||||
Delay_Ms(10);
|
||||
}
|
||||
|
||||
if (GPIO_ReadInputDataBit (GPIOB, GPIO_Pin_14)) { //todo change to actual pin
|
||||
if (stop) {
|
||||
MESH_LOGE (TAG, "WaitForIdle Timeout text=%s timeout=%lu", text, timeout);
|
||||
LoRaError (ERR_IDLE_TIMEOUT);
|
||||
} else {
|
||||
MESH_LOGW (TAG, "WaitForIdle Timeout text=%s timeout=%lu", text, timeout);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
uint8_t ReadBuffer (uint8_t *rxData, int16_t rxDataLen) {
|
||||
uint8_t offset = 0;
|
||||
uint8_t payloadLength = 0;
|
||||
GetRxBufferStatus (&payloadLength, &offset);
|
||||
if (payloadLength > rxDataLen) {
|
||||
MESH_LOGW (TAG, "ReadBuffer rxDataLen too small. payloadLength=%d rxDataLen=%d", payloadLength, rxDataLen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ensure BUSY is low (state meachine ready)
|
||||
WaitForIdle (BUSY_WAIT, "start ReadBuffer", 1);
|
||||
|
||||
// start transfer
|
||||
uint8_t *buf;
|
||||
buf = malloc (payloadLength + 3);
|
||||
if (buf != NULL) {
|
||||
buf[0] = SX126X_CMD_READ_BUFFER; // 0x1E
|
||||
buf[1] = offset; // offset in rx fifo
|
||||
buf[2] = SX126X_CMD_NOP;
|
||||
memset (&buf[3], SX126X_CMD_NOP, payloadLength);
|
||||
spi_read_byte (buf, buf, payloadLength + 3);
|
||||
memcpy (rxData, &buf[3], payloadLength);
|
||||
free (buf);
|
||||
} else {
|
||||
MESH_LOGE (TAG, "ReadBuffer malloc fail");
|
||||
payloadLength = 0;
|
||||
}
|
||||
|
||||
// wait for BUSY to go low
|
||||
WaitForIdle (BUSY_WAIT, "end ReadBuffer", 0);
|
||||
|
||||
return payloadLength;
|
||||
}*/
|
||||
|
||||
|
||||
uint8_t ReadBuffer (uint8_t *rxData, int16_t rxDataLen) {
|
||||
uint8_t offset = 0;
|
||||
uint8_t payloadLength = 0;
|
||||
GetRxBufferStatus (&payloadLength, &offset);
|
||||
|
||||
if (payloadLength > rxDataLen) {
|
||||
MESH_LOGW (TAG, "ReadBuffer rxDataLen too small. payloadLength=%d rxDataLen=%d", payloadLength, rxDataLen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
WaitForIdle (BUSY_WAIT, "start ReadBuffer", 1);
|
||||
|
||||
uint8_t cmd[3] = {SX126X_CMD_READ_BUFFER, offset, SX126X_CMD_NOP};
|
||||
|
||||
spi_read_byte_offs (rxData, cmd, rxDataLen, sizeof (cmd));
|
||||
|
||||
WaitForIdle (BUSY_WAIT, "end ReadBuffer", 0);
|
||||
|
||||
return payloadLength;
|
||||
}
|
||||
|
||||
void WriteBuffer (uint8_t *txData, int16_t txDataLen) {
|
||||
// ensure BUSY is low (state meachine ready)
|
||||
WaitForIdle (BUSY_WAIT, "start WriteBuffer", 1);
|
||||
|
||||
// start transfer
|
||||
uint8_t *buf;
|
||||
buf = malloc (txDataLen + 2);
|
||||
if (buf != NULL) {
|
||||
buf[0] = SX126X_CMD_WRITE_BUFFER; // 0x0E
|
||||
buf[1] = 0; // offset in tx fifo
|
||||
memcpy (&buf[2], txData, txDataLen);
|
||||
spi_write_byte (buf, txDataLen + 2);
|
||||
free (buf);
|
||||
} else {
|
||||
MESH_LOGE (TAG, "WriteBuffer malloc fail");
|
||||
}
|
||||
|
||||
// wait for BUSY to go low
|
||||
WaitForIdle (BUSY_WAIT, "end WriteBuffer", 0);
|
||||
}
|
||||
|
||||
void WriteRegister (uint16_t reg, uint8_t *data, uint8_t numBytes) {
|
||||
// ensure BUSY is low (state meachine ready)
|
||||
WaitForIdle (BUSY_WAIT, "start WriteRegister", 1);
|
||||
|
||||
if (debugPrint) {
|
||||
MESH_LOGI (TAG, "WriteRegister: REG=0x%02x", reg);
|
||||
for (uint8_t n = 0; n < numBytes; n++) {
|
||||
MESH_LOGI (TAG, "DataOut:%02x ", data[n]);
|
||||
}
|
||||
}
|
||||
|
||||
// start transfer
|
||||
uint8_t buf[16];
|
||||
buf[0] = SX126X_CMD_WRITE_REGISTER;
|
||||
buf[1] = (reg & 0xFF00) >> 8;
|
||||
buf[2] = reg & 0xff;
|
||||
memcpy (&buf[3], data, numBytes);
|
||||
spi_write_byte (buf, 3 + numBytes);
|
||||
|
||||
// wait for BUSY to go low
|
||||
WaitForIdle (BUSY_WAIT, "end WriteRegister", 0);
|
||||
}
|
||||
|
||||
void ReadRegister (uint16_t reg, uint8_t *data, uint8_t numBytes) {
|
||||
// ensure BUSY is low (state meachine ready)
|
||||
WaitForIdle (BUSY_WAIT, "start ReadRegister", 1);
|
||||
|
||||
if (debugPrint) {
|
||||
MESH_LOGI (TAG, "ReadRegister: REG=0x%02x", reg);
|
||||
}
|
||||
|
||||
// start transfer
|
||||
uint8_t buf[16];
|
||||
uint8_t buf2[16];
|
||||
memset (buf, SX126X_CMD_NOP, sizeof (buf));
|
||||
memset (buf2, SX126X_CMD_NOP, sizeof (buf2));
|
||||
buf[0] = SX126X_CMD_READ_REGISTER;
|
||||
buf[1] = (reg & 0xFF00) >> 8;
|
||||
buf[2] = reg & 0xff;
|
||||
if (debugPrint) {
|
||||
MESH_LOGI (TAG, "Reading bytes");
|
||||
}
|
||||
|
||||
spi_read_byte (buf2, buf, 4 + numBytes);
|
||||
MESH_LOGI (TAG, "read a byte");
|
||||
memcpy (data, &buf2[4], numBytes);
|
||||
if (debugPrint) {
|
||||
for (uint8_t n = 0; n < numBytes; n++) {
|
||||
MESH_LOGI (TAG, "DataIn:%02x ", data[n]);
|
||||
}
|
||||
}
|
||||
|
||||
// wait for BUSY to go low
|
||||
WaitForIdle (BUSY_WAIT, "end ReadRegister", 0);
|
||||
}
|
||||
|
||||
// WriteCommand with retry
|
||||
void WriteCommand (uint8_t cmd, uint8_t *data, uint8_t numBytes) {
|
||||
uint8_t status;
|
||||
for (int retry = 1; retry < 10; retry++) {
|
||||
status = WriteCommand2 (cmd, data, numBytes);
|
||||
if (debugPrint) {
|
||||
MESH_LOGD (TAG, "status=%02x", status);
|
||||
}
|
||||
if (status == 0)
|
||||
break;
|
||||
MESH_LOGW (TAG, "WriteCommand2 status=%02x retry=%d", status, retry);
|
||||
}
|
||||
if (status != 0) {
|
||||
MESH_LOGE (TAG, "SPI Transaction error:0x%02x", status);
|
||||
LoRaError (ERR_SPI_TRANSACTION);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t WriteCommand2 (uint8_t cmd, uint8_t *data, uint8_t numBytes) {
|
||||
// ensure BUSY is low (state meachine ready)
|
||||
WaitForIdle (BUSY_WAIT, "start WriteCommand2", 1);
|
||||
|
||||
if (debugPrint) {
|
||||
MESH_LOGI (TAG, "WriteCommand: CMD=0x%02x", cmd);
|
||||
}
|
||||
|
||||
// start transfer
|
||||
uint8_t buf[16];
|
||||
buf[0] = cmd;
|
||||
memcpy (&buf[1], data, numBytes);
|
||||
spi_read_byte (buf, buf, numBytes + 1);
|
||||
|
||||
uint8_t status = 0;
|
||||
uint8_t cmd_status = buf[1] & 0xe;
|
||||
|
||||
switch (cmd_status) {
|
||||
case SX126X_STATUS_CMD_TIMEOUT:
|
||||
case SX126X_STATUS_CMD_INVALID:
|
||||
case SX126X_STATUS_CMD_FAILED:
|
||||
status = cmd_status;
|
||||
break;
|
||||
|
||||
case 0:
|
||||
case 7:
|
||||
status = SX126X_STATUS_SPI_FAILED;
|
||||
break;
|
||||
// default: break; // success
|
||||
}
|
||||
|
||||
// wait for BUSY to go low
|
||||
WaitForIdle (BUSY_WAIT, "end WriteCommand2", 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
void ReadCommand (uint8_t cmd, uint8_t *data, uint8_t numBytes) {
|
||||
// ensure BUSY is low (state meachine ready)
|
||||
WaitForIdleBegin (BUSY_WAIT, "start ReadCommand");
|
||||
|
||||
if (debugPrint) {
|
||||
MESH_LOGI (TAG, "ReadCommand: CMD=0x%02x", cmd);
|
||||
}
|
||||
|
||||
// start transfer
|
||||
uint8_t buf[16];
|
||||
memset (buf, SX126X_CMD_NOP, sizeof (buf));
|
||||
buf[0] = cmd;
|
||||
spi_read_byte (buf, buf, 1 + numBytes);
|
||||
if (data != NULL && numBytes)
|
||||
memcpy (data, &buf[1], numBytes);
|
||||
|
||||
// wait for BUSY to go low
|
||||
Delay_Ms(1);
|
||||
WaitForIdle (BUSY_WAIT, "end ReadCommand", 0);
|
||||
}
|
||||
+452
@@ -0,0 +1,452 @@
|
||||
#ifndef _RA01S_H
|
||||
#define _RA01S_H
|
||||
|
||||
// return values
|
||||
#define ERR_NONE 0
|
||||
#define ERR_PACKET_TOO_LONG 1
|
||||
#define ERR_UNKNOWN 2
|
||||
#define ERR_TX_TIMEOUT 3
|
||||
#define ERR_RX_TIMEOUT 4
|
||||
#define ERR_CRC_MISMATCH 5
|
||||
#define ERR_WRONG_MODEM 6
|
||||
#define ERR_INVALID_BANDWIDTH 7
|
||||
#define ERR_INVALID_SPREADING_FACTOR 8
|
||||
#define ERR_INVALID_CODING_RATE 9
|
||||
#define ERR_INVALID_FREQUENCY_DEVIATION 10
|
||||
#define ERR_INVALID_BIT_RATE 11
|
||||
#define ERR_INVALID_RX_BANDWIDTH 12
|
||||
#define ERR_INVALID_DATA_SHAPING 13
|
||||
#define ERR_INVALID_SYNC_WORD 14
|
||||
#define ERR_INVALID_OUTPUT_POWER 15
|
||||
#define ERR_INVALID_MODE 16
|
||||
#define ERR_INVALID_TRANCEIVER 17
|
||||
#define ERR_INVALID_SETRX_STATE 18
|
||||
#define ERR_INVALID_SETTX_STATE 19
|
||||
#define ERR_IDLE_TIMEOUT 20
|
||||
#define ERR_SPI_TRANSACTION 21
|
||||
|
||||
// SX126X physical layer properties
|
||||
#define XTAL_FREQ 32000000
|
||||
#define FREQ_DIV 33554432UL
|
||||
|
||||
#define LOW 0
|
||||
#define HIGH 1
|
||||
#define BUSY_WAIT 5000
|
||||
|
||||
// SX126X Model
|
||||
#define SX1261_TRANCEIVER 0x01
|
||||
#define SX1262_TRANCEIVER 0x02
|
||||
#define SX1268_TRANCEIVER 0x08
|
||||
|
||||
// SX126X SPI commands
|
||||
// operational modes commands
|
||||
#define SX126X_CMD_NOP 0x00
|
||||
#define SX126X_CMD_SET_SLEEP 0x84
|
||||
#define SX126X_CMD_SET_STANDBY 0x80
|
||||
#define SX126X_CMD_SET_FS 0xC1
|
||||
#define SX126X_CMD_SET_TX 0x83
|
||||
#define SX126X_CMD_SET_RX 0x82
|
||||
#define SX126X_CMD_STOP_TIMER_ON_PREAMBLE 0x9F
|
||||
#define SX126X_CMD_SET_RX_DUTY_CYCLE 0x94
|
||||
#define SX126X_CMD_SET_CAD 0xC5
|
||||
#define SX126X_CMD_SET_TX_CONTINUOUS_WAVE 0xD1
|
||||
#define SX126X_CMD_SET_TX_INFINITE_PREAMBLE 0xD2
|
||||
#define SX126X_CMD_SET_REGULATOR_MODE 0x96
|
||||
#define SX126X_CMD_CALIBRATE 0x89
|
||||
#define SX126X_CMD_CALIBRATE_IMAGE 0x98
|
||||
#define SX126X_CMD_SET_PA_CONFIG 0x95
|
||||
#define SX126X_CMD_SET_RX_TX_FALLBACK_MODE 0x93
|
||||
|
||||
// register and buffer access commands
|
||||
#define SX126X_CMD_WRITE_REGISTER 0x0D
|
||||
#define SX126X_CMD_READ_REGISTER 0x1D
|
||||
#define SX126X_CMD_WRITE_BUFFER 0x0E
|
||||
#define SX126X_CMD_READ_BUFFER 0x1E
|
||||
|
||||
// DIO and IRQ control
|
||||
#define SX126X_CMD_SET_DIO_IRQ_PARAMS 0x08
|
||||
#define SX126X_CMD_GET_IRQ_STATUS 0x12
|
||||
#define SX126X_CMD_CLEAR_IRQ_STATUS 0x02
|
||||
#define SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL 0x9D
|
||||
#define SX126X_CMD_SET_DIO3_AS_TCXO_CTRL 0x97
|
||||
|
||||
// RF, modulation and packet commands
|
||||
#define SX126X_CMD_SET_RF_FREQUENCY 0x86
|
||||
#define SX126X_CMD_SET_PACKET_TYPE 0x8A
|
||||
#define SX126X_CMD_GET_PACKET_TYPE 0x11
|
||||
#define SX126X_CMD_SET_TX_PARAMS 0x8E
|
||||
#define SX126X_CMD_SET_MODULATION_PARAMS 0x8B
|
||||
#define SX126X_CMD_SET_PACKET_PARAMS 0x8C
|
||||
#define SX126X_CMD_SET_CAD_PARAMS 0x88
|
||||
#define SX126X_CMD_SET_BUFFER_BASE_ADDRESS 0x8F
|
||||
#define SX126X_CMD_SET_LORA_SYMB_NUM_TIMEOUT 0xA0
|
||||
|
||||
#define SX126X_PA_CONFIG_SX1261 0x01
|
||||
#define SX126X_PA_CONFIG_SX1262 0x00
|
||||
|
||||
// status commands
|
||||
#define SX126X_CMD_GET_STATUS 0xC0
|
||||
#define SX126X_CMD_GET_RSSI_INST 0x15
|
||||
#define SX126X_CMD_GET_RX_BUFFER_STATUS 0x13
|
||||
#define SX126X_CMD_GET_PACKET_STATUS 0x14
|
||||
#define SX126X_CMD_GET_DEVICE_ERRORS 0x17
|
||||
#define SX126X_CMD_CLEAR_DEVICE_ERRORS 0x07
|
||||
#define SX126X_CMD_GET_STATS 0x10
|
||||
#define SX126X_CMD_RESET_STATS 0x00
|
||||
|
||||
|
||||
// SX126X register map
|
||||
#define SX126X_REG_HOPPING_ENABLE 0x0385
|
||||
#define SX126X_REG_PACKECT_LENGTH 0x0386
|
||||
#define SX126X_REG_NB_HOPPING_BLOCKS 0x0387
|
||||
#define SX126X_REG_NB_SYMBOLS0 0x0388
|
||||
#define SX126X_REG_FREQ0 0x038A
|
||||
#define SX126X_REG_NB_SYMBOLS15 0x03E2
|
||||
#define SX126X_REG_FREQ15 0x03E4
|
||||
#define SX126X_REG_DIOX_OUTPUT_ENABLE 0x0580
|
||||
#define SX126X_REG_DIOX_INPUT_ENABLE 0x0583
|
||||
#define SX126X_REG_DIOX_PILL_UP_CONTROL 0x0584
|
||||
#define SX126X_REG_DIOX_PULL_DOWN_CONTROL 0x0585
|
||||
#define SX126X_REG_WHITENING_INITIAL_MSB 0x06B8
|
||||
#define SX126X_REG_WHITENING_INITIAL_LSB 0x06B9
|
||||
#define SX126X_REG_CRC_INITIAL_MSB 0x06BC
|
||||
#define SX126X_REG_CRC_INITIAL_LSB 0x06BD
|
||||
#define SX126X_REG_CRC_POLYNOMIAL_MSB 0x06BE
|
||||
#define SX126X_REG_CRC_POLYNOMIAL_LSB 0x06BF
|
||||
#define SX126X_REG_SYNC_WORD_0 0x06C0
|
||||
#define SX126X_REG_SYNC_WORD_1 0x06C1
|
||||
#define SX126X_REG_SYNC_WORD_2 0x06C2
|
||||
#define SX126X_REG_SYNC_WORD_3 0x06C3
|
||||
#define SX126X_REG_SYNC_WORD_4 0x06C4
|
||||
#define SX126X_REG_SYNC_WORD_5 0x06C5
|
||||
#define SX126X_REG_SYNC_WORD_6 0x06C6
|
||||
#define SX126X_REG_SYNC_WORD_7 0x06C7
|
||||
#define SX126X_REG_NODE_ADDRESS 0x06CD
|
||||
#define SX126X_REG_BROADCAST_ADDRESS 0x06CE
|
||||
#define SX126X_REG_IQ_POLARITY_SETUP 0x0736
|
||||
#define SX126X_REG_LORA_SYNC_WORD_MSB 0x0740
|
||||
#define SX126X_REG_LORA_SYNC_WORD_LSB 0x0741
|
||||
#define SX126X_REG_RANDOM_NUMBER_0 0x0819
|
||||
#define SX126X_REG_RANDOM_NUMBER_1 0x081A
|
||||
#define SX126X_REG_RANDOM_NUMBER_2 0x081B
|
||||
#define SX126X_REG_RANDOM_NUMBER_3 0x081C
|
||||
#define SX126X_REG_TX_MODULETION 0x0889
|
||||
#define SX126X_REG_RX_GAIN 0x08AC
|
||||
#define SX126X_REG_TX_CLAMP_CONFIG 0x08D8
|
||||
#define SX126X_REG_OCP_CONFIGURATION 0x08E7
|
||||
#define SX126X_REG_RTC_CONTROL 0x0902
|
||||
#define SX126X_REG_XTA_TRIM 0x0911
|
||||
#define SX126X_REG_XTB_TRIM 0x0912
|
||||
#define SX126X_REG_DIO3_OUTPUT_VOLTAGE_CONTROL 0x0920
|
||||
#define SX126X_REG_EVENT_MASK 0x0944
|
||||
|
||||
|
||||
// SX126X SPI command variables
|
||||
// SX126X_CMD_SET_SLEEP
|
||||
#define SX126X_SLEEP_START_COLD 0b00000000 // 2 2 sleep mode: cold start, configuration is lost (default)
|
||||
#define SX126X_SLEEP_START_WARM 0b00000100 // 2 2 warm start, configuration is retained
|
||||
#define SX126X_SLEEP_RTC_OFF 0b00000000 // 0 0 wake on RTC timeout: disabled
|
||||
#define SX126X_SLEEP_RTC_ON 0b00000001 // 0 0 enabled
|
||||
|
||||
// SX126X_CMD_SET_STANDBY
|
||||
#define SX126X_STANDBY_RC 0x00 // 7 0 standby mode: 13 MHz RC oscillator
|
||||
#define SX126X_STANDBY_XOSC 0x01 // 7 0 32 MHz crystal oscillator
|
||||
|
||||
// SX126X_CMD_SET_RX
|
||||
#define SX126X_RX_TIMEOUT_NONE 0x000000 // 23 0 Rx timeout duration: no timeout (Rx single mode)
|
||||
#define SX126X_RX_TIMEOUT_INF 0xFFFFFF // 23 0 infinite (Rx continuous mode)
|
||||
|
||||
// SX126X_CMD_STOP_TIMER_ON_PREAMBLE
|
||||
#define SX126X_STOP_ON_PREAMBLE_OFF 0x00 // 7 0 stop timer on: sync word or header (default)
|
||||
#define SX126X_STOP_ON_PREAMBLE_ON 0x01 // 7 0 preamble detection
|
||||
|
||||
// SX126X_CMD_SET_REGULATOR_MODE
|
||||
#define SX126X_REGULATOR_LDO 0x00 // 7 0 set regulator mode: LDO (default)
|
||||
#define SX126X_REGULATOR_DC_DC 0x01 // 7 0 DC-DC
|
||||
|
||||
// SX126X_CMD_CALIBRATE
|
||||
#define SX126X_CALIBRATE_IMAGE_OFF 0b00000000 // 6 6 image calibration: disabled
|
||||
#define SX126X_CALIBRATE_IMAGE_ON 0b01000000 // 6 6 enabled
|
||||
#define SX126X_CALIBRATE_ADC_BULK_P_OFF 0b00000000 // 5 5 ADC bulk P calibration: disabled
|
||||
#define SX126X_CALIBRATE_ADC_BULK_P_ON 0b00100000 // 5 5 enabled
|
||||
#define SX126X_CALIBRATE_ADC_BULK_N_OFF 0b00000000 // 4 4 ADC bulk N calibration: disabled
|
||||
#define SX126X_CALIBRATE_ADC_BULK_N_ON 0b00010000 // 4 4 enabled
|
||||
#define SX126X_CALIBRATE_ADC_PULSE_OFF 0b00000000 // 3 3 ADC pulse calibration: disabled
|
||||
#define SX126X_CALIBRATE_ADC_PULSE_ON 0b00001000 // 3 3 enabled
|
||||
#define SX126X_CALIBRATE_PLL_OFF 0b00000000 // 2 2 PLL calibration: disabled
|
||||
#define SX126X_CALIBRATE_PLL_ON 0b00000100 // 2 2 enabled
|
||||
#define SX126X_CALIBRATE_RC13M_OFF 0b00000000 // 1 1 13 MHz RC osc. calibration: disabled
|
||||
#define SX126X_CALIBRATE_RC13M_ON 0b00000010 // 1 1 enabled
|
||||
#define SX126X_CALIBRATE_RC64K_OFF 0b00000000 // 0 0 64 kHz RC osc. calibration: disabled
|
||||
#define SX126X_CALIBRATE_RC64K_ON 0b00000001 // 0 0 enabled
|
||||
|
||||
// SX126X_CMD_CALIBRATE_IMAGE
|
||||
#define SX126X_CAL_IMG_430_MHZ_1 0x6B
|
||||
#define SX126X_CAL_IMG_430_MHZ_2 0x6F
|
||||
#define SX126X_CAL_IMG_470_MHZ_1 0x75
|
||||
#define SX126X_CAL_IMG_470_MHZ_2 0x81
|
||||
#define SX126X_CAL_IMG_779_MHZ_1 0xC1
|
||||
#define SX126X_CAL_IMG_779_MHZ_2 0xC5
|
||||
#define SX126X_CAL_IMG_863_MHZ_1 0xD7
|
||||
#define SX126X_CAL_IMG_863_MHZ_2 0xDB
|
||||
#define SX126X_CAL_IMG_902_MHZ_1 0xE1
|
||||
#define SX126X_CAL_IMG_902_MHZ_2 0xE9
|
||||
|
||||
// SX126X_CMD_SET_PA_CONFIG
|
||||
#define SX126X_PA_CONFIG_HP_MAX 0x07
|
||||
#define SX126X_PA_CONFIG_SX1268 0x01
|
||||
#define SX126X_PA_CONFIG_PA_LUT 0x01
|
||||
|
||||
// SX126X_CMD_SET_RX_TX_FALLBACK_MODE
|
||||
#define SX126X_RX_TX_FALLBACK_MODE_FS 0x40 // 7 0 after Rx/Tx go to: FS mode
|
||||
#define SX126X_RX_TX_FALLBACK_MODE_STDBY_XOSC 0x30 // 7 0 standby with crystal oscillator
|
||||
#define SX126X_RX_TX_FALLBACK_MODE_STDBY_RC 0x20 // 7 0 standby with RC oscillator (default)
|
||||
|
||||
// SX126X_CMD_SET_DIO_IRQ_PARAMS
|
||||
#define SX126X_IRQ_TIMEOUT 0b1000000000 // 9 9 Rx or Tx timeout
|
||||
#define SX126X_IRQ_CAD_DETECTED 0b0100000000 // 8 8 channel activity detected
|
||||
#define SX126X_IRQ_CAD_DONE 0b0010000000 // 7 7 channel activity detection finished
|
||||
#define SX126X_IRQ_CRC_ERR 0b0001000000 // 6 6 wrong CRC received
|
||||
#define SX126X_IRQ_HEADER_ERR 0b0000100000 // 5 5 LoRa header CRC error
|
||||
#define SX126X_IRQ_HEADER_VALID 0b0000010000 // 4 4 valid LoRa header received
|
||||
#define SX126X_IRQ_SYNC_WORD_VALID 0b0000001000 // 3 3 valid sync word detected
|
||||
#define SX126X_IRQ_PREAMBLE_DETECTED 0b0000000100 // 2 2 preamble detected
|
||||
#define SX126X_IRQ_RX_DONE 0b0000000010 // 1 1 packet received
|
||||
#define SX126X_IRQ_TX_DONE 0b0000000001 // 0 0 packet transmission completed
|
||||
#define SX126X_IRQ_ALL 0b1111111111 // 9 0 all interrupts
|
||||
#define SX126X_IRQ_NONE 0b0000000000 // 9 0 no interrupts
|
||||
|
||||
// SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL
|
||||
#define SX126X_DIO2_AS_IRQ 0x00 // 7 0 DIO2 configuration: IRQ
|
||||
#define SX126X_DIO2_AS_RF_SWITCH 0x01 // 7 0 RF switch control
|
||||
|
||||
// SX126X_CMD_SET_DIO3_AS_TCXO_CTRL
|
||||
#define SX126X_DIO3_OUTPUT_1_6 0x00 // 7 0 DIO3 voltage output for TCXO: 1.6 V
|
||||
#define SX126X_DIO3_OUTPUT_1_7 0x01 // 7 0 1.7 V
|
||||
#define SX126X_DIO3_OUTPUT_1_8 0x02 // 7 0 1.8 V
|
||||
#define SX126X_DIO3_OUTPUT_2_2 0x03 // 7 0 2.2 V
|
||||
#define SX126X_DIO3_OUTPUT_2_4 0x04 // 7 0 2.4 V
|
||||
#define SX126X_DIO3_OUTPUT_2_7 0x05 // 7 0 2.7 V
|
||||
#define SX126X_DIO3_OUTPUT_3_0 0x06 // 7 0 3.0 V
|
||||
#define SX126X_DIO3_OUTPUT_3_3 0x07 // 7 0 3.3 V
|
||||
|
||||
// Radio complete Wake-up Time with TCXO stabilisation time
|
||||
#define RADIO_TCXO_SETUP_TIME 5000 // [us]
|
||||
|
||||
// SX126X_CMD_SET_PACKET_TYPE
|
||||
#define SX126X_PACKET_TYPE_GFSK 0x00 // 7 0 packet type: GFSK
|
||||
#define SX126X_PACKET_TYPE_LORA 0x01 // 7 0 LoRa
|
||||
|
||||
// SX126X_CMD_SET_TX_PARAMS
|
||||
#define SX126X_PA_RAMP_10U 0x00 // 7 0 ramp time: 10 us
|
||||
#define SX126X_PA_RAMP_20U 0x01 // 7 0 20 us
|
||||
#define SX126X_PA_RAMP_40U 0x02 // 7 0 40 us
|
||||
#define SX126X_PA_RAMP_80U 0x03 // 7 0 80 us
|
||||
#define SX126X_PA_RAMP_200U 0x04 // 7 0 200 us
|
||||
#define SX126X_PA_RAMP_800U 0x05 // 7 0 800 us
|
||||
#define SX126X_PA_RAMP_1700U 0x06 // 7 0 1700 us
|
||||
#define SX126X_PA_RAMP_3400U 0x07 // 7 0 3400 us
|
||||
|
||||
// SX126X_CMD_SET_MODULATION_PARAMS
|
||||
#define SX126X_GFSK_FILTER_NONE 0x00 // 7 0 GFSK filter: none
|
||||
#define SX126X_GFSK_FILTER_GAUSS_0_3 0x08 // 7 0 Gaussian, BT = 0.3
|
||||
#define SX126X_GFSK_FILTER_GAUSS_0_5 0x09 // 7 0 Gaussian, BT = 0.5
|
||||
#define SX126X_GFSK_FILTER_GAUSS_0_7 0x0A // 7 0 Gaussian, BT = 0.7
|
||||
#define SX126X_GFSK_FILTER_GAUSS_1 0x0B // 7 0 Gaussian, BT = 1
|
||||
#define SX126X_GFSK_RX_BW_4_8 0x1F // 7 0 GFSK Rx bandwidth: 4.8 kHz
|
||||
#define SX126X_GFSK_RX_BW_5_8 0x17 // 7 0 5.8 kHz
|
||||
#define SX126X_GFSK_RX_BW_7_3 0x0F // 7 0 7.3 kHz
|
||||
#define SX126X_GFSK_RX_BW_9_7 0x1E // 7 0 9.7 kHz
|
||||
#define SX126X_GFSK_RX_BW_11_7 0x16 // 7 0 11.7 kHz
|
||||
#define SX126X_GFSK_RX_BW_14_6 0x0E // 7 0 14.6 kHz
|
||||
#define SX126X_GFSK_RX_BW_19_5 0x1D // 7 0 19.5 kHz
|
||||
#define SX126X_GFSK_RX_BW_23_4 0x15 // 7 0 23.4 kHz
|
||||
#define SX126X_GFSK_RX_BW_29_3 0x0D // 7 0 29.3 kHz
|
||||
#define SX126X_GFSK_RX_BW_39_0 0x1C // 7 0 39.0 kHz
|
||||
#define SX126X_GFSK_RX_BW_46_9 0x14 // 7 0 46.9 kHz
|
||||
#define SX126X_GFSK_RX_BW_58_6 0x0C // 7 0 58.6 kHz
|
||||
#define SX126X_GFSK_RX_BW_78_2 0x1B // 7 0 78.2 kHz
|
||||
#define SX126X_GFSK_RX_BW_93_8 0x13 // 7 0 93.8 kHz
|
||||
#define SX126X_GFSK_RX_BW_117_3 0x0B // 7 0 117.3 kHz
|
||||
#define SX126X_GFSK_RX_BW_156_2 0x1A // 7 0 156.2 kHz
|
||||
#define SX126X_GFSK_RX_BW_187_2 0x12 // 7 0 187.2 kHz
|
||||
#define SX126X_GFSK_RX_BW_234_3 0x0A // 7 0 234.3 kHz
|
||||
#define SX126X_GFSK_RX_BW_312_0 0x19 // 7 0 312.0 kHz
|
||||
#define SX126X_GFSK_RX_BW_373_6 0x11 // 7 0 373.6 kHz
|
||||
#define SX126X_GFSK_RX_BW_467_0 0x09 // 7 0 467.0 kHz
|
||||
#define SX126X_LORA_BW_7_8 0x00 // 7 0 LoRa bandwidth: 7.8 kHz
|
||||
#define SX126X_LORA_BW_10_4 0x08 // 7 0 10.4 kHz
|
||||
#define SX126X_LORA_BW_15_6 0x01 // 7 0 15.6 kHz
|
||||
#define SX126X_LORA_BW_20_8 0x09 // 7 0 20.8 kHz
|
||||
#define SX126X_LORA_BW_31_25 0x02 // 7 0 31.25 kHz
|
||||
#define SX126X_LORA_BW_41_7 0x0A // 7 0 41.7 kHz
|
||||
#define SX126X_LORA_BW_62_5 0x03 // 7 0 62.5 kHz
|
||||
#define SX126X_LORA_BW_125_0 0x04 // 7 0 125.0 kHz
|
||||
#define SX126X_LORA_BW_250_0 0x05 // 7 0 250.0 kHz
|
||||
#define SX126X_LORA_BW_500_0 0x06 // 7 0 500.0 kHz
|
||||
#define SX126X_LORA_CR_4_5 0x01 // 7 0 LoRa coding rate: 4/5
|
||||
#define SX126X_LORA_CR_4_6 0x02 // 7 0 4/6
|
||||
#define SX126X_LORA_CR_4_7 0x03 // 7 0 4/7
|
||||
#define SX126X_LORA_CR_4_8 0x04 // 7 0 4/8
|
||||
#define SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF 0x00 // 7 0 LoRa low data rate optimization: disabled
|
||||
#define SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON 0x01 // 7 0 enabled
|
||||
|
||||
// SX126X_CMD_SET_PACKET_PARAMS
|
||||
#define SX126X_GFSK_PREAMBLE_DETECT_OFF 0x00 // 7 0 GFSK minimum preamble length before reception starts: detector disabled
|
||||
#define SX126X_GFSK_PREAMBLE_DETECT_8 0x04 // 7 0 8 bits
|
||||
#define SX126X_GFSK_PREAMBLE_DETECT_16 0x05 // 7 0 16 bits
|
||||
#define SX126X_GFSK_PREAMBLE_DETECT_24 0x06 // 7 0 24 bits
|
||||
#define SX126X_GFSK_PREAMBLE_DETECT_32 0x07 // 7 0 32 bits
|
||||
#define SX126X_GFSK_ADDRESS_FILT_OFF 0x00 // 7 0 GFSK address filtering: disabled
|
||||
#define SX126X_GFSK_ADDRESS_FILT_NODE 0x01 // 7 0 node only
|
||||
#define SX126X_GFSK_ADDRESS_FILT_NODE_BROADCAST 0x02 // 7 0 node and broadcast
|
||||
#define SX126X_GFSK_PACKET_FIXED 0x00 // 7 0 GFSK packet type: fixed (payload length known in advance to both sides)
|
||||
#define SX126X_GFSK_PACKET_VARIABLE 0x01 // 7 0 variable (payload length added to packet)
|
||||
#define SX126X_GFSK_CRC_OFF 0x01 // 7 0 GFSK packet CRC: disabled
|
||||
#define SX126X_GFSK_CRC_1_BYTE 0x00 // 7 0 1 byte
|
||||
#define SX126X_GFSK_CRC_2_BYTE 0x02 // 7 0 2 byte
|
||||
#define SX126X_GFSK_CRC_1_BYTE_INV 0x04 // 7 0 1 byte, inverted
|
||||
#define SX126X_GFSK_CRC_2_BYTE_INV 0x06 // 7 0 2 byte, inverted
|
||||
#define SX126X_GFSK_WHITENING_OFF 0x00 // 7 0 GFSK data whitening: disabled
|
||||
#define SX126X_GFSK_WHITENING_ON 0x01 // 7 0 enabled
|
||||
#define SX126X_LORA_HEADER_EXPLICIT 0x00 // 7 0 LoRa header mode: explicit
|
||||
#define SX126X_LORA_HEADER_IMPLICIT 0x01 // 7 0 implicit
|
||||
#define SX126X_LORA_CRC_OFF 0x00 // 7 0 LoRa CRC mode: disabled
|
||||
#define SX126X_LORA_CRC_ON 0x01 // 7 0 enabled
|
||||
#define SX126X_LORA_IQ_STANDARD 0x00 // 7 0 LoRa IQ setup: standard
|
||||
#define SX126X_LORA_IQ_INVERTED 0x01 // 7 0 inverted
|
||||
|
||||
// SX126X_CMD_SET_CAD_PARAMS
|
||||
#define SX126X_CAD_ON_1_SYMB 0x00 // 7 0 number of symbols used for CAD: 1
|
||||
#define SX126X_CAD_ON_2_SYMB 0x01 // 7 0 2
|
||||
#define SX126X_CAD_ON_4_SYMB 0x02 // 7 0 4
|
||||
#define SX126X_CAD_ON_8_SYMB 0x03 // 7 0 8
|
||||
#define SX126X_CAD_ON_16_SYMB 0x04 // 7 0 16
|
||||
#define SX126X_CAD_GOTO_STDBY 0x00 // 7 0 after CAD is done, always go to STDBY_RC mode
|
||||
#define SX126X_CAD_GOTO_RX 0x01 // 7 0 after CAD is done, go to Rx mode if activity is detected
|
||||
|
||||
// SX126X_CMD_GET_STATUS
|
||||
#define SX126X_STATUS_MODE_STDBY_RC 0b00100000 // 6 4 current chip mode: STDBY_RC
|
||||
#define SX126X_STATUS_MODE_STDBY_XOSC 0b00110000 // 6 4 STDBY_XOSC
|
||||
#define SX126X_STATUS_MODE_FS 0b01000000 // 6 4 FS
|
||||
#define SX126X_STATUS_MODE_RX 0b01010000 // 6 4 RX
|
||||
#define SX126X_STATUS_MODE_TX 0b01100000 // 6 4 TX
|
||||
#define SX126X_STATUS_DATA_AVAILABLE 0b00000100 // 3 1 command status: packet received and data can be retrieved
|
||||
#define SX126X_STATUS_CMD_TIMEOUT 0b00000110 // 3 1 SPI command timed out
|
||||
#define SX126X_STATUS_CMD_INVALID 0b00001000 // 3 1 invalid SPI command
|
||||
#define SX126X_STATUS_CMD_FAILED 0b00001010 // 3 1 SPI command failed to execute
|
||||
#define SX126X_STATUS_TX_DONE 0b00001100 // 3 1 packet transmission done
|
||||
#define SX126X_STATUS_SPI_FAILED 0b11111111 // 7 0 SPI transaction failed
|
||||
|
||||
// SX126X_CMD_GET_PACKET_STATUS
|
||||
#define SX126X_GFSK_RX_STATUS_PREAMBLE_ERR 0b10000000 // 7 7 GFSK Rx status: preamble error
|
||||
#define SX126X_GFSK_RX_STATUS_SYNC_ERR 0b01000000 // 6 6 sync word error
|
||||
#define SX126X_GFSK_RX_STATUS_ADRS_ERR 0b00100000 // 5 5 address error
|
||||
#define SX126X_GFSK_RX_STATUS_CRC_ERR 0b00010000 // 4 4 CRC error
|
||||
#define SX126X_GFSK_RX_STATUS_LENGTH_ERR 0b00001000 // 3 3 length error
|
||||
#define SX126X_GFSK_RX_STATUS_ABORT_ERR 0b00000100 // 2 2 abort error
|
||||
#define SX126X_GFSK_RX_STATUS_PACKET_RECEIVED 0b00000010 // 2 2 packet received
|
||||
#define SX126X_GFSK_RX_STATUS_PACKET_SENT 0b00000001 // 2 2 packet sent
|
||||
|
||||
// SX126X_CMD_GET_DEVICE_ERRORS
|
||||
#define SX126X_PA_RAMP_ERR 0b100000000 // 8 8 device errors: PA ramping failed
|
||||
#define SX126X_PLL_LOCK_ERR 0b001000000 // 6 6 PLL failed to lock
|
||||
#define SX126X_XOSC_START_ERR 0b000100000 // 5 5 crystal oscillator failed to start
|
||||
#define SX126X_IMG_CALIB_ERR 0b000010000 // 4 4 image calibration failed
|
||||
#define SX126X_ADC_CALIB_ERR 0b000001000 // 3 3 ADC calibration failed
|
||||
#define SX126X_PLL_CALIB_ERR 0b000000100 // 2 2 PLL calibration failed
|
||||
#define SX126X_RC13M_CALIB_ERR 0b000000010 // 1 1 RC13M calibration failed
|
||||
#define SX126X_RC64K_CALIB_ERR 0b000000001 // 0 0 RC64K calibration failed
|
||||
|
||||
|
||||
// SX126X SPI register variables
|
||||
// SX126X_REG_LORA_SYNC_WORD_MSB + LSB
|
||||
// #define SX126X_SYNC_WORD_PUBLIC 0x3444
|
||||
#define SX126X_SYNC_WORD_PUBLIC 0x24B4 // meshtastic
|
||||
#define SX126X_SYNC_WORD_PRIVATE 0x1424
|
||||
|
||||
#define SX126x_TXMODE_ASYNC 0x01
|
||||
#define SX126x_TXMODE_SYNC 0x02
|
||||
#define SX126x_TXMODE_BACK2RX 0x04
|
||||
|
||||
// Convert bandwidth constant to float in kHz
|
||||
//float loraBwToFloat(uint8_t bw);
|
||||
|
||||
// Convert float bandwidth (kHz) to constant
|
||||
//uint8_t floatToLoraBw(float bw);
|
||||
|
||||
// Convert coding rate constant to float
|
||||
//float loraCrToFloat(uint8_t cr);
|
||||
|
||||
// Convert float coding rate to constant
|
||||
//uint8_t floatToLoraCr(float cr);
|
||||
|
||||
// Convert MHz to Hz with limit check
|
||||
//uint32_t mhzToHzLimited(double freqMHz);
|
||||
|
||||
// Public function
|
||||
void LoRaInit (void);
|
||||
int16_t LoRaBegin (uint32_t frequencyInHz, int8_t txPowerInDbm, uint16_t tcxoVoltage, char useRegulatorLDO);
|
||||
void LoRaConfig (uint8_t spreadingFactor, uint8_t bandwidth, uint8_t codingRate, uint16_t preambleLength, uint8_t payloadLen, char crcOn, char invertIrq);
|
||||
uint8_t LoRaReceive (uint8_t *pData, int16_t len);
|
||||
char LoRaSend (uint8_t *pData, int16_t len, uint8_t mode);
|
||||
void LoRaDebugPrint (char enable);
|
||||
|
||||
// Private function
|
||||
void spi_write_byte (uint8_t *Dataout, size_t DataLength);
|
||||
void spi_read_byte (uint8_t *rx, const uint8_t *tx, size_t len);
|
||||
void spi_read_byte_offs (uint8_t *rx, const uint8_t *tx, size_t len, uint8_t off);
|
||||
uint8_t spi_transfer (uint8_t address);
|
||||
|
||||
char ReceiveMode (void);
|
||||
void GetPacketStatus (int8_t *rssiPacket, int8_t *snrPacket, int8_t *rawSnr);
|
||||
void SetTxPower (int8_t txPowerInDbm);
|
||||
|
||||
void FixInvertedIQ (uint8_t iqConfig);
|
||||
void SetDio3AsTcxoCtrl (uint16_t voltage, uint32_t delay);
|
||||
void SetDio2AsRfSwitchCtrl (uint8_t enable);
|
||||
void ResetLora (void);
|
||||
void SetStandby (uint8_t mode);
|
||||
void SetRfFrequency (uint32_t frequency);
|
||||
void Calibrate (uint8_t calibParam);
|
||||
void CalibrateImage (uint32_t frequency);
|
||||
void SetRegulatorMode (uint8_t mode);
|
||||
void SetBufferBaseAddress (uint8_t txBaseAddress, uint8_t rxBaseAddress);
|
||||
void SetPowerConfig (int8_t power, uint8_t rampTime);
|
||||
void SetOvercurrentProtection (uint16_t currentLimit);
|
||||
void SetSyncWord (int16_t sync);
|
||||
void SetPaConfig (uint8_t paDutyCycle, uint8_t hpMax, uint8_t deviceSel, uint8_t paLut);
|
||||
void SetDioIrqParams (uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask);
|
||||
void SetStopRxTimerOnPreambleDetect (char enable);
|
||||
void SetLoRaSymbNumTimeout (uint8_t SymbNum);
|
||||
void SetPacketType (uint8_t packetType);
|
||||
void SetModulationParams (uint8_t spreadingFactor, uint8_t bandwidth, uint8_t codingRate, uint8_t lowDataRateOptimize);
|
||||
void SetCadParams (uint8_t cadSymbolNum, uint8_t cadDetPeak, uint8_t cadDetMin, uint8_t cadExitMode, uint32_t cadTimeout);
|
||||
void SetCad();
|
||||
uint8_t GetStatus (void);
|
||||
uint16_t GetIrqStatus (void);
|
||||
void ClearIrqStatus (uint16_t irq);
|
||||
void SetRx (uint32_t timeout);
|
||||
void SetTx (uint32_t timeoutInMs);
|
||||
int GetPacketLost();
|
||||
uint8_t GetRssiInst();
|
||||
void GetRxBufferStatus (uint8_t *payloadLength, uint8_t *rxStartBufferPointer);
|
||||
void Wakeup (void);
|
||||
void WaitForIdleBegin (unsigned long timeout, char *text);
|
||||
char WaitForIdle (unsigned long timeout, char *text, char stop);
|
||||
uint8_t ReadBuffer (uint8_t *rxData, int16_t rxDataLen);
|
||||
void WriteBuffer (uint8_t *txData, int16_t txDataLen);
|
||||
void WriteRegister (uint16_t reg, uint8_t *data, uint8_t numBytes);
|
||||
void ReadRegister (uint16_t reg, uint8_t *data, uint8_t numBytes);
|
||||
void WriteCommand (uint8_t cmd, uint8_t *data, uint8_t numBytes);
|
||||
uint8_t WriteCommand2 (uint8_t cmd, uint8_t *data, uint8_t numBytes);
|
||||
void ReadCommand (uint8_t cmd, uint8_t *data, uint8_t numBytes);
|
||||
void SPItransfer (uint8_t cmd, char write, uint8_t *dataOut, uint8_t *dataIn, uint8_t numBytes, char waitForBusy);
|
||||
void LoRaError (int error);
|
||||
|
||||
extern uint8_t PacketParams[6];
|
||||
extern char txActive;
|
||||
extern int txLost;
|
||||
extern char debugPrint;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,987 @@
|
||||
/********************************** (C) COPYRIGHT *******************************
|
||||
* File Name : system_ch32v20x.c
|
||||
* Author : WCH
|
||||
* Version : V1.0.0
|
||||
* Date : 2021/06/06
|
||||
* Description : CH32V20x Device Peripheral Access Layer System Source File.
|
||||
* For HSE = 32Mhz (CH32V208x/CH32V203RBT)
|
||||
* For HSE = 8Mhz (other CH32V203x)
|
||||
*********************************************************************************
|
||||
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
|
||||
* Attention: This software (modified or not) and binary are used for
|
||||
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
|
||||
*******************************************************************************/
|
||||
#include "ch32v20x.h"
|
||||
|
||||
/*
|
||||
* Uncomment the line corresponding to the desired System clock (SYSCLK) frequency (after
|
||||
* reset the HSI is used as SYSCLK source).
|
||||
* If none of the define below is enabled, the HSI is used as System clock source.
|
||||
*/
|
||||
//#define SYSCLK_FREQ_HSE HSE_VALUE
|
||||
//#define SYSCLK_FREQ_48MHz_HSE 48000000
|
||||
//#define SYSCLK_FREQ_56MHz_HSE 56000000
|
||||
//#define SYSCLK_FREQ_72MHz_HSE 72000000
|
||||
//#define SYSCLK_FREQ_96MHz_HSE 96000000
|
||||
//#define SYSCLK_FREQ_120MHz_HSE 120000000
|
||||
#define SYSCLK_FREQ_144MHz_HSE 144000000
|
||||
//#define SYSCLK_FREQ_HSI HSI_VALUE
|
||||
//#define SYSCLK_FREQ_48MHz_HSI 48000000
|
||||
//#define SYSCLK_FREQ_56MHz_HSI 56000000
|
||||
//#define SYSCLK_FREQ_72MHz_HSI 72000000
|
||||
//#define SYSCLK_FREQ_96MHz_HSI 96000000
|
||||
//#define SYSCLK_FREQ_120MHz_HSI 120000000
|
||||
//#define SYSCLK_FREQ_144MHz_HSI 144000000
|
||||
|
||||
/* Clock Definitions */
|
||||
#ifdef SYSCLK_FREQ_HSE
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_HSE; /* System Clock Frequency (Core Clock) */
|
||||
#elif defined SYSCLK_FREQ_48MHz_HSE
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz_HSE; /* System Clock Frequency (Core Clock) */
|
||||
#elif defined SYSCLK_FREQ_56MHz_HSE
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz_HSE; /* System Clock Frequency (Core Clock) */
|
||||
#elif defined SYSCLK_FREQ_72MHz_HSE
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz_HSE; /* System Clock Frequency (Core Clock) */
|
||||
#elif defined SYSCLK_FREQ_96MHz_HSE
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_96MHz_HSE; /* System Clock Frequency (Core Clock) */
|
||||
#elif defined SYSCLK_FREQ_120MHz_HSE
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_120MHz_HSE; /* System Clock Frequency (Core Clock) */
|
||||
#elif defined SYSCLK_FREQ_144MHz_HSE
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_144MHz_HSE; /* System Clock Frequency (Core Clock) */
|
||||
#elif defined SYSCLK_FREQ_48MHz_HSI
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz_HSI; /* System Clock Frequency (Core Clock) */
|
||||
#elif defined SYSCLK_FREQ_56MHz_HSI
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz_HSI; /* System Clock Frequency (Core Clock) */
|
||||
#elif defined SYSCLK_FREQ_72MHz_HSI
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz_HSI; /* System Clock Frequency (Core Clock) */
|
||||
#elif defined SYSCLK_FREQ_96MHz_HSI
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_96MHz_HSI; /* System Clock Frequency (Core Clock) */
|
||||
#elif defined SYSCLK_FREQ_120MHz_HSI
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_120MHz_HSI; /* System Clock Frequency (Core Clock) */
|
||||
#elif defined SYSCLK_FREQ_144MHz_HSI
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_144MHz_HSI; /* System Clock Frequency (Core Clock) */
|
||||
#else
|
||||
uint32_t SystemCoreClock = HSI_VALUE; /* System Clock Frequency (Core Clock) */
|
||||
|
||||
#endif
|
||||
|
||||
__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
|
||||
|
||||
|
||||
/* system_private_function_proto_types */
|
||||
static void SetSysClock(void);
|
||||
|
||||
#ifdef SYSCLK_FREQ_HSE
|
||||
static void SetSysClockToHSE( void );
|
||||
#elif defined SYSCLK_FREQ_48MHz_HSE
|
||||
static void SetSysClockTo48_HSE( void );
|
||||
#elif defined SYSCLK_FREQ_56MHz_HSE
|
||||
static void SetSysClockTo56_HSE( void );
|
||||
#elif defined SYSCLK_FREQ_72MHz_HSE
|
||||
static void SetSysClockTo72_HSE( void );
|
||||
#elif defined SYSCLK_FREQ_96MHz_HSE
|
||||
static void SetSysClockTo96_HSE( void );
|
||||
#elif defined SYSCLK_FREQ_120MHz_HSE
|
||||
static void SetSysClockTo120_HSE( void );
|
||||
#elif defined SYSCLK_FREQ_144MHz_HSE
|
||||
static void SetSysClockTo144_HSE( void );
|
||||
#elif defined SYSCLK_FREQ_48MHz_HSI
|
||||
static void SetSysClockTo48_HSI( void );
|
||||
#elif defined SYSCLK_FREQ_56MHz_HSI
|
||||
static void SetSysClockTo56_HSI( void );
|
||||
#elif defined SYSCLK_FREQ_72MHz_HSI
|
||||
static void SetSysClockTo72_HSI( void );
|
||||
#elif defined SYSCLK_FREQ_96MHz_HSI
|
||||
static void SetSysClockTo96_HSI( void );
|
||||
#elif defined SYSCLK_FREQ_120MHz_HSI
|
||||
static void SetSysClockTo120_HSI( void );
|
||||
#elif defined SYSCLK_FREQ_144MHz_HSI
|
||||
static void SetSysClockTo144_HSI( void );
|
||||
|
||||
#endif
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SystemInit
|
||||
*
|
||||
* @brief Setup the microcontroller system Initialize the Embedded Flash Interface,
|
||||
* the PLL and update the SystemCoreClock variable.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void SystemInit (void)
|
||||
{
|
||||
RCC->CTLR |= (uint32_t)0x00000001;
|
||||
RCC->CFGR0 &= (uint32_t)0xF0FF0000;
|
||||
RCC->CTLR &= (uint32_t)0xFEF6FFFF;
|
||||
RCC->CTLR &= (uint32_t)0xFFFBFFFF;
|
||||
RCC->CFGR0 &= (uint32_t)0xFF00FFFF;
|
||||
RCC->INTR = 0x009F0000;
|
||||
SetSysClock();
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SystemCoreClockUpdate
|
||||
*
|
||||
* @brief Update SystemCoreClock variable according to Clock Register Values.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void SystemCoreClockUpdate (void)
|
||||
{
|
||||
uint32_t tmp = 0, pllmull = 0, pllsource = 0, Pll_6_5 = 0;
|
||||
|
||||
tmp = RCC->CFGR0 & RCC_SWS;
|
||||
|
||||
switch (tmp)
|
||||
{
|
||||
case 0x00:
|
||||
SystemCoreClock = HSI_VALUE;
|
||||
break;
|
||||
case 0x04:
|
||||
SystemCoreClock = HSE_VALUE;
|
||||
break;
|
||||
case 0x08:
|
||||
pllmull = RCC->CFGR0 & RCC_PLLMULL;
|
||||
pllsource = RCC->CFGR0 & RCC_PLLSRC;
|
||||
pllmull = ( pllmull >> 18) + 2;
|
||||
|
||||
if(pllmull == 17) pllmull = 18;
|
||||
|
||||
if (pllsource == 0x00)
|
||||
{
|
||||
if(EXTEN->EXTEN_CTR & EXTEN_PLL_HSI_PRE){
|
||||
SystemCoreClock = HSI_VALUE * pllmull;
|
||||
}
|
||||
else{
|
||||
SystemCoreClock = (HSI_VALUE >> 1) * pllmull;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined (CH32V20x_D8W) || defined (CH32V20x_D8)
|
||||
if(((RCC->CFGR0 & (3<<22)) == (3<<22)) && (RCC_USB5PRE_JUDGE()== SET))
|
||||
{
|
||||
SystemCoreClock = ((HSE_VALUE>>1)) * pllmull;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if ((RCC->CFGR0 & RCC_PLLXTPRE) != (uint32_t)RESET)
|
||||
{
|
||||
#if defined (CH32V20x_D8) || defined (CH32V20x_D8W)
|
||||
SystemCoreClock = ((HSE_VALUE>>2) >> 1) * pllmull;
|
||||
#else
|
||||
SystemCoreClock = (HSE_VALUE >> 1) * pllmull;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined (CH32V20x_D8) || defined (CH32V20x_D8W)
|
||||
SystemCoreClock = (HSE_VALUE>>2) * pllmull;
|
||||
#else
|
||||
SystemCoreClock = HSE_VALUE * pllmull;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if(Pll_6_5 == 1) SystemCoreClock = (SystemCoreClock / 2);
|
||||
|
||||
break;
|
||||
default:
|
||||
SystemCoreClock = HSI_VALUE;
|
||||
break;
|
||||
}
|
||||
|
||||
tmp = AHBPrescTable[((RCC->CFGR0 & RCC_HPRE) >> 4)];
|
||||
SystemCoreClock >>= tmp;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClock
|
||||
*
|
||||
* @brief Configures the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClock(void)
|
||||
{
|
||||
//GPIO_IPD_Unused();
|
||||
#ifdef SYSCLK_FREQ_HSE
|
||||
SetSysClockToHSE();
|
||||
#elif defined SYSCLK_FREQ_48MHz_HSE
|
||||
SetSysClockTo48_HSE();
|
||||
#elif defined SYSCLK_FREQ_56MHz_HSE
|
||||
SetSysClockTo56_HSE();
|
||||
#elif defined SYSCLK_FREQ_72MHz_HSE
|
||||
SetSysClockTo72_HSE();
|
||||
#elif defined SYSCLK_FREQ_96MHz_HSE
|
||||
SetSysClockTo96_HSE();
|
||||
#elif defined SYSCLK_FREQ_120MHz_HSE
|
||||
SetSysClockTo120_HSE();
|
||||
#elif defined SYSCLK_FREQ_144MHz_HSE
|
||||
SetSysClockTo144_HSE();
|
||||
#elif defined SYSCLK_FREQ_48MHz_HSI
|
||||
SetSysClockTo48_HSI();
|
||||
#elif defined SYSCLK_FREQ_56MHz_HSI
|
||||
SetSysClockTo56_HSI();
|
||||
#elif defined SYSCLK_FREQ_72MHz_HSI
|
||||
SetSysClockTo72_HSI();
|
||||
#elif defined SYSCLK_FREQ_96MHz_HSI
|
||||
SetSysClockTo96_HSI();
|
||||
#elif defined SYSCLK_FREQ_120MHz_HSI
|
||||
SetSysClockTo120_HSI();
|
||||
#elif defined SYSCLK_FREQ_144MHz_HSI
|
||||
SetSysClockTo144_HSI();
|
||||
|
||||
#endif
|
||||
|
||||
/* If none of the define above is enabled, the HSI is used as System clock
|
||||
* source (default after reset)
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
#ifdef SYSCLK_FREQ_HSE
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockToHSE
|
||||
*
|
||||
* @brief Sets HSE as System clock source and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockToHSE(void)
|
||||
{
|
||||
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
|
||||
|
||||
|
||||
RCC->CTLR |= ((uint32_t)RCC_HSEON);
|
||||
|
||||
/* Wait till HSE is ready and if Time out is reached exit */
|
||||
do
|
||||
{
|
||||
HSEStatus = RCC->CTLR & RCC_HSERDY;
|
||||
StartUpCounter++;
|
||||
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
|
||||
|
||||
if ((RCC->CTLR & RCC_HSERDY) != RESET)
|
||||
{
|
||||
HSEStatus = (uint32_t)0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
HSEStatus = (uint32_t)0x00;
|
||||
}
|
||||
|
||||
if (HSEStatus == (uint32_t)0x01)
|
||||
{
|
||||
/* HCLK = SYSCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
/* PCLK2 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
|
||||
/* PCLK1 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV1;
|
||||
|
||||
/* Select HSE as system clock source
|
||||
* CH32V20x_D6 (HSE=8MHZ)
|
||||
* CH32V20x_D8 (HSE=32MHZ)
|
||||
* CH32V20x_D8W (HSE=32MHZ)
|
||||
*/
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
|
||||
RCC->CFGR0 |= (uint32_t)RCC_SW_HSE;
|
||||
|
||||
/* Wait till HSE is used as system clock source */
|
||||
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x04)
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If HSE fails to start-up, the application will have wrong clock
|
||||
* configuration. User can add here some code to deal with this error
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined SYSCLK_FREQ_48MHz_HSE
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo48_HSE
|
||||
*
|
||||
* @brief Sets System clock frequency to 48MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo48_HSE(void)
|
||||
{
|
||||
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
|
||||
|
||||
|
||||
RCC->CTLR |= ((uint32_t)RCC_HSEON);
|
||||
/* Wait till HSE is ready and if Time out is reached exit */
|
||||
do
|
||||
{
|
||||
HSEStatus = RCC->CTLR & RCC_HSERDY;
|
||||
StartUpCounter++;
|
||||
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
|
||||
|
||||
if ((RCC->CTLR & RCC_HSERDY) != RESET)
|
||||
{
|
||||
HSEStatus = (uint32_t)0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
HSEStatus = (uint32_t)0x00;
|
||||
}
|
||||
|
||||
if (HSEStatus == (uint32_t)0x01)
|
||||
{
|
||||
/* HCLK = SYSCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
/* PCLK2 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
|
||||
/* PCLK1 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
|
||||
|
||||
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 6 = 48 MHz (HSE=8MHZ)
|
||||
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 6 = 48 MHz (HSE=32MHZ)
|
||||
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 6 = 48 MHz (HSE=32MHZ)
|
||||
*/
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
|
||||
|
||||
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL6);
|
||||
|
||||
/* Enable PLL */
|
||||
RCC->CTLR |= RCC_PLLON;
|
||||
/* Wait till PLL is ready */
|
||||
while((RCC->CTLR & RCC_PLLRDY) == 0)
|
||||
{
|
||||
}
|
||||
/* Select PLL as system clock source */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
|
||||
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
|
||||
/* Wait till PLL is used as system clock source */
|
||||
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* If HSE fails to start-up, the application will have wrong clock
|
||||
* configuration. User can add here some code to deal with this error
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined SYSCLK_FREQ_56MHz_HSE
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo56_HSE
|
||||
*
|
||||
* @brief Sets System clock frequency to 56MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo56_HSE(void)
|
||||
{
|
||||
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
|
||||
|
||||
RCC->CTLR |= ((uint32_t)RCC_HSEON);
|
||||
|
||||
/* Wait till HSE is ready and if Time out is reached exit */
|
||||
do
|
||||
{
|
||||
HSEStatus = RCC->CTLR & RCC_HSERDY;
|
||||
StartUpCounter++;
|
||||
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
|
||||
|
||||
if ((RCC->CTLR & RCC_HSERDY) != RESET)
|
||||
{
|
||||
HSEStatus = (uint32_t)0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
HSEStatus = (uint32_t)0x00;
|
||||
}
|
||||
|
||||
if (HSEStatus == (uint32_t)0x01)
|
||||
{
|
||||
/* HCLK = SYSCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
/* PCLK2 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
|
||||
/* PCLK1 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
|
||||
|
||||
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 7 = 56 MHz (HSE=8MHZ)
|
||||
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 7 = 56 MHz (HSE=32MHZ)
|
||||
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 7 = 56 MHz (HSE=32MHZ)
|
||||
*/
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
|
||||
|
||||
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL7);
|
||||
|
||||
/* Enable PLL */
|
||||
RCC->CTLR |= RCC_PLLON;
|
||||
/* Wait till PLL is ready */
|
||||
while((RCC->CTLR & RCC_PLLRDY) == 0)
|
||||
{
|
||||
}
|
||||
|
||||
/* Select PLL as system clock source */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
|
||||
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
|
||||
/* Wait till PLL is used as system clock source */
|
||||
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* If HSE fails to start-up, the application will have wrong clock
|
||||
* configuration. User can add here some code to deal with this error
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined SYSCLK_FREQ_72MHz_HSE
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo72_HSE
|
||||
*
|
||||
* @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo72_HSE(void)
|
||||
{
|
||||
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
|
||||
|
||||
RCC->CTLR |= ((uint32_t)RCC_HSEON);
|
||||
|
||||
/* Wait till HSE is ready and if Time out is reached exit */
|
||||
do
|
||||
{
|
||||
HSEStatus = RCC->CTLR & RCC_HSERDY;
|
||||
StartUpCounter++;
|
||||
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
|
||||
|
||||
if ((RCC->CTLR & RCC_HSERDY) != RESET)
|
||||
{
|
||||
HSEStatus = (uint32_t)0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
HSEStatus = (uint32_t)0x00;
|
||||
}
|
||||
|
||||
if (HSEStatus == (uint32_t)0x01)
|
||||
{
|
||||
/* HCLK = SYSCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
/* PCLK2 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
|
||||
/* PCLK1 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
|
||||
|
||||
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 9 = 72 MHz (HSE=8MHZ)
|
||||
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 9 = 72 MHz (HSE=32MHZ)
|
||||
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 9 = 72 MHz (HSE=32MHZ)
|
||||
*/
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE |
|
||||
RCC_PLLMULL));
|
||||
|
||||
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL9);
|
||||
|
||||
/* Enable PLL */
|
||||
RCC->CTLR |= RCC_PLLON;
|
||||
/* Wait till PLL is ready */
|
||||
while((RCC->CTLR & RCC_PLLRDY) == 0)
|
||||
{
|
||||
}
|
||||
/* Select PLL as system clock source */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
|
||||
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
|
||||
/* Wait till PLL is used as system clock source */
|
||||
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* If HSE fails to start-up, the application will have wrong clock
|
||||
* configuration. User can add here some code to deal with this error
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#elif defined SYSCLK_FREQ_96MHz_HSE
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo96_HSE
|
||||
*
|
||||
* @brief Sets System clock frequency to 96MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo96_HSE(void)
|
||||
{
|
||||
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
|
||||
|
||||
RCC->CTLR |= ((uint32_t)RCC_HSEON);
|
||||
|
||||
/* Wait till HSE is ready and if Time out is reached exit */
|
||||
do
|
||||
{
|
||||
HSEStatus = RCC->CTLR & RCC_HSERDY;
|
||||
StartUpCounter++;
|
||||
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
|
||||
|
||||
if ((RCC->CTLR & RCC_HSERDY) != RESET)
|
||||
{
|
||||
HSEStatus = (uint32_t)0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
HSEStatus = (uint32_t)0x00;
|
||||
}
|
||||
|
||||
if (HSEStatus == (uint32_t)0x01)
|
||||
{
|
||||
/* HCLK = SYSCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
/* PCLK2 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
|
||||
/* PCLK1 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
|
||||
|
||||
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 12 = 96 MHz (HSE=8MHZ)
|
||||
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 12 = 96 MHz (HSE=32MHZ)
|
||||
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 12 = 96 MHz (HSE=32MHZ)
|
||||
*/
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE |
|
||||
RCC_PLLMULL));
|
||||
|
||||
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL12);
|
||||
|
||||
/* Enable PLL */
|
||||
RCC->CTLR |= RCC_PLLON;
|
||||
/* Wait till PLL is ready */
|
||||
while((RCC->CTLR & RCC_PLLRDY) == 0)
|
||||
{
|
||||
}
|
||||
/* Select PLL as system clock source */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
|
||||
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
|
||||
/* Wait till PLL is used as system clock source */
|
||||
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* If HSE fails to start-up, the application will have wrong clock
|
||||
* configuration. User can add here some code to deal with this error
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#elif defined SYSCLK_FREQ_120MHz_HSE
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo120_HSE
|
||||
*
|
||||
* @brief Sets System clock frequency to 120MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo120_HSE(void)
|
||||
{
|
||||
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
|
||||
|
||||
RCC->CTLR |= ((uint32_t)RCC_HSEON);
|
||||
|
||||
/* Wait till HSE is ready and if Time out is reached exit */
|
||||
do
|
||||
{
|
||||
HSEStatus = RCC->CTLR & RCC_HSERDY;
|
||||
StartUpCounter++;
|
||||
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
|
||||
|
||||
if((RCC->CTLR & RCC_HSERDY) != RESET)
|
||||
{
|
||||
HSEStatus = (uint32_t)0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
HSEStatus = (uint32_t)0x00;
|
||||
}
|
||||
|
||||
if(HSEStatus == (uint32_t)0x01)
|
||||
{
|
||||
#if defined (CH32V20x_D8W)
|
||||
RCC->CFGR0 |= (uint32_t)(3<<22);
|
||||
/* HCLK = SYSCLK/2 */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV2;
|
||||
#else
|
||||
/* HCLK = SYSCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
#endif
|
||||
/* PCLK2 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
|
||||
/* PCLK1 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
|
||||
|
||||
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 15 = 120 MHz (HSE=8MHZ)
|
||||
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 15 = 120 MHz (HSE=32MHZ)
|
||||
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/2 * 15 = 240 MHz (HSE=32MHZ)
|
||||
*/
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE |
|
||||
RCC_PLLMULL));
|
||||
|
||||
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL15);
|
||||
|
||||
/* Enable PLL */
|
||||
RCC->CTLR |= RCC_PLLON;
|
||||
/* Wait till PLL is ready */
|
||||
while((RCC->CTLR & RCC_PLLRDY) == 0)
|
||||
{
|
||||
}
|
||||
/* Select PLL as system clock source */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW));
|
||||
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
|
||||
/* Wait till PLL is used as system clock source */
|
||||
while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* If HSE fails to start-up, the application will have wrong clock
|
||||
* configuration. User can add here some code to deal with this error
|
||||
*/
|
||||
}
|
||||
}
|
||||
#elif defined SYSCLK_FREQ_144MHz_HSE
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo144_HSE
|
||||
*
|
||||
* @brief Sets System clock frequency to 144MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo144_HSE(void)
|
||||
{
|
||||
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
|
||||
|
||||
RCC->CTLR |= ((uint32_t)RCC_HSEON);
|
||||
|
||||
/* Wait till HSE is ready and if Time out is reached exit */
|
||||
do
|
||||
{
|
||||
HSEStatus = RCC->CTLR & RCC_HSERDY;
|
||||
StartUpCounter++;
|
||||
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
|
||||
|
||||
if ((RCC->CTLR & RCC_HSERDY) != RESET)
|
||||
{
|
||||
HSEStatus = (uint32_t)0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
HSEStatus = (uint32_t)0x00;
|
||||
}
|
||||
|
||||
if (HSEStatus == (uint32_t)0x01)
|
||||
{
|
||||
/* HCLK = SYSCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
/* PCLK2 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
|
||||
/* PCLK1 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
|
||||
|
||||
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 18 = 144 MHz (HSE=8MHZ)
|
||||
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 18 = 144 MHz (HSE=32MHZ)
|
||||
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 18 = 144 MHz (HSE=32MHZ)
|
||||
*/
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE |
|
||||
RCC_PLLMULL));
|
||||
|
||||
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL18);
|
||||
|
||||
/* Enable PLL */
|
||||
RCC->CTLR |= RCC_PLLON;
|
||||
/* Wait till PLL is ready */
|
||||
while((RCC->CTLR & RCC_PLLRDY) == 0)
|
||||
{
|
||||
}
|
||||
/* Select PLL as system clock source */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
|
||||
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
|
||||
/* Wait till PLL is used as system clock source */
|
||||
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* If HSE fails to start-up, the application will have wrong clock
|
||||
* configuration. User can add here some code to deal with this error
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined SYSCLK_FREQ_48MHz_HSI
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo48_HSI
|
||||
*
|
||||
* @brief Sets System clock frequency to 48MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo48_HSI(void)
|
||||
{
|
||||
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
|
||||
|
||||
/* HCLK = SYSCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
/* PCLK2 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
|
||||
/* PCLK1 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
|
||||
|
||||
/* PLL configuration: PLLCLK = HSI * 6 = 48 MHz */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
|
||||
|
||||
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL6);
|
||||
|
||||
/* Enable PLL */
|
||||
RCC->CTLR |= RCC_PLLON;
|
||||
/* Wait till PLL is ready */
|
||||
while((RCC->CTLR & RCC_PLLRDY) == 0)
|
||||
{
|
||||
}
|
||||
/* Select PLL as system clock source */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
|
||||
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
|
||||
/* Wait till PLL is used as system clock source */
|
||||
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined SYSCLK_FREQ_56MHz_HSI
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo56_HSI
|
||||
*
|
||||
* @brief Sets System clock frequency to 56MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo56_HSI(void)
|
||||
{
|
||||
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
|
||||
|
||||
/* HCLK = SYSCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
/* PCLK2 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
|
||||
/* PCLK1 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
|
||||
|
||||
/* PLL configuration: PLLCLK = HSI * 7 = 48 MHz */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
|
||||
|
||||
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL7);
|
||||
|
||||
/* Enable PLL */
|
||||
RCC->CTLR |= RCC_PLLON;
|
||||
/* Wait till PLL is ready */
|
||||
while((RCC->CTLR & RCC_PLLRDY) == 0)
|
||||
{
|
||||
}
|
||||
/* Select PLL as system clock source */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
|
||||
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
|
||||
/* Wait till PLL is used as system clock source */
|
||||
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined SYSCLK_FREQ_72MHz_HSI
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo72_HSI
|
||||
*
|
||||
* @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo72_HSI(void)
|
||||
{
|
||||
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
|
||||
|
||||
/* HCLK = SYSCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
/* PCLK2 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
|
||||
/* PCLK1 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
|
||||
|
||||
/* PLL configuration: PLLCLK = HSI * 9 = 72 MHz */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
|
||||
|
||||
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL9);
|
||||
|
||||
/* Enable PLL */
|
||||
RCC->CTLR |= RCC_PLLON;
|
||||
/* Wait till PLL is ready */
|
||||
while((RCC->CTLR & RCC_PLLRDY) == 0)
|
||||
{
|
||||
}
|
||||
/* Select PLL as system clock source */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
|
||||
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
|
||||
/* Wait till PLL is used as system clock source */
|
||||
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#elif defined SYSCLK_FREQ_96MHz_HSI
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo96_HSI
|
||||
*
|
||||
* @brief Sets System clock frequency to 96MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo96_HSI(void)
|
||||
{
|
||||
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
|
||||
|
||||
/* HCLK = SYSCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
/* PCLK2 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
|
||||
/* PCLK1 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
|
||||
|
||||
/* PLL configuration: PLLCLK = HSI * 12 = 96 MHz */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
|
||||
|
||||
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL12);
|
||||
|
||||
/* Enable PLL */
|
||||
RCC->CTLR |= RCC_PLLON;
|
||||
/* Wait till PLL is ready */
|
||||
while((RCC->CTLR & RCC_PLLRDY) == 0)
|
||||
{
|
||||
}
|
||||
/* Select PLL as system clock source */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
|
||||
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
|
||||
/* Wait till PLL is used as system clock source */
|
||||
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#elif defined SYSCLK_FREQ_120MHz_HSI
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo120_HSI
|
||||
*
|
||||
* @brief Sets System clock frequency to 120MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo120_HSI(void)
|
||||
{
|
||||
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
|
||||
|
||||
/* HCLK = SYSCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
/* PCLK2 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
|
||||
/* PCLK1 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
|
||||
|
||||
/* PLL configuration: PLLCLK = HSI * 15 = 120 MHz */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE |
|
||||
RCC_PLLMULL));
|
||||
|
||||
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL15);
|
||||
|
||||
/* Enable PLL */
|
||||
RCC->CTLR |= RCC_PLLON;
|
||||
/* Wait till PLL is ready */
|
||||
while((RCC->CTLR & RCC_PLLRDY) == 0)
|
||||
{
|
||||
}
|
||||
/* Select PLL as system clock source */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW));
|
||||
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
|
||||
/* Wait till PLL is used as system clock source */
|
||||
while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
|
||||
{
|
||||
}
|
||||
}
|
||||
#elif defined SYSCLK_FREQ_144MHz_HSI
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo144_HSI
|
||||
*
|
||||
* @brief Sets System clock frequency to 144MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo144_HSI(void)
|
||||
{
|
||||
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
|
||||
|
||||
/* HCLK = SYSCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
/* PCLK2 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
|
||||
/* PCLK1 = HCLK */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
|
||||
|
||||
/* PLL configuration: PLLCLK = HSI * 18 = 144 MHz */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
|
||||
|
||||
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL18);
|
||||
|
||||
/* Enable PLL */
|
||||
RCC->CTLR |= RCC_PLLON;
|
||||
/* Wait till PLL is ready */
|
||||
while((RCC->CTLR & RCC_PLLRDY) == 0)
|
||||
{
|
||||
}
|
||||
/* Select PLL as system clock source */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
|
||||
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
|
||||
/* Wait till PLL is used as system clock source */
|
||||
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
/********************************** (C) COPYRIGHT *******************************
|
||||
* File Name : system_ch32v20x.h
|
||||
* Author : WCH
|
||||
* Version : V1.0.0
|
||||
* Date : 2021/06/06
|
||||
* Description : CH32V20x Device Peripheral Access Layer System Header File.
|
||||
*********************************************************************************
|
||||
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
|
||||
* Attention: This software (modified or not) and binary are used for
|
||||
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
|
||||
*******************************************************************************/
|
||||
#ifndef __SYSTEM_ch32v20x_H
|
||||
#define __SYSTEM_ch32v20x_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern uint32_t SystemCoreClock; /* System Clock Frequency (Core Clock) */
|
||||
|
||||
/* System_Exported_Functions */
|
||||
extern void SystemInit(void);
|
||||
extern void SystemCoreClockUpdate(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*__CH32V20x_SYSTEM_H */
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "stdio.h"
|
||||
#include <ctype.h>
|
||||
#include "hexdump.h"
|
||||
|
||||
void hexdump (const char *label, const uint8_t *data, size_t len) {
|
||||
if (label)
|
||||
iprintf ("%s (len=%u):\n", label, len);
|
||||
|
||||
for (size_t i = 0; i < len; i += 16) {
|
||||
iprintf ("%04u ", i); // offset
|
||||
for (size_t j = 0; j < 16; j++) {
|
||||
if (i + j < len)
|
||||
iprintf ("%02X ", data[i + j]);
|
||||
else
|
||||
iprintf (" "); // pad spacing
|
||||
}
|
||||
iprintf (" ");
|
||||
for (size_t j = 0; j < 16 && i + j < len; j++) {
|
||||
uint8_t c = data[i + j];
|
||||
iprintf ("%c", isprint (c) ? c : '.');
|
||||
}
|
||||
iprintf ("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void hexdump_compact(const uint8_t* data, size_t len, char* out, size_t out_size) {
|
||||
size_t pos = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (pos + 2 >= out_size) break; // make sure we don¡¯t overflow
|
||||
snprintf(out + pos, 3, "%02x", data[i]); // 2 chars + null terminator
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
if (pos < out_size)
|
||||
out[pos] = '\0';
|
||||
else
|
||||
out[out_size - 1] = '\0'; // ensure null termination
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef HEXDUMP_HEADER
|
||||
#define HEXDUMP_HEADER
|
||||
|
||||
|
||||
#include "stddef.h"
|
||||
#include "stdint.h"
|
||||
void hexdump (const char *label, const uint8_t *data, size_t len);
|
||||
|
||||
void hexdump_compact(const uint8_t* data, size_t len, char* out, size_t out_size);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef LOGGER_HEADER
|
||||
#define LOGGER_HEADER
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Log levels as integers for easy comparison
|
||||
#define LOG_LEVEL_ERROR 0
|
||||
#define LOG_LEVEL_WARN 1
|
||||
#define LOG_LEVEL_INFO 2
|
||||
#define LOG_LEVEL_DEBUG 3
|
||||
#define LOG_LEVEL_VERBOSE 4
|
||||
|
||||
// Current log level (change at runtime or compile-time)
|
||||
#ifndef CURRENT_LOG_LEVEL
|
||||
#define CURRENT_LOG_LEVEL LOG_LEVEL_DEBUG
|
||||
#endif
|
||||
|
||||
// Internal macro to check log level
|
||||
#define LOG_PRINT_IF(level, levelStr, tag, fmt, ...) \
|
||||
do { \
|
||||
if ((level) <= CURRENT_LOG_LEVEL) { \
|
||||
printf("[%s] %s: " fmt "\r\n", levelStr, tag, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// ESP-like shortcuts
|
||||
#define MESH_LOGE(tag, fmt, ...) LOG_PRINT_IF(LOG_LEVEL_ERROR, "E", tag, fmt, ##__VA_ARGS__)
|
||||
#define MESH_LOGW(tag, fmt, ...) LOG_PRINT_IF(LOG_LEVEL_WARN, "W", tag, fmt, ##__VA_ARGS__)
|
||||
#define MESH_LOGI(tag, fmt, ...) LOG_PRINT_IF(LOG_LEVEL_INFO, "I", tag, fmt, ##__VA_ARGS__)
|
||||
#define MESH_LOGD(tag, fmt, ...) LOG_PRINT_IF(LOG_LEVEL_DEBUG, "D", tag, fmt, ##__VA_ARGS__)
|
||||
#define MESH_LOGV(tag, fmt, ...) LOG_PRINT_IF(LOG_LEVEL_VERBOSE, "V", tag, fmt, ##__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user