| // Copyright 2022 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //! Provide a simple uwbd client, used to verify the uwbd service end-to-end. |
| |
| use std::error::Error; |
| use std::time::Duration; |
| |
| use clap::{arg, Command, ArgGroup}; |
| use dbus::blocking::{Connection, Proxy}; |
| use protobuf::{Message, RepeatedField}; |
| use uwb_core::proto::bindings::{ |
| EnableResponse, InitSessionRequest, SessionType, FiraAppConfigParams, DeviceType, |
| RangingRoundUsage, StsConfig, MultiNodeMode, UwbChannel, MacFcsType, RangingRoundControl, |
| AoaResultRequest, RangeDataNtfConfig, DeviceRole, RframeConfig, PsduDataRate, RangingTimeStruct, |
| TxAdaptivePayloadPower, PrfMode, ScheduledMode, KeyRotation, MacAddressMode, HoppingMode, |
| ResultReportConfig, BprfPhrDataRate, StsLength, InitSessionResponse, PreambleDuration, |
| StartRangingRequest, StartRangingResponse, StopRangingRequest, StopRangingResponse, |
| DeinitSessionRequest, DeinitSessionResponse, |
| }; |
| use uwb_core::proto::utils::write_to_bytes; |
| |
| use uwbd::common::{DBUS_OBJECT_PATH, DBUS_SERVICE_NAME}; |
| use uwbd::dbus_bindings::client::OrgChromiumUwbd; |
| |
| // Returns the clap Command that this CLI will use |
| fn cli() -> Command { |
| Command::new("uwbd_client") |
| .about("CLI connecting to uwbd to start and stop UWB ranging sessions") |
| .subcommand_required(true) |
| .arg_required_else_help(true) |
| .subcommand( |
| Command::new("start") |
| .about("Enables UWB, initializes and starts a ranging session.") |
| .args_conflicts_with_subcommands(true) |
| .arg(arg!(-i --initiator "Uses the initiator + controller parameter profile")) |
| .arg(arg!(-r --responder "Uses the responder + controlee parameter profile")) |
| .group(ArgGroup::new("vers") |
| .args(["initiator", "responder"]) |
| .required(true)) |
| ) |
| .subcommand( |
| Command::new("stop") |
| .about("stops and deinits the default UWB ranging session") |
| ) |
| } |
| |
| |
| // Returns a FiraAppConfigParams that has been initialized with a default set of parameters. |
| // The profile specific parameters i.e. Device Type, Device Role, Mac Address and Destination |
| // Mac Addresses are not set. |
| fn create_app_config_params() -> FiraAppConfigParams { |
| let mut app_config_params = FiraAppConfigParams::new(); |
| app_config_params.set_device_type(DeviceType::CONTROLEE); |
| app_config_params.set_ranging_round_usage(RangingRoundUsage::DS_TWR); |
| app_config_params.set_sts_config(StsConfig::STATIC); |
| app_config_params.set_multi_node_mode(MultiNodeMode::UNICAST); |
| app_config_params.set_channel_number(UwbChannel::CHANNEL_9); |
| |
| app_config_params.set_slot_duration_rstu(2400); |
| app_config_params.set_ranging_interval_ms(200); |
| app_config_params.set_mac_fcs_type(MacFcsType::CRC_16); |
| |
| let mut ranging_round_control = RangingRoundControl::new(); |
| ranging_round_control.set_ranging_result_report_message(true); |
| ranging_round_control.set_control_message(true); |
| ranging_round_control.set_measurement_report_message(false); |
| app_config_params.set_ranging_round_control(ranging_round_control); |
| app_config_params.set_aoa_result_request(AoaResultRequest::REQ_AOA_RESULTS); |
| app_config_params.set_range_data_ntf_config(RangeDataNtfConfig::RANGE_DATA_NTF_CONFIG_ENABLE); |
| app_config_params.set_range_data_ntf_proximity_near_cm(0); |
| app_config_params.set_range_data_ntf_proximity_far_cm(20000); |
| app_config_params.set_device_role(DeviceRole::RESPONDER); |
| app_config_params.set_rframe_config(RframeConfig::SP3); |
| app_config_params.set_preamble_code_index(9); |
| app_config_params.set_sfd_id(2); |
| app_config_params.set_psdu_data_rate(PsduDataRate::RATE_6M_81); |
| app_config_params.set_preamble_duration(PreambleDuration::T64_SYMBOLS); |
| app_config_params.set_ranging_time_struct(RangingTimeStruct::BLOCK_BASED_SCHEDULING); |
| app_config_params.set_slots_per_rr(25); |
| app_config_params.set_tx_adaptive_payload_power(TxAdaptivePayloadPower:: |
| TX_ADAPTIVE_PAYLOAD_POWER_DISABLE); |
| app_config_params.set_responder_slot_index(1); |
| app_config_params.set_prf_mode(PrfMode::BPRF); |
| app_config_params.set_scheduled_mode(ScheduledMode::TIME_SCHEDULED_RANGING); |
| app_config_params.set_key_rotation(KeyRotation::KEY_ROTATION_DISABLE); |
| app_config_params.set_key_rotation_rate(0); |
| app_config_params.set_session_priority(70); |
| app_config_params.set_mac_address_mode(MacAddressMode::MAC_ADDRESS_2_BYTES); |
| app_config_params.set_vendor_id(vec![0x0,0x0]); |
| app_config_params.set_static_sts_iv(vec![0x0,0x0,0x0,0x0,0x0,0x0]); |
| app_config_params.set_number_of_sts_segments(1); |
| app_config_params.set_max_rr_retry(0); |
| app_config_params.set_uwb_initiation_time_ms(0); |
| app_config_params.set_hopping_mode(HoppingMode::HOPPING_MODE_DISABLE); |
| app_config_params.set_block_stride_length(0); |
| let mut result_report_config = ResultReportConfig::new(); |
| result_report_config.set_tof(true); |
| result_report_config.set_aoa_azimuth(false); |
| result_report_config.set_aoa_elevation(false); |
| result_report_config.set_aoa_fom(false); |
| app_config_params.set_result_report_config(result_report_config); |
| app_config_params.set_in_band_termination_attempt_count(1); |
| app_config_params.set_sub_session_id(0); |
| app_config_params.set_bprf_phr_data_rate(BprfPhrDataRate::BPRF_PHR_DATA_RATE_850K); |
| app_config_params.set_max_number_of_measurements(0); |
| app_config_params.set_sts_length(StsLength::LENGTH_64); |
| app_config_params.set_number_of_range_measurements(0); //dropped by HAL |
| app_config_params.set_number_of_aoa_azimuth_measurements(0); //dropped by HAL |
| app_config_params.set_number_of_aoa_elevation_measurements(0); //dropped by HAL |
| |
| app_config_params |
| } |
| |
| |
| fn main() -> Result<(), Box<dyn Error>> { |
| let matches = cli().get_matches(); |
| |
| let session_id = 1; |
| const TIMEOUT: Duration = Duration::from_millis(5000); |
| |
| // Connect to D-Bus and create uwbd proxy. |
| let dbus_connection = Connection::new_system()?; |
| let uwbd_proxy = Proxy::new( |
| DBUS_SERVICE_NAME, |
| DBUS_OBJECT_PATH, |
| TIMEOUT, |
| &dbus_connection, |
| ); |
| |
| match matches.subcommand(){ |
| Some(("start", sub_matches)) => { |
| // Create params |
| let mut app_config_params = create_app_config_params(); |
| |
| // Set initiator profile in params |
| if sub_matches.get_flag("initiator"){ |
| app_config_params.set_device_type(DeviceType::CONTROLLER); |
| app_config_params.set_device_role(DeviceRole::INITIATOR); |
| app_config_params.set_device_mac_address(vec![0x08,0x00]); |
| let mut dst_mac_addresses: RepeatedField<Vec<u8>> = RepeatedField::new(); |
| dst_mac_addresses.push(vec![0x04,0x00]); |
| app_config_params.set_dst_mac_address(dst_mac_addresses); |
| println!("Using initiator profile"); |
| } |
| // Set responder profile in params |
| if sub_matches.get_flag("responder"){ |
| app_config_params.set_device_type(DeviceType::CONTROLEE); |
| app_config_params.set_device_role(DeviceRole::RESPONDER); |
| app_config_params.set_device_mac_address(vec![0x04,0x00]); |
| let mut dst_mac_addresses: RepeatedField<Vec<u8>> = RepeatedField::new(); |
| dst_mac_addresses.push(vec![0x08,0x00]); |
| app_config_params.set_dst_mac_address(dst_mac_addresses); |
| println!("Using responder profile"); |
| } |
| |
| // Enable UwB |
| let result_bytes = uwbd_proxy.enable()?; |
| let result = EnableResponse::parse_from_bytes(&result_bytes)?; |
| println!("Enable() returns: {:?}", result.status); |
| |
| // Initialize the ranging session |
| let mut request = InitSessionRequest::new(); |
| request.set_session_id(session_id); |
| request.set_session_type(SessionType::FIRA_RANGING_SESSION); |
| request.set_params(app_config_params); |
| let request_bytes = write_to_bytes(&request)?; |
| let result_bytes = uwbd_proxy.init_session(request_bytes)?; |
| let result = InitSessionResponse::parse_from_bytes(&result_bytes)?; |
| println!("InitSessionRequest returns: {:?}", result.status); |
| |
| // Start the ranging session |
| let mut request = StartRangingRequest::new(); |
| request.set_session_id(session_id); |
| let request_bytes = write_to_bytes(&request)?; |
| let result_bytes = uwbd_proxy.start_ranging(request_bytes)?; |
| let result = StartRangingResponse::parse_from_bytes(&result_bytes)?; |
| println!("StartRangingResponse returns: {:?}", result.status); |
| } |
| Some(("stop", _)) => { |
| // Stop the ranging session |
| let mut request = StopRangingRequest::new(); |
| request.set_session_id(session_id); |
| let request_bytes = write_to_bytes(&request)?; |
| let result_bytes = uwbd_proxy.stop_ranging(request_bytes)?; |
| let result = StopRangingResponse::parse_from_bytes(&result_bytes)?; |
| println!("StopRangingResponse returns: {:?}", result.status); |
| |
| // Deinit the ranging session |
| let mut request = DeinitSessionRequest::new(); |
| request.set_session_id(session_id); |
| let request_bytes = write_to_bytes(&request)?; |
| let result_bytes = uwbd_proxy.stop_ranging(request_bytes)?; |
| let result = DeinitSessionResponse::parse_from_bytes(&result_bytes)?; |
| println!("DeinitSessionResponse returns: {:?}", result.status); |
| } |
| _ => unreachable!(), |
| } |
| |
| Ok(()) |
| } |