blob: 037ceb318e9b4d7cdb0ac75fd59a56a5c6aedb82 [file] [log] [blame]
// Copyright 2020 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::common_utils::common::find_file;
use crate::common_utils::common::macros::{fx_err_and_bail, with_line};
use crate::gpio::types::SerializableGpioFlags;
use anyhow::Error;
use fidl_fuchsia_hardware_gpio::{GpioMarker, GpioProxy};
use fuchsia_syslog::macros::*;
use parking_lot::RwLock;
use std::path::Path;
/// Perform Gpio operations.
///
/// Note this object is shared among all threads created by server.
///
#[derive(Debug)]
pub struct GpioFacade {
proxy: RwLock<Option<GpioProxy>>,
}
impl GpioFacade {
pub fn new() -> GpioFacade {
GpioFacade { proxy: RwLock::new(None) }
}
#[cfg(test)]
fn new_with_proxy(proxy: GpioProxy) -> Self {
Self { proxy: RwLock::new(Some(proxy)) }
}
fn get_proxy(&self, pin: u32) -> Result<GpioProxy, Error> {
let tag = "GpioFacade::get_proxy";
let lock = self.proxy.upgradable_read();
if let Some(proxy) = lock.as_ref() {
Ok(proxy.clone())
} else {
let (proxy, server) = match fidl::endpoints::create_proxy::<GpioMarker>() {
Ok(r) => r,
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("Failed to get gpio proxy {:?}", e)
),
};
let find_path = format!("gpio-{}", pin);
let found_path = match find_file(Path::new("/dev/sys/platform"), &find_path) {
Ok(p) => p,
Err(_) => fx_err_and_bail!(&with_line!(tag), format_err!("Failed to find path")),
};
fdio::service_connect(found_path.to_str().unwrap(), server.into_channel())?;
Ok(proxy)
}
}
pub async fn config_in(&self, pin: u32, flags: SerializableGpioFlags) -> Result<(), Error> {
let tag = "gpiofacade::config_in";
match self.get_proxy(pin)?.config_in(Into::into(flags)).await? {
Ok(_) => Ok(()),
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("could not config gpio pin in: {:?}", e)
),
}
}
pub async fn config_out(&self, pin: u32, value: u8) -> Result<(), Error> {
let tag = "gpiofacade::config_out";
match self.get_proxy(pin)?.config_out(value).await? {
Ok(_) => Ok(()),
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("could not config gpio pin out: {:?}", e)
),
}
}
pub async fn read(&self, pin: u32) -> Result<u8, Error> {
let tag = "gpiofacade::read";
match self.get_proxy(pin)?.read().await? {
Ok(val) => Ok(val),
Err(e) => {
fx_err_and_bail!(&with_line!(tag), format_err!("could not read gpio pin: {:?}", e))
}
}
}
pub async fn write(&self, pin: u32, value: u8) -> Result<(), Error> {
let tag = "gpiofacade::write";
match self.get_proxy(pin)?.write(value).await? {
Ok(_) => Ok(()),
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("could not write to gpio pin: {:?}", e)
),
}
}
pub async fn set_drive_strength(&self, pin: u32, ds_ua: u64) -> Result<u64, Error> {
let tag = "gpiofacade::set_drive_strength";
match self.get_proxy(pin)?.set_drive_strength(ds_ua).await? {
Ok(ds) => Ok(ds),
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("could not set drive strength of gpio pin: {:?}", e)
),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use fidl_fuchsia_hardware_gpio::GpioRequest;
use futures::{future::Future, join, stream::StreamExt};
use matches::assert_matches;
struct MockGpioBuilder {
expected: Vec<Box<dyn FnOnce(GpioRequest) + Send + 'static>>,
}
impl MockGpioBuilder {
fn new() -> Self {
Self { expected: vec![] }
}
fn push(mut self, request: impl FnOnce(GpioRequest) + Send + 'static) -> Self {
self.expected.push(Box::new(request));
self
}
fn expect_config_in(
self,
_pin: u32,
flag: SerializableGpioFlags,
res: Result<(), i32>,
) -> Self {
self.push(move |req| match req {
GpioRequest::ConfigIn { flags, responder } => {
assert_eq!(flag, Into::into(flags));
responder.send(&mut res.map(Into::into)).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_config_out(self, _pin: u32, val: u8, res: Result<(), i32>) -> Self {
self.push(move |req| match req {
GpioRequest::ConfigOut { initial_value, responder } => {
assert_eq!(val, initial_value);
responder.send(&mut res.map(Into::into)).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_read(self, _pin: u32, res: Result<u8, i32>) -> Self {
self.push(move |req| match req {
GpioRequest::Read { responder } => {
responder.send(&mut res.map(Into::into)).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_write(self, _pin: u32, val: u8, res: Result<(), i32>) -> Self {
self.push(move |req| match req {
GpioRequest::Write { value, responder } => {
assert_eq!(val, value);
responder.send(&mut res.map(Into::into)).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_set_drive_strength(self, _pin: u32, ds: u64, res: Result<u64, i32>) -> Self {
self.push(move |req| match req {
GpioRequest::SetDriveStrength { ds_ua, responder } => {
assert_eq!(ds, ds_ua);
responder.send(&mut res.map(Into::into)).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn build(self) -> (GpioFacade, impl Future<Output = ()>) {
let (proxy, mut stream) =
fidl::endpoints::create_proxy_and_stream::<GpioMarker>().unwrap();
let fut = async move {
for expected in self.expected {
expected(stream.next().await.unwrap().unwrap());
}
assert_matches!(stream.next().await, None);
};
(GpioFacade::new_with_proxy(proxy), fut)
}
}
#[fuchsia_async::run_singlethreaded(test)]
async fn config_in_ok() {
let (facade, gpio) = MockGpioBuilder::new()
.expect_config_in(0, SerializableGpioFlags::PullUp, Ok(()))
.build();
let test = async move {
assert_matches!(facade.config_in(0, SerializableGpioFlags::PullUp).await, Ok(_));
};
join!(gpio, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn config_out_ok() {
let (facade, gpio) = MockGpioBuilder::new().expect_config_out(0, 8, Ok(())).build();
let test = async move {
assert_matches!(facade.config_out(0, 8).await, Ok(_));
};
join!(gpio, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn read_ok() {
let (facade, gpio) = MockGpioBuilder::new().expect_read(0, Ok(9)).build();
let test = async move {
assert_matches!(
facade.read(0).await,
Ok(v) if v == 9
);
};
join!(gpio, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn write_ok() {
let (facade, gpio) = MockGpioBuilder::new().expect_write(0, 15, Ok(())).build();
let test = async move {
assert_matches!(facade.write(0, 15).await, Ok(_));
};
join!(gpio, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn set_drive_strength_ok() {
let (facade, gpio) =
MockGpioBuilder::new().expect_set_drive_strength(0, 2000, Ok(2000)).build();
let test = async move {
assert_matches!(facade.set_drive_strength(0, 2000).await, Ok(ds) if ds == 2000);
};
join!(gpio, test);
}
}