Messages in this thread | | | Subject | Re: [PATCH v12 5/5] selftest: mhi: Add support to test MHI LOOPBACK channel | From | Shuah Khan <> | Date | Fri, 20 Nov 2020 08:26:06 -0700 |
| |
On 11/16/20 3:46 PM, Hemant Kumar wrote: > Loopback test opens the MHI device file node and writes > a data buffer to it. MHI UCI kernel space driver copies > the data and sends it to MHI uplink (Tx) LOOPBACK channel. > MHI device loops back the same data to MHI downlink (Rx) > LOOPBACK channel. This data is read by test application > and compared against the data sent. Test passes if data > buffer matches between Tx and Rx. Test application performs > open(), poll(), write(), read() and close() file operations. > > Signed-off-by: Hemant Kumar <hemantk@codeaurora.org> > --- > Documentation/mhi/uci.rst | 32 + > tools/testing/selftests/Makefile | 1 + > tools/testing/selftests/drivers/.gitignore | 1 + > tools/testing/selftests/drivers/mhi/Makefile | 8 + > tools/testing/selftests/drivers/mhi/config | 2 + > .../testing/selftests/drivers/mhi/loopback_test.c | 802 +++++++++++++++++++++ > 6 files changed, 846 insertions(+) > create mode 100644 tools/testing/selftests/drivers/mhi/Makefile > create mode 100644 tools/testing/selftests/drivers/mhi/config > create mode 100644 tools/testing/selftests/drivers/mhi/loopback_test.c > > diff --git a/Documentation/mhi/uci.rst b/Documentation/mhi/uci.rst > index ce8740e..0a04afe 100644 > --- a/Documentation/mhi/uci.rst > +++ b/Documentation/mhi/uci.rst > @@ -79,6 +79,38 @@ MHI client driver performs read operation, same data gets looped back to MHI > host using LOOPBACK channel 1. LOOPBACK channel is used to verify data path > and data integrity between MHI Host and MHI device. >
Nice.
> +Loopback Test > +~~~~~~~~~~~~~ > + > +Loopback test application is used to verify data integrity between MHI host and > +MHI device over LOOPBACK channel. This also confirms that basic MHI data path > +is working properly. Test performs write() to send tx buffer to MHI device file > +node for LOOPBACK uplink channel. MHI LOOPBACK downlink channel loops back > +transmit data to MHI Host. Test application receives data in receive buffer as > +part of read(). It verifies if tx buffer matches rx buffer. Test application > +performs poll() before making write() and read() system calls. Test passes if > +match is found. > + > +Test is present under tools/testing/selftests/drivers/mhi. It is compiled using > +following command in same dir:- > + > +make loopback_test > + > +Test is run using following command arguments:- > + > +loopback_test -c <device_node> -b <transmit buffer size> -l <log level> -i > +<number of iterations> > + > +Required argument: > +-c : loopback chardev node > + > +Optional argument: > +-b : transmit buffer size. If not present 1024 bytes size transmit buffer > + is sent. > +-i : Number of iterations to perform, If not present only one transmit buffer > + is sent. > +-l : Log level. If not present defaults to DBG_LVL_INFO. > + > Other Use Cases > --------------- > > diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile > index d9c2835..084bc1e 100644 > --- a/tools/testing/selftests/Makefile > +++ b/tools/testing/selftests/Makefile > @@ -10,6 +10,7 @@ TARGETS += core > TARGETS += cpufreq > TARGETS += cpu-hotplug > TARGETS += drivers/dma-buf > +TARGETS += drivers/mhi > TARGETS += efivarfs > TARGETS += exec > TARGETS += filesystems > diff --git a/tools/testing/selftests/drivers/.gitignore b/tools/testing/selftests/drivers/.gitignore > index ca74f2e..e4806d5 100644 > --- a/tools/testing/selftests/drivers/.gitignore > +++ b/tools/testing/selftests/drivers/.gitignore > @@ -1,2 +1,3 @@ > # SPDX-License-Identifier: GPL-2.0-only > /dma-buf/udmabuf > +/mhi/loopback_test > diff --git a/tools/testing/selftests/drivers/mhi/Makefile b/tools/testing/selftests/drivers/mhi/Makefile > new file mode 100644 > index 0000000..c06c925 > --- /dev/null > +++ b/tools/testing/selftests/drivers/mhi/Makefile > @@ -0,0 +1,8 @@ > +# SPDX-License-Identifier: GPL-2.0-only > +CFLAGS += -I../../../../../usr/include/ -g -Wall > + > +LDLIBS = -lpthread > +TEST_GEN_PROGS := loopback_test > + > +include ../../lib.mk > + > diff --git a/tools/testing/selftests/drivers/mhi/config b/tools/testing/selftests/drivers/mhi/config > new file mode 100644 > index 0000000..471dc92 > --- /dev/null > +++ b/tools/testing/selftests/drivers/mhi/config > @@ -0,0 +1,2 @@ > +CONFIG_MHI_BUS=y > +CONFIG_MHi_UCI=y > diff --git a/tools/testing/selftests/drivers/mhi/loopback_test.c b/tools/testing/selftests/drivers/mhi/loopback_test.c > new file mode 100644 > index 0000000..99b7712 > --- /dev/null > +++ b/tools/testing/selftests/drivers/mhi/loopback_test.c > @@ -0,0 +1,802 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Loopback test application performs write() to send tx buffer to MHI device > + * file node for LOOPBACK uplink channel. MHI LOOPBACK downlink channel loops > + * back transmit data to MHI Host. Test application receives data in receive > + * buffer as part of read(). It verifies if tx buffer matches rx buffer. Test > + * application performs poll() before making write() and read() system > + * calls. Test passes if match is found. > + * > + * Test is compiled using following command:- > + * > + * make loopback_test > + * > + * Test is run using following command arguments:- > + * > + * loopback_test -c <device_node> -b <transmit buffer size> -l <log level> -i > + * <number of iterations> > + * > + * Required argument: > + * -c : loopback chardev node > + * > + * Optional argument: > + * -b : transmit buffer size. If not present 1024 bytes size transmit buffer > + * is sent. > + * -i : Number of iterations to perform, If not present only one transmit buffer > + * is sent. > + * -l : Log level. If not present defaults to DBG_LVL_INFO. > + */ > + > +#include <errno.h> > +#include <fcntl.h> > +#include <getopt.h> > +#include <pthread.h> > +#include <stdbool.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <sys/poll.h> > +#include <unistd.h> > + > +enum debug_level { > + DBG_LVL_VERBOSE, > + DBG_LVL_INFO, > + DBG_LVL_ERROR, > +}; > + > +enum test_status { > + TEST_STATUS_SUCCESS, > + TEST_STATUS_ERROR, > + TEST_STATUS_NO_DEV, > +}; > +
Since you are running this test as part of kselftest, please use ksft errors nd returns.
> +struct lb_test_ctx { > + char dev_node[256]; > + unsigned char *tx_buff; > + unsigned char *rx_buff; > + unsigned int rx_pkt_count; > + unsigned int tx_pkt_count; > + int iterations; > + bool iter_complete; > + bool comp_complete; > + bool test_complete; > + bool all_complete; > + unsigned long buff_size; > + long byte_recvd; > + long byte_sent; > +}; > + > +bool force_exit; > +char write_data = 'a'; > +int completed_iterations; > + > +struct lb_test_ctx test_ctxt; > +enum debug_level msg_lvl; > +struct pollfd read_fd; > +struct pollfd write_fd; > +enum test_status mhi_test_return_value; > +enum test_status tx_status; > +enum test_status rx_status; > +enum test_status cmp_rxtx_status; > + > +#define test_log(test_msg_lvl, format, ...) do { \ > + if (test_msg_lvl >= msg_lvl) \ > + fprintf(stderr, format, ##__VA_ARGS__); \ > +} while (0) > + > +static void loopback_test_sleep_ms(int ms) > +{ > + usleep(1000 * ms); > +} > +
Have you run this as part of "make kselftest" run. How does this sleep work in that env.?
> +static enum test_status loopback_test_open(struct pollfd *write_fd, > + struct pollfd *read_fd, char *dev_name) > +{ > + enum test_status ret_val = TEST_STATUS_SUCCESS; > + > + write_fd->fd = open(dev_name, O_SYNC | O_WRONLY); > + if (write_fd->fd < 0) { > + test_log(DBG_LVL_ERROR, "Error opening file for w: errno: %d\n", errno); > + ret_val = TEST_STATUS_ERROR; > + } else { > + test_log(DBG_LVL_INFO, "Opened %s, return code: %i.\n", > + dev_name, write_fd->fd); > + write_fd->events = POLLOUT; > + } > + > + read_fd->fd = open(dev_name, O_SYNC | O_RDONLY); > + if (read_fd->fd < 0) { > + test_log(DBG_LVL_ERROR, "Error opening file for r: errno: %d\n", errno); > + ret_val = TEST_STATUS_ERROR; > + } else { > + test_log(DBG_LVL_INFO, "Opened %s, return code: %i.\n", > + dev_name, read_fd->fd); > + read_fd->events = POLLIN; > + } > + > + return ret_val; > +} > + > +static enum test_status loopback_test_close(struct pollfd *write_fd, > + struct pollfd *read_fd) > +{ > + enum test_status ret_val = TEST_STATUS_SUCCESS; > + > + if (close(read_fd->fd) == 0) { > + test_log(DBG_LVL_INFO, "Closed device handle\n"); > + } else { > + test_log(DBG_LVL_ERROR, "Unable to close handle to device\n"); > + ret_val = TEST_STATUS_ERROR; > + } > + > + if (close(write_fd->fd) == 0) { > + test_log(DBG_LVL_INFO, "Closed device handle\n"); > + } else { > + test_log(DBG_LVL_ERROR, "Unable to close handle to device\n"); > + ret_val = TEST_STATUS_ERROR; > + } > +
This should match the KSFT error codes
> + return ret_val; > +} > + > +static enum test_status loopback_test_alloc_rx_tx_buff(void) > +{ > + test_ctxt.rx_buff = (unsigned char *)malloc(test_ctxt.buff_size); > + if (!test_ctxt.rx_buff) { > + test_log(DBG_LVL_ERROR, "Failed to allocate rx buff\n"); > + return TEST_STATUS_ERROR; > + } > + > + memset(test_ctxt.rx_buff, 0x00, sizeof(test_ctxt.buff_size)); > + test_ctxt.tx_buff = (unsigned char *)malloc(test_ctxt.buff_size); > + if (!test_ctxt.tx_buff) { > + test_log(DBG_LVL_ERROR, "Failed to allocate tx buff\n"); > + return TEST_STATUS_ERROR; > + } > + > + return TEST_STATUS_SUCCESS; > +} > + > +static enum test_status loopback_test_write(struct pollfd *write_fd) > +{ > + enum test_status ret_value = TEST_STATUS_SUCCESS; > + signed long temp_bytes_sent = 0; > + signed long total_bytes_sent = 0; > + > + test_log(DBG_LVL_VERBOSE, "About to write %lu\n", test_ctxt.buff_size); > + > + temp_bytes_sent = write(write_fd->fd, > + test_ctxt.tx_buff + test_ctxt.byte_sent, test_ctxt.buff_size); > + > + test_log(DBG_LVL_VERBOSE, "Returned %li\n", temp_bytes_sent); > + test_log(DBG_LVL_INFO, "Wrote packet with %li bytes of %02x\n", > + temp_bytes_sent, *(test_ctxt.tx_buff + test_ctxt.byte_sent)); > + > + if (temp_bytes_sent < 0) { > + /* write returned a negative value i.e. write failed */ > + test_log(DBG_LVL_ERROR, "write() failed: err code %d\n", errno); > + return TEST_STATUS_ERROR; > + } > + > + if ((unsigned long)temp_bytes_sent > test_ctxt.buff_size) { > + test_log(DBG_LVL_ERROR, "Wrote more bytes than intended\n"); > + return TEST_STATUS_ERROR; > + } > + > + if (temp_bytes_sent == 0) { > + loopback_test_sleep_ms(10); > + } else if (temp_bytes_sent > 0) { > + test_ctxt.tx_pkt_count++; > + test_ctxt.byte_sent += temp_bytes_sent; > + total_bytes_sent += temp_bytes_sent; > + test_log(DBG_LVL_INFO, "Total written packets %d\n", > + test_ctxt.tx_pkt_count); > + } > + > + return ret_value; > +} > + > +static void loopback_test_reset_ctxt(void) > +{ > + test_ctxt.byte_sent = 0; > + __sync_synchronize(); > + test_ctxt.byte_recvd = 0; > + test_ctxt.rx_pkt_count = 0; > + test_ctxt.tx_pkt_count = 0; > + test_ctxt.comp_complete = false; > + memset((void *)test_ctxt.rx_buff, 0x00, test_ctxt.buff_size); > +} > + > +static enum test_status loopback_test_time_out(int *time_count) > +{ > + enum test_status ret_val = TEST_STATUS_SUCCESS; > + > + loopback_test_sleep_ms(3); > + if ((++*time_count) > 100000) { > + test_log(DBG_LVL_ERROR, "Error: all bytes not received for this iteration\n"); > + ret_val = TEST_STATUS_ERROR; > + while (1) > + loopback_test_sleep_ms(1000); > + } > + > + return ret_val; > +} > + > +static enum test_status loopback_test_poll(struct pollfd *fd, > + bool poll_timeout) > +{ > + enum test_status ret_val = TEST_STATUS_SUCCESS; > + int poll_return = 0; > + int i = 0; > + int timeout = 1000; /* poll-wait 1 second */ > + int file_desc_type = -1; > + > + if (fd->events == POLLIN) { > + file_desc_type = 1; /* read */ > + test_log(DBG_LVL_VERBOSE, > + "Poll: opened read handle to device\n"); > + } else if (fd->events == POLLOUT) { > + file_desc_type = 0; /* write */ > + test_log(DBG_LVL_VERBOSE, > + "Poll: opened write handle to device\n"); > + } > + > + if (poll_timeout == false) > + timeout = -1; /* poll-wait forever */ > + > + while (ret_val == TEST_STATUS_SUCCESS) { > + if (force_exit == true) { > + ret_val = TEST_STATUS_ERROR; > + } else { > + test_log(DBG_LVL_VERBOSE, "%s Polling %i\n", > + file_desc_type ? "Read" : "Write", i); > + > + poll_return = poll(fd, 1, timeout); > + if (poll_return == -1) { > + test_log(DBG_LVL_ERROR, "%s Poll error - %d\n", > + file_desc_type ? "Read" : "Write", > + errno); > + return TEST_STATUS_ERROR; > + } else if (((file_desc_type == 1) && > + (fd->revents & POLLIN)) || > + ((file_desc_type == 0) && > + (fd->revents & POLLOUT))) { > + test_log(DBG_LVL_VERBOSE, > + "%s Poll Returned\n", > + file_desc_type ? "Read" : "Write"); > + return TEST_STATUS_SUCCESS; > + } else if (poll_timeout == true) { > + i++; > + poll_return = 0; > + if (i >= 250) { > + test_log(DBG_LVL_ERROR, > + "Poll: %s Timed out: 10s\n", > + file_desc_type ? "Read" : "Write"); > + return TEST_STATUS_ERROR; > + } > + } > + > + loopback_test_sleep_ms(10); > + } > + } > + > + return ret_val; > +} > + > +static void *loopback_test_rx(void *data) > +{ > + bool poll_timeout = true; > + unsigned long read_size = test_ctxt.buff_size; > + long temp_bytes_rec; > + enum test_status status; > + > + rx_status = TEST_STATUS_SUCCESS; > + > + while (force_exit == false) { > + if (test_ctxt.test_complete) { > + test_log(DBG_LVL_INFO, "Rx: exiting thread\n"); > + pthread_exit(&rx_status); > + return NULL; > + } > + > + while ((test_ctxt.byte_sent > test_ctxt.byte_recvd) && > + (force_exit == false)) { > + test_log(DBG_LVL_VERBOSE, > + "Rx: Bytes Sent: %li, Received %li\n", > + test_ctxt.byte_sent, test_ctxt.byte_recvd); > + > + temp_bytes_rec = 0; > + > + if (test_ctxt.comp_complete == false) { > + status = loopback_test_poll(&read_fd, > + poll_timeout); > + if (status == TEST_STATUS_ERROR) { > + if (test_ctxt.test_complete == true) > + rx_status = TEST_STATUS_SUCCESS; > + else > + rx_status = TEST_STATUS_ERROR; > + > + test_log(DBG_LVL_INFO, > + "Rx: exiting thread\n"); > + pthread_exit(&rx_status); > + return NULL; > + } > + > + test_log(DBG_LVL_VERBOSE, "Rx: before read\n"); > + temp_bytes_rec = read(read_fd.fd, > + test_ctxt.rx_buff + test_ctxt.byte_recvd, > + read_size); > + if (temp_bytes_rec < 0) { > + test_log(DBG_LVL_ERROR, > + "Rx: read() failed - err code %d\n", > + errno); > + rx_status = TEST_STATUS_ERROR; > + pthread_exit(&rx_status); > + return NULL; > + } > + > + if (temp_bytes_rec == 0) { > + loopback_test_sleep_ms(10); > + rx_status = TEST_STATUS_ERROR; > + pthread_exit(&rx_status); > + return NULL; > + } > + > + test_ctxt.rx_pkt_count++; > + test_log(DBG_LVL_INFO, > + "Rx: Read packet with %li bytes of %02x\n", > + temp_bytes_rec, > + *(test_ctxt.rx_buff + test_ctxt.byte_recvd)); > + test_ctxt.byte_recvd += temp_bytes_rec; > + test_log(DBG_LVL_INFO, > + "Rx: Total byte_recvd: %li\n", > + test_ctxt.byte_recvd); > + test_log(DBG_LVL_INFO, > + "Rx: Total read packets %d\n", > + test_ctxt.rx_pkt_count); > + test_log(DBG_LVL_INFO, > + "Rx: %lu byte buff_size\n", > + test_ctxt.buff_size); > + > + read_fd.revents = 0; > + } > + } > + } > + rx_status = TEST_STATUS_ERROR; > + pthread_exit(&rx_status); > + return NULL; > +} > + > +/* This single compare thread, will compare each byte of tx and rx Buff */ > +static void *loopback_test_compare_rxtx(void *data) > +{ > + int k; > + bool completion = true; > + long curr_ptr = 0; > + > + test_log(DBG_LVL_INFO, "CompareRxTxThread: Inside thread\n"); > + cmp_rxtx_status = TEST_STATUS_SUCCESS; > + > + for (;;) { > + /* if the test is completed exit thread */ > + if (test_ctxt.all_complete == true) { > + curr_ptr = 0; > + test_log(DBG_LVL_INFO, > + "CompareRxTx: exiting thread\n"); > + pthread_exit(&cmp_rxtx_status); > + return NULL; > + } > + > + /* Simply compares each byte in each Tx and Rx buffer */ > + while (curr_ptr < test_ctxt.byte_recvd) { > + if (*(test_ctxt.rx_buff + curr_ptr) != > + *(test_ctxt.tx_buff + curr_ptr)) { > + test_log(DBG_LVL_ERROR, > + "Mismatch Byte num %li\n", curr_ptr); > + test_log(DBG_LVL_ERROR, > + "Bytes: Sent 0x%x, Received 0x%x\n", > + *(test_ctxt.tx_buff + curr_ptr), > + *(test_ctxt.rx_buff + curr_ptr)); > + test_log(DBG_LVL_VERBOSE, "Rx buffer dump:\n"); > + for (k = 0; k < 1024; k++) > + test_log(DBG_LVL_VERBOSE, "%d:0x%x ", k, > + *(test_ctxt.rx_buff + k)); > + > + test_log(DBG_LVL_VERBOSE, "\n"); > + > + test_log(DBG_LVL_INFO, > + "CompareRxTx: finished iterations %i\n", > + completed_iterations); > + test_log(DBG_LVL_INFO, > + "CompareRxTx: Total pkts sent %u\n", > + test_ctxt.tx_pkt_count); > + > + curr_ptr = 0; > + force_exit = true; > + cmp_rxtx_status = TEST_STATUS_ERROR; > + pthread_exit(&cmp_rxtx_status); > + return NULL; > + } > + > + test_log(DBG_LVL_VERBOSE, > + "CompareRxTx: Bytes Sent 0x%x, Received 0x%x\n", > + *(test_ctxt.tx_buff + curr_ptr), > + *(test_ctxt.rx_buff + curr_ptr)); > + curr_ptr++; > + } > + > + /* > + * completion will start as true and change depending if the > + * iterations has been completed for all client threads > + */ > + if (curr_ptr >= test_ctxt.byte_sent) > + completion = (completion && test_ctxt.iter_complete); > + else > + completion = false; > + > + /* > + * Once all client threads have finished their iteration, reset > + * everything and allow the client thread to start their next > + * iteration > + */ > + if (completion) { > + test_ctxt.byte_sent = 0; > + __sync_synchronize(); > + test_ctxt.byte_recvd = 0; > + test_ctxt.iter_complete = false; > + test_ctxt.comp_complete = true; > + curr_ptr = 0; > + test_log(DBG_LVL_INFO, "CompareRxTx: TX and RX match\n"); > + } else { > + completion = true; > + } > + } > +} > + > +static void loopback_test_display_err_data(void) > +{ > + test_log(DBG_LVL_ERROR, "Wrote %lu bytes out of a %lu buff size\n", > + test_ctxt.byte_sent, test_ctxt.buff_size); > + test_log(DBG_LVL_ERROR, "Remaining bytes to write %lu\n", > + (test_ctxt.buff_size - test_ctxt.byte_sent)); > +} > + > +static enum test_status loopback_test_setup_write_packet(void) > +{ > + bool poll_timeout = true; > + > + if (loopback_test_poll(&write_fd, poll_timeout) == TEST_STATUS_ERROR) > + return TEST_STATUS_ERROR; > + > + test_log(DBG_LVL_VERBOSE, "revents: %d\n", write_fd.revents); > + > + if (write_fd.revents & POLLOUT) { > + memset(test_ctxt.tx_buff + test_ctxt.byte_sent, write_data, > + test_ctxt.buff_size); > + test_log(DBG_LVL_VERBOSE, "Write %02x of size %lu\n", write_data, > + test_ctxt.buff_size); > + > + if (loopback_test_write(&write_fd) != TEST_STATUS_SUCCESS) { > + loopback_test_sleep_ms(1000); > + return TEST_STATUS_ERROR; > + } > + > + test_log(DBG_LVL_INFO, "Total byte_sent %li\n", > + test_ctxt.byte_sent); > + write_fd.revents = 0; > + } > + > + return TEST_STATUS_SUCCESS; > +} > + > +static enum test_status loopback_test_wait_to_get_bytes_back(void) > +{ > + int time_count = 0; > + > + test_log(DBG_LVL_INFO, "Sent %li bytes, received %li bytes\n", > + test_ctxt.byte_sent, test_ctxt.byte_recvd); > + > + /* read thread will fill bytesRec and wait till it equals byte_sent */ > + while (test_ctxt.byte_recvd != test_ctxt.byte_sent) { > + if (loopback_test_time_out(&time_count) == TEST_STATUS_ERROR) > + return TEST_STATUS_ERROR; > + }
Same here.
> + > + return TEST_STATUS_SUCCESS; > +} Here too
> + > +static void *loopback_test_tx(void *data) > +{ > + int i; > + int time_count = 0; > + int failed_iterations = 0; > + enum test_status status = TEST_STATUS_SUCCESS; > + > + tx_status = TEST_STATUS_SUCCESS; > + > + for (i = 0; i < test_ctxt.iterations; i++) { > + loopback_test_reset_ctxt(); > + > + test_ctxt.iter_complete = false; > + > + /* While the bytes sent is less than the buffsize */ > + while (((unsigned int)test_ctxt.byte_sent < test_ctxt.buff_size) > + && (status == TEST_STATUS_SUCCESS)) { > + if (force_exit == true) > + goto exit; > + > + if (status != TEST_STATUS_SUCCESS) { > + loopback_test_display_err_data(); > + tx_status = TEST_STATUS_ERROR; > + goto exit; > + } > + > + status = loopback_test_setup_write_packet(); > + if (status == TEST_STATUS_ERROR) { > + test_log(DBG_LVL_INFO, > + "Failed after %d iterations\n", i); > + failed_iterations++; > + goto exit; > + } > + } > + > + if (loopback_test_wait_to_get_bytes_back() == > + TEST_STATUS_ERROR) { > + tx_status = TEST_STATUS_ERROR; > + goto exit; > + } > + > + __sync_synchronize(); > + > + test_ctxt.iter_complete = true; > + > + while (!test_ctxt.comp_complete) { > + if (loopback_test_time_out(&time_count) == > + TEST_STATUS_ERROR) { > + tx_status = TEST_STATUS_ERROR; > + goto exit; > + } > + } > + time_count = 0; > + } > + > + if (failed_iterations > 0) > + test_log(DBG_LVL_INFO, "Amount of Failed Iterations: %d\n", > + failed_iterations); > + > +exit: > + loopback_test_reset_ctxt(); > + > + /* all of the iterations for this client thread have been completed */ > + test_ctxt.test_complete = true; > + > + /* close the opened file descriptors */ > + loopback_test_close(&write_fd, &read_fd); > + > + test_log(DBG_LVL_INFO, "mhi_test_loopback: exiting thread\n"); > + > + pthread_exit(&tx_status); > + > + return NULL; > +} > + > +/* > + * This function will be called when the user wishes to perform a loopback test. > + * In this function, a compare thread will be created to compare the tx buffer > + * and the rx Buffer. Device file node is opened for write and read. Tx thread > + * is created to send data and Rx thread to receive data. > + */ > +enum test_status loopback_test_thread_create(void) > +{ > + int i = 0; > + enum test_status status = TEST_STATUS_SUCCESS; > + enum test_status *write_thread_retval; > + enum test_status *read_thread_retval; > + enum test_status *compare_thread_retval; > + > + pthread_t compare_rx_tx_thread; > + pthread_t loopback_thread; > + pthread_t receive_thread; > + > + /* set all tests (including open/close) to not completed */ > + test_ctxt.all_complete = false; > + > + /* set current test to not completed */ > + test_ctxt.test_complete = false; > + > + if (loopback_test_alloc_rx_tx_buff() == TEST_STATUS_ERROR) { > + status = TEST_STATUS_ERROR; > + goto exit; > + } > + > + /* Create thread for comparing the rx and tx buffers */ > + if (pthread_create(&compare_rx_tx_thread, NULL, > + loopback_test_compare_rxtx, NULL) != 0) { > + test_log(DBG_LVL_ERROR, "Error creating compare thread\n"); > + status = TEST_STATUS_ERROR; > + goto exit; > + } > + > + test_log(DBG_LVL_INFO, "dev_node: %s\n", test_ctxt.dev_node); > + > + /* closing the device node will happen in the loopback thread */ > + status = loopback_test_open(&write_fd, &read_fd, test_ctxt.dev_node); > + if (status == TEST_STATUS_ERROR) { > + test_log(DBG_LVL_ERROR, "Failed to open device node\n"); > + goto exit; > + } > + > + /* Spawn the loopback tx thread */ > + if (pthread_create(&loopback_thread, NULL, loopback_test_tx, NULL) > + != 0) { > + test_log(DBG_LVL_ERROR, "Error creating tx thread"); > + status = TEST_STATUS_ERROR; > + goto exit; > + } > + > + /* Create thread for receiving packets */ > + if (pthread_create(&receive_thread, NULL, loopback_test_rx, NULL) > + != 0) { > + test_log(DBG_LVL_ERROR, "Error creating rx thread\n"); > + status = TEST_STATUS_ERROR; > + goto exit; > + } > + > + test_log(DBG_LVL_INFO, "Waiting for Tx and Rx threads to complete\n"); > + > + /* wait till all client and receive threads have finished */ > + pthread_join(loopback_thread, (void **)(&write_thread_retval)); > + pthread_join(receive_thread, (void **)(&read_thread_retval)); > + > + test_log(DBG_LVL_VERBOSE, "TX and Rx threads completed\n"); > + > + test_log(DBG_LVL_INFO, "Write thread status: %s\n", > + *write_thread_retval ? "Fail" : "Pass"); > + test_log(DBG_LVL_INFO, "Read thread status: %s\n", > + *read_thread_retval ? "Fail" : "Pass"); > + > + if ((*write_thread_retval == TEST_STATUS_ERROR) || > + (*read_thread_retval == TEST_STATUS_ERROR)) { > + test_log(DBG_LVL_ERROR, "Returned error, exiting\n"); > + status = TEST_STATUS_ERROR; > + goto exit; > + } > + > + test_log(DBG_LVL_INFO, "Thread %i of tests complete\n", i); > + test_ctxt.test_complete = false; > + > + test_log(DBG_LVL_INFO, "All loopback threads completed\n"); > + > + test_ctxt.all_complete = true; > + > + test_log(DBG_LVL_INFO, "Waiting for comp thread to exit\n"); > + > + pthread_join(compare_rx_tx_thread, (void **)(&compare_thread_retval)); > + if (*compare_thread_retval == TEST_STATUS_ERROR) { > + test_log(DBG_LVL_ERROR, "Compare thread returned error\n"); > + status = TEST_STATUS_ERROR; > + } > + > +exit: > + /* All threads have finished using the Tx and Rx Buffers, free them */ > + free(test_ctxt.tx_buff); > + test_ctxt.tx_buff = NULL; > + free(test_ctxt.rx_buff); > + test_ctxt.rx_buff = NULL; > + > + return status; > +} > + > +static void *loopback_test_run(void *data) > +{ > + enum test_status status = TEST_STATUS_SUCCESS; > + > + force_exit = false; > + srand(time(NULL)); > + > + status = loopback_test_thread_create(); > + > + pthread_exit(&status); > + > + return NULL; > +} > + > +static void loopback_test_set_defaults(void) > +{ > + msg_lvl = DBG_LVL_INFO; > + > + memset(&test_ctxt, 0, sizeof(test_ctxt)); > + test_ctxt.iterations = 1; > + test_ctxt.buff_size = 1024; > +} > + > +static void usage(void) > +{ > + test_log(DBG_LVL_INFO, "\nUsage:\n"); > + test_log(DBG_LVL_INFO, "-c: Device node\n"); > + test_log(DBG_LVL_INFO, "-b: Buffer size (default: 1024 bytes)\n"); > + test_log(DBG_LVL_INFO, "-i: Number of iterations\n"); > + test_log(DBG_LVL_INFO, "-l: log level, default - 1\n"); > + test_log(DBG_LVL_INFO, "log level : 0 -verbose, 1 -info , 2 -err\n"); > +} > + > +static int loopback_test_parse(int argc, char *argv[]) > +{ > + int i = 0; > + int command = 0; > + int indexptr = 0; > + char *optstr = "-:c:b:i:l:"; > + int *return_value = NULL; > + int status = -EINVAL; > + pthread_t run_test; > + > + optind = 1; > + > + static const struct option optlongstr[] = { > + { "device node", required_argument, NULL, 'c'}, > + {0, 0, 0, 0} > + }; > + > + test_log(DBG_LVL_INFO, "\nStarting MHI loopback tests\n"); > + > + test_log(DBG_LVL_VERBOSE, "argc: %d\n", argc); > + for (i = 0; i < argc; i++) > + test_log(DBG_LVL_VERBOSE, "argv[%i] = %s; ", i, argv[i]); > + > + while ((command = getopt_long(argc, (char **)argv, optstr, optlongstr, > + &indexptr)) && (command != -1)) { > + switch (command) { > + case 'c': > + sprintf(test_ctxt.dev_node, "%s", optarg); > + test_log(DBG_LVL_INFO, "%s\n", test_ctxt.dev_node); > + break; > + case 'b': > + test_ctxt.buff_size = atoi(optarg); > + test_log(DBG_LVL_INFO, "%lu\n", test_ctxt.buff_size); > + break; > + case 'i': > + test_ctxt.iterations = atoi(optarg); > + test_log(DBG_LVL_INFO, "%u\n", test_ctxt.iterations); > + break; > + case 'l': > + msg_lvl = atoi(optarg); > + test_log(DBG_LVL_INFO, "log level: %d\n", atoi(optarg)); > + break; > + default: > + test_log(DBG_LVL_ERROR, "Invalid Option: %d\n", > + command); > + usage(); > + return TEST_STATUS_ERROR; > + } > + } > + > + if (pthread_create(&run_test, NULL, loopback_test_run, NULL) != 0) { > + test_log(DBG_LVL_ERROR, "Error creating run_mhi_test\n"); > + } else { > + pthread_join(run_test, (void **)(&return_value)); > + > + if (*return_value != TEST_STATUS_SUCCESS) { > + test_log(DBG_LVL_ERROR, "Test Failed\n"); > + } else { > + test_log(DBG_LVL_INFO, "Test Passed\n"); > + status = 0; > + } > + } > + > + return status; > +} > + > +int main(int argc, char *argv[]) > +{ > + int ret = 0; > + > + loopback_test_set_defaults(); > + test_log(DBG_LVL_INFO, "MHI Loopback test App\n"); > + > + if (argc > 1) > + ret = loopback_test_parse(argc, argv); > + else > + usage(); > + > + return ret; > +} >
Are there any cases where this test can't run and have to - those cases need to be skips.
thanks, -- Shuah
| |