|
|
|
@ -4,11 +4,15 @@ |
|
|
|
|
|
|
|
|
|
#include <malloc.h> |
|
|
|
|
#include <assert.h> |
|
|
|
|
#include <utils/payload_parser.h> |
|
|
|
|
#include <utils/payload_builder.h> |
|
|
|
|
|
|
|
|
|
#define GEX_H // to allow including other headers
|
|
|
|
|
#include "gex_defines.h" |
|
|
|
|
#include "gex_helpers.h" |
|
|
|
|
#include "gex_message_types.h" |
|
|
|
|
#include "gex_unit.h" |
|
|
|
|
#include "gex_internal.h" |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Low level query |
|
|
|
@ -20,7 +24,11 @@ |
|
|
|
|
* @param lst - TF listener to handle the response, can be NULL |
|
|
|
|
* @param userdata2 userdata2 argument for the TF listener's message |
|
|
|
|
*/ |
|
|
|
|
static void GEX_LL_Query(GexUnit *unit, uint8_t cmd, const uint8_t *payload, uint32_t len, TF_Listener lst, void *userdata2) |
|
|
|
|
static void GEX_LL_Query(GexUnit *unit, |
|
|
|
|
uint8_t cmd, |
|
|
|
|
const uint8_t *payload, uint32_t len, |
|
|
|
|
GexSession session, bool is_reply, |
|
|
|
|
TF_Listener lst, void *userdata2) |
|
|
|
|
{ |
|
|
|
|
assert(unit != NULL); |
|
|
|
|
assert(unit->gex != NULL); |
|
|
|
@ -45,6 +53,8 @@ static void GEX_LL_Query(GexUnit *unit, uint8_t cmd, const uint8_t *payload, uin |
|
|
|
|
msg.len = (TF_LEN) (len + 2); |
|
|
|
|
msg.userdata = unit; |
|
|
|
|
msg.userdata2 = userdata2; |
|
|
|
|
msg.frame_id = session; |
|
|
|
|
msg.is_response = is_reply; // This ensures the frame_id is not re-generated
|
|
|
|
|
TF_Query(gex->tf, &msg, lst, 0); |
|
|
|
|
} |
|
|
|
|
free(pld); |
|
|
|
@ -56,7 +66,24 @@ void GEX_Send(GexUnit *unit, uint8_t cmd, const uint8_t *payload, uint32_t len) |
|
|
|
|
assert(unit != NULL); |
|
|
|
|
assert(unit->gex != NULL); |
|
|
|
|
|
|
|
|
|
GEX_LL_Query(unit, cmd, payload, len, NULL, NULL); |
|
|
|
|
GEX_LL_Query(unit, cmd, |
|
|
|
|
payload, len, |
|
|
|
|
0, false, |
|
|
|
|
NULL, NULL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Send with no listener, don't wait for response */ |
|
|
|
|
void GEX_SendEx(GexUnit *unit, uint8_t cmd, |
|
|
|
|
const uint8_t *payload, uint32_t len, |
|
|
|
|
GexSession session, bool is_reply) |
|
|
|
|
{ |
|
|
|
|
assert(unit != NULL); |
|
|
|
|
assert(unit->gex != NULL); |
|
|
|
|
|
|
|
|
|
GEX_LL_Query(unit, cmd, |
|
|
|
|
payload, len, |
|
|
|
|
session, is_reply, |
|
|
|
|
NULL, NULL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** listener for the synchronous query functionality */ |
|
|
|
@ -66,60 +93,82 @@ static TF_Result sync_query_lst(TinyFrame *tf, TF_Msg *msg) |
|
|
|
|
assert(gex != NULL); |
|
|
|
|
|
|
|
|
|
// clone the message
|
|
|
|
|
gex->sync_query_response.len = msg->len; |
|
|
|
|
gex->sync_query_response.unit = msg->userdata; |
|
|
|
|
gex->sync_query_response.type = msg->type; |
|
|
|
|
gex->squery_msg.len = msg->len; |
|
|
|
|
gex->squery_msg.unit = msg->userdata; |
|
|
|
|
gex->squery_msg.type = msg->type; |
|
|
|
|
gex->squery_msg.session = msg->frame_id; |
|
|
|
|
// clone the buffer
|
|
|
|
|
if (msg->len > 0) memcpy(gex->sync_query_buffer, msg->data, msg->len); |
|
|
|
|
if (msg->len > 0) memcpy(gex->squery_buffer, msg->data, msg->len); |
|
|
|
|
// re-link the buffer
|
|
|
|
|
gex->sync_query_response.payload = gex->sync_query_buffer; |
|
|
|
|
gex->sync_query_ok = true; |
|
|
|
|
gex->squery_msg.payload = gex->squery_buffer; |
|
|
|
|
gex->squery_ok = true; |
|
|
|
|
return TF_CLOSE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Static query */ |
|
|
|
|
GexMsg GEX_Query(GexUnit *unit, uint8_t cmd, const uint8_t *payload, uint32_t len) |
|
|
|
|
static GexMsg GEX_QueryEx(GexUnit *unit, uint8_t cmd, |
|
|
|
|
const uint8_t *payload, uint32_t len, |
|
|
|
|
GexSession session, bool is_reply) |
|
|
|
|
{ |
|
|
|
|
assert(unit != NULL); |
|
|
|
|
assert(unit->gex != NULL); |
|
|
|
|
|
|
|
|
|
GexClient *gex = unit->gex; |
|
|
|
|
|
|
|
|
|
gex->sync_query_ok = false; |
|
|
|
|
gex->squery_ok = false; |
|
|
|
|
|
|
|
|
|
// Default response that will be used if nothing is received
|
|
|
|
|
gex->sync_query_response.unit = unit; |
|
|
|
|
gex->sync_query_response.type = MSG_ERROR; |
|
|
|
|
sprintf((char *) gex->sync_query_buffer, "TIMEOUT"); |
|
|
|
|
gex->sync_query_response.len = (uint32_t) strlen("TIMEOUT"); |
|
|
|
|
gex->sync_query_response.payload = gex->sync_query_buffer; |
|
|
|
|
gex->squery_msg.unit = unit; |
|
|
|
|
gex->squery_msg.type = MSG_ERROR; |
|
|
|
|
gex->squery_msg.session = 0; |
|
|
|
|
sprintf((char *) gex->squery_buffer, "TIMEOUT"); |
|
|
|
|
gex->squery_msg.len = (uint32_t) strlen("TIMEOUT"); |
|
|
|
|
gex->squery_msg.payload = gex->squery_buffer; |
|
|
|
|
|
|
|
|
|
GEX_LL_Query(unit, cmd, |
|
|
|
|
payload, len, |
|
|
|
|
session, is_reply, |
|
|
|
|
sync_query_lst, NULL); |
|
|
|
|
|
|
|
|
|
GEX_LL_Query(unit, cmd, payload, len, sync_query_lst, NULL); |
|
|
|
|
GEX_Poll(gex); |
|
|
|
|
|
|
|
|
|
if (!gex->sync_query_ok) { |
|
|
|
|
if (!gex->squery_ok) { |
|
|
|
|
fprintf(stderr, "No response to query of unit %s!", unit->name); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return gex->sync_query_response; |
|
|
|
|
return gex->squery_msg; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Static query */ |
|
|
|
|
GexMsg GEX_Query(GexUnit *unit, uint8_t cmd, const uint8_t *payload, uint32_t len) |
|
|
|
|
{ |
|
|
|
|
assert(unit != NULL); |
|
|
|
|
assert(unit->gex != NULL); |
|
|
|
|
return GEX_QueryEx(unit, cmd, payload, len, 0, false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** listener for the synchronous query functionality */ |
|
|
|
|
static TF_Result async_query_lst(TinyFrame *tf, TF_Msg *msg) |
|
|
|
|
{ |
|
|
|
|
(void)tf; |
|
|
|
|
GexMsg gexMsg; |
|
|
|
|
|
|
|
|
|
// clone the message
|
|
|
|
|
gexMsg.len = msg->len; |
|
|
|
|
gexMsg.session = msg->frame_id; |
|
|
|
|
gexMsg.unit = msg->userdata; // gex is accessible via the unit
|
|
|
|
|
gexMsg.unit = msg->userdata; // Unit passed to "userdata" in GEX_LL_Query
|
|
|
|
|
gexMsg.type = msg->type; |
|
|
|
|
gexMsg.session = msg->frame_id; |
|
|
|
|
gexMsg.payload = (uint8_t *) msg->data; |
|
|
|
|
|
|
|
|
|
assert( gexMsg.unit != NULL); |
|
|
|
|
|
|
|
|
|
GexEventListener lst = msg->userdata2; |
|
|
|
|
assert(lst != NULL); |
|
|
|
|
|
|
|
|
|
lst(gexMsg); |
|
|
|
|
return TF_CLOSE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Sync query, without poll */ |
|
|
|
@ -128,14 +177,129 @@ void GEX_QueryAsync(GexUnit *unit, uint8_t cmd, const uint8_t *payload, uint32_t |
|
|
|
|
assert(unit != NULL); |
|
|
|
|
assert(unit->gex != NULL); |
|
|
|
|
|
|
|
|
|
GexClient *gex = unit->gex; |
|
|
|
|
gex->sync_query_ok = false; |
|
|
|
|
memset(&gex->sync_query_response, 0, sizeof(GexMsg)); |
|
|
|
|
// Async query does not poll, instead the user listener is attached to the message
|
|
|
|
|
GEX_LL_Query(unit, cmd, |
|
|
|
|
payload, len, |
|
|
|
|
0, false, |
|
|
|
|
async_query_lst, lst); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Default response that will be used if nothing is received
|
|
|
|
|
gex->sync_query_response.type = MSG_ERROR; |
|
|
|
|
sprintf((char *) gex->sync_query_buffer, "TIMEOUT"); |
|
|
|
|
gex->sync_query_response.len = (uint32_t) strlen("TIMEOUT"); |
|
|
|
|
// ---------------------------- BULK ----------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Bulk read from a unit, synchronous |
|
|
|
|
* |
|
|
|
|
* @param unit - the unit to target |
|
|
|
|
* @param bulk - the bulk transport info struct |
|
|
|
|
* @return actual number of bytes received. |
|
|
|
|
*/ |
|
|
|
|
uint32_t GEX_BulkRead(GexUnit *unit, GexBulk *bulk) |
|
|
|
|
{ |
|
|
|
|
// We ask for the transfer. This is unit specific and can contain information that determines the transferred data
|
|
|
|
|
GexMsg resp0 = GEX_Query(unit, bulk->req_cmd, bulk->req_data, bulk->req_len); |
|
|
|
|
|
|
|
|
|
if (resp0.type == MSG_ERROR) { |
|
|
|
|
fprintf(stderr, "Bulk read rejected! %.*s", resp0.len, (char*)resp0.payload); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (resp0.type != MSG_BULK_READ_OFFER) { |
|
|
|
|
fprintf(stderr, "Bulk read failed, response not BULK_READ_OFFER!"); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Check how much data is available
|
|
|
|
|
PayloadParser pp = pp_start(resp0.payload, resp0.len, NULL); |
|
|
|
|
uint32_t total = pp_u32(&pp); |
|
|
|
|
assert(pp.ok); |
|
|
|
|
|
|
|
|
|
total = MIN(total, bulk->capacity); // clamp...
|
|
|
|
|
|
|
|
|
|
uint32_t at = 0; |
|
|
|
|
while (at < total) { |
|
|
|
|
GexMsg resp = GEX_QueryEx(unit, MSG_BULK_READ_POLL, |
|
|
|
|
NULL, 0, |
|
|
|
|
resp0.session, true); |
|
|
|
|
|
|
|
|
|
if (resp.type == MSG_ERROR) { |
|
|
|
|
fprintf(stderr, "Bulk read failed! %.*s", resp.len, (char *) resp.payload); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (resp.type == MSG_BULK_END) { |
|
|
|
|
// No more data
|
|
|
|
|
return at; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (resp.type == MSG_BULK_DATA) { |
|
|
|
|
memcpy(bulk->buffer+at, resp.payload, resp.len); |
|
|
|
|
at += resp.len; |
|
|
|
|
} else { |
|
|
|
|
fprintf(stderr, "Bulk read failed! Bad response type."); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return at; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Bulk write to a unit, synchronous |
|
|
|
|
* |
|
|
|
|
* @param unit - the unit to target |
|
|
|
|
* @param bulk - the bulk transport info struct |
|
|
|
|
* @return true on success |
|
|
|
|
*/ |
|
|
|
|
bool GEX_BulkWrite(GexUnit *unit, GexBulk *bulk) |
|
|
|
|
{ |
|
|
|
|
// We ask for the transfer. This is unit specific
|
|
|
|
|
GexMsg resp0 = GEX_Query(unit, bulk->req_cmd, bulk->req_data, bulk->req_len); |
|
|
|
|
|
|
|
|
|
if (resp0.type == MSG_ERROR) { |
|
|
|
|
fprintf(stderr, "Bulk write rejected! %.*s", resp0.len, (char*)resp0.payload); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (resp0.type != MSG_BULK_WRITE_OFFER) { |
|
|
|
|
fprintf(stderr, "Bulk write failed, response not MSG_BULK_WRITE_OFFER!"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
PayloadParser pp = pp_start(resp0.payload, resp0.len, NULL); |
|
|
|
|
uint32_t max_size = pp_u32(&pp); |
|
|
|
|
uint32_t max_chunk = pp_u32(&pp); |
|
|
|
|
assert(pp.ok); |
|
|
|
|
|
|
|
|
|
if (max_size < bulk->len) { |
|
|
|
|
fprintf(stderr, "Write not possible, not enough space."); |
|
|
|
|
// Inform GEX we're not going to do it
|
|
|
|
|
GEX_SendEx(unit, MSG_BULK_ABORT, NULL, 0, resp0.session, true); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
uint32_t at = 0; |
|
|
|
|
while (at < bulk->len) { |
|
|
|
|
uint32_t chunk = MIN(max_chunk, bulk->len - at); |
|
|
|
|
GexMsg resp = GEX_QueryEx(unit, MSG_BULK_DATA, |
|
|
|
|
bulk->buffer+at, chunk, |
|
|
|
|
resp0.session, true); |
|
|
|
|
at += chunk; |
|
|
|
|
|
|
|
|
|
if (resp.type == MSG_ERROR) { |
|
|
|
|
fprintf(stderr, "Bulk write failed! %.*s", resp.len, (char *) resp.payload); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (resp.type != MSG_SUCCESS) { |
|
|
|
|
fprintf(stderr, "Bulk write failed! Bad response type."); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Conclude the transfer
|
|
|
|
|
GEX_SendEx(unit, MSG_BULK_END, NULL, 0, resp0.session, true); |
|
|
|
|
|
|
|
|
|
GEX_LL_Query(unit, cmd, payload, len, async_query_lst, lst); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|