blob: 84a5e76a6310391d4382dfca54cc1d8892341861 [file] [log] [blame]
// Copyright 2018 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.
use {
crate::{stats_scheduler::StatsRequest, Never},
failure::bail,
fidl_fuchsia_wlan_mlme::{MlmeEventStream, MlmeProxy},
fidl_fuchsia_wlan_sme as fidl_sme,
futures::{
channel::mpsc,
prelude::*,
select,
stream::{self, FuturesUnordered},
Poll,
},
log::error,
pin_utils::pin_mut,
std::{
marker::Unpin,
sync::{Arc, Mutex},
},
wlan_sme::{mesh as mesh_sme, timer::TimeEntry, DeviceInfo},
};
pub type Endpoint = fidl::endpoints::ServerEnd<fidl_sme::MeshSmeMarker>;
type Sme = mesh_sme::MeshSme;
pub async fn serve<S>(
proxy: MlmeProxy,
device_info: DeviceInfo,
event_stream: MlmeEventStream,
new_fidl_clients: mpsc::UnboundedReceiver<Endpoint>,
stats_requests: S,
) -> Result<(), failure::Error>
where
S: Stream<Item = StatsRequest> + Send + Unpin,
{
let (sme, mlme_stream) = Sme::new(device_info);
let sme = Arc::new(Mutex::new(sme));
let time_stream = stream::poll_fn::<TimeEntry<()>, _>(|_| Poll::Pending);
let mlme_sme = super::serve_mlme_sme(
proxy,
event_stream,
Arc::clone(&sme),
mlme_stream,
stats_requests,
time_stream,
);
let sme_fidl = serve_fidl(&sme, new_fidl_clients);
pin_mut!(mlme_sme);
pin_mut!(sme_fidl);
select! {
mlme_sme = mlme_sme.fuse() => mlme_sme?,
sme_fidl = sme_fidl.fuse() => sme_fidl?.into_any(),
}
Ok(())
}
async fn serve_fidl(
sme: &Mutex<Sme>,
new_fidl_clients: mpsc::UnboundedReceiver<Endpoint>,
) -> Result<Never, failure::Error> {
let mut fidl_clients = FuturesUnordered::new();
let mut new_fidl_clients = new_fidl_clients.fuse();
loop {
select! {
new_fidl_client = new_fidl_clients.next() => match new_fidl_client {
Some(c) => fidl_clients.push(serve_fidl_endpoint(sme, c)),
None => bail!("New FIDL client stream unexpectedly ended"),
},
// Drive clients towards completion
_ = fidl_clients.next() => {},
}
}
}
async fn serve_fidl_endpoint(sme: &Mutex<Sme>, endpoint: Endpoint) {
const MAX_CONCURRENT_REQUESTS: usize = 1000;
let stream = match endpoint.into_stream() {
Ok(s) => s,
Err(e) => {
error!("Failed to create a stream from a zircon channel: {}", e);
return;
}
};
let r = await!(stream.try_for_each_concurrent(MAX_CONCURRENT_REQUESTS, move |request| {
handle_fidl_request(&sme, request)
}));
if let Err(e) = r {
error!("Error serving a FIDL client of Mesh SME: {}", e);
}
}
async fn handle_fidl_request(
sme: &Mutex<Sme>,
request: fidl_sme::MeshSmeRequest,
) -> Result<(), ::fidl::Error> {
match request {
fidl_sme::MeshSmeRequest::Join { config, responder } => {
let code = await!(join_mesh(sme, config));
responder.send(code)
}
fidl_sme::MeshSmeRequest::Leave { responder } => {
let code = await!(leave_mesh(sme));
responder.send(code)
}
}
}
async fn join_mesh(sme: &Mutex<Sme>, config: fidl_sme::MeshConfig) -> fidl_sme::JoinMeshResultCode {
let receiver = sme
.lock()
.unwrap()
.on_join_command(mesh_sme::Config { mesh_id: config.mesh_id, channel: config.channel });
let r = await!(receiver).unwrap_or_else(|_| {
error!("Responder for Join Mesh command was dropped without sending a response");
mesh_sme::JoinMeshResult::InternalError
});
convert_join_mesh_result(r)
}
fn convert_join_mesh_result(result: mesh_sme::JoinMeshResult) -> fidl_sme::JoinMeshResultCode {
match result {
mesh_sme::JoinMeshResult::Success => fidl_sme::JoinMeshResultCode::Success,
mesh_sme::JoinMeshResult::Canceled => fidl_sme::JoinMeshResultCode::Canceled,
mesh_sme::JoinMeshResult::InternalError => fidl_sme::JoinMeshResultCode::InternalError,
mesh_sme::JoinMeshResult::InvalidArguments => {
fidl_sme::JoinMeshResultCode::InvalidArguments
}
mesh_sme::JoinMeshResult::DfsUnsupported => fidl_sme::JoinMeshResultCode::DfsUnsupported,
}
}
async fn leave_mesh(sme: &Mutex<Sme>) -> fidl_sme::LeaveMeshResultCode {
let receiver = sme.lock().unwrap().on_leave_command();
let r = await!(receiver).unwrap_or_else(|_| {
error!("Responder for Leave Mesh command was dropped without sending a response");
mesh_sme::LeaveMeshResult::InternalError
});
convert_leave_mesh_result(r)
}
fn convert_leave_mesh_result(result: mesh_sme::LeaveMeshResult) -> fidl_sme::LeaveMeshResultCode {
match result {
mesh_sme::LeaveMeshResult::Success => fidl_sme::LeaveMeshResultCode::Success,
mesh_sme::LeaveMeshResult::InternalError => fidl_sme::LeaveMeshResultCode::InternalError,
}
}