diff options
-rw-r--r-- | .clang-format | 21 | ||||
-rw-r--r-- | Android.bp | 83 | ||||
-rw-r--r-- | LICENSE | 205 | ||||
-rw-r--r-- | MODULE_LICENSE_APACHE2 | 0 | ||||
-rw-r--r-- | OWNERS | 2 | ||||
-rw-r--r-- | PREUPLOAD.cfg | 6 | ||||
-rw-r--r-- | TEST_MAPPING | 12 | ||||
-rw-r--r-- | main.cc | 62 | ||||
-rw-r--r-- | util/wmediumd_gen_config.c | 51 | ||||
-rw-r--r-- | wmediumd/.clang-format | 22 | ||||
-rw-r--r-- | wmediumd/api.h | 51 | ||||
-rw-r--r-- | wmediumd/config.c | 37 | ||||
-rw-r--r-- | wmediumd/config.h | 1 | ||||
-rw-r--r-- | wmediumd/grpc.h | 42 | ||||
-rw-r--r-- | wmediumd/inc/usfstl/list.h | 14 | ||||
-rw-r--r-- | wmediumd/inc/usfstl/vhostproto.h | 1 | ||||
-rw-r--r-- | wmediumd/lib/vhost.c | 5 | ||||
-rw-r--r-- | wmediumd/list.h | 53 | ||||
-rw-r--r-- | wmediumd/pmsr.c | 221 | ||||
-rw-r--r-- | wmediumd/pmsr.h | 85 | ||||
-rw-r--r-- | wmediumd/wmediumd.c | 487 | ||||
-rw-r--r-- | wmediumd/wmediumd.h | 37 | ||||
-rw-r--r-- | wmediumd_server/wmediumd.proto | 30 | ||||
-rw-r--r-- | wmediumd_server/wmediumd_server.cc | 147 | ||||
-rw-r--r-- | wmediumd_server/wmediumd_server.h | 19 |
25 files changed, 1610 insertions, 84 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..c8e6173 --- /dev/null +++ b/.clang-format @@ -0,0 +1,21 @@ +# +# Copyright (C) 2023 The Android Open Source Project +# +# 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. +# + +# See https://clang.llvm.org/docs/ClangFormatStyleOptions.html for the +# various options that can be configured and for the definitions of each +# of the below options. + +BasedOnStyle: Google @@ -6,6 +6,7 @@ license { name: "external_wmediumd_license", visibility: [":__subpackages__"], license_kinds: [ + "SPDX-license-identifier-Apache-2.0", "SPDX-license-identifier-GPL-2.0", ], license_text: [ @@ -24,13 +25,76 @@ cc_binary_host { "libconfig", ], visibility: [ - "//device/google/cuttlefish/build", + "//device/google/cuttlefish/build", + ], +} + +cc_library_host_static { + name: "libwmediumd_server", + shared_libs: [ + "libgflags", + "libgrpc++_unsecure", + "libprotobuf-cpp-full", + ], + cflags: [ + "-Wno-unused-parameter", + ], + generated_headers: [ + "WmediumdServerProto_h", + ], + generated_sources: [ + "WmediumdServerProto_cc", + ], + export_generated_headers: [ + "WmediumdServerProto_h", + ], +} + +filegroup { + name: "WmediumdServerProto", + srcs: [ + "wmediumd_server/wmediumd.proto", + ], +} + +// TODO(278065934): Add a module type for grpc service +genrule { + name: "WmediumdServerProto_h", + tools: [ + "aprotoc", + "protoc-gen-grpc-cpp-plugin", + ], + cmd: "$(location aprotoc) -Iexternal/wmediumd/wmediumd_server -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)", + srcs: [ + ":WmediumdServerProto", + ], + out: [ + "wmediumd.grpc.pb.h", + "wmediumd.pb.h", + ], +} + +genrule { + name: "WmediumdServerProto_cc", + tools: [ + "aprotoc", + "protoc-gen-grpc-cpp-plugin", + ], + cmd: "$(location aprotoc) -Iexternal/wmediumd/wmediumd_server -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)", + srcs: [ + ":WmediumdServerProto", + ], + out: [ + "wmediumd.grpc.pb.cc", + "wmediumd.pb.cc", ], } cc_binary_host { name: "wmediumd", srcs: [ + "main.cc", + "wmediumd_server/wmediumd_server.cc", "wmediumd/*.c", "wmediumd/lib/*.c", ], @@ -48,15 +112,22 @@ cc_binary_host { "-Wno-gnu-variable-sized-type-not-at-end", "-Wno-unused-function", ], - static_libs: [ + shared_libs: [ + "libbase", "libnl", + "libgflags", + "libgrpc++_unsecure", + "libprotobuf-cpp-full", + ], + static_libs: [ "libconfig", + "libgrpc++_reflection", + "libwmediumd_server", ], + cpp_std: "c++17", visibility: [ - "//device/google/cuttlefish/build", + "//device/google/cuttlefish/build", ], - stl: "none", - static_executable: true, } cc_binary_host { @@ -68,7 +139,7 @@ cc_binary_host { "wmediumd/inc", ], visibility: [ - "//device/google/cuttlefish/build", + "//device/google/cuttlefish/build", ], stl: "none", static_executable: true, @@ -1,3 +1,208 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. + +-------------------------------------------------------------------------------- + GNU GENERAL PUBLIC LICENSE Version 2, June 1991 diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/MODULE_LICENSE_APACHE2 @@ -2,3 +2,5 @@ jeongik@google.com jaeman@google.com sundongahn@google.com jooyung@google.com +jaewan@google.com +seungjaeyoo@google.com diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg new file mode 100644 index 0000000..0b46545 --- /dev/null +++ b/PREUPLOAD.cfg @@ -0,0 +1,6 @@ +[Builtin Hooks] +bpfmt = true +clang_format = true + +[Builtin Hooks Options] +clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
\ No newline at end of file diff --git a/TEST_MAPPING b/TEST_MAPPING new file mode 100644 index 0000000..aea06ad --- /dev/null +++ b/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit-large": [ + { + "name": "CtsWifiTestCases", + "options": [ + { + "exclude-annotation": "android.net.wifi.cts.VirtualDeviceNotSupported" + } + ] + } + ] +} @@ -0,0 +1,62 @@ +/* + * + * Copyright (C) 2023 The Android Open Source Project + * + * 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 <android-base/logging.h> +#include <android-base/strings.h> +#include <sys/eventfd.h> +#include <sys/msg.h> +#include <wmediumd/wmediumd.h> +#include <wmediumd_server/wmediumd_server.h> + +#include <string> +#include <thread> +#include <vector> + +constexpr char kGrpcUdsPathOption[] = "--grpc_uds_path="; + +int main(int argc, char* argv[]) { + std::vector<char*> wmediumd_args; + std::string grpc_uds_path; + for (int i = 0; i < argc; i++) { + if (android::base::StartsWith(argv[i], kGrpcUdsPathOption)) { + std::string current_arg(argv[i]); + grpc_uds_path = current_arg.substr(strlen(kGrpcUdsPathOption)); + } else { + wmediumd_args.push_back(argv[i]); + } + } + + int fd = eventfd(0, 0); + int msq_id = msgget(IPC_PRIVATE, IPC_CREAT | 0666); + + std::thread wmediumd_server_thread; + if (!grpc_uds_path.empty()) { + wmediumd_server_thread = + std::thread(RunWmediumdServer, grpc_uds_path, fd, msq_id); + } + + wmediumd_main(wmediumd_args.size(), wmediumd_args.data(), fd, msq_id); + + if (!grpc_uds_path.empty()) { + wmediumd_server_thread.join(); + } + + msgctl(msq_id, IPC_RMID, 0); + close(fd); + return 0; +}
\ No newline at end of file diff --git a/util/wmediumd_gen_config.c b/util/wmediumd_gen_config.c index 08d1d69..8984e7e 100644 --- a/util/wmediumd_gen_config.c +++ b/util/wmediumd_gen_config.c @@ -12,10 +12,17 @@ #define MAC_ADDR_LEN 6 #define STR_MAC_ADDR_LEN 17 -#define OPENWRT_MAC_ADDR "02:00:00:00:00:00" +#define OPENWRT_MAC_ADDR_1 "42:00:00:00:00:00" +#define OPENWRT_MAC_ADDR_2 "42:00:00:00:01:00" + +#define TX_POWER_DEFAULT 10 #define APPEND_LAST -1 +#define DEFAULT_RADIO_COUNT 2 +#define DEFAULT_CUTTLEFISH_INSTANCE_COUNT 64 +#define DEFAULT_MAC_PREFIX 5554 + #define PREVENT_MULTIPLE_OPTION(var, zero_val) \ do { \ if ((var) != (zero_val)) { \ @@ -59,6 +66,33 @@ int add_cuttlefish_mac_addresses(config_setting_t *ids, int mac_prefix, return 0; } +int add_cuttlefish_path_loss_model(config_setting_t *model, + int instance_count) { + config_setting_t *type = + config_setting_add(model, "type", CONFIG_TYPE_STRING); + config_setting_set_string(type, "path_loss"); + + config_setting_t *model_name = + config_setting_add(model, "model_name", CONFIG_TYPE_STRING); + config_setting_set_string(model_name, "free_space"); + + config_setting_t *positions = + config_setting_add(model, "positions", CONFIG_TYPE_LIST); + config_setting_t *tx_powers = + config_setting_add(model, "tx_powers", CONFIG_TYPE_ARRAY); + + for (int idx = 0; idx < instance_count; ++idx) { + config_setting_t *position = + config_setting_add(positions, NULL, CONFIG_TYPE_LIST); + config_setting_set_float_elem(position, APPEND_LAST, 0.0); + config_setting_set_float_elem(position, APPEND_LAST, 0.0); + + config_setting_set_float_elem(tx_powers, APPEND_LAST, TX_POWER_DEFAULT); + } + + return 0; +} + bool valid_mac_addr(const char *mac_addr) { if (strlen(mac_addr) != STR_MAC_ADDR_LEN) return false; @@ -167,7 +201,8 @@ int main(int argc, char **argv) { config_setting_add(ifaces, "count", CONFIG_TYPE_INT); config_setting_t *ids = config_setting_add(ifaces, "ids", CONFIG_TYPE_ARRAY); - config_setting_set_string_elem(ids, APPEND_LAST, OPENWRT_MAC_ADDR); + config_setting_set_string_elem(ids, APPEND_LAST, OPENWRT_MAC_ADDR_1); + config_setting_set_string_elem(ids, APPEND_LAST, OPENWRT_MAC_ADDR_2); FILE *output = stdout; char *out_path = NULL; @@ -233,18 +268,18 @@ int main(int argc, char **argv) { } } - /* Use default radio count if not specified */ + /* Use default values if not specified */ if (radio_count == -1) { - radio_count = 2; + radio_count = DEFAULT_RADIO_COUNT; } if (cuttlefish_instance_count == -1) { - cuttlefish_instance_count = 16; + cuttlefish_instance_count = DEFAULT_CUTTLEFISH_INSTANCE_COUNT; } if (mac_prefix == -1) { - mac_prefix = 5554; + mac_prefix = DEFAULT_MAC_PREFIX; } if (add_cuttlefish_mac_addresses(ids, mac_prefix, cuttlefish_instance_count, @@ -253,6 +288,10 @@ int main(int argc, char **argv) { print_help(-1); } + config_setting_t *model = + config_setting_add(root, "model", CONFIG_TYPE_GROUP); + add_cuttlefish_path_loss_model(model, config_setting_length(ids)); + config_setting_set_int(count, config_setting_length(ids)); if (out_path != NULL) { diff --git a/wmediumd/.clang-format b/wmediumd/.clang-format new file mode 100644 index 0000000..c7bf22d --- /dev/null +++ b/wmediumd/.clang-format @@ -0,0 +1,22 @@ +# +# Copyright (C) 2023 The Android Open Source Project +# +# 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. +# + +# See https://clang.llvm.org/docs/ClangFormatStyleOptions.html for the +# various options that can be configured and for the definitions of each +# of the below options. + +# Upstream follows other formatting rule. Do not apply clang-format in this dir. +BasedOnStyle: None diff --git a/wmediumd/api.h b/wmediumd/api.h index ba507e4..c887755 100644 --- a/wmediumd/api.h +++ b/wmediumd/api.h @@ -70,6 +70,21 @@ enum wmediumd_message { WMEDIUMD_MSG_STOP_PCAP, WMEDIUMD_MSG_STATIONS_LIST, + + /* + * Set position of station. + */ + WMEDIUMD_MSG_SET_POSITION, + + /* + * Set LCI of station + */ + WMEDIUMD_MSG_SET_LCI, + + /* + * Set CIVIC loc of station + */ + WMEDIUMD_MSG_SET_CIVICLOC, }; struct wmediumd_message_header { @@ -109,7 +124,7 @@ struct wmediumd_tx_start { }; #pragma pack(push, 1) - struct wmediumd_set_snr { +struct wmediumd_set_snr { /* MAC address of node 1 */ uint8_t node1_mac[6]; /* MAC address of node 2 */ @@ -136,6 +151,14 @@ struct wmediumd_station_info { double x; double y; + /* + * Offsets to the null-terminating string data. + * They point outside of the struct wmediumd_station_info, + * and even struct wmediumd_station_infos for multiple stations. + */ + int lci_offset; + int civicloc_offset; + int tx_power; }; @@ -143,6 +166,32 @@ struct wmediumd_station_infos { uint32_t count; struct wmediumd_station_info stations[0]; }; + +struct wmediumd_set_position { + /* MAC address */ + uint8_t mac[6]; + /* X position of station */ + double x; + /* Y position of station */ + double y; +}; + +struct wmediumd_set_lci { + /* MAC address */ + uint8_t mac[6]; + + /* LCI */ + char lci[0]; +}; + +struct wmediumd_set_civicloc { + /* MAC address */ + uint8_t mac[6]; + + /* CIVIC location */ + char civicloc[0]; +}; + #pragma pack(pop) #endif /* _WMEDIUMD_API_H */ diff --git a/wmediumd/config.c b/wmediumd/config.c index fc4986a..e0317ec 100644 --- a/wmediumd/config.c +++ b/wmediumd/config.c @@ -32,6 +32,14 @@ #include "wmediumd.h" #include "config.h" +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + static void string_to_mac_address(const char *str, u8 *addr) { int a[ETH_ALEN]; @@ -110,7 +118,7 @@ static int calc_path_loss_free_space(void *model_param, * https://en.wikipedia.org/wiki/Free-space_path_loss */ PL = 20.0 * log10(4.0 * M_PI * d * FREQ_1CH / SPEED_LIGHT); - return PL; + return MAX(PL, 0); } /* * Calculate path loss based on a log distance model @@ -146,7 +154,7 @@ static int calc_path_loss_log_distance(void *model_param, */ PL = PL0 + 10.0 * param->path_loss_exponent * log10(d) + param->Xg; - return PL; + return MAX(PL, 0); } /* * Calculate path loss based on a itu model @@ -176,23 +184,23 @@ static int calc_path_loss_itu(void *model_param, * nFLOORS: number of floors */ PL = 20.0 * log10(FREQ_1CH) + N * log10(d) + param->LF * param->nFLOORS - 28; - return PL; + return MAX(PL, 0); } static void recalc_path_loss(struct wmediumd *ctx) { - int start, end, path_loss; - + int start, end, tx_power, path_loss, signal; for (start = 0; start < ctx->num_stas; start++) { for (end = 0; end < ctx->num_stas; end++) { - if (start == end) + if (start == end) { continue; + } - path_loss = ctx->calc_path_loss(ctx->path_loss_param, - ctx->sta_array[end], ctx->sta_array[start]); - ctx->snr_matrix[ctx->num_stas * start + end] = - ctx->sta_array[start]->tx_power - path_loss - - NOISE_LEVEL; + tx_power = ctx->sta_array[start]->tx_power; + path_loss = ctx->calc_path_loss(ctx->path_loss_param, ctx->sta_array[end], ctx->sta_array[start]); + // Test breakage exists in WifiStatsTests when signal is not negative value. + signal = MIN(tx_power - path_loss, -1); + ctx->snr_matrix[ctx->num_stas * start + end] = signal - NOISE_LEVEL; } } } @@ -695,8 +703,15 @@ int clear_config(struct wmediumd *ctx) { struct station, list); list_del(&station->list); + + free(station->lci); + free(station->civicloc); free(station); } return 0; } + +void calc_path_loss(struct wmediumd *ctx) { + recalc_path_loss(ctx); +}
\ No newline at end of file diff --git a/wmediumd/config.h b/wmediumd/config.h index 471ffe1..feac4c2 100644 --- a/wmediumd/config.h +++ b/wmediumd/config.h @@ -24,6 +24,7 @@ #ifndef CONFIG_H_ #define CONFIG_H_ +void calc_path_loss(struct wmediumd *ctx); int clear_config(struct wmediumd *ctx); int validate_config(const char* file); int load_config(struct wmediumd *ctx, const char *file, const char *per_file); diff --git a/wmediumd/grpc.h b/wmediumd/grpc.h new file mode 100644 index 0000000..69084f2 --- /dev/null +++ b/wmediumd/grpc.h @@ -0,0 +1,42 @@ +/* + * + * Copyright (C) 2023 The Android Open Source Project + * + * 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. + * + */ + + +#define GRPC_MSG_BUF_MAX 1024 + +// Do not use_zero, the type of the message queue should be positive value. +enum wmediumd_grpc_type { + GRPC_REQUEST = 1, + GRPC_RESPONSE, +}; + +// Do not use zero, writing zero to eventfd doesn't throw an event. +enum wmediumd_grpc_request_type { + REQUEST_SET_POSITION = 1, +}; + +// Do not use zero, writing zero to eventfd doesn't throw an event. +enum wmediumd_grpc_response_type { + RESPONSE_INVALID = 1, + RESPONSE_ACK, +}; + +struct wmediumd_grpc_message { + long type; + char data[GRPC_MSG_BUF_MAX]; +};
\ No newline at end of file diff --git a/wmediumd/inc/usfstl/list.h b/wmediumd/inc/usfstl/list.h index a07c9e9..079a591 100644 --- a/wmediumd/inc/usfstl/list.h +++ b/wmediumd/inc/usfstl/list.h @@ -37,18 +37,18 @@ static inline void usfstl_list_init(struct usfstl_list *list) } static inline void usfstl_list_insert_before(struct usfstl_list_entry *existing, - struct usfstl_list_entry *new) + struct usfstl_list_entry *new_entry) { - new->prev = existing->prev; - existing->prev->next = new; - existing->prev = new; - new->next = existing; + new_entry->prev = existing->prev; + existing->prev->next = new_entry; + existing->prev = new_entry; + new_entry->next = existing; } static inline void usfstl_list_append(struct usfstl_list *list, - struct usfstl_list_entry *new) + struct usfstl_list_entry *new_entry) { - usfstl_list_insert_before(&list->list, new); + usfstl_list_insert_before(&list->list, new_entry); } #define usfstl_list_item(element, type, member) \ diff --git a/wmediumd/inc/usfstl/vhostproto.h b/wmediumd/inc/usfstl/vhostproto.h index 3c5c2db..4550179 100644 --- a/wmediumd/inc/usfstl/vhostproto.h +++ b/wmediumd/inc/usfstl/vhostproto.h @@ -81,6 +81,7 @@ struct vhost_user_msg { #define VHOST_USER_SET_SLAVE_REQ_FD 21 #define VHOST_USER_GET_CONFIG 24 #define VHOST_USER_VRING_KICK 35 +#define VHOST_USER_GET_SHARED_MEMORY_REGIONS 41 #define VHOST_USER_SLAVE_CONFIG_CHANGE_MSG 2 #define VHOST_USER_SLAVE_VRING_CALL 4 diff --git a/wmediumd/lib/vhost.c b/wmediumd/lib/vhost.c index d6ad1b1..b6a7a70 100644 --- a/wmediumd/lib/vhost.c +++ b/wmediumd/lib/vhost.c @@ -668,6 +668,11 @@ static void usfstl_vhost_user_handle_msg(struct usfstl_loop_entry *entry) USFSTL_ASSERT(msg.payload.vring_state.num == 0); usfstl_vhost_user_virtq_kick(dev, msg.payload.vring_state.idx); break; + case VHOST_USER_GET_SHARED_MEMORY_REGIONS: + USFSTL_ASSERT_EQ(len, (ssize_t)0, "%zd"); + reply_len = sizeof(uint64_t); + msg.payload.u64 = 0; + break; default: USFSTL_ASSERT(0, "Unsupported message: %d\n", msg.hdr.request); } diff --git a/wmediumd/list.h b/wmediumd/list.h index a6212af..8671c64 100644 --- a/wmediumd/list.h +++ b/wmediumd/list.h @@ -7,9 +7,8 @@ /* Stripped down from Linux ~5.5 */ -#define POISON_POINTER_DELTA 0 -#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA) -#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA) +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) #define WRITE_ONCE(p, v) do { p = v; } while (0) #define READ_ONCE(p) (p) #ifndef container_of @@ -48,7 +47,7 @@ static inline void INIT_LIST_HEAD(struct list_head *list) list->prev = list; } -static inline bool __list_add_valid(struct list_head *new, +static inline bool __list_add_valid(struct list_head *new_elem, struct list_head *prev, struct list_head *next) { @@ -65,44 +64,44 @@ static inline bool __list_del_entry_valid(struct list_head *entry) * This is only for internal list manipulation where we know * the prev/next entries already! */ -static inline void __list_add(struct list_head *new, +static inline void __list_add(struct list_head *new_elem, struct list_head *prev, struct list_head *next) { - if (!__list_add_valid(new, prev, next)) + if (!__list_add_valid(new_elem, prev, next)) return; - next->prev = new; - new->next = next; - new->prev = prev; - WRITE_ONCE(prev->next, new); + next->prev = new_elem; + new_elem->next = next; + new_elem->prev = prev; + WRITE_ONCE(prev->next, new_elem); } /** * list_add - add a new entry - * @new: new entry to be added + * @new_elem: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ -static inline void list_add(struct list_head *new, struct list_head *head) +static inline void list_add(struct list_head *new_elem, struct list_head *head) { - __list_add(new, head, head->next); + __list_add(new_elem, head, head->next); } /** * list_add_tail - add a new entry - * @new: new entry to be added + * @new_elem: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ -static inline void list_add_tail(struct list_head *new, struct list_head *head) +static inline void list_add_tail(struct list_head *new_elem, struct list_head *head) { - __list_add(new, head->prev, head); + __list_add(new_elem, head->prev, head); } /* @@ -149,37 +148,37 @@ static inline void __list_del_entry(struct list_head *entry) static inline void list_del(struct list_head *entry) { __list_del_entry(entry); - entry->next = LIST_POISON1; - entry->prev = LIST_POISON2; + entry->next = (struct list_head *)LIST_POISON1; + entry->prev = (struct list_head *)LIST_POISON2; } /** * list_replace - replace old entry by new one * @old : the element to be replaced - * @new : the new element to insert + * @new_elem : the new element to insert * * If @old was empty, it will be overwritten. */ static inline void list_replace(struct list_head *old, - struct list_head *new) + struct list_head *new_elem) { - new->next = old->next; - new->next->prev = new; - new->prev = old->prev; - new->prev->next = new; + new_elem->next = old->next; + new_elem->next->prev = new_elem; + new_elem->prev = old->prev; + new_elem->prev->next = new_elem; } /** * list_replace_init - replace old entry by new one and initialize the old one * @old : the element to be replaced - * @new : the new element to insert + * @new_elem : the new element to insert * * If @old was empty, it will be overwritten. */ static inline void list_replace_init(struct list_head *old, - struct list_head *new) + struct list_head *new_elem) { - list_replace(old, new); + list_replace(old, new_elem); INIT_LIST_HEAD(old); } diff --git a/wmediumd/pmsr.c b/wmediumd/pmsr.c new file mode 100644 index 0000000..7484500 --- /dev/null +++ b/wmediumd/pmsr.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2022 The Android Open Source Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "pmsr.h" + +#include <errno.h> +#include <stdio.h> + +static int parse_pmsr_request_ftm(struct nlattr *req, + struct pmsr_request_ftm *out) +{ + struct nlattr *tb[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1]; + + nla_parse_nested(tb, NL80211_PMSR_FTM_REQ_ATTR_MAX, req, NULL); + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]) + out->preamble = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]); + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]) + out->burst_period = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]); + + out->asap = !!tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP]; + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]) + out->num_bursts_exp = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]); + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]) + out->burst_duration = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]); + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]) + out->ftms_per_burst = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]); + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]) + out->ftmr_retries = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]); + + out->request_lci = !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI]; + + out->request_civicloc = + !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC]; + + out->trigger_based = + !!tb[NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED]; + + out->non_trigger_based = + !!tb[NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED]; + + out->lmr_feedback = + !!tb[NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK]; + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR]) + out->bss_color = + nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR]); + + return 0; +} + +int parse_pmsr_channel(struct nlattr *req, + struct pmsr_channel *out) +{ + struct nlattr *nla; + int rem; + + if (!req) + return -EINVAL; + + nla_for_each_attr(nla, req, nla_len(req), rem) { + switch (nla_type(nla)) { + case NL80211_ATTR_WIPHY_FREQ: + out->center_freq = nla_get_u32(nla); + break; + case NL80211_ATTR_WIPHY_FREQ_OFFSET: + out->freq_offset = nla_get_u32(nla); + break; + case NL80211_ATTR_WIPHY_CHANNEL_TYPE: + out->channel_type = nla_get_u32(nla); + break; + case NL80211_ATTR_CHANNEL_WIDTH: + out->width = nla_get_u32(nla); + break; + case NL80211_ATTR_CENTER_FREQ1: + out->center_freq1 = nla_get_u32(nla); + break; + case NL80211_ATTR_CENTER_FREQ2: + out->center_freq2 = nla_get_u32(nla); + break; + default: + printf("%s: unknown attributes\n", __func__); + } + } + + return 0; +} + +static int parse_pmsr_request_peer(struct nlattr *req, + struct pmsr_request_peer *out) +{ + struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1]; + struct nlattr *tb_req[NL80211_PMSR_REQ_ATTR_MAX + 1]; + struct nlattr *nla; + int err, rem; + + if (!req) + return EINVAL; + + err = nla_parse_nested(tb, NL80211_PMSR_PEER_ATTR_MAX, + req, NULL); + if (err) { + printf("%s: Failed to parse PMSR peer\n", __func__); + return err; + } + + if (!tb[NL80211_PMSR_PEER_ATTR_ADDR]) { + printf("%s: Failed to parse PMSR peer. Missing peer address\n", __func__); + return -EINVAL; + } + + memcpy(out->addr, nla_data(tb[NL80211_PMSR_PEER_ATTR_ADDR]), ETH_ALEN); + + err = parse_pmsr_channel(tb[NL80211_PMSR_PEER_ATTR_CHAN], + &out->channel); + if (err) + return err; + + err = nla_parse_nested(tb_req, NL80211_PMSR_REQ_ATTR_MAX, + tb[NL80211_PMSR_PEER_ATTR_REQ], NULL); + if (err) + return err; + + if (tb_req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF]) + out->report_ap_tsf = true; + + if (!tb_req[NL80211_PMSR_REQ_ATTR_DATA]) { + printf("%s: missing NL80211_PMSR_REQ_ATTR_DATA\n", __func__); + return -EINVAL; + } + + nla_for_each_nested(nla, tb_req[NL80211_PMSR_REQ_ATTR_DATA], rem) { + switch (nla_type(nla)) { + case NL80211_PMSR_TYPE_FTM: + err = parse_pmsr_request_ftm(nla, &out->ftm); + if (err) + return err; + break; + default: + printf("%s: unsupported measurement type\n", __func__); + return -EINVAL; + } + } + + return 0; +} + +int parse_pmsr_request(struct nlattr *req, + struct pmsr_request *out) +{ + struct nlattr *nla; + struct pmsr_request_peer *peer, *tmp; + int rem; + int err = 0; + + if (!req) + return -EINVAL; + + INIT_LIST_HEAD(&out->peers); + out->n_peers = 0; + + req = nla_find(nla_data(req), nla_len(req), NL80211_ATTR_PEER_MEASUREMENTS); + + nla_for_each_nested(nla, req, rem) { + switch (nla_type(nla)) { + case NL80211_ATTR_TIMEOUT: + out->timeout = nla_get_u32(nla); + break; + case NL80211_ATTR_MAC: + memcpy(out->mac_addr, nla_data(nla), ETH_ALEN); + break; + case NL80211_ATTR_MAC_MASK: + memcpy(out->mac_addr_mask, nla_data(nla), ETH_ALEN); + break; + case NL80211_PMSR_ATTR_PEERS: + peer = calloc(1, sizeof(struct pmsr_request_peer)); + list_add(&peer->list, &out->peers); + err = parse_pmsr_request_peer(nla, peer); + if (err) + goto out_err; + out->n_peers++; + break; + } + } + +out_err: + if (err) + list_for_each_entry_safe(peer, tmp, &out->peers, list) { + list_del(&peer->list); + free(peer); + } + return err; +} + diff --git a/wmediumd/pmsr.h b/wmediumd/pmsr.h new file mode 100644 index 0000000..3b408c5 --- /dev/null +++ b/wmediumd/pmsr.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022 The Android Open Source Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef PMSR_H_ +#define PMSR_H_ + +#include <stdint.h> +#include <stdbool.h> +#include <netlink/attr.h> +#include <linux/nl80211.h> + +#include "ieee80211.h" +#include "list.h" + +struct pmsr_channel { + uint32_t center_freq; + uint32_t freq_offset; + enum nl80211_channel_type channel_type; + enum nl80211_chan_width width; + uint32_t center_freq1; + uint32_t center_freq2; +}; + +struct pmsr_request_ftm { + enum nl80211_preamble preamble; + uint32_t burst_period; + uint8_t asap:1, + request_lci:1, + request_civicloc:1, + trigger_based:1, + non_trigger_based:1, + lmr_feedback:1; + uint8_t num_bursts_exp; + uint8_t burst_duration; + uint8_t ftms_per_burst; + uint8_t ftmr_retries; + uint8_t bss_color; +}; + +struct pmsr_request_peer { + struct list_head list; + + uint8_t addr[ETH_ALEN]; + struct pmsr_channel channel; + uint8_t report_ap_tsf:1; + struct pmsr_request_ftm ftm; +}; + +struct pmsr_request { + uint32_t timeout; + + // Only for mac randomization + uint8_t mac_addr[ETH_ALEN]; + uint8_t mac_addr_mask[ETH_ALEN]; + + uint32_t n_peers; + + // keeps pmsr_request_peer + struct list_head peers; +}; + +/* + * Parse pmsr request. + * + * Caller has responsibility to release peers in returned pmsr_request. + */ +int parse_pmsr_request(struct nlattr *req, struct pmsr_request *out); + +#endif /* PMSR_H_ */ diff --git a/wmediumd/wmediumd.c b/wmediumd/wmediumd.c index 6b3ae5d..0f4f050 100644 --- a/wmediumd/wmediumd.c +++ b/wmediumd/wmediumd.c @@ -31,12 +31,15 @@ #include <getopt.h> #include <signal.h> #include <math.h> +#include <sys/syslog.h> #include <sys/timerfd.h> #include <errno.h> #include <limits.h> +#include <time.h> #include <unistd.h> #include <stdarg.h> #include <endian.h> +#include <sys/msg.h> #include <usfstl/loop.h> #include <usfstl/sched.h> #include <usfstl/schedctrl.h> @@ -47,6 +50,8 @@ #include "ieee80211.h" #include "config.h" #include "api.h" +#include "pmsr.h" +#include "grpc.h" USFSTL_SCHEDULER(scheduler); @@ -58,11 +63,59 @@ enum { HWSIM_NUM_VQS, }; +static char *stpcpy_safe(char *dst, const char *src) { + if (dst == NULL) { + return NULL; + } + + if (src == NULL) { + *dst = '\0'; + return dst; + } + + return stpcpy(dst, src); +} + +static int strlen_safe(const char *src) { + return src == NULL ? 0 : strlen(src); +} + static inline int div_round(int a, int b) { return (a + b - 1) / b; } +static inline u64 sec_to_ns(time_t sec) +{ + return sec * 1000 * 1000 * 1000; +} + +static inline u64 ns_to_us(u64 ns) +{ + return ns / 1000L; +} + +static inline u64 ts_to_ns(struct timespec ts) +{ + return sec_to_ns(ts.tv_sec) + ts.tv_nsec; +} + +static inline double distance_to_rtt(double distance) +{ + const long light_speed = 299792458L; + return distance / light_speed; +} + +static inline double sec_to_ps(double sec) +{ + return sec * 1000 * 1000 * 1000; +} + +static inline double meter_to_mm(double meter) +{ + return meter * 1000; +} + static inline int pkt_duration(int len, int rate) { /* preamble + signal + t_sym * n_sym, rate in 100 kbps */ @@ -142,7 +195,6 @@ static inline bool frame_is_probe_req(struct frame *frame) (FTYPE_MGMT | STYPE_PROBE_REQ); } - static inline bool frame_has_zero_rates(const struct frame *frame) { for (int i = 0; i < frame->tx_rates_count; i++) { @@ -784,7 +836,7 @@ static void wmediumd_deliver_frame(struct usfstl_job *job) if (memcmp(src, station->addr, ETH_ALEN) == 0) continue; - if (is_multicast_ether_addr(dest)) { + if (is_multicast_ether_addr(dest) && station->client != NULL) { int snr, rate_idx, signal; double error_prob; @@ -884,6 +936,232 @@ int nl_err_cb(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg) return NL_SKIP; } +static int send_pmsr_result_ftm(struct nl_msg *msg, + struct pmsr_request_ftm *req, + struct wmediumd *ctx, + struct station *sender, + struct station *receiver) +{ + struct nlattr *ftm; + int err; + + ftm = nla_nest_start(msg, NL80211_PMSR_TYPE_FTM); + if (!ftm) + return -ENOMEM; + + if (!receiver) { + err = nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON, + NL80211_PMSR_FTM_FAILURE_NO_RESPONSE); + goto out; + } + + double distance = + sqrt(pow(sender->x - receiver->x, 2) + pow(sender->y - receiver->y, 2)); + double distance_in_mm = meter_to_mm(distance); + double rtt_in_ps = sec_to_ps(distance_to_rtt(distance)); + if (distance_in_mm > UINT64_MAX || rtt_in_ps > UINT64_MAX) { + w_logf(ctx, LOG_WARNING, + "%s: Devices are too far away", __func__); + return nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON, + NL80211_PMSR_FTM_FAILURE_NO_RESPONSE); + } + + int rssi = receiver->tx_power - + ctx->calc_path_loss(NULL, sender, receiver); + + if (nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS, + req->ftmr_retries) || + nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES, + req->ftmr_retries) || + nla_put_u8(msg, NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP, + req->num_bursts_exp) || + nla_put_u8(msg, NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION, + req->burst_duration) || + nla_put_u8(msg, NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST, + req->ftms_per_burst) || + nla_put_s32(msg, NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG, rssi) || + nla_put_s32(msg, NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD, 0) || + nla_put_u64(msg, NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG, + (uint64_t)rtt_in_ps) || + nla_put_u64(msg, NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE, 0) || + nla_put_u64(msg, NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD, 0) || + nla_put_u64(msg, NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG, + (uint64_t)distance_in_mm) || + nla_put_u64(msg, NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE, 0) || + nla_put_u64(msg, NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD, 0)) { + w_logf(ctx, LOG_ERR, "%s: Failed to fill a payload\n", __func__); + return -ENOMEM; + } + + if (req->request_lci && receiver->lci) { + nla_put_string(msg, NL80211_PMSR_FTM_RESP_ATTR_LCI, + receiver->lci); + } + + if (req->request_civicloc && receiver->civicloc) { + nla_put_string(msg, NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC, + receiver->civicloc); + } + +out: + if (ftm) + nla_nest_end(msg, ftm); + + return 0; +} + +static int send_pmsr_result_peer(struct nl_msg *msg, + struct pmsr_request_peer *req, + struct wmediumd *ctx, + struct station *sender) +{ + struct nlattr *peer, *resp, *data; + struct station *receiver; + int status; + struct timespec ts; + u64 host_time_ns; + u64 ap_tsf_us; + int err; + + peer = nla_nest_start(msg, 1); + if (!peer) + return -ENOMEM; + + receiver = get_station_by_addr(ctx, req->addr); + if (receiver) + status = NL80211_PMSR_STATUS_SUCCESS; + else { + w_logf(ctx, LOG_WARNING, "%s: unknown pmsr target " MAC_FMT "\n", + __func__, MAC_ARGS(req->addr)); + status = NL80211_PMSR_STATUS_FAILURE; + } + + if (clock_gettime(CLOCK_BOOTTIME, &ts)) { + w_logf(ctx, LOG_ERR, "%s: clock_gettime() failed\n", __func__); + return -EINVAL; + } + + host_time_ns = ts_to_ns(ts); + ap_tsf_us = ns_to_us(ts_to_ns(ts)); + + err = nla_put(msg, NL80211_PMSR_PEER_ATTR_ADDR, ETH_ALEN, req->addr); + if (err) + return err; + + resp = nla_nest_start(msg, NL80211_PMSR_PEER_ATTR_RESP); + if (!resp) + return -ENOMEM; + + if (nla_put_u32(msg, NL80211_PMSR_RESP_ATTR_STATUS, status) || + nla_put_u64(msg, NL80211_PMSR_RESP_ATTR_HOST_TIME, host_time_ns) || + nla_put_u64(msg, NL80211_PMSR_RESP_ATTR_AP_TSF, ap_tsf_us) || + nla_put_flag(msg, NL80211_PMSR_RESP_ATTR_FINAL)) { + w_logf(ctx, LOG_ERR, "%s: Failed to fill a payload\n", __func__); + return -ENOMEM; + } + + data = nla_nest_start(msg, NL80211_PMSR_RESP_ATTR_DATA); + if (!data) + return -ENOMEM; + + err = send_pmsr_result_ftm(msg, &req->ftm, ctx, sender, receiver); + + nla_nest_end(msg, data); + nla_nest_end(msg, resp); + nla_nest_end(msg, peer); + + return err; +} + +static void send_pmsr_result(struct pmsr_request* request, struct wmediumd *ctx, + struct station *sender, struct client *client) +{ + struct nl_msg *msg; + struct nlattr *pmsr, *peers; + struct pmsr_request_peer *peer; + int cnt; + int err; + + msg = nlmsg_alloc(); + if (!msg) { + w_logf(ctx, LOG_ERR, "%s: nlmsg_alloc failed\n", __func__); + return; + } + + if (!genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, ctx->family_id, + 0, NLM_F_REQUEST, HWSIM_CMD_REPORT_PMSR, VERSION_NR)) { + w_logf(ctx, LOG_ERR, "%s: genlmsg_put failed\n", __func__); + goto out; + } + + err = nla_put(msg, HWSIM_ATTR_ADDR_TRANSMITTER, + ETH_ALEN, sender->hwaddr); + + pmsr = nla_nest_start(msg, HWSIM_ATTR_PMSR_RESULT); + if (!pmsr) + goto out; + + peers = nla_nest_start(msg, NL80211_PMSR_ATTR_PEERS); + if (!peers) + goto out; + + list_for_each_entry(peer, &request->peers, list) { + err = send_pmsr_result_peer(msg, peer, ctx, sender); + if (err) { + w_logf(ctx, LOG_ERR, + "%s: Failed to send pmsr result from " MAC_FMT \ + " to " MAC_FMT ". Stopping\n", __func__, + MAC_ARGS(sender->addr), + MAC_ARGS(peer->addr)); + break; + } + } + + nla_nest_end(msg, peers); + nla_nest_end(msg, pmsr); + + wmediumd_send_to_client(ctx, client, msg); + +out: + nlmsg_free(msg); +} + +static void process_start_pmsr(struct nlattr *attrs[], struct wmediumd *ctx, + struct client *client) +{ + u8 *hwaddr; + struct station *sender; + + struct pmsr_request request; + struct pmsr_request_peer *peer, *tmp; + int err; + + if (!attrs[HWSIM_ATTR_ADDR_TRANSMITTER]) { + w_logf(ctx, LOG_ERR, "%s: Missing sender information\n", + __func__); + } + + hwaddr = (u8 *)nla_data(attrs[HWSIM_ATTR_ADDR_TRANSMITTER]); + sender = get_station_by_addr(ctx, hwaddr); + if (!sender) { + w_logf(ctx, LOG_ERR, "%s: Unknown sender " MAC_FMT "\n", + __func__, MAC_ARGS(hwaddr)); + return; + } + + err = parse_pmsr_request(attrs[HWSIM_ATTR_PMSR_REQUEST], &request); + if (err) + goto out; + + send_pmsr_result(&request, ctx, sender, client); + +out: + list_for_each_entry_safe(peer, tmp, &request.peers, list) { + list_del(&peer->list); + free(peer); + } +} + /* * Handle events from the kernel. Process CMD_FRAME events and queue them * for later delivery with the scheduler. @@ -902,7 +1180,7 @@ static void _process_messages(struct nl_msg *msg, struct frame *frame; struct ieee80211_hdr *hdr; u8 *src, *hwaddr, *addr; - void *new; + void *new_addrs; unsigned int i; genlmsg_parse(nlh, 0, attrs, HWSIM_ATTR_MAX, NULL); @@ -976,13 +1254,16 @@ static void _process_messages(struct nl_msg *msg, if (!sender) break; for (i = 0; i < sender->n_addrs; i++) { - if (memcmp(sender->addrs[i].addr, addr, ETH_ALEN) == 0) + if (memcmp(sender->addrs[i].addr, addr, ETH_ALEN) == 0) { + sender->addrs[i].count += 1; return; + } } - new = realloc(sender->addrs, ETH_ALEN * (sender->n_addrs + 1)); - if (!new) + new_addrs = realloc(sender->addrs, sizeof(struct addr) * (sender->n_addrs + 1)); + if (!new_addrs) break; - sender->addrs = new; + sender->addrs = new_addrs; + sender->addrs[sender->n_addrs].count = 1; memcpy(sender->addrs[sender->n_addrs].addr, addr, ETH_ALEN); sender->n_addrs += 1; break; @@ -998,13 +1279,23 @@ static void _process_messages(struct nl_msg *msg, for (i = 0; i < sender->n_addrs; i++) { if (memcmp(sender->addrs[i].addr, addr, ETH_ALEN)) continue; - sender->n_addrs -= 1; - memmove(sender->addrs[i].addr, - sender->addrs[sender->n_addrs].addr, - ETH_ALEN); + sender->addrs[i].count -= 1; + if (sender->addrs[i].count <= 0) { + sender->n_addrs -= 1; + memmove(&sender->addrs[i], + &sender->addrs[sender->n_addrs], + sizeof(struct addr)); + } break; } break; + case HWSIM_CMD_START_PMSR: + process_start_pmsr(attrs, ctx, client); + break; + + case HWSIM_CMD_ABORT_PMSR: + // Do nothing. Too late to abort any PMSR. + break; } } @@ -1109,18 +1400,33 @@ static int process_reload_current_config_message(struct wmediumd *ctx) { static int process_get_stations_message(struct wmediumd *ctx, ssize_t *response_len, unsigned char **response_data) { struct station *station; int station_count = 0; + int extra_data_len = 0; + // *reponse_data contains struct wmediumd_station_infos + // and then lci and civiclocs for each station follows afterwards. list_for_each_entry(station, &ctx->stations, list) { if (station->client != NULL) { ++station_count; + extra_data_len += strlen_safe(station->lci) + 1; + extra_data_len += strlen_safe(station->civicloc) + 1; } } - *response_len = sizeof(uint32_t) + sizeof(struct wmediumd_station_info) * station_count; - struct wmediumd_station_infos *station_infos = malloc(*response_len); + int station_len = sizeof(uint32_t) + sizeof(struct wmediumd_station_info) * station_count; + *response_len = station_len + extra_data_len; + *response_data = malloc(*response_len); + if (*response_data == NULL) { + w_logf(ctx, LOG_ERR, "%s: failed allocate response data\n", __func__); + return -1; + } + + struct wmediumd_station_infos *station_infos = (struct wmediumd_station_infos *)*response_data; station_infos->count = station_count; int station_index = 0; + // Pointer to the memory after structs wmediumd_station_infos + // to write lci and civicloc for each station. + char *extra_data_cursor = (char *)&(station_infos->stations[station_count]); list_for_each_entry(station, &ctx->stations, list) { if (station->client != NULL) { @@ -1130,18 +1436,71 @@ static int process_get_stations_message(struct wmediumd *ctx, ssize_t *response_ station_info->x = station->x; station_info->y = station->y; - + station_info->lci_offset = extra_data_cursor - (char *)station_info; + extra_data_cursor = stpcpy_safe(extra_data_cursor, station->lci) + 1; + station_info->civicloc_offset = extra_data_cursor - (char *)station_info; + extra_data_cursor = stpcpy_safe(extra_data_cursor, station->civicloc) + 1; station_info->tx_power = station->tx_power; - station_index++; } } - *response_data = (unsigned char *)station_infos; + return 0; +} + +static int process_set_position_message(struct wmediumd *ctx, struct wmediumd_set_position *set_position) { + struct station *node = get_station_by_addr(ctx, set_position->mac); + + if (node == NULL) { + return -1; + } + + node->x = set_position->x; + node->y = set_position->y; + + calc_path_loss(ctx); return 0; } +static int process_set_lci_message(struct wmediumd *ctx, struct wmediumd_set_lci *set_lci, size_t data_len) { + struct station *node = get_station_by_addr(ctx, set_lci->mac); + + if (node == NULL) { + return -1; + } + int expected_len = data_len - offsetof(struct wmediumd_set_lci, lci) - 1; + if (set_lci->lci[expected_len] != '\0') { + return -1; + } + + if (node->lci) { + free(node->lci); + } + node->lci = strdup(set_lci->lci); + + return node->lci != NULL; +} + +static int process_set_civicloc_message(struct wmediumd *ctx, struct wmediumd_set_civicloc *set_civicloc, size_t data_len) { + struct station *node = get_station_by_addr(ctx, set_civicloc->mac); + + if (node == NULL) { + return -1; + } + int expected_len = data_len - offsetof(struct wmediumd_set_civicloc, civicloc) - 1; + if (set_civicloc->civicloc[expected_len] != '\0') { + return -1; + } + + if (node->civicloc) { + free(node->civicloc); + } + node->civicloc = strdup(set_civicloc->civicloc); + + return node->civicloc != NULL; +} + static const struct usfstl_vhost_user_ops wmediumd_vu_ops = { .connected = wmediumd_vu_connected, .handle = wmediumd_vu_handle, @@ -1161,6 +1520,43 @@ static void close_pcapng(struct wmediumd *ctx) { static void init_pcapng(struct wmediumd *ctx, const char *filename); +static void wmediumd_grpc_service_handler(struct usfstl_loop_entry *entry) { + struct wmediumd *ctx = entry->data; + + // Receive request type from WmediumdService + uint64_t request_type; + read(entry->fd, &request_type, sizeof(uint64_t)); + + struct wmediumd_grpc_message request_body; + uint64_t response_type; + + // Receive request body from WmediumdService and do the task. + // TODO(273384914): Support more request types. + switch (request_type) { + case REQUEST_SET_POSITION: + if (msgrcv(ctx->msq_id, &request_body, sizeof(struct wmediumd_set_position), GRPC_REQUEST, 0) != sizeof(struct wmediumd_set_position)) { + w_logf(ctx, LOG_ERR, "%s: failed to get set_position request body\n", __func__); + } + + if (process_set_position_message(ctx, (struct wmediumd_set_position *)(request_body.data)) < 0) { + w_logf(ctx, LOG_ERR, "%s: failed to execute set_position\n", __func__); + response_type = RESPONSE_INVALID; + } + response_type = RESPONSE_ACK; + break; + default: + w_logf(ctx, LOG_ERR, "%s: unknown request type\n", __func__); + response_type = RESPONSE_INVALID; + break; + } + + // TODO(273384914): Send response with response_type + return; +} + +// TODO(273384914): Deprecate messages used in wmediumd_control after +// implementing in wmediumd_grpc_service_handler to be used in the command +// 'cvd env'. static void wmediumd_api_handler(struct usfstl_loop_entry *entry) { struct client *client = container_of(entry, struct client, loop); @@ -1175,20 +1571,31 @@ static void wmediumd_api_handler(struct usfstl_loop_entry *entry) ssize_t len; len = read(entry->fd, &hdr, sizeof(hdr)); - if (len != sizeof(hdr)) + if (len != sizeof(hdr)) { + if (len > 0) { + // Skipping log if the fd is closed. + w_logf(ctx, LOG_ERR, "%s: failed to read header\n", __func__); + } goto disconnect; + } /* safety valve */ - if (hdr.data_len > 1024 * 1024) + if (hdr.data_len > 1024 * 1024) { + w_logf(ctx, LOG_ERR, "%s: too large data\n", __func__); goto disconnect; + } data = malloc(hdr.data_len); - if (!data) + if (!data) { + w_logf(ctx, LOG_ERR, "%s: failed to malloc\n", __func__); goto disconnect; + } len = read(entry->fd, data, hdr.data_len); - if (len != hdr.data_len) + if (len != hdr.data_len) { + w_logf(ctx, LOG_ERR, "%s: failed to read data\n", __func__); goto disconnect; + } switch (hdr.type) { case WMEDIUMD_MSG_REGISTER: @@ -1243,18 +1650,18 @@ static void wmediumd_api_handler(struct usfstl_loop_entry *entry) case WMEDIUMD_MSG_SET_SNR: if (process_set_snr_message(ctx, (struct wmediumd_set_snr *)data) < 0) { response = WMEDIUMD_MSG_INVALID; - } + } break; case WMEDIUMD_MSG_RELOAD_CONFIG: if (process_reload_config_message(ctx, (struct wmediumd_reload_config *)data) < 0) { response = WMEDIUMD_MSG_INVALID; - } + } break; case WMEDIUMD_MSG_RELOAD_CURRENT_CONFIG: if (process_reload_current_config_message(ctx) < 0) { response = WMEDIUMD_MSG_INVALID; - } + } break; case WMEDIUMD_MSG_START_PCAP: init_pcapng(ctx, ((struct wmediumd_start_pcap *)data)->pcap_path); @@ -1262,6 +1669,25 @@ static void wmediumd_api_handler(struct usfstl_loop_entry *entry) case WMEDIUMD_MSG_STOP_PCAP: close_pcapng(ctx); break; + case WMEDIUMD_MSG_SET_POSITION: + if (process_set_position_message(ctx, (struct wmediumd_set_position *)data) < 0) { + response = WMEDIUMD_MSG_INVALID; + } + break; + case WMEDIUMD_MSG_SET_LCI: + if (process_set_lci_message(ctx, + (struct wmediumd_set_lci *)data, + hdr.data_len) < 0) { + response = WMEDIUMD_MSG_INVALID; + } + break; + case WMEDIUMD_MSG_SET_CIVICLOC: + if (process_set_civicloc_message(ctx, + (struct wmediumd_set_civicloc *)data, + hdr.data_len) < 0) { + response = WMEDIUMD_MSG_INVALID; + } + break; case WMEDIUMD_MSG_ACK: assert(client->wait_for_ack == true); assert(hdr.data_len == 0); @@ -1269,6 +1695,7 @@ static void wmediumd_api_handler(struct usfstl_loop_entry *entry) /* don't send a response to a response, of course */ return; default: + w_logf(ctx, LOG_ERR, "%s: unknown message\n", __func__); response = WMEDIUMD_MSG_INVALID; break; } @@ -1277,8 +1704,10 @@ static void wmediumd_api_handler(struct usfstl_loop_entry *entry) hdr.type = response; hdr.data_len = response_len; len = write(entry->fd, &hdr, sizeof(hdr)); - if (len != sizeof(hdr)) + if (len != sizeof(hdr)) { + w_logf(ctx, LOG_ERR, "%s: failed to write response header\n", __func__); goto disconnect; + } if (response_data != NULL) { if (response_len != 0) { @@ -1286,6 +1715,7 @@ static void wmediumd_api_handler(struct usfstl_loop_entry *entry) if (len != response_len) { free(response_data); + w_logf(ctx, LOG_ERR, "%s: failed to write response data\n", __func__); goto disconnect; } } @@ -1480,7 +1910,7 @@ static void init_pcapng(struct wmediumd *ctx, const char *filename) #define VIRTIO_F_VERSION_1 32 #endif -int main(int argc, char *argv[]) +int wmediumd_main(int argc, char *argv[], int event_fd, int msq_id) { int opt; struct wmediumd ctx = {}; @@ -1631,6 +2061,13 @@ int main(int argc, char *argv[]) usfstl_sched_wallclock_init(&scheduler, 1000); } + // Control event_fd to communicate WmediumdService. + ctx.grpc_loop.handler = wmediumd_grpc_service_handler; + ctx.grpc_loop.data = &ctx; + ctx.grpc_loop.fd = event_fd; + usfstl_loop_register(&ctx.grpc_loop); + ctx.msq_id = msq_id; + while (1) { if (time_socket) { usfstl_sched_next(&scheduler); diff --git a/wmediumd/wmediumd.h b/wmediumd/wmediumd.h index 3c9818d..695f871 100644 --- a/wmediumd/wmediumd.h +++ b/wmediumd/wmediumd.h @@ -39,6 +39,9 @@ enum { HWSIM_CMD_GET_RADIO, HWSIM_CMD_ADD_MAC_ADDR, HWSIM_CMD_DEL_MAC_ADDR, + HWSIM_CMD_START_PMSR, + HWSIM_CMD_ABORT_PMSR, + HWSIM_CMD_REPORT_PMSR, __HWSIM_CMD_MAX, }; #define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1) @@ -78,6 +81,16 @@ enum { * @HWSIM_ATTR_RADIO_NAME: Name of radio, e.g. phy666 * @HWSIM_ATTR_NO_VIF: Do not create vif (wlanX) when creating radio. * @HWSIM_ATTR_FREQ: Frequency at which packet is transmitted or received. + * @HWSIM_ATTR_TX_INFO_FLAGS: additional flags for corresponding + * rates of %HWSIM_ATTR_TX_INFO + * @HWSIM_ATTR_PERM_ADDR: permanent mac address of new radio + * @HWSIM_ATTR_IFTYPE_SUPPORT: u32 attribute of supported interface types bits + * @HWSIM_ATTR_CIPHER_SUPPORT: u32 array of supported cipher types + * @HWSIM_ATTR_MLO_SUPPORT: claim MLO support (exact parameters TBD) for + * the new radio + * @HWSIM_ATTR_PMSR_SUPPORT: claim peer measurement support + * @HWSIM_ATTR_PMSR_REQUEST: peer measurement request + * @HWSIM_ATTR_PMSR_RESULT: peer measurement result * @__HWSIM_ATTR_MAX: enum limit */ @@ -104,6 +117,14 @@ enum { HWSIM_ATTR_NO_VIF, HWSIM_ATTR_FREQ, HWSIM_ATTR_PAD, + HWSIM_ATTR_TX_INFO_FLAGS, + HWSIM_ATTR_PERM_ADDR, + HWSIM_ATTR_IFTYPE_SUPPORT, + HWSIM_ATTR_CIPHER_SUPPORT, + HWSIM_ATTR_MLO_SUPPORT, + HWSIM_ATTR_PMSR_SUPPORT, + HWSIM_ATTR_PMSR_REQUEST, + HWSIM_ATTR_PMSR_RESULT, __HWSIM_ATTR_MAX, }; #define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1) @@ -147,6 +168,7 @@ struct wqueue { struct addr { u8 addr[ETH_ALEN]; + uint16_t count; }; struct station { @@ -154,6 +176,8 @@ struct station { u8 addr[ETH_ALEN]; /* virtual interface mac address */ u8 hwaddr[ETH_ALEN]; /* hardware address of hwsim radio */ double x, y; /* position of the station [m] */ + char *lci; /* LCI */ + char *civicloc; /* CIVIC */ double dir_x, dir_y; /* direction of the station [meter per MOVE_INTERVAL] */ int tx_power; /* transmission power [dBm] */ struct wqueue queues[IEEE80211_NUM_ACS]; @@ -190,9 +214,10 @@ struct client { struct wmediumd { int timerfd; + int msq_id; struct nl_sock *sock; - struct usfstl_loop_entry nl_loop; + struct usfstl_loop_entry nl_loop, grpc_loop; struct usfstl_sched_ctrl *ctrl; @@ -282,4 +307,14 @@ int w_flogf(struct wmediumd *ctx, u8 level, FILE *stream, const char *format, .. int index_to_rate(size_t index, u32 freq); int get_max_index(void); +#ifdef __cplusplus +extern "C" { +#endif + +int wmediumd_main(int argc, char *argv[], int event_fd, int msq_id); + +#ifdef __cplusplus +} +#endif + #endif /* WMEDIUMD_H_ */ diff --git a/wmediumd_server/wmediumd.proto b/wmediumd_server/wmediumd.proto new file mode 100644 index 0000000..592e708 --- /dev/null +++ b/wmediumd_server/wmediumd.proto @@ -0,0 +1,30 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// 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. + +syntax = "proto3"; + +package wmediumdserver; + +import "google/protobuf/empty.proto"; + +service WmediumdService { + // TODO(273384914): Define a response type. + rpc SetPosition(SetPositionRequest) returns (google.protobuf.Empty) {} +} + +message SetPositionRequest { + string mac_address = 1; + double x_pos = 2; + double y_pos = 3; +} diff --git a/wmediumd_server/wmediumd_server.cc b/wmediumd_server/wmediumd_server.cc new file mode 100644 index 0000000..229d505 --- /dev/null +++ b/wmediumd_server/wmediumd_server.cc @@ -0,0 +1,147 @@ +/* + * + * Copyright (C) 2023 The Android Open Source Project + * + * 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 <android-base/strings.h> +#include <gflags/gflags.h> +#include <grpcpp/ext/proto_server_reflection_plugin.h> +#include <grpcpp/grpcpp.h> +#include <grpcpp/health_check_service_interface.h> +#include <sys/msg.h> +#include <unistd.h> + +#include <algorithm> +#include <array> +#include <iostream> +#include <memory> +#include <string> + +#include "wmediumd.grpc.pb.h" +#include "wmediumd/api.h" +#include "wmediumd/grpc.h" + +using google::protobuf::Empty; +using grpc::Server; +using grpc::ServerBuilder; +using grpc::ServerContext; +using grpc::Status; +using grpc::StatusCode; +using wmediumdserver::SetPositionRequest; +using wmediumdserver::WmediumdService; + +#define MAC_ADDR_LEN 6 +#define STR_MAC_ADDR_LEN 17 + +template <class T> +static void AppendBinaryRepresentation(std::string& buf, const T& data) { + std::copy(reinterpret_cast<const char*>(&data), + reinterpret_cast<const char*>(&data) + sizeof(T), + std::back_inserter(buf)); +} + +bool IsValidMacAddr(const std::string& mac_address) { + if (mac_address.size() != STR_MAC_ADDR_LEN) { + return false; + } + + if (mac_address[2] != ':' || mac_address[5] != ':' || mac_address[8] != ':' || + mac_address[11] != ':' || mac_address[14] != ':') { + return false; + } + + for (int i = 0; i < STR_MAC_ADDR_LEN; ++i) { + if ((i - 2) % 3 == 0) continue; + char c = mac_address[i]; + + if (isupper(c)) { + c = tolower(c); + } + + if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) return false; + } + + return true; +} + +static std::array<uint8_t, 6> ParseMacAddress(const std::string& mac_address) { + auto split_mac = android::base::Split(mac_address, ":"); + std::array<uint8_t, 6> mac; + for (int i = 0; i < 6; i++) { + char* end_ptr; + mac[i] = (uint8_t)strtol(split_mac[i].c_str(), &end_ptr, 16); + } + + return mac; +} + +class WmediumdServiceImpl final : public WmediumdService::Service { + public: + WmediumdServiceImpl(int event_fd, int msq_id) + : event_fd_(event_fd), msq_id_(msq_id) {} + + Status SetPosition(ServerContext* context, const SetPositionRequest* request, + Empty* reply) override { + // Validate parameters + if (!IsValidMacAddr(request->mac_address())) { + return Status(StatusCode::INVALID_ARGUMENT, "Got invalid mac address"); + } + auto mac = ParseMacAddress(request->mac_address()); + + // Construct request data + struct wmediumd_set_position data; + memcpy(data.mac, &mac, sizeof(mac)); + data.x = request->x_pos(); + data.y = request->y_pos(); + + // Fill data in the message queue + struct wmediumd_grpc_message msg; + msg.type = GRPC_REQUEST; + memcpy(msg.data, &data, sizeof(data)); + msgsnd(msq_id_, &msg, sizeof(data), 0); + + // Throw an event to wmediumd + uint64_t value = REQUEST_SET_POSITION; + write(event_fd_, &value, sizeof(uint64_t)); + + return Status::OK; + } + + private: + int event_fd_; + int msq_id_; +}; + +void RunWmediumdServer(std::string grpc_uds_path, int event_fd, int msq_id) { + std::string server_address("unix:" + grpc_uds_path); + WmediumdServiceImpl service(event_fd, msq_id); + + grpc::EnableDefaultHealthCheckService(true); + grpc::reflection::InitProtoReflectionServerBuilderPlugin(); + ServerBuilder builder; + // Listen on the given address without any authentication mechanism. + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); + // Register "service" as the instance through which we'll communicate with + // clients. In this case it corresponds to an *synchronous* service. + builder.RegisterService(&service); + // Finally assemble the server. + std::unique_ptr<Server> server(builder.BuildAndStart()); + std::cout << "Server listening on " << server_address << std::endl; + + // Wait for the server to shutdown. Note that some other thread must be + // responsible for shutting down the server for this call to ever return. + server->Wait(); +} diff --git a/wmediumd_server/wmediumd_server.h b/wmediumd_server/wmediumd_server.h new file mode 100644 index 0000000..31761ac --- /dev/null +++ b/wmediumd_server/wmediumd_server.h @@ -0,0 +1,19 @@ +/* + * + * Copyright (C) 2023 The Android Open Source Project + * + * 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. + * + */ + +void RunWmediumdServer(std::string grpc_uds_path, int event_fd, int msq_id);
\ No newline at end of file |