RevE-light development version

WARNING: The files in this commit are our current development version. The hardware and software is not functional yet - so do not use it. We will not answer any support inquiry regarding this version at the moment.
This commit is contained in:
KaOs
2017-11-23 14:40:40 +00:00
parent 43fac83781
commit d270131062
64 changed files with 21696 additions and 0 deletions
+1
View File
@@ -34,3 +34,4 @@ The code repository contains
* Firmware: The complete firmware including a modified Atmel DFU bootloader and LUFA
* Software: Contains a python tool for an easy configuration (and more) of the ChameleonMini, Note that this is currently under construction
* RevE: Contains the whole contents of the discontinued RevE repository.
* RevE-light: Contains our development files for the RevE-light - **WARNING:** currently not supported / not functional
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
WARNING: The files in this folder are our current development version. The hardware and software is not functional yet - so do not use it. We will not answer any support inquiry regarding this version at the moment.
+1
View File
@@ -0,0 +1 @@
/.metadata
@@ -0,0 +1 @@
/Bin
@@ -0,0 +1,51 @@
/*
* AntennaLevel.h
*
* Created on: 24.11.2013
* Author: skuser
*/
#ifndef ANTENNALEVEL_H_
#define ANTENNALEVEL_H_
#include "Common.h"
#define ANTENNA_LEVEL_R1 10E3
#define ANTENNA_LEVEL_R2 220E0
#define ANTENNA_LEVEL_VREF 1.0
#define ANTENNA_LEVEL_RES 4096
#define ANTENNA_LEVEL_OFFSET 190 /* LSB */
#define ANTENNA_LEVEL_MILLIVOLT 1E3
#define ANTENNA_LEVEL_FACTOR (ANTENNA_LEVEL_VREF * (ANTENNA_LEVEL_R1 + ANTENNA_LEVEL_R2) / (ANTENNA_LEVEL_RES * ANTENNA_LEVEL_R2) )
#define ANTENNA_LEVEL_SCALE ((uint32_t) 1<<16)
#define ANTENNA_LEVEL_NUMERATOR ((uint32_t) (ANTENNA_LEVEL_MILLIVOLT * ANTENNA_LEVEL_FACTOR * ANTENNA_LEVEL_SCALE + .5))
#define ANTENNA_LEVEL_DENOMINATOR (ANTENNA_LEVEL_SCALE)
static inline
void AntennaLevelInit(void)
{
ADCA.CTRLA = ADC_ENABLE_bm;
ADCA.CTRLB = ADC_RESOLUTION_12BIT_gc;
ADCA.REFCTRL = ADC_REFSEL_INT1V_gc | ADC_BANDGAP_bm;
ADCA.PRESCALER = ADC_PRESCALER_DIV32_gc;
ADCA.CH0.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc;
ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN7_gc;
}
static inline
uint16_t AntennaLevelGet(void)
{
ADCA.CH0.CTRL |= ADC_CH_START_bm;
while( !(ADCA.CH0.INTFLAGS & ADC_CH_CHIF_bm) );
ADCA.CH0.INTFLAGS = ADC_CH_CHIF_bm;
int16_t Result = ADCA.CH0RES - ANTENNA_LEVEL_OFFSET;
if (Result < 0) Result = 0;
return (uint16_t) (((uint32_t) Result * ANTENNA_LEVEL_NUMERATOR) / ANTENNA_LEVEL_DENOMINATOR);
}
#endif /* ANTENNALEVEL_H_ */
@@ -0,0 +1,51 @@
/*
* Application.h
*
* Created on: 18.02.2013
* Author: skuser
*/
#ifndef APPLICATION_H_
#define APPLICATION_H_
#include "../Common.h"
#include "../Configuration.h"
#include "../Log.h"
/* Applications */
#include "MifareUltralight.h"
#include "MifareClassic.h"
/* Function wrappers */
INLINE void ApplicationInit(void) {
ActiveConfiguration.ApplicationInitFunc();
}
INLINE void ApplicationTask(void) {
ActiveConfiguration.ApplicationTaskFunc();
}
INLINE uint16_t ApplicationProcess(uint8_t* ByteBuffer, uint16_t ByteCount) {
return ActiveConfiguration.ApplicationProcessFunc(ByteBuffer, ByteCount);
}
INLINE void ApplicationTick(void) {
ActiveConfiguration.ApplicationTickFunc();
}
INLINE void ApplicationReset(void) {
ActiveConfiguration.ApplicationResetFunc();
//LogEntry(LOG_INFO_APP_RESET, NULL, 0);
}
INLINE void ApplicationGetUid(ConfigurationUidType Uid) {
ActiveConfiguration.ApplicationGetUidFunc(Uid);
}
INLINE void ApplicationSetUid(ConfigurationUidType Uid) {
ActiveConfiguration.ApplicationSetUidFunc(Uid);
LogEntry(LOG_INFO_UID_SET, Uid, ActiveConfiguration.UidSize);
}
#endif /* APPLICATION_H_ */
@@ -0,0 +1,323 @@
#include "Crypto1.h"
#define PRNG_MASK 0x002D0000UL
/* x^16 + x^14 + x^13 + x^11 + 1 */
#define PRNG_SIZE 4 /* Bytes */
#define LFSR_MASK_EVEN 0x2010E1UL
#define LFSR_MASK_ODD 0x3A7394UL
/* x^48 + x^43 + x^39 + x^38 + x^36 + x^34 + x^33 + x^31 + x^29 +
* x^24 + x^23 + x^21 + x^19 + x^13 + x^9 + x^7 + x^6 + x^5 + 1 */
#define LFSR_SIZE 6 /* Bytes */
/* Functions fa, fb and fc in filter output network. Definitions taken
* from Timo Kasper's thesis */
#define FA(x3, x2, x1, x0) ( \
( (x0 | x1) ^ (x0 & x3) ) ^ ( x2 & ( (x0 ^ x1) | x3 ) ) \
)
#define FB(x3, x2, x1, x0) ( \
( (x0 & x1) | x2 ) ^ ( (x0 ^ x1) & (x2 | x3) ) \
)
#define FC(x4, x3, x2, x1, x0) ( \
( x0 | ( (x1 | x4) & (x3 ^ x4) ) ) ^ ( ( x0 ^ (x1 & x3) ) & ( (x2 ^ x3) | (x1 & x4) ) ) \
)
/* Create tables from function fa, fb and fc for faster access */
static const uint8_t TableAB[5][16] = {
{ /* fa with Input {3,2,1,0} = (0,0,0,0) to (1,1,1,1) shifted by 0 */
FA(0,0,0,0) << 0, FA(0,0,0,1) << 0, FA(0,0,1,0) << 0, FA(0,0,1,1) << 0,
FA(0,1,0,0) << 0, FA(0,1,0,1) << 0, FA(0,1,1,0) << 0, FA(0,1,1,1) << 0,
FA(1,0,0,0) << 0, FA(1,0,0,1) << 0, FA(1,0,1,0) << 0, FA(1,0,1,1) << 0,
FA(1,1,0,0) << 0, FA(1,1,0,1) << 0, FA(1,1,1,0) << 0, FA(1,1,1,1) << 0,
},
{ /* fb with Input {3,2,1,0} = (0,0,0,0) to (1,1,1,1) shifted by 1 */
FB(0,0,0,0) << 1, FB(0,0,0,1) << 1, FB(0,0,1,0) << 1, FB(0,0,1,1) << 1,
FB(0,1,0,0) << 1, FB(0,1,0,1) << 1, FB(0,1,1,0) << 1, FB(0,1,1,1) << 1,
FB(1,0,0,0) << 1, FB(1,0,0,1) << 1, FB(1,0,1,0) << 1, FB(1,0,1,1) << 1,
FB(1,1,0,0) << 1, FB(1,1,0,1) << 1, FB(1,1,1,0) << 1, FB(1,1,1,1) << 1,
},
{ /* fb with Input {3,2,1,0} = (0,0,0,0) to (1,1,1,1) shifted by 2 */
FB(0,0,0,0) << 2, FB(0,0,0,1) << 2, FB(0,0,1,0) << 2, FB(0,0,1,1) << 2,
FB(0,1,0,0) << 2, FB(0,1,0,1) << 2, FB(0,1,1,0) << 2, FB(0,1,1,1) << 2,
FB(1,0,0,0) << 2, FB(1,0,0,1) << 2, FB(1,0,1,0) << 2, FB(1,0,1,1) << 2,
FB(1,1,0,0) << 2, FB(1,1,0,1) << 2, FB(1,1,1,0) << 2, FB(1,1,1,1) << 2,
},
{ /* fa with Input {3,2,1,0} = (0,0,0,0) to (1,1,1,1) shifted by 3 */
FA(0,0,0,0) << 3, FA(0,0,0,1) << 3, FA(0,0,1,0) << 3, FA(0,0,1,1) << 3,
FA(0,1,0,0) << 3, FA(0,1,0,1) << 3, FA(0,1,1,0) << 3, FA(0,1,1,1) << 3,
FA(1,0,0,0) << 3, FA(1,0,0,1) << 3, FA(1,0,1,0) << 3, FA(1,0,1,1) << 3,
FA(1,1,0,0) << 3, FA(1,1,0,1) << 3, FA(1,1,1,0) << 3, FA(1,1,1,1) << 3,
},
{ /* fb with Input {3,2,1,0} = (0,0,0,0) to (1,1,1,1) shifted by 4 */
FB(0,0,0,0) << 4, FB(0,0,0,1) << 4, FB(0,0,1,0) << 4, FB(0,0,1,1) << 4,
FB(0,1,0,0) << 4, FB(0,1,0,1) << 4, FB(0,1,1,0) << 4, FB(0,1,1,1) << 4,
FB(1,0,0,0) << 4, FB(1,0,0,1) << 4, FB(1,0,1,0) << 4, FB(1,0,1,1) << 4,
FB(1,1,0,0) << 4, FB(1,1,0,1) << 4, FB(1,1,1,0) << 4, FB(1,1,1,1) << 4,
}
};
static const uint8_t TableC[32] = {
/* fc with Input {4,3,2,1,0} = (0,0,0,0,0) to (1,1,1,1,1) */
FC(0,0,0,0,0), FC(0,0,0,0,1), FC(0,0,0,1,0), FC(0,0,0,1,1),
FC(0,0,1,0,0), FC(0,0,1,0,1), FC(0,0,1,1,0), FC(0,0,1,1,1),
FC(0,1,0,0,0), FC(0,1,0,0,1), FC(0,1,0,1,0), FC(0,1,0,1,1),
FC(0,1,1,0,0), FC(0,1,1,0,1), FC(0,1,1,1,0), FC(0,1,1,1,1),
FC(1,0,0,0,0), FC(1,0,0,0,1), FC(1,0,0,1,0), FC(1,0,0,1,1),
FC(1,0,1,0,0), FC(1,0,1,0,1), FC(1,0,1,1,0), FC(1,0,1,1,1),
FC(1,1,0,0,0), FC(1,1,0,0,1), FC(1,1,0,1,0), FC(1,1,0,1,1),
FC(1,1,1,0,0), FC(1,1,1,0,1), FC(1,1,1,1,0), FC(1,1,1,1,1),
};
/* Split Crypto1 state into even and odd bits to speed up the output filter network */
static uint8_t StateEven[LFSR_SIZE/2] = {0};
static uint8_t StateOdd[LFSR_SIZE/2] = {0};
/* Proceed LFSR by one clock cycle */
static void Crypto1LFSR(uint8_t In) {
uint8_t Feedback = 0;
/* Calculate feedback according to LFSR taps. XOR all 6 state bytes
* into a single bit. */
Feedback ^= StateEven[0] & (uint8_t) (LFSR_MASK_EVEN >> 0);
Feedback ^= StateEven[1] & (uint8_t) (LFSR_MASK_EVEN >> 8);
Feedback ^= StateEven[2] & (uint8_t) (LFSR_MASK_EVEN >> 16);
Feedback ^= StateOdd[0] & (uint8_t) (LFSR_MASK_ODD >> 0);
Feedback ^= StateOdd[1] & (uint8_t) (LFSR_MASK_ODD >> 8);
Feedback ^= StateOdd[2] & (uint8_t) (LFSR_MASK_ODD >> 16);
Feedback ^= Feedback >> 4;
Feedback ^= Feedback >> 2;
Feedback ^= Feedback >> 1;
/* Now the shifting of the Crypto1 state gets more complicated when
* split up into even/odd parts. After some hard thinking, one can
* see that after one LFSR clock cycle
* - the new even state becomes the old odd state
* - the new odd state becomes the old even state right-shifted by 1.
* For shifting the even state, we convert it into a 32 bit int first */
uint32_t Temp = 0;
Temp |= ((uint32_t) StateEven[0] << 0);
Temp |= ((uint32_t) StateEven[1] << 8);
Temp |= ((uint32_t) StateEven[2] << 16);
/* Proceed LFSR. Try to force compiler not to shift the unneded upper bits. */
Temp = (Temp >> 1) & 0x00FFFFFF;
/* Calculate MSBit of even state as input bit to LFSR */
if ( (Feedback & 0x01) ^ In ) {
Temp |= (uint32_t) 1 << (8 * LFSR_SIZE/2 - 1);
}
/* Convert even state back into byte array and swap odd/even state
* as explained above. */
StateEven[0] = StateOdd[0];
StateEven[1] = StateOdd[1];
StateEven[2] = StateOdd[2];
StateOdd[0] = (uint8_t) (Temp >> 0);
StateOdd[1] = (uint8_t) (Temp >> 8);
StateOdd[2] = (uint8_t) (Temp >> 16);
}
uint8_t Crypto1FilterOutput(void) {
/* Calculate the functions fa, fb.
* Note that only bits {4...23} of the odd state
* get fed into these function.
* The tables are designed to hold mask values, which
* can simply be ORed together to produce the resulting
* 5 bits that are used to lookup the output bit.
*/
uint8_t Sum = 0;
Sum |= TableAB[0][(StateOdd[0] >> 4) & 0x0F];
Sum |= TableAB[1][(StateOdd[1] >> 0) & 0x0F];
Sum |= TableAB[2][(StateOdd[1] >> 4) & 0x0F];
Sum |= TableAB[3][(StateOdd[2] >> 0) & 0x0F];
Sum |= TableAB[4][(StateOdd[2] >> 4) & 0x0F];
return TableC[Sum];
}
void Crypto1Setup(uint8_t Key[6], uint8_t Uid[4], uint8_t CardNonce[4])
{
uint8_t i;
/* Again, one trade off when splitting up the state into even/odd parts
* is that loading the key into the state becomes a little more difficult.
* The inner loop generates 8 even and 8 odd bits from 16 key bits and
* the outer loop stores them. */
for (i=0; i<(LFSR_SIZE/2); i++) {
uint8_t EvenByte = 0;
uint8_t OddByte = 0;
uint16_t KeyWord = ((uint16_t) Key[2*i+1] << 8) | Key[2*i+0];
uint8_t j;
for (j=0; j<8; j++) {
EvenByte >>= 1;
OddByte >>= 1;
if (KeyWord & (1<<0)) {
EvenByte |= 0x80;
}
if (KeyWord & (1<<1)) {
OddByte |= 0x80;
}
KeyWord >>= 2;
}
StateEven[i] = EvenByte;
StateOdd[i] = OddByte;
}
/* Use Uid XOR CardNonce as feed-in and do 32 clocks on the
* Crypto1 LFSR.*/
uint32_t Temp = 0;
Temp |= (uint32_t) (Uid[0] ^ CardNonce[0]) << 0;
Temp |= (uint32_t) (Uid[1] ^ CardNonce[1]) << 8;
Temp |= (uint32_t) (Uid[2] ^ CardNonce[2]) << 16;
Temp |= (uint32_t) (Uid[3] ^ CardNonce[3]) << 24;
for (i=0; i<32; i++) {
uint8_t Out = Crypto1FilterOutput();
Crypto1LFSR(Temp & 0x01);
Temp >>= 1;
/* Store the keystream for later use */
if (Out) {
Temp |= (uint32_t) 1 << 31;
}
}
/* Crypto1 state register is now set up to be used for authentication.
* In case of nested authentication, we need to use the produced keystream
* to encrypt the CardNonce. For this case we do the encryption in-place. */
CardNonce[0] ^= (uint8_t) (Temp >> 0);
CardNonce[1] ^= (uint8_t) (Temp >> 8);
CardNonce[2] ^= (uint8_t) (Temp >> 16);
CardNonce[3] ^= (uint8_t) (Temp >> 24);
}
void Crypto1Auth(uint8_t EncryptedReaderNonce[4])
{
uint32_t Temp = 0;
/* For ease of processing, we convert the encrypted reader nonce
* into a 32 bit integer */
Temp |= (uint32_t) EncryptedReaderNonce[0] << 0;
Temp |= (uint32_t) EncryptedReaderNonce[1] << 8;
Temp |= (uint32_t) EncryptedReaderNonce[2] << 16;
Temp |= (uint32_t) EncryptedReaderNonce[3] << 24;
uint8_t i;
for (i=0; i<32; i++) {
/* Decrypt one output bit of the given encrypted nonce using the
* filter output as keystream. */
uint8_t Out = Crypto1FilterOutput();
uint8_t Bit = Out ^ (Temp & 0x01);
/* Feed back the bit to load the LFSR with the (decrypted) nonce */
Crypto1LFSR(Bit);
Temp >>= 1;
}
}
uint8_t Crypto1Byte(void)
{
uint8_t KeyStream = 0;
uint8_t i;
/* Generate 8 keystream-bits */
for (i=0; i<8; i++) {
/* Calculate output of function-network and cycle LFSR with no
* additional input, thus linearly! */
uint8_t Out = Crypto1FilterOutput();
Crypto1LFSR(0);
/* Store keystream bit */
KeyStream >>= 1;
if (Out) {
KeyStream |= (1<<7);
}
}
return KeyStream;
}
uint8_t Crypto1Nibble(void)
{
uint8_t KeyStream = 0;
uint8_t i;
/* Generate 4 keystream-bits */
for (i=0; i<4; i++) {
/* Calculate output of function-network and cycle LFSR with no
* additional input, thus linearly! */
uint8_t Out = Crypto1FilterOutput();
Crypto1LFSR(0);
/* Store keystream bit */
KeyStream >>= 1;
if (Out) {
KeyStream |= (1<<3);
}
}
return KeyStream;
}
void Crypto1PRNG(uint8_t State[4], uint16_t ClockCount)
{
while(ClockCount--) {
/* Actually, the PRNG is a 32 bit register with the upper 16 bit
* used as a LFSR. Furthermore only mask-byte 2 contains feedback at all.
* We rely on the compiler to optimize this for us here.
* XOR all tapped bits to a single feedback bit. */
uint8_t Feedback = 0;
Feedback ^= State[0] & (uint8_t) (PRNG_MASK >> 0);
Feedback ^= State[1] & (uint8_t) (PRNG_MASK >> 8);
Feedback ^= State[2] & (uint8_t) (PRNG_MASK >> 16);
Feedback ^= State[3] & (uint8_t) (PRNG_MASK >> 24);
Feedback ^= Feedback >> 4;
Feedback ^= Feedback >> 2;
Feedback ^= Feedback >> 1;
/* For ease of processing convert the state into a 32 bit integer first */
uint32_t Temp = 0;
Temp |= (uint32_t) State[0] << 0;
Temp |= (uint32_t) State[1] << 8;
Temp |= (uint32_t) State[2] << 16;
Temp |= (uint32_t) State[3] << 24;
/* Cycle LFSR and feed back. */
Temp >>= 1;
if (Feedback & 0x01) {
Temp |= (uint32_t) 1 << (8 * PRNG_SIZE - 1);
}
/* Store back state */
State[0] = (uint8_t) (Temp >> 0);
State[1] = (uint8_t) (Temp >> 8);
State[2] = (uint8_t) (Temp >> 16);
State[3] = (uint8_t) (Temp >> 24);
}
}
@@ -0,0 +1,25 @@
#ifndef CRYPTO1_H
#define CRYPTO1_H
#include <stdint.h>
/* Gets the current keystream-bit, without shifting the internal LFSR */
uint8_t Crypto1FilterOutput(void);
/* Set up Crypto1 cipher using the given Key, Uid and CardNonce. Also encrypts
* the CardNonce in-place while in non-linear mode. */
void Crypto1Setup(uint8_t Key[6], uint8_t Uid[4], uint8_t CardNonce[4]);
/* Load the decrypted ReaderNonce into the Crypto1 state LFSR */
void Crypto1Auth(uint8_t EncryptedReaderNonce[4]);
/* Generate 8 Bits of key stream */
uint8_t Crypto1Byte(void);
/* Generate 4 Bits of key stream */
uint8_t Crypto1Nibble(void);
/* Execute 'ClockCount' cycles on the PRNG state 'State' */
void Crypto1PRNG(uint8_t State[4], uint16_t ClockCount);
#endif //CRYPTO1_H
@@ -0,0 +1,108 @@
/*
* ISO14443A.c
*
* Created on: 19.03.2013
* Author: skuser
*/
#include "ISO14443-3A.h"
void ISO14443AAppendCRCA(void* Buffer, uint16_t ByteCount) {
uint16_t Checksum = 0x6363;
uint8_t* DataPtr = (uint8_t*) Buffer;
while(ByteCount--) {
uint8_t Byte = *DataPtr++;
Byte ^= (uint8_t) (Checksum & 0x00FF);
Byte ^= Byte << 4;
Checksum = (Checksum >> 8) ^ ( (uint16_t) Byte << 8 ) ^
( (uint16_t) Byte << 3 ) ^ ( (uint16_t) Byte >> 4 );
}
*DataPtr++ = (Checksum >> 0) & 0x00FF;
*DataPtr = (Checksum >> 8) & 0x00FF;
}
bool ISO14443ACheckCRCA(void* Buffer, uint16_t ByteCount)
{
uint16_t Checksum = 0x6363;
uint8_t* DataPtr = (uint8_t*) Buffer;
while(ByteCount--) {
uint8_t Byte = *DataPtr++;
Byte ^= (uint8_t) (Checksum & 0x00FF);
Byte ^= Byte << 4;
Checksum = (Checksum >> 8) ^ ( (uint16_t) Byte << 8 ) ^
( (uint16_t) Byte << 3 ) ^ ( (uint16_t) Byte >> 4 );
}
return (DataPtr[0] == ((Checksum >> 0) & 0xFF)) && (DataPtr[1] == ((Checksum >> 8) & 0xFF));
}
#if 0
bool ISO14443ASelect(void* Buffer, uint16_t* BitCount, uint8_t* UidCL, uint8_t SAKValue)
{
uint8_t* DataPtr = (uint8_t*) Buffer;
uint8_t NVB = DataPtr[1];
//uint8_t CollisionByteCount = (NVB >> 4) & 0x0F;
//uint8_t CollisionBitCount = (NVB >> 0) & 0x0F;
switch (NVB) {
case ISO14443A_NVB_AC_START:
/* Start of anticollision procedure.
* Send whole UID CLn + BCC */
DataPtr[0] = UidCL[0];
DataPtr[1] = UidCL[1];
DataPtr[2] = UidCL[2];
DataPtr[3] = UidCL[3];
DataPtr[4] = ISO14443A_CALC_BCC(DataPtr);
*BitCount = ISO14443A_CL_FRAME_SIZE;
return false;
case ISO14443A_NVB_AC_END:
/* End of anticollision procedure.
* Send SAK CLn if we are selected. */
if ( (DataPtr[2] == UidCL[0]) &&
(DataPtr[3] == UidCL[1]) &&
(DataPtr[4] == UidCL[2]) &&
(DataPtr[5] == UidCL[3]) ) {
DataPtr[0] = SAKValue;
ISO14443AAppendCRCA(Buffer, 1);
*BitCount = ISO14443A_SAK_FRAME_SIZE;
return true;
} else {
/* We have not been selected. Don't send anything. */
*BitCount = 0;
return false;
}
default:
/* TODO: No anticollision supported */
*BitCount = 0;
return false;
}
}
bool ISO14443AWakeUp(void* Buffer, uint16_t* BitCount, uint16_t ATQAValue)
{
uint8_t* DataPtr = (uint8_t*) Buffer;
if ( (DataPtr[0] == ISO14443A_CMD_REQA) || (DataPtr[0] == ISO14443A_CMD_WUPA) ){
DataPtr[0] = (ATQAValue >> 0) & 0x00FF;
DataPtr[1] = (ATQAValue >> 8) & 0x00FF;
*BitCount = ISO14443A_ATQA_FRAME_SIZE;
return true;
} else {
return false;
}
}
#endif
@@ -0,0 +1,117 @@
/*
* ISO14443-2A.h
*
* Created on: 19.03.2013
* Author: skuser
*/
#ifndef ISO14443_3A_H_
#define ISO14443_3A_H_
#include "../Common.h"
#define ISO14443A_UID_SIZE_SINGLE 4 /* bytes */
#define ISO14443A_UID_SIZE_DOUBLE 7
#define ISO14443A_UID_SIZE_TRIPLE 10
#define ISO14443A_CMD_REQA 0x26
#define ISO14443A_CMD_WUPA 0x52
#define ISO14443A_CMD_SELECT_CL1 0x93
#define ISO14443A_CMD_SELECT_CL2 0x95
#define ISO14443A_CMD_SELECT_CL3 0x97
#define ISO14443A_CMD_HLTA 0x50
#define ISO14443A_NVB_AC_START 0x20
#define ISO14443A_NVB_AC_END 0x70
#define ISO14443A_CL_UID_OFFSET 0
#define ISO14443A_CL_UID_SIZE 4
#define ISO14443A_CL_BCC_OFFSET 4
#define ISO14443A_CL_BCC_SIZE 1 /* Byte */
#define ISO14443A_CL_FRAME_SIZE ((ISO14443A_CL_UID_SIZE + ISO14443A_CL_BCC_SIZE) * 8) /* UID[N...N+3] || BCCN */
#define ISO14443A_SAK_INCOMPLETE 0x04
#define ISO14443A_SAK_COMPLETE_COMPLIANT 0x20
#define ISO14443A_SAK_COMPLETE_NOT_COMPLIANT 0x00
#define ISO14443A_ATQA_FRAME_SIZE (2 * 8) /* Bit */
#define ISO14443A_SAK_FRAME_SIZE (3 * 8) /* Bit */
#define ISO14443A_UID0_RANDOM 0x08
#define ISO14443A_UID0_CT 0x88
#define ISO14443A_CRCA_SIZE 2
#define ISO14443A_CALC_BCC(ByteBuffer) \
( ByteBuffer[0] ^ ByteBuffer[1] ^ ByteBuffer[2] ^ ByteBuffer[3] )
void ISO14443AAppendCRCA(void* Buffer, uint16_t ByteCount);
bool ISO14443ACheckCRCA(void* Buffer, uint16_t ByteCount);
INLINE bool ISO14443ASelect(void* Buffer, uint16_t* BitCount, uint8_t* UidCL, uint8_t SAKValue);
INLINE bool ISO14443AWakeUp(void* Buffer, uint16_t* BitCount, uint16_t ATQAValue);
INLINE
bool ISO14443ASelect(void* Buffer, uint16_t* BitCount, uint8_t* UidCL, uint8_t SAKValue)
{
uint8_t* DataPtr = (uint8_t*) Buffer;
uint8_t NVB = DataPtr[1];
//uint8_t CollisionByteCount = (NVB >> 4) & 0x0F;
//uint8_t CollisionBitCount = (NVB >> 0) & 0x0F;
switch (NVB) {
case ISO14443A_NVB_AC_START:
/* Start of anticollision procedure.
* Send whole UID CLn + BCC */
DataPtr[0] = UidCL[0];
DataPtr[1] = UidCL[1];
DataPtr[2] = UidCL[2];
DataPtr[3] = UidCL[3];
DataPtr[4] = ISO14443A_CALC_BCC(DataPtr);
*BitCount = ISO14443A_CL_FRAME_SIZE;
return false;
case ISO14443A_NVB_AC_END:
/* End of anticollision procedure.
* Send SAK CLn if we are selected. */
if ( (DataPtr[2] == UidCL[0]) &&
(DataPtr[3] == UidCL[1]) &&
(DataPtr[4] == UidCL[2]) &&
(DataPtr[5] == UidCL[3]) ) {
DataPtr[0] = SAKValue;
ISO14443AAppendCRCA(Buffer, 1);
*BitCount = ISO14443A_SAK_FRAME_SIZE;
return true;
} else {
/* We have not been selected. Don't send anything. */
*BitCount = 0;
return false;
}
default:
/* TODO: No anticollision supported */
*BitCount = 0;
return false;
}
}
INLINE
bool ISO14443AWakeUp(void* Buffer, uint16_t* BitCount, uint16_t ATQAValue)
{
uint8_t* DataPtr = (uint8_t*) Buffer;
if ( (DataPtr[0] == ISO14443A_CMD_REQA) || (DataPtr[0] == ISO14443A_CMD_WUPA) ){
DataPtr[0] = (ATQAValue >> 0) & 0x00FF;
DataPtr[1] = (ATQAValue >> 8) & 0x00FF;
*BitCount = ISO14443A_ATQA_FRAME_SIZE;
return true;
} else {
return false;
}
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,30 @@
/*
* MifareClassic.h
*
* Created on: 13.05.2013
* Author: skuser
*/
#ifndef MIFARECLASSIC_H_
#define MIFARECLASSIC_H_
#include "Application.h"
#include "ISO14443-3A.h"
#define MIFARE_CLASSIC_UID_SIZE ISO14443A_UID_SIZE_SINGLE
#define MIFARE_CLASSIC_1K_MEM_SIZE 1024
#define MIFARE_CLASSIC_4K_MEM_SIZE 4096
void MifareClassicAppInit1K(void);
void MifarePlus1kAppInit_7B(void);
void MifareClassicAppInit4K(void);
void MifareClassicAppReset(void);
void MifareClassicAppTask(void);
uint16_t MifareClassicAppProcess(uint8_t* Buffer, uint16_t BitCount);
void MifareClassicGetUid(ConfigurationUidType Uid);
void MifareClassicSetUid(ConfigurationUidType Uid);
#endif /* MIFARECLASSIC_H_ */
@@ -0,0 +1,288 @@
/*
* MifareUltralight.c
*
* Created on: 20.03.2013
* Author: skuser
*/
#include "MifareUltralight.h"
#include "ISO14443-3A.h"
#include "../Codec/ISO14443-2A.h"
#include "../Memory.h"
#define ATQA_VALUE 0x0044
#define SAK_CL1_VALUE ISO14443A_SAK_INCOMPLETE
#define SAK_CL2_VALUE ISO14443A_SAK_COMPLETE_NOT_COMPLIANT
#define ACK_VALUE 0x0A
#define ACK_FRAME_SIZE 4 /* Bits */
#define NAK_INVALID_ARG 0x00
#define NAK_CRC_ERROR 0x01
#define NAK_EEPROM_ERROR 0x05
#define NAK_OTHER_ERROR 0x06
#define NAK_FRAME_SIZE 4
#define CMD_READ 0x30
#define CMD_READ_FRAME_SIZE 2 /* without CRC bytes */
#define CMD_WRITE 0xA2
#define CMD_WRITE_FRAME_SIZE 6 /* without CRC bytes */
#define CMD_COMPAT_WRITE 0xA0
#define CMD_COMPAT_WRITE_FRAME_SIZE 2
#define CMD_HALT 0x50
#define UID_CL1_ADDRESS 0x00 /* In Card Memory */
#define UID_CL1_SIZE 3 /* In Bytes */
#define UID_BCC1_ADDRESS 0x03
#define UID_CL2_ADDRESS 0x04
#define UID_CL2_SIZE 4
#define UID_BCC2_ADDRESS 0x08
#define BYTES_PER_PAGE 4
#define PAGE_ADDRESS_MASK 0x0F
#define BYTES_PER_READ 16
#define PAGE_READ_MIN 0x00
#define PAGE_READ_MAX 0x0F
#define BYTES_PER_WRITE 4
#define PAGE_WRITE_MIN 0x02
#define PAGE_WRITE_MAX 0x0F
#define BYTES_PER_COMPAT_WRITE 16
static enum {
STATE_HALT,
STATE_IDLE,
STATE_READY1,
STATE_READY2,
STATE_ACTIVE,
STATE_COMPAT_WRITE
} State;
static uint8_t CompatWritePageAddress;
void MifareUltralightAppInit(void)
{
State = STATE_IDLE;
}
void MifareUltralightAppReset(void)
{
State = STATE_IDLE;
}
void MifareUltralightAppTask(void)
{
}
uint16_t MifareUltralightAppProcess(uint8_t* Buffer, uint16_t BitCount)
{
uint8_t Cmd = Buffer[0];
switch(State) {
case STATE_IDLE:
case STATE_HALT:
if (ISO14443AWakeUp(Buffer, &BitCount, ATQA_VALUE)) {
/* We received a REQA or WUPA command, so wake up. */
State = STATE_READY1;
return BitCount;
}
break;
case STATE_READY1:
if (ISO14443AWakeUp(Buffer, &BitCount, ATQA_VALUE)) {
State = STATE_READY1;
return BitCount;
} else if (Cmd == ISO14443A_CMD_SELECT_CL1) {
/* Load UID CL1 and perform anticollision. Since
* MF Ultralight use a double-sized UID, the first byte
* of CL1 has to be the cascade-tag byte. */
uint8_t UidCL1[ISO14443A_CL_UID_SIZE] = { [0] = ISO14443A_UID0_CT };
MemoryReadBlock(&UidCL1[1], UID_CL1_ADDRESS, UID_CL1_SIZE);
if (ISO14443ASelect(Buffer, &BitCount, UidCL1, SAK_CL1_VALUE)) {
/* CL1 stage has ended successfully */
State = STATE_READY2;
}
return BitCount;
} else {
/* Unknown command. Enter halt state */
State = STATE_IDLE;
}
break;
case STATE_READY2:
if (ISO14443AWakeUp(Buffer, &BitCount, ATQA_VALUE)) {
State = STATE_READY1;
return BitCount;
} else if (Cmd == ISO14443A_CMD_SELECT_CL2) {
/* Load UID CL2 and perform anticollision */
uint8_t UidCL2[ISO14443A_CL_UID_SIZE];
MemoryReadBlock(UidCL2, UID_CL2_ADDRESS, UID_CL2_SIZE);
if (ISO14443ASelect(Buffer, &BitCount, UidCL2, SAK_CL2_VALUE)) {
/* CL2 stage has ended successfully. This means
* our complete UID has been sent to the reader. */
State = STATE_ACTIVE;
}
return BitCount;
} else {
/* Unknown command. Enter halt state */
State = STATE_IDLE;
}
break;
case STATE_ACTIVE:
if (ISO14443AWakeUp(Buffer, &BitCount, ATQA_VALUE)) {
State = STATE_READY1;
return BitCount;
} else if (Cmd == CMD_READ) {
uint8_t PageAddress = Buffer[1];
if (ISO14443ACheckCRCA(Buffer, CMD_READ_FRAME_SIZE)) {
if ( (PageAddress >= PAGE_READ_MIN)
&& (PageAddress <= PAGE_READ_MAX) ) {
/* TODO: Missing address wrap around behaviour.
* Implement using a for-loop copying 4 bytes each iteration
* and mask pageaddress */
MemoryReadBlock(Buffer, PageAddress * BYTES_PER_PAGE, BYTES_PER_READ);
ISO14443AAppendCRCA(Buffer, BYTES_PER_READ);
return (BYTES_PER_READ + ISO14443A_CRCA_SIZE) * 8;
} else {
Buffer[0] = NAK_INVALID_ARG;
return NAK_FRAME_SIZE;
}
} else {
Buffer[0] = NAK_CRC_ERROR;
return NAK_FRAME_SIZE;
}
} else if (Cmd == CMD_WRITE) {
/* This is a write command containing 4 bytes of data that
* should be written to the given page address. */
uint8_t PageAddress = Buffer[1];
if (ISO14443ACheckCRCA(Buffer, CMD_WRITE_FRAME_SIZE)) {
/* CRC check passed */
if ( (PageAddress >= PAGE_WRITE_MIN)
&& (PageAddress <= PAGE_WRITE_MAX) ) {
/* PageAddress is within bounds. */
if (!ActiveConfiguration.ReadOnly) {
MemoryWriteBlock(&Buffer[2], PageAddress * BYTES_PER_PAGE, BYTES_PER_WRITE);
} else {
/* If the chameleon is in read only mode, it silently
* ignores any attempt to write data. */
}
Buffer[0] = ACK_VALUE;
return ACK_FRAME_SIZE;
} else {
Buffer[0] = NAK_INVALID_ARG;
return NAK_FRAME_SIZE;
}
} else {
Buffer[0] = NAK_CRC_ERROR;
return NAK_FRAME_SIZE;
}
} else if (Cmd == CMD_COMPAT_WRITE) {
/* The Mifare compatbility write command is a 2-frame command.
* The first frame contains the page-address and the second frame
* holds the data. */
uint8_t PageAddress = Buffer[1];
if (ISO14443ACheckCRCA(Buffer, CMD_COMPAT_WRITE_FRAME_SIZE)) {
if ( (PageAddress >= PAGE_WRITE_MIN)
&& (PageAddress <= PAGE_WRITE_MAX) ) {
/* CRC check passed and page-address is within bounds.
* Store address and proceed to receiving the data. */
CompatWritePageAddress = PageAddress;
State = STATE_COMPAT_WRITE;
Buffer[0] = ACK_VALUE;
return ACK_FRAME_SIZE;
} else {
Buffer[0] = NAK_INVALID_ARG;
return NAK_FRAME_SIZE;
}
} else {
Buffer[0] = NAK_CRC_ERROR;
return NAK_FRAME_SIZE;
}
} else if (Cmd == CMD_HALT) {
/* Halts the tag. According to the ISO14443, the second
* byte is supposed to be 0. */
if (Buffer[1] == 0) {
if (ISO14443ACheckCRCA(Buffer, 2)) {
/* According to ISO14443, we must not send anything
* in order to acknowledge the HALT command. */
State = STATE_HALT;
return ISO14443A_APP_NO_RESPONSE;
} else {
Buffer[0] = NAK_CRC_ERROR;
return NAK_FRAME_SIZE;
}
} else {
Buffer[0] = NAK_INVALID_ARG;
return NAK_FRAME_SIZE;
}
} else {
/* Unknown command. Enter halt state */
State = STATE_IDLE;
}
break;
case STATE_COMPAT_WRITE:
/* Compatibility write. Receiving 16 bytes of data of which 4 bytes are valid. */
if (ISO14443ACheckCRCA(Buffer, BYTES_PER_COMPAT_WRITE)) {
/* We don't perform any checks here. You will be able to program the
* whole memory. Also there is no OTP behaviour. */
if (!ActiveConfiguration.ReadOnly) {
MemoryWriteBlock(Buffer, CompatWritePageAddress * BYTES_PER_PAGE, BYTES_PER_WRITE);
} else {
/* If we are told to be read only, we silently ignore the write command
* and pretend to have written data. */
}
State = STATE_ACTIVE;
Buffer[0] = ACK_VALUE;
return ACK_FRAME_SIZE;
} else {
State = STATE_ACTIVE;
Buffer[0] = NAK_CRC_ERROR;
return NAK_FRAME_SIZE;
}
default:
/* Unknown state? Should never happen. */
break;
}
/* No response has been sent, when we reach here */
return ISO14443A_APP_NO_RESPONSE;
}
void MifareUltralightGetUid(ConfigurationUidType Uid)
{
/* Read UID from memory */
MemoryReadBlock(&Uid[0], UID_CL1_ADDRESS, UID_CL1_SIZE);
MemoryReadBlock(&Uid[UID_CL1_SIZE], UID_CL2_ADDRESS, UID_CL2_SIZE);
}
void MifareUltralightSetUid(ConfigurationUidType Uid)
{
/* Calculate check bytes and write everything into memory */
uint8_t BCC1 = ISO14443A_UID0_CT ^ Uid[0] ^ Uid[1] ^ Uid[2];
uint8_t BCC2 = Uid[3] ^ Uid[4] ^ Uid[5] ^ Uid[6];
MemoryWriteBlock(&Uid[0], UID_CL1_ADDRESS, UID_CL1_SIZE);
MemoryWriteBlock(&BCC1, UID_BCC1_ADDRESS, ISO14443A_CL_BCC_SIZE);
MemoryWriteBlock(&Uid[UID_CL1_SIZE], UID_CL2_ADDRESS, UID_CL2_SIZE);
MemoryWriteBlock(&BCC2, UID_BCC2_ADDRESS, ISO14443A_CL_BCC_SIZE);
}
@@ -0,0 +1,28 @@
/*
* MifareUltralight.h
*
* Created on: 20.03.2013
* Author: skuser
*/
#ifndef MIFAREULTRALIGHT_H_
#define MIFAREULTRALIGHT_H_
#include "Application.h"
#include "ISO14443-3A.h"
#define MIFARE_ULTRALIGHT_UID_SIZE ISO14443A_UID_SIZE_DOUBLE
#define MIFARE_ULTRALIGHT_MEM_SIZE 64
void MifareUltralightAppInit(void);
void MifareUltralightAppReset(void);
void MifareUltralightAppTask(void);
uint16_t MifareUltralightAppProcess(uint8_t* Buffer, uint16_t BitCount);
void MifareUltralightGetUid(ConfigurationUidType Uid);
void MifareUltralightSetUid(ConfigurationUidType Uid);
#endif /* MIFAREULTRALIGHT_H_ */
@@ -0,0 +1,36 @@
/*
* Battery.h
*
* Created on: 20.08.2014
* Author: sk
*/
#ifndef BATTERY_H_
#define BATTERY_H_
#include "Common.h"
#define BATTERY_PORT PORTD
#define BATTERY_STAT_PIN PIN4_bm
#define BATTERY_STAT_PINCTRL PIN4CTRL
#define BATTERY_PORT_MASK (BATTERY_STAT_PIN)
INLINE void BatteryInit(void)
{
BATTERY_PORT.DIRCLR = BATTERY_PORT_MASK;
BATTERY_PORT.BATTERY_STAT_PINCTRL = PORT_OPC_PULLUP_gc;
}
INLINE bool BatteryIsCharging(void)
{
if (!(BATTERY_PORT.IN & BATTERY_STAT_PIN))
{
return true;
}
else
{
return false;
}
}
#endif /* BATTERY_H_ */
+217
View File
@@ -0,0 +1,217 @@
#include "Button.h"
#include "Random.h"
#include "Common.h"
#include "Settings.h"
#include "Memory.h"
#include "Map.h"
#include "Application/Application.h"
#define BUTTON_PORT PORTA
#define BUTTON_L PIN3_bm
#define BUTTON_R PIN6_bm
#define BUTTON_L_PINCTRL PIN3CTRL
#define BUTTON_R_PINCTRL PIN6CTRL
#define BUTTON_MASK (BUTTON_L | BUTTON_R)
#define LONG_PRESS_TICK_COUNT 10
static const MapEntryType PROGMEM ButtonActionMap[] = {
{ .Id = BUTTON_ACTION_NONE, .Text = "NONE" },
{ .Id = BUTTON_ACTION_UID_RANDOM, .Text = "UID_RANDOM" },
{ .Id = BUTTON_ACTION_UID_LEFT_INCREMENT, .Text = "UID_LEFT_INCREMENT" },
{ .Id = BUTTON_ACTION_UID_RIGHT_INCREMENT, .Text = "UID_RIGHT_INCREMENT" },
{ .Id = BUTTON_ACTION_UID_LEFT_DECREMENT, .Text = "UID_LEFT_DECREMENT" },
{ .Id = BUTTON_ACTION_UID_RIGHT_DECREMENT, .Text = "UID_RIGHT_DECREMENT" },
{ .Id = BUTTON_ACTION_CYCLE_SETTINGS, .Text = "CYCLE_SETTINGS" },
{ .Id = BUTTON_ACTION_STORE_MEM, .Text = "STORE_MEM" },
{ .Id = BUTTON_ACTION_RECALL_MEM, .Text = "RECALL_MEM" },
};
static void ExecuteButtonAction(ButtonActionEnum ButtonAction)
{
uint8_t UidBuffer[32];
if (ButtonAction == BUTTON_ACTION_UID_RANDOM) {
for (uint8_t i=0; i<ActiveConfiguration.UidSize; i++) {
UidBuffer[i] = RandomGetByte();
}
ApplicationSetUid(UidBuffer);
} else if (ButtonAction == BUTTON_ACTION_UID_LEFT_INCREMENT) {
ApplicationGetUid(UidBuffer);
bool Carry = 1;
uint8_t i;
for (i=0; i<ActiveConfiguration.UidSize; i++) {
if (Carry) {
if (UidBuffer[i] == 0xFF) {
Carry = 1;
} else {
Carry = 0;
}
UidBuffer[i] = (UidBuffer[i] + 1) & 0xFF;
}
}
ApplicationSetUid(UidBuffer);
} else if (ButtonAction == BUTTON_ACTION_UID_RIGHT_INCREMENT) {
ApplicationGetUid(UidBuffer);
bool Carry = 1;
uint8_t i = ActiveConfiguration.UidSize;
while(i-- > 0) {
if (Carry) {
if (UidBuffer[i] == 0xFF) {
Carry = 1;
} else {
Carry = 0;
}
UidBuffer[i] = (UidBuffer[i] + 1) & 0xFF;
}
}
ApplicationSetUid(UidBuffer);
} else if (ButtonAction == BUTTON_ACTION_UID_LEFT_DECREMENT) {
ApplicationGetUid(UidBuffer);
bool Carry = 1;
uint8_t i;
for (i=0; i<ActiveConfiguration.UidSize; i++) {
if (Carry) {
if (UidBuffer[i] == 0x00) {
Carry = 1;
} else {
Carry = 0;
}
UidBuffer[i] = (UidBuffer[i] - 1) & 0xFF;
}
}
ApplicationSetUid(UidBuffer);
} else if (ButtonAction == BUTTON_ACTION_UID_RIGHT_DECREMENT) {
ApplicationGetUid(UidBuffer);
bool Carry = 1;
uint8_t i = ActiveConfiguration.UidSize;
while(i-- > 0) {
if (Carry) {
if (UidBuffer[i] == 0x00) {
Carry = 1;
} else {
Carry = 0;
}
UidBuffer[i] = (UidBuffer[i] - 1) & 0xFF;
}
}
ApplicationSetUid(UidBuffer);
} else if (ButtonAction == BUTTON_ACTION_CYCLE_SETTINGS) {
SettingsCycle();
} else if (ButtonAction == BUTTON_ACTION_STORE_MEM) {
MemoryStore();
} else if (ButtonAction == BUTTON_ACTION_RECALL_MEM) {
MemoryRecall();
}
}
void ButtonInit(void)
{
BUTTON_PORT.DIRCLR = BUTTON_MASK;
BUTTON_PORT.BUTTON_R_PINCTRL = PORT_OPC_PULLUP_gc;
BUTTON_PORT.BUTTON_L_PINCTRL = PORT_OPC_PULLUP_gc;
}
void ButtonTick(void)
{
static uint8_t ButtonRPressTick = 0;
static uint8_t ButtonLPressTick = 0;
uint8_t ThisButtonState = ~BUTTON_PORT.IN;
if (ThisButtonState & BUTTON_R) {
/* Button is currently pressed */
if (ButtonRPressTick < LONG_PRESS_TICK_COUNT) {
/* Count ticks while button is being pressed */
ButtonRPressTick++;
} else if (ButtonRPressTick == LONG_PRESS_TICK_COUNT) {
/* Long button press detected execute button action and advance PressTickCounter
* to an invalid state. */
ExecuteButtonAction(GlobalSettings.ActiveSettingPtr->ButtonActions[BUTTON_R_PRESS_LONG]);
ButtonRPressTick++;
} else {
/* Button is still pressed, ignore */
}
} else if (!(ThisButtonState & BUTTON_MASK)) {
/* Button is currently not being pressed. Check if PressTickCounter contains
* a recent short button press. */
if ( (ButtonRPressTick > 0) && (ButtonRPressTick <= LONG_PRESS_TICK_COUNT) ) {
/* We have a short button press */
ExecuteButtonAction(GlobalSettings.ActiveSettingPtr->ButtonActions[BUTTON_R_PRESS_SHORT]);
}
ButtonRPressTick = 0;
}
if (ThisButtonState & BUTTON_L) {
/* Button is currently pressed */
if (ButtonLPressTick < LONG_PRESS_TICK_COUNT) {
/* Count ticks while button is being pressed */
ButtonLPressTick++;
} else if (ButtonLPressTick == LONG_PRESS_TICK_COUNT) {
/* Long button press detected execute button action and advance PressTickCounter
* to an invalid state. */
ExecuteButtonAction(GlobalSettings.ActiveSettingPtr->ButtonActions[BUTTON_L_PRESS_LONG]);
ButtonLPressTick++;
} else {
/* Button is still pressed, ignore */
}
} else if (!(ThisButtonState & BUTTON_MASK)) {
/* Button is currently not being pressed. Check if PressTickCounter contains
* a recent short button press. */
if ( (ButtonLPressTick > 0) && (ButtonLPressTick <= LONG_PRESS_TICK_COUNT) ) {
/* We have a short button press */
ExecuteButtonAction(GlobalSettings.ActiveSettingPtr->ButtonActions[BUTTON_L_PRESS_SHORT]);
}
ButtonLPressTick = 0;
}
}
void ButtonGetActionList(char* List, uint16_t BufferSize)
{
MapToString(ButtonActionMap, sizeof(ButtonActionMap)/sizeof(*ButtonActionMap), List, BufferSize);
}
void ButtonSetActionById(ButtonTypeEnum Type, ButtonActionEnum Action)
{
#ifndef BUTTON_SETTING_GLOBAL
GlobalSettings.ActiveSettingPtr->ButtonActions[Type] = Action;
#else
/* Write button action to all settings when using global settings */
for (uint8_t i=0; i<SETTINGS_COUNT; i++) {
GlobalSettings.Settings[i].ButtonActions[Type] = Action;
}
#endif
}
void ButtonGetActionByName(ButtonTypeEnum Type, char* Action, uint16_t BufferSize)
{
MapIdToText(ButtonActionMap, sizeof(ButtonActionMap)/sizeof(*ButtonActionMap),
GlobalSettings.ActiveSettingPtr->ButtonActions[Type], Action, BufferSize);
}
bool ButtonSetActionByName(ButtonTypeEnum Type, const char* Action)
{
MapIdType Id;
if (MapTextToId(ButtonActionMap, sizeof(ButtonActionMap)/sizeof(*ButtonActionMap),
Action, &Id)) {
ButtonSetActionById(Type, Id);
return true;
} else {
return false;
}
}

Some files were not shown because too many files have changed in this diff Show More