Snap for 10453563 from 190a84103575e58100e3c1c78d084c6716aa490d to mainline-ipsec-release

Change-Id: Ic92406026a84c481a917aba2433636455a190331
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
diff --git a/Android.bp b/Android.bp
index 97dbe73..de1b8cf 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6,6 +6,7 @@
     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 @@
         "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 @@
         "-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 @@
         "wmediumd/inc",
     ],
     visibility: [
-       "//device/google/cuttlefish/build",
+        "//device/google/cuttlefish/build",
     ],
     stl: "none",
     static_executable: true,
diff --git a/LICENSE b/LICENSE
index d159169..73a0519 100644
--- a/LICENSE
+++ b/LICENSE
@@ -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
diff --git a/OWNERS b/OWNERS
index b9f5251..05d6f65 100644
--- a/OWNERS
+++ b/OWNERS
@@ -2,3 +2,5 @@
 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"
+        }
+      ]
+    }
+  ]
+}
diff --git a/main.cc b/main.cc
new file mode 100644
index 0000000..3e0c5f2
--- /dev/null
+++ b/main.cc
@@ -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 @@
   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 @@
       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 @@
     }
   }
 
-  /* 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 @@
     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 @@
 	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 @@
 };
 
 #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 @@
 	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 @@
 	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 @@
 	 * 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 @@
 	 */
 	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 @@
 	 * 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 @@
 					    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_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 @@
 #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 @@
 		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 @@
 	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 @@
  * 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(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 @@
 	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 @@
 		(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 @@
 			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 @@
 	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 @@
 	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 @@
 		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 @@
 		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_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 @@
 
 			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 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 @@
 	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 @@
 	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 @@
 	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 @@
 		/* 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 @@
 	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 @@
 
 			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 @@
 #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 @@
 		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 @@
 	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 @@
  * @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 @@
 	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 addr {
 	u8 addr[ETH_ALEN];
+	uint16_t count;
 };
 
 struct station {
@@ -154,6 +176,8 @@
 	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 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 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