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
62 changes: 59 additions & 3 deletions .fusa-reqs.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@
"title": "ValidateFrame rejects ID > 0x3F",
"description": "ValidateFrame shall return an error when Frame.ID exceeds MaxID (0x3F).",
"asil": "ASIL-B",
"rationale": "SG-05: prevents an invalid identifier from being transmitted or processed.",
"rationale": "SG-02: prevents an invalid identifier from being transmitted or processed.",
"tags": ["core", "validation"]
},
{
"id": "REQ-LIN-002",
"title": "ValidateFrame rejects empty data",
"description": "ValidateFrame shall return an error when Frame.Data has zero length.",
"asil": "ASIL-B",
"rationale": "SG-05: a zero-length LIN frame is malformed and has no defined wire format.",
"rationale": "SG-01: a zero-length LIN frame is malformed and has no defined wire format.",
"tags": ["core", "validation"]
},
{
"id": "REQ-LIN-003",
"title": "ValidateFrame rejects oversized data",
"description": "ValidateFrame shall return an error when len(Frame.Data) exceeds MaxDataLen (8).",
"asil": "ASIL-B",
"rationale": "SG-05: LIN 2.x limits payloads to 8 bytes; exceeding this is a protocol violation.",
"rationale": "SG-01: LIN 2.x limits payloads to 8 bytes; exceeding this is a protocol violation.",
"tags": ["core", "validation"]
},
{
Expand Down Expand Up @@ -1104,6 +1104,62 @@
"asil": "ASIL-B",
"rationale": "RELAY §3.2: robust protocol string parsing with two variants for exception/error-code styles.",
"tags": ["relay", "protocol"]
},
{
"id": "REQ-CLI-001",
"title": "version command prints tool and spec version",
"description": "The CLI `version` command shall print the tool and spec version as JSON (or text with --format text).",
"asil": "QM",
"rationale": "RELAY §11.1: mandatory version command for conformance self-reporting.",
"tags": ["cli"]
},
{
"id": "REQ-CLI-002",
"title": "capabilities command prints supported protocols and commands",
"description": "The CLI `capabilities` command shall print supported protocols, transports, commands and interfaces as JSON.",
"asil": "QM",
"rationale": "RELAY §11.2/§12: mandatory capabilities self-report.",
"tags": ["cli"]
},
{
"id": "REQ-CLI-003",
"title": "status command prints health status",
"description": "The CLI `status` command shall print the health status as JSON (or text with --format text).",
"asil": "QM",
"rationale": "RELAY §11.1: mandatory status command.",
"tags": ["cli"]
},
{
"id": "REQ-CLI-004",
"title": "CLI exits 2 on invalid arguments",
"description": "The CLI shall exit with code 2 and write an error name to stderr on invalid or unknown arguments.",
"asil": "QM",
"rationale": "RELAY §11.3: standardised CLI exit codes.",
"tags": ["cli"]
},
{
"id": "REQ-CLI-005",
"title": "CLI --format flag selects text or json output",
"description": "Commands accepting --format shall support 'text' and 'json' and reject other values with exit code 2.",
"asil": "QM",
"rationale": "RELAY §11.1: --format text|json handling.",
"tags": ["cli"]
},
{
"id": "REQ-CLI-006",
"title": "convert command bridges lin.Frame to relay.Message JSON",
"description": "The CLI `convert` command shall read a lin.Frame JSON from stdin, validate it, and write the corresponding relay.Message JSON to stdout, writing the sentinel error name to stderr on failure.",
"asil": "QM",
"rationale": "RELAY §11.2: cross-protocol conversion command.",
"tags": ["cli"]
},
{
"id": "REQ-SEC-013",
"title": "Bounded channel try_send rejects when full",
"description": "Chan::try_send shall return SendResult::Full when the channel is at capacity rather than growing unboundedly, bounding memory under back-pressure.",
"asil": "ASIL-B",
"rationale": "SG-04: no unbounded memory growth under load / denial-of-service resistance.",
"tags": ["security", "channel"]
}
]
}
62 changes: 62 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Changelog

