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