use packet_encoding::{decodable_enum, Decodable, Encodable};
mod command_response;
pub use command_response::CommandResponse;
mod error;
pub use error::FrameParseError;
mod fcs;
mod field;
pub mod mux_commands;
use self::fcs::{calculate_fcs, verify_fcs};
use self::field::*;
use self::mux_commands::MuxCommand;
use crate::{Role, DLCI};
decodable_enum! {
pub enum FrameTypeMarker<u8, FrameParseError, UnsupportedFrameType> {
SetAsynchronousBalancedMode = 0b00101111,
UnnumberedAcknowledgement = 0b01100011,
DisconnectedMode = 0b00001111,
Disconnect = 0b01000011,
UnnumberedInfoHeaderCheck = 0b11101111,
}
}
impl FrameTypeMarker {
fn is_mux_startup(&self, dlci: &DLCI) -> bool {
dlci.is_mux_control()
&& (*self == FrameTypeMarker::SetAsynchronousBalancedMode
|| *self == FrameTypeMarker::UnnumberedAcknowledgement
|| *self == FrameTypeMarker::DisconnectedMode)
}
fn fcs_octets(&self) -> usize {
if *self == FrameTypeMarker::UnnumberedInfoHeaderCheck {
2
} else {
3
}
}
fn has_credit_octet(&self, credit_based_flow: bool, poll_final: bool, dlci: DLCI) -> bool {
*self == FrameTypeMarker::UnnumberedInfoHeaderCheck
&& !dlci.is_mux_control()
&& credit_based_flow
&& poll_final
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct UserData {
pub information: Vec<u8>,
}
impl UserData {
pub fn is_empty(&self) -> bool {
self.information.is_empty()
}
}
impl Decodable for UserData {
type Error = FrameParseError;
fn decode(buf: &[u8]) -> Result<Self, FrameParseError> {
Ok(Self { information: buf.to_vec() })
}
}
impl Encodable for UserData {
type Error = FrameParseError;
fn encoded_len(&self) -> usize {
self.information.len()
}
fn encode(&self, buf: &mut [u8]) -> Result<(), FrameParseError> {
if buf.len() < self.encoded_len() {
return Err(FrameParseError::BufferTooSmall);
}
buf.copy_from_slice(&self.information);
Ok(())
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum UIHData {
User(UserData),
Mux(MuxCommand),
}
impl Encodable for UIHData {
type Error = FrameParseError;
fn encoded_len(&self) -> usize {
match self {
UIHData::User(data) => data.encoded_len(),
UIHData::Mux(command) => command.encoded_len(),
}
}
fn encode(&self, buf: &mut [u8]) -> Result<(), FrameParseError> {
if buf.len() < self.encoded_len() {
return Err(FrameParseError::BufferTooSmall);
}
match self {
UIHData::User(data) => data.encode(buf),
UIHData::Mux(command) => command.encode(buf),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum FrameData {
SetAsynchronousBalancedMode,
UnnumberedAcknowledgement,
DisconnectedMode,
Disconnect,
UnnumberedInfoHeaderCheck(UIHData),
}
impl FrameData {
pub fn marker(&self) -> FrameTypeMarker {
match self {
FrameData::SetAsynchronousBalancedMode => FrameTypeMarker::SetAsynchronousBalancedMode,
FrameData::UnnumberedAcknowledgement => FrameTypeMarker::UnnumberedAcknowledgement,
FrameData::DisconnectedMode => FrameTypeMarker::DisconnectedMode,
FrameData::Disconnect => FrameTypeMarker::Disconnect,
FrameData::UnnumberedInfoHeaderCheck(_) => FrameTypeMarker::UnnumberedInfoHeaderCheck,
}
}
fn decode(
frame_type: &FrameTypeMarker,
dlci: &DLCI,
buf: &[u8],
) -> Result<Self, FrameParseError> {
let data = match frame_type {
FrameTypeMarker::SetAsynchronousBalancedMode => FrameData::SetAsynchronousBalancedMode,
FrameTypeMarker::UnnumberedAcknowledgement => FrameData::UnnumberedAcknowledgement,
FrameTypeMarker::DisconnectedMode => FrameData::DisconnectedMode,
FrameTypeMarker::Disconnect => FrameData::Disconnect,
FrameTypeMarker::UnnumberedInfoHeaderCheck => {
let uih_data = if dlci.is_mux_control() {
UIHData::Mux(MuxCommand::decode(buf)?)
} else {
UIHData::User(UserData::decode(buf)?)
};
FrameData::UnnumberedInfoHeaderCheck(uih_data)
}
};
Ok(data)
}
}
impl Encodable for FrameData {
type Error = FrameParseError;
fn encoded_len(&self) -> usize {
match self {
FrameData::SetAsynchronousBalancedMode
| FrameData::UnnumberedAcknowledgement
| FrameData::DisconnectedMode
| FrameData::Disconnect => 0,
FrameData::UnnumberedInfoHeaderCheck(data) => data.encoded_len(),
}
}
fn encode(&self, buf: &mut [u8]) -> Result<(), FrameParseError> {
if buf.len() < self.encoded_len() {
return Err(FrameParseError::BufferTooSmall);
}
match self {
FrameData::SetAsynchronousBalancedMode
| FrameData::UnnumberedAcknowledgement
| FrameData::DisconnectedMode
| FrameData::Disconnect => Ok(()),
FrameData::UnnumberedInfoHeaderCheck(data) => data.encode(buf),
}
}
}
const MIN_FRAME_SIZE: usize = 4;
pub const MAX_RFCOMM_HEADER_SIZE: usize = 6;
const MAX_SINGLE_OCTET_LENGTH: usize = 127;
fn is_two_octet_length(length: usize) -> bool {
length > MAX_SINGLE_OCTET_LENGTH
}
fn cr_bit_for_non_uih_frame(role: Role, command_response: CommandResponse) -> bool {
match (role, command_response) {
(Role::Initiator, CommandResponse::Command)
| (Role::Responder, CommandResponse::Response) => true,
_ => false,
}
}
fn cr_bit_for_uih_frame(role: Role) -> bool {
match role {
Role::Initiator => true,
_ => false,
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Frame {
pub role: Role,
pub dlci: DLCI,
pub data: FrameData,
pub poll_final: bool,
pub command_response: CommandResponse,
pub credits: Option<u8>,
}
impl Frame {
pub fn parse(role: Role, credit_based_flow: bool, buf: &[u8]) -> Result<Self, FrameParseError> {
if buf.len() < MIN_FRAME_SIZE {
return Err(FrameParseError::BufferTooSmall);
}
let address_field = AddressField(buf[FRAME_ADDRESS_IDX]);
let dlci: DLCI = address_field.dlci()?;
let cr_bit: bool = address_field.cr_bit();
let control_field = ControlField(buf[FRAME_CONTROL_IDX]);
let frame_type: FrameTypeMarker = control_field.frame_type()?;
let poll_final = control_field.poll_final();
if !role.is_multiplexer_started() && !frame_type.is_mux_startup(&dlci) {
return Err(FrameParseError::InvalidFrame);
}
let command_response = CommandResponse::classify(role, frame_type, cr_bit)?;
let information_field = InformationField(buf[FRAME_INFORMATION_IDX]);
let is_two_octet_length = !information_field.ea_bit();
let mut length = information_field.length() as u16;
if is_two_octet_length {
length |= (buf[FRAME_INFORMATION_IDX + 1] as u16) << INFORMATION_SECOND_OCTET_SHIFT;
}
let mut header_size = 2 + if is_two_octet_length { 2 } else { 1 };
let mut credits = None;
if frame_type.has_credit_octet(credit_based_flow, poll_final, dlci) {
if buf.len() < header_size {
return Err(FrameParseError::BufferTooSmall);
}
credits = Some(buf[header_size]);
header_size += 1;
}
let fcs_index = header_size + usize::from(length);
if buf.len() <= fcs_index {
return Err(FrameParseError::BufferTooSmall);
}
let fcs = buf[fcs_index];
if !verify_fcs(fcs, &buf[..frame_type.fcs_octets()]) {
return Err(FrameParseError::FCSCheckFailed);
}
let data = &buf[header_size..fcs_index];
let data = FrameData::decode(&frame_type, &dlci, data)?;
Ok(Self { role, dlci, data, poll_final, command_response, credits })
}
pub fn make_sabm_command(role: Role, dlci: DLCI) -> Self {
Self {
role,
dlci,
data: FrameData::SetAsynchronousBalancedMode,
poll_final: true, command_response: CommandResponse::Command,
credits: None,
}
}
pub fn make_dm_response(role: Role, dlci: DLCI) -> Self {
Self {
role,
dlci,
data: FrameData::DisconnectedMode,
poll_final: true, command_response: CommandResponse::Response,
credits: None,
}
}
pub fn make_ua_response(role: Role, dlci: DLCI) -> Self {
Self {
role,
dlci,
data: FrameData::UnnumberedAcknowledgement,
poll_final: true, command_response: CommandResponse::Response,
credits: None,
}
}
pub fn make_mux_command(role: Role, data: MuxCommand) -> Self {
let command_response = data.command_response;
Self {
role,
dlci: DLCI::MUX_CONTROL_DLCI,
data: FrameData::UnnumberedInfoHeaderCheck(UIHData::Mux(data)),
poll_final: false, command_response,
credits: None,
}
}
pub fn make_user_data_frame(
role: Role,
dlci: DLCI,
user_data: UserData,
credits: Option<u8>,
) -> Self {
Self {
role,
dlci,
data: FrameData::UnnumberedInfoHeaderCheck(UIHData::User(user_data)),
poll_final: credits.is_some(),
command_response: CommandResponse::Command,
credits,
}
}
pub fn make_disc_command(role: Role, dlci: DLCI) -> Self {
Self {
role,
dlci,
data: FrameData::Disconnect,
poll_final: true, command_response: CommandResponse::Command,
credits: None,
}
}
}
impl Encodable for Frame {
type Error = FrameParseError;
fn encoded_len(&self) -> usize {
3 + self.credits.map_or(0, |_| 1)
+ if is_two_octet_length(self.data.encoded_len()) { 2 } else { 1 }
+ self.data.encoded_len()
}
fn encode(&self, buf: &mut [u8]) -> Result<(), FrameParseError> {
if buf.len() != self.encoded_len() {
return Err(FrameParseError::BufferTooSmall);
}
let assumed_role = if !self.role.is_multiplexer_started() {
if !self.data.marker().is_mux_startup(&self.dlci) {
return Err(FrameParseError::InvalidFrame);
}
if self.data.marker() == FrameTypeMarker::SetAsynchronousBalancedMode {
Role::Initiator
} else {
Role::Responder
}
} else {
self.role
};
let cr_bit = if self.data.marker() == FrameTypeMarker::UnnumberedInfoHeaderCheck {
cr_bit_for_uih_frame(assumed_role)
} else {
cr_bit_for_non_uih_frame(assumed_role, self.command_response)
};
let mut address_field = AddressField(0);
address_field.set_ea_bit(true);
address_field.set_cr_bit(cr_bit);
address_field.set_dlci(u8::from(self.dlci));
buf[FRAME_ADDRESS_IDX] = address_field.0;
let mut control_field = ControlField(0);
control_field.set_frame_type(u8::from(&self.data.marker()));
control_field.set_poll_final(self.poll_final);
buf[FRAME_CONTROL_IDX] = control_field.0;
let data_length = self.data.encoded_len();
let is_two_octet_length = is_two_octet_length(data_length);
let mut first_octet_length = InformationField(0);
first_octet_length.set_length(data_length as u8);
first_octet_length.set_ea_bit(!is_two_octet_length);
buf[FRAME_INFORMATION_IDX] = first_octet_length.0;
if is_two_octet_length {
let second_octet_length = (data_length >> INFORMATION_SECOND_OCTET_SHIFT) as u8;
buf[FRAME_INFORMATION_IDX + 1] = second_octet_length;
}
let mut header_size = 2 + if is_two_octet_length { 2 } else { 1 };
let credit_based_flow = self.credits.is_some();
if self.data.marker().has_credit_octet(credit_based_flow, self.poll_final, self.dlci) {
buf[header_size] = self.credits.unwrap();
header_size += 1;
}
let fcs_idx = header_size + data_length as usize;
self.data.encode(&mut buf[header_size..fcs_idx])?;
buf[fcs_idx] = calculate_fcs(&buf[..self.data.marker().fcs_octets()]);
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::frame::mux_commands::ModemStatusParams;
use super::*;
use assert_matches::assert_matches;
use mux_commands::{MuxCommandParams, RemotePortNegotiationParams};
#[test]
fn test_is_mux_startup_frame() {
let control_dlci = DLCI::try_from(0).unwrap();
let user_dlci = DLCI::try_from(5).unwrap();
let frame_type = FrameTypeMarker::SetAsynchronousBalancedMode;
assert!(frame_type.is_mux_startup(&control_dlci));
assert!(!frame_type.is_mux_startup(&user_dlci));
let frame_type = FrameTypeMarker::UnnumberedAcknowledgement;
assert!(frame_type.is_mux_startup(&control_dlci));
assert!(!frame_type.is_mux_startup(&user_dlci));
let frame_type = FrameTypeMarker::DisconnectedMode;
assert!(frame_type.is_mux_startup(&control_dlci));
assert!(!frame_type.is_mux_startup(&user_dlci));
let frame_type = FrameTypeMarker::Disconnect;
assert!(!frame_type.is_mux_startup(&control_dlci));
assert!(!frame_type.is_mux_startup(&user_dlci));
}
#[test]
fn test_has_credit_octet() {
let frame_type = FrameTypeMarker::UnnumberedInfoHeaderCheck;
let pf = true;
let credit_based_flow = true;
let dlci = DLCI::try_from(3).unwrap();
assert!(frame_type.has_credit_octet(credit_based_flow, pf, dlci));
let pf = false;
let credit_based_flow = true;
assert!(!frame_type.has_credit_octet(credit_based_flow, pf, dlci));
let pf = true;
let credit_based_flow = false;
assert!(!frame_type.has_credit_octet(credit_based_flow, pf, dlci));
let pf = true;
let credit_based_flow = true;
let dlci = DLCI::try_from(0).unwrap(); assert!(!frame_type.has_credit_octet(credit_based_flow, pf, dlci));
let pf = false;
let credit_based_flow = false;
assert!(!frame_type.has_credit_octet(credit_based_flow, pf, dlci));
let frame_type = FrameTypeMarker::SetAsynchronousBalancedMode;
let pf = true;
let credit_based_flow = true;
let dlci = DLCI::try_from(5).unwrap();
assert!(!frame_type.has_credit_octet(credit_based_flow, pf, dlci));
}
#[test]
fn test_parse_too_small_frame() {
let role = Role::Unassigned;
let buf: &[u8] = &[0x00];
assert_matches!(Frame::parse(role, false, buf), Err(FrameParseError::BufferTooSmall));
}
#[test]
fn test_parse_invalid_dlci() {
let role = Role::Unassigned;
let buf: &[u8] = &[
0b00000101, 0b00101111, 0b00000001, 0x00, ];
assert_matches!(Frame::parse(role, false, buf), Err(FrameParseError::InvalidDLCI(1)));
}
#[test]
fn test_parse_invalid_frame_type() {
let role = Role::Unassigned;
let buf: &[u8] = &[
0b00000001, 0b10101010, 0b00000001, 0x00, ];
assert_matches!(Frame::parse(role, false, buf), Err(FrameParseError::UnsupportedFrameType));
}
#[test]
fn test_parse_invalid_frame_type_sent_before_mux_startup() {
let role = Role::Unassigned;
let buf: &[u8] = &[
0b00000001, 0b11101111, 0b00000001, 0x00, ];
assert_matches!(Frame::parse(role, false, buf), Err(FrameParseError::InvalidFrame));
}
#[test]
fn test_parse_invalid_frame_missing_fcs() {
let role = Role::Unassigned;
let buf: &[u8] = &[
0b00000011, 0b00101111, 0b00000000, 0b00000001, ];
assert_matches!(Frame::parse(role, false, buf), Err(FrameParseError::BufferTooSmall));
}
#[test]
fn test_parse_valid_frame_over_mux_dlci() {
let role = Role::Unassigned;
let frame_type = FrameTypeMarker::SetAsynchronousBalancedMode;
let mut buf = vec![
0b00000011, 0b00101111, 0b00000001, ];
let fcs = calculate_fcs(&buf[..frame_type.fcs_octets()]);
buf.push(fcs);
let res = Frame::parse(role, false, &buf[..]).unwrap();
let expected_frame = Frame {
role,
dlci: DLCI::try_from(0).unwrap(),
data: FrameData::SetAsynchronousBalancedMode,
poll_final: false,
command_response: CommandResponse::Command,
credits: None,
};
assert_eq!(res, expected_frame);
}
#[test]
fn test_parse_valid_frame_over_user_dlci() {
let role = Role::Responder;
let frame_type = FrameTypeMarker::SetAsynchronousBalancedMode;
let mut buf = vec![
0b00001111, 0b00101111, 0b00000001, ];
let fcs = calculate_fcs(&buf[..frame_type.fcs_octets()]);
buf.push(fcs);
let res = Frame::parse(role, false, &buf[..]).unwrap();
let expected_frame = Frame {
role,
dlci: DLCI::try_from(3).unwrap(),
data: FrameData::SetAsynchronousBalancedMode,
poll_final: false,
command_response: CommandResponse::Response,
credits: None,
};
assert_eq!(res, expected_frame);
}
#[test]
fn test_parse_frame_with_information_length_invalid_buf_size() {
let role = Role::Responder;
let frame_type = FrameTypeMarker::UnnumberedInfoHeaderCheck;
let mut buf = vec![
0b00001111, 0b11101111, 0b00000111, 0b00000000, ];
let fcs = calculate_fcs(&buf[..frame_type.fcs_octets()]);
buf.push(fcs);
assert_matches!(Frame::parse(role, false, &buf[..]), Err(FrameParseError::BufferTooSmall));
}
#[test]
fn test_parse_valid_frame_with_information_length() {
let role = Role::Responder;
let frame_type = FrameTypeMarker::UnnumberedInfoHeaderCheck;
let mut buf = vec![
0b00001101, 0b11101111, 0b00000101, 0b00000000, 0b00000000, ];
let fcs = calculate_fcs(&buf[..frame_type.fcs_octets()]);
buf.push(fcs);
let res = Frame::parse(role, false, &buf[..]).unwrap();
let expected_frame = Frame {
role,
dlci: DLCI::try_from(3).unwrap(),
data: FrameData::UnnumberedInfoHeaderCheck(UIHData::User(UserData {
information: vec![
0b00000000, 0b00000000, ],
})),
poll_final: false,
command_response: CommandResponse::Response,
credits: None,
};
assert_eq!(res, expected_frame);
}
#[test]
fn test_parse_valid_frame_with_two_octet_information_length() {
let role = Role::Responder;
let frame_type = FrameTypeMarker::UnnumberedInfoHeaderCheck;
let length = 129;
let length_data = vec![0; length];
let buf = vec![
0b00001101, 0b11101111, 0b00000010, 0b00000001, ];
let fcs = calculate_fcs(&buf[..frame_type.fcs_octets()]);
let buf = [buf, length_data.clone(), vec![fcs]].concat();
let res = Frame::parse(role, false, &buf[..]).unwrap();
let expected_frame = Frame {
role,
dlci: DLCI::try_from(3).unwrap(),
data: FrameData::UnnumberedInfoHeaderCheck(UIHData::User(UserData {
information: length_data,
})),
poll_final: false,
command_response: CommandResponse::Response,
credits: None,
};
assert_eq!(res, expected_frame);
}
#[test]
fn test_parse_uih_frame_with_mux_command() {
let role = Role::Responder;
let frame_type = FrameTypeMarker::UnnumberedInfoHeaderCheck;
let mut buf = vec![
0b00000001, 0b11111111, 0b00000111, 0b10010001, 0b00000011, 0b00011111, ];
let fcs = calculate_fcs(&buf[..frame_type.fcs_octets()]);
buf.push(fcs);
let res = Frame::parse(role, false, &buf[..]).unwrap();
let expected_mux_command = MuxCommand {
params: MuxCommandParams::RemotePortNegotiation(RemotePortNegotiationParams {
dlci: DLCI::try_from(7).unwrap(),
port_values: None,
}),
command_response: CommandResponse::Response,
};
let expected_frame = Frame {
role,
dlci: DLCI::try_from(0).unwrap(),
data: FrameData::UnnumberedInfoHeaderCheck(UIHData::Mux(expected_mux_command)),
poll_final: true,
command_response: CommandResponse::Response,
credits: None,
};
assert_eq!(res, expected_frame);
}
#[test]
fn test_parse_uih_frame_with_credits() {
let role = Role::Initiator;
let frame_type = FrameTypeMarker::UnnumberedInfoHeaderCheck;
let credit_based_flow = true;
let mut buf = vec![
0b00011111, 0b11111111, 0b00000111, 0b00000101, 0b00000000, 0b00000001, 0b00000010, ];
let fcs = calculate_fcs(&buf[..frame_type.fcs_octets()]);
buf.push(fcs);
let res = Frame::parse(role, credit_based_flow, &buf[..]).unwrap();
let expected_user_data = UserData { information: vec![0x00, 0x01, 0x02] };
let expected_frame = Frame {
role,
dlci: DLCI::try_from(7).unwrap(),
data: FrameData::UnnumberedInfoHeaderCheck(UIHData::User(expected_user_data)),
poll_final: true,
command_response: CommandResponse::Command,
credits: Some(5),
};
assert_eq!(res, expected_frame);
}
#[test]
fn test_encode_frame_invalid_buf() {
let frame = Frame {
role: Role::Unassigned,
dlci: DLCI::try_from(0).unwrap(),
data: FrameData::SetAsynchronousBalancedMode,
poll_final: false,
command_response: CommandResponse::Command,
credits: None,
};
let mut buf = [];
assert_matches!(frame.encode(&mut buf[..]), Err(FrameParseError::BufferTooSmall));
}
#[test]
fn test_encode_mux_startup_frame_over_user_dlci_fails() {
let frame = Frame {
role: Role::Unassigned,
dlci: DLCI::try_from(3).unwrap(),
data: FrameData::SetAsynchronousBalancedMode,
poll_final: false,
command_response: CommandResponse::Command,
credits: None,
};
let mut buf = vec![0; frame.encoded_len()];
assert_matches!(frame.encode(&mut buf[..]), Err(FrameParseError::InvalidFrame));
}
#[test]
fn encode_mux_startup_command_succeeds() {
let frame = Frame {
role: Role::Unassigned,
dlci: DLCI::try_from(0).unwrap(),
data: FrameData::SetAsynchronousBalancedMode,
poll_final: true,
command_response: CommandResponse::Command,
credits: None,
};
let mut buf = vec![0; frame.encoded_len()];
assert!(frame.encode(&mut buf[..]).is_ok());
let expected = vec![
0b00000011, 0b00111111, 0b00000001, 0b00011100, ];
assert_eq!(buf, expected);
}
#[test]
fn encode_mux_startup_response_succeeds() {
let frame = Frame::make_ua_response(Role::Unassigned, DLCI::try_from(0).unwrap());
let mut buf = vec![0; frame.encoded_len()];
assert!(frame.encode(&mut buf[..]).is_ok());
let expected = vec![
0b00000011, 0b01110011, 0b00000001, 0b11010111, ];
assert_eq!(buf, expected);
}
#[test]
fn encode_user_data_as_initiator_succeeds() {
let frame = Frame::make_user_data_frame(
Role::Initiator,
DLCI::try_from(3).unwrap(),
UserData {
information: vec![
0b00000001, 0b00000010, ],
},
Some(8),
);
let mut buf = vec![0; frame.encoded_len()];
assert!(frame.encode(&mut buf[..]).is_ok());
let expected = vec![
0b00001111, 0b11111111, 0b00000101, 0b00001000, 0b00000001, 0b00000010, 0b11110011, ];
assert_eq!(buf, expected);
}
#[test]
fn test_encode_user_data_as_responder_succeeds() {
let frame = Frame::make_user_data_frame(
Role::Responder,
DLCI::try_from(9).unwrap(),
UserData {
information: vec![
0b00000001, ],
},
Some(10),
);
let mut buf = vec![0; frame.encoded_len()];
assert!(frame.encode(&mut buf[..]).is_ok());
let expected = vec![
0b00100101, 0b11111111, 0b00000011, 0b00001010, 0b00000001, 0b11101001, ];
assert_eq!(buf, expected);
}
#[test]
fn encode_mux_command_as_initiator() {
let mux_command = MuxCommand {
params: MuxCommandParams::ModemStatus(ModemStatusParams::default(
DLCI::try_from(5).unwrap(),
)),
command_response: CommandResponse::Command,
};
let frame = Frame::make_mux_command(Role::Initiator, mux_command);
let mut buf = vec![0; frame.encoded_len()];
assert!(frame.encode(&mut buf[..]).is_ok());
let expected = vec![
0b00000011, 0b11101111, 0b00001001, 0b11100011, 0b00000101, 0b00010111, 0b10001101, 0b01110000, ];
assert_eq!(buf, expected);
}
#[test]
fn encode_mux_command_as_responder() {
let mux_command = MuxCommand {
params: MuxCommandParams::RemotePortNegotiation(RemotePortNegotiationParams {
dlci: DLCI::try_from(7).unwrap(),
port_values: None,
}),
command_response: CommandResponse::Command,
};
let frame = Frame::make_mux_command(Role::Responder, mux_command);
let mut buf = vec![0; frame.encoded_len()];
assert!(frame.encode(&mut buf[..]).is_ok());
let expected = vec![
0b00000001, 0b11101111, 0b00000111, 0b10010011, 0b00000011, 0b00011111, 0b10101010, ];
assert_eq!(buf, expected);
}
#[test]
fn encode_mux_response_as_initiator() {
let mux_command = MuxCommand {
params: MuxCommandParams::RemotePortNegotiation(RemotePortNegotiationParams {
dlci: DLCI::try_from(13).unwrap(),
port_values: None,
}),
command_response: CommandResponse::Response,
};
let frame = Frame::make_mux_command(Role::Initiator, mux_command);
let mut buf = vec![0; frame.encoded_len()];
assert!(frame.encode(&mut buf[..]).is_ok());
let expected = vec![
0b00000011, 0b11101111, 0b00000111, 0b10010001, 0b00000011, 0b00110111, 0b01110000, ];
assert_eq!(buf, expected);
}
#[test]
fn encode_mux_response_as_responder() {
let mux_command = MuxCommand {
params: MuxCommandParams::ModemStatus(ModemStatusParams::default(
DLCI::try_from(11).unwrap(),
)),
command_response: CommandResponse::Response,
};
let frame = Frame::make_mux_command(Role::Responder, mux_command);
let mut buf = vec![0; frame.encoded_len()];
assert!(frame.encode(&mut buf[..]).is_ok());
let expected = vec![
0b00000001, 0b11101111, 0b00001001, 0b11100001, 0b00000101, 0b00101111, 0b10001101, 0b10101010, ];
assert_eq!(buf, expected);
}
#[test]
fn test_encode_user_data_with_two_octet_length_succeeds() {
let length = 130;
let mut information = vec![0; length];
let frame = Frame {
role: Role::Initiator,
dlci: DLCI::try_from(5).unwrap(),
data: FrameData::UnnumberedInfoHeaderCheck(UIHData::User(UserData {
information: information.clone(),
})),
poll_final: true,
command_response: CommandResponse::Command,
credits: Some(8),
};
let mut buf = vec![0; frame.encoded_len()];
assert!(frame.encode(&mut buf[..]).is_ok());
let mut expected = vec![
0b00010111, 0b11111111, 0b00000100, 0b00000001, 0b00001000, ];
expected.append(&mut information);
expected.push(0b0000_1100);
assert_eq!(buf, expected);
}
}