bulk kinda works (reading oly so far)Ã
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include "TinyFrame.h"
|
||||
#include "utils/hexdump.h"
|
||||
|
||||
#include "gex.h"
|
||||
#include "gex_internal.h"
|
||||
@@ -14,6 +15,8 @@ void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
|
||||
GexClient *gc = tf->userdata;
|
||||
assert(gc->acm_fd != 0);
|
||||
|
||||
hexDump("TF_Write", buff, (uint32_t)len);
|
||||
|
||||
ssize_t rv = write(gc->acm_fd, buff, len);
|
||||
if (rv != (ssize_t)len) {
|
||||
fprintf(stderr, "ERROR %d in TF write: %s\n", errno, strerror(errno));
|
||||
|
||||
+26
-3
@@ -8,6 +8,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <utils/hexdump.h>
|
||||
#include "TinyFrame.h"
|
||||
|
||||
#define GEX_H // to allow including other headers
|
||||
@@ -138,7 +139,8 @@ GexClient *GEX_Init(const char *device, int timeout_ms)
|
||||
|
||||
// --- Open the device ---
|
||||
gex->acm_device = device;
|
||||
gex->acm_fd = serial_open(device, false, (timeout_ms + 50) / 100);
|
||||
gex->ser_timeout = timeout_ms;
|
||||
gex->acm_fd = serial_open(device);
|
||||
if (gex->acm_fd == -1) {
|
||||
free(gex);
|
||||
fprintf(stderr, "FAILED TO CONNECT TO %s!\n", device);
|
||||
@@ -177,12 +179,33 @@ void GEX_Poll(GexClient *gex)
|
||||
|
||||
assert(gex != NULL);
|
||||
|
||||
ssize_t len = read(gex->acm_fd, pollbuffer, TF_MAX_PAYLOAD_RX); // TODO wait only for expect amount?
|
||||
bool first = true;
|
||||
|
||||
do {
|
||||
if (first) serial_shouldwait(gex->acm_fd, gex->ser_timeout);
|
||||
ssize_t len = read(gex->acm_fd, pollbuffer, TF_MAX_PAYLOAD_RX);
|
||||
if (first) {
|
||||
serial_noblock(gex->acm_fd);
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (len < 0) {
|
||||
fprintf(stderr, "ERROR %d in GEX Poll: %s\n", errno, strerror(errno));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (len == 0) {
|
||||
fprintf(stderr,"No more data to read.\n");
|
||||
break;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "rx %d bytes\n", (int) len);
|
||||
hexDump("TF_Receive", pollbuffer, (uint32_t) len);
|
||||
|
||||
TF_Accept(gex->tf, pollbuffer, (size_t) len);
|
||||
}
|
||||
}
|
||||
} while(1);
|
||||
}
|
||||
|
||||
/** Free the struct */
|
||||
|
||||
@@ -21,6 +21,7 @@ struct gex_client {
|
||||
const char *acm_device; //!< Comport device name, might be used to reconnect (?)
|
||||
int acm_fd; //!< Open comport file descriptor
|
||||
bool connected; //!< Flag that we're currently connected to the comport
|
||||
uint32_t ser_timeout; //!< Timeout for read()/write()
|
||||
|
||||
// synchronous query "hacks"
|
||||
bool squery_ok; //!< flag that the query response was received
|
||||
|
||||
+45
-33
@@ -24,33 +24,47 @@
|
||||
* @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,
|
||||
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)
|
||||
TF_Listener lst, void *userdata2,
|
||||
bool raw_pld)
|
||||
{
|
||||
assert(unit != NULL);
|
||||
assert(unit->gex != NULL);
|
||||
|
||||
fprintf(stderr, "raw pld? %d\n", raw_pld);
|
||||
|
||||
GexClient *gex = unit->gex;
|
||||
|
||||
uint8_t callsign = unit->callsign;
|
||||
uint8_t callsign = 0;
|
||||
uint8_t *pld = NULL;
|
||||
|
||||
if (!raw_pld) {
|
||||
callsign = unit->callsign;
|
||||
assert(callsign != 0);
|
||||
uint8_t *pld = malloc(len + 2);
|
||||
pld = malloc(len + 2);
|
||||
} else {
|
||||
pld = malloc(len);
|
||||
}
|
||||
|
||||
assert(pld != NULL);
|
||||
{
|
||||
if (!raw_pld) {
|
||||
// prefix the actual payload with the callsign and command bytes.
|
||||
// TODO provide TF API for sending the payload externally in smaller chunks? Will avoid the malloc here
|
||||
pld[0] = callsign;
|
||||
pld[1] = cmd;
|
||||
memcpy(pld + 2, payload, len);
|
||||
} else {
|
||||
memcpy(pld, payload, len);
|
||||
}
|
||||
|
||||
TF_Msg msg;
|
||||
TF_ClearMsg(&msg);
|
||||
msg.type = MSG_UNIT_REQUEST;
|
||||
msg.type = (raw_pld ? cmd : (uint8_t) MSG_UNIT_REQUEST);
|
||||
msg.data = pld;
|
||||
msg.len = (TF_LEN) (len + 2);
|
||||
msg.len = (TF_LEN) (len + (raw_pld?0:2));
|
||||
msg.userdata = unit;
|
||||
msg.userdata2 = userdata2;
|
||||
msg.frame_id = session;
|
||||
@@ -66,10 +80,7 @@ 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,
|
||||
0, false,
|
||||
NULL, NULL);
|
||||
GEX_LL_Query(unit, cmd, payload, len, 0, false, NULL, NULL, false);
|
||||
}
|
||||
|
||||
/** Send with no listener, don't wait for response */
|
||||
@@ -80,10 +91,7 @@ void GEX_SendEx(GexUnit *unit, uint8_t cmd,
|
||||
assert(unit != NULL);
|
||||
assert(unit->gex != NULL);
|
||||
|
||||
GEX_LL_Query(unit, cmd,
|
||||
payload, len,
|
||||
session, is_reply,
|
||||
NULL, NULL);
|
||||
GEX_LL_Query(unit, cmd, payload, len, session, is_reply, NULL, NULL, false);
|
||||
}
|
||||
|
||||
/** listener for the synchronous query functionality */
|
||||
@@ -92,6 +100,8 @@ static TF_Result sync_query_lst(TinyFrame *tf, TF_Msg *msg)
|
||||
GexClient *gex = tf->userdata;
|
||||
assert(gex != NULL);
|
||||
|
||||
fprintf(stderr, "sync query lst called <-\n");
|
||||
|
||||
// clone the message
|
||||
gex->squery_msg.len = msg->len;
|
||||
gex->squery_msg.unit = msg->userdata;
|
||||
@@ -109,7 +119,8 @@ static TF_Result sync_query_lst(TinyFrame *tf, TF_Msg *msg)
|
||||
/** Static query */
|
||||
static GexMsg GEX_QueryEx(GexUnit *unit, uint8_t cmd,
|
||||
const uint8_t *payload, uint32_t len,
|
||||
GexSession session, bool is_reply)
|
||||
GexSession session, bool is_reply,
|
||||
bool raw_pld)
|
||||
{
|
||||
assert(unit != NULL);
|
||||
assert(unit->gex != NULL);
|
||||
@@ -126,10 +137,7 @@ static GexMsg GEX_QueryEx(GexUnit *unit, uint8_t cmd,
|
||||
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, session, is_reply, sync_query_lst, NULL, raw_pld);
|
||||
|
||||
GEX_Poll(gex);
|
||||
|
||||
@@ -145,7 +153,7 @@ GexMsg GEX_Query(GexUnit *unit, uint8_t cmd, const uint8_t *payload, uint32_t le
|
||||
{
|
||||
assert(unit != NULL);
|
||||
assert(unit->gex != NULL);
|
||||
return GEX_QueryEx(unit, cmd, payload, len, 0, false);
|
||||
return GEX_QueryEx(unit, cmd, payload, len, 0, false, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -178,10 +186,7 @@ void GEX_QueryAsync(GexUnit *unit, uint8_t cmd, const uint8_t *payload, uint32_t
|
||||
assert(unit->gex != NULL);
|
||||
|
||||
// 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);
|
||||
GEX_LL_Query(unit, cmd, payload, len, 0, false, async_query_lst, lst, false);
|
||||
}
|
||||
|
||||
// ---------------------------- BULK ----------------------------
|
||||
@@ -196,6 +201,7 @@ void GEX_QueryAsync(GexUnit *unit, uint8_t cmd, const uint8_t *payload, uint32_t
|
||||
*/
|
||||
uint32_t GEX_BulkRead(GexUnit *unit, GexBulk *bulk)
|
||||
{
|
||||
fprintf(stderr, "Ask to read:\n");
|
||||
// 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);
|
||||
|
||||
@@ -220,14 +226,19 @@ uint32_t GEX_BulkRead(GexUnit *unit, GexBulk *bulk)
|
||||
fprintf(stderr, "Total is %d\n", total);
|
||||
|
||||
uint32_t at = 0;
|
||||
while (at < total) {
|
||||
while (at < total+1) { // +1 makes sure we read one past end and trigger the END OF DATA msg
|
||||
uint8_t buf[10];
|
||||
PayloadBuilder pb = pb_start(buf, 10, NULL);
|
||||
pb_u32(&pb, 512);
|
||||
pb_u32(&pb, 128); // This selects the chunk size.
|
||||
// FIXME Something is wrong in the poll function, or the transport in general,
|
||||
// because chunks larger than e.g. 130 bytes seem to get stuck half-transferred.
|
||||
// This isn't an issue for events and regular sending, only query responses like here.
|
||||
// This may also cause problems when receiving events while reading a bulk.
|
||||
// It may be best to get rid of the comport and use libusb, after all.
|
||||
|
||||
GexMsg resp = GEX_QueryEx(unit, MSG_BULK_READ_POLL,
|
||||
buf, (uint32_t) pb_length(&pb),
|
||||
resp0.session, true);
|
||||
fprintf(stderr, "Poll read:\n");
|
||||
GexMsg resp = GEX_QueryEx(unit, MSG_BULK_READ_POLL, buf,
|
||||
(uint32_t) pb_length(&pb), resp0.session, true, true);
|
||||
|
||||
if (resp.type == MSG_ERROR) {
|
||||
fprintf(stderr, "Bulk read failed! %.*s\n", resp.len, (char *) resp.payload);
|
||||
@@ -236,6 +247,7 @@ uint32_t GEX_BulkRead(GexUnit *unit, GexBulk *bulk)
|
||||
|
||||
if (resp.type == MSG_BULK_END) {
|
||||
// No more data
|
||||
fprintf(stderr, "Bulk read OK, closed.\n");
|
||||
return at;
|
||||
}
|
||||
|
||||
@@ -289,9 +301,9 @@ bool GEX_BulkWrite(GexUnit *unit, GexBulk *bulk)
|
||||
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);
|
||||
fprintf(stderr, "Query at %d, len %d\n", (int)at, (int)chunk);
|
||||
GexMsg resp = GEX_QueryEx(unit, MSG_BULK_DATA, bulk->buffer + at, chunk,
|
||||
resp0.session, true, true);
|
||||
at += chunk;
|
||||
|
||||
if (resp.type == MSG_ERROR) {
|
||||
|
||||
+34
-18
@@ -28,7 +28,7 @@ static int set_interface_attribs(int fd, int speed, int parity)
|
||||
// no canonical processing
|
||||
tty.c_oflag = 0; // no remapping, no delays
|
||||
tty.c_cc[VMIN] = 0; // read doesn't block
|
||||
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
|
||||
tty.c_cc[VTIME] = 2; // 0.2 seconds read timeout
|
||||
|
||||
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
|
||||
|
||||
@@ -46,23 +46,8 @@ static int set_interface_attribs(int fd, int speed, int parity)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_blocking(int fd, bool should_block, int read_timeout_0s1)
|
||||
{
|
||||
struct termios tty;
|
||||
memset(&tty, 0, sizeof tty);
|
||||
if (tcgetattr(fd, &tty) != 0) {
|
||||
fprintf(stderr, "error %d from tggetattr\n", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
tty.c_cc[VMIN] = (cc_t) (should_block ? 1 : 0);
|
||||
tty.c_cc[VTIME] = (cc_t) read_timeout_0s1;
|
||||
|
||||
if (tcsetattr(fd, TCSANOW, &tty) != 0)
|
||||
fprintf(stderr, "error %d setting term attributes\n", errno);
|
||||
}
|
||||
|
||||
int serial_open(const char *device, bool blocking, int timeout_0s1)
|
||||
int serial_open(const char *device)
|
||||
{
|
||||
int fd = open (device, O_RDWR | O_NOCTTY | O_SYNC);
|
||||
if (fd < 0) {
|
||||
@@ -71,7 +56,38 @@ int serial_open(const char *device, bool blocking, int timeout_0s1)
|
||||
}
|
||||
|
||||
set_interface_attribs(fd, B115200, 0);
|
||||
set_blocking (fd, blocking, timeout_0s1);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
void serial_noblock(int fd)
|
||||
{
|
||||
struct termios tty;
|
||||
memset(&tty, 0, sizeof tty);
|
||||
if (tcgetattr(fd, &tty) != 0) {
|
||||
fprintf(stderr, "error %d from tggetattr\n", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
tty.c_cc[VMIN] = (cc_t) 0;
|
||||
tty.c_cc[VTIME] = (cc_t) 0;
|
||||
|
||||
if (tcsetattr(fd, TCSANOW, &tty) != 0)
|
||||
fprintf(stderr, "error %d setting term attributes\n", errno);
|
||||
}
|
||||
|
||||
void serial_shouldwait(int fd, int ms)
|
||||
{
|
||||
struct termios tty;
|
||||
memset(&tty, 0, sizeof tty);
|
||||
if (tcgetattr(fd, &tty) != 0) {
|
||||
fprintf(stderr, "error %d from tggetattr\n", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
tty.c_cc[VMIN] = (cc_t) 0;
|
||||
tty.c_cc[VTIME] = (cc_t) (ms+50/100);
|
||||
|
||||
if (tcsetattr(fd, TCSANOW, &tty) != 0)
|
||||
fprintf(stderr, "error %d setting term attributes\n", errno);
|
||||
}
|
||||
|
||||
+5
-3
@@ -8,10 +8,12 @@
|
||||
* Open a serial port
|
||||
*
|
||||
* @param device - device to open, e.g. /dev/ttyACM0
|
||||
* @param blocking - true for `read()` to block until at least 1 byte is read
|
||||
* @param timeout_0s1 - timeout for `read()` - if blocking, starts after the first character
|
||||
* @return file descriptor
|
||||
*/
|
||||
int serial_open(const char *device, bool blocking, int timeout_0s1);
|
||||
int serial_open(const char *device);
|
||||
|
||||
void serial_shouldwait(int fd, int ms);
|
||||
|
||||
void serial_noblock(int fd);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -34,7 +34,7 @@ int main(void)
|
||||
// Bind ^C handler for safe shutdown
|
||||
signal(SIGINT, sigintHandler);
|
||||
|
||||
gex = GEX_Init("/dev/ttyACM0", 200);
|
||||
gex = GEX_Init("/dev/ttyACM0", 100);
|
||||
if (!gex) exit(1);
|
||||
|
||||
TF_AddGenericListener(GEX_GetTF(gex), hdl_default);
|
||||
@@ -67,7 +67,7 @@ int main(void)
|
||||
};
|
||||
uint32_t actuallyRead = GEX_BulkRead(test, &br);
|
||||
fprintf(stderr, "Read %d bytes:\n", actuallyRead);
|
||||
fprintf(stderr, "%*.s", actuallyRead, buffr);
|
||||
fprintf(stderr, "%.*s", actuallyRead, buffr);
|
||||
|
||||
fprintf(stderr, "ALL OK, ending.\n");
|
||||
GEX_DeInit(gex);
|
||||
|
||||
Reference in New Issue
Block a user