commit
56a935cdd0
@ -0,0 +1,72 @@ |
|||||||
|
//
|
||||||
|
// Rename to TF_Config.h
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef TF_CONFIG_H |
||||||
|
#define TF_CONFIG_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
//#include <esp8266.h> // when using with esphttpd
|
||||||
|
|
||||||
|
//----------------------------- FRAME FORMAT ---------------------------------
|
||||||
|
// The format can be adjusted to fit your particular application needs
|
||||||
|
|
||||||
|
// If the connection is reliable, you can disable the SOF byte and checksums.
|
||||||
|
// That can save up to 9 bytes of overhead.
|
||||||
|
|
||||||
|
// ,-----+----+-----+------+------------+- - - -+------------,
|
||||||
|
// | SOF | ID | LEN | TYPE | HEAD_CKSUM | DATA | PLD_CKSUM |
|
||||||
|
// | 1 | ? | ? | ? | ? | ... | ? | <- size (bytes)
|
||||||
|
// '-----+----+-----+------+------------+- - - -+------------'
|
||||||
|
|
||||||
|
// !!! BOTH SIDES MUST USE THE SAME SETTINGS !!!
|
||||||
|
|
||||||
|
// Adjust sizes as desired (1,2,4)
|
||||||
|
#define TF_ID_BYTES 2 |
||||||
|
#define TF_LEN_BYTES 2 |
||||||
|
#define TF_TYPE_BYTES 1 |
||||||
|
|
||||||
|
// Checksum type
|
||||||
|
//#define TF_CKSUM_TYPE TF_CKSUM_NONE
|
||||||
|
#define TF_CKSUM_TYPE TF_CKSUM_XOR |
||||||
|
//#define TF_CKSUM_TYPE TF_CKSUM_CRC16
|
||||||
|
//#define TF_CKSUM_TYPE TF_CKSUM_CRC32
|
||||||
|
|
||||||
|
// Use a SOF byte to mark the start of a frame
|
||||||
|
#define TF_USE_SOF_BYTE 1 |
||||||
|
// Value of the SOF byte (if TF_USE_SOF_BYTE == 1)
|
||||||
|
#define TF_SOF_BYTE 0x01 |
||||||
|
|
||||||
|
//----------------------- PLATFORM COMPATIBILITY ----------------------------
|
||||||
|
|
||||||
|
// used for timeout tick counters - should be large enough for all used timeouts
|
||||||
|
typedef uint16_t TF_TICKS; |
||||||
|
|
||||||
|
// used in loops iterating over listeners
|
||||||
|
typedef uint8_t TF_COUNT; |
||||||
|
|
||||||
|
//----------------------------- PARAMETERS ----------------------------------
|
||||||
|
|
||||||
|
// Maximum received payload size (static buffer)
|
||||||
|
// Larger payloads will be rejected.
|
||||||
|
#define TF_MAX_PAYLOAD_RX 640 |
||||||
|
// Size of the sending buffer. Larger payloads will be split to pieces and sent
|
||||||
|
// in multiple calls to the write function. This can be lowered to reduce RAM usage.
|
||||||
|
#define TF_SENDBUF_LEN 64 |
||||||
|
|
||||||
|
// --- Listener counts - determine sizes of the static slot tables ---
|
||||||
|
|
||||||
|
// Frame ID listeners (wait for response / multi-part message)
|
||||||
|
#define TF_MAX_ID_LST 4 |
||||||
|
// Frame Type listeners (wait for frame with a specific first payload byte)
|
||||||
|
#define TF_MAX_TYPE_LST 4 |
||||||
|
// Generic listeners (fallback if no other listener catches it)
|
||||||
|
#define TF_MAX_GEN_LST 1 |
||||||
|
|
||||||
|
// Timeout for receiving & parsing a frame
|
||||||
|
// ticks = number of calls to TF_Tick()
|
||||||
|
#define TF_PARSER_TIMEOUT_TICKS 10 |
||||||
|
|
||||||
|
//------------------------- End of user config ------------------------------
|
||||||
|
|
||||||
|
#endif //TF_CONFIG_H
|
@ -0,0 +1,44 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/21.
|
||||||
|
//
|
||||||
|
// TinyFrame integration
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "task_main.h" |
||||||
|
|
||||||
|
#include "USB/usbd_cdc_if.h" |
||||||
|
#include "TinyFrame.h" |
||||||
|
|
||||||
|
extern osSemaphoreId semVcomTxReadyHandle; |
||||||
|
extern osMutexId mutTinyFrameTxHandle; |
||||||
|
|
||||||
|
void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len) |
||||||
|
{ |
||||||
|
(void) tf; |
||||||
|
#define CHUNK 64 // same as TF_SENDBUF_LEN, so we should always have only one run of the loop
|
||||||
|
int32_t total = (int32_t) len; |
||||||
|
while (total > 0) { |
||||||
|
assert_param(osOK == osSemaphoreWait(semVcomTxReadyHandle, 5000)); |
||||||
|
assert_param(USBD_OK == CDC_Transmit_FS((uint8_t *) buff, (uint16_t) MIN(total, CHUNK))); |
||||||
|
buff += CHUNK; |
||||||
|
total -= CHUNK; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Claim the TX interface before composing and sending a frame */ |
||||||
|
void TF_ClaimTx(TinyFrame *tf) |
||||||
|
{ |
||||||
|
(void) tf; |
||||||
|
assert_param(osThreadGetId() != tskMainHandle); |
||||||
|
assert_param(!inIRQ()); |
||||||
|
|
||||||
|
assert_param(osOK == osMutexWait(mutTinyFrameTxHandle, 5000)); |
||||||
|
} |
||||||
|
|
||||||
|
/** Free the TX interface after composing and sending a frame */ |
||||||
|
void TF_ReleaseTx(TinyFrame *tf) |
||||||
|
{ |
||||||
|
(void) tf; |
||||||
|
assert_param(osOK == osMutexRelease(mutTinyFrameTxHandle)); |
||||||
|
} |
@ -0,0 +1,865 @@ |
|||||||
|
//---------------------------------------------------------------------------
|
||||||
|
#include "TinyFrame.h" |
||||||
|
#include <malloc.h> |
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Compatibility with ESP8266 SDK
|
||||||
|
#ifdef ICACHE_FLASH_ATTR |
||||||
|
#define _TF_FN ICACHE_FLASH_ATTR |
||||||
|
#else |
||||||
|
#define _TF_FN |
||||||
|
#endif |
||||||
|
|
||||||
|
// Helper macros
|
||||||
|
#define TF_MAX(a, b) ((a)>(b)?(a):(b)) |
||||||
|
#define TF_MIN(a, b) ((a)<(b)?(a):(b)) |
||||||
|
|
||||||
|
// TODO It would be nice to have per-instance configurable checksum types, but that would
|
||||||
|
// mandate configurable field sizes unless we use u32 everywhere (and possibly shorten
|
||||||
|
// it when encoding to the buffer). I don't really like this idea so much. -MP
|
||||||
|
|
||||||
|
//region Checksums
|
||||||
|
|
||||||
|
#if TF_CKSUM_TYPE == TF_CKSUM_NONE |
||||||
|
|
||||||
|
// NONE
|
||||||
|
#define CKSUM_RESET(cksum) |
||||||
|
#define CKSUM_ADD(cksum, byte) |
||||||
|
#define CKSUM_FINALIZE(cksum) |
||||||
|
|
||||||
|
#elif TF_CKSUM_TYPE == TF_CKSUM_XOR |
||||||
|
|
||||||
|
// ~XOR
|
||||||
|
#define CKSUM_RESET(cksum) do { (cksum) = 0; } while (0) |
||||||
|
#define CKSUM_ADD(cksum, byte) do { (cksum) ^= (byte); } while(0) |
||||||
|
#define CKSUM_FINALIZE(cksum) do { (cksum) = (TF_CKSUM)~cksum; } while(0) |
||||||
|
|
||||||
|
#elif TF_CKSUM_TYPE == TF_CKSUM_CRC16 |
||||||
|
|
||||||
|
// TODO try to replace with an algorithm
|
||||||
|
/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */ |
||||||
|
static const uint16_t crc16_table[256] = { |
||||||
|
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, |
||||||
|
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, |
||||||
|
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, |
||||||
|
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, |
||||||
|
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, |
||||||
|
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, |
||||||
|
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, |
||||||
|
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, |
||||||
|
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, |
||||||
|
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, |
||||||
|
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, |
||||||
|
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, |
||||||
|
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, |
||||||
|
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, |
||||||
|
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, |
||||||
|
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, |
||||||
|
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, |
||||||
|
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, |
||||||
|
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, |
||||||
|
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, |
||||||
|
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, |
||||||
|
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, |
||||||
|
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, |
||||||
|
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, |
||||||
|
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, |
||||||
|
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, |
||||||
|
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, |
||||||
|
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, |
||||||
|
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, |
||||||
|
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, |
||||||
|
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, |
||||||
|
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 |
||||||
|
}; |
||||||
|
|
||||||
|
static inline uint16_t crc16_byte(uint16_t cksum, const uint8_t byte) |
||||||
|
{ |
||||||
|
return (cksum >> 8) ^ crc16_table[(cksum ^ byte) & 0xff]; |
||||||
|
} |
||||||
|
|
||||||
|
#define CKSUM_RESET(cksum) do { (cksum) = 0; } while (0) |
||||||
|
#define CKSUM_ADD(cksum, byte) do { (cksum) = crc16_byte((cksum), (byte)); } while(0) |
||||||
|
#define CKSUM_FINALIZE(cksum) |
||||||
|
|
||||||
|
#elif TF_CKSUM_TYPE == TF_CKSUM_CRC32 |
||||||
|
|
||||||
|
// TODO try to replace with an algorithm
|
||||||
|
static const uint32_t crc32_table[] = { /* CRC polynomial 0xedb88320 */ |
||||||
|
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, |
||||||
|
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, |
||||||
|
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, |
||||||
|
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, |
||||||
|
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, |
||||||
|
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, |
||||||
|
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, |
||||||
|
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, |
||||||
|
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, |
||||||
|
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, |
||||||
|
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, |
||||||
|
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, |
||||||
|
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, |
||||||
|
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, |
||||||
|
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, |
||||||
|
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, |
||||||
|
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, |
||||||
|
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, |
||||||
|
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, |
||||||
|
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, |
||||||
|
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, |
||||||
|
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, |
||||||
|
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, |
||||||
|
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, |
||||||
|
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, |
||||||
|
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, |
||||||
|
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, |
||||||
|
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, |
||||||
|
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, |
||||||
|
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, |
||||||
|
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, |
||||||
|
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, |
||||||
|
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, |
||||||
|
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, |
||||||
|
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, |
||||||
|
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, |
||||||
|
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, |
||||||
|
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, |
||||||
|
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, |
||||||
|
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, |
||||||
|
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, |
||||||
|
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, |
||||||
|
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d |
||||||
|
}; |
||||||
|
|
||||||
|
static inline uint32_t crc32_byte(uint32_t cksum, const uint8_t byte) |
||||||
|
{ |
||||||
|
return (crc32_table[((cksum) ^ ((uint8_t)byte)) & 0xff] ^ ((cksum) >> 8)); |
||||||
|
} |
||||||
|
|
||||||
|
#define CKSUM_RESET(cksum) do { (cksum) = (TF_CKSUM)0xFFFFFFFF; } while (0) |
||||||
|
#define CKSUM_ADD(cksum, byte) do { (cksum) = crc32_byte(cksum, byte); } while(0) |
||||||
|
#define CKSUM_FINALIZE(cksum) do { (cksum) = (TF_CKSUM)~(cksum); } while(0) |
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
/** Init with a user-allocated buffer */ |
||||||
|
void _TF_FN TF_InitStatic(TinyFrame *tf, TF_Peer peer_bit) |
||||||
|
{ |
||||||
|
if (tf == NULL) return; |
||||||
|
|
||||||
|
// Zero it out, keeping user config
|
||||||
|
uint32_t usertag = tf->usertag; |
||||||
|
void * userdata = tf->userdata; |
||||||
|
|
||||||
|
memset(tf, 0, sizeof(struct TinyFrame_)); |
||||||
|
|
||||||
|
tf->usertag = usertag; |
||||||
|
tf->userdata = userdata; |
||||||
|
|
||||||
|
tf->peer_bit = peer_bit; |
||||||
|
} |
||||||
|
|
||||||
|
/** Init with malloc */ |
||||||
|
TinyFrame * _TF_FN TF_Init(TF_Peer peer_bit) |
||||||
|
{ |
||||||
|
TinyFrame *tf = malloc(sizeof(TinyFrame)); |
||||||
|
TF_InitStatic(tf, peer_bit); |
||||||
|
return tf; |
||||||
|
} |
||||||
|
|
||||||
|
/** Release the struct */ |
||||||
|
void TF_DeInit(TinyFrame *tf) |
||||||
|
{ |
||||||
|
if (tf == NULL) return; |
||||||
|
free(tf); |
||||||
|
} |
||||||
|
|
||||||
|
//region Listeners
|
||||||
|
|
||||||
|
/** Reset ID listener's timeout to the original value */ |
||||||
|
static inline void _TF_FN renew_id_listener(struct TF_IdListener_ *lst) |
||||||
|
{ |
||||||
|
lst->timeout = lst->timeout_max; |
||||||
|
} |
||||||
|
|
||||||
|
/** Notify callback about ID listener's demise & let it free any resources in userdata */ |
||||||
|
static void _TF_FN cleanup_id_listener(TinyFrame *tf, TF_COUNT i, struct TF_IdListener_ *lst) |
||||||
|
{ |
||||||
|
TF_Msg msg; |
||||||
|
if (lst->fn == NULL) return; |
||||||
|
|
||||||
|
// Make user clean up their data - only if not NULL
|
||||||
|
if (lst->userdata != NULL) { |
||||||
|
msg.userdata = lst->userdata; |
||||||
|
msg.userdata2 = lst->userdata2; |
||||||
|
msg.data = NULL; // this is a signal that the listener should clean up
|
||||||
|
lst->fn(tf, &msg); // return value is ignored here - use TF_STAY or TF_CLOSE
|
||||||
|
} |
||||||
|
|
||||||
|
lst->fn = NULL; // Discard listener
|
||||||
|
|
||||||
|
if (i == tf->count_id_lst - 1) { |
||||||
|
tf->count_id_lst--; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Clean up Type listener */ |
||||||
|
static inline void _TF_FN cleanup_type_listener(TinyFrame *tf, TF_COUNT i, struct TF_TypeListener_ *lst) |
||||||
|
{ |
||||||
|
lst->fn = NULL; // Discard listener
|
||||||
|
if (i == tf->count_type_lst - 1) { |
||||||
|
tf->count_type_lst--; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Clean up Generic listener */ |
||||||
|
static inline void _TF_FN cleanup_generic_listener(TinyFrame *tf, TF_COUNT i, struct TF_GenericListener_ *lst) |
||||||
|
{ |
||||||
|
lst->fn = NULL; // Discard listener
|
||||||
|
if (i == tf->count_generic_lst - 1) { |
||||||
|
tf->count_generic_lst--; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Add a new ID listener. Returns 1 on success. */ |
||||||
|
bool _TF_FN TF_AddIdListener(TinyFrame *tf, TF_Msg *msg, TF_Listener cb, TF_TICKS timeout) |
||||||
|
{ |
||||||
|
TF_COUNT i; |
||||||
|
struct TF_IdListener_ *lst; |
||||||
|
for (i = 0; i < TF_MAX_ID_LST; i++) { |
||||||
|
lst = &tf->id_listeners[i]; |
||||||
|
// test for empty slot
|
||||||
|
if (lst->fn == NULL) { |
||||||
|
lst->fn = cb; |
||||||
|
lst->id = msg->frame_id; |
||||||
|
lst->userdata = msg->userdata; |
||||||
|
lst->userdata2 = msg->userdata2; |
||||||
|
lst->timeout_max = lst->timeout = timeout; |
||||||
|
if (i >= tf->count_id_lst) { |
||||||
|
tf->count_id_lst = (TF_COUNT) (i + 1); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** Add a new Type listener. Returns 1 on success. */ |
||||||
|
bool _TF_FN TF_AddTypeListener(TinyFrame *tf, TF_TYPE frame_type, TF_Listener cb) |
||||||
|
{ |
||||||
|
TF_COUNT i; |
||||||
|
struct TF_TypeListener_ *lst; |
||||||
|
for (i = 0; i < TF_MAX_TYPE_LST; i++) { |
||||||
|
lst = &tf->type_listeners[i]; |
||||||
|
// test for empty slot
|
||||||
|
if (lst->fn == NULL) { |
||||||
|
lst->fn = cb; |
||||||
|
lst->type = frame_type; |
||||||
|
if (i >= tf->count_type_lst) { |
||||||
|
tf->count_type_lst = (TF_COUNT) (i + 1); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** Add a new Generic listener. Returns 1 on success. */ |
||||||
|
bool _TF_FN TF_AddGenericListener(TinyFrame *tf, TF_Listener cb) |
||||||
|
{ |
||||||
|
TF_COUNT i; |
||||||
|
struct TF_GenericListener_ *lst; |
||||||
|
for (i = 0; i < TF_MAX_GEN_LST; i++) { |
||||||
|
lst = &tf->generic_listeners[i]; |
||||||
|
// test for empty slot
|
||||||
|
if (lst->fn == NULL) { |
||||||
|
lst->fn = cb; |
||||||
|
if (i >= tf->count_generic_lst) { |
||||||
|
tf->count_generic_lst = (TF_COUNT) (i + 1); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** Remove a ID listener by its frame ID. Returns 1 on success. */ |
||||||
|
bool _TF_FN TF_RemoveIdListener(TinyFrame *tf, TF_ID frame_id) |
||||||
|
{ |
||||||
|
TF_COUNT i; |
||||||
|
struct TF_IdListener_ *lst; |
||||||
|
for (i = 0; i < tf->count_id_lst; i++) { |
||||||
|
lst = &tf->id_listeners[i]; |
||||||
|
// test if live & matching
|
||||||
|
if (lst->fn != NULL && lst->id == frame_id) { |
||||||
|
cleanup_id_listener(tf, i, lst); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** Remove a type listener by its type. Returns 1 on success. */ |
||||||
|
bool _TF_FN TF_RemoveTypeListener(TinyFrame *tf, TF_TYPE type) |
||||||
|
{ |
||||||
|
TF_COUNT i; |
||||||
|
struct TF_TypeListener_ *lst; |
||||||
|
for (i = 0; i < tf->count_type_lst; i++) { |
||||||
|
lst = &tf->type_listeners[i]; |
||||||
|
// test if live & matching
|
||||||
|
if (lst->fn != NULL && lst->type == type) { |
||||||
|
cleanup_type_listener(tf, i, lst); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** Remove a generic listener by its function pointer. Returns 1 on success. */ |
||||||
|
bool _TF_FN TF_RemoveGenericListener(TinyFrame *tf, TF_Listener cb) |
||||||
|
{ |
||||||
|
TF_COUNT i; |
||||||
|
struct TF_GenericListener_ *lst; |
||||||
|
for (i = 0; i < tf->count_generic_lst; i++) { |
||||||
|
lst = &tf->generic_listeners[i]; |
||||||
|
// test if live & matching
|
||||||
|
if (lst->fn == cb) { |
||||||
|
cleanup_generic_listener(tf, i, lst); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** Handle a message that was just collected & verified by the parser */ |
||||||
|
static void _TF_FN TF_HandleReceivedMessage(TinyFrame *tf) |
||||||
|
{ |
||||||
|
TF_COUNT i; |
||||||
|
struct TF_IdListener_ *ilst; |
||||||
|
struct TF_TypeListener_ *tlst; |
||||||
|
struct TF_GenericListener_ *glst; |
||||||
|
TF_Result res; |
||||||
|
|
||||||
|
// Prepare message object
|
||||||
|
TF_Msg msg; |
||||||
|
TF_ClearMsg(&msg); |
||||||
|
msg.frame_id = tf->id; |
||||||
|
msg.is_response = false; |
||||||
|
msg.type = tf->type; |
||||||
|
msg.data = tf->data; |
||||||
|
msg.len = tf->len; |
||||||
|
|
||||||
|
// Any listener can consume the message, or let someone else handle it.
|
||||||
|
|
||||||
|
// The loop upper bounds are the highest currently used slot index
|
||||||
|
// (or close to it, depending on the order of listener removals).
|
||||||
|
|
||||||
|
// ID listeners first
|
||||||
|
for (i = 0; i < tf->count_id_lst; i++) { |
||||||
|
ilst = &tf->id_listeners[i]; |
||||||
|
|
||||||
|
if (ilst->fn && ilst->id == msg.frame_id) { |
||||||
|
msg.userdata = ilst->userdata; // pass userdata pointer to the callback
|
||||||
|
msg.userdata2 = ilst->userdata2; |
||||||
|
res = ilst->fn(tf, &msg); |
||||||
|
ilst->userdata = msg.userdata; // put it back (may have changed the pointer or set to NULL)
|
||||||
|
ilst->userdata2 = msg.userdata2; // put it back (may have changed the pointer or set to NULL)
|
||||||
|
|
||||||
|
if (res != TF_NEXT) { |
||||||
|
// if it's TF_CLOSE, we assume user already cleaned up userdata
|
||||||
|
if (res == TF_RENEW) { |
||||||
|
renew_id_listener(ilst); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
// clean up for the following listeners that don't use userdata (this avoids data from
|
||||||
|
// an ID listener that returned TF_NEXT from leaking into Type and Generic listeners)
|
||||||
|
msg.userdata = NULL; |
||||||
|
msg.userdata2 = NULL; |
||||||
|
|
||||||
|
// Type listeners
|
||||||
|
for (i = 0; i < tf->count_type_lst; i++) { |
||||||
|
tlst = &tf->type_listeners[i]; |
||||||
|
|
||||||
|
if (tlst->fn && tlst->type == msg.type) { |
||||||
|
res = tlst->fn(tf, &msg); |
||||||
|
|
||||||
|
if (res != TF_NEXT) { |
||||||
|
// if it's TF_CLOSE, we assume user already cleaned up userdata
|
||||||
|
// TF_RENEW doesn't make sense here because type listeners don't expire
|
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Generic listeners
|
||||||
|
for (i = 0; i < tf->count_generic_lst; i++) { |
||||||
|
glst = &tf->generic_listeners[i]; |
||||||
|
|
||||||
|
if (glst->fn) { |
||||||
|
res = glst->fn(tf, &msg); |
||||||
|
|
||||||
|
if (res != TF_NEXT) { |
||||||
|
// if it's TF_CLOSE, we assume user already cleaned up userdata
|
||||||
|
// TF_RENEW doesn't make sense here because generic listeners don't expire
|
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//endregion Listeners
|
||||||
|
|
||||||
|
/** Handle a received byte buffer */ |
||||||
|
void _TF_FN TF_Accept(TinyFrame *tf, const uint8_t *buffer, size_t count) |
||||||
|
{ |
||||||
|
size_t i; |
||||||
|
for (i = 0; i < count; i++) { |
||||||
|
TF_AcceptChar(tf, buffer[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Reset the parser's internal state. */ |
||||||
|
void _TF_FN TF_ResetParser(TinyFrame *tf) |
||||||
|
{ |
||||||
|
tf->state = TFState_SOF; |
||||||
|
// more init will be done by the parser when the first byte is received
|
||||||
|
} |
||||||
|
|
||||||
|
/** SOF was received - prepare for the frame */ |
||||||
|
static void _TF_FN pars_begin_frame(TinyFrame *tf) { |
||||||
|
// Reset state vars
|
||||||
|
CKSUM_RESET(tf->cksum); |
||||||
|
#if TF_USE_SOF_BYTE |
||||||
|
CKSUM_ADD(tf->cksum, TF_SOF_BYTE); |
||||||
|
#endif |
||||||
|
|
||||||
|
tf->discard_data = false; |
||||||
|
|
||||||
|
// Enter ID state
|
||||||
|
tf->state = TFState_ID; |
||||||
|
tf->rxi = 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** Handle a received char - here's the main state machine */ |
||||||
|
void _TF_FN TF_AcceptChar(TinyFrame *tf, unsigned char c) |
||||||
|
{ |
||||||
|
// Parser timeout - clear
|
||||||
|
if (tf->parser_timeout_ticks >= TF_PARSER_TIMEOUT_TICKS) { |
||||||
|
TF_ResetParser(tf); |
||||||
|
} |
||||||
|
tf->parser_timeout_ticks = 0; |
||||||
|
|
||||||
|
// DRY snippet - collect multi-byte number from the input stream, byte by byte
|
||||||
|
// This is a little dirty, but makes the code easier to read. It's used like e.g. if(),
|
||||||
|
// the body is run only after the entire number (of data type 'type') was received
|
||||||
|
// and stored to 'dest'
|
||||||
|
#define COLLECT_NUMBER(dest, type) dest = (type)(((dest) << 8) | c); \ |
||||||
|
if (++tf->rxi == sizeof(type)) |
||||||
|
|
||||||
|
#if !TF_USE_SOF_BYTE |
||||||
|
if (tf->state == TFState_SOF) { |
||||||
|
TF_ParsBeginFrame(); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
switch (tf->state) { |
||||||
|
case TFState_SOF: |
||||||
|
if (c == TF_SOF_BYTE) { |
||||||
|
pars_begin_frame(tf); |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case TFState_ID: |
||||||
|
CKSUM_ADD(tf->cksum, c); |
||||||
|
COLLECT_NUMBER(tf->id, TF_ID) { |
||||||
|
// Enter LEN state
|
||||||
|
tf->state = TFState_LEN; |
||||||
|
tf->rxi = 0; |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case TFState_LEN: |
||||||
|
CKSUM_ADD(tf->cksum, c); |
||||||
|
COLLECT_NUMBER(tf->len, TF_LEN) { |
||||||
|
// Enter TYPE state
|
||||||
|
tf->state = TFState_TYPE; |
||||||
|
tf->rxi = 0; |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case TFState_TYPE: |
||||||
|
CKSUM_ADD(tf->cksum, c); |
||||||
|
COLLECT_NUMBER(tf->type, TF_TYPE) { |
||||||
|
#if TF_CKSUM_TYPE == TF_CKSUM_NONE |
||||||
|
tf->state = TFState_DATA; |
||||||
|
tf->rxi = 0; |
||||||
|
#else |
||||||
|
// enter HEAD_CKSUM state
|
||||||
|
tf->state = TFState_HEAD_CKSUM; |
||||||
|
tf->rxi = 0; |
||||||
|
tf->ref_cksum = 0; |
||||||
|
#endif |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case TFState_HEAD_CKSUM: |
||||||
|
COLLECT_NUMBER(tf->ref_cksum, TF_CKSUM) { |
||||||
|
// Check the header checksum against the computed value
|
||||||
|
CKSUM_FINALIZE(tf->cksum); |
||||||
|
|
||||||
|
if (tf->cksum != tf->ref_cksum) { |
||||||
|
TF_ResetParser(tf); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (tf->len == 0) { |
||||||
|
TF_HandleReceivedMessage(tf); |
||||||
|
TF_ResetParser(tf); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// Enter DATA state
|
||||||
|
tf->state = TFState_DATA; |
||||||
|
tf->rxi = 0; |
||||||
|
|
||||||
|
CKSUM_RESET(tf->cksum); // Start collecting the payload
|
||||||
|
|
||||||
|
if (tf->len >= TF_MAX_PAYLOAD_RX) { |
||||||
|
// ERROR - frame too long. Consume, but do not store.
|
||||||
|
tf->discard_data = true; |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case TFState_DATA: |
||||||
|
if (tf->discard_data) { |
||||||
|
tf->rxi++; |
||||||
|
} else { |
||||||
|
CKSUM_ADD(tf->cksum, c); |
||||||
|
tf->data[tf->rxi++] = c; |
||||||
|
} |
||||||
|
|
||||||
|
if (tf->rxi == tf->len) { |
||||||
|
#if TF_CKSUM_TYPE == TF_CKSUM_NONE |
||||||
|
// All done
|
||||||
|
TF_HandleReceivedMessage(); |
||||||
|
TF_ResetParser(); |
||||||
|
#else |
||||||
|
// Enter DATA_CKSUM state
|
||||||
|
tf->state = TFState_DATA_CKSUM; |
||||||
|
tf->rxi = 0; |
||||||
|
tf->ref_cksum = 0; |
||||||
|
#endif |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case TFState_DATA_CKSUM: |
||||||
|
COLLECT_NUMBER(tf->ref_cksum, TF_CKSUM) { |
||||||
|
// Check the header checksum against the computed value
|
||||||
|
CKSUM_FINALIZE(tf->cksum); |
||||||
|
if (!tf->discard_data && tf->cksum == tf->ref_cksum) { |
||||||
|
TF_HandleReceivedMessage(tf); |
||||||
|
} |
||||||
|
|
||||||
|
TF_ResetParser(tf); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// we get here after finishing HEAD, if no data are to be received - handle and clear
|
||||||
|
if (tf->len == 0 && tf->state == TFState_DATA) { |
||||||
|
TF_HandleReceivedMessage(tf); |
||||||
|
TF_ResetParser(tf); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Helper macros for the Compose functions
|
||||||
|
// use variables: si - signed int, b - byte, outbuff - target buffer, pos - count of bytes in buffer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a number to the output buffer. |
||||||
|
* |
||||||
|
* @param type - data type |
||||||
|
* @param num - number to write |
||||||
|
* @param xtra - extra callback run after each byte, 'b' now contains the byte. |
||||||
|
*/ |
||||||
|
#define WRITENUM_BASE(type, num, xtra) \ |
||||||
|
for (si = sizeof(type)-1; si>=0; si--) { \
|
||||||
|
b = (uint8_t)((num) >> (si*8) & 0xFF); \
|
||||||
|
outbuff[pos++] = b; \
|
||||||
|
xtra; \
|
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Do nothing |
||||||
|
*/ |
||||||
|
#define _NOOP() |
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a number without adding its bytes to the checksum |
||||||
|
* |
||||||
|
* @param type - data type |
||||||
|
* @param num - number to write |
||||||
|
*/ |
||||||
|
#define WRITENUM(type, num) WRITENUM_BASE(type, num, _NOOP()) |
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a number AND add its bytes to the checksum |
||||||
|
* |
||||||
|
* @param type - data type |
||||||
|
* @param num - number to write |
||||||
|
*/ |
||||||
|
#define WRITENUM_CKSUM(type, num) WRITENUM_BASE(type, num, CKSUM_ADD(cksum, b)) |
||||||
|
|
||||||
|
/**
|
||||||
|
* Compose a frame (used internally by TF_Send and TF_Respond). |
||||||
|
* The frame can be sent using TF_WriteImpl(), or received by TF_Accept() |
||||||
|
* |
||||||
|
* @param outbuff - buffer to store the result in |
||||||
|
* @param msg - message written to the buffer |
||||||
|
* @return nr of bytes in outbuff used by the frame, 0 on failure |
||||||
|
*/ |
||||||
|
static inline size_t _TF_FN TF_ComposeHead(TinyFrame *tf, uint8_t *outbuff, TF_Msg *msg) |
||||||
|
{ |
||||||
|
int8_t si = 0; // signed small int
|
||||||
|
uint8_t b = 0; |
||||||
|
TF_ID id = 0; |
||||||
|
TF_CKSUM cksum = 0; |
||||||
|
size_t pos = 0; // can be needed to grow larger than TF_LEN
|
||||||
|
|
||||||
|
(void)cksum; |
||||||
|
|
||||||
|
CKSUM_RESET(cksum); |
||||||
|
|
||||||
|
// Gen ID
|
||||||
|
if (msg->is_response) { |
||||||
|
id = msg->frame_id; |
||||||
|
} |
||||||
|
else { |
||||||
|
id = (TF_ID) (tf->next_id++ & TF_ID_MASK); |
||||||
|
if (tf->peer_bit) { |
||||||
|
id |= TF_ID_PEERBIT; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
msg->frame_id = id; // put the resolved ID into the message object for later use
|
||||||
|
|
||||||
|
// --- Start ---
|
||||||
|
CKSUM_RESET(cksum); |
||||||
|
|
||||||
|
#if TF_USE_SOF_BYTE |
||||||
|
outbuff[pos++] = TF_SOF_BYTE; |
||||||
|
CKSUM_ADD(cksum, TF_SOF_BYTE); |
||||||
|
#endif |
||||||
|
|
||||||
|
WRITENUM_CKSUM(TF_ID, id); |
||||||
|
WRITENUM_CKSUM(TF_LEN, msg->len); |
||||||
|
WRITENUM_CKSUM(TF_TYPE, msg->type); |
||||||
|
|
||||||
|
#if TF_CKSUM_TYPE != TF_CKSUM_NONE |
||||||
|
CKSUM_FINALIZE(cksum); |
||||||
|
WRITENUM(TF_CKSUM, cksum); |
||||||
|
#endif |
||||||
|
|
||||||
|
return pos; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Compose a frame (used internally by TF_Send and TF_Respond). |
||||||
|
* The frame can be sent using TF_WriteImpl(), or received by TF_Accept() |
||||||
|
* |
||||||
|
* @param outbuff - buffer to store the result in |
||||||
|
* @param data - data buffer |
||||||
|
* @param data_len - data buffer len |
||||||
|
* @param cksum - checksum variable, used for all calls to TF_ComposeBody. Must be reset before first use! (CKSUM_RESET(cksum);) |
||||||
|
* @return nr of bytes in outbuff used |
||||||
|
*/ |
||||||
|
static size_t _TF_FN TF_ComposeBody(uint8_t *outbuff, |
||||||
|
const uint8_t *data, TF_LEN data_len, |
||||||
|
TF_CKSUM *cksum) |
||||||
|
{ |
||||||
|
TF_LEN i = 0; |
||||||
|
uint8_t b = 0; |
||||||
|
size_t pos = 0; |
||||||
|
|
||||||
|
for (i = 0; i < data_len; i++) { |
||||||
|
b = data[i]; |
||||||
|
outbuff[pos++] = b; |
||||||
|
CKSUM_ADD(*cksum, b); |
||||||
|
} |
||||||
|
|
||||||
|
return pos; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Finalize a frame |
||||||
|
* |
||||||
|
* @param outbuff - buffer to store the result in |
||||||
|
* @param cksum - checksum variable used for the body |
||||||
|
* @return nr of bytes in outbuff used |
||||||
|
*/ |
||||||
|
static size_t _TF_FN TF_ComposeTail(uint8_t *outbuff, TF_CKSUM *cksum) |
||||||
|
{ |
||||||
|
int8_t si = 0; // signed small int
|
||||||
|
uint8_t b = 0; |
||||||
|
size_t pos = 0; |
||||||
|
|
||||||
|
#if TF_CKSUM_TYPE != TF_CKSUM_NONE |
||||||
|
CKSUM_FINALIZE(*cksum); |
||||||
|
WRITENUM(TF_CKSUM, *cksum); |
||||||
|
#endif |
||||||
|
return pos; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message |
||||||
|
* |
||||||
|
* @param tf - instance |
||||||
|
* @param msg - message object |
||||||
|
* @param listener - ID listener, or NULL |
||||||
|
* @param timeout - listener timeout, 0 is none |
||||||
|
* @return true if sent |
||||||
|
*/ |
||||||
|
static bool _TF_FN TF_SendFrame(TinyFrame *tf, TF_Msg *msg, TF_Listener listener, TF_TICKS timeout) |
||||||
|
{ |
||||||
|
size_t len = 0; |
||||||
|
size_t remain = 0; |
||||||
|
size_t sent = 0; |
||||||
|
TF_CKSUM cksum = 0; |
||||||
|
|
||||||
|
TF_ClaimTx(tf); |
||||||
|
|
||||||
|
len = TF_ComposeHead(tf, tf->sendbuf, msg); |
||||||
|
if (listener) TF_AddIdListener(tf, msg, listener, timeout); |
||||||
|
|
||||||
|
CKSUM_RESET(cksum); |
||||||
|
|
||||||
|
remain = msg->len; |
||||||
|
while (remain > 0) { |
||||||
|
size_t chunk = TF_MIN(TF_SENDBUF_LEN - len, remain); |
||||||
|
len += TF_ComposeBody(tf->sendbuf+len, msg->data+sent, (TF_LEN) chunk, &cksum); |
||||||
|
remain -= chunk; |
||||||
|
sent += chunk; |
||||||
|
|
||||||
|
// Flush if the buffer is full and we have more to send
|
||||||
|
if (remain > 0 && len == TF_SENDBUF_LEN) { |
||||||
|
TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, len); |
||||||
|
len = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Flush if checksum wouldn't fit in the buffer
|
||||||
|
if (TF_SENDBUF_LEN - len < sizeof(TF_CKSUM)) { |
||||||
|
TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, len); |
||||||
|
len = 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Add checksum, flush what remains to be sent
|
||||||
|
len += TF_ComposeTail(tf->sendbuf+len, &cksum); |
||||||
|
TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, len); |
||||||
|
|
||||||
|
TF_ReleaseTx(tf); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** send without listener */ |
||||||
|
bool _TF_FN TF_Send(TinyFrame *tf, TF_Msg *msg) |
||||||
|
{ |
||||||
|
return TF_SendFrame(tf, msg, NULL, 0); |
||||||
|
} |
||||||
|
|
||||||
|
/** send without listener and struct */ |
||||||
|
bool _TF_FN TF_SendSimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len) |
||||||
|
{ |
||||||
|
TF_Msg msg; |
||||||
|
TF_ClearMsg(&msg); |
||||||
|
msg.type = type; |
||||||
|
msg.data = data; |
||||||
|
msg.len = len; |
||||||
|
return TF_Send(tf, &msg); |
||||||
|
} |
||||||
|
|
||||||
|
/** send with a listener waiting for a reply, without the struct */ |
||||||
|
bool _TF_FN TF_QuerySimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len, TF_Listener listener, TF_TICKS timeout) |
||||||
|
{ |
||||||
|
TF_Msg msg; |
||||||
|
TF_ClearMsg(&msg); |
||||||
|
msg.type = type; |
||||||
|
msg.data = data; |
||||||
|
msg.len = len; |
||||||
|
return TF_SendFrame(tf, &msg, listener, timeout); |
||||||
|
} |
||||||
|
|
||||||
|
/** send with a listener waiting for a reply */ |
||||||
|
bool _TF_FN TF_Query(TinyFrame *tf, TF_Msg *msg, TF_Listener listener, TF_TICKS timeout) |
||||||
|
{ |
||||||
|
return TF_SendFrame(tf, msg, listener, timeout); |
||||||
|
} |
||||||
|
|
||||||
|
/** Like TF_Send, but with explicit frame ID (set inside the msg object), use for responses */ |
||||||
|
bool _TF_FN TF_Respond(TinyFrame *tf, TF_Msg *msg) |
||||||
|
{ |
||||||
|
msg->is_response = true; |
||||||
|
return TF_Send(tf, msg); |
||||||
|
} |
||||||
|
|
||||||
|
/** Externally renew an ID listener */ |
||||||
|
bool _TF_FN TF_RenewIdListener(TinyFrame *tf, TF_ID id) |
||||||
|
{ |
||||||
|
TF_COUNT i; |
||||||
|
struct TF_IdListener_ *lst; |
||||||
|
for (i = 0; i < tf->count_id_lst; i++) { |
||||||
|
lst = &tf->id_listeners[i]; |
||||||
|
// test if live & matching
|
||||||
|
if (lst->fn != NULL && lst->id == id) { |
||||||
|
renew_id_listener(lst); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** Timebase hook - for timeouts */ |
||||||
|
void _TF_FN TF_Tick(TinyFrame *tf) |
||||||
|
{ |
||||||
|
TF_COUNT i = 0; |
||||||
|
struct TF_IdListener_ *lst; |
||||||
|
|
||||||
|
// increment parser timeout (timeout is handled when receiving next byte)
|
||||||
|
if (tf->parser_timeout_ticks < TF_PARSER_TIMEOUT_TICKS) { |
||||||
|
tf->parser_timeout_ticks++; |
||||||
|
} |
||||||
|
|
||||||
|
// decrement and expire ID listeners
|
||||||
|
for (i = 0; i < tf->count_id_lst; i++) { |
||||||
|
lst = &tf->id_listeners[i]; |
||||||
|
if (!lst->fn || lst->timeout == 0) continue; |
||||||
|
// count down...
|
||||||
|
if (--lst->timeout == 0) { |
||||||
|
// Listener has expired
|
||||||
|
cleanup_id_listener(tf, i, lst); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Default impl for claiming write mutex; can be specific to the instance */ |
||||||
|
void __attribute__((weak)) TF_ClaimTx(TinyFrame *tf) |
||||||
|
{ |
||||||
|
(void) tf; |
||||||
|
|
||||||
|
// do nothing
|
||||||
|
} |
||||||
|
|
||||||
|
/** Default impl for releasing write mutex; can be specific to the instance */ |
||||||
|
void __attribute__((weak)) TF_ReleaseTx(TinyFrame *tf) |
||||||
|
{ |
||||||
|
(void) tf; |
||||||
|
|
||||||
|
// do nothing
|
||||||
|
} |
@ -0,0 +1,384 @@ |
|||||||
|
#ifndef TinyFrameH |
||||||
|
#define TinyFrameH |
||||||
|
|
||||||
|
/**
|
||||||
|
* TinyFrame protocol library |
||||||
|
*
|
||||||
|
* (c) Ondřej Hruška 2017, MIT License
|
||||||
|
* no liability/warranty, free for any use, must retain this notice & license |
||||||
|
*
|
||||||
|
* Upstream URL: https://github.com/MightyPork/TinyFrame
|
||||||
|
*/ |
||||||
|
|
||||||
|
#define TF_VERSION "2.0.1" |
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
#include <stdint.h> // for uint8_t etc |
||||||
|
#include <stdbool.h> // for bool |
||||||
|
#include <stddef.h> // for NULL |
||||||
|
#include <string.h> // for memset() |
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Select checksum type (0 = none, 8 = ~XOR, 16 = CRC16 0x8005, 32 = CRC32)
|
||||||
|
#define TF_CKSUM_NONE 0 |
||||||
|
#define TF_CKSUM_XOR 8 |
||||||
|
#define TF_CKSUM_CRC16 16 |
||||||
|
#define TF_CKSUM_CRC32 32 |
||||||
|
|
||||||
|
#include "TF_Config.h" |
||||||
|
|
||||||
|
//region Resolve data types
|
||||||
|
|
||||||
|
#if TF_LEN_BYTES == 1 |
||||||
|
typedef uint8_t TF_LEN; |
||||||
|
#elif TF_LEN_BYTES == 2 |
||||||
|
typedef uint16_t TF_LEN; |
||||||
|
#elif TF_LEN_BYTES == 4 |
||||||
|
typedef uint32_t TF_LEN; |
||||||
|
#else |
||||||
|
#error Bad value of TF_LEN_BYTES, must be 1, 2 or 4 |
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
#if TF_TYPE_BYTES == 1 |
||||||
|
typedef uint8_t TF_TYPE; |
||||||
|
#elif TF_TYPE_BYTES == 2 |
||||||
|
typedef uint16_t TF_TYPE; |
||||||
|
#elif TF_TYPE_BYTES == 4 |
||||||
|
typedef uint32_t TF_TYPE; |
||||||
|
#else |
||||||
|
#error Bad value of TF_TYPE_BYTES, must be 1, 2 or 4 |
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
#if TF_ID_BYTES == 1 |
||||||
|
typedef uint8_t TF_ID; |
||||||
|
#elif TF_ID_BYTES == 2 |
||||||
|
typedef uint16_t TF_ID; |
||||||
|
#elif TF_ID_BYTES == 4 |
||||||
|
typedef uint32_t TF_ID; |
||||||
|
#else |
||||||
|
#error Bad value of TF_ID_BYTES, must be 1, 2 or 4 |
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
#if TF_CKSUM_TYPE == TF_CKSUM_XOR || TF_CKSUM_TYPE == TF_CKSUM_NONE |
||||||
|
// ~XOR (if 0, still use 1 byte - it won't be used)
|
||||||
|
typedef uint8_t TF_CKSUM; |
||||||
|
#elif TF_CKSUM_TYPE == TF_CKSUM_CRC16 |
||||||
|
// CRC16
|
||||||
|
typedef uint16_t TF_CKSUM; |
||||||
|
#elif TF_CKSUM_TYPE == TF_CKSUM_CRC32 |
||||||
|
// CRC32
|
||||||
|
typedef uint32_t TF_CKSUM; |
||||||
|
#else |
||||||
|
#error Bad value for TF_CKSUM_TYPE, must be 8, 16 or 32 |
||||||
|
#endif |
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Type-dependent masks for bit manipulation in the ID field
|
||||||
|
#define TF_ID_MASK (TF_ID)(((TF_ID)1 << (sizeof(TF_ID)*8 - 1)) - 1) |
||||||
|
#define TF_ID_PEERBIT (TF_ID)((TF_ID)1 << ((sizeof(TF_ID)*8) - 1)) |
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Peer bit enum (used for init) */ |
||||||
|
typedef enum { |
||||||
|
TF_SLAVE = 0, |
||||||
|
TF_MASTER = 1, |
||||||
|
} TF_Peer; |
||||||
|
|
||||||
|
/** Response from listeners */ |
||||||
|
typedef enum { |
||||||
|
TF_NEXT = 0, //!< Not handled, let other listeners handle it
|
||||||
|
TF_STAY = 1, //!< Handled, stay
|
||||||
|
TF_RENEW = 2, //!< Handled, stay, renew - useful only with listener timeout
|
||||||
|
TF_CLOSE = 3, //!< Handled, remove self
|
||||||
|
} TF_Result; |
||||||
|
|
||||||
|
/** Data structure for sending / receiving messages */ |
||||||
|
typedef struct _TF_MSG_STRUCT_ { |
||||||
|
TF_ID frame_id; //!< message ID
|
||||||
|
bool is_response; //!< internal flag, set when using the Respond function. frame_id is then kept unchanged.
|
||||||
|
TF_TYPE type; //!< received or sent message type
|
||||||
|
const uint8_t *data; //!< buffer of received data or data to send. NULL = listener timed out, free userdata!
|
||||||
|
TF_LEN len; //!< length of the buffer
|
||||||
|
void *userdata; //!< here's a place for custom data; this data will be stored with the listener
|
||||||
|
void *userdata2; |
||||||
|
} TF_Msg; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear message struct |
||||||
|
*/ |
||||||
|
static inline void TF_ClearMsg(TF_Msg *msg) |
||||||
|
{ |
||||||
|
memset(msg, 0, sizeof(TF_Msg)); |
||||||
|
} |
||||||
|
|
||||||
|
typedef struct TinyFrame_ TinyFrame; |
||||||
|
|
||||||
|
/**
|
||||||
|
* TinyFrame Type Listener callback |
||||||
|
* |
||||||
|
* @param frame_id - ID of the received frame |
||||||
|
* @param type - type field from the message |
||||||
|
* @param data - byte buffer with the application data |
||||||
|
* @param len - number of bytes in the buffer |
||||||
|
* @return listener result |
||||||
|
*/ |
||||||
|
typedef TF_Result (*TF_Listener)(TinyFrame *tf, TF_Msg *msg); |
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
// region Internal
|
||||||
|
|
||||||
|
enum TFState_ { |
||||||
|
TFState_SOF = 0, //!< Wait for SOF
|
||||||
|
TFState_LEN, //!< Wait for Number Of Bytes
|
||||||
|
TFState_HEAD_CKSUM, //!< Wait for header Checksum
|
||||||
|
TFState_ID, //!< Wait for ID
|
||||||
|
TFState_TYPE, //!< Wait for message type
|
||||||
|
TFState_DATA, //!< Receive payload
|
||||||
|
TFState_DATA_CKSUM //!< Wait for Checksum
|
||||||
|
}; |
||||||
|
|
||||||
|
struct TF_IdListener_ { |
||||||
|
TF_ID id; |
||||||
|
TF_Listener fn; |
||||||
|
TF_TICKS timeout; // nr of ticks remaining to disable this listener
|
||||||
|
TF_TICKS timeout_max; // the original timeout is stored here
|
||||||
|
void *userdata; |
||||||
|
void *userdata2; |
||||||
|
}; |
||||||
|
|
||||||
|
struct TF_TypeListener_ { |
||||||
|
TF_TYPE type; |
||||||
|
TF_Listener fn; |
||||||
|
}; |
||||||
|
|
||||||
|
struct TF_GenericListener_ { |
||||||
|
TF_Listener fn; |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Frame parser internal state. |
||||||
|
*/ |
||||||
|
struct TinyFrame_ { |
||||||
|
/* Public user data */ |
||||||
|
void *userdata; |
||||||
|
uint32_t usertag; |
||||||
|
|
||||||
|
// --- the rest of the struct is internal, do not access directly ---
|
||||||
|
|
||||||
|
/* Own state */ |
||||||
|
TF_Peer peer_bit; //!< Own peer bit (unqiue to avoid msg ID clash)
|
||||||
|
TF_ID next_id; //!< Next frame / frame chain ID
|
||||||
|
|
||||||
|
/* Parser state */ |
||||||
|
enum TFState_ state; |
||||||
|
TF_TICKS parser_timeout_ticks; |
||||||
|
TF_ID id; //!< Incoming packet ID
|
||||||
|
TF_LEN len; //!< Payload length
|
||||||
|
uint8_t data[TF_MAX_PAYLOAD_RX]; //!< Data byte buffer
|
||||||
|
TF_LEN rxi; //!< Field size byte counter
|
||||||
|
TF_CKSUM cksum; //!< Checksum calculated of the data stream
|
||||||
|
TF_CKSUM ref_cksum; //!< Reference checksum read from the message
|
||||||
|
TF_TYPE type; //!< Collected message type number
|
||||||
|
bool discard_data; //!< Set if (len > TF_MAX_PAYLOAD) to read the frame, but ignore the data.
|
||||||
|
|
||||||
|
/* --- Callbacks --- */ |
||||||
|
|
||||||
|
/* Transaction callbacks */ |
||||||
|
struct TF_IdListener_ id_listeners[TF_MAX_ID_LST]; |
||||||
|
struct TF_TypeListener_ type_listeners[TF_MAX_TYPE_LST]; |
||||||
|
struct TF_GenericListener_ generic_listeners[TF_MAX_GEN_LST]; |
||||||
|
|
||||||
|
// Those counters are used to optimize look-up times.
|
||||||
|
// They point to the highest used slot number,
|
||||||
|
// or close to it, depending on the removal order.
|
||||||
|
TF_COUNT count_id_lst; |
||||||
|
TF_COUNT count_type_lst; |
||||||
|
TF_COUNT count_generic_lst; |
||||||
|
|
||||||
|
// Buffer for building frames
|
||||||
|
uint8_t sendbuf[TF_SENDBUF_LEN]; |
||||||
|
}; |
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the TinyFrame engine. |
||||||
|
* This can also be used to completely reset it (removing all listeners etc). |
||||||
|
* |
||||||
|
* The field .userdata (or .usertag) can be used to identify different instances |
||||||
|
* in the TF_WriteImpl() function etc. Set this field after the init. |
||||||
|
* |
||||||
|
* This function is a wrapper around TF_InitStatic that calls malloc() to obtain |
||||||
|
* the instance. |
||||||
|
* |
||||||
|
* @param peer_bit - peer bit to use for self |
||||||
|
*/ |
||||||
|
TinyFrame *TF_Init(TF_Peer peer_bit); |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the TinyFrame engine using a statically allocated instance struct. |
||||||
|
* |
||||||
|
* The .userdata / .usertag field is preserved when TF_InitStatic is called. |
||||||
|
* |
||||||
|
* @param peer_bit - peer bit to use for self |
||||||
|
*/ |
||||||
|
void TF_InitStatic(TinyFrame *tf, TF_Peer peer_bit); |
||||||
|
|
||||||
|
/**
|
||||||
|
* De-init the dynamically allocated TF instance |
||||||
|
* |
||||||
|
* @param tf |
||||||
|
*/ |
||||||
|
void TF_DeInit(TinyFrame *tf); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the frame parser state machine. |
||||||
|
* This does not affect registered listeners. |
||||||
|
*/ |
||||||
|
void TF_ResetParser(TinyFrame *tf); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a frame type listener. |
||||||
|
* |
||||||
|
* @param msg - message (contains frame_id and userdata) |
||||||
|
* @param cb - callback |
||||||
|
* @param timeout - timeout in ticks to auto-remove the listener (0 = keep forever) |
||||||
|
* @return slot index (for removing), or TF_ERROR (-1) |
||||||
|
*/ |
||||||
|
bool TF_AddIdListener(TinyFrame *tf, TF_Msg *msg, TF_Listener cb, TF_TICKS timeout); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a listener by the message ID it's registered for |
||||||
|
* |
||||||
|
* @param frame_id - the frame we're listening for |
||||||
|
*/ |
||||||
|
bool TF_RemoveIdListener(TinyFrame *tf, TF_ID frame_id); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a frame type listener. |
||||||
|
* |
||||||
|
* @param frame_type - frame type to listen for |
||||||
|
* @param cb - callback |
||||||
|
* @return slot index (for removing), or TF_ERROR (-1) |
||||||
|
*/ |
||||||
|
bool TF_AddTypeListener(TinyFrame *tf, TF_TYPE frame_type, TF_Listener cb); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a listener by type. |
||||||
|
* |
||||||
|
* @param type - the type it's registered for |
||||||
|
*/ |
||||||
|
bool TF_RemoveTypeListener(TinyFrame *tf, TF_TYPE type); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a generic listener. |
||||||
|
* |
||||||
|
* @param cb - callback |
||||||
|
* @return slot index (for removing), or TF_ERROR (-1) |
||||||
|
*/ |
||||||
|
bool TF_AddGenericListener(TinyFrame *tf, TF_Listener cb); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a generic listener by function pointer |
||||||
|
* |
||||||
|
* @param cb - callback function to remove |
||||||
|
*/ |
||||||
|
bool TF_RemoveGenericListener(TinyFrame *tf, TF_Listener cb); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a frame, no listener |
||||||
|
* |
||||||
|
* @param msg - message struct. ID is stored in the frame_id field |
||||||
|
* @return success |
||||||
|
*/ |
||||||
|
bool TF_Send(TinyFrame *tf, TF_Msg *msg); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Like TF_Send, but without the struct |
||||||
|
*/ |
||||||
|
bool TF_SendSimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a frame, and optionally attach an ID listener. |
||||||
|
* |
||||||
|
* @param msg - message struct. ID is stored in the frame_id field |
||||||
|
* @param listener - listener waiting for the response (can be NULL) |
||||||
|
* @param timeout - listener expiry time in ticks |
||||||
|
* @return success |
||||||
|
*/ |
||||||
|
bool TF_Query(TinyFrame *tf, TF_Msg *msg, TF_Listener listener, TF_TICKS timeout); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Like TF_Query, but without the struct |
||||||
|
*/ |
||||||
|
bool TF_QuerySimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len, |
||||||
|
TF_Listener listener, TF_TICKS timeout); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a response to a received message. |
||||||
|
* |
||||||
|
* @param msg - message struct. ID is read from frame_id. set ->renew to reset listener timeout |
||||||
|
* @return success |
||||||
|
*/ |
||||||
|
bool TF_Respond(TinyFrame *tf, TF_Msg *msg); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Renew an ID listener timeout externally (as opposed to by returning TF_RENEW from the ID listener) |
||||||
|
* |
||||||
|
* @param id - listener ID to renew |
||||||
|
* @return true if listener was found and renewed |
||||||
|
*/ |
||||||
|
bool TF_RenewIdListener(TinyFrame *tf, TF_ID id); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept incoming bytes & parse frames |
||||||
|
* |
||||||
|
* @param buffer - byte buffer to process |
||||||
|
* @param count - nr of bytes in the buffer |
||||||
|
*/ |
||||||
|
void TF_Accept(TinyFrame *tf, const uint8_t *buffer, size_t count); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept a single incoming byte |
||||||
|
* |
||||||
|
* @param c - a received char |
||||||
|
*/ |
||||||
|
void TF_AcceptChar(TinyFrame *tf, uint8_t c); |
||||||
|
|
||||||
|
/**
|
||||||
|
* This function should be called periodically. |
||||||
|
* |
||||||
|
* The time base is used to time-out partial frames in the parser and |
||||||
|
* automatically reset it. |
||||||
|
* |
||||||
|
* (suggestion - call this in a SysTick handler) |
||||||
|
*/ |
||||||
|
void TF_Tick(TinyFrame *tf); |
||||||
|
|
||||||
|
// --- TO BE IMPLEMENTED BY USER ---
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 'Write bytes' function that sends data to UART |
||||||
|
* |
||||||
|
* ! Implement this in your application code ! |
||||||
|
*/ |
||||||
|
extern void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len); |
||||||
|
|
||||||
|
/** Claim the TX interface before composing and sending a frame */ |
||||||
|
extern void TF_ClaimTx(TinyFrame *tf); |
||||||
|
|
||||||
|
/** Free the TX interface after composing and sending a frame */ |
||||||
|
extern void TF_ReleaseTx(TinyFrame *tf); |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,351 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/07.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "framework/system_settings.h" |
||||||
|
#include "usbd_desc.h" |
||||||
|
#include "task_main.h" |
||||||
|
#include "usbd_msc.h" |
||||||
|
#include "usbd_cdc.h" |
||||||
|
#include "usbd_msc_cdc.h" |
||||||
|
|
||||||
|
#define USBD_MSC_CDC_CONFIG_DESC_SIZ 98 |
||||||
|
/* USB Mass storage device Configuration Descriptor */ |
||||||
|
/* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */ |
||||||
|
|
||||||
|
// NOTE: This must NOT be in flash, otherwise the driver / peripheral crashes
|
||||||
|
__ALIGN_BEGIN uint8_t USBD_MSC_CDC_CfgFSDesc[USBD_MSC_CDC_CONFIG_DESC_SIZ] __ALIGN_END = |
||||||
|
{ |
||||||
|
/*0*/ 0x09, /* bLength: Configuation Descriptor size */ |
||||||
|
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */ |
||||||
|
LOBYTE(USBD_MSC_CDC_CONFIG_DESC_SIZ), // TODO
|
||||||
|
HIBYTE(USBD_MSC_CDC_CONFIG_DESC_SIZ), |
||||||
|
3, /* bNumInterfaces - 1 MSC + 2 composite CDC ACM */ |
||||||
|
0x01, /* bConfigurationValue: */ |
||||||
|
0, /* iConfiguration: - string descriptor */ |
||||||
|
0xC0, /* bmAttributes: - self powered */ |
||||||
|
150, /* MaxPower 300 mA */ |
||||||
|
|
||||||
|
/******************** Mass Storage interface ********************/ |
||||||
|
/*9*/ 0x09, /* bLength: Interface Descriptor size */ |
||||||
|
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */ |
||||||
|
0x00, /* bInterfaceNumber: Number of Interface */ |
||||||
|
0x00, /* bAlternateSetting: Alternate setting */ |
||||||
|
0x02, /* bNumEndpoints*/ |
||||||
|
0x08, /* bInterfaceClass: MSC Class */ // #14
|
||||||
|
0x06, /* bInterfaceSubClass : SCSI transparent*/ |
||||||
|
0x50, /* nInterfaceProtocol */ |
||||||
|
0x04, /* iInterface: String descriptor */ |
||||||
|
|
||||||
|
/******************** Mass Storage Endpoints ********************/ |
||||||
|
/*18*/ 0x07, /*Endpoint descriptor length = 7*/ |
||||||
|
0x05, /*Endpoint descriptor type */ |
||||||
|
MSC_EPIN_ADDR, /*Endpoint address (IN, address 1) */ |
||||||
|
0x02, /*Bulk endpoint type */ |
||||||
|
LOBYTE(MSC_MAX_FS_PACKET), |
||||||
|
HIBYTE(MSC_MAX_FS_PACKET), |
||||||
|
0x00, /*Polling interval in milliseconds */ |
||||||
|
|
||||||
|
/*25*/ 0x07, /*Endpoint descriptor length = 7 */ |
||||||
|
0x05, /*Endpoint descriptor type */ |
||||||
|
MSC_EPOUT_ADDR, /*Endpoint address (OUT, address 1) */ |
||||||
|
0x02, /*Bulk endpoint type */ |
||||||
|
LOBYTE(MSC_MAX_FS_PACKET), |
||||||
|
HIBYTE(MSC_MAX_FS_PACKET), |
||||||
|
0x00, /*Polling interval in milliseconds*/ |
||||||
|
|
||||||
|
/********************** IFACE ASSOC *************************/ |
||||||
|
/*32*/ 0x08, /* bLength */ |
||||||
|
USB_DESC_TYPE_IFACE_ASSOCIATION, /* bDescriptorType */ |
||||||
|
0x01, /* bFirstInterface */ |
||||||
|
0x02, /* bInterfaceCount */ |
||||||
|
0x02, /* bFunctionClass */ // #36
|
||||||
|
0x02, /* bFunctionSubClass (ACM) */ |
||||||
|
0x01, /* bFunctionProtocol (AT-COMMANDS) */ |
||||||
|
0x05, /* iFunction: string descriptor */ |
||||||
|
|
||||||
|
/********************** ACM interface **********************/ |
||||||
|
/*40*/ 0x09, /* bLength: Interface Descriptor size */ |
||||||
|
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */ |
||||||
|
0x01, /* bInterfaceNumber: Number of Interface */ |
||||||
|
0x00, /* bAlternateSetting: Alternate setting */ |
||||||
|
0x01, /* bNumEndpoints: One endpoints used */ |
||||||
|
0x02, /* bInterfaceClass: Communication Interface Class */ // #45
|
||||||
|
0x02, /* bInterfaceSubClass: Abstract Control Model */ |
||||||
|
0x01, /* bInterfaceProtocol: Common AT commands */ |
||||||
|
0x05, /* iInterface: string descriptor */ |
||||||
|
|
||||||
|
/**************** Header Functional Descriptor ***************/ |
||||||
|
/*49*/ 0x05, /* bLength: Endpoint Descriptor size */ |
||||||
|
0x24, /* bDescriptorType: CS_INTERFACE */ |
||||||
|
0x00, /* bDescriptorSubtype: Header Func Desc */ |
||||||
|
0x10, /* bcdCDC: spec release number */ |
||||||
|
0x01, |
||||||
|
|
||||||
|
/*********** Call Management Functional Descriptor **********/ |
||||||
|
/*54*/ 0x05, /* bFunctionLength */ |
||||||
|
0x24, /* bDescriptorType: CS_INTERFACE */ |
||||||
|
0x01, /* bDescriptorSubtype: Call Management Func Desc */ |
||||||
|
0x00, /* bmCapabilities: D0+D1 */ |
||||||
|
0x02, /* bDataInterface: 2 */ |
||||||
|
|
||||||
|
/*************** ACM Functional Descriptor ***************/ |
||||||
|
/*59*/ 0x04, /* bFunctionLength */ |
||||||
|
0x24, /* bDescriptorType: CS_INTERFACE */ |
||||||
|
0x02, /* bDescriptorSubtype: Abstract Control Management desc */ |
||||||
|
0x06, /* bmCapabilities XXX was:0x02? */ |
||||||
|
|
||||||
|
/************* Union Functional Descriptor **************/ |
||||||
|
/*63*/ 0x05, /* bFunctionLength */ |
||||||
|
0x24, /* bDescriptorType: CS_INTERFACE */ |
||||||
|
0x06, /* bDescriptorSubtype: Union func desc */ |
||||||
|
0x01, /* bMasterInterface: Communication class interface */ |
||||||
|
0x02, /* bSlaveInterface0: Data Class Interface */ |
||||||
|
|
||||||
|
/******************** CDC Endpoints ********************/ |
||||||
|
|
||||||
|
/// Command endpoint
|
||||||
|
/*68*/ 0x07, /* bLength: Endpoint Descriptor size */ |
||||||
|
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ |
||||||
|
CDC_CMD_EP, /* bEndpointAddress */ |
||||||
|
0x03, /* bmAttributes: Interrupt */ |
||||||
|
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: TODO: 2?*/ |
||||||
|
HIBYTE(CDC_CMD_PACKET_SIZE), |
||||||
|
0xFF, /* bInterval: TODO was 0x10?*/ |
||||||
|
|
||||||
|
/********** CDC Data Class Interface Descriptor ***********/ |
||||||
|
/*75*/ 0x09, /* bLength: Endpoint Descriptor size */ |
||||||
|
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */ |
||||||
|
0x02, /* bInterfaceNumber: Number of Interface */ |
||||||
|
0x00, /* bAlternateSetting: Alternate setting */ |
||||||
|
0x02, /* bNumEndpoints: Two endpoints used */ |
||||||
|
0x0A, /* bInterfaceClass: CDC */ // #80
|
||||||
|
0x00, /* bInterfaceSubClass: */ |
||||||
|
0x00, /* bInterfaceProtocol: */ |
||||||
|
0x06, /* iInterface: TODO: string descriptor */ |
||||||
|
|
||||||
|
/// Endpoint OUT Descriptor
|
||||||
|
/*84*/ 0x07, /* bLength: Endpoint Descriptor size */ |
||||||
|
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ |
||||||
|
CDC_OUT_EP, /* bEndpointAddress */ |
||||||
|
0x02, /* bmAttributes: Bulk */ |
||||||
|
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: TODO 8? */ |
||||||
|
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), |
||||||
|
0x00, /* bInterval: ignore for Bulk transfer */ |
||||||
|
|
||||||
|
/// Endpoint IN Descriptor
|
||||||
|
/*91*/ 0x07, /* bLength: Endpoint Descriptor size */ |
||||||
|
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ |
||||||
|
CDC_IN_EP, /* bEndpointAddress */ |
||||||
|
0x02, /* bmAttributes: Bulk */ |
||||||
|
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: TODO 16? */ |
||||||
|
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), |
||||||
|
0x00 /* bInterval: ignore for Bulk transfer */ |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
/* USB Standard Device Descriptor */ |
||||||
|
__ALIGN_BEGIN uint8_t USBD_MSC_CDC_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] __ALIGN_END = |
||||||
|
{ |
||||||
|
USB_LEN_DEV_QUALIFIER_DESC, |
||||||
|
USB_DESC_TYPE_DEVICE_QUALIFIER, |
||||||
|
0x00, |
||||||
|
0x02, |
||||||
|
0x00, |
||||||
|
0x00, |
||||||
|
0x00, |
||||||
|
MSC_MAX_FS_PACKET, |
||||||
|
0x01, |
||||||
|
0x00, |
||||||
|
}; |
||||||
|
|
||||||
|
static uint8_t USBD_MSC_CDC_Init(struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx) |
||||||
|
{ |
||||||
|
// this is not correct, but will work if neither returns BUSY (which they don't)
|
||||||
|
uint8_t ret = 0; |
||||||
|
ret |= USBD_MSC_Init(pdev, cfgidx); |
||||||
|
ret |= USBD_CDC_Init(pdev, cfgidx); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
static uint8_t USBD_MSC_CDC_DeInit(struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx) |
||||||
|
{ |
||||||
|
uint8_t ret = 0; |
||||||
|
ret |= USBD_MSC_DeInit(pdev, cfgidx); |
||||||
|
ret |= USBD_CDC_DeInit(pdev, cfgidx); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
/* Control Endpoints*/ |
||||||
|
static uint8_t USBD_MSC_CDC_Setup(struct _USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) |
||||||
|
{ |
||||||
|
static uint8_t interface_bit; |
||||||
|
|
||||||
|
// handle common
|
||||||
|
switch (req->bmRequest & USB_REQ_TYPE_MASK) { |
||||||
|
case USB_REQ_TYPE_STANDARD: |
||||||
|
switch (req->bRequest) { |
||||||
|
// those don't really do anything useful, but each class implements
|
||||||
|
// them and it would send the data twice.
|
||||||
|
case USB_REQ_GET_INTERFACE : |
||||||
|
USBD_CtlSendData(pdev, &interface_bit, 1); |
||||||
|
return USBD_OK; |
||||||
|
|
||||||
|
case USB_REQ_SET_INTERFACE : |
||||||
|
interface_bit = (uint8_t) (req->wValue); |
||||||
|
return USBD_OK; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// class specific, or Interface -> Clear Feature
|
||||||
|
USBD_MSC_Setup(pdev, req); |
||||||
|
USBD_CDC_Setup(pdev, req); |
||||||
|
return USBD_OK; |
||||||
|
} |
||||||
|
|
||||||
|
#if 0 |
||||||
|
static uint8_t USBD_MSC_CDC_EP0_TxSent(struct _USBD_HandleTypeDef *pdev) |
||||||
|
{ |
||||||
|
// not handled by either
|
||||||
|
xTaskNotify(tskUsbEventHandle, USBEVT_FLAG_EP0_TX_SENT, eSetBits); |
||||||
|
return USBD_OK; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
static uint8_t USBD_MSC_CDC_EP0_RxReady(struct _USBD_HandleTypeDef *pdev) |
||||||
|
{ |
||||||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE; |
||||||
|
xTaskNotifyFromISR(tskMainHandle, USBEVT_FLAG_EP0_RX_RDY, eSetBits, &xHigherPriorityTaskWoken); |
||||||
|
portYIELD_FROM_ISR(xHigherPriorityTaskWoken); |
||||||
|
//USBD_CDC_EP0_RxReady(pdev);
|
||||||
|
return USBD_OK; |
||||||
|
} |
||||||
|
|
||||||
|
/* Class Specific Endpoints*/ |
||||||
|
static uint8_t USBD_MSC_CDC_DataIn(struct _USBD_HandleTypeDef *pdev, uint8_t epnum) |
||||||
|
{ |
||||||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE; |
||||||
|
xTaskNotifyFromISR(tskMainHandle, USBEVT_FLAG_EPx_IN(epnum), eSetBits, &xHigherPriorityTaskWoken); |
||||||
|
portYIELD_FROM_ISR(xHigherPriorityTaskWoken); |
||||||
|
// if (epnum == MSC_EPIN_ADDR||epnum==MSC_EPOUT_ADDR) USBD_MSC_DataIn(pdev, epnum);
|
||||||
|
// else if (epnum == CDC_IN_EP||epnum == CDC_CMD_EP) USBD_CDC_DataIn(pdev, epnum);
|
||||||
|
return USBD_OK; |
||||||
|
} |
||||||
|
|
||||||
|
static uint8_t USBD_MSC_CDC_DataOut(struct _USBD_HandleTypeDef *pdev, uint8_t epnum) |
||||||
|
{ |
||||||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE; |
||||||
|
xTaskNotifyFromISR(tskMainHandle, USBEVT_FLAG_EPx_OUT(epnum), eSetBits, &xHigherPriorityTaskWoken); |
||||||
|
portYIELD_FROM_ISR(xHigherPriorityTaskWoken); |
||||||
|
// if (epnum == MSC_EPIN_ADDR||epnum == MSC_EPOUT_ADDR) USBD_MSC_DataOut(pdev, epnum);
|
||||||
|
// else if (epnum == CDC_OUT_EP||epnum == CDC_CMD_EP) USBD_CDC_DataOut(pdev, epnum);
|
||||||
|
return USBD_OK; |
||||||
|
} |
||||||
|
|
||||||
|
#if 0 |
||||||
|
static uint8_t USBD_MSC_CDC_SOF(struct _USBD_HandleTypeDef *pdev) |
||||||
|
{ |
||||||
|
// not handled by either
|
||||||
|
return USBD_OK; |
||||||
|
} |
||||||
|
|
||||||
|
static uint8_t USBD_MSC_CDC_IsoINIncomplete(struct _USBD_HandleTypeDef *pdev, uint8_t epnum) |
||||||
|
{ |
||||||
|
// not handled by either
|
||||||
|
return USBD_OK; |
||||||
|
} |
||||||
|
|
||||||
|
static uint8_t USBD_MSC_CDC_IsoOUTIncomplete(struct _USBD_HandleTypeDef *pdev, uint8_t epnum) |
||||||
|
{ |
||||||
|
// not handled by either
|
||||||
|
return USBD_OK; |
||||||
|
} |
||||||
|
|
||||||
|
static uint8_t *USBD_MSC_CDC_GetUsrStrDescriptor(struct _USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length) |
||||||
|
{ |
||||||
|
// not handled by either
|
||||||
|
return USBD_OK; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#if 0 |
||||||
|
uint8_t *USBD_MSC_CDC_GetHSCfgDesc (uint16_t *length) |
||||||
|
{ |
||||||
|
// *length = sizeof (USBD_MSC_CDC_CfgHSDesc);
|
||||||
|
// return USBD_MSC_CDC_CfgHSDesc;
|
||||||
|
*length = sizeof (USBD_MSC_CDC_CfgFSDesc); |
||||||
|
return USBD_MSC_CDC_CfgFSDesc; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
uint8_t *USBD_MSC_CDC_GetFSCfgDesc (uint16_t *length) |
||||||
|
{ |
||||||
|
*length = sizeof (USBD_MSC_CDC_CfgFSDesc); |
||||||
|
|
||||||
|
// Optionally hide CDC-ACM by setting its class to Vendor Specific
|
||||||
|
bool cdc_visible = SystemSettings.visible_vcom; |
||||||
|
USBD_MSC_CDC_CfgFSDesc[36] = (uint8_t) (cdc_visible ? 0x02 : 0xFF); |
||||||
|
USBD_MSC_CDC_CfgFSDesc[45] = (uint8_t) (cdc_visible ? 0x02 : 0xFF); |
||||||
|
USBD_MSC_CDC_CfgFSDesc[80] = (uint8_t) (cdc_visible ? 0x0A : 0xFF); |
||||||
|
|
||||||
|
// Optionally hide settings (if lock jumper is installed)
|
||||||
|
bool msc_visible = SystemSettings.editable; |
||||||
|
USBD_MSC_CDC_CfgFSDesc[14] = (uint8_t) (msc_visible ? 0x08 : 0xFF); |
||||||
|
|
||||||
|
return USBD_MSC_CDC_CfgFSDesc; |
||||||
|
} |
||||||
|
|
||||||
|
#if 0 |
||||||
|
uint8_t *USBD_MSC_CDC_GetOtherSpeedCfgDesc (uint16_t *length) |
||||||
|
{ |
||||||
|
// *length = sizeof (USBD_MSC_CDC_OtherSpeedCfgDesc);
|
||||||
|
// return USBD_MSC_CDC_OtherSpeedCfgDesc;
|
||||||
|
*length = sizeof (USBD_MSC_CDC_CfgFSDesc); |
||||||
|
return USBD_MSC_CDC_CfgFSDesc; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
uint8_t *USBD_MSC_CDC_GetDeviceQualifierDescriptor (uint16_t *length) |
||||||
|
{ |
||||||
|
*length = sizeof (USBD_MSC_CDC_DeviceQualifierDesc); |
||||||
|
return USBD_MSC_CDC_DeviceQualifierDesc; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t *USBD_MSC_CDC_GetUsrStrDescriptor(struct _USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length) |
||||||
|
{ |
||||||
|
const char *str; |
||||||
|
switch (index) { |
||||||
|
case 0x04: str = "Settings Virtual Mass Storage"; break; |
||||||
|
case 0x05: str = "Virtual Comport ACM"; break; |
||||||
|
case 0x06: str = "Virtual Comport CDC"; break; |
||||||
|
default: str = "No Descriptor"; |
||||||
|
} |
||||||
|
USBD_GetString ((uint8_t *) str, USBD_StrDesc, length); |
||||||
|
return USBD_StrDesc; |
||||||
|
} |
||||||
|
|
||||||
|
USBD_ClassTypeDef USBD_MSC_CDC = |
||||||
|
{ |
||||||
|
USBD_MSC_CDC_Init, |
||||||
|
USBD_MSC_CDC_DeInit, |
||||||
|
USBD_MSC_CDC_Setup, |
||||||
|
|
||||||
|
NULL, // USBD_MSC_CDC_EP0_TxSent,
|
||||||
|
USBD_MSC_CDC_EP0_RxReady, |
||||||
|
|
||||||
|
USBD_MSC_CDC_DataIn, |
||||||
|
USBD_MSC_CDC_DataOut, |
||||||
|
|
||||||
|
NULL, // USBD_MSC_CDC_SOF,
|
||||||
|
NULL, // USBD_MSC_CDC_IsoINIncomplete,
|
||||||
|
NULL, // USBD_MSC_CDC_IsoOUTIncomplete,
|
||||||
|
|
||||||
|
// we return only the FS one, others are useless
|
||||||
|
USBD_MSC_CDC_GetFSCfgDesc, //USBD_MSC_CDC_GetHSCfgDesc,
|
||||||
|
USBD_MSC_CDC_GetFSCfgDesc, |
||||||
|
USBD_MSC_CDC_GetFSCfgDesc, //USBD_MSC_CDC_GetOtherSpeedCfgDesc,
|
||||||
|
|
||||||
|
USBD_MSC_CDC_GetDeviceQualifierDescriptor, |
||||||
|
USBD_MSC_CDC_GetUsrStrDescriptor |
||||||
|
}; |
@ -0,0 +1,11 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/07.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_USBD_CDC_MSC_H |
||||||
|
#define GEX_USBD_CDC_MSC_H |
||||||
|
|
||||||
|
#include "usbd_def.h" |
||||||
|
extern USBD_ClassTypeDef USBD_MSC_CDC; |
||||||
|
|
||||||
|
#endif //GEX_USBD_CDC_MSC_H
|
@ -0,0 +1,115 @@ |
|||||||
|
/**
|
||||||
|
****************************************************************************** |
||||||
|
* @file : USB_DEVICE
|
||||||
|
* @version : v2.0_Cube |
||||||
|
* @brief : This file implements the USB Device
|
||||||
|
****************************************************************************** |
||||||
|
* This notice applies to any and all portions of this file |
||||||
|
* that are not between comment pairs USER CODE BEGIN and |
||||||
|
* USER CODE END. Other portions of this file, whether
|
||||||
|
* inserted by the user or by software development tools |
||||||
|
* are owned by their respective copyright owners. |
||||||
|
* |
||||||
|
* Copyright (c) 2017 STMicroelectronics International N.V.
|
||||||
|
* 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. Redistribution 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. |
||||||
|
* 3. Neither the name of STMicroelectronics nor the names of other
|
||||||
|
* contributors to this software may be used to endorse or promote products
|
||||||
|
* derived from this software without specific written permission. |
||||||
|
* 4. This software, including modifications and/or derivative works of this
|
||||||
|
* software, must execute solely and exclusively on microcontroller or |
||||||
|
* microprocessor devices manufactured by or for STMicroelectronics. |
||||||
|
* 5. Redistribution and use of this software other than as permitted under
|
||||||
|
* this license is void and will automatically terminate your rights under
|
||||||
|
* this license.
|
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY |
||||||
|
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
|
||||||
|
* SHALL STMICROELECTRONICS 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. |
||||||
|
* |
||||||
|
****************************************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Includes ------------------------------------------------------------------*/ |
||||||
|
#include "platform.h" |
||||||
|
|
||||||
|
#include "usb_device.h" |
||||||
|
#include "usbd_core.h" |
||||||
|
#include "usbd_desc.h" |
||||||
|
#include "usbd_msc.h" |
||||||
|
#include "usbd_storage_if.h" |
||||||
|
#include "usbd_cdc.h" |
||||||
|
#include "usbd_cdc_if.h" |
||||||
|
#include "usbd_msc_cdc.h" |
||||||
|
|
||||||
|
/* USB Device Core handle declaration */ |
||||||
|
USBD_HandleTypeDef hUsbDeviceFS; |
||||||
|
|
||||||
|
/* USER CODE BEGIN Includes */ |
||||||
|
extern PCD_HandleTypeDef hpcd_USB_FS; |
||||||
|
/* USER CODE END Includes */ |
||||||
|
|
||||||
|
/* init function */
|
||||||
|
void MX_USB_DEVICE_Init(void) |
||||||
|
{ |
||||||
|
/* USER CODE BEGIN USB_DEVICE_Init_PreTreatment */ |
||||||
|
|
||||||
|
/* USER CODE END USB_DEVICE_Init_PreTreatment */ |
||||||
|
|
||||||
|
/* Init Device Library,Add Supported Class and Start the library*/ |
||||||
|
USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS); |
||||||
|
|
||||||
|
USBD_RegisterClass(&hUsbDeviceFS, &USBD_MSC_CDC); |
||||||
|
|
||||||
|
USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_Interface_fops_FS); |
||||||
|
USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS); |
||||||
|
|
||||||
|
USBD_Start(&hUsbDeviceFS); |
||||||
|
|
||||||
|
/* USER CODE BEGIN USB_DEVICE_Init_PostTreatment */ |
||||||
|
|
||||||
|
/* USER CODE END USB_DEVICE_Init_PostTreatment */ |
||||||
|
} |
||||||
|
|
||||||
|
/* USER CODE BEGIN USB_IRQ */ |
||||||
|
/**
|
||||||
|
* @brief This function handles USB low priority or CAN RX0 interrupts. |
||||||
|
*/ |
||||||
|
void USB_LP_CAN1_RX0_IRQHandler(void) |
||||||
|
{ |
||||||
|
/* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 0 */ |
||||||
|
|
||||||
|
/* USER CODE END USB_LP_CAN1_RX0_IRQn 0 */ |
||||||
|
HAL_PCD_IRQHandler(&hpcd_USB_FS); |
||||||
|
/* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 1 */ |
||||||
|
|
||||||
|
/* USER CODE END USB_LP_CAN1_RX0_IRQn 1 */ |
||||||
|
} |
||||||
|
/* USER CODE END USB_IRQ */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/ |
||||||
|
|
||||||
|
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
@ -0,0 +1,78 @@ |
|||||||
|
/**
|
||||||
|
****************************************************************************** |
||||||
|
* @file : USB_DEVICE |
||||||
|
* @version : v2.0_Cube |
||||||
|
* @brief : Header for usb_device file. |
||||||
|
****************************************************************************** |
||||||
|
* This notice applies to any and all portions of this file |
||||||
|
* that are not between comment pairs USER CODE BEGIN and |
||||||
|
* USER CODE END. Other portions of this file, whether
|
||||||
|
* inserted by the user or by software development tools |
||||||
|
* are owned by their respective copyright owners. |
||||||
|
* |
||||||
|
* Copyright (c) 2017 STMicroelectronics International N.V.
|
||||||
|
* 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. Redistribution 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. |
||||||
|
* 3. Neither the name of STMicroelectronics nor the names of other
|
||||||
|
* contributors to this software may be used to endorse or promote products
|
||||||
|
* derived from this software without specific written permission. |
||||||
|
* 4. This software, including modifications and/or derivative works of this
|
||||||
|
* software, must execute solely and exclusively on microcontroller or |
||||||
|
* microprocessor devices manufactured by or for STMicroelectronics. |
||||||
|
* 5. Redistribution and use of this software other than as permitted under
|
||||||
|
* this license is void and will automatically terminate your rights under
|
||||||
|
* this license.
|
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY |
||||||
|
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
|
||||||
|
* SHALL STMICROELECTRONICS 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. |
||||||
|
* |
||||||
|
****************************************************************************** |
||||||
|
*/ |
||||||
|
/* Define to prevent recursive inclusion -------------------------------------*/ |
||||||
|
#ifndef __usb_device_H |
||||||
|
#define __usb_device_H |
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Includes ------------------------------------------------------------------*/ |
||||||
|
#include "platform.h" |
||||||
|
#include "usbd_def.h" |
||||||
|
|
||||||
|
extern USBD_HandleTypeDef hUsbDeviceFS; |
||||||
|
|
||||||
|
/* USB_Device init function */
|
||||||
|
void MX_USB_DEVICE_Init(void); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
#endif /*__usb_device_H */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/ |
||||||
|
|
||||||
|
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
@ -0,0 +1,326 @@ |
|||||||
|
/**
|
||||||
|
****************************************************************************** |
||||||
|
* @file : usbd_cdc_if.c |
||||||
|
* @brief : |
||||||
|
****************************************************************************** |
||||||
|
* This notice applies to any and all portions of this file |
||||||
|
* that are not between comment pairs USER CODE BEGIN and |
||||||
|
* USER CODE END. Other portions of this file, whether
|
||||||
|
* inserted by the user or by software development tools |
||||||
|
* are owned by their respective copyright owners. |
||||||
|
* |
||||||
|
* Copyright (c) 2017 STMicroelectronics International N.V.
|
||||||
|
* 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. Redistribution 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. |
||||||
|
* 3. Neither the name of STMicroelectronics nor the names of other
|
||||||
|
* contributors to this software may be used to endorse or promote products
|
||||||
|
* derived from this software without specific written permission. |
||||||
|
* 4. This software, including modifications and/or derivative works of this
|
||||||
|
* software, must execute solely and exclusively on microcontroller or |
||||||
|
* microprocessor devices manufactured by or for STMicroelectronics. |
||||||
|
* 5. Redistribution and use of this software other than as permitted under
|
||||||
|
* this license is void and will automatically terminate your rights under
|
||||||
|
* this license.
|
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY |
||||||
|
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
|
||||||
|
* SHALL STMICROELECTRONICS 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. |
||||||
|
* |
||||||
|
****************************************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Includes ------------------------------------------------------------------*/ |
||||||
|
#include "platform.h" |
||||||
|
#include "usbd_cdc_if.h" |
||||||
|
/* USER CODE BEGIN INCLUDE */ |
||||||
|
#include "task_main.h" |
||||||
|
#include "TinyFrame.h" |
||||||
|
#include "comm/messages.h" |
||||||
|
|
||||||
|
extern osSemaphoreId semVcomTxReadyHandle; |
||||||
|
/* USER CODE END INCLUDE */ |
||||||
|
|
||||||
|
/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @defgroup USBD_CDC
|
||||||
|
* @brief usbd core module |
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @defgroup USBD_CDC_Private_TypesDefinitions
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
/* USER CODE BEGIN PRIVATE_TYPES */ |
||||||
|
/* USER CODE END PRIVATE_TYPES */ |
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @defgroup USBD_CDC_Private_Defines
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
/* USER CODE BEGIN PRIVATE_DEFINES */ |
||||||
|
/* Define size for the receive and transmit buffer over CDC */ |
||||||
|
/* It's up to user to redefine and/or remove those define */ |
||||||
|
#define APP_RX_DATA_SIZE 64 |
||||||
|
#define APP_TX_DATA_SIZE 64 |
||||||
|
/* USER CODE END PRIVATE_DEFINES */ |
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @defgroup USBD_CDC_Private_Macros
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
/* USER CODE BEGIN PRIVATE_MACRO */ |
||||||
|
/* USER CODE END PRIVATE_MACRO */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @defgroup USBD_CDC_Private_Variables
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
/* Create buffer for reception and transmission */ |
||||||
|
/* It's up to user to redefine and/or remove those define */ |
||||||
|
/* Received Data over USB are stored in this buffer */ |
||||||
|
uint8_t UserRxBufferFS[APP_RX_DATA_SIZE]; |
||||||
|
|
||||||
|
/* Send Data over USB CDC are stored in this buffer */ |
||||||
|
//uint8_t UserTxBufferFS[APP_TX_DATA_SIZE];
|
||||||
|
|
||||||
|
/* USER CODE BEGIN PRIVATE_VARIABLES */ |
||||||
|
/* USER CODE END PRIVATE_VARIABLES */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @defgroup USBD_CDC_IF_Exported_Variables
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
extern USBD_HandleTypeDef hUsbDeviceFS; |
||||||
|
/* USER CODE BEGIN EXPORTED_VARIABLES */ |
||||||
|
/* USER CODE END EXPORTED_VARIABLES */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @defgroup USBD_CDC_Private_FunctionPrototypes
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
static int8_t CDC_Init_FS (void); |
||||||
|
static int8_t CDC_DeInit_FS (void); |
||||||
|
static int8_t CDC_Control_FS (uint8_t cmd, uint8_t* pbuf, uint16_t length); |
||||||
|
static int8_t CDC_Receive_FS (uint8_t *Buf, uint32_t *Len); |
||||||
|
|
||||||
|
/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */ |
||||||
|
/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/ |
||||||
|
|
||||||
|
USBD_CDC_ItfTypeDef USBD_Interface_fops_FS = |
||||||
|
{ |
||||||
|
CDC_Init_FS, |
||||||
|
CDC_DeInit_FS, |
||||||
|
CDC_Control_FS, |
||||||
|
CDC_Receive_FS |
||||||
|
}; |
||||||
|
|
||||||
|
/* Private functions ---------------------------------------------------------*/ |
||||||
|
/**
|
||||||
|
* @brief CDC_Init_FS |
||||||
|
* Initializes the CDC media low layer over the FS USB IP |
||||||
|
* @param None |
||||||
|
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL |
||||||
|
*/ |
||||||
|
static int8_t CDC_Init_FS(void) |
||||||
|
{ |
||||||
|
/* USER CODE BEGIN 3 */ |
||||||
|
/* Set Application Buffers */ |
||||||
|
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, NULL, 0); |
||||||
|
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS); |
||||||
|
|
||||||
|
return (USBD_OK); |
||||||
|
/* USER CODE END 3 */ |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CDC_DeInit_FS |
||||||
|
* DeInitializes the CDC media low layer |
||||||
|
* @param None |
||||||
|
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL |
||||||
|
*/ |
||||||
|
static int8_t CDC_DeInit_FS(void) |
||||||
|
{ |
||||||
|
/* USER CODE BEGIN 4 */ |
||||||
|
return (USBD_OK); |
||||||
|
/* USER CODE END 4 */ |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CDC_Control_FS |
||||||
|
* Manage the CDC class requests |
||||||
|
* @param cmd: Command code
|
||||||
|
* @param pbuf: Buffer containing command data (request parameters) |
||||||
|
* @param length: Number of data to be sent (in bytes) |
||||||
|
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL |
||||||
|
*/ |
||||||
|
static int8_t CDC_Control_FS (uint8_t cmd, uint8_t* pbuf, uint16_t length) |
||||||
|
{ |
||||||
|
/* USER CODE BEGIN 5 */ |
||||||
|
switch (cmd) |
||||||
|
{ |
||||||
|
case CDC_SEND_ENCAPSULATED_COMMAND: |
||||||
|
break; |
||||||
|
|
||||||
|
case CDC_GET_ENCAPSULATED_RESPONSE: |
||||||
|
break; |
||||||
|
|
||||||
|
case CDC_SET_COMM_FEATURE: |
||||||
|
break; |
||||||
|
|
||||||
|
case CDC_GET_COMM_FEATURE: |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case CDC_CLEAR_COMM_FEATURE: |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
/*******************************************************************************/ |
||||||
|
/* Line Coding Structure */ |
||||||
|
/*-----------------------------------------------------------------------------*/ |
||||||
|
/* Offset | Field | Size | Value | Description */ |
||||||
|
/* 0 | dwDTERate | 4 | Number |Data terminal rate, in bits per second*/ |
||||||
|
/* 4 | bCharFormat | 1 | Number | Stop bits */ |
||||||
|
/* 0 - 1 Stop bit */ |
||||||
|
/* 1 - 1.5 Stop bits */ |
||||||
|
/* 2 - 2 Stop bits */ |
||||||
|
/* 5 | bParityType | 1 | Number | Parity */ |
||||||
|
/* 0 - None */ |
||||||
|
/* 1 - Odd */ |
||||||
|
/* 2 - Even */ |
||||||
|
/* 3 - Mark */ |
||||||
|
/* 4 - Space */ |
||||||
|
/* 6 | bDataBits | 1 | Number Data bits (5, 6, 7, 8 or 16). */ |
||||||
|
/*******************************************************************************/ |
||||||
|
case CDC_SET_LINE_CODING: |
||||||
|
break; |
||||||
|
|
||||||
|
case CDC_GET_LINE_CODING: |
||||||
|
break; |
||||||
|
|
||||||
|
case CDC_SET_CONTROL_LINE_STATE: |
||||||
|
break; |
||||||
|
|
||||||
|
case CDC_SEND_BREAK: |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
return (USBD_OK); |
||||||
|
/* USER CODE END 5 */ |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CDC_Receive_FS |
||||||
|
* Data received over USB OUT endpoint are sent over CDC interface
|
||||||
|
* through this function. |
||||||
|
*
|
||||||
|
* @note |
||||||
|
* This function will block any OUT packet reception on USB endpoint
|
||||||
|
* untill exiting this function. If you exit this function before transfer |
||||||
|
* is complete on CDC interface (ie. using DMA controller) it will result
|
||||||
|
* in receiving more data while previous ones are still not sent. |
||||||
|
*
|
||||||
|
* @param Buf: Buffer of data to be received |
||||||
|
* @param Len: Number of data received (in bytes) |
||||||
|
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL |
||||||
|
*/ |
||||||
|
static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len) |
||||||
|
{ |
||||||
|
/* USER CODE BEGIN 6 */ |
||||||
|
// this does nothing?!
|
||||||
|
// the buffer was already assigned in the init function and we got it as argument here?!
|
||||||
|
// USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
|
||||||
|
|
||||||
|
assert_param(*Len <= APP_RX_DATA_SIZE); |
||||||
|
USBD_CDC_ReceivePacket(&hUsbDeviceFS); |
||||||
|
TF_Accept(comm, Buf, *Len); |
||||||
|
|
||||||
|
return (USBD_OK); |
||||||
|
/* USER CODE END 6 */ |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CDC_Transmit_FS |
||||||
|
* Data send over USB IN endpoint are sent over CDC interface
|
||||||
|
* through this function.
|
||||||
|
* @note |
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param Buf: Buffer of data to be send - does not need to stay alive after this call |
||||||
|
* @param Len: Number of data to be send (in bytes) |
||||||
|
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL or USBD_BUSY |
||||||
|
*/ |
||||||
|
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len) |
||||||
|
{ |
||||||
|
uint8_t result = USBD_OK; |
||||||
|
/* USER CODE BEGIN 7 */ |
||||||
|
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData2; |
||||||
|
if (hcdc->TxState != 0){ |
||||||
|
return USBD_BUSY; |
||||||
|
} |
||||||
|
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len); |
||||||
|
result = USBD_CDC_TransmitPacket(&hUsbDeviceFS); |
||||||
|
/* USER CODE END 7 */ |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */ |
||||||
|
void USBD_CDC_TransmitDone(USBD_HandleTypeDef *pdev) |
||||||
|
{ |
||||||
|
assert_param(xTaskGetCurrentTaskHandle() == tskMainHandle); |
||||||
|
|
||||||
|
// Notify the semaphore that we're ready to transmit more
|
||||||
|
assert_param(semVcomTxReadyHandle != NULL); |
||||||
|
xSemaphoreGive(semVcomTxReadyHandle); |
||||||
|
} |
||||||
|
/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/ |
||||||
|
|
||||||
|
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
||||||
|
|
@ -0,0 +1,144 @@ |
|||||||
|
/**
|
||||||
|
****************************************************************************** |
||||||
|
* @file : usbd_cdc_if.h |
||||||
|
* @brief : Header for usbd_cdc_if file. |
||||||
|
****************************************************************************** |
||||||
|
* This notice applies to any and all portions of this file |
||||||
|
* that are not between comment pairs USER CODE BEGIN and |
||||||
|
* USER CODE END. Other portions of this file, whether
|
||||||
|
* inserted by the user or by software development tools |
||||||
|
* are owned by their respective copyright owners. |
||||||
|
* |
||||||
|
* Copyright (c) 2017 STMicroelectronics International N.V.
|
||||||
|
* 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. Redistribution 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. |
||||||
|
* 3. Neither the name of STMicroelectronics nor the names of other
|
||||||
|
* contributors to this software may be used to endorse or promote products
|
||||||
|
* derived from this software without specific written permission. |
||||||
|
* 4. This software, including modifications and/or derivative works of this
|
||||||
|
* software, must execute solely and exclusively on microcontroller or |
||||||
|
* microprocessor devices manufactured by or for STMicroelectronics. |
||||||
|
* 5. Redistribution and use of this software other than as permitted under
|
||||||
|
* this license is void and will automatically terminate your rights under
|
||||||
|
* this license.
|
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY |
||||||
|
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
|
||||||
|
* SHALL STMICROELECTRONICS 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. |
||||||
|
* |
||||||
|
****************************************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Define to prevent recursive inclusion -------------------------------------*/ |
||||||
|
#ifndef __USBD_CDC_IF_H |
||||||
|
#define __USBD_CDC_IF_H |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
/* Includes ------------------------------------------------------------------*/ |
||||||
|
#include "platform.h" |
||||||
|
#include "usbd_cdc.h" |
||||||
|
/* USER CODE BEGIN INCLUDE */ |
||||||
|
/* USER CODE END INCLUDE */ |
||||||
|
|
||||||
|
/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @defgroup USBD_CDC_IF
|
||||||
|
* @brief header
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_CDC_IF_Exported_Defines
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
/* USER CODE BEGIN EXPORTED_DEFINES */ |
||||||
|
/* USER CODE END EXPORTED_DEFINES */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_CDC_IF_Exported_Types
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
/* USER CODE BEGIN EXPORTED_TYPES */ |
||||||
|
/* USER CODE END EXPORTED_TYPES */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_CDC_IF_Exported_Macros
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
/* USER CODE BEGIN EXPORTED_MACRO */ |
||||||
|
/* USER CODE END EXPORTED_MACRO */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_AUDIO_IF_Exported_Variables
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
extern USBD_CDC_ItfTypeDef USBD_Interface_fops_FS; |
||||||
|
|
||||||
|
/* USER CODE BEGIN EXPORTED_VARIABLES */ |
||||||
|
/* USER CODE END EXPORTED_VARIABLES */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_CDC_IF_Exported_FunctionsPrototype
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len); |
||||||
|
|
||||||
|
/* USER CODE BEGIN EXPORTED_FUNCTIONS */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* Semaphore that's "given" whenever the Tx queue is ready |
||||||
|
*/ |
||||||
|
extern SemaphoreHandle_t semCDCTxReady; |
||||||
|
|
||||||
|
/* USER CODE END EXPORTED_FUNCTIONS */ |
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* __USBD_CDC_IF_H */ |
||||||
|
|
||||||
|
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
@ -0,0 +1,542 @@ |
|||||||
|
/**
|
||||||
|
****************************************************************************** |
||||||
|
* @file : usbd_conf.c |
||||||
|
* @version : v2.0_Cube |
||||||
|
* @brief : This file implements the board support package for the USB device library |
||||||
|
****************************************************************************** |
||||||
|
* This notice applies to any and all portions of this file |
||||||
|
* that are not between comment pairs USER CODE BEGIN and |
||||||
|
* USER CODE END. Other portions of this file, whether
|
||||||
|
* inserted by the user or by software development tools |
||||||
|
* are owned by their respective copyright owners. |
||||||
|
* |
||||||
|
* Copyright (c) 2017 STMicroelectronics International N.V.
|
||||||
|
* 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. Redistribution 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. |
||||||
|
* 3. Neither the name of STMicroelectronics nor the names of other
|
||||||
|
* contributors to this software may be used to endorse or promote products
|
||||||
|
* derived from this software without specific written permission. |
||||||
|
* 4. This software, including modifications and/or derivative works of this
|
||||||
|
* software, must execute solely and exclusively on microcontroller or |
||||||
|
* microprocessor devices manufactured by or for STMicroelectronics. |
||||||
|
* 5. Redistribution and use of this software other than as permitted under
|
||||||
|
* this license is void and will automatically terminate your rights under
|
||||||
|
* this license.
|
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY |
||||||
|
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
|
||||||
|
* SHALL STMICROELECTRONICS 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. |
||||||
|
* |
||||||
|
****************************************************************************** |
||||||
|
*/ |
||||||
|
/* Includes ------------------------------------------------------------------*/ |
||||||
|
#include "platform.h" |
||||||
|
#include "usbd_def.h" |
||||||
|
#include "usbd_core.h" |
||||||
|
#include "usbd_msc.h" |
||||||
|
#include "usbd_cdc.h" |
||||||
|
/* Private typedef -----------------------------------------------------------*/ |
||||||
|
/* Private define ------------------------------------------------------------*/ |
||||||
|
/* Private macro -------------------------------------------------------------*/ |
||||||
|
/* Private variables ---------------------------------------------------------*/ |
||||||
|
extern PCD_HandleTypeDef hpcd_USB_FS; |
||||||
|
|
||||||
|
/* USER CODE BEGIN 0 */ |
||||||
|
/* USER CODE END 0 */ |
||||||
|
|
||||||
|
/* Private function prototypes -----------------------------------------------*/ |
||||||
|
/* Private functions ---------------------------------------------------------*/ |
||||||
|
/* USER CODE BEGIN 1 */ |
||||||
|
/* USER CODE END 1 */ |
||||||
|
void HAL_PCDEx_SetConnectionState(PCD_HandleTypeDef *hpcd, uint8_t state); |
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
LL Driver Callbacks (PCD -> USB Device Library) |
||||||
|
*******************************************************************************/ |
||||||
|
/* MSP Init */ |
||||||
|
void HAL_PCD_MspInit(PCD_HandleTypeDef* pcdHandle) |
||||||
|
{ |
||||||
|
if(pcdHandle->Instance==USB) |
||||||
|
{ |
||||||
|
/* USER CODE BEGIN USB_MspInit 0 */ |
||||||
|
|
||||||
|
/* USER CODE END USB_MspInit 0 */ |
||||||
|
/* Peripheral clock enable */ |
||||||
|
__HAL_RCC_USB_CLK_ENABLE(); |
||||||
|
|
||||||
|
/* Peripheral interrupt init */ |
||||||
|
HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 5, 0); |
||||||
|
HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn); |
||||||
|
/* USER CODE BEGIN USB_MspInit 1 */ |
||||||
|
|
||||||
|
/* USER CODE END USB_MspInit 1 */ |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void HAL_PCD_MspDeInit(PCD_HandleTypeDef* pcdHandle) |
||||||
|
{ |
||||||
|
if(pcdHandle->Instance==USB) |
||||||
|
{ |
||||||
|
/* USER CODE BEGIN USB_MspDeInit 0 */ |
||||||
|
|
||||||
|
/* USER CODE END USB_MspDeInit 0 */ |
||||||
|
/* Peripheral clock disable */ |
||||||
|
__HAL_RCC_USB_CLK_DISABLE(); |
||||||
|
|
||||||
|
/* Peripheral interrupt Deinit*/ |
||||||
|
HAL_NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn); |
||||||
|
|
||||||
|
/* USER CODE BEGIN USB_MspDeInit 1 */ |
||||||
|
|
||||||
|
/* USER CODE END USB_MspDeInit 1 */ |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Setup Stage callback |
||||||
|
* @param hpcd: PCD handle |
||||||
|
* @retval None |
||||||
|
*/ |
||||||
|
void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd) |
||||||
|
{ |
||||||
|
USBD_LL_SetupStage((USBD_HandleTypeDef*)hpcd->pData, (uint8_t *)hpcd->Setup); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Data Out Stage callback. |
||||||
|
* @param hpcd: PCD handle |
||||||
|
* @param epnum: Endpoint Number |
||||||
|
* @retval None |
||||||
|
*/ |
||||||
|
void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) |
||||||
|
{ |
||||||
|
USBD_LL_DataOutStage((USBD_HandleTypeDef*)hpcd->pData, epnum, hpcd->OUT_ep[epnum].xfer_buff); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Data In Stage callback.. |
||||||
|
* @param hpcd: PCD handle |
||||||
|
* @param epnum: Endpoint Number |
||||||
|
* @retval None |
||||||
|
*/ |
||||||
|
void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) |
||||||
|
{ |
||||||
|
USBD_LL_DataInStage((USBD_HandleTypeDef*)hpcd->pData, epnum, hpcd->IN_ep[epnum].xfer_buff); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SOF callback. |
||||||
|
* @param hpcd: PCD handle |
||||||
|
* @retval None |
||||||
|
*/ |
||||||
|
void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd) |
||||||
|
{ |
||||||
|
USBD_LL_SOF((USBD_HandleTypeDef*)hpcd->pData); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset callback. |
||||||
|
* @param hpcd: PCD handle |
||||||
|
* @retval None |
||||||
|
*/ |
||||||
|
void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd) |
||||||
|
{
|
||||||
|
// USBD_SpeedTypeDef speed = USBD_SPEED_FULL;
|
||||||
|
|
||||||
|
/*Set USB Current Speed*/ |
||||||
|
// switch (hpcd->Init.speed)
|
||||||
|
// {
|
||||||
|
// case PCD_SPEED_FULL:
|
||||||
|
// speed = USBD_SPEED_FULL;
|
||||||
|
// break;
|
||||||
|
//
|
||||||
|
// default:
|
||||||
|
// speed = USBD_SPEED_FULL;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
USBD_LL_SetSpeed((USBD_HandleTypeDef*)hpcd->pData, USBD_SPEED_FULL); |
||||||
|
|
||||||
|
/*Reset Device*/ |
||||||
|
USBD_LL_Reset((USBD_HandleTypeDef*)hpcd->pData); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Suspend callback. |
||||||
|
* When Low power mode is enabled the debug cannot be used (IAR, Keil doesn't support it) |
||||||
|
* @param hpcd: PCD handle |
||||||
|
* @retval None |
||||||
|
*/ |
||||||
|
void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd) |
||||||
|
{ |
||||||
|
/* Inform USB library that core enters in suspend Mode */ |
||||||
|
USBD_LL_Suspend((USBD_HandleTypeDef*)hpcd->pData); |
||||||
|
/*Enter in STOP mode */ |
||||||
|
/* USER CODE BEGIN 2 */
|
||||||
|
if (hpcd->Init.low_power_enable) |
||||||
|
{ |
||||||
|
/* Set SLEEPDEEP bit and SleepOnExit of Cortex System Control Register */ |
||||||
|
SCB->SCR |= (uint32_t)((uint32_t)(SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk)); |
||||||
|
} |
||||||
|
/* USER CODE END 2 */ |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resume callback. |
||||||
|
* When Low power mode is enabled the debug cannot be used (IAR, Keil doesn't support it) |
||||||
|
* @param hpcd: PCD handle |
||||||
|
* @retval None |
||||||
|
*/ |
||||||
|
void HAL_PCD_ResumeCallback(PCD_HandleTypeDef *hpcd) |
||||||
|
{ |
||||||
|
/* USER CODE BEGIN 3 */ |
||||||
|
/* USER CODE END 3 */ |
||||||
|
USBD_LL_Resume((USBD_HandleTypeDef*)hpcd->pData); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ISOOUTIncomplete callback. |
||||||
|
* @param hpcd: PCD handle |
||||||
|
* @param epnum: Endpoint Number |
||||||
|
* @retval None |
||||||
|
*/ |
||||||
|
void HAL_PCD_ISOOUTIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) |
||||||
|
{ |
||||||
|
USBD_LL_IsoOUTIncomplete((USBD_HandleTypeDef*)hpcd->pData, epnum); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ISOINIncomplete callback. |
||||||
|
* @param hpcd: PCD handle |
||||||
|
* @param epnum: Endpoint Number |
||||||
|
* @retval None |
||||||
|
*/ |
||||||
|
void HAL_PCD_ISOINIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) |
||||||
|
{ |
||||||
|
USBD_LL_IsoINIncomplete((USBD_HandleTypeDef*)hpcd->pData, epnum); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ConnectCallback callback. |
||||||
|
* @param hpcd: PCD handle |
||||||
|
* @retval None |
||||||
|
*/ |
||||||
|
void HAL_PCD_ConnectCallback(PCD_HandleTypeDef *hpcd) |
||||||
|
{ |
||||||
|
USBD_LL_DevConnected((USBD_HandleTypeDef*)hpcd->pData); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Disconnect callback. |
||||||
|
* @param hpcd: PCD handle |
||||||
|
* @retval None |
||||||
|
*/ |
||||||
|
void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd) |
||||||
|
{ |
||||||
|
USBD_LL_DevDisconnected((USBD_HandleTypeDef*)hpcd->pData); |
||||||
|
} |
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
LL Driver Interface (USB Device Library --> PCD) |
||||||
|
*******************************************************************************/ |
||||||
|
/**
|
||||||
|
* @brief Initializes the Low Level portion of the Device driver. |
||||||
|
* @param pdev: Device handle |
||||||
|
* @retval USBD Status |
||||||
|
*/ |
||||||
|
USBD_StatusTypeDef USBD_LL_Init (USBD_HandleTypeDef *pdev) |
||||||
|
{
|
||||||
|
/* Init USB_IP */ |
||||||
|
/* Link The driver to the stack */ |
||||||
|
hpcd_USB_FS.pData = pdev; |
||||||
|
pdev->pData = &hpcd_USB_FS; |
||||||
|
|
||||||
|
hpcd_USB_FS.Instance = USB; |
||||||
|
hpcd_USB_FS.Init.dev_endpoints = 8; |
||||||
|
hpcd_USB_FS.Init.speed = PCD_SPEED_FULL; |
||||||
|
hpcd_USB_FS.Init.ep0_mps = DEP0CTL_MPS_8; |
||||||
|
hpcd_USB_FS.Init.low_power_enable = DISABLE; |
||||||
|
hpcd_USB_FS.Init.lpm_enable = DISABLE; |
||||||
|
hpcd_USB_FS.Init.battery_charging_enable = DISABLE; |
||||||
|
if (HAL_PCD_Init(&hpcd_USB_FS) != HAL_OK) |
||||||
|
{ |
||||||
|
_Error_Handler(__FILE__, __LINE__); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t ptr = 0; |
||||||
|
const int TOTAL_EPS = (USBD_NUM_ENDPOINTS*2+1); |
||||||
|
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, ptr = TOTAL_EPS*8); // 64
|
||||||
|
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, ptr += 64); // 64
|
||||||
|
|
||||||
|
// MSC endpoints - EP1, two-way
|
||||||
|
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , MSC_EPIN_ADDR , PCD_SNG_BUF, ptr += 64); // 64
|
||||||
|
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , MSC_EPOUT_ADDR , PCD_SNG_BUF, ptr += 64); // 64
|
||||||
|
|
||||||
|
// CDC endpoints, EP2 two-way and EP3 in-only
|
||||||
|
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_IN_EP , PCD_SNG_BUF, ptr += 64); // 64
|
||||||
|
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_OUT_EP , PCD_SNG_BUF, ptr += 64); // 64
|
||||||
|
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_CMD_EP , PCD_SNG_BUF, ptr += 16); // 16
|
||||||
|
|
||||||
|
(void)ptr; |
||||||
|
|
||||||
|
return USBD_OK; |
||||||
|
} |
||||||
|
|
||||||
|
#define HAL_TO_USB_STATUS(hal) ((hal)==HAL_OK?USBD_OK:(hal)==HAL_BUSY?USBD_BUSY:USBD_FAIL) |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief De-Initializes the Low Level portion of the Device driver. |
||||||
|
* @param pdev: Device handle |
||||||
|
* @retval USBD Status |
||||||
|
*/ |
||||||
|
USBD_StatusTypeDef USBD_LL_DeInit (USBD_HandleTypeDef *pdev) |
||||||
|
{ |
||||||
|
HAL_StatusTypeDef hal_status = HAL_PCD_DeInit(pdev->pData); |
||||||
|
return HAL_TO_USB_STATUS(hal_status); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Starts the Low Level portion of the Device driver.
|
||||||
|
* @param pdev: Device handle |
||||||
|
* @retval USBD Status |
||||||
|
*/ |
||||||
|
USBD_StatusTypeDef USBD_LL_Start(USBD_HandleTypeDef *pdev) |
||||||
|
{ |
||||||
|
HAL_StatusTypeDef hal_status = HAL_PCD_Start(pdev->pData); |
||||||
|
return HAL_TO_USB_STATUS(hal_status); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stops the Low Level portion of the Device driver. |
||||||
|
* @param pdev: Device handle |
||||||
|
* @retval USBD Status |
||||||
|
*/ |
||||||
|
USBD_StatusTypeDef USBD_LL_Stop (USBD_HandleTypeDef *pdev) |
||||||
|
{ |
||||||
|
HAL_StatusTypeDef hal_status = HAL_PCD_Stop(pdev->pData); |
||||||
|
return HAL_TO_USB_STATUS(hal_status); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Opens an endpoint of the Low Level Driver. |
||||||
|
* @param pdev: Device handle |
||||||
|
* @param ep_addr: Endpoint Number |
||||||
|
* @param ep_type: Endpoint Type |
||||||
|
* @param ep_mps: Endpoint Max Packet Size |
||||||
|
* @retval USBD Status |
||||||
|
*/ |
||||||
|
USBD_StatusTypeDef USBD_LL_OpenEP (USBD_HandleTypeDef *pdev,
|
||||||
|
uint8_t ep_addr,
|
||||||
|
uint8_t ep_type, |
||||||
|
uint16_t ep_mps) |
||||||
|
{ |
||||||
|
HAL_StatusTypeDef hal_status = HAL_PCD_EP_Open(pdev->pData, |
||||||
|
ep_addr,
|
||||||
|
ep_mps,
|
||||||
|
ep_type); |
||||||
|
|
||||||
|
return HAL_TO_USB_STATUS(hal_status); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Closes an endpoint of the Low Level Driver. |
||||||
|
* @param pdev: Device handle |
||||||
|
* @param ep_addr: Endpoint Number |
||||||
|
* @retval USBD Status |
||||||
|
*/ |
||||||
|
USBD_StatusTypeDef USBD_LL_CloseEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr)
|
||||||
|
{ |
||||||
|
//trap("CloseEP noimpl"); // XXX This uses ~ 500 bytes, maybe could be disabled
|
||||||
|
HAL_StatusTypeDef hal_status = HAL_PCD_EP_Close(pdev->pData, ep_addr); |
||||||
|
return HAL_TO_USB_STATUS(hal_status); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Flushes an endpoint of the Low Level Driver. |
||||||
|
* @param pdev: Device handle |
||||||
|
* @param ep_addr: Endpoint Number |
||||||
|
* @retval USBD Status |
||||||
|
*/ |
||||||
|
USBD_StatusTypeDef USBD_LL_FlushEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr)
|
||||||
|
{ |
||||||
|
HAL_StatusTypeDef hal_status = HAL_PCD_EP_Flush(pdev->pData, ep_addr); |
||||||
|
return HAL_TO_USB_STATUS(hal_status); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets a Stall condition on an endpoint of the Low Level Driver. |
||||||
|
* @param pdev: Device handle |
||||||
|
* @param ep_addr: Endpoint Number |
||||||
|
* @retval USBD Status |
||||||
|
*/ |
||||||
|
USBD_StatusTypeDef USBD_LL_StallEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr)
|
||||||
|
{ |
||||||
|
HAL_StatusTypeDef hal_status = HAL_PCD_EP_SetStall(pdev->pData, ep_addr); |
||||||
|
return HAL_TO_USB_STATUS(hal_status); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clears a Stall condition on an endpoint of the Low Level Driver. |
||||||
|
* @param pdev: Device handle |
||||||
|
* @param ep_addr: Endpoint Number |
||||||
|
* @retval USBD Status |
||||||
|
*/ |
||||||
|
USBD_StatusTypeDef USBD_LL_ClearStallEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr)
|
||||||
|
{ |
||||||
|
HAL_StatusTypeDef hal_status = HAL_PCD_EP_ClrStall(pdev->pData, ep_addr); |
||||||
|
return HAL_TO_USB_STATUS(hal_status); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns Stall condition. |
||||||
|
* @param pdev: Device handle |
||||||
|
* @param ep_addr: Endpoint Number |
||||||
|
* @retval Stall (1: Yes, 0: No) |
||||||
|
*/ |
||||||
|
uint8_t USBD_LL_IsStallEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr)
|
||||||
|
{ |
||||||
|
PCD_HandleTypeDef *hpcd = (PCD_HandleTypeDef*) pdev->pData; |
||||||
|
|
||||||
|
if((ep_addr & 0x80) == 0x80) |
||||||
|
{ |
||||||
|
return hpcd->IN_ep[ep_addr & 0x7F].is_stall;
|
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
return hpcd->OUT_ep[ep_addr & 0x7F].is_stall;
|
||||||
|
} |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief Assigns a USB address to the device. |
||||||
|
* @param pdev: Device handle |
||||||
|
* @param ep_addr: Endpoint Number |
||||||
|
* @retval USBD Status |
||||||
|
*/ |
||||||
|
USBD_StatusTypeDef USBD_LL_SetUSBAddress (USBD_HandleTypeDef *pdev, uint8_t dev_addr)
|
||||||
|
{ |
||||||
|
HAL_StatusTypeDef hal_status = HAL_PCD_SetAddress(pdev->pData, dev_addr); |
||||||
|
return HAL_TO_USB_STATUS(hal_status); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Transmits data over an endpoint. |
||||||
|
* @param pdev: Device handle |
||||||
|
* @param ep_addr: Endpoint Number |
||||||
|
* @param pbuf: Pointer to data to be sent |
||||||
|
* @param size: Data size
|
||||||
|
* @retval USBD Status |
||||||
|
*/ |
||||||
|
USBD_StatusTypeDef USBD_LL_Transmit (USBD_HandleTypeDef *pdev,
|
||||||
|
uint8_t ep_addr,
|
||||||
|
uint8_t *pbuf, |
||||||
|
uint16_t size) |
||||||
|
{ |
||||||
|
HAL_StatusTypeDef hal_status = HAL_PCD_EP_Transmit(pdev->pData, ep_addr, pbuf, size); |
||||||
|
return HAL_TO_USB_STATUS(hal_status); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prepares an endpoint for reception. |
||||||
|
* @param pdev: Device handle |
||||||
|
* @param ep_addr: Endpoint Number |
||||||
|
* @param pbuf: Pointer to data to be received |
||||||
|
* @param size: Data size |
||||||
|
* @retval USBD Status |
||||||
|
*/ |
||||||
|
USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev,
|
||||||
|
uint8_t ep_addr,
|
||||||
|
uint8_t *pbuf, |
||||||
|
uint16_t size) |
||||||
|
{ |
||||||
|
HAL_StatusTypeDef hal_status = HAL_PCD_EP_Receive(pdev->pData, ep_addr, pbuf, size); |
||||||
|
return HAL_TO_USB_STATUS(hal_status); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the last transfered packet size. |
||||||
|
* @param pdev: Device handle |
||||||
|
* @param ep_addr: Endpoint Number |
||||||
|
* @retval Recived Data Size |
||||||
|
*/ |
||||||
|
uint32_t USBD_LL_GetRxDataSize (USBD_HandleTypeDef *pdev, uint8_t ep_addr)
|
||||||
|
{ |
||||||
|
return HAL_PCD_EP_GetRxCount((PCD_HandleTypeDef*) pdev->pData, ep_addr); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delays routine for the USB Device Library. |
||||||
|
* @param Delay: Delay in ms |
||||||
|
* @retval None |
||||||
|
*/ |
||||||
|
void USBD_LL_Delay (uint32_t Delay) |
||||||
|
{ |
||||||
|
HAL_Delay(Delay);
|
||||||
|
} |
||||||
|
|
||||||
|
static uint32_t _static_malloc_pos = 0; |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief static single allocation. |
||||||
|
* @param size: size of allocated memory |
||||||
|
* @retval None |
||||||
|
*/ |
||||||
|
void *USBD_static_malloc(uint32_t size) |
||||||
|
{ |
||||||
|
// XXX this was modified to support multiple classes
|
||||||
|
static uint32_t mem[(sizeof(USBD_MSC_BOT_HandleTypeDef)/4)+1+(sizeof(USBD_CDC_HandleTypeDef)/4)+1];/* On 32-bit boundary */ |
||||||
|
uint32_t oldpos = _static_malloc_pos; |
||||||
|
_static_malloc_pos += size/4+1; |
||||||
|
return &mem[oldpos]; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dummy memory free |
||||||
|
* @param *p pointer to allocated memory address |
||||||
|
* @retval None |
||||||
|
*/ |
||||||
|
void USBD_static_free(void *p) |
||||||
|
{ |
||||||
|
// This is wrong, but will work if both frees and malloc's
|
||||||
|
// are always called together and not interleaved
|
||||||
|
_static_malloc_pos = 0; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Software Device Connection |
||||||
|
* @param hpcd: PCD handle |
||||||
|
* @param state: connection state (0 : disconnected / 1: connected)
|
||||||
|
* @retval None |
||||||
|
*/ |
||||||
|
void HAL_PCDEx_SetConnectionState(PCD_HandleTypeDef *hpcd, uint8_t state) |
||||||
|
{ |
||||||
|
///* USER CODE BEGIN 5 */
|
||||||
|
// if (state == 1)
|
||||||
|
// {
|
||||||
|
// /* Configure Low Connection State */
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// /* Configure High Connection State */
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
///* USER CODE END 5 */
|
||||||
|
} |
||||||
|
|
||||||
|
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
@ -0,0 +1,201 @@ |
|||||||
|
/**
|
||||||
|
****************************************************************************** |
||||||
|
* @file : usbd_conf.h |
||||||
|
* @version : v2.0_Cube |
||||||
|
* @brief : Header for usbd_conf file. |
||||||
|
****************************************************************************** |
||||||
|
* This notice applies to any and all portions of this file |
||||||
|
* that are not between comment pairs USER CODE BEGIN and |
||||||
|
* USER CODE END. Other portions of this file, whether
|
||||||
|
* inserted by the user or by software development tools |
||||||
|
* are owned by their respective copyright owners. |
||||||
|
* |
||||||
|
* Copyright (c) 2017 STMicroelectronics International N.V.
|
||||||
|
* 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. Redistribution 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. |
||||||
|
* 3. Neither the name of STMicroelectronics nor the names of other
|
||||||
|
* contributors to this software may be used to endorse or promote products
|
||||||
|
* derived from this software without specific written permission. |
||||||
|
* 4. This software, including modifications and/or derivative works of this
|
||||||
|
* software, must execute solely and exclusively on microcontroller or |
||||||
|
* microprocessor devices manufactured by or for STMicroelectronics. |
||||||
|
* 5. Redistribution and use of this software other than as permitted under
|
||||||
|
* this license is void and will automatically terminate your rights under
|
||||||
|
* this license.
|
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY |
||||||
|
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
|
||||||
|
* SHALL STMICROELECTRONICS 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. |
||||||
|
* |
||||||
|
****************************************************************************** |
||||||
|
*/ |
||||||
|
/* Define to prevent recursive inclusion -------------------------------------*/ |
||||||
|
#ifndef __USBD_CONF__H__ |
||||||
|
#define __USBD_CONF__H__ |
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
/* Includes ------------------------------------------------------------------*/ |
||||||
|
//#include <stdio.h>
|
||||||
|
//#include <stdlib.h>
|
||||||
|
#include "platform.h" |
||||||
|
#include "usbd_def.h" |
||||||
|
|
||||||
|
/** @addtogroup USBD_OTG_DRIVER
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @defgroup USBD_CONF
|
||||||
|
* @brief usb otg low level driver configuration file |
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_CONF_Exported_Defines
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
|
||||||
|
/*---------- -----------*/ |
||||||
|
#define USBD_MAX_NUM_INTERFACES 2 |
||||||
|
/*---------- -----------*/ |
||||||
|
#define USBD_MAX_NUM_CONFIGURATION 1 |
||||||
|
/*---------- -----------*/ |
||||||
|
#define USBD_MAX_STR_DESC_SIZ 128 |
||||||
|
/*---------- -----------*/ |
||||||
|
#define USBD_SUPPORT_USER_STRING 1 |
||||||
|
/*---------- -----------*/ |
||||||
|
#define USBD_DEBUG_LEVEL 3 |
||||||
|
/*---------- -----------*/ |
||||||
|
#define USBD_SELF_POWERED 1 |
||||||
|
/*---------- -----------*/ |
||||||
|
#define MSC_MEDIA_PACKET 512 |
||||||
|
/****************************************/ |
||||||
|
/* #define for FS and HS identification */ |
||||||
|
#define DEVICE_FS 0 |
||||||
|
|
||||||
|
// Custom config
|
||||||
|
#define MSC_COMPOSITE |
||||||
|
#define CDC_COMPOSITE |
||||||
|
|
||||||
|
#define MSC_CUSTOM_EPS |
||||||
|
#define MSC_EPIN_ADDR 0x81 |
||||||
|
#define MSC_EPOUT_ADDR 0x01 |
||||||
|
|
||||||
|
#define CDC_CUSTOM_EPS |
||||||
|
#define CDC_IN_EP 0x82 /* EP1 for data IN */ |
||||||
|
#define CDC_OUT_EP 0x02 /* EP1 for data OUT */ |
||||||
|
#define CDC_CMD_EP 0x83 /* EP2 for CDC commands */ |
||||||
|
|
||||||
|
/** @defgroup USBD_Exported_Macros
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
|
||||||
|
/* Memory management macros */
|
||||||
|
#define USBD_malloc (uint32_t *)USBD_static_malloc |
||||||
|
#define USBD_free USBD_static_free |
||||||
|
#define USBD_memset /* Not used */ |
||||||
|
#define USBD_memcpy /* Not used */ |
||||||
|
|
||||||
|
#define USBD_Delay HAL_Delay |
||||||
|
|
||||||
|
/* For footprint reasons and since only one allocation is handled in the HID class
|
||||||
|
driver, the malloc/free is changed into a static allocation method */ |
||||||
|
void *USBD_static_malloc(uint32_t size); |
||||||
|
void USBD_static_free(void *p); |
||||||
|
|
||||||
|
|
||||||
|
#if (USBD_DEBUG_LEVEL > 0) |
||||||
|
#define USBD_UsrLog(...) PRINTF("USB USER : ") ;\ |
||||||
|
PRINTF(__VA_ARGS__);\
|
||||||
|
PRINTF("\r\n"); |
||||||
|
#else |
||||||
|
#define USBD_UsrLog(...) |
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
#if (USBD_DEBUG_LEVEL > 1) |
||||||
|
|
||||||
|
#define USBD_ErrLog(...) PRINTF("USB ERROR: ") ;\ |
||||||
|
PRINTF(__VA_ARGS__);\
|
||||||
|
PRINTF("\r\n"); |
||||||
|
#else |
||||||
|
#define USBD_ErrLog(...) |
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
#if (USBD_DEBUG_LEVEL > 2) |
||||||
|
#define USBD_DbgLog(...) PRINTF("USB DEBUG: ") ;\ |
||||||
|
PRINTF(__VA_ARGS__);\
|
||||||
|
PRINTF("\r\n"); |
||||||
|
#else |
||||||
|
#define USBD_DbgLog(...) |
||||||
|
#endif |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_CONF_Exported_Types
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_CONF_Exported_Macros
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_CONF_Exported_Variables
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_CONF_Exported_FunctionsPrototype
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /*__USBD_CONF__H__*/ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
||||||
|
|
@ -0,0 +1,297 @@ |
|||||||
|
/**
|
||||||
|
****************************************************************************** |
||||||
|
* @file : usbd_desc.c |
||||||
|
* @version : v2.0_Cube |
||||||
|
* @brief : This file implements the USB Device descriptors |
||||||
|
****************************************************************************** |
||||||
|
* This notice applies to any and all portions of this file |
||||||
|
* that are not between comment pairs USER CODE BEGIN and |
||||||
|
* USER CODE END. Other portions of this file, whether
|
||||||
|
* inserted by the user or by software development tools |
||||||
|
* are owned by their respective copyright owners. |
||||||
|
* |
||||||
|
* Copyright (c) 2017 STMicroelectronics International N.V.
|
||||||
|
* 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. Redistribution 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. |
||||||
|
* 3. Neither the name of STMicroelectronics nor the names of other
|
||||||
|
* contributors to this software may be used to endorse or promote products
|
||||||
|
* derived from this software without specific written permission. |
||||||
|
* 4. This software, including modifications and/or derivative works of this
|
||||||
|
* software, must execute solely and exclusively on microcontroller or |
||||||
|
* microprocessor devices manufactured by or for STMicroelectronics. |
||||||
|
* 5. Redistribution and use of this software other than as permitted under
|
||||||
|
* this license is void and will automatically terminate your rights under
|
||||||
|
* this license.
|
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY |
||||||
|
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
|
||||||
|
* SHALL STMICROELECTRONICS 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. |
||||||
|
* |
||||||
|
****************************************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Includes ------------------------------------------------------------------*/ |
||||||
|
#include "platform.h" |
||||||
|
#include "usbd_core.h" |
||||||
|
#include "usbd_desc.h" |
||||||
|
#include "usbd_conf.h" |
||||||
|
|
||||||
|
/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @defgroup USBD_DESC
|
||||||
|
* @brief USBD descriptors module |
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_DESC_Private_TypesDefinitions
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_DESC_Private_Defines
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
#define USBD_VID 1155 |
||||||
|
#define USBD_PID_FS 22314 |
||||||
|
#define USBD_LANGID_NUM 1033 |
||||||
|
#define USBD_MANUFACTURER_STRING "MightyPork" |
||||||
|
#define USBD_PRODUCT_STRING_FS "GEX" |
||||||
|
#define USBD_VERSION_NUM 0x0001 // 0xMMmp
|
||||||
|
//#define USBD_SERIALNUMBER_STRING_FS "00000000001A"
|
||||||
|
//#define USBD_CONFIGURATION_STRING_FS "MSC Config"
|
||||||
|
//#define USBD_INTERFACE_STRING_FS "MSC Interface"
|
||||||
|
|
||||||
|
/* USER CODE BEGIN 0 */ |
||||||
|
|
||||||
|
/* USER CODE END 0*/ |
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_DESC_Private_Macros
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_DESC_Private_Variables
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
uint8_t * USBD_FS_DeviceDescriptor( USBD_SpeedTypeDef speed , uint16_t *length); |
||||||
|
uint8_t * USBD_FS_LangIDStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length); |
||||||
|
uint8_t * USBD_FS_ManufacturerStrDescriptor ( USBD_SpeedTypeDef speed , uint16_t *length); |
||||||
|
uint8_t * USBD_FS_ProductStrDescriptor ( USBD_SpeedTypeDef speed , uint16_t *length); |
||||||
|
uint8_t * USBD_FS_SerialStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length); |
||||||
|
//uint8_t * USBD_FS_ConfigStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length);
|
||||||
|
//uint8_t * USBD_FS_InterfaceStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length);
|
||||||
|
|
||||||
|
#ifdef USB_SUPPORT_USER_STRING_DESC |
||||||
|
uint8_t * USBD_FS_USRStringDesc (USBD_SpeedTypeDef speed, uint8_t idx , uint16_t *length);
|
||||||
|
#endif /* USB_SUPPORT_USER_STRING_DESC */ |
||||||
|
|
||||||
|
USBD_DescriptorsTypeDef FS_Desc = |
||||||
|
{ |
||||||
|
USBD_FS_DeviceDescriptor, |
||||||
|
USBD_FS_LangIDStrDescriptor,
|
||||||
|
USBD_FS_ManufacturerStrDescriptor, |
||||||
|
USBD_FS_ProductStrDescriptor, |
||||||
|
USBD_FS_SerialStrDescriptor, |
||||||
|
NULL, NULL, |
||||||
|
// USBD_FS_ConfigStrDescriptor,
|
||||||
|
// USBD_FS_InterfaceStrDescriptor,
|
||||||
|
}; |
||||||
|
|
||||||
|
#if defined ( __ICCARM__ ) /*!< IAR Compiler */ |
||||||
|
#pragma data_alignment=4 |
||||||
|
#endif |
||||||
|
/* USB Standard Device Descriptor */ |
||||||
|
__ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END = |
||||||
|
{ |
||||||
|
USB_LEN_DEV_DESC, /*bLength */ |
||||||
|
USB_DESC_TYPE_DEVICE, /*bDescriptorType*/ |
||||||
|
0x00, /* bcdUSB 2.0 */ |
||||||
|
0x02, |
||||||
|
|
||||||
|
// magic triplet to allow use of the association descriptor
|
||||||
|
0xEF, /*bDeviceClass*/ |
||||||
|
0x02, /*bDeviceSubClass*/ |
||||||
|
0x01, /*bDeviceProtocol*/ |
||||||
|
|
||||||
|
USB_MAX_EP0_SIZE, /*bMaxPacketSize*/ |
||||||
|
LOBYTE(USBD_VID), /*idVendor*/ |
||||||
|
HIBYTE(USBD_VID), /*idVendor*/ |
||||||
|
LOBYTE(USBD_PID_FS), /*idVendor*/ |
||||||
|
HIBYTE(USBD_PID_FS), /*idVendor*/ |
||||||
|
LOBYTE(USBD_VERSION_NUM), /*bcdDevice rel. 2.00*/ |
||||||
|
HIBYTE(USBD_VERSION_NUM), |
||||||
|
USBD_IDX_MFC_STR, /*Index of manufacturer string*/ |
||||||
|
USBD_IDX_PRODUCT_STR, /*Index of product string*/ |
||||||
|
USBD_IDX_SERIAL_STR, /*Index of serial number string*/ |
||||||
|
USBD_MAX_NUM_CONFIGURATION /*bNumConfigurations*/ |
||||||
|
} ;
|
||||||
|
/* USB_DeviceDescriptor */ |
||||||
|
|
||||||
|
#if defined ( __ICCARM__ ) /*!< IAR Compiler */ |
||||||
|
#pragma data_alignment=4 |
||||||
|
#endif |
||||||
|
|
||||||
|
/* USB Standard Device Descriptor */ |
||||||
|
__ALIGN_BEGIN uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC] __ALIGN_END = |
||||||
|
{ |
||||||
|
USB_LEN_LANGID_STR_DESC,
|
||||||
|
USB_DESC_TYPE_STRING,
|
||||||
|
LOBYTE(USBD_LANGID_NUM), |
||||||
|
HIBYTE(USBD_LANGID_NUM), |
||||||
|
}; |
||||||
|
|
||||||
|
#if defined ( __ICCARM__ ) /*!< IAR Compiler */ |
||||||
|
#pragma data_alignment=4 |
||||||
|
#endif |
||||||
|
__ALIGN_BEGIN uint8_t USBD_StrDesc[USBD_MAX_STR_DESC_SIZ] __ALIGN_END; |
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_DESC_Private_FunctionPrototypes
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_DESC_Private_Functions
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief USBD_FS_DeviceDescriptor
|
||||||
|
* return the device descriptor |
||||||
|
* @param speed : current device speed |
||||||
|
* @param length : pointer to data length variable |
||||||
|
* @retval pointer to descriptor buffer |
||||||
|
*/ |
||||||
|
uint8_t * USBD_FS_DeviceDescriptor( USBD_SpeedTypeDef speed , uint16_t *length) |
||||||
|
{ |
||||||
|
*length = sizeof(USBD_FS_DeviceDesc); |
||||||
|
return USBD_FS_DeviceDesc; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief USBD_FS_LangIDStrDescriptor
|
||||||
|
* return the LangID string descriptor |
||||||
|
* @param speed : current device speed |
||||||
|
* @param length : pointer to data length variable |
||||||
|
* @retval pointer to descriptor buffer |
||||||
|
*/ |
||||||
|
uint8_t * USBD_FS_LangIDStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length) |
||||||
|
{ |
||||||
|
*length = sizeof(USBD_LangIDDesc);
|
||||||
|
return USBD_LangIDDesc; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief USBD_FS_ProductStrDescriptor
|
||||||
|
* return the product string descriptor |
||||||
|
* @param speed : current device speed |
||||||
|
* @param length : pointer to data length variable |
||||||
|
* @retval pointer to descriptor buffer |
||||||
|
*/ |
||||||
|
uint8_t * USBD_FS_ProductStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length) |
||||||
|
{ |
||||||
|
USBD_GetString (USBD_PRODUCT_STRING_FS, USBD_StrDesc, length); |
||||||
|
return USBD_StrDesc; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief USBD_FS_ManufacturerStrDescriptor
|
||||||
|
* return the manufacturer string descriptor |
||||||
|
* @param speed : current device speed |
||||||
|
* @param length : pointer to data length variable |
||||||
|
* @retval pointer to descriptor buffer |
||||||
|
*/ |
||||||
|
uint8_t * USBD_FS_ManufacturerStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length) |
||||||
|
{ |
||||||
|
USBD_GetString (USBD_MANUFACTURER_STRING, USBD_StrDesc, length); |
||||||
|
return USBD_StrDesc; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief USBD_FS_SerialStrDescriptor
|
||||||
|
* return the serial number string descriptor |
||||||
|
* @param speed : current device speed |
||||||
|
* @param length : pointer to data length variable |
||||||
|
* @retval pointer to descriptor buffer |
||||||
|
*/ |
||||||
|
uint8_t * USBD_FS_SerialStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length) |
||||||
|
{ |
||||||
|
char buff[25]; |
||||||
|
fixup_sprintf(buff, "%08"PRIX32"%08"PRIX32"%08"PRIX32, |
||||||
|
LL_GetUID_Word0(), |
||||||
|
LL_GetUID_Word1(), |
||||||
|
LL_GetUID_Word2() |
||||||
|
); |
||||||
|
|
||||||
|
USBD_GetString ((uint8_t *) &buff[0], USBD_StrDesc, length); |
||||||
|
return USBD_StrDesc; |
||||||
|
} |
||||||
|
//
|
||||||
|
///**
|
||||||
|
//* @brief USBD_FS_ConfigStrDescriptor
|
||||||
|
//* return the configuration string descriptor
|
||||||
|
//* @param speed : current device speed
|
||||||
|
//* @param length : pointer to data length variable
|
||||||
|
//* @retval pointer to descriptor buffer
|
||||||
|
//*/
|
||||||
|
//uint8_t * USBD_FS_ConfigStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length)
|
||||||
|
//{
|
||||||
|
// USBD_GetString (USBD_CONFIGURATION_STRING_FS, USBD_StrDesc, length);
|
||||||
|
// return USBD_StrDesc;
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
//* @brief USBD_HS_InterfaceStrDescriptor
|
||||||
|
//* return the interface string descriptor
|
||||||
|
//* @param speed : current device speed
|
||||||
|
//* @param length : pointer to data length variable
|
||||||
|
//* @retval pointer to descriptor buffer
|
||||||
|
//*/
|
||||||
|
//uint8_t * USBD_FS_InterfaceStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length)
|
||||||
|
//{
|
||||||
|
// USBD_GetString (USBD_INTERFACE_STRING_FS, USBD_StrDesc, length);
|
||||||
|
// return USBD_StrDesc;
|
||||||
|
//}
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
@ -0,0 +1,123 @@ |
|||||||
|
/**
|
||||||
|
****************************************************************************** |
||||||
|
* @file : usbd_desc.h |
||||||
|
* @version : v2.0_Cube |
||||||
|
* @brief : Header for usbd_desc file. |
||||||
|
****************************************************************************** |
||||||
|
* This notice applies to any and all portions of this file |
||||||
|
* that are not between comment pairs USER CODE BEGIN and |
||||||
|
* USER CODE END. Other portions of this file, whether
|
||||||
|
* inserted by the user or by software development tools |
||||||
|
* are owned by their respective copyright owners. |
||||||
|
* |
||||||
|
* Copyright (c) 2017 STMicroelectronics International N.V.
|
||||||
|
* 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. Redistribution 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. |
||||||
|
* 3. Neither the name of STMicroelectronics nor the names of other
|
||||||
|
* contributors to this software may be used to endorse or promote products
|
||||||
|
* derived from this software without specific written permission. |
||||||
|
* 4. This software, including modifications and/or derivative works of this
|
||||||
|
* software, must execute solely and exclusively on microcontroller or |
||||||
|
* microprocessor devices manufactured by or for STMicroelectronics. |
||||||
|
* 5. Redistribution and use of this software other than as permitted under
|
||||||
|
* this license is void and will automatically terminate your rights under
|
||||||
|
* this license.
|
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY |
||||||
|
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
|
||||||
|
* SHALL STMICROELECTRONICS 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. |
||||||
|
* |
||||||
|
****************************************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Define to prevent recursive inclusion -------------------------------------*/ |
||||||
|
#ifndef __USBD_DESC__H__ |
||||||
|
#define __USBD_DESC__H__ |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
/* Includes ------------------------------------------------------------------*/ |
||||||
|
#include "platform.h" |
||||||
|
#include "usbd_def.h" |
||||||
|
|
||||||
|
/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @defgroup USB_DESC
|
||||||
|
* @brief general defines for the usb device library file |
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USB_DESC_Exported_Defines
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_DESC_Exported_TypesDefinitions
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_DESC_Exported_Macros
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_DESC_Exported_Variables
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
extern USBD_DescriptorsTypeDef FS_Desc; |
||||||
|
|
||||||
|
// Buffer for string descriptors
|
||||||
|
extern __ALIGN_BEGIN uint8_t USBD_StrDesc[USBD_MAX_STR_DESC_SIZ] __ALIGN_END; |
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_DESC_Exported_FunctionsPrototype
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* __USBD_DESC_H */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
@ -0,0 +1,319 @@ |
|||||||
|
/**
|
||||||
|
****************************************************************************** |
||||||
|
* @file : usbd_storage_if.c |
||||||
|
* @brief : Memory management layer |
||||||
|
****************************************************************************** |
||||||
|
* This notice applies to any and all portions of this file |
||||||
|
* that are not between comment pairs USER CODE BEGIN and |
||||||
|
* USER CODE END. Other portions of this file, whether
|
||||||
|
* inserted by the user or by software development tools |
||||||
|
* are owned by their respective copyright owners. |
||||||
|
* |
||||||
|
* Copyright (c) 2017 STMicroelectronics International N.V.
|
||||||
|
* 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. Redistribution 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. |
||||||
|
* 3. Neither the name of STMicroelectronics nor the names of other
|
||||||
|
* contributors to this software may be used to endorse or promote products
|
||||||
|
* derived from this software without specific written permission. |
||||||
|
* 4. This software, including modifications and/or derivative works of this
|
||||||
|
* software, must execute solely and exclusively on microcontroller or |
||||||
|
* microprocessor devices manufactured by or for STMicroelectronics. |
||||||
|
* 5. Redistribution and use of this software other than as permitted under
|
||||||
|
* this license is void and will automatically terminate your rights under
|
||||||
|
* this license.
|
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY |
||||||
|
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
|
||||||
|
* SHALL STMICROELECTRONICS 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. |
||||||
|
* |
||||||
|
****************************************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Includes ------------------------------------------------------------------*/ |
||||||
|
#include <platform/status_led.h> |
||||||
|
#include "platform.h" |
||||||
|
#include "usbd_storage_if.h" |
||||||
|
/* USER CODE BEGIN INCLUDE */ |
||||||
|
#include "vfs/vfs_manager.h" |
||||||
|
/* USER CODE END INCLUDE */ |
||||||
|
|
||||||
|
/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @defgroup USBD_STORAGE
|
||||||
|
* @brief usbd core module |
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_STORAGE_Private_TypesDefinitions
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
/* USER CODE BEGIN PRIVATE_TYPES */ |
||||||
|
/* USER CODE END PRIVATE_TYPES */
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_STORAGE_Private_Defines
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
#define STORAGE_LUN_NBR 1 |
||||||
|
#define STORAGE_BLK_NBR 0x10000 |
||||||
|
#define STORAGE_BLK_SIZ 0x200 |
||||||
|
|
||||||
|
/* USER CODE BEGIN PRIVATE_DEFINES */ |
||||||
|
/* USER CODE END PRIVATE_DEFINES */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_STORAGE_Private_Macros
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
/* USER CODE BEGIN PRIVATE_MACRO */ |
||||||
|
/* USER CODE END PRIVATE_MACRO */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_STORAGE_IF_Private_Variables
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
/* USER CODE BEGIN INQUIRY_DATA_FS */
|
||||||
|
/* USB Mass storage Standard Inquiry Data */ |
||||||
|
int8_t STORAGE_Inquirydata_FS[] = {/* 36 */ |
||||||
|
/* LUN 0 */ |
||||||
|
0x00,
|
||||||
|
0x80,
|
||||||
|
0x02,
|
||||||
|
0x02, |
||||||
|
(STANDARD_INQUIRY_DATA_LEN - 5), |
||||||
|
0x00, |
||||||
|
0x00,
|
||||||
|
0x00, |
||||||
|
// Those strings are visible in dmesg, probably nowhere else.
|
||||||
|
'M', 'E', 'G', 'A', 'P', 'I', 'G', ' ', /* Manufacturer : 8 bytes */ |
||||||
|
'G', 'E', 'X', ' ', 'G', 'P', 'I', 'O', /* Product : 16 Bytes */ |
||||||
|
'-', 'E', 'x', 'p', 'a', 'n', 'd', 'r', |
||||||
|
'0', '.', '0' ,'1', /* Version : 4 Bytes */ |
||||||
|
};
|
||||||
|
/* USER CODE END INQUIRY_DATA_FS */
|
||||||
|
|
||||||
|
/* USER CODE BEGIN PRIVATE_VARIABLES */ |
||||||
|
/* USER CODE END PRIVATE_VARIABLES */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_STORAGE_IF_Exported_Variables
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
extern USBD_HandleTypeDef hUsbDeviceFS; |
||||||
|
/* USER CODE BEGIN EXPORTED_VARIABLES */ |
||||||
|
/* USER CODE END EXPORTED_VARIABLES */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_STORAGE_Private_FunctionPrototypes
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
static int8_t STORAGE_Init_FS (uint8_t lun); |
||||||
|
static int8_t STORAGE_GetCapacity_FS (uint8_t lun,
|
||||||
|
uint32_t *block_num,
|
||||||
|
uint16_t *block_size); |
||||||
|
static int8_t STORAGE_IsReady_FS (uint8_t lun); |
||||||
|
static int8_t STORAGE_IsWriteProtected_FS (uint8_t lun); |
||||||
|
static int8_t STORAGE_Read_FS (uint8_t lun,
|
||||||
|
uint8_t *buf,
|
||||||
|
uint32_t blk_addr, |
||||||
|
uint16_t blk_len); |
||||||
|
static int8_t STORAGE_Write_FS (uint8_t lun,
|
||||||
|
uint8_t *buf,
|
||||||
|
uint32_t blk_addr, |
||||||
|
uint16_t blk_len); |
||||||
|
static int8_t STORAGE_GetMaxLun_FS (void); |
||||||
|
|
||||||
|
/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */ |
||||||
|
/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
USBD_StorageTypeDef USBD_Storage_Interface_fops_FS = |
||||||
|
{ |
||||||
|
STORAGE_Init_FS, |
||||||
|
STORAGE_GetCapacity_FS, |
||||||
|
STORAGE_IsReady_FS, |
||||||
|
STORAGE_IsWriteProtected_FS, |
||||||
|
STORAGE_Read_FS, |
||||||
|
STORAGE_Write_FS, |
||||||
|
STORAGE_GetMaxLun_FS, |
||||||
|
(int8_t *)STORAGE_Inquirydata_FS, |
||||||
|
}; |
||||||
|
|
||||||
|
/* Private functions ---------------------------------------------------------*/ |
||||||
|
/*******************************************************************************
|
||||||
|
* Function Name : STORAGE_Init_FS |
||||||
|
* Description :
|
||||||
|
* Input : None. |
||||||
|
* Output : None. |
||||||
|
* Return : None. |
||||||
|
*******************************************************************************/ |
||||||
|
int8_t STORAGE_Init_FS (uint8_t lun) |
||||||
|
{ |
||||||
|
/* USER CODE BEGIN 2 */ |
||||||
|
dbg("Plug In"); |
||||||
|
vfs_mngr_fs_enable(1); |
||||||
|
return (USBD_OK); |
||||||
|
/* USER CODE END 2 */
|
||||||
|
} |
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Function Name : STORAGE_GetCapacity_FS |
||||||
|
* Description :
|
||||||
|
* Input : None. |
||||||
|
* Output : None. |
||||||
|
* Return : None. |
||||||
|
*******************************************************************************/ |
||||||
|
int8_t STORAGE_GetCapacity_FS (uint8_t lun, uint32_t *block_num, uint16_t *block_size) |
||||||
|
{ |
||||||
|
/* USER CODE BEGIN 3 */
|
||||||
|
// *block_num = STORAGE_BLK_NBR;
|
||||||
|
// *block_size = STORAGE_BLK_SIZ;
|
||||||
|
// we have only one LUN
|
||||||
|
*block_num = vfs_info.BlockCount; |
||||||
|
*block_size = vfs_info.BlockSize; |
||||||
|
vfs_printf("Get Capacity: %"PRIu32" sectors x %"PRIu16" bytes", *block_num, *block_size); |
||||||
|
return (USBD_OK); |
||||||
|
/* USER CODE END 3 */
|
||||||
|
} |
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Function Name : STORAGE_IsReady_FS |
||||||
|
* Description :
|
||||||
|
* Input : None. |
||||||
|
* Output : None. |
||||||
|
* Return : None. |
||||||
|
*******************************************************************************/ |
||||||
|
int8_t STORAGE_IsReady_FS (uint8_t lun) |
||||||
|
{ |
||||||
|
/* USER CODE BEGIN 4 */ |
||||||
|
// dbg("STORAGE_IsReady_FS? %d", vfs_info.MediaReady);
|
||||||
|
StatusLed_Set(STATUS_DISK_ATTACHED, vfs_info.MediaReady); |
||||||
|
|
||||||
|
// Media change - no re-plug
|
||||||
|
if (vfs_info.MediaChanged) { |
||||||
|
vfs_info.MediaChanged = false; // unset the flag
|
||||||
|
return -1; // Notify about media change
|
||||||
|
} |
||||||
|
|
||||||
|
// Plug out/in
|
||||||
|
return vfs_info.MediaReady ? USBD_OK : USBD_BUSY; |
||||||
|
// return (USBD_OK);
|
||||||
|
/* USER CODE END 4 */
|
||||||
|
} |
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Function Name : STORAGE_IsWriteProtected_FS |
||||||
|
* Description :
|
||||||
|
* Input : None. |
||||||
|
* Output : None. |
||||||
|
* Return : None. |
||||||
|
*******************************************************************************/ |
||||||
|
int8_t STORAGE_IsWriteProtected_FS (uint8_t lun) |
||||||
|
{ |
||||||
|
/* USER CODE BEGIN 5 */ |
||||||
|
// dbg("STORAGE_IsWriteProtected_FS? no");
|
||||||
|
return false; |
||||||
|
// return (USBD_OK);
|
||||||
|
/* USER CODE END 5 */
|
||||||
|
} |
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Function Name : STORAGE_Read_FS |
||||||
|
* Description :
|
||||||
|
* Input : None. |
||||||
|
* Output : None. |
||||||
|
* Return : None. |
||||||
|
*******************************************************************************/ |
||||||
|
int8_t STORAGE_Read_FS (uint8_t lun,
|
||||||
|
uint8_t *buf,
|
||||||
|
uint32_t blk_addr,
|
||||||
|
uint16_t blk_len) |
||||||
|
{ |
||||||
|
/* USER CODE BEGIN 6 */ |
||||||
|
// dbg("rd lun %d adr %d len %d", lun, blk_addr, blk_len);
|
||||||
|
vfs_if_usbd_msc_read_sect(blk_addr, buf, blk_len); // ????
|
||||||
|
return (USBD_OK); |
||||||
|
/* USER CODE END 6 */
|
||||||
|
} |
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Function Name : STORAGE_Write_FS |
||||||
|
* Description : |
||||||
|
* Input : None. |
||||||
|
* Output : None. |
||||||
|
* Return : None. |
||||||
|
*******************************************************************************/ |
||||||
|
int8_t STORAGE_Write_FS (uint8_t lun,
|
||||||
|
uint8_t *buf,
|
||||||
|
uint32_t blk_addr, |
||||||
|
uint16_t blk_len) |
||||||
|
{ |
||||||
|
/* USER CODE BEGIN 7 */ |
||||||
|
// dbg("STORAGE_Write_FS");
|
||||||
|
vfs_if_usbd_msc_write_sect(blk_addr, buf, blk_len); // ???
|
||||||
|
return (USBD_OK); |
||||||
|
/* USER CODE END 7 */
|
||||||
|
} |
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Function Name : STORAGE_GetMaxLun_FS |
||||||
|
* Description :
|
||||||
|
* Input : None. |
||||||
|
* Output : None. |
||||||
|
* Return : None. |
||||||
|
*******************************************************************************/ |
||||||
|
int8_t STORAGE_GetMaxLun_FS (void) |
||||||
|
{ |
||||||
|
/* USER CODE BEGIN 8 */
|
||||||
|
return 0; // we have 1 LUN
|
||||||
|
// return (STORAGE_LUN_NBR - 1);
|
||||||
|
/* USER CODE END 8 */
|
||||||
|
} |
||||||
|
|
||||||
|
/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */ |
||||||
|
/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
@ -0,0 +1,138 @@ |
|||||||
|
/**
|
||||||
|
****************************************************************************** |
||||||
|
* @file : usbd_storage_if.h |
||||||
|
* @brief : header file for the usbd_storage_if.c file |
||||||
|
****************************************************************************** |
||||||
|
* This notice applies to any and all portions of this file |
||||||
|
* that are not between comment pairs USER CODE BEGIN and |
||||||
|
* USER CODE END. Other portions of this file, whether
|
||||||
|
* inserted by the user or by software development tools |
||||||
|
* are owned by their respective copyright owners. |
||||||
|
* |
||||||
|
* Copyright (c) 2017 STMicroelectronics International N.V.
|
||||||
|
* 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. Redistribution 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. |
||||||
|
* 3. Neither the name of STMicroelectronics nor the names of other
|
||||||
|
* contributors to this software may be used to endorse or promote products
|
||||||
|
* derived from this software without specific written permission. |
||||||
|
* 4. This software, including modifications and/or derivative works of this
|
||||||
|
* software, must execute solely and exclusively on microcontroller or |
||||||
|
* microprocessor devices manufactured by or for STMicroelectronics. |
||||||
|
* 5. Redistribution and use of this software other than as permitted under
|
||||||
|
* this license is void and will automatically terminate your rights under
|
||||||
|
* this license.
|
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY |
||||||
|
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
|
||||||
|
* SHALL STMICROELECTRONICS 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. |
||||||
|
* |
||||||
|
****************************************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Define to prevent recursive inclusion -------------------------------------*/ |
||||||
|
|
||||||
|
#ifndef __USBD_STORAGE_IF_H_ |
||||||
|
#define __USBD_STORAGE_IF_H_ |
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Includes ------------------------------------------------------------------*/ |
||||||
|
#include "platform.h" |
||||||
|
#include "usbd_msc.h" |
||||||
|
/* USER CODE BEGIN INCLUDE */ |
||||||
|
/* USER CODE END INCLUDE */ |
||||||
|
|
||||||
|
/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
|
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @defgroup USBD_STORAGE
|
||||||
|
* @brief header file for the USBD_STORAGE.c file |
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_STORAGE_Exported_Defines
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
/* USER CODE BEGIN EXPORTED_DEFINES */ |
||||||
|
/* USER CODE END EXPORTED_DEFINES */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_STORAGE_Exported_Types
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
/* USER CODE BEGIN EXPORTED_TYPES */ |
||||||
|
/* USER CODE END EXPORTED_TYPES */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_STORAGE_Exported_Macros
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
/* USER CODE BEGIN EXPORTED_MACRO */ |
||||||
|
/* USER CODE END EXPORTED_MACRO */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_STORAGE_Exported_Variables
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
extern USBD_StorageTypeDef USBD_Storage_Interface_fops_FS; |
||||||
|
|
||||||
|
/* USER CODE BEGIN EXPORTED_VARIABLES */ |
||||||
|
/* USER CODE END EXPORTED_VARIABLES */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup USBD_STORAGE_Exported_FunctionsPrototype
|
||||||
|
* @{ |
||||||
|
*/
|
||||||
|
|
||||||
|
/* USER CODE BEGIN EXPORTED_FUNCTIONS */ |
||||||
|
/* USER CODE END EXPORTED_FUNCTIONS */ |
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* __USBD_STORAGE_IF_H */ |
||||||
|
|
||||||
|
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
@ -0,0 +1,218 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "TinyFrame.h" |
||||||
|
#include "framework/unit_registry.h" |
||||||
|
#include "comm/messages.h" |
||||||
|
#include "task_sched.h" |
||||||
|
|
||||||
|
TinyFrame tf_; |
||||||
|
TinyFrame *comm = &tf_; |
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Pre-declaring local functions
|
||||||
|
static TF_Result lst_ping(TinyFrame *tf, TF_Msg *msg); |
||||||
|
static TF_Result lst_unit(TinyFrame *tf, TF_Msg *msg); |
||||||
|
static TF_Result lst_list_units(TinyFrame *tf, TF_Msg *msg); |
||||||
|
static TF_Result lst_default(TinyFrame *tf, TF_Msg *msg); |
||||||
|
|
||||||
|
void comm_init(void) |
||||||
|
{ |
||||||
|
TF_InitStatic(comm, TF_SLAVE); |
||||||
|
TF_AddTypeListener(comm, MSG_PING, lst_ping); |
||||||
|
TF_AddTypeListener(comm, MSG_UNIT_REQUEST, lst_unit); |
||||||
|
TF_AddTypeListener(comm, MSG_LIST_UNITS, lst_list_units); |
||||||
|
|
||||||
|
// fall-through
|
||||||
|
TF_AddGenericListener(comm, lst_default); |
||||||
|
} |
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void tf_respond_snprintf(TF_TYPE type, TF_ID id, const char *format, ...) |
||||||
|
{ |
||||||
|
#define ERR_STR_LEN 64 |
||||||
|
char buf[ERR_STR_LEN]; |
||||||
|
va_list args; |
||||||
|
va_start(args, format); |
||||||
|
uint32_t len = (uint32_t) fixup_vsnprintf(&buf[0], ERR_STR_LEN, format, args); |
||||||
|
va_end(args); |
||||||
|
|
||||||
|
tf_respond_buf(type, id, (const uint8_t *) buf, len); |
||||||
|
} |
||||||
|
|
||||||
|
void tf_respond_buf(TF_TYPE type, TF_ID id, const uint8_t *buf, uint32_t len) |
||||||
|
{ |
||||||
|
TF_Msg msg; |
||||||
|
TF_ClearMsg(&msg); |
||||||
|
{ |
||||||
|
msg.type = type; |
||||||
|
msg.frame_id = id; |
||||||
|
msg.data = buf; |
||||||
|
msg.len = (TF_LEN) len; |
||||||
|
} |
||||||
|
TF_Respond(comm, &msg); |
||||||
|
} |
||||||
|
|
||||||
|
void tf_send_buf(TF_TYPE type, const uint8_t *buf, uint32_t len) |
||||||
|
{ |
||||||
|
TF_Msg msg; |
||||||
|
TF_ClearMsg(&msg); |
||||||
|
{ |
||||||
|
msg.type = MSG_UNIT_REPORT; |
||||||
|
msg.data = buf; |
||||||
|
msg.len = (TF_LEN) len; |
||||||
|
msg.type = type; |
||||||
|
} |
||||||
|
TF_Send(comm, &msg); // no listener
|
||||||
|
} |
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void job_respond_err(Job *job) |
||||||
|
{ |
||||||
|
tf_respond_str(MSG_ERROR, job->frame_id, job->str); |
||||||
|
} |
||||||
|
|
||||||
|
void sched_respond_err(TF_ID frame_id, const char *message) |
||||||
|
{ |
||||||
|
dbg("ERR: %s", message); |
||||||
|
Job job = { |
||||||
|
.cb = job_respond_err, |
||||||
|
.frame_id = frame_id, |
||||||
|
.str = message |
||||||
|
}; |
||||||
|
scheduleJob(&job, TSK_SCHED_LOW); |
||||||
|
} |
||||||
|
|
||||||
|
void sched_respond_bad_cmd(TF_ID frame_id) |
||||||
|
{ |
||||||
|
sched_respond_err(frame_id, "BAD COMMAND"); |
||||||
|
} |
||||||
|
|
||||||
|
void sched_respond_malformed_cmd(TF_ID frame_id) |
||||||
|
{ |
||||||
|
sched_respond_err(frame_id, "MALFORMED PAYLOAD"); |
||||||
|
} |
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void job_respond_suc(Job *job) |
||||||
|
{ |
||||||
|
tf_respond_ok(job->frame_id); |
||||||
|
} |
||||||
|
|
||||||
|
void sched_respond_suc(TF_ID frame_id) |
||||||
|
{ |
||||||
|
Job job = { |
||||||
|
.cb = job_respond_suc, |
||||||
|
.frame_id = frame_id |
||||||
|
}; |
||||||
|
scheduleJob(&job, TSK_SCHED_LOW); |
||||||
|
} |
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void job_respond_uX(Job *job) |
||||||
|
{ |
||||||
|
tf_respond_buf(MSG_SUCCESS, job->frame_id, (const uint8_t *) &job->d32, job->len); |
||||||
|
} |
||||||
|
|
||||||
|
void sched_respond_u8(TF_ID frame_id, uint8_t d) |
||||||
|
{ |
||||||
|
Job job = { |
||||||
|
.cb = job_respond_uX, |
||||||
|
.frame_id = frame_id, |
||||||
|
.d32 = d, |
||||||
|
.len = 1 |
||||||
|
}; |
||||||
|
scheduleJob(&job, TSK_SCHED_HIGH); |
||||||
|
} |
||||||
|
|
||||||
|
void sched_respond_u16(TF_ID frame_id, uint16_t d) |
||||||
|
{ |
||||||
|
Job job = { |
||||||
|
.cb = job_respond_uX, |
||||||
|
.frame_id = frame_id, |
||||||
|
.d32 = d, |
||||||
|
.len = 2 |
||||||
|
}; |
||||||
|
scheduleJob(&job, TSK_SCHED_HIGH); |
||||||
|
} |
||||||
|
|
||||||
|
void sched_respond_u32(TF_ID frame_id, uint32_t d) |
||||||
|
{ |
||||||
|
Job job = { |
||||||
|
.cb = job_respond_uX, |
||||||
|
.frame_id = frame_id, |
||||||
|
.d32 = d, |
||||||
|
.len = 4 |
||||||
|
}; |
||||||
|
scheduleJob(&job, TSK_SCHED_HIGH); |
||||||
|
} |
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void job_ping_reply(Job *job) |
||||||
|
{ |
||||||
|
tf_respond_snprintf(MSG_SUCCESS, job->frame_id, "%s/%s", GEX_VERSION, GEX_PLATFORM); |
||||||
|
} |
||||||
|
|
||||||
|
static TF_Result lst_ping(TinyFrame *tf, TF_Msg *msg) |
||||||
|
{ |
||||||
|
Job job = { |
||||||
|
.cb = job_ping_reply, |
||||||
|
.frame_id = msg->frame_id |
||||||
|
}; |
||||||
|
scheduleJob(&job, TSK_SCHED_LOW); |
||||||
|
|
||||||
|
return TF_STAY; |
||||||
|
} |
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void job_unhandled_resp(Job *job) |
||||||
|
{ |
||||||
|
tf_respond_snprintf(MSG_ERROR, job->frame_id, "UNKNOWN MSG %"PRIu32, job->d32); |
||||||
|
} |
||||||
|
|
||||||
|
static TF_Result lst_default(TinyFrame *tf, TF_Msg *msg) |
||||||
|
{ |
||||||
|
Job job = { |
||||||
|
.cb = job_unhandled_resp, |
||||||
|
.frame_id = msg->frame_id, |
||||||
|
.d32 = msg->type |
||||||
|
}; |
||||||
|
scheduleJob(&job, TSK_SCHED_LOW); |
||||||
|
|
||||||
|
return TF_STAY; |
||||||
|
} |
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static TF_Result lst_unit(TinyFrame *tf, TF_Msg *msg) |
||||||
|
{ |
||||||
|
ureg_deliver_unit_request(msg); |
||||||
|
return TF_STAY; |
||||||
|
} |
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void job_list_units(Job *job) |
||||||
|
{ |
||||||
|
ureg_report_active_units(job->frame_id); |
||||||
|
} |
||||||
|
|
||||||
|
static TF_Result lst_list_units(TinyFrame *tf, TF_Msg *msg) |
||||||
|
{ |
||||||
|
Job job = { |
||||||
|
.cb = job_list_units, |
||||||
|
.frame_id = msg->frame_id, |
||||||
|
}; |
||||||
|
scheduleJob(&job, TSK_SCHED_LOW); |
||||||
|
|
||||||
|
return TF_STAY; |
||||||
|
} |
@ -0,0 +1,150 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_MESSAGES_H |
||||||
|
#define GEX_MESSAGES_H |
||||||
|
|
||||||
|
#include "sched_queue.h" |
||||||
|
#include "task_sched.h" |
||||||
|
#include "TinyFrame.h" |
||||||
|
|
||||||
|
extern TinyFrame *comm; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize TinyFrame and set up listeners |
||||||
|
*/ |
||||||
|
void comm_init(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported message types (TF_TYPE) |
||||||
|
*/ |
||||||
|
enum TF_Types_ { |
||||||
|
// General, low level
|
||||||
|
MSG_SUCCESS = 0x00, //!< Generic success response; used by default in all responses; payload is transaction-specific
|
||||||
|
MSG_PING = 0x01, //!< Ping request (or response), used to test connection
|
||||||
|
MSG_ERROR = 0x02, //!< Generic failure response (when a request fails to execute)
|
||||||
|
// Unit messages
|
||||||
|
MSG_UNIT_REQUEST = 0x10, //!< Command addressed to a particular unit
|
||||||
|
MSG_UNIT_REPORT = 0x11, //!< Spontaneous report from a unit
|
||||||
|
// System messages
|
||||||
|
MSG_LIST_UNITS = 0x20, //!< Get all unit call-signs and names
|
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Respond to a TF message using printf-like formatting. |
||||||
|
* Works synchronously, must be called on a job queue. |
||||||
|
* |
||||||
|
* @param type - response type byte |
||||||
|
* @param frame_id - ID of the original msg |
||||||
|
* @param format - printf format |
||||||
|
* @param ... - replacements |
||||||
|
*/ |
||||||
|
void __attribute__((format(printf,3,4))) |
||||||
|
tf_respond_snprintf(TF_TYPE type, TF_ID frame_id, const char *format, ...); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Respond to a TF message with a buffer of fixed length and custom type. |
||||||
|
* Works synchronously, must be called on a job queue. |
||||||
|
* |
||||||
|
* @param type - response type byte |
||||||
|
* @param frame_id - ID of the original msg |
||||||
|
* @param buf - byte buffer |
||||||
|
* @param len - buffer size |
||||||
|
*/ |
||||||
|
void tf_respond_buf(TF_TYPE type, TF_ID frame_id, const uint8_t *buf, uint32_t len); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Respond to a TF message with empty body and MSG_SUCCESS type. |
||||||
|
* Works synchronously, must be called on a job queue. |
||||||
|
* |
||||||
|
* @param frame_id - ID of the original msg |
||||||
|
*/ |
||||||
|
static inline void tf_respond_ok(TF_ID frame_id) |
||||||
|
{ |
||||||
|
tf_respond_buf(MSG_SUCCESS, frame_id, NULL, 0); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Same like tf_respond_buf(), but used for sending spontaneous reports. |
||||||
|
* Works synchronously, must be called on a job queue / timer task etc. |
||||||
|
* |
||||||
|
* @param type - response type byte |
||||||
|
* @param buf - byte buffer |
||||||
|
* @param len - buffer size |
||||||
|
*/ |
||||||
|
void tf_send_buf(TF_TYPE type, const uint8_t *buf, uint32_t len); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Same like tf_respond_buf(), but the buffer length is measured with strlen. |
||||||
|
* Used to sending ASCII string responses. |
||||||
|
* Works synchronously, must be called on a job queue. |
||||||
|
* |
||||||
|
* @param type - response type byte |
||||||
|
* @param frame_id - ID of the original msg |
||||||
|
* @param str - character buffer, zero terminated |
||||||
|
*/ |
||||||
|
static inline void tf_respond_str(TF_TYPE type, TF_ID frame_id, const char *str) |
||||||
|
{ |
||||||
|
tf_respond_buf(type, frame_id, (const uint8_t *) str, (uint32_t) strlen(str)); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule sending an ASCII string error response. |
||||||
|
* Schedules a low priority job. |
||||||
|
* |
||||||
|
* @param frame_id - ID of the original msg |
||||||
|
* @param str - character buffer, zero terminated |
||||||
|
*/ |
||||||
|
void sched_respond_err(TF_ID frame_id, const char *str); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Variant of sched_respond_err() for reporting bad received command code |
||||||
|
* |
||||||
|
* @param msg_id - ID of the original msg |
||||||
|
*/ |
||||||
|
void sched_respond_bad_cmd(TF_ID frame_id); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Variant of sched_respond_err() for reporting malformed commands (e.g. too short payload) |
||||||
|
* |
||||||
|
* @param msg_id - ID of the original msg |
||||||
|
*/ |
||||||
|
void sched_respond_malformed_cmd(TF_ID frame_id); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule sending an empty response with MSG_SUCCESS type. |
||||||
|
* Schedules a low priority job. |
||||||
|
* |
||||||
|
* @param frame_id - ID of the original msg |
||||||
|
*/ |
||||||
|
void sched_respond_suc(TF_ID frame_id); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule sending a one-byte response with MSG_SUCCESS type. |
||||||
|
* Schedules a high priority job. |
||||||
|
* |
||||||
|
* @param frame_id - ID of the original msg |
||||||
|
* @param d - data |
||||||
|
*/ |
||||||
|
void sched_respond_u8(TF_ID frame_id, uint8_t d); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule sending a two-byte response with MSG_SUCCESS type. |
||||||
|
* Schedules a high priority job. |
||||||
|
* |
||||||
|
* @param frame_id - ID of the original msg |
||||||
|
* @param d - data |
||||||
|
*/ |
||||||
|
void sched_respond_u16(TF_ID frame_id, uint16_t d); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule sending a 4-byte response with MSG_SUCCESS type. |
||||||
|
* Schedules a high priority job. |
||||||
|
* |
||||||
|
* @param frame_id - ID of the original msg |
||||||
|
* @param d - data |
||||||
|
*/ |
||||||
|
void sched_respond_u32(TF_ID frame_id, uint32_t d); |
||||||
|
|
||||||
|
#endif //GEX_MESSAGES_H
|
@ -0,0 +1,184 @@ |
|||||||
|
/* Includes ------------------------------------------------------------------*/ |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include <TinyFrame.h> |
||||||
|
#include "platform/debug_uart.h" |
||||||
|
#include "platform/status_led.h" |
||||||
|
|
||||||
|
/* External variables --------------------------------------------------------*/ |
||||||
|
|
||||||
|
/******************************************************************************/ |
||||||
|
/* Cortex-M3 Processor Interruption and Exception Handlers */
|
||||||
|
/******************************************************************************/ |
||||||
|
|
||||||
|
/******************************************************************************/ |
||||||
|
/* STM32F1xx Peripheral Interrupt Handlers */ |
||||||
|
/* Add here the Interrupt Handlers for the used peripherals. */ |
||||||
|
/* For the available peripheral interrupt handler names, */ |
||||||
|
/* please refer to the startup file (startup_stm32f1xx.s). */ |
||||||
|
/******************************************************************************/ |
||||||
|
|
||||||
|
/* USER CODE BEGIN 1 */ |
||||||
|
|
||||||
|
#define tFAULT "\r\n\033[31mSYSTEM FAULT:\033[m" |
||||||
|
|
||||||
|
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName) |
||||||
|
{ |
||||||
|
/* Run time stack overflow checking is performed if
|
||||||
|
configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook function is |
||||||
|
called if a stack overflow is detected. */ |
||||||
|
PRINTF(tFAULT" RTOS stack overflow! tsk: %s\r\n", (char *) pcTaskName); |
||||||
|
StatusLed_On(STATUS_FAULT); |
||||||
|
while (1); |
||||||
|
} |
||||||
|
|
||||||
|
#if VERBOSE_HARDFAULT |
||||||
|
void prvGetRegistersFromStack( uint32_t *origStack, uint32_t lr_value) |
||||||
|
{ |
||||||
|
/* These are volatile to try and prevent the compiler/linker optimising them
|
||||||
|
away as the variables never actually get used. If the debugger won't show the |
||||||
|
values of the variables, make them global my moving their declaration outside |
||||||
|
of this function. */ |
||||||
|
volatile uint32_t stacked_r0; |
||||||
|
volatile uint32_t stacked_r1; |
||||||
|
volatile uint32_t stacked_r2; |
||||||
|
volatile uint32_t stacked_r3; |
||||||
|
volatile uint32_t stacked_r12; |
||||||
|
volatile uint32_t stacked_lr; /* Link register. */ |
||||||
|
volatile uint32_t stacked_pc; /* Program counter. */ |
||||||
|
volatile uint32_t stacked_psr;/* Program status register. */ |
||||||
|
|
||||||
|
uint32_t cfsr, hfsr, dfsr; |
||||||
|
uint32_t bus_fault_address; |
||||||
|
uint32_t memmanage_fault_address; |
||||||
|
|
||||||
|
bus_fault_address = SCB->BFAR; |
||||||
|
memmanage_fault_address = SCB->MMFAR; |
||||||
|
cfsr = SCB->CFSR; |
||||||
|
hfsr = SCB->HFSR; |
||||||
|
dfsr = SCB->DFSR; |
||||||
|
|
||||||
|
stacked_r0 = origStack[0]; |
||||||
|
stacked_r1 = origStack[1]; |
||||||
|
stacked_r2 = origStack[2]; |
||||||
|
stacked_r3 = origStack[3]; |
||||||
|
stacked_r12 = origStack[4]; |
||||||
|
stacked_lr = origStack[5]; |
||||||
|
stacked_pc = origStack[6]; |
||||||
|
stacked_psr = origStack[7]; |
||||||
|
|
||||||
|
#define BS(reg, pos, str) (((reg)&(1<<(pos)))?(str" "):"") |
||||||
|
#define REDPTR(val) (((val)&0xFF000000) != 0x08000000?"\033[31m":"\033[32m") |
||||||
|
|
||||||
|
/* USER CODE BEGIN HardFault_IRQn 0 */ |
||||||
|
PRINTF(tFAULT" HARD FAULT\r\n\r\n"); |
||||||
|
PRINTF("- Stack frame:\r\n"); |
||||||
|
PRINTF(" R0 = \033[35m%"PRIX32"h\033[m\r\n", stacked_r0); |
||||||
|
PRINTF(" R1 = \033[35m%"PRIX32"h\033[m\r\n", stacked_r1); |
||||||
|
PRINTF(" R2 = \033[35m%"PRIX32"h\033[m\r\n", stacked_r2); |
||||||
|
PRINTF(" R3 = \033[35m%"PRIX32"h\033[m\r\n", stacked_r3); |
||||||
|
PRINTF(" R12 = \033[35m%"PRIX32"h\033[m\r\n", stacked_r12); |
||||||
|
PRINTF(" LR = %s0x%08"PRIX32"\033[m\r\n", REDPTR(stacked_lr), stacked_lr); |
||||||
|
PRINTF(" PC = %s0x%08"PRIX32"\033[m\r\n", REDPTR(stacked_pc), stacked_pc); |
||||||
|
PRINTF(" PSR = \033[36m0x%08"PRIX32"\033[m", stacked_psr); |
||||||
|
uint32_t exc = stacked_psr & 0x3F; |
||||||
|
PRINTF(" [ %s%s%s%s%s ]\r\n", |
||||||
|
BS(stacked_psr, 31, "N"), |
||||||
|
BS(stacked_psr, 30, "Z"), |
||||||
|
BS(stacked_psr, 29, "C"), |
||||||
|
BS(stacked_psr, 28, "V"), |
||||||
|
//BS(stacked_psr, 24, "T"), - thumb, always ON
|
||||||
|
|
||||||
|
(exc==0)?"Thread": |
||||||
|
(exc==2)?"NMI": |
||||||
|
(exc==3)?"HardFault": |
||||||
|
(exc==11)?"SVCall": |
||||||
|
(exc==14)?"PendSV": |
||||||
|
(exc==15)?"SysTick": |
||||||
|
(exc>=16)?"IRQ":"Unknown" |
||||||
|
); |
||||||
|
|
||||||
|
PRINTF("\r\n- FSR/FAR:\r\n"); |
||||||
|
PRINTF(" CFSR = \033[36m0x%08"PRIX32"\033[m\r\n", cfsr); |
||||||
|
PRINTF(" UsageFault: \033[31;1m%s%s%s%s%s%s%s\033[m\r\n" |
||||||
|
" BusFault: \033[31;1m%s%s%s%s%s%s%s%s\033[m\r\n" |
||||||
|
" MemFault: \033[31;1m%s%s%s%s%s%s%s\033[m\r\n", |
||||||
|
BS(cfsr, 0, "IAccViol"), |
||||||
|
BS(cfsr, 1, "DAccViol"), |
||||||
|
BS(cfsr, 3, "MUnstkErr"), |
||||||
|
BS(cfsr, 4, "MStkErr"), |
||||||
|
BS(cfsr, 5, "MLSPErr(FPU)"), |
||||||
|
BS(cfsr, 7, "MMArValid"), |
||||||
|
((cfsr&0xFF)?"":"\033[m- "), |
||||||
|
|
||||||
|
BS(cfsr, 8, "IBusErr"), |
||||||
|
BS(cfsr, 9, "PreciseErr"), |
||||||
|
BS(cfsr, 10, "ImpreciseErr"), |
||||||
|
BS(cfsr, 11, "UnstkErr"), |
||||||
|
BS(cfsr, 12, "StkErr"), |
||||||
|
BS(cfsr, 13, "LSPErr"), |
||||||
|
BS(cfsr, 15, "BFArValid"), |
||||||
|
((cfsr&0xFF00)?"":"\033[m- "), |
||||||
|
|
||||||
|
BS(cfsr, 16, "UndefInstr"), |
||||||
|
BS(cfsr, 17, "InvState"), |
||||||
|
BS(cfsr, 18, "InvPC"), |
||||||
|
BS(cfsr, 19, "NoCP"), |
||||||
|
BS(cfsr, 24, "Unaligned"), |
||||||
|
BS(cfsr, 25, "Div0"), |
||||||
|
((cfsr&0xFFFF0000)?"":"\033[m- ") |
||||||
|
); |
||||||
|
|
||||||
|
PRINTF(" HFSR = \033[36m0x%08"PRIX32"\033[m", hfsr); |
||||||
|
PRINTF(" [ %s%s%s]\r\n", |
||||||
|
BS(hfsr, 31, "DebugEvt"), |
||||||
|
BS(hfsr, 30, "Forced"), |
||||||
|
BS(hfsr, 1, "VectTbl") |
||||||
|
); |
||||||
|
|
||||||
|
PRINTF(" DFSR = \033[36m0x%08"PRIX32"\033[m", dfsr); |
||||||
|
PRINTF(" [ %s%s%s%s%s]\r\n", |
||||||
|
BS(dfsr, 0, "Halted"), |
||||||
|
BS(dfsr, 1, "Bkpt"), |
||||||
|
BS(dfsr, 2, "DWtTrap"), |
||||||
|
BS(dfsr, 3, "VCatch"), |
||||||
|
BS(dfsr, 4, "External") |
||||||
|
); |
||||||
|
|
||||||
|
if (cfsr & 0x0080) PRINTF(" MMFAR = \033[33m0x%08"PRIX32"\033[m\r\n", memmanage_fault_address); |
||||||
|
if (cfsr & 0x8000) PRINTF(" BFAR = \033[33m0x%08"PRIX32"\033[m\r\n", bus_fault_address); |
||||||
|
PRINTF("\r\n- Misc\r\n"); |
||||||
|
PRINTF(" LR/EXC_RETURN= %s0x%08"PRIX32"\033[m\n", REDPTR(lr_value), lr_value); |
||||||
|
|
||||||
|
StatusLed_On(STATUS_FAULT); |
||||||
|
while (1); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function handles Hard fault interrupt. |
||||||
|
*/ |
||||||
|
void __attribute__((naked)) HardFault_Handler(void) |
||||||
|
{ |
||||||
|
#if VERBOSE_HARDFAULT |
||||||
|
__asm volatile |
||||||
|
( |
||||||
|
" tst lr, #4 \n" |
||||||
|
" ite eq \n" |
||||||
|
" mrseq r0, msp \n" |
||||||
|
" mrsne r0, psp \n" |
||||||
|
" ldr r1, [r0, #24] \n" |
||||||
|
" mov r2, lr \n" |
||||||
|
" ldr r3, handler2_address_const \n" |
||||||
|
" bx r3 \n" |
||||||
|
" handler2_address_const: .word prvGetRegistersFromStack \n" |
||||||
|
); |
||||||
|
#endif |
||||||
|
|
||||||
|
PRINTF(tFAULT" HARD FAULT\r\n\r\n"); |
||||||
|
StatusLed_On(STATUS_FAULT); |
||||||
|
while (1); |
||||||
|
} |
||||||
|
|
||||||
|
/* USER CODE END 1 */ |
||||||
|
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
@ -0,0 +1,83 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/04.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include <reent.h> |
||||||
|
|
||||||
|
#if USE_DEBUG_UART |
||||||
|
|
||||||
|
#define DBG_BUF_LEN 80 |
||||||
|
|
||||||
|
// debug printf
|
||||||
|
int PRINTF(const char *format, ...) |
||||||
|
{ |
||||||
|
va_list args; |
||||||
|
int len; |
||||||
|
char dbg_buf[DBG_BUF_LEN]; |
||||||
|
|
||||||
|
va_start(args, format); |
||||||
|
/*convert into string at buff[0] of length iw*/ |
||||||
|
len = (int)fixup_vsnprintf(&dbg_buf[0], DBG_BUF_LEN, format, args); |
||||||
|
_write_r(NULL, 2, dbg_buf, (size_t) len); |
||||||
|
va_end(args); |
||||||
|
return len; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts with fixed size (print to debug uart) |
||||||
|
* @param string - buffer to print |
||||||
|
* @param len - number of bytes to print |
||||||
|
*/ |
||||||
|
void PUTSN(const char *string, size_t len) |
||||||
|
{ |
||||||
|
if (len == 0) len = strlen(string); |
||||||
|
_write_r(NULL, 2, string, (size_t) len); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a string to debug uart |
||||||
|
* @param string - string to print, zero-terminated |
||||||
|
* @return number of characters printed |
||||||
|
*/ |
||||||
|
int PUTS(const char *string) |
||||||
|
{ |
||||||
|
size_t len = strlen(string); |
||||||
|
_write_r(NULL, 2, string, len); |
||||||
|
return (int) len; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Print one character to debug uart |
||||||
|
* @param ch - character ASCII code |
||||||
|
* @return the character code |
||||||
|
*/ |
||||||
|
int PUTCHAR(int ch) |
||||||
|
{ |
||||||
|
_write_r(NULL, 2, &ch, 1); |
||||||
|
return ch; // or EOF
|
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Print string to debug uart, add newline if missing (printf-like) |
||||||
|
* @param format |
||||||
|
* @param ... |
||||||
|
*/ |
||||||
|
void dbg(const char *format, ...) |
||||||
|
{ |
||||||
|
va_list args; |
||||||
|
int len; |
||||||
|
char dbg_buf[DBG_BUF_LEN]; |
||||||
|
|
||||||
|
va_start(args, format); |
||||||
|
len = (int)VSNPRINTF(&dbg_buf[0], DBG_BUF_LEN, format, args); |
||||||
|
_write_r(NULL, 2, dbg_buf, (size_t) len); |
||||||
|
|
||||||
|
// add newline if not present
|
||||||
|
if (dbg_buf[len-1] != '\n') |
||||||
|
_write_r(NULL, 2, "\r\n", 2); |
||||||
|
|
||||||
|
va_end(args); |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,29 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/04.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_DEBUG_H |
||||||
|
#define GEX_DEBUG_H |
||||||
|
|
||||||
|
#include <inttypes.h> |
||||||
|
#include <stddef.h> |
||||||
|
#include <stdarg.h> |
||||||
|
|
||||||
|
#if USE_DEBUG_UART |
||||||
|
|
||||||
|
void dbg(const char *format, ...) __attribute__((format(printf,1,2))) ; |
||||||
|
|
||||||
|
int PRINTF(const char *format, ...) __attribute__((format(printf,1,2))) ; |
||||||
|
void PUTSN(const char *string, size_t len); |
||||||
|
int PUTS(const char *string); |
||||||
|
int PUTCHAR(int ch); |
||||||
|
|
||||||
|
#else |
||||||
|
#define dbg(format, ...) do {} while (0) |
||||||
|
#define PRINTF(format, ...) do {} while (0) |
||||||
|
#define PUTSN(string, len) do {} while (0) |
||||||
|
#define PUTS(string) do {} while (0) |
||||||
|
#define PUTCHAR(ch) do {} while (0) |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif //GEX_DEBUG_H
|
@ -0,0 +1,136 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "unit.h" |
||||||
|
#include "resources.h" |
||||||
|
|
||||||
|
static bool rsc_initialized = false; |
||||||
|
|
||||||
|
// This takes quite a lot of space, we could use u8 and IDs instead if needed
|
||||||
|
struct resouce_slot { |
||||||
|
const char *name; |
||||||
|
Unit *owner; |
||||||
|
} __attribute__((packed)); |
||||||
|
|
||||||
|
static struct resouce_slot resources[R_RESOURCE_COUNT]; |
||||||
|
|
||||||
|
// here are the resource names for better debugging (could also be removed if absolutely necessary)
|
||||||
|
const char *const rsc_names[] = { |
||||||
|
#define X(res_name) #res_name, |
||||||
|
XX_RESOURCES |
||||||
|
#undef X |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the resources registry |
||||||
|
*/ |
||||||
|
void rsc_init_registry(void) |
||||||
|
{ |
||||||
|
for (int i = 0; i < R_RESOURCE_COUNT; i++) { |
||||||
|
resources[i].owner = &UNIT_PLATFORM; |
||||||
|
} |
||||||
|
rsc_initialized = true; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Claim a resource for a unit |
||||||
|
* |
||||||
|
* @param unit - claiming unit |
||||||
|
* @param rsc - resource to claim |
||||||
|
* @return true on successful claim |
||||||
|
*/ |
||||||
|
bool rsc_claim(Unit *unit, Resource rsc) |
||||||
|
{ |
||||||
|
assert_param(rsc_initialized); |
||||||
|
assert_param(rsc > R_NONE && rsc < R_RESOURCE_COUNT); |
||||||
|
assert_param(unit != NULL); |
||||||
|
|
||||||
|
if (resources[rsc].owner) { |
||||||
|
//TODO properly report to user
|
||||||
|
dbg("ERROR!! Unit %s failed to claim resource %s, already held by %s!", |
||||||
|
unit->name, rsc_names[rsc], resources[rsc].owner->name); |
||||||
|
|
||||||
|
unit->status = E_RESOURCE_NOT_AVAILABLE; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
resources[rsc].owner = unit; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Claim a range of resources for a unit (useful for GPIO) |
||||||
|
* |
||||||
|
* @param unit - claiming unit |
||||||
|
* @param rsc0 - first resource to claim |
||||||
|
* @param rsc1 - last resource to claim |
||||||
|
* @return true on complete claim, false if any failed (none are claimed in that case) |
||||||
|
*/ |
||||||
|
bool rsc_claim_range(Unit *unit, Resource rsc0, Resource rsc1) |
||||||
|
{ |
||||||
|
assert_param(rsc_initialized); |
||||||
|
assert_param(rsc0 > R_NONE && rsc0 < R_RESOURCE_COUNT); |
||||||
|
assert_param(rsc1 > R_NONE && rsc1 < R_RESOURCE_COUNT); |
||||||
|
assert_param(unit != NULL); |
||||||
|
|
||||||
|
for (int i = rsc0; i <= rsc1; i++) { |
||||||
|
if (!rsc_claim(unit, (Resource) i)) return false; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a resource for other use |
||||||
|
* |
||||||
|
* @param unit - owning unit; if not null, free only resources claimed by this unit |
||||||
|
* @param rsc - resource to free |
||||||
|
*/ |
||||||
|
void rsc_free(Unit *unit, Resource rsc) |
||||||
|
{ |
||||||
|
assert_param(rsc_initialized); |
||||||
|
assert_param(rsc > R_NONE && rsc < R_RESOURCE_COUNT); |
||||||
|
|
||||||
|
if (unit == NULL || resources[rsc].owner == unit) { |
||||||
|
resources[rsc].owner = NULL; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a range of resources (useful for GPIO) |
||||||
|
* |
||||||
|
* @param unit - owning unit; if not null, free only resources claimed by this unit |
||||||
|
* @param rsc0 - first resource to free |
||||||
|
* @param rsc1 - last resource to free |
||||||
|
*/ |
||||||
|
void rsc_free_range(Unit *unit, Resource rsc0, Resource rsc1) |
||||||
|
{ |
||||||
|
assert_param(rsc_initialized); |
||||||
|
assert_param(rsc0 > R_NONE && rsc0 < R_RESOURCE_COUNT); |
||||||
|
assert_param(rsc1 > R_NONE && rsc1 < R_RESOURCE_COUNT); |
||||||
|
|
||||||
|
for (int i = rsc0; i <= rsc1; i++) { |
||||||
|
if (unit == NULL || resources[i].owner == unit) { |
||||||
|
resources[i].owner = NULL; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Tear down a unit - release all resources owned by the unit |
||||||
|
* |
||||||
|
* @param unit - unit to tear down; free only resources claimed by this unit |
||||||
|
*/ |
||||||
|
void rsc_teardown(Unit *unit) |
||||||
|
{ |
||||||
|
assert_param(rsc_initialized); |
||||||
|
assert_param(unit != NULL); |
||||||
|
|
||||||
|
for (int i = R_NONE+1; i < R_RESOURCE_COUNT; i++) { |
||||||
|
if (resources[i].owner == unit) { |
||||||
|
resources[i].owner = NULL; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_RESOURCES_H |
||||||
|
#define GEX_RESOURCES_H |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "unit.h" |
||||||
|
|
||||||
|
#define CHECK_SUC() do { if (!suc) return false; } while (0) |
||||||
|
|
||||||
|
// X macro: Resource name,
|
||||||
|
#define XX_RESOURCES \ |
||||||
|
X(NONE) \
|
||||||
|
X(PA0) X(PA1) X(PA2) X(PA3) X(PA4) X(PA5) X(PA6) X(PA7) \
|
||||||
|
X(PA8) X(PA9) X(PA10) X(PA11) X(PA12) X(PA13) X(PA14) X(PA15) \
|
||||||
|
X(PB0) X(PB1) X(PB2) X(PB3) X(PB4) X(PB5) X(PB6) X(PB7) \
|
||||||
|
X(PB8) X(PB9) X(PB10) X(PB11) X(PB12) X(PB13) X(PB14) X(PB15) \
|
||||||
|
X(PC0) X(PC1) X(PC2) X(PC3) X(PC4) X(PC5) X(PC6) X(PC7) \
|
||||||
|
X(PC8) X(PC9) X(PC10) X(PC11) X(PC12) X(PC13) X(PC14) X(PC15) \
|
||||||
|
X(PD0) X(PD1) X(PD2) X(PD3) X(PD4) X(PD5) X(PD6) X(PD7) \
|
||||||
|
X(PD8) X(PD9) X(PD10) X(PD11) X(PD12) X(PD13) X(PD14) X(PD15) \
|
||||||
|
X(PE0) X(PE1) X(PE2) X(PE3) X(PE4) X(PE5) X(PE6) X(PE7) \
|
||||||
|
X(PE8) X(PE9) X(PE10) X(PE11) X(PE12) X(PE13) X(PE14) X(PE15) \
|
||||||
|
X(SPI1) X(SPI2) X(SPI3) \
|
||||||
|
X(I2C1) X(I2C2) X(I2C3) \
|
||||||
|
X(I2S1) X(I2S2) X(I2S3) \
|
||||||
|
X(ADC1) X(ADC2) X(ADC3) X(ADC4) \
|
||||||
|
X(DAC1) X(DAC2) \
|
||||||
|
X(USART1) X(USART2) X(USART3) X(USART4) X(USART5) X(USART6) \
|
||||||
|
X(TIM1) X(TIM2) X(TIM3) X(TIM4) X(TIM5) \
|
||||||
|
X(TIM6) X(TIM7) X(TIM8) X(TIM9) X(TIM10) X(TIM11) X(TIM12) X(TIM13) X(TIM14) \
|
||||||
|
X(TIM15) X(TIM16) X(TIM17) \
|
||||||
|
X(DMA1) X(DMA2) \
|
||||||
|
X(RNG) X(LCD) |
||||||
|
|
||||||
|
// GPIOs are allocated whenever the pin is needed
|
||||||
|
// (e.g. when used for SPI, the R_SPI resource as well as the corresponding R_GPIO resources must be claimed)
|
||||||
|
|
||||||
|
// Peripheral blocks (IPs) - not all chips have all blocks, usually the 1 and 2 are present as a minimum, if any.
|
||||||
|
// It doesn't really make sense to expose multiple instances of buses that support addressing
|
||||||
|
|
||||||
|
// ADCs - some more advanced chips support differential input mode on some (not all!) inputs
|
||||||
|
// Usually only one or two instances are present
|
||||||
|
|
||||||
|
// DAC - often only one is present, or none.
|
||||||
|
|
||||||
|
// UARTs
|
||||||
|
// - 1 and 2 are present universally, 2 is connected to VCOM on Nucleo/Discovery boards, good for debug messages
|
||||||
|
// 4 and 5 don't support synchronous mode.
|
||||||
|
|
||||||
|
// Timers
|
||||||
|
// - some support quadrature input, probably all support external clock / gating / clock-out/PWM generation
|
||||||
|
// Not all chips have all timers and not all timers are equal.
|
||||||
|
|
||||||
|
// DMA - Direct memory access lines - TODO split those to channels, they can be used separately
|
||||||
|
|
||||||
|
// The resource registry will be pre-loaded with platform-specific config of which blocks are available - the rest will be "pre-claimed"
|
||||||
|
// (i.e. unavailable to functional modules)
|
||||||
|
|
||||||
|
typedef enum hw_resource Resource; |
||||||
|
|
||||||
|
enum hw_resource { |
||||||
|
#define X(res_name) R_##res_name, |
||||||
|
XX_RESOURCES |
||||||
|
#undef X |
||||||
|
R_RESOURCE_COUNT |
||||||
|
}; |
||||||
|
|
||||||
|
void rsc_init_registry(void); |
||||||
|
|
||||||
|
bool rsc_claim(Unit *unit, Resource rsc); |
||||||
|
bool rsc_claim_range(Unit *unit, Resource rsc0, Resource rsc1); |
||||||
|
void rsc_teardown(Unit *unit); |
||||||
|
|
||||||
|
void rsc_free(Unit *unit, Resource rsc); |
||||||
|
void rsc_free_range(Unit *unit, Resource rsc0, Resource rsc1); |
||||||
|
|
||||||
|
#endif //GEX_RESOURCES_H
|
@ -0,0 +1,236 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "settings.h" |
||||||
|
#include "unit_registry.h" |
||||||
|
#include "system_settings.h" |
||||||
|
#include "utils/str_utils.h" |
||||||
|
|
||||||
|
// This is the first entry in a valid config.
|
||||||
|
// Change with each breaking change to force config reset.
|
||||||
|
#define CONFIG_MARKER 0xA55C |
||||||
|
|
||||||
|
void settings_load(void) |
||||||
|
{ |
||||||
|
dbg("Loading settings"); |
||||||
|
|
||||||
|
uint8_t *buffer = (uint8_t *) SETTINGS_FLASH_ADDR; |
||||||
|
|
||||||
|
PayloadParser pp = pp_start(buffer, SETTINGS_BLOCK_SIZE, NULL); |
||||||
|
|
||||||
|
// Check the integrity marker
|
||||||
|
if (pp_u16(&pp) != CONFIG_MARKER) { |
||||||
|
dbg("Config not valid!"); |
||||||
|
// Save for next run
|
||||||
|
settings_save(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// System section
|
||||||
|
if (!systemsettings_load(&pp)) { |
||||||
|
dbg("!! System settings failed to load"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (!ureg_load_units(&pp)) { |
||||||
|
dbg("!! Unit settings failed to load"); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#define SAVE_BUF_SIZE 256 |
||||||
|
static uint8_t save_buffer[SAVE_BUF_SIZE]; |
||||||
|
static uint32_t save_addr; |
||||||
|
|
||||||
|
#if DEBUG_FLASH_WRITE |
||||||
|
#define fls_printf(fmt, ...) dbg(fmt, ##__VA_ARGS__) |
||||||
|
#else |
||||||
|
#define fls_printf(fmt, ...) do {} while (0) |
||||||
|
#endif |
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush the save buffer to flash, moving leftovers from uneven half-words |
||||||
|
* to the beginning and adjusting the CWPack curent pointer accordingly. |
||||||
|
* |
||||||
|
* @param ctx - pack context |
||||||
|
* @param final - if true, flush uneven leftovers; else move them to the beginning and keep for next call. |
||||||
|
*/ |
||||||
|
static void savebuf_flush(PayloadBuilder *pb, bool final) |
||||||
|
{ |
||||||
|
// TODO this might be buggy, was not tested cross-boundary yet
|
||||||
|
// TODO remove those printf's after verifying correctness
|
||||||
|
|
||||||
|
uint32_t bytes = (uint32_t) pb_length(pb); |
||||||
|
|
||||||
|
// Dump what we're flushing
|
||||||
|
fls_printf("Flush: "); |
||||||
|
for (uint32_t i = 0; i < bytes; i++) { |
||||||
|
fls_printf("%02X ", save_buffer[i]); |
||||||
|
} |
||||||
|
fls_printf("\r\n"); |
||||||
|
|
||||||
|
uint32_t halfwords = bytes >> 1; |
||||||
|
uint32_t remain = bytes & 1; // how many bytes won't be programmed
|
||||||
|
|
||||||
|
fls_printf("Halfwords: %d, Remain: %d, last? %d\r\n", (int)halfwords, (int)remain, final); |
||||||
|
|
||||||
|
uint16_t *hwbuf = (void*) &save_buffer[0]; |
||||||
|
|
||||||
|
for (; halfwords > 0; halfwords--) { |
||||||
|
uint16_t hword = *hwbuf++; |
||||||
|
|
||||||
|
fls_printf("%04X ", hword); |
||||||
|
|
||||||
|
HAL_StatusTypeDef res = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, save_addr, hword); |
||||||
|
assert_param(HAL_OK == res); |
||||||
|
save_addr += 2; // advance
|
||||||
|
} |
||||||
|
|
||||||
|
// rewind the context buffer
|
||||||
|
pb->current = pb->start; |
||||||
|
|
||||||
|
if (remain) { |
||||||
|
// We have an odd byte left to write
|
||||||
|
if (final) { |
||||||
|
// We're done writing, this is the last call. Append the last byte to flash.
|
||||||
|
uint16_t hword = save_buffer[bytes-1]; |
||||||
|
fls_printf("& %02X ", hword); |
||||||
|
|
||||||
|
HAL_StatusTypeDef res = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, save_addr, hword); |
||||||
|
assert_param(HAL_OK == res); |
||||||
|
} else { |
||||||
|
// Move the leftover to the beginning of the buffer for next call.
|
||||||
|
save_buffer[0] = save_buffer[bytes-1]; |
||||||
|
pb->current++; |
||||||
|
} |
||||||
|
} |
||||||
|
fls_printf("\r\n"); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Save buffer overflow handler. |
||||||
|
* This should flush whatever is in the buffer and let CWPack continue |
||||||
|
* |
||||||
|
* @param pb - buffer |
||||||
|
* @param more - how many more bytes are needed (this is meant for realloc / buffer expanding) |
||||||
|
* @return - success code |
||||||
|
*/ |
||||||
|
static bool savebuf_ovhandler(PayloadBuilder *pb, uint32_t more) |
||||||
|
{ |
||||||
|
if (more > SAVE_BUF_SIZE) return false; |
||||||
|
savebuf_flush(pb, false); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// Save settings to flash
|
||||||
|
void settings_save(void) |
||||||
|
{ |
||||||
|
HAL_StatusTypeDef hst; |
||||||
|
PayloadBuilder pb = pb_start(save_buffer, SAVE_BUF_SIZE, savebuf_ovhandler); |
||||||
|
|
||||||
|
save_addr = SETTINGS_FLASH_ADDR; |
||||||
|
|
||||||
|
fls_printf("--- Starting flash write... ---\r\n"); |
||||||
|
hst = HAL_FLASH_Unlock(); |
||||||
|
assert_param(hst == HAL_OK); |
||||||
|
{ |
||||||
|
fls_printf("ERASE flash pages for settings storage...\r\n"); |
||||||
|
// We have to first erase the pages
|
||||||
|
FLASH_EraseInitTypeDef erase; |
||||||
|
erase.Banks = FLASH_BANK_1; // TODO ?????
|
||||||
|
erase.NbPages = SETTINGS_BLOCK_SIZE/FLASH_PAGE_SIZE; |
||||||
|
erase.PageAddress = SETTINGS_FLASH_ADDR; |
||||||
|
erase.TypeErase = FLASH_TYPEERASE_PAGES; |
||||||
|
uint32_t pgerror = 0; |
||||||
|
hst = HAL_FLASHEx_Erase(&erase, &pgerror); |
||||||
|
assert_param(pgerror == 0xFFFFFFFFU); |
||||||
|
assert_param(hst == HAL_OK); |
||||||
|
|
||||||
|
// and now we can start writing...
|
||||||
|
|
||||||
|
fls_printf("Beginning settings collect\r\n"); |
||||||
|
|
||||||
|
// Marker that this is a valid save
|
||||||
|
pb_u16(&pb, CONFIG_MARKER); |
||||||
|
fls_printf("Saving system settings\r\n"); |
||||||
|
systemsettings_save(&pb); |
||||||
|
fls_printf("Saving units\r\n"); |
||||||
|
ureg_save_units(&pb); |
||||||
|
|
||||||
|
fls_printf("Final flush\r\n"); |
||||||
|
savebuf_flush(&pb, true); |
||||||
|
} |
||||||
|
fls_printf("Locking flash...\r\n"); |
||||||
|
hst = HAL_FLASH_Lock(); |
||||||
|
assert_param(hst == HAL_OK); |
||||||
|
fls_printf("--- Flash done ---\r\n"); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Write system settings to INI (without section) |
||||||
|
*/ |
||||||
|
void settings_write_ini(IniWriter *iw) |
||||||
|
{ |
||||||
|
// File header
|
||||||
|
iw_comment(iw, "CONFIG.INI"); |
||||||
|
iw_comment(iw, "Changes are applied on file save and can be immediately tested and verified."); |
||||||
|
iw_comment(iw, "To persist to flash, replace the LOCK jumper before disconnecting from USB."); // TODO the jumper...
|
||||||
|
|
||||||
|
systemsettings_write_ini(iw); |
||||||
|
iw_newline(iw); |
||||||
|
|
||||||
|
ureg_export_combined(iw); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void settings_read_ini_begin(void) |
||||||
|
{ |
||||||
|
SystemSettings.modified = true; |
||||||
|
|
||||||
|
// load defaults
|
||||||
|
systemsettings_init(); |
||||||
|
ureg_remove_all_units(); |
||||||
|
} |
||||||
|
|
||||||
|
void settings_read_ini(const char *restrict section, const char *restrict key, const char *restrict value) |
||||||
|
{ |
||||||
|
// dbg("[%s] %s = %s", section, key, value);
|
||||||
|
|
||||||
|
if (streq(section, "SYSTEM")) { |
||||||
|
// system is always at the top
|
||||||
|
systemsettings_read_ini(key, value); |
||||||
|
} |
||||||
|
else if (streq(section, "UNITS")) { |
||||||
|
// this will always come before individual units config
|
||||||
|
// install or tear down units as described by the config
|
||||||
|
ureg_instantiate_by_ini(key, value); |
||||||
|
} else { |
||||||
|
// not a standard section, may be some unit config
|
||||||
|
// all unit sections contain the colon character [TYPE:NAME]
|
||||||
|
const char *nameptr = strchr(section, ':'); |
||||||
|
if (nameptr) { |
||||||
|
ureg_read_unit_ini(nameptr+1, key, value); |
||||||
|
} else { |
||||||
|
dbg("! Bad config key: [%s] %s = %s", section, key, value); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void settings_read_ini_end(void) |
||||||
|
{ |
||||||
|
if (!ureg_finalize_all_init()) { |
||||||
|
dbg("Some units failed to init!!"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t settings_get_ini_len(void) |
||||||
|
{ |
||||||
|
// this writer is configured to skip everything, so each written byte will decrement the skip count
|
||||||
|
IniWriter iw = iw_init(NULL, 0xFFFFFFFF, 1); |
||||||
|
settings_write_ini(&iw); |
||||||
|
// now we just check how many bytes were skipped
|
||||||
|
return 0xFFFFFFFF - iw.skip; |
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_SETTINGS_H |
||||||
|
#define GEX_SETTINGS_H |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "utils/ini_writer.h" |
||||||
|
|
||||||
|
/**
|
||||||
|
* Load settings from flash (system settings + units). |
||||||
|
* Unit registry must be already initialized. |
||||||
|
* |
||||||
|
* This should happen only once, during the boot sequence. |
||||||
|
*/ |
||||||
|
void settings_load(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Save all settings to flash. |
||||||
|
* This may be called multiple times after user changes the config file. |
||||||
|
*/ |
||||||
|
void settings_save(void); |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this before any of the ini read stuff |
||||||
|
* |
||||||
|
* Resets everything to defaults so we have a clean start. |
||||||
|
* |
||||||
|
* NOTE: Should the file be received only partially, this may corrupt the settings. |
||||||
|
* For this reason we don't commit it to flash immediately but require user to replace |
||||||
|
* the LOCK jumper before unplugging the device. (TODO implement the LOCK jumper and this feature!!) |
||||||
|
*/ |
||||||
|
void settings_read_ini_begin(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Load settings from INI kv pair. |
||||||
|
*/ |
||||||
|
void settings_read_ini(const char *restrict section, const char *restrict key, const char *restrict value); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this before any of the ini read stuff |
||||||
|
* |
||||||
|
* Resets everything to defaults so we have a clean start. |
||||||
|
* |
||||||
|
* NOTE: Should the file be received only partially, this may corrupt the settings. |
||||||
|
* For this reason we don't commit it to flash immediately but require user to replace |
||||||
|
* the LOCK jumper before unplugging the device. (TODO implement the LOCK jumper and this feature!!) |
||||||
|
*/ |
||||||
|
void settings_read_ini_end(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Write all settings to a iniwriter |
||||||
|
* @param iw - writer handle |
||||||
|
*/ |
||||||
|
void settings_write_ini(IniWriter *iw); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Get total settings len (caution: this is expensive, works by dummy-printing everything) |
||||||
|
* |
||||||
|
* @return bytes |
||||||
|
*/ |
||||||
|
uint32_t settings_get_ini_len(void); |
||||||
|
|
||||||
|
#endif //GEX_SETTINGS_H
|
@ -0,0 +1,57 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/02.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "utils/str_utils.h" |
||||||
|
#include "system_settings.h" |
||||||
|
|
||||||
|
struct system_settings SystemSettings; |
||||||
|
|
||||||
|
void systemsettings_init(void) |
||||||
|
{ |
||||||
|
SystemSettings.visible_vcom = true; |
||||||
|
SystemSettings.editable = false; // This will be loaded in platform init based on the LOCK pin
|
||||||
|
SystemSettings.modified = false; |
||||||
|
} |
||||||
|
|
||||||
|
// to binary
|
||||||
|
void systemsettings_save(PayloadBuilder *pb) |
||||||
|
{ |
||||||
|
pb_char(pb, 'S'); |
||||||
|
pb_bool(pb, SystemSettings.visible_vcom); |
||||||
|
} |
||||||
|
|
||||||
|
// from binary
|
||||||
|
bool systemsettings_load(PayloadParser *pp) |
||||||
|
{ |
||||||
|
if (pp_char(pp) != 'S') return false; |
||||||
|
SystemSettings.visible_vcom = pp_bool(pp); |
||||||
|
|
||||||
|
return pp->ok; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write system settings to INI (without section) |
||||||
|
*/ |
||||||
|
void systemsettings_write_ini(IniWriter *iw) |
||||||
|
{ |
||||||
|
iw_section(iw, "SYSTEM"); |
||||||
|
iw_comment(iw, "Expose the comm. channel as a virtual comport (Y, N)"); |
||||||
|
iw_entry(iw, "expose_vcom", str_yn(SystemSettings.visible_vcom)); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Load system settings from INI kv pair |
||||||
|
*/ |
||||||
|
bool systemsettings_read_ini(const char *restrict key, const char *restrict value) |
||||||
|
{ |
||||||
|
bool suc = true; |
||||||
|
if (streq(key, "expose_vcom")) { |
||||||
|
bool yn = str_parse_yn(value, &suc); |
||||||
|
if (suc) SystemSettings.visible_vcom = yn; |
||||||
|
} |
||||||
|
|
||||||
|
return suc; |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/02.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_SYSTEM_SETTINGS_H |
||||||
|
#define GEX_SYSTEM_SETTINGS_H |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "utils/ini_writer.h" |
||||||
|
#include "utils/payload_parser.h" |
||||||
|
#include "utils/payload_builder.h" |
||||||
|
|
||||||
|
struct system_settings { |
||||||
|
bool visible_vcom; |
||||||
|
|
||||||
|
// Support flags put here for scoping, but not atcually part of the persistent settings
|
||||||
|
volatile bool editable; //!< True if we booted with the LOCK jumper removed
|
||||||
|
volatile bool modified; //!< True if user did any change to the settings (checked when the LOCK jumper is replaced)
|
||||||
|
}; |
||||||
|
|
||||||
|
extern struct system_settings SystemSettings; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Load defaults |
||||||
|
*/ |
||||||
|
void systemsettings_init(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Write system settings to the pack context |
||||||
|
*/ |
||||||
|
void systemsettings_save(PayloadBuilder *pb); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Load system settings from the unpack context |
||||||
|
*/ |
||||||
|
bool systemsettings_load(PayloadParser *pp); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Write system settings to INI |
||||||
|
*/ |
||||||
|
void systemsettings_write_ini(IniWriter *iw); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Load system settings from INI kv pair |
||||||
|
* |
||||||
|
* @return true on success |
||||||
|
*/ |
||||||
|
bool systemsettings_read_ini(const char *restrict key, const char *restrict value); |
||||||
|
|
||||||
|
#endif //GEX_SYSTEM_SETTINGS_H
|
@ -0,0 +1,47 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "unit.h" |
||||||
|
#include "resources.h" |
||||||
|
|
||||||
|
// Abort partly inited unit
|
||||||
|
void clean_failed_unit(Unit *unit) |
||||||
|
{ |
||||||
|
if (unit == NULL) return; |
||||||
|
|
||||||
|
dbg("!! Init of [%s] failed!", unit->name); |
||||||
|
|
||||||
|
// Free if it looks like it might've been allocated
|
||||||
|
if (isDynAlloc(unit->data)) { |
||||||
|
dbg("Freeing allocated unit data"); |
||||||
|
free(unit->data); |
||||||
|
unit->data = NULL; |
||||||
|
} |
||||||
|
if (isDynAlloc(unit->name)) { |
||||||
|
dbg("Freeing allocated name"); |
||||||
|
free((void *) unit->name); |
||||||
|
unit->name = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
dbg("Releasing any held resources"); |
||||||
|
// Release any already claimed resources
|
||||||
|
rsc_teardown(unit); |
||||||
|
} |
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
// system unit is used to claim peripherals on behalf of the system (e.g. HAL tick source)
|
||||||
|
Unit UNIT_SYSTEM = { |
||||||
|
.name = "SYSTEM" |
||||||
|
}; |
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
// platform unit is used to claim peripherals not present on the current platform
|
||||||
|
Unit UNIT_PLATFORM = { |
||||||
|
.name = "PLATFORM" |
||||||
|
}; |
||||||
|
|
||||||
|
// ----------------------------------------------------
|
@ -0,0 +1,118 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_UNIT_H |
||||||
|
#define GEX_UNIT_H |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include <TinyFrame.h> |
||||||
|
#include "utils/ini_writer.h" |
||||||
|
#include "utils/payload_builder.h" |
||||||
|
#include "utils/payload_parser.h" |
||||||
|
|
||||||
|
typedef struct unit Unit; |
||||||
|
typedef struct unit_driver UnitDriver; |
||||||
|
|
||||||
|
struct unit { |
||||||
|
const UnitDriver *driver; |
||||||
|
|
||||||
|
/** Unit name (used in error messages) */ |
||||||
|
const char *name; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage for arbitrary unit data (allocated in 'preInit' and freed in 'deInit' or when init/load fails) |
||||||
|
*/ |
||||||
|
void *data; |
||||||
|
|
||||||
|
/** Unit init status */ |
||||||
|
error_t status; |
||||||
|
|
||||||
|
/** Unit call sign for messages */ |
||||||
|
uint8_t callsign; |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit instance - statically or dynamically allocated (depends whether it's system or user unit) |
||||||
|
*/ |
||||||
|
struct unit_driver { |
||||||
|
/** Driver ID */ |
||||||
|
const char *name; |
||||||
|
|
||||||
|
/** Unit type description (for use in comments) */ |
||||||
|
const char *description; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-init: allocate data object, init defaults |
||||||
|
*/ |
||||||
|
bool (*preInit)(Unit *unit); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Load settings from binary storage, parse and store them in the data object. |
||||||
|
* Don't do any validation, that's left for the init() function |
||||||
|
* |
||||||
|
* @param pp - parser |
||||||
|
*/ |
||||||
|
void (*cfgLoadBinary)(Unit *unit, PayloadParser *pp); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Write settings to binary storage. |
||||||
|
* |
||||||
|
* @param pb - builder |
||||||
|
*/ |
||||||
|
void (*cfgWriteBinary)(Unit *unit, PayloadBuilder *pb); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Load settings from a INI file. |
||||||
|
* Name has already been parsed and assigned. |
||||||
|
* This function is called repeatedly as kv-pairs are encountered in the stream. |
||||||
|
* |
||||||
|
* @param key - key from the INI file |
||||||
|
* @param value - value from the ini file; strings have already removed quotes and replaced escape sequences with ASCII as needed |
||||||
|
*/ |
||||||
|
bool (*cfgLoadIni)(Unit *unit, const char *key, const char *value); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Export settings to a INI file. |
||||||
|
* Capacity will likely be 512 bytes, do not waste space! |
||||||
|
* |
||||||
|
* @param buffer - destination buffer |
||||||
|
* @param capacity - buffer size |
||||||
|
* @return nubmer of bytes used |
||||||
|
*/ |
||||||
|
void (*cfgWriteIni)(Unit *unit, IniWriter *iw); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Finalize the init sequence, validate settings, enable peripherals and prepare for operation |
||||||
|
*/ |
||||||
|
bool (*init)(Unit *unit); |
||||||
|
|
||||||
|
/**
|
||||||
|
* De-initialize the unit: de-init peripheral, free resources, free data object... |
||||||
|
* This is called when disabling all units in order to reload new config. |
||||||
|
*/ |
||||||
|
void (*deInit)(Unit *unit); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an incoming request. Return true if command was OK. |
||||||
|
*/ |
||||||
|
bool (*handleRequest)(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp); |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
* De-init a partially initialized unit (before 'init' succeeds) |
||||||
|
* This releases all held resources and frees the *data, |
||||||
|
* if it looks like it has been dynamically allocated. |
||||||
|
* |
||||||
|
* Does NOT free the unit struct itself |
||||||
|
* |
||||||
|
* @param unit - unit to discard |
||||||
|
*/ |
||||||
|
void clean_failed_unit(Unit *unit); |
||||||
|
|
||||||
|
/** Marks peripherals claimed by the system */ |
||||||
|
extern Unit UNIT_SYSTEM; |
||||||
|
/** Marks peripherals not available on the platform */ |
||||||
|
extern Unit UNIT_PLATFORM; |
||||||
|
|
||||||
|
#endif //GEX_UNIT_H
|
@ -0,0 +1,12 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/09.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "unit.h" |
||||||
|
#include "pin_utils.h" |
||||||
|
#include "resources.h" |
||||||
|
#include "utils/str_utils.h" |
||||||
|
#include "utils/malloc_safe.h" |
||||||
|
#include "payload_builder.h" |
||||||
|
#include "payload_parser.h" |
@ -0,0 +1,579 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "utils/avrlibc.h" |
||||||
|
#include "comm/messages.h" |
||||||
|
#include "utils/ini_writer.h" |
||||||
|
#include "utils/str_utils.h" |
||||||
|
#include "utils/malloc_safe.h" |
||||||
|
#include "unit_registry.h" |
||||||
|
#include "resources.h" |
||||||
|
|
||||||
|
// ** Unit repository **
|
||||||
|
|
||||||
|
typedef struct ureg_entry UregEntry; |
||||||
|
typedef struct ulist_entry UlistEntry; |
||||||
|
|
||||||
|
struct ureg_entry { |
||||||
|
const UnitDriver *driver; |
||||||
|
UregEntry *next; |
||||||
|
}; |
||||||
|
|
||||||
|
UregEntry *ureg_head = NULL; |
||||||
|
UregEntry *ureg_tail = NULL; |
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
|
struct ulist_entry { |
||||||
|
Unit unit; |
||||||
|
UlistEntry *next; |
||||||
|
}; |
||||||
|
|
||||||
|
UlistEntry *ulist_head = NULL; |
||||||
|
UlistEntry *ulist_tail = NULL; |
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
|
void ureg_add_type(const UnitDriver *driver) |
||||||
|
{ |
||||||
|
assert_param(driver != NULL); |
||||||
|
assert_param(driver->description != NULL); |
||||||
|
assert_param(driver->name != NULL); |
||||||
|
assert_param(driver->preInit != NULL); |
||||||
|
assert_param(driver->cfgLoadBinary != NULL); |
||||||
|
assert_param(driver->cfgLoadIni != NULL); |
||||||
|
assert_param(driver->cfgWriteBinary != NULL); |
||||||
|
assert_param(driver->cfgWriteIni != NULL); |
||||||
|
assert_param(driver->init != NULL); |
||||||
|
assert_param(driver->deInit != NULL); |
||||||
|
assert_param(driver->handleRequest != NULL); |
||||||
|
|
||||||
|
UregEntry *re = malloc_s(sizeof(UregEntry)); |
||||||
|
re->driver = driver; |
||||||
|
re->next = NULL; |
||||||
|
|
||||||
|
if (ureg_head == NULL) { |
||||||
|
ureg_head = re; |
||||||
|
} else { |
||||||
|
ureg_tail->next = re; |
||||||
|
} |
||||||
|
ureg_tail = re; |
||||||
|
} |
||||||
|
|
||||||
|
/** Free unit in a list entry (do not free the list entry itself!) */ |
||||||
|
static void free_le_unit(UlistEntry *le) |
||||||
|
{ |
||||||
|
Unit *const pUnit = &le->unit; |
||||||
|
|
||||||
|
pUnit->driver->deInit(pUnit); |
||||||
|
// Name is not expected to be freed by the deInit() function
|
||||||
|
// - was alloc'd in the settings load loop
|
||||||
|
if (isDynAlloc(pUnit->name)) { |
||||||
|
dbg("Freeing allocated name"); |
||||||
|
free((void *) pUnit->name); |
||||||
|
pUnit->name = NULL; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Remove a unit and update links appropriately */ |
||||||
|
static void remove_unit_from_list(UlistEntry *restrict le, UlistEntry *restrict parent) |
||||||
|
{ |
||||||
|
if (parent == NULL) { |
||||||
|
ulist_head = le->next; |
||||||
|
} else { |
||||||
|
parent->next = le->next; |
||||||
|
} |
||||||
|
|
||||||
|
// Fix tail potentially pointing to the removed entry
|
||||||
|
if (ulist_tail == le) { |
||||||
|
ulist_tail = parent; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Add unit to the list, updating references as needed */ |
||||||
|
static void add_unit_to_list(UlistEntry *le) |
||||||
|
{ |
||||||
|
// Attach to the list
|
||||||
|
if (ulist_head == NULL) { |
||||||
|
ulist_head = le; |
||||||
|
} else { |
||||||
|
ulist_tail->next = le; |
||||||
|
} |
||||||
|
ulist_tail = le; |
||||||
|
} |
||||||
|
|
||||||
|
/** Find a unit in the list */ |
||||||
|
static UlistEntry *find_unit(const Unit *unit, UlistEntry **pParent) |
||||||
|
{ |
||||||
|
UlistEntry *le = ulist_head; |
||||||
|
UlistEntry *parent = NULL; |
||||||
|
while (le != NULL) { |
||||||
|
if (&le->unit == unit) { |
||||||
|
if (pParent != NULL) { |
||||||
|
*pParent = parent; |
||||||
|
} |
||||||
|
return le; |
||||||
|
} |
||||||
|
|
||||||
|
parent = le; |
||||||
|
le = le->next; |
||||||
|
} |
||||||
|
|
||||||
|
dbg("!! Unit was not found in registry"); |
||||||
|
*pParent = NULL; |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
// create a unit instance (not yet loading or initing - just pre-init)
|
||||||
|
Unit *ureg_instantiate(const char *driver_name) |
||||||
|
{ |
||||||
|
bool suc = true; |
||||||
|
|
||||||
|
dbg("Creating unit of type %s", driver_name); |
||||||
|
|
||||||
|
// Find type in the repository
|
||||||
|
UregEntry *re = ureg_head; |
||||||
|
while (re != NULL) { |
||||||
|
if (streq(re->driver->name, driver_name)) { |
||||||
|
// Create new list entry
|
||||||
|
UlistEntry *le = malloc_ck(sizeof(UlistEntry), &suc); |
||||||
|
CHECK_SUC(); |
||||||
|
|
||||||
|
le->next = NULL; |
||||||
|
|
||||||
|
Unit *pUnit = &le->unit; |
||||||
|
pUnit->driver = re->driver; |
||||||
|
pUnit->status = E_LOADING; |
||||||
|
pUnit->data = NULL; |
||||||
|
pUnit->callsign = 0; |
||||||
|
|
||||||
|
suc = pUnit->driver->preInit(pUnit); |
||||||
|
if (!suc) { |
||||||
|
// tear down what we already allocated and abort
|
||||||
|
|
||||||
|
// If it failed this early, the only plausible explanation is failed malloc,
|
||||||
|
// in which case the data structure is not populated and keeping the
|
||||||
|
// broken unit doesn't serve any purpose. Just ditch it...
|
||||||
|
|
||||||
|
dbg("!! Unit failed to pre-init!"); |
||||||
|
clean_failed_unit(pUnit); |
||||||
|
free(le); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
add_unit_to_list(le); |
||||||
|
return pUnit; |
||||||
|
} |
||||||
|
re = re->next; |
||||||
|
} |
||||||
|
|
||||||
|
dbg("!! Did not find unit type %s", driver_name); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
// remove before init()
|
||||||
|
void ureg_clean_failed(Unit *unit) |
||||||
|
{ |
||||||
|
dbg("Cleaning failed unit from registry"); |
||||||
|
|
||||||
|
UlistEntry *le; |
||||||
|
UlistEntry *parent; |
||||||
|
le = find_unit(unit, &parent); |
||||||
|
if (!le) return; |
||||||
|
|
||||||
|
clean_failed_unit(&le->unit); |
||||||
|
|
||||||
|
remove_unit_from_list(le, parent); |
||||||
|
|
||||||
|
free(le); |
||||||
|
} |
||||||
|
|
||||||
|
// remove after successful init()
|
||||||
|
void ureg_remove_unit(Unit *unit) |
||||||
|
{ |
||||||
|
dbg("Cleaning & removing unit from registry"); |
||||||
|
|
||||||
|
UlistEntry *le; |
||||||
|
UlistEntry *parent; |
||||||
|
le = find_unit(unit, &parent); |
||||||
|
if (!le) return; |
||||||
|
|
||||||
|
free_le_unit(le); |
||||||
|
|
||||||
|
remove_unit_from_list(le, parent); |
||||||
|
free(le); |
||||||
|
} |
||||||
|
|
||||||
|
void ureg_save_units(PayloadBuilder *pb) |
||||||
|
{ |
||||||
|
assert_param(pb->ok); |
||||||
|
|
||||||
|
uint32_t count = ureg_get_num_units(); |
||||||
|
|
||||||
|
pb_char(pb, 'U'); |
||||||
|
pb_u16(pb, (uint16_t) count); |
||||||
|
|
||||||
|
UlistEntry *le = ulist_head; |
||||||
|
while (le != NULL) { |
||||||
|
Unit *const pUnit = &le->unit; |
||||||
|
pb_char(pb, 'u'); |
||||||
|
pb_string(pb, pUnit->driver->name); |
||||||
|
pb_string(pb, pUnit->name); |
||||||
|
pb_u8(pb, pUnit->callsign); |
||||||
|
|
||||||
|
// Now all the rest, unit-specific
|
||||||
|
pUnit->driver->cfgWriteBinary(pUnit, pb); |
||||||
|
assert_param(pb->ok); |
||||||
|
le = le->next; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool ureg_load_units(PayloadParser *pp) |
||||||
|
{ |
||||||
|
bool suc; |
||||||
|
char typebuf[16]; |
||||||
|
|
||||||
|
assert_param(pp->ok); |
||||||
|
|
||||||
|
if (pp_char(pp) != 'U') return false; |
||||||
|
uint16_t unit_count = pp_u16(pp); |
||||||
|
|
||||||
|
for (uint32_t j = 0; j < unit_count; j++) { |
||||||
|
// We're now unpacking a single unit
|
||||||
|
|
||||||
|
// Marker that this is a unit - it could get out of alignment if structure changed
|
||||||
|
if (pp_char(pp) != 'u') return false; |
||||||
|
|
||||||
|
// TYPE
|
||||||
|
pp_string(pp, typebuf, 16); |
||||||
|
Unit *const pUnit = ureg_instantiate(typebuf); |
||||||
|
assert_param(pUnit); |
||||||
|
|
||||||
|
// NAME
|
||||||
|
pp_string(pp, typebuf, 16); |
||||||
|
pUnit->name = strdup(typebuf); |
||||||
|
assert_param(pUnit->name); |
||||||
|
|
||||||
|
// CALLSIGN
|
||||||
|
pUnit->callsign = pp_u8(pp); |
||||||
|
assert_param(pUnit->callsign != 0); |
||||||
|
|
||||||
|
// Load the rest of the unit
|
||||||
|
pUnit->driver->cfgLoadBinary(pUnit, pp); |
||||||
|
assert_param(pp->ok); |
||||||
|
|
||||||
|
suc = pUnit->driver->init(pUnit); // finalize the load and init the unit
|
||||||
|
if (pUnit->status == E_LOADING) { |
||||||
|
pUnit->status = suc ? E_SUCCESS : E_BAD_CONFIG; |
||||||
|
} |
||||||
|
|
||||||
|
// XXX we want to keep the failed unit to preserve settings and for error reporting
|
||||||
|
// if (!suc) {
|
||||||
|
// // Discard, remove from registry
|
||||||
|
// ureg_clean_failed(unit);
|
||||||
|
// }
|
||||||
|
} |
||||||
|
|
||||||
|
return pp->ok; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void ureg_remove_all_units(void) |
||||||
|
{ |
||||||
|
UlistEntry *le = ulist_head; |
||||||
|
UlistEntry *next; |
||||||
|
while (le != NULL) { |
||||||
|
next = le->next; |
||||||
|
|
||||||
|
free_le_unit(le); |
||||||
|
free(le); |
||||||
|
|
||||||
|
le = next; |
||||||
|
} |
||||||
|
|
||||||
|
ulist_head = ulist_tail = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool ureg_instantiate_by_ini(const char *restrict driver_name, const char *restrict names) |
||||||
|
{ |
||||||
|
UregEntry *re = ureg_head; |
||||||
|
while (re != NULL) { |
||||||
|
if (streq(re->driver->name, driver_name)) { |
||||||
|
const char *p = names; |
||||||
|
while (p != NULL) { // we use this to indicate we're done
|
||||||
|
// skip leading whitespace (assume there's never whitespace before a comma)
|
||||||
|
while (*p == ' ' || *p == '\t') p++; |
||||||
|
if (*p == 0) break; // out of characters
|
||||||
|
|
||||||
|
const char *delim = strchr(p, ','); |
||||||
|
char *name = NULL; |
||||||
|
if (delim != NULL) { |
||||||
|
// not last
|
||||||
|
name = strndup(p, delim - p); |
||||||
|
p = delim + 1; |
||||||
|
} else { |
||||||
|
// last name
|
||||||
|
name = strdup(p); |
||||||
|
p = NULL; // quit after this loop ends
|
||||||
|
} |
||||||
|
assert_param(name); |
||||||
|
Unit *pUnit = ureg_instantiate(driver_name); |
||||||
|
if (!pUnit) { |
||||||
|
free(name); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
pUnit->name = name; |
||||||
|
|
||||||
|
// don't init yet - leave that for when we're done with the INI
|
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
re = re->next; |
||||||
|
} |
||||||
|
|
||||||
|
dbg("! ureg instantiate - bad type"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
bool ureg_read_unit_ini(const char *restrict name, |
||||||
|
const char *restrict key, |
||||||
|
const char *restrict value) |
||||||
|
{ |
||||||
|
UlistEntry *li = ulist_head; |
||||||
|
while (li != NULL) { |
||||||
|
if (streq(li->unit.name, name)) { |
||||||
|
Unit *const pUnit = &li->unit; |
||||||
|
|
||||||
|
if (streq(key, "CALLSIGN")) { |
||||||
|
// handled separately from unit data
|
||||||
|
pUnit->callsign = (uint8_t) avr_atoi(value); |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
return pUnit->driver->cfgLoadIni(pUnit, key, value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
li = li->next; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
bool ureg_finalize_all_init(void) |
||||||
|
{ |
||||||
|
dbg("Finalizing units init..."); |
||||||
|
bool suc = true; |
||||||
|
UlistEntry *li = ulist_head; |
||||||
|
uint8_t callsign = 1; |
||||||
|
while (li != NULL) { |
||||||
|
Unit *const pUnit = &li->unit; |
||||||
|
|
||||||
|
bool s = pUnit->driver->init(pUnit); |
||||||
|
if (!s) { |
||||||
|
dbg("!!!! error initing unit %s", pUnit->name); |
||||||
|
if (pUnit->status == E_LOADING) { |
||||||
|
// assume it's a config error if not otherwise specified
|
||||||
|
pUnit->status = E_BAD_CONFIG; |
||||||
|
} |
||||||
|
} else { |
||||||
|
pUnit->status = E_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
// try to assign unique callsigns
|
||||||
|
if (pUnit->callsign == 0) { |
||||||
|
pUnit->callsign = callsign++; |
||||||
|
} else { |
||||||
|
if (pUnit->callsign >= callsign) { |
||||||
|
callsign = (uint8_t) (pUnit->callsign + 1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
suc &= s; |
||||||
|
li = li->next; |
||||||
|
} |
||||||
|
return suc; |
||||||
|
} |
||||||
|
|
||||||
|
static void export_unit_do(UlistEntry *li, IniWriter *iw) |
||||||
|
{ |
||||||
|
Unit *const pUnit = &li->unit; |
||||||
|
|
||||||
|
iw_section(iw, "%s:%s", pUnit->driver->name, pUnit->name); |
||||||
|
iw_comment(iw, ">> Status: %s", error_get_string(pUnit->status)); |
||||||
|
iw_newline(iw); |
||||||
|
iw_comment(iw, "Address for control messages (1-255)"); |
||||||
|
iw_entry(iw, "CALLSIGN", "%d", pUnit->callsign); |
||||||
|
|
||||||
|
pUnit->driver->cfgWriteIni(pUnit, iw); |
||||||
|
iw_newline(iw); |
||||||
|
} |
||||||
|
|
||||||
|
// unit to INI
|
||||||
|
void ureg_export_unit(uint32_t index, IniWriter *iw) |
||||||
|
{ |
||||||
|
UlistEntry *li = ulist_head; |
||||||
|
uint32_t count = 0; |
||||||
|
while (li != NULL) { |
||||||
|
if (count == index) { |
||||||
|
export_unit_do(li, iw); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
count++; |
||||||
|
li = li->next; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// unit to INI
|
||||||
|
void ureg_export_combined(IniWriter *iw) |
||||||
|
{ |
||||||
|
UlistEntry *li; |
||||||
|
UregEntry *re; |
||||||
|
|
||||||
|
// Unit list
|
||||||
|
iw_section(iw, "UNITS"); |
||||||
|
iw_comment(iw, "Here is a list of all unit types supported by the current firmware."); |
||||||
|
iw_comment(iw, "To manage units, simply add/remove their comma-separated names next to"); |
||||||
|
iw_comment(iw, "the desired unit type. Reload the file and the corresponding unit"); |
||||||
|
iw_comment(iw, "sections should appear below, ready to configure."); |
||||||
|
|
||||||
|
// This could certainly be done in some more efficient way ...
|
||||||
|
re = ureg_head; |
||||||
|
while (re != NULL) { |
||||||
|
// Should produce something like:
|
||||||
|
|
||||||
|
// # Description string here
|
||||||
|
// TYPE_ID=NAME1,NAME2
|
||||||
|
//
|
||||||
|
|
||||||
|
const UnitDriver *const pDriver = re->driver; |
||||||
|
|
||||||
|
iw_newline(iw); |
||||||
|
iw_comment(iw, pDriver->description); |
||||||
|
iw_string(iw, pDriver->name); |
||||||
|
iw_string(iw, "="); |
||||||
|
|
||||||
|
li = ulist_head; |
||||||
|
uint32_t count = 0; |
||||||
|
while (li != NULL) { |
||||||
|
Unit *const pUnit = &li->unit; |
||||||
|
if (streq(pUnit->driver->name, pDriver->name)) { |
||||||
|
if (count > 0) iw_string(iw, ","); |
||||||
|
iw_string(iw, pUnit->name); |
||||||
|
count++; |
||||||
|
} |
||||||
|
li = li->next; |
||||||
|
} |
||||||
|
re = re->next; |
||||||
|
iw_newline(iw); |
||||||
|
} |
||||||
|
iw_newline(iw); // space before the unit sections
|
||||||
|
|
||||||
|
// Now we dump all the units
|
||||||
|
li = ulist_head; |
||||||
|
while (li != NULL) { |
||||||
|
export_unit_do(li, iw); |
||||||
|
li = li->next; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// count units
|
||||||
|
uint32_t ureg_get_num_units(void) |
||||||
|
{ |
||||||
|
// TODO keep this in a variable
|
||||||
|
UlistEntry *li = ulist_head; |
||||||
|
uint32_t count = 0; |
||||||
|
while (li != NULL) { |
||||||
|
count++; |
||||||
|
li = li->next; |
||||||
|
} |
||||||
|
|
||||||
|
return count; |
||||||
|
} |
||||||
|
|
||||||
|
static void job_nosuch_unit(Job *job) |
||||||
|
{ |
||||||
|
tf_respond_snprintf(MSG_ERROR, job->frame_id, "NO UNIT @ %"PRIu32, job->d32); |
||||||
|
} |
||||||
|
|
||||||
|
/** Deliver message to it's destination unit */ |
||||||
|
void ureg_deliver_unit_request(TF_Msg *msg) |
||||||
|
{ |
||||||
|
PayloadParser pp = pp_start(msg->data, msg->len, NULL); |
||||||
|
uint8_t callsign = pp_u8(&pp); |
||||||
|
uint8_t command = pp_u8(&pp); |
||||||
|
|
||||||
|
// highest bit indicates user wants an extra confirmation on success
|
||||||
|
bool confirmed = (bool) (command & 0x80); |
||||||
|
command &= 0x7F; |
||||||
|
|
||||||
|
if (!pp.ok) { dbg("!! pp not OK!"); } |
||||||
|
|
||||||
|
if (callsign == 0 || !pp.ok) { |
||||||
|
sched_respond_malformed_cmd(msg->frame_id); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
UlistEntry *li = ulist_head; |
||||||
|
while (li != NULL) { |
||||||
|
Unit *const pUnit = &li->unit; |
||||||
|
if (pUnit->callsign == callsign) { |
||||||
|
bool ok = pUnit->driver->handleRequest(pUnit, msg->frame_id, command, &pp); |
||||||
|
if (ok && confirmed) { |
||||||
|
sched_respond_suc(msg->frame_id); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
li = li->next; |
||||||
|
} |
||||||
|
|
||||||
|
// Not found
|
||||||
|
Job job = { |
||||||
|
.cb = job_nosuch_unit, |
||||||
|
.frame_id = msg->frame_id, |
||||||
|
.d32 = callsign |
||||||
|
}; |
||||||
|
scheduleJob(&job, TSK_SCHED_LOW); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void ureg_report_active_units(TF_ID frame_id) |
||||||
|
{ |
||||||
|
// count bytes needed
|
||||||
|
uint32_t needed = 1; //
|
||||||
|
|
||||||
|
UlistEntry *li = ulist_head; |
||||||
|
uint32_t count = 0; |
||||||
|
while (li != NULL) { |
||||||
|
count++; |
||||||
|
needed += strlen(li->unit.name)+1; |
||||||
|
li = li->next; |
||||||
|
} |
||||||
|
needed += count; |
||||||
|
|
||||||
|
bool suc = true; |
||||||
|
uint8_t *buff = malloc_ck(needed, &suc); |
||||||
|
if (!suc) { tf_respond_str(MSG_ERROR, frame_id, "OUT OF MEMORY"); return; } |
||||||
|
|
||||||
|
{ |
||||||
|
PayloadBuilder pb = pb_start(buff, needed, NULL); |
||||||
|
pb_u8(&pb, (uint8_t) count); // assume we don't have more than 255
|
||||||
|
|
||||||
|
li = ulist_head; |
||||||
|
while (li != NULL) { |
||||||
|
pb_u8(&pb, li->unit.callsign); |
||||||
|
pb_string(&pb, li->unit.name); |
||||||
|
li = li->next; |
||||||
|
} |
||||||
|
|
||||||
|
assert_param(pb.ok); |
||||||
|
|
||||||
|
tf_respond_buf(MSG_SUCCESS, frame_id, buff, needed); |
||||||
|
} |
||||||
|
|
||||||
|
free(buff); |
||||||
|
} |
@ -0,0 +1,128 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_UNIT_REGISTRY_H |
||||||
|
#define GEX_UNIT_REGISTRY_H |
||||||
|
|
||||||
|
#include <TinyFrame/TinyFrame.h> |
||||||
|
#include "platform.h" |
||||||
|
#include "unit.h" |
||||||
|
|
||||||
|
/**
|
||||||
|
* Add instantiable unit type to the registry |
||||||
|
* |
||||||
|
* @param driver - unit template, will be shallowly cloned for new instances |
||||||
|
*/ |
||||||
|
void ureg_add_type(const UnitDriver *driver); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of a unit type. The unit is added to the unit list. |
||||||
|
* |
||||||
|
* @param driver_name - unit type, same as given when registering the type. CAN BE ON STACK! Not stored. |
||||||
|
* @return the unit, or NULL on failure |
||||||
|
*/ |
||||||
|
Unit *ureg_instantiate(const char *driver_name); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean a unit previously obtained by 'ureg_instantiate' that |
||||||
|
* failed to properly load or init. This tears it down and removes it from the unit list. |
||||||
|
* |
||||||
|
* @param unit - unit to remove |
||||||
|
*/ |
||||||
|
void ureg_clean_failed(Unit *unit); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely delete a unit instance (releasing memory and reosurces, removing it from the list) |
||||||
|
* |
||||||
|
* @param unit - unit to remove |
||||||
|
*/ |
||||||
|
void ureg_remove_unit(Unit *unit); |
||||||
|
|
||||||
|
/**
|
||||||
|
* De-init and remove all units |
||||||
|
*/ |
||||||
|
void ureg_remove_all_units(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Save all units to a binary buffer |
||||||
|
* @param ctx |
||||||
|
*/ |
||||||
|
void ureg_save_units(PayloadBuilder *pb); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Load units from the binary format |
||||||
|
* @param ctx |
||||||
|
*/ |
||||||
|
bool ureg_load_units(PayloadParser *pp); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Export unit as INI to a buffer. |
||||||
|
* |
||||||
|
* @param index - unit index |
||||||
|
* @param iw - iniwriter instance to use |
||||||
|
* @return real number of bytes used (should end with a newline) |
||||||
|
*/ |
||||||
|
void ureg_export_unit(uint32_t index, IniWriter *iw); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Export everything to INI |
||||||
|
* |
||||||
|
* @param iw |
||||||
|
*/ |
||||||
|
void ureg_export_combined(IniWriter *iw); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Get number of instantiated units |
||||||
|
* |
||||||
|
* @return nr of units |
||||||
|
*/ |
||||||
|
uint32_t ureg_get_num_units(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate a unit by INI |
||||||
|
* |
||||||
|
* This is called for lines inside the [UNITS] section, e.g. PIN=LED1, BUTTON |
||||||
|
* |
||||||
|
* @param driver_name - unit type ID |
||||||
|
* @param names - names string, comma separated (may have whitespace after commas) |
||||||
|
* @return all OK |
||||||
|
*/ |
||||||
|
bool ureg_instantiate_by_ini(const char *restrict driver_name, const char *restrict names); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a single INI line to a unit. |
||||||
|
* |
||||||
|
* @param name - unit name (for look-up) |
||||||
|
* @param key - property key |
||||||
|
* @param value - value to set as string |
||||||
|
* @return success |
||||||
|
*/ |
||||||
|
bool ureg_read_unit_ini(const char *restrict name, |
||||||
|
const char *restrict key, |
||||||
|
const char *restrict value); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Run init() for all unit instances. |
||||||
|
* |
||||||
|
* @return all OK |
||||||
|
*/ |
||||||
|
bool ureg_finalize_all_init(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Deliver a TinyFrame message to it's designed destination. |
||||||
|
* Unit is identified by the data first byte which is the "call sign" |
||||||
|
* |
||||||
|
* @param msg - message to deliver |
||||||
|
* @return true if delivered |
||||||
|
*/ |
||||||
|
void ureg_deliver_unit_request(TF_Msg *msg); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Report all unit callsigns and names to TF master |
||||||
|
* |
||||||
|
* @param frame_id - original message ID |
||||||
|
*/ |
||||||
|
void ureg_report_active_units(TF_ID frame_id); |
||||||
|
|
||||||
|
#endif //GEX_UNIT_REGISTRY_H
|
@ -0,0 +1,52 @@ |
|||||||
|
GEX_SRC_DIR = \
|
||||||
|
User \ |
||||||
|
User/utils \ |
||||||
|
User/USB \ |
||||||
|
User/comm \ |
||||||
|
User/framework \ |
||||||
|
User/platform \ |
||||||
|
User/units \ |
||||||
|
User/units/system \ |
||||||
|
User/units/neopixel \ |
||||||
|
User/units/pin \ |
||||||
|
User/TinyFrame \ |
||||||
|
User/CWPack \ |
||||||
|
User/USB/MSC_CDC \ |
||||||
|
User/vfs |
||||||
|
|
||||||
|
GEX_INCLUDES = \
|
||||||
|
-IUser \ |
||||||
|
-IUser/USB \ |
||||||
|
-IUser/USB/MSC_CDC \ |
||||||
|
-IUser/TinyFrame \ |
||||||
|
-IUser/vfs \ |
||||||
|
-IUser/utils \ |
||||||
|
-IUser/units \ |
||||||
|
-IUser/units/system \ |
||||||
|
-IUser/units/neopixel \ |
||||||
|
-IUser/units/pin \ |
||||||
|
-IUser/framework \ |
||||||
|
-IUser/platform |
||||||
|
|
||||||
|
GEX_CFLAGS = -D__weak="__attribute__((weak))" -D__packed="__attribute__((__packed__))"
|
||||||
|
GEX_CFLAGS += -std=gnu99 -Wfatal-errors
|
||||||
|
GEX_CFLAGS += -Wall -Wextra -Wshadow
|
||||||
|
GEX_CFLAGS += -Wwrite-strings -Wold-style-definition -Winline -Wno-missing-noreturn -Wstrict-prototypes -Wreturn-type
|
||||||
|
GEX_CFLAGS += -Wredundant-decls -Wfloat-equal -Wsign-compare
|
||||||
|
GEX_CFLAGS += -fno-common -ffunction-sections -fdata-sections -Wno-unused-function
|
||||||
|
GEX_CFLAGS += -MD -Wno-format-zero-length -Wno-redundant-decls -Wno-unused-parameter
|
||||||
|
GEX_CFLAGS += -Wno-discarded-qualifiers -Wno-unused-variable -Wno-inline
|
||||||
|
GEX_CFLAGS += -Wno-float-equal -Wno-implicit-fallthrough -Wno-strict-aliasing
|
||||||
|
GEX_CFLAGS += -fmerge-constants -fmerge-all-constants
|
||||||
|
GEX_CFLAGS += -fno-exceptions -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast -finline-small-functions -findirect-inlining
|
||||||
|
|
||||||
|
GEX_CDEFS = \
|
||||||
|
-D__weak="__attribute__((weak))" \
|
||||||
|
-D__packed="__attribute__((__packed__))" \
|
||||||
|
-DUSE_FULL_LL_DRIVER \ |
||||||
|
-DUSE_FULL_ASSERT=1 \
|
||||||
|
-DVERBOSE_ASSERT=1 \
|
||||||
|
-DDEBUG_VFS=0 \
|
||||||
|
-DVERBOSE_HARDFAULT=1 \
|
||||||
|
-DUSE_STACK_MONITOR=1 \
|
||||||
|
-DUSE_DEBUG_UART=1
|
@ -0,0 +1,36 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "USB/usb_device.h" |
||||||
|
#include "TinyFrame.h" |
||||||
|
#include "comm/messages.h" |
||||||
|
#include "platform/status_led.h" |
||||||
|
#include "platform/debug_uart.h" |
||||||
|
#include "gex_hooks.h" |
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a systick callback for GEX application logic |
||||||
|
*/ |
||||||
|
void GEX_MsTick(void) |
||||||
|
{ |
||||||
|
TF_Tick(comm); |
||||||
|
StatusLed_Tick(); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Early init, even before RTOS starts |
||||||
|
*/ |
||||||
|
void GEX_PreInit(void) |
||||||
|
{ |
||||||
|
DebugUart_PreInit(); |
||||||
|
dbg("\r\n\033[37;1m*** GEX "GEX_VERSION" on "GEX_PLATFORM" ***\033[m"); |
||||||
|
dbg("Build "__DATE__" "__TIME__"\r\n"); |
||||||
|
|
||||||
|
plat_init(); |
||||||
|
|
||||||
|
MX_USB_DEVICE_Init(); |
||||||
|
|
||||||
|
dbg("Starting FreeRTOS..."); |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_GEX_HOOKS_H |
||||||
|
#define GEX_GEX_HOOKS_H |
||||||
|
|
||||||
|
void GEX_MsTick(void); |
||||||
|
void GEX_PreInit(void); |
||||||
|
|
||||||
|
#endif //GEX_GEX_HOOKS_H
|
@ -0,0 +1,64 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "framework/resources.h" |
||||||
|
#include "debug_uart.h" |
||||||
|
#include "plat_compat.h" |
||||||
|
|
||||||
|
#if USE_DEBUG_UART |
||||||
|
|
||||||
|
/** Init the submodule. */ |
||||||
|
void DebugUart_Init(void) |
||||||
|
{ |
||||||
|
// Debug UART
|
||||||
|
bool ok = true; |
||||||
|
ok &= rsc_claim(&UNIT_SYSTEM, R_USART2); |
||||||
|
ok &= rsc_claim(&UNIT_SYSTEM, R_PA2); |
||||||
|
assert_param(ok); |
||||||
|
} |
||||||
|
|
||||||
|
/** Init the hardware peripheral - this is called early in the boot process */ |
||||||
|
void DebugUart_PreInit(void) |
||||||
|
{ |
||||||
|
__HAL_RCC_USART2_CLK_ENABLE(); |
||||||
|
__HAL_RCC_GPIOA_CLK_ENABLE(); |
||||||
|
|
||||||
|
LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_2, LL_GPIO_MODE_ALTERNATE); |
||||||
|
LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_2, LL_GPIO_OUTPUT_PUSHPULL); |
||||||
|
LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_2, LL_GPIO_SPEED_FREQ_HIGH); |
||||||
|
|
||||||
|
// commented out default values
|
||||||
|
// LL_USART_ConfigAsyncMode(USART2);
|
||||||
|
// LL_USART_SetDataWidth(USART2, LL_USART_DATAWIDTH_8B);
|
||||||
|
// LL_USART_SetParity(USART2, LL_USART_PARITY_NONE);
|
||||||
|
// LL_USART_SetStopBitsLength(USART2, LL_USART_STOPBITS_1);
|
||||||
|
// LL_USART_SetHWFlowCtrl(USART2, LL_USART_HWCONTROL_NONE);
|
||||||
|
LL_USART_EnableDirectionTx(USART2); |
||||||
|
LL_USART_SetBaudRate(USART2, SystemCoreClock/2, 115200); // This is not great, let's hope it's like this on all platforms...
|
||||||
|
LL_USART_Enable(USART2); |
||||||
|
} |
||||||
|
|
||||||
|
/** Debug print, used by debug / newlib */ |
||||||
|
ssize_t _write_r(struct _reent *rptr, int fd, const void *buf, size_t len) |
||||||
|
{ |
||||||
|
(void)rptr; |
||||||
|
|
||||||
|
uint8_t *buff = buf; |
||||||
|
|
||||||
|
for (uint32_t i = 0; i < len; i++) { |
||||||
|
while (!LL_USART_IsActiveFlag_TC(USART2)); |
||||||
|
LL_USART_TransmitData8(USART2, *buff++); |
||||||
|
} |
||||||
|
|
||||||
|
return len; |
||||||
|
} |
||||||
|
|
||||||
|
#else |
||||||
|
|
||||||
|
// No-uart variant
|
||||||
|
void DebugUart_Init(void) {} |
||||||
|
ssize_t _write_r(struct _reent *rptr, int fd, const void *buf, size_t len) {} |
||||||
|
|
||||||
|
#endif //USE_DEBUG_UART
|
@ -0,0 +1,11 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_DEBUG_UART_H |
||||||
|
#define GEX_DEBUG_UART_H |
||||||
|
|
||||||
|
void DebugUart_PreInit(void); |
||||||
|
void DebugUart_Init(void); |
||||||
|
|
||||||
|
#endif //GEX_DEBUG_UART_H
|
@ -0,0 +1,86 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <sched_queue.h> |
||||||
|
#include <task_sched.h> |
||||||
|
#include "usbd_core.h" |
||||||
|
#include "USB/usb_device.h" |
||||||
|
|
||||||
|
#include "framework/settings.h" |
||||||
|
#include "framework/resources.h" |
||||||
|
#include "framework/system_settings.h" |
||||||
|
#include "pin_utils.h" |
||||||
|
#include "lock_jumper.h" |
||||||
|
|
||||||
|
static GPIO_TypeDef *lock_periph; |
||||||
|
static uint32_t lock_llpin; |
||||||
|
|
||||||
|
// We use macros LOCK_JUMPER_PORT, LOCK_JUMPER_PIN from plat_compat.h
|
||||||
|
|
||||||
|
/** Init the jumper subsystem */ |
||||||
|
void LockJumper_Init(void) |
||||||
|
{ |
||||||
|
bool suc = true; |
||||||
|
|
||||||
|
// Resolve and claim resource
|
||||||
|
Resource rsc = plat_pin2resource(LOCK_JUMPER_PORT, LOCK_JUMPER_PIN, &suc); |
||||||
|
assert_param(suc); |
||||||
|
|
||||||
|
suc &= rsc_claim(&UNIT_SYSTEM, rsc); |
||||||
|
assert_param(suc); |
||||||
|
|
||||||
|
// Resolve pin
|
||||||
|
lock_periph = plat_port2periph(LOCK_JUMPER_PORT, &suc); |
||||||
|
lock_llpin = plat_pin2ll(LOCK_JUMPER_PIN, &suc); |
||||||
|
assert_param(suc); |
||||||
|
|
||||||
|
// Configure for input
|
||||||
|
LL_GPIO_SetPinMode(lock_periph, lock_llpin, LL_GPIO_MODE_INPUT); |
||||||
|
LL_GPIO_SetPinPull(lock_periph, lock_llpin, LL_GPIO_PULL_UP); |
||||||
|
|
||||||
|
SystemSettings.editable = (bool) LL_GPIO_IsInputPinSet(lock_periph, lock_llpin); |
||||||
|
dbg("Settings editable? %d", SystemSettings.editable); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Handle jumper state change */ |
||||||
|
static void jumper_changed(void) |
||||||
|
{ |
||||||
|
if (SystemSettings.editable) { |
||||||
|
// Unlock
|
||||||
|
dbg("LOCK jumper removed, enabling MSC!"); |
||||||
|
} else { |
||||||
|
// Lock
|
||||||
|
dbg("LOCK jumper replaced, saving to Flash & disabling MSC!"); |
||||||
|
|
||||||
|
if (SystemSettings.modified) { |
||||||
|
settings_save(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
plat_usb_reconnect(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Periodic jumper check */ |
||||||
|
void LockJumper_Check(void) |
||||||
|
{ |
||||||
|
// Debounce cooldown
|
||||||
|
static uint32_t cooldown = 0; |
||||||
|
if (cooldown > 0) { |
||||||
|
cooldown--; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Read the pin state
|
||||||
|
bool old = SystemSettings.editable; |
||||||
|
SystemSettings.editable = (bool) LL_GPIO_IsInputPinSet(lock_periph, lock_llpin); |
||||||
|
|
||||||
|
if (old != SystemSettings.editable) { |
||||||
|
// --- State changed ---
|
||||||
|
cooldown = 5; // 0.5s if called every 100 ms
|
||||||
|
|
||||||
|
jumper_changed(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_LOCK_JUMPER_H |
||||||
|
#define GEX_LOCK_JUMPER_H |
||||||
|
|
||||||
|
#include "plat_compat.h" |
||||||
|
|
||||||
|
/**
|
||||||
|
* Init the lock jumper subsystem |
||||||
|
*/ |
||||||
|
void LockJumper_Init(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Check state of the lock jumper |
||||||
|
*/ |
||||||
|
void LockJumper_Check(void); |
||||||
|
|
||||||
|
#endif //GEX_LOCK_JUMPER_H
|
@ -0,0 +1,90 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "pin_utils.h" |
||||||
|
|
||||||
|
#define PINS_COUNT 16 |
||||||
|
|
||||||
|
/** Pin number to LL bitfield mapping */ |
||||||
|
static const uint32_t ll_pins[PINS_COUNT] = { |
||||||
|
LL_GPIO_PIN_0, |
||||||
|
LL_GPIO_PIN_1, |
||||||
|
LL_GPIO_PIN_2, |
||||||
|
LL_GPIO_PIN_3, |
||||||
|
LL_GPIO_PIN_4, |
||||||
|
LL_GPIO_PIN_5, |
||||||
|
LL_GPIO_PIN_6, |
||||||
|
LL_GPIO_PIN_7, |
||||||
|
LL_GPIO_PIN_8, |
||||||
|
LL_GPIO_PIN_9, |
||||||
|
LL_GPIO_PIN_10, |
||||||
|
LL_GPIO_PIN_11, |
||||||
|
LL_GPIO_PIN_12, |
||||||
|
LL_GPIO_PIN_13, |
||||||
|
LL_GPIO_PIN_14, |
||||||
|
LL_GPIO_PIN_15, |
||||||
|
}; |
||||||
|
|
||||||
|
/** Port number (A=0) to config struct pointer mapping */ |
||||||
|
static GPIO_TypeDef * const port_periphs[PORTS_COUNT] = { |
||||||
|
GPIOA, |
||||||
|
GPIOB, |
||||||
|
GPIOC, |
||||||
|
GPIOD, |
||||||
|
GPIOE, |
||||||
|
}; |
||||||
|
|
||||||
|
/** Convert pin number to LL bitfield */ |
||||||
|
uint32_t plat_pin2ll(uint8_t pin_number, bool *suc) |
||||||
|
{ |
||||||
|
assert_param(suc != NULL); |
||||||
|
|
||||||
|
if(pin_number >= PINS_COUNT) { |
||||||
|
dbg("Bad pin: %d", pin_number); |
||||||
|
// TODO proper report
|
||||||
|
*suc = false; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
return ll_pins[pin_number]; |
||||||
|
} |
||||||
|
|
||||||
|
/** Convert port name (A,B,C...) to peripheral struct pointer */ |
||||||
|
GPIO_TypeDef *plat_port2periph(char port_name, bool *suc) |
||||||
|
{ |
||||||
|
assert_param(suc != NULL); |
||||||
|
|
||||||
|
if(port_name < 'A' || port_name >= ('A'+PORTS_COUNT)) { |
||||||
|
dbg("Bad port: %c", port_name); |
||||||
|
// TODO proper report
|
||||||
|
*suc = false; |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t num = (uint8_t) (port_name - 'A'); |
||||||
|
return port_periphs[num]; |
||||||
|
} |
||||||
|
|
||||||
|
/** Convert a pin to resource handle */ |
||||||
|
Resource plat_pin2resource(char port_name, uint8_t pin_number, bool *suc) |
||||||
|
{ |
||||||
|
assert_param(suc != NULL); |
||||||
|
|
||||||
|
if(port_name < 'A' || port_name >= ('A'+PORTS_COUNT)) { |
||||||
|
dbg("Bad port: %c", port_name); |
||||||
|
// TODO proper report
|
||||||
|
*suc = false; |
||||||
|
return R_NONE; |
||||||
|
} |
||||||
|
|
||||||
|
if(pin_number >= PINS_COUNT) { |
||||||
|
// TODO proper report
|
||||||
|
dbg("Bad pin: %d", pin_number); |
||||||
|
*suc = false; |
||||||
|
return R_NONE; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t num = (uint8_t) (port_name - 'A'); |
||||||
|
|
||||||
|
return R_PA0 + num*16 + pin_number; |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
//
|
||||||
|
// Utilities for parsing pins from settings to LL and resources
|
||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/08.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_PIN_UTILS_H |
||||||
|
#define GEX_PIN_UTILS_H |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "resources.h" |
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert pin number to LL driver bitfield for working with the pin. |
||||||
|
* |
||||||
|
* @param pin_number - number 0..15 |
||||||
|
* @param suc - set to false on failure, left unchanged on success. |
||||||
|
* @return LL_GPIO_PIN_x |
||||||
|
*/ |
||||||
|
uint32_t plat_pin2ll(uint8_t pin_number, bool *suc); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert pin name and number to a resource enum |
||||||
|
* |
||||||
|
* @param port_name - char 'A'..'Z' |
||||||
|
* @param pin_number - number 0..15 |
||||||
|
* @param suc - set to false on failure, left unchanged on success |
||||||
|
* @return the resource, or R_NONE |
||||||
|
*/ |
||||||
|
Resource plat_pin2resource(char port_name, uint8_t pin_number, bool *suc); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert port name to peripheral instance |
||||||
|
* |
||||||
|
* @param port_name - char 'A'..'Z' |
||||||
|
* @param suc - set to false on failure, left unchanged on success. |
||||||
|
* @return instance |
||||||
|
*/ |
||||||
|
GPIO_TypeDef *plat_port2periph(char port_name, bool *suc); |
||||||
|
|
||||||
|
#endif //GEX_PIN_UTILS_H
|
@ -0,0 +1,52 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/08.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_PLAT_COMPAT_H |
||||||
|
#define GEX_PLAT_COMPAT_H |
||||||
|
|
||||||
|
// platform name for the version string
|
||||||
|
#define GEX_PLATFORM "STM32F103" |
||||||
|
|
||||||
|
#include <stm32f1xx.h> |
||||||
|
#include <stm32f1xx_hal.h> |
||||||
|
#include <stm32f1xx_ll_adc.h> |
||||||
|
#include <stm32f1xx_ll_bus.h> |
||||||
|
#include <stm32f1xx_ll_cortex.h> |
||||||
|
#include <stm32f1xx_ll_crc.h> |
||||||
|
#include <stm32f1xx_ll_dac.h> |
||||||
|
#include <stm32f1xx_ll_dma.h> |
||||||
|
#include <stm32f1xx_ll_exti.h> |
||||||
|
#include <stm32f1xx_ll_fsmc.h> |
||||||
|
#include <stm32f1xx_ll_gpio.h> |
||||||
|
#include <stm32f1xx_ll_i2c.h> |
||||||
|
#include <stm32f1xx_ll_iwdg.h> |
||||||
|
#include <stm32f1xx_ll_pwr.h> |
||||||
|
#include <stm32f1xx_ll_rcc.h> |
||||||
|
#include <stm32f1xx_ll_rtc.h> |
||||||
|
#include <stm32f1xx_ll_sdmmc.h> |
||||||
|
#include <stm32f1xx_ll_spi.h> |
||||||
|
#include <stm32f1xx_ll_system.h> |
||||||
|
#include <stm32f1xx_ll_tim.h> |
||||||
|
#include <stm32f1xx_ll_usart.h> |
||||||
|
#include <stm32f1xx_ll_usb.h> |
||||||
|
#include <stm32f1xx_ll_utils.h> |
||||||
|
#include <stm32f1xx_ll_wwdg.h> |
||||||
|
|
||||||
|
// size, determines position of the flash storage
|
||||||
|
#define FLASH_SIZE (64*1024) |
||||||
|
#define SETTINGS_BLOCK_SIZE (1024*2) // this must be a multiple of FLASH pages
|
||||||
|
#define SETTINGS_FLASH_ADDR (0x08000000 + FLASH_SIZE - SETTINGS_BLOCK_SIZE) |
||||||
|
|
||||||
|
// Number of GPIO ports A,B,C...
|
||||||
|
#define PORTS_COUNT 5 |
||||||
|
|
||||||
|
// Lock jumper config
|
||||||
|
#define LOCK_JUMPER_PORT 'C' |
||||||
|
#define LOCK_JUMPER_PIN 14 |
||||||
|
|
||||||
|
// Status LED config
|
||||||
|
#define STATUS_LED_PORT 'C' |
||||||
|
#define STATUS_LED_PIN 13 |
||||||
|
|
||||||
|
#endif //GEX_PLAT_COMPAT_H
|
@ -0,0 +1,37 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "comm/messages.h" |
||||||
|
#include "framework/resources.h" |
||||||
|
#include "framework/settings.h" |
||||||
|
#include "framework/system_settings.h" |
||||||
|
|
||||||
|
#include "lock_jumper.h" |
||||||
|
#include "status_led.h" |
||||||
|
#include "debug_uart.h" |
||||||
|
|
||||||
|
void plat_init(void) |
||||||
|
{ |
||||||
|
// Load system defaults
|
||||||
|
systemsettings_init(); |
||||||
|
|
||||||
|
dbg("Setting up resources ..."); |
||||||
|
rsc_init_registry(); |
||||||
|
plat_init_resources(); |
||||||
|
|
||||||
|
LockJumper_Init(); |
||||||
|
StatusLed_Init(); |
||||||
|
DebugUart_Init(); // <- only the resource claim
|
||||||
|
|
||||||
|
dbg("Registering platform units ..."); |
||||||
|
// All user-configurable units are now added to the repository
|
||||||
|
plat_register_units(); |
||||||
|
|
||||||
|
dbg("Loading settings ..."); |
||||||
|
// Load settings from Flash and apply (includes System settings and all Unit settings)
|
||||||
|
settings_load(); |
||||||
|
|
||||||
|
comm_init(); |
||||||
|
} |
@ -0,0 +1,87 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "usbd_core.h" |
||||||
|
#include "USB/usb_device.h" |
||||||
|
#include "framework/resources.h" |
||||||
|
|
||||||
|
|
||||||
|
// ----- SUPPORTED UNITS -----
|
||||||
|
|
||||||
|
#include "framework/unit_registry.h" |
||||||
|
|
||||||
|
#include "units/pin/unit_pin.h" |
||||||
|
#include "units/neopixel/unit_neopixel.h" |
||||||
|
|
||||||
|
void plat_register_units(void) |
||||||
|
{ |
||||||
|
ureg_add_type(&UNIT_PIN); |
||||||
|
ureg_add_type(&UNIT_NEOPIXEL); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// ----- RELEASE AVAILABLE RESOURCES -----
|
||||||
|
|
||||||
|
void plat_init_resources(void) |
||||||
|
{ |
||||||
|
__HAL_RCC_GPIOA_CLK_ENABLE(); |
||||||
|
__HAL_RCC_GPIOB_CLK_ENABLE(); |
||||||
|
__HAL_RCC_GPIOC_CLK_ENABLE(); |
||||||
|
__HAL_RCC_GPIOD_CLK_ENABLE(); |
||||||
|
__HAL_RCC_GPIOE_CLK_ENABLE(); |
||||||
|
|
||||||
|
// Platform F103C8T6 - free all present resources
|
||||||
|
{ |
||||||
|
rsc_free(NULL, R_ADC1); |
||||||
|
rsc_free(NULL, R_ADC2); |
||||||
|
rsc_free(NULL, R_I2C1); |
||||||
|
rsc_free(NULL, R_I2C2); |
||||||
|
rsc_free(NULL, R_SPI1); |
||||||
|
rsc_free(NULL, R_SPI2); |
||||||
|
rsc_free(NULL, R_TIM1); |
||||||
|
rsc_free(NULL, R_TIM2); |
||||||
|
rsc_free(NULL, R_TIM3); |
||||||
|
rsc_free(NULL, R_TIM4); |
||||||
|
rsc_free(NULL, R_USART1); |
||||||
|
rsc_free(NULL, R_USART2); |
||||||
|
rsc_free(NULL, R_USART3); |
||||||
|
rsc_free_range(NULL, R_PA0, R_PA15); |
||||||
|
rsc_free_range(NULL, R_PB0, R_PB15); |
||||||
|
rsc_free_range(NULL, R_PC13, R_PC15); |
||||||
|
rsc_free_range(NULL, R_PD0, R_PD1); |
||||||
|
} |
||||||
|
|
||||||
|
// Claim resources not available due to board layout or internal usage
|
||||||
|
{ |
||||||
|
bool ok = true; |
||||||
|
|
||||||
|
// HAL timebase
|
||||||
|
ok &= rsc_claim(&UNIT_SYSTEM, R_TIM1); |
||||||
|
// HSE crystal
|
||||||
|
ok &= rsc_claim(&UNIT_SYSTEM, R_PD0); |
||||||
|
ok &= rsc_claim(&UNIT_SYSTEM, R_PD1); |
||||||
|
// SWD
|
||||||
|
ok &= rsc_claim(&UNIT_SYSTEM, R_PA13); |
||||||
|
ok &= rsc_claim(&UNIT_SYSTEM, R_PA14); |
||||||
|
// USB
|
||||||
|
ok &= rsc_claim(&UNIT_SYSTEM, R_PA11); |
||||||
|
ok &= rsc_claim(&UNIT_SYSTEM, R_PA12); |
||||||
|
// BOOT pin(s)
|
||||||
|
ok &= rsc_claim(&UNIT_SYSTEM, R_PB2); // BOOT1
|
||||||
|
|
||||||
|
assert_param(ok); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// ---- USB reconnect ----
|
||||||
|
|
||||||
|
/** USB re-connect */ |
||||||
|
void plat_usb_reconnect(void) |
||||||
|
{ |
||||||
|
// F103 doesn't have pull-up control, this is probably the best we can do
|
||||||
|
USBD_LL_Reset(&hUsbDeviceFS); |
||||||
|
} |
||||||
|
|
@ -0,0 +1,59 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/08.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_PLATFORM_H |
||||||
|
#define GEX_PLATFORM_H |
||||||
|
|
||||||
|
#include <inttypes.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <stdarg.h> |
||||||
|
#include <stdbool.h> |
||||||
|
#include <inttypes.h> |
||||||
|
#include <stddef.h> |
||||||
|
#include <string.h> |
||||||
|
#include <malloc.h> |
||||||
|
|
||||||
|
// FreeRTOS includes
|
||||||
|
#include <cmsis_os.h> |
||||||
|
// platform-specific stuff (includes stm32 driver headers)
|
||||||
|
#include "plat_compat.h" |
||||||
|
// assert_param, trap...
|
||||||
|
#include "stm32_assert.h" |
||||||
|
// inIRQ etc
|
||||||
|
#include "cortex_utils.h" |
||||||
|
// MIN, MAX, static assert etc
|
||||||
|
#include "macro.h" |
||||||
|
// smaller replacement for regular snprintf - SNPRINTF
|
||||||
|
#include "snprintf.h" |
||||||
|
// debug logging
|
||||||
|
#include "debug.h" |
||||||
|
// error codes and strings
|
||||||
|
#include "utils/error.h" |
||||||
|
// GEX version string
|
||||||
|
#include "version.h" |
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init the platform |
||||||
|
*/ |
||||||
|
void plat_init(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Init resources available for this platform |
||||||
|
*/ |
||||||
|
void plat_init_resources(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Register units available for this platform / build |
||||||
|
*/ |
||||||
|
void plat_register_units(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-connect the USB, triggering descriptors reload. |
||||||
|
* Use the DPPU bit on USB_BCDR, if available. |
||||||
|
*/ |
||||||
|
void plat_usb_reconnect(void); |
||||||
|
|
||||||
|
#endif //GEX_PLATFORM_H
|
@ -0,0 +1,90 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "framework/resources.h" |
||||||
|
#include "status_led.h" |
||||||
|
#include "pin_utils.h" |
||||||
|
|
||||||
|
static uint32_t indicators[_INDICATOR_COUNT]; |
||||||
|
|
||||||
|
static GPIO_TypeDef *led_periph; |
||||||
|
static uint32_t led_llpin; |
||||||
|
|
||||||
|
/** Set up the LED */ |
||||||
|
void StatusLed_Init(void) |
||||||
|
{ |
||||||
|
bool suc = true; |
||||||
|
|
||||||
|
// Resolve and claim resource
|
||||||
|
Resource rsc = plat_pin2resource(STATUS_LED_PORT, STATUS_LED_PIN, &suc); |
||||||
|
assert_param(suc); |
||||||
|
|
||||||
|
suc &= rsc_claim(&UNIT_SYSTEM, rsc); |
||||||
|
assert_param(suc); |
||||||
|
|
||||||
|
// Resolve pin
|
||||||
|
led_periph = plat_port2periph(STATUS_LED_PORT, &suc); |
||||||
|
led_llpin = plat_pin2ll(STATUS_LED_PIN, &suc); |
||||||
|
assert_param(suc); |
||||||
|
|
||||||
|
// Configure for output
|
||||||
|
LL_GPIO_SetPinMode(led_periph, led_llpin, LL_GPIO_MODE_OUTPUT); |
||||||
|
LL_GPIO_SetPinOutputType(led_periph, led_llpin, LL_GPIO_OUTPUT_PUSHPULL); |
||||||
|
LL_GPIO_SetPinSpeed(led_periph, led_llpin, LL_GPIO_SPEED_FREQ_LOW); |
||||||
|
} |
||||||
|
|
||||||
|
/** Set indicator ON */ |
||||||
|
void StatusLed_On(enum GEX_StatusIndicator indicator) |
||||||
|
{ |
||||||
|
indicators[indicator] = osWaitForever; |
||||||
|
|
||||||
|
if (indicator == STATUS_FAULT) { |
||||||
|
// Persistent light
|
||||||
|
GPIOC->ODR |= 1<<13; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Set indicator OFF */ |
||||||
|
void StatusLed_Off(enum GEX_StatusIndicator indicator) |
||||||
|
{ |
||||||
|
indicators[indicator] = 0; |
||||||
|
// TODO some effect
|
||||||
|
} |
||||||
|
|
||||||
|
/** Set or reset a indicator */ |
||||||
|
void StatusLed_Set(enum GEX_StatusIndicator indicator, bool set) |
||||||
|
{ |
||||||
|
if (set) { |
||||||
|
StatusLed_On(indicator); |
||||||
|
} else { |
||||||
|
StatusLed_Off(indicator); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Turn indicator ON for a given interval */ |
||||||
|
void StatusLed_Flash(enum GEX_StatusIndicator indicator, uint32_t ms) |
||||||
|
{ |
||||||
|
indicators[indicator] = ms; |
||||||
|
// TODO
|
||||||
|
} |
||||||
|
|
||||||
|
/** Millisecond tick */ |
||||||
|
void StatusLed_Tick(void) |
||||||
|
{ |
||||||
|
for (uint32_t i = 0; i < _INDICATOR_COUNT; i++) { |
||||||
|
if (indicators[i] != osWaitForever && indicators[i] != 0) { |
||||||
|
if (--indicators[i]) { |
||||||
|
StatusLed_Off((enum GEX_StatusIndicator) i); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Heartbeat callback from the main thread */ |
||||||
|
void StatusLed_Heartbeat(void) |
||||||
|
{ |
||||||
|
// TODO fixme
|
||||||
|
GPIOC->ODR ^= 1<<13; |
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_INDICATORS_H |
||||||
|
#define GEX_INDICATORS_H |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicator (LED or blinking pattern) |
||||||
|
*/ |
||||||
|
enum GEX_StatusIndicator { |
||||||
|
STATUS_FAULT = 0, |
||||||
|
STATUS_USB_CONN, |
||||||
|
STATUS_USB_ACTIVITY, |
||||||
|
STATUS_DISK_BUSY, |
||||||
|
STATUS_DISK_ATTACHED, |
||||||
|
_INDICATOR_COUNT |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the statis LED(s) |
||||||
|
*/ |
||||||
|
void StatusLed_Init(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Set indicator ON |
||||||
|
* |
||||||
|
* @param indicator |
||||||
|
*/ |
||||||
|
void StatusLed_On(enum GEX_StatusIndicator indicator); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Set indicator OFF |
||||||
|
* |
||||||
|
* @param indicator |
||||||
|
*/ |
||||||
|
void StatusLed_Off(enum GEX_StatusIndicator indicator); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicator set or reset |
||||||
|
* |
||||||
|
* @param indicator |
||||||
|
* @param set |
||||||
|
*/ |
||||||
|
void StatusLed_Set(enum GEX_StatusIndicator indicator, bool set); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn indicator ON for a given interval |
||||||
|
* |
||||||
|
* @param indicator |
||||||
|
* @param ms - time ON in ms |
||||||
|
*/ |
||||||
|
void StatusLed_Flash(enum GEX_StatusIndicator indicator, uint32_t ms); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Ms tick for indicators |
||||||
|
*/ |
||||||
|
void StatusLed_Tick(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Heartbeat callback from the main thread to indicate activity |
||||||
|
*/ |
||||||
|
void StatusLed_Heartbeat(void); |
||||||
|
|
||||||
|
#endif //GEX_INDICATORS_H
|
@ -0,0 +1,32 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_SCHED_QUEUE_H |
||||||
|
#define GEX_SCHED_QUEUE_H |
||||||
|
|
||||||
|
#include <TinyFrame/TinyFrame.h> |
||||||
|
|
||||||
|
typedef struct sched_que_item Job; |
||||||
|
typedef void (*ScheduledJobCb) (Job *job); |
||||||
|
|
||||||
|
struct sched_que_item { |
||||||
|
ScheduledJobCb cb; |
||||||
|
union { |
||||||
|
TF_ID frame_id; |
||||||
|
void *data1; |
||||||
|
}; |
||||||
|
union { |
||||||
|
uint32_t d32; |
||||||
|
uint8_t *buf; |
||||||
|
const uint8_t *cbuf; |
||||||
|
const char *str; |
||||||
|
void *data2; |
||||||
|
}; |
||||||
|
union { |
||||||
|
uint32_t len; |
||||||
|
void *data3; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif //GEX_SCHED_QUEUE_H
|
@ -0,0 +1,38 @@ |
|||||||
|
#include "platform.h" |
||||||
|
#include "platform/status_led.h" |
||||||
|
|
||||||
|
/**
|
||||||
|
* Abort at file, line with a custom tag (eg. ASSERT FAILED) |
||||||
|
* @param msg - tag message |
||||||
|
* @param filename - file |
||||||
|
* @param line - line |
||||||
|
*/ |
||||||
|
void __attribute__((noreturn)) abort_msg(const char *msg, const char *filename, uint32_t line) |
||||||
|
{ |
||||||
|
dbg("\r\n\033[31m%s:\033[m %s:%"PRIu32"\r\n", msg, filename, line); |
||||||
|
vPortEnterCritical(); |
||||||
|
StatusLed_On(STATUS_FAULT); |
||||||
|
while(1); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Warn at file, line with a custom tag (eg. ASSERT FAILED) |
||||||
|
* @param msg - tag message |
||||||
|
* @param filename - file |
||||||
|
* @param line - line |
||||||
|
*/ |
||||||
|
void warn_msg(const char *msg, const char *filename, uint32_t line) |
||||||
|
{ |
||||||
|
dbg("\r\n\033[33m%s:\033[m %s:%"PRIu32"\r\n", msg, filename, line); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reports the name of the source file and the source line number |
||||||
|
* where the assert_param error has occurred. |
||||||
|
* @param file: pointer to the source file name |
||||||
|
* @param line: assert_param error line source number |
||||||
|
*/ |
||||||
|
void __attribute__((noreturn)) assert_failed_(const char *file, uint32_t line) |
||||||
|
{ |
||||||
|
abort_msg("ASSERT FAILED", file, line); |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef STM32_ASSERT_H |
||||||
|
#define STM32_ASSERT_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
void __attribute__((noreturn)) abort_msg(const char *msg, const char *filename, uint32_t line); |
||||||
|
void warn_msg(const char *msg, const char *filename, uint32_t line); |
||||||
|
void __attribute__((noreturn)) assert_failed_(const char *file, uint32_t line); |
||||||
|
|
||||||
|
#if USE_FULL_ASSERT |
||||||
|
#if VERBOSE_ASSERT |
||||||
|
// With the filename enabled.
|
||||||
|
#define trap(msg) abort_msg(msg, __BASE_FILE__, __LINE__) |
||||||
|
#define assert_param(expression) do { if (!(expression)) assert_failed_(__BASE_FILE__, __LINE__); } while(0) |
||||||
|
#define assert_warn(expression, msg) do { if (!(expression)) warn_msg(msg, __BASE_FILE__, __LINE__); } while(0) |
||||||
|
#define _Error_Handler(file, line) assert_failed_(__BASE_FILE__, __LINE__) |
||||||
|
#else |
||||||
|
// Filename disabled to save code size.
|
||||||
|
#define trap(msg) abort_msg(msg, "??", __LINE__) |
||||||
|
#define assert_param(expression) do { if (!(expression)) assert_failed_("??", __LINE__); } while(0) |
||||||
|
#define assert_warn(expression, msg) do { if (!(expression)) warn_msg(msg, "??", __LINE__); } while(0) |
||||||
|
#define _Error_Handler(file, line) assert_failed_("??", __LINE__) |
||||||
|
#endif |
||||||
|
#else |
||||||
|
// This is after everything is well tested, to cut some flash and make code faster by removing checks
|
||||||
|
#define trap(msg) do {} while(1) |
||||||
|
#define assert_param(expression) do { (void)(expression); } while(0) |
||||||
|
#define assert_warn(expression, msg) do { (void)(expression); } while(0) |
||||||
|
#define _Error_Handler(file, line) do {} while(1) |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif //STM32_ASSERT_H
|
@ -0,0 +1,84 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/09.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "platform/lock_jumper.h" |
||||||
|
#include "status_led.h" |
||||||
|
#include "utils/stacksmon.h" |
||||||
|
#include "vfs/vfs_manager.h" |
||||||
|
#include "usbd_cdc.h" |
||||||
|
#include "usb_device.h" |
||||||
|
#include "usbd_msc.h" |
||||||
|
#include "task_main.h" |
||||||
|
|
||||||
|
extern void plat_init(void); |
||||||
|
|
||||||
|
/* TaskUsbEvent function */ |
||||||
|
void TaskMain(void const * argument) |
||||||
|
{ |
||||||
|
dbg("> Main task started!"); |
||||||
|
|
||||||
|
vfs_if_usbd_msc_init(); |
||||||
|
vfs_mngr_init(1); |
||||||
|
|
||||||
|
uint32_t startTime = xTaskGetTickCount(); |
||||||
|
uint32_t cnt = 1; |
||||||
|
while(1) { |
||||||
|
uint32_t msg; |
||||||
|
xTaskNotifyWait(0, UINT32_MAX, &msg, 100); // time out if nothing happened
|
||||||
|
|
||||||
|
// periodic updates to the VFS driver
|
||||||
|
uint32_t now = xTaskGetTickCount(); |
||||||
|
uint32_t elapsed = now - startTime; |
||||||
|
if (elapsed >= 100) { |
||||||
|
// interval 100ms or more - slow periodic
|
||||||
|
vfs_mngr_periodic(elapsed); |
||||||
|
LockJumper_Check(); |
||||||
|
startTime = now; |
||||||
|
|
||||||
|
cnt++; |
||||||
|
if (cnt%5==0) StatusLed_Heartbeat(); |
||||||
|
} |
||||||
|
|
||||||
|
// if no message and it just timed out, go wait some more...
|
||||||
|
if (msg == 0) { |
||||||
|
// Low priority periodic tasks... (TODO move to a timer?)
|
||||||
|
|
||||||
|
// Periodically check stacks for overrun
|
||||||
|
stackmon_check_canaries(); |
||||||
|
// Periodically dump all stacks - for checking levels before critical (to reduce size if not needed)
|
||||||
|
if ((cnt%50)==0) stackmon_dump(); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// Endpoint 0 - control messages for the different classes
|
||||||
|
if (msg & USBEVT_FLAG_EP0_RX_RDY) { |
||||||
|
USBD_CDC_EP0_RxReady(&hUsbDeviceFS); |
||||||
|
} |
||||||
|
|
||||||
|
if (msg & USBEVT_FLAG_EP0_TX_SENT) { |
||||||
|
//
|
||||||
|
} |
||||||
|
|
||||||
|
// MSC - read/write etc
|
||||||
|
if (msg & (USBEVT_FLAG_EPx_IN(MSC_EPIN_ADDR))) { |
||||||
|
USBD_MSC_DataIn(&hUsbDeviceFS, MSC_EPIN_ADDR); |
||||||
|
} |
||||||
|
|
||||||
|
if (msg & (USBEVT_FLAG_EPx_OUT(MSC_EPOUT_ADDR))) { |
||||||
|
USBD_MSC_DataOut(&hUsbDeviceFS, MSC_EPOUT_ADDR); |
||||||
|
} |
||||||
|
|
||||||
|
// CDC - config packets and data in/out
|
||||||
|
if (msg & (USBEVT_FLAG_EPx_IN(CDC_IN_EP))) { |
||||||
|
USBD_CDC_DataIn(&hUsbDeviceFS, CDC_IN_EP); |
||||||
|
} |
||||||
|
if (msg & (USBEVT_FLAG_EPx_IN(CDC_CMD_EP))) { |
||||||
|
USBD_CDC_DataIn(&hUsbDeviceFS, CDC_CMD_EP); |
||||||
|
} |
||||||
|
if (msg & (USBEVT_FLAG_EPx_OUT(CDC_OUT_EP))) { |
||||||
|
USBD_CDC_DataOut(&hUsbDeviceFS, CDC_OUT_EP); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/09.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_TASK_MAIN_H |
||||||
|
#define GEX_TASK_MAIN_H |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
|
||||||
|
extern osThreadId tskMainHandle; |
||||||
|
void TaskMain(const void *argument); |
||||||
|
|
||||||
|
// Notify flags:
|
||||||
|
#define USBEVT_FLAG_EPx_IN(ep) (uint32_t)(1<<((ep)&0x7)) |
||||||
|
#define USBEVT_FLAG_EPx_OUT(ep) (uint32_t)(1<<(8 + ((ep)&0x7))) |
||||||
|
#define USBEVT_FLAG_EP0_RX_RDY (1<<16) |
||||||
|
#define USBEVT_FLAG_EP0_TX_SENT (1<<17) |
||||||
|
|
||||||
|
#endif //GEX_TASK_MAIN_H
|
@ -0,0 +1,89 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "task_sched.h" |
||||||
|
|
||||||
|
extern osMessageQId queSchedLPHandle; |
||||||
|
extern osMessageQId queSchedHPHandle; |
||||||
|
|
||||||
|
volatile uint32_t jobQueHighWaterMarkHP = 0; |
||||||
|
volatile uint32_t jobQueHighWaterMarkLP = 0; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule a function for later execution in the jobs thread |
||||||
|
* |
||||||
|
* @param callback - the callback function |
||||||
|
* @param prio - priority, selects which job queue to use |
||||||
|
* @param data1 - first data object, NULL if not needed; usually context/handle |
||||||
|
* @param data2 - second data object, NULL if not needed; usually data/argument |
||||||
|
*/ |
||||||
|
void scheduleJob(Job *job, enum task_sched_prio prio) |
||||||
|
{ |
||||||
|
QueueHandle_t que = (prio == TSK_SCHED_LOW ? queSchedLPHandle : queSchedHPHandle); |
||||||
|
|
||||||
|
assert_param(que != NULL); |
||||||
|
assert_param(job->cb != NULL); |
||||||
|
|
||||||
|
uint32_t count; |
||||||
|
if (inIRQ()) { |
||||||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE; |
||||||
|
assert_param(pdPASS == xQueueSendFromISR(que, job, &xHigherPriorityTaskWoken)); |
||||||
|
portYIELD_FROM_ISR(xHigherPriorityTaskWoken); |
||||||
|
|
||||||
|
#if USE_STACK_MONITOR |
||||||
|
count = (uint32_t) uxQueueMessagesWaitingFromISR(que); |
||||||
|
#endif |
||||||
|
} else { |
||||||
|
assert_param(pdPASS == xQueueSend(que, job, 100)); |
||||||
|
|
||||||
|
#if USE_STACK_MONITOR |
||||||
|
count = (uint32_t) uxQueueMessagesWaiting(que); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
#if USE_STACK_MONITOR |
||||||
|
if (prio == TSK_SCHED_LOW) { |
||||||
|
jobQueHighWaterMarkLP = MAX(jobQueHighWaterMarkLP, count); |
||||||
|
} else { |
||||||
|
jobQueHighWaterMarkHP = MAX(jobQueHighWaterMarkHP, count); |
||||||
|
} |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Low priority task queue handler |
||||||
|
* |
||||||
|
* @param argument |
||||||
|
*/ |
||||||
|
void TaskSchedLP (const void * argument) |
||||||
|
{ |
||||||
|
dbg("> Low priority queue task started!"); |
||||||
|
|
||||||
|
struct sched_que_item job; |
||||||
|
while (1) { |
||||||
|
xQueueReceive(queSchedLPHandle, &job, osWaitForever); |
||||||
|
|
||||||
|
assert_param(job.cb != NULL); |
||||||
|
job.cb(&job); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* High priority task queue handler |
||||||
|
* |
||||||
|
* @param argument |
||||||
|
*/ |
||||||
|
void TaskSchedHP (const void * argument) |
||||||
|
{ |
||||||
|
dbg("> High priority queue task started!"); |
||||||
|
|
||||||
|
struct sched_que_item job; |
||||||
|
while (1) { |
||||||
|
xQueueReceive(queSchedHPHandle, &job, osWaitForever); |
||||||
|
|
||||||
|
assert_param(job.cb != NULL); |
||||||
|
job.cb(&job); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_TASK_SCHED_H |
||||||
|
#define GEX_TASK_SCHED_H |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "sched_queue.h" |
||||||
|
|
||||||
|
enum task_sched_prio { |
||||||
|
TSK_SCHED_LOW = 0, |
||||||
|
TSK_SCHED_HIGH = 1, |
||||||
|
}; |
||||||
|
|
||||||
|
#if USE_STACK_MONITOR |
||||||
|
extern volatile uint32_t jobQueHighWaterMarkHP; |
||||||
|
extern volatile uint32_t jobQueHighWaterMarkLP; |
||||||
|
#endif |
||||||
|
|
||||||
|
extern osThreadId tskSchedLPHandle; |
||||||
|
void TaskSchedLP (const void * argument); |
||||||
|
|
||||||
|
extern osThreadId tskSchedHPHandle; |
||||||
|
void TaskSchedHP (const void * argument); |
||||||
|
|
||||||
|
void scheduleJob(Job *job, enum task_sched_prio prio); |
||||||
|
|
||||||
|
#endif //GEX_TASK_SCHED_H
|
@ -0,0 +1,204 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "utils/avrlibc.h" |
||||||
|
#include "comm/messages.h" |
||||||
|
#include "unit_base.h" |
||||||
|
#include "unit_neopixel.h" |
||||||
|
#include "ws2812.h" |
||||||
|
|
||||||
|
/** Private data structure */ |
||||||
|
struct priv { |
||||||
|
char port_name; |
||||||
|
uint8_t pin_number; |
||||||
|
uint16_t pixels; |
||||||
|
|
||||||
|
uint32_t ll_pin; |
||||||
|
GPIO_TypeDef *port; |
||||||
|
}; |
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Load from a binary buffer stored in Flash */ |
||||||
|
static void Npx_loadBinary(Unit *unit, PayloadParser *pp) |
||||||
|
{ |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
priv->port_name = pp_char(pp); |
||||||
|
priv->pin_number = pp_u8(pp); |
||||||
|
priv->pixels = pp_u16(pp); |
||||||
|
} |
||||||
|
|
||||||
|
/** Write to a binary buffer for storing in Flash */ |
||||||
|
static void Npx_writeBinary(Unit *unit, PayloadBuilder *pb) |
||||||
|
{ |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
pb_char(pb, priv->port_name); |
||||||
|
pb_u8(pb, priv->pin_number); |
||||||
|
pb_u16(pb, priv->pixels); |
||||||
|
} |
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Parse a key-value pair from the INI file */ |
||||||
|
static bool Npx_loadIni(Unit *unit, const char *key, const char *value) |
||||||
|
{ |
||||||
|
bool suc = true; |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
if (streq(key, "pin")) { |
||||||
|
suc = str_parse_pin(value, &priv->port_name, &priv->pin_number); |
||||||
|
} |
||||||
|
else if (streq(key, "pixels")) { |
||||||
|
priv->pixels = (uint16_t) avr_atoi(value); |
||||||
|
} |
||||||
|
else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return suc; |
||||||
|
} |
||||||
|
|
||||||
|
/** Generate INI file section for the unit */ |
||||||
|
static void Npx_writeIni(Unit *unit, IniWriter *iw) |
||||||
|
{ |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
iw_comment(iw, "Data pin"); |
||||||
|
iw_entry(iw, "pin", "%c%d", priv->port_name, priv->pin_number); |
||||||
|
|
||||||
|
iw_comment(iw, "Number of pixels"); |
||||||
|
iw_entry(iw, "pixels", "%d", priv->pixels); |
||||||
|
} |
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Allocate data structure and set defaults */ |
||||||
|
static bool Npx_preInit(Unit *unit) |
||||||
|
{ |
||||||
|
bool suc = true; |
||||||
|
struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv), &suc); |
||||||
|
CHECK_SUC(); |
||||||
|
|
||||||
|
// some defaults
|
||||||
|
priv->pin_number = 0; |
||||||
|
priv->port_name = 'A'; |
||||||
|
priv->pixels = 1; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** Finalize unit set-up */ |
||||||
|
static bool Npx_init(Unit *unit) |
||||||
|
{ |
||||||
|
bool suc = true; |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
// --- Parse config ---
|
||||||
|
priv->ll_pin = plat_pin2ll(priv->pin_number, &suc); |
||||||
|
priv->port = plat_port2periph(priv->port_name, &suc); |
||||||
|
Resource rsc = plat_pin2resource(priv->port_name, priv->pin_number, &suc); |
||||||
|
if (!suc) { |
||||||
|
unit->status = E_BAD_CONFIG; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// --- Claim resources ---
|
||||||
|
if (!rsc_claim(unit, rsc)) return false; |
||||||
|
|
||||||
|
// --- Init hardware ---
|
||||||
|
LL_GPIO_SetPinMode(priv->port, priv->ll_pin, LL_GPIO_MODE_OUTPUT); |
||||||
|
LL_GPIO_SetPinOutputType(priv->port, priv->ll_pin, LL_GPIO_OUTPUT_PUSHPULL); |
||||||
|
LL_GPIO_SetPinSpeed(priv->port, priv->ll_pin, LL_GPIO_SPEED_FREQ_HIGH); |
||||||
|
|
||||||
|
// clear strip
|
||||||
|
|
||||||
|
ws2812_clear(priv->port, priv->ll_pin, priv->pixels); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** Tear down the unit */ |
||||||
|
static void Npx_deInit(Unit *unit) |
||||||
|
{ |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
// configure the pin as analog
|
||||||
|
LL_GPIO_SetPinMode(priv->port, priv->ll_pin, LL_GPIO_MODE_ANALOG); |
||||||
|
|
||||||
|
// Release all resources
|
||||||
|
rsc_teardown(unit); |
||||||
|
|
||||||
|
// Free memory
|
||||||
|
free(unit->data); |
||||||
|
unit->data = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
enum PinCmd_ { |
||||||
|
CMD_CLEAR = 0, |
||||||
|
CMD_LOAD = 1, |
||||||
|
CMD_LOAD_U32_LE = 2, |
||||||
|
CMD_LOAD_U32_BE = 3, |
||||||
|
}; |
||||||
|
|
||||||
|
/** Handle a request message */ |
||||||
|
static bool Npx_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp) |
||||||
|
{ |
||||||
|
(void)pp; |
||||||
|
|
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
switch (command) { |
||||||
|
case CMD_CLEAR: |
||||||
|
ws2812_clear(priv->port, priv->ll_pin, priv->pixels); |
||||||
|
break; |
||||||
|
|
||||||
|
case CMD_LOAD: |
||||||
|
if (pp_length(pp) != priv->pixels*3) goto bad_count; |
||||||
|
ws2812_load_raw(priv->port, priv->ll_pin, pp->current, priv->pixels); |
||||||
|
break; |
||||||
|
|
||||||
|
case CMD_LOAD_U32_LE: |
||||||
|
if (pp_length(pp) != priv->pixels*4) goto bad_count; |
||||||
|
ws2812_load_sparse(priv->port, priv->ll_pin, pp->current, priv->pixels, 0); |
||||||
|
break; |
||||||
|
|
||||||
|
case CMD_LOAD_U32_BE: |
||||||
|
if (pp_length(pp) != priv->pixels*4) goto bad_count; |
||||||
|
ws2812_load_sparse(priv->port, priv->ll_pin, pp->current, priv->pixels, 1); |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
sched_respond_bad_cmd(frame_id); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
|
||||||
|
bad_count: |
||||||
|
sched_respond_err(frame_id, "BAD PIXEL COUNT"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Unit template */ |
||||||
|
const UnitDriver UNIT_NEOPIXEL = { |
||||||
|
.name = "NEOPIXEL", |
||||||
|
.description = "Neopixel RGB LED strip", |
||||||
|
// Settings
|
||||||
|
.preInit = Npx_preInit, |
||||||
|
.cfgLoadBinary = Npx_loadBinary, |
||||||
|
.cfgWriteBinary = Npx_writeBinary, |
||||||
|
.cfgLoadIni = Npx_loadIni, |
||||||
|
.cfgWriteIni = Npx_writeIni, |
||||||
|
// Init
|
||||||
|
.init = Npx_init, |
||||||
|
.deInit = Npx_deInit, |
||||||
|
// Function
|
||||||
|
.handleRequest = Npx_handleRequest, |
||||||
|
}; |
@ -0,0 +1,12 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef U_NEOPIXEL_H |
||||||
|
#define U_NEOPIXEL_H |
||||||
|
|
||||||
|
#include "unit.h" |
||||||
|
|
||||||
|
extern const UnitDriver UNIT_NEOPIXEL; |
||||||
|
|
||||||
|
#endif //U_NEOPIXEL_H
|
@ -0,0 +1,95 @@ |
|||||||
|
#include "platform.h" |
||||||
|
#include "ws2812.h" |
||||||
|
|
||||||
|
static //inline __attribute__((always_inline))
|
||||||
|
void ws2812_byte(GPIO_TypeDef *port, uint32_t ll_pin, uint8_t b) |
||||||
|
{ |
||||||
|
__disable_irq(); |
||||||
|
for (register volatile uint8_t i = 0; i < 8; i++) { |
||||||
|
LL_GPIO_SetOutputPin(port, ll_pin); |
||||||
|
|
||||||
|
// duty cycle determines bit value
|
||||||
|
if (b & 0x80) { |
||||||
|
for(uint32_t _i = 0; _i < 10; _i++) asm volatile("nop"); |
||||||
|
LL_GPIO_ResetOutputPin(port, ll_pin); |
||||||
|
for(uint32_t _i = 0; _i < 10; _i++) asm volatile("nop"); |
||||||
|
} else { |
||||||
|
for(uint32_t _i = 0; _i < 5; _i++) asm volatile("nop"); |
||||||
|
LL_GPIO_ResetOutputPin(port, ll_pin); |
||||||
|
for(uint32_t _i = 0; _i < 10; _i++) asm volatile("nop"); |
||||||
|
} |
||||||
|
|
||||||
|
b <<= 1; // shift to next bit
|
||||||
|
} |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Set many RGBs */ |
||||||
|
void ws2812_load(GPIO_TypeDef *port, uint32_t ll_pin, uint32_t *rgbs, uint32_t count) |
||||||
|
{ |
||||||
|
vPortEnterCritical(); |
||||||
|
for (uint32_t i = 0; i < count; i++) { |
||||||
|
uint32_t rgb = *rgbs++; |
||||||
|
ws2812_byte(port, ll_pin, rgb_g(rgb)); |
||||||
|
ws2812_byte(port, ll_pin, rgb_r(rgb)); |
||||||
|
ws2812_byte(port, ll_pin, rgb_b(rgb)); |
||||||
|
} |
||||||
|
vPortExitCritical(); |
||||||
|
// TODO: Delay 50 us
|
||||||
|
} |
||||||
|
|
||||||
|
/** Set many RGBs from packed stream */ |
||||||
|
void ws2812_load_raw(GPIO_TypeDef *port, uint32_t ll_pin, uint8_t *rgbs, uint32_t count) |
||||||
|
{ |
||||||
|
vPortEnterCritical(); |
||||||
|
uint8_t b, g, r; |
||||||
|
for (uint32_t i = 0; i < count; i++) { |
||||||
|
r = *rgbs++; |
||||||
|
g = *rgbs++; |
||||||
|
b = *rgbs++; |
||||||
|
ws2812_byte(port, ll_pin, g); |
||||||
|
ws2812_byte(port, ll_pin, r); |
||||||
|
ws2812_byte(port, ll_pin, b); |
||||||
|
} |
||||||
|
vPortExitCritical(); |
||||||
|
// TODO: Delay 50 us
|
||||||
|
} |
||||||
|
|
||||||
|
/** Set many RGBs from uint32 stream */ |
||||||
|
void ws2812_load_sparse(GPIO_TypeDef *port, uint32_t ll_pin, uint8_t *rgbs, uint32_t count, bool bigendian) |
||||||
|
{ |
||||||
|
vPortEnterCritical(); |
||||||
|
uint8_t b, g, r; |
||||||
|
for (uint32_t i = 0; i < count; i++) { |
||||||
|
if (bigendian) { |
||||||
|
rgbs++; // skip
|
||||||
|
b = *rgbs++; |
||||||
|
g = *rgbs++; |
||||||
|
r = *rgbs++; |
||||||
|
} else { |
||||||
|
r = *rgbs++; |
||||||
|
g = *rgbs++; |
||||||
|
b = *rgbs++; |
||||||
|
rgbs++; // skip
|
||||||
|
} |
||||||
|
|
||||||
|
ws2812_byte(port, ll_pin, g); |
||||||
|
ws2812_byte(port, ll_pin, r); |
||||||
|
ws2812_byte(port, ll_pin, b); |
||||||
|
} |
||||||
|
vPortExitCritical(); |
||||||
|
// TODO: Delay 50 us
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Set many RGBs */ |
||||||
|
void ws2812_clear(GPIO_TypeDef *port, uint32_t ll_pin, uint32_t count) |
||||||
|
{ |
||||||
|
vPortEnterCritical(); |
||||||
|
for (uint32_t i = 0; i < count*3; i++) { |
||||||
|
ws2812_byte(port, ll_pin, 0); |
||||||
|
} |
||||||
|
vPortExitCritical(); |
||||||
|
// TODO: Delay 50 us
|
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
/* Includes ------------------------------------------------------------------*/ |
||||||
|
|
||||||
|
#include "main.h" |
||||||
|
|
||||||
|
/* Exported types ------------------------------------------------------------*/ |
||||||
|
/* Exported constants --------------------------------------------------------*/ |
||||||
|
|
||||||
|
/* Exported macros -----------------------------------------------------------*/ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compose an RGB color. |
||||||
|
* @param r, g, b - components 0xFF |
||||||
|
* @returns integer 0xRRGGBB |
||||||
|
*/ |
||||||
|
#define rgb(r, g, b) (((0xFF & (r)) << 16) | ((0xFF & (g)) << 8) | (0xFF & (b))) |
||||||
|
|
||||||
|
/* Get components */ |
||||||
|
#define rgb_r(rgb) (uint8_t)(((rgb) >> 16) & 0xFF) |
||||||
|
#define rgb_g(rgb) (uint8_t)(((rgb) >> 8) & 0xFF) |
||||||
|
#define rgb_b(rgb) (uint8_t)((rgb) & 0xFF) |
||||||
|
|
||||||
|
typedef union { |
||||||
|
struct { |
||||||
|
uint8_t r; |
||||||
|
uint8_t g; |
||||||
|
uint8_t b; |
||||||
|
}; |
||||||
|
uint32_t num; |
||||||
|
} xrgb_t; |
||||||
|
|
||||||
|
/* Exported functions --------------------------------------------------------*/ |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set color of multiple chained RGB leds |
||||||
|
* |
||||||
|
* @param port |
||||||
|
* @param ll_pin |
||||||
|
* @param rgbs - array of colors (0xRRGGBB) |
||||||
|
* @param count - number of pixels |
||||||
|
*/ |
||||||
|
void ws2812_load(GPIO_TypeDef *port, uint32_t ll_pin, uint32_t *rgbs, uint32_t count); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Load RGBs from a packed byte stream |
||||||
|
* |
||||||
|
* @param port |
||||||
|
* @param ll_pin |
||||||
|
* @param rgbs - packed R,G,B, R,G,B, ... array |
||||||
|
* @param count - number of pixels (triplets) |
||||||
|
*/ |
||||||
|
void ws2812_load_raw(GPIO_TypeDef *port, uint32_t ll_pin, uint8_t *rgbs, uint32_t count); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all pixels with BLACK (0,0,0) |
||||||
|
* |
||||||
|
* @param port |
||||||
|
* @param ll_pin |
||||||
|
* @param count - number of pixels |
||||||
|
*/ |
||||||
|
void ws2812_clear(GPIO_TypeDef *port, uint32_t ll_pin, uint32_t count); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Load from a stream of 32-bit numbers (4th or 1st byte skipped) |
||||||
|
* @param port |
||||||
|
* @param ll_pin |
||||||
|
* @param rgbs - payload |
||||||
|
* @param count - number of pixels |
||||||
|
* @param bigendian - big endian ordering |
||||||
|
*/ |
||||||
|
void ws2812_load_sparse(GPIO_TypeDef *port, uint32_t ll_pin, uint8_t *rgbs, uint32_t count, bool bigendian); |
@ -0,0 +1,234 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "comm/messages.h" |
||||||
|
#include "unit_base.h" |
||||||
|
#include "unit_pin.h" |
||||||
|
|
||||||
|
/** Private data structure */ |
||||||
|
struct priv { |
||||||
|
char port_name; |
||||||
|
uint8_t pin_number; |
||||||
|
bool output; |
||||||
|
bool pull_up; |
||||||
|
bool open_drain; |
||||||
|
|
||||||
|
uint32_t ll_pin; |
||||||
|
GPIO_TypeDef *port; |
||||||
|
}; |
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Load from a binary buffer stored in Flash */ |
||||||
|
static void Pin_loadBinary(Unit *unit, PayloadParser *pp) |
||||||
|
{ |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
priv->port_name = pp_char(pp); |
||||||
|
priv->pin_number = pp_u8(pp); |
||||||
|
priv->output = pp_bool(pp); |
||||||
|
priv->pull_up = pp_bool(pp); |
||||||
|
priv->open_drain = pp_bool(pp); |
||||||
|
} |
||||||
|
|
||||||
|
/** Write to a binary buffer for storing in Flash */ |
||||||
|
static void Pin_writeBinary(Unit *unit, PayloadBuilder *pb) |
||||||
|
{ |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
pb_char(pb, priv->port_name); |
||||||
|
pb_u8(pb, priv->pin_number); |
||||||
|
pb_bool(pb, priv->output); |
||||||
|
pb_bool(pb, priv->pull_up); |
||||||
|
pb_bool(pb, priv->open_drain); |
||||||
|
} |
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Parse a key-value pair from the INI file */ |
||||||
|
static bool Pin_loadIni(Unit *unit, const char *key, const char *value) |
||||||
|
{ |
||||||
|
bool suc = true; |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
if (streq(key, "pin")) { |
||||||
|
suc = str_parse_pin(value, &priv->port_name, &priv->pin_number); |
||||||
|
} |
||||||
|
else if (streq(key, "dir")) { |
||||||
|
priv->output = str_parse_01(value, "IN", "OUT", &suc); |
||||||
|
} |
||||||
|
else if (streq(key, "pull")) { |
||||||
|
priv->pull_up = str_parse_01(value, "DOWN", "UP", &suc); |
||||||
|
} |
||||||
|
else if (streq(key, "opendrain")) { |
||||||
|
priv->open_drain = str_parse_yn(value, &suc); |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return suc; |
||||||
|
} |
||||||
|
|
||||||
|
/** Generate INI file section for the unit */ |
||||||
|
static void Pin_writeIni(Unit *unit, IniWriter *iw) |
||||||
|
{ |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
iw_comment(iw, "Physical pin"); |
||||||
|
iw_entry(iw, "pin", "%c%d", priv->port_name, priv->pin_number); |
||||||
|
|
||||||
|
iw_comment(iw, "Direction (IN, OUT)"); |
||||||
|
iw_entry(iw, "dir", "%s", str_01(priv->output, "IN", "OUT")); |
||||||
|
|
||||||
|
iw_comment(iw, "Pull resistor, only for input (UP, DOWN)"); |
||||||
|
iw_entry(iw, "pull", "%s", str_01(priv->pull_up, "DOWN", "UP")); |
||||||
|
|
||||||
|
iw_comment(iw, "Open drain, only for output (Y, N)"); |
||||||
|
iw_entry(iw, "opendrain", "%s", str_yn(priv->open_drain)); |
||||||
|
} |
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Allocate data structure and set defaults */ |
||||||
|
static bool Pin_preInit(Unit *unit) |
||||||
|
{ |
||||||
|
bool suc = true; |
||||||
|
struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv), &suc); |
||||||
|
CHECK_SUC(); |
||||||
|
|
||||||
|
// some defaults
|
||||||
|
priv->pin_number = 0; |
||||||
|
priv->port_name = 'A'; |
||||||
|
priv->output = true; |
||||||
|
priv->open_drain = false; |
||||||
|
priv->pull_up = false; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** Finalize unit set-up */ |
||||||
|
static bool Pin_init(Unit *unit) |
||||||
|
{ |
||||||
|
bool suc = true; |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
// --- Parse config ---
|
||||||
|
priv->ll_pin = plat_pin2ll(priv->pin_number, &suc); |
||||||
|
priv->port = plat_port2periph(priv->port_name, &suc); |
||||||
|
Resource rsc = plat_pin2resource(priv->port_name, priv->pin_number, &suc); |
||||||
|
if (!suc) { |
||||||
|
unit->status = E_BAD_CONFIG; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// --- Claim resources ---
|
||||||
|
if (!rsc_claim(unit, rsc)) return false; |
||||||
|
|
||||||
|
// --- Init hardware ---
|
||||||
|
LL_GPIO_SetPinMode(priv->port, priv->ll_pin, |
||||||
|
priv->output ? LL_GPIO_MODE_OUTPUT : LL_GPIO_MODE_INPUT); |
||||||
|
|
||||||
|
LL_GPIO_SetPinOutputType(priv->port, priv->ll_pin, |
||||||
|
priv->open_drain ? LL_GPIO_OUTPUT_OPENDRAIN : LL_GPIO_OUTPUT_PUSHPULL); |
||||||
|
|
||||||
|
LL_GPIO_SetPinPull(priv->port, priv->ll_pin, |
||||||
|
priv->pull_up ? LL_GPIO_PULL_UP : LL_GPIO_PULL_DOWN); |
||||||
|
|
||||||
|
LL_GPIO_SetPinSpeed(priv->port, priv->ll_pin, |
||||||
|
LL_GPIO_SPEED_FREQ_HIGH); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** Tear down the unit */ |
||||||
|
static void Pin_deInit(Unit *unit) |
||||||
|
{ |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
// configure the pin as analog
|
||||||
|
LL_GPIO_SetPinMode(priv->port, priv->ll_pin, LL_GPIO_MODE_ANALOG); |
||||||
|
|
||||||
|
// Release all resources
|
||||||
|
rsc_teardown(unit); |
||||||
|
|
||||||
|
// Free memory
|
||||||
|
free(unit->data); |
||||||
|
unit->data = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
enum PinCmd_ { |
||||||
|
CMD_CLEAR = 0, |
||||||
|
CMD_SET = 1, |
||||||
|
CMD_TOGGLE = 2, |
||||||
|
CMD_READ = 3, |
||||||
|
}; |
||||||
|
|
||||||
|
/** Handle a request message */ |
||||||
|
static bool Pin_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp) |
||||||
|
{ |
||||||
|
(void)pp; |
||||||
|
|
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
switch (command) { |
||||||
|
case CMD_CLEAR: |
||||||
|
if (priv->output) { |
||||||
|
LL_GPIO_ResetOutputPin(priv->port, priv->ll_pin); |
||||||
|
} else goto must_be_output; |
||||||
|
break; |
||||||
|
|
||||||
|
case CMD_SET: |
||||||
|
if (priv->output) { |
||||||
|
LL_GPIO_SetOutputPin(priv->port, priv->ll_pin); |
||||||
|
} else goto must_be_output; |
||||||
|
break; |
||||||
|
|
||||||
|
case CMD_TOGGLE: |
||||||
|
if (priv->output) { |
||||||
|
LL_GPIO_TogglePin(priv->port, priv->ll_pin); |
||||||
|
} else goto must_be_output; |
||||||
|
break; |
||||||
|
|
||||||
|
case CMD_READ: |
||||||
|
if (!priv->output) { |
||||||
|
sched_respond_u8(frame_id, (bool) LL_GPIO_IsInputPinSet(priv->port, priv->ll_pin)); |
||||||
|
} else goto must_be_input; |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
sched_respond_bad_cmd(frame_id); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
|
||||||
|
must_be_output: |
||||||
|
sched_respond_err(frame_id, "NOT OUTPUT PIN"); |
||||||
|
return false; |
||||||
|
|
||||||
|
must_be_input: |
||||||
|
sched_respond_err(frame_id, "NOT INPUT PIN"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Unit template */ |
||||||
|
const UnitDriver UNIT_PIN = { |
||||||
|
.name = "PIN", |
||||||
|
.description = "Single digital I/O pin", |
||||||
|
// Settings
|
||||||
|
.preInit = Pin_preInit, |
||||||
|
.cfgLoadBinary = Pin_loadBinary, |
||||||
|
.cfgWriteBinary = Pin_writeBinary, |
||||||
|
.cfgLoadIni = Pin_loadIni, |
||||||
|
.cfgWriteIni = Pin_writeIni, |
||||||
|
// Init
|
||||||
|
.init = Pin_init, |
||||||
|
.deInit = Pin_deInit, |
||||||
|
// Function
|
||||||
|
.handleRequest = Pin_handleRequest, |
||||||
|
}; |
@ -0,0 +1,12 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef U_PIN_H |
||||||
|
#define U_PIN_H |
||||||
|
|
||||||
|
#include "unit.h" |
||||||
|
|
||||||
|
extern const UnitDriver UNIT_PIN; |
||||||
|
|
||||||
|
#endif //U_PIN_H
|
@ -0,0 +1,35 @@ |
|||||||
|
/* Copyright (c) 2002, Marek Michalkiewicz
|
||||||
|
All rights reserved. |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions are met: |
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
* 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. |
||||||
|
* Neither the name of the copyright holders nor the names of |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
|
||||||
|
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 OWNER 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. */ |
||||||
|
|
||||||
|
#include "avrlibc.h" |
||||||
|
|
||||||
|
int |
||||||
|
avr_atoi(const char *p) |
||||||
|
{ |
||||||
|
return (int) avr_atol(p); |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
/* Copyright (c) 2002, Marek Michalkiewicz
|
||||||
|
All rights reserved. |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions are met: |
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
* 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. |
||||||
|
* Neither the name of the copyright holders nor the names of |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
|
||||||
|
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 OWNER 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. */ |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include "avrlibc.h" |
||||||
|
|
||||||
|
long |
||||||
|
avr_atol(const char *p) |
||||||
|
{ |
||||||
|
return avr_strtol(p, (char **) NULL, 10); |
||||||
|
} |
@ -0,0 +1,220 @@ |
|||||||
|
/* Copyright (c) 2002-2005 Michael Stumpf <mistumpf@de.pepperl-fuchs.com>
|
||||||
|
Copyright (c) 2006,2008 Dmitry Xmelkov |
||||||
|
|
||||||
|
All rights reserved. |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions are met: |
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
* 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. |
||||||
|
* Neither the name of the copyright holders nor the names of |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
|
||||||
|
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 OWNER 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. */ |
||||||
|
|
||||||
|
/* $Id: strtod.c 2191 2010-11-05 13:45:57Z arcanum $ */ |
||||||
|
|
||||||
|
#include <errno.h> |
||||||
|
#include <limits.h> |
||||||
|
#include <math.h> /* INFINITY, NAN */ |
||||||
|
#include <strings.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include "avrlibc.h" |
||||||
|
|
||||||
|
/* Only GCC 4.2 calls the library function to convert an unsigned long
|
||||||
|
to float. Other GCC-es (including 4.3) use a signed long to float |
||||||
|
conversion along with a large inline code to correct the result. */ |
||||||
|
extern double __floatunsisf (unsigned long); |
||||||
|
|
||||||
|
static const float pwr_p10 [6] = { |
||||||
|
1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32 |
||||||
|
}; |
||||||
|
static const float pwr_m10 [6] = { |
||||||
|
1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32 |
||||||
|
}; |
||||||
|
|
||||||
|
/* PSTR() is not used to save 1 byte per string: '\0' at the tail. */ |
||||||
|
static const char pstr_inf[] = {'I','N','F'}; |
||||||
|
static const char pstr_inity[] = {'I','N','I','T','Y'}; |
||||||
|
static const char pstr_nan[] = {'N','A','N'}; |
||||||
|
|
||||||
|
/** The strtod() function converts the initial portion of the string pointed
|
||||||
|
to by \a nptr to double representation. |
||||||
|
|
||||||
|
The expected form of the string is an optional plus ( \c '+' ) or minus |
||||||
|
sign ( \c '-' ) followed by a sequence of digits optionally containing |
||||||
|
a decimal-point character, optionally followed by an exponent. An |
||||||
|
exponent consists of an \c 'E' or \c 'e', followed by an optional plus |
||||||
|
or minus sign, followed by a sequence of digits. |
||||||
|
|
||||||
|
Leading white-space characters in the string are skipped. |
||||||
|
|
||||||
|
The strtod() function returns the converted value, if any. |
||||||
|
|
||||||
|
If \a endptr is not \c NULL, a pointer to the character after the last |
||||||
|
character used in the conversion is stored in the location referenced by |
||||||
|
\a endptr. |
||||||
|
|
||||||
|
If no conversion is performed, zero is returned and the value of |
||||||
|
\a nptr is stored in the location referenced by \a endptr. |
||||||
|
|
||||||
|
If the correct value would cause overflow, plus or minus \c INFINITY is |
||||||
|
returned (according to the sign of the value), and \c ERANGE is stored |
||||||
|
in \c errno. If the correct value would cause underflow, zero is |
||||||
|
returned and \c ERANGE is stored in \c errno. |
||||||
|
*/ |
||||||
|
|
||||||
|
double |
||||||
|
avr_strtod (const char * nptr, char ** endptr) |
||||||
|
{ |
||||||
|
union { |
||||||
|
unsigned long u32; |
||||||
|
float flt; |
||||||
|
} x; |
||||||
|
unsigned char c; |
||||||
|
int exp; |
||||||
|
|
||||||
|
unsigned char flag; |
||||||
|
#define FL_MINUS 0x01 /* number is negative */ |
||||||
|
#define FL_ANY 0x02 /* any digit was readed */ |
||||||
|
#define FL_OVFL 0x04 /* overflow was */ |
||||||
|
#define FL_DOT 0x08 /* decimal '.' was */ |
||||||
|
#define FL_MEXP 0x10 /* exponent 'e' is neg. */ |
||||||
|
|
||||||
|
if (endptr) |
||||||
|
*endptr = (char *)nptr; |
||||||
|
|
||||||
|
do { |
||||||
|
c = *nptr++; |
||||||
|
} while (c!=0&&c<=' '); |
||||||
|
|
||||||
|
flag = 0; |
||||||
|
if (c == '-') { |
||||||
|
flag = FL_MINUS; |
||||||
|
c = *nptr++; |
||||||
|
} else if (c == '+') { |
||||||
|
c = *nptr++; |
||||||
|
} |
||||||
|
|
||||||
|
if (!strncasecmp(nptr - 1, pstr_inf, 3)) { |
||||||
|
nptr += 2; |
||||||
|
if (!strncasecmp(nptr, pstr_inity, 5)) |
||||||
|
nptr += 5; |
||||||
|
if (endptr) |
||||||
|
*endptr = (char *)nptr; |
||||||
|
return flag & FL_MINUS ? -INFINITY : +INFINITY; |
||||||
|
} |
||||||
|
|
||||||
|
/* NAN() construction is not realised.
|
||||||
|
Length would be 3 characters only. */ |
||||||
|
if (!strncasecmp(nptr - 1, pstr_nan, 3)) { |
||||||
|
if (endptr) |
||||||
|
*endptr = (char *)nptr + 2; |
||||||
|
return NAN; |
||||||
|
} |
||||||
|
|
||||||
|
x.u32 = 0; |
||||||
|
exp = 0; |
||||||
|
while (1) { |
||||||
|
|
||||||
|
c -= '0'; |
||||||
|
|
||||||
|
if (c <= 9) { |
||||||
|
flag |= FL_ANY; |
||||||
|
if (flag & FL_OVFL) { |
||||||
|
if (!(flag & FL_DOT)) |
||||||
|
exp += 1; |
||||||
|
} else { |
||||||
|
if (flag & FL_DOT) |
||||||
|
exp -= 1; |
||||||
|
/* x.u32 = x.u32 * 10 + c */ |
||||||
|
x.u32 = (((x.u32 << 2) + x.u32) << 1) + c; |
||||||
|
if (x.u32 >= (ULONG_MAX - 9) / 10) |
||||||
|
flag |= FL_OVFL; |
||||||
|
} |
||||||
|
|
||||||
|
} else if (c == (('.'-'0') & 0xff) && !(flag & FL_DOT)) { |
||||||
|
flag |= FL_DOT; |
||||||
|
} else { |
||||||
|
break; |
||||||
|
} |
||||||
|
c = *nptr++; |
||||||
|
} |
||||||
|
|
||||||
|
if (c == (('e'-'0') & 0xff) || c == (('E'-'0') & 0xff)) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
c = *nptr++; |
||||||
|
i = 2; |
||||||
|
if (c == '-') { |
||||||
|
flag |= FL_MEXP; |
||||||
|
c = *nptr++; |
||||||
|
} else if (c == '+') { |
||||||
|
c = *nptr++; |
||||||
|
} else { |
||||||
|
i = 1; |
||||||
|
} |
||||||
|
c -= '0'; |
||||||
|
if (c > 9) { |
||||||
|
nptr -= i; |
||||||
|
} else { |
||||||
|
i = 0; |
||||||
|
do { |
||||||
|
if (i < 3200) |
||||||
|
i = (((i << 2) + i) << 1) + c; /* i = 10*i + c */ |
||||||
|
c = *nptr++ - '0'; |
||||||
|
} while (c <= 9); |
||||||
|
if (flag & FL_MEXP) |
||||||
|
i = -i; |
||||||
|
exp += i; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if ((flag & FL_ANY) && endptr) |
||||||
|
*endptr = (char *)nptr - 1; |
||||||
|
|
||||||
|
x.flt = __floatunsisf (x.u32); /* manually */ |
||||||
|
if ((flag & FL_MINUS) && (flag & FL_ANY)) |
||||||
|
x.flt = -x.flt; |
||||||
|
|
||||||
|
if (x.flt != 0) { |
||||||
|
int pwr; |
||||||
|
if (exp < 0) { |
||||||
|
nptr = (void *)(pwr_m10 + 5); |
||||||
|
exp = -exp; |
||||||
|
} else { |
||||||
|
nptr = (void *)(pwr_p10 + 5); |
||||||
|
} |
||||||
|
for (pwr = 32; pwr; pwr >>= 1) { |
||||||
|
for (; exp >= pwr; exp -= pwr) { |
||||||
|
union { |
||||||
|
unsigned long u32; |
||||||
|
float flt; |
||||||
|
} y; |
||||||
|
y.u32 = (uint32_t) *((float *)nptr); |
||||||
|
x.flt *= y.flt; |
||||||
|
} |
||||||
|
nptr -= sizeof(float); |
||||||
|
} |
||||||
|
if (!isfinite(x.flt) || x.flt == 0) |
||||||
|
errno = ERANGE; |
||||||
|
} |
||||||
|
|
||||||
|
return x.flt; |
||||||
|
} |
@ -0,0 +1,167 @@ |
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1990, 1993 |
||||||
|
* The Regents of the University of California. All rights reserved. |
||||||
|
* Copyright (c) 2005, Dmitry Xmelkov |
||||||
|
* |
||||||
|
* 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. |
||||||
|
* 3. Neither the name of the University nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. |
||||||
|
* |
||||||
|
* $Id: strtol.c 1944 2009-04-01 23:12:20Z arcanum $ |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <limits.h> |
||||||
|
#include <errno.h> |
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert a string to a long integer. |
||||||
|
* |
||||||
|
* Ignores `locale' stuff. Assumes that the upper and lower case |
||||||
|
* alphabets and digits are each contiguous. |
||||||
|
*/ |
||||||
|
long |
||||||
|
avr_strtol(const char *nptr, char **endptr, register int base) |
||||||
|
{ |
||||||
|
register unsigned long acc; |
||||||
|
register unsigned char c; |
||||||
|
register unsigned long cutoff; |
||||||
|
register signed char any; |
||||||
|
unsigned char flag = 0; |
||||||
|
#define FL_NEG 0x01 /* number is negative */ |
||||||
|
#define FL_0X 0x02 /* number has a 0x prefix */ |
||||||
|
|
||||||
|
if (endptr) |
||||||
|
*endptr = (char *)nptr; |
||||||
|
if (base != 0 && (base < 2 || base > 36)) |
||||||
|
return 0; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip white space and pick up leading +/- sign if any. |
||||||
|
* If base is 0, allow 0x for hex and 0 for octal, else |
||||||
|
* assume decimal; if base is already 16, allow 0x. |
||||||
|
*/ |
||||||
|
do { |
||||||
|
c = *nptr++; |
||||||
|
} while (c!=0&&c<=' '); |
||||||
|
if (c == '-') { |
||||||
|
flag = FL_NEG; |
||||||
|
c = *nptr++; |
||||||
|
} else if (c == '+') |
||||||
|
c = *nptr++; |
||||||
|
if ((base == 0 || base == 16) && |
||||||
|
c == '0' && (*nptr == 'x' || *nptr == 'X')) { |
||||||
|
c = nptr[1]; |
||||||
|
nptr += 2; |
||||||
|
base = 16; |
||||||
|
flag |= FL_0X; |
||||||
|
} |
||||||
|
if (base == 0) |
||||||
|
base = c == '0' ? 8 : 10; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute the cutoff value between legal numbers and illegal |
||||||
|
* numbers. That is the largest legal value, divided by the |
||||||
|
* base. An input number that is greater than this value, if |
||||||
|
* followed by a legal input character, is too big. One that |
||||||
|
* is equal to this value may be valid or not; the decision |
||||||
|
* about this is done as outlined below. |
||||||
|
* |
||||||
|
* Overflow detections works as follows: |
||||||
|
* |
||||||
|
* As: |
||||||
|
* acc_old <= cutoff |
||||||
|
* then: |
||||||
|
* acc_old * base <= 0x80000000 (unsigned) |
||||||
|
* then: |
||||||
|
* acc_old * base + c <= 0x80000000 + c |
||||||
|
* or: |
||||||
|
* acc_new <= 0x80000000 + 35 |
||||||
|
* |
||||||
|
* i.e. carry from MSB (by calculating acc_new) is impossible |
||||||
|
* and we can check result directly: |
||||||
|
* |
||||||
|
* if (acc_new > 0x80000000) then overflow |
||||||
|
* |
||||||
|
* Set any if any `digits' consumed; make it negative to indicate |
||||||
|
* overflow. |
||||||
|
*/ |
||||||
|
#if LONG_MIN != -LONG_MAX - 1 |
||||||
|
# error "This implementation of strtol() does not work on this platform." |
||||||
|
#endif |
||||||
|
switch (base) { |
||||||
|
case 10: |
||||||
|
cutoff = ((unsigned long)LONG_MAX + 1) / 10; |
||||||
|
break; |
||||||
|
case 16: |
||||||
|
cutoff = ((unsigned long)LONG_MAX + 1) / 16; |
||||||
|
break; |
||||||
|
case 8: |
||||||
|
cutoff = ((unsigned long)LONG_MAX + 1) / 8; |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
cutoff = ((unsigned long)LONG_MAX + 1) / 2; |
||||||
|
break; |
||||||
|
default: |
||||||
|
cutoff = ((unsigned long)LONG_MAX + 1) / base; |
||||||
|
} |
||||||
|
|
||||||
|
for (acc = 0, any = 0;; c = *nptr++) { |
||||||
|
if (c >= '0' && c <= '9') |
||||||
|
c -= '0'; |
||||||
|
else if (c >= 'A' && c <= 'Z') |
||||||
|
c -= 'A' - 10; |
||||||
|
else if (c >= 'a' && c <= 'z') |
||||||
|
c -= 'a' - 10; |
||||||
|
else |
||||||
|
break; |
||||||
|
if (c >= base) |
||||||
|
break; |
||||||
|
if (any < 0) |
||||||
|
continue; |
||||||
|
if (acc > cutoff) { |
||||||
|
any = -1; |
||||||
|
continue; |
||||||
|
} |
||||||
|
acc = acc * base + c; |
||||||
|
if (acc > (unsigned long)LONG_MAX + 1) |
||||||
|
any = -1; |
||||||
|
else |
||||||
|
any = 1; |
||||||
|
} |
||||||
|
if (endptr) { |
||||||
|
if (any) |
||||||
|
*endptr = (char *)nptr - 1; |
||||||
|
else if (flag & FL_0X) |
||||||
|
*endptr = (char *)nptr - 2; |
||||||
|
} |
||||||
|
if (any < 0) { |
||||||
|
acc = (flag & FL_NEG) ? LONG_MIN : LONG_MAX; |
||||||
|
errno = ERANGE; |
||||||
|
} else if (flag & FL_NEG) { |
||||||
|
acc = -acc; |
||||||
|
} else if ((signed long)acc < 0) { |
||||||
|
acc = LONG_MAX; |
||||||
|
errno = ERANGE; |
||||||
|
} |
||||||
|
return (acc); |
||||||
|
} |
@ -0,0 +1,145 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 1990, 1993 |
||||||
|
* The Regents of the University of California. All rights reserved. |
||||||
|
* Copyright (c) 2005, Dmitry Xmelkov |
||||||
|
* |
||||||
|
* 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. |
||||||
|
* 3. Neither the name of the University nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. |
||||||
|
* |
||||||
|
* $Id: strtoul.c 1944 2009-04-01 23:12:20Z arcanum $ |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <limits.h> |
||||||
|
#include <errno.h> |
||||||
|
#include "avrlibc.h" |
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert a string to an unsigned long integer. |
||||||
|
* |
||||||
|
* Ignores `locale' stuff. Assumes that the upper and lower case |
||||||
|
* alphabets and digits are each contiguous. |
||||||
|
*/ |
||||||
|
unsigned long |
||||||
|
avr_strtoul(const char *nptr, char **endptr, register int base) |
||||||
|
{ |
||||||
|
register unsigned long acc; |
||||||
|
register unsigned char c; |
||||||
|
register unsigned long cutoff; |
||||||
|
register signed char any; |
||||||
|
unsigned char flag = 0; |
||||||
|
#define FL_NEG 0x01 /* number is negative */ |
||||||
|
#define FL_0X 0x02 /* number has a 0x prefix */ |
||||||
|
|
||||||
|
if (endptr) |
||||||
|
*endptr = (char *)nptr; |
||||||
|
if (base != 0 && (base < 2 || base > 36)) |
||||||
|
return 0; |
||||||
|
|
||||||
|
/*
|
||||||
|
* See strtol for comments as to the logic used. |
||||||
|
*/ |
||||||
|
do { |
||||||
|
c = *nptr++; |
||||||
|
} while (c!=0&&c<=' '); |
||||||
|
if (c == '-') { |
||||||
|
flag = FL_NEG; |
||||||
|
c = *nptr++; |
||||||
|
} else if (c == '+') |
||||||
|
c = *nptr++; |
||||||
|
if ((base == 0 || base == 16) && |
||||||
|
c == '0' && (*nptr == 'x' || *nptr == 'X')) { |
||||||
|
c = nptr[1]; |
||||||
|
nptr += 2; |
||||||
|
base = 16; |
||||||
|
flag |= FL_0X; |
||||||
|
} |
||||||
|
if (base == 0) |
||||||
|
base = c == '0' ? 8 : 10; |
||||||
|
|
||||||
|
/*
|
||||||
|
* cutoff computation is similar to strtol(). |
||||||
|
* |
||||||
|
* Description of the overflow detection logic used. |
||||||
|
* |
||||||
|
* First, let us assume an overflow. |
||||||
|
* |
||||||
|
* Result of `acc_old * base + c' is cut to 32 bits: |
||||||
|
* acc_new <-- acc_old * base + c - 0x100000000 |
||||||
|
* |
||||||
|
* `acc_old * base' is <= 0xffffffff (cutoff control) |
||||||
|
* |
||||||
|
* then: acc_new <= 0xffffffff + c - 0x100000000 |
||||||
|
* |
||||||
|
* or: acc_new <= c - 1 |
||||||
|
* |
||||||
|
* or: acc_new < c |
||||||
|
* |
||||||
|
* Second: |
||||||
|
* if (no overflow) then acc * base + c >= c |
||||||
|
* (or: acc_new >= c) |
||||||
|
* is clear (alls are unsigned). |
||||||
|
* |
||||||
|
*/ |
||||||
|
switch (base) { |
||||||
|
case 16: cutoff = ULONG_MAX / 16; break; |
||||||
|
case 10: cutoff = ULONG_MAX / 10; break; |
||||||
|
case 8: cutoff = ULONG_MAX / 8; break; |
||||||
|
default: cutoff = ULONG_MAX / base; |
||||||
|
} |
||||||
|
|
||||||
|
for (acc = 0, any = 0;; c = *nptr++) { |
||||||
|
if (c >= '0' && c <= '9') |
||||||
|
c -= '0'; |
||||||
|
else if (c >= 'A' && c <= 'Z') |
||||||
|
c -= 'A' - 10; |
||||||
|
else if (c >= 'a' && c <= 'z') |
||||||
|
c -= 'a' - 10; |
||||||
|
else |
||||||
|
break; |
||||||
|
if (c >= base) |
||||||
|
break; |
||||||
|
if (any < 0) |
||||||
|
continue; |
||||||
|
if (acc > cutoff) { |
||||||
|
any = -1; |
||||||
|
continue; |
||||||
|
} |
||||||
|
acc = acc * base + c; |
||||||
|
any = (c > acc) ? -1 : 1; |
||||||
|
} |
||||||
|
|
||||||
|
if (endptr) { |
||||||
|
if (any) |
||||||
|
*endptr = (char *)nptr - 1; |
||||||
|
else if (flag & FL_0X) |
||||||
|
*endptr = (char *)nptr - 2; |
||||||
|
} |
||||||
|
if (flag & FL_NEG) |
||||||
|
acc = -acc; |
||||||
|
if (any < 0) { |
||||||
|
acc = ULONG_MAX; |
||||||
|
errno = ERANGE; |
||||||
|
} |
||||||
|
return (acc); |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_AVRLIBC_H_H |
||||||
|
#define GEX_AVRLIBC_H_H |
||||||
|
|
||||||
|
int avr_atoi(const char *p); |
||||||
|
long avr_strtol(const char *nptr, char **endptr, register int base); |
||||||
|
long avr_atol(const char *p); |
||||||
|
double avr_strtod (const char * nptr, char ** endptr); |
||||||
|
unsigned long avr_strtoul(const char *nptr, char **endptr, register int base); |
||||||
|
|
||||||
|
#endif //GEX_AVRLIBC_H_H
|
@ -0,0 +1,5 @@ |
|||||||
|
#!/bin/bash |
||||||
|
|
||||||
|
echo "-- Building parser from Ragel source --" |
||||||
|
|
||||||
|
ragel -L -G0 ini_parser.rl -o ini_parser.c |
@ -0,0 +1,177 @@ |
|||||||
|
/**
|
||||||
|
* @file circular_buffer.c |
||||||
|
* @brief Implementation of a circular buffer |
||||||
|
* |
||||||
|
* DAPLink Interface Firmware |
||||||
|
* Copyright (c) 2016-2016, ARM Limited, All Rights Reserved |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may |
||||||
|
* not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "circ_buf.h" |
||||||
|
|
||||||
|
void circ_buf_init(circ_buf_t *circ_buf, uint8_t *buffer, uint32_t size) |
||||||
|
{ |
||||||
|
vPortEnterCritical(); |
||||||
|
{ |
||||||
|
circ_buf->buf = buffer; |
||||||
|
circ_buf->size = size; |
||||||
|
circ_buf->head = 0; |
||||||
|
circ_buf->tail = 0; |
||||||
|
} |
||||||
|
vPortExitCritical(); |
||||||
|
} |
||||||
|
|
||||||
|
static void push_do(circ_buf_t *circ_buf, uint8_t data) |
||||||
|
{ |
||||||
|
circ_buf->buf[circ_buf->tail] = data; |
||||||
|
circ_buf->tail += 1; |
||||||
|
if (circ_buf->tail >= circ_buf->size) { |
||||||
|
assert_param(circ_buf->tail == circ_buf->size); |
||||||
|
circ_buf->tail = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool circ_buf_push_try(circ_buf_t *circ_buf, uint8_t data) |
||||||
|
{ |
||||||
|
bool success; |
||||||
|
vPortEnterCritical(); |
||||||
|
{ |
||||||
|
if (circ_buf_count_free(circ_buf) == 0) { |
||||||
|
success = false; |
||||||
|
goto quit; |
||||||
|
} |
||||||
|
|
||||||
|
push_do(circ_buf, data); |
||||||
|
success = true; |
||||||
|
} |
||||||
|
quit: |
||||||
|
vPortExitCritical(); |
||||||
|
return success; |
||||||
|
} |
||||||
|
|
||||||
|
void circ_buf_push(circ_buf_t *circ_buf, uint8_t data) |
||||||
|
{ |
||||||
|
vPortEnterCritical(); |
||||||
|
{ |
||||||
|
push_do(circ_buf, data); |
||||||
|
// Assert no overflow
|
||||||
|
assert_param(circ_buf->head != circ_buf->tail); |
||||||
|
} |
||||||
|
vPortExitCritical(); |
||||||
|
} |
||||||
|
|
||||||
|
static uint8_t pop_do(circ_buf_t *circ_buf) |
||||||
|
{ |
||||||
|
uint8_t data; |
||||||
|
data = circ_buf->buf[circ_buf->head]; |
||||||
|
circ_buf->head += 1; |
||||||
|
if (circ_buf->head >= circ_buf->size) { |
||||||
|
assert_param(circ_buf->head == circ_buf->size); |
||||||
|
circ_buf->head = 0; |
||||||
|
} |
||||||
|
return data; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t circ_buf_pop(circ_buf_t *circ_buf) |
||||||
|
{ |
||||||
|
uint8_t data; |
||||||
|
|
||||||
|
vPortEnterCritical(); |
||||||
|
{ |
||||||
|
// Assert buffer isn't empty
|
||||||
|
assert_param(circ_buf->head != circ_buf->tail); |
||||||
|
data = pop_do(circ_buf); |
||||||
|
} |
||||||
|
vPortExitCritical(); |
||||||
|
|
||||||
|
return data; |
||||||
|
} |
||||||
|
|
||||||
|
bool circ_buf_pop_try(circ_buf_t *circ_buf, uint8_t *data) |
||||||
|
{ |
||||||
|
bool success; |
||||||
|
vPortEnterCritical(); |
||||||
|
{ |
||||||
|
if (circ_buf->head == circ_buf->tail) { |
||||||
|
success = false; |
||||||
|
goto quit; |
||||||
|
} |
||||||
|
|
||||||
|
*data = pop_do(circ_buf); |
||||||
|
success = true; |
||||||
|
} |
||||||
|
quit: |
||||||
|
vPortExitCritical(); |
||||||
|
return success; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t circ_buf_count_used(circ_buf_t *circ_buf) |
||||||
|
{ |
||||||
|
uint32_t cnt; |
||||||
|
|
||||||
|
vPortEnterCritical(); |
||||||
|
{ |
||||||
|
if (circ_buf->tail >= circ_buf->head) { |
||||||
|
cnt = circ_buf->tail - circ_buf->head; |
||||||
|
} else { |
||||||
|
cnt = circ_buf->tail + circ_buf->size - circ_buf->head; |
||||||
|
} |
||||||
|
} |
||||||
|
vPortExitCritical(); |
||||||
|
|
||||||
|
return cnt; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t circ_buf_count_free(circ_buf_t *circ_buf) |
||||||
|
{ |
||||||
|
uint32_t cnt; |
||||||
|
|
||||||
|
vPortEnterCritical(); |
||||||
|
{ |
||||||
|
cnt = circ_buf->size - circ_buf_count_used(circ_buf) - 1; |
||||||
|
} |
||||||
|
vPortExitCritical(); |
||||||
|
|
||||||
|
return cnt; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t circ_buf_read(circ_buf_t *circ_buf, uint8_t *data, uint32_t size) |
||||||
|
{ |
||||||
|
uint32_t cnt; |
||||||
|
uint32_t i; |
||||||
|
|
||||||
|
cnt = circ_buf_count_used(circ_buf); |
||||||
|
cnt = MIN(size, cnt); |
||||||
|
for (i = 0; i < cnt; i++) { |
||||||
|
data[i] = circ_buf_pop(circ_buf); |
||||||
|
} |
||||||
|
|
||||||
|
return cnt; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t circ_buf_write(circ_buf_t *circ_buf, const uint8_t *data, uint32_t size) |
||||||
|
{ |
||||||
|
uint32_t cnt; |
||||||
|
uint32_t i; |
||||||
|
|
||||||
|
cnt = circ_buf_count_free(circ_buf); |
||||||
|
cnt = MIN(size, cnt); |
||||||
|
for (i = 0; i < cnt; i++) { |
||||||
|
circ_buf_push(circ_buf, data[i]); |
||||||
|
} |
||||||
|
|
||||||
|
return cnt; |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
/**
|
||||||
|
* @file circ_buf.h |
||||||
|
* @brief Implementation of a circular buffer |
||||||
|
* |
||||||
|
* DAPLink Interface Firmware |
||||||
|
* Copyright (c) 2016-2016, ARM Limited, All Rights Reserved |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may |
||||||
|
* not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef CIRC_BUF_H |
||||||
|
#define CIRC_BUF_H |
||||||
|
|
||||||
|
#include <stdbool.h> |
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
uint32_t head; |
||||||
|
uint32_t tail; |
||||||
|
uint32_t size; |
||||||
|
uint8_t *buf; |
||||||
|
} circ_buf_t; |
||||||
|
|
||||||
|
// Initialize or reinitialize a circular buffer
|
||||||
|
void circ_buf_init(circ_buf_t *circ_buf, uint8_t *buffer, uint32_t size); |
||||||
|
|
||||||
|
// Push a byte into the circular buffer
|
||||||
|
void circ_buf_push(circ_buf_t *circ_buf, uint8_t data); |
||||||
|
|
||||||
|
// Return a byte from the circular buffer
|
||||||
|
uint8_t circ_buf_pop(circ_buf_t *circ_buf); |
||||||
|
|
||||||
|
// try to read the buffer, return false if empty
|
||||||
|
bool circ_buf_pop_try(circ_buf_t *circ_buf, uint8_t *data); |
||||||
|
|
||||||
|
// try to write the buffer, return false if full
|
||||||
|
bool circ_buf_push_try(circ_buf_t *circ_buf, uint8_t data); |
||||||
|
|
||||||
|
// Get the number of bytes in the circular buffer
|
||||||
|
uint32_t circ_buf_count_used(circ_buf_t *circ_buf); |
||||||
|
|
||||||
|
// Get the number of free spots left in the circular buffer
|
||||||
|
uint32_t circ_buf_count_free(circ_buf_t *circ_buf); |
||||||
|
|
||||||
|
// Attempt to read size bytes from the buffer. Return the number of bytes read
|
||||||
|
uint32_t circ_buf_read(circ_buf_t *circ_buf, uint8_t *data, uint32_t size); |
||||||
|
|
||||||
|
// Attempt to write size bytes to the buffer. Return the number of bytes written
|
||||||
|
uint32_t circ_buf_write(circ_buf_t *circ_buf, const uint8_t *data, uint32_t size); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,26 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_CORTEX_UTILS_H |
||||||
|
#define GEX_CORTEX_UTILS_H |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
|
||||||
|
static inline bool inIRQ(void) |
||||||
|
{ |
||||||
|
return __get_IPSR() != 0; |
||||||
|
} |
||||||
|
|
||||||
|
register char *__SP asm("sp"); |
||||||
|
|
||||||
|
static inline bool isDynAlloc(const void *obj) |
||||||
|
{ |
||||||
|
// this is the 0x20000000-something address that should correspond to heap bottom
|
||||||
|
extern char heapstart __asm("end"); |
||||||
|
|
||||||
|
return ((uint32_t)obj >= (uint32_t)&heapstart) |
||||||
|
&& ((uint32_t)obj < (uint32_t)__SP); |
||||||
|
} |
||||||
|
|
||||||
|
#endif //GEX_CORTEX_UTILS_H
|
@ -0,0 +1,45 @@ |
|||||||
|
/**
|
||||||
|
* @file error.c |
||||||
|
* @brief Implementation of error.h |
||||||
|
* |
||||||
|
* DAPLink Interface Firmware |
||||||
|
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may |
||||||
|
* not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
|
||||||
|
static const char *const error_message[] = { |
||||||
|
#define X(name, text) text, |
||||||
|
X_ERROR_CODES |
||||||
|
#undef X |
||||||
|
}; |
||||||
|
COMPILER_ASSERT(ERROR_COUNT == ELEMENTS_IN_ARRAY(error_message)); |
||||||
|
|
||||||
|
const char *error_get_string(error_t error) |
||||||
|
{ |
||||||
|
const char *msg = 0; |
||||||
|
|
||||||
|
if (error < ERROR_COUNT) { |
||||||
|
msg = error_message[error]; |
||||||
|
} |
||||||
|
|
||||||
|
if (0 == msg) { |
||||||
|
assert_param(0); |
||||||
|
msg = ""; |
||||||
|
} |
||||||
|
|
||||||
|
return msg; |
||||||
|
} |
@ -0,0 +1,65 @@ |
|||||||
|
/**
|
||||||
|
* @file error.h |
||||||
|
* @brief collection of known errors and accessor for the friendly string |
||||||
|
* |
||||||
|
* DAPLink Interface Firmware |
||||||
|
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may |
||||||
|
* not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef ERROR_H |
||||||
|
#define ERROR_H |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#define X_ERROR_CODES \ |
||||||
|
/* Shared errors */ \
|
||||||
|
X(SUCCESS,"OK") \
|
||||||
|
X(FAILURE,"Failure") \
|
||||||
|
X(INTERNAL,"Internal error") \
|
||||||
|
X(LOADING,"Init in progress") \
|
||||||
|
\
|
||||||
|
/* VFS user errors */ \
|
||||||
|
X(ERROR_DURING_TRANSFER,"Error during transfer") \
|
||||||
|
X(TRANSFER_TIMEOUT,"Transfer timed out") \
|
||||||
|
/*X(FILE_BOUNDS,"Possible mismatch between file size and size programmed")*/ \
|
||||||
|
X(OOO_SECTOR,"File sent out of order by PC") \
|
||||||
|
\
|
||||||
|
/* File stream errors */ \
|
||||||
|
X(SUCCESS_DONE,"End of stream") \
|
||||||
|
X(SUCCESS_DONE_OR_CONTINUE,"End of stream, or more to come") \
|
||||||
|
\
|
||||||
|
/* Unit loading */ \
|
||||||
|
X(BAD_CONFIG, "Configuration error") \
|
||||||
|
X(OUT_OF_MEM, "Not enough RAM") \
|
||||||
|
X(RESOURCE_NOT_AVAILABLE, "Required pin / peripheral not available") |
||||||
|
|
||||||
|
// Keep in sync with the list error_message
|
||||||
|
typedef enum { |
||||||
|
#define X(name, text) E_##name, |
||||||
|
X_ERROR_CODES |
||||||
|
#undef X |
||||||
|
ERROR_COUNT |
||||||
|
} error_t; |
||||||
|
|
||||||
|
const char *error_get_string(error_t error) __attribute__((pure)); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,57 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/04.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "hexdump.h" |
||||||
|
|
||||||
|
void hexDump(const char *restrict desc, const void *restrict addr, uint32_t len) |
||||||
|
{ |
||||||
|
uint32_t i; |
||||||
|
uint8_t buff[17]; |
||||||
|
uint8_t *pc = (unsigned char*)addr; |
||||||
|
|
||||||
|
// Output description if given.
|
||||||
|
if (desc != NULL) |
||||||
|
PRINTF ("%s:\r\n", desc); |
||||||
|
|
||||||
|
if (len == 0) { |
||||||
|
PRINTF(" ZERO LENGTH\r\n"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Process every byte in the data.
|
||||||
|
for (i = 0; i < len; i++) { |
||||||
|
// Multiple of 16 means new line (with line offset).
|
||||||
|
|
||||||
|
if ((i % 16) == 0) { |
||||||
|
// Just don't print ASCII for the zeroth line.
|
||||||
|
if (i != 0) |
||||||
|
PRINTF (" %s\r\n", buff); |
||||||
|
|
||||||
|
// Output the offset.
|
||||||
|
PRINTF (" %04"PRIx32" ", i); |
||||||
|
} |
||||||
|
|
||||||
|
// Now the hex code for the specific character.
|
||||||
|
PRINTF (" %02x", pc[i]); |
||||||
|
|
||||||
|
// And store a printable ASCII character for later.
|
||||||
|
if ((pc[i] < 0x20) || (pc[i] > 0x7e)) |
||||||
|
buff[i % 16] = '.'; |
||||||
|
else |
||||||
|
buff[i % 16] = pc[i]; |
||||||
|
buff[(i % 16) + 1] = '\0'; |
||||||
|
} |
||||||
|
|
||||||
|
// Pad out last line if not exactly 16 characters.
|
||||||
|
while ((i % 16) != 0) { |
||||||
|
PRINTF (" "); |
||||||
|
i++; |
||||||
|
} |
||||||
|
|
||||||
|
// And print the final ASCII bit.
|
||||||
|
PRINTF (" %s\r\n", buff); |
||||||
|
|
||||||
|
(void)buff; |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/04.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_HEXDUMP_H |
||||||
|
#define GEX_HEXDUMP_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
/**
|
||||||
|
* HEXDUMP https://stackoverflow.com/a/7776146/2180189
|
||||||
|
* |
||||||
|
* @param desc - description printed above the dump |
||||||
|
* @param addr - address to dump |
||||||
|
* @param len - number of bytes |
||||||
|
*/ |
||||||
|
void hexDump(const char *restrict desc, const void *restrict addr, uint32_t len); |
||||||
|
|
||||||
|
#endif //GEX_HEXDUMP_H
|
@ -0,0 +1,526 @@ |
|||||||
|
|
||||||
|
/* #line 1 "ini_parser.rl" */ |
||||||
|
|
||||||
|
/* Ragel constants block */ |
||||||
|
#include "ini_parser.h" |
||||||
|
|
||||||
|
// Ragel setup
|
||||||
|
|
||||||
|
/* #line 10 "ini_parser.c" */ |
||||||
|
static const char _ini_actions[] = { |
||||||
|
0, 1, 1, 1, 2, 1, 3, 1,
|
||||||
|
4, 1, 5, 1, 6, 1, 7, 1,
|
||||||
|
8, 1, 9, 1, 10, 1, 11, 1,
|
||||||
|
13, 2, 0, 4, 2, 12, 4 |
||||||
|
}; |
||||||
|
|
||||||
|
static const char _ini_eof_actions[] = { |
||||||
|
0, 23, 5, 5, 15, 15, 15, 15,
|
||||||
|
19, 19, 0, 0, 0, 0, 0, 0,
|
||||||
|
0 |
||||||
|
}; |
||||||
|
|
||||||
|
static const int ini_start = 1; |
||||||
|
static const int ini_first_final = 12; |
||||||
|
static const int ini_error = 0; |
||||||
|
|
||||||
|
static const int ini_en_section = 2; |
||||||
|
static const int ini_en_keyvalue = 4; |
||||||
|
static const int ini_en_comment = 8; |
||||||
|
static const int ini_en_discard2eol = 10; |
||||||
|
static const int ini_en_main = 1; |
||||||
|
|
||||||
|
|
||||||
|
/* #line 10 "ini_parser.rl" */ |
||||||
|
|
||||||
|
|
||||||
|
// Persistent state
|
||||||
|
static int8_t cs = -1; //!< Ragel's Current State variable
|
||||||
|
static uint32_t buff_i = 0; //!< Write pointer for the buffers
|
||||||
|
static char value_quote = 0; //!< Quote character of the currently collected value
|
||||||
|
static bool value_nextesc = false; //!< Next character is escaped, trated specially, and if quote, as literal quote character
|
||||||
|
static IniParserCallback keyCallback = NULL; //!< Currently assigned callback
|
||||||
|
static void *userdata = NULL; //!< Currently assigned user data for the callback
|
||||||
|
|
||||||
|
// Buffers
|
||||||
|
static char keybuf[INI_KEY_MAX]; |
||||||
|
static char secbuf[INI_KEY_MAX]; |
||||||
|
static char valbuf[INI_VALUE_MAX]; |
||||||
|
|
||||||
|
// See header for doxygen!
|
||||||
|
|
||||||
|
void |
||||||
|
ini_parse_reset_partial(void) |
||||||
|
{ |
||||||
|
buff_i = 0; |
||||||
|
value_quote = 0; |
||||||
|
value_nextesc = false; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
ini_parse_reset(void) |
||||||
|
{ |
||||||
|
ini_parse_reset_partial(); |
||||||
|
keybuf[0] = secbuf[0] = valbuf[0] = 0; |
||||||
|
|
||||||
|
/* #line 67 "ini_parser.c" */ |
||||||
|
{ |
||||||
|
cs = ini_start; |
||||||
|
} |
||||||
|
|
||||||
|
/* #line 41 "ini_parser.rl" */ |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
ini_parser_error(const char* msg) |
||||||
|
{ |
||||||
|
ini_error("Parser error: %s", msg); |
||||||
|
ini_parse_reset_partial(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void |
||||||
|
ini_parse_begin(IniParserCallback callback, void *userData) |
||||||
|
{ |
||||||
|
keyCallback = callback; |
||||||
|
userdata = userData; |
||||||
|
ini_parse_reset(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void |
||||||
|
*ini_parse_end(void) |
||||||
|
{ |
||||||
|
ini_parse("\n", 1); |
||||||
|
if (keyCallback) { |
||||||
|
keyCallback = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
void *ud = userdata; |
||||||
|
userdata = NULL; |
||||||
|
return ud; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void |
||||||
|
ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData) |
||||||
|
{ |
||||||
|
ini_parse_begin(callback, userData); |
||||||
|
ini_parse(text, len); |
||||||
|
ini_parse_end(); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
rtrim_buf(char *buf, int32_t end) |
||||||
|
{ |
||||||
|
if (end > 0) { |
||||||
|
while ((uint8_t)buf[--end] < 33); |
||||||
|
end++; // go past the last character
|
||||||
|
} |
||||||
|
|
||||||
|
buf[end] = 0; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void |
||||||
|
ini_parse(const char *newstr, size_t len) |
||||||
|
{ |
||||||
|
int32_t i; |
||||||
|
char c; |
||||||
|
bool isnl; |
||||||
|
bool isquot; |
||||||
|
|
||||||
|
// Load new data to Ragel vars
|
||||||
|
const uint8_t *p; |
||||||
|
const uint8_t *eof; |
||||||
|
const uint8_t *pe; |
||||||
|
|
||||||
|
if (len == 0) while(newstr[++len] != 0); // alternative to strlen
|
||||||
|
|
||||||
|
p = (const uint8_t *) newstr; |
||||||
|
eof = NULL; |
||||||
|
pe = (const uint8_t *) (newstr + len); |
||||||
|
|
||||||
|
// Init Ragel on the first run
|
||||||
|
if (cs == -1) { |
||||||
|
ini_parse_reset(); |
||||||
|
} |
||||||
|
|
||||||
|
// The parser
|
||||||
|
|
||||||
|
/* #line 152 "ini_parser.c" */ |
||||||
|
{ |
||||||
|
const char *_acts; |
||||||
|
unsigned int _nacts; |
||||||
|
|
||||||
|
if ( p == pe ) |
||||||
|
goto _test_eof; |
||||||
|
if ( cs == 0 ) |
||||||
|
goto _out; |
||||||
|
_resume: |
||||||
|
switch ( cs ) { |
||||||
|
case 1: |
||||||
|
switch( (*p) ) { |
||||||
|
case 32u: goto tr1; |
||||||
|
case 35u: goto tr3; |
||||||
|
case 58u: goto tr0; |
||||||
|
case 59u: goto tr3; |
||||||
|
case 61u: goto tr0; |
||||||
|
case 91u: goto tr4; |
||||||
|
} |
||||||
|
if ( (*p) < 9u ) { |
||||||
|
if ( (*p) <= 8u ) |
||||||
|
goto tr0; |
||||||
|
} else if ( (*p) > 13u ) { |
||||||
|
if ( 14u <= (*p) && (*p) <= 31u ) |
||||||
|
goto tr0; |
||||||
|
} else |
||||||
|
goto tr1; |
||||||
|
goto tr2; |
||||||
|
case 0: |
||||||
|
goto _out; |
||||||
|
case 12: |
||||||
|
goto tr0; |
||||||
|
case 2: |
||||||
|
switch( (*p) ) { |
||||||
|
case 9u: goto tr6; |
||||||
|
case 32u: goto tr6; |
||||||
|
case 93u: goto tr5; |
||||||
|
} |
||||||
|
if ( (*p) <= 31u ) |
||||||
|
goto tr5; |
||||||
|
goto tr7; |
||||||
|
case 3: |
||||||
|
if ( (*p) == 93u ) |
||||||
|
goto tr8; |
||||||
|
if ( (*p) > 8u ) { |
||||||
|
if ( 10u <= (*p) && (*p) <= 31u ) |
||||||
|
goto tr5; |
||||||
|
} else |
||||||
|
goto tr5; |
||||||
|
goto tr7; |
||||||
|
case 13: |
||||||
|
goto tr5; |
||||||
|
case 4: |
||||||
|
switch( (*p) ) { |
||||||
|
case 10u: goto tr10; |
||||||
|
case 58u: goto tr11; |
||||||
|
case 61u: goto tr11; |
||||||
|
} |
||||||
|
goto tr9; |
||||||
|
case 5: |
||||||
|
switch( (*p) ) { |
||||||
|
case 9u: goto tr13; |
||||||
|
case 10u: goto tr14; |
||||||
|
case 13u: goto tr15; |
||||||
|
case 32u: goto tr13; |
||||||
|
} |
||||||
|
goto tr12; |
||||||
|
case 6: |
||||||
|
switch( (*p) ) { |
||||||
|
case 10u: goto tr14; |
||||||
|
case 13u: goto tr15; |
||||||
|
} |
||||||
|
goto tr12; |
||||||
|
case 14: |
||||||
|
goto tr10; |
||||||
|
case 7: |
||||||
|
if ( (*p) == 10u ) |
||||||
|
goto tr14; |
||||||
|
goto tr10; |
||||||
|
case 8: |
||||||
|
switch( (*p) ) { |
||||||
|
case 10u: goto tr17; |
||||||
|
case 13u: goto tr18; |
||||||
|
} |
||||||
|
goto tr16; |
||||||
|
case 15: |
||||||
|
goto tr19; |
||||||
|
case 9: |
||||||
|
if ( (*p) == 10u ) |
||||||
|
goto tr17; |
||||||
|
goto tr19; |
||||||
|
case 10: |
||||||
|
switch( (*p) ) { |
||||||
|
case 10u: goto tr21; |
||||||
|
case 13u: goto tr22; |
||||||
|
} |
||||||
|
goto tr20; |
||||||
|
case 16: |
||||||
|
goto tr23; |
||||||
|
case 11: |
||||||
|
if ( (*p) == 10u ) |
||||||
|
goto tr21; |
||||||
|
goto tr23; |
||||||
|
} |
||||||
|
|
||||||
|
tr23: cs = 0; goto _again; |
||||||
|
tr0: cs = 0; goto f0; |
||||||
|
tr5: cs = 0; goto f4; |
||||||
|
tr10: cs = 0; goto f7; |
||||||
|
tr19: cs = 0; goto f11; |
||||||
|
tr1: cs = 1; goto _again; |
||||||
|
tr6: cs = 2; goto _again; |
||||||
|
tr7: cs = 3; goto f5; |
||||||
|
tr9: cs = 4; goto f8; |
||||||
|
tr13: cs = 5; goto _again; |
||||||
|
tr11: cs = 5; goto f9; |
||||||
|
tr12: cs = 6; goto f10; |
||||||
|
tr15: cs = 7; goto _again; |
||||||
|
tr16: cs = 8; goto _again; |
||||||
|
tr18: cs = 9; goto _again; |
||||||
|
tr20: cs = 10; goto _again; |
||||||
|
tr22: cs = 11; goto _again; |
||||||
|
tr2: cs = 12; goto f1; |
||||||
|
tr3: cs = 12; goto f2; |
||||||
|
tr4: cs = 12; goto f3; |
||||||
|
tr8: cs = 13; goto f6; |
||||||
|
tr14: cs = 14; goto f10; |
||||||
|
tr17: cs = 15; goto f12; |
||||||
|
tr21: cs = 16; goto f13; |
||||||
|
|
||||||
|
f5: _acts = _ini_actions + 1; goto execFuncs; |
||||||
|
f6: _acts = _ini_actions + 3; goto execFuncs; |
||||||
|
f4: _acts = _ini_actions + 5; goto execFuncs; |
||||||
|
f1: _acts = _ini_actions + 7; goto execFuncs; |
||||||
|
f8: _acts = _ini_actions + 9; goto execFuncs; |
||||||
|
f9: _acts = _ini_actions + 11; goto execFuncs; |
||||||
|
f10: _acts = _ini_actions + 13; goto execFuncs; |
||||||
|
f7: _acts = _ini_actions + 15; goto execFuncs; |
||||||
|
f12: _acts = _ini_actions + 17; goto execFuncs; |
||||||
|
f11: _acts = _ini_actions + 19; goto execFuncs; |
||||||
|
f13: _acts = _ini_actions + 21; goto execFuncs; |
||||||
|
f0: _acts = _ini_actions + 23; goto execFuncs; |
||||||
|
f3: _acts = _ini_actions + 25; goto execFuncs; |
||||||
|
f2: _acts = _ini_actions + 28; goto execFuncs; |
||||||
|
|
||||||
|
execFuncs: |
||||||
|
_nacts = *_acts++; |
||||||
|
while ( _nacts-- > 0 ) { |
||||||
|
switch ( *_acts++ ) { |
||||||
|
case 0: |
||||||
|
/* #line 130 "ini_parser.rl" */ |
||||||
|
{ |
||||||
|
buff_i = 0; |
||||||
|
{cs = 2;goto _again;} |
||||||
|
} |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
/* #line 135 "ini_parser.rl" */ |
||||||
|
{ |
||||||
|
if (buff_i >= INI_KEY_MAX) { |
||||||
|
ini_parser_error("Section name too long"); |
||||||
|
{cs = 10;goto _again;} |
||||||
|
} |
||||||
|
keybuf[buff_i++] = (*p); |
||||||
|
} |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
/* #line 143 "ini_parser.rl" */ |
||||||
|
{ |
||||||
|
// we need a separate buffer for the result, otherwise a failed
|
||||||
|
// partial parse would corrupt the section string
|
||||||
|
rtrim_buf(keybuf, buff_i); |
||||||
|
for (i = 0; (c = keybuf[i]) != 0; i++) secbuf[i] = c; |
||||||
|
secbuf[i] = 0; |
||||||
|
{cs = 1;goto _again;} |
||||||
|
} |
||||||
|
break; |
||||||
|
case 3: |
||||||
|
/* #line 155 "ini_parser.rl" */ |
||||||
|
{ |
||||||
|
ini_parser_error("Syntax error in [section]"); |
||||||
|
if((*p) == '\n') {cs = 1;goto _again;} else {cs = 10;goto _again;} |
||||||
|
} |
||||||
|
break; |
||||||
|
case 4: |
||||||
|
/* #line 162 "ini_parser.rl" */ |
||||||
|
{ |
||||||
|
buff_i = 0; |
||||||
|
keybuf[buff_i++] = (*p); // add the first char
|
||||||
|
{cs = 4;goto _again;} |
||||||
|
} |
||||||
|
break; |
||||||
|
case 5: |
||||||
|
/* #line 168 "ini_parser.rl" */ |
||||||
|
{ |
||||||
|
if (buff_i >= INI_KEY_MAX) { |
||||||
|
ini_parser_error("Key too long"); |
||||||
|
{cs = 10;goto _again;} |
||||||
|
} |
||||||
|
keybuf[buff_i++] = (*p); |
||||||
|
} |
||||||
|
break; |
||||||
|
case 6: |
||||||
|
/* #line 176 "ini_parser.rl" */ |
||||||
|
{ |
||||||
|
rtrim_buf(keybuf, buff_i); |
||||||
|
|
||||||
|
// --- Value begin ---
|
||||||
|
buff_i = 0; |
||||||
|
value_quote = 0; |
||||||
|
value_nextesc = false; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 7: |
||||||
|
/* #line 185 "ini_parser.rl" */ |
||||||
|
{ |
||||||
|
isnl = ((*p) == '\r' || (*p) == '\n'); |
||||||
|
isquot = ((*p) == '\'' || (*p) == '"'); |
||||||
|
|
||||||
|
// detect our starting quote
|
||||||
|
if (isquot && !value_nextesc && buff_i == 0 && value_quote == 0) { |
||||||
|
value_quote = (*p); |
||||||
|
goto valueCharDone; |
||||||
|
} |
||||||
|
|
||||||
|
if (buff_i >= INI_VALUE_MAX) { |
||||||
|
ini_parser_error("Value too long"); |
||||||
|
{cs = 10;goto _again;} |
||||||
|
} |
||||||
|
|
||||||
|
// end of string - clean up and report
|
||||||
|
if ((!value_nextesc && (*p) == value_quote) || isnl) { |
||||||
|
if (isnl && value_quote) { |
||||||
|
ini_parser_error("Unterminated string"); |
||||||
|
{cs = 1;goto _again;} |
||||||
|
} |
||||||
|
|
||||||
|
// unquoted: trim from the end
|
||||||
|
if (!value_quote) { |
||||||
|
rtrim_buf(valbuf, buff_i); |
||||||
|
} else { |
||||||
|
valbuf[buff_i] = 0; |
||||||
|
} |
||||||
|
|
||||||
|
if (keyCallback) { |
||||||
|
keyCallback(secbuf, keybuf, valbuf, userdata); |
||||||
|
} |
||||||
|
|
||||||
|
// we don't want to discard to eol if the string was terminated by eol
|
||||||
|
// - would delete the next line
|
||||||
|
|
||||||
|
if (isnl) {cs = 1;goto _again;} else {cs = 10;goto _again;} |
||||||
|
} |
||||||
|
|
||||||
|
c = (*p); |
||||||
|
// escape...
|
||||||
|
if (value_nextesc) { |
||||||
|
if ((*p) == 'n') c = '\n'; |
||||||
|
else if ((*p) == 'r') c = '\r'; |
||||||
|
else if ((*p) == 't') c = '\t'; |
||||||
|
else if ((*p) == 'e') c = '\033'; |
||||||
|
} |
||||||
|
|
||||||
|
// collecting characters...
|
||||||
|
if (value_nextesc || (*p) != '\\') { // is quoted, or is not a quoting backslash - literal character
|
||||||
|
valbuf[buff_i++] = c; |
||||||
|
} |
||||||
|
|
||||||
|
value_nextesc = (!value_nextesc && (*p) == '\\'); |
||||||
|
valueCharDone:; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 8: |
||||||
|
/* #line 247 "ini_parser.rl" */ |
||||||
|
{ |
||||||
|
ini_parser_error("Syntax error in key=value"); |
||||||
|
if((*p) == '\n') {cs = 1;goto _again;} else {cs = 10;goto _again;} |
||||||
|
} |
||||||
|
break; |
||||||
|
case 9: |
||||||
|
/* #line 257 "ini_parser.rl" */ |
||||||
|
{ {cs = 1;goto _again;} } |
||||||
|
break; |
||||||
|
case 10: |
||||||
|
/* #line 258 "ini_parser.rl" */ |
||||||
|
{ |
||||||
|
ini_parser_error("Syntax error in comment"); |
||||||
|
if((*p) == '\n') {cs = 1;goto _again;} else {cs = 10;goto _again;} |
||||||
|
} |
||||||
|
break; |
||||||
|
case 11: |
||||||
|
/* #line 265 "ini_parser.rl" */ |
||||||
|
{ {cs = 1;goto _again;} } |
||||||
|
break; |
||||||
|
case 12: |
||||||
|
/* #line 273 "ini_parser.rl" */ |
||||||
|
{ {cs = 8;goto _again;} } |
||||||
|
break; |
||||||
|
case 13: |
||||||
|
/* #line 276 "ini_parser.rl" */ |
||||||
|
{ |
||||||
|
ini_parser_error("Syntax error in root"); |
||||||
|
{cs = 10;goto _again;} |
||||||
|
} |
||||||
|
break; |
||||||
|
/* #line 458 "ini_parser.c" */ |
||||||
|
} |
||||||
|
} |
||||||
|
goto _again; |
||||||
|
|
||||||
|
_again: |
||||||
|
if ( cs == 0 ) |
||||||
|
goto _out; |
||||||
|
if ( ++p != pe ) |
||||||
|
goto _resume; |
||||||
|
_test_eof: {} |
||||||
|
if ( p == eof ) |
||||||
|
{ |
||||||
|
const char *__acts = _ini_actions + _ini_eof_actions[cs]; |
||||||
|
unsigned int __nacts = (unsigned int) *__acts++; |
||||||
|
while ( __nacts-- > 0 ) { |
||||||
|
switch ( *__acts++ ) { |
||||||
|
case 3: |
||||||
|
/* #line 155 "ini_parser.rl" */ |
||||||
|
{ |
||||||
|
ini_parser_error("Syntax error in [section]"); |
||||||
|
if((*p) == '\n') {cs = 1; if ( p == pe ) |
||||||
|
goto _test_eof; |
||||||
|
goto _again;} else {cs = 10; if ( p == pe ) |
||||||
|
goto _test_eof; |
||||||
|
goto _again;} |
||||||
|
} |
||||||
|
break; |
||||||
|
case 8: |
||||||
|
/* #line 247 "ini_parser.rl" */ |
||||||
|
{ |
||||||
|
ini_parser_error("Syntax error in key=value"); |
||||||
|
if((*p) == '\n') {cs = 1; if ( p == pe ) |
||||||
|
goto _test_eof; |
||||||
|
goto _again;} else {cs = 10; if ( p == pe ) |
||||||
|
goto _test_eof; |
||||||
|
goto _again;} |
||||||
|
} |
||||||
|
break; |
||||||
|
case 10: |
||||||
|
/* #line 258 "ini_parser.rl" */ |
||||||
|
{ |
||||||
|
ini_parser_error("Syntax error in comment"); |
||||||
|
if((*p) == '\n') {cs = 1; if ( p == pe ) |
||||||
|
goto _test_eof; |
||||||
|
goto _again;} else {cs = 10; if ( p == pe ) |
||||||
|
goto _test_eof; |
||||||
|
goto _again;} |
||||||
|
} |
||||||
|
break; |
||||||
|
case 13: |
||||||
|
/* #line 276 "ini_parser.rl" */ |
||||||
|
{ |
||||||
|
ini_parser_error("Syntax error in root"); |
||||||
|
{cs = 10; if ( p == pe ) |
||||||
|
goto _test_eof; |
||||||
|
goto _again;} |
||||||
|
} |
||||||
|
break; |
||||||
|
/* #line 517 "ini_parser.c" */ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
_out: {} |
||||||
|
} |
||||||
|
|
||||||
|
/* #line 283 "ini_parser.rl" */ |
||||||
|
|
||||||
|
} |
@ -0,0 +1,65 @@ |
|||||||
|
#ifndef INIPARSE_STREAM_H |
||||||
|
#define INIPARSE_STREAM_H |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
|
||||||
|
#ifdef DEBUG_INI |
||||||
|
#define ini_error(fmt, ...) dbg("! INI err: "#fmt, ##__VA_ARGS__) |
||||||
|
#else |
||||||
|
#define ini_error(fmt, ...) |
||||||
|
#endif |
||||||
|
|
||||||
|
// buffer sizes
|
||||||
|
#define INI_KEY_MAX 20 |
||||||
|
#define INI_VALUE_MAX 30 |
||||||
|
|
||||||
|
/**
|
||||||
|
* INI parser callback, called for each found key-value pair. |
||||||
|
* |
||||||
|
* @param section - current section, empty string for global keys |
||||||
|
* @param key - found key (trimmed of whitespace) |
||||||
|
* @param value - value, trimmed of quotes or whitespace |
||||||
|
* @param userData - opaque user data pointer, general purpose |
||||||
|
*/ |
||||||
|
typedef void (*IniParserCallback)(const char *section, const char *key, const char *value, void *userData); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Begin parsing a stream |
||||||
|
* |
||||||
|
* @param callback - key callback to assign |
||||||
|
* @param userData - optional user data that will be passed to the callback |
||||||
|
*/ |
||||||
|
void ini_parse_begin(IniParserCallback callback, void *userData); |
||||||
|
|
||||||
|
/**
|
||||||
|
* End parse stream. |
||||||
|
* Flushes what remains in the buffer and removes callback. |
||||||
|
* |
||||||
|
* @returns userData or NULL if none |
||||||
|
*/ |
||||||
|
void* ini_parse_end(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a string (needn't be complete line or file) |
||||||
|
* |
||||||
|
* @param data - string to parse |
||||||
|
* @param len - string length (0 = use strlen) |
||||||
|
*/ |
||||||
|
void ini_parse(const char *data, size_t len); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a complete file loaded to string |
||||||
|
* |
||||||
|
* @param text - entire file as string |
||||||
|
* @param len - file length (0 = use strlen) |
||||||
|
* @param callback - key callback |
||||||
|
* @param userData - optional user data for key callback |
||||||
|
*/ |
||||||
|
void ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Explicitly reset the parser |
||||||
|
*/ |
||||||
|
void ini_parse_reset(void); |
||||||
|
|
||||||
|
#endif // INIPARSE_STREAM_H
|
@ -0,0 +1,284 @@ |
|||||||
|
|
||||||
|
/* Ragel constants block */ |
||||||
|
#include "ini_parser.h" |
||||||
|
|
||||||
|
// Ragel setup |
||||||
|
%%{ |
||||||
|
machine ini; |
||||||
|
write data; |
||||||
|
alphtype unsigned char; |
||||||
|
}%% |
||||||
|
|
||||||
|
// Persistent state |
||||||
|
static int8_t cs = -1; //!< Ragel's Current State variable |
||||||
|
static uint32_t buff_i = 0; //!< Write pointer for the buffers |
||||||
|
static char value_quote = 0; //!< Quote character of the currently collected value |
||||||
|
static bool value_nextesc = false; //!< Next character is escaped, trated specially, and if quote, as literal quote character |
||||||
|
static IniParserCallback keyCallback = NULL; //!< Currently assigned callback |
||||||
|
static void *userdata = NULL; //!< Currently assigned user data for the callback |
||||||
|
|
||||||
|
// Buffers |
||||||
|
static char keybuf[INI_KEY_MAX]; |
||||||
|
static char secbuf[INI_KEY_MAX]; |
||||||
|
static char valbuf[INI_VALUE_MAX]; |
||||||
|
|
||||||
|
// See header for doxygen! |
||||||
|
|
||||||
|
void |
||||||
|
ini_parse_reset_partial(void) |
||||||
|
{ |
||||||
|
buff_i = 0; |
||||||
|
value_quote = 0; |
||||||
|
value_nextesc = false; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
ini_parse_reset(void) |
||||||
|
{ |
||||||
|
ini_parse_reset_partial(); |
||||||
|
keybuf[0] = secbuf[0] = valbuf[0] = 0; |
||||||
|
%% write init; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
ini_parser_error(const char* msg) |
||||||
|
{ |
||||||
|
ini_error("Parser error: %s", msg); |
||||||
|
ini_parse_reset_partial(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void |
||||||
|
ini_parse_begin(IniParserCallback callback, void *userData) |
||||||
|
{ |
||||||
|
keyCallback = callback; |
||||||
|
userdata = userData; |
||||||
|
ini_parse_reset(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void |
||||||
|
*ini_parse_end(void) |
||||||
|
{ |
||||||
|
ini_parse("\n", 1); |
||||||
|
if (keyCallback) { |
||||||
|
keyCallback = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
void *ud = userdata; |
||||||
|
userdata = NULL; |
||||||
|
return ud; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void |
||||||
|
ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData) |
||||||
|
{ |
||||||
|
ini_parse_begin(callback, userData); |
||||||
|
ini_parse(text, len); |
||||||
|
ini_parse_end(); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
rtrim_buf(char *buf, int32_t end) |
||||||
|
{ |
||||||
|
if (end > 0) { |
||||||
|
while ((uint8_t)buf[--end] < 33); |
||||||
|
end++; // go past the last character |
||||||
|
} |
||||||
|
|
||||||
|
buf[end] = 0; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void |
||||||
|
ini_parse(const char *newstr, size_t len) |
||||||
|
{ |
||||||
|
int32_t i; |
||||||
|
char c; |
||||||
|
bool isnl; |
||||||
|
bool isquot; |
||||||
|
|
||||||
|
// Load new data to Ragel vars |
||||||
|
const uint8_t *p; |
||||||
|
const uint8_t *eof; |
||||||
|
const uint8_t *pe; |
||||||
|
|
||||||
|
if (len == 0) while(newstr[++len] != 0); // alternative to strlen |
||||||
|
|
||||||
|
p = (const uint8_t *) newstr; |
||||||
|
eof = NULL; |
||||||
|
pe = (const uint8_t *) (newstr + len); |
||||||
|
|
||||||
|
// Init Ragel on the first run |
||||||
|
if (cs == -1) { |
||||||
|
ini_parse_reset(); |
||||||
|
} |
||||||
|
|
||||||
|
// The parser |
||||||
|
%%{ |
||||||
|
#/ * |
||||||
|
ispace = [ \t]; # inline space |
||||||
|
wchar = any - 0..8 - 10..31; |
||||||
|
#apos = '\''; |
||||||
|
#quot = '\"'; |
||||||
|
nonl = [^\r\n]; |
||||||
|
nl = '\r'? '\n'; |
||||||
|
|
||||||
|
# ---- [SECTION] ---- |
||||||
|
|
||||||
|
action sectionStart { |
||||||
|
buff_i = 0; |
||||||
|
fgoto section; |
||||||
|
} |
||||||
|
|
||||||
|
action sectionChar { |
||||||
|
if (buff_i >= INI_KEY_MAX) { |
||||||
|
ini_parser_error("Section name too long"); |
||||||
|
fgoto discard2eol; |
||||||
|
} |
||||||
|
keybuf[buff_i++] = fc; |
||||||
|
} |
||||||
|
|
||||||
|
action sectionEnd { |
||||||
|
// we need a separate buffer for the result, otherwise a failed |
||||||
|
// partial parse would corrupt the section string |
||||||
|
rtrim_buf(keybuf, buff_i); |
||||||
|
for (i = 0; (c = keybuf[i]) != 0; i++) secbuf[i] = c; |
||||||
|
secbuf[i] = 0; |
||||||
|
fgoto main; |
||||||
|
} |
||||||
|
|
||||||
|
section := |
||||||
|
( |
||||||
|
ispace* <: ((wchar - ']')+ @sectionChar) ']' @sectionEnd |
||||||
|
) $!{ |
||||||
|
ini_parser_error("Syntax error in [section]"); |
||||||
|
if(fc == '\n') fgoto main; else fgoto discard2eol; |
||||||
|
}; |
||||||
|
|
||||||
|
# ---- KEY=VALUE ---- |
||||||
|
|
||||||
|
action keyStart { |
||||||
|
buff_i = 0; |
||||||
|
keybuf[buff_i++] = fc; // add the first char |
||||||
|
fgoto keyvalue; |
||||||
|
} |
||||||
|
|
||||||
|
action keyChar { |
||||||
|
if (buff_i >= INI_KEY_MAX) { |
||||||
|
ini_parser_error("Key too long"); |
||||||
|
fgoto discard2eol; |
||||||
|
} |
||||||
|
keybuf[buff_i++] = fc; |
||||||
|
} |
||||||
|
|
||||||
|
action keyEnd { |
||||||
|
rtrim_buf(keybuf, buff_i); |
||||||
|
|
||||||
|
// --- Value begin --- |
||||||
|
buff_i = 0; |
||||||
|
value_quote = 0; |
||||||
|
value_nextesc = false; |
||||||
|
} |
||||||
|
|
||||||
|
action valueChar { |
||||||
|
isnl = (fc == '\r' || fc == '\n'); |
||||||
|
isquot = (fc == '\'' || fc == '"'); |
||||||
|
|
||||||
|
// detect our starting quote |
||||||
|
if (isquot && !value_nextesc && buff_i == 0 && value_quote == 0) { |
||||||
|
value_quote = fc; |
||||||
|
goto valueCharDone; |
||||||
|
} |
||||||
|
|
||||||
|
if (buff_i >= INI_VALUE_MAX) { |
||||||
|
ini_parser_error("Value too long"); |
||||||
|
fgoto discard2eol; |
||||||
|
} |
||||||
|
|
||||||
|
// end of string - clean up and report |
||||||
|
if ((!value_nextesc && fc == value_quote) || isnl) { |
||||||
|
if (isnl && value_quote) { |
||||||
|
ini_parser_error("Unterminated string"); |
||||||
|
fgoto main; |
||||||
|
} |
||||||
|
|
||||||
|
// unquoted: trim from the end |
||||||
|
if (!value_quote) { |
||||||
|
rtrim_buf(valbuf, buff_i); |
||||||
|
} else { |
||||||
|
valbuf[buff_i] = 0; |
||||||
|
} |
||||||
|
|
||||||
|
if (keyCallback) { |
||||||
|
keyCallback(secbuf, keybuf, valbuf, userdata); |
||||||
|
} |
||||||
|
|
||||||
|
// we don't want to discard to eol if the string was terminated by eol |
||||||
|
// - would delete the next line |
||||||
|
|
||||||
|
if (isnl) fgoto main; else fgoto discard2eol; |
||||||
|
} |
||||||
|
|
||||||
|
c = fc; |
||||||
|
// escape... |
||||||
|
if (value_nextesc) { |
||||||
|
if (fc == 'n') c = '\n'; |
||||||
|
else if (fc == 'r') c = '\r'; |
||||||
|
else if (fc == 't') c = '\t'; |
||||||
|
else if (fc == 'e') c = '\033'; |
||||||
|
} |
||||||
|
|
||||||
|
// collecting characters... |
||||||
|
if (value_nextesc || fc != '\\') { // is quoted, or is not a quoting backslash - literal character |
||||||
|
valbuf[buff_i++] = c; |
||||||
|
} |
||||||
|
|
||||||
|
value_nextesc = (!value_nextesc && fc == '\\'); |
||||||
|
valueCharDone:; |
||||||
|
} |
||||||
|
|
||||||
|
# use * for key, first char is already consumed. |
||||||
|
keyvalue := |
||||||
|
( |
||||||
|
([^\n=:]* @keyChar %keyEnd) |
||||||
|
[=:] ispace* <: nonl* @valueChar nl @valueChar |
||||||
|
) $!{ |
||||||
|
ini_parser_error("Syntax error in key=value"); |
||||||
|
if(fc == '\n') fgoto main; else fgoto discard2eol; |
||||||
|
}; |
||||||
|
|
||||||
|
# ---- COMMENT ---- |
||||||
|
|
||||||
|
comment := |
||||||
|
( |
||||||
|
nonl* nl |
||||||
|
@{ fgoto main; } |
||||||
|
) $!{ |
||||||
|
ini_parser_error("Syntax error in comment"); |
||||||
|
if(fc == '\n') fgoto main; else fgoto discard2eol; |
||||||
|
}; |
||||||
|
|
||||||
|
# ---- CLEANUP ---- |
||||||
|
|
||||||
|
discard2eol := nonl* nl @{ fgoto main; }; |
||||||
|
|
||||||
|
# ---- ROOT ---- |
||||||
|
|
||||||
|
main := |
||||||
|
(space* |
||||||
|
( |
||||||
|
'[' @sectionStart | |
||||||
|
[#;] @{ fgoto comment; } | |
||||||
|
(wchar - [\t =:]) @keyStart |
||||||
|
) |
||||||
|
) $!{ |
||||||
|
ini_parser_error("Syntax error in root"); |
||||||
|
fgoto discard2eol; |
||||||
|
}; |
||||||
|
|
||||||
|
write exec; |
||||||
|
#*/ |
||||||
|
}%% |
||||||
|
} |
@ -0,0 +1,82 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/01.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "ini_writer.h" |
||||||
|
|
||||||
|
#ifndef MIN |
||||||
|
#define MIN(a,b) ((a)>(b)?(b):(a)) |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef MAX |
||||||
|
#define MAX(a,b) ((a)>(b)?(a):(b)) |
||||||
|
#endif |
||||||
|
|
||||||
|
#define IWBUFFER_LEN 128 |
||||||
|
|
||||||
|
// sprintf from varargs, allocating buffer on stack. Uses 'format' argument
|
||||||
|
#define IW_VPRINTF() do { \ |
||||||
|
char iwbuffer[IWBUFFER_LEN]; \
|
||||||
|
va_list args; \
|
||||||
|
va_start(args, format); \
|
||||||
|
uint32_t len = (int)fixup_vsnprintf(&iwbuffer[0], IWBUFFER_LEN, format, args); \
|
||||||
|
iw_buff(iw, (uint8_t *) iwbuffer, len); \
|
||||||
|
va_end(args); \
|
||||||
|
} while(0) |
||||||
|
|
||||||
|
void iw_buff(IniWriter *iw, const uint8_t *buf, uint32_t len) |
||||||
|
{ |
||||||
|
if (iw->count == 0) return; |
||||||
|
|
||||||
|
uint32_t skip = 0; |
||||||
|
if (iw->skip >= len) { |
||||||
|
// Skip entire string
|
||||||
|
iw->skip -= len; |
||||||
|
return; |
||||||
|
} else { |
||||||
|
// skip part
|
||||||
|
skip = iw->skip; |
||||||
|
iw->skip = 0; |
||||||
|
uint32_t count = MIN(iw->count, len-skip); |
||||||
|
memcpy(iw->ptr, buf+skip, count); |
||||||
|
iw->ptr += count; |
||||||
|
iw->count -= count; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void iw_sprintf(IniWriter *iw, const char *format, ...) |
||||||
|
{ |
||||||
|
if (iw->count == 0) return; |
||||||
|
|
||||||
|
IW_VPRINTF(); |
||||||
|
} |
||||||
|
|
||||||
|
// High level stuff
|
||||||
|
void iw_section(IniWriter *iw, const char *format, ...) |
||||||
|
{ |
||||||
|
if (iw->count == 0) return; |
||||||
|
|
||||||
|
iw_string(iw, "\r\n["); // newline before section as a separator
|
||||||
|
IW_VPRINTF(); |
||||||
|
iw_string(iw, "]\r\n"); |
||||||
|
} |
||||||
|
|
||||||
|
void iw_comment(IniWriter *iw, const char *format, ...) |
||||||
|
{ |
||||||
|
if (iw->count == 0) return; |
||||||
|
|
||||||
|
iw_string(iw, "# "); |
||||||
|
IW_VPRINTF(); |
||||||
|
iw_newline(iw); |
||||||
|
} |
||||||
|
|
||||||
|
void iw_entry(IniWriter *iw, const char *key, const char *format, ...) |
||||||
|
{ |
||||||
|
if (iw->count == 0) return; |
||||||
|
|
||||||
|
iw_string(iw, key); |
||||||
|
iw_string(iw, "="); |
||||||
|
IW_VPRINTF(); |
||||||
|
iw_newline(iw); // one newline after entry
|
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/01.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef INIWRITER_H |
||||||
|
#define INIWRITER_H |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
|
||||||
|
typedef struct iniwriter_ { |
||||||
|
char *ptr; |
||||||
|
uint32_t skip; |
||||||
|
uint32_t count; |
||||||
|
} IniWriter; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a IniWriter struct (macro) |
||||||
|
* |
||||||
|
* @param buffer - buffer backing the writer, result will be written here |
||||||
|
* @param skip - number of written bytes to skip |
||||||
|
* @param count - number of bytes to write, truncate rest |
||||||
|
* @return structure initializer |
||||||
|
*/ |
||||||
|
#define iw_init(buffer, skip, count) (IniWriter){buffer, skip, count} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to write a buffer to the file |
||||||
|
* |
||||||
|
* @param iw - iniwriter handle |
||||||
|
* @param buf - buffer to write |
||||||
|
* @param len - buffer len |
||||||
|
*/ |
||||||
|
void iw_buff(IniWriter *iw, const uint8_t *buf, uint32_t len); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to write a string to the file |
||||||
|
* |
||||||
|
* @param iw - iniwriter handle |
||||||
|
* @param str - string to write |
||||||
|
*/ |
||||||
|
static inline void iw_string(IniWriter *iw, const char *str) |
||||||
|
{ |
||||||
|
if (iw->count != 0) { |
||||||
|
iw_buff(iw, (uint8_t *) str, (uint32_t) strlen(str)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#define iw_newline(iw) iw_string(iw, "\r\n") |
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to snprintf to the file |
||||||
|
* |
||||||
|
* @param iw - iniwriter handle |
||||||
|
* @param format - format, like printf |
||||||
|
* @param ... - replacements |
||||||
|
*/ |
||||||
|
void iw_sprintf(IniWriter *iw, const char *format, ...) |
||||||
|
__attribute__((format(printf,2,3))); |
||||||
|
|
||||||
|
// High level stuff
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to write a INI section header [foobar]\r\n |
||||||
|
* @param iw - iniwriter handle |
||||||
|
* @param format - format, like printf |
||||||
|
* @param ... - replacements |
||||||
|
*/ |
||||||
|
void iw_section(IniWriter *iw, const char *format, ...) |
||||||
|
__attribute__((format(printf,2,3))); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to write a INI comment # blah\r\n |
||||||
|
* @param iw - iniwriter handle |
||||||
|
* @param format - format, like printf |
||||||
|
* @param ... - replacements |
||||||
|
*/ |
||||||
|
void iw_comment(IniWriter *iw, const char *format, ...) |
||||||
|
__attribute__((format(printf,2,3))); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to write a key-value entry |
||||||
|
* |
||||||
|
* @param iw - iniwriter handle |
||||||
|
* @param key - key |
||||||
|
* @param format - value format (like printf) |
||||||
|
* @param ... - replacements |
||||||
|
*/ |
||||||
|
void iw_entry(IniWriter *iw, const char *key, const char *format, ...) |
||||||
|
__attribute__((format(printf,3,4))); |
||||||
|
|
||||||
|
#endif //INIWRITER_H
|
@ -0,0 +1,74 @@ |
|||||||
|
/**
|
||||||
|
* @file macro.h |
||||||
|
* @brief useful things + Special asserts and macros |
||||||
|
* |
||||||
|
* DAPLink Interface Firmware |
||||||
|
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may |
||||||
|
* not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef VFS_MACRO_H |
||||||
|
#define VFS_MACRO_H |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
// --------------- VFS macros and general purpose --------------------
|
||||||
|
|
||||||
|
#define ELEMENTS_IN_ARRAY(array) (sizeof(array)/sizeof(array[0])) |
||||||
|
|
||||||
|
#define MB(size) ((size) * 1024 * 1024) |
||||||
|
|
||||||
|
#define KB(size) ((size) * 1024) |
||||||
|
|
||||||
|
#ifndef MIN |
||||||
|
#define MIN(a,b) ((a) < (b) ? (a) : (b)) |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef MAX |
||||||
|
#define MAX(a,b) ((a) > (b) ? (a) : (b)) |
||||||
|
#endif |
||||||
|
|
||||||
|
#define ROUND_UP(value, boundary) ((value) + ((boundary) - (value)) % (boundary)) |
||||||
|
|
||||||
|
#define ROUND_DOWN(value, boundary) ((value) - ((value) % (boundary))) |
||||||
|
|
||||||
|
// ---------- HELPERS FOR XMACROS -----------------
|
||||||
|
|
||||||
|
#define XJOIN(a, b) a##b |
||||||
|
#define STR_(x) #x |
||||||
|
#define STR(x) STR_(x) |
||||||
|
|
||||||
|
// ---------- COMPILER SPECIAL MACROS -------------
|
||||||
|
|
||||||
|
#define COMPILER_CONCAT_(a, b) a##b |
||||||
|
#define COMPILER_CONCAT(a, b) COMPILER_CONCAT_(a, b) |
||||||
|
|
||||||
|
// Divide by zero if the the expression is false. This
|
||||||
|
// causes an error at compile time.
|
||||||
|
//
|
||||||
|
// The special value '__COUNTER__' is used to create a unique value to
|
||||||
|
// append to 'compiler_assert_' to create a unique token. This prevents
|
||||||
|
// conflicts resulting from the same enum being declared multiple times.
|
||||||
|
#define COMPILER_ASSERT(e) enum { COMPILER_CONCAT(compiler_assert_, __COUNTER__) = 1/((e) ? 1 : 0) } |
||||||
|
|
||||||
|
#define __at(_addr) __attribute__ ((at(_addr))) |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,48 @@ |
|||||||
|
#include <stdlib.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <stdbool.h> |
||||||
|
#include <inttypes.h> |
||||||
|
#include "debug.h" |
||||||
|
#include "stm32_assert.h" |
||||||
|
#include "malloc_safe.h" |
||||||
|
|
||||||
|
#if 1 |
||||||
|
|
||||||
|
void *malloc_safe_do(size_t size, const char *file, uint32_t line) |
||||||
|
{ |
||||||
|
void *mem = malloc(size); |
||||||
|
if (mem == NULL) abort_msg("MALLOC FAILED", file, line); |
||||||
|
return mem; |
||||||
|
} |
||||||
|
|
||||||
|
void *calloc_safe_do(size_t nmemb, size_t size, const char *file, uint32_t line) |
||||||
|
{ |
||||||
|
void *mem = calloc(size, nmemb); |
||||||
|
if (mem == NULL) abort_msg("CALLOC FAILED", file, line); |
||||||
|
return mem; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void *malloc_ck_do(size_t size, bool *suc, const char *file, uint32_t line) |
||||||
|
{ |
||||||
|
void *mem = malloc(size); |
||||||
|
if (mem == NULL) { |
||||||
|
warn_msg("MALLOC FAILED", file, line); |
||||||
|
*suc = false; |
||||||
|
mem = NULL; |
||||||
|
} |
||||||
|
return mem; |
||||||
|
} |
||||||
|
|
||||||
|
void *calloc_ck_do(size_t nmemb, size_t size, bool *suc, const char *file, uint32_t line) |
||||||
|
{ |
||||||
|
void *mem = calloc(size, nmemb); |
||||||
|
if (mem == NULL) { |
||||||
|
warn_msg("CALLOC FAILED", file, line); |
||||||
|
*suc = false; |
||||||
|
mem = NULL; |
||||||
|
} |
||||||
|
return mem; |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,22 @@ |
|||||||
|
#ifndef MALLOC_SAFE_H |
||||||
|
#define MALLOC_SAFE_H |
||||||
|
|
||||||
|
/**
|
||||||
|
* Malloc that prints error and restarts the system on failure. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <stdbool.h> |
||||||
|
|
||||||
|
void *malloc_safe_do(size_t size, const char* file, uint32_t line) __attribute__((malloc)); |
||||||
|
void *calloc_safe_do(size_t nmemb, size_t size, const char* file, uint32_t line) __attribute__((malloc)); |
||||||
|
void *malloc_ck_do(size_t size, bool *suc, const char* file, uint32_t line) __attribute__((malloc)); |
||||||
|
void *calloc_ck_do(size_t nmemb, size_t size, bool *suc, const char* file, uint32_t line) __attribute__((malloc)); |
||||||
|
|
||||||
|
#define malloc_s(size) malloc_safe_do(size, __BASE_FILE__, __LINE__) |
||||||
|
#define calloc_s(nmemb, size) calloc_safe_do(nmemb, size, __BASE_FILE__, __LINE__) |
||||||
|
#define malloc_ck(size, suc) malloc_ck_do(size, suc, __BASE_FILE__, __LINE__) |
||||||
|
#define calloc_ck(nmemb, size, suc) calloc_ck_do(nmemb, size, suc, __BASE_FILE__, __LINE__) |
||||||
|
|
||||||
|
#endif // MALLOC_SAFE_H
|
@ -0,0 +1,100 @@ |
|||||||
|
#include <string.h> |
||||||
|
#include "payload_builder.h" |
||||||
|
|
||||||
|
#define pb_check_capacity(pb, needed) \ |
||||||
|
if ((pb)->current + (needed) > (pb)->end) { \
|
||||||
|
if ((pb)->full_handler == NULL || !(pb)->full_handler(pb, needed)) (pb)->ok = 0; \
|
||||||
|
} |
||||||
|
|
||||||
|
/** Write from a buffer */ |
||||||
|
bool pb_buf(PayloadBuilder *pb, const uint8_t *buf, uint32_t len) |
||||||
|
{ |
||||||
|
pb_check_capacity(pb, len); |
||||||
|
if (!pb->ok) return false; |
||||||
|
|
||||||
|
memcpy(pb->current, buf, len); |
||||||
|
pb->current += len; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** Write s zero terminated string */ |
||||||
|
bool pb_string(PayloadBuilder *pb, const char *str) |
||||||
|
{ |
||||||
|
uint32_t len = (uint32_t) strlen(str); |
||||||
|
pb_check_capacity(pb, len+1); |
||||||
|
if (!pb->ok) return false; |
||||||
|
|
||||||
|
memcpy(pb->current, str, len+1); |
||||||
|
pb->current += len+1; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** Write uint8_t to the buffer */ |
||||||
|
bool pb_u8(PayloadBuilder *pb, uint8_t byte) |
||||||
|
{ |
||||||
|
pb_check_capacity(pb, 1); |
||||||
|
if (!pb->ok) return false; |
||||||
|
|
||||||
|
*pb->current++ = byte; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** Write uint16_t to the buffer. */ |
||||||
|
bool pb_u16(PayloadBuilder *pb, uint16_t word) |
||||||
|
{ |
||||||
|
pb_check_capacity(pb, 2); |
||||||
|
if (!pb->ok) return false; |
||||||
|
|
||||||
|
if (pb->bigendian) { |
||||||
|
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF); |
||||||
|
*pb->current++ = (uint8_t) (word & 0xFF); |
||||||
|
} else { |
||||||
|
*pb->current++ = (uint8_t) (word & 0xFF); |
||||||
|
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** Write uint32_t to the buffer. */ |
||||||
|
bool pb_u32(PayloadBuilder *pb, uint32_t word) |
||||||
|
{ |
||||||
|
pb_check_capacity(pb, 4); |
||||||
|
if (!pb->ok) return false; |
||||||
|
|
||||||
|
if (pb->bigendian) { |
||||||
|
*pb->current++ = (uint8_t) ((word >> 24) & 0xFF); |
||||||
|
*pb->current++ = (uint8_t) ((word >> 16) & 0xFF); |
||||||
|
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF); |
||||||
|
*pb->current++ = (uint8_t) (word & 0xFF); |
||||||
|
} else { |
||||||
|
*pb->current++ = (uint8_t) (word & 0xFF); |
||||||
|
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF); |
||||||
|
*pb->current++ = (uint8_t) ((word >> 16) & 0xFF); |
||||||
|
*pb->current++ = (uint8_t) ((word >> 24) & 0xFF); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** Write int8_t to the buffer. */ |
||||||
|
bool pb_i8(PayloadBuilder *pb, int8_t byte) |
||||||
|
{ |
||||||
|
return pb_u8(pb, ((union conv8){.i8 = byte}).u8); |
||||||
|
} |
||||||
|
|
||||||
|
/** Write int16_t to the buffer. */ |
||||||
|
bool pb_i16(PayloadBuilder *pb, int16_t word) |
||||||
|
{ |
||||||
|
return pb_u16(pb, ((union conv16){.i16 = word}).u16); |
||||||
|
} |
||||||
|
|
||||||
|
/** Write int32_t to the buffer. */ |
||||||
|
bool pb_i32(PayloadBuilder *pb, int32_t word) |
||||||
|
{ |
||||||
|
return pb_u32(pb, ((union conv32){.i32 = word}).u32); |
||||||
|
} |
||||||
|
|
||||||
|
/** Write 4-byte float to the buffer. */ |
||||||
|
bool pb_float(PayloadBuilder *pb, float f) |
||||||
|
{ |
||||||
|
return pb_u32(pb, ((union conv32){.f32 = f}).u32); |
||||||
|
} |
@ -0,0 +1,110 @@ |
|||||||
|
#ifndef PAYLOAD_BUILDER_H |
||||||
|
#define PAYLOAD_BUILDER_H |
||||||
|
|
||||||
|
/**
|
||||||
|
* PayloadBuilder, part of the TinyFrame utilities collection |
||||||
|
*
|
||||||
|
* (c) Ondřej Hruška, 2014-2017. MIT license. |
||||||
|
*
|
||||||
|
* The builder supports big and little endian which is selected when
|
||||||
|
* initializing it or by accessing the bigendian struct field. |
||||||
|
*
|
||||||
|
* This module helps you with building payloads (not only for TinyFrame) |
||||||
|
* |
||||||
|
* The builder performs bounds checking and calls the provided handler when
|
||||||
|
* the requested write wouldn't fit. Use the handler to realloc / flush the buffer |
||||||
|
* or report an error. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <stdbool.h> |
||||||
|
#include <stddef.h> |
||||||
|
#include "type_coerce.h" |
||||||
|
|
||||||
|
typedef struct PayloadBuilder_ PayloadBuilder; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Full buffer handler.
|
||||||
|
*
|
||||||
|
* 'needed' more bytes should be written but the end of the buffer was reached. |
||||||
|
*
|
||||||
|
* Return true if the problem was solved (e.g. buffer was flushed and the
|
||||||
|
* 'current' pointer moved to the beginning). |
||||||
|
*
|
||||||
|
* If false is returned, the 'ok' flag on the struct is set to false |
||||||
|
* and all following writes are discarded. |
||||||
|
*/ |
||||||
|
typedef bool (*pb_full_handler)(PayloadBuilder *pb, uint32_t needed); |
||||||
|
|
||||||
|
struct PayloadBuilder_ { |
||||||
|
uint8_t *start; //!< Pointer to the beginning of the buffer
|
||||||
|
uint8_t *current; //!< Pointer to the next byte to be read
|
||||||
|
uint8_t *end; //!< Pointer to the end of the buffer (start + length)
|
||||||
|
pb_full_handler full_handler; //!< Callback for buffer overrun
|
||||||
|
bool bigendian; //!< Flag to use big-endian parsing
|
||||||
|
bool ok; //!< Indicates that all reads were successful
|
||||||
|
}; |
||||||
|
|
||||||
|
// --- initializer helper macros ---
|
||||||
|
|
||||||
|
/** Start the builder. */ |
||||||
|
#define pb_start_e(buf, capacity, bigendian, full_handler) ((PayloadBuilder){buf, buf, (buf)+(capacity), full_handler, bigendian, 1}) |
||||||
|
|
||||||
|
/** Start the builder in big-endian mode */ |
||||||
|
#define pb_start_be(buf, capacity, full_handler) pb_start_e(buf, capacity, 1, full_handler) |
||||||
|
|
||||||
|
/** Start the builder in little-endian mode */ |
||||||
|
#define pb_start_le(buf, capacity, full_handler) pb_start_e(buf, capacity, 0, full_handler) |
||||||
|
|
||||||
|
/** Start the parser in little-endian mode (default) */ |
||||||
|
#define pb_start(buf, capacity, full_handler) pb_start_le(buf, capacity, full_handler) |
||||||
|
|
||||||
|
// --- utilities ---
|
||||||
|
|
||||||
|
/** Get already used bytes count */ |
||||||
|
#define pb_length(pb) ((pb)->current - (pb)->start) |
||||||
|
|
||||||
|
/** Reset the current pointer to start */ |
||||||
|
#define pb_rewind(pb) do { pb->current = pb->start; } while (0) |
||||||
|
|
||||||
|
|
||||||
|
/** Write from a buffer */ |
||||||
|
bool pb_buf(PayloadBuilder *pb, const uint8_t *buf, uint32_t len); |
||||||
|
|
||||||
|
/** Write a zero terminated string */ |
||||||
|
bool pb_string(PayloadBuilder *pb, const char *str); |
||||||
|
|
||||||
|
/** Write uint8_t to the buffer */ |
||||||
|
bool pb_u8(PayloadBuilder *pb, uint8_t byte); |
||||||
|
|
||||||
|
/** Write boolean to the buffer. */ |
||||||
|
static inline bool pb_bool(PayloadBuilder *pb, bool b) |
||||||
|
{ |
||||||
|
return pb_u8(pb, (uint8_t) b); |
||||||
|
} |
||||||
|
|
||||||
|
/** Write uint16_t to the buffer. */ |
||||||
|
bool pb_u16(PayloadBuilder *pb, uint16_t word); |
||||||
|
|
||||||
|
/** Write uint32_t to the buffer. */ |
||||||
|
bool pb_u32(PayloadBuilder *pb, uint32_t word); |
||||||
|
|
||||||
|
/** Write int8_t to the buffer. */ |
||||||
|
bool pb_i8(PayloadBuilder *pb, int8_t byte); |
||||||
|
|
||||||
|
/** Write char (int8_t) to the buffer. */ |
||||||
|
static inline bool pb_char(PayloadBuilder *pb, char c) |
||||||
|
{ |
||||||
|
return pb_i8(pb, c); |
||||||
|
} |
||||||
|
|
||||||
|
/** Write int16_t to the buffer. */ |
||||||
|
bool pb_i16(PayloadBuilder *pb, int16_t word); |
||||||
|
|
||||||
|
/** Write int32_t to the buffer. */ |
||||||
|
bool pb_i32(PayloadBuilder *pb, int32_t word); |
||||||
|
|
||||||
|
/** Write 4-byte float to the buffer. */ |
||||||
|
bool pb_float(PayloadBuilder *pb, float f); |
||||||
|
|
||||||
|
#endif // PAYLOAD_BUILDER_H
|
@ -0,0 +1,121 @@ |
|||||||
|
#include "payload_parser.h" |
||||||
|
|
||||||
|
#define pp_check_capacity(pp, needed) \ |
||||||
|
if ((pp)->current + (needed) > (pp)->end) { \
|
||||||
|
if ((pp)->empty_handler == NULL || !(pp)->empty_handler(pp, needed)) {(pp)->ok = 0;} ; \
|
||||||
|
} |
||||||
|
|
||||||
|
void pp_skip(PayloadParser *pp, uint32_t num) |
||||||
|
{ |
||||||
|
pp->current += num; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t pp_u8(PayloadParser *pp) |
||||||
|
{ |
||||||
|
pp_check_capacity(pp, 1); |
||||||
|
if (!pp->ok) return 0; |
||||||
|
|
||||||
|
return *pp->current++; |
||||||
|
} |
||||||
|
|
||||||
|
uint16_t pp_u16(PayloadParser *pp) |
||||||
|
{ |
||||||
|
pp_check_capacity(pp, 2); |
||||||
|
if (!pp->ok) return 0; |
||||||
|
|
||||||
|
uint16_t x = 0; |
||||||
|
|
||||||
|
if (pp->bigendian) { |
||||||
|
x |= *pp->current++ << 8; |
||||||
|
x |= *pp->current++; |
||||||
|
} else { |
||||||
|
x |= *pp->current++; |
||||||
|
x |= *pp->current++ << 8; |
||||||
|
} |
||||||
|
return x; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t pp_u32(PayloadParser *pp) |
||||||
|
{ |
||||||
|
pp_check_capacity(pp, 4); |
||||||
|
if (!pp->ok) return 0; |
||||||
|
|
||||||
|
uint32_t x = 0; |
||||||
|
|
||||||
|
if (pp->bigendian) { |
||||||
|
x |= (uint32_t) (*pp->current++ << 24); |
||||||
|
x |= (uint32_t) (*pp->current++ << 16); |
||||||
|
x |= (uint32_t) (*pp->current++ << 8); |
||||||
|
x |= *pp->current++; |
||||||
|
} else { |
||||||
|
x |= *pp->current++; |
||||||
|
x |= (uint32_t) (*pp->current++ << 8); |
||||||
|
x |= (uint32_t) (*pp->current++ << 16); |
||||||
|
x |= (uint32_t) (*pp->current++ << 24); |
||||||
|
} |
||||||
|
return x; |
||||||
|
} |
||||||
|
|
||||||
|
const uint8_t *pp_tail(PayloadParser *pp, uint32_t *length) |
||||||
|
{ |
||||||
|
int32_t len = (int) (pp->end - pp->current); |
||||||
|
if (!pp->ok || len <= 0) { |
||||||
|
if (length != NULL) *length = 0; |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
if (length != NULL) { |
||||||
|
*length = (uint32_t) len; |
||||||
|
} |
||||||
|
|
||||||
|
return pp->current; |
||||||
|
} |
||||||
|
|
||||||
|
/** Read int8_t from the payload. */ |
||||||
|
int8_t pp_i8(PayloadParser *pp) |
||||||
|
{ |
||||||
|
return ((union conv8) {.u8 = pp_u8(pp)}).i8; |
||||||
|
} |
||||||
|
|
||||||
|
/** Read int16_t from the payload. */ |
||||||
|
int16_t pp_i16(PayloadParser *pp) |
||||||
|
{ |
||||||
|
return ((union conv16) {.u16 = pp_u16(pp)}).i16; |
||||||
|
} |
||||||
|
|
||||||
|
/** Read int32_t from the payload. */ |
||||||
|
int32_t pp_i32(PayloadParser *pp) |
||||||
|
{ |
||||||
|
return ((union conv32) {.u32 = pp_u32(pp)}).i32; |
||||||
|
} |
||||||
|
|
||||||
|
/** Read 4-byte float from the payload. */ |
||||||
|
float pp_float(PayloadParser *pp) |
||||||
|
{ |
||||||
|
return ((union conv32) {.u32 = pp_u32(pp)}).f32; |
||||||
|
} |
||||||
|
|
||||||
|
/** Read a zstring */ |
||||||
|
uint32_t pp_string(PayloadParser *pp, char *buffer, uint32_t maxlen) |
||||||
|
{ |
||||||
|
pp_check_capacity(pp, 1); |
||||||
|
uint32_t len = 0; |
||||||
|
while (len < maxlen-1 && pp->current != pp->end) { |
||||||
|
char c = *buffer++ = *pp->current++; |
||||||
|
if (c == 0) break; |
||||||
|
len++; |
||||||
|
} |
||||||
|
*buffer = 0; |
||||||
|
return len; |
||||||
|
} |
||||||
|
|
||||||
|
/** Read a buffer */ |
||||||
|
uint32_t pp_buf(PayloadParser *pp, uint8_t *buffer, uint32_t maxlen) |
||||||
|
{ |
||||||
|
uint32_t len = 0; |
||||||
|
while (len < maxlen && pp->current != pp->end) { |
||||||
|
*buffer++ = *pp->current++; |
||||||
|
len++; |
||||||
|
} |
||||||
|
return len; |
||||||
|
} |
@ -0,0 +1,143 @@ |
|||||||
|
#ifndef PAYLOAD_PARSER_H |
||||||
|
#define PAYLOAD_PARSER_H |
||||||
|
|
||||||
|
/**
|
||||||
|
* PayloadParser, part of the TinyFrame utilities collection |
||||||
|
*
|
||||||
|
* (c) Ondřej Hruška, 2016-2017. MIT license. |
||||||
|
*
|
||||||
|
* This module helps you with parsing payloads (not only from TinyFrame). |
||||||
|
*
|
||||||
|
* The parser supports big and little-endian which is selected when
|
||||||
|
* initializing it or by accessing the bigendian struct field. |
||||||
|
* |
||||||
|
* The parser performs bounds checking and calls the provided handler when
|
||||||
|
* the requested read doesn't have enough data. Use the callback to take |
||||||
|
* appropriate action, e.g. report an error. |
||||||
|
*
|
||||||
|
* If the handler function is not defined, the pb->ok flag is set to false |
||||||
|
* (use this to check for success), and further reads won't have any effect
|
||||||
|
* and always result in 0 or empty array. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <stddef.h> |
||||||
|
#include <stdbool.h> |
||||||
|
#include "type_coerce.h" |
||||||
|
|
||||||
|
typedef struct PayloadParser_ PayloadParser; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty buffer handler.
|
||||||
|
*
|
||||||
|
* 'needed' more bytes should be read but the end was reached. |
||||||
|
*
|
||||||
|
* Return true if the problem was solved (e.g. new data loaded into
|
||||||
|
* the buffer and the 'current' pointer moved to the beginning). |
||||||
|
*
|
||||||
|
* If false is returned, the 'ok' flag on the struct is set to false |
||||||
|
* and all following reads will fail / return 0. |
||||||
|
*/ |
||||||
|
typedef bool (*pp_empty_handler)(PayloadParser *pp, uint32_t needed); |
||||||
|
|
||||||
|
struct PayloadParser_ { |
||||||
|
uint8_t *start; //!< Pointer to the beginning of the buffer
|
||||||
|
uint8_t *current; //!< Pointer to the next byte to be read
|
||||||
|
uint8_t *end; //!< Pointer to the end of the buffer (start + length)
|
||||||
|
pp_empty_handler empty_handler; //!< Callback for buffer underrun
|
||||||
|
bool bigendian; //!< Flag to use big-endian parsing
|
||||||
|
bool ok; //!< Indicates that all reads were successful
|
||||||
|
}; |
||||||
|
|
||||||
|
// --- initializer helper macros ---
|
||||||
|
|
||||||
|
/** Start the parser. */ |
||||||
|
#define pp_start_e(buf, length, bigendian, empty_handler) ((PayloadParser){buf, buf, (buf)+(length), empty_handler, bigendian, 1}) |
||||||
|
|
||||||
|
/** Start the parser in big-endian mode */ |
||||||
|
#define pp_start_be(buf, length, empty_handler) pp_start_e(buf, length, 1, empty_handler) |
||||||
|
|
||||||
|
/** Start the parser in little-endian mode */ |
||||||
|
#define pp_start_le(buf, length, empty_handler) pp_start_e(buf, length, 0, empty_handler) |
||||||
|
|
||||||
|
/** Start the parser in little-endian mode (default) */ |
||||||
|
#define pp_start(buf, length, empty_handler) pp_start_le(buf, length, empty_handler) |
||||||
|
|
||||||
|
// --- utilities ---
|
||||||
|
|
||||||
|
/** Get remaining length */ |
||||||
|
#define pp_length(pp) ((pp)->end - (pp)->current) |
||||||
|
|
||||||
|
/** Reset the current pointer to start */ |
||||||
|
#define pp_rewind(pp) do { pp->current = pp->start; } while (0) |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the remainder of the buffer. |
||||||
|
* |
||||||
|
* Returns NULL and sets 'length' to 0 if there are no bytes left. |
||||||
|
* |
||||||
|
* @param pp |
||||||
|
* @param length : here the buffer length will be stored. NULL to do not store. |
||||||
|
* @return the remaining portion of the input buffer |
||||||
|
*/ |
||||||
|
const uint8_t *pp_tail(PayloadParser *pp, uint32_t *length); |
||||||
|
|
||||||
|
/** Read uint8_t from the payload. */ |
||||||
|
uint8_t pp_u8(PayloadParser *pp); |
||||||
|
|
||||||
|
/** Read bool from the payload. */ |
||||||
|
static inline int8_t pp_bool(PayloadParser *pp) |
||||||
|
{ |
||||||
|
return pp_u8(pp) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** Skip bytes */ |
||||||
|
void pp_skip(PayloadParser *pp, uint32_t num); |
||||||
|
|
||||||
|
/** Read uint16_t from the payload. */ |
||||||
|
uint16_t pp_u16(PayloadParser *pp); |
||||||
|
|
||||||
|
/** Read uint32_t from the payload. */ |
||||||
|
uint32_t pp_u32(PayloadParser *pp); |
||||||
|
|
||||||
|
/** Read int8_t from the payload. */ |
||||||
|
int8_t pp_i8(PayloadParser *pp); |
||||||
|
|
||||||
|
/** Read char (int8_t) from the payload. */ |
||||||
|
static inline int8_t pp_char(PayloadParser *pp) |
||||||
|
{ |
||||||
|
return pp_i8(pp); |
||||||
|
} |
||||||
|
|
||||||
|
/** Read int16_t from the payload. */ |
||||||
|
int16_t pp_i16(PayloadParser *pp); |
||||||
|
|
||||||
|
/** Read int32_t from the payload. */ |
||||||
|
int32_t pp_i32(PayloadParser *pp); |
||||||
|
|
||||||
|
/** Read 4-byte float from the payload. */ |
||||||
|
float pp_float(PayloadParser *pp); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a zero-terminated string |
||||||
|
* |
||||||
|
* @param pp - parser |
||||||
|
* @param buffer - target buffer |
||||||
|
* @param maxlen - buffer size |
||||||
|
* @return actual number of bytes, excluding terminator |
||||||
|
*/ |
||||||
|
uint32_t pp_string(PayloadParser *pp, char *buffer, uint32_t maxlen); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a buffer |
||||||
|
* |
||||||
|
* @param pp - parser |
||||||
|
* @param buffer - target buffer |
||||||
|
* @param maxlen - buffer size |
||||||
|
* @return actual number of bytes, excluding terminator |
||||||
|
*/ |
||||||
|
uint32_t pp_buf(PayloadParser *pp, uint8_t *buffer, uint32_t maxlen); |
||||||
|
|
||||||
|
|
||||||
|
#endif // PAYLOAD_PARSER_H
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,31 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/09.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_SNPRINTF_H |
||||||
|
#define GEX_SNPRINTF_H |
||||||
|
|
||||||
|
#include <stdarg.h> |
||||||
|
#include <limits.h> |
||||||
|
#include "macro.h" |
||||||
|
|
||||||
|
size_t fixup_vsnprintf(char *str, size_t count, const char *fmt, va_list args); |
||||||
|
size_t fixup_snprintf(char *str, size_t count,const char *fmt,...); |
||||||
|
size_t fixup_vasprintf(char **ptr, const char *format, va_list ap); |
||||||
|
size_t fixup_asprintf(char **ptr, const char *format, ...); |
||||||
|
size_t fixup_sprintf(char *ptr, const char *format, ...); |
||||||
|
|
||||||
|
// Trap for using newlib functions
|
||||||
|
//#define vsnprintf fuck1
|
||||||
|
//#define snprintf fuck2
|
||||||
|
//#define vasprintf fuck3
|
||||||
|
//#define asprintf fuck4
|
||||||
|
//#define sprintf fuck5
|
||||||
|
|
||||||
|
#define VSNPRINTF(...) fixup_vsnprintf(__VA_ARGS__) |
||||||
|
#define SNPRINTF(...) fixup_snprintf(__VA_ARGS__) |
||||||
|
#define VASPRINTF(...) fixup_vasprintf(__VA_ARGS__) |
||||||
|
#define ASPRINTF(...) fixup_asprintf(__VA_ARGS__) |
||||||
|
#define SPRINTF(...) fixup_sprintf(__VA_ARGS__) |
||||||
|
|
||||||
|
#endif //GEX_SNPRINTF_H
|
@ -0,0 +1,87 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/04.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <task_sched.h> |
||||||
|
#include "platform.h" |
||||||
|
#include "stacksmon.h" |
||||||
|
|
||||||
|
#if USE_STACK_MONITOR |
||||||
|
|
||||||
|
struct stackhandle { |
||||||
|
const char *description; |
||||||
|
uint8_t *buffer; |
||||||
|
uint32_t len; |
||||||
|
}; |
||||||
|
|
||||||
|
#define STACK_NUM 16 |
||||||
|
static uint32_t nextidx = 0; |
||||||
|
static struct stackhandle stacks[STACK_NUM]; |
||||||
|
|
||||||
|
void stackmon_register(const char *description, void *buffer, uint32_t len) |
||||||
|
{ |
||||||
|
assert_param(nextidx < STACK_NUM); |
||||||
|
|
||||||
|
uint8_t *bv = buffer; |
||||||
|
|
||||||
|
// This is probably redundant, FreeRTOS does it too.
|
||||||
|
for (uint32_t i = 0; i < len; i++) bv[i] = 0xA5; |
||||||
|
|
||||||
|
stacks[nextidx].description = description; |
||||||
|
stacks[nextidx].buffer = bv; |
||||||
|
stacks[nextidx].len = len; |
||||||
|
nextidx++; |
||||||
|
} |
||||||
|
|
||||||
|
void stackmon_dump(void) |
||||||
|
{ |
||||||
|
PUTS("\r\n\033[1m---- STACK USAGE REPORT ---\033[m\r\n"); |
||||||
|
for (uint32_t i = 0; i < nextidx; i++) { |
||||||
|
PRINTF("\033[36m>> %s\033[m @ %p\r\n", stacks[i].description, (void *) stacks[i].buffer); |
||||||
|
struct stackhandle *stack = &stacks[i]; |
||||||
|
uint32_t free = 0; |
||||||
|
if (stack->buffer[0] != 0xA5) { |
||||||
|
PUTS(" \033[31m!!! OVERRUN !!!\033[m\r\n"); |
||||||
|
} else { |
||||||
|
for (uint32_t j = 0; j < stack->len; j++) { |
||||||
|
if (stack->buffer[j] == 0xA5) { |
||||||
|
free++; |
||||||
|
} else { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t words = ((stack->len-free)>>2)+1; |
||||||
|
if (words>stack->len>>2) words=stack->len>>2; |
||||||
|
|
||||||
|
PRINTF(" Used: \033[33m%"PRIu32" / %"PRIu32" bytes\033[m (\033[33m%"PRIu32" / %"PRIu32" words\033[m) ~ \033[33m%"PRIu32" %%\033[m\r\n", |
||||||
|
(stack->len-free), |
||||||
|
stack->len, |
||||||
|
words, |
||||||
|
stack->len>>2, |
||||||
|
(stack->len-free)*100/stack->len |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
PUTS("\033[36m>> QUEUES\033[m\r\n"); |
||||||
|
PRINTF(" Used slots: \033[33mHP %"PRIu32", LP %"PRIu32"\033[m\r\n", |
||||||
|
jobQueHighWaterMarkHP, jobQueHighWaterMarkLP); |
||||||
|
|
||||||
|
PRINTF("\033[1m---------------------------\033[m\r\n\r\n"); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void stackmon_check_canaries(void) |
||||||
|
{ |
||||||
|
for (uint32_t i = 0; i < nextidx; i++) { |
||||||
|
struct stackhandle *stack = &stacks[i]; |
||||||
|
if (stack->buffer[0] != 0xA5) { |
||||||
|
dbg("\r\n\033[31;1m!!!! STACK \"%s\" OVERRUN - CANARY IS DEAD !!!!\033[m\r\n", stack->description); |
||||||
|
stackmon_dump(); |
||||||
|
trap("ABORT"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,20 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/04.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_STACKSMON_H |
||||||
|
#define GEX_STACKSMON_H |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
|
||||||
|
#if USE_STACK_MONITOR |
||||||
|
void stackmon_check_canaries(void); |
||||||
|
void stackmon_dump(void); |
||||||
|
void stackmon_register(const char *description, void *buffer, uint32_t len); |
||||||
|
#else |
||||||
|
#define stackmon_check_canaries() do {} while(0) |
||||||
|
#define stackmon_dump() do {} while(0) |
||||||
|
#define stackmon_register(description, buffer, len) do {} while(0) |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif //GEX_STACKSMON_H
|
@ -0,0 +1,57 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "str_utils.h" |
||||||
|
#include "avrlibc.h" |
||||||
|
|
||||||
|
bool str_parse_yn(const char *str, bool *suc) |
||||||
|
{ |
||||||
|
if (streq(str, "Y")) return true; |
||||||
|
if (streq(str, "1")) return true; |
||||||
|
if (streq(str, "YES")) return true; |
||||||
|
|
||||||
|
if (streq(str, "N")) return false; |
||||||
|
if (streq(str, "0")) return false; |
||||||
|
if (streq(str, "NO")) return false; |
||||||
|
|
||||||
|
*suc = false; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t str_parse_01(const char *str, const char *a, const char *b, bool *suc) |
||||||
|
{ |
||||||
|
if (streq(str, a)) return 0; |
||||||
|
if (streq(str, b)) return 1; |
||||||
|
|
||||||
|
*suc = false; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t str_parse_012(const char *str, const char *a, const char *b, const char *c, bool *suc) |
||||||
|
{ |
||||||
|
if (streq(str, a)) return 0; |
||||||
|
if (streq(str, b)) return 1; |
||||||
|
if (streq(str, c)) return 2; |
||||||
|
|
||||||
|
*suc = false; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool str_parse_pin(const char *value, char *targetName, uint8_t *targetNumber) |
||||||
|
{ |
||||||
|
// discard leading 'P'
|
||||||
|
if (value[0] == 'P') { |
||||||
|
value++; |
||||||
|
} |
||||||
|
|
||||||
|
size_t len = strlen(value); |
||||||
|
if (len<2||len>3) return false; |
||||||
|
|
||||||
|
*targetName = (uint8_t) value[0]; |
||||||
|
if (!(*targetName >= 'A' && *targetName <= 'H')) return false; |
||||||
|
|
||||||
|
// lets just hope it's OK
|
||||||
|
*targetNumber = (uint8_t) avr_atoi(value + 1); |
||||||
|
return true; |
||||||
|
} |
@ -0,0 +1,59 @@ |
|||||||
|
#ifndef PLATFORSTR_UTILS_H |
||||||
|
#define PLATFORSTR_UTILS_H |
||||||
|
|
||||||
|
#include <stdbool.h> |
||||||
|
#include <string.h> |
||||||
|
#include "stringbuilder.h" |
||||||
|
|
||||||
|
static inline bool __attribute__((const)) |
||||||
|
streq(const char *restrict str1, const char *restrict str2) |
||||||
|
{ |
||||||
|
return strcmp(str1, str2) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
static inline bool __attribute__((const)) |
||||||
|
strcaseq(const char *restrict str1, const char *restrict str2) |
||||||
|
{ |
||||||
|
return strcasecmp(str1, str2) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
static inline bool __attribute__((const)) |
||||||
|
strneq(const char *restrict str1, const char *restrict str2, uint32_t limit) |
||||||
|
{ |
||||||
|
return strncmp(str1, str2, limit) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
static inline bool __attribute__((const)) |
||||||
|
strncaseq(const char *restrict str1, const char *restrict str2, uint32_t limit) |
||||||
|
{ |
||||||
|
return strncasecmp(str1, str2, limit) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
static inline bool __attribute__((const)) |
||||||
|
strstarts(const char *restrict str, const char *restrict prefix) |
||||||
|
{ |
||||||
|
return strncmp(str, prefix, strlen(prefix)) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
static inline char __attribute__((const)) |
||||||
|
last_char_n(const char *str, uint32_t nth) |
||||||
|
{ |
||||||
|
return str[strlen(str) - nth]; |
||||||
|
} |
||||||
|
|
||||||
|
static inline char __attribute__((const)) |
||||||
|
last_char(const char *str) |
||||||
|
{ |
||||||
|
return str[strlen(str) - 1]; |
||||||
|
} |
||||||
|
|
||||||
|
bool str_parse_yn(const char *str, bool *suc); |
||||||
|
uint8_t str_parse_01(const char *str, const char *a, const char *b, bool *suc); |
||||||
|
uint8_t str_parse_012(const char *str, const char *a, const char *b, const char *c, bool *suc); |
||||||
|
|
||||||
|
#define str_01(cond, a, b) ((cond) ? (b) : (a)) |
||||||
|
#define str_yn(cond) ((cond) ? ("Y") : ("N")) |
||||||
|
|
||||||
|
bool str_parse_pin(const char *str, char *targetName, uint8_t *targetNumber); |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,114 @@ |
|||||||
|
/**
|
||||||
|
* @file stringbuilder.c |
||||||
|
* |
||||||
|
* ADAPTED FROM: |
||||||
|
* |
||||||
|
* DAPLink Interface Firmware |
||||||
|
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may |
||||||
|
* not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "stringbuilder.h" |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
uint32_t strb_write_hex8(char *str, uint8_t value) |
||||||
|
{ |
||||||
|
static const char nybble_chars[] = "0123456789abcdef"; |
||||||
|
*(str + 0) = nybble_chars[(value >> 4) & 0x0F ]; |
||||||
|
*(str + 1) = nybble_chars[(value >> 0) & 0x0F ]; |
||||||
|
return 2; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t strb_write_hex16(char *str, uint16_t value) |
||||||
|
{ |
||||||
|
uint32_t pos = 0; |
||||||
|
pos += strb_write_hex8(str + pos, (uint8_t) ((value >> 8) & 0xFF)); |
||||||
|
pos += strb_write_hex8(str + pos, (uint8_t) ((value >> 0) & 0xFF)); |
||||||
|
return pos; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t strb_write_hex32(char *str, uint32_t value) |
||||||
|
{ |
||||||
|
uint32_t pos = 0; |
||||||
|
pos += strb_write_hex8(str + pos, (uint8_t) ((value >> 0x18) & 0xFF)); |
||||||
|
pos += strb_write_hex8(str + pos, (uint8_t) ((value >> 0x10) & 0xFF)); |
||||||
|
pos += strb_write_hex8(str + pos, (uint8_t) ((value >> 0x08) & 0xFF)); |
||||||
|
pos += strb_write_hex8(str + pos, (uint8_t) ((value >> 0x00) & 0xFF)); |
||||||
|
return pos; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t strb_write_uint32(char *str, uint32_t value) |
||||||
|
{ |
||||||
|
uint32_t temp_val; |
||||||
|
uint32_t digits; |
||||||
|
uint32_t i; |
||||||
|
// Count the number of digits
|
||||||
|
digits = 0; |
||||||
|
temp_val = value; |
||||||
|
|
||||||
|
while (temp_val > 0) { |
||||||
|
temp_val /= 10; |
||||||
|
digits += 1; |
||||||
|
} |
||||||
|
|
||||||
|
if (digits <= 0) { |
||||||
|
digits = 1; |
||||||
|
} |
||||||
|
|
||||||
|
// Write the number
|
||||||
|
for (i = 0; i < digits; i++) { |
||||||
|
str[digits - i - 1] = (char) ('0' + (value % 10)); |
||||||
|
value /= 10; |
||||||
|
} |
||||||
|
|
||||||
|
return digits; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t strb_write_uint32_zp(char *str, uint32_t value, uint16_t total_size) |
||||||
|
{ |
||||||
|
uint32_t size; |
||||||
|
// Get the size of value
|
||||||
|
size = strb_write_uint32(str, value); |
||||||
|
|
||||||
|
if (size >= total_size) { |
||||||
|
return size; |
||||||
|
} |
||||||
|
|
||||||
|
// Zero fill
|
||||||
|
memset(str, '0', total_size); |
||||||
|
// Write value
|
||||||
|
strb_write_uint32(str + (total_size - size), value); |
||||||
|
return total_size; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t strb_write_string(char *str, const char *data) |
||||||
|
{ |
||||||
|
uint32_t pos = 0; |
||||||
|
|
||||||
|
while (0 != data[pos]) { |
||||||
|
str[pos] = data[pos]; |
||||||
|
pos++; |
||||||
|
} |
||||||
|
|
||||||
|
return pos; |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
@ -0,0 +1,36 @@ |
|||||||
|
/**
|
||||||
|
* @file stringbuilder.h |
||||||
|
* |
||||||
|
* ADAPTED FROM: |
||||||
|
* |
||||||
|
* DAPLink Interface Firmware |
||||||
|
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may |
||||||
|
* not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef STRINGBUILDER_H |
||||||
|
#define STRINGBUILDER_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
// Write the value to the address specified and return the size
|
||||||
|
uint32_t strb_write_hex8(char *str, uint8_t value); |
||||||
|
uint32_t strb_write_hex16(char *str, uint16_t value); |
||||||
|
uint32_t strb_write_hex32(char *str, uint32_t value); |
||||||
|
uint32_t strb_write_uint32(char *str, uint32_t value); |
||||||
|
uint32_t strb_write_uint32_zp(char *str, uint32_t value, uint16_t total_size); |
||||||
|
uint32_t strb_write_string(char *str, const char *data); |
||||||
|
|
||||||
|
#endif //STRINGBUILDER_H
|
@ -0,0 +1,32 @@ |
|||||||
|
#ifndef TYPE_COERCE_H |
||||||
|
#define TYPE_COERCE_H |
||||||
|
|
||||||
|
/**
|
||||||
|
* Structs for conversion between types, |
||||||
|
* part of the TinyFrame utilities collection |
||||||
|
*
|
||||||
|
* (c) Ondřej Hruška, 2016-2017. MIT license. |
||||||
|
*
|
||||||
|
* This is a support header file for PayloadParser and PayloadBuilder. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <stddef.h> |
||||||
|
|
||||||
|
union conv8 { |
||||||
|
uint8_t u8; |
||||||
|
int8_t i8; |
||||||
|
}; |
||||||
|
|
||||||
|
union conv16 { |
||||||
|
uint16_t u16; |
||||||
|
int16_t i16; |
||||||
|
}; |
||||||
|
|
||||||
|
union conv32 { |
||||||
|
uint32_t u32; |
||||||
|
int32_t i32; |
||||||
|
float f32; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // TYPE_COERCE_H
|
@ -0,0 +1,10 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/12/08.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_VERSION_H |
||||||
|
#define GEX_VERSION_H |
||||||
|
|
||||||
|
#define GEX_VERSION "0.0.1" |
||||||
|
|
||||||
|
#endif //GEX_VERSION_H
|
@ -0,0 +1,245 @@ |
|||||||
|
/**
|
||||||
|
* @file file_stream.c |
||||||
|
* @brief Implementation of file_stream.h |
||||||
|
* |
||||||
|
* DAPLink Interface Firmware |
||||||
|
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may |
||||||
|
* not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <platform/status_led.h> |
||||||
|
#include "platform.h" |
||||||
|
#include "utils/ini_parser.h" |
||||||
|
#include "framework/settings.h" |
||||||
|
#include "file_stream.h" |
||||||
|
#include "vfs_manager.h" |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
STREAM_STATE_CLOSED, |
||||||
|
STREAM_STATE_OPEN, |
||||||
|
STREAM_STATE_END, |
||||||
|
STREAM_STATE_ERROR |
||||||
|
} stream_state_t; |
||||||
|
|
||||||
|
typedef bool (*stream_detect_cb_t)(const uint8_t *data, uint32_t size); |
||||||
|
typedef error_t (*stream_open_cb_t)(void *state); |
||||||
|
typedef error_t (*stream_write_cb_t)(void *state, const uint8_t *data, uint32_t size); |
||||||
|
typedef error_t (*stream_close_cb_t)(void *state); |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
stream_detect_cb_t detect; |
||||||
|
stream_open_cb_t open; |
||||||
|
stream_write_cb_t write; |
||||||
|
stream_close_cb_t close; |
||||||
|
} stream_t; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
size_t file_pos; |
||||||
|
} conf_state_t; |
||||||
|
|
||||||
|
typedef union { |
||||||
|
conf_state_t conf; |
||||||
|
} shared_state_t; |
||||||
|
|
||||||
|
static bool detect_conf(const uint8_t *data, uint32_t size); |
||||||
|
static error_t open_conf(void *state); |
||||||
|
static error_t write_conf(void *state, const uint8_t *data, uint32_t size); |
||||||
|
static error_t close_conf(void *state); |
||||||
|
|
||||||
|
stream_t stream[] = { |
||||||
|
{detect_conf, open_conf, write_conf, close_conf}, // STREAM_TYPE_CONF
|
||||||
|
}; |
||||||
|
COMPILER_ASSERT(ELEMENTS_IN_ARRAY(stream) == STREAM_TYPE_COUNT); |
||||||
|
// STREAM_TYPE_NONE must not be included in count
|
||||||
|
COMPILER_ASSERT(STREAM_TYPE_NONE > STREAM_TYPE_COUNT); |
||||||
|
|
||||||
|
static shared_state_t shared_state; |
||||||
|
static stream_state_t stream_state = STREAM_STATE_CLOSED; |
||||||
|
static stream_t *current_stream = 0; |
||||||
|
|
||||||
|
stream_type_t stream_start_identify(const uint8_t *data, uint32_t size) |
||||||
|
{ |
||||||
|
stream_type_t i; |
||||||
|
|
||||||
|
for (i = STREAM_TYPE_START; i < STREAM_TYPE_COUNT; i++) { |
||||||
|
if (stream[i].detect(data, size)) { |
||||||
|
return i; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return STREAM_TYPE_NONE; |
||||||
|
} |
||||||
|
|
||||||
|
// Identify the file type from its extension
|
||||||
|
stream_type_t stream_type_from_name(const vfs_filename_t filename) |
||||||
|
{ |
||||||
|
// 8.3 file names must be in upper case
|
||||||
|
if (0 == strncmp("INI", &filename[8], 3)) { |
||||||
|
// This is used only to verify we identified the file correctly (?)
|
||||||
|
return STREAM_TYPE_CONF; |
||||||
|
} else { |
||||||
|
return STREAM_TYPE_NONE; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
error_t stream_open(stream_type_t stream_type) |
||||||
|
{ |
||||||
|
error_t status; |
||||||
|
|
||||||
|
// Stream must not be open already
|
||||||
|
if (stream_state != STREAM_STATE_CLOSED) { |
||||||
|
vfs_printf("!! Stream is not closed, cant open"); |
||||||
|
assert_param(0); |
||||||
|
return E_INTERNAL; |
||||||
|
} |
||||||
|
|
||||||
|
// Stream must be of a supported type
|
||||||
|
if (stream_type >= STREAM_TYPE_COUNT) { |
||||||
|
vfs_printf("!! Stream bad type"); |
||||||
|
assert_param(0); |
||||||
|
return E_INTERNAL; |
||||||
|
} |
||||||
|
|
||||||
|
StatusLed_On(STATUS_DISK_BUSY); |
||||||
|
// TODO create a thread...?
|
||||||
|
|
||||||
|
// Initialize all variables
|
||||||
|
memset(&shared_state, 0, sizeof(shared_state)); |
||||||
|
stream_state = STREAM_STATE_OPEN; |
||||||
|
current_stream = &stream[stream_type]; |
||||||
|
// Initialize the specified stream
|
||||||
|
status = current_stream->open(&shared_state); |
||||||
|
|
||||||
|
if (E_SUCCESS != status) { |
||||||
|
stream_state = STREAM_STATE_ERROR; |
||||||
|
vfs_printf("!! not success"); |
||||||
|
} |
||||||
|
|
||||||
|
return status; |
||||||
|
} |
||||||
|
|
||||||
|
error_t stream_write(const uint8_t *data, uint32_t size) |
||||||
|
{ |
||||||
|
error_t status; |
||||||
|
|
||||||
|
// Stream must be open already
|
||||||
|
if (stream_state != STREAM_STATE_OPEN) { |
||||||
|
vfs_printf("!! Stream is not open, cant write"); |
||||||
|
assert_param(0); |
||||||
|
return E_INTERNAL; |
||||||
|
} |
||||||
|
|
||||||
|
// Check thread after checking state since the stream thread is
|
||||||
|
// set only if stream_open has been called
|
||||||
|
//stream_thread_assert(); // ???
|
||||||
|
|
||||||
|
// Write to stream
|
||||||
|
status = current_stream->write(&shared_state, data, size); |
||||||
|
|
||||||
|
if (E_SUCCESS_DONE == status) { |
||||||
|
vfs_printf("Stream DONE"); |
||||||
|
stream_state = STREAM_STATE_END; |
||||||
|
} else if ((E_SUCCESS_DONE_OR_CONTINUE == status) || (E_SUCCESS == status)) { |
||||||
|
// Stream should remain in the open state
|
||||||
|
assert_param(STREAM_STATE_OPEN == stream_state); |
||||||
|
vfs_printf("Stream may close or get more data.,,"); |
||||||
|
} else { |
||||||
|
stream_state = STREAM_STATE_ERROR; |
||||||
|
vfs_printf("!! FAIL in stream"); |
||||||
|
} |
||||||
|
|
||||||
|
return status; |
||||||
|
} |
||||||
|
|
||||||
|
error_t stream_close(void) |
||||||
|
{ |
||||||
|
error_t status; |
||||||
|
|
||||||
|
// Stream must not be closed already
|
||||||
|
if (STREAM_STATE_CLOSED == stream_state) { |
||||||
|
vfs_printf("!! Stream already closed"); |
||||||
|
assert_param(0); |
||||||
|
return E_INTERNAL; |
||||||
|
} |
||||||
|
|
||||||
|
// Check thread after checking state since the stream thread is
|
||||||
|
// set only if stream_open has been called
|
||||||
|
// stream_thread_assert(); // ???
|
||||||
|
// Close stream
|
||||||
|
StatusLed_Off(STATUS_DISK_BUSY); |
||||||
|
status = current_stream->close(&shared_state); |
||||||
|
stream_state = STREAM_STATE_CLOSED; |
||||||
|
return status; |
||||||
|
} |
||||||
|
|
||||||
|
static bool detect_conf(const uint8_t *data, uint32_t size) |
||||||
|
{ |
||||||
|
// Here we have received the first sector of a potential INI file (assuming it's a whole sector, since
|
||||||
|
// this is called from the MSC driver).
|
||||||
|
//
|
||||||
|
// We can start parsing and look for the first section or some other marker. The file name is yet unknown
|
||||||
|
// and may not be known for a while - we cannot use that to detect anything, unless we buffer the entire file
|
||||||
|
// (a bad idea)
|
||||||
|
|
||||||
|
// TODO detect config file
|
||||||
|
return data[0] == '#'; // here we just assume everything is INI
|
||||||
|
} |
||||||
|
|
||||||
|
static void iniparser_cb(const char *section, const char *key, const char *value, void *userData) |
||||||
|
{ |
||||||
|
settings_read_ini(section, key, value); |
||||||
|
} |
||||||
|
|
||||||
|
static error_t open_conf(void *state) |
||||||
|
{ |
||||||
|
conf_state_t *conf = state; |
||||||
|
conf->file_pos = 0; |
||||||
|
vfs_printf("\r\n---- INI OPEN! ----"); |
||||||
|
|
||||||
|
settings_read_ini_begin(); |
||||||
|
ini_parse_begin(iniparser_cb, NULL); |
||||||
|
|
||||||
|
return E_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
static error_t write_conf(void *state, const uint8_t *data, uint32_t size) |
||||||
|
{ |
||||||
|
conf_state_t *conf = state; |
||||||
|
conf->file_pos += size; |
||||||
|
|
||||||
|
vfs_printf("Writing INI - RX %d bytes", size); |
||||||
|
vfs_printf_nonl("\033[92m", 5); |
||||||
|
vfs_printf_nonl((const char *) data, size); |
||||||
|
vfs_printf_nonl("\033[0m\r\n", 6); |
||||||
|
|
||||||
|
ini_parse((const char *) data, size); |
||||||
|
|
||||||
|
return E_SUCCESS_DONE_OR_CONTINUE; // indicate we don't really know if it's over or not
|
||||||
|
// TODO use some marker for EOF in the actual config files
|
||||||
|
} |
||||||
|
|
||||||
|
static error_t close_conf(void *state) |
||||||
|
{ |
||||||
|
conf_state_t *conf = state; |
||||||
|
vfs_printf("Close INI, total bytes = %d", conf->file_pos); |
||||||
|
|
||||||
|
ini_parse_end(); |
||||||
|
settings_read_ini_end(); |
||||||
|
|
||||||
|
// force a full remount to have the changes be visible
|
||||||
|
vfs_mngr_fs_remount(true); |
||||||
|
|
||||||
|
return E_SUCCESS; |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
/**
|
||||||
|
* @file file_stream.h |
||||||
|
* @brief Different file stream parsers that are supported |
||||||
|
* |
||||||
|
* DAPLink Interface Firmware |
||||||
|
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may |
||||||
|
* not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef VFS_FILE_STREAM_H |
||||||
|
#define VFS_FILE_STREAM_H |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "virtual_fs.h" |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
STREAM_TYPE_START = 0, |
||||||
|
|
||||||
|
STREAM_TYPE_CONF = STREAM_TYPE_START, |
||||||
|
// STREAM_TYPE_HEX,
|
||||||
|
|
||||||
|
// Add new stream types here
|
||||||
|
|
||||||
|
STREAM_TYPE_COUNT, |
||||||
|
|
||||||
|
STREAM_TYPE_NONE |
||||||
|
} stream_type_t; |
||||||
|
|
||||||
|
// Stateless function to identify a filestream by its contents
|
||||||
|
stream_type_t stream_start_identify(const uint8_t *data, uint32_t size); |
||||||
|
|
||||||
|
// Stateless function to identify a filestream by its name
|
||||||
|
stream_type_t stream_type_from_name(const vfs_filename_t filename); |
||||||
|
|
||||||
|
error_t stream_open(stream_type_t stream_type); |
||||||
|
|
||||||
|
error_t stream_write(const uint8_t *data, uint32_t size); |
||||||
|
|
||||||
|
error_t stream_close(void); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif//VFS_FILE_STREAM_H
|
@ -0,0 +1,949 @@ |
|||||||
|
/**
|
||||||
|
* @file vfs_manager.c |
||||||
|
* @brief Implementation of vfs_manager.h |
||||||
|
* |
||||||
|
* DAPLink Interface Firmware |
||||||
|
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may |
||||||
|
* not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <task_main.h> |
||||||
|
#include "platform.h" |
||||||
|
#include "task_main.h" |
||||||
|
#include "virtual_fs.h" |
||||||
|
#include "vfs_manager.h" |
||||||
|
#include "file_stream.h" |
||||||
|
|
||||||
|
#define INVALID_TIMEOUT_MS 0xFFFFFFFF |
||||||
|
#define MAX_EVENT_TIME_MS 60000 |
||||||
|
|
||||||
|
#define CONNECT_DELAY_MS 0 |
||||||
|
#define RECONNECT_DELAY_MS 2500 // Must be above 1s for windows (more for linux)
|
||||||
|
// TRANSFER_IN_PROGRESS
|
||||||
|
#define DISCONNECT_DELAY_TRANSFER_TIMEOUT_MS 500 // was 20000 - this is triggered after a partial file upload
|
||||||
|
// TRANSFER_CAN_BE_FINISHED
|
||||||
|
#define DISCONNECT_DELAY_TRANSFER_IDLE_MS 500 |
||||||
|
// TRANSFER_NOT_STARTED || TRASNFER_FINISHED
|
||||||
|
#define DISCONNECT_DELAY_MS 500 |
||||||
|
|
||||||
|
// Make sure none of the delays exceed the max time
|
||||||
|
COMPILER_ASSERT(CONNECT_DELAY_MS < MAX_EVENT_TIME_MS); |
||||||
|
COMPILER_ASSERT(RECONNECT_DELAY_MS < MAX_EVENT_TIME_MS); |
||||||
|
COMPILER_ASSERT(DISCONNECT_DELAY_TRANSFER_TIMEOUT_MS < MAX_EVENT_TIME_MS); |
||||||
|
COMPILER_ASSERT(DISCONNECT_DELAY_TRANSFER_IDLE_MS < MAX_EVENT_TIME_MS); |
||||||
|
COMPILER_ASSERT(DISCONNECT_DELAY_MS < MAX_EVENT_TIME_MS); |
||||||
|
|
||||||
|
volatile vfs_info_t vfs_info; |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
TRANSFER_NOT_STARTED, |
||||||
|
TRANSFER_IN_PROGRESS, |
||||||
|
TRANSFER_CAN_BE_FINISHED, |
||||||
|
TRASNFER_FINISHED, |
||||||
|
} transfer_state_t; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
vfs_file_t file_to_program; // A pointer to the directory entry of the file being programmed
|
||||||
|
vfs_sector_t start_sector; // Start sector of the file being programmed
|
||||||
|
vfs_sector_t file_next_sector; // Expected next sector of the file
|
||||||
|
vfs_sector_t last_ooo_sector; // Last out of order sector within the file
|
||||||
|
uint32_t size_processed; // The number of bytes processed by the stream
|
||||||
|
uint32_t file_size; // Size of the file indicated by root dir. Only allowed to increase
|
||||||
|
uint32_t size_transferred; // The number of bytes transferred
|
||||||
|
transfer_state_t transfer_state;// Transfer state
|
||||||
|
bool stream_open; // State of the stream
|
||||||
|
bool stream_started; // Stream processing started. This only gets reset remount
|
||||||
|
bool stream_finished; // Stream processing is done. This only gets reset remount
|
||||||
|
bool stream_optional_finish; // True if the stream processing can be considered done
|
||||||
|
bool file_info_optional_finish; // True if the file transfer can be considered done
|
||||||
|
bool transfer_timeout; // Set if the transfer was finished because of a timeout. This only gets reset remount
|
||||||
|
stream_type_t stream; // Current stream or STREAM_TYPE_NONE is stream is closed. This only gets reset remount
|
||||||
|
} file_transfer_state_t; |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
VFS_MNGR_STATE_DISCONNECTED, |
||||||
|
VFS_MNGR_STATE_RECONNECTING, |
||||||
|
VFS_MNGR_STATE_CONNECTED |
||||||
|
} vfs_mngr_state_t; |
||||||
|
|
||||||
|
static const file_transfer_state_t default_transfer_state = { |
||||||
|
VFS_FILE_INVALID, |
||||||
|
VFS_INVALID_SECTOR, |
||||||
|
VFS_INVALID_SECTOR, |
||||||
|
VFS_INVALID_SECTOR, |
||||||
|
0, |
||||||
|
0, |
||||||
|
0, |
||||||
|
TRANSFER_NOT_STARTED, |
||||||
|
false, |
||||||
|
false, |
||||||
|
false, |
||||||
|
false, |
||||||
|
false, |
||||||
|
false, |
||||||
|
STREAM_TYPE_NONE, |
||||||
|
}; |
||||||
|
|
||||||
|
//static uint32_t usb_buffer[VFS_SECTOR_SIZE / sizeof(uint32_t)];
|
||||||
|
static error_t fail_reason = E_SUCCESS; |
||||||
|
static file_transfer_state_t file_transfer_state; |
||||||
|
|
||||||
|
// These variables can be access from multiple threads
|
||||||
|
// so access to them must be synchronized
|
||||||
|
static vfs_mngr_state_t vfs_state; |
||||||
|
static vfs_mngr_state_t vfs_state_next; |
||||||
|
static bool vfs_next_remount_full = false; |
||||||
|
static uint32_t time_usb_idle; |
||||||
|
|
||||||
|
static osStaticMutexDef_t vfsMutexControlBlock; |
||||||
|
static osSemaphoreId vfsMutexHandle = NULL; |
||||||
|
|
||||||
|
// Synchronization functions
|
||||||
|
static void sync_init(void); |
||||||
|
static void sync_assert_usb_thread(void); |
||||||
|
static void sync_lock(void); |
||||||
|
static void sync_unlock(void); |
||||||
|
|
||||||
|
static bool changing_state(void); |
||||||
|
static void build_filesystem(void); |
||||||
|
static void file_change_handler(const vfs_filename_t filename, vfs_file_change_t change, vfs_file_t file, vfs_file_t new_file_data); |
||||||
|
static void file_data_handler(uint32_t sector, const uint8_t *buf, uint32_t num_of_sectors); |
||||||
|
static bool ready_for_state_change(void); |
||||||
|
static void abort_remount(void); |
||||||
|
|
||||||
|
static void transfer_update_file_info(vfs_file_t file, uint32_t start_sector, uint32_t size, stream_type_t stream); |
||||||
|
static void transfer_reset_file_info(void); |
||||||
|
static void transfer_stream_open(stream_type_t stream, uint32_t start_sector); |
||||||
|
static void transfer_stream_data(uint32_t sector, const uint8_t *data, uint32_t size); |
||||||
|
static void transfer_update_state(error_t status); |
||||||
|
|
||||||
|
|
||||||
|
void vfs_mngr_fs_enable(bool enable) |
||||||
|
{ |
||||||
|
sync_lock(); |
||||||
|
vfs_printf("Enable = %d", enable); |
||||||
|
|
||||||
|
if (enable) { |
||||||
|
if (VFS_MNGR_STATE_DISCONNECTED == vfs_state_next) { |
||||||
|
vfs_printf(" Switch to Connected"); |
||||||
|
vfs_state_next = VFS_MNGR_STATE_CONNECTED; |
||||||
|
} else { |
||||||
|
vfs_printf(" no switch"); |
||||||
|
}; |
||||||
|
} else { |
||||||
|
vfs_printf(" Switch to DISconnected"); |
||||||
|
vfs_state_next = VFS_MNGR_STATE_DISCONNECTED; |
||||||
|
} |
||||||
|
sync_unlock(); |
||||||
|
} |
||||||
|
|
||||||
|
void vfs_mngr_fs_remount(bool force_full) |
||||||
|
{ |
||||||
|
sync_lock(); |
||||||
|
|
||||||
|
// Only start a remount if in the connected state and not in a transition
|
||||||
|
if (!changing_state() && (VFS_MNGR_STATE_CONNECTED == vfs_state)) { |
||||||
|
vfs_state_next = VFS_MNGR_STATE_RECONNECTING; |
||||||
|
} |
||||||
|
|
||||||
|
vfs_next_remount_full |= force_full; |
||||||
|
|
||||||
|
sync_unlock(); |
||||||
|
} |
||||||
|
|
||||||
|
void vfs_mngr_init(bool enable) |
||||||
|
{ |
||||||
|
vfs_printf("vfs_mngr_init"); |
||||||
|
sync_assert_usb_thread(); |
||||||
|
build_filesystem(); |
||||||
|
|
||||||
|
if (enable) { |
||||||
|
vfs_state = VFS_MNGR_STATE_CONNECTED; |
||||||
|
vfs_state_next = VFS_MNGR_STATE_CONNECTED; |
||||||
|
vfs_info.MediaReady = 1; |
||||||
|
} else { |
||||||
|
vfs_state = VFS_MNGR_STATE_DISCONNECTED; |
||||||
|
vfs_state_next = VFS_MNGR_STATE_DISCONNECTED; |
||||||
|
vfs_info.MediaReady = 0; |
||||||
|
} |
||||||
|
vfs_info.MediaChanged = 0; |
||||||
|
} |
||||||
|
|
||||||
|
void vfs_mngr_periodic(uint32_t elapsed_ms) |
||||||
|
{ |
||||||
|
bool change_state; |
||||||
|
vfs_mngr_state_t vfs_state_local; |
||||||
|
vfs_mngr_state_t vfs_state_local_prev; |
||||||
|
sync_assert_usb_thread(); |
||||||
|
sync_lock(); |
||||||
|
|
||||||
|
// Return immediately if the desired state has been reached
|
||||||
|
if (!changing_state()) { |
||||||
|
sync_unlock(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
change_state = ready_for_state_change(); |
||||||
|
|
||||||
|
if (time_usb_idle < MAX_EVENT_TIME_MS) { |
||||||
|
time_usb_idle += elapsed_ms; |
||||||
|
} |
||||||
|
|
||||||
|
if (!change_state) { |
||||||
|
sync_unlock(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
vfs_printf("vfs_mngr_periodic()\r\n"); |
||||||
|
vfs_printf(" time_usb_idle=%i\r\n", time_usb_idle); |
||||||
|
vfs_printf(" transfer_state=%i\r\n", file_transfer_state.transfer_state); |
||||||
|
// Transition to new state
|
||||||
|
vfs_state_local_prev = vfs_state; |
||||||
|
vfs_state = vfs_state_next; |
||||||
|
|
||||||
|
switch (vfs_state) { |
||||||
|
case VFS_MNGR_STATE_RECONNECTING: |
||||||
|
// Transition back to the connected state
|
||||||
|
vfs_state_next = VFS_MNGR_STATE_CONNECTED; |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
// No state change logic required in other states
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
vfs_state_local = vfs_state; |
||||||
|
time_usb_idle = 0; |
||||||
|
sync_unlock(); |
||||||
|
// Processing when leaving a state
|
||||||
|
vfs_printf(" state %i->%i\r\n", vfs_state_local_prev, vfs_state_local); |
||||||
|
|
||||||
|
bool want_notify_only = false; // Use this if the transfer timed out and we dont need full reconnect
|
||||||
|
switch (vfs_state_local_prev) { |
||||||
|
case VFS_MNGR_STATE_DISCONNECTED: |
||||||
|
// No action needed
|
||||||
|
break; |
||||||
|
|
||||||
|
case VFS_MNGR_STATE_RECONNECTING: |
||||||
|
// No action needed
|
||||||
|
break; |
||||||
|
|
||||||
|
case VFS_MNGR_STATE_CONNECTED: |
||||||
|
// Close ongoing transfer if there is one
|
||||||
|
if (file_transfer_state.transfer_state != TRASNFER_FINISHED) { |
||||||
|
vfs_printf(" transfer timeout\r\n"); |
||||||
|
file_transfer_state.transfer_timeout = true; |
||||||
|
transfer_update_state(E_SUCCESS); |
||||||
|
want_notify_only = true; |
||||||
|
} |
||||||
|
|
||||||
|
assert_param(TRASNFER_FINISHED == file_transfer_state.transfer_state); |
||||||
|
vfs_user_disconnecting(); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// Maybe make this configurable?
|
||||||
|
//want_notify_only = false;
|
||||||
|
|
||||||
|
// Processing when entering a state
|
||||||
|
switch (vfs_state_local) { |
||||||
|
case VFS_MNGR_STATE_DISCONNECTED: |
||||||
|
vfs_printf("+DISCON"); |
||||||
|
if (want_notify_only && !vfs_next_remount_full) { |
||||||
|
vfs_info.MediaChanged = 1; |
||||||
|
vfs_printf("Notify media change"); |
||||||
|
} else { |
||||||
|
vfs_info.MediaReady = 0; |
||||||
|
vfs_printf("Reconnect mass storage"); |
||||||
|
} |
||||||
|
vfs_next_remount_full = false; |
||||||
|
break; |
||||||
|
|
||||||
|
case VFS_MNGR_STATE_RECONNECTING: |
||||||
|
vfs_printf("+RECON"); |
||||||
|
if (want_notify_only && !vfs_next_remount_full) { |
||||||
|
vfs_info.MediaChanged = 1; |
||||||
|
vfs_printf("Notify media change"); |
||||||
|
} else { |
||||||
|
vfs_info.MediaReady = 0; |
||||||
|
vfs_printf("Reconnect mass storage"); |
||||||
|
} |
||||||
|
vfs_next_remount_full = false; |
||||||
|
break; |
||||||
|
|
||||||
|
case VFS_MNGR_STATE_CONNECTED: |
||||||
|
vfs_printf("+CONNECTED"); |
||||||
|
build_filesystem(); |
||||||
|
vfs_info.MediaReady = 1; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
error_t vfs_mngr_get_transfer_status(void) |
||||||
|
{ |
||||||
|
sync_assert_usb_thread(); |
||||||
|
return fail_reason; |
||||||
|
} |
||||||
|
|
||||||
|
void vfs_if_usbd_msc_init(void) |
||||||
|
{ |
||||||
|
sync_init(); |
||||||
|
build_filesystem(); |
||||||
|
vfs_state = VFS_MNGR_STATE_DISCONNECTED; |
||||||
|
vfs_state_next = VFS_MNGR_STATE_DISCONNECTED; |
||||||
|
time_usb_idle = 0; |
||||||
|
vfs_info.MediaReady = 0; |
||||||
|
vfs_info.MediaChanged = 0; |
||||||
|
vfs_printf("vfs_if_usbd_msc_init"); |
||||||
|
} |
||||||
|
|
||||||
|
void vfs_if_usbd_msc_read_sect(uint32_t sector, uint8_t *buf, uint32_t num_of_sectors) |
||||||
|
{ |
||||||
|
sync_assert_usb_thread(); |
||||||
|
// /* this is very spammy */ vfs_printf("\033[35mREAD @ %d, len %d\033[0m", sector, num_of_sectors);
|
||||||
|
|
||||||
|
// dont proceed if we're not ready
|
||||||
|
if (!vfs_info.MediaReady) { |
||||||
|
vfs_printf("Not Rdy"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// indicate msc activity
|
||||||
|
// main_blink_msc_led(MAIN_LED_OFF);
|
||||||
|
vfs_read(sector, buf, num_of_sectors); |
||||||
|
} |
||||||
|
|
||||||
|
void vfs_if_usbd_msc_write_sect(uint32_t sector, uint8_t *buf, uint32_t num_of_sectors) |
||||||
|
{ |
||||||
|
sync_assert_usb_thread(); |
||||||
|
vfs_printf("\033[32mWRITE @ %d, len %d\033[0m", sector, num_of_sectors); |
||||||
|
if (buf[0] == 0xF8 && buf[1] == 0xFF && buf[2] == 0xFF && buf[3] == 0xFF) { |
||||||
|
vfs_printf("Discard write of F8,FF,FF,FF"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (!vfs_info.MediaReady) { |
||||||
|
vfs_printf("Not Rdy"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Restart the disconnect counter on every packet
|
||||||
|
// so the device does not detach in the middle of a
|
||||||
|
// transfer.
|
||||||
|
time_usb_idle = 0; |
||||||
|
|
||||||
|
if (TRASNFER_FINISHED == file_transfer_state.transfer_state) { |
||||||
|
vfs_printf("Xfer done."); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// indicate msc activity
|
||||||
|
// main_blink_msc_led(MAIN_LED_OFF);
|
||||||
|
vfs_printf("call vfs_write"); |
||||||
|
vfs_write(sector, buf, num_of_sectors); |
||||||
|
if (TRASNFER_FINISHED == file_transfer_state.transfer_state) { |
||||||
|
vfs_printf("Xfer done now."); |
||||||
|
return; |
||||||
|
} |
||||||
|
file_data_handler(sector, buf, num_of_sectors); |
||||||
|
} |
||||||
|
|
||||||
|
static void sync_init(void) |
||||||
|
{ |
||||||
|
osMutexStaticDef(vfsMutex, &vfsMutexControlBlock); |
||||||
|
vfsMutexHandle = osMutexCreate(osMutex(vfsMutex)); |
||||||
|
} |
||||||
|
|
||||||
|
static inline void sync_assert_usb_thread(void) |
||||||
|
{ |
||||||
|
assert_param(osThreadGetId() == tskMainHandle); |
||||||
|
} |
||||||
|
|
||||||
|
static void sync_lock(void) |
||||||
|
{ |
||||||
|
assert_param(osOK == osMutexWait(vfsMutexHandle, 100)); |
||||||
|
} |
||||||
|
|
||||||
|
static void sync_unlock(void) |
||||||
|
{ |
||||||
|
assert_param(osOK == osMutexRelease(vfsMutexHandle)); |
||||||
|
} |
||||||
|
|
||||||
|
static bool changing_state(void) |
||||||
|
{ |
||||||
|
return vfs_state != vfs_state_next; |
||||||
|
} |
||||||
|
|
||||||
|
static void build_filesystem(void) |
||||||
|
{ |
||||||
|
// Update anything that could have changed file system state
|
||||||
|
file_transfer_state = default_transfer_state; |
||||||
|
vfs_user_build_filesystem(); |
||||||
|
vfs_set_file_change_callback(file_change_handler); |
||||||
|
// Set mass storage parameters
|
||||||
|
|
||||||
|
vfs_info.MemorySize = vfs_get_total_size(); |
||||||
|
vfs_info.BlockSize = VFS_SECTOR_SIZE; |
||||||
|
vfs_info.BlockGroup = 1; |
||||||
|
vfs_info.BlockCount = vfs_info.MemorySize / vfs_info.BlockSize; |
||||||
|
// vfs_info.BlockBuf = (uint8_t *) usb_buffer;
|
||||||
|
} |
||||||
|
|
||||||
|
static void switch_to_new_file(stream_type_t stream, uint32_t start_sector, bool andReopen) |
||||||
|
{ // This should close the stream
|
||||||
|
|
||||||
|
vfs_printf("****** NEW FILE STREAM! *******"); |
||||||
|
if (!file_transfer_state.transfer_state) { |
||||||
|
file_transfer_state.transfer_timeout = true; |
||||||
|
transfer_update_state(E_SUCCESS); |
||||||
|
} else { |
||||||
|
if (file_transfer_state.stream_open) { |
||||||
|
stream_close(); |
||||||
|
file_transfer_state.stream_open = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (andReopen) { |
||||||
|
// and we start anew
|
||||||
|
file_transfer_state.start_sector = VFS_INVALID_SECTOR; // pretend we have no srtart sector yet!
|
||||||
|
transfer_stream_open(stream, start_sector); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Callback to handle changes to the root directory. Should be used with vfs_set_file_change_callback
|
||||||
|
static void file_change_handler(const vfs_filename_t filename, vfs_file_change_t change, vfs_file_t file, vfs_file_t new_file_data) |
||||||
|
{ |
||||||
|
vfs_printf("\033[33m@file_change_handler\033[0m (name=%*s, file=%p, ftp=%p, change=%i)\r\n", 11, filename, file, change); |
||||||
|
|
||||||
|
vfs_user_file_change_handler(filename, change, file, new_file_data); |
||||||
|
if (TRASNFER_FINISHED == file_transfer_state.transfer_state) { |
||||||
|
// If the transfer is finished stop further processing
|
||||||
|
vfs_printf("> Transfer is finished."); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (VFS_FILE_CHANGED == change) { |
||||||
|
vfs_printf("> Change"); |
||||||
|
if (file == file_transfer_state.file_to_program) { |
||||||
|
vfs_printf(" Stream is open, continue"); |
||||||
|
stream_type_t stream; |
||||||
|
uint32_t size = vfs_file_get_size(new_file_data); |
||||||
|
vfs_sector_t sector = vfs_file_get_start_sector(new_file_data); |
||||||
|
stream = stream_type_from_name(filename); |
||||||
|
transfer_update_file_info(file, sector, size, stream); |
||||||
|
} else { |
||||||
|
vfs_printf(" No stream."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (VFS_FILE_CREATED == change) { |
||||||
|
stream_type_t stream; |
||||||
|
vfs_printf("> Created"); |
||||||
|
|
||||||
|
if (STREAM_TYPE_NONE != stream_type_from_name(filename)) { |
||||||
|
vfs_printf(" Stream is open, continue"); |
||||||
|
|
||||||
|
// Check for a know file extension to detect the current file being
|
||||||
|
// transferred. Ignore hidden files since MAC uses hidden files with
|
||||||
|
// the same extension to keep track of transfer info in some cases.
|
||||||
|
if (!(VFS_FILE_ATTR_HIDDEN & vfs_file_get_attr(new_file_data))) { |
||||||
|
stream = stream_type_from_name(filename); |
||||||
|
uint32_t size = vfs_file_get_size(new_file_data); |
||||||
|
vfs_sector_t sector = vfs_file_get_start_sector(new_file_data); |
||||||
|
transfer_update_file_info(file, sector, size, stream); |
||||||
|
} |
||||||
|
} else { |
||||||
|
vfs_printf(" No matching stream found!"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (VFS_FILE_DELETED == change) { |
||||||
|
vfs_printf("> Deleted"); |
||||||
|
if (file == file_transfer_state.file_to_program) { |
||||||
|
vfs_printf(" Deleted transferred file!"); |
||||||
|
// The file that was being transferred has been deleted
|
||||||
|
transfer_reset_file_info(); |
||||||
|
} else { |
||||||
|
vfs_printf("Delete other file"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Handler for file data arriving over USB. This function is responsible
|
||||||
|
// for detecting the start of a BIN/HEX file and performing programming
|
||||||
|
static void file_data_handler(uint32_t sector, const uint8_t *buf, uint32_t num_of_sectors) |
||||||
|
{ |
||||||
|
stream_type_t stream; |
||||||
|
uint32_t size; |
||||||
|
vfs_printf("\033[33m@file_data_handler\033[0m (sec=%d, num=%d)", sector, num_of_sectors); |
||||||
|
|
||||||
|
if (sector <= 1) { |
||||||
|
vfs_printf("Discard write to sector %d", sector); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// this is the key for starting a file write - we dont care what file types are sent
|
||||||
|
// just look for something unique (NVIC table, hex, srec, etc) until root dir is updated
|
||||||
|
if (!file_transfer_state.stream_started) { |
||||||
|
vfs_printf("Stream not started yet"); |
||||||
|
// look for file types we can program
|
||||||
|
stream = stream_start_identify((uint8_t *) buf, VFS_SECTOR_SIZE * num_of_sectors); |
||||||
|
|
||||||
|
if (STREAM_TYPE_NONE != stream) { |
||||||
|
vfs_printf("Opening a stream..."); |
||||||
|
transfer_stream_open(stream, sector); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (file_transfer_state.stream_started) { |
||||||
|
vfs_printf("Stream is open, check if we can write ...."); |
||||||
|
|
||||||
|
// // Ignore sectors coming before this file
|
||||||
|
// if (sector < file_transfer_state.start_sector) {
|
||||||
|
// vfs_printf("Sector ABOVE current file!?");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// sectors must be in order
|
||||||
|
if (sector != file_transfer_state.file_next_sector) { |
||||||
|
vfs_printf("file_data_handler BAD sector=%i\r\n", sector); |
||||||
|
|
||||||
|
// Try to find what file this belongs to, if any
|
||||||
|
// OS sometimes first writes the FAT and then the individual files,
|
||||||
|
// so it looks to us as a discontinuous file (in the better case)
|
||||||
|
vfs_filename_t fname; |
||||||
|
vfs_file_t *file; |
||||||
|
if (vfs_find_file(sector, &fname, &file)) { |
||||||
|
vfs_printf("FOUND A FILE!! matches to %s", fname); |
||||||
|
file_transfer_state.file_to_program = file; |
||||||
|
|
||||||
|
stream = stream_start_identify((uint8_t *) buf, VFS_SECTOR_SIZE * num_of_sectors); |
||||||
|
if (stream != STREAM_TYPE_NONE) { |
||||||
|
switch_to_new_file(stream, sector, true); |
||||||
|
goto proceed; |
||||||
|
} |
||||||
|
|
||||||
|
vfs_printf("No stream can handle this, give up. Could be kateswap junk (1)\r\n"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (sector >= file_transfer_state.start_sector && sector < file_transfer_state.file_next_sector) { |
||||||
|
vfs_printf(" sector out of order! lowest ooo = %i\r\n", |
||||||
|
file_transfer_state.last_ooo_sector); |
||||||
|
|
||||||
|
if (VFS_INVALID_SECTOR == file_transfer_state.last_ooo_sector) { |
||||||
|
file_transfer_state.last_ooo_sector = sector; |
||||||
|
} |
||||||
|
|
||||||
|
file_transfer_state.last_ooo_sector = |
||||||
|
MIN(file_transfer_state.last_ooo_sector, sector); |
||||||
|
} else { |
||||||
|
vfs_printf(" sector not part of file transfer\r\n"); |
||||||
|
|
||||||
|
// BUT!! this can be a whole different file written elsewhere
|
||||||
|
// Let's try it.
|
||||||
|
if (sector > 70) { |
||||||
|
// this is a guess as to where the actual data can start - usually 34 and 67 are some garbage in the FAT(s)
|
||||||
|
|
||||||
|
stream = stream_start_identify((uint8_t *) buf, VFS_SECTOR_SIZE * num_of_sectors); |
||||||
|
if (stream != STREAM_TYPE_NONE) { |
||||||
|
switch_to_new_file(stream, sector, true); |
||||||
|
goto proceed; |
||||||
|
} |
||||||
|
|
||||||
|
vfs_printf("No stream can handle this, give up. Could be kateswap junk (2)\r\n"); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
vfs_printf(" discarding data - size transferred=0x%x\r\n", |
||||||
|
file_transfer_state.size_transferred); |
||||||
|
|
||||||
|
vfs_printf_nonl("\033[31m", 5); |
||||||
|
vfs_printf_nonl((const char *) buf, VFS_SECTOR_SIZE * num_of_sectors); |
||||||
|
vfs_printf_nonl("\033[0m\r\n", 6); |
||||||
|
|
||||||
|
return; |
||||||
|
} else { |
||||||
|
vfs_printf("sector is good"); |
||||||
|
} |
||||||
|
proceed: |
||||||
|
// This sector could be part of the file so record it
|
||||||
|
size = VFS_SECTOR_SIZE * num_of_sectors; |
||||||
|
file_transfer_state.size_transferred += size; |
||||||
|
file_transfer_state.file_next_sector = sector + num_of_sectors; |
||||||
|
|
||||||
|
// If stream processing is done then discard the data
|
||||||
|
if (file_transfer_state.stream_finished) { |
||||||
|
vfs_printf("vfs_manager file_data_handler\r\n sector=%i, size=%i\r\n", sector, size); |
||||||
|
vfs_printf(" discarding data - size transferred=0x%x\r\n", |
||||||
|
file_transfer_state.size_transferred); |
||||||
|
|
||||||
|
vfs_printf_nonl("\033[31m", 5); |
||||||
|
vfs_printf_nonl((const char *) buf, VFS_SECTOR_SIZE * num_of_sectors); |
||||||
|
vfs_printf_nonl("\033[0m\r\n", 6); |
||||||
|
|
||||||
|
transfer_update_state(E_SUCCESS); |
||||||
|
return; |
||||||
|
} else { |
||||||
|
vfs_printf("stream is not finished, can handle..."); |
||||||
|
} |
||||||
|
|
||||||
|
transfer_stream_data(sector, buf, size); |
||||||
|
} else { |
||||||
|
vfs_printf("Stream not started!!!!!!"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static bool ready_for_state_change(void) |
||||||
|
{ |
||||||
|
uint32_t timeout_ms = INVALID_TIMEOUT_MS; |
||||||
|
assert_param(vfs_state != vfs_state_next); |
||||||
|
|
||||||
|
if (VFS_MNGR_STATE_CONNECTED == vfs_state) { |
||||||
|
switch (file_transfer_state.transfer_state) { |
||||||
|
case TRANSFER_NOT_STARTED: |
||||||
|
case TRASNFER_FINISHED: |
||||||
|
timeout_ms = DISCONNECT_DELAY_MS; |
||||||
|
break; |
||||||
|
|
||||||
|
case TRANSFER_IN_PROGRESS: |
||||||
|
timeout_ms = DISCONNECT_DELAY_TRANSFER_TIMEOUT_MS; |
||||||
|
break; |
||||||
|
|
||||||
|
case TRANSFER_CAN_BE_FINISHED: |
||||||
|
timeout_ms = DISCONNECT_DELAY_TRANSFER_IDLE_MS; |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
assert_param(0); |
||||||
|
timeout_ms = DISCONNECT_DELAY_MS; |
||||||
|
break; |
||||||
|
} |
||||||
|
} else if ((VFS_MNGR_STATE_DISCONNECTED == vfs_state) && |
||||||
|
(VFS_MNGR_STATE_CONNECTED == vfs_state_next)) { |
||||||
|
timeout_ms = CONNECT_DELAY_MS; |
||||||
|
} else if ((VFS_MNGR_STATE_RECONNECTING == vfs_state) && |
||||||
|
(VFS_MNGR_STATE_CONNECTED == vfs_state_next)) { |
||||||
|
timeout_ms = RECONNECT_DELAY_MS; |
||||||
|
} else if ((VFS_MNGR_STATE_RECONNECTING == vfs_state) && |
||||||
|
(VFS_MNGR_STATE_DISCONNECTED == vfs_state_next)) { |
||||||
|
timeout_ms = 0; |
||||||
|
} |
||||||
|
|
||||||
|
if (INVALID_TIMEOUT_MS == timeout_ms) { |
||||||
|
assert_param(0); |
||||||
|
timeout_ms = 0; |
||||||
|
} |
||||||
|
|
||||||
|
return time_usb_idle > timeout_ms ? true : false; |
||||||
|
} |
||||||
|
|
||||||
|
// Abort a remount if one is pending
|
||||||
|
void abort_remount(void) |
||||||
|
{ |
||||||
|
sync_lock(); |
||||||
|
|
||||||
|
// Only abort a remount if in the connected state and reconnecting is the next state
|
||||||
|
if ((VFS_MNGR_STATE_RECONNECTING == vfs_state_next) && (VFS_MNGR_STATE_CONNECTED == vfs_state)) { |
||||||
|
vfs_state_next = VFS_MNGR_STATE_CONNECTED; |
||||||
|
} |
||||||
|
|
||||||
|
sync_unlock(); |
||||||
|
} |
||||||
|
|
||||||
|
// Update the tranfer state with file information
|
||||||
|
static void transfer_update_file_info(vfs_file_t file, uint32_t start_sector, uint32_t size, stream_type_t stream) |
||||||
|
{ |
||||||
|
vfs_printf("\033[33m@transfer_update_file_info\033[0m (file=%p, start_sector=%i, size=%i)\r\n", file, start_sector, size); |
||||||
|
|
||||||
|
if (TRASNFER_FINISHED == file_transfer_state.transfer_state) { |
||||||
|
assert_param(0); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Initialize the directory entry if it has not been set
|
||||||
|
if (VFS_FILE_INVALID == file_transfer_state.file_to_program) { |
||||||
|
file_transfer_state.file_to_program = file; |
||||||
|
|
||||||
|
if (file != VFS_FILE_INVALID) { |
||||||
|
vfs_printf(" file_to_program=%p\r\n", file); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Initialize the starting sector if it has not been set
|
||||||
|
if (VFS_INVALID_SECTOR == file_transfer_state.start_sector) { |
||||||
|
file_transfer_state.start_sector = start_sector; |
||||||
|
|
||||||
|
if (start_sector != VFS_INVALID_SECTOR) { |
||||||
|
vfs_printf(" start_sector=%i\r\n", start_sector); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Initialize the stream if it has not been set
|
||||||
|
if (STREAM_TYPE_NONE == file_transfer_state.stream) { |
||||||
|
file_transfer_state.stream = stream; |
||||||
|
|
||||||
|
if (stream != STREAM_TYPE_NONE) { |
||||||
|
vfs_printf(" stream=%i\r\n", stream); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Check - File size must either grow or be smaller than the size already transferred
|
||||||
|
if ((size < file_transfer_state.file_size) && (size < file_transfer_state.size_transferred)) { |
||||||
|
vfs_printf(" error: file size changed from %i to %i\r\n", file_transfer_state.file_size, size); |
||||||
|
// this is probably a new file
|
||||||
|
trap("File shrinks");//XXX
|
||||||
|
switch_to_new_file(stream, start_sector, true); |
||||||
|
} |
||||||
|
|
||||||
|
// Check - Starting sector must be the same - this is optional for file info since it may not be present initially
|
||||||
|
if ((VFS_INVALID_SECTOR != start_sector) && (start_sector != file_transfer_state.start_sector)) { |
||||||
|
vfs_printf(" error: starting sector changed from %i to %i\r\n", file_transfer_state.start_sector, start_sector); |
||||||
|
// this is probably a new file
|
||||||
|
|
||||||
|
trap("Changed start offset");//XXX
|
||||||
|
switch_to_new_file(stream, start_sector, true); |
||||||
|
} |
||||||
|
|
||||||
|
// Check - stream must be the same
|
||||||
|
if (stream != file_transfer_state.stream) { |
||||||
|
vfs_printf(" error: changed types during transfer from %i to %i\r\n", stream, file_transfer_state.stream); |
||||||
|
transfer_update_state(E_ERROR_DURING_TRANSFER); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Update values - Size is the only value that can change
|
||||||
|
file_transfer_state.file_size = size; |
||||||
|
vfs_printf(" updated size=%i\r\n", size); |
||||||
|
|
||||||
|
transfer_update_state(E_SUCCESS); |
||||||
|
} |
||||||
|
|
||||||
|
// Reset the transfer information or error if transfer is already in progress
|
||||||
|
static void transfer_reset_file_info(void) |
||||||
|
{ |
||||||
|
vfs_printf("vfs_manager transfer_reset_file_info()\r\n"); |
||||||
|
if (file_transfer_state.stream_open) { |
||||||
|
transfer_update_state(E_ERROR_DURING_TRANSFER); |
||||||
|
} else { |
||||||
|
file_transfer_state = default_transfer_state; |
||||||
|
abort_remount(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Update the tranfer state with new information
|
||||||
|
static void transfer_stream_open(stream_type_t stream, uint32_t start_sector) |
||||||
|
{ |
||||||
|
error_t status; |
||||||
|
assert_param(!file_transfer_state.stream_open); |
||||||
|
assert_param(start_sector != VFS_INVALID_SECTOR); |
||||||
|
vfs_printf("\033[33m@transfer_stream_open\033[0m (stream=%i, start_sector=%i)\r\n", |
||||||
|
stream, start_sector); |
||||||
|
|
||||||
|
// Check - Starting sector must be the same
|
||||||
|
if (start_sector != file_transfer_state.start_sector && file_transfer_state.start_sector != VFS_INVALID_SECTOR) { |
||||||
|
vfs_printf(" error: starting sector changed from %i to %i\r\n", file_transfer_state.start_sector, start_sector); |
||||||
|
// this is probably a new file
|
||||||
|
switch_to_new_file(stream, start_sector, false); |
||||||
|
file_transfer_state.start_sector = VFS_INVALID_SECTOR; |
||||||
|
} |
||||||
|
|
||||||
|
// Check - stream must be the same
|
||||||
|
if (stream != file_transfer_state.stream && file_transfer_state.stream != STREAM_TYPE_NONE) { |
||||||
|
vfs_printf(" error: changed types during tranfer from %i to %i\r\n", stream, file_transfer_state.stream); |
||||||
|
// this is probably a new file
|
||||||
|
switch_to_new_file(stream, start_sector, false); |
||||||
|
file_transfer_state.start_sector = VFS_INVALID_SECTOR; |
||||||
|
} |
||||||
|
|
||||||
|
// Initialize the starting sector if it has not been set
|
||||||
|
if (VFS_INVALID_SECTOR == file_transfer_state.start_sector) { |
||||||
|
file_transfer_state.start_sector = start_sector; |
||||||
|
|
||||||
|
if (start_sector != VFS_INVALID_SECTOR) { |
||||||
|
vfs_printf(" start_sector=%i\r\n", start_sector); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Initialize the stream if it has not been set
|
||||||
|
if (STREAM_TYPE_NONE == file_transfer_state.stream) { |
||||||
|
file_transfer_state.stream = stream; |
||||||
|
|
||||||
|
if (stream != STREAM_TYPE_NONE) { |
||||||
|
vfs_printf(" stream=%i\r\n", stream); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Open stream
|
||||||
|
status = stream_open(stream); |
||||||
|
vfs_printf(" stream_open stream=%i ret %i\r\n", stream, status); |
||||||
|
|
||||||
|
if (E_SUCCESS == status) { |
||||||
|
file_transfer_state.file_next_sector = start_sector; |
||||||
|
file_transfer_state.stream_open = true; |
||||||
|
file_transfer_state.stream_started = true; |
||||||
|
} |
||||||
|
|
||||||
|
transfer_update_state(status); |
||||||
|
} |
||||||
|
|
||||||
|
// Update the tranfer state with new information
|
||||||
|
static void transfer_stream_data(uint32_t sector, const uint8_t *data, uint32_t size) |
||||||
|
{ |
||||||
|
error_t status; |
||||||
|
vfs_printf("\033[33m@transfer_stream_data\033[0m (sector=%i, size=%i)\r\n", sector, size); |
||||||
|
vfs_printf(" size processed=0x%x, data=%x,%x,%x,%x,...\r\n", |
||||||
|
file_transfer_state.size_processed, data[0], data[1], data[2], data[3]); |
||||||
|
|
||||||
|
if (file_transfer_state.stream_finished) { |
||||||
|
assert_param(0); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
assert_param(size % VFS_SECTOR_SIZE == 0); |
||||||
|
assert_param(file_transfer_state.stream_open); |
||||||
|
status = stream_write((uint8_t *) data, size); |
||||||
|
vfs_printf(" stream_write ret=%i\r\n", status); |
||||||
|
|
||||||
|
if (E_SUCCESS_DONE == status) { |
||||||
|
// Override status so E_SUCCESS_DONE
|
||||||
|
// does not get passed into transfer_update_state
|
||||||
|
status = stream_close(); |
||||||
|
vfs_printf(" stream_close ret=%i\r\n", status); |
||||||
|
file_transfer_state.stream_open = false; |
||||||
|
file_transfer_state.stream_finished = true; |
||||||
|
file_transfer_state.stream_optional_finish = true; |
||||||
|
} else if (E_SUCCESS_DONE_OR_CONTINUE == status) { |
||||||
|
status = E_SUCCESS; |
||||||
|
file_transfer_state.stream_optional_finish = true; |
||||||
|
} else { |
||||||
|
file_transfer_state.stream_optional_finish = false; |
||||||
|
} |
||||||
|
|
||||||
|
file_transfer_state.size_processed += size; |
||||||
|
transfer_update_state(status); |
||||||
|
} |
||||||
|
|
||||||
|
// Check if the current transfer is still in progress, done, or if an error has occurred
|
||||||
|
static void transfer_update_state(error_t status) |
||||||
|
{ |
||||||
|
bool transfer_timeout; |
||||||
|
bool transfer_started; |
||||||
|
bool transfer_can_be_finished; |
||||||
|
bool transfer_must_be_finished; |
||||||
|
bool out_of_order_sector; |
||||||
|
error_t local_status = status; |
||||||
|
assert_param((status != E_SUCCESS_DONE) && |
||||||
|
(status != E_SUCCESS_DONE_OR_CONTINUE)); |
||||||
|
|
||||||
|
if (TRASNFER_FINISHED == file_transfer_state.transfer_state) { |
||||||
|
assert_param(0); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Update file info status. The end of a file is never known for sure since
|
||||||
|
// what looks like a complete file could be part of a file getting flushed to disk.
|
||||||
|
// The criteria for an successful optional finish is
|
||||||
|
// 1. A file has been detected
|
||||||
|
// 2. The size of the file indicated in the root dir has been transferred
|
||||||
|
// 3. The file size is greater than zero
|
||||||
|
file_transfer_state.file_info_optional_finish = |
||||||
|
(file_transfer_state.file_to_program != VFS_FILE_INVALID) && |
||||||
|
(file_transfer_state.size_transferred >= file_transfer_state.file_size) && |
||||||
|
(file_transfer_state.file_size > 0); |
||||||
|
transfer_timeout = file_transfer_state.transfer_timeout; |
||||||
|
transfer_started = (VFS_FILE_INVALID != file_transfer_state.file_to_program) || |
||||||
|
(STREAM_TYPE_NONE != file_transfer_state.stream); |
||||||
|
// The transfer can be finished if both file and stream processing
|
||||||
|
// can be considered complete
|
||||||
|
transfer_can_be_finished = file_transfer_state.file_info_optional_finish && |
||||||
|
file_transfer_state.stream_optional_finish; |
||||||
|
// The transfer must be fnished if stream processing is for sure complete
|
||||||
|
// and file processing can be considered complete
|
||||||
|
transfer_must_be_finished = file_transfer_state.stream_finished && |
||||||
|
file_transfer_state.file_info_optional_finish; |
||||||
|
out_of_order_sector = false; |
||||||
|
|
||||||
|
if (file_transfer_state.last_ooo_sector != VFS_INVALID_SECTOR) { |
||||||
|
assert_param(file_transfer_state.start_sector != VFS_INVALID_SECTOR); |
||||||
|
uint32_t sector_offset = (file_transfer_state.last_ooo_sector - |
||||||
|
file_transfer_state.start_sector) * VFS_SECTOR_SIZE; |
||||||
|
|
||||||
|
if (sector_offset < file_transfer_state.size_processed) { |
||||||
|
// The out of order sector was within the range of data already
|
||||||
|
// processed.
|
||||||
|
out_of_order_sector = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Set the transfer state and set the status if necessary
|
||||||
|
if (local_status != E_SUCCESS) { |
||||||
|
file_transfer_state.transfer_state = TRASNFER_FINISHED; |
||||||
|
} else if (transfer_timeout) { |
||||||
|
if (out_of_order_sector) { |
||||||
|
local_status = E_OOO_SECTOR; |
||||||
|
} else if (!transfer_started) { |
||||||
|
local_status = E_SUCCESS; |
||||||
|
} else if (transfer_can_be_finished) { |
||||||
|
local_status = E_SUCCESS; |
||||||
|
} else { |
||||||
|
local_status = E_TRANSFER_TIMEOUT; |
||||||
|
} |
||||||
|
|
||||||
|
file_transfer_state.transfer_state = TRASNFER_FINISHED; |
||||||
|
} else if (transfer_must_be_finished) { |
||||||
|
file_transfer_state.transfer_state = TRASNFER_FINISHED; |
||||||
|
} else if (transfer_can_be_finished) { |
||||||
|
file_transfer_state.transfer_state = TRANSFER_CAN_BE_FINISHED; |
||||||
|
} else if (transfer_started) { |
||||||
|
file_transfer_state.transfer_state = TRANSFER_IN_PROGRESS; |
||||||
|
} |
||||||
|
|
||||||
|
if (TRASNFER_FINISHED == file_transfer_state.transfer_state) { |
||||||
|
vfs_printf("vfs_manager transfer_update_state(status=%i)\r\n", status); |
||||||
|
vfs_printf(" file=%p, start_sect= %i, size=%i\r\n", |
||||||
|
file_transfer_state.file_to_program, file_transfer_state.start_sector, |
||||||
|
file_transfer_state.file_size); |
||||||
|
vfs_printf(" stream=%i, size_processed=%i, opt_finish=%i, timeout=%i\r\n", |
||||||
|
file_transfer_state.stream, file_transfer_state.size_processed, |
||||||
|
file_transfer_state.file_info_optional_finish, transfer_timeout); |
||||||
|
|
||||||
|
// Close the file stream if it is open
|
||||||
|
if (file_transfer_state.stream_open) { |
||||||
|
error_t close_status; |
||||||
|
close_status = stream_close(); |
||||||
|
vfs_printf(" stream closed ret=%i\r\n", close_status); |
||||||
|
file_transfer_state.stream_open = false; |
||||||
|
|
||||||
|
if (E_SUCCESS == local_status) { |
||||||
|
local_status = close_status; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Set the fail reason
|
||||||
|
fail_reason = local_status; |
||||||
|
vfs_printf(" Transfer finished, status: %i=%s\r\n", fail_reason, error_get_string(fail_reason)); |
||||||
|
} |
||||||
|
|
||||||
|
// If this state change is not from aborting a transfer
|
||||||
|
// due to a remount then trigger a remount
|
||||||
|
if (!transfer_timeout) { |
||||||
|
vfs_printf("~~ request Remount from transfer_update_state()"); |
||||||
|
vfs_mngr_fs_remount(false); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
/**
|
||||||
|
* @file vfs_manager.h |
||||||
|
* @brief Methods that build and manipulate a virtual file system |
||||||
|
* |
||||||
|
* DAPLink Interface Firmware |
||||||
|
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may |
||||||
|
* not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef VFS_MANAGER_USER_H |
||||||
|
#define VFS_MANAGER_USER_H |
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "virtual_fs.h" |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Callable from anywhere */ |
||||||
|
|
||||||
|
// Enable or disable the virtual filesystem
|
||||||
|
void vfs_mngr_fs_enable(bool enabled); |
||||||
|
|
||||||
|
// Remount the virtual filesystem
|
||||||
|
void vfs_mngr_fs_remount(bool force_full); |
||||||
|
|
||||||
|
|
||||||
|
/* Callable only from the thread running the virtual fs */ |
||||||
|
|
||||||
|
// Initialize the VFS manager
|
||||||
|
// Must be called after USB has been initialized (usbd_init())
|
||||||
|
// Notes: Must only be called from the thread runnning USB
|
||||||
|
void vfs_mngr_init(bool enabled); |
||||||
|
|
||||||
|
// Run the vfs manager state machine
|
||||||
|
// Notes: Must only be called from the thread runnning USB
|
||||||
|
void vfs_mngr_periodic(uint32_t elapsed_ms); |
||||||
|
|
||||||
|
// Return the status of the last transfer or E_SUCCESS
|
||||||
|
// if none have been performed yet
|
||||||
|
error_t vfs_mngr_get_transfer_status(void); |
||||||
|
|
||||||
|
|
||||||
|
/* Use functions */ |
||||||
|
|
||||||
|
// Build the filesystem by calling vfs_init and then adding files with vfs_create_file
|
||||||
|
void vfs_user_build_filesystem(void); |
||||||
|
|
||||||
|
// Called when a file on the filesystem changes
|
||||||
|
void vfs_user_file_change_handler(const vfs_filename_t filename, vfs_file_change_t change, vfs_file_t file, vfs_file_t new_file_data); |
||||||
|
|
||||||
|
// Called when VFS is disconnecting
|
||||||
|
void vfs_user_disconnecting(void); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// --- interface ---
|
||||||
|
void vfs_if_usbd_msc_init(void); |
||||||
|
void vfs_if_usbd_msc_read_sect(uint32_t sector, uint8_t *buf, uint32_t num_of_sectors); |
||||||
|
void vfs_if_usbd_msc_write_sect(uint32_t sector, uint8_t *buf, uint32_t num_of_sectors); |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
uint32_t MemorySize; |
||||||
|
uint16_t BlockSize; |
||||||
|
uint32_t BlockGroup; // LUN?
|
||||||
|
uint32_t BlockCount; |
||||||
|
// uint8_t *BlockBuf; // apparently unused :thaenkin:
|
||||||
|
bool MediaReady; |
||||||
|
bool MediaChanged; |
||||||
|
} vfs_info_t; |
||||||
|
|
||||||
|
extern volatile vfs_info_t vfs_info; |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif// VFS_MANAGER_USER_H
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue