Skip to main content

usbpd/protocol_layer/message/
header.rs

1//! Definitions for a USB PD message header.
2//!
3//! See [6.2.1.1].
4use core::convert::TryFrom;
5
6use byteorder::{ByteOrder, LittleEndian};
7use proc_bitfield::bitfield;
8
9use crate::counters::Counter;
10use crate::protocol_layer::message::ParseError;
11use crate::{DataRole, PowerRole};
12
13bitfield! {
14    #[derive(Clone, Copy, PartialEq, Eq)]
15    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
16    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17    /// Definition of the message header. Every message shall start with it.
18    pub struct Header(pub u16): Debug, FromStorage, IntoStorage {
19        /// Shall be set to zero to indicate a Control Message or Data Message
20        /// and set to one to indicate an Extended Message.
21        pub extended: bool @ 15,
22        /// The number of 32 bit data objects that follow the header.
23        pub num_objects: u8 [get usize] @ 12..=14,
24        /// A rolling counter, maintained by the originator of the message.
25        pub message_id: u8 @ 9..=11,
26        /// Indicate the port's present power role (0 -> sink, 1 -> source).
27        pub port_power_role: bool [get PowerRole, set PowerRole] @ 8,
28        /// The specification revision.
29        ///
30        /// 00b - Revision 1.0 (deprecated)
31        /// 01b - Revision 2.0
32        /// 10b - Revision 3.x
33        /// 11b - Reserved, shall not be used
34        pub spec_revision: u8 [try_get SpecificationRevision, set SpecificationRevision] @ 6..=7,
35        /// The port's data role (0 -> UFP, 1 -> DFP).
36        pub port_data_role: bool [get DataRole, set DataRole] @ 5,
37        /// The type of message being sent. See [6.2.1.1.8] for details
38        pub message_type_raw: u8 @ 0..=4,
39    }
40}
41
42impl Header {
43    /// Create a header template with the given attributes.
44    pub fn new_template(
45        port_data_role: DataRole,
46        port_power_role: PowerRole,
47        spec_revision: SpecificationRevision,
48    ) -> Self {
49        Self(0)
50            .with_port_data_role(port_data_role)
51            .with_port_power_role(port_power_role)
52            .with_spec_revision(spec_revision)
53    }
54
55    /// Create a new header that follows a template.
56    pub fn new(
57        template: Self,
58        message_id: Counter,
59        message_type: MessageType,
60        num_objects: u8,
61        extended: bool,
62    ) -> Self {
63        template
64            .with_message_id(message_id.value())
65            .with_message_type_raw(match message_type {
66                MessageType::Control(x) => x as u8,
67                MessageType::Extended(x) => x as u8,
68                MessageType::Data(x) => x as u8,
69            })
70            .with_num_objects(num_objects)
71            .with_extended(extended)
72    }
73
74    /// Create a new control message header.
75    pub fn new_control(template: Self, message_id: Counter, message_type: ControlMessageType) -> Self {
76        Self::new(template, message_id, MessageType::Control(message_type), 0, false)
77    }
78
79    /// Create a new data message header.
80    pub fn new_data(template: Self, message_id: Counter, message_type: DataMessageType, num_objects: u8) -> Self {
81        Self::new(
82            template,
83            message_id,
84            MessageType::Data(message_type),
85            num_objects,
86            false,
87        )
88    }
89
90    /// Create a new extended message header.
91    pub fn new_extended(
92        template: Self,
93        message_id: Counter,
94        extended_message_type: ExtendedMessageType,
95        num_objects: u8,
96    ) -> Self {
97        Self::new(
98            template,
99            message_id,
100            MessageType::Extended(extended_message_type),
101            num_objects,
102            true,
103        )
104    }
105
106    /// Parse a header from its binary representation.
107    pub fn from_bytes(buf: &[u8]) -> Result<Self, ParseError> {
108        assert!(buf.len() == 2);
109
110        let header = Header(LittleEndian::read_u16(buf));
111        // Validate spec_revision
112        header.spec_revision()?;
113        Ok(header)
114    }
115
116    /// Serialize the header to its binary representation.
117    pub fn to_bytes(self, buf: &mut [u8]) -> usize {
118        LittleEndian::write_u16(buf, self.0);
119        2
120    }
121
122    /// Extract the message type that the header encodes.
123    pub fn message_type(&self) -> MessageType {
124        // Check extended bit first - Extended messages can have data objects (e.g., EPR Source Capabilities)
125        if self.extended() {
126            MessageType::Extended(self.message_type_raw().into())
127        } else if self.num_objects() == 0 {
128            MessageType::Control(self.message_type_raw().into())
129        } else {
130            MessageType::Data(self.message_type_raw().into())
131        }
132    }
133}
134
135/// Specification revieions.
136#[derive(Debug, Clone, Copy)]
137#[cfg_attr(feature = "defmt", derive(defmt::Format))]
138#[allow(non_camel_case_types)]
139pub enum SpecificationRevision {
140    /// Version 1.0.
141    R1_0,
142    /// Version 2.0.
143    R2_0,
144    /// Version 3.x.
145    R3_X,
146}
147
148impl TryFrom<u8> for SpecificationRevision {
149    type Error = ParseError;
150    fn try_from(value: u8) -> Result<Self, Self::Error> {
151        match value {
152            0b00 => Ok(Self::R1_0),
153            0b01 => Ok(Self::R2_0),
154            0b10 => Ok(Self::R3_X),
155            _ => Err(ParseError::UnsupportedSpecificationRevision(value)),
156        }
157    }
158}
159
160impl From<SpecificationRevision> for u8 {
161    fn from(value: SpecificationRevision) -> Self {
162        match value {
163            SpecificationRevision::R1_0 => 0b00,
164            SpecificationRevision::R2_0 => 0b01,
165            SpecificationRevision::R3_X => 0b10,
166        }
167    }
168}
169
170/// The type of message that a header encodes.
171#[derive(Clone, Copy, Debug, PartialEq, Eq)]
172#[cfg_attr(feature = "defmt", derive(defmt::Format))]
173pub enum MessageType {
174    /// A control message, as defined in [6.3].
175    Control(ControlMessageType),
176    /// A data message, as defined in [6.4].
177    Data(DataMessageType),
178    /// A data message, as defined in [6.5].
179    Extended(ExtendedMessageType),
180}
181
182/// Types of control messages.
183#[allow(missing_docs)]
184#[derive(Clone, Copy, Debug, PartialEq, Eq)]
185#[cfg_attr(feature = "defmt", derive(defmt::Format))]
186pub enum ControlMessageType {
187    GoodCRC = 0b0_0001,
188    GotoMin = 0b0_0010,
189    Accept = 0b0_0011,
190    Reject = 0b0_0100,
191    Ping = 0b0_0101,
192    PsRdy = 0b0_0110,
193    GetSourceCap = 0b0_0111,
194    GetSinkCap = 0b0_1000,
195    DrSwap = 0b0_1001,
196    PrSwap = 0b0_1010,
197    VconnSwap = 0b0_1011,
198    Wait = 0b0_1100,
199    SoftReset = 0b0_1101,
200    DataReset = 0b0_1110,
201    DataResetComplete = 0b0_1111,
202    NotSupported = 0b1_0000,
203    GetSourceCapExtended = 0b1_0001,
204    GetStatus = 0b1_0010,
205    FrSwap = 0b1_0011,
206    GetPpsStatus = 0b1_0100,
207    GetCountryCodes = 0b1_0101,
208    GetSinkCapExtended = 0b1_0110,
209    GetSourceInfo = 0b1_0111,
210    GetRevision = 0b1_1000,
211    Reserved,
212}
213
214impl From<u8> for ControlMessageType {
215    fn from(value: u8) -> Self {
216        match value {
217            0b0_0001 => Self::GoodCRC,
218            0b0_0010 => Self::GotoMin,
219            0b0_0011 => Self::Accept,
220            0b0_0100 => Self::Reject,
221            0b0_0101 => Self::Ping,
222            0b0_0110 => Self::PsRdy,
223            0b0_0111 => Self::GetSourceCap,
224            0b0_1000 => Self::GetSinkCap,
225            0b0_1001 => Self::DrSwap,
226            0b0_1010 => Self::PrSwap,
227            0b0_1011 => Self::VconnSwap,
228            0b0_1100 => Self::Wait,
229            0b0_1101 => Self::SoftReset,
230            0b0_1110 => Self::DataReset,
231            0b0_1111 => Self::DataResetComplete,
232            0b1_0000 => Self::NotSupported,
233            0b1_0001 => Self::GetSourceCapExtended,
234            0b1_0010 => Self::GetStatus,
235            0b1_0011 => Self::FrSwap,
236            0b1_0100 => Self::GetPpsStatus,
237            0b1_0101 => Self::GetCountryCodes,
238            0b1_0110 => Self::GetSinkCapExtended,
239            0b1_0111 => Self::GetSourceInfo,
240            0b1_1000 => Self::GetRevision,
241            _ => Self::Reserved,
242        }
243    }
244}
245
246/// Types of data messages.
247#[derive(Clone, Copy, Debug, PartialEq, Eq)]
248#[cfg_attr(feature = "defmt", derive(defmt::Format))]
249#[allow(missing_docs)]
250pub enum DataMessageType {
251    SourceCapabilities = 0b0_0001,
252    Request = 0b0_0010,
253    Bist = 0b0_0011,
254    SinkCapabilities = 0b0_0100,
255    BatteryStatus = 0b0_0101,
256    Alert = 0b0_0110,
257    GetCountryInfo = 0b0_0111,
258    EnterUsb = 0b0_1000,
259    EprRequest = 0b0_1001,
260    EprMode = 0b0_1010,
261    SourceInfo = 0b0_1011,
262    Revision = 0b0_1100,
263    VendorDefined = 0b0_1111,
264    Reserved,
265}
266
267impl From<u8> for DataMessageType {
268    fn from(value: u8) -> Self {
269        match value {
270            0b0_0001 => Self::SourceCapabilities,
271            0b0_0010 => Self::Request,
272            0b0_0011 => Self::Bist,
273            0b0_0100 => Self::SinkCapabilities,
274            0b0_0101 => Self::BatteryStatus,
275            0b0_0110 => Self::Alert,
276            0b0_0111 => Self::GetCountryInfo,
277            0b0_1000 => Self::EnterUsb,
278            0b0_1001 => Self::EprRequest,
279            0b0_1010 => Self::EprMode,
280            0b0_1011 => Self::SourceInfo,
281            0b0_1100 => Self::Revision,
282            0b0_1111 => Self::VendorDefined,
283            _ => Self::Reserved,
284        }
285    }
286}
287
288impl From<u8> for ExtendedMessageType {
289    fn from(value: u8) -> Self {
290        match value {
291            0b0_0001 => Self::SourceCapabilitiesExtended,
292            0b0_0010 => Self::Status,
293            0b0_0011 => Self::GetBatteryCap,
294            0b0_0100 => Self::GetBatteryStatus,
295            0b0_0101 => Self::BatteryCapabilities,
296            0b0_0110 => Self::GetManufacturerInfo,
297            0b0_0111 => Self::ManufacturerInfo,
298            0b0_1000 => Self::SecurityRequest,
299            0b0_1001 => Self::SecurityResponse,
300            0b0_1010 => Self::FirmwareUpdateRequest,
301            0b0_1011 => Self::FirmwareUpdateResponse,
302            0b0_1100 => Self::PpsStatus,
303            0b0_1101 => Self::CountryInfo,
304            0b0_1110 => Self::CountryCodes,
305            0b0_1111 => Self::SinkCapabilitiesExtended,
306            0b1_0000 => Self::ExtendedControl,
307            0b1_0001 => Self::EprSourceCapabilities,
308            0b1_0010 => Self::EprSinkCapabilities,
309            0b1_1110 => Self::VendorDefinedExtended,
310            _ => Self::Reserved,
311        }
312    }
313}
314
315/// Types of extended messages.
316#[derive(Clone, Copy, Debug, PartialEq, Eq)]
317#[cfg_attr(feature = "defmt", derive(defmt::Format))]
318#[allow(missing_docs)]
319pub enum ExtendedMessageType {
320    SourceCapabilitiesExtended = 0b0_0001,
321    Status = 0b0_0010,
322    GetBatteryCap = 0b0_0011,
323    GetBatteryStatus = 0b0_0100,
324    BatteryCapabilities = 0b0_0101,
325    GetManufacturerInfo = 0b0_0110,
326    ManufacturerInfo = 0b0_0111,
327    SecurityRequest = 0b0_1000,
328    SecurityResponse = 0b0_1001,
329    FirmwareUpdateRequest = 0b0_1010,
330    FirmwareUpdateResponse = 0b0_1011,
331    PpsStatus = 0b0_1100,
332    CountryInfo = 0b0_1101,
333    CountryCodes = 0b0_1110,
334    SinkCapabilitiesExtended = 0b0_1111,
335    ExtendedControl = 0b1_0000,
336    EprSourceCapabilities = 0b1_0001,
337    EprSinkCapabilities = 0b1_0010,
338    VendorDefinedExtended = 0b1_1110,
339    Reserved,
340}