Merge branch 'bulk'

sipo
Ondřej Hruška 6 years ago
commit 45910e7f04
  1. 3
      TinyFrame/TF_Config.h
  2. 9
      TinyFrame/TF_Integration.c
  3. 90
      TinyFrame/TinyFrame.c
  4. 8
      TinyFrame/TinyFrame.h
  5. 9
      USB/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c
  6. 8
      USB/usbd_cdc_if.c
  7. 206
      comm/messages.c
  8. 147
      comm/messages.h
  9. 98
      comm/msg_bulkread.c
  10. 45
      comm/msg_bulkread.h
  11. 89
      comm/msg_bulkwrite.c
  12. 41
      comm/msg_bulkwrite.h
  13. 98
      comm/msg_responses.c
  14. 114
      comm/msg_responses.h
  15. 3
      cortex_handlers.c
  16. 54
      framework/unit_registry.c
  17. 67
      freertos.c
  18. 21
      gex.mk
  19. 1
      platform/debug_uart.c
  20. 4
      platform/platform.c
  21. 11
      tasks/sched_queue.h
  22. 2
      tasks/task_main.c
  23. 31
      tasks/task_msg.c
  24. 12
      tasks/task_msg.h
  25. 43
      tasks/task_sched.c
  26. 17
      tasks/task_sched.h
  27. 4
      units/neopixel/unit_neopixel.c
  28. 8
      units/pin/unit_pin.c
  29. 190
      units/test/unit_test.c
  30. 13
      units/test/unit_test.h
  31. 0
      utils/build_parser.sh
  32. 4
      utils/macro.h
  33. 8
      utils/stacksmon.c

@ -5,6 +5,7 @@
#ifndef TF_CONFIG_H
#define TF_CONFIG_H
#include "platform.h"
#include <stdint.h>
//#include <esp8266.h> // when using with esphttpd
@ -65,7 +66,7 @@ typedef uint8_t TF_COUNT;
// Timeout for receiving & parsing a frame
// ticks = number of calls to TF_Tick()
#define TF_PARSER_TIMEOUT_TICKS 10
#define TF_PARSER_TIMEOUT_TICKS 250
//------------------------- End of user config ------------------------------

@ -7,6 +7,7 @@
#include "platform.h"
#include "task_main.h"
#include "utils/hexdump.h"
#include "USB/usbd_cdc_if.h"
#include "TinyFrame.h"
@ -20,9 +21,11 @@ void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
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;
uint16_t chunksize = (uint16_t) MIN(total, CHUNK);
assert_param(USBD_OK == CDC_Transmit_FS((uint8_t *) buff, chunksize));
buff += chunksize;
total -= chunksize;
}
}

@ -372,6 +372,12 @@ static void _TF_FN TF_HandleReceivedMessage(TinyFrame *tf)
if (res == TF_RENEW) {
renew_id_listener(ilst);
}
else if (res == TF_CLOSE) {
// Set userdata to NULL to avoid calling user for cleanup
ilst->userdata = NULL;
ilst->userdata2 = NULL;
cleanup_id_listener(tf, i, ilst);
}
return;
}
}
@ -389,8 +395,12 @@ static void _TF_FN TF_HandleReceivedMessage(TinyFrame *tf)
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
// type listeners don't have userdata.
// TF_RENEW doesn't make sense here because type listeners don't expire = same as TF_STAY
if (res == TF_CLOSE) {
cleanup_type_listener(tf, i, tlst);
}
return;
}
}
@ -404,8 +414,16 @@ static void _TF_FN TF_HandleReceivedMessage(TinyFrame *tf)
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
// generic listeners don't have userdata.
// TF_RENEW doesn't make sense here because generic listeners don't expire = same as TF_STAY
// note: It's not expected that user will have multiple generic listeners, or
// ever actually remove them. They're most useful as default callbacks if no other listener
// handled the message.
if (res == TF_CLOSE) {
cleanup_generic_listener(tf, i, glst);
}
return;
}
}
@ -463,10 +481,11 @@ void _TF_FN TF_AcceptChar(TinyFrame *tf, unsigned char c)
#if !TF_USE_SOF_BYTE
if (tf->state == TFState_SOF) {
TF_ParsBeginFrame();
}
TF_ParsBeginFrame();
}
#endif
//@formatter:off
switch (tf->state) {
case TFState_SOF:
if (c == TF_SOF_BYTE) {
@ -495,15 +514,15 @@ void _TF_FN TF_AcceptChar(TinyFrame *tf, unsigned char c)
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
#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;
@ -518,6 +537,7 @@ void _TF_FN TF_AcceptChar(TinyFrame *tf, unsigned char c)
}
if (tf->len == 0) {
// if the message has no body, we're done.
TF_HandleReceivedMessage(tf);
TF_ResetParser(tf);
break;
@ -529,7 +549,7 @@ void _TF_FN TF_AcceptChar(TinyFrame *tf, unsigned char c)
CKSUM_RESET(tf->cksum); // Start collecting the payload
if (tf->len >= TF_MAX_PAYLOAD_RX) {
if (tf->len > TF_MAX_PAYLOAD_RX) {
// ERROR - frame too long. Consume, but do not store.
tf->discard_data = true;
}
@ -545,16 +565,16 @@ void _TF_FN TF_AcceptChar(TinyFrame *tf, unsigned char 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
#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;
@ -570,6 +590,7 @@ void _TF_FN TF_AcceptChar(TinyFrame *tf, unsigned char c)
}
break;
}
//@formatter:on
// we get here after finishing HEAD, if no data are to be received - handle and clear
if (tf->len == 0 && tf->state == TFState_DATA) {
@ -753,16 +774,19 @@ static bool _TF_FN TF_SendFrame(TinyFrame *tf, TF_Msg *msg, TF_Listener listener
}
}
// 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;
// Checksum only if message had a body
if (msg->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);
}
// 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;

@ -3,14 +3,14 @@
/**
* TinyFrame protocol library
*
* (c) Ondřej Hruška 2017, MIT License
*
* (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"
#define TF_VERSION "2.0.4"
//---------------------------------------------------------------------------
#include <stdint.h> // for uint8_t etc

@ -646,9 +646,12 @@ uint8_t USBD_CDC_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum)
if(pdev->pClassData2 != NULL)
{
hcdc->TxState = 0;
USBD_CDC_TransmitDone(pdev);
if (epnum == CDC_IN_EP) {
hcdc->TxState = 0;
USBD_CDC_TransmitDone(pdev);
} else if (epnum == CDC_CMD_EP) {
// command EP send done
}
return USBD_OK;
}

@ -48,6 +48,7 @@
/* Includes ------------------------------------------------------------------*/
#include "platform.h"
#include "tasks/task_msg.h"
#include "usbd_cdc_if.h"
/* USER CODE BEGIN INCLUDE */
#include "task_main.h"
@ -265,6 +266,7 @@ 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)
{
static struct rx_que_item rxitem;
/* USER CODE BEGIN 6 */
// this does nothing?!
// the buffer was already assigned in the init function and we got it as argument here?!
@ -272,7 +274,11 @@ static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
assert_param(*Len <= APP_RX_DATA_SIZE);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
TF_Accept(comm, Buf, *Len);
// Post the data chunk on the RX queue to be handled asynchronously.
rxitem.len = *Len;
memcpy(rxitem.data, Buf, rxitem.len);
assert_param(pdPASS == xQueueSend(queRxDataHandle, &rxitem, 100));
return (USBD_OK);
/* USER CODE END 6 */

@ -6,213 +6,57 @@
#include "TinyFrame.h"
#include "framework/unit_registry.h"
#include "comm/messages.h"
#include "task_sched.h"
TinyFrame tf_;
static 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);
}
/**
* Ping request listener - returns version string and other info about the build
*/
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);
com_respond_snprintf(msg->frame_id, MSG_SUCCESS, "%s/%s", GEX_VERSION, GEX_PLATFORM);
return TF_STAY;
}
// ----------------------------------------------------------------------------
static void job_unhandled_resp(Job *job)
{
tf_respond_snprintf(MSG_ERROR, job->frame_id, "UNKNOWN MSG %"PRIu32, job->d32);
}
/**
* Default listener, fallback for unhandled messages
*/
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);
dbg("!! Unhandled msg type %02"PRIx8", frame_id 0x%04"PRIx16, msg->type, msg->frame_id);
com_respond_snprintf(msg->frame_id, MSG_ERROR, "UNKNOWN MSG %"PRIu8, msg->type);
return TF_STAY;
}
// ----------------------------------------------------------------------------
/**
* Unit request listener, a message targeted at a particular
*/
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)
static TF_Result lst_list_units(TinyFrame *tf, TF_Msg *msg)
{
ureg_report_active_units(job->frame_id);
ureg_report_active_units(msg->frame_id);
return TF_STAY;
}
static TF_Result lst_list_units(TinyFrame *tf, TF_Msg *msg)
// ---------------------------------------------------------------------------
void comm_init(void)
{
Job job = {
.cb = job_list_units,
.frame_id = msg->frame_id,
};
scheduleJob(&job, TSK_SCHED_LOW);
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);
return TF_STAY;
// fall-through
TF_AddGenericListener(comm, lst_default);
}

@ -9,142 +9,43 @@
#include "task_sched.h"
#include "TinyFrame.h"
extern TinyFrame *comm;
/**
* Initialize TinyFrame and set up listeners
*/
void comm_init(void);
#define BULK_LST_TIMEOUT_MS 200
#define BULKREAD_MAX_CHUNK 512 // this is a static buffer
/**
* 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);
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)
/**
* 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);
}
MSG_BULK_READ_OFFER = 0x03, //!< Offer of data to read. Payload: u32 total len
MSG_BULK_READ_POLL = 0x04, //!< Request to read a previously announced chunk.
MSG_BULK_WRITE_OFFER = 0x05, //!< Offer to receive data in a write transaction. Payload: u32 max size, u32 max chunk
MSG_BULK_DATA = 0x06, //!< Writing a chunk, or sending a chunk to master.
MSG_BULK_END = 0x07, //!< Bulk transfer is done, no more data to read or write. Recipient shall check total len and discard it on mismatch. There could be a checksum ...
MSG_BULK_ABORT = 0x08, //!< Discard the ongoing transfer
/**
* 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);
// Unit messages
MSG_UNIT_REQUEST = 0x10, //!< Command addressed to a particular unit
MSG_UNIT_REPORT = 0x11, //!< Spontaneous report from a unit
/**
* 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);
// System messages
MSG_LIST_UNITS = 0x20, //!< Get all unit call-signs and names
};
/**
* 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);
extern TinyFrame *comm;
/**
* 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);
// Must be after the enum because it's used in the header file.
#include "msg_responses.h"
#include "msg_bulkread.h"
#include "msg_bulkwrite.h"
/**
* 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
* Initialize TinyFrame and set up listeners
*/
void sched_respond_u32(TF_ID frame_id, uint32_t d);
void comm_init(void);
#endif //GEX_MESSAGES_H

@ -0,0 +1,98 @@
//
// Created by MightyPork on 2017/12/23.
//
#include "platform.h"
#include <TinyFrame.h>
#include "messages.h"
#include "utils/payload_parser.h"
#include "utils/payload_builder.h"
/** Buffer for preparing bulk chunks */
static uint8_t bulkread_buffer[BULKREAD_MAX_CHUNK];
/**
* TF listener for the bulk read transaction
*/
static TF_Result bulkread_lst(TinyFrame *tf, TF_Msg *msg)
{
struct bulk_read *bulk = msg->userdata;
// this is a final call before timeout, to clean up
if (msg->data == NULL) {
goto close;
}
assert_param(NULL != bulk);
if (msg->type == MSG_BULK_ABORT) {
goto close;
}
else if (msg->type == MSG_BULK_READ_POLL) {
// find how much data the reader wants
PayloadParser pp = pp_start(msg->data, msg->len, NULL);
uint32_t chunk = pp_u32(&pp);
chunk = MIN(chunk, bulk->len - bulk->offset);
chunk = MIN(chunk, BULKREAD_MAX_CHUNK);
// load data into the buffer
bulk->read(bulk, chunk, bulkread_buffer);
bool last = (bulk->offset + chunk >= bulk->len);
TF_Msg resp;
TF_ClearMsg(&resp);
resp.frame_id = bulk->frame_id;
resp.type = (last ? MSG_BULK_END : MSG_BULK_DATA); // the last chunk has the END type
resp.data = bulkread_buffer;
resp.len = (TF_LEN) chunk;
TF_Respond(tf, &resp);
if (last) goto close;
// advance the position pointer
bulk->offset += chunk;
}
return TF_RENEW;
close:
if (bulk) {
// Ask user to free the bulk and userdata
bulk->read(bulk, 0, NULL);
msg->userdata = NULL;
}
return TF_CLOSE;
}
/** Start the bulk read flow */
void bulkread_start(TinyFrame *tf, struct bulk_read *bulk)
{
assert_param(bulk);
assert_param(bulk->len);
assert_param(bulk->read);
bulk->offset = 0;
{
uint8_t buf[8];
PayloadBuilder pb = pb_start(buf, 4, NULL);
pb_u32(&pb, bulk->len);
pb_u32(&pb, BULKREAD_MAX_CHUNK);
// We use userdata1 to hold a reference to the bulk transfer
TF_Msg msg = {
.type = MSG_BULK_READ_OFFER,
.frame_id = bulk->frame_id,
.is_response = true, // this ensures the frame_id is not re-generated
.data = buf,
.len = (TF_LEN) pb_length(&pb),
.userdata = bulk,
};
TF_Query(tf, &msg, bulkread_lst, BULK_LST_TIMEOUT_MS);
}
}

@ -0,0 +1,45 @@
//
// Created by MightyPork on 2017/12/23.
//
#ifndef GEX_F072_MSG_BULKREAD_H
#define GEX_F072_MSG_BULKREAD_H
#ifndef GEX_MESSAGES_H
#error "Include messages.h instead!"
#endif
#include <TinyFrame.h>
typedef struct bulk_read BulkRead;
/**
* Type of the bulk-read listener. Offset within the data can be retrieved from bulk->offset.
*
* If buffer is NULL, this is the last call and the bulk struct must be freed if it was allocated.
*
* @param bulk - a data object passed to the bulkread_start() function.
* @param chunk - size of the chunk to read
* @param buffer - destination buffer to fill with the data. NULL if bulk should be freed.
*/
typedef void (*bulkread_data_cb)(BulkRead *bulk, uint32_t chunk, uint8_t *buffer);
/** Bulk read structure */
struct bulk_read {
TF_ID frame_id; //!< ID of the requesting frame, used for the entire bulk read session
bulkread_data_cb read; //!< Read callback
uint32_t len; //!< Total data length
void *userdata; //!< A place for arbitrary userdata
uint32_t offset; //!< Internal offset counter, will be set to 0 on start.
};
/**
* Start a bulk read session
* @param tf - tinyframe instance
* @param bulk - bulk read config structure (malloc'd or static)
*/
void bulkread_start(TinyFrame *tf, struct bulk_read *bulk);
#endif //GEX_F072_MSG_BULKREAD_H

@ -0,0 +1,89 @@
//
// Created by MightyPork on 2017/12/23.
//
#include "platform.h"
#include <TinyFrame.h>
#include "messages.h"
#include "utils/payload_parser.h"
#include "utils/payload_builder.h"
/**
* TF listener for the bulk write transaction
*/
static TF_Result bulkwrite_lst(TinyFrame *tf, TF_Msg *msg)
{
struct bulk_write *bulk = msg->userdata;
// this is a final call before timeout, to clean up
if (msg->data == NULL) {
goto close;
}
assert_param(NULL != bulk);
if (msg->type == MSG_BULK_ABORT) {
goto close;
}
else if (msg->type == MSG_BULK_DATA || msg->type == MSG_BULK_END) {
// if past len, say we're done and close
if (bulk->offset >= bulk->len) {
com_respond_err(bulk->frame_id, "WRITE OVERRUN");
goto close;
}
bulk->write(bulk, msg->data, msg->len);
// Return SUCCESS
com_respond_ok(msg->frame_id);
// advance the position pointer (can be used by the write callback)
bulk->offset += msg->len;
if (msg->type == MSG_BULK_END) {
// this was the last chunk
goto close;
}
}
return TF_RENEW;
close:
if (bulk) {
// Ask user to free the bulk and userdata
bulk->write(bulk, NULL, 0);
msg->userdata = NULL;
}
return TF_CLOSE;
}
/** Start the bulk write flow */
void bulkwrite_start(TinyFrame *tf, struct bulk_write *bulk)
{
assert_param(bulk);
assert_param(bulk->len);
assert_param(bulk->write);
bulk->offset = 0;
{
uint8_t buf[8];
PayloadBuilder pb = pb_start(buf, 8, NULL);
pb_u32(&pb, bulk->len);
pb_u32(&pb, TF_MAX_PAYLOAD_RX);
// We use userdata1 to hold a reference to the bulk transfer
TF_Msg msg = {
.type = MSG_BULK_WRITE_OFFER,
.frame_id = bulk->frame_id,
.is_response = true, // this ensures the frame_id is not re-generated
.data = buf,
.len = (TF_LEN) pb_length(&pb),
.userdata = bulk,
};
TF_Query(tf, &msg, bulkwrite_lst, BULK_LST_TIMEOUT_MS);
}
}

@ -0,0 +1,41 @@
//
// Created by MightyPork on 2017/12/23.
//
#ifndef GEX_F072_MSG_BULKWRITE_H
#define GEX_F072_MSG_BULKWRITE_H
#include <TinyFrame.h>
typedef struct bulk_write BulkWrite;
/**
* Type of the bulk-write listener. Offset within the data can be retrieved from bulk->offset.
*
* If buffer is NULL, this is the last call and the bulk struct must be freed if it was allocated.
*
* @param bulk - a data object passed to the bulkwrite_start() function.
* @param chunk - destination buffer to fill with the data. NULL if bulk should be freed.
* @param len - size of the chunk to write
*/
typedef void (*bulkwrite_data_cb)(BulkWrite *bulk, const uint8_t *chunk, uint32_t len);
/** Bulk read structure */
struct bulk_write {
TF_ID frame_id; //!< ID of the requesting frame, used for the entire bulk read session
bulkwrite_data_cb write; //!< Write callback
uint32_t len; //!< Total data length
void *userdata; //!< A place for arbitrary userdata
uint32_t offset; //!< Internal offset counter, will be set to 0 on start.
};
/**
* Start a bulk write session
* @param tf - tinyframe instance
* @param bulk - bulk write config structure (malloc'd or static)
*/
void bulkwrite_start(TinyFrame *tf, struct bulk_write *bulk);
#endif //GEX_F072_MSG_BULKWRITE_H

@ -0,0 +1,98 @@
//
// Created by MightyPork on 2017/12/22.
//
#include "messages.h"
#include "msg_responses.h"
void com_respond_snprintf(TF_ID frame_id, TF_TYPE type, 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);
com_respond_buf(frame_id, type, (const uint8_t *) buf, len);
}
void com_respond_buf(TF_ID frame_id, TF_TYPE type, const uint8_t *buf, uint32_t len)
{
TF_Msg msg;
TF_ClearMsg(&msg);
{
msg.type = type;
msg.frame_id = frame_id;
msg.data = buf;
msg.len = (TF_LEN) len;
}
TF_Respond(comm, &msg);
}
void com_respond_ok(TF_ID frame_id)
{
com_respond_buf(frame_id, MSG_SUCCESS, NULL, 0);
}
void com_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
}
void com_respond_str(TF_TYPE type, TF_ID frame_id, const char *str)
{
com_respond_buf(frame_id, type, (const uint8_t *) str, (uint32_t) strlen(str));
}
// ---------------------------------------------------------------------------
void com_respond_err(TF_ID frame_id, const char *message)
{
com_respond_str(MSG_ERROR, frame_id, message);
}
void com_respond_bad_cmd(TF_ID frame_id)
{
com_respond_err(frame_id, "BAD COMMAND");
}
void com_respond_malformed_cmd(TF_ID frame_id)
{
com_respond_err(frame_id, "MALFORMED PAYLOAD");
}
// ---------------------------------------------------------------------------
void com_respond_u8(TF_ID frame_id, uint8_t d)
{
com_respond_buf(frame_id, MSG_SUCCESS, (const uint8_t *) &d, 1);
}
void com_respond_u16(TF_ID frame_id, uint16_t d)
{
com_respond_buf(frame_id, MSG_SUCCESS, (const uint8_t *) &d, 2);
}
void com_respond_u32(TF_ID frame_id, uint32_t d)
{
com_respond_buf(frame_id, MSG_SUCCESS, (const uint8_t *) &d, 4);
}

@ -0,0 +1,114 @@
//
// Created by MightyPork on 2017/12/22.
//
#ifndef GEX_F072_MSG_RESPONSES_H
#define GEX_F072_MSG_RESPONSES_H
#ifndef GEX_MESSAGES_H
#error "Include messages.h instead!"
#endif
/**
* 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)))
com_respond_snprintf(TF_ID frame_id, TF_TYPE type, 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 com_respond_buf(TF_ID frame_id, TF_TYPE type, 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
*/
void com_respond_ok(TF_ID frame_id);
/**
* 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 com_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
*/
void com_respond_str(TF_TYPE type, TF_ID frame_id, const char *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 com_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 com_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 com_respond_malformed_cmd(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 com_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 com_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 com_respond_u32(TF_ID frame_id, uint32_t d);
#endif //GEX_F072_MSG_RESPONSES_H

@ -4,6 +4,7 @@
#include <TinyFrame.h>
#include "platform/debug_uart.h"
#include "platform/status_led.h"
#include "utils/stacksmon.h"
/* External variables --------------------------------------------------------*/
@ -29,6 +30,8 @@ void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName)
called if a stack overflow is detected. */
PRINTF(tFAULT" RTOS stack overflow! tsk: %s\r\n", (char *) pcTaskName);
StatusLed_On(STATUS_FAULT);
stackmon_dump();
while (1);
}

@ -2,6 +2,7 @@
// Created by MightyPork on 2017/11/26.
//
#include <utils/hexdump.h>
#include "platform.h"
#include "utils/avrlibc.h"
#include "comm/messages.h"
@ -494,11 +495,6 @@ uint32_t ureg_get_num_units(void)
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)
{
@ -513,17 +509,20 @@ void ureg_deliver_unit_request(TF_Msg *msg)
if (!pp.ok) { dbg("!! pp not OK!"); }
if (callsign == 0 || !pp.ok) {
sched_respond_malformed_cmd(msg->frame_id);
com_respond_malformed_cmd(msg->frame_id);
return;
}
UlistEntry *li = ulist_head;
while (li != NULL) {
Unit *const pUnit = &li->unit;
if (pUnit->callsign == callsign) {
if (pUnit->callsign == callsign && pUnit->status == E_SUCCESS) {
bool ok = pUnit->driver->handleRequest(pUnit, msg->frame_id, command, &pp);
// send extra SUCCESS confirmation message.
// error is expected to have already been reported.
if (ok && confirmed) {
sched_respond_suc(msg->frame_id);
com_respond_ok(msg->frame_id);
}
return;
}
@ -531,48 +530,47 @@ void ureg_deliver_unit_request(TF_Msg *msg)
}
// Not found
Job job = {
.cb = job_nosuch_unit,
.frame_id = msg->frame_id,
.d32 = callsign
};
scheduleJob(&job, TSK_SCHED_LOW);
com_respond_snprintf(msg->frame_id, MSG_ERROR, "NO UNIT @ %"PRIu8, callsign);
}
/** Send a response for a unit-list request */
void ureg_report_active_units(TF_ID frame_id)
{
// count bytes needed
uint32_t needed = 1; //
uint32_t msglen = 1; // for the count byte
UlistEntry *li = ulist_head;
uint32_t count = 0;
while (li != NULL) {
count++;
needed += strlen(li->unit.name)+1;
if (li->unit.status == E_SUCCESS) {
count++;
msglen += strlen(li->unit.name) + 1;
msglen += strlen(li->unit.driver->name) + 1;
}
li = li->next;
}
needed += count;
msglen += count; // one byte per message for the callsign
bool suc = true;
uint8_t *buff = malloc_ck(needed, &suc);
if (!suc) { tf_respond_str(MSG_ERROR, frame_id, "OUT OF MEMORY"); return; }
uint8_t *buff = malloc_ck(msglen, &suc);
if (!suc) { com_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
PayloadBuilder pb = pb_start(buff, msglen, NULL);
pb_u8(&pb, (uint8_t) count); // assume we don't have more than 255 units
li = ulist_head;
while (li != NULL) {
pb_u8(&pb, li->unit.callsign);
pb_string(&pb, li->unit.name);
if (li->unit.status == E_SUCCESS) {
pb_u8(&pb, li->unit.callsign);
pb_string(&pb, li->unit.name);
pb_string(&pb, li->unit.driver->name);
}
li = li->next;
}
assert_param(pb.ok);
tf_respond_buf(MSG_SUCCESS, frame_id, buff, needed);
com_respond_buf(frame_id, MSG_SUCCESS, buff, msglen);
}
free(buff);
}

@ -57,23 +57,34 @@
/* USER CODE END Includes */
/* Variables -----------------------------------------------------------------*/
#define STACK_MAIN 160
#define STACK_MSG 230
#define STACK_JOBRUNNER 150
osThreadId tskMainHandle;
uint32_t mainTaskBuffer[ 160 ];
uint32_t mainTaskBuffer[ STACK_MAIN ];
osStaticThreadDef_t mainTaskControlBlock;
osThreadId tskSchedLPHandle;
uint32_t schedLowBuffer[ 128 ];
osStaticThreadDef_t schedLowControlBlock;
osThreadId tskSchedHPHandle;
uint32_t schedHighBuffer[ 128 ];
osStaticThreadDef_t schedHighControlBlock;
osMessageQId queSchedLPHandle;
uint8_t myQueue01Buffer[ 5 * sizeof( struct sched_que_item ) ];
osStaticMessageQDef_t myQueue01ControlBlock;
osMessageQId queSchedHPHandle;
uint8_t myQueue02Buffer[ 5 * sizeof( struct sched_que_item ) ];
osThreadId tskMsgHandle;
uint32_t msgTaskBuffer[ STACK_MSG ];
osStaticThreadDef_t msgTaskControlBlock;
osThreadId tskJobRunnerHandle;
uint32_t jobRunnerBuffer[ STACK_JOBRUNNER ];
osStaticThreadDef_t jobRunnerControlBlock;
osMessageQId queSchedHandle;
uint8_t myQueue02Buffer[ HP_SCHED_CAPACITY * sizeof( struct sched_que_item ) ];
osStaticMessageQDef_t myQueue02ControlBlock;
osMessageQId queRxDataHandle;
uint8_t myQueue03Buffer[ RX_QUE_CAPACITY * sizeof( struct rx_que_item ) ];
osStaticMessageQDef_t myQueue03ControlBlock;
osMutexId mutTinyFrameTxHandle;
osStaticMutexDef_t myMutex01ControlBlock;
osSemaphoreId semVcomTxReadyHandle;
osStaticSemaphoreDef_t myBinarySem01ControlBlock;
@ -84,7 +95,8 @@ osStaticSemaphoreDef_t myBinarySem01ControlBlock;
/* Function prototypes -------------------------------------------------------*/
void TaskMain(void const * argument);
extern void TaskSchedLP(void const * argument);
extern void TaskSchedHP(void const * argument);
extern void TaskJobQueue(void const *argument);
extern void TaskMessaging(void const * argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
@ -125,8 +137,8 @@ void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackTy
void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN Init */
stackmon_register("Main", mainTaskBuffer, sizeof(mainTaskBuffer));
stackmon_register("Job Queue Low", schedLowBuffer, sizeof(schedLowBuffer));
stackmon_register("Job Queue High", schedHighBuffer, sizeof(schedHighBuffer));
stackmon_register("JobRunner", jobRunnerBuffer, sizeof(jobRunnerBuffer));
stackmon_register("Messaging", msgTaskBuffer, sizeof(msgTaskBuffer));
/* USER CODE END Init */
/* Create the mutex(es) */
@ -154,29 +166,30 @@ void MX_FREERTOS_Init(void) {
/* Create the thread(s) */
/* definition and creation of tskMain */
osThreadStaticDef(tskMain, TaskMain, osPriorityHigh, 0, 160, mainTaskBuffer, &mainTaskControlBlock);
osThreadStaticDef(tskMain, TaskMain, osPriorityHigh, 0, STACK_MAIN, mainTaskBuffer, &mainTaskControlBlock);
tskMainHandle = osThreadCreate(osThread(tskMain), NULL);
/* definition and creation of tskSchedLP */
osThreadStaticDef(tskSchedLP, TaskSchedLP, osPriorityLow, 0, 128, schedLowBuffer, &schedLowControlBlock);
tskSchedLPHandle = osThreadCreate(osThread(tskSchedLP), NULL);
/* definition and creation of tskJobRunner */
osThreadStaticDef(tskJobRunner, TaskJobQueue, osPriorityAboveNormal, 0, STACK_JOBRUNNER, jobRunnerBuffer, &jobRunnerControlBlock);
tskJobRunnerHandle = osThreadCreate(osThread(tskJobRunner), NULL);
/* definition and creation of tskSchedHP */
osThreadStaticDef(tskSchedHP, TaskSchedHP, osPriorityAboveNormal, 0, 128, schedHighBuffer, &schedHighControlBlock);
tskSchedHPHandle = osThreadCreate(osThread(tskSchedHP), NULL);
/* definition and creation of TaskMessaging */
osThreadStaticDef(tskMsg, TaskMessaging, osPriorityNormal, 0, STACK_MSG, msgTaskBuffer, &msgTaskControlBlock);
tskMsgHandle = osThreadCreate(osThread(tskMsg), NULL);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* Create the queue(s) */
/* definition and creation of queSchedLP */
osMessageQStaticDef(queSchedLP, 5, struct sched_que_item, myQueue01Buffer, &myQueue01ControlBlock);
queSchedLPHandle = osMessageCreate(osMessageQ(queSchedLP), NULL);
/* definition and creation of queSchedHP */
osMessageQStaticDef(queSchedHP, 5, struct sched_que_item, myQueue02Buffer, &myQueue02ControlBlock);
queSchedHPHandle = osMessageCreate(osMessageQ(queSchedHP), NULL);
osMessageQStaticDef(queSchedHP, HP_SCHED_CAPACITY, struct sched_que_item, myQueue02Buffer, &myQueue02ControlBlock);
queSchedHandle = osMessageCreate(osMessageQ(queSchedHP), NULL);
/* definition and creation of queRxData */
osMessageQStaticDef(queRxData, RX_QUE_CAPACITY, struct rx_que_item, myQueue03Buffer, &myQueue03ControlBlock);
queRxDataHandle = osMessageCreate(osMessageQ(queRxData), NULL);
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */

@ -7,6 +7,7 @@ GEX_SRC_DIR = \
User/units \
User/units/system \
User/units/neopixel \
User/units/test \
User/units/pin \
User/TinyFrame \
User/CWPack \
@ -36,10 +37,6 @@ GEX_INCLUDES = \
-IUser/TinyFrame \
-IUser/vfs \
-IUser/utils \
-IUser/units \
-IUser/units/system \
-IUser/units/neopixel \
-IUser/units/pin \
-IUser/framework \
-IUser/platform \
-IUser/tasks \
@ -65,10 +62,14 @@ GEX_CFLAGS = \
-fmerge-constants -fmerge-all-constants \
-fno-exceptions -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast -finline-small-functions -findirect-inlining \
GEX_CDEFS = \
GEX_CDEFS_BASE = \
-D__weak="__attribute__((weak))" \
-D__packed="__attribute__((__packed__))" \
-DUSE_FULL_LL_DRIVER \
# TODO implement debug build choice
ifeq '1' '1'
GEX_CDEFS = $(GEX_CDEFS_BASE) \
-DUSE_FULL_ASSERT=1 \
-DVERBOSE_ASSERT=1 \
-DDEBUG_VFS=0 \
@ -76,3 +77,13 @@ GEX_CDEFS = \
-DVERBOSE_HARDFAULT=1 \
-DUSE_STACK_MONITOR=1 \
-DUSE_DEBUG_UART=1
else
GEX_CDEFS = $(GEX_CDEFS_BASE) \
-DUSE_FULL_ASSERT=0 \
-DVERBOSE_ASSERT=0 \
-DDEBUG_VFS=0 \
-DDEBUG_FLASH_WRITE=0 \
-DVERBOSE_HARDFAULT=0 \
-DUSE_STACK_MONITOR=0 \
-DUSE_DEBUG_UART=0
endif

@ -79,6 +79,7 @@ ssize_t _write_r(struct _reent *rptr, int fd, const void *buf, size_t len)
#else
// No-uart variant
void DebugUart_PreInit(void) {}
void DebugUart_Init(void) {}
ssize_t _write_r(struct _reent *rptr, int fd, const void *buf, size_t len) {
return len;

@ -6,11 +6,11 @@
#include "usbd_core.h"
#include "USB/usb_device.h"
#include "framework/resources.h"
#include "framework/unit_registry.h"
#include "units/pin/unit_pin.h"
#include "units/neopixel/unit_neopixel.h"
#include "units/test/unit_test.h"
// ----- SUPPORTED UNITS -----
@ -20,6 +20,8 @@ void plat_register_units(void)
ureg_add_type(&UNIT_PIN);
ureg_add_type(&UNIT_NEOPIXEL);
ureg_add_type(&UNIT_TEST);
// Platform-specific units could be added here
}

@ -5,7 +5,7 @@
#ifndef GEX_SCHED_QUEUE_H
#define GEX_SCHED_QUEUE_H
#include <TinyFrame/TinyFrame.h>
#include <TinyFrame.h>
typedef struct sched_que_item Job;
typedef void (*ScheduledJobCb) (Job *job);
@ -29,4 +29,13 @@ struct sched_que_item {
};
};
// This que is used to stash frames received from TinyFrame for later evaluation on the application thread
struct rx_que_item {
uint32_t len;
uint8_t data[64];
};
#define HP_SCHED_CAPACITY 5
#define RX_QUE_CAPACITY 16
#endif //GEX_SCHED_QUEUE_H

@ -48,7 +48,7 @@ void TaskMain(void const * argument)
// 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();
if ((cnt%75)==0) stackmon_dump();
continue;
}

@ -0,0 +1,31 @@
//
// Created by MightyPork on 2017/12/22.
//
#include "platform.h"
#include "comm/messages.h"
#include "utils/hexdump.h"
#include "task_msg.h"
#include "sched_queue.h"
/**
* Process data received from TinyFrame.
* The queue holds received messages or parts of messages,
* copied there by the USB thread.
*
* Since this is a separate thread that handles TF input and runs the listeners,
* TF functions (send, respond) can be called immediately without the need for an
* intermediate queued job.
*/
void TaskMessaging(const void * argument)
{
dbg("> Message queue task started!");
struct rx_que_item slot;
while (1) {
xQueueReceive(queRxDataHandle, &slot, osWaitForever);
assert_param(slot.len>0 && slot.len<=64); // check the len is within bounds
TF_Accept(comm, slot.data, slot.len);
}
}

@ -0,0 +1,12 @@
//
// Created by MightyPork on 2017/12/22.
//
#ifndef GEX_F072_TASK_MSG_H
#define GEX_F072_TASK_MSG_H
extern osMessageQId queRxDataHandle;
extern osThreadId tskMsgHandle;
void TaskMessaging(const void * argument);
#endif //GEX_F072_TASK_MSG_H

@ -5,23 +5,18 @@
#include "platform.h"
#include "task_sched.h"
extern osMessageQId queSchedLPHandle;
extern osMessageQId queSchedHPHandle;
extern osMessageQId queSchedHandle;
volatile uint32_t jobQueHighWaterMarkHP = 0;
volatile uint32_t jobQueHighWaterMarkLP = 0;
volatile uint32_t jobQueHighWaterMark = 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)
void scheduleJob(Job *job)
{
QueueHandle_t que = (prio == TSK_SCHED_LOW ? queSchedLPHandle : queSchedHPHandle);
QueueHandle_t que = queSchedHandle;
assert_param(que != NULL);
assert_param(job->cb != NULL);
@ -44,44 +39,22 @@ void scheduleJob(Job *job, enum task_sched_prio prio)
}
#if USE_STACK_MONITOR
if (prio == TSK_SCHED_LOW) {
jobQueHighWaterMarkLP = MAX(jobQueHighWaterMarkLP, count);
} else {
jobQueHighWaterMarkHP = MAX(jobQueHighWaterMarkHP, count);
}
jobQueHighWaterMark = MAX(jobQueHighWaterMark, 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
* job queue handler (for use in interrupts to do longer stuff on a thread)
*
* @param argument
*/
void TaskSchedHP (const void * argument)
void TaskJobQueue(const void *argument)
{
dbg("> High priority queue task started!");
struct sched_que_item job;
while (1) {
xQueueReceive(queSchedHPHandle, &job, osWaitForever);
xQueueReceive(queSchedHandle, &job, osWaitForever);
assert_param(job.cb != NULL);
job.cb(&job);

@ -8,22 +8,13 @@
#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;
extern volatile uint32_t jobQueHighWaterMark;
#endif
extern osThreadId tskSchedLPHandle;
void TaskSchedLP (const void * argument);
extern osThreadId tskSchedHPHandle;
void TaskSchedHP (const void * argument);
extern osThreadId tskJobRunnerHandle;
void TaskJobQueue(const void *argument);
void scheduleJob(Job *job, enum task_sched_prio prio);
void scheduleJob(Job *job);
#endif //GEX_TASK_SCHED_H

@ -173,14 +173,14 @@ static bool Npx_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, Paylo
break;
default:
sched_respond_bad_cmd(frame_id);
com_respond_bad_cmd(frame_id);
return false;
}
return true;
bad_count:
sched_respond_err(frame_id, "BAD PIXEL COUNT");
com_respond_err(frame_id, "BAD PIXEL COUNT");
return false;
}

@ -194,23 +194,23 @@ static bool Pin_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, Paylo
case CMD_READ:
if (!priv->output) {
sched_respond_u8(frame_id, (bool) LL_GPIO_IsInputPinSet(priv->port, priv->ll_pin));
com_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);
com_respond_bad_cmd(frame_id);
return false;
}
return true;
must_be_output:
sched_respond_err(frame_id, "NOT OUTPUT PIN");
com_respond_err(frame_id, "NOT OUTPUT PIN");
return false;
must_be_input:
sched_respond_err(frame_id, "NOT INPUT PIN");
com_respond_err(frame_id, "NOT INPUT PIN");
return false;
}

@ -0,0 +1,190 @@
//
// Created by MightyPork on 2017/11/25.
//
#include "comm/messages.h"
#include "unit_base.h"
#include "unit_test.h"
/** Private data structure */
struct priv {
uint32_t unused;
};
// ------------------------------------------------------------------------
/** Load from a binary buffer stored in Flash */
static void Tst_loadBinary(Unit *unit, PayloadParser *pp)
{
struct priv *priv = unit->data;
//
}
/** Write to a binary buffer for storing in Flash */
static void Tst_writeBinary(Unit *unit, PayloadBuilder *pb)
{
struct priv *priv = unit->data;
//
}
// ------------------------------------------------------------------------
/** Parse a key-value pair from the INI file */
static bool Tst_loadIni(Unit *unit, const char *key, const char *value)
{
bool suc = true;
struct priv *priv = unit->data;
//
return suc;
}
/** Generate INI file section for the unit */
static void Tst_writeIni(Unit *unit, IniWriter *iw)
{
struct priv *priv = unit->data;
//
}
// ------------------------------------------------------------------------
/** Allocate data structure and set defaults */
static bool Tst_preInit(Unit *unit)
{
bool suc = true;
struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv), &suc);
CHECK_SUC();
//
return true;
}
/** Finalize unit set-up */
static bool Tst_init(Unit *unit)
{
bool suc = true;
struct priv *priv = unit->data;
//
return true;
}
/** Tear down the unit */
static void Tst_deInit(Unit *unit)
{
struct priv *priv = unit->data;
//
// Free memory
free(unit->data);
unit->data = NULL;
}
// ------------------------------------------------------------------------
enum PinCmd_ {
CMD_PING = 0,
CMD_ECHO = 1,
CMD_BULKREAD = 2,
CMD_BULKWRITE = 3,
};
static const char *longtext = "The history of all hitherto existing societies is the history of class struggles.\n\nFreeman and slave, patrician and plebeian, lord and serf, guild-master and journeyman, in a word, oppressor and oppressed, stood in constant opposition to one another, carried on an uninterrupted, now hidden, now open fight, a fight that each time ended, either in a revolutionary re-constitution of society at large, or in the common ruin of the contending classes.\n\nIn the earlier epochs of history, we find almost everywhere a complicated arrangement of society into various orders, a manifold gradation of social rank. In ancient Rome we have patricians, knights, plebeians, slaves; in the Middle Ages, feudal lords, vassals, guild-masters, journeymen, apprentices, serfs; in almost all of these classes, again, subordinate gradations.\n\nThe modern bourgeois society that has sprouted from the ruins of feudal society has not done away with class antagonisms. It has but established new classes, new conditions of oppression, new forms of struggle in place of the old ones. Our epoch, the epoch of the bourgeoisie, possesses, however, this distinctive feature: it has simplified the class antagonisms. Society as a whole is more and more splitting up into two great hostile camps, into two great classes, directly facing each other: Bourgeoisie and Proletariat.\n\nFrom the serfs of the Middle Ages sprang the chartered burghers of the earliest towns. From these burgesses the first elements of the bourgeoisie were developed.\n\nThe discovery of America, the rounding of the Cape, opened up fresh ground for the rising bourgeoisie. The East-Indian and Chinese markets, the colonisation of America, trade with the colonies, the increase in the means of exchange and in commodities generally, gave to commerce, to navigation, to industry, an impulse never before known, and thereby, to the revolutionary element in the tottering feudal society, a rapid development.\n\nThe feudal system of industry, under which industrial production was monopolised by closed guilds, now no longer sufficed for the growing wants of the new markets. The manufacturing system took its place. The guild-masters were pushed on one side by the manufacturing middle class; division of labour between the different corporate guilds vanished in the face of division of labour in each single workshop.\n\nMeantime the markets kept ever growing, the demand ever rising. Even manufacture no longer sufficed. Thereupon, steam and machinery revolutionised industrial production. The place of manufacture was taken by the giant, Modern Industry, the place of the industrial middle class, by industrial millionaires, the leaders of whole industrial armies, the modern bourgeois.\n\nModern industry has established the world-market, for which the discovery of America paved the way. This market has given an immense development to commerce, to navigation, to communication by land. This development has, in its time, reacted on the extension of industry; and in proportion as industry, commerce, navigation, railways extended, in the same proportion the bourgeoisie developed, increased its capital, and pushed into the background every class handed down from the Middle Ages.\n\nWe see, therefore, how the modern bourgeoisie is itself the product of a long course of development, of a series of revolutions in the modes of production and of exchange.\n\nEach step in the development of the bourgeoisie was accompanied by a corresponding political advance of that class. An oppressed class under the sway of the feudal nobility, an armed and self-governing association in the mediaeval commune; here independent urban republic (as in Italy and Germany), there taxable \"third estate\" of the monarchy (as in France), afterwards, in the period of manufacture proper, serving either the semi-feudal or the absolute monarchy as a counterpoise against the nobility, and, in fact, corner-stone of the great monarchies in general, the bourgeoisie has at last, since the establishment of Modern Industry and of the world-market, conquered for itself, in the modern representative State, exclusive political sway. The executive of the modern State is but a committee for managing the common affairs of the whole bourgeoisie.";
static void br_longtext(struct bulk_read *bulk, uint32_t chunk, uint8_t *buffer)
{
// clean-up request
if (buffer == NULL) {
free(bulk);
return;
}
memcpy(buffer, longtext+bulk->offset, chunk);
}
static void bw_dump(struct bulk_write *bulk, const uint8_t *chunk, uint32_t len)
{
// clean-up request
if (chunk == NULL) {
free(bulk);
return;
}
dbg("\r\nBulk write at %d, len %d", (int)bulk->offset, (int)len);
PUTSN((const char *) chunk, len);
PUTS("\r\n");
}
/** Handle a request message */
static bool Tst_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp)
{
(void)pp;
struct priv *priv = unit->data;
switch (command) {
case CMD_PING:
com_respond_ok(frame_id);
break;
case CMD_ECHO:;
uint32_t len;
const uint8_t *data = pp_tail(pp, &len);
com_respond_buf(frame_id, MSG_SUCCESS, data, len);
break;
case CMD_BULKREAD:;
struct bulk_read *br = malloc(sizeof(struct bulk_read));
assert_param(br);
br->len = (uint32_t) strlen(longtext);
br->frame_id = frame_id;
br->read = br_longtext;
bulkread_start(comm, br);
break;
case CMD_BULKWRITE:;
struct bulk_write *bw = malloc(sizeof(struct bulk_write));
assert_param(bw);
bw->len = 1024;
bw->frame_id = frame_id;
bw->write = bw_dump;
bulkwrite_start(comm, bw);
break;
default:
com_respond_bad_cmd(frame_id);
return false;
}
return true;
}
// ------------------------------------------------------------------------
/** Unit template */
const UnitDriver UNIT_TEST = {
.name = "TEST",
.description = "Test unit",
// Settings
.preInit = Tst_preInit,
.cfgLoadBinary = Tst_loadBinary,
.cfgWriteBinary = Tst_writeBinary,
.cfgLoadIni = Tst_loadIni,
.cfgWriteIni = Tst_writeIni,
// Init
.init = Tst_init,
.deInit = Tst_deInit,
// Function
.handleRequest = Tst_handleRequest,
};

