Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .fusa-reqs.json
Original file line number Diff line number Diff line change
Expand Up @@ -5427,6 +5427,15 @@
"level": "HLR",
"asil": "ASIL-B",
"verificationMethod": "test"
},
{
"id": "REQ-RESP-004",
"title": "ByteMessageInfo::response_kind classifies per TC18 Table 15",
"text": "ByteMessageInfo::response_kind() shall return ResponseKind::Acknowledge whenever evt.raw_nibble() equals 0xF, regardless of err or op, per TC18 Table 15/\u00a711.3.1 -- a rejected Acknowledge (err=true) is still an Acknowledge, not a ResponseKind::Error. Otherwise it shall return ResponseKind::Error whenever err is true, and otherwise ResponseKind::Write when op is true or ResponseKind::Read.",
"standard": "iso26262",
"level": "HLR",
"asil": "ASIL-B",
"verificationMethod": "test"
}
]
}
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rcp"
version = "5.0.1"
version = "5.1.0"
edition = "2021"
rust-version = "1.75"
license = "MPL-2.0"
Expand Down
25 changes: 25 additions & 0 deletions docs/PUBLIC_API.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
pub mod rcp
pub mod rcp::acf
pub enum rcp::acf::ResponseKind
pub rcp::acf::ResponseKind::Acknowledge
pub rcp::acf::ResponseKind::Error
pub rcp::acf::ResponseKind::Read
pub rcp::acf::ResponseKind::Write
impl core::clone::Clone for rcp::acf::ResponseKind
pub fn rcp::acf::ResponseKind::clone(&self) -> rcp::acf::ResponseKind
impl core::cmp::Eq for rcp::acf::ResponseKind
impl core::cmp::PartialEq for rcp::acf::ResponseKind
pub fn rcp::acf::ResponseKind::eq(&self, &rcp::acf::ResponseKind) -> bool
impl core::fmt::Debug for rcp::acf::ResponseKind
pub fn rcp::acf::ResponseKind::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl core::marker::Copy for rcp::acf::ResponseKind
impl core::marker::StructuralPartialEq for rcp::acf::ResponseKind
impl core::marker::Freeze for rcp::acf::ResponseKind
impl core::marker::Send for rcp::acf::ResponseKind
impl core::marker::Sync for rcp::acf::ResponseKind
impl core::marker::Unpin for rcp::acf::ResponseKind
impl core::marker::UnsafeUnpin for rcp::acf::ResponseKind
impl core::panic::unwind_safe::RefUnwindSafe for rcp::acf::ResponseKind
impl core::panic::unwind_safe::UnwindSafe for rcp::acf::ResponseKind
pub struct rcp::acf::AcfAbbMessage
pub rcp::acf::AcfAbbMessage::info: rcp::acf::ByteMessageInfo
pub rcp::acf::AcfAbbMessage::payload: alloc::vec::Vec<u8>
Expand Down Expand Up @@ -58,6 +79,7 @@ pub rcp::acf::ByteMessageInfo::rsp: bool
pub rcp::acf::ByteMessageInfo::transaction_num: u8
impl rcp::acf::ByteMessageInfo
pub fn rcp::acf::ByteMessageInfo::read_size(&self) -> core::option::Option<u16>
pub fn rcp::acf::ByteMessageInfo::response_kind(&self) -> rcp::acf::ResponseKind
pub fn rcp::acf::ByteMessageInfo::segment_num(&self) -> core::option::Option<u16>
impl core::clone::Clone for rcp::acf::ByteMessageInfo
pub fn rcp::acf::ByteMessageInfo::clone(&self) -> rcp::acf::ByteMessageInfo
Expand All @@ -84,6 +106,8 @@ impl core::panic::unwind_safe::UnwindSafe for rcp::acf::ByteMessageInfo
pub struct rcp::acf::Evt
pub rcp::acf::Evt::ack: bool
pub rcp::acf::Evt::sub_opcode: u8
impl rcp::acf::Evt
pub fn rcp::acf::Evt::raw_nibble(&self) -> u8
impl core::clone::Clone for rcp::acf::Evt
pub fn rcp::acf::Evt::clone(&self) -> rcp::acf::Evt
impl core::cmp::Eq for rcp::acf::Evt
Expand Down Expand Up @@ -132,6 +156,7 @@ pub const rcp::acf::ACF_MSG_LENGTH_9BIT_MAX: u16
pub const rcp::acf::ACF_MSG_TYPE_7BIT_MAX: u8
pub const rcp::acf::BYTE_MESSAGE_INFO_11BIT_MAX: u16
pub const rcp::acf::BYTE_MESSAGE_INFO_LEN: usize
pub const rcp::acf::EVT_RESPONSE_ACKNOWLEDGE: u8
pub const rcp::acf::EVT_SUB_OPCODE_MAX: u8
pub const rcp::acf::PAD_2BIT_MAX: u8
pub const rcp::acf::QUADLET_LEN: usize
Expand Down
181 changes: 179 additions & 2 deletions src/acf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,19 @@ pub struct Evt {
pub sub_opcode: u8,
}

