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
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@
"${workspaceFolder}/Cargo.toml"
],
"rust-analyzer.cargo.features": [
"defmt",
]
}
}
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2021"
[dependencies]
defmt = { version = "0.3", optional = true }
embedded-hal-async = "1.0.0"
bitfield = "0.18.1"
bitfield = "0.19.0"

[features]
default = []
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![no_std]

pub mod pdinfo;
pub mod pdo;
pub mod type_c;
pub mod ucsi;
Expand Down
204 changes: 204 additions & 0 deletions src/pdinfo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
//! Types for the `pdinfo` command to query the PD controller for information on the current port and status.

/// The status for the switches on the power path.
#[derive(Clone, Copy)]
pub struct PowerPathStatus(inner::PowerPathStatus);

impl PowerPathStatus {
/// Creates a new [`PowerPathStatus`] instance with the given status bits.
pub fn new(ext_vbus_sw_en: bool, int_vbus_sw_en: bool) -> Self {
Self(inner::PowerPathStatus::new(
ext_vbus_sw_en.into(),
int_vbus_sw_en.into(),
))
}

/// Returns true if the external VBUS switch is enabled.
pub fn ext_vbus_sw_en(&self) -> bool {
self.0.ext_vbus_sw_en() == 1
}

/// Returns true if the internal VBUS switch is enabled.
pub fn int_vbus_sw_en(&self) -> bool {
self.0.int_vbus_sw_en() == 1
}
}

impl core::fmt::Debug for PowerPathStatus {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
// Implementing Debug manually to avoid the `.0: 123` field in the bitfield debug impl.
f.debug_struct("PowerPathStatus")
.field("ext_vbus_sw_en", &self.ext_vbus_sw_en())
.field("int_vbus_sw_en", &self.int_vbus_sw_en())
.finish()
}
}

#[cfg(feature = "defmt")]
impl defmt::Format for PowerPathStatus {
fn format(&self, fmt: defmt::Formatter) {
// Implementing Format manually to avoid the `.0: 123` field in the bitfield debug impl.
defmt::write!(
fmt,
"PowerPathStatus {{ ext_vbus_sw_en: {}, int_vbus_sw_en: {} }}",
self.ext_vbus_sw_en(),
self.int_vbus_sw_en()
)
}
}

/// The Alternate Mode the controller is in.
///
/// Alternate Modes are vendor-defined therefore the meaning of the `userX` getters, such as
/// [`AltMode::user0()`], are defined by the particular driver. Additionally, common alternate
/// modes are defined and should be used by drivers that support them.
#[derive(Clone, Copy)]
pub struct AltMode(inner::AltMode);

impl core::fmt::Debug for AltMode {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
// Implementing Debug manually to avoid the `.0: 123` field in the bitfield debug impl.
f.debug_struct("AltMode")
.field("user0", &self.user0())
.field("user1", &self.user1())
.field("user2", &self.user2())
.field("user3", &self.user3())
.field("display_port", &self.display_port())
.field("thunderbolt", &self.thunderbolt())
.field("usb4", &self.usb4())
.finish()
}
}

#[cfg(feature = "defmt")]
impl defmt::Format for AltMode {
fn format(&self, fmt: defmt::Formatter) {
// Implementing Format manually to avoid the `.0: 123` field in the bitfield debug impl.
defmt::write!(
fmt,
"AltMode {{ user0: {}, user1: {}, user2: {}, user3: {}, display_port: {}, thunderbolt: {}, usb4: {} }}",
self.user0(),
self.user1(),
self.user2(),
self.user3(),
self.display_port(),
self.thunderbolt(),
self.usb4(),
)
}
}