All notable changes to cpp-LIN are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.4.1]
### Added
- `verify_checksum()`: validates a received checksum byte against the
locally-computed value (ISO 17987 frame reception).
- `lin::Errc::no_response`, a distinct "no slave answered" sentinel that
compares equivalent to a relay timeout, replacing the previous generic
timeout return from `send_header`.
- `CHANGELOG.md` (this file).
### Changed
- Declared RELAY spec version bumped 1.11 → 2.0 across headers, CLI JSON,
README, HARA, and requirements.
- `master::Node::set_schedule` now accepts an empty schedule (disables
scheduled transmission), matching the virtual bus and the spec.
- `Chan::send_drop_oldest` reports whether it evicted an item so DropOldest
back-pressure now increments the drop counter instead of the delivery
counter on eviction.
- `Frame::checksum_type` now defaults to Classic (the enum's zero value),
matching the canonical default used by the other language bindings.
- `capabilities` no longer duplicates `virtual` between `features` and
`transports`.
- E2E `Receiver::unwrap` no longer advances its sequence counter on a
rejected (out-of-order) frame, so a persistent shifted stream keeps being
flagged instead of resyncing after a single error.
### Fixed
- `from_message` now rejects an unrecognized `lin.checksum_type` instead of
silently coercing it to Classic.
- `testdata/relay-vectors/lin-frame.json` now encodes `lin.checksum_type` as
`"enhanced"` instead of the numeral `"1"`.
- HARA SG-03 status corrected from "Implemented" to "Partial" pending a
verified-checksum code path in a real (non-virtual) bus backend.
- Corrected the Safety Goal cross-references on three `.fusa-reqs.json`
requirement rationales (SG-02/SG-01 instead of SG-05) and added the
previously-undefined `REQ-CLI-001..006` / `REQ-SEC-013` requirement
entries for existing `fusa:req` trace tags.
- HARA citation for error handling corrected from spec §15 to §5.

## [0.4.0]
### Added
- RELAY ecosystem audit fixes.

## [0.3.0]
### Added
- RELAY spec v1.11 conformance (see ROADMAP.md for detail).

## [0.2.0]
### Added
- Full safety and cyber pack: TARA, FMEA, SAS, boundary diagram, Safety
Manual, IEC 62443 artifacts (see ROADMAP.md for detail).

## [0.1.0]
### Added
- Initial C++ LIN bus port: core frame types, PID/checksum, virtual bus,
master/slave nodes, E2E safety layer, LDF parser, and RELAY adapter/CLI.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.21)