@ -0,0 +1,13 @@
//
// Created by MightyPork on 2017/11/25.
// Testing unit that uses most of the protocol to verify the core functionality
//
#ifndef U_TEST_H
#define U_TEST_H
#include "unit.h"
extern const UnitDriver UNIT_TEST;
#endif //U_TEST_H

@ -35,11 +35,11 @@ extern "C" {
#define KB(size) ((size) * 1024)
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#endif
#define ROUND_UP(value, boundary) ((value) + ((boundary) - (value)) % (boundary))

@ -14,7 +14,7 @@ struct stackhandle {
uint32_t len;
};
#define STACK_NUM 16
#define STACK_NUM 8
static uint32_t nextidx = 0;
static struct stackhandle stacks[STACK_NUM];
@ -64,9 +64,9 @@ void stackmon_dump(void)
);
}
PUTS("\033[36m>> QUEUES\033[m\r\n");
PRINTF(" Used slots: \033[33mHP %"PRIu32", LP %"PRIu32"\033[m\r\n",
jobQueHighWaterMarkHP, jobQueHighWaterMarkLP);
PUTS("\033[36m>> JOB QUEUE\033[m\r\n");
PRINTF(" Used slots: \033[33m%"PRIu32"\033[m\r\n",
jobQueHighWaterMark);
PRINTF("\033[1m---------------------------\033[m\r\n\r\n");
}

Loading…
Cancel
Save