| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/connectivity/wlan/drivers/testing/lib/sim-env/sim-env.h" |
| |
| #include <lib/async/task.h> |
| #include <zircon/assert.h> |
| #include <zircon/errors.h> |
| |
| #include <algorithm> |
| |
| namespace wlan::simulation { |
| |
| Environment::Environment() |
| : signal_loss_model_(std::unique_ptr<SignalLossModel>(new LogSignalLossModel())) { |
| // Construct the dispatcher ops for task post/cancel using the notification system. |
| static const async_ops_t ops = { |
| .version = ASYNC_OPS_V1, |
| .v1 = { |
| .now = |
| [](async_dispatcher_t* dispatcher) { |
| return static_cast<Dispatcher*>(dispatcher)->parent->GetTime().get(); |
| }, |
| .post_task = |
| [](async_dispatcher_t* dispatcher, async_task_t* task) { |
| return static_cast<Dispatcher*>(dispatcher)->parent->PostTask(task); |
| }, |
| .cancel_task = |
| [](async_dispatcher_t* dispatcher, async_task_t* task) { |
| return static_cast<Dispatcher*>(dispatcher)->parent->CancelTask(task); |
| }, |
| }, |
| }; |
| dispatcher_.parent = this; |
| dispatcher_.ops = &ops; |
| } |
| |
| Environment::~Environment() { |
| while (!events_.empty()) { |
| auto& event = events_.front(); |
| time_ = std::max(time_, event.deadline); |
| event.fn(ZX_ERR_CANCELED); |
| events_.pop_front(); |
| } |
| } |
| |
| void Environment::Run(zx::duration run_time_limit) { |
| std::lock_guard lock(event_mutex_); |
| const zx::time run_deadline = time_ + run_time_limit; |
| while (!events_.empty()) { |
| auto& event = events_.front(); |
| if (event.deadline > run_deadline) { |
| break; |
| } |
| |
| std::function<void(zx_status_t)> fn = std::move(event.fn); |
| time_ = std::max(time_, event.deadline); |
| events_.pop_front(); |
| |
| // Send event to client who requested it |
| event_mutex_.unlock(); |
| fn(ZX_OK); |
| event_mutex_.lock(); |
| } |
| } |
| |
| void Environment::Tx(const SimFrame& frame, const WlanTxInfo& tx_info, StationIfc* sender) { |
| std::shared_ptr<const SimFrame> tx_frame(frame.CopyFrame()); |
| for (auto& staLocPair : stations_) { |
| StationIfc* sta = staLocPair.first; |
| if (sta != sender) { |
| double signal_strength = |
| signal_loss_model_->CalcSignalStrength(&stations_.at(sender), &stations_.at(sta)); |
| auto rx_info = std::make_shared<WlanRxInfo>(); |
| rx_info->channel = tx_info.channel; |
| rx_info->signal_strength = signal_strength; |
| rx_info->noise_level = kNoiseLevel; |
| // Only deliver frame if the station is sensitive enough to pick it up |
| if (signal_strength >= sta->rx_sensitivity_) { |
| zx::duration trans_delay = CalcTransTime(sender, sta); |
| ScheduleNotification( |
| std::bind(&Environment::HandleTxNotification, this, sta, tx_frame, rx_info), |
| trans_delay); |
| } |
| } |
| } |
| } |
| |
| zx::duration Environment::CalcTransTime(StationIfc* staTx, StationIfc* staRx) { |
| Location location_tx = stations_.at(staTx); |
| Location location_rx = stations_.at(staRx); |
| double distance = location_tx.distanceFrom(&location_rx); |
| |
| // Calcualte how many nanoseconds are needed to do transmission. |
| int64_t trans_time = distance / kRadioWaveVelocity; |
| return zx::nsec(1) * trans_time; |
| } |
| |
| zx_status_t Environment::ScheduleNotification(std::function<void()> fn, zx::duration delay, |
| uint64_t* id_out) { |
| // Disallow past events |
| if (delay < zx::usec(0)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| const zx::time deadline = time_ + delay; |
| |
| uint64_t id = 0; |
| { |
| std::lock_guard lock(event_mutex_); |
| id = event_id_++; |
| // Our events are sorted in order of ascending deadline, then by order of insertion. |
| const auto end_iter = |
| std::find_if(events_.begin(), events_.end(), |
| [deadline](const EnvironmentEvent& e) { return e.deadline > deadline; }); |
| events_.emplace( |
| end_iter, |
| [fn = std::move(fn)](zx_status_t status) { |
| // Notifications scheduled through ScheduleNotfication() do not get explicit cancellation |
| // on shutdown of the environment. |
| if (status == ZX_OK) { |
| fn(); |
| } |
| }, |
| deadline, id); |
| latest_event_deadline_ = std::max(latest_event_deadline_, deadline); |
| } |
| |
| if (id_out != nullptr) { |
| *id_out = id; |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t Environment::CancelNotification(uint64_t id) { |
| std::lock_guard lock(event_mutex_); |
| const auto remove_iter = std::remove_if(events_.begin(), events_.end(), |
| [id](const EnvironmentEvent& e) { return e.id == id; }); |
| if (remove_iter == events_.end()) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| events_.erase(remove_iter, events_.end()); |
| return ZX_OK; |
| } |
| |
| zx::time Environment::GetTime() const { |
| std::lock_guard lock(event_mutex_); |
| return time_; |
| } |
| |
| zx::time Environment::GetLatestEventTime() const { |
| std::lock_guard lock(event_mutex_); |
| return latest_event_deadline_; |
| } |
| |
| zx_status_t Environment::PostTask(async_task_t* task) { |
| uint64_t id = 0; |
| { |
| std::lock_guard lock(event_mutex_); |
| id = event_id_++; |
| const auto end_iter = |
| std::find_if(events_.begin(), events_.end(), |
| [deadline = zx::time(task->deadline)](const EnvironmentEvent& e) { |
| return e.deadline > deadline; |
| }); |
| events_.emplace( |
| end_iter, |
| [dispatcher = &dispatcher_, task](zx_status_t status) { |
| task->handler(dispatcher, task, status); |
| }, |
| zx::time(task->deadline), id); |
| latest_event_deadline_ = std::max(latest_event_deadline_, zx::time(task->deadline)); |
| } |
| |
| task->state.reserved[0] = id; |
| return ZX_OK; |
| } |
| |
| zx_status_t Environment::CancelTask(async_task_t* task) { |
| return CancelNotification(task->state.reserved[0]); |
| } |
| |
| async_dispatcher_t* Environment::GetDispatcher() { return &dispatcher_; } |
| |
| void Environment::HandleTxNotification(StationIfc* sta, std::shared_ptr<const SimFrame> frame, |
| std::shared_ptr<const WlanRxInfo> rx_info) { |
| sta->Rx(frame, rx_info); |
| } |
| |
| } // namespace wlan::simulation |