1use std::path::Path;
6use std::{fs, io};
7
8use anyhow::{format_err, Context as _, Error};
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 match &input.values {
74 Some(axes) => {
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 => {
83 illuminance = Some(component)
84 }
85 SensorType::LightRed => red = Some(component),
86 SensorType::LightGreen => green = Some(component),
87 SensorType::LightBlue => blue = Some(component),
88 _ => {}
89 }
90 }
91 }
92 _ => {}
93 }
94 }
95
96 if illuminance.is_some() {
97 let (proxy, server_end) =
98 fidl::endpoints::create_proxy::<InputReportsReaderMarker>();
99 if let Ok(()) = device.get_input_reports_reader(server_end) {
100 return Ok(AmbientLightInputReportReaderProxy {
101 proxy: proxy,
102 illuminance,
103 red,
104 blue,
105 green,
106 });
107 }
108 }
109 }
110 Err(e) => {
111 log::info!("Skip device {}: {}", device_path, e);
112 }
113 };
114 }
115 }
116 Err(io::Error::new(io::ErrorKind::NotFound, "no sensor found").into())
117}
118
119async fn read_sensor_input_report(
121 device: &AmbientLightInputReportReaderProxy,
122) -> Result<Option<AmbientLightInputRpt>, Error> {
123 let r = device.proxy.read_input_reports().await;
124
125 match r {
126 Ok(Ok(reports)) => {
127 for report in reports {
128 if report.report_id.unwrap_or(0) != device.illuminance.as_ref().unwrap().report_id {
129 continue;
130 }
131
132 if let Some(sensor) = report.sensor {
133 if let Some(values) = sensor.values {
134 let f = |component: &Option<AmbientLightComponent>| match component {
135 Some(val) => match val.exponent {
136 0 => values[val.report_index] as f32,
137 _ => {
138 values[val.report_index] as f32
139 * f32::powf(10.0, val.exponent as f32)
140 }
141 },
142 None => 0.0,
143 };
144
145 let illuminance = f(&device.illuminance);
146 let red = f(&device.red);
147 let green = f(&device.green);
148 let blue = f(&device.blue);
149
150 return Ok(Some(AmbientLightInputRpt { illuminance, red, blue, green }));
151 }
152 }
153 }
154 Ok(None)
155 }
156 Ok(Err(e)) => Err(format_err!("ReadInputReports error: {}", e)),
157 Err(e) => Err(format_err!("FIDL call failed: {}", e)),
158 }
159}
160
161fn default_report() -> Result<Option<AmbientLightInputRpt>, Error> {
163 Ok(Some(AmbientLightInputRpt { illuminance: 200.0, red: 200.0, green: 200.0, blue: 200.0 }))
164}
165
166pub struct Sensor {
167 proxy: Option<AmbientLightInputReportReaderProxy>,
168}
169
170impl Sensor {
171 pub async fn new() -> Sensor {
172 let proxy = open_sensor_input_report_reader().await;
173 match proxy {
174 Ok(proxy) => return Sensor { proxy: Some(proxy) },
175 Err(_e) => {
176 println!("No valid sensor found.");
177 return Sensor { proxy: None };
178 }
179 }
180 }
181
182 async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error> {
183 if self.proxy.is_none() {
184 default_report()
185 } else {
186 read_sensor_input_report(self.proxy.as_ref().unwrap()).await
187 }
188 }
189}
190
191#[async_trait]
192pub trait SensorControl: Send {
193 async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error>;
194}
195
196#[async_trait]
197impl SensorControl for Sensor {
198 async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error> {
199 self.read().await
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206 use fuchsia_async as fasync;
207
208 #[fasync::run_singlethreaded(test)]
209 async fn test_open_sensor_error() {
210 let sensor = Sensor { proxy: None };
211 if let Some(ambient_light_input_rpt) = sensor.read().await.unwrap() {
212 assert_eq!(ambient_light_input_rpt.illuminance, 200.0);
213 assert_eq!(ambient_light_input_rpt.red, 200.0);
214 assert_eq!(ambient_light_input_rpt.green, 200.0);
215 assert_eq!(ambient_light_input_rpt.blue, 200.0);
216 }
217 }
218}