scmi: Provide help for devices

Writing `--device help' on the command line will list all the
available devices and their parameters.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
This commit is contained in:
Milan Zamazal 2023-07-31 10:17:06 +02:00 committed by Alex Bennée
parent 4014a6366f
commit 73c536df2e
5 changed files with 108 additions and 3 deletions

View File

@ -30,6 +30,7 @@ the Examples section below.
Can be used multiple times for multiple exposed devices.
If no device is specified then no device will be provided to the
guest OS but VirtIO SCMI will be still available there.
Use `help` as the device ID to list help on all the available devices.
You can set `RUST_LOG` environment variable to `debug` to get maximum
messages on the standard error output.

View File

@ -1,9 +1,10 @@
// SPDX-FileCopyrightText: Red Hat, Inc.
// SPDX-License-Identifier: Apache-2.0
use std::collections::HashMap;
use log::debug;
use std::collections::HashMap;
use std::fmt::Write;
use std::process::exit;
use crate::{
scmi::{
@ -15,6 +16,10 @@ use crate::{
DeviceProperties,
};
enum ExitCodes {
Help = 1,
}
type DeviceSpecification = fn() -> Box<dyn ScmiDevice>;
type NameDeviceMapping = HashMap<String, DeviceSpecification>;
@ -24,6 +29,40 @@ pub fn available_devices() -> NameDeviceMapping {
devices
}
fn devices_help() -> String {
let mut help = String::new();
writeln!(help, "Available devices:").unwrap();
for (name, constructor) in available_devices().iter() {
let device = constructor();
let short_help = device.short_help();
let long_help = device.long_help();
let parameters_help = device.parameters_help();
writeln!(help, "\n- {name}: {short_help}").unwrap();
for line in long_help.lines() {
writeln!(help, " {line}").unwrap();
}
if !parameters_help.is_empty() {
writeln!(help, " Parameters:").unwrap();
for parameter in parameters_help {
writeln!(help, " - {parameter}").unwrap();
}
}
}
writeln!(help, "\nDevice specification example:").unwrap();
writeln!(
help,
"--device iio,path=/sys/bus/iio/devices/iio:device0,channel=in_accel"
)
.unwrap();
help
}
pub(crate) fn print_devices_help() {
let help = devices_help();
println!("{}", help);
exit(ExitCodes::Help as i32);
}
// Common sensor infrastructure
pub struct Sensor {
@ -44,6 +83,18 @@ trait SensorT: Send {
fn sensor(&self) -> &Sensor;
fn sensor_mut(&mut self) -> &mut Sensor;
fn short_help(&self) -> String {
"sensor".to_owned()
}
fn long_help(&self) -> String {
"".to_owned()
}
fn parameters_help(&self) -> Vec<String> {
vec!["name: an optional name of the sensor, max. 15 characters".to_owned()]
}
fn protocol(&self) -> ProtocolId {
SENSOR_PROTOCOL_ID
}
@ -182,6 +233,18 @@ impl ScmiDevice for SensorDevice {
self.0.configure(properties)
}
fn short_help(&self) -> String {
self.0.short_help()
}
fn long_help(&self) -> String {
self.0.long_help()
}
fn parameters_help(&self) -> Vec<String> {
self.0.parameters_help()
}
fn protocol(&self) -> ProtocolId {
self.0.protocol()
}
@ -207,6 +270,14 @@ impl SensorT for FakeSensor {
&mut self.sensor
}
fn short_help(&self) -> String {
"fake accelerometer".to_owned()
}
fn long_help(&self) -> String {
"A simple 3-axes sensor providing fake pre-defined values.".to_owned()
}
fn number_of_axes(&self) -> u32 {
3
}
@ -245,3 +316,25 @@ impl FakeSensor {
Box::new(sensor_device)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_help() {
let help = devices_help();
assert!(
help.contains("Available devices:\n"),
"global label missing"
);
assert!(help.contains("fake:"), "sensor name missing");
assert!(
help.contains("fake accelerometer"),
"short description missing"
);
assert!(help.contains("3-axes sensor"), "long description missing");
assert!(help.contains("Parameters:\n"), "parameter label missing");
assert!(help.contains("- name:"), "parameter `name' missing");
}
}

View File

@ -29,7 +29,11 @@ struct ScmiArgs {
#[clap(short, long, help = "vhost-user socket to use")]
socket_path: String,
// Specification of SCMI devices to create.
#[clap(short, long)]
#[clap(
short,
long,
help = "Devices to expose (use `help' device for more info)"
)]
#[arg(num_args(1..))]
device: Vec<String>,
}

View File

@ -480,6 +480,9 @@ pub enum ScmiDeviceError {
}
pub trait ScmiDevice: Send {
fn short_help(&self) -> String;
fn long_help(&self) -> String;
fn parameters_help(&self) -> Vec<String>;
fn configure(&mut self, properties: &DeviceProperties) -> Result<(), String>;
fn protocol(&self) -> ProtocolId;
fn handle(

View File

@ -21,6 +21,7 @@ use vmm_sys_util::epoll::EventSet;
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
use crate::devices::available_devices;
use crate::devices::print_devices_help;
use crate::scmi::ScmiDevice;
use crate::scmi::{MessageHeader, ScmiHandler, ScmiRequest};
use crate::VuScmiConfig;
@ -99,6 +100,9 @@ impl VuScmiBackend {
let mut handler = ScmiHandler::new();
let device_mapping = available_devices();
for (name, properties) in config.devices.iter() {
if name == "help" {
print_devices_help();
}
match device_mapping.get(name) {
Some(constructor) => {
let mut device: Box<dyn ScmiDevice> = constructor();