diff --git a/eloop.c b/eloop.c index 024b0ba..a19f6a1 100644 --- a/eloop.c +++ b/eloop.c @@ -20,7 +20,11 @@ #include #include #include +#ifdef CONFIG_ZEPHYR +#include +#else #include "sys/select.h" +#endif #ifdef CONFIG_NATIVE_WINDOWS #include "common.h" @@ -235,7 +239,7 @@ int qt_eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), } -#ifndef CONFIG_NATIVE_WINDOWS +#if !defined(CONFIG_NATIVE_WINDOWS) && !defined(CONFIG_ZEPHYR) static void eloop_handle_alarm(int sig) { (void) sig; @@ -247,9 +251,10 @@ static void eloop_handle_alarm(int sig) vendor_deinit(); exit(1); } -#endif /* CONFIG_NATIVE_WINDOWS */ +#endif /* CONFIG_NATIVE_WINDOWS && CONFIG_ZEPHYR*/ +#ifndef CONFIG_ZEPHYR static void eloop_handle_signal(int sig) { int i; @@ -324,6 +329,7 @@ int qt_eloop_register_signal(int sig, return 0; } +#endif void qt_eloop_run(void) { @@ -361,7 +367,9 @@ void qt_eloop_run(void) free(rfds); return; } +#ifndef CONFIG_ZEPHYR eloop_process_pending_signals(); +#endif /* check if some registered timeouts have occurred */ if (eloop.timeout) { diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 1a11ec3..9a376fc 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -26,6 +26,7 @@ zephyr_include_directories_ifdef(CONFIG_WFA_QT_CONTROL_APP zephyr_library_sources_ifdef(CONFIG_WFA_QT_CONTROL_APP # Zephyr's port of the Indigo API + ${SOURCES_BASE}/zephyr/src/zephyr_dut_main.c ${SOURCES_BASE}/zephyr/src/indigo_api_callback_dut.c ${SOURCES_BASE}/zephyr/src/vendor_specific_dut.c ${SOURCES_BASE}/zephyr/src/utils.c diff --git a/zephyr/Kconfig b/zephyr/Kconfig index bcd53ec..f2a9110 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -9,3 +9,9 @@ config WFA_QT_CONTROL_APP depends on !POSIX_API select PTHREAD_IPC bool "WFA Quicktrack control app" + +config WFA_QT_THREAD_STACK_SIZE + int "Thread stack size" + default 4096 + help + Thread stack size. diff --git a/zephyr/src/zephyr_dut_main.c b/zephyr/src/zephyr_dut_main.c new file mode 100755 index 0000000..b32c603 --- /dev/null +++ b/zephyr/src/zephyr_dut_main.c @@ -0,0 +1,245 @@ +/* Copyright (c) 2020 Wi-Fi Alliance */ + +/* Permission to use, copy, modify, and/or distribute this software for any */ +/* purpose with or without fee is hereby granted, provided that the above */ +/* copyright notice and this permission notice appear in all copies. */ + +/* THE SOFTWARE IS PROVIDED 'AS IS' AND THE AUTHOR DISCLAIMS ALL */ +/* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED */ +/* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL */ +/* THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR */ +/* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING */ +/* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF */ +/* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT */ +/* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS */ +/* SOFTWARE. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vendor_specific.h" +#include "eloop.h" +#include "indigo_api.h" +#include "utils.h" + +#include + +void qt_main(void); +K_THREAD_DEFINE(qt_main_tid, + CONFIG_WFA_QT_THREAD_STACK_SIZE, + qt_main, + NULL, + NULL, + NULL, + 0, + 0, + 0); + +static pthread_t main_thread; +#define STACK_SIZE 4096 +K_THREAD_STACK_DEFINE(main_thread_stack, STACK_SIZE); + +unsigned char *cmd_rcv_buf = NULL; /*Buffer to receive the message*/ + +/* Internal functions */ +static void control_receive_message(int sock, void *eloop_ctx, void *sock_ctx); + +/* Initiate the service port. */ +static int control_socket_init(int port) { + int s = -1; + struct sockaddr_in addr; + + /* Open UDP socket */ + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + indigo_logger(LOG_LEVEL_ERROR, "Failed to open server socket: %s", strerror(errno)); + return -1; + } + + /* Bind specific port */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + indigo_logger(LOG_LEVEL_ERROR, "Failed to bind server socket: %s", strerror(errno)); + close(s); + return -1; + } + + /* Register to eloop and ready for the socket event */ + if (qt_eloop_register_read_sock(s, control_receive_message, NULL, NULL)) { + indigo_logger(LOG_LEVEL_ERROR, "Failed to initiate ControlAppC"); + close(s); + return -1; + } + return s; +} + +void *main_thread_handler() { + int service_socket = -1; + + /* Bind the service port and register to eloop */ + service_socket = control_socket_init(get_service_port()); + if (service_socket >= 0) { + qt_eloop_run(); + } else { + indigo_logger(LOG_LEVEL_INFO, "Failed to initiate the UDP socket"); + } + + /* Stop eloop */ + qt_eloop_destroy(); + indigo_logger(LOG_LEVEL_INFO, "ControlAppC stops"); + if (service_socket >= 0) { + indigo_logger(LOG_LEVEL_INFO, "Close service port: %d", get_service_port()); + close(service_socket); + free(cmd_rcv_buf); + } + + return 0; +} + +struct sockaddr_in *tool_addr; // For HTTP Post +/* Callback function of the QuickTrack API. */ +static void control_receive_message(int sock, void *eloop_ctx, void *sock_ctx) { + int ret; // return code + int fromlen, len; // structure size and received length + struct sockaddr_storage from; // source address of the message + struct packet_wrapper req, resp; // packet wrapper for the received message and response + struct indigo_api *api = NULL; // used for API search, validation and handler call + + (void) eloop_ctx; + (void) sock_ctx; + + memset(cmd_rcv_buf, '\0', BUFFER_LEN); + /* Receive request */ + fromlen = sizeof(from); + len = recvfrom(sock, cmd_rcv_buf, BUFFER_LEN, 0, (struct sockaddr *) &from, (socklen_t*)&fromlen); + if (len < 0) { + indigo_logger(LOG_LEVEL_ERROR, "Server: Failed to receive the packet"); + return ; + } else { + indigo_logger(LOG_LEVEL_DEBUG, "Server: Receive the packet"); + } + tool_addr = (struct sockaddr_in *)&from; + + /* Parse request to HDR and TLV. Response NACK if parser fails. Otherwises, ACK. */ + memset(&req, 0, sizeof(struct packet_wrapper)); + memset(&resp, 0, sizeof(struct packet_wrapper)); + ret = parse_packet(&req, cmd_rcv_buf, len); + if (ret == 0) { + indigo_logger(LOG_LEVEL_DEBUG, "Server: Parsed packet successfully"); + } else { + indigo_logger(LOG_LEVEL_ERROR, "Server: Failed to parse the packet"); + fill_wrapper_ack(&resp, req.hdr.seq, 0x31, "Unable to parse the packet"); + len = assemble_packet(cmd_rcv_buf, BUFFER_LEN, &resp); + + sendto(sock, (const char *)cmd_rcv_buf, len, MSG_CONFIRM, (const struct sockaddr *) &from, fromlen); + goto done; + } + + /* Find API by ID. If API is not supported, assemble NACK. */ + api = get_api_by_id(req.hdr.type); + if (api) { + indigo_logger(LOG_LEVEL_DEBUG, "API %s: Found handler", api->name); + } else { + indigo_logger(LOG_LEVEL_ERROR, "API Unknown (0x%04x): No registered handler", req.hdr.type); + fill_wrapper_ack(&resp, req.hdr.seq, 0x31, "Unable to find the API handler"); + len = assemble_packet(cmd_rcv_buf, BUFFER_LEN, &resp); + sendto(sock, (const char *)cmd_rcv_buf, len, MSG_CONFIRM, (const struct sockaddr *) &from, fromlen); + goto done; + } + + /* Verify. Optional. If validation is failed, then return NACK. */ + if (api->verify == NULL || (api->verify && api->verify(&req, &resp) == 0)) { + indigo_logger(LOG_LEVEL_INFO, "API %s: Return ACK", api->name); + fill_wrapper_ack(&resp, req.hdr.seq, 0x30, "ACK: Command received"); + len = assemble_packet(cmd_rcv_buf, BUFFER_LEN, &resp); + sendto(sock, (const char *)cmd_rcv_buf, len, MSG_CONFIRM, (const struct sockaddr *) &from, fromlen); + free_packet_wrapper(&resp); + } else { + indigo_logger(LOG_LEVEL_ERROR, "API %s: Failed to verify and return NACK", api->name); + fill_wrapper_ack(&resp, req.hdr.seq, 1, "Unable to find the API handler"); + len = assemble_packet(cmd_rcv_buf, BUFFER_LEN, &resp); + sendto(sock, (const char *)cmd_rcv_buf, len, MSG_CONFIRM, (const struct sockaddr *) &from, fromlen); + goto done; + } + + /* Optional, use timer to handle the execution */ + /* Handle & Response. Call API handle(), assemble packet by response wrapper and send back to source address. */ + if (api->handle && api->handle(&req, &resp) == 0) { + indigo_logger(LOG_LEVEL_INFO, "API %s: Return execution result", api->name); + len = assemble_packet(cmd_rcv_buf, BUFFER_LEN, &resp); + sendto(sock, (const char *)cmd_rcv_buf, len, MSG_CONFIRM, (const struct sockaddr *) &from, fromlen); + } else { + indigo_logger(LOG_LEVEL_DEBUG, "API %s (0x%04x): No handle function", api ? api->name : "Unknown", req.hdr.type); + } + +done: + /* Clean up resource */ + free_packet_wrapper(&req); + free_packet_wrapper(&resp); + indigo_logger(LOG_LEVEL_DEBUG, "API %s: Complete", api ? api->name : "Unknown"); +} + +/* Show the welcome message with role and version */ +static void print_welcome() { + printf("Welcome to use QuickTrack Control App DUT version %s \n", TLV_VALUE_APP_VERSION); +} + +void qt_main(void) { + int ret =0; + + pthread_attr_t ptAttr; + struct sched_param ptSchedParam; + int ptPolicy; + + /* Welcome message */ + print_welcome(); + + /* Print the run-time information */ + indigo_logger(LOG_LEVEL_INFO, "QuickTrack control app running at: %d", get_service_port()); + indigo_logger(LOG_LEVEL_INFO, "Wireless Interface: %s", WIRELESS_INTERFACE_DEFAULT); + + /* Register the callback */ + register_apis(); + + /* Intiate the vendor's specific startup commands */ + vendor_init(); + + /* Start eloop */ + qt_eloop_init(NULL); + + /* Allocate memory to receive the control message*/ + cmd_rcv_buf = malloc(BUFFER_LEN); + if(!cmd_rcv_buf) { + indigo_logger(LOG_LEVEL_ERROR, "Malloc Failure for receive buffer"); + return; + } + + ret = pthread_attr_init(&ptAttr); + if (ret != 0) { + printf("%s:%d ret: %d\n", __func__, __LINE__, ret); + return; + } + + ptSchedParam.sched_priority = 10; + pthread_attr_setschedparam(&ptAttr, &ptSchedParam); + pthread_attr_getschedpolicy(&ptAttr, &ptPolicy); + pthread_attr_setschedpolicy(&ptAttr, SCHED_RR); + pthread_attr_getschedpolicy(&ptAttr, &ptPolicy); + + ret = pthread_attr_setstack(&ptAttr, &main_thread_stack, 4096); + if (ret != 0) { + printf("%s:%d ret: %d\n", __func__, __LINE__, ret); + return; + } + + pthread_create(&main_thread, &ptAttr, main_thread_handler, NULL); +}