bt_rfcomm/frame/mux_commands/
dlc_parameter_negotiation.rsuse bitfield::bitfield;
use packet_encoding::{decodable_enum, Decodable, Encodable};
use crate::frame::FrameParseError;
use crate::DLCI;
const DLC_PARAMETER_NEGOTIATION_LENGTH: usize = 8;
pub const DEFAULT_INITIAL_CREDITS: u8 = 7;
decodable_enum! {
pub enum CreditBasedFlowHandshake<u8, FrameParseError, OutOfRange> {
Unsupported = 0x0,
SupportedRequest = 0xF,
SupportedResponse = 0xE,
}
}
bitfield! {
struct DLCParameterNegotiationFields([u8]);
impl Debug;
pub u8, dlci_raw, set_dlci: 5,0;
pub u8, credit_handshake, set_credit_handshake: 15, 12;
pub u8, priority, set_priority: 21, 16;
pub u16, max_frame_size, set_max_frame_size: 47, 32;
pub u8, initial_credits, set_initial_credits: 58, 56;
}
impl DLCParameterNegotiationFields<[u8; DLC_PARAMETER_NEGOTIATION_LENGTH]> {
fn dlci(&self) -> Result<DLCI, FrameParseError> {
DLCI::try_from(self.dlci_raw())
}
fn credit_based_flow_handshake(&self) -> Result<CreditBasedFlowHandshake, FrameParseError> {
CreditBasedFlowHandshake::try_from(self.credit_handshake())
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ParameterNegotiationParams {
pub dlci: DLCI,
pub credit_based_flow_handshake: CreditBasedFlowHandshake,
pub priority: u8,
pub max_frame_size: u16,
pub initial_credits: u8,
}
impl ParameterNegotiationParams {
pub fn default_command(dlci: DLCI, max_frame_size: u16) -> Self {
Self {
dlci,
credit_based_flow_handshake: CreditBasedFlowHandshake::SupportedRequest,
priority: 1,
max_frame_size,
initial_credits: DEFAULT_INITIAL_CREDITS,
}
}
pub fn credit_based_flow(&self) -> bool {
self.credit_based_flow_handshake == CreditBasedFlowHandshake::SupportedRequest
|| self.credit_based_flow_handshake == CreditBasedFlowHandshake::SupportedResponse
}
}
impl Decodable for ParameterNegotiationParams {
type Error = FrameParseError;
fn decode(buf: &[u8]) -> Result<Self, FrameParseError> {
if buf.len() != DLC_PARAMETER_NEGOTIATION_LENGTH {
return Err(FrameParseError::InvalidBufferLength(
DLC_PARAMETER_NEGOTIATION_LENGTH,
buf.len(),
));
}
let mut fixed_buf = [0; DLC_PARAMETER_NEGOTIATION_LENGTH];
fixed_buf.copy_from_slice(&buf[..]);
let parameters = DLCParameterNegotiationFields(fixed_buf);
let dlci = parameters.dlci()?;
let credit_based_flow_handshake = parameters.credit_based_flow_handshake()?;
let priority = parameters.priority();
let max_frame_size = parameters.max_frame_size();
let initial_credits = parameters.initial_credits();
Ok(ParameterNegotiationParams {
dlci,
credit_based_flow_handshake,
priority,
max_frame_size,
initial_credits,
})
}
}
impl Encodable for ParameterNegotiationParams {
type Error = FrameParseError;
fn encoded_len(&self) -> usize {
DLC_PARAMETER_NEGOTIATION_LENGTH
}
fn encode(&self, buf: &mut [u8]) -> Result<(), FrameParseError> {
if buf.len() < self.encoded_len() {
return Err(FrameParseError::BufferTooSmall);
}
let mut params = DLCParameterNegotiationFields([0; DLC_PARAMETER_NEGOTIATION_LENGTH]);
params.set_dlci(u8::from(self.dlci));
params.set_credit_handshake(u8::from(&self.credit_based_flow_handshake));
params.set_priority(self.priority);
params.set_max_frame_size(self.max_frame_size);
params.set_initial_credits(self.initial_credits);
let params_bytes = params.0;
buf.copy_from_slice(¶ms_bytes[..]);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
#[test]
fn test_parse_too_small_buf() {
let buf = [0x00, 0x00, 0x00]; assert_matches!(
ParameterNegotiationParams::decode(&buf[..]),
Err(FrameParseError::InvalidBufferLength(DLC_PARAMETER_NEGOTIATION_LENGTH, 3))
);
}
#[test]
fn test_parse_too_large_buf() {
let buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; assert_matches!(
ParameterNegotiationParams::decode(&buf[..]),
Err(FrameParseError::InvalidBufferLength(DLC_PARAMETER_NEGOTIATION_LENGTH, 9))
);
}
#[test]
fn test_parse_invalid_dlci() {
let buf = [
0b00000001, 0b11110000, 0b00000010, 0b00000000, 0b00000010, 0b00000000, 0b00000000, 0b00000001, ];
assert_matches!(
ParameterNegotiationParams::decode(&buf[..]),
Err(FrameParseError::InvalidDLCI(1))
);
}
#[test]
fn test_parse_invalid_credit_handshake() {
let buf = [
0b00000000, 0b10010000, 0b00000010, 0b00000000, 0b00000010, 0b00000000, 0b00000000, 0b00000001, ];
assert_matches!(
ParameterNegotiationParams::decode(&buf[..]),
Err(FrameParseError::OutOfRange)
);
}
#[test]
fn test_parse_valid_command() {
let buf = [
0b00000000, 0b11110000, 0b00001000, 0b00000000, 0b00000100, 0b00000000, 0b00000000, 0b00000001, ];
let expected = ParameterNegotiationParams {
dlci: DLCI::try_from(0).unwrap(),
credit_based_flow_handshake: CreditBasedFlowHandshake::SupportedRequest,
priority: 8,
max_frame_size: 4,
initial_credits: 1,
};
assert_eq!(ParameterNegotiationParams::decode(&buf[..]).unwrap(), expected);
}
#[test]
fn test_parse_two_octet_frame_size_command() {
let buf = [
0b00000000, 0b11110000, 0b00001000, 0b00000000, 0b10000100, 0b00000001, 0b00011100, 0b00001100, ];
let expected = ParameterNegotiationParams {
dlci: DLCI::try_from(0).unwrap(),
credit_based_flow_handshake: CreditBasedFlowHandshake::SupportedRequest,
priority: 8,
max_frame_size: 388,
initial_credits: 4,
};
assert_eq!(ParameterNegotiationParams::decode(&buf[..]).unwrap(), expected);
}
#[test]
fn test_encode_invalid_buf_error() {
let mut small_buf = [];
let dlc_pn_command = ParameterNegotiationParams {
dlci: DLCI::try_from(0).unwrap(),
credit_based_flow_handshake: CreditBasedFlowHandshake::SupportedRequest,
priority: 8,
max_frame_size: 260,
initial_credits: 4,
};
assert_matches!(
dlc_pn_command.encode(&mut small_buf[..]),
Err(FrameParseError::BufferTooSmall)
);
}
#[test]
fn test_encode_command_success() {
let dlc_pn_command = ParameterNegotiationParams {
dlci: DLCI::try_from(5).unwrap(),
credit_based_flow_handshake: CreditBasedFlowHandshake::SupportedRequest,
priority: 8,
max_frame_size: 260,
initial_credits: 6,
};
let mut buf = vec![0; dlc_pn_command.encoded_len()];
assert!(dlc_pn_command.encode(&mut buf[..]).is_ok());
let expected_buf = [
0b00000101, 0b11110000, 0b00001000, 0b00000000, 0b00000100, 0b00000001, 0b00000000, 0b00000110, ];
assert_eq!(buf, expected_buf);
}
}