From 0f711495dfb6d6507b30872dfc6e8f1ea67f22c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Fri, 22 Dec 2017 00:41:09 +0100 Subject: [PATCH] =?UTF-8?q?bulk=20kinda=20works=20(reading=20oly=20so=20fa?= =?UTF-8?q?r)=C3=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gex/TF_Integration.c | 3 ++ gex/gex_client.c | 37 ++++++++++++++---- gex/gex_internal.h | 1 + gex/gex_unit.c | 90 +++++++++++++++++++++++++------------------- gex/serial/serial.c | 42 ++++++++++++++------- gex/serial/serial.h | 8 ++-- main.c | 4 +- 7 files changed, 121 insertions(+), 64 deletions(-) diff --git a/gex/TF_Integration.c b/gex/TF_Integration.c index 97ff6e3..c5d7061 100644 --- a/gex/TF_Integration.c +++ b/gex/TF_Integration.c @@ -5,6 +5,7 @@ #include #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)); diff --git a/gex/gex_client.c b/gex/gex_client.c index 9500533..ccd5a6f 100644 --- a/gex/gex_client.c +++ b/gex/gex_client.c @@ -8,6 +8,7 @@ #include #include #include +#include #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? - if (len < 0) { - fprintf(stderr, "ERROR %d in GEX Poll: %s\n", errno, strerror(errno)); - } else { - TF_Accept(gex->tf, pollbuffer, (size_t) len); - } + 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)); + 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 */ diff --git a/gex/gex_internal.h b/gex/gex_internal.h index 0163429..ec7d2b2 100644 --- a/gex/gex_internal.h +++ b/gex/gex_internal.h @@ -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 diff --git a/gex/gex_unit.c b/gex/gex_unit.c index b943af7..99bcccf 100644 --- a/gex/gex_unit.c +++ b/gex/gex_unit.c @@ -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; - assert(callsign != 0); - uint8_t *pld = malloc(len + 2); + uint8_t callsign = 0; + uint8_t *pld = NULL; + + if (!raw_pld) { + callsign = unit->callsign; + assert(callsign != 0); + pld = malloc(len + 2); + } else { + pld = malloc(len); + } + assert(pld != NULL); { - // 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); + 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) { diff --git a/gex/serial/serial.c b/gex/serial/serial.c index d53d5fa..b5d102f 100644 --- a/gex/serial/serial.c +++ b/gex/serial/serial.c @@ -27,8 +27,8 @@ static int set_interface_attribs(int fd, int speed, int parity) tty.c_lflag = 0; // no signaling chars, no echo, // 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[VMIN] = 0; // read doesn't block + tty.c_cc[VTIME] = 2; // 0.2 seconds read timeout tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl @@ -46,7 +46,21 @@ 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) + +int serial_open(const char *device) +{ + int fd = open (device, O_RDWR | O_NOCTTY | O_SYNC); + if (fd < 0) { + fprintf (stderr, "FAILED TO OPEN SERIAL! Error %d opening %s: %s\n", errno, device, strerror (errno)); + return -1; + } + + set_interface_attribs(fd, B115200, 0); + + return fd; +} + +void serial_noblock(int fd) { struct termios tty; memset(&tty, 0, sizeof tty); @@ -55,23 +69,25 @@ static void set_blocking(int fd, bool should_block, int read_timeout_0s1) return; } - tty.c_cc[VMIN] = (cc_t) (should_block ? 1 : 0); - tty.c_cc[VTIME] = (cc_t) read_timeout_0s1; + 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); } -int serial_open(const char *device, bool blocking, int timeout_0s1) +void serial_shouldwait(int fd, int ms) { - int fd = open (device, O_RDWR | O_NOCTTY | O_SYNC); - if (fd < 0) { - fprintf (stderr, "FAILED TO OPEN SERIAL! Error %d opening %s: %s\n", errno, device, strerror (errno)); - return -1; + struct termios tty; + memset(&tty, 0, sizeof tty); + if (tcgetattr(fd, &tty) != 0) { + fprintf(stderr, "error %d from tggetattr\n", errno); + return; } - set_interface_attribs(fd, B115200, 0); - set_blocking (fd, blocking, timeout_0s1); + tty.c_cc[VMIN] = (cc_t) 0; + tty.c_cc[VTIME] = (cc_t) (ms+50/100); - return fd; + if (tcsetattr(fd, TCSANOW, &tty) != 0) + fprintf(stderr, "error %d setting term attributes\n", errno); } diff --git a/gex/serial/serial.h b/gex/serial/serial.h index 992c58a..eeccda5 100644 --- a/gex/serial/serial.h +++ b/gex/serial/serial.h @@ -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 diff --git a/main.c b/main.c index db25ca8..1253d6b 100644 --- a/main.c +++ b/main.c @@ -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);