1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//! Pure-Rust, `no_std`-friendly implementation of the SIA OSDP v2.2 protocol.
//!
//! # Crate features
//!
//! - `std` *(default)*: enables [`alloc`], plus desktop convenience.
//! - `alloc` *(default via `std`)*: enables [`alloc::vec::Vec`]-based codec paths.
//! - `secure-channel` *(default)*: enables Annex D (AES-128, MAC, encryption).
//! - `embedded-io` / `embedded-io-async`: transport adapters.
//! - `defmt`: structured logging on embedded targets.
//!
//! # Layering
//!
//! See [`architecture`] for a rendered diagram of how these modules relate.
//!
//! - [`packet`] — wire framing (SOM, header, SCB, MAC, trailer)
//! - [`command`] / [`reply`] — typed messages
//! - [`multipart`] — RFC §5.10 multi-part assembly / disassembly
//! - [`secure`] — Annex D secure channel
//! - [`transport`] — byte-stream abstraction
//! - [`driver`] — ACU and PD state machines
//! - [`caps`] — Annex B function codes
//!
//! # Quick start
//!
//! Drive a PD at address `0x05` through one POLL exchange. The example uses
//! [`transport::VecTransport`], so without a peer feeding bytes back the
//! exchange will end in [`driver::acu::ExchangeOutcome::Timeout`] — wire it
//! up to a real `Transport` (or another `VecTransport`, see
//! `examples/loopback_poll.rs`) for a successful round-trip.
//!
//! ```
//! use osdp::clock::SystemClock;
//! use osdp::command::{Command, Poll};
//! use osdp::driver::acu::{Acu, ExchangeOutcome, PdState};
//! use osdp::reply::Reply;
//! use osdp::transport::VecTransport;
//!
//! let mut acu = Acu::new(VecTransport::new(), SystemClock::new());
//! let mut pd = PdState::default();
//! match acu.exchange(0x05, &mut pd, &Command::Poll(Poll))? {
//! ExchangeOutcome::Reply(Reply::Ack(_)) => { /* PD alive */ }
//! ExchangeOutcome::Busy => { /* PD asked us to back off */ }
//! ExchangeOutcome::Timeout => { /* no reply within budget */ }
//! ExchangeOutcome::Offline => { /* PD declared offline */ }
//! _ => {}
//! }
//! # Ok::<(), osdp::Error>(())
//! ```
//!
//! For the secure-channel walk see `examples/handshake.rs`; for an end-to-end
//! loopback that exercises SQN cycling see `examples/loopback_poll.rs`.
//!
//! # Specification cross-references
//!
//! All spec citations refer to *SIA OSDP v2.2* (©2020 Security Industry
//! Association). Where an item maps directly onto a spec section it is noted
//! in the docs as `# Spec: §X.Y`.
extern crate alloc;
/// Crate architecture diagram.
///
/// `osdp-rs` layers responsibilities so that the high-level state machines
/// in [`driver`] can stay independent of the wire format and the I/O
/// substrate. Every arrow points "uses".
///
/// ```mermaid
/// flowchart TB
/// APP([Application])
/// subgraph drivers["driver — high-level state machines"]
/// ACU[acu::Acu]
/// PD[pd::Pd]
/// end
/// subgraph messages["typed messages"]
/// CMD[command]
/// REP[reply]
/// MP[multipart]
/// end
/// subgraph wire["wire layer"]
/// PKT[packet]
/// SEC["secure (Annex D)"]
/// end
/// TR[transport]
/// APP --> drivers
/// drivers --> messages
/// drivers --> wire
/// drivers --> TR
/// messages --> wire
/// wire --> TR
/// ```
pub use ;
pub use ;
pub use Packet;
/// Start of message marker — begins every OSDP packet header.
///
/// # Spec: §5.9
pub const SOM: u8 = 0x53;
/// Broadcast address.
///
/// # Spec: §5.4
///
/// Address `0x7F` is reserved as a special "BROADCAST" address that each PD
/// will accept and respond to. The reply uses `0x7F | 0x80 = 0xFF` in its
/// address field.
pub const BROADCAST_ADDR: u8 = 0x7F;
/// Largest legal PD unicast address.
///
/// # Spec: §5.4
pub const MAX_PD_ADDR: u8 = 0x7E;
/// Reply flag set in [`Address`] when sent from PD to ACU.
///
/// # Spec: §5.9, Table 1
pub const REPLY_FLAG: u8 = 0x80;
/// Minimum receive-buffer size every PD must support.
///
/// # Spec: §5.6
pub const MIN_RX_SIZE: usize = 128;
/// Maximum packet length that any device must tolerate on the wire (even if
/// addressed elsewhere).
///
/// # Spec: §5.6
pub const MAX_BUS_PACKET: usize = 1440;
/// Default ACU reply-delay budget.
///
/// # Spec: §5.7
pub const REPLY_DELAY_MS: u32 = 200;
/// Off-line declaration threshold.
///
/// # Spec: §5.7
pub const OFFLINE_THRESHOLD_MS: u32 = 8_000;