impl Evt {
/// The raw 4-bit `evt` nibble this value packs to on the wire
/// (`ack << 3 | sub_opcode`), the same arithmetic
/// [`encode_byte_message_info`] uses. Needed by
/// [`ByteMessageInfo::response_kind`], which reads `evt` under TC18
/// §11.3 Table 15's whole-nibble response encoding rather than this
/// type's own request-side ack+sub_opcode split — see this struct's
/// doc comment.
pub fn raw_nibble(&self) -> u8 {
((self.ack as u8) << 3) | (self.sub_opcode & 0x7)
}
}

/// The dual-purpose 12-bit field TC18 Table 4 calls
/// `read_size_or_segment_num` (row 2, octet 6 bits 3:0 + octet 7) — a
/// requested read byte count when the enclosing [`ByteMessageInfo::op`]
Expand Down Expand Up @@ -361,6 +374,57 @@ impl ByteMessageInfo {
None
}
}

/// Classify this header under TC18 §11.3 Table 15/§11.3.1-§11.3.4's
/// response-direction reading of `evt` — the whole-nibble split
/// [`Evt`]'s own doc comment says its `ack`+`sub_opcode` fields do
/// *not* model. Meaningless for a request header, whose `evt` carries
/// §13.5's entirely different per-endpoint meaning instead (see
/// [`Evt`]).
///
/// `evt[3:0] == 0xF` (§11.3.1) identifies an Acknowledge
/// unconditionally, checked before `err`/`op`: a rejected Acknowledge
/// (`err == true`) is still an Acknowledge, not a
/// [`ResponseKind::Error`], which is a distinct `evt[3:0] < 0x9`
/// case (§11.3.4).
//fusa:req REQ-RESP-004
pub fn response_kind(&self) -> ResponseKind {
if self.evt.raw_nibble() == EVT_RESPONSE_ACKNOWLEDGE {
ResponseKind::Acknowledge
} else if self.err {
ResponseKind::Error
} else if self.op {
ResponseKind::Write
} else {
ResponseKind::Read
}
}
}

/// TC18 §11.3 Table 15's `evt[3:0] == 0xF` response-kind marker
/// (TC18.txt line 1876: "evt[3:0] = 0xF - acknowledge").
pub const EVT_RESPONSE_ACKNOWLEDGE: u8 = 0x0F;

/// The four response semantics TC18 §11.3 Table 15/§11.3.1-§11.3.4 define
/// for a decoded [`ByteMessageInfo`] whose `rsp` bit is set. See
/// [`ByteMessageInfo::response_kind`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResponseKind {
/// `evt[3:0] == 0xF` (§11.3.1): `err == false` confirms the request
/// was filed into the endpoint's request storage with no
/// `byte_msg_payload`; `err == true` indicates the request was
/// rejected and `byte_msg_payload` carries an error code. A rejected
/// Acknowledge stays an Acknowledge — it is not [`ResponseKind::Error`].
Acknowledge,
/// `evt[3:0] < 0x9` and `op == true` (§11.3.2): confirms successful
/// execution of a write request, no `byte_msg_payload`.
Write,
/// `evt[3:0] < 0x9` and `op == false` (§11.3.3): confirms successful
/// execution of a read request, `byte_msg_payload` present.
Read,
/// `evt[3:0] < 0x9` and `err == true` (§11.3.4): execution of the
/// request failed, `byte_msg_payload` carries an error code.
Error,
}

