From 32dbdc54bb42132842284417d535146d26591e5d Mon Sep 17 00:00:00 2001 From: joseph calderon Date: Sun, 30 Aug 2026 10:26:33 -0700 Subject: [PATCH 1/5] hostmot2: fix PktUART queued-read counts --- docs/src/man/man3/hm2_pktuart.3.adoc | 2 ++ src/hal/drivers/mesa-hostmot2/pktuart.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/src/man/man3/hm2_pktuart.3.adoc b/docs/src/man/man3/hm2_pktuart.3.adoc index 802e5ae4660..4e601950ca1 100644 --- a/docs/src/man/man3/hm2_pktuart.3.adoc +++ b/docs/src/man/man3/hm2_pktuart.3.adoc @@ -150,11 +150,13 @@ This is typically a multi-step process: 1. rxstatus indicates that there are packets of data, but at this point we need to know how big each packet is (and reading two much or two little data from the FIFOs will cause problems). 2. Queue a read of the frame sizes. **hm2_pktuart_queue_get_frame_sizes(name, fsizes[])** On return, the fsizes[] array will have been loaded with the frame sizes (size in bytes). +The function returns the number of frame-size reads queued, or a negative error code. If fsizes are [8] [7] [6] and you only read 1 frame from the data FIFO then on the next call to get_frame_sizes the returned array would be [7] [6]. 3. Wait one thread cycle to get the data. Note that there is no serial latency here, the data is already on the FPGA but we can only know how much data to request once we know the packet size 4. Queue enough data reads to get all the data frames that the packet is spread over. int hm2_pktuart_queue_read_data(name, data, bytes) On return the data[] array will have been loaded with enough 32-bit frames to include "bytes" bytes. +The function returns the number of 32-bit data reads queued, or a negative error code. 5. Parse the data. === Data Formats === diff --git a/src/hal/drivers/mesa-hostmot2/pktuart.c b/src/hal/drivers/mesa-hostmot2/pktuart.c index 4560ba18059..36747a86440 100644 --- a/src/hal/drivers/mesa-hostmot2/pktuart.c +++ b/src/hal/drivers/mesa-hostmot2/pktuart.c @@ -871,7 +871,7 @@ int hm2_pktuart_queue_get_frame_sizes(const char *name, rtapi_u32 fsizes[]) HM2_ERR("Unable to queue Rx FIFO count read %d of %d (error %d))\n", j, nfs, r); } } - return j - 1; + return j; } EXPORT_SYMBOL_GPL(hm2_pktuart_queue_get_frame_sizes); @@ -915,7 +915,7 @@ int hm2_pktuart_queue_read_data(const char *name, rtapi_u32 data[], int bytes) HM2_ERR("Unable to queue Rx FIFO read %d of %d (error %d)\n", i, nrx, r); } } - return i - 1; + return i; } EXPORT_SYMBOL_GPL(hm2_pktuart_queue_read_data); From 661c202bb67dd8725a560aabb51e1c0a74564514 Mon Sep 17 00:00:00 2001 From: joseph calderon Date: Fri, 9 Oct 2026 18:17:21 -0700 Subject: [PATCH 2/5] hostmot2: fix PktUART byte packing and diagnostics --- src/hal/drivers/mesa-hostmot2/pktuart.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/hal/drivers/mesa-hostmot2/pktuart.c b/src/hal/drivers/mesa-hostmot2/pktuart.c index 36747a86440..bc5828ab721 100644 --- a/src/hal/drivers/mesa-hostmot2/pktuart.c +++ b/src/hal/drivers/mesa-hostmot2/pktuart.c @@ -129,7 +129,8 @@ int hm2_pktuart_parse_md(hostmot2_t *hm2, int md_index) // For the time being we assume that all PktUARTS come on pairs if(inst->clock_freq == 0) { inst->clock_freq = md->clock_freq; - r = rtapi_snprintf(inst->name, sizeof(inst->name), "%s.pktuart.%01d", hm2->llio->name, i); + rtapi_snprintf(inst->name, sizeof(inst->name), + "%s.pktuart.%01d", hm2->llio->name, i); HM2_PRINT("created PktUART Interface function %s.\n", inst->name); } if(md->gtag == HM2_GTAG_PKTUART_TX) { @@ -613,7 +614,10 @@ int hm2_pktuart_send(const char *name, const unsigned char data[], rtapi_u8 *num for(unsigned i = 0; i < nframes; i++) { count += frame_sizes[i]; while(c < count - 3) { - rtapi_u32 buff = data[c] + (data[c+1] << 8) + (data[c+2] << 16) + (data[c+3] << 24); + rtapi_u32 buff = ((rtapi_u32)data[c] + | ((rtapi_u32)data[c+1] << 8) + | ((rtapi_u32)data[c+2] << 16) + | ((rtapi_u32)data[c+3] << 24)); r = hm2->llio->queue_write(hm2->llio, hm2->pktuart.instance[inst].tx_addr, &buff, sizeof(buff)); if(r < 0) { HM2_ERR("%s send: hm2->llio->queue_write failure\n", name); @@ -627,8 +631,15 @@ int hm2_pktuart_send(const char *name, const unsigned char data[], rtapi_u8 *num rtapi_u32 buff; switch(count - c) { case 1: buff = data[c]; break; - case 2: buff = (data[c] + (data[c+1] << 8)); break; - case 3: buff = (data[c] + (data[c+1] << 8) + (data[c+2] << 16)); break; + case 2: + buff = ((rtapi_u32)data[c] + | ((rtapi_u32)data[c+1] << 8)); + break; + case 3: + buff = ((rtapi_u32)data[c] + | ((rtapi_u32)data[c+1] << 8) + | ((rtapi_u32)data[c+2] << 16)); + break; default: HM2_ERR("%s send error in buffer parsing: count = %i, i = %i\n", name, count, c); return -1; @@ -806,8 +817,8 @@ int hm2_pktuart_read(const char *name, unsigned char data[], rtapi_u8 *num_frame if(countb - c) { r = hm2->llio->read(hm2->llio, hm2->pktuart.instance[inst].rx_addr, &buff, sizeof(buff)); if(r < 0) { - HM2_ERR("%s read: hm2->llio->queue_write failure\n", name); - return -1; + HM2_ERR("%s read: hm2->llio->read failure\n", name); + return r; } switch(countb - c) { case 1: From 733841090d27f28ddf0c7510c358c937f0c00f7d Mon Sep 17 00:00:00 2001 From: joseph calderon Date: Fri, 9 Oct 2026 18:42:50 -0700 Subject: [PATCH 3/5] hostmot2: propagate PktUART LLIO failures and test error paths --- docs/src/man/man3/hm2_pktuart.3.adoc | 2 + src/hal/drivers/mesa-hostmot2/pktuart.c | 98 ++++++------ tests/hostmot2/pktuart/README | 21 +++ tests/hostmot2/pktuart/expected | 2 + tests/hostmot2/pktuart/test.c | 189 ++++++++++++++++++++++++ tests/hostmot2/pktuart/test.sh | 15 ++ 6 files changed, 281 insertions(+), 46 deletions(-) create mode 100644 tests/hostmot2/pktuart/README create mode 100644 tests/hostmot2/pktuart/expected create mode 100644 tests/hostmot2/pktuart/test.c create mode 100644 tests/hostmot2/pktuart/test.sh diff --git a/docs/src/man/man3/hm2_pktuart.3.adoc b/docs/src/man/man3/hm2_pktuart.3.adoc index 4e601950ca1..dea310ee7ab 100644 --- a/docs/src/man/man3/hm2_pktuart.3.adoc +++ b/docs/src/man/man3/hm2_pktuart.3.adoc @@ -157,6 +157,8 @@ If fsizes are [8] [7] [6] and you only read 1 frame from the data FIFO then on t int hm2_pktuart_queue_read_data(name, data, bytes) On return the data[] array will have been loaded with enough 32-bit frames to include "bytes" bytes. The function returns the number of 32-bit data reads queued, or a negative error code. +Both queue helpers stop at the first queue failure; earlier reads may already +be queued. A successful return does not confirm completion of hardware I/O. 5. Parse the data. === Data Formats === diff --git a/src/hal/drivers/mesa-hostmot2/pktuart.c b/src/hal/drivers/mesa-hostmot2/pktuart.c index bc5828ab721..c216c3b424a 100644 --- a/src/hal/drivers/mesa-hostmot2/pktuart.c +++ b/src/hal/drivers/mesa-hostmot2/pktuart.c @@ -301,24 +301,24 @@ static int config_tx(const char *name, const hostmot2_t* hm2, hm2_pktuart_instan } if((cfg->flags & HM2_PKTUART_CONFIG_FORCECONFIG) || bitrate != inst->tx_bitrate) { - inst->tx_bitrate = bitrate; - if((r = writefn(hm2->llio, inst->tx_bitrate_addr, &bitrate, sizeof(bitrate))) < 0) { + if((r = writefn(hm2->llio, inst->tx_bitrate_addr, &bitrate, sizeof(bitrate))) <= 0) { HM2_ERR("Configure TX baudrate: hm2->llio->%s failure %s (error %d)\n", writenm, name, r); - return r; + return r < 0 ? r : -EIO; } + inst->tx_bitrate = bitrate; } if((cfg->flags & HM2_PKTUART_CONFIG_FORCECONFIG) || mode != inst->tx_mode) { - inst->tx_mode = mode; - if((r = writefn(hm2->llio, inst->tx_mode_addr, &mode, sizeof(mode))) < 0) { + if((r = writefn(hm2->llio, inst->tx_mode_addr, &mode, sizeof(mode))) <= 0) { HM2_ERR("Configure TX mode: hm2->llio->%s failure %s (error %d)\n", writenm, name, r); - return r; + return r < 0 ? r : -EIO; } + inst->tx_mode = mode; } if(cfg->flags & HM2_PKTUART_CONFIG_FLUSH) { rtapi_u32 buff = HM2_PKTUART_CLEAR; // clear data FIFO and count register - if((r = writefn(hm2->llio, inst->tx_mode_addr, &buff, sizeof(buff))) < 0) { + if((r = writefn(hm2->llio, inst->tx_mode_addr, &buff, sizeof(buff))) <= 0) { HM2_ERR("Configure TX flush: hm2->llio->%s failure %s (error %d)\n", writenm, name, r); - return r; + return r < 0 ? r : -EIO; } } return 0; @@ -381,24 +381,24 @@ static int config_rx(const char *name, const hostmot2_t *hm2, hm2_pktuart_instan } if((cfg->flags & HM2_PKTUART_CONFIG_FORCECONFIG) || bitrate != inst->rx_bitrate) { - inst->rx_bitrate = bitrate; - if((r = writefn(hm2->llio, inst->rx_bitrate_addr, &bitrate, sizeof(bitrate))) < 0) { + if((r = writefn(hm2->llio, inst->rx_bitrate_addr, &bitrate, sizeof(bitrate))) <= 0) { HM2_ERR("Configure RX baudrate: hm2->llio->%s failure %s (error %d)\n", writenm, name, r); - return r; + return r < 0 ? r : -EIO; } + inst->rx_bitrate = bitrate; } if((cfg->flags & HM2_PKTUART_CONFIG_FORCECONFIG) || mode != inst->rx_mode) { - inst->rx_mode = mode; - if((r = writefn(hm2->llio, inst->rx_mode_addr, &mode, sizeof(mode))) < 0) { + if((r = writefn(hm2->llio, inst->rx_mode_addr, &mode, sizeof(mode))) <= 0) { HM2_ERR("Configure RX mode: hm2->llio->%s failure %s (error %d)\n", writenm, name, r); - return r; + return r < 0 ? r : -EIO; } + inst->rx_mode = mode; } if(cfg->flags & HM2_PKTUART_CONFIG_FLUSH) { rtapi_u32 buff = HM2_PKTUART_CLEAR; // clear data FIFO and count register - if((r = writefn(hm2->llio, inst->rx_mode_addr, &buff, sizeof(buff))) < 0) { + if((r = writefn(hm2->llio, inst->rx_mode_addr, &buff, sizeof(buff))) <= 0) { HM2_ERR("Configure RX flush: hm2->llio->%s failure %s (error %d)\n", writenm, name, r); - return r; + return r < 0 ? r : -EIO; } } return 0; @@ -457,10 +457,10 @@ static void perform_reset(const char *name, int queue) } // clear sends, data FIFO and count register - if((i = wr(hm2->llio, inst->tx_mode_addr, &buff, sizeof(rtapi_u32))) < 0) + if((i = wr(hm2->llio, inst->tx_mode_addr, &buff, sizeof(rtapi_u32))) <= 0) HM2_ERR("Failed to %s reset of TX data and FIFO (error %d)\n", msg, i); // clear receives, data FIFO and count register - if((i = wr(hm2->llio, inst->rx_mode_addr, &buff, sizeof(rtapi_u32))) < 0) + if((i = wr(hm2->llio, inst->rx_mode_addr, &buff, sizeof(rtapi_u32))) <= 0) HM2_ERR("Failed to %s reset of RX data and FIFO (error %d)\n", msg, i); } @@ -516,11 +516,11 @@ int hm2_pktuart_setup(const char *name, unsigned bitrate, rtapi_s32 tx_mode, rta buff = (rtapi_u32)((bitrate * 1048576.0)/inst->clock_freq); // 20 bits in v0 & v1 } if(buff != inst->tx_bitrate) { - inst->tx_bitrate = buff; - if((r = hm2->llio->write(hm2->llio, inst->tx_bitrate_addr, &buff, sizeof(rtapi_u32))) < 0) { + if((r = hm2->llio->write(hm2->llio, inst->tx_bitrate_addr, &buff, sizeof(rtapi_u32))) <= 0) { HM2_ERR("PktUART setup: hm2->llio->write failure %s on tx bitrate (error %d)\n", name, r); - return r; + return r < 0 ? r : -EIO; } + inst->tx_bitrate = buff; } if(hm2->pktuart.rx_version >= 2) { buff = (rtapi_u32)((bitrate * 16777216.0)/inst->clock_freq); // 24 bits in v2+ @@ -528,43 +528,43 @@ int hm2_pktuart_setup(const char *name, unsigned bitrate, rtapi_s32 tx_mode, rta buff = (rtapi_u32)((bitrate * 1048576.0)/inst->clock_freq); // 20 bits in v0 & v1 } if(buff != inst->rx_bitrate) { - inst->rx_bitrate = buff; - if((r = hm2->llio->write(hm2->llio, inst->rx_bitrate_addr, &buff, sizeof(rtapi_u32))) < 0) { + if((r = hm2->llio->write(hm2->llio, inst->rx_bitrate_addr, &buff, sizeof(rtapi_u32))) <= 0) { HM2_ERR("PktUART setup: hm2->llio->write failure %s on rx bitrate (error %d)\n", name, r); - return r; + return r < 0 ? r : -EIO; } + inst->rx_bitrate = buff; } } if(tx_mode >= 0) { buff = ((rtapi_u32)tx_mode) & HM2_PKTUART_TXMODE_MASK; - if((r = hm2->llio->write(hm2->llio, inst->tx_mode_addr, &buff, sizeof(rtapi_u32))) < 0) { + if((r = hm2->llio->write(hm2->llio, inst->tx_mode_addr, &buff, sizeof(rtapi_u32))) <= 0) { HM2_ERR("PktUART setup: hm2->llio->write failure %s on tx_mode (error %d)\n", name, r); - return r; + return r < 0 ? r : -EIO; } } if(rx_mode >= 0) { buff = ((rtapi_u32)rx_mode) & HM2_PKTUART_RXMODE_MASK; - if((r = hm2->llio->write(hm2->llio, inst->rx_mode_addr, &buff, sizeof(rtapi_u32))) < 0) { + if((r = hm2->llio->write(hm2->llio, inst->rx_mode_addr, &buff, sizeof(rtapi_u32))) <= 0) { HM2_ERR("PktUART setup: hm2->llio->write failure %s on rx_mode (error %d)\n", name, r); - return r; + return r < 0 ? r : -EIO; } } buff = HM2_PKTUART_CLEAR; if(txclear == 1) { // clear sends, data FIFO and count register - if((r = hm2->llio->write(hm2->llio, inst->tx_mode_addr, &buff, sizeof(rtapi_u32))) < 0) { + if((r = hm2->llio->write(hm2->llio, inst->tx_mode_addr, &buff, sizeof(rtapi_u32))) <= 0) { HM2_ERR("PktUART setup: hm2->llio->write failure %s on tx clear (error %d)\n", name, r); - return r; + return r < 0 ? r : -EIO; } } if(rxclear == 1) { // clear receives, data FIFO and count register - if((r = hm2->llio->write(hm2->llio, inst->rx_mode_addr, &buff, sizeof(rtapi_u32))) < 0 ) { + if((r = hm2->llio->write(hm2->llio, inst->rx_mode_addr, &buff, sizeof(rtapi_u32))) <= 0) { HM2_ERR("PktUART setup: hm2->llio->write failure %s on rx clear (error %d)\n", name, r); - return r; + return r < 0 ? r : -EIO; } } @@ -619,9 +619,9 @@ int hm2_pktuart_send(const char *name, const unsigned char data[], rtapi_u8 *num | ((rtapi_u32)data[c+2] << 16) | ((rtapi_u32)data[c+3] << 24)); r = hm2->llio->queue_write(hm2->llio, hm2->pktuart.instance[inst].tx_addr, &buff, sizeof(buff)); - if(r < 0) { + if(r <= 0) { HM2_ERR("%s send: hm2->llio->queue_write failure\n", name); - return r; + return r < 0 ? r : -EIO; } c += 4; } @@ -645,9 +645,9 @@ int hm2_pktuart_send(const char *name, const unsigned char data[], rtapi_u8 *num return -1; } // end switch r = hm2->llio->queue_write(hm2->llio, hm2->pktuart.instance[inst].tx_addr, &buff, sizeof(buff)); - if(r < 0) { + if(r <= 0) { HM2_ERR("%s send: hm2->llio->queue_write failure\n", name); - return r; + return r < 0 ? r : -EIO; } } (*num_frames)++; @@ -663,9 +663,9 @@ int hm2_pktuart_send(const char *name, const unsigned char data[], rtapi_u8 *num // Not possible to check for errors on the fly when using queued // transfers. The data does not arrive immediately. Therefore, no // checks can be done here. - if(r < 0) { + if(r <= 0) { HM2_ERR("%s send: hm2->llio->queue_write failure\n", name); - return r; + return r < 0 ? r : -EIO; } } return count; @@ -709,9 +709,9 @@ int hm2_pktuart_read(const char *name, unsigned char data[], rtapi_u8 *num_frame // First poll the mode register for a non zero frames received count // (mode register bits 20..16) r = hm2->llio->read(hm2->llio, hm2->pktuart.instance[inst].rx_mode_addr, &buff, sizeof(rtapi_u32)); - if(r < 0) { + if(r <= 0) { HM2_ERR("%s read: hm2->llio->read failure\n", name); - return r; + return r < 0 ? r : -EIO; } if(buff & (0x1 << 21)) { countp = (buff >> 16) & 0x1f; @@ -764,6 +764,10 @@ int hm2_pktuart_read(const char *name, unsigned char data[], rtapi_u8 *num_frame of received packets. Since it is a FIFO it must only be read once after it has be determined that there are packets available to read. */ r = hm2->llio->read(hm2->llio, hm2->pktuart.instance[inst].rx_fifo_count_addr, &buff, sizeof(buff)); + if(r <= 0) { + HM2_ERR("%s read: hm2->llio->read failure\n", name); + return r < 0 ? r : -EIO; + } countb = buff & 0x3ff; // PktUARTr receive count register Bits 9..0 : bytes in receive packet @@ -802,9 +806,9 @@ int hm2_pktuart_read(const char *name, unsigned char data[], rtapi_u8 *num_frame while(c < countb - 3) { r = hm2->llio->read(hm2->llio, hm2->pktuart.instance[inst].rx_addr, &buff, sizeof(buff)); - if(r < 0) { + if(r <= 0) { HM2_ERR("%s read: hm2->llio->read failure\n", name); - return r; + return r < 0 ? r : -EIO; } data[bytes_total+c+0] = (buff & 0x000000FF); // i*frame_sizes[i] @@ -816,9 +820,9 @@ int hm2_pktuart_read(const char *name, unsigned char data[], rtapi_u8 *num_frame if(countb - c) { r = hm2->llio->read(hm2->llio, hm2->pktuart.instance[inst].rx_addr, &buff, sizeof(buff)); - if(r < 0) { + if(r <= 0) { HM2_ERR("%s read: hm2->llio->read failure\n", name); - return r; + return r < 0 ? r : -EIO; } switch(countb - c) { case 1: @@ -878,8 +882,9 @@ int hm2_pktuart_queue_get_frame_sizes(const char *name, rtapi_u32 fsizes[]) for(j = 0; j < nfs; j++ ) { r = hm2->llio->queue_read(hm2->llio, hm2->pktuart.instance[inst].rx_fifo_count_addr, &fsizes[j], sizeof(rtapi_u32)); - if(r < 0) { + if(r <= 0) { HM2_ERR("Unable to queue Rx FIFO count read %d of %d (error %d))\n", j, nfs, r); + return r < 0 ? r : -EIO; } } return j; @@ -922,8 +927,9 @@ int hm2_pktuart_queue_read_data(const char *name, rtapi_u32 data[], int bytes) for(i = 0; i < nrx; i++ ) { r = hm2->llio->queue_read(hm2->llio, hm2->pktuart.instance[inst].rx_addr, &data[i], sizeof(rtapi_u32)); - if(r < 0) { + if(r <= 0) { HM2_ERR("Unable to queue Rx FIFO read %d of %d (error %d)\n", i, nrx, r); + return r < 0 ? r : -EIO; } } return i; diff --git a/tests/hostmot2/pktuart/README b/tests/hostmot2/pktuart/README new file mode 100644 index 00000000000..f710710ea5f --- /dev/null +++ b/tests/hostmot2/pktuart/README @@ -0,0 +1,21 @@ +HostMot2 PktUART software regression tests +======================================== + +Compiles the production driver with mock LLIO. No hardware is accessed. +Run after configuring/building a run-in-place tree: + + scripts/rip-environment runtests tests/hostmot2/pktuart + SANITIZERS=address,undefined bash tests/hostmot2/pktuart/test.sh + +CC can select another C compiler. Sanitizers are optional. +Checks cover queue counts, high-bit byte packing, missing/unconfigured +instances, zero and negative LLIO failures at each transfer stage, failed +configuration retries, legacy setup, and reset diagnostics. + +A successful queue operation is not confirmation that hardware received it. +On a partial queue failure, earlier operations may remain queued; callers +must handle the error and recover, not blindly resend the entire request. + +Hardware validation remains pending: record commit hash, board/firmware, +configuration, and results for hm2_modbus traffic, multi-frame transfers, +empty/full FIFO behavior, configuration changes and connection recovery. diff --git a/tests/hostmot2/pktuart/expected b/tests/hostmot2/pktuart/expected new file mode 100644 index 00000000000..656aa14c310 --- /dev/null +++ b/tests/hostmot2/pktuart/expected @@ -0,0 +1,2 @@ +PASS: 17 frame counts, 4097 byte counts, 4 error paths, 2048 packing cases +PASS: zero/negative LLIO failures at every read/write stage, config retries and reset diagnostics diff --git a/tests/hostmot2/pktuart/test.c b/tests/hostmot2/pktuart/test.c new file mode 100644 index 00000000000..636aafcc778 --- /dev/null +++ b/tests/hostmot2/pktuart/test.c @@ -0,0 +1,189 @@ +#include +#include +#include +#include "hostmot2.h" +#include "hostmot2-serial.h" + +static hostmot2_t board; +static hm2_lowlevel_io_t llio; +static hm2_pktuart_instance_t instance; +static rtapi_u32 status; +static unsigned reads, writes; +static rtapi_u32 words[64], addresses[64]; +static int lookup_result; +static unsigned fail_at, calls, errors; +static int failure; +static rtapi_u32 read_size; + +int hm2_get_pktuart(hostmot2_t **hm2, const char *name) { + (void)name; + *hm2 = &board; + return lookup_result; +} + +void rtapi_print_msg(msg_level_t level, const char *fmt, ...) { + if (level == RTAPI_MSG_ERR) errors++; + (void)fmt; +} + +static int queue_read(hm2_lowlevel_io_t *io, rtapi_u32 addr, void *data, int size) { + (void)io; + (void)addr; + (void)data; + assert(size == 4); + reads++; + calls++; + if (fail_at && calls == fail_at) return failure; + rtapi_u32 value = addr == instance.rx_mode_addr ? ((1u << 21) | (1u << 16)) : + addr == instance.rx_fifo_count_addr ? read_size : 0x12345678; + memcpy(data, &value, 4); + return 1; +} + +static int queue_write(hm2_lowlevel_io_t *io, rtapi_u32 addr, const void *data, int size) { + (void)io; + assert(size == 4 && writes < 64); + calls++; + if (fail_at && calls == fail_at) return failure; + addresses[writes] = addr; + memcpy(&words[writes++], data, 4); + return 1; +} + + +static void test_failures(void) { + llio.read = llio.queue_read = queue_read; + llio.write = llio.queue_write = queue_write; + instance.clock_freq = 50000000; + instance.rx_mode_addr = 0x2000; + instance.rx_fifo_count_addr = 0x2100; + instance.rx_addr = 0x2200; + instance.rx_bitrate_addr = 0x2300; + instance.tx_mode_addr = 0x2400; + instance.tx_bitrate_addr = 0x2500; + unsigned char data[8] = {0}; + rtapi_u32 buffer[4]; + hm2_pktuart_config_t cfg = { .baudrate = 115200, + .flags = HM2_PKTUART_CONFIG_FLUSH | HM2_PKTUART_CONFIG_RXEN | HM2_PKTUART_CONFIG_DRIVEEN }; + for (unsigned convention = 0; convention < 2; convention++) { + failure = convention ? -EFAULT : 0; + int expected = convention ? -EFAULT : -EIO; + instance.tx_bitrate = instance.rx_bitrate = 115200; + status = 2u << 16; + for (fail_at = 1; fail_at <= 2; fail_at++) { + calls = 0; + assert(hm2_pktuart_queue_get_frame_sizes("test", buffer) == expected); + assert(calls == fail_at); + calls = 0; + assert(hm2_pktuart_queue_read_data("test", buffer, 8) == expected); + assert(calls == fail_at); + } + for (rtapi_u16 size = 1; size <= 8; size++) { + unsigned operations = (size + 3) / 4 + 1; + for (fail_at = 1; fail_at <= operations; fail_at++) { + rtapi_u8 frames = 1; + calls = writes = 0; + assert(hm2_pktuart_send("test", data, &frames, &size) == expected); + assert(calls == fail_at); + } + read_size = size; + for (fail_at = 1; fail_at <= operations + 1; fail_at++) { + rtapi_u8 frames = 1; + rtapi_u16 limit = sizeof(data), frame_sizes[1]; + calls = 0; + assert(hm2_pktuart_read("test", data, &frames, &limit, frame_sizes) == expected); + assert(calls == fail_at); + } + } + for (int queued = 0; queued <= 1; queued++) { + for (unsigned fail = 1; fail <= 6; fail++) { + instance.tx_bitrate = instance.rx_bitrate = 0; + instance.tx_mode = instance.rx_mode = 0; + fail_at = fail; calls = writes = 0; + assert(hm2_pktuart_config("test", &cfg, &cfg, queued) == expected); + assert(calls == fail); + if (fail <= 1) assert(instance.rx_bitrate == 0); + if (fail <= 2) assert(instance.rx_mode == 0); + if (fail <= 4) assert(instance.tx_bitrate == 0); + if (fail <= 5) assert(instance.tx_mode == 0); + fail_at = 0; calls = writes = 0; + assert(hm2_pktuart_config("test", &cfg, &cfg, queued) == 0); + assert(instance.tx_bitrate != 0 && instance.rx_bitrate != 0); + assert(instance.tx_mode != 0 && instance.rx_mode != 0); + } + for (fail_at = 1; fail_at <= 2; fail_at++) { + calls = writes = errors = 0; + if (queued) hm2_pktuart_queue_reset("test"); + else hm2_pktuart_reset("test"); + assert(calls == 2 && errors == 1); + } + } + for (unsigned fail = 1; fail <= 6; fail++) { + instance.tx_bitrate = instance.rx_bitrate = 0; + fail_at = fail; calls = writes = 0; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + assert(hm2_pktuart_setup("test", 115200, 0, 0, 1, 1) == expected); +#pragma GCC diagnostic pop + assert(calls == fail); + } + } + fail_at = 0; + puts("PASS: zero/negative LLIO failures at every read/write stage, config retries and reset diagnostics"); +} + +int main(void) { + board.llio = &llio; + board.pktuart.instance = &instance; + board.pktuart.rx_status_reg = &status; + instance.tx_bitrate = instance.rx_bitrate = 115200; + instance.tx_addr = 0x1000; + instance.tx_fifo_count_addr = 0x1100; + llio.queue_read = queue_read; + llio.queue_write = queue_write; + rtapi_u32 buffer[1024] = {0}; + + for (unsigned count = 0; count <= 16; count++) { + status = count << 16; + reads = 0; + assert(hm2_pktuart_queue_get_frame_sizes("test", buffer) == (int)count); + assert(reads == count); + } + for (int bytes = 0; bytes <= 4096; bytes++) { + reads = 0; + assert(hm2_pktuart_queue_read_data("test", buffer, bytes) == (bytes + 3) / 4); + assert(reads == (unsigned)(bytes + 3) / 4); + } + instance.rx_bitrate = 0; + assert(hm2_pktuart_queue_get_frame_sizes("test", buffer) == -EINVAL); + assert(hm2_pktuart_queue_read_data("test", buffer, 4) == -EINVAL); + instance.rx_bitrate = 115200; + lookup_result = -1; + assert(hm2_pktuart_queue_get_frame_sizes("test", buffer) == -ENODEV); + assert(hm2_pktuart_queue_read_data("test", buffer, 4) == -ENODEV); + lookup_result = 0; + + for (unsigned byte = 0; byte <= 255; byte++) { + unsigned char data[8]; + memset(data, byte, sizeof(data)); + for (rtapi_u16 size = 1; size <= 8; size++) { + rtapi_u8 frames = 1; + writes = 0; + assert(hm2_pktuart_send("test", data, &frames, &size) == size); + assert(frames == 1); + unsigned nwords = (size + 3) / 4; + assert(writes == nwords + 1); + for (unsigned i = 0; i < nwords; i++) { + rtapi_u32 expected = 0; + for (unsigned j = 0; j < 4 && i * 4 + j < size; j++) + expected |= (rtapi_u32)byte << (j * 8); + assert(addresses[i] == instance.tx_addr && words[i] == expected); + } + assert(addresses[nwords] == instance.tx_fifo_count_addr); + assert(words[nwords] == size); + } + } + puts("PASS: 17 frame counts, 4097 byte counts, 4 error paths, 2048 packing cases"); + test_failures(); + return 0; +} diff --git a/tests/hostmot2/pktuart/test.sh b/tests/hostmot2/pktuart/test.sh new file mode 100644 index 00000000000..05b1b4d83c9 --- /dev/null +++ b/tests/hostmot2/pktuart/test.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -eu +repo_root=$(cd "$(dirname "$0")/../../.." && pwd) +task_tmp=$(mktemp -d) +trap 'rm -f "$task_tmp/check"; rmdir "$task_tmp"' EXIT +cc=${CC:-cc} +flags=(-O1 -g -fno-strict-aliasing -fwrapv -DRTAPI -DUSPACE -D_GNU_SOURCE + -I"$repo_root/include" -I"$repo_root/src" -I"$repo_root/src/hal/drivers/mesa-hostmot2" + -ffunction-sections -fdata-sections) +if [ -n "${SANITIZERS:-}" ]; then + flags+=("-fsanitize=$SANITIZERS" -fno-sanitize-recover=all) +fi +"$cc" "${flags[@]}" "$repo_root/src/hal/drivers/mesa-hostmot2/pktuart.c" \ + "$repo_root/tests/hostmot2/pktuart/test.c" -Wl,--gc-sections -o "$task_tmp/check" +"$task_tmp/check" From 7a995e5fd13e37b37362ef87a48733c23557cc8b Mon Sep 17 00:00:00 2001 From: joseph calderon Date: Fri, 9 Oct 2026 19:06:55 -0700 Subject: [PATCH 4/5] hostmot2: reject negative PktUART queued-read byte counts Return -EINVAL before computing the word count so negative requests cannot appear to succeed with zero reads. Test representative negative counts, including INT_MIN, and verify that they issue no LLIO calls. --- src/hal/drivers/mesa-hostmot2/pktuart.c | 1 + tests/hostmot2/pktuart/README | 6 +++--- tests/hostmot2/pktuart/expected | 2 +- tests/hostmot2/pktuart/test.c | 9 ++++++++- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/hal/drivers/mesa-hostmot2/pktuart.c b/src/hal/drivers/mesa-hostmot2/pktuart.c index c216c3b424a..aa6adccee2e 100644 --- a/src/hal/drivers/mesa-hostmot2/pktuart.c +++ b/src/hal/drivers/mesa-hostmot2/pktuart.c @@ -920,6 +920,7 @@ int hm2_pktuart_queue_read_data(const char *name, rtapi_u32 data[], int bytes) HM2_ERR("%s has not been configured.\n", name); return -EINVAL; } + if(bytes < 0) return -EINVAL; // queue enough reads to get the whole frame. Data will be transferred // to data[] next thread cycle, direct from FPGA, no serial latency diff --git a/tests/hostmot2/pktuart/README b/tests/hostmot2/pktuart/README index f710710ea5f..32f5db70849 100644 --- a/tests/hostmot2/pktuart/README +++ b/tests/hostmot2/pktuart/README @@ -8,9 +8,9 @@ Run after configuring/building a run-in-place tree: SANITIZERS=address,undefined bash tests/hostmot2/pktuart/test.sh CC can select another C compiler. Sanitizers are optional. -Checks cover queue counts, high-bit byte packing, missing/unconfigured -instances, zero and negative LLIO failures at each transfer stage, failed -configuration retries, legacy setup, and reset diagnostics. +Checks cover queue counts, negative byte counts, high-bit byte packing, +missing/unconfigured instances, zero and negative LLIO failures at each +transfer stage, failed configuration retries, legacy setup, and reset diagnostics. A successful queue operation is not confirmation that hardware received it. On a partial queue failure, earlier operations may remain queued; callers diff --git a/tests/hostmot2/pktuart/expected b/tests/hostmot2/pktuart/expected index 656aa14c310..f50d6792519 100644 --- a/tests/hostmot2/pktuart/expected +++ b/tests/hostmot2/pktuart/expected @@ -1,2 +1,2 @@ -PASS: 17 frame counts, 4097 byte counts, 4 error paths, 2048 packing cases +PASS: 17 frame counts, 4097 byte counts, 8 error cases, 2048 packing cases PASS: zero/negative LLIO failures at every read/write stage, config retries and reset diagnostics diff --git a/tests/hostmot2/pktuart/test.c b/tests/hostmot2/pktuart/test.c index 636aafcc778..61b47934b8e 100644 --- a/tests/hostmot2/pktuart/test.c +++ b/tests/hostmot2/pktuart/test.c @@ -1,4 +1,5 @@ #include +#include #include #include #include "hostmot2.h" @@ -154,6 +155,12 @@ int main(void) { assert(hm2_pktuart_queue_read_data("test", buffer, bytes) == (bytes + 3) / 4); assert(reads == (unsigned)(bytes + 3) / 4); } + const int invalid_bytes[] = {INT_MIN, -1024, -4, -1}; + for (unsigned i = 0; i < sizeof(invalid_bytes) / sizeof(invalid_bytes[0]); i++) { + reads = calls = 0; + assert(hm2_pktuart_queue_read_data("test", buffer, invalid_bytes[i]) == -EINVAL); + assert(reads == 0 && calls == 0); + } instance.rx_bitrate = 0; assert(hm2_pktuart_queue_get_frame_sizes("test", buffer) == -EINVAL); assert(hm2_pktuart_queue_read_data("test", buffer, 4) == -EINVAL); @@ -183,7 +190,7 @@ int main(void) { assert(words[nwords] == size); } } - puts("PASS: 17 frame counts, 4097 byte counts, 4 error paths, 2048 packing cases"); + puts("PASS: 17 frame counts, 4097 byte counts, 8 error cases, 2048 packing cases"); test_failures(); return 0; } From 4081d6b24ca8508c6e74ebefc6d0426678f17638 Mon Sep 17 00:00:00 2001 From: joseph calderon Date: Mon, 12 Oct 2026 10:33:17 -0700 Subject: [PATCH 5/5] hostmot2: align LLIO checks and test flags --- src/hal/drivers/mesa-hostmot2/pktuart.c | 88 ++++++++++++------------- tests/hostmot2/harness.mk | 16 +++++ tests/hostmot2/pktuart/README | 3 +- tests/hostmot2/pktuart/expected | 2 +- tests/hostmot2/pktuart/test.c | 17 +++-- tests/hostmot2/pktuart/test.sh | 16 ++--- 6 files changed, 78 insertions(+), 64 deletions(-) create mode 100644 tests/hostmot2/harness.mk diff --git a/src/hal/drivers/mesa-hostmot2/pktuart.c b/src/hal/drivers/mesa-hostmot2/pktuart.c index aa6adccee2e..bb0a202a987 100644 --- a/src/hal/drivers/mesa-hostmot2/pktuart.c +++ b/src/hal/drivers/mesa-hostmot2/pktuart.c @@ -301,24 +301,24 @@ static int config_tx(const char *name, const hostmot2_t* hm2, hm2_pktuart_instan } if((cfg->flags & HM2_PKTUART_CONFIG_FORCECONFIG) || bitrate != inst->tx_bitrate) { - if((r = writefn(hm2->llio, inst->tx_bitrate_addr, &bitrate, sizeof(bitrate))) <= 0) { + if((r = writefn(hm2->llio, inst->tx_bitrate_addr, &bitrate, sizeof(bitrate))) == 0) { HM2_ERR("Configure TX baudrate: hm2->llio->%s failure %s (error %d)\n", writenm, name, r); - return r < 0 ? r : -EIO; + return -EIO; } inst->tx_bitrate = bitrate; } if((cfg->flags & HM2_PKTUART_CONFIG_FORCECONFIG) || mode != inst->tx_mode) { - if((r = writefn(hm2->llio, inst->tx_mode_addr, &mode, sizeof(mode))) <= 0) { + if((r = writefn(hm2->llio, inst->tx_mode_addr, &mode, sizeof(mode))) == 0) { HM2_ERR("Configure TX mode: hm2->llio->%s failure %s (error %d)\n", writenm, name, r); - return r < 0 ? r : -EIO; + return -EIO; } inst->tx_mode = mode; } if(cfg->flags & HM2_PKTUART_CONFIG_FLUSH) { rtapi_u32 buff = HM2_PKTUART_CLEAR; // clear data FIFO and count register - if((r = writefn(hm2->llio, inst->tx_mode_addr, &buff, sizeof(buff))) <= 0) { + if((r = writefn(hm2->llio, inst->tx_mode_addr, &buff, sizeof(buff))) == 0) { HM2_ERR("Configure TX flush: hm2->llio->%s failure %s (error %d)\n", writenm, name, r); - return r < 0 ? r : -EIO; + return -EIO; } } return 0; @@ -381,24 +381,24 @@ static int config_rx(const char *name, const hostmot2_t *hm2, hm2_pktuart_instan } if((cfg->flags & HM2_PKTUART_CONFIG_FORCECONFIG) || bitrate != inst->rx_bitrate) { - if((r = writefn(hm2->llio, inst->rx_bitrate_addr, &bitrate, sizeof(bitrate))) <= 0) { + if((r = writefn(hm2->llio, inst->rx_bitrate_addr, &bitrate, sizeof(bitrate))) == 0) { HM2_ERR("Configure RX baudrate: hm2->llio->%s failure %s (error %d)\n", writenm, name, r); - return r < 0 ? r : -EIO; + return -EIO; } inst->rx_bitrate = bitrate; } if((cfg->flags & HM2_PKTUART_CONFIG_FORCECONFIG) || mode != inst->rx_mode) { - if((r = writefn(hm2->llio, inst->rx_mode_addr, &mode, sizeof(mode))) <= 0) { + if((r = writefn(hm2->llio, inst->rx_mode_addr, &mode, sizeof(mode))) == 0) { HM2_ERR("Configure RX mode: hm2->llio->%s failure %s (error %d)\n", writenm, name, r); - return r < 0 ? r : -EIO; + return -EIO; } inst->rx_mode = mode; } if(cfg->flags & HM2_PKTUART_CONFIG_FLUSH) { rtapi_u32 buff = HM2_PKTUART_CLEAR; // clear data FIFO and count register - if((r = writefn(hm2->llio, inst->rx_mode_addr, &buff, sizeof(buff))) <= 0) { + if((r = writefn(hm2->llio, inst->rx_mode_addr, &buff, sizeof(buff))) == 0) { HM2_ERR("Configure RX flush: hm2->llio->%s failure %s (error %d)\n", writenm, name, r); - return r < 0 ? r : -EIO; + return -EIO; } } return 0; @@ -457,10 +457,10 @@ static void perform_reset(const char *name, int queue) } // clear sends, data FIFO and count register - if((i = wr(hm2->llio, inst->tx_mode_addr, &buff, sizeof(rtapi_u32))) <= 0) + if((i = wr(hm2->llio, inst->tx_mode_addr, &buff, sizeof(rtapi_u32))) == 0) HM2_ERR("Failed to %s reset of TX data and FIFO (error %d)\n", msg, i); // clear receives, data FIFO and count register - if((i = wr(hm2->llio, inst->rx_mode_addr, &buff, sizeof(rtapi_u32))) <= 0) + if((i = wr(hm2->llio, inst->rx_mode_addr, &buff, sizeof(rtapi_u32))) == 0) HM2_ERR("Failed to %s reset of RX data and FIFO (error %d)\n", msg, i); } @@ -516,9 +516,9 @@ int hm2_pktuart_setup(const char *name, unsigned bitrate, rtapi_s32 tx_mode, rta buff = (rtapi_u32)((bitrate * 1048576.0)/inst->clock_freq); // 20 bits in v0 & v1 } if(buff != inst->tx_bitrate) { - if((r = hm2->llio->write(hm2->llio, inst->tx_bitrate_addr, &buff, sizeof(rtapi_u32))) <= 0) { + if((r = hm2->llio->write(hm2->llio, inst->tx_bitrate_addr, &buff, sizeof(rtapi_u32))) == 0) { HM2_ERR("PktUART setup: hm2->llio->write failure %s on tx bitrate (error %d)\n", name, r); - return r < 0 ? r : -EIO; + return -EIO; } inst->tx_bitrate = buff; } @@ -528,9 +528,9 @@ int hm2_pktuart_setup(const char *name, unsigned bitrate, rtapi_s32 tx_mode, rta buff = (rtapi_u32)((bitrate * 1048576.0)/inst->clock_freq); // 20 bits in v0 & v1 } if(buff != inst->rx_bitrate) { - if((r = hm2->llio->write(hm2->llio, inst->rx_bitrate_addr, &buff, sizeof(rtapi_u32))) <= 0) { + if((r = hm2->llio->write(hm2->llio, inst->rx_bitrate_addr, &buff, sizeof(rtapi_u32))) == 0) { HM2_ERR("PktUART setup: hm2->llio->write failure %s on rx bitrate (error %d)\n", name, r); - return r < 0 ? r : -EIO; + return -EIO; } inst->rx_bitrate = buff; } @@ -538,33 +538,33 @@ int hm2_pktuart_setup(const char *name, unsigned bitrate, rtapi_s32 tx_mode, rta if(tx_mode >= 0) { buff = ((rtapi_u32)tx_mode) & HM2_PKTUART_TXMODE_MASK; - if((r = hm2->llio->write(hm2->llio, inst->tx_mode_addr, &buff, sizeof(rtapi_u32))) <= 0) { + if((r = hm2->llio->write(hm2->llio, inst->tx_mode_addr, &buff, sizeof(rtapi_u32))) == 0) { HM2_ERR("PktUART setup: hm2->llio->write failure %s on tx_mode (error %d)\n", name, r); - return r < 0 ? r : -EIO; + return -EIO; } } if(rx_mode >= 0) { buff = ((rtapi_u32)rx_mode) & HM2_PKTUART_RXMODE_MASK; - if((r = hm2->llio->write(hm2->llio, inst->rx_mode_addr, &buff, sizeof(rtapi_u32))) <= 0) { + if((r = hm2->llio->write(hm2->llio, inst->rx_mode_addr, &buff, sizeof(rtapi_u32))) == 0) { HM2_ERR("PktUART setup: hm2->llio->write failure %s on rx_mode (error %d)\n", name, r); - return r < 0 ? r : -EIO; + return -EIO; } } buff = HM2_PKTUART_CLEAR; if(txclear == 1) { // clear sends, data FIFO and count register - if((r = hm2->llio->write(hm2->llio, inst->tx_mode_addr, &buff, sizeof(rtapi_u32))) <= 0) { + if((r = hm2->llio->write(hm2->llio, inst->tx_mode_addr, &buff, sizeof(rtapi_u32))) == 0) { HM2_ERR("PktUART setup: hm2->llio->write failure %s on tx clear (error %d)\n", name, r); - return r < 0 ? r : -EIO; + return -EIO; } } if(rxclear == 1) { // clear receives, data FIFO and count register - if((r = hm2->llio->write(hm2->llio, inst->rx_mode_addr, &buff, sizeof(rtapi_u32))) <= 0) { + if((r = hm2->llio->write(hm2->llio, inst->rx_mode_addr, &buff, sizeof(rtapi_u32))) == 0) { HM2_ERR("PktUART setup: hm2->llio->write failure %s on rx clear (error %d)\n", name, r); - return r < 0 ? r : -EIO; + return -EIO; } } @@ -619,9 +619,9 @@ int hm2_pktuart_send(const char *name, const unsigned char data[], rtapi_u8 *num | ((rtapi_u32)data[c+2] << 16) | ((rtapi_u32)data[c+3] << 24)); r = hm2->llio->queue_write(hm2->llio, hm2->pktuart.instance[inst].tx_addr, &buff, sizeof(buff)); - if(r <= 0) { + if(r == 0) { HM2_ERR("%s send: hm2->llio->queue_write failure\n", name); - return r < 0 ? r : -EIO; + return -EIO; } c += 4; } @@ -645,9 +645,9 @@ int hm2_pktuart_send(const char *name, const unsigned char data[], rtapi_u8 *num return -1; } // end switch r = hm2->llio->queue_write(hm2->llio, hm2->pktuart.instance[inst].tx_addr, &buff, sizeof(buff)); - if(r <= 0) { + if(r == 0) { HM2_ERR("%s send: hm2->llio->queue_write failure\n", name); - return r < 0 ? r : -EIO; + return -EIO; } } (*num_frames)++; @@ -663,9 +663,9 @@ int hm2_pktuart_send(const char *name, const unsigned char data[], rtapi_u8 *num // Not possible to check for errors on the fly when using queued // transfers. The data does not arrive immediately. Therefore, no // checks can be done here. - if(r <= 0) { + if(r == 0) { HM2_ERR("%s send: hm2->llio->queue_write failure\n", name); - return r < 0 ? r : -EIO; + return -EIO; } } return count; @@ -709,9 +709,9 @@ int hm2_pktuart_read(const char *name, unsigned char data[], rtapi_u8 *num_frame // First poll the mode register for a non zero frames received count // (mode register bits 20..16) r = hm2->llio->read(hm2->llio, hm2->pktuart.instance[inst].rx_mode_addr, &buff, sizeof(rtapi_u32)); - if(r <= 0) { + if(r == 0) { HM2_ERR("%s read: hm2->llio->read failure\n", name); - return r < 0 ? r : -EIO; + return -EIO; } if(buff & (0x1 << 21)) { countp = (buff >> 16) & 0x1f; @@ -764,9 +764,9 @@ int hm2_pktuart_read(const char *name, unsigned char data[], rtapi_u8 *num_frame of received packets. Since it is a FIFO it must only be read once after it has be determined that there are packets available to read. */ r = hm2->llio->read(hm2->llio, hm2->pktuart.instance[inst].rx_fifo_count_addr, &buff, sizeof(buff)); - if(r <= 0) { + if(r == 0) { HM2_ERR("%s read: hm2->llio->read failure\n", name); - return r < 0 ? r : -EIO; + return -EIO; } countb = buff & 0x3ff; // PktUARTr receive count register Bits 9..0 : bytes in receive packet @@ -806,9 +806,9 @@ int hm2_pktuart_read(const char *name, unsigned char data[], rtapi_u8 *num_frame while(c < countb - 3) { r = hm2->llio->read(hm2->llio, hm2->pktuart.instance[inst].rx_addr, &buff, sizeof(buff)); - if(r <= 0) { + if(r == 0) { HM2_ERR("%s read: hm2->llio->read failure\n", name); - return r < 0 ? r : -EIO; + return -EIO; } data[bytes_total+c+0] = (buff & 0x000000FF); // i*frame_sizes[i] @@ -820,9 +820,9 @@ int hm2_pktuart_read(const char *name, unsigned char data[], rtapi_u8 *num_frame if(countb - c) { r = hm2->llio->read(hm2->llio, hm2->pktuart.instance[inst].rx_addr, &buff, sizeof(buff)); - if(r <= 0) { + if(r == 0) { HM2_ERR("%s read: hm2->llio->read failure\n", name); - return r < 0 ? r : -EIO; + return -EIO; } switch(countb - c) { case 1: @@ -882,9 +882,9 @@ int hm2_pktuart_queue_get_frame_sizes(const char *name, rtapi_u32 fsizes[]) for(j = 0; j < nfs; j++ ) { r = hm2->llio->queue_read(hm2->llio, hm2->pktuart.instance[inst].rx_fifo_count_addr, &fsizes[j], sizeof(rtapi_u32)); - if(r <= 0) { + if(r == 0) { HM2_ERR("Unable to queue Rx FIFO count read %d of %d (error %d))\n", j, nfs, r); - return r < 0 ? r : -EIO; + return -EIO; } } return j; @@ -928,9 +928,9 @@ int hm2_pktuart_queue_read_data(const char *name, rtapi_u32 data[], int bytes) for(i = 0; i < nrx; i++ ) { r = hm2->llio->queue_read(hm2->llio, hm2->pktuart.instance[inst].rx_addr, &data[i], sizeof(rtapi_u32)); - if(r <= 0) { + if(r == 0) { HM2_ERR("Unable to queue Rx FIFO read %d of %d (error %d)\n", i, nrx, r); - return r < 0 ? r : -EIO; + return -EIO; } } return i; diff --git a/tests/hostmot2/harness.mk b/tests/hostmot2/harness.mk new file mode 100644 index 00000000000..d9c8a7233a1 --- /dev/null +++ b/tests/hostmot2/harness.mk @@ -0,0 +1,16 @@ +# Used with src/Makefile so mocks inherit the configured realtime build flags. +ifneq ($(BUILD_SYS),uspace) +$(error HostMot2 mock tests require a configured uspace build) +endif + +ifneq ($(SANITIZERS),) +HM2_TEST_SANITIZERS = -fsanitize=$(SANITIZERS) -fno-sanitize-recover=all +endif + +.PHONY: hm2-test +hm2-test: + $(CC) $(OPT) $(DEBUG) $(EXTRA_DEBUG) -DRTAPI \ + $(EXTRA_CFLAGS) $(XENOMAI_EVL_CFLAGS) $(HM2_TEST_SANITIZERS) \ + -I$(BASEPWD)/hal/drivers/mesa-hostmot2 -ffunction-sections -fdata-sections \ + $(TEST_CFLAGS) $(TEST_SOURCES) -Wl,--gc-sections $(TEST_LIBS) \ + $(XENOMAI_EVL_LDFLAGS) $(LDFLAGS) -o "$(TEST_OUTPUT)" diff --git a/tests/hostmot2/pktuart/README b/tests/hostmot2/pktuart/README index 32f5db70849..bf12703296a 100644 --- a/tests/hostmot2/pktuart/README +++ b/tests/hostmot2/pktuart/README @@ -7,9 +7,10 @@ Run after configuring/building a run-in-place tree: scripts/rip-environment runtests tests/hostmot2/pktuart SANITIZERS=address,undefined bash tests/hostmot2/pktuart/test.sh +Uses the configured realtime compiler and flags from src/Makefile. CC can select another C compiler. Sanitizers are optional. Checks cover queue counts, negative byte counts, high-bit byte packing, -missing/unconfigured instances, zero and negative LLIO failures at each +missing/unconfigured instances, nonzero LLIO success and zero failures at each transfer stage, failed configuration retries, legacy setup, and reset diagnostics. A successful queue operation is not confirmation that hardware received it. diff --git a/tests/hostmot2/pktuart/expected b/tests/hostmot2/pktuart/expected index f50d6792519..595c3299a5d 100644 --- a/tests/hostmot2/pktuart/expected +++ b/tests/hostmot2/pktuart/expected @@ -1,2 +1,2 @@ PASS: 17 frame counts, 4097 byte counts, 8 error cases, 2048 packing cases -PASS: zero/negative LLIO failures at every read/write stage, config retries and reset diagnostics +PASS: zero LLIO failures at every read/write stage, nonzero success, config retries and reset diagnostics diff --git a/tests/hostmot2/pktuart/test.c b/tests/hostmot2/pktuart/test.c index 61b47934b8e..5e49f2c7375 100644 --- a/tests/hostmot2/pktuart/test.c +++ b/tests/hostmot2/pktuart/test.c @@ -13,7 +13,7 @@ static unsigned reads, writes; static rtapi_u32 words[64], addresses[64]; static int lookup_result; static unsigned fail_at, calls, errors; -static int failure; +static int success = 1; static rtapi_u32 read_size; int hm2_get_pktuart(hostmot2_t **hm2, const char *name) { @@ -34,21 +34,21 @@ static int queue_read(hm2_lowlevel_io_t *io, rtapi_u32 addr, void *data, int siz assert(size == 4); reads++; calls++; - if (fail_at && calls == fail_at) return failure; + if (fail_at && calls == fail_at) return 0; rtapi_u32 value = addr == instance.rx_mode_addr ? ((1u << 21) | (1u << 16)) : addr == instance.rx_fifo_count_addr ? read_size : 0x12345678; memcpy(data, &value, 4); - return 1; + return success; } static int queue_write(hm2_lowlevel_io_t *io, rtapi_u32 addr, const void *data, int size) { (void)io; assert(size == 4 && writes < 64); calls++; - if (fail_at && calls == fail_at) return failure; + if (fail_at && calls == fail_at) return 0; addresses[writes] = addr; memcpy(&words[writes++], data, 4); - return 1; + return success; } @@ -66,9 +66,8 @@ static void test_failures(void) { rtapi_u32 buffer[4]; hm2_pktuart_config_t cfg = { .baudrate = 115200, .flags = HM2_PKTUART_CONFIG_FLUSH | HM2_PKTUART_CONFIG_RXEN | HM2_PKTUART_CONFIG_DRIVEEN }; - for (unsigned convention = 0; convention < 2; convention++) { - failure = convention ? -EFAULT : 0; - int expected = convention ? -EFAULT : -EIO; + for (success = 1; success <= 2; success++) { + int expected = -EIO; instance.tx_bitrate = instance.rx_bitrate = 115200; status = 2u << 16; for (fail_at = 1; fail_at <= 2; fail_at++) { @@ -130,7 +129,7 @@ static void test_failures(void) { } } fail_at = 0; - puts("PASS: zero/negative LLIO failures at every read/write stage, config retries and reset diagnostics"); + puts("PASS: zero LLIO failures at every read/write stage, nonzero success, config retries and reset diagnostics"); } int main(void) { diff --git a/tests/hostmot2/pktuart/test.sh b/tests/hostmot2/pktuart/test.sh index 05b1b4d83c9..332247b2e9d 100644 --- a/tests/hostmot2/pktuart/test.sh +++ b/tests/hostmot2/pktuart/test.sh @@ -3,13 +3,11 @@ set -eu repo_root=$(cd "$(dirname "$0")/../../.." && pwd) task_tmp=$(mktemp -d) trap 'rm -f "$task_tmp/check"; rmdir "$task_tmp"' EXIT -cc=${CC:-cc} -flags=(-O1 -g -fno-strict-aliasing -fwrapv -DRTAPI -DUSPACE -D_GNU_SOURCE - -I"$repo_root/include" -I"$repo_root/src" -I"$repo_root/src/hal/drivers/mesa-hostmot2" - -ffunction-sections -fdata-sections) -if [ -n "${SANITIZERS:-}" ]; then - flags+=("-fsanitize=$SANITIZERS" -fno-sanitize-recover=all) -fi -"$cc" "${flags[@]}" "$repo_root/src/hal/drivers/mesa-hostmot2/pktuart.c" \ - "$repo_root/tests/hostmot2/pktuart/test.c" -Wl,--gc-sections -o "$task_tmp/check" +make_args=(-s --no-print-directory -C "$repo_root/src" -f Makefile + -f "$repo_root/tests/hostmot2/harness.mk" -o Makefile -o Makefile.inc + TRIVIAL_BUILD=yes) +if [ -n "${CC:-}" ]; then make_args+=("CC=$CC"); fi +make "${make_args[@]}" hm2-test \ + "TEST_SOURCES=hal/drivers/mesa-hostmot2/pktuart.c ../tests/hostmot2/pktuart/test.c" \ + "TEST_OUTPUT=$task_tmp/check" >&2 "$task_tmp/check"