From ec562377e608d0cde8993d76af06094969b3a067 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Gupta Date: Wed, 27 Nov 2024 17:00:10 +0530 Subject: [PATCH] framework/src/csifw: Add CSI framework and driver framework/src/csifw: CSIFW source code framework/include/csifw: CSIFW headers os/drivers/wifi_csi: Add driver source code os/board: Enable driver register at bootup os/include/tinyara/wifi_csi/wifi_csi_struct.h: Define commmon wifi csi kernel constants and structures. --- apps/examples/csifw_test/Kconfig | 12 + apps/examples/csifw_test/Kconfig_ENTRY | 4 + apps/examples/csifw_test/Make.defs | 22 + apps/examples/csifw_test/Makefile | 164 +++++ apps/examples/csifw_test/csifw_test.c | 240 ++++++++ apps/examples/csifw_test/csifw_test.h | 38 ++ .../rtl8730e/loadable_ext_psram/defconfig | 29 + framework/include/csifw/csifw_api.h | 117 ++++ framework/include/csifw/readme.md | 103 ++++ framework/src/csifw/CSINetworkMonitor.c | 116 ++++ framework/src/csifw/CSIPacketReceiver.c | 325 ++++++++++ framework/src/csifw/CSIParser.c | 125 ++++ framework/src/csifw/CSIService.c | 505 ++++++++++++++++ framework/src/csifw/Kconfig | 53 ++ framework/src/csifw/Make.defs | 27 + framework/src/csifw/PingGenerator.c | 296 +++++++++ .../src/csifw/include/CSINetworkMonitor.h | 26 + .../src/csifw/include/CSIPacketReceiver.h | 32 + framework/src/csifw/include/CSIParser.h | 40 ++ framework/src/csifw/include/PingGenerator.h | 34 ++ framework/src/csifw/include/csifw.h | 52 ++ framework/src/csifw/include/csifw_log.h | 65 ++ os/Kconfig | 4 + os/Kconfig.debug | 32 + os/board/rtl8730e/src/Make.defs | 5 + .../src/component/os/tizenrt/Make.defs | 4 + .../src/component/os/tizenrt/rtk_wifi_csi.c | 294 +++++++++ os/board/rtl8730e/src/rtl8730e_boot.c | 7 + os/board/rtl8730e/src/rtl8730e_rtk_csi.c | 96 +++ os/drivers/Kconfig | 2 + os/drivers/Makefile | 1 + os/drivers/wifi_csi/Kconfig | 35 ++ os/drivers/wifi_csi/Make.defs | 65 ++ os/drivers/wifi_csi/wifi_csi.c | 562 ++++++++++++++++++ os/include/debug.h | 36 ++ os/include/tinyara/fs/ioctl.h | 7 + os/include/tinyara/wifi_csi/wifi_csi.h | 278 +++++++++ os/include/tinyara/wifi_csi/wifi_csi_struct.h | 84 +++ 38 files changed, 3937 insertions(+) create mode 100644 apps/examples/csifw_test/Kconfig create mode 100644 apps/examples/csifw_test/Kconfig_ENTRY create mode 100644 apps/examples/csifw_test/Make.defs create mode 100644 apps/examples/csifw_test/Makefile create mode 100644 apps/examples/csifw_test/csifw_test.c create mode 100644 apps/examples/csifw_test/csifw_test.h create mode 100644 framework/include/csifw/csifw_api.h create mode 100644 framework/include/csifw/readme.md create mode 100644 framework/src/csifw/CSINetworkMonitor.c create mode 100644 framework/src/csifw/CSIPacketReceiver.c create mode 100644 framework/src/csifw/CSIParser.c create mode 100644 framework/src/csifw/CSIService.c create mode 100644 framework/src/csifw/Kconfig create mode 100644 framework/src/csifw/Make.defs create mode 100644 framework/src/csifw/PingGenerator.c create mode 100644 framework/src/csifw/include/CSINetworkMonitor.h create mode 100644 framework/src/csifw/include/CSIPacketReceiver.h create mode 100644 framework/src/csifw/include/CSIParser.h create mode 100644 framework/src/csifw/include/PingGenerator.h create mode 100644 framework/src/csifw/include/csifw.h create mode 100644 framework/src/csifw/include/csifw_log.h create mode 100644 os/board/rtl8730e/src/component/os/tizenrt/rtk_wifi_csi.c create mode 100644 os/board/rtl8730e/src/rtl8730e_rtk_csi.c create mode 100644 os/drivers/wifi_csi/Kconfig create mode 100644 os/drivers/wifi_csi/Make.defs create mode 100644 os/drivers/wifi_csi/wifi_csi.c create mode 100644 os/include/tinyara/wifi_csi/wifi_csi.h create mode 100644 os/include/tinyara/wifi_csi/wifi_csi_struct.h diff --git a/apps/examples/csifw_test/Kconfig b/apps/examples/csifw_test/Kconfig new file mode 100644 index 0000000000..13d1708267 --- /dev/null +++ b/apps/examples/csifw_test/Kconfig @@ -0,0 +1,12 @@ +# +# For a description of the syntax of this configuration file, +# see kconfig-language at https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt +# + +config EXAMPLES_CSIFW_TEST + bool "CSIFW test application" + default n + depends on CSIFW + ---help--- + CSIFW test application + diff --git a/apps/examples/csifw_test/Kconfig_ENTRY b/apps/examples/csifw_test/Kconfig_ENTRY new file mode 100644 index 0000000000..0ce4b83ad8 --- /dev/null +++ b/apps/examples/csifw_test/Kconfig_ENTRY @@ -0,0 +1,4 @@ +config ENTRY_CSIFW_TEST + bool "CSI Framework example" + depends on EXAMPLES_CSIFW_TEST + diff --git a/apps/examples/csifw_test/Make.defs b/apps/examples/csifw_test/Make.defs new file mode 100644 index 0000000000..eb872d2307 --- /dev/null +++ b/apps/examples/csifw_test/Make.defs @@ -0,0 +1,22 @@ +########################################################################### +# +# Copyright 2024 Samsung Electronics All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +# +########################################################################### + +ifeq ($(CONFIG_EXAMPLES_CSIFW_TEST),y) +CONFIGURED_APPS += examples/csifw_test +endif + diff --git a/apps/examples/csifw_test/Makefile b/apps/examples/csifw_test/Makefile new file mode 100644 index 0000000000..17af9c481d --- /dev/null +++ b/apps/examples/csifw_test/Makefile @@ -0,0 +1,164 @@ +########################################################################### +# +# Copyright 2024 Samsung Electronics All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +# +########################################################################### +############################################################################ +# apps/examples/csifw_test/Makefile +# +# Copyright (C) 2009-2012 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ +-include $(TOPDIR)/.config +-include $(TOPDIR)/Make.defs +include $(APPDIR)/Make.defs + +CXXEXT ?= .cpp +# C++ Test Example + +APPNAME = csifw_test +FUNCNAME = $(APPNAME)_main +THREADEXEC = TASH_EXECMD_ASYNC + +# C++ Test Example +MAINSRC = csifw_test.c + +AOBJS = $(ASRCS:.S=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) +MAINOBJ = $(MAINSRC:.c=$(OBJEXT)) + +SRCS = $(ASRCS) $(CSRCS) $(MAINSRC) +OBJS = $(AOBJS) $(COBJS) + +ifneq ($(CONFIG_BUILD_KERNEL),y) +OBJS += $(MAINOBJ) +endif + +ifeq ($(CONFIG_WINDOWS_NATIVE),y) +BIN = $(APPDIR)\libapps$(LIBEXT) +else +ifeq ($(WINTOOL),y) + BIN = $(APPDIR)\\libapps$(LIBEXT) +else + BIN = $(APPDIR)/libapps$(LIBEXT) +endif +endif + +CONFIG_EXAMPLES_CSIFW_TEST_PROGNAME ?= csifw_test$(EXEEXT) +PROGNAME = $(CONFIG_EXAMPLES_CSIFW_TEST_PROGNAME) + +ROOTDEPPATH = --dep-path . + +# Common build + +VPATH = + +all: .built +.PHONY: clean depend distclean + +$(AOBJS): %$(OBJEXT): %.S + $(call ASSEMBLE, $<, $@) + +$(COBJS): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +$(CXXOBJS): %$(OBJEXT): %$(CXXEXT) + $(call COMPILEXX, $<, $@) + +ifeq ($(suffix $(MAINSRC)),$(CXXEXT)) +$(MAINOBJ): %$(OBJEXT): %$(CXXEXT) + $(call COMPILEXX, $<, $@) +else +$(MAINOBJ): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) +endif + +.built: $(OBJS) + $(call ARCHIVE, $(BIN), $(OBJS)) + @touch .built + +ifeq ($(CONFIG_BUILD_KERNEL),y) +$(BIN_DIR)$(DELIM)$(PROGNAME): $(OBJS) $(MAINOBJ) + @echo "LD: $(PROGNAME)" + $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $(INSTALL_DIR)$(DELIM)$(PROGNAME) $(ARCHCRT0OBJ) $(MAINOBJ) $(LDLIBS) + $(Q) $(NM) -u $(INSTALL_DIR)$(DELIM)$(PROGNAME) + +install: $(BIN_DIR)$(DELIM)$(PROGNAME) + +else +install: + +endif + +ifeq ($(CONFIG_BUILTIN_APPS)$(CONFIG_EXAMPLES_CSIFW_TEST),yy) +$(BUILTIN_REGISTRY)$(DELIM)$(FUNCNAME).bdat: $(DEPCONFIG) Makefile + $(Q) $(call REGISTER,$(APPNAME),$(FUNCNAME),$(THREADEXEC),$(PRIORITY),$(STACKSIZE)) + +context: $(BUILTIN_REGISTRY)$(DELIM)$(FUNCNAME).bdat + +else +context: + +endif + +.depend: Makefile $(SRCS) +ifeq ($(filter %$(CXXEXT),$(SRCS)),) + @$(MKDEP) $(ROOTDEPPATH) "$(CC)" -- $(CFLAGS) -- $(SRCS) >Make.dep +else + @$(MKDEP) $(ROOTDEPPATH) "$(CXX)" -- $(CXXFLAGS) -- $(SRCS) >Make.dep +endif + @touch $@ + +depend: .depend + +clean: + $(call DELFILE, .built) + $(call CLEAN) + +distclean: clean + $(call DELFILE, Make.dep) + $(call DELFILE, .depend) + +-include Make.dep +.PHONY: preconfig +preconfig: diff --git a/apps/examples/csifw_test/csifw_test.c b/apps/examples/csifw_test/csifw_test.c new file mode 100644 index 0000000000..1c0ab269e5 --- /dev/null +++ b/apps/examples/csifw_test/csifw_test.c @@ -0,0 +1,240 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#include "csifw_test.h" +#include "csifw/csifw_api.h" +#include +#include + +/**************************************************************************** + * csifw_test + ****************************************************************************/ + +#include +static int gFd; /* File descriptor*/ + +#define PRNT_LN1 printf("______________________________________________________________________________________________________\n") +#define PRNT_LN2 printf("______________________________________________________________________________________________________\n\n") +#define PRNT_LN3 printf("\n\n______________________________________________________________________________________________________\n") + +#define LONG_RUN_TEST 0 +#define PRINT_RAW_DATA 0 +#define LOG_PARSED_DATA 0 + +typedef enum CSI_DATA_TYPE { + CSI_DATA_UNKNOWN = -1, + RAW_CSI_DATA =0, + PARSED_CSI_DATA =1, + RAW_PARSED_CSI_DATA =2, +}CSI_DATA_TYPE; + +static CSI_DATA_TYPE g_csi_data_type; +static CSI_CONFIG_TYPE g_csi_config_type; +static int g_interval; +static unsigned int gCounter; +static unsigned int g_app_id[3] = {1,2,3}; +static unsigned int g_app_number = 0; + +static void print_buf(const unsigned char* buf, int len) +{ + printf("\n RAW DATA %d\n\n", len); + unsigned long long *buff_tmp = (u64 *)buf; + int buff_len = (len / 8) + 1; + for (int i = 0; i < buff_len; i++) + printf("[%02d]0x%016llx\n", i, buff_tmp[i]); +} + +static void demo_upd_raw_data_listener(CSIFW_RES res, int csi_buff_len, unsigned char *csi_raw_buff, void* ptr) +{ + if(res == CSIFW_OK) { + gCounter++; + unsigned int app_id = *(unsigned int*)ptr; + printf("\n%d. Raw Data received in APP[%d] [%d]\n", gCounter, app_id, csi_buff_len); + #if PRINT_RAW_DATA + printf("\n[APP]LOGGING DATA\n"); + print_buf(csi_raw_buff, csi_buff_len); + #endif + return; + } + if(res == CSIFW_ERROR_WIFI_DIS_CONNECTED) { + printf("[APP] wifi disconnected\n"); + printf("[APP] so service will PAUSE\n"); + return; + } + if (res == CSIFW_ERROR) { + printf("[APP] Some error\n"); + return; + } +} + +static void demo_upd_parsed_data_listener(CSIFW_RES res, int csi_data_len, float *csi_parsed_buff, void* ptr) +{ + // print parsed data buffer + printf("\n%d. Parsed Data received in APP [%d]\n", gCounter, csi_data_len); + + #if LOG_PARSED_DATA + printf("[APP] Displaying parsed data, csi_data_len: %d\n\n", csi_data_len); + for(int i = 0; i < csi_data_len; i += 2) { + printf("sub_carrier:%d[%f,%f], ", i/2, csi_parsed_buff[i], csi_parsed_buff[i+1]); + } + printf("\n"); + #endif + printf("\n\n"); +} + +static int start_test(void) +{ + printf("\n[APP]: CSI Manager DEFAULT FLOW \n"); + PRNT_LN1; + PRNT_LN2; + + printf("[APP]: csi_service_init\n"); + PRNT_LN2; + + CSIFW_RES res = csi_service_init(g_csi_config_type, demo_upd_raw_data_listener, NULL, g_interval, &g_app_id[g_app_number]); + if (res != CSIFW_OK) { + PRNT_LN2; + if (res == CSIFW_ERROR_WIFI_NOT_CONNECTED) { + printf("\n[APP]: CSIFW-> {WIFI NOT CONNECTED}\n"); + } else { + printf("\n[APP]: CSI Manager INIT FAIL\n"); + return 0; + } + } + g_app_number++; + PRNT_LN2; + printf("\n[APP]: CSI Manager INIT SUCCESS\n"); + printf("[APP]: SLEEP--> 5 seconds\n"); + PRNT_LN2; + // sleep(5); + + PRNT_LN3; + printf("[APP]: 1 csi_service_start\n"); + PRNT_LN2; + // sleep(2); + if (csi_service_start() == CSIFW_ERROR) { + PRNT_LN2; + printf("[APP]: CSI Manager START FAIL\n"); + return 0; + } + printf("[APP]: CSI Manager START SUCCESS\n"); + printf("[APP]: SLEEP--> 15 seconds\n"); + PRNT_LN2; +#if LONG_RUN_TEST + while (1) { + sleep(2); + } +#else + sleep(15); + + PRNT_LN3; + printf("[APP]: csi_service_stop (CSIFW_WIFI_DISCONNECTED)\n"); + PRNT_LN2; + // sleep(2); + CSIFW_REASON reason = CSIFW_NORMAL; + + if (csi_service_stop(CSIFW_WIFI_DISCONNECTED)== CSIFW_ERROR) { + printf("[APP]: CSI Manager STOP FAIL\n"); + PRNT_LN2; + return 0; + } + printf("[APP]: CSI Manager STOP SUCCESS\n"); + printf("[APP]: SLEEP--> 10 seconds\n"); + PRNT_LN2; + sleep(10); + + PRNT_LN3; + printf("[APP]: 2 csi_service_start\n"); + PRNT_LN2; + // sleep(2); + if (csi_service_start() == CSIFW_ERROR) { + printf("[APP]: CSI Manager START FAIL\n"); + PRNT_LN2; + return 0; + } + printf("[APP]: SLEEP--> 20 seconds\n"); + PRNT_LN2; + sleep(20); + + PRNT_LN3; + printf("[APP]: csi_service_stop (CSIFW_NORMAL)\n"); + PRNT_LN2; + // sleep(2); + if (csi_service_stop(CSIFW_NORMAL) == CSIFW_ERROR) { + printf("[APP]: CSI Manager STOP FAIL\n"); + PRNT_LN2; + return 0; + } + printf("[APP]: SLEEP--> 10 seconds\n"); + PRNT_LN2; + sleep(10); + + PRNT_LN3; + printf("[APP]: csi_service_deinit\n"); + PRNT_LN2; + // sleep(2); + if (csi_service_deinit() == CSIFW_ERROR) { + printf("[APP]: CSI Manager DEINIT FAIL\n"); + PRNT_LN2; + return 0; + } + g_app_number--; + printf("[APP]: SLEEP--> 20 seconds\n"); + PRNT_LN2; + // sleep(20); + PRNT_LN2; + printf("[APP]: DEFAULT FLOW ENDSSS\n"); + printf("[APP]: DEFAULT FLOW ENDSSS\n"); +#endif /* LONG_RUN_TEST */ + return 0; +} + +void csifw_test_main(int argc, char **args) +{ + /* default values */ + g_csi_data_type = RAW_CSI_DATA; + g_csi_config_type = HT_CSI_DATA; + g_interval = 40; // interval in ms + printf("WIFI CSI %d\n", argc); + + if(argc > 1) + { + g_interval = atoi(args[1]); + printf("PING_INTERVAL SET: %d us\n", g_interval); + + if(argc == 3) + { + g_csi_config_type = atoi(args[2]); + printf("CSI_CONFIG_TYPE SET: %d \n", g_csi_config_type); + } + else + { + printf("CSI_CONFIG_TYPE default : %d \n", g_csi_config_type); + } + if (g_csi_config_type != HT_CSI_DATA && g_csi_config_type != NON_HT_CSI_DATA) { + printf("Invalid CSI type, try again [0: HT_CSI_DATA, 1: NON_HT_CSI_DATA]\n"); + return; + } + } + else + { + printf("PING_INTERVAL default: %d us\n", g_interval); + printf("CSI_CONFIG_TYPE default : %d \n", g_csi_config_type); + } + start_test(); +} diff --git a/apps/examples/csifw_test/csifw_test.h b/apps/examples/csifw_test/csifw_test.h new file mode 100644 index 0000000000..7f89a0c3ee --- /dev/null +++ b/apps/examples/csifw_test/csifw_test.h @@ -0,0 +1,38 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +/** + * @file csifw_test.h + * @brief Sample application to show case usage of CSI Framework basic functionality. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief: It starts csifw test application. +*/ +void csifw_test_main(int argc, char **args); + +#ifdef __cplusplus +} +#endif + diff --git a/build/configs/rtl8730e/loadable_ext_psram/defconfig b/build/configs/rtl8730e/loadable_ext_psram/defconfig index cab47b9240..771eb017ef 100644 --- a/build/configs/rtl8730e/loadable_ext_psram/defconfig +++ b/build/configs/rtl8730e/loadable_ext_psram/defconfig @@ -557,6 +557,17 @@ CONFIG_AUDIO_ALC1019=y # CONFIG_AUDIO_TAS5749 is not set # CONFIG_DRIVERS_VIDEO is not set +# +# CSI Driver Support +# +CONFIG_WIFI_CSI=y +CONFIG_WIFICSI_CUSTOM_DEV_PATH="/dev/wificsi" + +# +# CSI Devices +# +CONFIG_WIFI_CSI_RTL8730E=y + # # LCD Driver Support # @@ -1187,11 +1198,26 @@ CONFIG_JEDEC_SPI_FREQUENCY=48000000 # # CONFIG_AIFW is not set + # # AraUI Framework # # CONFIG_UI is not set +# +# CSI Framework +# +CONFIG_CSIFW=y + +# +# CSIFW Debug Logs +# +CONFIG_CSIFW_LOGS=y +CONFIG_CSIFW_LOGE=y +# CONFIG_CSIFW_LOGI is not set +# CONFIG_CSIFW_LOGD is not set +# CONFIG_CSIFW_LOGV is not set + # # Memory Management # @@ -1272,6 +1298,8 @@ CONFIG_DEBUG_PM=y # CONFIG_DEBUG_SCHED is not set # CONFIG_DEBUG_SYSCALL is not set # CONFIG_DEBUG_TASH is not set +CONFIG_DEBUG_WIFICSI=y +CONFIG_DEBUG_WIFICSI_ERROR=y # # Framework Debug Options @@ -1524,6 +1552,7 @@ CONFIG_BUILTIN_APPS=y # CONFIG_EXAMPLES_BLE_PERFS is not set # CONFIG_EXAMPLES_BLE_RMC is not set # CONFIG_EXAMPLES_BLE_TESTER is not set +# CONFIG_EXAMPLES_CSIFW_TEST is not set # # Board Specific Demos diff --git a/framework/include/csifw/csifw_api.h b/framework/include/csifw/csifw_api.h new file mode 100644 index 0000000000..ba64003f22 --- /dev/null +++ b/framework/include/csifw/csifw_api.h @@ -0,0 +1,117 @@ +#ifndef __CSIFW_API_H__ +#define __CSIFW_API_H__ + +#ifdef __cplusplus /* If this is a C++ compiler, use C linkage */ + extern "C" { +#endif + +#include +#include + + +typedef enum { + CSIFW_ERROR_CLIENT_NOT_REGISTERED = -8, /* Client not registered. Cannot start/stop/deinit */ + CSIFW_ERROR_WIFI_NOT_CONNECTED = -7, /* WIFI not connected */ + CSIFW_ERROR_WIFI_DIS_CONNECTED = -6, /* WIFI WIFI_DISCONNECTED */ + CSIFW_INVALID_RAWDATA = -5, /* Invalid Raw Data */ + CSIFW_INVALID_ARG = -4, /* Invalid argument */ + CSIFW_NOT_ENOUGH_SPACE = -3, /* read/write/other buffer has empty space less than required size */ + CSIFW_NO_MEM = -2, /* Memory allocation (malloc/calloc) failed */ + CSIFW_ERROR = -1, /* ERROR: All other types of error not specified by any following enum */ + CSIFW_OK = 0 /* OK: Without any error */ +} CSIFW_RES; + +typedef enum { + CSIFW_WIFI_DISCONNECTED = -1, + CSIFW_NORMAL = 0 +} CSIFW_REASON; + +typedef enum CSI_CONFIG_TYPE { + HT_CSI_DATA = 0, /* 56 subcarriers each sub-carrier data is 2 bytes */ + NON_HT_CSI_DATA = 1, /* 52 subcarriers each sub-carrier data is 2 bytes */ + HT_CSI_DATA_ACC1 = 2, /* 56 subcarriers each sub-carrier data is 4 bytes */ + NON_HT_CSI_DATA_ACC1 = 3, /* 52 subcarriers each sub-carrier data is 4 bytes */ +}CSI_CONFIG_TYPE; + +/** + * @brief Callback function for receiving raw CSI data. + * + * This callback function is used by the CSI service to send the raw CSI data to the application. The CSI service will call this callback function when it has collected raw CSI data. + * + * @param[in] status Status of the operation. Check status == CSIFW_OK before using csi_raw_buff. status == CSIFW_ERROR_WIFI_DIS_CONNECTED, if network is disconnected. + * @param[in] csi_data_len Length of the raw CSI data. + * @param[in] csi_raw_buff Pointer to the raw CSI data buffer. + */ +typedef void (*client_raw_data_listener)(CSIFW_RES status, int csi_data_len, unsigned char *csi_raw_buff, void* ptr); + +/** + * @brief Callback function type for receiving parsed CSI data. + * + * This callback function is used by the CSI service to send the parsed CSI data to the application. The CSI service will call this callback function when it has collected parsed CSI data. + * + * @param[in] status Status of the operation. Check status == CSIFW_OK before using csi_parsed_buff. status == CSIFW_ERROR_WIFI_DIS_CONNECTED, if network is disconnected + * @param[in] csi_data_len Length of the parsed CSI data. + * @param[in] csi_parsed_buff Pointer to the parsed CSI data buffer. + */ +typedef void (*client_parsed_data_listener)(CSIFW_RES status, int csi_data_len, float *csi_parsed_buff, void* ptr); + +/** + * @brief Initializes the CSI service. + * It should be called before calling any other CSI service APIs. + * + * @note The CSI data buffer pointer is shared in service's callback, the service must memcpy data and release the callback. + * + * + * @param[in] config_type Configuration type for the CSI service. These parameters control various aspects of the CSI service, such as the number of antennas, accuracy, mode of CSI data, etc. + * @param[in] raw_callback Callback function for receiving raw CSI data. Pass NULL if raw csi data not required. + * @param[in] parsed_callback Callback function for receiving parsed CSI data. Pass NULL if parsed csi data not required. + * @param[in] interval Data collection interval in ms. For Non-HT data Interval range (3.2 ~ 81.6 ms), HT data Interval range (30ms ~ 1000ms) + * @param[in] ptr An optional pointer that can be used to pass additional context data to the callbacks. + * + * @return #CSIFW_OK on success, otherwise an error code. + */ +CSIFW_RES csi_service_init(CSI_CONFIG_TYPE config_type, client_raw_data_listener raw_callback, client_parsed_data_listener parsed_callback, unsigned int interval, void* ptr); + +/** + * @brief Get MAC address of the connected AP. + * + * struct csifw_mac_info object is provided as argument. + * + * @param[out] mac_info mac_addr is updated with current AP mac address. + * + * @return #CSIFW_OK on success, otherwise an error code. + */ +CSIFW_RES csi_get_ap_mac_addr(csifw_mac_info *mac_info); + +/** + * @brief Starts the CSI service. + * + * The CSI service will start collecting CSI data and sending it to the application through the registered callback functions. + * + * @return #CSIFW_OK on success, otherwise an error code. + */ +CSIFW_RES csi_service_start(); + +/** + * @brief Stops the CSI service. + * + * The CSI service will stop collecting CSI data and sending it to the application. + * + * @return #CSIFW_OK on success, otherwise an error code. + */ +CSIFW_RES csi_service_stop(CSIFW_REASON reason); + +/** + * @brief Deinitializes the CSI service. + * + * It should be called after stopping the CSI service and before exiting the application. + * + * @return #CSIFW_OK on success, otherwise an error code. + */ +CSIFW_RES csi_service_deinit(); + +#ifdef __cplusplus /* If this is a C++ compiler, end C linkage */ + } +#endif +#endif /* __CSIFW_API_H__ */ + diff --git a/framework/include/csifw/readme.md b/framework/include/csifw/readme.md new file mode 100644 index 0000000000..156b8f3e7d --- /dev/null +++ b/framework/include/csifw/readme.md @@ -0,0 +1,103 @@ +# A. How to build csifw test app + +1. Run command `./os/dbuild.sh menu`. +2. Select board `rtl8730e` +3. Select configuration `loadable_ext_psram` +4. Modify build by selecting `Modify Current Configuration` +5. Enable csifw_test and wm_test app from menuconfig: + + Application Configuration-> Examples-> [*] CSIFW test application + + Wifi Manager-> [*] Wi-Fi Manager Sample + +6. Turn on CSIFW logs from menuconfig: + + CSI Framework-> CSIFW Debug Logs-> [*] CSIFW Logs + [*] CSIFW Error Logs + [ ] CSIFW Information Logs + [ ] CSIFW Debug Logs + [ ] CSIFW Verbose Logs + +# B. How to run csifw test app on board + +1. Connect board to wifi. + +3. Run csifw_test command as shown below: + + csifw_test + + Example: + + csifw_test 30 0 // CSI service will start sending HT data at 30ms. + csifw_test 40 1 // CSI service will start sending NON-HT data at 40ms. + + +
+
+
+ +# B. CSI Framework Guide + + +### Supported CSI Data Configs + +CSIFW supports following configurations in STA mode: +- `HT_CSI_DATA` (56 subcarriers each sub-carrier data is 2 bytes) +- `NON_HT_CSI_DATA` (52 subcarriers each sub-carrier data is 2 bytes) +- `HT_CSI_DATA_ACC1` (56 subcarriers each sub-carrier data is 4 bytes) +- `NON_HT_CSI_DATA_ACC1` (52 subcarriers each sub-carrier data is 4 bytes) +
+ +The config can be set using `CSI_CONFIG_TYPE` enum. + + + + +
+ +## HOW TO USE CSIFW APIs (*CSI_Platform/include/csimanager/csifw_api.h*) +
+ +1. __Create callbacks__: Before initializing CSIFW, client needs to implement raw and/or parsed callbacks to receive csi data: + + **RAW DATA CALLBACK** + + typedef void (*client_raw_data_listener)(CSIFW_RES status, int csi_data_len, unsigned char *csi_raw_buff, void* ptr); + + **PARSED DATA CALLBACK** + + typedef void (*client_parsed_data_listener)(CSIFW_RES status, int csi_data_len, float *csi_parsed_buff, void* ptr); + + > Note: Client should copy the data from buffer and return the callback, **the callback must not be blocked**. + +
+ +2. __csi_service_init__: After implementing the callbacks, client can initialize CSIFW by calling `csi_service_init` + + CSIFW_RES csi_service_init(CSI_CONFIG_TYPE config_type, client_raw_data_listener raw_callback, client_parsed_data_listener parsed_callback, unsigned int interval, void* ptr); + - `interval` : The interval in microseconds. + - If raw/parsed data is not required, pass `NULL` in place of callback. +
+ +3. __csi_service_start__: Call `csi_service_start` and csi service will start sending data to client. + + CSIFW_RES csi_service_start(); +
+ +4. __csi_service_stop__: Call `csi_service_stop` and csi service will stop sending data to client. + + CSIFW_RES csi_service_stop(CSIFW_REASON reason); + - `reason`: Reason for stopping the service. {`CSIFW_NORMAL`, `CSIFW_WIFI_DISCONNECTED`} + - `CSIFW_WIFI_DISCONNECTED`: If wifi monitoring is done by clinet then client should call stop api with `CSIFW_WIFI_DISCONNECTED` reason when wifi disconnects. + - `CSIFW_NORMAL`: If client wants to stop the service normally. +
+ +5. __csi_service_deinit__: Call `csi_service_deinit` to deinit csi service. + + CSIFW_RES csi_service_deinit(); + +
+ +6. __csi_get_ap_mac_addr__: Call `csi_get_ap_mac_addr` to get mac address of board. + + CSIFW_RES csi_get_ap_mac_addr(csifw_mac_info *mac_info); diff --git a/framework/src/csifw/CSINetworkMonitor.c b/framework/src/csifw/CSINetworkMonitor.c new file mode 100644 index 0000000000..ad66bb254f --- /dev/null +++ b/framework/src/csifw/CSINetworkMonitor.c @@ -0,0 +1,116 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + + +#include "include/CSINetworkMonitor.h" +#include + +static network_status_listener g_network_status_callback; + +static void wm_cb_sta_connected(wifi_manager_cb_msg_s msg, void *arg); +static void wm_cb_sta_disconnected(wifi_manager_cb_msg_s msg, void *arg); +static CSIFW_RES checkWifiConnection(); + +static wifi_manager_cb_s g_wifi_callbacks = { + wm_cb_sta_connected, + wm_cb_sta_disconnected, + NULL, + NULL, + NULL, +}; + +CSIFW_RES network_monitor_init(network_status_listener network_status_callback) +{ + CSIFW_RES res = CSIFW_OK; + res = checkWifiConnection(); + if (res != CSIFW_OK) { + if (res == CSIFW_ERROR_WIFI_NOT_CONNECTED) { + CSIFW_LOGD("[network_monitor_init] Wifi is not connected"); + } else { + CSIFW_LOGE("Some error in checking wifi connection"); + } + } + CSIFW_LOGD("Connection check result-> Connected"); + + //regsiter callback + if (wifi_manager_register_cb(&g_wifi_callbacks)!= WIFI_MANAGER_SUCCESS) + { + CSIFW_LOGE("[network_monitor_init] wifi_manager_register_cb fail"); + return CSIFW_ERROR; + } + g_network_status_callback = network_status_callback; + return res; +} + +CSIFW_RES network_monitor_deinit() +{ + CSIFW_LOGI("Deregistering network_monitor"); + + if (wifi_manager_unregister_cb(&g_wifi_callbacks)!= WIFI_MANAGER_SUCCESS) + { + CSIFW_LOGE("[network_monitor_deinit] wifi_manager_unregister_cb fail"); + return CSIFW_ERROR; + } + CSIFW_LOGI("network_monitor_deinit done"); + return CSIFW_OK; +} + + +static CSIFW_RES checkWifiConnection() +{ + // Initial Wi-fi Connection checking. + CSIFW_LOGI("Checking WIFI connection . . . . . ."); + wifi_manager_info_s wminfo; + //get info from wifi manager + if (wifi_manager_get_info(&wminfo)!= WIFI_MANAGER_SUCCESS) { + CSIFW_LOGE("get info fail"); + return CSIFW_ERROR; + } + // check connection status + if (wminfo.status == AP_CONNECTED) { + CSIFW_LOGD("WIFI is connected"); + } else if (wminfo.status == AP_DISCONNECTED) { + CSIFW_LOGD("WIFI is not connected"); + return CSIFW_ERROR_WIFI_NOT_CONNECTED; + } else { + CSIFW_LOGE("WIFI status is unknown"); + return CSIFW_ERROR; + } + CSIFW_LOGI("[CSIFW_NM] WIFI CONNECTION CHECK SUCCESS"); + return CSIFW_OK; +} + +static void wm_cb_sta_connected(wifi_manager_cb_msg_s msg, void *arg) +{ + CSIFW_LOGI("[CSIFW_NM]--> res(%d)", msg.res); + CSIFW_LOGI("[CSIFW_NM] bssid %02x:%02x:%02x:%02x:%02x:%02x", + msg.bssid[0], msg.bssid[1], + msg.bssid[2], msg.bssid[3], + msg.bssid[4], msg.bssid[5]); + //inform CSI Service + g_network_status_callback(WIFI_CONNECTED); +} + +static void wm_cb_sta_disconnected(wifi_manager_cb_msg_s msg, void *arg) +{ + CSIFW_LOGI("[CSIFW_NM] WIFI Disconnected --> res(%d) reason %d", msg.res, msg.reason); + //inform CSI Service + g_network_status_callback(WIFI_DISCONNECTED); + +} + diff --git a/framework/src/csifw/CSIPacketReceiver.c b/framework/src/csifw/CSIPacketReceiver.c new file mode 100644 index 0000000000..5340a49f42 --- /dev/null +++ b/framework/src/csifw/CSIPacketReceiver.c @@ -0,0 +1,325 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#include "include/CSIPacketReceiver.h" +#include +#include +#include +#include +#include +#include + +#define RCVR_TH_NAME "csifw_receiver" + +#define OPEN_DRIVER_OR_EXIT(_FD) \ + _FD = open(CONFIG_WIFICSI_CUSTOM_DEV_PATH, O_RDONLY);\ + if (_FD < 0) {\ + CSIFW_LOGE("Failed to open device path : %s errno : %d", CONFIG_WIFICSI_CUSTOM_DEV_PATH, get_errno());\ + return CSIFW_ERROR;\ + }\ + +#define CLOSE_DRIVER_OR_EXIT(_FD) close(_FD); + +extern int g_pkt_count; +static pthread_t gCSIDataReceiver; +static unsigned char *g_get_data_buffptr; // the buffer to get data from driver +static uint16_t gCSIRawBufLen; +static CSIDataListener gCSIDataCallback; +static bool g_csi_thread_stop = true; +static bool g_csi_enabled; // this flag maintains wifi_csi_config enable state during wifi re-connection +static bool g_skip_pkt; +static mqd_t g_mq_handle; + +typedef enum CSI_SET_CONFIG_CMD { + CSIFW_CONFIG_INIT, + CSIFW_CONFIG_ENABLE, + CSIFW_CONFIG_DISABLE +} CSI_SET_CONFIG_CMD; + +CSIFW_RES csi_packet_receiver_set_csi_config(csi_action_param_t *config, CSI_SET_CONFIG_CMD config_cmd); + +static int readCSIData(int fd,char* buf, int size) +{ + int err=0; + csi_driver_buffer_args_t buf_args; + buf_args.buflen = size; + buf_args.buffer = buf; + /* ToDo: store length in buf_args not in err*/ + err = ioctl(fd, CSIIOC_GET_DATA, (unsigned long)&buf_args); + if (err <= OK) { + CSIFW_LOGE("Fail to ioctl(%d, CSIIOC_GET_DATA), ret: %d, errno : %d", fd, err, get_errno()); + } + return err; +} + +static void* dataReceiverThread(void *vargp) { + int len; + int fd, ret; + /* open mq */ + struct mq_attr attr_mq; + attr_mq.mq_maxmsg = CSI_MQ_MSG_COUNT; + attr_mq.mq_msgsize = sizeof(struct wifi_csi_msg_s); + attr_mq.mq_flags = 0; + CSIFW_LOGD("MQ_NAME: %s, MSG_SIZE: %zu, MQ_TYPE: BLOCKING", CSI_MQ_NAME, attr_mq.mq_msgsize); + g_mq_handle = mq_open(CSI_MQ_NAME, O_RDWR | O_CREAT, 0666, &attr_mq); + if (g_mq_handle == (mqd_t)ERROR) { + CSIFW_LOGE("Failed to open mq %d \n", get_errno()); + return NULL; + } + OPEN_DRIVER_OR_EXIT(fd) + // start csi data from driver + ret = ioctl(fd, CSIIOC_START_CSI , NULL); + if (ret < OK) { + CSIFW_LOGE("Fail to ioctl(%d, CSIIOC_START_CSI), errno : %d", fd, get_errno()); + mq_close(g_mq_handle); + CLOSE_DRIVER_OR_EXIT(fd) + return NULL; + } + CSIFW_LOGI("Driver data collect started"); + + int prio; + size_t size; + struct wifi_csi_msg_s msg; + + while (!g_csi_thread_stop) { + size = mq_receive(g_mq_handle, (char *)&msg, sizeof(msg), &prio); + if (size != sizeof(msg)) { + CSIFW_LOGD("Interrupted while waiting for deque message from kernel %zu, errono: %d", size, errno); + } else { + switch (msg.msgId) { + case CSI_MSG_DATA_READY_CB: + if (g_skip_pkt && g_pkt_count <= 0) { + g_pkt_count--; + CSIFW_LOGD("Skipping extra packet number: [%d] size: %d", g_pkt_count, msg.data_len); + continue; + } + if (msg.data_len == 0 || msg.data_len > CSIFW_MAX_RAW_BUFF_LEN) { + CSIFW_LOGE("Skipping packet: invalid data length: %d", msg.data_len); + continue; + } + g_pkt_count--; + gCSIRawBufLen = msg.data_len; + len = readCSIData(fd, g_get_data_buffptr, gCSIRawBufLen); + if (len < 0) { + CSIFW_LOGE("Skipping packet: error: %d", len); + continue; + } + if (len != gCSIRawBufLen - CSIFW_CSI_HEADER_LEN) { + CSIFW_LOGE("Invalid csi data packet, gCSIRawBufLen: %d, new size (w/o header): %d", gCSIRawBufLen, len); + // continue; ==> Cant skip as the message queue call might not be in sync in case of error packets. + gCSIRawBufLen = len + CSIFW_CSI_HEADER_LEN; + } + gCSIDataCallback(CSIFW_OK, gCSIRawBufLen, g_get_data_buffptr, len); + break; + + case CSI_MSG_ERROR: + CSIFW_LOGE("CSI_MSG_ERROR received"); + break; + + default: + CSIFW_LOGE("Received unknown message ID: %d", msg.msgId); + break; + } + } + } + CSIFW_LOGD("dataReceiverThread: Stopping thread"); + //close mq + mq_close(g_mq_handle); + CSIFW_LOGD("dataReceiverThread: MQ closed"); + // stop csi data from driver + ret = ioctl(fd, CSIIOC_STOP_CSI , NULL); + if (ret < OK) { + CSIFW_LOGE("Fail to ioctl(%d, CSIIOC_STOP_CSI), errno : %d", fd, get_errno()); + } else { + CSIFW_LOGD("Driver data collect stopped"); + } + CLOSE_DRIVER_OR_EXIT(fd) + CSIFW_LOGD("[dataReceiverThread] THREAD EXIT"); + return NULL; +} + +CSIFW_RES csi_packet_receiver_init(CSIDataListener CSIDataCallback) { + g_csi_enabled = false; + gCSIRawBufLen = CSIFW_MAX_RAW_BUFF_LEN; + gCSIDataCallback = CSIDataCallback; + return CSIFW_OK; +} + +CSIFW_RES csi_packet_receiver_set_csi_config(csi_action_param_t *config, CSI_SET_CONFIG_CMD config_cmd) { + + int fd, ret; + OPEN_DRIVER_OR_EXIT(fd) + switch (config_cmd) { + case CSIFW_CONFIG_INIT: + config->act = 1; + config->enable = 0; + // memset config to 0 + CSIFW_LOGI("Memset 0 config param before setting config"); + ret = ioctl(fd, CSIIOC_PARAM_SETZERO, NULL); + if (ret < OK) { + CSIFW_LOGE("Fail to ioctl(%d, CSIIOC_PARAM_SETZERO ), errno : %d", fd, get_errno()); + CLOSE_DRIVER_OR_EXIT(fd) + return CSIFW_ERROR; + } + CSIFW_LOGI("Set CSI config"); + break; + + case CSIFW_CONFIG_ENABLE: + config->act = 0; + config->enable = 1; + break; + case CSIFW_CONFIG_DISABLE: + config->act = 0; + config->enable = 0; + break; + default: + CSIFW_LOGE("Invalid command"); + CLOSE_DRIVER_OR_EXIT(fd) + return CSIFW_INVALID_ARG; + } + + ret = ioctl(fd, CSIIOC_SET_CONFIG, (unsigned long)config); + if (ret < OK) { + CSIFW_LOGE("Fail to ioctl(%d, CSIIOC_SET_CONFIG ), errno : %d", fd, get_errno()); + CLOSE_DRIVER_OR_EXIT(fd) + return CSIFW_ERROR; + } + CLOSE_DRIVER_OR_EXIT(fd) + return CSIFW_OK; +} + +CSIFW_RES csi_packet_receiver_get_mac_addr(csifw_mac_info *mac_info) { + int fd; + csifw_mac_info *mac_info_t = (csifw_mac_info *)mac_info; + OPEN_DRIVER_OR_EXIT(fd) + int ret = ioctl(fd, CSIIOC_GET_MAC_ADDR, (unsigned long)(mac_info_t)); + if (ret < OK) { + CSIFW_LOGE("Fail to ioctl(%d, CSIIOC_SET_CONFIG ), errno : %d", fd, get_errno()); + return CSIFW_ERROR; + } + CLOSE_DRIVER_OR_EXIT(fd) + CSIFW_LOGD("Got mac address from driver: [%02x:%02x:%02x:%02x:%02x:%02x]", mac_info->mac_addr[0], mac_info->mac_addr[1], mac_info->mac_addr[2], mac_info->mac_addr[3], mac_info->mac_addr[4], mac_info->mac_addr[5]); + return CSIFW_OK; +} + +CSIFW_RES csi_packet_receiver_start_collect(csi_action_param_t *config) { + CSIFW_RES res; + // allocate buffer for receiveing data from driver + if (!g_get_data_buffptr) { + g_get_data_buffptr = (unsigned char *)malloc(CSIFW_MAX_RAW_BUFF_LEN); + if (!g_get_data_buffptr) { + CSIFW_LOGE("Buffer allocation fail."); + return CSIFW_ERROR; + } + CSIFW_LOGD("Get data buffer allocation done, size: %d", gCSIRawBufLen); + } + g_csi_thread_stop = false; + + //create receiver thread + pthread_attr_t recv_th_attr; + pthread_attr_init(&recv_th_attr); + /* ToDo: stack size of thread needs to be optimized */ + pthread_attr_setstacksize(&recv_th_attr, (10*1024)); + if (pthread_create(&gCSIDataReceiver, &recv_th_attr, dataReceiverThread, NULL) != 0) { + CSIFW_LOGE("Failed to create csi data receiver thread"); + return CSIFW_ERROR; + } + if (pthread_setname_np(gCSIDataReceiver, RCVR_TH_NAME) != 0) { + CSIFW_LOGE("Error in setting receiver thread name, error_no: %d", get_errno()); + } + CSIFW_LOGD("CSI data receive thread created"); + + // set config + res = csi_packet_receiver_set_csi_config(config, CSIFW_CONFIG_INIT); + if (res != CSIFW_OK) { + return res; + } + + if (g_csi_enabled) { + // disable wifi csi report + CSIFW_LOGD("Disabling CSI config"); + res = csi_packet_receiver_set_csi_config(config, CSIFW_CONFIG_DISABLE); + if (res != CSIFW_OK) { + return res; + } + g_csi_enabled = false; + // set config + CSIFW_LOGD("[AGAIN] SET CSI config"); + res = csi_packet_receiver_set_csi_config(config, CSIFW_CONFIG_INIT); + if (res != CSIFW_OK) { + return res; + } + } + // enable csi report + CSIFW_LOGD("Enabling CSI config"); + res = csi_packet_receiver_set_csi_config(config, CSIFW_CONFIG_ENABLE); + if (res != CSIFW_OK) { + return res; + } + g_csi_enabled = true; + return CSIFW_OK; +} + +CSIFW_RES csi_packet_receiver_stop_collect(CSIFW_REASON reason, csi_action_param_t *config) +{ + //join thread + g_csi_thread_stop = true; + // send dummy message to close blocking mq + if (g_mq_handle) { + CSIFW_LOGD("Sending dummy message to close blocking mq"); + struct wifi_csi_msg_s msg; + msg.msgId = CSI_MSG_ERROR; + mq_send(g_mq_handle, (FAR const char *)&msg, sizeof(msg), MQ_PRIO_MAX); + } + pthread_join(gCSIDataReceiver, NULL); + CSIFW_LOGD("Receiver thread join done"); + if (g_get_data_buffptr) { + free(g_get_data_buffptr); + g_get_data_buffptr = NULL; + } + if (reason == CSIFW_WIFI_DISCONNECTED) { + CSIFW_LOGI("Disable not required as wifi disconnected"); + CSIFW_LOGI("csi data collect stopped"); + return CSIFW_OK; + } + + int fd; + // else-if WIFI_CONNECTED disable wifi csi report + OPEN_DRIVER_OR_EXIT(fd) + config->act = 0; + config->enable = 0; + CSIFW_LOGI("Disabling CSI config"); + int ret = ioctl(fd, CSIIOC_SET_CONFIG, (unsigned long)config); + if (ret < OK) { + CSIFW_LOGE("Fail to ioctl(%d, CSIIOC_SET_CONFIG ), errno : %d", fd, get_errno()); + return CSIFW_ERROR; + } + CLOSE_DRIVER_OR_EXIT(fd) + CSIFW_LOGD("csi data collect stopped"); + g_csi_enabled = false; + return CSIFW_OK; +} + +CSIFW_RES csi_packet_receiver_deinit() +{ + CSIFW_LOGI("csi_packet_receiver_deinit"); + gCSIDataCallback = NULL; + CSIFW_LOGD("csi_packet_receiver_deinit done"); + return CSIFW_OK; +} + diff --git a/framework/src/csifw/CSIParser.c b/framework/src/csifw/CSIParser.c new file mode 100644 index 0000000000..d45a85dfcb --- /dev/null +++ b/framework/src/csifw/CSIParser.c @@ -0,0 +1,125 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#include +#include "include/CSIParser.h" + +static int print_log(const char *format, ...) +{ + + va_list args; + va_start(args, format); + + vprintf(format, args); + va_end(args); + return 1; +} + +static void print_buf(const unsigned char* buf, int len) +{ + print_log("RAW DATA %d", len); + unsigned long long *buff_tmp = (u64 *)buf; + int buff_len = (len / 8) + 1; + for (int i = 0; i < buff_len; i++) { + print_log("[%02d]0x%016llx", i, buff_tmp[i]); + } +} + +static float get_parsed_sub_carrier(const unsigned char* buf, int acc) +{ + float Y; + if (acc) { + Y = (buf[1]<<8) | buf[0]; + if (Y > 0x8000) { + Y = (Y-0xffff-1)/(4096.0); + } else { + Y = Y/(4096.0); + } + return Y; + } else { + if (*buf > 0x80) { + Y = (*buf-0xff-1)/(16.0); + } else { + Y = *buf/(16.0); + } + return Y; + } +} + +void logReceivedData(int csi_buff_len, unsigned char *csi_buff, int accuracy) +{ +#if 1 + unsigned int timestamp; + unsigned int csi_seq_num; + /*LOGGING STARTS HERE*/ + CSIFW_LOGI("================== LOGGING =================="); + print_buf(csi_buff, csi_buff_len); + + //PRINTING PARSED DATA + CSIFW_LOGI("PRINTING PARSED DATA"); //csi_sequence, timestamp + print_log("\n csi_buff_len (with header): %d, only csi len: 112",csi_buff_len); + print_log("\nSub-carrier Count: 0x%x%x,", csi_buff[26], csi_buff[25]); + timestamp = (int)(csi_buff[18] << 24) | (int)(csi_buff[17] << 16) | (int)(csi_buff[16] << 8) | (int)csi_buff[15]; + csi_seq_num = (int)(csi_buff[37] << 24) | (int)(csi_buff[36] << 16) | (int)(csi_buff[35] << 8) | (int)csi_buff[34]; + + + CSIFW_LOGI("%d,%d,", csi_seq_num, timestamp); //csi_sequence, timestamp + + print_log("0x%x%x,", csi_buff[1], csi_buff[0]); //csi_signature + print_log("0x%x,", csi_buff[2]); //hdr_len + CSIFW_LOGI("%x:%x:%x:%x:%x:%x,", csi_buff[8],csi_buff[7],csi_buff[6],csi_buff[5],csi_buff[4], csi_buff[3]); //mac_addr + CSIFW_LOGI("%x:%x:%x:%x:%x:%x,", csi_buff[14],csi_buff[13],csi_buff[12],csi_buff[11],csi_buff[10], csi_buff[9]); //trig_mac_addr + CSIFW_LOGI("%d,", ((int)csi_buff[31]/2 - 110)); //rssi + + // CSIFW_LOGI("%s,",argv[1]); //real + CSIFW_LOGI("%d,", (int)csi_buff[19]); //channel, infered + + //Sub-Carrier parser code + int sub_carrier_data_index = 43; + int sub_carrier = 0; + int acc = 0; + if (accuracy == 1) { + acc = 1; + } + int increment = 2 + 2*acc; + for(; sub_carrier_data_index < csi_buff_len; sub_carrier_data_index += increment) { + CSIFW_LOGI("sub_carrier:%d[",sub_carrier); + CSIFW_LOGI("%f,", get_parsed_sub_carrier(csi_buff + sub_carrier_data_index + 1 + acc, acc)); //Img number + CSIFW_LOGI("%f], ", get_parsed_sub_carrier(csi_buff + sub_carrier_data_index, acc)); //real number + sub_carrier += 1; + } + print_log(" sub_carrier_data_index: %d, csi_buff_len: %d", sub_carrier_data_index, csi_buff_len); +#endif +} + +void getParsedData(unsigned char *rawDatabuff, int raw_csi_buff_len, csi_action_param_t *config, float *parsedDatabuff, uint16_t *parsed_csi_buff_len) +{ + csi_accuracy_t accuracy = config->accuracy; + int acc = 0; + if (accuracy == CSI_ACCU_2BYTES) { + acc = 1; + } + int parsedDataIdx, sub_carrier_data_index = 43; + int increment = 2 + (2 * acc); + for (int parsedDataIdx = 0; sub_carrier_data_index < raw_csi_buff_len; parsedDataIdx += 2, sub_carrier_data_index += increment) { + parsedDatabuff[parsedDataIdx] = get_parsed_sub_carrier(rawDatabuff + sub_carrier_data_index + 1 + acc, acc); //Imaginary + parsedDatabuff[parsedDataIdx + 1] = get_parsed_sub_carrier(rawDatabuff + sub_carrier_data_index, acc); //Real + } + *parsed_csi_buff_len = (raw_csi_buff_len - CSIFW_CSI_HEADER_LEN) / (accuracy + 1); +} + diff --git a/framework/src/csifw/CSIService.c b/framework/src/csifw/CSIService.c new file mode 100644 index 0000000000..1e345a0961 --- /dev/null +++ b/framework/src/csifw/CSIService.c @@ -0,0 +1,505 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#include +#include "include/csifw.h" +#include "include/PingGenerator.h" +#include "include/CSIParser.h" +#include "include/CSIPacketReceiver.h" +#include "include/CSINetworkMonitor.h" + +#define CSI_INTERVAL_TO_TRIG_PERIOD 320 /* Conversion factor to convert interval to trig period */ + +COLLECT_STATE g_service_state = CSI_STATE_UNITIALIZED; + +typedef enum _CLIENT_STATE { + CSI_CLIENT_UNREGISTERED = 0, /* CLIENT NOT REGISTERED */ + CSI_CLIENT_STOP = 1, /* CLIENT NOT COLLECTING DATA */ + CSI_CLIENT_START = 2, /* CLIENT COLLECTING DATA */ +} CLIENT_STATE; + +typedef struct CSIFW_CLIENT_INFO_T{ + pthread_t client_tid; // thread id of client thread + CLIENT_STATE state; + client_raw_data_listener raw_cb; + client_parsed_data_listener parsed_cb; + void* client_data; +} csifw_client_info_t; + +int SYSTEM_TAG_CSIFW; +static float *g_parsed_buffptr; +static uint16_t gParsedDataBufferLen; +static unsigned int g_service_interval; +static csi_action_param_t g_csi_config __attribute__((aligned(64))) = {0,}; +static CSI_CONFIG_TYPE g_csi_config_type; + +/* Array of pointers to registered clients' struct */ +static csifw_client_info_t g_client_info[CSIFW_MAX_NUM_APPS]; +static unsigned int g_num_clients; +static unsigned int g_num_start_clients; +static unsigned int g_count_parsed_clients; +static sem_t g_sema; + +/* Private functions */ +static int get_client_idx(void); +static int get_empty_idx(void); +static int register_csi_client(CSI_CONFIG_TYPE config_type, client_raw_data_listener raw_callback, client_parsed_data_listener parsed_callback, unsigned int interval, void* ptr); +static void set_csi_config(void); +static CSIFW_RES start_csi_client(); +static CSIFW_RES stop_csi_client(void); +static CSIFW_RES unregister_csi_client(void); +static CSIFW_RES unregister_csi_client(void); + +static void CSIRawDataListener(CSIFW_RES res, int raw_csi_buff_len, unsigned char *raw_csi_buff, int raw_csi_data_len) +{ + //Send data to clients + if (g_count_parsed_clients > 0) { + getParsedData(raw_csi_buff, raw_csi_buff_len, &g_csi_config, g_parsed_buffptr, &gParsedDataBufferLen); + } + for(int i = 0; i < CSIFW_MAX_NUM_APPS; i++) { + //send raw + if (g_client_info[i].state == CSI_CLIENT_START) { + if (g_client_info[i].raw_cb) { + g_client_info[i].raw_cb(res, raw_csi_buff_len, raw_csi_buff, g_client_info[i].client_data); + } + //send parsed + if (g_client_info[i].parsed_cb) { + g_client_info[i].parsed_cb(res, gParsedDataBufferLen, g_parsed_buffptr, g_client_info[i].client_data); + } + } + } +} + +//ToDO: Check if this also should be handled by lower driver in LATER. (then also need to pass interval to driver..) +static CSIFW_RES csi_set_data_collect_interval(unsigned interval) +{ + if (g_csi_config_type == HT_CSI_DATA || g_csi_config_type == HT_CSI_DATA_ACC1) { + g_csi_config.trig_period = 200; //default value of trig_period. + ping_generator_change_interval(interval); + } else if (g_csi_config_type == NON_HT_CSI_DATA || g_csi_config_type == NON_HT_CSI_DATA_ACC1) { + /* Packet count per second is controllable using act_param.trig_period (unit in 320 us, set values between 10-255) (320us amounts to 1 unit in trig period)*/ + g_csi_config.trig_period = (interval * 1000) / CSI_INTERVAL_TO_TRIG_PERIOD; + ping_generator_change_interval(0); + } else { + CSIFW_LOGE("Config type is invalid"); + return CSIFW_ERROR; + } + return CSIFW_OK; +} + +CSIFW_RES csi_service_init(CSI_CONFIG_TYPE config_type, client_raw_data_listener raw_callback, client_parsed_data_listener parsed_callback, unsigned int interval, void* ptr) +{ + CSIFW_LOGE("CSIPID[%d]", pthread_self()); + if (g_num_clients == 0) { + if (sem_init(&g_sema, 0, 1) < OK) { + CSIFW_LOGE("Semaphore init failed, errno: %d", get_errno()); + return CSIFW_ERROR; + } + } + // manage client + int idx = register_csi_client(config_type, raw_callback, parsed_callback, interval, ptr); + if (idx == -1) { + CSIFW_LOGE("Failed to register client"); + return CSIFW_ERROR; + } + // manage csifw init + if (g_num_clients != 1) { + CSIFW_LOGE("Already Initialized with %d client(s).", g_num_clients); + return CSIFW_OK; + } + + // if num_client == 1 -> Init csi service + CSIFW_LOGI("Initializing CSIFW"); + CSIFW_RES res = CSIFW_OK; + g_csi_config_type = config_type; + set_csi_config(); + g_service_interval = interval; + csi_set_data_collect_interval(g_service_interval); + gParsedDataBufferLen = CSIFW_MAX_RAW_BUFF_LEN; + g_parsed_buffptr = (float *)malloc(sizeof(float) * gParsedDataBufferLen); + if (!g_parsed_buffptr) { + CSIFW_LOGE("Failed to allocate memory for parsed data buffer, size: %zu", sizeof(float) * gParsedDataBufferLen); + // if init fails remove the client + if (unregister_csi_client() != CSIFW_OK) { + CSIFW_LOGE("Client unregister fail."); + } + return CSIFW_ERROR; + } + res = csi_packet_receiver_init(CSIRawDataListener); + if (res != CSIFW_OK) { + CSIFW_LOGE("csi_packet_receiver_init failed"); + if (unregister_csi_client() != CSIFW_OK) { + CSIFW_LOGE("Client unregister fail."); + } + if (!g_parsed_buffptr) { + free(g_parsed_buffptr); + g_parsed_buffptr = NULL; + } + return CSIFW_ERROR; + } + CSIFW_LOGD("CSI Packet receiver init done"); + return res; +} + +CSIFW_RES csi_service_start() +{ + CSIFW_RES res = start_csi_client(); + if (res != CSIFW_OK){ + CSIFW_LOGE("Client start failed"); + return res; + } + CSIFW_LOGI("Client start success"); + // now manage csi service start + if (g_service_state == CSI_COLLECT_STATE_STARTED) { + CSIFW_LOGE("CSI Service already running"); + return CSIFW_OK; + } + res = csi_packet_receiver_start_collect(&g_csi_config); + if (res != CSIFW_OK){ + CSIFW_LOGE("CSI packet receiver start failed"); + if (stop_csi_client() != CSIFW_OK) { + CSIFW_LOGE("Client stop fail."); + } + return res; + } + res = ping_generator_start(); + if (res != CSIFW_OK){ + CSIFW_LOGE("CSI ping generator start failed"); + if (stop_csi_client() != CSIFW_OK) { + CSIFW_LOGE("Client stop fail."); + } + } + g_service_state = CSI_COLLECT_STATE_STARTED; + return res; +} + +CSIFW_RES csi_service_stop(CSIFW_REASON reason) +{ + //manage client + CSIFW_LOGE("CSIPID[%d]", pthread_self()); + CSIFW_RES res = CSIFW_OK; + res = stop_csi_client(); + if (res != CSIFW_OK) { + CSIFW_LOGE("Client stop fail"); + return res; + } + CSIFW_LOGI("Client stopped"); + if (reason == CSIFW_WIFI_DISCONNECTED) { + CSIFW_LOGI("[CSIFW_REASON] CSIFW_WIFI_DISCONNECTED %d", reason); + } else { + CSIFW_LOGI("[CSIFW_REASON] CSIFW_NORMAL %d", reason); + } + //manage csifw + // state is required in following case: + // Wifi dicsonnects + // client 1 c1->stop(disconnect)-->CSIFW service stops-> Success + // client 2 c2->stop(disconnect)-->CSIFW service stops-->Fail... Crash + // solution: dont stop csifw if state is already stopped + if (g_service_state == CSI_COLLECT_STATE_STOPPED) { + CSIFW_LOGI("CSIFW service already stopped"); + return CSIFW_OK; // already stopped due to N/W disconnection + } + if (reason == CSIFW_NORMAL && g_num_start_clients != 0) { + CSIFW_LOGI("CSIFW service will continue running for %d client(s).", g_num_start_clients); + return CSIFW_OK; + } + res = ping_generator_stop(); + if (res != CSIFW_OK) { + CSIFW_LOGE("CSI ping generator stop failed"); + } + res = csi_packet_receiver_stop_collect(reason, &g_csi_config); + if (res != CSIFW_OK) { + CSIFW_LOGE("CSI packet receiver stop failed"); + } + g_service_state = CSI_COLLECT_STATE_STOPPED; + return res; +} + +CSIFW_RES csi_get_ap_mac_addr(csifw_mac_info *mac_info) { + return csi_packet_receiver_get_mac_addr(mac_info); +} + +CSIFW_RES csi_service_deinit() +{ + CSIFW_LOGD("csi_service_deinit"); + CSIFW_LOGE("CSIPID[%d]", pthread_self()); + //manage client + CSIFW_RES res = unregister_csi_client(); + if (res != CSIFW_OK) { + CSIFW_LOGE("Failed to unregister client"); + return res; + } + CSIFW_LOGI("Client unregistered with CSIFW"); + + // manage csifw deinit + if (g_num_clients != 0) { + CSIFW_LOGI("CSIFW will not deinit due to %d registered client(s).", g_num_clients); + return CSIFW_OK; + } + res = csi_packet_receiver_deinit(); + if (res != CSIFW_OK) { + CSIFW_LOGE("CSI packet receiver deinit failed"); + res = CSIFW_ERROR; + } + if (g_parsed_buffptr) { + free(g_parsed_buffptr); + g_parsed_buffptr = NULL; + } + if (sem_destroy(&g_sema) < OK) { + CSIFW_LOGE("Semaphore destroy failed, errno: %d", get_errno()); + } + CSIFW_LOGD("csi_service_deinit done"); + return res; +} + +/* Client handling functions */ + +static int get_client_idx(void) +{ + pthread_t tid = pthread_self(); + if (g_num_clients == 0) { + return -1; + } + for (int i = 0; i < CSIFW_MAX_NUM_APPS; i++) { + if (g_client_info[i].client_tid == tid) { + return i; + } + } + return -1; +} + +static int get_empty_idx(void) +{ + if (g_num_clients == 0) { + return 0; + } + for (int i = 0; i < CSIFW_MAX_NUM_APPS; i++) { + if (g_client_info[i].client_tid == 0) { + return i; + } + } + return -1; +} + +/** + * @brief Register a CSI client with the CSI service. + * + * @param config_type Type of CSI data to collect. + * @param raw_callback Callback function to receive raw CSI data. + * @param parsed_callback Callback function to receive parsed CSI data. + * @param interval Interval between CSI data collections. + * @param ptr Pointer to client-specific data. + * + * @return Index of register client on success, -1 in case of failure. + */ +static int register_csi_client(CSI_CONFIG_TYPE config_type, client_raw_data_listener raw_callback, client_parsed_data_listener parsed_callback, unsigned int interval, void* ptr) +{ + CSIFW_LOGE("CSIPID[%d]", pthread_self()); + // checks on client + + // max client registered + if (g_num_clients == CSIFW_MAX_NUM_APPS) { + CSIFW_LOGE("Max number of clients reached"); + return -1; + } + // config_type valid? + if (config_type < HT_CSI_DATA || config_type > NON_HT_CSI_DATA_ACC1) { + CSIFW_LOGE("Invalid config type"); + return -1; + } + // if num_client > 1 -> is config and interval same as first client + if (g_num_clients != 0) { + if (g_csi_config_type != config_type) { + CSIFW_LOGE("Already initialized with different config type: %d", g_csi_config_type); + return -1; + } + if (g_service_interval != interval) { + CSIFW_LOGE("[Warning!!!]Already initialized with different interval: %d", g_service_interval); // new service is allowed for csi data, but not allowed to change interval. + } + } + + // register the client + pthread_t tid = pthread_self(); + /* Check if already registered */ + int idx = get_client_idx(); + if (idx != -1) { + CSIFW_LOGE("Client with ID: %d already registered at index %d", tid, idx); + return -1; + } + /* get empty index */ + idx = get_empty_idx(); + /* Take sem */ + if (sem_wait(&g_sema) < OK) { + CSIFW_LOGE("Semaphore wait failed, errno: %d", get_errno()); + return CSIFW_ERROR; + } + g_client_info[idx].client_tid = tid; + g_client_info[idx].state = CSI_CLIENT_STOP; + g_client_info[idx].raw_cb = raw_callback; + g_client_info[idx].parsed_cb = parsed_callback; + g_client_info[idx].client_data = ptr; + g_num_clients++; + /* Give sem */ + sem_post(&g_sema); + CSIFW_LOGI("Idx%d: Client(%d) registered with CSIFW. Total clients: %d", idx, tid, g_num_clients); + return idx; // return index of client +} + +static CSIFW_RES start_csi_client() +{ + CSIFW_LOGE("CSIPID[%d]", pthread_self()); + int tid = pthread_self(); + /* get client index */ + int idx = get_client_idx(); + if (idx == -1) { + CSIFW_LOGE("Client(%d) start failed, client not registered.", tid); + return CSIFW_ERROR_CLIENT_NOT_REGISTERED; + } + if (g_client_info[idx].state == CSI_CLIENT_START) { + CSIFW_LOGE("Client already started"); + return CSIFW_ERROR; + } + /* Take sem */ + if (sem_wait(&g_sema) < OK) { + CSIFW_LOGE("Semaphore wait failed, errno: %d", get_errno()); + return CSIFW_ERROR; + } + g_client_info[idx].state = CSI_CLIENT_START; + g_num_start_clients++; + if (g_client_info[idx].parsed_cb) { + g_count_parsed_clients++; // count clients who need parsed data + } + /* Give sem */ + sem_post(&g_sema); + CSIFW_LOGI("Client(%d) start success", tid); + return CSIFW_OK; +} + +static CSIFW_RES stop_csi_client(void) +{ + // find client index and stop + int tid = pthread_self(); + /* get client index */ + int idx = get_client_idx(); + if (idx == -1) { + CSIFW_LOGE("Client(%d) stop failed, client not registered.", tid); + return CSIFW_ERROR_CLIENT_NOT_REGISTERED; + } + if (g_client_info[idx].state == CSI_CLIENT_STOP) { + CSIFW_LOGE("Client already stopped"); + return CSIFW_ERROR; + } + /* Take sem */ + if (sem_wait(&g_sema) < OK) { + CSIFW_LOGE("Semaphore wait failed, errno: %d", get_errno()); + return CSIFW_ERROR; + } + g_client_info[idx].state = CSI_CLIENT_STOP; + g_num_start_clients--; + if (g_client_info[idx].parsed_cb) { + g_count_parsed_clients--; // count clients who need parsed data + } + /* Give sem */ + sem_post(&g_sema); + CSIFW_LOGI("Client(%d) stop success", tid); + return CSIFW_OK; +} + +static CSIFW_RES unregister_csi_client(void) +{ + CSIFW_RES res = CSIFW_OK; + if (g_num_clients == 0) { + CSIFW_LOGE("No clients registered"); + return CSIFW_ERROR_CLIENT_NOT_REGISTERED; + } + pthread_t tid = pthread_self(); + int idx = get_client_idx(); + if (idx == -1) { + CSIFW_LOGE("Client with ID: %d not registered", tid); + return CSIFW_ERROR_CLIENT_NOT_REGISTERED; + } + if (g_client_info[idx].state != CSI_CLIENT_STOP) { + CSIFW_LOGD("Client (pid: %d) not stopped. Stopping client before unregistering", tid); + res = csi_service_stop(CSIFW_NORMAL); + if (res != CSIFW_OK) { + CSIFW_LOGE("Failed to stop client"); + } + } + //unregister after stop + /* Take sem */ + if (sem_wait(&g_sema) < OK) { + CSIFW_LOGE("Semaphore wait failed, errno: %d", get_errno()); + return CSIFW_ERROR; + } + g_client_info[idx].state = CSI_CLIENT_UNREGISTERED; + g_client_info[idx].client_tid = 0; + g_num_clients--; + /* Give sem */ + sem_post(&g_sema); + CSIFW_LOGI("Idx%d: Client(%d) unregistered with CSIFW. Total clients: %d", idx, tid, g_num_clients); + return res; +} + +//todo: In later releases this config type will be handled by driver (lower) not in CSIFW. +static void set_csi_config(void) +{ + /* Take sem */ + if (sem_wait(&g_sema) < OK) { + CSIFW_LOGE("Semaphore wait failed, errno: %d", get_errno()); + return; + } + g_csi_config.group_num = CSI_GROUP_NUM_1; + switch (g_csi_config_type) { + case HT_CSI_DATA: + g_csi_config.mode = CSI_MODE_NORMAL; + g_csi_config.ch_opt = CSI_CH_NON_LEGACY; + g_csi_config.data_rate = 0x80; + g_csi_config.accuracy = CSI_ACCU_1BYTE; + break; + + case HT_CSI_DATA_ACC1: + g_csi_config.mode = CSI_MODE_NORMAL; + g_csi_config.ch_opt = CSI_CH_NON_LEGACY; + g_csi_config.data_rate = 0x80; + g_csi_config.accuracy = CSI_ACCU_2BYTES; + break; + + case NON_HT_CSI_DATA: + g_csi_config.mode = CSI_MODE_RX_RESP; + g_csi_config.ch_opt = CSI_CH_LEGACY; + g_csi_config.data_rate = 0xC; + g_csi_config.accuracy = CSI_ACCU_1BYTE; + break; + + case NON_HT_CSI_DATA_ACC1: + g_csi_config.mode = CSI_MODE_RX_RESP; + g_csi_config.ch_opt = CSI_CH_LEGACY; + g_csi_config.data_rate = 0xC; + g_csi_config.accuracy = CSI_ACCU_2BYTES; + break; + + default: + CSIFW_LOGE("Unknown config type: %d", g_csi_config_type); + break; + + } + /* Give sem */ + sem_post(&g_sema); + CSIFW_LOGI("CSIFW config type: %d and interval: %d set", g_csi_config_type, g_service_interval); +} + diff --git a/framework/src/csifw/Kconfig b/framework/src/csifw/Kconfig new file mode 100644 index 0000000000..24d56fdeeb --- /dev/null +++ b/framework/src/csifw/Kconfig @@ -0,0 +1,53 @@ +# +#For a description of the syntax of this configuration file, +#see kconfig - language at https: //www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt +# + +config CSIFW + bool "CSI Framework" + default n + ---help--- + Enables CSI Framework. + +if CSIFW + +menu "CSIFW Debug Logs" + +config CSIFW_LOGS + bool "CSIFW Logs" + default y + ---help--- + Enables CSIFW logs + +if CSIFW_LOGS + +config CSIFW_LOGE + bool "CSIFW Error Logs" + default y + ---help--- + Enables CSIFW Error Logs + +config CSIFW_LOGI + bool "CSIFW Information Logs" + default n + ---help--- + Enables CSIFW Information Logs + +config CSIFW_LOGD + bool "CSIFW Debug Logs" + default n + ---help--- + Enables CSIFW Debug Logs + +config CSIFW_LOGV + bool "CSIFW Verbose Logs" + default n + ---help--- + Enables CSIFW Verbose Logs + +endif #CSIFW_LOGS + +endmenu + +endif #if CSIFW + diff --git a/framework/src/csifw/Make.defs b/framework/src/csifw/Make.defs new file mode 100644 index 0000000000..ff69bea55f --- /dev/null +++ b/framework/src/csifw/Make.defs @@ -0,0 +1,27 @@ +########################################################################### +# +# Copyright 2024 Samsung Electronics All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +# +########################################################################### + +ifeq ($(CONFIG_CSIFW), y) + +CSRCS += CSIPacketReceiver.c CSIParser.c CSIService.c PingGenerator.c CSINetworkMonitor.c + +DEPPATH += --dep-path src/csifw +VPATH += :src/csifw + +endif + diff --git a/framework/src/csifw/PingGenerator.c b/framework/src/csifw/PingGenerator.c new file mode 100644 index 0000000000..719571ac45 --- /dev/null +++ b/framework/src/csifw/PingGenerator.c @@ -0,0 +1,296 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#include "include/PingGenerator.h" +#include +#include +#include + +extern COLLECT_STATE g_service_state; +static unsigned int gPingInterval = 0; +static pthread_t gPingThread; +static g_ping_count; +int g_pkt_count; //extern + +void* pingThreadFun(void *vargp); +static CSIFW_RES ping_generator_open_socket(int *ping_socket, struct sockaddr **socketAddr, struct addrinfo **result); +static CSIFW_RES ping_generator_close_socket(int *ping_socket, struct addrinfo **result); +static int setIcmp4Config(struct addrinfo *hints); +static bool gStopPingThread; + +#define PING_TH_NAME "csifw_ping" + +void ping_generator_change_interval(unsigned int pingInterval) +{ + CSIFW_LOGI("Ping interval set to %u MS", pingInterval); // interval in ms + gPingInterval = pingInterval * 1000; // converting interval in microsecond +} + +static u16_t standard_chksum(void *dataptr, u16_t len) +{ + u32_t acc; + u16_t src; + u8_t *octetptr; + + acc = 0; + /* dataptr may be at odd or even addresses */ + octetptr = (u8_t *) dataptr; + while (len > 1) { + /* declare first octet as most significant + thus assume network order, ignoring host order */ + src = (*octetptr) << 8; + octetptr++; + /* declare second octet as least significant */ + src |= (*octetptr); + octetptr++; + acc += src; + len -= 2; + } + if (len > 0) { + /* accumulate remaining octet */ + src = (*octetptr) << 8; + acc += src; + } + /* add deferred carry bits */ + acc = (acc >> 16) + (acc & 0x0000ffffUL); + if ((acc & 0xffff0000UL) != 0) { + acc = (acc >> 16) + (acc & 0x0000ffffUL); + } + /* This maybe a little confusing: reorder sum using htons() + instead of ntohs() since it has a little less call overhead. + The caller must invert bits for Internet sum ! */ + return htons((u16_t) acc); +} + +static void ping_prepare_echo(struct icmp_echo_hdr *p_iecho, u16_t len) +{ + size_t i = 0; + int icmp_hdrlen = sizeof(struct icmp_echo_hdr); + ICMPH_CODE_SET(p_iecho, 0); + p_iecho->id = 0xAFAF; //PING_ID; + p_iecho->seqno = htons(1); + + /* fill the additional data buffer with some data */ + for (i = icmp_hdrlen; i < len; i++) { + ((char *)p_iecho)[i] = (char)i; + } + + ICMPH_TYPE_SET(p_iecho, ICMP_ECHO); + p_iecho->chksum = 0; + p_iecho->chksum = ~standard_chksum(p_iecho, len); + +} + + +static CSIFW_RES ping_send(int ping_socket, struct icmp_echo_hdr *p_iecho, struct sockaddr *socketAddr) +{ + if (g_pkt_count < 0) { + g_pkt_count = 0; + } + g_pkt_count++; /* increment in case of error also (ping fails, but proxy packet not skipped) to ensure that packets are sent at gInterval */ + g_ping_count++; + if(!(g_ping_count % 1000)) CSIFW_LOGD("ping_send(): ping sent %d times", g_ping_count); + + int ret; + socklen_t addrlen = sizeof(struct sockaddr_in); + size_t icmplen = sizeof(struct icmp_echo_hdr) + 8; + + ret = sendto(ping_socket, p_iecho, icmplen, 0, socketAddr, addrlen); + if (ret <= 0) { + CSIFW_LOGE("sendto() return val is %d, errno: %d", ret, errno); + perror("sendto"); + return CSIFW_ERROR; + } + + return CSIFW_OK; +} + +void* pingThreadFun(void *vargp) +{ + int ping_socket = -1; + struct addrinfo *result = NULL; + struct sockaddr *socketAddr = NULL; + + if (ping_generator_open_socket(&ping_socket, &socketAddr, &result) != CSIFW_OK) { + CSIFW_LOGE("Start collect failed as socket open failed"); + if (ping_generator_close_socket(&ping_socket, &result) != CSIFW_OK) { + CSIFW_LOGE("socket close failed"); + } + return NULL; + } + + size_t icmplen = sizeof(struct icmp_echo_hdr) + 8; + struct icmp_echo_hdr *p_iecho = NULL; + + /* ToDo: Check this repetitive malloc */ + p_iecho = (struct icmp_echo_hdr *)malloc(icmplen); + if (!p_iecho) { + CSIFW_LOGE("fail to allocate memory %zu", icmplen); + ping_generator_close_socket(&ping_socket, &result); + return NULL; + } + ping_prepare_echo(p_iecho, (u16_t)icmplen); + + while (!gStopPingThread) { + usleep(gPingInterval); + if (ping_send(ping_socket, p_iecho, socketAddr) == CSIFW_OK) { + } else { + CSIFW_LOGE("[T] ping_process: sendto error(%d)", errno); + } + } + if (p_iecho) { + free(p_iecho); + } + if (ping_generator_close_socket(&ping_socket, &result) != CSIFW_OK) { + CSIFW_LOGE("socket close failed"); + } + CSIFW_LOGD("[PING Thread] THREAD EXIT"); + return NULL; +} + +CSIFW_RES ping_generator_start(void) +{ + if (gPingInterval == 0) { + CSIFW_LOGD("Ping interval is 0 ==> NO PING START", gPingInterval); + return CSIFW_OK; + } + g_ping_count = 0; + g_pkt_count = 0; + gPingThread = 0; + // create thread and values init + gStopPingThread = false; + if (pthread_create(&gPingThread, NULL, pingThreadFun, NULL) != 0) { + CSIFW_LOGE("Failed to create csi data collect thread"); + return CSIFW_ERROR; + } + if (pthread_setname_np(gPingThread, PING_TH_NAME) != 0) { + CSIFW_LOGE("Error in setting ping thread name, error_no: %d", get_errno()); + } + CSIFW_LOGI("Thread created and values initialized"); + return CSIFW_OK; +} + +CSIFW_RES ping_generator_stop(void) +{ + if (gPingInterval == 0) { // means ping never started + CSIFW_LOGI("Ping interval is 0 ==> PING STOP NOT REQUIRED", gPingInterval); + return CSIFW_OK; + } + gStopPingThread = true; + pthread_join(gPingThread, NULL); + CSIFW_LOGI("Ping thread stopped"); + return CSIFW_OK; +} + +static int setIcmp4Config(struct addrinfo *hints) +{ + memset(hints, 0, sizeof(struct addrinfo)); + hints->ai_family = AF_INET; + hints->ai_socktype = SOCK_RAW; + hints->ai_protocol = IPPROTO_ICMP; + return CSIFW_OK; +} + +static CSIFW_RES ping_generator_open_socket(int *ping_socket, struct sockaddr **socketAddr, struct addrinfo **result) +{ + char ipv4_address[4]; + char ipv4_buf[16]; + const char *tAddr; + if (netlib_get_ipv4_gateway_addr("wlan0", ipv4_address) == 0) { + snprintf(ipv4_buf, 16, "%d.%d.%d.%d", ipv4_address[0], ipv4_address[1], ipv4_address[2], ipv4_address[3]); + } + + struct timeval tv; + struct timespec ping_time; + struct addrinfo hints; + struct addrinfo *rp = NULL; + + /* write information for getaddrinfo() */ + if (setIcmp4Config(&hints) != CSIFW_OK) { + CSIFW_LOGE("ping_process: invalid IP address"); + return CSIFW_ERROR; + } + tAddr = ipv4_buf; + CSIFW_LOGI("PING %s (%s)bytes of data.", tAddr, tAddr); + + /* get address information */ + if (getaddrinfo(tAddr, NULL, &hints, result) != 0) { + CSIFW_LOGE("ping_process: fail to get addrinfo"); + return CSIFW_ERROR; + } + /* try to find valid socket with address information */ + for (rp = *result; rp != NULL; rp = rp->ai_next) { + if ((*ping_socket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0) { + /* continue if open is failed */ + continue; + } else { + /* success */ + CSIFW_LOGI("SOCKET SET!!!"); + break; + } + } + CSIFW_LOGI("ping_socket: %d", *ping_socket); + if (rp == NULL) { + /* opening socket is totally failed */ + CSIFW_LOGE("ping_process: fail to create raw socket"); + return CSIFW_ERROR; + } + + /* copy the socket pointer we found */ + *socketAddr = rp->ai_addr; + if (!(*socketAddr)) + { + CSIFW_LOGE("ping_send: invalid rp->ai_addr"); + return CSIFW_ERROR; + } + + if ((*socketAddr)->sa_family != AF_INET) { + CSIFW_LOGE("ping_send: invalid family"); + return CSIFW_ERROR; + } + + tv.tv_sec = 1; + tv.tv_usec = 0; + + if (setsockopt(*ping_socket, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval)) != ERR_OK) { + CSIFW_LOGE("ping_process: setsockopt error"); + return CSIFW_ERROR; + } + CSIFW_LOGI("setsockopt Done"); + return CSIFW_OK; +} + +static CSIFW_RES ping_generator_close_socket(int *ping_socket, struct addrinfo **result) +{ + CSIFW_RES res = CSIFW_OK; + if (*result) { + freeaddrinfo(*result); + CSIFW_LOGI("freeaddrinfo Done"); + } + + if (*ping_socket >= 0) { + if (close(*ping_socket) < 0) { + CSIFW_LOGE("close Socket failed"); + res = CSIFW_ERROR; + } + *ping_socket = -1; + } + CSIFW_LOGI("Ping generator socket closed"); + return res; +} + diff --git a/framework/src/csifw/include/CSINetworkMonitor.h b/framework/src/csifw/include/CSINetworkMonitor.h new file mode 100644 index 0000000000..5d7a0e0f2b --- /dev/null +++ b/framework/src/csifw/include/CSINetworkMonitor.h @@ -0,0 +1,26 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#ifndef __CSI_NETWORK_MONITOR_H__ +#define __CSI_NETWORK_MONITOR_H__ +#include "csifw.h" + +CSIFW_RES network_monitor_init(network_status_listener network_status_callback); +CSIFW_RES network_monitor_deinit(); +#endif /* __CSI_NETWORK_MONITOR_H__ */ + diff --git a/framework/src/csifw/include/CSIPacketReceiver.h b/framework/src/csifw/include/CSIPacketReceiver.h new file mode 100644 index 0000000000..da47b24305 --- /dev/null +++ b/framework/src/csifw/include/CSIPacketReceiver.h @@ -0,0 +1,32 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#ifndef __CSI_PACKET_RECEIVER_H__ +#define __CSI_PACKET_RECEIVER_H__ + +#include "csifw.h" + +CSIFW_RES csi_packet_receiver_init(CSIDataListener CSIDataCallback); +CSIFW_RES csi_packet_receiver_get_mac_addr(csifw_mac_info *mac_info); +CSIFW_RES csi_packet_receiver_start_collect(csi_action_param_t *config); +CSIFW_RES csi_packet_receiver_pause_collect(csi_action_param_t *config); +CSIFW_RES csi_packet_receiver_stop_collect(CSIFW_REASON reason, csi_action_param_t *config); +CSIFW_RES csi_packet_receiver_deinit(); + +#endif /* __CSI_PACKET_RECEIVER_H__ */ + diff --git a/framework/src/csifw/include/CSIParser.h b/framework/src/csifw/include/CSIParser.h new file mode 100644 index 0000000000..4c40375a78 --- /dev/null +++ b/framework/src/csifw/include/CSIParser.h @@ -0,0 +1,40 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#ifndef __CSI_PARSER_H__ +#define __CSI_PARSER_H__ +#include "csifw.h" + +void logReceivedData(int csi_data_len, unsigned char *csi_buff, int accuracy); + +/** + * @brief Parses raw csi data. + * It should be called before calling any other CSI service APIs. + * + * @param[in] rawDatabuff Raw csi data buffer. + * @param[in] raw_csi_buff_len Raw csi buffer length. + * @param[in] config Configuration parameters for the CSI service.. + * @param[out] parsedDatabuff Parsed csi data buffer to save parsed data. + * @param[out] parsed_csi_buff_len Parsed csi buffer length. + * + * @return + */ +void getParsedData(unsigned char *rawDatabuff, int raw_csi_buff_len, csi_action_param_t *config, float *parsedDatabuff, uint16_t *parsed_csi_buff_len); + +#endif /* __CSI_PARSER_H__ */ + diff --git a/framework/src/csifw/include/PingGenerator.h b/framework/src/csifw/include/PingGenerator.h new file mode 100644 index 0000000000..98028d8894 --- /dev/null +++ b/framework/src/csifw/include/PingGenerator.h @@ -0,0 +1,34 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#ifndef __PING_GENERATOR_H__ +#define __PING_GENERATOR_H__ +#include "csifw.h" + +/** + * @brief Sets the Ping sending interval for the CSI service. + * + * @param[in] interval Data collection interval in ms. + * + */ +void ping_generator_change_interval(unsigned int pingInterval); +CSIFW_RES ping_generator_start(void); +CSIFW_RES ping_generator_stop(void); + +#endif /* __PING_GENERATOR_H__ */ + diff --git a/framework/src/csifw/include/csifw.h b/framework/src/csifw/include/csifw.h new file mode 100644 index 0000000000..27e1776ca1 --- /dev/null +++ b/framework/src/csifw/include/csifw.h @@ -0,0 +1,52 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#ifndef __CSIFW_H__ +#define __CSIFW_H__ + +#include +#include "csifw_log.h" +#include "csifw/csifw_api.h" + +#define CSIFW_MAX_NUM_APPS 3 +#define CSIFW_CSI_HEADER_LEN 43 +#define CSIFW_MAX_RAW_BUFF_LEN 1024 +typedef enum _COLLECT_STATE { + CSI_STATE_UNITIALIZED = -1, /* NOT INIT */ + CSI_COLLECT_STATE_STARTED = 0, /* START CSI_STATE_STARTED */ + CSI_COLLECT_STATE_STOPPED = 1, /* STOP CSI_STATE_STARTED */ + CSI_STATE_INITIALIZED = 2 /* START CSI_STATE_STARTED */ +} COLLECT_STATE; + +typedef enum CSI_DRIVER_CMD { + SET_CSI_CONFIG = 1, + DISABLE_CSI_CONFIG = 2, + ENABLE_CSI_CONFIG = 3, + REGISTER_CALLBACK = 4 +} CSI_DRIVER_CMD; + +typedef enum CONNECTION_STATE { + WIFI_CONNECTED, + WIFI_DISCONNECTED +} CONNECTION_STATE; + +typedef void (*CSIDataListener)(CSIFW_RES res, int csi_buff_len, unsigned char *csi_buff, int csi_data_len); +typedef void (*network_status_listener)(CONNECTION_STATE state); + +#endif /* __CSIFW_H__ */ + diff --git a/framework/src/csifw/include/csifw_log.h b/framework/src/csifw/include/csifw_log.h new file mode 100644 index 0000000000..d931d9a8d9 --- /dev/null +++ b/framework/src/csifw/include/csifw_log.h @@ -0,0 +1,65 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +/** + * @file csifw/csifw_log.h + * @brief This file defines macros used to add debug logs in AI Framework and it's applications. + * + * Log levels are Error, Information and Verbose. Error is by default enabled. + */ + +#pragma once + +#include +#include + +static const char *file_name = 0; +#define TAG "[CSIFW]" +#define TAG_CSIFW_UTILS "[CSIFW_UTILS]" +#define _FILE_NAME file_name ? file_name : (file_name = strrchr(__FILE__, '/') ? (char *)(strrchr(__FILE__, '/') + 1) : __FILE__) + +#define PRINT_LOG(color, tag, ...) \ + do { \ + printf(color tag "[%s]::%s():%d: ", _FILE_NAME, __func__, __LINE__); \ + printf(__VA_ARGS__); \ + printf("\033[m\n"); \ + } while (0) + +#ifdef CONFIG_CSIFW_LOGE +#define CSIFW_LOGE(...) PRINT_LOG("\033[31mE", TAG, __VA_ARGS__) +#else +#define CSIFW_LOGE(...) +#endif + +#ifdef CONFIG_CSIFW_LOGI +#define CSIFW_LOGI(...) PRINT_LOG("\033[32mI", TAG, __VA_ARGS__) +#else +#define CSIFW_LOGI(...) +#endif + +#ifdef CONFIG_CSIFW_LOGV +#define CSIFW_LOGV(...) PRINT_LOG("\033[mV", TAG, __VA_ARGS__) +#else +#define CSIFW_LOGV(...) +#endif + +#ifdef CONFIG_CSIFW_LOGD +#define CSIFW_LOGD(...) PRINT_LOG("\033[33mD", TAG, __VA_ARGS__) +#else +#define CSIFW_LOGD(...) +#endif \ No newline at end of file diff --git a/os/Kconfig b/os/Kconfig index 1bc144cf00..88cfa0916d 100644 --- a/os/Kconfig +++ b/os/Kconfig @@ -566,6 +566,10 @@ menu "AraUI Framework" source "$FRAMEWORK_DIR/src/araui/Kconfig" endmenu +menu "CSI Framework" +source "$FRAMEWORK_DIR/src/csifw/Kconfig" +endmenu + menu "Memory Management" source mm/Kconfig endmenu diff --git a/os/Kconfig.debug b/os/Kconfig.debug index 5bde150b14..f40bf9194e 100644 --- a/os/Kconfig.debug +++ b/os/Kconfig.debug @@ -493,6 +493,38 @@ config DEBUG_TASH_INFO depends on DEBUG_VERBOSE endif #DEBUG_TASH +config DEBUG_WIFICSI + bool "WIFI CSI Debug Feature" + default n + depends on WIFI_CSI + ---help--- + Enable WIFI CSI debug feature. + +if DEBUG_WIFICSI + +config DEBUG_WIFICSI_ERROR + bool "WIFI CSI Error Output" + default n + depends on DEBUG_ERROR + ---help--- + Enable WIFI CSI error debug SYSLOG output. + +config DEBUG_WIFICSI_WARN + bool "WIFI CSI Warning Output" + default n + depends on DEBUG_WARN + ---help--- + Enable WIFI CSI warning debug SYSLOG output. + +config DEBUG_WIFICSI_INFO + bool "WIFI CSI Infomational Debug Output" + default n + depends on DEBUG_VERBOSE + ---help--- + Enable WIFI CSI informational debug SYSLOG output. + +endif #DEBUG_WIFICSI + comment "Framework Debug Options" config DEBUG_EVENTLOOP diff --git a/os/board/rtl8730e/src/Make.defs b/os/board/rtl8730e/src/Make.defs index 9723903119..29b0ca8cc7 100644 --- a/os/board/rtl8730e/src/Make.defs +++ b/os/board/rtl8730e/src/Make.defs @@ -84,3 +84,8 @@ endif ifeq ($(CONFIG_AUDIO_NDP120),y) CSRCS += rtl8730e_ndp120.c endif + +ifeq ($(CONFIG_WIFI_CSI),y) +CSRCS += rtl8730e_rtk_csi.c +endif + diff --git a/os/board/rtl8730e/src/component/os/tizenrt/Make.defs b/os/board/rtl8730e/src/component/os/tizenrt/Make.defs index 99be126c5a..6ac4a000ca 100644 --- a/os/board/rtl8730e/src/component/os/tizenrt/Make.defs +++ b/os/board/rtl8730e/src/component/os/tizenrt/Make.defs @@ -55,5 +55,9 @@ CSRCS += lwip_intf_tizenrt.c \ CSRCS += rtk_blemgr.c \ rtk_netmgr.c \ +ifeq ($(CONFIG_WIFI_CSI),y) +CSRCS += rtk_wifi_csi.c +endif + DEPPATH += --dep-path component/os/tizenrt VPATH += :component/os/tizenrt diff --git a/os/board/rtl8730e/src/component/os/tizenrt/rtk_wifi_csi.c b/os/board/rtl8730e/src/component/os/tizenrt/rtk_wifi_csi.c new file mode 100644 index 0000000000..6d4eb09b2b --- /dev/null +++ b/os/board/rtl8730e/src/component/os/tizenrt/rtk_wifi_csi.c @@ -0,0 +1,294 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wifi_intf_drv_to_app_basic.h" +#include + +/**************************************************************************** + * Private Type Definitions + ****************************************************************************/ + +/* This structure describes the state of the lower layer driver */ + +struct rtk_csi_dev_s { + /* common parts */ + struct wifi_csi_lowerhalf_s dev; /* rtk wifi_csi lower half (this device) */ + + sem_t devsem; /* Protection for both pendq & dev */ + + /* rtk_wifi_csi specific data */ + rtw_csi_action_parm_t act_param; + unsigned char rtk_csi_buf[MAX_CSI_BUFF_LEN]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int rtk_wifi_csi_ioctl(int cmd, unsigned long arg); +static int rtk_wifi_csi_getmacaddr(char *mac_addr); +static int rtk_wifi_csi_getcsidata(char *buffer, size_t buflen); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static FAR struct rtk_csi_dev_s *g_rtk_drv; + +static const struct wifi_csi_ops_s g_wificsiops = { + NULL, /* read */ + rtk_wifi_csi_ioctl, /* ioctl */ + rtk_wifi_csi_getmacaddr, /* getmacaddr */ + rtk_wifi_csi_getcsidata, /* getcsidata */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void data_ready_listener(char *buf, int buf_len, int flags, void *userdata) +{ + if (!g_rtk_drv) { + csidbg("ERROR: priv null\n"); + return; + } + struct wifi_csi_lowerhalf_s *dev = &g_rtk_drv->dev; + /* flags-> data len with header */ + dev->upper_cb(dev->priv, CSI_CALLBACK_DATA_READY, NULL, flags); +} + +static void log_csi_config(void) +{ + if (!g_rtk_drv) { + csidbg("ERROR: priv null\n"); + return -EINVAL; + } + csidbg("CSI config:\n"); + csidbg("group_num: %d\n", g_rtk_drv->act_param.group_num); + csidbg("mode: %d\n", g_rtk_drv->act_param.mode); + csidbg("accuracy: %d\n", g_rtk_drv->act_param.accuracy); + csidbg("ch_opt: %d\n", g_rtk_drv->act_param.ch_opt); + csidbg("trig_period: %d\n", g_rtk_drv->act_param.trig_period); + csidbg("data_rate: %d\n", g_rtk_drv->act_param.data_rate); + csidbg("act: %d\n", g_rtk_drv->act_param.act); + csidbg("enable: %d\n", g_rtk_drv->act_param.enable); +} + +/**************************************************************************** + * rtk semaphore functions + ****************************************************************************/ + +static inline void rtk_wifi_csi_takesem(sem_t *sem) +{ + int ret; + + do { + ret = sem_wait(sem); + DEBUGASSERT(ret == 0 || errno == EINTR); + } while (ret < 0); +} + +static inline int rtk_wifi_csi_givesem(sem_t *sem) +{ + return sem_post(sem); +} + +static inline int rtk_wifi_csi_get_semvalue(sem_t *sem) +{ + int val; + int ret; + ret = sem_getvalue(sem, &val); + if (ret < 0) { + csidbg("ERROR: could not get semaphore value\n"); + return ret; + } + return val; +} + +/**************************************************************************** + * rtk wificsi operations + ****************************************************************************/ + +static int rtk_wifi_csi_ioctl(int cmd, unsigned long arg) +{ + if (!g_rtk_drv) { + csidbg("ERROR: priv null\n"); + return -EINVAL; + } + /* Deal with ioctls passed from the upper-half driver */ + int ret = 0; + switch (cmd) { + case CSIIOC_PARAM_SETZERO: { + csivdbg("CSIIOC_PARAM_SETZERO\n"); + rtk_wifi_csi_takesem(&g_rtk_drv->devsem); + memset(&g_rtk_drv->act_param, 0, sizeof(rtw_csi_action_parm_t)); + rtk_wifi_csi_givesem(&g_rtk_drv->devsem); + } + break; + + case CSIIOC_SET_CONFIG: { + csivdbg("CSIIOC_SET_CONFIG\n"); + if (!arg) { + csidbg("ERROR: invalid config arg\n"); + ret = -EINVAL; + return ret; + } + rtk_wifi_csi_takesem(&g_rtk_drv->devsem); + rtw_csi_action_parm_t *g_act_param_t = ((rtw_csi_action_parm_t*)(arg)); + g_rtk_drv->act_param.group_num = g_act_param_t->group_num; + g_rtk_drv->act_param.mode = g_act_param_t->mode; + g_rtk_drv->act_param.accuracy = g_act_param_t->accuracy; + g_rtk_drv->act_param.ch_opt = g_act_param_t->ch_opt; + g_rtk_drv->act_param.trig_period = g_act_param_t->trig_period; + g_rtk_drv->act_param.data_rate = g_act_param_t->data_rate; + g_rtk_drv->act_param.act = g_act_param_t->act; + g_rtk_drv->act_param.enable = g_act_param_t->enable; + ret = wifi_csi_config(&g_rtk_drv->act_param); + if (ret != OK) { + csidbg("ERROR: wifi csi set config failed ret: %d\n", ret); + ret = -EIO; + rtk_wifi_csi_givesem(&g_rtk_drv->devsem); + break; + } + rtk_wifi_csi_givesem(&g_rtk_drv->devsem); + csivdbg("csi config has been set\n"); + csivdbg("logging csi config\n"); + log_csi_config(); + } + break; + + case CSIIOC_START_CSI: { + csivdbg("CSIIOC_START_CSI\n"); + rtk_wifi_csi_takesem(&g_rtk_drv->devsem); + wifi_reg_event_handler(WIFI_EVENT_CSI_DONE, data_ready_listener, g_rtk_drv); + rtk_wifi_csi_givesem(&g_rtk_drv->devsem); + csivdbg("event listener callback registered\n"); + } + break; + + case CSIIOC_STOP_CSI: { + csivdbg("CSIIOC_STOP_CSI\n"); + rtk_wifi_csi_takesem(&g_rtk_drv->devsem); + wifi_unreg_event_handler(WIFI_EVENT_CSI_DONE, data_ready_listener); + rtk_wifi_csi_givesem(&g_rtk_drv->devsem); + csivdbg("callback unregistered \n"); + } + break; + + default: + csidbg("ERROR: invalid csiwifi command received [%d]\n", cmd); + ret = -EINVAL; + break; + } + return ret; +} + +static int rtk_wifi_csi_getmacaddr(char *mac_addr) { + if (!g_rtk_drv) { + csidbg("ERROR: priv null\n"); + return -EINVAL; + } + rtk_wifi_csi_takesem(&g_rtk_drv->devsem); + csivdbg("mac addr: %02x:%02x:%02x:%02x:%02x:%02x\n", g_rtk_drv->act_param.mac_addr[0], g_rtk_drv->act_param.mac_addr[1], g_rtk_drv->act_param.mac_addr[2], g_rtk_drv->act_param.mac_addr[3], g_rtk_drv->act_param.mac_addr[4], g_rtk_drv->act_param.mac_addr[5]); + memcpy(mac_addr, g_rtk_drv->act_param.mac_addr, 6); + rtk_wifi_csi_givesem(&g_rtk_drv->devsem); + return OK; +} + +static int rtk_wifi_csi_getcsidata(char *buffer, size_t buflen) { + if (!g_rtk_drv) { + csidbg("ERROR: priv null\n"); + return -EINVAL; + } + int len; + rtk_wifi_csi_takesem(&g_rtk_drv->devsem); + if (wifi_csi_report(buflen, buffer, &len) == -1) { + csidbg("ERROR: wifi csi report call failed\n"); + len = -EIO; + } + rtk_wifi_csi_givesem(&g_rtk_drv->devsem); + return len; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rtk_csi_initialize + * + * Description: + * Initialize the rtk device. + * + * Input Parameters: + * + * Returned Value: + * A new lower half wificsi interface for the rtk device is returned on + * success; NULL is returned on failure. + * + ****************************************************************************/ +FAR struct wifi_csi_lowerhalf_s *rtk_csi_initialize() +{ + /* Allocate a RTK_CSI device structure */ + g_rtk_drv = (FAR struct rtk_csi_dev_s *)kmm_zalloc(sizeof(struct rtk_csi_dev_s)); + if (g_rtk_drv == NULL) { + csidbg("ERROR: failed to allocate driver structure\n"); + return NULL; + } + g_rtk_drv->dev.ops = &g_wificsiops; + sem_init(&g_rtk_drv->devsem, 0, 1); + return &g_rtk_drv->dev; +} + +/**************************************************************************** + * Name: rtk_csi_deinitialize + * + * Description: + * Deinitialize the rtk device. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ +void rtk_csi_deinitialize(void) +{ + if (!g_rtk_drv) { + csidbg("ERROR: invalid priv\n"); + return; + } + sem_destroy(&g_rtk_drv->devsem); + /* We should free g_rtk_drv->dev->priv here ? */ + free(g_rtk_drv); + g_rtk_drv = NULL; +} + diff --git a/os/board/rtl8730e/src/rtl8730e_boot.c b/os/board/rtl8730e/src/rtl8730e_boot.c index c9b819bd93..1500c5d03a 100644 --- a/os/board/rtl8730e/src/rtl8730e_boot.c +++ b/os/board/rtl8730e/src/rtl8730e_boot.c @@ -491,6 +491,13 @@ void board_initialize(void) #ifdef CONFIG_AMEBASMART_WIFI wlan_initialize(); #endif + +#ifdef CONFIG_WIFI_CSI + if (rtl8730e_rtk_csi_initialize(0) != 0) { + lldbg("rtl8730e_rtk_csi initialization failed\n"); + } +#endif + /* Enable IPC buffered print */ inic_ipc_buffered_printf_set_np_enable(1); diff --git a/os/board/rtl8730e/src/rtl8730e_rtk_csi.c b/os/board/rtl8730e/src/rtl8730e_rtk_csi.c new file mode 100644 index 0000000000..8084eeaa1b --- /dev/null +++ b/os/board/rtl8730e/src/rtl8730e_rtk_csi.c @@ -0,0 +1,96 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define RTK_CSI_AVAILABLE_MINOR_MIN 0 +#define RTK_CSI_AVAILABLE_MINOR_MAX 25 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rtl8730e_rtk_csi_initialize + * + * Description: + * This function is called by platform-specific, setup logic to configure + * and register the rtk_csi device. This function will register the driver + * as /dev/wificsi[x] where x is determined by the minor device number. + * + * Input Parameters: + * minor - The input device minor number + * + * Returned Value: + * Zero is returned on success. Otherwise, a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ +int rtl8730e_rtk_csi_initialize(int minor) +{ + FAR struct wifi_csi_lowerhalf_s *rtkcsi; + static bool initialized = false; + char devname[12]; + int ret; + + csivdbg("minor %d\n", minor); + DEBUGASSERT(minor >= RTK_CSI_AVAILABLE_MINOR_MIN && minor <= RTK_CSI_AVAILABLE_MINOR_MAX); + + /* Have we already initialized? Since we never uninitialize we must prevent + * multiple initializations. This is necessary, for example, when the + * touchscreen example is used as a built-in application in NSH and can be + * called numerous time. It will attempt to initialize each time. + */ + + if (!initialized) { + + rtkcsi = rtk_csi_initialize(); + if (rtkcsi == NULL) { + csidbg("rtk_csi_initialize failed \n"); + return ERROR; + } + + snprintf(devname, sizeof(devname), "csi%u%u", minor, 0); + ret = wifi_csi_register(devname, rtkcsi); + + if(ret != 0) { + csidbg("wifi_csi_register failed \n"); + /* We should free rtkcsi->priv (wifi_csi_upperhalf_s) also here */ + rtk_csi_deinitialize(rtkcsi); + return ret; + } + initialized = true; + } + return ret; +} + diff --git a/os/drivers/Kconfig b/os/drivers/Kconfig index 07aca745b0..393ed52c40 100644 --- a/os/drivers/Kconfig +++ b/os/drivers/Kconfig @@ -282,6 +282,8 @@ if DRIVERS_VIDEO source drivers/video/Kconfig endif # DRIVERS_VIDEO +source drivers/wifi_csi/Kconfig + menu "AI SoC devices" source drivers/ai-soc/Kconfig endmenu diff --git a/os/drivers/Makefile b/os/drivers/Makefile index 5b1f8b4fa3..d5d0d8cea6 100644 --- a/os/drivers/Makefile +++ b/os/drivers/Makefile @@ -103,6 +103,7 @@ include ttrace$(DELIM)Make.defs include usbdev$(DELIM)Make.defs include usbhost$(DELIM)Make.defs include video$(DELIM)Make.defs +include wifi_csi$(DELIM)Make.defs include wireless$(DELIM)Make.defs ifneq ($(CONFIG_NFILE_DESCRIPTORS),0) diff --git a/os/drivers/wifi_csi/Kconfig b/os/drivers/wifi_csi/Kconfig new file mode 100644 index 0000000000..5586342211 --- /dev/null +++ b/os/drivers/wifi_csi/Kconfig @@ -0,0 +1,35 @@ +menu "CSI Driver Support" + +menuconfig WIFI_CSI + bool "CSI Support" + default n + ---help--- + Enable support for WIFI CSI driver. + +if WIFI_CSI + +config WIFICSI_CUSTOM_DEV_PATH + string "Use custom device path" + default "/dev/wificsi" + +comment "CSI Devices" + +choice + prompt "Choose CSI device" + default WIFI_CSI_RTL8730E + ---help--- + WIFI_CSI_RTL8730E for aidual chip + /* Will support more in future like shown below */ + /* WIFI_CSI_RTL8721CSM for tp1x chip */ + + +config WIFI_CSI_RTL8730E + bool "WIFI_CSI_RTL8730E Driver" + ---help--- + Enable CSI Driver, WIFI_CSI_RTL8730E. + +endchoice + +endif # WIFI_CSI +endmenu + diff --git a/os/drivers/wifi_csi/Make.defs b/os/drivers/wifi_csi/Make.defs new file mode 100644 index 0000000000..0214faf0d3 --- /dev/null +++ b/os/drivers/wifi_csi/Make.defs @@ -0,0 +1,65 @@ +########################################################################### +# +# Copyright 2024 Samsung Electronics All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +# +########################################################################### +############################################################################ +# drivers/wifi_csi/Make.defs +# +# Copyright (C) 2013 Ken Pettit. All rights reserved. +# Author: Ken Pettit +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +ifeq ($(CONFIG_WIFI_CSI),y) + +# Include CSI driver + +CSRCS += wifi_csi.c + +# Include CSI driver support + +DEPPATH += --dep-path wifi_csi +VPATH += :wifi_csi + +endif + diff --git a/os/drivers/wifi_csi/wifi_csi.c b/os/drivers/wifi_csi/wifi_csi.c new file mode 100644 index 0000000000..6d79b800a2 --- /dev/null +++ b/os/drivers/wifi_csi/wifi_csi.c @@ -0,0 +1,562 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Debug ********************************************************************/ +/* Non-standard debug that may be enabled just for testing Wificsi */ + +#ifndef WIFI_CSI_MAX_DEVICE_PATH +#define WIFI_CSI_MAX_DEVICE_PATH 32 +#endif +#ifndef CONFIG_WIFICSI_DATA_READY_PRIO +#define CONFIG_WIFICSI_DATA_READY_PRIO 1 +#endif + +/**************************************************************************** + * Private Type Definitions + ****************************************************************************/ + +/* This structure describes the state of the upper half driver */ + +struct wifi_csi_upperhalf_s { + uint8_t crefs; /* The number of times the device has been opened */ + sem_t exclsem; /* Supports mutual exclusion */ + FAR struct wifi_csi_lowerhalf_s *dev; /* lower-half state */ + mqd_t usermq; /* User mode app's message queue */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int wifi_csi_open(FAR struct file *filep); +static int wifi_csi_close(FAR struct file *filep); +static ssize_t wifi_csi_read(FAR struct file *filep, FAR char *buffer, size_t buflen); +static int wifi_csi_ioctl(FAR struct file *filep, int cmd, unsigned long arg); + +static void wifi_csi_callback(FAR void *handle, uint16_t reason, char *buff, uint32_t csi_data_len); +static int wifi_csi_mq_open(FAR struct wifi_csi_upperhalf_s *upper); +static int wifi_csi_mq_close(FAR struct wifi_csi_upperhalf_s *upper); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_wifi_csiops = { + wifi_csi_open, /* open */ + wifi_csi_close, /* close */ + wifi_csi_read, /* read */ + 0, /* write */ + 0, /* seek */ + wifi_csi_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , 0 /* poll */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/************************************************************************************ + * Name: wifi_csi_open + * + * Description: + * This function is called whenever the Wificsi device is opened. + * + ************************************************************************************/ + +static int wifi_csi_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct wifi_csi_upperhalf_s *upper = inode->i_private; + uint8_t tmp; + int ret; + + csivdbg("crefs: %d\n", upper->crefs); + + /* Get exclusive access to the device structures */ + ret = sem_wait(&upper->exclsem); + if (ret < OK) { + ret = -errno; + goto errout; + } + + /* Increment the count of references to the device. If this the first + * time that the driver has been opened for this device, then initialize + * the device. + */ + tmp = upper->crefs + 1; + if (tmp == 0) { + /* More than 255 opens; uint8_t overflows to zero */ + + ret = -EMFILE; + goto errout_with_sem; + } + + /* Save the new open count on success */ + upper->crefs = tmp; + + /* Initialize usermq only when it is the first open */ + if (upper->crefs == 1) { + upper->usermq = (mqd_t)ERROR; + } + ret = OK; + +errout_with_sem: + sem_post(&upper->exclsem); + +errout: + return ret; +} + +/************************************************************************************ + * Name: wifi_csi_close + * + * Description: + * This function is called when the Wificsi device is closed. + * + ************************************************************************************/ + +static int wifi_csi_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct wifi_csi_upperhalf_s *upper = inode->i_private; + int ret = OK; + + csivdbg("crefs: %d\n", upper->crefs); + + /* Get exclusive access to the device structures */ + ret = sem_wait(&upper->exclsem); + if (ret < OK) { + ret = -errno; + goto errout; + } + + /* Decrement the references to the driver. If the reference count will + * decrement to 0, then uninitialize the driver. + */ + if (upper->crefs > 1) { + upper->crefs--; + } else { + /* There are no more references to the port */ + upper->crefs = 0; + } + +//errout_with_sem: + sem_post(&upper->exclsem); + +errout: + return ret; +} + +/************************************************************************************ + * Name: wifi_csi_read + * + * Description: + * A dummy read method. Some hardwares might support read. + * + ************************************************************************************/ +static ssize_t wifi_csi_read(FAR struct file *filep, FAR char *buffer, size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct wifi_csi_upperhalf_s *upper = inode->i_private; + FAR struct wifi_csi_lowerhalf_s *lower = upper->dev; + + if (lower->ops->read != NULL) { + return lower->ops->read(buffer, buflen); + } + return OK; +} + +/************************************************************************************ + * Name: wifi_csi_ioctl + * + * Description: + * The standard ioctl method. This is where ALL of the Wificsi work is done. + * + ************************************************************************************/ + +static int wifi_csi_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct wifi_csi_upperhalf_s *upper = inode->i_private; + FAR struct wifi_csi_lowerhalf_s *lower = upper->dev; + int ret; + // csivdbg("cmd: %d arg: %ld\n", cmd, arg); + + /* Get exclusive access to the device structures */ + ret = sem_wait(&upper->exclsem); + if (ret < OK) { + return ret; + } + + /* Handle built-in ioctl commands */ + switch (cmd) { + + case CSIIOC_START_CSI: { + csivdbg("CSIIOC_START_CSI\n"); + /* fisrt open message queue */ + ret = wifi_csi_mq_open(upper); + if (ret != OK) { + csidbg("MQ open failed, ret:%d\n", ret); + break; + } + /* now start lower */ + if (lower->ops->ioctl != NULL) { + csivdbg("Calling lower ioctl CSIIOC_START_CSI\n"); + ret = lower->ops->ioctl(cmd, arg); + if (ret != OK) { + csidbg("lower driver IOCTL fail: CSIIOC_START_CSI, ret:%d\n", ret); + if (wifi_csi_mq_open(upper) != OK) { + csidbg("MQ close failed."); + } + break; + } + } else { + csidbg("IOCTL not supported by lower driver\n"); + ret = -ENOSYS; + } + } + break; + + case CSIIOC_STOP_CSI: { + csivdbg("CSIIOC_STOP_CSI\n"); + /* first stop lower */ + if (lower->ops->ioctl != NULL) { + csivdbg("Calling lower ioctl CSIIOC_STOP_CSI\n"); + ret = lower->ops->ioctl(cmd, arg); + if (ret != OK) { + csidbg("lower driver IOCTL fail: CSIIOC_STOP_CSI, ret:%d\n", ret); + } + } else { + csivdbg("IOCTL not supported by lower driver\n"); + ret = -ENOSYS; + if (wifi_csi_mq_close(upper) != OK) { + csidbg("MQ close failed\n"); + } + break; + } + /* close message queue */ + ret = wifi_csi_mq_close(upper); + if (ret != OK) { + csidbg("MQ close failed, ret:%d\n", ret); + } + } + break; + + case CSIIOC_GET_MAC_ADDR: { + csivdbg("CSIIOC_GET_MAC_ADDR\n"); + if (!arg) { + csidbg("ERROR: Invalid mac info arg\n"); + ret = -EINVAL; + break; + } + csifw_mac_info *mac_info = (csifw_mac_info*)arg; + if (lower->ops->getmacaddr != NULL) { + ret = lower->ops->getmacaddr(mac_info->mac_addr); + if (ret != OK) { + csidbg("ERROR: Failed to get mac address, ret:%d\n", ret); + } + } else { + csidbg("ERROR: Unsupported operation: get mac\n"); + ret = -ENOSYS; + } + } + break; + + case CSIIOC_GET_DATA: { + if (!arg) { + csidbg("ERROR: invalid buffer arg\n"); + ret = -EINVAL; + break; + } + csi_driver_buffer_args_t *buf_arg = (csi_driver_buffer_args_t*)arg; + if (!buf_arg->buffer) { + csidbg("ERROR: invalid buffer ptr\n"); + ret = -EINVAL; + break; + } + if (lower->ops->getcsidata != NULL) { + ret = lower->ops->getcsidata(buf_arg->buffer, buf_arg->buflen); + /* length of data read is returned */ + if (ret <= OK) { + csidbg("ERROR: Failed to get csi data, ret:%d\n", ret); + } + } else { + csidbg("ERROR: Unsupported operation: getcsidata\n"); + ret = -ENOSYS; + } + } + break; + + /* Any unrecognized IOCTL commands might be platform-specific ioctl commands */ + default: { + if (lower->ops->ioctl != NULL) { + csivdbg("Forwarding ioctl cmd: %d arg: %ld\n", cmd, arg); + ret = lower->ops->ioctl(cmd, arg); + } else { + ret = -ENOSYS; + } + } + break; + } + sem_post(&upper->exclsem); + return ret; +} + +/**************************************************************************** + * Name: wifi_csi_error_handler + * + * Description: + * Send an CSI_CALLBACK_ERROR message to the client to indicate that the + * an error has occured. The lower-half driver initiates this + * call via its callback pointer to our upper-half driver. + * + ****************************************************************************/ + +static inline void wifi_csi_error_handler(FAR struct wifi_csi_upperhalf_s *upper, uint16_t reason) +{ + struct wifi_csi_msg_s msg; + + /* Send a error message to the user if a message queue is registered */ + if (upper->usermq != (mqd_t)ERROR) { + /* We are always sending CSI_MSG_ERROR error msg since it is the + only type of error that we handle now. If new error scenario + is required, then we need to handle here */ + msg.msgId = CSI_MSG_ERROR; + mq_send(upper->usermq, (FAR const char *)&msg, sizeof(msg), MQ_PRIO_MAX); + } +} + +/**************************************************************************** + * Name: wifi_csi_data_ready + * + * Description: + * Send an CSI_CALLBACK_DATA_READY message to the client to indicate that the + * wificsi data is ready. The lower-half driver initiates this + * call via its callback pointer to our upper-half driver. + * + ****************************************************************************/ +static inline void wifi_csi_data_ready(FAR struct wifi_csi_upperhalf_s *upper, char *buff, uint32_t csi_data_len) +{ + /* Send a data ready message to the user if a message queue is registered */ + if (upper->usermq == (mqd_t)ERROR) { + csidbg("Mq handle invalid.\n"); + return; + } + /* send message */ + struct wifi_csi_msg_s msg; + msg.msgId = CSI_MSG_DATA_READY_CB; + msg.data_len = csi_data_len; + int ret = mq_send(upper->usermq, (FAR const char *)&msg, sizeof(msg), CONFIG_WIFICSI_DATA_READY_PRIO); + if (ret != OK) { + /* This error message will also be sent when message queue is full */ + csivdbg("ERROR: mq_send failed for mq: %d, errno: %d, ret: %d\n", upper->usermq, get_errno(), ret); + } +} + +/**************************************************************************** + * Name: wifi_csi_callback + * + * Description: + * Provides a callback interface for lower-half drivers to call to the + * upper-half for buffer dequeueing, error reporting, etc. + * + * Input parameters: + * priv - Private context data owned by the upper-half + * reason - The reason code for the callback + * apb - A pointer to the previsously enqueued ap_buffer_s + * status - Status information associated with the callback + * + * Returned Value: + * None + * + * Assumptions: + * This function may be called from an interrupt handler. + * + ****************************************************************************/ + +static void wifi_csi_callback(FAR void *handle, uint16_t reason, char *buff, uint32_t csi_data_len) +{ + FAR struct wifi_csi_upperhalf_s *upper = (FAR struct wifi_csi_upperhalf_s *)handle; + + /* Perform operation based on reason code */ + switch (reason) { + + /* Lower-half I/O error occurred */ + case CSI_CALLBACK_ERROR: { + wifi_csi_error_handler(upper, reason); + } + break; + + /* Lower-half driver has csi data ready */ + case CSI_CALLBACK_DATA_READY: { + /* Send a data ready message to the user if a message queue is registered */ + wifi_csi_data_ready(upper, buff, csi_data_len); + } + break; + + default: { + csidbg("ERROR: Unknown callback reason code %d\n", reason); + break; + } + } +} + +/************************************************************************************ + * Name: wifi_csi_mq_open + * + * Description: + * Open message queue for communication with app/framework layers. + * + ************************************************************************************/ + +static int wifi_csi_mq_open(FAR struct wifi_csi_upperhalf_s *upper) +{ + if (upper->usermq == (mqd_t)ERROR) { + struct mq_attr attr_mq; + attr_mq.mq_maxmsg = CSI_MQ_MSG_COUNT; + attr_mq.mq_msgsize = sizeof(struct wifi_csi_msg_s); + attr_mq.mq_flags = 0; + csidbg("\nMQ_NAME: %s, MSG_SIZE: %zu, MQ_TYPE: NON_BLOCKING\n", CSI_MQ_NAME, attr_mq.mq_msgsize); + /* Non_blocking message queue */ + upper->usermq = mq_open(CSI_MQ_NAME, O_RDWR | O_CREAT | O_NONBLOCK, 0666, &attr_mq); + if (upper->usermq == (mqd_t)ERROR) { + csidbg("Failed to open mq errno: %d\n", get_errno()); + return ERROR; + } + csidbg("CSI driver MQ open success.\n"); + } else { + csidbg("CSI driver MQ already open.\n"); + } + return OK; +} + +/************************************************************************************ + * Name: wifi_csi_mq_close + * + * Description: + * Open message queue for communication with app/framework layers. + * + ************************************************************************************/ + +static int wifi_csi_mq_close(FAR struct wifi_csi_upperhalf_s *upper) +{ + /* check if mq handle valid */ + if (upper->usermq == (mqd_t)ERROR) { + csidbg("MQ already closed"); + return OK; + } + /* close mq */ + int ret = mq_close(upper->usermq); + if (ret != OK) { + csidbg("ERROR: mq_close failed, errno: %d\n", get_errno()); + } else { + csidbg("MQ close success\n"); + } + upper->usermq = (mqd_t)ERROR; + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: wifi_csi_register + * + * Description: + * This function binds an instance of a "lower half" wificsi driver with the + * "upper half" Wificsi device and registers that device so that can be used + * by application code. + * + * When this function is called, the "lower half" driver should be in the + * reset state (as if the shutdown() method had already been called). + * + * Input parameters: + * path - The full path to the driver to be registers in the NuttX pseudo- + * filesystem. The recommended convention is to name Wificsi drivers + * based on the function they provide, such as "/dev/pcm0", "/dev/mp31", + * etc. + * dev - A pointer to an instance of lower half wificsi driver. This instance + * is bound to the Wificsi driver and must persists as long as the driver + * persists. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int wifi_csi_register(FAR const char *name, FAR struct wifi_csi_lowerhalf_s *dev) +{ + FAR struct wifi_csi_upperhalf_s *upper; + char* path; + static bool dev_wifi_csi_created = false; + int ret; +#ifndef CONFIG_WIFICSI_CUSTOM_DEV_PATH + path = "/dev/wificsi"; +#endif + path = CONFIG_WIFICSI_CUSTOM_DEV_PATH; + + /* Allocate the upper-half data structure */ + upper = (FAR struct wifi_csi_upperhalf_s *)kmm_zalloc(sizeof(struct wifi_csi_upperhalf_s)); + if (!upper) { + csidbg("ERROR: Allocation failed\n"); + return -ENOMEM; + } + + /* Initialize the Wificsi device structure (it was already zeroed by kmm_zalloc()) */ + sem_init(&upper->exclsem, 0, 1); + upper->dev = dev; + + /* Give the lower-half a context to the upper half */ + dev->upper_cb = wifi_csi_callback; + dev->priv = upper; + + csidbg("Registering %s\n", path); + return register_driver(path, &g_wifi_csiops, 0666, upper); +} + diff --git a/os/include/debug.h b/os/include/debug.h index f428d0f742..36dac6aeb5 100644 --- a/os/include/debug.h +++ b/os/include/debug.h @@ -651,6 +651,24 @@ int get_errno(void); #define sellvdbg(...) #endif +#ifdef CONFIG_DEBUG_WIFICSI_ERROR +#define csidbg(format, ...) dbg(format, ##__VA_ARGS__) +#else +#define csidbg(...) +#endif + +#ifdef CONFIG_DEBUG_WIFICSI_WARN +#define csiwdbg(format, ...) wdbg(format, ##__VA_ARGS__) +#else +#define csiwdbg(...) +#endif + +#ifdef CONFIG_DEBUG_WIFICSI_INFO +#define csivdbg(format, ...) vdbg(format, ##__VA_ARGS__) +#else +#define csivdbg(...) +#endif + /****************************************/ /* Framework specific debug */ /****************************************/ @@ -1521,6 +1539,24 @@ int get_errno(void); #define sellvdbg (void) #endif +#ifdef CONFIG_DEBUG_WIFICSI_ERROR +#define csidbg dbg +#else +#define csidbg (void) +#endif + +#ifdef CONFIG_DEBUG_WIFICSI_WARN +#define csiwdbg wdbg +#else +#define csiwdbg (void) +#endif + +#ifdef CONFIG_DEBUG_WIFICSI_INFO +#define csivdbg vdbg +#else +#define csivdbg (void) +#endif + /****************************************/ /* Framework specific debug */ /****************************************/ diff --git a/os/include/tinyara/fs/ioctl.h b/os/include/tinyara/fs/ioctl.h index 374dd55a69..32fa3dc3bf 100644 --- a/os/include/tinyara/fs/ioctl.h +++ b/os/include/tinyara/fs/ioctl.h @@ -106,6 +106,7 @@ #define _PMBASE (0x2b00) /* pm ioctl commands */ #define _TESTIOCBASE (0xfe00) /* KERNEL TEST DRV module ioctl commands */ #define _MIPIDSIBASE (0x3900) /* Mipidsi device ioctl commands */ +#define _CSIIOCBASE (0x3a00) /* Wifi CSI ioctl commands */ @@ -334,6 +335,12 @@ #define _QEIOCVALID(c) (_IOC_TYPE(c) == _QEIOCBASE) #define _QEIOC(nr) _IOC(_QEIOCBASE, nr) +/* Wifi CSI driver ioctl definitions *************************************/ +/* (see tinyara/wifi_csi/wifi_csi.h) */ + +#define _CSIIOCVALID(c) (_IOC_TYPE(c) == _CSIIOCBASE) +#define _CSIIOC(nr) _IOC(_CSIIOCBASE, nr) + /* Audio driver ioctl definitions *************************************/ /* (see tinyara/audio/audio.h) */ diff --git a/os/include/tinyara/wifi_csi/wifi_csi.h b/os/include/tinyara/wifi_csi/wifi_csi.h new file mode 100644 index 0000000000..c9ffad998a --- /dev/null +++ b/os/include/tinyara/wifi_csi/wifi_csi.h @@ -0,0 +1,278 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ +/**************************************************************************** + * include/tinyara/wifi_csi/wifi_csi.h + * + * Copyright (C) 2013 Ken Pettit. All rights reserved. + * Author: Ken Pettit + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_TINYARA_WIFI_CSI_H +#define __INCLUDE_TINYARA_WIFI_CSI_H + +/* For the purposes of this driver, an Wificsi device is any device that + * sends wificsi data. + * + * The Wificsi driver is split into two parts: + * + * 1) An "upper half", generic driver that provides the comman Wificsi interface + * to application level code, and + * 2) A "lower half", platform-specific driver that implements the low-level + * controls to configure and communicate with the wificsi device(s). + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_CSI_BUFF_LEN 536 /*assuming max subcarrier: 244 (1019 = 43 + 244*4), as per rtk pdf */ +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Configuration ************************************************************/ +/* CONFIG_WIFI_CSI - Enables Wificsi driver support + * CONFIG_DEBUG_WIFICSI - If enabled (with CONFIG_DEBUG_FEATURES and, optionally, + * CONFIG_DEBUG_VERBOSE), this will generate output that can be used to + * debug Wificsi drivers. + */ + +/* IOCTL Commands ***********************************************************/ +/* The Wificsi module uses a standard character driver framework. However, a + * lot of the Wificsi driver functionality is configured via a device control + * interface, such as sampling rate, volume, data format, etc. + * The Wificsi ioctl commands are listed below: + * + * CSIIOC_PARAM_SETZERO - Set the wifi csi config params zero. + * + * ioctl argument: None + * + * CSIIOC_SET_CONFIG - Set wificsi device config for the specified mode + * + * ioctl argument: Pointer to the csi_action_param_t structure which specifies + * the config params. + * + * CSIIOC_REGISTER_CALLBACK - Register callback to receive wifi csi data ready event. + * + * ioctl argument: None + * + * CSIIOC_UNREGISTER_CALLBACK - Unregister callback to receive wifi csi data ready event + * + * ioctl argument: None + * + * CSIIOC_GET_MAC_ADDR - Get Wifi csi device MAC address + * + * ioctl argument: Pointer to csifw_mac_info structure. + * + * CSIIOC_REGISTERMQ - Register message queue to receive message from kernel. + * + * ioctl argument: Pointer to message queue descriptor structure. + * + * CSIIOC_UNREGISTERMQ - Unregsiter message queue. + * + * ioctl argument: Pointer to message queue descriptor structure. + * + * CSIIOC_GET_DATA - Get csi data. + * + * ioctl argument: Pointer to csi_driver_buffer_args_t structure. + */ + +#define CSIIOC_PARAM_SETZERO _CSIIOC(1) +#define CSIIOC_SET_CONFIG _CSIIOC(2) +#define CSIIOC_START_CSI _CSIIOC(3) +#define CSIIOC_STOP_CSI _CSIIOC(4) +#define CSIIOC_GET_MAC_ADDR _CSIIOC(5) +#define CSIIOC_GET_DATA _CSIIOC(6) + +/* CSI Operation Callback Reasons ***************************************************/ + +#define CSI_CALLBACK_ERROR 0x00 +#define CSI_CALLBACK_DATA_READY 0x01 + +/* Wifi CSI Message Queue message name */ + +#define CSI_MQ_NAME "WIFI_CSI_DRIVER_Q" +#define CSI_MQ_MSG_COUNT 5 + +/* Standard Wifi CSI Message Queue message IDs */ + +#define CSI_MSG_ERROR 0 +#define CSI_MSG_DATA_READY_CB 1 +#define CSI_MSG_USER 64 + +/**************************************************************************** + * Public Types + ****************************************************************************/ +typedef struct CSI_DRIVER_BUFFER_ARGS_T { + size_t buflen; + char *buffer; +}csi_driver_buffer_args_t; + +/* Typedef for lower-level to upper-level callback for buffer dequeuing */ + +typedef CODE void (*wifi_csi_callback_t)(FAR void *priv, uint16_t reason, char *buff, uint32_t csi_data_len); + + +/* Structure defining the messages passed to a listening wificsi thread + * for notifying errors and data ready event. + */ +struct wifi_csi_msg_s { + uint16_t msgId; /* Message ID */ + uint16_t data_len; /* CSI Data Length (inclusive of header) */ +}; + +struct wifi_csi_lowerhalf_s; + +/* This structure is a set of callback functions used to call from the upper- + * half, generic Wificsi driver into lower-half, platform-specific logic that + * supports the low-level functionality. + */ +struct wifi_csi_ops_s { + /* Lower-half logic may support platform-specific read commands */ + + CODE int (*read)(FAR char *buffer, size_t buflen); + + /* Lower-half logic may support platform-specific ioctl commands */ + + CODE int (*ioctl)(int cmd, unsigned long arg); + + /* Get mac address of the wificsi device */ + + CODE int (*getmacaddr)(char *mac_addr); + + /* Get csi data */ + + CODE int (*getcsidata)(char *buffer, size_t buflen); + +}; + +/* This structure is the generic form of state structure used by lower half + * Wificsi driver. This state structure is passed to the Wificsi driver when the + * driver is initialized. Then, on subsequent callbacks into the lower half + * Wificsi logic, this structure is provided so that the Wificsi logic can + * maintain state information. + * + * Normally that Wificsi logic will have its own, custom state structure + * that is simply cast to struct wifi_csi_lowerhalf_s. In order to perform such + * casts, the initial fields of the custom state structure match the initial + * fields of the following generic Wificsi state structure. + */ + +struct wifi_csi_lowerhalf_s { + /* The first field of this state structure must be a pointer to the Wificsi + * callback structure: + */ + + FAR const struct wifi_csi_ops_s *ops; + + /* The bind data to the upper-half driver used for callbacks of dequeuing + * buffer, reporting asynchronous event, reporting errors, etc. + */ + + FAR wifi_csi_callback_t upper_cb; + + /* The private opaque pointer to be passed to upper-layer during callbacks */ + + FAR void *priv; /* contains upperhalf instance ptr: wifi_csi_upperhalf_s */ + + /* The custom Wificsi device state structure may include additional fields + * after the pointer to the Wificsi callback structure. + */ +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * "Upper-Half" Wificsi Driver Interfaces + ****************************************************************************/ +/**************************************************************************** + * Name: wifi_csi_register + * + * Description: + * This function binds an instance of a "lower half" Wificsi driver with the + * "upper half" Wificsi device and registers that device so that can be used + * by application code. + * + * When this function is called, the "lower half" driver should be in the + * reset state (as if the shutdown() method had already been called). + * + * Input parameters: + * name - The name of the wificsi device. This name will be used to generate + * a full path to the driver in the format "/dev/wificsi/[name]" in the TizenRT + * filesystem (i.e. the path "/dev/wificsi" will be prepended to the supplied + * device name. The recommended convention is to name Wificsi drivers + * based on the type of functionality they provide, such as "/dev/wificsi/pcm0", + * "/dev/wificsi/midi0", "/dev/wificsi/mp30, etc. + * dev - A pointer to an instance of lower half wificsi driver. This instance + * is bound to the Wificsi driver and must persists as long as the driver + * persists. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int wifi_csi_register(FAR const char *name, FAR struct wifi_csi_lowerhalf_s *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_TINYARA_WIFI_CSI_H */ + diff --git a/os/include/tinyara/wifi_csi/wifi_csi_struct.h b/os/include/tinyara/wifi_csi/wifi_csi_struct.h new file mode 100644 index 0000000000..70a1ca13ee --- /dev/null +++ b/os/include/tinyara/wifi_csi/wifi_csi_struct.h @@ -0,0 +1,84 @@ +/**************************************************************************** + * + * Copyright 2024 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ +#ifndef __INCLUDE_TINYARA_WIFI_CSI_STRUCT_H +#define __INCLUDE_TINYARA_WIFI_CSI_STRUCT_H + +#ifdef __cplusplus /* If this is a C++ compiler, use C linkage */ + extern "C" { +#endif + +typedef struct { + char mac_addr[6]; +} csifw_mac_info; + +typedef enum CSI_GROUP_NUM_T{ + CSI_GROUP_NUM_1 = 0, /**< per tone */ + CSI_GROUP_NUM_2, /**< per 2tone */ + CSI_GROUP_NUM_4, /**< per 4tone */ + CSI_GROUP_NUM_16, /**< per 16tone */ + CSI_GROUP_NUM_MAX +} csi_group_num_t; + +typedef enum CSI_MODE_T{ + CSI_MODE_NORMAL = 0, ///< normal mode + CSI_MODE_NDP, ///< ndp mode + CSI_MODE_RX_RESP, ///< rx rsp mode + CSI_MODE_MAX, ///< max mode +} csi_mode_t; + +typedef enum CSI_ACTION_T{ + CSI_ACT_EN, /**< enable or disable csi func */ + CSI_ACT_CFG, /**< config csi parameters */ + CSI_ACT_MAX +} csi_action_t; + +typedef enum CSI_ACCURACY_T{ + CSI_ACCU_1BYTE = 0, /**< CSI_ACCU_1BYTE: S(8,4) */ + CSI_ACCU_2BYTES, /**< CSI_ACCU_2BYTE: S(16,12) */ + CSI_ACCU_MAX +} csi_accuracy_t; + +typedef enum CSI_CH_OPT_T{ + CSI_CH_LEGACY = 0, /**< legacy part(L-LTF) channel estmation result */ + CSI_CH_NON_LEGACY, /**< non-legacy(HT-LTF) part */ + CSI_CH_MAX +} csi_ch_opt_t; + +typedef enum { + CSI_ALG_LS = 0, + CSI_ALG_SMOTHING, + CSI_ALG_MAX +} rtw_csi_alg_opt; + +typedef struct CSI_ACTION_PARAM_T{ + csi_group_num_t group_num; + csi_mode_t mode; + csi_action_t act; + csi_accuracy_t accuracy; + rtw_csi_alg_opt alg_opt; + csi_ch_opt_t ch_opt; + unsigned char enable; + unsigned char trig_period; /* unit:ms*/ + unsigned char data_rate; +} csi_action_param_t; + +#ifdef __cplusplus /* If this is a C++ compiler, end C linkage */ + } +#endif +#endif /* __INCLUDE_TINYARA_WIFI_CSI_STRUCT_H */ +