/// Encode a [`ByteMessageInfo`] to its 8-byte wire representation, per this
Expand Down Expand Up @@ -407,8 +471,7 @@ pub fn encode_byte_message_info(

// octet 4: evt (ack:1 + sub_opcode:3) in bits 7:4, rsv(2 bits, zero) in
// bits 3:2, hs in bit 1, cs in bit 0.
let evt_bits = ((info.evt.ack as u8) << 3) | (info.evt.sub_opcode & 0x7);
buf[4] = (evt_bits << 4) | ((info.hs as u8) << 1) | (info.cs as u8);
buf[4] = (info.evt.raw_nibble() << 4) | ((info.hs as u8) << 1) | (info.cs as u8);

// octet 5: transaction_num, full byte — comes before the op/rsp/err/ms
// group, per Table 4's row-2 ordering.
Expand Down Expand Up @@ -1167,6 +1230,120 @@ mod tests {
}
}

// ── response_kind (TC18 §11.3 Table 15) ─────────────────────────────────

#[test]
//fusa:test REQ-RESP-004
fn response_kind_acknowledge() {
let info = ByteMessageInfo {
evt: Evt {
ack: true,
sub_opcode: 0x7,
},
op: true,
err: false,
..Default::default()
};
assert_eq!(info.response_kind(), ResponseKind::Acknowledge);
}

#[test]
//fusa:test REQ-RESP-004
fn response_kind_acknowledge_from_read_op() {
let info = ByteMessageInfo {
evt: Evt {
ack: true,
sub_opcode: 0x7,
},
op: false,
err: false,
..Default::default()
};
assert_eq!(info.response_kind(), ResponseKind::Acknowledge);
}

// A rejected Acknowledge (evt[3:0] = 0xF, err = true) is still an
// Acknowledge per TC18 §11.3.1, not a ResponseKind::Error -- that's a
// distinct evt[3:0] < 0x9 case (§11.3.4). err must not take priority
// over evt here.
#[test]
//fusa:test REQ-RESP-004
fn response_kind_acknowledge_rejected_is_not_error() {
let info = ByteMessageInfo {
evt: Evt {
ack: true,
sub_opcode: 0x7,
},
op: true,
err: true,
..Default::default()
};
assert_eq!(info.response_kind(), ResponseKind::Acknowledge);
}

#[test]
//fusa:test REQ-RESP-004
fn response_kind_write() {
let info = ByteMessageInfo {
evt: Evt {
ack: false,
sub_opcode: 0,
},
op: true,
err: false,
..Default::default()
};
assert_eq!(info.response_kind(), ResponseKind::Write);
}

#[test]
//fusa:test REQ-RESP-004
fn response_kind_read() {
let info = ByteMessageInfo {
evt: Evt {
ack: false,
sub_opcode: 0,
},
op: false,
err: false,
..Default::default()
};
assert_eq!(info.response_kind(), ResponseKind::Read);
}

#[test]
//fusa:test REQ-RESP-004
fn response_kind_error() {
let info = ByteMessageInfo {
evt: Evt {
ack: false,
sub_opcode: 0,
},
op: false,
err: true,
..Default::default()
};
assert_eq!(info.response_kind(), ResponseKind::Error);
}

// evt[3:0] = 0x1..0x8 is the multi-response counter range (§11.3, not
// yet modeled as its own concept), which must still fall through to
// the ordinary err/op classification, not be misread as Acknowledge.
#[test]
//fusa:test REQ-RESP-004
fn response_kind_counter_range_falls_through_to_write() {
let info = ByteMessageInfo {
evt: Evt {
ack: false,
sub_opcode: 0x3,
},
op: true,
err: false,
..Default::default()
};
assert_eq!(info.response_kind(), ResponseKind::Write);
}

// ── Canonical layout: bit-position pins ─────────────────────────────────
//
// Directly pins the octet-by-octet packing this module's "Canonical
Expand Down
Loading