1use std::path::Path;
6use std::{fs, io};
7
8use anyhow::{Context as _, Error, format_err};
9use async_trait::async_trait;
10use fidl_fuchsia_input_report::{
11 DeviceDescriptor, InputDeviceMarker, InputDeviceProxy, InputReportsReaderMarker,
12 InputReportsReaderProxy, SensorInputDescriptor, SensorType,
13};
14
15#[derive(Debug)]
16pub struct AmbientLightInputRpt {
17 pub illuminance: f32,
18 pub red: f32,
19 pub green: f32,
20 pub blue: f32,
21}
22
23struct AmbientLightComponent {
24 pub report_index: usize,
25 pub exponent: i32,
26 pub report_id: u8, }
28
29struct AmbientLightInputReportReaderProxy {
30 pub proxy: InputReportsReaderProxy,
31
32 pub illuminance: Option<AmbientLightComponent>,
33 pub red: Option<AmbientLightComponent>,
34 pub green: Option<AmbientLightComponent>,
35 pub blue: Option<AmbientLightComponent>,
36}
37
38fn open_input_report_device(path: &str) -> Result<InputDeviceProxy, Error> {
39 log::info!("Opening sensor at {:?}", path);
40 let (proxy, server) = fidl::endpoints::create_proxy::<InputDeviceMarker>();
41 fdio::service_connect(path, server.into_channel())
42 .context("Failed to connect built-in service")?;
43 Ok(proxy)
44}
45
46async fn open_sensor_input_report_reader() -> Result<AmbientLightInputReportReaderProxy, Error> {
47 let input_report_directory = "/dev/class/input-report";
48 let dir_path = Path::new(input_report_directory);
49 let entries = fs::read_dir(dir_path)?;
50 for entry in entries {
51 let entry = entry?;
52 let device_path = entry.path();
53 let device_path = device_path.to_str().expect("Bad path");
54 let device = open_input_report_device(device_path)?;
55
56 fn get_sensor_input(
57 descriptor: &DeviceDescriptor,
58 ) -> Result<&Vec<SensorInputDescriptor>, Error> {
59 let sensor = descriptor.sensor.as_ref().context("device has no sensor")?;
60 let input_desc = sensor.input.as_ref().context("sensor has no input descriptor")?;
61 Ok(input_desc)
62 }
63
64 if let Ok(descriptor) = device.get_descriptor().await {
65 match get_sensor_input(&descriptor) {
66 Ok(input_desc) => {
67 let mut illuminance = None;
68 let mut red = None;
69 let mut green = None;
70 let mut blue = None;
71
72 for input in input_desc {
73 let axes =
74 input.values.as_ref().context("SensorInputDescriptor has no values")?;
75 for (i, val) in axes.iter().enumerate() {
76 let component = AmbientLightComponent {
77 report_index: i,
78 exponent: val.axis.unit.exponent,
79 report_id: input.report_id.unwrap_or(0),
80 };
81 match val.type_ {
82 SensorType::LightIlluminance => illuminance = Some(component),
83 SensorType::LightRed => red = Some(component),
84 SensorType::LightGreen => green = Some(component),
85 SensorType::LightBlue => blue = Some(component),
86 _ => {}
87 }
88 }
89 }
90
91 if illuminance.is_some() {
92 let (proxy, server_end) =
93 fidl::endpoints::create_proxy::<InputReportsReaderMarker>();
94 if let Ok(()) = device.get_input_reports_reader(server_end) {
95 return Ok(AmbientLightInputReportReaderProxy {
96 proxy: proxy,
97 illuminance,
98 red,
99 blue,
100 green,
101 });
102 }
103 }
104 }
105 Err(e) => {
106 log::info!("Skip device {}: {}", device_path, e);
107 }
108 };
109 }
110 }
111 Err(io::Error::new(io::ErrorKind::NotFound, "no sensor found").into())
112}
113
114async fn read_sensor_input_report(
116 device: &AmbientLightInputReportReaderProxy,
117) -> Result<Option<AmbientLightInputRpt>, Error> {
118 let r = device.proxy.read_input_reports().await;
119
120 match r {
121 Ok(Ok(reports)) => {
122 for report in reports {
123 if report.report_id.unwrap_or(0) != device.illuminance.as_ref().unwrap().report_id {
124 continue;
125 }
126
127 let sensor =
128 report.sensor.context("sensor required in InputReport for sensor device")?;
129 let values = sensor.values.context("values required in SensorInputReport")?;
130 let f = |component: &Option<AmbientLightComponent>| match component {
131 Some(val) => match val.exponent {
132 0 => values[val.report_index] as f32,
133 _ => values[val.report_index] as f32 * f32::powf(10.0, val.exponent as f32),
134 },
135 None => 0.0,
136 };
137
138 let illuminance = f(&device.illuminance);
139 let red = f(&device.red);
140 let green = f(&device.green);
141 let blue = f(&device.blue);
142
143 return Ok(Some(AmbientLightInputRpt { illuminance, red, blue, green }));
144 }
145 Ok(None)
146 }
147 Ok(Err(e)) => Err(format_err!("ReadInputReports error: {}", e)),
148 Err(e) => Err(format_err!("FIDL call failed: {}", e)),
149 }
150}
151
152fn default_report() -> Result<Option<AmbientLightInputRpt>, Error> {
154 Ok(Some(AmbientLightInputRpt { illuminance: 200.0, red: 200.0, green: 200.0, blue: 200.0 }))
155}
156
157pub struct Sensor {
158 proxy: Option<AmbientLightInputReportReaderProxy>,
159}
160
161impl Sensor {
162 pub async fn new() -> Sensor {
163 let proxy = open_sensor_input_report_reader().await;
164 match proxy {
165 Ok(proxy) => return Sensor { proxy: Some(proxy) },
166 Err(_e) => {
167 println!("No valid sensor found.");
168 return Sensor { proxy: None };
169 }
170 }
171 }
172
173 async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error> {
174 if self.proxy.is_none() {
175 default_report()
176 } else {
177 read_sensor_input_report(self.proxy.as_ref().unwrap()).await
178 }
179 }
180}
181
182#[async_trait]
183pub trait SensorControl: Send {
184 async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error>;
185}
186
187#[async_trait]
188impl SensorControl for Sensor {
189 async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error> {
190 self.read().await
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use fuchsia_async as fasync;
198
199 #[fasync::run_singlethreaded(test)]
200 async fn test_open_sensor_error() {
201 let sensor = Sensor { proxy: None };
202 if let Some(ambient_light_input_rpt) = sensor.read().await.unwrap() {
203 assert_eq!(ambient_light_input_rpt.illuminance, 200.0);
204 assert_eq!(ambient_light_input_rpt.red, 200.0);
205 assert_eq!(ambient_light_input_rpt.green, 200.0);
206 assert_eq!(ambient_light_input_rpt.blue, 200.0);
207 }
208 }
209}