project(cpplin
VERSION 0.4.0
VERSION 0.4.1
DESCRIPTION "C++ LIN bus library — RELAY-conformant, ISO 26262 ASIL-B"
HOMEPAGE_URL "https://github.com/SoundMatt/cpp-LIN"
LANGUAGES CXX
Expand Down
4 changes: 2 additions & 2 deletions HARA.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ ASIL-D at system level if required.
|----|-------------|------|--------|
| SG-01 | No data corruption during TX/RX | ASIL-B | Implemented (checksum, validation) |
| SG-02 | Reject frames with ID > 0x3F | ASIL-B | Implemented (validate_frame) |
| SG-03 | Detect incorrect checksums | ASIL-B | Implemented (calc_checksum, verify_pid) |
| SG-03 | Detect incorrect checksums | ASIL-B | Partial (verify_checksum compares a received checksum; calc_checksum/verify_pid alone only compute a checksum / check PID parity, not the data checksum) |
| SG-04 | No unbounded memory growth | ASIL-B | Implemented (Chan capacity, drop policy) |
| SG-05 | Detect E2E protection errors | ASIL-B | Implemented (E2E CRC-16, seq check) |
| SG-06 | Diagnostic frames use Classic checksum | ASIL-B | Implemented (validate_frame) |
Expand All @@ -131,6 +131,6 @@ ASIL-D at system level if required.

- ISO 26262:2018 Part 3 — Concept phase
- LIN Specification Package Rev 2.2A — Section 2 (Frame structure)
- RELAY Specification v1.11 — §15 (Error handling)
- RELAY Specification v2.0 — §5 (Error handling)
- `SEOOC.md` — Assumptions on use
- `SAFETY_PLAN.md` — Development process
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
C++ LIN bus library — RELAY-conformant, ISO 26262 ASIL-B.

[![CI](https://github.com/SoundMatt/cpp-LIN/actions/workflows/ci.yml/badge.svg)](https://github.com/SoundMatt/cpp-LIN/actions/workflows/ci.yml)
[![RELAY Conformant](https://img.shields.io/badge/RELAY-v1.11%20conformant-blue)](https://github.com/SoundMatt/RELAY)
[![RELAY Conformant](https://img.shields.io/badge/RELAY-v2.0%20conformant-blue)](https://github.com/SoundMatt/RELAY)
[![ASIL-B](https://img.shields.io/badge/ISO%2026262-ASIL--B-orange)](SAFETY_PLAN.md)
[![License: MPL 2.0](https://img.shields.io/badge/License-MPL%202.0-brightgreen.svg)](LICENSE)

## Overview

cpp-LIN is a production-quality C++ implementation of the LIN (Local Interconnect
Network) protocol, following the RELAY specification v1.11. It is the C++ sibling
Network) protocol, following the RELAY specification v2.0. It is the C++ sibling
of [go-LIN](https://github.com/SoundMatt/go-LIN) and is patterned after
[cpp-CAN](https://github.com/SoundMatt/cpp-CAN).

Expand Down Expand Up @@ -50,7 +50,7 @@ ctest --test-dir build --output-on-failure
```
include/lin/
channel.hpp — Chan<T>: bounded, thread-safe FIFO
relay.hpp — RELAY v1.11 types (Protocol, Message, INode, …)
relay.hpp — RELAY v2.0 types (Protocol, Message, INode, …)
lin.hpp — IBus, IMasterBus, Frame, Filter, free functions
virtual/bus.hpp — In-process virtual LIN bus
mock/mock.hpp — RELAY spec §13.7.1 canonical `mock` module (alias of virtual::Bus)
Expand Down Expand Up @@ -97,7 +97,7 @@ P1 = NOT(ID1 ^ ID3 ^ ID4 ^ ID5) (bit 7)

## RELAY Integration

cpp-LIN exposes a RELAY v1.11 `INode` adapter:
cpp-LIN exposes a RELAY v2.0 `INode` adapter:

```cpp
#include <lin/lin.hpp>
Expand Down
22 changes: 21 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
- [x] `RELAY_BUILD_CLI` CMake alias (§17.7, since removed — see v0.4.0)
- [x] Test vectors updated to spec_version 1.11

## v0.4.0 — RELAY ecosystem audit fixes (current)
## v0.4.0 — RELAY ecosystem audit fixes

- [x] LIN-specific error category (`lin::Errc::invalid_frame`) distinct from
`ErrPayloadTooLarge` for out-of-range frame IDs (spec §5.3)
Expand All @@ -50,6 +50,26 @@
convention), matching peers like `go-can`
- [x] `lin::mock` canonical module (spec §13.7.1), aliasing `lin::virt::Bus`

## v0.4.1 — RELAY ecosystem audit fixes, pass 2 (current)

- [x] Declared RELAY spec version bumped 1.11 → 2.0
- [x] `verify_checksum()` added; HARA SG-03 downgraded from "Implemented" to
"Partial" pending a verified-checksum path in a real (non-virtual) bus
backend
- [x] `lin::Errc::no_response` distinct sentinel replaces the generic
timeout previously returned by `send_header` on no slave response
- [x] `from_message` rejects unrecognized `lin.checksum_type` instead of
silently defaulting to Classic
- [x] `master::Node::set_schedule` accepts an empty schedule (spec §8.3)
- [x] DropOldest back-pressure counts an eviction as a drop, not a delivery
- [x] `Frame::checksum_type` defaults to Classic (enum zero value)
- [x] E2E `Receiver::unwrap` no longer resyncs its sequence counter after a
single rejected frame
- [x] Requirement/HARA traceability corrections: `REQ-CLI-001..006` /
`REQ-SEC-013` defined, three misattributed SG rationale references
fixed, HARA §15→§5 citation fixed
- [x] `CHANGELOG.md` added

## v0.5.0 — Enhanced Bus Features

- [ ] Sleep/wake frame support (LIN 2.x go-to-sleep command)
Expand Down
8 changes: 4 additions & 4 deletions cli/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ inline std::string version_json() {
"\"protocol\":\"LIN\","
"\"protocol_int\":3,"
"\"version\":\"" + std::string(kToolVersion) + "\","
"\"spec_version\":\"1.11\","
"\"spec_version\":\"2.0\","
"\"language\":\"cpp\","
"\"runtime\":\"c++17\""
"}";
Expand All @@ -195,7 +195,7 @@ inline std::string version_text() {
o << "tool: cpp-lin\n"
<< "protocol: LIN\n"
<< "version: " << kToolVersion << "\n"
<< "spec: 1.11\n"
<< "spec: 2.0\n"
<< "language: cpp\n"
<< "runtime: c++17\n";
return o.str();
Expand All @@ -209,10 +209,10 @@ inline std::string capabilities_json() {
"\"protocol\":\"LIN\","
"\"protocol_int\":3,"
"\"version\":\"" + std::string(kToolVersion) + "\","
"\"spec_version\":\"1.11\","
"\"spec_version\":\"2.0\","
"\"commands\":[\"version\",\"capabilities\",\"status\",\"convert\"],"
"\"transports\":[\"virtual\"],"
"\"features\":[\"ldf\",\"e2e\",\"master\",\"slave\",\"virtual\",\"mock\"],"
"\"features\":[\"ldf\",\"e2e\",\"master\",\"slave\",\"mock\"],"
"\"interfaces\":[\"IBus\",\"IMasterBus\",\"INode\"],"
"\"optional_interfaces\":[\"IHealthProvider\",\"IMetricsProvider\",\"IDrainer\"],"
"\"adapt\":true"
Expand Down
14 changes: 8 additions & 6 deletions include/lin/channel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace lin {
template<typename T>
class Chan {
public:
enum class SendResult { Ok, Full, Closed };
enum class SendResult { Ok, Full, Closed, Evicted };

explicit Chan(std::size_t capacity = 64) : capacity_(capacity) {}

Expand Down Expand Up @@ -54,14 +54,16 @@ class Chan {
}

// Send with DropOldest policy — evicts head when at capacity.
// Returns false if closed.
bool send_drop_oldest(T value) {
// Returns Closed if closed, Evicted if an item was discarded to make room
// (the new item is still enqueued), or Ok if enqueued without eviction.
SendResult send_drop_oldest(T value) {
std::lock_guard<std::mutex> lk(mu_);
if (closed_) return false;
if (buf_.size() >= capacity_) buf_.pop_front();
if (closed_) return SendResult::Closed;
bool evicted = false;
if (buf_.size() >= capacity_) { buf_.pop_front(); evicted = true; }
buf_.push_back(std::move(value));
cv_not_empty_.notify_one();
return true;
return evicted ? SendResult::Evicted : SendResult::Ok;
}

// Blocking recv — returns nullopt only when closed and empty.
Expand Down
18 changes: 15 additions & 3 deletions include/lin/lin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace lin {

// ── Spec version ─────────────────────────────────────────────────────────────

inline constexpr const char* kSpecVersion = "1.11";
inline constexpr const char* kSpecVersion = "2.0";

// ── Constants ─────────────────────────────────────────────────────────────────

Expand All @@ -53,7 +53,7 @@ struct Frame {
uint8_t id{}; // 6-bit frame identifier (0x00–0x3F)
std::vector<uint8_t> data; // payload (1–8 bytes)
uint8_t checksum{}; // wire checksum byte
ChecksumType checksum_type{ChecksumType::Enhanced};
ChecksumType checksum_type{ChecksumType::Classic}; // §18.2 canonical default (enum zero value)
};

// ── Filter ────────────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -116,9 +116,13 @@ inline std::error_code ErrPayloadTooLarge() noexcept { return relay::ErrPayloadT
// not the relay.Node interface, so — like CAN's ErrInvalidFrame (§5.4, "not a
// relay sentinel") — this code is not mapped to any relay::Errc value.

// fusa:req REQ-LIN-015
// fusa:req REQ-LIN-015 REQ-LIN-021
enum class Errc : int {
invalid_frame = 1,
// Distinct "no slave answered" sentinel (§5.4). Its error category maps it
// as equivalent to relay::Errc::timeout so callers that only test for a
// deadline expiry still match, while the distinct condition is preserved.
no_response = 2,
};

const std::error_category& error_category() noexcept;
Expand Down Expand Up @@ -147,6 +151,14 @@ uint8_t verify_pid(uint8_t pid);
// fusa:req REQ-LIN-008 REQ-LIN-009 REQ-LIN-010
uint8_t calc_checksum(uint8_t pid, const std::vector<uint8_t>& data, ChecksumType ct) noexcept;

// Verifies a received checksum byte against the locally-computed value for the
// given PID and data (ISO 17987 frame reception). Returns true when received
// equals the recomputed checksum, false on a mismatch (corrupt frame — H-01).
//
// fusa:req REQ-LIN-008 REQ-LIN-009 REQ-LIN-010
bool verify_checksum(uint8_t pid, const std::vector<uint8_t>& data,
uint8_t received, ChecksumType ct) noexcept;

// Validates f against LIN protocol constraints.
// Throws ErrInvalidFrame if any constraint is violated.
//
Expand Down
4 changes: 2 additions & 2 deletions include/lin/relay.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// relay.hpp — RELAY spec types shared across all protocol implementations.
// Mirrors the RELAY Go package spec v1.11.
// Mirrors the RELAY Go package spec v2.0.

#pragma once

Expand All @@ -24,7 +24,7 @@ namespace relay {
// ── Spec version ─────────────────────────────────────────────────────────────

// fusa:req REQ-RELAY-020
inline constexpr const char* kSpecVersion = "1.11";
inline constexpr const char* kSpecVersion = "2.0";

// ── Protocol ─────────────────────────────────────────────────────────────────

Expand Down
2 changes: 1 addition & 1 deletion requirements/requirements.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"language": "cpp",
"standard": "iso26262",
"asil": "ASIL-B",
"spec": "RELAY v1.11"
"spec": "RELAY v2.0"
},
"requirements": [
{"id": "REQ-LIN-001", "title": "ValidateFrame rejects ID > 0x3F", "asil": "ASIL-B", "tags": ["core", "validation"]},
Expand Down
Loading
Loading