blob: 323053135e5d668c2a27307486efdd31a9f84fb5 [file] [log] [blame]
// Copyright 2022, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::convert::{TryFrom, TryInto};
use bytes::Bytes;
use log::error;
use num_traits::FromPrimitive;
use crate::error::{Error, Result};
use crate::params::uci_packets::{
AppConfigTlv, AppConfigTlvType, Controlees, CountryCode, DeviceConfigId, DeviceConfigTlv,
ResetConfig, SessionId, SessionType, UpdateMulticastListAction,
};
use uwb_uci_packets::{build_session_update_controller_multicast_list_cmd, GroupId, MessageType};
/// The enum to represent the UCI commands. The definition of each field should follow UCI spec.
#[allow(missing_docs)]
#[derive(Debug, Clone, PartialEq)]
pub enum UciCommand {
DeviceReset {
reset_config: ResetConfig,
},
CoreGetDeviceInfo,
CoreGetCapsInfo,
CoreSetConfig {
config_tlvs: Vec<DeviceConfigTlv>,
},
CoreGetConfig {
cfg_id: Vec<DeviceConfigId>,
},
SessionInit {
session_id: SessionId,
session_type: SessionType,
},
SessionDeinit {
session_id: SessionId,
},
SessionSetAppConfig {
session_id: SessionId,
config_tlvs: Vec<AppConfigTlv>,
},
SessionGetAppConfig {
session_id: SessionId,
app_cfg: Vec<AppConfigTlvType>,
},
SessionGetCount,
SessionGetState {
session_id: SessionId,
},
SessionUpdateControllerMulticastList {
session_id: SessionId,
action: UpdateMulticastListAction,
controlees: Controlees,
},
SessionUpdateActiveRoundsDtTag {
session_id: u32,
ranging_round_indexes: Vec<u8>,
},
SessionStart {
session_id: SessionId,
},
SessionStop {
session_id: SessionId,
},
SessionGetRangingCount {
session_id: SessionId,
},
AndroidSetCountryCode {
country_code: CountryCode,
},
AndroidGetPowerStats,
RawUciCmd {
mt: u32,
gid: u32,
oid: u32,
payload: Vec<u8>,
},
}
impl TryFrom<UciCommand> for uwb_uci_packets::UciControlPacketPacket {
type Error = Error;
fn try_from(cmd: UciCommand) -> std::result::Result<Self, Self::Error> {
let packet = match cmd {
// UCI Session Config Commands
UciCommand::SessionInit { session_id, session_type } => {
uwb_uci_packets::SessionInitCmdBuilder { session_id, session_type }.build().into()
}
UciCommand::SessionDeinit { session_id } => {
uwb_uci_packets::SessionDeinitCmdBuilder { session_id }.build().into()
}
UciCommand::CoreGetDeviceInfo => {
uwb_uci_packets::GetDeviceInfoCmdBuilder {}.build().into()
}
UciCommand::CoreGetCapsInfo => uwb_uci_packets::GetCapsInfoCmdBuilder {}.build().into(),
UciCommand::SessionGetState { session_id } => {
uwb_uci_packets::SessionGetStateCmdBuilder { session_id }.build().into()
}
UciCommand::SessionUpdateControllerMulticastList { session_id, action, controlees } => {
build_session_update_controller_multicast_list_cmd(session_id, action, controlees)
.map_err(|_| Error::BadParameters)?
.into()
}
UciCommand::CoreSetConfig { config_tlvs } => {
uwb_uci_packets::SetConfigCmdBuilder { tlvs: config_tlvs }.build().into()
}
UciCommand::CoreGetConfig { cfg_id } => uwb_uci_packets::GetConfigCmdBuilder {
cfg_id: cfg_id.into_iter().map(|item| item as u8).collect(),
}
.build()
.into(),
UciCommand::SessionSetAppConfig { session_id, config_tlvs } => {
uwb_uci_packets::SessionSetAppConfigCmdBuilder {
session_id,
tlvs: config_tlvs.into_iter().map(|tlv| tlv.into_inner()).collect(),
}
.build()
.into()
}
UciCommand::SessionGetAppConfig { session_id, app_cfg } => {
uwb_uci_packets::SessionGetAppConfigCmdBuilder {
session_id,
app_cfg: app_cfg.into_iter().map(|item| item as u8).collect(),
}
.build()
.into()
}
UciCommand::SessionUpdateActiveRoundsDtTag { session_id, ranging_round_indexes } => {
uwb_uci_packets::SessionUpdateActiveRoundsDtTagCmdBuilder {
session_id,
ranging_round_indexes,
}
.build()
.into()
}
UciCommand::AndroidGetPowerStats => {
uwb_uci_packets::AndroidGetPowerStatsCmdBuilder {}.build().into()
}
UciCommand::RawUciCmd { mt, gid, oid, payload } => {
build_raw_uci_cmd_packet(mt, gid, oid, payload)?
}
UciCommand::SessionGetCount => {
uwb_uci_packets::SessionGetCountCmdBuilder {}.build().into()
}
UciCommand::AndroidSetCountryCode { country_code } => {
uwb_uci_packets::AndroidSetCountryCodeCmdBuilder {
country_code: country_code.into(),
}
.build()
.into()
}
UciCommand::DeviceReset { reset_config } => {
uwb_uci_packets::DeviceResetCmdBuilder { reset_config }.build().into()
}
// UCI Session Control Commands
UciCommand::SessionStart { session_id } => {
uwb_uci_packets::SessionStartCmdBuilder { session_id }.build().into()
}
UciCommand::SessionStop { session_id } => {
uwb_uci_packets::SessionStopCmdBuilder { session_id }.build().into()
}
UciCommand::SessionGetRangingCount { session_id } => {
uwb_uci_packets::SessionGetRangingCountCmdBuilder { session_id }.build().into()
}
};
Ok(packet)
}
}
fn build_raw_uci_cmd_packet(
mt: u32,
gid: u32,
oid: u32,
payload: Vec<u8>,
) -> Result<uwb_uci_packets::UciControlPacketPacket> {
let group_id = GroupId::from_u32(gid).ok_or_else(|| {
error!("Invalid GroupId: {}", gid);
Error::BadParameters
})?;
let payload = if payload.is_empty() { None } else { Some(Bytes::from(payload)) };
let opcode = oid.try_into().map_err(|_| {
error!("Invalid opcod: {}", oid);
Error::BadParameters
})?;
let message_type = MessageType::from_u32(mt).ok_or_else(|| {
error!("Invalid MessageType: {}", mt);
Error::BadParameters
})?;
match uwb_uci_packets::build_uci_control_packet(message_type, group_id, opcode, payload) {
Some(cmd) => Ok(cmd),
None => Err(Error::BadParameters),
}
}