diff --git a/.fusa-reqs.json b/.fusa-reqs.json index 7cc0c7e..b40b7fb 100644 --- a/.fusa-reqs.json +++ b/.fusa-reqs.json @@ -3325,11 +3325,12 @@ { "id": "REQ-I2C-012", "title": "rcp_ep_i2c_decode_transfer_request() rejects a malformed or misaddressed frame and accepts both directions", - "text": "rcp_ep_i2c_decode_transfer_request() shall return RCP_EP_I2C_ERR_SHORT_FRAME for a frame too short for the ACF_ABB fixed header or its declared payload length, RCP_EP_I2C_ERR_BAD_MSG_TYPE for a non-ACF_ABB frame, and RCP_EP_I2C_ERR_WRONG_BUS when byte_bus_id != expected_bus_id. It shall accept BOTH op senses -- an I2C transfer is half duplex and either-directional, so neither is malformed -- and report which via *out_direction.", + "text": "rcp_ep_i2c_decode_transfer_request() shall return RCP_EP_I2C_ERR_SHORT_FRAME for a frame too short for the ACF_ABB fixed header or its declared payload length, RCP_EP_I2C_ERR_BAD_MSG_TYPE for a non-ACF_ABB frame, RCP_EP_I2C_ERR_WRONG_BUS when byte_bus_id != expected_bus_id, and RCP_EP_I2C_ERR_BAD_EVT when evt[2:0] is not 0b000 -- TC18 §13.5 Table 30's only legal value for a plain request in I2C's endpoint-type row; every other value shall be rejected with error code UNSUPPORTED_CMD at the dispatch layer. It shall accept BOTH op senses -- an I2C transfer is half duplex and either-directional, so neither is malformed -- and report which via *out_direction.", "standard": "iso26262", "level": "ASIL-B", "asil": "ASIL-B", - "scope": "tc18" + "scope": "tc18", + "tc18": "§13.5 Table 30, TC18.txt L3666-3710" }, { "id": "REQ-I2C-013", @@ -3558,12 +3559,13 @@ }, { "id": "REQ-UART-020", - "title": "rcp_ep_uart_decode_write_request() rejects a malformed, misaddressed, or misdirected frame", - "text": "rcp_ep_uart_decode_write_request() shall return RCP_EP_UART_ERR_SHORT_FRAME for a frame too short for the ACF_ABB fixed header or its declared payload length, RCP_EP_UART_ERR_BAD_MSG_TYPE for a non-ACF_ABB frame, RCP_EP_UART_ERR_WRONG_BUS when byte_bus_id != expected_bus_id, and RCP_EP_UART_ERR_WRONG_OP when op is not RCP_ACF_OP_WRITE.", + "title": "rcp_ep_uart_decode_write_request() rejects a malformed, misaddressed, misdirected, or wrong-evt frame", + "text": "rcp_ep_uart_decode_write_request() shall return RCP_EP_UART_ERR_SHORT_FRAME for a frame too short for the ACF_ABB fixed header or its declared payload length, RCP_EP_UART_ERR_BAD_MSG_TYPE for a non-ACF_ABB frame, RCP_EP_UART_ERR_WRONG_BUS when byte_bus_id != expected_bus_id, RCP_EP_UART_ERR_WRONG_OP when op is not RCP_ACF_OP_WRITE, and RCP_EP_UART_ERR_BAD_EVT when evt[2:0] is not 0b000 -- TC18 §13.5 Table 30's only legal value for a plain request in UART's endpoint-type row; every other value shall be rejected with error code UNSUPPORTED_CMD at the dispatch layer.", "standard": "iso26262", "level": "ASIL-B", "asil": "ASIL-B", - "scope": "tc18" + "scope": "tc18", + "tc18": "§13.5 Table 30, TC18.txt L3666-3710" }, { "id": "REQ-UART-021", @@ -3604,11 +3606,12 @@ { "id": "REQ-UART-025", "title": "rcp_ep_uart_decode_read_request() rejects a payload-bearing read request with RCP_EP_UART_ERR_UNKNOWN_CMD", - "text": "rcp_ep_uart_decode_read_request() shall return RCP_EP_UART_ERR_UNKNOWN_CMD, rather than RCP_EP_UART_OK, when an otherwise well-formed read request carries a non-empty payload -- the deliberate asymmetry against GPIO/PWM_OUT request types documented in ep_uart.h.", + "text": "rcp_ep_uart_decode_read_request() shall return RCP_EP_UART_ERR_UNKNOWN_CMD, rather than RCP_EP_UART_OK, when an otherwise well-formed read request carries a non-empty payload -- the deliberate asymmetry against GPIO/PWM_OUT request types documented in ep_uart.h. It shall also return RCP_EP_UART_ERR_BAD_EVT when evt[2:0] is not 0b000 -- TC18 §13.5 Table 30's only legal value for a plain request in UART's endpoint-type row; every other value shall be rejected with error code UNSUPPORTED_CMD at the dispatch layer.", "standard": "iso26262", "level": "ASIL-B", "asil": "ASIL-B", - "scope": "tc18" + "scope": "tc18", + "tc18": "§13.5 Table 30, TC18.txt L3666-3710" }, { "id": "REQ-UART-026", @@ -5631,12 +5634,13 @@ }, { "id": "REQ-CANEP-001", - "title": "rcp_ep_can_frame_format_valid() accepts exactly the six defined evt[2:0] values", + "title": "rcp_ep_can_frame_format_valid() accepts exactly the six FrameFormat values Table 54 defines", "text": "rcp_ep_can_frame_format_valid() shall return true for v in 0..5 and false for any value above 5.", "standard": "iso26262", "level": "ASIL-B", "asil": "ASIL-B", - "scope": "tc18" + "scope": "tc18", + "tc18": "§13.7.11.3 Table 54, TC18.txt L5447-5456" }, { "id": "REQ-CANEP-002", @@ -5767,20 +5771,22 @@ { "id": "REQ-CANEP-016", "title": "rcp_ep_can_encode_frame_request() enforces frame-format, id, length, and xl_header preconditions", - "text": "rcp_ep_can_encode_frame_request() shall return a zeroed rcp_bytes_t if frame_format is not rcp_ep_can_frame_format_valid(), arbitration_id is not rcp_ep_can_arbitration_id_valid() for frame_format, tx_len exceeds rcp_ep_can_frame_format_max_data_len(frame_format), or xl_header's presence does not match rcp_ep_can_frame_format_is_xl(frame_format); otherwise it shall encode an ACF_ABB request carrying frame_format in evt[2:0] and this module's own arbitration-id/xl-header/data prefix layout.", + "text": "rcp_ep_can_encode_frame_request() shall return a zeroed rcp_bytes_t if frame_format is not rcp_ep_can_frame_format_valid(), arbitration_id is not rcp_ep_can_arbitration_id_valid() for frame_format, tx_len exceeds rcp_ep_can_frame_format_max_data_len(frame_format), or xl_header's presence does not match rcp_ep_can_frame_format_is_xl(frame_format); otherwise it shall encode an ACF_ABB request with evt left at 0 (TC18 Table 30's plain-request value for CAN's endpoint-type row) and frame_format packed into the payload's leading quadlet's top 3 bits alongside arbitration_id (TC18 §13.7.11.3 Figure 39), followed by this module's own xl-header/data layout. FIXED (v0.109.0): previously packed frame_format into evt[2:0] instead, a wire position TC18 does not assign it (see REQ-CANEP-031's corrected text).", "standard": "iso26262", "level": "ASIL-B", "asil": "ASIL-B", - "scope": "tc18" + "scope": "tc18", + "tc18": "§13.7.11.3 Figure 39, TC18.txt L5426-5446" }, { "id": "REQ-CANEP-017", - "title": "rcp_ep_can_decode_frame_request() rejects a malformed, misaddressed, or malformed-selector frame", - "text": "rcp_ep_can_decode_frame_request() shall return RCP_EP_CAN_ERR_SHORT_FRAME for a frame too short for the ACF_ABB fixed header, its declared payload length, or this module's own frame_format-dependent prefix length; RCP_EP_CAN_ERR_BAD_MSG_TYPE if b is not an ACF_ABB message; RCP_EP_CAN_ERR_WRONG_BUS when byte_bus_id != expected_bus_id; RCP_EP_CAN_ERR_WRONG_OP when op is not RCP_ACF_OP_WRITE; and RCP_EP_CAN_ERR_BAD_FRAME_FORMAT when evt[2:0] is not rcp_ep_can_frame_format_valid().", + "title": "rcp_ep_can_decode_frame_request() rejects a malformed, misaddressed, wrong-evt, malformed-selector, or bad-arbitration-id frame", + "text": "rcp_ep_can_decode_frame_request() shall return RCP_EP_CAN_ERR_SHORT_FRAME for a frame too short for the ACF_ABB fixed header, its declared payload length, the 4-byte leading quadlet, or the full frame_format-dependent prefix length; RCP_EP_CAN_ERR_BAD_MSG_TYPE if b is not an ACF_ABB message; RCP_EP_CAN_ERR_WRONG_BUS when byte_bus_id != expected_bus_id; RCP_EP_CAN_ERR_WRONG_OP when op is not RCP_ACF_OP_WRITE; RCP_EP_CAN_ERR_BAD_EVT when evt[2:0] is not 0b000 (TC18 §13.5 Table 30's only legal value for a plain request in CAN's endpoint-type row); RCP_EP_CAN_ERR_BAD_FRAME_FORMAT when the leading quadlet's top 3 bits are not rcp_ep_can_frame_format_valid(); and RCP_EP_CAN_ERR_BAD_ARBITRATION_ID when the leading quadlet's low 29 bits are not rcp_ep_can_arbitration_id_valid() for that frame_format.", "standard": "iso26262", "level": "ASIL-B", "asil": "ASIL-B", - "scope": "tc18" + "scope": "tc18", + "tc18": "§13.7.11.3 Figure 39, TC18.txt L5426-5446" }, { "id": "REQ-CANEP-018", @@ -5802,12 +5808,13 @@ }, { "id": "REQ-CANEP-020", - "title": "rcp_ep_can_decode_frame_response() rejects a malformed, misaddressed, or malformed-selector frame across both ACF_ABB and ACF_GBB", - "text": "rcp_ep_can_decode_frame_response() shall return RCP_EP_CAN_ERR_SHORT_FRAME for a frame too short for the applicable fixed header, its declared payload length, or this module's own frame_format-dependent prefix length; RCP_EP_CAN_ERR_WRONG_BUS when byte_bus_id != expected_bus_id; and RCP_EP_CAN_ERR_BAD_FRAME_FORMAT when evt[2:0] is not rcp_ep_can_frame_format_valid(), regardless of whether the message is ACF_ABB or ACF_GBB.", + "title": "rcp_ep_can_decode_frame_response() rejects a malformed, misaddressed, wrong-evt, malformed-selector, or bad-arbitration-id frame across both ACF_ABB and ACF_GBB", + "text": "rcp_ep_can_decode_frame_response() shall return RCP_EP_CAN_ERR_SHORT_FRAME for a frame too short for the applicable fixed header, its declared payload length, the 4-byte leading quadlet, or the full frame_format-dependent prefix length; RCP_EP_CAN_ERR_WRONG_BUS when byte_bus_id != expected_bus_id; RCP_EP_CAN_ERR_BAD_EVT when evt[2:0] is not 0b000 (TC18 §13.5 Table 30's only legal value for a plain response in CAN's endpoint-type row); RCP_EP_CAN_ERR_BAD_FRAME_FORMAT when the leading quadlet's top 3 bits are not rcp_ep_can_frame_format_valid(); and RCP_EP_CAN_ERR_BAD_ARBITRATION_ID when the leading quadlet's low 29 bits are not rcp_ep_can_arbitration_id_valid() for that frame_format -- regardless of whether the message is ACF_ABB or ACF_GBB.", "standard": "iso26262", "level": "ASIL-B", "asil": "ASIL-B", - "scope": "tc18" + "scope": "tc18", + "tc18": "§13.7.11.3 Figure 39, TC18.txt L5426-5446" }, { "id": "REQ-CANEP-021", @@ -6019,11 +6026,12 @@ { "id": "REQ-ISELED-022", "title": "rcp_ep_iseled_decode_command_request() validates and decodes an ACF-level command request", - "text": "rcp_ep_iseled_decode_command_request() shall return RCP_EP_ISELED_ERR_SHORT_FRAME for a frame shorter than the ACF_ABB header or its declared payload, RCP_EP_ISELED_ERR_BAD_MSG_TYPE for a non-ACF_ABB frame, RCP_EP_ISELED_ERR_WRONG_BUS for a mismatched byte_bus_id, RCP_EP_ISELED_ERR_WRONG_OP for an op other than RCP_ACF_OP_WRITE, and otherwise RCP_EP_ISELED_OK with the plain payload and transaction number populated.", + "text": "rcp_ep_iseled_decode_command_request() shall return RCP_EP_ISELED_ERR_SHORT_FRAME for a frame shorter than the ACF_ABB header or its declared payload, RCP_EP_ISELED_ERR_BAD_MSG_TYPE for a non-ACF_ABB frame, RCP_EP_ISELED_ERR_WRONG_BUS for a mismatched byte_bus_id, RCP_EP_ISELED_ERR_WRONG_OP for an op other than RCP_ACF_OP_WRITE, RCP_EP_ISELED_ERR_BAD_EVT when evt[2:0] is not 0b000 (TC18 §13.5 Table 30's only legal value for a plain request in ISELED's endpoint-type row; every other value shall be rejected with error code UNSUPPORTED_CMD at the dispatch layer), and otherwise RCP_EP_ISELED_OK with the plain payload and transaction number populated.", "standard": "iso26262", "level": "ASIL-B", "asil": "ASIL-B", - "scope": "tc18" + "scope": "tc18", + "tc18": "§13.5 Table 30, TC18.txt L3666-3710" }, { "id": "REQ-ISELED-023", @@ -6154,11 +6162,12 @@ { "id": "REQ-MDIO-013", "title": "rcp_ep_mdio_decode_read_request() validates and decodes an ACF-level MDIO read request", - "text": "rcp_ep_mdio_decode_read_request() shall return RCP_EP_MDIO_ERR_SHORT_FRAME for a frame shorter than the ACF_ABB header, its declared payload, or the 7-byte request prefix; RCP_EP_MDIO_ERR_BAD_MSG_TYPE for a non-ACF_ABB frame; RCP_EP_MDIO_ERR_WRONG_BUS for a mismatched byte_bus_id; RCP_EP_MDIO_ERR_WRONG_OP for an op other than RCP_ACF_OP_READ; RCP_EP_MDIO_ERR_BAD_ADDR when the decoded address fails rcp_ep_mdio_addr_valid(); RCP_EP_MDIO_ERR_BAD_WORD_COUNT when the decoded word_count is 0 or exceeds RCP_EP_MDIO_MAX_BURST_WORDS; and otherwise RCP_EP_MDIO_OK with the address, word count, and transaction number populated.", + "text": "rcp_ep_mdio_decode_read_request() shall return RCP_EP_MDIO_ERR_SHORT_FRAME for a frame shorter than the ACF_ABB header, its declared payload, or the 7-byte request prefix; RCP_EP_MDIO_ERR_BAD_MSG_TYPE for a non-ACF_ABB frame; RCP_EP_MDIO_ERR_WRONG_BUS for a mismatched byte_bus_id; RCP_EP_MDIO_ERR_WRONG_OP for an op other than RCP_ACF_OP_READ; RCP_EP_MDIO_ERR_BAD_EVT when evt[2:0] is not 0b000 (TC18 §13.5 Table 30's only legal value for a plain request in MDIO's endpoint-type row; every other value shall be rejected with error code UNSUPPORTED_CMD at the dispatch layer); RCP_EP_MDIO_ERR_BAD_ADDR when the decoded address fails rcp_ep_mdio_addr_valid(); RCP_EP_MDIO_ERR_BAD_WORD_COUNT when the decoded word_count is 0 or exceeds RCP_EP_MDIO_MAX_BURST_WORDS; and otherwise RCP_EP_MDIO_OK with the address, word count, and transaction number populated.", "standard": "iso26262", "level": "ASIL-B", "asil": "ASIL-B", - "scope": "tc18" + "scope": "tc18", + "tc18": "§13.5 Table 30, TC18.txt L3666-3710" }, { "id": "REQ-MDIO-014", @@ -6190,11 +6199,12 @@ { "id": "REQ-MDIO-017", "title": "rcp_ep_mdio_decode_write_request() validates and decodes an ACF-level MDIO write request", - "text": "rcp_ep_mdio_decode_write_request() shall return RCP_EP_MDIO_ERR_SHORT_FRAME for a frame shorter than the ACF_ABB header, its declared payload, or the 5-byte address prefix; RCP_EP_MDIO_ERR_BAD_MSG_TYPE for a non-ACF_ABB frame; RCP_EP_MDIO_ERR_WRONG_BUS for a mismatched byte_bus_id; RCP_EP_MDIO_ERR_WRONG_OP for an op other than RCP_ACF_OP_WRITE; RCP_EP_MDIO_ERR_BAD_ADDR when the decoded address fails rcp_ep_mdio_addr_valid(); RCP_EP_MDIO_ERR_BAD_WORD_COUNT when the trailing words region's byte length is odd, is 0, or represents more than RCP_EP_MDIO_MAX_BURST_WORDS words; and otherwise RCP_EP_MDIO_OK with the address, packed word bytes, word count, and transaction number populated.", + "text": "rcp_ep_mdio_decode_write_request() shall return RCP_EP_MDIO_ERR_SHORT_FRAME for a frame shorter than the ACF_ABB header, its declared payload, or the 5-byte address prefix; RCP_EP_MDIO_ERR_BAD_MSG_TYPE for a non-ACF_ABB frame; RCP_EP_MDIO_ERR_WRONG_BUS for a mismatched byte_bus_id; RCP_EP_MDIO_ERR_WRONG_OP for an op other than RCP_ACF_OP_WRITE; RCP_EP_MDIO_ERR_BAD_EVT when evt[2:0] is not 0b000 (TC18 §13.5 Table 30's only legal value for a plain request in MDIO's endpoint-type row; every other value shall be rejected with error code UNSUPPORTED_CMD at the dispatch layer); RCP_EP_MDIO_ERR_BAD_ADDR when the decoded address fails rcp_ep_mdio_addr_valid(); RCP_EP_MDIO_ERR_BAD_WORD_COUNT when the trailing words region's byte length is odd, is 0, or represents more than RCP_EP_MDIO_MAX_BURST_WORDS words; and otherwise RCP_EP_MDIO_OK with the address, packed word bytes, word count, and transaction number populated.", "standard": "iso26262", "level": "ASIL-B", "asil": "ASIL-B", - "scope": "tc18" + "scope": "tc18", + "tc18": "§13.5 Table 30, TC18.txt L3666-3710" }, { "id": "REQ-MDIO-018", @@ -6666,21 +6676,23 @@ }, { "id": "REQ-CANEP-026", - "title": "rcp_ep_can_decode_frame_response_fragment() validates byte_bus_id, message type, and frame_format the same way the unfragmented response decoder does", - "text": "rcp_ep_can_decode_frame_response_fragment() shall return the same RCP_EP_CAN_ERR_SHORT_FRAME/_ERR_BAD_MSG_TYPE/_ERR_WRONG_BUS/_ERR_BAD_FRAME_FORMAT error codes, under the same conditions, that rcp_ep_can_decode_frame_response() returns them for.", + "title": "rcp_ep_can_decode_frame_response_fragment() validates byte_bus_id, message type, and evt the same way the unfragmented response decoder does", + "text": "rcp_ep_can_decode_frame_response_fragment() shall return the same RCP_EP_CAN_ERR_SHORT_FRAME/_ERR_BAD_MSG_TYPE/_ERR_WRONG_BUS/_ERR_BAD_EVT error codes, under the same conditions, that rcp_ep_can_decode_frame_response() returns them for. It does NOT validate or output frame_format: frame_format lives in the combined payload's own leading quadlet (TC18 §13.7.11.3 Figure 39), not per-fragment header state, so it is only recoverable after reassembly -- see REQ-CANEP-027. FIXED (v0.109.0): this function previously validated and output frame_format derived from evt[2:0], the same non-conformant design corrected across this module.", "standard": "iso26262", "level": "ASIL-B", "asil": "ASIL-B", - "scope": "tc18" + "scope": "tc18", + "tc18": "§13.7.11.3 Figure 39, TC18.txt L5426-5446" }, { "id": "REQ-CANEP-027", - "title": "rcp_ep_can_decode_reassembled_frame_response() parses a fully reassembled combined payload into arbitration_id/xl_header/rx_data", - "text": "rcp_ep_can_decode_reassembled_frame_response() shall, given frame_format and a reassembled buffer of at least that format's prefix length, populate out_arbitration_id, out_xl_header (only when the format is a CAN XL variant), and out_rx_data/out_rx_len identically to how rcp_ep_can_decode_frame_response() would have for the same original, unfragmented payload.", + "title": "rcp_ep_can_decode_reassembled_frame_response() parses a fully reassembled combined payload into frame_format/arbitration_id/xl_header/rx_data", + "text": "rcp_ep_can_decode_reassembled_frame_response() shall, given a reassembled buffer of at least 4 bytes, read frame_format from its leading quadlet's top 3 bits (TC18 §13.7.11.3 Figure 39), returning RCP_EP_CAN_ERR_SHORT_FRAME if the buffer is shorter than that format's own full prefix length, RCP_EP_CAN_ERR_BAD_FRAME_FORMAT if it is not rcp_ep_can_frame_format_valid(), or RCP_EP_CAN_ERR_BAD_ARBITRATION_ID if the leading quadlet's low 29 bits are not rcp_ep_can_arbitration_id_valid() for that format; otherwise populate out_frame_format, out_arbitration_id, out_xl_header (only when the format is a CAN XL variant), and out_rx_data/out_rx_len identically to how rcp_ep_can_decode_frame_response() would have for the same original, unfragmented payload. FIXED (v0.109.0): frame_format was previously a caller-supplied input (sourced from a per-fragment evt[2:0], REQ-CANEP-026's now-corrected design); it is now derived from the reassembled payload itself, the only place it is actually carried.", "standard": "iso26262", "level": "ASIL-B", "asil": "ASIL-B", - "scope": "tc18" + "scope": "tc18", + "tc18": "§13.7.11.3 Figure 39, TC18.txt L5426-5446" }, { "id": "REQ-UART-029", @@ -8953,14 +8965,14 @@ }, { "id": "REQ-CANEP-031", - "title": "The FrameFormat selector's numeric code assignment (CBFF 0 through XL new-PL 5, with 6 and 7 reserved)", - "text": "rcp_ep_can_frame_format_t shall assign exactly the FrameFormat codes TC18 \u00a713.7.11.3 Table 54 fixes: RCP_EP_CAN_FRAME_CBFF = 0, RCP_EP_CAN_FRAME_CEFF = 1, RCP_EP_CAN_FRAME_FBFF = 2, RCP_EP_CAN_FRAME_FEFF = 3, RCP_EP_CAN_FRAME_XL_CLASSICAL_PL = 4 (XL over the classical physical layer), and RCP_EP_CAN_FRAME_XL_NEW_PL = 5 (XL over the new physical layer); codes 6 and 7 are reserved and rcp_ep_can_frame_format_valid() shall reject them, causing rcp_ep_can_decode_frame_request()/_decode_frame_response() to return RCP_EP_CAN_ERR_BAD_FRAME_FORMAT. The selector shall be carried in the low three bits of the ACF byte_message_info evt field.", + "title": "The FrameFormat selector's numeric code assignment (CBFF 0 through XL new-PL 5, with 6 and 7 reserved), carried in the payload's leading quadlet", + "text": "rcp_ep_can_frame_format_t shall assign exactly the FrameFormat codes TC18 \u00a713.7.11.3 Table 54 fixes: RCP_EP_CAN_FRAME_CBFF = 0, RCP_EP_CAN_FRAME_CEFF = 1, RCP_EP_CAN_FRAME_FBFF = 2, RCP_EP_CAN_FRAME_FEFF = 3, RCP_EP_CAN_FRAME_XL_CLASSICAL_PL = 4 (XL over the classical physical layer), and RCP_EP_CAN_FRAME_XL_NEW_PL = 5 (XL over the new physical layer); codes 6 and 7 are reserved and rcp_ep_can_frame_format_valid() shall reject them, causing rcp_ep_can_decode_frame_request()/_decode_frame_response()/_decode_reassembled_frame_response() to return RCP_EP_CAN_ERR_BAD_FRAME_FORMAT. The selector shall be carried in the top 3 bits of the payload's leading quadlet (TC18 \u00a713.7.11.3 Figure 39), sharing that quadlet with a 29-bit arbitration_id. CORRECTED (v0.109.0): this requirement previously (and incorrectly) asserted the selector was carried in the ACF byte_message_info evt field's low three bits -- verified against Figure 39 directly, that claim was never true of TC18 and was itself the bug (evt[2:0] instead carries CAN's ordinary TC18 \u00a713.5 Table 30 Row-2 meaning: 000b plain, 111b configuration-write, else UNSUPPORTED_CMD). Found and fixed while investigating this repo's Table 30 centralization.", "standard": "iso26262", "level": "ASIL-B", "asil": "ASIL-B", "scope": "tc18", "status": "implemented", - "tc18": "\u00a713.7.11.3 Table 54, TC18.txt L5447-5456" + "tc18": "\u00a713.7.11.3 Table 54 + Figure 39, TC18.txt L5426-5456" }, { "id": "REQ-CANEP-032", diff --git a/.fusa.json b/.fusa.json index 52279ab..4c79457 100644 --- a/.fusa.json +++ b/.fusa.json @@ -2,7 +2,7 @@ "configVersion": "1.0", "project": { "name": "c-RCP", - "version": "0.107.0" + "version": "0.109.0" }, "standard": "iso26262", "strict": false, diff --git a/CMakeLists.txt b/CMakeLists.txt index 23f1411..41a6a85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.16) project(c-rcp - VERSION 0.107.0 + VERSION 0.109.0 DESCRIPTION "OPEN Alliance TC18 Remote Control Protocol (C)" HOMEPAGE_URL "https://github.com/SoundMatt/c-RCP" LANGUAGES C diff --git a/include/rcp/ep_can.h b/include/rcp/ep_can.h index 3df19b9..473b618 100644 --- a/include/rcp/ep_can.h +++ b/include/rcp/ep_can.h @@ -133,23 +133,34 @@ * out of this milestone's scope; there is no wire representation, encode * function, or decode outcome for a remote frame anywhere in this module. * - * ── FrameFormat selection (evt[2:0]) ──────────────────────────────────────── + * ── FrameFormat selection (payload prefix, NOT evt[2:0]) ──────────────────── * - * rcp_ep_can_frame_format_t names the six frame variants this endpoint - * selects among -- Classical Base/Extended Frame Format (CBFF/CEFF), - * FD Base/Extended Frame Format (FBFF/FEFF), and CAN XL over either the - * classical or the new CAN XL physical layer -- selected via the ACF - * byte_message_info header's evt field's low three bits (acf.h), the same - * evt[2:0]-as-selector convention ep_spi.h's channel selector already - * established (there, a channel number; here, a frame-format code). - * rcp_ep_can_frame_format_valid() accepts only the six defined values (0-5); - * values 6-7 select no defined format and are rejected on decode with - * RCP_EP_CAN_ERR_BAD_FRAME_FORMAT, mirroring ep_spi.h's own - * RCP_EP_SPI_ERR_BAD_CHANNEL treatment of its own out-of-range evt[2:0] - * values -- the roadmap does not itself enumerate which 3-bit codes name - * which frame format; the assignment above is this module's own original - * design filling that gap, the same kind of gap-filling ep_lin.h's own - * evt[2:0] comparison-mode enumeration already did for LIN. + * TC18 §13.7.11.3 Figure 39 packs FrameFormat into the payload itself, as + * the top 3 bits of the request/response's leading quadlet, sharing that + * quadlet with a 29-bit CAN ID (right-aligned for an 11-bit base id, per + * TC18 §13.7.11.3's own note) -- NOT into evt[2:0], which for CAN (a + * member of TC18 §13.5 Table 30's {ADC, PWM_IN, I2C, LIN, CAN, UART, + * ISELED, MDIO} row) has the same ordinary meaning every other endpoint in + * that row gives it: 000b for a plain request/response, 111b for a + * configuration write (§12.7.1), every other value rejected with error + * code UNSUPPORTED_CMD (see rcp_acf_evt_row2_is_plain(), acf.h). An earlier + * revision of this module packed frame_format into evt[2:0] instead -- + * modeled on ep_spi.h's real, TC18-sanctioned evt[2:0] channel-selector + * row -- without checking that CAN belongs to the *other* Table 30 row, + * whose evt[2:0] values TC18 reserves. That was wrong: it accepted + * evt[2:0] values Table 30 requires this endpoint type to reject, and put + * FrameFormat at a wire position no compliant peer reads it from. Fixed + * (v0.109.0) by moving frame_format into the payload's leading quadlet, per + * Figure 39, and giving evt[2:0] its ordinary Table 30 meaning. + * + * rcp_ep_can_frame_format_t names the six frame variants TC18's own Table + * 54 enumerates -- Classical Base/Extended Frame Format (CBFF/CEFF), FD + * Base/Extended Frame Format (FBFF/FEFF), and CAN XL over either the + * classical or the new CAN XL physical layer -- with Table 54's own + * numeric assignments (0-5); values 6-7 are Table 54's own two reserved + * codes and select no defined format. + * rcp_ep_can_frame_format_valid() accepts only 0-5; a decoded FrameFormat + * of 6 or 7 is rejected with RCP_EP_CAN_ERR_BAD_FRAME_FORMAT. * * rcp_ep_can_frame_format_id_width() reports whether a format's arbitration * identifier is an 11-bit ("base") or 29-bit ("extended") value: CBFF/FBFF @@ -228,21 +239,21 @@ * request cannot be sent in one ACF message and has no multi-message * alternative yet -- tracked as a follow-up, not this fix's scope. * - * ── Wire layout: this module's own prefix-then-data choice ───────────────── + * ── Wire layout: TC18 §13.7.11.3 Figure 39 ────────────────────────────────── * - * A frame request/response's ACF payload is this module's own fixed-prefix- - * then-raw-data layout (the same kind of "this module's own wire-layout - * choice" ep_uart.h's own bit-padding scheme already established as this - * codebase's convention for representing a structured concept the spec - * names but does not itself lay out bit-for-bit): a big-endian 4-byte - * arbitration_id, followed -- only when frame_format is one of the two CAN - * XL variants -- by xl_header's big-endian sdt (1 byte), vcid (1 byte), and - * af (4 bytes), followed by the raw CAN data bytes exactly as supplied - * (this module never inspects, generates, or reformats any byte of the - * data payload itself, the same "dumb pass-through for the data content" - * philosophy ep_lin.h/ep_i2c.h/ep_uart.h already established for their own - * raw-byte content -- only the frame's own structural fields, not its - * data, are modeled here). + * A frame request/response's ACF payload is a big-endian 4-byte leading + * quadlet packing frame_format (top 3 bits) and arbitration_id (bottom 29 + * bits, right-aligned for an 11-bit base id -- see rcp_ep_can_frame_format_id_width()), + * followed -- only when frame_format is one of the two CAN XL variants -- + * by xl_header's big-endian sdt (1 byte), vcid (1 byte), and af (4 bytes) + * (Figure 39's own "CAN data field includes ... RRS, SDT, VCID, AF" note: + * these six bytes are the leading bytes of Figure 39's "CAN data" region, + * not a separate field ahead of it), followed by the raw CAN data bytes + * exactly as supplied (this module never inspects, generates, or reformats + * any byte of the data payload itself, the same "dumb pass-through for the + * data content" philosophy ep_lin.h/ep_i2c.h/ep_uart.h already established + * for their own raw-byte content -- only the frame's own structural + * fields, not its data, are modeled here). * * ── No trigger-signal table: a documented upstream spec gap ───────────────── * @@ -485,12 +496,25 @@ bool rcp_ep_can_set_xl_filter(rcp_ep_can_functional_cfg_t *cfg, uint8_t index, /* ── Error codes ───────────────────────────────────────────────────────────── */ typedef enum { - RCP_EP_CAN_OK = 0, - RCP_EP_CAN_ERR_SHORT_FRAME = 1, - RCP_EP_CAN_ERR_BAD_MSG_TYPE = 2, - RCP_EP_CAN_ERR_WRONG_BUS = 3, - RCP_EP_CAN_ERR_WRONG_OP = 4, - RCP_EP_CAN_ERR_BAD_FRAME_FORMAT = 5, + RCP_EP_CAN_OK = 0, + RCP_EP_CAN_ERR_SHORT_FRAME = 1, + RCP_EP_CAN_ERR_BAD_MSG_TYPE = 2, + RCP_EP_CAN_ERR_WRONG_BUS = 3, + RCP_EP_CAN_ERR_WRONG_OP = 4, + /* The payload's leading quadlet's top 3 bits (FrameFormat, TC18 + * Table 54) are not one of Table 54's six defined values -- see the + * file header's "FrameFormat selection" section. */ + RCP_EP_CAN_ERR_BAD_FRAME_FORMAT = 5, + /* evt[2:0] is not 0b000, TC18 §13.5 Table 30's only legal value for a + * plain (non-configuration) request/response in CAN's endpoint-type + * row -- caller shall respond with error code UNSUPPORTED_CMD (see + * rcp_acf_evt_row2_is_plain()). */ + RCP_EP_CAN_ERR_BAD_EVT = 6, + /* The decoded arbitration_id does not fit FrameFormat's own id width + * (TC18 §13.7.11.3: an 11-bit base id must be right-aligned, i.e. the + * leading quadlet's bits 3-20 must be zero for a base-11 format) -- see + * rcp_ep_can_arbitration_id_valid(). */ + RCP_EP_CAN_ERR_BAD_ARBITRATION_ID = 7, } rcp_ep_can_errc_t; /* Human-readable message for an rcp_ep_can_errc_t value. Never returns NULL. */ @@ -498,15 +522,15 @@ const char *rcp_ep_can_strerror(rcp_ep_can_errc_t e); /* ── Frame request ─────────────────────────────────────────────────────────── */ -/* Encodes an ACF_ABB frame request addressed to byte_bus_id: evt's low - * three bits carry frame_format (any other bits of the ACF header's evt - * field are left 0), and the payload is this module's own prefix-then-data - * layout -- see the file header -- of arbitration_id, xl_header (only when - * frame_format is a CAN XL variant), and tx_data[0..tx_len) (the raw CAN - * data bytes; tx_data may be NULL iff tx_len == 0). Returns a zeroed - * rcp_bytes_t (data=NULL) if: frame_format is not - * rcp_ep_can_frame_format_valid(); arbitration_id is not - * rcp_ep_can_arbitration_id_valid() for frame_format; tx_len exceeds +/* Encodes an ACF_ABB frame request addressed to byte_bus_id: evt is left + * entirely 0 (TC18 §13.5 Table 30's ordinary "plain request" value for + * CAN's endpoint-type row), and the payload is TC18 §13.7.11.3 Figure 39's + * layout -- see the file header -- of frame_format+arbitration_id (the + * leading quadlet), xl_header (only when frame_format is a CAN XL + * variant), and tx_data[0..tx_len) (the raw CAN data bytes; tx_data may be + * NULL iff tx_len == 0). Returns a zeroed rcp_bytes_t (data=NULL) if: + * frame_format is not rcp_ep_can_frame_format_valid(); arbitration_id is + * not rcp_ep_can_arbitration_id_valid() for frame_format; tx_len exceeds * rcp_ep_can_frame_format_max_data_len(frame_format); xl_header is NULL * when rcp_ep_can_frame_format_is_xl(frame_format) is true, or non-NULL * when it is false; or on allocation failure. Caller frees the result with @@ -520,18 +544,24 @@ rcp_bytes_t rcp_ep_can_encode_frame_request(rcp_byte_bus_id_t byte_bus_id, /* Decodes and validates an ACF-level CAN frame request from b[0..len). * Fails with RCP_EP_CAN_ERR_SHORT_FRAME if b is shorter than the ACF_ABB - * fixed header, its declared payload length, or this module's own - * frame_format-dependent prefix length; RCP_EP_CAN_ERR_BAD_MSG_TYPE if b is - * not an ACF_ABB message; RCP_EP_CAN_ERR_WRONG_BUS if its byte_bus_id != - * expected_bus_id; RCP_EP_CAN_ERR_WRONG_OP if its op is not - * RCP_ACF_OP_WRITE; RCP_EP_CAN_ERR_BAD_FRAME_FORMAT if evt[2:0] is not - * rcp_ep_can_frame_format_valid(). On RCP_EP_CAN_OK, *out_frame_format, - * *out_arbitration_id, and *out_transaction_num are populated; - * *out_xl_header is populated iff rcp_ep_can_frame_format_is_xl() - * (*out_frame_format), left entirely untouched otherwise; *out_tx_data / - * *out_tx_len are set to a *borrowed* view into b (not copied -- matching - * every prior endpoint type's own raw-data decode convention) of the raw - * CAN data bytes following this module's own prefix. */ + * fixed header, its declared payload length, the 4-byte frame_format+ + * arbitration_id quadlet, or the full frame_format-dependent prefix + * length; RCP_EP_CAN_ERR_BAD_MSG_TYPE if b is not an ACF_ABB message; + * RCP_EP_CAN_ERR_WRONG_BUS if its byte_bus_id != expected_bus_id; + * RCP_EP_CAN_ERR_WRONG_OP if its op is not RCP_ACF_OP_WRITE; + * RCP_EP_CAN_ERR_BAD_EVT if its evt[2:0] is not 0b000 + * (rcp_acf_evt_row2_is_plain(), TC18 §13.5 Table 30 -- the caller shall + * respond with error code UNSUPPORTED_CMD); RCP_EP_CAN_ERR_BAD_FRAME_FORMAT + * if the decoded frame_format is not rcp_ep_can_frame_format_valid(); + * RCP_EP_CAN_ERR_BAD_ARBITRATION_ID if the decoded arbitration_id is not + * rcp_ep_can_arbitration_id_valid() for that frame_format. On + * RCP_EP_CAN_OK, *out_frame_format, *out_arbitration_id, and + * *out_transaction_num are populated; *out_xl_header is populated iff + * rcp_ep_can_frame_format_is_xl() (*out_frame_format), left entirely + * untouched otherwise; *out_tx_data / *out_tx_len are set to a *borrowed* + * view into b (not copied -- matching every prior endpoint type's own + * raw-data decode convention) of the raw CAN data bytes following this + * module's own prefix. */ rcp_ep_can_errc_t rcp_ep_can_decode_frame_request(const uint8_t *b, size_t len, rcp_byte_bus_id_t expected_bus_id, rcp_ep_can_frame_format_t *out_frame_format, @@ -567,10 +597,14 @@ rcp_bytes_t rcp_ep_can_encode_frame_response(rcp_byte_bus_id_t byte_bus_id, * decoder above, since a response's encoding depends on the responding * endpoint's own timed/untimed choice). Fails with RCP_EP_CAN_ERR_SHORT_FRAME * (frame too short for the applicable fixed header, its declared payload - * length, or this module's own frame_format-dependent prefix length), - * RCP_EP_CAN_ERR_WRONG_BUS (byte_bus_id != expected_bus_id), or - * RCP_EP_CAN_ERR_BAD_FRAME_FORMAT (evt[2:0] is not - * rcp_ep_can_frame_format_valid()). On RCP_EP_CAN_OK, *out_frame_format, + * length, the 4-byte frame_format+arbitration_id quadlet, or the full + * frame_format-dependent prefix length), RCP_EP_CAN_ERR_WRONG_BUS + * (byte_bus_id != expected_bus_id), RCP_EP_CAN_ERR_BAD_EVT (evt[2:0] is + * not 0b000, rcp_acf_evt_row2_is_plain()), RCP_EP_CAN_ERR_BAD_FRAME_FORMAT + * (the decoded frame_format is not rcp_ep_can_frame_format_valid()), or + * RCP_EP_CAN_ERR_BAD_ARBITRATION_ID (the decoded arbitration_id is not + * rcp_ep_can_arbitration_id_valid() for that frame_format). On + * RCP_EP_CAN_OK, *out_frame_format, * *out_arbitration_id, and *out_transaction_num are populated; * *out_xl_header is populated iff rcp_ep_can_frame_format_is_xl() * (*out_frame_format), left entirely untouched otherwise; *out_rx_data / @@ -612,8 +646,11 @@ size_t rcp_ep_can_frame_response_fragment_count(rcp_ep_can_frame_format_t frame_ * max_fragment_payload octets -- into * out_frames[0..rcp_ep_can_frame_response_fragment_count(...)) (caller- * allocated, sized by calling that function first). Every fragment - * shares byte_bus_id/frame_format(evt)/op(READ)/transaction_num/timed/ - * timestamp with rcp_ep_can_encode_frame_response(); only the ms flag, + * shares byte_bus_id/evt(0)/op(READ)/transaction_num/timed/timestamp with + * rcp_ep_can_encode_frame_response() -- frame_format itself lives inside + * the combined payload's own leading quadlet (see the file header), not + * per-fragment header state, so only the first fragment actually carries + * it. Only the ms flag, * the read_size_or_segment_num field (meaningful only on an ms=true * fragment -- see acf.h/fragment.h), and each fragment's own payload * slice differ. When the combined payload already fits in one fragment, @@ -642,26 +679,31 @@ size_t rcp_ep_can_encode_frame_response_fragmented(rcp_byte_bus_id_t byte_bus_id rcp_bytes_t *out_frames); /* Decodes one fragment of a (possibly multi-fragment) CAN frame response - * from b[0..len) -- the same peek-message-type/byte_bus_id/frame_format - * validation rcp_ep_can_decode_frame_response() applies, but this - * function does *not* strip this module's own prefix-then-data layout - * from the payload (a fragment other than the first may not even contain - * the whole prefix -- fragmentation operates on the flat combined byte - * sequence, agnostic to its own internal structure, per fragment.h's file - * header). Instead it surfaces the fragment's own ms bit, + * from b[0..len) -- the same peek-message-type/byte_bus_id validation + * rcp_ep_can_decode_frame_response() applies, but this function does + * *not* strip TC18 Figure 39's leading-quadlet-then-data layout from the + * payload (a fragment other than the first may not even contain the + * whole leading quadlet -- fragmentation operates on the flat combined + * byte sequence, agnostic to its own internal structure, per fragment.h's + * file header). Since frame_format now lives inside that leading quadlet + * rather than in evt (see the file header's "FrameFormat selection" + * section), it is not obtainable per-fragment at all -- unlike the prior + * evt-carried design, this function does not (and cannot) output it; + * rcp_ep_can_decode_reassembled_frame_response() recovers it once, + * *after* reassembly, from the reassembled buffer's own leading quadlet. + * This function instead surfaces the fragment's own ms bit, * read_size_or_segment_num (as *out_segment_num, meaningful only when * *out_ms), and raw ACF payload slice (*out_payload / *out_payload_len, * borrowed into b, matching every decode function in this module), for a * caller to feed straight into a rcp_fragment_reassembler_t * (fragment.h). Once reassembly reports RCP_FRAGMENT_REASM_COMPLETE, pass * the reassembled buffer to rcp_ep_can_decode_reassembled_frame_response() - * to extract arbitration_id/xl_header/rx_data. Fails with the same - * RCP_EP_CAN_ERR_SHORT_FRAME/_ERR_BAD_MSG_TYPE/_ERR_WRONG_BUS/ - * _ERR_BAD_FRAME_FORMAT conditions rcp_ep_can_decode_frame_response() - * does; on RCP_EP_CAN_OK, every output parameter is populated. */ + * to extract frame_format/arbitration_id/xl_header/rx_data. Fails with + * the same RCP_EP_CAN_ERR_SHORT_FRAME/_ERR_BAD_MSG_TYPE/_ERR_WRONG_BUS/ + * _ERR_BAD_EVT conditions rcp_ep_can_decode_frame_response() does; on + * RCP_EP_CAN_OK, every output parameter is populated. */ rcp_ep_can_errc_t rcp_ep_can_decode_frame_response_fragment(const uint8_t *b, size_t len, rcp_byte_bus_id_t expected_bus_id, - rcp_ep_can_frame_format_t *out_frame_format, bool *out_ms, uint8_t *out_segment_num, const uint8_t **out_payload, @@ -670,24 +712,25 @@ rcp_ep_can_errc_t rcp_ep_can_decode_frame_response_fragment(const uint8_t *b, si uint64_t *out_timestamp, uint8_t *out_transaction_num); -/* Applies this module's own prefix-then-data parsing (see the file +/* Applies TC18 Figure 39's leading-quadlet-then-data parsing (see the file * header's "Wire layout" section) to a fully reassembled combined payload * -- rcp_fragment_reassembler_get()'s output once - * rcp_fragment_reassembler_feed() has reported RCP_FRAGMENT_REASM_COMPLETE - * -- for frame_format as recorded from (any of) that sequence's own - * fragments (rcp_ep_can_decode_frame_response_fragment()'s *out_frame_format, - * which is round-tripped identically on every fragment of one logical - * response). This is the second half of what - * rcp_ep_can_decode_frame_response() does in one step for a single, - * unfragmented frame. Returns RCP_EP_CAN_ERR_SHORT_FRAME if reassembled_len - * is shorter than frame_format's own prefix length; RCP_EP_CAN_ERR_BAD_FRAME_FORMAT - * if frame_format itself is not rcp_ep_can_frame_format_valid(). On - * RCP_EP_CAN_OK, *out_arbitration_id / *out_xl_header are populated (the - * latter only when rcp_ep_can_frame_format_is_xl(frame_format)) and - * *out_rx_data / *out_rx_len are set to a *borrowed* view into reassembled. */ + * rcp_fragment_reassembler_feed() has reported RCP_FRAGMENT_REASM_COMPLETE. + * This is the second half of what rcp_ep_can_decode_frame_response() does + * in one step for a single, unfragmented frame. Returns + * RCP_EP_CAN_ERR_SHORT_FRAME if reassembled_len is shorter than the + * leading quadlet, or shorter than the full frame_format-dependent + * prefix length once frame_format is known; RCP_EP_CAN_ERR_BAD_FRAME_FORMAT + * if the decoded frame_format is not rcp_ep_can_frame_format_valid(); + * RCP_EP_CAN_ERR_BAD_ARBITRATION_ID if the decoded arbitration_id is not + * rcp_ep_can_arbitration_id_valid() for that frame_format. On + * RCP_EP_CAN_OK, *out_frame_format / *out_arbitration_id / *out_xl_header + * are populated (the latter only when + * rcp_ep_can_frame_format_is_xl(*out_frame_format)) and *out_rx_data / + * *out_rx_len are set to a *borrowed* view into reassembled. */ rcp_ep_can_errc_t rcp_ep_can_decode_reassembled_frame_response(const uint8_t *reassembled, size_t reassembled_len, - rcp_ep_can_frame_format_t frame_format, + rcp_ep_can_frame_format_t *out_frame_format, uint32_t *out_arbitration_id, rcp_ep_can_xl_header_t *out_xl_header, const uint8_t **out_rx_data, diff --git a/include/rcp/ep_i2c.h b/include/rcp/ep_i2c.h index 9a557ed..cedd52d 100644 --- a/include/rcp/ep_i2c.h +++ b/include/rcp/ep_i2c.h @@ -261,6 +261,11 @@ typedef enum { * this module: both op senses are valid on an I2C transfer (see the * file header), so there is no longer a "wrong" one to reject. */ RCP_EP_I2C_ERR_WRONG_OP = 4, + /* evt[2:0] is not 0b000, TC18 §13.5 Table 30's only legal value for a + * plain (non-configuration) request in I2C's endpoint-type row -- + * caller shall respond with error code UNSUPPORTED_CMD (see + * rcp_acf_evt_row2_is_plain()). */ + RCP_EP_I2C_ERR_BAD_EVT = 5, } rcp_ep_i2c_errc_t; /* Human-readable message for an rcp_ep_i2c_errc_t value. Never returns NULL. */ @@ -299,8 +304,10 @@ rcp_bytes_t rcp_ep_i2c_encode_transfer_request(rcp_byte_bus_id_t byte_bus_id, * Fails with RCP_EP_I2C_ERR_SHORT_FRAME if b is shorter than the ACF_ABB * fixed header or its declared payload length; RCP_EP_I2C_ERR_BAD_MSG_TYPE * if b is not an ACF_ABB message; RCP_EP_I2C_ERR_WRONG_BUS if its - * byte_bus_id != expected_bus_id. Both op senses are accepted -- see the - * file header -- and reported via *out_direction. + * byte_bus_id != expected_bus_id; RCP_EP_I2C_ERR_BAD_EVT if its evt[2:0] + * is not 0b000 (rcp_acf_evt_row2_is_plain(), TC18 §13.5 Table 30 -- the + * caller shall respond with error code UNSUPPORTED_CMD). Both op senses + * are accepted -- see the file header -- and reported via *out_direction. * * On RCP_EP_I2C_OK, *out_direction, *out_read_size (the requested * read_size for RCP_EP_I2C_DIR_READ, 0 for RCP_EP_I2C_DIR_WRITE, whose diff --git a/include/rcp/ep_iseled.h b/include/rcp/ep_iseled.h index 9062313..1d51a1b 100644 --- a/include/rcp/ep_iseled.h +++ b/include/rcp/ep_iseled.h @@ -369,6 +369,11 @@ typedef enum { RCP_EP_ISELED_ERR_CRC_MISMATCH = 6, RCP_EP_ISELED_ERR_ODD_SYMBOL_COUNT = 7, RCP_EP_ISELED_ERR_ALLOC = 8, + /* evt[2:0] is not 0b000, TC18 §13.5 Table 30's only legal value for a + * plain (non-configuration) request in ISELED's endpoint-type row -- + * caller shall respond with error code UNSUPPORTED_CMD (see + * rcp_acf_evt_row2_is_plain()). */ + RCP_EP_ISELED_ERR_BAD_EVT = 9, } rcp_ep_iseled_errc_t; /* Human-readable message for an rcp_ep_iseled_errc_t value. Never returns NULL. */ @@ -410,7 +415,10 @@ rcp_bytes_t rcp_ep_iseled_encode_command_request(rcp_byte_bus_id_t byte_bus_id, * the ACF_ABB fixed header or its declared payload length; * RCP_EP_ISELED_ERR_BAD_MSG_TYPE if b is not an ACF_ABB message; * RCP_EP_ISELED_ERR_WRONG_BUS if its byte_bus_id != expected_bus_id; - * RCP_EP_ISELED_ERR_WRONG_OP if its op is not RCP_ACF_OP_WRITE. On + * RCP_EP_ISELED_ERR_WRONG_OP if its op is not RCP_ACF_OP_WRITE; + * RCP_EP_ISELED_ERR_BAD_EVT if its evt[2:0] is not 0b000 + * (rcp_acf_evt_row2_is_plain(), TC18 §13.5 Table 30 -- the caller shall + * respond with error code UNSUPPORTED_CMD). On * RCP_EP_ISELED_OK, *out_transaction_num is populated, and *out_tx_data / * *out_tx_len are set to a *borrowed* view into b (not copied) of the raw * plain content. */ diff --git a/include/rcp/ep_mdio.h b/include/rcp/ep_mdio.h index 6028db2..be8c22b 100644 --- a/include/rcp/ep_mdio.h +++ b/include/rcp/ep_mdio.h @@ -357,6 +357,11 @@ typedef enum { RCP_EP_MDIO_ERR_BAD_ADDR = 5, RCP_EP_MDIO_ERR_BAD_WORD_COUNT = 6, RCP_EP_MDIO_ERR_ALLOC = 7, + /* evt[2:0] is not 0b000, TC18 §13.5 Table 30's only legal value for a + * plain (non-configuration) request in MDIO's endpoint-type row -- + * caller shall respond with error code UNSUPPORTED_CMD (see + * rcp_acf_evt_row2_is_plain()). */ + RCP_EP_MDIO_ERR_BAD_EVT = 8, } rcp_ep_mdio_errc_t; /* Human-readable message for an rcp_ep_mdio_errc_t value. Never returns NULL. */ @@ -382,7 +387,10 @@ rcp_bytes_t rcp_ep_mdio_encode_read_request(rcp_byte_bus_id_t byte_bus_id, * RCP_EP_MDIO_ERR_WRONG_OP if its op is not RCP_ACF_OP_READ; * RCP_EP_MDIO_ERR_BAD_ADDR if the decoded address fails * rcp_ep_mdio_addr_valid(); RCP_EP_MDIO_ERR_BAD_WORD_COUNT if the decoded - * word_count is 0 or exceeds RCP_EP_MDIO_MAX_BURST_WORDS. On + * word_count is 0 or exceeds RCP_EP_MDIO_MAX_BURST_WORDS; + * RCP_EP_MDIO_ERR_BAD_EVT if its evt[2:0] is not 0b000 + * (rcp_acf_evt_row2_is_plain(), TC18 §13.5 Table 30 -- the caller shall + * respond with error code UNSUPPORTED_CMD). On * RCP_EP_MDIO_OK, *out_addr, *out_word_count, and *out_transaction_num * are populated. */ rcp_ep_mdio_errc_t rcp_ep_mdio_decode_read_request(const uint8_t *b, size_t len, @@ -448,7 +456,10 @@ rcp_bytes_t rcp_ep_mdio_encode_write_request(rcp_byte_bus_id_t byte_bus_id, * RCP_EP_MDIO_ERR_BAD_ADDR if the decoded address fails * rcp_ep_mdio_addr_valid(); RCP_EP_MDIO_ERR_BAD_WORD_COUNT if the words * region's own byte length is odd, is 0, or represents more than - * RCP_EP_MDIO_MAX_BURST_WORDS words. On RCP_EP_MDIO_OK, *out_addr and + * RCP_EP_MDIO_MAX_BURST_WORDS words; RCP_EP_MDIO_ERR_BAD_EVT if its + * evt[2:0] is not 0b000 (rcp_acf_evt_row2_is_plain(), TC18 §13.5 + * Table 30 -- the caller shall respond with error code UNSUPPORTED_CMD). + * On RCP_EP_MDIO_OK, *out_addr and * *out_transaction_num are populated, and *out_words_data / * *out_word_count are set to a *borrowed* view into b (not copied) of the * packed word bytes following the address prefix. */ diff --git a/include/rcp/ep_uart.h b/include/rcp/ep_uart.h index 4668b16..5159d94 100644 --- a/include/rcp/ep_uart.h +++ b/include/rcp/ep_uart.h @@ -297,6 +297,11 @@ typedef enum { RCP_EP_UART_ERR_WRONG_OP = 4, RCP_EP_UART_ERR_UNKNOWN_CMD = 5, /* payload-bearing read request -- see the file header */ + /* evt[2:0] is not 0b000, TC18 §13.5 Table 30's only legal value for a + * plain (non-configuration) request in UART's endpoint-type row -- + * caller shall respond with error code UNSUPPORTED_CMD (see + * rcp_acf_evt_row2_is_plain()). */ + RCP_EP_UART_ERR_BAD_EVT = 6, } rcp_ep_uart_errc_t; /* Human-readable message for an rcp_ep_uart_errc_t value. Never returns NULL. */ @@ -319,7 +324,10 @@ rcp_bytes_t rcp_ep_uart_encode_write_request(rcp_byte_bus_id_t byte_bus_id, * fixed header or its declared payload length; RCP_EP_UART_ERR_BAD_MSG_TYPE * if b is not an ACF_ABB message; RCP_EP_UART_ERR_WRONG_BUS if its * byte_bus_id != expected_bus_id; RCP_EP_UART_ERR_WRONG_OP if its op is not - * RCP_ACF_OP_WRITE. On RCP_EP_UART_OK, *out_transaction_num is populated, + * RCP_ACF_OP_WRITE; RCP_EP_UART_ERR_BAD_EVT if its evt[2:0] is not 0b000 + * (rcp_acf_evt_row2_is_plain(), TC18 §13.5 Table 30 -- the caller shall + * respond with error code UNSUPPORTED_CMD). On RCP_EP_UART_OK, + * *out_transaction_num is populated, * and *out_tx_data / *out_tx_len are set to a *borrowed* view into b (not * copied -- see the file header) of the raw outgoing payload. */ rcp_ep_uart_errc_t rcp_ep_uart_decode_write_request(const uint8_t *b, size_t len, @@ -376,7 +384,10 @@ rcp_bytes_t rcp_ep_uart_encode_read_request(rcp_byte_bus_id_t byte_bus_id, uint8 * RCP_EP_UART_ERR_WRONG_BUS if its byte_bus_id != expected_bus_id; * RCP_EP_UART_ERR_WRONG_OP if its op is not RCP_ACF_OP_READ; * RCP_EP_UART_ERR_UNKNOWN_CMD if it carries any payload at all -- the - * deliberate asymmetry documented in the file header. On RCP_EP_UART_OK, + * deliberate asymmetry documented in the file header; RCP_EP_UART_ERR_BAD_EVT + * if its evt[2:0] is not 0b000 (rcp_acf_evt_row2_is_plain(), TC18 §13.5 + * Table 30 -- the caller shall respond with error code UNSUPPORTED_CMD). + * On RCP_EP_UART_OK, * *out_read_size and *out_transaction_num are populated. */ rcp_ep_uart_errc_t rcp_ep_uart_decode_read_request(const uint8_t *b, size_t len, rcp_byte_bus_id_t expected_bus_id, diff --git a/include/rcp/version.h b/include/rcp/version.h index a1cbe27..9bd5467 100644 --- a/include/rcp/version.h +++ b/include/rcp/version.h @@ -6,6 +6,6 @@ #ifndef RCP_VERSION_H #define RCP_VERSION_H -#define RCP_VERSION "0.107.0" +#define RCP_VERSION "0.109.0" #endif /* RCP_VERSION_H */ diff --git a/src/ep_can.c b/src/ep_can.c index 39efa41..3432a0c 100644 --- a/src/ep_can.c +++ b/src/ep_can.c @@ -21,7 +21,7 @@ static uint32_t get_u32(const uint8_t *p) (uint32_t)p[3]; } -/* ── FrameFormat selection (evt[2:0]) ────────────────────────────────────── */ +/* ── FrameFormat selection (payload leading quadlet, TC18 Table 54) ──────── */ //cfusa:req REQ-CANEP-001 bool rcp_ep_can_frame_format_valid(uint8_t v) @@ -191,22 +191,29 @@ const char *rcp_ep_can_strerror(rcp_ep_can_errc_t e) case RCP_EP_CAN_ERR_BAD_MSG_TYPE: return "rcp/ep_can: unexpected ACF message type"; case RCP_EP_CAN_ERR_WRONG_BUS: return "rcp/ep_can: wrong byte_bus_id"; case RCP_EP_CAN_ERR_WRONG_OP: return "rcp/ep_can: wrong ACF op"; - case RCP_EP_CAN_ERR_BAD_FRAME_FORMAT: return "rcp/ep_can: invalid frame format selector"; + case RCP_EP_CAN_ERR_BAD_FRAME_FORMAT: return "rcp/ep_can: invalid frame format"; + case RCP_EP_CAN_ERR_BAD_EVT: return "rcp/ep_can: evt[2:0] is not 0b000"; + case RCP_EP_CAN_ERR_BAD_ARBITRATION_ID: return "rcp/ep_can: arbitration_id out of range for frame format"; default: return "rcp/ep_can: unknown error"; } } -/* ── Wire layout helpers (this module's own prefix-then-data choice) ────── */ +/* ── Wire layout helpers (TC18 §13.7.11.3 Figure 39) ─────────────────────── */ static size_t prefix_len_for(rcp_ep_can_frame_format_t format) { return rcp_ep_can_frame_format_is_xl(format) ? (size_t)10u : (size_t)4u; } +/* Figure 39's leading quadlet: frame_format in the top 3 bits, the + * (right-aligned, for an 11-bit base id) arbitration_id in the bottom 29 + * bits. */ static void write_prefix(uint8_t *p, rcp_ep_can_frame_format_t format, uint32_t arbitration_id, const rcp_ep_can_xl_header_t *xl_header) { - put_u32(p, arbitration_id); + uint32_t combined = ((uint32_t)format << 29) | (arbitration_id & 0x1FFFFFFFu); + + put_u32(p, combined); if (rcp_ep_can_frame_format_is_xl(format)) { p[4] = xl_header->sdt; p[5] = xl_header->vcid; @@ -214,10 +221,18 @@ static void write_prefix(uint8_t *p, rcp_ep_can_frame_format_t format, uint32_t } } +/* Reads just the leading quadlet's top 3 bits -- the only field a caller + * needs before it knows which format (and therefore which full prefix + * length) it is looking at. */ +static rcp_ep_can_frame_format_t read_frame_format(const uint8_t *p) +{ + return (rcp_ep_can_frame_format_t)((get_u32(p) >> 29) & 0x7u); +} + static void read_prefix(const uint8_t *p, rcp_ep_can_frame_format_t format, uint32_t *out_arbitration_id, rcp_ep_can_xl_header_t *out_xl_header) { - *out_arbitration_id = get_u32(p); + *out_arbitration_id = get_u32(p) & 0x1FFFFFFFu; if (rcp_ep_can_frame_format_is_xl(format)) { out_xl_header->sdt = p[4]; out_xl_header->vcid = p[5]; @@ -290,7 +305,7 @@ rcp_bytes_t rcp_ep_can_encode_frame_request(rcp_byte_bus_id_t byte_bus_id, hdr.byte_bus_id = byte_bus_id; hdr.op = RCP_ACF_OP_WRITE; - hdr.evt = (uint8_t)frame_format; + hdr.evt = 0; /* TC18 Table 30: plain request in CAN's Row-2 -- frame_format now lives in the payload, see the file header */ hdr.transaction_num = transaction_num; frame = rcp_acf_encode_abb(&hdr, payload, payload_len); @@ -322,14 +337,18 @@ rcp_ep_can_errc_t rcp_ep_can_decode_frame_request(const uint8_t *b, size_t len, if (hdr.byte_bus_id != expected_bus_id) return RCP_EP_CAN_ERR_WRONG_BUS; if (hdr.op != RCP_ACF_OP_WRITE) return RCP_EP_CAN_ERR_WRONG_OP; + if (!rcp_acf_evt_row2_is_plain(hdr.evt)) return RCP_EP_CAN_ERR_BAD_EVT; - if (!rcp_ep_can_frame_format_valid(hdr.evt & 0x07u)) return RCP_EP_CAN_ERR_BAD_FRAME_FORMAT; - frame_format = (rcp_ep_can_frame_format_t)(hdr.evt & 0x07u); + if (payload_len < 4u) return RCP_EP_CAN_ERR_SHORT_FRAME; + frame_format = read_frame_format(payload); + if (!rcp_ep_can_frame_format_valid((uint8_t)frame_format)) return RCP_EP_CAN_ERR_BAD_FRAME_FORMAT; prefix_len = prefix_len_for(frame_format); if (payload_len < prefix_len) return RCP_EP_CAN_ERR_SHORT_FRAME; read_prefix(payload, frame_format, out_arbitration_id, out_xl_header); + if (!rcp_ep_can_arbitration_id_valid(frame_format, *out_arbitration_id)) + return RCP_EP_CAN_ERR_BAD_ARBITRATION_ID; *out_frame_format = frame_format; *out_tx_data = &payload[prefix_len]; @@ -365,7 +384,7 @@ rcp_bytes_t rcp_ep_can_encode_frame_response(rcp_byte_bus_id_t byte_bus_id, hdr.info.byte_bus_id = byte_bus_id; hdr.info.op = RCP_ACF_OP_READ; hdr.info.rsp = 1; /* TC18.txt:1885 -- rsp=1b identifies a response */ - hdr.info.evt = (uint8_t)frame_format; + hdr.info.evt = 0; /* TC18 Table 30: plain response, see the file header */ hdr.info.mtv = RCP_ACF_MTV_VALID; hdr.info.transaction_num = transaction_num; hdr.message_timestamp = timestamp; @@ -377,7 +396,7 @@ rcp_bytes_t rcp_ep_can_encode_frame_response(rcp_byte_bus_id_t byte_bus_id, hdr.byte_bus_id = byte_bus_id; hdr.op = RCP_ACF_OP_READ; hdr.rsp = 1; /* TC18.txt:1885 -- rsp=1b identifies a response */ - hdr.evt = (uint8_t)frame_format; + hdr.evt = 0; /* TC18 Table 30: plain request in CAN's Row-2 -- frame_format now lives in the payload, see the file header */ hdr.transaction_num = transaction_num; frame = rcp_acf_encode_abb(&hdr, payload, payload_len); @@ -439,14 +458,18 @@ rcp_ep_can_errc_t rcp_ep_can_decode_frame_response(const uint8_t *b, size_t len, } if (bus_id != expected_bus_id) return RCP_EP_CAN_ERR_WRONG_BUS; + if (!rcp_acf_evt_row2_is_plain(evt)) return RCP_EP_CAN_ERR_BAD_EVT; - if (!rcp_ep_can_frame_format_valid(evt & 0x07u)) return RCP_EP_CAN_ERR_BAD_FRAME_FORMAT; - frame_format = (rcp_ep_can_frame_format_t)(evt & 0x07u); + if (payload_len < 4u) return RCP_EP_CAN_ERR_SHORT_FRAME; + frame_format = read_frame_format(payload); + if (!rcp_ep_can_frame_format_valid((uint8_t)frame_format)) return RCP_EP_CAN_ERR_BAD_FRAME_FORMAT; prefix_len = prefix_len_for(frame_format); if (payload_len < prefix_len) return RCP_EP_CAN_ERR_SHORT_FRAME; read_prefix(payload, frame_format, out_arbitration_id, out_xl_header); + if (!rcp_ep_can_arbitration_id_valid(frame_format, *out_arbitration_id)) + return RCP_EP_CAN_ERR_BAD_ARBITRATION_ID; *out_frame_format = frame_format; *out_rx_data = &payload[prefix_len]; @@ -521,7 +544,7 @@ size_t rcp_ep_can_encode_frame_response_fragmented(rcp_byte_bus_id_t byte_bus_id hdr.info.byte_bus_id = byte_bus_id; hdr.info.op = RCP_ACF_OP_READ; hdr.info.rsp = 1; /* TC18.txt:1885 -- rsp=1b identifies a response */ - hdr.info.evt = (uint8_t)frame_format; + hdr.info.evt = 0; /* TC18 Table 30: plain response, see the file header */ hdr.info.mtv = RCP_ACF_MTV_VALID; hdr.info.transaction_num = transaction_num; hdr.info.ms = segs[i].ms ? 1u : 0u; @@ -535,7 +558,7 @@ size_t rcp_ep_can_encode_frame_response_fragmented(rcp_byte_bus_id_t byte_bus_id hdr.byte_bus_id = byte_bus_id; hdr.op = RCP_ACF_OP_READ; hdr.rsp = 1; /* TC18.txt:1885 -- rsp=1b identifies a response */ - hdr.evt = (uint8_t)frame_format; + hdr.evt = 0; /* TC18 Table 30: plain response, see the file header */ hdr.transaction_num = transaction_num; hdr.ms = segs[i].ms ? 1u : 0u; hdr.read_size_or_segment_num = segs[i].ms ? segs[i].segment_num : 0u; @@ -564,7 +587,6 @@ size_t rcp_ep_can_encode_frame_response_fragmented(rcp_byte_bus_id_t byte_bus_id //cfusa:req REQ-CANEP-026 rcp_ep_can_errc_t rcp_ep_can_decode_frame_response_fragment(const uint8_t *b, size_t len, rcp_byte_bus_id_t expected_bus_id, - rcp_ep_can_frame_format_t *out_frame_format, bool *out_ms, uint8_t *out_segment_num, const uint8_t **out_payload, @@ -592,7 +614,6 @@ rcp_ep_can_errc_t rcp_ep_can_decode_frame_response_fragment(const uint8_t *b, si uint8_t transaction_num; bool timed; uint64_t timestamp; - rcp_ep_can_frame_format_t frame_format; if (rcp_acf_peek_msg_type(b, len, &msg_type) != RCP_ACF_OK) return RCP_EP_CAN_ERR_SHORT_FRAME; @@ -623,11 +644,8 @@ rcp_ep_can_errc_t rcp_ep_can_decode_frame_response_fragment(const uint8_t *b, si } if (bus_id != expected_bus_id) return RCP_EP_CAN_ERR_WRONG_BUS; + if (!rcp_acf_evt_row2_is_plain(evt)) return RCP_EP_CAN_ERR_BAD_EVT; - if (!rcp_ep_can_frame_format_valid(evt & 0x07u)) return RCP_EP_CAN_ERR_BAD_FRAME_FORMAT; - frame_format = (rcp_ep_can_frame_format_t)(evt & 0x07u); - - *out_frame_format = frame_format; *out_ms = (ms != 0u); *out_segment_num = segment_num; *out_payload = payload; @@ -641,22 +659,28 @@ rcp_ep_can_errc_t rcp_ep_can_decode_frame_response_fragment(const uint8_t *b, si //cfusa:req REQ-CANEP-027 rcp_ep_can_errc_t rcp_ep_can_decode_reassembled_frame_response(const uint8_t *reassembled, size_t reassembled_len, - rcp_ep_can_frame_format_t frame_format, + rcp_ep_can_frame_format_t *out_frame_format, uint32_t *out_arbitration_id, rcp_ep_can_xl_header_t *out_xl_header, const uint8_t **out_rx_data, size_t *out_rx_len) { - size_t prefix_len; + rcp_ep_can_frame_format_t frame_format; + size_t prefix_len; + if (reassembled_len < 4u) return RCP_EP_CAN_ERR_SHORT_FRAME; + frame_format = read_frame_format(reassembled); if (!rcp_ep_can_frame_format_valid((uint8_t)frame_format)) return RCP_EP_CAN_ERR_BAD_FRAME_FORMAT; prefix_len = prefix_len_for(frame_format); if (reassembled_len < prefix_len) return RCP_EP_CAN_ERR_SHORT_FRAME; read_prefix(reassembled, frame_format, out_arbitration_id, out_xl_header); + if (!rcp_ep_can_arbitration_id_valid(frame_format, *out_arbitration_id)) + return RCP_EP_CAN_ERR_BAD_ARBITRATION_ID; - *out_rx_data = &reassembled[prefix_len]; - *out_rx_len = reassembled_len - prefix_len; + *out_frame_format = frame_format; + *out_rx_data = &reassembled[prefix_len]; + *out_rx_len = reassembled_len - prefix_len; return RCP_EP_CAN_OK; } diff --git a/src/ep_i2c.c b/src/ep_i2c.c index 8ae9dc4..086f122 100644 --- a/src/ep_i2c.c +++ b/src/ep_i2c.c @@ -81,6 +81,7 @@ const char *rcp_ep_i2c_strerror(rcp_ep_i2c_errc_t e) case RCP_EP_I2C_ERR_BAD_MSG_TYPE: return "rcp/ep_i2c: unexpected ACF message type"; case RCP_EP_I2C_ERR_WRONG_BUS: return "rcp/ep_i2c: wrong byte_bus_id"; case RCP_EP_I2C_ERR_WRONG_OP: return "rcp/ep_i2c: wrong ACF op"; + case RCP_EP_I2C_ERR_BAD_EVT: return "rcp/ep_i2c: evt[2:0] is not 0b000"; default: return "rcp/ep_i2c: unknown error"; } } @@ -136,6 +137,7 @@ rcp_ep_i2c_errc_t rcp_ep_i2c_decode_transfer_request(const uint8_t *b, size_t le if (acf_rc != RCP_ACF_OK) return RCP_EP_I2C_ERR_BAD_MSG_TYPE; if (hdr.byte_bus_id != expected_bus_id) return RCP_EP_I2C_ERR_WRONG_BUS; + if (!rcp_acf_evt_row2_is_plain(hdr.evt)) return RCP_EP_I2C_ERR_BAD_EVT; /* Both op senses are valid on an I2C transfer request -- see the file * header. The direction is reported, not rejected. */ diff --git a/src/ep_iseled.c b/src/ep_iseled.c index 4b473fe..905f1f4 100644 --- a/src/ep_iseled.c +++ b/src/ep_iseled.c @@ -200,6 +200,7 @@ const char *rcp_ep_iseled_strerror(rcp_ep_iseled_errc_t e) case RCP_EP_ISELED_ERR_ODD_SYMBOL_COUNT: return "rcp/ep_iseled: odd symbol count -- every octet frames to two symbols"; case RCP_EP_ISELED_ERR_ALLOC: return "rcp/ep_iseled: allocation failure"; + case RCP_EP_ISELED_ERR_BAD_EVT: return "rcp/ep_iseled: evt[2:0] is not 0b000"; default: return "rcp/ep_iseled: unknown error"; } } @@ -296,6 +297,7 @@ rcp_ep_iseled_errc_t rcp_ep_iseled_decode_command_request(const uint8_t *b, size if (hdr.byte_bus_id != expected_bus_id) return RCP_EP_ISELED_ERR_WRONG_BUS; if (hdr.op != RCP_ACF_OP_WRITE) return RCP_EP_ISELED_ERR_WRONG_OP; + if (!rcp_acf_evt_row2_is_plain(hdr.evt)) return RCP_EP_ISELED_ERR_BAD_EVT; /* payload is round-tripped verbatim, byte for byte -- see the file * header; this endpoint's own bit-framing happens only when it diff --git a/src/ep_mdio.c b/src/ep_mdio.c index fb7be0e..dbb1de6 100644 --- a/src/ep_mdio.c +++ b/src/ep_mdio.c @@ -134,6 +134,7 @@ const char *rcp_ep_mdio_strerror(rcp_ep_mdio_errc_t e) case RCP_EP_MDIO_ERR_BAD_ADDR: return "rcp/ep_mdio: invalid MDIO address"; case RCP_EP_MDIO_ERR_BAD_WORD_COUNT: return "rcp/ep_mdio: invalid register-word count"; case RCP_EP_MDIO_ERR_ALLOC: return "rcp/ep_mdio: allocation failure"; + case RCP_EP_MDIO_ERR_BAD_EVT: return "rcp/ep_mdio: evt[2:0] is not 0b000"; default: return "rcp/ep_mdio: unknown error"; } } @@ -204,6 +205,7 @@ rcp_ep_mdio_errc_t rcp_ep_mdio_decode_read_request(const uint8_t *b, size_t len, if (hdr.byte_bus_id != expected_bus_id) return RCP_EP_MDIO_ERR_WRONG_BUS; if (hdr.op != RCP_ACF_OP_READ) return RCP_EP_MDIO_ERR_WRONG_OP; + if (!rcp_acf_evt_row2_is_plain(hdr.evt)) return RCP_EP_MDIO_ERR_BAD_EVT; if (payload_len < READ_REQUEST_PAYLOAD_LEN) return RCP_EP_MDIO_ERR_SHORT_FRAME; addr = get_addr_prefix(payload); @@ -378,6 +380,7 @@ rcp_ep_mdio_errc_t rcp_ep_mdio_decode_write_request(const uint8_t *b, size_t len if (hdr.byte_bus_id != expected_bus_id) return RCP_EP_MDIO_ERR_WRONG_BUS; if (hdr.op != RCP_ACF_OP_WRITE) return RCP_EP_MDIO_ERR_WRONG_OP; + if (!rcp_acf_evt_row2_is_plain(hdr.evt)) return RCP_EP_MDIO_ERR_BAD_EVT; if (payload_len < ADDR_PREFIX_LEN) return RCP_EP_MDIO_ERR_SHORT_FRAME; addr = get_addr_prefix(payload); diff --git a/src/ep_uart.c b/src/ep_uart.c index 33a44cf..9b255b4 100644 --- a/src/ep_uart.c +++ b/src/ep_uart.c @@ -117,6 +117,7 @@ const char *rcp_ep_uart_strerror(rcp_ep_uart_errc_t e) case RCP_EP_UART_ERR_WRONG_BUS: return "rcp/ep_uart: wrong byte_bus_id"; case RCP_EP_UART_ERR_WRONG_OP: return "rcp/ep_uart: wrong ACF op"; case RCP_EP_UART_ERR_UNKNOWN_CMD: return "rcp/ep_uart: unrecognized command (payload-bearing read request)"; + case RCP_EP_UART_ERR_BAD_EVT: return "rcp/ep_uart: evt[2:0] is not 0b000"; default: return "rcp/ep_uart: unknown error"; } } @@ -157,6 +158,7 @@ rcp_ep_uart_errc_t rcp_ep_uart_decode_write_request(const uint8_t *b, size_t len if (hdr.byte_bus_id != expected_bus_id) return RCP_EP_UART_ERR_WRONG_BUS; if (hdr.op != RCP_ACF_OP_WRITE) return RCP_EP_UART_ERR_WRONG_OP; + if (!rcp_acf_evt_row2_is_plain(hdr.evt)) return RCP_EP_UART_ERR_BAD_EVT; *out_tx_data = payload; *out_tx_len = payload_len; @@ -281,6 +283,7 @@ rcp_ep_uart_errc_t rcp_ep_uart_decode_read_request(const uint8_t *b, size_t len, if (hdr.byte_bus_id != expected_bus_id) return RCP_EP_UART_ERR_WRONG_BUS; if (hdr.op != RCP_ACF_OP_READ) return RCP_EP_UART_ERR_WRONG_OP; + if (!rcp_acf_evt_row2_is_plain(hdr.evt)) return RCP_EP_UART_ERR_BAD_EVT; /* A UART read request has nothing meaningful a payload could carry * (read_size already rides the ACF header itself) -- a payload-bearing diff --git a/tests/test_ep_can.c b/tests/test_ep_can.c index 350463e..56e5a31 100644 --- a/tests/test_ep_can.c +++ b/tests/test_ep_can.c @@ -337,6 +337,7 @@ static void test_strerror_never_null_and_distinct(void) RCP_EP_CAN_OK, RCP_EP_CAN_ERR_SHORT_FRAME, RCP_EP_CAN_ERR_BAD_MSG_TYPE, RCP_EP_CAN_ERR_WRONG_BUS, RCP_EP_CAN_ERR_WRONG_OP, RCP_EP_CAN_ERR_BAD_FRAME_FORMAT, + RCP_EP_CAN_ERR_BAD_EVT, RCP_EP_CAN_ERR_BAD_ARBITRATION_ID, }; size_t i, j; @@ -556,7 +557,11 @@ static void test_frame_request_decode_rejects_short_frame(void) &out_tx, &out_tx_len, &txn)); } -static void test_frame_request_decode_rejects_bad_frame_format(void) +/* TC18 §13.7.11.3 Figure 39: FrameFormat is the payload's leading + * quadlet's top 3 bits, not evt[2:0] -- a request with evt[2:0] = 7 + * (reserved in CAN's Table 30 Row-2) is now rejected for its evt value, + * before frame_format is even inspected. */ +static void test_frame_request_decode_rejects_bad_evt(void) { rcp_acf_byte_message_info_t hdr = {0}; rcp_bytes_t frame; @@ -570,7 +575,33 @@ static void test_frame_request_decode_rejects_bad_frame_format(void) hdr.byte_bus_id = 4; hdr.op = RCP_ACF_OP_WRITE; - hdr.evt = 7; /* reserved -- no defined frame format */ + hdr.evt = 7; /* reserved in CAN's Table 30 Row-2 */ + frame = rcp_acf_encode_abb(&hdr, payload, sizeof(payload)); + + TEST_ASSERT_EQUAL(RCP_EP_CAN_ERR_BAD_EVT, + rcp_ep_can_decode_frame_request(frame.data, frame.len, 4, &format, &id, &xl_hdr, &out_tx, + &out_tx_len, &txn)); + + rcp_bytes_free(&frame); +} + +/* Payload's leading quadlet's top 3 bits are 0b111 (7), Table 54's own + * second reserved code -- evt[2:0] is a plain 0b000, so this exercises + * frame_format validation specifically, not the evt check above. */ +static void test_frame_request_decode_rejects_bad_frame_format(void) +{ + rcp_acf_byte_message_info_t hdr = {0}; + rcp_bytes_t frame; + rcp_ep_can_frame_format_t format; + uint32_t id; + rcp_ep_can_xl_header_t xl_hdr; + const uint8_t *out_tx; + size_t out_tx_len; + uint8_t txn; + uint8_t payload[4] = {0xE0, 0, 0, 0}; /* top 3 bits = 111b = 7 */ + + hdr.byte_bus_id = 4; + hdr.op = RCP_ACF_OP_WRITE; frame = rcp_acf_encode_abb(&hdr, payload, sizeof(payload)); TEST_ASSERT_EQUAL(RCP_EP_CAN_ERR_BAD_FRAME_FORMAT, @@ -580,11 +611,40 @@ static void test_frame_request_decode_rejects_bad_frame_format(void) rcp_bytes_free(&frame); } +/* An 11-bit-width format (CBFF) whose leading quadlet carries a + * arbitration_id value outside the low 11 bits -- TC18 §13.7.11.3's own + * "shall be right aligned" rule, violated here on purpose. */ +static void test_frame_request_decode_rejects_bad_arbitration_id(void) +{ + rcp_acf_byte_message_info_t hdr = {0}; + rcp_bytes_t frame; + rcp_ep_can_frame_format_t format; + uint32_t id; + rcp_ep_can_xl_header_t xl_hdr; + const uint8_t *out_tx; + size_t out_tx_len; + uint8_t txn; + /* frame_format = CBFF (000b) in the top 3 bits; bit 3 (the first bit + * of the 29-bit id field's own upper, must-be-zero-for-base-11 range) + * set to 1 -- id = 0x08000000, far outside CBFF's 0x7FF ceiling. */ + uint8_t payload[4] = {0x08, 0, 0, 0}; + + hdr.byte_bus_id = 4; + hdr.op = RCP_ACF_OP_WRITE; + frame = rcp_acf_encode_abb(&hdr, payload, sizeof(payload)); + + TEST_ASSERT_EQUAL(RCP_EP_CAN_ERR_BAD_ARBITRATION_ID, + rcp_ep_can_decode_frame_request(frame.data, frame.len, 4, &format, &id, &xl_hdr, &out_tx, + &out_tx_len, &txn)); + + rcp_bytes_free(&frame); +} + +/* frame_format = XL_NEW_PL (leading quadlet's top 3 bits = 101b = 5) but + * payload is only the 4-byte leading quadlet, short of the 6-byte + * SDT/VCID/AF prefix an XL frame requires. */ static void test_frame_request_decode_rejects_short_xl_prefix(void) { - /* frame_format = XL_NEW_PL (evt=5) but payload is only the 4-byte - * arbitration-id prefix, short of the 6-byte SDT/VCID/AF prefix an XL - * frame requires. */ rcp_acf_byte_message_info_t hdr = {0}; rcp_bytes_t frame; rcp_ep_can_frame_format_t format; @@ -593,11 +653,11 @@ static void test_frame_request_decode_rejects_short_xl_prefix(void) const uint8_t *out_tx; size_t out_tx_len; uint8_t txn; - uint8_t payload[4] = {0}; + /* top 3 bits = 101b (XL_NEW_PL = 5), remaining 29 bits (id) = 0. */ + uint8_t payload[4] = {(uint8_t)(RCP_EP_CAN_FRAME_XL_NEW_PL << 5), 0, 0, 0}; hdr.byte_bus_id = 4; hdr.op = RCP_ACF_OP_WRITE; - hdr.evt = (uint8_t)RCP_EP_CAN_FRAME_XL_NEW_PL; frame = rcp_acf_encode_abb(&hdr, payload, sizeof(payload)); TEST_ASSERT_EQUAL(RCP_EP_CAN_ERR_SHORT_FRAME, @@ -607,6 +667,28 @@ static void test_frame_request_decode_rejects_short_xl_prefix(void) rcp_bytes_free(&frame); } +/* Golden vector, independently hand-computed against TC18 §13.7.11.3 + * Figure 39 -- not re-derived from rcp_ep_can_encode_frame_request() + * itself, which is what this test exists to check. frame_format = CEFF + * (1) in the top 3 bits (0b001), arbitration_id = 0x1ABCDEF (extended-29, + * fits in the low 29 bits) -- combined leading quadlet = + * (1 << 29) | 0x1ABCDEF = 0x21ABCDEF. */ +static void test_frame_request_golden_leading_quadlet_bit_packing(void) +{ + rcp_bytes_t frame = rcp_ep_can_encode_frame_request( + 4, RCP_EP_CAN_FRAME_CEFF, 0x01ABCDEFu, NULL, NULL, 0, 3); + const uint8_t *payload = frame.data + RCP_ACF_ABB_HEADER_LEN; + + TEST_ASSERT_NOT_NULL(frame.data); + TEST_ASSERT_EQUAL_UINT(RCP_ACF_ABB_HEADER_LEN + 4, frame.len); + TEST_ASSERT_EQUAL_HEX8(0x21u, payload[0]); + TEST_ASSERT_EQUAL_HEX8(0xABu, payload[1]); + TEST_ASSERT_EQUAL_HEX8(0xCDu, payload[2]); + TEST_ASSERT_EQUAL_HEX8(0xEFu, payload[3]); + + rcp_bytes_free(&frame); +} + /* ── Response round trip ───────────────────────────────────────────────────── */ static void test_frame_response_round_trip_untimed(void) @@ -788,7 +870,6 @@ static void test_fragment_worst_case_can_xl_response_round_trip(void) rcp_fragment_reassembler_init(&reasm, combined_len); for (i = 0; i < count; i++) { - rcp_ep_can_frame_format_t fmt; bool ms; uint8_t segnum; const uint8_t *payload; @@ -799,10 +880,9 @@ static void test_fragment_worst_case_can_xl_response_round_trip(void) rcp_fragment_reasm_result_t rc; TEST_ASSERT_EQUAL(RCP_EP_CAN_OK, - rcp_ep_can_decode_frame_response_fragment(frames[i].data, frames[i].len, 7, &fmt, + rcp_ep_can_decode_frame_response_fragment(frames[i].data, frames[i].len, 7, &ms, &segnum, &payload, &payload_len, &timed, &ts, &txn)); - TEST_ASSERT_EQUAL(RCP_EP_CAN_FRAME_XL_NEW_PL, fmt); TEST_ASSERT_EQUAL_UINT8(55, txn); TEST_ASSERT_FALSE(timed); @@ -822,15 +902,17 @@ static void test_fragment_worst_case_can_xl_response_round_trip(void) TEST_ASSERT_EQUAL_UINT(combined_len, reassembled_len); { + rcp_ep_can_frame_format_t out_format; uint32_t out_id = 0; const uint8_t *out_rx = NULL; size_t out_rx_len = 0; TEST_ASSERT_EQUAL(RCP_EP_CAN_OK, rcp_ep_can_decode_reassembled_frame_response( - reassembled, reassembled_len, RCP_EP_CAN_FRAME_XL_NEW_PL, &out_id, + reassembled, reassembled_len, &out_format, &out_id, &xl_hdr_out, &out_rx, &out_rx_len)); + TEST_ASSERT_EQUAL(RCP_EP_CAN_FRAME_XL_NEW_PL, out_format); TEST_ASSERT_EQUAL_UINT32(0x123, out_id); TEST_ASSERT_EQUAL_UINT8(0x5, xl_hdr_out.sdt); TEST_ASSERT_EQUAL_UINT8(0x9, xl_hdr_out.vcid); @@ -884,7 +966,6 @@ static void test_fragment_decode_fragment_rejects_wrong_bus(void) rcp_bytes_t frames[1]; size_t count = rcp_ep_can_encode_frame_response_fragmented( 4, RCP_EP_CAN_FRAME_CBFF, 0x42, NULL, rx, sizeof(rx), 9, false, 0, 1024, frames); - rcp_ep_can_frame_format_t fmt; bool ms; uint8_t segnum; const uint8_t *payload; @@ -895,7 +976,7 @@ static void test_fragment_decode_fragment_rejects_wrong_bus(void) TEST_ASSERT_EQUAL_UINT(1, count); TEST_ASSERT_EQUAL(RCP_EP_CAN_ERR_WRONG_BUS, - rcp_ep_can_decode_frame_response_fragment(frames[0].data, frames[0].len, 99, &fmt, &ms, + rcp_ep_can_decode_frame_response_fragment(frames[0].data, frames[0].len, 99, &ms, &segnum, &payload, &payload_len, &timed, &ts, &txn)); @@ -904,15 +985,16 @@ static void test_fragment_decode_fragment_rejects_wrong_bus(void) static void test_reassembled_decode_rejects_short_frame(void) { - uint8_t too_short[3] = {0}; - uint32_t id = 0; - rcp_ep_can_xl_header_t xl_hdr; - const uint8_t *out_rx = NULL; - size_t out_rx_len = 0; + uint8_t too_short[3] = {0}; + rcp_ep_can_frame_format_t format; + uint32_t id = 0; + rcp_ep_can_xl_header_t xl_hdr; + const uint8_t *out_rx = NULL; + size_t out_rx_len = 0; TEST_ASSERT_EQUAL(RCP_EP_CAN_ERR_SHORT_FRAME, rcp_ep_can_decode_reassembled_frame_response(too_short, sizeof(too_short), - RCP_EP_CAN_FRAME_CBFF, &id, &xl_hdr, + &format, &id, &xl_hdr, &out_rx, &out_rx_len)); } @@ -961,7 +1043,10 @@ int main(void) RUN_TEST(test_frame_request_decode_rejects_wrong_op); RUN_TEST(test_frame_request_decode_rejects_bad_msg_type); RUN_TEST(test_frame_request_decode_rejects_short_frame); + RUN_TEST(test_frame_request_decode_rejects_bad_evt); RUN_TEST(test_frame_request_decode_rejects_bad_frame_format); + RUN_TEST(test_frame_request_decode_rejects_bad_arbitration_id); + RUN_TEST(test_frame_request_golden_leading_quadlet_bit_packing); RUN_TEST(test_frame_request_decode_rejects_short_xl_prefix); RUN_TEST(test_frame_response_round_trip_untimed); diff --git a/tests/test_ep_i2c.c b/tests/test_ep_i2c.c index 41a060f..28896b7 100644 --- a/tests/test_ep_i2c.c +++ b/tests/test_ep_i2c.c @@ -375,6 +375,31 @@ static void test_transfer_request_rejects_wrong_bus(void) rcp_bytes_free(&frame); } +/* TC18 §13.5 Table 30: evt[2:0] = 000b is the only legal value for a + * plain I2C transfer request; every other value (here, 0b011, a reserved + * value in I2C's endpoint-type row) shall be rejected. */ +static void test_transfer_request_rejects_nonzero_evt(void) +{ + rcp_acf_byte_message_info_t hdr = {0}; + rcp_bytes_t frame; + rcp_ep_i2c_dir_t dir; + const uint8_t *out_tx; + size_t out_tx_len; + uint16_t read_size; + uint8_t txn; + + hdr.byte_bus_id = 4; + hdr.op = RCP_ACF_OP_WRITE; + hdr.evt = 0x3; + frame = rcp_acf_encode_abb(&hdr, NULL, 0); + + TEST_ASSERT_EQUAL(RCP_EP_I2C_ERR_BAD_EVT, + rcp_ep_i2c_decode_transfer_request(frame.data, frame.len, 4, &dir, &out_tx, &out_tx_len, + &read_size, &txn)); + + rcp_bytes_free(&frame); +} + /* The former test_transfer_request_rejects_wrong_op() asserted that a * read-direction (op=0) frame is not an I2C transfer request. It is one -- * see the direction tests above -- so its replacement asserts the @@ -594,6 +619,7 @@ int main(void) RUN_TEST(test_transfer_request_round_trip_read_direction); RUN_TEST(test_transfer_request_round_trip_empty_payload); RUN_TEST(test_transfer_request_rejects_wrong_bus); + RUN_TEST(test_transfer_request_rejects_nonzero_evt); RUN_TEST(test_transfer_request_accepts_hand_built_read_direction_frame); RUN_TEST(test_transfer_request_rejects_bad_msg_type); RUN_TEST(test_transfer_request_rejects_short_frame); diff --git a/tests/test_ep_iseled.c b/tests/test_ep_iseled.c index d710342..e6c7b69 100644 --- a/tests/test_ep_iseled.c +++ b/tests/test_ep_iseled.c @@ -489,6 +489,29 @@ static void test_command_request_rejects_wrong_op(void) rcp_bytes_free(&frame); } +/* TC18 §13.5 Table 30: evt[2:0] = 000b is the only legal value for a + * plain ISELED command request; every other value (here, 0b010, a + * reserved value in ISELED's endpoint-type row) shall be rejected. */ +static void test_command_request_rejects_nonzero_evt(void) +{ + rcp_acf_byte_message_info_t hdr = {0}; + rcp_bytes_t frame; + const uint8_t *out_tx; + size_t out_tx_len; + uint8_t txn; + + hdr.byte_bus_id = 4; + hdr.op = RCP_ACF_OP_WRITE; + hdr.evt = 0x2; + frame = rcp_acf_encode_abb(&hdr, NULL, 0); + + TEST_ASSERT_EQUAL(RCP_EP_ISELED_ERR_BAD_EVT, + rcp_ep_iseled_decode_command_request(frame.data, frame.len, 4, &out_tx, &out_tx_len, + &txn)); + + rcp_bytes_free(&frame); +} + static void test_command_request_rejects_bad_msg_type(void) { rcp_acf_gbb_header_t gbb_hdr = {0}; @@ -645,6 +668,7 @@ int main(void) RUN_TEST(test_command_request_round_trip_empty_payload); RUN_TEST(test_command_request_rejects_wrong_bus); RUN_TEST(test_command_request_rejects_wrong_op); + RUN_TEST(test_command_request_rejects_nonzero_evt); RUN_TEST(test_command_request_rejects_bad_msg_type); RUN_TEST(test_command_request_rejects_short_frame); diff --git a/tests/test_ep_mdio.c b/tests/test_ep_mdio.c index d4913af..4d27193 100644 --- a/tests/test_ep_mdio.c +++ b/tests/test_ep_mdio.c @@ -370,6 +370,28 @@ static void test_read_request_decode_rejects_wrong_op(void) rcp_bytes_free(&frame); } +/* TC18 §13.5 Table 30: evt[2:0] = 000b is the only legal value for a + * plain MDIO read request; every other value (here, 0b100, reserved in + * MDIO's endpoint-type row) shall be rejected. */ +static void test_read_request_decode_rejects_nonzero_evt(void) +{ + rcp_acf_byte_message_info_t hdr = {0}; + rcp_bytes_t frame; + rcp_ep_mdio_addr_t out_addr; + size_t out_word_count; + uint8_t txn; + + hdr.byte_bus_id = 2; + hdr.op = RCP_ACF_OP_READ; + hdr.evt = 0x4; + frame = rcp_acf_encode_abb(&hdr, NULL, 0); + + TEST_ASSERT_EQUAL(RCP_EP_MDIO_ERR_BAD_EVT, rcp_ep_mdio_decode_read_request( + frame.data, frame.len, 2, &out_addr, &out_word_count, &txn)); + + rcp_bytes_free(&frame); +} + static void test_read_request_decode_rejects_bad_msg_type(void) { rcp_acf_gbb_header_t gbb_hdr = {0}; @@ -674,6 +696,29 @@ static void test_write_request_decode_rejects_wrong_op(void) rcp_bytes_free(&frame); } +/* TC18 §13.5 Table 30: evt[2:0] = 000b is the only legal value for a + * plain MDIO write request; every other value (here, 0b001, reserved in + * MDIO's endpoint-type row) shall be rejected. */ +static void test_write_request_decode_rejects_nonzero_evt(void) +{ + rcp_acf_byte_message_info_t hdr = {0}; + rcp_bytes_t frame; + rcp_ep_mdio_addr_t out_addr; + const uint8_t *out_words_data; + size_t out_word_count; + uint8_t txn; + + hdr.byte_bus_id = 2; + hdr.op = RCP_ACF_OP_WRITE; + hdr.evt = 0x1; + frame = rcp_acf_encode_abb(&hdr, NULL, 0); + + TEST_ASSERT_EQUAL(RCP_EP_MDIO_ERR_BAD_EVT, rcp_ep_mdio_decode_write_request( + frame.data, frame.len, 2, &out_addr, &out_words_data, &out_word_count, &txn)); + + rcp_bytes_free(&frame); +} + static void test_write_request_decode_rejects_short_frame(void) { rcp_acf_byte_message_info_t hdr = {0}; @@ -867,6 +912,7 @@ int main(void) RUN_TEST(test_read_request_encode_rejects_word_count_above_max); RUN_TEST(test_read_request_decode_rejects_wrong_bus); RUN_TEST(test_read_request_decode_rejects_wrong_op); + RUN_TEST(test_read_request_decode_rejects_nonzero_evt); RUN_TEST(test_read_request_decode_rejects_bad_msg_type); RUN_TEST(test_read_request_decode_rejects_short_frame); RUN_TEST(test_read_request_decode_rejects_bad_addr); @@ -886,6 +932,7 @@ int main(void) RUN_TEST(test_write_request_encode_rejects_word_count_above_max); RUN_TEST(test_write_request_decode_rejects_wrong_bus); RUN_TEST(test_write_request_decode_rejects_wrong_op); + RUN_TEST(test_write_request_decode_rejects_nonzero_evt); RUN_TEST(test_write_request_decode_rejects_short_frame); RUN_TEST(test_write_request_decode_rejects_bad_addr); RUN_TEST(test_write_request_decode_rejects_zero_words); diff --git a/tests/test_ep_uart.c b/tests/test_ep_uart.c index 49db630..adff483 100644 --- a/tests/test_ep_uart.c +++ b/tests/test_ep_uart.c @@ -349,6 +349,28 @@ static void test_write_request_rejects_wrong_bus_op_short_frame_bad_type(void) &txn)); } +/* TC18 §13.5 Table 30: evt[2:0] = 000b is the only legal value for a + * plain UART write request; every other value (here, 0b110, a reserved + * value in UART's endpoint-type row) shall be rejected. */ +static void test_write_request_rejects_nonzero_evt(void) +{ + rcp_acf_byte_message_info_t hdr = {0}; + rcp_bytes_t frame; + const uint8_t *out_tx; + size_t out_tx_len; + uint8_t txn; + + hdr.byte_bus_id = 4; + hdr.op = RCP_ACF_OP_WRITE; + hdr.evt = 0x6; + frame = rcp_acf_encode_abb(&hdr, NULL, 0); + + TEST_ASSERT_EQUAL(RCP_EP_UART_ERR_BAD_EVT, + rcp_ep_uart_decode_write_request(frame.data, frame.len, 4, &out_tx, &out_tx_len, &txn)); + + rcp_bytes_free(&frame); +} + static void test_write_response_round_trip_untimed_and_timed(void) { uint8_t accepted[2] = {0x55, 0x66}; @@ -462,6 +484,27 @@ static void test_read_request_rejects_wrong_bus_op_short_frame(void) rcp_ep_uart_decode_read_request(too_short, sizeof(too_short), 6, &read_size, &txn)); } +/* TC18 §13.5 Table 30: evt[2:0] = 000b is the only legal value for a + * plain UART read request; every other value (here, 0b101, a reserved + * value in UART's endpoint-type row) shall be rejected. */ +static void test_read_request_rejects_nonzero_evt(void) +{ + rcp_acf_byte_message_info_t hdr = {0}; + rcp_bytes_t frame; + uint8_t read_size; + uint8_t txn; + + hdr.byte_bus_id = 6; + hdr.op = RCP_ACF_OP_READ; + hdr.evt = 0x5; + frame = rcp_acf_encode_abb(&hdr, NULL, 0); + + TEST_ASSERT_EQUAL(RCP_EP_UART_ERR_BAD_EVT, + rcp_ep_uart_decode_read_request(frame.data, frame.len, 6, &read_size, &txn)); + + rcp_bytes_free(&frame); +} + static void test_read_response_round_trip_full_length(void) { uint8_t rx[4] = {0xDE, 0xAD, 0xBE, 0xEF}; @@ -675,12 +718,14 @@ int main(void) RUN_TEST(test_write_request_round_trip); RUN_TEST(test_write_request_rejects_wrong_bus_op_short_frame_bad_type); + RUN_TEST(test_write_request_rejects_nonzero_evt); RUN_TEST(test_write_response_round_trip_untimed_and_timed); RUN_TEST(test_write_response_decode_rejects_wrong_bus_and_short_frame); RUN_TEST(test_read_request_round_trip); RUN_TEST(test_read_request_rejects_payload_with_unknown_cmd); RUN_TEST(test_read_request_rejects_wrong_bus_op_short_frame); + RUN_TEST(test_read_request_rejects_nonzero_evt); RUN_TEST(test_read_response_round_trip_full_length); RUN_TEST(test_read_response_round_trip_short_read_single_avtpdu); RUN_TEST(test_read_response_decode_rejects_wrong_bus_and_short_frame); diff --git a/tests/test_tc18_gaps_ep2.c b/tests/test_tc18_gaps_ep2.c index 2db22de..1bf3dae 100644 --- a/tests/test_tc18_gaps_ep2.c +++ b/tests/test_tc18_gaps_ep2.c @@ -439,7 +439,9 @@ static void test_lin_trigger_ignores_trailing_time_and_block_lacks_registers(voi static void test_can_frame_format_values_match_table_54(void) { rcp_acf_byte_message_info_t hdr = {0}; - const uint8_t body[4] = {0, 0, 0, 1}; + /* Leading quadlet's top 3 bits = 110b (6), Table 54's first reserved + * code -- the low 29 bits (arbitration_id) are irrelevant here. */ + const uint8_t body[4] = {0xC0u, 0, 0, 0}; rcp_bytes_t f; rcp_ep_can_frame_format_t fmt = RCP_EP_CAN_FRAME_CBFF; uint32_t id = 0u; @@ -460,11 +462,14 @@ static void test_can_frame_format_values_match_table_54(void) TEST_ASSERT_FALSE(rcp_ep_can_frame_format_valid(6u)); TEST_ASSERT_FALSE(rcp_ep_can_frame_format_valid(7u)); - /* The selector rides the low three bits of the ACF evt field, and a - * reserved code there is rejected by the frame decoder. */ + /* FIXED (v0.109.0) -- the selector rides the payload's own leading + * quadlet (TC18 §13.7.11.3 Figure 39), not evt[2:0] (an earlier + * revision's own design choice, not TC18's); evt is left at its + * ordinary Table 30 Row-2 "plain request" value (0), and a reserved + * FrameFormat code in the payload is rejected by the frame decoder. */ hdr.byte_bus_id = 0x31u; hdr.op = RCP_ACF_OP_WRITE; - hdr.evt = 6u; + hdr.evt = 0u; hdr.transaction_num = 2u; f = rcp_acf_encode_abb(&hdr, body, sizeof(body)); TEST_ASSERT_NOT_NULL(f.data);