blob: 37bc021478936d0ef561ed672a6a29592b5c7257 [file] [log] [blame]
// 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.
use std::path::Path;
use std::{fs, io};
use anyhow::{format_err, Context as _, Error};
use async_trait::async_trait;
use fidl_fuchsia_input_report::{
DeviceDescriptor, InputDeviceMarker, InputDeviceProxy, InputReportsReaderMarker,
InputReportsReaderProxy, SensorInputDescriptor, SensorType,
};
#[derive(Debug)]
pub struct AmbientLightInputRpt {
pub illuminance: f32,
pub red: f32,
pub green: f32,
pub blue: f32,
}
struct AmbientLightComponent {
pub report_index: usize,
pub exponent: i32,
pub report_id: u8, // report ID associated with descriptor
}
struct AmbientLightInputReportReaderProxy {
pub proxy: InputReportsReaderProxy,
pub illuminance: Option<AmbientLightComponent>,
pub red: Option<AmbientLightComponent>,
pub green: Option<AmbientLightComponent>,
pub blue: Option<AmbientLightComponent>,
}
fn open_input_report_device(path: &str) -> Result<InputDeviceProxy, Error> {
tracing::info!("Opening sensor at {:?}", path);
let (proxy, server) = fidl::endpoints::create_proxy::<InputDeviceMarker>()
.context("Failed to create sensor proxy")?;
fdio::service_connect(path, server.into_channel())
.context("Failed to connect built-in service")?;
Ok(proxy)
}
async fn open_sensor_input_report_reader() -> Result<AmbientLightInputReportReaderProxy, Error> {
let input_report_directory = "/dev/class/input-report";
let dir_path = Path::new(input_report_directory);
let entries = fs::read_dir(dir_path)?;
for entry in entries {
let entry = entry?;
let device_path = entry.path();
let device_path = device_path.to_str().expect("Bad path");
let device = open_input_report_device(device_path)?;
fn get_sensor_input(
descriptor: &DeviceDescriptor,
) -> Result<&Vec<SensorInputDescriptor>, Error> {
let sensor = descriptor.sensor.as_ref().context("device has no sensor")?;
let input_desc = sensor.input.as_ref().context("sensor has no input descriptor")?;
Ok(input_desc)
}
if let Ok(descriptor) = device.get_descriptor().await {
match get_sensor_input(&descriptor) {
Ok(input_desc) => {
let mut illuminance = None;
let mut red = None;
let mut green = None;
let mut blue = None;
for input in input_desc {
match &input.values {
Some(axes) => {
for (i, val) in axes.iter().enumerate() {
let component = AmbientLightComponent {
report_index: i,
exponent: val.axis.unit.exponent,
report_id: input.report_id.unwrap_or(0),
};
match val.type_ {
SensorType::LightIlluminance => {
illuminance = Some(component)
}
SensorType::LightRed => red = Some(component),
SensorType::LightGreen => green = Some(component),
SensorType::LightBlue => blue = Some(component),
_ => {}
}
}
}
_ => {}
}
}
if illuminance.is_some() {
let (proxy, server_end) =
fidl::endpoints::create_proxy::<InputReportsReaderMarker>()?;
if let Ok(()) = device.get_input_reports_reader(server_end) {
return Ok(AmbientLightInputReportReaderProxy {
proxy: proxy,
illuminance,
red,
blue,
green,
});
}
}
}
Err(e) => {
tracing::info!("Skip device {}: {}", device_path, e);
}
};
}
}
Err(io::Error::new(io::ErrorKind::NotFound, "no sensor found").into())
}
/// Reads the sensor's input report and decodes it.
async fn read_sensor_input_report(
device: &AmbientLightInputReportReaderProxy,
) -> Result<Option<AmbientLightInputRpt>, Error> {
let r = device.proxy.read_input_reports().await;
match r {
Ok(Ok(reports)) => {
for report in reports {
if report.report_id.unwrap_or(0) != device.illuminance.as_ref().unwrap().report_id {
continue;
}
if let Some(sensor) = report.sensor {
if let Some(values) = sensor.values {
let f = |component: &Option<AmbientLightComponent>| match component {
Some(val) => match val.exponent {
0 => values[val.report_index] as f32,
_ => {
values[val.report_index] as f32
* f32::powf(10.0, val.exponent as f32)
}
},
None => 0.0,
};
let illuminance = f(&device.illuminance);
let red = f(&device.red);
let green = f(&device.green);
let blue = f(&device.blue);
return Ok(Some(AmbientLightInputRpt { illuminance, red, blue, green }));
}
}
}
Ok(None)
}
Ok(Err(e)) => Err(format_err!("ReadInputReports error: {}", e)),
Err(e) => Err(format_err!("FIDL call failed: {}", e)),
}
}
/// TODO(lingxueluo) Default and temporary report when sensor is not valid(https://fxbug.dev/42119013).
fn default_report() -> Result<Option<AmbientLightInputRpt>, Error> {
Ok(Some(AmbientLightInputRpt { illuminance: 200.0, red: 200.0, green: 200.0, blue: 200.0 }))
}
pub struct Sensor {
proxy: Option<AmbientLightInputReportReaderProxy>,
}
impl Sensor {
pub async fn new() -> Sensor {
let proxy = open_sensor_input_report_reader().await;
match proxy {
Ok(proxy) => return Sensor { proxy: Some(proxy) },
Err(_e) => {
println!("No valid sensor found.");
return Sensor { proxy: None };
}
}
}
async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error> {
if self.proxy.is_none() {
default_report()
} else {
read_sensor_input_report(self.proxy.as_ref().unwrap()).await
}
}
}
#[async_trait]
pub trait SensorControl: Send {
async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error>;
}
#[async_trait]
impl SensorControl for Sensor {
async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error> {
self.read().await
}
}
#[cfg(test)]
mod tests {
use super::*;
use fuchsia_async as fasync;
#[fasync::run_singlethreaded(test)]
async fn test_open_sensor_error() {
let sensor = Sensor { proxy: None };
if let Some(ambient_light_input_rpt) = sensor.read().await.unwrap() {
assert_eq!(ambient_light_input_rpt.illuminance, 200.0);
assert_eq!(ambient_light_input_rpt.red, 200.0);
assert_eq!(ambient_light_input_rpt.green, 200.0);
assert_eq!(ambient_light_input_rpt.blue, 200.0);
}
}
}