diff --git a/docs/src/man/man3/hm2_pktuart.3.adoc b/docs/src/man/man3/hm2_pktuart.3.adoc index 802e5ae4660..dea310ee7ab 100644 --- a/docs/src/man/man3/hm2_pktuart.3.adoc +++ b/docs/src/man/man3/hm2_pktuart.3.adoc @@ -150,11 +150,15 @@ 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. +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 4560ba18059..bb0a202a987 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) { @@ -300,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 -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 -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 -EIO; } } return 0; @@ -380,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 -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 -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 -EIO; } } return 0; @@ -456,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); } @@ -515,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 -EIO; } + inst->tx_bitrate = buff; } if(hm2->pktuart.rx_version >= 2) { buff = (rtapi_u32)((bitrate * 16777216.0)/inst->clock_freq); // 24 bits in v2+ @@ -527,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 -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 -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 -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 -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 -EIO; } } @@ -613,11 +614,14 @@ 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) { + if(r == 0) { HM2_ERR("%s send: hm2->llio->queue_write failure\n", name); - return r; + return -EIO; } c += 4; } @@ -627,16 +631,23 @@ 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; } // 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 -EIO; } } (*num_frames)++; @@ -652,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 -EIO; } } return count; @@ -698,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 -EIO; } if(buff & (0x1 << 21)) { countp = (buff >> 16) & 0x1f; @@ -753,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 -EIO; + } countb = buff & 0x3ff; // PktUARTr receive count register Bits 9..0 : bytes in receive packet @@ -791,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 -EIO; } data[bytes_total+c+0] = (buff & 0x000000FF); // i*frame_sizes[i] @@ -805,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) { - HM2_ERR("%s read: hm2->llio->queue_write failure\n", name); - return -1; + if(r == 0) { + HM2_ERR("%s read: hm2->llio->read failure\n", name); + return -EIO; } switch(countb - c) { case 1: @@ -867,11 +882,12 @@ 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 -EIO; } } - return j - 1; + return j; } EXPORT_SYMBOL_GPL(hm2_pktuart_queue_get_frame_sizes); @@ -904,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 @@ -911,11 +928,12 @@ 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 -EIO; } } - return i - 1; + return i; } EXPORT_SYMBOL_GPL(hm2_pktuart_queue_read_data); 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 new file mode 100644 index 00000000000..bf12703296a --- /dev/null +++ b/tests/hostmot2/pktuart/README @@ -0,0 +1,22 @@ +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 + +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, 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. +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..595c3299a5d --- /dev/null +++ b/tests/hostmot2/pktuart/expected @@ -0,0 +1,2 @@ +PASS: 17 frame counts, 4097 byte counts, 8 error cases, 2048 packing cases +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 new file mode 100644 index 00000000000..5e49f2c7375 --- /dev/null +++ b/tests/hostmot2/pktuart/test.c @@ -0,0 +1,195 @@ +#include +#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 success = 1; +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 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 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 0; + addresses[writes] = addr; + memcpy(&words[writes++], data, 4); + return success; +} + + +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 (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++) { + 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 LLIO failures at every read/write stage, nonzero success, 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); + } + 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); + 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, 8 error cases, 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..332247b2e9d --- /dev/null +++ b/tests/hostmot2/pktuart/test.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -eu +repo_root=$(cd "$(dirname "$0")/../../.." && pwd) +task_tmp=$(mktemp -d) +trap 'rm -f "$task_tmp/check"; rmdir "$task_tmp"' EXIT +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"