diff --git a/vhost-device-scmi/CHANGELOG.md b/vhost-device-scmi/CHANGELOG.md index 1a502e3b9..626dec624 100644 --- a/vhost-device-scmi/CHANGELOG.md +++ b/vhost-device-scmi/CHANGELOG.md @@ -3,6 +3,8 @@ ### Added +- [[#798]](https://github.com/rust-vmm/vhost-device/pull/798) scmi: extended sensor attributes support + ### Changed ### Fixed diff --git a/vhost-device-scmi/src/devices/common.rs b/vhost-device-scmi/src/devices/common.rs index efabe005a..18903c370 100644 --- a/vhost-device-scmi/src/devices/common.rs +++ b/vhost-device-scmi/src/devices/common.rs @@ -359,6 +359,11 @@ pub trait SensorT: Send { 0 } + /// Returns the resolution of the sensor scale. + fn resolution(&self) -> u32 { + 0 + } + /// Returns the prefix of axes names. /// /// Usually no need to redefine this. @@ -383,8 +388,10 @@ pub trait SensorT: Send { /// Usually no need to redefine this. fn axis_description(&self, axis: u32) -> Vec { let mut values = vec![]; + let axis_exponent = self.unit_exponent(0); + let axis_resolution = self.resolution(); values.push(MessageValue::Unsigned(axis)); // axis id - values.push(MessageValue::Unsigned(0)); // attributes low + values.push(MessageValue::Unsigned(1 << 8)); // attributes low (Extended attributes support) values.push(MessageValue::Unsigned(self.format_unit(axis))); // attributes high // Name in the recommended format, 16 bytes: @@ -394,6 +401,22 @@ pub trait SensorT: Send { format!("{prefix}_{suffix}"), MAX_SIMPLE_STRING_LENGTH, )); + + // Extended attribute + values.push(MessageValue::Unsigned( + axis_resolution | ((axis_exponent as u32) << 27), + )); //resolution + + // In SCMI spec, it specifies that if the sensor does not report the min and max range, + // the following field should be as as: + // axis_min_range_low 0x0 + // axis_min_range_high 0x80000000 + // axis_max_range_low 0xFFFFFFFF + // axis_max_range_high 0x7FFFFFFF + values.push(MessageValue::Signed(0)); // min_range_low + values.push(MessageValue::Signed(i32::MIN)); // min_range_high + values.push(MessageValue::Signed(-1i32)); // max_range_low + values.push(MessageValue::Signed(i32::MAX)); // max_range_high values } diff --git a/vhost-device-scmi/src/devices/iio.rs b/vhost-device-scmi/src/devices/iio.rs index 52103953a..7a82665ff 100644 --- a/vhost-device-scmi/src/devices/iio.rs +++ b/vhost-device-scmi/src/devices/iio.rs @@ -255,6 +255,10 @@ struct Axis { /// sufficiently accurate SCMI value that is represented by an integer (not /// a float) + decadic exponent. custom_exponent: i8, + /// This is an extended attribute field. It reports the resolution of the sensor axis. + /// The representation is in [custom_resolution] x 10^[custom_exponent] format. + /// This field is present only if Bit[8] of axis_attributes_low is set to 1. + custom_resolution: u64, /// Channel scan type, necessary if the sensor supports notifications. /// The data from /dev/iio:deviceX will be formatted according to this. /// The ChanScanType is parsed from "scan_elements/_type" @@ -262,7 +266,7 @@ struct Axis { } impl Axis { - fn new(path: OsString, unit_exponent: i8, custom_exponent: i8) -> Axis { + fn new(path: OsString, unit_exponent: i8, custom_exponent: i8, custom_resolution: u64) -> Axis { let scan_path = Path::new(&path).parent().unwrap().join("scan_elements"); let mut scan_name = path.clone(); scan_name.push("_type"); @@ -273,6 +277,7 @@ impl Axis { path, unit_exponent, custom_exponent, + custom_resolution, scan_type: ChanScanType::new(scan_type), } } else { @@ -280,6 +285,7 @@ impl Axis { path, unit_exponent, custom_exponent, + custom_resolution, scan_type: None, } } @@ -371,6 +377,13 @@ impl SensorT for IIOSensor { axis.unit_exponent + axis.custom_exponent } + fn resolution(&self) -> u32 { + // All the axes are supposed to have the same value for resolution. + // We are just using the values from the Axis 0 here. + let axis: &Axis = self.axes.first().unwrap(); + axis.custom_resolution as u32 + } + fn number_of_axes(&self) -> u32 { if self.scalar { 0 @@ -579,14 +592,23 @@ impl IIOSensor { } } - fn custom_exponent(&self, path: &OsStr, unit_exponent: i8) -> i8 { + // This function gets both custom exponent and resolution by reading "scale" + // A scale value should be parsed as "[resolution]e[exponent]" + fn custom_exponent_and_resolution(&self, path: &OsStr, unit_exponent: i8) -> (i8, u64) { let mut custom_exponent: i8 = 0; + let mut custom_resolution: u64 = 0; if let Ok(Some(scale)) = self.read_axis_scale(path) { // Crash completely OK if *this* doesn't fit: custom_exponent = scale.log10() as i8; if scale < 1.0 { // The logarithm is truncated towards zero, we need floor custom_exponent -= 1; + // Calculate the resolution of scale + custom_resolution = + (scale * 10i32.pow(-custom_exponent as u32) as f64).trunc() as u64; + } else { + custom_resolution = + (scale / 10i32.pow(custom_exponent as u32) as f64).trunc() as u64; } // The SCMI exponent (unit_exponent + custom_exponent) can have max. 5 bits: custom_exponent = min(15 - unit_exponent, custom_exponent); @@ -596,7 +618,7 @@ impl IIOSensor { &path, custom_exponent ); } - custom_exponent + (custom_exponent, custom_resolution) } fn add_axis(&mut self, axes: &mut Vec, path: &OsStr) { @@ -606,11 +628,13 @@ impl IIOSensor { .map_or(0, |mapping| mapping.unit_exponent); // To get meaningful integer values, we must adjust exponent to // the provided scale if any. - let custom_exponent = self.custom_exponent(path, unit_exponent); + let (custom_exponent, custom_resolution) = + self.custom_exponent_and_resolution(path, unit_exponent); axes.push(Axis::new( OsString::from(path), unit_exponent, custom_exponent, + custom_resolution, )); } diff --git a/vhost-device-scmi/src/scmi.rs b/vhost-device-scmi/src/scmi.rs index 7e6e02b4d..da8c90788 100644 --- a/vhost-device-scmi/src/scmi.rs +++ b/vhost-device-scmi/src/scmi.rs @@ -1384,9 +1384,15 @@ mod tests { let name = format!("acc_{}", char::from_u32('X' as u32 + axis_index).unwrap()).to_string(); let mut description = vec![ MessageValue::Unsigned(axis_index), - MessageValue::Unsigned(0), + MessageValue::Unsigned(1 << 8), MessageValue::Unsigned(u32::from(SENSOR_UNIT_METERS_PER_SECOND_SQUARED)), MessageValue::String(name, MAX_SIMPLE_STRING_LENGTH), + // Add extended attributes + MessageValue::Unsigned(0), + MessageValue::Signed(0), + MessageValue::Signed(i32::MIN), + MessageValue::Signed(-1i32), + MessageValue::Signed(i32::MAX), ]; result.append(&mut description); test_message(