diff --git a/src/bin/bmputil-cli.rs b/src/bin/bmputil-cli.rs index f2338f7..4e90f53 100644 --- a/src/bin/bmputil-cli.rs +++ b/src/bin/bmputil-cli.rs @@ -439,9 +439,13 @@ fn power_command(cli_args: &CliArguments) -> Result<()> let device = results.pop_single("power").map_err(|kind| kind.error())?; let remote = device.bmd_serial_interface()?.remote()?; - let power = remote.get_target_power_state()?; + match remote.get_target_power_state() { + Ok(power) => info!("Device target power state: {}", power), + Err(e) => warn!("Failed to retrieve target power state: {}", e), + }; - info!("Device target power state: {}", power); + let voltage = remote.get_nrst_voltage()?; + info!("Target voltage: {}V", voltage); Ok(()) } diff --git a/src/serial/bmd_rsp.rs b/src/serial/bmd_rsp.rs index 08bf5b2..bfc0af5 100644 --- a/src/serial/bmd_rsp.rs +++ b/src/serial/bmd_rsp.rs @@ -18,14 +18,11 @@ pub struct BmdRspInterface handle: File, protocol_version: ProtocolVersion, - read_buffer: [u8; REMOTE_MAX_MSG_SIZE], + read_buffer: [u8; RemoteResponse::MAX_MSG_SIZE], read_buffer_fullness: usize, read_buffer_offset: usize, } -const REMOTE_START: &str = "+#!GA#"; -const REMOTE_HL_CHECK: &str = "!HC#"; - impl BmdRspInterface { pub fn from_path(serial_port: &Path) -> Result @@ -35,7 +32,7 @@ impl BmdRspInterface let handle = File::options().read(true).write(true).open(serial_port)?; // Construct an interface object - let mut result = Self { + let mut interface = Self { handle, // We start out by not knowing what version of protocol the probe talks protocol_version: ProtocolVersion::Unknown, @@ -44,20 +41,66 @@ impl BmdRspInterface // probe responses, being mindful that there's no good way to find out // how much data is waiting for us from the probe, so it's this or use // a read call a byte, which is extremely expensive! - read_buffer: [0; REMOTE_MAX_MSG_SIZE], + read_buffer: [0; RemoteResponse::MAX_MSG_SIZE], read_buffer_fullness: 0, read_buffer_offset: 0, }; // Call the OS-specific handle configuration function to ready // the interface handle for use with the remote serial protocol - result.init_handle()?; + interface.init_handle()?; // Start remote protocol communications with the probe - result.buffer_write(REMOTE_START)?; - let buffer = result.buffer_read()?; + Self::start_probe_communication(&mut interface)?; + + // Next, ask the probe for its protocol version number. + // For historical reasons this is part of the "high level" protocol set, but is + // actually a general request. + interface.protocol_version = Self::protocol_version(&mut interface)?; + trace!("Probe talks BMD RSP {}", interface.protocol_version); + + // Now the object is ready to go, return it to the caller + Ok(interface) + } + + fn protocol_version(interface: &mut BmdRspInterface) -> Result + { + interface.buffer_write(RemoteCommands::HL_CHECK)?; + let buffer = interface.buffer_read()?; + + // Check for communication failures, it should respond with at least 1 character + let (first, rest) = buffer + .split_at_checked(1) + .ok_or_else(|| eyre!("Probe failed to respond at all to protocol version request"))?; + + match (first.as_bytes()[0], rest) { + // If the request failed by way of a not implemented response, we're on a v0 protocol probe + (RemoteResponse::RESP_NOTSUP, _) => Ok(ProtocolVersion::V0), + (RemoteResponse::RESP_OK, rest) => { + // Parse out the version number from the response + let version = decode_response(&rest, 8); + // Then decode/translate that to a protocol version enum value + match version { + // Protocol version number 0 coresponds to an enchanced v0 probe protocol ("v0+") + 0 => Ok(ProtocolVersion::V0Plus), + 1 => Ok(ProtocolVersion::V1), + 2 => Ok(ProtocolVersion::V2), + 3 => Ok(ProtocolVersion::V3), + 4 => Ok(ProtocolVersion::V4), + _ => Err(eyre!("Unknown remote protocol version {}", version)), + } + }, + // If the probe responded with anything other than OK or not supported, we're done + _ => Err(eyre!("Probe responded improperly to protocol version request with {}", buffer)), + } + } + + fn start_probe_communication(interface: &mut BmdRspInterface) -> Result<()> + { + interface.buffer_write(RemoteCommands::START)?; + let buffer = interface.buffer_read()?; // Check if that failed for any reason - if buffer.is_empty() || buffer.as_bytes()[0] != REMOTE_RESP_OK { + if buffer.is_empty() || buffer.as_bytes()[0] != RemoteResponse::RESP_OK { let message = if buffer.len() > 1 { &buffer[1..] } else { @@ -66,41 +109,9 @@ impl BmdRspInterface return Err(eyre!("Remote protocol startup failed, error {}", message)); } // It did not, grand - we now have the firmware version string, so log it - debug!("Remote is {}", &buffer[1..]); - - // Next, ask the probe for its protocol version number. - // For historical reasons this is part of the "high level" protocol set, but is - // actually a general request. - result.buffer_write(REMOTE_HL_CHECK)?; - let buffer = result.buffer_read()?; - // Check for communication failures - if buffer.is_empty() { - return Err(eyre!("Probe failed to respond at all to protocol version request")); - } else if buffer.as_bytes()[0] != REMOTE_RESP_OK && buffer.as_bytes()[0] != REMOTE_RESP_NOTSUP { - // If the probe responded with anything other than OK or not supported, we're done - return Err(eyre!("Probe responded improperly to protocol version request with {}", buffer)); - } - // If the request failed by way of a not implemented response, we're on a v0 protocol probe - if buffer.as_bytes()[0] == REMOTE_RESP_NOTSUP { - result.protocol_version = ProtocolVersion::V0; - } else { - // Parse out the version number from the response - let version = decode_response(&buffer[1..], 8); - // Then decode/translate that to a protocol version enum value - result.protocol_version = match version { - // Protocol version number 0 coresponds to an enchanced v0 probe protocol ("v0+") - 0 => ProtocolVersion::V0Plus, - 1 => ProtocolVersion::V1, - 2 => ProtocolVersion::V2, - 3 => ProtocolVersion::V3, - 4 => ProtocolVersion::V4, - _ => return Err(eyre!("Unknown remote protocol version {}", version)), - }; - } - trace!("Probe talks BMD RSP {}", result.protocol_version); - - // Now the object is ready to go, return it to the caller - Ok(result) + let firmware_version = &buffer[1..]; + debug!("Remote is {}", firmware_version); + Ok(()) } /// Extract the remote protocol object to use to talk with this probe @@ -124,7 +135,7 @@ impl BmdRspInterface { // First drain the buffer till we see a start-of-response byte let mut response = 0; - while response != REMOTE_RESP { + while response != RemoteResponse::RESP { if self.read_buffer_offset == self.read_buffer_fullness { self.read_more_data()?; } @@ -133,7 +144,7 @@ impl BmdRspInterface } // Now collect the response - let mut buffer = [0u8; REMOTE_MAX_MSG_SIZE]; + let mut buffer = [0u8; RemoteResponse::MAX_MSG_SIZE]; let mut offset = 0; while offset < buffer.len() { // Check if we need more data or should use what's in the buffer already @@ -145,7 +156,7 @@ impl BmdRspInterface while self.read_buffer_offset + response_length < self.read_buffer_fullness && offset + response_length < buffer.len() { - if self.read_buffer[self.read_buffer_offset + response_length] == REMOTE_EOM { + if self.read_buffer[self.read_buffer_offset + response_length] == RemoteResponse::EOM { response_length += 1; break; } @@ -158,7 +169,7 @@ impl BmdRspInterface self.read_buffer_offset += response_length; offset += response_length - 1; // If this was because of REMOTE_EOM, return - if buffer[offset] == REMOTE_EOM { + if buffer[offset] == RemoteResponse::EOM { buffer[offset] = 0; let result = unsafe { String::from_utf8_unchecked(buffer[..offset].to_vec()) }; debug!("BMD RSP read: {}", result); diff --git a/src/serial/remote/mod.rs b/src/serial/remote/mod.rs index f08045d..bca8398 100644 --- a/src/serial/remote/mod.rs +++ b/src/serial/remote/mod.rs @@ -25,25 +25,49 @@ mod protocol_v3; mod protocol_v4; pub mod riscv_debug; -/// This is the max possible size of a remote protocol packet which a hard limitation of the -/// firmware on the probe - 1KiB is all the buffer that could be spared. -pub const REMOTE_MAX_MSG_SIZE: usize = 1024; +pub struct RemoteCommands; -/// Start of message marker for the protocol -pub const REMOTE_SOM: u8 = b'!'; -/// End of message marker for the protocol -pub const REMOTE_EOM: u8 = b'#'; -/// Response marker for the protocol -pub const REMOTE_RESP: u8 = b'&'; +impl RemoteCommands +{ + /// This command asks the probe if the reset pin is on + pub const GET_NRST: &str = "!Gz#"; + /// This command asks the probe if power is being supplied to the target + pub const GET_TARGET_POWER_STATE: &str = "!Gp#"; + /// This command asks the probe what it's protocol version is + pub const HL_CHECK: &str = "!HC#"; + /// This command asks the probe to initialise JTAG comms to any connected targets + pub const JTAG_INIT: &str = "!JS#"; + pub const NRST_TARGET_VOLTAGE: &str = "!GV#"; + /// This command asks the probe to the reset pin state + pub const SET_NRST: &str = "!GZ#"; + /// This command asks the probe to set the power state to the target + pub const SET_TARGET_POWER_STATE: &str = "!GP#"; + /// This command asks to start remote protocol communications with the probe + pub const START: &str = "+#!GA#"; +} + +pub struct RemoteResponse; -/// Probe response was okay and the data returned is valid -pub const REMOTE_RESP_OK: u8 = b'K'; -/// Probe found an error with a request parameter -pub const REMOTE_RESP_PARERR: u8 = b'P'; -/// Probe encountered an error executing the request -pub const REMOTE_RESP_ERR: u8 = b'E'; -/// Probe does not support the request made -pub const REMOTE_RESP_NOTSUP: u8 = b'N'; +impl RemoteResponse +{ + /// End of message marker for the protocol + pub const EOM: u8 = b'#'; + /// This is the max possible size of a remote protocol packet which a hard limitation of the + /// firmware on the probe - 1KiB is all the buffer that could be spared. + pub const MAX_MSG_SIZE: usize = 1024; + /// Response marker for the protocol + pub const RESP: u8 = b'&'; + /// Probe encountered an error executing the request + pub const RESP_ERR: u8 = b'E'; + /// Probe does not support the request made + pub const RESP_NOTSUP: u8 = b'N'; + /// Probe response was okay and the data returned is valid + pub const RESP_OK: u8 = b'K'; + /// Probe found an error with a request parameter + pub const RESP_PARERR: u8 = b'P'; + /// Start of message marker for the protocol + pub const SOM: u8 = b'!'; +} pub type TargetAddr32 = u32; pub type TargetAddr64 = u64; @@ -112,6 +136,8 @@ pub trait BmdRemoteProtocol fn supported_architectures(&self) -> Result>; fn supported_families(&self) -> Result>; fn get_target_power_state(&self) -> Result; + fn get_nrst_voltage(&self) -> Result; + fn get_nrst_val(&self) -> Result; } /// Types implementing this trait provide raw SWD access to targets over the BMD remote protocol diff --git a/src/serial/remote/protocol_v0.rs b/src/serial/remote/protocol_v0.rs index 21d94d2..4d5dede 100644 --- a/src/serial/remote/protocol_v0.rs +++ b/src/serial/remote/protocol_v0.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-FileCopyrightText: 2025 1BitSquared // SPDX-FileContributor: Written by Rachel Mant +// SPDX-FileContributor: Modified by P-Storm use std::sync::{Arc, Mutex, MutexGuard}; @@ -11,7 +12,7 @@ use crate::serial::bmd_rsp::BmdRspInterface; use crate::serial::remote::adi::{AdiV5AccessPort, AdiV5DebugPort}; use crate::serial::remote::{ Align, BmdAdiV5Protocol, BmdJtagProtocol, BmdRemoteProtocol, BmdRiscvProtocol, BmdSwdProtocol, JtagDev, - REMOTE_RESP_ERR, TargetAddr64, TargetArchitecture, TargetFamily, + RemoteCommands, RemoteResponse, TargetAddr64, TargetArchitecture, TargetFamily, }; pub struct RemoteV0 @@ -83,7 +84,7 @@ impl BmdRemoteProtocol for RemoteV0 self.interface().buffer_write(REMOTE_JTAG_INIT)?; let buffer = self.interface().buffer_read()?; // If that failed for some reason, report it and abort - if buffer.is_empty() || buffer.as_bytes()[0] == REMOTE_RESP_ERR { + if buffer.is_empty() || buffer.as_bytes()[0] == RemoteResponse::RESP_ERR { let message = if buffer.len() > 1 { &buffer[1..] } else { @@ -102,7 +103,7 @@ impl BmdRemoteProtocol for RemoteV0 self.interface().buffer_write(REMOTE_SWD_INIT)?; let buffer = self.interface().buffer_read()?; // If that failed for some reason, report it and abort - if buffer.is_empty() || buffer.as_bytes()[0] == REMOTE_RESP_ERR { + if buffer.is_empty() || buffer.as_bytes()[0] == RemoteResponse::RESP_ERR { let message = if buffer.len() > 1 { &buffer[1..] } else { @@ -169,6 +170,44 @@ impl BmdRemoteProtocol for RemoteV0 { Err(eyre!("Not supported")) } + + fn get_nrst_voltage(&self) -> Result + { + self.interface().buffer_write(RemoteCommands::NRST_TARGET_VOLTAGE)?; + let buffer = self.interface().buffer_read()?; + + if buffer.is_empty() || buffer.as_bytes()[0] != RemoteResponse::RESP_OK { + return Err(eyre!("Target voltage request failed")); + } + + if buffer.len() < 2 { + return Err(eyre!("Target voltage response is too short")); + } + + let value = buffer + .get(1..buffer.len().saturating_sub(1)) + .expect("Should have some value"); + + value + .parse::() + .map_err(|e| eyre!("Can't parse target voltage value to a float, input {}, reason: {}", value, e)) + } + + fn get_nrst_val(&self) -> Result + { + self.interface().buffer_write(RemoteCommands::GET_NRST)?; + let buffer = self.interface().buffer_read()?; + + if buffer.is_empty() || buffer.as_bytes()[0] != RemoteResponse::RESP_OK { + return Err(eyre!("srst value request failed")); + } + + if buffer.len() < 2 { + return Err(eyre!("srst value response is too short")); + } + + Ok(buffer.as_bytes()[1] == b'1') + } } impl From>> for RemoteV0Plus @@ -253,6 +292,16 @@ impl BmdRemoteProtocol for RemoteV0Plus { self.0.get_target_power_state() } + + fn get_nrst_voltage(&self) -> Result + { + self.0.get_nrst_voltage() + } + + fn get_nrst_val(&self) -> Result + { + self.0.get_nrst_val() + } } impl From>> for RemoteV0JTAG diff --git a/src/serial/remote/protocol_v1.rs b/src/serial/remote/protocol_v1.rs index 8eb2a18..67c1340 100644 --- a/src/serial/remote/protocol_v1.rs +++ b/src/serial/remote/protocol_v1.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-FileCopyrightText: 2025 1BitSquared // SPDX-FileContributor: Written by Rachel Mant +// SPDX-FileContributor: Modified by P-Storm use std::sync::{Arc, Mutex, MutexGuard}; @@ -115,6 +116,16 @@ impl BmdRemoteProtocol for RemoteV1 { self.0.get_target_power_state() } + + fn get_nrst_voltage(&self) -> Result + { + self.0.get_nrst_voltage() + } + + fn get_nrst_val(&self) -> Result + { + self.0.get_nrst_val() + } } impl From>> for RemoteV1ADIv5 diff --git a/src/serial/remote/protocol_v2.rs b/src/serial/remote/protocol_v2.rs index 5026f55..c771803 100644 --- a/src/serial/remote/protocol_v2.rs +++ b/src/serial/remote/protocol_v2.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-FileCopyrightText: 2025 1BitSquared // SPDX-FileContributor: Written by Rachel Mant +// SPDX-FileContributor: Modified by P-Storm use std::sync::{Arc, Mutex, MutexGuard}; @@ -11,20 +12,14 @@ use crate::serial::bmd_rsp::BmdRspInterface; use crate::serial::remote::protocol_v0::RemoteV0JTAG; use crate::serial::remote::protocol_v1::RemoteV1; use crate::serial::remote::{ - BmdAdiV5Protocol, BmdJtagProtocol, BmdRemoteProtocol, BmdRiscvProtocol, BmdSwdProtocol, JtagDev, REMOTE_RESP_ERR, - REMOTE_RESP_OK, TargetArchitecture, TargetFamily, + BmdAdiV5Protocol, BmdJtagProtocol, BmdRemoteProtocol, BmdRiscvProtocol, BmdSwdProtocol, JtagDev, RemoteCommands, + RemoteResponse, TargetArchitecture, TargetFamily, }; pub struct RemoteV2(RemoteV1); pub struct RemoteV2JTAG(RemoteV0JTAG); -const REMOTE_JTAG_INIT: &str = "!JS#"; -/// This command asks the probe if the power is used -#[allow(dead_code)] -const REMOTE_TARGET_VOLTAGE: &str = "!GV#"; -const REMOTE_GET_TARGET_POWER_STATE: &str = "!Gp#"; - impl From>> for RemoteV2 { fn from(interface: Arc>) -> Self @@ -58,10 +53,10 @@ impl BmdRemoteProtocol for RemoteV2 { // Try to have the probe initialise JTAG comms to any connected targets debug!("Remote JTAG init"); - self.interface().buffer_write(REMOTE_JTAG_INIT)?; + self.interface().buffer_write(RemoteCommands::JTAG_INIT)?; let buffer = self.interface().buffer_read()?; // If that failed for some reason, report it and abort - if buffer.is_empty() || buffer.as_bytes()[0] == REMOTE_RESP_ERR { + if buffer.is_empty() || buffer.as_bytes()[0] == RemoteResponse::RESP_ERR { let message = if buffer.len() > 1 { &buffer[1..] } else { @@ -126,10 +121,10 @@ impl BmdRemoteProtocol for RemoteV2 fn get_target_power_state(&self) -> Result { - self.interface().buffer_write(REMOTE_GET_TARGET_POWER_STATE)?; + self.interface().buffer_write(RemoteCommands::GET_TARGET_POWER_STATE)?; let buffer = self.interface().buffer_read()?; - if buffer.is_empty() || buffer.as_bytes()[0] != REMOTE_RESP_OK { + if buffer.is_empty() || buffer.as_bytes()[0] != RemoteResponse::RESP_OK { return Err(eyre!("Supported current powered request failed")); } @@ -139,6 +134,16 @@ impl BmdRemoteProtocol for RemoteV2 Ok(buffer.as_bytes()[1] == b'1') } + + fn get_nrst_voltage(&self) -> Result + { + self.0.get_nrst_voltage() + } + + fn get_nrst_val(&self) -> Result + { + self.0.get_nrst_val() + } } impl From>> for RemoteV2JTAG diff --git a/src/serial/remote/protocol_v3.rs b/src/serial/remote/protocol_v3.rs index 9f4e68f..02de3e6 100644 --- a/src/serial/remote/protocol_v3.rs +++ b/src/serial/remote/protocol_v3.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-FileCopyrightText: 2025 1BitSquared // SPDX-FileContributor: Written by Rachel Mant +// SPDX-FileContributor: Modified by P-Storm use std::sync::{Arc, Mutex, MutexGuard}; @@ -111,6 +112,16 @@ impl BmdRemoteProtocol for RemoteV3 { self.0.get_target_power_state() } + + fn get_nrst_voltage(&self) -> Result + { + self.0.get_nrst_voltage() + } + + fn get_nrst_val(&self) -> Result + { + self.0.get_nrst_val() + } } impl From>> for RemoteV3ADIv5 diff --git a/src/serial/remote/protocol_v4.rs b/src/serial/remote/protocol_v4.rs index 2f6ce86..c712c38 100644 --- a/src/serial/remote/protocol_v4.rs +++ b/src/serial/remote/protocol_v4.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-FileCopyrightText: 2025 1BitSquared // SPDX-FileContributor: Written by Rachel Mant +// SPDX-FileContributor: Modified by P-Storm use std::fmt::Display; use std::sync::{Arc, Mutex, MutexGuard}; @@ -15,7 +16,7 @@ use crate::serial::remote::protocol_v3::RemoteV3; use crate::serial::remote::riscv_debug::RiscvDmi; use crate::serial::remote::{ Align, BmdAdiV5Protocol, BmdJtagProtocol, BmdRemoteProtocol, BmdRiscvProtocol, BmdSwdProtocol, JtagDev, - REMOTE_RESP_NOTSUP, REMOTE_RESP_OK, TargetAddr64, TargetArchitecture, TargetFamily, decode_response, + RemoteResponse, TargetAddr64, TargetArchitecture, TargetFamily, decode_response, }; pub struct RemoteV4 @@ -87,7 +88,7 @@ impl RemoteV4 let buffer = iface.buffer_read()?; drop(iface); // Check for communication failures - if buffer.is_empty() || buffer.as_bytes()[0] != REMOTE_RESP_OK { + if buffer.is_empty() || buffer.as_bytes()[0] != RemoteResponse::RESP_OK { return Err(eyre!( "Error talking with probe, expected OK response to supported accelerations query, got {:?}", buffer @@ -180,14 +181,16 @@ impl BmdRemoteProtocol for RemoteV4 self.interface().buffer_write(REMOTE_HL_ARCHS)?; let buffer = self.interface().buffer_read()?; // Check too see if that failed for some reason - if buffer.is_empty() || (buffer.as_bytes()[0] != REMOTE_RESP_OK && buffer.as_bytes()[0] != REMOTE_RESP_NOTSUP) { + if buffer.is_empty() || + (buffer.as_bytes()[0] != RemoteResponse::RESP_OK && buffer.as_bytes()[0] != RemoteResponse::RESP_NOTSUP) + { let message = if buffer.len() > 1 { &buffer[1..] } else { "unknown" }; Err(eyre!("Supported architectures request failed, error {}", message)) - } else if buffer.as_bytes()[0] == REMOTE_RESP_NOTSUP { + } else if buffer.as_bytes()[0] == RemoteResponse::RESP_NOTSUP { // If we get here, the probe talks v4 but doesn't know this command - meaning pre-v2.0.0 firmware // but post-v1.10.2. Ask the user to upgrade off development firmware onto the release or later. warn!("Please upgrade your firmware to allow checking supported target architectures to work properly"); @@ -205,14 +208,16 @@ impl BmdRemoteProtocol for RemoteV4 self.interface().buffer_write(REMOTE_HL_FAMILIES)?; let buffer = self.interface().buffer_read()?; // Check too see if that failed for some reason - if buffer.is_empty() || (buffer.as_bytes()[0] != REMOTE_RESP_OK && buffer.as_bytes()[0] != REMOTE_RESP_NOTSUP) { + if buffer.is_empty() || + (buffer.as_bytes()[0] != RemoteResponse::RESP_OK && buffer.as_bytes()[0] != RemoteResponse::RESP_NOTSUP) + { let message = if buffer.len() > 1 { &buffer[1..] } else { "unknown" }; Err(eyre!("Supported architectures request failed, error {}", message)) - } else if buffer.as_bytes()[0] == REMOTE_RESP_NOTSUP { + } else if buffer.as_bytes()[0] == RemoteResponse::RESP_NOTSUP { // If we get here, the probe talks v4 but doesn't know this command - meaning pre-v2.0.0 firmware // but post-v1.10.2. Ask the user to upgrade off development firmware onto the release or later. warn!("Please upgrade your firmware to allow checking supported target families to work properly"); @@ -228,6 +233,16 @@ impl BmdRemoteProtocol for RemoteV4 { self.inner_protocol.get_target_power_state() } + + fn get_nrst_voltage(&self) -> Result + { + self.inner_protocol.get_nrst_voltage() + } + + fn get_nrst_val(&self) -> Result + { + self.inner_protocol.get_nrst_val() + } } impl From>> for RemoteV4ADIv5