impl AltMode {
/// Creates a new [`AltMode`] instance with the given status bits.
pub fn new(
user0: bool,
user1: bool,
user2: bool,
user3: bool,
display_port: bool,
thunderbolt: bool,
usb4: bool,
) -> Self {
Self(inner::AltMode::new(
user0.into(),
user1.into(),
user2.into(),
user3.into(),
display_port.into(),
thunderbolt.into(),
usb4.into(),
))
}

/// User-defined alternate mode 0 is active.
pub fn user0(&self) -> bool {
self.0.user0() == 1
}

/// User-defined alternate mode 1 is active.
pub fn user1(&self) -> bool {
self.0.user1() == 1
}

/// User-defined alternate mode 2 is active.
pub fn user2(&self) -> bool {
self.0.user2() == 1
}

/// User-defined alternate mode 3 is active.
pub fn user3(&self) -> bool {
self.0.user3() == 1
}

/// DisplayPort alternate mode is active.
pub fn display_port(&self) -> bool {
self.0.display_port() == 1
}

/// Thunderbolt alternate mode is active.
pub fn thunderbolt(&self) -> bool {
self.0.thunderbolt() == 1
}

/// USB4 alternate mode is active.
pub fn usb4(&self) -> bool {
self.0.usb4() == 1
}
}

mod inner {
use bitfield::bitfield;

bitfield! {
#[derive(Clone, Copy)]
pub struct PowerPathStatus(u8);
impl new;
u8;
pub ext_vbus_sw_en, set_ext_vbus_sw_en: 0, 0;
pub int_vbus_sw_en, set_int_vbus_sw_en: 1, 1;
}

bitfield! {
#[derive(Clone, Copy)]
pub struct AltMode(u8);
impl new;
u8;
pub user0, set_user0: 0, 0;
pub user1, set_user1: 1, 1;
pub user2, set_user2: 2, 2;
pub user3, set_user3: 3, 3;
pub display_port, set_display_port: 4, 4;
pub thunderbolt, set_thunderbolt: 5, 5;
pub usb4, set_usb4: 6, 6;
}
}

#[cfg(test)]
mod tests {
use super::*;

extern crate std;

#[test]
fn power_path_status_debug_contains_type_and_field_names() {
let status = PowerPathStatus(inner::PowerPathStatus(0b0000_0001));
let debug = std::format!("{:?}", status);
assert!(debug.contains("PowerPathStatus"));
assert!(debug.contains("ext_vbus_sw_en:"));
assert!(debug.contains("int_vbus_sw_en:"));
}

#[test]
fn alt_mode_debug_contains_type_and_field_names() {
let status = AltMode(inner::AltMode(0b0000_0001));
let debug = std::format!("{:?}", status);
assert!(debug.contains("AltMode"));
assert!(debug.contains("user0:"));
assert!(debug.contains("user1:"));
assert!(debug.contains("user2:"));
assert!(debug.contains("user3:"));
assert!(debug.contains("display_port:"));
assert!(debug.contains("thunderbolt:"));
assert!(debug.contains("usb4:"));
}
}
28 changes: 28 additions & 0 deletions src/type_c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,31 @@ impl Current {
}
}
}

/// The current state of a Type-C port.
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ConnectionState {
/// The port is connected to an USB Type-C Digital Audio (TCDA) accessory.
///
/// See [USB Type-C specification, release 2.4](https://www.usb.org/document-library/usb-type-cr-cable-and-connector-specification-release-24),
/// section C "USB Type-C Digital Audio".
AudioAccessory,

/// The port is in Debug Accessory Mode (DAM).
///
/// See [USB Type-C specification, release 2.4](https://www.usb.org/document-library/usb-type-cr-cable-and-connector-specification-release-24),
/// section B "Debug Accessory Mode".
DebugAccessory,

/// A port that is attached to another device, either PD-capable or not.
///
/// An *attached* port is one that is mechanically joined with USB cable to another port.
///
/// A *connected* port is one that has exchanged a Message and a GoodCRC Message response using
/// the USB Power Delivery protocol so that both Port Partners know that each is PD Capable.
///
/// See [USB PD specification, revision 3.2, version 1.1](https://www.usb.org/document-library/usb-power-delivery),
/// section 1.6 "Terms and Abbreviations".
Attached,
}