use bitfield::bitfield;
use packet_encoding::{decodable_enum, Decodable, Encodable};
mod dlc_parameter_negotiation;
pub use dlc_parameter_negotiation::{
CreditBasedFlowHandshake, ParameterNegotiationParams, DEFAULT_INITIAL_CREDITS,
};
mod flow_control;
pub use flow_control::FlowControlParams;
mod modem_status;
pub use modem_status::ModemStatusParams;
mod non_supported;
pub use non_supported::NonSupportedCommandParams;
mod remote_line_status;
pub use remote_line_status::{RemoteLineStatusParams, RlsError};
mod remote_port_negotiation;
pub use remote_port_negotiation::RemotePortNegotiationParams;
mod test_command;
pub use test_command::TestCommandParams;
use crate::{
frame::{CommandResponse, FrameParseError},
DLCI,
};
decodable_enum! {
pub enum MuxCommandMarker<u8, FrameParseError, OutOfRange> {
ParameterNegotiation = 0b100000,
Test = 0b001000,
FlowControlOn = 0b101000,
FlowControlOff = 0b011000,
ModemStatus = 0b111000,
NonSupportedCommand = 0b000100,
RemotePortNegotiation = 0b100100,
RemoteLineStatus = 0b010100,
}
}
const MIN_MUX_COMMAND_SIZE: usize = 2;
const MUX_COMMAND_TYPE_IDX: usize = 0;
bitfield! {
struct TypeField(u8);
impl Debug;
pub bool, ea_bit, set_ea_bit: 0;
pub bool, cr_bit, set_cr_bit: 1;
pub u8, command_type_raw, set_command_type: 7, 2;
}
impl TypeField {
fn command_type(&self) -> Result<MuxCommandMarker, FrameParseError> {
MuxCommandMarker::try_from(self.command_type_raw())
.or(Err(FrameParseError::UnsupportedMuxCommandType(self.command_type_raw())))
}
fn command_response(&self) -> CommandResponse {
if self.cr_bit() {
CommandResponse::Command
} else {
CommandResponse::Response
}
}
}
const MUX_COMMAND_LENGTH_IDX: usize = 1;
const MUX_COMMAND_LENGTH_SHIFT: usize = 7;
bitfield! {
pub struct LengthField(u8);
impl Debug;
pub bool, ea_bit, set_ea_bit: 0;
pub u8, length, set_length: 7, 1;
}
#[derive(Clone, Debug, PartialEq)]
pub enum MuxCommandParams {
ParameterNegotiation(ParameterNegotiationParams),
FlowControlOn(FlowControlParams),
FlowControlOff(FlowControlParams),
ModemStatus(ModemStatusParams),
NonSupported(NonSupportedCommandParams),
RemoteLineStatus(RemoteLineStatusParams),
RemotePortNegotiation(RemotePortNegotiationParams),
Test(TestCommandParams),
}
impl MuxCommandParams {
pub fn marker(&self) -> MuxCommandMarker {
match self {
Self::ParameterNegotiation(_) => MuxCommandMarker::ParameterNegotiation,
Self::FlowControlOn(_) => MuxCommandMarker::FlowControlOn,
Self::FlowControlOff(_) => MuxCommandMarker::FlowControlOff,
Self::ModemStatus(_) => MuxCommandMarker::ModemStatus,
Self::NonSupported(_) => MuxCommandMarker::NonSupportedCommand,
Self::RemoteLineStatus(_) => MuxCommandMarker::RemoteLineStatus,
Self::RemotePortNegotiation(_) => MuxCommandMarker::RemotePortNegotiation,
Self::Test(_) => MuxCommandMarker::Test,
}
}
fn decode(command_type: &MuxCommandMarker, buf: &[u8]) -> Result<Self, FrameParseError> {
let params = match command_type {
MuxCommandMarker::ParameterNegotiation => {
Self::ParameterNegotiation(ParameterNegotiationParams::decode(buf)?)
}
MuxCommandMarker::FlowControlOn => Self::FlowControlOn(FlowControlParams::decode(buf)?),
MuxCommandMarker::FlowControlOff => {
Self::FlowControlOff(FlowControlParams::decode(buf)?)
}
MuxCommandMarker::ModemStatus => Self::ModemStatus(ModemStatusParams::decode(buf)?),
MuxCommandMarker::NonSupportedCommand => {
Self::NonSupported(NonSupportedCommandParams::decode(buf)?)
}
MuxCommandMarker::RemoteLineStatus => {
Self::RemoteLineStatus(RemoteLineStatusParams::decode(buf)?)
}
MuxCommandMarker::RemotePortNegotiation => {
Self::RemotePortNegotiation(RemotePortNegotiationParams::decode(buf)?)
}
MuxCommandMarker::Test => Self::Test(TestCommandParams::decode(buf)?),
};
Ok(params)
}
}
impl Encodable for MuxCommandParams {
type Error = FrameParseError;
fn encoded_len(&self) -> usize {
match self {
Self::ParameterNegotiation(cmd) => cmd.encoded_len(),
Self::FlowControlOn(cmd) => cmd.encoded_len(),
Self::FlowControlOff(cmd) => cmd.encoded_len(),
Self::ModemStatus(cmd) => cmd.encoded_len(),
Self::NonSupported(cmd) => cmd.encoded_len(),
Self::RemoteLineStatus(cmd) => cmd.encoded_len(),
Self::RemotePortNegotiation(cmd) => cmd.encoded_len(),
Self::Test(cmd) => cmd.encoded_len(),
}
}
fn encode(&self, buf: &mut [u8]) -> Result<(), FrameParseError> {
match self {
Self::ParameterNegotiation(cmd) => cmd.encode(buf),
Self::FlowControlOn(cmd) => cmd.encode(buf),
Self::FlowControlOff(cmd) => cmd.encode(buf),
Self::ModemStatus(cmd) => cmd.encode(buf),
Self::NonSupported(cmd) => cmd.encode(buf),
Self::RemoteLineStatus(cmd) => cmd.encode(buf),
Self::RemotePortNegotiation(cmd) => cmd.encode(buf),
Self::Test(cmd) => cmd.encode(buf),
}
}
}
fn length_to_ea_format(mut length: usize) -> Vec<u8> {
if length == 0 {
let mut length_field = LengthField(0);
length_field.set_ea_bit(true);
return vec![length_field.0];
}
let mut octets = vec![];
let mask = 0b01111111;
while length != 0 {
let mut length_field = LengthField(0);
let length_octet = (length & mask) as u8;
length_field.set_length(length_octet);
length_field.set_ea_bit(false);
octets.push(length_field);
length >>= MUX_COMMAND_LENGTH_SHIFT;
}
let last_idx = octets.len() - 1;
octets[last_idx].set_ea_bit(true);
octets.iter().map(|l| l.0).collect()
}
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct MuxCommandIdentifier(pub Option<DLCI>, pub MuxCommandMarker);
#[derive(Clone, Debug, PartialEq)]
pub struct MuxCommand {
pub params: MuxCommandParams,
pub command_response: CommandResponse,
}
impl MuxCommand {
pub fn dlci(&self) -> Option<DLCI> {
match &self.params {
MuxCommandParams::ParameterNegotiation(pn) => Some(pn.dlci),
MuxCommandParams::ModemStatus(status) => Some(status.dlci),
MuxCommandParams::RemoteLineStatus(rls) => Some(rls.dlci),
MuxCommandParams::RemotePortNegotiation(rpn) => Some(rpn.dlci),
_ => None,
}
}
pub fn identifier(&self) -> MuxCommandIdentifier {
MuxCommandIdentifier(self.dlci(), self.params.marker())
}
}
impl Decodable for MuxCommand {
type Error = FrameParseError;
fn decode(buf: &[u8]) -> Result<Self, FrameParseError> {
if buf.len() < MIN_MUX_COMMAND_SIZE {
return Err(FrameParseError::BufferTooSmall);
}
let type_field = TypeField(buf[MUX_COMMAND_TYPE_IDX]);
let command_response = type_field.command_response();
let command_type = type_field.command_type()?;
const MAX_LENGTH_OCTETS: usize = 8;
let mut length: u64 = 0;
let mut num_length_octets: usize = 0;
for i in MUX_COMMAND_LENGTH_IDX..buf.len() {
let length_field = LengthField(buf[i]);
let length_octet: u64 = length_field.length().into();
length |= length_octet << (MUX_COMMAND_LENGTH_SHIFT * num_length_octets);
num_length_octets += 1;
if length_field.ea_bit() {
break;
}
if num_length_octets > MAX_LENGTH_OCTETS {
return Err(FrameParseError::InvalidFrame);
}
}
let header_len = 1 + num_length_octets;
let calculated_buf_size: u64 = header_len as u64 + length;
if (buf.len() as u64) < calculated_buf_size {
return Err(FrameParseError::BufferTooSmall);
}
let params_payload = &buf[header_len..calculated_buf_size as usize];
let params = MuxCommandParams::decode(&command_type, params_payload)?;
Ok(Self { params, command_response })
}
}
impl Encodable for MuxCommand {
type Error = FrameParseError;
fn encoded_len(&self) -> usize {
let length = self.params.encoded_len();
1 + length_to_ea_format(length).len() + length
}
fn encode(&self, buf: &mut [u8]) -> Result<(), FrameParseError> {
if buf.len() < self.encoded_len() {
return Err(FrameParseError::BufferTooSmall);
}
let cr_bit = if self.command_response == CommandResponse::Command { true } else { false };
let mut type_field = TypeField(0);
type_field.set_ea_bit(true);
type_field.set_cr_bit(cr_bit);
type_field.set_command_type(u8::from(&self.params.marker()));
buf[0] = type_field.0;
let length = self.params.encoded_len();
let length_octets = length_to_ea_format(length);
let length_end_idx = MUX_COMMAND_LENGTH_IDX + length_octets.len();
buf[MUX_COMMAND_LENGTH_IDX..length_end_idx].copy_from_slice(&length_octets);
self.params.encode(&mut buf[length_end_idx..])?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
use modem_status::ModemStatusSignals;
#[test]
fn test_decode_mux_command_empty_buf() {
let empty_buf = [];
assert_matches!(MuxCommand::decode(&empty_buf[..]), Err(FrameParseError::BufferTooSmall));
}
#[test]
fn test_decode_mux_command_invalid_command_type() {
let buf = [
0b11111101, 0b00000001, ];
assert_matches!(
MuxCommand::decode(&buf[..]),
Err(FrameParseError::UnsupportedMuxCommandType(0b111111))
);
}
#[test]
fn test_decode_mux_command_too_large_length() {
let buf = [
0b10000001, 0b00000010, 0b00000010, 0b00000010, 0b00000010, 0b00000010, 0b00000010, 0b00000010, 0b00000010, 0b00000010, 0b00000000, ];
assert_matches!(MuxCommand::decode(&buf[..]), Err(FrameParseError::InvalidFrame));
}
#[test]
fn test_decode_mux_command_missing_length_octets() {
let buf = [
0b10000001, 0b00000101, 0b00000010, ];
assert_matches!(MuxCommand::decode(&buf[..]), Err(FrameParseError::BufferTooSmall));
}
#[test]
fn test_decode_dlc_parameter_negotiation_command() {
let buf = [
0b10000011, 0b00010001, 0b00000000, 0b11110000, 0b00001100, 0b00000000, 0b00010100, 0b00000000, 0b00000000, 0b00000001, ];
let expected = MuxCommand {
params: MuxCommandParams::ParameterNegotiation(ParameterNegotiationParams {
dlci: DLCI::try_from(0).unwrap(),
credit_based_flow_handshake: CreditBasedFlowHandshake::SupportedRequest,
priority: 12,
max_frame_size: 20,
initial_credits: 1,
}),
command_response: CommandResponse::Command,
};
assert_eq!(MuxCommand::decode(&buf[..]).unwrap(), expected);
}
#[test]
fn test_decode_test_command() {
let buf = [
0b00100011, 0b00001001, 0b00000000, 0b00000000, 0b00000000, 0b00000000, ];
let expected = MuxCommand {
params: MuxCommandParams::Test(TestCommandParams {
test_pattern: vec![0x00, 0x00, 0x00, 0x00],
}),
command_response: CommandResponse::Command,
};
assert_eq!(MuxCommand::decode(&buf[..]).unwrap(), expected);
}
#[test]
fn test_decode_flow_control_on_command() {
let buf = [
0b10100001, 0b00000001, ];
let expected = MuxCommand {
params: MuxCommandParams::FlowControlOn(FlowControlParams {}),
command_response: CommandResponse::Response,
};
assert_eq!(MuxCommand::decode(&buf[..]).unwrap(), expected);
}
#[test]
fn test_decode_flow_control_off_command() {
let buf = [
0b01100011, 0b00000001, ];
let expected = MuxCommand {
params: MuxCommandParams::FlowControlOff(FlowControlParams {}),
command_response: CommandResponse::Command,
};
assert_eq!(MuxCommand::decode(&buf[..]).unwrap(), expected);
}
#[test]
fn test_decode_modem_status_command() {
let buf = [
0b11100011, 0b00000111, 0b00001111, 0b00000000, 0b00000001, ];
let expected = MuxCommand {
params: MuxCommandParams::ModemStatus(ModemStatusParams {
dlci: DLCI::try_from(3).unwrap(),
signals: ModemStatusSignals(0),
break_value: None,
}),
command_response: CommandResponse::Command,
};
assert_eq!(MuxCommand::decode(&buf[..]).unwrap(), expected);
}
#[test]
fn test_decode_not_supported_command_response() {
let buf = [
0b00010001, 0b00000011, 0b00000001, ];
let expected = MuxCommand {
params: MuxCommandParams::NonSupported(NonSupportedCommandParams {
cr_bit: false,
non_supported_command: 0,
}),
command_response: CommandResponse::Response,
};
assert_eq!(MuxCommand::decode(&buf[..]).unwrap(), expected);
}
#[test]
fn test_decode_rpn_command() {
let buf = [
0b10010011, 0b00000011, 0b00011111, ];
let expected = MuxCommand {
params: MuxCommandParams::RemotePortNegotiation(RemotePortNegotiationParams {
dlci: DLCI::try_from(7).unwrap(),
port_values: None,
}),
command_response: CommandResponse::Command,
};
assert_eq!(MuxCommand::decode(&buf[..]).unwrap(), expected);
}
#[test]
fn test_decode_rls_command() {
let buf = [
0b01010011, 0b00000101, 0b00011111, 0b00000000, ];
let expected = MuxCommand {
params: MuxCommandParams::RemoteLineStatus(RemoteLineStatusParams::new(
DLCI::try_from(7).unwrap(),
None,
)),
command_response: CommandResponse::Command,
};
assert_eq!(MuxCommand::decode(&buf[..]).unwrap(), expected);
}
#[test]
fn test_encode_invalid_buffer() {
let command = MuxCommand {
params: MuxCommandParams::RemotePortNegotiation(RemotePortNegotiationParams {
dlci: DLCI::try_from(5).unwrap(),
port_values: None,
}),
command_response: CommandResponse::Command,
};
let mut buf = vec![];
assert_matches!(command.encode(&mut buf[..]), Err(FrameParseError::BufferTooSmall));
}
#[test]
fn test_encode_rpn_response() {
let command = MuxCommand {
params: MuxCommandParams::RemotePortNegotiation(RemotePortNegotiationParams {
dlci: DLCI::try_from(5).unwrap(),
port_values: None,
}),
command_response: CommandResponse::Response,
};
let mut buf = vec![0; command.encoded_len()];
assert!(command.encode(&mut buf[..]).is_ok());
let expected = vec![
0b10010001, 0b00000011, 0b00010111, ];
assert_eq!(buf, expected);
}
#[test]
fn test_encode_long_test_command() {
let test_pattern = vec![
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01,
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f,
];
let command = MuxCommand {
params: MuxCommandParams::Test(TestCommandParams {
test_pattern: test_pattern.clone(),
}),
command_response: CommandResponse::Command,
};
let mut buf = vec![0; command.encoded_len()];
assert!(command.encode(&mut buf[..]).is_ok());
let expected = vec![
0b00100011, 0b00000000, 0b00000011, ];
let expected = vec![expected, test_pattern].concat();
assert_eq!(buf, expected);
}
#[test]
fn test_zero_length_to_ea_format() {
let length = 0;
let expected = [0b00000001];
assert_eq!(length_to_ea_format(length), expected);
}
#[test]
fn test_small_length_to_ea_format() {
let small_length = 8;
let expected = [0b00010001];
assert_eq!(length_to_ea_format(small_length), expected);
}
#[test]
fn test_two_octet_length_to_ea_format() {
let two_octet_length = 245;
let expected = [0b11101010, 0b00000011];
assert_eq!(length_to_ea_format(two_octet_length), expected);
}
#[test]
fn test_multi_octet_length_to_ea_format() {
let multi_octet_length = 0b100000001100001001000;
let expected = [0b10010000, 0b01100000, 0b10000001];
assert_eq!(length_to_ea_format(multi_octet_length), expected);
}
}